summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Lando <ml636r@att.com>2017-02-19 10:28:42 +0200
committerMichael Lando <ml636r@att.com>2017-02-19 10:51:01 +0200
commit451a3400b76511393c62a444f588a4ed15f4a549 (patch)
treee4f5873a863d1d3e55618eab48b83262f874719d
parent5abfe4e1fb5fae4bbd5fbc340519f52075aff3ff (diff)
Initial OpenECOMP SDC commit
Change-Id: I0924d5a6ae9cdc161ae17c68d3689a30d10f407b Signed-off-by: Michael Lando <ml636r@att.com>
-rw-r--r--.gitignore99
-rw-r--r--.gitreview4
-rw-r--r--.pydevproject5
-rw-r--r--LICENSE.TXT24
-rw-r--r--README.md165
-rw-r--r--asdc-tests/.gitignore4
-rw-r--r--asdc-tests/pom.xml367
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java348
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentInstanceBaseTest.java763
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/SdcTest.java202
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/Urls.java348
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/Config.java584
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/InvokedMethodListener.java63
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactAssetStructure.java135
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactReqDetails.java226
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/AssetStructure.java122
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentInstanceReqDetails.java121
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentReqDetails.java272
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GroupHeatMetaDefinition.java60
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ImportReqDetails.java332
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ProductReqDetails.java88
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyHeatMetaDefinition.java53
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyReqDetails.java145
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceAssetStructure.java58
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceDetailedAssetStructure.java89
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceInstanceAssetStructure.java116
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceReqDetails.java220
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceRespJavaObject.java337
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceAssetStructure.java49
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceDetailedAssetStructure.java78
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceReqDetails.java91
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceRespJavaObject.java267
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypeHeatMetaDefinition.java57
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ArtifactTypeEnum.java75
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AssocType.java42
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditEnum.java51
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditJsonKeysEnum.java40
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ComponentType.java37
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ErrorInfo.java93
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/EsIndexTypeIdToDelete.java64
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ExceptionEnumType.java36
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ImportTestTypesEnum.java95
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/LifeCycleStatesEnum.java75
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryResourceArtifactTypeEnum.java53
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryServiceArtifactTypeEnum.java66
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/NormativeTypesEnum.java55
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/PropertyTypeEnum.java105
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ResourceCategoryEnum.java56
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/RespJsonKeysEnum.java60
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceApiArtifactEnum.java40
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceCategoriesEnum.java37
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ToscaKeysEnum.java49
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/UserRoleEnum.java76
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedArtifactAudit.java166
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedAuthenticationAudit.java90
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedCategoryAudit.java151
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedDistDownloadAudit.java79
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedEcomConsumerAudit.java88
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedExternalAudit.java179
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGetUserListAudit.java88
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGroupingAudit.java121
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedProductAudit.java142
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedResourceAuditJavaObject.java300
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedUserCRUDAudit.java98
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderData.java114
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderValue.java38
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpHeaderEnum.java58
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpRequest.java883
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/MustHeaders.java53
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/RestResponse.java84
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/TODO/ImportCapabilityTypeCITest.java135
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ArtifactServletTest.java656
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/CrudArt.java1750
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/DownloadComponentArt.java687
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/PlaceHolderValidations.java696
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateArtResponse.java631
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsFromUI.java444
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsTypes.java176
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/attribute/ComponentInstanceAttributeTest.java83
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CatalogDataApiTest.java234
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesBaseTest.java49
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesTests.java2289
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/ElementsApiTest.java147
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/GroupingTest.java2003
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/SubCategoriesTest.java1907
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/CRUDExternalAPI.java1011
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarUpdate.java356
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarValidateArtifacts.java103
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ToscaGroupInsideVF.java578
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/AuthanticationTests.java186
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/DistributionDownloadArtifactTest.java542
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/externalapi/DownloadArtifactsTest.java338
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/BasicHttpAuthenticationTest.java403
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/FeProxyTest.java53
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/ManageEcompConsumerCredentials.java1388
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/UuidTest.java104
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/CsarUtilsTest.java430
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ExportToscaTest.java333
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportCsarResourceTest.java1538
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportGenericResourceCITest.java599
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportNewResourceCITest.java1431
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaCapabilitiesWithProperties.java416
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaResourceTest.java2404
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportUpdateResourseCsarTest.java282
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/inputs/InputsApiTests.java225
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/lifecycle/LCSbaseTest.java274
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ChangeServiceInstanceVersionTest.java1478
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductBaseTest.java195
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckinTest.java199
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckoutTest.java146
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductComponentInstanceCRUDTest.java1437
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCreateWithValidationsTest.java1710
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCrudTest.java2197
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductGetFollowedTest.java164
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductLifecycleTest.java54
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductTestBase.java148
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductToscaYamlGenerationTest.java83
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductUndoCheckoutTest.java216
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/AdditionalInformationServletTest.java2021
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentInstancePropertyTest.java1263
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentProperty.java1796
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/PropertyApisTest.java383
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CheckGetResource.java52
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ComponentRelationshipInVfTest.java1408
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CreateResourceApiTest.java2199
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetAllResourceVersions.java580
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetResourceNotAbstractApiTest.java326
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ResourceApiTest.java366
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SampleDataProvider.java41
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SimultaneousApiTest.java102
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/UpdateResourceMetadataTest.java2254
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VFResourceInstanceNameCRUD.java480
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ValidateExtendedVfData.java315
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VfComponentInstanceCRUDTest.java1792
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ChangeServiceDistributionStatusApiTest.java1008
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/CreateServiceMetadataApiTest.java1300
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetAllServiceVersions.java350
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetComponentAuditApiTest.java322
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetServiceLatestVersionTest.java684
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ReqCapOccurrencesTest.java1191
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ServiceComponentInstanceCRUDTest.java1624
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/UpdateServiceMetadataTest.java1984
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/ActivateDeActivateDeleteUser.java832
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/CreateUserApiTest.java1451
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/GovernorWorkspaceApiTest.java354
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/executeOnUGN/distributionClient/ClientConfiguration.java141
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/ComplexResourceBaseTest.java177
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/DownloadArtifactBaseTest.java125
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/SimpleOneRsrcOneServiceTest.java96
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/rules/MyTestWatcher.java82
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/ExtentReporterNG.java94
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest.java293
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest2backup.java410
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/CrudE2E.java220
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/MultipleResourceUpdate.java126
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaCapabilitiesNodeTemplatesDefinition.java25
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaDefinition.java74
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaGroupsTopologyTemplateDefinition.java78
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaImportsDefinition.java59
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaInputsTopologyTemplateDefinition.java25
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTemplatesTopologyTemplateDefinition.java83
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTypesDefinition.java54
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaPropertiesNodeTemplatesDefinition.java53
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaRequirementsNodeTemplatesDefinition.java72
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaTopologyTemplateDefinition.java77
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/AddUserAuditMessageInfo.java113
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserAuditJavaObject.java133
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserHeaderData.java59
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserResponseMessageEnum.java48
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/WebSealUserDetails.java74
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ArtifactUtils.java43
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/DbUtils.java306
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Decoder.java62
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ReqCap.java630
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ToscaParserUtils.java267
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Utils.java233
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils.java213
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils2.java172
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/AtomicOperationUtils.java627
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/Convertor.java309
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ElementFactory.java873
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/FileUtils.java136
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ImportUtils.java56
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ArtifactRestUtils.java689
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/AssetRestUtils.java510
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/BaseRestUtils.java256
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CatalogRestUtils.java75
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CategoryRestUtils.java298
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentInstanceRestUtils.java276
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentRestUtils.java62
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ConsumerRestUtils.java206
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/EcompUserRestUtils.java255
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/GroupRestUtils.java61
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ImportRestUtils.java339
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/InputsRestUtils.java122
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/LifecycleRestUtils.java360
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ProductRestUtils.java185
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/PropertyRestUtils.java310
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResourceRestUtils.java575
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java541
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ServiceRestUtils.java262
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/UserRestUtils.java282
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ArtifactValidationUtils.java228
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/AuditValidationUtils.java1328
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/BaseValidationUtils.java116
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CategoryValidationUtils.java126
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CsarValidationUtils.java274
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ErrorValidationUtils.java112
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ProductValidationUtils.java239
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ResourceValidationUtils.java354
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ServiceValidationUtils.java130
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/UserValidationUtils.java279
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/webSealAccess/NeoJavaObject.java111
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetAssetServlet.java351
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetFilteredAssetServlet.java438
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetSpecificAssetMetadataServlet.java773
-rw-r--r--asdc-tests/src/main/java/org/openecomp/sdc/post/Install.java69
-rw-r--r--asdc-tests/src/main/resources/ci/conf/log4j.properties34
-rw-r--r--asdc-tests/src/main/resources/ci/conf/sdc-packages.yaml12
-rw-r--r--asdc-tests/src/main/resources/ci/conf/sdc.yaml88
-rw-r--r--asdc-tests/src/main/resources/ci/conf/testngLifeCycle.xml27
-rw-r--r--asdc-tests/src/main/resources/ci/conf/titan.properties8
-rw-r--r--asdc-tests/src/main/resources/ci/conf/truststorebin0 -> 971 bytes
-rw-r--r--asdc-tests/src/main/resources/ci/scripts/startTest.sh88
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/artifacts.xml19
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/category.xml11
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/ciFull.xml221
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/externalApis.xml11
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/general.xml11
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/imports.xml12
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/product.xml14
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/property.xml11
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/resource.xml336
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/sanity.xml389
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/service.xml15
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/testngLifeCycle.xml27
-rw-r--r--asdc-tests/src/main/resources/ci/testSuites/user.xml18
-rw-r--r--asdc-tests/src/test/resources/CI/components/apache/apache-type.yml50
-rw-r--r--asdc-tests/src/test/resources/CI/components/apache/images/apache.pngbin0 -> 10518 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/apache/scripts/install_apache.sh53
-rw-r--r--asdc-tests/src/test/resources/CI/components/apache/scripts/start_apache.sh10
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/images/relational_db.pngbin0 -> 51853 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/normative-types-DBMS.yml36
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/images/volume.pngbin0 -> 16643 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/normative-types-blockStorage.yml40
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/compute/images/compute.pngbin0 -> 61345 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/compute/normative-types-compute.yml77
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/database/images/relational_db.pngbin0 -> 51853 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/database/normative-types-database.yml41
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/network/images/network.pngbin0 -> 159707 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/network/normative-types-network.yml39
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/images/objectstore.pngbin0 -> 4354 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/normative-types-objectStorage.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/root/images/root.pngbin0 -> 5335 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/root/normative-types-root.yml168
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/images/software.pngbin0 -> 55407 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/normative-types-softwareComponent.yml25
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/images/network.pngbin0 -> 159707 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/normative-types-webApplication.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/components/normativeTypes/webServer/normative-types-webServer.yml24
-rw-r--r--asdc-tests/src/test/resources/CI/configuration.yaml382
-rw-r--r--asdc-tests/src/test/resources/CI/csars/FCGI_with_inputs.csarbin0 -> 35433 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/LDSA1_with_inputs.csarbin0 -> 28243 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts.csarbin0 -> 36440 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_a.csarbin0 -> 36440 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_b.csarbin0 -> 36444 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming.csarbin0 -> 35872 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_a.csarbin0 -> 35874 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_add_update.csarbin0 -> 35854 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_delete_update.csarbin0 -> 35852 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_update.csarbin0 -> 36436 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/jsonPropertyTypeTest.csarbin0 -> 24762 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/mycompute.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/csars/mycompute2.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/csars/mycompute_failed.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2G.csarbin0 -> 38900 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2GV001.csarbin0 -> 38994 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2GV001_a.csarbin0 -> 38994 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2GV006-remove-all-nested-artifacts.csarbin0 -> 38993 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2GV008-change-nested-oam-fileContent.csarbin0 -> 39005 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2G_a.csarbin0 -> 38900 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/orig2G_updated.csarbin0 -> 38775 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/valid_vf.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/valid_vf_a.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/valid_vf_b.csarbin0 -> 1244 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/valid_vf_c.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/valid_vf_d.csarbin0 -> 1406 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/valid_vf_f.csarbin0 -> 1443 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/vf_relate_by_cap_name.csarbin0 -> 25963 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop.csarbin0 -> 1411 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1.csarbin0 -> 1548 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1_failed.csarbin0 -> 1546 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop_failed.csarbin0 -> 1412 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/csars/vmmc_relate_by_cap_name.csarbin0 -> 35606 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.yml23
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.zipbin0 -> 530 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.zipbin0 -> 449 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.yml10
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.zipbin0 -> 394 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.yml25
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.zipbin0 -> 505 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.yml4
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.zipbin0 -> 269 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.yml10
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.zipbin0 -> 423 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/deleteResourcesLanWanDemo.sh36
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/importResourcesLanWanDemo.sh49
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.yml3
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.zipbin0 -> 242 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.json11
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.yml13
-rw-r--r--asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.zipbin0 -> 455 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/error-configuration.yaml1576
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml.old28
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.zipbin0 -> 357 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml13
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml.old24
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.zipbin0 -> 347 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.yml13
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.zipbin0 -> 343 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml13
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml.old23
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.zipbin0 -> 346 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/compute/compute.json15
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/compute/compute.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/compute/compute.zipbin0 -> 477 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/deleteResourcesDemo.sh36
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/importResourcesDemo.sh39
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.zipbin0 -> 384 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/root/root.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/root/root.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/root/root.zipbin0 -> 378 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.yml15
-rw-r--r--asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.zipbin0 -> 358 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.capabilities.yaml13
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.yml2
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.zipbin0 -> 230 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.yml2
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.zipbin0 -> 194 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.json13
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.yml3
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.zipbin0 -> 180 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.zipbin0 -> 336 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.yml12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.zipbin0 -> 350 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.yml12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.zipbin0 -> 347 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.yml12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.zipbin0 -> 340 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.zipbin0 -> 337 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.yml15
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.zipbin0 -> 359 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.zipbin0 -> 262 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.zipbin0 -> 256 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.zipbin0 -> 255 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.zipbin0 -> 302 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.zipbin0 -> 254 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.zipbin0 -> 300 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/capabilityTypes.zipbin0 -> 320 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/deleteNormative.sh35
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-MMSC/importNormative.sh85
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/VCE_Brocade_Tosca.yaml834
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/capabilityTypes.zipbin0 -> 1158 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/importVceBrocade.sh89
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.yaml48
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.zipbin0 -> 805 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.yaml48
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.zipbin0 -> 795 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.yaml48
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.zipbin0 -> 796 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.yaml7
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.zipbin0 -> 339 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.yaml7
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.zipbin0 -> 395 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.yaml156
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.zipbin0 -> 1751 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.yaml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.zipbin0 -> 344 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.yaml10
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.zipbin0 -> 480 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.yaml10
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.zipbin0 -> 475 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.yaml10
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.zipbin0 -> 489 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.yaml10
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.zipbin0 -> 550 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.yaml10
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.zipbin0 -> 538 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.yaml7
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.zipbin0 -> 310 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.yaml32
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.zipbin0 -> 592 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.yaml5
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.zipbin0 -> 309 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.yaml16
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.zipbin0 -> 414 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.yaml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.zipbin0 -> 532 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.yaml24
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.zipbin0 -> 462 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.json11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.yaml28
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.zipbin0 -> 502 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/vce.brocade.capabilities.yaml191
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/DBMS/DBMS.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.zipbin0 -> 422 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/blockStorage/blockStorage.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.zipbin0 -> 392 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/capabilityTypes.yml148
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/capabilityTypes.zipbin0 -> 1018 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/capabilityTypesCi.zipbin0 -> 990 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/categoryTypes.yml89
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/categoryTypes.zipbin0 -> 710 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/compute/compute.json24
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.zipbin0 -> 559 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/containerApplication/containerApplication.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.zipbin0 -> 358 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/containerRuntime/containerRuntime.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.zipbin0 -> 335 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/database/database.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.yml27
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.zipbin0 -> 517 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/deleteNormative.sh49
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/importNormative.sh89
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.yml11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.zipbin0 -> 273 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/loadBalancer/loadBalancer.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.zipbin0 -> 468 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/network/network.json21
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.yml41
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.zipbin0 -> 461 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.yml31
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.zipbin0 -> 454 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/normative-port/port.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/notmative-network/network.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.yml41
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.zipbin0 -> 450 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.zipbin0 -> 386 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/objectStorage/objectStorage.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.yml31
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.zipbin0 -> 467 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/port/port.json21
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.yml23
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.zipbin0 -> 457 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/root/root.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.zipbin0 -> 435 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/softwareComponent/softwareComponent.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.yml15
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.zipbin0 -> 398 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/webApplication/webApplication.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.yml11
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.zipbin0 -> 380 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResource/webServer/webServer.json20
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/VSPPackage.csarbin0 -> 26339 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_1.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_2.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/csars.zipbin0 -> 34892 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/fiveLinesAsBlock0.csarbin0 -> 1325 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion.csarbin0 -> 1317 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion2.csarbin0 -> 1315 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion3.csarbin0 -> 1317 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion4.csarbin0 -> 1317 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion5.csarbin0 -> 1319 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile1.csarbin0 -> 1322 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile2.csarbin0 -> 1322 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile3.csarbin0 -> 1319 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile4.csarbin0 -> 1319 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile5.csarbin0 -> 1319 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/missingEntryDefintionPair.csarbin0 -> 1285 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/missingOneLineInToscaMeta.csarbin0 -> 1295 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf2.csarbin0 -> 1314 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThanOneMetaFile.csarbin0 -> 1808 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThenOneYamlFile.csarbin0 -> 2470 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/noCSARVersion.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/noCreatedByValue.csarbin0 -> 1304 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/noEntryDefinitionsValue.csarbin0 -> 1296 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/noNewLineAfterBLock0.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/noTOSCAMetaFileVersionValue.csarbin0 -> 1315 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlAndMetaFiles.csarbin0 -> 490 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlFile.csarbin0 -> 757 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/someValueAfterBlock0.csarbin0 -> 1322 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaFolderNotExists.csarbin0 -> 907 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaFileNotExists.csarbin0 -> 1049 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaOutsideTheFolder.csarbin0 -> 1286 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/underscoreInsteadOfDash.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/validCsarVersion.csarbin0 -> 1320 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf.csarbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf_zip.zipbin0 -> 1316 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_category/empty_category.json22
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.zipbin0 -> 394 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/empty_contact.json22
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.zipbin0 -> 445 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/empty_desc.json22
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.zipbin0 -> 386 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/empty_icon.json22
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.zipbin0 -> 386 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/empty_payloadName.json22
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.zipbin0 -> 400 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/empty_resource_name.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.zipbin0 -> 456 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/empty_tag.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.zipbin0 -> 436 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/import _multiple_tags.json18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.zipbin0 -> 408 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/importResource4test.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.zipbin0 -> 630 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/importResource4testCP.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.zipbin0 -> 637 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/importResource4testMissingNameSpace.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.zipbin0 -> 662 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/importResource4testUnknown.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.zipbin0 -> 651 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/importResource4testUpdateVendorNameAndCategory.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.yml12
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.zipbin0 -> 447 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/importResource4testUpdateWithoutReqCap.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.yml12
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.zipbin0 -> 460 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/importResource4testVF.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.zipbin0 -> 637 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/importResource4testVFC.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.zipbin0 -> 641 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/importResource4testVL.json17
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.yml30
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.zipbin0 -> 637 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.yml141
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.zipbin0 -> 979 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi.zipbin0 -> 990 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_MissingDerivedFrom.yml132
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_WithoutRoot.yml141
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_category/missing_category.json12
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.zipbin0 -> 450 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/missing_contact.json21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.zipbin0 -> 449 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/missing_derived_from.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.yml15
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.zipbin0 -> 391 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/missing_desc.json15
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.zipbin0 -> 442 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/missing_icon.json15
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.zipbin0 -> 442 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/missing_payloadName.json15
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.zipbin0 -> 456 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/missing_resource_name.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.zipbin0 -> 460 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/missing_tags.json15
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.zipbin0 -> 442 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/myCompute/myCompute.json24
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.zipbin0 -> 567 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.yml29
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.zipbin0 -> 470 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/portInvalidDefaultValue.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.zipbin0 -> 610 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/userCompute/userCompute.json26
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.yml29
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.zipbin0 -> 580 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/userUpdateCompute.json22
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.yml39
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.zipbin0 -> 591 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/validateProporties_happyScenarios.json14
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.zipbin0 -> 578 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/validateProporties_typeBoolean_valueInit.json14
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.zipbin0 -> 579 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/validateProporties_typeBoolean_valueString.json14
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.zipbin0 -> 575 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/validateProporties_typeFloat_valueBoolean.json14
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.zipbin0 -> 574 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/validateProporties_typeFloat_valueString.json14
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.zipbin0 -> 574 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/validateProporties_typeInit_valueBoolean.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.zipbin0 -> 572 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/validateProporties_typeInit_valueFloat.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.yml21
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.zipbin0 -> 572 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/validateProporties_typeInit_valueString.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.yml50
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.zipbin0 -> 717 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/validateProporties_typeList_valueUrlCredential.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.yml29
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.zipbin0 -> 628 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/validateProporties_typeMap_valueUrlCredential.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/normative-types-new-validateProporties_typeString_valueString_special_chars.yml19
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/validateProporties_typeString_valueString_special_chars.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/normative-types-new-validateProporties_typeString_valueString_trimming.yml19
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/validateProporties_typeString_valueString_trimming.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.yml32
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.zipbin0 -> 638 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/validateProporties_typeTestDataType.json16
-rw-r--r--asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.yml23
-rw-r--r--asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.zipbin0 -> 333 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.yml13
-rw-r--r--asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.zipbin0 -> 401 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.yml14
-rw-r--r--asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.zipbin0 -> 386 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/other/mapping.json182
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/addYangXmlArtifactToResource.xml32
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.yaml787
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.zipbin0 -> 2420 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat_net 0 2.yaml787
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatEnvfile.env787
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatInvalidFormat.yaml9
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heat_mini.yaml13
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidJson.json11
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYamlFormat.yaml787
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYangXml.xml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/jsonArtifact.json22
-rw-r--r--asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/other.txt3
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/bluePrintSampleArtifact.xml3
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/docSampleArtifact.docxbin0 -> 11307 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.emf2
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.xml3
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/eventSampleArtifact.xml3
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/jsonSampleArtifact.json3
-rw-r--r--asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/toscaSampleArtifact.yml5
-rw-r--r--asdc-tests/src/test/resources/CI/tests/addHeatArtifactToServiceAndSertify/asc_heat 0 2.yaml787
-rw-r--r--asdc-tests/src/test/resources/CI/tests/downloadResourceArtifactSuccess/org.openstack.Rally.zipbin0 -> 11590 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactListNoContentTest/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataNoContentTest/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getResourceArtifactPayloadNoContentTest/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/install_mysql2.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/start_mysql2.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topology.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topologyTemplate.txt2
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topology.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topologyTemplate.txt2
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/install_mysql2.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/start_mysql2.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topology.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topologyTemplate.txt2
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/install_mysql2.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/start_mysql2.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topology.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topologyTemplate.txt2
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topology.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topologyTemplate.txt2
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDefault.yaml603
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDesc.yaml603
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingType.yaml603
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithValidParams.yaml787
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_1.yaml144
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_2.yaml469
-rw-r--r--asdc-tests/src/test/resources/CI/tests/heatEnv/yuli.yaml144
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/BindingAsset.yml14
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap_DerivedFromMyCompute1.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPWithAttributes.yml59
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_1.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_2.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_1.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_2.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromCPWithOwnReq.yml14
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromWebApplication_HasNoReqType.yml27
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentCapFromRoot.yml26
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqAndCap.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute1.yml22
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute2.yml25
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqFromCompute.yml32
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/FatherHasNoReqCap.yml9
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure01.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure02.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure03.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure04.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure05.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure06.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure07.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure08.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure09.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure10.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure11.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure12.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure13.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure14.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure15.yml19
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure16.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure01.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure02.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure03.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure04.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure05.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure06.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure07.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure08.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure09.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure10.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure11.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure12.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure13.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure14.yml18
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure15.yml19
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure16.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MyFatherCompute_NoReqCap.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameCapAsCompute.yml26
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute.yml23
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute_DerivedFromMyCompute1.yml23
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap11.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap1UNBOUNDED.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromMyCompute.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromWebAppDerivedReqCap.yml15
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importAttributeSuccessFlow.yml53
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importCapabilityNameExistsOnParent.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateCapability.yml42
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateRequirements.yml40
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyBadDefault.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyGoodDefault.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertySuccessFlow.yml198
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importMapPropertySuccessFlow.yml452
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent_DerivedFromMyCompute1.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInCapDefinition.yml37
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInReqDefinition.yml25
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildCompute_NoReqCap.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildWebApp_DerivedFromContainer.yml15
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myCompute.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeDerivedFromNotExists.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectDefenitionVersionValue.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectNameSpaceFormat.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeNoDefenitionVersion.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeOccurencySuccess.yml77
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeParssingFalure.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeReqNameExistsOnDerived.yml34
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeVF.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithNodeTypesTwice.yml42
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithTopologyTemplate.yml44
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myFatherWebApp_derviedFromDocker.yml16
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myLinkVL.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myPortCP.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/noContent.yml0
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure01.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure02.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure03.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure04.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure05.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure06.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure07.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure08.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure09.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure10.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure11.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure31.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure32.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure33.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure34.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure35.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure36.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure37.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure38.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure39.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure40.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure41.yml35
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq11.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq12.yml17
-rw-r--r--asdc-tests/src/test/resources/CI/tests/testCsarAPI/topology.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/testCsarAPI/topologyTemplate.txt1
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool10_false.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool11_false.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool12_false.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool1_true.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool2_true.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool3_true.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool4_true.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool5_true.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool6_true.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool7_false.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool8_false.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool9_false.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number1.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number2.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/uploadComponent/images/mysql.pngbin0 -> 63119 bytes
-rw-r--r--asdc-tests/src/test/resources/CI/tests/uploadComponent/mysql.yml85
-rw-r--r--asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/install_mysql.sh28
-rw-r--r--asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/start_mysql.sh105
-rw-r--r--asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/artifact_unsupported.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool1.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool2.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number1.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number2.yaml140
-rw-r--r--asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_string1.yaml140
-rw-r--r--asdc-tests/src/test/resources/config.json12
-rw-r--r--asdc-tests/src/test/resources/logback-test.xml13
-rw-r--r--asdc-tests/tarball.xml71
-rw-r--r--asdc-tests/testng.xml15
-rw-r--r--asdc-tests/testngLifeCycle.xml27
-rw-r--r--asdctool/.gitignore3
-rw-r--r--asdctool/.pydevproject5
-rw-r--r--asdctool/pom.xml434
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/Utils.java152
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/DataMigration.java830
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/EsToCassandraDataMigrationConfig.java51
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLConverter.java694
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLDataAnalyzer.java364
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/PopulateComponentCache.java388
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/ProductLogic.java103
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/RestUtils.java83
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/UpdatePropertyOnVertex.java180
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AddGroupUuid.java132
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AllowMultipleHeats.java144
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AppConfig.java538
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/DerivedFromAlignment.java232
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/GroupsAlignment.java201
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationCategory.java48
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationSubCategory.java36
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/ServiceMigration.java1703
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/VfcNamingAlignment.java185
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1607/CsarMigration.java93
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/TitanFixUtils.java387
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/ToscaArtifactsAlignment.java461
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/main/DataSchemaMenu.java97
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/main/EsToCassandraDataMigrationMenu.java109
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/main/ExportImportMenu.java169
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/main/MigrationMenu.java251
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/main/RemoveUtils.java78
-rw-r--r--asdctool/src/main/java/org/openecomp/sdc/asdctool/main/UpdateIsVnfMenu.java122
-rw-r--r--asdctool/src/main/resources/application-context.xml11
-rw-r--r--asdctool/src/main/resources/config/categoryMigration.yaml113
-rw-r--r--asdctool/src/main/resources/config/configuration.yaml376
-rw-r--r--asdctool/src/main/resources/config/elasticsearch.yml392
-rw-r--r--asdctool/src/main/resources/config/error-configuration.yaml1671
-rw-r--r--asdctool/src/main/resources/config/logback.xml44
-rw-r--r--asdctool/src/main/resources/config/titan.properties3
-rw-r--r--asdctool/src/main/resources/es-resources/README.txt43
-rw-r--r--asdctool/src/main/resources/es-resources/auditMappings.txt169
-rw-r--r--asdctool/src/main/resources/es-resources/audit_migration_1602.py132
-rw-r--r--asdctool/src/main/resources/es-resources/config_properties.py11
-rw-r--r--asdctool/src/main/resources/es-resources/file_utils.py21
-rw-r--r--asdctool/src/main/resources/es-resources/get-pip.py17759
-rw-r--r--asdctool/src/main/resources/es-resources/index_ops.py151
-rw-r--r--asdctool/src/main/resources/es-resources/types/auditinggetuebclusterevent.txt8
-rw-r--r--asdctool/src/main/resources/es-resources/types/distributiondeployevent.txt14
-rw-r--r--asdctool/src/main/resources/es-resources/types/distributiondownloadevent.txt9
-rw-r--r--asdctool/src/main/resources/es-resources/types/distributionengineevent.txt13
-rw-r--r--asdctool/src/main/resources/es-resources/types/distributionnotificationevent.txt16
-rw-r--r--asdctool/src/main/resources/es-resources/types/distributionstatusevent.txt12
-rw-r--r--asdctool/src/main/resources/es-resources/types/resourceadminevent.txt21
-rw-r--r--asdctool/src/main/resources/es-resources/types/useraccessevent.txt10
-rw-r--r--asdctool/src/main/resources/es-resources/types/useradminevent.txt20
-rw-r--r--asdctool/src/main/resources/scripts/baseOperation.sh33
-rw-r--r--asdctool/src/main/resources/scripts/cleanCsar.sh33
-rw-r--r--asdctool/src/main/resources/scripts/dataMigration.sh33
-rw-r--r--asdctool/src/main/resources/scripts/dataMigration1607.sh33
-rw-r--r--asdctool/src/main/resources/scripts/dataMigration1610.sh41
-rw-r--r--asdctool/src/main/resources/scripts/deleteAllProducts.sh33
-rw-r--r--asdctool/src/main/resources/scripts/derivedFromAlignment.sh33
-rw-r--r--asdctool/src/main/resources/scripts/esToCassandraMigration.sh29
-rw-r--r--asdctool/src/main/resources/scripts/esToCassandraMigrationExportOnly.sh29
-rw-r--r--asdctool/src/main/resources/scripts/esToCassandraMigrationImportOnly.sh29
-rw-r--r--asdctool/src/main/resources/scripts/exportGraph.sh29
-rw-r--r--asdctool/src/main/resources/scripts/exportGraphAsGraphMl.sh29
-rw-r--r--asdctool/src/main/resources/scripts/exportUsers.sh33
-rw-r--r--asdctool/src/main/resources/scripts/fix_icons.sh31
-rw-r--r--asdctool/src/main/resources/scripts/fix_issue.sh33
-rw-r--r--asdctool/src/main/resources/scripts/groupsAlignment.sh33
-rw-r--r--asdctool/src/main/resources/scripts/importGraph.sh29
-rw-r--r--asdctool/src/main/resources/scripts/populateComponentCache.sh33
-rw-r--r--asdctool/src/main/resources/scripts/python/duplicates.py47
-rw-r--r--asdctool/src/main/resources/scripts/python/duplicatesAndRemove.py136
-rw-r--r--asdctool/src/main/resources/scripts/python/graphSize.py56
-rw-r--r--asdctool/src/main/resources/scripts/python/user/exportUsers.py122
-rw-r--r--asdctool/src/main/resources/scripts/python/user/importUsers.py215
-rw-r--r--asdctool/src/main/resources/scripts/schemaCreation.sh33
-rw-r--r--asdctool/src/main/resources/scripts/updateIsVnf.sh36
-rw-r--r--asdctool/src/main/resources/scripts/vfcNameAlignment.sh33
-rw-r--r--asdctool/tarball.xml26
-rw-r--r--catalog-be/.gitignore8
-rw-r--r--catalog-be/.pydevproject5
-rw-r--r--catalog-be/README.txt41
-rw-r--r--catalog-be/normatives.xml18
-rw-r--r--catalog-be/pom.xml790
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/auditing/api/IAuditingManager.java31
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatConstants.java258
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatUtil.java268
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingManager.java137
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/AsdcComponentsCleanerTask.java175
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/ComponentsCleanBusinessLogic.java89
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ArtifactInfoImpl.java196
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaErrorResponse.java86
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaHandler.java610
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DeConfigurationStatus.java40
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngine.java367
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineClusterHealth.java356
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTask.java293
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEnginePollingTask.java207
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionNotificationSender.java127
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotification.java84
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotificationEnum.java26
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IArtifactInfo.java63
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IDistributionEngine.java45
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/INotificationData.java102
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IResourceArtifactInfo.java39
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IServiceArtifactInfo.java25
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/JsonContainerResourceInstance.java108
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationDataImpl.java118
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationExecutorService.java81
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/PublishNotificationRunnable.java156
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ResourceArtifactInfoImpl.java58
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceArtifactInfoImpl.java30
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceDistributionArtifactsBuilder.java268
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/SubscriberTypeEnum.java26
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/TestQueue.java188
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/UebHealthCheckCall.java77
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/VfModuleArtifactPayload.java71
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AdditionalInformationBusinessLogic.java573
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java4127
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AttributeBusinessLogic.java295
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/BaseBusinessLogic.java571
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java123
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CategoriesImportManager.java274
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java309
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java860
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java1661
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogic.java285
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ConsumerBusinessLogic.java313
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CsarValidationUtils.java265
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java255
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DistributionMonitoringBusinessLogic.java214
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java1125
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupBusinessLogic.java1512
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java151
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/HealthCheckBusinessLogic.java441
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/IDeploymentArtifactTypeConfigGetter.java27
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ImportUtils.java722
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InformationDeployedArtifactsBusinessLogic.java117
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogic.java526
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java124
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/MonitoringBusinessLogic.java71
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java130
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductBusinessLogic.java887
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductComponentInstanceBusinessLogic.java59
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PropertyBusinessLogic.java391
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RequirementsBusinessLogic.java94
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java5475
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java921
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResponseFormatManager.java80
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java1768
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceComponentInstanceBusinessLogic.java58
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/VFComponentInstanceBusinessLogic.java72
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransition.java209
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTransition.java409
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckinTransition.java119
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTransition.java141
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifeCycleTransition.java160
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleBusinessLogic.java572
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoBase.java42
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoWithAction.java50
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/StartCertificationTransition.java119
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTransition.java170
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/CategoryTypeEnum.java39
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/HighestFilterEnum.java27
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/ArtifactUtils.java58
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/NodeTypeConvertUtils.java76
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/AuditHandler.java65
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogic.java243
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/CambriaOperationStatus.java25
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/RegistrationRequest.java40
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/ServerListResponse.java36
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicRegistrationResponse.java42
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicUnregistrationResponse.java52
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionCatalogServlet.java230
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionServlet.java279
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/EcompIntImpl.java376
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompRoleConverter.java53
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompUserConverter.java118
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/ArtifactExternalServlet.java646
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetMetadataConverter.java381
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetsDataServlet.java300
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ArtifactMetadata.java106
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/AssetMetadata.java129
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/IAssetMetadata.java45
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductAssetMetadata.java72
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductCategoryGroupMetadata.java57
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetDetailedMetadata.java63
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetMetadata.java69
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceInstanceMetadata.java89
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetDetailedMetadata.java53
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetMetadata.java60
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/filters/BasicAuthenticationFilter.java225
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/filters/BeServletFilter.java202
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/filters/ComponentsAvailabilityFilter.java122
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java1461
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/impl/DownloadArtifactLogic.java166
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/impl/ServletUtils.java49
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/impl/WebAppContextWrapper.java35
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessInfo.java159
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessList.java40
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactDefinitionInfo.java84
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java352
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTypesInfo.java48
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/CreateAndAssotiateInfo.java52
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatus.java62
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusInfo.java91
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusListResponse.java37
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceInfo.java74
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceListResponce.java37
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupDefinitionInfo.java130
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupTemplateInfo.java60
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/MergedArtifactInfo.java151
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceInfo.java51
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceVersionInfo.java53
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ServicesWrapper.java35
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ServletJsonResponse.java44
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInfo.java69
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInterface.java37
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/listen/BEAppContextListener.java132
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/monitoring/EsGateway.java109
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AbstractValidationsServlet.java843
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AdditionalInformationServlet.java472
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ArtifactServlet.java564
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AttributeServlet.java278
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeGenericServlet.java226
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeMonitoringServlet.java203
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java744
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentServlet.java270
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigMgrServlet.java125
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigServlet.java81
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConsumerServlet.java227
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/CsarBuildServlet.java296
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DistributionServiceServlet.java169
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java611
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/GroupServlet.java182
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/InputsServlet.java320
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java216
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ProductServlet.java311
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/PropertyServlet.java562
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RepresentationUtils.java124
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RequirementsServlet.java84
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceArtifactDownloadServlet.java188
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java180
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java692
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ServiceServlet.java728
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ToscaDaoServlet.java92
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesFetchServlet.java122
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java317
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/UserAdminServlet.java516
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/switchover/detector/SwitchoverDetector.java315
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ArtifactTypes.java37
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CSARTool.java45
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java261
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java649
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java189
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaError.java26
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java629
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaRepresentation.java49
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java69
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/EntrySchema.java43
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/IToscaMetadata.java33
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/SubstitutionMapping.java60
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaCapability.java80
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaGroupTemplate.java69
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java137
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeTemplate.java73
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeType.java87
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaProperty.java83
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java45
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplate.java90
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateCapability.java46
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java68
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java63
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/VfModuleToscaMetadata.java66
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/user/IUserBusinessLogic.java51
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/user/Role.java29
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminAction.java27
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminValidator.java69
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/user/UserBusinessLogic.java714
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ICommitHandler.java27
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBAction.java25
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBType.java27
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ITransactionSdnc.java40
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/RollbackHandler.java121
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/TransactionUtils.java85
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESAction.java58
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESRollbackHandler.java92
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanCommitHandler.java52
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanRollbackHandler.java55
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/CommitManager.java87
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/RollbackManager.java107
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionManager.java98
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionSdncImpl.java313
-rw-r--r--catalog-be/src/main/resources/application-context.xml64
-rw-r--r--catalog-be/src/main/resources/config/configuration.yaml478
-rw-r--r--catalog-be/src/main/resources/config/distribution-engine-configuration.yaml46
-rw-r--r--catalog-be/src/main/resources/config/ecomp-error-configuration.yaml383
-rw-r--r--catalog-be/src/main/resources/config/error-configuration.yaml1694
-rw-r--r--catalog-be/src/main/resources/config/logback.xml226
-rw-r--r--catalog-be/src/main/resources/config/neo4j-errors-configuration.yaml60
-rw-r--r--catalog-be/src/main/resources/config/titan.properties10
-rw-r--r--catalog-be/src/main/resources/elasticsearch.yml393
-rw-r--r--catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.yml204
-rw-r--r--catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.zipbin0 -> 1385 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/categories/categoryTypes.yml116
-rw-r--r--catalog-be/src/main/resources/import/tosca/categories/categoryTypes.zipbin0 -> 848 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml821
-rw-r--r--catalog-be/src/main/resources/import/tosca/data-types/dataTypes.zipbin0 -> 3456 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/group-types/groupTypes.yml30
-rw-r--r--catalog-be/src/main/resources/import/tosca/group-types/groupTypes.zipbin0 -> 523 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.yml17
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.zipbin0 -> 469 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.yml177
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.zipbin0 -> 1425 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.json17
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.yml137
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.zipbin0 -> 1017 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.json15
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.yml89
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.zipbin0 -> 781 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.yml49
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.zipbin0 -> 675 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.yml64
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.zipbin0 -> 720 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.yml35
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.zipbin0 -> 638 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.json15
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.yml59
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.zipbin0 -> 718 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.yml76
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.zipbin0 -> 764 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.yml84
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.zipbin0 -> 826 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.yml9
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.zipbin0 -> 321 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.yml97
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.zipbin0 -> 972 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.yml136
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.zipbin0 -> 1192 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.json24
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.yml249
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.zipbin0 -> 1840 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.yml42
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.zipbin0 -> 692 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.yml18
-rw-r--r--catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.zipbin0 -> 355 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.yml11
-rw-r--r--catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.zipbin0 -> 273 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.yml17
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.zipbin0 -> 382 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.yml18
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.zipbin0 -> 352 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.json24
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.yml35
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.zipbin0 -> 519 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.yml9
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.zipbin0 -> 318 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.yml9
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.zipbin0 -> 295 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/database/database.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/database/database.yml27
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/database/database.zipbin0 -> 477 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.yml20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.zipbin0 -> 428 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/network/network.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/network/network.yml45
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/network/network.zipbin0 -> 463 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.yml18
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.zipbin0 -> 346 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/port/port.json21
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/port/port.yml31
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/port/port.zipbin0 -> 414 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/root/root.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/root/root.yml23
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/root/root.zipbin0 -> 417 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.yml17
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.zipbin0 -> 395 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.yml15
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.zipbin0 -> 358 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.json20
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.yml11
-rw-r--r--catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.zipbin0 -> 340 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.yml94
-rw-r--r--catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.zipbin0 -> 673 bytes
-rw-r--r--catalog-be/src/main/resources/import/tosca/users/importUsers.yaml13
-rw-r--r--catalog-be/src/main/resources/jetty-ssl.xml51
-rw-r--r--catalog-be/src/main/resources/keystore/README.txt16
-rw-r--r--catalog-be/src/main/resources/keystore/catalogbe.jksbin0 -> 2201 bytes
-rw-r--r--catalog-be/src/main/resources/keystore/catalogbe.jks.pwd1
-rw-r--r--catalog-be/src/main/resources/portal.properties25
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importCategoryTypes.py74
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importCommon.py43
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importDataTypes.py74
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importGroupTypes.py74
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importHeatTypes.py113
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importNodeType.py156
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importNormativeAll.py135
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importNormativeCapabilities.py77
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importNormativeElements.py65
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importNormativeInterfaceLifecycleTypes.py75
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importNormativeTypes.py159
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importPolicyTypes.py74
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/importUsersFromYaml.py221
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/upgradeNormative.py114
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersion.py143
-rw-r--r--catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersionAll.py93
-rw-r--r--catalog-be/src/main/resources/swagger/css/print.css1155
-rw-r--r--catalog-be/src/main/resources/swagger/css/reset.css125
-rw-r--r--catalog-be/src/main/resources/swagger/css/screen.css1256
-rw-r--r--catalog-be/src/main/resources/swagger/css/typography.css26
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.eotbin0 -> 22922 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.svg411
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.ttfbin0 -> 40513 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woffbin0 -> 25992 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff2bin0 -> 11480 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.eotbin0 -> 22008 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.svg403
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.ttfbin0 -> 39069 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woffbin0 -> 24868 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff2bin0 -> 11304 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/explorer_icons.pngbin0 -> 5763 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/favicon-16x16.pngbin0 -> 645 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/favicon-32x32.pngbin0 -> 1654 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/favicon.icobin0 -> 5430 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/logo_small.pngbin0 -> 770 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/pet_store_api.pngbin0 -> 824 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/throbber.gifbin0 -> 9257 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/images/wordnik_api.pngbin0 -> 980 bytes
-rw-r--r--catalog-be/src/main/resources/swagger/index.html123
-rw-r--r--catalog-be/src/main/resources/swagger/lib/backbone-min.js35
-rw-r--r--catalog-be/src/main/resources/swagger/lib/handlebars-2.0.0.js48
-rw-r--r--catalog-be/src/main/resources/swagger/lib/highlight.7.3.pack.js21
-rw-r--r--catalog-be/src/main/resources/swagger/lib/jquery-1.8.0.min.js22
-rw-r--r--catalog-be/src/main/resources/swagger/lib/jquery.ba-bbq.min.js38
-rw-r--r--catalog-be/src/main/resources/swagger/lib/jquery.slideto.min.js21
-rw-r--r--catalog-be/src/main/resources/swagger/lib/jquery.wiggle.min.js28
-rw-r--r--catalog-be/src/main/resources/swagger/lib/marked.js1292
-rw-r--r--catalog-be/src/main/resources/swagger/lib/swagger-oauth.js304
-rw-r--r--catalog-be/src/main/resources/swagger/lib/underscore-min.js26
-rw-r--r--catalog-be/src/main/resources/swagger/lib/underscore-min.map1
-rw-r--r--catalog-be/src/main/resources/swagger/o2c.html20
-rw-r--r--catalog-be/src/main/resources/swagger/swagger-ui.js21738
-rw-r--r--catalog-be/src/main/resources/swagger/swagger-ui.min.js28
-rw-r--r--catalog-be/src/main/webapp/META-INF/MANIFEST.MF9
-rw-r--r--catalog-be/src/main/webapp/WEB-INF/jetty-web.xml8
-rw-r--r--catalog-be/src/main/webapp/WEB-INF/web.xml131
-rw-r--r--catalog-be/src/main/webapp/index.html7
-rw-r--r--catalog-be/src/main/webapp/index.jsp5
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/AuditingMockManager.java44
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java266
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/ErrorConfigurationTest.java77
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java63
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/ZipUtil.java132
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/AuditingManagerTest.java79
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/BaseConfDependentTest.java48
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/ComponentBusinessLogicTest.java61
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/HealthCheckBusinessLogicTest.java91
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/PropertyBusinessLogicTest.java189
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java369
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceTestUtils.java90
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/ServiceBusinessLogicTest.java931
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineConfigTest.java168
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineHealthCheckTest.java138
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTaskTest.java269
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ArtifactBusinessLogicTest.java234
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManagerTest.java105
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CategoriesImportManagerTest.java111
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogicTest.java155
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CsarValidationUtilsTest.java37
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ImportUtilsTest.java509
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManagerTest.java94
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java1423
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceInstanceBusinessLogicTest.java284
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransitionTest.java169
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTest.java230
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckinTest.java170
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTest.java188
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/LifecycleTestBase.java215
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTest.java125
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogicTest.java192
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/distribution/servlet/DistributionServletTest.java159
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/ecomp/GenerateEcompErrorFileTest.java77
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/AbstractValidationsServletTest.java72
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ApplicationConfig.java62
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceServletTest.java268
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java189
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/TypesUploadServletTest.java154
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/UserAdminServletTest.java148
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/user/UserAdminManagerTest.java91
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/common/transaction/mngr/SdncTransactionTest.java437
-rw-r--r--catalog-be/src/test/resources/config/catalog-be/configuration.yaml427
-rw-r--r--catalog-be/src/test/resources/config/catalog-be/distribution-engine-configuration.yaml43
-rw-r--r--catalog-be/src/test/resources/config/catalog-be/ecomp-error-configuration.yaml383
-rw-r--r--catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml1583
-rw-r--r--catalog-be/src/test/resources/config/catalog-be/neo4j-errors-configuration.yaml60
-rw-r--r--catalog-be/src/test/resources/config/catalog-be/users-configuration.yaml2
-rw-r--r--catalog-be/src/test/resources/config/configuration1.yaml17
-rw-r--r--catalog-be/src/test/resources/config/elasticsearch.yml387
-rw-r--r--catalog-be/src/test/resources/config/elasticsearch.yml.bak387
-rw-r--r--catalog-be/src/test/resources/config/mysql-type-empty-nodes.zipbin0 -> 1894 bytes
-rw-r--r--catalog-be/src/test/resources/config/mysql-type-no-nodes.zipbin0 -> 448 bytes
-rw-r--r--catalog-be/src/test/resources/config/mysql-type-no-version.zipbin0 -> 1142 bytes
-rw-r--r--catalog-be/src/test/resources/config/mysql-type-only-yaml.zipbin0 -> 1129 bytes
-rw-r--r--catalog-be/src/test/resources/config/mysql-type-with-scripts.zipbin0 -> 66267 bytes
-rw-r--r--catalog-be/src/test/resources/config/mysql-type.yml82
-rw-r--r--catalog-be/src/test/resources/config/mysql-type.zipbin0 -> 1129 bytes
-rw-r--r--catalog-be/src/test/resources/config/normative-types-root.zipbin0 -> 1638 bytes
-rw-r--r--catalog-be/src/test/resources/config/sample.yaml17
-rw-r--r--catalog-be/src/test/resources/config/sampleNoProtocol.yaml17
-rw-r--r--catalog-be/src/test/resources/elasticsearch.yml391
-rw-r--r--catalog-be/src/test/resources/logback-test.xml13
-rw-r--r--catalog-be/src/test/resources/mock_vf.csarbin0 -> 1316 bytes
-rw-r--r--catalog-be/src/test/resources/normativeTypes/importToscaProperties.yml452
-rw-r--r--catalog-be/src/test/resources/normativeTypes/importToscaWithAttribute.yml41
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-all-map-test.yml30
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-DBMS.yml17
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-Root.yml23
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-blockStorage.yml18
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-compute.yml35
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-database.yml27
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-port.yml31
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-softwareComponent.yml17
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-new-webServer.yml11
-rw-r--r--catalog-be/src/test/resources/normativeTypes/normative-types-string-list-test.yml29
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_duplicateNode.yml23
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_empty.yml17
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_inputs.yml103
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_nodeEmpty.yml18
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_nodeVF.yml38
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_notValidNode.yml36
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_notValidRelationNode.yml36
-rw-r--r--catalog-be/src/test/resources/normativeTypes/topology_template_sample.yml36
-rw-r--r--catalog-be/src/test/resources/types/capabilityTypes.yml148
-rw-r--r--catalog-be/src/test/resources/types/capabilityTypes.zipbin0 -> 979 bytes
-rw-r--r--catalog-be/src/test/resources/types/categoryTypes.yml74
-rw-r--r--catalog-be/src/test/resources/types/categoryTypes.zipbin0 -> 403 bytes
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeDeriveFromIntegerWithProperty.yml8
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeDerivedFromRootNoProperties.yml4
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeDuplicateProperty.yml11
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeForGroup.yml40
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part1.yml15
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part2.yml12
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part1.yml19
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part2.yml19
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part1.yml20
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part2.yml20
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part1.yml22
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part2.yml19
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part1.yml19
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part2.yml25
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypeWithPropertyTypeThisDataType.yml9
-rw-r--r--catalog-be/src/test/resources/types/datatypes/dataTypes.yml129
-rw-r--r--catalog-be/src/test/resources/types/datatypes/derived3levelDataType.yml37
-rw-r--r--catalog-be/src/test/resources/types/datatypes/derivedDataType.yml19
-rw-r--r--catalog-be/src/test/resources/types/datatypes/derivedDataTypeNoProperties.yml15
-rw-r--r--catalog-be/src/test/resources/types/datatypes/derivedInvalidDataType.yml15
-rw-r--r--catalog-be/src/test/resources/types/datatypes/emptyDataType.yml4
-rw-r--r--catalog-be/src/test/resources/types/datatypes/emptyDataTypeNoPropertiesTag.yml3
-rw-r--r--catalog-be/src/test/resources/types/datatypes/exitingPropertyAtAncestor.yml19
-rw-r--r--catalog-be/src/test/resources/types/datatypes/invalidDataType.yml1
-rw-r--r--catalog-be/src/test/resources/types/datatypes/oneDataType.yml9
-rw-r--r--catalog-be/src/test/resources/types/interfaceLifecycleTypes.yml11
-rw-r--r--catalog-be/src/test/resources/types/interfaceLifecycleTypes.zipbin0 -> 273 bytes
-rw-r--r--catalog-be/src/test/resources/valid_vf.csarbin0 -> 1316 bytes
-rw-r--r--catalog-be/tarball.xml54
-rw-r--r--catalog-dao/.gitignore1
-rw-r--r--catalog-dao/pom.xml335
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/Account.java86
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java88
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/BasicDao.java182
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericIdDAO.java171
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericSearchDAO.java132
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IBasicDAO.java65
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ICatalogDAO.java72
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IElementDAO.java36
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IEsHealthCheckDao.java29
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericIdDAO.java66
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericSearchDAO.java42
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IPropertyDAO.java28
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IResourceDAO.java57
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IUsersDAO.java37
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ResourceUploadStatus.java25
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ArtifactCassandraDao.java110
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditAccessor.java75
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditCassandraDao.java383
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraClient.java207
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraDao.java65
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraOperationStatus.java26
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCacheAccessor.java48
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCassandraDao.java293
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/ITableDescription.java46
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/SdcSchemaBuilder.java425
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/Table.java71
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ArtifactTableDescription.java95
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/AuthEventTableDescription.java103
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/CategoryEventTableDescription.java105
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ComponentCacheTableDescription.java95
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ConsumerEventTableDefinition.java101
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDeployEventTableDesc.java104
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDownloadEventTableDesc.java100
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribEngineEventTableDesc.java103
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribNotifEventTableDesc.java104
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribStatusEventTableDesc.java102
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ExternalApiEventTableDesc.java104
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetCatHierEventTableDesc.java99
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUebClusterEventTableDesc.java101
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUsersListEventTableDesc.java99
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ResAdminEventTableDescription.java132
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAccessEventTableDescription.java104
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAdminEventTableDescription.java102
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/es/ElasticSearchClient.java226
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java264
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/ActionEnum.java25
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/AdditionalInformationEnum.java25
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphEdge.java94
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElement.java58
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElementTypeEnum.java25
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphNode.java77
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphRelation.java106
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/RelationEndPoint.java100
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/AuditingDao.java264
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESCatalogDAO.java214
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESTimeBasedDao.java305
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/EsHealthCheckDao.java50
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/MonitoringDao.java41
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jElementDAO.java112
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jPropertyDAO.java99
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jResourceDAO.java242
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jUsersDAO.java169
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchFacet.java55
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchResult.java72
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FetchContext.java32
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/GetMultipleDataResult.java113
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/BatchBuilder.java83
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTemplates.java52
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTranslator.java251
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgeLabels.java101
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgePropertiesDictionary.java80
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphNeighbourTable.java64
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphPropertiesDictionary.java212
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jClient.java1004
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jEdge.java69
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jGraphBatchBuilder.java194
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jOperationStatus.java77
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/NodeRelation.java65
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/MatchFilter.java51
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveByRelationFilter.java81
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveFilter.java68
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/UpdateFilter.java56
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/HttpRestClient.java403
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/RestConfigurationInfo.java110
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/QueryType.java28
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGenericDao.java1848
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGraphClient.java872
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanOperationStatus.java26
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/CollectionUtils.java125
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Constants.java38
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/DaoUtils.java59
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ElasticSearchUtil.java48
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Exceptions.java35
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageQuality.java40
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageResizeUtil.java142
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/JsonUtil.java209
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapEntry.java55
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapUtil.java86
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/TypeMap.java63
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/UserStatusEnum.java40
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/ArtifactDataEnum.java26
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/IResourceUploader.java76
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AdditionalInfoParameterData.java155
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ArtifactData.java183
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeData.java131
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeValueData.java145
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityData.java182
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityInstData.java128
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityTypeData.java131
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CategoryData.java114
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentCacheData.java153
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentInstanceData.java114
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentMetadataData.java147
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ConsumerData.java92
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/DataTypeData.java106
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ESArtifactData.java106
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/EntryData.java52
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GraphNodeLock.java79
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupData.java97
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupTypeData.java130
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterData.java172
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterValueData.java91
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputValueData.java145
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputsData.java149
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InterfaceData.java91
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/OperationData.java88
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PolicyTypeData.java130
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ProductMetadataData.java77
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyData.java149
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyValueData.java158
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipInstData.java164
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipTypeData.java128
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementData.java158
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementImplData.java141
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceCategoryData.java72
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceMetadataData.java80
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceArtifactsDataCollection.java45
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceCategoryData.java47
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceMetadataData.java62
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/TagData.java112
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UniqueIdData.java47
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserData.java228
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserFunctionalMenuData.java95
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditRecordFactory.java84
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingActionEnum.java135
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGenericEvent.java91
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGetUebClusterEvent.java191
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingTypesConstants.java49
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuthEvent.java209
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/CategoryEvent.java240
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ConsumerEvent.java184
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDeployEvent.java253
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDownloadEvent.java205
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionEngineEvent.java273
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionNotificationEvent.java286
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionStatusEvent.java254
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ExternalApiEvent.java279
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetCategoryHierarchyEvent.java182
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetUsersListEvent.java186
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ResourceAdminEvent.java437
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAccessEvent.java179
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAdminEvent.java224
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/CategoryData.java90
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/GroupingData.java73
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/SubCategoryData.java86
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/DeleteDeployedException.java42
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/InitializationException.java32
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/ResourceDAOException.java58
-rw-r--r--catalog-dao/src/main/java/org/openecomp/sdc/be/resources/impl/ResourceUploader.java206
-rw-r--r--catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ArtifactDaoTest.java577
-rw-r--r--catalog-dao/src/test/java/org/openecomp/sdc/be/resources/AuditingDaoTest.java463
-rw-r--r--catalog-dao/src/test/java/org/openecomp/sdc/be/resources/CassandraTest.java74
-rw-r--r--catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ESUsersDAOTest.java64
-rw-r--r--catalog-dao/src/test/java/org/openecomp/sdc/be/resources/TitanGenericDaoTest.java721
-rw-r--r--catalog-dao/src/test/resources/application-context-test.xml22
-rw-r--r--catalog-dao/src/test/resources/cassandra.yaml801
-rw-r--r--catalog-dao/src/test/resources/config/catalog-dao/configuration.yaml120
-rw-r--r--catalog-dao/src/test/resources/config/catalog-dao/ecomp-error-configuration.yaml383
-rw-r--r--catalog-dao/src/test/resources/elasticsearch.yml392
-rw-r--r--catalog-dao/src/test/resources/images/apache.pngbin0 -> 10518 bytes
-rw-r--r--catalog-dao/src/test/resources/log4j.properties8
-rw-r--r--catalog-dao/src/test/resources/logback-test.xml13
-rw-r--r--catalog-fe/.gitignore5
-rw-r--r--catalog-fe/pom.xml441
-rw-r--r--catalog-fe/readMe.txt14
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/Constants.java15
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/client/BackendClient.java179
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/Audit.java73
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/CrudOperation.java26
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HttpRequestInfo.java77
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/ImportMetadata.java80
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/FEAppContextListener.java85
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/MyObjectMapperProvider.java49
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigMgrServlet.java88
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigServlet.java114
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeHealthCheckServlet.java49
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeProxyServlet.java214
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/HealthCheckService.java219
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/KibanaServlet.java98
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/PortalServlet.java279
-rw-r--r--catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java108
-rw-r--r--catalog-fe/src/main/resources/application-context.xml23
-rw-r--r--catalog-fe/src/main/resources/config/configuration.yaml78
-rw-r--r--catalog-fe/src/main/resources/config/ecomp-error-configuration.yaml48
-rw-r--r--catalog-fe/src/main/resources/config/logback.xml125
-rw-r--r--catalog-fe/src/main/resources/config/rest-configuration-info.yaml12
-rw-r--r--catalog-fe/src/main/resources/jetty-ipaccess.xml30
-rw-r--r--catalog-fe/src/main/resources/portal.properties28
-rw-r--r--catalog-fe/src/main/resources/scripts/install.sh61
-rw-r--r--catalog-fe/src/main/resources/scripts/installJettyBase.sh14
-rw-r--r--catalog-fe/src/main/resources/scripts/jvm.properties5
-rw-r--r--catalog-fe/src/main/resources/scripts/startJetty.sh10
-rw-r--r--catalog-fe/src/main/resources/scripts/updateSslParams.sh33
-rw-r--r--catalog-fe/src/main/webapp/META-INF/MANIFEST.MF9
-rw-r--r--catalog-fe/src/main/webapp/WEB-INF/jetty-web.xml8
-rw-r--r--catalog-fe/src/main/webapp/WEB-INF/web.xml280
-rw-r--r--catalog-fe/src/test/SpecRunner.html36
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/MIT.LICENSE20
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/SpecRunner.html26
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js201
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/console.js180
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine-html.js379
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.css55
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js2422
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine_favicon.pngbin0 -> 2057 bytes
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/spec/PlayerSpec.js78
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/spec/SpecHelper.js35
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/src/Player.js42
-rw-r--r--catalog-fe/src/test/jasmine-standalone-2.0.0/src/Song.js27
-rw-r--r--catalog-fe/src/test/java/org/openecomp/sdc/ApplicationConfig.java41
-rw-r--r--catalog-fe/src/test/java/org/openecomp/sdc/ContentDisposiotionDelegator.java35
-rw-r--r--catalog-fe/src/test/java/org/openecomp/sdc/Main.java63
-rw-r--r--catalog-fe/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java66
-rw-r--r--catalog-fe/src/test/java/org/openecomp/sdc/servlets/FeProxyServletTest.java140
-rw-r--r--catalog-fe/src/test/java/org/openecomp/sdc/servlets/PortalServletTest.java159
-rw-r--r--catalog-fe/src/test/resources/CI/ReadMe.txt5
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/apache-type.yml50
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/apache.pngbin0 -> 10518 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/compute.pngbin0 -> 61345 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/loadbalancer.pngbin0 -> 26899 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/network.pngbin0 -> 159707 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/objectstore.pngbin0 -> 4354 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/relational_db.pngbin0 -> 51853 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/root.pngbin0 -> 5335 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/router.pngbin0 -> 26404 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/software.pngbin0 -> 55407 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/images/volume.pngbin0 -> 16643 bytes
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-DBMS.yml36
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-blockStorage.yml40
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-compute.yml77
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-database.yml41
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-network.yml39
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-objectStorage.yml35
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-root.yml168
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-softwareComponent.yml25
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-webApplication.yml21
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/normative-types-webServer.yml24
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/scripts/apache_start_detection.groovy6
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/scripts/install_apache.sh53
-rw-r--r--catalog-fe/src/test/resources/CI/originalResources/scripts/start_apache.sh10
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/body.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/command15
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/body.txt0
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/command13
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/body.txt0
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/command13
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/body.txt0
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/command13
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results.json12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/body.txt0
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/command13
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/commandTrial12
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/contentMD5.txt1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/headers8
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/results1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/toExec1
-rw-r--r--catalog-fe/src/test/resources/CI/tests/env/env.sh11
-rw-r--r--catalog-fe/src/test/resources/CI/tests/runTests.sh90
-rw-r--r--catalog-fe/src/test/resources/config/catalog-fe/configuration.yaml69
-rw-r--r--catalog-fe/src/test/resources/config/catalog-fe/rest-configuration-info.yaml12
-rw-r--r--catalog-fe/src/test/resources/config/configuration1.yaml17
-rw-r--r--catalog-fe/src/test/resources/config/sample.yaml17
-rw-r--r--catalog-fe/src/test/resources/config/sampleNoProtocol.yaml17
-rw-r--r--catalog-fe/src/test/resources/logback-test.xml2
-rw-r--r--catalog-fe/src/test/spec/codeSpec.js197
-rw-r--r--catalog-fe/src/test/testScripts/filesContents.js207
-rw-r--r--catalog-fe/src/test/testScripts/jasmine-fixture.js453
-rw-r--r--catalog-fe/tarball.xml32
-rw-r--r--catalog-model/.gitignore2
-rw-r--r--catalog-model/pom.xml267
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInfoParameterInfo.java82
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInformationDefinition.java80
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactDefinition.java138
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactType.java64
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactUiDownloadData.java45
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/AttributeDefinition.java71
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/CapReqDef.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityDefinition.java266
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityTypeDefinition.java65
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabiltyInstance.java52
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Category.java65
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Component.java598
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstInputsMap.java37
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstance.java109
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribute.java74
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceInput.java137
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceProperty.java117
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentMetadataDefinition.java74
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java316
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ConsumerDefinition.java35
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java86
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionStatusEnum.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionTransitionEnum.java54
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/FunctionalMenuInfo.java44
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/GetInputValueInfo.java71
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupDefinition.java133
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupProperty.java76
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupTypeDefinition.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/HeatParameterDefinition.java42
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/IComplexDefaultValue.java35
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/IComponentInstanceConnectedElement.java29
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/IOperationParameter.java37
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ImplementationArtifact.java67
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/InputDefinition.java89
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/InterfaceDefinition.java89
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/LifeCycleTransitionEnum.java83
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/LifecycleStateEnum.java44
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Operation.java105
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ParsedToscaYamlInfo.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Point.java59
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/PolicyTypeDefinition.java55
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Product.java80
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ProductMetadataDefinition.java35
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyConstraint.java34
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyDefinition.java175
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyScope.java64
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyValueDefinition.java44
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/RelationshipImpl.java47
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementAndRelationshipPair.java122
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementCapabilityRelDef.java47
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementDefinition.java241
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementImplDef.java76
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementInstance.java59
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Resource.java273
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceInstanceHeatParameter.java48
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceMetadataDefinition.java37
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Schema.java42
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Service.java100
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/ServiceMetadataDefinition.java36
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/Tag.java65
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/TargetCapabilityRelDef.java80
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadCapInfo.java58
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadComponentInstanceInfo.java73
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadInfo.java45
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadPropInfo.java68
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadReqInfo.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadResourceInfo.java304
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/User.java205
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java35
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java335
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ComponentCache.java997
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/DaoInfo.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/CheckAndUpdateJob.java131
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/DeleteJob.java64
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/Job.java109
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/OverrideJob.java74
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/StoreJob.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/CacheWorker.java93
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/IWorker.java28
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/SyncWorker.java266
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/category/CategoryDefinition.java66
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/category/GroupingDefinition.java35
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/category/SubCategoryDefinition.java60
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/heat/HeatParameterType.java95
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAdditionalInformationOperation.java94
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IArtifactOperation.java70
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAttributeOperation.java71
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICacheMangerOperation.java44
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityInstanceOperation.java143
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityOperation.java79
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityTypeOperation.java47
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentInstanceOperation.java246
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentOperation.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IConsumerOperation.java107
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IDataTypeOperation.java46
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java104
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGraphLockOperation.java36
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupOperation.java114
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupTypeOperation.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IHeatParametersOperation.java46
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInputsOperation.java81
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInterfaceLifecycleOperation.java92
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ILifecycleOperation.java65
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPolicyTypeOperation.java38
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IProductOperation.java47
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPropertyOperation.java110
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IRequirementOperation.java85
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IResourceOperation.java131
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IServiceOperation.java88
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IUserAdminOperation.java58
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/StorageOperationStatus.java27
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java413
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperation.java960
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AllOperationsUtil.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperation.java1202
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AttributeOperation.java463
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CacheMangerOperation.java213
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityInstanceOperation.java1215
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityOperation.java1196
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperation.java416
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperation.java5852
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperation.java2886
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ConsumerOperation.java151
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CsarOperation.java115
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DaoStatusConverter.java141
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java902
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GraphLockOperation.java234
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupOperation.java2093
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupTypeOperation.java385
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperation.java492
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InputsOperation.java1184
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InterfaceLifecycleOperation.java1308
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperation.java1143
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/Neo4jStatusConverter.java78
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/OnboardingClient.java190
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperation.java230
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ProductOperation.java1067
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java2788
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperation.java1674
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperation.java3089
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperation.java1566
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java244
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperation.java501
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/ComponentValidationUtils.java123
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/GraphDeleteUtil.java119
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaPropertyType.java199
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaType.java120
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/VersionUtil.java94
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractComparablePropertyConstraint.java72
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractPropertyConstraint.java46
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractStringPropertyConstraint.java53
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintType.java60
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintUtil.java145
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/EqualConstraint.java77
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterOrEqualConstraint.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterThanConstraint.java66
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/InRangeConstraint.java130
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LengthConstraint.java54
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessOrEqualConstraint.java70
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessThanConstraint.java58
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MaxLengthConstraint.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MinLengthConstraint.java62
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/PatternConstraint.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ValidValuesConstraint.java92
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintFunctionalException.java51
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintRequiredParameterException.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintTechnicalException.java41
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintValueDoNotMatchPropertyTypeException.java49
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintViolationException.java49
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/FunctionalException.java41
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/InvalidPropertyConstraintImplementationException.java40
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/TechnicalException.java40
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/BooleanConverter.java42
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/DefaultConverter.java43
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/FloatConverter.java42
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatBooleanConverter.java54
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatCommaDelimitedListConverter.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatJsonConverter.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatNumberConverter.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatStringConverter.java51
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/IntegerConverter.java44
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/JsonConverter.java62
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ListConverter.java217
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/LowerCaseConverter.java48
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/MapConverter.java249
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/PropertyValueConverter.java31
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/StringConvertor.java55
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaBooleanConverter.java54
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaFloatConverter.java50
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaJsonValueConverter.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaListValueConverter.java182
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaMapValueConverter.java184
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaStringConvertor.java43
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueBaseConverter.java153
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueConverter.java30
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueDefaultConverter.java43
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/BooleanValidator.java55
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/DataTypeValidatorConverter.java499
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/FloatValidator.java60
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatBooleanValidator.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatCommaDelimitedListValidator.java55
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatNumberValidator.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatStringValidator.java55
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidator.java85
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/JsonValidator.java76
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/KeyValidator.java61
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ListValidator.java160
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/MapValidator.java184
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/PropertyTypeValidator.java46
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/StringValidator.java85
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ToscaBooleanValidator.java56
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ApplicationVersionException.java36
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ComparableVersion.java463
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/Version.java192
-rw-r--r--catalog-model/src/main/java/org/openecomp/sdc/be/unittests/utils/FactoryUtils.java233
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/ModelTestBase.java46
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/JsonObjectTest.java80
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperationTest.java216
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperationTest.java574
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperationTest.java345
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationSpringTest.java543
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationTest.java136
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperationTest.java395
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java109
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperationTest.java289
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/InterfaceOperationTest.java272
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperationTest.java1991
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperationTest.java120
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperationTest.java548
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperationTest.java236
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceInstanceOperationTest.java2511
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperationTest.java734
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperationTest.java964
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperationTest.java239
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/DataTypeValidatorTest.java1004
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/OperationTestsUtil.java95
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/PrintGraph.java461
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/ResourceCreationUtils.java36
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/serialize/TestResourceSerialization.java222
-rw-r--r--catalog-model/src/test/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidatorTest.java75
-rw-r--r--catalog-model/src/test/resources/application-context-test.xml17
-rw-r--r--catalog-model/src/test/resources/config/ecomp-error-configuration.yaml308
-rw-r--r--catalog-model/src/test/resources/logback-test.xml13
-rw-r--r--catalog-ui/Gruntfile.js807
-rw-r--r--catalog-ui/SETTING-ENVIRONMENT.md36
-rw-r--r--catalog-ui/SETTING-MOCK-SERVER.md53
-rw-r--r--catalog-ui/app/_favicon.pngbin0 -> 6364 bytes
-rw-r--r--catalog-ui/app/index.html392
-rw-r--r--catalog-ui/app/languages/en_US_OS.json420
-rw-r--r--catalog-ui/app/scripts/app.ts936
-rw-r--r--catalog-ui/app/scripts/directives/clicked-outside/clicked-outside-directive.ts131
-rw-r--r--catalog-ui/app/scripts/directives/custom-validation/custom-validation.ts55
-rw-r--r--catalog-ui/app/scripts/directives/download-artifact/download-artifact.ts141
-rw-r--r--catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.html73
-rw-r--r--catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.less296
-rw-r--r--catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.ts235
-rw-r--r--catalog-ui/app/scripts/directives/edit-name-popover/edit-module-name-popover.html31
-rw-r--r--catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-directive.ts98
-rw-r--r--catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-view.html1
-rw-r--r--catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover.less71
-rw-r--r--catalog-ui/app/scripts/directives/elements/checkbox/checkbox.html13
-rw-r--r--catalog-ui/app/scripts/directives/elements/checkbox/checkbox.less35
-rw-r--r--catalog-ui/app/scripts/directives/elements/checkbox/checkbox.ts66
-rw-r--r--catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.html5
-rw-r--r--catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.less0
-rw-r--r--catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.ts71
-rw-r--r--catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.html7
-rw-r--r--catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.less10
-rw-r--r--catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.ts80
-rw-r--r--catalog-ui/app/scripts/directives/events/on-last-repeat/on-last-repeat.ts61
-rw-r--r--catalog-ui/app/scripts/directives/file-opener/file-opener.html3
-rw-r--r--catalog-ui/app/scripts/directives/file-opener/file-opener.ts77
-rw-r--r--catalog-ui/app/scripts/directives/file-type/file-type.ts66
-rw-r--r--catalog-ui/app/scripts/directives/file-upload/file-upload.html22
-rw-r--r--catalog-ui/app/scripts/directives/file-upload/file-upload.less75
-rw-r--r--catalog-ui/app/scripts/directives/file-upload/file-upload.ts134
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/common/common-graph-utils.ts361
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/common/style/component-instances-nodes-style.ts259
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/common/style/module-node-style.ts92
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.directive.ts555
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.html22
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.less14
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts243
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts347
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts220
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts265
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts114
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.html2
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.less14
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts24
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/image-creator/image-creator.service.ts46
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts7
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/palette/palette.directive.ts327
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/palette/palette.html59
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/palette/palette.less92
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.html63
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.less118
-rw-r--r--catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.ts113
-rw-r--r--catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.html10
-rw-r--r--catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.less39
-rw-r--r--catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.ts60
-rw-r--r--catalog-ui/app/scripts/directives/invalid-characters/invalid-characters.ts72
-rw-r--r--catalog-ui/app/scripts/directives/layout/top-nav/top-nav.html54
-rw-r--r--catalog-ui/app/scripts/directives/layout/top-nav/top-nav.less218
-rw-r--r--catalog-ui/app/scripts/directives/layout/top-nav/top-nav.ts155
-rw-r--r--catalog-ui/app/scripts/directives/layout/top-progress/top-progress.html22
-rw-r--r--catalog-ui/app/scripts/directives/layout/top-progress/top-progress.less58
-rw-r--r--catalog-ui/app/scripts/directives/layout/top-progress/top-progress.ts57
-rw-r--r--catalog-ui/app/scripts/directives/loader/loader-directive.html4
-rw-r--r--catalog-ui/app/scripts/directives/loader/loader-directive.less74
-rw-r--r--catalog-ui/app/scripts/directives/loader/loader-directive.ts155
-rw-r--r--catalog-ui/app/scripts/directives/modal/sdc-modal.html18
-rw-r--r--catalog-ui/app/scripts/directives/modal/sdc-modal.less10
-rw-r--r--catalog-ui/app/scripts/directives/modal/sdc-modal.ts103
-rw-r--r--catalog-ui/app/scripts/directives/page-scroller/page-scroller.html22
-rw-r--r--catalog-ui/app/scripts/directives/page-scroller/page-scroller.less98
-rw-r--r--catalog-ui/app/scripts/directives/page-scroller/page-scroller.ts247
-rw-r--r--catalog-ui/app/scripts/directives/perfect-scrollbar/angular-perfect-scrollbar.ts159
-rw-r--r--catalog-ui/app/scripts/directives/print-graph-screen/print-graph-screen.ts211
-rw-r--r--catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.html82
-rw-r--r--catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.less90
-rw-r--r--catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts165
-rw-r--r--catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.html57
-rw-r--r--catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.less85
-rw-r--r--catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.ts130
-rw-r--r--catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.html70
-rw-r--r--catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.less83
-rw-r--r--catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.ts157
-rw-r--r--catalog-ui/app/scripts/directives/punch-out/punch-out.ts99
-rw-r--r--catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab-directive.ts67
-rw-r--r--catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab.less1
-rw-r--r--catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive-view.html17
-rw-r--r--catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive.ts69
-rw-r--r--catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs.less68
-rw-r--r--catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.html54
-rw-r--r--catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.less68
-rw-r--r--catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.ts197
-rw-r--r--catalog-ui/app/scripts/directives/tag/tag-directive.html10
-rw-r--r--catalog-ui/app/scripts/directives/tag/tag-directive.less51
-rw-r--r--catalog-ui/app/scripts/directives/tag/tag-directive.ts71
-rw-r--r--catalog-ui/app/scripts/directives/tutorial/image-template.html7
-rw-r--r--catalog-ui/app/scripts/directives/tutorial/text-template.html4
-rw-r--r--catalog-ui/app/scripts/directives/tutorial/tutorial-directive.html22
-rw-r--r--catalog-ui/app/scripts/directives/tutorial/tutorial-directive.less213
-rw-r--r--catalog-ui/app/scripts/directives/tutorial/tutorial-directive.ts147
-rw-r--r--catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.html9
-rw-r--r--catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.less62
-rw-r--r--catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.ts72
-rw-r--r--catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collaps-menu-box.ts66
-rw-r--r--catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.html15
-rw-r--r--catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.less55
-rw-r--r--catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.html1
-rw-r--r--catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.less10
-rw-r--r--catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.ts136
-rw-r--r--catalog-ui/app/scripts/directives/utils/page-selector/page-selector.html9
-rw-r--r--catalog-ui/app/scripts/directives/utils/page-selector/page-selector.less51
-rw-r--r--catalog-ui/app/scripts/directives/utils/page-selector/page-selector.ts106
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc-keyboard-events/sdc-keyboard-events.ts106
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.html27
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.less61
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.ts97
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.html6
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.ts109
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts179
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less10
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts245
-rw-r--r--catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html1
-rw-r--r--catalog-ui/app/scripts/directives/utils/smart-tooltip/smart-tooltip.ts85
-rw-r--r--catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.html16
-rw-r--r--catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.less69
-rw-r--r--catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.ts139
-rw-r--r--catalog-ui/app/scripts/filters/_category-name-filter.ts40
-rw-r--r--catalog-ui/app/scripts/filters/capitalize-filter.ts43
-rw-r--r--catalog-ui/app/scripts/filters/catalog-status-filter.ts41
-rw-r--r--catalog-ui/app/scripts/filters/category-icon-filter.ts54
-rw-r--r--catalog-ui/app/scripts/filters/category-type-filter.ts45
-rw-r--r--catalog-ui/app/scripts/filters/clear-whitespaces-filter.ts39
-rw-r--r--catalog-ui/app/scripts/filters/entity-filter.ts117
-rw-r--r--catalog-ui/app/scripts/filters/graph-resource-name-filter.ts47
-rw-r--r--catalog-ui/app/scripts/filters/product-category-name-filter.ts39
-rw-r--r--catalog-ui/app/scripts/filters/product-subcategory-name-filter.ts39
-rw-r--r--catalog-ui/app/scripts/filters/relation-name-fllter.ts53
-rw-r--r--catalog-ui/app/scripts/filters/resource-name-filter.ts45
-rw-r--r--catalog-ui/app/scripts/filters/resource-type-filter.ts38
-rw-r--r--catalog-ui/app/scripts/filters/string-to-date-filter.ts34
-rw-r--r--catalog-ui/app/scripts/filters/tests-id-filter.ts33
-rw-r--r--catalog-ui/app/scripts/filters/trim-filter.ts39
-rw-r--r--catalog-ui/app/scripts/filters/truncate-filter.ts48
-rw-r--r--catalog-ui/app/scripts/filters/underscoreless-filter.ts33
-rw-r--r--catalog-ui/app/scripts/models/activity.ts48
-rw-r--r--catalog-ui/app/scripts/models/additional-information.ts44
-rw-r--r--catalog-ui/app/scripts/models/app-config.ts232
-rw-r--r--catalog-ui/app/scripts/models/artifacts.ts115
-rw-r--r--catalog-ui/app/scripts/models/aschema-property.ts63
-rw-r--r--catalog-ui/app/scripts/models/attributes.ts139
-rw-r--r--catalog-ui/app/scripts/models/capability.ts116
-rw-r--r--catalog-ui/app/scripts/models/category.ts67
-rw-r--r--catalog-ui/app/scripts/models/comments.ts33
-rw-r--r--catalog-ui/app/scripts/models/components/component.ts828
-rw-r--r--catalog-ui/app/scripts/models/components/displayComponent.ts98
-rw-r--r--catalog-ui/app/scripts/models/components/product.ts109
-rw-r--r--catalog-ui/app/scripts/models/components/resource.ts185
-rw-r--r--catalog-ui/app/scripts/models/components/service.ts147
-rw-r--r--catalog-ui/app/scripts/models/componentsInstances/componentInstance.ts126
-rw-r--r--catalog-ui/app/scripts/models/componentsInstances/productInstance.ts34
-rw-r--r--catalog-ui/app/scripts/models/componentsInstances/resourceInstance.ts36
-rw-r--r--catalog-ui/app/scripts/models/componentsInstances/serviceInstance.ts35
-rw-r--r--catalog-ui/app/scripts/models/csar-component.ts36
-rw-r--r--catalog-ui/app/scripts/models/data-type-properties.ts65
-rw-r--r--catalog-ui/app/scripts/models/data-types-map.ts39
-rw-r--r--catalog-ui/app/scripts/models/data-types.ts55
-rw-r--r--catalog-ui/app/scripts/models/distribution.ts66
-rw-r--r--catalog-ui/app/scripts/models/file-download.ts28
-rw-r--r--catalog-ui/app/scripts/models/graph/d2-node.ts31
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/common-base-link.ts53
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/common-ci-link-base.ts50
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-link-base.ts46
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-simple-link.ts31
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-host-link.ts33
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-link.ts37
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-link.ts34
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-ucpe-link.ts33
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/links-factory.ts80
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-link-base.ts38
-rw-r--r--catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-vl-link.ts37
-rw-r--r--catalog-ui/app/scripts/models/graph/graphTooltip.ts38
-rw-r--r--catalog-ui/app/scripts/models/graph/link-menu.ts38
-rw-r--r--catalog-ui/app/scripts/models/graph/match-relation.ts109
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/base-common-node.ts73
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/common-ci-node-base.ts46
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts72
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-cp.ts48
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-service.ts42
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe-cp.ts39
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe.ts50
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vf.ts41
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vfc.ts33
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts54
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/modules-graph-nodes/module-node-base.ts52
-rw-r--r--catalog-ui/app/scripts/models/graph/nodes/nodes-factory.ts63
-rw-r--r--catalog-ui/app/scripts/models/graph/point.ts26
-rw-r--r--catalog-ui/app/scripts/models/graph/relationMenuObjects.ts138
-rw-r--r--catalog-ui/app/scripts/models/graph/relationship.ts107
-rw-r--r--catalog-ui/app/scripts/models/inputs.ts74
-rw-r--r--catalog-ui/app/scripts/models/instance-inputs-properties-map.ts39
-rw-r--r--catalog-ui/app/scripts/models/instances-inputs-map.ts39
-rw-r--r--catalog-ui/app/scripts/models/left-panel.ts33
-rw-r--r--catalog-ui/app/scripts/models/member.ts39
-rw-r--r--catalog-ui/app/scripts/models/modules/base-module.ts108
-rw-r--r--catalog-ui/app/scripts/models/properties.ts176
-rw-r--r--catalog-ui/app/scripts/models/requirement.ts91
-rw-r--r--catalog-ui/app/scripts/models/schema-attribute.ts37
-rw-r--r--catalog-ui/app/scripts/models/tab.ts47
-rw-r--r--catalog-ui/app/scripts/models/tooltip-data.ts28
-rw-r--r--catalog-ui/app/scripts/models/user.ts117
-rw-r--r--catalog-ui/app/scripts/models/validate.ts29
-rw-r--r--catalog-ui/app/scripts/modules/directive-module.ts106
-rw-r--r--catalog-ui/app/scripts/modules/filters.ts44
-rw-r--r--catalog-ui/app/scripts/modules/service-module.ts64
-rw-r--r--catalog-ui/app/scripts/modules/utils.ts39
-rw-r--r--catalog-ui/app/scripts/modules/view-model-module.ts96
-rw-r--r--catalog-ui/app/scripts/services/activity-log-service.ts48
-rw-r--r--catalog-ui/app/scripts/services/angular-js-bridge-service.ts22
-rw-r--r--catalog-ui/app/scripts/services/available-icons-service.ts105
-rw-r--r--catalog-ui/app/scripts/services/cache-service.ts58
-rw-r--r--catalog-ui/app/scripts/services/category-resource-service.ts83
-rw-r--r--catalog-ui/app/scripts/services/components/component-service.ts698
-rw-r--r--catalog-ui/app/scripts/services/components/product-service.ts61
-rw-r--r--catalog-ui/app/scripts/services/components/resource-service.ts61
-rw-r--r--catalog-ui/app/scripts/services/components/service-service.ts97
-rw-r--r--catalog-ui/app/scripts/services/components/utils/composition-left-palette-service.ts248
-rw-r--r--catalog-ui/app/scripts/services/configuration-ui-service.ts49
-rw-r--r--catalog-ui/app/scripts/services/cookie-service.ts95
-rw-r--r--catalog-ui/app/scripts/services/data-types-service.ts129
-rw-r--r--catalog-ui/app/scripts/services/ecomp-service.ts54
-rw-r--r--catalog-ui/app/scripts/services/entity-service.ts114
-rw-r--r--catalog-ui/app/scripts/services/event-listener-service.ts78
-rw-r--r--catalog-ui/app/scripts/services/header-interceptor.ts93
-rw-r--r--catalog-ui/app/scripts/services/http-error-interceptor.ts118
-rw-r--r--catalog-ui/app/scripts/services/loader-service.ts26
-rw-r--r--catalog-ui/app/scripts/services/onboarding-service.ts103
-rw-r--r--catalog-ui/app/scripts/services/progress-service.ts112
-rw-r--r--catalog-ui/app/scripts/services/relation-icons-service.ts61
-rw-r--r--catalog-ui/app/scripts/services/sdc-version-service.ts48
-rw-r--r--catalog-ui/app/scripts/services/sharing-service.ts41
-rw-r--r--catalog-ui/app/scripts/services/url-tobase64-service.ts52
-rw-r--r--catalog-ui/app/scripts/services/user-resource-service.ts123
-rw-r--r--catalog-ui/app/scripts/utils/artifacts-utils.ts121
-rw-r--r--catalog-ui/app/scripts/utils/change-lifecycle-state-handler.ts151
-rw-r--r--catalog-ui/app/scripts/utils/common-utils.ts81
-rw-r--r--catalog-ui/app/scripts/utils/component-factory.ts181
-rw-r--r--catalog-ui/app/scripts/utils/component-instance-factory.ts85
-rw-r--r--catalog-ui/app/scripts/utils/constants.ts247
-rw-r--r--catalog-ui/app/scripts/utils/dictionary/dictionary.ts257
-rw-r--r--catalog-ui/app/scripts/utils/file-utils.ts73
-rw-r--r--catalog-ui/app/scripts/utils/functions.ts58
-rw-r--r--catalog-ui/app/scripts/utils/menu-handler.ts145
-rw-r--r--catalog-ui/app/scripts/utils/modals-handler.ts275
-rw-r--r--catalog-ui/app/scripts/utils/prototypes.ts155
-rw-r--r--catalog-ui/app/scripts/utils/validation-utils.ts173
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts94
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html41
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less3
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view-model.ts82
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view.html24
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard.less49
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view-model.ts197
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view.html53
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management.less118
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/ecomp/ecomp-view.html1
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view-model.ts220
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view.html102
-rw-r--r--catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management.less251
-rw-r--r--catalog-ui/app/scripts/view-models/catalog/catalog-view-model.ts312
-rw-r--r--catalog-ui/app/scripts/view-models/catalog/catalog-view-tests.ts309
-rw-r--r--catalog-ui/app/scripts/view-models/catalog/catalog-view.html190
-rw-r--r--catalog-ui/app/scripts/view-models/catalog/catalog.less304
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.html16
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.less18
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/component-viewer-view-model.ts211
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/component-viewer.html55
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/component-viewer.less148
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/properties/product-properties-view.html76
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/properties/properties-view.less128
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/properties/resource-properties-view.html169
-rw-r--r--catalog-ui/app/scripts/view-models/component-viewer/properties/service-properties-view.html167
-rw-r--r--catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view-model.ts91
-rw-r--r--catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view.html1
-rw-r--r--catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model-tests.ts276
-rw-r--r--catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model.ts415
-rw-r--r--catalog-ui/app/scripts/view-models/dashboard/dashboard-view.html106
-rw-r--r--catalog-ui/app/scripts/view-models/dashboard/dashboard.less420
-rw-r--r--catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view-model.ts354
-rw-r--r--catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view.html169
-rw-r--r--catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form.less44
-rw-r--r--catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-form-view.html153
-rw-r--r--catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-from-view-model.ts255
-rw-r--r--catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.html39
-rw-r--r--catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.less74
-rw-r--r--catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.ts149
-rw-r--r--catalog-ui/app/scripts/view-models/forms/property-form/property-form-view-model.ts330
-rw-r--r--catalog-ui/app/scripts/view-models/forms/property-form/property-form-view.html219
-rw-r--r--catalog-ui/app/scripts/view-models/forms/property-form/property-form.less63
-rw-r--r--catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts105
-rw-r--r--catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-view.html72
-rw-r--r--catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name.less29
-rw-r--r--catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts96
-rw-r--r--catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view.html29
-rw-r--r--catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal.less30
-rw-r--r--catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view-model.ts117
-rw-r--r--catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view.html79
-rw-r--r--catalog-ui/app/scripts/view-models/modals/email-modal/email-modal.less56
-rw-r--r--catalog-ui/app/scripts/view-models/modals/error-modal/error-403-view.html4
-rw-r--r--catalog-ui/app/scripts/view-models/modals/error-modal/error-view-model.ts43
-rw-r--r--catalog-ui/app/scripts/view-models/modals/error-modal/error.less13
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-base-modal-model.ts64
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts43
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html16
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal.less0
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts45
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html17
-rw-r--r--catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal.less0
-rw-r--r--catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts249
-rw-r--r--catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view.html142
-rw-r--r--catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal.less148
-rw-r--r--catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view-model.ts148
-rw-r--r--catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view.html14
-rw-r--r--catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor.less303
-rw-r--r--catalog-ui/app/scripts/view-models/preloading/preloading-view.html9
-rw-r--r--catalog-ui/app/scripts/view-models/preloading/preloading-view.less107
-rw-r--r--catalog-ui/app/scripts/view-models/preloading/preloading-view.ts47
-rw-r--r--catalog-ui/app/scripts/view-models/support/support-view-model.ts38
-rw-r--r--catalog-ui/app/scripts/view-models/support/support-view.html33
-rw-r--r--catalog-ui/app/scripts/view-models/support/support.less8
-rw-r--r--catalog-ui/app/scripts/view-models/tabs/general-tab.less131
-rw-r--r--catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view-model.ts99
-rw-r--r--catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view.html57
-rw-r--r--catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy.less71
-rw-r--r--catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.html10
-rw-r--r--catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.less41
-rw-r--r--catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.ts42
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide0.html50
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide1.html34
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide2.html26
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide3.html27
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide4.html29
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide5.html27
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/slide6.html26
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/welcome-steps-controller.ts74
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/welcome-view.html22
-rw-r--r--catalog-ui/app/scripts/view-models/welcome/welcome-view.ts267
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/ReadMe.txt54
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html137
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less107
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts228
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts304
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html147
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less45
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html48
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less50
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts168
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html270
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less34
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts381
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html40
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less125
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts149
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html26
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less55
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts150
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html57
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less55
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts123
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts163
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts250
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html133
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less7
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html12
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less60
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts399
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts114
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts164
-rw-r--r--catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts64
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.html85
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.less83
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.ts122
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view-model.ts107
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view.html52
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes.less54
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view-model.ts232
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view.html99
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition.less864
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts255
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html55
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less172
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts132
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view.html129
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details.less68
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts228
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html81
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less16
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts81
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view.html57
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations.less116
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.html13
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts34
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts253
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html149
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less102
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view-model.ts127
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view.html10
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment.less33
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts67
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html126
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less33
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view-model.ts135
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view.html171
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution.less361
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view-model.ts379
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view.html307
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/general/general.less64
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view-model.ts131
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view.html26
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons.less65
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts150
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html57
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts.less47
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/inputs.less286
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts145
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html136
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less9
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts246
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html205
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs.less54
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts128
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view.html3
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts80
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html3
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts134
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html40
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less130
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view-model.ts114
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view.html62
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties.less115
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts165
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html144
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less196
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts87
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html45
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less74
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/workspace-view-model.ts703
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/workspace-view.html86
-rw-r--r--catalog-ui/app/scripts/view-models/workspace/workspace.less144
-rw-r--r--catalog-ui/app/styles/animation.less51
-rw-r--r--catalog-ui/app/styles/app.less122
-rw-r--r--catalog-ui/app/styles/buttons.less274
-rw-r--r--catalog-ui/app/styles/dark-header.less51
-rw-r--r--catalog-ui/app/styles/fonts.less72
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.eotbin0 -> 92160 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.svg424
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.ttfbin0 -> 93916 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.woffbin0 -> 42192 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.eotbin0 -> 104184 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.svg425
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.ttfbin0 -> 105932 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.woffbin0 -> 46444 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.eotbin0 -> 86088 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.svg425
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.ttfbin0 -> 88696 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.woffbin0 -> 39280 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.eotbin0 -> 104224 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.svg425
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.ttfbin0 -> 105972 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.woffbin0 -> 46676 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.eotbin0 -> 89640 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.svg425
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.ttfbin0 -> 92288 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.woffbin0 -> 40332 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.eotbin0 -> 108396 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.svg424
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.ttfbin0 -> 110192 bytes
-rw-r--r--catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.woffbin0 -> 48120 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/AT&T Variation ID.tab9
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.eotbin0 -> 31322 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.svg3671
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.ttfbin0 -> 68660 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.woffbin0 -> 35986 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.eotbin0 -> 30321 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.svg3694
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.ttfbin0 -> 71692 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.woffbin0 -> 35610 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.eotbin0 -> 32077 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.svg4365
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.ttfbin0 -> 71564 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.woffbin0 -> 37149 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.eotbin0 -> 32079 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.svg3799
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.ttfbin0 -> 76564 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.woffbin0 -> 37872 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.eotbin0 -> 32072 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.svg3872
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.ttfbin0 -> 73968 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.woffbin0 -> 37342 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.eotbin0 -> 30983 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.svg3030
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.ttfbin0 -> 70088 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.woffbin0 -> 36261 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.eotbin0 -> 28695 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.svg2473
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.ttfbin0 -> 65152 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.woffbin0 -> 33641 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.eotbin0 -> 33730 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.svg3837
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.ttfbin0 -> 77508 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.woffbin0 -> 39182 bytes
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/demo-async.htm169
-rw-r--r--catalog-ui/app/styles/fonts/OmnesATT/demo.htm155
-rw-r--r--catalog-ui/app/styles/form-elements.less199
-rw-r--r--catalog-ui/app/styles/global.less52
-rw-r--r--catalog-ui/app/styles/images/anonymous.jpgbin0 -> 1150 bytes
-rw-r--r--catalog-ui/app/styles/images/att_logo_white.pngbin0 -> 2827 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/AttachesTo.svg37
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/BindsTo.svg19
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/ConnectedTo.svg19
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/DependsOn.svg34
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/HostedOn.svg25
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/LinksTo.svg22
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/RoutesTo.svg19
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/arrow.pngbin0 -> 1111 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/arrow.svg11
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/arrow_connection_right.svg11
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/attach.pngbin0 -> 1485 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/binding.pngbin0 -> 1385 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/conected.pngbin0 -> 1481 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/conected.svg19
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/dependency.pngbin0 -> 1632 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/host.pngbin0 -> 1836 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/link.pngbin0 -> 1628 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/local_storage.pngbin0 -> 1485 bytes
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/local_storage.svg37
-rw-r--r--catalog-ui/app/styles/images/relationship-icons/rout.pngbin0 -> 1630 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Red_PLUS_HOVER.pngbin0 -> 1456 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COLLABORATION-.pngbin0 -> 3898 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COMPUTE-AS-SERVICE-.pngbin0 -> 3129 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MESSAGING-.pngbin0 -> 2376 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MOBILITY.pngbin0 -> 3396 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-.pngbin0 -> 4190 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-CLOUD-.pngbin0 -> 2908 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-1-3.pngbin0 -> 3239 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-4.pngbin0 -> 3104 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NOTIFICATION-.pngbin0 -> 2818 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_ORPHAN.pngbin0 -> 3831 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_PLATFORM-AS-SERVICE-.pngbin0 -> 3728 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SECURITY-.pngbin0 -> 3523 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SETTING-.pngbin0 -> 4019 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_avater.pngbin0 -> 3818 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/Resource_Icons_STORAGE-AS-SERVICE.pngbin0 -> 3106 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/alcatelLucent.pngbin0 -> 3301 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/applicationServer.pngbin0 -> 2152 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/aricent.pngbin0 -> 2407 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/att.pngbin0 -> 3241 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/borderElement.pngbin0 -> 3744 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/broadsoft.pngbin0 -> 2725 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/brocade.pngbin0 -> 3601 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/call_controll.pngbin0 -> 3146 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/canvasPlusIcon-red.pngbin0 -> 1434 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/canvasPlusIcon.pngbin0 -> 1455 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/cisco.pngbin0 -> 2702 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/closeModule.pngbin0 -> 15427 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/closeModuleHover.pngbin0 -> 14979 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/cloud.pngbin0 -> 3561 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/cloudep.pngbin0 -> 2944 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/compute-uncertified.pngbin0 -> 2598 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/compute.pngbin0 -> 2212 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/connector.pngbin0 -> 2040 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/cp.pngbin0 -> 1507 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/database.pngbin0 -> 2447 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_analytics.pngbin0 -> 2501 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_collector.pngbin0 -> 2582 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_database.pngbin0 -> 2447 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_microservice.pngbin0 -> 2485 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_policy.pngbin0 -> 2492 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_source.pngbin0 -> 3451 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/dcae_utilty.pngbin0 -> 3840 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/default.pngbin0 -> 3831 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/defaulticon.pngbin0 -> 2132 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/ericsson.pngbin0 -> 3606 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/firewall.pngbin0 -> 3348 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/fortinet.pngbin0 -> 2016 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/gateway.pngbin0 -> 2647 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/juniper.pngbin0 -> 3505 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/loadBalancer.pngbin0 -> 2949 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/metaswitch.pngbin0 -> 3141 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/module.pngbin0 -> 1216 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/mysql.pngbin0 -> 3033 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/network.pngbin0 -> 4214 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/networkrules.pngbin0 -> 2920 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/nokia_siemens.pngbin0 -> 2615 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/objectStorage.pngbin0 -> 3031 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/openModule.pngbin0 -> 14990 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/openModuleHover.pngbin0 -> 14989 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/oracle.pngbin0 -> 2432 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/ossep.pngbin0 -> 3768 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/personep.pngbin0 -> 3419 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/port.pngbin0 -> 2359 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/premisesep.pngbin0 -> 2822 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/router.pngbin0 -> 4031 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/securityrules.pngbin0 -> 2965 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/selectedCPInstance.pngbin0 -> 1090 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/selectedInstance.pngbin0 -> 1143 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/selectedUcpeInstance.pngbin0 -> 3850 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/selectedVLInstance.pngbin0 -> 1072 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/server.pngbin0 -> 2078 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/tropo.pngbin0 -> 3558 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/ucpe.pngbin0 -> 3983 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/uncertified.pngbin0 -> 1503 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/vfw.pngbin0 -> 3730 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/vl.pngbin0 -> 3376741 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/vrouter.pngbin0 -> 3088 bytes
-rw-r--r--catalog-ui/app/styles/images/resource-icons/wanx.pngbin0 -> 3166 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/call_controll.pngbin0 -> 3238 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/collaboration.pngbin0 -> 3804 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/collaboration1.pngbin0 -> 3194 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/compute.pngbin0 -> 3201 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/defaulticon.pngbin0 -> 2168 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/messaging.pngbin0 -> 2458 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/mobility.pngbin0 -> 3333 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/network_l_1-3.pngbin0 -> 3030 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/network_l_4.pngbin0 -> 2961 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/notification.pngbin0 -> 2714 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/platform.pngbin0 -> 3762 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/storage.pngbin0 -> 3136 bytes
-rw-r--r--catalog-ui/app/styles/images/service-icons/uncertified.pngbin0 -> 1503 bytes
-rw-r--r--catalog-ui/app/styles/images/sprites/sprite-global-old.pngbin0 -> 84465 bytes
-rw-r--r--catalog-ui/app/styles/images/sprites/sprite-global.pngbin0 -> 71883 bytes
-rw-r--r--catalog-ui/app/styles/images/sprites/sprite-product-icons.pngbin0 -> 132122 bytes
-rw-r--r--catalog-ui/app/styles/images/sprites/sprite-resource-icons.pngbin0 -> 469610 bytes
-rw-r--r--catalog-ui/app/styles/images/sprites/sprite-services-icons.pngbin0 -> 127233 bytes
-rw-r--r--catalog-ui/app/styles/images/sprites/tlv-sprite.pngbin0 -> 14677 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/10.pngbin0 -> 6707 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/11.pngbin0 -> 7697 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/13.pngbin0 -> 11072 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/14.pngbin0 -> 2768 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/15.pngbin0 -> 8250 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/16.pngbin0 -> 11421 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/17.pngbin0 -> 12030 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/18.pngbin0 -> 10696 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/19.pngbin0 -> 7678 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/2.pngbin0 -> 5480 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/20.pngbin0 -> 8421 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/21.pngbin0 -> 11205 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/22.pngbin0 -> 9610 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/23.pngbin0 -> 9229 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/24.pngbin0 -> 7842 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/25.pngbin0 -> 7579 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/26.pngbin0 -> 8364 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/27.pngbin0 -> 10243 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/28.pngbin0 -> 6838 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/29.pngbin0 -> 9140 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/3.pngbin0 -> 6717 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/30.pngbin0 -> 8680 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/4.pngbin0 -> 3154 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/5.pngbin0 -> 3233 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/6.pngbin0 -> 6294 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/7.pngbin0 -> 6574 bytes
-rw-r--r--catalog-ui/app/styles/images/tutorial/8.pngbin0 -> 8534 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome.pngbin0 -> 44721 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/SD&C_Welcome15.jpgbin0 -> 3088201 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/SD&C_Welcome15_b.jpgbin0 -> 3082618 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/001.jpgbin0 -> 278335 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/002.jpgbin0 -> 503913 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/002.pngbin0 -> 2804026 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/003.pngbin0 -> 1470296 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/004.pngbin0 -> 2906163 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/bg02.pngbin0 -> 141906 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/bg03.pngbin0 -> 148913 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/connection02.pngbin0 -> 2735 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/connection03.pngbin0 -> 7056 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/connection04.pngbin0 -> 2712 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/global.pngbin0 -> 58238 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/shape02.pngbin0 -> 163714 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/shape03.pngbin0 -> 89805 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/shape04.pngbin0 -> 127232 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/bg/shape05.pngbin0 -> 299086 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/laptop.pngbin0 -> 83877 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/logo_att.pngbin0 -> 19214 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/sprite.pngbin0 -> 3386285 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/ss-01.pngbin0 -> 3413794 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/ss-02.pngbin0 -> 67191 bytes
-rw-r--r--catalog-ui/app/styles/images/welcome/ss-03.pngbin0 -> 46870 bytes
-rw-r--r--catalog-ui/app/styles/layout/header.less78
-rw-r--r--catalog-ui/app/styles/layout/main.less124
-rw-r--r--catalog-ui/app/styles/layout/sidebar.less163
-rw-r--r--catalog-ui/app/styles/mixins.less217
-rw-r--r--catalog-ui/app/styles/mixins_old.less558
-rw-r--r--catalog-ui/app/styles/modal.less356
-rw-r--r--catalog-ui/app/styles/scroller.less15
-rw-r--r--catalog-ui/app/styles/sprite-old.less132
-rw-r--r--catalog-ui/app/styles/sprite-product-icons.less71
-rw-r--r--catalog-ui/app/styles/sprite-resource-icons.less258
-rw-r--r--catalog-ui/app/styles/sprite-services-icons.less66
-rw-r--r--catalog-ui/app/styles/sprite.html65
-rw-r--r--catalog-ui/app/styles/sprite.less212
-rw-r--r--catalog-ui/app/styles/table-flex.less178
-rw-r--r--catalog-ui/app/styles/tlv-buttons.less234
-rw-r--r--catalog-ui/app/styles/tlv-checkbox.less87
-rw-r--r--catalog-ui/app/styles/tlv-loader.less164
-rw-r--r--catalog-ui/app/styles/tlv-sprite.less138
-rw-r--r--catalog-ui/app/styles/tooltips.less110
-rw-r--r--catalog-ui/app/styles/variables-old.less77
-rw-r--r--catalog-ui/app/styles/variables.less48
-rw-r--r--catalog-ui/app/styles/welcome-sprite.less57
-rw-r--r--catalog-ui/app/styles/welcome-style.less651
-rw-r--r--catalog-ui/app/third-party/PunchOutRegistry.js98
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/.npmignore3
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml12
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee110
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/LICENSE22
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/README.md71
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/bower.json16
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js209
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js22
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/package.json80
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee209
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js27
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js26
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js32
-rw-r--r--catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee221
-rw-r--r--catalog-ui/bower dependecies29
-rw-r--r--catalog-ui/bower.json42
-rw-r--r--catalog-ui/build_catalog_ui.bat29
-rw-r--r--catalog-ui/build_catalog_ui.sh84
-rw-r--r--catalog-ui/configurations/MenuReadMe.txt60
-rw-r--r--catalog-ui/configurations/dev.json430
-rw-r--r--catalog-ui/configurations/menu.json576
-rw-r--r--catalog-ui/configurations/mock.json192
-rw-r--r--catalog-ui/configurations/prod.json432
-rw-r--r--catalog-ui/docs/colors.jpgbin0 -> 80399 bytes
-rw-r--r--catalog-ui/non_bower_components/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js554
-rw-r--r--catalog-ui/non_bower_components/cytoscape.js-edge-editation/README.md58
-rw-r--r--catalog-ui/non_bower_components/cytoscape.js-edge-editation/index.html169
-rw-r--r--catalog-ui/package.json67
-rw-r--r--catalog-ui/pom.xml231
-rw-r--r--catalog-ui/server-mock/mock-data/artifact/artifact-types.json23
-rw-r--r--catalog-ui/server-mock/mock-data/category/category.json65
-rw-r--r--catalog-ui/server-mock/mock-data/element/element.json2915
-rw-r--r--catalog-ui/server-mock/mock-data/resource/properties.json35
-rw-r--r--catalog-ui/server-mock/mock-data/resource/resource.json153
-rw-r--r--catalog-ui/server-mock/mock-data/resources/resourcesAbstract.json284
-rw-r--r--catalog-ui/server-mock/mock-data/resources/resourcesNotAbstract.json510
-rw-r--r--catalog-ui/server-mock/mock-data/template/template.json37
-rw-r--r--catalog-ui/server-mock/mock-data/user/user.json7
-rw-r--r--catalog-ui/server-mock/mock-server.js140
-rw-r--r--catalog-ui/server-mock/routes/property.js45
-rw-r--r--catalog-ui/server-mock/routes/resource.js60
-rw-r--r--catalog-ui/server-mock/routes/resources.js38
-rw-r--r--catalog-ui/server-mock/routes/template.js45
-rw-r--r--catalog-ui/server-mock/routes/user.js45
-rw-r--r--catalog-ui/tests/karma.unit.conf.js149
-rw-r--r--catalog-ui/tsd.json15
-rw-r--r--catalog-ui/tslint.json58
-rw-r--r--catalog-ui/typings/angular-ui-bootstrap/angular-ui-bootstrap.d.ts658
-rw-r--r--catalog-ui/typings/angular-ui-router/angular-ui-router.d.ts207
-rw-r--r--catalog-ui/typings/angularjs/angular-mocks.d.ts337
-rw-r--r--catalog-ui/typings/angularjs/angular-resource.d.ts183
-rw-r--r--catalog-ui/typings/angularjs/angular.d.ts1613
-rw-r--r--catalog-ui/typings/angularjs/restangular.d.ts156
-rw-r--r--catalog-ui/typings/cytoscape/cytoscape-extension.js1
-rw-r--r--catalog-ui/typings/cytoscape/cytoscape-extension.js.map1
-rw-r--r--catalog-ui/typings/cytoscape/cytoscape-extension.ts40
-rw-r--r--catalog-ui/typings/cytoscape/cytoscape.d.ts3095
-rw-r--r--catalog-ui/typings/cytoscape/edge-editation.d.ts28
-rw-r--r--catalog-ui/typings/d3/d3.d.ts3308
-rw-r--r--catalog-ui/typings/jasmine/jasmine.d.ts515
-rw-r--r--catalog-ui/typings/jquery/jquery.d.ts3181
-rw-r--r--catalog-ui/typings/jsMd5/md5.d.ts55
-rw-r--r--catalog-ui/typings/lodash/lodash.d.ts22949
-rw-r--r--catalog-ui/typings/notifyjs/notifyjs.d.ts125
-rw-r--r--catalog-ui/typings/tsd.d.ts21
-rw-r--r--common-app-api/.gitignore2
-rw-r--r--common-app-api/pom.xml209
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/BeEcompErrorManager.java431
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/CleanComponentsConfiguration.java46
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java1233
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/ConfigurationManager.java154
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/DistributionEngineConfiguration.java430
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorConfiguration.java54
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorInfo.java102
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/Neo4jErrorsConfiguration.java47
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/config/validation/DeploymentArtifactHeatConfiguration.java57
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/be/monitoring/BeMonitoringService.java140
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ApplicationErrorCodesEnum.java37
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactGroupTypeEnum.java66
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactTypeEnum.java81
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/BasicConfiguration.java25
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationListener.java50
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationSource.java28
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/Constants.java126
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/FileChangeCallback.java27
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckInfo.java76
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckWrapper.java62
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ResourceType.java25
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ResponseInfo.java84
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInfo.java79
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInterface.java37
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/UploadArtifactInfo.java147
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/UserRoleEnum.java41
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlConstants.java29
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlSuffixEnum.java60
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/AbsEcompErrorManager.java121
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompClassification.java41
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorCode.java155
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorConfiguration.java161
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorEnum.java484
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorInfo.java78
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorLogUtil.java199
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorName.java41
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompConfigurationManager.java25
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompErrorManager.java27
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/config/generation/GenerateEcompErrorsCsv.java242
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/AuditingFieldsKeysEnum.java103
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/CapList.java324
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/ESTimeBasedEvent.java125
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/FunctionalInterfaces.java282
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/MonitoringFieldsKeysEnum.java57
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/Wrapper.java52
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/impl/ConfigFileChangeListener.java148
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/impl/ExternalConfiguration.java106
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/impl/FSConfigurationSource.java121
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/impl/MutableHttpServletRequest.java73
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/kpi/api/ASDCKpiApi.java67
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/listener/AppContextListener.java129
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringEvent.java126
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringMetricsFetcher.java212
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/IRestClient.java103
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestClientServiceExeption.java78
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestConfigurationInfo.java107
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponse.java108
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponseAsByteArray.java133
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/HttpRestClientServiceImpl.java436
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/RestClientServiceFactory.java48
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RequestHeadersValidator.java95
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RestRequestValidationException.java34
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java30
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/CapabilityTypeNameEnum.java46
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/GeneralUtility.java150
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/GsonFactory.java40
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/HtmlCleaner.java112
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/HttpUtil.java81
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/JsonUtils.java45
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/MethodActivationStatusEnum.java25
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/SerializationUtils.java87
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/StreamUtils.java136
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/ThreadLocalsHolder.java63
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java498
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/YamlToObjectConverter.java275
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/common/util/ZipUtil.java178
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/AbstractSdncException.java116
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/AlreadyExistException.java41
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteLastApplicationEnvironmentException.java34
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteReferencedObjectException.java35
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/FunctionalException.java41
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/IndexingServiceException.java39
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/InvalidArgumentException.java33
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/NotFoundException.java35
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/OkResponseInfo.java28
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/PolicyException.java31
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java191
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/ServiceException.java32
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/TechnicalException.java40
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/exception/VersionConflictException.java33
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java304
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/fe/config/ConfigurationManager.java117
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/fe/config/Connection.java48
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/fe/config/FeEcompErrorManager.java77
-rw-r--r--common-app-api/src/main/java/org/openecomp/sdc/fe/monitoring/FeMonitoringService.java140
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/data_structure/CapListTest.java126
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/test/CommonUtilsTest.java549
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/test/TestExternalConfiguration.java258
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/test/YamlTest.java61
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConfiguration.java149
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConnection.java48
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestNotExistConfiguration.java47
-rw-r--r--common-app-api/src/test/java/org/openecomp/sdc/common/util/StreamUtilsTests.java143
-rw-r--r--common-app-api/src/test/resources/config/common/distribution-engine-configuration.yaml35
-rw-r--r--common-app-api/src/test/resources/config/common/test-configuration.yaml35
-rw-r--r--common-app-api/src/test/resources/logback-test.xml13
-rw-r--r--common-be/.gitignore1
-rw-r--r--common-be/pom.xml116
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/CategoryDataDefinition.java130
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/GroupingDataDefinition.java112
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/SubCategoryDataDefinition.java128
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ComponentMetadataDataDefinition.java429
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ResourceMetadataDataDefinition.java176
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ServiceMetadataDataDefinition.java87
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AdditionalInfoParameterDataDefinition.java89
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ArtifactDataDefinition.java587
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AttributeDataDefinition.java186
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityTypeDataDefinition.java128
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ComponentInstanceDataDefinition.java187
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ConsumerDataDefinition.java204
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/DataTypeDataDefinition.java119
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupDataDefinition.java142
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupTypeDataDefinition.java163
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/HeatParameterDataDefinition.java162
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InputsValueDataDefinition.java63
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InterfaceDataDefinition.java109
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/OperationDataDefinition.java96
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PolicyTypeDataDefinition.java163
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ProductMetadataDataDefinition.java126
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyDataDefinition.java195
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyRule.java136
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipTypeDataDefinition.java128
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SchemaDefinition.java139
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/AssetTypeEnum.java53
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ComponentTypeEnum.java104
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/FilterKeyEnum.java55
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/GroupTypeEnum.java35
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java92
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/OriginTypeEnum.java69
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ResourceTypeEnum.java49
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/utils/CommonBeUtils.java61
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/workers/Job.java29
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/workers/Main.java50
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/workers/Manager.java85
-rw-r--r--common-be/src/main/java/org/openecomp/sdc/be/workers/Worker.java70
-rw-r--r--common-be/src/test/java/org/openecomp/sdc/be/utils/CommonBeUtilsTest.java42
-rw-r--r--distribution-ci/.gitignore1
-rw-r--r--distribution-ci/etc/asdc-client.jksbin0 -> 1177 bytes
-rw-r--r--distribution-ci/etc/asdcclientstore.jksbin0 -> 907 bytes
-rw-r--r--distribution-ci/log4j.properties17
-rw-r--r--distribution-ci/pom.xml64
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/BaseInit.java122
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/CallableTask.java57
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/ClientTest.java33
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/E2eFlows.java108
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/NotificationCallback.java19
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/SimpleCallback.java275
-rw-r--r--distribution-ci/src/main/java/org/openecomp/test/SimpleConfiguration.java149
-rw-r--r--distribution-ci/src/main/resources/ci/conf/log4j.properties34
-rw-r--r--distribution-ci/src/main/resources/ci/conf/sdc-packages.yaml10
-rw-r--r--distribution-ci/src/main/resources/ci/conf/sdc.yaml35
-rw-r--r--distribution-ci/src/main/resources/ci/conf/titan.properties5
-rw-r--r--distribution-ci/src/main/resources/ci/scripts/startTest.sh87
-rw-r--r--distribution-ci/src/test/java/org/openecomp/sdc/ci/tests/execute/downloadArtifactUGN/ClientDownloadArtifact.java491
-rw-r--r--pom.xml833
-rw-r--r--sdc-os-chef/docker-compose.yml156
-rw-r--r--sdc-os-chef/environments/Template.json75
-rw-r--r--sdc-os-chef/pom.xml46
-rw-r--r--sdc-os-chef/scripts/docker_clean.sh6
-rw-r--r--sdc-os-chef/scripts/docker_health.sh22
-rw-r--r--sdc-os-chef/scripts/docker_login.sh8
-rw-r--r--sdc-os-chef/scripts/docker_run.sh128
-rw-r--r--sdc-os-chef/scripts/docker_stats.sh36
-rw-r--r--sdc-os-chef/scripts/docker_watchdog.sh43
-rw-r--r--sdc-os-chef/scripts/restart_docker.sh97
-rw-r--r--sdc-os-chef/sdc-backend/Dockerfile.template30
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/attributes/default.rb1
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-ecomp-error-configuration.yaml383
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-error-configuration.yaml1694
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-logback.xml227
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/consumers.py89
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/user.py92
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_1_cleanup_jettydir.rb49
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_2_locate_wars.rb26
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_3_create_DMaaP_keys.rb34
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_4_setup_configuration.rb43
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_5_setup_elasticsearch.rb15
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_6_setup_portal_properties.rb17
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_7_logback.rb7
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_8_errors_config.rb14
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_9_import_Normatives.rb16
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-configuration.yaml.erb459
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-distribution-engine-configuration.yaml.erb44
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-elasticsearch.yml.erb11
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-portal.properties.erb32
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-titan.properties.erb24
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/attributes/default.rb1
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/check_Backend_Health.py51
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/get-pip.py17760
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gzbin0 -> 49376 bytes
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/check_Backend.rb19
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/import_Normatives.rb16
-rw-r--r--sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/upgrade_Normatives.rb17
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/LICENSE201
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/README.md37
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/chefignore11
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/cookbooks/README.md54
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/data_bags/README.md63
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/environments/README.md5
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/normatives.json4
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/normatives.rb16
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/roles/README.md16
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/roles/catalog-be.json24
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/solo.json4
-rw-r--r--sdc-os-chef/sdc-backend/chef-solo/solo.rb16
-rw-r--r--sdc-os-chef/sdc-backend/startup.sh40
-rw-r--r--sdc-os-chef/sdc-cassandra/Dockerfile22
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/01-configureCassandra.rb49
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/02-createCsUser.rb8
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/03-createDoxKeyspace.rb8
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/04-schemaCreation.rb35
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra-rackdc.properties.erb27
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra.yaml.erb824
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/configuration.yaml.erb459
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_cassandra_user.sh.erb57
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_dox_keyspace.sh.erb77
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/elasticsearch.yml.erb11
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/LICENSE201
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/README.md37
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/chefignore11
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/cookbooks/README.md54
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/data_bags/README.md63
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/environments/README.md5
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/roles/README.md16
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/roles/cassandra-actions.json23
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/solo.json6
-rw-r--r--sdc-os-chef/sdc-cassandra/chef-solo/solo.rb16
-rw-r--r--sdc-os-chef/sdc-cassandra/startup.sh27
-rw-r--r--sdc-os-chef/sdc-elasticsearch/Dockerfile21
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/attributes/default.rb1
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_BI-Dashboard.json13
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_Monitoring-Dashboared.json15
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/logging.yml23
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-CPU.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Memory.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Threads-Num.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-certified-services-ampersand-resources-(per-day).json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-created-Resources-slash-Services-slash-Products.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-distributed-services.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-CPU.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-Threads-Num.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_number-of-user-accesses.json10
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_1_setup_elasticsearch.rb16
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_2_setup_logging.rb6
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_3_create_audit_template.rb238
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_4_create_resources_template.rb37
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_5_create_monitoring_template.rb47
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_6_kibana_dashboard_virtualization.rb57
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/templates/default/ES-elasticsearch.yml.erb16
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/LICENSE201
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/README.md37
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/chefignore11
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/cookbooks/README.md54
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/data_bags/README.md63
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/environments/README.md5
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/roles/README.md16
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/roles/elasticsearch.json19
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/solo.json4
-rw-r--r--sdc-os-chef/sdc-elasticsearch/chef-solo/solo.rb16
-rw-r--r--sdc-os-chef/sdc-elasticsearch/startup.sh19
-rw-r--r--sdc-os-chef/sdc-frontend/Dockerfile.template26
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/attributes/default.rb2
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-ecomp-error-configuration.yaml48
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-logback.xml227
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-rest-configuration.yaml11
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_1_cleanup_jettydir.rb49
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_2_setup_configuration.rb13
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_3_errors_config.rb7
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_4_logback.rb7
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_5_rest_configuration.rb7
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_create_jetty_modules.rb45
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-configuration.yaml.erb79
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-http-ini.erb32
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-https-ini.erb15
-rw-r--r--sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-ssl-ini.erb83
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/LICENSE201
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/README.md37
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/chefignore11
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/cookbooks/README.md54
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/data_bags/README.md63
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/environments/README.md5
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/roles/README.md16
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/roles/catalog-fe.json23
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/solo.json4
-rw-r--r--sdc-os-chef/sdc-frontend/chef-solo/solo.rb16
-rw-r--r--sdc-os-chef/sdc-frontend/startup.sh12
-rw-r--r--sdc-os-chef/sdc-kibana/Dockerfile18
-rw-r--r--sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/attributes/default.rb1
-rw-r--r--sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/recipes/setup_kibana.rb19
-rw-r--r--sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/templates/default/kibana.yml.erb74
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/LICENSE201
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/README.md37
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/chefignore11
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/cookbooks/README.md54
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/data_bags/README.md63
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/environments/README.md5
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/roles/README.md16
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/solo.json4
-rw-r--r--sdc-os-chef/sdc-kibana/chef-solo/solo.rb16
-rw-r--r--sdc-os-chef/sdc-kibana/startup.sh7
-rw-r--r--sdc-os-chef/sdc-sanity/Dockerfile20
-rw-r--r--sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/attributes/default.rb1
-rw-r--r--sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/recipes/setup_sanity.rb68
-rw-r--r--sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/BE-titan.properties.erb24
-rw-r--r--sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/sdc-sanity.yaml.erb82
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/LICENSE201
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/README.md37
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/chefignore11
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/cookbooks/README.md54
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/data_bags/README.md63
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/environments/README.md5
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/roles/README.md16
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/solo.json4
-rw-r--r--sdc-os-chef/sdc-sanity/chef-solo/solo.rb16
-rw-r--r--sdc-os-chef/sdc-sanity/startup.sh17
-rw-r--r--security-utils/.gitignore1
-rw-r--r--security-utils/pom.xml22
-rw-r--r--security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java163
-rw-r--r--security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java49
-rw-r--r--ui-ci-dev/.gitignore2
-rw-r--r--ui-ci-dev/pom.xml255
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/scripts/CreateVfsFromOnboarding.java67
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasElement.java33
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasManager.java206
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CleanTypeEnum.java28
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndImportButtonsEnum.java7
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndUpdateStepsEnum.java25
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/DataTestIdEnum.java428
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/MenuOptionsEnum.java17
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/UserCredentials.java29
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/base/SetupCDTest.java408
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceBasicTests.java147
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceInputsTests.java124
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfBasicTests.java234
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfCanvasTests.java80
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfDeploymentTests.java340
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfOnboardingTests.java63
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vfc/VfcBasicTests.java219
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/run/StartTest.java394
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ArtifactUIUtils.java70
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/FileHandling.java30
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/GeneralUIUtils.java346
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/MethodManipulationUtils.java15
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/OnboardUtility.java477
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ResourceUIUtils.java299
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/RestCDUtils.java167
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ServiceUIUtils.java147
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/ServiceVerificator.java55
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VerificatorUtil.java27
-rw-r--r--ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VfVerificator.java143
-rw-r--r--ui-ci-dev/src/main/resources/Files/CP.yml65
-rw-r--r--ui-ci-dev/src/main/resources/Files/CP_LAN - Copy.yml13
-rw-r--r--ui-ci-dev/src/main/resources/Files/CP_LAN.yml19
-rw-r--r--ui-ci-dev/src/main/resources/Files/CP_WAN.yml19
-rw-r--r--ui-ci-dev/src/main/resources/Files/Heat-File 1.yaml791
-rw-r--r--ui-ci-dev/src/main/resources/Files/Heat-File 2.yaml791
-rw-r--r--ui-ci-dev/src/main/resources/Files/Heat-File.yaml791
-rw-r--r--ui-ci-dev/src/main/resources/Files/InValid_tosca_File .yml34
-rw-r--r--ui-ci-dev/src/main/resources/Files/JDM_vf.yml57
-rw-r--r--ui-ci-dev/src/main/resources/Files/JDM_vfc.yml57
-rw-r--r--ui-ci-dev/src/main/resources/Files/Sample_CSAR.csarbin0 -> 1094 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/Sample_CSAR2.csarbin0 -> 1085 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/UCPE_VFC.yml65
-rw-r--r--ui-ci-dev/src/main/resources/Files/VF.yml17
-rw-r--r--ui-ci-dev/src/main/resources/Files/VFC.yml77
-rw-r--r--ui-ci-dev/src/main/resources/Files/VFCWithAttributes.yml43
-rw-r--r--ui-ci-dev/src/main/resources/Files/VL.yml17
-rw-r--r--ui-ci-dev/src/main/resources/Files/Valid xml.xml4
-rw-r--r--ui-ci-dev/src/main/resources/Files/Valid_tosca_Mycompute.yml35
-rw-r--r--ui-ci-dev/src/main/resources/Files/Valid_tosca_ReplaceTest.yml35
-rw-r--r--ui-ci-dev/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env6
-rw-r--r--ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.env18
-rw-r--r--ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml108
-rw-r--r--ui-ci-dev/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml80
-rw-r--r--ui-ci-dev/src/main/resources/Files/myYang.xml8
-rw-r--r--ui-ci-dev/src/main/resources/Files/mycompute.yml18
-rw-r--r--ui-ci-dev/src/main/resources/Files/service_with_inputs.csarbin0 -> 35388 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/vADTRAN.zipbin0 -> 2499 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/vCDN.zipbin0 -> 4547 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/vFW_VF.yml58
-rw-r--r--ui-ci-dev/src/main/resources/Files/vFW_VFC.yml58
-rw-r--r--ui-ci-dev/src/main/resources/Files/vRouter_vfc.yml78
-rw-r--r--ui-ci-dev/src/main/resources/Files/valid HEAT_ENV files.env54
-rw-r--r--ui-ci-dev/src/main/resources/Files/validHEATfiles.yaml787
-rw-r--r--ui-ci-dev/src/main/resources/Files/valid_vf.csarbin0 -> 1316 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/vf_with_groups.csarbin0 -> 25253 bytes
-rw-r--r--ui-ci-dev/src/main/resources/Files/yamlSample.yml5
-rw-r--r--ui-ci-dev/src/main/resources/Files/yamlSample2.yml5
-rw-r--r--ui-ci-dev/src/main/resources/ci/conf/credentials.yaml48
-rw-r--r--ui-ci-dev/src/main/resources/ci/conf/log4j.properties34
-rw-r--r--ui-ci-dev/src/main/resources/ci/conf/sdc-packages.yaml2
-rw-r--r--ui-ci-dev/src/main/resources/ci/conf/sdc.yaml80
-rw-r--r--ui-ci-dev/src/main/resources/ci/conf/titan.properties7
-rw-r--r--ui-ci-dev/src/main/resources/ci/scripts/startTest.sh123
-rw-r--r--ui-ci-dev/src/main/resources/ci/testSuites/fullTests.xml26
-rw-r--r--ui-ci-dev/src/main/resources/ci/testSuites/sanity.xml55
-rw-r--r--ui-ci-dev/src/main/resources/images/gizmorambo.jpgbin0 -> 16605 bytes
-rw-r--r--ui-ci-dev/src/test/Completetheform.js3
-rw-r--r--ui-ci-dev/tarball.xml66
-rw-r--r--ui-ci/.gitignore3
-rw-r--r--ui-ci/pom.xml260
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactInfo.java85
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/BreadCrumbsButtonsEnum.java38
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasElement.java65
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasManager.java250
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CatalogFilterTitlesEnum.java37
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CheckBoxStatusEnum.java50
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndImportButtonsEnum.java27
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndUpdateStepsEnum.java45
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/DataTestIdEnum.java380
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GeneralCanvasItemsEnum.java39
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/LifeCycleStateEnum.java46
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/MenuOptionsEnum.java45
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyInfo.java76
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceCategoriesNameEnum.java63
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceCategoriesNameEnum.java39
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypesEnum.java43
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/UserCredentials.java57
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/WorkMode.java25
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/environmentLocal10
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/adminworkspace/AdminUserManagment.java141
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogLeftFilterPanelCheckBoxTest.java272
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogSearchBoxTest.java130
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ChangeLifeCycleStatFromCatalogTest.java103
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ImportAssetInUITest.java480
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFCanvasTest.java91
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFUITest.java37
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VfTests.java33
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/WorkspaceCheckBoxFilterTest.java120
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Import.java177
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Onboard.java222
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Vf.java377
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/OnboardCSVReport.java63
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/RemoteWebDriverTest.java45
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/SetupCDTest.java538
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ComponentLeftMenu.java25
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/CompositionPage.java56
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/DeploymentArtifactPage.java91
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GeneralPageElements.java139
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GovernorOperationPage.java46
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/IconPage.java51
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/InformationalArtifactPage.java57
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/OpsOperationPage.java183
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertiesPage.java61
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertyPopup.java77
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceGeneralPage.java154
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceLeftMenu.java63
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ServiceGeneralPage.java47
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/TesterOperationPage.java71
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ToscaArtifactsPage.java35
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/UploadArtifactPopup.java76
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdditionalConditions.java84
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdminWorkspaceUIUtilies.java71
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ArtifactUIUtils.java302
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AuditCDUtils.java67
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasElement.java54
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasManager.java181
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CaptureFailedTests.java54
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CatalogUIUtilitis.java148
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/FileHandling.java88
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/GeneralUIUtils.java1081
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ImportAssetUIUtils.java50
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/OnboardingUtils.java457
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/PropertiesUIUtils.java92
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ResourceUIUtils.java864
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/RestCDUtils.java182
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ServiceUIUtils.java241
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/ServiceVerificator.java60
-rw-r--r--ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/VfVerificator.java93
-rw-r--r--ui-ci/src/main/resources/Downloads/CP_WAN.yml23
-rw-r--r--ui-ci/src/main/resources/Downloads/Fortigate02_vFW_VFC.yml63
-rw-r--r--ui-ci/src/main/resources/Files/CP.yml65
-rw-r--r--ui-ci/src/main/resources/Files/CPWithAttributes.yml78
-rw-r--r--ui-ci/src/main/resources/Files/CP_LAN - Copy.yml12
-rw-r--r--ui-ci/src/main/resources/Files/CP_LAN.yml19
-rw-r--r--ui-ci/src/main/resources/Files/CP_WAN.yml19
-rw-r--r--ui-ci/src/main/resources/Files/DNSscaling12.8.16.zipbin0 -> 2868 bytes
-rw-r--r--ui-ci/src/main/resources/Files/Heat-File 1.yaml791
-rw-r--r--ui-ci/src/main/resources/Files/Heat-File 2.yaml791
-rw-r--r--ui-ci/src/main/resources/Files/Heat-File.yaml791
-rw-r--r--ui-ci/src/main/resources/Files/InValid_tosca_File .yml34
-rw-r--r--ui-ci/src/main/resources/Files/JDM_vf.yml57
-rw-r--r--ui-ci/src/main/resources/Files/JDM_vfc.yml57
-rw-r--r--ui-ci/src/main/resources/Files/Sample_CSAR.csarbin0 -> 1085 bytes
-rw-r--r--ui-ci/src/main/resources/Files/UCPE_VFC.yml65
-rw-r--r--ui-ci/src/main/resources/Files/VF.yml17
-rw-r--r--ui-ci/src/main/resources/Files/VFC.yml77
-rw-r--r--ui-ci/src/main/resources/Files/VL.yml18
-rw-r--r--ui-ci/src/main/resources/Files/Valid xml.xml4
-rw-r--r--ui-ci/src/main/resources/Files/Valid_tosca_Mycompute.yml35
-rw-r--r--ui-ci/src/main/resources/Files/Valid_tosca_ReplaceTest.yml35
-rw-r--r--ui-ci/src/main/resources/Files/asc_heat 0 2.yaml787
-rw-r--r--ui-ci/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env6
-rw-r--r--ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.env18
-rw-r--r--ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml108
-rw-r--r--ui-ci/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml80
-rw-r--r--ui-ci/src/main/resources/Files/sample-xml-alldata-1-1.xml298
-rw-r--r--ui-ci/src/main/resources/Files/vFW_8.12.16.zipbin0 -> 3650 bytes
-rw-r--r--ui-ci/src/main/resources/Files/vFW_VF.yml58
-rw-r--r--ui-ci/src/main/resources/Files/vFW_VFC.yml58
-rw-r--r--ui-ci/src/main/resources/Files/vLB12.8.16.zipbin0 -> 3484 bytes
-rw-r--r--ui-ci/src/main/resources/Files/vRouter_vfc.yml78
-rw-r--r--ui-ci/src/main/resources/Files/valid HEAT_ENV files.env54
-rw-r--r--ui-ci/src/main/resources/Files/validHEATfiles.yaml787
-rw-r--r--ui-ci/src/main/resources/ci/conf/credentials.yaml48
-rw-r--r--ui-ci/src/main/resources/ci/conf/log4j.properties34
-rw-r--r--ui-ci/src/main/resources/ci/conf/sdc-packages.yaml2
-rw-r--r--ui-ci/src/main/resources/ci/conf/sdc.yaml57
-rw-r--r--ui-ci/src/main/resources/ci/conf/titan.properties7
-rw-r--r--ui-ci/src/main/resources/ci/scripts/startTest.sh96
-rw-r--r--ui-ci/src/main/resources/ci/testSuites/ui-ci.xml16
-rw-r--r--ui-ci/src/main/resources/images/gizmorambo.jpgbin0 -> 16605 bytes
-rw-r--r--ui-ci/src/test/Completetheform.js23
-rw-r--r--ui-ci/tarball.xml60
-rw-r--r--webseal-simulator/README.txt18
-rw-r--r--webseal-simulator/pom.xml126
-rw-r--r--webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/Login.java131
-rw-r--r--webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MethodEnum.java10
-rw-r--r--webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MutableHttpServletRequest.java55
-rw-r--r--webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/SdcProxy.java318
-rw-r--r--webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/User.java64
-rw-r--r--webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/conf/Conf.java67
-rw-r--r--webseal-simulator/src/main/resources/webseal.conf64
-rw-r--r--webseal-simulator/src/main/webapp/WEB-INF/jetty-web.xml8
-rw-r--r--webseal-simulator/src/main/webapp/WEB-INF/web.xml32
-rw-r--r--webseal-simulator/src/main/webapp/login.html18
3438 files changed, 556530 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000..da39ab03ca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,99 @@
+# Eclipse
+.classpath
+.project
+.settings/
+
+# Maven
+log/
+target/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# Other
+*.class
+*.orig
+catalog-ui/.*
+catalog-ui/app/scripts/tsconfig.json
+catalog-ui/app/scripts/references.ts
+catalog-ui/app/scripts/**/*.js
+catalog-ui/app/scripts/**/*.js.map
+
+# catalog-ui
+catalog-ui/app/**/*.css
+catalog-ui/.editorconfig
+catalog-ui/.jshintrc
+catalog-ui/.yo-rc.json
+catalog-ui/app/robots.txt
+catalog-ui/node_modules/
+catalog-ui/bower_components/
+catalog-ui/app/tsconfig.json
+
+
+catalog-be/.checkstyle
+catalog-be/bin/
+catalog-be/src/main/java/META-INF/MANIFEST.MF
+catalog-builders/src/test/java/com/att/tlv/sdc/be/builders/tosca/t1
+a4c/
+catalog-ui/*.tmp.txt
+catalog-dao/bin/
+catalog-ui/dist
+catalog-builders/bin/
+common-app-api/bin/
+data/compute.png
+data/loadbalancer.png
+data/mysql.png
+data/network.png
+data/normative-types-blockStorage.yml
+data/normative-types-compute.yml
+data/normative-types-database.yml
+data/normative-types-DBMS.yml
+data/normative-types-network.yml
+data/normative-types-objectStorage.yml
+data/normative-types-root.yml
+data/normative-types-softwareComponent.yml
+data/normative-types-webApplication.yml
+data/normative-types-webServer.yml
+data/objectstore.png
+data/relational_db.png
+data/root.png
+data/router.png
+data/software.png
+data/volume.png
+catalog-ui/app/dist/*
+catalog-fe/src/main/webapp/*
+catalog-fe/bin
+!catalog-fe/src/main/webapp/WEB-INF/
+!catalog-fe/src/main/webapp/META-INF/
+ui-ci/.tern-project
+catalog-be/data/*
+ci/src/main/java/com/att/tlv/sdc/ci/tests/execute/ShayPracticeTests.java
+ci/src/main/java/com/att/tlv/sdc/ci/tests/execute/ShayPracticeTests.java
+.metadata/*
+.metadata/*
+.idea/*
+*.iml
+catalog-ui/npm-debug.log
+sdnc-tests/logs/wordnik.log
+distribution-ci/logs/*
+vagrant-asdc-all-in-one/
+catalog-ui/tests/Coverage/*
+catalog-ui/Chrome*
+catalog-ui/PhantomJS_1.9.8_(Windows_7_0.0.0)/*
+
+.metadata/
+bin/
+Vagrantfile
+sdnc-tests/test-output/*
+sdnc-tests-new-frame-work/test-output/*
+ui-ci/test-output/*
+
+
+sdnc-tests-new-frame-work/test-output/**
+asdc-tests/test-output/**
+
+asdc-chef/cookbooks/Deploy-SDandC/attributes/version.rb
+asdc-tests/ExtentReport/ASDC_CI_Extent_Report.html
+
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000000..921054bdf2
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.openecomp.org
+port=29418
+project=sdc.git \ No newline at end of file
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 0000000000..40e9f40a0a
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?><pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+</pydev_project>
diff --git a/LICENSE.TXT b/LICENSE.TXT
new file mode 100644
index 0000000000..649de64348
--- /dev/null
+++ b/LICENSE.TXT
@@ -0,0 +1,24 @@
+/*
+* ============LICENSE_START==========================================
+* ===================================================================
+* Copyright © 2017 AT&T Intellectual Property.
+* Copyright © 2017 Amdocs
+* All rights reserved.
+* ===================================================================
+* 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.
+* ============LICENSE_END============================================
+*
+* ECOMP and OpenECOMP are trademarks
+* and service marks of AT&T Intellectual Property.
+*
+*/ \ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000..297457a339
--- /dev/null
+++ b/README.md
@@ -0,0 +1,165 @@
+# OpenECOMP SDC
+
+---
+---
+
+
+# Introduction
+
+OpenECOMP SDC is delivered with 5 Docker containers:
+1. sdc-FE - frontend SDC application running on jetty server
+2. sdc-BE - backend SDC application running on jetty server
+3. sdc-kbn - hosting kibana application
+4. sdc-cs - hosting cassandra
+5. sdc-es - hosting elastic search
+
+All containers runs on the same machine and can be started by runnin the command:
+/data/scripts/docker_run.sh -e <environment name> -r <release> -p <docker-hub-port>
+Example: /data/scripts/docker_run.sh -e OS-ETE-DFW -p 51220
+
+
+# Compiling SDC
+
+SDC is built from several projects, while "sdc-main" contains the main pom.xml for all project:
+- catalog-be - backend code
+- catalog-fe - frontend java code (servlet, proxy)
+- catalog-dao - database layer
+- catalog-model - data model of the application
+- catalog-ui - front end code (javascript, html, css)
+- common-app-api - common code for frontend and backend
+- common-be - utilities, datatypes and enums
+- security-utils - handle encryption/decryption of passwords
+
+SDC projects can be compiled easily using maven command: `mvn clean install`.
+In order to build all projects, enter to sdc-main project and run the command: `mvn clean install`.
+By default unit test will run when compiling
+
+** igor **
+Docker containers are build with the following profile
+`-P docker -Ddocker.buildArg.chef_repo_branch_name=bugfix/external_adress -Ddocker.buildArg.chef_repo_git_username=git -Ddocker.buildArg.chef_repo_address=23.253.149.175/SDC -Ddocker.buildArg.chef_repo_git_name=chef-repo`
+
+
+# Getting the containers
+
+***to be changed for rrelease*** OpenECOMP SDC containers are stored on the Rackspace Nexus Docker Registry
+
+The following Docker images are the actual deployment images used for running SDC
+
+| Name | Tag | Description |
+|---------|-----------|-------------------------------------------------------------------------------------------------------------------------------|
+| sdc-FE | 1610.2.16 | Contains Jetty + OpenJDK + SDC frontend code + **3rd party jars** |
+| sdc-BE | 1610.2.16 | Contains Jetty + OpenJDK + SDC backend code + **3rd party jars** |
+| sdc-kbn | 1610.2.16 | Contains nodeJs + Kibana application |
+| sdc-cs | 1610.2.16 | OpenJDK + Contains cassandra application |
+| sdc-es | 1610.2.16 | Elastic search application |
+
+
+*********************** Israel ************************
+# Starting SDC
+There are several ways to start OpenECOMP SDC:
+TBD - Israel
+
+# Accessing SDC
+SDC UI can be accessed from:
+
+### Ecomp portal
+Login to ecomp portal URL with user that has permission for SDC application.
+Define in your hosts file the following:
+<ip address of SDC application> sdc.api.simpledemo.openecomp.org
+<ip address of Ecomp portal URL> portal.api.simpledemo.openecomp.org
+Open browser and navigate to: http://portal.api.simpledemo.openecomp.org:8989/ECOMPPORTAL/login.htm
+
+### Webseal simulator
+This options is for developers to run locally SDC
+1. Build web simulator WAR file: run `mvn clean install` on project "webseal simulator". This will generate war file (WSSimulator.war) in the target folder.
+2. Copy the war to: /home/vagrant/webseal-simulator/webapps
+3. Add users to simulator: open configuration file - /home/vagrant/webseal-simulator/config/webseal.conf and add new user to the user list.
+ Note: You need to define the user in the SDC as well.
+4. Restart the simulator:
+ Stop the simulator: stopWebsealSimulator.sh
+ Start the simulator: startWebsealSimulator.sh
+5. Enter to UI: http://<ip address>:8285/sdc1/login
+
+### SDC import normatives from CLI
+SDC needs to work with predefined basic normatives, in order to update the database with the normatives need to:
+1. From catalog-be project copy:
+ src\main\resources\import\tosca -> to <machine ip address>:catalog-be/import/tosca
+ src\main\resources\scripts\import\tosca ->to <machine ip address>:catalog-be/scripts/import/tosca
+2. cd catalog-be/scripts/import/tosca
+3. Run: python importNormativeAll.py
+4. Wait until all normatives are loaded to the database
+
+
+### SDC APIs
+TBD
+
+##### Main API endpoints in the first open source release
+
+- ***to be completed*** APIHandler health checks
+TBD
+
+# Configuration of SDC
+TBD
+
+Here are the main parameters you could change:
+TBD
+
+The credentials are defined in 2 places:
+TBD
+
+# Logging
+TBD
+
+### Jetty
+TBD
+
+### Debuging
+TBD
+
+# Testing SDC Functionalities
+TBD
+
+### Frontend Local Env - onboarding
+
+Steps:
+------
+Install nodejs & gulp
+1. download nodejs from here: https://nodejs.org/en/ (take the "current" version with latest features) & install it.
+2. install gulp by running the following command: npm install --global gulp-cli
+
+Install DOX-UI a:
+-----------------
+1. pull for latest changes
+2. go to folder dox-sequence-diagram-ui
+3. run npm install
+4. wait for it...
+5. go to folder dox-ui
+6. run npm install
+7. create a copy of devConfig.defaults.json file and name it devConfig.json (we already configured git to ignore it so it will not be pushed)
+8. in that file, populate the fields of the IP addresses of your BE machine you'd like to connect (pay attention, it is a JSON file): For example http://<host>:<port>
+9. after everything was successful, run gulp
+10. after server was up, your favorite UI will wait for you at: http://localhost:9000/sdc1/proxy-designer1#/onboardVendor
+
+Troubleshooting:
+----------------
+| Problem | Why is this happening | Solution |
+--------------------------------------------------------------------------------------------------------------------------------------------------------
+| npm cannot reach destination | onboarding proxy | When within onboarding network, you should set onboarding proxy to NPM as the following: |
+| | | npm config set proxy http://genproxy:8080 |
+| | | npm config set https-proxy http://genproxy:8080 |
+| | | |
+| git protocol is blocked | onboarding network | When within onboarding network, you should set globally that when git |
+| and cannot connect | rules for protocols | protocol is used, then it will be replaced with "https" |
+| | | git config --global url."https://".insteadOf git:// |
+--------------------------------------------------------------------------------------------------------------------------------------------------------
+
+# Getting Help
+
+*** to be completed on rrelease ***
+
+SDC@lists.openecomp.org
+
+SDC Javadoc and Maven site
+
+*** to be completed on rrelease ***
+
diff --git a/asdc-tests/.gitignore b/asdc-tests/.gitignore
new file mode 100644
index 0000000000..9b563712b6
--- /dev/null
+++ b/asdc-tests/.gitignore
@@ -0,0 +1,4 @@
+/target
+/build/
+/bin/
+/target/
diff --git a/asdc-tests/pom.xml b/asdc-tests/pom.xml
new file mode 100644
index 0000000000..ce346ae901
--- /dev/null
+++ b/asdc-tests/pom.xml
@@ -0,0 +1,367 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>asdc-tests</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.10</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.relevantcodes</groupId>
+ <artifactId>extentreports</artifactId>
+ <version>2.41.0</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.10.19</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.typesafe</groupId>
+ <artifactId>config</artifactId>
+ <version>1.0.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.17</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.jcraft.jsch</groupId>
+ <artifactId>com.springsource.com.jcraft.jsch</artifactId>
+ <version>0.1.41</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>common-be</artifactId>
+ <version>${common-be.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- CHECK CATLOG MODEL -->
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-model</artifactId>
+ <version>${catalog-model.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- catalog dao -->
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-dao</artifactId>
+ <version>${catalog-dao.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- CASSANDRA -->
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-core</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- CASSANDRA END -->
+
+
+ <!-- slf4j + logback -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- File changes listener -->
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Gson -->
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Snake Yaml -->
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.5</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http core -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <version>1.9.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-distribution-client</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.ecompsdkos</groupId>
+ <artifactId>ecompFW</artifactId>
+ <version>${ecomp.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20090211</version>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <!-- ============================================= -->
+ <!-- Create the JAR file with its dependencies -->
+ <!-- ============================================= -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.5.5</version>
+ <executions>
+ <execution>
+ <id>create.jar.with.dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.openecomp.sdc.ci.tests.run.StartTest</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <configuration>
+ <finalName>${project.artifactId}</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptor>${project.basedir}/tarball.xml</descriptor>
+ </configuration>
+ <id>assemble-file</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>3.0.2</version>
+
+ <executions>
+ <execution>
+ <id>copy-asdc-tests</id>
+ <phase>install</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.parent.basedir}/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/files/default</outputDirectory>
+ <resources>
+ <resource>
+ <directory>./target</directory>
+ <includes>
+ <include>asdc-tests.tar</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+</project>
+
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java
new file mode 100644
index 0000000000..370c195f53
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentBaseTest.java
@@ -0,0 +1,348 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.api;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.thinkaurelius.titan.core.TitanGraphQuery;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.cassandra.CassandraUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.ITestResult;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
+
+import com.relevantcodes.extentreports.ExtentReports;
+import com.relevantcodes.extentreports.ExtentTest;
+import com.relevantcodes.extentreports.LogStatus;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+
+public abstract class ComponentBaseTest {
+
+ private static Logger logger = LoggerFactory.getLogger(ComponentBaseTest.class.getName());
+
+ // public ComponentBaseTest(TestName testName, String className) {
+ // super(testName, className);
+ // }
+
+ protected static ExtentReports extentReport;
+ protected static ExtentTest extendTest;
+ public static final String REPORT_FOLDER = "./ExtentReport/";
+ private static final String REPORT_FILE_NAME = "ASDC_CI_Extent_Report.html";
+ protected static TitanGraph titanGraph;
+ private static Config myconfig;
+ public static Config config;
+
+ public static enum ComponentOperationEnum {
+ CREATE_COMPONENT, UPDATE_COMPONENT, GET_COMPONENT, DELETE_COMPONENT, CHANGE_STATE_CHECKIN, CHANGE_STATE_CHECKOUT, CHANGE_STATE_UNDO_CHECKOUT
+ };
+
+ public ComponentBaseTest(TestName name, String name2) {
+ // TODO Auto-generated constructor stub
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ lc.getLogger("com.thinkaurelius").setLevel(Level.INFO);
+ lc.getLogger("com.datastax").setLevel(Level.INFO);
+ lc.getLogger("io.netty").setLevel(Level.INFO);
+ lc.getLogger("c.d").setLevel(Level.INFO);
+ }
+
+ @BeforeSuite(alwaysRun = true)
+ public static void openTitan() throws FileNotFoundException {
+
+ File dir = new File(REPORT_FOLDER);
+ try {
+ FileUtils.deleteDirectory(dir);
+ } catch (IOException e) {
+ }
+ extentReport = new ExtentReports(REPORT_FOLDER + REPORT_FILE_NAME);
+
+ myconfig = Utils.getConfig();
+ config = Utils.getConfig();
+ logger.trace(config.toString());
+ String titanConfigFilePath = myconfig.getTitanPropertiesFile();
+ System.out.println("titan configuration path:\n"+titanConfigFilePath);
+ titanGraph = TitanFactory.open(titanConfigFilePath);
+ System.out.println("is open:\n"+titanGraph.isOpen());
+ assertNotNull(titanGraph);
+ }
+
+ @AfterSuite(alwaysRun = true)
+ public static void shutdownTitan() {
+ if (titanGraph.isOpen()) {
+ titanGraph.close();
+ }
+ CassandraUtils.close();
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void beforeState(java.lang.reflect.Method method) throws Exception {
+
+ cleanComponents();
+ CassandraUtils.truncateAllKeyspaces();
+ extendTest = extentReport.startTest(method.getName());
+ extendTest.log(LogStatus.INFO, "Test started");
+
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void afterState(ITestResult result) throws Exception {
+ cleanComponents();
+ CassandraUtils.truncateAllKeyspaces();
+
+ if (result.isSuccess()) {
+ extendTest.log(LogStatus.PASS, "Test Result : <span class='label success'>Success</span>");
+ } else {
+ extendTest.log(LogStatus.ERROR, "ERROR - The following exepction occured");
+ extendTest.log(LogStatus.ERROR, result.getThrowable());
+ extendTest.log(LogStatus.FAIL, "<span class='label failure'>Failure</span>");
+ }
+
+ extentReport.endTest(extendTest);
+ extentReport.flush();
+
+ }
+
+ public void verifyErrorCode(RestResponse response, String action, int expectedCode) {
+ assertNotNull("check response object is not null after " + action, response);
+ assertNotNull("check error code exists in response after " + action, response.getErrorCode());
+ assertEquals("Check response code after + action" + action, expectedCode, response.getErrorCode().intValue());
+ }
+
+ private void cleanComponents() throws Exception {
+
+ // Components to delete
+ List<String> vfResourcesToDelete = new ArrayList<String>();
+ List<String> nonVfResourcesToDelete = new ArrayList<String>();
+ List<String> servicesToDelete = new ArrayList<String>();
+ List<String> productsToDelete = new ArrayList<String>();
+
+ // Categories to delete
+ List<ImmutableTriple<String, String, String>> productGroupingsToDelete = new ArrayList<>();
+ List<ImmutablePair<String, String>> productSubsToDelete = new ArrayList<>();
+ List<ImmutablePair<String, String>> resourceSubsToDelete = new ArrayList<>();
+ List<String> productCategoriesToDelete = new ArrayList<>();
+ List<String> resourceCategoriesToDelete = new ArrayList<String>();
+ List<String> serviceCategoriesToDelete = new ArrayList<String>();
+
+ List<String> resourcesNotToDelete = config.getResourcesNotToDelete();
+ List<String> resourceCategoriesNotToDelete = config.getResourceCategoriesNotToDelete();
+ List<String> serviceCategoriesNotToDelete = config.getServiceCategoriesNotToDelete();
+ TitanGraphQuery<? extends TitanGraphQuery> query = titanGraph.query();
+ query = query.has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName());
+ Iterable<TitanVertex> vertices=query.vertices();
+// Iterable<TitanVertex> vertices = titanGraph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex vertex = iter.next();
+ Boolean isAbstract = vertex.value(GraphPropertiesDictionary.IS_ABSTRACT.getProperty());
+ // if (!isAbstract) {
+ String name = vertex.value(GraphPropertiesDictionary.NAME.getProperty());
+ String version = vertex.value(GraphPropertiesDictionary.VERSION.getProperty());
+
+ if ((resourcesNotToDelete != null && !resourcesNotToDelete.contains(name)) || (version != null && !version.equals("1.0"))) {
+ String id = vertex.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String resourceType = vertex.value(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty());
+ // if (name.startsWith("ci")) {
+ if (resourceType.equals(ResourceTypeEnum.VF.name())) {
+ vfResourcesToDelete.add(id);
+ } else {
+ nonVfResourcesToDelete.add(id);
+ }
+ // }
+ } else if ((resourcesNotToDelete != null && !resourcesNotToDelete.contains(name)) || (version != null && version.equals("1.0"))) {
+ if ((boolean) vertex.value(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty()) == false) {
+ vertex.property(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ }
+ }
+ // }
+ }
+ }
+ vertices = titanGraph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Service.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex vertex = iter.next();
+ String id = vertex.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String name = vertex.value(GraphPropertiesDictionary.NAME.getProperty());
+ // if (name.startsWith("ci")){
+ servicesToDelete.add(id);
+ // }
+ }
+ }
+
+ vertices = titanGraph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Product.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex vertex = iter.next();
+ String id = vertex.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String name = vertex.value(GraphPropertiesDictionary.NAME.getProperty());
+ //if (name.startsWith("ci")) {
+ productsToDelete.add(id);
+ //}
+ }
+ }
+
+ // Getting categories
+
+ vertices = titanGraph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.ResourceNewCategory.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex category = iter.next();
+ String name = category.value(GraphPropertiesDictionary.NAME.getProperty());
+ if (!resourceCategoriesNotToDelete.contains(name)) {
+ String catId = category.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ resourceCategoriesToDelete.add(catId);
+ Iterator<Vertex> subs = category.vertices(Direction.OUT, GraphEdgeLabels.SUB_CATEGORY.getProperty());
+ while (subs.hasNext()) {
+ Vertex sub = subs.next();
+ String subCatId = sub.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ resourceSubsToDelete.add(new ImmutablePair<String, String>(catId, subCatId));
+ }
+ }
+ }
+ }
+
+ vertices = titanGraph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.ServiceNewCategory.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex category = iter.next();
+ String name = category.value(GraphPropertiesDictionary.NAME.getProperty());
+ if (!serviceCategoriesNotToDelete.contains(name)) {
+ String id = category.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ serviceCategoriesToDelete.add(id);
+ }
+ }
+ }
+
+ vertices = titanGraph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.ProductCategory.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex category = iter.next();
+ String catId = category.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ productCategoriesToDelete.add(catId);
+ Iterator<Vertex> subs = category.vertices(Direction.OUT, GraphEdgeLabels.SUB_CATEGORY.getProperty());
+ while (subs.hasNext()) {
+ Vertex sub = subs.next();
+ String subCatId = sub.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ productSubsToDelete.add(new ImmutablePair<String, String>(catId, subCatId));
+ Iterator<Vertex> groupings = sub.vertices(Direction.OUT, GraphEdgeLabels.GROUPING.getProperty());
+ while (groupings.hasNext()) {
+ Vertex grouping = groupings.next();
+ String groupId = grouping.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ productGroupingsToDelete.add(new ImmutableTriple<String, String, String>(catId, subCatId, groupId));
+ }
+ }
+
+ }
+ }
+
+ titanGraph.tx().commit();
+
+ String adminId = UserRoleEnum.ADMIN.getUserId();
+ String productStrategistId = UserRoleEnum.PRODUCT_STRATEGIST1.getUserId();
+
+ // Component delete
+ for (String id : productsToDelete) {
+ RestResponse deleteProduct = ProductRestUtils.deleteProduct(id, productStrategistId);
+
+ }
+ for (String id : servicesToDelete) {
+ RestResponse deleteServiceById = ServiceRestUtils.deleteServiceById(id, adminId);
+
+ }
+ for (String id : vfResourcesToDelete) {
+ RestResponse deleteResource = ResourceRestUtils.deleteResource(id, adminId);
+
+ }
+
+ for (String id : nonVfResourcesToDelete) {
+ RestResponse deleteResource = ResourceRestUtils.deleteResource(id, adminId);
+
+ }
+
+ // Categories delete - product
+ String componentType = BaseRestUtils.PRODUCT_COMPONENT_TYPE;
+ for (ImmutableTriple<String, String, String> triple : productGroupingsToDelete) {
+ CategoryRestUtils.deleteGrouping(triple.getRight(), triple.getMiddle(), triple.getLeft(), productStrategistId, componentType);
+ }
+ for (ImmutablePair<String, String> pair : productSubsToDelete) {
+ CategoryRestUtils.deleteSubCategory(pair.getRight(), pair.getLeft(), productStrategistId, componentType);
+ }
+ for (String id : productCategoriesToDelete) {
+ CategoryRestUtils.deleteCategory(id, productStrategistId, componentType);
+ }
+
+ // Categories delete - resource
+ componentType = BaseRestUtils.RESOURCE_COMPONENT_TYPE;
+ for (ImmutablePair<String, String> pair : resourceSubsToDelete) {
+ CategoryRestUtils.deleteSubCategory(pair.getRight(), pair.getLeft(), adminId, componentType);
+ }
+ for (String id : resourceCategoriesToDelete) {
+ CategoryRestUtils.deleteCategory(id, adminId, componentType);
+ }
+ // Categories delete - resource
+ componentType = BaseRestUtils.SERVICE_COMPONENT_TYPE;
+ for (String id : serviceCategoriesToDelete) {
+ CategoryRestUtils.deleteCategory(id, adminId, componentType);
+ }
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentInstanceBaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentInstanceBaseTest.java
new file mode 100644
index 0000000000..2b0a35d472
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/ComponentInstanceBaseTest.java
@@ -0,0 +1,763 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.api;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.Assert;
+
+public class ComponentInstanceBaseTest extends ComponentBaseTest {
+ public static final String acceptHeaderData = "application/json";
+ // Req/cap of container component
+ protected Map<String, List<CapabilityDefinition>> expectedContainerCapabilities;
+ protected Map<String, List<RequirementDefinition>> expectedContainerRequirements;
+ protected Map<String, Map<String, List<RequirementDefinition>>> removedRequirements;
+ protected Map<String, ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>>> expectedContInstReqCap;
+
+ protected User sdncPsDetails1;
+ protected User sdncPsDetails2;
+ protected User sdncPmDetails1;
+ protected User sdncPmDetails2;
+ protected User sdncDesignerDetails;
+ protected User sdncAdminDetails;
+ protected User sdncTesterDetails;
+ protected ResourceReqDetails resourceDetailsVFC_01;
+ protected ResourceReqDetails resourceDetailsVFC_02;
+ protected ResourceReqDetails resourceDetailsVF_01;
+ protected ResourceReqDetails resourceDetailsVF_02;
+ protected ResourceReqDetails resourceDetailsCP_01;
+ protected ResourceReqDetails resourceDetailsCP_02;
+ protected ResourceReqDetails resourceDetailsVL_01;
+ protected ResourceReqDetails resourceDetailsVL_02;
+ protected ServiceReqDetails serviceDetails_01;
+ protected ServiceReqDetails serviceDetails_02;
+ protected ServiceReqDetails serviceDetails_03;
+ protected ProductReqDetails productDetails_01;
+ protected ProductReqDetails productDetails_02;
+
+ public void init() {
+ // Req/caps of inner componentInstances
+ expectedContainerCapabilities = new LinkedHashMap<String, List<CapabilityDefinition>>();
+ expectedContainerRequirements = new LinkedHashMap<String, List<RequirementDefinition>>();
+ removedRequirements = new HashMap<>();
+ expectedContInstReqCap = new HashMap<>();
+
+ sdncPsDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+ sdncPsDetails2 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST2);
+ sdncPmDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ sdncPmDetails2 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER2);
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncTesterDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ resourceDetailsVFC_01 = ElementFactory.getDefaultResourceByType("ciVFC100", NormativeTypesEnum.SOFTWARE_COMPONENT, ResourceCategoryEnum.GENERIC_DATABASE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VFC.toString()); // resourceType = VFC
+ resourceDetailsVFC_02 = ElementFactory.getDefaultResourceByType("ciVFC200", NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VFC.toString());
+ resourceDetailsVF_01 = ElementFactory.getDefaultResourceByType("ciVF100", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VF.toString());
+ resourceDetailsVF_02 = ElementFactory.getDefaultResourceByType("ciVF200", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VF.toString());
+ resourceDetailsCP_01 = ElementFactory.getDefaultResourceByType("ciCP100", NormativeTypesEnum.PORT, ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, sdncDesignerDetails.getUserId(), ResourceTypeEnum.CP.toString());
+ resourceDetailsCP_02 = ElementFactory.getDefaultResourceByType("ciCP200", NormativeTypesEnum.PORT, ResourceCategoryEnum.GENERIC_DATABASE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.CP.toString());
+ resourceDetailsVL_01 = ElementFactory.getDefaultResourceByType("ciVL100", NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VL.toString());
+ resourceDetailsVL_02 = ElementFactory.getDefaultResourceByType("ciVL200", NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VL.toString());
+ serviceDetails_01 = ElementFactory.getDefaultService("ciNewtestservice1", ServiceCategoriesEnum.MOBILITY, sdncDesignerDetails.getUserId());
+ serviceDetails_02 = ElementFactory.getDefaultService("ciNewtestservice2", ServiceCategoriesEnum.MOBILITY, sdncDesignerDetails.getUserId());
+ serviceDetails_03 = ElementFactory.getDefaultService("ciNewtestservice3", ServiceCategoriesEnum.MOBILITY, sdncDesignerDetails.getUserId());
+ productDetails_01 = ElementFactory.getDefaultProduct("ciProduct01");
+ productDetails_02 = ElementFactory.getDefaultProduct("ciProduct02");
+ }
+
+ public ComponentInstanceBaseTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ public void verifyVFReqCap(String componentId) throws Exception {
+ RestResponse restResponse = ResourceRestUtils.getResource(componentId);
+ Resource resource = ResponseParser.parseToObject(restResponse.getResponse(), Resource.class);
+ verifyReqCap(resource);
+ }
+
+ public void verifyServiceReqCap(String componentId) throws Exception {
+ RestResponse restResponse = ServiceRestUtils.getService(componentId, sdncDesignerDetails);
+ Service service = ResponseParser.parseToObject(restResponse.getResponse(), Service.class);
+ verifyReqCap(service);
+ }
+
+ public void verifyProductReqCap(String componentId) throws Exception {
+ RestResponse restResponse = ProductRestUtils.getProduct(componentId, sdncPsDetails1.getUserId());
+ Product product = ResponseParser.parseToObject(restResponse.getResponse(), Product.class);
+ verifyReqCap(product);
+ }
+
+ public void verifyReqCap(Component actualComponent) {
+ verifyContainerReqCap(actualComponent);
+ verifyCompInstReqCap(actualComponent);
+ }
+
+ public RestResponse changeServiceInstanceVersion(String componentUniqueId, String serviceInstanceToReplaceUniqueId, String serviceUniqueId, User sdncModifierDetails, ComponentTypeEnum componentType, boolean isHighestLevel) throws Exception {
+ RestResponse changeResourceInstanceVersion = ProductRestUtils.changeServiceInstanceVersion(componentUniqueId, serviceInstanceToReplaceUniqueId, serviceUniqueId, sdncModifierDetails, componentType);
+ if (changeResourceInstanceVersion.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS) && isHighestLevel) {
+ /*
+ * // Add RI Capabilities and Requirements to expected MAP --> expectedVfCapabilities and expectedVfRequirements
+ *
+ * ComponentInstance componentInstance = ResponseParser.parseToObjectUsingMapper( changeResourceInstanceVersion.getResponse(), ComponentInstance.class); addCompInstReqCapToExpected(componentInstance, componentType);
+ */
+ }
+ return changeResourceInstanceVersion;
+ }
+
+ protected void updateExpectedReqCapAfterChangeLifecycleState(String oldContainerUniqueIdToReplace, String newContainerUniqueId) {
+
+ // Update of container req/cap
+
+ Set<String> compInstKeysToChange = new HashSet<>();
+
+ for (String expKey : expectedContainerCapabilities.keySet()) {
+ List<CapabilityDefinition> expCapList = expectedContainerCapabilities.get(expKey);
+ for (CapabilityDefinition cap : expCapList) {
+ String ownerId = cap.getOwnerId();
+
+ if (ownerId.contains(oldContainerUniqueIdToReplace)) {
+ compInstKeysToChange.add(ownerId);
+ cap.setOwnerId(cap.getOwnerId().replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+ }
+ }
+ }
+
+ for (String expKey : expectedContainerRequirements.keySet()) {
+ List<RequirementDefinition> expCapList = expectedContainerRequirements.get(expKey);
+ for (RequirementDefinition cap : expCapList) {
+ String ownerId = cap.getOwnerId();
+ if (ownerId.contains(oldContainerUniqueIdToReplace)) {
+ compInstKeysToChange.add(ownerId);
+ cap.setOwnerId(cap.getOwnerId().replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+ }
+ }
+ }
+
+ // Update of internal comp instances req/cap
+ for (String oldKey : compInstKeysToChange) {
+ ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>> immutablePair = expectedContInstReqCap.get(oldKey);
+ if (immutablePair != null) {
+ expectedContInstReqCap.remove(oldKey);
+ String newKey = oldKey.replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId);
+ expectedContInstReqCap.put(newKey, immutablePair);
+ }
+ }
+
+ // Update of removed req
+ for (String oldKey : compInstKeysToChange) {
+ Map<String, List<RequirementDefinition>> map = removedRequirements.get(oldKey);
+ if (map != null) {
+ removedRequirements.remove(oldKey);
+ String newKey = oldKey.replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId);
+ Collection<List<RequirementDefinition>> values = map.values();
+ if (values != null) {
+ for (List<RequirementDefinition> list : values) {
+ for (RequirementDefinition reqDef : list) {
+ reqDef.setOwnerId(reqDef.getOwnerId().replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+ }
+ }
+ }
+ removedRequirements.put(newKey, map);
+ }
+ }
+ }
+
+ private void verifyCompInstReqCap(Component actualComponent) {
+ List<ComponentInstance> componentInstances = actualComponent.getComponentInstances();
+ if (componentInstances != null) {
+ assertEquals(expectedContInstReqCap.size(), componentInstances.size());
+ for (ComponentInstance compInst : componentInstances) {
+ String uniqueId = compInst.getUniqueId();
+ // System.out.println("Verifying req/cap of component instance
+ // "+ uniqueId);
+ Map<String, List<RequirementDefinition>> actualCompInstReq = compInst.getRequirements();
+ if (actualCompInstReq == null) {
+ actualCompInstReq = new HashMap<>();
+ }
+ Map<String, List<CapabilityDefinition>> actualCompInstCap = compInst.getCapabilities();
+ if (actualCompInstCap == null) {
+ actualCompInstCap = new HashMap<>();
+ }
+ ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>> expReqCap = expectedContInstReqCap.get(uniqueId);
+ assertNotNull(expReqCap);
+ // System.out.println("expected instance requirements:
+ // "+expReqCap.right);
+ // System.out.println("expected instance capabilities:
+ // "+expReqCap.left);
+ // System.out.println("actual instance requirements:
+ // "+actualCompInstReq);
+ // System.out.println("actual instance capabilities:
+ // "+actualCompInstCap);
+
+ // REQ comparison
+ compareReqCapMaps(expReqCap.right, actualCompInstReq);
+
+ // CAP comparison
+ compareReqCapMaps(expReqCap.left, actualCompInstCap);
+ }
+
+ } else {
+ assertTrue(expectedContInstReqCap.isEmpty());
+ }
+ }
+
+ private void verifyContainerReqCap(Component actualComponent) {
+ Map<String, List<RequirementDefinition>> actualContainerRequirements = actualComponent.getRequirements();
+ if (actualContainerRequirements == null) {
+ actualContainerRequirements = new HashMap<>();
+ }
+ Map<String, List<CapabilityDefinition>> actualContainerCapabilities = actualComponent.getCapabilities();
+ if (actualContainerCapabilities == null) {
+ actualContainerCapabilities = new HashMap<>();
+ }
+ // System.out.println("Verifying req/cap of container component "+
+ // actualComponent.getUniqueId());
+ // System.out.println("expected container requirements:
+ // "+expectedContainerRequirements);
+ // System.out.println("expected container capabilities:
+ // "+expectedContainerCapabilities);
+ // System.out.println("actual container requirements:
+ // "+actualContainerRequirements);
+ // System.out.println("actual container capabilities:
+ // "+actualContainerCapabilities);
+
+ // REQ comparison
+ compareReqCapMaps(expectedContainerRequirements, actualContainerRequirements);
+
+ // CAP comparison
+ compareReqCapMaps(expectedContainerCapabilities, actualContainerCapabilities);
+ }
+
+ private <T> void compareReqCapMaps(Map<String, List<T>> expectedMap, Map<String, List<T>> actualMap) {
+ assertEquals(expectedMap.size(), actualMap.size());
+ for (String expKey : expectedMap.keySet()) {
+ List<?> expCapList = expectedMap.get(expKey);
+ List<?> actCapList = actualMap.get(expKey);
+ assertEquals(expCapList.size(), actCapList.size());
+ assertEquals(new HashSet<>(expCapList), new HashSet<>(actCapList));
+ }
+ }
+
+ public void addCompInstReqCapToExpected(ComponentInstance componentInstance, ComponentTypeEnum containerComponentType) throws Exception {
+ String uniqueId = componentInstance.getUniqueId();
+ String name = componentInstance.getName();
+ String originComponentId = componentInstance.getComponentUid();
+ RestResponse getResponse = null;
+ ComponentTypeEnum compInstType = getCompInstTypeByContainerType(containerComponentType);
+ Component component = null;
+ if (compInstType == ComponentTypeEnum.RESOURCE) {
+ getResponse = ResourceRestUtils.getResource(sdncDesignerDetails, originComponentId);
+ ResourceRestUtils.checkSuccess(getResponse);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Resource.class);
+ } else if (compInstType == ComponentTypeEnum.SERVICE) {
+ getResponse = ServiceRestUtils.getService(originComponentId, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getResponse);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Service.class);
+ } else {
+ Assert.fail("Unsupported type - " + containerComponentType);
+ }
+
+ Map<String, List<RequirementDefinition>> resourceRequirements = component.getRequirements();
+ if (resourceRequirements == null) {
+ resourceRequirements = new HashMap<>();
+ }
+ Function<Entry<String, List<RequirementDefinition>>, List<RequirementDefinition>> requirementDefinitionMapper = e -> new ArrayList<>(e.getValue().stream().map(item -> new RequirementDefinition(item)).collect(Collectors.toList()));
+ Map<String, List<RequirementDefinition>> reqCopy = resourceRequirements.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), requirementDefinitionMapper));
+
+ Map<String, List<CapabilityDefinition>> resourceCapabilities = component.getCapabilities();
+ if (resourceCapabilities == null) {
+ resourceCapabilities = new HashMap<>();
+ }
+
+ Function<? super Entry<String, List<CapabilityDefinition>>, List<CapabilityDefinition>> capabilityDefinitionMapper = e -> new ArrayList<>(e.getValue().stream().map(item -> new CapabilityDefinition(item)).collect(Collectors.toList()));
+ Map<String, List<CapabilityDefinition>> capCopy = resourceCapabilities.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), capabilityDefinitionMapper));
+
+ setupContainerExpectedReqCap(uniqueId, name, resourceRequirements, resourceCapabilities);
+ if (component.getComponentType().equals(ComponentTypeEnum.RESOURCE) && ((Resource) component).getResourceType() != ResourceTypeEnum.VF) {
+ setupConstInstExpectedReqCap(uniqueId, name, reqCopy, capCopy);
+ }
+
+ // adding entry for expected componentInstance
+ ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>> compInstReqCapPair = new ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>>(capCopy, reqCopy);
+ expectedContInstReqCap.put(uniqueId, compInstReqCapPair);
+ }
+
+ private void setupContainerExpectedReqCap(String uniqueId, String name, Map<String, List<RequirementDefinition>> componentRequirements, Map<String, List<CapabilityDefinition>> componentCapabilities) {
+ for (Entry<String, List<RequirementDefinition>> resReq : componentRequirements.entrySet()) {
+ List<RequirementDefinition> reqListToAdd = resReq.getValue();
+ for (RequirementDefinition requirementDefinition : reqListToAdd) {
+ requirementDefinition.setOwnerId(uniqueId);
+ requirementDefinition.setOwnerName(name);
+ }
+ List<RequirementDefinition> expectedReqList = expectedContainerRequirements.get(resReq.getKey());
+ if (expectedReqList == null) {
+ expectedReqList = reqListToAdd;
+ } else {
+ expectedReqList.addAll(reqListToAdd);
+ }
+ expectedContainerRequirements.put(resReq.getKey(), expectedReqList);
+ }
+
+ for (Entry<String, List<CapabilityDefinition>> resCap : componentCapabilities.entrySet()) {
+ List<CapabilityDefinition> capListToAdd = resCap.getValue();
+ for (CapabilityDefinition capDefinition : capListToAdd) {
+ capDefinition.setOwnerId(uniqueId);
+ capDefinition.setOwnerName(name);
+ }
+ List<CapabilityDefinition> expectedCapList = expectedContainerCapabilities.get(resCap.getKey());
+ if (expectedCapList == null) {
+ expectedCapList = capListToAdd;
+ } else {
+ expectedCapList.addAll(capListToAdd);
+ }
+ expectedContainerCapabilities.put(resCap.getKey(), expectedCapList);
+ }
+ }
+
+ private void setupConstInstExpectedReqCap(String uniqueId, String name, Map<String, List<RequirementDefinition>> componentRequirements, Map<String, List<CapabilityDefinition>> componentCapabilities) {
+ for (Entry<String, List<RequirementDefinition>> resReq : componentRequirements.entrySet()) {
+ List<RequirementDefinition> reqListToAdd = resReq.getValue();
+ for (RequirementDefinition requirementDefinition : reqListToAdd) {
+ requirementDefinition.setOwnerId(uniqueId);
+ requirementDefinition.setOwnerName(name);
+ }
+ }
+
+ for (Entry<String, List<CapabilityDefinition>> resCap : componentCapabilities.entrySet()) {
+ List<CapabilityDefinition> capListToAdd = resCap.getValue();
+ for (CapabilityDefinition capDefinition : capListToAdd) {
+ capDefinition.setOwnerId(uniqueId);
+ capDefinition.setOwnerName(name);
+ }
+ }
+ }
+
+ private ComponentTypeEnum getCompInstTypeByContainerType(ComponentTypeEnum componentType) {
+ switch (componentType) {
+ case RESOURCE:
+ return ComponentTypeEnum.RESOURCE;
+ case SERVICE:
+ return ComponentTypeEnum.RESOURCE;
+ case PRODUCT:
+ return ComponentTypeEnum.SERVICE;
+ default:
+ break;
+ }
+ return null;
+ }
+
+ public void deleteCompInstReqCapFromExpected(String componentInstanceId) {
+ List<String> entriesRequirementsToRemove = new ArrayList<>();
+ List<String> entriesCapabilitiesToRemove = new ArrayList<>();
+ for (Entry<String, List<RequirementDefinition>> reqEntry : expectedContainerRequirements.entrySet()) {
+ List<RequirementDefinition> reqList = reqEntry.getValue();
+ List<RequirementDefinition> reqListToDelete = new ArrayList<>();
+ for (RequirementDefinition requirementDefinition : reqList) {
+ if (requirementDefinition.getOwnerId().equals(componentInstanceId)) {
+ reqListToDelete.add(requirementDefinition);
+ }
+ }
+ reqList.removeAll(reqListToDelete);
+ if (reqList.isEmpty()) {
+ entriesRequirementsToRemove.add(reqEntry.getKey());
+ }
+ }
+
+ for (String ekey : entriesRequirementsToRemove) {
+ expectedContainerRequirements.remove(ekey);
+ }
+
+ for (Entry<String, List<CapabilityDefinition>> capEntry : expectedContainerCapabilities.entrySet()) {
+ List<CapabilityDefinition> capList = capEntry.getValue();
+ List<CapabilityDefinition> capListToDelete = new ArrayList<>();
+ for (CapabilityDefinition capabilityDefinition : capList) {
+ if (capabilityDefinition.getOwnerId().equals(componentInstanceId)) {
+ capListToDelete.add(capabilityDefinition);
+ }
+ }
+ capList.removeAll(capListToDelete);
+ if (capList.isEmpty()) {
+ entriesCapabilitiesToRemove.add(capEntry.getKey());
+ }
+ }
+ for (String ekey : entriesCapabilitiesToRemove) {
+ expectedContainerCapabilities.remove(ekey);
+ }
+
+ expectedContInstReqCap.remove(componentInstanceId);
+
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse createAtomicInstanceForVF(ResourceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.RESOURCE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse createAtomicInstanceForService(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse createVFInstance(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse createServiceInstance(ProductReqDetails containerDetails, ServiceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.PRODUCT, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse deleteAtomicInstanceForVF(String compInstUniqueId, ResourceReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.RESOURCE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse deleteAtomicInstanceForService(String compInstUniqueId, ServiceReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse deleteVFInstance(String compInstUniqueId, ServiceReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, true);
+
+ }
+
+ // Automatically updates the expected req/cap of the container
+ protected RestResponse deleteServiceInstance(String compInstUniqueId, ProductReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.PRODUCT, true);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse createAtomicInstanceForVFDuringSetup(ResourceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.RESOURCE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse createAtomicInstanceForServiceDuringSetup(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse createVFInstanceDuringSetup(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse createServiceInstanceDuringSetup(ProductReqDetails containerDetails, ServiceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.PRODUCT, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse deleteAtomicInstanceForVFDuringSetup(String compInstUniqueId, ResourceReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.RESOURCE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse deleteAtomicInstanceForServiceDuringSetup(String compInstUniqueId, ServiceReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse deleteVFInstanceDuringSetup(String compInstUniqueId, ServiceReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, false);
+
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ protected RestResponse deleteServiceInstanceDuringSetup(String compInstUniqueId, ProductReqDetails containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.PRODUCT, false);
+ }
+
+ protected Component getComponentAndValidateRIs(ComponentReqDetails componentDetails, int numberOfRIs, int numberOfRelations) throws IOException, Exception {
+
+ RestResponse getResponse = null;
+ Component component = null;
+ if (componentDetails instanceof ResourceReqDetails) {
+ getResponse = ResourceRestUtils.getResource(sdncAdminDetails, componentDetails.getUniqueId());
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Resource.class);
+ } else if (componentDetails instanceof ServiceReqDetails) {
+ getResponse = ServiceRestUtils.getService((ServiceReqDetails) componentDetails, sdncAdminDetails);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Service.class);
+ } else if (componentDetails instanceof ProductReqDetails) {
+ getResponse = ProductRestUtils.getProduct(componentDetails.getUniqueId(), sdncAdminDetails.getUserId());
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Product.class);
+ } else {
+ Assert.fail("Unsupported type of componentDetails - " + componentDetails.getClass().getSimpleName());
+ }
+ ResourceRestUtils.checkSuccess(getResponse);
+ int numberOfActualRIs = component.getComponentInstances() != null ? component.getComponentInstances().size() : 0;
+ int numberOfActualRelations = component.getComponentInstancesRelations() != null ? component.getComponentInstancesRelations().size() : 0;
+ assertEquals("Check number of RIs meet the expected number", numberOfRIs, numberOfActualRIs);
+ assertEquals("Check number of RI relations meet the expected number", numberOfRelations, numberOfActualRelations);
+ verifyReqCap(component);
+
+ return component;
+ }
+
+ protected void getComponentAndValidateRIsAfterChangeLifecycleState(String oldComponentUniqueIdToReplace, ComponentReqDetails componentDetails, int numOfRIs, int numOfRelations) throws IOException, Exception {
+ updateExpectedReqCapAfterChangeLifecycleState(oldComponentUniqueIdToReplace, componentDetails.getUniqueId());
+ getComponentAndValidateRIs(componentDetails, numOfRIs, numOfRelations);
+ }
+
+ private RestResponse createComponentInstance(ComponentReqDetails containerDetails, ComponentReqDetails compInstOriginDetails, User modifier, ComponentTypeEnum containerComponentTypeEnum, boolean isHighestLevel) throws IOException, Exception {
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory.getComponentResourceInstance(compInstOriginDetails);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, modifier, containerDetails.getUniqueId(), containerComponentTypeEnum);
+ if (createResourceInstanceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_CREATED) && isHighestLevel) {
+ // Add RI Capabilities and Requirements to expected MAP -->
+ // expectedVfCapabilities and expectedVfRequirements
+ ComponentInstance componentInstance = ResponseParser.parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, containerComponentTypeEnum);
+ }
+ return createResourceInstanceResponse;
+ }
+
+ private RestResponse deleteComponentInstance(String compInstUniqueId, ComponentReqDetails containerDetails, User modifier, ComponentTypeEnum componentTypeEnum, boolean isHighestLevel) throws Exception {
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(modifier, containerDetails.getUniqueId(), compInstUniqueId, componentTypeEnum);
+ if (deleteResourceInstanceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_DELETE) && isHighestLevel) {
+ deleteCompInstReqCapFromExpected(compInstUniqueId);
+ }
+ return deleteResourceInstanceResponse;
+ }
+
+ // Create Atomic resource ( VFC/CP/VL)
+ protected void createAtomicResource(ResourceReqDetails resourceDetails) throws Exception {
+ RestResponse createResourceResponse = ResourceRestUtils.createResource(resourceDetails, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceResponse);
+
+ }
+
+ protected void createVF(ResourceReqDetails resourceDetails) throws Exception {
+ createVF(resourceDetails, sdncDesignerDetails);
+
+ }
+
+ protected void createVF(ResourceReqDetails resourceDetails, User sdncModifier) throws Exception {
+ RestResponse createVfResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifier);
+ ResourceRestUtils.checkCreateResponse(createVfResponse);
+ }
+
+ protected void createService(ServiceReqDetails serviceDetails) throws Exception {
+ createService(serviceDetails, sdncDesignerDetails);
+ }
+
+ protected void createService(ServiceReqDetails serviceDetails, User sdncModifier) throws Exception {
+ RestResponse createServiceResponse = ServiceRestUtils.createService(serviceDetails, sdncModifier);
+ ResourceRestUtils.checkCreateResponse(createServiceResponse);
+ }
+
+ protected void createProduct(ProductReqDetails productDetails) throws Exception {
+ createProduct(productDetails, sdncPmDetails1);
+ }
+
+ protected void createProduct(ProductReqDetails productDetails, User sdncModifier) throws Exception {
+ RestResponse createProductResponse = ProductRestUtils.createProduct(productDetails, sdncModifier);
+ ResourceRestUtils.checkCreateResponse(createProductResponse);
+ }
+
+ protected RestResponse associateComponentInstancesForService(RequirementCapabilityRelDef requirementDef, ComponentReqDetails containerDetails, User user) throws IOException {
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, user, containerDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ deleteAssociatedFromExpected(requirementDef);
+
+ return associateInstances;
+ }
+
+ private void deleteAssociatedFromExpected(RequirementCapabilityRelDef requirementDef) {
+ // removing from requirements
+ RequirementAndRelationshipPair relationship = requirementDef.getRelationships().get(0);
+ String type = relationship.getRelationship().getType();
+ String fromId = requirementDef.getFromNode();
+ List<RequirementDefinition> reqList = expectedContainerRequirements.get(type);
+ List<CapabilityDefinition> capList = expectedContainerCapabilities.get(type);
+ RequirementDefinition toDelete = null;
+ if (reqList != null) {
+ for (RequirementDefinition reqDef : reqList) {
+ if (reqDef.getOwnerId().equals(fromId)) {
+ toDelete = reqDef;
+ }
+ }
+ if (toDelete != null) {
+ reqList.remove(toDelete);
+ if (reqList.isEmpty()) {
+ expectedContainerRequirements.remove(type);
+ }
+ String ownerId = toDelete.getOwnerId();
+ Map<String, List<RequirementDefinition>> map = removedRequirements.get(ownerId);
+ if (map == null) {
+ map = new HashMap<>();
+ removedRequirements.put(ownerId, map);
+ }
+ List<RequirementDefinition> list = map.get(type);
+ if (list == null) {
+ list = new ArrayList<>();
+ map.put(type, list);
+ }
+ list.add(toDelete);
+ }
+ }
+
+ for (CapabilityDefinition capabilityDefinition : capList) {
+ if (capabilityDefinition.getType().equals(type)) {
+ int minOccurrences = Integer.parseInt(capabilityDefinition.getMinOccurrences()) - 1;
+ if (minOccurrences < 0)
+ minOccurrences = 0;
+ String minOccurrencesString = Integer.toString(minOccurrences);
+ capabilityDefinition.setMinOccurrences(minOccurrencesString);
+ if (!capabilityDefinition.getMaxOccurrences().equals("UNBOUNDED")) {
+ int maxOccurrences = Integer.parseInt(capabilityDefinition.getMaxOccurrences()) - 1;
+ if (maxOccurrences < 0)
+ maxOccurrences = 0;
+ String maxOccurrencesString = Integer.toString(maxOccurrences);
+ capabilityDefinition.setMaxOccurrences(maxOccurrencesString);
+ }
+ }
+ }
+ expectedContainerCapabilities.put(type, capList);
+ }
+
+ protected void dissociateComponentInstancesForService(RequirementCapabilityRelDef requirementDef, ComponentReqDetails containerDetails, User user) throws IOException {
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, user, containerDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(dissociateInstances);
+ addDissociatedToExpected(requirementDef);
+ }
+
+ protected void fulfillCpRequirement(ComponentReqDetails component, String cpCompInstId, String cpReqFulfillerCompInstId, String cpReqFulfillerOwnerId, User user, ComponentTypeEnum containerCompType) throws IOException {
+ // Fulfilling cp's "binding" requirement - US626240
+ String requirementName = "binding";
+ String capType = "tosca.capabilities.network.Bindable";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(user, component);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef reqCapRelation = ElementFactory.getReqCapRelation(cpCompInstId, cpReqFulfillerCompInstId, cpCompInstId, cpReqFulfillerOwnerId, capType, requirementName, capList, reqList);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(reqCapRelation, user, component.getUniqueId(), containerCompType);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ }
+
+ protected void consumeVlCapability(ComponentReqDetails component, String vlCapConsumerCompInstId, String vlCompInstId, String vlCapConsumerOwnerId, User user, ComponentTypeEnum containerCompType) throws IOException {
+ // Consuming vl's "link" capability - US626240
+ String requirementName = "link";
+ String capType = "tosca.capabilities.network.Linkable";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(user, component);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef reqCapRelation = ElementFactory.getReqCapRelation(vlCapConsumerCompInstId, vlCompInstId, vlCapConsumerOwnerId, vlCompInstId, capType, requirementName, capList, reqList);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(reqCapRelation, user, component.getUniqueId(), containerCompType);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ }
+
+ private void addDissociatedToExpected(RequirementCapabilityRelDef requirementDef) {
+ // adding to requirements
+ RequirementAndRelationshipPair relationship = requirementDef.getRelationships().get(0);
+ String type = relationship.getRelationship().getType();
+ String fromId = requirementDef.getFromNode();
+ Map<String, List<RequirementDefinition>> map = removedRequirements.get(fromId);
+ if (map != null) {
+ List<RequirementDefinition> list = map.get(type);
+ if (list != null && !list.isEmpty()) {
+ List<RequirementDefinition> reqList = expectedContainerRequirements.get(type);
+ if (reqList == null) {
+ reqList = new ArrayList<>();
+ expectedContainerRequirements.put(type, reqList);
+ }
+ reqList.add(list.remove(0));
+ }
+ }
+
+ List<CapabilityDefinition> capList = expectedContainerCapabilities.get(type);
+
+ for (CapabilityDefinition capabilityDefinition : capList) {
+ if (capabilityDefinition.getType().equals(type)) {
+ int minOccurrences = Integer.parseInt(capabilityDefinition.getMinOccurrences()) + 1;
+ String minOccurrencesString = Integer.toString(minOccurrences);
+ capabilityDefinition.setMinOccurrences(minOccurrencesString);
+ if (!capabilityDefinition.getMaxOccurrences().equals("UNBOUNDED")) {
+ int maxOccurrences = Integer.parseInt(capabilityDefinition.getMaxOccurrences()) + 1;
+ String maxOccurrencesString = Integer.toString(maxOccurrences);
+ capabilityDefinition.setMaxOccurrences(maxOccurrencesString);
+ }
+ }
+ }
+ expectedContainerCapabilities.put(type, capList);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/SdcTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/SdcTest.java
new file mode 100644
index 0000000000..ebad749097
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/SdcTest.java
@@ -0,0 +1,202 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.api;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.junit.rules.TestWatcher;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.rules.MyTestWatcher;
+import org.openecomp.sdc.ci.tests.run.StartTest;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public abstract class SdcTest {
+
+ public static StringBuilder doc = new StringBuilder();
+ public static String file = null;
+ public static Config config = null;
+ // protected Gson gson = new Gson();
+ protected Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ protected TestName testName = null;
+ protected Logger logger = null;
+
+ protected static boolean displayException = false;
+
+ public SdcTest(TestName testName, String className) {
+ super();
+
+ StartTest.enableLogger();
+
+ this.testName = testName;
+ this.logger = LoggerFactory.getLogger(className);
+
+ String displayEx = System.getProperty("displayException");
+ if (displayEx != null && Boolean.valueOf(displayEx).booleanValue()) {
+ displayException = true;
+ }
+
+ }
+
+ @Rule
+ public TestWatcher tw = new MyTestWatcher(this);
+
+ @BeforeClass
+ public static void beforeClass() {
+ doc = new StringBuilder();
+ doc.append(
+ "<Html><head><style>th{background-color: gray;color: white;height: 30px;}td {color: black;height: 30px;}.fail {background-color: #FF5555;width: 100px;text-align: center;}.success {background-color: #00FF00;width: 100px;text-align: center;}.name {width: 200px;background-color: #F0F0F0;}.message {width: 300px;background-color: #F0F0F0;}</style>");
+
+ doc.append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
+ doc.append(
+ "<link rel=\"stylesheet\" href=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\">");
+
+ doc.append("</head><body>");
+
+ doc.append("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>");
+ doc.append("<script src=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js\"></script>");
+
+ doc.append("<table>");
+
+ doc.append("<tr>");
+ doc.append("<th>").append("Test Name").append("</th>");
+ doc.append("<th>").append("Status").append("</th>");
+ doc.append("<th>").append("Message").append("</th>");
+
+ if (displayException) {
+ doc.append("<th>").append("Exception").append("</th>");
+ }
+ doc.append("</tr>");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ doc.append("<table>");
+ FileUtils.writeToFile(
+ Config.instance().getOutputFolder() + File.separator + file + StartTest.timeOfTest + ".html",
+ doc.toString());
+
+ }
+
+ @Before
+ public void beforeTest() throws FileNotFoundException {
+ file = FileUtils.getFileName(this.getClass().getName());
+ config = Utils.getConfig();
+ assertTrue(config != null);
+
+ // logger.info("Start running test " + testName.getMethodName());
+ }
+
+ @After
+ public void afterTest() throws FileNotFoundException {
+
+ // logger.info("Finish running test " + testName.getMethodName());
+ }
+
+ public void addTestSummary(String testName, boolean isSuccess) {
+ addTestSummary(testName, isSuccess, null);
+ }
+
+ public void addTestSummary(String testName, boolean isSuccess, Throwable exception) {
+
+ String message = exception == null ? "" : exception.getMessage();
+
+ String result = (isSuccess) ? "success" : "fail";
+ doc.append("<tr>");
+ doc.append("<td class=\"name\">").append(testName).append("</td>");
+ doc.append("<td class=\"" + result + "\">").append(result).append("</td>");
+ doc.append("<td class=\"message\">").append(message).append("</td>");
+
+ if (displayException) {
+ // doc.append("<td
+ // class=\"message\">").append(convertExceptionToString(exception)).append("</td>");
+ doc.append("<td class=\"message\">");
+
+ doc.append("<button type=\"button\" class=\"btn btn-info\" data-toggle=\"collapse\" data-target=\"#demo"
+ + testName + "\">Simple collapsible</button>");
+ doc.append("<div id=\"demo" + testName + "\" class=\"collapse out\">");
+
+ doc.append(convertExceptionToString(exception));
+
+ doc.append("</div>");
+ doc.append("</td>");
+ }
+
+ doc.append("</tr>");
+
+ if (isSuccess) {
+ logger.debug("Test {} succeeded.", testName);
+ } else {
+ logger.error("Test {} failed with error: {}", testName, message);
+ }
+ }
+
+ private String convertExceptionToString(Throwable exception) {
+
+ if (exception == null) {
+ return "";
+ }
+
+ StringWriter sw = new StringWriter();
+ exception.printStackTrace(new PrintWriter(sw));
+ String exceptionAsString = sw.toString();
+
+ return exceptionAsString;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ protected boolean ignoreDueToBug(String bug) {
+
+ List<String> bugs = config.getBugs();
+
+ if (bugs != null && bugs.size() > 0) {
+ for (String bugNumber : bugs) {
+ if (bugNumber.startsWith(bug)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/Urls.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/Urls.java
new file mode 100644
index 0000000000..5aceb4fae3
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/api/Urls.java
@@ -0,0 +1,348 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.api;
+
+public interface Urls {
+
+ final String UPLOAD_ZIP_URL = "http://%s:%s/sdc1/rest/v1/catalog/resources";
+ final String GET_IMAGE_DATA_FROM_ES = "http://%s:%s/resources/imagedata/_search?q=resourceName:%s&pretty=true&size=1000";
+ final String GET_SCRIPT_DATA_FROM_ES = "http://%s:%s/resources/artifactdata/_search?q=resourceName:%s&pretty=true&size=1000";
+ final String GET_ID_LIST_BY_INDEX_FROM_ES = "http://%s:%s/%s/%s/_search?fields=_id&size=1000";
+
+ final String ES_URL = "http://%s:%s";
+ final String GET_SERVICE_CSAR_API1 = "http://%s:%s/sdc2/rest/services/%s/%s";
+ final String GET_SERVICE_CSAR_API2 = "http://%s:%s/sdc2/rest/services/%s/%s/csar";
+
+ final String GET_SERVICE_CSAR_FE_PROXY_API1 = "http://%s:%s/sdc1/portal/rest/services/%s/%s";
+ final String GET_CSAR_USING_SIMULATOR = "http://%s:%s/onboardingci/onbrest/onboarding-api/v1.0/vendor-software-products/packages/%s";
+ final String COPY_CSAR_USING_SIMULATOR = "http://%s:%s/onboardingci/onbrest/onboarding-api/v1.0/vendor-software-products/packages/%s/%s";
+
+ final String GET_HEALTH_CHECK_VIA_PROXY = "http://%s:%s/sdc1/rest/healthCheck";
+
+ // Get back-end config http://172.20.43.132:8080/sdc2/rest/configmgr/get
+ final String GET_CONFIG_MANAGER = "http://%s:%s/sdc2/rest/configmgr/get";
+
+ // Get latest version of all non-abstract resources
+ final String GET_RESOURCE_lATEST_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/resources/latestversion/notabstract";
+
+ final String GET_SERVICE_lATEST_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/services/latestversion/notabstract";
+
+ // Get resource artifact list:
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts
+ final String GET_RESOURCE_ARTIFACTS_LIST = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/artifacts";
+
+ // get resource artifact metadata (creation, MD5, etc):
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/install_apache.sh/metadata
+ final String GET_RESOURCE_ARTIFACT_METADATA = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/artifacts/%s/metadata";
+
+ // resource artifact payload:
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/install_apache.sh
+ final String GET_RESOURCE_ARTIFACT_PAYLOAD = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/artifacts/%s";
+
+ final String GET_RESOURCE_ARTIFACT_PAYLOAD_FE_PROXY = "http://%s:%s/sdc1/portal/rest/v1/catalog/resources/%s/%s/artifacts/%s";
+
+ // Get service list: http://172.20.43.124:8080/sdc2/rest/v1/catalog/services
+ final String GET_SERVICE_LIST = "http://%s:%s/sdc2/rest/v1/catalog/services";
+
+ // Get service versions:
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/services/MyService
+ final String GET_SERVICE_VERSIONS = "http://%s:%s/sdc2/rest/v1/catalog/services/%s";
+
+ // Get service artifact list:
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/services/alien.nodes.Apache/0.0.1/artifacts
+ final String GET_SERVICE_ARTIFACTS_LIST = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/%s/artifacts";
+
+ // get service artifact metadata (creation, MD5, etc):
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/services/alien.nodes.Apache/0.0.1/artifacts/install_apache.sh/metadata
+ final String GET_SERVICE_METADATA = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/%s/artifacts/%s/metadata";
+
+ // service artifact payload:
+ // http://172.20.43.124:8080/sdc2/rest/v1/catalog/services/alien.nodes.Apache/0.0.1/artifacts/install_apache.sh
+ final String GET_SERVICE_ARTIFACT_PAYLOAD = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/artifacts/%s";
+
+ final String GET_SEARCH_DATA_FROM_ES = "http://%s:%s/%s";
+
+ // ****************************************************USER
+ // URLs********************************************************
+ final String GET_USER = "http://%s:%s/sdc2/rest/v1/user/%s";
+
+ final String GET_USER_ROLE = "http://%s:%s/sdc2/rest/v1/user/%s/role";
+
+ final String CREATE_USER = "http://%s:%s/sdc2/rest/v1/user";
+
+ final String UPDATE_USER = "http://%s:%s/sdc2/rest/v1/user/%s";
+
+ final String UPDATE_USER_ROLE = "http://%s:%s/sdc2/rest/v1/user/%s/role";
+
+ final String DELETE_USER = "http://%s:%s/sdc2/rest/v1/user/%s";
+
+ final String GET_ALL_ADMIN_USERS = "http://%s:%s/sdc2/rest/v1/user/admins";
+
+ final String AUTHORIZE_USER = "http://%s:%s/sdc2/rest/v1/user/authorize";
+
+ final String GET_ALL_TAGS = "http://%s:%s/sdc2/rest/v1/tags";
+
+ final String AUTH_USER = "http://%s:%s/sdc2/rest/v1/user/authorize";
+
+ final String GET_ALL_NOT_ABSTRACT_RESOURCES = "http://%s:%s/sdc2/rest/v1/catalog/resources/certified/notabstract";
+
+ final String GET_ALL_ABSTRACT_RESOURCES = "http://%s:%s/sdc2/rest/v1/catalog/resources/certified/abstract";
+
+ final String QUERY_NEO4J = "http://%s:%s/db/data/transaction";
+ final String CHANGE_IN_NEO4J = "http://%s:%s/db/data/transaction/commit";
+
+ final String GET_ALL_ADMINS = "http://%s:%s/sdc2/rest/v1/user/admins";
+
+ final String GET_USERS_BY_ROLES = "http://%s:%s/sdc2/rest/v1/user/users?roles=%s";
+
+ final String GET_ALL_USERS = "http://%s:%s/sdc2/rest/v1/user/users?roles/";
+
+ // *****************************************ECOMP User
+ // URL's*****************************************************
+ final String ECOMP_PUSH_USER = "http://%s:%s/api/user";
+
+ final String ECOMP_EDIT_USER = "http://%s:%s/api/user/%s";
+
+ final String ECOMP_GET_USER = "http://%s:%s/api/user/%s";
+
+ final String ECOMP_GET_ALL_USERS = "http://%s:%s/api/users";
+
+ final String ECOMP_GET_ALL_AVAILABLE_ROLES = "http://%s:%s/api/roles";
+
+ final String ECOMP_PUSH_USER_ROLES = "http://%s:%s/api/user/%s/roles";
+
+ final String ECOMP_GET_USER_ROLES = "http://%s:%s/api/user/%s/roles";
+
+ // *****************************************Elements*************************************************************
+ final String GET_TAGS_LIST = "http://%s:%s/sdc2/rest/v1/tags";
+
+ final String GET_PROPERTY_SCOPES_LIST = "http://%s:%s/sdc2/rest/v1/propertyScopes";
+
+ final String GET_CONFIGURATION = "http://%s:%s/sdc2/rest/v1/configuration/ui";
+
+ final String GET_ALL_ARTIFACTS = "http://%s:%s/sdc2/rest/v1/artifactTypes";
+
+ final String GET_FOLLWED_LIST = "http://%s:%s/sdc2/rest/v1/followed";
+
+ final String GET_CATALOG_DATA = "http://%s:%s/sdc2/rest/v1/screen";
+
+ // *****************************************Resources
+ // **********************************************************************
+ final String GET_LIST_CERTIFIED_RESOURCE_TEMPLATES = "http://%s:%s/sdc2/rest/v1/resoourceTemplates";
+
+ final String CREATE_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources";
+ final String UPDATE_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s";
+
+ final String IMPORT_RESOURCE_NORMATIVE = "http://%s:%s/sdc2/rest/v1/catalog/upload/multipart";
+
+ final String IMPORT_USER_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/upload/user-resource";
+
+ final String IMPORT_CAPABILITY_TYPE = "http://%s:%s/sdc2/rest/v1/catalog/uploadType/capability";
+ final String IMPORT_CATEGORIES = "http://%s:%s/sdc2/rest/v1/catalog/uploadType/categories";
+ final String IMPORT_GROUP_TYPE = "http://%s:%s/sdc2/rest/v1/catalog/uploadType/grouptypes";
+
+ // last %s is resourceId, resourceId = resourceName.resourceVersion
+ final String GET_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s";
+ final String GET_RESOURCE_BY_NAME_AND_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/resources/resourceName/%s/resourceVersion/%s";
+ final String GET_RESOURCE_BY_CSAR_UUID = "http://%s:%s/sdc2/rest/v1/catalog/resources/csar/%s";
+ final String GET_COMPONENT_REQUIRMENTS_CAPABILITIES = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/requirmentsCapabilities";
+
+ final String DELETE_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s";
+ final String DELETE_RESOURCE_BY_NAME_AND_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s";
+ final String DELETE_SERVICE_BY_NAME_AND_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/%s";
+
+ final String DELETE_MARKED_RESOURCES = "http://%s:%s/sdc2/rest/v1/inactiveComponents/resource";
+ final String DELETE_MARKED_SERVICES = "http://%s:%s/sdc2/rest/v1/inactiveComponents/service";
+
+ final String GET_FOLLOWED_RESOURCES = "http://%s:%s/sdc2/rest/v1/followed/resources/%s";
+ final String CHANGE_RESOURCE_LIFECYCLE_STATE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/lifecycleState/%s";
+ final String CHANGE_SERVICE_LIFECYCLE_STATE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/lifecycleState/%s";
+ final String CHANGE_PRODUCT_LIFECYCLE_STATE = "http://%s:%s/sdc2/rest/v1/catalog/products/%s/lifecycleState/%s";
+ final String CHANGE_COMPONENT_LIFECYCLE_STATE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/lifecycleState/%s";
+
+ final String CREATE_PROPERTY = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/properties";
+
+ final String UPDATE_RESOURCE_METADATA = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/metadata";
+
+ // ***********************************External API's
+ // (AssetData)****************************************
+
+ final String POST_EXTERNAL_API_UPLOAD_ARTIFACT_OF_ASSET = "http://%s:%s/asdc/v1/catalog/%s/%s/artifacts";
+
+ final String GET_DOWNLOAD_RESOURCE_ARTIFACT_OF_ASSET = "http://%s:%s/asdc/v1/catalog/resources/%s/artifacts/%s";
+ final String GET_DOWNLOAD_SERVICE_ARTIFACT_OF_ASSET = "http://%s:%s/asdc/v1/catalog/services/%s/artifacts/%s";
+
+ final String GET_DOWNLOAD_RESOURCE_ARTIFACT_OF_COMPONENT_INSTANCE = "http://%s:%s/asdc/v1/catalog/resources/%s/resourceInstances/%s/artifacts/%s";
+ final String GET_DOWNLOAD_SERVICE_ARTIFACT_OF_COMPONENT_INSTANCE = "http://%s:%s/asdc/v1/catalog/services/%s/resourceInstances/%s/artifacts/%s";
+
+ final String GET_ASSET_LIST = "http://%s:%s/asdc/v1/catalog/%s";
+ final String GET_FILTERED_ASSET_LIST = "http://%s:%s/asdc/v1/catalog/%s?%s";
+ final String GET_TOSCA_MODEL = "http://%s:%s/asdc/v1/catalog/%s/%s/toscaModel";
+ // https://{serverRoot}/asdc/v1/catalog/{assetType}/{uuid}/metadata, where
+ // assetType in {resources, services}
+ final String GET_ASSET_METADATA = "http://%s:%s/asdc/v1/catalog/%s/%s/metadata";
+ final String POST_AUTHORIZATION = "http://%s:%s/sdc2/rest/v1/consumers";
+ final String GET_DOWNLOAD_SERVICE_RI_ARTIFACT = "http://%s:%s/asdc/v1/catalog/services/%s/resourceInstances/%s/artifacts/%s";
+ final String GET_DOWNLOAD_SERVICE_ARTIFACT = "http://%s:%s/asdc/v1/catalog/services/%s/artifacts/%s";
+
+ // *****************************************************************************************************
+
+ final String ADD_ARTIFACT_TO_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/artifacts";
+ final String UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/artifacts/%s";
+ final String ADD_ARTIFACT_TO_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/artifacts";
+ final String UPDATE_OR_DELETE_ARTIFACT_OF_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/artifacts/%s";
+
+ final String UPLOAD_DELETE_ARTIFACT_OF_COMPONENT = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/artifacts";
+ final String UPDATE_ARTIFACT_OF_COMPONENT = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/artifacts/%s";
+ final String UPLOAD_HEAT_ENV_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/%s/artifacts/%s";
+ // *****************************************************************************************************
+ final String UPLOAD_ARTIFACT_BY_INTERFACE_TO_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/%s/artifacts/";
+ final String UPDATE_OR_DELETE_ARTIFACT_BY_INTERFACE_TO_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/%s/artifacts/%s";
+
+ final String UPLOAD_ARTIFACT_BY_INTERFACE_TO_COMPONENT = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/%s/artifacts/";
+ final String UPDATE_OR_DELETE_ARTIFACT_BY_INTERFACE_TO_COMPONENT = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/%s/%s/artifacts/%s";
+
+ // *****************************************************************************************************
+ // "/sdc2/v1/services/<serviceName>/<0.1>/artifacts/aaa.hh"
+ final String DISTRIB_DOWNLOAD_SERVICE_ARTIFACT = "/sdc2/rest/v1/catalog/services/%s/%s/artifacts/%s";
+ // "/sdc2/v1/services/<serviceName>/<0.1>/resources/{resourceName}/{resourceVersion}/artifacts/<opeartion_name>_aaa.hh"
+ final String DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT = "/sdc2/rest/v1/catalog/services/%s/%s/resources/%s/%s/artifacts/%s";
+ final String DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL = "/asdc/v1/catalog/services/%s/%s/artifacts/%s";
+ final String DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL = "/asdc/v1/catalog/services/%s/%s/resources/%s/%s/artifacts/%s";
+ final String DOWNLOAD_SERVICE_ARTIFACT_FULL_URL = "http://%s:%s%s";
+ final String DOWNLOAD_RESOURCE_ARTIFACT_FULL_URL = "http://%s:%s%s";
+ // **********************************************************************************
+ final String UI_DOWNLOAD_RESOURCE_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/artifacts/%s";
+ final String UI_DOWNLOAD_SERVICE_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/artifacts/%s";
+
+ // **********************************************************************************************************
+ final String UPDATE_PROPERTY = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/properties/%s";
+
+ final String DELETE_PROPERTY = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/properties/%s";
+
+ final String GET_PROPERTY = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/properties/%s";
+
+ // *****************************************************************************************************
+
+ final String VALIDATE_RESOURCE_NAME = "http://%s:%s/sdc2/rest/v1/catalog/resources/validate-name/%s";
+
+ final String CREATE_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services";
+ final String DELETE_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s";
+ final String GET_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s";
+ final String GET_SERVICE_BY_NAME_AND_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/services/serviceName/%s/serviceVersion/%s";
+
+ final String GET_SERVICES_REQUIRMENTS_CAPABILITIES = "http://%s:%s/sdc2/rest/v1/catalog/requirmentsCapabilities/services/%s";
+
+ final String CREATE_COMPONENT_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance";
+ final String DELETE_COMPONENT_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/%s";
+ final String UPDATE_COMPONENT_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/%s";
+ final String GET_COMPONENT_INSTANCES = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/componentInstances";
+ // Tal New API
+ final String UPDATE_MULTIPLE_COMPONENT_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/multipleComponentInstance";
+
+ final String CHANGE__RESOURCE_INSTANCE_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/%s/changeVersion";
+
+ final String CREATE_AND_ASSOCIATE_RESOURCE_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstance/createAndAssociate";
+ final String ASSOCIATE__RESOURCE_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/associate";
+ final String DISSOCIATE__RESOURCE_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/dissociate";
+
+ final String DISTRIBUTION_INIT = "http://%s:%s/init";
+ final String DISTRIBUTION_INIT_RESET = "http://%s:%s/initReset";
+ final String APPROVE_DISTRIBUTION = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/distribution-state/approve";
+ final String REJECT_DISTRIBUTION = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/distribution-state/reject";
+ final String DISTRIBUTION_DOWNLOAD_ARTIFACT = "http://%s:%s/download";
+ final String ACTIVATE_DISTRIBUTION = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/distribution/%s/activate";
+
+ final String DEPLOY_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/distribution/%s/markDeployed";
+ final String UPDATE_SERVICE_METADATA = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/metadata";
+
+ // Andrey changed name from ADD_PROPERTY_TO_RESOURCE_INSTANCE to
+ // UPDATE_PROPERTY_TO_RESOURCE_INSTANCE
+ final String UPDATE_PROPERTY_TO_RESOURCE_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/%s/property";
+ final String DELETE_PROPERTY_FROM_RESOURCE_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstance/%s/property/%s";
+ final String UPDATE_RESOURCE_INSTANCE_HEAT_ENV_PARAMS = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstance/%s/artifacts/%s/heatParams";
+
+ // Actions on artifact in resource instance
+ final String ADD_RESOURCE_INSTANCE_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstance/%s/artifacts";
+ final String UPDATE_RESOURCE_INSTANCE_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstance/%s/artifacts/%s";
+ final String DELETE_RESOURCE_INSTANCE_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstance/%s/artifacts/%s";
+
+ // Attributes On Resource instance
+ public static final String UPDATE_ATTRIBUTE_ON_RESOURCE_INSTANCE = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/resourceInstance/%s/attribute";
+
+ // ("/services/{serviceId}/resourceInstances/{resourceInstanceId}/artifacts/{artifactId}")
+ final String DOWNLOAD_COMPONENT_INSTANCE_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/resourceInstances/%s/artifacts/%s";
+
+ // -------------------------------service api
+ // artifact-----------------------------------------------------
+ final String UPDATE_DELETE_SERVICE_API_ARTIFACT = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/artifacts/api/%s";
+
+ final String CREATE_ADDITIONAL_INFORMATION_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/additionalinfo";
+ final String UPDATE_ADDITIONAL_INFORMATION_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/additionalinfo/%s";
+ final String DELETE_ADDITIONAL_INFORMATION_RESOURCE = UPDATE_ADDITIONAL_INFORMATION_RESOURCE;
+ final String GET_ADDITIONAL_INFORMATION_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/additionalinfo/%s";
+ final String GET_ALL_ADDITIONAL_INFORMATION_RESOURCE = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/additionalinfo";
+
+ final String CREATE_ADDITIONAL_INFORMATION_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/additionalinfo";
+ final String UPDATE_ADDITIONAL_INFORMATION_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/additionalinfo/%s";
+ final String DELETE_ADDITIONAL_INFORMATION_SERVICE = UPDATE_ADDITIONAL_INFORMATION_SERVICE;
+ final String GET_ADDITIONAL_INFORMATION_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/additionalinfo/%s";
+ final String GET_ALL_ADDITIONAL_INFORMATION_SERVICE = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/additionalinfo";
+
+ final String GET_COMPONENT_AUDIT_RECORDS = "http://%s:%s/sdc2/rest/v1/catalog/audit-records/%s/%s";
+
+ // CONSUMER
+ final String CREATE_CONSUMER = "http://%s:%s/sdc2/rest/v1/consumers";
+ final String GET_CONSUMER = "http://%s:%s/sdc2/rest/v1/consumers/%s";
+ final String DELETE_CONSUMER = "http://%s:%s/sdc2/rest/v1/consumers/%s";
+
+ // Categories
+ final String CREATE_CATEGORY = "http://%s:%s/sdc2/rest/v1/category/%s";
+ final String GET_ALL_CATEGORIES = "http://%s:%s/sdc2/rest/v1/categories/%s";
+ final String GET_ALL_CATEGORIES_FE = "http://%s:%s/sdc1/feProxy/rest/v1/categories/%s";
+ final String DELETE_CATEGORY = "http://%s:%s/sdc2/rest/v1/category/%s/%s";
+ final String CREATE_SUB_CATEGORY = "http://%s:%s/sdc2/rest/v1/category/%s/%s/subCategory";
+ final String DELETE_SUB_CATEGORY = "http://%s:%s/sdc2/rest/v1/category/%s/%s/subCategory/%s";
+ final String CREATE_GROUPING = "http://%s:%s/sdc2/rest/v1/category/%s/%s/subCategory/%s/grouping";
+ final String DELETE_GROUPING = "http://%s:%s/sdc2/rest/v1/category/%s/%s/subCategory/%s/grouping/%s";
+
+ // product
+ final String CREATE_PRODUCT = "http://%s:%s/sdc2/rest/v1/catalog/products";
+ final String DELETE_PRODUCT = "http://%s:%s/sdc2/rest/v1/catalog/products/%s";
+ // last %s is resourceId, productId
+ final String GET_PRODUCT = "http://%s:%s/sdc2/rest/v1/catalog/products/%s";
+ final String UPDATE_PRODUCT = "http://%s:%s/sdc2/rest/v1/catalog/products/%s/metadata";
+ final String GET_PRODUCT_BY_NAME_AND_VERSION = "http://%s:%s/sdc2/rest/v1/catalog/products/productName/%s/productVersion/%s";
+
+ // groups
+ final String GET_GROUP_BY_ID = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/groups/%s";
+
+ // modules
+ final String GET_MODULE_BY_ID = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/groups/%s";
+
+ // inputs
+ final String ADD_INPUTS = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/create/inputs"; //{componentType}/{componentId}/create/inputs
+ final String DELETE_INPUT_BY_ID = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/delete/%s/input"; //{componentType}/{componentId}/delete/{inputId}/input
+ final String GET_COMPONENT_INPUTS = "http://%s:%s/sdc2/rest/v1/catalog/services/%s/inputs"; //services/{componentId}/inputs
+ final String GET_COMPONENT_INSTANCE_INPUTS = "http://%s:%s/sdc2/rest/v1/catalog/%s/%s/componentInstances/%s/%s/inputs"; //{componentType}/{componentId}/componentInstances/{instanceId}/{originComonentUid}/inputs
+ final String GET_INPUTS_FOR_COMPONENT_INPUT = "http://%s:%s/sdc2/rest/v1/catalog/resources/%s/groups/%s"; //{componentType}/{componentId}/inputs/{inputId}/inputs
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/Config.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/Config.java
new file mode 100644
index 0000000000..ce025715fd
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/Config.java
@@ -0,0 +1,584 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.config;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class Config {
+
+ private static String WINDOWS_CONFIG_FILE = "src/main/resources/ci/conf/sdc.yaml";
+
+ String catalogBeHost;
+ String esHost;
+ String esPort;
+ String neoHost;
+ String neoPort;
+ String disributionClientHost;
+ String disributionClientPort;
+
+ String errorConfigurationFile;
+ String resourceConfigDir;
+ String componentsConfigDir;
+ String importResourceConfigDir;
+ String importResourceTestsConfigDir;
+ String importTypesConfigDir;
+ String testSuites;
+
+ String catalogFeHost;
+ String catalogFePort;
+ String catalogBePort;
+ String catalogBeTlsPort;
+
+ String neoDBusername;
+ String neoDBpassword;
+
+ String titanPropertiesFile;
+ List<String> packages;
+ List<String> bugs;
+ List<String> resourcesNotToDelete;
+ List<String> resourceCategoriesNotToDelete;
+ List<String> serviceCategoriesNotToDelete;
+ Boolean stopOnClassFailure = false;
+
+ private String outputFolder;
+ private String reportName;
+ private String url;
+ private String remoteTestingMachineIP;
+ private String remoteTestingMachinePort;
+ private boolean remoteTesting;
+
+ private String cassandraHost;
+ private String cassandraAuditKeySpace;
+ private String cassandraArtifactKeySpace;
+ private Boolean cassandraAuthenticate;
+ private String cassandraUsername;
+ private String cassandraPassword;
+ private Boolean cassandraSsl;
+ private String cassandraTruststorePath;
+ private String cassandraTruststorePassword;
+
+ private static Config configIt = null;
+
+ private static Yaml yaml = new Yaml();
+
+ private Config() {
+ super();
+ }
+
+ public static class TestPackages {
+
+ List<String> packages;
+ List<String> bugs;
+
+ public List<String> getPackages() {
+ return packages;
+ }
+
+ public void setPackages(List<String> packages) {
+ this.packages = packages;
+ }
+
+ public List<String> getBugs() {
+ return bugs;
+ }
+
+ public void setBugs(List<String> bugs) {
+ this.bugs = bugs;
+ }
+
+ @Override
+ public String toString() {
+ return "TestPackages [packages=" + packages + ", bugs=" + bugs + "]";
+ }
+
+ }
+
+ public synchronized static Config instance() {
+ if (configIt == null) {
+ try {
+ configIt = init();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ return configIt;
+ }
+
+ private static Config init() throws IOException {
+
+ Config config = null;
+
+ String configFile = System.getProperty("config.resource");
+ if (configFile == null) {
+ if (System.getProperty("os.name").contains("Windows")) {
+ configFile = WINDOWS_CONFIG_FILE;
+ } else {
+ throw new RuntimeException("Please Add Jvm Argument config.resource");
+ }
+ }
+
+ File file = new File(configFile);
+ if (false == file.exists()) {
+ throw new RuntimeException("The config file " + configFile + " cannot be found.");
+ }
+
+ InputStream in = null;
+ try {
+
+ in = Files.newInputStream(Paths.get(configFile));
+
+ config = yaml.loadAs(in, Config.class);
+
+ setPackagesAndBugs(configFile, config);
+
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return config;
+ }
+
+ private static void setPackagesAndBugs(String path, Config config) throws IOException {
+
+ int separator = Math.max(path.lastIndexOf("\\"), path.lastIndexOf("/"));
+ String dirPath = path.substring(0, separator + 1);
+ String packagesFile = dirPath + File.separator + "sdc-packages.yaml";
+ File file = new File(packagesFile);
+ if (false == file.exists()) {
+ throw new RuntimeException("The config file " + packagesFile + " cannot be found.");
+ }
+
+ TestPackages testPackages = null;
+ InputStream in = null;
+ try {
+
+ in = Files.newInputStream(Paths.get(packagesFile));
+
+ testPackages = yaml.loadAs(in, TestPackages.class);
+
+ List<String> bugs = testPackages.getBugs();
+ List<String> packages = testPackages.getPackages();
+
+ config.setBugs(bugs);
+ config.setPackages(packages);
+
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+
+ // public Config(String catalogBeHost, String esHost, String esPort, String
+ // resourceConfigDir, String componentsConfigDir, String catalogFeHost,
+ // String catalogFePort, String catalogBePort) {
+ // super();
+ // this.catalogBeHost = catalogBeHost;
+ // this.esHost = esHost;
+ // this.esPort = esPort;
+ // this.resourceConfigDir = resourceConfigDir;
+ // this.componentsConfigDir = componentsConfigDir;
+ // this.catalogFeHost = catalogFeHost;
+ // this.catalogFePort = catalogFePort;
+ // this.catalogBePort = catalogBePort;
+ // }
+
+ String configurationFile;
+
+ public String getConfigurationFile() {
+ return configurationFile;
+ }
+
+ public void setConfigurationFile(String configurationFile) {
+ this.configurationFile = configurationFile;
+ }
+
+ public String getCatalogBePort() {
+ return catalogBePort;
+ }
+
+ public String getDisributionClientHost() {
+ return disributionClientHost;
+ }
+
+ public void setDisributionClientHost(String disributionClientHost) {
+ this.disributionClientHost = disributionClientHost;
+ }
+
+ public String getDisributionClientPort() {
+ return disributionClientPort;
+ }
+
+ public void setDisributionClientPort(String disributionClientPort) {
+ this.disributionClientPort = disributionClientPort;
+ }
+
+ public void setCatalogBePort(String catalogBePort) {
+ this.catalogBePort = catalogBePort;
+ }
+
+ public String getCatalogFeHost() {
+ return catalogFeHost;
+ }
+
+ public void setCatalogFeHost(String catalogFeHost) {
+ this.catalogFeHost = catalogFeHost;
+ }
+
+ public String getCatalogFePort() {
+ return catalogFePort;
+ }
+
+ public void setCatalogFePort(String catalogFePort) {
+ this.catalogFePort = catalogFePort;
+ }
+
+ public String getCatalogBeHost() {
+ return catalogBeHost;
+ }
+
+ public void setCatalogBeHost(String catalogBeHost) {
+ this.catalogBeHost = catalogBeHost;
+ }
+
+ public String getEsHost() {
+ return esHost;
+ }
+
+ public void setEsHost(String esHost) {
+ this.esHost = esHost;
+ }
+
+ public String getEsPort() {
+ return esPort;
+ }
+
+ public void setEsPort(String esPort) {
+ this.esPort = esPort;
+ }
+
+ public String getResourceConfigDir() {
+ return resourceConfigDir;
+ }
+
+ public void setResourceConfigDir(String resourceConfigDir) {
+ this.resourceConfigDir = resourceConfigDir;
+ }
+
+ public String getComponentsConfigDir() {
+ return componentsConfigDir;
+ }
+
+ public void setComponentsConfigDir(String componentsConfigDir) {
+ this.componentsConfigDir = componentsConfigDir;
+ }
+
+ public String getOutputFolder() {
+ return outputFolder;
+ }
+
+ public void setOutputFolder(String outputFolder) {
+ this.outputFolder = outputFolder;
+ }
+
+ public String getReportName() {
+ return reportName;
+ }
+
+ public void setReportName(String reportName) {
+ this.reportName = reportName;
+ }
+
+ public String getNeoPort() {
+ return neoPort;
+ }
+
+ public void setNeoPort(String neoPort) {
+ this.neoPort = neoPort;
+ }
+
+ public String getNeoHost() {
+ return neoHost;
+ }
+
+ public void setNeoHost(String neoHost) {
+ this.neoHost = neoHost;
+ }
+
+ public String getNeoDBpassword() {
+ return neoDBpassword;
+ }
+
+ public String getNeoDBusername() {
+ return neoDBusername;
+ }
+
+ public void setNeoDBusername(String neoDBusername) {
+ this.neoDBusername = neoDBusername;
+ }
+
+ public void setNeoDBpassword(String neoDBpassword) {
+ this.neoDBpassword = neoDBpassword;
+ }
+
+ public String getTitanPropertiesFile() {
+ return titanPropertiesFile;
+ }
+
+ public void setTitanPropertiesFile(String titanPropertiesFile) {
+ this.titanPropertiesFile = titanPropertiesFile;
+ }
+
+ public List<String> getPackages() {
+ return packages;
+ }
+
+ public void setPackages(List<String> packages) {
+ this.packages = packages;
+ }
+
+ public List<String> getBugs() {
+ return bugs;
+ }
+
+ public void setBugs(List<String> bugs) {
+ this.bugs = bugs;
+ }
+
+ public Boolean isStopOnClassFailure() {
+ return stopOnClassFailure;
+ }
+
+ public void setStopOnClassFailure(Boolean stopOnClassFailure) {
+ this.stopOnClassFailure = stopOnClassFailure;
+ }
+
+ public String getImportResourceConfigDir() {
+ return importResourceConfigDir;
+ }
+
+ public void setImportResourceConfigDir(String importResourceConfigDir) {
+ this.importResourceConfigDir = importResourceConfigDir;
+ }
+
+ public String getImportResourceTestsConfigDir() {
+ return importResourceTestsConfigDir;
+ }
+
+ public void setImportResourceTestsConfigDir(String importResourceTestsConfigDir) {
+ this.importResourceTestsConfigDir = importResourceTestsConfigDir;
+ }
+
+ public String getErrorConfigurationFile() {
+ return errorConfigurationFile;
+ }
+
+ public void setErrorConfigurationFile(String errorConfigurationFile) {
+ this.errorConfigurationFile = errorConfigurationFile;
+ }
+
+ public String getCatalogBeTlsPort() {
+ return catalogBeTlsPort;
+ }
+
+ public void setCatalogBeTlsPort(String catalogBeTlsPort) {
+ this.catalogBeTlsPort = catalogBeTlsPort;
+ }
+
+ public List<String> getResourcesNotToDelete() {
+ return resourcesNotToDelete;
+ }
+
+ public void setResourcesNotToDelete(List<String> resourcesNotToDelete) {
+ this.resourcesNotToDelete = resourcesNotToDelete;
+ }
+
+ public List<String> getResourceCategoriesNotToDelete() {
+ return resourceCategoriesNotToDelete;
+ }
+
+ public void setResourceCategoriesNotToDelete(List<String> resourceCategoriesNotToDelete) {
+ this.resourceCategoriesNotToDelete = resourceCategoriesNotToDelete;
+ }
+
+ public List<String> getServiceCategoriesNotToDelete() {
+ return serviceCategoriesNotToDelete;
+ }
+
+ public void setServiceCategoriesNotToDelete(List<String> serviceCategoriesNotToDelete) {
+ this.serviceCategoriesNotToDelete = serviceCategoriesNotToDelete;
+ }
+
+ public String getImportTypesConfigDir() {
+ return importTypesConfigDir;
+ }
+
+ public void setImportTypesConfigDir(String importTypesConfigDir) {
+ this.importTypesConfigDir = importTypesConfigDir;
+ }
+
+ public String getCassandraHost() {
+ return cassandraHost;
+ }
+
+ public void setCassandraHost(String cassandraHost) {
+ this.cassandraHost = cassandraHost;
+ }
+
+ public String getCassandraAuditKeySpace() {
+ return cassandraAuditKeySpace;
+ }
+
+ public void setCassandraAuditKeySpace(String cassandraAuditKeySpace) {
+ this.cassandraAuditKeySpace = cassandraAuditKeySpace;
+ }
+
+ public String getCassandraArtifactKeySpace() {
+ return cassandraArtifactKeySpace;
+ }
+
+ public void setCassandraArtifactKeySpace(String cassandraArtifactKeySpace) {
+ this.cassandraArtifactKeySpace = cassandraArtifactKeySpace;
+ }
+
+ @Override
+ public String toString() {
+ return "Config [catalogBeHost=" + catalogBeHost + ", esHost=" + esHost + ", esPort=" + esPort + ", neoHost="
+ + neoHost + ", neoPort=" + neoPort + ", disributionClientHost=" + disributionClientHost
+ + ", disributionClientPort=" + disributionClientPort + ", errorConfigurationFile="
+ + errorConfigurationFile + ", resourceConfigDir=" + resourceConfigDir + ", componentsConfigDir="
+ + componentsConfigDir + ", importResourceConfigDir=" + importResourceConfigDir
+ + ", importResourceTestsConfigDir=" + importResourceTestsConfigDir + ", importTypesConfigDir="
+ + importTypesConfigDir + ", catalogFeHost=" + catalogFeHost + ", catalogFePort=" + catalogFePort
+ + ", catalogBePort=" + catalogBePort + ", catalogBeTlsPort=" + catalogBeTlsPort + ", neoDBusername="
+ + neoDBusername + ", neoDBpassword=" + neoDBpassword + ", titanPropertiesFile=" + titanPropertiesFile
+ + ", packages=" + packages + ", bugs=" + bugs + ", resourcesNotToDelete=" + resourcesNotToDelete
+ + ", resourceCategoriesNotToDelete=" + resourceCategoriesNotToDelete + ", serviceCategoriesNotToDelete="
+ + serviceCategoriesNotToDelete + ", stopOnClassFailure=" + stopOnClassFailure + ", outputFolder="
+ + outputFolder + ", reportName=" + reportName + ", configurationFile=" + configurationFile + "]";
+ }
+
+ public boolean isRemoteTesting() {
+ return remoteTesting;
+ }
+
+ public void setRemoteTesting(boolean remoteTesting) {
+ this.remoteTesting = remoteTesting;
+ }
+
+ public String getUrl() {
+ try {
+ return url;
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getRemoteTestingMachineIP() {
+ return remoteTestingMachineIP;
+ }
+
+ public void setRemoteTestingMachineIP(String remoteTestingMachineIP) {
+ this.remoteTestingMachineIP = remoteTestingMachineIP;
+ }
+
+ public String getRemoteTestingMachinePort() {
+ return remoteTestingMachinePort;
+ }
+
+ public void setRemoteTestingMachinePort(String remoteTestingMachinePort) {
+ this.remoteTestingMachinePort = remoteTestingMachinePort;
+ }
+
+ public Boolean getCassandraAuthenticate() {
+ return cassandraAuthenticate;
+ }
+
+ public void setCassandraAuthenticate(Boolean cassandraAuthenticate) {
+ this.cassandraAuthenticate = cassandraAuthenticate;
+ }
+
+ public String getCassandraUsername() {
+ return cassandraUsername;
+ }
+
+ public void setCassandraUsername(String cassandraUsername) {
+ this.cassandraUsername = cassandraUsername;
+ }
+
+ public String getCassandraPassword() {
+ return cassandraPassword;
+ }
+
+ public void setCassandraPassword(String cassandraPassword) {
+ this.cassandraPassword = cassandraPassword;
+ }
+
+ public Boolean getCassandraSsl() {
+ return cassandraSsl;
+ }
+
+ public void setCassandraSsl(Boolean cassandraSsl) {
+ this.cassandraSsl = cassandraSsl;
+ }
+
+ public String getCassandraTruststorePath() {
+ return cassandraTruststorePath;
+ }
+
+ public void setCassandraTruststorePath(String cassandraTruststorePath) {
+ this.cassandraTruststorePath = cassandraTruststorePath;
+ }
+
+ public String getCassandraTruststorePassword() {
+ return cassandraTruststorePassword;
+ }
+
+ public void setCassandraTruststorePassword(String cassandraTruststorePassword) {
+ this.cassandraTruststorePassword = cassandraTruststorePassword;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/InvokedMethodListener.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/InvokedMethodListener.java
new file mode 100644
index 0000000000..fbc493ffd6
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/config/InvokedMethodListener.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.testng.IInvokedMethod;
+import org.testng.IInvokedMethodListener;
+import org.testng.ITestResult;
+import org.testng.SkipException;
+import org.testng.internal.TestResult;
+
+public class InvokedMethodListener implements IInvokedMethodListener {
+
+ static Map<String, Integer> methodFailCount = new HashMap<String, Integer>();
+
+ @Override
+
+ public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
+
+ if (methodFailCount.get(method.getTestMethod().getMethodName()) != null
+ && methodFailCount.get(method.getTestMethod().getMethodName()) > 1)
+ throw new SkipException("Skipped due to failure count > 1");
+ ;
+
+ }
+
+ @Override
+
+ public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
+
+ if (testResult.getStatus() == TestResult.FAILURE) {
+ if (methodFailCount.get(method.getTestMethod().getMethodName()) == null)
+ methodFailCount.put(method.getTestMethod().getMethodName(), 1);
+ else {
+ methodFailCount.put(method.getTestMethod().getMethodName(),
+ methodFailCount.get(method.getTestMethod().getMethodName()) + 1);
+ }
+
+ }
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactAssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactAssetStructure.java
new file mode 100644
index 0000000000..41936e2c30
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactAssetStructure.java
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public class ArtifactAssetStructure {
+
+ String artifactName;
+ String artifactType;
+ String artifactURL;
+ String artifactDescription;
+ int artifactTimeout;// optional
+ String artifactChecksum;
+ String artifactUUID;
+ String artifactVersion;
+ String generatedFromUUID;// optional
+
+ public ArtifactAssetStructure(String artifactName, String artifactType, String artifactURL,
+ String artifactDescription, int artifactTimeout, String artifactChecksum, String artifactUUID,
+ String artifactVersion, String generatedFromUUID) {
+ super();
+ this.artifactName = artifactName;
+ this.artifactType = artifactType;
+ this.artifactURL = artifactURL;
+ this.artifactDescription = artifactDescription;
+ this.artifactTimeout = artifactTimeout;
+ this.artifactChecksum = artifactChecksum;
+ this.artifactUUID = artifactUUID;
+ this.artifactVersion = artifactVersion;
+ this.generatedFromUUID = generatedFromUUID;
+ }
+
+ public ArtifactAssetStructure() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(String artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getArtifactURL() {
+ return artifactURL;
+ }
+
+ public void setArtifactURL(String artifactURL) {
+ this.artifactURL = artifactURL;
+ }
+
+ public String getArtifactDescription() {
+ return artifactDescription;
+ }
+
+ public void setArtifactDescription(String artifactDescription) {
+ this.artifactDescription = artifactDescription;
+ }
+
+ public int getArtifactTimeout() {
+ return artifactTimeout;
+ }
+
+ public void setArtifactTimeout(int artifactTimeout) {
+ this.artifactTimeout = artifactTimeout;
+ }
+
+ public String getArtifactChecksum() {
+ return artifactChecksum;
+ }
+
+ public void setArtifactChecksum(String artifactChecksum) {
+ this.artifactChecksum = artifactChecksum;
+ }
+
+ public String getArtifactUUID() {
+ return artifactUUID;
+ }
+
+ public void setArtifactUUID(String artifactUUID) {
+ this.artifactUUID = artifactUUID;
+ }
+
+ public String getArtifactVersion() {
+ return artifactVersion;
+ }
+
+ public void setArtifactVersion(String artifactVersion) {
+ this.artifactVersion = artifactVersion;
+ }
+
+ public String getGeneratedFromUUID() {
+ return generatedFromUUID;
+ }
+
+ public void setGeneratedFromUUID(String generatedFromUUID) {
+ this.generatedFromUUID = generatedFromUUID;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtifactAssetStructure [artifactName=" + artifactName + ", artifactType=" + artifactType
+ + ", artifactURL=" + artifactURL + ", artifactDescription=" + artifactDescription + ", artifactTimeout="
+ + artifactTimeout + ", artifactChecksum=" + artifactChecksum + ", artifactUUID=" + artifactUUID
+ + ", artifactVersion=" + artifactVersion + ", generatedFromUUID=" + generatedFromUUID + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactReqDetails.java
new file mode 100644
index 0000000000..28606af14d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactReqDetails.java
@@ -0,0 +1,226 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+
+public class ArtifactReqDetails {
+
+ public ArtifactReqDetails() {
+
+ }
+
+ public ArtifactReqDetails(String artifactName, String artifactType, String artifactDescription, String payloadData,
+ String artifactLable) {
+ super();
+ this.artifactName = artifactName;
+ this.artifactType = artifactType;
+ this.description = artifactDescription;
+ this.payloadData = payloadData;
+ this.artifactLabel = artifactLable;
+ }
+
+ public ArtifactReqDetails(String artifactLable, ArtifactReqDetails a) {
+ super();
+ this.artifactName = a.getArtifactName();
+ this.artifactType = a.getArtifactType();
+ this.description = a.getArtifactType();
+ this.payloadData = a.getPayload();
+ this.artifactLabel = artifactLable;
+ }
+
+ private String uniqueId;
+ private String artifactName;
+ private String artifactType;
+ private String description;
+ private String payloadData;
+ private String artifactLabel;
+ private String apiUrl;
+ private String artifactGroupType;
+ private Integer timeout;
+ private String userIdLastUpdater;
+ private String creatorFullName;
+ private String updaterFullName;
+ private String artifactChecksum;
+ private String artifactDisplayName;
+ private List<HeatParameterDefinition> heatParameters;
+
+ private boolean mandatory;
+ private boolean serviceApi;
+
+ public boolean isServiceApi() {
+ return serviceApi;
+ }
+
+ public void setServiceApi(boolean serviceApi) {
+ this.serviceApi = serviceApi;
+ }
+
+ public String getArtifactLabel() {
+ return artifactLabel;
+ }
+
+ public void setArtifactLabel(String artifactLabel) {
+ this.artifactLabel = artifactLabel;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(String artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getPayload() {
+ return payloadData;
+ }
+
+ public void setPayload(String payload) {
+ this.payloadData = payload;
+ }
+
+ public void setPayloadData(String payloadData) {
+ this.payloadData = payloadData;
+ }
+
+ public String getArtifactGroupType() {
+ return artifactGroupType;
+ }
+
+ public void setArtifactGroupType(String artifactGroupType) {
+ this.artifactGroupType = artifactGroupType;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public boolean isMandatory() {
+ return mandatory;
+ }
+
+ public void setMandatory(boolean mandatory) {
+ this.mandatory = mandatory;
+ }
+
+ public String getUrl() {
+ return apiUrl;
+ }
+
+ public void setUrl(String url) {
+ this.apiUrl = url;
+ }
+
+ @Override
+ public String toString() {
+ if (!apiUrl.isEmpty()) {
+ return "ArtifactReqDetails [artifactName=" + artifactName + ", artifactType=" + artifactType
+ + ", description=" + description + ", payloadData=" + payloadData + ", artifactLabel="
+ + artifactLabel + ", mandatory=" + mandatory + ", url=" + apiUrl + "]";
+ }
+
+ return "ArtifactReqDetails [artifactName=" + artifactName + ", artifactType=" + artifactType + ", description="
+ + description + ", payloadData=" + payloadData + ", artifactLabel=" + artifactLabel
+ + ", artifactUniqueId=" + uniqueId + ", mandatory=" + mandatory + ", serviceApi=" + serviceApi + "]";
+
+ }
+
+ public String getArtifactDisplayName() {
+
+ return artifactDisplayName;
+ }
+
+ public void setArtifactDisplayName(String artifactDisplayName) {
+ this.artifactDisplayName = artifactDisplayName;
+ }
+
+ public String getUserIdLastUpdater() {
+ return userIdLastUpdater;
+ }
+
+ public void setUserIdLastUpdater(String userIdLastUpdater) {
+ this.userIdLastUpdater = userIdLastUpdater;
+ }
+
+ public String getCreatorFullName() {
+ return creatorFullName;
+ }
+
+ public void setCreatorFullName(String creatorFullName) {
+ this.creatorFullName = creatorFullName;
+ }
+
+ public String getUpdaterFullName() {
+ return updaterFullName;
+ }
+
+ public void setUpdaterFullName(String updaterFullName) {
+ this.updaterFullName = updaterFullName;
+ }
+
+ public String getArtifactChecksum() {
+ return artifactChecksum;
+ }
+
+ public void setArtifactChecksum(String artifactChecksum) {
+ this.artifactChecksum = artifactChecksum;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String artifactUniqueId) {
+ this.uniqueId = artifactUniqueId;
+ }
+
+ public List<HeatParameterDefinition> getHeatParameters() {
+ return heatParameters;
+ }
+
+ public void setHeatParameters(List<HeatParameterDefinition> heatParameters) {
+ this.heatParameters = heatParameters;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/AssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/AssetStructure.java
new file mode 100644
index 0000000000..8820b05772
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/AssetStructure.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public class AssetStructure {
+
+ private String uuid;
+ private String invariantUUID;
+ private String name;
+ private String version;
+ private String toscaModelURL;
+ private String category;
+ private String lifecycleState;
+ private String lastUpdaterUserId;
+
+ public AssetStructure() {
+ super();
+ }
+
+ public AssetStructure(String uuid, String invariantUUID, String name, String version, String toscaModelURL,
+ String category, String lifecycleState, String lastUpdaterUserId) {
+ super();
+ this.uuid = uuid;
+ this.invariantUUID = invariantUUID;
+ this.name = name;
+ this.version = version;
+ this.toscaModelURL = toscaModelURL;
+ this.category = category;
+ this.lifecycleState = lifecycleState;
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "AssetStructure [uuid=" + uuid + ", invariantUUID=" + invariantUUID + ", name=" + name + ", version="
+ + version + ", toscaModelURL=" + toscaModelURL + ", category=" + category + ", lifecycleState="
+ + lifecycleState + ", lastUpdaterUserId=" + lastUpdaterUserId + "]";
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getToscaModelURL() {
+ return toscaModelURL;
+ }
+
+ public void setToscaModelURL(String toscaModelURL) {
+ this.toscaModelURL = toscaModelURL;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public void setLifecycleState(String lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentInstanceReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentInstanceReqDetails.java
new file mode 100644
index 0000000000..549700f384
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentInstanceReqDetails.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import org.openecomp.sdc.be.model.ComponentInstance;
+
+public class ComponentInstanceReqDetails {
+
+ String componentUid;
+ String description;
+ String posX;
+ String posY;
+ String name;
+ String uniqueId;
+
+ public ComponentInstanceReqDetails() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public ComponentInstanceReqDetails(ComponentInstance componentInstance) {
+ super();
+ this.setUniqueId(componentInstance.getUniqueId());
+ this.description = componentInstance.getDescription();
+ this.posX = componentInstance.getPosX();
+ this.posY = componentInstance.getPosY();
+ // this.name = "myResourceInstance";
+ this.name = componentInstance.getName();
+ }
+
+ public ComponentInstanceReqDetails(String resourceUid, String description, String posX, String posY, String name) {
+ super();
+ this.componentUid = resourceUid;
+ this.description = description;
+ this.posX = posX;
+ this.posY = posY;
+ // this.name = "myResourceInstance";
+ this.name = name;
+ }
+
+ public ComponentInstanceReqDetails(String resourceUid, String description, String posX, String posY) {
+ super();
+ this.componentUid = resourceUid;
+ this.description = description;
+ this.posX = posX;
+ this.posY = posY;
+ }
+
+ public String getComponentUid() {
+ return componentUid;
+ }
+
+ public void setComponentUid(String resourceUid) {
+ this.componentUid = resourceUid;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getPosX() {
+ return posX;
+ }
+
+ public void setPosX(String posX) {
+ this.posX = posX;
+ }
+
+ public String getPosY() {
+ return posY;
+ }
+
+ public void setPosY(String posY) {
+ this.posY = posY;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceInstanceReqDetails [resourceUid=" + componentUid + ", description=" + description + ", posX="
+ + posX + ", posY=" + posY + ", name=" + name + ", uniqueId=" + uniqueId + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentReqDetails.java
new file mode 100644
index 0000000000..8546732414
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ComponentReqDetails.java
@@ -0,0 +1,272 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+
+public abstract class ComponentReqDetails {
+
+ protected String name;
+ protected String description;
+ protected List<String> tags = new ArrayList<>();
+ protected String contactId;
+ protected String icon;
+ protected String uniqueId;
+ protected String creatorUserId;
+ protected String creatorFullName;
+ protected String lastUpdaterUserId;
+ protected String lastUpdaterFullName;
+ protected Long creationDate;
+ protected Long lastUpdateDate;
+ protected LifecycleStateEnum lifecycleState;
+ protected String version;
+ protected String UUID;
+ protected List<CategoryDefinition> categories;
+ protected String projectCode;
+ protected String csarUUID;
+ protected String csarVersion;
+ protected String importedToscaChecksum;
+ protected String invariantUUID;
+
+ public String getCsarVersion() {
+ return csarVersion;
+ }
+
+ public void setCsarVersion(String csarVersion) {
+ this.csarVersion = csarVersion;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ tags.add(name);
+ }
+
+ public List<String> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<String> tags) {
+ this.tags = tags;
+ }
+
+ // public String getCategory() {
+ // return category;
+ // }
+ //
+ public String getContactId() {
+ return contactId;
+ }
+
+ public void setContactId(String contactId) {
+ this.contactId = contactId;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public void setCreatorUserId(String creatorUserId) {
+ this.creatorUserId = creatorUserId;
+ }
+
+ public void setCreatorFullName(String creatorFullName) {
+ this.creatorFullName = creatorFullName;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public void setCreationDate(Long creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public void setLastUpdateDate(Long lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public void setLifecycleState(LifecycleStateEnum lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public void setUUID(String uUID) {
+ this.UUID = uUID;
+ }
+
+ public String getCreatorUserId() {
+ return creatorUserId;
+ }
+
+ public String getCreatorFullName() {
+ return creatorFullName;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public Long getCreationDate() {
+ return creationDate;
+ }
+
+ public Long getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public LifecycleStateEnum getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public String getUUID() {
+ return UUID;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public List<CategoryDefinition> getCategories() {
+ return categories;
+ }
+
+ public void setCategories(List<CategoryDefinition> categories) {
+ this.categories = categories;
+ }
+
+ public void removeAllCategories() {
+ this.categories = new ArrayList<>();
+ }
+
+ public void addCategoryChain(String category, String subCategory) {
+ if (category != null || subCategory != null) {
+ if (categories == null) {
+ categories = new ArrayList<>();
+ }
+ CategoryDefinition selectedCategory = null;
+ for (CategoryDefinition categoryDef : categories) {
+ if (categoryDef.getName().equals(category)) {
+ selectedCategory = categoryDef;
+ }
+ }
+ if (selectedCategory == null) {
+ selectedCategory = new CategoryDefinition();
+ selectedCategory.setName(category);
+ categories.add(selectedCategory);
+ }
+ if (subCategory != null) {
+ List<SubCategoryDefinition> subcategories = selectedCategory.getSubcategories();
+ if (subcategories == null) {
+ subcategories = new ArrayList<>();
+ selectedCategory.setSubcategories(subcategories);
+ }
+ SubCategoryDefinition selectedSubcategory = null;
+ for (SubCategoryDefinition subcategory : subcategories) {
+ if (subcategory.getName().equals(subCategory)) {
+ selectedSubcategory = subcategory;
+ }
+ }
+ if (selectedSubcategory == null) {
+ selectedSubcategory = new SubCategoryDefinition();
+ selectedSubcategory.setName(subCategory);
+ subcategories.add(selectedSubcategory);
+ }
+ }
+ }
+ }
+
+ public void addCategory(String category) {
+ addCategoryChain(category, null);
+ }
+
+ public String getProjectCode() {
+ return projectCode;
+ }
+
+ public void setProjectCode(String projectCode) {
+ this.projectCode = projectCode;
+ }
+
+ public String getCsarUUID() {
+ return csarUUID;
+ }
+
+ public void setCsarUUID(String csarUUID) {
+ this.csarUUID = csarUUID;
+ }
+
+ public String getImportedToscaChecksum() {
+ return importedToscaChecksum;
+ }
+
+ public void setImportedToscaChecksum(String importedToscaChecksum) {
+ this.importedToscaChecksum = importedToscaChecksum;
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GroupHeatMetaDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GroupHeatMetaDefinition.java
new file mode 100644
index 0000000000..690f63955d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GroupHeatMetaDefinition.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GroupHeatMetaDefinition {
+
+ int group = 0;
+ List<String> artifactList = new ArrayList<String>();
+ PropertyHeatMetaDefinition propertyHeatMetaDefinition;
+
+ public PropertyHeatMetaDefinition getPropertyHeatMetaDefinition() {
+ return propertyHeatMetaDefinition;
+ }
+
+ public void setPropertyHeatMetaDefinition(PropertyHeatMetaDefinition propertyHeatMetaDefinition) {
+ this.propertyHeatMetaDefinition = propertyHeatMetaDefinition;
+ }
+
+ public GroupHeatMetaDefinition() {
+ super();
+ }
+
+ public int getGroup() {
+ return group;
+ }
+
+ public void setGroup(int group) {
+ this.group = group;
+ }
+
+ public List<String> getArtifactList() {
+ return artifactList;
+ }
+
+ public void setArtifactList(List<String> artifactList) {
+ this.artifactList = artifactList;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ImportReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ImportReqDetails.java
new file mode 100644
index 0000000000..0089eec5a7
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ImportReqDetails.java
@@ -0,0 +1,332 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.yaml.snakeyaml.Yaml;
+
+public class ImportReqDetails extends ResourceReqDetails {
+
+ private String payloadName;
+ private String payloadData;
+
+ private Map<String, Object> requirements;
+ private Map<String, Object> capabilities;
+
+ private List<String> derivedList;
+ private String derivedFromField;
+
+ public ImportReqDetails(String resourceName, String description, List<String> tags, List<String> derivedFrom,
+ String vendorName, String vendorRelease, String contactId, String icon) {
+ super(resourceName, description, tags, null, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ }
+
+ public String getPayloadName() {
+ return payloadName;
+ }
+
+ public void setPayloadName(String payloadName) {
+ this.payloadName = payloadName;
+ }
+
+ public String getPayloadData() {
+ return payloadData;
+ }
+
+ public void setPayloadData(String payloadData) {
+ this.payloadData = payloadData;
+ }
+
+ @Override
+ public String toString() {
+ return "ImportReqDetails [payloadName=" + payloadName + ", payloadData=" + payloadData + "]";
+ }
+
+ public void setReqirementsAndCapabilities(String path, String fileName, User user, String derivedFromSource)
+ throws Exception {
+ setRequirements(path, fileName, user, derivedFromSource);
+ setCapabilities(path, fileName, user, derivedFromSource);
+ }
+
+ public List<String> getDerivedList() {
+ return derivedList;
+ }
+
+ public void setDerivedList(List<String> derivedList) {
+ this.derivedList = derivedList;
+ }
+
+ public String getDerivedFromField() {
+ return derivedFromField;
+ }
+
+ public void setDerivedFromField(String derivedFromField) {
+ this.derivedFromField = derivedFromField;
+ }
+
+ public Map<String, Object> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(String path, String fileName, User user, String derivedFromSource) throws Exception {
+ Map<String, Object> requirementsFromFile = getRequirementsMapFromFile(path + File.separator + fileName,
+ toscaResourceName, "requirements");
+ Map<String, Object> requirements = organizeRequirementsMap(requirementsFromFile);
+ getDerivedReqCap(user, requirements, "requirements", derivedFromSource);
+ this.requirements = requirements;
+ }
+
+ private void getDerivedReqCap(User user, Map<String, Object> reqCapMap, String field, String derivedFromResource)
+ throws IOException, JSONException {
+
+ if (derivedFromResource == null) {
+ derivedFromResource = "Root";
+ }
+
+ RestResponse rest = getResourceSource(user, derivedFromResource);
+ Map<String, Object> parsedFieldFromResponseAsMap = ResponseParser.getJsonValueAsMap(rest, field);
+ Iterator<String> iterator = parsedFieldFromResponseAsMap.keySet().iterator();
+ Map<String, Object> convertListToMap = null;
+ while (iterator.hasNext()) {
+ String type = iterator.next();
+ List<Object> lst = (List<Object>) parsedFieldFromResponseAsMap.get(type);
+ convertListToMap = convertListToMap(lst);
+
+ if (field.equals("capabilities")) {
+ convertListToMap.replace("capabilitySources", derivedList);
+ lst = new ArrayList<Object>(Arrays.asList(convertListToMap));
+ }
+
+ Object existingValue = reqCapMap.get(type);
+ if (existingValue != null) {
+ Map<String, Object> convertedExistingValue = convertListToMap((List<Object>) existingValue);
+ if (convertedExistingValue.get("name").toString().toLowerCase()
+ .equals(convertListToMap.get("name").toString().toLowerCase())) {
+ lst = new ArrayList<Object>(Arrays.asList(convertedExistingValue));
+ } else {
+ lst.add(convertedExistingValue);
+ }
+ }
+
+ reqCapMap.put(type, lst);
+ }
+ }
+
+ private RestResponse getResourceSource(User user, String source) throws IOException, JSONException {
+ org.codehaus.jettison.json.JSONObject getResourceJSONObject = null;
+ RestResponse rest = ResourceRestUtils.getResourceByNameAndVersion(user.getUserId(), source, "1.0");
+ if (rest.getErrorCode().intValue() == 200) {
+ JSONArray jArray = new JSONArray(rest.getResponse());
+ for (int i = 0; i < jArray.length(); i++) {
+ getResourceJSONObject = jArray.getJSONObject(i);
+ String resourceType = getResourceJSONObject.get("resourceType").toString();
+ if (!resourceType.equals("VF")) {
+ rest.setResponse(getResourceJSONObject.toString());
+ }
+ }
+ }
+ return rest;
+ }
+
+ public Map<String, Object> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(String path, String fileName, User user, String derivedFromSource) throws Exception {
+ Map<String, Object> capabilitiesFromFile = getCapabilitiesMapFromFile(path + File.separator + fileName,
+ toscaResourceName, "capabilities");
+ Map<String, Object> capabilities = organizeCapabilitiesMap(capabilitiesFromFile);
+ getDerivedReqCap(user, capabilities, "capabilities", derivedFromSource);
+ this.capabilities = capabilities;
+ }
+
+ private Map<String, Object> organizeCapabilitiesMap(Map<String, Object> capabilitiesFromFile) {
+ Iterator<String> iterator = capabilitiesFromFile.keySet().iterator();
+ Map<String, Object> capMap = new HashMap<String, Object>();
+ while (iterator.hasNext()) {
+ List<Object> valueList = new ArrayList<Object>();
+ String next = iterator.next();
+ Map<String, Object> valuesMap = (Map<String, Object>) capabilitiesFromFile.get(next);
+ String key = valuesMap.remove("type").toString();
+ valuesMap.put("name", next);
+ valuesMap.put("capabilitySources", derivedList);
+ valuesMap.put("type", key);
+
+ if (!valuesMap.containsKey("occurrences")) {
+ valuesMap.put("minOccurrences", "1");
+ valuesMap.put("maxOccurrences", "UNBOUNDED");
+ }
+
+ Object tempValue = capMap.get(key);
+ if (tempValue == null) {
+ valueList.add(valuesMap);
+ } else {
+ Map<String, Object> convertValue = convertListToMap((List<Object>) tempValue);
+ valueList = new ArrayList<Object>(Arrays.asList(convertValue, valuesMap));
+ }
+ capMap.put(key, valueList);
+ }
+ return capMap;
+ }
+
+ private Map<String, Object> getCapabilitiesMapFromFile(String fileName, String toscaResourceName,
+ String fieldToTest) throws Exception {
+ Map<String, Object> resourceToscaMap = getToscaResourceFromFile(fileName, toscaResourceName);
+ Object capMap = resourceToscaMap.get(fieldToTest);
+ if (capMap == null) {
+ return new HashMap<String, Object>();
+ }
+ return (Map<String, Object>) capMap;
+ }
+
+ private Map<String, Object> organizeRequirementsMap(Map<String, Object> requirementsFromFile) {
+ Map<String, Object> reqMap = new HashMap<String, Object>();
+ List<Object> valueList = new ArrayList<Object>();
+ Iterator<String> iterator = requirementsFromFile.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Map<String, Object> valuesMap = (Map<String, Object>) requirementsFromFile.get(key);
+ valuesMap.put("name", key);
+ String capability = valuesMap.get("capability").toString();
+
+ List<Object> occurencesList = (List<Object>) valuesMap.remove("occurrences");
+ if (occurencesList != null) {
+ valuesMap.put("minOccurrences", occurencesList.get(0).toString());
+ valuesMap.put("maxOccurrences", occurencesList.get(1).toString());
+ }
+
+ valueList.add(valuesMap);
+ reqMap.put(capability, valueList);
+ }
+
+ return reqMap;
+ }
+
+ private Map<String, Object> getRequirementsMapFromFile(String fileName, String toscaResourceName,
+ String fieldToTest) throws Exception {
+ Map<String, Object> resourceToscaMap = getToscaResourceFromFile(fileName, toscaResourceName);
+ List<Object> reqListFromFile = (List<Object>) resourceToscaMap.get(fieldToTest);
+ if (reqListFromFile == null) {
+ return new HashMap<String, Object>();
+ }
+ Map<String, Object> testedMapFromFile = convertListToMap(reqListFromFile);
+ return testedMapFromFile;
+ }
+
+ private Map<String, Object> getToscaResourceFromFile(String fullFileName, String toscaResourceName)
+ throws Exception {
+ Map<String, Object> nodesTypesMap = getNodesTypesMapFromFile(fullFileName);
+ Map<String, Object> resourceToscaMap = (Map<String, Object>) nodesTypesMap.get(toscaResourceName);
+
+ derivedFromField = resourceToscaMap.get("derived_from").toString();
+
+ return resourceToscaMap;
+ }
+
+ private Map<String, Object> getNodesTypesMapFromFile(String fullFileName) throws FileNotFoundException {
+ Yaml yaml = new Yaml();
+ File file = new File(fullFileName);
+ InputStream inputStream = new FileInputStream(file);
+ Map<?, ?> mapFromFile = (Map<?, ?>) yaml.load(inputStream);
+ Map<String, Object> nodesTypesMap = (Map<String, Object>) mapFromFile.get("node_types");
+ return nodesTypesMap;
+ }
+
+ private Map<String, Object> convertListToMap(List<Object> testedListFromFile) {
+ Map<String, Object> testedMapFromFile = new HashMap<String, Object>();
+ for (int i = 0; i < testedListFromFile.size(); i++) {
+ Object req = testedListFromFile.get(i);
+ ObjectMapper m = new ObjectMapper();
+ Map<? extends String, ? extends String> mappedObject = m.convertValue(req, Map.class);
+ testedMapFromFile.putAll(mappedObject);
+ }
+ return testedMapFromFile;
+ }
+
+ public void compareRequirementsOrCapabilities(Map<String, Object> exepectedReq, Map<String, Object> actualReq) {
+ Iterator<String> iterator = exepectedReq.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ List<Object> expectedValues = (List<Object>) exepectedReq.get(key);
+ List<Object> actualValues = (List<Object>) actualReq.get(key);
+ assertNotNull(actualValues);
+
+ List<Map<String, Object>> expectedMapsList = convertListToMapList(expectedValues);
+ List<Map<String, Object>> actualMapsList = convertListToMapList(actualValues);
+ assertEquals(expectedMapsList.size(), actualMapsList.size());
+
+ for (int i = 0; i < expectedMapsList.size(); i++) {
+ Map<String, Object> expectedMap = expectedMapsList.get(i);
+ Map<String, Object> actualdMap = actualMapsList.get(i);
+ if (expectedMap.get("name").equals(actualdMap.get("name"))) {
+ Iterator<String> iterator2 = expectedMap.keySet().iterator();
+ while (iterator2.hasNext()) {
+ String innerKey = iterator2.next();
+ assertTrue(
+ "check " + innerKey + " in " + key + ":\nexpected: "
+ + expectedMap.get(innerKey).toString() + "\nactual: "
+ + actualdMap.get(innerKey).toString(),
+ expectedMap.get(innerKey).equals(actualdMap.get(innerKey)));
+
+ }
+
+ }
+ }
+ }
+ }
+
+ private List<Map<String, Object>> convertListToMapList(List<Object> testedListFromFile) {
+ List<Map<String, Object>> listOfMaps = new ArrayList<Map<String, Object>>();
+ for (int i = 0; i < testedListFromFile.size(); i++) {
+ Object req = testedListFromFile.get(i);
+ ObjectMapper m = new ObjectMapper();
+ Map<? extends String, ? extends String> mappedObject = m.convertValue(req, Map.class);
+ mappedObject.remove("uniqueId");
+ Map<String, Object> testedMapFromFile = new HashMap<String, Object>();
+ testedMapFromFile.putAll(mappedObject);
+ listOfMaps.add(testedMapFromFile);
+ }
+ return listOfMaps;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ProductReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ProductReqDetails.java
new file mode 100644
index 0000000000..f2484e274d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ProductReqDetails.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+
+public class ProductReqDetails extends ComponentReqDetails {
+
+ private String fullName;
+ private List<String> contacts;
+ private String isActive;
+
+ public ProductReqDetails(String name, List<CategoryDefinition> category) {
+ this.categories = category;
+ this.name = name;
+ }
+
+ public ProductReqDetails(String name) {
+ this.name = name;
+ }
+
+ public void addCategory(CategoryDefinition category) {
+ if (categories == null) {
+ categories = new ArrayList<>();
+ }
+ categories.add(category);
+ }
+
+ public void addContact(String contactUserId) {
+ if (contacts == null) {
+ contacts = new ArrayList<>();
+ }
+ contacts.add(contactUserId);
+ }
+
+ public List<String> getContacts() {
+ return contacts;
+ }
+
+ public void setContacts(List<String> contacts) {
+ this.contacts = contacts;
+ }
+
+ public List<CategoryDefinition> getCategories() {
+ return categories;
+ }
+
+ public void setCategories(List<CategoryDefinition> categories) {
+ this.categories = categories;
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+
+ public String getActive() {
+ return isActive;
+ }
+
+ public void setActive(String isActive) {
+ this.isActive = isActive;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyHeatMetaDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyHeatMetaDefinition.java
new file mode 100644
index 0000000000..6271e1f59d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyHeatMetaDefinition.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public class PropertyHeatMetaDefinition {
+
+ String name;
+ boolean value;
+
+ public PropertyHeatMetaDefinition() {
+ super();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isValue() {
+ return value;
+ }
+
+ public void setValue(boolean value) {
+ this.value = value;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyHeatMetaDefinition [name=" + name + ", value=" + value + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyReqDetails.java
new file mode 100644
index 0000000000..208e4aa3ea
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyReqDetails.java
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+
+public class PropertyReqDetails {
+ String name;
+ String type;
+ Boolean required = false;
+ String defaultValue;
+ String description;
+ String propertyRangeMin;
+ String propertyRangeMax;
+ Boolean isPassword = false;
+ SchemaDefinition schema;
+
+ public PropertyReqDetails() {
+ super();
+ }
+
+ public PropertyReqDetails(String propertyName, String propertyType, Boolean propertyRequired,
+ String propertyDefaultValue, String propertyDescription, String propertyRangeMin, String propertyRangeMax,
+ Boolean propertyPassword) {
+ super();
+ this.name = propertyName;
+ this.type = propertyType;
+ this.required = propertyRequired;
+ this.defaultValue = propertyDefaultValue;
+ this.description = propertyDescription;
+ this.propertyRangeMin = propertyRangeMin;
+ this.propertyRangeMax = propertyRangeMax;
+ this.isPassword = propertyPassword;
+ }
+
+ public PropertyReqDetails(String propertyName, String propertyType, String propertyDefaultValue,
+ String propertyDescription, SchemaDefinition schema) {
+ super();
+ this.name = propertyName;
+ this.type = propertyType;
+ this.defaultValue = propertyDefaultValue;
+ this.description = propertyDescription;
+ this.schema = schema;
+ }
+
+ public SchemaDefinition getSchema() {
+ return schema;
+ }
+
+ public void setSchema(SchemaDefinition schema) {
+ this.schema = schema;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String propertyName) {
+ this.name = propertyName;
+ }
+
+ public String getPropertyType() {
+ return type;
+ }
+
+ public void setPropertyType(String propertyType) {
+ this.type = propertyType;
+ }
+
+ public Boolean getPropertyRequired() {
+ return required;
+ }
+
+ public void setPropertyRequired(Boolean propertyRequired) {
+ this.required = propertyRequired;
+ }
+
+ public String getPropertyDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setPropertyDefaultValue(String propertyDefaultValue) {
+ this.defaultValue = propertyDefaultValue;
+ }
+
+ public String getPropertyDescription() {
+ return description;
+ }
+
+ public void setPropertyDescription(String propertyDescription) {
+ this.description = propertyDescription;
+ }
+
+ public String getPropertyRangeMin() {
+ return propertyRangeMin;
+ }
+
+ public void setPropertyRangeMin(String propertyRangeMin) {
+ this.propertyRangeMin = propertyRangeMin;
+ }
+
+ public String getPropertyRangeMax() {
+ return propertyRangeMax;
+ }
+
+ public void setPropertyRangeMax(String propertyRangeMax) {
+ this.propertyRangeMax = propertyRangeMax;
+ }
+
+ public Boolean getPropertyPassword() {
+ return isPassword;
+ }
+
+ public void setPropertyPassword(Boolean propertyPassword) {
+ this.isPassword = propertyPassword;
+ }
+
+ public String propertyToJsonString() {
+ String jsonString;
+ jsonString = "{\"" + this.getName() + "\":{" + "\"type\":\"" + this.getPropertyType() + "\"," + "\"required\":"
+ + this.getPropertyRequired() + "," + "\"defaultValue\":\"" + this.getPropertyDefaultValue() + "\","
+ + "\"description\":\"" + this.getPropertyDescription() + "\"," + "\"constraints\":[{\"inRange\":[\""
+ + this.getPropertyRangeMin() + "\",\"" + this.getPropertyRangeMax() + "\"]}]," + "\"isPassword\":"
+ + this.getPropertyPassword() + "}}";
+ return jsonString;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceAssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceAssetStructure.java
new file mode 100644
index 0000000000..d429d05a51
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceAssetStructure.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public class ResourceAssetStructure extends AssetStructure {
+
+ private String subCategory;
+ private String resourceType;
+
+ public ResourceAssetStructure() {
+ super();
+ }
+
+ public ResourceAssetStructure(String uuid, String invariantUUID, String name, String version, String toscaModelURL,
+ String category, String lifecycleState, String lastUpdaterUserId) {
+ super(uuid, invariantUUID, name, version, toscaModelURL, category, lifecycleState, lastUpdaterUserId);
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceAssetStructure [subCategory=" + subCategory + ", resourceType=" + resourceType + "]";
+ }
+
+ public String getSubCategory() {
+ return subCategory;
+ }
+
+ public void setSubCategory(String subCategory) {
+ this.subCategory = subCategory;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceDetailedAssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceDetailedAssetStructure.java
new file mode 100644
index 0000000000..737343f24c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceDetailedAssetStructure.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+public class ResourceDetailedAssetStructure extends ResourceAssetStructure {
+
+ private String lastUpdaterFullName;
+ private String toscaResourceName;
+ private List<ResourceInstanceAssetStructure> resources;
+ private List<ArtifactAssetStructure> artifacts;
+
+ public ResourceDetailedAssetStructure() {
+ super();
+ }
+
+ public ResourceDetailedAssetStructure(String lastUpdaterFullName, String toscaResourceName,
+ List<ResourceInstanceAssetStructure> resources, List<ArtifactAssetStructure> artifacts) {
+ super();
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ this.toscaResourceName = toscaResourceName;
+ this.resources = resources;
+ this.artifacts = artifacts;
+ }
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public String getToscaResourceName() {
+ return toscaResourceName;
+ }
+
+ public void setToscaResourceName(String toscaResourceName) {
+ this.toscaResourceName = toscaResourceName;
+ }
+
+ public List<ResourceInstanceAssetStructure> getResources() {
+ return resources;
+ }
+
+ public void setResources(List<ResourceInstanceAssetStructure> resources) {
+ this.resources = resources;
+ }
+
+ public List<ArtifactAssetStructure> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactAssetStructure> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceDetailedAssetStructure [lastUpdaterFullName=" + lastUpdaterFullName + ", toscaResourceName="
+ + toscaResourceName + ", resources=" + resources + ", artifacts=" + artifacts + ", toString()="
+ + super.toString() + ", getSubCategory()=" + getSubCategory() + ", getResourceType()="
+ + getResourceType() + ", getUuid()=" + getUuid() + ", getInvariantUUID()=" + getInvariantUUID()
+ + ", getName()=" + getName() + ", getVersion()=" + getVersion() + ", getToscaModelURL()="
+ + getToscaModelURL() + ", getCategory()=" + getCategory() + ", getLifecycleState()="
+ + getLifecycleState() + ", getLastUpdaterUserId()=" + getLastUpdaterUserId() + ", getClass()="
+ + getClass() + ", hashCode()=" + hashCode() + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceInstanceAssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceInstanceAssetStructure.java
new file mode 100644
index 0000000000..6a69120a05
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceInstanceAssetStructure.java
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+public class ResourceInstanceAssetStructure {
+
+ String resourceInstanceName;
+ String resourceName;
+ String resourceInvariantUUID;
+ String resourceVersion;
+ String resoucreType;
+ String resourceUUID;
+ List<ArtifactAssetStructure> artifacts;
+
+ public ResourceInstanceAssetStructure() {
+ super();
+ }
+
+ public ResourceInstanceAssetStructure(String resourceInstanceName, String resourceName,
+ String resourceInvariantUUID, String resourceVersion, String resoucreType, String resourceUUID,
+ List<ArtifactAssetStructure> artifacts) {
+ super();
+ this.resourceInstanceName = resourceInstanceName;
+ this.resourceName = resourceName;
+ this.resourceInvariantUUID = resourceInvariantUUID;
+ this.resourceVersion = resourceVersion;
+ this.resoucreType = resoucreType;
+ this.resourceUUID = resourceUUID;
+ this.artifacts = artifacts;
+ }
+
+ public String getResourceInstanceName() {
+ return resourceInstanceName;
+ }
+
+ public void setResourceInstanceName(String resourceInstanceName) {
+ this.resourceInstanceName = resourceInstanceName;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceInvariantUUID() {
+ return resourceInvariantUUID;
+ }
+
+ public void setResourceInvariantUUID(String resourceInvariantUUID) {
+ this.resourceInvariantUUID = resourceInvariantUUID;
+ }
+
+ public String getResourceVersion() {
+ return resourceVersion;
+ }
+
+ public void setResourceVersion(String resourceVersion) {
+ this.resourceVersion = resourceVersion;
+ }
+
+ public String getResoucreType() {
+ return resoucreType;
+ }
+
+ public void setResoucreType(String resoucreType) {
+ this.resoucreType = resoucreType;
+ }
+
+ public String getResourceUUID() {
+ return resourceUUID;
+ }
+
+ public void setResourceUUID(String resourceUUID) {
+ this.resourceUUID = resourceUUID;
+ }
+
+ public List<ArtifactAssetStructure> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactAssetStructure> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceInstanceAssetStructure [resourceInstanceName=" + resourceInstanceName + ", resourceName="
+ + resourceName + ", resourceInvariantUUID=" + resourceInvariantUUID + ", resourceVersion="
+ + resourceVersion + ", resoucreType=" + resoucreType + ", resourceUUID=" + resourceUUID + ", artifacts="
+ + artifacts + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceReqDetails.java
new file mode 100644
index 0000000000..8456e5b35b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceReqDetails.java
@@ -0,0 +1,220 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+
+public class ResourceReqDetails extends ComponentReqDetails {
+ List<String> derivedFrom;
+ String vendorName;
+ String vendorRelease;
+
+ // Unsettable/unupdatable fields
+
+ Boolean isAbstract;
+ Boolean isHighestVersion;
+ String cost;
+ String licenseType;
+ String toscaResourceName;
+
+ private String resourceType = ResourceTypeEnum.VFC.toString(); // Default
+ // value
+
+ public ResourceReqDetails() {
+ super();
+ }
+
+ public ResourceReqDetails(Resource resource) {
+ super();
+ this.resourceType = resource.getResourceType().toString();
+ this.name = resource.getName();
+ this.description = resource.getDescription();
+ this.tags = resource.getTags();
+ // this.category = resource.getCategories();
+ this.derivedFrom = resource.getDerivedFrom();
+ this.vendorName = resource.getVendorName();
+ this.vendorRelease = resource.getVendorRelease();
+ this.contactId = resource.getContactId();
+ this.icon = resource.getIcon();
+ this.toscaResourceName = resource.getToscaResourceName();
+ this.uniqueId = resource.getUniqueId();
+ this.creatorUserId = resource.getCreatorUserId();
+ this.creatorFullName = resource.getCreatorFullName();
+ this.lastUpdaterUserId = resource.getLastUpdaterUserId();
+ this.lastUpdaterFullName = resource.getLastUpdaterFullName();
+ this.lifecycleState = resource.getLifecycleState();
+ this.version = resource.getVersion();
+ this.UUID = resource.getUUID();
+ this.categories = resource.getCategories();
+ this.importedToscaChecksum = resource.getImportedToscaChecksum();
+
+ }
+
+ public ResourceReqDetails(String resourceName, String description, List<String> tags, String category,
+ List<String> derivedFrom, String vendorName, String vendorRelease, String contactId, String icon) {
+ this(resourceName, description, tags, category, derivedFrom, vendorName, vendorRelease, contactId, icon,
+ ResourceTypeEnum.VFC.toString());
+ }
+
+ // new
+ public ResourceReqDetails(String resourceName, String description, List<String> tags, String category,
+ List<String> derivedFrom, String vendorName, String vendorRelease, String contactId, String icon,
+ String resourceType) {
+ super();
+ this.resourceType = resourceType;
+ this.name = resourceName;
+ this.description = description;
+ this.tags = tags;
+ // this.category = category;
+ this.derivedFrom = derivedFrom;
+ this.vendorName = vendorName;
+ this.vendorRelease = vendorRelease;
+ this.contactId = contactId;
+ this.icon = icon;
+ if (category != null) {
+ String[] arr = category.split("/");
+ if (arr.length == 2) {
+ addCategoryChain(arr[0], arr[1]);
+ }
+ }
+ this.toscaResourceName = resourceName;
+ }
+
+ public ResourceReqDetails(ResourceReqDetails originalResource, String version) {
+ super();
+ this.name = originalResource.getName();
+ this.description = originalResource.getDescription();
+ this.tags = originalResource.getTags();
+ // this.category = originalResource.getCategory();
+ this.derivedFrom = originalResource.getDerivedFrom();
+ this.vendorName = originalResource.getVendorName();
+ this.vendorRelease = originalResource.getVendorRelease();
+ this.contactId = originalResource.getContactId();
+ this.icon = originalResource.getIcon();
+ this.version = version;
+ this.uniqueId = originalResource.getUniqueId();
+ this.categories = originalResource.getCategories();
+ this.toscaResourceName = originalResource.getToscaResourceName();
+ this.resourceType = originalResource.getResourceType();
+ }
+
+ public ResourceReqDetails(String resourceName, List<String> derivedFrom, String vendorName, String vendorRelease,
+ String resourceVersion, Boolean isAbstract, Boolean isHighestVersion, String cost, String licenseType,
+ String resourceType) {
+ super();
+ this.name = resourceName;
+ this.derivedFrom = derivedFrom;
+ this.vendorName = vendorName;
+ this.vendorRelease = vendorRelease;
+ this.version = resourceVersion;
+ this.isAbstract = isAbstract;
+ this.isHighestVersion = isHighestVersion;
+ this.cost = cost;
+ this.licenseType = licenseType;
+ this.resourceType = resourceType;
+ this.toscaResourceName = resourceName;
+ }
+
+ public String getToscaResourceName() {
+ return toscaResourceName;
+ }
+
+ public void setToscaResourceName(String toscaResourceName) {
+ this.toscaResourceName = toscaResourceName;
+ }
+
+ public List<String> getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(List<String> derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public String getVendorName() {
+ return vendorName;
+ }
+
+ public void setVendorName(String vendorName) {
+ this.vendorName = vendorName;
+ }
+
+ public String getVendorRelease() {
+ return vendorRelease;
+ }
+
+ public void setVendorRelease(String vendorRelease) {
+ this.vendorRelease = vendorRelease;
+ }
+
+ public String getCost() {
+ return cost;
+ }
+
+ public void setCost(String cost) {
+ this.cost = cost;
+ }
+
+ public String getLicenseType() {
+ return licenseType;
+ }
+
+ public void setLicenseType(String licenseType) {
+ this.licenseType = licenseType;
+ }
+
+ // Unupdatable fields - to check that they are not updated
+ public void setIsAbstract(Boolean isAbstract) {
+ this.isAbstract = isAbstract;
+ }
+
+ public void setIsHighestVersion(Boolean isHighestVersion) {
+ this.isHighestVersion = isHighestVersion;
+ }
+
+ public Boolean getIsAbstract() {
+ return isAbstract;
+ }
+
+ public Boolean getIsHighestVersion() {
+ return isHighestVersion;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceReqDetails [name=" + name + ", derivedFrom=" + derivedFrom + ", vendorName=" + vendorName
+ + ", vendorRelease=" + vendorRelease + ", version=" + version + ", isAbstract=" + isAbstract
+ + ", isHighestVersion=" + isHighestVersion + ", cost=" + cost + ", licenseType=" + licenseType
+ + ", resourceType=" + resourceType + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceRespJavaObject.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceRespJavaObject.java
new file mode 100644
index 0000000000..eb473ab39f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceRespJavaObject.java
@@ -0,0 +1,337 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+
+public class ResourceRespJavaObject {
+ String uniqueId;
+ String name;
+ String version;
+ String creatorUserId;
+ String creatorFullName;
+ String lastUpdaterUserId;
+ String lastUpdaterFullName;
+ String description;
+ String icon;
+ List<String> tags;
+ String isHighestVersion;
+ String creationDate;
+ String lastUpdateDate;
+ // String category;
+ String lifecycleState;
+ List<String> derivedFrom;
+ String vendorName;
+ String vendorRelease;
+ String contactId;
+ String abstractt;
+ String highestVersion;
+ List<String> artifacts;
+ List<String> interfaces;
+ String uuid;
+ String cost;
+ String licenseType;
+ String resourceType;
+ List<CategoryDefinition> categories;
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public ResourceRespJavaObject(String uniqueId, String resourceName, String resourceVersion, String creatorUserId,
+ String creatorFullName, String lastUpdaterUserId, String lastUpdaterFullName, String description,
+ String icon, List<String> tags, String isHighestVersion, String creationDate, String lastUpdateDate,
+ String category, String lifecycleState, List<String> derivedFrom, String vendorName, String vendorRelease,
+ String contactId, String abstractt, String highestVersion, List<String> artifacts, List<String> interfaces,
+ String uuid, String cost, String licenseType, String resourceType) {
+ super();
+ this.uniqueId = uniqueId;
+ this.name = resourceName;
+ this.version = resourceVersion;
+ this.creatorUserId = creatorUserId;
+ this.creatorFullName = creatorFullName;
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ this.description = description;
+ this.icon = icon;
+ this.tags = tags;
+ this.isHighestVersion = isHighestVersion;
+ this.creationDate = creationDate;
+ this.lastUpdateDate = lastUpdateDate;
+ // this.category = category;
+ this.lifecycleState = lifecycleState;
+ this.derivedFrom = derivedFrom;
+ this.vendorName = vendorName;
+ this.vendorRelease = vendorRelease;
+ this.contactId = contactId;
+ this.abstractt = abstractt;
+ this.highestVersion = highestVersion;
+ this.artifacts = artifacts;
+ this.interfaces = interfaces;
+ this.uuid = uuid;
+ this.cost = cost;
+ this.licenseType = licenseType;
+ this.resourceType = resourceType;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getCost() {
+ return cost;
+ }
+
+ public void setCost(String cost) {
+ this.cost = cost;
+ }
+
+ public String getLicenseType() {
+ return licenseType;
+ }
+
+ public void setLicenseType(String licenseType) {
+ this.licenseType = licenseType;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public String setUuid() {
+ return uuid;
+ }
+
+ public List<String> getInterfaces() {
+ return interfaces;
+ }
+
+ public void setInterfaces(List<String> interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ public List<String> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<String> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ public ResourceRespJavaObject() {
+ super();
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String resourceName) {
+ this.name = resourceName;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String resourceVersion) {
+ this.version = resourceVersion;
+ }
+
+ public String getCreatorUserId() {
+ return creatorUserId;
+ }
+
+ public void setCreatorUserId(String creatorUserId) {
+ this.creatorUserId = creatorUserId;
+ }
+
+ public String getCreatorFullName() {
+ return creatorFullName;
+ }
+
+ public void setCreatorFullName(String creatorFullName) {
+ this.creatorFullName = creatorFullName;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public List<String> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<String> tags) {
+ this.tags = tags;
+ }
+
+ public String getIsHighestVersion() {
+ return isHighestVersion;
+ }
+
+ public void setIsHighestVersion(String isHighestVersion) {
+ this.isHighestVersion = isHighestVersion;
+ }
+
+ public String getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(String creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public String getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(String lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ // public String getCategory() {
+ // return category;
+ // }
+ // public void setCategory(String category) {
+ // this.category = category;
+ // }
+ public String getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public void setLifecycleState(String lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public List<String> getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(List<String> derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public String getVendorName() {
+ return vendorName;
+ }
+
+ public void setVendorName(String vendorName) {
+ this.vendorName = vendorName;
+ }
+
+ public String getVendorRelease() {
+ return vendorRelease;
+ }
+
+ public void setVendorRelease(String vendorRelease) {
+ this.vendorRelease = vendorRelease;
+ }
+
+ public String getContactId() {
+ return contactId;
+ }
+
+ public void setContactId(String contactId) {
+ this.contactId = contactId;
+ }
+
+ public String getAbstractt() {
+ return abstractt;
+ }
+
+ public void setAbstractt(String abstractt) {
+ this.abstractt = abstractt;
+ }
+
+ public String getHighestVersion() {
+ return highestVersion;
+ }
+
+ public void setHighestVersion(String highestVersion) {
+ this.highestVersion = highestVersion;
+ }
+
+ public List<CategoryDefinition> getCategories() {
+ return categories;
+ }
+
+ public void setCategories(List<CategoryDefinition> categories) {
+ this.categories = categories;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceRespJavaObject [uniqueId=" + uniqueId + ", resourceName=" + name + ", resourceVersion="
+ + version + ", creatorUserId=" + creatorUserId + ", creatorFullName=" + creatorFullName
+ + ", lastUpdaterUserId=" + lastUpdaterUserId + ", lastUpdaterFullName=" + lastUpdaterFullName
+ + ", description=" + description + ", icon=" + icon + ", tags=" + tags + ", isHighestVersion="
+ + isHighestVersion + ", creationDate=" + creationDate + ", lastUpdateDate=" + lastUpdateDate
+ + ", lifecycleState=" + lifecycleState + ", derivedFrom=" + derivedFrom + ", vendorName=" + vendorName
+ + ", vendorRelease=" + vendorRelease + ", contactId=" + contactId + ", abstractt=" + abstractt
+ + ", highestVersion=" + highestVersion + ", artifacts=" + artifacts + ", interfaces=" + interfaces
+ + ", uuid=" + uuid + ", cost=" + cost + ", licenseType=" + licenseType + ", resourceType="
+ + resourceType + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceAssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceAssetStructure.java
new file mode 100644
index 0000000000..015d228acf
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceAssetStructure.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public class ServiceAssetStructure extends AssetStructure {
+
+ private String distributionStatus;
+
+ public ServiceAssetStructure() {
+ super();
+ }
+
+ public ServiceAssetStructure(String uuid, String invariantUUID, String name, String version, String toscaModelURL,
+ String category, String lifecycleState, String lastUpdaterUserId) {
+ super(uuid, invariantUUID, name, version, toscaModelURL, category, lifecycleState, lastUpdaterUserId);
+ }
+
+ @Override
+ public String toString() {
+ return "ServiceAssetStructure [distributionStatus=" + distributionStatus + "]";
+ }
+
+ public String getDistributionStatus() {
+ return distributionStatus;
+ }
+
+ public void setDistributionStatus(String distributionStatus) {
+ this.distributionStatus = distributionStatus;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceDetailedAssetStructure.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceDetailedAssetStructure.java
new file mode 100644
index 0000000000..cc283a3169
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceDetailedAssetStructure.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+public class ServiceDetailedAssetStructure extends ServiceAssetStructure {
+
+ String lastUpdaterFullName;
+ List<ResourceInstanceAssetStructure> resources;
+ List<ArtifactAssetStructure> artifacts;
+
+ public ServiceDetailedAssetStructure() {
+ super();
+ }
+
+ public ServiceDetailedAssetStructure(String uuid, String invariantUUID, String name, String version,
+ String toscaModelURL, String category, String lifecycleState, String lastUpdaterUserId) {
+ super(uuid, invariantUUID, name, version, toscaModelURL, category, lifecycleState, lastUpdaterUserId);
+ }
+
+ public ServiceDetailedAssetStructure(String lastUpdaterFullName, List<ResourceInstanceAssetStructure> resources,
+ List<ArtifactAssetStructure> artifacts) {
+ super();
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ this.resources = resources;
+ this.artifacts = artifacts;
+ }
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public List<ResourceInstanceAssetStructure> getResources() {
+ return resources;
+ }
+
+ public void setResources(List<ResourceInstanceAssetStructure> resources) {
+ this.resources = resources;
+ }
+
+ public List<ArtifactAssetStructure> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactAssetStructure> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ @Override
+ public String toString() {
+ return "ServiceDetailedAssetStructure [lastUpdaterFullName=" + lastUpdaterFullName + ", resources=" + resources
+ + ", artifacts=" + artifacts + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceReqDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceReqDetails.java
new file mode 100644
index 0000000000..e33183ca94
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceReqDetails.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.ArrayList;
+
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+
+public class ServiceReqDetails extends ComponentReqDetails {
+
+ public ServiceReqDetails(String serviceName, String category, ArrayList<String> tags, String description,
+ String contactId, String icon) {
+ this.name = serviceName;
+ // this.category = category;
+ this.tags = tags;
+ this.description = description;
+ this.contactId = contactId;
+ this.icon = icon;
+ projectCode = "12345";
+ CategoryDefinition categoryDefinition = new CategoryDefinition();
+ categoryDefinition.setName(category);
+ categories = new ArrayList<>();
+ categories.add(categoryDefinition);
+
+ }
+
+ public ServiceReqDetails(Service service) {
+ this.contactId = service.getContactId();
+ this.categories = service.getCategories();
+ this.creatorUserId = service.getCreatorUserId();
+ this.creatorFullName = service.getCreatorFullName();
+ this.description = service.getDescription();
+ this.icon = service.getIcon();
+ this.name = service.getName();
+ this.projectCode = service.getProjectCode();
+ this.tags = service.getTags();
+ this.uniqueId = service.getUniqueId();
+ this.UUID = service.getUUID();
+ this.version = service.getVersion();
+
+ }
+
+ public ServiceReqDetails() {
+ contactId = "aa1234";
+ projectCode = "12345";
+ }
+
+ public ServiceReqDetails(ServiceReqDetails a, String newServiceName) {
+ a.setName(newServiceName);
+ }
+
+ @Override
+ public String toString() {
+ return "ServiceDetails [name=" + name + ", category=" + getCategory() + ", tags=" + tags + ", description="
+ + description + ", contactId=" + contactId + ", icon=" + icon + "]";
+ }
+
+ public ServiceReqDetails(ServiceReqDetails aService) {
+ this(aService.getName(), aService.getCategory(), (ArrayList<String>) aService.getTags(),
+ aService.getDescription(), aService.getContactId(), aService.getIcon());
+ uniqueId = aService.getUniqueId();
+ version = aService.getVersion();
+ }
+
+ public String getCategory() {
+ if (categories != null && categories.size() >= 1) {
+ return categories.get(0).getName();
+ }
+ return null;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceRespJavaObject.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceRespJavaObject.java
new file mode 100644
index 0000000000..4aeb0ce589
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceRespJavaObject.java
@@ -0,0 +1,267 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.ArrayList;
+
+public class ServiceRespJavaObject {
+
+ String category;
+ String creatorUserId;
+ String creatorFullName;
+ String lastUpdaterUserId;
+ String lastUpdaterFullName;
+ String serviceName;
+ String version;
+ String creationDate;
+ String icon;
+ String name;
+ String description;
+ ArrayList<String> tags;
+ String uniqueId;
+ String lastUpdateDate;
+ String contactId;
+ String vendorName;
+ String vendorRelease;
+ String lifecycleState;
+ String highestVersion;
+ ArrayList<String> artifacts;
+ ArrayList<String> ResourceInstances;
+ ArrayList<String> ResourceInstancesRelations;
+
+ public ServiceRespJavaObject() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public ServiceRespJavaObject(String category, String creatorUserId, String creatorFullName,
+ String lastUpdaterUserId, String lastUpdaterFullName, String serviceName, String version,
+ String creationDate, String icon, String name, String description, ArrayList<String> tags, String uniqueId,
+ String lastUpdateDate, String contactId, String vendorName, String vendorRelease, String lifecycleState,
+ String highestVersion, ArrayList<String> artifacts, ArrayList<String> resourceInstances,
+ ArrayList<String> resourceInstancesRelations) {
+ super();
+ this.category = category;
+ this.creatorUserId = creatorUserId;
+ this.creatorFullName = creatorFullName;
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ this.serviceName = serviceName;
+ this.version = version;
+ this.creationDate = creationDate;
+ this.icon = icon;
+ this.name = name;
+ this.description = description;
+ this.tags = tags;
+ this.uniqueId = uniqueId;
+ this.lastUpdateDate = lastUpdateDate;
+ this.contactId = contactId;
+ this.vendorName = vendorName;
+ this.vendorRelease = vendorRelease;
+ this.lifecycleState = lifecycleState;
+ this.highestVersion = highestVersion;
+ this.artifacts = artifacts;
+ ResourceInstances = resourceInstances;
+ ResourceInstancesRelations = resourceInstancesRelations;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getCreatorUserId() {
+ return creatorUserId;
+ }
+
+ public void setCreatorUserId(String creatorUserId) {
+ this.creatorUserId = creatorUserId;
+ }
+
+ public String getCreatorFullName() {
+ return creatorFullName;
+ }
+
+ public void setCreatorFullName(String creatorFullName) {
+ this.creatorFullName = creatorFullName;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(String creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ArrayList<String> getTags() {
+ return tags;
+ }
+
+ public void setTags(ArrayList<String> tags) {
+ this.tags = tags;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(String lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public String getContactId() {
+ return contactId;
+ }
+
+ public void setContactId(String contactId) {
+ this.contactId = contactId;
+ }
+
+ public String getVendorName() {
+ return vendorName;
+ }
+
+ public void setVendorName(String vendorName) {
+ this.vendorName = vendorName;
+ }
+
+ public String getVendorRelease() {
+ return vendorRelease;
+ }
+
+ public void setVendorRelease(String vendorRelease) {
+ this.vendorRelease = vendorRelease;
+ }
+
+ public String getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public void setLifecycleState(String lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public String getHighestVersion() {
+ return highestVersion;
+ }
+
+ public void setHighestVersion(String highest) {
+ this.highestVersion = highest;
+ }
+
+ public ArrayList<String> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(ArrayList<String> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ public ArrayList<String> getResourceInstances() {
+ return ResourceInstances;
+ }
+
+ public void setResourceInstances(ArrayList<String> resourceInstances) {
+ ResourceInstances = resourceInstances;
+ }
+
+ public ArrayList<String> getResourceInstancesRelations() {
+ return ResourceInstancesRelations;
+ }
+
+ public void setResourceInstancesRelations(ArrayList<String> resourceInstancesRelations) {
+ ResourceInstancesRelations = resourceInstancesRelations;
+ }
+
+ @Override
+ public String toString() {
+ return "ServiceRespJavaObject [category=" + category + ", creatorUserId=" + creatorUserId + ", creatorFullName="
+ + creatorFullName + ", lastUpdaterUserId=" + lastUpdaterUserId + ", lastUpdaterFullName="
+ + lastUpdaterFullName + ", serviceName=" + serviceName + ", version=" + version + ", creationDate="
+ + creationDate + ", icon=" + icon + ", name=" + name + ", description=" + description + ", tags=" + tags
+ + ", uniqueId=" + uniqueId + ", lastUpdateDate=" + lastUpdateDate + ", contactId=" + contactId
+ + ", vendorName=" + vendorName + ", vendorRelease=" + vendorRelease + ", lifecycleState="
+ + lifecycleState + ", lifecycleState=" + lifecycleState + ", artifacts=" + artifacts
+ + ", ResourceInstances=" + ResourceInstances + ", ResourceInstancesRelations="
+ + ResourceInstancesRelations + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypeHeatMetaDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypeHeatMetaDefinition.java
new file mode 100644
index 0000000000..d0f029242f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypeHeatMetaDefinition.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import java.util.List;
+
+public class TypeHeatMetaDefinition {
+
+ String typeName;
+
+ List<GroupHeatMetaDefinition> groupHeatMetaDefinition;
+
+ public TypeHeatMetaDefinition() {
+ super();
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ public void setTypeName(String typeName) {
+ this.typeName = typeName;
+ }
+
+ public List<GroupHeatMetaDefinition> getGroupHeatMetaDefinition() {
+ return groupHeatMetaDefinition;
+ }
+
+ public void setGroupHeatMetaDefinition(List<GroupHeatMetaDefinition> groupHeatMetaDefinition) {
+ this.groupHeatMetaDefinition = groupHeatMetaDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeHeatMetaDefinition [typeName=" + typeName + ", groupHeatMetaDefinition=" + groupHeatMetaDefinition
+ + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ArtifactTypeEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ArtifactTypeEnum.java
new file mode 100644
index 0000000000..1e6b06a6c8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ArtifactTypeEnum.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Enum That Represents possible Artifacts Types.
+ *
+ */
+public enum ArtifactTypeEnum {
+ CHEF("CHEF"), PUPPET("PUPPET"), YANG("YANG"), SHELL_SCRIPT("SHELL_SCRIPT"), ICON("ICON"), UNKNOWN("UNKNOWN"), HEAT("HEAT"), DG_XML("DG_XML"), MURANO_PKG("MURANO_PKG"),
+ HEAT_ENV("HEAT_ENV"), YANG_XML("YANG_XML"), HEAT_VOL("HEAT_VOL"), HEAT_NET("HEAT_NET"), OTHER("OTHER"), WORKFLOW("WORKFLOW"), NETWORK_CALL_FLOW("NETWORK_CALL_FLOW"),
+ TOSCA_TEMPLATE("TOSCA_TEMPLATE"), TOSCA_CSAR("TOSCA_CSAR"), VNF_CATALOG("VNF_CATALOG"), VF_LICENSE("VF_LICENSE"), VENDOR_LICENSE("VENDOR_LICENSE"),
+ MODEL_INVENTORY_PROFILE("MODEL_INVENTORY_PROFILE"), MODEL_QUERY_SPEC("MODEL_QUERY_SPEC"), APPC_CONFIG("APPC_CONFIG"), HEAT_NESTED("HEAT_NESTED"),
+ HEAT_ARTIFACT("HEAT_ARTIFACT"), VF_MODULES_METADATA("VF_MODULES_METADATA"),
+ // DCAE Artifacts
+ DCAE_TOSCA("DCAE_TOSCA"), DCAE_JSON("DCAE_JSON"), DCAE_POLICY("DCAE_POLICY"), DCAE_DOC("DCAE_DOC"), DCAE_EVENT("DCAE_EVENT"), DCAE_INVENTORY_TOSCA("DCAE_INVENTORY_TOSCA"),
+ DCAE_INVENTORY_JSON("DCAE_INVENTORY_JSON"), DCAE_INVENTORY_POLICY("DCAE_INVENTORY_POLICY"), DCAE_INVENTORY_DOC("DCAE_INVENTORY_DOC"),
+ DCAE_INVENTORY_BLUEPRINT("DCAE_INVENTORY_BLUEPRINT"), DCAE_INVENTORY_EVENT("DCAE_INVENTORY_EVENT"),
+ // AAI Artifacts
+ AAI_SERVICE_MODEL("AAI_SERVICE_MODEL"), AAI_VF_MODEL("AAI_VF_MODEL"), AAI_VF_MODULE_MODEL("AAI_VF_MODULE_MODEL"), AAI_VF_INSTANCE_MODEL("AAI_VF_INSTANCE_MODEL")
+ ;
+
+ ArtifactTypeEnum(String type) {
+ this.type = type;
+ }
+
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public static ArtifactTypeEnum findType(final String type) {
+ for (ArtifactTypeEnum ate : ArtifactTypeEnum.values()) {
+ // According to Pavel/Ella
+ if (ate.getType().equalsIgnoreCase(type)) {
+ return ate;
+ }
+ }
+ return null;
+ }
+
+ public static List<String> getAllTypes() {
+ List<String> types = new ArrayList<String>();
+ for (ArtifactTypeEnum ate : ArtifactTypeEnum.values()) {
+ types.add(ate.getType());
+ }
+ return types;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AssocType.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AssocType.java
new file mode 100644
index 0000000000..b0da1f987d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AssocType.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum AssocType {
+
+ BINDABLE("tosca.capabilities.network.Bindable"),
+ HOSTEDON("tosca.relationships.HostedOn"),
+ LINKABLE("tosca.capabilities.network.Linkable"),
+ CONTAINER("tosca.capabilities.Container"),
+ NODE("tosca.capabilities.Node");
+
+ private String assocType;
+
+ private AssocType(String assocType) {
+ this.assocType = assocType;
+
+ }
+
+ public String getAssocType() {
+ return assocType;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditEnum.java
new file mode 100644
index 0000000000..a73cff9373
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditEnum.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum AuditEnum {
+
+ ACTION("ACTION"),
+ RESOURCE_NAME("RESOURCE_NAME"),
+ RESOURCE_TYPE("RESOURCE_TYPE"),
+ PREV_VERSION("PREV_VERSION"),
+ CURR_VERSION("CURR_VERSION"),
+ MODIFIER("MODIFIER"),
+ PREV_STATE("PREV_STATE"),
+ CURR_STATE("CURR_STATE"),
+ STATUS("STATUS-Type"),
+ DESC("DESC"),
+ URL("URL"),
+ USER("USER"),
+ AUTH_STATUS("AUTH_STATUS"),
+ REALM("REALM");
+
+ String value;
+
+ private AuditEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+
+ return value.toLowerCase();
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditJsonKeysEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditJsonKeysEnum.java
new file mode 100644
index 0000000000..c1b9fd4747
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/AuditJsonKeysEnum.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum AuditJsonKeysEnum {
+
+ ACTION("ACTION"), RESOURCE_NAME("RESOURCE_NAME"), RESOURCE_TYPE("RESOURCE_TYPE"), PREV_VERSION("PREV_VERSION"), CURR_VERSION("CURR_VERSION"), PREV_STATE("PREV_STATE"), CURR_STATE("CURR_STATE"),
+ DPREV_STATUS("DPREV_STATUS"), DCURR_STATUS("DCURR_STATUS"), STATUS("STATUS"), DESCRIPTION("DESCRIPTION"), ARTIFACT_DATA("ARTIFACT_DATA"), CONSUMER_ID("CONSUMER_ID"), RESOURCE_URL("RESOURCE_URL"),
+ COMMENT("COMMENT"), DID("DID"), TOPIC_NAME("TOPIC_NAME"), TOSCA_NODE_TYPE("TOSCA_NODE_TYPE"), CURR_ARTIFACT_UUID("CURR_ARTIFACT_UUID"), PREV_ARTIFACT_UUID("PREV_ARTIFACT_UUID"), DETAILS("DETAILS"),
+ MODIFIER("MODIFIER"), SERVICE_INSTANCE_ID("SERVICE_INSTANCE_ID");
+
+ private String auditJsonKeyName;
+
+ private AuditJsonKeysEnum(String auditJsonKeyName) {
+ this.auditJsonKeyName = auditJsonKeyName;
+ }
+
+ public String getAuditJsonKeyName() {
+ return auditJsonKeyName.toLowerCase();
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ComponentType.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ComponentType.java
new file mode 100644
index 0000000000..3d242e8c52
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ComponentType.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum ComponentType {
+
+ RESOURCE("Resource"), SERVICE("Service"), ARTIFACT("Artifact");
+
+ String value;
+
+ private ComponentType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+
+ return value;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ErrorInfo.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ErrorInfo.java
new file mode 100644
index 0000000000..c2a28a1edf
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ErrorInfo.java
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public class ErrorInfo {
+
+ private Integer code;
+ private String message;
+ private String messageId;
+
+ public ErrorInfo() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public ErrorInfo(Integer code, String message, String messageId) {
+ super();
+ this.code = code;
+ this.message = message;
+ this.messageId = messageId;
+ }
+
+ public ErrorInfo(Integer code, String message) {
+ super();
+ this.code = code;
+ this.message = message;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public void setCode(Integer code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getMessageAndReplaceVariables(Object... variables) {
+ String formatReadyString = message.replaceAll("%[\\d]+", "%s");
+ formatReadyString = String.format(formatReadyString, variables);
+ return formatReadyString;
+ }
+
+ public String getAuditDesc(Object... variables) {
+ String messageAndReplaceVariables = getMessageAndReplaceVariables(variables);
+ String res;
+ if (messageId != null) {
+ res = messageId + ": " + messageAndReplaceVariables;
+ } else {
+ res = messageAndReplaceVariables;
+ }
+ return res;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessageId() {
+ return messageId;
+ }
+
+ public void setMessageId(String messageId) {
+ this.messageId = messageId;
+ }
+
+ @Override
+ public String toString() {
+ return "ErrorInfo [code=" + code + ", message=" + message + ", messageId=" + messageId + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/EsIndexTypeIdToDelete.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/EsIndexTypeIdToDelete.java
new file mode 100644
index 0000000000..6bfbd8af7d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/EsIndexTypeIdToDelete.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public class EsIndexTypeIdToDelete {
+
+ String index;
+ String type;
+ String id;
+
+ public EsIndexTypeIdToDelete() {
+ super();
+ }
+
+ public EsIndexTypeIdToDelete(String index, String type, String id) {
+ super();
+ this.index = index;
+ this.type = type;
+ this.id = id;
+ }
+
+ public String getIndex() {
+ return index;
+ }
+
+ public void setIndex(String index) {
+ this.index = index;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ExceptionEnumType.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ExceptionEnumType.java
new file mode 100644
index 0000000000..59d1dec453
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ExceptionEnumType.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum ExceptionEnumType {
+
+ SERVICE_EXCEPTION("serviceException"), POLICY_EXCPTION("policyException");
+
+ String value;
+
+ private ExceptionEnumType(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ImportTestTypesEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ImportTestTypesEnum.java
new file mode 100644
index 0000000000..21901e4635
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ImportTestTypesEnum.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+
+public enum ImportTestTypesEnum {
+
+ MISSING_CONTACT("tosca.nodes.missing_contact", "missing_contact", ActionStatus.COMPONENT_MISSING_CONTACT, Arrays.asList("Resource"), true),
+ MISSING_RESOURCE_NAME("tosca.nodes.missing_resource_name", "missing_resource_name", ActionStatus.MISSING_COMPONENT_NAME, Arrays.asList("Resource"), true),
+ MISSING_DESC("tosca.nodes.missing_desc", "missing_desc", ActionStatus.COMPONENT_MISSING_DESCRIPTION, Arrays.asList("Resource"), true),
+ MISSING_ICON("tosca.nodes.missing_icon", "missing_icon", ActionStatus.COMPONENT_MISSING_ICON, Arrays.asList("Resource"), true),
+ MISSING_TAGS("tosca.nodes.missing_tags", "missing_tags", ActionStatus.COMPONENT_MISSING_TAGS, null, true),
+ MISSING_CATEGORY("tosca.nodes.missing_category", "missing_category", ActionStatus.COMPONENT_MISSING_CATEGORY, Arrays.asList("Resource"), true),
+ // MISSING_PAYLOADNAME("tosca.nodes.missing_payloadName",
+ // "missing_payloadName", ActionStatus.INVALID_TOSCA_FILE_EXTENSION, null,
+ // true),
+
+ EMPTY_RESOURCE_NAME("tosca.nodes.empty_resource_name", "missing_resource_name"/* "empty_resource_name" */, ActionStatus.MISSING_COMPONENT_NAME, Arrays.asList("Resource"), false),
+ EMPTY_CONTACT("tosca.nodes.empty_contact", "missing_contact"/* "empty_contact" */, ActionStatus.COMPONENT_MISSING_CONTACT, Arrays.asList("Resource"), false),
+ EMPTY_CATEGORY("tosca.nodes.empty_category", "missing_category"/* "empty_category" */, ActionStatus.COMPONENT_MISSING_CATEGORY, Arrays.asList("Resource"), false),
+ EMPTY_DESC("tosca.nodes.empty_desc", "missing_desc"/* "empty_desc" */, ActionStatus.COMPONENT_MISSING_DESCRIPTION, Arrays.asList("Resource"), false),
+ EMPTY_ICON("tosca.nodes.empty_icon", "missing_icon"/* "empty_icon" */, ActionStatus.COMPONENT_MISSING_ICON, Arrays.asList("Resource"), false),
+ EMPTY_PAYLOADNAME("tosca.nodes.empty_payloadName", "missing_payloadName"/* "empty_payloadName" */, ActionStatus.INVALID_TOSCA_FILE_EXTENSION, null, false),
+ EMPTY_TAG("tosca.nodes.empty_tag", "empty_tag", ActionStatus.INVALID_FIELD_FORMAT, Arrays.asList("Resource", "tag"), false),
+ VALIDATE_PROPORTIES_1("tosca.nodes.validateProporties_typeBoolean_valueInit", "validateProporties_typeBoolean_valueInit", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "boolean", "123456"), false),
+ VALIDATE_PROPORTIES_2("tosca.nodes.validateProporties_typeBoolean_valueString", "validateProporties_typeBoolean_valueString", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "boolean", "abcd"), false),
+ VALIDATE_PROPORTIES_3("tosca.nodes.validateProporties_typeFloat_valueBoolean", "validateProporties_typeFloat_valueBoolean", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "float", "true"), false),
+ VALIDATE_PROPORTIES_4("tosca.nodes.validateProporties_typeFloat_valueString", "validateProporties_typeFloat_valueString", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "float", "abcd"), false),
+ VALIDATE_PROPORTIES_5("tosca.nodes.validateProporties_typeInit_valueBoolean", "validateProporties_typeInit_valueBoolean", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "integer", "true"), false),
+ VALIDATE_PROPORTIES_6("tosca.nodes.validateProporties_typeInit_valueFloat", "validateProporties_typeInit_valueFloat", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "integer", "0.123"), false),
+ VALIDATE_PROPORTIES_7("tosca.nodes.validateProporties_typeInit_valueString", "validateProporties_typeInit_valueString", ActionStatus.INVALID_DEFAULT_VALUE, Arrays.asList("validation_test", "integer", "abcd"), false);
+ // VALIDATE_PROPORTIES_8("tosca.nodes.validateProporties_happyScenarios","validateProporties_happyScenarios", ActionStatus.OK, null, false);
+
+ private String normativeName;
+ private String folderName;
+ private ActionStatus actionStatus;
+ private Boolean validateAudit;
+ private List<String> errorParams;
+ private Boolean validateYaml;
+
+ // private enum ActionStatus;
+
+ private ImportTestTypesEnum(String resourceName, String folderName, ActionStatus actionStatus,
+ List<String> errorParams, Boolean validateAudit) {
+ this.normativeName = resourceName;
+ this.folderName = folderName;
+ this.actionStatus = actionStatus;
+ this.errorParams = errorParams;
+ this.validateAudit = validateAudit;
+
+ }
+
+ public String getNormativeName() {
+ return normativeName;
+ }
+
+ public String getFolderName() {
+ return folderName;
+ }
+
+ public ActionStatus getActionStatus() {
+ return actionStatus;
+ }
+
+ public Boolean getvalidateAudit() {
+ return validateAudit;
+ }
+
+ public List<String> getErrorParams() {
+ return errorParams;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/LifeCycleStatesEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/LifeCycleStatesEnum.java
new file mode 100644
index 0000000000..7c330a3d10
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/LifeCycleStatesEnum.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum LifeCycleStatesEnum {
+
+ CHECKOUT("checkout", "NOT_CERTIFIED_CHECKOUT"),
+ CHECKIN("checkin", "NOT_CERTIFIED_CHECKIN"),
+ CERTIFICATIONREQUEST("certificationRequest", "READY_FOR_CERTIFICATION"),
+ UNDOCHECKOUT("undoCheckout", ""),
+ CANCELCERTIFICATION("cancelCertification", ""),
+ STARTCERTIFICATION("startCertification", "CERTIFICATION_IN_PROGRESS"),
+ FAILCERTIFICATION("failCertification", ""),
+ CERTIFY("certify", "CERTIFIED");
+
+ private String state;
+ private String componentState;
+
+ private LifeCycleStatesEnum(String state, String componentState) {
+ this.state = state;
+ this.componentState = componentState;
+
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public String getComponentState() {
+ return componentState;
+ }
+
+ public static LifeCycleStatesEnum findByCompState(String compState) {
+
+ for (LifeCycleStatesEnum lifeCycleStatesEnum : LifeCycleStatesEnum.values()) {
+ if (lifeCycleStatesEnum.getComponentState().equals(compState)) {
+ return lifeCycleStatesEnum;
+ }
+ }
+
+ return null;
+
+ }
+
+ public static LifeCycleStatesEnum findByState(String state) {
+
+ for (LifeCycleStatesEnum lifeCycleStatesEnum : LifeCycleStatesEnum.values()) {
+ if (lifeCycleStatesEnum.name().equals(state)) {
+ return lifeCycleStatesEnum;
+ }
+ }
+
+ return null;
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryResourceArtifactTypeEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryResourceArtifactTypeEnum.java
new file mode 100644
index 0000000000..7769d19802
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryResourceArtifactTypeEnum.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+public enum MandatoryResourceArtifactTypeEnum {
+
+ TEST_SCRIPTS(null, "testscripts"),
+ FEATURES(null, "features"),
+ CAPACITY(null, "capacity"),
+ VENDOR_TEST_RESULT(null,"vendortestresult"),
+ CLOUD_QUESTIONNAIRE(null, "cloudquestionnaire");
+
+ String artifactName;
+ String logicalName;
+
+ private MandatoryResourceArtifactTypeEnum(String artifactName, String logicalName) {
+ this.artifactName = artifactName;
+ this.logicalName = logicalName;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public String getLogicalName() {
+ return logicalName;
+ }
+
+ public ArtifactGroupTypeEnum getGroupType() {
+ return ArtifactGroupTypeEnum.INFORMATIONAL;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryServiceArtifactTypeEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryServiceArtifactTypeEnum.java
new file mode 100644
index 0000000000..b73d5a8fee
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/MandatoryServiceArtifactTypeEnum.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum MandatoryServiceArtifactTypeEnum {
+
+ MESSAGE_FLOWS(null, "messageflows".toLowerCase(), "Message Flows"),
+ INSTANT_FLOWS(null, "instantiationflows".toLowerCase(), "Management Flows"),
+ SERVICE_ART_PLAN(null, "serviceartifactplan".toLowerCase(), "Service Artifact Plan"),
+ SUM_OF_ELEMENTS(null, "summaryofimpactstoecompelements".toLowerCase(), "Summary of impacts to ECOMP elements,OSSs, BSSs"),
+ CONTROL_LOOP_FUN(null, "controlloopfunctions".toLowerCase(), "Control Loop Functions"),
+ DIMENSIONNING_INFO(null, "dimensioninginfo".toLowerCase(), "Dimensioning Info"),
+ AFFINITY_RULES(null, "affinityrules".toLowerCase(), "Affinity Rules"),
+ OPERATIONAL_POLICIES(null, "operationalpolicies".toLowerCase(), "Operational Policies"),
+ SERVICE_SPECIFIC_POLICIES(null, "servicespecificpolicies".toLowerCase(), "Service-specific Policies"),
+ ENGINEERING_RULES(null, "engineeringrules".toLowerCase(), "Engineering Rules (ERD)"),
+ DISTRIB_INSTRUCTIONS(null, "distributioninstructions".toLowerCase(), "Distribution Instructions"),
+ DEPLOYMENT_VOTING_REC(null, "deploymentvotingrecord".toLowerCase(), "Deployment Voting Record"),
+ CERTIFICATION_TEST_RESULT(null, "certificationtestresults".toLowerCase(), "TD Certification Test Results");
+ // SERVICE_QUESTIONNAIRE(null, "serviceQuestionnaire".toLowerCase());
+
+ String artifactName;
+ String logicalName;
+ String artifactDisplayName;
+
+ private MandatoryServiceArtifactTypeEnum(String artifactName, String logicalName, String artifactDisplayName) {
+ this.artifactName = artifactName;
+ this.logicalName = logicalName;
+ this.artifactDisplayName = artifactDisplayName;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public String getLogicalName() {
+ return logicalName;
+ }
+
+ public String getArtifactDisplayName() {
+ return artifactDisplayName;
+ }
+
+ public void setArtifactDisplayName(String artifactDisplayName) {
+ this.artifactDisplayName = artifactDisplayName;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/NormativeTypesEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/NormativeTypesEnum.java
new file mode 100644
index 0000000000..f1cfcf0c98
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/NormativeTypesEnum.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum NormativeTypesEnum {
+ ROOT("tosca.nodes.Root", "root"),
+ COMPUTE("tosca.nodes.Compute", "compute"),
+ BLOCK_STORAGE("tosca.nodes.BlockStorage", "blockStorage"),
+ CONTAINER_APPLICATION("tosca.nodes.Container.Application", "containerApplication"),
+ CONTAINER_RUNTIME("tosca.nodes.Container.Runtime", "containerRuntime"),
+ DATABASE("tosca.nodes.Database", "database"),
+ DBMS("tosca.nodes.DBMS", "DBMS"),
+ LOAD_BALANCER("tosca.nodes.LoadBalancer", "loadBalancer"),
+ OBJECT_STORAGE("tosca.nodes.ObjectStorage", "objectStorage"),
+ NETWORK("tosca.nodes.network.Network", "network"),
+ PORT("tosca.nodes.network.Port", "port"),
+ SOFTWARE_COMPONENT("tosca.nodes.SoftwareComponent", "softwareComponent"),
+ WEB_APPLICATION("tosca.nodes.webapplication", "webApplication"),
+ WEB_SERVER("tosca.nodes.WebServer", "webServer");
+
+ public String normativeName;
+ private String folderName;
+
+ private NormativeTypesEnum(String resourceName, String folderName) {
+ this.normativeName = resourceName;
+ this.folderName = folderName;
+ }
+
+ public String getNormativeName() {
+ return normativeName;
+ }
+
+ public String getFolderName() {
+ return folderName;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/PropertyTypeEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/PropertyTypeEnum.java
new file mode 100644
index 0000000000..5dac326c70
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/PropertyTypeEnum.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+
+public enum PropertyTypeEnum {
+ INTEGER("defaultIntegerPropName1", "integer", "125", "default integer type property description", null),
+ STRING("defaultStringPropName1", "string", "string", "default string type property description", null),
+ BOOLEAN("defaultBooleanPropName1", "boolean", "true", "default boolean type property description", null),
+ STRING_LIST("defaultStringListPropName", "list", "[a,b]", "outer description", getDefaultStringSchema(ToscaPropertyType.STRING.getType())),
+ INTEGER_LIST("defaultIntegerListPropName", "list", "[1,2]", "outer description", getDefaultStringSchema(ToscaPropertyType.INTEGER.getType())),
+ BOOLEAN_LIST("defaultBooleanListPropName", "list", "[true,false]", "outer description", getDefaultStringSchema(ToscaPropertyType.BOOLEAN.getType())),
+ FLOAT_LIST("defaultFloatMapPropName", "list", "[1.0,2.0]", "outer description", getDefaultStringSchema(ToscaPropertyType.FLOAT.getType())),
+ STRING_MAP("defaultStringMapPropName", "map", "{\"key1\":val1 , \"key2\":val2}", "outer description", getDefaultStringSchema(ToscaPropertyType.STRING.getType())),
+ INTEGER_MAP("defaultIntegerMapPropName", "map", "{\"key1\":123 , \"key2\":-456}", "outer description", getDefaultStringSchema(ToscaPropertyType.INTEGER.getType())),
+ BOOLEAN_MAP("defaultBooleanMapPropName", "map", "{\"key1\":true , \"key2\":false}", "outer description", getDefaultStringSchema(ToscaPropertyType.BOOLEAN.getType())),
+ FLOAT_MAP("defaultFloatMapPropName", "map", "{\"key1\":0.2123 , \"key2\":43.545f}", "outer description", getDefaultStringSchema(ToscaPropertyType.FLOAT.getType()));
+
+ private String name;
+ private String type;
+ private String value;
+ private String description;
+ private SchemaDefinition schemaDefinition;
+
+ private PropertyTypeEnum(String name, String type, String value, String description,
+ SchemaDefinition schemaDefinition) {
+ this.name = name;
+ this.type = type;
+ this.value = value;
+ this.description = description;
+ this.schemaDefinition = schemaDefinition;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public SchemaDefinition getSchemaDefinition() {
+ return schemaDefinition;
+ }
+
+ public void setSchemaDefinition(SchemaDefinition schemaDefinition) {
+ this.schemaDefinition = schemaDefinition;
+ }
+
+ private static SchemaDefinition getDefaultStringSchema(String innerType) {
+ SchemaDefinition schema = new SchemaDefinition();
+ String description = "inner description";
+ PropertyDefinition property = new PropertyDefinition();
+ property.setType(innerType);
+ property.setDescription(description);
+ schema.setProperty(property);
+ return schema;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ResourceCategoryEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ResourceCategoryEnum.java
new file mode 100644
index 0000000000..3ece77b8ec
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ResourceCategoryEnum.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum ResourceCategoryEnum {
+
+ NETWORK_L2_3_ROUTERS("Network L2-3", "Router"), NETWORK_L2_3_GETEWAY("Network L2-3","Gateway"), NETWORK_L2_3_WAN_CONNECTORS("Network L2-3", "WAN Connectors"), NETWORK_L2_3_LAN_CONNECTORS("Network L2-3", "LAN Connectors"),
+ NETWORK_L2_3_INFRASTRUCTURE("Network L2-3", "Infrastructure"), NETWORK_L4("Network L4+", "Common Network Resources"), APPLICATION_L4_BORDER("Application L4+", "Border Element"),
+ APPLICATION_L4_APP_SERVER("Application L4+", "Application Server"), APPLICATION_L4_WEB_SERVERS("Application L4+", "Web Server"), APPLICATION_L4_CALL_CONTROL("Application L4+","Call Control"),
+ APPLICATION_L4_MEDIA_SERVER("Application L4+", "Media Servers"), APPLICATION_L4_LOAD_BALANCER("Application L4+", "Load Balancer"), APPLICATION_L4_DATABASE("Application L4+","Database"),
+ APPLICATION_L4_FIREWALL("Application L4+", "Firewall"), GENERIC_INFRASTRUCTURE("Generic", "Infrastructure"), GENERIC_ABSTRACT("Generic", "Abstract"), GENERIC_NETWORK_ELEMENTS("Generic","Network Elements"),
+ GENERIC_DATABASE("Generic", "Database"), NETWORK_CONNECTIVITY_CON_POINT("Network Connectivity", "Connection Points"), NETWORK_CONNECTIVITY_VIRTUAL_LINK("Network Connectivity","Virtual Links");
+
+ private String category;
+ private String subCategory;
+
+ ResourceCategoryEnum(String category, String subCategory) {
+ this.category = category;
+ this.subCategory = subCategory;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getSubCategory() {
+ return subCategory;
+ }
+
+ public void setSubCategory(String subCategory) {
+ this.subCategory = subCategory;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/RespJsonKeysEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/RespJsonKeysEnum.java
new file mode 100644
index 0000000000..4350cba105
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/RespJsonKeysEnum.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum RespJsonKeysEnum {
+
+ IS_ABSTRACT("abstract"),
+ UNIQUE_ID("uniqueId"),
+ RESOURCE_NAME("name"),
+ RESOURCE_VERSION("version"),
+ TAGS("tags"),
+ LIFE_CYCLE_STATE("lifecycleState"),
+ DERIVED_FROM("derivedFrom"),
+ RESOURCE_DESC("description"),
+ VENDOR_NAME("vendorName"),
+ VENDOR_RELEASE("vendorRelease"),
+ CONTACT_ID("contactId"),
+ ICON("icon"),
+ HIGHEST_VERSION("highestVersion"),
+ CREATOR_USER_ID("creatorUserId"),
+ CREATOR_FULL_NAME("creatorFullName"),
+ LAST_UPDATER_ATT_UID("lastUpdaterUserId"),
+ LAST_UPDATER_FULL_NAME("lastUpdaterFullName"),
+ ARTIFACTS("artifacts"),
+ DESCRIPTION("description"),
+ UUID("uuid"),
+ COST("cost"),
+ LICENSE_TYPE("licenseType"),
+ RESOURCE_TYPE("resourceType"),
+ CATEGORIES("categories");
+
+ private String respJsonKeyName;
+
+ private RespJsonKeysEnum(String respJsonKeyName) {
+ this.respJsonKeyName = respJsonKeyName;
+ }
+
+ public String getRespJsonKeyName() {
+ return respJsonKeyName;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceApiArtifactEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceApiArtifactEnum.java
new file mode 100644
index 0000000000..675e1d7502
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceApiArtifactEnum.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum ServiceApiArtifactEnum {
+ CONFIGURATION("Configuration".toLowerCase()),
+ INSTANTIATION("Instantiation".toLowerCase()),
+ MONITORING("Monitoring".toLowerCase()),
+ REPORTING("Reporting".toLowerCase()),
+ LOGGING("Logging".toLowerCase()),
+ TESTING("Testing".toLowerCase());
+
+ String logicalName;
+
+ private ServiceApiArtifactEnum(String logicalName) {
+ this.logicalName = logicalName;
+ }
+
+ public String getLogicalName() {
+ return logicalName;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceCategoriesEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceCategoriesEnum.java
new file mode 100644
index 0000000000..61d4e487e2
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ServiceCategoriesEnum.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum ServiceCategoriesEnum {
+
+ VOIP("VoIP Call Control"), MOBILITY("Mobility"), NETWORK_L4("Network L4+"), NETWORK_L3("Network L1-3");
+ String value;
+
+ private ServiceCategoriesEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+
+ return value;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ToscaKeysEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ToscaKeysEnum.java
new file mode 100644
index 0000000000..f8479e912c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/ToscaKeysEnum.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum ToscaKeysEnum {
+
+ TOSCA_DEFINITION_VERSION("tosca_definitions_version"),
+ METADATA("metadata"), IMPORTS("imports"),
+ NODE_TYPES("node_types"),
+ TOPOLOGY_TEMPLATE("topology_template");
+
+ private String toscaKey;
+
+ public String getToscaKey() {
+ return toscaKey;
+ }
+
+ private ToscaKeysEnum(String toscaKey) {
+ this.toscaKey = toscaKey;
+ }
+
+ public static ToscaKeysEnum findToscaKey(final String toscaKey) {
+ for (ToscaKeysEnum toscaKeyEnum : ToscaKeysEnum.values()) {
+ if (toscaKeyEnum.getToscaKey().equalsIgnoreCase(toscaKey)) {
+ return toscaKeyEnum;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/UserRoleEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/UserRoleEnum.java
new file mode 100644
index 0000000000..399779fefe
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/enums/UserRoleEnum.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.enums;
+
+public enum UserRoleEnum {
+
+ ADMIN("jh0003","Jimmy", "Hendrix"), DESIGNER("cs0008","Carlos", "Santana"), DESIGNER2("me0009","Melissa", "Etheridge"), TESTER("jm0007","Joni", "Mitchell"),ADMIN4("km2000","Kot", "May"), GOVERNOR("gv0001","David", "Shadmi"),
+ OPS("op0001","Steve", "Regev"),PRODUCT_STRATEGIST1("ps0001","Eden", "Rozin"),PRODUCT_STRATEGIST2("ps0002","Ella", "Kvetny"),PRODUCT_STRATEGIST3("ps0003","Geva", "Alon"), PRODUCT_MANAGER1("pm0001","Teddy", "Isashar"),
+ PRODUCT_MANAGER2("pm0002","Sarah", "Bettens");
+
+ private String userId;
+ private String firstName;
+ private String lastName;
+ private String userName;
+ private UserRoleEnum(String userId, String userName) {
+ this.userId = userId;
+ this.userName = userName;
+ }
+
+ private UserRoleEnum(String userId, String firstName, String lastName) {
+ this.userId = userId;
+ this.firstName =firstName;
+ this.lastName = lastName;
+ this.userName = firstName+" " + lastName;
+ }
+ public String getUserId() {
+ return userId;
+ }
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+ public String getUserName() {
+ return userName;
+ }
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+
+}
+
+
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedArtifactAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedArtifactAudit.java
new file mode 100644
index 0000000000..344f353348
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedArtifactAudit.java
@@ -0,0 +1,166 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedArtifactAudit {
+
+ private String action;
+ private String resourceName;
+ private String resourceType;
+ private String prevVersion;
+ private String currVersion;
+ private String modifier;
+ private String prevState;
+ private String currState;
+ private String prevArtifactUuid;
+ private String currArtifactUuid;
+ private String artifactData;
+ private String status;
+ private String desc;
+
+ public ExpectedArtifactAudit(String action, String resourceName, String resourceType, String prevVersion,
+ String currVersion, String modifier, String prevState, String currState, String prevArtifactUuid,
+ String currArtifactUuid, String artifactData, String status, String desc) {
+ super();
+ this.action = action;
+ this.resourceName = resourceName;
+ this.resourceType = resourceType;
+ this.prevVersion = prevVersion;
+ this.currVersion = currVersion;
+ this.modifier = modifier;
+ this.prevState = prevState;
+ this.currState = currState;
+ this.prevArtifactUuid = prevArtifactUuid;
+ this.currArtifactUuid = currArtifactUuid;
+ this.artifactData = artifactData;
+ this.status = status;
+ this.desc = desc;
+ }
+
+ public ExpectedArtifactAudit() {
+ super();
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getPrevVersion() {
+ return prevVersion;
+ }
+
+ public void setPrevVersion(String prevVersion) {
+ this.prevVersion = prevVersion;
+ }
+
+ public String getCurrVersion() {
+ return currVersion;
+ }
+
+ public void setCurrVersion(String currVersion) {
+ this.currVersion = currVersion;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getPrevState() {
+ return prevState;
+ }
+
+ public void setPrevState(String prevState) {
+ this.prevState = prevState;
+ }
+
+ public String getCurrState() {
+ return currState;
+ }
+
+ public void setCurrState(String currState) {
+ this.currState = currState;
+ }
+
+ public String getPrevArtifactUuid() {
+ return prevArtifactUuid;
+ }
+
+ public void setPrevArtifactUuid(String prevArtifactUuid) {
+ this.prevArtifactUuid = prevArtifactUuid;
+ }
+
+ public String getCurrArtifactUuid() {
+ return currArtifactUuid;
+ }
+
+ public void setCurrArtifactUuid(String currArtifactUuid) {
+ this.currArtifactUuid = currArtifactUuid;
+ }
+
+ public String getArtifactData() {
+ return artifactData;
+ }
+
+ public void setArtifactData(String artifactData) {
+ this.artifactData = artifactData;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedAuthenticationAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedAuthenticationAudit.java
new file mode 100644
index 0000000000..0d6a5f3b3c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedAuthenticationAudit.java
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedAuthenticationAudit {
+
+ private String url;
+ private String realm;
+ private String user;
+ private String action;
+ private String authStatus;
+
+ public ExpectedAuthenticationAudit(String url, String user, String action, String authStatus) {
+ super();
+ this.url = url;
+ this.user = user;
+ this.action = action;
+ this.authStatus = authStatus;
+ this.realm = "ASDC";
+ }
+
+ public ExpectedAuthenticationAudit() {
+
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getAuthStatus() {
+ return authStatus;
+ }
+
+ public void setAuthStatus(String authStatus) {
+ this.authStatus = authStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "ExpectedAuthenticationAudit [url=" + url + ", realm=" + realm + ", user=" + user + ", action=" + action
+ + ", authStatus=" + authStatus + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedCategoryAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedCategoryAudit.java
new file mode 100644
index 0000000000..b11f7f585a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedCategoryAudit.java
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedCategoryAudit {
+ String action;
+ String modifier;
+ String modifierUid;
+ String modifierName;
+ String categoryName;
+ String subCategoryName;
+ String groupingName;
+ String resourceType;
+ String status;
+ String desc;
+ String details;
+
+ public ExpectedCategoryAudit(String action, String modifier, String categoryName, String subCategoryName,
+ String groupingName, String resourceType, String status, String desc) {
+ super();
+ this.action = action;
+ this.modifier = modifier;
+ this.categoryName = categoryName;
+ this.subCategoryName = subCategoryName;
+ this.groupingName = groupingName;
+ this.resourceType = resourceType;
+ this.status = status;
+ this.desc = desc;
+ }
+
+ public ExpectedCategoryAudit() {
+ action = null;
+ modifier = null;
+ categoryName = null;
+ subCategoryName = null;
+ groupingName = null;
+ resourceType = null;
+ status = null;
+ desc = null;
+ details = null;
+ modifierName = null;
+ modifierUid = null;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+
+ public void setCategoryName(String categoryName) {
+ this.categoryName = categoryName;
+ }
+
+ public String getSubCategoryName() {
+ return subCategoryName;
+ }
+
+ public void setSubCategoryName(String subCategoryName) {
+ this.subCategoryName = subCategoryName;
+ }
+
+ public String getGroupingName() {
+ return groupingName;
+ }
+
+ public void setGroupingName(String groupingName) {
+ this.groupingName = groupingName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+ public String getModifierUid() {
+ return modifierUid;
+ }
+
+ public void setModifierUid(String modifierUid) {
+ this.modifierUid = modifierUid;
+ }
+
+ public String getModifierName() {
+ return modifierName;
+ }
+
+ public void setModifierName(String modifierName) {
+ this.modifierName = modifierName;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedDistDownloadAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedDistDownloadAudit.java
new file mode 100644
index 0000000000..4b135f66f6
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedDistDownloadAudit.java
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedDistDownloadAudit {
+
+ String action;
+ String consumerId;
+ String resourceUrl;
+ String status;
+ String desc;
+
+ public ExpectedDistDownloadAudit(String action, String consumerId, String resourceUrl, String status, String desc) {
+ super();
+ this.action = action;
+ this.consumerId = consumerId;
+ this.resourceUrl = resourceUrl;
+ this.status = status;
+ this.desc = desc;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ public String getResourceUrl() {
+ return resourceUrl;
+ }
+
+ public void setResourceUrl(String resourceUrl) {
+ this.resourceUrl = resourceUrl;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedEcomConsumerAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedEcomConsumerAudit.java
new file mode 100644
index 0000000000..1414742423
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedEcomConsumerAudit.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedEcomConsumerAudit {
+
+ String action;
+ String modifier;
+ String ecomUser;
+ String status;
+ String desc;
+
+ public ExpectedEcomConsumerAudit(String action, String modifier, String ecomUser, String status, String desc) {
+ super();
+ this.action = action;
+ this.modifier = modifier;
+ this.ecomUser = ecomUser;
+ this.status = status;
+ this.desc = desc;
+ }
+
+ public ExpectedEcomConsumerAudit() {
+ action = null;
+ modifier = null;
+ ecomUser = null;
+ status = null;
+ desc = null;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getEcomUser() {
+ return ecomUser;
+ }
+
+ public void setEcomUser(String ecomUser) {
+ this.ecomUser = ecomUser;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedExternalAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedExternalAudit.java
new file mode 100644
index 0000000000..e689a3921b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedExternalAudit.java
@@ -0,0 +1,179 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedExternalAudit {
+
+ String ACTION;
+ String CONSUMER_ID;
+ String RESOURCE_URL;
+ String STATUS;
+ String DESC;
+ String RESOURCE_NAME;
+ String RESOURCE_TYPE;
+ String SERVICE_INSTANCE_ID;// resource/ service UUID
+ String MODIFIER;
+ String PREV_ARTIFACT_UUID;
+ String CURR_ARTIFACT_UUID;
+ String ARTIFACT_DATA;
+
+ public ExpectedExternalAudit() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public String getRESOURCE_NAME() {
+ return RESOURCE_NAME;
+ }
+
+ public void setRESOURCE_NAME(String rESOURCE_NAME) {
+ RESOURCE_NAME = rESOURCE_NAME;
+ }
+
+ public String getRESOURCE_TYPE() {
+ return RESOURCE_TYPE;
+ }
+
+ public void setRESOURCE_TYPE(String rESOURCE_TYPE) {
+ RESOURCE_TYPE = rESOURCE_TYPE;
+ }
+
+ public String getSERVICE_INSTANCE_ID() {
+ return SERVICE_INSTANCE_ID;
+ }
+
+ public void setSERVICE_INSTANCE_ID(String sERVICE_INSTANCE_ID) {
+ SERVICE_INSTANCE_ID = sERVICE_INSTANCE_ID;
+ }
+
+ public ExpectedExternalAudit(String aCTION, String cONSUMER_ID, String rESOURCE_URL, String sTATUS, String dESC,
+ String rESOURCE_NAME, String rESOURCE_TYPE, String sERVICE_INSTANCE_ID) {
+ super();
+ ACTION = aCTION;
+ CONSUMER_ID = cONSUMER_ID;
+ RESOURCE_URL = rESOURCE_URL;
+ STATUS = sTATUS;
+ DESC = dESC;
+ RESOURCE_NAME = rESOURCE_NAME;
+ RESOURCE_TYPE = rESOURCE_TYPE;
+ SERVICE_INSTANCE_ID = sERVICE_INSTANCE_ID;
+ }
+
+ public ExpectedExternalAudit(String aCTION, String cONSUMER_ID, String rESOURCE_URL, String sTATUS, String dESC) {
+ super();
+ ACTION = aCTION;
+ CONSUMER_ID = cONSUMER_ID;
+ RESOURCE_URL = rESOURCE_URL;
+ STATUS = sTATUS;
+ DESC = dESC;
+ }
+
+ public ExpectedExternalAudit(String aCTION, String cONSUMER_ID, String rESOURCE_URL, String sTATUS, String dESC,
+ String rESOURCE_NAME, String rESOURCE_TYPE, String sERVICE_INSTANCE_ID, String mODIFIER,
+ String pREV_ARTIFACT_UUID, String cURR_ARTIFACT_UUID, String aRTIFACT_DATA) {
+ super();
+ ACTION = aCTION;
+ CONSUMER_ID = cONSUMER_ID;
+ RESOURCE_URL = rESOURCE_URL;
+ STATUS = sTATUS;
+ DESC = dESC;
+ RESOURCE_NAME = rESOURCE_NAME;
+ RESOURCE_TYPE = rESOURCE_TYPE;
+ SERVICE_INSTANCE_ID = sERVICE_INSTANCE_ID;
+ MODIFIER = mODIFIER;
+ PREV_ARTIFACT_UUID = pREV_ARTIFACT_UUID;
+ CURR_ARTIFACT_UUID = cURR_ARTIFACT_UUID;
+ ARTIFACT_DATA = aRTIFACT_DATA;
+ }
+
+ public String getACTION() {
+ return ACTION;
+ }
+
+ public void setACTION(String aCTION) {
+ ACTION = aCTION;
+ }
+
+ public String getCONSUMER_ID() {
+ return CONSUMER_ID;
+ }
+
+ public void setCONSUMER_ID(String cONSUMER_ID) {
+ CONSUMER_ID = cONSUMER_ID;
+ }
+
+ public String getRESOURCE_URL() {
+ return RESOURCE_URL;
+ }
+
+ public void setRESOURCE_URL(String rESOURCE_URL) {
+ RESOURCE_URL = rESOURCE_URL;
+ }
+
+ public String getSTATUS() {
+ return STATUS;
+ }
+
+ public void setSTATUS(String sTATUS) {
+ STATUS = sTATUS;
+ }
+
+ public String getDESC() {
+ return DESC;
+ }
+
+ public void setDESC(String dESC) {
+ DESC = dESC;
+ }
+
+ public String getMODIFIER() {
+ return MODIFIER;
+ }
+
+ public void setMODIFIER(String mODIFIER) {
+ MODIFIER = mODIFIER;
+ }
+
+ public String getPREV_ARTIFACT_UUID() {
+ return PREV_ARTIFACT_UUID;
+ }
+
+ public void setPREV_ARTIFACT_UUID(String pREV_ARTIFACT_UUID) {
+ PREV_ARTIFACT_UUID = pREV_ARTIFACT_UUID;
+ }
+
+ public String getCURR_ARTIFACT_UUID() {
+ return CURR_ARTIFACT_UUID;
+ }
+
+ public void setCURR_ARTIFACT_UUID(String cURR_ARTIFACT_UUID) {
+ CURR_ARTIFACT_UUID = cURR_ARTIFACT_UUID;
+ }
+
+ public String getARTIFACT_DATA() {
+ return ARTIFACT_DATA;
+ }
+
+ public void setARTIFACT_DATA(String aRTIFACT_DATA) {
+ ARTIFACT_DATA = aRTIFACT_DATA;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGetUserListAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGetUserListAudit.java
new file mode 100644
index 0000000000..561b92a317
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGetUserListAudit.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedGetUserListAudit {
+
+ String action;
+ String modifier;
+ String status;
+ String desc;
+ String details;
+
+ public ExpectedGetUserListAudit(String action, String modifier, String status, String desc, String details) {
+ super();
+ this.action = action;
+ this.modifier = modifier;
+ this.status = status;
+ this.desc = desc;
+ this.details = details;
+ }
+
+ public ExpectedGetUserListAudit() {
+ action = null;
+ modifier = null;
+ details = null;
+ status = null;
+ desc = null;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGroupingAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGroupingAudit.java
new file mode 100644
index 0000000000..b481cb77af
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedGroupingAudit.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedGroupingAudit {
+ String action;
+ String modifier;
+ String categoryName;
+ String subCategoryName;
+ String groupingName;
+ String resourceType;
+ String status;
+ String desc;
+
+ public ExpectedGroupingAudit(String action, String modifier, String categoryName, String subCategoryName,
+ String groupingName, String resourceType, String status, String desc) {
+ super();
+ this.action = action;
+ this.modifier = modifier;
+ this.categoryName = categoryName;
+ this.subCategoryName = subCategoryName;
+ this.groupingName = groupingName;
+ this.resourceType = resourceType;
+ this.status = status;
+ this.desc = desc;
+ }
+
+ public ExpectedGroupingAudit() {
+ action = null;
+ modifier = null;
+ categoryName = null;
+ subCategoryName = null;
+ groupingName = null;
+ resourceType = null;
+ status = null;
+ desc = null;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+
+ public void setCategoryName(String categoryName) {
+ this.categoryName = categoryName;
+ }
+
+ public String getSubCategoryName() {
+ return subCategoryName;
+ }
+
+ public void setSubCategoryName(String subCategoryName) {
+ this.subCategoryName = subCategoryName;
+ }
+
+ public String getGroupingName() {
+ return groupingName;
+ }
+
+ public void setGroupingName(String groupingName) {
+ this.groupingName = groupingName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedProductAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedProductAudit.java
new file mode 100644
index 0000000000..40b86fa528
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedProductAudit.java
@@ -0,0 +1,142 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedProductAudit {
+
+ String ACTION;
+ String MODIFIER;
+ String STATUS;
+ String DESC;
+ String RESOURCE_NAME;
+ String RESOURCE_TYPE;
+ String PREV_VERSION;
+ String CURR_VERSION;
+ String PREV_STATE;
+ String CURR_STATE;
+ String TIMESTAMP;
+ String SERVICE_INSTANCE_ID;
+ String COMMENT;
+
+ public String getCOMMENT() {
+ return COMMENT;
+ }
+
+ public void setCOMMENT(String cOMMENT) {
+ COMMENT = cOMMENT;
+ }
+
+ public String getSERVICE_INSTANCE_ID() {
+ return SERVICE_INSTANCE_ID;
+ }
+
+ public void setSERVICE_INSTANCE_ID(String sERVICE_INSTANCE_ID) {
+ SERVICE_INSTANCE_ID = sERVICE_INSTANCE_ID;
+ }
+
+ public String getACTION() {
+ return ACTION;
+ }
+
+ public void setACTION(String aCTION) {
+ ACTION = aCTION;
+ }
+
+ public String getMODIFIER() {
+ return MODIFIER;
+ }
+
+ public void setMODIFIER(String mODIFIER) {
+ MODIFIER = mODIFIER;
+ }
+
+ public String getSTATUS() {
+ return STATUS;
+ }
+
+ public void setSTATUS(String sTATUS) {
+ STATUS = sTATUS;
+ }
+
+ public String getDESC() {
+ return DESC;
+ }
+
+ public void setDESC(String dESC) {
+ DESC = dESC;
+ }
+
+ public String getRESOURCE_NAME() {
+ return RESOURCE_NAME;
+ }
+
+ public void setRESOURCE_NAME(String rESOURCE_NAME) {
+ RESOURCE_NAME = rESOURCE_NAME;
+ }
+
+ public String getRESOURCE_TYPE() {
+ return RESOURCE_TYPE;
+ }
+
+ public void setRESOURCE_TYPE(String rESOURCE_TYPE) {
+ RESOURCE_TYPE = rESOURCE_TYPE;
+ }
+
+ public String getPREV_VERSION() {
+ return PREV_VERSION;
+ }
+
+ public void setPREV_VERSION(String pREV_VERSION) {
+ PREV_VERSION = pREV_VERSION;
+ }
+
+ public String getCURR_VERSION() {
+ return CURR_VERSION;
+ }
+
+ public void setCURR_VERSION(String cURR_VERSION) {
+ CURR_VERSION = cURR_VERSION;
+ }
+
+ public String getPREV_STATE() {
+ return PREV_STATE;
+ }
+
+ public void setPREV_STATE(String pREV_STATE) {
+ PREV_STATE = pREV_STATE;
+ }
+
+ public String getCURR_STATE() {
+ return CURR_STATE;
+ }
+
+ public void setCURR_STATE(String cURR_STATE) {
+ CURR_STATE = cURR_STATE;
+ }
+
+ public String getTIMESTAMP() {
+ return TIMESTAMP;
+ }
+
+ public void setTIMESTAMP(String tIMESTAMP) {
+ TIMESTAMP = tIMESTAMP;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedResourceAuditJavaObject.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedResourceAuditJavaObject.java
new file mode 100644
index 0000000000..cc78709888
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedResourceAuditJavaObject.java
@@ -0,0 +1,300 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedResourceAuditJavaObject {
+
+ String ACTION;
+ String MODIFIER_NAME;
+ String MODIFIER_UID;
+ String STATUS;
+ String DESC;
+ String RESOURCE_NAME;
+ String RESOURCE_TYPE;
+ String PREV_VERSION;
+ String CURR_VERSION;
+ String PREV_STATE;
+ String CURR_STATE;
+ String TIMESTAMP;
+ String ARTIFACT_DATA;
+ String DPREV_STATUS;
+ String DCURR_STATUS;
+ String COMMENT;
+ String DID;
+ String TOPIC_NAME;
+ String TOSCA_NODE_TYPE;
+ String CURR_ARTIFACT_UUID;
+ String PREV_ARTIFACT_UUID;
+ String ARTIFACT_TIMEOUT;
+ String MODIFIER;
+ String SERVICE_INSTANCE_ID;
+ String CONSUMER_ID;
+ String RESOURCE_URL;
+
+ public String getCONSUMER_ID() {
+ return CONSUMER_ID;
+ }
+
+ public void setCONSUMER_ID(String consumer_id) {
+ CONSUMER_ID = consumer_id;
+ }
+
+ public String getRESOURCE_URL() {
+ return RESOURCE_URL;
+ }
+
+ public void setRESOURCE_URL(String resource_url) {
+ RESOURCE_URL = resource_url;
+ }
+
+ public String getSERVICE_INSTANCE_ID() {
+ return SERVICE_INSTANCE_ID;
+ }
+
+ public void setSERVICE_INSTANCE_ID(String sERVICE_INSTANCE_ID) {
+ SERVICE_INSTANCE_ID = sERVICE_INSTANCE_ID;
+ }
+
+ public String getMODIFIER() {
+ return MODIFIER;
+ }
+
+ public void setMODIFIER(String mODIFIER) {
+ MODIFIER = mODIFIER;
+ }
+
+ public String getArtifactTimeout() {
+ return ARTIFACT_TIMEOUT;
+ }
+
+ public void setArtifactTimeout(String artifactTimeout) {
+ this.ARTIFACT_TIMEOUT = artifactTimeout;
+ }
+
+ public String getCurrArtifactUuid() {
+ return CURR_ARTIFACT_UUID;
+ }
+
+ public void setCurrArtifactUuid(String currArtifactUuid) {
+ this.CURR_ARTIFACT_UUID = currArtifactUuid;
+ }
+
+ public String getPrevArtifactUuid() {
+ return PREV_ARTIFACT_UUID;
+ }
+
+ public void setPrevArtifactUuid(String prevArtifactUuid) {
+ this.PREV_ARTIFACT_UUID = prevArtifactUuid;
+ }
+
+ public String getToscaNodeType() {
+ return TOSCA_NODE_TYPE;
+ }
+
+ public void setToscaNodeType(String ToscaNodeType) {
+ this.TOSCA_NODE_TYPE = ToscaNodeType;
+ }
+
+ public String getTopicName() {
+ return TOPIC_NAME;
+ }
+
+ public void setTopicName(String topicName) {
+ this.TOPIC_NAME = topicName;
+ }
+
+ public String getDistributionId() {
+ return DID;
+ }
+
+ public void setDistributionId(String did) {
+ this.DID = did;
+ }
+
+ public ExpectedResourceAuditJavaObject() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public ExpectedResourceAuditJavaObject(String action, String modifierName, String modifierUid, String status,
+ String desc, String resourceName, String resourceType, String prevVersion, String currVersion,
+ String prevState, String currState, String timestamp, String toscaNodesType, String timeout,
+ String modifier, String serviceInstanceId) {
+ super();
+ this.ACTION = action;
+ this.MODIFIER_NAME = modifierName;
+ this.MODIFIER_UID = modifierUid;
+ this.STATUS = status;
+ this.DESC = desc;
+ this.RESOURCE_NAME = resourceName;
+ this.RESOURCE_TYPE = resourceType;
+ this.PREV_VERSION = prevVersion;
+ this.CURR_VERSION = currVersion;
+ this.PREV_STATE = prevState;
+ this.CURR_STATE = currState;
+ this.TIMESTAMP = timestamp;
+ this.TOSCA_NODE_TYPE = toscaNodesType;
+ this.ARTIFACT_TIMEOUT = timeout;
+ this.MODIFIER = modifier;
+ this.SERVICE_INSTANCE_ID = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return ACTION;
+ }
+
+ public void setAction(String action) {
+ this.ACTION = action;
+ }
+
+ public String getModifierName() {
+ return MODIFIER_NAME;
+ }
+
+ public void setModifierName(String modifierName) {
+ this.MODIFIER_NAME = modifierName;
+ }
+
+ public String getModifierUid() {
+ return MODIFIER_UID;
+ }
+
+ public void setModifierUid(String modifierUid) {
+ this.MODIFIER_UID = modifierUid;
+ }
+
+ public String getStatus() {
+ return STATUS;
+ }
+
+ public void setStatus(String status) {
+ this.STATUS = status;
+ }
+
+ public String getDesc() {
+ return DESC;
+ }
+
+ public void setDesc(String desc) {
+ this.DESC = desc;
+ }
+
+ public String getResourceName() {
+ return RESOURCE_NAME;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.RESOURCE_NAME = resourceName;
+ }
+
+ public String getResourceType() {
+ return RESOURCE_TYPE;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.RESOURCE_TYPE = resourceType;
+ }
+
+ public String getPrevVersion() {
+ return PREV_VERSION;
+ }
+
+ public void setPrevVersion(String prevVersion) {
+ this.PREV_VERSION = prevVersion;
+ }
+
+ public String getCurrVersion() {
+ return CURR_VERSION;
+ }
+
+ public void setCurrVersion(String currVersion) {
+ this.CURR_VERSION = currVersion;
+ }
+
+ public String getPrevState() {
+ return PREV_STATE;
+ }
+
+ public void setPrevState(String prevState) {
+ this.PREV_STATE = prevState;
+ }
+
+ public String getCurrState() {
+ return CURR_STATE;
+ }
+
+ public void setCurrState(String currState) {
+ this.CURR_STATE = currState;
+ }
+
+ public String getTimestamp() {
+ return TIMESTAMP;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.TIMESTAMP = timestamp;
+ }
+
+ public String getArtifactData() {
+ return ARTIFACT_DATA;
+ }
+
+ public void setArtifactData(String artifactData) {
+ this.ARTIFACT_DATA = artifactData;
+ }
+
+ public String getDprevStatus() {
+ return DPREV_STATUS;
+ }
+
+ public void setDprevStatus(String dprevStatus) {
+ this.DPREV_STATUS = dprevStatus;
+ }
+
+ public String getDcurrStatus() {
+ return DCURR_STATUS;
+ }
+
+ public void setDcurrStatus(String dcurrStatus) {
+ this.DCURR_STATUS = dcurrStatus;
+ }
+
+ public String getComment() {
+ return COMMENT;
+ }
+
+ public void setComment(String comment) {
+ this.COMMENT = comment;
+ }
+
+ @Override
+ public String toString() {
+ return "ExpectedResourceAuditJavaObject [ACTION=" + ACTION + ", STATUS=" + STATUS + ", DESC=" + DESC
+ + ", RESOURCE_NAME=" + RESOURCE_NAME + ", RESOURCE_TYPE=" + RESOURCE_TYPE + ", PREV_VERSION="
+ + PREV_VERSION + ", CURR_VERSION=" + CURR_VERSION + ", PREV_STATE=" + PREV_STATE + ", CURR_STATE="
+ + CURR_STATE + ", TIMESTAMP=" + TIMESTAMP + ", ARTIFACT_DATA=" + ARTIFACT_DATA + ", DPREV_STATUS="
+ + DPREV_STATUS + ", DCURR_STATUS=" + DCURR_STATUS + ", COMMENT=" + COMMENT + ", DID=" + DID
+ + ", TOPIC_NAME=" + TOPIC_NAME + ", TOSCA_NODE_TYPE=" + TOSCA_NODE_TYPE + ", CURR_ARTIFACT_UUID="
+ + CURR_ARTIFACT_UUID + ", PREV_ARTIFACT_UUID=" + PREV_ARTIFACT_UUID + ", ARTIFACT_TIMEOUT="
+ + ARTIFACT_TIMEOUT + ", MODIFIER=" + MODIFIER + ", SERVICE_INSTANCE_ID=" + SERVICE_INSTANCE_ID + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedUserCRUDAudit.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedUserCRUDAudit.java
new file mode 100644
index 0000000000..2bc8625057
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/expected/ExpectedUserCRUDAudit.java
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.expected;
+
+public class ExpectedUserCRUDAudit {
+ String action;
+ String modifier;
+ String status;
+ String desc;
+ String userBefore;
+ String userAfter;
+
+ public ExpectedUserCRUDAudit(String action, String modifier, String status, String desc, String userBefore,
+ String userAfter) {
+ super();
+ this.action = action;
+ this.modifier = modifier;
+ this.status = status;
+ this.desc = desc;
+ this.userBefore = userBefore;
+ this.userAfter = userAfter;
+ }
+
+ public ExpectedUserCRUDAudit() {
+ action = null;
+ modifier = null;
+ userBefore = null;
+ userAfter = null;
+ status = null;
+ desc = null;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getUserBefore() {
+ return userBefore;
+ }
+
+ public void setUserBefore(String userBefore) {
+ this.userBefore = userBefore;
+ }
+
+ public String getUserAfter() {
+ return userAfter;
+ }
+
+ public void setUserAfter(String userAfter) {
+ this.userAfter = userAfter;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderData.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderData.java
new file mode 100644
index 0000000000..2c23b08717
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderData.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.http;
+
+public class HeaderData {
+ String contentMd5;
+ String contentType;
+ String HttpCspUserId;
+ String HttpCspFirstName;
+ String HttpCspLastName;
+ String HttpCspWsType;
+ String HttpIvRemoteAddress;
+ String HttpIvUser;
+
+ public HeaderData() {
+ super();
+ }
+
+ public HeaderData(String contentMd5, String contentType, String httpCspUserId, String httpCspFirstName,
+ String httpCspLastName, String httpCspWsType, String httpIvRemoteAddress, String httpIvUser) {
+ super();
+ this.contentMd5 = contentMd5;
+ this.contentType = contentType;
+ HttpCspUserId = httpCspUserId;
+ HttpCspFirstName = httpCspFirstName;
+ HttpCspLastName = httpCspLastName;
+ HttpCspWsType = httpCspWsType;
+ HttpIvRemoteAddress = httpIvRemoteAddress;
+ HttpIvUser = httpIvUser;
+ }
+
+ public String getContentMd5() {
+ return contentMd5;
+ }
+
+ public void setContentMd5(String contentMd5) {
+ this.contentMd5 = contentMd5;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public String getHttpCspUserId() {
+ return HttpCspUserId;
+ }
+
+ public void setHttpCspUserId(String httpCspUserId) {
+ HttpCspUserId = httpCspUserId;
+ }
+
+ public String getHttpCspFirstName() {
+ return HttpCspFirstName;
+ }
+
+ public void setHttpCspFirstName(String httpCspFirstName) {
+ HttpCspFirstName = httpCspFirstName;
+ }
+
+ public String getHttpCspLastName() {
+ return HttpCspLastName;
+ }
+
+ public void setHttpCspLastName(String httpCspLastName) {
+ HttpCspLastName = httpCspLastName;
+ }
+
+ public String getHttpCspWsType() {
+ return HttpCspWsType;
+ }
+
+ public void setHttpCspWsType(String httpCspWsType) {
+ HttpCspWsType = httpCspWsType;
+ }
+
+ public String getHttpIvRemoteAddress() {
+ return HttpIvRemoteAddress;
+ }
+
+ public void setHttpIvRemoteAddress(String httpIvRemoteAddress) {
+ HttpIvRemoteAddress = httpIvRemoteAddress;
+ }
+
+ public String getHttpIvUser() {
+ return HttpIvUser;
+ }
+
+ public void setHttpIvUser(String httpIvUser) {
+ HttpIvUser = httpIvUser;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderValue.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderValue.java
new file mode 100644
index 0000000000..4a2ad9ab82
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HeaderValue.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.http;
+
+public enum HeaderValue {
+
+ APPLICATION_JSON("application/json");
+
+ String value;
+
+ private HeaderValue(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+
+ return value;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpHeaderEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpHeaderEnum.java
new file mode 100644
index 0000000000..cd3beee150
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpHeaderEnum.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.http;
+
+public enum HttpHeaderEnum {
+
+ Content_MD5("Content-MD5"),
+ USER_ID("USER_ID"),
+ HTTP_CSP_FIRSTNAME("HTTP_CSP_FIRSTNAME"),
+ HTTP_CSP_LASTNAME("HTTP_CSP_LASTNAME"),
+ HTTP_CSP_WSTYPE("HTTP_CSP_WSTYPE"),
+ HTTP_IV_REMOTE_ADDRESS("HTTP_IV_REMOTE_ADDRESS"),
+ HTTP_IV_USER("HTTP_IV_USER"),
+ HTTP_CSP_EMAIL("HTTP_CSP_EMAIL"),
+ CONTENT_TYPE("Content-Type"),
+ ACCEPT("Accept"),
+ X_ECOMP_REQUEST_ID_HEADER("X-ECOMP-RequestID"),
+ CACHE_CONTROL("Cache-Control"),
+ X_ECOMP_INSTANCE_ID("X-ECOMP-InstanceID"),
+ AUTHORIZATION("Authorization"),
+ CONTENT_LENGTH("Content-Length"),
+ CONTENT_DISPOSITION("Content-Disposition"),
+ HOST("Host"),
+ X_ECOMP_SERVICE_ID_HEADER("X-ECOMP-ServiceID"),
+ WWW_AUTHENTICATE("WWW-Authenticate"),
+ ECOMP_PASSWORD("password"),
+ ECOMP_USERNAME("username");
+
+ String value;
+
+ private HttpHeaderEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+
+ return value;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpRequest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpRequest.java
new file mode 100644
index 0000000000..7c2830ec5f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/HttpRequest.java
@@ -0,0 +1,883 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.http;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Scanner;
+
+import javax.net.ssl.HttpsURLConnection;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.annotation.NotThreadSafe;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class HttpRequest {
+
+ static Logger logger = LoggerFactory.getLogger(HttpRequest.class.getName());
+
+ public RestResponse httpSendGet(String url, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+ // optional default is GET
+ con.setRequestMethod("GET");
+ // add request header
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+
+ }
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send GET http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+ String result;
+
+ try {
+
+ result = IOUtils.toString(con.getInputStream());
+ response.append(result);
+
+ } catch (Exception e) {
+ // logger.error("Error during getting input stream: ",e);
+ }
+
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e) {
+ // logger.error("Error during getting error stream: ",e);
+ }
+
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ restResponse.setErrorCode(responseCode);
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+
+ return restResponse;
+ }
+
+ public RestResponse httpsSendGet(String url, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
+ // optional default is GET
+ con.setRequestMethod("GET");
+ // add request header
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+
+ }
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send GET http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ logger.debug("response body is null");
+ }
+
+ String result;
+
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ // result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ restResponse.setErrorCode(responseCode);
+ // restResponse.setResponse(result);
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+
+ return restResponse;
+ }
+
+ public RestResponse httpSendByMethod(String url, String method, String body, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ // add request method
+ con.setRequestMethod(method);
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+
+ }
+ if (body != null && !body.isEmpty() && !method.equals("DELETE")) {
+ // Send post request
+ con.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+ wr.writeBytes(body);
+ wr.flush();
+ wr.close();
+ }
+
+ // con.connect();
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send {} http request, url: {}", method, url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ // response = null;
+ logger.debug("response body is null");
+ }
+
+ String result;
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+ // if (response == null) {
+ // restResponse.setResponse(null);
+ // } else {
+ // restResponse.setResponse(response.toString());
+ // }
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+ return restResponse;
+
+ }
+
+ public RestResponse sendHttpPost(String url, String body, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ // add request method
+ con.setRequestMethod("POST");
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+ }
+
+ // Send post request
+ if (body != null) {
+ con.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+ wr.writeBytes(body);
+ wr.flush();
+ wr.close();
+ }
+
+ // con.connect();
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send POST http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ logger.debug("response body is null");
+ }
+
+ String result;
+
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+ return restResponse;
+
+ }
+
+ public RestResponse httpSendPost(String url, String body, Map<String, String> headers) throws IOException {
+ return httpSendPost(url, body, headers, "POST");
+ }
+
+ public RestResponse httpSendPut(String url, String body, Map<String, String> headers) throws IOException {
+ return httpSendPost(url, body, headers, "PUT");
+ }
+
+ public RestResponse httpSendPost(String url, String body, Map<String, String> headers, String methodType) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ // add request method
+ con.setRequestMethod(methodType);
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+ }
+
+ // Send post request
+ if (body != null) {
+ con.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+ wr.writeBytes(body);
+ wr.flush();
+ wr.close();
+ }
+
+ // con.connect();
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send POST http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ logger.debug("response body is null");
+ }
+
+ String result;
+
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+ return restResponse;
+
+ }
+
+ public RestResponse httpSendDeleteWithBody2(String url, String body, Map<String, String> headers) throws ClientProtocolException, IOException {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ RestResponse restResponse = new RestResponse();
+ HttpDeleteWithBody httpDelete = new HttpDeleteWithBody(url);
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ httpDelete.addHeader(key, value);
+ }
+ }
+
+ // add body to request
+ StringEntity input = new StringEntity(body, ContentType.APPLICATION_JSON);
+ httpDelete.setEntity(input);
+
+ // execute request
+ CloseableHttpResponse response = httpclient.execute(httpDelete);
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+
+ return restResponse;
+ }
+
+ public RestResponse httpSendDeleteWithBody(String url, String body, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ // add request method
+ con.setRequestMethod("DELETE");
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+ }
+
+ // Send post request
+ con.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+ wr.writeBytes(body);
+ wr.flush();
+ wr.close();
+
+ // con.connect();
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send DELETE http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ logger.debug("response body is null");
+ }
+
+ String result;
+
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+ return restResponse;
+
+ }
+
+ public RestResponse httpSendPostWithOutBody(String url, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ // add request method
+ con.setRequestMethod("POST");
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+ }
+
+ // con.connect();
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send POST http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ // response = null;
+ logger.debug("response body is null");
+ }
+
+ String result;
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+ // if (response == null) {
+ // restResponse.setResponse(null);
+ // } else {
+ // restResponse.setResponse(response.toString());
+ // }
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+ return restResponse;
+
+ }
+
+ public RestResponse httpSendPostMultipart(String url, Map<String, String> headers, String jsonLocation, String zipLocation) throws IOException {
+
+ Gson gson = new Gson();
+ String gsonToSend = null;
+ RestResponse restResponse = new RestResponse();
+ BufferedReader br = null;
+ //
+ //
+ //
+ //
+ // try {
+ //
+ // String sCurrentLine;
+ //
+ // br = new BufferedReader(new FileReader(jsonLocation));
+ //
+ // while ((sCurrentLine = br.readLine()) != null) {
+ // System.out.println(sCurrentLine);
+ // }
+ //
+ // } catch (IOException e) {
+ // e.printStackTrace();
+ // } finally {
+ // try {
+ // if (br != null)br.close();
+ // gsonToSend = br.toString();
+ // } catch (IOException ex) {
+ // ex.printStackTrace();
+ // }
+ // }
+
+ gsonToSend = new Scanner(new File(jsonLocation)).useDelimiter("\\Z").next();
+ logger.debug("gsonToSend: {}", gsonToSend);
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+ mpBuilder.addPart("resourceZip", new FileBody(new File(zipLocation)));
+ mpBuilder.addPart("resourceMetadata", new StringBody(gsonToSend, ContentType.APPLICATION_JSON));
+
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("USER_ID", "adminid");
+ httpPost.setEntity(mpBuilder.build());
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ CloseableHttpResponse response = client.execute(httpPost);
+ try {
+ logger.debug("----------------------------------------");
+ logger.debug("response.getStatusLine(): {}", response.getStatusLine());
+ HttpEntity resEntity = response.getEntity();
+ if (resEntity != null) {
+ logger.debug("Response content length: {}", resEntity.getContentLength());
+ }
+ EntityUtils.consume(resEntity);
+ } finally {
+
+ response.close();
+ client.close();
+ }
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+ restResponse.setResponse(response.getEntity().toString());
+
+ return restResponse;
+
+ }
+
+ public RestResponse httpSendPostWithAuth(String url, String body, Map<String, String> headers, String username, String password) throws IOException {
+
+ String userPassword = username + ":" + password;
+ String encoding = Base64.encodeBase64String(userPassword.getBytes());
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ // add request method
+ con.setRequestMethod("POST");
+
+ con.setRequestProperty("Authorization", "Basic " + encoding);
+
+ // add request headers
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+
+ }
+
+ // Send post request
+ con.setDoOutput(true);
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+ wr.writeBytes(body);
+ wr.flush();
+ wr.close();
+
+ // con.connect();
+
+ int responseCode = con.getResponseCode();
+ logger.debug("Send POST http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ response = null;
+
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+ if (response == null) {
+ restResponse.setResponse(null);
+ } else {
+ restResponse.setResponse(response.toString());
+ }
+
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+ return restResponse;
+
+ }
+
+ public RestResponse httpSendDelete(String url, Map<String, String> headers) throws IOException {
+
+ RestResponse restResponse = new RestResponse();
+ URL obj = new URL(url);
+ HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+
+ if (headers != null) {
+ for (Entry<String, String> header : headers.entrySet()) {
+ String key = header.getKey();
+ String value = header.getValue();
+ con.setRequestProperty(key, value);
+ }
+
+ }
+
+ con.setDoOutput(true);
+ con.setRequestMethod("DELETE");
+ int responseCode = con.getResponseCode();
+ logger.debug("Send DELETE http request, url: {}", url);
+ logger.debug("Response Code: {}", responseCode);
+
+ StringBuffer response = new StringBuffer();
+
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ logger.debug("response body is null");
+ }
+
+ String result;
+
+ try {
+
+ result = IOUtils.toString(con.getErrorStream());
+ response.append(result);
+
+ } catch (Exception e2) {
+ result = null;
+ }
+ logger.debug("Response body: {}", response);
+
+ // print result
+
+ restResponse.setErrorCode(responseCode);
+
+ if (response != null) {
+ restResponse.setResponse(response.toString());
+ }
+
+ restResponse.setErrorCode(con.getResponseCode());
+ Map<String, List<String>> headerFields = con.getHeaderFields();
+ restResponse.setHeaderFields(headerFields);
+ String responseMessage = con.getResponseMessage();
+ restResponse.setResponseMessage(responseMessage);
+
+ con.disconnect();
+
+ return restResponse;
+ }
+
+ public static RestResponse sendHttpPostWithEntity(HttpEntity requestEntity, String url, Map<String, String> headers) throws IOException, ClientProtocolException {
+ CloseableHttpResponse response = null;
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ RestResponse restResponse = new RestResponse();
+ for (Entry<String, String> entry : headers.entrySet()) {
+ httpPost.addHeader(entry.getKey(), entry.getValue());
+ }
+
+ httpPost.setEntity(requestEntity);
+ response = client.execute(httpPost);
+ HttpEntity responseEntity = response.getEntity();
+ String responseBody = null;
+ if (responseEntity != null) {
+ InputStream instream = responseEntity.getContent();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(instream, writer);
+ responseBody = writer.toString();
+ try {
+
+ } finally {
+ instream.close();
+ }
+ }
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+ restResponse.setResponse(responseBody);
+
+ return restResponse;
+
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ private static void closeHttpClient(CloseableHttpClient client) {
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ logger.debug("failed to close client or response: ", e);
+ }
+ }
+
+ private static void closeResponse(CloseableHttpResponse response) {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ } catch (IOException e) {
+ logger.debug("failed to close client or response: ", e);
+ }
+ }
+
+ @NotThreadSafe
+ class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
+ public static final String METHOD_NAME = "DELETE";
+
+ public String getMethod() {
+ return METHOD_NAME;
+ }
+
+ public HttpDeleteWithBody(final String uri) {
+ super();
+ setURI(URI.create(uri));
+ }
+
+ public HttpDeleteWithBody(final URI uri) {
+ super();
+ setURI(uri);
+ }
+
+ public HttpDeleteWithBody() {
+ super();
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/MustHeaders.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/MustHeaders.java
new file mode 100644
index 0000000000..6937608d2a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/MustHeaders.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.http;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MustHeaders {
+
+ private Map<String, String> headers = new HashMap<String, String>();
+
+ public MustHeaders(HeaderData headerData) {
+
+ super();
+ headers.put(HttpHeaderEnum.Content_MD5.getValue(), headerData.getContentMd5());
+ headers.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), headerData.getContentType());
+ headers.put(HttpHeaderEnum.ACCEPT.getValue(), headerData.getContentType());
+ headers.put(HttpHeaderEnum.USER_ID.getValue(), headerData.getHttpCspUserId());
+ headers.put(HttpHeaderEnum.HTTP_CSP_FIRSTNAME.getValue(), headerData.getHttpCspFirstName());
+ headers.put(HttpHeaderEnum.HTTP_CSP_LASTNAME.getValue(), headerData.getHttpCspLastName());
+ headers.put(HttpHeaderEnum.HTTP_CSP_WSTYPE.getValue(), headerData.getHttpCspWsType());
+ headers.put(HttpHeaderEnum.HTTP_IV_REMOTE_ADDRESS.getValue(), headerData.getHttpIvRemoteAddress());
+ headers.put(HttpHeaderEnum.HTTP_IV_USER.getValue(), headerData.getHttpIvUser());
+
+ }
+
+ public MustHeaders() {
+ super();
+ }
+
+ public Map<String, String> getMap() {
+ return headers;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/RestResponse.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/RestResponse.java
new file mode 100644
index 0000000000..f11d35a646
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/datatypes/http/RestResponse.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes.http;
+
+import java.util.List;
+import java.util.Map;
+
+public class RestResponse {
+
+ Integer errorCode;
+ String response;
+ Map<String, List<String>> headerFields;
+ String responseMessage;
+
+ public RestResponse() {
+ super();
+ }
+
+ public RestResponse(Integer errorCode, String response, Map<String, List<String>> headerFields,
+ String responseMessage) {
+ super();
+ this.errorCode = errorCode;
+ this.response = response;
+ this.headerFields = headerFields;
+ this.responseMessage = responseMessage;
+ }
+
+ public Integer getErrorCode() {
+ return errorCode;
+ }
+
+ public void setErrorCode(Integer errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public String getResponse() {
+ return response;
+ }
+
+ public void setResponse(String response) {
+ this.response = response;
+ }
+
+ public Map<String, List<String>> getHeaderFields() {
+ return headerFields;
+ }
+
+ public void setHeaderFields(Map<String, List<String>> headerFields) {
+ this.headerFields = headerFields;
+ }
+
+ public String getResponseMessage() {
+ return responseMessage;
+ }
+
+ public void setResponseMessage(String responseMessage) {
+ this.responseMessage = responseMessage;
+ }
+
+ @Override
+ public String toString() {
+ return "RestResponse [errorCode=" + errorCode + ", response=" + response + ", headerFields=" + headerFields
+ + ", responseMessage=" + responseMessage + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/TODO/ImportCapabilityTypeCITest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/TODO/ImportCapabilityTypeCITest.java
new file mode 100644
index 0000000000..3af40c5bb8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/TODO/ImportCapabilityTypeCITest.java
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.TODO;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.DbUtils.TitanState;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import fj.data.Either;
+
+public class ImportCapabilityTypeCITest {
+ public static final DbUtils DbUtils = new DbUtils();
+
+ @AfterClass
+ public static void afterClass() {
+ DbUtils.shutDowntitan();
+ }
+
+ static Config config = Config.instance();
+
+ // private final String IMPORT_CAPABILITY_TYPES_PATH =
+ // "src/test/resources/CI/importResourceTests/import_capabilitiesTypes/";
+
+ @Test
+ public void testAddingCapabilityTypes() throws IOException {
+ TitanState originalState = DbUtils.getCurrentTitanState();
+
+ String importResourceDir = config.getImportResourceConfigDir();
+
+ String capabilityTypes = importResourceDir + File.separator + "capabilityTypesCi.zip";
+ // importCapabilityType("src/test/resources/CI/importResource/capabilityTypesCi.zip");
+ importCapabilityType(capabilityTypes);
+ Either<Vertex, Boolean> eitherVertex = DbUtils.getVertexByUId("tosca.capabilities.Test.Ci");
+ AssertJUnit.assertTrue(eitherVertex.isLeft());
+ DbUtils.restoreToTitanState(originalState);
+ eitherVertex = DbUtils.getVertexByUId("tosca.capabilities.Test.Ci");
+ AssertJUnit.assertTrue(eitherVertex.isRight());
+ }
+
+ @Test
+ public void AddingCapabilityNotFound() throws IOException {
+ TitanState originalState = DbUtils.getCurrentTitanState();
+ String importResourceTestsDir = config.getImportResourceTestsConfigDir();
+ String capabilitiesTests = importResourceTestsDir + File.separator + "capabilityTypesCi.zip";
+ importCapabilityType(capabilitiesTests);
+ Either<Vertex, Boolean> eitherVertex = DbUtils.getVertexByUId("tosca.capabilities.NonExsitingCapability");
+ AssertJUnit.assertTrue(eitherVertex.isRight());
+ DbUtils.restoreToTitanState(originalState);
+ }
+
+ public static Integer importAllCapabilityTypes() throws IOException {
+
+ String importResourceDir = config.getImportResourceConfigDir() + File.separator + "capabilityTypes.zip";
+ // return
+ // importCapabilityType("src/test/resources/CI/importResource/capabilityTypes.zip");
+ return importCapabilityType(importResourceDir);
+ }
+
+ private static Integer importCapabilityType(String filePath) throws IOException {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("capabilityTypeZip", new FileBody(new File(filePath)));
+
+ String url = String.format(Urls.IMPORT_CAPABILITY_TYPE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("USER_ID", "jh0003");
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ return response.getStatusLine().getStatusCode();
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ private static void closeHttpClient(CloseableHttpClient client) {
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ System.out.println("failed to close client or response: " + e.getMessage());
+ }
+ }
+
+ private static void closeResponse(CloseableHttpResponse response) {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ } catch (IOException e) {
+ System.out.println("failed to close client or response: " + e.getMessage());
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ArtifactServletTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ArtifactServletTest.java
new file mode 100644
index 0000000000..cd0647d8b1
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ArtifactServletTest.java
@@ -0,0 +1,656 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+public class ArtifactServletTest extends ComponentBaseTest {
+
+ private static Logger log = LoggerFactory.getLogger(ArtifactServletTest.class.getName());
+ protected static final String UPLOAD_ARTIFACT_PAYLOAD = "UHVUVFktVXNlci1LZXktRmlsZS0yOiBzc2gtcnNhDQpFbmNyeXB0aW9uOiBhZXMyNTYtY2JjDQpDb21tZW5wOA0K";
+ protected static final String UPLOAD_ARTIFACT_NAME = "TLV_prv.ppk";
+ protected Config config = Config.instance();
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";
+ protected Gson gson = new Gson();
+ protected JSONParser jsonParser = new JSONParser();
+ protected String serviceVersion;
+ protected Resource resourceDetailsVFCcomp;
+ protected Service defaultService1;
+
+ protected User sdncUserDetails;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ArtifactServletTest() {
+ super(name, ArtifactServletTest.class.getName());
+
+ }
+
+ @BeforeMethod
+ public void create() throws Exception {
+
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ Either<Resource, RestResponse> resourceDetailsVFCcompE = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC, NormativeTypesEnum.COMPUTE,
+ ResourceCategoryEnum.APPLICATION_L4_APP_SERVER, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVFCcomp = resourceDetailsVFCcompE.left().value();
+ Either<Service, RestResponse> defaultService1e = AtomicOperationUtils
+ .createDefaultService(UserRoleEnum.DESIGNER, true);
+ defaultService1 = defaultService1e.left().value();
+ }
+
+ @Test
+ public void upadteArtifactWithPayLoadToResourcseTest() throws Exception {
+
+ ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultArtifact();
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ int status = response.getErrorCode();
+ AssertJUnit.assertEquals("add informational artifact request returned status: " + response.getErrorCode(), 200,
+ status);
+
+ defaultArtifact.setDescription("kjglkh");
+ defaultArtifact.setArtifactName("install_apache.sh");
+ defaultArtifact.setArtifactType("SHELL");
+ defaultArtifact.setPayload("new payload");
+
+ response = ArtifactRestUtils.updateInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ status = response.getErrorCode();
+ AssertJUnit.assertEquals("failed to update artifact metatdata: " + response.getErrorCode(), 200, status);
+
+ response = ArtifactRestUtils.deleteInformationalArtifactFromResource(resourceDetailsVFCcomp.getUniqueId(),
+ defaultArtifact, sdncUserDetails);
+ status = response.getErrorCode();
+ AssertJUnit.assertEquals("failed to remove artifact: " + response.getErrorCode(), 200, status);
+
+ }
+
+ @Test
+ public void createAndUpdateArtifactToInterface() throws Exception {
+
+ CloseableHttpResponse response;
+ int status;
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ try {
+ // upload artifact to interface
+ String interfaceName = "Standard";
+ String operationName = "configure";
+
+ String userBodyJson = createUploadArtifactBodyJson();
+ String url = String.format(Urls.UPLOAD_ARTIFACT_BY_INTERFACE_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), interfaceName, operationName);
+
+ HttpPost httpPost = createPostAddArtifactRequeast(userBodyJson, url, true);
+ response = httpclient.execute(httpPost);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, status, 200);
+
+ // get artifact uniqueId
+ String artifactId = getLifecycleArtifactUid(response);
+
+ Map<String, Object> jsonBody = new HashMap<String, Object>();
+ jsonBody.put("artifactName", "TLV_prv.ppk");
+ jsonBody.put("artifactDisplayName", "configure");
+ jsonBody.put("artifactType", "SHELL");
+ jsonBody.put("mandatory", "false");
+ String newDescription = "new something";
+ jsonBody.put("description", newDescription);
+ jsonBody.put("artifactLabel", "configure");
+ userBodyJson = gson.toJson(jsonBody);
+
+ url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_BY_INTERFACE_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), interfaceName, operationName,
+ artifactId);
+
+ httpPost = createPostAddArtifactRequeast(userBodyJson, url, false);
+
+ response = httpclient.execute(httpPost);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, 200, status);
+
+ url = String.format(Urls.GET_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ resourceDetailsVFCcomp.getUniqueId());
+ HttpGet httpGet = createGetRequest(url);
+ response = httpclient.execute(httpGet);
+ AssertJUnit.assertTrue(response.getStatusLine().getStatusCode() == 200);
+ String responseString = new BasicResponseHandler().handleResponse(response);
+
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ responseMap = (JSONObject) responseMap.get("interfaces");
+ responseMap = (JSONObject) responseMap.get(interfaceName.toLowerCase());
+ responseMap = (JSONObject) responseMap.get("operations");
+ responseMap = (JSONObject) responseMap.get(operationName.toLowerCase());
+ responseMap = (JSONObject) responseMap.get("implementation");
+ String description = (String) responseMap.get("description");
+
+ AssertJUnit.assertEquals("the new description value was not set", newDescription, description);
+
+ // delete artifact
+ url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_BY_INTERFACE_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), interfaceName, operationName,
+ artifactId);
+ HttpDelete httpDelete = createDeleteArtifactRequest(url);
+
+ response = httpclient.execute(httpDelete);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, status, 200);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ protected String createUploadArtifactBodyJson() {
+ Map<String, Object> jsonBody = new HashMap<String, Object>();
+ jsonBody.put("artifactName", UPLOAD_ARTIFACT_NAME);
+ jsonBody.put("artifactDisplayName", "configure");
+ jsonBody.put("artifactType", "SHELL");
+ jsonBody.put("mandatory", "false");
+ jsonBody.put("description", "ff");
+ jsonBody.put("payloadData", UPLOAD_ARTIFACT_PAYLOAD);
+ jsonBody.put("artifactLabel", "configure");
+ return gson.toJson(jsonBody);
+ }
+
+ protected ArtifactDefinition getArtifactDataFromJson(String json) {
+ Gson gson = new Gson();
+ ArtifactDefinition artifact = new ArtifactDefinition();
+ artifact = gson.fromJson(json, ArtifactDefinition.class);
+
+ /*
+ * atifact.setArtifactName(UPLOAD_ARTIFACT_NAME);
+ * artifact.setArtifactDisplayName("configure");
+ * artifact.setArtifactType("SHELL"); artifact.setMandatory(false);
+ * artifact.setDescription("ff");
+ * artifact.setPayloadData(UPLOAD_ARTIFACT_PAYLOAD);
+ * artifact.setArtifactLabel("configure");
+ */
+ return artifact;
+ }
+
+ protected HttpGet createGetRequest(String url) {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ httpGet.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ httpGet.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ return httpGet;
+ }
+
+ protected String getArtifactUid(HttpResponse response) throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ String artifactId = (String) responseMap.get("uniqueId");
+ return artifactId;
+ }
+
+ protected String getArtifactEsId(HttpResponse response) throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ String esId = (String) responseMap.get("EsId");
+ return esId;
+ }
+
+ protected ArtifactDefinition addArtifactDataFromResponse(HttpResponse response, ArtifactDefinition artifact)
+ throws HttpResponseException, IOException, ParseException {
+ // String responseString = new
+ // BasicResponseHandler().handleResponse(response);
+ HttpEntity entity = response.getEntity();
+ String responseString = EntityUtils.toString(entity);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ artifact.setEsId((String) responseMap.get("esId"));
+ artifact.setUniqueId((String) responseMap.get("uniqueId"));
+ artifact.setArtifactGroupType(ArtifactGroupTypeEnum.findType((String) responseMap.get("artifactGroupType")));
+ artifact.setTimeout(((Long) responseMap.get("timeout")).intValue());
+ return artifact;
+ }
+
+ protected String getLifecycleArtifactUid(CloseableHttpResponse response)
+ throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ responseMap = (JSONObject) responseMap.get("implementation");
+ String artifactId = (String) responseMap.get("uniqueId");
+ return artifactId;
+ }
+
+ protected HttpDelete createDeleteArtifactRequest(String url) {
+ HttpDelete httpDelete = new HttpDelete(url);
+ httpDelete.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ httpDelete.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ return httpDelete;
+ }
+
+ protected HttpPost createPostAddArtifactRequeast(String jsonBody, String url, boolean addMd5Header)
+ throws UnsupportedEncodingException {
+ HttpPost httppost = new HttpPost(url);
+ httppost.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ httppost.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ httppost.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ if (addMd5Header) {
+ httppost.addHeader(HttpHeaderEnum.Content_MD5.getValue(), GeneralUtility.calculateMD5ByString(jsonBody));
+ }
+ StringEntity input = new StringEntity(jsonBody);
+ input.setContentType("application/json");
+ httppost.setEntity(input);
+ log.debug("Executing request {}", httppost.getRequestLine());
+ return httppost;
+ }
+
+ protected String createLoadArtifactBody() {
+ Map<String, Object> json = new HashMap<String, Object>();
+ json.put("artifactName", "install_apache2.sh");
+ json.put("artifactType", "SHELL");
+ json.put("description", "ddd");
+ json.put("payloadData", "UEsDBAoAAAAIAAeLb0bDQz");
+ json.put("artifactLabel", "name123");
+
+ String jsonStr = gson.toJson(json);
+ return jsonStr;
+ }
+
+ protected void checkDeleteResponse(RestResponse response) {
+ BaseRestUtils.checkStatusCode(response, "delete request failed", false, 204, 404);
+ }
+
+ protected ArtifactUiDownloadData getArtifactUiDownloadData(String artifactUiDownloadDataStr) throws Exception {
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ ArtifactUiDownloadData artifactUiDownloadData = mapper.readValue(artifactUiDownloadDataStr,
+ ArtifactUiDownloadData.class);
+ return artifactUiDownloadData;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ // TODO
+ // @Ignore("")
+ @Test
+ public void addArtifactNoPayLoadToResourcseTest() throws Exception {
+ ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultArtifact();
+ defaultArtifact.setPayload(null);
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ int status = response.getErrorCode();
+ AssertJUnit.assertTrue(status == 400);
+
+ }
+
+ @Test
+ public void upadteArtifactNoPayLoadToResourcseTest() throws Exception {
+
+ ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultArtifact();
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ int status = response.getErrorCode();
+ AssertJUnit.assertEquals("add informational artifact request returned status: " + response.getErrorCode(), 200,
+ status);
+
+ defaultArtifact.setDescription("kjglkh");
+ defaultArtifact.setArtifactName("install_apache.sh");
+ defaultArtifact.setArtifactType("SHELL");
+ defaultArtifact.setPayload(null);
+
+ response = ArtifactRestUtils.updateInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ status = response.getErrorCode();
+ AssertJUnit.assertEquals("failed to update artifact metatdata: " + response.getErrorCode(), 200, status);
+
+ response = ArtifactRestUtils.deleteInformationalArtifactFromResource(resourceDetailsVFCcomp.getUniqueId(),
+ defaultArtifact, sdncUserDetails);
+ status = response.getErrorCode();
+ AssertJUnit.assertEquals("failed to remove artifact: " + response.getErrorCode(), 200, status);
+
+ }
+
+ // TODO
+ @Test(enabled = false)
+ public void updateDeploymentArtifactToResourcseTest() throws Exception {
+
+ ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultDeploymentArtifactForType("HEAT");
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ int status = response.getErrorCode();
+ AssertJUnit.assertEquals("add informational artifact request returned status: " + response.getErrorCode(), 200,
+ status);
+
+ response = ArtifactRestUtils.updateInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ status = response.getErrorCode();
+ AssertJUnit.assertEquals("failed to update artifact metatdata: " + response.getErrorCode(), 200, status);
+
+ response = ArtifactRestUtils.deleteInformationalArtifactFromResource(resourceDetailsVFCcomp.getUniqueId(),
+ defaultArtifact, sdncUserDetails);
+ status = response.getErrorCode();
+ AssertJUnit.assertEquals("failed to remove artifact: " + response.getErrorCode(), 200, status);
+
+ }
+
+ // --------------------
+ @Test
+ public void addArtifactToResourcse_AlreadyExistsTest() throws Exception {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ String jsonBody = createLoadArtifactBody();
+
+ String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId());
+ HttpPost httppost = createPostAddArtifactRequeast(jsonBody, url, true);
+ CloseableHttpResponse response = httpclient.execute(httppost);
+ int status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertTrue("failed to add artifact", status == 200);
+
+ String artifactId = getArtifactUid(response);
+
+ httppost = createPostAddArtifactRequeast(jsonBody, url, true);
+ response = httpclient.execute(httppost);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("the returned status code is in correct", status, 400);
+
+ url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), artifactId);
+ HttpDelete httpDelete = createDeleteArtifactRequest(url);
+ response = httpclient.execute(httpDelete);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertTrue("failed to remove artifact", status == 200);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void addArtifactToResourcse_MissingContentTest() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ Map<String, Object> json = new HashMap<String, Object>();
+ json.put("description", "desc");
+ json.put("payloadData", "UEsDBAoAAAAIAAeLb0bDQz");
+ json.put("Content-MD5", "YTg2Mjg4MWJhNmI5NzBiNzdDFkMWI=");
+
+ String jsonBody = gson.toJson(json);
+
+ String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId());
+ HttpPost httppost = createPostAddArtifactRequeast(jsonBody, url, true);
+ CloseableHttpResponse response = httpclient.execute(httppost);
+ int status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("the returned status code is in correct", status, 400);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void addArtifactToResourcse_MissingMd5Test() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ HashMap<String, Object> json = new HashMap<String, Object>();
+ json.put("artifactName", "install_apache.sh");
+ json.put("artifactType", "SHELL");
+ json.put("description", "kjglkh");
+ json.put("payloadData", "UEsDBYTEIWUYIFHWFMABCNAoAAAAIAAeLb0bDQz");
+ json.put("artifactLabel", "name123");
+ String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId());
+ String jsonBody = gson.toJson(json);
+ HttpPost httppost = createPostAddArtifactRequeast(jsonBody, url, false);
+ CloseableHttpResponse response = httpclient.execute(httppost);
+ int status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertTrue("failed to update artifact metatdata", status == 400);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void deleteArtifact_NotExistsTest() throws Exception {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), "someFakeId");
+ HttpDelete httpDelete = createDeleteArtifactRequest(url);
+ CloseableHttpResponse response = httpclient.execute(httpDelete);
+ int status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("the returned status code is in correct", status, 404);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void createAndRemoveArtifactToInterface() throws Exception {
+ CloseableHttpResponse response;
+ int status;
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ try {
+ // upload artifact to interface
+ String interfaceName = "Standard";
+ String operationName = "configure";
+
+ String userBodyJson = createUploadArtifactBodyJson();
+ String url = String.format(Urls.UPLOAD_ARTIFACT_BY_INTERFACE_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), interfaceName, operationName);
+
+ HttpPost httpPost = createPostAddArtifactRequeast(userBodyJson, url, true);
+ response = httpclient.execute(httpPost);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, status, 200);
+
+ // get artifact uniqueId
+ String artifactId = getLifecycleArtifactUid(response);
+
+ // delete artifact
+ url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_BY_INTERFACE_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetailsVFCcomp.getUniqueId(), interfaceName, operationName,
+ artifactId);
+ HttpDelete httpDelete = createDeleteArtifactRequest(url);
+
+ response = httpclient.execute(httpDelete);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, status, 200);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void addArtifactToServiceTest() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ try {
+ String jsonStr = createLoadArtifactBody();
+
+ String url = String.format(Urls.ADD_ARTIFACT_TO_SERVICE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), defaultService1.getUniqueId());
+ HttpPost httpPost = createPostAddArtifactRequeast(jsonStr, url, true);
+ CloseableHttpResponse result = httpclient.execute(httpPost);
+ int status = result.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, 200, status);
+
+ String artifactId = getArtifactUid(result);
+
+ url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_SERVICE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), defaultService1.getUniqueId(), artifactId);
+ HttpDelete httpDelete = createDeleteArtifactRequest(url);
+
+ result = httpclient.execute(httpDelete);
+ status = result.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 200, returned :" + status, 200, status);
+ } finally {
+ RestResponse response = ServiceRestUtils.deleteService(defaultService1.getName(), serviceVersion,
+ sdncUserDetails);
+ checkDeleteResponse(response);
+ httpclient.close();
+ }
+ }
+
+ @Test
+ public void addArtifactNotSupportedTypeToServiceTest() throws Exception {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ Map<String, Object> json = new HashMap<String, Object>();
+ json.put("artifactName", "install_apache.sh");
+ json.put("artifactType", "SHELL11");
+ json.put("description", "fff");
+ json.put("payloadData", "UEsDBAoAAAAIAAeLb0bDQz");
+ json.put("artifactLabel", "name123");
+
+ String jsonStr = gson.toJson(json);
+
+ String url = String.format(Urls.ADD_ARTIFACT_TO_SERVICE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), defaultService1.getUniqueId());
+
+ HttpPost httpPost = createPostAddArtifactRequeast(jsonStr, url, true);
+ CloseableHttpResponse result = httpclient.execute(httpPost);
+ int status = result.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("response code is not 400, returned :" + status, 400, status);
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name());
+
+ String responseString = EntityUtils.toString(result.getEntity());
+
+ JSONObject map = (JSONObject) jsonParser.parse(responseString);
+ JSONObject requestError = (JSONObject) map.get("requestError");
+ JSONObject serviceException = (JSONObject) requestError.get("serviceException");
+
+ String msgId = (String) serviceException.get("messageId");
+ AssertJUnit.assertEquals("message id did not match expacted", errorInfo.getMessageId(), msgId);
+
+ String text = (String) serviceException.get("text");
+ AssertJUnit.assertEquals("text did not match expacted", errorInfo.getMessage(), text);
+
+ JSONArray variables = (JSONArray) serviceException.get("variables");
+ String type = (String) variables.get(0);
+ AssertJUnit.assertEquals("variable did not match expacted", "SHELL11", type);
+ } finally {
+ RestResponse response = ServiceRestUtils.deleteService(defaultService1.getName(), serviceVersion,
+ sdncUserDetails);
+ checkDeleteResponse(response);
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void addArtifactToResourceTest() throws Exception {
+
+ ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultArtifact();
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetailsVFCcomp.getUniqueId());
+ int status = response.getErrorCode();
+ AssertJUnit.assertEquals("add informational artifact request returned status: " + response.getErrorCode(), 200,
+ status);
+
+ RestResponse resourceResp = ResourceRestUtils.getResource(resourceDetailsVFCcomp.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(resourceResp.getResponse());
+ AssertJUnit.assertNotNull(resource);
+
+ Map<String, ArtifactDefinition> artifacts = resource.getArtifacts();
+ boolean isExist = false;
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ if (entry.getKey().equals(defaultArtifact.getArtifactLabel())) {
+ isExist = true;
+
+ }
+ }
+ AssertJUnit.assertTrue(isExist);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/CrudArt.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/CrudArt.java
new file mode 100644
index 0000000000..b3c3f3f5b6
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/CrudArt.java
@@ -0,0 +1,1750 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ArtifactValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.BaseValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class CrudArt extends ComponentBaseTest {
+
+ private static Logger logger = LoggerFactory.getLogger(CrudArt.class.getName());
+ private static final User sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ private static final String HEAT_NET_LABEL = "heatnet";
+ private static final String HEAT_LABEL = "heat";
+
+ protected String testResourcesPath;
+ protected String testResourcesInstancesPath;
+
+ protected static final String dcaeInventoryToscaFile = "toscaSampleArtifact.yml";
+ protected static final String dcaeInventoryJsonFile = "jsonSampleArtifact.json";
+ protected static final String dcaeInventoryPolicyFile = "emfSampleArtifact.emf";
+ protected static final String dcaeInventoryDocFile = "docSampleArtifact.doc";
+ protected static final String dcaeInventoryBlueprintFile = "bluePrintSampleArtifact.xml";
+ protected static final String dcaeInventoryEventFile = "eventSampleArtifact.xml";
+
+ protected static final String heatSuccessFile = "asc_heat 0 2.yaml";
+ protected static final String heatNetSuccessFile = "asc_heat_net 0 2.yaml";
+ protected static final String yangFile = "addYangXmlArtifactToResource.xml";
+ protected static final String jsonFile = "jsonArtifact.json";
+ protected static final String invalidJsonFile = "invalidJson.json";
+ protected static final String invalidYangFile = "invalidYangXml.xml";
+ protected static final String otherFile = "other.txt";
+ protected static final String muranoFile = "asc_heat 0 2.zip";
+ protected static final String heatSuccessMiniFile = "heat_mini.yaml";
+ protected static final String heatInvalidFormat = "heatInvalidFormat.yaml";
+ protected static final String yamlInvalidFormat = "invalidYamlFormat.yaml";
+ protected static final String heatEnvfile = "heatEnvfile.env";
+
+ protected ServiceReqDetails serviceDetails;
+ protected ResourceReqDetails vfResourceDetails;
+ protected ResourceReqDetails cpResourceDetails;
+ protected ResourceReqDetails vfcResourceDetails;
+ protected ResourceReqDetails vlResourceDetails;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CrudArt() {
+ super(name, CrudArt.class.getName());
+ }
+
+ @DataProvider
+ private static final Object[][] getDepArtByType() throws IOException, Exception {
+ return new Object[][] { { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType()) }, { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_VOL.getType()) },
+ { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_NET.getType()) }, { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.DCAE_INVENTORY_TOSCA.getType()) },
+ { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.DCAE_INVENTORY_JSON.getType()) }, { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.DCAE_INVENTORY_POLICY.getType()) },
+ { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.DCAE_INVENTORY_DOC.getType()) }, { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.DCAE_INVENTORY_BLUEPRINT.getType()) },
+ { ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.DCAE_INVENTORY_EVENT.getType()) } };
+ }
+
+ @DataProvider
+ private static final Object[][] getServiceDepArtByType() throws IOException, Exception {
+ return new Object[][] { { ArtifactTypeEnum.OTHER.getType() }, { ArtifactTypeEnum.YANG_XML.getType() }, };
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+ // Set files working directory
+ String sourceDir = config.getResourceConfigDir();
+ String workDir = "HeatDeploymentArtifacts";
+ testResourcesPath = sourceDir + File.separator + workDir;
+ String workDirResourceInstanceArtifacts = "ResourceInstanceArtifacts";
+ testResourcesInstancesPath = sourceDir + File.separator + workDirResourceInstanceArtifacts;
+
+ // Build the components
+ Service serviceObj = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ serviceDetails = new ServiceReqDetails(serviceObj);
+
+ Resource vfcResourceObj = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ vfcResourceDetails = new ResourceReqDetails(vfcResourceObj);
+
+ Resource vfResourceObj = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ vfResourceDetails = new ResourceReqDetails(vfResourceObj);
+
+ Resource cpResourceObj = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.CP, UserRoleEnum.DESIGNER, true).left().value();
+ cpResourceDetails = new ResourceReqDetails(cpResourceObj);
+
+ Resource vlResourceObj = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VL, UserRoleEnum.DESIGNER, true).left().value();
+ vlResourceDetails = new ResourceReqDetails(vlResourceObj);
+ }
+
+ // ---------------------------------Resource
+ // success--------------------------------
+ @Test
+ public void addHeatArtifactToResourceAndCertify() throws Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // certified resource
+ RestResponse changeResourceState = LifecycleRestUtils.certifyResource(vfResourceDetails);
+ int status = changeResourceState.getErrorCode();
+ assertEquals("certify resource request returned status:" + status, BaseRestUtils.STATUS_CODE_SUCCESS, status);
+
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(changeResourceState.getResponse());
+ Map<String, ArtifactDefinition> artifactsMap = resourceJavaObject.getDeploymentArtifacts();
+ boolean flag = false;
+ if (artifactsMap != null) {
+ for (Entry<String, ArtifactDefinition> art : artifactsMap.entrySet()) {
+ if (art.getValue().getArtifactName().equals(heatArtifactDetails.getArtifactName())) {
+ assertTrue("expected artifact type is " + ArtifactGroupTypeEnum.DEPLOYMENT.getType() + " but was " + art.getValue().getArtifactGroupType(), art.getValue().getArtifactGroupType().equals(ArtifactGroupTypeEnum.DEPLOYMENT));
+ flag = true;
+ break;
+ }
+ }
+ assertTrue("expected artifact not found", flag == true);
+ }
+
+ }
+
+ // ---------------------------------Resource
+ // success--------------------------------
+ @Test
+ public void addDcaeInventoryToscaArtifactToResourceInstanceAndCertify() throws Exception {
+ String artifactFileName = dcaeInventoryToscaFile;
+ String artifactName = dcaeInventoryToscaFile;
+ String artifactLabel = "dcae inv tosca label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_TOSCA;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addDcaeInventoryJsonArtifactToResourceInstanceAndCertify() throws Exception {
+ String artifactFileName = dcaeInventoryJsonFile;
+ String artifactName = dcaeInventoryJsonFile;
+ String artifactLabel = "dcae inv json label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_JSON;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addDcaeInventoryPolicyArtifactToResourceInstanceAndCertify() throws Exception {
+ String artifactFileName = dcaeInventoryPolicyFile;
+ String artifactName = dcaeInventoryPolicyFile;
+ String artifactLabel = "dcae inv policy label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_POLICY;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addDcaeInventoryDocArtifactToResourceInstanceAndCertify() throws Exception {
+ String artifactFileName = dcaeInventoryDocFile;
+ String artifactName = dcaeInventoryDocFile;
+ String artifactLabel = "dcae inv doc label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_DOC;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addDcaeInventoryBluePrintArtifactToResourceInstanceAndCertify() throws Exception {
+ String artifactFileName = dcaeInventoryBlueprintFile;
+ String artifactName = dcaeInventoryBlueprintFile;
+ String artifactLabel = "dcae inv blueprint label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_BLUEPRINT;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addDcaeInventoryEventArtifactToResourceInstanceAndCertify() throws Exception {
+ String artifactFileName = dcaeInventoryEventFile;
+ String artifactName = dcaeInventoryEventFile;
+ String artifactLabel = "dcae inv event label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_EVENT;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ private RestResponse addArtifactToResourceInstanceAndCertify(String artifactFileName, String artifactName, String artifactLabel, ArtifactTypeEnum artifactType) throws Exception {
+
+ // Get the resource
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+
+ // Certify VF
+ Pair<Component, RestResponse> changeComponentState = AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + changeComponentState.getRight().getErrorCode(), changeComponentState.getRight().getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Add VF instance to service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource, service, UserRoleEnum.DESIGNER, true);
+
+ // Get the VF instance
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ComponentInstance VfInstance = service.getComponentInstances().get(0);
+
+ // Create the artifact
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstance(artifactFileName, artifactName, artifactLabel, artifactType, VfInstance, serviceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addArtifactToResourceInstanceResponse.getResponseMessage());
+ return addArtifactToResourceInstanceResponse;
+ }
+
+ @Test
+ public void updateArtifactDescriptionToResourceInstance() throws Exception {
+ String artifactFileName = dcaeInventoryToscaFile;
+ String artifactName = dcaeInventoryToscaFile;
+ String artifactLabel = "dcae inv tosca label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_TOSCA;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ logger.debug("addInformationalArtifactToResource response: {}", addArtifactToResourceInstanceResponse.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Get the artifact from VF instance and change his description.
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ComponentInstance vfInstance = service.getComponentInstances().get(0);
+ Map<String, ArtifactDefinition> deploymentArtifacts = vfInstance.getDeploymentArtifacts();
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get("dcaeinvtoscalabel");
+ artifactDefinition.setDescription("My new description");
+
+ // Update the artifact
+ RestResponse updateDeploymentArtifactToRI = ArtifactRestUtils.updateArtifactToResourceInstance(artifactDefinition, sdncDesignerDetails1, vfInstance.getUniqueId(), service.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", updateDeploymentArtifactToRI.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + updateDeploymentArtifactToRI.getErrorCode(), updateDeploymentArtifactToRI.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void deleteArtifactToResourceInstance() throws Exception {
+ String artifactFileName = dcaeInventoryToscaFile;
+ String artifactName = dcaeInventoryToscaFile;
+ String artifactLabel = "dcae inv tosca label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.DCAE_INVENTORY_TOSCA;
+ RestResponse addArtifactToResourceInstanceResponse = addArtifactToResourceInstanceAndCertify(artifactFileName, artifactName, artifactLabel, artifactType);
+ logger.debug("addInformationalArtifactToResource response: {}", addArtifactToResourceInstanceResponse.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addArtifactToResourceInstanceResponse.getErrorCode(), addArtifactToResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Get the artifact from VF instance and change his description.
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ComponentInstance vfInstance = service.getComponentInstances().get(0);
+ Map<String, ArtifactDefinition> deploymentArtifacts = vfInstance.getDeploymentArtifacts();
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get("dcaeinvtoscalabel");
+
+ // Delete the artifact
+ RestResponse deleteInformationalArtifactFromResource = ArtifactRestUtils.deleteArtifactFromResourceInstance(artifactDefinition, sdncDesignerDetails1, vfInstance.getUniqueId(), service.getUniqueId());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + deleteInformationalArtifactFromResource.getErrorCode(), deleteInformationalArtifactFromResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addHeatArtifactToResource() throws Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ }
+
+ @Test
+ public void addHeatAndHeatNetArtifactsToResource() throws Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+
+ // Add HEAT
+ logger.debug("listFileName: {}", listFileName.toString());
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Add HEAT_NET
+ String payloadNet = FileUtils.loadPayloadFile(listFileName, heatNetSuccessFile, true);
+ ArtifactReqDetails heatNetArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_NET.getType());
+ heatNetArtifactDetails.setPayload(payloadNet);
+ heatNetArtifactDetails.setArtifactLabel(HEAT_NET_LABEL);
+
+ RestResponse addInformationalArtifactToResource1 = ArtifactRestUtils.uploadArtifactToPlaceholderOnResource(heatNetArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId(), HEAT_NET_LABEL);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource1.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource1.getErrorCode(), addInformationalArtifactToResource1.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(vfResourceDetails, sdncDesignerDetails1);
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get(HEAT_LABEL);
+ assertNotNull(artifactDefinition);
+ String heatEsId = artifactDefinition.getEsId();
+ assertNotNull(heatEsId);
+
+ ArtifactDefinition artifactDefinitionNet = deploymentArtifacts.get(HEAT_NET_LABEL);
+ assertNotNull(artifactDefinitionNet);
+ String heatNetEsId = artifactDefinitionNet.getEsId();
+ assertNotNull(heatNetEsId);
+ assertFalse(heatEsId.equalsIgnoreCase(heatNetEsId));
+ }
+
+ @Test
+ public void addDeleteAddHeatArtifactToResource() throws Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ RestResponse deleteInformationalArtifactFromResource = ArtifactRestUtils.deleteInformationalArtifactFromResource(vfResourceDetails.getUniqueId(), heatArtifactDetails, sdncDesignerDetails1);
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + deleteInformationalArtifactFromResource.getErrorCode(), deleteInformationalArtifactFromResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ @Test
+ public void addYangXmlArtifactToResource() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "asc_heat 0 2.XML";
+ String artifactLabel = "Label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.YANG_XML;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addOtherTypeDeploymentArtifactToResource() throws Exception {
+
+ String fileName = otherFile;
+ String artifactName = "other.txt";
+ String artifactLabel = "Label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.OTHER;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addYangXmlArtifactSameName() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "asc_heat_0_2.XML";
+ String artifactLabel = "Label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.YANG_XML;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // Changing label but not name
+ artifactLabel = "Label1";
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is not 400, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == 400);
+ BaseValidationUtils.checkErrorResponse(addInformationalArtifactToResource, ActionStatus.DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS, new String[] { "Resource", vfResourceDetails.getName(), artifactName });
+
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addInvalidYangXmlFormat() throws Exception {
+
+ String fileName = invalidYangFile;
+ String artifactName = "asc_heat_0_2.XML";
+ String artifactLabel = "Label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.YANG_XML;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is not 400, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == 400);
+ BaseValidationUtils.checkErrorResponse(addInformationalArtifactToResource, ActionStatus.INVALID_XML, new String[] { "YANG_XML" });
+
+ }
+
+ @Test
+ public void addSeveralYangXmlArtifacts() throws Exception {
+
+ // Adding 4 artifacts
+ String fileName = yangFile;
+ String artifactName = "asc_heat_0_2.XML";
+ String artifactLabel = "Label";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.YANG_XML;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // Changing label and name
+ artifactLabel = "Label1";
+ artifactName = "asc_heat_0_3.XML";
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Changing label and name
+ artifactLabel = "Label2";
+ artifactName = "asc_heat_0_4.XML";
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+
+ // Changing label and name
+ artifactLabel = "Label3";
+ artifactName = "asc_heat_0_5.XML";
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType);
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 4);
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void updateHeatArtifactToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ heatTypeArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // update
+ heatTypeArtifactDetails.setArtifactName("UPDATE.yaml");
+ heatTypeArtifactDetails.setPayloadData(null);
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", updateInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + updateInformationalArtifactToResource.getErrorCode(), updateInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void updateHeatArtifactTimeOutToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ Resource resource = getResourceByResDetails(vfResourceDetails, sdncDesignerDetails1);
+ int actualTimeout = resource.getDeploymentArtifacts().get(heatTypeArtifactDetails.getArtifactLabel().toLowerCase()).getTimeout();
+ assertTrue("verify " + heatTypeArtifactDetails.getArtifactLabel().toLowerCase() + " artifact timout, expected " + heatTypeArtifactDetails.getTimeout() + ", but was " + actualTimeout, heatTypeArtifactDetails.getTimeout() == actualTimeout);
+
+ // update
+ heatTypeArtifactDetails.setTimeout(35);
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", updateInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + updateInformationalArtifactToResource.getErrorCode(), updateInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ resource = getResourceByResDetails(vfResourceDetails, sdncDesignerDetails1);
+ actualTimeout = resource.getDeploymentArtifacts().get(heatTypeArtifactDetails.getArtifactLabel().toLowerCase()).getTimeout();
+ assertTrue("verify " + heatTypeArtifactDetails.getArtifactLabel().toLowerCase() + " artifact timout, expected " + heatTypeArtifactDetails.getTimeout() + ", but was " + actualTimeout, heatTypeArtifactDetails.getTimeout() == actualTimeout);
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void updateHeatArtifactDescriptionToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ Resource resource = getResourceByResDetails(vfResourceDetails, sdncDesignerDetails1);
+ String actualDescription = resource.getDeploymentArtifacts().get(heatTypeArtifactDetails.getArtifactLabel().toLowerCase()).getDescription();
+ assertTrue("verify " + heatTypeArtifactDetails.getArtifactLabel().toLowerCase() + " artifact Description, expected " + heatTypeArtifactDetails.getDescription() + ", but was " + actualDescription, heatTypeArtifactDetails.getDescription().equals(actualDescription));
+
+ // update
+ heatTypeArtifactDetails.setDescription("the best description was ever");
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", updateInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + updateInformationalArtifactToResource.getErrorCode(), updateInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ resource = getResourceByResDetails(vfResourceDetails, sdncDesignerDetails1);
+ actualDescription = resource.getDeploymentArtifacts().get(heatTypeArtifactDetails.getArtifactLabel().toLowerCase()).getDescription();
+ assertTrue("verify " + heatTypeArtifactDetails.getArtifactLabel().toLowerCase() + " artifact Description, expected " + heatTypeArtifactDetails.getDescription() + ", but was " + actualDescription, heatTypeArtifactDetails.getDescription().equals(actualDescription));
+ }
+
+ private Resource getResourceByResDetails(ResourceReqDetails resDetails, User userDetails) throws IOException {
+ RestResponse response = ResourceRestUtils.getResource(resDetails, userDetails);
+ assertTrue("response code on get resource not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + response.getErrorCode(), response.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ return resource;
+ }
+
+ // ---------------------------------Service
+ // success--------------------------------
+ @Test()
+ public void addAllTypesDepArtifactToService() throws Exception, Exception {
+
+ // String fileName = heatSuccessFile;
+ // List<String> listFileName =
+ // FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ // logger.debug("listFileName: {}", listFileName.toString());
+
+ // String payload = FileUtils.loadPayloadFile(listFileName, fileName,
+ // true);
+ ArtifactReqDetails otherArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.OTHER.getType());
+ // otherArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(otherArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ ArtifactReqDetails yangXmlArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.YANG_XML.getType());
+
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(yangXmlArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ }
+
+ @Test(enabled = false)
+ public void addMuranoPkgArtifactToService() throws Exception, Exception {
+
+ String fileName = muranoFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.MURANO_PKG.getType());
+ heatArtifactDetails.setPayload(payload);
+ heatArtifactDetails.setArtifactName("asc_heat 0 2.zip");
+ heatArtifactDetails.setArtifactLabel("Label");
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ }
+
+ @Test(dataProvider = "getServiceDepArtByType")
+ public void addHeatArtifactToServiceAndCertify(String artType) throws Exception, Exception {
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(artType);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // certified service
+ RestResponse changeServiceState = LifecycleRestUtils.certifyService(serviceDetails);
+ int status = changeServiceState.getErrorCode();
+ assertEquals("certify service request returned status:" + status, BaseRestUtils.STATUS_CODE_SUCCESS, status);
+
+ Service resourceJavaObject = ResponseParser.convertServiceResponseToJavaObject(changeServiceState.getResponse());
+ Map<String, ArtifactDefinition> artifactsMap = resourceJavaObject.getDeploymentArtifacts();
+ boolean flag = false;
+ if (artifactsMap != null) {
+ for (Entry<String, ArtifactDefinition> art : artifactsMap.entrySet()) {
+ if (art.getValue().getArtifactName().equals(heatArtifactDetails.getArtifactName())) {
+ assertTrue("expected artifact type is " + ArtifactGroupTypeEnum.DEPLOYMENT.getType() + " but was " + art.getValue().getArtifactGroupType(), art.getValue().getArtifactGroupType().equals(ArtifactGroupTypeEnum.DEPLOYMENT));
+ flag = true;
+ break;
+ }
+ }
+ assertTrue("expected artifact not found", flag == true);
+ }
+
+ }
+
+ @Test(enabled = false, dataProvider = "getServiceDepArtByType")
+ public void updateHeatArtifactToService(String artType) throws Exception, Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(artType);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // update
+ heatArtifactDetails.setPayloadData(payload);
+ RestResponse updateInformationalArtifactToService = ArtifactRestUtils.updateInformationalArtifactOfServiceByMethod(heatArtifactDetails, serviceDetails.getUniqueId(), sdncDesignerDetails1, "POST");
+ logger.debug("updateInformationalArtifactToService response: {}", updateInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + updateInformationalArtifactToService.getErrorCode(), updateInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ }
+
+ // --------------------------------------Resource Negative
+ // Tests-------------------------------------
+
+ // TODO Andrey the method of DEPLOYMENT artifact is update and not add
+ @Test(dataProvider = "getServiceDepArtByType")
+ public void addTheSameAdditionalHeatArtifactToResource(String artType) throws Exception, Exception {
+
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(artType);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // add the same artifact one more time
+ artifactDetails.setArtifactLabel("the second artifact");
+ addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", vfResourceDetails.getName(), artifactDetails.getArtifactName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ }
+
+ @Test
+ public void addHeatArtifactTwiceSameNameToResource() throws Exception, Exception {
+
+ String filename1 = heatSuccessFile;
+ // String filename2 = heatSuccessMiniFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, filename1, true);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Add HEAT_NET
+ String payloadNet = FileUtils.loadPayloadFile(listFileName, heatNetSuccessFile, true);
+ ArtifactReqDetails heatNetArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_NET.getType());
+ heatNetArtifactDetails.setPayload(payloadNet);
+ heatNetArtifactDetails.setArtifactLabel(HEAT_NET_LABEL);
+ heatNetArtifactDetails.setArtifactName(heatArtifactDetails.getArtifactName());
+
+ RestResponse addInformationalArtifactToResource1 = ArtifactRestUtils.uploadArtifactToPlaceholderOnResource(heatNetArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId(), HEAT_NET_LABEL);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource1.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource1.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", vfResourceDetails.getName(), heatNetArtifactDetails.getArtifactName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS.name(), variables, addInformationalArtifactToResource1.getResponse());
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void addHeatArtifactTwiceToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String filename1 = heatSuccessFile;
+ String filename2 = heatSuccessMiniFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, filename1, true);
+ heatTypeArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // add the second artifact
+ payload = FileUtils.loadPayloadFile(listFileName, heatSuccessMiniFile, true);
+ heatTypeArtifactDetails.setPayload(payload);
+ heatTypeArtifactDetails.setArtifactName(filename2);
+ heatTypeArtifactDetails.setArtifactLabel("the second artifact");
+
+ addInformationalArtifactToResource = ArtifactRestUtils.explicitAddInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", vfResourceDetails.getName(), heatTypeArtifactDetails.getArtifactType(), heatTypeArtifactDetails.getArtifactType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void addHeatArtifactInvalidHeatFormatToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String fileName = heatInvalidFormat;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ heatTypeArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList(heatTypeArtifactDetails.getArtifactType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void addHeatArtifactInvalidYamlFormatToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String fileName = yamlInvalidFormat;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ heatTypeArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_YAML.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList(heatTypeArtifactDetails.getArtifactType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_YAML.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void addHeatArtifactInvalidFileExtensionToResource(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String fileName = yangFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+
+ heatTypeArtifactDetails.setPayload(payload);
+ heatTypeArtifactDetails.setArtifactName(fileName);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList(heatTypeArtifactDetails.getArtifactType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void addHeatArtifactToResourceCertifyAndAddAdditionalHeatArtifact(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ heatTypeArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // certified resource
+ RestResponse changeResourceState = LifecycleRestUtils.certifyResource(vfResourceDetails);
+ int status = changeResourceState.getErrorCode();
+ assertEquals("certify resource request returned status:" + status, BaseRestUtils.STATUS_CODE_SUCCESS, status);
+
+ // add second HEAT artifact to the certified resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(vfResourceDetails, sdncDesignerDetails1, LifeCycleStatesEnum.CHECKOUT);
+ assertTrue("expected code response on change resource state to CHECKOUT BaseRestUtils.STATUS_CODE_SUCCESS, but was " + changeResourceState.getErrorCode(), changeResourceState.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // ArtifactReqDetails heatArtifactDetails1 =
+ // ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatTypeArtifactDetails.setPayload(payload);
+ heatTypeArtifactDetails.setArtifactName(fileName);
+ heatTypeArtifactDetails.setArtifactLabel("the second artifact");
+
+ addInformationalArtifactToResource = ArtifactRestUtils.explicitAddInformationalArtifactToResource(heatTypeArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", vfResourceDetails.getName(), heatTypeArtifactDetails.getArtifactType(), heatTypeArtifactDetails.getArtifactType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ }
+
+ // -----------------Service Negative
+ // Tests--------------------------------------------------------
+
+ // Absolute
+ @Test(enabled = false)
+ public void addHeatArtifactTwiceToService() throws Exception, Exception {
+
+ String fileName1 = heatSuccessFile;
+ String fileName2 = heatSuccessMiniFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName1, true);
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.OTHER.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // add the second artifact
+ payload = FileUtils.loadPayloadFile(listFileName, fileName2, true);
+
+ ArtifactReqDetails heatArtifactDetails1 = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.OTHER.getType());
+ heatArtifactDetails1.setPayload(payload);
+ heatArtifactDetails1.setArtifactName(fileName2);
+ heatArtifactDetails1.setArtifactLabel("the second artifact");
+
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails1, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToService.getErrorCode());
+
+ List<String> variables = Arrays.asList("Service", serviceDetails.getName(), ArtifactTypeEnum.OTHER.getType(), ArtifactTypeEnum.OTHER.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS.name(), variables, addInformationalArtifactToService.getResponse());
+
+ }
+
+ // TODO Andrey Obsolete
+ @Test(enabled = false)
+ public void addHeatArtifactInvalidHeatFormatToService() throws Exception, Exception {
+
+ String fileName = heatInvalidFormat;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToService.getErrorCode());
+
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.HEAT.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT.name(), variables, addInformationalArtifactToService.getResponse());
+
+ }
+
+ // TODO Andrey Obsolete
+ @Test(enabled = false)
+ public void addHeatArtifactInvalidYamlFormatToService() throws Exception, Exception {
+
+ String fileName = yamlInvalidFormat;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_YAML.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToService.getErrorCode());
+
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.HEAT.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_YAML.name(), variables, addInformationalArtifactToService.getResponse());
+
+ }
+
+ @Test
+ public void addHeatArtifactInvalidFileExtensionToService() throws Exception, Exception {
+
+ String fileName = muranoFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.YANG_XML.getType());
+ heatArtifactDetails.setPayload(payload);
+ heatArtifactDetails.setArtifactName(fileName);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ logger.debug("addInformationalArtifactToService response: {}", addInformationalArtifactToService.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToService.getErrorCode());
+
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.YANG_XML.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name(), variables, addInformationalArtifactToService.getResponse());
+
+ }
+
+ @Test(dataProvider = "getDepArtByType")
+ public void addHeatEnvArtifactToResourceNotSupportedType(ArtifactReqDetails heatTypeArtifactDetails) throws Exception, Exception {
+
+ String fileName = heatEnvfile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_ENV.getType());
+ heatArtifactDetails.setPayload(payload);
+ heatArtifactDetails.setArtifactName("asc_heat 0 2.env");
+ heatArtifactDetails.setArtifactLabel(heatTypeArtifactDetails.getArtifactLabel());
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToResource.getErrorCode());
+
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.HEAT_ENV.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name(), variables, addInformationalArtifactToResource.getResponse());
+ }
+
+ // TODO Andrey
+ @Test
+ public void addHeatArtifactToServiceNotSupportDeploymentArt() throws Exception, Exception {
+
+ String fileName = heatSuccessFile;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, fileName, true);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(payload);
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(heatArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), addInformationalArtifactToService.getErrorCode());
+
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.HEAT.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name(), variables, addInformationalArtifactToService.getResponse());
+
+ }
+
+ protected RestResponse addArtifactToResourceInstance(String artifactFileName, String artifactName, String artifactLabel, ArtifactTypeEnum artifactType, ComponentInstance componentInstance, ServiceReqDetails serviceDetails) throws Exception {
+ ArtifactReqDetails dcaeArtifactDetails = buildArtifactReqDetailsObject(testResourcesInstancesPath, artifactFileName, artifactName, artifactLabel, artifactType);
+ RestResponse addArtifactToResourceInstance = ArtifactRestUtils.addArtifactToResourceInstance(dcaeArtifactDetails, sdncDesignerDetails1, componentInstance.getUniqueId(), serviceDetails.getUniqueId());
+ return addArtifactToResourceInstance;
+ }
+
+ protected RestResponse addDeploymentArtifactToResource(String artifactFileName, String artifactName, String artifactLabel, ArtifactTypeEnum artifactType) throws Exception {
+ ArtifactReqDetails heatArtifactDetails = buildArtifactReqDetailsObject(testResourcesPath, artifactFileName, artifactName, artifactLabel, artifactType);
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, vfResourceDetails.getUniqueId());
+ return addInformationalArtifactToResource;
+ }
+
+ protected RestResponse addDeploymentArtifactToResource(String artifactFileName, String artifactName, String artifactLabel, ArtifactTypeEnum artifactType, ResourceReqDetails resource) throws Exception {
+ ArtifactReqDetails heatArtifactDetails = buildArtifactReqDetailsObject(testResourcesPath, artifactFileName, artifactName, artifactLabel, artifactType);
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, resource.getUniqueId());
+ return addInformationalArtifactToResource;
+ }
+
+ // US672293 - Support new artifact type : BEVF_LICENSE , VENDOR_LICENSE
+ @Test
+ public void addNewArtifactsToVFResource() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "artifact1.xml";
+ String artifactLabel = "Label1";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VNF_CATALOG;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ artifactName = "artifact2.xml";
+ artifactLabel = "Label2";
+ artifactType = ArtifactTypeEnum.VF_LICENSE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact4.xml";
+ artifactLabel = "Label4";
+ artifactType = ArtifactTypeEnum.MODEL_INVENTORY_PROFILE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact5.xml";
+ artifactLabel = "Label5";
+ artifactType = ArtifactTypeEnum.MODEL_QUERY_SPEC;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact6.xml";
+ artifactLabel = "Label6";
+ artifactType = ArtifactTypeEnum.APPC_CONFIG;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ fileName = jsonFile;
+ artifactName = "artifact7.json";
+ artifactLabel = "Label7";
+ artifactType = ArtifactTypeEnum.APPC_CONFIG;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 2);
+
+ }
+
+ @Test
+ public void addNewArtifactsToVFCResource() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "artifact1.xml";
+ String artifactLabel = "Label1";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VNF_CATALOG;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact2.xml";
+ artifactLabel = "Label2";
+ artifactType = ArtifactTypeEnum.VF_LICENSE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact4.xml";
+ artifactLabel = "Label4";
+ artifactType = ArtifactTypeEnum.MODEL_INVENTORY_PROFILE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifac5.xml";
+ artifactLabel = "Label5";
+ artifactType = ArtifactTypeEnum.MODEL_QUERY_SPEC;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addNewArtifactsToVfc() throws Exception {
+ String fileName = yangFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VF_LICENSE;
+ RestResponse addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addNewArtifactsToCp() throws Exception {
+ String fileName = yangFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VF_LICENSE;
+ RestResponse addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, cpResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(cpResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, cpResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(cpResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addNewArtifactsToVl() throws Exception {
+ String fileName = yangFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VF_LICENSE;
+ RestResponse addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vlResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vlResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vlResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vlResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addVfInstanceWithNewArtifactsToService() throws Exception {
+ String fileName = yangFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VF_LICENSE;
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ // Certify VF
+ Pair<Component, RestResponse> changeComponentState = AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + changeComponentState.getRight().getErrorCode(), changeComponentState.getRight().getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // Add VF instance to service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource, service, UserRoleEnum.DESIGNER, true);
+ // get service and verify VF instance contain the Artifacts :VF_LICENSE
+ // and VENDOR_LICENSE
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ComponentInstance VfInstance = service.getComponentInstances().get(0);
+ ArtifactValidationUtils.validateArtifactsNumberInComponentInstance(VfInstance, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.VENDOR_LICENSE, 1);
+ ArtifactValidationUtils.validateArtifactsNumberInComponentInstance(VfInstance, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.VF_LICENSE, 1);
+ }
+
+ @Test
+ public void addNotSupportedArtifactsTypeToService01() throws Exception, Exception {
+ // Artifact type : VF_LICENSE
+ ArtifactReqDetails deploymentArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.VF_LICENSE.getType());
+ RestResponse addDeploymentArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(deploymentArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code eturned :" + addDeploymentArtifactToService.getErrorCode(), addDeploymentArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("VF_LICENSE");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name(), variables, addDeploymentArtifactToService.getResponse());
+ }
+
+ @Test
+ public void addNotSupportedArtifactsTypeToService02() throws Exception, Exception {
+ // Artifact type : VENDOR_LICENSE
+ ArtifactReqDetails deploymentArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.VENDOR_LICENSE.getType());
+ RestResponse addDeploymentArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(deploymentArtifactDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code eturned :" + addDeploymentArtifactToService.getErrorCode(), addDeploymentArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("VENDOR_LICENSE");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name(), variables, addDeploymentArtifactToService.getResponse());
+ }
+
+ @Test
+ public void addInvalidFileForArtifactTypeVendorLicenseToResource() throws Exception {
+ String fileName = yangFile;
+ String NonXmlFile = heatSuccessFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VF_LICENSE;
+ RestResponse addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ // Artifact type VENDOR_LICENSE must be XML file
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ addDeploymentArtifactToResource = addDeploymentArtifactToResource(NonXmlFile, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code 400 returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("VENDOR_LICENSE");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_XML.name(), variables, addDeploymentArtifactToResource.getResponse());
+ // get resource and verify that file not exist within
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 0);
+ }
+
+ @Test
+ public void addInvalidFileForArtifactTypeVfLicenseToResource() throws Exception {
+ String fileName = yangFile;
+ String NonXmlFile = heatSuccessFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ RestResponse addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ // Artifact type VF_LICENSE must be XML file
+ artifactName = "artifact3.xml";
+ artifactLabel = "Label3";
+ artifactType = ArtifactTypeEnum.VF_LICENSE;
+ addDeploymentArtifactToResource = addDeploymentArtifactToResource(NonXmlFile, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code 400 returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("VF_LICENSE");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_XML.name(), variables, addDeploymentArtifactToResource.getResponse());
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 0);
+ }
+
+ @Test
+ public void addVendorLicenseArtifactAlreadyExistsInResource() throws Exception {
+ String fileName = yangFile;
+ String artifactName = "artifact2.xml";
+ String artifactLabel = "Label2";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VENDOR_LICENSE;
+ RestResponse addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ // Add same file again to resource
+ addDeploymentArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addDeploymentArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 400, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(artifactLabel.toLowerCase());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_EXIST.name(), variables, addDeploymentArtifactToResource.getResponse());
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ // US672294
+
+ @Test()
+ public void addVnfCatalogArtifactsToService() throws Exception, Exception {
+
+ ArtifactReqDetails artDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.VNF_CATALOG.getType());
+ RestResponse resp = ArtifactRestUtils.addInformationalArtifactToService(artDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + resp.getErrorCode(), resp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // get service and verify the Artifacts :VNF_CATALOG
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(service, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.VNF_CATALOG, 1);
+ }
+
+ @Test()
+ public void addModelInventoryProfileArtifactsToService() throws Exception, Exception {
+
+ ArtifactReqDetails artDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.MODEL_INVENTORY_PROFILE.getType());
+ RestResponse resp = ArtifactRestUtils.addInformationalArtifactToService(artDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + resp.getErrorCode(), resp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // get service and verify the Artifacts :VNF_CATALOG
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(service, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.MODEL_INVENTORY_PROFILE, 1);
+ }
+
+ @Test()
+ public void addModelQuerySpecArtifactsToService() throws Exception, Exception {
+
+ ArtifactReqDetails artDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.MODEL_QUERY_SPEC.getType());
+ RestResponse resp = ArtifactRestUtils.addInformationalArtifactToService(artDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + resp.getErrorCode(), resp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // get service and verify the Artifacts :VNF_CATALOG
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(service, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.MODEL_QUERY_SPEC, 1);
+ }
+
+ @Test
+ public void addVfInstanceWithNewArtifactsToService02() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "artifact1.xml";
+ String artifactLabel = "Label1";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VNF_CATALOG;
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ fileName = yangFile;
+ artifactName = "artifact2.xml";
+ artifactLabel = "Label2";
+ artifactType = ArtifactTypeEnum.APPC_CONFIG;
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact4.xml";
+ artifactLabel = "Label4";
+ artifactType = ArtifactTypeEnum.MODEL_INVENTORY_PROFILE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifac5.xml";
+ artifactLabel = "Label5";
+ artifactType = ArtifactTypeEnum.MODEL_QUERY_SPEC;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ // Certify VF
+ Pair<Component, RestResponse> changeComponentState = AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + changeComponentState.getRight().getErrorCode(), changeComponentState.getRight().getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Add VF instance to service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource, service, UserRoleEnum.DESIGNER, true);
+
+ // get service and verify VF instance contain the Artifacts :VF_LICENSE
+ // and VENDOR_LICENSE
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ComponentInstance VfInstance = service.getComponentInstances().get(0);
+ ArtifactValidationUtils.validateArtifactsNumberInComponentInstance(VfInstance, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.VNF_CATALOG, 1);
+ ArtifactValidationUtils.validateArtifactsNumberInComponentInstance(VfInstance, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.MODEL_INVENTORY_PROFILE, 1);
+ ArtifactValidationUtils.validateArtifactsNumberInComponentInstance(VfInstance, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.MODEL_QUERY_SPEC, 1);
+ ArtifactValidationUtils.validateArtifactsNumberInComponentInstance(VfInstance, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.APPC_CONFIG, 1);
+ }
+
+ @Test
+ public void addAppcConfigArtifactToVfc() throws Exception {
+ String fileName = jsonFile;
+ String artifactName = "artifact7.json";
+ String artifactLabel = "Label7";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.APPC_CONFIG;
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code 400, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(artifactName);
+ variables.add("[VF]");
+ variables.add("VFC (Virtual Function Component)");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE.name(), variables, addInformationalArtifactToResource.getResponse());
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 0);
+ }
+
+ @Test
+ public void addAppcConfigArtifactToCp() throws Exception {
+ String fileName = jsonFile;
+ String artifactName = "artifact7.json";
+ String artifactLabel = "Label7";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.APPC_CONFIG;
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, cpResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code 400, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(artifactName);
+ variables.add("[VF]");
+ variables.add("CP (Connection Point)");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE.name(), variables, addInformationalArtifactToResource.getResponse());
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 0);
+ }
+
+ @Test
+ public void addAppcConfigArtifactToVl() throws Exception {
+ String fileName = jsonFile;
+ String artifactName = "artifact7.json";
+ String artifactLabel = "Label7";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.APPC_CONFIG;
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vlResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code 400, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(artifactName);
+ variables.add("[VF]");
+ variables.add("VL (Virtual Link)");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE.name(), variables, addInformationalArtifactToResource.getResponse());
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 0);
+ }
+
+ @Test()
+ public void addAppcConfigArtifactsToService() throws Exception, Exception {
+ ArtifactReqDetails artDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.APPC_CONFIG.getType());
+ RestResponse addDeploymentArtifactToResource = ArtifactRestUtils.addInformationalArtifactToService(artDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code 400, returned :" + addDeploymentArtifactToResource.getErrorCode(), addDeploymentArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(ArtifactTypeEnum.APPC_CONFIG.toString());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED.name(), variables, addDeploymentArtifactToResource.getResponse());
+ }
+
+ @Test
+ public void addAppcConfigInvalidJsonToVFResourceFailed() throws Exception {
+
+ String fileName = invalidJsonFile;
+ String artifactName = "invalidJson.json";
+ String artifactLabel = "Label7";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.APPC_CONFIG;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is 400, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(ArtifactTypeEnum.APPC_CONFIG.toString());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_JSON.name(), variables, addInformationalArtifactToResource.getResponse());
+
+ RestResponse getResource = ResourceRestUtils.getResource(vfResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 0);
+
+ }
+
+ @Test
+ public void addNewArtifactsToCp02() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "artifact1.xml";
+ String artifactLabel = "Label1";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VNF_CATALOG;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, cpResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(cpResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact4.xml";
+ artifactLabel = "Label4";
+ artifactType = ArtifactTypeEnum.MODEL_INVENTORY_PROFILE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, cpResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(cpResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifac5.xml";
+ artifactLabel = "Label5";
+ artifactType = ArtifactTypeEnum.MODEL_QUERY_SPEC;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, cpResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(cpResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addNewArtifactsToVl02() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "artifact1.xml";
+ String artifactLabel = "Label1";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VNF_CATALOG;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vlResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vlResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact4.xml";
+ artifactLabel = "Label4";
+ artifactType = ArtifactTypeEnum.MODEL_INVENTORY_PROFILE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vlResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vlResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifac5.xml";
+ artifactLabel = "Label5";
+ artifactType = ArtifactTypeEnum.MODEL_QUERY_SPEC;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vlResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vlResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addNewArtifactsToVfc02() throws Exception {
+
+ String fileName = yangFile;
+ String artifactName = "artifact1.xml";
+ String artifactLabel = "Label1";
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.VNF_CATALOG;
+
+ RestResponse addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifact4.xml";
+ artifactLabel = "Label4";
+ artifactType = ArtifactTypeEnum.MODEL_INVENTORY_PROFILE;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+
+ artifactName = "artifac5.xml";
+ artifactLabel = "Label5";
+ artifactType = ArtifactTypeEnum.MODEL_QUERY_SPEC;
+
+ addInformationalArtifactToResource = addDeploymentArtifactToResource(fileName, artifactName, artifactLabel, artifactType, vfcResourceDetails);
+ logger.debug("addInformationalArtifactToResource response: {}", addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToResource.getErrorCode(), addInformationalArtifactToResource.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ getResource = ResourceRestUtils.getResource(vfcResourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(resource, ArtifactGroupTypeEnum.DEPLOYMENT, artifactType, 1);
+ }
+
+ @Test
+ public void addNewArtifactAlreadyExistsInService() throws Exception {
+ ArtifactReqDetails artDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.MODEL_QUERY_SPEC.getType());
+ RestResponse addDeploymentArtifactoService = ArtifactRestUtils.addInformationalArtifactToService(artDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addDeploymentArtifactoService.getErrorCode(), addDeploymentArtifactoService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ // get service and verify the Artifacts :VNF_CATALOG
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(service, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.MODEL_QUERY_SPEC, 1);
+ // Add same file again to resource
+ addDeploymentArtifactoService = ArtifactRestUtils.addInformationalArtifactToService(artDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+ assertTrue("response code is 400, returned :" + addDeploymentArtifactoService.getErrorCode(), addDeploymentArtifactoService.getErrorCode() == BaseRestUtils.STATUS_CODE_INVALID_CONTENT);
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(artDetails.getArtifactLabel().toLowerCase());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.ARTIFACT_EXIST.name(), variables, addDeploymentArtifactoService.getResponse());
+ // get service and verify the Artifacts :VNF_CATALOG is still exist and
+ // has one occurrences
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails1);
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ArtifactValidationUtils.validateArtifactsNumberInComponent(service, ArtifactGroupTypeEnum.DEPLOYMENT, ArtifactTypeEnum.MODEL_QUERY_SPEC, 1);
+ }
+
+ private ArtifactReqDetails buildArtifactReqDetailsObject(String filesPath, String artifactFileName, String artifactName, String artifactLabel, ArtifactTypeEnum artifactType) throws IOException, Exception {
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(filesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, artifactFileName, true);
+
+ ArtifactReqDetails dcaeArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(artifactType.getType());
+ dcaeArtifactDetails.setPayload(payload);
+ dcaeArtifactDetails.setArtifactName(artifactName);
+ dcaeArtifactDetails.setArtifactLabel(artifactLabel);
+ return dcaeArtifactDetails;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/DownloadComponentArt.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/DownloadComponentArt.java
new file mode 100644
index 0000000000..0ae975207b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/DownloadComponentArt.java
@@ -0,0 +1,687 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ServiceValidationUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import fj.data.Either;
+
+public class DownloadComponentArt extends ComponentBaseTest {
+
+ private static Logger log = LoggerFactory.getLogger(DownloadComponentArt.class.getName());
+ protected static final String UPLOAD_ARTIFACT_PAYLOAD = "UHVUVFktVXNlci1LZXktRmlsZS0yOiBzc2gtcnNhDQpFbmNyeXB0aW9uOiBhZXMyNTYtY2JjDQpDb21tZW5wOA0K";
+ protected static final String UPLOAD_ARTIFACT_NAME = "TLV_prv.ppk";
+
+ protected Config config = Config.instance();
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";
+
+ protected Gson gson = new Gson();
+ protected JSONParser jsonParser = new JSONParser();
+
+ protected String serviceVersion;
+ protected ResourceReqDetails resourceDetails;
+ protected User sdncUserDetails;
+ protected ServiceReqDetails serviceDetails;
+
+ @BeforeMethod
+ public void init() throws Exception {
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ Resource resourceObj = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ Service serviceObj = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ resourceDetails = new ResourceReqDetails(resourceObj);
+ serviceDetails = new ServiceReqDetails(serviceObj);
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public DownloadComponentArt() {
+ super(name, DownloadComponentArt.class.getName());
+
+ }
+
+ // External API - Download artifact for resource
+ @Test
+ public void downloadArtifactFromResourceViaExternalAPI() throws Exception {
+ Resource resourceDetailsVF;
+ Either<Resource, RestResponse> createdResource = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VF, NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE,
+ UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF = createdResource.left().value();
+ ArtifactDefinition heatArtifact = AtomicOperationUtils
+ .uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVF, UserRoleEnum.DESIGNER, true, true)
+ .left().value();
+ resourceDetails = new ResourceReqDetails(resourceDetailsVF);
+
+ String resourceUUID = resourceDetailsVF.getUUID();
+ String artifactUUID = heatArtifact.getArtifactUUID();
+
+ System.out.println("Resource UUID: " + resourceUUID);
+ System.out.println("Artifact UUID: " + artifactUUID);
+
+ RestResponse restResponse = ArtifactRestUtils.getResourceDeploymentArtifactExternalAPI(resourceUUID,
+ artifactUUID, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), "Resource");
+
+ Integer responseCode = restResponse.getErrorCode();
+ Integer expectedCode = 200;
+ Assert.assertEquals(responseCode, expectedCode, "Response code is not correct.");
+
+ String response = restResponse.getResponse();
+
+ String payloadData = "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ String decodedPaypload = Decoder.decode(payloadData);
+
+ Assert.assertEquals(response, decodedPaypload, "Response deployment artifact not correct.");
+
+ String auditAction = "ArtifactDownload";
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setResourceName(resourceDetails.getName());
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ expectedResourceAuditJavaObject.setCONSUMER_ID("ci");
+ String resource_url = String.format("/asdc/v1/catalog/resources/%s/artifacts/%s", resourceUUID, artifactUUID);
+ expectedResourceAuditJavaObject.setRESOURCE_URL(resource_url);
+
+ AuditValidationUtils.validateAuditDownloadExternalAPI(expectedResourceAuditJavaObject, auditAction, null,
+ false);
+ }
+
+ // External API - Download artifact for resource - negative test
+ @Test
+ public void downloadArtifactFromResourceViaExternalAPINegativeTest() throws Exception {
+ Resource resourceDetailsVF;
+ Either<Resource, RestResponse> createdResource = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VF, NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE,
+ UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF = createdResource.left().value();
+ ArtifactDefinition heatArtifact = AtomicOperationUtils
+ .uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVF, UserRoleEnum.DESIGNER, true, true)
+ .left().value();
+ resourceDetails = new ResourceReqDetails(resourceDetailsVF);
+
+ String resourceUUID = resourceDetailsVF.getUUID();
+ String artifactUUID = heatArtifact.getArtifactUUID();
+
+ System.out.println("Resource UUID: " + resourceUUID);
+ System.out.println("Artifact UUID: " + artifactUUID);
+
+ RestResponse restResponse = ArtifactRestUtils.getResourceDeploymentArtifactExternalAPI(resourceUUID,
+ "dfsgfdsg324", ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), "Resource");
+
+ Integer responseCode = restResponse.getErrorCode();
+ Integer expectedCode = 200;
+ Assert.assertEquals(responseCode, expectedCode, "Response code is not correct.");
+ }
+
+ // External API - Download artifact for service - negative test
+ @Test
+ public void downloadArtifactFromServiceViaExternalAPI() throws Exception {
+
+ Service resourceDetailsService;
+ Either<Service, RestResponse> createdResource = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER,
+ true);
+ resourceDetailsService = createdResource.left().value();
+
+ ArtifactDefinition heatArtifact = AtomicOperationUtils
+ .uploadArtifactByType(ArtifactTypeEnum.OTHER, resourceDetailsService, UserRoleEnum.DESIGNER, true, true)
+ .left().value();
+
+ String resourceUUID = resourceDetailsService.getUUID();
+ String artifactUUID = heatArtifact.getArtifactUUID();
+
+ System.out.println("Resource UUID: " + resourceUUID);
+ System.out.println("Artifact UUID: " + artifactUUID);
+
+ RestResponse restResponse = ArtifactRestUtils.getResourceDeploymentArtifactExternalAPI(resourceUUID,
+ artifactUUID, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), "Service");
+
+ Integer responseCode = restResponse.getErrorCode();
+ Integer expectedCode = 200;
+ Assert.assertEquals(responseCode, expectedCode, "Response code is not correct.");
+
+ String response = restResponse.getResponse();
+
+ String payloadData = "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ String decodedPaypload = Decoder.decode(payloadData);
+
+ Assert.assertEquals(response, decodedPaypload, "Response deployment artifact not correct.");
+
+ String auditAction = "ArtifactDownload";
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setResourceName(resourceDetailsService.getName());
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ expectedResourceAuditJavaObject.setCONSUMER_ID("ci");
+ String resource_url = String.format("/asdc/v1/catalog/services/%s/artifacts/%s", resourceUUID, artifactUUID);
+ expectedResourceAuditJavaObject.setRESOURCE_URL(resource_url);
+
+ AuditValidationUtils.validateAuditDownloadExternalAPI(expectedResourceAuditJavaObject, auditAction, null,
+ false);
+ }
+
+ // External API - Download ComponentInstance artifact of service - negative
+ // test
+ @Test
+ public void downloadArtifactOfComponentInstanceFromServiceViaExternalAPI() throws Exception {
+
+ Either<Resource, RestResponse> resourceDetailsVF_01e = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, UserRoleEnum.DESIGNER, true);
+ Component resourceDetailsVF_01 = resourceDetailsVF_01e.left().value();
+ ArtifactDefinition heatArtifact = AtomicOperationUtils
+ .uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVF_01, UserRoleEnum.DESIGNER, true, true)
+ .left().value();
+
+ resourceDetailsVF_01 = AtomicOperationUtils
+ .changeComponentState(resourceDetailsVF_01, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true)
+ .getLeft();
+
+ Service resourceDetailsService;
+ Either<Service, RestResponse> createdResource = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER,
+ true);
+ resourceDetailsService = createdResource.left().value();
+
+ ComponentInstance resourceDetailsVF1ins_01 = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceDetailsVF_01, resourceDetailsService,
+ UserRoleEnum.DESIGNER, true)
+ .left().value();
+
+ System.out.println("-----");
+
+ String resourceUUID = resourceDetailsService.getUUID();
+ String componentInstanceUID = resourceDetailsVF1ins_01.getUniqueId();
+ String artifactUUID = heatArtifact.getArtifactUUID();
+
+ System.out.println("Resource UUID: " + resourceUUID);
+ System.out.println("Component instance UID: " + componentInstanceUID);
+ System.out.println("Artifact UUID: " + artifactUUID);
+
+ RestResponse restResponse = ArtifactRestUtils.getComponentInstanceDeploymentArtifactExternalAPI(resourceUUID,
+ componentInstanceUID, artifactUUID, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), "Service");
+ //
+ Integer responseCode = restResponse.getErrorCode();
+ Integer expectedCode = 200;
+ Assert.assertEquals(responseCode, expectedCode, "Response code is not correct.");
+
+ String response = restResponse.getResponse();
+
+ String payloadData = "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ String decodedPaypload = Decoder.decode(payloadData);
+
+ Assert.assertEquals(response, decodedPaypload, "Response deployment artifact not correct.");
+
+ String auditAction = "ArtifactDownload";
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setResourceName(resourceDetailsVF1ins_01.getName());
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ expectedResourceAuditJavaObject.setCONSUMER_ID("ci");
+ String resource_url = String.format("/asdc/v1/catalog/services/%s/resourceInstances/%s/artifacts/%s",
+ resourceUUID, componentInstanceUID, artifactUUID);
+ expectedResourceAuditJavaObject.setRESOURCE_URL(resource_url);
+
+ AuditValidationUtils.validateAuditDownloadExternalAPI(expectedResourceAuditJavaObject, auditAction, null,
+ false);
+ }
+
+ @Test
+ public void downloadArtifactFromResourceTest() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ String jsonBody = createUploadArtifactBodyJson();
+
+ String resourceId = resourceDetails.getUniqueId();
+ String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceId);
+ HttpPost httppost = createPostAddArtifactRequeast(jsonBody, url, true);
+ HttpResponse response = httpclient.execute(httppost);
+ int status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("failed to add artifact", 200, status);
+
+ ArtifactDefinition origArtifact = getArtifactDataFromJson(jsonBody);
+ addArtifactDataFromResponse(response, origArtifact);
+ String artifactId = origArtifact.getUniqueId();
+
+ url = String.format(Urls.UI_DOWNLOAD_RESOURCE_ARTIFACT, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceId, artifactId);
+ HttpGet httpGet = createGetRequest(url);
+ response = httpclient.execute(httpGet);
+ status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("failed to download artifact", 200, status);
+
+ InputStream inputStream = response.getEntity().getContent();
+ ArtifactUiDownloadData artifactUiDownloadData = getArtifactUiDownloadData(IOUtils.toString(inputStream));
+ AssertJUnit.assertEquals("Downloaded payload is different from uploaded one", UPLOAD_ARTIFACT_PAYLOAD,
+ artifactUiDownloadData.getBase64Contents());
+ AssertJUnit.assertEquals("Downloaded artifact name is different from uploaded one", UPLOAD_ARTIFACT_NAME,
+ artifactUiDownloadData.getArtifactName());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceDetails.getVersion(), sdncUserDetails);
+ String auditAction = "ArtifactDownload";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setArtifactData(AuditValidationUtils.buildArtifactDataAudit(origArtifact));
+ expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void downloadArtifactFromServiceTest() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ try {
+
+ String jsonStr = createUploadArtifactBodyJson();
+
+ String url = String.format(Urls.ADD_ARTIFACT_TO_SERVICE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), serviceDetails.getUniqueId());
+ HttpPost httpPost = createPostAddArtifactRequeast(jsonStr, url, true);
+ CloseableHttpResponse result = httpclient.execute(httpPost);
+ int status = result.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("failed to add artifact", 200, status);
+
+ ArtifactDefinition origArtifact = getArtifactDataFromJson(jsonStr);
+ addArtifactDataFromResponse(result, origArtifact);
+ String artifactId = origArtifact.getUniqueId();
+
+ url = String.format(Urls.UI_DOWNLOAD_SERVICE_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(),
+ serviceDetails.getUniqueId(), artifactId);
+ HttpGet httpGet = createGetRequest(url);
+ CloseableHttpResponse response2 = httpclient.execute(httpGet);
+ status = response2.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("failed to download artifact", 200, status);
+ InputStream inputStream = response2.getEntity().getContent();
+ ArtifactUiDownloadData artifactUiDownloadData = getArtifactUiDownloadData(IOUtils.toString(inputStream));
+ AssertJUnit.assertEquals("Downloaded payload is different from uploaded one", UPLOAD_ARTIFACT_PAYLOAD,
+ artifactUiDownloadData.getBase64Contents());
+ AssertJUnit.assertEquals("Downloaded artifact name is different from uploaded one", UPLOAD_ARTIFACT_NAME,
+ artifactUiDownloadData.getArtifactName());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = AuditValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceDetails.getVersion(), sdncUserDetails);
+ String auditAction = "ArtifactDownload";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setArtifactData(AuditValidationUtils.buildArtifactDataAudit(origArtifact));
+ expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ } finally {
+ // RestResponse response =
+ // ServiceRestUtils.deleteService(serviceDetails, serviceVersion,
+ // sdncUserDetails );
+ // checkDeleteResponse(response);
+ httpclient.close();
+ }
+ }
+
+ @Test
+ public void downloadArtifactFromResourceNotFound() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+
+ String resourceId = resourceDetails.getUniqueId();
+ String artifactIdNotFound = "11111";
+
+ ArtifactDefinition origArtifact = new ArtifactDefinition();
+ origArtifact.setUniqueId(artifactIdNotFound);
+
+ String url = String.format(Urls.UI_DOWNLOAD_RESOURCE_ARTIFACT, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceId, artifactIdNotFound);
+ HttpGet httpGet = createGetRequest(url);
+ CloseableHttpResponse response = httpclient.execute(httpGet);
+ int status = response.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("expected 404 not found", 404, status);
+
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.ARTIFACT_NOT_FOUND.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceDetails.getVersion(), sdncUserDetails);
+ String auditAction = "ArtifactDownload";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ expectedResourceAuditJavaObject.setArtifactData("");
+ expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ expectedResourceAuditJavaObject.setPrevArtifactUuid(null);
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void downloadArtifactFromServiceNotFound() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+
+ String artifactIdNotFound = "11111";
+ ArtifactDefinition origArtifact = new ArtifactDefinition();
+ origArtifact.setUniqueId(artifactIdNotFound);
+
+ String url = String.format(Urls.UI_DOWNLOAD_SERVICE_ARTIFACT, config.getCatalogBeHost(),
+ config.getCatalogBePort(), serviceDetails.getUniqueId(), artifactIdNotFound);
+ HttpGet httpGet = createGetRequest(url);
+ CloseableHttpResponse response2 = httpclient.execute(httpGet);
+ int status = response2.getStatusLine().getStatusCode();
+ AssertJUnit.assertEquals("expected 404 not found", 404, status);
+
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.ARTIFACT_NOT_FOUND.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceDetails.getVersion(), sdncUserDetails);
+ String auditAction = "ArtifactDownload";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ expectedResourceAuditJavaObject.setArtifactData("");
+ expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ @Test
+ public void addArtifactToResourceTest() throws Exception {
+
+ ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultArtifact();
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact, sdncUserDetails,
+ resourceDetails.getUniqueId());
+ int status = response.getErrorCode();
+ AssertJUnit.assertEquals("add informational artifact request returned status: " + response.getErrorCode(), 200,
+ status);
+
+ RestResponse resourceResp = ResourceRestUtils.getResource(resourceDetails.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(resourceResp.getResponse());
+ AssertJUnit.assertNotNull(resource);
+
+ Map<String, ArtifactDefinition> artifacts = resource.getArtifacts();
+ boolean isExist = false;
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ if (entry.getKey().equals(defaultArtifact.getArtifactLabel())) {
+ isExist = true;
+
+ }
+ }
+ AssertJUnit.assertTrue(isExist);
+ }
+
+ protected String createUploadArtifactBodyJson() {
+ Map<String, Object> jsonBody = new HashMap<String, Object>();
+ jsonBody.put("artifactName", UPLOAD_ARTIFACT_NAME);
+ jsonBody.put("artifactDisplayName", "configure");
+ jsonBody.put("artifactType", "SHELL");
+ jsonBody.put("mandatory", "false");
+ jsonBody.put("description", "ff");
+ jsonBody.put("payloadData", UPLOAD_ARTIFACT_PAYLOAD);
+ jsonBody.put("artifactLabel", "configure");
+ return gson.toJson(jsonBody);
+ }
+
+ protected ArtifactDefinition getArtifactDataFromJson(String json) {
+ Gson gson = new Gson();
+ JsonObject jsonElement = new JsonObject();
+ jsonElement = gson.fromJson(json, jsonElement.getClass());
+ ArtifactDefinition artifact = new ArtifactDefinition();
+ String payload = null;
+ JsonElement artifactPayload = jsonElement.get(Constants.ARTIFACT_PAYLOAD_DATA);
+ if (artifactPayload != null && !artifactPayload.isJsonNull()) {
+ payload = artifactPayload.getAsString();
+ }
+ jsonElement.remove(Constants.ARTIFACT_PAYLOAD_DATA);
+ artifact = gson.fromJson(jsonElement, ArtifactDefinition.class);
+ artifact.setPayloadData(payload);
+
+ /*
+ * atifact.setArtifactName(UPLOAD_ARTIFACT_NAME);
+ * artifact.setArtifactDisplayName("configure");
+ * artifact.setArtifactType("SHELL"); artifact.setMandatory(false);
+ * artifact.setDescription("ff");
+ * artifact.setPayloadData(UPLOAD_ARTIFACT_PAYLOAD);
+ * artifact.setArtifactLabel("configure");
+ */
+ return artifact;
+ }
+
+ protected HttpGet createGetRequest(String url) {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ httpGet.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ httpGet.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ return httpGet;
+ }
+
+ protected String getArtifactUid(HttpResponse response) throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ String artifactId = (String) responseMap.get("uniqueId");
+ return artifactId;
+ }
+
+ protected String getArtifactEsId(HttpResponse response) throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ String esId = (String) responseMap.get("EsId");
+ return esId;
+ }
+
+ protected ArtifactDefinition addArtifactDataFromResponse(HttpResponse response, ArtifactDefinition artifact)
+ throws HttpResponseException, IOException, ParseException {
+ // String responseString = new
+ // BasicResponseHandler().handleResponse(response);
+ HttpEntity entity = response.getEntity();
+ String responseString = EntityUtils.toString(entity);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ artifact.setEsId((String) responseMap.get("esId"));
+ artifact.setUniqueId((String) responseMap.get("uniqueId"));
+ artifact.setArtifactGroupType(ArtifactGroupTypeEnum.findType((String) responseMap.get("artifactGroupType")));
+ artifact.setTimeout(((Long) responseMap.get("timeout")).intValue());
+ return artifact;
+ }
+
+ protected String getLifecycleArtifactUid(CloseableHttpResponse response)
+ throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ responseMap = (JSONObject) responseMap.get("implementation");
+ String artifactId = (String) responseMap.get("uniqueId");
+ return artifactId;
+ }
+
+ protected HttpDelete createDeleteArtifactRequest(String url) {
+ HttpDelete httpDelete = new HttpDelete(url);
+ httpDelete.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ httpDelete.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ return httpDelete;
+ }
+
+ protected HttpPost createPostAddArtifactRequeast(String jsonBody, String url, boolean addMd5Header)
+ throws UnsupportedEncodingException {
+ HttpPost httppost = new HttpPost(url);
+ httppost.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ httppost.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ httppost.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ if (addMd5Header) {
+ httppost.addHeader(HttpHeaderEnum.Content_MD5.getValue(), GeneralUtility.calculateMD5ByString(jsonBody));
+ }
+ StringEntity input = new StringEntity(jsonBody);
+ input.setContentType("application/json");
+ httppost.setEntity(input);
+ log.debug("Executing request {}", httppost.getRequestLine());
+ return httppost;
+ }
+
+ protected String createLoadArtifactBody() {
+ Map<String, Object> json = new HashMap<String, Object>();
+ json.put("artifactName", "install_apache2.sh");
+ json.put("artifactType", "SHELL");
+ json.put("description", "ddd");
+ json.put("payloadData", "UEsDBAoAAAAIAAeLb0bDQz");
+ json.put("artifactLabel", "name123");
+
+ String jsonStr = gson.toJson(json);
+ return jsonStr;
+ }
+
+ protected void checkDeleteResponse(RestResponse response) {
+ BaseRestUtils.checkStatusCode(response, "delete request failed", false, 204, 404);
+ }
+
+ protected ArtifactUiDownloadData getArtifactUiDownloadData(String artifactUiDownloadDataStr) throws Exception {
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ ArtifactUiDownloadData artifactUiDownloadData = mapper.readValue(artifactUiDownloadDataStr,
+ ArtifactUiDownloadData.class);
+ return artifactUiDownloadData;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/PlaceHolderValidations.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/PlaceHolderValidations.java
new file mode 100644
index 0000000000..02bef89f2c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/PlaceHolderValidations.java
@@ -0,0 +1,696 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+//import static org.junit.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.javatuples.Pair;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.cassandra.CassandraUtils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.datastax.driver.core.Row;
+
+import fj.data.Either;
+
+public class PlaceHolderValidations extends ComponentBaseTest {
+ private static Logger logger = LoggerFactory.getLogger(PlaceHolderValidations.class.getName());
+ private static final String heatExtension = "yaml";
+ // private static final String yangXmlExtension = "xml";
+ // private static final String muranoPkgExtension = "zip";
+ private final String folderName = "addHeatArtifactToServiceAndSertify";
+ private Resource resource;
+ private final int timeOut = 60;
+ private ArtifactReqDetails updateArtifactReqDetails = null;
+ protected User sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ protected User sdncDesignerDetails2 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2);
+ protected ResourceReqDetails resourceDetails1;
+ protected ResourceReqDetails resourceVF;
+ protected ResourceReqDetails resourceCP;
+ protected ResourceReqDetails resourceVL;
+
+ protected ArtifactReqDetails heatArtifactDetails;
+ protected ArtifactReqDetails heatVolArtifactDetails;
+ protected ArtifactReqDetails heatNetArtifactDetails;
+
+ public PlaceHolderValidations() {
+ super(name, PlaceHolderValidations.class.getName());
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ @BeforeMethod
+ public void init() throws IOException, Exception {
+
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatNetArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_NET.getType());
+ heatVolArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_VOL.getType());
+ Resource resourceObject = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetails1 = new ResourceReqDetails(resourceObject);
+ resourceObject = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ resourceVF = new ResourceReqDetails(resourceObject);
+ resourceObject = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.CP, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ resourceCP = new ResourceReqDetails(resourceObject);
+ resourceObject = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VL, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ resourceVL = new ResourceReqDetails(resourceObject);
+ }
+
+ @Test
+ public void validateDeploymentPlaceHoldersByConfig() throws IOException {
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails1, sdncDesignerDetails1);
+ Resource resourceObject = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceObject.getDeploymentArtifacts();
+ assertNotNull("deploymentArtifacts list is null", deploymentArtifacts);
+ List<String> listOfResDepArtTypesFromConfig = Utils.getListOfDepResArtLabels(true);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtTypesFromConfig);
+ for (String resDepArtType : listOfResDepArtTypesFromConfig) {
+ assertNotNull("placeholder of " + resDepArtType + " type doesn't exist",
+ deploymentArtifacts.get(resDepArtType));
+ }
+ }
+
+ private void validateToscaArtifactsBeforeAndAfterSFT(ResourceReqDetails resourceDetails)
+ throws IOException, Exception {
+ RestResponse componentResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails1);
+ Component component = ResponseParser.convertResourceResponseToJavaObject(componentResponse.getResponse());
+ Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
+ for (ArtifactDefinition artifact : toscaArtifacts.values()) {
+ assertNull(artifact.getEsId());
+ }
+
+ componentResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails1,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ component = ResponseParser.convertResourceResponseToJavaObject(componentResponse.getResponse());
+ toscaArtifacts = component.getToscaArtifacts();
+
+ for (ArtifactDefinition artifact : toscaArtifacts.values()) {
+ assertEquals(artifact.getEsId(), artifact.getUniqueId());
+ List<Pair<String, String>> fields = new ArrayList();
+ fields.add(new Pair<String, String>("id", artifact.getEsId()));
+ List<Row> fetchFromTable = CassandraUtils.fetchFromTableQuery("sdcartifact", "resources", fields);
+ assertTrue(1 == fetchFromTable.size());
+ }
+ }
+
+ @Test
+ public void validateToscaArtifactsBeforeAndAfterSFT() throws IOException, Exception {
+ // TODO ADD VF and Service
+ validateToscaArtifactsBeforeAndAfterSFT(resourceDetails1);
+ validateToscaArtifactsBeforeAndAfterSFT(resourceCP);
+ validateToscaArtifactsBeforeAndAfterSFT(resourceVL);
+ }
+
+ @Test
+ public void validateToscaPlaceHoldersByConfig() throws IOException, Exception {
+ List<Component> components = new ArrayList<>();
+ RestResponse componentGetResponse = ResourceRestUtils.getResource(resourceDetails1, sdncDesignerDetails1);
+ components.add(ResponseParser.convertResourceResponseToJavaObject(componentGetResponse.getResponse()));
+
+ componentGetResponse = ResourceRestUtils.getResource(resourceCP, sdncDesignerDetails1);
+ components.add(ResponseParser.convertResourceResponseToJavaObject(componentGetResponse.getResponse()));
+
+ componentGetResponse = ResourceRestUtils.getResource(resourceVF, sdncDesignerDetails1);
+ components.add(ResponseParser.convertResourceResponseToJavaObject(componentGetResponse.getResponse()));
+
+ componentGetResponse = ResourceRestUtils.getResource(resourceVL, sdncDesignerDetails1);
+ components.add(ResponseParser.convertResourceResponseToJavaObject(componentGetResponse.getResponse()));
+
+ Service service = AtomicOperationUtils
+ .createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ componentGetResponse = ServiceRestUtils.getService(service.getUniqueId(),
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ components.add(ResponseParser.parseToObjectUsingMapper(componentGetResponse.getResponse(), Service.class));
+
+ for (Component component : components) {
+ Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
+ assertNotNull("toscaArtifacts list is null", toscaArtifacts);
+ List<String> listOfToscaArtTypesFromConfig = Utils.getListOfToscaArtLabels(true);
+ assertNotNull("tosca artifact types list is null", listOfToscaArtTypesFromConfig);
+ for (String toscaArtType : listOfToscaArtTypesFromConfig) {
+ assertNotNull("placeholder of " + toscaArtType + " type doesn't exist",
+ toscaArtifacts.get(toscaArtType));
+ }
+ }
+
+ }
+
+ // test check configuration of "displayName" field for "heat" type
+ // deployment artifact
+ @Test
+ public void validateDeploymentPlaceHoldersDescriptionOfHeatByConfig() throws IOException {
+
+ Map<String, Object> mapOfDepResArtTypesObjects = getMapOfDepResArtTypesObjects();
+ assertNotNull("deployment artifact types list is null", mapOfDepResArtTypesObjects);
+ Object object = mapOfDepResArtTypesObjects.get("heat");
+ if (object instanceof Map<?, ?>) {
+ Map<String, Object> map = (Map<String, Object>) object;
+ assertTrue(map.get("displayName").equals("Base HEAT Template"));
+ } else {
+ assertTrue("return object does not instance of map", false);
+ }
+ }
+
+ @Test
+ public void addDepResArtEachType() throws Exception {
+
+ String artType;
+
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatVolArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatNetArtifactDetails);
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails1.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ List<String> listOfResDepArtTypesFromConfig = Utils.getListOfDepResArtLabels(true);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtTypesFromConfig);
+ for (String iter : listOfResDepArtTypesFromConfig) {
+ artType = iter;
+ verifyDepArtPlaceHoldersByType(artType);
+ }
+ }
+
+ @Test
+ public void checkHeatParametersExistingForEachType() throws Exception {
+
+ String artType;
+
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatVolArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatNetArtifactDetails);
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails1.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ List<String> listOfResDepArtTypesFromConfig = Utils.getListOfDepResArtLabels(true);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtTypesFromConfig);
+ for (String iter : listOfResDepArtTypesFromConfig) {
+ artType = iter;
+ verifyDepArtPlaceHoldersByType(artType);
+ verifyHeatParametersExistance(artType, false);
+ }
+ }
+
+ @Test
+ public void checkHeatParametersExistingForSpecificType() throws Exception {
+
+ String artType;
+
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatVolArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatNetArtifactDetails);
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails1.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ List<String> listOfResDepArtTypesFromConfig = Utils.getListOfDepResArtLabels(true);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtTypesFromConfig);
+ for (String iter : listOfResDepArtTypesFromConfig) {
+ artType = iter;
+ if (heatArtifactDetails.getArtifactLabel().equals(iter)) {
+ verifyHeatParametersExistance(artType, true);
+ } else {
+ verifyHeatParametersExistance(artType, false);
+ }
+ }
+ }
+
+ @Test
+ public void addAndDeleteDepResArtEachType() throws Exception {
+
+ String artType;
+
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatVolArtifactDetails);
+ addDeploymentArtifactByTypeToResource(resourceDetails1, heatNetArtifactDetails);
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails1.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ List<String> listOfResDepArtTypesFromConfig = Utils.getListOfDepResArtLabels(true);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtTypesFromConfig);
+ for (String iter : listOfResDepArtTypesFromConfig) {
+ artType = iter;
+ verifyDepArtPlaceHoldersByType(artType);
+ }
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails1,
+ sdncDesignerDetails1, LifeCycleStatesEnum.CHECKIN);
+ assertTrue("expected response code in CHECKIN 200", restResponseResource.getErrorCode() == 200);
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails1, sdncDesignerDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertTrue("expected response code in CHECKOUT 200", restResponseResource.getErrorCode() == 200);
+
+ // delete all deployment artifacts
+ deleteDeploymentArtifactByTypeToResource(resourceDetails1, heatArtifactDetails);
+ deleteDeploymentArtifactByTypeToResource(resourceDetails1, heatVolArtifactDetails);
+ deleteDeploymentArtifactByTypeToResource(resourceDetails1, heatNetArtifactDetails);
+ response = ResourceRestUtils.getResource(resourceDetails1.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ listOfResDepArtTypesFromConfig = Utils.getListOfDepResArtLabels(true);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtTypesFromConfig);
+ for (String iter : listOfResDepArtTypesFromConfig) {
+ artType = iter;
+ verifyDepArtPlaceHoldersByType(artType);
+ }
+ }
+
+ @Test
+ public void addRemoveAddAgainArtifact() throws Exception {
+
+ // get MAP before upload artifact
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails1, sdncDesignerDetails1);
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get("heat");
+
+ // validate place holder exist
+ assertNotNull(artifactDefinition);
+
+ // add artifact
+ updateArtifactReqDetails = getUpdateArtifactDetails(ArtifactTypeEnum.HEAT.getType());
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(
+ updateArtifactReqDetails, sdncDesignerDetails1, resourceDetails1.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: "
+ + addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 200);
+
+ ArtifactDefinition artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addInformationalArtifactToResource.getResponse());
+ ArtifactDefinition artDef1 = fillArtDefFromResponse(artifactDefinitionResponseJavaObject);
+
+ // remove artifact
+ RestResponse deleteArtifactFromResource = ArtifactRestUtils.deleteInformationalArtifactFromResource(
+ resourceDetails1.getUniqueId(), updateArtifactReqDetails, sdncDesignerDetails1);
+ logger.debug(
+ "addInformationalArtifactToResource response: " + deleteArtifactFromResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + deleteArtifactFromResource.getErrorCode(),
+ deleteArtifactFromResource.getErrorCode() == 200);
+
+ RestResponse getResourceResp = ResourceRestUtils.getResource(resourceDetails1, sdncDesignerDetails1);
+
+ artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(deleteArtifactFromResource.getResponse());
+ assertTrue(artifactDefinitionResponseJavaObject.getArtifactName().isEmpty());
+ assertTrue(artifactDefinitionResponseJavaObject.getDescription().isEmpty());
+ assertTrue(artifactDefinitionResponseJavaObject.getArtifactChecksum().isEmpty());
+ assertTrue(artifactDefinitionResponseJavaObject.getEsId().isEmpty());
+ assertTrue(artifactDefinitionResponseJavaObject.getArtifactUUID().isEmpty());
+ assertNull(artifactDefinitionResponseJavaObject.getHeatParameters());
+
+ // add artifact again with different user
+ addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails1, resourceDetails1.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: "
+ + addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 200);
+
+ artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addInformationalArtifactToResource.getResponse());
+ ArtifactDefinition artDef2 = fillArtDefFromResponse(artifactDefinitionResponseJavaObject);
+
+ assertFalse("check artifact checksum", artDef1.getArtifactChecksum().equals(artDef2.getArtifactChecksum()));
+ assertTrue("check artifact EsId", artDef1.getEsId().equals(artDef2.getEsId()));
+ assertFalse("check artifact UUID", artDef1.getArtifactUUID().equals(artDef2.getArtifactUUID()));
+ assertTrue("check UserIdCreator", artDef1.getUserIdCreator().equals(artDef2.getUserIdCreator()));
+ assertTrue("check UserIdLastUpdater", artDef1.getUserIdLastUpdater().equals(artDef2.getUserIdLastUpdater()));
+ }
+
+ @Test
+ public void addUpdateArtifactByType() throws Exception {
+
+ // get MAP before upload artifact
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails1, sdncDesignerDetails1);
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get("heat");
+
+ // validate place holder exist
+ assertNotNull(artifactDefinition);
+
+ // add artifact
+ updateArtifactReqDetails = getUpdateArtifactDetails(ArtifactTypeEnum.HEAT.getType());
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(
+ updateArtifactReqDetails, sdncDesignerDetails1, resourceDetails1.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: "
+ + addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 200);
+
+ ArtifactDefinition artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addInformationalArtifactToResource.getResponse());
+ ArtifactDefinition artDef1 = fillArtDefFromResponse(artifactDefinitionResponseJavaObject);
+
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails1,
+ sdncDesignerDetails1, LifeCycleStatesEnum.CHECKIN);
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails1, sdncDesignerDetails2,
+ LifeCycleStatesEnum.CHECKOUT);
+
+ // update with different user artifact
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setUniqueId(artifactDefinition.getUniqueId());
+ heatArtifactDetails.setArtifactName("2.yaml");
+ heatArtifactDetails.setArtifactLabel(artifactDefinition.getArtifactLabel());
+
+ addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails2, resourceDetails1.getUniqueId(), "heat");
+ logger.debug("addInformationalArtifactToResource response: "
+ + addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 200);
+
+ artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addInformationalArtifactToResource.getResponse());
+ ArtifactDefinition artDef2 = fillArtDefFromResponse(artifactDefinitionResponseJavaObject);
+ verifyArtDefFields(artDef1, artDef2);
+
+ }
+
+ @Test
+ public void addUpdateDeleteArtifact() throws Exception {
+
+ // get MAP before upload artifact
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails1, sdncDesignerDetails1);
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get("heat");
+
+ // validate place holder exist
+ assertNotNull(artifactDefinition);
+
+ updateArtifactReqDetails = getUpdateArtifactDetails(ArtifactTypeEnum.HEAT.getType());
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(
+ updateArtifactReqDetails, sdncDesignerDetails1, resourceDetails1.getUniqueId());
+ logger.debug("addInformationalArtifactToResource response: "
+ + addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 200);
+
+ ArtifactDefinition artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addInformationalArtifactToResource.getResponse());
+ ArtifactDefinition artDef1 = fillArtDefFromResponse(artifactDefinitionResponseJavaObject);
+
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails1,
+ sdncDesignerDetails1, LifeCycleStatesEnum.CHECKIN);
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails1, sdncDesignerDetails2,
+ LifeCycleStatesEnum.CHECKOUT);
+
+ // update with different user artifact
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setArtifactName("2.yaml");
+
+ addInformationalArtifactToResource = ArtifactRestUtils.updateInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails2, resourceDetails1.getUniqueId(), "heat");
+ logger.debug("addInformationalArtifactToResource response: "
+ + addInformationalArtifactToResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 200);
+
+ artifactDefinitionResponseJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addInformationalArtifactToResource.getResponse());
+ ArtifactDefinition artDef2 = fillArtDefFromResponse(artifactDefinitionResponseJavaObject);
+
+ verifyArtDefFields(artDef1, artDef2);
+
+ RestResponse delteArtifactFromResource = ArtifactRestUtils.deleteInformationalArtifactFromResource(
+ resourceDetails1.getUniqueId(), heatArtifactDetails, sdncDesignerDetails2);
+ logger.debug("addInformationalArtifactToResource response: {}", delteArtifactFromResource.getResponseMessage());
+ assertTrue("response code is not 200, returned :" + delteArtifactFromResource.getErrorCode(),
+ delteArtifactFromResource.getErrorCode() == 200);
+
+ }
+
+ @Test
+ public void addHeatVolArtInvalidExtension() throws Exception {
+
+ heatVolArtifactDetails.setArtifactName("heatVol.txt");
+ RestResponse response = getResponseOnAddDeploymentArtifactByTypeToResource(resourceDetails1,
+ heatVolArtifactDetails);
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name());
+ assertEquals("Check response code after upload artifact", errorInfo.getCode(), response.getErrorCode());
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.HEAT_VOL.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name(), variables,
+ response.getResponse());
+ }
+
+ @Test
+ public void addHeatNetArtInvalidExtension() throws Exception {
+
+ heatNetArtifactDetails.setArtifactName("yaml");
+ RestResponse response = getResponseOnAddDeploymentArtifactByTypeToResource(resourceDetails1,
+ heatNetArtifactDetails);
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name());
+ assertEquals("Check response code after upload artifact", errorInfo.getCode(), response.getErrorCode());
+ List<String> variables = Arrays.asList(ArtifactTypeEnum.HEAT_NET.getType());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION.name(), variables,
+ response.getResponse());
+ }
+
+ @Test
+ public void checkServiceSecurityTemplateInformationalArtifactsCreation() throws IOException, Exception {
+
+ Either<Service, RestResponse> createServiceResponse = AtomicOperationUtils
+ .createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true);
+ Map<String, ArtifactDefinition> artifacts = null;
+ ArtifactDefinition securitytemplate = null;
+ if (createServiceResponse.isLeft()) {
+ Component component = createServiceResponse.left().value();
+ artifacts = component.getArtifacts();
+ securitytemplate = artifacts.get("servicesecuritytemplate");
+ assertNotNull(securitytemplate);
+ assertEquals("Service Security Template", securitytemplate.getArtifactDisplayName());
+ } else {
+ logger.debug("checkSecurityTemplateInformationalArtifactsCreation service creation response: "
+ + createServiceResponse.right().value().getResponseMessage());
+ }
+ }
+
+ @Test
+ public void checkResourceSecurityTemplateInformationalArtifactsCreation() throws IOException, Exception {
+
+ Resource resource = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.CONTAINER_APPLICATION, ResourceCategoryEnum.APPLICATION_L4_BORDER,
+ UserRoleEnum.DESIGNER, true).left().value();
+ Map<String, ArtifactDefinition> artifacts = resource.getArtifacts();
+ ArtifactDefinition securitytemplate = artifacts.get("resourcesecuritytemplate");
+ assertNotNull(securitytemplate);
+ assertEquals("Resource Security Template", securitytemplate.getArtifactDisplayName());
+ }
+
+ // Benny
+ @Test
+ public void serviceSecurityTemplateInformationalArtifact() throws IOException, Exception {
+ String artifactPlaceHolder = "servicesecuritytemplate";
+ Service service = AtomicOperationUtils
+ .createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ Map<String, ArtifactDefinition> artifacts = service.getArtifacts();
+ ArtifactDefinition securitytemplate = artifacts.get(artifactPlaceHolder);
+ assertNotNull(securitytemplate);
+ assertEquals("Service Security Template", securitytemplate.getArtifactDisplayName());
+ assertEquals("OTHER", securitytemplate.getArtifactType());
+ assertEquals(artifactPlaceHolder, securitytemplate.getArtifactLabel());
+ // Get service
+ RestResponse getService = ServiceRestUtils.getService(service.getUniqueId(),
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, getService.getErrorCode().intValue());
+ service = ResponseParser.parseToObjectUsingMapper(getService.getResponse(), Service.class);
+ artifacts = service.getArtifacts();
+ securitytemplate = artifacts.get(artifactPlaceHolder);
+ assertNotNull(securitytemplate);
+ assertEquals("Service Security Template", securitytemplate.getArtifactDisplayName());
+ assertEquals("OTHER", securitytemplate.getArtifactType());
+ assertEquals(artifactPlaceHolder, securitytemplate.getArtifactLabel());
+ }
+
+ @Test
+ public void resourceSecurityTemplateInformationalArtifacts() throws IOException, Exception {
+ String artifactPlaceHolder = "resourcesecuritytemplate";
+ Resource resource = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.CONTAINER_APPLICATION, ResourceCategoryEnum.APPLICATION_L4_BORDER,
+ UserRoleEnum.DESIGNER, true).left().value();
+ Map<String, ArtifactDefinition> artifacts = resource.getArtifacts();
+ ArtifactDefinition securitytemplate = artifacts.get("resourcesecuritytemplate");
+ assertNotNull(securitytemplate);
+ assertEquals("Resource Security Template", securitytemplate.getArtifactDisplayName());
+ assertEquals("OTHER", securitytemplate.getArtifactType());
+ assertEquals(artifactPlaceHolder, securitytemplate.getArtifactLabel());
+ // Get resource
+ RestResponse getresource = ResourceRestUtils.getResource(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER),
+ resource.getUniqueId());
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, getresource.getErrorCode().intValue());
+ resource = ResponseParser.parseToObjectUsingMapper(getresource.getResponse(), Resource.class);
+ artifacts = resource.getArtifacts();
+ securitytemplate = artifacts.get(artifactPlaceHolder);
+ assertNotNull(securitytemplate);
+ assertEquals("Resource Security Template", securitytemplate.getArtifactDisplayName());
+ assertEquals("OTHER", securitytemplate.getArtifactType());
+ assertEquals(artifactPlaceHolder, securitytemplate.getArtifactLabel());
+ }
+
+ // ================================================
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Object> getMapOfDepResArtTypesObjects() throws FileNotFoundException {
+
+ return (Map<String, Object>) Utils.parseYamlConfig("deploymentResourceArtifacts");
+
+ }
+
+ private void addDeploymentArtifactByTypeToResource(ResourceReqDetails resourceReqDetails,
+ ArtifactReqDetails artReqDetails) throws IOException, Exception {
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(artReqDetails,
+ sdncDesignerDetails1, resourceReqDetails.getUniqueId());
+ assertTrue("add" + artReqDetails.getArtifactLabel() + " artifact to resource request returned status:"
+ + response.getErrorCode(), response.getErrorCode() == 200);
+ }
+
+ private RestResponse getResponseOnAddDeploymentArtifactByTypeToResource(ResourceReqDetails resourceReqDetails,
+ ArtifactReqDetails artReqDetails) throws IOException, Exception {
+
+ return ArtifactRestUtils.addInformationalArtifactToResource(artReqDetails, sdncDesignerDetails1,
+ resourceReqDetails.getUniqueId());
+ }
+
+ private void deleteDeploymentArtifactByTypeToResource(ResourceReqDetails resourceReqDetails,
+ ArtifactReqDetails artReqDetails) throws IOException, Exception {
+
+ RestResponse response = ArtifactRestUtils.deleteInformationalArtifactFromResource(
+ resourceReqDetails.getUniqueId(), artReqDetails, sdncDesignerDetails1);
+ assertTrue("delete" + artReqDetails.getArtifactLabel() + " artifact to resource request returned status:"
+ + response.getErrorCode(), response.getErrorCode() == 200);
+ }
+
+ private void verifyDepArtPlaceHoldersByType(String artType) {
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = resource.getDeploymentArtifacts();
+ assertNotNull("deployment artifact data is null", deploymentArtifacts.get(artType));
+ assertNotNull("deployment artifact data is null", deploymentArtifacts.get(artType).getEsId());
+ assertNotNull("deployment artifact data is null", deploymentArtifacts.get(artType).getDescription());
+ assertTrue(
+ "deployment artifact timeout does not equal to default value " + timeOut + " expected " + timeOut
+ + ", actual - " + deploymentArtifacts.get(artType).getTimeout(),
+ deploymentArtifacts.get(artType).getTimeout() == timeOut);
+ assertTrue("deployment artifact label value ",
+ deploymentArtifacts.get(artType).getArtifactLabel().equals(artType));
+ }
+
+ private void verifyHeatParametersExistance(String artType, Boolean isNull) {
+ Map<String, ArtifactDefinition> deploymentArtifacts = resource.getDeploymentArtifacts();
+ if (isNull) {
+ assertNull("heatParameters list for type " + artType + " is not null",
+ deploymentArtifacts.get(artType).getHeatParameters());
+ } else {
+ assertNotNull("heatParameters list for type " + artType + " is null",
+ deploymentArtifacts.get(artType).getHeatParameters());
+ }
+ }
+
+ private void verifyArtDefFields(ArtifactDefinition artDef1, ArtifactDefinition artDef2) {
+
+ assertFalse("check artifact checksum", artDef1.getArtifactChecksum().equals(artDef2.getArtifactChecksum()));
+ assertFalse("check artifact EsId", artDef1.getEsId().equals(artDef2.getEsId()));
+ assertFalse("check artifact UUID", artDef1.getArtifactUUID().equals(artDef2.getArtifactUUID()));
+ assertTrue("check UserIdCreator", artDef1.getUserIdCreator().equals(artDef2.getUserIdCreator()));
+ assertFalse("check UserIdLastUpdater", artDef1.getUserIdLastUpdater().equals(artDef2.getUserIdLastUpdater()));
+
+ }
+
+ private ArtifactDefinition fillArtDefFromResponse(ArtifactDefinition artifactDefinitionResponseJavaObject) {
+ ArtifactDefinition artDef = new ArtifactDefinition();
+ artDef.setArtifactChecksum(artifactDefinitionResponseJavaObject.getArtifactChecksum());
+ artDef.setEsId(artifactDefinitionResponseJavaObject.getEsId());
+ artDef.setArtifactUUID(artifactDefinitionResponseJavaObject.getArtifactUUID());
+ artDef.setUserIdCreator(artifactDefinitionResponseJavaObject.getUserIdCreator());
+ artDef.setUserIdLastUpdater(artifactDefinitionResponseJavaObject.getUserIdLastUpdater());
+ return artDef;
+ }
+
+ private ArtifactReqDetails getUpdateArtifactDetails(String artType) throws IOException, Exception {
+ String ext = heatExtension;
+ String sourceDir = config.getResourceConfigDir();
+ String testResourcesPath = sourceDir + File.separator + folderName;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ logger.debug("listFileName: {}", listFileName.toString());
+
+ String payload = FileUtils.loadPayloadFile(listFileName, ext, true);
+ ArtifactReqDetails updateArtifactReqDetails = ElementFactory.getDefaultDeploymentArtifactForType(artType);
+ updateArtifactReqDetails.setPayload(payload);
+ updateArtifactReqDetails.setArtifactName("1.yaml");
+ return updateArtifactReqDetails;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateArtResponse.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateArtResponse.java
new file mode 100644
index 0000000000..01169630a4
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateArtResponse.java
@@ -0,0 +1,631 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ResourceValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.yaml.snakeyaml.Yaml;
+
+public class ValidateArtResponse extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+ protected String serviceVersion;
+
+ public ValidateArtResponse() {
+ super(name, ArtifactServletTest.class.getName());
+
+ }
+
+ protected final String pathToFile = "heatArtifactParameters";
+ protected final String heatWithValidParams = "heatWithValidParams.yaml";
+ protected final String heatWithParamsMissingDefault = "heatWithParamsMissingDefault.yaml";
+ protected final String heatWithParamsMissingDesc = "heatWithParamsMissingDesc.yaml";
+ protected final String heatWithParamsMissingType = "heatWithParamsMissingType.yaml";
+ protected final String importNoDerivedFromFile = "myComputeDerivedFromNotExists.yml";
+ protected final String decodedPayload = "decodedPayload";
+ protected final String encodedPayload = "encodedPayload";
+
+ protected Resource resourceDetailsObj;
+ protected ResourceReqDetails resourceDetails;
+ protected User sdncDesignerDetails;
+
+ @BeforeMethod
+ public void init() throws Exception {
+
+ resourceDetailsObj = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetails = new ResourceReqDetails(resourceDetailsObj);
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ }
+
+ @Test
+ public void compareParamtersVsYaml() throws Exception {
+
+ // select file to upload
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithValidParams);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // create MAP from received JSON
+
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ protected void assertnull(String string, boolean equals) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Map<String, String> extractSingleParameter(Map<String, String> curr) {
+ Map<String, String> innerMap = new HashMap<String, String>();
+ if (curr.containsKey("description")) {
+ innerMap.put("description", curr.get("description"));
+ }
+
+ if (curr.containsKey("defaultValue")) {
+ innerMap.put("default", curr.get("defaultValue"));
+ } else {
+ // System.out.println("kuku");
+ }
+ innerMap.put("type", curr.get("type"));
+ return innerMap;
+ }
+
+ public Map<String, Map> createMapFromYaml(String payload) {
+ ArrayList<String> parametersList = new ArrayList<String>();
+
+ Yaml yaml = new Yaml();
+
+ Map<String, Map> result = (Map<String, Map>) yaml.load(payload);
+ Map<String, Map> paramters = (Map<String, Map>) result.get("parameters");
+
+ for (Map.Entry<String, Map> entry : paramters.entrySet()) {
+ Map<String, String> origInnerMap = (Map<String, String>) entry.getValue();
+
+ if (origInnerMap.containsKey("label")) {
+ origInnerMap.remove("label");
+ paramters.remove(entry);
+ paramters.put(entry.getKey(), origInnerMap);
+ }
+ }
+ return paramters;
+ }
+
+ public Map<String, Map<String, String>> jsonToMap(RestResponse addInformationalArtifactToResource,
+ String section2extract, String createKeyMapBy) {
+ Map<String, Object> JsonToMap = new HashMap<String, Object>();
+ JsonToMap = (Map<String, Object>) ResponseParser.parseToObject(addInformationalArtifactToResource.getResponse(),
+ JsonToMap.getClass());
+
+ List<Map<String, String>> listOfParamters = (List<Map<String, String>>) JsonToMap.get(section2extract);
+ Map<String, Map<String, String>> mapOfActualParameters = new HashMap<String, Map<String, String>>();
+
+ for (Map<String, String> curr : listOfParamters) {
+ Map<String, String> innerMap = extractSingleParameter(curr);
+
+ mapOfActualParameters.put(curr.get(createKeyMapBy), innerMap);
+ }
+ return mapOfActualParameters;
+ }
+
+ public Map<String, String> selectFileToUpload(String pathToFile, String fileName) throws IOException {
+ String sourceDir = config.getResourceConfigDir();
+ String testResourcesPath = sourceDir + File.separator + pathToFile;
+ String file = fileName;
+ Map<String, String> filePayload = new HashMap<String, String>();
+ String payload = Decoder.readFileToString(testResourcesPath + File.separator + file);
+ filePayload.put(decodedPayload, payload);
+ filePayload.put(encodedPayload, Decoder.encode(payload.getBytes()));
+
+ return filePayload;
+ }
+
+ @Test
+ public void missingDescParam() throws Exception {
+
+ // select file to upload
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithParamsMissingDesc);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // create MAP from received JSON
+
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ @Test
+ public void missingDefaultParam() throws Exception {
+
+ // select file to upload
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithParamsMissingDefault);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // create MAP from received JSON
+
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ @Test
+ public void missingTypeParam() throws Exception {
+
+ // select file to upload
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithParamsMissingType);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // System.out.println(addInformationalArtifactToResource);
+ AssertJUnit.assertTrue(
+ "response code is not 400, returned :" + addInformationalArtifactToResource.getErrorCode(),
+ addInformationalArtifactToResource.getErrorCode() == 400);
+
+ }
+
+ @Test
+ public void updateValueParam() throws Exception {
+
+ String updateValueParam = "changed";
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithValidParams);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+ // System.out.println(resourceGetResponse.getResponse().toString());
+ String atifactUniqueId = ResponseParser
+ .getValueFromJsonResponse(addInformationalArtifactToResource.getResponse(), "uniqueId");
+
+ ArtifactReqDetails artifacJavaObject = ResponseParser
+ .convertArtifactReqDetailsToJavaObject(addInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters2 = artifacJavaObject.getHeatParameters();
+
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters2) {
+ heatParameterDefinition.setCurrentValue(updateValueParam);
+ }
+ artifacJavaObject.setHeatParameters(heatParameters2);
+ artifacJavaObject.setPayloadData(null);
+
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateDeploymentArtifactToResource(
+ artifacJavaObject, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // verify change in update response
+
+ ArtifactDefinition ArtifactDefinitionRespJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(updateInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters = ArtifactDefinitionRespJavaObject.getHeatParameters();
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ String verify = updateValueParam;
+ AssertJUnit.assertTrue("verification failed", verify.equals(heatParameterDefinition.getCurrentValue()));
+ }
+
+ // verify change in getResource
+
+ resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+ deploymentArtifacts.get(heatArtifactDetails.getArtifactName());
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ String verify = updateValueParam;
+ AssertJUnit.assertTrue("verification failed", verify.equals(heatParameterDefinition.getCurrentValue()));
+ }
+
+ // create MAP from received JSON
+
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ @Test
+ public void updateValueParamMissingDefault() throws Exception {
+
+ String updateValueParam = "changed";
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithParamsMissingDefault);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+ // System.out.println(resourceGetResponse.getResponse().toString());
+ String atifactUniqueId = ResponseParser
+ .getValueFromJsonResponse(addInformationalArtifactToResource.getResponse(), "uniqueId");
+
+ ArtifactReqDetails artifacJavaObject = ResponseParser
+ .convertArtifactReqDetailsToJavaObject(addInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters2 = artifacJavaObject.getHeatParameters();
+
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters2) {
+ heatParameterDefinition.setCurrentValue(updateValueParam);
+ }
+ artifacJavaObject.setHeatParameters(heatParameters2);
+ artifacJavaObject.setPayloadData(null);
+
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateDeploymentArtifactToResource(
+ artifacJavaObject, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // verify change in update response
+
+ ArtifactDefinition ArtifactDefinitionRespJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(updateInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters = ArtifactDefinitionRespJavaObject.getHeatParameters();
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ String verify = updateValueParam;
+ AssertJUnit.assertTrue("verification failed", verify.equals(heatParameterDefinition.getCurrentValue()));
+ }
+
+ // verify change in getResource
+
+ resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+ deploymentArtifacts.get(heatArtifactDetails.getArtifactName());
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ String verify = updateValueParam;
+ AssertJUnit.assertTrue("verification failed", verify.equals(heatParameterDefinition.getCurrentValue()));
+ }
+
+ // create MAP from received JSON
+
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ @Test
+ public void updateValueParamNull() throws Exception {
+
+ String updateValueParam = null;
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithValidParams);
+
+ // upload HEAT file and save JSON response
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+ // System.out.println(resourceGetResponse.getResponse().toString());
+ String atifactUniqueId = ResponseParser
+ .getValueFromJsonResponse(addInformationalArtifactToResource.getResponse(), "uniqueId");
+
+ ArtifactReqDetails artifacJavaObject = ResponseParser
+ .convertArtifactReqDetailsToJavaObject(addInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters2 = artifacJavaObject.getHeatParameters();
+
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters2) {
+ heatParameterDefinition.setCurrentValue(updateValueParam);
+ }
+ artifacJavaObject.setHeatParameters(heatParameters2);
+ artifacJavaObject.setPayloadData(null);
+
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateDeploymentArtifactToResource(
+ artifacJavaObject, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // verify change in update response
+ ArtifactDefinition ArtifactDefinitionRespJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(updateInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters = ArtifactDefinitionRespJavaObject.getHeatParameters();
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ // String verify = updateValueParam;
+ if (heatParameterDefinition.getDefaultValue() != null) {
+ AssertJUnit.assertTrue(
+ heatParameterDefinition.getDefaultValue().equals(heatParameterDefinition.getCurrentValue()));
+ } else {
+ AssertJUnit.assertNull("verification failed", heatParameterDefinition.getCurrentValue());
+ }
+ }
+
+ // verify change in getResource
+ resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+ deploymentArtifacts.get(heatArtifactDetails.getArtifactName());
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ // String verify = updateValueParam;
+ if (heatParameterDefinition.getDefaultValue() != null) {
+ AssertJUnit.assertTrue(
+ heatParameterDefinition.getDefaultValue().equals(heatParameterDefinition.getCurrentValue()));
+ } else {
+ AssertJUnit.assertNull("verification failed", heatParameterDefinition.getCurrentValue());
+ }
+ }
+
+ // create MAP from received JSON
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ @Test
+ public void updateValueParamEmpty() throws Exception {
+
+ String updateValueParam = "";
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithValidParams);
+
+ // upload HEAT file and save JSON response
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+ // System.out.println(resourceGetResponse.getResponse().toString());
+ String atifactUniqueId = ResponseParser
+ .getValueFromJsonResponse(addInformationalArtifactToResource.getResponse(), "uniqueId");
+
+ ArtifactReqDetails artifacJavaObject = ResponseParser
+ .convertArtifactReqDetailsToJavaObject(addInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters2 = artifacJavaObject.getHeatParameters();
+
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters2) {
+ heatParameterDefinition.setCurrentValue(updateValueParam);
+ }
+ artifacJavaObject.setHeatParameters(heatParameters2);
+ artifacJavaObject.setPayloadData(null);
+
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateDeploymentArtifactToResource(
+ artifacJavaObject, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ // verify change in update response
+
+ ArtifactDefinition ArtifactDefinitionRespJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(updateInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters = ArtifactDefinitionRespJavaObject.getHeatParameters();
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ String verify = updateValueParam;
+ AssertJUnit.assertTrue("verification failed", verify.equals(heatParameterDefinition.getCurrentValue()));
+ }
+
+ // verify change in getResource
+
+ resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+ deploymentArtifacts.get(heatArtifactDetails.getArtifactName());
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ String verify = updateValueParam;
+ AssertJUnit.assertTrue("verification failed", verify.equals(heatParameterDefinition.getCurrentValue()));
+ }
+
+ // create MAP from received JSON
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+ @Test
+ public void onlyValueParamPermited() throws Exception {
+
+ Map<String, String> filePayload = selectFileToUpload(pathToFile, heatWithValidParams);
+
+ // upload HEAT file and save JSON response
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatArtifactDetails.setPayload(filePayload.get(encodedPayload));
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+ // System.out.println(resourceGetResponse.getResponse().toString());
+ String atifactUniqueId = ResponseParser
+ .getValueFromJsonResponse(addInformationalArtifactToResource.getResponse(), "uniqueId");
+
+ ArtifactReqDetails artifacJavaObject = ResponseParser
+ .convertArtifactReqDetailsToJavaObject(addInformationalArtifactToResource.getResponse());
+ List<HeatParameterDefinition> heatParameters2 = artifacJavaObject.getHeatParameters();
+
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters2) {
+ heatParameterDefinition.setDefaultValue("changed");
+ heatParameterDefinition.setName("changed");
+ heatParameterDefinition.setDescription("changed");
+ heatParameterDefinition.setType("changed");
+ heatParameterDefinition.setCurrentValue("changed");
+ }
+ artifacJavaObject.setHeatParameters(heatParameters2);
+ artifacJavaObject.setPayloadData(null);
+
+ RestResponse updateInformationalArtifactToResource = ArtifactRestUtils.updateDeploymentArtifactToResource(
+ artifacJavaObject, sdncDesignerDetails, resourceDetails.getUniqueId());
+
+ resourceGetResponse = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails);
+
+ // create MAP from received JSON
+
+ String section2extract = "heatParameters";
+ String createKeyMapBy = "name";
+ Map<String, Map<String, String>> mapOfActualParameters = jsonToMap(addInformationalArtifactToResource,
+ section2extract, createKeyMapBy);
+
+ // Prepare map to validate JS
+
+ Map<String, Map> paramters = createMapFromYaml(filePayload.get(decodedPayload));
+
+ // compare MAPs
+
+ ResourceValidationUtils.compareElements(mapOfActualParameters, paramters);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsFromUI.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsFromUI.java
new file mode 100644
index 0000000000..c22dcb96d4
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsFromUI.java
@@ -0,0 +1,444 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+//
+//import java.util.Arrays;
+//import java.util.List;
+//
+//import org.junit.Rule;
+//import org.junit.rules.TestName;
+//import org.testng.AssertJUnit;
+//import org.testng.annotations.Test;
+//
+//import org.openecomp.sdc.be.dao.api.ActionStatus;
+//import org.openecomp.sdc.be.model.ArtifactDefinition;
+//import org.openecomp.sdc.be.model.ComponentInstance;
+//import org.openecomp.sdc.be.model.HeatParameterDefinition;
+//import org.openecomp.sdc.be.model.Resource;
+//import org.openecomp.sdc.be.model.Service;
+//import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+//import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+//import org.openecomp.sdc.ci.tests.datatypes.enums.RespJsonKeysEnum;
+//import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+//import org.openecomp.sdc.ci.tests.preRequisites.HeatEnvBaseTest;
+//import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+//import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+//import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+//import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+//import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+//import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+//
+//public class ValidateHeatArtFieldsFromUI extends HeatEnvBaseTest {
+public class ValidateHeatArtFieldsFromUI {
+ //
+ //
+ //
+ // private static final String heatExtension = "yaml";
+ // private static final String yangXmlExtension = "xml";
+ // private static final String muranoPkgExtension = "zip";
+ // private final String folderName= "yamlFieldsValidation";
+ //
+ //
+ // private final String uuidString =
+ // RespJsonKeysEnum.UUID.getRespJsonKeyName().toString();
+ //
+ // public ValidateHeatArtFieldsFromUI() {
+ // super(name, ValidateHeatArtFieldsFromUI.class.getName());
+ // }
+ //
+ // @Rule
+ // public static TestName name = new TestName();
+ //
+ //
+ // @Test
+ // public void heatEnvValidateHeatArtFiledTypes_UpdateFailed() throws
+ // Exception {
+ //
+ // //get relevant service
+ // RestResponse serviceGetResponse =
+ // ServiceRestUtils.getService(serviceDetails2, sdncDesignerDetails);
+ // Service service =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ // List<ComponentInstance> resourceInstances =
+ // service.getComponentInstances();
+ // ComponentInstance resourceInstance = resourceInstances.get(0);
+ //
+ //
+ // String defaultParam = null;
+ //
+ // //update heatEnv with invalid value in number field
+ //
+ // ArtifactDefinition artifact = getCurrentArtifactDefinitionFromService();
+ // String currentHeatEnvParamBeforeUpdate = replaceHeatParamsValue(artifact,
+ // "home_number", "Tel Aviv");
+ // ArtifactReqDetails artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // RestResponse res = ArtifactRestUtils.updateDeploymentArtifactToRI(artReq,
+ // sdncDesignerDetails, resourceInstance.getUniqueId(),
+ // service.getUniqueId());
+ //
+ // // validate negative response
+ // List<String> variables = Arrays.asList("HEAT_ENV", "number",
+ // "home_number");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ // variables, res.getResponse());
+ //
+ // //validate current value not affected
+ // artifact = getCurrentArtifactDefinitionFromService();
+ // String currentHeatEnvParamAfterUpdate = getHeatParamsValue(artifact,
+ // "home_number");
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBeforeUpdate,
+ // currentHeatEnvParamBeforeUpdate.equals(currentHeatEnvParamAfterUpdate));
+ //
+ // //update heatEnv with invalid value in boolean field
+ //
+ // artifact = getCurrentArtifactDefinitionFromService();
+ // currentHeatEnvParamBeforeUpdate = replaceHeatParamsValue(artifact,
+ // "private_building", "Tel Aviv");
+ // artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // res = ArtifactRestUtils.updateDeploymentArtifactToRI(artReq,
+ // sdncDesignerDetails, resourceInstance.getUniqueId(),
+ // service.getUniqueId());
+ //
+ // // validate negative response
+ // variables = Arrays.asList("HEAT_ENV", "boolean", "private_building");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ // variables, res.getResponse());
+ //
+ // //validate current value not affected
+ // artifact = getCurrentArtifactDefinitionFromService();
+ // currentHeatEnvParamAfterUpdate = getHeatParamsValue(artifact,
+ // "private_building");
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBeforeUpdate,
+ // currentHeatEnvParamBeforeUpdate.equals(currentHeatEnvParamAfterUpdate));
+ //
+ //
+ //
+ //
+ // //update heatEnv with invalid value in boolean field
+ //
+ // artifact = getCurrentArtifactDefinitionFromService();
+ // currentHeatEnvParamBeforeUpdate = replaceHeatParamsValue(artifact,
+ // "city_name", "\uC2B5");
+ // artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // res = ArtifactRestUtils.updateDeploymentArtifactToRI(artReq,
+ // sdncDesignerDetails, resourceInstance.getUniqueId(),
+ // service.getUniqueId());
+ // // validate negative response
+ // variables = Arrays.asList("HEAT_ENV", "string", "city_name");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ // variables, res.getResponse());
+ //
+ // //validate current value not affected
+ //
+ // artifact = getCurrentArtifactDefinitionFromService();
+ // currentHeatEnvParamAfterUpdate = getHeatParamsValue(artifact,
+ // "city_name");
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBeforeUpdate,
+ // currentHeatEnvParamBeforeUpdate.equals(currentHeatEnvParamAfterUpdate));
+ // }
+ //
+ // @Test
+ // public void heatValidateHeatArtFiledTypes_UpdateFailed() throws Exception
+ // {
+ //
+ //
+ // RestResponse checkOutResponse =
+ // LifecycleRestUtils.changeResourceState(resourceSC, sdncDesignerDetails,
+ // LifeCycleStatesEnum.CHECKOUT);
+ // AssertJUnit.assertTrue("response code is not 200, returned: " +
+ // checkOutResponse.getErrorCode(),checkOutResponse.getErrorCode() == 200);
+ // //get relevant service
+ // RestResponse resourceGetResponse =
+ // ResourceRestUtils.getResource(resourceSC, sdncDesignerDetails);
+ // Resource resource =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ //
+ // //update heatEnv with invalid value in number field
+ //
+ // ArtifactDefinition artifact = getCurrentArtifactDefinitionFromResource();
+ // String currentHeatEnvParamBeforeUpdate = replaceHeatParamsValue(artifact,
+ // "home_number", "Tel Aviv");
+ // ArtifactReqDetails artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // RestResponse res =
+ // ArtifactRestUtils.updateInformationalArtifactToResource(artReq,
+ // sdncDesignerDetails, resource.getUniqueId());
+ // // validate negative response
+ // List<String> variables = Arrays.asList("HEAT", "number", "home_number");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ // variables, res.getResponse());
+ //
+ // //validate current value not affected
+ //
+ // artifact = getCurrentArtifactDefinitionFromResource();
+ // String currentHeatEnvParamAfterUpdate = getHeatParamsValue(artifact,
+ // "home_number");
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBeforeUpdate,
+ // currentHeatEnvParamBeforeUpdate.equals(currentHeatEnvParamAfterUpdate));
+ //
+ //
+ // //update heatEnv with invalid value in boolean field
+ //
+ // artifact = getCurrentArtifactDefinitionFromResource();
+ // currentHeatEnvParamBeforeUpdate = replaceHeatParamsValue(artifact,
+ // "private_building", "Tel Aviv");
+ // artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // res = ArtifactRestUtils.updateDeploymentArtifactToResource(artReq,
+ // sdncDesignerDetails, resource.getUniqueId());
+ // // validate negative response
+ // variables = Arrays.asList("HEAT", "boolean", "private_building");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ // variables, res.getResponse());
+ //
+ // //validate current value not affected
+ //
+ // artifact = getCurrentArtifactDefinitionFromResource();
+ //
+ // currentHeatEnvParamAfterUpdate = getHeatParamsValue(artifact,
+ // "private_building");
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBeforeUpdate,
+ // currentHeatEnvParamBeforeUpdate.equals(currentHeatEnvParamAfterUpdate));
+ //
+ //
+ // //update heatEnv with invalid value in boolean field
+ //
+ // artifact = getCurrentArtifactDefinitionFromResource();
+ // currentHeatEnvParamBeforeUpdate = replaceHeatParamsValue(artifact,
+ // "city_name", "\uC2B5");
+ //
+ // artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // res = ArtifactRestUtils.updateDeploymentArtifactToResource(artReq,
+ // sdncDesignerDetails, resource.getUniqueId());
+ // // validate negative response
+ // variables = Arrays.asList("HEAT", "string", "city_name");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ // variables, res.getResponse());
+ //
+ // //validate current value not affected
+ //
+ // artifact = getCurrentArtifactDefinitionFromResource();
+ // currentHeatEnvParamAfterUpdate = getHeatParamsValue(artifact,
+ // "city_name");
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBeforeUpdate,
+ // currentHeatEnvParamBeforeUpdate.equals(currentHeatEnvParamAfterUpdate));
+ //
+ // }
+ //
+ // @Test
+ // public void heatEnvValidateHeatArtFiledTypes_boolNormalization_suc()
+ // throws Exception {
+ //
+ // //get relevant service
+ // RestResponse serviceGetResponse =
+ // ServiceRestUtils.getService(serviceDetails2, sdncDesignerDetails);
+ // Service service =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ // List<ComponentInstance> resourceInstances =
+ // service.getComponentInstances();
+ // ComponentInstance resourceInstance = resourceInstances.get(0);
+ //
+ // String defaultParam = null;
+ // String currentHeatEnvParamBefore = null;
+ // String currentHeatEnvParamAfter = null;
+ //
+ //
+ // List<String> listOfBoolValuesToValidate = Arrays.asList("T", "on", "1",
+ // "yes", "y");
+ //
+ // for (String element : listOfBoolValuesToValidate) {
+ //
+ // //update heatEnv with invalid value in boolean field
+ //
+ // ArtifactDefinition artifact = getCurrentArtifactDefinitionFromService();
+ // List<HeatParameterDefinition> heatParameters =
+ // artifact.getHeatParameters();
+ // for(HeatParameterDefinition param : heatParameters){
+ // if (param.getName().equals("private_building")){
+ // defaultParam = param.getDefaultValue();
+ // currentHeatEnvParamBefore = param.getCurrentValue();
+ // param.setCurrentValue(element);
+ // }
+ // }
+ // artifact.setHeatParameters(heatParameters);
+ // ArtifactReqDetails artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // RestResponse res = ArtifactRestUtils.updateDeploymentArtifactToRI(artReq,
+ // sdncDesignerDetails, resourceInstance.getUniqueId(),
+ // service.getUniqueId());
+ // AssertJUnit.assertTrue("response code is not 200, returned: " +
+ // res.getErrorCode(), res.getErrorCode() == 200);
+ //
+ // //validate current value not affected
+ //
+ // artifact = getCurrentArtifactDefinitionFromService();
+ // heatParameters = artifact.getHeatParameters();
+ // for(HeatParameterDefinition param : heatParameters){
+ // if (param.getName().equals("private_building")){
+ // currentHeatEnvParamAfter = param.getCurrentValue();
+ // }
+ // }
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBefore, currentHeatEnvParamAfter.equals("true"));
+ //
+ // }
+ //
+ // }
+ //
+ //
+ // @Test
+ // public void heatValidateHeatArtFiledTypes_boolNormalization_suc() throws
+ // Exception {
+ //
+ // RestResponse checkOutResponse =
+ // LifecycleRestUtils.changeResourceState(resourceSC, sdncDesignerDetails,
+ // LifeCycleStatesEnum.CHECKOUT);
+ // AssertJUnit.assertTrue("response code is not 200, returned: " +
+ // checkOutResponse.getErrorCode(),checkOutResponse.getErrorCode() == 200);
+ // //get relevant service
+ // RestResponse resourceGetResponse =
+ // ResourceRestUtils.getResource(resourceSC, sdncDesignerDetails);
+ // Resource resource =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ //
+ //
+ // String defaultParam = null;
+ // String currentHeatEnvParamBefore = null;
+ // String currentHeatEnvParamAfter = null;
+ //
+ //
+ // List<String> listOfBoolValuesToValidate = Arrays.asList("T", "on", "1",
+ // "yes", "y");
+ //
+ // for (String element : listOfBoolValuesToValidate) {
+ //
+ // //update heatEnv with invalid value in boolean field
+ //
+ // ArtifactDefinition artifact = getCurrentArtifactDefinitionFromResource();
+ // List<HeatParameterDefinition> heatParameters =
+ // artifact.getHeatParameters();
+ // for(HeatParameterDefinition param : heatParameters){
+ // if (param.getName().equals("private_building")){
+ // defaultParam = param.getDefaultValue();
+ // currentHeatEnvParamBefore = param.getCurrentValue();
+ // param.setCurrentValue(element);
+ // }
+ // }
+ // artifact.setHeatParameters(heatParameters);
+ // ArtifactReqDetails artReq =
+ // ResponseParser.convertArtifactDefinitionToArtifactReqDetailsObject(artifact);
+ // RestResponse res =
+ // ArtifactRestUtils.updateInformationalArtifactToResource(artReq,
+ // sdncDesignerDetails, resource.getUniqueId());
+ // AssertJUnit.assertTrue("response code is not 200, returned: " +
+ // res.getErrorCode(), res.getErrorCode() == 200);
+ //
+ // //validate current value not affected
+ //
+ // artifact = getCurrentArtifactDefinitionFromResource();
+ // heatParameters = artifact.getHeatParameters();
+ // for(HeatParameterDefinition param : heatParameters){
+ // if (param.getName().equals("private_building")){
+ // currentHeatEnvParamAfter = param.getCurrentValue();
+ // }
+ // }
+ // AssertJUnit.assertTrue("HeatEnvParam was not updated: " +
+ // currentHeatEnvParamBefore, currentHeatEnvParamAfter.equals("true"));
+ //
+ // }
+ //
+ // }
+ //
+ //
+ // private ArtifactDefinition getCurrentArtifactDefinitionFromResource()
+ // throws Exception {
+ //
+ // RestResponse resourceGetResponse =
+ // ResourceRestUtils.getResource(resourceSC, sdncDesignerDetails);
+ // Resource resource =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ //
+ // ArtifactDefinition artifactDefinition =
+ // resource.getDeploymentArtifacts().get("heat");
+ //
+ //
+ // return artifactDefinition;
+ //
+ // }
+ //
+ //
+ // private ArtifactDefinition getCurrentArtifactDefinitionFromService()
+ // throws Exception {
+ //
+ // RestResponse serviceGetResponse =
+ // ServiceRestUtils.getService(serviceDetails2, sdncDesignerDetails);
+ // Service service =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ // ArtifactDefinition artifactDefinition = new ArtifactDefinition();
+ // artifactDefinition =
+ // service.getComponentInstances().get(0).getDeploymentArtifacts().get("heatenv");
+ //
+ //
+ // return artifactDefinition;
+ //
+ // }
+ //
+ // private String replaceHeatParamsValue(ArtifactDefinition artifact, String
+ // paramName, String paramValue) {
+ // String defaultParam;
+ // String currentHeatEnvParam = null;
+ // List<HeatParameterDefinition> heatParameters =
+ // artifact.getHeatParameters();
+ // for(HeatParameterDefinition param : heatParameters){
+ // if (param.getName().equals(paramName)){
+ // defaultParam = param.getDefaultValue();
+ // currentHeatEnvParam = param.getCurrentValue();
+ // param.setCurrentValue(paramValue);
+ // }
+ // }
+ // artifact.setHeatParameters(heatParameters);
+ // return currentHeatEnvParam;
+ // }
+ //
+ // private String getHeatParamsValue(ArtifactDefinition artifact,
+ // String paramName) {
+ // List<HeatParameterDefinition> heatParameters =
+ // artifact.getHeatParameters();
+ // String currentHeatEnvParamValue = null;
+ // for(HeatParameterDefinition param : heatParameters){
+ // if (param.getName().equals(paramName)){
+ // currentHeatEnvParamValue = param.getCurrentValue();
+ // }
+ // }
+ // return currentHeatEnvParamValue;
+ // }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsTypes.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsTypes.java
new file mode 100644
index 0000000000..09cda0b73c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/artifacts/ValidateHeatArtFieldsTypes.java
@@ -0,0 +1,176 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.artifacts;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.RespJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ArtifactValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+public class ValidateHeatArtFieldsTypes extends ComponentBaseTest {
+
+ protected User sdncDesignerDetails;
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+
+ private static final String heatExtension = "yaml";
+ private static final String yangXmlExtension = "xml";
+ private static final String muranoPkgExtension = "zip";
+ private final String folderName = "yamlFieldsValidation";
+
+ private final String uuidString = RespJsonKeysEnum.UUID.getRespJsonKeyName().toString();
+
+ public ValidateHeatArtFieldsTypes() {
+ super(name, ValidateHeatArtFieldsTypes.class.getName());
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ @Test
+ public void validateHeatArtFiledTypes() throws Exception {
+
+ // get relevant resource and service
+
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ resourceDetails = ElementFactory.getDefaultResource();
+
+ RestResponse response = ResourceRestUtils.createResource(resourceDetails, sdncDesignerDetails);
+ AssertJUnit.assertTrue("create request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 201);
+
+ // add artifact to resource1
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ List<String> listOfArtifactFromFolder = ArtifactValidationUtils.getListOfArtifactFromFolder(folderName);
+ for (int i = 0; i < listOfArtifactFromFolder.size(); i++) {
+ heatArtifactDetails = ArtifactValidationUtils.replaceDefaultArtWithArtFromList(heatArtifactDetails,
+ heatExtension, folderName, i);
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails,
+ resourceDetails.getUniqueId());
+
+ if (heatArtifactDetails.getArtifactName().contains("bool")) {
+ if (heatArtifactDetails.getArtifactName().contains("negative")) {
+ // validate negative response
+ List<String> variables = Arrays.asList("HEAT", "boolean", "city_name");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ variables, response.getResponse());
+ }
+ if (heatArtifactDetails.getArtifactName().contains("positive")) {
+ AssertJUnit.assertTrue(
+ "add HEAT artifact to resource request returned status:" + response.getErrorCode()
+ + " fileName: " + heatArtifactDetails.getArtifactName(),
+ response.getErrorCode() == 200);
+ ArtifactDefinition artifactDefinitionJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(response.getResponse());
+ List<HeatParameterDefinition> heatParameters = artifactDefinitionJavaObject.getHeatParameters();
+ String currentValue = null;
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ if (heatParameterDefinition.getName().equals("city_name")) {
+ currentValue = heatParameterDefinition.getCurrentValue();
+ }
+ }
+ if (heatArtifactDetails.getArtifactName().contains("true")) {
+ AssertJUnit.assertTrue(currentValue.equals("true"));
+ }
+ if (heatArtifactDetails.getArtifactName().contains("false")) {
+ AssertJUnit.assertTrue(currentValue.equals("false"));
+ }
+ RestResponse deleteInformationalArtifactFromResource = ArtifactRestUtils
+ .deleteInformationalArtifactFromResource(resourceDetails.getUniqueId(), heatArtifactDetails,
+ sdncDesignerDetails);
+ AssertJUnit.assertTrue(
+ "delete HEAT artifact from resource request returned status:"
+ + deleteInformationalArtifactFromResource.getErrorCode(),
+ deleteInformationalArtifactFromResource.getErrorCode() == 200);
+ }
+
+ } else if (heatArtifactDetails.getArtifactName().contains("number")) {
+ if (heatArtifactDetails.getArtifactName().contains("negative")) {
+ // validate negative response
+ List<String> variables = Arrays.asList("HEAT", "number", "city_name");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ variables, response.getResponse());
+ }
+ if (heatArtifactDetails.getArtifactName().contains("positive")) {
+ AssertJUnit.assertTrue(
+ "add HEAT artifact to resource request returned status:" + response.getErrorCode()
+ + " fileName: " + heatArtifactDetails.getArtifactName(),
+ response.getErrorCode() == 200);
+ }
+
+ } else if (heatArtifactDetails.getArtifactName().contains("string")) {
+ if (heatArtifactDetails.getArtifactName().contains("negative")) {
+ // validate negative response
+ List<String> variables = Arrays.asList("HEAT", "string", "city_name");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_VALUE.name(),
+ variables, response.getResponse());
+ }
+ if (heatArtifactDetails.getArtifactName().contains("positive")) {
+ AssertJUnit.assertTrue(
+ "add HEAT artifact to resource request returned status:" + response.getErrorCode()
+ + " fileName: " + heatArtifactDetails.getArtifactName(),
+ response.getErrorCode() == 200);
+ }
+
+ }
+
+ else if (heatArtifactDetails.getArtifactName().contains("unsupported")) {
+
+ // validate negative response
+ List<String> variables = Arrays.asList("HEAT", "number123");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_HEAT_PARAMETER_TYPE.name(),
+ variables, response.getResponse());
+
+ }
+
+ else {
+ AssertJUnit.assertTrue(
+ "add HEAT artifact to resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ }
+ }
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/attribute/ComponentInstanceAttributeTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/attribute/ComponentInstanceAttributeTest.java
new file mode 100644
index 0000000000..58e27177d1
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/attribute/ComponentInstanceAttributeTest.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.attribute;
+
+import static org.junit.Assert.assertEquals;
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.swallowException;
+
+import java.io.File;
+import java.util.function.Function;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class ComponentInstanceAttributeTest extends ComponentBaseTest {
+
+ public static Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ComponentInstanceAttributeTest() {
+ super(name, ComponentInstanceAttributeTest.class.getName());
+ }
+
+ @Test
+ public void testUpdateAttributeOnResourceInstance() {
+ // Prepare VF with vfc instance with Attributes
+ String testResourcesPath = config.getResourceConfigDir() + File.separator + "importToscaResourceByCreateUrl";
+ final Resource vfcWithAttributes = AtomicOperationUtils.importResource(testResourcesPath, "CPWithAttributes.yml").left().value();
+ swallowException(() -> AtomicOperationUtils.changeComponentState(vfcWithAttributes, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, false));
+ Resource vf = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, false).left().value();
+ ComponentInstance vfcInstance = AtomicOperationUtils.addComponentInstanceToComponentContainer(vfcWithAttributes, vf).left().value();
+
+ // util method to get the specific attribute from the vf
+ Function<Resource, ComponentInstanceAttribute> attributeGetter = resourceVf -> resourceVf.getComponentInstancesAttributes().values().iterator().next().stream().filter(att -> att.getName().equals("private_address")).findAny().get();
+ // update attribute on vfc instance
+ final Resource vfWithInsatncePreUpdate = swallowException(() -> (Resource) AtomicOperationUtils.getCompoenntObject(vf, UserRoleEnum.DESIGNER));
+ ComponentInstanceAttribute attributeOfRI = attributeGetter.apply(vfWithInsatncePreUpdate);
+ final String newAttValue = "NewValue";
+ attributeOfRI.setValue(newAttValue);
+ String body = gson.toJson(attributeOfRI);
+ String url = String.format(Urls.UPDATE_ATTRIBUTE_ON_RESOURCE_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(), ComponentTypeEnum.findParamByType(ComponentTypeEnum.RESOURCE), vf.getUniqueId(), vfcInstance.getUniqueId());
+ swallowException(() -> BaseRestUtils.sendPost(url, body, UserRoleEnum.DESIGNER.getUserId(), BaseRestUtils.acceptHeaderData));
+ // Retrieve updated vf and verify attribute was updated
+ final Resource vfWithInsatncePostUpdate = swallowException(() -> (Resource) AtomicOperationUtils.getCompoenntObject(vf, UserRoleEnum.DESIGNER));
+ ComponentInstanceAttribute updatedAttribute = attributeGetter.apply(vfWithInsatncePostUpdate);
+ assertEquals(updatedAttribute.getValue(), newAttValue);
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CatalogDataApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CatalogDataApiTest.java
new file mode 100644
index 0000000000..d9df79350b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CatalogDataApiTest.java
@@ -0,0 +1,234 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.category;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class CatalogDataApiTest extends ComponentBaseTest {
+
+ protected Config config = Config.instance();
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";
+
+ @Rule
+ public static TestName name = new TestName();
+ protected User user;
+ protected RestResponse res1;
+ protected RestResponse res2;
+ protected RestResponse svc1;
+ protected ResourceReqDetails resourceDetails1;
+ protected ResourceReqDetails resourceDetails2;
+ protected ServiceReqDetails svcDetails1;
+
+ public CatalogDataApiTest() {
+ super(name, CatalogDataApiTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ user = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ resourceDetails1 = buildResourceDetails(user, "TestResource1");
+ resourceDetails2 = buildResourceDetails(user, "TestResource2");
+ svcDetails1 = buildServiceDetails("TestService1");
+
+ res1 = createResource(user, resourceDetails1);
+ AssertJUnit.assertEquals("create resorce failed", 201, res1.getErrorCode().intValue());
+ resourceDetails1.setUniqueId(ResponseParser.getUniqueIdFromResponse(res1));
+ resourceDetails2.setVersion(ResponseParser.getVersionFromResponse(res1));
+
+ res2 = createResource(user, resourceDetails2);
+ AssertJUnit.assertEquals("create resorce failed", 201, res2.getErrorCode().intValue());
+ resourceDetails2.setUniqueId(ResponseParser.getUniqueIdFromResponse(res2));
+ resourceDetails2.setVersion(ResponseParser.getVersionFromResponse(res2));
+
+ svc1 = createService(user, svcDetails1);
+ AssertJUnit.assertEquals("create resorce failed", 201, svc1.getErrorCode().intValue());
+ svcDetails1.setUniqueId(ResponseParser.convertServiceResponseToJavaObject(svc1.getResponse()).getUniqueId());
+ svcDetails1.setVersion(ResponseParser.convertServiceResponseToJavaObject(svc1.getResponse()).getVersion());
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ deleteResource(resourceDetails1.getUniqueId(), user.getUserId());
+ deleteResource(resourceDetails2.getUniqueId(), user.getUserId());
+ deleteService(svcDetails1.getUniqueId(), user);
+ }
+
+ // Keep 1
+ @Test
+ public void getCatalogData() throws Exception {
+
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails1, user, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("check in operation failed", 200, checkInResponse.getErrorCode().intValue());
+
+ RestResponse res = CatalogRestUtils.getCatalog(user.getUserId());
+ String json = res.getResponse();
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(json);
+ JSONArray resources = (JSONArray) jsonResp.get("resources");
+ JSONArray services = (JSONArray) jsonResp.get("services");
+
+ // Verify all the expected resources received.
+ AssertJUnit.assertTrue("check resource1 is in response",
+ isComponentInArray(resourceDetails1.getUniqueId(), resources));
+ AssertJUnit.assertTrue("check resource2 is in response",
+ isComponentInArray(resourceDetails2.getUniqueId(), resources));
+ AssertJUnit.assertTrue("check service1 is in response",
+ isComponentInArray(svcDetails1.getUniqueId(), services));
+
+ }
+
+ protected void deleteResource(String resourceUniqueId, String httpCspUserId) throws Exception {
+ RestResponse deleteResourceResponse = ResourceRestUtils.deleteResource(resourceUniqueId, httpCspUserId);
+
+ }
+
+ protected RestResponse createResource(User user, ResourceReqDetails resourceDetails) throws Exception {
+ deleteResource(resourceDetails.getName(), user.getUserId());
+ return ResourceRestUtils.createResource(resourceDetails, user);
+ }
+
+ protected ResourceReqDetails buildResourceDetails(User user, String resourceName) {
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.0";
+ String contactId = user.getUserId();
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_DATABASE.getCategory(),
+ ResourceCategoryEnum.GENERIC_DATABASE.getSubCategory());
+ return resourceDetails;
+ }
+
+ protected boolean isComponentInArray(String id, JSONArray component) {
+ for (int i = 0; i < component.size(); i++) {
+ JSONObject jobject = (JSONObject) component.get(i);
+ if (jobject.get("uniqueId").toString().equals(id.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected RestResponse createService(User user, ServiceReqDetails svcDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = getHeadersMap(user);
+
+ Gson gson = new Gson();
+ String body = gson.toJson(svcDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort());
+ RestResponse res = http.httpSendPost(url, body, headersMap);
+ // System.out.println("Create service was finished with response:
+ // "+res.getErrorCode());
+ return res;
+ }
+
+ protected Map<String, String> getHeadersMap(User user) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), user.getUserId());
+ return headersMap;
+ }
+
+ protected ServiceReqDetails buildServiceDetails(String serviceName) {
+ String description = "description";
+ ArrayList<String> serviceTags = new ArrayList<String>();
+ serviceTags.add("tag1");
+ serviceTags.add(serviceName);
+ String category = ServiceCategoriesEnum.MOBILITY.getValue();
+ String vendorName = "Oracle";
+ String vendorRelease = "0.1";
+ String contactId = "al1976";
+ String icon = "myIcon";
+
+ ServiceReqDetails svcdetails = new ServiceReqDetails(serviceName, category, serviceTags, description,
+ contactId, icon);
+ return svcdetails;
+ }
+
+ public RestResponse deleteService(String serviceId, User user) throws Exception {
+ HttpRequest httpRequest = new HttpRequest();
+ String url = String.format(Urls.DELETE_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ serviceId);
+
+ Map<String, String> headersMap = getHeadersMap(user);
+ RestResponse res = httpRequest.httpSendDelete(url, headersMap);
+ // System.out.println("Delete service was finished with response:
+ // "+res.getErrorCode());
+ return res;
+ }
+
+ public class NewObject {
+ private String _name;
+
+ public String getName() {
+ return _name;
+ }
+
+ public void setName(String name) {
+ this._name = name;
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesBaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesBaseTest.java
new file mode 100644
index 0000000000..d68f3f5582
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesBaseTest.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.category;
+
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+
+public abstract class CategoriesBaseTest extends ComponentBaseTest {
+
+ public CategoriesBaseTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ protected static final String AUDIT_SERVICE_TYPE = "Service";
+ protected static final String AUDIT_RESOURCE_TYPE = "Resource";
+ protected static final String AUDIT_PRODUCT_TYPE = "Product";
+ protected static final String GET_CATEGORY_HIERARCHY = "GetCategoryHierarchy";
+ protected static User sdncAdminUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ protected static User sdncAdminUserDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ protected static User sdncDesignerUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ protected static User sdncTesterUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ protected static User sdncGovernorUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ protected static User sdncOpsUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+ protected static User sdncProductManagerUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ protected static User sdncProductStrategistUserDetails = ElementFactory
+ .getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesTests.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesTests.java
new file mode 100644
index 0000000000..a3ee121611
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/CategoriesTests.java
@@ -0,0 +1,2289 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.category;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.PRODUCT_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.RESOURCE_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.SERVICE_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_ALREADY_EXISTS;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_CREATED;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_MISSING_INFORMATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.text.WordUtils;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.json.JSONArray;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedCategoryAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.CategoryValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CategoriesTests extends CategoriesBaseTest {
+
+ private static final String GET_CATEGORY_HIERARCHY = "GetCategoryHierarchy";
+ protected static final String ADD_CATEGORY = "AddCategory";
+ protected static final String DELETE_CATEGORY = "DeleteCategory";
+
+ public CategoriesTests() {
+ super(name, CategoriesTests.class.getName());
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+ private CategoryDefinition categoryDefinition;
+ private List<CategoryDefinition> categoryList;
+ private List<SubCategoryDefinition> subCategoryList;
+ private Map<String, List<String>> subCategoriesToDeleteMap;
+
+ @BeforeMethod
+ public void init() throws Exception {
+ subCategoriesToDeleteMap = new HashMap<String, List<String>>();
+ DbUtils.deleteFromEsDbByPattern("_all");
+
+ categoryDefinition = new CategoryDefinition();
+ categoryDefinition.setName("Abcd");
+ categoryList = defineCategories();
+ subCategoryList = defineSubCategories(categoryList.size());
+ }
+
+ // pass
+ @Test
+ public void createServiceCategorySuccessFlow() throws Exception {
+ // Add New category
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ // get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition); // also
+ // set
+ // catalog
+ // uniqeId
+
+ }
+
+ // pass
+ @Test
+ public void createResourceCategorySuccessFlow() throws Exception {
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get Category
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ // pass
+ @Test
+ public void createProductCategorySuccessFlow() throws Exception {
+ // Add Category by Product-strategist
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition,
+ sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+
+ // Get Category
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void CategoryNameValidation_FirstWordStartWithAlphaNumeric_01() throws Exception { // category
+ // for
+ // service
+ categoryDefinition.setName("Category14AadE &&&---+++.'''###=:@@@____");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category14AadE &-+.'#=:@_");
+ categoryDefinition.setNormalizedName("category14aade &-+.'#=:@_");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_FirstWordStartWithAlphaNumeric_02() throws Exception { // category
+ // for
+ // resource
+ categoryDefinition.setName("Category14AadE &&&---+++.'''###=:@@@____");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category14AadE &-+.'#=:@_");
+ categoryDefinition.setNormalizedName("category14aade &-+.'#=:@_");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_FirstWordStartWithAlphaNumeric_03() throws Exception { // category
+ // for
+ // resource
+ categoryDefinition.setName("Category14AadE &&&---+++.'''###=:@@@____");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition,
+ sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category14AadE &-+.'#=:@_");
+ categoryDefinition.setNormalizedName("category14aade &-+.'#=:@_");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ // pass
+ @Test
+ public void createServiceCategoryByNonAdminUser() throws Exception {
+ // Add New category
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition,
+ sdncProductStrategistUserDetails, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_RESTRICTED_OPERATION,
+ createCategotyRest.getErrorCode().intValue());
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.RESTRICTED_OPERATION, STATUS_CODE_RESTRICTED_OPERATION, AUDIT_SERVICE_TYPE);
+ }
+
+ // pass
+ @Test
+ public void createResourceCategoryByNonAdminUser() throws Exception {
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition,
+ sdncProductStrategistUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_RESTRICTED_OPERATION,
+ createCategotyRest.getErrorCode().intValue());
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.RESTRICTED_OPERATION, STATUS_CODE_RESTRICTED_OPERATION, AUDIT_RESOURCE_TYPE);
+ }
+
+ // pass
+ @Test
+ public void createProductCategoryByNonProductStrategistUser() throws Exception {
+ // Add New product category not by Product-Strategist
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_RESTRICTED_OPERATION,
+ createCategotyRest.getErrorCode().intValue());
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ ActionStatus.RESTRICTED_OPERATION, STATUS_CODE_RESTRICTED_OPERATION, AUDIT_PRODUCT_TYPE);
+
+ }
+
+ // pass
+ @Test
+ public void addCategoryByNonExistingUser() throws Exception {
+ User sdncAdminUserDetailsNonExisting = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncAdminUserDetailsNonExisting.setUserId("bt555h");
+ // Add New category
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition,
+ sdncAdminUserDetailsNonExisting, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_RESTRICTED_OPERATION,
+ createCategotyRest.getErrorCode().intValue());
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_CATEGORY);
+ expectedCatrgoryAuditJavaObject.setModifier("(" + sdncAdminUserDetailsNonExisting.getUserId() + ")");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDefinition.getName());
+ expectedCatrgoryAuditJavaObject.setSubCategoryName("");
+ expectedCatrgoryAuditJavaObject.setGroupingName("");
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_SERVICE_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_RESTRICTED_OPERATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_CATEGORY);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_01() throws Exception {
+ categoryDefinition.setName("1234AbcdE&");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde&"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_02() throws Exception {
+ categoryDefinition.setName("1234AbcdE-");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde-"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_03() throws Exception {
+ categoryDefinition.setName("1234AbcdE+");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde+"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_04() throws Exception {
+ categoryDefinition.setName("1234AbcdE.");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde."); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_05() throws Exception {
+ categoryDefinition.setName("1234AbcdE'");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde'");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_06() throws Exception {
+ categoryDefinition.setName("1234AbcdE=");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde="); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_07() throws Exception {
+ categoryDefinition.setName("1234AbcdE:");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde:"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_08() throws Exception {
+ categoryDefinition.setName("1234AbcdE@");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde@"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_09() throws Exception {
+ categoryDefinition.setName("1234AbcdE_");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde_"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_10() throws Exception {
+ categoryDefinition.setName("1234AbcdE#");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde#"); // normalization
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_11() throws Exception {
+ categoryDefinition.setName("1234AbcdE d");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde d"); // normalization
+ categoryDefinition.setName("1234AbcdE D");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void addServiceCategoryAllowedcharacters_12() throws Exception {
+ categoryDefinition.setName("1234AbcdE &_=+.-'#:@ d");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("1234abcde &_=+.-'#:@ d"); // normalization
+ categoryDefinition.setName("1234AbcdE &_=+.-'#:@ D");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveSpaceFromBeginning() throws Exception {
+ categoryDefinition.setName(" Category01");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("category01"); // normalization
+ categoryDefinition.setName("Category01");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveSpaceFromEnd() throws Exception {
+ categoryDefinition.setName("Category01 ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("category01"); // normalization
+ categoryDefinition.setName("Category01");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraSpace() throws Exception {
+ categoryDefinition.setName("Category 02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("category 02"); // normalization
+ categoryDefinition.setName("Category 02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraAmpersand() throws Exception {
+ categoryDefinition.setName("Category&& &02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("category& &02"); // normalization
+ categoryDefinition.setName("Category& &02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraDash() throws Exception {
+ categoryDefinition.setName("CategorY-- --02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("CategorY- -02");
+ categoryDefinition.setNormalizedName("category- -02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraPlus() throws Exception {
+ categoryDefinition.setName("CateGory++++ +02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("CateGory+ +02");
+ categoryDefinition.setNormalizedName("category+ +02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraPeriod() throws Exception {
+ categoryDefinition.setName("Category.... .02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category. .02");
+ categoryDefinition.setNormalizedName("category. .02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraApostrophe() throws Exception {
+ categoryDefinition.setName("CaTegory''' '02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("CaTegory' '02");
+ categoryDefinition.setNormalizedName("category' '02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraHashtag() throws Exception {
+ categoryDefinition.setName("Category### #02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category# #02");
+ categoryDefinition.setNormalizedName("category# #02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtrEequal() throws Exception {
+ categoryDefinition.setName("Category=== =02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category= =02");
+ categoryDefinition.setNormalizedName("category= =02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtrColon() throws Exception {
+ categoryDefinition.setName("Category::: :02");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category: :02");
+ categoryDefinition.setNormalizedName("category: :02");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtrAt() throws Exception {
+ categoryDefinition.setName("Category@@@ @a2");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category@ @a2");
+ categoryDefinition.setNormalizedName("category@ @a2");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_RemoveExtraUnderscore() throws Exception {
+ categoryDefinition.setName("Category___ _22");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Category_ _22");
+ categoryDefinition.setNormalizedName("category_ _22");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_FirstWordStartWithNumber() throws Exception {
+ categoryDefinition.setName("1Category one");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("1Category One");
+ categoryDefinition.setNormalizedName("1category one");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_FirstWordStartWithNonAlphaNumeric() throws Exception { // The
+ // first
+ // word
+ // must
+ // start
+ // with
+ // an
+ // alpha-numeric
+ // character
+ // [a-Z
+ // A..Z,
+ // 0..9]
+ char invalidChars[] = { '&', '-', '+', '.', '\'', '#', '=', ':', '@', '_' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ categoryDefinition.setName(invalidChars[i] + "AbcD123");
+ categoryDefinition.setNormalizedName((invalidChars[i] + "AbcD123").toLowerCase());
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition,
+ sdncAdminUserDetails1, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createCategotyRest.getErrorCode().intValue());
+
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails1,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE,
+ "Service", "category");
+
+ }
+ }
+
+ @Test
+ public void addServiceCategoryAlreadyExist_uniqueness() throws Exception { // Verify
+ // category
+ // name
+ // duplication
+ // ("uniqueness")
+ // as
+ // non-case-sensitive,
+ // so
+ // we
+ // don’t
+ // create
+ // duplicate
+ // names
+ // with
+ // upper/lower
+ // case
+ // inconsistency.
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition); // also
+ // set
+ // catalog
+ // uniqeId
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ // Create same category name again
+ DbUtils.deleteFromEsDbByPattern("_all");
+ CategoryDefinition categoryDataDefinition2 = new CategoryDefinition();
+ categoryDataDefinition2.setName(categoryDefinition.getName());
+ RestResponse addDuplicateCategoryRest = CategoryRestUtils.createCategory(categoryDataDefinition2,
+ sdncAdminUserDetails, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_ALREADY_EXISTS,
+ addDuplicateCategoryRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDataDefinition2, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_CATEGORY_ALREADY_EXISTS, STATUS_CODE_ALREADY_EXISTS, AUDIT_SERVICE_TYPE,
+ "Service", categoryDefinition.getName());
+ // Get Category and verify that category was created is not deleted
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+
+ }
+
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_01() throws Exception {
+ categoryDefinition.setName("At and T");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("At & T");
+ categoryDefinition.setNormalizedName("at & t");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_02() throws Exception {
+ categoryDefinition.setName("At and t");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("At & T");
+ categoryDefinition.setNormalizedName("at & t");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_03() throws Exception {
+ categoryDefinition.setName("Atand T");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("atand t");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_04() throws Exception {
+ categoryDefinition.setName("At andT");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("at andt");
+ categoryDefinition.setName("At AndT");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_05() throws Exception {
+ categoryDefinition.setName(" and AttT");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("and attt");
+ categoryDefinition.setName("And AttT");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_06() throws Exception {
+ categoryDefinition.setName("AttT and ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("attt and");
+ categoryDefinition.setName("AttT And");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ // Bug
+ @Test
+ public void categoryNameValidation_ReplaceAndWithAmpersand_07() throws Exception {
+ categoryDefinition.setName(" and a");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("and a");
+ categoryDefinition.setName("And a");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationMaxLength() throws Exception {
+ categoryDefinition.setName("AsdfghjQ234567890@#.&:+-_");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("asdfghjq234567890@#.&:+-_");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+
+ }
+
+ @Test
+ public void categoryNameValidationMaxLengthAfterNormalization() throws Exception {
+ categoryDefinition.setName(" A jQ234 @@@___ +++ At and T and and ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("A JQ234 @_ + At & T & And");
+ categoryDefinition.setNormalizedName("a jq234 @_ + at & t & and");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+
+ }
+
+ @Test
+ public void categoryNameValidationExceedMaxLengthAfterNormalization() throws Exception {
+ categoryDefinition.setName(" AbdfghBCVa jQ234 @@___ +++ At and T ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("abdfghbcva jq234 @_ + at&t");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE,
+ "Service", "category");
+ }
+
+ @Test
+ public void categoryNameValidationMinLengthAfterNormalization() throws Exception { // MinLengthAfterNormalization
+ // =
+ // 4
+ // characters
+ categoryDefinition.setName(" At and T ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("At & T");
+ categoryDefinition.setNormalizedName("at & t");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationLessThanMinLengthAfterNormalization() throws Exception {
+ categoryDefinition.setName(" A&&&&&&&&&&&&&&&&&T ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("a&t");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE,
+ "Service", "category");
+ }
+
+ @Test
+ public void categoryNameValidationIsNull() throws Exception {
+ categoryDefinition.setName(null);
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createCategotyRest.getErrorCode().intValue());
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE,
+ "Service", "category");
+ }
+
+ @Test
+ public void categoryNameValidationIsEmpty() throws Exception {
+ categoryDefinition.setName("");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE,
+ "Service", "category");
+ }
+
+ @Test
+ public void categoryNameValidationInvalidCharacters() throws Exception {
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/',
+ '|', '\\', ',' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // DbUtils.cleanAllAudits();
+ categoryDefinition.setName("AbcD123" + invalidChars[i]);
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setNormalizedName("");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditFailure(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE,
+ "Service", "category");
+ }
+ }
+
+ @Test
+ public void categoryNameValidationSameNameDifferentResourceType() throws Exception { // same
+ // Catalog
+ // Name
+ // for
+ // service/resource/product
+ // is
+ // allowed
+ String name = ("Abcd");
+ CategoryDefinition categoryDataDefinition1 = new CategoryDefinition();
+ CategoryDefinition categoryDataDefinition2 = new CategoryDefinition();
+ CategoryDefinition categoryDataDefinition3 = new CategoryDefinition();
+ categoryDataDefinition1.setName(name);
+ categoryDataDefinition2.setName(name);
+ categoryDataDefinition3.setName(name);
+ // CREATE CATEGORY FOR SERVICE
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDataDefinition1,
+ sdncAdminUserDetails, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDataDefinition1.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDataDefinition1);
+ // get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDataDefinition1); // also
+ // set
+ // catalog
+ // uniqeId
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDataDefinition1, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ // CREATE CATEGORY FOR RESOURCE_COMPONENT_TYPE
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createCategotyRest = CategoryRestUtils.createCategory(categoryDataDefinition2, sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDataDefinition2.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDataDefinition2);
+ // Get Category
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDataDefinition2);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDataDefinition2, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ // CREATE CATEGORY FOR PRODUCT
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse addCategotyRest = CategoryRestUtils.createCategory(categoryDataDefinition3,
+ sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ addCategotyRest.getErrorCode().intValue());
+ categoryDataDefinition3.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateCategoryResponse(addCategotyRest, categoryDataDefinition3);
+
+ // Get Category
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDataDefinition3);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationFirstLetterOfKeyWordsCapitalized() throws Exception { // First
+ // letter
+ // of
+ // key
+ // words
+ // are
+ // capitalized
+ categoryDefinition.setName("beNNy shaY michEl");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("BeNNy ShaY MichEl");
+ categoryDefinition.setNormalizedName("benny shay michel");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_01() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" bank OF america ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Bank of America");
+ categoryDefinition.setNormalizedName("bank of america");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_02() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName("THE america bank ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("THE America Bank");
+ categoryDefinition.setNormalizedName("the america bank");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_03() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" A bank OF america ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("A Bank of America");
+ categoryDefinition.setNormalizedName("a bank of america");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_04() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" bank america is A big ban ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Bank America Is a Big Ban");
+ categoryDefinition.setNormalizedName("bank america is a big ban");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_05() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" aN apple comPany inC ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("AN Apple ComPany InC");
+ categoryDefinition.setNormalizedName("an apple company inc");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_06() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" eat AN apple ANAN");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Eat an Apple ANAN");
+ categoryDefinition.setNormalizedName("eat an apple anan");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_07() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" united states OF americA ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("United States of AmericA");
+ categoryDefinition.setNormalizedName("united states of america");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ // need to re-check
+ @Test
+ public void categoryNameValidationConjunctions_08() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" oF united states OF amer ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("OF United States of Amer");
+ categoryDefinition.setNormalizedName("of united states of amer");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_09() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" to Apple TO at&T TOO ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("To Apple to At&T TOO");
+ categoryDefinition.setNormalizedName("to apple to at&t too");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_10() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" eat apple AS you liiikeas ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Eat Apple as You Liiikeas");
+ categoryDefinition.setNormalizedName("eat apple as you liiikeas");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_11() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" as you may want ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("As You May Want");
+ categoryDefinition.setNormalizedName("as you may want");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void categoryNameValidationConjunctions_12() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" the bank OF america ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("The Bank of America");
+ categoryDefinition.setNormalizedName("the bank of america");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ // need to recheck
+ @Test
+ public void categoryNameValidationConjunctions_13() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" To tel-toto ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("To Tel-toto");
+ categoryDefinition.setNormalizedName("to tel-toto");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ // recheck
+ @Test
+ public void categoryNameValidationConjunctions_14() throws Exception { // Normalize
+ // the
+ // category
+ // name
+ // conjunctions
+ // ('of',
+ // 'to',
+ // 'for',
+ // 'as',
+ // 'a',
+ // 'an'
+ // ,
+ // 'the')
+ // are
+ // lower
+ // case.
+ categoryDefinition.setName(" tel-aviv To la ");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_CREATED,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Tel-aviv to La");
+ categoryDefinition.setNormalizedName("tel-aviv to la");
+ CategoryValidationUtils.validateCreateCategoryResponse(createCategotyRest, categoryDefinition);
+ // Get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ AuditValidationUtils.categoryAuditSuccess(ADD_CATEGORY, categoryDefinition, sdncAdminUserDetails,
+ STATUS_CODE_CREATED, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void createServiceCategoryHttpCspUserIdIsEmpty() throws Exception {
+ User sdncAdminUserDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncAdminUserDetails1.setUserId("");
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Abcd");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_CATEGORY);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDefinition.getName());
+ expectedCatrgoryAuditJavaObject.setSubCategoryName("");
+ expectedCatrgoryAuditJavaObject.setGroupingName("");
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_SERVICE_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_CATEGORY);
+ }
+
+ @Test
+ public void createServiceCategorHttpCspUserIdIsNull() throws Exception {
+ User sdncAdminUserDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncAdminUserDetails1.setUserId(null);
+ RestResponse createCategotyRest = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails1,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createCategotyRest.getErrorCode().intValue());
+ categoryDefinition.setName("Abcd");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_CATEGORY);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDefinition.getName());
+ expectedCatrgoryAuditJavaObject.setSubCategoryName("");
+ expectedCatrgoryAuditJavaObject.setGroupingName("");
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_SERVICE_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_CATEGORY);
+ }
+
+ @Test
+ public void createSrvcCategoryHttpCspUserIdHeaderIsMissing() throws Exception {
+ RestResponse createConsumerRest = CategoryRestUtils
+ .createServiceCategoryHttpCspAtuUidIsMissing(categoryDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createConsumerRest.getErrorCode().intValue());
+ categoryDefinition.setName("Abcd");
+ // get service category and validate that category was not added
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyCategoryNotExistsInGetResponse(getAllCategoriesRest, categoryDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_CATEGORY);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDefinition.getName());
+ expectedCatrgoryAuditJavaObject.setSubCategoryName("");
+ expectedCatrgoryAuditJavaObject.setGroupingName("");
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_SERVICE_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_CATEGORY);
+ }
+
+ @Test
+ public void getServiceCategoryHierarchySuccessFlow() throws Exception {
+
+ int numOfCategories = 3;
+ List<CategoryDefinition> categories = new ArrayList<CategoryDefinition>();
+ RestResponse restResponse;
+ CategoryDefinition category;
+ String categoryName = categoryDefinition.getName();
+ for (int i = 0; i < numOfCategories; i++) {
+ categoryDefinition.setName(categoryName + i);
+ restResponse = CategoryRestUtils.createCategory(categoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ category = ResponseParser.parseToObject(restResponse.getResponse(), CategoryDefinition.class);
+ categories.add(category);
+ }
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+
+ AuditValidationUtils.GetCategoryHierarchyAuditSuccess(GET_CATEGORY_HIERARCHY, AUDIT_SERVICE_TYPE,
+ sdncAdminUserDetails, STATUS_CODE_SUCCESS);
+ for (CategoryDefinition categoryCurr : categories) {
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryCurr);
+ }
+ }
+
+ ///////////////////////////////// US570520 /////////////////////////////////
+ private List<CategoryDefinition> defineCategories() throws Exception {
+ String firstCategory = "FirstCategory";
+ String secondCategory = "secondCategory";
+ String thirdCategory = "ThirdCategory";
+ String forthCategory = "forthCategory";
+ CategoryDefinition category1 = new CategoryDefinition(categoryDefinition);
+ category1.setName(firstCategory);
+ CategoryDefinition category2 = new CategoryDefinition(categoryDefinition);
+ category2.setName(secondCategory);
+ CategoryDefinition category3 = new CategoryDefinition(categoryDefinition);
+ category3.setName(thirdCategory);
+ CategoryDefinition category4 = new CategoryDefinition(categoryDefinition);
+ category4.setName(forthCategory);
+ ArrayList<CategoryDefinition> categoryList = new ArrayList<CategoryDefinition>();
+ categoryList.add(category1);
+ categoryList.add(category2);
+ categoryList.add(category3);
+ categoryList.add(category4);
+ return categoryList;
+ }
+
+ @Test
+ public void getAllResourceCategoriesHirarchy() throws Exception {
+ createAndValidateCategoriesExist(RESOURCE_COMPONENT_TYPE, categoryList);
+
+ for (int i = 0; i < categoryList.size(); i++) {
+ List<String> subCategorieUniqueIdList = new ArrayList<String>();
+ for (int j = 0; j < subCategoryList.size(); j++) {
+ RestResponse createSubCategory = CategoryRestUtils.createSubCategory(subCategoryList.get(j),
+ categoryList.get(i), sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ if (createSubCategory.getErrorCode().intValue() == STATUS_CODE_CREATED) {
+ String subCategoryUniqeId = ResponseParser.getUniqueIdFromResponse(createSubCategory);
+ subCategorieUniqueIdList.add(subCategoryUniqeId);
+ subCategoriesToDeleteMap.put(categoryList.get(i).getUniqueId(), subCategorieUniqueIdList);
+ }
+ }
+ }
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+
+ for (int i = 0; i < categoryList.size(); i++) {
+ for (int j = 0; j < subCategoryList.size(); j++) {
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ categoryList.get(i).getUniqueId(), subCategoryList.get(j));
+ }
+ }
+
+ checkAuditSuccess(RESOURCE_COMPONENT_TYPE);
+ }
+
+ private List<SubCategoryDefinition> defineSubCategories(int catListSize) {
+ List<SubCategoryDefinition> subCatList = new ArrayList<SubCategoryDefinition>();
+ for (int j = 1; j <= catListSize; j++) {
+ SubCategoryDefinition subCategory = new SubCategoryDefinition();
+ subCategory.setName("SubCategory" + String.valueOf(j));
+ subCatList.add(subCategory);
+ }
+ return subCatList;
+ }
+
+ private void createAndValidateCategoriesExist(String comp, List<CategoryDefinition> categoryList) throws Exception {
+ createCategories(comp, categoryList);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, comp);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ verifyCategoriesExist(categoryList, getAllCategoriesRest);
+ }
+
+ private void verifyCategoriesExist(List<CategoryDefinition> categoryList, RestResponse getAllCategoriesRest) {
+ for (int i = 0; i < categoryList.size(); i++) {
+ categoryList.get(i).setName(WordUtils.capitalize(categoryList.get(i).getName()));
+ CategoryValidationUtils.verifyCategoryExistInGetResponse(getAllCategoriesRest, categoryList.get(i));
+ }
+ }
+
+ private void createCategories(String comp, List<CategoryDefinition> categoryList) throws Exception {
+ for (int i = 0; i < categoryList.size(); i++) {
+ CategoryRestUtils.createCategory(categoryList.get(i), sdncAdminUserDetails, comp);
+ }
+ }
+
+ @Test
+ public void getAllServiceCategoriesHirarchy() throws Exception {
+ // deleteCategories(categoryList, SERVICE_COMPONENT_TYPE);
+ createAndValidateCategoriesExist(SERVICE_COMPONENT_TYPE, categoryList);
+ checkAuditSuccess(SERVICE_COMPONENT_TYPE);
+ // deleteCategories(categoryList, SERVICE_COMPONENT_TYPE);
+ }
+
+ @Test
+ public void getAllResourceCategories_noAttUserHeader() throws Exception {
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(new User(), RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", 403, getAllCategoriesRest.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), new ArrayList<String>(),
+ getAllCategoriesRest.getResponse());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(GET_CATEGORY_HIERARCHY);
+ expectedCatrgoryAuditJavaObject.setModifierName("");
+ expectedCatrgoryAuditJavaObject.setModifierUid("");
+ expectedCatrgoryAuditJavaObject.setDetails(RESOURCE_COMPONENT_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateGetCategoryHirarchy(expectedCatrgoryAuditJavaObject, GET_CATEGORY_HIERARCHY);
+ }
+
+ @Test
+ public void getAllResourceCategories_userNotProvisioned() throws Exception {
+ User notProvisionedUser = new User();
+ notProvisionedUser.setUserId("aa0001");
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(notProvisionedUser,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", 409, getAllCategoriesRest.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ getAllCategoriesRest.getResponse());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(GET_CATEGORY_HIERARCHY);
+ expectedCatrgoryAuditJavaObject.setModifierName("");
+ expectedCatrgoryAuditJavaObject.setModifierUid(notProvisionedUser.getUserId());
+ expectedCatrgoryAuditJavaObject.setDetails(RESOURCE_COMPONENT_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_RESTRICTED_OPERATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateGetCategoryHirarchy(expectedCatrgoryAuditJavaObject, GET_CATEGORY_HIERARCHY);
+ }
+
+ @Test
+ public void getAllResourceCategories_unsupportedComponent() throws Exception {
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, "comp");
+ assertEquals("Check response code after get all categories hirarchy", 400,
+ getAllCategoriesRest.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.UNSUPPORTED_ERROR.name(),
+ new ArrayList<String>(Arrays.asList("component type")), getAllCategoriesRest.getResponse());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.UNSUPPORTED_ERROR.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(GET_CATEGORY_HIERARCHY);
+ expectedCatrgoryAuditJavaObject.setModifierUid(sdncAdminUserDetails.getUserId());
+ expectedCatrgoryAuditJavaObject.setModifierName(sdncAdminUserDetails.getFullName());
+ expectedCatrgoryAuditJavaObject.setDetails("comp");
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_INVALID_CONTENT));
+ expectedCatrgoryAuditJavaObject.setDesc(AuditValidationUtils.buildAuditDescription(errorInfo,
+ new ArrayList<String>(Arrays.asList("component type"))));
+ AuditValidationUtils.validateGetCategoryHirarchy(expectedCatrgoryAuditJavaObject, GET_CATEGORY_HIERARCHY);
+ }
+
+ @Test(enabled = false)
+ public void getAllResourceCategories_emptyList() throws Exception {
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ JSONArray jArr = new JSONArray(getAllCategoriesRest.getResponse());
+ assertTrue(jArr.length() == 0);
+
+ checkAuditSuccess(RESOURCE_COMPONENT_TYPE);
+ }
+
+ private void checkAuditSuccess(String componentType) throws Exception {
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(GET_CATEGORY_HIERARCHY);
+ expectedCatrgoryAuditJavaObject.setModifierName(sdncAdminUserDetails.getFullName());
+ expectedCatrgoryAuditJavaObject.setModifierUid(sdncAdminUserDetails.getUserId());
+ expectedCatrgoryAuditJavaObject.setDetails(componentType);
+ expectedCatrgoryAuditJavaObject.setStatus("200");
+ expectedCatrgoryAuditJavaObject.setDesc("OK");
+ AuditValidationUtils.validateGetCategoryHirarchy(expectedCatrgoryAuditJavaObject, GET_CATEGORY_HIERARCHY);
+ }
+
+ @Test(enabled = false)
+ public void getAllServiceCategories_emptyList() throws Exception {
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ JSONArray jArr = new JSONArray(getAllCategoriesRest.getResponse());
+ assertTrue(jArr.length() == 0);
+
+ checkAuditSuccess(SERVICE_COMPONENT_TYPE);
+ }
+
+ @Test(enabled = false)
+ public void getAllProductCategories_emptyList() throws Exception {
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get Category", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ JSONArray jArr = new JSONArray(getAllCategoriesRest.getResponse());
+ assertTrue(jArr.length() == 0);
+
+ checkAuditSuccess(PRODUCT_COMPONENT_TYPE);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ @Test
+ public void importCategories() throws Exception {
+
+ String importResourceDir = config.getImportTypesConfigDir() + File.separator + "categoryTypesTest.zip";
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+ mpBuilder.addPart("categoriesZip", new FileBody(new File(importResourceDir)));
+
+ RestResponse importResult = CategoryRestUtils.importCategories(mpBuilder, sdncAdminUserDetails.getUserId());
+ assertEquals("Check response code after Import", BaseRestUtils.STATUS_CODE_CREATED,
+ importResult.getErrorCode().intValue());
+
+ Map<String, Object> map = ResponseParser.parseToObjectUsingMapper(importResult.getResponse(), Map.class);
+ assertEquals("Check entries count", 2, map.size());
+
+ List<Map<String, Object>> resources = (List<Map<String, Object>>) map.get("resources");
+ assertEquals("Check resource category entries count", 1, resources.size());
+
+ List<Map<String, Object>> services = (List<Map<String, Object>>) map.get("services");
+ assertEquals("Check resource category entries count", 2, services.size());
+
+ RestResponse allCategories = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, "resources");
+ List<CategoryDefinition> resourceCategories = ResponseParser.parseCategories(allCategories);
+ for (Map<String, Object> resource : resources) {
+ boolean exist = false;
+
+ for (CategoryDefinition categ : resourceCategories) {
+ if (categ.getName().equals(resource.get("name"))) {
+ exist = true;
+ break;
+ }
+ }
+ assertTrue("Check existance resource category " + resource.get("name"), exist);
+ }
+
+ allCategories = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, "services");
+ List<CategoryDefinition> servicesCategories = ResponseParser.parseCategories(allCategories);
+ for (Map<String, Object> service : services) {
+ boolean exist = false;
+
+ for (CategoryDefinition categ : servicesCategories) {
+ if (categ.getName().equals(service.get("name"))) {
+ exist = true;
+ break;
+ }
+ }
+ assertTrue("Check existance service category " + service.get("name"), exist);
+ }
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/ElementsApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/ElementsApiTest.java
new file mode 100644
index 0000000000..7f30f8ca92
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/ElementsApiTest.java
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.category;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.testng.annotations.Test;
+
+public class ElementsApiTest extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ElementsApiTest() {
+ super(name, ElementsApiTest.class.getName());
+ }
+
+ // public LCSbaseTest(TestName testName, String className) {
+ // super(testName, className);
+ // }
+
+ // public ElementsApiTest(TestName name, String name2) {
+ //// super(name, name2);
+ // // TODO Auto-generated constructor stub
+ // }
+
+ // Expected 200 Keep
+ @Test
+ public void getAllPropertyScopesSuccess() throws Exception {
+ RestResponse response = ResourceRestUtils.getAllPropertyScopesTowardsCatalogBe();
+ String action = "Get All Property Scopes";
+ int expectedCode = 200;
+ verifyErrorCode(response, action, expectedCode);
+ }
+
+ // Expected 200 Keep
+ @Test
+ public void getAllArtifactTypes() throws Exception {
+ RestResponse response = ResourceRestUtils.getAllArtifactTypesTowardsCatalogBe();
+ String action = "Get All Artifact Types";
+ int expectedCode = 200;
+ verifyErrorCode(response, action, expectedCode);
+ }
+
+ // Expected 200 Keep
+ @Test
+ public void getConfiguration() throws Exception {
+ RestResponse response = ResourceRestUtils.getConfigurationTowardsCatalogBe();
+ String action = "Get All Artifact Types";
+ int expectedCode = 200;
+
+ String json = response.getResponse();
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(json);
+
+ HashMap<String, Object> artifacts = (HashMap<String, Object>) jsonResp.get("artifacts");
+ Long defaultHeatTimeout = (Long) jsonResp.get("defaultHeatTimeout");
+
+ if (defaultHeatTimeout == null) {
+ response.setErrorCode(500);
+ verifyErrorCode(response, action, expectedCode);
+ return;
+ }
+
+ if (artifacts == null) {
+ response.setErrorCode(500);
+ verifyErrorCode(response, action, expectedCode);
+ return;
+ }
+
+ JSONObject deploymentResources = (JSONObject) artifacts.get("deployment");
+ JSONArray otherResources = (JSONArray) artifacts.get("other");
+ if (deploymentResources == null || otherResources == null) {
+ response.setErrorCode(500);
+ verifyErrorCode(response, action, expectedCode);
+ return;
+ }
+
+ JSONArray roles = (JSONArray) jsonResp.get("roles");
+ if (roles == null) {
+ response.setErrorCode(500);
+ verifyErrorCode(response, action, expectedCode);
+ return;
+ }
+
+ }
+
+ public void verifyErrorCode(RestResponse response, String action, int expectedCode) {
+ assertNotNull("check response object is not null after " + action, response);
+ assertNotNull("check error code exists in response after " + action, response.getErrorCode());
+ assertEquals("Check response code after + action" + action, expectedCode, response.getErrorCode().intValue());
+ }
+
+ @Test(enabled = false)
+ public void getAllCategoriesSuccess() throws Exception {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ RestResponse response = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+ String action = "Get All Categories";
+ int expectedCode = 200;
+ verifyErrorCode(response, action, expectedCode);
+ }
+
+ @Test(enabled = false)
+ public void getAllTagSuccess() throws Exception {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ RestResponse response = ResourceRestUtils.getAllTagsTowardsCatalogBe();
+ String action = "Get All Categories";
+ int expectedCode = 200;
+ verifyErrorCode(response, action, expectedCode);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/GroupingTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/GroupingTest.java
new file mode 100644
index 0000000000..7858b3259f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/GroupingTest.java
@@ -0,0 +1,2003 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.category;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.PRODUCT_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.RESOURCE_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.SERVICE_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_ALREADY_EXISTS;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_CREATED;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_MISSING_INFORMATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_NOT_FOUND;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedCategoryAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.CategoryValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.SkipException;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GroupingTest extends CategoriesBaseTest {
+
+ protected static final String ADD_GROUPING = "AddGrouping";
+ protected static final String CATEGORY = "category";
+ protected static final String SUB_CATEGORY = "sub-category";
+ protected static final String GROUPING = "grouping";
+
+ public GroupingTest() {
+ super(name, GroupingTest.class.getName());
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ private CategoryDefinition productCategoryDefinition;
+ private CategoryDefinition productCategoryDefinition2;
+
+ private SubCategoryDefinition productSubCategoryDefinition;
+ private SubCategoryDefinition productSubCategoryDefinition2;
+ private SubCategoryDefinition productSubCategoryDefinition3;
+
+ private GroupingDefinition productGroupingDefinition;
+ private GroupingDefinition productGroupingDefinition2;
+ private GroupingDefinition productGroupingDefinition3;
+
+ @BeforeMethod
+ public void init() throws Exception {
+
+ // Category setup
+ productCategoryDefinition = new CategoryDefinition();
+ productCategoryDefinition.setName("Category1");
+ productCategoryDefinition2 = new CategoryDefinition();
+ productCategoryDefinition2.setName("Category2");
+
+ // Subcategory setup
+ productSubCategoryDefinition = new SubCategoryDefinition();
+ productSubCategoryDefinition.setName("SubCategory1");
+
+ productSubCategoryDefinition2 = new SubCategoryDefinition();
+ productSubCategoryDefinition2.setName("SubCategory2");
+
+ productSubCategoryDefinition3 = new SubCategoryDefinition();
+ productSubCategoryDefinition3.setName("SubCategory1");
+
+ // Group setup
+ productGroupingDefinition = new GroupingDefinition();
+ productGroupingDefinition.setName("Grouping1");
+
+ productGroupingDefinition2 = new GroupingDefinition();
+ productGroupingDefinition2.setName("Grouping2");
+
+ productGroupingDefinition3 = new GroupingDefinition();
+ productGroupingDefinition3.setName("Grouping1");
+
+ // Init product category
+ RestResponse createCategory = CategoryRestUtils.createCategory(productCategoryDefinition,
+ sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ CategoryDefinition category = ResponseParser.parseToObject(createCategory.getResponse(),
+ CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productCategoryDefinition.getName(),
+ category.getName());
+ productCategoryDefinition = category;
+
+ // Init product category1
+ createCategory = CategoryRestUtils.createCategory(productCategoryDefinition2, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ category = ResponseParser.parseToObject(createCategory.getResponse(), CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productCategoryDefinition2.getName(),
+ category.getName());
+ productCategoryDefinition2 = category;
+
+ // Init product productSubCategoryDefinition to
+ // productCategoryDefinition
+ RestResponse createSubCategory = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createSubCategory.getErrorCode().intValue());
+ SubCategoryDefinition subCategory = ResponseParser.parseToObject(createSubCategory.getResponse(),
+ SubCategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productSubCategoryDefinition.getName(),
+ subCategory.getName());
+ productSubCategoryDefinition = subCategory;
+ productCategoryDefinition.addSubCategory(productSubCategoryDefinition);
+
+ // Init product productSubCategoryDefinition1 to
+ // productCategoryDefinition
+ createSubCategory = CategoryRestUtils.createSubCategory(productSubCategoryDefinition2,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createSubCategory.getErrorCode().intValue());
+ subCategory = ResponseParser.parseToObject(createSubCategory.getResponse(), SubCategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productSubCategoryDefinition2.getName(),
+ subCategory.getName());
+ productSubCategoryDefinition2 = subCategory;
+ productCategoryDefinition.addSubCategory(productSubCategoryDefinition2);
+
+ // Init product productSubCategoryDefinition3 to
+ // productCategoryDefinition2
+ createSubCategory = CategoryRestUtils.createSubCategory(productSubCategoryDefinition3,
+ productCategoryDefinition2, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createSubCategory.getErrorCode().intValue());
+ subCategory = ResponseParser.parseToObject(createSubCategory.getResponse(), SubCategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productSubCategoryDefinition3.getName(),
+ subCategory.getName());
+ productSubCategoryDefinition3 = subCategory;
+ productCategoryDefinition2.addSubCategory(productSubCategoryDefinition3);
+ }
+
+ @Test
+ public void createProductGroupCategorySuccess() throws Exception {
+ createGroupingSuccess(productGroupingDefinition, productSubCategoryDefinition, productCategoryDefinition,
+ sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE, AUDIT_PRODUCT_TYPE);
+ }
+
+ private void createGroupingSuccess(GroupingDefinition groupingDefinition,
+ SubCategoryDefinition subCategoryDefinition, CategoryDefinition categoryDefinition,
+ User sdncProductStrategistUserDetails, String productComponentType, String auditType) throws Exception {
+
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(groupingDefinition, subCategoryDefinition,
+ categoryDefinition, sdncProductStrategistUserDetails, productComponentType);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("grouping1");
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ productComponentType);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest, categoryDefinition.getUniqueId(),
+ subCategoryDefinition.getUniqueId(), groupingDefinition);
+
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, categoryDefinition, subCategoryDefinition,
+ groupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, auditType);
+ }
+
+ //// Benny
+
+ @Test
+ public void createProductGroupByProductStrategist() throws Exception {
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("grouping1");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void createProductGroupAlreadyExistInSameCategorySubCategory() throws Exception {
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("grouping1");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ // Create Same Group already exist on same Category/SubCategory
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition, productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_ALREADY_EXISTS,
+ createGroupingRest.getErrorCode().intValue());
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY, STATUS_CODE_ALREADY_EXISTS, AUDIT_PRODUCT_TYPE,
+ AUDIT_PRODUCT_TYPE, productGroupingDefinition.getName(), productSubCategoryDefinition.getName());
+ }
+
+ @Test
+ public void createProductGroupUnderSameCategoryButDifferentSubCategory() throws Exception {
+ // Setting : Category-A, Sub-category-B , group : aBcd (display-Name :
+ // ABcd, normalized: abcd)  [A, B, ABcd]
+ // Action : Category-A, Sub-category-C, group : abcD (display-Name :
+ // ABcd, normalized: abcd)  [A, C, ABcd]
+ productGroupingDefinition.setName("ABCd");
+ productGroupingDefinition2.setName("abcD");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition2, productSubCategoryDefinition2,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition2.setName("ABCd");
+ productGroupingDefinition2.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition2);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition,
+ productSubCategoryDefinition2, productGroupingDefinition2, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition2.getUniqueId(),
+ productGroupingDefinition2);
+ }
+
+ @Test
+ public void createProductGroupUnderSameSubCategoryButDifferentCategory() throws Exception {
+ // Setting : Category-A, Sub-category-B , group : aBcd (display-Name :
+ // ABcd, normalized: abcd)  [A, B, ABcd]
+ // : Category-A, Sub-category-C, group : abcD (display-Name : ABcd,
+ // normalized: abcd)  [A, C, ABcd]
+ // : Category-K, Sub-category-B, group : abcD (display-Name : ABcd,
+ // normalized: abcd)  [K, B, ABcd]
+ productGroupingDefinition.setName("ABCd");
+ productGroupingDefinition2.setName("abcD");
+ productGroupingDefinition3.setName("aBCd");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition2, productSubCategoryDefinition2,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition2.setName("ABCd");
+ productGroupingDefinition2.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition2);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition,
+ productSubCategoryDefinition2, productGroupingDefinition2, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition3, productSubCategoryDefinition3,
+ productCategoryDefinition2, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition3.setName("ABCd");
+ productGroupingDefinition3.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition3);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition2,
+ productSubCategoryDefinition3, productGroupingDefinition3, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition2.getUniqueId(),
+ productGroupingDefinition2);
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition2.getUniqueId(), productSubCategoryDefinition3.getUniqueId(),
+ productGroupingDefinition3);
+
+ }
+
+ @Test
+ public void createProductGroupsOnSameCategorySubCategory() throws Exception {
+ // Setting : Category-A, Sub-category-B , group : ABcd (display-Name :
+ // ABcd, normalized: abcd) [A ,B, ABcd]
+ // Action : Category-A, Sub-category-B, group : ZXcv (display-Name :
+ // ZXcv, normalized: zxcv) [A, B, ZXcv]
+ productGroupingDefinition.setName("ABcd");
+ productGroupingDefinition2.setName("ZXcv");
+ productGroupingDefinition2.setNormalizedName("zxcv");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition2, productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition2);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition2, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition2);
+ }
+
+ @Test
+ public void createProductGroupUnderDifferentCategory() throws Exception {
+ // Setting : Category-A, Sub-category-B , group : aBcd (display-Name :
+ // ABcd, normalized: abcd) [A ,B, ABcd]
+ // Action : Category-K, Sub-category-B, group : abcD (display-Name :
+ // ABcd, normalized: abcd) [K, B, ABcd]
+ // productGroupingDefinition.setName("ABCd");
+ productGroupingDefinition.setName("ABcD");
+ productGroupingDefinition2.setName("abcD");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("abcd");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition2, productSubCategoryDefinition3,
+ productCategoryDefinition2, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition2.setNormalizedName("abcd");
+ productGroupingDefinition2.setName("ABcD");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition2);
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition2,
+ productSubCategoryDefinition3, productGroupingDefinition2, sdncProductStrategistUserDetails,
+ STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition2.getUniqueId(), productSubCategoryDefinition3.getUniqueId(),
+ productGroupingDefinition2);
+ }
+
+ ///////////
+ @Test
+ public void createProductGroupByNonProductStrategist() throws Exception {
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_RESTRICTED_OPERATION,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ productSubCategoryDefinition.setName(productSubCategoryDefinition.getUniqueId());
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncAdminUserDetails, ActionStatus.RESTRICTED_OPERATION,
+ STATUS_CODE_RESTRICTED_OPERATION, AUDIT_PRODUCT_TYPE);
+ }
+
+ // @Ignore("DE176245")
+ @Test
+ public void createProductGroupForNonExistingComponentType() throws Exception {
+ String nonSupportedComponentType = "NonExistingComponentType"; // instead
+ // resource/product
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ nonSupportedComponentType);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ productSubCategoryDefinition.setName(productSubCategoryDefinition.getUniqueId());
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, nonSupportedComponentType);
+ }
+
+ // @Ignore("DE176245")
+ @Test
+ public void createResourceGroup() throws Exception {
+ // Resource doesn't have group
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ productSubCategoryDefinition.setName(productSubCategoryDefinition.getUniqueId());
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_RESOURCE_TYPE);
+ }
+
+ // @Ignore("DE176245")
+ @Test
+ public void createServiceGroup() throws Exception {
+ // Service doesn't have group
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ productSubCategoryDefinition.setName(productSubCategoryDefinition.getUniqueId());
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void createProductGroupForNonExistingCategory() throws Exception {
+
+ CategoryDefinition productCategoryDefinition100 = new CategoryDefinition();
+ productCategoryDefinition100.setName("category.nonexistingCategory");
+ productCategoryDefinition100.setUniqueId("category.nonexistingCategory");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition100, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_NOT_FOUND,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ productSubCategoryDefinition.setName(productSubCategoryDefinition.getUniqueId());
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition100,
+ productSubCategoryDefinition, productGroupingDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_CATEGORY_NOT_FOUND, STATUS_CODE_NOT_FOUND, AUDIT_PRODUCT_TYPE,
+ PRODUCT_COMPONENT_TYPE, CATEGORY, "");
+ }
+
+ @Test
+ public void createProductGroupForNonExistingSunCategory() throws Exception {
+ throw new SkipException(
+ "Skipping - failed in audit validation expected \"products\" actual result was \"product\" ");
+ // SubCategoryDefinition productSubCategoryDefinition100 = new
+ // SubCategoryDefinition();
+ // productSubCategoryDefinition100.setUniqueId("category.nonexistingSubCategory");
+ // RestResponse createGroupingRest =
+ // CategoryRestUtils.createGrouping(productGroupingDefinition,
+ // productSubCategoryDefinition100, productCategoryDefinition,
+ // sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ // assertEquals("Check response code after create Sub category",
+ // STATUS_CODE_NOT_FOUND, createGroupingRest.getErrorCode().intValue());
+ // RestResponse getAllCategoriesRest =
+ // CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ // PRODUCT_COMPONENT_TYPE);
+ // assertEquals("Check response code after get all categories ",
+ // STATUS_CODE_SUCCESS, getAllCategoriesRest.getErrorCode().intValue());
+ // CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ // productCategoryDefinition.getUniqueId(),
+ // productSubCategoryDefinition.getUniqueId(),
+ // productGroupingDefinition);
+ // //Audit validation
+ // productSubCategoryDefinition100.setName(productSubCategoryDefinition100.getUniqueId());
+ // AuditValidationUtils.groupingAuditFailure(ADD_GROUPING ,
+ // productCategoryDefinition, productSubCategoryDefinition100,
+ // productGroupingDefinition, sdncProductStrategistUserDetails,
+ // ActionStatus.COMPONENT_CATEGORY_NOT_FOUND,
+ // STATUS_CODE_NOT_FOUND,AUDIT_PRODUCT_TYPE, PRODUCT_COMPONENT_TYPE,
+ // SUB_CATEGORY, "");
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_01() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE-");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde-");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_02() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE+");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde+");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_03() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE&");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde&");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_04() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE-");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde-");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_05() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE+");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde+");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_06() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE.");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde.");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_07() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE'");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde'");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_08() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE=");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde=");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_09() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE:");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde:");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_10() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE@");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde@");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_11() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE_");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde_");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_12() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE#");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("1234abcde#");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void ProductGroupAllowedcharacters_13() throws Exception {
+ productGroupingDefinition.setName("1234AbcdE d");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("1234AbcdE D");
+ productGroupingDefinition.setNormalizedName("1234abcde d");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveSpaceFromBeginning() throws Exception {
+ productGroupingDefinition.setName(" Category01");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("category01");
+ productGroupingDefinition.setName("Category01");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveSpaceFromEnd() throws Exception {
+ productGroupingDefinition.setName("Category01 ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("category01");
+ productGroupingDefinition.setName("Category01");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraSpace() throws Exception {
+ productGroupingDefinition.setName("Category 02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("category 02");
+ productGroupingDefinition.setName("Category 02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraAmpersand() throws Exception {
+ productGroupingDefinition.setName("Category&& &02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("category& &02");
+ productGroupingDefinition.setName("Category& &02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraDash() throws Exception {
+ productGroupingDefinition.setName("CategorY-- --02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("category- -02");
+ productGroupingDefinition.setName("CategorY- -02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraPlus() throws Exception {
+ productGroupingDefinition.setName("CateGory++++ +02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("CateGory+ +02");
+ productGroupingDefinition.setNormalizedName("category+ +02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraPeriod() throws Exception {
+ productGroupingDefinition.setName("Category.... .02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Category. .02");
+ productGroupingDefinition.setNormalizedName("category. .02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraApostrophe() throws Exception {
+ productGroupingDefinition.setName("CaTegory''' '02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("CaTegory' '02");
+ productGroupingDefinition.setNormalizedName("category' '02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraHashtag() throws Exception {
+ productGroupingDefinition.setName("Category### #02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Category# #02");
+ productGroupingDefinition.setNormalizedName("category# #02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtrEequal() throws Exception {
+ productGroupingDefinition.setName("Category=== =02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Category= =02");
+ productGroupingDefinition.setNormalizedName("category= =02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtrColon() throws Exception {
+ productGroupingDefinition.setName("Category::: :02");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Category: :02");
+ productGroupingDefinition.setNormalizedName("category: :02");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtrAt() throws Exception {
+ productGroupingDefinition.setName("Category@@@ @a2");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Category@ @a2");
+ productGroupingDefinition.setNormalizedName("category@ @a2");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_RemoveExtraUnderscore() throws Exception {
+ productGroupingDefinition.setName("Category___ _22");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Category_ _22");
+ productGroupingDefinition.setNormalizedName("category_ _22");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_FirstWordStartWithNumber() throws Exception {
+ productGroupingDefinition.setName("1Category one");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("1Category One");
+ productGroupingDefinition.setNormalizedName("1category one");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_FirstWordStartWithNonAlphaNumeric() throws Exception { // The
+ // first
+ // word
+ // must
+ // start
+ // with
+ // an
+ // alpha-numeric
+ // character
+ // [a-Z
+ // A..Z,
+ // 0..9]
+ char invalidChars[] = { '&', '-', '+', '.', '\'', '#', '=', ':', '@', '_' };
+ RestResponse createGroupingRest;
+ RestResponse getAllCategoriesRest;
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ productGroupingDefinition.setName(invalidChars[i] + "AbcD123");
+ productGroupingDefinition.setNormalizedName((invalidChars[i] + "AbcD123").toLowerCase());
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ }
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_01() throws Exception {
+ productGroupingDefinition.setName("At and T");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("At & T");
+ productGroupingDefinition.setNormalizedName("at & t");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_02() throws Exception {
+ productGroupingDefinition.setName("At and t");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("At & T");
+ productGroupingDefinition.setNormalizedName("at & t");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_03() throws Exception {
+ productGroupingDefinition.setName("Atand T");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("atand t");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_04() throws Exception {
+ productGroupingDefinition.setName("At andT");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("at andt");
+ productGroupingDefinition.setName("At AndT");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_05() throws Exception {
+ productGroupingDefinition.setName(" and AttT");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("and attt");
+ productGroupingDefinition.setName("And AttT");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_06() throws Exception {
+ productGroupingDefinition.setName("AttT and ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("attt and");
+ productGroupingDefinition.setName("AttT And");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidation_ReplaceAndWithAmpersand_07() throws Exception {
+ productGroupingDefinition.setName(" and a");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("and a");
+ productGroupingDefinition.setName("And a");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationMaxLength() throws Exception {
+ productGroupingDefinition.setName("AsdfghjQ234567890@#.&:+-_");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("asdfghjq234567890@#.&:+-_");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationMaxLengthAfterNormalization() throws Exception {
+ productGroupingDefinition.setName(" A jQ234 @@@___ +++ At and T and and ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("a jq234 @_ + at & t & and");
+ productGroupingDefinition.setName("A JQ234 @_ + At & T & And");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationExceedMaxLengthAfterNormalization() throws Exception {
+ productGroupingDefinition.setName(" AbdfghBCVa jQ234 @@___ +++ At and T ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("abdfghbcva jq234 @_ + at&t");
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, STATUS_CODE_INVALID_CONTENT, AUDIT_PRODUCT_TYPE,
+ AUDIT_PRODUCT_TYPE, GROUPING);
+ }
+
+ @Test
+ public void groupNameValidationMinLengthAfterNormalization() throws Exception {
+ productGroupingDefinition.setName(" At&&&&&&&&&&&&t ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("at&t");
+ productGroupingDefinition.setName("At&t");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationLessThanMinLengthAfterNormalization() throws Exception {
+ productGroupingDefinition.setName(" A&&&&&&&&&&&&T ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("a&t");
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, STATUS_CODE_INVALID_CONTENT, AUDIT_PRODUCT_TYPE,
+ AUDIT_PRODUCT_TYPE, GROUPING);
+ }
+
+ @Test
+ public void groupNameValidationIsEmpty() throws Exception {
+ productGroupingDefinition.setName("");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("");
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT, AUDIT_PRODUCT_TYPE,
+ AUDIT_PRODUCT_TYPE, GROUPING);
+ }
+
+ @Test
+ public void groupNameValidationInvalidCharacters() throws Exception {
+ RestResponse createGroupingRest;
+ RestResponse getAllCategoriesRest;
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/',
+ '|', '\\', ',' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ productGroupingDefinition.setName("AbcD123" + invalidChars[i]);
+ createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_INVALID_CONTENT,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("");
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditFailure(ADD_GROUPING, productCategoryDefinition,
+ productSubCategoryDefinition, productGroupingDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT, AUDIT_PRODUCT_TYPE,
+ AUDIT_PRODUCT_TYPE, GROUPING);
+ }
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_01() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" bank OF america ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setNormalizedName("bank of america");
+ productGroupingDefinition.setName("Bank of America");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_02() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName("THE america bank ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("THE America Bank");
+ productGroupingDefinition.setNormalizedName("the america bank");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_03() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" A bank OF america ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("A Bank of America");
+ productGroupingDefinition.setNormalizedName("a bank of america");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_04() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" bank america is A big ban ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Bank America Is a Big Ban");
+ productGroupingDefinition.setNormalizedName("bank america is a big ban");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_05() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" aN apple comPany inC ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("AN Apple ComPany InC");
+ productGroupingDefinition.setNormalizedName("an apple company inc");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_06() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" eat AN apple ANAN");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Eat an Apple ANAN");
+ productGroupingDefinition.setNormalizedName("eat an apple anan");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_07() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" united states OF americA ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("United States of AmericA");
+ productGroupingDefinition.setNormalizedName("united states of america");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_08() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" oF united states OF amer ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("OF United States of Amer");
+ productGroupingDefinition.setNormalizedName("of united states of amer");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_09() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" to Apple TO at&T TOO ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("To Apple to At&T TOO");
+ productGroupingDefinition.setNormalizedName("to apple to at&t too");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_10() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" eat apple AS you liiikeas ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Eat Apple as You Liiikeas");
+ productGroupingDefinition.setNormalizedName("eat apple as you liiikeas");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_11() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" as you may want ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("As You May Want");
+ productGroupingDefinition.setNormalizedName("as you may want");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_12() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" the bank OF america ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("The Bank of America");
+ productGroupingDefinition.setNormalizedName("the bank of america");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_13() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" To tel-toto ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("To Tel-toto");
+ productGroupingDefinition.setNormalizedName("to tel-toto");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void groupNameValidationConjunctions_14() throws Exception {
+ // Normalize the grouping name conjunctions ('of', 'to', 'for', 'as',
+ // 'a', 'an' , 'the') are lower case.
+ productGroupingDefinition.setName(" tel-aviv To la ");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create product group", STATUS_CODE_CREATED,
+ createGroupingRest.getErrorCode().intValue());
+ productGroupingDefinition.setName("Tel-aviv to La");
+ productGroupingDefinition.setNormalizedName("tel-aviv to la");
+ CategoryValidationUtils.validateCreateGroupResponse(createGroupingRest, productGroupingDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ AuditValidationUtils.groupingAuditSuccess(ADD_GROUPING, productCategoryDefinition, productSubCategoryDefinition,
+ productGroupingDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void createProductGroupHttpCspUserIdIsEmpty() throws Exception {
+ User sdncPS = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+ sdncPS.setUserId("");
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncPS, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_GROUPING);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(productCategoryDefinition.getUniqueId());
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(productSubCategoryDefinition.getUniqueId());
+ expectedCatrgoryAuditJavaObject.setGroupingName(productGroupingDefinition.getName());
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_PRODUCT_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_GROUPING);
+ }
+
+ @Test
+ public void createProductGroupHttpCspUserIdIsNull() throws Exception {
+ User sdncPS = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+ sdncPS.setUserId(null);
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(productGroupingDefinition,
+ productSubCategoryDefinition, productCategoryDefinition, sdncPS, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createGroupingRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifyGroupingNotExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition.getUniqueId(),
+ productGroupingDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_GROUPING);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(productCategoryDefinition.getUniqueId());
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(productSubCategoryDefinition.getUniqueId());
+ expectedCatrgoryAuditJavaObject.setGroupingName(productGroupingDefinition.getName());
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_PRODUCT_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_GROUPING);
+ }
+
+ ////////////////////////////////////////////////
+ ///////////////////////////////////////////////
+ @Test
+ public void getProductCategoryHierarchySuccessFlow() throws Exception {
+ throw new SkipException(
+ "Skipping - failed in audit validation expected \"products\" actual result was \"product\" ");
+ // int numOfGrouping = 3;
+ // List<GroupingDefinition> groupingList = new ArrayList<>();
+ // RestResponse restResponse;
+ // GroupingDefinition grouping;
+ // String groupingName = productGroupingDefinition.getName();
+ // for (int i = 0; i < numOfGrouping; i++) {
+ // productGroupingDefinition.setName(groupingName+i);
+ // restResponse =
+ // CategoryRestUtils.createGrouping(productGroupingDefinition,
+ // productSubCategoryDefinition, productCategoryDefinition,
+ // sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ // grouping = ResponseParser.parseToObject(restResponse.getResponse(),
+ // GroupingDefinition.class);
+ // groupingList.add(grouping);
+ // }
+ // RestResponse getAllCategoriesRest =
+ // CategoryRestUtils.getAllCategories(sdncProductStrategistUserDetails,
+ // PRODUCT_COMPONENT_TYPE);
+ // assertEquals("Check response code after get all categories ",
+ // STATUS_CODE_SUCCESS, getAllCategoriesRest.getErrorCode().intValue());
+ // AuditValidationUtils.GetCategoryHierarchyAuditSuccess(GET_CATEGORY_HIERARCHY,
+ // AUDIT_PRODUCT_TYPE, sdncProductStrategistUserDetails,
+ // STATUS_CODE_SUCCESS);
+ //
+ // for (GroupingDefinition group : groupingList) {
+ // CategoryValidationUtils.verifyGroupingExistInGetResponse(getAllCategoriesRest,
+ // productCategoryDefinition.getUniqueId(),
+ // productSubCategoryDefinition.getUniqueId(), group);
+ // }
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/SubCategoriesTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/SubCategoriesTest.java
new file mode 100644
index 0000000000..dd96ebfc2f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/category/SubCategoriesTest.java
@@ -0,0 +1,1907 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.category;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.PRODUCT_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.RESOURCE_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.SERVICE_COMPONENT_TYPE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_ALREADY_EXISTS;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_CREATED;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_MISSING_INFORMATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_NOT_FOUND;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedCategoryAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.CategoryValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class SubCategoriesTest extends ComponentBaseTest {
+
+ protected static final String ADD_SUB_CATEGORY = "AddSubCategory";
+ protected static final String CATEGORY = "category";
+ protected static final String SUB_CATEGORY = "sub-category";
+
+ protected static final String AUDIT_SERVICE_TYPE = "Service";
+ protected static final String AUDIT_RESOURCE_TYPE = "Resource";
+ protected static final String AUDIT_PRODUCT_TYPE = "Product";
+ protected static final String GET_CATEGORY_HIERARCHY = "GetCategoryHierarchy";
+ protected static User sdncAdminUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ protected static User sdncAdminUserDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ protected static User sdncDesignerUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ protected static User sdncTesterUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ protected static User sdncGovernorUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ protected static User sdncOpsUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+ protected static User sdncProductManagerUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ protected static User sdncProductStrategistUserDetails = ElementFactory
+ .getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+
+ public SubCategoriesTest() {
+ super(name, SubCategoriesTest.class.getName());
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ private CategoryDefinition resourceCategoryDefinition;
+ private CategoryDefinition resourceCategoryDefinition1;
+ private CategoryDefinition serviceCategoryDefinition;
+ private CategoryDefinition productCategoryDefinition;
+ private CategoryDefinition productCategoryDefinition1;
+ private CategoryDefinition resourceCategoryDefinition100;
+ private CategoryDefinition productCategoryDefinition200;
+
+ private SubCategoryDefinition resourceSubCategoryDefinition;
+ private SubCategoryDefinition resourceSubCategoryDefinition1;
+ private SubCategoryDefinition serviceSubCategoryDefinition;
+ private SubCategoryDefinition productSubCategoryDefinition;
+ private SubCategoryDefinition productSubCategoryDefinition1;
+
+ @BeforeMethod
+ public void init() throws Exception {
+
+ // Category setup
+ resourceCategoryDefinition = new CategoryDefinition();
+ resourceCategoryDefinition1 = new CategoryDefinition();
+ serviceCategoryDefinition = new CategoryDefinition();
+ productCategoryDefinition = new CategoryDefinition();
+ productCategoryDefinition1 = new CategoryDefinition();
+ resourceCategoryDefinition100 = new CategoryDefinition(); // for
+ // negative
+ // tests
+ productCategoryDefinition200 = new CategoryDefinition(); // for negative
+ // tests
+
+ resourceCategoryDefinition.setName("Category1");
+ resourceCategoryDefinition1.setName("Category2");
+ serviceCategoryDefinition.setName("Category1");
+ productCategoryDefinition.setName("Category2");
+ productCategoryDefinition1.setName("Category3");
+ resourceCategoryDefinition100.setName("Category100");
+ productCategoryDefinition200.setName("Category100");
+
+ // Subcategory setup
+ resourceSubCategoryDefinition = new SubCategoryDefinition();
+ resourceSubCategoryDefinition1 = new SubCategoryDefinition();
+ serviceSubCategoryDefinition = new SubCategoryDefinition();
+ productSubCategoryDefinition = new SubCategoryDefinition();
+ productSubCategoryDefinition1 = new SubCategoryDefinition();
+
+ resourceSubCategoryDefinition.setName("Resource-subcat");
+ // Service sub - for negative testing since it's not allowed
+ serviceSubCategoryDefinition.setName("Service-subcat");
+ productSubCategoryDefinition.setName("Product-subcat");
+
+ // Init resource category
+ RestResponse createCategory = CategoryRestUtils.createCategory(resourceCategoryDefinition, sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ CategoryDefinition category = ResponseParser.parseToObject(createCategory.getResponse(),
+ CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", resourceCategoryDefinition.getName(),
+ category.getName());
+ resourceCategoryDefinition = category;
+
+ // Init resource category1
+ createCategory = CategoryRestUtils.createCategory(resourceCategoryDefinition1, sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ category = ResponseParser.parseToObject(createCategory.getResponse(), CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", resourceCategoryDefinition1.getName(),
+ category.getName());
+ resourceCategoryDefinition1 = category;
+
+ // Init service category
+ createCategory = CategoryRestUtils.createCategory(serviceCategoryDefinition, sdncAdminUserDetails,
+ SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ category = ResponseParser.parseToObject(createCategory.getResponse(), CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", serviceCategoryDefinition.getName(),
+ category.getName());
+ serviceCategoryDefinition = category;
+
+ // Init product category
+ createCategory = CategoryRestUtils.createCategory(productCategoryDefinition, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ category = ResponseParser.parseToObject(createCategory.getResponse(), CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productCategoryDefinition.getName(),
+ category.getName());
+ productCategoryDefinition = category;
+
+ // Init product category1
+ createCategory = CategoryRestUtils.createCategory(productCategoryDefinition1, sdncProductStrategistUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create category", STATUS_CODE_CREATED,
+ createCategory.getErrorCode().intValue());
+ category = ResponseParser.parseToObject(createCategory.getResponse(), CategoryDefinition.class);
+ assertEquals("Check category name after creating category ", productCategoryDefinition1.getName(),
+ category.getName());
+ productCategoryDefinition1 = category;
+
+ }
+
+ @Test
+ public void createResourceSubCategorySuccess() throws Exception {
+ createSubCategorySuccess(resourceCategoryDefinition, resourceSubCategoryDefinition, sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void createProductSubCategorySuccess() throws Exception {
+ createSubCategorySuccess(productCategoryDefinition, productSubCategoryDefinition,
+ sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void createProductSubCategoryTwoCategoriesCaseInsensitive() throws Exception {
+ String componentType = PRODUCT_COMPONENT_TYPE;
+ String auditType = AUDIT_PRODUCT_TYPE;
+ User user = sdncProductStrategistUserDetails;
+ // Create product sub Category2-->Product-subcat
+ createSubCategorySuccess(productCategoryDefinition, productSubCategoryDefinition, user, componentType,
+ auditType);
+ DbUtils.deleteFromEsDbByPattern("_all");
+
+ // Create product sub Category3-->PRoDUCT-SUBcat
+ // Should be created Category3-->Product-subcat
+ productSubCategoryDefinition1.setName("PRoDUCT-SUBcat");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition1,
+ productCategoryDefinition1, user, componentType);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(user, componentType);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ productSubCategoryDefinition1.setName(productSubCategoryDefinition.getName());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition1.getUniqueId(), productSubCategoryDefinition1);
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, productCategoryDefinition1,
+ productSubCategoryDefinition1, user, STATUS_CODE_CREATED, auditType);
+ }
+
+ // Benny
+ @Test
+ public void createResourceSubCategoryAlreadyExistInDifferentResourceCategory() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ resourceSubCategoryDefinition1.setName("ResourcE-subCat");
+ createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition1,
+ resourceCategoryDefinition1, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition1.setName(resourceSubCategoryDefinition.getName());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition1.getUniqueId(), resourceSubCategoryDefinition1); // also
+ // set
+ // catalog
+ // uniqeId
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition1,
+ resourceSubCategoryDefinition1, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void createProductSubCategoryAlreadyExistInDifferentProductCategory() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED,
+ AUDIT_PRODUCT_TYPE);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ productSubCategoryDefinition1.setName("PRoDUCT-SUBcat");
+ createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition1,
+ productCategoryDefinition1, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ productSubCategoryDefinition1.setName(productSubCategoryDefinition.getName());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition1.getUniqueId(), productSubCategoryDefinition1); // also
+ // set
+ // catalog
+ // uniqeId
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, productCategoryDefinition1,
+ productSubCategoryDefinition1, sdncProductStrategistUserDetails, STATUS_CODE_CREATED,
+ AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void createResourceSubCategoryAlreadyExistInCategory() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ resourceSubCategoryDefinition1.setName("ResourcE-subCat");
+ createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition1,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_ALREADY_EXISTS,
+ createSubCategoryRest.getErrorCode().intValue());
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition1, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY, STATUS_CODE_ALREADY_EXISTS,
+ AUDIT_RESOURCE_TYPE, AUDIT_RESOURCE_TYPE, resourceSubCategoryDefinition1.getName(),
+ resourceCategoryDefinition.getName());
+ }
+
+ @Test
+ public void createProductSubCategoryAlreadyExistInCategory() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED,
+ AUDIT_PRODUCT_TYPE);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ productSubCategoryDefinition1.setName("ProducT-subCat");
+ createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition1,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_ALREADY_EXISTS,
+ createSubCategoryRest.getErrorCode().intValue());
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition1, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY, STATUS_CODE_ALREADY_EXISTS, AUDIT_PRODUCT_TYPE,
+ AUDIT_PRODUCT_TYPE, productSubCategoryDefinition1.getName(), productCategoryDefinition.getName());
+ }
+
+ @Test
+ public void addSameNormalizedSubCategoryNameForRecourceAndProductCategory() throws Exception {
+ // add sub-categoty name "SubCaT" to resource category
+ // add sub-categoty name "SUbcAt" to product category
+ resourceSubCategoryDefinition.setName("SubCaT"); // normalized 'subcat'
+ productSubCategoryDefinition.setName("SUbcAt"); // normalized 'subcat'
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncProductStrategistUserDetails, STATUS_CODE_CREATED,
+ AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void createResourceSubCategoryByNonAdminUser() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncTesterUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_RESTRICTED_OPERATION,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ resourceCategoryDefinition.setName(resourceCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncTesterUserDetails, ActionStatus.RESTRICTED_OPERATION,
+ STATUS_CODE_RESTRICTED_OPERATION, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void createResourceSubCategoryByProducStrategistUser() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncProductStrategistUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_RESTRICTED_OPERATION,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ resourceCategoryDefinition.setName(resourceCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncProductStrategistUserDetails, ActionStatus.RESTRICTED_OPERATION,
+ STATUS_CODE_RESTRICTED_OPERATION, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void createProductSubCategoryByNonProducStrategistUser() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncDesignerUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_RESTRICTED_OPERATION,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncDesignerUserDetails, ActionStatus.RESTRICTED_OPERATION,
+ STATUS_CODE_RESTRICTED_OPERATION, AUDIT_PRODUCT_TYPE);
+ }
+
+ @Test
+ public void createProductSubCategoryByAdminUser() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncAdminUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_RESTRICTED_OPERATION,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.RESTRICTED_OPERATION,
+ STATUS_CODE_RESTRICTED_OPERATION, AUDIT_PRODUCT_TYPE);
+ }
+
+ // @Ignore("DE176245")
+ @Test
+ public void createResourceSubCategoryForNonExistingComponentType() throws Exception {
+ String nonSupportedComponentType = "NonExistingComponentType"; // instead
+ // resource/product
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, nonSupportedComponentType);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ resourceCategoryDefinition.setName(resourceCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, nonSupportedComponentType);
+ }
+
+ // @Ignore("DE176245")
+ @Test
+ public void createProductSubCategoryForNonExistingComponentType() throws Exception {
+ String nonSupportedComponentType = "NonExistingComponentType"; // instead
+ // resource/product
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, nonSupportedComponentType);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncProductStrategistUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, nonSupportedComponentType);
+ }
+
+ @Test
+ public void createServiceSubCategoryByAdmin() throws Exception {
+ // Service doesn't have sub-category
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ resourceCategoryDefinition.setName(resourceCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void createServiceSubCategoryByProductStrategist() throws Exception {
+ // Service doesn't have sub-category
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition, sdncProductStrategistUserDetails, SERVICE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation
+ productCategoryDefinition.setName(productCategoryDefinition.getUniqueId());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, productCategoryDefinition,
+ productSubCategoryDefinition, sdncProductStrategistUserDetails, ActionStatus.INVALID_CONTENT,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_SERVICE_TYPE);
+ }
+
+ @Test
+ public void createResourceSubCategoryForNonExistingCategory() throws Exception {
+ resourceCategoryDefinition100.setUniqueId(resourceCategoryDefinition100.getName());
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition100, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_NOT_FOUND,
+ createSubCategoryRest.getErrorCode().intValue());
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition100,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.COMPONENT_CATEGORY_NOT_FOUND,
+ STATUS_CODE_NOT_FOUND, AUDIT_RESOURCE_TYPE, RESOURCE_COMPONENT_TYPE, CATEGORY, "");
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition100.getUniqueId(), resourceSubCategoryDefinition);
+ }
+
+ @Test
+ public void createProductSubCategoryForNonExistingCategory() throws Exception {
+ productCategoryDefinition200.setUniqueId(productCategoryDefinition200.getName());
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(productSubCategoryDefinition,
+ productCategoryDefinition200, sdncProductStrategistUserDetails, PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_NOT_FOUND,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ PRODUCT_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ productCategoryDefinition200.getUniqueId(), productSubCategoryDefinition);
+ // Audit validation // need to change ActionStatus
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, productCategoryDefinition200,
+ productSubCategoryDefinition, sdncProductStrategistUserDetails,
+ ActionStatus.COMPONENT_CATEGORY_NOT_FOUND, STATUS_CODE_NOT_FOUND, AUDIT_PRODUCT_TYPE,
+ PRODUCT_COMPONENT_TYPE, CATEGORY, "");
+ }
+
+ // pass
+ @Test
+ public void subCategoryAllowedcharacters_01() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE-");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition); // also
+ // set
+ // catalog
+ // uniqeId
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ // pass
+ @Test
+ public void subCategoryAllowedcharacters_02() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE+");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_03() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE&");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_04() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE.");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_05() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE'");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_06() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE=");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_07() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE:");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_08() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE@");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_09() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE_");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_10() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE#");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_11() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE d");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("1234AbcdE D");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryAllowedcharacters_12() throws Exception {
+ resourceSubCategoryDefinition.setName("1234AbcdE &_=+.-'#:@ d");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("1234AbcdE &_=+.-'#:@ D");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveSpaceFromBeginning() throws Exception {
+ resourceSubCategoryDefinition.setName(" Category01");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category01");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveSpaceFromEnd() throws Exception {
+ resourceSubCategoryDefinition.setName("Category01 ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category01");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraSpace() throws Exception {
+ resourceSubCategoryDefinition.setName("Category 02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category 02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraAmpersand() throws Exception {
+ resourceSubCategoryDefinition.setName("Category&& &02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category& &02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraDash() throws Exception {
+ resourceSubCategoryDefinition.setName("CategorY-- --02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("CategorY- -02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraPlus() throws Exception {
+ resourceSubCategoryDefinition.setName("CateGory++++ +02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("CateGory+ +02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraPeriod() throws Exception {
+ resourceSubCategoryDefinition.setName("Category.... .02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category. .02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraApostrophe() throws Exception {
+ resourceSubCategoryDefinition.setName("CaTegory''' '02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("CaTegory' '02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraHashtag() throws Exception {
+ resourceSubCategoryDefinition.setName("Category### #02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category# #02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtrEequal() throws Exception {
+ resourceSubCategoryDefinition.setName("Category=== =02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category= =02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtrColon() throws Exception {
+ resourceSubCategoryDefinition.setName("Category::: :02");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category: :02");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtrAt() throws Exception {
+ resourceSubCategoryDefinition.setName("Category@@@ @a2");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category@ @a2");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryRemoveExtraUnderscore() throws Exception {
+ resourceSubCategoryDefinition.setName("Category___ _22");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Category_ _22");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryFirstWordStartWithNumber() throws Exception {
+ resourceSubCategoryDefinition.setName("1Category one");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("1Category One");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ // Bug
+ // Desc=<ACTION = "AddSubCategory" MODIFIER = "Jimmy Hendrix(jh0003)"
+ // CATEGORY_NAME = "Category1" SUB_CATEGORY_NAME = "&AbcD123" GROUPING_NAME
+ // = "" RESOURCE_TYPE = "Resource" STATUS = "400" DESC = "SVC4556: Error:
+ // Invalid Resource sub-category name format.">
+ // DESC=SVC4556: Error: InvalidResourcesub-categorynameformat.,
+ // @Ignore
+ @Test
+ public void subCategoryFirstWordStartWithNonAlphaNumeric() throws Exception {
+ // The first word must start with an alpha-numeric character [a-Z A..Z,
+ // 0..9]
+ char invalidChars[] = { '&', '-', '+', '.', '\'', '#', '=', ':', '@', '_' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ resourceSubCategoryDefinition.setName(invalidChars[i] + "AbcD123");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT,
+ AUDIT_RESOURCE_TYPE, AUDIT_RESOURCE_TYPE, SUB_CATEGORY);
+
+ }
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_01() throws Exception {
+ resourceSubCategoryDefinition.setName("At and T");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("At & T");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_02() throws Exception {
+ resourceSubCategoryDefinition.setName("At and t");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("At & T");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_03() throws Exception {
+ resourceSubCategoryDefinition.setName("Atand T");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_04() throws Exception {
+ resourceSubCategoryDefinition.setName("At andT");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("At AndT");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_05() throws Exception {
+ resourceSubCategoryDefinition.setName(" and AttT");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("And AttT");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_06() throws Exception {
+ resourceSubCategoryDefinition.setName("AttT and ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("AttT And");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryReplaceAndWithAmpersand_07() throws Exception {
+ resourceSubCategoryDefinition.setName(" and a");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("And a");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationMaxLength() throws Exception {
+ resourceSubCategoryDefinition.setName("AsdfghjQ234567890@#.&:+-_");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationMaxLengthAfterNormalization() throws Exception {
+ resourceSubCategoryDefinition.setName(" A jQ234 @@@___ +++ At and T and and ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("A JQ234 @_ + At & T & And");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ // bug :
+ // Desc=<ACTION = "AddSubCategory" MODIFIER = "Jimmy Hendrix(jh0003)"
+ // CATEGORY_NAME = "Category1" SUB_CATEGORY_NAME = " AbdfghBCVa jQ234 @@___
+ // +++ At and T " GROUPING_NAME = "" RESOURCE_TYPE = "Resource" STATUS =
+ // "400" DESC = "SVC4555: Error: Invalid Resource sub-category name
+ // length.">
+ @Test
+ public void subCategoryNameValidationExceedMaxLengthAfterNormalization() throws Exception {
+ resourceSubCategoryDefinition.setName(" AbdfghBCVa jQ234 @@___ +++ At and T ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_RESOURCE_TYPE, AUDIT_RESOURCE_TYPE, SUB_CATEGORY);
+ }
+
+ @Test
+ public void subCategoryNameValidationMinLengthAfterNormalization() throws Exception {
+ resourceSubCategoryDefinition.setName(" AT&&&&&&&&&T ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("AT&T");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ // bug
+ // Desc=<ACTION = "AddSubCategory" MODIFIER = "Jimmy Hendrix(jh0003)"
+ // CATEGORY_NAME = "Category1" SUB_CATEGORY_NAME = " A and T " GROUPING_NAME
+ // = "" RESOURCE_TYPE = "Resource" STATUS = "400" DESC = "SVC4555: Error:
+ // Invalid Resource sub-category name length.">
+ @Test
+ public void subCategoryNameValidationLessThanMinLengthAfterNormalization() throws Exception {
+ resourceSubCategoryDefinition.setName(" A&&&T ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_RESOURCE_TYPE, AUDIT_RESOURCE_TYPE, SUB_CATEGORY);
+ }
+
+ @Test
+ public void subCategoryNameIsEmpty() throws Exception {
+ resourceSubCategoryDefinition.setName("");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT,
+ STATUS_CODE_INVALID_CONTENT, AUDIT_RESOURCE_TYPE, AUDIT_RESOURCE_TYPE, SUB_CATEGORY);
+ }
+
+ // bug
+ // Desc=<ACTION = "AddSubCategory" MODIFIER = "Jimmy Hendrix(jh0003)"
+ // CATEGORY_NAME = "Category1" SUB_CATEGORY_NAME = "AbcD123~" GROUPING_NAME
+ // = "" RESOURCE_TYPE = "Resource" STATUS = "400" DESC = "SVC4556: Error:
+ // Invalid Resource sub-category name format.">
+ @Test
+ public void subCategoryNameValidationInvalidCharacters() throws Exception {
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/',
+ '|', '\\', ',' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ resourceSubCategoryDefinition.setName("AbcD123" + invalidChars[i]);
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_INVALID_CONTENT,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditFailure(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, STATUS_CODE_INVALID_CONTENT,
+ AUDIT_RESOURCE_TYPE, AUDIT_RESOURCE_TYPE, SUB_CATEGORY);
+ }
+ }
+
+ @Test
+ public void subCategoryNameValidationFirstLetterOfKeyWordsCapitalized() throws Exception {
+ resourceSubCategoryDefinition.setName("beNNy shaY michEl");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("BeNNy ShaY MichEl");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_01() throws Exception {
+ resourceSubCategoryDefinition.setName(" bank OF america ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Bank of America");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_02() throws Exception {
+ resourceSubCategoryDefinition.setName("THE america bank ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("THE America Bank");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_03() throws Exception {
+ resourceSubCategoryDefinition.setName(" A bank OF america ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("A Bank of America");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_04() throws Exception {
+ resourceSubCategoryDefinition.setName(" bank america is A big ban ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Bank America Is a Big Ban");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_05() throws Exception {
+ resourceSubCategoryDefinition.setName(" aN apple comPany inC ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("AN Apple ComPany InC");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_06() throws Exception {
+ resourceSubCategoryDefinition.setName(" eat AN apple ANAN");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Eat an Apple ANAN");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_07() throws Exception {
+ resourceSubCategoryDefinition.setName(" united states OF americA ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("United States of AmericA");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_08() throws Exception {
+ resourceSubCategoryDefinition.setName(" oF united states OF amer ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("OF United States of Amer");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_09() throws Exception {
+ resourceSubCategoryDefinition.setName(" to Apple TO at&T TOO ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("To Apple to At&T TOO");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_10() throws Exception {
+ resourceSubCategoryDefinition.setName(" eat apple AS you liiikeas ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Eat Apple as You Liiikeas");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_11() throws Exception {
+ resourceSubCategoryDefinition.setName(" as you may want ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("As You May Want");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_12() throws Exception {
+ resourceSubCategoryDefinition.setName(" the bank OF america ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("The Bank of America");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_13() throws Exception {
+ resourceSubCategoryDefinition.setName(" To tel-toto ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("To Tel-toto");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void subCategoryNameValidationConjunctions_14() throws Exception {
+ resourceSubCategoryDefinition.setName(" tel-aviv To la ");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ resourceSubCategoryDefinition.setName("Tel-aviv to La");
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, resourceSubCategoryDefinition);
+
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, resourceCategoryDefinition,
+ resourceSubCategoryDefinition, sdncAdminUserDetails, STATUS_CODE_CREATED, AUDIT_RESOURCE_TYPE);
+ }
+
+ @Test
+ public void createSubCategoryHttpCspUserIdHeaderIsMissing() throws Exception {
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategoryHttpCspAtuUidIsMissing(
+ resourceSubCategoryDefinition, resourceCategoryDefinition, sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_MISSING_INFORMATION,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_SUB_CATEGORY);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(resourceCategoryDefinition.getUniqueId());
+ // String subCategoryName = (resourceSubCategoryDefinition != null ?
+ // resourceSubCategoryDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(resourceSubCategoryDefinition.getName());
+ // String groupingName = (groupingDefinition != null ?
+ // groupingDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setGroupingName("");
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_RESOURCE_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_SUB_CATEGORY);
+ }
+
+ @Test
+ public void createSubCategoryHttpCspUserIdIsEmpty() throws Exception {
+ User sdncAdminUserDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncAdminUserDetails1.setUserId("");
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails1, RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_MISSING_INFORMATION,
+ createSubCategoryRest.getErrorCode().intValue());
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryNotExistsInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), resourceSubCategoryDefinition);
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(ADD_SUB_CATEGORY);
+ expectedCatrgoryAuditJavaObject.setModifier("");
+ expectedCatrgoryAuditJavaObject.setCategoryName(resourceCategoryDefinition.getUniqueId());
+ // String subCategoryName = (resourceSubCategoryDefinition != null ?
+ // resourceSubCategoryDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(resourceSubCategoryDefinition.getName());
+ // String groupingName = (groupingDefinition != null ?
+ // groupingDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setGroupingName("");
+ expectedCatrgoryAuditJavaObject.setResourceType(AUDIT_RESOURCE_TYPE);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(STATUS_CODE_MISSING_INFORMATION));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, ADD_SUB_CATEGORY);
+ }
+
+ ////////////////////////////////////////////////////////////
+ private void createSubCategorySuccess(CategoryDefinition categoryDefinition,
+ SubCategoryDefinition subCategoryDefinition, User sdncAdminUserDetails, String componentType,
+ String auditType) throws Exception {
+
+ RestResponse createSubCategoryRest = CategoryRestUtils.createSubCategory(subCategoryDefinition,
+ categoryDefinition, sdncAdminUserDetails, componentType);
+ assertEquals("Check response code after create Sub category", STATUS_CODE_CREATED,
+ createSubCategoryRest.getErrorCode().intValue());
+ CategoryValidationUtils.validateCreateSubCategoryResponse(createSubCategoryRest, subCategoryDefinition);
+ // Audit validation
+ AuditValidationUtils.subCategoryAuditSuccess(ADD_SUB_CATEGORY, categoryDefinition, subCategoryDefinition,
+ sdncAdminUserDetails, STATUS_CODE_CREATED, auditType);
+ // get service category and validate that category added as defined
+ // (also set catalog uniqeId)
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails, componentType);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ categoryDefinition.getUniqueId(), subCategoryDefinition); // also
+ // set
+ // catalog
+ // uniqeId
+ }
+
+ @Test
+ public void getResourceCategoryHierarchySuccessFlow() throws Exception {
+ int numOfSubCategories = 3;
+ List<SubCategoryDefinition> subCategories = new ArrayList();
+ RestResponse restResponse;
+ SubCategoryDefinition subCategory;
+ String subName = resourceSubCategoryDefinition.getName();
+ for (int i = 0; i < numOfSubCategories; i++) {
+ resourceSubCategoryDefinition.setName(subName + i);
+ restResponse = CategoryRestUtils.createSubCategory(resourceSubCategoryDefinition,
+ resourceCategoryDefinition, sdncAdminUserDetails, RESOURCE_COMPONENT_TYPE);
+ subCategory = ResponseParser.parseToObject(restResponse.getResponse(), SubCategoryDefinition.class);
+ subCategories.add(subCategory);
+ }
+ RestResponse getAllCategoriesRest = CategoryRestUtils.getAllCategories(sdncAdminUserDetails,
+ RESOURCE_COMPONENT_TYPE);
+ assertEquals("Check response code after get all categories ", STATUS_CODE_SUCCESS,
+ getAllCategoriesRest.getErrorCode().intValue());
+ AuditValidationUtils.GetCategoryHierarchyAuditSuccess(GET_CATEGORY_HIERARCHY, AUDIT_RESOURCE_TYPE,
+ sdncAdminUserDetails, STATUS_CODE_SUCCESS);
+ for (SubCategoryDefinition sub : subCategories) {
+ CategoryValidationUtils.verifySubCategoryExistInGetResponse(getAllCategoriesRest,
+ resourceCategoryDefinition.getUniqueId(), sub);
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/CRUDExternalAPI.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/CRUDExternalAPI.java
new file mode 100644
index 0000000000..418e8f6dc8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/CRUDExternalAPI.java
@@ -0,0 +1,1011 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.devCI;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.util.EntityUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceDetailedAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedExternalAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.AssetRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import fj.data.Either;
+
+public class CRUDExternalAPI extends ComponentBaseTest {
+
+ private static Logger log = LoggerFactory.getLogger(CRUDExternalAPI.class.getName());
+ protected static final String UPLOAD_ARTIFACT_PAYLOAD = "UHVUVFktVXNlci1LZXktRmlsZS0yOiBzc2gtcnNhDQpFbmNyeXB0aW9uOiBhZXMyNTYtY2JjDQpDb21tZW5wOA0K";
+ protected static final String UPLOAD_ARTIFACT_NAME = "TLV_prv.ppk";
+
+ protected Config config = Config.instance();
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";
+
+ protected Gson gson = new Gson();
+ protected JSONParser jsonParser = new JSONParser();
+
+ protected String serviceVersion;
+ protected ResourceReqDetails resourceDetails;
+ protected User sdncUserDetails;
+ protected ServiceReqDetails serviceDetails;
+
+ @BeforeMethod
+ public void init() throws Exception {
+ AtomicOperationUtils.createDefaultConsumer(true);
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CRUDExternalAPI() {
+ super(name, CRUDExternalAPI.class.getName());
+
+ }
+
+ @DataProvider(name = "uploadArtifactOnVFViaExternalAPI")
+ public static Object[][] dataProviderUploadArtifactOnVFViaExternalAPI() {
+ return new Object[][] { { LifeCycleStatesEnum.CHECKIN, "DCAE_JSON" },
+ { LifeCycleStatesEnum.CHECKOUT, "DCAE_POLICY" }, { LifeCycleStatesEnum.CHECKOUT, "DCAE_EVENT" },
+ { LifeCycleStatesEnum.CHECKOUT, "APPC_CONFIG" }, { LifeCycleStatesEnum.CHECKOUT, "DCAE_DOC" },
+ { LifeCycleStatesEnum.CHECKOUT, "DCAE_TOSCA" }, { LifeCycleStatesEnum.CHECKOUT, "YANG_XML" },
+ { LifeCycleStatesEnum.CHECKOUT, "VNF_CATALOG" }, { LifeCycleStatesEnum.CHECKOUT, "VF_LICENSE" },
+ { LifeCycleStatesEnum.CHECKOUT, "VENDOR_LICENSE" },
+ { LifeCycleStatesEnum.CHECKOUT, "MODEL_INVENTORY_PROFILE" },
+ { LifeCycleStatesEnum.CHECKOUT, "MODEL_QUERY_SPEC" }, { LifeCycleStatesEnum.CHECKOUT, "OTHER" } };
+ }
+
+ // External API
+ // Upload artifact on VF via external API - happy flow
+ @Test(dataProvider = "uploadArtifactOnVFViaExternalAPI")
+ public void uploadArtifactOnVFViaExternalAPI(LifeCycleStatesEnum chosenLifeCycleState, String artifactType)
+ throws Exception {
+ // Method passed variable
+ // artifactType = "YANG_XML";
+ // chosenLifeCycleState = LifeCycleStatesEnum.CHECKOUT;
+
+ // String operationType = "Upload";
+ UserRoleEnum creatorUser = UserRoleEnum.DESIGNER;
+ String specificUser = "DESIGNER";
+
+ Component resourceDetails = getComponentInTargetLifeCycleState("vf", creatorUser, chosenLifeCycleState);
+
+ String resourceUUID = resourceDetails.getUUID();
+ System.out.println("Resource UUID: " + resourceUUID);
+
+ // get artifact data
+ ArtifactReqDetails artifactReqDetails = ElementFactory.getArtifactByType("Abcd", artifactType, true);
+
+ RestResponse restResponse = ArtifactRestUtils.externalAPIUploadArtifactOfTheAsset(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.valueOf(specificUser.toUpperCase())), artifactReqDetails);
+
+ Integer responseCode = restResponse.getErrorCode();
+ Integer expectedCode = 200;
+ Assert.assertEquals(responseCode, expectedCode, "Response code is not correct.");
+
+ // ArtifactDefinition artifactJavaObject =
+ // ResponseParser.convertArtifactDefinitionResponseToJavaObject(restResponse.getResponse());
+ // AuditingActionEnum action =
+ // AuditingActionEnum.ARTIFACT_UPLOAD_BY_API;
+ // ExpectedExternalAudit expectedExternalAudit =
+ // ElementFactory.getDefaultExternalArtifactAuditSuccess(AssetTypeEnum.RESOURCES,
+ // action, artifactJavaObject, resourceUUID);
+ // AuditValidationUtils.validateExternalAudit(expectedExternalAudit,
+ // AuditingActionEnum.ARTIFACT_UPLOAD_BY_API.getName(), null);
+ }
+
+ @DataProvider(name = "uploadArtifactOnServiceViaExternalAPI")
+ public static Object[][] dataProviderUploadArtifactOnServiceViaExternalAPI() {
+ return new Object[][] {
+ // {LifeCycleStatesEnum.CHECKOUT, "YANG_XML"},
+ // {LifeCycleStatesEnum.CHECKOUT, "VNF_CATALOG"},
+ // {LifeCycleStatesEnum.CHECKOUT, "MODEL_INVENTORY_PROFILE"},
+ // {LifeCycleStatesEnum.CHECKOUT, "MODEL_QUERY_SPEC"},
+ // {LifeCycleStatesEnum.CHECKOUT, "OTHER"},
+ { LifeCycleStatesEnum.CHECKIN, "YANG_XML" }, { LifeCycleStatesEnum.CHECKIN, "VNF_CATALOG" },
+ { LifeCycleStatesEnum.CHECKIN, "MODEL_INVENTORY_PROFILE" },
+ { LifeCycleStatesEnum.CHECKIN, "MODEL_QUERY_SPEC" }, { LifeCycleStatesEnum.CHECKIN, "OTHER" },
+ { LifeCycleStatesEnum.CERTIFICATIONREQUEST, "YANG_XML" },
+ { LifeCycleStatesEnum.CERTIFICATIONREQUEST, "VNF_CATALOG" },
+ { LifeCycleStatesEnum.CERTIFICATIONREQUEST, "MODEL_INVENTORY_PROFILE" },
+ { LifeCycleStatesEnum.CERTIFICATIONREQUEST, "MODEL_QUERY_SPEC" },
+ { LifeCycleStatesEnum.CERTIFICATIONREQUEST, "OTHER" } };
+ }
+
+ @Test(dataProvider = "uploadArtifactOnServiceViaExternalAPI")
+ public void uploadArtifactOnServiceViaExternalAPI(LifeCycleStatesEnum chosenLifeCycleState, String artifactType)
+ throws Exception {
+ Component resourceDetails = getComponentInTargetLifeCycleState("service", UserRoleEnum.DESIGNER,
+ chosenLifeCycleState);
+ double resourceVersion = Double.parseDouble(resourceDetails.getVersion());
+
+ // get artifact data
+ ArtifactReqDetails artifactReqDetails = ElementFactory.getArtifactByType("Abcd", artifactType, true);
+
+ System.out.println("Service UUID: " + resourceDetails.getUUID());
+ System.out.println("Service Version: " + resourceVersion);
+ System.out.println("Service life cycle state: " + chosenLifeCycleState);
+ System.out.println("Artifact Type: " + artifactReqDetails.getArtifactType());
+
+ RestResponse restResponse = ArtifactRestUtils.externalAPIUploadArtifactOfTheAsset(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), artifactReqDetails);
+
+ // Check response of external API
+ Integer responseCode = restResponse.getErrorCode();
+ Integer expectedCode = 200;
+ Assert.assertEquals(responseCode, expectedCode, "Response code is not correct.");
+
+ // TODO: Check auditing for upload opeartion
+ // ArtifactDefinition artifactJavaObject =
+ // ResponseParser.convertArtifactDefinitionResponseToJavaObject(restResponse.getResponse());
+ // AuditingActionEnum action =
+ // AuditingActionEnum.ARTIFACT_UPLOAD_BY_API;
+ // ExpectedExternalAudit expectedExternalAudit =
+ // ElementFactory.getDefaultExternalArtifactAuditSuccess(AssetTypeEnum.RESOURCES,
+ // action, artifactJavaObject, resourceUUID);
+ // AuditValidationUtils.validateExternalAudit(expectedExternalAudit,
+ // AuditingActionEnum.ARTIFACT_UPLOAD_BY_API.getName(), null);
+
+ // Check Service version (increase by one if not in checkout)
+ if (!chosenLifeCycleState.equals(LifeCycleStatesEnum.CHECKOUT)) {
+ double resourceNewVersion = Double.parseDouble(resourceDetails.getVersion());
+
+ System.out.println(resourceVersion);
+ System.out.println(resourceNewVersion);
+ }
+ }
+
+ @Test
+ public void artifactOperationOnRIViaExternalAPI() throws Exception {
+ // Method passed variable
+ String operationType = "Upload";
+ LifeCycleStatesEnum chosenLifeCycleState = LifeCycleStatesEnum.CHECKOUT;
+ UserRoleEnum creatorUser = UserRoleEnum.DESIGNER;
+ String specificUser = "DESIGNER";
+ String artifactType = "OTHER";
+
+ Component resourceDetails = getComponentInTargetLifeCycleState("service", creatorUser, chosenLifeCycleState);
+ Component resourceInstanceDetails = getComponentInTargetLifeCycleState("vf", creatorUser, chosenLifeCycleState);
+ ComponentInstance componentResourceInstanceDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceInstanceDetails, resourceDetails,
+ UserRoleEnum.DESIGNER, true)
+ .left().value();
+
+ String resourceUUID = resourceDetails.getUUID();
+ System.out.println("Resource UUID: " + resourceUUID);
+
+ // get artifact data
+ ArtifactReqDetails artifactReqDetails = ElementFactory.getArtifactByType("Abcd", artifactType, true);
+
+ RestResponse restResponse = null;
+
+ if (operationType.toLowerCase().equals("upload")) {
+ restResponse = ArtifactRestUtils.externalAPIUploadArtifactOfTheAsset(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.valueOf(specificUser.toUpperCase())),
+ artifactReqDetails);
+ System.out.println("1234");
+ }
+
+ ArtifactDefinition artifactJavaObject = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(restResponse.getResponse());
+ AuditingActionEnum action = AuditingActionEnum.ARTIFACT_UPLOAD_BY_API;
+ ExpectedExternalAudit expectedExternalAudit = ElementFactory.getDefaultExternalArtifactAuditSuccess(
+ AssetTypeEnum.RESOURCES, action, artifactJavaObject, resourceUUID);
+ AuditValidationUtils.validateExternalAudit(expectedExternalAudit,
+ AuditingActionEnum.ARTIFACT_UPLOAD_BY_API.getName(), null);
+ }
+
+ // @Test
+ // public void artifactViaExternalAPI() throws Exception {
+ // // Method passed variable
+ // String operationType = "Upload";
+ // String assetType = "vf";
+ // LifeCycleStatesEnum chosenLifeCycleState = LifeCycleStatesEnum.CHECKOUT;
+ // String componentInstanceAssetType = "vf";
+ // LifeCycleStatesEnum componentInstanceChosenLifeCycleState =
+ // LifeCycleStatesEnum.CHECKOUT;
+ // UserRoleEnum creatorUser = UserRoleEnum.DESIGNER;
+ // String specificUser = "DESIGNER";
+ // String artifactType = "HEAT";
+ //
+ //
+ // Component resourceDetails = null;
+ // Component resourceInstanceDetails = null;
+ // ComponentInstance componentResourceInstanceDetails = null;
+ //
+ // String resourceUUID = null;
+ // String resourceType = null;
+ // String resourceInstanceName = null;
+ //
+ // // Create resource & resource instance of requested type and state
+ // if (assetType.toLowerCase().equals("vf")) {
+ // resourceDetails = getComponentInTargetLifeCycleState("vf", creatorUser,
+ // chosenLifeCycleState);
+ // } else if (assetType.toLowerCase().equals("service")) {
+ // resourceDetails = getComponentInTargetLifeCycleState("service",
+ // creatorUser, chosenLifeCycleState);
+ // } else if (assetType.toLowerCase().equals("ri")) {
+ // resourceInstanceDetails = getComponentInTargetLifeCycleState("vf",
+ // creatorUser, chosenLifeCycleState);
+ // if(componentInstanceAssetType.toLowerCase().equals("vf")) {
+ // resourceDetails = getComponentInTargetLifeCycleState("vf", creatorUser,
+ // componentInstanceChosenLifeCycleState);
+ // componentResourceInstanceDetails =
+ // AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceInstanceDetails,
+ // resourceDetails, UserRoleEnum.DESIGNER, true).left().value();
+ // } else if (componentInstanceAssetType.toLowerCase().equals("service")) {
+ // resourceDetails = getComponentInTargetLifeCycleState("service",
+ // creatorUser, componentInstanceChosenLifeCycleState);
+ // componentResourceInstanceDetails =
+ // AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceInstanceDetails,
+ // resourceDetails, UserRoleEnum.DESIGNER, true).left().value();
+ // } else {
+ // Assert.assertEquals(false, true, "Component instance asset type is
+ // wrong.");
+ // }
+ // } else {
+ // Assert.assertEquals(false, true, "Asset type is wrong.");
+ // }
+ //
+ //
+ // // print uuid & resource instance name
+ // if (assetType.toLowerCase().equals("ri")) {
+ // resourceUUID = resourceDetails.getUUID();
+ // resourceInstanceName =
+ // componentResourceInstanceDetails.getNormalizedName();
+ //
+ //
+ // System.out.println("Resource UUID: " + resourceUUID);
+ // System.out.println("Resource instance name: " + resourceInstanceName);
+ // System.out.println("Resource type: " + resourceType);
+ // } else {
+ // resourceUUID = resourceDetails.getUUID();
+ //
+ // System.out.println("Resource UUID: " + resourceUUID);
+ // System.out.println("Resource type: " + resourceType);
+ // }
+ //
+ // // get artifact data
+ // ArtifactReqDetails artifactReqDetails =
+ // ElementFactory.getArtifactByType("Abcd", artifactType, true);
+ //
+ //// RestResponse restResponse;
+ // // using rest external api
+ //// if(operationType.toLowerCase().equals("upload")) {
+ //// if (!assetType.toLowerCase().equals("ri")) {
+ // RestResponse restResponse =
+ // ArtifactRestUtils.externalAPIUploadArtifactOfTheAsset(resourceDetails,
+ // ElementFactory.getDefaultUser(UserRoleEnum.valueOf(specificUser.toUpperCase())),
+ // artifactReqDetails);
+ // System.out.println("1234");
+ //// } else {
+ ////
+ //// }
+ //// } else {
+ ////
+ //// }
+ //
+ // ArtifactDefinition artifactJavaObject =
+ // ResponseParser.convertArtifactDefinitionResponseToJavaObject(restResponse.getResponse());
+ //// AuditingActionEnum action = AuditingActionEnum.ARTIFACT_UPLOAD_BY_API;
+ // AuditingActionEnum action = AuditingActionEnum.ARTIFACT_UPLOAD_BY_API;
+ // ExpectedExternalAudit expectedExternalAudit =
+ // ElementFactory.getDefaultExternalArtifactAuditSuccess(AssetTypeEnum.RESOURCES,
+ // action, artifactJavaObject, resourceUUID);
+ // AuditValidationUtils.validateExternalAudit(expectedExternalAudit,
+ // AuditingActionEnum.ARTIFACT_UPLOAD_BY_API.getName(), null);
+ //
+ //
+ //
+ //
+ // }
+ //
+
+ @Test
+ public void getResourceAssetMetadataWithNonCertifiedResourceInstancesAndArtifactsSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ Resource resource3 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource3 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource3, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource3, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ // TODO
+ Either<ArtifactDefinition, RestResponse> artifactDefinition = AtomicOperationUtils
+ .uploadArtifactByType(ArtifactTypeEnum.VENDOR_LICENSE, resourceVF, UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.APPC_CONFIG, resourceVF, UserRoleEnum.DESIGNER, true,
+ true);
+ resourceVF = ResponseParser.parseToObjectUsingMapper(
+ ResourceRestUtils.getResource(resourceVF.getUniqueId()).getResponse(), Resource.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ // AuditValidationUtils.validateAudit(expectedAssetListAudit,
+ // AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+
+ }
+
+ public Component getComponentInTargetLifeCycleState(String componentType, UserRoleEnum creatorUser,
+ LifeCycleStatesEnum targetLifeCycleState) throws Exception {
+ Component resourceDetails = null;
+
+ if (componentType.toLowerCase().equals("vf")) {
+ Either<Resource, RestResponse> createdResource = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, creatorUser, true);
+ resourceDetails = createdResource.left().value();
+ resourceDetails = AtomicOperationUtils
+ .changeComponentState(resourceDetails, creatorUser, targetLifeCycleState, true).getLeft();
+ } else {
+ Either<Service, RestResponse> createdResource = AtomicOperationUtils.createDefaultService(creatorUser,
+ true);
+ resourceDetails = createdResource.left().value();
+ resourceDetails = AtomicOperationUtils
+ .changeComponentState(resourceDetails, creatorUser, targetLifeCycleState, true).getLeft();
+ }
+
+ return resourceDetails;
+ }
+
+ // // External API - Download artifact for resource - negative test
+ // @Test
+ // public void k() throws Exception {
+ // Resource resourceDetailsVF;
+ // Either<Resource, RestResponse> createdResource =
+ // AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF,
+ // NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE,
+ // UserRoleEnum.DESIGNER, true);
+ // resourceDetailsVF = createdResource.left().value();
+ // ArtifactDefinition heatArtifact =
+ // AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT,
+ // resourceDetailsVF, UserRoleEnum.DESIGNER, true, true).left().value();
+ // resourceDetails = new ResourceReqDetails(resourceDetailsVF);
+ //
+ // String resourceUUID = resourceDetailsVF.getUUID();
+ // String artifactUUID = heatArtifact.getArtifactUUID();
+ //
+ // System.out.println("Resource UUID: " + resourceUUID);
+ // System.out.println("Artifact UUID: " + artifactUUID);
+ //
+ // RestResponse restResponse =
+ // ArtifactRestUtils.getResourceDeploymentArtifactExternalAPI(resourceUUID,
+ // "dfsgfdsg324", ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER),
+ // "Resource");
+ //
+ // Integer responseCode = restResponse.getErrorCode();
+ // Integer expectedCode = 200;
+ // Assert.assertEquals(responseCode,expectedCode, "Response code is not
+ // correct.");
+ // }
+ //
+ //
+ //
+ //
+ //
+ // // External API - Download artifact for service - negative test
+ // @Test
+ // public void downloadArtifactFromServiceViaExternalAPI() throws Exception
+ // {
+ //
+ // Service resourceDetailsService;
+ // Either<Service, RestResponse> createdResource =
+ // AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true);
+ // resourceDetailsService = createdResource.left().value();
+ //
+ // ArtifactDefinition heatArtifact =
+ // AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER,
+ // resourceDetailsService, UserRoleEnum.DESIGNER, true,
+ // true).left().value();
+ //
+ // String resourceUUID = resourceDetailsService.getUUID();
+ // String artifactUUID = heatArtifact.getArtifactUUID();
+ //
+ // System.out.println("Resource UUID: " + resourceUUID);
+ // System.out.println("Artifact UUID: " + artifactUUID);
+ //
+ // RestResponse restResponse =
+ // ArtifactRestUtils.getResourceDeploymentArtifactExternalAPI(resourceUUID,
+ // artifactUUID, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER),
+ // "Service");
+ //
+ // Integer responseCode = restResponse.getErrorCode();
+ // Integer expectedCode = 200;
+ // Assert.assertEquals(responseCode,expectedCode, "Response code is not
+ // correct.");
+ //
+ // String response = restResponse.getResponse();
+ //
+ // String payloadData =
+ // "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ // String decodedPaypload = Decoder.decode(payloadData);
+ //
+ // Assert.assertEquals(response, decodedPaypload, "Response deployment
+ // artifact not correct.");
+ //
+ // String auditAction = "ArtifactDownload";
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new
+ // ExpectedResourceAuditJavaObject();
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceName(resourceDetailsService.getName());
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setStatus("200");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ //
+ // expectedResourceAuditJavaObject.setCONSUMER_ID("ci");
+ // String resource_url =
+ // String.format("/asdc/v1/catalog/services/%s/artifacts/%s", resourceUUID,
+ // artifactUUID);
+ // expectedResourceAuditJavaObject.setRESOURCE_URL(resource_url);
+ //
+ // AuditValidationUtils.validateAuditDownloadExternalAPI(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ // }
+ //
+ //
+ //
+ //
+ //
+ //
+ // // External API - Download ComponentInstance artifact of service -
+ // negative test
+ // @Test
+ // public void
+ // downloadArtifactOfComponentInstanceFromServiceViaExternalAPI() throws
+ // Exception {
+ //
+ // Either<Resource, RestResponse> resourceDetailsVF_01e =
+ // AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF,
+ // NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE,
+ // UserRoleEnum.DESIGNER, true);
+ // Component resourceDetailsVF_01 = resourceDetailsVF_01e.left().value();
+ // ArtifactDefinition heatArtifact =
+ // AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT,
+ // resourceDetailsVF_01, UserRoleEnum.DESIGNER, true, true).left().value();
+ //
+ // resourceDetailsVF_01 =
+ // AtomicOperationUtils.changeComponentState(resourceDetailsVF_01,
+ // UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ //
+ // Service resourceDetailsService;
+ // Either<Service, RestResponse> createdResource =
+ // AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true);
+ // resourceDetailsService = createdResource.left().value();
+ //
+ //
+ // ComponentInstance resourceDetailsVF1ins_01 =
+ // AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVF_01,
+ // resourceDetailsService, UserRoleEnum.DESIGNER, true).left().value();
+ //
+ //
+ // System.out.println("-----");
+ //
+ //
+ // String resourceUUID = resourceDetailsService.getUUID();
+ // String componentInstanceUID = resourceDetailsVF1ins_01.getUniqueId();
+ // String artifactUUID = heatArtifact.getArtifactUUID();
+ //
+ // System.out.println("Resource UUID: " + resourceUUID);
+ // System.out.println("Component instance UID: " + componentInstanceUID);
+ // System.out.println("Artifact UUID: " + artifactUUID);
+ //
+ // RestResponse restResponse =
+ // ArtifactRestUtils.getComponentInstanceDeploymentArtifactExternalAPI(resourceUUID,
+ // componentInstanceUID, artifactUUID,
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), "Service");
+ ////
+ // Integer responseCode = restResponse.getErrorCode();
+ // Integer expectedCode = 200;
+ // Assert.assertEquals(responseCode,expectedCode, "Response code is not
+ // correct.");
+ //
+ // String response = restResponse.getResponse();
+ //
+ // String payloadData =
+ // "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ // String decodedPaypload = Decoder.decode(payloadData);
+ //
+ // Assert.assertEquals(response, decodedPaypload, "Response deployment
+ // artifact not correct.");
+ //
+ // String auditAction = "ArtifactDownload";
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new
+ // ExpectedResourceAuditJavaObject();
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceName(resourceDetailsVF1ins_01.getName());
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setStatus("200");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ //
+ // expectedResourceAuditJavaObject.setCONSUMER_ID("ci");
+ // String resource_url =
+ // String.format("/asdc/v1/catalog/services/%s/resourceInstances/%s/artifacts/%s",
+ // resourceUUID, componentInstanceUID, artifactUUID);
+ // expectedResourceAuditJavaObject.setRESOURCE_URL(resource_url);
+ //
+ // AuditValidationUtils.validateAuditDownloadExternalAPI(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ // }
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // @Test
+ // public void downloadArtifactFromResourceTest() throws Exception {
+ //
+ // CloseableHttpClient httpclient = HttpClients.createDefault();
+ // try {
+ // String jsonBody = createUploadArtifactBodyJson();
+ //
+ // String resourceId = resourceDetails.getUniqueId();
+ // String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE,
+ // config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ // HttpPost httppost = createPostAddArtifactRequeast(jsonBody, url, true);
+ // HttpResponse response = httpclient.execute(httppost);
+ // int status = response.getStatusLine().getStatusCode();
+ // AssertJUnit.assertEquals("failed to add artifact", 200, status);
+ //
+ // ArtifactDefinition origArtifact = getArtifactDataFromJson(jsonBody);
+ // addArtifactDataFromResponse(response, origArtifact);
+ // String artifactId = origArtifact.getUniqueId();
+ //
+ // url = String.format(Urls.UI_DOWNLOAD_RESOURCE_ARTIFACT,
+ // config.getCatalogBeHost(), config.getCatalogBePort(), resourceId,
+ // artifactId);
+ // HttpGet httpGet = createGetRequest(url);
+ // response = httpclient.execute(httpGet);
+ // status = response.getStatusLine().getStatusCode();
+ // AssertJUnit.assertEquals("failed to download artifact", 200, status);
+ //
+ // InputStream inputStream = response.getEntity().getContent();
+ // ArtifactUiDownloadData artifactUiDownloadData =
+ // getArtifactUiDownloadData(IOUtils.toString(inputStream));
+ // AssertJUnit.assertEquals("Downloaded payload is different from uploaded
+ // one", UPLOAD_ARTIFACT_PAYLOAD,
+ // artifactUiDownloadData.getBase64Contents());
+ // AssertJUnit.assertEquals("Downloaded artifact name is different from
+ // uploaded one", UPLOAD_ARTIFACT_NAME,
+ // artifactUiDownloadData.getArtifactName());
+ //
+ // // validate audit
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // Convertor.constructFieldsForAuditValidation(resourceDetails,
+ // resourceDetails.getVersion(), sdncUserDetails);
+ // String auditAction = "ArtifactDownload";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("200");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // expectedResourceAuditJavaObject.setArtifactData(AuditValidationUtils.buildArtifactDataAudit(origArtifact));
+ // expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ // expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ // } finally {
+ // httpclient.close();
+ // }
+ //
+ // }
+ //
+ // @Test
+ // public void downloadArtifactFromServiceTest() throws Exception {
+ //
+ // CloseableHttpClient httpclient = HttpClients.createDefault();
+ //
+ // try {
+ //
+ // String jsonStr = createUploadArtifactBodyJson();
+ //
+ // String url = String.format(Urls.ADD_ARTIFACT_TO_SERVICE,
+ // config.getCatalogBeHost(), config.getCatalogBePort(),
+ // serviceDetails.getUniqueId());
+ // HttpPost httpPost = createPostAddArtifactRequeast(jsonStr, url, true);
+ // CloseableHttpResponse result = httpclient.execute(httpPost);
+ // int status = result.getStatusLine().getStatusCode();
+ // AssertJUnit.assertEquals("failed to add artifact", 200, status);
+ //
+ // ArtifactDefinition origArtifact = getArtifactDataFromJson(jsonStr);
+ // addArtifactDataFromResponse(result, origArtifact);
+ // String artifactId = origArtifact.getUniqueId();
+ //
+ // url = String.format(Urls.UI_DOWNLOAD_SERVICE_ARTIFACT,
+ // config.getCatalogBeHost(), config.getCatalogBePort(),
+ // serviceDetails.getUniqueId(), artifactId);
+ // HttpGet httpGet = createGetRequest(url);
+ // CloseableHttpResponse response2 = httpclient.execute(httpGet);
+ // status = response2.getStatusLine().getStatusCode();
+ // AssertJUnit.assertEquals("failed to download artifact", 200, status);
+ // InputStream inputStream = response2.getEntity().getContent();
+ // ArtifactUiDownloadData artifactUiDownloadData =
+ // getArtifactUiDownloadData(IOUtils.toString(inputStream));
+ // AssertJUnit.assertEquals("Downloaded payload is different from uploaded
+ // one", UPLOAD_ARTIFACT_PAYLOAD,
+ // artifactUiDownloadData.getBase64Contents());
+ // AssertJUnit.assertEquals("Downloaded artifact name is different from
+ // uploaded one", UPLOAD_ARTIFACT_NAME,
+ // artifactUiDownloadData.getArtifactName());
+ //
+ // // validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // AuditValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // serviceDetails.getVersion(), sdncUserDetails);
+ // String auditAction = "ArtifactDownload";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("200");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // expectedResourceAuditJavaObject.setArtifactData(AuditValidationUtils.buildArtifactDataAudit(origArtifact));
+ // expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ // expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ //
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ // } finally {
+ //// RestResponse response = ServiceRestUtils.deleteService(serviceDetails,
+ // serviceVersion, sdncUserDetails );
+ //// checkDeleteResponse(response);
+ // httpclient.close();
+ // }
+ // }
+ //
+ // @Test
+ // public void downloadArtifactFromResourceNotFound() throws Exception {
+ //
+ // CloseableHttpClient httpclient = HttpClients.createDefault();
+ // try {
+ //
+ // String resourceId = resourceDetails.getUniqueId();
+ // String artifactIdNotFound = "11111";
+ //
+ // ArtifactDefinition origArtifact = new ArtifactDefinition();
+ // origArtifact.setUniqueId(artifactIdNotFound);
+ //
+ // String url = String.format(Urls.UI_DOWNLOAD_RESOURCE_ARTIFACT,
+ // config.getCatalogBeHost(), config.getCatalogBePort(), resourceId,
+ // artifactIdNotFound);
+ // HttpGet httpGet = createGetRequest(url);
+ // CloseableHttpResponse response = httpclient.execute(httpGet);
+ // int status = response.getStatusLine().getStatusCode();
+ // AssertJUnit.assertEquals("expected 404 not found", 404, status);
+ //
+ // // validate audit
+ // ErrorInfo errorInfo =
+ // ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.ARTIFACT_NOT_FOUND.name());
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // Convertor.constructFieldsForAuditValidation(resourceDetails,
+ // resourceDetails.getVersion(), sdncUserDetails);
+ // String auditAction = "ArtifactDownload";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ // expectedResourceAuditJavaObject.setArtifactData("");
+ // expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ // expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ // expectedResourceAuditJavaObject.setPrevArtifactUuid(null);
+ // } finally {
+ // httpclient.close();
+ // }
+ //
+ // }
+ //
+ // @Test
+ // public void downloadArtifactFromServiceNotFound() throws Exception {
+ //
+ // CloseableHttpClient httpclient = HttpClients.createDefault();
+ // try {
+ //
+ // String artifactIdNotFound = "11111";
+ // ArtifactDefinition origArtifact = new ArtifactDefinition();
+ // origArtifact.setUniqueId(artifactIdNotFound);
+ //
+ // String url = String.format(Urls.UI_DOWNLOAD_SERVICE_ARTIFACT,
+ // config.getCatalogBeHost(), config.getCatalogBePort(),
+ // serviceDetails.getUniqueId(), artifactIdNotFound);
+ // HttpGet httpGet = createGetRequest(url);
+ // CloseableHttpResponse response2 = httpclient.execute(httpGet);
+ // int status = response2.getStatusLine().getStatusCode();
+ // AssertJUnit.assertEquals("expected 404 not found", 404, status);
+ //
+ // // validate audit
+ // ErrorInfo errorInfo =
+ // ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.ARTIFACT_NOT_FOUND.name());
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // serviceDetails.getVersion(), sdncUserDetails);
+ // String auditAction = "ArtifactDownload";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ // expectedResourceAuditJavaObject.setArtifactData("");
+ // expectedResourceAuditJavaObject.setCurrArtifactUuid(origArtifact.getUniqueId());
+ // expectedResourceAuditJavaObject.setPrevArtifactUuid("");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ // } finally {
+ // httpclient.close();
+ // }
+ //
+ // }
+ //
+ // @Test
+ // public void addArtifactToResourceTest() throws Exception {
+ //
+ // ArtifactReqDetails defaultArtifact = ElementFactory.getDefaultArtifact();
+ //
+ // RestResponse response =
+ // ArtifactRestUtils.addInformationalArtifactToResource(defaultArtifact,
+ // sdncUserDetails, resourceDetails.getUniqueId());
+ // int status = response.getErrorCode();
+ // AssertJUnit.assertEquals("add informational artifact request returned
+ // status: " + response.getErrorCode(), 200, status);
+ //
+ // RestResponse resourceResp =
+ // ResourceRestUtils.getResource(resourceDetails.getUniqueId());
+ // Resource resource =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceResp.getResponse());
+ // AssertJUnit.assertNotNull(resource);
+ //
+ // Map<String, ArtifactDefinition> artifacts = resource.getArtifacts();
+ // boolean isExist = false;
+ // for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet())
+ // {
+ // if (entry.getKey().equals(defaultArtifact.getArtifactLabel())) {
+ // isExist = true;
+ //
+ // }
+ // }
+ // AssertJUnit.assertTrue(isExist);
+ // }
+
+ protected String createUploadArtifactBodyJson() {
+ Map<String, Object> jsonBody = new HashMap<String, Object>();
+ jsonBody.put("artifactName", UPLOAD_ARTIFACT_NAME);
+ jsonBody.put("artifactDisplayName", "configure");
+ jsonBody.put("artifactType", "SHELL");
+ jsonBody.put("mandatory", "false");
+ jsonBody.put("description", "ff");
+ jsonBody.put("payloadData", UPLOAD_ARTIFACT_PAYLOAD);
+ jsonBody.put("artifactLabel", "configure");
+ return gson.toJson(jsonBody);
+ }
+
+ protected ArtifactDefinition getArtifactDataFromJson(String json) {
+ Gson gson = new Gson();
+ JsonObject jsonElement = new JsonObject();
+ jsonElement = gson.fromJson(json, jsonElement.getClass());
+ ArtifactDefinition artifact = new ArtifactDefinition();
+ String payload = null;
+ JsonElement artifactPayload = jsonElement.get(Constants.ARTIFACT_PAYLOAD_DATA);
+ if (artifactPayload != null && !artifactPayload.isJsonNull()) {
+ payload = artifactPayload.getAsString();
+ }
+ jsonElement.remove(Constants.ARTIFACT_PAYLOAD_DATA);
+ artifact = gson.fromJson(jsonElement, ArtifactDefinition.class);
+ artifact.setPayloadData(payload);
+
+ /*
+ * atifact.setArtifactName(UPLOAD_ARTIFACT_NAME);
+ * artifact.setArtifactDisplayName("configure");
+ * artifact.setArtifactType("SHELL"); artifact.setMandatory(false);
+ * artifact.setDescription("ff");
+ * artifact.setPayloadData(UPLOAD_ARTIFACT_PAYLOAD);
+ * artifact.setArtifactLabel("configure");
+ */
+ return artifact;
+ }
+
+ protected HttpGet createGetRequest(String url) {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ httpGet.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ httpGet.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ return httpGet;
+ }
+
+ protected String getArtifactUid(HttpResponse response) throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ String artifactId = (String) responseMap.get("uniqueId");
+ return artifactId;
+ }
+
+ protected String getArtifactEsId(HttpResponse response) throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ String esId = (String) responseMap.get("EsId");
+ return esId;
+ }
+
+ protected ArtifactDefinition addArtifactDataFromResponse(HttpResponse response, ArtifactDefinition artifact)
+ throws HttpResponseException, IOException, ParseException {
+ // String responseString = new
+ // BasicResponseHandler().handleResponse(response);
+ HttpEntity entity = response.getEntity();
+ String responseString = EntityUtils.toString(entity);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ artifact.setEsId((String) responseMap.get("esId"));
+ artifact.setUniqueId((String) responseMap.get("uniqueId"));
+ artifact.setArtifactGroupType(ArtifactGroupTypeEnum.findType((String) responseMap.get("artifactGroupType")));
+ artifact.setTimeout(((Long) responseMap.get("timeout")).intValue());
+ return artifact;
+ }
+
+ protected String getLifecycleArtifactUid(CloseableHttpResponse response)
+ throws HttpResponseException, IOException, ParseException {
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseMap = (JSONObject) jsonParser.parse(responseString);
+ responseMap = (JSONObject) responseMap.get("implementation");
+ String artifactId = (String) responseMap.get("uniqueId");
+ return artifactId;
+ }
+
+ protected HttpDelete createDeleteArtifactRequest(String url) {
+ HttpDelete httpDelete = new HttpDelete(url);
+ httpDelete.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ httpDelete.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ return httpDelete;
+ }
+
+ protected HttpPost createPostAddArtifactRequeast(String jsonBody, String url, boolean addMd5Header)
+ throws UnsupportedEncodingException {
+ HttpPost httppost = new HttpPost(url);
+ httppost.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ httppost.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ httppost.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ if (addMd5Header) {
+ httppost.addHeader(HttpHeaderEnum.Content_MD5.getValue(), GeneralUtility.calculateMD5ByString(jsonBody));
+ }
+ StringEntity input = new StringEntity(jsonBody);
+ input.setContentType("application/json");
+ httppost.setEntity(input);
+ log.debug("Executing request {}", httppost.getRequestLine());
+ return httppost;
+ }
+
+ protected String createLoadArtifactBody() {
+ Map<String, Object> json = new HashMap<String, Object>();
+ json.put("artifactName", "install_apache2.sh");
+ json.put("artifactType", "SHELL");
+ json.put("description", "ddd");
+ json.put("payloadData", "UEsDBAoAAAAIAAeLb0bDQz");
+ json.put("artifactLabel", "name123");
+
+ String jsonStr = gson.toJson(json);
+ return jsonStr;
+ }
+
+ protected void checkDeleteResponse(RestResponse response) {
+ BaseRestUtils.checkStatusCode(response, "delete request failed", false, 204, 404);
+ }
+
+ protected ArtifactUiDownloadData getArtifactUiDownloadData(String artifactUiDownloadDataStr) throws Exception {
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ ArtifactUiDownloadData artifactUiDownloadData = mapper.readValue(artifactUiDownloadDataStr,
+ ArtifactUiDownloadData.class);
+ return artifactUiDownloadData;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarUpdate.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarUpdate.java
new file mode 100644
index 0000000000..2c9d96d79d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarUpdate.java
@@ -0,0 +1,356 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.devCI;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.imports.ImportCsarResourceTest;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class ImportCsarUpdate extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ImportCsarUpdate() {
+ super(name, ImportCsarUpdate.class.getName());
+ }
+
+ @DataProvider(name = "happyArts")
+ public Object[][] getHappyArtifacts() {
+
+ return new Object[][] { { "happy_VF_RI2_G2_two_different_artifacts_under_heatBaseheatVolheatNet2" },
+ { "happy_VF_RI2_G2_two_different_artifacts_under_heatBaseheatVolheatNet" },
+ { "happy_VF_RI2_G2_two_identical_artifacts_under_heatBaseheatVolheatNet" },
+ { "happy_VF_RI2_G2_two_different_artifacts_under_nested" },
+ { "happy_VF_RI2_G2_two_indentical_nested_under_different_groups" },
+ { "happy_VF_RI2_G2_two_different_nested_under_different_groups" },
+ { "happy_VF_RI2_G2_two_different_nested_under_same_group" },
+
+ };
+ }
+
+ @DataProvider(name = "negativeArts")
+ public Object[][] getNegativeArtifacts() {
+
+ return new Object[][] {
+
+ { "negative_VF_RI2_G2_same_heatVol_different_groups" },
+ { "negative_VF_RI2_G2_same_heatBase_different_envs" },
+ { "negative_VF_RI2_G2_heatBaseHeatVolHeatNet_under_nested" },
+ { "negative_VF_RI2_G2_two_indentical_artifacts_under_nested" },
+ { "negative_VF_RI2_G2_nested_under_nested" }, { "negative_VF_RI2_G2_same_heatVol_different_groups" }, };
+ }
+
+ @BeforeTest
+ public void resumeOrigCsarBefore() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportCsarResourceTest.copyCsarRest(sdncModifierDetails, "orig.csar", "importCsar_2Gartifacts.csar");
+
+ }
+
+ @AfterTest
+ public void resumeOrigCsarAfter() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportCsarResourceTest.copyCsarRest(sdncModifierDetails, "orig.csar", "importCsar_2Gartifacts.csar");
+
+ }
+
+ @Test
+ public void updateVFsearchByCsarIdCheckInState() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true)
+ .getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "updateImportCsar_2Gartifacts_topologyChanged.csar", "importCsar_2Gartifacts.csar");
+
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+ @Test
+ public void updateVFsearchByCsarIdCheckInState_checkSum() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true)
+ .getLeft();
+
+ // User sdncModifierDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // RestResponse copyRes =
+ // ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,"updateImportCsar_2Gartifacts_topologyChanged.csar","importCsar_2Gartifacts.csar");
+
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+ @Test
+ public void updateVFsearchByCsarIdCheckOutState() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ // Component resourceObject =
+ // AtomicOperationUtils.changeComponentState(resourceFirstImport,
+ // UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "updateImportCsar_2Gartifacts_topologyChanged.csar", "importCsar_2Gartifacts.csar");
+
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+ @Test
+ public void updateVFsearchByCsarIdCertifyStat() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true)
+ .getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "updateImportCsar_2Gartifacts_topologyChanged.csar", "importCsar_2Gartifacts.csar");
+
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+ @Test
+ public void updateVFsearchByCsarStartCertificationState() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils.changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST, true).getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "updateImportCsar_2Gartifacts_topologyChanged.csar", "importCsar_2Gartifacts.csar");
+
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+ @Test
+ public void updateVFsearchBySystemNameCheckInState() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true)
+ .getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "updateImportCsar_2Gartifacts_topologyChanged.csar", "importCsar_2Gartifacts.csar");
+
+ resourceDetails.setName("hardcodedNameChanged");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+ @Test
+ public void updateVFsearchBySystemNameCertifyState() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true)
+ .getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "updateImportCsar_2Gartifacts_topologyChanged.csar", "importCsar_2Gartifacts.csar");
+
+ resourceDetails.setName("hardcodedNameChanged");
+ resourceDetails.setCsarUUID("importCsar_2Gartifacts");
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ }
+
+ @Test
+ public void updateVFsearchBySystemNameCsarIdNotExist() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("hardcodedName");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceFirstImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resourceFirstImport, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true)
+ .getLeft();
+ // User sdncModifierDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // RestResponse copyRes =
+ // ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,"updateImportCsar_2Gartifacts_topologyChanged.csar","importCsar_2Gartifacts.csar");
+ ResourceReqDetails resourceDetails2 = ElementFactory.getDefaultResource();
+ resourceDetails2.setName("hardcodedName");
+ resourceDetails2.setCsarUUID("importCsar_2Gartifacts");
+ resourceDetails2.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails2,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resourceSecondImport = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ // Validation Part
+
+ resourceFirstImport.getGroups().equals(resourceSecondImport.getGroups());
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarValidateArtifacts.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarValidateArtifacts.java
new file mode 100644
index 0000000000..49e5950db5
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ImportCsarValidateArtifacts.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.devCI;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.CsarValidationUtils;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class ImportCsarValidateArtifacts extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ImportCsarValidateArtifacts() {
+ super(name, ImportCsarValidateArtifacts.class.getName());
+ }
+
+ @DataProvider(name = "happyArts")
+ public Object[][] getHappyArtifacts() {
+
+ return new Object[][] { { "happy_VF_RI2_G2_two_different_artifacts_under_heatBaseheatVolheatNet2" },
+ { "happy_VF_RI2_G2_two_different_artifacts_under_heatBaseheatVolheatNet" },
+ { "happy_VF_RI2_G2_two_identical_artifacts_under_heatBaseheatVolheatNet" },
+ { "happy_VF_RI2_G2_two_different_artifacts_under_nested" },
+ { "happy_VF_RI2_G2_two_indentical_nested_under_different_groups" },
+ { "happy_VF_RI2_G2_two_different_nested_under_different_groups" },
+ { "happy_VF_RI2_G2_two_different_nested_under_same_group" },
+
+ };
+ }
+
+ @DataProvider(name = "negativeArts")
+ public Object[][] getNegativeArtifacts() {
+
+ return new Object[][] {
+
+ { "negative_VF_RI2_G2_same_heatVol_different_groups" },
+ { "negative_VF_RI2_G2_same_heatBase_different_envs" },
+ { "negative_VF_RI2_G2_heatBaseHeatVolHeatNet_under_nested" },
+ { "negative_VF_RI2_G2_two_indentical_artifacts_under_nested" },
+ { "negative_VF_RI2_G2_nested_under_nested" }, { "negative_VF_RI2_G2_same_heatVol_different_groups" }, };
+ }
+
+ @Test(dataProvider = "happyArts")
+ public void createResourceFromCsarArtsHappy(String artifactName) throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(artifactName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ CsarValidationUtils.validateCsarVfArtifact(artifactName, resource);
+
+ }
+
+ @Test(dataProvider = "negativeArts")
+ public void createResourceFromCsarArtsNegative(String artifactName) throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(artifactName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ assertTrue(createResource.getErrorCode() != 201 && createResource.getErrorCode() != 500);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ToscaGroupInsideVF.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ToscaGroupInsideVF.java
new file mode 100644
index 0000000000..bb86f8f336
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/devCI/ToscaGroupInsideVF.java
@@ -0,0 +1,578 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.devCI;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaDefinition;
+import org.openecomp.sdc.ci.tests.utils.ToscaParserUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.CsarValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+public class ToscaGroupInsideVF extends ComponentBaseTest {
+ private static Logger logger = LoggerFactory.getLogger(ToscaGroupInsideVF.class.getName());
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ToscaGroupInsideVF() {
+ super(name, ToscaGroupInsideVF.class.getName());
+ }
+
+ @Test
+ public void createResourceFromCsarArts() throws Exception {
+
+ // String csar = getCsar();
+ // parseCsar(csar);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("VF_RI2_G6_withArtifacts");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ Resource vfManual = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, vfManual, UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.changeComponentState(vfManual, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true)
+ .getLeft();
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceObject, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(vfManual, service, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+ AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CANCELCERTIFICATION, true)
+ .getLeft();
+ AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+
+ }
+
+ @Test
+ public void soferTest() throws Exception {
+
+ // String csar = getCsar();
+ // parseCsar(csar);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("sofer");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ }
+
+ @Test
+ public void createVFwith2VLs() throws Exception {
+
+ // String csar = getCsar();
+ // parseCsar(csar);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("VSPPackage");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ Component resourceObject = AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ Resource vfManual = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, vfManual, UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.changeComponentState(vfManual, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true)
+ .getLeft();
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceObject, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(vfManual, service, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+ AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CANCELCERTIFICATION, true)
+ .getLeft();
+ AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true)
+ .getLeft();
+
+ }
+
+ @Test // (enabled = false)
+ public void createResourceFromCsarHappy() throws Exception {
+ // String csarUUID = "VF_RI2_G2_withArtifacts";
+ String csarUUID = "VF_RI2_G1_Invalid";
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ }
+
+ @Test // (enabled = false)
+ public void createResourceFromCsarWithProperty() throws Exception {
+ String csarUUID = "VF_RI2_G4_withArtifacts";
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ }
+
+ @Test // (enabled = false)
+ public void UpdateCsarWithNonExistingResourceInstanceFail() throws Exception {
+
+ // String csarUUID = "VF_RI2_G1-RI_NotExist";
+ // String csarUUID = "nested3";
+
+ // String csarUUID = "VF_RI2_G1_Invalid_WithArtifacts";
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ String csarUUID = "VF_RI2_G4_withArtifacts.csar";
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifactsRI_FAIL.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails.setName(resource.getName());
+ // resourceDetails.setVendorName("Govnuk");
+ // resourceDetails.setDescription("Other");
+ RestResponse createResource2 = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource2);
+ Resource resource2 = ResponseParser.parseToObjectUsingMapper(createResource2.getResponse(), Resource.class);
+
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ ToscaDefinition toscaDefinition = ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition, resource);
+
+ // CsarValidationUtils.validateCsarVfArtifact(csarUUID2, resource2);
+ // ToscaDefinition toscaDefinition2 =
+ // ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID2);
+ // CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition2,
+ // resource2);
+
+ // Csar csar = parserTocsarObject(csarUUID);
+ // validateCsarVsResourceObj(csar, resource);
+ // csar.node_types();
+
+ }
+
+ @Test // (enabled = false)
+ public void UpdateCsarWithSameCsarDifferentMetadata() throws Exception {
+
+ // User sdncModifierDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // RestResponse copyRes =
+ // copyCsarRest(sdncModifierDetails,"VF_RI2_G4_withArtifacts_a.csar","VF_RI2_G4_withArtifacts.csar");
+ // BaseRestUtils.checkSuccess(copyRes);
+ String csarUUID = "VF_RI2_G4_withArtifacts.csar";
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ resourceDetails.setName(resource.getName());
+ resourceDetails.setVendorName("Govnuk");
+ resourceDetails.setDescription("Other");
+ RestResponse createResource2 = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource2);
+ Resource resource2 = ResponseParser.parseToObjectUsingMapper(createResource2.getResponse(), Resource.class);
+
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ ToscaDefinition toscaDefinition = ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition, resource);
+
+ // CsarValidationUtils.validateCsarVfArtifact(csarUUID2, resource2);
+ // ToscaDefinition toscaDefinition2 =
+ // ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID2);
+ // CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition2,
+ // resource2);
+
+ // Csar csar = parserTocsarObject(csarUUID);
+ // validateCsarVsResourceObj(csar, resource);
+ // csar.node_types();
+
+ }
+
+ @Test // (enabled = false)
+ public void UpdateCsarWithSameCsar() throws Exception {
+
+ // User sdncModifierDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // RestResponse copyRes =
+ // copyCsarRest(sdncModifierDetails,"VF_RI2_G4_withArtifacts_a.csar","VF_RI2_G4_withArtifacts.csar");
+ // BaseRestUtils.checkSuccess(copyRes);
+ String csarUUID = "VF_RI2_G4_withArtifacts.csar";
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ resourceDetails.setName(resource.getName());
+ RestResponse createResource2 = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource2);
+ Resource resource2 = ResponseParser.parseToObjectUsingMapper(createResource2.getResponse(), Resource.class);
+
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ ToscaDefinition toscaDefinition = ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition, resource);
+
+ // CsarValidationUtils.validateCsarVfArtifact(csarUUID2, resource2);
+ // ToscaDefinition toscaDefinition2 =
+ // ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID2);
+ // CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition2,
+ // resource2);
+
+ // Csar csar = parserTocsarObject(csarUUID);
+ // validateCsarVsResourceObj(csar, resource);
+ // csar.node_types();
+
+ }
+
+ @Test // (enabled = false)
+ public void UpdateCsarCertifiedVfWithSameCsar() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ String csarUUID = "VF_RI2_G4_withArtifacts.csar";
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ Pair<Component, RestResponse> changeComponentState = AtomicOperationUtils.changeComponentState(resource,
+ UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ resource = (Resource) changeComponentState.getLeft();
+
+ resourceDetails.setName(resource.getName());
+ RestResponse createResource2 = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource2);
+ Resource resource2 = ResponseParser.parseToObjectUsingMapper(createResource2.getResponse(), Resource.class);
+
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ ToscaDefinition toscaDefinition = ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition, resource);
+
+ // CsarValidationUtils.validateCsarVfArtifact(csarUUID2, resource2);
+ // ToscaDefinition toscaDefinition2 =
+ // ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID2);
+ // CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition2,
+ // resource2);
+
+ // Csar csar = parserTocsarObject(csarUUID);
+ // validateCsarVsResourceObj(csar, resource);
+ // csar.node_types();
+
+ }
+
+ @Test // (enabled = false)
+ public void UpdateCsarDifferentTosca() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ String csarUUID = "VF_RI2_G4_withArtifacts.csar";
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifactsUpdated.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails.setName(resource.getName());
+ RestResponse createResource2 = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource2);
+ Resource resource2 = ResponseParser.parseToObjectUsingMapper(createResource2.getResponse(), Resource.class);
+
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ ToscaDefinition toscaDefinition = ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition, resource);
+
+ // CsarValidationUtils.validateCsarVfArtifact(csarUUID2, resource2);
+ // ToscaDefinition toscaDefinition2 =
+ // ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID2);
+ // CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition2,
+ // resource2);
+
+ // Csar csar = parserTocsarObject(csarUUID);
+ // validateCsarVsResourceObj(csar, resource);
+ // csar.node_types();
+
+ }
+
+ @Test // (enabled = false)
+ public void UpdateCsarDifferentToscaAndArtifacts() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ String csarUUID = "VF_RI2_G4_withArtifacts.csar";
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_UpdateToscaAndArtifacts.csar",
+ "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails.setName(resource.getName());
+ RestResponse createResource2 = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource2);
+ Resource resource2 = ResponseParser.parseToObjectUsingMapper(createResource2.getResponse(), Resource.class);
+
+ CsarValidationUtils.validateCsarVfArtifact(csarUUID, resource);
+ ToscaDefinition toscaDefinition = ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition, resource);
+
+ // CsarValidationUtils.validateCsarVfArtifact(csarUUID2, resource2);
+ // ToscaDefinition toscaDefinition2 =
+ // ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID2);
+ // CsarValidationUtils.validateToscaDefinitonObjectVsResource(toscaDefinition2,
+ // resource2);
+
+ // Csar csar = parserTocsarObject(csarUUID);
+ // validateCsarVsResourceObj(csar, resource);
+ // csar.node_types();
+
+ }
+
+ @Test // (enabled = false)
+ public void migration() throws Exception {
+ String csarUUID = "VF_RI2_G4_withArtifacts";
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setName("Resource1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ resourceDetails.setName("Resource2");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+
+ resourceDetails.setName("Resource3");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ resourceDetails.setName("Resource4");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+
+ resourceDetails.setName("Resource5");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+
+ resourceDetails.setName("Resource6");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.STARTCERTIFICATION, true)
+ .getLeft();
+
+ resourceDetails.setName("Resource7");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+
+ logger.debug("7 VF resources were created");
+
+ }
+
+ public static RestResponse copyCsarRest(User sdncModifierDetails, String sourceCsarUuid, String targetCsarUuid)
+ throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.COPY_CSAR_USING_SIMULATOR, config.getCatalogBeHost(), config.getCatalogBePort(),
+ sourceCsarUuid, targetCsarUuid);
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+
+ RestResponse copyCsarResponse = http.httpSendPost(url, "dummy", headersMap);
+ if (copyCsarResponse.getErrorCode() != 200) {
+ return null;
+ }
+ return copyCsarResponse;
+
+ }
+
+ private static Map<String, String> prepareHeadersMap(String userId) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ if (userId != null) {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ }
+ return headersMap;
+ }
+
+ public static void main(String[] args) throws Exception {
+ // String csarUUID = "VF_RI2_G4_withArtifacts";
+ String csarUUID = "node_types";
+ ToscaParserUtils.getToscaDefinitionObjectByCsarUuid(csarUUID);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/AuthanticationTests.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/AuthanticationTests.java
new file mode 100644
index 0000000000..b4a9bb87ce
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/AuthanticationTests.java
@@ -0,0 +1,186 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.distribution;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedAuthenticationAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ConsumerRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class AuthanticationTests extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected User sdncUserDetails;
+
+ protected static final String AUTH_SUCCESS = "AUTH_SUCCESS";
+
+ protected static final String AUTH_REQUIRED = "AUTH_REQUIRED";
+
+ // user ci password 123456
+ // protected final String authorizationHeader = "Basic Y2k6MTIzNDU2";
+ // user ci password 123456
+ protected final String USER = "ci";
+ protected final String PASSWORD = "123456";
+ protected final String SALT = "2a1f887d607d4515d4066fe0f5452a50";
+ protected final String HASHED_PASSWORD = "0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b";
+ protected User sdncAdminUserDetails;
+ protected ConsumerDataDefinition consumerDataDefinition;
+
+ public AuthanticationTests() {
+ super(name, AuthanticationTests.class.getName());
+ }
+
+ @DataProvider
+ private final Object[][] getServiceDepArtType() throws IOException, Exception {
+ return new Object[][] { { ArtifactTypeEnum.YANG_XML.getType() }, { ArtifactTypeEnum.OTHER.getType() } };
+ }
+
+ @BeforeMethod
+ public void setup() throws Exception {
+ resourceDetails = ElementFactory.getDefaultResource();
+ serviceDetails = ElementFactory.getDefaultService();
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncAdminUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ createComponents();
+ consumerDataDefinition = createConsumer();
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+ ;
+
+ RestResponse createResponse = ConsumerRestUtils.createConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ BaseRestUtils.checkCreateResponse(createResponse);
+
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+ ;
+
+ }
+
+ protected ConsumerDataDefinition createConsumer() {
+ ConsumerDataDefinition consumer = new ConsumerDataDefinition();
+ consumer.setConsumerName(USER);
+ consumer.setConsumerSalt(SALT);
+ consumer.setConsumerPassword(HASHED_PASSWORD);
+ return consumer;
+
+ }
+
+ protected void createComponents() throws Exception {
+ RestResponse response = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ ServiceRestUtils.checkCreateResponse(response);
+ }
+
+ @Test(dataProvider = "getServiceDepArtType", description = "mumu")
+ public void downloadServiceArtifactSuccessWithAutantication(String serviceDepArtType) throws Exception {
+ String serviceUniqueId = serviceDetails.getUniqueId();
+
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(serviceDepArtType);
+
+ RestResponse addArtifactResponse = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails,
+ sdncUserDetails, serviceUniqueId, ArtifactRestUtils.calculateChecksum(artifactDetails));
+ AssertJUnit.assertEquals("Check response code after adding interface artifact", 200,
+ addArtifactResponse.getErrorCode().intValue());
+
+ String artifactName = ValidationUtils.normalizeFileName(artifactDetails.getArtifactName());
+ // Thread.sleep(5000);
+ Map<String, String> authorizationHeaders = BaseRestUtils.addAuthorizeHeader(USER, PASSWORD);
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, artifactDetails,
+ sdncUserDetails, authorizationHeaders);
+ AssertJUnit.assertEquals("Check response code after download resource", 200,
+ restResponse.getErrorCode().intValue());
+
+ List<String> contDispHeaderList = restResponse.getHeaderFields().get(Constants.CONTENT_DISPOSITION_HEADER);
+ AssertJUnit.assertNotNull(contDispHeaderList);
+ AssertJUnit.assertEquals("Check content disposition header",
+ new StringBuilder().append("attachment; filename=\"").append(artifactName).append("\"").toString(),
+ contDispHeaderList.get(0));
+
+ String downloadUrl = ArtifactRestUtils
+ .getPartialUrlByArtifactName(serviceDetails, serviceDetails.getVersion(), artifactName).substring(6);
+
+ ExpectedAuthenticationAudit expectedAuthenticationAudit = new ExpectedAuthenticationAudit(downloadUrl, USER,
+ AuditingActionEnum.AUTH_REQUEST.getName(), AUTH_SUCCESS);
+ AuditValidationUtils.validateAuthenticationAudit(expectedAuthenticationAudit);
+ }
+
+ @Test(dataProvider = "getServiceDepArtType")
+ public void downloadServiceArtifactWithOutAutantication(String serviceDepArtType) throws Exception {
+ String serviceUniqueId = serviceDetails.getUniqueId();
+
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(serviceDepArtType);
+
+ RestResponse addArtifactResponse = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails,
+ sdncUserDetails, serviceUniqueId, ArtifactRestUtils.calculateChecksum(artifactDetails));
+ assertEquals("Check response code after adding interface artifact", 200,
+ addArtifactResponse.getErrorCode().intValue());
+
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, artifactDetails,
+ sdncUserDetails, authorizationHeaders);
+ assertEquals("Check response code after download resource failure", 401,
+ restResponse.getErrorCode().intValue());
+
+ String downloadUrl = ArtifactRestUtils.getPartialUrlByArtifactName(serviceDetails, serviceDetails.getVersion(),
+ artifactDetails.getArtifactName()).substring(6);
+ ExpectedAuthenticationAudit expectedAuthenticationAudit = new ExpectedAuthenticationAudit(downloadUrl, "",
+ AuditingActionEnum.AUTH_REQUEST.getName(), AUTH_REQUIRED);
+ AuditValidationUtils.validateAuthenticationAudit(expectedAuthenticationAudit);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/DistributionDownloadArtifactTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/DistributionDownloadArtifactTest.java
new file mode 100644
index 0000000000..6cf5626e7e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/distribution/DistributionDownloadArtifactTest.java
@@ -0,0 +1,542 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.distribution;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipInputStream;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedDistDownloadAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ConsumerRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class DistributionDownloadArtifactTest extends ComponentBaseTest {
+
+ protected static ResourceReqDetails resourceDetails;
+ protected static User designerUser;
+ protected static User adminUser;
+ protected static String resourceBaseVersion;
+ // user ci password 123456
+ protected final String authorizationHeader = "Basic Y2k6MTIzNDU2";
+ protected ConsumerDataDefinition consumerDataDefinition;
+
+ @Rule
+ public static TestName name = new TestName();
+ protected static String artifactInterfaceType;
+ protected static String artifactOperationName;
+
+ protected static ServiceReqDetails serviceDetails;
+ protected static String serviceBaseVersion;
+ protected static String serviceUniqueId;
+ protected final String USER = "ci";
+ protected final String PASSWORD = "123456";
+ protected final String SALT = "2a1f887d607d4515d4066fe0f5452a50";
+ protected final String HASHED_PASSWORD = "0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b";
+
+ public DistributionDownloadArtifactTest() {
+ super(name, DistributionDownloadArtifactTest.class.getName());
+ }
+
+ // @BeforeClass
+ // public static void InitBeforeTest() throws Exception
+ // {
+ //
+ //
+ // resourceBaseVersion = "0.1";
+ // serviceBaseVersion = "0.1";
+ // designerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // adminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ //// resourceDetails =
+ // ElementFactory.getDefaultResource("tosca.nodes.newnotgenericresource4testNew",
+ // NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS,
+ // "jh0003");
+ // resourceDetails =
+ // ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ // NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS,
+ // adminUser);
+ // serviceDetails = ElementFactory.getDefaultService();
+ // serviceUniqueId = "svc_" + serviceDetails.getName().toLowerCase() + "." +
+ // serviceBaseVersion;
+ // artifactInterfaceType = "standard";
+ // artifactOperationName = "start";
+ // }
+
+ @BeforeMethod
+ public void setup() throws Exception {
+
+ resourceBaseVersion = "0.1";
+ serviceBaseVersion = "0.1";
+ designerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ adminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // resourceDetails =
+ // ElementFactory.getDefaultResource("tosca.nodes.newnotgenericresource4testNew",
+ // NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS,
+ // "jh0003");
+ resourceDetails = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, adminUser);
+ serviceDetails = ElementFactory.getDefaultService();
+ serviceUniqueId = "svc_" + serviceDetails.getName().toLowerCase() + "." + serviceBaseVersion;
+ artifactInterfaceType = "standard";
+ artifactOperationName = "start";
+ consumerDataDefinition = createConsumer();
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, adminUser);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+
+ RestResponse createResponse = ConsumerRestUtils.createConsumer(consumerDataDefinition, adminUser);
+ BaseRestUtils.checkCreateResponse(createResponse);
+ }
+
+ @Test
+ public void downloadResourceArtifactSuccess() throws Exception {
+ // Create service
+ RestResponse serviceResponse = ServiceRestUtils.createService(serviceDetails, designerUser);
+ AssertJUnit.assertEquals("Check response code after creating resource", 201,
+ serviceResponse.getErrorCode().intValue());
+
+ // Create resource
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, designerUser);
+ AssertJUnit.assertEquals("Check response code after creating resource", 201,
+ createResource.getErrorCode().intValue());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(createResource.getResponse());
+
+ ArtifactReqDetails artifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ // Setting the name to be with space
+ artifactDetails.setArtifactName("test artifact file.yaml");
+ // artifactDetails.set(ArtifactRestUtils.calculateChecksum(artifactDetails));
+
+ RestResponse addArtifactResponse = ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails,
+ designerUser, resource.getUniqueId(), ArtifactRestUtils.calculateChecksum(artifactDetails));
+ AssertJUnit.assertEquals("Check response code after adding interface artifact", 200,
+ addArtifactResponse.getErrorCode().intValue());
+
+ // Getting expected artifact checksum
+ ArtifactDefinition artifactResp = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addArtifactResponse.getResponse());
+ String expectedPayloadChecksum = artifactResp.getArtifactChecksum();
+
+ Config config = Utils.getConfig();
+ String relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceBaseVersion,
+ ValidationUtils.convertToSystemName(resource.getName()), resource.getVersion(),
+ artifactResp.getArtifactName()));
+ // String fullUrlFormatted =
+ // String.format(Urls.DOWNLOAD_RESOURCE_ARTIFACT_FULL_URL,
+ // config.getCatalogBeHost(),config.getCatalogBePort(), relativeUrl);
+ // String consumerId = "dummy.ecomp";
+
+ ResourceReqDetails resourceInfo = new ResourceReqDetails();
+ resourceInfo.setName(resource.getName());
+ resourceInfo.setVersion(resource.getVersion());
+
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ RestResponse restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceDetails, resourceInfo,
+ artifactDetails, designerUser, authorizationHeaders);
+ // RestResponse restResponse =
+ // artifactUtils.downloadResourceArtifact(designerUser,fullUrlFormatted,
+ // consumerId,true);
+ AssertJUnit.assertEquals("Check response code after download resource", 200,
+ restResponse.getErrorCode().intValue());
+
+ // Validating headers
+ // content disposition
+ List<String> contDispHeaderList = restResponse.getHeaderFields().get(Constants.CONTENT_DISPOSITION_HEADER);
+ AssertJUnit.assertNotNull(contDispHeaderList);
+ AssertJUnit
+ .assertEquals(
+ "Check content disposition header", new StringBuilder().append("attachment; filename=\"")
+ .append(artifactResp.getArtifactName()).append("\"").toString(),
+ contDispHeaderList.get(0));
+
+ // content type
+ List<String> contTypeHeaderList = restResponse.getHeaderFields().get(Constants.CONTENT_TYPE_HEADER);
+ AssertJUnit.assertNotNull(contTypeHeaderList);
+ AssertJUnit.assertEquals("Check content type", "application/octet-stream", contTypeHeaderList.get(0));
+
+ String actualContents = restResponse.getResponse();
+
+ // Contents - comparing decoded content
+ AssertJUnit.assertEquals(artifactDetails.getPayload(), Base64.encodeBase64String(actualContents.getBytes()));
+
+ // validating checksum
+ String actualPayloadChecksum = GeneralUtility.calculateMD5ByByteArray(actualContents.getBytes());
+ AssertJUnit.assertEquals(expectedPayloadChecksum, actualPayloadChecksum);
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction,
+ BaseRestUtils.ecomp, relativeUrl, "200", "OK");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ protected void download_serviceNameNotFound_inner(String serviceName, String serviceVersion, String resourceName,
+ String resourceVersion) throws Exception {
+ Config config = Utils.getConfig();
+ String artifactName = "kuku";
+ ArtifactReqDetails artifact = new ArtifactReqDetails();
+ artifact.setArtifactName(artifactName);
+ String relativeUrl;
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ ServiceReqDetails serviceInfo = new ServiceReqDetails();
+ serviceInfo.setName(serviceName);
+ serviceInfo.setVersion(serviceVersion);
+ RestResponse restResponse = null;
+ if (resourceName != null) {
+ ResourceReqDetails resourceDetailes = new ResourceReqDetails();
+ resourceDetailes.setName(resourceName);
+ resourceDetailes.setVersion(resourceVersion);
+ relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceName), serviceVersion,
+ ValidationUtils.convertToSystemName(resourceName), resourceVersion, artifactName));
+ restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceInfo, resourceDetailes, artifact,
+ designerUser, authorizationHeaders);
+ } else {
+ relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceName), serviceVersion, artifactName));
+ restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceInfo, artifact, designerUser,
+ authorizationHeaders);
+ }
+
+ // RestResponse restResponse =
+ // artifactUtils.downloadResourceArtifact(designerUser,fullUrlFormatted,
+ // consumerId,true);
+ AssertJUnit.assertEquals("Check response code after download resource", 404,
+ restResponse.getErrorCode().intValue());
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction,
+ BaseRestUtils.ecomp, relativeUrl, "404", "SVC4503: Error: Requested '"
+ + ValidationUtils.convertToSystemName(serviceName) + "' service was not found.");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ protected void download_serviceVersionNotFound_inner(String serviceName, String serviceVersion, String resourceName,
+ String resourceVersion) throws Exception {
+ Config config = Utils.getConfig();
+ String artifactName = "kuku";
+ String relativeUrl;
+ ArtifactReqDetails artifact = new ArtifactReqDetails();
+ artifact.setArtifactName(artifactName);
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ ServiceReqDetails serviceInfo = new ServiceReqDetails();
+ serviceInfo.setName(serviceName);
+ serviceInfo.setVersion(serviceVersion);
+ RestResponse restResponse = null;
+ if (resourceName != null) {
+ ResourceReqDetails resourceDetailes = new ResourceReqDetails();
+ resourceDetailes.setName(resourceName);
+ resourceDetailes.setVersion(resourceVersion);
+ relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceName), serviceVersion,
+ ValidationUtils.convertToSystemName(resourceName), resourceVersion, artifactName));
+ restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceInfo, resourceDetailes, artifact,
+ designerUser, authorizationHeaders);
+ } else {
+ relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceName), serviceVersion, artifactName));
+ restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceInfo, artifact, designerUser,
+ authorizationHeaders);
+ }
+ // String fullUrlFormatted =
+ // String.format(Urls.DOWNLOAD_RESOURCE_ARTIFACT_FULL_URL,
+ // config.getCatalogBeHost(),config.getCatalogBePort(), relativeUrl);
+ // String consumerId = "dummy.ecomp";
+
+ // RestResponse restResponse =
+ // artifactUtils.downloadResourceArtifact(designerUser,fullUrlFormatted,
+ // consumerId,true);
+ AssertJUnit.assertEquals("Check response code after download resource", 404,
+ restResponse.getErrorCode().intValue());
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction,
+ BaseRestUtils.ecomp, relativeUrl, "404",
+ "SVC4504: Error: Service version " + serviceVersion + " was not found.");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ protected String encodeUrlForDownload(String url) {
+ return url.replaceAll(" ", "%20");
+ }
+
+ protected ConsumerDataDefinition createConsumer() {
+ ConsumerDataDefinition consumer = new ConsumerDataDefinition();
+ consumer.setConsumerName(USER);
+ consumer.setConsumerSalt(SALT);
+ consumer.setConsumerPassword(HASHED_PASSWORD);
+ return consumer;
+
+ }
+
+ @Test(enabled = false)
+ public void downloadServiceArtifactSuccess() throws Exception {
+ // Create service
+ RestResponse serviceResponse = ServiceRestUtils.createService(serviceDetails, designerUser);
+ assertEquals("Check response code after creating resource", 201, serviceResponse.getErrorCode().intValue());
+ serviceUniqueId = ResponseParser.convertServiceResponseToJavaObject(serviceResponse.getResponse())
+ .getUniqueId();
+
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultDeploymentArtifactForType("MURANO_PKG");
+
+ RestResponse addArtifactResponse = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails,
+ designerUser, serviceUniqueId, ArtifactRestUtils.calculateMD5Header(artifactDetails));
+ assertEquals("Check response code after adding interface artifact", 200,
+ addArtifactResponse.getErrorCode().intValue());
+
+ // Getting expected artifact checksum
+
+ // ArtifactResJavaObject artifactResp =
+ // artifactUtils.parseInformationalArtifactResp(addArtifactResponse);
+ String expectedPayloadChecksum = ResponseParser
+ .convertArtifactDefinitionResponseToJavaObject(addArtifactResponse.getResponse()).getArtifactChecksum();
+
+ String artifactName = ValidationUtils.normalizeFileName(artifactDetails.getArtifactName());
+
+ String relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceBaseVersion, artifactName));
+
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, artifactDetails,
+ designerUser, authorizationHeaders);
+ assertEquals("Check response code after download resource", 200, restResponse.getErrorCode().intValue());
+
+ // Validating headers
+ // content disposition
+ List<String> contDispHeaderList = restResponse.getHeaderFields().get(Constants.CONTENT_DISPOSITION_HEADER);
+ assertNotNull(contDispHeaderList);
+ assertEquals("Check content disposition header",
+ new StringBuilder().append("attachment; filename=\"").append(artifactName).append("\"").toString(),
+ contDispHeaderList.get(0));
+
+ // content type
+ List<String> contTypeHeaderList = restResponse.getHeaderFields().get(Constants.CONTENT_TYPE_HEADER);
+ assertNotNull(contTypeHeaderList);
+ assertEquals("Check content type", "application/octet-stream", contTypeHeaderList.get(0));
+
+ String actualContents = restResponse.getResponse();
+
+ assertEquals(artifactDetails.getPayload(), Base64.encodeBase64String(actualContents.getBytes()));
+
+ // validating checksum
+ byte[] bytes = actualContents.getBytes();
+ String actualPayloadChecksum = GeneralUtility.calculateMD5ByByteArray(bytes);
+ assertEquals(expectedPayloadChecksum, actualPayloadChecksum);
+
+ // validating valid zip
+ InputStream is = new ByteArrayInputStream(bytes);
+ InputStream zis = new ZipInputStream(is);
+ zis.close();
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction,
+ ResourceRestUtils.ecomp, encodeUrlForDownload(relativeUrl), "200", "OK");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ @Test
+ public void downloadResourceArtifact_NoConsumerId() throws Exception {
+
+ String artifactName = "kuku";
+ ArtifactReqDetails artifact = new ArtifactReqDetails();
+ artifact.setArtifactName(artifactName);
+ ResourceReqDetails resource = new ResourceReqDetails();
+ resource.setName("notExisting");
+ resource.setVersion("0.1");
+ String relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceBaseVersion,
+ ValidationUtils.convertToSystemName(resource.getName()), resource.getVersion(), artifactName));
+ serviceDetails.setVersion("0.1");
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ RestResponse restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceDetails, resource, artifact,
+ designerUser, authorizationHeaders, false);
+ assertEquals("Check response code after download resource", 400, restResponse.getErrorCode().intValue());
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction, "",
+ relativeUrl, "400", "POL5001: Error: Missing 'X-ECOMP-InstanceID' HTTP header.");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ @Test
+ public void downloadResourceArtifact_ResourceNameNotFound() throws Exception {
+
+ String artifactName = "kuku";
+ ArtifactReqDetails artifact = new ArtifactReqDetails();
+ artifact.setArtifactName(artifactName);
+ ResourceReqDetails resource = new ResourceReqDetails();
+ resource.setName("notExisting");
+ resource.setVersion("0.1");
+ serviceDetails.setVersion("0.1");
+ String relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceDetails.getVersion(),
+ ValidationUtils.convertToSystemName(resource.getName()), resource.getVersion(), artifactName));
+
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ RestResponse restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceDetails, resource, artifact,
+ designerUser, authorizationHeaders);
+
+ assertEquals("Check response code after download resource", 404, restResponse.getErrorCode().intValue());
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction,
+ BaseRestUtils.ecomp, relativeUrl, "404",
+ "SVC4063: Error: Requested 'Notexisting' resource was not found.");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ @Test
+ public void downloadResourceArtifact_ResourceVersionNotFound() throws Exception {
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, designerUser);
+ assertEquals("Check response code after creating resource", 201, createResource.getErrorCode().intValue());
+
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(createResource.getResponse());
+ ResourceReqDetails resourceDetailes = new ResourceReqDetails();
+ resourceDetailes.setName(resource.getName());
+ resourceDetailes.setVersion("0.2");
+
+ serviceDetails.setVersion("0.1");
+
+ String artifactName = "kuku";
+ ArtifactReqDetails artifact = new ArtifactReqDetails();
+ artifact.setArtifactName(artifactName);
+
+ String relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL,
+ ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceBaseVersion,
+ ValidationUtils.convertToSystemName(resourceDetailes.getName()), resourceDetailes.getVersion(),
+ artifactName));
+
+ Map<String, String> authorizationHeaders = new HashMap<String, String>();
+ authorizationHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ RestResponse restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceDetails, resourceDetailes,
+ artifact, designerUser, authorizationHeaders);
+ assertEquals("Check response code after download resource", 404, restResponse.getErrorCode().intValue());
+
+ // validate audit
+ String auditAction = "DArtifactDownload";
+
+ ExpectedDistDownloadAudit expectedDistDownloadAudit = new ExpectedDistDownloadAudit(auditAction,
+ BaseRestUtils.ecomp, relativeUrl, "404", "SVC4504: Error: Resource version 0.2 was not found.");
+ AuditValidationUtils.validateAudit(expectedDistDownloadAudit, auditAction);
+ }
+
+ @Test
+ public void downloadResourceArtifact_ServiceNameNotFound() throws Exception {
+ // Create resource
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, designerUser);
+ assertEquals("Check response code after creating resource", 201, createResource.getErrorCode().intValue());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(createResource.getResponse());
+ download_serviceNameNotFound_inner("notExistingServiceName", serviceBaseVersion, resource.getName(),
+ resource.getVersion());
+
+ }
+
+ @Test
+ public void downloadResourceArtifact_ServiceVersionNotFound() throws Exception {
+ // Create resource
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, designerUser);
+ assertEquals("Check response code after creating resource", 201, createResource.getErrorCode().intValue());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(createResource.getResponse());
+
+ // Create service
+ RestResponse serviceResponse = ServiceRestUtils.createService(serviceDetails, designerUser);
+ assertEquals("Check response code after creating resource", 201, serviceResponse.getErrorCode().intValue());
+ serviceUniqueId = ResponseParser.convertServiceResponseToJavaObject(serviceResponse.getResponse())
+ .getUniqueId();
+
+ download_serviceVersionNotFound_inner(serviceDetails.getName(), "0.3", resource.getName(),
+ resource.getVersion());
+ }
+
+ @Test
+ public void downloadServiceArtifact_ServiceNameNotFound() throws Exception {
+ download_serviceNameNotFound_inner("notExistingServiceName", serviceBaseVersion, null, null);
+
+ }
+
+ @Test
+ public void downloadServiceArtifact_ServiceVersionNotFound() throws Exception {
+
+ // Create service
+ RestResponse serviceResponse = ServiceRestUtils.createService(serviceDetails, designerUser);
+ assertEquals("Check response code after creating resource", 201, serviceResponse.getErrorCode().intValue());
+ serviceUniqueId = ResponseParser.convertServiceResponseToJavaObject(serviceResponse.getResponse())
+ .getUniqueId();
+
+ download_serviceVersionNotFound_inner(serviceDetails.getName(), "0.2", null, null);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/externalapi/DownloadArtifactsTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/externalapi/DownloadArtifactsTest.java
new file mode 100644
index 0000000000..3325227ebc
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/externalapi/DownloadArtifactsTest.java
@@ -0,0 +1,338 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.externalapi;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class DownloadArtifactsTest extends ComponentBaseTest {
+ @Rule
+ public static TestName name = new TestName();
+
+ Gson gson = new Gson();
+
+ public DownloadArtifactsTest() {
+ super(name, DownloadArtifactsTest.class.getName());
+ }
+
+ private User sdncDesignerDetails;
+ private User sdncAdminDetails;
+ private ImportReqDetails resourceDetailsVF_01;
+ private ResourceReqDetails resourceDetailsVF_02;
+ private ResourceReqDetails resourceDetailsVF_03;
+ private ResourceReqDetails resourceDetailsCP_01;
+ private ServiceReqDetails serviceDetails_01;
+ private ServiceReqDetails serviceDetails_02;
+ public static String rootPath = System.getProperty("user.dir");
+
+ @BeforeMethod(alwaysRun = true)
+ public void before() throws Exception {
+ init();
+ createComponents();
+ }
+
+ private void createComponents() throws Exception {
+ createAtomicResource(resourceDetailsCP_01);
+ importVfWithArtifacts(resourceDetailsVF_01);
+ createVF(resourceDetailsVF_03);
+ createVF(resourceDetailsVF_02);
+ createService(serviceDetails_01);
+ }
+
+ public void init() {
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ resourceDetailsVF_01 = ElementFactory.getDefaultImportResourceByType("VF100", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VF.toString());
+ resourceDetailsVF_02 = ElementFactory.getDefaultResourceByType("VF200", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VF.toString());
+ resourceDetailsVF_03 = ElementFactory.getDefaultResourceByType("VF300", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(), ResourceTypeEnum.VF.toString());
+ resourceDetailsCP_01 = ElementFactory.getDefaultResourceByType("CP100", NormativeTypesEnum.PORT, ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, sdncDesignerDetails.getUserId(), ResourceTypeEnum.CP.toString());
+ serviceDetails_01 = ElementFactory.getDefaultService("newtestservice1", ServiceCategoriesEnum.MOBILITY, sdncDesignerDetails.getUserId());
+ serviceDetails_02 = ElementFactory.getDefaultService("newtestservice2", ServiceCategoriesEnum.MOBILITY, sdncDesignerDetails.getUserId());
+ }
+
+ @Test
+ public void downloadResourceInstanceArtifactsFromServiceTest() throws Exception {
+ Service service = createServiceWithRIsWithArtifacts();
+ Map<String, ArtifactDefinition> deploymentArtifacts;
+ List<ComponentInstance> resourceInstances = service.getComponentInstances();
+ for (ComponentInstance ri : resourceInstances) {
+ deploymentArtifacts = ri.getDeploymentArtifacts();
+ for (ArtifactDefinition artifact : deploymentArtifacts.values()) {
+ assertNotNull(downloadResourceInstanceArtifact(service, ri, artifact));
+ }
+ }
+ }
+
+ @Test
+ public void downloadServiceArtifactsTest() throws Exception {
+ Service service = createServiceWithArtifacts();
+ Map<String, ArtifactDefinition> deploymentArtifacts = service.getDeploymentArtifacts();
+ for (ArtifactDefinition artifact : deploymentArtifacts.values()) {
+ assertNotNull(downloadServiceArtifact(service, artifact));
+ }
+
+ }
+
+ private Service createServiceWithArtifacts() throws Exception {
+
+ ArtifactReqDetails otherArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.OTHER.getType());
+
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(otherArtifactDetails, sdncDesignerDetails, serviceDetails_01.getUniqueId());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ ArtifactReqDetails yangXmlArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.YANG_XML.getType());
+
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(yangXmlArtifactDetails, sdncDesignerDetails, serviceDetails_01.getUniqueId());
+ assertTrue("response code is not BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + addInformationalArtifactToService.getErrorCode(), addInformationalArtifactToService.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ RestResponse createServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ return ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse());
+ }
+
+ private RestResponse downloadResourceInstanceArtifact(Service service, ComponentInstance ri, ArtifactDefinition artifact) throws Exception {
+ String url = String.format(Urls.GET_DOWNLOAD_SERVICE_RI_ARTIFACT, "localhost", "8080", service.getUUID(), ri.getNormalizedName(), artifact.getArtifactUUID());
+ String userId = sdncDesignerDetails.getUserId();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.CACHE_CONTROL.getValue(), "no-cache");
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), "Basic dGVzdDoxMjM0NTY=");
+ headersMap.put("X-ECOMP-InstanceID", "test");
+ if (userId != null) {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ }
+ sendAuthorizationRequest();
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendGet(url, headersMap);
+ if (response.getErrorCode() != 200 && response.getResponse().getBytes() == null && response.getResponse().getBytes().length == 0) {
+ return null;
+ }
+ return response;
+ }
+
+ private RestResponse downloadServiceArtifact(Service service, ArtifactDefinition artifact) throws Exception {
+ String url = String.format(Urls.GET_DOWNLOAD_SERVICE_ARTIFACT, "localhost", "8080", service.getUUID(), artifact.getArtifactUUID());
+ String userId = sdncDesignerDetails.getUserId();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.CACHE_CONTROL.getValue(), "no-cache");
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), "Basic dGVzdDoxMjM0NTY=");
+ headersMap.put("X-ECOMP-InstanceID", "test");
+ if (userId != null) {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ }
+ sendAuthorizationRequest();
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendGet(url, headersMap);
+ if (response.getErrorCode() != 200 && response.getResponse().getBytes() == null && response.getResponse().getBytes().length == 0) {
+ return null;
+ }
+ return response;
+
+ }
+
+ private RestResponse sendAuthorizationRequest() throws IOException {
+ String url = String.format(Urls.POST_AUTHORIZATION, "localhost", "8080");
+ String userId = sdncAdminDetails.getUserId();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.CACHE_CONTROL.getValue(), "no-cache");
+ if (userId != null) {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ }
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, "{\"consumerName\":\"test\",\"consumerPassword\":\"0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b\",\"consumerSalt\":\"2a1f887d607d4515d4066fe0f5452a50\"}", headersMap);
+ if (response.getErrorCode() != 201) {
+ return null;
+ }
+ return response;
+ }
+
+ private Service createServiceWithRIsWithArtifacts() throws Exception {
+ serviceDetails_02.setUniqueId(serviceDetails_01.getUniqueId());
+ createTreeCheckedinVFInstances();
+ LifecycleRestUtils.changeResourceState(resourceDetailsCP_01, sdncDesignerDetails, "0.1", LifeCycleStatesEnum.CHECKIN);
+ createVFInstanceAndAtomicResourceInstanceWithoutCheckin(resourceDetailsVF_01, resourceDetailsCP_01, sdncDesignerDetails);
+ RestResponse updateServiceResp = ServiceRestUtils.updateService(serviceDetails_02, sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(updateServiceResp);
+ getComponentAndValidateRIs(serviceDetails_01, 5, 0);
+
+ return ResponseParser.convertServiceResponseToJavaObject(updateServiceResp.getResponse());
+ }
+
+ private void createTreeCheckedinVFInstances() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createSecondVFInstResp);
+ RestResponse createThirdVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_03, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createThirdVFInstResp);
+ }
+
+ private Component getComponentAndValidateRIs(ComponentReqDetails componentDetails, int numberOfRIs, int numberOfRelations) throws IOException, Exception {
+
+ RestResponse getResponse = null;
+ Component component = null;
+ if (componentDetails instanceof ResourceReqDetails) {
+ getResponse = ResourceRestUtils.getResource(sdncAdminDetails, componentDetails.getUniqueId());
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Resource.class);
+ } else if (componentDetails instanceof ServiceReqDetails) {
+ getResponse = ServiceRestUtils.getService((ServiceReqDetails) componentDetails, sdncAdminDetails);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Service.class);
+ } else if (componentDetails instanceof ProductReqDetails) {
+ getResponse = ProductRestUtils.getProduct(componentDetails.getUniqueId(), sdncAdminDetails.getUserId());
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Product.class);
+ } else {
+ Assert.fail("Unsupported type of componentDetails - " + componentDetails.getClass().getSimpleName());
+ }
+ ResourceRestUtils.checkSuccess(getResponse);
+ int numberOfActualRIs = component.getComponentInstances() != null ? component.getComponentInstances().size() : 0;
+ int numberOfActualRelations = component.getComponentInstancesRelations() != null ? component.getComponentInstancesRelations().size() : 0;
+ assertEquals("Check number of RIs meet the expected number", numberOfRIs, numberOfActualRIs);
+ assertEquals("Check number of RI relations meet the expected number", numberOfRelations, numberOfActualRelations);
+
+ return component;
+ }
+
+ private void createVFInstanceAndAtomicResourceInstanceWithoutCheckin(ResourceReqDetails vf, ResourceReqDetails atomicResource, User user) throws Exception {
+ RestResponse createVFInstance = createVFInstance(serviceDetails_01, vf, user);
+ ResourceRestUtils.checkCreateResponse(createVFInstance);
+ RestResponse atomicInstanceForService = createAtomicInstanceForService(serviceDetails_01, atomicResource, user);
+ ResourceRestUtils.checkCreateResponse(atomicInstanceForService);
+ }
+
+ private RestResponse createCheckedinVFInstance(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ changeResourceLifecycleState(compInstOriginDetails, modifier.getUserId(), LifeCycleStatesEnum.CHECKIN);
+ return createVFInstance(containerDetails, compInstOriginDetails, modifier);
+ }
+
+ private RestResponse createVFInstance(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE, true);
+ }
+
+ private RestResponse createAtomicInstanceForService(ServiceReqDetails containerDetails, ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE, true);
+ }
+
+ private RestResponse createComponentInstance(ComponentReqDetails containerDetails, ComponentReqDetails compInstOriginDetails, User modifier, ComponentTypeEnum containerComponentTypeEnum, boolean isHighestLevel) throws IOException, Exception {
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory.getComponentResourceInstance(compInstOriginDetails);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, modifier, containerDetails.getUniqueId(), containerComponentTypeEnum);
+ return createResourceInstanceResponse;
+ }
+
+ private void changeResourceLifecycleState(ResourceReqDetails resourceDetails, String userId, LifeCycleStatesEnum lifeCycleStates) throws Exception {
+ RestResponse response = LifecycleRestUtils.changeResourceState(resourceDetails, userId, lifeCycleStates);
+ LifecycleRestUtils.checkLCS_Response(response);
+ }
+
+ private void createAtomicResource(ResourceReqDetails resourceDetails) throws Exception {
+ RestResponse createResourceResponse = ResourceRestUtils.createResource(resourceDetails, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceResponse);
+
+ }
+
+ private void createVF(ResourceReqDetails resourceDetails) throws Exception {
+ createVF(resourceDetails, sdncDesignerDetails);
+
+ }
+
+ private void createVF(ResourceReqDetails resourceDetails, User sdncModifier) throws Exception {
+ RestResponse createVfResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifier);
+ ResourceRestUtils.checkCreateResponse(createVfResponse);
+ }
+
+ private void createService(ServiceReqDetails serviceDetails) throws Exception {
+ createService(serviceDetails, sdncDesignerDetails);
+ }
+
+ private void createService(ServiceReqDetails serviceDetails, User sdncModifier) throws Exception {
+ RestResponse createServiceResponse = ServiceRestUtils.createService(serviceDetails, sdncModifier);
+ ResourceRestUtils.checkCreateResponse(createServiceResponse);
+ }
+
+ private void importVfWithArtifacts(ImportReqDetails resourceDetailsVF_01) throws Exception {
+ String payloadName = "VF_RI2_G4_withArtifacts.csar";
+ Path path = Paths.get(rootPath + "/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts.csar");
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetailsVF_01.setPayloadData(payloadData);
+
+ resourceDetailsVF_01.setPayloadName(payloadName);
+ resourceDetailsVF_01.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetailsVF_01, sdncDesignerDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/BasicHttpAuthenticationTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/BasicHttpAuthenticationTest.java
new file mode 100644
index 0000000000..83a50baeeb
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/BasicHttpAuthenticationTest.java
@@ -0,0 +1,403 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+//US505653
+package org.openecomp.sdc.ci.tests.execute.general;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedAuthenticationAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ConsumerRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class BasicHttpAuthenticationTest extends ComponentBaseTest {
+
+ protected static final String AUTH_FAILED_INVALID_AUTHENTICATION_HEADER = "AUTH_FAILED_INVALID_AUTHENTICATION_HEADER";
+
+ protected static final String AUTH_SUCCESS = "AUTH_SUCCESS";
+
+ protected static final String AUTH_FAILED_INVALID_PASSWORD = "AUTH_FAILED_INVALID_PASSWORD";
+
+ protected static final String AUTH_FAILED_USER_NOT_FOUND = "AUTH_FAILED_USER_NOT_FOUND";
+
+ protected static final String AUTH_REQUIRED = "AUTH_REQUIRED";
+
+ protected static final String WWW_AUTHENTICATE = "WWW-Authenticate";
+
+ // user ci password 123456
+ // protected final String authorizationHeader = "Basic Y2k6MTIzNDU2";
+ // user ci password 123456
+ protected final String USER = "ci";
+
+ protected final String PASSWORD = "123456";
+
+ protected final String SALT = "2a1f887d607d4515d4066fe0f5452a50";
+
+ protected final String HASHED_PASSWORD = "0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b";
+
+ protected User sdncAdminUserDetails;
+
+ protected ConsumerDataDefinition consumerDataDefinition;
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected User sdncUserDetails;
+
+ protected ArtifactReqDetails deploymentArtifact;
+
+ protected ExpectedAuthenticationAudit expectedAuthenticationAudit;
+
+ protected final String auditAction = "HttpAuthentication";
+
+ protected String expectedDownloadServiceUrl;
+ protected String expectedDownloadResourceUrl;
+ protected ComponentInstanceReqDetails componentInstanceReqDetails;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public BasicHttpAuthenticationTest() {
+ super(name, BasicHttpAuthenticationTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ Resource resourceObject = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetails = new ResourceReqDetails(resourceObject);
+ Service serviceObject = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ serviceDetails = new ServiceReqDetails(serviceObject);
+
+ deploymentArtifact = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(deploymentArtifact, sdncUserDetails, resourceDetails.getUniqueId());
+ AssertJUnit.assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(), response.getErrorCode() == 200);
+
+ componentInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+ // certified resource
+ response = LifecycleRestUtils.certifyResource(resourceDetails);
+ AssertJUnit.assertTrue("certify resource request returned status:" + response.getErrorCode(), response.getErrorCode() == 200);
+
+ // add resource instance with HEAT deployment artifact to the service
+ componentInstanceReqDetails.setComponentUid(resourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(componentInstanceReqDetails, sdncUserDetails, serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ AssertJUnit.assertTrue("response code is not 201, returned: " + response.getErrorCode(), response.getErrorCode() == 201);
+ expectedAuthenticationAudit = new ExpectedAuthenticationAudit();
+
+ // RestResponse addDeploymentArtifactResponse =
+ // ArtifactRestUtils.addInformationalArtifactToService(deploymentArtifact,
+ // sdncUserDetails, serviceDetails.getUniqueId());
+ // assertEquals("didn't succeed to upload deployment artifact", 200,
+ // addDeploymentArtifactResponse.getErrorCode().intValue());
+ //
+ // downloadUrl =
+ // String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL,
+ // ValidationUtils.convertToSystemName(serviceDetails.getServiceName()),
+ // serviceDetails.getVersion(),
+ // ValidationUtils.normalizeFileName(deploymentArtifact.getArtifactName()));
+
+ expectedDownloadResourceUrl = String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL, ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceDetails.getVersion(),
+ ValidationUtils.convertToSystemName(resourceDetails.getName()), resourceDetails.getVersion(), ValidationUtils.normalizeFileName(deploymentArtifact.getArtifactName()));
+ expectedDownloadResourceUrl = expectedDownloadResourceUrl.substring("/asdc/".length(), expectedDownloadResourceUrl.length());
+
+ expectedDownloadServiceUrl = String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL, ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceDetails.getVersion(),
+ ValidationUtils.normalizeFileName(deploymentArtifact.getArtifactName()));
+ expectedDownloadServiceUrl = expectedDownloadServiceUrl.substring("/asdc/".length(), expectedDownloadServiceUrl.length());
+
+ sdncAdminUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ consumerDataDefinition = createConsumer();
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+ ;
+
+ RestResponse createResponse = ConsumerRestUtils.createConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ BaseRestUtils.checkCreateResponse(createResponse);
+
+ }
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+ ;
+ }
+
+ @Test
+ public void sendAuthenticatedRequestTest_success() throws Exception, Exception {
+ DbUtils.cleanAllAudits();
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(USER, PASSWORD);
+ // RestResponse restResponse =
+ // ArtifactRestUtils.downloadServiceArtifact(serviceDetails,
+ // deploymentArtifact, sdncUserDetails, authorizationHeader);
+ RestResponse restResponse = ArtifactRestUtils.downloadResourceArtifact(serviceDetails, resourceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ AssertJUnit.assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+ AssertJUnit.assertFalse(restResponse.getHeaderFields().containsKey(HttpHeaderEnum.WWW_AUTHENTICATE.getValue()));
+
+ validateAuditAuthentication(USER, AUTH_SUCCESS, ComponentTypeEnum.RESOURCE);
+
+ }
+
+ protected void validateAuditAuthentication(String userName, String AuthStatus, ComponentTypeEnum compType) throws Exception {
+ if (compType.equals(ComponentTypeEnum.RESOURCE)) {
+ expectedAuthenticationAudit = new ExpectedAuthenticationAudit(expectedDownloadResourceUrl, userName, auditAction, AuthStatus);
+ } else {
+ expectedAuthenticationAudit = new ExpectedAuthenticationAudit(expectedDownloadServiceUrl, userName, auditAction, AuthStatus);
+ }
+ AuditValidationUtils.validateAuthenticationAudit(expectedAuthenticationAudit);
+ }
+
+ protected ConsumerDataDefinition createConsumer() {
+ ConsumerDataDefinition consumer = new ConsumerDataDefinition();
+ consumer.setConsumerName(USER);
+ consumer.setConsumerSalt(SALT);
+ consumer.setConsumerPassword(HASHED_PASSWORD);
+ return consumer;
+
+ }
+
+ @Test
+ public void sendAuthenticatedRequestWithoutHeadersTest() throws Exception, Exception {
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, new HashMap<String, String>());
+ assertEquals("Check response code after download artifact", 401, restResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.AUTH_REQUIRED.name(), new ArrayList<String>(), restResponse.getResponse());
+ assertTrue(restResponse.getHeaderFields().containsKey(WWW_AUTHENTICATE));
+ List<String> getAuthenticateHeader = restResponse.getHeaderFields().get(WWW_AUTHENTICATE);
+ assertEquals("www-authenticate header contains more then one value", 1, getAuthenticateHeader.size());
+ assertTrue(getAuthenticateHeader.get(0).equals("Basic realm=" + "\"ASDC\""));
+
+ validateAuditAuthentication("", AUTH_REQUIRED, ComponentTypeEnum.SERVICE);
+ }
+
+ @Test
+ public void sendAuthenticatedRequestTest_userIsNotProvsioned() throws Exception, Exception {
+ String userName = "shay";
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(userName, "123456");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 403, restResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.AUTH_FAILED.name(), new ArrayList<String>(), restResponse.getResponse());
+ assertFalse(restResponse.getHeaderFields().containsKey(WWW_AUTHENTICATE));
+
+ validateAuditAuthentication(userName, AUTH_FAILED_USER_NOT_FOUND, ComponentTypeEnum.SERVICE);
+ }
+
+ @Test
+ public void sendAuthenticatedRequestTest_userIsNull() throws Exception, Exception {
+ String userName = "";
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(userName, "123456");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 403, restResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.AUTH_FAILED.name(), new ArrayList<String>(), restResponse.getResponse());
+ assertFalse(restResponse.getHeaderFields().containsKey(WWW_AUTHENTICATE));
+
+ validateAuditAuthentication(userName, AUTH_FAILED_USER_NOT_FOUND, ComponentTypeEnum.SERVICE);
+ }
+
+ @Test
+ public void sendAuthenticatedRequestTest_passwordIsNull() throws Exception, Exception {
+ String userName = "ci";
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(userName, "");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 403, restResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.AUTH_FAILED.name(), new ArrayList<String>(), restResponse.getResponse());
+ assertFalse(restResponse.getHeaderFields().containsKey(WWW_AUTHENTICATE));
+
+ validateAuditAuthentication(userName, AUTH_FAILED_INVALID_PASSWORD, ComponentTypeEnum.SERVICE);
+ }
+
+ @Test
+ public void sendAuthenticatedRequestTest_passowrdIsNotValidated() throws Exception, Exception {
+ String userCi = "ci";
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(userCi, "98765");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 403, restResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.AUTH_FAILED.name(), new ArrayList<String>(), restResponse.getResponse());
+ assertFalse(restResponse.getHeaderFields().containsKey(HttpHeaderEnum.WWW_AUTHENTICATE.getValue()));
+
+ validateAuditAuthentication(userCi, AUTH_FAILED_INVALID_PASSWORD, ComponentTypeEnum.SERVICE);
+ }
+
+ @Test
+ public void sendAuthenticatedRequestTest_InvalidHeader() throws Exception, Exception {
+ String userCredentials = USER + ":" + PASSWORD;
+ byte[] encodeBase64 = Base64.encodeBase64(userCredentials.getBytes());
+ String encodedUserCredentials = new String(encodeBase64);
+ Map<String, String> authorizationHeader = new HashMap<String, String>();
+ authorizationHeader.put(HttpHeaderEnum.AUTHORIZATION.getValue(), encodedUserCredentials);
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 400, restResponse.getErrorCode().intValue());
+ assertFalse(restResponse.getHeaderFields().containsKey(HttpHeaderEnum.WWW_AUTHENTICATE.getValue()));
+
+ validateAuditAuthentication("", AUTH_FAILED_INVALID_AUTHENTICATION_HEADER, ComponentTypeEnum.SERVICE);
+ }
+
+ @Test(enabled = false)
+ public void sendTwoAuthenticatedRequestsTest() throws Exception, Exception {
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(USER, PASSWORD);
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+
+ RestResponse secondRestResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after second download artifact", 200, secondRestResponse.getErrorCode().intValue());
+ }
+
+ @Test(enabled = false)
+ public void sendAuthenticatedRequestTest_userValidation_1() throws Exception, Exception {
+
+ ConsumerDataDefinition consumer = new ConsumerDataDefinition();
+ consumer.setConsumerName("cI2468");
+ consumer.setConsumerPassword(HASHED_PASSWORD);
+ consumer.setConsumerSalt(SALT);
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumer, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+
+ RestResponse createResponse = ConsumerRestUtils.createConsumer(consumer, sdncAdminUserDetails);
+ BaseRestUtils.checkCreateResponse(createResponse);
+
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(consumer.getConsumerName(), PASSWORD);
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+
+ deleteResponse = ConsumerRestUtils.deleteConsumer(consumer, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+ }
+
+ // ECOMP Consumer Name - UTF-8 string up to 255 characters containing the
+ // following characters : ( maybe to limit 4-64 chars ? )
+ // Lowercase characters {a-z}
+ // Uppercase characters {A-Z}
+ // Numbers {0-9}
+ // Dash {-}; this character is not supported as the first character in the
+ // user name
+ // Period {.}; this character is not supported as the first character in the
+ // user name
+ // Underscore {_}
+ // @Ignore("add manually user:password 24-!68:123456 to
+ // users-configuration.yaml in runtime")
+ @Test(enabled = false)
+ public void sendAuthenticatedRequestTest_userValidation_2() throws Exception, Exception {
+ ConsumerDataDefinition consumer = new ConsumerDataDefinition();
+ consumer.setConsumerName("24-!68");
+ consumer.setConsumerPassword(HASHED_PASSWORD);
+ consumer.setConsumerSalt(SALT);
+ RestResponse deleteResponse = ConsumerRestUtils.deleteConsumer(consumer, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+
+ RestResponse createResponse = ConsumerRestUtils.createConsumer(consumer, sdncAdminUserDetails);
+ BaseRestUtils.checkCreateResponse(createResponse);
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader(consumer.getConsumerName(), PASSWORD);
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+
+ deleteResponse = ConsumerRestUtils.deleteConsumer(consumer, sdncAdminUserDetails);
+ BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed", false, 404, 200);
+ }
+
+ // this is invalide becouse we do not use the : any more
+ // @Ignore("can't exectue, yaml file does not allow to enter more then one
+ // colon continuously (\":\") ")
+ @Test(enabled = false)
+ public void sendAuthenticatedRequestTest_userValidation_3() throws Exception, Exception {
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader("a:", "123456");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+ }
+
+ //
+ // * ECOMP Consumer Password - expected to be SHA-2 256 encrypted value (
+ // SALT + "real" password ) => maximal length 256 bytes = 32 characters
+ // Before storing/comparing please convert upper case letter to lower.
+ // The "normalized" encrypted password should match the following format :
+ // [a-z0-9]
+ // @Ignore("add manually user:password 2468:123:456 to
+ // users-configuration.yaml in runtime")
+ @Test(enabled = false)
+ public void sendAuthenticatedRequestTest_passwordValidation_1() throws Exception, Exception {
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader("A1", "123:456");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+ }
+
+ // * ECOMP Consumer Password - expected to be SHA-2 256 encrypted value (
+ // SALT + "real" password ) => maximal length 256 bytes = 32 characters
+ // Before storing/comparing please convert upper case letter to lower.
+ // The "normalized" encrypted password should match the following format :
+ // [a-z0-9]
+ @Test(enabled = false)
+ // @Ignore("add manually user:password 2468:Sq123a456B to
+ // users-configuration.yaml in runtime")
+ public void sendAuthenticatedRequestTest_passwordValidation_2() throws Exception, Exception {
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader("B2", "Sq123a456B");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+ }
+
+ // * ECOMP Consumer Password - expected to be SHA-2 256 encrypted value (
+ // SALT + "real" password ) => maximal length 256 bytes = 32 characters
+ // Before storing/comparing please convert upper case letter to lower.
+ // The "normalized" encrypted password should match the following format :
+ // [a-z0-9]
+ @Test
+ // @Ignore("add C3:111T-0-*# to file")
+ public void sendAuthenticatedRequestTest_passwordValidation_3() throws Exception, Exception {
+ Map<String, String> authorizationHeader = BaseRestUtils.addAuthorizeHeader("C3", "111T-0-*#");
+ RestResponse restResponse = ArtifactRestUtils.downloadServiceArtifact(serviceDetails, deploymentArtifact, sdncUserDetails, authorizationHeader);
+ assertEquals("Check response code after download artifact", 200, restResponse.getErrorCode().intValue());
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/FeProxyTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/FeProxyTest.java
new file mode 100644
index 0000000000..e4f7d396be
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/FeProxyTest.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.general;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+public class FeProxyTest extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public FeProxyTest() {
+ super(name, FeProxyTest.class.getName());
+ }
+
+ @Test
+ public void testFeProxy() throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ RestResponse allCategoriesTowardsFe = CategoryRestUtils.getAllCategoriesTowardsFe(defaultUser,
+ BaseRestUtils.RESOURCE_COMPONENT_TYPE);
+ AssertJUnit.assertEquals("Check response code after get categories towards FE", 200,
+ allCategoriesTowardsFe.getErrorCode().intValue());
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/ManageEcompConsumerCredentials.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/ManageEcompConsumerCredentials.java
new file mode 100644
index 0000000000..9e1b151c49
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/ManageEcompConsumerCredentials.java
@@ -0,0 +1,1388 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.general;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.HashMap;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedEcomConsumerAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ConsumerRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class ManageEcompConsumerCredentials extends ComponentBaseTest {
+
+ protected static final String ADD_ECOMP_USER_CREDENTIALS = "AddECOMPUserCredentials";
+ protected static final String DELETE_ECOMP_USER_CREDENTIALS = "DeleteECOMPUserCredentials";
+ protected static final String GET_ECOMP_USER_CREDENTIALS = "GetECOMPUserCredentials";
+
+ public static final String contentTypeHeaderData = "application/json";
+ public static final String acceptHeaderData = "application/json";
+
+ public static final int STATUS_CODE_SUCCESS = 200;
+ public static final int STATUS_CODE_SUCSESS_CREATED = 201;
+ public static final int STATUS_CODE_SUCCESS_DELETE_GET = 200;
+ public static final int STATUS_CODE_INVALID_CONTENT = 400;
+ public static final int STATUS_CODE_MISSING_DATA = 400;
+ public static final int STATUS_CODE_MISSING_INFORMATION = 403;
+ public static final int STATUS_CODE_RESTRICTED_ACCESS = 403;
+
+ public static final int STATUS_CODE_NOT_FOUND = 404;
+ public static final int STATUS_CODE_RESTRICTED_OPERATION = 409;
+
+ protected static Gson gson = new Gson();
+ protected ConsumerDataDefinition consumerDataDefinition;
+ protected User sdncAdminUserDetails;
+ protected User sdncDesignerUserDetails;
+ protected User sdncTesterUserDetails;
+ protected User sdncGovernorUserDetails;
+ protected User sdncOpsUserDetails;
+
+ public ManageEcompConsumerCredentials() {
+ super(name, ManageEcompConsumerCredentials.class.getName());
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ protected String salt = "123456789012345678901234567890ab";
+ protected String password = "123456789012345678901234567890ab123456789012345678901234567890ab";
+ protected String ecompUser = "benny";
+
+ protected Long consumerDetailsLastupdatedtime;
+
+ @BeforeMethod
+ public void init() throws Exception {
+ sdncAdminUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncDesignerUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncTesterUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncGovernorUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ sdncOpsUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+
+ consumerDataDefinition = new ConsumerDataDefinition();
+ consumerDataDefinition.setConsumerName(ecompUser);
+ consumerDataDefinition.setConsumerPassword(password);
+ consumerDataDefinition.setConsumerSalt(salt);
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+
+ }
+
+ // US563681 manage ECOMP consumer credentials - DELETE/GET
+ @Test
+ public void deleteEcompCredentialsMethodDelete() throws Exception {
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ AssertJUnit.assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ AssertJUnit.assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ // DbUtils.deleteFromEsDbByPattern("_all");
+ DbUtils.cleanAllAudits();
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ AssertJUnit.assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS_DELETE_GET,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCCESS_DELETE_GET);
+ // Get Consumer to verify that consumer user does not exist
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ AssertJUnit.assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ //// US561728 CREATE ECOMP consumer credentials
+ @Test
+ public void createEcompCredentialsMethodPost() throws Exception {
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ // Validate actual consumerData to returned from response
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ }
+
+ @Test(enabled = false)
+ public void createEcompCredentialsUserAlreayExist() throws Exception {
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+
+ // Create consumer which already exists with different password and Salt
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerPassword("zxcvb");
+ consumerDataDefinition.setConsumerSalt("1234567890qwertyuiop1234567890as");
+ createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer with new data
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsByDesigner() throws Exception { // HttpCspUserId header contains Designer UserId
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition, sdncDesignerUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_RESTRICTED_OPERATION, createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND, getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition, sdncDesignerUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ }
+
+ @Test
+ public void createEcompCredentialsByTester() throws Exception { // HttpCspUserId header contains Tester UserId
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition, sdncTesterUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_RESTRICTED_OPERATION, createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND, getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition, sdncTesterUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ }
+
+ @Test
+ public void createEcompCredentialsByOps() throws Exception { // HttpCspUserId header contains OPS UserId
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition, sdncOpsUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncOpsUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ }
+
+ @Test
+ public void createEcompCredentialsByGovernor() throws Exception { // HttpCspUserId header contains Governor UserId Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncGovernorUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncGovernorUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ }
+
+ @Test
+ public void createEcompCredentialsByNoExistingIUser() throws Exception {
+ User noSdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ noSdncUserDetails.setRole("blabla");
+ noSdncUserDetails.setUserId("bt750h");
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition, noSdncUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_ACCESS.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(
+ consumerDataDefinition.getConsumerName() + "," + consumerDataDefinition.getConsumerSalt().toLowerCase()
+ + "," + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ expectedEcomConsumerAuditJavaObject.setModifier("(" + noSdncUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ // user name
+ @Test
+ public void createEcompCredentialsUserNameIsNull() throws Exception {
+ consumerDataDefinition.setConsumerName(null); // SVC4528
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_DATA,
+ createConsumerRest.getErrorCode().intValue());
+ // verify taht consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerSalt().toLowerCase() + ","
+ + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer name"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameIsEmpty() throws Exception {
+ consumerDataDefinition.setConsumerName("");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerSalt().toLowerCase() + ","
+ + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer name"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameIsNotUTF8() throws Exception {
+ consumerDataDefinition.setConsumerName("בני"); // SVC4528
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameMaxLength() throws Exception {
+ consumerDataDefinition.setConsumerName(
+ "_ABCD-.abcdqwertyuiopasdfghjklzxcvbnmqw1234567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890pf34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfgghjklqwertyuiopzaiutrewasdfghjklqwertyuiopzasxcdferf123456.-"); // SVC4528
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Validate actual consumerData to returned from response
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameExceedMaxLength() throws Exception {
+ consumerDataDefinition.setConsumerName(
+ "_ABCD-.abcdqwertyuiopasdfghjklzxcvbnmqw1234567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890pf34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfgghjklqwertyuiopzaiutrewasdfghjklqwertyuiopzasxcdferf123456.--"); // SVC4528
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.EXCEEDS_LIMIT, "Consumer name", "255");
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameLastCharIsDash() throws Exception { // allowed
+ consumerDataDefinition.setConsumerName("ABCD34567890pf34567890poiutrew-");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject , Validate actual consumerData
+ // to returned from response
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameLastCharIsPeriod() throws Exception {
+ consumerDataDefinition.setConsumerName("ABCD34567890pf34567890poiutrew.");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject , Validate actual consumerData
+ // to returned from response
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameLastCharIsUnderscore() throws Exception {
+ consumerDataDefinition.setConsumerName("ABCD34567890pf34567890poiutrew_");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject , Validate actual consumerData
+ // to returned from response
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameFirstCharIsUnderscore() throws Exception {
+ consumerDataDefinition.setConsumerName("_ABCD34567890pf34567890poiutre");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject , Validate actual consumerData
+ // to returned from response
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameFirstCharIsPeriod() throws Exception {
+ consumerDataDefinition.setConsumerName(".ABCD34567890pf34567890poiutre");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_CONTENT_PARAM, "Consumer name");
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameFirstCharIsDash() throws Exception { // Not
+ // allowed
+ consumerDataDefinition.setConsumerName("-ABCD34567890pf34567890poiutre");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_CONTENT_PARAM, "Consumer name");
+ }
+
+ /// Password
+ @Test
+ public void createEcompCredentialsPasswordIsNull() throws Exception {
+ consumerDataDefinition.setConsumerPassword(null);
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_DATA,
+ createConsumerRest.getErrorCode().intValue());
+ // verify taht consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerSalt().toLowerCase());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer password"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsPasswordIsEmpty() throws Exception {
+ consumerDataDefinition.setConsumerPassword("");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_DATA,
+ createConsumerRest.getErrorCode().intValue());
+ // verify taht consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerSalt().toLowerCase());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer password"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsPasswordMaxLength() throws Exception { // password
+ // must
+ // be
+ // 64
+ // chars
+ consumerDataDefinition.setConsumerPassword("123456789012345678901234567890ab123456789012345678901234567890ab");
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ // Validate actual consumerData to returned from response
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ // Delete Consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsPasswordExceeedMaxLength() throws Exception { // password
+ // must
+ // be
+ // 64
+ // chars
+ consumerDataDefinition.setConsumerPassword("123456789012345678901234567890ab123456789012345678901234567890ab1");
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_LENGTH, "Consumer password", "64");
+ }
+
+ @Test
+ public void createEcompCredentiaPasswordValid() throws Exception {
+ // Password Contains lowercase/uppercase characters and numbers -
+ // convert upper case letter to lower
+ consumerDataDefinition.setConsumerPassword("ABCabc1234567890POImnb12345678901234567890POIUzxcvbNMASDFGhjkl12");
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+
+ }
+
+ //// Salt
+ @Test
+ public void createEcompCredentialsSaltIsNull() throws Exception {
+ // Length must be 32 characters
+ consumerDataDefinition.setConsumerSalt(null);
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_DATA,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerPassword().toLowerCase());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer salt"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+
+ }
+
+ @Test
+ public void createEcompCredentialsSaltIsEmpty() throws Exception {
+ consumerDataDefinition.setConsumerSalt("");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_DATA,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerPassword().toLowerCase());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer salt"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsSaltLengthLessThan32() throws Exception {
+ consumerDataDefinition.setConsumerSalt("123456789012345678901234567890a");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_LENGTH, "Consumer salt");
+
+ }
+
+ // Bug
+ @Test
+ public void createEcompCredentialsSaltLengthMoreThan32() throws Exception { // Length
+ // must
+ // be
+ // 32
+ // characters
+ // -
+ // SVC4529
+ // "Error:
+ // Invalid
+ // Content.
+ // %1
+ // exceeds
+ // limit
+ // of
+ // %2
+ // characters."
+ consumerDataDefinition.setConsumerSalt("123456789012345678901234567890abc");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_LENGTH, "Consumer salt");
+
+ }
+
+ @Test
+ public void createEcompCredentialsSaltUppercaseCharacters() throws Exception {
+ // Contains uppercase characters– exception invalid content
+ consumerDataDefinition.setConsumerSalt("123456789012345678901234567890AB");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // verify that consumer didn't created
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_CONTENT_PARAM.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerSalt() + "," + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer salt"));
+ expectedEcomConsumerAuditJavaObject
+ .setModifier(sdncAdminUserDetails.getFullName() + "(" + sdncAdminUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ // USER_ID (USER_ID is taken from USER_ID header)
+
+ @Test
+ public void createEcompCredentialsHttpCspUserIdIsEmpty() throws Exception {
+ // USER_ID is taken from USER_ID header
+ sdncAdminUserDetails.setUserId("");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerSalt() + "," + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer salt"));
+ expectedEcomConsumerAuditJavaObject.setModifier("");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsHttpCspUserIdIsNull() throws Exception { // USER_ID is taken from USER_ID header
+ sdncAdminUserDetails.setUserId(null);
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerSalt() + "," + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer salt"));
+ expectedEcomConsumerAuditJavaObject.setModifier("");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void createEcompCredentialsHttpCspUserIdHeaderIsMissing() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumerHttpCspAtuUidIsMissing(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName() + ","
+ + consumerDataDefinition.getConsumerSalt() + "," + consumerDataDefinition.getConsumerPassword());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc("Consumer salt"));
+ expectedEcomConsumerAuditJavaObject.setModifier("");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ ADD_ECOMP_USER_CREDENTIALS);
+ }
+
+ // add USER_ID in json body
+ @Test
+ public void createEcompCredentiaJsonBodyContainLastModfierAtuid() throws Exception {
+ // Add USER_ID (not admin) to json - we will ignore and create the user
+ HashMap<String, String> jsonMap = new HashMap<String, String>();
+ jsonMap.put("consumerName", "benny");
+ jsonMap.put("consumerPassword", "123456789012345678901234567890ab123456789012345678901234567890ab");
+ jsonMap.put("consumerSalt", "123456789012345678901234567890ab");
+ jsonMap.put("lastModfierAtuid", "cs0008"); // designer
+ Gson gson = new Gson();
+ ConsumerDataDefinition consumer = gson.fromJson(jsonMap.toString(), ConsumerDataDefinition.class);
+
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumer, sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Validate actual consumerData to returned from response
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumer, getConsumerDataObject);
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumer, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumer, sdncAdminUserDetails,
+ STATUS_CODE_SUCSESS_CREATED);
+ // Delete consumer
+ ConsumerRestUtils.deleteConsumer(consumer, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void createEcompCredentialsUserNameNotAllowedCharacters() throws Exception {
+ char invalidChars[] = { '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '+', '=', '<', '>', '?', '/',
+ '"', ':', '}', ']', '[', '{', '|', '\\', ' ', '\t', '\n' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerName(invalidChars[i] + "ABCdef123");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_CONTENT_PARAM, "Consumer name");
+ }
+ }
+
+ @Test
+ public void createEcompCredentialsPasswordIsInvalid() throws Exception {
+ char invalidChars[] = { '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '+', '=', '<', '>', '?', '/',
+ '"', ':', '}', ']', '[', '{', '|', '\\', ' ', '\t', '\n' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerPassword(
+ "ABC" + invalidChars[i] + "ABCabc1234567890POImnb12345678901234567890POIUzxcvbNMASDFGhj");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_CONTENT_PARAM, "Consumer password");
+ }
+ }
+
+ @Test
+ public void createEcompCredentialsSaltNotAllowedCharacters() throws Exception { // Salt
+ // must
+ // be
+ // 32
+ // chars
+ char invalidChars[] = { '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '+', '=', '<', '>', '?', '/',
+ '"', ':', '}', ']', '[', '{', '|', '\\', ' ', '\t', '\n' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerSalt(invalidChars[i] + "1234567890123456789012345678901");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_INVALID_CONTENT,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.createEcompConsumerAuditFailure(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.INVALID_CONTENT_PARAM, "Consumer salt");
+ }
+ }
+
+ @Test
+ public void createEcompCredentialsPasswordEncoded() throws Exception {
+ consumerDataDefinition.setConsumerPassword("0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ // Validate actual consumerData to returned from response
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(ADD_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCSESS_CREATED);
+ }
+
+ //
+
+ @Test
+ public void deleteEcompUserAlreayDeleted() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete ECOMP consumer
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS_DELETE_GET,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Try to delete ECOMP consumer already deleted
+ DbUtils.deleteFromEsDbByPattern("_all");
+ deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ }
+
+ @Test
+ public void deleteEcompUserByTester() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncTesterUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncTesterUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Verify that consumer is not deleted
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void deleteEcompUserByOps() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncOpsUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncOpsUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Verify that consumer is not deleted
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void deleteEcompUserByGovernor() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncGovernorUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncGovernorUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Verify that consumer is not deleted
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void deleteEcompUserByDesigner() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncDesignerUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncDesignerUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Verify that consumer is not deleted
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void deleteEcompUserByNoExistingIUser() throws Exception {
+ User noSdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ noSdncUserDetails.setRole("blabla");
+ noSdncUserDetails.setUserId("bt750h");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition, noSdncUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_ACCESS,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_ACCESS.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(ADD_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ expectedEcomConsumerAuditJavaObject.setModifier("(" + noSdncUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ DELETE_ECOMP_USER_CREDENTIALS);
+ // Verify that consumer is not deleted
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void deleteEcompCredentialsUserDoesNotExist() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+
+ }
+
+ @Test
+ public void deleteEcompCredentialsUserNameIsNull() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerName(null);
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ }
+
+ @Test
+ public void deleteEcompCredentialsUserNameMaxLength() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerName(
+ "_BCD-.abcdqwertyuiopasdfghjklzxcvbnmqw1234567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890pf34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfgghjklqwertyuiopzaiutrewasdfghjklqwertyuiopzasxcdferf123456.--"); // SVC4528
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ }
+
+ @Test
+ public void deleteEcompCredentialsUserNameExceedMaxLength() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerName(
+ "_XXXBCD-.abcdqwertyuiopasdfghjklzxcvbnmqw1234567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890pf34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfgghjklqwertyuiopzaiutrewasdfghjklqwertyuiopzasxcdferf123456.--"); // SVC4528
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ }
+
+ @Test
+ public void deleteEcompCredentialsHttpCspUserIdHeaderIsMissing() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse createConsumerRest = ConsumerRestUtils.deleteConsumerHttpCspAtuUidIsMissing(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_MISSING_INFORMATION,
+ createConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(DELETE_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc());
+ expectedEcomConsumerAuditJavaObject.setModifier("");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ DELETE_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void deleteEcompCredentialsNameIsUpperCase() throws Exception {
+ consumerDataDefinition.setConsumerName("benny");
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerName("BENNY");
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ deleteConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(DELETE_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ // Get Consumer to verify that consumer user was not deleted
+ consumerDataDefinition.setConsumerName("benny");
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ }
+
+ @Test
+ public void getEcompCredentialsMethodGet() throws Exception {
+ // Create Consumer
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ // Validate actual consumerData to returned from response
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCCESS_DELETE_GET);
+ // Delete consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void getEcompUserAlreayDeleted() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Delete ECOMP consumer
+ RestResponse deleteConsumerRest = ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS_DELETE_GET,
+ deleteConsumerRest.getErrorCode().intValue());
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // Try to get ECOMP consumer already deleted
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ }
+
+ @Test
+ public void getEcompUserByTester() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer by Tester user
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncTesterUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncTesterUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Get Consumer by Admin
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void getEcompUserByOps() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer by Ops user
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncOpsUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncOpsUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Get Consumer by Admin
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void getEcompUserByGovernor() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer by Ops user
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncGovernorUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncGovernorUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Get Consumer by Admin
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void getEcompUserByDesigner() throws Exception {
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // Get Consumer by Designer user
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncDesignerUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_OPERATION,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncDesignerUserDetails, ActionStatus.RESTRICTED_OPERATION);
+ // Get Consumer by Admin
+ getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+
+ @Test
+ public void getEcompUserByNoExistingIUser() throws Exception {
+ User noSdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ noSdncUserDetails.setRole("blabla");
+ noSdncUserDetails.setUserId("bt750h");
+ // Get Consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, noSdncUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_RESTRICTED_ACCESS,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_ACCESS.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(GET_ECOMP_USER_CREDENTIALS);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc(""));
+ expectedEcomConsumerAuditJavaObject.setModifier("(" + noSdncUserDetails.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject,
+ GET_ECOMP_USER_CREDENTIALS);
+ }
+
+ @Test
+ public void getEcompCredentialsUserDoesNotExist() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+
+ }
+
+ @Test
+ public void getEcompCredentialsUserNameIsNull() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ consumerDataDefinition.setConsumerName(null);
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_NOT_FOUND,
+ getConsumerRest.getErrorCode().intValue());
+ // Audit validation
+ AuditValidationUtils.deleteEcompConsumerAuditFailure(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, ActionStatus.ECOMP_USER_NOT_FOUND, consumerDataDefinition.getConsumerName());
+ }
+
+ @Test
+ public void getEcompCredentialsUserNameMaxLength() throws Exception {
+ consumerDataDefinition.setConsumerName(
+ "_ABCD-.abcdqwertyuiopasdfghjklzxcvbnmqw1234567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfg34567890pf34567890poiutrewasdfghjklqwertyuiopzaiutrewasdfgghjklqwertyuiopzaiutrewasdfghjklqwertyuiopzasxcdferf123456.-"); // SVC4528
+ RestResponse createConsumerRest = ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ sdncAdminUserDetails);
+ assertEquals("Check response code after create Consumer", STATUS_CODE_SUCSESS_CREATED,
+ createConsumerRest.getErrorCode().intValue());
+ // parse updated response to javaObject
+ ConsumerDataDefinition getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(createConsumerRest);
+ // Validate actual consumerData to returned from response
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Get Consumer
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse getConsumerRest = ConsumerRestUtils.getConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ assertEquals("Check response code after get Consumer", STATUS_CODE_SUCCESS,
+ getConsumerRest.getErrorCode().intValue());
+ getConsumerDataObject = ConsumerRestUtils.parseComsumerResp(getConsumerRest);
+ ConsumerRestUtils.validateConsumerReqVsResp(consumerDataDefinition, getConsumerDataObject);
+ // Audit validation
+ AuditValidationUtils.ecompConsumerAuditSuccess(GET_ECOMP_USER_CREDENTIALS, consumerDataDefinition,
+ sdncAdminUserDetails, STATUS_CODE_SUCCESS_DELETE_GET);
+ // Delete consumer
+ ConsumerRestUtils.deleteConsumer(consumerDataDefinition, sdncAdminUserDetails);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/UuidTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/UuidTest.java
new file mode 100644
index 0000000000..acb7e15c6c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/general/UuidTest.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.general;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+public class UuidTest extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public UuidTest() {
+ super(name, UuidTest.class.getName());
+ config = Config.instance();
+ }
+
+ @Test
+ public void testE2EUuidHeaderReturnedAndPreserved() throws IOException {
+ UUID randomUUID = UUID.randomUUID();
+ String uuidStr = randomUUID.toString();
+ RestResponse allTagsTowardsCatalogFe = CatalogRestUtils.getAllCategoriesTowardsCatalogFeWithUuid(uuidStr);
+ AssertJUnit.assertEquals(allTagsTowardsCatalogFe.getErrorCode(), new Integer(200));
+ List<String> list = allTagsTowardsCatalogFe.getHeaderFields().get(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ // check that header is returned
+ AssertJUnit.assertTrue(list != null && !list.isEmpty());
+ String receivedUuid = list.get(0);
+ // Check that same uuid returned
+ AssertJUnit.assertEquals(uuidStr, receivedUuid);
+ }
+
+ @Test
+ public void testUuidHeaderGeneratedBe() throws IOException {
+ RestResponse allTagsTowardsCatalogBe = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+ List<String> list = allTagsTowardsCatalogBe.getHeaderFields().get(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ // check that response was OK
+ assertEquals(allTagsTowardsCatalogBe.getErrorCode(), new Integer(200));
+ // check that header is returned
+ assertTrue(list != null && !list.isEmpty());
+ String uuid = list.get(0);
+ // Check there is no conversion error
+ UUID.fromString(uuid);
+ }
+
+ @Test
+ public void testE2EOptionsNoUuid() throws IOException {
+ RestResponse allTagsTowardsCatalogFe = ResourceRestUtils.sendOptionsTowardsCatalogFeWithUuid();
+ assertEquals(allTagsTowardsCatalogFe.getErrorCode(), new Integer(200));
+ List<String> list = allTagsTowardsCatalogFe.getHeaderFields().get(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ // check that header is returned (generated by BE)
+ assertTrue(list != null && !list.isEmpty());
+ String receivedUuid = list.get(0);
+ // Check there is no conversion error
+ UUID.fromString(receivedUuid);
+ }
+
+ @Test
+ public void testE2EMethodNotAllowedWithUuid() throws IOException {
+ UUID randomUUID = UUID.randomUUID();
+ String uuidStr = randomUUID.toString();
+ RestResponse allTagsTowardsCatalogFe = ResourceRestUtils
+ .putAllCategoriesTowardsCatalogFeWithUuidNotAllowed(uuidStr);
+ assertEquals(allTagsTowardsCatalogFe.getErrorCode(), new Integer(405));
+ List<String> list = allTagsTowardsCatalogFe.getHeaderFields().get(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ // check that header is returned (generated by BE)
+ assertTrue(list != null && !list.isEmpty());
+ String receivedUuid = list.get(0);
+ // Check that same uuid returned
+ assertEquals(uuidStr, receivedUuid);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/CsarUtilsTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/CsarUtilsTest.java
new file mode 100644
index 0000000000..7aae0e9a4e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/CsarUtilsTest.java
@@ -0,0 +1,430 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+import org.testng.annotations.Test;
+import org.yaml.snakeyaml.Yaml;
+
+public class CsarUtilsTest extends ComponentBaseTest {
+
+ public static final String ASSET_TOSCA_TEMPLATE = "assettoscatemplate";
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CsarUtilsTest() {
+ super(name, CsarUtilsTest.class.getName());
+ }
+
+ @Test(enabled = true)
+ public void createServiceCsarBasicTest() throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ Resource resourceVF = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.VENDOR_LICENSE, resourceVF, UserRoleEnum.DESIGNER, true, true);
+ resourceVF = (Resource) AtomicOperationUtils.changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ byte[] downloadCSAR = downloadCSAR(sdncModifierDetails, service);
+
+ csarBasicValidation(service, downloadCSAR);
+ }
+
+ @Test(enabled = true)
+ public void createResourceCsarBasicTest() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ resourceVF = (Resource) AtomicOperationUtils.changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ byte[] downloadCSAR = downloadCSAR(sdncModifierDetails, resourceVF);
+
+ csarBasicValidation(resourceVF, downloadCSAR);
+ }
+
+ @Test(enabled = true)
+ public void createServiceCsarInclDeploymentArtTest() throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ Resource resourceVF1 = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resourceVF2 = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+
+ resourceVF1 = (Resource) AtomicOperationUtils.changeComponentState(resourceVF1, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ resourceVF2 = (Resource) AtomicOperationUtils.changeComponentState(resourceVF2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF1, service, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF2, service, UserRoleEnum.DESIGNER, true);
+
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.YANG_XML, service, UserRoleEnum.DESIGNER, true, true);
+
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ byte[] downloadCSAR = downloadCSAR(sdncModifierDetails, service);
+
+ csarBasicValidation(service, downloadCSAR);
+
+ validateServiceCsar(resourceVF1, resourceVF2, service, downloadCSAR, 3, 5, 1);
+ }
+
+ @Test(enabled = true)
+ public void createResourceCsarInclDeploymentArtTest() throws Exception {
+
+ Resource resourceVF1 = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.YANG_XML, resourceVF1, UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_ARTIFACT, resourceVF1, UserRoleEnum.DESIGNER, true, true);
+ resourceVF1 = (Resource) AtomicOperationUtils.changeComponentState(resourceVF1, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ byte[] downloadCSAR = downloadCSAR(sdncModifierDetails, resourceVF1);
+
+ csarBasicValidation(resourceVF1, downloadCSAR);
+ //TODO when feature is integrated to OS should modify expected deployment artifacts in csar Artifacts folder to (0,1,1)
+ validateVFCsar(resourceVF1, downloadCSAR, 1, 0, 0, 0);
+ }
+
+ private void csarBasicValidation(Component mainComponent, byte[] downloadCSAR) {
+ try (ByteArrayInputStream ins = new ByteArrayInputStream(downloadCSAR); ZipInputStream zip = new ZipInputStream(ins);) {
+
+ String resourceYaml = null;
+ byte[] buffer = new byte[1024];
+ ZipEntry nextEntry = zip.getNextEntry();
+ StringBuffer sb = new StringBuffer();
+ int len;
+
+ while ((len = zip.read(buffer)) > 0) {
+ sb.append(new String(buffer, 0, len));
+ }
+
+ assertTrue(nextEntry.getName().equals("TOSCA-Metadata/TOSCA.meta"));
+
+ sb.setLength(0);
+ nextEntry = zip.getNextEntry();
+
+ while ((len = zip.read(buffer)) > 0) {
+ sb.append(new String(buffer, 0, len));
+ }
+
+ resourceYaml = sb.toString();
+
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+ ArtifactDefinition artifactDefinition = mainComponent.getToscaArtifacts().get(ASSET_TOSCA_TEMPLATE);
+ String fileName = artifactDefinition.getArtifactName();
+ assertEquals("Tosca-Template file name: ", "Definitions/" + fileName, nextEntry.getName());
+ assertTrue("Tosca template Yaml validation: ", yamlToObjectConverter.isValidYaml(resourceYaml.getBytes()));
+
+ ins.close();
+ zip.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void validateServiceCsar(Component certifiedVFC1, Component certifiedVFC2, Service fetchedService, byte[] resultByte, int toscaEntryIndexToPass, int generatorEntryIndexToPass, int deploymentArtifactIndexToPass) {
+
+ // TODO Test to validate everything is right (comment out after testing)
+ /*
+ * try { FileUtils.writeByteArrayToFile(new File("c:/TestCSAR/" + fetchedService.getName() + ".zip"), resultByte); } catch (IOException e) { // Auto-generated catch block e.printStackTrace(); }
+ */
+
+ try (ByteArrayInputStream ins = new ByteArrayInputStream(resultByte); ZipInputStream zip = new ZipInputStream(ins);) {
+
+ String resourceYaml = null;
+ byte[] buffer = new byte[1024];
+ ZipEntry nextEntry = zip.getNextEntry();
+ StringBuffer sb = new StringBuffer();
+ int len;
+
+ while ((len = zip.read(buffer)) > 0) {
+ sb.append(new String(buffer, 0, len));
+ }
+
+ assertTrue(nextEntry.getName().equals("TOSCA-Metadata/TOSCA.meta"));
+
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+
+ int toscaEntryIndex = 0;
+ int generatorEntryIndex = 0;
+ int deploymentArtifactIndex = 0;
+ String fileName = null;
+ ArtifactDefinition artifactDefinition;
+ Component componentToValidate = null;
+
+ artifactDefinition = fetchedService.getToscaArtifacts().get(ASSET_TOSCA_TEMPLATE);
+ String serviceFileName = artifactDefinition.getArtifactName();
+ artifactDefinition = certifiedVFC1.getToscaArtifacts().get(ASSET_TOSCA_TEMPLATE);
+ String vfc1FileName = artifactDefinition.getArtifactName();
+ artifactDefinition = certifiedVFC2.getToscaArtifacts().get(ASSET_TOSCA_TEMPLATE);
+ String vfc2FileName = artifactDefinition.getArtifactName();
+
+ while ((nextEntry = zip.getNextEntry()) != null) {
+ sb.setLength(0);
+
+ while ((len = zip.read(buffer)) > 0) {
+ sb.append(new String(buffer, 0, len));
+ }
+
+ String entryName = nextEntry.getName();
+
+ resourceYaml = sb.toString();
+ if (entryName.contains(serviceFileName)) {
+ componentToValidate = fetchedService;
+ fileName = "Definitions/" + serviceFileName;
+
+ assertEquals("Validate entry Name", (fileName), nextEntry.getName());
+ assertTrue(yamlToObjectConverter.isValidYaml(resourceYaml.getBytes()));
+ validateContent(resourceYaml, componentToValidate);
+ ++toscaEntryIndex;
+ continue;
+ }
+
+ if (entryName.contains(vfc1FileName)) {
+ componentToValidate = certifiedVFC1;
+ fileName = "Definitions/" + vfc1FileName;
+
+ assertEquals("Validate entry Name", (fileName), nextEntry.getName());
+ assertTrue(yamlToObjectConverter.isValidYaml(resourceYaml.getBytes()));
+ validateContent(resourceYaml, componentToValidate);
+ ++toscaEntryIndex;
+ continue;
+ }
+ if (entryName.contains(vfc2FileName)) {
+ componentToValidate = certifiedVFC2;
+ fileName = "Definitions/" + vfc2FileName;
+
+ assertEquals("Validate entry Name", (fileName), nextEntry.getName());
+ assertTrue(yamlToObjectConverter.isValidYaml(resourceYaml.getBytes()));
+ validateContent(resourceYaml, componentToValidate);
+ ++toscaEntryIndex;
+ continue;
+ }
+
+ if (entryName.contains(".xml") && !entryName.startsWith("Artifacts/AAI")) {
+ ++deploymentArtifactIndex;
+ continue;
+ }
+
+ if (entryName.startsWith("Artifacts/AAI")) {
+ ++generatorEntryIndex;
+ continue;
+ }
+
+ assertTrue("Unexpected entry: " + entryName, true);
+ }
+ assertEquals("Validate amount of entries", toscaEntryIndexToPass, toscaEntryIndex);
+ assertEquals("Validate amount of generated AAI artifacts", generatorEntryIndexToPass, generatorEntryIndex);
+ assertEquals("Validate amount of generated Deployment artifacts", deploymentArtifactIndexToPass, deploymentArtifactIndex);
+
+ ins.close();
+ zip.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void validateVFCsar(Component certifiedVF, byte[] resultByte, int toscaEntryIndexToPass, int ymlDeploymentArtifactIndexToPass, int xmlDeploymentArtifactIndexToPass, int heatEnvDeploymentArtifactIndexToPass) {
+
+ // TODO Test to validate everything is right (comment out after testing)
+ /*
+ * try { FileUtils.writeByteArrayToFile(new File("c:/TestCSAR/" + fetchedService.getName() + ".zip"), resultByte); } catch (IOException e) { // Auto-generated catch block e.printStackTrace(); }
+ */
+
+ try (ByteArrayInputStream ins = new ByteArrayInputStream(resultByte); ZipInputStream zip = new ZipInputStream(ins);) {
+
+ String resourceYaml = null;
+ byte[] buffer = new byte[1024];
+ ZipEntry nextEntry = zip.getNextEntry();
+ StringBuffer sb = new StringBuffer();
+ int len;
+
+ while ((len = zip.read(buffer)) > 0) {
+ sb.append(new String(buffer, 0, len));
+ }
+
+ assertTrue(nextEntry.getName().equals("TOSCA-Metadata/TOSCA.meta"));
+
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+
+ int toscaEntryIndex = 0;
+ int ymlEntryIndex = 0;
+ int xmlArtifactsIndex = 0;
+ int heatEnvDeploymentArtifactIndex = 0;
+ String fileName = null;
+ ArtifactDefinition artifactDefinition;
+ Component componentToValidate = null;
+
+ artifactDefinition = certifiedVF.getToscaArtifacts().get(ASSET_TOSCA_TEMPLATE);
+ String vfFileName = artifactDefinition.getArtifactName();
+
+ while ((nextEntry = zip.getNextEntry()) != null) {
+ sb.setLength(0);
+
+ while ((len = zip.read(buffer)) > 0) {
+ sb.append(new String(buffer, 0, len));
+ }
+
+ String entryName = nextEntry.getName();
+
+ resourceYaml = sb.toString();
+ if (entryName.contains(vfFileName)) {
+ componentToValidate = certifiedVF;
+ fileName = "Definitions/" + vfFileName;
+
+ assertEquals("Validate entry Name", (fileName), nextEntry.getName());
+ assertTrue(yamlToObjectConverter.isValidYaml(resourceYaml.getBytes()));
+ validateContent(resourceYaml, componentToValidate);
+ ++toscaEntryIndex;
+ continue;
+ }
+
+ if (entryName.contains(".xml") && entryName.startsWith("Artifacts/")) {
+ ++xmlArtifactsIndex;
+ continue;
+ }
+
+ if (entryName.contains(".sh") && entryName.startsWith("Artifacts/")) {
+ ++heatEnvDeploymentArtifactIndex;
+ continue;
+ }
+
+ if (entryName.contains(".yml") && entryName.startsWith("Artifacts/")) {
+ ++ymlEntryIndex;
+ continue;
+ }
+
+ assertTrue("Unexpected entry: " + entryName, false);
+ }
+ assertEquals("Validate amount of entries", toscaEntryIndexToPass, toscaEntryIndex);
+ assertEquals("Validate amount of YAML artifacts", ymlDeploymentArtifactIndexToPass, ymlEntryIndex);
+ assertEquals("Validate amount of generated XML artifacts", xmlDeploymentArtifactIndexToPass, xmlArtifactsIndex);
+ assertEquals("Validate amount of generated HEAT ENV artifacts", heatEnvDeploymentArtifactIndexToPass, heatEnvDeploymentArtifactIndex);
+
+ ins.close();
+ zip.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void validateContent(String content, Component component) {
+ Yaml yaml = new Yaml();
+
+ InputStream inputStream = new ByteArrayInputStream(content.getBytes());
+ @SuppressWarnings("unchecked")
+ Map<String, Object> load = (Map<String, Object>) yaml.load(inputStream);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> metadata = (Map<String, Object>) load.get("metadata");
+ assertNotNull(metadata);
+
+ String name = (String) metadata.get("name");
+ assertNotNull(name);
+ assertEquals("Validate component name", component.getName(), name);
+
+ String invariantUUID = (String) metadata.get("invariantUUID");
+ assertNotNull(invariantUUID);
+ assertEquals("Validate component invariantUUID", component.getInvariantUUID(), invariantUUID);
+
+ String UUID = (String) metadata.get("UUID");
+ assertNotNull(UUID);
+ assertEquals("Validate component invariantUUID", component.getUUID(), UUID);
+
+ String type = (String) metadata.get("type");
+ assertNotNull(type);
+ if (component.getComponentType().equals(ComponentTypeEnum.SERVICE)) {
+ assertEquals("Validate component type", component.getComponentType().getValue(), type);
+ } else {
+ assertEquals("Validate component type", ((Resource) component).getResourceType(), ResourceTypeEnum.valueOf(type));
+ }
+ }
+
+ private byte[] downloadCSAR(User sdncModifierDetails, Component createdComponent) throws Exception {
+
+ String artifactUniqeId = createdComponent.getToscaArtifacts().get("assettoscacsar").getUniqueId();
+ RestResponse getCsarResponse = null;
+
+ switch (createdComponent.getComponentType()) {
+ case RESOURCE:
+ getCsarResponse = ArtifactRestUtils.downloadResourceArtifactInternalApi(createdComponent.getUniqueId(), sdncModifierDetails, artifactUniqeId);
+ break;
+ case SERVICE:
+ getCsarResponse = ArtifactRestUtils.downloadServiceArtifactInternalApi(createdComponent.getUniqueId(), sdncModifierDetails, artifactUniqeId);
+ break;
+ default:
+ break;
+ }
+
+ assertNotNull(getCsarResponse);
+ BaseRestUtils.checkSuccess(getCsarResponse);
+
+ ArtifactUiDownloadData artifactUiDownloadData = ResponseParser.parseToObject(getCsarResponse.getResponse(), ArtifactUiDownloadData.class);
+
+ assertNotNull(artifactUiDownloadData);
+
+ byte[] fromUiDownload = artifactUiDownloadData.getBase64Contents().getBytes();
+ byte[] decodeBase64 = Base64.decodeBase64(fromUiDownload);
+
+ return decodeBase64;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ExportToscaTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ExportToscaTest.java
new file mode 100644
index 0000000000..e4360329ad
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ExportToscaTest.java
@@ -0,0 +1,333 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.annotations.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonParser;
+
+public class ExportToscaTest extends ComponentBaseTest {
+ @Rule
+ public static TestName name = new TestName();
+
+ public ExportToscaTest() {
+ super(name, ExportToscaTest.class.getName());
+ }
+
+ @Test(enabled = true)
+ public void exportVfModuleTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ Resource createdResource = createVfFromCSAR(sdncModifierDetails, "VSPPackage");
+
+ Map<String, Object> load = downloadAndParseToscaTemplate(sdncModifierDetails, createdResource);
+ assertNotNull(load);
+ Map<String, Object> topology_template = (Map<String, Object>) load.get("topology_template");
+ assertNotNull(topology_template);
+ Map<String, Object> groups = (Map<String, Object>) topology_template.get("groups");
+ assertNotNull(groups);
+ List<GroupDefinition> groupsOrigin = createdResource.getGroups();
+
+ assertEquals("Validate groups size", groupsOrigin.size(), groups.size());
+ for (GroupDefinition group : groupsOrigin) {
+ Map<String, Object> groupTosca = (Map<String, Object>) groups.get(group.getName());
+ assertNotNull(groupTosca);
+
+ Map<String, Object> metadata = (Map<String, Object>) groupTosca.get("metadata");
+ assertNotNull(metadata);
+
+ String invariantUUID;
+ String name;
+ String UUID;
+ String version;
+ Map<String, Object> properties = (Map<String, Object>) groupTosca.get("properties");
+
+ if (group.getType().equals(Constants.DEFAULT_GROUP_VF_MODULE)) {
+ invariantUUID = (String) metadata.get("vfModuleModelInvariantUUID");
+ name = (String) metadata.get("vfModuleModelName");
+ UUID = (String) metadata.get("vfModuleModelUUID");
+ version = (String) metadata.get("vfModuleModelVersion");
+ assertNotNull(properties);
+
+ String vf_module_type = (String) properties.get("vf_module_type");
+ List<GroupProperty> props = group.getProperties();
+ for (GroupProperty prop : props) {
+ if (prop.getName().equals(Constants.IS_BASE)) {
+ String value = prop.getValue() == null ? prop.getDefaultValue() : prop.getValue();
+ boolean bvalue = Boolean.parseBoolean(value);
+ if (bvalue) {
+ assertEquals("Validate vf_module_type", "Base", vf_module_type);
+ } else {
+ assertEquals("Validate vf_module_type", "Expansion", vf_module_type);
+ }
+ break;
+ }
+ }
+ String vf_module_description = (String) properties.get("vf_module_description");
+ assertEquals("Validate vf_module_description", group.getDescription(), vf_module_description);
+
+ Boolean volume_group = (Boolean) properties.get("volume_group");
+ boolean isVolume = false;
+ List<String> artifactsList = group.getArtifacts();
+ List<ArtifactDefinition> artifacts = new ArrayList<>();
+ if (artifactsList != null && !artifactsList.isEmpty()) {
+ ArtifactDefinition masterArtifact = findMasterArtifact(createdResource.getDeploymentArtifacts(),
+ artifacts, artifactsList);
+ if (masterArtifact.getArtifactType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType())) {
+ isVolume = true;
+ }
+ }
+ assertEquals("Validate volume_group", isVolume, volume_group);
+
+ } else {
+ invariantUUID = (String) metadata.get("invariantUUID");
+ name = (String) metadata.get("name");
+ UUID = (String) metadata.get("UUID");
+ version = (String) metadata.get("version");
+ assertNull(properties);
+
+ }
+ assertEquals("Validate InvariantUUID", group.getInvariantUUID(), invariantUUID);
+ assertEquals("Validate name", group.getName(), name);
+ assertEquals("Validate UUID", group.getGroupUUID(), UUID);
+ assertEquals("Validate version", group.getVersion(), version);
+
+ }
+ }
+
+ @Test(enabled = true)
+ public void exportCsarInputsTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ Resource createdResource = createVfFromCSAR(sdncModifierDetails, "csar_1");
+ Map<String, Object> load = downloadAndParseToscaTemplate(sdncModifierDetails, createdResource);
+ assertNotNull(load);
+
+ Map<String, Object> topology_template = (Map<String, Object>) load.get("topology_template");
+ assertNotNull(topology_template);
+
+ Map<String, Object> inputs = (Map<String, Object>) topology_template.get("inputs");
+ assertNotNull(inputs);
+
+ List<InputDefinition> inputsFromResource = createdResource.getInputs();
+ assertEquals("validate inputs size", inputsFromResource.size(), inputs.size());
+ for (InputDefinition inputDef : inputsFromResource) {
+ Map<String, Object> inputInFile = (Map<String, Object>) inputs.get(inputDef.getName());
+ assertNotNull(inputInFile);
+ validateInput(inputDef, inputInFile);
+ }
+ List<ComponentInstance> componentInstances = createdResource.getComponentInstances();
+ Map<String, List<ComponentInstanceProperty>> componentInstancesProperties = createdResource
+ .getComponentInstancesProperties();
+ Map<String, Object> node_templates = (Map<String, Object>) topology_template.get("node_templates");
+ assertNotNull(node_templates);
+
+ JsonParser jsonParser = new JsonParser();
+
+ for (Map.Entry<String, List<ComponentInstanceProperty>> entry : componentInstancesProperties.entrySet()) {
+
+ Optional<ComponentInstance> findFirst = componentInstances.stream()
+ .filter(ci -> ci.getUniqueId().equals(entry.getKey())).findFirst();
+ assertTrue(findFirst.isPresent());
+ String resourceName = findFirst.get().getName();
+ Map<String, Object> instance = (Map<String, Object>) node_templates.get(resourceName);
+ assertNotNull(instance);
+ Map<String, Object> properties = (Map<String, Object>) instance.get("properties");
+
+ for (ComponentInstanceProperty cip : entry.getValue()) {
+ if (cip.getValueUniqueUid() != null && !cip.getValueUniqueUid().isEmpty()) {
+ assertNotNull(properties);
+ if (cip.getValue().contains("get_input")) {
+ Object prop = properties.get(cip.getName());
+ assertNotNull(prop);
+
+ Gson gson = new Gson();
+ String json = gson.toJson(prop);
+ assertEquals("validate json property", cip.getValue(), json);
+ }
+
+ }
+ }
+
+ }
+
+ }
+
+ @Test
+ public void importExportCsarWithJsonPropertyType() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "jsonPropertyTypeTest.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/jsonPropertyTypeTest.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ resourceDetails.setCsarUUID(payloadName);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ ComponentInstance pmaaServer = resource.getComponentInstances().stream()
+ .filter(p -> p.getName().equals("pmaa_server_0")).findAny().get();
+ ComponentInstanceProperty jsonProp = resource.getComponentInstancesProperties().get(pmaaServer.getUniqueId())
+ .stream().filter(p -> p.getType().equals(ToscaPropertyType.JSON.getType())).findAny().get();
+ String jsonValue = "{\"pmaa.sb_nic\":{\"address\":{\"get_input\":\"pmaa_dpu_fixed_ip\"},\"cidr\":{\"get_input\":\"pmaa_dpu_cidr\"},\"gateway\":{\"get_input\":\"pmaa_dpu_gateway\"}}}";
+ assertEquals(jsonProp.getValue(), jsonValue);
+ // download and compare
+ Map<String, Object> load = downloadAndParseToscaTemplate(sdncModifierDetails, resource);
+ assertNotNull(load);
+ Map<String, Object> topology_template = (Map<String, Object>) load.get("topology_template");
+ assertNotNull(topology_template);
+ Map<String, Object> nodes = (Map<String, Object>) topology_template.get("node_templates");
+ assertNotNull(nodes);
+ Map<String, Object> pmaaServerObj = (Map<String, Object>) nodes.get("pmaa_server_0");
+ assertNotNull(pmaaServerObj);
+ Map<String, Object> props = (Map<String, Object>) pmaaServerObj.get("properties");
+ assertNotNull(props);
+ Map<String, Object> jsonPropObj = (Map<String, Object>) props.get("metadata");
+ assertNotNull(jsonPropObj);
+ Gson gson = new Gson();
+ String json = gson.toJson(jsonPropObj);
+ assertEquals(json, jsonValue);
+ }
+
+ private void validateInput(InputDefinition inputDef, Map<String, Object> inputInFile) {
+ assertEquals("validate input type", inputDef.getType(), (String) inputInFile.get("type"));
+
+ if (inputDef.getDefaultValue() == null) {
+ assertNull(inputInFile.get("default"));
+ } else {
+ assertNotNull(inputInFile.get("default"));
+ String value = inputDef.getDefaultValue().replace("\"", "");
+ value = value.replace(" ", "");
+ String expValue = inputInFile.get("default").toString().replace(" ", "");
+ assertEquals("validate input default", value, expValue);
+ }
+ assertEquals("validate input description", inputDef.getDescription(), (String) inputInFile.get("description"));
+ }
+
+ // ----------------------------------------
+ private Map<String, Object> downloadAndParseToscaTemplate(User sdncModifierDetails, Resource createdResource)
+ throws Exception {
+ String artifactUniqeId = createdResource.getToscaArtifacts().get("assettoscatemplate").getUniqueId();
+
+ RestResponse toscaTemplate = ArtifactRestUtils.downloadResourceArtifactInternalApi(
+ createdResource.getUniqueId(), sdncModifierDetails, artifactUniqeId);
+ ArtifactUiDownloadData artifactUiDownloadData = ResponseParser.parseToObject(toscaTemplate.getResponse(),
+ ArtifactUiDownloadData.class);
+ byte[] fromUiDownload = artifactUiDownloadData.getBase64Contents().getBytes();
+ byte[] decodeBase64 = Base64.decodeBase64(fromUiDownload);
+ Yaml yaml = new Yaml();
+
+ InputStream inputStream = new ByteArrayInputStream(decodeBase64);
+
+ Map<String, Object> load = (Map<String, Object>) yaml.load(inputStream);
+ return load;
+ }
+
+ private Resource createVfFromCSAR(User sdncModifierDetails, String csarId) throws Exception {
+ // create new resource from Csar
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+
+ resourceDetails.setCsarUUID(csarId);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource createdResource = ResponseParser.convertResourceResponseToJavaObject(createResource.getResponse());
+ return createdResource;
+ }
+
+ public ArtifactDefinition findMasterArtifact(Map<String, ArtifactDefinition> deplymentArtifact,
+ List<ArtifactDefinition> artifacts, List<String> artifactsList) {
+ for (String artifactUid : artifactsList) {
+ for (Entry<String, ArtifactDefinition> entry : deplymentArtifact.entrySet()) {
+ ArtifactDefinition artifact = entry.getValue();
+ if (artifactUid.equalsIgnoreCase(artifact.getUniqueId())) {
+ artifacts.add(artifact);
+ }
+
+ }
+ }
+ ArtifactDefinition masterArtifact = null;
+ for (ArtifactDefinition artifactInfo : artifacts) {
+ String atrifactType = artifactInfo.getArtifactType();
+ if (atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType())
+ || atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT_NET.getType())) {
+ masterArtifact = artifactInfo;
+ continue;
+ }
+ if (atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType())) {
+ masterArtifact = artifactInfo;
+ break;
+ }
+ }
+ return masterArtifact;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportCsarResourceTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportCsarResourceTest.java
new file mode 100644
index 0000000000..eab4e5bed2
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportCsarResourceTest.java
@@ -0,0 +1,1538 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.WordUtils;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.GroupRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ImportRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class ImportCsarResourceTest extends ComponentBaseTest {
+ private static Logger log = LoggerFactory.getLogger(ImportCsarResourceTest.class.getName());
+ @Rule
+ public static TestName name = new TestName();
+
+ Gson gson = new Gson();
+
+ public ImportCsarResourceTest() {
+ super(name, ImportCsarResourceTest.class.getName());
+ }
+
+ private String buildAssertMessage(String expectedString, String actualString) {
+ return String.format("expected is : %s , actual is: %s", expectedString, actualString);
+ }
+
+ /**
+ *
+ * User Story : US640615 [BE] - Extend create VF API with Import TOSCA CSAR
+ */
+
+ @Test(enabled = true)
+ public void createResourceFromCsarHappy() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("AF7F231969C5463F9C968570070E8877");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ String expectedCsarUUID = resourceDetails.getCsarUUID();
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, resource.getCsarUUID()), expectedCsarUUID.equals(resource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, getResource.getCsarUUID()), expectedCsarUUID.equals(getResource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void emptyStringInCsarUUIDFieldTest() throws Exception {
+ String emptyString = "";
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(emptyString);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(null, resource.getComponentInstances());
+
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(emptyString, resource.getCsarUUID()), resource.getCsarUUID() == emptyString);
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(emptyString, getResource.getCsarUUID()), getResource.getCsarUUID() == emptyString);
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromScratchTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(null, resource.getComponentInstances());
+
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(null, resource.getCsarUUID()), resource.getCsarUUID() == null);
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(null, getResource.getCsarUUID()), getResource.getCsarUUID() == null);
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void fileNotCsarTypeTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("valid_vf_zip");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_NOT_FOUND.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void missingToscaMetadataFolderTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("toscaFolderNotExists");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void missingToscaMetaFileTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("toscaMetaFileNotExists");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void toscaMetaFileOutsideTheFolderTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("toscaMetaOutsideTheFolder");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void caseSensitiveTest_1() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("caseSensitiveTest_1");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void caseSensitiveTest_2() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("caseSensitiveTest_2");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void missingOneLineInToscaMetaFileTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("missingOneLineInToscaMeta");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void noCSARVersionTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("noCSARVersion");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void noCreatedByValueTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("noCreatedByValue");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void noEntryDefinitionsValueTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("noEntryDefinitionsValue");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void noTOSCAMetaFileVersionValueTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("noTOSCAMetaFileVersionValue");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void invalidCsarVersionInMetaFileTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("invalidCsarVersion");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+
+ resourceDetails.setCsarUUID("invalidCsarVersion2");
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+
+ resourceDetails.setCsarUUID("invalidCsarVersion3");
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+
+ resourceDetails.setCsarUUID("invalidCsarVersion4");
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+
+ resourceDetails.setCsarUUID("invalidCsarVersion5");
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+
+ }
+
+ @Test(enabled = true)
+ public void validCsarVersionInMetaFileTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("validCsarVersion");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ String expectedCsarUUID = resourceDetails.getCsarUUID();
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, resource.getCsarUUID()), expectedCsarUUID.equals(resource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, getResource.getCsarUUID()), expectedCsarUUID.equals(getResource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void underscoreInToscaMetaFileVersionNameTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("underscoreInsteadOfDash");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void missingEntryDefintionInMetaFileTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("missingEntryDefintionPair");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = false)
+ public void noNewLineAfterBLock0Test() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("noNewLineAfterBLock0");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void moreThanOneYamlFileTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("moreThenOneYamlFile");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ String expectedCsarUUID = resourceDetails.getCsarUUID();
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, resource.getCsarUUID()), expectedCsarUUID.equals(resource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, getResource.getCsarUUID()), expectedCsarUUID.equals(getResource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void moreThanOneMetaFileTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("moreThanOneMetaFile");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ String expectedCsarUUID = resourceDetails.getCsarUUID();
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, resource.getCsarUUID()), expectedCsarUUID.equals(resource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, getResource.getCsarUUID()), expectedCsarUUID.equals(getResource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void csarNotContainsYamlAndMetaFilesTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("notContainYamlAndMetaFiles");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void csarNotContainsYamlFileTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("notContainYamlFile");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ variables.add("Definitions/tosca_mock_vf.yaml");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.YAML_NOT_FOUND_IN_CSAR.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void missingCsarFileTest() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("abc");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_NOT_FOUND.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void longNamesInToscaMetaFileTest_1() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("longNamesInToscaMetaFile1");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void longNamesInToscaMetaFileTest_2() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("longNamesInToscaMetaFile2");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void longNamesInToscaMetaFileTest_3() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("longNamesInToscaMetaFile3");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void longNamesInToscaMetaFileTest_4() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("longNamesInToscaMetaFile4");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void longNamesInToscaMetaFileTest_5() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("longNamesInToscaMetaFile5");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ // possible to have more than four lines in block 0
+ // @Test (enabled = true)
+ public void fiveLinesAsBlock0Test() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ resourceDetails.setCsarUUID("fiveLinesAsBlock0");
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ List<String> variables = new ArrayList<String>();
+ variables.add(resourceDetails.getCsarUUID());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.CSAR_INVALID_FORMAT.name(), variables, createResource.getResponse());
+ }
+
+ @Test(enabled = true)
+ public void lifecycleChangingToResourceFromCsarTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("valid_vf");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertTrue("0.1".equals(resource.getVersion()));
+ assertTrue(LifeCycleStatesEnum.CHECKOUT.getComponentState().equals(resource.getLifecycleState().toString()));
+
+ String designerUserId = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId();
+ String testerUserId = ElementFactory.getDefaultUser(UserRoleEnum.TESTER).getUserId();
+ String csarUniqueId = resourceDetails.getUniqueId();
+ assertNotNull(csarUniqueId);
+
+ RestResponse lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUserId, LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUserId, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUserId, LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUserId, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUserId, LifeCycleStatesEnum.STARTCERTIFICATION);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUserId, LifeCycleStatesEnum.CERTIFY);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUserId, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+
+ resource = ResponseParser.parseToObjectUsingMapper(lifecycleChangeResponse.getResponse(), Resource.class);
+ Map<String, String> allVersions = resource.getAllVersions();
+ assertEquals(2, allVersions.keySet().size());
+ assertEquals(2, allVersions.values().size());
+ Set<String> keySet = allVersions.keySet();
+ assertTrue(keySet.contains("1.0"));
+ assertTrue(keySet.contains("1.1"));
+ }
+
+ @Test(enabled = true)
+ public void csarWithJsonPromEnvTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("VSPPackageJsonProp.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ }
+
+ @Test(enabled = true)
+ public void uploadArtifactToResourceFromCsarTest() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("valid_vf");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ User designer = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultArtifact("firstArtifact");
+ String firstArtifactLabel = artifactDetails.getArtifactLabel();
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails, designer, resourceDetails.getUniqueId());
+ ArtifactRestUtils.checkSuccess(addInformationalArtifactToResource);
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resourceDetails.getUniqueId());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ Map<String, ArtifactDefinition> informationalArtifacts = resource.getArtifacts();
+ assertEquals(1, informationalArtifacts.keySet().size());
+ Set<String> keySet = informationalArtifacts.keySet();
+ assertTrue(keySet.contains(firstArtifactLabel.toLowerCase()));
+ Collection<ArtifactDefinition> values = informationalArtifacts.values();
+ assertEquals(1, values.size());
+ Iterator<ArtifactDefinition> iterator = values.iterator();
+ while (iterator.hasNext()) {
+ ArtifactDefinition actualArtifact = iterator.next();
+ assertTrue(firstArtifactLabel.equals(actualArtifact.getArtifactDisplayName()));
+ }
+
+ RestResponse lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designer.getUserId(), LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+ lifecycleChangeResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designer.getUserId(), LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(lifecycleChangeResponse);
+
+ ArtifactReqDetails artifactDetails2 = ElementFactory.getDefaultArtifact("secondArtifact");
+ artifactDetails2.setArtifactName("secondArtifact");
+ addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails2, designer, resourceDetails.getUniqueId());
+ ArtifactRestUtils.checkSuccess(addInformationalArtifactToResource);
+
+ getResourceResponse = ResourceRestUtils.getResource(resourceDetails.getUniqueId());
+ resource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ informationalArtifacts = resource.getArtifacts();
+ assertEquals(2, informationalArtifacts.keySet().size());
+ keySet = informationalArtifacts.keySet();
+ assertTrue(keySet.contains(firstArtifactLabel.toLowerCase()));
+ assertTrue(keySet.contains(artifactDetails2.getArtifactLabel().toLowerCase()));
+ values = informationalArtifacts.values();
+ assertEquals(2, values.size());
+ ArtifactDefinition[] actualArtifacts = values.toArray(new ArtifactDefinition[2]);
+ assertTrue(firstArtifactLabel.equals(actualArtifacts[0].getArtifactDisplayName()));
+ assertTrue(artifactDetails2.getArtifactLabel().equals(actualArtifacts[1].getArtifactDisplayName()));
+ }
+
+ /*
+ * // @Test (enabled = true) public void createUpdateImportResourceFromCsarArtifactsWereNotChangedTest() throws Exception { // User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER); // //back original scar RestResponse
+ * copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar"); BaseRestUtils.checkSuccess(copyRes);
+ *
+ * // resourceDetails.setResourceType(ResourceTypeEnum.VF.name()); // RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails); resourceDetails.setName("test5");
+ * resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar"); resourceDetails.setCsarVersion("1"); // String invariantUUID = resource.getInvariantUUID(); // // RestResponse changeResourceState =
+ * LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN); // assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ *
+ * // BaseRestUtils.checkSuccess(copyRes); // //change name (temporary) resourceDetails.setCsarVersion("2"); resourceDetails.setName("test6"); createResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails,
+ * resourceDetails.getUniqueId()); Resource updatedResource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class); Map<String, ArtifactDefinition> updatedArtifacts = updatedResource.getDeploymentArtifacts(); for
+ * (Entry<String, ArtifactDefinition> artifactEntry : resource.getDeploymentArtifacts().entrySet()) { if (updatedArtifacts.containsKey(artifactEntry.getKey())) { ArtifactDefinition currArt = updatedArtifacts.get(artifactEntry.getKey());
+ * assertEquals(currArt.getArtifactVersion(), artifactEntry.getValue().getArtifactVersion()); assertEquals(currArt.getArtifactUUID(), artifactEntry.getValue().getArtifactUUID()); assertEquals(currArt.getArtifactChecksum(),
+ * artifactEntry.getValue().getArtifactChecksum()); } } // resourceDetails = ElementFactory.getDefaultResource(); // resourceDetails.setName("test5"); // resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar"); }
+ */
+
+ @Test(enabled = true)
+ public void createImportResourceFromCsarDissotiateArtifactFromGroupTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+
+ // create new resource from Csar
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ String invariantUUID = resource.getInvariantUUID();
+
+ // add artifact from metadata (resource metadata should be updated)
+ // RestResponse addInformationalArtifactToResource =
+ // ArtifactRestUtils.addInformationalArtifactToResource(ElementFactory.getDefaultArtifact(),
+ // sdncModifierDetails, resourceDetails.getUniqueId());
+ // ArtifactRestUtils.checkSuccess(addInformationalArtifactToResource);
+ resourceDetails.setName("test4");
+ RestResponse updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(invariantUUID, resource.getInvariantUUID());
+
+ // wrong RI (without node types, resource shouldn't be updated)
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_dissociate.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ // change name (temporary)
+ resourceDetails.setName("test4");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(invariantUUID, resource.getInvariantUUID());
+
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ }
+
+ @Test(enabled = true)
+ public void createImportResourceFromCsarNewgroupTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+
+ // create new resource from Csar
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ String invariantUUID = resource.getInvariantUUID();
+
+ // update scar
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_UpdateToscaAndArtifacts.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails.setName("test2");
+ // change resource metaData (resource should be updated)
+ resourceDetails.setDescription("It is new description bla bla bla");
+ RestResponse updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ assertEquals(invariantUUID, resource.getInvariantUUID());
+
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ }
+
+ @Test(enabled = true)
+ public void createImportResourceFromCsarGetGroupTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // RestResponse copyRes =
+ // copyCsarRest(sdncModifierDetails,"VF_RI2_G4_withArtifacts_a.csar","VF_RI2_G4_withArtifacts.csar");
+
+ // create new resource from Csar
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("VSPPackage");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ String invariantUUID = resource.getInvariantUUID();
+ List<GroupDefinition> groups = resource.getGroups();
+
+ GroupDefinition groupWithArtifact = groups.stream().filter(p -> p.getArtifacts() != null && !p.getArtifacts().isEmpty()).findFirst().get();
+
+ RestResponse groupRest = GroupRestUtils.getGroupById(resource, groupWithArtifact.getUniqueId(), sdncModifierDetails);
+ BaseRestUtils.checkSuccess(groupRest);
+
+ GroupDefinition groupWithoutArtifact = groups.stream().filter(p -> p.getArtifacts() == null || p.getArtifacts().isEmpty()).findFirst().get();
+
+ groupRest = GroupRestUtils.getGroupById(resource, groupWithoutArtifact.getUniqueId(), sdncModifierDetails);
+ BaseRestUtils.checkSuccess(groupRest);
+ }
+
+ @Test(enabled = true)
+ public void createImportResourceFromCsarUITest() throws Exception {
+ RestResponse getResource = null;
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "valid_vf.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ // create new resource from Csar
+ resourceDetails.setCsarUUID(payloadName);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ RestResponse changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change composition (resource should be updated)
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf_b.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ // change name
+ resourceDetails.setName("test1");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(2, resource.getComponentInstances().size());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change name
+ resourceDetails.setName("test2");
+ // change resource metaData (resource should be updated)
+ resourceDetails.setDescription("It is new description bla bla bla");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(2, resource.getComponentInstances().size());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // wrong RI (without node types, resource shouldn't be updated)
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf_c.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ // change name
+ resourceDetails.setName("test3");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.INVALID_NODE_TEMPLATE, "Definitions/tosca_mock_vf.yaml", "nodejs", "tosca.nodes.Weber");
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(null, resource);
+ getResource = ResourceRestUtils.getResourceByNameAndVersion(sdncModifierDetails.getUserId(), "test3", resourceDetails.getVersion());
+ BaseRestUtils.checkErrorResponse(getResource, ActionStatus.RESOURCE_NOT_FOUND, "test3");
+
+ // create new resource from other Csar
+ resourceDetails = ElementFactory.getDefaultImportResource();
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ resourceDetails.setPayloadName("VF_RI2_G4_withArtifacts.csar");
+ resourceDetails.setName("test4");
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // wrong RI (with node types) resource shouldn't be created
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts_b.csar");
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_b.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ resourceDetails.setPayloadName("VF_RI2_G4_withArtifacts_b.csar");
+ resourceDetails.setName("test5");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.INVALID_NODE_TEMPLATE, "Definitions/VF_RI2_G1.yaml", "ps04_port_0", "org.openecomp.resource.cp.nodes.heat.network.neutron.Portur");
+ }
+
+ @Test(enabled = true)
+ public void createUpdateImportResourceFromCsarUITest() throws Exception {
+ RestResponse getResource = null;
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "valid_vf.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ // create new resource from Csar
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ RestResponse changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change composition and update resource
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf_b.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ resourceDetails.setUniqueId(resource.getUniqueId());
+ // change name
+ RestResponse updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resource.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(2, resource.getComponentInstances().size());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change name
+ resourceDetails.setName("test2");
+ // change resource metaData (resource should be updated)
+ resourceDetails.setDescription("It is new description bla bla bla");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resource.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(2, resource.getComponentInstances().size());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // try to update resource with wrong RI (without node types, resource
+ // shouldn't be updated)
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf_c.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ // change name
+ resourceDetails.setName("test3");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resource.getUniqueId());
+ BaseRestUtils.checkErrorResponse(updateResource, ActionStatus.INVALID_NODE_TEMPLATE, "Definitions/tosca_mock_vf.yaml", "nodejs", "tosca.nodes.Weber");
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(null, resource);
+ getResource = ResourceRestUtils.getResourceByNameAndVersion(sdncModifierDetails.getUserId(), "test3", resourceDetails.getVersion());
+ BaseRestUtils.checkErrorResponse(getResource, ActionStatus.RESOURCE_NOT_FOUND, "test3");
+ }
+
+ @Test(enabled = true)
+ public void createUpdateImportResourceFromCsarTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = null;
+ RestResponse getResource = null;
+ ResourceReqDetails resourceDetails = null;
+ RestResponse updateResource = null;
+ RestResponse createResource = null;
+ Resource resource = null;
+ RestResponse changeResourceState = null;
+
+ // create new resource from Csar
+ copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_a.csar", "valid_vf.csar");
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("valid_vf.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+ String invariantUUID = resource.getInvariantUUID();
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change composition and update resource
+ copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_b.csar", "valid_vf.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ // change name
+ resourceDetails.setName("test1");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resource.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(2, resource.getComponentInstances().size());
+ assertEquals(invariantUUID, resource.getInvariantUUID());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_a.csar", "valid_vf.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ // change name
+ resourceDetails.setName("test2");
+ // change resource metaData and update resource
+ resourceDetails.setDescription("It is new description bla bla bla");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resource.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+ assertEquals(invariantUUID, resource.getInvariantUUID());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_a.csar", "valid_vf.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ // try to update resource with wrong RI (without node types, resource
+ // shouldn't be updated)
+ copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_c.csar", "valid_vf.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ // change name (temporary)
+ resourceDetails.setName("test3");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resource.getUniqueId());
+ BaseRestUtils.checkErrorResponse(updateResource, ActionStatus.INVALID_NODE_TEMPLATE, "Definitions/tosca_mock_vf.yaml", "nodejs", "tosca.nodes.Weber");
+
+ getResource = ResourceRestUtils.getResourceByNameAndVersion(sdncModifierDetails.getUserId(), "test3", resourceDetails.getVersion());
+ BaseRestUtils.checkErrorResponse(getResource, ActionStatus.RESOURCE_NOT_FOUND, "test3");
+ getResource = ResourceRestUtils.getResourceByNameAndVersion(sdncModifierDetails.getUserId(), "test2", resourceDetails.getVersion());
+ BaseRestUtils.checkSuccess(getResource);
+
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_a.csar", "valid_vf.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ // create new resource from Csar
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("TEST01");
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // scar with wrong RI
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_b.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ resourceDetails.setDescription("BLA BLA BLA");
+ // wrong RI (with node types) resource shouldn't be created
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkErrorResponse(updateResource, ActionStatus.INVALID_NODE_TEMPLATE, "Definitions/VF_RI2_G1.yaml", "ps04_port_0", "org.openecomp.resource.cp.nodes.heat.network.neutron.Portur");
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ }
+
+ @Test(enabled = true)
+ public void createUpdateImportResourceFromCsarWithArtifactsTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = null;
+ ResourceReqDetails resourceDetails = null;
+ RestResponse updateResource = null;
+ RestResponse createResource = null;
+ Resource resource = null;
+ RestResponse changeResourceState = null;
+
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("TEST01");
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts.csar");
+ resourceDetails.setCsarVersion("1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ // create new resource from Csar
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ List<String> requiredArtifactsOld = resource.getDeploymentArtifacts().get("heat5").getRequiredArtifacts();
+ assertTrue(requiredArtifactsOld != null && !requiredArtifactsOld.isEmpty() && requiredArtifactsOld.size() == 3);
+ assertTrue(requiredArtifactsOld.contains("hot-nimbus-pcm-volumes_v1.0.yaml"));
+ assertTrue(requiredArtifactsOld.contains("nested-pcm_v1.0.yaml"));
+ assertTrue(requiredArtifactsOld.contains("hot-nimbus-oam-volumes_v1.0.yaml"));
+
+ // update scar with new artifacts
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_updated.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ resourceDetails.setDescription("BLA BLA BLA");
+ resourceDetails.setCsarVersion("2");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+
+ List<String> requiredArtifactsNew = resource.getDeploymentArtifacts().get("heat5").getRequiredArtifacts();
+ assertTrue(requiredArtifactsNew != null && !requiredArtifactsNew.isEmpty() && requiredArtifactsNew.size() == 3);
+ assertTrue(requiredArtifactsNew.contains("hot-nimbus-swift-container_v1.0.yaml"));
+ assertTrue(requiredArtifactsNew.contains("hot-nimbus-oam-volumes_v1.0.yaml"));
+ assertTrue(requiredArtifactsNew.contains("nested-oam_v1.0.yaml"));
+
+ // back original scar
+ copyRes = copyCsarRest(sdncModifierDetails, "VF_RI2_G4_withArtifacts_a.csar", "VF_RI2_G4_withArtifacts.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ }
+
+ @Test(enabled = true)
+ public void createUpdateImportWithPropertiesFromCsarUITest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "valid_vf.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ // create new resource from Csar
+ resourceDetails.setCsarUUID(payloadName);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ RestResponse changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change composition (add new RI with specified property values)
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf_d.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ // change name
+ resourceDetails.setName("test1");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(6, resource.getComponentInstances().size());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ // change composition (add new specified property values to existing RI)
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/valid_vf_f.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ // change name
+ resourceDetails.setName("test2");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(6, resource.getComponentInstances().size());
+
+ }
+
+ public static RestResponse copyCsarRest(User sdncModifierDetails, String sourceCsarUuid, String targetCsarUuid) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.COPY_CSAR_USING_SIMULATOR, config.getCatalogBeHost(), config.getCatalogBePort(), sourceCsarUuid, targetCsarUuid);
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+
+ RestResponse copyCsarResponse = http.httpSendPost(url, "dummy", headersMap);
+ if (copyCsarResponse.getErrorCode() != 200) {
+ return null;
+ }
+ return copyCsarResponse;
+
+ }
+
+ public static RestResponse getCsarRest(User sdncModifierDetails, String sourceCsarUuid) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_CSAR_USING_SIMULATOR, config.getCatalogBeHost(), config.getCatalogBePort(), sourceCsarUuid);
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+
+ RestResponse copyCsarResponse = http.httpSendGet(url, headersMap);
+ if (copyCsarResponse.getErrorCode() != 200) {
+ return null;
+ }
+ return copyCsarResponse;
+
+ }
+
+ @Test(enabled = true)
+ public void updateResourceFromCsarHappy() throws Exception {
+ RestResponse copyRes = copyCsarRest(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), "valid_vf_a.csar", "valid_vf.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ // create
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("valid_vf");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ String expectedCsarUUID = resourceDetails.getCsarUUID();
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, resource.getCsarUUID()), expectedCsarUUID.equals(resource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getResource(resource.getUniqueId());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, getResource.getCsarUUID()), expectedCsarUUID.equals(getResource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+
+ RestResponse updateResource = ResourceRestUtils.updateResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarWithGroupsHappy() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroups");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ assertEquals("verify there are 2 groups", 2, resource.getGroups().size());
+
+ Map<String, String> compNameToUniqueId = resource.getComponentInstances().stream().collect(Collectors.toMap(p -> p.getName(), p -> p.getUniqueId()));
+
+ // Verify 2 members on group1
+ // members: [ app_server, mongo_server ]
+ String[] membersNameGroup1 = { "app_server", "mongo_server" };
+ verifyMembersInResource(resource, compNameToUniqueId, "group1", membersNameGroup1);
+ // Verify 4 members on group2
+ // members: [ mongo_db, nodejs, app_server, mongo_server ]
+ String[] membersNameGroup2 = { "app_server", "mongo_server", "mongo_db", "nodejs" };
+ verifyMembersInResource(resource, compNameToUniqueId, "group2", membersNameGroup2);
+
+ // Check OUT
+ resourceDetails.setUniqueId(resource.getUniqueId());
+ RestResponse changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ changeResourceState = LifecycleRestUtils.changeResourceState(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+
+ Resource checkedOutResource = ResponseParser.parseToObjectUsingMapper(changeResourceState.getResponse(), Resource.class);
+ compNameToUniqueId = checkedOutResource.getComponentInstances().stream().collect(Collectors.toMap(p -> p.getName(), p -> p.getUniqueId()));
+
+ // Verify 2 members on group1
+ // members: [ app_server, mongo_server ]
+ verifyMembersInResource(checkedOutResource, compNameToUniqueId, "group1", membersNameGroup1);
+ // Verify 4 members on group2
+ // members: [ mongo_db, nodejs, app_server, mongo_server ]
+ verifyMembersInResource(checkedOutResource, compNameToUniqueId, "group2", membersNameGroup2);
+
+ }
+
+ private void verifyMembersInResource(Resource resource, Map<String, String> compNameToUniqueId, String groupName, String[] membersName) {
+ GroupDefinition groupDefinition = resource.getGroups().stream().filter(p -> p.getName().equals(groupName)).findFirst().get();
+ assertEquals("Verify number of members", membersName.length, groupDefinition.getMembers().size());
+ Map<String, String> createdMembers = groupDefinition.getMembers();
+ Arrays.asList(membersName).forEach(p -> {
+ assertTrue("check member name exist", createdMembers.containsKey(p));
+ });
+
+ verifyMembers(createdMembers, compNameToUniqueId);
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarWithGroupsAndPropertiesHappy() throws Exception {
+
+ RestResponse importNewGroupTypeByName = ImportRestUtils.importNewGroupTypeByName("myHeatStack1", UserRoleEnum.ADMIN);
+ // BaseRestUtils.checkCreateResponse(importNewGroupTypeByName);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroupsWithProps");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+
+ assertEquals("verify there are 2 groups", 2, resource.getGroups().size());
+
+ Map<String, String> compNameToUniqueId = resource.getComponentInstances().stream().collect(Collectors.toMap(p -> p.getName(), p -> p.getUniqueId()));
+
+ // Verify 2 members on group1
+ // members: [ app_server, mongo_server ]
+ List<GroupDefinition> groupDefinition1 = resource.getGroups().stream().filter(p -> p.getName().equals("group1")).collect(Collectors.toList());
+ assertEquals("Verify number of members", 2, groupDefinition1.get(0).getMembers().size());
+ Map<String, String> createdMembers = groupDefinition1.get(0).getMembers();
+ verifyMembers(createdMembers, compNameToUniqueId);
+
+ List<GroupProperty> properties = groupDefinition1.get(0).getProperties();
+ assertEquals("Verify number of members", 2, properties.size());
+
+ GroupProperty heatFiles = properties.stream().filter(p -> p.getName().equals("heat_files")).findFirst().get();
+ assertNotNull("check heat files not empty", heatFiles);
+ List<String> heatFilesValue = new ArrayList<>();
+ heatFilesValue.add("heat1.yaml");
+ heatFilesValue.add("heat2.yaml");
+ String heatFilesJson = gson.toJson(heatFilesValue);
+ log.debug(heatFiles.getValue());
+ assertEquals("check heat files value", heatFilesJson, heatFiles.getValue());
+
+ GroupProperty urlCredential = properties.stream().filter(p -> p.getName().equals("url_credential")).findFirst().get();
+ assertNotNull("check heat files not empty", urlCredential);
+ log.debug(urlCredential.getValue());
+ assertEquals("check url credential", "{\"protocol\":\"protocol1\",\"keys\":{\"keya\":\"valuea\",\"keyb\":\"valueb\"}}", urlCredential.getValue());
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarWithGroupsAndPropertyInvalidValue() throws Exception {
+
+ RestResponse importNewGroupTypeByName = ImportRestUtils.importNewGroupTypeByName("myHeatStack1", UserRoleEnum.ADMIN);
+ // BaseRestUtils.checkCreateResponse(importNewGroupTypeByName);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroupsInvalidPropertyValue");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ BaseRestUtils.checkStatusCode(createResource, "Check bad request error", false, 400);
+
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarWithGroupsAndInvalidPropertyName() throws Exception {
+
+ RestResponse importNewGroupTypeByName = ImportRestUtils.importNewGroupTypeByName("myHeatStack1", UserRoleEnum.ADMIN);
+ // BaseRestUtils.checkCreateResponse(importNewGroupTypeByName);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroupsPropertyNotExist");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ BaseRestUtils.checkStatusCode(createResource, "Check bad request error", false, 400);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.GROUP_PROPERTY_NOT_FOUND, "url_credential111", "group1", "org.openecomp.groups.MyHeatStack1");
+
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarGroupTypeNotExist() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroupsInvalidGroupType");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ BaseRestUtils.checkStatusCode(createResource, "Check bad request error", false, 400);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.GROUP_TYPE_IS_INVALID, "org.openecomp.groups.stamGroupType");
+
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarMemberNotExist() throws Exception {
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroupsInvalidMember");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ BaseRestUtils.checkStatusCode(createResource, "Check bad request error", false, 400);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.GROUP_INVALID_COMPONENT_INSTANCE, "mycomp", "mygroup", ValidationUtils.normaliseComponentName(resourceDetails.getName()), "VF");
+
+ }
+
+ @Test(enabled = true)
+ public void createResourceFromCsarMemberNotAllowed() throws Exception {
+
+ RestResponse importNewGroupTypeByName = ImportRestUtils.importNewGroupTypeByName("myHeatStack2", UserRoleEnum.ADMIN);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("csarWithGroupsNotAllowedMember");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+
+ BaseRestUtils.checkStatusCode(createResource, "Check bad request error", false, 400);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE, "nodejs", "group1", "org.openecomp.groups.MyHeatStack2");
+
+ }
+
+ @Test(enabled = true)
+ public void getResourceFromCsarUuidHappy() throws Exception {
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("tam");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(6, resource.getComponentInstances().size());
+
+ String expectedCsarUUID = resourceDetails.getCsarUUID();
+ String expectedToscaResourceName = "org.openecomp.resource.vf." + WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, resource.getCsarUUID()), expectedCsarUUID.equals(resource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, resource.getToscaResourceName()), expectedToscaResourceName.equals(resource.getToscaResourceName()));
+
+ RestResponse getResourceResponse = ResourceRestUtils.getLatestResourceFromCsarUuid(resource.getCsarUUID());
+ Resource getResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(), Resource.class);
+ assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID, getResource.getCsarUUID()), expectedCsarUUID.equals(getResource.getCsarUUID()));
+ assertTrue("toscaResourceName : " + buildAssertMessage(expectedToscaResourceName, getResource.getToscaResourceName()), expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+ }
+
+ @Test(enabled = true)
+ public void getResourceFromCsarResourceNotFound() throws Exception {
+ String csarUUID = "tam";
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+
+ RestResponse resResponse = ResourceRestUtils.getLatestResourceFromCsarUuid(csarUUID);
+
+ BaseRestUtils.checkStatusCode(resResponse, "Check bad request error", false, 400);
+ BaseRestUtils.checkErrorResponse(resResponse, ActionStatus.RESOURCE_FROM_CSAR_NOT_FOUND, csarUUID);
+
+ }
+
+ @Test(enabled = true)
+ public void getResourceFromMissingCsar() throws Exception {
+ String csarUUID = "abcdefg12345";
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID(csarUUID);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+
+ RestResponse resResponse = ResourceRestUtils.getLatestResourceFromCsarUuid(csarUUID);
+
+ BaseRestUtils.checkStatusCode(resResponse, "Check bad request error", false, 400);
+ BaseRestUtils.checkErrorResponse(resResponse, ActionStatus.RESOURCE_FROM_CSAR_NOT_FOUND, csarUUID);
+
+ }
+
+ @Test(enabled = true)
+ public void createUpdateCertifiedImportResourceFromCsarTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes = copyCsarRest(sdncModifierDetails, "valid_vf_a.csar", "valid_vf.csar");
+ RestResponse updateResponse = null;
+ String oldName = null;
+ // create new resource from Csar
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setCsarUUID("valid_vf.csar");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+ String invariantUUID = resource.getInvariantUUID();
+
+ // change metadata
+ // resource name, icon, vendor name, category, template derivedFrom
+ oldName = resourceDetails.getName();
+ resourceDetails.setName("test1");
+ resourceDetails.setIcon("newicon");
+ resourceDetails.setVendorName("newname");
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.VSP_ALREADY_EXISTS, "valid_vf.csar", oldName);
+
+ updateResponse = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResponse);
+
+ LifecycleRestUtils.certifyResource(resourceDetails);
+ // change metadata
+ // resource name, icon, vendor name, category, template derivedFrom
+ resourceDetails.setName("test2");
+ resourceDetails.setIcon("new icon1");
+ resourceDetails.setVendorName("new name1");
+ resourceDetails.setDescription("bla bla bla");
+ updateResponse = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResponse);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResponse.getResponse(), Resource.class);
+ assertEquals(5, resource.getComponentInstances().size());
+ assertEquals(invariantUUID, resource.getInvariantUUID());
+ assertEquals(resource.getName(), "test1");
+ assertEquals(resource.getIcon(), "newicon");
+ assertEquals(resource.getVendorName(), "newname");
+ assertEquals(resource.getDescription(), "bla bla bla");
+ assertEquals(resource.getTags().contains("test2"), false);
+ }
+
+ @Test
+ public void createImportRIRelationByCapNameFromCsarUITest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "vmmc_relate_by_cap_name.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/vmmc_relate_by_cap_name.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ // create new resource from Csar
+ resourceDetails.setCsarUUID(payloadName);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ // assert all relations created
+ assertEquals(80, resource.getComponentInstancesRelations().size());
+ }
+
+ @Test
+ public void createImportRIRelationByCapNameFromCsarUITest2() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "vf_relate_by_cap_name.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+
+ path = Paths.get(rootPath + "/src/test/resources/CI/csars/vf_relate_by_cap_name.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ // create new resource from Csar
+ resourceDetails.setCsarUUID(payloadName);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ // assert relations created: 1.by name: virtual_linkable. 2.by name:
+ // link
+ Map<String, ComponentInstance> nodes = resource.getComponentInstances().stream().collect(Collectors.toMap(n -> n.getName(), n -> n));
+ Map<String, CapabilityDefinition> capabilities = nodes.get("elinenode").getCapabilities().get("tosca.capabilities.network.Linkable").stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
+ String cp1Uid = nodes.get("cp1node").getUniqueId();
+ String cp2Uid = nodes.get("cp2node").getUniqueId();
+ Map<String, List<RequirementCapabilityRelDef>> mappedByReqOwner = resource.getComponentInstancesRelations().stream().collect(Collectors.groupingBy(e -> e.getFromNode()));
+ assertEquals(mappedByReqOwner.get(cp1Uid).get(0).getRelationships().get(0).getCapabilityUid(), capabilities.get("virtual_linkable").getUniqueId());
+ assertEquals(mappedByReqOwner.get(cp2Uid).get(0).getRelationships().get(0).getCapabilityUid(), capabilities.get("link").getUniqueId());
+ }
+
+ private void verifyMembers(Map<String, String> createdMembers, Map<String, String> compNameToUniqueId) {
+ for (Map.Entry<String, String> entry : createdMembers.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ String comparedValue = compNameToUniqueId.get(key);
+
+ assertEquals("compare instance ids", comparedValue, value);
+ }
+
+ }
+
+ private static Map<String, String> prepareHeadersMap(String userId) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ if (userId != null) {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ }
+ return headersMap;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportGenericResourceCITest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportGenericResourceCITest.java
new file mode 100644
index 0000000000..873d33979f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportGenericResourceCITest.java
@@ -0,0 +1,599 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ImportTestTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.RespJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.TODO.ImportCapabilityTypeCITest;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ImportRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+public class ImportGenericResourceCITest extends ComponentBaseTest {
+ private static Logger log = LoggerFactory.getLogger(ImportGenericResourceCITest.class.getName());
+ private static final String FILE_NAME_MY_COMPUTE = "tosca.nodes.MyCompute";
+ private static final String RESOURCE_NAME_UPDATE_COMPUTE = "userUpdateCompute";
+ private static final String RESOURCE_NAME_MY_COMPUTE = "myCompute";
+ private static final String RESOURCE_NAME_USER_COMPUTE = "userCompute";
+ private static final String FILE_NAME_USER_COMPUTE = "tosca.nodes.userCompute";
+ @Rule
+ public static TestName name = new TestName();
+
+ public ImportGenericResourceCITest() {
+ super(name, ImportGenericResourceCITest.class.getName());
+ }
+
+ @BeforeClass
+ public static void beforeImportClass() throws IOException {
+ ImportCapabilityTypeCITest.importAllCapabilityTypes();
+ // removeAllNormativeTypeResources();
+ // importAllNormativeTypesResources(UserRoleEnum.ADMIN);
+ }
+
+ static Config config = Config.instance();
+
+ public static Map<NormativeTypesEnum, Boolean> removeAllNormativeTypeResources() throws ClientProtocolException, IOException {
+ Map<NormativeTypesEnum, Boolean> normativeExistInDB = new HashMap<>();
+
+ for (NormativeTypesEnum current : NormativeTypesEnum.values()) {
+ Boolean existedBeforeDelete = ImportRestUtils.removeNormativeTypeResource(current);
+ normativeExistInDB.put(current, existedBeforeDelete);
+ }
+ return normativeExistInDB;
+ }
+
+ public static Either<String, Boolean> getNormativeTypeResource(NormativeTypesEnum current) throws ClientProtocolException, IOException {
+ return getResource(current.getNormativeName(), "1.0");
+ }
+
+ @Test
+ public void importAllTestResources() throws Exception {
+ for (ImportTestTypesEnum currResource : ImportTestTypesEnum.values()) {
+ DbUtils.cleanAllAudits();
+
+ RestResponse importResponse = ImportRestUtils.importTestResource(currResource, UserRoleEnum.ADMIN);
+ // System.err.println("import Resource
+ // "+"<"+currResource+">"+"response:
+ // "+importResponse.getErrorCode());
+ ImportRestUtils.validateImportTestTypesResp(currResource, importResponse);
+ if (currResource.getvalidateAudit() == true) {
+ // validate audit
+ String baseVersion = "1.0";
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(currResource.getActionStatus().name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.ADMIN.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.ADMIN.getUserName());
+ expectedResourceAuditJavaObject.setResourceName(currResource.getNormativeName());
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion(baseVersion);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.toString());
+ expectedResourceAuditJavaObject.setComment(null);
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ List<String> variables = (currResource.getErrorParams() != null ? currResource.getErrorParams() : new ArrayList<String>());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAuditImport(expectedResourceAuditJavaObject, auditAction);
+ }
+ }
+ }
+
+ // -----------------------------------------------------------------------------------
+ protected void validateMyComputeCapabilities(Map<String, Object> map) {
+ assertTrue(map.containsKey("capabilities"));
+ Map<String, Object> capabilities = (Map<String, Object>) map.get("capabilities");
+ assertTrue(capabilities.containsKey("tosca.capabilities.Container"));
+ List<Object> hostCapList = (List<Object>) capabilities.get("tosca.capabilities.Container");
+ assertFalse(hostCapList.isEmpty());
+ Map<String, Object> hostCap = (Map<String, Object>) hostCapList.get(0);
+ validateField(hostCap, "type", "tosca.capabilities.Container");
+ validateField(hostCap, "name", "host");
+ validateField(hostCap, "validSourceTypes", Arrays.asList(new String[] { "tosca.nodes.SoftwareComponent" }));
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.Endpoint.Admin"));
+ List<Object> endPointCapList = (List<Object>) capabilities.get("tosca.capabilities.Endpoint.Admin");
+ assertFalse(endPointCapList.isEmpty());
+ Map<String, Object> endPointCap = (Map<String, Object>) endPointCapList.get(0);
+ validateField(endPointCap, "name", "endpoint");
+ validateField(endPointCap, "type", "tosca.capabilities.Endpoint.Admin");
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.OperatingSystem"));
+ List<Object> osCapList = (List<Object>) capabilities.get("tosca.capabilities.OperatingSystem");
+ assertFalse(osCapList.isEmpty());
+ Map<String, Object> osCap = (Map<String, Object>) osCapList.get(0);
+ validateField(osCap, "name", "os");
+ validateField(osCap, "type", "tosca.capabilities.OperatingSystem");
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.Scalable"));
+ List<Object> scalableCapList = (List<Object>) capabilities.get("tosca.capabilities.Scalable");
+ assertFalse(scalableCapList.isEmpty());
+ Map<String, Object> scalableCap = (Map<String, Object>) scalableCapList.get(0);
+ validateField(scalableCap, "name", "scalable");
+ validateField(scalableCap, "type", "tosca.capabilities.Scalable");
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.network.Bindable"));
+ List<Object> bindingCapList = (List<Object>) capabilities.get("tosca.capabilities.network.Bindable");
+ assertFalse(bindingCapList.isEmpty());
+ Map<String, Object> bindingCap = (Map<String, Object>) bindingCapList.get(0);
+ validateField(bindingCap, "name", "binding");
+ validateField(bindingCap, "type", "tosca.capabilities.network.Bindable");
+
+ }
+
+ protected void validateMyComputeResource(String resourceName, String resourceVersion, String expectedState) throws ClientProtocolException, IOException {
+ Either<String, Boolean> eitherMyCompute = getResource(resourceName, resourceVersion);
+ assertTrue(eitherMyCompute.isLeft());
+ String testComputeYml = eitherMyCompute.left().value();
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map = (Map<String, Object>) new Gson().fromJson(testComputeYml, map.getClass());
+
+ validateMyComputeBasicFields(map, resourceName, resourceVersion, expectedState);
+
+ validateMyComputeCapabilities(map);
+
+ validateMyComputeRequirements(map);
+ validateField(map, RespJsonKeysEnum.RESOURCE_VERSION.getRespJsonKeyName(), resourceVersion);
+
+ }
+
+ protected void validateMyComputeResource(String uid, String resourceName, String resourceVersion, String expectedState) throws ClientProtocolException, IOException {
+ RestResponse resourceResponse = ResourceRestUtils.getResource(uid);
+ ResourceRestUtils.checkSuccess(resourceResponse);
+ String testComputeYml = resourceResponse.getResponse();
+
+ // Either<String, Boolean> eitherMyCompute = getResource(resourceName,
+ // resourceVersion);
+ // assertTrue( eitherMyCompute.isLeft() );
+ // String testComputeYml = eitherMyCompute.left().value();
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map = (Map<String, Object>) new Gson().fromJson(testComputeYml, map.getClass());
+
+ validateMyComputeBasicFields(map, resourceName, resourceVersion, expectedState);
+
+ validateMyComputeCapabilities(map);
+
+ validateMyComputeRequirements(map);
+ validateField(map, RespJsonKeysEnum.RESOURCE_VERSION.getRespJsonKeyName(), resourceVersion);
+
+ }
+
+ protected void validateMyComputeResourceAfterUpdate(String uid, String resourceName, String resourceVersion, String expectedState) throws ClientProtocolException, IOException {
+ RestResponse resourceResponse = ResourceRestUtils.getResource(uid);
+ ResourceRestUtils.checkSuccess(resourceResponse);
+ String testComputeYml = resourceResponse.getResponse();
+
+ // Either<String, Boolean> eitherMyCompute = getResource(resourceName,
+ // resourceVersion);
+ // assertTrue( eitherMyCompute.isLeft() );
+
+ // String testComputeYml = eitherMyCompute.left().value();
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map = (Map<String, Object>) new Gson().fromJson(testComputeYml, map.getClass());
+
+ validateMyComputeBasicFields(map, resourceName, resourceVersion, expectedState);
+ validateField(map, RespJsonKeysEnum.DESCRIPTION.getRespJsonKeyName(), "Short description");
+ validateField(map, RespJsonKeysEnum.VENDOR_NAME.getRespJsonKeyName(), "UserVendor");
+ validateField(map, RespJsonKeysEnum.VENDOR_RELEASE.getRespJsonKeyName(), "1.1.2");
+
+ // validateMyComputeCapabilities(map);
+ // AssertJUnit.assertTrue(map.containsKey("capabilities"));
+ // Map<String, Object> capabilities = (Map<String, Object>)
+ // map.get("capabilities");
+ // AssertJUnit.assertTrue(capabilities.containsKey("host"));
+ // Map<String, Object> hostCap = (Map<String, Object>)
+ // capabilities.get("host");
+ // validateField(hostCap, "type", "tosca.capabilities.Container");
+ // validateField(hostCap, "validSourceTypes", Arrays.asList(new
+ // String[]{"tosca.nodes.SoftwareComponent"}));
+ //
+ // AssertJUnit.assertTrue(capabilities.containsKey("endpoint"));
+ // Map<String, Object> endPointCap = (Map<String, Object>)
+ // capabilities.get("endpoint");
+ // validateField(endPointCap, "type",
+ // "tosca.capabilities.Endpoint.Admin");
+
+ assertTrue(map.containsKey("capabilities"));
+ Map<String, Object> capabilities = (Map<String, Object>) map.get("capabilities");
+ assertTrue(capabilities.containsKey("tosca.capabilities.Container"));
+ List<Object> hostCapList = (List<Object>) capabilities.get("tosca.capabilities.Container");
+ assertFalse(hostCapList.isEmpty());
+ Map<String, Object> hostCap = (Map<String, Object>) hostCapList.get(0);
+ validateField(hostCap, "type", "tosca.capabilities.Container");
+ validateField(hostCap, "name", "host");
+ validateField(hostCap, "validSourceTypes", Arrays.asList(new String[] { "tosca.nodes.SoftwareComponent" }));
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.Endpoint.Admin"));
+ List<Object> endPointCapList = (List<Object>) capabilities.get("tosca.capabilities.Endpoint.Admin");
+ assertFalse(endPointCapList.isEmpty());
+ Map<String, Object> endPointCap = (Map<String, Object>) endPointCapList.get(0);
+ validateField(endPointCap, "name", "endpoint");
+ validateField(endPointCap, "type", "tosca.capabilities.Endpoint.Admin");
+
+ validateMyComputeRequirements(map);
+ validateField(map, RespJsonKeysEnum.RESOURCE_VERSION.getRespJsonKeyName(), resourceVersion);
+
+ }
+
+ protected void validateMyComputeRequirements(Map<String, Object> map) {
+ assertTrue(map.containsKey("requirements"));
+ Map<String, Object> requirements = (Map<String, Object>) map.get("requirements");
+
+ assertTrue(requirements.containsKey("tosca.capabilities.Attachment"));
+ List<Object> localStorageReqList = (List<Object>) requirements.get("tosca.capabilities.Attachment");
+ assertFalse(localStorageReqList.isEmpty());
+ Map<String, Object> localStorageReq = (Map<String, Object>) localStorageReqList.get(0);
+ validateField(localStorageReq, "capability", "tosca.capabilities.Attachment");
+ validateField(localStorageReq, "node", "tosca.nodes.BlockStorage");
+ validateField(localStorageReq, "relationship", "tosca.relationships.AttachesTo");
+ validateField(localStorageReq, "name", "local_storage");
+ }
+
+ protected void validateMyComputeBasicFields(Map<String, Object> map, String resourceName, String resourceVersion, String expectedState) {
+ validateField(map, RespJsonKeysEnum.IS_ABSTRACT.getRespJsonKeyName(), false);
+ // validateField(map, RespJsonKeysEnum.CATEGORIES.getRespJsonKeyName(),
+ // categoryDefinition);
+ // validateField(map, RespJsonKeysEnum.UNIQUE_ID.getRespJsonKeyName(),
+ // UniqueIdBuilder.buildResourceUniqueId(resourceName,
+ // resourceVersion));
+ validateField(map, RespJsonKeysEnum.RESOURCE_NAME.getRespJsonKeyName(), resourceName);
+ validateField(map, RespJsonKeysEnum.TAGS.getRespJsonKeyName(), Arrays.asList(new String[] { resourceName }));
+ validateField(map, RespJsonKeysEnum.LIFE_CYCLE_STATE.getRespJsonKeyName(), expectedState);
+
+ validateField(map, RespJsonKeysEnum.DERIVED_FROM.getRespJsonKeyName(), Arrays.asList(new String[] { "tosca.nodes.Root" }));
+ }
+
+ protected static void validateField(Map<String, Object> map, String jsonField, Object expectedValue) {
+ if (expectedValue == null) {
+ assertTrue(!map.containsKey(jsonField));
+ } else {
+ assertTrue("map does not contain field " + jsonField, map.containsKey(jsonField));
+ Object foundValue = map.get(jsonField);
+ compareElements(expectedValue, foundValue);
+ }
+ }
+
+ protected static void compareElements(Object expectedValue, Object foundValue) {
+ if (expectedValue instanceof String) {
+ assertTrue(foundValue instanceof String);
+ assertTrue(foundValue.equals(expectedValue));
+ }
+
+ else if (expectedValue instanceof Boolean) {
+ assertTrue(foundValue instanceof Boolean);
+ assertTrue(foundValue == expectedValue);
+ } else if (expectedValue instanceof Map) {
+ assertTrue(foundValue instanceof Map);
+ Map<String, Object> foundMap = (Map<String, Object>) foundValue;
+ Map<String, Object> excpectedMap = (Map<String, Object>) expectedValue;
+ assertTrue(foundMap.size() == excpectedMap.size());
+ Iterator<String> foundkeyItr = foundMap.keySet().iterator();
+ while (foundkeyItr.hasNext()) {
+ String foundKey = foundkeyItr.next();
+ assertTrue(excpectedMap.containsKey(foundKey));
+ compareElements(excpectedMap.get(foundKey), foundMap.get(foundKey));
+ }
+
+ } else if (expectedValue instanceof List) {
+ assertTrue(foundValue instanceof List);
+ List<Object> foundList = (List<Object>) foundValue;
+ List<Object> excpectedList = (List<Object>) expectedValue;
+ assertTrue(foundList.size() == excpectedList.size());
+ for (int i = 0; i < foundList.size(); i++) {
+ compareElements(excpectedList.get(i), foundList.get(i));
+ }
+
+ } else if (expectedValue instanceof CategoryDefinition) {
+ assertTrue(foundValue instanceof Map);
+ CategoryDefinition expCat = (CategoryDefinition) expectedValue;
+ Map<String, Object> actCat = (Map<String, Object>) foundValue;
+ assertEquals(expCat.getName(), actCat.get("name"));
+
+ // assertEquals(expCat.getSubcategories().get(0).getName(),
+ // actCat.get("subcategories").getName());
+ } else {
+ assertTrue(foundValue.equals(expectedValue));
+ }
+ }
+
+ public static void restoreToOriginalState(Map<NormativeTypesEnum, Boolean> originalState, UserRoleEnum userRole) throws IOException {
+ removeAllNormativeTypeResources();
+
+ Iterator<Entry<NormativeTypesEnum, Boolean>> iterator = originalState.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<NormativeTypesEnum, Boolean> entry = iterator.next();
+ Boolean isExistBeforeDelete = entry.getValue();
+ if (isExistBeforeDelete) {
+ importNormativeResource(entry.getKey(), userRole);
+ }
+ }
+
+ }
+
+ public static void importAllNormativeTypesResources(UserRoleEnum userRole) throws IOException {
+ for (NormativeTypesEnum currResource : NormativeTypesEnum.values()) {
+ Either<String, Boolean> resource = getResource(currResource.getNormativeName(), "1.0");
+ if (resource.isRight()) {
+ importNormativeResource(currResource, userRole);
+ }
+ }
+
+ }
+
+ protected static Integer importNormativeResource(NormativeTypesEnum resource, UserRoleEnum userRole) throws IOException {
+ return importResource(resource.getFolderName(), userRole, true);
+ }
+
+ protected static Integer importResource(String folderName, UserRoleEnum userRole, boolean isNormative) throws IOException {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("resourceZip", new FileBody(getZipFile(folderName)));
+ mpBuilder.addPart("resourceMetadata", new StringBody(getJsonStringOfFile(folderName, folderName + ".json"), ContentType.APPLICATION_JSON));
+
+ String url = String.format(Urls.IMPORT_RESOURCE_NORMATIVE, config.getCatalogBeHost(), config.getCatalogBePort());
+ if (!isNormative) {
+ url = String.format(Urls.IMPORT_USER_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+ }
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("USER_ID", userRole.getUserId());
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ return response.getStatusLine().getStatusCode();
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ public static void closeHttpClient(CloseableHttpClient client) {
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ log.debug("failed to close client or response: ", e);
+ }
+ }
+
+ public static void closeResponse(CloseableHttpResponse response) {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ } catch (IOException e) {
+ log.debug("failed to close client or response: ", e);
+ }
+ }
+
+ protected static String getJsonStringOfFile(String folderName, String fileName) throws IOException {
+ String sourceDir = config.getImportResourceConfigDir();
+ sourceDir += File.separator + "normative-types";
+
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + folderName, fileName);
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String content = new String(fileContent);
+ return content;
+ }
+
+ protected static File getZipFile(String elementName) throws IOException {
+ String sourceDir = config.getImportResourceConfigDir();
+ sourceDir += File.separator + "normative-types";
+
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + elementName, "normative-types-new-" + elementName + ".zip");
+ return filePath.toFile();
+ }
+
+ protected static String getTestJsonStringOfFile(String folderName, String fileName) throws IOException {
+ String sourceDir = config.getImportResourceTestsConfigDir();
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + folderName, fileName);
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String content = new String(fileContent);
+ return content;
+ }
+
+ protected static File getTestZipFile(String elementName) throws IOException {
+ String sourceDir = config.getImportResourceTestsConfigDir();
+
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + elementName, "normative-types-new-" + elementName + ".zip");
+ return filePath.toFile();
+ }
+
+ protected static Either<String, Boolean> getResource(String name, String version) throws IOException {
+ RestResponse resource = ResourceRestUtils.getResourceByNameAndVersion(UserRoleEnum.DESIGNER.getUserId(), name, version);
+ if (resource.getErrorCode() == ImportRestUtils.STATUS_CODE_GET_SUCCESS) {
+ return Either.left(resource.getResponse());
+ // return Either.right(true);
+
+ }
+ return Either.right(false);
+ }
+
+ @Test
+ public void testImportWithRequirmentsAndCapabilities() throws IOException {
+ String fileName = FILE_NAME_MY_COMPUTE;
+ RestResponse response = ImportRestUtils.importNormativeResourceByName(RESOURCE_NAME_MY_COMPUTE, UserRoleEnum.ADMIN);
+ Integer statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_IMPORT_SUCCESS);
+ String uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "1.0", "CERTIFIED");
+ }
+
+ @Test
+ public void testImportWithUpdateNormativeType() throws IOException {
+ String fileName = FILE_NAME_MY_COMPUTE;
+ RestResponse response = ImportRestUtils.importNormativeResourceByName(RESOURCE_NAME_MY_COMPUTE, UserRoleEnum.ADMIN);
+ Integer statusCode = response.getErrorCode();
+ assertTrue(statusCode == BaseRestUtils.STATUS_CODE_IMPORT_SUCCESS);
+ String uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "1.0", "CERTIFIED");
+
+ // update
+ response = ImportRestUtils.importNormativeResourceByName(RESOURCE_NAME_MY_COMPUTE, UserRoleEnum.ADMIN);
+ statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_UPDATE_SUCCESS);
+ uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "2.0", "CERTIFIED");
+
+ }
+
+ @Test
+ public void testImportWithInvalidDefaultValue() throws IOException {
+ RestResponse response = ImportRestUtils.importNewResourceByName("portInvalidDefaultValue", UserRoleEnum.DESIGNER);
+ assertTrue(response.getErrorCode() == HttpStatus.SC_BAD_REQUEST);
+ }
+
+ @Test
+ public void testImportUserResource() throws IOException {
+ String fileName = FILE_NAME_USER_COMPUTE;
+ RestResponse response = ImportRestUtils.importNewResourceByName(RESOURCE_NAME_USER_COMPUTE, UserRoleEnum.DESIGNER);
+ Integer statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_IMPORT_SUCCESS);
+ String uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "0.1", "NOT_CERTIFIED_CHECKOUT");
+
+ }
+
+ @Test
+ public void testImportAndUpdateUserResource() throws IOException {
+ String fileName = FILE_NAME_USER_COMPUTE;
+ RestResponse response = ImportRestUtils.importNewResourceByName(RESOURCE_NAME_USER_COMPUTE, UserRoleEnum.DESIGNER);
+ Integer statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_IMPORT_SUCCESS);
+ String uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "0.1", "NOT_CERTIFIED_CHECKOUT");
+ response = ImportRestUtils.importNewResourceByName(RESOURCE_NAME_UPDATE_COMPUTE, UserRoleEnum.DESIGNER);
+ statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_UPDATE_SUCCESS);
+ uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResourceAfterUpdate(uid, fileName, "0.1", "NOT_CERTIFIED_CHECKOUT");
+
+ }
+
+ @Test
+ public void testImportAndUpdateChangesUserResource() throws IOException {
+ String fileName = FILE_NAME_USER_COMPUTE;
+ RestResponse response = ImportRestUtils.importNewResourceByName(RESOURCE_NAME_USER_COMPUTE, UserRoleEnum.DESIGNER);
+ Integer statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_IMPORT_SUCCESS);
+ String uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "0.1", "NOT_CERTIFIED_CHECKOUT");
+ // Either<String, Boolean> resource = getResource(fileName, "0.1");
+ // assertTrue(resource.isLeft());
+
+ response = ImportRestUtils.importNewResourceByName(RESOURCE_NAME_UPDATE_COMPUTE, UserRoleEnum.DESIGNER);
+ statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_UPDATE_SUCCESS);
+ validateMyComputeResourceAfterUpdate(uid, fileName, "0.1", "NOT_CERTIFIED_CHECKOUT");
+
+ }
+
+ @Test
+ public void testImportCheckoutAndUpdateUserResource() throws IOException {
+ String fileName = FILE_NAME_USER_COMPUTE;
+ RestResponse response = ImportRestUtils.importNormativeResourceByName(RESOURCE_NAME_USER_COMPUTE, UserRoleEnum.ADMIN);
+ Integer statusCode = response.getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.STATUS_CODE_IMPORT_SUCCESS);
+ String uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "1.0", "CERTIFIED");
+
+ response = ImportRestUtils.importNewResourceByName(RESOURCE_NAME_USER_COMPUTE, UserRoleEnum.DESIGNER);
+ statusCode = response.getErrorCode();
+ assertEquals("check response code after update resource", ImportRestUtils.STATUS_CODE_UPDATE_SUCCESS, statusCode.intValue());
+ uid = ResponseParser.getUniqueIdFromResponse(response);
+ validateMyComputeResource(uid, fileName, "1.1", "NOT_CERTIFIED_CHECKOUT");
+
+ }
+
+ @Test
+ public void importNormativeTypesTesterUserRole() throws Exception {
+ Integer statusCode = ImportRestUtils.importNormativeResourceByName(RESOURCE_NAME_MY_COMPUTE, UserRoleEnum.TESTER).getErrorCode();
+ assertTrue(statusCode == ImportRestUtils.RESTRICTED_OPERATION);
+ }
+
+ @Test
+ public void importNormativeTypesDesignerUserRole() throws Exception {
+ Integer statusCode = ImportRestUtils.importNormativeResourceByName(RESOURCE_NAME_MY_COMPUTE, UserRoleEnum.DESIGNER).getErrorCode();
+ assertTrue(statusCode == 409);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportNewResourceCITest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportNewResourceCITest.java
new file mode 100644
index 0000000000..ec335fa65f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportNewResourceCITest.java
@@ -0,0 +1,1431 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpStatus;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ImportTestTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ImportRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ResourceValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class ImportNewResourceCITest extends ComponentBaseTest {
+
+ // public static UserUtils userUtils = new UserUtils();
+ // public ResourceUtils resourceUtils = new ResourceUtils();
+ // public AuditValidationUtils AuditValidationUtils = new
+ // AuditValidationUtils();
+ // protected ArtifactUtils artifactUtils = new ArtifactUtils();
+
+ protected String resourceVersion = null;
+ protected String auditAction = null;
+ public User sdncModifierDetails = new User();
+ protected String artifactName1 = "data_artifact1.sh";
+ protected String artifactName2 = "data_artifact2.sh";
+ protected String interfaze = "standard";
+ protected String interfaceArtifactName = "data_interface1.sh";
+
+ private String SPECIAL_CHARACTERS = "~!#@~$%^*()[];:'\"|\\/";
+
+ public ResourceReqDetails resourceDetails = new ResourceReqDetails();
+
+ public Gson gson = new Gson();
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ImportNewResourceCITest() {
+ super(name, ImportNewResourceCITest.class.getName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+
+ // init user
+ sdncModifierDetails.setUserId(UserRoleEnum.ADMIN.getUserId());
+ // init resource details
+ resourceDetails = ElementFactory.getDefaultResource("importResource4test", NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, "jh0003");
+ }
+
+ @Test
+ public void importAllTestResources_toValidateNewAPI() throws Exception {
+
+ for (ImportTestTypesEnum currResource : ImportTestTypesEnum.values()) {
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import testResources trough newResource API
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName(currResource.getFolderName(), UserRoleEnum.ADMIN);
+ System.err.println("import Resource " + "<" + currResource.getFolderName() + ">" + "response: " + importResponse.getErrorCode());
+
+ // validate response
+ ImportRestUtils.validateImportTestTypesResp(currResource, importResponse);
+ if (currResource.getvalidateAudit() == true) {
+ // validate audit
+ // String baseVersion="0.1";
+ String baseVersion = "";
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(currResource.getActionStatus().name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.ADMIN.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.ADMIN.getUserName());
+ expectedResourceAuditJavaObject.setResourceName(currResource.getNormativeName());
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion(baseVersion);
+ expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.toString());
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setComment(null);
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ List<String> variables = (currResource.getErrorParams() != null ? currResource.getErrorParams() : new ArrayList<String>());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAuditImport(expectedResourceAuditJavaObject, auditAction);
+ }
+ }
+ }
+
+ protected RestResponse importNewResource(UserRoleEnum userRoleEnum) throws Exception {
+
+ // init user
+ sdncModifierDetails.setUserId(userRoleEnum.getUserId());
+ // init resource details
+ resourceDetails = ElementFactory.getDefaultResource("importResource4test", NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, "jh0003");
+ // clean ES DB
+ DbUtils.cleanAllAudits();
+ // import new resource (expected checkOut state)
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4test", userRoleEnum);
+ return importResponse;
+ }
+
+ @Test(enabled = false)
+ public void importUIResource() throws IOException {
+ String payload = "tosca_definitions_version: tosca_simple_yaml_1_0_0\r\n" + "node_types: \r\n" + " org.openecomp.resource.importResource4test:\r\n" + " derived_from: tosca.nodes.Root\r\n" + " description: someDesc";
+
+ String encodedPayload = new String(Base64.encodeBase64(payload.getBytes()));
+
+ String json = "{\r\n" + " \"resourceName\": \"importResource4test\",\r\n" + " \"payloadName\": \"importResource4test.yml\",\r\n"
+ + " \"categories\": [{\"name\": \"Application L4+\",\"normalizedName\": \"application l4+\",\"uniqueId\": \"resourceNewCategory.application l4+\",\"subcategories\": [{\"name\": \"Web Server\"}]}],\r\n"
+ + " \"description\": \"ResourceDescription\",\r\n" + " \"vendorName\": \"VendorName\",\r\n" + " \"vendorRelease\": \"VendorRelease\",\r\n" + " \"contactId\": \"AT1234\",\r\n" + " \"icon\": \"router\",\r\n" + " \"tags\": [\r\n"
+ + " \"importResource4test\"\r\n" + " ],\r\n" + " \"payloadData\": \"" + encodedPayload + "\"\r\n" + "}";
+
+ String md5 = GeneralUtility.calculateMD5ByString(json);
+
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put(Constants.MD5_HEADER, md5);
+ headers.put(Constants.USER_ID_HEADER, UserRoleEnum.ADMIN.getUserId());
+ headers.put(Constants.CONTENT_TYPE_HEADER, "application/json");
+
+ String url = String.format(Urls.CREATE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ HttpRequest httpUtil = new HttpRequest();
+ RestResponse httpSendPost = httpUtil.httpSendPost(url, json, headers);
+ Integer errorCode = httpSendPost.getErrorCode();
+ assertTrue(errorCode == HttpStatus.SC_CREATED);
+
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_suc() throws Exception {
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ // validate response
+
+ resourceVersion = "0.1";
+
+ // ResourceRespJavaObject resourceRespJavaObject =
+ // Convertor.constructFieldsForRespValidation(resourceDetails,
+ // resourceVersion);
+ // resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // ResourceValidationUtils.validateResp(importResponse,
+ // resourceRespJavaObject);
+ //
+ // //validate get response
+ //
+ // RestResponse resourceGetResponse =
+ // ResourceRestUtils.getResource(sdncModifierDetails, resourceVersion);
+ // ResourceValidationUtils.validateResp(resourceGetResponse,
+ // resourceRespJavaObject);
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertNotNull(resourceFromImport);
+
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceRespJavaObject.getUniqueId());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // validate
+ ResourceValidationUtils.validateModelObjects(resourceFromImport, resourceFromGet);
+
+ // validate audit
+ resourceDetails.setVersion(resourceDetails.getVersion());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+
+ auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void importNewResource_byTester_failed() throws Exception {
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.TESTER);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 409, importResponse.getErrorCode().intValue());
+
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existInCheckout_updateVendorName_updateCategory() throws Exception {
+
+ // import new resource
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in other state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4testUpdateVendorNameAndCategory", UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 200, importResponse.getErrorCode().intValue());
+
+ // validate response
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertNotNull(resourceFromImport);
+
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceRespJavaObject.getUniqueId());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // validate
+ ResourceValidationUtils.validateModelObjects(resourceFromImport, resourceFromGet);
+
+ // validate audit
+ resourceDetails.setVersion(resourceDetails.getVersion());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails);
+
+ auditAction = "ResourceImport";
+ resourceVersion = "0.1";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @Test
+ public void importNewResource_perfromByAdmin_ownedBy_diffrentUser() throws Exception {
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.DESIGNER);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_IN_CHECKOUT_STATE.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), importResponse.getErrorCode());
+
+ String[] split = resourceFromImport.getLastUpdaterFullName().split(" ");
+ String firstName = split[0];
+ String lastName = split[1];
+ List<String> variables = Arrays.asList(resourceFromImport.getName(), "resource", firstName, lastName, resourceFromImport.getLastUpdaterUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_IN_CHECKOUT_STATE.name(), variables, importResponse.getResponse());
+
+ }
+
+ @Test
+ public void importNewResource_perfromByDesigner_ownedBy_diffrentUser() throws Exception {
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ importResponse = importNewResource(UserRoleEnum.DESIGNER);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_IN_CHECKOUT_STATE.name());
+ assertEquals("Check response code after adding artifact", errorInfo.getCode(), importResponse.getErrorCode());
+
+ String[] split = resourceFromImport.getLastUpdaterFullName().split(" ");
+ String firstName = split[0];
+ String lastName = split[1];
+ List<String> variables = Arrays.asList(resourceFromImport.getName(), "resource", firstName, lastName, resourceFromImport.getLastUpdaterUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_IN_CHECKOUT_STATE.name(), variables, importResponse.getResponse());
+
+ }
+
+ @Test(enabled = false)
+ public void importNewResource_nameSpace_vf() throws Exception {
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4testVF", UserRoleEnum.DESIGNER);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertTrue(resourceRespJavaObject.getResourceType().equals(ResourceTypeEnum.VF));
+
+ }
+
+ @Test
+ public void importNewResource_nameSpace_vfc() throws Exception {
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4testVFC", UserRoleEnum.DESIGNER);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertTrue(resourceRespJavaObject.getResourceType().equals(ResourceTypeEnum.VFC));
+ }
+
+ @Test
+ public void importNewResource_nameSpace_vl() throws Exception {
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4testVL", UserRoleEnum.DESIGNER);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertTrue(resourceRespJavaObject.getResourceType().equals(ResourceTypeEnum.VL));
+
+ }
+
+ @Test
+ public void importNewResource_nameSpace_cp() throws Exception {
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4testCP", UserRoleEnum.DESIGNER);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertTrue(resourceRespJavaObject.getResourceType().equals(ResourceTypeEnum.CP));
+ }
+
+ @Test
+ public void importNewResource_nameSpace_unknown() throws Exception {
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.DESIGNER);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertTrue(resourceRespJavaObject.getResourceType().equals(ResourceTypeEnum.VFC));
+
+ }
+
+ @Test
+ public void importNewResource_MissingNameSpace() throws Exception {
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName("importResource4testMissingNameSpace", UserRoleEnum.DESIGNER);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 400, importResponse.getErrorCode().intValue());
+
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existInCheckOut() throws Exception {
+
+ // import new resource
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in CHECKOUT state
+
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 200, importResponse.getErrorCode().intValue());
+
+ // validate response
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertNotNull(resourceFromImport);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceRespJavaObject.getUniqueId());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // validate
+ ResourceValidationUtils.validateModelObjects(resourceFromImport, resourceFromGet);
+
+ // validate audit
+ resourceDetails.setVersion(resourceDetails.getVersion());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails);
+
+ auditAction = "ResourceImport";
+ resourceVersion = "0.1";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existIn_CheckIn_state() throws Exception {
+
+ // import new resource
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ // checkIn resource
+
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in CHECKIN state
+
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 200, importResponse.getErrorCode().intValue());
+
+ // validate response
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertNotNull(resourceFromImport);
+
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceRespJavaObject.getUniqueId());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // validate
+ ResourceValidationUtils.validateModelObjects(resourceFromImport, resourceFromGet);
+
+ // validate audit
+ resourceDetails.setVersion(resourceDetails.getVersion());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails);
+
+ resourceVersion = "0.2";
+ auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @Test
+ public void importNewResource_existIn_Ready4cert_state_performByTester() throws Exception {
+ // import new resource
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ // add mandatory artifacts
+ // // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // checkIn resource
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(checkInResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(checkInResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in CHECKIN state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.TESTER);
+
+ // validate response
+ resourceVersion = resourceDetails.getVersion();
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ assertNotNull("check response object is not null after create resouce", importResponse);
+ assertNotNull("check error code exists in response after create resource", importResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), importResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables, importResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setResourceName("");
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.TESTER.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.TESTER.getUserName());
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existIn_Ready4cert_state_performByDesigner() throws Exception {
+ // import new resource
+
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ // add mandatory artifacts
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // checkIn resource
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in other state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.DESIGNER);
+
+ // validate response
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_SENT_FOR_CERTIFICATION.name());
+ assertNotNull("check response object is not null after create resouce", importResponse);
+ assertNotNull("check error code exists in response after create resource", importResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), importResponse.getErrorCode());
+ String[] split = resourceFromGet.getLastUpdaterFullName().split(" ");
+ String firstName = split[0];
+ String lastName = split[1];
+ List<String> variables = Arrays.asList(resourceFromGet.getName(), "resource", firstName, lastName, resourceFromGet.getLastUpdaterUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_SENT_FOR_CERTIFICATION.name(), variables, importResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.DESIGNER.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.DESIGNER.getUserName());
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.READY_FOR_CERTIFICATION).toString());
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existIn_Ready4cert_state_performByAdmin() throws Exception {
+
+ // import new resource
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // add mandatory artifacts
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // checkIn resource
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in other state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.ADMIN);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 200, importResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+ resourceVersion = resourceDetails.getVersion();
+ // resourceVersion="0.2";
+
+ // validate response
+ Resource resourceFromImport = ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ assertNotNull(resourceFromImport);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+
+ // validate get response
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceRespJavaObject.getUniqueId());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // validate
+ ResourceValidationUtils.validateModelObjects(resourceFromImport, resourceFromGet);
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @Test
+ public void importNewResource_existIn_CerInProgress_state_performByTester() throws Exception {
+
+ // import new resource
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // add mandatory artifacts
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // checkIn resource
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // startCert
+ RestResponse startCert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertNotNull("check response object is not null after resource request start certification", startCert);
+ assertEquals("Check response code after checkout resource", 200, startCert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(startCert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(startCert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in other state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.TESTER);
+
+ // validate response
+ resourceVersion = resourceDetails.getVersion();
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ assertNotNull("check response object is not null after create resouce", importResponse);
+ assertNotNull("check error code exists in response after create resource", importResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), importResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables, importResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setResourceName("");
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.TESTER.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.TESTER.getUserName());
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existIn_CerInProgress_state_performByDesigner() throws Exception {
+
+ User sdncAdminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // import new resource
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // add mandatory artifacts
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // checkIn resource
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // startCert
+ RestResponse startCert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertNotNull("check response object is not null after resource request start certification", startCert);
+ assertEquals("Check response code after checkout resource", 200, startCert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(startCert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(startCert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+ resourceVersion = resourceDetails.getVersion();
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in other state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.DESIGNER);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE.name());
+ assertNotNull("check response object is not null after create resouce", importResponse);
+ assertNotNull("check error code exists in response after create resource", importResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), importResponse.getErrorCode());
+ List<String> variables = Arrays.asList(resourceDetails.getName(), "resource", sdncAdminUser.getFirstName(), sdncAdminUser.getLastName(), sdncAdminUser.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE.name(), variables, importResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.DESIGNER.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.DESIGNER.getUserName());
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.CERTIFICATION_IN_PROGRESS).toString());
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ // TODO DE171337
+ @Test(enabled = false)
+ public void importNewResource_existIn_CerInProgress_state_performByAdmin() throws Exception {
+
+ User sdncAdminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // import new resource
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+
+ // add mandatory artifacts
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // checkIn resource
+ resourceVersion = resourceDetails.getVersion();
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ // startCert
+ RestResponse startCert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertNotNull("check response object is not null after resource request start certification", startCert);
+ assertEquals("Check response code after checkout resource", 200, startCert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(startCert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(startCert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+ resourceVersion = resourceDetails.getVersion();
+
+ // clean audit
+ DbUtils.cleanAllAudits();
+
+ // import new resource while resource already exist in other state
+ importResponse = ImportRestUtils.importNewResourceByName("importResource4test", UserRoleEnum.ADMIN);
+
+ // validate response
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE.name());
+ assertNotNull("check response object is not null after create resouce", importResponse);
+ assertNotNull("check error code exists in response after create resource", importResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), importResponse.getErrorCode());
+ List<String> variables = Arrays.asList(resourceDetails.getName(), "resource", sdncAdminUser.getFirstName(), sdncAdminUser.getLastName(), sdncAdminUser.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE.name(), variables, importResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "ResourceImport";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.ADMIN.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.ADMIN.getUserName());
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.CERTIFICATION_IN_PROGRESS).toString());
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ // TODO DE171337
+ // @Test(enabled = false)
+ // public void
+ // importNewResource_existIn_Certified_state_chnage_reqAndCap_byDesigner()
+ // throws Exception{
+ //
+ // // Andrey - set default artifact details
+ // ArtifactDefinition artifactDefinition =
+ // artifactUtils.constructDefaultArtifactInfo();
+ //
+ // // import new resource
+ // RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+ // assertNotNull("check response object is not null after import resource",
+ // importResponse);
+ // assertNotNull("check error code exists in response after import
+ // resource", importResponse.getErrorCode());
+ // assertEquals("Check response code after import resource", 201,
+ // importResponse.getErrorCode().intValue());
+ // String resourceId =
+ // ResponseParser.getUniqueIdFromResponse(importResponse);
+ // resourceDetails =
+ // ResponseParser.parseToObject(importResponse.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceVersion = resourceDetails.getVersion();
+ // RestResponse resourceGetResponse =
+ // ResourceRestUtils.getResource(sdncModifierDetails,
+ // resourceDetails.getUniqueId());
+ // assertEquals("Check response code after get resource", 200,
+ // resourceGetResponse.getErrorCode().intValue());
+ // Resource resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ // assertNotNull(resourceFromGet);
+ //
+ // // add mandatory artifacts
+ // // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ // resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ // resourceDetails.getUniqueId());
+ // assertEquals("Check response code after get resource", 200,
+ // resourceGetResponse.getErrorCode().intValue());
+ // resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ // assertNotNull(resourceFromGet);
+ // resourceDetails =
+ // ResponseParser.parseToObject(importResponse.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceDetails.setVersion(resourceFromGet.getVersion());
+ //
+ // // add artifact
+ // artifactDefinition.setArtifactName(artifactName1);
+ // ArtifactRestUtils.addInformationalArtifactToResource(resourceDetails,
+ // sdncModifierDetails, resourceVersion , artifactDefinition);
+ //
+ // // add artifact
+ // artifactDefinition.setArtifactName(artifactName2);
+ // resourceUtils.add_artifact(resourceDetails, sdncModifierDetails,
+ // resourceVersion , artifactDefinition);
+ //
+ // // add interface
+ // artifactDefinition.setArtifactName(interfaceArtifactName);
+ // ResourceRestUtils.add_interface(resourceDetails, sdncModifierDetails,
+ // resourceVersion , artifactDefinition);
+ //
+ // //construct fields for validation
+ // resourceVersion="1.0";
+ //
+ // ResourceRespJavaObject resourceRespJavaObject =
+ // Convertor.constructFieldsForRespValidation(resourceDetails,
+ // resourceVersion);
+ // ArrayList<String> artifacts = new ArrayList<String>();
+ //
+ // artifacts.add(resourceId+":"+artifactName1);
+ // artifacts.add(resourceId+":"+artifactName2);
+ // resourceRespJavaObject.setArtifacts(artifacts);
+ // ArrayList<String> interfaces = new ArrayList<String>();
+ //
+ // interfaces.add(interfaze);
+ // resourceRespJavaObject.setInterfaces(interfaces);
+ //
+ // // checkIn resource
+ // resourceVersion = resourceDetails.getVersion();
+ // String checkinComment = "good checkin";
+ // String checkinComentJson = "{\"userRemarks\": \""+checkinComment+"\"}";
+ // RestResponse checkInResponse =
+ // LifecycleRestUtils.changeResourceState(resourceDetails,
+ // sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CHECKIN,
+ // checkinComentJson);
+ // assertNotNull("check response object is not null after import resource",
+ // checkInResponse);
+ // assertEquals("Check response code after checkout resource", 200,
+ // checkInResponse.getErrorCode().intValue());
+ //
+ // // req4cert resource
+ // RestResponse request4cert =
+ // LifecycleRestUtils.changeResourceState(resourceDetails,
+ // sdncModifierDetails, resourceVersion,
+ // LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ // assertNotNull("check response object is not null after resource request
+ // for certification", request4cert);
+ // assertEquals("Check response code after checkout resource", 200,
+ // request4cert.getErrorCode().intValue());
+ // resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ // assertNotNull(resourceFromGet);
+ // resourceDetails =
+ // ResponseParser.parseToObject(request4cert.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceDetails.setVersion(resourceFromGet.getVersion());
+ //
+ // // startCert
+ // RestResponse startCert =
+ // LifecycleRestUtils.changeResourceState(resourceDetails,
+ // sdncModifierDetails, resourceVersion,
+ // LifeCycleStatesEnum.STARTCERTIFICATION);
+ // assertNotNull("check response object is not null after resource request
+ // start certification", startCert);
+ // assertEquals("Check response code after checkout resource", 200,
+ // startCert.getErrorCode().intValue());
+ // resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(startCert.getResponse());
+ // assertNotNull(resourceFromGet);
+ // resourceDetails = ResponseParser.parseToObject(startCert.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceDetails.setVersion(resourceFromGet.getVersion());
+ //
+ // // certify
+ // RestResponse certify =
+ // LifecycleRestUtils.changeResourceState(resourceDetails,
+ // sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFY);
+ // assertNotNull("check response object is not null after resource request
+ // certify", certify);
+ // assertEquals("Check response code after certify resource", 200,
+ // certify.getErrorCode().intValue());
+ // resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(certify.getResponse());
+ // assertNotNull(resourceFromGet);
+ // resourceDetails = ResponseParser.parseToObject(certify.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceDetails.setVersion(resourceFromGet.getVersion());
+ //
+ // // clean audit
+ // DbUtils.cleanAllAudits();
+ //
+ // // change resource details
+ // resourceDetails =
+ // ElementFactory.getDefaultResource("tosca.nodes.importResource4test",
+ // NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS,
+ // "jh0003");
+ //
+ // // import new resource while resource already exist in other state
+ // importResponse =
+ // ImportRestUtils.importNewResourceByName("importResource4testUpdateWithoutReqCap",
+ // UserRoleEnum.ADMIN);
+ // assertNotNull("check response object is not null after import resource",
+ // importResponse);
+ // assertNotNull("check error code exists in response after import
+ // resource", importResponse.getErrorCode());
+ // assertEquals("Check response code after import resource", 200,
+ // importResponse.getErrorCode().intValue());
+ // resourceDetails =
+ // ResponseParser.parseToObject(importResponse.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceVersion = resourceDetails.getVersion();
+ // resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ // resourceDetails.getUniqueId());
+ // assertEquals("Check response code after get resource", 200,
+ // resourceGetResponse.getErrorCode().intValue());
+ // resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ // assertNotNull(resourceFromGet);
+ //
+ // // validate response
+ // Resource resourceFromImport =
+ // ResponseParser.convertResourceResponseToJavaObject(importResponse.getResponse());
+ // assertNotNull(resourceFromImport);
+ //
+ // resourceDetails =
+ // ResponseParser.parseToObject(importResponse.getResponse(),
+ // ResourceReqDetails.class);
+ // resourceRespJavaObject =
+ // Convertor.constructFieldsForRespValidation(resourceDetails);
+ // resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ //
+ // // validate get response
+ // resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ // resourceRespJavaObject.getUniqueId());
+ // resourceFromGet =
+ // ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ // assertNotNull(resourceFromGet);
+ //
+ // // validate
+ // ResourceValidationUtils.validateModelObjects(resourceFromImport,
+ // resourceFromGet);
+ //
+ // // validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // Convertor.constructFieldsForAuditValidation(resourceDetails,
+ // resourceVersion);
+ // auditAction="ResourceImport";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setPrevVersion(resourceVersion);
+ // expectedResourceAuditJavaObject.setStatus("200");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // expectedResourceAuditJavaObject.setToscaNodeType(resourceFromGet.getToscaResourceName());
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ // }
+
+ @Test
+ public void importNewResource_uuidTest() throws Exception {
+ RestResponse importResponse = importNewResource(UserRoleEnum.ADMIN);
+
+ assertNotNull("check response object is not null after import resource", importResponse);
+ assertNotNull("check error code exists in response after import resource", importResponse.getErrorCode());
+ assertEquals("Check response code after import resource", 201, importResponse.getErrorCode().intValue());
+ String oldUuid = ResponseParser.getValueFromJsonResponse(importResponse.getResponse(), "uuid");
+
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceVersion = resourceDetails.getVersion();
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ Resource resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ // add mandatory artifacts
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // resourceGetResponse);
+ resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertEquals("Check response code after get resource", 200, resourceGetResponse.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(importResponse.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, "0.1", LifeCycleStatesEnum.CHECKIN);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ String newUuid = ResponseParser.getValueFromJsonResponse(checkInResponse.getResponse(), "uuid");
+ assertTrue(ResourceValidationUtils.validateUuidAfterChangingStatus(oldUuid, newUuid));
+
+ // req4cert resource
+ RestResponse request4cert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertNotNull("check response object is not null after resource request for certification", request4cert);
+ assertEquals("Check response code after checkout resource", 200, request4cert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(request4cert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(request4cert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ String newUuid2 = ResponseParser.getValueFromJsonResponse(request4cert.getResponse(), "uuid");
+ assertTrue(ResourceValidationUtils.validateUuidAfterChangingStatus(oldUuid, newUuid2));
+
+ // startCert
+ RestResponse startCert = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertNotNull("check response object is not null after resource request start certification", startCert);
+ assertEquals("Check response code after checkout resource", 200, startCert.getErrorCode().intValue());
+ resourceFromGet = ResponseParser.convertResourceResponseToJavaObject(startCert.getResponse());
+ assertNotNull(resourceFromGet);
+ resourceDetails = ResponseParser.parseToObject(startCert.getResponse(), ResourceReqDetails.class);
+ resourceDetails.setVersion(resourceFromGet.getVersion());
+
+ String newUuid3 = ResponseParser.getValueFromJsonResponse(startCert.getResponse(), "uuid");
+ assertTrue(ResourceValidationUtils.validateUuidAfterChangingStatus(oldUuid, newUuid3));
+
+ RestResponse certify = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, "0.1", LifeCycleStatesEnum.CERTIFY);
+ assertNotNull("check response object is not null after import resource", certify);
+ assertEquals("Check response code after checkout resource", 200, certify.getErrorCode().intValue());
+
+ String newUuid4 = ResponseParser.getValueFromJsonResponse(certify.getResponse(), "uuid");
+ assertTrue(ResourceValidationUtils.validateUuidAfterChangingStatus(oldUuid, newUuid4));
+
+ RestResponse checkoutResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, "1.0", LifeCycleStatesEnum.CHECKOUT);
+ assertNotNull("check response object is not null after import resource", checkInResponse);
+ assertEquals("Check response code after checkout resource", 200, checkInResponse.getErrorCode().intValue());
+
+ String newUuid5 = ResponseParser.getValueFromJsonResponse(checkoutResponse.getResponse(), "uuid");
+ assertFalse(ResourceValidationUtils.validateUuidAfterChangingStatus(oldUuid, newUuid5));
+ }
+
+ @Test
+ public void importNewResource_propertiesMapInternalUrlCredential() throws Exception {
+ String folderName = "validateProporties_typeMap_valueUrlCredential";
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName(folderName, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(importResponse.getResponse(), Resource.class);
+
+ List<PropertyDefinition> properties = resource.getProperties();
+ assertEquals("check properties size", 3, properties.size());
+
+ PropertyDefinition propertyDefinition = properties.stream().filter(p -> p.getName().equals("validation_test")).findFirst().get();
+ String defaultValue = propertyDefinition.getDefaultValue();
+
+ Map mapValue = gson.fromJson(defaultValue, Map.class);
+ assertEquals("check Map value size", 2, mapValue.size());
+ checkMapValues(mapValue, "key", 1, null);
+ checkMapValues(mapValue, "key", 2, null);
+
+ System.err.println("import Resource " + "<" + folderName + ">" + "response: " + importResponse.getErrorCode());
+
+ }
+
+ @Test
+ public void importNewResource_propertiesListInternalUrlCredential() throws Exception {
+ String folderName = "validateProporties_typeList_valueUrlCredential";
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName(folderName, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(importResponse.getResponse(), Resource.class);
+
+ List<PropertyDefinition> properties = resource.getProperties();
+ assertEquals("check properties size", 3, properties.size());
+
+ PropertyDefinition propertyDefinition = properties.stream().filter(p -> p.getName().equals("validation_test")).findFirst().get();
+ String defaultValue = propertyDefinition.getDefaultValue();
+
+ List listValue = gson.fromJson(defaultValue, List.class);
+ assertEquals("check List value size", 2, listValue.size());
+ checkListValues(listValue.get(0), 1, SPECIAL_CHARACTERS);
+ checkListValues(listValue.get(1), 2, SPECIAL_CHARACTERS);
+
+ // Verify attributes
+ List<AttributeDefinition> attributes = resource.getAttributes();
+
+ assertEquals("check properties size", 2, attributes.size());
+
+ // Verify attribute from type map
+ AttributeDefinition attributeMapDefinition = attributes.stream().filter(p -> p.getName().equals("validation_test_map")).findFirst().get();
+ String defaultMapValue = attributeMapDefinition.getDefaultValue();
+ Map attributeMapValue = gson.fromJson(defaultMapValue, Map.class);
+ assertEquals("check Map value size", 2, attributeMapValue.size());
+ checkMapValues(attributeMapValue, "key", 1, SPECIAL_CHARACTERS);
+ checkMapValues(attributeMapValue, "key", 2, SPECIAL_CHARACTERS);
+
+ // Verify attribute from type list
+ AttributeDefinition attributeListDefinition = attributes.stream().filter(p -> p.getName().equals("validation_test_list")).findFirst().get();
+ String defaultListValue = attributeListDefinition.getDefaultValue();
+
+ List attributeListValue = gson.fromJson(defaultListValue, List.class);
+ assertEquals("check List value size", 2, attributeListValue.size());
+ checkListValues(attributeListValue.get(0), 1, SPECIAL_CHARACTERS);
+ checkListValues(attributeListValue.get(1), 2, SPECIAL_CHARACTERS);
+
+ System.err.println("import Resource " + "<" + folderName + ">" + "response: " + importResponse.getErrorCode());
+
+ }
+
+ private void checkListValues(Object object, int index, String suffix) {
+
+ Map map = (Map) object;
+ assertEquals("check Map protocol value", "protocol" + index + (suffix == null ? "" : suffix), map.get("protocol"));
+ assertEquals("check Map token value", "token" + index, map.get("token"));
+ }
+
+ // @Test
+ public void importNewResource_validateProporties_typeTestDataType() throws Exception {
+ String folderName = "validateProporties_typeTestDataType";
+ RestResponse importResponse = ImportRestUtils.importNewResourceByName(folderName, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(importResponse.getResponse(), Resource.class);
+
+ }
+
+ private void checkMapValues(Map mapValue, String key, int index, String suffix) {
+
+ Map map1 = (Map) mapValue.get(key + index);
+ assertEquals("check Map protocol value", "protocol" + index + (suffix == null ? "" : suffix), map1.get("protocol"));
+ assertEquals("check Map token value", "token" + index, map1.get("token"));
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaCapabilitiesWithProperties.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaCapabilitiesWithProperties.java
new file mode 100644
index 0000000000..3d7c81abae
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaCapabilitiesWithProperties.java
@@ -0,0 +1,416 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+/**
+ * US US730518 Story [BE] - TOSCA capabilities with properties - import "As a
+ * resource designer, I would like to add my VFC capabilities with properties."
+ *
+ * @author ns019t
+ *
+ */
+public class ImportToscaCapabilitiesWithProperties extends ComponentBaseTest {
+ @Rule
+ public static TestName name = new TestName();
+
+ Gson gson = new Gson();
+
+ /**
+ * public Constructor ImportToscaCapabilitiesWithProperties
+ */
+ public ImportToscaCapabilitiesWithProperties() {
+ super(name, ImportToscaCapabilitiesWithProperties.class.getName());
+ }
+
+ /**
+ * String constants
+ */
+ public static String propertyForTestName = "propertyfortest";
+ public static String rootPath = System.getProperty("user.dir");
+ public static String scalable = "tosca.capabilities.Scalable";
+ public static String container = "tosca.capabilities.Container";
+ public static String minInstances = "min_instances";
+ public static String userDefinedNodeYaml = "mycompute.yml";
+
+ /**
+ * Capability Type - capability type on the graph should already have
+ * properties modeled on it. please verify. The import of the capability
+ * types should support adding those properties. when importing, validate
+ * name uniqueness between the capability's properties see capability
+ * tosca.capabilities.Container
+ *
+ * Acceptance Criteria: validate capability type properties (for example,
+ * compute have capability Container -> the properties of this capability
+ * should be in the Json response)
+ *
+ * @throws IOException
+ */
+ @Test
+ public void validateCapabilityTypePropertiesSucceed() throws IOException {
+ User user = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse createResourceRes = ResourceRestUtils.getResourceByNameAndVersion(user.getUserId(), "Compute",
+ "1.0");
+ BaseRestUtils.checkSuccess(createResourceRes);
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(createResourceRes.getResponse());
+ Map<String, List<CapabilityDefinition>> capabilities = resource.getCapabilities();
+ assertEquals(capabilities.size(), 6);
+
+ CapabilityDefinition capability = capabilities.get(scalable).get(0);
+ List<ComponentInstanceProperty> properties = capability.getProperties();
+ assertEquals(properties.size(), 3);
+ assertTrue(!properties.stream().filter(p -> p.getName().equalsIgnoreCase(propertyForTestName)).findAny()
+ .isPresent());
+
+ ComponentInstanceProperty originalProperty = properties.stream()
+ .filter(p -> p.getName().equalsIgnoreCase(minInstances)).findAny().get();
+ assertEquals(originalProperty.getType(), "integer");
+ assertEquals(originalProperty.getDefaultValue(), "1");
+
+ capability = capabilities.get(container).get(0);
+ properties = capability.getProperties();
+ assertEquals(properties.size(), 4);
+ }
+
+ /**
+ * Capability Definition on VFC / CP / VL - properties can also be defined
+ * on the capability when the capability is declared. (property definition
+ * with default value) If the property name (case insensitive) already
+ * defined on the capability type, it overrides the capability from the
+ * capability type Import of VFC / CP /VL should support adding properties
+ * to the capability. when importing, validate name uniqueness between the
+ * capability's properties
+ *
+ * Acceptance Criteria: import node type with capability definition on it.
+ * use the attached "myCompute"
+ *
+ * @throws Exception
+ */
+ @Test
+ public void importNodeTypeWithCapabilityWithPropertiesFromYmlSucceed() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ RestResponse createResource = importUserDefinedNodeType(userDefinedNodeYaml, sdncModifierDetails,
+ resourceDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ Map<String, List<CapabilityDefinition>> capabilities = resource.getCapabilities();
+ assertEquals(capabilities.size(), 6);
+
+ CapabilityDefinition capability = capabilities.get(scalable).get(0);
+ List<ComponentInstanceProperty> properties = capability.getProperties();
+ assertEquals(properties.size(), 4);
+
+ ComponentInstanceProperty newProperty = properties.stream()
+ .filter(p -> p.getName().equalsIgnoreCase(propertyForTestName)).findAny().get();
+ assertEquals(newProperty.getType(), "string");
+ assertEquals(newProperty.getDescription(), "test");
+ assertEquals(newProperty.getDefaultValue(), "success");
+
+ ComponentInstanceProperty overriddenProperty = properties.stream()
+ .filter(p -> p.getName().equalsIgnoreCase(minInstances)).collect(Collectors.toList()).get(0);
+ assertEquals(overriddenProperty.getType(), "integer");
+ assertEquals(overriddenProperty.getDefaultValue(), "3");
+
+ }
+
+ /**
+ * importNodeTypeWithCapabilityWithPropertiesFromYmlFailed
+ *
+ * @throws Exception
+ */
+ @Test
+ public void importNodeTypeWithCapabilityWithPropertiesFromYmlFailed() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ RestResponse createResource = importUserDefinedNodeType("mycompute_failed.yml", sdncModifierDetails,
+ resourceDetails);
+ BaseRestUtils.checkErrorMessageResponse(createResource, ActionStatus.PROPERTY_NAME_ALREADY_EXISTS);
+ }
+
+ /**
+ * Capability Assignment (on node_template / resource instance) - should
+ * support assignment of the property (property value). On the resource
+ * instance level, value can be assigned to either properties that are
+ * defined on the capability type or on the capability definition. When
+ * importing a VF - the node_template can have capability's property value.
+ * It should be imported and saved on the graph Acceptance Criteria: import
+ * a VF that assign values to property of capability that was defined on the
+ * capability type
+ *
+ * @throws Exception
+ */
+ @Test
+ public void importResourceWithCapabilityWithPropertiesOverridingCapTypePropertiesSucceed() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "vf_with_cap_prop_override_cap_type_prop.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ Path path = Paths.get(rootPath + "/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop.csar");
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ List<ImmutablePair<String, String>> propertyNamesValues = new ArrayList<>();
+ propertyNamesValues.add(new ImmutablePair<String, String>("num_cpus", "2"));
+ propertyNamesValues.add(new ImmutablePair<String, String>("mem_size", "2000 MB"));
+ checkResource(createResource, 8, container, "DBMS", propertyNamesValues);
+
+ ResourceReqDetails resourceDetails2 = ElementFactory.getDefaultResource();
+ resourceDetails2.setCsarUUID("vf_with_cap_prop_override_cap_type_prop.csar");
+ resourceDetails2.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ checkResource(createResource, 8, container, "DBMS", propertyNamesValues);
+ }
+
+ /**
+ * importResourceWithCapabilityWithPropertiesOverridingCapTypePropertiesFailed
+ *
+ * @throws Exception
+ */
+ @Test
+ public void importResourceWithCapabilityWithPropertiesOverridingCapTypePropertiesFailed() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String payloadName = "vf_with_cap_prop_override_cap_type_prop_failed.csar";
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ Path path = Paths
+ .get(rootPath + "/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop_failed.csar");
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkErrorMessageResponse(createResource, ActionStatus.INVALID_PROPERTY);
+
+ ResourceReqDetails resourceDetails2 = ElementFactory.getDefaultResource();
+ resourceDetails2.setCsarUUID("vf_with_cap_prop_override_cap_type_prop_failed.csar");
+ resourceDetails2.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+ BaseRestUtils.checkErrorMessageResponse(createResource, ActionStatus.INVALID_PROPERTY);
+
+ }
+
+ /**
+ * Capability Assignment (on node_template / resource instance) - should
+ * support assignment of the property (property value). On the resource
+ * instance level, value can be assigned to either properties that are
+ * defined on the capability type or on the capability definition. When
+ * importing a VF - the node_template can have capability's property value.
+ * It should be imported and saved on the graph Acceptance Criteria: import
+ * a VF that assign values to property of capability that was defined on the
+ * capability definition (on the node type)
+ *
+ * @throws Exception
+ */
+ @Test
+ public void importResourceWithCapabilityWithPropertiesOverridingNodeTypeCapPropertiesSucceed() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ RestResponse createResource = importUserDefinedNodeType(userDefinedNodeYaml, sdncModifierDetails,
+ resourceDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource userDefinedNodeType = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(),
+ Resource.class);
+
+ String payloadName = "vf_with_cap_prop_override_cap_type_prop1.csar";
+ resourceDetails = ElementFactory.getDefaultImportResource();
+ Path path = Paths.get(rootPath + "/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1.csar");
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ List<ImmutablePair<String, String>> propertyNamesValues = new ArrayList<>();
+ propertyNamesValues.add(new ImmutablePair<String, String>("num_cpus", "2"));
+ propertyNamesValues.add(new ImmutablePair<String, String>("mem_size", "2000 MB"));
+ checkResource(createResource, 8, container, "DBMS", propertyNamesValues);
+
+ List<ImmutablePair<String, String>> propertyNamesValues1 = new ArrayList<>();
+ propertyNamesValues1.add(new ImmutablePair<String, String>(propertyForTestName, "success_again"));
+ propertyNamesValues1.add(new ImmutablePair<String, String>(minInstances, "4"));
+ checkResource(createResource, 8, scalable, userDefinedNodeType.getName(), propertyNamesValues1);
+
+ ResourceReqDetails resourceDetails2 = ElementFactory.getDefaultResource();
+ resourceDetails2.setCsarUUID("vf_with_cap_prop_override_cap_type_prop1.csar");
+ resourceDetails2.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ checkResource(createResource, 8, container, "DBMS", propertyNamesValues);
+ checkResource(createResource, 8, scalable, userDefinedNodeType.getName(), propertyNamesValues1);
+
+ }
+
+ /**
+ * importResourceWithCapabilityWithPropertiesOverridingNodeTypeCapPropertiesFailed
+ *
+ * @throws Exception
+ */
+ @Test
+ public void importResourceWithCapabilityWithPropertiesOverridingNodeTypeCapPropertiesFailed() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ RestResponse createResource = importUserDefinedNodeType(userDefinedNodeYaml, sdncModifierDetails,
+ resourceDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+
+ String payloadName = "vf_with_cap_prop_override_cap_type_prop1_failed.csar";
+ resourceDetails = ElementFactory.getDefaultImportResource();
+ Path path = Paths
+ .get(rootPath + "/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1_failed.csar");
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.PROPERTY_NAME_ALREADY_EXISTS,
+ propertyForTestName);
+
+ ResourceReqDetails resourceDetails2 = ElementFactory.getDefaultResource();
+ resourceDetails2.setCsarUUID("vf_with_cap_prop_override_cap_type_prop1_failed.csar");
+ resourceDetails2.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+ BaseRestUtils.checkErrorResponse(createResource, ActionStatus.PROPERTY_NAME_ALREADY_EXISTS,
+ propertyForTestName);
+ }
+
+ private RestResponse importUserDefinedNodeType(String payloadName, User sdncModifierDetails,
+ ImportReqDetails resourceDetails) throws Exception {
+
+ Path path = Paths.get(rootPath + "/src/test/resources/CI/csars/" + payloadName);
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VFC.name());
+ return ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ }
+
+ // TODO Tal: Since Cashing change partial resource returned that causes null
+ // pointer exception
+ // commented out till fixing
+ private void checkResource(RestResponse createResource, int capNum, String capType, String riName,
+ List<ImmutablePair<String, String>> propertyNamesValues) {
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ Map<String, List<CapabilityDefinition>> capabilities = resource.getCapabilities();
+ // TODO Tal: Since Cashing change partial resource returned that causes
+ // null pointer exception
+ /* assertEquals(capabilities.size(), capNum); */
+ /*
+ * List<CapabilityDefinition> capabilitesContainer =
+ * capabilities.get(capType);
+ */
+
+ ComponentInstance resourceRI = resource.getComponentInstances().stream()
+ .filter(ri -> ri.getComponentName().equals(riName)).collect(Collectors.toList()).get(0);
+ // TODO Tal: Since Cashing change partial resource returned that causes
+ // null pointer exception
+ /*
+ * CapabilityDefinition capabilityFromContainer =
+ * capabilitesContainer.stream()
+ * .filter(cap->cap.getOwnerId().equals(resourceRI.getUniqueId())).
+ * collect(Collectors.toList()).get(0);
+ */
+
+ CapabilityDefinition capabilityFromRI = resourceRI.getCapabilities().get(capType).get(0);
+ for (ImmutablePair<String, String> propValuePair : propertyNamesValues) {
+ // TODO Tal: Since Cashing change partial resource returned that
+ // causes null pointer exception
+ /*
+ * Map<String, ComponentInstanceProperty> propertiesFromContainer =
+ * capabilityFromContainer.getProperties()
+ * .stream().filter(p->p.getName().equalsIgnoreCase(propValuePair.
+ * getLeft())) .collect(Collectors.toMap(p->p.getName(), p->p));
+ */
+
+ List<ComponentInstanceProperty> propertiesFromRI = capabilityFromRI.getProperties().stream()
+ .filter(p -> p.getName().equalsIgnoreCase(propValuePair.getLeft())).collect(Collectors.toList());
+ // TODO Tal: Since Cashing change partial resource returned that
+ // causes null pointer exception
+ /*
+ * for(ComponentInstanceProperty riProp : propertiesFromRI){
+ * assertTrue(propertiesFromContainer.containsKey(riProp.getName()))
+ * ; ComponentInstanceProperty containerProp =
+ * propertiesFromContainer.get(riProp.getName());
+ * assertEquals(riProp.getValue(), containerProp.getValue());
+ * if(riProp.getName().equals(propValuePair.getLeft()))
+ * assertEquals(riProp.getValue(), propValuePair.getRight());
+ *
+ * }
+ */
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaResourceTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaResourceTest.java
new file mode 100644
index 0000000000..b78bf3de36
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportToscaResourceTest.java
@@ -0,0 +1,2404 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_CREATED;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.client.ClientProtocolException;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.general.ImportUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.api.ToscaNodeTypeInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Andrey + Pavel + Shay
+ *
+ */
+
+public class ImportToscaResourceTest extends ComponentBaseTest {
+ private static Logger logger = LoggerFactory.getLogger(ImportToscaResourceTest.class.getName());
+ protected Utils utils = new Utils();
+
+ public ImportToscaResourceTest() {
+ super(name, ImportToscaResourceTest.class.getName());
+ }
+
+ public ImportReqDetails importReqDetails;
+ protected static User sdncUserDetails;
+ protected static User testerUser;
+ protected String testResourcesPath;
+ protected ResourceReqDetails resourceDetails;
+ private HashSet<String> capabilitySources;
+ private int actualNumOfReqOrCap;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ @BeforeMethod
+ public void before() throws Exception {
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ testerUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ resourceDetails = ElementFactory.getDefaultResource();
+ String sourceDir = config.getResourceConfigDir();
+ final String workDir = "importToscaResourceByCreateUrl";
+ testResourcesPath = sourceDir + File.separator + workDir;
+ capabilitySources = new HashSet<String>();
+ actualNumOfReqOrCap = 0;
+ }
+
+ @DataProvider
+ private static final Object[][] getYmlWithInValidListProperties() throws IOException, Exception {
+ return new Object[][] { { "ListPropertyFalure02.yml", "[false,\"truee\"]", "boolean" }, { "ListPropertyFalure03.yml", "[false,3]", "boolean" }, { "ListPropertyFalure04.yml", "[false,3.56]", "boolean" },
+ { "ListPropertyFalure05.yml", "[10000,3.56]", "integer" }, { "ListPropertyFalure06.yml", "[10000,\"aaaa\"]", "integer" }, { "ListPropertyFalure07.yml", "[10000,true]", "integer" },
+ { "ListPropertyFalure08.yml", "[10.5,true]", "float" }, { "ListPropertyFalure09.yml", "[10.5,\"asdc\"]", "float" }, // type
+ // float
+ { "ListPropertyFalure11.yml", "[10.5,\"500.0@\"]", "float" }, // property
+ // list
+ // float
+ // type
+ // contain
+ // @
+ // in
+ // default
+ // value
+ { "ListPropertyFalure12.yml", "[10000,\"3#\"]", "integer" }, // property
+ // list
+ // integer
+ // type
+ // contain
+ // #
+ // in
+ // default
+ // value
+ { "ListPropertyFalure13.yml", "[false,\"true%\"]", "boolean" }, // property
+ // list
+ // boolean
+ // type
+ // contain
+ // %
+ // in
+ // default
+ // value
+ { "ListPropertyFalure14.yml", "[false,\"falsee\",true]", "boolean" }, { "ListPropertyFalure15.yml", "[10.5,\"10.6x\",20.5,30.5]", "float" } // float
+ // with
+ // value
+ // 10.6x
+ // instead
+ // 10.6f
+
+ };
+ }
+
+ @DataProvider
+ private static final Object[][] getYmlWithInValidMapProperties() throws IOException, Exception {
+ return new Object[][] { { "MapPropertyFalure02.yml", "[false,\"truee\"]", "boolean" }, { "MapPropertyFalure03.yml", "[false,3]", "boolean" }, { "MapPropertyFalure04.yml", "[false,3.56]", "boolean" },
+ { "MapPropertyFalure05.yml", "[10000,3.56]", "integer" }, { "MapPropertyFalure06.yml", "[10000,\"aaaa\"]", "integer" }, { "MapPropertyFalure07.yml", "[10000,true]", "integer" }, { "MapPropertyFalure08.yml", "[10.5,true]", "float" },
+ { "MapPropertyFalure09.yml", "[10.5,\"asdc\"]", "float" }, // type
+ // float
+ { "MapPropertyFalure11.yml", "[10.5,\"500.0@\"]", "float" }, // property
+ // list
+ // float
+ // type
+ // contain
+ // @
+ // in
+ // default
+ // value
+ { "MapPropertyFalure12.yml", "[10000,\"3#\"]", "integer" }, // property
+ // list
+ // integer
+ // type
+ // contain
+ // #
+ // in
+ // default
+ // value
+ { "MapPropertyFalure13.yml", "[false,\"true%\"]", "boolean" }, // property
+ // list
+ // boolean
+ // type
+ // contain
+ // %
+ // in
+ // default
+ // value
+ { "MapPropertyFalure14.yml", "[false,\"falsee\",true]", "boolean" }, { "MapPropertyFalure15.yml", "[10.5,\"10.6x\",20.5,30.5]", "float" } // float
+ // with
+ // value
+ // 10.6x
+ // instead
+ // 10.6f
+
+ };
+ }
+
+ @DataProvider
+ private static final Object[][] getYmlWithInValidOccurrences() throws IOException, Exception {
+ return new Object[][] { { "occurencyFalure01.yml" }, // requirements [2
+ // , 0]
+ { "occurencyFalure02.yml" }, // requirements [-1, 2]
+ { "occurencyFalure03.yml" }, // requirements [1 ,-2]
+ { "occurencyFalure05.yml" }, // requirements MAX occurrences not
+ // exist [ 1 , ]
+ { "occurencyFalure06.yml" }, // requirements [ 0 , 0 ]
+ { "occurencyFalure08.yml" }, // requirements [ 1.0 , 2.0 ]
+ { "occurencyFalure09.yml" }, // requirements [ "1" , "2" ]
+ { "occurencyFalure10.yml" }, // requirements [ ]
+ { "occurencyFalure11.yml" }, // requirements [ UNBOUNDED ,
+ // UNBOUNDED ]
+ { "occurencyFalure31.yml" }, // capability [ 2, 1]
+ { "occurencyFalure32.yml" }, // capability [-1, 2]
+ { "occurencyFalure33.yml" }, // capability [1, -2]
+ { "occurencyFalure35.yml" }, // capability MAX occurrences not
+ // exist [ 1 , ]
+ { "occurencyFalure36.yml" }, // capability [ 0 , 0 ]
+ { "occurencyFalure38.yml" }, // capability [ 1.0 , 2.0 ]
+ { "occurencyFalure39.yml" }, // capability [ "1" , "2" ]
+ { "occurencyFalure40.yml" }, // capability [ ]
+ { "occurencyFalure41.yml" } // capability [ UNBOUNDED ,
+ // UNBOUNDED ]
+ };
+ }
+
+ @DataProvider
+ private static final Object[][] getInvalidYmlWithOccurrences() throws IOException, Exception {
+ return new Object[][] { { "occurencyFalure04.yml" }, // requirements MIN
+ // occurrences
+ // not exist [ ,
+ // 1]
+ { "occurencyFalure07.yml" }, // requirements [ @ , 1 ]
+ { "occurencyFalure34.yml" }, // capability MIN occurrences not
+ // exist [ , 1]
+ { "occurencyFalure37.yml" } // capability [ 0 , # ]
+
+ };
+ }
+
+ // US656928
+ protected final String importMapPropertySuccess = "importMapPropertySuccessFlow.yml";
+ protected final String importAttributeSuccess = "importAttributeSuccessFlow.yml";
+ protected final String importSuccessFile = "myCompute.yml";
+ protected final String derivedFromMyCompute = "derivedFromMyCompute.yml";
+ protected final String importSuccessVFFile = "myComputeVF.yml";
+ protected final String importNoDerivedFromFile = "myComputeDerivedFromNotExists.yml";
+ protected final String importInvalidDefinitionVersionFile = "myComputeIncorrectDefenitionVersionValue.yml";
+ protected final String importIncorrectNameSpaceFormatFile = "myComputeIncorrectNameSpaceFormat.yml";
+ protected final String importNoDefenitionVersionFile = "myComputeNoDefenitionVersion.yml";
+ protected final String importNodeTypesTwiceFile = "myComputeWithNodeTypesTwice.yml";
+ protected final String importTopologyTemplateFile = "myComputeWithTopologyTemplate.yml";
+ protected final String importNoContentFile = "noContent.yml";
+ protected final String importWithOccurrences = "myComputeOccurencySuccess.yml";
+ protected final String importListPropertyBadDefault = "importListPropertyBadDefault.yml";
+ protected final String importListPropertyGoodDefault = "importListPropertyGoodDefault.yml";
+ protected final String importListPropertySuccess = "importListPropertySuccessFlow.yml";
+ // US631462
+ protected final String importDuplicateRequirements = "importDuplicateRequirements.yml";
+ protected final String importDuplicateCapability = "importDuplicateCapability.yml";
+ protected final String importCapabilityNameExistsOnParent = "importCapabilityNameExistsOnParent.yml";
+ protected final String importRequirementNameExistsOnParent = "importRequirementNameExistsOnParent.yml";
+ protected final String importToscaResourceReqCapDerivedFromParent = "derivedFromWebAppDerivedReqCap.yml";
+ protected final String missingCapInReqDef = "missingCapInReqDefinition.yml";
+ protected final String missingCapInCapDef = "missingCapInCapDefinition.yml";
+
+ // US558432 - Support for Capability/Requirement "occurences" Import
+ @Test(dataProvider = "getYmlWithInValidOccurrences")
+ public void importToscaResourceWithOccurrencesFailuresFlow01(String ymlFileWithInvalidCapReqOccurrences) throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFileWithInvalidCapReqOccurrences);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_OCCURRENCES.name(), new ArrayList<String>(), importResourceResponse.getResponse());
+ }
+
+ @Test(dataProvider = "getInvalidYmlWithOccurrences")
+ public void importToscaResourceWithOccurrencesFailuresFlow02(String ymlFileWithInvalidCapReqOccurrences) throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFileWithInvalidCapReqOccurrences);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_YAML_FILE.name(), new ArrayList<String>(), importResourceResponse.getResponse());
+ }
+
+ @Test
+ public void importToscaResource() throws Exception {
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importSuccessFile);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+ AssertJUnit.assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ AssertJUnit.assertTrue("validate toscaResourceName field", resourceJavaObject.getToscaResourceName().equals(parseToscaNodeYaml.getNodeName()));
+ AssertJUnit.assertTrue("validate resourceType field", resourceJavaObject.getResourceType().equals(ResourceTypeEnum.VFC));
+ // find derived from resource details
+ // Validate resource details after import-create resource including
+ // capabilities, interfaces from derived_from resource
+
+ // Validate audit message
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceWithOccurrencesSuccessFlow() throws Exception {
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importWithOccurrences);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+ AssertJUnit.assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ AssertJUnit.assertTrue("validate toscaResourceName field", resourceJavaObject.getToscaResourceName().equals(parseToscaNodeYaml.getNodeName()));
+ AssertJUnit.assertTrue("validate resourceType field", resourceJavaObject.getResourceType().equals(ResourceTypeEnum.VFC));
+ String requirementsType = "tosca.capabilities.Attachment";
+ String capabilitType = "tosca.capabilities.Endpoint.Admin";
+ // Verify Occurrences of requirements and capabilities in resource
+ verifyRequirementsOccurrences(resourceJavaObject, requirementsType);
+ verifyCapabilitiesOccurrences(resourceJavaObject, capabilitType);
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ // ------------------------------Success---------------------------------
+
+ @Test(enabled = false)
+ public void importToscaResourceVFResType() throws Exception {
+
+ String resourceType = ResourceTypeEnum.VF.toString();
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importSuccessVFFile);
+ // importReqDetails.setResourceType(resourceType);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+ assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ assertTrue("validate toscaResourceName field", resourceJavaObject.getToscaResourceName().equals(parseToscaNodeYaml.getNodeName()));
+ assertTrue("validate resourceType field, expected - " + resourceType + ", actual - " + resourceJavaObject.getResourceType(), resourceJavaObject.getResourceType().toString().equals(resourceType));
+
+ // Validate audit message
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ // ------------------------------Failure---------------------------------
+
+ @Test
+ public void importToscaResourceDerivedFromNotExist() throws Exception {
+
+ String fileName = importNoDerivedFromFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ // List<String> derivedFrom = new ArrayList<String>() ;
+ // derivedFrom.add("hh");
+ // importReqDetails.setDerivedFrom(derivedFrom);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.PARENT_RESOURCE_NOT_FOUND.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PARENT_RESOURCE_NOT_FOUND.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceIncorrectDefinitionVersion() throws Exception {
+
+ String fileName = importInvalidDefinitionVersionFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_TOSCA_TEMPLATE.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_TOSCA_TEMPLATE.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceIncorrectSpaceNameFormat() throws Exception {
+
+ String fileName = importIncorrectNameSpaceFormatFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_RESOURCE_NAMESPACE.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_RESOURCE_NAMESPACE.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceNoDefinitionVersion() throws Exception {
+
+ String fileName = importNoDefenitionVersionFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_TOSCA_TEMPLATE.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_TOSCA_TEMPLATE.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceNoContent() throws Exception {
+
+ String fileName = importNoContentFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_RESOURCE_PAYLOAD.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_RESOURCE_PAYLOAD.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceWithTopologyTemplate() throws Exception {
+
+ String fileName = importTopologyTemplateFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.NOT_RESOURCE_TOSCA_TEMPLATE.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_RESOURCE_TOSCA_TEMPLATE.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceWithNodeTypesTwice() throws Exception {
+
+ String fileName = importNodeTypesTwiceFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.NOT_SINGLE_RESOURCE.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_SINGLE_RESOURCE.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ // failed case - uniqueness of toscaResourceName - RESOURCE_ALREADY_EXISTS
+ @Test
+ public void importToscaResourceTwice() throws Exception {
+ String fileName = importSuccessFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+ assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ RestResponse checkInresponse = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKIN);
+ assertTrue("checkIn resource request returned status:" + checkInresponse.getErrorCode(), checkInresponse.getErrorCode() == 200);
+
+ // Validate audit message
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+
+ // import the same tosca resource with different resourceName
+ DbUtils.cleanAllAudits();
+
+ importReqDetails.setName("kuku");
+ List<String> tags = new ArrayList<String>();
+ tags.add(importReqDetails.getName());
+ importReqDetails.setTags(tags);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESOURCE_ALREADY_EXISTS.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_ALREADY_EXISTS.name(), variables, importResourceResponse.getResponse());
+
+ expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(importReqDetails.getToscaResourceName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+
+ }
+
+ @Test
+ public void importToscaResourceWithTheSameNameAsCreatedResourceBefore() throws Exception {
+
+ // create resource
+ String fileName = importSuccessFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName(importReqDetails.getName());
+
+ RestResponse response = ResourceRestUtils.createResource(resourceDetails, sdncUserDetails);
+ int status = response.getErrorCode();
+ assertEquals("create request returned status:" + status, 201, status);
+ assertNotNull("resource uniqueId is null:", resourceDetails.getUniqueId());
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ // assertNull("validate toscaResourceName field",
+ // resourceJavaObject.getToscaResourceName());
+
+ // import the same tosca resource
+ DbUtils.cleanAllAudits();
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESOURCE_ALREADY_EXISTS.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_ALREADY_EXISTS.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+
+ }
+
+ @Test
+ public void importToscaResourceInvalidChecksum() throws Exception {
+ String fileName = importSuccessFile;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), "invalidMd5Sum");
+
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, headersMap);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ assertNotNull("check response object is not null after import tosca resource", importResourceResponse);
+ assertNotNull("check error code exists in response after import tosca resource", importResourceResponse.getErrorCode());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_RESOURCE_CHECKSUM.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_RESOURCE_CHECKSUM.name(), variables, importResourceResponse.getResponse());
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceInvalidResType() throws Exception {
+
+ String resourceType = "invalidResourceType";
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importSuccessFile);
+ importReqDetails.setResourceType(resourceType);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_CONTENT.name());
+ assertNotNull("check response object is not null after import resouce", importResourceResponse);
+ assertNotNull("check error code exists in response after import resource", importResourceResponse.getErrorCode());
+ assertEquals("Check response code after import resource", errorInfo.getCode(), importResourceResponse.getErrorCode());
+
+ List<String> variables = new ArrayList<>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), variables, importResourceResponse.getResponse());
+
+ // Validate audit message
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void derivedTemplateImportedSecondResourceAsFirstImportedNodeType() throws Exception {
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importSuccessFile);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+ assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ assertTrue("validate toscaResourceName field", resourceJavaObject.getToscaResourceName().equals(parseToscaNodeYaml.getNodeName()));
+ assertTrue("validate resourceType field, expected - " + importReqDetails.getResourceType() + ", actual - " + resourceJavaObject.getResourceType(), resourceJavaObject.getResourceType().toString().equals(importReqDetails.getResourceType()));
+
+ // Validate audit message
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+
+ RestResponse certifyResource = LifecycleRestUtils.certifyResource(importReqDetails);
+ assertTrue("certify resource request returned status:" + certifyResource.getErrorCode(), certifyResource.getErrorCode() == 200);
+
+ // import second resource template derived from first resource
+ DbUtils.cleanAllAudits();
+ importReqDetails.setName("kuku");
+ List<String> tags = new ArrayList<String>();
+ tags.add(importReqDetails.getName());
+ importReqDetails.setTags(tags);
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, derivedFromMyCompute);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+ parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ Resource resourceJavaObject2 = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ assertTrue("validate toscaResourceName field", resourceJavaObject2.getToscaResourceName().equals(parseToscaNodeYaml.getNodeName()));
+ assertTrue("validate resourceType field, expected - " + importReqDetails.getResourceType() + ", actual - " + resourceJavaObject2.getResourceType(), resourceJavaObject2.getResourceType().toString().equals(importReqDetails.getResourceType()));
+
+ // Validate audit message
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject2 = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject2.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject2.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject2.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject2.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject2, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+
+ }
+
+ @Test
+ public void importToscaResourceListPropertyGoodDefault() throws Exception {
+
+ String fileName = importListPropertyGoodDefault;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ assertTrue("response code is not 201, returned :" + importResourceResponse.getErrorCode(), importResourceResponse.getErrorCode() == 201);
+
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ assertTrue("Properties size : " + resourceJavaObject.getProperties().size(), resourceJavaObject.getProperties().size() == 1);
+ assertTrue("Property type : " + resourceJavaObject.getProperties().get(0).getType(), resourceJavaObject.getProperties().get(0).getType().equals(ToscaPropertyType.LIST.getType()));
+ assertTrue("actual Default values : " + resourceJavaObject.getProperties().get(0).getDefaultValue() + " , expected : " + "[false, true]", resourceJavaObject.getProperties().get(0).getDefaultValue().equals("[\"false\",\"true\"]"));
+
+ }
+
+ @Test
+ public void importToscaResourceListPropertyBadDefault() throws Exception {
+
+ String fileName = importListPropertyBadDefault;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name());
+ assertEquals("Check response code after tosca resource import", errorInfo.getCode(), importResourceResponse.getErrorCode());
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("my_prop");
+ variables.add("list");
+ variables.add("boolean");
+ variables.add("[12,true]");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name(), variables, importResourceResponse.getResponse());
+
+ }
+
+ // Benny US580744 - Add support for TOSCA "list" type - import
+
+ @Test
+ public void importToscaResourceListPropertySuccessFlow() throws Exception {
+ String fileName = importListPropertySuccess;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ ResourceRestUtils.checkCreateResponse(importResourceResponse);
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ // Verify Properties List in resource
+ verifyResourcePropertiesList(resourceJavaObject);
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ // DE198534
+ @Test(dataProvider = "getYmlWithInValidListProperties") // invalid default
+ // values
+ public void importToscaResourceListPropertyFailureFlows(String ymlFileWithInvalidPropertyDefualtValues, String defualtValues, String enterySchemaType) throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFileWithInvalidPropertyDefualtValues);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("my_property");
+ variables.add("list");
+ variables.add(enterySchemaType);
+ variables.add(defualtValues);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name(), variables, importResourceResponse.getResponse());
+ }
+
+ // BUG DE198650
+ @Test
+ public void importToscaResourceListPropertyNonSupportEntrySchemaType() throws Exception {
+ String ymlFile = "ListPropertyFalure01.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFile);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("booolean"); // property entry_schema data type
+ variables.add("my_boolean");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_INNER_TYPE.name(), variables, importResourceResponse.getResponse());
+ }
+
+ // BUG DE198676
+ @Test // (enabled=false)
+ public void importToscaResourceListPropertyNonSupportedPropertyType() throws Exception { // Not
+ // "list"
+ // type
+ String ymlFile = "ListPropertyFalure16.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFile);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("koko"); // property data type (koko instead list)
+ variables.add("my_boolean");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_TYPE.name(), variables, importResourceResponse.getResponse());
+ }
+
+ /// US656928 - [BE] - Add support for TOSCA "map" type - Phase 1 import
+ @Test
+ public void importToscaResourceMapPropertySuccessFlow() throws Exception {
+ String fileName = importMapPropertySuccess;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ ResourceRestUtils.checkCreateResponse(importResourceResponse);
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+ // Verify Properties MAP in resource
+ verifyResourcePropertiesMap(resourceJavaObject);
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test(dataProvider = "getYmlWithInValidMapProperties") // invalid default
+ // values
+ public void importToscaResourceMapPropertyFailureFlows(String ymlFileWithInvalidPropertyDefualtValues, String defualtValues, String enterySchemaType) throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFileWithInvalidPropertyDefualtValues);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("my_property");
+ variables.add("map");
+ variables.add(enterySchemaType);
+ variables.add(defualtValues);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name(), variables, importResourceResponse.getResponse());
+ }
+
+ @Test
+ public void importToscaResourceMaptPropertyNonSupportedPropertyType() throws Exception { // Not
+ // "Map"
+ // type
+ String ymlFile = "MapPropertyFalure16.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, ymlFile);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("koko"); // property data type (koko instead list)
+ variables.add("my_boolean");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_TYPE.name(), variables, importResourceResponse.getResponse());
+ }
+
+ @Test
+ public void importToscaResourceMissingCapabilityInReqDefinition() throws Exception {
+
+ String fileName = missingCapInReqDef;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_CAPABILITY_TYPE.name());
+ String missingCapName = "org.openecomp.capabilities.networkInterfaceNotFound";
+ BaseRestUtils.checkErrorResponse(importResourceResponse, ActionStatus.MISSING_CAPABILITY_TYPE, missingCapName);
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, Arrays.asList(missingCapName));
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType("org.openecomp.resource.vSCP-03-16");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceMissingCapabilityInCapDefinition() throws Exception {
+
+ String fileName = missingCapInCapDef;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ logger.debug("import tosca resource response: {}", importResourceResponse.getResponseMessage());
+
+ // Validate audit message
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_CAPABILITY_TYPE.name());
+ String missingCapName = "org.openecomp.capabilities.networkInterfaceNotFound";
+ BaseRestUtils.checkErrorResponse(importResourceResponse, ActionStatus.MISSING_CAPABILITY_TYPE, missingCapName);
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, Arrays.asList(missingCapName));
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType("org.openecomp.resource.vSCP-03-16");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceDuplicateRequirements() throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importDuplicateRequirements);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("requirement");
+ variables.add("local_storage");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME.name(), variables, importResourceResponse.getResponse());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceDuplicateCapabilities() throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importDuplicateCapability);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("capability");
+ variables.add("scalable");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME.name(), variables, importResourceResponse.getResponse());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceRequirementNameExistsOnParent() throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importRequirementNameExistsOnParent);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("requirement");
+ variables.add("local_storage");
+ variables.add("Compute");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED.name(), variables, importResourceResponse.getResponse());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceCapabilityNameExistsOnParent() throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importCapabilityNameExistsOnParent);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertTrue(importResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("capability");
+ variables.add("binding");
+ variables.add("Compute");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED.name(), variables, importResourceResponse.getResponse());
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgFailure(errorInfo, variables);
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ @Test
+ public void importToscaResourceReqCapDerivedFromParent() throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, importToscaResourceReqCapDerivedFromParent);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ BaseRestUtils.checkCreateResponse(importResourceResponse);
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType("org.openecomp.resource.MyWebApp");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ /************************ Shay ************************/
+
+ @Test
+ public void caseRequirementInsensitiveTest() throws Exception {
+ String fileName = "CaseInsensitiveReqTest_1.yml";
+ int expectedNumOfRequirements = 2;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+ importReqDetails.setRequirements(testResourcesPath, fileName, sdncUserDetails, null);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String fileName2 = "CaseInsensitiveReqTest_2.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName2);
+ importReqDetails.setName("secondImportedResource");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, null);
+ requirements = importReqDetails.getRequirements();
+ requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ checkImportedAssetAssociated(importReqDetails);
+
+ }
+
+ private void checkImportedAssetAssociated(ImportReqDetails importDetails) throws IOException, Exception {
+ RestResponse importResourceResponse;
+ ImportReqDetails importReqDetails2 = ElementFactory.getDefaultImportResource();
+ importReqDetails2 = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails2, testResourcesPath, "BindingAsset.yml");
+ importReqDetails2.setName("bindingAsset");
+ importReqDetails2.setTags(Arrays.asList(importReqDetails2.getName()));
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails2, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ ResourceReqDetails vf = ElementFactory.getDefaultResourceByType("VF100", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncUserDetails.getUserId(), ResourceTypeEnum.VF.toString());
+ RestResponse createResourceResponse = ResourceRestUtils.createResource(vf, sdncUserDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceResponse);
+
+ LifecycleRestUtils.changeResourceState(importDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ LifecycleRestUtils.changeResourceState(importReqDetails2, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+
+ RestResponse response = ResourceRestUtils.createResourceInstance(importDetails, sdncUserDetails, vf.getUniqueId());
+ ResourceRestUtils.checkCreateResponse(response);
+ ComponentInstance riCap = ResponseParser.parseToObject(response.getResponse(), ComponentInstance.class);
+
+ response = ResourceRestUtils.createResourceInstance(importReqDetails2, sdncUserDetails, vf.getUniqueId());
+ ResourceRestUtils.checkCreateResponse(response);
+ ComponentInstance riReq = ResponseParser.parseToObject(response.getResponse(), ComponentInstance.class);
+
+ RestResponse getResourceBeforeAssociate = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncUserDetails, vf);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceBeforeAssociate.getResponse(), CapReqDef.class);
+
+ String capbilityUid = capReqDef.getCapabilities().get("tosca.capabilities.network.Bindable").get(0).getUniqueId();
+ String requirementUid = capReqDef.getRequirements().get("tosca.capabilities.network.Bindable").get(0).getUniqueId();
+
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ requirementDef.setFromNode(riReq.getUniqueId());
+ requirementDef.setToNode(riCap.getUniqueId());
+
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair();
+ pair.setRequirementOwnerId(riReq.getUniqueId());
+ pair.setCapabilityOwnerId(riCap.getUniqueId());
+ pair.setRequirement("VirtualBinding");
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType("tosca.capabilities.network.Bindable");
+ pair.setRelationships(relationship);
+ pair.setCapabilityUid(capbilityUid);
+ pair.setRequirementUid(requirementUid);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<>();
+ relationships.add(pair);
+ requirementDef.setRelationships(relationships);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, sdncUserDetails, vf.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ }
+
+ @Test
+ public void caseCapabilitiesInsensitiveTest() throws Exception {
+ String fileName = "CaseInsensitiveCapTest_1.yml";
+ int expectedNumOfCapabilities = 6;
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setCapabilities(testResourcesPath, fileName, sdncUserDetails, null);
+ Map<String, Object> capabilities = importReqDetails.getCapabilities();
+ Map<String, Object> capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String fileName2 = "CaseInsensitiveCapTest_2.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName2);
+ importReqDetails.setName("secondImportedResource");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setCapabilities(testResourcesPath, fileName2, sdncUserDetails, null);
+ capabilities = importReqDetails.getCapabilities();
+ capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+
+ }
+
+ @Test
+ public void fatherAndChildHaveDifferentRequirementsTest() throws Exception {
+ String fileName = "DifferentReqFromCompute.yml";
+ int expectedNumOfRequirements = 3;
+
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, fileName, sdncUserDetails, "Compute");
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ checkImportedAssetAssociated(importReqDetails);
+ }
+
+ @Test
+ public void fatherHasNoRequirementsTest() throws Exception {
+ String fatherFileName = "CPHasNoReqCap.yml";
+ String childFileName = "DerivedFromCPWithOwnReq.yml";
+ int expectedNumOfRequirements = 3;
+
+ importReqDetails.setName("father");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String derivedFromResourceName = importReqDetails.getName();
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromResourceName);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ }
+
+ @Test
+ public void childHasSameReqNameAndTypeLikeFatherTest() throws Exception {
+ String childFileName = "SameReqAsCompute.yml";
+ int expectedNumOfRequirements = 2;
+
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, null);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+ }
+
+ @Test
+ public void childHasSameCapNameAndTypeLikeFatherTest() throws Exception {
+ String childFileName = "SameCapAsCompute.yml";
+ int expectedNumOfCapabilities = 6;
+
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setCapabilities(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, "Compute");
+ Map<String, Object> capabilities = importReqDetails.getCapabilities();
+ Map<String, Object> capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+ }
+
+ @Test
+ public void childGetsAllRequirementsOfFatherAndGrandfatherTest() throws Exception {
+ int expectedNumOfRequirements = 4;
+
+ String fatherFileName = "DifferentReqFromCompute.yml";
+ importReqDetails.setName("father");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String derivedFromName = importReqDetails.getName();
+ String childFileName = "DifferentReqCapFromCompute1.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ }
+
+ @Test
+ public void childOverridesGrandfatherRequirementsTest() throws Exception {
+ int expectedNumOfRequirements = 3;
+
+ String fatherFileName = "DifferentReqFromCompute.yml";
+ importReqDetails.setName("father");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String derivedFromName = importReqDetails.getName();
+ String childFileName = "SameReqAsCompute_DerivedFromMyCompute1.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+ }
+
+ @Test
+ public void childAndGrandfatherHaveDifferenetReqiurementTypeTest() throws Exception {
+ int expectedNumOfRequirements = 3;
+ int expectedNumOfCapabilities = 6;
+
+ String fatherName = "father";
+ String fatherFileName = "DifferentReqFromCompute.yml";
+ importReqDetails.setName(fatherName);
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String fatherUniqueId = importReqDetails.getUniqueId();
+ ImportReqDetails importReqDetailsFather = importReqDetails;
+
+ String childFileName = "importRequirementNameExistsOnParent_DerivedFromMyCompute1.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_INVALID_CONTENT, importResourceResponse.getErrorCode().intValue());
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("requirement");
+ variables.add("local_storage");
+ variables.add(fatherName);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED.name(), variables, importResourceResponse.getResponse());
+
+ importReqDetails.setUniqueId(fatherUniqueId);
+
+ importReqDetailsFather.setRequirements(testResourcesPath, fatherFileName, sdncUserDetails, "Compute");
+ Map<String, Object> requirements = importReqDetailsFather.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetailsFather, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetailsFather.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ importReqDetailsFather.setCapabilities(testResourcesPath, fatherFileName, sdncUserDetails, "Compute");
+ Map<String, Object> capabilities = importReqDetailsFather.getCapabilities();
+ Map<String, Object> capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetailsFather, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetailsFather.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+ }
+
+ @Test
+ public void childHasNoReqCapTest() throws Exception {
+ int expectedNumOfRequirements = 3;
+ int expectedNumOfCapabilities = 6;
+
+ String fatherFileName = "DifferentReqFromCompute.yml";
+ importReqDetails.setName("father");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String derivedFromName = importReqDetails.getName();
+ String childFileName = "CPHasNoReqCap_DerivedFromMyCompute1.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ importReqDetails.setCapabilities(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> capabilities = importReqDetails.getCapabilities();
+ Map<String, Object> capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+ }
+
+ @Test
+ public void fatherAndChildGetReqCapFromGrandfatherTest() throws Exception {
+ int expectedNumOfRequirements = 2;
+ int expectedNumOfCapabilities = 6;
+
+ String fatherFileName = "MyFatherCompute_NoReqCap.yml";
+ importReqDetails.setName("father");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String derivedFromName = importReqDetails.getName();
+ String childFileName = "myChildCompute_NoReqCap.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ importReqDetails.setCapabilities(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> capabilities = importReqDetails.getCapabilities();
+ Map<String, Object> capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+ }
+
+ @Test
+ public void reverseInheritanceTest() throws Exception {
+ int expectedNumOfRequirements = 2;
+ int expectedNumOfCapabilities = 2;
+
+ String fatherName = "father";
+ String fatherFileName = "myFatherWebApp_derviedFromDocker.yml";
+ importReqDetails.setName(fatherName);
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String fatherUniqueId = importReqDetails.getUniqueId();
+ ImportReqDetails importReqDetailsFather = importReqDetails;
+ String childFileName = "myChildWebApp_DerivedFromContainer.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_INVALID_CONTENT, importResourceResponse.getErrorCode().intValue());
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("requirement");
+ variables.add("host");
+ variables.add(fatherName);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED.name(), variables, importResourceResponse.getResponse());
+
+ importReqDetails.setUniqueId(fatherUniqueId);
+ importReqDetailsFather.setRequirements(testResourcesPath, fatherFileName, sdncUserDetails, "Root");
+ Map<String, Object> requirements = importReqDetailsFather.getRequirements();
+ Map<String, Object> requirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetailsFather, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), requirementsFromResponse.keySet().size());
+ importReqDetailsFather.compareRequirementsOrCapabilities(requirements, requirementsFromResponse);
+
+ importReqDetailsFather.setCapabilities(testResourcesPath, fatherFileName, sdncUserDetails, "Root");
+ Map<String, Object> capabilities = importReqDetailsFather.getCapabilities();
+ Map<String, Object> capabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetailsFather, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), capabilitiesFromResponse.keySet().size());
+ importReqDetailsFather.compareRequirementsOrCapabilities(capabilities, capabilitiesFromResponse);
+ }
+
+ // DE202329
+ @Test(enabled = false)
+ public void requirementWithMissingTypeTest() throws Exception {
+ String fatherName = "father";
+ String fatherFileName = "DerivedFromWebApplication_HasNoReqType.yml";
+ importReqDetails.setName(fatherName);
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_INVALID_CONTENT, importResourceResponse.getErrorCode().intValue());
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("diff");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_CAPABILITY_TYPE.name(), variables, importResourceResponse.getResponse());
+
+ }
+
+ @Test
+ public void TwinBrothersHaveSameReqCapTest() throws Exception {
+ int expectedNumOfRequirements = 4;
+ int expectedNumOfCapabilities = 7;
+
+ String derivedFromName = "father";
+ String fatherFileName = "DifferentReqFromCompute.yml";
+ importReqDetails.setName(derivedFromName);
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fatherFileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ RestResponse changeResourceState1 = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState1.getErrorCode().intValue());
+ RestResponse changeResourceState2 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState2.getErrorCode().intValue());
+ RestResponse changeResourceState3 = LifecycleRestUtils.changeResourceState(importReqDetails, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState3.getErrorCode().intValue());
+
+ String childFileName = "DifferentReqCapFromCompute1.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("child");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, childFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ Map<String, Object> childRequirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ Map<String, Object> childCapabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities - 1);
+
+ String twinFileName = "DifferentReqCapFromCompute2.yml";
+ importReqDetails = ElementFactory.getDefaultImportResource();
+ importReqDetails.setName("twin");
+ importReqDetails.setTags(Arrays.asList(importReqDetails.getName()));
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, twinFileName);
+ importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+
+ importReqDetails.setRequirements(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> requirements = importReqDetails.getRequirements();
+ Map<String, Object> twinRequirementsFromResponse = parseReqOrCapFromResponse("requirements", importReqDetails, expectedNumOfRequirements);
+ assertEquals(requirements.keySet().size(), twinRequirementsFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(requirements, twinRequirementsFromResponse);
+
+ importReqDetails.setCapabilities(testResourcesPath, importReqDetails.getPayloadName(), sdncUserDetails, derivedFromName);
+ Map<String, Object> capabilities = importReqDetails.getCapabilities();
+ Map<String, Object> twinCapabilitiesFromResponse = parseReqOrCapFromResponse("capabilities", importReqDetails, expectedNumOfCapabilities);
+ assertEquals(capabilities.keySet().size(), twinCapabilitiesFromResponse.keySet().size());
+ importReqDetails.compareRequirementsOrCapabilities(capabilities, twinCapabilitiesFromResponse);
+
+ assertEquals(childRequirementsFromResponse.keySet().size(), twinRequirementsFromResponse.keySet().size());
+ assertEquals(childCapabilitiesFromResponse.keySet().size(), twinCapabilitiesFromResponse.keySet().size());
+ }
+
+ /*
+ * invariantUUID - US672129
+ */
+
+ private void checkInvariantUuidIsImmutableInDifferentAction(ImportReqDetails importReqDetails) throws Exception {
+ // create resource
+ importReqDetails.setName("import");
+ String invariantUuidDefinedByUser = "abcd1234";
+ RestResponse importResourceResponse = importResourceWithRequestedInvariantUuid(importReqDetails, invariantUuidDefinedByUser);
+ String invariantUUIDcreation = ResponseParser.getInvariantUuid(importResourceResponse);
+ assertFalse(checkInvariantUuidEqual(invariantUuidDefinedByUser, importResourceResponse));
+
+ // get resource
+ RestResponse getResource = ResourceRestUtils.getResource(importReqDetails.getUniqueId());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, getResource));
+
+ // checkin resource
+ RestResponse changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // checkout resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // checkin resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // checkout resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // checkin resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // certification request
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // start certification
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, testerUser, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // certify
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, testerUser, LifeCycleStatesEnum.CERTIFY);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+ String certifiedUniqueId = importReqDetails.getUniqueId();
+
+ // update resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKOUT);
+ ResourceReqDetails updatedResourceReqDetails = new ResourceReqDetails(importReqDetails, importReqDetails.getVersion());
+ updatedResourceReqDetails.setDescription("updatedDescription");
+ updatedResourceReqDetails.setVendorRelease("1.2.3.4");
+ RestResponse updateResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceReqDetails, sdncUserDetails, importReqDetails.getUniqueId());
+ assertEquals(STATUS_CODE_SUCCESS, updateResponse.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, updateResponse));
+
+ // certification request
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // checkout resource
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // certification request
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // start certification
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, testerUser, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // cancel certification
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, testerUser, LifeCycleStatesEnum.CANCELCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // start certification
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, testerUser, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // failure
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, testerUser, LifeCycleStatesEnum.FAILCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // upload artifact
+ changeResourceState = LifecycleRestUtils.changeResourceState(importReqDetails, sdncUserDetails, LifeCycleStatesEnum.CHECKOUT);
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultArtifact();
+ ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails, sdncUserDetails, importReqDetails.getUniqueId());
+ assertEquals(STATUS_CODE_SUCCESS, changeResourceState.getErrorCode().intValue());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, changeResourceState));
+
+ // create instance
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.toString());
+ ResourceRestUtils.createResource(resourceDetails, sdncUserDetails);
+ importReqDetails.setUniqueId(certifiedUniqueId);
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory.getComponentResourceInstance(importReqDetails);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, sdncUserDetails, resourceDetails.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals(STATUS_CODE_CREATED, createResourceInstanceResponse.getErrorCode().intValue());
+ getResource = ResourceRestUtils.getResource(importReqDetails.getUniqueId());
+ assertTrue(checkInvariantUuidEqual(invariantUUIDcreation, getResource));
+ }
+
+ private boolean checkInvariantUuidEqual(String expectedInvariantUuid, RestResponse response) {
+ String invariantUUIDFromResponse = ResponseParser.getInvariantUuid(response);
+ return expectedInvariantUuid.equals(invariantUUIDFromResponse);
+ }
+
+ @Test
+ public void checkCPHasImmutableInvariantUuidTest() throws Exception {
+ String filename = "FatherHasNoReqCap.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, filename);
+ checkResourceHasImmutableInvariantUuidTest(importReqDetails);
+ }
+
+ @Test
+ public void checkVFCHasImmutableInvariantUuidTest() throws Exception {
+ String filename = "computeCap11.yml";
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, filename);
+ checkResourceHasImmutableInvariantUuidTest(importReqDetails);
+ }
+
+ public void checkResourceHasImmutableInvariantUuidTest(ImportReqDetails importReqDetails) throws Exception {
+ // invariantUuid is null
+ importReqDetails.setName("first");
+ RestResponse importResourceResponse = importResourceWithRequestedInvariantUuid(importReqDetails, null);
+ String invariantUUIDcreation = ResponseParser.getInvariantUuid(importResourceResponse);
+ assertNotNull(invariantUUIDcreation);
+
+ ResourceRestUtils.deleteResource(importReqDetails.getUniqueId(), sdncUserDetails.getUserId());
+
+ // invariantUuid is empty
+ importReqDetails.setName("second");
+ String invariantUuidDefinedByUser = "";
+ importResourceResponse = importResourceWithRequestedInvariantUuid(importReqDetails, invariantUuidDefinedByUser);
+ invariantUUIDcreation = ResponseParser.getInvariantUuid(importResourceResponse);
+ assertNotNull(invariantUUIDcreation);
+
+ ResourceRestUtils.deleteResource(importReqDetails.getUniqueId(), sdncUserDetails.getUserId());
+
+ checkInvariantUuidIsImmutableInDifferentAction(importReqDetails);
+ }
+
+ private static RestResponse importResourceWithRequestedInvariantUuid(ImportReqDetails importDetails, String invariantUuid) throws Exception {
+ importDetails.setInvariantUUID(invariantUuid);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importDetails, sdncUserDetails, null);
+ assertEquals(STATUS_CODE_CREATED, importResourceResponse.getErrorCode().intValue());
+ return importResourceResponse;
+ }
+
+ private Map<String, Object> parseReqOrCapFromResponse(String parsedFieldName, ImportReqDetails importReqDetails, int expectedNumOfReqCap) throws ClientProtocolException, IOException {
+ RestResponse getResource = ResourceRestUtils.getResource(importReqDetails.getUniqueId());
+ assertTrue(getResource.getErrorCode().equals(STATUS_CODE_SUCCESS));
+ Map<String, Object> parsedFieldFromResponseToMap = ResponseParser.getJsonValueAsMap(getResource, parsedFieldName);
+ Iterator<String> iterator = parsedFieldFromResponseToMap.keySet().iterator();
+ actualNumOfReqOrCap = 0;
+ while (iterator.hasNext()) {
+ String next = iterator.next();
+ List<Object> object = (List<Object>) parsedFieldFromResponseToMap.get(next);
+ actualNumOfReqOrCap += object.size();
+ }
+ assertEquals(expectedNumOfReqCap, actualNumOfReqOrCap);
+ return parsedFieldFromResponseToMap;
+ }
+
+ // ---------------------------------
+
+ private void verifyResourcePropertiesList(Resource resourceJavaObject) { // use
+ // importListPropertySuccessFlow.yml
+ boolean isPropertyAppear = false;
+ List<PropertyDefinition> propertiesList = resourceJavaObject.getProperties();
+ for (PropertyDefinition pro : propertiesList) {
+ switch (pro.getName()) {
+ case "my_boolean":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[false,true]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "my_boolean_array":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[true,false]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "duplicate_boolean_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[true,false,true]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_values_Insensitive":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[true,false,true]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "my_integers":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[0,1000,-1000,50]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "my_integers_array":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[10,-1000,0]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "duplicate_integers_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[10,10,-1000,0]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "my_string":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[\"asdc\",\"$?^@ecomp$!#%()_-~@+*^...;;/w#\",\"uc\"]"));
+ // assertTrue("Check Property default values ",
+ // pro.getDefaultValue().equals("[\"asdc\",\"@=~!@#$%^&*()_+=?><:-w\",\"uc\"]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "my_string_array":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[\"AAA\",\"~$~#bbb%^*_-\",\"qwe\",\"1.3\",\"500\",\"true\"]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "duplicate_string_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[\"asdc\",\"asdc\",\"uc\"]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_null_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[\"asdc\",\"uc\"]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_space_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[\"asdc\",\"uc\"]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_array_null_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[\"aaa\",\"bbb\",\"500\"]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "my_float":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[6,1000.000001,-3.0]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "my_float_array":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[0.01,-5.0,2.1]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "duplicate_float_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[0.0,0.0,4.555555]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_no_default_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "integer_no_default_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "string_no_default_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_no_default_values":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "integer_null_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[1000,2000]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_null_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[true,false]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "float_null_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[6,-3.0]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_space_value":
+ assertTrue("Check Property Type ", pro.getType().equals("list"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("[6,-3.0]"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+
+ }
+ assertTrue(isPropertyAppear);
+ isPropertyAppear = false;
+ }
+
+ }
+
+ private void verifyRequirementsOccurrences(Resource resourceJavaObject, String requirementsType) {
+ boolean isRequirementAppear = false;
+ // List<RequirementDefinition> requerments =
+ // resourceJavaObject.getRequirements().get("tosca.capabilities.Attachment");
+ List<RequirementDefinition> requerments = resourceJavaObject.getRequirements().get(requirementsType);
+
+ for (RequirementDefinition req : requerments) {
+ switch (req.getName()) {
+ case "local_storage100":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals("1"));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals("UNBOUNDED"));
+ isRequirementAppear = true;
+ break;
+ case "local_storage200":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals("1"));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals("1"));
+ isRequirementAppear = true;
+ break;
+ case "local_storage300":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals("1"));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals("10"));
+ isRequirementAppear = true;
+ break;
+ case "local_storage400":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals("1"));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals("10000000"));
+ isRequirementAppear = true;
+ break;
+ case "local_storage500":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals("2"));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals("3"));
+ isRequirementAppear = true;
+ break;
+ case "local_storageNoOccurrences600":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals("1"));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals("1"));
+ isRequirementAppear = true;
+ break;
+ }
+ assertTrue(isRequirementAppear);
+ isRequirementAppear = false;
+ }
+
+ }
+
+ private void verifyCapabilitiesOccurrences(Resource resourceJavaObject, String capabilitType) {
+ boolean isCapabilityAppear = false;
+ // List<CapabilityDefinition> capabilities =
+ // resourceJavaObject.getCapabilities().get("tosca.capabilities.Endpoint.Admin");
+ List<CapabilityDefinition> capabilities = resourceJavaObject.getCapabilities().get(capabilitType);
+
+ for (CapabilityDefinition cap : capabilities) {
+ switch (cap.getName()) {
+ case "endpointNoOccurrence":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("1"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("UNBOUNDED"));
+ isCapabilityAppear = true;
+ break;
+ case "endpoint200":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("1"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("2"));
+ isCapabilityAppear = true;
+ break;
+ case "endpoint300":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("1"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("1"));
+ isCapabilityAppear = true;
+ break;
+ case "endpoint400":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("1"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("10"));
+ isCapabilityAppear = true;
+ break;
+ case "endpoint500":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("1"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("10000000"));
+ isCapabilityAppear = true;
+ break;
+ case "endpoint600":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("1"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("UNBOUNDED"));
+ isCapabilityAppear = true;
+ break;
+ case "endpoint700":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals("2"));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals("4"));
+ isCapabilityAppear = true;
+ break;
+
+ }
+ assertTrue(isCapabilityAppear);
+ isCapabilityAppear = false;
+ }
+
+ }
+
+ private void verifyResourcePropertiesMap(Resource resourceJavaObject) { // use
+ // importMapPropertySuccessFlow.yml
+ boolean isPropertyAppear = false;
+ List<PropertyDefinition> propertiesList = resourceJavaObject.getProperties();
+ for (PropertyDefinition pro : propertiesList) {
+ switch (pro.getName()) {
+ case "string_prop01":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"val1\",\"keyB\":\"val2\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop02":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"val1\",\"keyB\":\"val2\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop03":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"val1\",\"keyB\":\"val2\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop04":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"10\",\"keyB\":\"true\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop05":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":\"Big\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop06":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"aaaA\",\"keyB\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop07":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop08":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"\",\"keyB\":\"abcd\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop09":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\" \",\"keyB\":\"abcd\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop10":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\" aaaa\",\"keyB\":\" bbbb\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop11":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"aaaa \",\"keyB\":\"bbbb \"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop12":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\" aaaa \",\"keyB\":\" bbbb ccccc \"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop13":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"aaaa\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop14":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\" aaaa \"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop15":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"AbcD\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop16":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"AbcD\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop17":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"AbcD\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop18":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"AbcD\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop19":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"AbcD\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop20":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":\"aaaa\",\"keya\":\"aaaa\",\"Keya\":\"Aaaa\",\"KEYA\":\"nnnn\"}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop21":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "string_prop22":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("string"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop01":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":1,\"keyB\":1000}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop02":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop03":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":800,\"keyB\":-600}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop04":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":-600}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop05":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":100,\"keyB\":0}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop06":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":100,\"keyB\":0}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop07":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":100,\"keyB\":100}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop08":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":100,\"keyB\":200}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop09":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":100,\"keyB\":200}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop10":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":2222}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop11":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null,\"keyD\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop12":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "integer_prop13":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":200}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("integer"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop01":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":true,\"keyB\":false,\"keyC\":false}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop02":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":true,\"keyB\":false,\"keyC\":false}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop03":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null,\"keyD\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop04":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null,\"keyD\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop05":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":true,\"keyB\":false,\"keyC\":false}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop06":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":true,\"keyB\":true,\"keyC\":false}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop07":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop08":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":true,\"keyB\":false}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "boolean_prop09":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":false,\"keyB\":true}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("boolean"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop01":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":1.2,\"keyB\":3.56,\"keyC\":33}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop02":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":0.0,\"keyB\":0.0,\"keyC\":0}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop03":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null,\"keyD\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop04":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":1.2,\"keyB\":3.56,\"keyC\":33}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop05":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":33,\"keyB\":1.2,\"keyC\":3.607,\"keyD\":0}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop06":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":33,\"keyB\":1.2,\"keyC\":3.607}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop07":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":null,\"keyB\":null,\"keyC\":null,\"keyD\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop08":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertEquals("Check Property default values ", pro.getDefaultValue(), null);
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop09":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":0.01,\"keyB\":null}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop10":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":0.00020}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ case "float_prop11":
+ assertTrue("Check Property Type ", pro.getType().equals("map"));
+ assertTrue("Check Property default values ", pro.getDefaultValue().equals("{\"keyA\":3.56,\"keyB\":33}"));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType().equals("float"));
+ isPropertyAppear = true;
+ break;
+ }
+ assertTrue(isPropertyAppear);
+ isPropertyAppear = false;
+ }
+
+ }
+
+ @Test
+ public void importToscaResourceAttributeSuccessFlow() throws Exception {
+
+ String fileName = importAttributeSuccess;
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, sdncUserDetails, null);
+ ResourceRestUtils.checkCreateResponse(importResourceResponse);
+ Resource resourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ ToscaNodeTypeInfo parseToscaNodeYaml = utils.parseToscaNodeYaml(Decoder.decode(importReqDetails.getPayloadData()));
+
+ HashMap<String, AttributeDefinition> attr = new HashMap<>();
+
+ AttributeDefinition newAttr2 = new AttributeDefinition();
+ newAttr2.setName("networks");
+ newAttr2.setType("map");
+ newAttr2.setDefaultValue("{\"keyA\" : val1 , \"keyB\" : val2}");
+ SchemaDefinition schema = new SchemaDefinition();
+ PropertyDataDefinition prop = new PropertyDataDefinition();
+ prop.setType("string");
+ schema.setProperty(prop);
+ newAttr2.setSchema(schema);
+ attr.put("networks", newAttr2);
+
+ AttributeDefinition newAttr1 = new AttributeDefinition();
+ newAttr1.setName("public_address");
+ newAttr1.setType("string");
+ attr.put("public_address", newAttr1);
+
+ AttributeDefinition newAttr3 = new AttributeDefinition();
+ newAttr3.setName("ports");
+ newAttr3.setDescription("this is my description");
+ attr.put("ports", newAttr3);
+
+ AttributeDefinition newAttr = new AttributeDefinition();
+ newAttr.setDefaultValue("myDefault");
+ newAttr.setName("private_address");
+ newAttr.setStatus("supported");
+ newAttr.setType("string");
+ attr.put("private_address", newAttr);
+
+ // verify Resource Attributes
+ validateResourceAttribute(resourceJavaObject, attr);
+
+ // TO DO
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ElementFactory.getDefaultImportResourceAuditMsgSuccess();
+ expectedResourceAuditJavaObject.setResourceName(importReqDetails.getName());
+ expectedResourceAuditJavaObject.setModifierName(sdncUserDetails.getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ expectedResourceAuditJavaObject.setToscaNodeType(parseToscaNodeYaml.getNodeName());
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, AuditingActionEnum.IMPORT_RESOURCE.getName(), null, false);
+ }
+
+ private void validateResourceAttribute(Resource resource, Map<String, AttributeDefinition> attr) {
+ List<AttributeDefinition> resList = resource.getAttributes();
+ int size = resList.size();
+ String attributeName;
+ for (int i = 0; i < size; i++) {
+ attributeName = resList.get(i).getName();
+ assertEquals(attr.get(attributeName).getDefaultValue(), resList.get(i).getDefaultValue());
+ assertEquals(attr.get(attributeName).getName(), resList.get(i).getName());
+ assertEquals(attr.get(attributeName).getDescription(), resList.get(i).getDescription());
+ assertEquals(attr.get(attributeName).getStatus(), resList.get(i).getStatus());
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportUpdateResourseCsarTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportUpdateResourseCsarTest.java
new file mode 100644
index 0000000000..9e8b94e5a8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/imports/ImportUpdateResourseCsarTest.java
@@ -0,0 +1,282 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.imports;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class ImportUpdateResourseCsarTest extends ComponentBaseTest {
+ @Rule
+ public static TestName name = new TestName();
+
+ Gson gson = new Gson();
+ public static String userDefinedNodeYaml = "mycompute2.yml";
+ public static String rootPath = System.getProperty("user.dir");
+ public static String csarFolderPath = "/src/test/resources/CI/csars/";
+
+ public ImportUpdateResourseCsarTest() {
+ super(name, ImportUpdateResourseCsarTest.class.getName());
+ }
+
+ @Test
+ public void createUpdateImportResourceFromCsarTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ RestResponse updateResource = null;
+ RestResponse createResource = null;
+ Resource resource = null;
+ String payloadName = "orig2G.csar";
+ String rootPath = System.getProperty("user.dir");
+ Path path = Paths.get(rootPath + csarFolderPath + "orig2G.csar");
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setName("TEST01");
+ resourceDetails.setCsarUUID("orig2G.csar");
+ resourceDetails.setCsarVersion("1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ // create new resource from Csar
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ // update scar with new artifacts
+ path = Paths.get(rootPath + csarFolderPath + "orig2G_update.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setDescription("update");
+ resourceDetails.setCsarVersion("2");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ }
+
+ @Test
+ public void createUpdateImportResourceFromCsarWithArtifactsGroupNamingTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes;
+ ResourceReqDetails resourceDetails;
+ RestResponse updateResource;
+ RestResponse createResource;
+ Resource resource;
+
+ // back original scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "VF_RI2_G4_withArtifacts_group_naming_a.csar", "VF_RI2_G4_withArtifacts_group_naming.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("TEST01");
+ resourceDetails.setCsarUUID("VF_RI2_G4_withArtifacts_group_naming.csar");
+ resourceDetails.setCsarVersion("1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ // create new resource from Csar
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ List<GroupDefinition> groups = resource.getGroups();
+ assertTrue(groups != null && groups.size() == 6);
+ assertTrue(groups.stream()
+ .filter(g -> g.getType().equals(Constants.DEFAULT_GROUP_VF_MODULE)
+ && !Pattern.compile(Constants.MODULE_NEW_NAME_PATTERN).matcher(g.getName()).matches())
+ .count() == 0);
+ // update scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "VF_RI2_G4_withArtifacts_group_naming_delete_update.csar", "VF_RI2_G4_withArtifacts_group_naming.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ resourceDetails.setDescription("BLA BLA BLA");
+ resourceDetails.setCsarVersion("2");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ groups = resource.getGroups();
+ assertTrue(groups != null && groups.size() == 5);
+ // back original scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "VF_RI2_G4_withArtifacts_group_naming_a.csar", "VF_RI2_G4_withArtifacts_group_naming.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ resourceDetails.setDescription("BLA BLA BLA");
+ resourceDetails.setCsarVersion("3");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ groups = resource.getGroups();
+ assertTrue(groups != null && groups.size() == 6);
+ assertTrue(groups.stream()
+ .filter(g -> g.getType().equals(Constants.DEFAULT_GROUP_VF_MODULE)
+ && !Pattern.compile(Constants.MODULE_NEW_NAME_PATTERN).matcher(g.getName()).matches())
+ .count() == 0);
+ }
+
+ @Test
+ public void createUpdateDeleteAllRequiredArtifactsTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes;
+ ResourceReqDetails resourceDetails;
+ RestResponse updateResource;
+ RestResponse createResource;
+ Resource resource;
+ String artifactName = "heatnested7";
+
+ ImportReqDetails resourceDetails0 = ElementFactory.getDefaultImportResource();
+ createResource = importUserDefinedNodeType(userDefinedNodeYaml, sdncModifierDetails, resourceDetails0);
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+
+ // back original scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails, "orig2GV001_a.csar", "orig2GV001.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("TEST01");
+ resourceDetails.setCsarUUID("orig2GV001.csar");
+ resourceDetails.setCsarVersion("1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ // create new resource from Csar
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertTrue(resource.getDeploymentArtifacts().get(artifactName).getRequiredArtifacts().size() == 2);
+ List<GroupDefinition> groups = resource.getGroups();
+ // update scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "orig2GV006-remove-all-nested-artifacts.csar", "orig2GV001.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ resourceDetails.setDescription("BLA BLA BLA");
+ resourceDetails.setCsarVersion("2");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertTrue(resource.getDeploymentArtifacts().get(artifactName).getRequiredArtifacts().size() == 0);
+ groups = resource.getGroups();
+ // back original scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails, "orig2GV001_a.csar", "orig2GV001.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ }
+
+ // First create from orig2GV006-remove-all-nested-artifacts.csar (without
+ // requiredArtifact)
+ // Submit for testing
+ // Login as tester -> Certification
+ // Login as designer
+ // then update to orig2GV008-change-nested-oam-fileContent.csar (with
+ // requiredArtifact)
+ // Expected: requiredArtifact: ["hot-nimbus-psm_v1.0.yaml",
+ // "hot-nimbus-swift-container_v1.0.yaml"]
+ // Actual: no requiredArtifact
+ @Test
+ public void createUpdateAddRequiredArtifactsTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse copyRes;
+ ResourceReqDetails resourceDetails;
+ RestResponse updateResource;
+ RestResponse createResource;
+ Resource resource;
+ String artifactName = "heatnested7";
+
+ ImportReqDetails resourceDetails0 = ElementFactory.getDefaultImportResource();
+ createResource = importUserDefinedNodeType(userDefinedNodeYaml, sdncModifierDetails, resourceDetails0);
+ BaseRestUtils.checkCreateResponse(createResource);
+ createResource = LifecycleRestUtils.certifyResource(resourceDetails0);
+ BaseRestUtils.checkSuccess(createResource);
+
+ // back original scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "orig2GV006-remove-all-nested-artifacts.csar", "orig2GV001.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("TEST01");
+ resourceDetails.setCsarUUID("orig2GV001.csar");
+ resourceDetails.setCsarVersion("1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ // create new resource from Csar
+ createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ createResource = LifecycleRestUtils.certifyResource(resourceDetails);
+ BaseRestUtils.checkSuccess(createResource);
+
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ assertTrue(resource.getDeploymentArtifacts().get(artifactName).getRequiredArtifacts().size() == 0);
+ List<GroupDefinition> groups = resource.getGroups();
+ // update scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails,
+ "orig2GV008-change-nested-oam-fileContent.csar", "orig2GV001.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ resourceDetails.setDescription("BLA BLA BLA");
+ resourceDetails.setCsarVersion("2");
+ updateResource = ResourceRestUtils.updateResource(resourceDetails, sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(updateResource);
+ resource = ResponseParser.parseToObjectUsingMapper(updateResource.getResponse(), Resource.class);
+ assertTrue(resource.getDeploymentArtifacts().get(artifactName).getRequiredArtifacts().size() == 2);
+ groups = resource.getGroups();
+ // back original scar
+ copyRes = ImportCsarResourceTest.copyCsarRest(sdncModifierDetails, "orig2GV001_a.csar", "orig2GV001.csar");
+ BaseRestUtils.checkSuccess(copyRes);
+ }
+
+ private RestResponse importUserDefinedNodeType(String payloadName, User sdncModifierDetails,
+ ImportReqDetails resourceDetails) throws Exception {
+
+ Path path = Paths.get(rootPath + csarFolderPath + payloadName);
+ byte[] data = Files.readAllBytes(path);
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(ResourceTypeEnum.VFC.name());
+ return ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/inputs/InputsApiTests.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/inputs/InputsApiTests.java
new file mode 100644
index 0000000000..345b81eb3e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/inputs/InputsApiTests.java
@@ -0,0 +1,225 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.inputs;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstInputsMap;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.InputsRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.BaseValidationUtils;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import fj.data.Either;
+
+/**
+ * CI-Tests for inputs
+ * @author il0695
+ *
+ */
+public class InputsApiTests extends ComponentBaseTest {
+
+ private static String inputCsar1 = "FCGI_with_inputs.csar";
+ private static String inputCsar2 = "LDSA1_with_inputs.csar";
+ private static User sdncDesignerDetails = null;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ /**
+ * Constructor
+ */
+ public InputsApiTests() {
+ super(name, InputsApiTests.class.getName());
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ }
+
+ /**
+ * Create VF with inputs from CSAR file
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCreateResourceInstanceWithInputsFromCsar() throws Exception {
+ Resource vf = AtomicOperationUtils.importResourceFromCSAR(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, inputCsar1);
+ assertTrue("Success creating VF from CSAR", !vf.getInputs().isEmpty());
+ }
+
+ /**
+ * Create service and add to it VF instance with inputs
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testAddVfInstanceWithInputsToService() throws Exception {
+ createServiceWithVFInstanceWithInputs();
+ }
+
+ /**
+ * General test to check most functionality of inputs
+ * <ul>
+ * <li>Create service with VF instance that has inputs)</li>
+ * <li>Get all inputs of VF instance</li>
+ * <li>Add inputs to service</li>
+ * <li>Get service inputs</li>
+ * <li>Delete service inputs</li>
+ * </ul>
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testInputsMainFunctionality() throws Exception {
+ Service service = createServiceWithVFInstanceWithInputs();
+ int totalInputsBeforeAdd = service.getInputs().size();
+
+ // Get component instances
+ RestResponse getInstancesResponse = ComponentInstanceRestUtils.getComponentInstances(ComponentTypeEnum.SERVICE, service.getUniqueId(), sdncDesignerDetails);
+ BaseValidationUtils.checkSuccess(getInstancesResponse);
+ List<ComponentInstance> serviceInstances = new Gson().fromJson(getInstancesResponse.getResponse(), new TypeToken<ArrayList<ComponentInstance>>(){}.getType());
+
+ // Get all inputs of first instance
+ ComponentInstance vfInstance = serviceInstances.get(0);
+ RestResponse getComponentInstanceInputsResponse = InputsRestUtils.getComponentInstanceInputs(service, vfInstance);
+ BaseValidationUtils.checkSuccess(getComponentInstanceInputsResponse);
+ List<InputDefinition> instanceInputs = new Gson().fromJson(getComponentInstanceInputsResponse.getResponse(), new TypeToken<ArrayList<InputDefinition>>(){}.getType());
+
+ // Take only the 2 first inputs
+ List<InputDefinition> inputsToAdd = instanceInputs.stream().limit(2).collect(Collectors.toList());
+
+ // Build component instances input map to add to server
+ ComponentInstInputsMap buildComponentInstInputsMap = buildComponentInstInputsMap(vfInstance.getUniqueId(), inputsToAdd);
+ RestResponse addInputResponse = InputsRestUtils.addInput(service, buildComponentInstInputsMap, UserRoleEnum.DESIGNER);
+ BaseValidationUtils.checkSuccess(addInputResponse);
+
+ // Get service inputs count
+ RestResponse getComponentInputsResponse = InputsRestUtils.getComponentInputs(service);
+ BaseValidationUtils.checkSuccess(getComponentInputsResponse);
+ List<InputDefinition> serviceInputsAfterAdd = new Gson().fromJson(getComponentInputsResponse.getResponse(), new TypeToken<ArrayList<InputDefinition>>(){}.getType());
+ if (serviceInputsAfterAdd.size()-totalInputsBeforeAdd!=2) {
+ assertTrue("Error adding inputs to service (service should have 2 inputs)", false);
+ }
+
+ // Delete 1 input from service
+ RestResponse deleteInputFromComponentResponse = InputsRestUtils.deleteInputFromComponent(service, serviceInputsAfterAdd.get(0).getUniqueId());
+ BaseValidationUtils.checkSuccess(deleteInputFromComponentResponse);
+
+ // Get service inputs count after delete
+ RestResponse getComponentInputsResponseAfterDelete = InputsRestUtils.getComponentInputs(service);
+ BaseValidationUtils.checkSuccess(getComponentInputsResponseAfterDelete);
+ List<InputDefinition> serviceInputsAfterDelete = new Gson().fromJson(getComponentInputsResponseAfterDelete.getResponse(), new TypeToken<ArrayList<InputDefinition>>(){}.getType());
+ if (serviceInputsAfterDelete.size()-totalInputsBeforeAdd!=1) {
+ assertTrue("Error deleting inputs from service (service should have 1 input)", false);
+ }
+
+ assertTrue("Success testing inputs main functionality", true);
+ }
+
+ /**
+ * Private method to create service with VF instance that has inputs
+ * This is private method to be used by multiple tests
+ *
+ * @return {@link org.openecomp.sdc.be.model}
+ * @throws Exception
+ * @throws IOException
+ */
+ private Service createServiceWithVFInstanceWithInputs() throws Exception, IOException {
+ // Create default service
+ Either<Service, RestResponse> createDefaultServiceEither = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true);
+ if (createDefaultServiceEither.isRight()){
+ assertTrue("Error creating default service", false);
+ }
+ Service service = createDefaultServiceEither.left().value();
+
+ // Create VF from CSAR file
+ Resource vfWithInputs = AtomicOperationUtils.importResourceFromCSAR(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, inputCsar2);
+
+ // Certify VF
+ Pair<Component, RestResponse> changeComponentState = AtomicOperationUtils.changeComponentState(vfWithInputs, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ assertTrue("response code is BaseRestUtils.STATUS_CODE_SUCCESS, returned :" + changeComponentState.getRight().getErrorCode(), changeComponentState.getRight().getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ // Add VF instance to service
+ Either<ComponentInstance, RestResponse> addComponentInstanceToComponentContainerEither = AtomicOperationUtils.addComponentInstanceToComponentContainer(vfWithInputs, service, UserRoleEnum.DESIGNER, true);
+ if (addComponentInstanceToComponentContainerEither.isRight()){
+ assertTrue("Error adding VF to service", false);
+ }
+
+ // Get service response
+ ServiceReqDetails serviceDetails = new ServiceReqDetails(service);
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+
+ // Get VF instance from service
+ ComponentInstance vfInstance = service.getComponentInstances().get(0);
+ if (vfInstance!=null){
+ assertTrue("Success creating service with VF instance", true);
+ } else {
+ assertTrue("Error creating service with VF instance", false);
+ }
+ return service;
+ }
+
+ /**
+ * Return default ComponentInstInputsMap
+ *
+ * @param addToInput
+ * @param inputs
+ * @return {@link org.openecomp.sdc.be.model.ComponentInstInputsMap}
+ */
+ private ComponentInstInputsMap buildComponentInstInputsMap (String addToInput, List<InputDefinition> inputs) {
+ Map<String, List<InputDefinition>> map = new HashMap<>();
+ map.put(addToInput, inputs);
+ ComponentInstInputsMap componentInstInputsMap = new ComponentInstInputsMap();
+ componentInstInputsMap.setComponentInstanceInputsMap(map);
+ return componentInstInputsMap;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/lifecycle/LCSbaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/lifecycle/LCSbaseTest.java
new file mode 100644
index 0000000000..e15652dddf
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/lifecycle/LCSbaseTest.java
@@ -0,0 +1,274 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.lifecycle;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+
+import org.apache.log4j.lf5.util.ResourceUtils;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.ArtifactUtils;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.annotations.BeforeMethod;
+
+/**
+ *
+ * @author al714h
+ *
+ * resourceDetails - create, Add Heat, certify resourceDetails1 - create
+ * resource, LCS - CheckOut serviceDetails - create, add RI from
+ * resourceDetails serviceDetails2 - create, add RI from resourceDetails
+ * serviceDetailsEmpty - create, LCS - CheckOut serviceDetailsEmpty2 -
+ * create, LCS - CheckOut
+ *
+ */
+public abstract class LCSbaseTest extends ComponentBaseTest {
+
+ protected ResourceReqDetails resourceDetails;
+ protected ResourceReqDetails resourceDetails1;
+ protected ServiceReqDetails serviceDetails;
+ protected ServiceReqDetails serviceDetails2;
+ protected ServiceReqDetails serviceDetailsEmpty;
+ protected ServiceReqDetails serviceDetailsEmpty2;
+ protected ComponentInstanceReqDetails componentInstanceReqDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails2;
+ protected User sdncDesignerDetails1;
+ protected User sdncDesignerDetails2;
+ protected static User sdncTesterDeatails1;
+ protected User sdncAdminDetails1;
+ protected ArtifactReqDetails heatArtifactDetails;
+ protected ArtifactReqDetails heatVolArtifactDetails;
+ protected ArtifactReqDetails heatNetArtifactDetails;
+
+ protected ArtifactReqDetails defaultArtifactDetails;
+ protected ResourceUtils resourceUtils;
+ protected ArtifactUtils artifactUtils;
+
+ // protected static ServiceUtils serviceUtils = new ServiceUtils();
+ public LCSbaseTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+
+ initializeMembers();
+
+ createComponents();
+
+ }
+
+ public void initializeMembers() throws IOException, Exception {
+ resourceDetails = ElementFactory.getDefaultResource();
+ // resourceDetails =
+ // ElementFactory.getDefaultResource("myNewResource1234567890",
+ // NormativeTypesEnum.ROOT, ResourceServiceCategoriesEnum.ROUTERS,
+ // UserRoleEnum.DESIGNER.getUserId());
+ resourceDetails1 = ElementFactory.getDefaultResource("secondResource", NormativeTypesEnum.ROOT);
+ serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails2 = ElementFactory.getDefaultService("newTestService2", ServiceCategoriesEnum.MOBILITY, "al1976");
+ serviceDetailsEmpty = ElementFactory.getDefaultService("newEmptyService", ServiceCategoriesEnum.MOBILITY,
+ "al1976");
+ serviceDetailsEmpty2 = ElementFactory.getDefaultService("newEmptyService2", ServiceCategoriesEnum.MOBILITY,
+ "al1976");
+ sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncDesignerDetails2 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2);
+ sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncAdminDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ heatNetArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_NET.getType());
+ heatVolArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT_VOL.getType());
+ componentInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+ resourceInstanceReqDetails2 = ElementFactory.getDefaultComponentInstance();
+
+ }
+
+ protected void createComponents() throws Exception {
+
+ RestResponse response = ResourceRestUtils.createResource(resourceDetails1, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetails1.getUniqueId());
+
+ response = ResourceRestUtils.createResource(resourceDetails, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetails.getUniqueId());
+
+ response = ServiceRestUtils.createService(serviceDetails, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("service uniqueId is null:", serviceDetails.getUniqueId());
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1,
+ resourceDetails.getUniqueId());
+ assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ // certified resource
+ response = LCSbaseTest.certifyResource(resourceDetails, sdncDesignerDetails1);
+ assertTrue("certify resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ // add resource instance with HEAT deployment artifact to the service
+ componentInstanceReqDetails.setComponentUid(resourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(componentInstanceReqDetails, sdncDesignerDetails1,
+ serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertTrue("response code is not 201, returned: " + response.getErrorCode(), response.getErrorCode() == 201);
+
+ response = ServiceRestUtils.createService(serviceDetails2, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("service uniqueId is null:", serviceDetails2.getUniqueId());
+
+ componentInstanceReqDetails.setComponentUid(resourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(componentInstanceReqDetails, sdncDesignerDetails1,
+ serviceDetails2.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertTrue("response code is not 201, returned: " + response.getErrorCode(), response.getErrorCode() == 201);
+
+ response = ServiceRestUtils.createService(serviceDetailsEmpty, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("service uniqueId is null:", serviceDetailsEmpty.getUniqueId());
+
+ response = ServiceRestUtils.createService(serviceDetailsEmpty2, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("service uniqueId is null:", serviceDetailsEmpty2.getUniqueId());
+
+ DbUtils.cleanAllAudits();
+
+ }
+
+ public static RestResponse certifyResource(ResourceReqDetails resourceDetails, User user) throws Exception {
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user.getUserId(),
+ LifeCycleStatesEnum.CHECKIN);
+ // if (restResponseResource.getErrorCode() == 200){
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user.getUserId(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ // }else
+ // return restResponseResource;
+ sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ if (restResponseResource.getErrorCode() == 200) {
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails,
+ sdncTesterDeatails1.getUserId(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponseResource;
+ if (restResponseResource.getErrorCode() == 200) {
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails,
+ sdncTesterDeatails1.getUserId(), LifeCycleStatesEnum.CERTIFY);
+ }
+ return restResponseResource;
+ }
+
+ public static RestResponse certifyService(ServiceReqDetails serviceDetails, User user) throws Exception {
+ RestResponse restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails, user,
+ LifeCycleStatesEnum.CHECKIN);
+ // if (restResponseService.getErrorCode() == 200){
+ restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails, user,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ // }else
+ // return restResponseService;
+
+ sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ if (restResponseService.getErrorCode() == 200) {
+ restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails, sdncTesterDeatails1,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponseService;
+ if (restResponseService.getErrorCode() == 200) {
+ restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails, sdncTesterDeatails1,
+ LifeCycleStatesEnum.CERTIFY);
+ }
+ return restResponseService;
+ }
+
+ protected static RestResponse raiseResourceToTargetVersion(ResourceReqDetails resourceDetails, String targetVersion,
+ User user) throws Exception {
+ return raiseResourceToTargetVersion(resourceDetails, targetVersion, null, user);
+ }
+
+ protected static RestResponse raiseResourceToTargetVersion(ResourceReqDetails resourceDetails, String targetVersion,
+ RestResponse prevResponse, User user) throws Exception {
+
+ String[] splitParts = targetVersion.split("\\.");
+
+ int version = Integer.parseInt(splitParts[1]);
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+
+ if (prevResponse != null) {
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(prevResponse.getResponse());
+ if (resourceRespJavaObject.getLifecycleState().equals(LifecycleStateEnum.CERTIFIED)) {
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails,
+ user.getUserId(), LifeCycleStatesEnum.CHECKOUT);
+ }
+ }
+
+ RestResponse restResponseResource = null;
+ for (int i = 0; i < (version - 1); i++) {
+
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user, null,
+ LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ if (restResponseResource.getErrorCode() == 200) {
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user.getUserId(),
+ LifeCycleStatesEnum.CHECKOUT);
+ if (restResponseResource.getErrorCode() == 200) {
+
+ } else
+ break;
+
+ } else
+ break;
+
+ }
+
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user, null,
+ LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+ assertEquals("Check response code ", 200, restResponseResource.getErrorCode().intValue());
+ return restResponseResource;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ChangeServiceInstanceVersionTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ChangeServiceInstanceVersionTest.java
new file mode 100644
index 0000000000..6a4fa3ba55
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ChangeServiceInstanceVersionTest.java
@@ -0,0 +1,1478 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_MISSING_INFORMATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_NOT_FOUND;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS_DELETE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_UNSUPPORTED_ERROR;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentInstanceBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ChangeServiceInstanceVersionTest extends ComponentInstanceBaseTest {
+
+ protected ArtifactReqDetails heatArtifactDetails;
+ public String firstVfInstanceUniqueId;
+ public String firstVfInstanceName;
+ public String secondVfInstanceUniqueId;
+ public String secoundVfInstanceName;
+ public String serviceInstanceToReplaceUniqueId;
+ public String expectedServiceName;
+ public String expectedPosX;
+ public String expectedPosY;
+ public String actualServiceInstanceName;
+ public String actualPosX;
+ public String actualPosY;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ChangeServiceInstanceVersionTest() {
+ super(name, ChangeServiceInstanceVersionTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+ firstVfInstanceName = null;
+ secoundVfInstanceName = null;
+ firstVfInstanceUniqueId = null;
+ secondVfInstanceUniqueId = null;
+ serviceInstanceToReplaceUniqueId = null;
+ expectedServiceName = null;
+ expectedPosX = null;
+ expectedPosY = null;
+ actualServiceInstanceName = null;
+ actualPosX = null;
+ actualPosY = null;
+ init();
+ createComponents();
+ }
+
+ private void createComponents() throws Exception {
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ createAtomicResource(resourceDetailsVFC_01);
+ changeResourceStateToCertified(resourceDetailsVFC_01);
+ createAtomicResource(resourceDetailsCP_01);
+ changeResourceStateToCertified(resourceDetailsCP_01);
+ createAtomicResource(resourceDetailsVL_01);
+ changeResourceStateToCertified(resourceDetailsVL_01);
+ createAtomicResource(resourceDetailsVFC_02);
+ changeResourceStateToCertified(resourceDetailsVFC_02);
+ createAtomicResource(resourceDetailsCP_02);
+ changeResourceStateToCertified(resourceDetailsCP_02);
+ createAtomicResource(resourceDetailsVL_02);
+ changeResourceStateToCertified(resourceDetailsVL_02);
+ createVF(resourceDetailsVF_02);
+ createVF(resourceDetailsVF_01);
+ // create check-In services
+ createService(serviceDetails_01);
+ createService(serviceDetails_02);
+ createService(serviceDetails_03);
+ createProduct(productDetails_01);
+ createProduct(productDetails_02);
+
+ // add resourceDetailsCP_01 ,resourceDetailsVFC_01 and
+ // resourceDetailsCP_01 to resourceDetailsVF_01 and certify
+ // resourceDetailsVF_01
+ certifyVf(resourceDetailsVF_01, resourceDetailsVFC_02, resourceDetailsCP_01);
+ // add resourceDetailsCP_02 ,resourceDetailsVFC_02 and
+ // resourceDetailsVL_02 to resourceDetailsVF_02 and certify
+ // resourceDetailsVF_02
+ certifyVf(resourceDetailsVF_02, resourceDetailsVFC_02, resourceDetailsCP_02);
+ RestResponse createVFInstanceResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails); // serviceDetails_01 has certified
+ // resourceDetailsVF_01
+ firstVfInstanceName = ResponseParser.getNameFromResponse(createVFInstanceResponse);
+ createVFInstanceResponse = createVFInstanceDuringSetup(serviceDetails_02, resourceDetailsVF_02,
+ sdncDesignerDetails); // serviceDetails_01 has certified
+ // resourceDetailsVF_02
+ secoundVfInstanceName = ResponseParser.getUniqueIdFromResponse(createVFInstanceResponse);
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ }
+
+ @Test
+ public void changeServiceInstanceVersionByPm() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // get the new VF instance uniqueId after checkout service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ @Test
+ public void changeServiceInstanceVersionByAdmin() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // get the new VF instance uniqueId after checkout service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // service [0.2] state to CERTIFICATIONREQUEST
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncAdminDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncAdminDetails, ComponentTypeEnum.PRODUCT,
+ true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncAdminDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ @Test
+ public void changeServiceInstanceToOlderVersion() throws Exception {
+ // Get VF Instance UniquId [Service version 0.1]
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ ComponentInstance actualComponentInstance = service.getComponentInstances().get(0);
+ firstVfInstanceUniqueId = actualComponentInstance.getUniqueId();
+ String serviceOlderVersionUniquId = ResponseParser.getUniqueIdFromResponse(getServiceResponse);
+
+ // Checkout service [0.2]
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01,
+ sdncDesignerDetails, LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // get the new VF instance uniqueId after checkout service
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // Adding service instance (serviceDetails_01 V0.2) to product without
+ // saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to Older version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceOlderVersionUniquId, sdncPmDetails1, ComponentTypeEnum.PRODUCT,
+ true);
+ // RestResponse changeServiceInstanceVersionResponse =
+ // changeServiceInstanceVersion(productDetails_01.getUniqueId(),
+ // serviceInstanceToReplaceUniqueId , serviceNewUniqueUid,
+ // sdncPmDetails1, ComponentTypeEnum.PRODUCT , true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ // DE190201
+ @Test
+ public void changeServiceInstanceVersionToCertifiedVersion() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ /*
+ * String serviceNewUniqueUid =
+ * ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ * serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ */
+ // get the new VF instance uniqueId after checkout service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ // RestResponse changeServiceInstanceVersionResponse =
+ // changeServiceInstanceVersion(productDetails_01.getUniqueId(),
+ // serviceInstanceToReplaceUniqueId , serviceNewUniqueUid,
+ // sdncPmDetails1, ComponentTypeEnum.PRODUCT , true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ // DE191927
+ @Test(enabled = false)
+ public void changeServiceInstanceVersionThenReCheckInProduct() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // get the new VF instance uniqueId after checkout service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ /////////////////////
+ productOldUniqueId = productDetails_01.getUniqueId();
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ /////////////////////////////////////////////
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ @Test
+ public void changeServiceInstanceToHisVersion() throws Exception {
+ // Get VF Instance UniquId [Service version 0.1]
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ String serviceOlderVersionUniquId = ResponseParser.getUniqueIdFromResponse(getServiceResponse);
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // get the new VF instance uniqueId after checkout service
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceOlderVersionUniquId, sdncPmDetails1, ComponentTypeEnum.PRODUCT,
+ true);
+ // RestResponse changeServiceInstanceVersionResponse =
+ // changeServiceInstanceVersion(productDetails_01.getUniqueId(),
+ // serviceInstanceToReplaceUniqueId , serviceNewUniqueUid,
+ // sdncPmDetails1, ComponentTypeEnum.PRODUCT , true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ @Test
+ public void changeServiceInstanceVersionByAdminNotByProductOwner() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncAdminDetails, ComponentTypeEnum.PRODUCT,
+ true);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+
+ }
+
+ @Test
+ public void changeServiceInstanceVersionByPmNotByProductOwner() throws Exception {
+ // Adding service instance (serviceDetails_01) to product AND --->
+ // saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // change uniqueId after product check-out in expected Req&Cap
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ updateNewComponentInstanceId(createServiceInstanceResp, productNewUniqueId);
+ // CHANGE Service Instance VERSION BY NON PRODUCT OWNER (sdncPmDetails1
+ // instead sdncPmDetails1)
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(
+ productDetails_01.getUniqueId(), serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails2,
+ ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void changeServiceInstanceVersionByTester() throws Exception {
+ // Adding service instance (serviceDetails_01) to product AND --->
+ // saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceToReplaceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // change uniqueId after product check-out in expected Req&Cap
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // CHANGE Service Instance VERSION BY NON PRODUCT OWNER (sdncPmDetails1
+ // instead sdncPmDetails1)
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(
+ productDetails_01.getUniqueId(), serviceInstanceToReplaceUniqueId, serviceNewUniqueUid,
+ sdncTesterDetails, ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void changeServiceInstanceVersionProductIsNotCheckOut() throws Exception {
+ // Adding service instance (serviceDetails_01) to product AND --->
+ // saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceToReplaceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // CHANGE Service Instance VERSION for Non checkedOut product
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productOldUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeServiceInstanceVersionResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ // DE191727
+ @Test(enabled = false)
+ public void changeServiceInstanceVersionServiceIsInCheckOutState() throws Exception {
+ // Adding service instance (serviceDetails_01) to product AND --->
+ // saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceToReplaceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // change uniqueId after product check-out in expected Req&Cap
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // CHANGE Service Instance VERSION to service in checkOut state
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(
+ productDetails_01.getUniqueId(), serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1,
+ ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(serviceNewUniqueUid);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), varibales,
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+
+ }
+
+ @Test
+ public void changeServiceInstanceVersionServiceInstanceDoesNotExist() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // change service instance to newer version
+ String serviceUniqueUidNotExist = "1234567890";
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceUniqueUidNotExist, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(serviceUniqueUidNotExist);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND.name(), varibales,
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ // DE189392
+ @Test(enabled = false)
+ public void changeServiceInstanceNonExistingProduct() throws Exception {
+ // Adding service instance (serviceDetails_01) to product saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // change service instance to newer version - Non existing Product
+ String productNewUniqueIdNotExist = "1234567890";
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueIdNotExist,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(productNewUniqueIdNotExist);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND.name(), varibales,
+ changeServiceInstanceVersionResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void changeServiceInstanceVersionToNonExisitingServiceVersion() throws Exception {
+ // Adding service instance (serviceDetails_01) to product saving Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ updateNewComponentInstanceId(createServiceInstanceResp, productNewUniqueId);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // change service instance to Non-existing version
+ String serviceUniqueUidNotExist = "1234567890";
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceUniqueUidNotExist, sdncPmDetails1, ComponentTypeEnum.PRODUCT,
+ true);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(serviceUniqueUidNotExist);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), varibales,
+ changeServiceInstanceVersionResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void changeServiceInstanceComponentTypeIsNotProduct() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version for NON-Component Type =
+ // Product (e.g. service)
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.SERVICE, true);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), varibales,
+ changeServiceInstanceVersionResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void changeServiceInstanceComponentTypeNotSupported() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version for NON-Component Type =
+ // Product (e.g. service)
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1,
+ ComponentTypeEnum.SERVICE_INSTANCE, true);
+ assertEquals("Check response code ", STATUS_CODE_UNSUPPORTED_ERROR,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("null");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.UNSUPPORTED_ERROR.name(), varibales,
+ changeServiceInstanceVersionResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void SeveralServiceInstanceFromSameServiceVersionChangeVersionOnlyForOneServiceInstance() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstanceDuringSetup(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ serviceDetails_01.setUniqueId(serviceNewUniqueUid);
+ // get the new VF instance uniqueId after checkout service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01.getUniqueId(), sdncPmDetails1);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ List<ComponentInstance> serviceComponentInstances = service.getComponentInstances();
+ for (ComponentInstance component : serviceComponentInstances) {
+ if (component.getName().equals(firstVfInstanceName)) {
+ firstVfInstanceUniqueId = component.getUniqueId();
+ }
+ }
+ assertTrue(firstVfInstanceUniqueId != null);
+ // delete resource instance (resourceDetailsVF_01) from Service
+ RestResponse deleteVfFromServiceResponse = deleteVFInstanceDuringSetup(firstVfInstanceUniqueId,
+ serviceDetails_01, sdncDesignerDetails);
+ assertTrue(deleteVfFromServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Add different VF instance (resourceDetailsVF_02) to Service
+ RestResponse restResponse = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(restResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get service instance new uniqueId , name and position after checkout
+ // product
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, sdncPmDetails1, ComponentTypeEnum.PRODUCT, true);
+ ProductRestUtils.checkSuccess(changeServiceInstanceVersionResponse);
+ actualServiceInstanceName = ResponseParser.getNameFromResponse(changeServiceInstanceVersionResponse);
+ actualPosX = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posX");
+ actualPosY = ResponseParser.getValueFromJsonResponse(changeServiceInstanceVersionResponse.getResponse(),
+ "posY");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(changeServiceInstanceVersionResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Verify that Service instance name and position didn't change after
+ // changing service instance version
+ assertTrue(actualServiceInstanceName.equals(expectedServiceName));
+ assertTrue(actualPosX.equals(expectedPosX));
+ assertTrue(actualPosY.equals(expectedPosY));
+ }
+
+ @Test
+ public void changeServiceInstanceVersionByNonAsdcUser() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ nonAsdcUser.setUserId("bt760h");
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, nonAsdcUser, ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void changeServiceInstanceVersionEmptyUserId() throws Exception {
+ // Adding service instance (serviceDetails_01) to product without saving
+ // Req&Cap
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Adding service instance (serviceDetails_02) to product AND ---> Save
+ // Req&Cap
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // check-in product
+ RestResponse changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productOldUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Checkout service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ String serviceNewUniqueUid = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ // Check-In service [0.2]
+ changeStatusResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeStatusResponse);
+ // check-out product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(changeStatusResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ nonAsdcUser.setUserId("");
+ // change service instance to newer version
+ RestResponse changeServiceInstanceVersionResponse = changeServiceInstanceVersion(productNewUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceNewUniqueUid, nonAsdcUser, ComponentTypeEnum.PRODUCT, true);
+ assertEquals("Check response code ", STATUS_CODE_MISSING_INFORMATION,
+ changeServiceInstanceVersionResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), new ArrayList<String>(),
+ changeServiceInstanceVersionResponse.getResponse());
+ // Check-in product
+ changeStatusResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeStatusResponse);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ ////////////////////////////////////
+ private void updateNewComponentInstanceId(RestResponse createServiceInstanceResp, String productNewUniqueId)
+ throws Exception {
+ String firstServiceInstanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "normalizedName");
+ RestResponse getProductResponse = ProductRestUtils.getProduct(productNewUniqueId, sdncPmDetails1.getUserId());
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductResponse.getResponse(), Product.class);
+ List<ComponentInstance> componentInstances = product.getComponentInstances();
+ for (ComponentInstance component : componentInstances) {
+ if (component.getNormalizedName().equals(firstServiceInstanceNormalizedName)) {
+ serviceInstanceToReplaceUniqueId = component.getUniqueId();
+ expectedServiceName = component.getName();
+ expectedPosX = component.getPosX();
+ expectedPosY = component.getPosY();
+ }
+ }
+ assertTrue(serviceInstanceToReplaceUniqueId != null);
+ }
+
+ private RestResponse changeResourceStateToCertified(ResourceReqDetails resourceDetails) throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponse;
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ if (restResponse.getErrorCode() == 200) {
+ String newVersion = ResponseParser.getVersionFromResponse(restResponse);
+ resourceDetails.setVersion(newVersion);
+ resourceDetails.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ resourceDetails.setLastUpdaterUserId(sdncTesterDetails.getUserId());
+ resourceDetails.setLastUpdaterFullName(sdncTesterDetails.getFullName());
+ String uniqueIdFromRresponse = ResponseParser.getValueFromJsonResponse(restResponse.getResponse(),
+ "uniqueId");
+ resourceDetails.setUniqueId(uniqueIdFromRresponse);
+ }
+ }
+ return restResponse;
+ }
+
+ private void certifyVf(ResourceReqDetails resource, ResourceReqDetails computeResource,
+ ResourceReqDetails cpResource) throws Exception {
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, cpResource,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, computeResource,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails,
+ sdncDesignerDetails, resource.getUniqueId());
+ ResourceRestUtils.checkSuccess(response);
+
+ String capOwnerId = toCompInstId;
+ User user = sdncDesignerDetails;
+ ComponentTypeEnum containerCompType = ComponentTypeEnum.RESOURCE;
+
+ fulfillCpRequirement(resource, fromCompInstId, toCompInstId, capOwnerId, user, containerCompType);
+
+ RestResponse changeResourceStateToCertified = changeResourceStateToCertified(resource);
+ ResourceRestUtils.checkSuccess(changeResourceStateToCertified);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductBaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductBaseTest.java
new file mode 100644
index 0000000000..7891f4a46b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductBaseTest.java
@@ -0,0 +1,195 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.PRODUCT_COMPONENT_TYPE;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.annotations.BeforeMethod;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+public abstract class ProductBaseTest extends ComponentBaseTest {
+ User productStrategistUser1;
+ User productStrategistUser2;
+ User productStrategistUser3;
+ User productManager1;
+ User productManager2;
+ User adminUser;
+ User designerUser;
+
+ // Category1->Subcategory1->Grouping1
+ protected List<CategoryDefinition> defaultCategories;
+
+ protected static String auditAction;
+ protected static ComponentOperationEnum operation;
+
+ public ProductBaseTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ @BeforeMethod
+ public void beforeProductTest() throws IOException, Exception {
+ productStrategistUser1 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+ productStrategistUser2 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST2);
+ productStrategistUser3 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST3);
+ productManager1 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ productManager2 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER2);
+ adminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ designerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ createDefaultChain();
+ }
+
+ private void createDefaultChain() throws Exception {
+ CategoryDefinition productCategoryDefinition = ElementFactory.getDefaultCategory();
+ SubCategoryDefinition productSubCategoryDefinition = ElementFactory.getDefaultSubCategory();
+ GroupingDefinition productGroupingDefinition = ElementFactory.getDefaultGroup();
+ productCategoryDefinition.addSubCategory(productSubCategoryDefinition);
+ productSubCategoryDefinition.addGrouping(productGroupingDefinition);
+ List<CategoryDefinition> definitionsList = new ArrayList<>();
+ definitionsList.add(productCategoryDefinition);
+ defaultCategories = createCategoriesChain(definitionsList);
+ }
+
+ private List<CategoryDefinition> createCategoriesChain(List<CategoryDefinition> categoryDefinitions) throws Exception {
+ for (CategoryDefinition categoryDefinition : categoryDefinitions) {
+ RestResponse createCategory = CategoryRestUtils.createCategory(categoryDefinition, productStrategistUser1, PRODUCT_COMPONENT_TYPE);
+ int status = createCategory.getErrorCode().intValue();
+ if (status == BaseRestUtils.STATUS_CODE_CREATED) {
+ String categoryId = ResponseParser.getUniqueIdFromResponse(createCategory);
+ categoryDefinition.setUniqueId(categoryId);
+ }
+ List<SubCategoryDefinition> subcategories = categoryDefinition.getSubcategories();
+ for (SubCategoryDefinition subCategoryDefinition : subcategories) {
+ RestResponse createSubCategory = CategoryRestUtils.createSubCategory(subCategoryDefinition, categoryDefinition, productStrategistUser1, PRODUCT_COMPONENT_TYPE);
+ status = createSubCategory.getErrorCode().intValue();
+ if (status == BaseRestUtils.STATUS_CODE_CREATED) {
+ String subCategoryId = ResponseParser.getUniqueIdFromResponse(createSubCategory);
+ subCategoryDefinition.setUniqueId(subCategoryId);
+ }
+ List<GroupingDefinition> groupings = subCategoryDefinition.getGroupings();
+ for (GroupingDefinition groupingDefinition : groupings) {
+ RestResponse createGroupingRest = CategoryRestUtils.createGrouping(groupingDefinition, subCategoryDefinition, categoryDefinition, productStrategistUser1, PRODUCT_COMPONENT_TYPE);
+ status = createGroupingRest.getErrorCode().intValue();
+ if (status == BaseRestUtils.STATUS_CODE_CREATED) {
+ String groupingId = ResponseParser.getUniqueIdFromResponse(createGroupingRest);
+ groupingDefinition.setUniqueId(groupingId);
+ }
+ }
+ }
+ }
+ RestResponse allCategories = CategoryRestUtils.getAllCategories(productStrategistUser1, PRODUCT_COMPONENT_TYPE);
+ Gson gson = new Gson();
+ List<CategoryDefinition> res = gson.fromJson(allCategories.getResponse(), new TypeToken<List<CategoryDefinition>>() {
+ }.getType());
+ return res;
+ }
+
+ // Category1->Subcategory1->[Grouping1, Grouping11]
+ protected List<CategoryDefinition> addSecondGroupingToDefaultCategory() throws Exception {
+ GroupingDefinition productGroupingDefinition = ElementFactory.getDefaultGroup();
+ productGroupingDefinition.setName("Grouping11");
+ defaultCategories.get(0).getSubcategories().get(0).addGrouping(productGroupingDefinition);
+ return createCategoriesChain(defaultCategories);
+ }
+
+ // Category1->[Subcategory1->[Grouping1,
+ // Grouping11],Subcategory2->[Grouping12]]
+ protected List<CategoryDefinition> addSubcategoryAndGroupingToDefaultCategory() throws Exception {
+ GroupingDefinition groupingDefinition1 = ElementFactory.getDefaultGroup();
+ groupingDefinition1.setName("Grouping11");
+ defaultCategories.get(0).getSubcategories().get(0).addGrouping(groupingDefinition1);
+
+ SubCategoryDefinition subCategory2 = ElementFactory.getDefaultSubCategory();
+ subCategory2.setName("Subcategory2");
+ GroupingDefinition groupingDefinition2 = ElementFactory.getDefaultGroup();
+ groupingDefinition2.setName("Grouping12");
+ subCategory2.addGrouping(groupingDefinition2);
+ defaultCategories.get(0).addSubCategory(subCategory2);
+ return createCategoriesChain(defaultCategories);
+ }
+
+ // [Category1->[Subcategory1->[Grouping1,
+ // Grouping11],Subcategory2->[Grouping12]],
+ // Category2->[Subcategory1->[Grouping1],Subcategory2->[Grouping1]],
+ // Category3->[Subcategory1->[Grouping11],Subcategory2->[Grouping11,
+ // Grouping22]]]
+ protected List<CategoryDefinition> addManyGroupingsDiffCategories() throws Exception {
+ CategoryDefinition category2 = ElementFactory.getDefaultCategory();
+ category2.setName("Category2");
+ CategoryDefinition category3 = ElementFactory.getDefaultCategory();
+ category3.setName("Category3");
+ SubCategoryDefinition subCategory1 = ElementFactory.getDefaultSubCategory();
+ subCategory1.setName("Subcategory1");
+ SubCategoryDefinition subCategory2 = ElementFactory.getDefaultSubCategory();
+ subCategory2.setName("Subcategory2");
+ SubCategoryDefinition subCategory1_2 = ElementFactory.getDefaultSubCategory();
+ subCategory1_2.setName("Subcategory1");
+ SubCategoryDefinition subCategory2_2 = ElementFactory.getDefaultSubCategory();
+ subCategory2_2.setName("Subcategory2");
+ SubCategoryDefinition subCategory1_3 = ElementFactory.getDefaultSubCategory();
+ subCategory1_3.setName("Subcategory1");
+ SubCategoryDefinition subCategory2_3 = ElementFactory.getDefaultSubCategory();
+ subCategory2_3.setName("Subcategory2");
+
+ GroupingDefinition groupingDefinition1 = ElementFactory.getDefaultGroup();
+ groupingDefinition1.setName("Grouping1");
+ GroupingDefinition groupingDefinition11 = ElementFactory.getDefaultGroup();
+ groupingDefinition11.setName("Grouping11");
+ GroupingDefinition groupingDefinition12 = ElementFactory.getDefaultGroup();
+ groupingDefinition12.setName("Grouping12");
+ GroupingDefinition groupingDefinition22 = ElementFactory.getDefaultGroup();
+ groupingDefinition22.setName("Grouping22");
+
+ defaultCategories.get(0).getSubcategories().get(0).addGrouping(groupingDefinition11);
+ subCategory2.addGrouping(groupingDefinition12);
+ defaultCategories.get(0).addSubCategory(subCategory2);
+
+ defaultCategories.add(category2);
+ defaultCategories.add(category3);
+ category2.addSubCategory(subCategory1_2);
+ category2.addSubCategory(subCategory2_2);
+ subCategory1_2.addGrouping(groupingDefinition1);
+ subCategory2_2.addGrouping(groupingDefinition1);
+ category3.addSubCategory(subCategory1_3);
+ category3.addSubCategory(subCategory2_3);
+ subCategory1_3.addGrouping(groupingDefinition11);
+ subCategory2_3.addGrouping(groupingDefinition11);
+ subCategory2_3.addGrouping(groupingDefinition22);
+ return createCategoriesChain(defaultCategories);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckinTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckinTest.java
new file mode 100644
index 0000000000..1820315841
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckinTest.java
@@ -0,0 +1,199 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.enums.AuditJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ProductValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ProductCheckinTest extends ProductLifecycleTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ProductCheckinTest() {
+ super(name, ProductCheckinTest.class.getName());
+ }
+
+ @BeforeClass
+ public static void staticInit() {
+ auditAction = CHECKIN_ACTION;
+ operation = ComponentOperationEnum.CHANGE_STATE_CHECKIN;
+ }
+
+ @Test
+ public void checkInProductByCreator() throws Exception {
+
+ String checkinComment = "good checkin";
+ RestResponse checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN, checkinComment);
+ assertEquals("Check response code after checkin resource", 200, checkInResponse.getErrorCode().intValue());
+ Product checkedInProduct = ResponseParser.parseToObjectUsingMapper(checkInResponse.getResponse(),
+ Product.class);
+
+ expectedProduct.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, checkedInProduct, operation);
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(checkedInProduct,
+ auditAction, productManager1, ActionStatus.OK, "0.1", "0.1", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, checkedInProduct.getUUID());
+ expectedProductAudit.setCOMMENT(checkinComment);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction, AuditJsonKeysEnum.COMMENT);
+ }
+
+ @Test
+ public void checkInProductByPM() throws Exception {
+
+ String checkinComment = "good checkin";
+ RestResponse response = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN, checkinComment);
+ assertEquals("Check response code after checkin resource", 200, response.getErrorCode().intValue());
+
+ User checkoutUser = productManager2;
+ response = LifecycleRestUtils.changeProductState(expectedProduct, checkoutUser, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkin resource", 200, response.getErrorCode().intValue());
+ expectedProduct = ResponseParser.parseToObjectUsingMapper(response.getResponse(), Product.class);
+
+ DbUtils.cleanAllAudits();
+ checkinComment = "good checkin no 2";
+ response = LifecycleRestUtils.changeProductState(expectedProduct, checkoutUser, LifeCycleStatesEnum.CHECKIN,
+ checkinComment);
+ assertEquals("Check response code after checkin resource", 200, response.getErrorCode().intValue());
+
+ Product checkedInProduct = ResponseParser.parseToObjectUsingMapper(response.getResponse(), Product.class);
+
+ expectedProduct.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ expectedProduct.setVersion("0.2");
+ expectedProduct.setLastUpdaterUserId(checkoutUser.getUserId());
+ expectedProduct.setLastUpdaterFullName(checkoutUser.getFullName());
+
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, checkedInProduct, operation);
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(checkedInProduct,
+ auditAction, checkoutUser, ActionStatus.OK, "0.2", "0.2", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, checkedInProduct.getUUID());
+ expectedProductAudit.setCOMMENT(checkinComment);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction, AuditJsonKeysEnum.COMMENT);
+ }
+
+ @Test
+ public void checkInProductByAdmin() throws Exception {
+
+ String checkinComment = "good checkin";
+ RestResponse checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, adminUser,
+ LifeCycleStatesEnum.CHECKIN, checkinComment);
+ assertEquals("Check response code after checkin resource", 200, checkInResponse.getErrorCode().intValue());
+ Product checkedInProduct = ResponseParser.parseToObjectUsingMapper(checkInResponse.getResponse(),
+ Product.class);
+
+ expectedProduct.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ expectedProduct.setLastUpdaterUserId(adminUser.getUserId());
+ expectedProduct.setLastUpdaterFullName(adminUser.getFullName());
+
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, checkedInProduct, operation);
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(checkedInProduct,
+ auditAction, adminUser, ActionStatus.OK, "0.1", "0.1", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, checkedInProduct.getUUID());
+ expectedProductAudit.setCOMMENT(checkinComment);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction, AuditJsonKeysEnum.COMMENT);
+ }
+
+ @Test
+ public void checkInProductByPMNotOwner() throws Exception {
+
+ RestResponse checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager2,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 403, checkInResponse.getErrorCode().intValue());
+ String[] auditParameters = new String[] { expectedProduct.getName(), "product", productManager1.getFirstName(),
+ productManager1.getLastName(), productManager1.getUserId() };
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, productManager2, ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, "0.1", "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ expectedProduct.getUUID(), auditParameters);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ @Test
+ public void checkInProductByPsRoleNotAllowed() throws Exception {
+
+ RestResponse checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, productStrategistUser1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 409, checkInResponse.getErrorCode().intValue());
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, productStrategistUser1, ActionStatus.RESTRICTED_OPERATION, "0.1", "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ expectedProduct.getUUID());
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ @Test
+ public void checkInProductNotExist() throws Exception {
+ String notExisitingUuid = "1234";
+ expectedProduct.setUniqueId(notExisitingUuid);
+ RestResponse checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 404, checkInResponse.getErrorCode().intValue());
+ String[] auditParameters = new String[] { "", "product" };
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, productManager1, ActionStatus.PRODUCT_NOT_FOUND, Constants.EMPTY_STRING,
+ Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, auditParameters);
+ expectedProductAudit.setCURR_STATE(Constants.EMPTY_STRING);
+ expectedProductAudit.setRESOURCE_NAME(notExisitingUuid);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ @Test
+ public void checkInProductAlreadyCheckedIn() throws Exception {
+ RestResponse checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 200, checkInResponse.getErrorCode().intValue());
+ DbUtils.cleanAllAudits();
+ checkInResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager2,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 409, checkInResponse.getErrorCode().intValue());
+ String[] auditParameters = new String[] { expectedProduct.getName(), "product", productManager1.getFirstName(),
+ productManager1.getLastName(), productManager1.getUserId() };
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, productManager2, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, "0.1", "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ expectedProduct.getUUID(), auditParameters);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckoutTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckoutTest.java
new file mode 100644
index 0000000000..ec1f7ad38d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCheckoutTest.java
@@ -0,0 +1,146 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.FileNotFoundException;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ProductValidationUtils;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ProductCheckoutTest extends ProductLifecycleTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ @BeforeClass
+ public static void staticInit() {
+ auditAction = CHECKOUT_ACTION;
+ operation = ComponentOperationEnum.CHANGE_STATE_CHECKOUT;
+ }
+
+ public ProductCheckoutTest() {
+ super(name, ProductCheckoutTest.class.getName());
+ }
+
+ @Test
+ public void checkOutProductByPmNotInContacts() throws Exception {
+ checkOutProductSuccess(productManager2);
+ }
+
+ @Test
+ public void checkOutProductByPmInContacts() throws Exception {
+ checkOutProductSuccess(productManager1);
+ }
+
+ @Test
+ public void checkOutProductByAdmin() throws Exception {
+ checkOutProductSuccess(adminUser);
+ }
+
+ @Test
+ public void checkOutProductByPs() throws Exception {
+ // Changed in 1604 patch - now it's restricted
+ checkOutProductRestricted(productStrategistUser3);
+ // checkOutProductSuccess(productStrategistUser3);
+ }
+
+ @Test
+ public void checkOutProductByDesignerRoleNotAllowed() throws Exception {
+ checkOutProductRestricted(designerUser);
+ }
+
+ @Test
+ public void checkOutProductAlreadyCheckedOut() throws Exception {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 200, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkin resource", 200, lcsResponse.getErrorCode().intValue());
+ Product checkedOutProduct = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+
+ DbUtils.cleanAllAudits();
+
+ lcsResponse = LifecycleRestUtils.changeProductState(checkedOutProduct, productManager2, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkin resource", 403, lcsResponse.getErrorCode().intValue());
+ String[] auditParameters = new String[] { checkedOutProduct.getName(), "product", productManager1.getFirstName(), productManager1.getLastName(), productManager1.getUserId() };
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(checkedOutProduct, auditAction, productManager2, ActionStatus.COMPONENT_IN_CHECKOUT_STATE, "0.2", "0.2", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, checkedOutProduct.getUUID(), auditParameters);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ private void checkOutProductSuccess(User checkoutUser) throws Exception, FileNotFoundException {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 200, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, checkoutUser, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkin resource", 200, lcsResponse.getErrorCode().intValue());
+
+ // 0.1 is not highest now
+ RestResponse prevVersionProductResp = ProductRestUtils.getProduct(expectedProduct.getUniqueId(), productStrategistUser1.getUserId());
+ Product prevVersionProduct = ResponseParser.parseToObjectUsingMapper(prevVersionProductResp.getResponse(), Product.class);
+ Boolean falseParam = false;
+ assertEquals(falseParam, prevVersionProduct.isHighestVersion());
+
+ Product checkedOutProduct = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+
+ expectedProduct.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ expectedProduct.setVersion("0.2");
+ expectedProduct.setLastUpdaterUserId(checkoutUser.getUserId());
+ expectedProduct.setLastUpdaterFullName(checkoutUser.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, checkedOutProduct, operation);
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(checkedOutProduct, auditAction, checkoutUser, ActionStatus.OK, "0.1", "0.2", LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, checkedOutProduct.getUUID());
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ private void checkOutProductRestricted(User checkoutUser) throws Exception, FileNotFoundException {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin resource", 200, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, checkoutUser, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkin resource", 409, lcsResponse.getErrorCode().intValue());
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct, auditAction, checkoutUser, ActionStatus.RESTRICTED_OPERATION, "0.1", "0.1", LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, expectedProduct.getUUID());
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductComponentInstanceCRUDTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductComponentInstanceCRUDTest.java
new file mode 100644
index 0000000000..ec3d9b75a5
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductComponentInstanceCRUDTest.java
@@ -0,0 +1,1437 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_ALREADY_EXISTS;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_COMPONENT_NAME_EXCEEDS_LIMIT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_MISSING_INFORMATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_NOT_FOUND;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS_DELETE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_UNSUPPORTED_ERROR;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentInstanceBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ProductComponentInstanceCRUDTest extends ComponentInstanceBaseTest {
+
+ protected ArtifactReqDetails heatArtifactDetails;
+ @Rule
+ public static TestName name = new TestName();
+
+ public ProductComponentInstanceCRUDTest() {
+ super(name, ProductComponentInstanceCRUDTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+ init();
+ createComponents();
+ }
+
+ private void createComponents() throws Exception {
+
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ createAtomicResource(resourceDetailsVFC_01);
+ changeResourceStateToCertified(resourceDetailsVFC_01);
+ createAtomicResource(resourceDetailsCP_01);
+ changeResourceStateToCertified(resourceDetailsCP_01);
+ createAtomicResource(resourceDetailsVL_01);
+ changeResourceStateToCertified(resourceDetailsVL_01);
+ createAtomicResource(resourceDetailsVFC_02);
+ changeResourceStateToCertified(resourceDetailsVFC_02);
+ createAtomicResource(resourceDetailsCP_02);
+ changeResourceStateToCertified(resourceDetailsCP_02);
+ createAtomicResource(resourceDetailsVL_02);
+ changeResourceStateToCertified(resourceDetailsVL_02);
+ createVF(resourceDetailsVF_02);
+ createVF(resourceDetailsVF_01);
+ // create check-In services
+ createService(serviceDetails_01);
+ createService(serviceDetails_02);
+ createService(serviceDetails_03);
+ createProduct(productDetails_01);
+ createProduct(productDetails_02);
+
+ // addresourceDetailsCP_02 ,resourceDetailsVFC_02 and
+ // resourceDetailsVL_02 to resourceDetailsVF_02 check-in VF
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resourceDetailsVF_02,
+ resourceDetailsVFC_02, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resourceDetailsVF_02, resourceDetailsCP_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resourceDetailsVF_02, resourceDetailsVL_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ resourceDetailsVF_02.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ // addresourceDetailsCP_01 ,resourceDetailsVFC_01 and
+ // resourceDetailsVL_01 to resourceDetailsVF_01 and certify
+ // resourceDetailsVF_01
+ certifyVf(resourceDetailsVF_01);
+ createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails); // serviceDetails_01
+ // has
+ // certified
+ // VF
+ createVFInstanceDuringSetup(serviceDetails_02, resourceDetailsVF_02, sdncDesignerDetails); // serviceDetails_02
+ // has
+ // check-in
+ // VF
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ }
+
+ // pass
+ @Test
+ public void createServiceInstanceTest() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ // DE189427
+ @Test(enabled = false)
+ public void createServiceInstanceFromCheckedOutState() throws Exception {
+ // can't create instance of checked-out component
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceInToAnotherServiceInstance() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String uniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(serviceInstanceReqDetails,
+ sdncPmDetails1, uniqueIdFromResponse, ComponentTypeEnum.PRODUCT);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_NOT_FOUND);
+ }
+
+ @Test
+ public void createSeveralServiceInstanceFromSameServices() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void createSeveralServiceInstanceFromDifferentServices() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void createCertifiedServiceInstance() throws Exception {
+ changeServiceStateToCertified(serviceDetails_01);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceByPm() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceWithoutVf() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails_03, sdncAdminDetails, "0.1", LifeCycleStatesEnum.CHECKIN);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_02, serviceDetails_03,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_02, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceByNonProductOwner() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails2);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceByNonAsdcUser() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ User nonExistingSdncUser = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ ;
+ nonExistingSdncUser.setUserId("bt1234");
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, nonExistingSdncUser);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceToNotCheckOutProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ // pass
+ @Test
+ public void createServiceInstanceNameIsEmpty() throws Exception {
+ String expectedServiceInstanceName = serviceDetails_01.getName() + " 1";
+ String expectedServiceInstancenormalizedName = serviceDetails_01.getName() + "1";
+ serviceDetails_01.setName("");
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String instanceNormalizedName = ResponseParser.getValueFromJsonResponse(createServiceInstanceResp.getResponse(),
+ "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "name");
+ assertEquals("check Resource Instance normalizedName ", (expectedServiceInstancenormalizedName).toLowerCase(),
+ instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", expectedServiceInstanceName, instanceName);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(expectedServiceInstanceName, actualComponentInstance.getName());
+ assertEquals((expectedServiceInstancenormalizedName).toLowerCase(),
+ actualComponentInstance.getNormalizedName());
+ }
+
+ // pass
+ @Test
+ public void createServiceInstanceNameIsNull() throws Exception {
+ String expectedServiceInstanceName = serviceDetails_01.getName() + " 1";
+ String expectedServiceInstancenormalizedName = serviceDetails_01.getName() + "1";
+ serviceDetails_01.setName(null);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String instanceNormalizedName = ResponseParser.getValueFromJsonResponse(createServiceInstanceResp.getResponse(),
+ "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "name");
+ assertEquals("check Resource Instance normalizedName ", (expectedServiceInstancenormalizedName).toLowerCase(),
+ instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", expectedServiceInstanceName, instanceName);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(expectedServiceInstanceName, actualComponentInstance.getName());
+ assertEquals((expectedServiceInstancenormalizedName).toLowerCase(),
+ actualComponentInstance.getNormalizedName());
+ }
+
+ @Test(enabled = false)
+ public void createServiceInstanceToNonExistingProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(serviceInstanceReqDetails,
+ sdncPmDetails1, "blabla", ComponentTypeEnum.PRODUCT);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ createServiceInstanceResp.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("blabla");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PRODUCT_NOT_FOUND.name(), varibales,
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceToNonSupportedComponentType() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(serviceInstanceReqDetails,
+ sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.RESOURCE_INSTANCE);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_UNSUPPORTED_ERROR);
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("null");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.UNSUPPORTED_ERROR.name(), varibales,
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ // pass
+ @Test
+ public void createServiceInstancePositionIsEmpty() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ serviceInstanceReqDetails.setPosX("");
+ serviceInstanceReqDetails.setPosY("");
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createServiceInstanceResp.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstancePositionIsNull() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ serviceInstanceReqDetails.setPosX(null);
+ serviceInstanceReqDetails.setPosY(null);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createServiceInstanceResp.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.PRODUCT);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceByDesigner() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncDesignerDetails);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void createServiceInstanceUserIdIsEmpty() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ User nonSdncDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ nonSdncDetails.setUserId("");
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, nonSdncDetails);
+ assertTrue(createServiceInstanceResp.getErrorCode() == STATUS_CODE_MISSING_INFORMATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), new ArrayList<String>(),
+ createServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ //// Update Service instance
+
+ @Test
+ public void updateServiceInstanceNameByPm() throws Exception {
+ // Check-in Product by PM and Check-out by PM
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create service instance by PM
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "abcD";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", (newName).toLowerCase(), instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", newName, instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(newName, actualComponentInstance.getName());
+ assertEquals((newName).toLowerCase(), actualComponentInstance.getNormalizedName());
+ }
+
+ @Test
+ public void updateServiceInstanceNewNameAndLocation() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "updaatedName100";
+ serviceInstanceReqDetails.setPosX("100");
+ serviceInstanceReqDetails.setPosY("100");
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(serviceInstanceReqDetails.getPosX(), actualComponentInstance.getPosX());
+ assertEquals(serviceInstanceReqDetails.getPosY(), actualComponentInstance.getPosY());
+ assertEquals(newName, actualComponentInstance.getName());
+ assertEquals(newName.toLowerCase(), actualComponentInstance.getNormalizedName());
+ }
+
+ @Test(enabled = false)
+ public void updateServiceInstanceNameRemoveSpacesFromBiginningAndEnd() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = " Abcd ";
+ String expectedNewName = " Abcd ";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", (expectedNewName).toLowerCase(),
+ instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", expectedNewName, instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(expectedNewName, actualComponentInstance.getName());
+ assertEquals((expectedNewName).toLowerCase(), actualComponentInstance.getNormalizedName());
+ }
+
+ // pass
+ @Test
+ public void updateServiceInstanceNameAllowedCharacters() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "qwer-TYUIOP_asd_0987654321.Abcd";
+ String ExpectedNormalizName = "qwertyuiopasd0987654321abcd";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", ExpectedNormalizName, instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", newName, instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(newName, actualComponentInstance.getName());
+ assertEquals(ExpectedNormalizName, actualComponentInstance.getNormalizedName());
+
+ }
+
+ @Test
+ public void updateInstanceNameInvalidCharacters() throws Exception {
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/',
+ '|', '\\', ',' };
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "Abcd1";
+ String updateName;
+ for (int i = 0; i < invalidChars.length; i++) {
+ updateName = newName + invalidChars[i];
+ serviceInstanceReqDetails.setName(updateName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(),
+ ComponentTypeEnum.PRODUCT);
+ assertEquals("Check response code ", STATUS_CODE_INVALID_CONTENT,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("Service Instance");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPONENT_NAME.name(), varibales,
+ updateServiceInstanceResponse.getResponse());
+ }
+ }
+
+ // pass
+ @Test
+ public void updateInstanceNameMaxLength() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "Qwertyuiop1234567890asdfAhjklzxcvbnmasdfghjkl12345";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", (newName).toLowerCase(), instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", newName, instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(newName, actualComponentInstance.getName());
+ assertEquals((newName).toLowerCase(), actualComponentInstance.getNormalizedName());
+ }
+
+ @Test
+ public void updateInstanceNameExceedMaxLength() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String expectedName = ResponseParser.getValueFromJsonResponse(createServiceInstanceResp.getResponse(), "name");
+ String expectedNormalizedName = ResponseParser.getValueFromJsonResponse(createServiceInstanceResp.getResponse(),
+ "normalizedName");
+ String newName = "Qwertyuiop1234567890asdfAhjklzxcvbnmasdfghjkl123456";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ assertEquals("Check response code ", STATUS_CODE_COMPONENT_NAME_EXCEEDS_LIMIT,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("Service Instance");
+ varibales.add("50");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT.name(), varibales,
+ updateServiceInstanceResponse.getResponse());
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(expectedName, actualComponentInstance.getName());
+ assertEquals(expectedNormalizedName, actualComponentInstance.getNormalizedName());
+ }
+
+ @Test
+ public void updateServiceInstanceNameEmpty() throws Exception {
+ // see US534663 In case a PS/PM removes the current service instance
+ // name then BE has to generate again the "default" service instance
+ // name
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", (serviceDetails_01.getName() + "2").toLowerCase(),
+ instanceNormalizedName);
+ assertEquals("check Resource Instance normalizedName ", (serviceDetails_01.getName() + " 2"), instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(instanceName, actualComponentInstance.getName());
+ assertEquals(instanceNormalizedName, actualComponentInstance.getNormalizedName());
+ }
+
+ // pass
+ @Test
+ public void updateServiceInstanceNameNull() throws Exception {
+ // see US534663 In case a PS/PM removes the current service instance
+ // name then BE has to generate again the "default" service instance
+ // name
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = null;
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", (serviceDetails_01.getName() + "2").toLowerCase(),
+ instanceNormalizedName);
+ assertEquals("check Resource Instance normalizedName ", (serviceDetails_01.getName() + " 2"), instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(instanceName, actualComponentInstance.getName());
+ assertEquals(instanceNormalizedName, actualComponentInstance.getNormalizedName());
+ }
+
+ @Test
+ public void updateServiceInstanceCheckedByOtherUser() throws Exception {
+ // see US534663 In case a PS/PM removes the current service instance
+ // name then BE has to generate again the "default" service instance
+ // name
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "blabla";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails2, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ updateServiceInstanceResponse.getResponse());
+ }
+
+ @Test
+ public void updateServiceInstance_UserIdIsNonAsdcUser() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "blabla";
+ serviceInstanceReqDetails.setName(newName);
+ User nonSdncUserDetails = new User();
+ nonSdncUserDetails.setUserId("bt4567");
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, nonSdncUserDetails, productDetails_01.getUniqueId(),
+ ComponentTypeEnum.PRODUCT);
+ assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ updateServiceInstanceResponse.getResponse());
+ }
+
+ @Test
+ public void updateServiceInstanceNameToAlreadyExisting() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String ServiceName1 = ResponseParser.getNameFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(serviceInstanceReqDetails,
+ sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // Update service instance2 name to service instance1
+ serviceInstanceReqDetails.setName(ServiceName1);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ assertEquals("Check response code ", STATUS_CODE_ALREADY_EXISTS,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("Service Instance");
+ varibales.add(ServiceName1);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_ALREADY_EXIST.name(), varibales,
+ updateServiceInstanceResponse.getResponse());
+ }
+
+ @Test
+ public void updateServiceInstanceForNonExistingProduct() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "blabla";
+ serviceInstanceReqDetails.setName(newName);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, "blablabla", ComponentTypeEnum.PRODUCT);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PRODUCT_NOT_FOUND.name(), varibales,
+ updateServiceInstanceResponse.getResponse());
+
+ }
+
+ @Test
+ public void updateNonExistingServiceInstance() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String newName = "blabla";
+ serviceInstanceReqDetails.setName(newName);
+ serviceInstanceReqDetails.setUniqueId("11111111");
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(newName);
+ varibales.add("service instance");
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ updateServiceInstanceResponse.getErrorCode().intValue());
+ // need to change ActionStatus.RESOURCE_INSTANCE_NOT_FOUND.name() to
+ // ActionStatus.SERVICE_INSTANCE_NOT_FOUND.name()
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND.name(), varibales,
+ updateServiceInstanceResponse.getResponse());
+ }
+
+ @Test
+ public void updateServiceInstanceLocation() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ serviceInstanceReqDetails.setPosX("50");
+ serviceInstanceReqDetails.setPosY("100");
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(serviceInstanceReqDetails.getPosX(), actualComponentInstance.getPosX());
+ assertEquals(serviceInstanceReqDetails.getPosY(), actualComponentInstance.getPosY());
+ }
+
+ @Test
+ public void updateServiceInstanceToNonExistingLocation() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ String nameFromResponse = ResponseParser.getNameFromResponse(createServiceInstanceResp);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ serviceInstanceReqDetails.setPosX("-50");
+ serviceInstanceReqDetails.setPosY("-100");
+ serviceInstanceReqDetails.setName(nameFromResponse);
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(serviceInstanceReqDetails.getPosX(), actualComponentInstance.getPosX());
+ assertEquals(serviceInstanceReqDetails.getPosY(), actualComponentInstance.getPosY());
+ assertEquals(nameFromResponse, actualComponentInstance.getName());
+ }
+
+ @Test
+ public void updateServiceInstanceLocationNameIsEmpty() throws Exception {
+ String expectedServiceInstanceName = serviceDetails_01.getName() + " 2";
+ String expectedServiceInstancenormalizedName = serviceDetails_01.getName() + "2";
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ serviceInstanceReqDetails.setPosX("100");
+ serviceInstanceReqDetails.setPosY("200");
+ serviceInstanceReqDetails.setName("");
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String nameFromResponse = ResponseParser.getNameFromResponse(updateServiceInstanceResponse);
+ String postX = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "posX");
+ String postY = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "posY");
+ assertEquals(nameFromResponse, expectedServiceInstanceName);
+ assertEquals(postX, "100");
+ assertEquals(postY, "200");
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(serviceInstanceReqDetails.getPosX(), actualComponentInstance.getPosX());
+ assertEquals(serviceInstanceReqDetails.getPosY(), actualComponentInstance.getPosY());
+ assertEquals(nameFromResponse, actualComponentInstance.getName());
+ assertEquals(expectedServiceInstancenormalizedName.toLowerCase(), actualComponentInstance.getNormalizedName());
+ }
+
+ // pass
+ @Test
+ public void updateServiceInstanceNameToProductName() throws Exception {
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_01);
+ RestResponse createServiceInstanceResp = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // ComponentInstance componentInstance =
+ // ResponseParser.parseToObjectUsingMapper(createServiceInstanceResp.getResponse(),
+ // ComponentInstance.class);
+ // addCompInstReqCapToExpected(componentInstance,
+ // ComponentTypeEnum.PRODUCT);
+ serviceInstanceReqDetails.setName(productDetails_01.getName());
+ RestResponse updateServiceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ResourceRestUtils.checkSuccess(updateServiceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateServiceInstanceResponse.getResponse(),
+ "name");
+ assertEquals("check Resource Instance normalizedName ", (serviceInstanceReqDetails.getName()).toLowerCase(),
+ instanceNormalizedName);
+ assertEquals("check Resource Instance Name ", serviceInstanceReqDetails.getName(), instanceName);
+ // get product and verify that service instanceName is correct
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ assertEquals(serviceInstanceReqDetails.getName(), actualComponentInstance.getName());
+ assertEquals((serviceInstanceReqDetails.getName()).toLowerCase(), actualComponentInstance.getNormalizedName());
+ }
+
+ //// Delete Service Instance
+
+ @Test
+ public void deleteAllServiceInstanceFromProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails1);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 0, 0);
+ }
+
+ @Test
+ public void deleteServiceWhileServiceInstanceExistInProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ // Delete service while service instance of it exist in product
+ RestResponse deleteServiceResponse = ServiceRestUtils.deleteServiceById(serviceDetails_01.getUniqueId(),
+ sdncDesignerDetails.getUserId());
+ assertTrue(deleteServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ // Get product and verify that service instance still exists
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ // ComponentInstance actualComponentInstance =
+ // actualProduct.getComponentInstances().get(0);
+ // assertTrue(serviceInstanceUniqueIdFromResponse ==
+ // actualComponentInstance.getUniqueId());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ // pass
+ @Test
+ public void deleteServiceInstanceByPm() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails1);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceByPmCreatedByPm() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ productDetails_01.setUniqueId(ResponseParser.getUniqueIdFromResponse(restResponse));
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails1);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceByPmWhichIsCheckedOutByAnotherPm() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails2);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ // DE190189
+ @Test
+ public void deleteServiceInstanceByPmCreatedByPs() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ String productOldUniqueId = productDetails_01.getUniqueId();
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(restResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get product and get service instance new uniquId
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPmDetails1.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ String serviceInstanceUniqueId = actualComponentInstance.getUniqueId();
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails1);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 0, 0);
+ }
+
+ // DE190189
+ @Test
+ public void deleteServiceInstanceByAdminCreatedByPs() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ String productOldUniqueId = productDetails_01.getUniqueId();
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncAdminDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ String productNewUniqueId = ResponseParser.getUniqueIdFromResponse(restResponse);
+ updateExpectedReqCapAfterChangeLifecycleState(productOldUniqueId, productNewUniqueId);
+ // get product and get service instance new uniquId
+ RestResponse getActualProductResponse = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncAdminDetails.getUserId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getActualProductResponse.getResponse(),
+ Product.class);
+ ComponentInstance actualComponentInstance = actualProduct.getComponentInstances().get(0);
+ String serviceInstanceUniqueId = actualComponentInstance.getUniqueId();
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncAdminDetails);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 0, 0);
+ }
+
+ @Test
+ public void createAndDeleteServiceInstanceByAdmin() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncAdminDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // productDetails_01.setUniqueId(ResponseParser.getUniqueIdFromResponse(restResponse));
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncAdminDetails);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncAdminDetails);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncAdminDetails);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceFromNonCheckOutProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ RestResponse restResponse = LifecycleRestUtils.changeProductState(productDetails_01, sdncPmDetails1,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails1);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceByDesigner() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncDesignerDetails);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceByTester() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncTesterDetails);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceByPsWhichIsCheckedOutByAnotherPs() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPsDetails2);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ // pass
+ @Test
+ public void deleteServiceInstanceByNonAsdcUser() throws Exception {
+ User nonExistingSdncUser = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ ;
+ nonExistingSdncUser.setUserId("bt1234");
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ nonExistingSdncUser);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceFromNonExistingProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(sdncPmDetails1,
+ "1234567890", serviceInstanceUniqueId, ComponentTypeEnum.PRODUCT);
+ assertTrue(deleteResourceInstanceResponse.getErrorCode() == STATUS_CODE_NOT_FOUND);
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PRODUCT_NOT_FOUND.name(), varibales,
+ deleteResourceInstanceResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteNonExistingServiceInstanceFromProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ // String serviceInstanceUniqueId =
+ // ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(sdncPmDetails1,
+ productDetails_01.getUniqueId(), "1234567890123456unExistingServiceInstance",
+ ComponentTypeEnum.PRODUCT);
+ assertTrue(deleteResourceInstanceResponse.getErrorCode() == STATUS_CODE_NOT_FOUND);
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("1234567890123456unExistingServiceInstance");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PRODUCT_NOT_FOUND.name(), varibales,
+ deleteResourceInstanceResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceFromNonSupportedComponentType() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(sdncPmDetails1,
+ productDetails_01.getUniqueId(), serviceInstanceUniqueId, ComponentTypeEnum.RESOURCE_INSTANCE);
+ assertTrue(deleteResourceInstanceResponse.getErrorCode() == STATUS_CODE_UNSUPPORTED_ERROR);
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("null");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.UNSUPPORTED_ERROR.name(), varibales,
+ deleteResourceInstanceResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceComponentTypeIsNotProduct() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(sdncPmDetails1,
+ productDetails_01.getUniqueId(), serviceInstanceUniqueId, ComponentTypeEnum.SERVICE);
+ assertTrue(deleteResourceInstanceResponse.getErrorCode() == STATUS_CODE_NOT_FOUND);
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), varibales,
+ deleteResourceInstanceResponse.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteServiceInstanceUserIdIsEmpty() throws Exception {
+ User nonSdncDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ nonSdncDetails.setUserId("");
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ nonSdncDetails);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_MISSING_INFORMATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), new ArrayList<String>(),
+ deleteServiceInstanceResp.getResponse());
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ @Test
+ public void deleteCertifiedServiceInstance() throws Exception {
+ changeServiceStateToCertified(serviceDetails_01);
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(serviceInstanceUniqueId, productDetails_01,
+ sdncPmDetails1);
+ assertTrue(deleteServiceInstanceResp.getErrorCode() == STATUS_CODE_SUCCESS_DELETE);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ }
+
+ ////////////////////////////////////
+
+ private void certifyVf(ResourceReqDetails resource) throws Exception {
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String cpCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, resourceDetailsVFC_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String computeCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, resourceDetailsVL_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String vlCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ // Fixing Vl/Cp req/cap
+ ComponentTypeEnum containerCompType = ComponentTypeEnum.RESOURCE;
+ User user = sdncDesignerDetails;
+ fulfillCpRequirement(resource, cpCompInstId, computeCompInstId, computeCompInstId, user, containerCompType);
+ consumeVlCapability(resource, cpCompInstId, vlCompInstId, cpCompInstId, user, containerCompType);
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails,
+ sdncDesignerDetails, resource.getUniqueId());
+ ResourceRestUtils.checkSuccess(response);
+ RestResponse changeResourceStateToCertified = changeResourceStateToCertified(resource);
+ ResourceRestUtils.checkSuccess(changeResourceStateToCertified);
+ }
+
+ private RestResponse changeResourceStateToCertified(ResourceReqDetails resourceDetails) throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponse;
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ if (restResponse.getErrorCode() == 200) {
+ String newVersion = ResponseParser.getVersionFromResponse(restResponse);
+ resourceDetails.setVersion(newVersion);
+ resourceDetails.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ resourceDetails.setLastUpdaterUserId(sdncTesterDetails.getUserId());
+ resourceDetails.setLastUpdaterFullName(sdncTesterDetails.getFullName());
+ String uniqueIdFromRresponse = ResponseParser.getValueFromJsonResponse(restResponse.getResponse(),
+ "uniqueId");
+ resourceDetails.setUniqueId(uniqueIdFromRresponse);
+ }
+ }
+ return restResponse;
+ }
+
+ private RestResponse changeServiceStateToCertified(ServiceReqDetails serviceDetails) throws Exception {
+ /*
+ * RestResponse restResponse =
+ * LifecycleRestUtils.changeServiceState(serviceDetails,
+ * sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ * ResourceRestUtils.checkSuccess(restResponse);
+ */
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponse;
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ if (restResponse.getErrorCode() == 200) {
+ serviceDetails.setVersion("1.0");
+ serviceDetails.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ serviceDetails.setLastUpdaterUserId(sdncTesterDetails.getUserId());
+ serviceDetails.setLastUpdaterFullName(sdncTesterDetails.getFullName());
+ String uniqueIdFromRresponse = ResponseParser.getValueFromJsonResponse(restResponse.getResponse(),
+ "uniqueId");
+ serviceDetails.setUniqueId(uniqueIdFromRresponse);
+ }
+ }
+ return restResponse;
+ }
+
+ @Test
+ public void deleteServiceInstanceTest() throws Exception {
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+
+ createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_02, sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 2, 0);
+ String compInstId2 = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+
+ RestResponse deleteServiceInstanceResp = deleteServiceInstance(compInstId, productDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkDeleteResponse(deleteServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 1, 0);
+
+ deleteServiceInstanceResp = deleteServiceInstance(compInstId2, productDetails_01, sdncPmDetails1);
+ ResourceRestUtils.checkDeleteResponse(deleteServiceInstanceResp);
+ getComponentAndValidateRIs(productDetails_01, 0, 0);
+ }
+
+ @Test
+ public void returnedServiceInstanceTypeAttributeTest() throws Exception {
+ String expectedServiceType = ComponentTypeEnum.SERVICE.getValue().toUpperCase();
+
+ RestResponse createServiceInstanceResp = createServiceInstance(productDetails_01, serviceDetails_01,
+ sdncPmDetails1);
+ ResourceRestUtils.checkCreateResponse(createServiceInstanceResp);
+ String serviceUniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(createServiceInstanceResp);
+ ComponentInstanceRestUtils.checkComponentInstanceType(createServiceInstanceResp, expectedServiceType);
+
+ RestResponse getProductResp = ProductRestUtils.getProduct(productDetails_01.getUniqueId(),
+ sdncPsDetails1.getUserId());
+ ProductRestUtils.checkSuccess(getProductResp);
+ Product productObject = ResponseParser.parseToObjectUsingMapper(getProductResp.getResponse(), Product.class);
+ List<ComponentInstance> productComponentInstances = productObject.getComponentInstances();
+ for (ComponentInstance comp : productComponentInstances) {
+ String actualOriginType = comp.getOriginType().getValue().toUpperCase();
+ assertTrue(expectedServiceType.equals(actualOriginType));
+ }
+
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails_02);
+ serviceInstanceReqDetails.setUniqueId(serviceUniqueIdFromResponse);
+ serviceInstanceReqDetails.setComponentUid(serviceDetails_01.getUniqueId());
+ RestResponse updateResourceInstance = ComponentInstanceRestUtils.updateComponentInstance(
+ serviceInstanceReqDetails, sdncPmDetails1, productDetails_01.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ ComponentInstanceRestUtils.checkSuccess(updateResourceInstance);
+ ComponentInstanceRestUtils.checkComponentInstanceType(updateResourceInstance, expectedServiceType);
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCreateWithValidationsTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCreateWithValidationsTest.java
new file mode 100644
index 0000000000..d2c2dfe77f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCreateWithValidationsTest.java
@@ -0,0 +1,1710 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Arrays;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ProductValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.annotations.Test;
+
+public class ProductCreateWithValidationsTest extends ProductBaseTest {
+ @Rule
+ public static TestName name = new TestName();
+
+ public static String INITIAL_PRODUCT_VERSION = "0.1";
+ public static String CREATE_AUDIT_ACTION = "Create";
+ public String normalizedName;
+
+ public ProductCreateWithValidationsTest() {
+ super(name, ProductCreateWithValidationsTest.class.getName());
+ }
+
+ @Test
+ public void createProductSuccessValidation() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNotByPmUser() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productStrategistUser1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION,
+ productStrategistUser1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productStrategistUser1, ActionStatus.RESTRICTED_OPERATION,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNotByAsdcUser() throws Exception {
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ nonAsdcUser.setUserId("bt750k");
+ nonAsdcUser.setFirstName(null);
+ nonAsdcUser.setLastName(null);
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, nonAsdcUser);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION,
+ nonAsdcUser);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, nonAsdcUser, ActionStatus.RESTRICTED_OPERATION,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductUserIdIsEmpty() throws Exception {
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ nonAsdcUser.setUserId("");
+ nonAsdcUser.setFirstName(null);
+ nonAsdcUser.setLastName(null);
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, nonAsdcUser);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_MISSING_INFORMATION,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION,
+ nonAsdcUser);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, nonAsdcUser, ActionStatus.MISSING_INFORMATION,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationLessThanMinCharacters() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Pro");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, Constants.EMPTY_STRING, Constants.EMPTY_STRING,
+ null, null, Constants.EMPTY_STRING, "Product", "abbreviated");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationMaxLength() throws Exception {
+ // Max length = 25
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Qwertyuiop1234567890asdfA");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationExceedMaxLength() throws Exception {
+ // Max length = 25
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Qwertyuiop1234567890asdfAa");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, Constants.EMPTY_STRING, Constants.EMPTY_STRING,
+ null, null, Constants.EMPTY_STRING, "Product", "abbreviated");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationEmptyName() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product",
+ "abbreviated");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameAlreadyExist() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ // productReqDetails.setName("ProDuct1");
+ DbUtils.deleteFromEsDbByPattern("_all");
+ createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_ALREADY_EXISTS,
+ createProduct.getErrorCode().intValue());
+ productReqDetails.setVersion("0.1");
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_NAME_ALREADY_EXIST,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product",
+ productReqDetails.getName());
+ constructFieldsForAuditValidation.setCURR_VERSION("0.1");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ constructFieldsForAuditValidation.setCURR_STATE("NOT_CERTIFIED_CHECKOUT");
+ constructFieldsForAuditValidation.setSERVICE_INSTANCE_ID(null);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationNameIsNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName(null);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product",
+ "abbreviated");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ // DE193857
+ @Test(enabled = false)
+ public void createProductNameValidationAllowedCharacters() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Ac_2@3:4& m=n+b-u.j-u'g#b"); // Bug @:&=+'#
+ normalizedName = "ac234mnbujugb";
+ String expectedProductName = "Ac_2@3:4& M=n+b-u.j-u'g#b";
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ productReqDetails.setName(expectedProductName);
+ productReqDetails.setName("Ac_2@3:4& M=n+b-u.j-u'g#b");
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setNormalizedName(normalizedName);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ // DE193857
+ @Test(enabled = false)
+ public void createProductNameValidationREmoveExtraNonAlphanumericChars() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Ac____222----333......asd");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setName("Ac_222-333.asd");
+ normalizedName = "ac222333asd";
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationNotAllowedCharacters() throws Exception {
+ ExpectedProductAudit constructFieldsForAuditValidation;
+ char invalidChars[] = { '~', '!', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/', '|',
+ '\\', ',', '$' };
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("abc" + invalidChars[i]);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "",
+ productManager1);
+ constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product",
+ "abbreviated");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+ }
+
+ @Test
+ public void createProductFullNameContainSpecialCharacters() throws Exception {
+ char invalidChars[] = { '~', '!', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/', '|',
+ '\\', ',', '$' };
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ for (int i = 0; i < invalidChars.length; i++) {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ productReqDetails.setFullName("abc" + invalidChars[i]);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_CREATED,
+ createProduct.getErrorCode().intValue());
+ RestResponse deleteProduct = ProductRestUtils.deleteProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS,
+ deleteProduct.getErrorCode().intValue());
+ }
+ }
+
+ @Test(enabled = false)
+ public void createProductNameValidationRemoveSpaceFromBeginning() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName(" Qwertyuiop1234567890asdfA");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName().trim()));
+ normalizedName = productReqDetails.getName().trim().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test(enabled = false)
+ public void createProductNameValidationRemoveSpaceFromTheEnd() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Qwertyuiop1234567890asdfA ");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName().trim()));
+ normalizedName = productReqDetails.getName().trim().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationStartWithNumber() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("1Qwert");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName().trim()));
+ normalizedName = productReqDetails.getName().trim().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationStartWithNonAlphaNumeric() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("_Qwert");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName().trim()));
+ normalizedName = productReqDetails.getName().trim().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, Constants.EMPTY_STRING, Constants.EMPTY_STRING,
+ null, null, Constants.EMPTY_STRING, "Product", "abbreviated");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductNameValidationFirstLetterOfKeyWordsCapitalized() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ // productReqDetails.setTags(Arrays.asList("abba"));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ // productReqDetails.setName("Abba");
+ /*
+ * String actualNormalizedNameFromResponse =
+ * ResponseParser.getValueFromJsonResponse(createProduct.getResponse(),
+ * "normalizedName");
+ * assertTrue(actualNormalizedNameFromResponse.equals(normalizedName));
+ */
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameValidationIsEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setFullName("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product", "full");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameValidationIsNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setFullName("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product", "full");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameLessThanMinLength() throws Exception {
+ // Min is 4 characters
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setFullName("abc");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, Constants.EMPTY_STRING, Constants.EMPTY_STRING,
+ null, null, Constants.EMPTY_STRING, "Product", "full");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameHasMinLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setFullName("abcd");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameHasMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setFullName(
+ "Abba1234567890asdfghjk l123zxcvbnm432adfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameExceedMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setFullName(
+ "Abba1234567890asdfghjk l123zxcvbnm432adfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.123");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1,
+ ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, Constants.EMPTY_STRING, Constants.EMPTY_STRING,
+ null, null, Constants.EMPTY_STRING, "Product", "full");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductFullNameRemoveExtraSpaces() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setFullName("Abbaaa a1");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setFullName("Abbaaa a1");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionValidationIsEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setDescription("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_MISSING_DESCRIPTION,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionValidationIsNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setDescription(null);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_MISSING_DESCRIPTION,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionValidCharacters01() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription("qwertyuiopasdfghjklzxcvbnm1234567890<b>Bold<</b>");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setDescription("qwertyuiopasdfghjklzxcvbnm1234567890Bold&lt;");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionValidCharacters02() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription("~!@#$%^&*()_+<>?qwertyuiopasdfghjklzxcvbnm1234567890#");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setDescription("~!@#$%^&amp;*()_+&lt;&gt;?qwertyuiopasdfghjklzxcvbnm1234567890#");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionInValidCharacters() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription("מה");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_INVALID_DESCRIPTION,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionRemoveSpacesFromBeginning() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription(" abcd12345");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setDescription("abcd12345");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionRemoveSpacesFromTheEnd() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription("abcd 12345 xcvb ");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setDescription("abcd 12345 xcvb");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription(
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12asdfghjklzxcvbnmqwertyui");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDescriptionExceedMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setDescription(
+ "Abxba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12Abba1234567890asdfghjkl123zxcvbnm432asdfghjkl_-.123Abba1234567890asdfghjkl23zxcvbnm432asdfghjkl_-.12asdfghjklzxcvbnmqwertyui");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product", "1024");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductTagIsEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product");
+ productReqDetails.setTags(Arrays.asList(""));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_FIELD_FORMAT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product", "tag");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ // DE192351
+ @Test
+ public void createProductTagValidationAllowedCharacters() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), "Acde2@3:4& m=n+b-u.j-u'g#b"));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductTagsNameValidationProductNameIsNotInTag() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Qwertyuiop1234567890asdfA");
+ productReqDetails.setTags(Arrays.asList("Abc"));
+ normalizedName = productReqDetails.getName().trim().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductSingleTagMaxLength() throws Exception {
+ // SingleTagMaxLength = 50
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(
+ Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345678"));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductSingleTagExceedMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(
+ Arrays.asList(productReqDetails.getName(), "Axbba1234567890asdfghjkl123zxcvbnm432asdfgh12345678"));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "50");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductAllTagsMaxLength() throws Exception {
+ // AllTagsMaxLength = 1024
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(
+ Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345601",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345602",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345603",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345604",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345605",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345606",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345607",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345608",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh1234569",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345610",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345611",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345612",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345613",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345614",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345615",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345616",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345617",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345618",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345619",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345"));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductAllTagsExceedMaxLength() throws Exception {
+ // AllTagsMaxLength = 1024
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(
+ Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345601",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345602",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345603",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345604",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345605",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345606",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345607",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345608",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh1234569",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345610",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345611",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345612",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345613",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345614",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345615",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345616",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345617",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345618",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345619",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh123456"));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "1024");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductDuplicateTagRemoved() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductContactsIsEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ productReqDetails.setContacts(Arrays.asList(""));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_INVALID_CONTACT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductContactsInvalidFormat() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ productReqDetails.setContacts(Arrays.asList("bt750345"));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_INVALID_CONTACT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductConvertContactsToLowerCase() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ productReqDetails.setContacts(Arrays.asList(productManager1.getUserId().toUpperCase()));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setContacts(Arrays.asList(productManager1.getUserId().toLowerCase()));
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductContactsDoexNotContainTheProductCreator() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ productReqDetails.setContacts(Arrays.asList(productManager2.getUserId()));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setContacts(Arrays.asList(productManager2.getUserId(), productManager1.getUserId()));
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductContactsNotAllowedAsdcUsers() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ productReqDetails.setContacts(Arrays.asList(designerUser.getUserId()));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_PRODUCT_CONTACT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING,
+ designerUser.getUserId());
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductContactsNotAsdcUser() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1"); // Bug @:&=+'#
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase();
+ String nonAsdcUser = "bh1234";
+ productReqDetails.setContacts(Arrays.asList(nonAsdcUser));
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_PRODUCT_CONTACT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, nonAsdcUser);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeIsEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.MISSING_PROJECT_CODE,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeIsNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode(null);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.MISSING_PROJECT_CODE,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeIsNotNumeric() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode("asdfgh");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_PROJECT_CODE,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeHasnMinCharacters() throws Exception {
+ // Min =5
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode("12345");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeHasnMaxCharacters() throws Exception {
+ // Max =10
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode("1234567890");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeExceedMaxCharacters() throws Exception {
+ // Max =10
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode("12345678901");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_PROJECT_CODE,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductProjectCodeLessThanMinCharacters() throws Exception {
+ // Max =10
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setProjectCode("1234");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_PROJECT_CODE,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIconIsEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setIcon("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_MISSING_ICON,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIconIsNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setIcon(null);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_MISSING_ICON,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIconMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setIcon("asdfghjklqwertyuiozxcvbfv");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIconExceedMaxLength() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setIcon("asdfghjklqwertyuiozxcvbf12");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product", "25");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIconAllowedCharacters() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setIcon("a--s-fghjk_q__r1234567890");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIconInValidCharacters() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ String icon = "asdfg";
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/',
+ '|', '\\', ',' };
+ RestResponse createProduct;
+ for (int i = 0; i < invalidChars.length; i++) {
+ productReqDetails.setIcon(icon + invalidChars[i]);
+ createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "",
+ productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.COMPONENT_INVALID_ICON,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, "Product");
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+ }
+
+ @Test
+ public void createProductIsActiveisEmpty() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setActive("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setActive("false");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIsActiveisNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setActive("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ productReqDetails.setActive("false");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIsActiveisFalse() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setActive("false");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIsActiveisHasInvalidValue() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setActive("xfalse");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT,
+ createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "", productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_CONTENT,
+ Constants.EMPTY_STRING, Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ constructFieldsForAuditValidation.setRESOURCE_NAME("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test
+ public void createProductIsActiveisTrue() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Product1");
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ productReqDetails.setActive("true");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ //////////////////////////////////////////////
+ // DE192424
+ @Test
+ public void createProductNameValidationNormalizationNameWithSpaces() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setName("Abba Emma");
+ // productReqDetails.setName("abba emma");
+ // productReqDetails.setTags(Arrays.asList("abba emma"));
+ normalizedName = productReqDetails.getName().toLowerCase().replaceAll("\\s+", "");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ String actualNormalizedNameFromResponse = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(),
+ "normalizedName");
+ assertTrue(actualNormalizedNameFromResponse.equals(normalizedName));
+ // productReqDetails.setName("Abba Emma");
+ String productUuid = ResponseParser.getUuidFromResponse(createProduct);
+ compareExpectedAndActualProducts(productReqDetails, createProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProductRes);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(
+ expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING,
+ "0.1", null, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, productUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ private void compareExpectedAndActualProducts(ProductReqDetails productReqDetails, RestResponse createProduct)
+ throws JSONException {
+ String productName = ResponseParser.getNameFromResponse(createProduct);
+ assertTrue(productReqDetails.getName().equals(productName));
+ String productIcon = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "icon");
+ assertTrue(productReqDetails.getIcon().equals(productIcon));
+ String productFullName = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "fullName");
+ assertTrue(productReqDetails.getFullName().equals(productFullName));
+ String productProjectCode = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "projectCode");
+ assertTrue(productReqDetails.getProjectCode().equals(productProjectCode));
+ String productIsActive = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "isActive");
+ String expectedIsActive = (productReqDetails.getActive() != null ? productReqDetails.getActive() : "false");
+ assertTrue(productIsActive.equals(expectedIsActive));
+ String productdescription = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "description");
+ assertTrue(productReqDetails.getDescription().equals(productdescription));
+ String productNormalizedName = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(),
+ "normalizedName");
+ assertTrue(normalizedName.equals(productNormalizedName));
+ String productContacts = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "contacts");
+ JSONArray reciviedContacts = new JSONArray(productContacts);
+ String actualContact = null;
+ for (int i = 0; i < reciviedContacts.length(); i++) {
+ actualContact = reciviedContacts.getString(i);
+ assertEquals(productReqDetails.getContacts().get(i), actualContact);
+ }
+ String productTags = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "tags");
+ JSONArray reciviedTages = new JSONArray(productTags);
+ String actualTag = null;
+ for (int i = 0; i < reciviedTages.length(); i++) {
+ actualTag = reciviedTages.getString(i);
+ assertEquals(productReqDetails.getTags().get(i), actualTag);
+ }
+ }
+
+ // END
+ ///////////////////////////////////////////////////////
+ @Test
+ public void createProductSuccessFlow() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED,
+ createProduct.getErrorCode().intValue());
+
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ assertEquals("Check response code after getting created Product", BaseRestUtils.STATUS_CODE_SUCCESS,
+ getProductRes.getErrorCode().intValue());
+
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(false));
+ }
+
+ @Test
+ public void createProductSetIsActive() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setActive("true");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED,
+ createProduct.getErrorCode().intValue());
+
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(),
+ productManager1.getUserId());
+ assertEquals("Check response code after getting created Product", BaseRestUtils.STATUS_CODE_SUCCESS,
+ getProductRes.getErrorCode().intValue());
+
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct,
+ ComponentOperationEnum.GET_COMPONENT);
+ assertEquals(actualProduct.getIsActive(), new Boolean(true));
+ }
+
+ @Test
+ public void createProductNoIcon() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setIcon(null);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_MISSING_DATA,
+ createProduct.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createProductNoProjectCode() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ productReqDetails.setProjectCode(null);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_MISSING_DATA,
+ createProduct.getErrorCode().intValue());
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCrudTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCrudTest.java
new file mode 100644
index 0000000000..f234708b7a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductCrudTest.java
@@ -0,0 +1,2197 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ProductValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class ProductCrudTest extends ProductBaseTest {
+ private static Logger log = LoggerFactory.getLogger(ProductCrudTest.class.getName());
+ @Rule
+ public static TestName name = new TestName();
+
+ public static String INITIAL_PRODUCT_VERSION = "0.1";
+ public static String CREATE_AUDIT_ACTION = "Create";
+ public static String UPDATE_AUDIT_ACTION = "Update";
+ public static String COMPONENT_TYPE = "Product";
+
+ private ProductReqDetails productReqDetails;
+ private RestResponse createProduct;
+ private Product product;
+
+ public ProductCrudTest() {
+ super(name, ProductCrudTest.class.getName());
+ }
+
+ @Test // (enabled=false)
+ public void createAndGetAll() throws Exception {
+ createProductAndGet(UserRoleEnum.DESIGNER);
+ }
+
+ private void createProductAndGet(UserRoleEnum user) throws Exception, IOException {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+
+ RestResponse catalog = CatalogRestUtils.getCatalog(user.getUserId());
+ assertEquals("Check response code after get catalog", BaseRestUtils.STATUS_CODE_SUCCESS, catalog.getErrorCode().intValue());
+
+ try {
+ JsonElement jElement = new JsonParser().parse(catalog.getResponse());
+ JsonObject jObject = jElement.getAsJsonObject();
+ JsonArray products = (JsonArray) jObject.get("products");
+ assertEquals("Check product array size", 1, products.size());
+ Iterator<JsonElement> iter = products.iterator();
+ while (iter.hasNext()) {
+ JsonElement next = iter.next();
+ Product product = ResponseParser.parseToObjectUsingMapper(next.toString(), Product.class);
+ assertNotNull(product);
+ assertEquals("Check product name", productReqDetails.getName(), product.getName());
+ // Map<String, String> allVersions = product.getAllVersions();
+ // assertEquals("Check product name", 1, allVersions.size());
+ }
+
+ } catch (Exception e) {
+ log.debug("exception", e);
+ }
+ }
+
+ @Test
+ public void getAllNoProcduts() throws Exception {
+
+ RestResponse catalog = CatalogRestUtils.getCatalog();
+ assertEquals("Check response code after get catalog", BaseRestUtils.STATUS_CODE_SUCCESS, catalog.getErrorCode().intValue());
+
+ try {
+ JsonElement jElement = new JsonParser().parse(catalog.getResponse());
+ JsonObject jObject = jElement.getAsJsonObject();
+ JsonArray products = (JsonArray) jObject.get("products");
+ assertEquals("Check product array size", 0, products.size());
+ } catch (Exception e) {
+ log.debug("exception", e);
+ }
+
+ }
+
+ @Test
+ public void getAllNoAttHeader() throws Exception {
+ String url = String.format(Urls.GET_CATALOG_DATA, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ List<String> headersToRemove = new ArrayList<String>();
+ headersToRemove.add(HttpHeaderEnum.USER_ID.getValue());
+
+ RestResponse catalog = CatalogRestUtils.sendGetAndRemoveHeaders(url, null, headersToRemove);
+ assertEquals("Check response code after get catalog", BaseRestUtils.STATUS_CODE_MISSING_INFORMATION, catalog.getErrorCode().intValue());
+
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), new ArrayList<String>(), catalog.getResponse());
+ }
+
+ @Test
+ public void getAllWrongUser() throws Exception {
+ RestResponse catalog = CatalogRestUtils.getCatalog("kj8976");
+ assertEquals("Check response code after get catalog", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, catalog.getErrorCode().intValue());
+
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), catalog.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void getAllWithProductStrategist_User() throws Exception {
+ createProductAndGet(UserRoleEnum.PRODUCT_STRATEGIST1);
+ }
+
+ @Test // (enabled=false)
+ public void getAllWithProductManager_User() throws Exception {
+ createProductAndGet(UserRoleEnum.PRODUCT_MANAGER1);
+ }
+
+ @Test // (enabled=false)
+ public void createProductNoCategories() throws Exception {
+ createProductWithCategories(null);
+ }
+
+ @Test // (enabled=false)
+ public void createProductOneGrouping() throws Exception {
+ // Category1->[Subcategory1->[Grouping1]]
+ createProductWithCategories(defaultCategories);
+ }
+
+ @Test // (enabled=false)
+ public void createProductTwoGroupingsSameSubCategory() throws Exception {
+ // Category1->Subcategory1->[Grouping1, Grouping11]
+ List<CategoryDefinition> addSecondGroupingToDefaultCategory = addSecondGroupingToDefaultCategory();
+ createProductWithCategories(addSecondGroupingToDefaultCategory);
+ }
+
+ @Test // (enabled=false)
+ public void createProductTwoSubsDifferentGroupings() throws Exception {
+ // Category1->[Subcategory1->[Grouping1,
+ // Grouping11],Subcategory2->[Grouping12]]
+ List<CategoryDefinition> addSubcategoryAndGroupingToDefaultCategory = addSubcategoryAndGroupingToDefaultCategory();
+ createProductWithCategories(addSubcategoryAndGroupingToDefaultCategory);
+ }
+
+ @Test // (enabled=false)
+ public void createManyGroupingsDiffCategories() throws Exception {
+ // [Category1->[Subcategory1->[Grouping1,
+ // Grouping11],Subcategory2->[Grouping12]],
+ // Category2->[Subcategory1->[Grouping1],Subcategory2->[Grouping1]],
+ // Category3->[Subcategory1->[Grouping11],Subcategory2->[Grouping11,
+ // Grouping22]]]
+ List<CategoryDefinition> addSubcategoryAndGroupingToDefaultCategory = addManyGroupingsDiffCategories();
+ createProductWithCategories(addSubcategoryAndGroupingToDefaultCategory);
+ }
+
+ @Test // (enabled=false)
+ public void createProductEmptyUserId() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ User emptyUser = new User();
+ emptyUser.setUserId("");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, emptyUser);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_MISSING_INFORMATION, createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION, emptyUser);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct, CREATE_AUDIT_ACTION, emptyUser, ActionStatus.MISSING_INFORMATION, Constants.EMPTY_STRING, Constants.EMPTY_STRING, null,
+ null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test // (enabled=false)
+ public void createProductNonExistingUserId() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ User notExistingUser = new User();
+ notExistingUser.setUserId("jj6444");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, notExistingUser);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION, notExistingUser);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct, CREATE_AUDIT_ACTION, notExistingUser, ActionStatus.RESTRICTED_OPERATION, Constants.EMPTY_STRING, Constants.EMPTY_STRING,
+ null, null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test // (enabled=false)
+ public void createProductInvalidJson() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct_Invalid_Json(productManager1.getUserId());
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION, productManager1);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.INVALID_CONTENT, Constants.EMPTY_STRING, Constants.EMPTY_STRING, null,
+ null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setRESOURCE_NAME("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test // (enabled=false)
+ public void createProductAdminRoleNotAllowed() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ User wrongRole = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, wrongRole);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION, wrongRole);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct, CREATE_AUDIT_ACTION, wrongRole, ActionStatus.RESTRICTED_OPERATION, Constants.EMPTY_STRING, Constants.EMPTY_STRING, null,
+ null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test // (enabled=false)
+ public void createProductProductStrategistRoleNotAllowed() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ User wrongRole = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST3);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, wrongRole);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, createProduct.getErrorCode().intValue());
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION, wrongRole);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct, CREATE_AUDIT_ACTION, wrongRole, ActionStatus.RESTRICTED_OPERATION, Constants.EMPTY_STRING, Constants.EMPTY_STRING, null,
+ null, Constants.EMPTY_STRING);
+ constructFieldsForAuditValidation.setCURR_VERSION("");
+ constructFieldsForAuditValidation.setCURR_STATE("");
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ @Test // (enabled=false)
+ public void getProductSuccessFlow() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code after getting created Product", BaseRestUtils.STATUS_CODE_SUCCESS, getProductRes.getErrorCode().intValue());
+
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.GET_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void getNonExistedProduct() throws Exception {
+
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code after getting created Product", BaseRestUtils.STATUS_CODE_SUCCESS, getProductRes.getErrorCode().intValue());
+
+ Product product = ResponseParser.parseToObjectUsingMapper(getProductRes.getResponse(), Product.class);
+ assertEquals("Assert on product icon", "Product1", product.getName());
+
+ RestResponse deleteProductRes = ProductRestUtils.deleteProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code for deletign Product", BaseRestUtils.STATUS_CODE_SUCCESS, deleteProductRes.getErrorCode().intValue());
+
+ RestResponse getProductAfterDeleteRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code after getting deleted Product", BaseRestUtils.STATUS_CODE_NOT_FOUND, getProductAfterDeleteRes.getErrorCode().intValue());
+ }
+
+ @Test // (enabled=false)
+ public void getProductMissingHeader() throws Exception {
+
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+
+ productManager1.setUserId(null);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code after getting created Producuct with userId extracted from header", BaseRestUtils.STATUS_CODE_MISSING_INFORMATION, getProductRes.getErrorCode().intValue());
+
+ }
+
+ @Test // (enabled=false)
+ public void getProductNonExistingUser() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+
+ productManager1.setUserId("bt1111");
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code after getting created Producuct with non exsisting user", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, getProductRes.getErrorCode().intValue());
+ }
+
+ @Test // (enabled=false)
+ public void createProductAndGetProductWithDifferentUser() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+ User sdncProductStrategistUserAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), sdncProductStrategistUserAdminDetails.getUserId());
+ assertEquals("Check response code after getting created Product different user role", BaseRestUtils.STATUS_CODE_SUCCESS, getProductRes.getErrorCode().intValue());
+ }
+
+ // US594753 - Update Product metadata
+
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductAllFieldsByPM() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ // Update product
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("NewProductName");
+ List<CategoryDefinition> addSecondGroupingToDefaultCategory = addSecondGroupingToDefaultCategory();
+ productReqDetails.setFullName("New Full name");
+ productReqDetails.setActive("false");
+ productReqDetails.setContacts(Arrays.asList(productManager2.getUserId().toLowerCase(), productManager1.getUserId().toLowerCase()));
+ productReqDetails.setDescription("New Product Description");
+ productReqDetails.setIcon("asdfghjklqwertyuiozxcvbfv");
+ productReqDetails.setProjectCode("98765");
+ productReqDetails.setCategories(addSecondGroupingToDefaultCategory);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // productReqDetails.setTags(Arrays.asList(productReqDetails.getName(),
+ // productOldName));
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(productReqDetails.getName().toLowerCase());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductByPS() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("New discription");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productStrategistUser1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void updateProductByAdmin() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("New discription");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, designerUser);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void updateProductByNonPmUser() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ // Update product name
+ productReqDetails.setDescription("New discription");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, designerUser);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void updateProductByNonAsdcUser() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ // Update product name
+ productReqDetails.setDescription("New discription");
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ nonAsdcUser.setUserId("bt789k");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, nonAsdcUser);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void updateProductUserIdIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ // Update product name
+ productReqDetails.setDescription("New discription");
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ nonAsdcUser.setUserId("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, nonAsdcUser);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_MISSING_INFORMATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void updateProductByNonProductOwner() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct(defaultCategories);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ Product product = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("New discription");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager2);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.GET_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNotInCheckoutState() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct(defaultCategories);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ Product product = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ // Update product name
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(), updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ String valueFromJsonResponse = ResponseParser.getValueFromJsonResponse(changeProductLifeCycle.getResponse(), "lastUpdateDate");
+ expectedProduct.setLastUpdateDate(Long.parseLong(valueFromJsonResponse));
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNameIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("abbreviated");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNameIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager2, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ // List<String> tags = productReqDetails.getTags();
+ // tags.removeAll(tags);
+ productReqDetails.setTags(new ArrayList<>());
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName(null); // no update will be performed
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager2);
+ ProductRestUtils.checkSuccess(updateProduct);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager2);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(product.getName().toLowerCase());
+ expectedProduct.setName(product.getName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNameLessThanMinLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("ABC"); // no update will be performed
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("abbreviated");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductNameHasMinLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager2, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("NewP");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager2);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ productReqDetails.setLastUpdaterUserId(productManager2.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager2.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager2);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(productReqDetails.getName().toLowerCase());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ // DE193857 - Normalized Name is not removing special characters
+ @Test(enabled = false)
+ public void updateProductNameMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ // Update product name
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("Ac_2B3U4k mSKnob-u.j-uGgP");
+ String newNormalizedName = "ac2b3u4kmsknobujuggp";
+ String newName = "Ac_2B3U4k MSKnob-u.j-uGgP";
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setTags(Arrays.asList(newName));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(newNormalizedName);
+ expectedProduct.setName(newName);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNameExceedMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("Ac_2B3U4k mSKnob-u.j-uGgPx");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("abbreviated");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNameAlreadyExist() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct(defaultCategories);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager2);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ Product product1 = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product1, productManager2, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setName("CiProduct2000");
+ // productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ createProduct = ProductRestUtils.createProduct(productReqDetails, productManager2);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ Product product2 = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product2, productManager2, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product2, productManager2, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product2.getUniqueId());
+ productReqDetails.setUUID(product2.getUUID());
+ productReqDetails.setName(product1.getName());
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager2);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_ALREADY_EXISTS, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add(product1.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_ALREADY_EXIST.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager2.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product2.getUniqueId());
+ expectedProduct.setVersion(product2.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // DE193857 - Normalized Name is not removing special characters
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductNameAllowedCharacters() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ // Update product name
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("A_BU4k m&K=o#b-u.j-uG'g+P"); // Allowed
+ // characters
+ // [a-z,A-Z,0-9]
+ // , ‘ ‘
+ // (space),
+ // ampersand
+ // dash
+ // “-“, plus
+ // "+", period
+ // ".",
+ // apostrophe
+ // "'", hashtag
+ // "#", equal
+ // "=", period
+ // ":", at "@",
+ // and
+ // underscore
+ //
+ String newNormalizedName = "abu4km&kobujuggp";
+ String newName = "A_BU4k M&K=o#b-u.j-uG'g+P";
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setTags(Arrays.asList(newName));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(newNormalizedName);
+ expectedProduct.setName(newName);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductNameRemoveSpaceFromBeginning() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName(" asdfg");
+ String newNormalizedName = "asdfg";
+ String newName = "Asdfg";
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // productReqDetails.setTags(Arrays.asList(newName, productOldName));
+ productReqDetails.setTags(Arrays.asList(newName));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(newNormalizedName);
+ expectedProduct.setName(newName);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductNameRemoveSpaceFromEnd() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("asdfg fc ");
+ String newNormalizedName = "asdfgfc";
+ String newName = "Asdfg Fc";
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // productReqDetails.setTags(Arrays.asList(newName, productOldName));
+ productReqDetails.setTags(Arrays.asList(newName));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(newNormalizedName);
+ expectedProduct.setName(newName);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ //// DE193857 - Normalized Name is not removing special characters
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductNameRemoveExtraNonAlphanumericChars() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("A__k &&==##---u..hG'''+++");
+ String newNormalizedName = "akhg";
+ String newName = "A_k &=#-u.hG'+";
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // productReqDetails.setTags(Arrays.asList(newName, productOldName));
+ productReqDetails.setTags(Arrays.asList(newName));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(newNormalizedName);
+ expectedProduct.setName(newName);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // If user update Product Name we need to remove the old product name from
+ // Tags and add the new product name instead - will be handled in new US
+ @Test(enabled = false)
+ public void updateProductNameValidationStartWithNumber() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("1000Ab");
+ String newNormalizedName = productReqDetails.getName().toLowerCase();
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName()));
+ // productReqDetails.setTags(Arrays.asList(productReqDetails.getName(),
+ // productOldName));
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setNormalizedName(newNormalizedName);
+ expectedProduct.setName(productReqDetails.getName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductNameValidationStartWithNonAlphaNumeric() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setName("_1000Ab");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("abbreviated");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("full");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setNormalizedName(product.getNormalizedName());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setFullName(product.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameHasMinLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("asdc");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setNormalizedName(product.getNormalizedName());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setFullName(productReqDetails.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameHasMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setNormalizedName(product.getNormalizedName());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setFullName(productReqDetails.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNamelessThanMinLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("123");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("full");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameExceedMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjkx");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("full");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ // DE193947
+ @Test
+ public void updateProductFullNameWithSpecialCharacters() throws Exception {
+ char invalidChars[] = { '~', '!', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/', '|', '\\', ',', '$', '#', '@', '+' };
+ String fullName = "avbng";
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ for (int i = 0; i < invalidChars.length; i++) {
+ productReqDetails.setFullName(fullName + invalidChars[i]);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, updateProduct.getErrorCode().intValue());
+ }
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameValidCharactersCharacters01() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("qwertyuiopasdfghjklzxcvbnm1234567890<b>Bold<</b>");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setNormalizedName(product.getNormalizedName());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setFullName("qwertyuiopasdfghjklzxcvbnm1234567890Bold&lt;");
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductFullNameRemoveExtraSpaces() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setFullName("Abbaaa a1");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setNormalizedName(product.getNormalizedName());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setFullName("Abbaaa a1");
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test(enabled = false)
+ public void updateProductDescriptionValidCharacters01() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("~!@#$%^&*()_+<>?qwertyuiopasdfghjklzxcvbnm1234567890#");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setDescription("~!@#$%^&amp;*()_+&lt;&gt;?qwertyuiopasdfghjklzxcvbnm1234567890#");
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionValidCharacters02() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("qwertyuiopasdfghjklzxcvbnm1234567890<b>Bold<</b>");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setDescription("qwertyuiopasdfghjklzxcvbnm1234567890Bold&lt;");
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionInValidCharacters() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("מה");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_DESCRIPTION.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionRemoveSpacesFromBeginning() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription(" abcd12345 g");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setDescription("abcd12345 g");
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionRemoveSpacesFromTheEnd() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setDescription("abcd12345 gdf ");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setDescription("abcd12345 gdf");
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ String description = "1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfg";
+ productReqDetails.setDescription(description);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setDescription(description);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductDescriptionExceedMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ String description = "01234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjk aa1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfghjklzxcvbnm1234567890qwertyuiopasdfg";
+ productReqDetails.setDescription(description);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("1024");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductTagIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList(""));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ varibales.add("tag");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FIELD_FORMAT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductTagIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setTags(product.getTags());
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductTagsNameValidationProductNameIsNotInTag() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList("Abc"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME.name(), new ArrayList<String>(), updateProduct.getResponse());
+ }
+
+ @Test // (enabled=false)
+ public void createProductSingleTagMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345678"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setTags(productReqDetails.getTags());
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductSingleTagExceedMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh123456788"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("50");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductAllTagsMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345601", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345602", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345603",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345604", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345605", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345606", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345607",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345608", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh1234569", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345610", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345611",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345612", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345613", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345614", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345615",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345616", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345617", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345618", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345619",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setTags(productReqDetails.getTags());
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductAllTagsExceedMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345601", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345602", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345603",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345604", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345605", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345606", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345607",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345608", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh1234569", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345610", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345611",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345612", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345613", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345614", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345615",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345616", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345617", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345618", "Abba1234567890asdfghjkl123zxcvbnm432asdfgh12345619",
+ "Abba1234567890asdfghjkl123zxcvbnm432asdfgh123456"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add("1024");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductTagsDuplicateTagRemoved() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setTags(Arrays.asList(productReqDetails.getName(), "KoKo", "KoKo"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata updated
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setTags(Arrays.asList(productReqDetails.getName(), "KoKo"));
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductContactsIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setContacts(Arrays.asList(""));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductContactsIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setContacts(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductContactsInvalidFormat() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setContacts(Arrays.asList("bt750345"));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(COMPONENT_TYPE);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductConvertContactsToLowerCase() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setContacts(Arrays.asList(productManager2.getUserId().toUpperCase()));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setContacts(Arrays.asList(productManager2.getUserId().toLowerCase(), productManager1.getUserId()));
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductContactsNotAllowedAsdcUsers() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setContacts(Arrays.asList(productStrategistUser1.getUserId()));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(productStrategistUser1.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PRODUCT_CONTACT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductContactsNotAsdcUser() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ String nonAsdcUserUserId = "bt567h";
+ productReqDetails.setContacts(Arrays.asList(nonAsdcUserUserId));
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(nonAsdcUserUserId);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PRODUCT_CONTACT.name(), varibales, updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_PROJECT_CODE.name(), new ArrayList<String>(), updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setProjectCode(product.getProjectCode());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeLessThanMinCharacters() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode("9870");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROJECT_CODE.name(), new ArrayList<String>(), updateProduct.getResponse());
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeHasnMinCharacters() throws Exception { // min
+ // =5
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode("98700");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setProjectCode(productReqDetails.getProjectCode());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeHasnMaxCharacters() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode("1234567890");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setProjectCode(productReqDetails.getProjectCode());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeExceedMaxCharacters() throws Exception {// Max
+ // =10
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode("12345678901");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROJECT_CODE.name(), new ArrayList<String>(), updateProduct.getResponse());
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setProjectCode(product.getProjectCode());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductProjectCodeIsNotNumeric() throws Exception {
+ // Max =10
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setProjectCode("1234a");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROJECT_CODE.name(), new ArrayList<String>(), updateProduct.getResponse());
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setProjectCode(product.getProjectCode());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIconIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setIcon("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> variables = new ArrayList<String>();
+ variables.add(COMPONENT_TYPE);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_ICON.name(), variables, updateProduct.getResponse());
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIcon(product.getIcon());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIconIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setIcon(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ // Get Product and verify that metadata didn't change
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIcon(product.getIcon());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIconMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setIcon("A_a-1-2--b__BB1234567890A"); // Max length =
+ // 25
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIcon(productReqDetails.getIcon());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIconExceedMaxLength() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setIcon("A_a-1-2--b__BB1234567890A_");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> variables = new ArrayList<String>();
+ variables.add(COMPONENT_TYPE);
+ variables.add("25");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT.name(), variables, updateProduct.getResponse());
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIcon(product.getIcon());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIconInValidCharacters() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ String icon = "asdfg"; // Allowed characters [a-zA-Z0-9], dash (‘-‘),
+ // underscore (‘_’).
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/', '|', '\\', ',' };
+ RestResponse updateProduct;
+ for (int i = 0; i < invalidChars.length; i++) {
+ productReqDetails.setIcon(icon + invalidChars[i]);
+ updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ArrayList<String> variables = new ArrayList<String>();
+ variables.add(COMPONENT_TYPE);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_ICON.name(), variables, updateProduct.getResponse());
+ }
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIcon(product.getIcon());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIsActiveIsEmpty() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setActive("");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIsActive(false);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIsActiveIsTrue() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setActive("true");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIsActive(true);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIsActiveIsNull() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct(defaultCategories);
+ productReqDetails.setActive("true");
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ Product product = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setActive(null);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIsActive(true);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIsActiveIsFalse() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setActive("false");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ expectedProduct.setIsActive(false);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductIsActiveHasInvalidValue() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setActive("eeeee");
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(), updateProduct.getResponse());
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_INVALID_CONTENT, updateProduct.getErrorCode().intValue());
+ RestResponse getProduct = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ ProductRestUtils.checkSuccess(getProduct);
+ Product expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ expectedProduct.setUniqueId(product.getUniqueId());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(getProduct.getResponse(), Product.class);
+ expectedProduct.setVersion(product.getVersion());
+ expectedProduct.setLastUpdaterUserId(productManager1.getUserId());
+ expectedProduct.setLastUpdaterFullName(productManager1.getFullName());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductAssociations() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ List<CategoryDefinition> addSecondGroupingToDefaultCategory = addSecondGroupingToDefaultCategory();
+ productReqDetails.setCategories(addSecondGroupingToDefaultCategory);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setCategories(productReqDetails.getCategories());
+ expectedProduct.setNormalizedName(productReqDetails.getName().toLowerCase());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductAssociations02() throws Exception {
+ List<CategoryDefinition> addSecondGroupingToDefaultCategory = addSecondGroupingToDefaultCategory(); // Category1->Subcategory1->[Grouping1,
+ // Grouping11]
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct(addSecondGroupingToDefaultCategory);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ Product product = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ List<CategoryDefinition> defaultCategory = addSubcategoryAndGroupingToDefaultCategory(); // Category1->[Subcategory1->[Grouping1,
+ // Grouping11],Subcategory2->[Grouping12]]
+ productReqDetails.setCategories(defaultCategory);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setCategories(productReqDetails.getCategories());
+ expectedProduct.setNormalizedName(productReqDetails.getName().toLowerCase());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductRemoveAllAssociations() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ List<CategoryDefinition> defaultCategory = new ArrayList<CategoryDefinition>();
+ productReqDetails.setCategories(defaultCategory);
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setCategories(productReqDetails.getCategories());
+ expectedProduct.setNormalizedName(productReqDetails.getName().toLowerCase());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ @Test // (enabled=false)
+ public void updateProductAssociationsCategotyIsNull() throws Exception {
+ createProducrByPSAndCheckIn();
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ productReqDetails.setUniqueId(product.getUniqueId());
+ productReqDetails.setUUID(product.getUUID());
+ productReqDetails.setCategories(null);// product categories will not be
+ // updated
+ RestResponse updateProduct = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkSuccess(updateProduct);
+ productReqDetails.setLastUpdaterUserId(productManager1.getUserId());
+ productReqDetails.setLastUpdaterFullName(productManager1.getFullName());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(updateProduct.getResponse(), Product.class);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, "0.2", productManager1);
+ expectedProduct.setUUID(product.getUUID());
+ expectedProduct.setInvariantUUID(product.getInvariantUUID());
+ expectedProduct.setCategories(product.getCategories());
+ expectedProduct.setNormalizedName(productReqDetails.getName().toLowerCase());
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.UPDATE_COMPONENT);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+
+ private void createProductWithCategories(List<CategoryDefinition> categoryDefinitions) throws Exception {
+ ProductReqDetails productReqDetails = (categoryDefinitions != null ? ElementFactory.getDefaultProduct(categoryDefinitions) : ElementFactory.getDefaultProduct());
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ String actualUuid = ResponseParser.getUuidFromResponse(createProduct);
+ Product expectedProduct = Convertor.constructFieldsForRespValidation(productReqDetails, INITIAL_PRODUCT_VERSION, productManager1);
+ String normalizedNameFomJsonResponse = ResponseParser.getValueFromJsonResponse(createProduct.getResponse(), "normalizedName");
+ expectedProduct.setNormalizedName(normalizedNameFomJsonResponse);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, ComponentOperationEnum.CREATE_COMPONENT);
+ ExpectedProductAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(expectedProduct, CREATE_AUDIT_ACTION, productManager1, ActionStatus.CREATED, Constants.EMPTY_STRING, "0.1", null,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, actualUuid);
+ AuditValidationUtils.validateAuditProduct(constructFieldsForAuditValidation, CREATE_AUDIT_ACTION);
+ }
+
+ private void createProducrByPSAndCheckIn() throws Exception {
+ productReqDetails = ElementFactory.getDefaultProduct(defaultCategories);
+ createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ ProductRestUtils.checkCreateResponse(createProduct);
+ product = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ RestResponse changeProductLifeCycle = ProductRestUtils.changeProductLifeCycle(product, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.checkSuccess(changeProductLifeCycle);
+ }
+
+ @Test
+ public void checkInvariantUuidIsImmutable() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ String invariantUuidDefinedByUser = "!!!!!!!!!!!!!!!!!!!!!!!!";
+ productReqDetails.setInvariantUUID(invariantUuidDefinedByUser);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ BaseRestUtils.checkStatusCode(createProduct, "create request failed", false, 201);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+ Product ProductCreation = ResponseParser.convertProductResponseToJavaObject(createProduct.getResponse());
+ String invariantUUIDcreation = ProductCreation.getInvariantUUID();
+
+ // validate get response
+ RestResponse getProductRes = ProductRestUtils.getProduct(productReqDetails.getUniqueId(), productManager1.getUserId());
+ BaseRestUtils.checkSuccess(getProductRes);
+ Product productGetting = ResponseParser.convertProductResponseToJavaObject(getProductRes.getResponse());
+ String invariantUUIDgetting = productGetting.getInvariantUUID();
+ assertEquals(invariantUUIDcreation, invariantUUIDgetting);
+
+ // Update Product with new invariant UUID
+ RestResponse restResponseUpdate = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ BaseRestUtils.checkSuccess(restResponseUpdate);
+ Product updatedProduct = ResponseParser.convertProductResponseToJavaObject(restResponseUpdate.getResponse());
+ String invariantUUIDupdating = updatedProduct.getInvariantUUID();
+ assertEquals(invariantUUIDcreation, invariantUUIDupdating);
+
+ // Do checkin
+ RestResponse restResponseCheckin = LifecycleRestUtils.changeProductState(productReqDetails, productManager1, LifeCycleStatesEnum.CHECKIN);
+ BaseRestUtils.checkSuccess(restResponseCheckin);
+ Product checkinProduct = ResponseParser.convertProductResponseToJavaObject(restResponseCheckin.getResponse());
+ String invariantUUIDcheckin = checkinProduct.getInvariantUUID();
+ String version = checkinProduct.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcheckin);
+ assertEquals(version, "0.1");
+
+ // Do checkout
+ RestResponse restResponseCheckout = LifecycleRestUtils.changeProductState(productReqDetails, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ BaseRestUtils.checkSuccess(restResponseCheckout);
+ Product checkoutProduct = ResponseParser.convertProductResponseToJavaObject(restResponseCheckout.getResponse());
+ String invariantUUIDcheckout = checkoutProduct.getInvariantUUID();
+ version = checkoutProduct.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcheckout);
+ assertEquals(version, "0.2");
+
+ }
+
+ // US672129 Benny
+ private void getProductValidateInvariantUuid(String productUniqueId, String invariantUUIDcreation) throws Exception {
+ RestResponse getProduct = ProductRestUtils.getProduct(productUniqueId, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, getProduct.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(getProduct));
+ }
+
+ @Test // invariantUUID generated when the component is created and never
+ // changed
+ public void productInvariantUuid() throws Exception {
+ ProductReqDetails productReqDetails = ElementFactory.getDefaultProduct();
+ String invariantUuidDefinedByUser = "12345";
+ productReqDetails.setInvariantUUID(invariantUuidDefinedByUser);
+ RestResponse createProduct = ProductRestUtils.createProduct(productReqDetails, productManager1);
+ assertEquals("Check response code after create resource", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+ // invariantUUID generated when the component is created and never
+ // changed
+ String invariantUUIDcreation = ResponseParser.getInvariantUuid(createProduct);
+ getProductValidateInvariantUuid(productReqDetails.getUniqueId(), invariantUUIDcreation);
+ // Update Product with new invariant UUID
+ RestResponse restResponse = ProductRestUtils.updateProduct(productReqDetails, productManager1);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getProductValidateInvariantUuid(productReqDetails.getUniqueId(), invariantUUIDcreation);
+ // Checkin
+ restResponse = LifecycleRestUtils.changeProductState(productReqDetails, productManager1, LifeCycleStatesEnum.CHECKIN);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getProductValidateInvariantUuid(productReqDetails.getUniqueId(), invariantUUIDcreation);
+ // Checkout
+ restResponse = LifecycleRestUtils.changeProductState(productReqDetails, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getProductValidateInvariantUuid(productReqDetails.getUniqueId(), invariantUUIDcreation);
+
+ // UnDo-CheckOut
+ restResponse = LifecycleRestUtils.changeProductState(productReqDetails, productManager1, LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getProductValidateInvariantUuid(productReqDetails.getUniqueId(), invariantUUIDcreation);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductGetFollowedTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductGetFollowedTest.java
new file mode 100644
index 0000000000..aebea697b2
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductGetFollowedTest.java
@@ -0,0 +1,164 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ProductValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ProductGetFollowedTest extends ProductBaseTest {
+
+ protected Product product200;
+ protected Product product400;
+
+ private ProductReqDetails productDetails200;
+ private ProductReqDetails productDetails400;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ProductGetFollowedTest() {
+ super(name, ProductGetFollowedTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+ createProducts();
+ }
+
+ @Test
+ public void followedPageTest() throws Exception { // Actions
+ RestResponse changeLifeCycleResponse;
+ changeLifeCycleResponse = ProductRestUtils.changeProductLifeCycle(product200, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeLifeCycleResponse);
+ changeLifeCycleResponse = ProductRestUtils.changeProductLifeCycle(product400, productManager2, LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeLifeCycleResponse);
+ // Expected users Followed page
+ ProductValidationUtils.checkUserFollowedPage(productManager1, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager1, product400);
+ ProductValidationUtils.checkUserFollowedPage(productManager2, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager2, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(adminUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(designerUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productStrategistUser1, product200, product400);
+ }
+
+ @Test
+ public void followedPagePmCheckedOutProductWasCheckInByOtherPm() throws Exception {
+ ProductRestUtils.changeProductLifeCycle(product200, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.changeProductLifeCycle(product400, productManager2, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.changeProductLifeCycle(product400, productManager1, LifeCycleStatesEnum.CHECKOUT);
+ RestResponse changeLifeCycleResponse = ProductRestUtils.changeProductLifeCycle(product200, productManager2, LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeLifeCycleResponse);
+
+ ProductValidationUtils.checkUserFollowedPage(productManager1, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager2, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(adminUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(designerUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productStrategistUser1, product200, product400);
+ }
+
+ @Test
+ public void followedPagePmCheckInProduct02() throws Exception {
+ ProductRestUtils.changeProductLifeCycle(product200, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.changeProductLifeCycle(product400, productManager2, LifeCycleStatesEnum.CHECKIN);
+
+ ProductValidationUtils.checkUserFollowedPage(productManager1, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager1, product400);
+ ProductValidationUtils.checkUserFollowedPage(productManager2, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager2, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(adminUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(designerUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productStrategistUser1, product200, product400);
+ }
+
+ @Test
+ public void followedPageAdminCheckoutProductWasCheckedinByPm() throws Exception {
+ ProductRestUtils.changeProductLifeCycle(product400, productManager2, LifeCycleStatesEnum.CHECKIN);
+ RestResponse changeLifeCycleResponse = ProductRestUtils.changeProductLifeCycle(product400, adminUser, LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeLifeCycleResponse);
+
+ ProductValidationUtils.checkUserFollowedPage(productManager1, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager1, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager2, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(adminUser, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(designerUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productStrategistUser1, product200, product400);
+ }
+
+ @Test
+ public void followedPageAdminCheckInProduct() throws Exception {
+ ProductRestUtils.changeProductLifeCycle(product400, productManager2, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.changeProductLifeCycle(product400, adminUser, LifeCycleStatesEnum.CHECKOUT);
+ RestResponse changeLifeCycleResponse = ProductRestUtils.changeProductLifeCycle(product400, adminUser, LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(changeLifeCycleResponse);
+
+ ProductValidationUtils.checkUserFollowedPage(productManager1, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager1, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager2, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(adminUser, product200);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(designerUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productStrategistUser1, product200, product400);
+ }
+
+ @Test
+ public void followedPagePmCheckoutProductWasCheckedinByAdmin() throws Exception {
+ ProductRestUtils.changeProductLifeCycle(product200, productManager1, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.changeProductLifeCycle(product400, productManager2, LifeCycleStatesEnum.CHECKIN);
+ ProductRestUtils.changeProductLifeCycle(product200, adminUser, LifeCycleStatesEnum.CHECKOUT);
+ ProductRestUtils.changeProductLifeCycle(product200, adminUser, LifeCycleStatesEnum.CHECKIN);
+ RestResponse changeLifeCycleResponse = ProductRestUtils.changeProductLifeCycle(product200, productManager2, LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(changeLifeCycleResponse);
+
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productManager1, product200);
+ ProductValidationUtils.checkUserFollowedPage(productManager2, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(adminUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(designerUser, product200, product400);
+ ProductValidationUtils.verifyProductsNotExistInUserFollowedPage(productStrategistUser1, product200, product400);
+ }
+
+ private void createProducts() throws Exception {
+ // PM1 (Product manager) create :PR200 (check In State)
+ // PM2 (Product manager) create :PR400 (check In State)
+
+ productDetails200 = ElementFactory.getDefaultProduct("CiProd200", defaultCategories);
+ productDetails400 = ElementFactory.getDefaultProduct("CiProd400", defaultCategories);
+
+ RestResponse createProduct = ProductRestUtils.createProduct(productDetails200, productManager1);
+ ResourceRestUtils.checkCreateResponse(createProduct);
+ product200 = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+
+ createProduct = ProductRestUtils.createProduct(productDetails400, productManager2);
+ ResourceRestUtils.checkCreateResponse(createProduct);
+ product400 = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductLifecycleTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductLifecycleTest.java
new file mode 100644
index 0000000000..8c3409342b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductLifecycleTest.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.annotations.BeforeMethod;
+
+public abstract class ProductLifecycleTest extends ProductBaseTest {
+
+ protected static final String CHECKIN_ACTION = "Checkin";
+ protected static final String CHECKOUT_ACTION = "Checkout";
+ protected static final String UNDO_CHECKOUT_ACTION = "UndoCheckout";
+
+ protected Product expectedProduct;
+
+ public ProductLifecycleTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+ ProductReqDetails defaultProduct = ElementFactory.getDefaultProduct(defaultCategories);
+ RestResponse createProduct = ProductRestUtils.createProduct(defaultProduct, productManager1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED, createProduct.getErrorCode().intValue());
+ expectedProduct = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductTestBase.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductTestBase.java
new file mode 100644
index 0000000000..479dbb9436
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductTestBase.java
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.apache.log4j.lf5.util.ResourceUtils;
+import org.junit.Before;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.ArtifactUtils;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+
+public class ProductTestBase extends ProductBaseTest {
+
+ public ProductTestBase(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ protected ResourceReqDetails downloadResourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+ protected User sdncUserDetails;
+ protected ArtifactReqDetails heatArtifactDetails;
+ protected ArtifactReqDetails defaultArtifactDetails;
+ protected ResourceUtils resourceUtils;
+ protected ArtifactUtils artifactUtils;
+ protected Resource resource;
+ protected Service service;
+ protected Product product;
+
+ // protected static ServiceUtils serviceUtils = new ServiceUtils();
+
+ @Before
+ public void before() throws Exception {
+
+ initializeMembers();
+ createComponents();
+
+ }
+
+ public void initializeMembers() throws IOException, Exception {
+
+ downloadResourceDetails = ElementFactory.getDefaultResource();
+ serviceDetails = ElementFactory.getDefaultService();
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ resourceInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+
+ }
+
+ protected void createComponents() throws Exception {
+
+ RestResponse response = ResourceRestUtils.createResource(downloadResourceDetails, sdncUserDetails);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", downloadResourceDetails.getUniqueId());
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncUserDetails,
+ downloadResourceDetails.getUniqueId());
+ assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ // certified resource
+ response = LifecycleRestUtils.certifyResource(downloadResourceDetails);
+ assertTrue("certify resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ response = ResourceRestUtils.getResource(downloadResourceDetails.getUniqueId());
+ assertTrue("response code is not 200, returned: " + response.getErrorCode(), response.getErrorCode() == 200);
+ resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+
+ response = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("service uniqueId is null:", serviceDetails.getUniqueId());
+
+ // add resource instance with HEAT deployment artifact to the service
+ resourceInstanceReqDetails.setComponentUid(downloadResourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, sdncUserDetails,
+ serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertTrue("response code is not 201, returned: " + response.getErrorCode(), response.getErrorCode() == 201);
+
+ // certified service
+ response = LifecycleRestUtils.certifyService(serviceDetails);
+ assertTrue("certify service request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ response = ServiceRestUtils.getService(serviceDetails, sdncUserDetails);
+ assertTrue("response code is not 200, returned: " + response.getErrorCode(), response.getErrorCode() == 200);
+ service = ResponseParser.convertServiceResponseToJavaObject(response.getResponse());
+
+ DbUtils.cleanAllAudits();
+
+ ProductReqDetails defaultProduct = ElementFactory.getDefaultProduct(defaultCategories);
+ RestResponse createProduct = ProductRestUtils.createProduct(defaultProduct, productStrategistUser1);
+ assertEquals("Check response code after create Product", BaseRestUtils.STATUS_CODE_CREATED,
+ createProduct.getErrorCode().intValue());
+ product = ResponseParser.parseToObjectUsingMapper(createProduct.getResponse(), Product.class);
+
+ // add service instance to product
+ //
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductToscaYamlGenerationTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductToscaYamlGenerationTest.java
new file mode 100644
index 0000000000..412e6fd92d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductToscaYamlGenerationTest.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class ProductToscaYamlGenerationTest extends ProductTestBase {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ProductToscaYamlGenerationTest() {
+ super(name, ProductToscaYamlGenerationTest.class.getName());
+ }
+
+ @Test
+ public void productToscaYamlFormat() throws IOException {
+
+ String heatEnvArtifactHeader = (String) Utils.parseYamlConfig("heatEnvArtifactHeader");
+ // System.out.println("heatEnvArtifactHeader = \n" +
+ // heatEnvArtifactHeader);
+
+ String heatEnvArtifactFooter = (String) Utils.parseYamlConfig("heatEnvArtifactFooter");
+ // System.out.println("heatEnvArtifactFooter = \n" +
+ // heatEnvArtifactFooter);
+
+ // temporary commented
+ // RestResponse downloadResourceInstanceHeatArtifact =
+ // ArtifactRestUtils.downloadResourceInstanceArtifact(service.getUniqueId(),
+ // resourceInstanceId, sdncUserDetails,
+ // heatArtifactDefinition.getUniqueId());
+ // assertTrue("expected request returned status:" + 200 + ", actual: " +
+ // downloadResourceInstanceHeatArtifact.getErrorCode(),
+ // downloadResourceInstanceHeatArtifact.getErrorCode()==200);
+
+ // InputStream stream =
+ // getDecodedSteramFromString(downloadResourceInstanceHeatArtifact.getResponse());
+ // System.out.println(Utils.getParamValueFromYamlByKey(stream,
+ // "description", String.class));
+
+ // node_types
+
+ }
+
+ private InputStream getDecodedSteramFromString(String encoded64Payload) throws IOException {
+
+ Gson gson = new Gson();
+ ArtifactUiDownloadData artifactUiDownloadData = gson.fromJson(encoded64Payload, ArtifactUiDownloadData.class);
+ String decodedPayload = Decoder.decode(artifactUiDownloadData.getBase64Contents());
+ return new ByteArrayInputStream(decodedPayload.getBytes());
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductUndoCheckoutTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductUndoCheckoutTest.java
new file mode 100644
index 0000000000..d4bfb62377
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/product/ProductUndoCheckoutTest.java
@@ -0,0 +1,216 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.product;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+
+import java.io.FileNotFoundException;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ProductValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ProductUndoCheckoutTest extends ProductLifecycleTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ @BeforeClass
+ public static void staticInit() {
+ auditAction = UNDO_CHECKOUT_ACTION;
+ operation = ComponentOperationEnum.CHANGE_STATE_UNDO_CHECKOUT;
+ }
+
+ public ProductUndoCheckoutTest() {
+ super(name, ProductUndoCheckoutTest.class.getName());
+ }
+
+ @Test
+ public void undoCheckOutProductByPm() throws Exception {
+ undoCheckOutProductSuccess(productManager1, false);
+ }
+
+ @Test
+ public void undoCheckOutProductByAdmin() throws Exception {
+ undoCheckOutProductSuccess(adminUser, true);
+ }
+
+ @Test
+ public void undoCheckOutAfterCreate() throws Exception {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals("Check response code after undo checkout product", 200, lcsResponse.getErrorCode().intValue());
+
+ // Verify version was removed
+ lcsResponse = ProductRestUtils.getProduct(expectedProduct.getUniqueId(), productManager1.getUserId());
+ assertEquals("Check response code after get undone product", 404, lcsResponse.getErrorCode().intValue());
+
+ Product emptyProduct = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+ assertNull(emptyProduct);
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(emptyProduct,
+ auditAction, productManager1, ActionStatus.OK, "0.1", Constants.EMPTY_STRING,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null, Constants.EMPTY_STRING);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+
+ }
+
+ @Test
+ public void undoCheckOutNotExist() throws Exception {
+ String notExistId = "1234";
+ expectedProduct.setUniqueId(notExistId);
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals("Check response code after undo checkout product", 404, lcsResponse.getErrorCode().intValue());
+ expectedProduct.setName(notExistId);
+ String[] auditParameters = new String[] { Constants.EMPTY_STRING };
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, productManager1, ActionStatus.PRODUCT_NOT_FOUND, Constants.EMPTY_STRING,
+ Constants.EMPTY_STRING, null, null, Constants.EMPTY_STRING, auditParameters);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+
+ }
+
+ @Test
+ public void undoCheckOutNotInCheckout() throws Exception {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin product", 200, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals("Check response code after undo checkout product", 409, lcsResponse.getErrorCode().intValue());
+
+ String[] auditParameters = new String[] { expectedProduct.getName(), "product", productManager1.getFirstName(),
+ productManager1.getLastName(), productManager1.getUserId() };
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, productManager1, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, "0.1", "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ expectedProduct.getUUID(), auditParameters);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+
+ }
+
+ @Test
+ public void undoCheckOutProductByPsRoleNotAllowed() throws Exception {
+ undoCheckOutProductRestricted(productStrategistUser1);
+ }
+
+ @Test
+ public void undoCheckOutProductByPmNotStateOwner() throws Exception {
+ undoCheckOutProductForbidden(productManager2);
+ }
+
+ private void undoCheckOutProductSuccess(User user, boolean isAdmin) throws Exception, FileNotFoundException {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin product", 200, lcsResponse.getErrorCode().intValue());
+
+ // Checking undo checkout of admin even if not state owner
+ User checkoutUser = isAdmin ? productManager1 : user;
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, checkoutUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout product", 200, lcsResponse.getErrorCode().intValue());
+ Product productToBeUndone = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+
+ lcsResponse = LifecycleRestUtils.changeProductState(productToBeUndone, user, LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals("Check response code after undo checkout product", 200, lcsResponse.getErrorCode().intValue());
+
+ // Verify version was removed
+ lcsResponse = ProductRestUtils.getProduct(productToBeUndone.getUniqueId(), user.getUserId());
+ assertEquals("Check response code after get undone product", 404, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = ProductRestUtils.getProduct(expectedProduct.getUniqueId(), user.getUserId());
+ assertEquals("Check response code after get latest version", 200, lcsResponse.getErrorCode().intValue());
+
+ Product latestProduct = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+
+ expectedProduct.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, latestProduct, operation);
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(latestProduct,
+ auditAction, user, ActionStatus.OK, "0.2", "0.1", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, latestProduct.getUUID());
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ private void undoCheckOutProductRestricted(User undoCheckoutUser) throws Exception, FileNotFoundException {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin product", 200, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout product", 200, lcsResponse.getErrorCode().intValue());
+ Product productToBeUndone = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+
+ lcsResponse = LifecycleRestUtils.changeProductState(productToBeUndone, undoCheckoutUser,
+ LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals("Check response code after undocheckout product", 409, lcsResponse.getErrorCode().intValue());
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(productToBeUndone,
+ auditAction, undoCheckoutUser, ActionStatus.RESTRICTED_OPERATION, "0.2", "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ productToBeUndone.getUUID());
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+ private void undoCheckOutProductForbidden(User undoCheckoutUser) throws Exception, FileNotFoundException {
+ RestResponse lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkin product", 200, lcsResponse.getErrorCode().intValue());
+
+ lcsResponse = LifecycleRestUtils.changeProductState(expectedProduct, productManager1,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout product", 200, lcsResponse.getErrorCode().intValue());
+ Product productToBeUndone = ResponseParser.parseToObjectUsingMapper(lcsResponse.getResponse(), Product.class);
+
+ lcsResponse = LifecycleRestUtils.changeProductState(productToBeUndone, undoCheckoutUser,
+ LifeCycleStatesEnum.UNDOCHECKOUT);
+ assertEquals("Check response code after undocheckout product", 403, lcsResponse.getErrorCode().intValue());
+ String[] auditParameters = new String[] { productToBeUndone.getName(), "product",
+ productManager1.getFirstName(), productManager1.getLastName(), productManager1.getUserId() };
+
+ ExpectedProductAudit expectedProductAudit = Convertor.constructFieldsForAuditValidation(expectedProduct,
+ auditAction, undoCheckoutUser, ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, "0.2", "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ productToBeUndone.getUUID(), auditParameters);
+ AuditValidationUtils.validateAuditProduct(expectedProductAudit, auditAction);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/AdditionalInformationServletTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/AdditionalInformationServletTest.java
new file mode 100644
index 0000000000..303be77f92
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/AdditionalInformationServletTest.java
@@ -0,0 +1,2021 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.property;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.parser.JSONParser;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInfoParameterInfo;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.resource.ResourceApiTest;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+public class AdditionalInformationServletTest extends ComponentBaseTest {
+
+ protected Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+
+ protected Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
+
+ @Rule
+ public static TestName name = new TestName();
+
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";
+
+ protected JSONParser jsonParser = new JSONParser();
+
+ public AdditionalInformationServletTest() {
+ super(name, AdditionalInformationServletTest.class.getName());
+ }
+
+ // @Before
+ // public void deleteResources() {
+ // //TODO Evg : will be new API added for delete by name and version
+ //
+ // ResourceReqDetails resource = getResource();
+ // User user = getUser();
+ //
+ // try {
+ // String resourceName = resource.getResourceName();
+ // ResourceRestUtils.deleteResourceByNameAndVersion(user, resourceName,
+ // "0.1");
+ // ResourceRestUtils.deleteResourceByNameAndVersion(user, resourceName,
+ // "0.2");
+ // ResourceRestUtils.deleteResourceByNameAndVersion(user, resourceName,
+ // "1.0");
+ // ResourceRestUtils.deleteResourceByNameAndVersion(user, resourceName,
+ // "1.1");
+ // ResourceRestUtils.deleteResourceByNameAndVersion(user, resourceName +
+ // "aa", "0.1");
+ // resourceUtils.deleteResource_allVersions(resource, user);
+ //
+ // } catch (IOException e) {
+ // assertTrue(false);
+ // }
+ //
+ // try {
+ // ServiceReqDetails serviceDetails = getServiceDetails();
+ //
+ // RestResponse deleteServiceResponse =
+ // serviceUtils.deleteServiceByNameAndVersion(UserUtils.getAdminDetails(),
+ // serviceDetails.getServiceName(), "0.1");
+ //
+ // assertNotNull("check response object is not null after delete
+ // service",deleteServiceResponse);
+ // assertNotNull("check error code exists in response after delete
+ // service",deleteServiceResponse.getErrorCode());
+ // assertTrue("delete service failed status:" +
+ // deleteServiceResponse.getErrorCode(),
+ // deleteServiceResponse.getErrorCode() != 500);
+ //
+ // deleteServiceResponse =
+ // serviceUtils.deleteServiceByNameAndVersion(UserUtils.getAdminDetails(),
+ // serviceDetails.getServiceName(), "1.0");
+ //
+ // assertNotNull("check response object is not null after delete
+ // service",deleteServiceResponse);
+ // assertNotNull("check error code exists in response after delete
+ // service",deleteServiceResponse.getErrorCode());
+ // assertTrue("delete service failed status:" +
+ // deleteServiceResponse.getErrorCode(),
+ // deleteServiceResponse.getErrorCode() != 500);
+ //
+ // serviceUtils.deleteService_allVersions(serviceDetails, user);
+ //
+ // } catch (IOException e) {
+ // assertTrue(false);
+ // }
+ // }
+
+ @Test
+ public void updateResourceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ AssertJUnit.assertNotNull("check response object is not null after create resource", createResourceResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ String updatedKey = "ZZZ ZZZ";
+ String updatedValue = "JJJJ";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ AssertJUnit.assertNotNull("check response object is not null after create property", createProperty);
+ AssertJUnit.assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ AssertJUnit.assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ AssertJUnit.assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ fromJson.setKey(updatedKey);
+ fromJson.setValue(updatedValue);
+
+ RestResponse updatedProperty = updateAdditionalInformation(resourceId, fromJson, user, fromJson.getUniqueId());
+ AssertJUnit.assertNotNull("check response object is not null after update additional information", updatedProperty);
+ AssertJUnit.assertNotNull("check error code exists in response after additional information", updatedProperty.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after additional information", 200, updatedProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo updatedJson = gson.fromJson(updatedProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ AssertJUnit.assertFalse("check number of spaces", updatedJson.getKey().contains(" "));
+ AssertJUnit.assertEquals("check returned key", "ZZZ ZZZ", updatedJson.getKey());
+ AssertJUnit.assertEquals("check returned value", updatedValue, updatedJson.getValue());
+ AssertJUnit.assertEquals("check returned id", fromJson.getUniqueId(), updatedJson.getUniqueId());
+
+ }
+
+ @Test
+ public void deleteResourceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ AssertJUnit.assertNotNull("check response object is not null after create resource", createResourceResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ AssertJUnit.assertNotNull("check response object is not null after create property", createProperty);
+ AssertJUnit.assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ AssertJUnit.assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ AssertJUnit.assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ RestResponse deletedProperty = deleteAdditionalInformation(resourceId, fromJson.getUniqueId(), user);
+ AssertJUnit.assertNotNull("check response object is not null after update additional information", deletedProperty);
+ AssertJUnit.assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo updatedJson = gson.fromJson(deletedProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ AssertJUnit.assertFalse("check number of spaces", updatedJson.getKey().contains(" "));
+ AssertJUnit.assertEquals("check returned key", "AAA AAA", updatedJson.getKey());
+ AssertJUnit.assertEquals("check returned value", value, updatedJson.getValue());
+ AssertJUnit.assertEquals("check returned id", fromJson.getUniqueId(), updatedJson.getUniqueId());
+
+ deletedProperty = deleteAdditionalInformation(resourceId, fromJson.getUniqueId(), user);
+ AssertJUnit.assertNotNull("check response object is not null after update additional information", deletedProperty);
+ AssertJUnit.assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after additional information", 409, deletedProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceAdditionalInformationTestDuringLifecycle() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ AssertJUnit.assertNotNull("check response object is not null after create resource", createResourceResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ AssertJUnit.assertNotNull("check response object is not null after create property", createProperty);
+ AssertJUnit.assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ AssertJUnit.assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ AssertJUnit.assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ resource.setUniqueId(resourceId);
+
+ // resourceUtils.addResourceMandatoryArtifacts(user,
+ // createResourceResponse);
+
+ certifyResource(user, resource, null, 1);
+
+ }
+
+ public RestResponse createService() {
+
+ User user = getUser();
+ ServiceReqDetails serviceDetails = getServiceDetails();
+
+ RestResponse createServiceResponse = null;
+ try {
+ createServiceResponse = ServiceRestUtils.createService(serviceDetails, user);
+ AssertJUnit.assertNotNull("check response object is not null after create user", createServiceResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create resource", createServiceResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after checkout resource", 201, createServiceResponse.getErrorCode().intValue());
+ } catch (Exception e) {
+ AssertJUnit.assertTrue(false);
+ }
+
+ return createServiceResponse;
+
+ }
+
+ protected User getUser() {
+ String adminFirstName = "Jimmy";
+ String adminLastName = "Hendrix";
+ String adminUserId = "jh0003";
+ return new User(adminFirstName, adminLastName, adminUserId, null, null, null);
+ }
+
+ protected ResourceReqDetails getResource() {
+ String resourceName = "ciResourceforproperty4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "pe0123";
+ String icon = "myICON";
+ ResourceReqDetails resource = new ResourceReqDetails(resourceName, description, resourceTags, null, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resource.addCategoryChain(ResourceCategoryEnum.GENERIC_DATABASE.getCategory(), ResourceCategoryEnum.GENERIC_DATABASE.getSubCategory());
+ return resource;
+ }
+
+ protected RestResponse createResource(ResourceReqDetails resourceDetails, User sdncModifierDetails) throws IOException {
+
+ ResourceApiTest rat = new ResourceApiTest();
+ ResourceReqDetails resourceObj = rat.getResourceObj();
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resourceDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+ RestResponse createResourceResponse = http.httpSendPost(url, userBodyJson, headersMap);
+
+ return createResourceResponse;
+
+ }
+
+ protected RestResponse getResource(User sdncModifierDetails, String resourceUid) throws IOException {
+
+ ResourceApiTest rat = new ResourceApiTest();
+ ResourceReqDetails resourceObj = rat.getResourceObj();
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid);
+ RestResponse createResourceResponse = http.httpSendGet(url, headersMap);
+
+ return createResourceResponse;
+
+ }
+
+ protected RestResponse deleteResource(String resourceName, String resourceVersion, User sdncModifierDetails) throws IOException {
+ RestResponse deleteResourceResponse = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceName, resourceVersion);
+
+ return deleteResourceResponse;
+
+ }
+
+ protected RestResponse updateAdditionalInformation(String resourceId, AdditionalInfoParameterInfo additionalInfo, User sdncModifierDetails, String id) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String body = gson.toJson(additionalInfo);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_ADDITIONAL_INFORMATION_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId, id);
+ return http.httpSendPut(url, body, headersMap);
+
+ }
+
+ protected RestResponse updateServiceAdditionalInformation(String resourceId, AdditionalInfoParameterInfo additionalInfo, User sdncModifierDetails, String id) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String body = gson.toJson(additionalInfo);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_ADDITIONAL_INFORMATION_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId, id);
+ return http.httpSendPut(url, body, headersMap);
+
+ }
+
+ protected RestResponse deleteAdditionalInformation(String resourceId, String id, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.DELETE_ADDITIONAL_INFORMATION_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId, id);
+ return http.httpSendDelete(url, headersMap);
+
+ }
+
+ protected RestResponse deleteServiceAdditionalInformation(String resourceId, String id, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.DELETE_ADDITIONAL_INFORMATION_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId, id);
+ return http.httpSendDelete(url, headersMap);
+
+ }
+
+ protected RestResponse getAdditionalInformation(String resourceId, String id, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ADDITIONAL_INFORMATION_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId, id);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ protected RestResponse getServiceAdditionalInformation(String resourceId, String id, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ADDITIONAL_INFORMATION_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId, id);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ protected RestResponse getResourceAllAdditionalInformation(String resourceId, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ALL_ADDITIONAL_INFORMATION_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ protected RestResponse getServiceAllAdditionalInformation(String resourceId, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ALL_ADDITIONAL_INFORMATION_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ protected RestResponse createResourceAdditionalInformation(String resourceId, AdditionalInfoParameterInfo additionalInfo, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String body = gson.toJson(additionalInfo);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_ADDITIONAL_INFORMATION_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ return http.httpSendPost(url, body, headersMap);
+
+ }
+
+ protected RestResponse createServiceAdditionalInformation(String serviceId, AdditionalInfoParameterInfo additionalInfo, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String body = gson.toJson(additionalInfo);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_ADDITIONAL_INFORMATION_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId);
+ return http.httpSendPost(url, body, headersMap);
+
+ }
+
+ protected ServiceReqDetails getServiceDetails() {
+ String serviceName = "ciNewTestService21";
+ String category = ServiceCategoriesEnum.MOBILITY.getValue();
+ ArrayList<String> tags = new ArrayList<String>();
+ tags.add("serviceTag");
+ tags.add(serviceName);
+ String description = "service Description";
+ String vendorName = "Oracle";
+ String vendorRelease = "0.1";
+ String contactId = "al1976";
+ String icon = "myIcon";
+
+ return new ServiceReqDetails(serviceName, category, tags, description, contactId, icon);
+ }
+
+ // TODO Tal: Since Cashing change partial resource returned that causes null
+ // pointer exception in line:
+ // commented out till fixing
+ protected Resource certifyResource(User user, ResourceReqDetails resource, String resourceVersion, int numberOfAI) throws IOException {
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKIN);
+
+ AssertJUnit.assertNotNull("check response object is not null after create user", checkInResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create user", checkInResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create user", 200, checkInResponse.getErrorCode().intValue());
+
+ Resource resourceAfterOperation = gson.fromJson(checkInResponse.getResponse(), Resource.class);
+ // TODO Tal: Since Cashing change partial resource returned that causes
+ // null pointer exception
+ /*
+ * AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ */
+ /*
+ * AssertJUnit.assertEquals("check size of additional information", numberOfAI, resourceAfterOperation.getAdditionalInformation().get(0). getParameters().size());
+ */
+
+ RestResponse req4certResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+
+ AssertJUnit.assertNotNull("check response object is not null after create user", req4certResponse);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200, req4certResponse.getErrorCode().intValue());
+
+ resourceAfterOperation = gson.fromJson(req4certResponse.getResponse(), Resource.class);
+ // TODO Tal: Since Cashing change partial resource returned that causes
+ // null pointer exception
+ /*
+ * AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ */
+ /*
+ * AssertJUnit.assertEquals("check size of additional information", numberOfAI, resourceAfterOperation.getAdditionalInformation().get(0). getParameters().size());
+ */
+
+ // change modifier
+ user.setUserId(UserRoleEnum.TESTER.getUserId());
+ // start certification
+
+ RestResponse startCertResourceResponse3 = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ AssertJUnit.assertNotNull("check response object is not null after create user", startCertResourceResponse3);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200, startCertResourceResponse3.getErrorCode().intValue());
+
+ resourceAfterOperation = gson.fromJson(startCertResourceResponse3.getResponse(), Resource.class);
+ // TODO Tal: Since Cashing change partial resource returned that causes
+ // null pointer exception
+ /*
+ * AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ */
+ /*
+ * AssertJUnit.assertEquals("check size of additional information", numberOfAI, resourceAfterOperation.getAdditionalInformation().get(0). getParameters().size());
+ */
+
+ // certify
+
+ RestResponse certifyResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CERTIFY);
+ AssertJUnit.assertNotNull("check response object is not null after create user", certifyResponse);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200, certifyResponse.getErrorCode().intValue());
+
+ resourceAfterOperation = gson.fromJson(certifyResponse.getResponse(), Resource.class);
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ AssertJUnit.assertEquals("check size of additional information", numberOfAI, resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ Resource certifyResource = gson.fromJson(certifyResponse.getResponse(), Resource.class);
+ return certifyResource;
+ }
+
+ protected Resource certifyService(User user, ServiceReqDetails service, String resourceVersion) throws Exception {
+ RestResponse checkInResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.CHECKIN);
+
+ AssertJUnit.assertNotNull("check response object is not null after create user", checkInResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create user", checkInResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create user", 200, checkInResponse.getErrorCode().intValue());
+
+ Resource resourceAfterOperation = gson.fromJson(checkInResponse.getResponse(), Resource.class);
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ RestResponse req4certResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+
+ AssertJUnit.assertNotNull("check response object is not null after create user", req4certResponse);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200, req4certResponse.getErrorCode().intValue());
+
+ resourceAfterOperation = gson.fromJson(req4certResponse.getResponse(), Resource.class);
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ // change modifier
+ user.setUserId(UserRoleEnum.TESTER.getUserId());
+ // start certification
+
+ RestResponse startCertResourceResponse3 = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ AssertJUnit.assertNotNull("check response object is not null after create user", startCertResourceResponse3);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200, startCertResourceResponse3.getErrorCode().intValue());
+
+ resourceAfterOperation = gson.fromJson(startCertResourceResponse3.getResponse(), Resource.class);
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ // certify
+
+ RestResponse certifyResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.CERTIFY);
+ AssertJUnit.assertNotNull("check response object is not null after create user", certifyResponse);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200, certifyResponse.getErrorCode().intValue());
+
+ resourceAfterOperation = gson.fromJson(certifyResponse.getResponse(), Resource.class);
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ AssertJUnit.assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ Resource certifyResource = gson.fromJson(certifyResponse.getResponse(), Resource.class);
+ return certifyResource;
+ }
+
+ @Test
+ public void createResourceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceAdditionalInfoFormatWithTags() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "<b>Bold<</b>";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertEquals("check returned key", "Bold&amp;lt;", fromJson.getValue());
+
+ }
+
+ @Test
+ public void createServiceAdditionalInfoFormatWithTags() throws Exception {
+ User user = getUser();
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ assertNotNull("check response object is not null after create resource", createServiceResponse);
+ assertNotNull("check error code exists in response after create resource", createServiceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createServiceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "<b>Bold<</b>";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertEquals("check returned key", "Bold&amp;lt;", fromJson.getValue());
+
+ }
+
+ @Test
+ public void createResourceAdditionalInfoFormatWithWhiteSpaces() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = " ";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ key = " ";
+ value = "AAA AAA";
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceAdditionalInfoFormatWithWhiteSpaces() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+ ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ assertNotNull("check response object is not null after create resource", createServiceResponse);
+ assertNotNull("check error code exists in response after create resource", createServiceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createServiceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = " ";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ key = " ";
+ value = "AAA AAA";
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceAndUpdateAdditionalInfo() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ key = "BBB BBB";
+ value = "BBBB";
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "BBB BBB", fromJson.getKey());
+
+ String updatedKey = "AAA AAA";
+ String updatedValue = "JJJJ";
+
+ fromJson.setKey(updatedKey);
+ fromJson.setValue(updatedValue);
+
+ RestResponse updatedProperty = updateAdditionalInformation(resourceId, fromJson, user, fromJson.getUniqueId());
+ assertNotNull("check response object is not null after update additional information", updatedProperty);
+ assertNotNull("check error code exists in response after additional information", updatedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 409, updatedProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceEmptyAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ key = "BBBB";
+ value = "";
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceEmptyAdditionalInformationTest() throws Exception {
+
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceAllSpacesAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = " ";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceAllSpacesAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = " ";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceInvalidKeyAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "abc?";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceInvalidKeyAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "abc?";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceAdditionalInformationNullKeyTest() throws Exception {
+
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ String key = null;
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceAdditionalInformationNullKeyTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = null;
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceMaximumInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ for (int i = 0; i < 50; i++) {
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + i, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ }
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceLifeCycleAndMaximumInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+ resource.setUniqueId(resourceId);
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ for (int i = 0; i < 49; i++) {
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + i, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ }
+
+ String resourceVersion = "0.1";
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+
+ assertNotNull("check response object is not null after create property", checkInResponse);
+ assertNotNull("check error code exists in response after create property", checkInResponse.getErrorCode());
+ assertEquals("Check response code after create property", 200, checkInResponse.getErrorCode().intValue());
+
+ resourceVersion = "0.2";
+
+ RestResponse checkOutResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT, null);
+
+ resourceId = ResponseParser.getUniqueIdFromResponse(checkOutResponse);
+ resource.setUniqueId(resourceId);
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 50, value);
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 51, value);
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ RestResponse checkUndoOutResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.UNDOCHECKOUT, null);
+
+ resourceVersion = "0.1";
+
+ checkOutResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT, null);
+ resourceId = ResponseParser.getUniqueIdFromResponse(checkOutResponse);
+ resource.setUniqueId(resourceId);
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 50, value);
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 51, value);
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceLifeCycleCertifyAndMaximumInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+ resource.setUniqueId(resourceId);
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ for (int i = 0; i < 49; i++) {
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + i, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ }
+
+ String resourceVersion = "0.1";
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+
+ // resourceUtils.addResourceMandatoryArtifacts(user,
+ // createResourceResponse);
+
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+
+ assertNotNull("check response object is not null after create property", checkInResponse);
+ assertNotNull("check error code exists in response after create property", checkInResponse.getErrorCode());
+ assertEquals("Check response code after create property", 200, checkInResponse.getErrorCode().intValue());
+
+ RestResponse changeStateResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST, null);
+ changeStateResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.STARTCERTIFICATION, null);
+ changeStateResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CERTIFY, null);
+
+ assertNotNull("check response object is not null after create property", checkInResponse);
+ assertNotNull("check error code exists in response after create property", checkInResponse.getErrorCode());
+ assertEquals("Check response code after create property", 200, checkInResponse.getErrorCode().intValue());
+
+ resourceId = ResponseParser.getUniqueIdFromResponse(changeStateResponse);
+ resource.setUniqueId(resourceId);
+
+ resourceVersion = "1.0";
+
+ changeStateResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT, null);
+ resourceId = ResponseParser.getUniqueIdFromResponse(changeStateResponse);
+ resource.setUniqueId(resourceId);
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 50, value);
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 51, value);
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceCycleAndMaximumInformationTest() throws Exception {
+
+ User user = getUser();
+
+ ServiceReqDetails service = getServiceDetails();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ assertNotNull("check response object is not null after create resource", createServiceResponse);
+ assertNotNull("check error code exists in response after create resource", createServiceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createServiceResponse.getErrorCode().intValue());
+
+ service.setUniqueId(serviceId);
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ for (int i = 0; i < 49; i++) {
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + i, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ }
+
+ String resourceVersion = "0.1";
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+ RestResponse checkInResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.CHECKIN, checkinComentJson);
+
+ assertNotNull("check response object is not null after create property", checkInResponse);
+ assertNotNull("check error code exists in response after create property", checkInResponse.getErrorCode());
+ assertEquals("Check response code after create property", 200, checkInResponse.getErrorCode().intValue());
+
+ resourceVersion = "0.2";
+
+ RestResponse checkOutResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT, null);
+
+ serviceId = ResponseParser.convertServiceResponseToJavaObject(checkOutResponse.getResponse()).getUniqueId();
+ service.setUniqueId(serviceId);
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 50, value);
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 51, value);
+ createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ RestResponse checkUndoOutResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.UNDOCHECKOUT, null);
+
+ resourceVersion = "0.1";
+
+ checkOutResponse = LifecycleRestUtils.changeServiceState(service, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT, null);
+ serviceId = ResponseParser.convertServiceResponseToJavaObject(checkOutResponse.getResponse()).getUniqueId();
+ service.setUniqueId(serviceId);
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 50, value);
+ createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + 51, value);
+ createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createServiceMaximumInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ String lastCreatedProperty = null;
+
+ for (int i = 0; i < 50; i++) {
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key + i, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ lastCreatedProperty = fromJson.getUniqueId();
+
+ }
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 409, createProperty.getErrorCode().intValue());
+
+ RestResponse deletedProperty = deleteServiceAdditionalInformation(serviceId, lastCreatedProperty, user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+ createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void updateServiceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ String updatedKey = "ZZZ ZZZ";
+ String updatedValue = "JJJJ";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ fromJson.setKey(updatedKey);
+ fromJson.setValue(updatedValue);
+
+ RestResponse updatedProperty = updateServiceAdditionalInformation(serviceId, fromJson, user, fromJson.getUniqueId());
+ assertNotNull("check response object is not null after update additional information", updatedProperty);
+ assertNotNull("check error code exists in response after additional information", updatedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, updatedProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo updatedJson = gson.fromJson(updatedProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", updatedJson.getKey().contains(" "));
+ assertEquals("check returned key", "ZZZ ZZZ", updatedJson.getKey());
+ assertEquals("check returned value", updatedValue, updatedJson.getValue());
+ assertEquals("check returned id", fromJson.getUniqueId(), updatedJson.getUniqueId());
+
+ fromJson.setKey(updatedKey);
+ fromJson.setValue("");
+
+ updatedProperty = updateServiceAdditionalInformation(serviceId, fromJson, user, fromJson.getUniqueId());
+ assertNotNull("check response object is not null after update additional information", updatedProperty);
+ assertNotNull("check error code exists in response after additional information", updatedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 400, updatedProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void deleteServiceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ RestResponse deletedProperty = deleteServiceAdditionalInformation(serviceId, fromJson.getUniqueId(), user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo updatedJson = gson.fromJson(deletedProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", updatedJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", updatedJson.getKey());
+ assertEquals("check returned value", value, updatedJson.getValue());
+ assertEquals("check returned id", fromJson.getUniqueId(), updatedJson.getUniqueId());
+
+ deletedProperty = deleteServiceAdditionalInformation(serviceId, fromJson.getUniqueId(), user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 409, deletedProperty.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void getResourceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ RestResponse deletedProperty = getAdditionalInformation(resourceId, fromJson.getUniqueId(), user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo updatedJson = gson.fromJson(deletedProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", updatedJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", updatedJson.getKey());
+ assertEquals("check returned value", value, updatedJson.getValue());
+ assertEquals("check returned id", fromJson.getUniqueId(), updatedJson.getUniqueId());
+
+ }
+
+ @Test
+ public void getServiceAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ RestResponse deletedProperty = getServiceAdditionalInformation(serviceId, fromJson.getUniqueId(), user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo updatedJson = gson.fromJson(deletedProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", updatedJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", updatedJson.getKey());
+ assertEquals("check returned value", value, updatedJson.getValue());
+ assertEquals("check returned id", fromJson.getUniqueId(), updatedJson.getUniqueId());
+
+ }
+
+ @Test
+ public void getResourceAllAdditionalInformationTest() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ RestResponse deletedProperty = getResourceAllAdditionalInformation(resourceId, user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ AdditionalInformationDefinition updatedJson = gson.fromJson(deletedProperty.getResponse(), AdditionalInformationDefinition.class);
+ assertEquals("check number of parameters", 1, updatedJson.getParameters().size());
+ AdditionalInfoParameterInfo info = updatedJson.getParameters().iterator().next();
+
+ assertFalse("check number of spaces", info.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", info.getKey());
+ assertEquals("check returned value", value, info.getValue());
+ assertEquals("check returned id", fromJson.getUniqueId(), info.getUniqueId());
+
+ }
+
+ @Test
+ public void getServiceAllAdditionalInformationTest() throws Exception {
+ User user = getUser();
+
+ RestResponse createServiceResponse = createService();
+
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ RestResponse deletedProperty = getServiceAllAdditionalInformation(serviceId, user);
+ assertNotNull("check response object is not null after update additional information", deletedProperty);
+ assertNotNull("check error code exists in response after additional information", deletedProperty.getErrorCode());
+ assertEquals("Check response code after additional information", 200, deletedProperty.getErrorCode().intValue());
+
+ AdditionalInformationDefinition updatedJson = gson.fromJson(deletedProperty.getResponse(), AdditionalInformationDefinition.class);
+ assertEquals("check number of parameters", 1, updatedJson.getParameters().size());
+ AdditionalInfoParameterInfo info = updatedJson.getParameters().iterator().next();
+
+ assertFalse("check number of spaces", info.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", info.getKey());
+ assertEquals("check returned value", value, info.getValue());
+ assertEquals("check returned id", fromJson.getUniqueId(), info.getUniqueId());
+
+ }
+
+ @Test
+ public void createServiceAdditionalInformationTestDuringLifecycle() throws Exception {
+
+ User user = getUser();
+ RestResponse createServiceResponse = createService();
+ String serviceId = ResponseParser.convertServiceResponseToJavaObject(createServiceResponse.getResponse()).getUniqueId();
+ String key = "AAA AAA";
+ String value = "BBBB";
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createServiceAdditionalInformation(serviceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ AdditionalInfoParameterInfo fromJson = gson.fromJson(createProperty.getResponse(), AdditionalInfoParameterInfo.class);
+ assertFalse("check number of spaces", fromJson.getKey().contains(" "));
+ assertEquals("check returned key", "AAA AAA", fromJson.getKey());
+
+ ServiceReqDetails serviceDetails = getServiceDetails();
+
+ serviceDetails.setUniqueId(serviceId);
+
+ // serviceUtils.addServiceMandatoryArtifacts(user,
+ // createServiceResponse);
+
+ certifyService(user, serviceDetails, null);
+
+ }
+
+ @Test
+ public void createCascadeResource() {
+
+ // TODO: to check after rebase
+
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+ String newResourceNameSuffix = "aa";
+
+ RestResponse createResourceResponse = null;
+ try {
+
+ createResourceResponse = createResource(resource, user);
+ assertEquals("check invalid type", 201, createResourceResponse.getErrorCode().intValue());
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ String resourceVersion = "0.1";
+ // resourceUtils.addResourceMandatoryArtifacts(user,
+ // createResourceResponse);
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ resource.setUniqueId(resourceId);
+ Resource certifiedResource = certifyResource(user, resource, resourceVersion, 1);
+
+ ResourceReqDetails newResourceDetails = getResource();
+ String newResourceName = newResourceDetails.getName() + newResourceNameSuffix;
+ newResourceDetails.setName(newResourceName);
+ List<String> derivedFrom = new ArrayList<>();
+ derivedFrom.add(certifiedResource.getName());
+ newResourceDetails.setDerivedFrom(derivedFrom);
+ newResourceDetails.getTags().add(newResourceName);
+
+ user.setUserId(UserRoleEnum.ADMIN.getUserId());
+ RestResponse newCreateResourceResponse = createResource(newResourceDetails, user);
+ assertEquals("Check response code after creating resource", 201, newCreateResourceResponse.getErrorCode().intValue());
+ Resource newResource = gson.fromJson(newCreateResourceResponse.getResponse(), Resource.class);
+
+ RestResponse allAdditionalInformation = getResourceAllAdditionalInformation(newResource.getUniqueId(), user);
+
+ assertNotNull("check response object is not null after update additional information", allAdditionalInformation);
+ assertNotNull("check error code exists in response after additional information", allAdditionalInformation.getErrorCode());
+ assertEquals("Check response code after additional information", 200, allAdditionalInformation.getErrorCode().intValue());
+
+ AdditionalInformationDefinition updatedJson = gson.fromJson(allAdditionalInformation.getResponse(), AdditionalInformationDefinition.class);
+ assertEquals("check number of parameters", 0, updatedJson.getParameters().size());
+ // AdditionalInfoParameterInfo info =
+ // updatedJson.getParameters().iterator().next();
+
+ String newResourceId = newResource.getUniqueId();
+ createProperty = createResourceAdditionalInformation(newResourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ allAdditionalInformation = getResourceAllAdditionalInformation(newResourceId, user);
+
+ assertNotNull("check response object is not null after update additional information", allAdditionalInformation);
+ assertNotNull("check error code exists in response after additional information", allAdditionalInformation.getErrorCode());
+ assertEquals("Check response code after additional information", 200, allAdditionalInformation.getErrorCode().intValue());
+
+ updatedJson = gson.fromJson(allAdditionalInformation.getResponse(), AdditionalInformationDefinition.class);
+ assertEquals("check number of parameters", 1, updatedJson.getParameters().size());
+
+ } catch (IOException e) {
+ assertTrue(false);
+ }
+
+ }
+
+ @Test
+ public void createSamePropertyAfterCiCOResource() {
+
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ RestResponse createResourceResponse = null;
+ try {
+
+ createResourceResponse = createResource(resource, user);
+ assertEquals("check invalid type", 201, createResourceResponse.getErrorCode().intValue());
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ String resourceVersion = "0.1";
+ // resourceUtils.addResourceMandatoryArtifacts(user,
+ // createResourceResponse);
+
+ String key = "AAA AAA";
+ String value = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ resource.setUniqueId(resourceId);
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKIN);
+
+ assertNotNull("check response object is not null after create user", checkInResponse);
+ assertNotNull("check error code exists in response after create user", checkInResponse.getErrorCode());
+ assertEquals("Check response code after create user", 200, checkInResponse.getErrorCode().intValue());
+
+ Resource resourceAfterOperation = gson.fromJson(checkInResponse.getResponse(), Resource.class);
+ assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().size());
+ assertEquals("check size of additional information", 1, resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ RestResponse checkOutResponse = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT);
+
+ assertNotNull("check response object is not null after create user", checkOutResponse);
+ assertNotNull("check error code exists in response after create user", checkOutResponse.getErrorCode());
+ assertEquals("Check response code after create user", 200, checkOutResponse.getErrorCode().intValue());
+
+ Resource resourceAfterCoOperation = gson.fromJson(checkOutResponse.getResponse(), Resource.class);
+ assertEquals("check size of additional information", 1, resourceAfterCoOperation.getAdditionalInformation().size());
+ assertEquals("check size of additional information", 1, resourceAfterCoOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ String newResourceId = ResponseParser.getUniqueIdFromResponse(checkOutResponse);
+
+ String key2 = "ZZZ";
+ String value2 = "BBBB";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo2 = new AdditionalInfoParameterInfo(null, key2, value2);
+
+ RestResponse createProperty2 = createResourceAdditionalInformation(newResourceId, additionalInfoParameterInfo2, user);
+ assertNotNull("check response object is not null after create property", createProperty2);
+ assertNotNull("check error code exists in response after create property", createProperty2.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty2.getErrorCode().intValue());
+
+ RestResponse afterCreateAI = ResourceRestUtils.getResource(user, newResourceId);
+ Resource resourceNew = gson.fromJson(afterCreateAI.getResponse(), Resource.class);
+ assertEquals("check size of additional information", 1, resourceNew.getAdditionalInformation().size());
+ assertEquals("check size of additional information", 2, resourceNew.getAdditionalInformation().get(0).getParameters().size());
+
+ resource.setUniqueId(newResourceId);
+ Resource certifiedResource = certifyResource(user, resource, resourceVersion, 2);
+ assertEquals("check size of additional information", 1, certifiedResource.getAdditionalInformation().size());
+ assertEquals("check size of additional information", 2, certifiedResource.getAdditionalInformation().get(0).getParameters().size());
+
+ user.setUserId(UserRoleEnum.DESIGNER.getUserId());
+ resource.setUniqueId(certifiedResource.getUniqueId());
+ RestResponse checkOutResponseAfterCertify = LifecycleRestUtils.changeResourceState(resource, user, resourceVersion, LifeCycleStatesEnum.CHECKOUT);
+
+ assertNotNull("check response object is not null after create user", checkOutResponseAfterCertify);
+ assertNotNull("check error code exists in response after create user", checkOutResponseAfterCertify.getErrorCode());
+ assertEquals("Check response code after create user", 200, checkOutResponseAfterCertify.getErrorCode().intValue());
+
+ Resource resourceAfterCertifyCoOperation = gson.fromJson(checkOutResponseAfterCertify.getResponse(), Resource.class);
+ assertEquals("check size of additional information", 1, resourceAfterCertifyCoOperation.getAdditionalInformation().size());
+ assertEquals("check size of additional information", 2, resourceAfterCertifyCoOperation.getAdditionalInformation().get(0).getParameters().size());
+
+ } catch (IOException e) {
+ assertTrue(false);
+ }
+
+ }
+
+ // public Resource certifyService(User user, ServiceReqDetails service,
+ // String resourceVersion) throws Exception {
+ //
+ // RestResponse checkInResponse =
+ // LifecycleRestUtils.changeServiceState(service, user, resourceVersion,
+ // LifeCycleStates.CHECKIN);
+ //
+ // assertNotNull("check response object is not null after create user",
+ // checkInResponse);
+ // assertNotNull("check error code exists in response after create user",
+ // checkInResponse.getErrorCode());
+ // assertEquals("Check response code after create user", 200,
+ // checkInResponse.getErrorCode().intValue());
+ //
+ // Resource resourceAfterOperation =
+ // gson.fromJson(checkInResponse.getResponse(), Resource.class);
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().size());
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+ //
+ //// TODO Andrey
+ // createAndAddCertResourceToService(service, user);
+ //
+ // RestResponse req4certResponse =
+ // LifecycleRestUtils.changeServiceState(service, user, resourceVersion,
+ // LifeCycleStates.CERTIFICATIONREQUEST);
+ //
+ // assertNotNull("check response object is not null after create user",
+ // req4certResponse);
+ // assertEquals("Check response code after checkout resource", 200,
+ // req4certResponse.getErrorCode().intValue());
+ //
+ // resourceAfterOperation = gson.fromJson(req4certResponse.getResponse(),
+ // Resource.class);
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().size());
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+ //
+ // //change modifier
+ // user.setUserId(UserRoleEnum.TESTER.getUserId());
+ //
+ // //start certification
+ // RestResponse startCertResourceResponse3 =
+ // LifecycleRestUtils.changeServiceState(service, user, resourceVersion,
+ // LifeCycleStates.STARTCERTIFICATION);
+ // assertNotNull("check response object is not null after create user",
+ // startCertResourceResponse3);
+ // assertEquals("Check response code after checkout resource", 200,
+ // startCertResourceResponse3.getErrorCode().intValue());
+ //
+ // resourceAfterOperation =
+ // gson.fromJson(startCertResourceResponse3.getResponse(), Resource.class);
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().size());
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+ //
+ // //certify
+ //
+ // RestResponse certifyResponse =
+ // LifecycleRestUtils.changeServiceState(service, user, resourceVersion,
+ // LifeCycleStates.CERTIFY);
+ // assertNotNull("check response object is not null after create user",
+ // certifyResponse);
+ // assertEquals("Check response code after checkout resource", 200,
+ // certifyResponse.getErrorCode().intValue());
+ //
+ // resourceAfterOperation = gson.fromJson(certifyResponse.getResponse(),
+ // Resource.class);
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().size());
+ // assertEquals("check size of additional information", 1,
+ // resourceAfterOperation.getAdditionalInformation().get(0).getParameters().size());
+ //
+ // Resource certifyResource = gson.fromJson(certifyResponse.getResponse(),
+ // Resource.class);
+ // return certifyResource;
+ // }
+
+ private void createAndAddCertResourceToService(ServiceReqDetails serviceDetails, User user) throws Exception {
+
+ User sdncTesterUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+
+ RestResponse response = ResourceRestUtils.createResource(resourceDetails, user);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetails.getUniqueId());
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, user, resourceDetails.getUniqueId());
+ assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(), response.getErrorCode() == 200);
+
+ // certified resource
+ // response = LCSbaseTest.certifyResource(resourceDetails);
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user, LifeCycleStatesEnum.CHECKIN);
+ assertTrue("certify resource request returned status:" + restResponseResource.getErrorCode(), response.getErrorCode() == 200);
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, user, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertTrue("certify resource request returned status:" + restResponseResource.getErrorCode(), response.getErrorCode() == 200);
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterUser, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertTrue("certify resource request returned status:" + restResponseResource.getErrorCode(), response.getErrorCode() == 200);
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterUser, LifeCycleStatesEnum.CERTIFY);
+ assertTrue("certify resource request returned status:" + restResponseResource.getErrorCode(), response.getErrorCode() == 200);
+
+ // add resource instance with HEAT deployment artifact to the service
+ restResponseResource = LifecycleRestUtils.changeServiceState(serviceDetails, user, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertTrue("certify resource request returned status:" + restResponseResource.getErrorCode(), response.getErrorCode() == 200);
+ resourceInstanceReqDetails.setComponentUid(resourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, user, serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertTrue("response code is not 201, returned: " + response.getErrorCode(), response.getErrorCode() == 201);
+ }
+
+ @Test
+ public void createResourceAdditionalInformationTestAddValue() throws Exception {
+ User user = getUser();
+ ResourceReqDetails resource = getResource();
+
+ // deleteResource(resourceId, user);
+ RestResponse createResourceResponse = createResource(resource, user);
+
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResourceResponse);
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse);
+ assertNotNull("check error code exists in response after create resource", createResourceResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResourceResponse.getErrorCode().intValue());
+
+ String key = "AAA AAA";
+ String value = "";
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ RestResponse createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ value = "";
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 400, createProperty.getErrorCode().intValue());
+
+ value = "----<b></b><>;";
+
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo(null, key, value);
+
+ createProperty = createResourceAdditionalInformation(resourceId, additionalInfoParameterInfo, user);
+ assertNotNull("check response object is not null after create property", createProperty);
+ assertNotNull("check error code exists in response after create property", createProperty.getErrorCode());
+ assertEquals("Check response code after create property", 201, createProperty.getErrorCode().intValue());
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentInstancePropertyTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentInstancePropertyTest.java
new file mode 100644
index 0000000000..6ba1b2ccca
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentInstancePropertyTest.java
@@ -0,0 +1,1263 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.property;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.PropertyTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.PropertyRestUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import fj.data.Either;
+
+// open bug for this class: DE199108 - closed, DE199741
+public class ComponentInstancePropertyTest extends ComponentBaseTest {
+
+ protected Resource basicVFC;
+ protected Resource vfc1FromBasicVFC;
+ protected Resource vfc2FromVfc1;
+ protected Resource vfResource;
+
+ private List<ComponentInstanceProperty> expectedPropertyList;
+ private List<ComponentInstanceProperty> actualPropertyList;
+ // protected String updatedStringValue = "{Not Default String Value}";
+ protected String updatedStringValue = "Not Default String Value";
+ protected String updatedIntegerValue = "666";
+ protected String updatedBooleanValue = "false";
+ protected String newStringPropName = "stringProp2";
+ protected String newIntegerPropName = "integerProp2";
+ protected String newBooleanPropName = "booleanProp2";
+ // bug DE199741 protected String newStringPropValue = "<second string
+ // value>";
+ protected String newStringPropValue = "second string value";
+ protected String newIntegerPropValue = "888";
+ protected String newBooleanPropValue = "false";
+
+ @BeforeMethod
+ public void init() {
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ComponentInstancePropertyTest() {
+ super(name, ComponentInstancePropertyTest.class.getName());
+ }
+
+ // --------------Regular
+ // resource-------------------------------------------------------------------------------
+
+ @Test
+ public void nestedResourceProperty3Levels() throws Exception {
+
+ // first res
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ // second resource
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ // third resource
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ // verify property
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, actualPropertyList);
+ assertTrue("check list size failed, expected 3", actualPropertyList.size() == 3);
+
+ }
+
+ // --------------VF
+ // resource-----------------------------------------------------------
+
+ @Test
+ public void nestedVfResourceProperty3Levels() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ // verify property
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+ }
+
+ @Test
+ public void nestedVfResourceProperty3LevelsAndCpWithProp() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // four resource
+ Resource cp = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP,
+ NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ PropertyReqDetails cpStringProperty = ElementFactory.getDefaultStringProperty();
+ cpStringProperty.setName("Different Name");
+ cpStringProperty.setPropertyDefaultValue("Different value from default");
+ AtomicOperationUtils.addCustomPropertyToResource(cpStringProperty, cp, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ cp = AtomicOperationUtils.getResourceObject(cp, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(cp, expectedPropertyList);
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, vfResource, UserRoleEnum.DESIGNER, true).left().value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, cp, expectedPropertyList,
+ vfResource);
+ // verify property
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+ }
+
+ @Test
+ public void nestedCertifiedVfResourceProperty3Levels() throws Exception {
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CERTIFY, vfc1FromBasicVFC);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ // verify property
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+ }
+
+ @Test
+ public void nestedVfResourceProperty3Levels2SameResInstances() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ // verify property
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+ }
+
+ // ------------------update resource
+ // property-----------------------------------
+
+ @Test
+ public void nestedVfResourceProperty3LevelsUpdateFirstLevelProperty() throws Exception {
+ // first res
+ basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addDefaultPropertyToResource(PropertyTypeEnum.STRING, basicVFC, UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+
+ // verify property
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedStringValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(vfResource, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ @Test
+ public void nestedVfResourceProperty3LevelsUpdateSecondLevelProperty() throws Exception {
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+
+ // second resource
+ vfc1FromBasicVFC = AtomicOperationUtils.createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC,
+ basicVFC, ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true).left().value();
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addCustomPropertyToResource(ElementFactory.getDefaultIntegerProperty(), vfc1FromBasicVFC,
+ UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(vfc1FromBasicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY,
+ true);
+
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+
+ // verify property
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedIntegerValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(vfResource, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue(
+ "expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS
+ + " ,but was " + updatePropertyValueOnResourceInstance.getErrorCode(),
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated properly",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ @Test
+ public void nestedVfResourceProperty3LevelsUpdateThirdLevelProperty() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+
+ // third resource
+ vfc2FromVfc1 = AtomicOperationUtils.createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC,
+ vfc1FromBasicVFC, ResourceCategoryEnum.GENERIC_DATABASE, UserRoleEnum.DESIGNER, true).left().value();
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addCustomPropertyToResource(ElementFactory.getDefaultBooleanProperty(), vfc2FromVfc1,
+ UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(vfc2FromVfc1, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN,
+ true);
+
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+
+ // verify property
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedBooleanValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(vfResource, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ // ---------------------Service------------------------------------------------------------------------
+
+ /**
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p3) (p2) (p1)
+ */
+ @Test
+ public void serviceWithNestedResourceProperty3Levels() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ }
+
+ /**
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1)
+ */
+ @Test
+ public void serviceWithNestedResourceProperty3LevelsAndVfProperty() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ PropertyReqDetails propDetails = ElementFactory.getDefaultBooleanProperty();
+ propDetails.setName(newBooleanPropName);
+ propDetails.setPropertyDefaultValue(newBooleanPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(propDetails, vfResource, UserRoleEnum.DESIGNER, true);
+ propDetails = ElementFactory.getDefaultStringProperty();
+ propDetails.setName(newStringPropName);
+ propDetails.setPropertyDefaultValue(newStringPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(propDetails, vfResource, UserRoleEnum.DESIGNER, true);
+ propDetails = ElementFactory.getDefaultIntegerProperty();
+ propDetails.setName(newIntegerPropName);
+ propDetails.setPropertyDefaultValue(newIntegerPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(propDetails, vfResource, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfResource, expectedPropertyList);
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ }
+
+ /**
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1) CP(VF inst) (p5)
+ */
+ @Test
+ public void serviceWithNestedResourceProperty3LevelsAndCp() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // four resource
+ Resource cp = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP,
+ NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ PropertyReqDetails cpStringProperty = ElementFactory.getDefaultStringProperty();
+ cpStringProperty.setName("Different Name");
+ cpStringProperty.setPropertyDefaultValue("Different value from default");
+ AtomicOperationUtils.addCustomPropertyToResource(cpStringProperty, cp, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ cp = AtomicOperationUtils.getResourceObject(cp, UserRoleEnum.DESIGNER);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(cp, expectedPropertyList);
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, vfResource, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, cp, expectedPropertyList,
+ vfResource);
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ }
+
+ /**
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1) CP(inst) (p5)
+ */
+ @Test
+ public void serviceWithNestedResourceProperty3LevelsAndCpResInst() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ // expectedPropertyList =
+ // PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1,
+ // expectedPropertyList);
+
+ // four resource
+ Resource cp = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP,
+ NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ PropertyReqDetails cpStringProperty = ElementFactory.getDefaultStringProperty();
+ cpStringProperty.setName("Different Name");
+ cpStringProperty.setPropertyDefaultValue("Different value from default");
+ AtomicOperationUtils.addCustomPropertyToResource(cpStringProperty, cp, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(cp, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ cp = AtomicOperationUtils.getResourceObject(cp, UserRoleEnum.DESIGNER);
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(cp, expectedPropertyList);
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, service, UserRoleEnum.DESIGNER, true).left().value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ }
+
+ /**
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) | (p3) (p2) (p1)
+ * | (VFC(inst)-->VFC-->VFC-->VFC) (p3) (p2) (p1)
+ *
+ * VF2(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) | (p3') (p2') (p1') |
+ * (VFC(inst)-->VFC-->VFC-->VFC) (p3) (p2) (p1)
+ */
+ @Test
+ public void serviceNestedVfResourceProperty3Levels2SameResInstances() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1, expectedPropertyList);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ // verify property
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnResource(componentInstDetails, vfc2FromVfc1, expectedPropertyList,
+ vfResource);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ }
+
+ // service test template
+ /**
+ * Service-->VF(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1)
+ */
+ @Test
+ public void serviceNestedVfResourceProperty3LevelsAndSelfVfProperty() throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+ PropertyReqDetails newProp = ElementFactory.getDefaultStringProperty();
+ newProp.setName(newStringPropName);
+ newProp.setPropertyDefaultValue(newStringPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(newProp, vfResource, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ }
+
+ /**
+ * update property(p4)
+ *
+ *
+ * VFC(p1) ^ | VFC(p2) ^ | Service-->VF(inst)-->VF-->(VFC(inst)-->VFC(p3)
+ * (p4)
+ */
+ @Test
+ public void serviceNestedVfResourceProperty3LevelsAndSelfVfProperty_UpdateVfproperty() throws Exception {
+ // Create VFC(check-in state) derived from another resource
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+
+ // add property to VF
+ PropertyReqDetails newProp = ElementFactory.getDefaultStringProperty();
+ newProp.setName(newStringPropName);
+ newProp.setPropertyDefaultValue(newStringPropValue);
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addCustomPropertyToResource(newProp, vfResource, UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update VF instance property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedStringValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(service, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ /**
+ * update property(p1)
+ * Service-->VF(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1)
+ */
+ @Test
+ public void serviceNestedVfResourceProperty3LevelsAndSelfVfPropertyUpdateVfInheritance1LevelProperty()
+ throws Exception {
+
+ basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addDefaultPropertyToResource(PropertyTypeEnum.STRING, basicVFC, UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // verify property
+ PropertyReqDetails newProp = ElementFactory.getDefaultStringProperty();
+ newProp.setName(newStringPropName);
+ newProp.setPropertyDefaultValue(newStringPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(newProp, vfResource, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update VF property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedStringValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(service, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ /**
+ * update property(p2)
+ * Service-->VF(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1)
+ */
+ @Test
+ public void serviceNestedVfResourceProperty3LevelsAndSelfVfPropertyUpdateVfInheritance2LevelProperty()
+ throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = AtomicOperationUtils.createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC,
+ basicVFC, ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true).left().value();
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addCustomPropertyToResource(ElementFactory.getDefaultIntegerProperty(), vfc1FromBasicVFC,
+ UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(vfc1FromBasicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY,
+ true);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+
+ // verify property
+ PropertyReqDetails newProp = ElementFactory.getDefaultStringProperty();
+ newProp.setName(newStringPropName);
+ newProp.setPropertyDefaultValue(newStringPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(newProp, vfResource, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update VF property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedIntegerValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(service, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ /**
+ * update property(p3)
+ * Service-->VF(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1)
+ */
+ @Test
+ public void serviceNestedVfResourceProperty3LevelsAndSelfVfPropertyUpdateVfInheritance3LevelProperty()
+ throws Exception {
+
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC,
+ vfc1FromBasicVFC, ResourceCategoryEnum.GENERIC_DATABASE, UserRoleEnum.DESIGNER, true).left().value();
+ Either<ComponentInstanceProperty, RestResponse> propDetailsToUpdate = AtomicOperationUtils
+ .addCustomPropertyToResource(ElementFactory.getDefaultBooleanProperty(), vfc2FromVfc1,
+ UserRoleEnum.DESIGNER, true);
+ String propNameToUpdate = propDetailsToUpdate.left().value().getName();
+ String propTypeToUpdate = propDetailsToUpdate.left().value().getType();
+ AtomicOperationUtils.changeComponentState(vfc2FromVfc1, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN,
+ true);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // verify property
+ PropertyReqDetails newProp = ElementFactory.getDefaultStringProperty();
+ newProp.setName(newStringPropName);
+ newProp.setPropertyDefaultValue(newStringPropValue);
+ AtomicOperationUtils.addCustomPropertyToResource(newProp, vfResource, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ // update VF property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils
+ .getPropFromListByPropNameAndType(actualPropertyList, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedBooleanValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(service, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, null);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+
+ }
+
+ /**
+ * update property p5'
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1) CP(inst on VF) (p5) CP(inst) (p5')
+ */
+ @Test
+ public void serviceWithNestedResourceProperty3LevelsAndCpOnVfUpdateCpInstanceOfService() throws Exception {
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CHECKIN, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ // expectedPropertyList =
+ // PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1,
+ // expectedPropertyList);
+
+ // four resource
+ Resource cp = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP,
+ NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ PropertyReqDetails cpStringProperty = ElementFactory.getDefaultStringProperty();
+ cpStringProperty.setName("Different Name");
+ cpStringProperty.setPropertyDefaultValue("Different value from default");
+ AtomicOperationUtils.addCustomPropertyToResource(cpStringProperty, cp, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(cp, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ cp = AtomicOperationUtils.getResourceObject(cp, UserRoleEnum.DESIGNER);
+ // create VF + add RI
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, vfResource, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(cp, expectedPropertyList);
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, service, UserRoleEnum.DESIGNER, true).left().value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ // service = AtomicOperationUtils.getServiceObject(service,
+ // UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ String propNameToUpdate = "cidr";
+ String propTypeToUpdate = "string";
+
+ // update CP property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils.getCompPropInstListByInstIdAndPropName(
+ service, componentInstDetails, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedStringValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ List<String> path = expectedUpdatePropDetails.getPath();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(service, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, path);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+ }
+
+ /**
+ * update property p5
+ * Service-->VF1(inst)-->VF-->(VFC(inst)-->VFC-->VFC-->VFC) (p4) (p3) (p2)
+ * (p1) CP(inst on VF) (p5) CP(inst) (p5')
+ */
+ @Test
+ public void serviceWithNestedResourceProperty3LevelsAndCpOnVfUpdateCpInstanceOfVf() throws Exception {
+ basicVFC = createResourceWithProperty(ElementFactory.getDefaultStringProperty(), LifeCycleStatesEnum.CERTIFY);
+ vfc1FromBasicVFC = createResourceWithPropertyDerivedFromOtherResource(
+ ElementFactory.getDefaultIntegerProperty(), LifeCycleStatesEnum.CERTIFY, basicVFC);
+ vfc2FromVfc1 = createResourceWithPropertyDerivedFromOtherResource(ElementFactory.getDefaultBooleanProperty(),
+ LifeCycleStatesEnum.CERTIFY, vfc1FromBasicVFC);
+ vfc2FromVfc1 = AtomicOperationUtils.getResourceObject(vfc2FromVfc1, UserRoleEnum.DESIGNER);
+ // expectedPropertyList =
+ // PropertyRestUtils.addResourcePropertiesToList(vfc2FromVfc1,
+ // expectedPropertyList);
+
+ // four resource
+ Resource cp = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP,
+ NormativeTypesEnum.NETWORK, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ PropertyReqDetails cpStringProperty = ElementFactory.getDefaultStringProperty();
+ cpStringProperty.setName("Different Name");
+ cpStringProperty.setPropertyDefaultValue("Different value from default");
+ AtomicOperationUtils.addCustomPropertyToResource(cpStringProperty, cp, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(cp, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ // create VF + add RI
+ cp = AtomicOperationUtils.getResourceObject(cp, UserRoleEnum.DESIGNER);
+ vfResource = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ ComponentInstance componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfc2FromVfc1, vfResource, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, vfResource, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.changeComponentState(vfResource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ vfResource = AtomicOperationUtils.getResourceObject(vfResource, UserRoleEnum.DESIGNER);
+
+ // Service
+ expectedPropertyList = new ArrayList<ComponentInstanceProperty>();
+ actualPropertyList = new ArrayList<ComponentInstanceProperty>();
+ expectedPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(vfResource, expectedPropertyList,
+ null);
+ expectedPropertyList = PropertyRestUtils.addResourcePropertiesToList(cp, expectedPropertyList);
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vfResource, service, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ componentInstDetails = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp, service, UserRoleEnum.DESIGNER, true).left().value();
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ PropertyRestUtils.updatePropertyListWithPathOnComponentInstance(componentInstDetails, service,
+ expectedPropertyList);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+ PropertyRestUtils.comparePropertyLists(expectedPropertyList, actualPropertyList, false);
+
+ String propNameToUpdate = "cidr";
+ String propTypeToUpdate = "string";
+
+ // update CP property
+ ComponentInstanceProperty expectedUpdatePropDetails = PropertyRestUtils.getCompPropInstListByInstIdAndPropName(
+ service, componentInstDetails, propNameToUpdate, propTypeToUpdate);
+ expectedUpdatePropDetails.setValue(updatedStringValue);
+ String propUniqeId = expectedUpdatePropDetails.getUniqueId();
+ List<String> path = expectedUpdatePropDetails.getPath();
+ RestResponse updatePropertyValueOnResourceInstance = ComponentInstanceRestUtils
+ .updatePropertyValueOnResourceInstance(service, componentInstDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), expectedUpdatePropDetails);
+ assertTrue("expected updatePropertyValueOnResourceInstance response code: " + BaseRestUtils.STATUS_CODE_SUCCESS,
+ updatePropertyValueOnResourceInstance.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ service = AtomicOperationUtils.getServiceObject(service, UserRoleEnum.DESIGNER);
+
+ actualPropertyList = new ArrayList<>();
+ actualPropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, actualPropertyList, null);
+
+ ComponentInstanceProperty actualUpdatedPropDetails = PropertyRestUtils
+ .getPropFromListByPropIdAndPath(actualPropertyList, propUniqeId, path);
+ assertTrue("property was not updated propely",
+ PropertyRestUtils.comparePropertyObjects(expectedUpdatePropDetails, actualUpdatedPropDetails, true));
+ }
+
+ // -------------------Methods--------------------------
+ public static PropertyDataDefinition convertToPropertyDataDefinitionObject(PropertyReqDetails prop) {
+ PropertyDataDefinition propDataDef = new PropertyDataDefinition();
+ propDataDef.setDefaultValue(prop.getPropertyDefaultValue());
+ propDataDef.setType(prop.getPropertyType());
+ propDataDef.setPassword(prop.getPropertyPassword());
+ propDataDef.setDescription(prop.getPropertyDescription());
+ return propDataDef;
+ }
+
+ protected Resource createResourceWithPropertyDerivedFromOtherResource(PropertyReqDetails propertyReqDetails,
+ LifeCycleStatesEnum state, Resource derivedFromResource) throws Exception {
+ Resource resource = AtomicOperationUtils.createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC,
+ derivedFromResource, ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ if (propertyReqDetails != null) {
+ AtomicOperationUtils.addCustomPropertyToResource(propertyReqDetails, resource, UserRoleEnum.DESIGNER, true);
+ }
+ AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, state, true);
+ return AtomicOperationUtils.getResourceObject(resource, UserRoleEnum.DESIGNER);
+ // return resource;
+ }
+
+ protected Resource createResourceWithProperty(PropertyReqDetails propertyReqDetails, LifeCycleStatesEnum state)
+ throws Exception {
+ Resource resource = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ if (propertyReqDetails != null) {
+ AtomicOperationUtils.addCustomPropertyToResource(propertyReqDetails, resource, UserRoleEnum.DESIGNER, true);
+ }
+ AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, state, true);
+ return AtomicOperationUtils.getResourceObject(resource, UserRoleEnum.DESIGNER);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentProperty.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentProperty.java
new file mode 100644
index 0000000000..e1c4c18270
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/ComponentProperty.java
@@ -0,0 +1,1796 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.property;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.PropertyTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class ComponentProperty extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ComponentProperty() {
+ super(name, ComponentProperty.class.getName());
+ }
+
+ @DataProvider
+ private static final Object[][] propertiesListDefaultValueSuccessFlow() throws IOException, Exception {
+ return new Object[][] {
+ // integer
+ { "integer", "[1,2]", "[1,2]" },
+ { "tosca.datatypes.Credential",
+ "[{\"protocol\":\"protocol1\",\"token\":\"token1\"},{\"protocol\":\"protocol2\",\"token\":\"token2\"}]",
+ "[{\"protocol\":\"protocol1\",\"token\":\"token1\"},{\"protocol\":\"protocol2\",\"token\":\"token2\"}]" },
+ { "tosca.datatypes.Credential",
+ "[{\"protocol\":\"protocol1\",\"token\":\"token1\"},{\"protocol\":\"protocol<br>2\",\"token\":\"token2 2\"}]",
+ "[{\"protocol\":\"protocol1\",\"token\":\"token1\"},{\"protocol\":\"protocol2\",\"token\":\"token2 2\"}]" },
+ { "tosca.datatypes.Credential", null, null }, { "tosca.datatypes.Credential", "[]", "[]" },
+ { "integer", "[1,2,1,2]", "[1,2,1,2]" }, { "integer", "[1,,2]", "[1,2]" },
+ { "integer", "[1,null,2]", "[1,2]" }, { "integer", "[1,2,null]", "[1,2]" },
+ { "integer", "[null,1,2]", "[1,2]" }, { "integer", "[1,,2]", "[1,2]" },
+ { "integer", "[,1,2]", "[1,2]" },
+ // {"integer",
+ // "[1000000000000000000000000000000000000000000000000000,2]" ,
+ // "[1000000000000000000000000000000000000000000000000000,2]"},
+ { "integer", "[100000000,2]", "[100000000,2]" }, // Andrey, in
+ // success
+ // flow
+ // integer
+ // max value
+ // is
+ // 2147483647
+ { "integer", null, null }, // no default value
+ { "integer",
+ "[1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2]",
+ "[1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2]" },
+ // boolean
+ { "boolean", "[true,false]", "[true,false]" },
+ { "boolean", "[true,false,false]", "[true,false,false]" },
+ { "boolean", "[null,true,false]", "[true,false]" }, { "boolean", "[true,false,null]", "[true,false]" },
+ { "boolean", "[true,,false]", "[true,false]" }, { "boolean", "[true,false,]", "[true,false]" },
+ { "boolean", "[,true,false]", "[true,false]" }, { "boolean", null, null },
+ // DE199713 - Default value for property type Boolean should
+ // support also the following values: true, t , one , 1 ,
+ // false, f , off , 0
+ { "boolean", "[on,off]", "[true,false]" }, { "boolean", "[ON,OFF]", "[true,false]" },
+ { "boolean", "[On,Off]", "[true,false]" }, { "boolean", "[yes,no]", "[true,false]" },
+ { "boolean", "[YES,NO]", "[true,false]" }, { "boolean", "[Yes,No]", "[true,false]" },
+ { "boolean", "[y,n]", "[true,false]" }, { "boolean", "[Y,N]", "[true,false]" },
+ // float
+ { "float", "[10.0,0.0]", "[10.0,0.0]" }, { "float", "[10,0]", "[10,0]" }, // contain
+ // integer
+ { "float", "[-10,-5.30]", "[-10,-5.30]" }, // Negative numbers
+ { "float", "[10,null,0]", "[10,0]" }, { "float", "[null,10,0]", "[10,0]" },
+ { "float", "[10,0,null]", "[10,0]" },
+ { "float", "[10,0.1111111111111111111111111111111111111111]",
+ "[10,0.1111111111111111111111111111111111111111]" },
+ { "float", "[10, ,7.3 ]", "[10,7.3]" }, { "float", "[10 , 7.3 , ]", "[10,7.3]" },
+ { "float", "[, , 10 , 7.3 , ]", "[10,7.3]" }, { "float", "[4.7f, -5.5f ]", "[4.7,-5.5]" },
+ { "float", "[4.7f, 6.3 ,6.3, 4.7f]", "[4.7,6.3,6.3,4.7]" }, // duplicate
+ // value
+ { "float", null, null }, { "string", "[aaaa , AAAA ]", "[\"aaaa\",\"AAAA\"]" },
+
+ { "string",
+ "[1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2]",
+ "[\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\",\"1\",\"2\"]" },
+ { "string", "[aaaa , AAAA, 1, off , true, false ]",
+ "[\"aaaa\",\"AAAA\",\"1\",\"off\",\"true\",\"false\"]" },
+ { "string", "[aaaa , AAAA, aaaa, Aaaa , aaaa ]", "[\"aaaa\",\"AAAA\",\"aaaa\",\"Aaaa\",\"aaaa\"]" },
+ { "string", "[aaaa , AAAA, , ]", "[\"aaaa\",\"AAAA\"]" },
+ { "string", "[ , aaaa , AAAA ]", "[\"aaaa\",\"AAAA\"]" },
+ { "string", "[ aaaa , , AAAA ]", "[\"aaaa\",\"AAAA\"]" },
+ { "string", "[ aaaa , AAAA, null ]", "[\"aaaa\",\"AAAA\"]" },
+ { "string", "[ null, aaaa , AAAA ]", "[\"aaaa\",\"AAAA\"]" },
+ { "string", "[ aaaa , null , AAAA ]", "[\"aaaa\",\"AAAA\"]" }, { "string", null, null }, // without
+ // default
+ // values
+ // -
+ // Property
+ // will
+ // be
+ // without
+ // default
+ // parameter
+ { "string", "[ <b>AAA</b> ]", "[\"AAA\"]" }, // BUG DE199715 -
+ // Error 400
+ // response
+ // received
+ // while adding
+ // property with
+ // default value
+ // contain HTML
+ // tags.
+ // Need to check
+ // whether / is
+ // legal in yaml
+
+ };
+ }
+
+ @DataProvider
+ private static final Object[][] invalidListProperties() throws IOException, Exception {
+ return new Object[][] {
+
+ { "integer", "[1,aaa]" },
+ { "tosca.datatypes.Credential",
+ "[{\"protocol\":\"protocol1\",\"token\":\"token1\"},{\"protocol\":\"protocol2\",\"token1\":\"token2\"}]" },
+ { "integer", "[1,false]" }, { "integer", "[1,3.5]" }, { "integer", "[1,3#]" },
+ { "boolean", "[true,3.5]" }, { "boolean", "[true,1000]" }, { "boolean", "[false,trueee]" },
+ { "boolean", "[true,false!]" }, { "float", "[5.0000001,true]" }, { "float", "[0.0001,koko]" },
+ { "float", "[0.0001,6.3@]" }, { "float", "[0.0001f,6.3x]" }, };
+ }
+
+ @DataProvider
+ private static final Object[][] updatePropertiesListDefaultValueSuccessFlow() throws IOException, Exception {
+ return new Object[][] {
+ // integer
+ // Setting --- update properties
+ // -----------------------------------------------------------------------
+ { "integer", "[1,2]", "[1,2]", "integer", "[200,100]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[200,100,null]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[null, 200,100]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[200,null,100]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[200,100, ]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[ , 200,100 ]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[200 , ,100 ]", "[200,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", null, null },
+ { "integer", "[1,2]", "[1,2]", "integer", "[200 , 100 , 200, 100]", "[200,100,200,100]" },
+ //
+ // ////DE199829 update resource property schema_type is not
+ // updated
+ { "integer", "[1,2]", "[1,2]", "string", "[aaaa , bbbb ]", "[\"aaaa\",\"bbbb\"]" },
+ { "integer", "[1,2]", "[1,2]", "boolean", "[true , false ]", "[true,false]" },
+ { "integer", "[1,2]", "[1,2]", "float", "[3.5,4.8f ]", "[3.5,4.8]" },
+ // {"string", "[aaa,bbb]" , "[\"aaa\",\"bbb\"]","integer","[100,
+ // 200]" , "[\"100\",\"200\"]"},
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "integer", "[100, 200]", "[100,200]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "float", "[0.1f, 3.01]", "[0.1,3.01]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "boolean", "[true, false]", "[true,false]" },
+ { "float", "[1.2,2.3]", "[1.2,2.3]", "boolean", "[true, false]", "[true,false]" },
+ { "float", "[1.2,2.3]", "[1.2,2.3]", "integer", "[100, 200]", "[100,200]" },
+ { "float", "[1.2,2.3]", "[1.2,2.3]", "string", "[koko, moko]", "[\"koko\",\"moko\"]" },
+ { "boolean", "[true,false]", "[true,false]", "string", "[koko, moko]", "[\"koko\",\"moko\"]" },
+ // {"boolean", "[true,false]" ,
+ // "[\"true\",\"false\"]","integer","[100, 300000000000000]" ,
+ // "[\"100\",\"300000000000000\"]"},// Andrey, value not valid
+ // for integer success flow
+ { "boolean", "[true,false]", "[true,false]", "integer", "[100,2147483647]", "[100,2147483647]" }, // Andrey,
+ // in
+ // success
+ // flow
+ // integer
+ // max
+ // value
+ // is
+ // 2147483647
+ { "boolean", "[true,false]", "[true,false]", "float", "[3.000000000000002, 5.67f]",
+ "[3.000000000000002,5.67]" },
+ // ////DE199829
+ //
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[xxx, yyy]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[xxx , yyy ,null]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[null, xxx, yyy]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[xxx ,null,yyy]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[xxx ,yyy, ]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[ , xxx,yyy ]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[xxx , ,yyy ]", "[\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[ xxx , yyy , xxx , yyy]",
+ "[\"xxx\",\"yyy\",\"xxx\",\"yyy\"]" },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", null, null },
+ { "string", "[aaa,bbb]", "[\"aaa\",\"bbb\"]", "string", "[xxx_-x, y__y--y]",
+ "[\"xxx_-x\",\"y__y--y\"]" },
+ // DE199715
+ // {"string", "[aaa,bbb]" , "[\"aaa\",\"bbb\"]", "string" ,
+ // "[\"<b>xxx</b>\", \"<b>yyy</b>\"]" , "[\"xxx\",\"yyy\"]"},
+ //
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1 , -0.1]", "[2.1,-0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1, 0.1 ,null]", "[2.1,0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[null , 2.1, 0.1]", "[2.1,0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1,null,0.1]", "[2.1,0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1,0.1, ]", "[2.1,0.1]" },
+ // {"float", "[1.00,0.02]" , "[1.00,0.02]","float","[ ,
+ // 2.00000000000001,0.00000000000000100 ]" ,
+ // "[2.00000000000001,0.00000000000000100]"},
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1 , ,0.1 ]", "[2.1,0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", null, null },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1f , ,0.1f ]", "[2.1,0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[2.1 , 0.1 , 2.1, 0.1]", "[2.1,0.1,2.1,0.1]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[200 , 100.11]", "[200,100.11]" },
+ { "float", "[1.00,0.02]", "[1.00,0.02]", "float", "[-2.35 , 100.11]", "[-2.35,100.11]" },
+ //
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[false , false]", "[false,false]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[false, true ,null]", "[false,true]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[null , false, true]", "[false,true]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[false,null,true]", "[false,true]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[false ,true , ]", "[false,true]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[ , false, true ]", "[false,true]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", "[false , ,true ]", "[false,true]" },
+ { "boolean", "[true,false]", "[true,false]", "boolean", null, null }, { "boolean", "[true,false]",
+ "[true,false]", "boolean", "[false , true , false, true]", "[false,true,false,true]" }, };
+ }
+
+ @DataProvider
+ private static final Object[][] updatePropertiesListDefaultValueFailureFlow() throws IOException, Exception {
+ return new Object[][] {
+ // integer
+ // Setting --- update properties
+ // -----------------------------------------------------------------------
+ { "integer", "[1,2]", "[1,2]", "integer", "[aaa,bbb]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[true,false]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[1.0,100]" },
+ { "integer", "[1,2]", "[1,2]", "integer", "[@12,100]" },
+ { "float", "[0.11,0.22]", "[0.11,0.22]", "float", "[aaa, bbb]" },
+ { "float", "[0.11,0.22]", "[0.11,0.22]", "float", "[0.88, false]" },
+ { "float", "[0.11,0.22]", "[0.11,0.22]", "float", "[0.88g, 0.3]" },
+ { "float", "[0.11,0.22]", "[0.11,0.22]", "float", "[@0.88, 0.3]" },
+ { "boolean", "[true, false]", "[true,false]", "boolean", "[true, 100]" },
+ { "boolean", "[true, false]", "[true,false]", "boolean", "[false, 0.01]" },
+ { "boolean", "[true, false]", "[true,false]", "boolean", "[koko, true]" },
+ { "boolean", "[true, false]", "[true,false]", "boolean", "[@false, true]" },
+
+ };
+ }
+
+ // Map properties
+ @DataProvider
+ private static final Object[][] updatePropertiesMapDefaultValueSuccessFlow() throws IOException, Exception {
+ return new Object[][] {
+ // entrySchemaType , propertyDefaultValues ,
+ // expectedDefaultValue , newEntrySchemaType ,
+ // newPropertyDefaultValue , newExpectedDefaultValue
+ // integer
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "integer",
+ "{\"key1\":200,\"key2\":null , \"key3\":300}", "{\"key1\":200,\"key2\":null,\"key3\":300}" },
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "integer",
+ "{\"key1\":null,\"key2\":200 , \"key3\":100}", "{\"key1\":null,\"key2\":200,\"key3\":100}" },
+ // string
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "string",
+ "{\"key1\":\"aaaa\" , \"key2\":\"aaaa\"}", "{\"key1\":\"aaaa\",\"key2\":\"aaaa\"}" },
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "boolean",
+ "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}" },
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "float",
+ "{\"key1\":3.5 , \"key2\":4.8f}", "{\"key1\":3.5,\"key2\":4.8}" },
+ // string
+ { "string", "{\"key1\":aaa , \"key2\":bbb}", "{\"key1\":\"aaa\",\"key2\":\"bbb\"}", "string",
+ "{\"key1\":xxx , \"key2\":yyy}", "{\"key1\":\"xxx\",\"key2\":\"yyy\"}" },
+ // float
+ { "float", "{\"key1\":1.00 , \"key2\":0.02}", "{\"key1\":1.00,\"key2\":0.02}", "float",
+ "{\"key1\":2.1, \"key2\":-0.1}", "{\"key1\":2.1,\"key2\":-0.1}" },
+ { "float", "{\"key1\":1.00 , \"key2\":0.02}", "{\"key1\":1.00,\"key2\":0.02}", "float",
+ "{\"key1\":2.1 , \"key2\":0.1 , \"key3\":null}", "{\"key1\":2.1,\"key2\":0.1,\"key3\":null}" },
+ // boolean
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":false , \"key2\":false}", "{\"key1\":false,\"key2\":false}" },
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":false , \"key2\":true , \"key3\":null}",
+ "{\"key1\":false,\"key2\":true,\"key3\":null}" },
+ // null
+ { "boolean", "{\"key1\":null , \"key2\":false}", "{\"key1\":null,\"key2\":false}", "boolean",
+ "{\"key1\":false , \"key2\":true , \"key3\":null}",
+ "{\"key1\":false,\"key2\":true,\"key3\":null}" },
+ // tosca.datatypes.Credential
+ { "tosca.datatypes.Credential",
+ "{\"key1\":{\"protocol\":\"protocol<br>1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}",
+ "{\"key1\":{\"protocol\":\"protocol1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}",
+ "tosca.datatypes.Credential",
+ "{\"key1\":{\"protocol\":\"protocol<br>1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}",
+ "{\"key1\":{\"protocol\":\"protocol1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}" },
+
+ };
+ }
+
+ @DataProvider
+ private static final Object[][] propertiesMapDefaultValueSuccessFlow() throws IOException, Exception {
+ return new Object[][] {
+
+ // entrySchemaType , propertyDefaultValues ,
+ // expectedDefaultValue
+ //
+ // {"string",
+ // "{\"vf_module_id\":{\"get_input\":\"vf_module_id\"},
+ // \"vnf_idw\": 2}",
+ // "{\"vf_module_id\":{\"get_input\":\"vf_module_id\"},
+ // \"vnf_idw\": 2}"},
+
+ // tosca.datatypes.Credential
+ { "tosca.datatypes.Credential",
+ "{\"key1\":{\"protocol\":\"protocol<br>1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}",
+ "{\"key1\":{\"protocol\":\"protocol1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}" },
+ // integer
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}" },
+ { "integer", "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":2}",
+ "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":2}" },
+ { "integer", "{\"key1\":1,\"key2\":null,\"key3\":1,\"key4\":2}",
+ "{\"key1\":1,\"key2\":null,\"key3\":1,\"key4\":2}" },
+ { "integer", "{\"key1\":null,\"key2\":1,\"key3\":1,\"key4\":2}",
+ "{\"key1\":null,\"key2\":1,\"key3\":1,\"key4\":2}" },
+ { "integer", "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":null}",
+ "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":null}" },
+ { "integer", "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":NULL}",
+ "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":null}" },
+ { "integer", "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":Null}",
+ "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":null}" },
+ { "integer", "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":nuLL}",
+ "{\"key1\":1,\"key2\":2,\"key3\":1,\"key4\":null}" },
+ { "integer", null, null }, // no default value
+ // //BUG
+ //// {"integer",
+ // "{\"key1\":1000000000000000000000000000000000000000000000000000,\"key2\":2}"
+ // ,"{\"key1\":1000000000000000000000000000000000000000000000000000,\"key2\":2}"},
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":true , \"key2\":false, \"key3\":false }",
+ "{\"key1\":true,\"key2\":false,\"key3\":false}" },
+ { "boolean", "{\"key1\":null , \"key2\":true, \"key3\":false }",
+ "{\"key1\":null,\"key2\":true,\"key3\":false}" },
+ { "boolean", "{\"key1\":true , \"key2\":Null, \"key3\":false }",
+ "{\"key1\":true,\"key2\":null,\"key3\":false}" },
+ { "boolean", "{\"key1\":true , \"key2\":false, \"key3\":nULL }",
+ "{\"key1\":true,\"key2\":false,\"key3\":null}" },
+ { "boolean", null, null },
+ { "boolean", "{\"key1\":on , \"key2\":off}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":ON , \"key2\":OFF}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":On , \"key2\":Off}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":yes , \"key2\":no}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":YES , \"key2\":NO}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":Yes , \"key2\":No}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":y , \"key2\":n}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{\"key1\":Y , \"key2\":N}", "{\"key1\":true,\"key2\":false}" },
+ { "boolean", "{null:false}", "{\"null\":false}" },
+ // float
+ { "float", "{\"key1\":10.0 , \"key2\":0.0}", "{\"key1\":10.0,\"key2\":0.0}" },
+ { "float", "{\"key1\":10 , \"key2\":0}", "{\"key1\":10,\"key2\":0}" }, // contain
+ // integer
+ { "float", "{\"key1\":null , \"key2\":Null}", "{\"key1\":null,\"key2\":null}" }, // contain
+ // null
+ { "float", "{\"key1\":3.5 , \"key2\":nULL}", "{\"key1\":3.5,\"key2\":null}" },
+ // BUG
+ { "float", "{\"key1\":3.5 , \"key2\":0.1111111111111111111111111111111111111111}",
+ "{\"key1\":3.5,\"key2\":0.1111111111111111111111111111111111111111}" },
+ { "float", "{\"key1\":4.7f , \"key2\":-5.5f}", "{\"key1\":4.7,\"key2\":-5.5}" },
+ { "float", "{\"key1\":4.7f , \"key2\":-5.5f, \"key3\":-5.5f}",
+ "{\"key1\":4.7,\"key2\":-5.5,\"key3\":-5.5}" },
+ { "boolean", null, null },
+ { "string", "{\"key1\":aaaa , \"key2\":AAAA}", "{\"key1\":\"aaaa\",\"key2\":\"AAAA\"}" },
+ { "string", "{\"key1\":off , \"key2\":true , \"key3\":1}",
+ "{\"key1\":\"off\",\"key2\":\"true\",\"key3\":\"1\"}" },
+ { "string", "{\"key1\":aaaa , \"key2\":Aaaa , \"key3\":aaaa}",
+ "{\"key1\":\"aaaa\",\"key2\":\"Aaaa\",\"key3\":\"aaaa\"}" },
+ { "string", "{\"key1\":aaaa , \"key2\":bbbb , \"key3\":null}",
+ "{\"key1\":\"aaaa\",\"key2\":\"bbbb\",\"key3\":null}" },
+ { "string", "{\"key1\":NULL , \"key2\":bbbb , \"key3\":aaaa}",
+ "{\"key1\":null,\"key2\":\"bbbb\",\"key3\":\"aaaa\"}" },
+ { "string", "{\"key1\":aaaa , \"key2\":Null , \"key3\":bbbb}",
+ "{\"key1\":\"aaaa\",\"key2\":null,\"key3\":\"bbbb\"}" },
+ { "string", null, null }, // without default values - Property
+ // will be without default parameter
+ { "string", "{\"key1\":\"<b>AAAA</b>\" }", "{\"key1\":\"AAAA\"}" },
+
+ };
+ }
+
+ @DataProvider
+ private static final Object[][] updatePropertiesMapDefaultValueFailureFlow() throws IOException, Exception {
+ return new Object[][] {
+
+ // integer
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "integer",
+ "{\"key1\":aaa , \"key2\":bbb}" },
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "integer",
+ "{\"key1\":true , \"key2\":false}" },
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "integer",
+ "{\"key1\":1.0 , \"key2\":100}" },
+ { "integer", "{\"key1\":1 , \"key2\":2}", "{\"key1\":1,\"key2\":2}", "integer",
+ "{\"key1\":12@ , \"key2\":100}" },
+ // float
+ { "float", "{\"key1\":0.11 , \"key2\":0.22}", "{\"key1\":0.11,\"key2\":0.22}", "float",
+ "{\"key1\":aaa , \"key2\":bbb}" },
+ { "float", "{\"key1\":0.11 , \"key2\":0.22}", "{\"key1\":0.11,\"key2\":0.22}", "float",
+ "{\"key1\":0.88 , \"key2\":false}" },
+ { "float", "{\"key1\":0.11 , \"key2\":0.22}", "{\"key1\":0.11,\"key2\":0.22}", "float",
+ "{\"key1\":0.88g , \"key2\":0.3}" },
+ { "float", "{\"key1\":0.11 , \"key2\":0.22}", "{\"key1\":0.11,\"key2\":0.22}", "float",
+ "{\"key1\":@0.88g , \"key2\":0.3}" },
+ // boolean
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":true , \"key2\":100}" },
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":false , \"key2\":0.01}" },
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":koko , \"key2\":true}" },
+ { "boolean", "{\"key1\":true , \"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":@false , \"key2\":true}" },
+ { "boolean", "{\"key1\":true,\"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{:false , \"key2\":true}" },
+ { "boolean", "{\"key1\":true,\"key2\":false}", "{\"key1\":true,\"key2\":false}", "boolean",
+ "{\"key1\":true , , \"key2\":false}" },
+ // tosca.datatypes.Credential
+ { "tosca.datatypes.Credential",
+ "{\"key1\":{\"protocol\":\"protocol<br>1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}",
+ "{\"key1\":{\"protocol\":\"protocol1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token\":\"token2\"}}",
+ "tosca.datatypes.Credential",
+ "{\"key1\":{\"protocol\":\"protocol<br>1\",\"token\":\"token1\"},\"key2\":{\"protocol\":\"protocol2\",\"token2\":\"token2\"}}" },
+
+ };
+ }
+
+ // US594938 - UPDATE PROPERTY
+ // DE199718
+ @Test(dataProvider = "updatePropertiesListDefaultValueFailureFlow")
+ public void updateDefaultValueOfResourcePropertyListFailureFlow(String entrySchemaType, String propertyDefaltValues,
+ String expecteddefaultValues, String newEntrySchemaType, String newPropertyDefaltValues) throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.setPropertyDefaultValue(propertyDefaltValues);
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // verify properties return from response
+ assertEquals("list", resourcePropertiesFromResponse.getType());
+ assertEquals(expecteddefaultValues, resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(propertyDetails.getSchema().getProperty().getType(),
+ resourcePropertiesFromResponse.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ // Update resource property type = "list"
+ propertyDetails.setPropertyDefaultValue(newPropertyDefaltValues);
+ propertyDetails.getSchema().getProperty().setType(newEntrySchemaType);
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(propertyDetails.getName());
+ variables.add(propertyDetails.getPropertyType());
+ variables.add(propertyDetails.getSchema().getProperty().getType());
+ variables.add(newPropertyDefaltValues);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name(), variables,
+ updatePropertyResponse.getResponse());
+ }
+
+ @Test
+ public void updatePropertyOfDerivedResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty(PropertyTypeEnum.STRING_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String derivedResourcePropertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ // second resource derived from basicVFC
+ Resource vfc1FromBasicVFC = AtomicOperationUtils
+ .createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC, basicVFC,
+ ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ // add property Type list to second resource
+ PropertyReqDetails defaultListProperty = ElementFactory.getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(defaultListProperty, vfc1FromBasicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Update property (list) of derived resource
+ RestResponse updatePropertyResponse = AtomicOperationUtils.updatePropertyOfResource(propertyDetails, basicVFC,
+ derivedResourcePropertyUniqueId, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ updatePropertyResponse.getResponse());
+ // Verify resource's priority list did not changed
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[\"a\",\"b\"]");
+ }
+
+ @Test
+ public void updatePropertyOfNonDerivedResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty(PropertyTypeEnum.STRING_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ // second resource derived from basicVFC
+ Resource vfc1FromBasicVFC = AtomicOperationUtils
+ .createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC, basicVFC,
+ ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ // add property Type list to second resource
+ PropertyReqDetails defaultListProperty = ElementFactory.getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(defaultListProperty, vfc1FromBasicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // Update property (list) of derived resource
+ defaultListProperty.setPropertyDefaultValue("[1,2,3,4]");
+ String expectedDefaultValue = "[1,2,3,4]";
+ ComponentInstanceProperty resourcePropertyAfterUpdate = AtomicOperationUtils
+ .updatePropertyOfResource(defaultListProperty, vfc1FromBasicVFC, propertyUniqueId,
+ UserRoleEnum.DESIGNER, true)
+ .left().value();
+ assertEquals(resourcePropertyAfterUpdate.getType(), "list");
+ assertEquals(resourcePropertyAfterUpdate.getDefaultValue(), expectedDefaultValue);
+ assertEquals(resourcePropertyAfterUpdate.getSchema().getProperty().getType(),
+ defaultListProperty.getSchema().getProperty().getType()); // string/integer/boolean/float
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(vfc1FromBasicVFC.getUniqueId());
+ String expectedDefaultValueFromDerivedResource = "[\"a\",\"b\"]";
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ resource.getProperties().get(0).getDefaultValue().equals(expectedDefaultValue);
+ resource.getProperties().get(1).getDefaultValue().equals(expectedDefaultValueFromDerivedResource);
+ }
+
+ @Test
+ public void updateListPropertyToNonCheckedOutResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String PropertyDefaultValue = "[2,3]";
+ propertyDetails.setPropertyDefaultValue(PropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType("integer");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ // Update resource property type = "list"
+ propertyDetails.setPropertyDefaultValue("[3,4]");
+ propertyDetails.getSchema().getProperty().setType("integer");
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ updatePropertyResponse.getResponse());
+ // Verify resource's priority list did not changed
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[2,3]");
+ }
+
+ @Test
+ public void updateListPropertyResourceByNonResouceOwner() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String PropertyDefaultValue = "[2,3]";
+ propertyDetails.setPropertyDefaultValue(PropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType("integer");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // AtomicOperationUtils.changeComponentState(basicVFC,
+ // UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ // Update resource property type = "list"
+ propertyDetails.setPropertyDefaultValue("[3,4]");
+ propertyDetails.getSchema().getProperty().setType("integer");
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER2, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ updatePropertyResponse.getResponse());
+ // Verify resource's priority list did not changed
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[2,3]");
+ }
+
+ @Test
+ public void updateListPropertyResourceByTester() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String PropertyDefaultValue = "[2,3]";
+ propertyDetails.setPropertyDefaultValue(PropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType("integer");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // AtomicOperationUtils.changeComponentState(basicVFC,
+ // UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ // Update resource property type = "list"
+ propertyDetails.setPropertyDefaultValue("[3,4]");
+ propertyDetails.getSchema().getProperty().setType("integer");
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.TESTER, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ updatePropertyResponse.getResponse());
+ // Verify resource's priority list did not changed
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[2,3]");
+ }
+
+ // DE199964
+ @Test(enabled = false)
+ public void updateListPropertyToNonExistingResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String PropertyDefaultValue = "[2,3]";
+ propertyDetails.setPropertyDefaultValue(PropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType("integer");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ String resourceUniqueId = basicVFC.getUniqueId();
+ basicVFC.setUniqueId("1111111");
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_NOT_FOUND));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(basicVFC.getUniqueId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), variables,
+ updatePropertyResponse.getResponse());
+ // Verify resource's priority list did not changed
+ basicVFC.setUniqueId(resourceUniqueId);
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[2,3]");
+ }
+
+ // DE199725
+ @Test
+ public void updateResourcePropertyListNonSupportedPropertyType() throws Exception { // Not
+ // "list"
+ // type
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String PropertyDefaultValue = "[2,3]";
+ propertyDetails.setPropertyDefaultValue(PropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType("integer");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // update resource property
+ String propertyType = "listttttttt";
+ propertyDetails.setPropertyType(propertyType);
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, false)
+ .right().value();
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(propertyDetails.getPropertyType()); // property data type
+ // (koko instead
+ // list)
+ variables.add(propertyDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_TYPE.name(), variables,
+ updatePropertyResponse.getResponse());
+ // Verify resource's priority list did not changed
+ propertyDetails.setPropertyType("list");
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[2,3]");
+ }
+
+ @Test(enabled = false) // DE199732
+ public void updateResourcePropertyListNonSupportedEntrySchemaType() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String PropertyDefaultValue = "[2,3]";
+ propertyDetails.setPropertyDefaultValue(PropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType("integer");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // update resource property
+ String EntrySchemaType = "integerrrrrr";
+ propertyDetails.getSchema().getProperty().setType(EntrySchemaType);
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(EntrySchemaType);
+ variables.add(propertyDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_INNER_TYPE.name(), variables,
+ updatePropertyResponse.getResponse());
+ propertyDetails.getSchema().getProperty().setType("integer");
+ verifyResourcePropertyList(basicVFC, propertyDetails, "[2,3]");
+ }
+
+ @Test(dataProvider = "updatePropertiesListDefaultValueSuccessFlow")
+ public void updateResourcePropertyListSuccessFlow(String entrySchemaType, String propertyDefaltValues,
+ String expecteddefaultValues, String newEntrySchemaType, String newPropertyDefaltValues,
+ String newExpecteddefaultValues) throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.setPropertyDefaultValue(propertyDefaltValues);
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // verify properties return from response
+ assertEquals("list", resourcePropertiesFromResponse.getType());
+ assertEquals(expecteddefaultValues, resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(propertyDetails.getSchema().getProperty().getType(),
+ resourcePropertiesFromResponse.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ // Update resource property type = "list"
+ propertyDetails.setPropertyDefaultValue(newPropertyDefaltValues);
+ propertyDetails.getSchema().getProperty().setType(newEntrySchemaType);
+ ComponentInstanceProperty resourcePropertyAfterUpdate = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ assertEquals("list", resourcePropertyAfterUpdate.getType());
+ assertEquals(newExpecteddefaultValues, resourcePropertyAfterUpdate.getDefaultValue());
+ assertEquals(propertyDetails.getSchema().getProperty().getType(),
+ resourcePropertyAfterUpdate.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, newExpecteddefaultValues);
+ }
+
+ // Add property type list to resource
+ // DE199718
+ @Test(dataProvider = "invalidListProperties") // invalid default values
+ public void addListPropertyToResourceFailureFlow(String entrySchemaType, String propertyDefaltValues)
+ throws Exception {
+ // String propertyType = "list";
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ propertyDetails.setPropertyDefaultValue(propertyDefaltValues);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ RestResponse addPropertyToResourceResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyToResourceResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(propertyDetails.getName());
+ variables.add(propertyDetails.getPropertyType());
+ variables.add(propertyDetails.getSchema().getProperty().getType());
+ variables.add(propertyDefaltValues);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name(), variables,
+ addPropertyToResourceResponse.getResponse());
+
+ }
+
+ // DE199964
+ @Test
+ public void addListPropertyToNonExistingResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType("integer");
+ propertyDetails.setPropertyDefaultValue("[1,2]");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to non existing resource
+ basicVFC.setUniqueId("1111111");
+ RestResponse addPropertyToResourceResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyToResourceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_NOT_FOUND));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), variables,
+ addPropertyToResourceResponse.getResponse());
+ }
+
+ @Test
+ public void addListPropertyToNonCheckedOutResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType("integer");
+ propertyDetails.setPropertyDefaultValue("[1,2]");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ // Add property type list to non Checked-Out resource
+ RestResponse addPropertyToResourceResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyToResourceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ addPropertyToResourceResponse.getResponse());
+ }
+
+ @Test
+ public void addListPropertyToResourceByNonResourceOwner() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType("integer");
+ propertyDetails.setPropertyDefaultValue("[1,2]");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to non Checked-Out resource
+ RestResponse addPropertyToResourceResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER2, false).right().value();
+ assertTrue(addPropertyToResourceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ addPropertyToResourceResponse.getResponse());
+ }
+
+ @Test
+ public void addListPropertyToResourcePropertyAlreadyExists01() throws Exception {
+ String propertyType = "list";
+ String propertySchemaType = "integer";
+ String defaultValues = "[1,2]";
+ String expecteddefaultValues = "[1,2]";
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType(propertySchemaType);
+ propertyDetails.setPropertyDefaultValue(defaultValues);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // check-in and check-out resource
+ RestResponse changeComponentState = LifecycleRestUtils.changeComponentState(basicVFC,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKIN);
+ assertTrue(changeComponentState.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS));
+ changeComponentState = LifecycleRestUtils.changeComponentState(basicVFC,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKOUT);
+ assertTrue(changeComponentState.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS));
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ // verify properties return from response
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyType);
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), expecteddefaultValues);
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(), propertySchemaType); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ // Add same property again to resource
+ RestResponse addPropertyRestResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyRestResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_ALREADY_EXISTS));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_ALREADY_EXIST.name(), variables,
+ addPropertyRestResponse.getResponse());
+ // verify property not deleted
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ }
+
+ @Test
+ public void addListPropertyToResourcePropertyAlreadyExists02() throws Exception {
+ String propertyType = "list";
+ String propertySchemaType = "integer";
+ String defaultValues = "[1,2]";
+ String expecteddefaultValues = "[1,2]";
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType(propertySchemaType);
+ propertyDetails.setPropertyDefaultValue(defaultValues);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ // verify properties return from response
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyType);
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), expecteddefaultValues);
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(), propertySchemaType); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ // check-in and check-out resource
+ RestResponse changeComponentState = LifecycleRestUtils.changeComponentState(basicVFC,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKIN);
+ assertTrue(changeComponentState.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS));
+ changeComponentState = LifecycleRestUtils.changeComponentState(basicVFC,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKOUT);
+ assertTrue(changeComponentState.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS));
+ // Add same property again to resource
+ RestResponse addPropertyRestResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyRestResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_ALREADY_EXISTS));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_ALREADY_EXIST.name(), variables,
+ addPropertyRestResponse.getResponse());
+ // verify property not deleted
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ }
+
+ @Test // DE199725
+ public void addListPropertyToResourceNonSupportedPropertyType() throws Exception { // Not
+ // "list"
+ // type
+ String propertyType = "listttttttt";
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.setPropertyType(propertyType);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ RestResponse addPropertyRestResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyRestResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(propertyDetails.getPropertyType()); // property data type
+ // (koko instead
+ // list)
+ variables.add(propertyDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_TYPE.name(), variables,
+ addPropertyRestResponse.getResponse());
+ }
+
+ @Test // DE199732
+ public void addListPropertyToResourceNonSupportedEntrySchemaType() throws Exception {
+ String EntrySchemaType = "stringggg"; // instead "string"
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType(EntrySchemaType);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ RestResponse addPropertyRestResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyRestResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(EntrySchemaType);
+ variables.add(propertyDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_PROPERTY_INNER_TYPE.name(), variables,
+ addPropertyRestResponse.getResponse());
+ }
+
+ @Test
+ public void addHundredPropertyListToResourceSuccessFlow() throws Exception {
+ String propertyType = "list";
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ String propertyName = propertyDetails.getName();
+ int numberOfPropertiesToAddToResource = 100;
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ for (int x = 0; x < numberOfPropertiesToAddToResource; x++) {
+ propertyDetails.setName(propertyName + x);
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ // verify properties return from response
+ assertEquals(resourcePropertiesFromResponse.getName(), propertyName + x);
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyType);
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[\"a\",\"b\"]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetails.getSchema().getProperty().getType()); // string/integer/boolean/float
+ }
+ // get resource and verify that 100 properties exist
+ Resource resourceObject = AtomicOperationUtils.getResourceObject(basicVFC, UserRoleEnum.DESIGNER);
+ assertEquals(numberOfPropertiesToAddToResource, resourceObject.getProperties().size());
+
+ }
+
+ @Test(dataProvider = "propertiesListDefaultValueSuccessFlow")
+ public void addListPropertyToResourceSuccessFlow(String entrySchemaType, String propertyDefaltValues,
+ String expecteddefaultValues) throws Exception {
+ String propertyType = "list";
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ propertyDetails.setPropertyDefaultValue(propertyDefaltValues);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ // verify properties return from response
+ assertEquals(propertyType, resourcePropertiesFromResponse.getType());
+ assertEquals(expecteddefaultValues, resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(entrySchemaType, resourcePropertiesFromResponse.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyType);
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), expecteddefaultValues);
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(), entrySchemaType); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ }
+
+ // Delete property type list
+ @Test
+ public void deleteOneOfTheListPropertiesFromResourceAndAddItAgain() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.STRING_LIST);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[\"a\",\"b\"]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1,2]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, "[\"a\",\"b\"]");
+ // Add deleted property again to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1,2]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ }
+
+ @Test
+ public void deletePropertyListTypeInteger() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.STRING_LIST);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[\"a\",\"b\"]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1,2]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "[1,2]");
+ }
+
+ @Test
+ public void deletePropertyListTypeBoolean() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.BOOLEAN_LIST);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[true,false]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1,2]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one property
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "[1,2]");
+ }
+
+ @Test
+ public void deletePropertyListTypeFloat() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.FLOAT_LIST);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1.0,2.0]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1,2]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one property
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "[1,2]");
+ }
+
+ @Test
+ public void deletePropertyListAlreadyDeleted() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.FLOAT_LIST);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1.0,2.0]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), "[1,2]");
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one property
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "[1,2]");
+ // delete again the same property
+ deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_NOT_FOUND == deletePropertyOfResource.getErrorCode());
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_NOT_FOUND.name(), variables,
+ deletePropertyOfResource.getResponse());
+ }
+
+ @Test
+ public void deletePropertyListResourceIsNotCheckedOutState() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.FLOAT_LIST);
+ String expectedDefaultvalues = "[1.0,2.0]";
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), expectedDefaultvalues);
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ // Get resource and verify updated default value
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, expectedDefaultvalues);
+ // Check-in resource
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ // Delete property
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION == deletePropertyOfResource.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deletePropertyOfResource.getResponse());
+ // Get resource and verify property is not deleted
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, expectedDefaultvalues);
+ }
+
+ @Test
+ public void deletePropertyListResourceByNotIsNonResouceOwner() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.FLOAT_LIST);
+ String expectedDefaultvalues = "[1.0,2.0]";
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), expectedDefaultvalues);
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ // Get resource and verify updated default value
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, expectedDefaultvalues);
+ // Delete property by non resource owner
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER2);
+ assertTrue(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION == deletePropertyOfResource.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ deletePropertyOfResource.getResponse());
+ // Get resource and verify property is not deleted
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, expectedDefaultvalues);
+ }
+
+ @Test
+ public void deletePropertyListFromNonExistingResource() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.FLOAT_LIST);
+ String expectedDefaultvalues = "[1.0,2.0]";
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String actualResourceUniqueId = basicVFC.getUniqueId();
+ // Add property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals(resourcePropertiesFromResponse.getDefaultValue(), expectedDefaultvalues);
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ // Get resource and verify updated default value
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, expectedDefaultvalues);
+ // Delete property from non existing resource
+ basicVFC.setUniqueId("1111111");
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(deletePropertyOfResource.getErrorCode().equals(BaseRestUtils.STATUS_CODE_NOT_FOUND));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), variables,
+ deletePropertyOfResource.getResponse());
+ // Get resource and verify property is not deleted
+ basicVFC.setUniqueId(actualResourceUniqueId);
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, expectedDefaultvalues);
+ }
+
+ @Test
+ public void deletePropertyOfDerivedResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty(PropertyTypeEnum.STRING_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String derivedResourcePropertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ // second resource derived from basicVFC
+ Resource vfc1FromBasicVFC = AtomicOperationUtils
+ .createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC, basicVFC,
+ ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ // Delete property (list) of derived resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(
+ vfc1FromBasicVFC.getUniqueId(), derivedResourcePropertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(deletePropertyOfResource.getErrorCode().equals(BaseRestUtils.STATUS_CODE_NOT_FOUND));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_NOT_FOUND.name(), variables,
+ deletePropertyOfResource.getResponse());
+ // Verify resource's priority list did not changed
+ verifyResourcePropertyList(vfc1FromBasicVFC, propertyDetails, "[\"a\",\"b\"]");
+ }
+
+ @Test
+ public void deletePropertyOfNonDerivedResource() throws Exception {
+ PropertyReqDetails propertyListString = ElementFactory.getDefaultListProperty(PropertyTypeEnum.STRING_LIST);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyListString, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.changeComponentState(basicVFC, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ // second resource derived from basicVFC
+ Resource vfc1FromBasicVFC = AtomicOperationUtils
+ .createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum.VFC, basicVFC,
+ ResourceCategoryEnum.APPLICATION_L4_BORDER, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ // add property Type list to second resource
+ PropertyReqDetails propertyListInteger = ElementFactory.getDefaultListProperty(PropertyTypeEnum.INTEGER_LIST);
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyListInteger, vfc1FromBasicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // Delete property (list) of derived resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils
+ .deletePropertyOfResource(vfc1FromBasicVFC.getUniqueId(), propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyListString, "[\"a\",\"b\"]");
+ }
+
+ private void verifyResourcePropertyList(Resource resource, PropertyReqDetails expectedProperty,
+ String expecteddefaultValues) throws Exception {
+ // get resource and verify property from type list
+ Resource getResource = AtomicOperationUtils.getResourceObject(resource, UserRoleEnum.DESIGNER);
+ List<PropertyDefinition> actualResourceProperties = getResource.getProperties();
+ boolean isPropertyAppear = false;
+ for (PropertyDefinition pro : actualResourceProperties) {
+ if (expectedProperty.getName().equals(pro.getName())) {
+ assertTrue("Check Property Type ", pro.getType().equals(expectedProperty.getPropertyType()));
+ assertEquals("Check Property default values ", expecteddefaultValues, pro.getDefaultValue());
+ // assertTrue("Check Property default values ",
+ // pro.getDefaultValue().equals(expecteddefaultValues));
+ assertTrue("Check entrySchema Property Type ", pro.getSchema().getProperty().getType()
+ .equals(expectedProperty.getSchema().getProperty().getType()));
+ isPropertyAppear = true;
+ }
+ }
+ assertTrue(isPropertyAppear);
+ }
+
+ // US656905
+ // --------------------- Map Property
+ // ----------------------------------------------------------------
+ @Test(dataProvider = "updatePropertiesMapDefaultValueFailureFlow")
+ public void updateDefaultValueOfResourcePropertyMapFailureFlow(String entrySchemaType, String propertyDefaultValues,
+ String expectedDefaultValue, String newEntrySchemaType, String newPropertyDefaultValue) throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultMapProperty();
+ propertyDetails.setPropertyDefaultValue(propertyDefaultValues);
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // verify properties return from response
+ assertEquals("map", resourcePropertiesFromResponse.getType());
+ assertEquals(expectedDefaultValue, resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(propertyDetails.getSchema().getProperty().getType(),
+ resourcePropertiesFromResponse.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expectedDefaultValue);
+ // Update resource property type = "map"
+ propertyDetails.setPropertyDefaultValue(newPropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType(newEntrySchemaType);
+ RestResponse updatePropertyResponse = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, false)
+ .right().value();
+ assertTrue(updatePropertyResponse.getErrorCode().equals(STATUS_CODE_INVALID_CONTENT));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(propertyDetails.getName());
+ variables.add(propertyDetails.getPropertyType());
+ variables.add(propertyDetails.getSchema().getProperty().getType());
+ variables.add(newPropertyDefaultValue);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE.name(), variables,
+ updatePropertyResponse.getResponse());
+ }
+
+ @Test(dataProvider = "updatePropertiesMapDefaultValueSuccessFlow")
+ public void updateResourcePropertyMapSuccessFlow(String entrySchemaType, String propertyDefaultValues,
+ String expectedDefaultValue, String newEntrySchemaType, String newPropertyDefaultValue,
+ String newExpectedDefaultValue) throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultMapProperty();
+ propertyDetails.setPropertyDefaultValue(propertyDefaultValues);
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ // verify properties return from response
+ assertEquals("map", resourcePropertiesFromResponse.getType());
+ assertEquals(expectedDefaultValue, resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(propertyDetails.getSchema().getProperty().getType(),
+ resourcePropertiesFromResponse.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expectedDefaultValue);
+ // Update resource property type = "map"
+ propertyDetails.setPropertyDefaultValue(newPropertyDefaultValue);
+ propertyDetails.getSchema().getProperty().setType(newEntrySchemaType);
+ ComponentInstanceProperty resourcePropertyAfterUpdate = AtomicOperationUtils
+ .updatePropertyOfResource(propertyDetails, basicVFC, propertyUniqueId, UserRoleEnum.DESIGNER, true)
+ .left().value();
+ assertEquals("map", resourcePropertyAfterUpdate.getType());
+ assertEquals(newExpectedDefaultValue, resourcePropertyAfterUpdate.getDefaultValue());
+ assertEquals(propertyDetails.getSchema().getProperty().getType(),
+ resourcePropertyAfterUpdate.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, newExpectedDefaultValue);
+ }
+
+ @Test
+ public void deletePropertyMapTypeString() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultMapProperty(PropertyTypeEnum.STRING_MAP);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory.getDefaultMapProperty(PropertyTypeEnum.INTEGER_MAP);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals("{\"key1\":\"val1\",\"key2\":\"val2\"}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals("{\"key1\":123,\"key2\":-456}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "{\"key1\":123,\"key2\":-456}");
+ }
+
+ @Test
+ public void deletePropertyMapTypeFloat() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeFloat = ElementFactory.getDefaultMapProperty(PropertyTypeEnum.FLOAT_MAP);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory.getDefaultMapProperty(PropertyTypeEnum.INTEGER_MAP);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeFloat, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeFloat.getPropertyType());
+ assertEquals("{\"key1\":0.2123,\"key2\":43.545}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeFloat.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals("{\"key1\":123,\"key2\":-456}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "{\"key1\":123,\"key2\":-456}");
+ }
+
+ @Test
+ public void deletePropertyMapTypeBoolean() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeBoolean = ElementFactory
+ .getDefaultMapProperty(PropertyTypeEnum.BOOLEAN_MAP);
+ PropertyReqDetails propertyDetailsInteger = ElementFactory.getDefaultMapProperty(PropertyTypeEnum.INTEGER_MAP);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeBoolean, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeBoolean.getPropertyType());
+ assertEquals("{\"key1\":true,\"key2\":false}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeBoolean.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsInteger.getPropertyType());
+ assertEquals("{\"key1\":123,\"key2\":-456}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsInteger.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsInteger, "{\"key1\":123,\"key2\":-456}");
+ }
+
+ @Test
+ public void deletePropertyMapTypeInteger() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeInteger = ElementFactory
+ .getDefaultMapProperty(PropertyTypeEnum.INTEGER_MAP);
+ PropertyReqDetails propertyDetailsBoolean = ElementFactory.getDefaultMapProperty(PropertyTypeEnum.BOOLEAN_MAP);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeInteger, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ String propertyUniqueId = resourcePropertiesFromResponse.getUniqueId();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeInteger.getPropertyType());
+ assertEquals("{\"key1\":123,\"key2\":-456}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeInteger.getSchema().getProperty().getType()); // string/integer/boolean/float
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsBoolean, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsBoolean.getPropertyType());
+ assertEquals("{\"key1\":true,\"key2\":false}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsBoolean.getSchema().getProperty().getType());
+ // Get resource and verify updated default value
+ RestResponse restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(2, resource.getProperties().size());
+ // Delete one resource
+ RestResponse deletePropertyOfResource = AtomicOperationUtils.deletePropertyOfResource(basicVFC.getUniqueId(),
+ propertyUniqueId, UserRoleEnum.DESIGNER);
+ assertTrue(BaseRestUtils.STATUS_CODE_DELETE == deletePropertyOfResource.getErrorCode());
+ // Get resource and verify updated default value
+ restResponse = ResourceRestUtils.getResource(basicVFC.getUniqueId());
+ resource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ assertEquals(1, resource.getProperties().size());
+ verifyResourcePropertyList(basicVFC, propertyDetailsBoolean, "{\"key1\":true,\"key2\":false}");
+ }
+
+ @Test(dataProvider = "propertiesMapDefaultValueSuccessFlow")
+ public void addMapPropertyToResourceSuccessFlow(String entrySchemaType, String propertyDefaltValues,
+ String expecteddefaultValues) throws Exception {
+ String propertyType = "map";
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultMapProperty();
+ propertyDetails.getSchema().getProperty().setType(entrySchemaType);
+ propertyDetails.setPropertyDefaultValue(propertyDefaltValues);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to resource
+ ComponentInstanceProperty resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, true).left().value();
+ // verify properties return from response
+ assertEquals(propertyType, resourcePropertiesFromResponse.getType());
+ assertEquals(expecteddefaultValues, resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(entrySchemaType, resourcePropertiesFromResponse.getSchema().getProperty().getType()); // string/integer/boolean/float
+ verifyResourcePropertyList(basicVFC, propertyDetails, expecteddefaultValues);
+ }
+
+ @Test
+ public void addMapPropertyToNonExistingResource() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType("integer");
+ propertyDetails.setPropertyDefaultValue("{\"key1\":1 , \"key2\":2}");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to non existing resource
+ basicVFC.setUniqueId("1111111");
+ RestResponse addPropertyToResourceResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER, false).right().value();
+ assertTrue(addPropertyToResourceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_NOT_FOUND));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), variables,
+ addPropertyToResourceResponse.getResponse());
+ }
+
+ @Test
+ public void addMaptPropertyToResourceByNonResourceOwner() throws Exception {
+ PropertyReqDetails propertyDetails = ElementFactory.getDefaultListProperty();
+ propertyDetails.getSchema().getProperty().setType("integer");
+ propertyDetails.setPropertyDefaultValue("{\"key1\":1 , \"key2\":2}");
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add property type list to non Checked-Out resource
+ RestResponse addPropertyToResourceResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetails, basicVFC, UserRoleEnum.DESIGNER2, false).right().value();
+ assertTrue(addPropertyToResourceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION));
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ addPropertyToResourceResponse.getResponse());
+ }
+
+ @Test
+ public void addMapPropertyToResourcePropertyAlreadyExists() throws Exception {
+ ComponentInstanceProperty resourcePropertiesFromResponse;
+ PropertyReqDetails propertyDetailsTypeString = ElementFactory
+ .getDefaultListProperty(PropertyTypeEnum.STRING_MAP);
+ // create resource
+ Resource basicVFC = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC,
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ // Add 2 property type list to resource
+ resourcePropertiesFromResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, true).left()
+ .value();
+ assertEquals(resourcePropertiesFromResponse.getType(), propertyDetailsTypeString.getPropertyType());
+ assertEquals("{\"key1\":\"val1\",\"key2\":\"val2\"}", resourcePropertiesFromResponse.getDefaultValue());
+ assertEquals(resourcePropertiesFromResponse.getSchema().getProperty().getType(),
+ propertyDetailsTypeString.getSchema().getProperty().getType()); // string/integer/boolean/float
+ // check-in and check-out resource
+ RestResponse changeComponentState = LifecycleRestUtils.changeComponentState(basicVFC,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKIN);
+ assertTrue(changeComponentState.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS));
+ changeComponentState = LifecycleRestUtils.changeComponentState(basicVFC,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKOUT);
+ assertTrue(changeComponentState.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS));
+ // Add same property again to resource
+ RestResponse addPropertyRestResponse = AtomicOperationUtils
+ .addCustomPropertyToResource(propertyDetailsTypeString, basicVFC, UserRoleEnum.DESIGNER, false).right()
+ .value();
+ assertTrue(addPropertyRestResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_ALREADY_EXISTS));
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_ALREADY_EXIST.name(), variables,
+ addPropertyRestResponse.getResponse());
+ // verify property not deleted
+ verifyResourcePropertyList(basicVFC, propertyDetailsTypeString, "{\"key1\":\"val1\",\"key2\":\"val2\"}");
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/PropertyApisTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/PropertyApisTest.java
new file mode 100644
index 0000000000..d1302c8d8c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/property/PropertyApisTest.java
@@ -0,0 +1,383 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.property;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.preRequisites.SimpleOneRsrcOneServiceTest;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.PropertyRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.UserRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class PropertyApisTest extends SimpleOneRsrcOneServiceTest {
+
+ protected static final String RESOURCE_CATEGORY = "Generic/Databases";
+ protected Config config = Config.instance();
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";;
+
+ // protected User sdncDesignerDetails;
+ // protected ResourceReqDetails resourceDetails;
+ protected PropertyReqDetails property;
+ protected String body;
+
+ protected HttpRequest httpRequest = new HttpRequest();
+ protected Map<String, String> headersMap = new HashMap<String, String>();
+
+ @Rule
+ public static TestName testName = new TestName();
+
+ public PropertyApisTest() {
+ super(testName, PropertyApisTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+ // //Create user
+ // sdncDesignerDetails = createUser("tu1234", "Test", "User",
+ // "tu1234@intl.sdc.com", "DESIGNER");
+ //
+ // //Delete resource
+ //
+ // resourceDetails = new ResourceReqDetails();
+ // resourceDetails.setResourceName("testresourceDetails");
+ //
+ // resourceUtils.deleteResource_allVersions(resourceDetails,
+ // sdncDesignerDetails);
+ //
+ // //Create resource
+ // resourceDetails = createResource(sdncDesignerDetails,
+ // "testresourceDetails");
+
+ // Create property
+ // property.setPropertyName("test");
+ // property.setPropertyType("integer");
+ // property.setPropertySource("A&AI");
+ // property.setPropertyDescription("test property");
+
+ // body = gson.toJson(property);
+ property = ElementFactory.getDefaultProperty();
+ body = property.propertyToJsonString();
+ // System.out.println(body);
+ // HTTP (for negative tests)
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncDesignerDetails.getUserId());
+
+ }
+
+ @Test
+ public void testPropertyApis() throws Exception {
+ // Create property
+ // System.out.println ("---- Create Property (POST) ----");
+
+ String propertyId = UniqueIdBuilder.buildPropertyUniqueId(getResourceId(resourceDetails), property.getName());
+
+ PropertyRestUtils.deleteProperty(getResourceId(resourceDetails), propertyId, sdncDesignerDetails);
+ RestResponse createPropertyResponse = PropertyRestUtils.createProperty(getResourceId(resourceDetails), body,
+ sdncDesignerDetails);
+ AssertJUnit.assertTrue("Expected result code - 201, received - " + createPropertyResponse.getErrorCode(),
+ createPropertyResponse.getErrorCode() == 201);
+
+ // Get property
+ // System.out.println ("---- Get Property (GET) ----");
+ RestResponse getPropertyResponse = PropertyRestUtils.getProperty(getResourceId(resourceDetails), propertyId,
+ sdncDesignerDetails);
+ AssertJUnit.assertTrue("Expected result code - 200, received - " + getPropertyResponse.getErrorCode(),
+ getPropertyResponse.getErrorCode() == 200);
+
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(getPropertyResponse.getResponse());
+
+ // assertTrue("Wrong 'type' in the
+ // response",jsonResp.get("type").equals(property.getPropertyType()));
+ // assertTrue("Wrong 'source' in the
+ // response",jsonResp.get("name").equals(property.getPropertyName()));
+ // assertTrue("Wrong 'name' in the
+ // response",jsonResp.get("source").equals(property.getPropertySource()));
+ // assertTrue("Wrong 'description' in the
+ // response",jsonResp.get("description").equals(property.getPropertyDescription()));
+
+ // Update property
+ // System.out.println ("---- Update Property (UPDATE) ----");
+ property.setPropertyDescription("Updated description");
+ // body = gson.toJson(property);
+ body = property.propertyToJsonString();
+
+ RestResponse updatePropertyResponse = PropertyRestUtils.updateProperty(getResourceId(resourceDetails),
+ propertyId, body, sdncDesignerDetails);
+ AssertJUnit.assertTrue("Expected result code - 200, received - " + updatePropertyResponse.getErrorCode(),
+ updatePropertyResponse.getErrorCode() == 200);
+
+ // Get property
+ // System.out.println ("---- Get Property (GET) ----");
+ getPropertyResponse = PropertyRestUtils.getProperty(getResourceId(resourceDetails), propertyId,
+ sdncDesignerDetails);
+ AssertJUnit.assertTrue("Expected result code - 200, received - " + getPropertyResponse.getErrorCode(),
+ getPropertyResponse.getErrorCode() == 200);
+
+ jsonResp = (JSONObject) JSONValue.parse(getPropertyResponse.getResponse());
+
+ // assertTrue("Wrong 'type' in the
+ // response",jsonResp.get("type").equals(property.getPropertyType()));
+ // assertTrue("Wrong 'source' in the
+ // response",jsonResp.get("name").equals(property.getPropertyName()));
+ // assertTrue("Wrong 'name' in the
+ // response",jsonResp.get("source").equals(property.getPropertySource()));
+ // assertTrue("Wrong 'description' in the
+ // response",jsonResp.get("description").equals(property.getPropertyDescription()));
+
+ // Delete property
+ // System.out.println ("---- Delete Property (DELETE) ----");
+ RestResponse deletePropertyResponse = PropertyRestUtils.deleteProperty(getResourceId(resourceDetails),
+ propertyId, sdncDesignerDetails);
+ AssertJUnit.assertTrue("Expected result code - 204, received - " + deletePropertyResponse.getErrorCode(),
+ deletePropertyResponse.getErrorCode() == 204);
+
+ // Get property - verify that the property doesn't exist.
+ // System.out.println("---- GET - Property Not Found ----");
+ getPropertyResponse = PropertyRestUtils.getProperty(getResourceId(resourceDetails), propertyId,
+ sdncDesignerDetails);
+ List<String> variables = Arrays.asList("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_NOT_FOUND.name(), variables,
+ getPropertyResponse.getResponse());
+
+ }
+
+ // --------------------------------------------------------------------------------------
+
+ protected String getPropertyId(ResourceReqDetails resource, PropertyReqDetails property) {
+ // return
+ // resource.getResourceName().toLowerCase()+".0.1."+property.getPropertyName();
+ return UniqueIdBuilder.buildPropertyUniqueId(resource.getUniqueId(), property.getName());
+ }
+
+ protected String getResourceId(ResourceReqDetails resource) {
+ // String resourceUid =
+ // UniqueIdBuilder.buildResourceUniqueId(resource.getResourceName(),
+ // "0.1");
+
+ return resource.getUniqueId();
+ }
+
+ protected User createUser(String cspUserId, String firstName, String lastName, String email, String role)
+ throws Exception {
+ User sdncUserDetails = new User(firstName, lastName, cspUserId, email, role, null);
+
+ User adminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ UserRestUtils.createUser(sdncUserDetails, adminUser);
+
+ return sdncUserDetails;
+ }
+
+ protected ResourceReqDetails createResource(User sdncUserDetails, String resourceName) throws Exception {
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ // String category = ResourceCategoryEnum.DATABASE.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.0";
+ String contactId = sdncUserDetails.getUserId();
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_DATABASE.getCategory(),
+ ResourceCategoryEnum.GENERIC_DATABASE.getSubCategory());
+ // deleteResource(resourceName.toLowerCase()+".0.1",sdncUserDetails.getUserId());
+ // TODO delete by name
+ // deleteResource(UniqueIdBuilder.buildResourceUniqueId(resourceName,
+ // "0.1"), sdncUserDetails.getUserId());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncUserDetails);
+ AssertJUnit.assertTrue(createResource.getErrorCode().intValue() == 201);
+ String resourceId = ResponseParser.getUniqueIdFromResponse(createResource);
+ resourceDetails.setUniqueId(resourceId);
+
+ return resourceDetails;
+
+ }
+
+ @Test
+ public void putReqToCreateUriNotAllowed() throws Exception {
+ // System.out.println("---- PUT request to Create uri - Not Allowed
+ // ----");
+ String url = String.format(Urls.CREATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails));
+ RestResponse propertyErrorResponse = httpRequest.httpSendByMethod(url, "PUT", body, headersMap);
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void getReqToCreateUriNotAllowed() throws Exception {
+ // System.out.println("---- GET request to Create uri - Not Allowed
+ // ----");
+ String url = String.format(Urls.CREATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails));
+ RestResponse propertyErrorResponse = httpRequest.httpSendGet(url, headersMap);
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void deleteReqToCreateUriNotAllowed() throws Exception {
+ // System.out.println("---- DELETE request to Create uri - Not Allowed
+ // ----");
+ String url = String.format(Urls.CREATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails));
+ RestResponse propertyErrorResponse = httpRequest.httpSendDelete(url, headersMap);
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void postReqToUpdateUriNotAllowed() throws Exception {
+ // System.out.println("---- POST request to Update uri - Not Allowed
+ // ----");
+ String url = String.format(Urls.UPDATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails), getPropertyId(resourceDetails, property));
+ RestResponse propertyErrorResponse = httpRequest.httpSendPost(url, body, headersMap);
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void deleteReqPropertyNotFound() throws Exception {
+ // System.out.println("---- DELETE - Property Not Found ----");
+ String unknownPropertyId = getPropertyId(resourceDetails, property) + "111";
+ String url = String.format(Urls.DELETE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails), unknownPropertyId);
+ RestResponse propertyErrorResponse = httpRequest.httpSendDelete(url, headersMap);
+ List<String> variables = Arrays.asList("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_NOT_FOUND.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void updateReqPropertyNotFound() throws Exception {
+ // System.out.println("---- PUT - Property Not Found ----");
+ String unknownPropertyId = getPropertyId(resourceDetails, property) + "111";
+ String url = String.format(Urls.UPDATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails), unknownPropertyId);
+ RestResponse propertyErrorResponse = httpRequest.httpSendByMethod(url, "PUT", body, headersMap);
+ List<String> variables = Arrays.asList("");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.PROPERTY_NOT_FOUND.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void modifierNotTheStateOwner() throws Exception {
+ // System.out.println("---- The modifier is not the state owner -
+ // Operation Not Allowed ----");
+ User sdncUserDetails2 = createUser("tu5555", "Test", "User", "tu5555@intl.sdc.com", "DESIGNER");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails2.getUserId());
+ property.setPropertyDescription("new description");
+ // body = gson.toJson(property);
+ body = property.propertyToJsonString();
+ String url = String.format(Urls.UPDATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails), getPropertyId(resourceDetails, property));
+ RestResponse propertyErrorResponse = httpRequest.httpSendByMethod(url, "PUT", body, headersMap);
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables,
+ propertyErrorResponse.getResponse());
+
+ }
+
+ @Test
+ public void postReqInvalidContent() throws Exception {
+ // System.out.println("---- POST - Invalid Content ----");
+ body = "invalid";
+ String url = String.format(Urls.CREATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails), getPropertyId(resourceDetails, property));
+ RestResponse propertyErrorResponse = httpRequest.httpSendPost(url, body, headersMap);
+
+ // System.out.println(propertyErrorResponse.getResponse()+" "+
+ // propertyErrorResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ @Test
+ public void putReqInvalidContent() throws Exception {
+
+ // Create property
+ // System.out.println ("---- Create Property (POST) ----");
+ RestResponse createPropertyResponse = PropertyRestUtils.createProperty(getResourceId(resourceDetails), body,
+ sdncDesignerDetails);
+ assertTrue("Expected result code - 201, received - " + createPropertyResponse.getErrorCode(),
+ createPropertyResponse.getErrorCode() == 201);
+
+ // System.out.println("---- PUT - Invalid Content ----");
+ body = "invalid";
+
+ String url = String.format(Urls.UPDATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ getResourceId(resourceDetails), getPropertyId(resourceDetails, property));
+
+ // System.out.println(url + "\n" + body);
+
+ RestResponse propertyErrorResponse = httpRequest.httpSendByMethod(url, "PUT", body, headersMap);
+
+ // System.out.println(propertyErrorResponse.getResponse()+" "+
+ // propertyErrorResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), variables,
+ propertyErrorResponse.getResponse());
+ }
+
+ // --------------------------------------------------------------------------------------
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CheckGetResource.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CheckGetResource.java
new file mode 100644
index 0000000000..85dfe4e13a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CheckGetResource.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.util.SerializationUtils;
+
+import fj.data.Either;
+
+public class CheckGetResource {
+
+ public void checkGetVmmsc6() throws Exception {
+
+ try {
+
+ System.out.println("dddd");
+ RestResponse getResource = ResourceRestUtils.getResource("96eb6583-2822-448b-a284-bfc144fa627e");
+
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+
+ Either<byte[], Boolean> serialize = SerializationUtils.serializeExt(resource);
+
+ SerializationUtils.deserializeExt(serialize.left().value(), Resource.class, "ffff");
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ComponentRelationshipInVfTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ComponentRelationshipInVfTest.java
new file mode 100644
index 0000000000..d05dd1039a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ComponentRelationshipInVfTest.java
@@ -0,0 +1,1408 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.client.ClientProtocolException;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ComponentRelationshipInVfTest extends ComponentBaseTest {
+
+ public ComponentRelationshipInVfTest() {
+ super(new TestName(), ComponentRelationshipInVfTest.class.getName());
+ }
+
+ private ResourceReqDetails resourceDetailsVF;
+ private User designerUser;
+ private User adminUser;
+ private User testerUser;
+ private ResourceReqDetails resourceDetailsReq;
+ private ResourceReqDetails resourceDetailsCap;
+
+ @BeforeMethod
+ public void before() throws Exception {
+ designerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ adminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ testerUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+
+ resourceDetailsVF = ElementFactory.getDefaultResourceByType("VF100", NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, designerUser.getUserId(), ResourceTypeEnum.VF.toString());
+ createResource(resourceDetailsVF, designerUser);
+
+ resourceDetailsReq = ElementFactory.getDefaultResourceByType("SoftCompRouter",
+ NormativeTypesEnum.SOFTWARE_COMPONENT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS,
+ designerUser.getUserId(), ResourceTypeEnum.CP.toString()); // resourceType
+ // =
+ // VFC
+ resourceDetailsCap = ElementFactory.getDefaultResourceByType("MyCompute", NormativeTypesEnum.COMPUTE,
+ ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, designerUser.getUserId(), ResourceTypeEnum.CP.toString()); // resourceType
+ // =
+ // VFC
+
+ }
+
+ private void createResource(ResourceReqDetails resourceDetails, User user) throws Exception, IOException {
+ RestResponse createResourceResponse = ResourceRestUtils.createResource(resourceDetails, user);
+ ResourceRestUtils.checkCreateResponse(createResourceResponse);
+ if (!resourceDetails.getResourceType().equals("VF"))
+ LifecycleRestUtils.changeResourceState(resourceDetails, user, "0.1", LifeCycleStatesEnum.CHECKIN);
+ }
+
+ private void createAtomicResource(ResourceReqDetails resourceDetails, User user) throws Exception {
+ createResource(resourceDetails, user);
+ }
+
+ private RequirementCapabilityRelDef setRelationshipBetweenInstances(ComponentInstance riReq,
+ ComponentInstance riCap, CapReqDef capReqDef) throws Exception {
+
+ String capbilityUid = capReqDef.getCapabilities().get("tosca.capabilities.Container").get(0).getUniqueId();
+ String requirementUid = capReqDef.getRequirements().get("tosca.capabilities.Container").get(0).getUniqueId();
+
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ requirementDef.setFromNode(riReq.getUniqueId());
+ requirementDef.setToNode(riCap.getUniqueId());
+
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair();
+ pair.setRequirementOwnerId(riReq.getUniqueId());
+ pair.setCapabilityOwnerId(riCap.getUniqueId());
+ pair.setRequirement("host");
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType("tosca.capabilities.Container");
+ pair.setRelationships(relationship);
+ pair.setCapabilityUid(capbilityUid);
+ pair.setRequirementUid(requirementUid);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<>();
+ relationships.add(pair);
+ requirementDef.setRelationships(relationships);
+ return requirementDef;
+ }
+
+ private ComponentInstance createComponentInstance(ResourceReqDetails res) throws Exception {
+ return createComponentInstance(res, designerUser);
+ }
+
+ private ComponentInstance createComponentInstance(ResourceReqDetails res, User user, ResourceReqDetails vf)
+ throws Exception {
+ RestResponse response = ResourceRestUtils.createResourceInstance(res, user, vf.getUniqueId());
+ ResourceRestUtils.checkCreateResponse(response);
+ ComponentInstance compInstance = ResponseParser.parseToObject(response.getResponse(), ComponentInstance.class);
+ return compInstance;
+ }
+
+ private ComponentInstance createComponentInstance(ResourceReqDetails res, User user) throws Exception {
+ return createComponentInstance(res, user, resourceDetailsVF);
+ }
+
+ private void createTwoAtomicResourcesByType(String reqType, String capType, User user1, User user2)
+ throws Exception {
+ resourceDetailsReq.setResourceType(reqType);
+ createAtomicResource(resourceDetailsReq, user1);
+ resourceDetailsCap.setResourceType(capType);
+ createAtomicResource(resourceDetailsCap, user2);
+ }
+
+ private void createTwoAtomicResourcesByType(String reqType, String capType) throws Exception {
+ createTwoAtomicResourcesByType(reqType, capType, designerUser, designerUser);
+ }
+
+ @Test
+ public void associateInVF() throws Exception {
+
+ createTwoAtomicResourcesByType(ResourceTypeEnum.VFC.toString(), ResourceTypeEnum.VFC.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get("tosca.capabilities.Container");
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get("tosca.capabilities.Container");
+
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ requirementDef.setFromNode(riReq.getUniqueId());
+ requirementDef.setToNode(riCap.getUniqueId());
+
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair();
+ pair.setRequirementOwnerId(riReq.getUniqueId());
+ pair.setCapabilityOwnerId(riCap.getUniqueId());
+ pair.setRequirement("host");
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType("tosca.capabilities.Container");
+ pair.setRelationships(relationship);
+ pair.setCapabilityUid(capList.get(0).getUniqueId());
+ pair.setRequirementUid(reqList.get(0).getUniqueId());
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<>();
+ relationships.add(pair);
+ requirementDef.setRelationships(relationships);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(designerUser,
+ resourceDetailsVF);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+
+ List<RequirementDefinition> list = capReqDef.getRequirements().get("tosca.capabilities.Container");
+ assertEquals("Check requirement", null, list);
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, dissociateInstances.getErrorCode().intValue());
+
+ getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(designerUser, resourceDetailsVF);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+
+ list = capReqDef.getRequirements().get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, list.size());
+ }
+
+ //////////////////////////////// Q A //////////////////////////////
+ private boolean checkRealtionship(String fromNode, String toNode, String resourceUniqueId) throws Exception {
+ List<RequirementCapabilityRelDef> componentInstancesRelations = getComponentInstancesRelations(
+ resourceUniqueId);
+ RequirementCapabilityRelDef requirementCapabilityRelDef = componentInstancesRelations.get(0);
+ boolean fromNodeCheck = requirementCapabilityRelDef.getFromNode().equals(fromNode);
+ boolean toNodeCheck = requirementCapabilityRelDef.getToNode().equals(toNode);
+
+ return fromNodeCheck && toNodeCheck;
+ }
+
+ private List<RequirementCapabilityRelDef> getComponentInstancesRelations(String resourceUniqueId)
+ throws ClientProtocolException, IOException {
+ Resource resource = getVfAsResourceObject(resourceUniqueId);
+ List<RequirementCapabilityRelDef> componenRelationInstances = resource.getComponentInstancesRelations();
+
+ return componenRelationInstances;
+ }
+
+ private Resource getVfAsResourceObject(String resourceUniqueId) throws ClientProtocolException, IOException {
+ RestResponse getResource = ResourceRestUtils.getResource(resourceUniqueId);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ return resource;
+ }
+
+ private List<ComponentInstance> getComponentInstancesList(String resourceUniqueId) throws Exception {
+ Resource resource = getVfAsResourceObject(resourceUniqueId);
+ List<ComponentInstance> componentInstances = resource.getComponentInstances();
+ return componentInstances;
+ }
+
+ @Test
+ public void associateCpToCpTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.CP.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ requirementsBeforeAssociate.remove("tosca.capabilities.Container");
+ assertTrue(capReqDef.getRequirements().equals(requirementsBeforeAssociate));
+
+ List<CapabilityDefinition> list = capabilitiesBeforeAssociate.get("tosca.capabilities.Container");
+ for (CapabilityDefinition cap : list) {
+ cap.setMinOccurrences("0");
+ }
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+ }
+
+ private CapReqDef getResourceReqCap(ResourceReqDetails res) throws IOException {
+ RestResponse getResourceBeforeAssociate = ComponentRestUtils.getComponentRequirmentsCapabilities(designerUser,
+ resourceDetailsVF);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceBeforeAssociate.getResponse(), CapReqDef.class);
+ return capReqDef;
+ }
+
+ private CapReqDef getResourceReqCap() throws IOException {
+ return getResourceReqCap(resourceDetailsVF);
+ }
+
+ @Test
+ public void associateCpToVLTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ requirementsBeforeAssociate.remove("tosca.capabilities.Container");
+ assertTrue(capReqDef.getRequirements().equals(requirementsBeforeAssociate));
+
+ List<CapabilityDefinition> list = capabilitiesBeforeAssociate.get("tosca.capabilities.Container");
+ for (CapabilityDefinition cap : list) {
+ cap.setMinOccurrences("0");
+ }
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+
+ }
+
+ // Error handling
+ // ELLA - more informative error
+ @Test
+ public void associateCpToVlInVFCTest() throws Exception {
+ ResourceReqDetails vfcDetails = ElementFactory.getDefaultResourceByType("VFC100", NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, designerUser.getUserId(), ResourceTypeEnum.VFC.toString());
+ RestResponse createVfcResponse = ResourceRestUtils.createResource(vfcDetails, designerUser);
+ ResourceRestUtils.checkCreateResponse(createVfcResponse);
+
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ vfcDetails.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 400, associateInstances.getErrorCode().intValue());
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "SoftCompRouter 1",
+ // "MyCompute 2",
+ // "host"
+ // ]
+ }
+
+ // Error handling
+ @Test
+ public void associateCpToVfTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riCapInVfInstance = createComponentInstance(resourceDetailsCap, designerUser,
+ resourceDetailsVF);
+ ComponentInstance riReqInVfInstance = createComponentInstance(resourceDetailsReq, designerUser,
+ resourceDetailsVF);
+
+ ResourceReqDetails vfHigh = new ResourceReqDetails(resourceDetailsVF, "0.1");
+ vfHigh.setName("vfHigh");
+ vfHigh.setTags(new ArrayList<String>(Arrays.asList(vfHigh.getName())));
+ vfHigh.setResourceType(ResourceTypeEnum.VF.toString());
+ createResource(vfHigh, designerUser);
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq, designerUser, vfHigh);
+ LifecycleRestUtils.changeResourceState(resourceDetailsVF, designerUser, "0.1", LifeCycleStatesEnum.CHECKIN);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsVF, designerUser, vfHigh);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ associateInstances.getResponse());
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "SoftCompRouter 1",
+ // "VF100 2",
+ // "host"
+ // ]
+ }
+
+ // Error handling
+ @Test
+ public void associateVfcToVfcNotFoundTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.VFC.toString(), ResourceTypeEnum.VFC.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+ riCap.setUniqueId("123");
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 400, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_BAD_REQUEST.name(),
+ new ArrayList<String>(), associateInstances.getResponse());
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "SoftCompRouter 1",
+ // "012f6dcd-bcdf-4d9b-87be-ff1442b95831.5d265453-0b6a-4453-8f3d-57a253b88432.softcomprouter1",
+ // "host"
+ }
+
+ @Test
+ public void associateCpToDeletedVfcTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VFC.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse deleteResourceResponse = ResourceRestUtils.deleteResource(resourceDetailsCap.getUniqueId(),
+ designerUser.getUserId());
+ ResourceRestUtils.checkDeleteResponse(deleteResourceResponse);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ }
+
+ @Test
+ public void associateCpToDeletedVlTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse deleteResourceResponse = ResourceRestUtils.deleteResource(resourceDetailsCap.getUniqueId(),
+ designerUser.getUserId());
+ ResourceRestUtils.checkDeleteResponse(deleteResourceResponse);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ }
+
+ @Test
+ public void associateCpToDeletedCpTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.CP.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse deleteResourceResponse = ResourceRestUtils.deleteResource(resourceDetailsCap.getUniqueId(),
+ designerUser.getUserId());
+ ResourceRestUtils.checkDeleteResponse(deleteResourceResponse);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ }
+
+ // Error handling
+ @Test
+ public void associateCpToDeletedCpInstanceTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.CP.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse deleteComponentInstance = ComponentInstanceRestUtils.deleteComponentInstance(designerUser,
+ resourceDetailsVF.getUniqueId(), riReq.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ComponentInstanceRestUtils.checkDeleteResponse(deleteComponentInstance);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 400, associateInstances.getErrorCode().intValue());
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "7d6aca08-9321-4ea1-a781-c52c8214a30e.c0e63466-5283-44d8-adff-365c0885a6ba.softcomprouter1",
+ // "MyCompute 2",
+ // "host"
+ // ]
+ }
+
+ // Error handling
+ @Test
+ public void associateVfcToDeletedVFCInstanceTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.VFC.toString(), ResourceTypeEnum.VFC.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse deleteComponentInstance = ComponentInstanceRestUtils.deleteComponentInstance(designerUser,
+ resourceDetailsVF.getUniqueId(), riReq.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ComponentInstanceRestUtils.checkDeleteResponse(deleteComponentInstance);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 400, associateInstances.getErrorCode().intValue());
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "7d6aca08-9321-4ea1-a781-c52c8214a30e.c0e63466-5283-44d8-adff-365c0885a6ba.softcomprouter1",
+ // "MyCompute 2",
+ // "host"
+ // ]
+ }
+
+ @Test
+ public void associateWithDifferentOwnerOfVf() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2), resourceDetailsVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ associateInstances.getResponse());
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsAfterAssociate = capReqDef.getRequirements();
+
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+ assertTrue(requirementsAfterAssociate.equals(requirementsBeforeAssociate));
+ }
+
+ @Test
+ public void associateWithTester() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ ElementFactory.getDefaultUser(UserRoleEnum.TESTER), resourceDetailsVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ associateInstances.getResponse());
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsAfterAssociate = capReqDef.getRequirements();
+
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+ assertTrue(requirementsAfterAssociate.equals(requirementsBeforeAssociate));
+ }
+
+ // Error handling
+ @Test
+ public void associateCpToVLIntoVFNotFound() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ String uidNotFound = "123";
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ uidNotFound, ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 404, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(),
+ new ArrayList<String>(Arrays.asList("")), associateInstances.getResponse());
+
+ // {"serviceException":{"messageId":"SVC4063","text":"Error: Requested
+ // '%1' resource was not found.","variables":[""]}}}
+ }
+
+ // Error Handling
+ @Test
+ public void associateCpToVlWithMissingUid() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ requirementDef.setToNode("");
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 400, associateInstances.getErrorCode().intValue());
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(),
+ // new ArrayList<String>(), associateInstances.getResponse());
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "SoftCompRouter 1",
+ // "fd3a689b-fa1c-4105-933d-d1310e642f05.95bce626-ce73-413b-8c14-2388d1589d5c.softcomprouter1",
+ // "host"
+ // ]
+ }
+
+ @Test
+ public void associateInServiceWithUidOfVf() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", 404, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(),
+ new ArrayList<String>(Arrays.asList("")), associateInstances.getResponse());
+ }
+
+ @Test
+ public void associateCpToVl_DifferentOwners() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString(), designerUser,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2));
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ requirementsBeforeAssociate.remove("tosca.capabilities.Container");
+ assertTrue(capReqDef.getRequirements().equals(requirementsBeforeAssociate));
+
+ List<CapabilityDefinition> list = capabilitiesBeforeAssociate.get("tosca.capabilities.Container");
+ for (CapabilityDefinition cap : list) {
+ cap.setMinOccurrences("0");
+ }
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+ }
+
+ @Test(enabled = false)
+ public void associateToNotCheckedoutVf() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse changeResourceStateToCheckin = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckin);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ associateInstances.getResponse());
+
+ CapReqDef capReqDef = getResourceReqCap();
+ assertTrue(capReqDef.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDef.getCapabilities().equals(capabilitiesBeforeAssociate));
+
+ String firstUniqueId = resourceDetailsVF.getUniqueId();
+
+ // checkout
+
+ RestResponse changeResourceStateToCheckout = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckout);
+ String secondUniqueId = resourceDetailsVF.getUniqueId();
+
+ CapReqDef capReqDefAfterFirstCheckout = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterFirstCheckout = capReqDefAfterFirstCheckout
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsAfterFirstCheckout = capReqDefAfterFirstCheckout
+ .getRequirements();
+
+ requirementDef = setUidsOfInstancesAfterLifecycleStateChange(riReq, riCap, capReqDefBeforeAssociate);
+
+ RestResponse firstAssociateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ designerUser, resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, firstAssociateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ CapReqDef capReqDefAfterFirstAssociate = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterFirstAssociate = capReqDefAfterFirstAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsAfterFirstAssociate = capReqDefAfterFirstAssociate
+ .getRequirements();
+
+ requirementsAfterFirstCheckout.remove("tosca.capabilities.Container");
+ assertTrue(requirementsAfterFirstAssociate.equals(requirementsAfterFirstCheckout));
+ assertTrue(capabilitiesAfterFirstAssociate.equals(capabilitiesAfterFirstCheckout));
+
+ resourceDetailsVF.setUniqueId(firstUniqueId);
+ CapReqDef capReqDefOfFirstVersion = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesOfFirstVersion = capReqDefOfFirstVersion.getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsOfFirstVersion = capReqDefOfFirstVersion.getRequirements();
+
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+ assertTrue(requirementsBeforeAssociate.equals(requirementsOfFirstVersion));
+ assertTrue(capabilitiesBeforeAssociate.equals(capabilitiesOfFirstVersion));
+
+ // checkin-checkout
+ resourceDetailsVF.setUniqueId(secondUniqueId);
+ RestResponse changeResourceStateToCheckin2 = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckin2);
+ RestResponse changeResourceStateToCheckout2 = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckout2);
+
+ List<RequirementCapabilityRelDef> componentInstancesRelations = getComponentInstancesRelations(
+ resourceDetailsVF.getUniqueId());
+ assertFalse(componentInstancesRelations.isEmpty());
+ assertEquals(1, componentInstancesRelations.size());
+ List<ComponentInstance> componentInstancesList = getComponentInstancesList(resourceDetailsVF.getUniqueId());
+ for (ComponentInstance comp : componentInstancesList) {
+ String instanceUid = comp.getUniqueId();
+ assertTrue(checkNodesInRelations(instanceUid, componentInstancesRelations.get(0)));
+ }
+ assertEquals(2, componentInstancesList.size());
+
+ }
+
+ private RequirementCapabilityRelDef setUidsOfInstancesAfterLifecycleStateChange(ComponentInstance riReq,
+ ComponentInstance riCap, CapReqDef capReqDefBeforeAssociate)
+ throws ClientProtocolException, IOException, Exception {
+ RequirementCapabilityRelDef requirementDef;
+ // RestResponse getResourceResponse =
+ // ResourceRestUtils.getResource(resourceDetailsVF.getUniqueId());
+ // Resource resource_0_2 =
+ // ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(),
+ // Resource.class);
+ // List<ComponentInstance> componentInstances =
+ // resource_0_2.getComponentInstances();
+ List<ComponentInstance> componentInstances = getComponentInstancesList(resourceDetailsVF.getUniqueId());
+
+ for (ComponentInstance comp : componentInstances) {
+ if (comp.getName().equals(riReq.getName())) {
+ riReq.setUniqueId(comp.getUniqueId());
+ } else if (comp.getName().equals(riCap.getName())) {
+ riCap.setUniqueId(comp.getUniqueId());
+ }
+ }
+ requirementDef = setRelationshipBetweenInstances(riReq, riCap, capReqDefBeforeAssociate);
+ return requirementDef;
+ }
+
+ private boolean checkNodesInRelations(String instanceUid, RequirementCapabilityRelDef relation) {
+ if (relation.getToNode().equals(instanceUid)) {
+ return true;
+ } else if (relation.getFromNode().equals(instanceUid)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Test
+ public void associateOneOfTwoCPsToVl_ThenDiscocciate() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+ ResourceReqDetails secondResourceDetailsReq = new ResourceReqDetails(resourceDetailsReq, "0.1");
+ secondResourceDetailsReq.setName("secondCP");
+ secondResourceDetailsReq.setTags(Arrays.asList(secondResourceDetailsReq.getName()));
+ createAtomicResource(secondResourceDetailsReq, designerUser);
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riReq2 = createComponentInstance(secondResourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ List<RequirementDefinition> expectedList = requirementsBeforeAssociate.get("tosca.capabilities.Container");
+ for (RequirementDefinition req : expectedList) {
+ if (req.getOwnerName().equals(riReq2.getName())) {
+ expectedList = new ArrayList<RequirementDefinition>(Arrays.asList(req));
+ break;
+ }
+ }
+ requirementsBeforeAssociate.put("tosca.capabilities.Container", expectedList);
+ assertTrue(capReqDef.getRequirements().equals(requirementsBeforeAssociate));
+
+ List<CapabilityDefinition> list = capabilitiesBeforeAssociate.get("tosca.capabilities.Container");
+ for (CapabilityDefinition cap : list) {
+ cap.setMinOccurrences("0");
+ }
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+
+ // second relationship
+
+ RequirementCapabilityRelDef secondRequirementDef = setRelationshipBetweenInstances(riReq2, riCap,
+ capReqDefBeforeAssociate);
+ RestResponse secondAssociateInstances = ComponentInstanceRestUtils.associateInstances(secondRequirementDef,
+ designerUser, resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, secondAssociateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(secondRequirementDef.getFromNode(), secondRequirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ CapReqDef capReqDefAfterSecondAssociation = getResourceReqCap();
+
+ requirementsBeforeAssociate.remove("tosca.capabilities.Container");
+ assertTrue(capReqDefAfterSecondAssociation.getRequirements().equals(requirementsBeforeAssociate));
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterSecondAssociate = capReqDefAfterSecondAssociation
+ .getCapabilities();
+ assertTrue(capabilitiesAfterSecondAssociate.equals(capabilitiesBeforeAssociate));
+
+ // dissociate
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(secondRequirementDef,
+ designerUser, resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ CapReqDef capReqDefAfterDissociation = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterDissociate = capReqDefAfterDissociation
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsAfterDissociate = capReqDefAfterDissociation
+ .getRequirements();
+
+ assertTrue(capabilitiesAfterDissociate.equals(capReqDef.getCapabilities()));
+ requirementsBeforeAssociate.put("tosca.capabilities.Container", expectedList);
+ assertTrue(requirementsAfterDissociate.equals(requirementsBeforeAssociate));
+ }
+
+ @Test
+ public void associateNotCompitableCapAndReq() throws Exception {
+ resourceDetailsReq = ElementFactory.getDefaultResourceByType("Database", NormativeTypesEnum.DATABASE,
+ ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, designerUser.getUserId(), ResourceTypeEnum.CP.toString()); // resourceType
+ // =
+ // VFC
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ assertTrue(requirementDef.getRelationships().size() == 1);
+ String requirement = requirementDef.getRelationships().get(0).getRequirement();
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 404, associateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_MATCH_NOT_FOUND.name(),
+ new ArrayList<String>(Arrays.asList(riReq.getName(), riCap.getName(), requirement)),
+ associateInstances.getResponse());
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsAfterAssociate = capReqDef.getRequirements();
+
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+ assertTrue(requirementsAfterAssociate.equals(requirementsBeforeAssociate));
+
+ }
+
+ @Test
+ public void disassociateCpAndCpTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.CP.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+ }
+
+ @Test
+ public void disassociateCpAndVfcTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VFC.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+ }
+
+ @Test
+ public void disassociateCpAndVLTest() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+ }
+
+ // Error handliing
+ // in the error should we get the unique id of instances instead of names
+ @Test
+ public void disassociateNotFoundAssociation() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+ String requirementName = requirementDef.getRelationships().get(0).getRequirement();
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 404, dissociateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND.name(),
+ new ArrayList<String>(Arrays.asList(riReq.getName(), riCap.getName(), requirementName)),
+ dissociateInstances.getResponse());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+ }
+
+ // Error handliing
+ @Test
+ public void disassociateRelationInVfNotFound() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ String uidNotFound = "123";
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ uidNotFound, ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 404, dissociateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(),
+ new ArrayList<String>(Arrays.asList(uidNotFound)), dissociateInstances.getResponse());
+
+ // "serviceException": {
+ // "messageId": "SVC4063",
+ // "text": "Error: Requested \u0027%1\u0027 resource was not found.",
+ // "variables": [
+ // ""
+ // ]
+ }
+
+ @Test
+ public void disassociateWithDifferentDesigner() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2), resourceDetailsVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, dissociateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ dissociateInstances.getResponse());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+
+ }
+
+ @Test
+ public void disassociateWithTester() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ ElementFactory.getDefaultUser(UserRoleEnum.TESTER), resourceDetailsVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, dissociateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ dissociateInstances.getResponse());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertNotNull("Requierment is null after disassociate with tester", listOfRequierments);
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+ }
+
+ @Test
+ public void disassociateServiceWithUidOfVF() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VFC.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", 404, dissociateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(),
+ new ArrayList<String>(Arrays.asList("")), dissociateInstances.getResponse());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertTrue(listOfRequierments == null);
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).size() != 0);
+ }
+
+ @Test
+ public void disassociateWithEmptyVfUid() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ "", ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 404, dissociateInstances.getErrorCode().intValue());
+
+ CapReqDef capReqDef = getResourceReqCap();
+
+ requirementsBeforeAssociate.remove("tosca.capabilities.Container");
+ assertTrue(capReqDef.getRequirements().equals(requirementsBeforeAssociate));
+
+ List<CapabilityDefinition> list = capabilitiesBeforeAssociate.get("tosca.capabilities.Container");
+ for (CapabilityDefinition cap : list) {
+ cap.setMinOccurrences("0");
+ }
+
+ Map<String, List<CapabilityDefinition>> capabilitiesAfterAssociate = capReqDef.getCapabilities();
+ assertTrue(capabilitiesAfterAssociate.equals(capabilitiesBeforeAssociate));
+ }
+
+ @Test
+ public void disassociateOneComponentDeleted() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+
+ RestResponse deleteResourceResponse = ResourceRestUtils.deleteResource(resourceDetailsCap.getUniqueId(),
+ designerUser.getUserId());
+ ResourceRestUtils.checkDeleteResponse(deleteResourceResponse);
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+
+ List<RequirementDefinition> listOfRequierments = capReqDefAfterDissociate.getRequirements()
+ .get("tosca.capabilities.Container");
+ assertEquals("Check requirement", 1, listOfRequierments.size());
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+ }
+
+ @Test
+ public void disassociateNotCheckedoutVf() throws Exception {
+ createTwoAtomicResourcesByType(ResourceTypeEnum.CP.toString(), ResourceTypeEnum.VL.toString());
+
+ ComponentInstance riReq = createComponentInstance(resourceDetailsReq);
+ ComponentInstance riCap = createComponentInstance(resourceDetailsCap);
+
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap();
+ Map<String, List<CapabilityDefinition>> capabilitiesBeforeAssociate = capReqDefBeforeAssociate
+ .getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementsBeforeAssociate = capReqDefBeforeAssociate
+ .getRequirements();
+
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(riReq, riCap,
+ capReqDefBeforeAssociate);
+
+ RestResponse changeResourceStateToCheckin = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckin);
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", 409, dissociateInstances.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ dissociateInstances.getResponse());
+
+ CapReqDef capReqDefAfterDissociate = getResourceReqCap();
+ assertTrue(capReqDefAfterDissociate.getRequirements().equals(requirementsBeforeAssociate));
+ assertTrue(capReqDefAfterDissociate.getCapabilities().equals(capabilitiesBeforeAssociate));
+
+ RestResponse changeResourceStateToCheckout = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckout);
+
+ requirementDef = setUidsOfInstancesAfterLifecycleStateChange(riReq, riCap, capReqDefBeforeAssociate);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, designerUser,
+ resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF.getUniqueId()));
+
+ RestResponse secondDisociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ designerUser, resourceDetailsVF.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, secondDisociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ RestResponse changeResourceStateToCheckout2 = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckout2);
+ RestResponse changeResourceStateToCheckout3 = LifecycleRestUtils.changeResourceState(resourceDetailsVF,
+ designerUser, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.checkSuccess(changeResourceStateToCheckout3);
+
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF.getUniqueId()).isEmpty());
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CreateResourceApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CreateResourceApiTest.java
new file mode 100644
index 0000000000..a9ed54ef15
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/CreateResourceApiTest.java
@@ -0,0 +1,2199 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ResourceValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+/**
+ * @author yshlosberg
+ *
+ */
+public class CreateResourceApiTest extends ComponentBaseTest {
+
+ private static Logger log = LoggerFactory.getLogger(CreateResourceApiTest.class.getName());
+
+ String contentTypeHeaderData = "application/json";
+ String acceptHeaderDate = "application/json";
+ String resourceVersion = "0.1";
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CreateResourceApiTest() {
+ super(name, CreateResourceApiTest.class.getName());
+ }
+
+ @Test
+ public void createResourceTest() throws Exception {
+
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // ResourceReqDetails resourceDetails = new
+ // ResourceReqDetails(resourceName, description, resourceTags, category,
+ // derivedFrom, vendorName, vendorRelease, contactId, icon);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ String resourceName = resourceDetails.getName();
+ resourceDetails.setTags(Arrays.asList(resourceName, resourceName, resourceName, resourceName, "tag2", "tag2"));
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @Test
+ public void createResourceNonDefaultResourceTypeTest() throws Exception {
+
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ String resourceType = ResourceTypeEnum.CP.toString();
+ resourceDetails.setResourceType(resourceType);
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ resourceRespJavaObject.setResourceType(resourceType);
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @Test
+ public void createResourceTest_costAndLicenseType() throws Exception {
+
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ String resourceVersion = "0.1";
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+
+ }
+
+ // ////Benny
+ @Test
+ public void createResourceTest_CostIsMissing() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ // resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ String resourceVersion = "0.1";
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+ }
+
+ @Test
+ public void createResourceTest_LicenseTypeMissing() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ // resourceDetails.setLicenseType("User");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ String resourceVersion = "0.1";
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+ }
+
+ @Test
+ public void createResourceTest_LicenseType_Installation() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("99999.999");
+ resourceDetails.setLicenseType("Installation");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ String resourceVersion = "0.1";
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+ }
+
+ @Test
+ public void createResourceTest_LicenseType_CPU() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("0.0");
+ resourceDetails.setLicenseType("CPU");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ // validate response
+ String resourceVersion = "0.1";
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails,
+ resourceDetails.getUniqueId());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+ }
+
+ @Test
+ public void createResourceTest_LicenseType_Uppercase() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("0.0");
+ resourceDetails.setLicenseType("INSTALLATION");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request", createResponse.getResponseMessage());
+ }
+
+ @Test
+ public void createResourceTest_LicenseType_Invalid() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("0.0");
+ resourceDetails.setLicenseType("CPUUU");
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request", createResponse.getResponseMessage());
+ }
+
+ @Test
+ public void createResourceTest_CostValidation_noNumeric() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+ resourceDetails.setCost("12355.34b");
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ }
+
+ @Test
+ public void createResourceTest_CostValidation_valueLength() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+
+ // Adding invalid cost
+ resourceDetails.setCost("12355.3434");
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+ }
+
+ @Test
+ public void createResourceTest_CostValidation_PriceLimitations() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+
+ // Adding invalid cost
+ RestResponse createResponse;
+ // create resource
+
+ resourceDetails.setCost("000000.000");
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ /*
+ * resourceDetails.setCost("0550.457"); createResponse =
+ * resourceUtils.createResource(resourceDetails, sdncModifierDetails);
+ * assertNotNull("check response object is not null after create resource"
+ * , createResponse);
+ * assertNotNull("check error code exists in response after create resource"
+ * , createResponse.getErrorCode());
+ * assertEquals("Check response code after create resource", 400,
+ * createResponse.getErrorCode().intValue());
+ * assertEquals("Check response code after create resource",
+ * "Bad Request", createResponse.getResponseMessage().toString());
+ */
+
+ resourceDetails.setCost("1");
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ resourceDetails.setCost("123555.340");
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ resourceDetails.setCost("123.4570");
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ resourceDetails.setCost("123555.30");
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ resourceDetails.setCost("123.5550");
+ createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request",
+ createResponse.getResponseMessage().toString());
+
+ }
+
+ @Test
+ public void createResourceTest_CostIsNull() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+ resourceDetails.setCost("");
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request", createResponse.getResponseMessage());
+
+ }
+
+ @Test
+ public void createResourceTest_LicenseIsNull() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // set resource details
+ String resourceName = "CISCO4572";
+ String description = "description";
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add(resourceName);
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ // Adding cost and licenseType
+ resourceDetails.setCost("12355.345");
+ resourceDetails.setLicenseType("User");
+ resourceDetails.setLicenseType("");
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 400, createResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create resource", "Bad Request", createResponse.getResponseMessage());
+
+ }
+
+ @Test
+ public void createResourceTest_uri_methods() throws Exception {
+
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ ResourceReqDetails resourceDetails = createRandomResource();
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resourceDetails);
+ log.debug(userBodyJson);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ RestResponse createResourceResponse2 = http.httpSendByMethod(url, "PUT", userBodyJson, headersMap);
+
+ // validate response
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.NOT_ALLOWED.name());
+
+ assertNotNull("check response object is not null after create resource", createResourceResponse2);
+ assertNotNull("check error code exists in response after create resource",
+ createResourceResponse2.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(),
+ createResourceResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ createResourceResponse2.getResponse());
+
+ }
+
+ private ResourceReqDetails createRandomResource() {
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ return resourceDetails;
+ }
+
+ @Test
+ public void createResource_role_tester() throws Exception {
+
+ // init TESTER user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+
+ ResourceReqDetails resourceDetails2 = createRandomResource();
+
+ // create resource
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.TESTER.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.TESTER.getUserName());
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ // TODO DE171450(to check)
+ @Test
+ public void createResource_role_DESIGNER() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ResourceReqDetails resourceDetails = createRandomResource();
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ // validate response
+ assertNotNull("check response object is not null after create resource", restResponse);
+ assertNotNull("check error code exists in response after create resource", restResponse.getErrorCode());
+ assertEquals(
+ "Check response code after create resource, response message is: " + restResponse.getResponseMessage(),
+ 201, restResponse.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResource_missing_header() throws Exception {
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ ResourceReqDetails resourceDetails = createRandomResource();
+
+ // set null in userId header
+ sdncModifierDetails.setUserId(null);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_INFORMATION.name(), variables,
+ restResponse2.getResponse());
+
+ // //validate audit
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // resourceUtils.constructFieldsForAuditValidation(resourceDetails,resourceVersion);
+ //
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setModifierUid("null null");
+ // expectedResourceAuditJavaObject.setModifierName("null null");
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setCurrState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrVersion("");
+ // expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ //
+ // String auditDesc =
+ // AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ // expectedResourceAuditJavaObject.setDesc(auditDesc);
+ //
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction);
+ // TODO: yshlosberg enable back
+
+ }
+
+ @Test
+ public void createResource_existing_resource() throws Exception {
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // ResourceReqDetails resourceDetails = createRandomResource();
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+
+ // create resource
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ assertNotNull("check response object is not null after create resource", restResponse);
+ assertNotNull("check error code exists in response after create resource", restResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+
+ // set resource details
+ ResourceReqDetails resourceDetails2 = ElementFactory.getDefaultResource();
+
+ // clean ES DB
+ DbUtils.cleanAllAudits();
+
+ // create resource
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_NAME_ALREADY_EXIST.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", resourceDetails2.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_ALREADY_EXIST.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_category() throws Exception {
+
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ ;
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ category = null;
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_CATEGORY.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CATEGORY.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_empty_category() throws Exception {
+
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ category = "";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_CATEGORY.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CATEGORY.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_tags() throws Exception {
+
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ ;
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_TAGS.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_TAGS.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ // TODO DE171450(to check)
+ @Test
+ public void createResourceTest_with_multiple_tags() throws Exception {
+
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setTags(Arrays.asList(resourceDetails.getName(), "tag2"));
+
+ // create resource
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ assertNotNull("check response object is not null after create resource", restResponse);
+ assertNotNull("check error code exists in response after create resource", restResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createResourceTest_empty_tag() throws Exception {
+
+ // init ADMIN user
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add("");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_FIELD_FORMAT.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse);
+ assertNotNull("check error code exists in response after create resource", restResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), restResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", "tag");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FIELD_FORMAT.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_with_empty_vendorName() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ vendorName = "";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_VENDOR_NAME.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_NAME.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_vendorName() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ vendorName = null;
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+ assertNotNull("check response object is not null after create resource", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create resource", 400, restResponse2.getErrorCode().intValue());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_NAME.name(), variables,
+ restResponse2.getResponse());
+
+ }
+
+ @Test
+ public void createResourceTest_with_empty_vendorRelease() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ vendorRelease = "";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_VENDOR_RELEASE.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_RELEASE.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_vendorRelease() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ vendorRelease = null;
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_VENDOR_RELEASE.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_RELEASE.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_with_empty_contactId() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ contactId = "";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_CONTACT.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CONTACT.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_contactId() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ contactId = null;
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_CONTACT.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CONTACT.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_with_empty_icon() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ icon = "";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_ICON.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_ICON.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_icon() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ icon = null;
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_ICON.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_ICON.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_with_empty_description() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ description = "";
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createResourceTest_without_description() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // set resource details
+ String resourceName = "CISCO4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ // set resource details
+ description = null;
+
+ ResourceReqDetails resourceDetails2 = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ // create resource
+
+ RestResponse restResponse2 = ResourceRestUtils.createResource(resourceDetails2, sdncModifierDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name());
+
+ assertNotNull("check response object is not null after create resouce", restResponse2);
+ assertNotNull("check error code exists in response after create resource", restResponse2.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse2.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), variables,
+ restResponse2.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails2, resourceVersion);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createAndGetResourceByNameAndVersion() throws Exception {
+
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ ResourceReqDetails resourceDetailsComp = ElementFactory.getDefaultResource("testresourceComp",
+ NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, sdncModifierDetails.getUserId());
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetailsComp, sdncModifierDetails);
+ // validate response
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ String resourceVersion = "0.1";
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetailsComp,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResourceByNameAndVersion(
+ sdncModifierDetails.getUserId(), resourceDetailsComp.getName(), resourceDetailsComp.getVersion());
+ assertEquals("Check response code after delete resource", 200, resourceGetResponse.getErrorCode().intValue());
+ // Resource resource =
+ // ResourceRestUtils.parseResourceFromListResp(resourceGetResponse);
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+ // resourceDetailsComp.setUniqueId(resource.getUniqueId());
+
+ }
+
+ @Test
+ public void createResourceResourceTypeNotExistsTest() throws Exception {
+
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ String resourceType = "NOT EXISTS";
+ resourceDetails.setResourceType(resourceType);
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_CONTENT.name());
+
+ assertNotNull("check response object is not null after create resouce", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), createResponse.getErrorCode());
+
+ List<String> variables = new ArrayList<>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), variables,
+ createResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resourceDetails, resourceVersion);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @Test
+ public void createResourceResourceTypeEmptyTest() throws Exception {
+
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ // String resourceType = "";
+ // resourceDetails.setResourceType(resourceType);
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // ErrorInfo errorInfo =
+ // ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_CONTENT.name());
+ //
+ // assertNotNull("check response object is not null after create
+ // resouce", createResponse);
+ // assertNotNull("check error code exists in response after create
+ // resource", createResponse.getErrorCode());
+ // assertEquals("Check response code after create service",
+ // errorInfo.getCode(), createResponse.getErrorCode());
+ //
+ // List<String> variables = new ArrayList<>();
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(),
+ // variables, createResponse.getResponse());
+ //
+ // // validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // Convertor.constructFieldsForAuditValidation(resourceDetails,
+ // resourceVersion);
+ // String auditAction = "Create";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState("");
+ // expectedResourceAuditJavaObject.setCurrVersion("");
+ // expectedResourceAuditJavaObject.setResourceName("");
+ // expectedResourceAuditJavaObject.setModifierUid(ElementFactory.getDefaultUser(UserRoleEnum.ADMIN).getUserId());
+ // expectedResourceAuditJavaObject.setModifierName(ElementFactory.getDefaultUser(UserRoleEnum.ADMIN).getFullName());
+ // expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ //
+ // String auditDesc =
+ // AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ // expectedResourceAuditJavaObject.setDesc(auditDesc);
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ }
+
+ @Test
+ public void checkInvariantUuidIsImmutable() throws Exception {
+ // choose the user to create resource
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ String invariantUuidDefinedByUser = "!!!!!!!!!!!!!!!!!!!!!!!!";
+ resourceDetails.setInvariantUUID(invariantUuidDefinedByUser);
+ String resourceName = resourceDetails.getName();
+ resourceDetails.setTags(Arrays.asList(resourceName, resourceName, resourceName, resourceName, "tag2", "tag2"));
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncUserDetails);
+ BaseRestUtils.checkStatusCode(createResponse, "create request failed", false, 201);
+ // validate response
+ assertNotNull("check response object is not null after create resource", createResponse);
+ assertNotNull("check error code exists in response after create resource", createResponse.getErrorCode());
+ assertEquals("Check response code after create resource", 201, createResponse.getErrorCode().intValue());
+
+ Resource resourceCreation = ResponseParser.convertResourceResponseToJavaObject(createResponse.getResponse());
+ String invariantUUIDcreation = resourceCreation.getInvariantUUID();
+ // validate response
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ ResourceValidationUtils.validateResp(createResponse, resourceRespJavaObject);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncUserDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(resourceGetResponse);
+ Resource resourceGetting = ResponseParser
+ .convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+ String invariantUUIDgetting = resourceGetting.getInvariantUUID();
+ assertEquals(invariantUUIDcreation, invariantUUIDgetting);
+
+ // Update resource with new invariant UUID
+ RestResponse restResponseUpdate = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncUserDetails,
+ resourceDetails.getUniqueId());
+ BaseRestUtils.checkSuccess(restResponseUpdate);
+ Resource updatedResource = ResponseParser.convertResourceResponseToJavaObject(restResponseUpdate.getResponse());
+ String invariantUUIDupdating = updatedResource.getInvariantUUID();
+ assertEquals(invariantUUIDcreation, invariantUUIDupdating);
+
+ // Do checkin
+ RestResponse restResponseCheckin = LifecycleRestUtils.changeResourceState(resourceDetails, sdncUserDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ BaseRestUtils.checkSuccess(restResponseCheckin);
+ Resource checkinResource = ResponseParser
+ .convertResourceResponseToJavaObject(restResponseCheckin.getResponse());
+ String invariantUUIDcheckin = checkinResource.getInvariantUUID();
+ String version = checkinResource.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcheckin);
+ assertEquals(version, "0.1");
+
+ // Do checkout
+ RestResponse restResponseCheckout = LifecycleRestUtils.changeResourceState(resourceDetails, sdncUserDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ BaseRestUtils.checkSuccess(restResponseCheckout);
+ Resource ResourceResource = ResponseParser
+ .convertResourceResponseToJavaObject(restResponseCheckout.getResponse());
+ String invariantUUIDcheckout = ResourceResource.getInvariantUUID();
+ version = ResourceResource.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcheckout);
+ assertEquals(version, "0.2");
+
+ // do certification request
+ RestResponse restResponseCertificationRequest = LifecycleRestUtils.changeResourceState(resourceDetails,
+ sdncUserDetails, resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ BaseRestUtils.checkSuccess(restResponseCertificationRequest);
+ Resource certificationRequestResource = ResponseParser
+ .convertResourceResponseToJavaObject(restResponseCertificationRequest.getResponse());
+ String invariantUUIDcertificationRequest = certificationRequestResource.getInvariantUUID();
+ version = certificationRequestResource.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcertificationRequest);
+ assertEquals(version, "0.2");
+
+ // start certification
+ RestResponse restResponseStartCertification = LifecycleRestUtils.changeResourceState(resourceDetails,
+ sdncUserDetails, resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ BaseRestUtils.checkSuccess(restResponseStartCertification);
+ Resource startCertificationRequestResource = ResponseParser
+ .convertResourceResponseToJavaObject(restResponseStartCertification.getResponse());
+ String invariantUUIDStartCertification = startCertificationRequestResource.getInvariantUUID();
+ version = startCertificationRequestResource.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDStartCertification);
+ assertEquals(version, "0.2");
+
+ // certify
+ RestResponse restResponseCertify = LifecycleRestUtils.changeResourceState(resourceDetails, sdncUserDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ BaseRestUtils.checkSuccess(restResponseCertify);
+ Resource certifyResource = ResponseParser
+ .convertResourceResponseToJavaObject(restResponseCertify.getResponse());
+ String invariantUUIDcertify = certifyResource.getInvariantUUID();
+ version = certifyResource.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcertify);
+ assertEquals(version, "1.0");
+
+ }
+
+ // US672129 BENNY
+
+ private void getResourceValidateInvariantUuid(String resourceUniqueId, String invariantUUIDcreation)
+ throws Exception {
+ RestResponse getResource = ResourceRestUtils.getResource(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER),
+ resourceUniqueId);
+ BaseRestUtils.checkSuccess(getResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ assertEquals(invariantUUIDcreation, resource.getInvariantUUID());
+ }
+
+ @Test
+ public void resourceInvariantUuid() throws Exception {
+
+ User designerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ User testerUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResourceByType("VF200", NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, designerUser.getUserId(), ResourceTypeEnum.VF.toString());
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService("newtestservice1",
+ ServiceCategoriesEnum.MOBILITY, designerUser.getUserId());
+
+ // ResourceReqDetails resourceDetails =
+ // ElementFactory.getDefaultResource();
+ resourceDetails.setInvariantUUID("kokomoko");
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, designerUser);
+ assertEquals("Check response code after create resource", BaseRestUtils.STATUS_CODE_CREATED,
+ createResponse.getErrorCode().intValue());
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResponse.getResponse(), Resource.class);
+ String invariantUUIDcreation = resource.getInvariantUUID(); // generated
+ // when the
+ // component
+ // is
+ // created
+ // and never
+ // changed
+ // get resource and verify InvariantUuid is not changed
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+
+ // Update resource with new invariant UUID
+ resourceDetails.setInvariantUUID("1234567890");
+ RestResponse updateResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, designerUser,
+ resourceDetails.getUniqueId());
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS,
+ updateResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+
+ // checkIn resource
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+
+ // checkIn resource
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+ // certification request
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+ // start certification
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUser,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+ // certify
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUser, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getResourceValidateInvariantUuid(resource.getUniqueId(), invariantUUIDcreation);
+ // update resource
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ resourceDetails.setDescription("updatedDescription");
+ resourceDetails.setVendorRelease("1.2.3.4");
+ updateResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, designerUser,
+ resourceDetails.getUniqueId());
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, updateResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // certification request
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // checkout resource
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // certification request
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+ // start certification
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUser,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // cancel certification
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUser,
+ LifeCycleStatesEnum.CANCELCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // start certification
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUser,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // failure
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, testerUser,
+ LifeCycleStatesEnum.FAILCERTIFICATION);
+ assertEquals(STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // upload artifact
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ ArtifactReqDetails artifactDetails = ElementFactory.getDefaultArtifact();
+ ArtifactRestUtils.addInformationalArtifactToResource(artifactDetails, designerUser,
+ resourceDetails.getUniqueId());
+ assertEquals(STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // checkIn resource
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, designerUser,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ // create instance
+ RestResponse createServiceResponse = ServiceRestUtils.createService(serviceDetails, designerUser);
+ ResourceRestUtils.checkCreateResponse(createServiceResponse);
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetails);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, designerUser, serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_CREATED,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getResourceValidateInvariantUuid(resourceDetails.getUniqueId(), invariantUUIDcreation);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetAllResourceVersions.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetAllResourceVersions.java
new file mode 100644
index 0000000000..a3372098a0
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetAllResourceVersions.java
@@ -0,0 +1,580 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GetAllResourceVersions extends ComponentBaseTest {
+
+ private static Logger logger = LoggerFactory.getLogger(GetAllResourceVersions.class.getName());
+ protected User designerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ protected User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ protected ResourceReqDetails resourceDetails;
+
+ public static TestName name = new TestName();
+
+ public GetAllResourceVersions() {
+ super(name, GetAllResourceVersions.class.getName());
+
+ }
+
+ //// NEW
+
+ protected void deleteAllVersionOfResource() throws Exception {
+ RestResponse response = null;
+
+ String[] versions = { "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "2.0",
+ "2.1", "2.2", "2.3", "2.4", "2.5", "3.0", "4.0", "4.1" };
+
+ for (String version : versions) {
+
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(designerDetails,
+ resourceDetails.getName().toUpperCase(), version);
+ AssertJUnit.assertTrue("delete request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 204 || response.getErrorCode() == 404);
+
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(designerDetails, resourceDetails.getName(),
+ version);
+ AssertJUnit.assertTrue("delete request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 204 || response.getErrorCode() == 404);
+
+ }
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+ resourceDetails = defineResourse();
+ deleteAllVersionOfResource();
+
+ }
+
+ @AfterMethod
+ public void endOfTests() throws Exception {
+ deleteAllVersionOfResource();
+ }
+
+ protected ResourceReqDetails defineResourse() {
+ String resourceName = "cisco4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ // String category = ServiceCategoriesEnum.MOBILITY.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getCategory(),
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getSubCategory());
+
+ return resourceDetails;
+ }
+
+ @Test
+ public void getResourceAllVersions_version15() throws Exception {
+ // create resource
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ AssertJUnit.assertTrue("create request returned status:" + restResponse.getErrorCode(),
+ restResponse.getErrorCode() == 201);
+ String resourceName = resourceDetails.getName();
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+ for (int x = 0; x < 4; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ // change resource version to 1.5
+ for (int x = 0; x < 5; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ AssertJUnit.assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ protected RestResponse createResource(User sdncModifierDetails, ResourceReqDetails resourceDetails)
+ throws Exception {
+ // clean ES DB
+ DbUtils.cleanAllAudits();
+
+ // create resource
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ AssertJUnit.assertNotNull("check response object is not null after create resource", restResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create resource",
+ restResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create resource", 201,
+ restResponse.getErrorCode().intValue());
+
+ return restResponse;
+ }
+
+ @Test
+ public void getResourceAllVersions_version05() throws Exception {
+
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+
+ logger.debug("Changing resource life cycle ");
+ for (int x = 0; x < 4; x++) {
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ }
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_version01() throws Exception {
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_version25() throws Exception {
+
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ assertTrue("create request returned status:" + restResponse.getErrorCode(), restResponse.getErrorCode() == 201);
+ String resourceName = resourceDetails.getName();
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+ for (int x = 0; x < 4; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ // resource version 1.0
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ // change resource version to 1.5
+ for (int x = 0; x < 5; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ // resource version 2.0
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ // change resource version to 2.5
+ for (int x = 0; x < 5; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_ReadyForCertification_version05() throws Exception {
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ assertTrue("create request returned status:" + restResponse.getErrorCode(), restResponse.getErrorCode() == 201);
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ String resourceName = resourceDetails.getName();
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+ for (int x = 0; x < 4; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ }
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_CertifactionInProgress_version05() throws Exception {
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ assertTrue("create request returned status:" + restResponse.getErrorCode(), restResponse.getErrorCode() == 201);
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ String resourceName = resourceDetails.getName();
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+ for (int x = 0; x < 4; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ }
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_Certified_version10() throws Exception {
+
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ assertTrue("create request returned status:" + restResponse.getErrorCode(), restResponse.getErrorCode() == 201);
+ String resourceName = resourceDetails.getName();
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+ for (int x = 0; x < 4; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+
+ }
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_Certified_version20() throws Exception {
+
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+
+ // create resource
+ RestResponse restResponse = createResource(designerDetails, resourceDetails);
+ assertTrue("create request returned status:" + restResponse.getErrorCode(), restResponse.getErrorCode() == 201);
+ String resourceName = resourceDetails.getName();
+ // resourceUtils.addResourceMandatoryArtifacts(designerDetails,
+ // restResponse);
+
+ // change resource version to 0.5
+ RestResponse checkoutResource;
+ for (int x = 0; x < 4; x++) {
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ // get to version 1.0
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ // change resource version to 1.5
+ for (int x = 0; x < 4; x++) {
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, designerDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200,
+ checkoutResource.getErrorCode().intValue());
+ }
+
+ // get to version 1.0
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+ origVersionsMap.put(resourceDetails.getVersion(), resourceDetails.getUniqueId());
+
+ // validate get response
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails,
+ resourceDetails.getUniqueId());
+ Resource res = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void getResourceAllVersions_ResourceNotFound() throws Exception {
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(designerDetails, "123456789");
+ assertEquals("Check response code after checkout resource", 404, resourceGetResponse.getErrorCode().intValue());
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetResourceNotAbstractApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetResourceNotAbstractApiTest.java
new file mode 100644
index 0000000000..ccf6142538
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/GetResourceNotAbstractApiTest.java
@@ -0,0 +1,326 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.imports.ImportGenericResourceCITest;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class GetResourceNotAbstractApiTest extends ComponentBaseTest {
+
+ private static Logger logger = LoggerFactory.getLogger(ComponentBaseTest.class.getName());
+ protected static final int STATUS_CODE_GET_SUCCESS = 200;
+
+ protected Config config = Config.instance();
+ protected String contentTypeHeaderData = "application/json";
+ protected String acceptHeaderDate = "application/json";
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetResourceNotAbstractApiTest() {
+ super(name, GetResourceNotAbstractApiTest.class.getName());
+ }
+
+ @Test
+ public void getNotAbstractResourceList() throws Exception {
+
+ // remove all the not abstract resources
+ // Map<NormativeTypes, Boolean> originalState =
+ // ImportResourceCITest.removeAllNormativeTypeResources();
+
+ // import all the default not abstract resources
+ // ImportGenericResourceCITest.importAllNormativeTypesResources(UserRoleEnum.ADMIN);
+
+ // Get not abstract resources
+ RestResponse getResourceNotAbstarctResponse = getNotAbstractResources();
+ // Check that received 200.
+ assertEquals("Check response code after get abstract resources", STATUS_CODE_GET_SUCCESS,
+ getResourceNotAbstarctResponse.getErrorCode().intValue());
+ // Verify that all the resources not abstract
+ assertTrue("One or more resources are abstract", isAllResourcesNotAbstract(getResourceNotAbstarctResponse));
+ // Verify that all the resources are certified
+ assertTrue("Not all the resources are certified", isAllResourcesCertified(getResourceNotAbstarctResponse));
+
+ String objectStorageUid = "ObjectStorage";
+ String computeUid = "Compute";
+ String blockStorageUid = "BlockStorage";
+ String loadBalancerUid = "LoadBalancer";
+ // String portUid = "tosca.nodes.Network.Port";
+ String portUid = "Port";
+ String networkUid = "Network";
+ String databaseUid = "Database";
+
+ // Compare expected list of abstract resources to actual list of
+ // abstract resources.
+ List<String> expectedNotAbstractResourcesUniqueIdArray = new ArrayList<String>(Arrays.asList(computeUid,
+ databaseUid, objectStorageUid, blockStorageUid, loadBalancerUid, portUid, networkUid));
+
+ List<String> actualNotAbstarctResourcesUniqueIdArray = restResponseToListByHeader(
+ getResourceNotAbstarctResponse, "name");
+
+ // Collections.sort(actualNotAbstarctResourcesUniqueIdArray);
+ // Collections.sort(expectedNotAbstractResourcesUniqueIdArray);
+
+ List<String> toFind = new ArrayList<>();
+ toFind.add(objectStorageUid);
+ toFind.add(computeUid);
+ toFind.add(blockStorageUid);
+ toFind.add(loadBalancerUid);
+ toFind.add(portUid);
+
+ boolean removeAll = toFind.removeAll(actualNotAbstarctResourcesUniqueIdArray);
+ logger.debug("Cannot find resources {}", toFind.toString());
+
+ for (String expectedResource : expectedNotAbstractResourcesUniqueIdArray) {
+ if (false == actualNotAbstarctResourcesUniqueIdArray.contains(expectedResource)) {
+ // System.out.println("Not found abstract resource " +
+ // expectedResource);
+ }
+ }
+
+ assertTrue(
+ "Expected abstract resources list: " + expectedNotAbstractResourcesUniqueIdArray.toString()
+ + " Actual: " + actualNotAbstarctResourcesUniqueIdArray.toString(),
+ actualNotAbstarctResourcesUniqueIdArray.containsAll(expectedNotAbstractResourcesUniqueIdArray));
+
+ /*
+ * java.lang.AssertionError: Expected abstract resources list:
+ * [tosca.nodes.Compute, tosca.nodes.ObjectStorage,
+ * tosca.nodes.BlockStorage, tosca.nodes.LoadBalancer,
+ * tosca.nodes.Network.Port] Actual: [resourceforproperty216,
+ * tosca.nodes.Compute, tosca.nodes.Database, resourceforproperty217,
+ * resourceforproperty217, tosca.nodes.ObjectStorage,
+ * tosca.nodes.BlockStorage, tosca.nodes.LoadBalancer,
+ * tosca.nodes.network.Port, tosca.nodes.network.Network,
+ * resourceforproperty217, resourceforproperty217,
+ * resourceforproperty217, resourceforproperty217,
+ * resourceforproperty217, resourceforproperty217,
+ * resourceforproperty217, resourceforproperty217,
+ * resourceforproperty217, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317,
+ * resourceforproperty317, resourceforproperty317]
+ */
+
+ // Create resource (not certified)
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ String resourceName = "TestResource";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ String category = ServiceCategoriesEnum.MOBILITY.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.0";
+ String contactId = "Peter";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ // assertEquals("Check response code after create user", 201,
+ // restResponse.getErrorCode().intValue());
+
+ // Get not abstract resources
+ getResourceNotAbstarctResponse = getNotAbstractResources();
+ // Check that received 200.
+ assertEquals("Check response code after get abstract resources", STATUS_CODE_GET_SUCCESS,
+ getResourceNotAbstarctResponse.getErrorCode().intValue());
+ // Verify that all the resources not abstract
+ assertTrue("One or more resources are abstract", isAllResourcesNotAbstract(getResourceNotAbstarctResponse));
+ // Verify that all the resources are certified
+ assertTrue("Not all the resources are certified", isAllResourcesCertified(getResourceNotAbstarctResponse));
+
+ // Compare expected list of abstract resources to actual list of
+ // abstract resources.
+ // expectedNotAbstractResourcesUniqueIdArray = new
+ // ArrayList<String>(Arrays.asList("tosca.nodes.compute.1.0",
+ // "tosca.nodes.objectstorage.1.0", "tosca.nodes.blockstorage.1.0",
+ // "tosca.nodes.loadbalancer.1.0", "tosca.nodes.network.port.1.0"));
+
+ // actualNotAbstarctResourcesUniqueIdArray =
+ // restResponseToListByHeader(getResourceNotAbstarctResponse,
+ // "uniqueId");
+
+ actualNotAbstarctResourcesUniqueIdArray = restResponseToListByHeader(getResourceNotAbstarctResponse, "name");
+
+ Collections.sort(actualNotAbstarctResourcesUniqueIdArray);
+ Collections.sort(expectedNotAbstractResourcesUniqueIdArray);
+
+ for (String expectedResource : expectedNotAbstractResourcesUniqueIdArray) {
+ if (false == actualNotAbstarctResourcesUniqueIdArray.contains(expectedResource)) {
+ // System.out.println("Not found abstract resource " +
+ // expectedResource);
+ }
+ }
+ assertTrue(
+ "Expected abstract resources list: " + expectedNotAbstractResourcesUniqueIdArray.toString()
+ + " Actual: " + actualNotAbstarctResourcesUniqueIdArray.toString(),
+ actualNotAbstarctResourcesUniqueIdArray.containsAll(expectedNotAbstractResourcesUniqueIdArray));
+ // assertTrue("Expected abstract resources list: "+
+ // expectedNotAbstractResourcesUniqueIdArray.toString()+ " Actual:
+ // "+actualNotAbstarctResourcesUniqueIdArray.toString(),expectedNotAbstractResourcesUniqueIdArray.equals(actualNotAbstarctResourcesUniqueIdArray));
+
+ // restore the resources
+ // ImportResourceCITest.restoreToOriginalState(originalState);
+
+ }
+
+ protected RestResponse getNotAbstractResources() throws Exception {
+ HttpRequest httpRequest = new HttpRequest();
+
+ String url = String.format(Urls.GET_ALL_NOT_ABSTRACT_RESOURCES, config.getCatalogBeHost(),
+ config.getCatalogBePort());
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), "cs0008");
+
+ RestResponse getResourceNotAbstarctResponse = httpRequest.httpSendGet(url, headersMap);
+
+ return getResourceNotAbstarctResponse;
+ }
+
+ protected List<String> restResponseToListByHeader(RestResponse restResponse, String restResponseHeader) {
+ JsonElement jelement = new JsonParser().parse(restResponse.getResponse());
+ JsonArray jsonArray = jelement.getAsJsonArray();
+
+ List<String> restResponseArray = new ArrayList<>();
+
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonObject jobject = (JsonObject) jsonArray.get(i);
+ String header = jobject.get(restResponseHeader).toString();
+ header = header.replace("\"", "");
+ restResponseArray.add(header);
+ }
+
+ return restResponseArray;
+
+ }
+
+ protected boolean isAllResourcesNotAbstract(RestResponse restResponse) {
+ JsonElement jelement = new JsonParser().parse(restResponse.getResponse());
+ JsonArray jsonArray = jelement.getAsJsonArray();
+
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonObject jobject = (JsonObject) jsonArray.get(i);
+
+ if (jobject.get("abstract").getAsBoolean()) {
+ return false;
+ }
+
+ }
+ return true;
+
+ }
+
+ protected boolean isEmptyList(RestResponse restResponse) {
+ JsonElement jelement = new JsonParser().parse(restResponse.getResponse());
+ JsonArray jsonArray = jelement.getAsJsonArray();
+
+ if (jsonArray.size() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean isAllResourcesCertified(RestResponse restResponse) {
+ JsonElement jelement = new JsonParser().parse(restResponse.getResponse());
+ JsonArray jsonArray = jelement.getAsJsonArray();
+
+ String certified = "CERTIFIED";
+ String lifecycleState;
+
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JsonObject jobject = (JsonObject) jsonArray.get(i);
+ lifecycleState = jobject.get("lifecycleState").getAsString();
+ if (!lifecycleState.equals(certified)) {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ @Test(enabled = false)
+ public void getEmptyNonAbstractResourcesList() throws Exception {
+ // remove all the not abstract resources
+ Map<NormativeTypesEnum, Boolean> originalState = ImportGenericResourceCITest.removeAllNormativeTypeResources();
+
+ // Get not abstract resources
+ RestResponse getResourceNotAbstarctResponse = getNotAbstractResources();
+ // Check that received 200.
+ assertEquals("Check response code after get abstract resources", STATUS_CODE_GET_SUCCESS,
+ getResourceNotAbstarctResponse.getErrorCode().intValue());
+ // Verify empty list
+ assertTrue("Received list is not empty", isEmptyList(getResourceNotAbstarctResponse));
+
+ // restore the resources
+ // ImportResourceCITest.restoreToOriginalState(originalState);
+ // import the resources
+ ImportGenericResourceCITest.importAllNormativeTypesResources(UserRoleEnum.ADMIN);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ResourceApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ResourceApiTest.java
new file mode 100644
index 0000000000..eac33242ed
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ResourceApiTest.java
@@ -0,0 +1,366 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+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.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ResourceValidationUtils;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class ResourceApiTest extends ComponentBaseTest {
+
+ protected final String contentTypeHeaderData = "application/json";
+ protected final String acceptHeaderDate = "application/json";
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ResourceApiTest() {
+ super(name, ResourceApiTest.class.getName());
+ }
+
+ // Keep
+ @Test
+ public void updateResourceMetadataSuccess() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncModifierDetails.setUserId("jh0003");
+ RestResponse restResponse = createResourceForUpdate(sdncModifierDetails);
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ // set resource details
+ ResourceReqDetails resourceDetails = new ResourceReqDetails();
+ resourceDetails.setDescription("updatedDescription");
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ // Duplicate tags are allowed and should be de-duplicated by the server
+ // side
+ resourceTags.add(resourceRespJavaObject.getName());
+ resourceTags.add("tag1");
+ resourceTags.add("tag1");
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ resourceDetails.setTags(resourceTags);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.NETWORK_L2_3_ROUTERS.getCategory(),
+ ResourceCategoryEnum.NETWORK_L2_3_ROUTERS.getSubCategory());
+ resourceDetails.setVendorName("OracleUp");
+ resourceDetails.setVendorRelease("1.5Up");
+ resourceDetails.setContactId("pe1116");
+
+ resourceDetails.setIcon(resourceRespJavaObject.getIcon());
+ resourceDetails.setName(resourceRespJavaObject.getName());
+ resourceDetails.setDerivedFrom(resourceRespJavaObject.getDerivedFrom());
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resourceDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_RESOURCE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(),
+ resourceRespJavaObject.getUniqueId());
+ RestResponse updateResourceResponse = http.httpSendByMethod(url, "PUT", userBodyJson, headersMap);
+
+ // resourceDetails.setResourceName(resourceRespJavaObject.getResourceName());
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails,
+ ResponseParser.convertResourceResponseToJavaObject(updateResourceResponse.getResponse()));
+
+ // Delete resource
+ deleteResource(resourceRespJavaObject.getUniqueId(), sdncModifierDetails.getUserId());
+
+ }
+
+ protected void deleteResource(String resourceUniqueId, String httpCspUserId) throws Exception {
+ RestResponse res = ResourceRestUtils.deleteResource(resourceUniqueId, httpCspUserId);
+
+ // System.out.println("Delete resource was finished with response: " +
+ // res.getErrorCode());
+ }
+
+ protected RestResponse createResourceForUpdate(User sdncModifierDetails) throws Exception {
+
+ ResourceReqDetails resourceDetails = getResourceObj();
+
+ // create resource
+ return ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ }
+
+ public ResourceReqDetails getResourceObj() {
+ // set resource details
+ String resourceName = "ResourceForUpdate" + (int) (Math.random() * 100);
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ // String category = ResourceCategoriesEnum.MOBILITY.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "pe1116";
+ String icon = "myICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getCategory(),
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getSubCategory());
+ return resourceDetails;
+ }
+
+ // -------------------------------------------------------------------
+
+ protected ResourceReqDetails defineResourse_Benny(int n) {
+ String resourceName = "cisco" + String.valueOf(n);
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add("tag1");
+ String category = ServiceCategoriesEnum.MOBILITY.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "borderElement";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ return resourceDetails;
+ }
+
+ @Test
+ public void getAllAbstractResources() throws Exception {
+ RestResponse abstractResources = CatalogRestUtils.getAbstractResources();
+
+ int status = abstractResources.getErrorCode();
+ assertTrue(status == 200);
+ String json = abstractResources.getResponse();
+ JSONArray array = (JSONArray) JSONValue.parse(json);
+ for (Object o : array) {
+ JSONObject value = (JSONObject) o;
+ Boolean element = (Boolean) value.get("abstract");
+ assertTrue(element);
+ }
+
+ }
+
+ @Test
+ public void getAllNotAbstractResources() throws Exception {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ String url = String.format(Urls.GET_ALL_NOT_ABSTRACT_RESOURCES, config.getCatalogBeHost(),
+ config.getCatalogBePort());
+ HttpGet httpget = new HttpGet(url);
+
+ httpget.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+
+ httpget.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+
+ httpget.addHeader(HttpHeaderEnum.USER_ID.getValue(), ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+
+ // System.out.println("Executing request " +
+ // httpget.getRequestLine());
+ CloseableHttpResponse response = httpclient.execute(httpget);
+ int status = response.getStatusLine().getStatusCode();
+ assertTrue(status == 200);
+ try {
+ String json = EntityUtils.toString(response.getEntity());
+ JSONArray array = (JSONArray) JSONValue.parse(json);
+ for (Object o : array) {
+ JSONObject value = (JSONObject) o;
+ Boolean element = (Boolean) value.get("abstract");
+ assertTrue(!element);
+ }
+
+ } finally {
+ response.close();
+ }
+ } finally {
+ httpclient.close();
+ }
+ }
+
+ @Test
+ public void updateResourceMetadata_methodNotAllowed() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ // set resource details
+ String resourceName = "ResForUpdate";
+ String description = "updatedDescription";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add("tag1");
+ resourceTags.add("tag2");
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.root");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ String vendorName = "OracleUp";
+ String vendorRelease = "1.5Up";
+ String contactId = "pe1117";
+ String icon = "myICON.jpgUp";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category,
+ derivedFrom, vendorName, vendorRelease, contactId, icon);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resourceDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_RESOURCE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(),
+ "NotExistsId");
+
+ RestResponse updateResourceResponse = http.httpSendByMethod(url, "POST", userBodyJson, headersMap);
+
+ assertNotNull("Check error code exists in response after wrong update resource",
+ updateResourceResponse.getErrorCode());
+ assertEquals("Check error code after update resource", 405, updateResourceResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void validateResourceNameTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncModifierDetails.setUserId("jh0003");
+
+ ResourceReqDetails resourceDetails = getResourceObj();
+
+ // create resource
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ Resource resourceRespJavaObject = ResponseParser
+ .convertResourceResponseToJavaObject(restResponse.getResponse());
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+
+ // check invalid
+ String url = String.format(Urls.VALIDATE_RESOURCE_NAME, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetails.getName());
+
+ HttpGet httpget = new HttpGet(url);
+
+ httpget.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+
+ httpget.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+
+ httpget.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ // System.out.println("Executing request " +
+ // httpget.getRequestLine());
+ CloseableHttpResponse response = httpclient.execute(httpget);
+ int status = response.getStatusLine().getStatusCode();
+ assertTrue(status == 200);
+ try {
+ String json = EntityUtils.toString(response.getEntity());
+ JSONObject object = (JSONObject) JSONValue.parse(json);
+ Boolean element = (Boolean) object.get("isValid");
+ assertTrue(!element);
+
+ } finally {
+ response.close();
+ }
+ // check valid
+ url = String.format(Urls.VALIDATE_RESOURCE_NAME, config.getCatalogBeHost(), config.getCatalogBePort(),
+ resourceDetails.getName() + "temp");
+
+ httpget = new HttpGet(url);
+
+ httpget.addHeader(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+
+ httpget.addHeader(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+
+ httpget.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ // System.out.println("Executing request " +
+ // httpget.getRequestLine());
+ response = httpclient.execute(httpget);
+ status = response.getStatusLine().getStatusCode();
+ assertTrue(status == 200);
+ try {
+ String json = EntityUtils.toString(response.getEntity());
+ JSONObject object = (JSONObject) JSONValue.parse(json);
+ Boolean element = (Boolean) object.get("isValid");
+ assertTrue(element);
+
+ } finally {
+ response.close();
+ }
+ } finally {
+ httpclient.close();
+ }
+
+ // Delete resource
+ ResourceRestUtils.deleteResource(resourceDetails, sdncModifierDetails, "0.1");
+
+ }
+
+ // -------------------------------------------------------------------
+ // //Benny Tal
+ // @Test
+ // public void createResource_Benny() throws Exception {
+ // for (int i = 0; i < 100; i++) {
+ // ResourceReqDetails resourceDetails = defineResourse_Benny(i);
+ //
+ // ResourceRestUtils.createResource(resourceDetails,
+ // UserUtils.getDesignerDetails());
+ // // resourceUtils.deleteResource(resourceDetails,
+ // UserUtils.getDesignerDetails(), "0.1");
+ // }
+ // }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SampleDataProvider.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SampleDataProvider.java
new file mode 100644
index 0000000000..f4a4fa108a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SampleDataProvider.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import java.io.IOException;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.testng.ITestContext;
+import org.testng.annotations.DataProvider;
+
+public class SampleDataProvider {
+
+ @DataProvider
+ public static Object[][] getResourceByType(ITestContext context) throws IOException, Exception {
+ return new Object[][] {
+ { AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true) },
+ { AtomicOperationUtils.createResourceByType(ResourceTypeEnum.CP, UserRoleEnum.DESIGNER, true) },
+ { AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VL, UserRoleEnum.DESIGNER, true) } };
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SimultaneousApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SimultaneousApiTest.java
new file mode 100644
index 0000000000..de83385029
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/SimultaneousApiTest.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.annotations.Test;
+
+public class SimultaneousApiTest extends ComponentBaseTest {
+
+ protected static ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+
+ @Rule
+ public static TestName name = new TestName();
+
+ static String httpCspUserId = "km2000";
+ static String userFirstName = "Kot";
+ static String userLastName = "Matroskin";
+ static String email = "km2000@intl.sdc.com";
+ static String role = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN).getRole();
+
+ public SimultaneousApiTest() {
+ super(name, SimultaneousApiTest.class.getName());
+
+ }
+
+ public static class WorkerThread implements Runnable {
+ CountDownLatch countDownLatch;
+ int threadIndex;
+
+ public WorkerThread(int threadIndex, CountDownLatch countDownLatch) {
+ this.threadIndex = threadIndex;
+ this.countDownLatch = countDownLatch;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("**** Thread started " + threadIndex);
+ try {
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ String id = ResponseParser.getUniqueIdFromResponse(createResource);
+ // System.out.println("**** Thread " + threadIndex + " create
+ // resource status " + createResource.getErrorCode() + " id = "
+ // + id + " error " + createResource.getResponse());
+ // assertEquals("**** create resource: " +
+ // createResource.getErrorCode() + " thread " + threadIndex,
+ // 201, status);
+ } catch (Exception e) {
+ // System.out.println("**** Thread " + threadIndex + " exception
+ // " + e);
+ }
+ countDownLatch.countDown();
+ // System.out.println("**** Thread finished " + threadIndex);
+
+ }
+
+ }
+
+ @Test
+ public void create2Resources() throws InterruptedException {
+ int threadCount = 5;
+ CountDownLatch countDownLatch = new CountDownLatch(threadCount);
+ ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+ for (int i = 0; i < threadCount; i++) {
+ Runnable worker = new WorkerThread(i + 1, countDownLatch);
+ executor.execute(worker);
+ }
+ countDownLatch.await();
+ // System.out.println(" finished ");
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/UpdateResourceMetadataTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/UpdateResourceMetadataTest.java
new file mode 100644
index 0000000000..b61489d12d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/UpdateResourceMetadataTest.java
@@ -0,0 +1,2254 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ResourceValidationUtils;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class UpdateResourceMetadataTest extends ComponentBaseTest {
+ private static Logger logger = LoggerFactory.getLogger(UpdateResourceMetadataTest.class.getName());
+ protected List<String> Empty_List = new ArrayList<String>();
+ protected String extendedChars;
+
+ protected final String contentTypeHeaderData = "application/json";
+ protected final String acceptHeaderDate = "application/json";
+ protected final String CHARSET_ISO_8859 = "charset=ISO-8859-1";
+
+ public static TestName name = new TestName();
+ protected User sdncModifierDetails;
+ protected ResourceReqDetails resourceDetails;
+
+ public UpdateResourceMetadataTest() {
+ super(name, UpdateResourceMetadataTest.class.getName());
+
+ }
+
+ public String extendedCharsStringBuilder() throws Exception {
+ char[] extendedCharsArray = new char[128];
+ char ch = 128;
+ for (int i = 0; i < extendedCharsArray.length - 1; i++) {
+ extendedCharsArray[i] = ch;
+ ch++;
+ }
+ extendedChars = new String(extendedCharsArray);
+ return extendedChars;
+
+ }
+
+ @BeforeMethod
+ public void setup() throws Exception {
+ sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ resourceDetails = defineResourse();
+
+ }
+
+ // Keep
+ @Test
+ public void UpdateDerivedFromSuccess() throws Exception {
+
+ String oldDerivedFromName = NormativeTypesEnum.ROOT.getNormativeName();
+ String newDerivedFromName = NormativeTypesEnum.SOFTWARE_COMPONENT.getNormativeName();
+
+ // Getting both derived from resources for validation
+ /*
+ * RestResponse resourceByNameAndVersion = resourceUtils.getResourceByNameAndVersion(sdncModifierDetails, oldDerivedFromName, "1.0"); assertEquals("Check response code after get database normative", 200,
+ * resourceByNameAndVersion.getErrorCode().intValue()); Resource databaseNormative = resourceUtils.parseResourceResp(resourceByNameAndVersion);
+ *
+ * resourceByNameAndVersion = resourceUtils.getResourceByNameAndVersion(sdncModifierDetails, newDerivedFromName, "1.0"); assertEquals("Check response code after get database normative", 200,
+ * resourceByNameAndVersion.getErrorCode().intValue()); Resource lbNormative = resourceUtils.parseResourceResp(resourceByNameAndVersion);
+ */
+
+ // Derived from set to Database
+ List<String> derivedFrom = new ArrayList<>();
+ derivedFrom.add(oldDerivedFromName);
+ resourceDetails.setDerivedFrom(derivedFrom);
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ AssertJUnit.assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResource = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ derivedFrom.clear();
+ derivedFrom.add(newDerivedFromName);
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResource.getUniqueId(), "");
+ AssertJUnit.assertEquals("Check response code after create resource", 200, updatedRestResponse.getErrorCode().intValue());
+
+ }
+
+ protected ResourceReqDetails defineUpdateResourceWithNonUpdatableFields(Resource resourceBeforeUpdate) {
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceBeforeUpdate.getName());
+
+ updatedResourceDetails.setVersion("mumu");
+ updatedResourceDetails.setIsAbstract(true);
+ updatedResourceDetails.setIsHighestVersion(true);
+ updatedResourceDetails.setCreatorUserId("df4444");
+ updatedResourceDetails.setCreatorFullName("John Doe");
+ updatedResourceDetails.setLastUpdaterUserId("gf5646");
+ updatedResourceDetails.setLastUpdaterFullName("Viktor Tzoy");
+ updatedResourceDetails.setCreationDate(new Long(4444));
+ updatedResourceDetails.setLastUpdateDate(new Long("534535"));
+ updatedResourceDetails.setLifecycleState(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ updatedResourceDetails.setCost("6.1");
+ updatedResourceDetails.setLicenseType("Installation");
+ updatedResourceDetails.setUUID("dfsfsdf");
+ return updatedResourceDetails;
+ }
+
+ public void UpdateResourceNotFoundTest() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ String resourceName = "cisco4";
+ // update resource
+ String description = "updatedDescription";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.MOBILITY.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "newOracle";
+ String vendorRelease = "2.5";
+ String contactId = "jh0003";
+ String icon = "myICON";
+
+ ResourceReqDetails updatedResourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ updatedResourceDetails.setUniqueId("dummyId");
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, "0.1");
+
+ // validate response
+ AssertJUnit.assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after update resource", 404, updatedRestResponse.getErrorCode().intValue());
+ // String resourceId =
+ // UniqueIdBuilder.buildResourceUniqueId(resourceName, "0.1");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), Arrays.asList("dummyId"), updatedRestResponse.getResponse());
+
+ resourceName = "";
+ // resourceId = UniqueIdBuilder.buildResourceUniqueId(resourceName,
+ // "0.1");
+ updatedResourceDetails = defineUpdatedResourse(resourceName);
+ updatedResourceDetails.setUniqueId("dummyId");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, "0.1");
+ AssertJUnit.assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), Arrays.asList("dummyId"), updatedRestResponse.getResponse());
+
+ }
+
+ public char[] getInValidChars() throws Exception {
+
+ char[] extendedCharsArray = new char[59];
+ char ch = 1;
+ for (int i = 0; i < 44; i++) {
+ extendedCharsArray[i] = ch;
+ ch++;
+ }
+ ch = 58;
+ for (int i = 44; i < 51; i++) {
+ extendedCharsArray[i] = ch;
+ ch++;
+ }
+ ch = 91;
+ for (int i = 51; i < 55; i++) {
+ extendedCharsArray[i] = ch;
+ ch++;
+ }
+ ch = 123;
+ for (int i = 55; i < 59; i++) {
+ extendedCharsArray[i] = ch;
+ ch++;
+ }
+ return extendedCharsArray;
+ }
+
+ public char[] getTagInValidFormatChars() throws Exception {
+ // Tag format is the same as defined for Resource Name :
+ // Allowed characters: Alphanumeric (a-zA-Z0-9), space (' '), underscore
+ // ('_'), dash ('-'), dot ('.')
+ char[] notValidCharsArray = new char[30];
+ char ch = 33;
+ for (int i = 0; i < 12; i++) {
+ notValidCharsArray[i] = ch;
+ ch++;
+ }
+ notValidCharsArray[13] = 47;
+ ch = 58;
+ for (int i = 14; i < 21; i++) {
+ notValidCharsArray[i] = ch;
+ ch++;
+ }
+ ch = 91;
+ for (int i = 21; i < 24; i++) {
+ notValidCharsArray[i] = ch;
+ ch++;
+ }
+ notValidCharsArray[24] = 96;
+ ch = 123;
+ for (int i = 25; i < 30; i++) {
+ notValidCharsArray[i] = ch;
+ ch++;
+ }
+ return notValidCharsArray;
+ }
+
+ public void Validation_UpdateWithIncompleteJsonBodyTest() throws Exception {
+ // init ADMIN user
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // define and create resource
+ ResourceReqDetails resourceDetails = defineResourse();
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceDetails.getName(), "0.1");
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceDetails.getName(), "1.0");
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceDetails.getName(), "1.1");
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceId = resourceDetails.getUniqueId();
+ resourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(restResponse));
+
+ // build Json Object
+ JSONObject jsonObject = JsonObjectBuilder(resourceDetails);
+
+ List<String> resource = new ArrayList<>();
+ resource.add("Resource");
+
+ // remove Description
+ UpdateAndValidateWithIncompletedJsonBody(sdncModifierDetails, jsonObject, resourceId, "description", ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), resource);
+ // remove Tags
+ UpdateAndValidateWithIncompletedJsonBody(sdncModifierDetails, jsonObject, resourceId, "tags", ActionStatus.COMPONENT_MISSING_TAGS.name(), Empty_List);
+ // remove Category
+ UpdateAndValidateWithIncompletedJsonBody(sdncModifierDetails, jsonObject, resourceId, "category", ActionStatus.COMPONENT_MISSING_CATEGORY.name(), resource);
+ // remove VendorName
+ UpdateAndValidateWithIncompletedJsonBody(sdncModifierDetails, jsonObject, resourceId, "vendorName", ActionStatus.MISSING_VENDOR_NAME.name(), Empty_List);
+ // remove VendorRelease
+ UpdateAndValidateWithIncompletedJsonBody(sdncModifierDetails, jsonObject, resourceId, "vendorRelease", ActionStatus.MISSING_VENDOR_RELEASE.name(), Empty_List);
+ // remove AT&T Contact
+ UpdateAndValidateWithIncompletedJsonBody(sdncModifierDetails, jsonObject, resourceId, "contactId", ActionStatus.COMPONENT_MISSING_CONTACT.name(), resource);
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, "0.1");
+ // validate response
+ AssertJUnit.assertNotNull("check response object is not null after get resource", getRestResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceDetails.getName(), "0.1");
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceDetails.getName(), "1.0");
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, resourceDetails.getName(), "1.1");
+ }
+
+ // End of validation tests
+ // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected ResourceReqDetails defineUpdatedResourse(String resourceName) {
+ String description = "updatedDescription";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ // Duplicate tags are allowed and should be de-duplicated by server side
+ resourceTags.add(resourceName);
+ resourceTags.add("tag1");
+ resourceTags.add("tag1");
+ resourceTags.add("tag2");
+ resourceTags.add("tag2");
+ String category = ServiceCategoriesEnum.VOIP.getValue();
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());
+ String vendorName = "updatedOracle";
+ String vendorRelease = "3.5";
+ String contactId = "jh0001";
+ String icon = "myUpdatedICON";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, category, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getCategory(), ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getSubCategory());
+
+ return resourceDetails;
+ }
+
+ protected ResourceReqDetails defineResourse() {
+ String resourceName = "cisco4";
+ String description = "description";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(NormativeTypesEnum.ROOT.getNormativeName());// "tosca.nodes.Root");
+ String vendorName = "Oracle";
+ String vendorRelease = "1.5";
+ String contactId = "jh0003";
+ String icon = "objectStorage";
+
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getCategory(), ResourceCategoryEnum.GENERIC_INFRASTRUCTURE.getSubCategory());
+
+ return resourceDetails;
+ }
+
+ protected RestResponse createResource(User sdncModifierDetails, ResourceReqDetails resourceDetails) throws Exception {
+ // clean ES DB
+ DbUtils.cleanAllAudits();
+
+ // create resource
+ RestResponse restResponse = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+
+ // validate response
+ AssertJUnit.assertNotNull("check response object is not null after create resource", restResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create resource", restResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+
+ return restResponse;
+ }
+
+ protected RestResponse TryUpdateByAnotherVerb(ResourceReqDetails updatedResourceDetails, User sdncModifierDetails, String uri) throws Exception {
+ // delete resource
+ Config config;
+ RestResponse ResourceResponse;
+ try {
+ config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_RESOURCE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(), updatedResourceDetails.getName() + ".0.1");
+
+ if (uri == "GET") {
+ ResourceResponse = http.httpSendGet(url, headersMap);
+ } else if (uri == "POST") {
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(updatedResourceDetails);
+ ResourceResponse = http.httpSendPost(url, userBodyJson, headersMap);
+ } else if (uri == "DELETE") {
+ ResourceResponse = http.httpSendDelete(url, headersMap);
+ } else
+ return null;
+
+ return ResourceResponse;
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return null;
+
+ }
+
+ protected JSONObject JsonObjectBuilder(ResourceReqDetails resourceDetails) throws JSONException {
+ // json object: resourceName and icon are must
+ JSONObject jObject = new JSONObject();
+
+ List<String> tagsList = Arrays.asList(resourceDetails.getName());
+ List<String> derivedFromList = Arrays.asList("[tosca.nodes.Root]");
+
+ jObject.put("name", resourceDetails.getName());
+ jObject.put("description", "updatedDescription");
+ jObject.put("tags", tagsList);
+ jObject.put("category", ServiceCategoriesEnum.VOIP.getValue());
+ jObject.put("derivedFrom", derivedFromList);
+ jObject.put("vendorName", "newOracle");
+ jObject.put("vendorRelease", "1.5");
+ jObject.put("contactId", "jh0003");
+ jObject.put("icon", resourceDetails.getIcon());
+
+ return jObject;
+ }
+
+ protected JSONObject RemoveFromJsonObject(JSONObject jObject, String removedPropery) {
+ jObject.remove(removedPropery);
+
+ return jObject;
+ }
+
+ // purpose: function for controlling json body fields and validating
+ // response
+ protected void UpdateAndValidateWithIncompletedJsonBody(User sdncModifierDetails, JSONObject jsonObject, String resourceId, String removedField, String errorMessage, List<String> variables) throws Exception {
+
+ JSONObject jObject = new JSONObject(jsonObject, JSONObject.getNames(jsonObject));
+ // remove description from jsonObject
+ jObject = RemoveFromJsonObject(jObject, removedField);
+ // update with incomplete body.
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(jObject.toString(), sdncModifierDetails, resourceId);
+ // validate response
+ AssertJUnit.assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(errorMessage, variables, updatedRestResponse.getResponse());
+
+ }
+
+ // purpose: function for validating error response
+ protected void UpdateAndValidate(User sdncModifierDetails, ResourceReqDetails resourceDetails, String recievedMessage, List<String> variables) throws Exception {
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, "0.1");
+ // validate response
+ AssertJUnit.assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(recievedMessage, variables, updatedRestResponse.getResponse());
+
+ }
+
+ protected void parseResponseAndValidateNonUpdatable(ResourceReqDetails resourceDetails, RestResponse restResponse) throws Exception {
+ // parse response to javaObject
+ Resource updatedResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ AssertJUnit.assertTrue(!resourceDetails.getIsHighestVersion().equals(updatedResourceRespJavaObject.isHighestVersion()));
+ AssertJUnit.assertTrue(!resourceDetails.getVersion().equals(updatedResourceRespJavaObject.getName()));
+ AssertJUnit.assertTrue(!resourceDetails.getIsAbstract().equals(updatedResourceRespJavaObject.isAbstract()));
+ AssertJUnit.assertTrue(!resourceDetails.getCreatorUserId().equals(updatedResourceRespJavaObject.getCreatorUserId()));
+ AssertJUnit.assertTrue(!resourceDetails.getCreatorFullName().equals(updatedResourceRespJavaObject.getCreatorFullName()));
+ AssertJUnit.assertTrue(!resourceDetails.getLastUpdateDate().equals(updatedResourceRespJavaObject.getLastUpdateDate()));
+ AssertJUnit.assertTrue(!resourceDetails.getCreationDate().equals(updatedResourceRespJavaObject.getCreationDate()));
+ AssertJUnit.assertTrue(!resourceDetails.getLastUpdaterUserId().equals(updatedResourceRespJavaObject.getLastUpdaterUserId()));
+ AssertJUnit.assertTrue(!resourceDetails.getLastUpdaterFullName().equals(updatedResourceRespJavaObject.getLastUpdaterFullName()));
+ AssertJUnit.assertTrue(!resourceDetails.getLifecycleState().equals(updatedResourceRespJavaObject.getLifecycleState()));
+ AssertJUnit.assertTrue(!resourceDetails.getCost().equals(updatedResourceRespJavaObject.getCost()));
+ AssertJUnit.assertTrue(!resourceDetails.getLicenseType().equals(updatedResourceRespJavaObject.getLicenseType()));
+ AssertJUnit.assertTrue(!resourceDetails.getUUID().equals(updatedResourceRespJavaObject.getUUID()));
+ }
+
+ protected void parseResponseAndValidate(ResourceReqDetails ResourceDetails, RestResponse restResponse) throws Exception {
+ // parse response to javaObject
+ Resource updatedResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ // validate request vs response
+ ResourceValidationUtils.validateResourceReqVsResp(ResourceDetails, updatedResourceRespJavaObject);
+ }
+
+ public ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(ResourceReqDetails resourceDetails, String resourceVersion) {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+
+ expectedResourceAuditJavaObject.setAction("Checkout");
+ expectedResourceAuditJavaObject.setModifierUid(UserRoleEnum.ADMIN.getUserId());
+ expectedResourceAuditJavaObject.setModifierName(UserRoleEnum.ADMIN.getUserName());
+ expectedResourceAuditJavaObject.setStatus("200.0");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setResourceName(resourceDetails.getName().toLowerCase());
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion(String.valueOf(Float.parseFloat(resourceVersion) - 0.1f));
+ expectedResourceAuditJavaObject.setCurrVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public enum FieldToValidate {
+ ContactId, Tags, VendorName, VendorRelease, Description
+ }
+
+ @Test
+ public void UpdateBy_postTest() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // update resource - without changing resourceName
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+
+ RestResponse updatedRestResponse = TryUpdateByAnotherVerb(updatedResourceDetails, sdncModifierDetails, "POST");
+
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), Empty_List, updatedRestResponse.getResponse());
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(resourceDetails, getRestResponse);
+
+ }
+
+ @Test
+ public void UpdateBy_getTest() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // update resource - without changing resourceName
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ RestResponse updatedRestResponse = TryUpdateByAnotherVerb(updatedResourceDetails, sdncModifierDetails, "GET");
+
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), Empty_List, updatedRestResponse.getResponse());
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(resourceDetails, getRestResponse);
+
+ }
+
+ @Test
+ public void UpdateBy_deleteTest() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // update resource - without changing resourceName
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ RestResponse updatedRestResponse = TryUpdateByAnotherVerb(updatedResourceDetails, sdncModifierDetails, "DELETE");
+
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), Empty_List, updatedRestResponse.getResponse());
+
+ RestResponse getRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(resourceDetails, getRestResponse);
+
+ }
+
+ // TODO DE
+ // @Ignore("")
+ @Test
+ public void UpdateWithInvaldJsonBodyTest() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ resourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(restResponse));
+ String resourceId = resourceDetails.getUniqueId();
+
+ // update Descirption value
+ String description = "updatedDescription";
+
+ // send update with incompleted json, only description string
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(description, sdncModifierDetails, resourceId);
+
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("check error code after update resource", 400, updatedRestResponse.getErrorCode().intValue());
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(resourceDetails, getRestResponse);
+
+ }
+
+ @Test
+ public void UpdateResourceNameSensitiveTest() throws Exception {
+ User sdncModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+ String resourceName = "Ab";
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ // Delete resources
+ RestResponse response = null;
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.1");
+ BaseRestUtils.checkDeleteResponse(response);
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.2");
+ BaseRestUtils.checkDeleteResponse(response);
+
+ RestResponse restResponse = createResource(sdncModifierDetails, updatedResourceDetails);
+ assertEquals("create resource failed", 201, restResponse.getErrorCode().intValue());
+
+ // check-in Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(updatedResourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // String resourceCertifyVersion = "0.1";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(updatedResourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ updatedResourceDetails.setName("ABC_-bt.aT");
+ ArrayList<String> resourceTag = new ArrayList<String>();
+ resourceTag.add(0, "ABC_-bt.aT");
+ updatedResourceDetails.setTags(resourceTag);
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, updatedResourceDetails.getUniqueId(), "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(updatedResourceDetails, updatedRestResponse);
+
+ // Delete resources
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.1");
+ BaseRestUtils.checkDeleteResponse(response);
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.2");
+ BaseRestUtils.checkDeleteResponse(response);
+
+ }
+
+ @Test
+ public void UpdateIcon_InegativeFlow() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ List<String> resourceList = new ArrayList<String>();
+ resourceList.add(0, "Resource");
+ // check InValid Characters
+ char[] notValidCharsArray = new char[59];
+ notValidCharsArray = getInValidChars();
+ // update metadata details
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ RestResponse updatedRestResponse;
+
+ for (int i = 0; i < notValidCharsArray.length; i++) {
+ // change icon of metadata
+ updatedResourceDetails.setIcon("MyIcon" + notValidCharsArray[i]);
+ // PUT request
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_ICON.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource icon", 400, updatedRestResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating resource icon", "Bad Request", updatedRestResponse.getResponseMessage().toString());
+
+ }
+
+ // empty icon
+ String updateIcon = "";
+ updatedResourceDetails.setIcon(updateIcon);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_ICON.name(), resourceList, updatedRestResponse.getResponse());
+
+ // Icon length more then 25 characters
+ resourceList.add(1, "25");
+ updatedResourceDetails.setIcon("1234567890_-qwertyuiopASDNNN");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after create resource", 400, updatedRestResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating resource icon", "Bad Request", updatedRestResponse.getResponseMessage().toString());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResource_NoTagsEqualToResourceName() throws Exception {
+
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ ResourceReqDetails updatedResourceDetails = defineResourse();
+ updatedResourceDetails.setName("updatedResourceName");
+ List<String> tags = updatedResourceDetails.getTags();
+
+ for (Iterator<String> iter = tags.listIterator(); iter.hasNext();) {
+ String a = iter.next();
+ if (a.equals("updatedResourceName")) {
+ iter.remove();
+ }
+ }
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource icon", 400, updatedRestResponse.getErrorCode().intValue());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResourceName_negativeFlow() throws Exception {
+ // The validation are done in Tag's validation
+ User sdncAdminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ RestResponse updatedRestResponse;
+ RestResponse restResponse = createResource(sdncAdminModifierDetails, resourceDetails);
+ assertEquals("create resource failed", 201, restResponse.getErrorCode().intValue());
+ String uniqueId = resourceDetails.getUniqueId();
+ String resourceName = resourceDetails.getName();
+ // check InValid Characters
+ char[] notValidCharsArray = new char[59];
+ notValidCharsArray = getInValidChars();
+ ArrayList<String> resource_Name = new ArrayList<String>();
+ List<String> resourceList = new ArrayList<String>();
+
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceList.add(0, "Resource");
+
+ // update metadata details
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ for (int i = 0; i < notValidCharsArray.length; i++, resource_Name.clear()) {
+ if (i != 1 && i != 46 && /*
+ * i != 8 && i != 9 && i != 10 && i != 11 && i != 12 &&
+ */ i != 31) // space ("") and dot(.)
+ {
+ // change resourceName parameter
+ updatedResourceDetails.setName("UpdatedResourceName" + notValidCharsArray[i]);
+ resource_Name.add("UpdatedResourceName" + notValidCharsArray[i]);
+ updatedResourceDetails.setTags(resource_Name);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncAdminModifierDetails, uniqueId, "");
+ // validate response
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_TAG.name(),
+ // Empty_List, updatedRestResponse.getResponse());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPONENT_NAME.name(), resourceList, updatedRestResponse.getResponse());
+
+ }
+ }
+
+ // resourceName length more then 50 characters
+ // Duplicate tags are allowed and should be de-duplicated by server side
+ resource_Name.add(resourceName);
+ resource_Name.add("tag1");
+ resource_Name.add("tag1");
+ resource_Name.add("tag2");
+ resource_Name.add("tag2");
+
+ resourceList.add(1, "1024");
+ // updatedResourceDetails.setName("123456789012345678901234567890123456789012345678901");
+ updatedResourceDetails.setName(new String(new char[1025]).replace("\0", "a"));
+ // resource_Name.add("123456789012345678901234567890123456789012345678901");
+ updatedResourceDetails.setTags(resource_Name);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncAdminModifierDetails, uniqueId, "");
+ // validate response
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT.name(), resourceList, updatedRestResponse.getResponse());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncAdminModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ // delete resource
+ RestResponse response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncAdminModifierDetails, updatedResourceDetails.getName(), "0.1");
+ BaseRestUtils.checkDeleteResponse(response);
+ }
+
+ @Test
+ public void UpdateResourceInformation_NotCheckedOut() throws Exception {
+
+ String resourceBaseVersion = "0.1";
+ List<String> resourceList = new ArrayList<String>();
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // CheckIn Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN); // NOT_CERTIFIED_CHECKIN
+ assertNotNull("check response object is not null after checkout resource", checkoutResource);
+ assertNotNull("check error code exists in response after checkIn resource", checkoutResource.getErrorCode());
+ assertEquals("Check response code after checkin resource", 200, checkoutResource.getErrorCode().intValue());
+
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource icon", 409, updatedRestResponse.getErrorCode().intValue());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResourceInformation_resourceVersion_11() throws Exception {
+
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), resourceList, updatedRestResponse.getResponse());
+ // assertEquals("Check response code after updating resource icon", 409,
+ // updatedRestResponse.getErrorCode().intValue());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResourceInformation_resourceVersion_02() throws Exception {
+
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // String resourceCertifyVersion = "0.1";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after updating resource icon", 200, updatedRestResponse.getErrorCode().intValue());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(updatedResourceDetails, getResourceRespJavaObject);
+
+ // delete resource
+ RestResponse response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.1");
+ BaseRestUtils.checkDeleteResponse(response);
+ response = ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.2");
+ BaseRestUtils.checkDeleteResponse(response);
+
+ }
+
+ @Test
+ public void UpdateResourceIcon_resourceVersion_11() throws Exception {
+ // Can be changed only if major version is 0.
+
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceCertifyVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // ResourceReqDetails updatedResourceDetails =
+ // defineUpdatedResourse(resourceName);
+ ResourceReqDetails updatedResourceDetails = defineResourse();
+ // updatedResourceDetails.setVendorName("updatedVandorName");
+ updatedResourceDetails.setIcon("updatedIcon");
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_ICON_CANNOT_BE_CHANGED.name(), resourceList, updatedRestResponse.getResponse());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResourceVandorName_resourceVersion_11() throws Exception {
+ // Can be changed only if the major resource version is 0.
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceCertifyVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // ResourceReqDetails updatedResourceDetails =
+ // defineUpdatedResourse(resourceName);
+ ResourceReqDetails updatedResourceDetails = defineResourse();
+
+ updatedResourceDetails.setVendorName("updatedVandorName");
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource icon", 400, updatedRestResponse.getErrorCode().intValue());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResourceName_resourceVersion_11() throws Exception {
+ // Can be changed only if the major resource version is 0.
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("create resource failed", 201, restResponse.getErrorCode().intValue());
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceCertifyVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ /*
+ * //ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName); ResourceReqDetails updatedResourceDetails = defineResourse();
+ *
+ * updatedResourceDetails.setResourceName("updatedResourceName"); updatedResourceDetails.setIcon("updatedResourceName");
+ */
+ resourceDetails.setName("updatedResourceName");
+ List<String> tagList = new ArrayList<String>();
+ tagList.add(0, "updatedResourceName");
+ resourceDetails.setTags(tagList);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NAME_CANNOT_BE_CHANGED.name(), resourceList, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResourceTag_resourceVersion_11() throws Exception {
+ // Tag Can be updated when major version is 0.
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceCertifyVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // ResourceReqDetails updatedResourceDetails =
+ // defineUpdatedResourse(resourceName);
+ ResourceReqDetails updatedResourceDetails = defineResourse();
+ // updatedResourceDetails.setVendorName("updatedVandorName");
+
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add("NewTag");
+ resourceTags.add(resourceDetails.getName());
+
+ updatedResourceDetails.setTags(resourceTags);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(updatedResourceDetails, getRestResponse);
+
+ }
+
+ @Test
+ public void UpdateAllowedParames_resourceVersion_11() throws Exception {
+
+ // Tag, contactId, vendorRelease,tags And description - Can be also
+ // updated when major version is NOT 0.
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceCertifyVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // ResourceReqDetails updatedResourceDetails =
+ // defineUpdatedResourse(resourceName);
+ ResourceReqDetails updatedResourceDetails = defineResourse();
+ // updatedResourceDetails.setVendorName("updatedVandorName");
+
+ // updated allowed parameters when major resource version is NOT "0"
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add("NewTag");
+ resourceTags.add(resourceDetails.getName());
+ updatedResourceDetails.setTags(resourceTags);
+ updatedResourceDetails.setDescription("UpdatedDescription");
+ updatedResourceDetails.setVendorRelease("5.1");
+ updatedResourceDetails.setContactId("bt750h");
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(updatedResourceDetails, getRestResponse);
+
+ }
+
+ @Test
+ public void UpdateResourceDerivedFrom_resourceVersion_11() throws Exception {
+ // DerivedFrom parameter - Can be updated when major version is 0.
+ User adminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ String resourceBaseVersion = "0.1";
+
+ // create resource
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String resourceName = resourceDetails.getName();
+
+ // resourceUtils.addResourceMandatoryArtifacts(sdncModifierDetails,
+ // restResponse);
+
+ // Certify Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, adminModifierDetails, resourceBaseVersion, LifeCycleStatesEnum.CERTIFY);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ String resourceCertifyVersion = "1.0";
+ logger.debug("Changing resource life cycle ");
+ checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceCertifyVersion, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after checkout resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // ResourceReqDetails updatedResourceDetails =
+ // defineUpdatedResourse(resourceName);
+ ResourceReqDetails updatedResourceDetails = defineResourse();
+ ArrayList<String> drivenFrom = new ArrayList<String>();
+ drivenFrom.add(0, "tosca.nodes.Container.Application");
+ updatedResourceDetails.setDerivedFrom(drivenFrom);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED.name(), resourceList, updatedRestResponse.getResponse());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ }
+
+ @Test
+ public void UpdateResource_vendorNameValidation() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ String updatedVendorName = "";
+ String uniqueId = resourceDetails.getUniqueId();
+ resourceDetails.setVendorName(updatedVendorName);
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ // update resource vendorName metadata: 1 characters
+ updatedVendorName = " ";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ // update resource vendorName metadata: 25 characters
+ updatedVendorName = "Verification and validati";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // update resource vendorName metadata: 26 characters
+ updatedVendorName = "Verification and validatii";
+ // set vendorName
+ List<String> myList = new ArrayList<String>();
+ myList.add(0, "25");
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.VENDOR_NAME_EXCEEDS_LIMIT.name(), myList, updatedRestResponse.getResponse());
+
+ // update resource VendorRelease metadata: forbidden characters
+ updatedVendorName = "A1<";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1>";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1:";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1\"";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1/";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1\\";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1|";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1?";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorName = "A1*";
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ // update resource vendorName metadata: null
+ updatedVendorName = null;
+ // set vendorName
+ resourceDetails.setVendorName(updatedVendorName);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_NAME.name(), Empty_List, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResource_vendorReleaseValidation() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ RestResponse updatedRestResponse;
+ String uniqueId = resourceDetails.getUniqueId();
+ String updatedVendorRelease;
+ // set VendorRelease
+
+ // update resource VendorRelease metadata: 1 characters
+ updatedVendorRelease = "1";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // update resource VendorRelease metadata: 25 characters
+ updatedVendorRelease = "(!#1.00000000000000000000";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // update resource VendorRelease metadata: 26 characters
+ updatedVendorRelease = "(!#1.000000000000000000005";// set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.VENDOR_RELEASE_EXCEEDS_LIMIT.name(), Arrays.asList("" + ValidationUtils.VENDOR_RELEASE_MAX_LENGTH), updatedRestResponse.getResponse());
+
+ // UpdateAndValidate(sdncModifierDetails, resourceDetails,
+ // ActionStatus.VENDOR_RELEASE_EXCEEDS_LIMIT.name(),
+ // Arrays.asList(""+ValidationUtils.VENDOR_RELEASE_MAX_LENGTH));
+
+ // update resource VendorRelease metadata: forbidden characters
+ updatedVendorRelease = "A1<";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1>";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1:";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1\"";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1/";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1\\";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1|";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1?";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ updatedVendorRelease = "A1*";
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ // update resource VendorRelease metadata: null
+ updatedVendorRelease = null;
+ // set VendorRelease
+ resourceDetails.setVendorRelease(updatedVendorRelease);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_VENDOR_RELEASE.name(), Empty_List, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResource_contactIdValidation() throws Exception { // [a-zA-Z]{2}[0-9]{3}[a-zA-Z0-9]{1}
+ // (6
+ // characters
+ // now,
+ // may
+ // be
+ // expanded
+ // up
+ // to
+ // 8
+ // characters
+ // in
+ // the
+ // future).
+ // Convert
+ // Upper
+ // case
+ // character
+ // to
+ // lower
+ // case
+ RestResponse updatedRestResponse;
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ String uniqueId = resourceDetails.getUniqueId();
+
+ List<String> myList = new ArrayList<String>();
+ myList.add(0, "Resource");
+ String updatedContactId = "";
+ resourceDetails.setContactId(updatedContactId);
+
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "ab12345";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = " ";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "ab 50h";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "ab123c";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ updatedContactId = "cd789E";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ // contactId norm
+ resourceDetails.setContactId(updatedContactId.toLowerCase());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ updatedContactId = "ef4567";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ updatedContactId = "AA012A";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ // contactId norm
+ resourceDetails.setContactId(updatedContactId.toLowerCase());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ updatedContactId = "CD012c";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ // contactId norm
+ resourceDetails.setContactId(updatedContactId.toLowerCase());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ updatedContactId = "EF0123";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ // contactId norm
+ resourceDetails.setContactId(updatedContactId.toLowerCase());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ ////////////////////////////// **************//////////////////////////////
+ List<String> resource = Arrays.asList("Resource");
+ updatedContactId = "01345a";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "0y000B";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "Y1000b";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "abxyzC";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "cdXYZc";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "efXY1D";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "EFabcD";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "EFABCD";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "EFABC1";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "efui1D";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "efui1!";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "ef555!";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = ",f555";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ updatedContactId = "EF55.5";
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ // update resource contactId metadata: extended character set (128–255)
+ resourceDetails.setContactId(extendedCharsStringBuilder());
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ // update resource contactId metadata: null
+ updatedContactId = null;
+ resourceDetails.setContactId(updatedContactId);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CONTACT.name(), myList, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResource_TagsFieldValidation() throws Exception {
+ RestResponse updatedRestResponse;
+ // define and create resource
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ String uniqueId = resourceDetails.getUniqueId();
+
+ String updatedTagField = "";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(updatedTagField);
+ // set description
+ resourceDetails.setTags(resourceTags);
+ List<String> variables = Arrays.asList("Resource", "tag");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FIELD_FORMAT.name(), variables, updatedRestResponse.getResponse());
+
+ // update resource tags metadata: empty
+ resourceTags = new ArrayList<String>();
+ // set Tags
+ resourceDetails.setTags(resourceTags);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_TAGS.name(), Empty_List, updatedRestResponse.getResponse());
+
+ // update resource description metadata: 1 characters
+ updatedTagField = "A";
+ resourceTags = new ArrayList<String>();
+ resourceTags.add(updatedTagField);
+ resourceTags.add(resourceDetails.getName());
+ // set description
+ resourceDetails.setTags(resourceTags);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // OK - tag up to 50 chars
+ updatedTagField = "The Indian-crested.porcupine_The Indian cresteddds";
+ resourceTags.add(updatedTagField);
+ resourceDetails.setTags(resourceTags);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // OK - sum is 1024, 50x20+48+20(commas)+6(cisco4 - resource name)
+ String updatedTagField1 = "The Indian-crested.porcupine_The Indian crestedd01";
+ String updatedTagField2 = "The Indian-crested.porcupine_The Indian crestedd02";
+ String updatedTagField3 = "The Indian-crested.porcupine_The Indian crestedd03";
+ String updatedTagField4 = "The Indian-crested.porcupine_The Indian crestedd04";
+ String updatedTagField5 = "The Indian-crested.porcupine_The Indian crestedd05";
+ String updatedTagField6 = "The Indian-crested.porcupine_The Indian crestedd06";
+ String updatedTagField7 = "The Indian-crested.porcupine_The Indian crestedd07";
+ String updatedTagField8 = "The Indian-crested.porcupine_The Indian crestedd08";
+ String updatedTagField9 = "The Indian-crested.porcupine_The Indian crestedd09";
+ String updatedTagField10 = "The Indian-crested.porcupine_The Indian crestedd10";
+ String updatedTagField11 = "The Indian-crested.porcupine_The Indian crestedd11";
+ String updatedTagField12 = "The Indian-crested.porcupine_The Indian crestedd12";
+ String updatedTagField13 = "The Indian-crested.porcupine_The Indian crestedd13";
+ String updatedTagField14 = "The Indian-crested.porcupine_The Indian crestedd14";
+ String updatedTagField15 = "The Indian-crested.porcupine_The Indian crestedd15";
+ String updatedTagField16 = "The Indian-crested.porcupine_The Indian crestedd16";
+ String updatedTagField17 = "The Indian-crested.porcupine_The Indian crestedd17";
+ String updatedTagField18 = "The Indian-crested.porcupine_The Indian crestedd18";
+ String updatedTagField19 = "The Indian-crested.porcupine_The Indian crestaa";
+
+ resourceTags = new ArrayList<String>();
+ resourceTags.add(updatedTagField);
+ resourceTags.add(updatedTagField1);
+ resourceTags.add(updatedTagField2);
+ resourceTags.add(updatedTagField3);
+ resourceTags.add(updatedTagField4);
+ resourceTags.add(updatedTagField5);
+ resourceTags.add(updatedTagField6);
+ resourceTags.add(updatedTagField7);
+ resourceTags.add(updatedTagField8);
+ resourceTags.add(updatedTagField9);
+ resourceTags.add(updatedTagField10);
+ resourceTags.add(updatedTagField11);
+ resourceTags.add(updatedTagField12);
+ resourceTags.add(updatedTagField13);
+ resourceTags.add(updatedTagField14);
+ resourceTags.add(updatedTagField15);
+ resourceTags.add(updatedTagField16);
+ resourceTags.add(updatedTagField17);
+ resourceTags.add(updatedTagField18);
+ resourceTags.add(updatedTagField19);
+ resourceTags.add(resourceDetails.getName());
+ // set description
+ resourceDetails.setTags(resourceTags);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // Add another tag-exceeds limit
+ resourceTags.add("d");
+ resourceDetails.setTags(resourceTags);
+ ArrayList<String> myArray = new ArrayList<String>();
+ myArray.add(0, "1024");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT.name(), myArray, updatedRestResponse.getResponse());
+
+ // Tag exceeds limit - 51
+ resourceTags = new ArrayList<String>();
+ updatedTagField = "The Indian-crested.porcupine_The Indian crestedddsw";
+ resourceTags.add(updatedTagField);
+ resourceTags.add(resourceDetails.getName());
+ // set description
+ resourceDetails.setTags(resourceTags);
+ myArray.remove(0);
+ myArray.add(0, "50");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT.name(), myArray, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResource_DesriptionFieldValidation() throws Exception {
+ // define and create resource
+ RestResponse updatedRestResponse;
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ String uniqueId = resourceDetails.getUniqueId();
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ List<String> resource = new ArrayList<>();
+ resource.add("Resource");
+ // update resource description metadata: 0 characters
+ String updatedDescription = "";
+ // set description
+ resourceDetails.setDescription(updatedDescription);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), resource, updatedRestResponse.getResponse());
+
+ // update resource description metadata: null
+ updatedDescription = null;
+ // set description
+ resourceDetails.setDescription(updatedDescription);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), resource, updatedRestResponse.getResponse());
+
+ // update resource description metadata: 1 characters
+ updatedDescription = "A";
+ // set description
+ resourceDetails.setDescription(updatedDescription);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // update resource description metadata: 1024 characters
+ updatedDescription = "The Indian crested porcupine *{Hystrix indica}*, or Indian porcupine is a member of the Old World porcupines." + "It is quite an adaptable rodent, found throughout southern Asia and the Middle East."
+ + "It is tolerant of several different habitats: mountains, tropical and subtropical grasslands, scrublands, and forests."
+ + "It is a large rodent, growing more than 0.9 m = (3 ft) long and weighing 14.5 kg = (32 lb)! [citation needed] It is covered in multiple layers of quills."
+ + "The longest quills grow from its shoulders to about a third of the animal's length." + "Its tail is covered in short, hollow quills that can rattle when threatened."
+ + "It has broad feet and long claws for digging. When attacked, the Indian crested porcupine raises its quills and rattles the hollow quills on its tail."
+ + "If the predator persists past these threats, the porcupine launches a backwards assault, hoping to stab its attacker with its quills."
+ + "It does this so effectively that most brushes between predators and the Indian porcupine end in death or severe injury";
+ // set description
+ resourceDetails.setDescription(updatedDescription);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ parseResponseAndValidate(resourceDetails, updatedRestResponse);
+
+ // update resource description metadata: 1025 characters
+ updatedDescription = "The Indian crested porcupine *{Hystrix indica}*, or Indian porcupine is a member of the Old World porcupines." + "It is quite an adaptable rodent, found throughout southern Asia and the Middle East."
+ + "It is tolerant of several different habitats: mountains, tropical and subtropical grasslands, scrublands, and forests."
+ + "It is a large rodent, growing more than 0.9 m = (3 ft) long and weighing 14.5 kg = (32 lb)! [citation needed] It is covered in multiple layers of quills."
+ + "The longest quills grow from its shoulders to about a third of the animal's length." + "Its tail is covered in short, hollow quills that can rattle when threatened."
+ + "It has broad feet and long claws for digging. When attacked, the Indian crested porcupine raises its quills and rattles the hollow quills on its tail."
+ + "If the predator persists past these threats, the porcupine launches a backwards assault, hoping to stab its attacker with its quills."
+ + "It does this so effectively that most brushes between predators and the Indian porcupine end in death or severe injury.";
+ // set description
+ resourceDetails.setDescription(updatedDescription);
+ resource.add(1, "1024");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, uniqueId, "");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT.name(), resource, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResource_TagsFormatValidation() throws Exception {
+ char[] notValidCharsArray = getTagInValidFormatChars();
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check recourse created ", 201, restResponse.getErrorCode().intValue());
+ String resourceName = resourceDetails.getName();
+
+ // update tag details
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ ArrayList<String> resourceTags = new ArrayList<String>();
+
+ String updatedTagField;
+ RestResponse updatedRestResponse;
+ List<String> variables = Arrays.asList("Resource", "tag");
+
+ for (int i = 0; i < notValidCharsArray.length; i++) {
+ updatedTagField = "UpdatedTag" + notValidCharsArray[i];
+ resourceTags = new ArrayList<String>();
+ resourceTags.add(updatedTagField);
+ resourceTags.add(resourceDetails.getName());
+ // set description
+ updatedResourceDetails.setTags(resourceTags);
+
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FIELD_FORMAT.name(), variables, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource icon", 400, updatedRestResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating resource icon", "Bad Request", updatedRestResponse.getResponseMessage().toString());
+
+ }
+
+ }
+
+ @Test
+ public void UpdateResourceCategory_negativeFlow() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after update resource", 201, restResponse.getErrorCode().intValue());
+ Resource resourceBeforeUpdate = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ String uniqueID = resourceDetails.getUniqueId();
+
+ // Update resource Category Successfully
+ ResourceReqDetails updatedResourceDetails = resourceDetails;
+
+ updatedResourceDetails.removeAllCategories();
+ updatedResourceDetails.addCategoryChain(ServiceCategoriesEnum.MOBILITY.getValue(), ResourceCategoryEnum.APPLICATION_L4_DATABASE.getSubCategory());
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+
+ // validate response
+ List<String> resourceList = new ArrayList<String>();
+ resourceList.add(0, "Resource");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CATEGORY.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource", 400, updatedRestResponse.getErrorCode().intValue());
+
+ // Updating resource category
+ updatedResourceDetails = defineUpdateResourceWithNonUpdatableFields(resourceBeforeUpdate);
+ updatedResourceDetails.addCategory("");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ resourceList = new ArrayList<String>();
+ resourceList.add(0, "Resource");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CATEGORY.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource", 400, updatedRestResponse.getErrorCode().intValue());
+
+ // Updating resource category
+ updatedResourceDetails = defineUpdateResourceWithNonUpdatableFields(resourceBeforeUpdate);
+ updatedResourceDetails.addCategory("XXXXXX");
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // validate response
+ resourceList = new ArrayList<String>();
+ resourceList.add(0, "Resource");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_CATEGORY.name(), resourceList, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource", 400, updatedRestResponse.getErrorCode().intValue());
+
+ // CheckIn Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkoutResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN); // NOT_CERTIFIED_CHECKIN
+ assertEquals("Check response code after checkin resource", 200, checkoutResource.getErrorCode().intValue());
+
+ // Update resource Category
+ updatedResourceDetails = defineUpdateResourceWithNonUpdatableFields(resourceBeforeUpdate);
+ updatedResourceDetails.addCategory(ServiceCategoriesEnum.VOIP.getValue());
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+ // verify response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), Empty_List, updatedRestResponse.getResponse());
+ assertEquals("Check response code after updating resource", 409, updatedRestResponse.getErrorCode().intValue());
+
+ // CheckIn Resource
+ logger.debug("Changing resource life cycle ");
+ RestResponse checkinResource = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT); // NOT_CERTIFIED_CHECKIN
+ assertNotNull("check response object is not null after checkout resource", checkoutResource);
+ assertNotNull("check error code exists in response after checkIn resource", checkoutResource.getErrorCode());
+ assertEquals("Check response code after checkin resource", 200, checkoutResource.getErrorCode().intValue());
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, uniqueID);
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(resourceDetails, getRestResponse);
+
+ }
+
+ @Test
+ public void UpdateResourceCategorySuccessfully() throws Exception {
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after update resource", 201, restResponse.getErrorCode().intValue());
+ Resource resourceBeforeUpdate = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ // Update resource Category Successfully
+ ResourceReqDetails updatedResourceDetails = resourceDetails;
+
+ updatedResourceDetails.removeAllCategories();
+ updatedResourceDetails.addCategoryChain(ResourceCategoryEnum.APPLICATION_L4_DATABASE.getCategory(), ResourceCategoryEnum.APPLICATION_L4_DATABASE.getSubCategory());
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, resourceDetails.getUniqueId(), "");
+
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, updatedRestResponse.getErrorCode().intValue());
+ // parseResponseAndValidateNonUpdatable(updatedResourceDetails,
+ // updatedRestResponse);
+ parseResponseAndValidate(updatedResourceDetails, updatedRestResponse);
+
+ // validate category updated
+ assertTrue(updatedResourceDetails.getCategories().get(0).getName().equals(ResourceCategoryEnum.APPLICATION_L4_DATABASE.getCategory()));
+
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceDetails.getUniqueId());
+ assertNotNull("check response object is not null after update resource", getRestResponse);
+ parseResponseAndValidate(updatedResourceDetails, getRestResponse);
+
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncModifierDetails, updatedResourceDetails.getName(), "0.1");
+ }
+
+ // Benny
+
+ @Test
+ public void Validation_UpdateIcon() throws Exception {
+ // Fields to update (Forbidden)
+ String _updatedIcon = "mySecondIcon.Jpg";
+
+ // administrator permissions
+ User sdncAdminModifierDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // define and create resource
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncAdminModifierDetails, resourceDetails.getName(), "0.1");
+
+ RestResponse restResponse = createResource(sdncAdminModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ String resourceName = resourceDetails.getName();
+
+ // update metadata details
+ ResourceReqDetails updatedResourceDetails = defineUpdatedResourse(resourceName);
+ // change icon of metadata
+ updatedResourceDetails.setIcon(_updatedIcon);
+ // PUT request
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncAdminModifierDetails, resourceDetails.getUniqueId(), "");
+
+ // validate response
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_INVALID_ICON.name(), Arrays.asList("Resource"), updatedRestResponse.getResponse());
+
+ // empty icon
+ _updatedIcon = "";
+ updatedResourceDetails.setIcon(_updatedIcon);
+ updatedRestResponse = ResourceRestUtils.updateResourceMetadata(updatedResourceDetails, sdncAdminModifierDetails, resourceDetails.getUniqueId(), "");
+ assertNotNull("check response object is not null after update resource", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_ICON.name(), Arrays.asList("Resource"), updatedRestResponse.getResponse());
+
+ // get resource with original name. original metadata should be returned
+ RestResponse getRestResponse = ResourceRestUtils.getResource(sdncAdminModifierDetails, resourceDetails.getUniqueId());
+ // validate response
+ assertNotNull("check response object is not null after get resource", getRestResponse);
+ assertNotNull("check error code exists in response after get resource", getRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", 200, getRestResponse.getErrorCode().intValue());
+
+ // parse updated response to javaObject
+ Resource getResourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(getRestResponse.getResponse());
+ // validate that metadata was not changed
+ ResourceValidationUtils.validateResourceReqVsResp(resourceDetails, getResourceRespJavaObject);
+
+ ResourceRestUtils.deleteResourceByNameAndVersion(sdncAdminModifierDetails, updatedResourceDetails.getName(), "0.1");
+
+ }
+
+ @Test
+ public void UpdateResourceTypeSuccess() throws Exception {
+ // LCS is CheckOut
+ String newResourceType = ResourceTypeEnum.VL.toString();
+ String currentResourceType = resourceDetails.getResourceType();
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ resourceDetails.setResourceType(newResourceType);
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResourceJavaObject.getUniqueId(), "");
+ assertEquals("Check response code after create resource", 200, updatedRestResponse.getErrorCode().intValue());
+ Resource updatedResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(updatedRestResponse.getResponse());
+ // assertTrue("Check resource type after update resource",
+ // updatedResourceJavaObject.getResourceType().toString().equals(resourceType));
+ assertTrue("Check resource type after update resource", updatedResourceJavaObject.getResourceType().toString().equals(currentResourceType));
+
+ }
+
+ @Test
+ public void UpdateResourceTypeAndNameSuccess() throws Exception {
+ // LCS is CheckOut
+ String newResourceType = ResourceTypeEnum.VL.toString();
+ String currentResourceType = resourceDetails.getResourceType();
+ String newResourceName = "new Name";
+
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ resourceDetails.setResourceType(newResourceType);
+ resourceDetails.setName(newResourceName);
+ List<String> tags = resourceDetails.getTags();
+ tags.add(newResourceName);
+ resourceDetails.setTags(tags);
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResourceJavaObject.getUniqueId(), "");
+ assertEquals("Check response code after create resource", 200, updatedRestResponse.getErrorCode().intValue());
+ Resource updatedResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(updatedRestResponse.getResponse());
+ assertTrue("Check resource type after update resource", updatedResourceJavaObject.getResourceType().toString().equals(currentResourceType));
+ assertTrue("Check resource name after update resource", updatedResourceJavaObject.getName().equals(newResourceName));
+
+ }
+
+ @Test
+ public void UpdateResourceTypeAfterResourceCertification() throws Exception {
+
+ String newResourceType = ResourceTypeEnum.VF.toString();
+ String currentResourceType = resourceDetails.getResourceType();
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ resourceDetails.setResourceType(newResourceType);
+ restResponse = LifecycleRestUtils.certifyResource(resourceDetails);
+ assertEquals("Check response code after resource CheckIn", 200, restResponse.getErrorCode().intValue());
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after resource CheckIn", 200, restResponse.getErrorCode().intValue());
+ currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResourceJavaObject.getUniqueId(), "");
+ assertEquals("Check response code after create resource", 200, updatedRestResponse.getErrorCode().intValue());
+ Resource updatedResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(updatedRestResponse.getResponse());
+ // assertTrue("Check resource type after update resource",
+ // updatedResourceJavaObject.getResourceType().toString().equals(newResourceType));
+ assertTrue("Check resource type after update resource", updatedResourceJavaObject.getResourceType().toString().equals(currentResourceType));
+
+ }
+
+ @Test
+ public void UpdateResourceTypeCheckInLCS() throws Exception {
+
+ String resourceType = ResourceTypeEnum.VL.toString();
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ resourceDetails.setResourceType(resourceType);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum.CHECKIN);
+ assertEquals("Check response code after resource CheckIn", 200, restResponse.getErrorCode().intValue());
+
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResourceJavaObject.getUniqueId(), "");
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+
+ assertNotNull("check response object is not null after create resouce", updatedRestResponse);
+ assertNotNull("check error code exists in response after create resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), updatedRestResponse.getErrorCode());
+
+ List<String> variables = new ArrayList<>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResourceTypeCertifiedLCS() throws Exception {
+
+ String resourceType = ResourceTypeEnum.VL.toString();
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ restResponse = LifecycleRestUtils.certifyResource(resourceDetails);
+ assertEquals("Check response code after resource CheckIn", 200, restResponse.getErrorCode().intValue());
+
+ resourceDetails.setResourceType(resourceType);
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResourceJavaObject.getUniqueId(), "");
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+
+ assertNotNull("check response object is not null after create resouce", updatedRestResponse);
+ assertNotNull("check error code exists in response after create resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), updatedRestResponse.getErrorCode());
+
+ List<String> variables = new ArrayList<>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables, updatedRestResponse.getResponse());
+
+ }
+
+ @Test
+ public void UpdateResourceTypeInvalidType() throws Exception {
+
+ String resourceType = "INVALID TYPE";
+ RestResponse restResponse = createResource(sdncModifierDetails, resourceDetails);
+ assertEquals("Check response code after create resource", 201, restResponse.getErrorCode().intValue());
+ Resource currentResourceJavaObject = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ resourceDetails.setResourceType(resourceType);
+ RestResponse updatedRestResponse = ResourceRestUtils.updateResourceMetadata(resourceDetails, sdncModifierDetails, currentResourceJavaObject.getUniqueId(), "");
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_CONTENT.name());
+
+ assertNotNull("check response object is not null after update resouce", updatedRestResponse);
+ assertNotNull("check error code exists in response after update resource", updatedRestResponse.getErrorCode());
+ assertEquals("Check response code after update resource", errorInfo.getCode(), updatedRestResponse.getErrorCode());
+
+ List<String> variables = new ArrayList<>();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), variables, updatedRestResponse.getResponse());
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VFResourceInstanceNameCRUD.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VFResourceInstanceNameCRUD.java
new file mode 100644
index 0000000000..895390f764
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VFResourceInstanceNameCRUD.java
@@ -0,0 +1,480 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+public class VFResourceInstanceNameCRUD extends ComponentBaseTest {
+
+ protected static ServiceReqDetails serviceDetails;
+ protected static ResourceReqDetails resourceDetailsVFC;
+ protected static ResourceReqDetails resourceDetailsVL;
+ protected static ResourceReqDetails resourceDetailsVF;
+ protected static ResourceReqDetails resourceDetailsCP;
+ protected static ComponentInstanceReqDetails resourceInstanceReqDetailsVF;
+ protected static ComponentInstanceReqDetails resourceInstanceReqDetailsVFC;
+ protected static ComponentInstanceReqDetails resourceInstanceReqDetailsVL;
+ protected static ComponentInstanceReqDetails resourceInstanceReqDetailsCP;
+ protected static User sdncDesignerDetails1;
+ protected static User sdncTesterDeatails1;
+ protected static User sdncAdminDetails1;
+ protected static ArtifactReqDetails heatArtifactDetails;
+ protected static ArtifactReqDetails defaultArtifactDetails;
+ protected static int maxLength = 50;
+ protected static Resource resourceVF = null;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public VFResourceInstanceNameCRUD() {
+ super(name, VFResourceInstanceNameCRUD.class.getName());
+ }
+
+ @BeforeMethod
+
+ public void init() throws Exception {
+
+ // serviceDetails = ElementFactory.getDefaultService();
+ // resourceDetailsVFC =
+ // ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VFC.toString(),
+ // "resourceVFC");
+ // resourceDetailsVF =
+ // ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF.toString(),
+ // "resourceVF3");
+ // resourceDetailsVL =
+ // ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VL.toString(),
+ // "resourceVL");
+ // resourceDetailsCP =
+ // ElementFactory.getDefaultResourceByType(ResourceTypeEnum.CP.toString(),
+ // "resourceCP");
+ sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncAdminDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // heatArtifactDetails =
+ // ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+
+ Either<Resource, RestResponse> resourceDetailsCP_01e = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.CP, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsCP_01e.left().value(), UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CHECKIN, true);
+ resourceDetailsCP = new ResourceReqDetails(resourceDetailsCP_01e.left().value());
+ Either<Resource, RestResponse> resourceDetailsVL_01e = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VL, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsVL_01e.left().value(), UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CHECKIN, true);
+ resourceDetailsVL = new ResourceReqDetails(resourceDetailsVL_01e.left().value());
+ Either<Resource, RestResponse> resourceDetailsVF_01e = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF = new ResourceReqDetails(resourceDetailsVF_01e.left().value());
+ Either<Resource, RestResponse> resourceDetailsVFC_01e = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFC_01e.left().value(), UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CHECKIN, true);
+ resourceDetailsVFC = new ResourceReqDetails(resourceDetailsVFC_01e.left().value());
+
+ resourceInstanceReqDetailsVFC = ElementFactory.getDefaultComponentInstance("VFC", resourceDetailsVFC);
+ resourceInstanceReqDetailsVF = ElementFactory.getDefaultComponentInstance("VF", resourceDetailsVF);
+ resourceInstanceReqDetailsVL = ElementFactory.getDefaultComponentInstance("VL", resourceDetailsVL);
+ resourceInstanceReqDetailsCP = ElementFactory.getDefaultComponentInstance("CP", resourceDetailsCP);
+ sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncAdminDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ }
+
+ @Test
+ public void addResourceInstanceToVF() throws Exception {
+
+ createVFWithCertifiedResourceInstance(resourceDetailsCP, resourceInstanceReqDetailsCP);
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsCP.getName() + " 1");
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ @Test
+ public void updateResourceInstanceName() throws Exception {
+
+ // update resource instance name
+ String resourceInstanceUpdatedName = "resource New 2";
+
+ ResourceReqDetails updatedResourceDetailsVLC = changeResouceName(resourceDetailsVFC,
+ resourceInstanceUpdatedName);
+ createVFWithCertifiedResourceInstance(updatedResourceDetailsVLC, resourceInstanceReqDetailsVFC);
+
+ resourceInstanceReqDetailsVFC.setName(resourceInstanceUpdatedName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetailsVFC, sdncDesignerDetails1, resourceVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ assertTrue(updateResourceInstanceResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceUpdatedName);
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ @Test
+ public void updateResourceInstanceNameToNextGeneratedName() throws Exception {
+
+ // update resource instance name
+ String resourceInstanceUpdatedName = resourceInstanceReqDetailsCP.getName() + " 2";
+
+ ResourceReqDetails updatedResourceDetailsVL = changeResouceName(resourceDetailsVL, resourceInstanceUpdatedName);
+ createVFWithCertifiedResourceInstance(updatedResourceDetailsVL, resourceInstanceReqDetailsVL);
+ resourceInstanceReqDetailsCP.setName(resourceInstanceUpdatedName);
+
+ // add second resource instance
+ RestResponse response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetailsCP,
+ sdncDesignerDetails1, resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsVL.getName() + " 1");
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsCP.getName() + " 2");
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ @Test
+ public void normolizeUpdatedResourceInstanceName() throws Exception {
+
+ String resourceInstanceUpdatedName = "resource new - .2";
+ String normalizedName = "resourcenew2";
+
+ ResourceReqDetails updatedResourceDetailsVL = changeResouceName(resourceDetailsVL, resourceInstanceUpdatedName);
+
+ createVFWithCertifiedResourceInstance(updatedResourceDetailsVL, resourceInstanceReqDetailsVL);
+ // update resource instance name
+ resourceInstanceReqDetailsCP.setName(resourceInstanceUpdatedName);
+
+ // add second resource instance
+ RestResponse response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetailsCP,
+ sdncDesignerDetails1, resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsVL.getName() + " 1");
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsCP.getName() + " 2");
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ @Test
+ public void updatedResourceInstanceNameToEmpty() throws Exception {
+
+ createVFWithCertifiedResourceInstance(resourceDetailsVL, resourceInstanceReqDetailsVL);
+ String resourceInstanceUpdatedName = "";
+ String resourceInstancePreviousName = resourceDetailsCP.getName();
+
+ // add second resource instance
+ RestResponse response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetailsCP,
+ sdncDesignerDetails1, resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ resourceInstanceReqDetailsCP.setName(resourceInstanceUpdatedName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails1, resourceVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code after RI update request", 200,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ // change request
+ // ErrorInfo errorInfo =
+ // Utils.parseYaml(ActionStatus.MISSING_COMPONENT_NAME.name());
+ // utils.validateResponseCode(updateResourceInstanceResponse,
+ // errorInfo.getCode(), "update resource instance");
+ //
+ // List<String> variables = Arrays.asList("Resource Instance");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_COMPONENT_NAME.name(),
+ // variables, updateResourceInstanceResponse.getResponse());
+
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsVL.getName() + " 1");
+ resourceInstanceExpectedListName.add(resourceInstancePreviousName + " 3");
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ @Test
+ public void updatedResourceNameLengthExceedMaximumCharacters() throws Exception {
+
+ String resourceInstancePreviousName = resourceDetailsCP.getName();
+ // update resource instance name
+ String resourceInstanceUpdatedName = "a";
+ for (int i = 0; i < maxLength; i++) {
+ resourceInstanceUpdatedName += "b";
+ }
+ // ResourceReqDetails updatedResourceDetailsVL =
+ // changeResouceName(resourceDetailsVL, resourceInstanceUpdatedName);
+
+ createVFWithCertifiedResourceInstance(resourceDetailsVL, resourceInstanceReqDetailsVL);
+ // add second resource instance
+ RestResponse response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetailsCP,
+ sdncDesignerDetails1, resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ String prevName = resourceInstanceReqDetailsCP.getName();
+ resourceInstanceReqDetailsCP.setName(resourceInstanceUpdatedName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails1, resourceVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT.name());
+ // utils.validateResponseCode(updateResourceInstanceResponse,
+ // errorInfo.getCode(), "update resource instance");
+
+ List<String> variables = Arrays.asList("Resource Instance", "50");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT.name(), variables,
+ updateResourceInstanceResponse.getResponse());
+
+ resourceInstanceReqDetailsCP.setName(prevName);
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsVL.getName() + " 1");
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsCP.getName() + " 2");
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ @Test
+ public void updatedResourceNameWithUnSupportedCharacters() throws Exception {
+
+ createVFWithCertifiedResourceInstance(resourceDetailsVL, resourceInstanceReqDetailsVL);
+ String resourceInstancePreviousName = resourceDetailsCP.getName();
+ // update resource instance name
+ String resourceInstanceUpdatedName = "a???<>";
+
+ // add second resource instance
+ RestResponse response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetailsCP,
+ sdncDesignerDetails1, resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ String prevValue = resourceInstanceReqDetailsCP.getName();
+ resourceInstanceReqDetailsCP.setName(resourceInstanceUpdatedName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails1, resourceVF.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_COMPONENT_NAME.name());
+ // ResourceRestUtils.validateResponseCode(updateResourceInstanceResponse,
+ // errorInfo.getCode(), "update resource instance");
+
+ List<String> variables = Arrays.asList("Resource Instance");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_COMPONENT_NAME.name(), variables,
+ updateResourceInstanceResponse.getResponse());
+
+ resourceInstanceReqDetailsCP.setName(prevValue);
+
+ // validate RI name
+ List<ComponentInstance> resourceInstances = resourceVF.getComponentInstances();
+ List<String> resourceInstanceListName = new ArrayList<String>();
+ for (int i = 0; i < resourceInstances.size(); i++) {
+ resourceInstanceListName.add(resourceInstances.get(i).getName());
+ }
+ List<String> resourceInstanceExpectedListName = new ArrayList<String>();
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsVL.getName() + " 1");
+ resourceInstanceExpectedListName.add(resourceInstanceReqDetailsCP.getName() + " 2");
+ String message = "resource instance name";
+ Utils.compareArrayLists(resourceInstanceListName, resourceInstanceExpectedListName, message);
+
+ }
+
+ private static ResourceReqDetails changeResouceName(ResourceReqDetails resourceDet,
+ String resourceInstanceUpdatedName) throws Exception {
+
+ ResourceReqDetails updatedResourceDetails = new ResourceReqDetails();
+ updatedResourceDetails = resourceDet;
+ updatedResourceDetails.setName(resourceInstanceUpdatedName);
+ List<String> tags = new ArrayList<String>();
+ tags.add(resourceInstanceUpdatedName);
+ updatedResourceDetails.setTags(tags);
+ Gson gson = new Gson();
+ String updatedResourceBodyJson = gson.toJson(updatedResourceDetails);
+ RestResponse response = LifecycleRestUtils.changeResourceState(resourceDet, sdncDesignerDetails1,
+ resourceDet.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertTrue("change LS state to CHECKOUT, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ response = ResourceRestUtils.updateResourceMetadata(updatedResourceBodyJson, sdncDesignerDetails1,
+ updatedResourceDetails.getUniqueId());
+ assertEquals("Check response code after updateresource name", 200, response.getErrorCode().intValue());
+ response = LifecycleRestUtils.changeResourceState(updatedResourceDetails, sdncDesignerDetails1,
+ resourceDet.getVersion(), LifeCycleStatesEnum.CHECKIN);
+
+ return updatedResourceDetails;
+
+ }
+
+ // private Component changeResouceName(Resource resourceDet, String
+ // resourceInstanceUpdatedName) throws Exception{
+ //
+ // User defaultUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // Resource updatedResourceDetails = resourceDet;
+ // RestResponse response =
+ // LifecycleRestUtils.changeComponentState(updatedResourceDetails,
+ // defaultUser, LifeCycleStatesEnum.CHECKOUT, "state changed");
+ // assertTrue("change LS state to CHECKOUT, returned status:" +
+ // response.getErrorCode(),response.getErrorCode() == 200);
+ // updatedResourceDetails.setName(resourceInstanceUpdatedName);
+ // List<String> tags = new ArrayList<String>();
+ // tags.add(resourceInstanceUpdatedName);
+ // updatedResourceDetails.setTags(tags);
+ // Gson gson = new Gson();
+ // ResourceReqDetails resourceReqDetails = new
+ // ResourceReqDetails(updatedResourceDetails);
+ // String updatedResourceBodyJson = gson.toJson(resourceReqDetails);
+ // response = ResourceRestUtils.updateResource(updatedResourceBodyJson,
+ // defaultUser, updatedResourceDetails.getUniqueId());
+ // assertEquals("Check response code after updateresource name", 200,
+ // response.getErrorCode().intValue());
+ // response =
+ // LifecycleRestUtils.changeComponentState(updatedResourceDetails,
+ // defaultUser, LifeCycleStatesEnum.CHECKIN, "state changed");
+ // assertEquals("Check response code after updateresource name", 200,
+ // response.getErrorCode().intValue());
+ //
+ // return updatedResourceDetails;
+ //
+ // }
+
+ private void createVFWithCertifiedResourceInstance(ResourceReqDetails resourceDetails,
+ ComponentInstanceReqDetails resourceInstanceReqDetails) throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails1,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after CHECKOUT", 200, response.getErrorCode().intValue());
+
+ // add heat artifact to resource and certify
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1,
+ resourceDetails.getUniqueId());
+ assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ response = LifecycleRestUtils.certifyResource(resourceDetails);
+ assertEquals("Check response code after CERTIFY request", 200, response.getErrorCode().intValue());
+
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ resourceInstanceReqDetails.setComponentUid(resourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, sdncDesignerDetails1,
+ resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+ }
+
+ protected Resource convertResourceGetResponseToJavaObject(ResourceReqDetails resourceDetails) throws IOException {
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails1);
+ assertEquals("Check response code after get resource", 200, response.getErrorCode().intValue());
+ return ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ValidateExtendedVfData.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ValidateExtendedVfData.java
new file mode 100644
index 0000000000..37e7539fd9
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/ValidateExtendedVfData.java
@@ -0,0 +1,315 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.List;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.AssocType;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import fj.data.Either;
+
+public class ValidateExtendedVfData extends ComponentBaseTest {
+
+ protected Resource resourceDetailsVF;
+ protected Resource resourceDetailsCP_01;
+ protected Resource resourceDetailsVL_01;
+ protected Resource resourceDetailsVFCcomp;
+
+ protected User sdncUserDetails;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ValidateExtendedVfData() {
+ super(name, ValidateExtendedVfData.class.getName());
+ }
+
+ @BeforeMethod
+ public void create() throws Exception {
+
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ Either<Resource, RestResponse> resourceDetailsVFe = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF = resourceDetailsVFe.left().value();
+ Either<Resource, RestResponse> resourceDetailsCP_01e = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP, NormativeTypesEnum.PORT,
+ ResourceCategoryEnum.GENERIC_DATABASE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsCP_01 = resourceDetailsCP_01e.left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsCP_01, UserRoleEnum.DESIGNER,
+ true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_VOL, resourceDetailsCP_01,
+ UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_VOL, resourceDetailsCP_01,
+ UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_NET, resourceDetailsCP_01,
+ UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, resourceDetailsCP_01, UserRoleEnum.DESIGNER,
+ true, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsCP_01, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFY, true);
+ Either<Resource, RestResponse> resourceDetailsVL_01e = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VL, NormativeTypesEnum.NETWORK,
+ ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVL_01 = resourceDetailsVL_01e.left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVL_01, UserRoleEnum.DESIGNER,
+ true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_VOL, resourceDetailsVL_01,
+ UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_VOL, resourceDetailsVL_01,
+ UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_NET, resourceDetailsVL_01,
+ UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, resourceDetailsVL_01, UserRoleEnum.DESIGNER,
+ true, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsVL_01, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFY, true);
+
+ Either<Resource, RestResponse> resourceDetailsVFCcompE = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC, NormativeTypesEnum.COMPUTE,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVFCcomp = resourceDetailsVFCcompE.left().value();
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFY, true);
+
+ ComponentInstance resourceDetailsCP_01ins = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceDetailsCP_01, resourceDetailsVF,
+ UserRoleEnum.DESIGNER, true)
+ .left().value();
+ ComponentInstance resourceDetailsVL_01ins = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceDetailsVL_01, resourceDetailsVF,
+ UserRoleEnum.DESIGNER, true)
+ .left().value();
+ ComponentInstance resourceDetailsVFCcomp_ins = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(resourceDetailsVFCcomp, resourceDetailsVF,
+ UserRoleEnum.DESIGNER, true)
+ .left().value();
+
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF, resourceDetailsCP_01ins,
+ resourceDetailsVL_01ins, AssocType.LINKABLE.getAssocType(), UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF, resourceDetailsCP_01ins,
+ resourceDetailsVFCcomp_ins, AssocType.BINDABLE.getAssocType(), UserRoleEnum.DESIGNER, true);
+
+ }
+
+ @Test
+ public void getResourceLatestVersion() throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LC state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ // resourceDetailsVF =
+ // AtomicOperationUtils.getResourceObject(resourceDetailsVF,
+ // UserRoleEnum.DESIGNER);
+ RestResponse getResourceLatestVersionResponse = ResourceRestUtils.getResourceLatestVersionList(sdncUserDetails);
+ assertTrue("response code is not 200, returned :" + getResourceLatestVersionResponse.getErrorCode(),
+ getResourceLatestVersionResponse.getErrorCode() == 200);
+
+ List<Resource> resourceList = ResourceRestUtils
+ .restResponseToResourceObjectList(getResourceLatestVersionResponse.getResponse());
+ Resource resource = ResourceRestUtils.getResourceObjectFromResourceListByUid(resourceList,
+ resourceDetailsVF.getUniqueId());
+
+ callAllCheckMethods(resource);
+ }
+
+ @Test
+ public void getFollowedResources() throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LC state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ // resourceDetailsVF =
+ // AtomicOperationUtils.getResourceObject(resourceDetailsVF,
+ // UserRoleEnum.DESIGNER);
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+
+ RestResponse getFollowedResourcesResponse = ResourceRestUtils.getFollowedList(sdncUserDetails);
+ String json = getFollowedResourcesResponse.getResponse();
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(json);
+ JSONArray resources = (JSONArray) jsonResp.get("resources");
+
+ List<Resource> resourceList = ResourceRestUtils.restResponseToResourceObjectList(resources.toString());
+ Resource resource = ResourceRestUtils.getResourceObjectFromResourceListByUid(resourceList,
+ resourceDetailsVF.getUniqueId());
+ // TODO if get followed list Api should return full object data?
+ // callAllCheckMethods(resource);
+ }
+
+ @Test
+ public void lifeCycleChekInRequest() throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LC state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ // resourceDetailsVF =
+ // AtomicOperationUtils.getResourceObject(resourceDetailsVF,
+ // UserRoleEnum.DESIGNER);
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ callAllCheckMethods(resource);
+ }
+
+ @Test
+ public void lifeCycleChekOutRequest() throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LC state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertTrue("change LC state to CHECKOUT, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ callAllCheckMethods(resource);
+ }
+
+ @Test
+ public void lifeCycleRequestForCertification() throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LC state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ response = LifecycleRestUtils.changeComponentState(resourceDetailsVF, sdncUserDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertTrue("change LC state to CERTIFICATIONREQUEST, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ callAllCheckMethods(resource);
+ }
+
+ @Test
+ public void lifeCycleCertificationRequest() throws Exception {
+
+ RestResponse response = AtomicOperationUtils
+ .changeComponentState(resourceDetailsVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, false)
+ .getRight();
+ assertTrue("change LC state to CERTIFY, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ callAllCheckMethods(resource);
+ }
+
+ @Test
+ public void checkGetResourceAfterCertificationRequest() throws Exception {
+
+ RestResponse response = AtomicOperationUtils
+ .changeComponentState(resourceDetailsVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, false)
+ .getRight();
+ assertTrue("change LC state to CERTIFY, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ resourceDetailsVF = AtomicOperationUtils.getResourceObject(resourceDetailsVF, UserRoleEnum.DESIGNER);
+
+ callAllCheckMethods(resourceDetailsVF);
+ }
+
+ @Test
+ public void updateResourceMetadata() throws Exception {
+
+ resourceDetailsVF.setDescription("stamStam");
+ ResourceReqDetails resourceDetailsVFreqD = new ResourceReqDetails(resourceDetailsVF);
+ RestResponse updateResourceResponse = ResourceRestUtils.updateResourceMetadata(resourceDetailsVFreqD,
+ sdncUserDetails, resourceDetailsVF.getUniqueId());
+ assertTrue("response code is not 200, returned :" + updateResourceResponse.getErrorCode(),
+ updateResourceResponse.getErrorCode() == 200);
+
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(updateResourceResponse.getResponse());
+
+ callAllCheckMethods(resource);
+ }
+
+ private void checkResourceInstances(Resource resource) {
+ assertNotNull("resource component Instances list is null ", resource.getComponentInstances());
+ assertTrue("resource component Instances list is empty ", !resource.getComponentInstances().equals(""));
+ }
+
+ private void checkResourceInstancesProperties(Resource resource) {
+ assertNotNull("component Instances properies list is null ", resource.getComponentInstancesProperties());
+ assertTrue("component Instances properies list is empty ",
+ !resource.getComponentInstancesProperties().equals(""));
+ }
+
+ private void checkResourceInstancesRelations(Resource resource) {
+ assertNotNull("component Instances Relations list is null ", resource.getComponentInstancesRelations());
+ assertTrue("component Instances Relations list is empty ",
+ !resource.getComponentInstancesRelations().equals(""));
+ }
+
+ private void checkResourceCapabilities(Resource resource) {
+ assertNotNull("component Instances Capabilities list is null ", resource.getCapabilities());
+ assertTrue("component Instances Capabilities list is empty ", !resource.getCapabilities().equals(""));
+ }
+
+ private void checkResourceRequirements(Resource resource) {
+ assertNotNull("component Instances Requirements list is null ", resource.getRequirements());
+ assertTrue("component Instances Requirements list is empty ", !resource.getRequirements().equals(""));
+ }
+
+ private void callAllCheckMethods(Resource resource) {
+
+ checkResourceInstances(resource);
+ checkResourceInstancesProperties(resource);
+ checkResourceInstancesRelations(resource);
+ checkResourceCapabilities(resource);
+ checkResourceRequirements(resource);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VfComponentInstanceCRUDTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VfComponentInstanceCRUDTest.java
new file mode 100644
index 0000000000..ea8b89200e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/resource/VfComponentInstanceCRUDTest.java
@@ -0,0 +1,1792 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resource;
+
+import static org.junit.Assert.assertTrue;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_ALREADY_EXISTS;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_COMPONENT_NAME_EXCEEDS_LIMIT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_DELETE;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_INVALID_CONTENT;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_NOT_FOUND;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentInstanceBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.BaseValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class VfComponentInstanceCRUDTest extends ComponentInstanceBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public VfComponentInstanceCRUDTest() {
+ super(name, VfComponentInstanceCRUDTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+ init();
+ createComponents();
+ }
+
+ // CREATE Resource
+ private void createComponents() throws Exception {
+ createAtomicResource(resourceDetailsVFC_01);
+ LifecycleRestUtils.changeResourceState(resourceDetailsVFC_01, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createAtomicResource(resourceDetailsVFC_02);
+ LifecycleRestUtils.changeResourceState(resourceDetailsVFC_02, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createAtomicResource(resourceDetailsCP_01);
+ LifecycleRestUtils.changeResourceState(resourceDetailsCP_01, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createAtomicResource(resourceDetailsCP_02);
+ LifecycleRestUtils.changeResourceState(resourceDetailsCP_02, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createAtomicResource(resourceDetailsVL_01);
+ LifecycleRestUtils.changeResourceState(resourceDetailsVL_01, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createAtomicResource(resourceDetailsVL_02);
+ LifecycleRestUtils.changeResourceState(resourceDetailsVL_02, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createVF(resourceDetailsVF_02);
+ }
+
+ @Test
+ public void createVfcInstanceByDesigner() throws Exception {
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVFC_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test(enabled = false) // DE189419
+ public void createInstanceOfVfToItself() throws Exception {
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVF_02);
+ createAtomicResourceInstance = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertTrue(createAtomicResourceInstance.getErrorCode() == STATUS_CODE_NOT_FOUND);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void createVfcInstanceByAdmin() throws Exception {
+ User user = sdncAdminDetails;
+ createVF(resourceDetailsVF_01, user);
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_01,
+ resourceDetailsVFC_01, user);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_01, 1, 0);
+ }
+
+ @Test
+ public void createCpInstance() throws Exception {
+ // Create CP instance
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void createVlInstance() throws Exception {
+ // Create VL instance
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void updateResourceInstanceNameLessMaxLegth() throws Exception {
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("xxxxXthisXstringxisx49XcharcatersXlengthXxxxxxxxx");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+
+ }
+
+ @Test
+ public void updateInstanceNameExceedMaxLegth() throws Exception {
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("xxxxXthisXstringxisx51XcharcatersXlengthXxxxxxxxxxx");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_COMPONENT_NAME_EXCEEDS_LIMIT,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void updateResourceInstanceNameHasMaxLegth() throws Exception {
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("xxxxXthisXstringxisx50XcharcatersXlengthXxxxxxxxxx");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ }
+
+ @Test
+ public void resourceInstanceNameIsEmpty() throws Exception {
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ resourceInstanceReqDetails.setName("");
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "name");
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "1").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance Name ", (resourceDetailsVFC_01.getName() + " 1"),
+ instanceName);
+ }
+
+ @Test
+ public void resourceInstanceNameIsNull() throws Exception {
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ resourceInstanceReqDetails.setName(null);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "name");
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "1").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance Name ", (resourceDetailsVFC_01.getName() + " 1"),
+ instanceName);
+ }
+
+ @Test
+ public void resourceInstanceNameValidation01() throws Exception {
+ // 2 Instances
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "name");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "1").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance Name ", (resourceDetailsVFC_01.getName() + " 1"),
+ instanceName);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ instanceNormalizedName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "name");
+ componentInstance = ResponseParser.parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(),
+ ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "2").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ", (resourceDetailsVFC_01.getName() + " 2"),
+ instanceName);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void resourceInstanceNameValidation02() throws Exception {
+
+ // 2 Instances
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "name");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "1").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance Name ", (resourceDetailsVFC_01.getName() + " 1"),
+ instanceName);
+ resourceInstanceReqDetails = ElementFactory.getComponentResourceInstance(resourceDetailsCP_01);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ instanceNormalizedName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "name");
+ componentInstance = ResponseParser.parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(),
+ ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsCP_01.getName() + "2").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ", (resourceDetailsCP_01.getName() + " 2"),
+ instanceName);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createVfcInstanceByTester() throws Exception { // Response 409
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncTesterDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void createVfcInstance_UserIdIsEmpty() throws Exception {
+
+ User sdncUserDetails = new User();
+ sdncUserDetails.setUserId("");
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncUserDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void createVfcInstance_UserIdIsNonAsdcUser() throws Exception {
+
+ User sdncUserDetails = new User();
+ sdncUserDetails.setUserId("bt4567");
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncUserDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void createAllAtomicInstances() throws Exception {
+
+ // Add to VF resource VFC, CP and VL instances
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVFC_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 3, 0);
+ }
+
+ @Test
+ public void createDefferentVfcInstances() throws Exception {
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVFC_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVFC_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createDefferentCpInstances() throws Exception {
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsCP_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createDefferentVLInstances() throws Exception {
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVL_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createSeveralInstanceOfSameVFC() throws Exception {
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVFC_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVFC_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createSeveralInstanceOfSameVL() throws Exception {
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVL_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createSeveralInstanceOfSameCP() throws Exception {
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void createInstanceOfCpToVfc() throws Exception { // Add to CP to VFC
+ // (not allowed)
+
+ ComponentInstanceReqDetails resourceInstanceReqDetailsCP = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ ComponentInstanceReqDetails resourceInstanceReqDetailsVFC = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails, resourceInstanceReqDetailsVFC.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void createInstanceVfcToCp() throws Exception { // (not allowed)
+
+ ComponentInstanceReqDetails resourceInstanceReqDetailsCP = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ ComponentInstanceReqDetails resourceInstanceReqDetailsVFC = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsVFC, sdncDesignerDetails, resourceInstanceReqDetailsCP.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void createInstanceVlToVfc() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetailsVL = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ ComponentInstanceReqDetails resourceInstanceReqDetailsVFC = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsVFC, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsVL, sdncDesignerDetails, resourceInstanceReqDetailsVFC.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void createInstanceToNonSupportedComponentType() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetailsCP = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetailsCP, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE_INSTANCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_INVALID_CONTENT,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ // ("Create instance without position is allowed")
+ @Test
+ public void createInstanceOfVlWithoutPosXAndPosY() throws Exception { // instance
+ // does
+ // not
+ // have
+ // position
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ resourceInstanceReqDetails.setPosX("");
+ resourceInstanceReqDetails.setPosY("");
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ // Create instance without position is allowed")
+ @Test
+ public void createInstanceOfVlWithPositionNull() throws Exception { // instance
+ // does
+ // not
+ // have
+ // position
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ resourceInstanceReqDetails.setPosX(null);
+ resourceInstanceReqDetails.setPosY(null);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void createResourceInstanceForNonCheckedOutVF() throws Exception {
+
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02, sdncAdminDetails,
+ "0.1", LifeCycleStatesEnum.CHECKIN);
+ resourceDetailsVF_02.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ AssertJUnit.assertEquals("Check response code after create user", STATUS_CODE_SUCCESS,
+ checkInResponse.getErrorCode().intValue());
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createResourceInstanceVfCheckedOutByOtherUser() throws Exception {
+
+ // Admin try to add RI to VF which is checked-Out By Designer
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncAdminDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void createResourceInstanceForNonExistingVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, "blablabla", ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ createResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ // Delete
+ @Test
+ public void deleteVfcInstanceByDesigner() throws Exception {
+
+ // Create RI
+ RestResponse createResourceInstanceResponse = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVFC_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = deleteAtomicInstanceForVF(compInstId, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkDeleteResponse(deleteResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void deleteVfcInstanceByAdmin() throws Exception {
+ createVF(resourceDetailsVF_01, sdncAdminDetails);
+ RestResponse createResourceInstanceResponse = createAtomicInstanceForVF(resourceDetailsVF_01,
+ resourceDetailsVL_01, sdncAdminDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_01, 1, 0);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = deleteAtomicInstanceForVF(compInstId, resourceDetailsVF_01,
+ sdncAdminDetails);
+ ResourceRestUtils.checkDeleteResponse(deleteResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_01, 0, 0);
+ }
+
+ @Test
+ public void deleteCpInstance() throws Exception {
+
+ RestResponse createResourceInstanceResponse = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = deleteAtomicInstanceForVF(compInstId, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkDeleteResponse(deleteResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void deleteVlInstance() throws Exception {
+
+ RestResponse createResourceInstanceResponse = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = deleteAtomicInstanceForVF(compInstId, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkDeleteResponse(deleteResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ }
+
+ @Test
+ public void deleteOneVlInstance() throws Exception {
+
+ // RI-1
+ RestResponse createResourceInstanceResponse = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ // RI-2
+ createResourceInstanceResponse = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVL_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ // Delete Resource instance RI-1
+ RestResponse deleteResourceInstanceResponse = deleteAtomicInstanceForVF(compInstId, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkDeleteResponse(deleteResourceInstanceResponse);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteVfcInstanceCheckedByOtherUser() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncTesterDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteInstanceNonSupportedComponentType() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE_INSTANCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_INVALID_CONTENT,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteInstanceFromNonVF() throws Exception {
+ // RI-1
+
+ ComponentInstanceReqDetails resourceInstanceVlReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceVlReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance1 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance1, ComponentTypeEnum.RESOURCE);
+ // RI-2
+ ComponentInstanceReqDetails resourceInstanceCplReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceCplReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance2 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance2, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ // Delete VL instance from CP instance
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceInstanceCplReqDetails.getUniqueId(),
+ resourceInstanceVlReqDetails.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ }
+
+ @Test
+ public void deleteNonExistingInstanceFromVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceVlReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceVlReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance1 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance1, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ resourceInstanceVlReqDetails.setUniqueId("1234567890");
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceVlReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteCpInstanceFromNonCheckOutVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceCpReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceCpReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance1 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance1, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02, sdncDesignerDetails,
+ "0.1", LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_SUCCESS,
+ checkInResponse.getErrorCode().intValue());
+ resourceDetailsVF_02.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceCpReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteVlInstanceFromNonCheckOutVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceVlReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceVlReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance1 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance1, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02, sdncDesignerDetails,
+ "0.1", LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_SUCCESS,
+ checkInResponse.getErrorCode().intValue());
+ resourceDetailsVF_02.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceVlReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteVfcInstanceFromNonCheckOutVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceVfcReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceVfcReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance1 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance1, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ RestResponse checkInResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02, sdncDesignerDetails,
+ "0.1", LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_SUCCESS,
+ checkInResponse.getErrorCode().intValue());
+ resourceDetailsVF_02.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ // Delete Resource instance
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceVfcReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteVlInstance_UserIdIsNonAsdcUser() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ // Delete Resource instance by non-ASDC User
+ User sdncUserDetails = new User();
+ sdncUserDetails.setUserId("bt4567");
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncUserDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void deleteAlreadyDeletedInstance() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_DELETE,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ deleteCompInstReqCapFromExpected(componentInstance.getUniqueId());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 0, 0);
+ deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(sdncDesignerDetails,
+ resourceDetailsVF_02.getUniqueId(), resourceInstanceReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void reCreateDeletedInstance() throws Exception {
+
+ // 2 Instances
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "name");
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "1").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance Name ", (resourceDetailsVFC_01.getName() + " 1"),
+ instanceName);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ instanceNormalizedName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "name");
+ componentInstance = ResponseParser.parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(),
+ ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "2").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ", (resourceDetailsVFC_01.getName() + " 2"),
+ instanceName);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ // Delete one instance
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), resourceInstanceReqDetails.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_DELETE,
+ deleteResourceInstanceResponse.getErrorCode().intValue());
+ deleteCompInstReqCapFromExpected(componentInstance.getUniqueId());
+ // Create same instance again
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ instanceNormalizedName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ instanceName = ResponseParser.getValueFromJsonResponse(createResourceInstanceResponse.getResponse(), "name");
+ componentInstance = ResponseParser.parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(),
+ ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "3").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance Name ", (resourceDetailsVFC_01.getName() + " 3"),
+ instanceName);
+
+ }
+
+ // Update
+ @Test
+ public void updateVfcInstanceNameByDesigner() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("abcd");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName, "abcd");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, "abcd");
+ }
+
+ @Test
+ public void updateVfcInstanceNameByAdmin() throws Exception {
+ User user = sdncAdminDetails;
+ createVF(resourceDetailsVF_01, user);
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncAdminDetails, resourceDetailsVF_01.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_01, 1, 0);
+ vfcResourceInstanceReqDetails.setName("ABCD E");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncAdminDetails, resourceDetailsVF_01.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName, "abcde");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, "ABCD E");
+ }
+
+ @Test
+ public void updateInstanceNameAllowedCharacters() throws Exception {
+ // Allowed characters: Alphanumeric (a-zA-Z0-9), space (' '), underscore
+ // ('_'), dash ('-'), dot ('.'))
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("Abcd_1234567890-qwert-yuiop.zxcvb");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ // assertEquals("Check if RI normalizedName is correct ",
+ // riNormalizedName, "abcd_1234567890-qwert-yuiop.zxcv" );
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, "Abcd_1234567890-qwert-yuiop.zxcvb");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName,
+ "abcd1234567890qwertyuiopzxcvb");
+
+ }
+
+ @Test
+ public void updateVfcInstanceNameEmpty() throws Exception {
+ // see US534663 In case a designer removes the current resource instance
+ // name then BE has to generate again the "default" resource instance
+ // name
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = "";
+ vfcResourceInstanceReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String instanceNormalizedName = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "name");
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "2").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ", (resourceDetailsVFC_01.getName() + " 2"),
+ instanceName);
+ }
+
+ @Test
+ public void updateVfcInstanceNameNull() throws Exception {
+ // see US534663 In case a designer removes the current resource instance
+ // name then BE has to generate again the "default" resource instance
+ // name
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = null;
+ vfcResourceInstanceReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ final String updateResponse = updateResourceInstanceResponse.getResponse();
+ String instanceNormalizedName = ResponseParser.getValueFromJsonResponse(updateResponse, "normalizedName");
+ String instanceName = ResponseParser.getValueFromJsonResponse(updateResponse, "name");
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ",
+ (resourceDetailsVFC_01.getName() + "2").toLowerCase(), instanceNormalizedName);
+ AssertJUnit.assertEquals("check Resource Instance normalizedName ", (resourceDetailsVFC_01.getName() + " 2"),
+ instanceName);
+ }
+
+ @Test
+ public void updateCpInstanceName() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("AbcD");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName, "abcd");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, "AbcD");
+ }
+
+ @Test
+ public void updateVlInstanceName() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName("ABCD");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName, "abcd");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, "ABCD");
+ }
+
+ @Test
+ public void updateInstanceNameToArleadyExistInstanceName02() throws Exception {
+
+ // Create VFC instance
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance vfcComponentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(vfcComponentInstance, ComponentTypeEnum.RESOURCE);
+ // Create CP instance
+ ComponentInstanceReqDetails cpResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ cpResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance cpComponentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(cpComponentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 2, 0);
+ cpResourceInstanceReqDetails.setName(vfcComponentInstance.getName());
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ cpResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_ALREADY_EXISTS,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void updateInstanceNameMaxLength() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = "Qwertyuiop1234567890asdfAhjklzxcvbnmasdfghjkl12345";
+ vfcResourceInstanceReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName, newName.toLowerCase());
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, newName);
+ }
+
+ @Test
+ public void updateInstanceNameExceedMaxLength() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = "Qwertyuiop1234567890asdfAhjklzxcvbnmasdfghjkl123456";
+ vfcResourceInstanceReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_COMPONENT_NAME_EXCEEDS_LIMIT,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void updateCpInstanceCheckedByOtherUser() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = "Qwertyuiop1234567890";
+ resourceInstanceReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetails, sdncAdminDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void UpdateVfcInstance_UserIdIsNonAsdcUser() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = "Qwertyuiop1234567890";
+ resourceInstanceReqDetails.setName(newName);
+ User nonSdncUserDetails = new User();
+ nonSdncUserDetails.setUserId("bt4567");
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetails, nonSdncUserDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_RESTRICTED_OPERATION,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ }
+
+ @Test
+ public void UpdateResourceInstanceFormNonExistingVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ // LifecycleRestUtils.changeResourceState(resourceDetailsVL_01,
+ // sdncAdminDetails, "0.1", LifeCycleStatesEnum.CHECKIN);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String newName = "Qwertyuiop1234567890";
+ resourceInstanceReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, "blablabla", ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void updateNonExistingInstanceFromVF() throws Exception {
+
+ ComponentInstanceReqDetails resourceInstanceVlReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceVlReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance1 = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance1, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ resourceInstanceVlReqDetails.setUniqueId("1234567890");
+ // String newName= "Qwertyuiop1234567890";
+ // resourceInstanceVlReqDetails.setName(newName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ resourceInstanceVlReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ }
+
+ // Update
+ @Test
+ public void updateVfcInstanceNameAsVfName() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ vfcResourceInstanceReqDetails.setName(resourceDetailsVF_02.getName());
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String resourceNameFromJsonResponse = ResponseParser.getNameFromResponse(updateResourceInstanceResponse);
+ AssertJUnit.assertEquals(resourceNameFromJsonResponse, vfcResourceInstanceReqDetails.getName());
+ String riNormalizedName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "normalizedName");
+ String riName = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "name");
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riNormalizedName,
+ resourceDetailsVF_02.getName().toLowerCase());
+ AssertJUnit.assertEquals("Check if RI normalizedName is correct ", riName, resourceDetailsVF_02.getName());
+ }
+
+ @Test
+ public void updateInstanceNameInvalidCharacters() throws Exception {
+ char invalidChars[] = { '~', '!', '$', '%', '^', '*', '(', ')', '"', '{', '}', '[', ']', '?', '>', '<', '/',
+ '|', '\\', ',' };
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String newName = "Abcd1";
+ String updateName;
+ for (int i = 0; i < invalidChars.length; i++) {
+ updateName = newName + invalidChars[i];
+ vfcResourceInstanceReqDetails.setName(updateName);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_INVALID_CONTENT,
+ updateResourceInstanceResponse.getErrorCode().intValue());
+ }
+ }
+
+ // Update Position
+ @Test
+ public void updateVfcInstancePosition() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String updatePosX = "130";
+ String updatePosY = "180";
+ vfcResourceInstanceReqDetails.setPosX(updatePosX);
+ vfcResourceInstanceReqDetails.setPosY(updatePosY);
+ vfcResourceInstanceReqDetails.setName(null);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String posXFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posX");
+ String posYFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posY");
+ AssertJUnit.assertEquals(posXFromJsonResponse, updatePosX);
+ AssertJUnit.assertEquals(posYFromJsonResponse, updatePosY);
+ }
+
+ @Test
+ public void updateVlInstancePosition() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVL_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String updatePosX = "130";
+ String updatePosY = "180";
+ vfcResourceInstanceReqDetails.setPosX(updatePosX);
+ vfcResourceInstanceReqDetails.setPosY(updatePosY);
+ vfcResourceInstanceReqDetails.setName(null);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String posXFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posX");
+ String posYFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posY");
+ AssertJUnit.assertEquals(posXFromJsonResponse, updatePosX);
+ AssertJUnit.assertEquals(posYFromJsonResponse, updatePosY);
+ }
+
+ @Test
+ public void updateCpInstancePosition() throws Exception {
+
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String updatePosX = "130";
+ String updatePosY = "180";
+ vfcResourceInstanceReqDetails.setPosX(updatePosX);
+ vfcResourceInstanceReqDetails.setPosY(updatePosY);
+ vfcResourceInstanceReqDetails.setName(null);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String posXFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posX");
+ String posYFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posY");
+ AssertJUnit.assertEquals(posXFromJsonResponse, updatePosX);
+ AssertJUnit.assertEquals(posYFromJsonResponse, updatePosY);
+ }
+
+ @Test
+ public void updateInstancePositionNegativePosition() throws Exception {
+
+ ComponentInstanceReqDetails cpResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ cpResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, ComponentTypeEnum.RESOURCE);
+ getComponentAndValidateRIs(resourceDetailsVF_02, 1, 0);
+ String updatePosX = "-100";
+ String updatePosY = "-100";
+ cpResourceInstanceReqDetails.setPosX(updatePosX);
+ cpResourceInstanceReqDetails.setPosY(updatePosY);
+ cpResourceInstanceReqDetails.setName(null);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ cpResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String posXFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posX");
+ String posYFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posY");
+ AssertJUnit.assertEquals(posXFromJsonResponse, updatePosX);
+ AssertJUnit.assertEquals(posYFromJsonResponse, updatePosY);
+ }
+
+ @Test
+ public void updateInstancesPositionSameLocationForBothInstances() throws Exception {
+
+ ComponentInstanceReqDetails cpResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ cpResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ ComponentInstanceReqDetails vfcResourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsVFC_01);
+ createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ String updatePosX = "100";
+ String updatePosY = "500";
+ vfcResourceInstanceReqDetails.setPosX(updatePosX);
+ vfcResourceInstanceReqDetails.setPosY(updatePosY);
+ vfcResourceInstanceReqDetails.setName(null);
+ cpResourceInstanceReqDetails.setPosX(updatePosX);
+ cpResourceInstanceReqDetails.setPosY(updatePosY);
+ cpResourceInstanceReqDetails.setName(null);
+ RestResponse updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ vfcResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ String posXFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posX");
+ String posYFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(), "posY");
+ AssertJUnit.assertEquals(posXFromJsonResponse, updatePosX);
+ AssertJUnit.assertEquals(posYFromJsonResponse, updatePosY);
+ updateResourceInstanceResponse = ComponentInstanceRestUtils.updateComponentInstance(
+ cpResourceInstanceReqDetails, sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(),
+ ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(updateResourceInstanceResponse);
+ posXFromJsonResponse = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "posX");
+ posYFromJsonResponse = ResponseParser.getValueFromJsonResponse(updateResourceInstanceResponse.getResponse(),
+ "posY");
+ AssertJUnit.assertEquals(posXFromJsonResponse, updatePosX);
+ AssertJUnit.assertEquals(posYFromJsonResponse, updatePosY);
+ }
+
+ @Test
+ public void createAllAtomicInstancesTestGetReqCapAPI_suc() throws Exception {
+
+ // Add to VF resource VFC, CP and VL instances
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVFC_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+
+ getVfResourceReqCapUsingAPI(3, 0, sdncDesignerDetails);
+
+ }
+
+ // END of Update
+
+ @Test
+ public void createAllAtomicInstancesTestGetReqCapAPIfailed() throws Exception {
+
+ // Add to VF resource VFC, CP and VL instances
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsVL_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVFC_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ resourceDetailsVF_02.setUniqueId("dummy");
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncAdminDetails,
+ resourceDetailsVF_02);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_NOT_FOUND,
+ getResourceResponse.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void associateInVF() throws Exception {
+
+ ResourceReqDetails resourceDetailsReq = ElementFactory.getDefaultResourceByType("SoftCompRouter",
+ NormativeTypesEnum.SOFTWARE_COMPONENT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS,
+ sdncDesignerDetails.getUserId(), ResourceTypeEnum.VFC); // resourceType
+ // = VFC
+ ResourceReqDetails resourceDetailsCap = ElementFactory.getDefaultResourceByType("MyComput",
+ NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.VFC); // resourceType = VFC
+ createAtomicResource(resourceDetailsReq);
+ LifecycleRestUtils.changeResourceState(resourceDetailsReq, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createAtomicResource(resourceDetailsCap);
+ LifecycleRestUtils.changeResourceState(resourceDetailsCap, sdncAdminDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+
+ RestResponse riReqR = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsReq, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(riReqR);
+ RestResponse riCapR = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsCap, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(riCapR);
+
+ ComponentInstance riReq = ResponseParser.parseToObject(riReqR.getResponse(), ComponentInstance.class);
+ ComponentInstance riCap = ResponseParser.parseToObject(riCapR.getResponse(), ComponentInstance.class);
+
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ resourceDetailsVF_02);
+
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get("tosca.capabilities.Container");
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get("tosca.capabilities.Container");
+
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ requirementDef.setFromNode(riReq.getUniqueId());
+ requirementDef.setToNode(riCap.getUniqueId());
+
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair();
+ pair.setRequirementOwnerId(riReq.getUniqueId());
+ pair.setCapabilityOwnerId(riCap.getUniqueId());
+ pair.setRequirement("host");
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType("tosca.capabilities.Container");
+ pair.setRelationships(relationship);
+ pair.setCapabilityUid(capList.get(0).getUniqueId());
+ pair.setRequirementUid(reqList.get(0).getUniqueId());
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<>();
+ relationships.add(pair);
+ requirementDef.setRelationships(relationships);
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_SUCCESS,
+ associateInstances.getErrorCode().intValue());
+
+ getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ resourceDetailsVF_02);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+
+ List<RequirementDefinition> list = capReqDef.getRequirements().get("tosca.capabilities.Container");
+ AssertJUnit.assertEquals("Check requirement", null, list);
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF_02.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_SUCCESS,
+ dissociateInstances.getErrorCode().intValue());
+
+ getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ resourceDetailsVF_02);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+
+ list = capReqDef.getRequirements().get("tosca.capabilities.Container");
+ AssertJUnit.assertEquals("Check requirement", 1, list.size());
+
+ }
+
+ @Test
+ public void testUnsatisfiedCpReqInVF() throws Exception {
+
+ // Certify all the needed atomic resources
+ RestResponse response = LifecycleRestUtils.certifyResource(resourceDetailsVFC_02);
+ ResourceRestUtils.checkSuccess(response);
+ response = LifecycleRestUtils.certifyResource(resourceDetailsCP_01);
+ ResourceRestUtils.checkSuccess(response);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails,
+ resourceDetailsVF_02.getUniqueId());
+ ResourceRestUtils.checkSuccess(response);
+
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String compInstName = ResponseParser.getNameFromResponse(createAtomicResourceInstance);
+ String cpCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ RestResponse submitForTesting = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02,
+ sdncDesignerDetails, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ String[] variables = new String[] { resourceDetailsVF_02.getName(), "VF", "CP (Connection Point)", compInstName,
+ "requirement", "tosca.capabilities.network.Bindable", "fulfilled" };
+ BaseValidationUtils.checkErrorResponse(submitForTesting,
+ ActionStatus.REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION, variables);
+
+ createAtomicResourceInstance = createAtomicInstanceForVF(resourceDetailsVF_02, resourceDetailsVFC_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String computeCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+ fulfillCpRequirement(resourceDetailsVF_02, cpCompInstId, computeCompInstId, computeCompInstId,
+ sdncDesignerDetails, ComponentTypeEnum.RESOURCE);
+
+ submitForTesting = LifecycleRestUtils.changeResourceState(resourceDetailsVF_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ BaseValidationUtils.checkSuccess(submitForTesting);
+ }
+
+ private void getVfResourceReqCapUsingAPI(int numberOfRIs, int numberOfRelations, User user)
+ throws IOException, Exception {
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncAdminDetails,
+ resourceDetailsVF_02);
+ AssertJUnit.assertEquals("Check response code ", STATUS_CODE_SUCCESS,
+ getResourceResponse.getErrorCode().intValue());
+ // ResourceValidationUtils.validateResp(getResourceResponse,
+ // resourceRespJavaObject);
+ // int numberOfActualRIs = resource.getComponentInstances()!=null ?
+ // resource.getComponentInstances().size() : 0;
+ // int numberOfActualRelations =
+ // resource.getComponentInstancesRelations()!=null ?
+ // resource.getComponentInstancesRelations().size() : 0;
+ // assertEquals("Check number of RIs meet the expected number",
+ // numberOfRIs ,numberOfActualRIs);
+ // assertEquals("Check number of RI relations meet the expected number",
+ // numberOfRelations ,numberOfActualRelations);
+
+ //// get VF actual Capabilities and Requirements and validate according
+ //// to expected
+ Resource vfResource = ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(),
+ Resource.class);
+ verifyReqCap(vfResource);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ChangeServiceDistributionStatusApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ChangeServiceDistributionStatusApiTest.java
new file mode 100644
index 0000000000..c8a20cf44a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ChangeServiceDistributionStatusApiTest.java
@@ -0,0 +1,1008 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ServiceValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ChangeServiceDistributionStatusApiTest extends ComponentBaseTest {
+
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected User sdncDesignerDetails;
+ protected User sdncAdminDetails;
+ protected User sdncGovernorDeatails;
+ protected User sdncTesterDetails;
+ protected User sdncOpsDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+ protected Component resourceDetailsVFCcomp;
+ protected Component serviceDetailsCompp;
+
+ private String userRemarks = "commentTest";
+
+ private List<String> variablesAsList;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ChangeServiceDistributionStatusApiTest() throws Exception {
+ super(name, ChangeServiceDistributionStatusApiTest.class.getName());
+
+ }
+
+ @BeforeMethod
+ public void init() throws Exception {
+
+ variablesAsList = new ArrayList<String>();
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncGovernorDeatails = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ sdncTesterDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncOpsDetails = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+ resourceDetailsVFCcomp = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ true, true);
+
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFY, true);
+ Service serviceServ = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCcomp, serviceServ,
+ UserRoleEnum.DESIGNER, true);
+
+ serviceDetails = new ServiceReqDetails(serviceServ);
+
+ }
+
+ // -----------------------------------------------T E S T
+ // S--------------------------------------------//
+
+ @Test
+ public void approveNotCertifiedService_checkout() throws Exception {
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ validateAudit("DApprove", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+ }
+
+ @Test
+ public void approveNotCertifiedService_checkedin() throws Exception {
+ RestResponse checkinResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals(200, checkinResp.getErrorCode().intValue());
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ validateAudit("DApprove", LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+ }
+
+ @Test
+ public void approveNotCertifiedService_inProgress() throws Exception {
+ RestResponse certReqResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(200, certReqResp.getErrorCode().intValue());
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name());
+ // String auditAction="DApprove";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // version, sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.READY_FOR_CERTIFICATION.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setStatus("403");
+ // expectedResourceAuditJavaObject.setDesc(String.format(errorInfo.getMessageId()
+ // + ": " + errorInfo.getMessage(), version,
+ // serviceDetails.getServiceName()));
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DApprove", LifecycleStateEnum.READY_FOR_CERTIFICATION,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+
+ }
+
+ @Test
+ public void approveNotCertifiedService_readyForCer() throws Exception {
+ approveNotCertifiedService_inProgress();
+ DbUtils.deleteFromEsDbByPattern("_all");
+
+ RestResponse startCertResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(200, startCertResp.getErrorCode().intValue());
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name());
+ // String auditAction="DApprove";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // version, sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setDprevStatus("");
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setStatus("403");
+ // expectedResourceAuditJavaObject.setDesc(String.format(errorInfo.getMessageId()
+ // + ": " + errorInfo.getMessage(), version,
+ // serviceDetails.getServiceName()));
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFICATION_IN_PROGRESS,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+ }
+
+ @Test
+ public void rejectNotCertifiedService_checkeout() throws Exception {
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // version, sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setDprevStatus("");
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setStatus("403");
+ // expectedResourceAuditJavaObject.setDesc(String.format(errorInfo.getMessageId()
+ // + ": " + errorInfo.getMessage(), version,
+ // serviceDetails.getServiceName()));
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+ }
+
+ @Test
+ public void rejectNotCertifiedService_checkedin() throws Exception {
+ RestResponse startCertResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertEquals(200, startCertResp.getErrorCode().intValue());
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // version, sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN.name());
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setDprevStatus("");
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setStatus("403");
+ // expectedResourceAuditJavaObject.setDesc(String.format(errorInfo.getMessageId()
+ // + ": " + errorInfo.getMessage(), version,
+ // serviceDetails.getServiceName()));
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+ }
+
+ @Test
+ public void rejectNotCertifiedService_inProgress() throws Exception {
+ RestResponse startCertResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(200, startCertResp.getErrorCode().intValue());
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // version, sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.READY_FOR_CERTIFICATION.name());
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setDprevStatus("");
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setStatus("403");
+ // expectedResourceAuditJavaObject.setDesc(String.format(errorInfo.getMessageId()
+ // + ": " + errorInfo.getMessage(), version,
+ // serviceDetails.getServiceName()));
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.READY_FOR_CERTIFICATION,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+ }
+
+ @Test
+ public void rejectNotCertifiedService_readyForCer() throws Exception {
+ rejectNotCertifiedService_inProgress();
+ DbUtils.deleteFromEsDbByPattern("_all");
+
+ RestResponse startCertResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(200, startCertResp.getErrorCode().intValue());
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 403, serviceDetails.getVersion());
+
+ variablesAsList = Arrays.asList(serviceDetails.getVersion(), serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name(),
+ variablesAsList, changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // version, sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setDprevStatus("");
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setStatus("403");
+ // expectedResourceAuditJavaObject.setDesc(String.format(errorInfo.getMessageId()
+ // + ": " + errorInfo.getMessage(), version,
+ // serviceDetails.getServiceName()));
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFICATION_IN_PROGRESS,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ "403", ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, sdncAdminDetails);
+
+ }
+
+ @Test
+ public void approveCertifiedService_bysdncGovernorDeatails() throws Exception {
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncGovernorDeatails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(changeDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, null, null, sdncGovernorDeatails);
+ }
+
+ @Test
+ public void approveCertifiedService_bysdncAdminDetails() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(changeDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, null, null, sdncAdminDetails);
+ }
+
+ @Test
+ public void approveCertifiedService_byDesigner() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncDesignerDetails, 409, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ // String auditAction="DApprove";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncDesignerDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setStatus("409");
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getMessageId() + ":
+ // " + errorInfo.getMessage());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, "409", ActionStatus.RESTRICTED_OPERATION,
+ sdncDesignerDetails);
+ }
+
+ @Test
+ public void approveCertifiedService_byTester() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncTesterDetails, 409, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ // String auditAction="DApprove";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncTesterDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setStatus("409");
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getMessageId() + ":
+ // " + errorInfo.getMessage());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, "409", ActionStatus.RESTRICTED_OPERATION,
+ sdncTesterDetails);
+ }
+
+ @Test
+ public void approveCertifiedService_byOps() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncOpsDetails, 409, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, "409", ActionStatus.RESTRICTED_OPERATION,
+ sdncOpsDetails);
+
+ }
+
+ @Test
+ public void rejectCertifiedService_bysdncGovernorDeatails() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncGovernorDeatails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(changeDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_REJECTED);
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_REJECTED);
+
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncGovernorDeatails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setPrevState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_REJECTED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, null, null, sdncGovernorDeatails);
+
+ }
+
+ @Test
+ public void rejectCertifiedService_bysdncAdminDetails() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(changeDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_REJECTED);
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_REJECTED);
+
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncAdminDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setPrevState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_REJECTED.name());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, null, null, sdncAdminDetails);
+ }
+
+ @Test
+ public void rejectCertifiedService_byDesigner() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncDesignerDetails, 409, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncDesignerDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setPrevState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setStatus("409");
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getMessageId() + ":
+ // " + errorInfo.getMessage());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, "409", ActionStatus.RESTRICTED_OPERATION,
+ sdncDesignerDetails);
+ }
+
+ @Test
+ public void rejectCertifiedService_byTester() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncTesterDetails, 409, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncTesterDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setPrevState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setStatus("409");
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getMessageId() + ":
+ // " + errorInfo.getMessage());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, "409", ActionStatus.RESTRICTED_OPERATION,
+ sdncTesterDetails);
+ }
+
+ @Test
+ public void rejectCertifiedService_byOps() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncOpsDetails, 409, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ // ErrorInfo errorInfo =
+ // utils.parseYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ // String auditAction="DReject";
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(certifyService,
+ // certifyService.getVersion(), sdncOpsDetails);
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setResourceType("Service");
+ // expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.CERTIFIED.name());
+ // expectedResourceAuditJavaObject.setPrevState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ // expectedResourceAuditJavaObject.setDprevStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setDcurrStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED.name());
+ // expectedResourceAuditJavaObject.setStatus("409");
+ // expectedResourceAuditJavaObject.setDesc(errorInfo.getMessageId() + ":
+ // " + errorInfo.getMessage());
+ // expectedResourceAuditJavaObject.setComment(userRemarks);
+ // expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ //
+ // AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, "409", ActionStatus.RESTRICTED_OPERATION,
+ sdncOpsDetails);
+ }
+
+ @Test
+ public void approveServiceNotFound() throws Exception {
+ String previuosId = serviceDetails.getUniqueId();
+ serviceDetails.setUniqueId("dummyId");
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 404, serviceDetails.getVersion());
+ serviceDetails.setUniqueId(previuosId);
+
+ variablesAsList = Arrays.asList("dummyId");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), variablesAsList,
+ changeDistStatusAndValidate.getResponse());
+
+ }
+
+ @Test
+ public void rejectServiceNotFound() throws Exception {
+ String previuosId = serviceDetails.getUniqueId();
+ serviceDetails.setUniqueId("dummyId");
+
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 404, serviceDetails.getVersion());
+ serviceDetails.setUniqueId(previuosId);
+
+ variablesAsList = Arrays.asList("dummyId");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), variablesAsList,
+ changeDistStatusAndValidate.getResponse());
+
+ }
+
+ @Test
+ public void rejectService_emptyComment() throws Exception {
+ userRemarks = "";
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 400, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ }
+
+ @Test
+ public void rejectService_nullComment() throws Exception {
+ userRemarks = null;
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 400, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+ }
+
+ @Test
+ public void rejectService_spaceComment() throws Exception {
+ userRemarks = " ";
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncAdminDetails, 400, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ }
+
+ @Test
+ public void approveService_emptyComment() throws Exception {
+ userRemarks = "";
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 400, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ }
+
+ @Test
+ public void approveService_nullComment() throws Exception {
+ userRemarks = null;
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 400, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ }
+
+ @Test
+ public void approveService_spaceComment() throws Exception {
+ userRemarks = " ";
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse changeDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 400, certifyService.getVersion());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_CONTENT.name(), new ArrayList<String>(),
+ changeDistStatusAndValidate.getResponse());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ }
+
+ @Test
+ public void distributionStatusChange_approve_Reject_AprroveBysdncAdminDetails() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+
+ RestResponse approveDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncGovernorDeatails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(approveDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse rejectDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, sdncGovernorDeatails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(rejectDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_REJECTED);
+
+ validateAudit("DReject", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_APPROVED,
+ DistributionStatusEnum.DISTRIBUTION_REJECTED, null, null, sdncGovernorDeatails);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse secondApproveDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncAdminDetails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(secondApproveDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ validateAudit("DApprove", LifecycleStateEnum.CERTIFIED, DistributionStatusEnum.DISTRIBUTION_REJECTED,
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, null, null, sdncAdminDetails);
+
+ }
+
+ @Test
+ public void distributeNotCertifiedServiceTest() throws Exception {
+ RestResponse approveDistStatusAndValidate = changeDistStatusAndValidate(DistributionStatusEnum.DISTRIBUTED,
+ sdncGovernorDeatails, 200, serviceDetails.getVersion());
+
+ RestResponse getService = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTED);
+
+ }
+
+ @Test
+ public void distributeCertifiedServiceTest() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse approveDistStatusAndValidate = changeDistStatusAndValidate(DistributionStatusEnum.DISTRIBUTED,
+ sdncGovernorDeatails, 200, certifyService.getVersion());
+
+ RestResponse getService = ServiceRestUtils.getService(certifyService, sdncDesignerDetails);
+ getDistrubtionStatusValue(getService, DistributionStatusEnum.DISTRIBUTED);
+
+ }
+
+ @Test
+ public void approveCheckedoutCertifiedServiceTest() throws Exception {
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser
+ .convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ RestResponse approveDistStatusAndValidate = changeDistStatusAndValidate(
+ DistributionStatusEnum.DISTRIBUTION_APPROVED, sdncGovernorDeatails, 200, certifyService.getVersion());
+ getDistrubtionStatusValue(approveDistStatusAndValidate, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ RestResponse checkoutResp = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(200, checkoutResp.getErrorCode().intValue());
+ // Utils r = new Utils();
+
+ String distributionStatus = ResponseParser.getValueFromJsonResponse(checkoutResp.getResponse(),
+ "distributionStatus");
+ // Utils r1 = new Utils();
+ String lifecycleState = ResponseParser.getValueFromJsonResponse(checkoutResp.getResponse(), "lifecycleState");
+
+ assertTrue("NOT_CERTIFIED_CHECKOUT".equals(lifecycleState));
+ assertTrue("DISTRIBUTION_NOT_APPROVED".equals(distributionStatus));
+ }
+
+ private RestResponse changeDistStatusAndValidate(DistributionStatusEnum distStatus, User user, int errorCode,
+ String serviceVersion) throws Exception {
+ RestResponse distributionResponse = LifecycleRestUtils.changeDistributionStatus(serviceDetails, serviceVersion,
+ user, userRemarks, distStatus);
+ assertNotNull(distributionResponse);
+ assertNotNull(distributionResponse.getErrorCode());
+ assertEquals(errorCode, distributionResponse.getErrorCode().intValue());
+
+ if (userRemarks == " " || userRemarks == null) {
+ userRemarks = "";
+ }
+
+ return distributionResponse;
+ }
+
+ private void getDistrubtionStatusValue(RestResponse response, DistributionStatusEnum expectedDistributionValue)
+ throws Exception {
+ String actualDistributionValue = ResponseParser.getValueFromJsonResponse(response.getResponse(),
+ "distributionStatus");
+ assertEquals(expectedDistributionValue.name(), actualDistributionValue);
+ }
+
+ private void validateAudit(String Action, LifecycleStateEnum currState, DistributionStatusEnum dPrevStatus,
+ DistributionStatusEnum dCurrStatus, String status, ActionStatus errorInfoFromFile, User user)
+ throws Exception {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceDetails.getVersion(), user);
+ expectedResourceAuditJavaObject.setAction(Action);
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ expectedResourceAuditJavaObject.setCurrState(currState.name());
+ expectedResourceAuditJavaObject.setDprevStatus(dPrevStatus.name());
+ expectedResourceAuditJavaObject.setDcurrStatus(dCurrStatus.name());
+ expectedResourceAuditJavaObject.setComment(userRemarks);
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ if (errorInfoFromFile != null) {
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorInfoFromFile.name());
+ expectedResourceAuditJavaObject
+ .setDesc(AuditValidationUtils.buildAuditDescription(errorInfo, variablesAsList));
+ }
+
+ if (status != null)
+ expectedResourceAuditJavaObject.setStatus(status);
+
+ if (currState != LifecycleStateEnum.CERTIFIED) {
+ expectedResourceAuditJavaObject.setModifierName("");
+ }
+
+ AuditValidationUtils.validateAuditDistribution(expectedResourceAuditJavaObject, Action);
+ }
+
+ // private ServiceReqDetails certifyService() throws Exception
+ // {
+ // ServiceReqDetails certifyService =
+ // LifecycleRestUtils.certifyService(serviceDetails,
+ // serviceDetails.getVersion(), sdncAdminDetails);
+ //// version = certifyService.getVersion();
+ //
+ // return certifyService;
+ // }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/CreateServiceMetadataApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/CreateServiceMetadataApiTest.java
new file mode 100644
index 0000000000..56cfeb54ea
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/CreateServiceMetadataApiTest.java
@@ -0,0 +1,1300 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONObject;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ServiceValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class CreateServiceMetadataApiTest extends ComponentBaseTest {
+ private static Logger logger = LoggerFactory.getLogger(CreateServiceMetadataApiTest.class.getName());
+
+ String serviceBaseVersion = "0.1";
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CreateServiceMetadataApiTest() {
+ super(name, CreateServiceMetadataApiTest.class.getName());
+ }
+
+ @Test
+ public void createDefaultService() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // String creator =
+ // ElementFactory.getDefaultUser(UserRoleEnum.ADMIN).getUserId();
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", 201, restResponse.getErrorCode().intValue());
+
+ // validate create service response vs actual
+
+ Service service = ResponseParser.convertServiceResponseToJavaObject(restResponse.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails, service, sdncUserDetails,
+ (LifecycleStateEnum) null);
+
+ // validate get service response vs actual
+ restResponse = ServiceRestUtils.getService(serviceDetails, sdncUserDetails);
+ service = ResponseParser.convertServiceResponseToJavaObject(restResponse.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails, service, sdncUserDetails,
+ (LifecycleStateEnum) null);
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createDefaultServiceUserDesigner() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", 201, restResponse.getErrorCode().intValue());
+
+ // validate create service response vs actual
+
+ Service service = ResponseParser.convertServiceResponseToJavaObject(restResponse.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails, service, sdncUserDetails,
+ (LifecycleStateEnum) null);
+
+ // validate get service response vs actual
+ restResponse = ServiceRestUtils.getService(serviceDetails, sdncUserDetails);
+ service = ResponseParser.convertServiceResponseToJavaObject(restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceUserNotFound() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncUserDetails.setUserId("no1234");
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ sdncUserDetails.setFirstName("");
+ sdncUserDetails.setLastName("");
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setModifierName("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceUserNotAllowed() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceEmptyName() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ String serviceName = "";
+ serviceDetails.setName(serviceName);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_COMPONENT_NAME.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("Service");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_COMPONENT_NAME.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceEmptyCategory() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ String category = "";
+
+ serviceDetails.setCategories(null);
+ // serviceDetails.addCategory(category);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_CATEGORY.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("Service");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_CATEGORY.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceEmptyTag() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ArrayList<String> tags = new ArrayList<String>();
+ tags.add("");
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setTags(tags);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_FIELD_FORMAT.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("Service", "tag");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FIELD_FORMAT.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceEmptyDescription() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ String description = "";
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setDescription(description);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("Service");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_MISSING_DESCRIPTION.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceEmptyTags() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ArrayList<String> tags = new ArrayList<String>();
+ tags.add("");
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setTags(tags);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_FIELD_FORMAT.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("Service", "tag");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FIELD_FORMAT.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceByPutHttpMethod() throws Exception {
+
+ String method = "PUT";
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+
+ RestResponse restResponse = ServiceRestUtils.createServiceByHttpMethod(serviceDetails, sdncUserDetails, method,
+ Urls.CREATE_SERVICE);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.NOT_ALLOWED.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ restResponse.getResponse());
+
+ // //validate audit
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // resourceUtils.constructFieldsForAuditValidation(serviceDetails,
+ // serviceBaseVersion, sdncUserDetails);
+ //
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ //
+ // String auditDesc =
+ // AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ // expectedResourceAuditJavaObject.setDesc(auditDesc);
+ //
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ }
+
+ @Test
+ public void createServiceByDeleteHttpMethod() throws Exception {
+
+ String method = "DELETE";
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+
+ RestResponse restResponse = ServiceRestUtils.createServiceByHttpMethod(serviceDetails, sdncUserDetails, method,
+ Urls.CREATE_SERVICE);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.NOT_ALLOWED.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.NOT_ALLOWED.name(), variables,
+ restResponse.getResponse());
+
+ // //validate audit
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // resourceUtils.constructFieldsForAuditValidation(serviceDetails,
+ // serviceBaseVersion, sdncUserDetails);
+ //
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ //
+ // String auditDesc =
+ // AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ // expectedResourceAuditJavaObject.setDesc(auditDesc);
+ //
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction);
+
+ }
+
+ @Test
+ public void createServiceTagLengthExceedLimit() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ StringBuffer tagBuffer = new StringBuffer();
+ for (int i = 0; i < 1025; i++) {
+ tagBuffer.append("a");
+ }
+ ArrayList<String> tags = new ArrayList<String>();
+ tags.add(tagBuffer.toString());
+ serviceDetails.setTags(tags);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("50");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc("50"));
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ /*
+ * ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ * ServiceValidationUtils.constructFieldsForAuditValidation(
+ * serviceDetails, serviceBaseVersion, sdncUserDetails);
+ *
+ * String auditAction="Create";
+ * expectedResourceAuditJavaObject.setAction(auditAction);
+ * expectedResourceAuditJavaObject.setPrevState("");
+ * expectedResourceAuditJavaObject.setPrevVersion("");
+ * expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.
+ * NOT_CERTIFIED_CHECKOUT).toString());
+ * expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().
+ * toString()); expectedResourceAuditJavaObject.setDesc(auditDesc);
+ *
+ * AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ * auditAction, null);
+ */
+
+ }
+
+ @Test
+ public void createServiceAlreadyExistException() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // create service with the same name
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_NAME_ALREADY_EXIST.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+
+ List<String> variables = Arrays.asList("Service", serviceDetails.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_NAME_ALREADY_EXIST.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void createServiceWrongContactId() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setContactId("123as");
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.COMPONENT_INVALID_CONTACT.name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+ }
+
+ @Test
+ public void createServiceProjectName() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setProjectCode("12345");
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ Integer expectedCode = 201;
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", expectedCode, restResponse.getErrorCode());
+ Service service = ResponseParser.convertServiceResponseToJavaObject(restResponse.getResponse());
+
+ assertEquals("12345", service.getProjectCode());
+ }
+
+ @Test
+ public void createAndGetByNameAndVersion() throws Exception {
+
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ // create
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ assertEquals("Check response code after create service", 201, restResponse.getErrorCode().intValue());
+
+ // get
+ restResponse = ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails, serviceDetails.getName(),
+ serviceBaseVersion);
+ assertEquals("Check response code after get service", 200, restResponse.getErrorCode().intValue());
+
+ Service service = ResponseParser.convertServiceResponseToJavaObject(restResponse.getResponse());
+ String uniqueId = service.getUniqueId();
+ serviceDetails.setUniqueId(uniqueId);
+ ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails, service, sdncUserDetails,
+ (LifecycleStateEnum) null);
+ }
+
+ //// US553874
+
+ @JsonIgnore
+ @Test
+ public void createServiceIsVNF_isFalse() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after updating Interface Artifact", 201,
+ restResponse.getErrorCode().intValue());
+
+ // get service and verify that service created with isVNF defined in
+ // serviceDetails
+ RestResponse serviceByNameAndVersion = ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ serviceDetails.getName(), serviceBaseVersion);
+ Service serviceObject = ResponseParser
+ .convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails, serviceObject, sdncUserDetails,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @JsonIgnore
+ @Test
+ public void createServiceIsVNF_isTrue() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after updating Interface Artifact", 201,
+ restResponse.getErrorCode().intValue());
+
+ // get service and verify that service created with isVNF defined in
+ // serviceDetails
+ RestResponse serviceByNameAndVersion = ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ serviceDetails.getName(), serviceBaseVersion);
+ Service serviceObject = ResponseParser
+ .convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails, serviceObject, sdncUserDetails,
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ // validate audit
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @JsonIgnore
+ @Test(enabled = false)
+ public void createServiceIsVNF_isNull() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ // clean audit DB before service creation
+ DbUtils.cleanAllAudits();
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertEquals("Check response code after updating Interface Artifact", 400,
+ restResponse.getErrorCode().intValue());
+ List<String> variables = Arrays.asList("VNF Service Indicator");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_DATA.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc("VNF Service Indicator"));
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+ }
+
+ @JsonIgnore
+ @Test(enabled = false)
+ public void createServiceEmptyIsVNF() throws Exception {
+
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+
+ DbUtils.cleanAllAudits();
+
+ // send create service toward BE
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", restResponse.getErrorCode(),
+ restResponse.getErrorCode());
+
+ // validate create service response vs actual
+ List<String> variables = Arrays.asList("VNF Service Indicator");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_DATA.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, serviceBaseVersion, sdncUserDetails);
+
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ private RestResponse createServiceWithMissingAttribute(String serviceDetails, User sdncModifierDetails)
+ throws Exception {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = ServiceRestUtils.prepareHeadersMap(sdncModifierDetails, false);
+ headersMap.put(HttpHeaderEnum.CACHE_CONTROL.getValue(), "no-cache");
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort());
+ // TODO: ADD AUTHENTICATION IN REQUEST
+ logger.debug(url);
+ logger.debug("Send POST request to create service: {}", url);
+ logger.debug("Service body: {}", serviceDetails);
+ logger.debug("Service headers: {}", headersMap);
+ RestResponse sendCreateUserRequest = http.httpSendPost(url, serviceDetails, headersMap);
+
+ return sendCreateUserRequest;
+
+ }
+
+ @JsonIgnore
+ @Test(enabled = false)
+ public void createServiceVersion_isVNFDoesNotExistInJson() throws Exception {
+
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // clean audit DB before updating service
+ DbUtils.cleanAllAudits();
+
+ // remove isVNF from json sent to create service
+ JSONObject jObject = new JSONObject(serviceDetails);
+ jObject.remove("VNF");
+
+ // send create service toward BE
+ RestResponse restResponse = createServiceWithMissingAttribute(jObject.toString(), sdncUserDetails);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after updating Interface Artifact", 400,
+ restResponse.getErrorCode().intValue());
+ List<String> variables = new ArrayList<String>();
+ variables.add("VNF Service Indicator");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_DATA.name(), variables,
+ restResponse.getResponse());
+
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.MISSING_DATA.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = ServiceValidationUtils
+ .constructFieldsForAuditValidation(serviceDetails, "0.1", sdncUserDetails);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc("VNF Service Indicator"));
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null, false);
+
+ }
+
+ @Test
+ public void checkInvariantUuidIsImmutable() throws Exception {
+ // choose the user to create service
+ User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ Component resourceDetailsVFCcomp = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.ADMIN, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVFCcomp, UserRoleEnum.ADMIN,
+ true, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp, UserRoleEnum.ADMIN,
+ LifeCycleStatesEnum.CERTIFY, true);
+
+ // fill new service details
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ String invariantUuidDefinedByUser = "!!!!!!!!!!!!!!!!!!!!!!!!";
+ serviceDetails.setInvariantUUID(invariantUuidDefinedByUser);
+
+ // create service
+ RestResponse restResponseCreation = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ BaseRestUtils.checkStatusCode(restResponseCreation, "create request failed", false, 201);
+ Service service = ResponseParser.convertServiceResponseToJavaObject(restResponseCreation.getResponse());
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCcomp, service,
+ UserRoleEnum.ADMIN, true);
+
+ String invariantUUIDcreation = service.getInvariantUUID();
+
+ // validate get service response vs actual
+ RestResponse restResponseGetting = ServiceRestUtils.getService(serviceDetails, sdncUserDetails);
+ BaseRestUtils.checkSuccess(restResponseGetting);
+ service = ResponseParser.convertServiceResponseToJavaObject(restResponseGetting.getResponse());
+ String invariantUUIDgetting = service.getInvariantUUID();
+
+ assertEquals(invariantUUIDcreation, invariantUUIDgetting);
+
+ // Update service with new invariant UUID
+ RestResponse restResponseUpdate = ServiceRestUtils.updateService(serviceDetails, sdncUserDetails);
+ BaseRestUtils.checkSuccess(restResponseUpdate);
+ Service updatedService = ResponseParser.convertServiceResponseToJavaObject(restResponseUpdate.getResponse());
+ String invariantUUIDupdating = updatedService.getInvariantUUID();
+ assertEquals(invariantUUIDcreation, invariantUUIDupdating);
+
+ // Do checkin
+ RestResponse restResponseCheckin = LifecycleRestUtils.changeServiceState(serviceDetails, sdncUserDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ BaseRestUtils.checkSuccess(restResponseCheckin);
+ Service checkinService = ResponseParser.convertServiceResponseToJavaObject(restResponseCheckin.getResponse());
+ String invariantUUIDcheckin = checkinService.getInvariantUUID();
+ String version = checkinService.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcheckin);
+ assertEquals(version, "0.1");
+
+ // Do checkout
+ RestResponse restResponseCheckout = LifecycleRestUtils.changeServiceState(serviceDetails, sdncUserDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ BaseRestUtils.checkSuccess(restResponseCheckout);
+ Service checkoutService = ResponseParser.convertServiceResponseToJavaObject(restResponseCheckout.getResponse());
+ String invariantUUIDcheckout = checkoutService.getInvariantUUID();
+ version = checkoutService.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcheckout);
+ assertEquals(version, "0.2");
+
+ // do certification request
+ RestResponse restResponseCertificationRequest = LifecycleRestUtils.changeServiceState(serviceDetails,
+ sdncUserDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ BaseRestUtils.checkSuccess(restResponseCertificationRequest);
+ Service certificationRequestService = ResponseParser
+ .convertServiceResponseToJavaObject(restResponseCertificationRequest.getResponse());
+ String invariantUUIDcertificationRequest = certificationRequestService.getInvariantUUID();
+ version = certificationRequestService.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcertificationRequest);
+ assertEquals(version, "0.2");
+
+ // start certification
+ RestResponse restResponseStartCertification = LifecycleRestUtils.changeServiceState(serviceDetails,
+ sdncUserDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ BaseRestUtils.checkSuccess(restResponseStartCertification);
+ Service startCertificationRequestService = ResponseParser
+ .convertServiceResponseToJavaObject(restResponseStartCertification.getResponse());
+ String invariantUUIDStartCertification = startCertificationRequestService.getInvariantUUID();
+ version = startCertificationRequestService.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDStartCertification);
+ assertEquals(version, "0.2");
+
+ // certify
+ RestResponse restResponseCertify = LifecycleRestUtils.changeServiceState(serviceDetails, sdncUserDetails,
+ serviceDetails.getVersion(), LifeCycleStatesEnum.CERTIFY);
+ BaseRestUtils.checkSuccess(restResponseCertify);
+ Service certifyService = ResponseParser.convertServiceResponseToJavaObject(restResponseCertify.getResponse());
+ String invariantUUIDcertify = certifyService.getInvariantUUID();
+ version = certifyService.getVersion();
+ assertEquals(invariantUUIDcreation, invariantUUIDcertify);
+ assertEquals(version, "1.0");
+
+ }
+
+ // US672129 Benny
+ private void getServiceValidateInvariantUuid(String serviceUniqueId, String invariantUUIDcreation)
+ throws Exception {
+ RestResponse getService = ServiceRestUtils.getService(serviceUniqueId,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, getService.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(getService));
+ }
+
+ @Test // invariantUUID generated when the component is created and never
+ // changed
+ public void serviceInvariantUuid() throws Exception {
+ User designerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ User testerUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ User pmUser = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ Component resourceDetailsVFCcomp = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ true, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFY, true);
+ // create service
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setInvariantUUID("123456");
+ RestResponse restResponse = ServiceRestUtils.createService(serviceDetails, designerUser);
+ assertEquals("Check response code after create resource", BaseRestUtils.STATUS_CODE_CREATED,
+ restResponse.getErrorCode().intValue());
+ Service service = ResponseParser.parseToObjectUsingMapper(restResponse.getResponse(), Service.class);
+ // invariantUUID generated when the component is created and never
+ // changed
+ String invariantUUIDcreation = ResponseParser.getInvariantUuid(restResponse);
+ // Add VF instance to service
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCcomp, service,
+ UserRoleEnum.DESIGNER, true);
+ // get resource and verify InvariantUuid is not changed
+ getServiceValidateInvariantUuid(service.getUniqueId(), invariantUUIDcreation);
+
+ // Update service with new invariant UUID
+ restResponse = ServiceRestUtils.updateService(serviceDetails, designerUser);
+ assertEquals("Check response code after create resource", BaseRestUtils.STATUS_CODE_SUCCESS,
+ restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // Checkin
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser, LifeCycleStatesEnum.CHECKIN);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // Checkout
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // certification request
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // start certification
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, testerUser,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // certify
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, testerUser, LifeCycleStatesEnum.CERTIFY);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // update resource
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ serviceDetails.setDescription("updatedDescription");
+ restResponse = ServiceRestUtils.updateService(serviceDetails, designerUser);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // certification request
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // Checkout
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // certification request
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // start certification
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, testerUser,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // cancel certification
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, testerUser,
+ LifeCycleStatesEnum.CANCELCERTIFICATION);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // start certification
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, testerUser,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // failure
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, testerUser,
+ LifeCycleStatesEnum.FAILCERTIFICATION);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // Checkout
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser,
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // Checkin
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser, LifeCycleStatesEnum.CHECKIN);
+ assertEquals(BaseRestUtils.STATUS_CODE_SUCCESS, restResponse.getErrorCode().intValue());
+ assertEquals(invariantUUIDcreation, ResponseParser.getInvariantUuid(restResponse));
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ // create instance
+ ProductReqDetails productDetails = ElementFactory.getDefaultProduct();
+ RestResponse createProductResponse = ProductRestUtils.createProduct(productDetails, pmUser);
+ assertEquals(BaseRestUtils.STATUS_CODE_CREATED, createProductResponse.getErrorCode().intValue());
+ ComponentInstanceReqDetails serviceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(serviceDetails);
+ RestResponse createServiceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ serviceInstanceReqDetails, pmUser, productDetails.getUniqueId(), ComponentTypeEnum.PRODUCT);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_CREATED,
+ createServiceInstanceResponse.getErrorCode().intValue());
+ getServiceValidateInvariantUuid(serviceDetails.getUniqueId(), invariantUUIDcreation);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetAllServiceVersions.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetAllServiceVersions.java
new file mode 100644
index 0000000000..760d002856
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetAllServiceVersions.java
@@ -0,0 +1,350 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GetAllServiceVersions extends ComponentBaseTest {
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected User sdncDesignerDetails;
+ protected User sdncDesignerDetails2;
+ protected User sdncAdminDetails;
+ protected User sdncGovernorDeatails;
+ protected User sdncTesterDetails;
+ protected User sdncOpsDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+ protected Component resourceDetailsVFCcomp;
+ protected Service serviceServ;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetAllServiceVersions() {
+ super(name, GetAllServiceVersions.class.getName());
+ ;
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncDesignerDetails2 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncGovernorDeatails = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ sdncTesterDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncOpsDetails = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+ resourceDetailsVFCcomp = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ true, true);
+
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp, UserRoleEnum.DESIGNER,
+ LifeCycleStatesEnum.CERTIFY, true);
+ serviceServ = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCcomp, serviceServ,
+ UserRoleEnum.DESIGNER, true);
+
+ serviceDetails = new ServiceReqDetails(serviceServ);
+
+ }
+
+ @Test
+ public void GetAllServiceVersions_Version05() throws Exception {
+
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ }
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+
+ }
+
+ @Test
+ public void GetAllServiceVersions_Version01() throws Exception {
+
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncDesignerDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_Version15() throws Exception {
+ // addMandatoryArtifactsToService();
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ }
+
+ RestResponse changeServiceState = LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ // serviceServ.setUniqueId(serviceDetails.getUniqueId());
+ // RestResponse changeServiceState =
+ // AtomicOperationUtils.changeComponentState(serviceServ,
+ // UserRoleEnum.ADMIN, LifeCycleStatesEnum.CERTIFY, false).getRight();
+
+ assertTrue("certify service request returned status:" + changeServiceState.getErrorCode(),
+ changeServiceState.getErrorCode() == 200);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ for (int x = 0; x < 5; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ }
+
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncAdminDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_Version25() throws Exception {
+ // addMandatoryArtifactsToService();
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ }
+
+ // getting to version 1.0
+ RestResponse changeServiceState = LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ assertTrue("certify service request returned status:" + changeServiceState.getErrorCode(),
+ changeServiceState.getErrorCode() == 200);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ // getting to version 1.5
+ for (int x = 0; x < 5; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ }
+
+ // getting to version 2.0
+ changeServiceState = LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ assertTrue("certify service request returned status:" + changeServiceState.getErrorCode(),
+ changeServiceState.getErrorCode() == 200);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ // getting to version 2.5
+ for (int x = 0; x < 5; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+ }
+
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncAdminDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_ReadyForCertification_version05() throws Exception {
+ // addMandatoryArtifactsToService();
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+ }
+
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncAdminDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_CertifactionInProgress_version05() throws Exception {
+ // addMandatoryArtifactsToService();
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+ }
+
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncAdminDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncAdminDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_Certified_version10() throws Exception {
+ // addMandatoryArtifactsToService();
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ // get to version 0.5
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+
+ }
+
+ // get version 1.0
+ RestResponse changeServiceState = LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ assertTrue("certify service request returned status:" + changeServiceState.getErrorCode(),
+ changeServiceState.getErrorCode() == 200);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncAdminDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_Certified_version20() throws Exception {
+ // addMandatoryArtifactsToService();
+ Map<String, String> origVersionsMap = new HashMap<String, String>();
+ // get to version 0.5
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ }
+
+ // get version 1.0
+ RestResponse changeServiceState = LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ assertTrue("certify service request returned status:" + changeServiceState.getErrorCode(),
+ changeServiceState.getErrorCode() == 200);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ // get version 1.5
+ for (int x = 0; x < 4; x++) {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKIN);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(),
+ LifeCycleStatesEnum.CHECKOUT);
+ }
+
+ // get version 2.0
+ changeServiceState = LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ assertTrue("certify service request returned status:" + changeServiceState.getErrorCode(),
+ changeServiceState.getErrorCode() == 200);
+ origVersionsMap.put(serviceDetails.getVersion(), serviceDetails.getUniqueId());
+
+ // validate get response
+ RestResponse serviceGetResponse = ServiceRestUtils.getService(serviceDetails, sdncAdminDetails);
+ Service res = ResponseParser.convertServiceResponseToJavaObject(serviceGetResponse.getResponse());
+ Map<String, String> getVersionsMap = res.getAllVersions();
+ assertTrue(origVersionsMap.equals(getVersionsMap));
+ }
+
+ @Test
+ public void GetAllServiceVersions_ServiceNotFound() throws Exception {
+
+ RestResponse serviceGetResponse = ServiceRestUtils.getService("123456789", sdncAdminDetails);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.SERVICE_NOT_FOUND.name());
+ assertEquals("Check response code after get service without cache", errorInfo.getCode(),
+ serviceGetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList("123456789");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), variables,
+ serviceGetResponse.getResponse());
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetComponentAuditApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetComponentAuditApiTest.java
new file mode 100644
index 0000000000..b84728a06c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetComponentAuditApiTest.java
@@ -0,0 +1,322 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.HttpStatus;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.JsonElement;
+
+import fj.data.Either;
+
+public class GetComponentAuditApiTest extends ComponentBaseTest {
+
+ public static final String SERVICES_API = "services";
+ public static final String RESOURCES_API = "resources";
+
+ protected User sdncAdminUser;
+ protected User sdncDesignerUser;
+ protected User sdncTesterUser;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetComponentAuditApiTest() {
+ super(name, GetComponentAuditApiTest.class.getName());
+ }
+
+ // in case tests fail, run this method as test to create mapping in ES
+ public void updateElasticSearchMapping() throws IOException {
+ Either<String, Exception> fileContentUTF8 = FileUtils.getFileContentUTF8("src\\test\\resources\\CI\\other\\mapping.json");
+ AssertJUnit.assertTrue(fileContentUTF8.isLeft());
+
+ final String ES_TEMPLATE_URL = "http://%s:%s/_template/audit_template";
+ String url = String.format(ES_TEMPLATE_URL, config.getEsHost(), config.getEsPort());
+
+ RestResponse sendHttpPost = new HttpRequest().sendHttpPost(url, fileContentUTF8.left().value(), null);
+ AssertJUnit.assertTrue(sendHttpPost.getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ @BeforeMethod
+ public void init() {
+ sdncAdminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncDesignerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncTesterUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ ;
+
+ }
+
+ @Test
+ public void testServiceAuditCertifiedVersion() throws Exception {
+
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ Wrapper<String> versionZeroOneIDWrapper = new Wrapper<String>(), versionZeroTwoIDWrapper = new Wrapper<String>();
+
+ createBasicServiceForAudit(versionZeroOneIDWrapper, versionZeroTwoIDWrapper, serviceDetails, true);
+ // First Certification
+
+ LifecycleRestUtils.certifyService(serviceDetails);
+ // LCSbaseTest.certifyService(serviceDetails);
+ AssertJUnit.assertTrue(serviceDetails.getVersion().equals("1.0"));
+
+ // Second Certification
+ increaseServiceVersion(serviceDetails, "1.1");
+ increaseServiceVersion(serviceDetails, "1.2");
+ increaseServiceVersion(serviceDetails, "1.3");
+ increaseServiceVersion(serviceDetails, "1.4");
+ LifecycleRestUtils.certifyService(serviceDetails);
+ AssertJUnit.assertTrue(serviceDetails.getVersion().equals("2.0"));
+ String certifiedId = serviceDetails.getUniqueId();
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerUser, LifeCycleStatesEnum.CHECKOUT);
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerUser, LifeCycleStatesEnum.CHECKIN);
+
+ JsonElement element = getAuditJson(SERVICES_API, certifiedId);
+ // audits kept: 5*check ins + 4*check outs + 2*artifact payload
+ // updates(tosca) + certification request + certification start +
+ // certification success
+ // + 3 A&AI(ArtifactDelete, ArtifactUpload, ArtifactUpdate)
+ AssertJUnit.assertTrue("expected: 17, actual: " + element.getAsJsonArray().size(), element.getAsJsonArray().size() == 17);
+
+ }
+
+ protected void certifyResource(ResourceReqDetails defaultResource) throws IOException {
+ RestResponse response = LifecycleRestUtils.changeResourceState(defaultResource, sdncDesignerUser, LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ response = LifecycleRestUtils.changeResourceState(defaultResource, sdncTesterUser, LifeCycleStatesEnum.STARTCERTIFICATION);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ response = LifecycleRestUtils.changeResourceState(defaultResource, sdncTesterUser, LifeCycleStatesEnum.CERTIFY);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ protected JsonElement getAuditJson(String componentType, String componentId) throws IOException {
+ Map<String, String> headers = new HashMap<String, String>() {
+ {
+ put(Constants.USER_ID_HEADER, UserRoleEnum.ADMIN.getUserId());
+ }
+ };
+ String url = String.format(Urls.GET_COMPONENT_AUDIT_RECORDS, config.getCatalogBeHost(), config.getCatalogBePort(), componentType, componentId);
+
+ RestResponse httpSendGet = new HttpRequest().httpSendGet(url, headers);
+ AssertJUnit.assertTrue(httpSendGet.getErrorCode() == HttpStatus.SC_OK);
+ JsonElement element = ResponseParser.parseToObject(httpSendGet.getResponse(), JsonElement.class);
+ AssertJUnit.assertTrue(element.isJsonArray());
+ return element;
+ }
+
+ protected void createBasicServiceForAudit(Wrapper<String> versionZeroOneIDWrapper, Wrapper<String> versionZeroTwoIDWrapper, ServiceReqDetails serviceDetails, Boolean withResInst) throws Exception {
+
+ User designerUser = sdncDesignerUser;
+
+ RestResponse response = ServiceRestUtils.createService(serviceDetails, designerUser);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_CREATED);
+ versionZeroOneIDWrapper.setInnerElement(serviceDetails.getUniqueId());
+
+ if (withResInst) {
+ Resource resourceObj = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceObj, UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.changeComponentState(resourceObj, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ ResourceReqDetails resource = new ResourceReqDetails(resourceObj);
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+ resourceInstanceReqDetails.setComponentUid(resource.getUniqueId());
+ ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, sdncDesignerUser, serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+
+ // ServiceUtils.createCertResourceWithDeploymentArt(serviceDetails,
+ // "myResource");
+ }
+
+ response = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser, LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ AssertJUnit.assertTrue(serviceDetails.getVersion().equals("0.1"));
+
+ response = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser, LifeCycleStatesEnum.CHECKOUT);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ // ServiceUtils.addServiceDeploymentArtifact(serviceDetails.getUniqueId(),
+ // designerUser);
+ versionZeroTwoIDWrapper.setInnerElement(serviceDetails.getUniqueId());
+ AssertJUnit.assertTrue(serviceDetails.getVersion().equals("0.2"));
+ response = LifecycleRestUtils.changeServiceState(serviceDetails, designerUser, LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+
+ increaseServiceVersion(serviceDetails, "0.3");
+
+ increaseServiceVersion(serviceDetails, "0.4");
+
+ increaseServiceVersion(serviceDetails, "0.5");
+
+ }
+
+ protected void increaseServiceVersion(ServiceReqDetails serviceDetails, String excpectedVersion) throws Exception {
+ RestResponse response = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerUser, LifeCycleStatesEnum.CHECKOUT);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ AssertJUnit.assertTrue(serviceDetails.getVersion().equals(excpectedVersion));
+ response = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerUser, LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ protected void createBasicResourceForAudit(Wrapper<String> versionOnePointTwoIDWrapper, ResourceReqDetails defaultResource) throws Exception {
+
+ RestResponse response = ResourceRestUtils.createResource(defaultResource, sdncDesignerUser);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_CREATED);
+
+ // ArtifactDefinition artifactDef = new
+ // ArtifactUtils().constructDefaultArtifactInfo();
+ // response = resourceUtils.add_artifact(defaultResource,
+ // sdncDesignerUser, defaultResource.getVersion(), artifactDef);
+ // assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerUser, defaultResource.getUniqueId());
+ AssertJUnit.assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(), response.getErrorCode() == 200);
+
+ response = LifecycleRestUtils.changeResourceState(defaultResource, sdncDesignerUser, LifeCycleStatesEnum.CHECKIN);
+
+ increaseResourceVersion(defaultResource, "0.2");
+
+ increaseResourceVersion(defaultResource, "0.3");
+
+ increaseResourceVersion(defaultResource, "0.4");
+
+ increaseResourceVersion(defaultResource, "0.5");
+
+ certifyResource(defaultResource);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ AssertJUnit.assertTrue(defaultResource.getVersion().equals("1.0"));
+
+ increaseResourceVersion(defaultResource, "1.1");
+
+ increaseResourceVersion(defaultResource, "1.2");
+ versionOnePointTwoIDWrapper.setInnerElement(defaultResource.getUniqueId());
+
+ increaseResourceVersion(defaultResource, "1.3");
+
+ increaseResourceVersion(defaultResource, "1.4");
+
+ }
+
+ protected void increaseResourceVersion(ResourceReqDetails defaultResource, String expectedVersion) throws IOException {
+ RestResponse response = LifecycleRestUtils.changeResourceState(defaultResource, sdncDesignerUser, LifeCycleStatesEnum.CHECKOUT);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ AssertJUnit.assertTrue(defaultResource.getVersion().equals(expectedVersion));
+ response = LifecycleRestUtils.changeResourceState(defaultResource, sdncDesignerUser, LifeCycleStatesEnum.CHECKIN);
+ AssertJUnit.assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ @Test
+ public void testServiceAuditLastUncertifiedVersion() throws Exception {
+
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ Wrapper<String> versionZeroOneIDWrapper = new Wrapper<String>(), versionZeroTwoIDWrapper = new Wrapper<String>();
+
+ createBasicServiceForAudit(versionZeroOneIDWrapper, versionZeroTwoIDWrapper, serviceDetails, false);
+
+ JsonElement element = getAuditJson(SERVICES_API, versionZeroTwoIDWrapper.getInnerElement());
+
+ assertTrue(element.getAsJsonArray().size() == 3);
+
+ }
+
+ @Test
+ public void testServiceAuditFirstUncertifiedVersion() throws Exception {
+
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ Wrapper<String> versionZeroOneIDWrapper = new Wrapper<String>(), versionZeroTwoIDWrapper = new Wrapper<String>();
+
+ createBasicServiceForAudit(versionZeroOneIDWrapper, versionZeroTwoIDWrapper, serviceDetails, false);
+
+ JsonElement element = getAuditJson(SERVICES_API, versionZeroOneIDWrapper.getInnerElement());
+
+ assertTrue(element.getAsJsonArray().size() == 3);
+
+ }
+
+ @Test
+ public void testResourceAuditUncertifiedVersion() throws Exception {
+
+ ResourceReqDetails defaultResource = ElementFactory.getDefaultResource();
+ Wrapper<String> versionOnePointTwoIDWrapper = new Wrapper<String>();
+
+ createBasicResourceForAudit(versionOnePointTwoIDWrapper, defaultResource);
+
+ JsonElement element = getAuditJson(RESOURCES_API, versionOnePointTwoIDWrapper.getInnerElement());
+
+ assertTrue(element.getAsJsonArray().size() == 3);
+
+ }
+
+ @Test
+ public void testResourceAuditCertifiedVersion() throws Exception {
+
+ ResourceReqDetails defaultResource = ElementFactory.getDefaultResource();
+ Wrapper<String> versionOnePointTwoIDWrapper = new Wrapper<String>();
+
+ createBasicResourceForAudit(versionOnePointTwoIDWrapper, defaultResource);
+
+ certifyResource(defaultResource);
+ assertTrue(defaultResource.getVersion().equals("2.0"));
+ String certifiedId = defaultResource.getUniqueId();
+
+ increaseResourceVersion(defaultResource, "2.1");
+
+ increaseResourceVersion(defaultResource, "2.2");
+
+ JsonElement element = getAuditJson(RESOURCES_API, certifiedId);
+
+ assertTrue(element.getAsJsonArray().size() == 13);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetServiceLatestVersionTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetServiceLatestVersionTest.java
new file mode 100644
index 0000000000..19bed4d380
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/GetServiceLatestVersionTest.java
@@ -0,0 +1,684 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_RESTRICTED_OPERATION;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentInstanceBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+public class GetServiceLatestVersionTest extends ComponentInstanceBaseTest {
+
+ protected ArtifactReqDetails heatArtifactDetails;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetServiceLatestVersionTest() {
+ super(name, GetServiceLatestVersionTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+ initMembers();
+ createAtomicResource(resourceDetailsVFC_01);
+ changeResourceStateToCertified(resourceDetailsVFC_01);
+ createAtomicResource(resourceDetailsCP_01);
+ changeResourceStateToCertified(resourceDetailsCP_01);
+ createAtomicResource(resourceDetailsVL_01);
+ changeResourceStateToCertified(resourceDetailsVL_01);
+ createVF(resourceDetailsVF_01);
+ certifyVf(resourceDetailsVF_01);
+ createService(serviceDetails_01);
+ createService(serviceDetails_02);
+ createService(serviceDetails_03);
+ createProduct(productDetails_01);
+ createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails); // create
+ // certified
+ // VF
+ // instance
+ // in
+ // service
+ /*
+ * RestResponse restResponse =
+ * LifecycleRestUtils.changeServiceState(serviceDetails_01,
+ * sdncDesignerDetails, LifeCycleStates.CHECKIN);
+ * ResourceRestUtils.checkSuccess(restResponse);
+ */
+ }
+
+ public void initMembers() throws Exception {
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ sdncPsDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1);
+ sdncPmDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1);
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncTesterDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ resourceDetailsVFC_01 = ElementFactory.getDefaultResourceByType("VFC100", NormativeTypesEnum.COMPUTE,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.VFC.toString()); // resourceType = VFC
+ resourceDetailsVF_01 = ElementFactory.getDefaultResourceByType("VF100", NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.VF.toString());
+ resourceDetailsCP_01 = ElementFactory.getDefaultResourceByType("CP100", NormativeTypesEnum.PORT,
+ ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.CP.toString());
+ resourceDetailsVL_01 = ElementFactory.getDefaultResourceByType("VL100", NormativeTypesEnum.NETWORK,
+ ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.VL.toString());
+ serviceDetails_01 = ElementFactory.getDefaultService("newtestservice1", ServiceCategoriesEnum.MOBILITY,
+ sdncDesignerDetails.getUserId());
+ serviceDetails_02 = ElementFactory.getDefaultService("newtestservice2", ServiceCategoriesEnum.MOBILITY,
+ sdncDesignerDetails.getUserId());
+ serviceDetails_03 = ElementFactory.getDefaultService("newtestservice3", ServiceCategoriesEnum.MOBILITY,
+ sdncDesignerDetails.getUserId());
+ productDetails_01 = ElementFactory.getDefaultProduct("product01");
+ }
+
+ @Test
+ public void getServicesLatestVersionServiceInCheckOutState() throws Exception {
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncPsDetails1);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertNull("No Service returned, one service in checkout state 0.1", servcieFromList);
+ }
+
+ @Test
+ public void getServicesLatestVersionServiceInCheckInState() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncPsDetails1);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.1"));
+ }
+
+ @Test
+ public void getServicesLatestVersionByPm() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncPmDetails1);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.1"));
+ }
+
+ @Test
+ public void getServicesLatestVersionByAdmin() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncAdminDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.1"));
+ }
+
+ @Test
+ public void getServicesLatestVersionService02CheckOutState() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ String serviceUniqueID = ResponseParser.getUniqueIdFromResponse(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceUniqueID);
+ assertTrue(servcieFromList.getVersion().equals("0.1"));
+ servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertNull(servcieFromList);
+ }
+
+ @Test
+ public void getServicesLatestVersionService02CheckInState() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.2"));
+ }
+
+ @Test
+ public void getServicesLatestVersionServiceWaitingForCertification() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.2"));
+ }
+
+ @Test
+ public void getServicesLatestVersionServiceCertificationInProgress() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.2"));
+ }
+
+ @Test
+ public void getServicesLatestVersionServiceCertificationFail() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.FAILCERTIFICATION);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.2"));
+ }
+
+ @Test
+ public void getServicesLatestVersionServiceCertifed() throws Exception {
+ certifyService(serviceDetails_01);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("1.0"));
+ }
+
+ @Test
+ public void getLatestVersionServiceHasSeveralCertifedVersion_01() throws Exception {
+ RestResponse certifyServiceResponse;
+ String serviceUniqueIdFromResponse = null;
+ int numberOfCertifiedService = 3;
+ for (int i = 0; i < numberOfCertifiedService; i++) {
+ certifyServiceResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(certifyServiceResponse);
+ if (i == (numberOfCertifiedService - 1)) {
+ serviceUniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(certifyServiceResponse);
+ }
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ }
+ // We have service with following versions : 1.0, 2.0 ,3.0 and
+ // 3.1(checkedOut)
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceUniqueIdFromResponse);
+ assertTrue(servcieFromList.getVersion().equals("3.0"));
+ }
+
+ @Test
+ public void getLatestVersionServiceHasSeveralCertifedVersions02() throws Exception {
+ RestResponse certifyServiceResponse;
+ certifyServiceResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(certifyServiceResponse);
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ certifyServiceResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(certifyServiceResponse);
+ // We have service with following versions : 1.0, 2.0
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("2.0"));
+ }
+
+ @Test
+ public void getLatestVersionServiceCertifedWasCheckedOutAndCheckedin() throws Exception {
+ RestResponse certifyServiceResponse;
+ int numberOfCertifiedService = 3;
+ for (int i = 0; i < numberOfCertifiedService; i++) {
+ certifyServiceResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(certifyServiceResponse);
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ }
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ // We have service with following versions : 1.0, 2.0 and 2.1(checkedIn)
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("3.1"));
+ }
+
+ @Test
+ public void getLatestVersionServiceCheckOutCertifedService() throws Exception {
+ RestResponse restResponse;
+ String serviceUniqueIdFromResponse = null;
+ RestResponse certifyServiceResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(certifyServiceResponse);
+ for (int i = 0; i < 11; i++) {
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ if (i == (10)) {
+ serviceUniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(restResponse);
+ }
+ }
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ // We have service with following versions : 1.0 and 1.11(Check-out)
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceUniqueIdFromResponse);
+ assertTrue(servcieFromList.getVersion().equals("1.11"));
+ }
+
+ @Test
+ public void getLatestVersionServiceCheckOutCheckInCertifedService() throws Exception {
+ RestResponse restResponse;
+ String serviceUniqueIdFromResponse = null;
+ RestResponse certifyServiceResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(certifyServiceResponse);
+ for (int i = 0; i < 12; i++) {
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ if (i == (11)) {
+ serviceUniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(restResponse);
+ }
+ }
+ // We have service with following versions : 1.0 and 1.11(Check-out)
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceUniqueIdFromResponse);
+ assertTrue(servcieFromList.getVersion().equals("1.12"));
+ }
+
+ @Test
+ public void getLatestVersionServiceCertifedCheckedOutAndInWaitingForCertificationState() throws Exception {
+ certifyService(serviceDetails_01); // 1.0
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ServiceRestUtils.checkSuccess(restResponse);
+ // We have service with following versions : 1.0 and 1.1(Waiting For
+ // Certification)
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("1.1"));
+ }
+
+ @Test
+ public void getLatestVersionServiceCertifedCheckedOutAndInCertificationInProgressState() throws Exception {
+ certifyService(serviceDetails_01); // 1.0
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ ServiceRestUtils.checkSuccess(restResponse);
+ // We have service with following versions : 1.0 and 1.1(Certification
+ // In Progress)
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("1.1"));
+ }
+
+ // DE190818
+ @Test(enabled = false)
+ public void getLatestVersionByNonAsdcUser() throws Exception {
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ nonAsdcUser.setUserId("gg750g");
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(nonAsdcUser);
+ assertTrue(getServicesLatestVersion.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ getServicesLatestVersion.getResponse());
+ }
+
+ // DE190818
+ @Test(enabled = false)
+ public void getLatestVersionUserIdIsEmpty() throws Exception {
+ User nonAsdcUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ nonAsdcUser.setUserId("");
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(nonAsdcUser);
+ assertTrue(getServicesLatestVersion.getErrorCode() == STATUS_CODE_RESTRICTED_OPERATION);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_OPERATION.name(), new ArrayList<String>(),
+ getServicesLatestVersion.getResponse());
+ }
+
+ @Test
+ public void getServicesLatestVersionByTester() throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncTesterDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 1);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_01.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.1"));
+ }
+
+ @Test
+ public void getLatestVersionSeveralServicesInDifferentVersion() throws Exception {
+ RestResponse restResponse = certifyService(serviceDetails_01); // 1.0
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = certifyService(serviceDetails_01);
+ ServiceRestUtils.checkSuccess(restResponse);
+ String service1_UniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse); // serviceDetails_01
+ // version is 2.1
+ // (check-out)
+
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_02, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse); // serviceDetails_02
+ // version 0.2
+ // (Check-in)
+
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_03, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ String service3_UniqueIdFromResponse = ResponseParser.getUniqueIdFromResponse(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails_03, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse); // serviceDetails_03
+ // version 0.2
+ // (Check-out)
+
+ RestResponse getServicesLatestVersion = ServiceRestUtils.getServiceLatestVersionList(sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(getServicesLatestVersion);
+ List<Service> serviceList = restResponseToResourceObjectList(getServicesLatestVersion);
+ assertTrue(serviceList.size() == 3);
+ Service servcieFromList = getResourceObjectFromResourceListByUid(serviceList, service1_UniqueIdFromResponse);
+ assertTrue(servcieFromList.getVersion().equals("2.0"));
+ servcieFromList = getResourceObjectFromResourceListByUid(serviceList, serviceDetails_02.getUniqueId());
+ assertTrue(servcieFromList.getVersion().equals("0.2"));
+ servcieFromList = getResourceObjectFromResourceListByUid(serviceList, service3_UniqueIdFromResponse);
+ assertTrue(servcieFromList.getVersion().equals("0.1"));
+ }
+
+ ///////////////////////////////////////////////////////////////
+ private RestResponse certifyService(ServiceReqDetails serviceDetails) throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKOUT);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ ServiceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeServiceState(serviceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ ServiceRestUtils.checkSuccess(restResponse);
+ return restResponse;
+ }
+
+ protected List<Service> restResponseToResourceObjectList(RestResponse restResponse) {
+ JsonElement jelement = new JsonParser().parse(restResponse.getResponse());
+ JsonArray jsonArray = jelement.getAsJsonArray();
+ List<Service> restResponseArray = new ArrayList<>();
+ Service service = null;
+ for (int i = 0; i < jsonArray.size(); i++) {
+ String serviceString = (String) jsonArray.get(i).toString();
+ service = ResponseParser.convertServiceResponseToJavaObject(serviceString);
+ restResponseArray.add(service);
+ }
+ return restResponseArray;
+ }
+
+ protected Service getResourceObjectFromResourceListByUid(List<Service> serviceList, String uid) {
+ if (serviceList != null && serviceList.size() > 0) {
+ for (Service service : serviceList) {
+ if (service.getUniqueId().equals(uid))
+ return service;
+ }
+ } else
+ return null;
+ return null;
+ }
+
+ private RestResponse changeResourceStateToCertified(ResourceReqDetails resourceDetails) throws Exception {
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponse;
+ if (restResponse.getErrorCode() == 200) {
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetails, sdncTesterDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ if (restResponse.getErrorCode() == 200) {
+ String newVersion = ResponseParser.getVersionFromResponse(restResponse);
+ resourceDetails.setVersion(newVersion);
+ resourceDetails.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ resourceDetails.setLastUpdaterUserId(sdncTesterDetails.getUserId());
+ resourceDetails.setLastUpdaterFullName(sdncTesterDetails.getFullName());
+ String uniqueIdFromRresponse = ResponseParser.getValueFromJsonResponse(restResponse.getResponse(),
+ "uniqueId");
+ resourceDetails.setUniqueId(uniqueIdFromRresponse);
+ }
+ }
+ return restResponse;
+ }
+
+ // private void certifyVf(ResourceReqDetails resource) throws Exception {
+ // RestResponse createAtomicResourceInstance =
+ // createAtomicInstanceForVFDuringSetup(resource, resourceDetailsVFC_01,
+ // sdncDesignerDetails);
+ // ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ // createAtomicResourceInstance =
+ // createAtomicInstanceForVFDuringSetup(resource, resourceDetailsCP_01,
+ // sdncDesignerDetails);
+ // ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ // createAtomicResourceInstance =
+ // createAtomicInstanceForVFDuringSetup(resource, resourceDetailsVL_01,
+ // sdncDesignerDetails);
+ // ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ // //createVFInstanceDuringSetup(service, resource, sdncDesignerDetails);
+ // RestResponse response =
+ // ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails,
+ // sdncDesignerDetails, resource.getUniqueId());
+ // ResourceRestUtils.checkSuccess(response);
+ // RestResponse changeResourceStateToCertified =
+ // changeResourceStateToCertified(resource);
+ // ResourceRestUtils.checkSuccess(changeResourceStateToCertified);
+ // }
+
+ private void certifyVf(ResourceReqDetails resource) throws Exception {
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String cpCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, resourceDetailsVFC_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String computeCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resource, resourceDetailsVL_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String vlCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ // Fixing Vl/Cp req/cap
+ ComponentTypeEnum containerCompType = ComponentTypeEnum.RESOURCE;
+ User user = sdncDesignerDetails;
+ fulfillCpRequirement(resource, cpCompInstId, computeCompInstId, computeCompInstId, user, containerCompType);
+ consumeVlCapability(resource, cpCompInstId, vlCompInstId, cpCompInstId, user, containerCompType);
+
+ RestResponse response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails,
+ sdncDesignerDetails, resource.getUniqueId());
+ ResourceRestUtils.checkSuccess(response);
+ RestResponse changeResourceStateToCertified = changeResourceStateToCertified(resource);
+ ResourceRestUtils.checkSuccess(changeResourceStateToCertified);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ReqCapOccurrencesTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ReqCapOccurrencesTest.java
new file mode 100644
index 0000000000..c21deafcd8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ReqCapOccurrencesTest.java
@@ -0,0 +1,1191 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.apache.http.client.ClientProtocolException;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentInstanceBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.general.ImportUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ReqCapOccurrencesTest extends ComponentInstanceBaseTest {
+
+ private ImportReqDetails importReqDetails1; // atomic resource
+ private ImportReqDetails importReqDetails2;
+ private ImportReqDetails importReqDetails3;
+ private ImportReqDetails importReqDetails4;
+ private Resource resourceVFC1;
+ private Resource resourceVFC2;
+ private Resource resourceVFC3;
+ private Resource resourceVFC4;
+ private ResourceReqDetails resourceDetailsVF100;
+ private ResourceReqDetails resourceDetailsVF200;
+ private Resource resourceVF100;
+ private Resource resourceVF200;
+ protected String testResourcesPath;
+
+ protected final String importYmlWithReq11 = "softwareComponentReq11.yml";
+ protected final String importYmlWithReq12 = "softwareComponentReq12.yml";
+ protected final String importYmlWithCap11 = "computeCap11.yml";
+ protected final String importYmlWithCap1Unbounded = "computeCap1UNBOUNDED.yml";
+ protected final String capabilitiesAndRequirementsType = "tosca.capabilities.Container";
+
+ public ReqCapOccurrencesTest() {
+ super(new TestName(), ReqCapOccurrencesTest.class.getSimpleName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+ // Do not use call init() from ComponentInstanceBaseTest
+ expectedContainerCapabilities = new LinkedHashMap<String, List<CapabilityDefinition>>();
+ expectedContainerRequirements = new LinkedHashMap<String, List<RequirementDefinition>>();
+ removedRequirements = new HashMap<>();
+ expectedContInstReqCap = new HashMap<>();
+
+ RestResponse importResourceResponse;
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // import yml file location
+ String sourceDir = config.getResourceConfigDir();
+ final String workDir = "importToscaResourceByCreateUrl";
+ testResourcesPath = sourceDir + File.separator + workDir;
+ ///// Create atomic resources /////////////////////////
+ // import VFC1 with Requirements : MIN=1 MAX=2
+ ///// (tosca.capabilities.Container)
+ importReqDetails1 = ElementFactory.getDefaultImportResource("VFC1");
+ importResourceResponse = importedResource(importReqDetails1, importYmlWithReq12);
+ // resourceVFC1 =
+ // ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(importReqDetails1, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ resourceVFC1 = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ // import VFC2 with Capabilities : MIN 1 MAX UNBOUNDED
+ // (tosca.capabilities.Container)
+ importReqDetails2 = ElementFactory.getDefaultImportResource("VFC2");
+ importResourceResponse = importedResource(importReqDetails2, importYmlWithCap1Unbounded);
+ // resourceVFC2 =
+ // ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ restResponse = LifecycleRestUtils.changeResourceState(importReqDetails2, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ resourceVFC2 = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ // import VFC3 with Capabilities : MIN 1 MAX 1
+ // (tosca.capabilities.Container)
+ importReqDetails3 = ElementFactory.getDefaultImportResource("VFC3");
+ importResourceResponse = importedResource(importReqDetails3, importYmlWithCap11);
+ // resourceVFC3 =
+ // ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ restResponse = LifecycleRestUtils.changeResourceState(importReqDetails3, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ resourceVFC3 = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ // import VFC4 with Requirements : MIN 1 MAX 1
+ // (tosca.capabilities.Container)
+ importReqDetails4 = ElementFactory.getDefaultImportResource("VFC4");
+ importResourceResponse = importedResource(importReqDetails4, importYmlWithReq11);
+ // resourceVFC4 =
+ // ResponseParser.convertResourceResponseToJavaObject(importResourceResponse.getResponse());
+ restResponse = LifecycleRestUtils.changeResourceState(importReqDetails4, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ resourceVFC4 = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+
+ // create VF100
+ resourceDetailsVF100 = ElementFactory.getDefaultResourceByType("VF1000", NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.VF.toString());
+ RestResponse createResourceVF100 = ResourceRestUtils.createResource(resourceDetailsVF100, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceVF100);
+ // create VF200
+ resourceDetailsVF200 = ElementFactory.getDefaultResourceByType("VF2000", NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, sdncDesignerDetails.getUserId(),
+ ResourceTypeEnum.VF.toString());
+ RestResponse createResourceVF200 = ResourceRestUtils.createResource(resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createResourceVF200);
+ // Create Service
+ serviceDetails_01 = ElementFactory.getDefaultService("newtestservice1", ServiceCategoriesEnum.MOBILITY,
+ sdncDesignerDetails.getUserId());
+ RestResponse createServiceRestResponse = ServiceRestUtils.createService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createServiceRestResponse);
+
+ }
+
+ // US628514 Capability/Requirement "Occurrences" attribute in CREATE/DELETE
+ // Relation APIs
+ // Container = SERVICE , Container instance = VF
+ @Test
+ public void capAndReqOccurrencesInServiceAndHisInstancesNoAssociation() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC1 (Req MIN=1 MAX=2) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC21 (Cap MIN=1 MAX=UNBOUNDED) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "1";
+ String containerMaxReq = "2";
+ String containerMinCap = "1";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ }
+
+ @Test
+ public void serviceInstanceAssociationReqMaxOccurrencesNotReached() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC1 (Req MIN=1 MAX=2) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC2 (Cap MIN=1 MAX=UNBOUNDED) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VFs
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ }
+
+ @Test
+ public void serviceInstanceAssociationReqMaxOccurrencesIsReached() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC4 (Req MIN=1 MAX=1) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails4, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC2 (Cap MIN=1 MAX=UNBOUNDED) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VFs
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "0";
+ String containerMinCap = "0";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ }
+
+ @Test
+ public void associateServiceInstanceWhenReqMaxOccurrencesAlreadyReached() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC4 (Req MIN=1 MAX=1) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails4, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC2 (Cap MIN=1 MAX=UNBOUNDED) to VF2.00
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String vf1Name = ResponseParser.getValueFromJsonResponse(createVFInstResp.getResponse(), "name");
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String vf2Name = ResponseParser.getValueFromJsonResponse(createVFInstResp.getResponse(), "name");
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VFs
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ // associate same instances again - when requirement Max Occurrences
+ // reached
+ associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, sdncDesignerDetails,
+ serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_NOT_FOUND,
+ associateInstances.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(vf1Name);
+ varibales.add(vf2Name);
+ varibales.add("host");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_MATCH_NOT_FOUND.name(), varibales,
+ associateInstances.getResponse());
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "0";
+ String containerMinCap = "0";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ }
+
+ @Test
+ public void serviceInstanceAssociationCapMaxOccurrencesIsReached() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC1 (Req MIN=1 MAX=2) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC3 (Cap MIN=1 MAX=1) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VFs
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ }
+
+ @Test
+ public void associationServiceInstanceWhenCapMaxOccurrencesAlreadyReached() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC1 (Req MIN=1 MAX=2) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC3 (Cap MIN=1 MAX=1) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ // RestResponse createVFInstResp = createVFInstance(serviceDetails_01,
+ // resourceDetailsVF100, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String vf1Name = ResponseParser.getValueFromJsonResponse(createVFInstResp.getResponse(), "name");
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ // createVFInstResp = createVFInstance(serviceDetails_01,
+ // resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String vf2Name = ResponseParser.getValueFromJsonResponse(createVFInstResp.getResponse(), "name");
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VFs
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ // associate same instances again - when requirement Max Occurrences
+ // reached
+ associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, sdncDesignerDetails,
+ serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_NOT_FOUND,
+ associateInstances.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(vf1Name);
+ varibales.add(vf2Name);
+ varibales.add("host");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND.name(),
+ varibales, associateInstances.getResponse());
+ }
+
+ @Test
+ public void associationAndDisassociateServiceInstancesWhenReqMaxOccurrencesAlreadyReached()
+ throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC4 (Req MIN=1 MAX=1) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails4, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC3 (Cap MIN=1 MAX=1) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ RestResponse createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ sdncDesignerDetails);
+ // RestResponse createVFInstResp = createVFInstance(serviceDetails_01,
+ // resourceDetailsVF100, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ // createVFInstResp = createVFInstance(serviceDetails_01,
+ // resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VF Instances
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "0";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ // Disassociate 2 VF Instances
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS,
+ dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF100.getUniqueId()).isEmpty());
+ // get service and verify Occurrences in container and container
+ // instance requirements and Capabilities
+ getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ containerMinReq = "1";
+ containerMaxReq = "1";
+ containerMinCap = "1";
+ containerMaxCap = "1";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ }
+
+ @Test(enabled = false)
+ public void aaaa() throws Exception, Exception {
+ RestResponse getResourseRestResponse;
+ // Add instance of VFC1 (Req MIN=1 MAX=2) to VF1000
+ ComponentInstance componentInstanceReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceReq);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Add instance of VFC3 (Cap MIN=1 MAX=1) to VF2000
+ ComponentInstance componentInstanceCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF200);
+ assertNotNull(componentInstanceCap);
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF200.getUniqueId());
+ resourceVF200 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Check-In both VFs
+ RestResponse restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF100, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ restResponse = LifecycleRestUtils.changeResourceState(resourceDetailsVF200, sdncDesignerDetails,
+ LifeCycleStatesEnum.CHECKIN);
+ ResourceRestUtils.checkSuccess(restResponse);
+ // Create VF instances
+ // RestResponse createVFInstResp =
+ // createVFInstanceDuringSetup(serviceDetails_01, resourceDetailsVF100,
+ // sdncDesignerDetails);
+ RestResponse createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF100, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String vf1Name = ResponseParser.getValueFromJsonResponse(createVFInstResp.getResponse(), "name");
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // createVFInstResp = createVFInstanceDuringSetup(serviceDetails_01,
+ // resourceDetailsVF200, sdncDesignerDetails);
+ createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF200, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String vf2Name = ResponseParser.getValueFromJsonResponse(createVFInstResp.getResponse(), "name");
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // associate 2 VFs
+ String capType = capabilitiesAndRequirementsType;
+ String reqName = "host";
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList, componentInstanceReq.getUniqueId(), componentInstanceCap.getUniqueId());
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+ // get service
+ RestResponse getServiceResponse = ServiceRestUtils.getService(serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getServiceResponse);
+ Service service = ResponseParser.parseToObjectUsingMapper(getServiceResponse.getResponse(), Service.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType, containerMinReq,
+ containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(service, capabilitiesAndRequirementsType,
+ resourceVF200, resourceVF100);
+ // associate same instances again - when requirement Max Occurrences
+ // reached
+ associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, sdncDesignerDetails,
+ serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_NOT_FOUND,
+ associateInstances.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(vf1Name);
+ varibales.add(vf2Name);
+ varibales.add("host");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND.name(),
+ varibales, associateInstances.getResponse());
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////
+ // US628514 Capability/Requirement "Occurrences" attribute in CREATE/DELETE
+ ////////////////////////////////////////////////////////////////////////////////////////////////// Relation
+ ////////////////////////////////////////////////////////////////////////////////////////////////// APIs
+ // Container = VF , Container instance = VFC
+ @Test
+ public void capAndReqOccurrencesInVfAndHisInstancesNoAssociation() throws Exception, Exception {
+ // Add VFC1 and VFC2 instances in VF
+ ComponentInstance createComponentInstance1 = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(createComponentInstance1);
+ ComponentInstance createComponentInstance2 = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(createComponentInstance2);
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "1";
+ String containerMaxReq = "2";
+ String containerMinCap = "1";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC2, resourceVFC1);
+ }
+
+ @Test
+ public void vfInstanceAssociationReqMaxOccurrencesNotReached() throws Exception, Exception {
+ // Add VFC1 (with Requirements: tosca.capabilities.Container, MIN=1
+ // MAX=2) instance to VF
+ ComponentInstance componentInstanceWithReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithReq);
+ // Add VFC2 (with Capabilities: tosca.capabilities.Container, MIN=1,
+ // MAX=UNBOUNDED ) instance to VF
+ ComponentInstance componentInstanceWithCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithCap);
+ // associate Instances
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap(resourceDetailsVF100);
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(componentInstanceWithReq,
+ componentInstanceWithCap, capReqDefBeforeAssociate);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF100.getUniqueId()));
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC2, resourceVFC1);
+
+ }
+
+ @Test
+ public void vfInstanceAssociationReqMaxOccurrencesIsReached() throws Exception, Exception {
+ // Add VFC4 (with Requirements: tosca.capabilities.Container, MIN=1
+ // MAX=1) instance to VF
+ ComponentInstance componentInstanceWithReq = createComponentInstance(importReqDetails4, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithReq);
+ // Add VFC2 (with Capabilities: tosca.capabilities.Container, MIN=1,
+ // MAX=UNBOUNDED ) instance to VF
+ ComponentInstance componentInstanceWithCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithCap);
+ // associate Instances
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap(resourceDetailsVF100);
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(componentInstanceWithReq,
+ componentInstanceWithCap, capReqDefBeforeAssociate);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF100.getUniqueId()));
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "0";
+ String containerMinCap = "0";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC2, resourceVFC4);
+ }
+
+ @Test
+ public void associateVfInstanceWhenReqMaxOccurrencesAlreadyReached() throws Exception, Exception {
+ // Add VFC4 (with Requirements: tosca.capabilities.Container, MIN=1
+ // MAX=1) instance to VF
+ ComponentInstance componentInstanceWithReq = createComponentInstance(importReqDetails4, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithReq);
+ // Add VFC2 (with Capabilities: tosca.capabilities.Container, MIN=1,
+ // MAX=UNBOUNDED ) instance to VF
+ ComponentInstance componentInstanceWithCap = createComponentInstance(importReqDetails2, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithCap);
+ // associate Instances
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap(resourceDetailsVF100);
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(componentInstanceWithReq,
+ componentInstanceWithCap, capReqDefBeforeAssociate);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF100.getUniqueId()));
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "0";
+ String containerMinCap = "0";
+ String containerMaxCap = "UNBOUNDED";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC2, resourceVFC4);
+ // associate same instances again - when requirement Max Occurrences
+ // reached
+ associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_NOT_FOUND,
+ associateInstances.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(componentInstanceWithReq.getName());
+ varibales.add(componentInstanceWithCap.getName());
+ varibales.add("host");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_MATCH_NOT_FOUND.name(), varibales,
+ associateInstances.getResponse());
+
+ }
+
+ @Test
+ public void vfInstanceAssociationCapMaxOccurrencesIsReached() throws Exception, Exception {
+ // Add VFC1 (with Requirements: tosca.capabilities.Container, MIN=1
+ // MAX=2) instance to VF
+ ComponentInstance componentInstanceWithReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithReq);
+ // Add VFC3 (with Capabilities: tosca.capabilities.Container, MIN=1
+ // MAX=1 ) instance to VF
+ ComponentInstance componentInstanceWithCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithCap);
+ // associate Instances
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap(resourceDetailsVF100);
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(componentInstanceWithReq,
+ componentInstanceWithCap, capReqDefBeforeAssociate);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF100.getUniqueId()));
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC3, resourceVFC1);
+ }
+
+ @Test
+ public void associationVfInstanceWhenCapMaxOccurrencesAlreadyReached() throws Exception, Exception {
+ // Add VFC1 (with Requirements: tosca.capabilities.Container, MIN=1
+ // MAX=2) instance to VF
+ ComponentInstance componentInstanceWithReq = createComponentInstance(importReqDetails1, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithReq);
+ // Add VFC3 (with Capabilities: tosca.capabilities.Container, MIN=1
+ // MAX=1 ) instance to VF
+ ComponentInstance componentInstanceWithCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithCap);
+ // associate Instances
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap(resourceDetailsVF100);
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(componentInstanceWithReq,
+ componentInstanceWithCap, capReqDefBeforeAssociate);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", STATUS_CODE_SUCCESS, associateInstances.getErrorCode().intValue());
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF100.getUniqueId()));
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "1";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC3, resourceVFC1);
+ // associate same instances again - when requirement Max Occurrences
+ // reached
+ associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_NOT_FOUND,
+ associateInstances.getErrorCode().intValue());
+ ArrayList<String> varibales = new ArrayList<String>();
+ varibales.add(componentInstanceWithReq.getName());
+ varibales.add(componentInstanceWithCap.getName());
+ varibales.add("host");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND.name(),
+ varibales, associateInstances.getResponse());
+ }
+
+ @Test
+ public void associationAndDisassociateVfInstancesWhenReqMaxOccurrencesAlreadyReached() throws Exception, Exception {
+ // Add VFC4 (with Requirements: tosca.capabilities.Container, MIN=1
+ // MAX=1) instance to VF
+ ComponentInstance componentInstanceWithReq = createComponentInstance(importReqDetails4, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithReq);
+ // Add VFC3 (with Capabilities: tosca.capabilities.Container, MIN=1
+ // MAX=1 ) instance to VF
+ ComponentInstance componentInstanceWithCap = createComponentInstance(importReqDetails3, sdncDesignerDetails,
+ resourceDetailsVF100);
+ assertNotNull(componentInstanceWithCap);
+ // associate Instances
+ CapReqDef capReqDefBeforeAssociate = getResourceReqCap(resourceDetailsVF100);
+ RequirementCapabilityRelDef requirementDef = setRelationshipBetweenInstances(componentInstanceWithReq,
+ componentInstanceWithCap, capReqDefBeforeAssociate);
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ assertTrue(checkRealtionship(requirementDef.getFromNode(), requirementDef.getToNode(),
+ resourceDetailsVF100.getUniqueId()));
+ // GET resource
+ RestResponse getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ String containerMinReq = "0";
+ String containerMaxReq = "0";
+ String containerMinCap = "0";
+ String containerMaxCap = "0";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC3, resourceVFC4);
+ // Disassociate 2 Instances
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ sdncDesignerDetails, resourceDetailsVF100.getUniqueId(), ComponentTypeEnum.RESOURCE);
+ assertEquals("Check response code ", BaseRestUtils.STATUS_CODE_SUCCESS,
+ dissociateInstances.getErrorCode().intValue());
+ assertTrue(getComponentInstancesRelations(resourceDetailsVF100.getUniqueId()).isEmpty());
+ // GET resource
+ getResourseRestResponse = ResourceRestUtils.getResource(sdncDesignerDetails,
+ resourceDetailsVF100.getUniqueId());
+ resourceVF100 = ResponseParser.parseToObjectUsingMapper(getResourseRestResponse.getResponse(), Resource.class);
+ // Verify Container requirements and Capabilities
+ containerMinReq = "1";
+ containerMaxReq = "1";
+ containerMinCap = "1";
+ containerMaxCap = "1";
+ verifyContainerCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ containerMinReq, containerMaxReq, containerMinCap, containerMaxCap);
+ verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(resourceVF100, capabilitiesAndRequirementsType,
+ resourceVFC3, resourceVFC4);
+ }
+
+ ///////////////////////////////////////
+
+ private boolean checkRealtionship(String fromNode, String toNode, String resourceUniqueId) throws Exception {
+ List<RequirementCapabilityRelDef> componentInstancesRelations = getComponentInstancesRelations(
+ resourceUniqueId);
+ RequirementCapabilityRelDef requirementCapabilityRelDef = componentInstancesRelations.get(0);
+ boolean fromNodeCheck = requirementCapabilityRelDef.getFromNode().equals(fromNode);
+ boolean toNodeCheck = requirementCapabilityRelDef.getToNode().equals(toNode);
+
+ return fromNodeCheck && toNodeCheck;
+ }
+
+ private List<RequirementCapabilityRelDef> getComponentInstancesRelations(String resourceUniqueId)
+ throws ClientProtocolException, IOException {
+ Resource resource = getVfAsResourceObject(resourceUniqueId);
+ List<RequirementCapabilityRelDef> componenRelationInstances = resource.getComponentInstancesRelations();
+
+ return componenRelationInstances;
+ }
+
+ private Resource getVfAsResourceObject(String resourceUniqueId) throws ClientProtocolException, IOException {
+ RestResponse getResource = ResourceRestUtils.getResource(resourceUniqueId);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(getResource.getResponse(), Resource.class);
+ return resource;
+ }
+
+ private RequirementCapabilityRelDef setRelationshipBetweenInstances(ComponentInstance riReq,
+ ComponentInstance riCap, CapReqDef capReqDef) throws Exception {
+
+ String capbilityUid = capReqDef.getCapabilities().get("tosca.capabilities.Container").get(0).getUniqueId();
+ String requirementUid = capReqDef.getRequirements().get("tosca.capabilities.Container").get(0).getUniqueId();
+
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ requirementDef.setFromNode(riReq.getUniqueId());
+ requirementDef.setToNode(riCap.getUniqueId());
+
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair();
+ pair.setRequirementOwnerId(riReq.getUniqueId());
+ pair.setCapabilityOwnerId(riCap.getUniqueId());
+ pair.setRequirement("host");
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType("tosca.capabilities.Container");
+ pair.setRelationships(relationship);
+ pair.setCapabilityUid(capbilityUid);
+ pair.setRequirementUid(requirementUid);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<>();
+ relationships.add(pair);
+ requirementDef.setRelationships(relationships);
+ return requirementDef;
+ }
+
+ private CapReqDef getResourceReqCap(ResourceReqDetails res) throws IOException {
+ RestResponse getResourceBeforeAssociate = ComponentRestUtils
+ .getComponentRequirmentsCapabilities(sdncDesignerDetails, resourceDetailsVF100);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceBeforeAssociate.getResponse(), CapReqDef.class);
+ return capReqDef;
+ }
+
+ private RestResponse importedResource(ImportReqDetails importReqDetails, String ymlFile) throws Exception {
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, testResourcesPath,
+ ymlFile);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails,
+ sdncDesignerDetails, null);
+ assertEquals("Check response code after importing resource", BaseRestUtils.STATUS_CODE_CREATED,
+ importResourceResponse.getErrorCode().intValue());
+ return importResourceResponse;
+ }
+
+ private ComponentInstance createComponentInstance(ResourceReqDetails res, User user, ResourceReqDetails vf)
+ throws Exception {
+ RestResponse response = ResourceRestUtils.createResourceInstance(res, user, vf.getUniqueId());
+ ResourceRestUtils.checkCreateResponse(response);
+ ComponentInstance compInstance = ResponseParser.parseToObject(response.getResponse(), ComponentInstance.class);
+ return compInstance;
+ }
+
+ private void verifyContainerCapabilitiesAndRequirementsOccurrences(Component component,
+ String CapabilitiesAndRequirementsType, String minReqOccurrences, String maxReqOccurrences,
+ String minCapabilities, String maxCapabilities) throws Exception {
+ boolean isRequirementAppear = false;
+ boolean isCapabilityAppear = false;
+ List<RequirementDefinition> requirements;
+ List<CapabilityDefinition> capabilities;
+ requirements = component.getRequirements().get(CapabilitiesAndRequirementsType);
+ if (maxReqOccurrences == "0") {
+ assertTrue(requirements == null);
+ } // if container MAX requirement = 0
+ if (maxReqOccurrences != "0") {
+ assertNotNull(requirements);
+ for (RequirementDefinition req : requirements) {
+ switch (req.getName()) {
+ case "host":
+ assertTrue("Check Min Requirement Occurrences ", req.getMinOccurrences().equals(minReqOccurrences));
+ assertTrue("Check Max Requirement Occurrences ", req.getMaxOccurrences().equals(maxReqOccurrences));
+ isRequirementAppear = true;
+ break;
+ }
+ assertTrue(isRequirementAppear);
+ isRequirementAppear = false;
+ }
+ }
+ // Container Capabilities
+ capabilities = component.getCapabilities().get(CapabilitiesAndRequirementsType);
+ if (maxCapabilities == "0") {// if container MAX capabilities = 0
+ assertTrue(capabilities == null);
+ }
+ if (maxCapabilities != "0") {
+ assertNotNull(capabilities);
+ for (CapabilityDefinition cap : capabilities) {
+ switch (cap.getName()) {
+ case "host":
+ assertTrue("Check Min capability Occurrences ", cap.getMinOccurrences().equals(minCapabilities));
+ assertTrue("Check Max capability Occurrences ", cap.getMaxOccurrences().equals(maxCapabilities));
+ isCapabilityAppear = true;
+ break;
+ }
+ assertTrue(isCapabilityAppear);
+ isCapabilityAppear = false;
+ }
+ }
+
+ }
+
+ private void verifyContainerInstanceCapabilitiesAndRequirementsOccurrences(Component component,
+ String CapabilitiesAndRequirementsType, Resource vfWithCapabilities, Resource vfWithRequirements)
+ throws Exception {
+ boolean isCapReqAppear = false;
+ List<ComponentInstance> listOfComponentInstances = component.getComponentInstances();
+
+ for (ComponentInstance instance : listOfComponentInstances) {
+ if (instance.getComponentUid().equals(vfWithCapabilities.getUniqueId())) {
+ List<CapabilityDefinition> capFromResource = vfWithCapabilities.getCapabilities()
+ .get(CapabilitiesAndRequirementsType);
+ List<CapabilityDefinition> capFromInstance = instance.getCapabilities()
+ .get(CapabilitiesAndRequirementsType);
+ for (CapabilityDefinition resourceCap : capFromResource)
+ for (CapabilityDefinition instanceReq : capFromInstance) {
+ if (resourceCap.getUniqueId().equals(instanceReq.getUniqueId())) {
+ assertTrue("Check Min capability Occurrences ",
+ resourceCap.getMinOccurrences().equals(instanceReq.getMinOccurrences()));
+ assertTrue("Check Max capability Occurrences ",
+ resourceCap.getMaxOccurrences().equals(instanceReq.getMaxOccurrences()));
+ isCapReqAppear = true;
+ break;
+ }
+
+ }
+ }
+
+ if (instance.getComponentUid().equals(vfWithRequirements.getUniqueId())) {
+ List<RequirementDefinition> reqFromAtomicResource = vfWithRequirements.getRequirements()
+ .get(CapabilitiesAndRequirementsType);
+ List<RequirementDefinition> reqFromInstance = instance.getRequirements()
+ .get(CapabilitiesAndRequirementsType);
+ for (RequirementDefinition resourceReq : reqFromAtomicResource)
+ for (RequirementDefinition instanceReq : reqFromInstance) {
+ if (resourceReq.getUniqueId().equals(instanceReq.getUniqueId())) {
+ assertTrue("Check Min Requirement Occurrences ",
+ resourceReq.getMinOccurrences().equals(instanceReq.getMinOccurrences()));
+ assertTrue("Check Max Requirement Occurrences ",
+ resourceReq.getMaxOccurrences().equals(instanceReq.getMaxOccurrences()));
+ isCapReqAppear = true;
+ break;
+ }
+ }
+ }
+ assertTrue(isCapReqAppear);
+ isCapReqAppear = false;
+ }
+
+ }
+
+ private RequirementCapabilityRelDef getReqCapRelation(String reqCompInstId, String capCompInstId, String capType,
+ String reqName, List<CapabilityDefinition> capList, List<RequirementDefinition> reqList,
+ String vfc1UniqueId, String vfc2UniqueId) {
+ return ElementFactory.getReqCapRelation(reqCompInstId, capCompInstId, vfc1UniqueId, vfc2UniqueId, capType,
+ reqName, capList, reqList);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ServiceComponentInstanceCRUDTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ServiceComponentInstanceCRUDTest.java
new file mode 100644
index 0000000000..b2728ea0da
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/ServiceComponentInstanceCRUDTest.java
@@ -0,0 +1,1624 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.codehaus.jettison.json.JSONException;
+import org.json.JSONArray;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentInstanceBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.BaseValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ServiceComponentInstanceCRUDTest extends ComponentInstanceBaseTest {
+ private static Logger log = LoggerFactory.getLogger(ServiceComponentInstanceCRUDTest.class.getName());
+ private static final String SPACE_STRING = " ";
+ private static String REQUIREMENT_NAME = "host";
+ private static String CAPABILITY_TYPE = "tosca.capabilities.Container";
+
+ private String reqOwnerId;
+ private String capOwnerId;
+
+ public ServiceComponentInstanceCRUDTest() {
+ super(new TestName(), ServiceComponentInstanceCRUDTest.class.getSimpleName());
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void before() throws Exception {
+ init();
+ createComponents();
+ }
+
+ private void createComponents() throws Exception {
+ createAtomicResource(resourceDetailsVFC_01);
+ createAtomicResource(resourceDetailsVFC_02);
+ createAtomicResource(resourceDetailsCP_01);
+ createAtomicResource(resourceDetailsVL_01);
+ createAtomicResource(resourceDetailsVL_02);
+ createVF(resourceDetailsVF_01);
+ createVF(resourceDetailsVF_02);
+ createService(serviceDetails_01);
+ certifyResource(resourceDetailsVFC_01);
+ certifyResource(resourceDetailsVFC_02);
+ RestResponse createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resourceDetailsVF_01,
+ resourceDetailsVFC_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ reqOwnerId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+ createAtomicResourceInstance = createAtomicInstanceForVFDuringSetup(resourceDetailsVF_02, resourceDetailsVFC_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ capOwnerId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);// should
+ // be
+ // updated
+ // to
+ // getUniqueIdOfFirstInstance
+ // in
+ // service
+ // context
+ }
+
+ private void certifyResource(ResourceReqDetails resource) throws Exception {
+ changeResourceLifecycleState(resource, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ changeResourceLifecycleState(resource, sdncTesterDetails.getUserId(), LifeCycleStatesEnum.STARTCERTIFICATION);
+ changeResourceLifecycleState(resource, sdncTesterDetails.getUserId(), LifeCycleStatesEnum.CERTIFY);
+ }
+
+ private void changeResourceLifecycleState(ResourceReqDetails resourceDetails, String userUserId,
+ LifeCycleStatesEnum lifeCycleStates) throws Exception {
+ RestResponse response = LifecycleRestUtils.changeResourceState(resourceDetails, userUserId, lifeCycleStates);
+ LifecycleRestUtils.checkLCS_Response(response);
+ }
+
+ private void changeServiceLifecycleState(ServiceReqDetails serviceDetails, User user,
+ LifeCycleStatesEnum lifeCycleStates) throws Exception {
+ RestResponse response = LifecycleRestUtils.changeServiceState(serviceDetails, user, lifeCycleStates);
+ LifecycleRestUtils.checkLCS_Response(response);
+ }
+
+ private void createVFInstanceFailWithoutChangeState(ActionStatus actionStatus, List<String> variables,
+ ResourceReqDetails vfResource, User user, int errorCode) throws Exception {
+ RestResponse createVFInstanceSuccessfullyWithoutChangeStateResp = createVFInstance(serviceDetails_01,
+ vfResource, user);
+ checkErrorMessage(actionStatus, variables, errorCode, createVFInstanceSuccessfullyWithoutChangeStateResp);
+ }
+
+ private void createVFInstanceFail(ActionStatus actionStatus, List<String> variables, ResourceReqDetails vfResource,
+ User user, int errorCode) throws Exception, FileNotFoundException, JSONException {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, vfResource, user);
+ checkErrorMessage(actionStatus, variables, errorCode, createVFInstResp);
+ }
+
+ private void deleteVFInstanceFail(ActionStatus actionStatus, List<String> variables, ResourceReqDetails vfResource,
+ User user, int errorCode) throws Exception, FileNotFoundException, JSONException {
+ RestResponse deleteVFInstResp = deleteVFInstance(vfResource.getUniqueId(), serviceDetails_01, user);
+ checkErrorMessage(actionStatus, variables, errorCode, deleteVFInstResp);
+ }
+
+ private void createAtomicResourceInstanceFailWithoutChangeState(ActionStatus actionStatus, List<String> variables,
+ ResourceReqDetails atomicResource, User user, int errorCode)
+ throws Exception, FileNotFoundException, JSONException {
+ RestResponse createAtomicInstResp = createAtomicInstanceForService(serviceDetails_01, atomicResource, user);
+ checkErrorMessage(actionStatus, variables, errorCode, createAtomicInstResp);
+ }
+
+ private void createAtomicResourceInstanceFail(ActionStatus actionStatus, List<String> variables,
+ ResourceReqDetails atomicResource, User user, int errorCode)
+ throws Exception, FileNotFoundException, JSONException {
+ RestResponse createAtomicInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, atomicResource,
+ user);
+ checkErrorMessage(actionStatus, variables, errorCode, createAtomicInstResp);
+ }
+
+ private void deleteAtomicResourceInstanceFail(ActionStatus actionStatus, List<String> variables,
+ ResourceReqDetails atomicResource, User user, int errorCode)
+ throws Exception, FileNotFoundException, JSONException {
+ RestResponse deleteAtomicInstResp = deleteAtomicInstanceForService(atomicResource.getUniqueId(),
+ serviceDetails_01, user);
+ checkErrorMessage(actionStatus, variables, errorCode, deleteAtomicInstResp);
+ }
+
+ private void checkErrorMessage(ActionStatus actionStatus, List<String> variables, int errorCode,
+ RestResponse response) throws Exception {
+
+ log.debug(response.getResponse());
+ AssertJUnit.assertEquals(errorCode, response.getErrorCode().intValue());
+ ErrorValidationUtils.checkBodyResponseOnError(actionStatus.name(), variables, response.getResponse());
+ }
+
+ private RestResponse createCheckedinVFInstance(ServiceReqDetails containerDetails,
+ ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ changeResourceLifecycleState(compInstOriginDetails, compInstOriginDetails.getCreatorUserId(),
+ LifeCycleStatesEnum.CHECKIN);
+ return createVFInstance(containerDetails, compInstOriginDetails, modifier);
+ }
+
+ private RestResponse createCheckedinAtomicInstanceForService(ServiceReqDetails containerDetails,
+ ResourceReqDetails compInstOriginDetails, User modifier) throws Exception {
+ changeResourceLifecycleState(compInstOriginDetails, compInstOriginDetails.getCreatorUserId(),
+ LifeCycleStatesEnum.CHECKIN);
+ return createAtomicInstanceForService(containerDetails, compInstOriginDetails, modifier);
+ }
+
+ private void createVFInstanceAndAtomicResourceInstanceWithoutCheckin(ResourceReqDetails vf,
+ ResourceReqDetails atomicResource, User user) throws Exception {
+ RestResponse createVFInstance = createVFInstance(serviceDetails_01, vf, user);
+ ResourceRestUtils.checkCreateResponse(createVFInstance);
+ RestResponse atomicInstanceForService = createAtomicInstanceForService(serviceDetails_01, atomicResource, user);
+ ResourceRestUtils.checkCreateResponse(atomicInstanceForService);
+ }
+
+ private void createVFInstanceAndAtomicResourceInstanceSuccessully(ResourceReqDetails vf,
+ ResourceReqDetails atomicResource) throws Exception, IOException {
+ createVFInstanceAndAtomicResourceInstanceSuccessully(vf, atomicResource, sdncDesignerDetails);
+ }
+
+ private void createVFInstanceAndAtomicResourceInstanceSuccessully(ResourceReqDetails vf,
+ ResourceReqDetails atomicResource, User user) throws Exception, IOException {
+ changeResourceLifecycleState(vf, vf.getCreatorUserId(), LifeCycleStatesEnum.CHECKIN);
+ changeResourceLifecycleState(atomicResource, atomicResource.getCreatorUserId(), LifeCycleStatesEnum.CHECKIN);
+ createVFInstanceAndAtomicResourceInstanceWithoutCheckin(vf, atomicResource, user);
+ }
+
+ @Test
+ public void createVFInstanceSuccessfullyTest() throws Exception {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void createVFAndAtomicInstanceTest() throws Exception {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsVL_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsVL_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ getComponentAndValidateRIs(serviceDetails_01, 4, 0);
+ }
+
+ @Test
+ public void deleteAtomicInstanceTest() throws Exception {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ // 1 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ // 2 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsVL_01,
+ sdncDesignerDetails);
+ // 3 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsVL_02,
+ sdncDesignerDetails);
+ // 4 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ // To delete
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // 3 rel
+ createVFInstResp = deleteAtomicInstanceForService(compInstId, serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkDeleteResponse(createVFInstResp);
+ getComponentAndValidateRIs(serviceDetails_01, 3, 0);
+ }
+
+ @Test
+ public void deleteVFInstanceTest() throws Exception {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ // 1 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ String compInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ // 2 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ createVFInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ // 3 rel
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ // 2 rel
+ createVFInstResp = deleteVFInstance(compInstId, serviceDetails_01, sdncDesignerDetails);
+ ResourceRestUtils.checkDeleteResponse(createVFInstResp);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void associateDissociateTwoVFs() throws Exception {
+
+ RestResponse createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String fromCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String toCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+
+ String capType = CAPABILITY_TYPE;
+ String reqName = REQUIREMENT_NAME;
+
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(fromCompInstId, toCompInstId, capType, reqName,
+ capList, reqList);
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<RequirementDefinition> list = capReqDef.getRequirements().get(capType);
+ AssertJUnit.assertEquals("Check requirement", null, list);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+
+ dissociateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ list = capReqDef.getRequirements().get(capType);
+ AssertJUnit.assertEquals("Check requirement", 1, list.size());
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ private RequirementCapabilityRelDef getReqCapRelation(String reqCompInstId, String capCompInstId, String capType,
+ String reqName, List<CapabilityDefinition> capList, List<RequirementDefinition> reqList) {
+ return ElementFactory.getReqCapRelation(reqCompInstId, capCompInstId, reqOwnerId, capOwnerId, capType, reqName,
+ capList, reqList);
+ }
+
+ @Test
+ public void createResourceInstanceByDifferentDesignerTest() throws Exception {
+ createVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2), 409);
+ createAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2), 409);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test
+ public void createResourceInstanceByDifferentDesignerTest_ServiceIsCheckedin() throws Exception {
+ User designer2 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2);
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ changeServiceLifecycleState(serviceDetails_01, designer2, LifeCycleStatesEnum.CHECKOUT);
+
+ createVFInstanceAndAtomicResourceInstanceSuccessully(resourceDetailsVF_01, resourceDetailsCP_01, designer2);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+
+ }
+
+ @Test
+ public void createResourceInstanceByTester() throws Exception {
+ createVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ ElementFactory.getDefaultUser(UserRoleEnum.TESTER), 409);
+ createAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), 409);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test
+ public void createResourceInstanceWithNotASDCUserTest() throws Exception {
+ sdncDesignerDetails.setUserId("ab0001");
+ createVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ sdncDesignerDetails, 409);
+ createAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, sdncDesignerDetails, 409);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test
+ public void createResourceInstanceWithEmptyUserIdTest() throws Exception {
+ sdncDesignerDetails.setUserId("");
+ createVFInstanceFail(ActionStatus.MISSING_INFORMATION, new ArrayList<String>(), resourceDetailsVF_01,
+ sdncDesignerDetails, 403);
+ createAtomicResourceInstanceFail(ActionStatus.MISSING_INFORMATION, new ArrayList<String>(),
+ resourceDetailsCP_01, sdncDesignerDetails, 403);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test
+ public void createResourceInstanceWithEmptyServiceUidTest() throws Exception {
+ serviceDetails_01.setUniqueId("");
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ assertEquals(404, createVFInstResp.getErrorCode().intValue());
+ RestResponse createAtomicInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ assertEquals(404, createAtomicInstResp.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createResourceInstanceWhileResourceNotExistTest() throws Exception {
+ String vfResourceUniqueId = "1234";
+ String atomicResourceUniqueId = "5678";
+
+ resourceDetailsVF_01.setUniqueId(vfResourceUniqueId);
+ resourceDetailsCP_01.setUniqueId(atomicResourceUniqueId);
+
+ createVFInstanceFailWithoutChangeState(ActionStatus.RESOURCE_NOT_FOUND,
+ new ArrayList<String>(Arrays.asList("")), resourceDetailsVF_01, sdncDesignerDetails, 404);
+ createAtomicResourceInstanceFailWithoutChangeState(ActionStatus.RESOURCE_NOT_FOUND,
+ new ArrayList<String>(Arrays.asList("")), resourceDetailsCP_01, sdncDesignerDetails, 404);
+ }
+
+ @Test
+ public void createResourceInstanceInServiceNotExistsTest() throws Exception {
+ serviceDetails_01.setUniqueId("1234");
+ createVFInstanceFail(ActionStatus.SERVICE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")),
+ resourceDetailsVF_01, sdncDesignerDetails, 404);
+ createAtomicResourceInstanceFail(ActionStatus.SERVICE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")),
+ resourceDetailsCP_01, sdncDesignerDetails, 404);
+ }
+
+ @Test
+ public void createResourceInstanceInCheckedinServiceTest() throws Exception {
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+
+ createVFInstanceFailWithoutChangeState(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsVF_01, sdncDesignerDetails, 409);
+ createAtomicResourceInstanceFailWithoutChangeState(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, sdncDesignerDetails, 409);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test(enabled = false)
+ public void createResourceInstance_ResourceInCheckoutStateTest() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ RestResponse createVFInstanceWithoutChangeStateResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ComponentInstanceRestUtils.checkCreateResponse(createVFInstanceWithoutChangeStateResp);
+ RestResponse createAtomicInstWithoutCheangeStateResp = createAtomicInstanceForService(serviceDetails_01,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ComponentInstanceRestUtils.checkCreateResponse(createAtomicInstWithoutCheangeStateResp);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void createResourceInstance_ResourceInCertificationRequestStateTest() throws Exception {
+ changeResourceLifecycleState(resourceDetailsVF_01, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ changeResourceLifecycleState(resourceDetailsCP_01, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+
+ createVFInstanceAndAtomicResourceInstanceSuccessully(resourceDetailsVF_01, resourceDetailsCP_01);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void createResourceInstance_startCertificationStateTest() throws Exception {
+ changeResourceLifecycleState(resourceDetailsVF_01, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ changeResourceLifecycleState(resourceDetailsCP_01, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+
+ changeResourceLifecycleState(resourceDetailsVF_01, sdncTesterDetails.getUserId(),
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ changeResourceLifecycleState(resourceDetailsCP_01, sdncTesterDetails.getUserId(),
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+
+ createVFInstanceAndAtomicResourceInstanceWithoutCheckin(resourceDetailsVF_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+
+ }
+
+ @Test
+ public void createResourceInstance_certifiedStateTest() throws Exception {
+ certifyResource(resourceDetailsVF_01);
+ certifyResource(resourceDetailsCP_01);
+
+ createVFInstanceAndAtomicResourceInstanceWithoutCheckin(resourceDetailsVF_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ }
+
+ @Test
+ public void createResourceInstance_OneHasDifferentOwner() throws Exception {
+ User designer2 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2);
+
+ ResourceReqDetails vfResource = new ResourceReqDetails(resourceDetailsVF_01, "0.1");
+ vfResource.setUniqueId(null);
+ vfResource.setName("newVF");
+ vfResource.setTags(new ArrayList<String>(Arrays.asList(vfResource.getName())));
+ createVF(vfResource, designer2);
+
+ RestResponse atomicInstanceForService = createCheckedinAtomicInstanceForService(serviceDetails_01,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(atomicInstanceForService);
+ createVFInstanceFailWithoutChangeState(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), vfResource,
+ designer2, 409);
+
+ getComponentAndValidateRIs(serviceDetails_01, 1, 0);
+ }
+
+ @Test
+ public void indexesOfVFInstancesTest() throws Exception {
+ String firstInstanceName = resourceDetailsVF_01.getName() + SPACE_STRING + "1";
+ String secondInstanceName = resourceDetailsVF_01.getName() + SPACE_STRING + "2";
+ String thirdInstanceName = resourceDetailsVF_01.getName() + SPACE_STRING + "3";
+
+ LifecycleRestUtils.changeResourceState(resourceDetailsVF_01, sdncDesignerDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+
+ RestResponse createFirstVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createSecondVFInstResp);
+ RestResponse createThirdVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createThirdVFInstResp);
+
+ Component service = getComponentAndValidateRIs(serviceDetails_01, 3, 0);
+ List<ComponentInstance> componentInstancesList = service.getComponentInstances();
+ for (ComponentInstance instance : componentInstancesList) {
+ String instanceName = instance.getName();
+ boolean isEqualToFirstInstanceName = instanceName.equals(firstInstanceName);
+ boolean isEqualToSecondInstanceName = instanceName.equals(secondInstanceName);
+ boolean isEqualToThirdInstanceName = instanceName.equals(thirdInstanceName);
+ assertTrue(isEqualToFirstInstanceName || isEqualToSecondInstanceName || isEqualToThirdInstanceName);
+ }
+ }
+
+ @Test
+ public void vfInstancesAmountInTwoServiceVersionsTest() throws Exception {
+ String oldServiceUniqueId = serviceDetails_01.getUniqueId();
+
+ createTwoCheckedinVFInstances();
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKOUT);
+
+ String newSerivceUniqueIdAfterChangeLifecycleState = serviceDetails_01.getUniqueId();
+ getComponentAndValidateRIsAfterChangeLifecycleState(oldServiceUniqueId, serviceDetails_01, 2, 0);
+
+ // Check old version
+ checkServiceOldVersionRIs(oldServiceUniqueId, newSerivceUniqueIdAfterChangeLifecycleState, 2, 0);
+
+ // Add one more resource instance to second version of service
+ LifecycleRestUtils.changeResourceState(resourceDetailsVL_01, sdncDesignerDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ RestResponse createAtomicResourceInstResp = createAtomicResourceInstanceToSecondServiceVersion(
+ newSerivceUniqueIdAfterChangeLifecycleState, resourceDetailsVL_01);
+ String atomicResourceUniqueId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstResp);
+ getComponentAndValidateRIsAfterAddingAtomicResourceInstance(oldServiceUniqueId, serviceDetails_01, 3, 0);
+
+ // Check that RIs are same as in the beginning - like in old version of
+ // service
+ deleteCompInstReqCapFromExpected(atomicResourceUniqueId);
+ checkServiceOldVersionRIs(oldServiceUniqueId, newSerivceUniqueIdAfterChangeLifecycleState, 2, 0);
+
+ }
+
+ private void createTwoCheckedinVFInstances() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createSecondVFInstResp);
+ }
+
+ private void getComponentAndValidateRIsAfterAddingAtomicResourceInstance(String oldComponentUniqueId,
+ ComponentReqDetails componentDetails, int numOfRIs, int numOfRelations) throws Exception {
+ getComponentAndValidateRIsAfterChangeLifecycleState(oldComponentUniqueId, componentDetails, numOfRIs,
+ numOfRelations);
+
+ }
+
+ private void checkServiceOldVersionRIs(String oldUniqueId, String newUniqueId, int numOfRIs, int numOfRelations)
+ throws IOException, Exception {
+ serviceDetails_01.setUniqueId(oldUniqueId);
+ getComponentAndValidateRIsAfterChangeLifecycleState(newUniqueId, serviceDetails_01, numOfRIs, numOfRelations);
+ }
+
+ private RestResponse createAtomicResourceInstanceToSecondServiceVersion(String secondServiceUniqueId,
+ ResourceReqDetails resourceToAdd) throws Exception {
+ serviceDetails_01.setUniqueId(secondServiceUniqueId);
+ RestResponse createAtomicResourceInstResp = createAtomicInstanceForService(serviceDetails_01, resourceToAdd,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstResp);
+ return createAtomicResourceInstResp;
+ }
+
+ @Test
+ public void createResourceInstanceToUnsupportedComponentTest() throws Exception {
+ String unsupportedType = "unsupported";
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentResourceInstance(resourceDetailsCP_01);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, sdncDesignerDetails, serviceDetails_01.getUniqueId(), unsupportedType);
+ checkErrorMessage(ActionStatus.UNSUPPORTED_ERROR, new ArrayList<String>(Arrays.asList(unsupportedType)), 400,
+ createResourceInstanceResponse);
+ }
+
+ @Test
+ public void deleteResourceInstanceByDifferentDesignerTest() throws Exception {
+
+ createVFInstanceAndAtomicResourceInstanceSuccessully(resourceDetailsVF_01, resourceDetailsCP_01);
+
+ deleteVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2), 409);
+ deleteAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2), 409);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteResourceInstanceByDifferentDesignerTest_ServiceIsCheckedin() throws Exception {
+
+ String oldServiceUniqueId = serviceDetails_01.getUniqueId();
+
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ RestResponse createAtomicResourceInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstResp);
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ changeServiceLifecycleState(serviceDetails_01, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2),
+ LifeCycleStatesEnum.CHECKOUT);
+ String newServiceUniqueId = serviceDetails_01.getUniqueId();
+
+ String oldVFInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ String newVFInstanceUniqueId = oldVFInstanceUniqueId.replaceAll(oldServiceUniqueId,
+ serviceDetails_01.getUniqueId());
+ String oldAtomicResourceInstanceUniqueId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstResp);
+ String newAtomicResourceInstanceUniqueId = oldAtomicResourceInstanceUniqueId.replaceAll(oldServiceUniqueId,
+ serviceDetails_01.getUniqueId());
+
+ deleteVFInstanceAndAtomicResourceInstanceSuccessfully(newVFInstanceUniqueId, newAtomicResourceInstanceUniqueId,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2));
+
+ serviceDetails_01.setUniqueId(oldServiceUniqueId);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+
+ serviceDetails_01.setUniqueId(newServiceUniqueId);
+ updateExpectedReqCapAfterChangeLifecycleState(oldServiceUniqueId, serviceDetails_01.getUniqueId());
+ deleteCompInstReqCapFromExpected(newVFInstanceUniqueId);
+ deleteCompInstReqCapFromExpected(newAtomicResourceInstanceUniqueId);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ private void deleteVFInstanceAndAtomicResourceInstanceSuccessfully(String vfInstanceUniqueId,
+ String atomicResourceInstanceUniqueId) throws IOException, Exception {
+ deleteVFInstanceAndAtomicResourceInstanceSuccessfully(vfInstanceUniqueId, atomicResourceInstanceUniqueId,
+ sdncDesignerDetails);
+ }
+
+ private void deleteVFInstanceAndAtomicResourceInstanceSuccessfully(String vfInstanceUniqueId,
+ String atomicResourceInstanceUniqueId, User user) throws IOException, Exception {
+ RestResponse deleteVFInstResp = deleteVFInstance(vfInstanceUniqueId, serviceDetails_01, user);
+ ResourceRestUtils.checkDeleteResponse(deleteVFInstResp);
+ RestResponse deleteAtomicResourceInsResp = deleteAtomicInstanceForService(atomicResourceInstanceUniqueId,
+ serviceDetails_01, user);
+ ResourceRestUtils.checkDeleteResponse(deleteAtomicResourceInsResp);
+ }
+
+ @Test
+ public void deleteResourceInstanceByTesterUserTest() throws Exception {
+ createVFInstanceAndAtomicResourceInstanceSuccessully(resourceDetailsVF_01, resourceDetailsCP_01);
+ deleteVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ ElementFactory.getDefaultUser(UserRoleEnum.TESTER), 409);
+ deleteAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, ElementFactory.getDefaultUser(UserRoleEnum.TESTER), 409);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteResourceInstanceByNotASDCUserTest() throws Exception {
+ createVFInstanceAndAtomicResourceInstanceSuccessully(resourceDetailsVF_01, resourceDetailsCP_01);
+ User notASDCUser = new User();
+ notASDCUser.setUserId("ab0001");
+ deleteVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ notASDCUser, 409);
+ deleteAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, notASDCUser, 409);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteResourceInstanceFromCheckedinServiceTest() throws Exception {
+ createVFInstanceAndAtomicResourceInstanceSuccessully(resourceDetailsVF_01, resourceDetailsCP_01);
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ deleteVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ sdncDesignerDetails, 409);
+ deleteAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, sdncDesignerDetails, 409);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void deleteResourceInstanceWhileResourceCertifiedStateTest() throws Exception {
+ certifyResource(resourceDetailsVF_01);
+ certifyResource(resourceDetailsCP_01);
+
+ RestResponse createVFInstance = createVFInstance(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstance);
+ String vfInstUniqueId = ResponseParser.getUniqueIdFromResponse(createVFInstance);
+ RestResponse atomicInstanceForService = createAtomicInstanceForService(serviceDetails_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(atomicInstanceForService);
+ String atomicInstUniqueId = ResponseParser.getUniqueIdFromResponse(atomicInstanceForService);
+
+ deleteVFInstanceAndAtomicResourceInstanceSuccessfully(vfInstUniqueId, atomicInstUniqueId);
+
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ // fail - bug DE191849
+ @Test
+ public void deleteNotFoundResourceInstanceTest() throws Exception, Throwable {
+
+ resourceDetailsVF_01.setUniqueId("1234");
+ resourceDetailsCP_01.setUniqueId("5678");
+
+ deleteVFInstanceFail(ActionStatus.RESOURCE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")),
+ resourceDetailsVF_01, sdncDesignerDetails, 404);
+ deleteAtomicResourceInstanceFail(ActionStatus.RESOURCE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")),
+ resourceDetailsCP_01, sdncDesignerDetails, 404);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+
+ // {"requestError":{"serviceException":{"messageId":"SVC4503","text":"Error:
+ // Requested '%1' service was not found.","variables":["1234"]}}}>
+ }
+
+ @Test
+ public void deleteResourceInstanceFromServiceNotFoundTest() throws Exception, Throwable {
+ serviceDetails_01.setUniqueId("1234");
+ deleteVFInstanceFail(ActionStatus.SERVICE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")),
+ resourceDetailsVF_01, sdncDesignerDetails, 404);
+ deleteAtomicResourceInstanceFail(ActionStatus.SERVICE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")),
+ resourceDetailsCP_01, sdncDesignerDetails, 404);
+ }
+
+ @Test
+ public void deleteResourceInstanceFromUnsupportedTypeTest() throws Exception {
+ String unsupportedType = "unsupportedType";
+ RestResponse deleteVFInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(sdncDesignerDetails,
+ serviceDetails_01.getUniqueId(), resourceDetailsVF_01.getUniqueId(), unsupportedType);
+ checkErrorMessage(ActionStatus.UNSUPPORTED_ERROR, new ArrayList<String>(Arrays.asList(unsupportedType)), 400,
+ deleteVFInstanceResponse);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test
+ public void deleteResourceInstanceWithEmptyServiceUidTest() throws Exception, Throwable {
+ serviceDetails_01.setUniqueId("");
+ RestResponse deleteVFInstResp = deleteVFInstance(resourceDetailsVF_01.getUniqueId(), serviceDetails_01,
+ sdncDesignerDetails);
+ assertEquals(404, deleteVFInstResp.getErrorCode().intValue());
+ }
+
+ @Test
+ public void deleteResourceInstanceWithEmptyResourceInstanceUidTest() throws Exception, Throwable {
+ RestResponse deleteVFInstResp = deleteVFInstance("", serviceDetails_01, sdncDesignerDetails);
+ assertEquals(405, deleteVFInstResp.getErrorCode().intValue());
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ @Test
+ public void deleteResourceInstanceWithEmptyUserIdTest() throws Exception {
+ sdncDesignerDetails.setUserId("");
+ deleteVFInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(), resourceDetailsVF_01,
+ sdncDesignerDetails, 409);
+ deleteAtomicResourceInstanceFail(ActionStatus.RESTRICTED_OPERATION, new ArrayList<String>(),
+ resourceDetailsCP_01, sdncDesignerDetails, 409);
+ getComponentAndValidateRIs(serviceDetails_01, 0, 0);
+ }
+
+ // fail - bug DE188994
+ @Test
+ public void associateResourceInstanceToResourceInstanceNotFoundTest() throws Exception, Throwable {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ String capCompInstId = "1234";
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ List<CapabilityDefinition> capListBeforeAssociate = new ArrayList<CapabilityDefinition>();
+ CapabilityDefinition cap = new CapabilityDefinition();
+ cap.setUniqueId(capCompInstId);
+ capListBeforeAssociate.add(cap);
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ assocaiteInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESOURCE_INSTANCE_NOT_FOUND, 404,
+ new ArrayList<String>(Arrays.asList(capCompInstId)));
+
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capabilitiesAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> requirementsAfterAssoicate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ // AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate,
+ // requirementsAfterAssoicate);
+ // AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate,
+ // capabilitiesAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 1, 0);
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "VF100 1",
+ // "9ae76786-2a9c-4409-95cb-db32885ed07f.eece8aaf-eb9f-4aff-b9a5-a11ca11de9e5.vf1001",
+ // "host"
+ // ]
+ }
+
+ // this case is not relevant any more, it is tested as part of occurrences
+ // story
+ @Test(enabled = false)
+ public void associateOnceAgainExistingRelationTest() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ //////////////////////////////////////////////
+ // NO ERROR - RELATION ALREADY EXIST
+ // assocaiteInstancesFail(requirementDef, sdncDesignerDetails,
+ // ActionStatus.RESOURCE_INSTANCE_NOT_FOUND, 404, new
+ // ArrayList<String>(Arrays.asList(capCompInstId)));
+ //////////////////////////////////////////////
+
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListAfterAssociate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ // AssertJUnit.assertEquals("Check requirement", null,
+ // reqListAfterAssociate);
+ // AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate,
+ // capListAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+
+ // "messageId": "SVC4119",
+ // "text": "Error: No relation found between resource instances
+ // \u0027%1\u0027 and \u0027%2\u0027 for requirement \u0027%3\u0027.",
+ // "variables": [
+ // "VF100 1",
+ // "VF_admin 2",
+ // "host"
+
+ }
+
+ @Test
+ public void associateInstancesInMissingServiceTest() throws Exception {
+ serviceDetails_01.setUniqueId("1234");
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ assocaiteInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.SERVICE_NOT_FOUND, 404,
+ new ArrayList<String>(Arrays.asList("")));
+ }
+
+ @Test
+ public void associateAfterDeletingResourceTest() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ ResourceRestUtils.deleteResource(resourceDetailsVF_01.getUniqueId(), sdncDesignerDetails.getUserId());
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+
+ // for (CapabilityDefinition capabilityDefinition :
+ // capListBeforeAssociate) {
+ // if (capabilityDefinition.getType().equals(CAPABILITY_TYPE)){
+ // capabilityDefinition.setMinOccurrences("0");
+ // }
+ // }
+ //
+ // List<RequirementDefinition> reqListAfterAssociate =
+ // capReqDefAfterAssociate.getRequirements().get(CAPABILITY_TYPE);
+ //
+ // AssertJUnit.assertEquals("Check requirement", null,
+ // reqListAfterAssociate);
+ //
+ // AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate,
+ // capListAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+ }
+
+ @Test
+ public void associateInstancesInCheckedinServiceTest() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+
+ assocaiteInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESTRICTED_OPERATION, 409,
+ new ArrayList<String>());
+
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capabilitiesAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> requirementsAfterAssoicate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate, requirementsAfterAssoicate);
+ AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate, capabilitiesAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ // fail - bug DE188994
+ @Test
+ public void associateAfterCheckoutAllInstancesTest() throws Exception {
+ String firstVFUniqueId = resourceDetailsVF_01.getUniqueId();
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ String secondVFUniqueId = resourceDetailsVF_02.getUniqueId();
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ changeResourceLifecycleState(resourceDetailsVF_01, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CHECKOUT);
+ changeResourceLifecycleState(resourceDetailsVF_02, sdncDesignerDetails.getUserId(),
+ LifeCycleStatesEnum.CHECKOUT);
+
+ requirementDef.setFromNode(
+ requirementDef.getFromNode().replaceAll(firstVFUniqueId, resourceDetailsVF_01.getUniqueId()));
+ requirementDef
+ .setToNode(requirementDef.getToNode().replaceAll(secondVFUniqueId, resourceDetailsVF_02.getUniqueId()));
+
+ assocaiteInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESTRICTED_OPERATION, 409,
+ new ArrayList<String>());
+
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capabilitiesAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> requirementsAfterAssoicate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ // AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate,
+ // requirementsAfterAssoicate);
+ // AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate,
+ // capabilitiesAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+
+ // "messageId": "SVC4116",
+ // "text": "Error: Invalid Content.",
+ // "variables": [
+ // "e9dcea15-ce27-4381-a554-4278973cefb1.d0b3affd-cf92-4626-adfe-961b44103924.vf1001",
+ // "e9dcea15-ce27-4381-a554-4278973cefb1.d0b3affd-cf92-4626-adfe-961b44103924.vf1001",
+ // "host"
+ // ]
+
+ }
+
+ @Test
+ public void associateInstancesByDifferentUsersTest() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ assocaiteInstancesFail(requirementDef, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2),
+ ActionStatus.RESTRICTED_OPERATION, 409, new ArrayList<String>());
+ assocaiteInstancesFail(requirementDef, ElementFactory.getDefaultUser(UserRoleEnum.TESTER),
+ ActionStatus.RESTRICTED_OPERATION, 409, new ArrayList<String>());
+ assocaiteInstancesFail(requirementDef, ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR),
+ ActionStatus.RESTRICTED_OPERATION, 409, new ArrayList<String>());
+ assocaiteInstancesFail(requirementDef, ElementFactory.getDefaultUser(UserRoleEnum.OPS),
+ ActionStatus.RESTRICTED_OPERATION, 409, new ArrayList<String>());
+ assocaiteInstancesFail(requirementDef, ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1),
+ ActionStatus.RESTRICTED_OPERATION, 409, new ArrayList<String>());
+
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capabilitiesAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> requirementsAfterAssoicate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate, requirementsAfterAssoicate);
+ AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate, capabilitiesAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ private void assocaiteInstancesFail(RequirementCapabilityRelDef requirementDef, User user,
+ ActionStatus actionStatus, int errorCode, List<String> variables) throws IOException, Exception {
+ RestResponse associateInstancesResp = ComponentInstanceRestUtils.associateInstances(requirementDef, user,
+ serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ checkErrorMessage(actionStatus, variables, errorCode, associateInstancesResp);
+ }
+
+ private void dissoicateInstancesFail(RequirementCapabilityRelDef requirementDef, User user,
+ ActionStatus actionStatus, int errorCode, List<String> variables) throws IOException, Exception {
+ RestResponse dissoicateInstancesResp = ComponentInstanceRestUtils.dissociateInstances(requirementDef, user,
+ serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ checkErrorMessage(actionStatus, variables, errorCode, dissoicateInstancesResp);
+ }
+
+ @Test
+ public void associateWithMissingServiceUidTest() throws Exception {
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ serviceDetails_01.setUniqueId("");
+ RestResponse associateInstancesResp = ComponentInstanceRestUtils.associateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals(404, associateInstancesResp.getErrorCode().intValue());
+ }
+
+ // fail - bug DE191824
+ @Test
+ public void associateNotCompitableReqCapTest() throws Exception {
+ RestResponse createFirstAtomicResourceInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01,
+ resourceDetailsCP_01, sdncDesignerDetails);
+ String reqCompInstName = ResponseParser.getNameFromResponse(createFirstAtomicResourceInstResp);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstAtomicResourceInstResp);
+ RestResponse createSecondAtomicResourceInstResp = createCheckedinAtomicInstanceForService(serviceDetails_01,
+ resourceDetailsVL_02, sdncDesignerDetails);
+ String capCompInstName = ResponseParser.getNameFromResponse(createSecondAtomicResourceInstResp);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondAtomicResourceInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ List<String> variables = new ArrayList<String>();
+ variables.add(reqCompInstName);
+ variables.add(capCompInstName);
+ variables.add(REQUIREMENT_NAME);
+
+ assocaiteInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESOURCE_INSTANCE_MATCH_NOT_FOUND, 404,
+ variables);
+
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capabilitiesAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> requirementsAfterAssoicate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ // AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate,
+ // requirementsAfterAssoicate);
+ // AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate,
+ // capabilitiesAfterAssociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+
+ // {"requestError":{"serviceException":{"messageId":"SVC4119","text":"Error:
+ // No relation found between resource instances '%1' and '%2' for
+ // requirement '%3'.","variables":["CP100 1","VL200 2","host"]}}}>
+ }
+
+ @Test
+ public void associateInstancesInTwoServiceVersionsTest() throws Exception {
+ String oldServiceUniqueId = serviceDetails_01.getUniqueId();
+ RestResponse createFirstVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKOUT);
+ String secondServiceUniqueId = serviceDetails_01.getUniqueId();
+
+ serviceDetails_01.setUniqueId(oldServiceUniqueId);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+
+ updateCapabilitiesOwnerId(oldServiceUniqueId, capListBeforeAssociate, secondServiceUniqueId);
+ updateExpectedReqCapAfterChangeLifecycleState(oldServiceUniqueId, secondServiceUniqueId);
+ CapReqDef capReqDefAfterAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListAfterAssociate = capReqDefAfterAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListAfterAssociate = capReqDefAfterAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ // AssertJUnit.assertEquals("Check requirement", null,
+ // reqListAfterAssociate);
+ // AssertJUnit.assertEquals("Check capabilities",
+ // capListBeforeAssociate, capListAfterAssociate);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+
+ RestResponse createThirdVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqSecondCompInstId = ResponseParser.getUniqueIdFromResponse(createThirdVFInstResp);
+
+ CapReqDef capReqDefBeforeSeconderyAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeSeconderyAssociate = capReqDefBeforeSeconderyAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeSeconderyAssociate = capReqDefBeforeSeconderyAssociate
+ .getRequirements().get(CAPABILITY_TYPE);
+
+ capCompInstId = capCompInstId.replaceAll(oldServiceUniqueId, secondServiceUniqueId);
+ RequirementCapabilityRelDef secondRequirementDef = getReqCapRelation(reqSecondCompInstId, capCompInstId,
+ CAPABILITY_TYPE, REQUIREMENT_NAME, capListBeforeSeconderyAssociate, reqListBeforeSeconderyAssociate);
+ associateComponentInstancesForService(secondRequirementDef, serviceDetails_01, sdncDesignerDetails);
+
+ CapReqDef capReqDefAfterSeconderyAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListAfterSeconderyAssociate = capReqDefAfterSeconderyAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListAfterSeconderyAssociate = capReqDefAfterSeconderyAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ // AssertJUnit.assertEquals("Check requirement", null,
+ // reqListAfterSeconderyAssociate);
+ // AssertJUnit.assertEquals("Check capabilities",
+ // capListBeforeAssociate, capListAfterSeconderyAssociate);
+ getComponentAndValidateRIs(serviceDetails_01, 3, 2);
+ }
+
+ private void updateCapabilitiesOwnerId(String oldUniqueId, List<CapabilityDefinition> capList, String newUniqueId) {
+ serviceDetails_01.setUniqueId(newUniqueId);
+ for (CapabilityDefinition cap : capList) {
+ String oldOwnerId = cap.getOwnerId();
+ String newOwnerId = oldOwnerId.replaceAll(oldUniqueId, newUniqueId);
+ cap.setOwnerId(newOwnerId);
+ }
+ }
+
+ @Test
+ public void dissociateRelationNotFoundTest() throws Exception {
+ createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails);
+ String reqCompInstId = "1234";
+ createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ String capCompInstId = "4567";
+
+ CapReqDef capReqDef = ComponentRestUtils.getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capList, reqList);
+
+ List<String> variables = new ArrayList<String>();
+ variables.add(reqCompInstId);
+ variables.add(capCompInstId);
+ variables.add(REQUIREMENT_NAME);
+ dissoicateInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND,
+ 404, variables);
+
+ CapReqDef capReqDefAfterDissociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListAfterDissociate = capReqDefAfterDissociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListAfterDissociate = capReqDefAfterDissociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ AssertJUnit.assertEquals("Check requirement", 1, reqListAfterDissociate.size());
+ AssertJUnit.assertEquals("Check requirement", reqList, reqListAfterDissociate);
+ AssertJUnit.assertEquals("Check capabilities", capList, capListAfterDissociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+
+ }
+
+ @Test
+ public void dissociateRelationInServiceNotFoundTest() throws Exception {
+ String uniqueId = "1234";
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ serviceDetails_01.setUniqueId(uniqueId);
+ dissoicateInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.SERVICE_NOT_FOUND, 404,
+ new ArrayList<String>(Arrays.asList("")));
+
+ }
+
+ @Test
+ public void dissoicateRelationWhileInstanceNotFound() throws Exception {
+ String capUniqueId = "1234";
+
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ String capCompInstId = capUniqueId;
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ List<String> variables = new ArrayList<String>();
+ variables.add(reqCompInstId);
+ variables.add(capCompInstId);
+ variables.add(REQUIREMENT_NAME);
+ dissoicateInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND,
+ 404, variables);
+
+ CapReqDef capReqDefAfterDissociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListAfterDissociate = capReqDefAfterDissociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListAfterDissociate = capReqDefAfterDissociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+ AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate, reqListAfterDissociate);
+ AssertJUnit.assertEquals("Check capabilities", capListBeforeAssociate, capListAfterDissociate);
+
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void dissociateWhileServiceCheckedinTest() throws Exception {
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ dissoicateInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESTRICTED_OPERATION, 409,
+ new ArrayList<String>());
+ }
+
+ @Test
+ public void dissoicateWithEmptyUserIdHeaderTest() throws Exception {
+ sdncDesignerDetails.setUserId("");
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ dissoicateInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESTRICTED_OPERATION, 409,
+ new ArrayList<String>());
+ }
+
+ @Test
+ public void dissociateWithMissingUidOfServiceTest() throws Exception {
+ serviceDetails_01.setUniqueId("");
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ RestResponse dissociateResp = ComponentInstanceRestUtils.dissociateInstances(requirementDef,
+ sdncDesignerDetails, serviceDetails_01.getUniqueId(), ComponentTypeEnum.SERVICE);
+ assertEquals(404, dissociateResp.getErrorCode().intValue());
+ }
+
+ @Test
+ public void relationDeletedAfterDeletingResourceInstanceTest() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+
+ RestResponse deleteVFInstance = deleteVFInstance(reqCompInstId, serviceDetails_01, sdncDesignerDetails);
+ ComponentInstanceRestUtils.checkDeleteResponse(deleteVFInstance);
+ getComponentAndValidateRIs(serviceDetails_01, 1, 0);
+ }
+
+ @Test
+ public void relationNotFoundInSecondVersionAfterDissociateTest() throws Exception {
+ String oldContainerUniqueIdToReplace = serviceDetails_01.getUniqueId();
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ dissociateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKOUT);
+
+ updateExpectedReqCapAfterChangeLifecycleState(oldContainerUniqueIdToReplace, serviceDetails_01.getUniqueId());
+ getComponentAndValidateRIs(serviceDetails_01, 2, 0);
+ }
+
+ @Test
+ public void dissociateOnceAgainTest() throws Exception {
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ String reqCompInsName = ResponseParser
+ .convertComponentInstanceResponseToJavaObject(createFirstVFInstResp.getResponse()).getName();
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+ String capCompInstName = ResponseParser
+ .convertComponentInstanceResponseToJavaObject(createSecondVFInstResp.getResponse()).getName();
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, CAPABILITY_TYPE,
+ REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ dissociateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+
+ List<String> variables = new ArrayList<String>();
+ variables.add(reqCompInsName);
+ variables.add(capCompInstName);
+ variables.add(REQUIREMENT_NAME);
+
+ dissoicateInstancesFail(requirementDef, sdncDesignerDetails, ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND,
+ 404, variables);
+ }
+
+ // fail - bug : DE191707
+ @Test
+ public void associateTwoRelations_CheckinCheckout_DissoicateOneRelationInSecondVersion() throws Exception {
+ String oldContainerUniqueIdToReplace = serviceDetails_01.getUniqueId();
+ RestResponse createFirstVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createFirstVFInstResp);
+ RestResponse createSecondVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createSecondVFInstResp);
+ RestResponse createThirdVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ String secondReqCompInstId = ResponseParser.getUniqueIdFromResponse(createThirdVFInstResp);
+
+ CapReqDef capReqDefBeforeAssociate = ComponentRestUtils
+ .getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails, serviceDetails_01);
+ List<CapabilityDefinition> capListBeforeAssociate = capReqDefBeforeAssociate.getCapabilities()
+ .get(CAPABILITY_TYPE);
+ List<RequirementDefinition> reqListBeforeAssociate = capReqDefBeforeAssociate.getRequirements()
+ .get(CAPABILITY_TYPE);
+
+ RequirementCapabilityRelDef requirementDefFirstRelation = getReqCapRelation(reqCompInstId, capCompInstId,
+ CAPABILITY_TYPE, REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+ RequirementCapabilityRelDef requirementDefSecondRelation = getReqCapRelation(secondReqCompInstId, capCompInstId,
+ CAPABILITY_TYPE, REQUIREMENT_NAME, capListBeforeAssociate, reqListBeforeAssociate);
+
+ associateComponentInstancesForService(requirementDefFirstRelation, serviceDetails_01, sdncDesignerDetails);
+ associateComponentInstancesForService(requirementDefSecondRelation, serviceDetails_01, sdncDesignerDetails);
+ getComponentAndValidateRIs(serviceDetails_01, 3, 2);
+
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKOUT);
+ String newContainerUniqueId = serviceDetails_01.getUniqueId();
+
+ // check if dissoicate of old relation is possibile
+ // dissoicateInstancesFail(requirementDefFirstRelation,
+ // sdncDesignerDetails, actionStatus, errorCode, variables);
+ getComponentAndValidateRIs(serviceDetails_01, 3, 2);
+
+ requirementDefFirstRelation
+ .setFromNode(reqCompInstId.replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+ requirementDefFirstRelation
+ .setToNode(reqCompInstId.replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+
+ dissociateComponentInstancesForService(requirementDefFirstRelation, serviceDetails_01, sdncDesignerDetails);
+
+ // updateCapabilitiesOwnerId(oldContainerUniqueIdToReplace,
+ // capListBeforeAssociate, newContainerUniqueId);
+ // CapReqDef capReqDefAfterAssociate =
+ // ComponentRestUtils.getAndParseComponentRequirmentsCapabilities(sdncDesignerDetails,
+ // serviceDetails_01);
+ // List<CapabilityDefinition> capListAfterAssociate =
+ // capReqDefAfterAssociate.getCapabilities().get(CAPABILITY_TYPE);
+ // List<RequirementDefinition> reqListAfterAssociate =
+ // capReqDefAfterAssociate.getRequirements().get(CAPABILITY_TYPE);
+ // AssertJUnit.assertEquals("Check requirement", reqListBeforeAssociate,
+ // reqListAfterAssociate);
+ // AssertJUnit.assertEquals("Check requirement", capListBeforeAssociate,
+ // capListAfterAssociate);
+ updateExpectedReqCapAfterChangeLifecycleState(oldContainerUniqueIdToReplace, serviceDetails_01.getUniqueId());
+ getComponentAndValidateRIs(serviceDetails_01, 3, 1);
+ }
+
+ @Test
+ public void createResourceInstancesAndUpdatedServiceMetadataTest() throws Exception, Exception {
+ serviceDetails_02.setUniqueId(serviceDetails_01.getUniqueId());
+ createTwoCheckedinVFInstances();
+ LifecycleRestUtils.changeResourceState(resourceDetailsCP_01, sdncDesignerDetails, "0.1",
+ LifeCycleStatesEnum.CHECKIN);
+ createVFInstanceAndAtomicResourceInstanceWithoutCheckin(resourceDetailsVF_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ RestResponse updateServiceResp = ServiceRestUtils.updateService(serviceDetails_02, sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(updateServiceResp);
+ getComponentAndValidateRIs(serviceDetails_01, 4, 0);
+ }
+
+ @Test(enabled = false)
+ public void forAcceptanceUserStory() throws Exception {
+ RestResponse createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String reqCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+ createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_02, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ String capCompInstId = ResponseParser.getUniqueIdFromResponse(createVFInstResp);
+
+ String capType = CAPABILITY_TYPE;
+ String reqName = REQUIREMENT_NAME;
+
+ RestResponse getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ ResourceRestUtils.checkSuccess(getResourceResponse);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<CapabilityDefinition> capList = capReqDef.getCapabilities().get(capType);
+ List<RequirementDefinition> reqList = capReqDef.getRequirements().get(capType);
+
+ RequirementCapabilityRelDef requirementDef = getReqCapRelation(reqCompInstId, capCompInstId, capType, reqName,
+ capList, reqList);
+
+ associateComponentInstancesForService(requirementDef, serviceDetails_01, sdncDesignerDetails);
+ getResourceResponse = ComponentRestUtils.getComponentRequirmentsCapabilities(sdncDesignerDetails,
+ serviceDetails_01);
+ capReqDef = ResponseParser.parseToObject(getResourceResponse.getResponse(), CapReqDef.class);
+ List<RequirementDefinition> list = capReqDef.getRequirements().get(capType);
+ AssertJUnit.assertEquals("Check requirement", null, list);
+
+ serviceDetails_02.setUniqueId(serviceDetails_01.getUniqueId());
+ RestResponse updateServiceResp = ServiceRestUtils.updateService(serviceDetails_02, sdncDesignerDetails);
+ ServiceRestUtils.checkSuccess(updateServiceResp);
+ changeServiceLifecycleState(serviceDetails_01, sdncDesignerDetails, LifeCycleStatesEnum.CHECKIN);
+ getComponentAndValidateRIs(serviceDetails_01, 2, 1);
+ }
+
+ @Test
+ public void testUnsatisfiedCpReqInService() throws Exception {
+
+ // Certify all the needed atomic resources
+ RestResponse response = LifecycleRestUtils.certifyResource(resourceDetailsCP_01);
+ ResourceRestUtils.checkSuccess(response);
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails,
+ resourceDetailsVF_02.getUniqueId());
+ ResourceRestUtils.checkSuccess(response);
+ response = LifecycleRestUtils.certifyResource(resourceDetailsVF_02);
+ ResourceRestUtils.checkSuccess(response);
+ capOwnerId = getUniqueIdOfFirstInstanceFromResponse(response);
+
+ RestResponse createAtomicResourceInstance = createVFInstance(serviceDetails_01, resourceDetailsVF_02,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String vfCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ createAtomicResourceInstance = createAtomicInstanceForService(serviceDetails_01, resourceDetailsCP_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createAtomicResourceInstance);
+ String compInstName = ResponseParser.getNameFromResponse(createAtomicResourceInstance);
+ String cpCompInstId = ResponseParser.getUniqueIdFromResponse(createAtomicResourceInstance);
+
+ RestResponse submitForTesting = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ String[] variables = new String[] { serviceDetails_01.getName(), "service", "CP (Connection Point)",
+ compInstName, "requirement", "tosca.capabilities.network.Bindable", "fulfilled" };
+ BaseValidationUtils.checkErrorResponse(submitForTesting,
+ ActionStatus.REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION, variables);
+
+ fulfillCpRequirement(serviceDetails_01, cpCompInstId, vfCompInstId, capOwnerId, sdncDesignerDetails,
+ ComponentTypeEnum.SERVICE);
+
+ submitForTesting = LifecycleRestUtils.changeServiceState(serviceDetails_01, sdncDesignerDetails,
+ LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ BaseValidationUtils.checkSuccess(submitForTesting);
+ }
+
+ @Test
+ public void getVFInstanceSuccessfullyTest() throws Exception {
+ RestResponse createVFInstResp = createCheckedinVFInstance(serviceDetails_01, resourceDetailsVF_01,
+ sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ System.out.println("instance successfuly created");
+ RestResponse getInstancesResponce = ComponentInstanceRestUtils.getComponentInstances(ComponentTypeEnum.SERVICE,
+ serviceDetails_01.getUniqueId(), sdncDesignerDetails);
+
+ for (int i = 0; i < 1500; i++) {
+ createVFInstResp = createVFInstance(serviceDetails_01, resourceDetailsVF_01, sdncDesignerDetails);
+ ResourceRestUtils.checkCreateResponse(createVFInstResp);
+ System.out.println("instance " + i + "successfuly created");
+ }
+
+ getInstancesResponce = ComponentInstanceRestUtils.getComponentInstances(ComponentTypeEnum.SERVICE,
+ serviceDetails_01.getUniqueId(), sdncDesignerDetails);
+
+ BaseValidationUtils.checkSuccess(getInstancesResponce);
+
+ }
+
+ private String getUniqueIdOfFirstInstanceFromResponse(RestResponse response) {
+ try {
+ JSONArray value = ResponseParser.getListFromJson(response, "componentInstances");
+ return ResponseParser.getValueFromJsonResponse(value.get(0).toString(), "uniqueId");
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/UpdateServiceMetadataTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/UpdateServiceMetadataTest.java
new file mode 100644
index 0000000000..2f5b47452e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/service/UpdateServiceMetadataTest.java
@@ -0,0 +1,1984 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.codehaus.jettison.json.JSONException;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ServiceValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class UpdateServiceMetadataTest extends ComponentBaseTest {
+
+ protected ArrayList<String> listForMessage = new ArrayList<String>();
+
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected User sdncDesignerDetails;
+ protected User sdncDesignerDetails2;
+ protected User sdncAdminDetails;
+ protected User sdncGovernorDeatails;
+ protected User sdncTesterDetails;
+ protected User sdncOpsDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+ protected Component resourceDetailsVFCcomp;
+ protected Component serviceDetailsCompp;
+
+ @Rule
+ public static TestName name = new TestName();
+ protected ServiceReqDetails updatedServiceDetails;
+
+ public UpdateServiceMetadataTest() {
+ super(name, UpdateServiceMetadataTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncDesignerDetails2 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER2);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncAdminDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN4);
+ sdncGovernorDeatails = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ sdncTesterDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncOpsDetails = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+ resourceDetailsVFCcomp = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVFCcomp, UserRoleEnum.DESIGNER, true, true);
+
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true);
+ Service serviceServ = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCcomp, serviceServ, UserRoleEnum.DESIGNER, true);
+
+ serviceDetails = new ServiceReqDetails(serviceServ);
+ updatedServiceDetails = updatedServiceDetails(serviceDetails);
+
+ }
+
+ protected void certifyService(ServiceReqDetails serviceDetails, String version) throws Exception {
+ LifecycleRestUtils.certifyService(serviceDetails);
+ }
+
+ protected ServiceReqDetails updatedServiceDetails(ServiceReqDetails service) {
+ ServiceReqDetails updatedServiceDetails = new ServiceReqDetails(service);
+
+ updatedServiceDetails.setDescription("updatedDescription");
+ updatedServiceDetails.setName(service.getName());
+ updatedServiceDetails.setProjectCode("987654654");
+ updatedServiceDetails.setIcon("icon-service-red3");
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("updateTag", updatedServiceDetails.getName())));
+ updatedServiceDetails.removeAllCategories();
+ updatedServiceDetails.setCategories(null);
+ updatedServiceDetails.addCategory(ServiceCategoriesEnum.VOIP.getValue());
+ updatedServiceDetails.setContactId("xy0123");
+
+ return updatedServiceDetails;
+ }
+
+ protected void addMandatoryArtifactsToService() throws Exception {
+ // TODO Andrey US575052
+ // ServiceRestUtils.addServiceMandatoryArtifacts(sdncDesignerDetails,
+ // createServiceResponse);
+ }
+
+ protected void getServiceAndValidate(ServiceReqDetails excpectedService, User creator, User updater, LifecycleStateEnum lifeCycleState) throws Exception {
+ RestResponse getServiceResponse = ServiceRestUtils.getService(excpectedService.getUniqueId(), sdncDesignerDetails);
+ AssertJUnit.assertNotNull("check response object is not null after updating service", getServiceResponse);
+ AssertJUnit.assertNotNull("check if error code exists in response after updating service", getServiceResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after updating service", 200, getServiceResponse.getErrorCode().intValue());
+ Service actualService = ResponseParser.convertServiceResponseToJavaObject(getServiceResponse.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(excpectedService, actualService, creator, updater, lifeCycleState);
+ }
+
+ public void getServiceAndValidate(ServiceReqDetails excpectedService, LifecycleStateEnum lifecycleState) throws Exception {
+ getServiceAndValidate(excpectedService, sdncDesignerDetails, sdncDesignerDetails, lifecycleState);
+ }
+
+ protected void validateResponse(RestResponse response, int errorCode, ActionStatus actionResponse, List<String> listOfVariables) throws Exception {
+ AssertJUnit.assertNotNull("check response object is not null after updating service", response);
+ AssertJUnit.assertNotNull("check if error code exists in response after updating service", response.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after updating service", errorCode, response.getErrorCode().intValue());
+
+ if (actionResponse != null) {
+ ErrorValidationUtils.checkBodyResponseOnError(actionResponse.name(), listOfVariables, response.getResponse());
+ return;
+ }
+
+ Service actualService = ResponseParser.convertServiceResponseToJavaObject(response.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(updatedServiceDetails, actualService, sdncDesignerDetails, sdncDesignerDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ protected void validateActualVsExpected(ServiceReqDetails expectedService, RestResponse actualServiceFromResponse) {
+ Service actualService = ResponseParser.convertServiceResponseToJavaObject(actualServiceFromResponse.getResponse());
+ ServiceValidationUtils.validateServiceResponseMetaData(updatedServiceDetails, actualService, sdncDesignerDetails, sdncDesignerDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ protected String multipleString(String ch, int repeat) {
+ return StringUtils.repeat(ch, repeat);
+ }
+
+ protected void correctUpdate() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 200, null, listForMessage);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ protected void updateWithInvalidValue(ActionStatus invalidValue, List<String> arr) throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, invalidValue, arr);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ protected void charactersInRangeChecking(int min, int max, String field) throws Exception {
+ if (field != null) {
+ if (field == "name") {
+ for (char ch = (char) min; ch <= (char) max; ch++) {
+ updatedServiceDetails.setName("testname" + String.valueOf(ch));
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ updateWithInvalidValue(ActionStatus.INVALID_COMPONENT_NAME, new ArrayList<>(Arrays.asList("Service")));
+ }
+ } else if (field == "icon") {
+ for (char ch = (char) min; ch <= (char) max; ch++) {
+ updatedServiceDetails.setIcon("testname" + String.valueOf(ch));
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_ICON, new ArrayList<>(Arrays.asList("Service")));
+ }
+ } else if (field == "tags") {
+ List<String> variables = Arrays.asList("Service", "tag");
+ for (char ch = (char) min; ch <= (char) max; ch++) {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(String.valueOf(ch), updatedServiceDetails.getName())));
+ updateWithInvalidValue(ActionStatus.INVALID_FIELD_FORMAT, variables);
+ }
+ } else if (field == "category") {
+ for (char ch = (char) min; ch <= (char) max; ch++) {
+ updatedServiceDetails.addCategoryChain(multipleString("1", 5) + String.valueOf(ch), multipleString("1", 5) + String.valueOf(ch));
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CATEGORY, new ArrayList<>(Arrays.asList("Service")));
+ }
+ }
+
+ else if (field == "projectCode") {
+ for (char ch = (char) min; ch <= (char) max; ch++) {
+ updatedServiceDetails.setProjectCode(multipleString("1", 5) + String.valueOf(ch));
+ updateWithInvalidValue(ActionStatus.INVALID_PROJECT_CODE, listForMessage);
+ }
+ }
+
+ else
+ return;
+ }
+
+ }
+
+ protected void specialCharsChecking(String field) throws Exception {
+ charactersInRangeChecking(33, 44, field);
+ charactersInRangeChecking(47, 47, field);
+ charactersInRangeChecking(58, 64, field);
+ charactersInRangeChecking(91, 94, field);
+ charactersInRangeChecking(96, 96, field);
+ charactersInRangeChecking(123, 126, field);
+ }
+
+ @Test
+ public void updateServiceSuccessfully() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 200, null, listForMessage);
+
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ }
+
+ protected void checkErrorResponse(ActionStatus actionStatus, ArrayList<String> arrList, RestResponse response) throws Exception, JSONException {
+ ErrorValidationUtils.checkBodyResponseOnError(actionStatus.name(), arrList, response.getResponse());
+ }
+
+ protected List<String> addServiceNameToTagsList(String serviceName, List<String> tagsList) {
+ tagsList.add(serviceName);
+ return tagsList;
+
+ }
+
+ @Test
+ public void updateService_ByOtherDesigner() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails2);
+ validateResponse(updateServiceResponse, 409, ActionStatus.RESTRICTED_OPERATION, listForMessage);
+
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void updateService_ByAdmin() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncAdminDetails);
+ validateResponse(updateServiceResponse, 409, ActionStatus.RESTRICTED_OPERATION, listForMessage);
+
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void updateServiceNotExist() throws Exception {
+ updatedServiceDetails.setUniqueId("nnnnn");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 404, ActionStatus.SERVICE_NOT_FOUND, new ArrayList<String>(Arrays.asList("")));
+ }
+
+ @Test
+ public void updateCheckedinService() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 409, ActionStatus.RESTRICTED_OPERATION, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ }
+
+ @Test
+ public void updateCertifiedService() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 409, ActionStatus.RESTRICTED_OPERATION, listForMessage);
+ getServiceAndValidate(serviceDetails, sdncDesignerDetails, sdncAdminDetails, LifecycleStateEnum.CERTIFIED);
+ }
+
+ // TODO Irrelevant
+ // @Test(enabled = false)
+ // public void updateService_NameCaseSensitiveTest() throws Exception {
+ // ServiceRestUtils.setServiceUniqueId(serviceDetails.getName().toUpperCase());
+ //
+ // RestResponse updateServiceResponse =
+ // ServiceRestUtils.updateService(updatedServiceDetails,
+ // sdncDesignerDetails);
+ // validateResponse(updateServiceResponse, 200, null, listForMessage);
+ //
+ // Service serviceFromJsonResponse =
+ // ResponseParser.convertServiceResponseToJavaObject(updateServiceResponse.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(updatedServiceDetails,
+ // serviceFromJsonResponse, sdncDesignerDetails, (LifecycleStateEnum)null);
+ //
+ // getServiceAndValidate(updatedServiceDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // }
+
+ // @Test
+ // public void updateApprovedDistributionServiceTest() throws Exception {
+ // // addMandatoryArtifactsToService();
+ // certifyService(serviceDetails, serviceDetails.getVersion());
+ //
+ // RestResponse approveResponse =
+ // ServiceRestUtils.sendApproveDistribution(sdncAdminDetails,
+ // serviceDetails.getUniqueId(), userRemarks);
+ // // validateResponse(approveResponse, 200, null, listForMessage);
+ //
+ // RestResponse updateServiceResponse =
+ // ServiceRestUtils.updateService(updatedServiceDetails,
+ // sdncDesignerDetails);
+ // validateResponse(updateServiceResponse, 409,
+ // ActionStatus.RESTRICTED_OPERATION, listForMessage);
+ //
+ // getServiceAndValidate(serviceDetails, sdncDesignerDetails,
+ // sdncAdminDetails,LifecycleStateEnum.CERTIFIED);
+ // }
+
+ @Test
+ public void updateServiceByMethod_delete() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.createServiceByHttpMethod(updatedServiceDetails, sdncDesignerDetails, "DELETE", Urls.UPDATE_SERVICE_METADATA);
+ validateResponse(updateServiceResponse, 405, ActionStatus.NOT_ALLOWED, listForMessage);
+
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void updateServiceByMethod_get() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.createServiceByHttpMethod(updatedServiceDetails, sdncDesignerDetails, "GET", Urls.UPDATE_SERVICE_METADATA);
+ validateResponse(updateServiceResponse, 405, ActionStatus.NOT_ALLOWED, listForMessage);
+
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void updateServiceByMethod_post() throws Exception {
+ RestResponse updateServiceResponse = ServiceRestUtils.createServiceByHttpMethod(updatedServiceDetails, sdncDesignerDetails, "POST", Urls.UPDATE_SERVICE_METADATA);
+ validateResponse(updateServiceResponse, 405, ActionStatus.NOT_ALLOWED, listForMessage);
+
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void updateCheckoutCertifiedService() throws Exception // certify a
+ // service
+ // and
+ // checkout
+ // it
+ {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.SERVICE_CATEGORY_CANNOT_BE_CHANGED, listForMessage);
+
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ // ---------------------------------------------------------Validation
+ // Tests---------------------------------------------------------
+
+ @Test
+ public void missingCategoryTest1() throws Exception {
+ List<CategoryDefinition> categories = updatedServiceDetails.getCategories();
+ CategoryDefinition categoryDefinition = categories.get(0);
+ CategoryDefinition categoryDefinition2 = categoryDefinition;
+ categoryDefinition2.setName("");
+ categories.set(0, categoryDefinition2);
+ updatedServiceDetails.setCategories(categories);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_CATEGORY, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingCategoryTest2() throws Exception {
+ updatedServiceDetails.setCategories(null);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_CATEGORY, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingServiceNameTest1() throws Exception {
+ updatedServiceDetails.setName(StringUtils.EMPTY);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.MISSING_COMPONENT_NAME, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingServiceNameTest2() throws Exception {
+
+ updatedServiceDetails.setName(null);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.MISSING_COMPONENT_NAME, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ // TODO Irrelevant
+ @Test(enabled = false)
+ public void missingProjectCodeTest1() throws Exception {
+ updatedServiceDetails.setProjectCode(StringUtils.EMPTY);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.MISSING_PROJECT_CODE, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ // TODO Irrelevant
+ @Test(enabled = false)
+ public void missingProjectCodeTest2() throws Exception {
+ updatedServiceDetails.setProjectCode(null);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.MISSING_PROJECT_CODE, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingIconTest1() throws Exception {
+ updatedServiceDetails.setIcon(StringUtils.EMPTY);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_ICON, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingIconTest2() throws Exception {
+ updatedServiceDetails.setIcon(null);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_ICON, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingDescriptionTest1() throws Exception {
+ updatedServiceDetails.setDescription(StringUtils.EMPTY);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_DESCRIPTION, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingDescriptionTest2() throws Exception {
+ updatedServiceDetails.setDescription(null);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_DESCRIPTION, Arrays.asList("Service"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingTagsTest1() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<String>());
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_TAGS, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingTagsTest2() throws Exception {
+ updatedServiceDetails.setTags(null);
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.COMPONENT_MISSING_TAGS, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingTagsTest3() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(StringUtils.EMPTY)));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.INVALID_FIELD_FORMAT, Arrays.asList("Service", "tag"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void missingTagsTest4() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(StringUtils.EMPTY, updatedServiceDetails.getName())));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse, 400, ActionStatus.INVALID_FIELD_FORMAT, Arrays.asList("Service", "tag"));
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ // update non-settable/"updatable" parameters tests
+
+ // ------------------------------------------correct
+ // values------------------------------------------
+ @Test
+ public void contactIdValidationTest1() throws Exception {
+ updatedServiceDetails.setContactId("ab3456");
+ correctUpdate();
+ }
+
+ @Test
+ public void contactIdValidationTest2() throws Exception {
+
+ updatedServiceDetails.setContactId("cd789E");
+ correctUpdate();
+ }
+
+ @Test
+ public void contactIdValidationTest3() throws Exception {
+
+ updatedServiceDetails.setContactId("ef4567");
+ correctUpdate();
+ }
+
+ @Test
+ public void contactIdValidationTest4() throws Exception {
+ updatedServiceDetails.setContactId("AA012A");
+ correctUpdate();
+ }
+
+ @Test
+ public void contactIdValidationTest5() throws Exception {
+ updatedServiceDetails.setContactId("CD012c");
+ correctUpdate();
+ }
+
+ @Test
+ public void contactIdValidationTest6() throws Exception {
+ updatedServiceDetails.setContactId("EF0123");
+ correctUpdate();
+ }
+
+ // ------------------------------------------invalid
+ // values------------------------------------------
+ @Test
+ public void contactIdValidationTest7() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ updatedServiceDetails.setContactId("ab0001");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void contactIdValidationTest8() throws Exception {
+ // addMandatoryArtifactsToService();
+
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser.convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ updatedServiceDetails = new ServiceReqDetails(certifyService);
+ updatedServiceDetails.setContactId("ab0001");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void contactIdValidationTest9() throws Exception {
+ updatedServiceDetails.setContactId("01345a");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, Arrays.asList("Service"));
+ }
+
+ @Test
+ public void contactIdValidationTest10() throws Exception {
+ updatedServiceDetails.setContactId("0y000B");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, Arrays.asList("Service"));
+ }
+
+ @Test
+ public void contactIdValidationTest11() throws Exception {
+ updatedServiceDetails.setContactId("Y1000b");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, Arrays.asList("Service"));
+ }
+
+ @Test
+ public void contactIdValidationTest12() throws Exception {
+ updatedServiceDetails.setContactId("abxyzC");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, Arrays.asList("Service"));
+ }
+
+ @Test
+ public void contactIdValidationTest13() throws Exception {
+ updatedServiceDetails.setContactId("cdXYZc");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest14() throws Exception {
+ updatedServiceDetails.setContactId("efXY1D");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest15() throws Exception {
+ updatedServiceDetails.setContactId("EFabcD");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest16() throws Exception {
+ updatedServiceDetails.setContactId("EFABCD");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest17() throws Exception {
+ updatedServiceDetails.setContactId("EFABC1");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest18() throws Exception {
+ updatedServiceDetails.setContactId("efui1D");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest19() throws Exception {
+ updatedServiceDetails.setContactId("efui1!");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest20() throws Exception {
+ updatedServiceDetails.setContactId("ef555!");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest21() throws Exception {
+ updatedServiceDetails.setContactId(",f555");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest22() throws Exception {
+ updatedServiceDetails.setContactId("EF55.5");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest23() throws Exception {
+ updatedServiceDetails.setContactId("ab000");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest24() throws Exception {
+ updatedServiceDetails.setContactId("ab000c0");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest25() throws Exception {
+ updatedServiceDetails.setContactId(" ab0001");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CONTACT, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void contactIdValidationTest26() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ updatedServiceDetails = new ServiceReqDetails(serviceDetails);
+ updatedServiceDetails.setContactId("xy0002");
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest1() throws Exception {
+ updatedServiceDetails.setName(multipleString("a", 49));
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest2() throws Exception {
+ updatedServiceDetails.setName(multipleString("b", 50));
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest3() throws Exception {
+ updatedServiceDetails.setName("testNamE");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest4() throws Exception {
+ updatedServiceDetails.setName("Testname");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest5() throws Exception {
+ updatedServiceDetails.setName("Test_name");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest6() throws Exception {
+ updatedServiceDetails.setName("Test name");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest7() throws Exception {
+ updatedServiceDetails.setName("Test-name");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest8() throws Exception {
+ updatedServiceDetails.setName("Test.name");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest9() throws Exception {
+ updatedServiceDetails.setName("...1...");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest10() throws Exception {
+ updatedServiceDetails.setName("-a_1. Arrrrrr");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest11() throws Exception {
+ updatedServiceDetails.setName("Testname1234567890");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ correctUpdate();
+ }
+
+ @Test
+ public void serviceNameValidationTest14() throws Exception {
+ updatedServiceDetails.setName(StringUtils.SPACE); // one space with
+ // nothing
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ // updateWithInvalidValue(ActionStatus.INVALID_COMPONENT_NAME, new
+ // ArrayList<>(Arrays.asList("Service")));
+ validateResponse(updateServiceResponse, 400, ActionStatus.MISSING_COMPONENT_NAME, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ // ------------------------------------------invalid
+ // values------------------------------------------
+ @Test
+ public void serviceNameValidationTest12() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ updatedServiceDetails.setName("TestNamE");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ }
+
+ @Test
+ public void serviceNameValidationTest13() throws Exception {
+ updatedServiceDetails.setName(multipleString("c", 51));
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ updateWithInvalidValue(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT, new ArrayList<>(Arrays.asList("Service", "50")));
+ }
+
+ @Test
+ public void serviceNameValidationTest15() throws Exception {
+ specialCharsChecking("name");
+ }
+
+ @Test
+ public void serviceNameValidationTest16() throws Exception {
+ // addMandatoryArtifactsToService();
+ LifecycleRestUtils.certifyService(serviceDetails);
+ updatedServiceDetails.setName("testnamename");
+ updatedServiceDetails.setCategories(serviceDetails.getCategories());
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ getServiceAndValidate(serviceDetails, sdncDesignerDetails, sdncTesterDetails, LifecycleStateEnum.CERTIFIED);
+ }
+
+ @Test
+ public void serviceNameValidationTest17() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ updatedServiceDetails.setName("TestNamE");
+ updatedServiceDetails.setCategories(serviceDetails.getCategories());
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ RestResponse updateServiceResponse2 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse2, 400, ActionStatus.SERVICE_NAME_CANNOT_BE_CHANGED, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void serviceNameValidationTest18() throws Exception {
+ updatedServiceDetails.setName(" testname ");
+ updatedServiceDetails.setTags(addServiceNameToTagsList(updatedServiceDetails.getName(), updatedServiceDetails.getTags()));
+ RestResponse updateServiceResponse1 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse1);
+ assertNotNull(updateServiceResponse1.getErrorCode());
+ assertEquals(200, updateServiceResponse1.getErrorCode().intValue());
+ updatedServiceDetails.setName(updatedServiceDetails.getName());
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse1);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void iconValidationTest1() throws Exception {
+ updatedServiceDetails.setIcon(multipleString("a", 24));
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest2() throws Exception {
+ updatedServiceDetails.setIcon(multipleString("b", 25));
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest3() throws Exception {
+ updatedServiceDetails.setIcon("testNamE");
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest4() throws Exception {
+ updatedServiceDetails.setIcon("Testname");
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest5() throws Exception {
+ updatedServiceDetails.setIcon("Test_name");
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest6() throws Exception {
+ updatedServiceDetails.setIcon("Test-name");
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest7() throws Exception {
+ updatedServiceDetails.setIcon("Testname1234567890");
+ correctUpdate();
+ }
+
+ @Test
+ public void iconValidationTest8() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ updatedServiceDetails.setIcon("TestNamE");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void iconValidationTest9() throws Exception {
+ // addMandatoryArtifactsToService();
+ LifecycleRestUtils.certifyService(serviceDetails);
+ updatedServiceDetails.setIcon("testnamename");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ // ------------------------------------------invalid
+ // values------------------------------------------
+ @Test
+ public void iconValidationTest10() throws Exception {
+ updatedServiceDetails.setIcon("Test name");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_ICON, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void iconValidationTest11() throws Exception {
+ updatedServiceDetails.setIcon(StringUtils.SPACE); // one space with
+ // nothing
+ updateWithInvalidValue(ActionStatus.COMPONENT_MISSING_ICON, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void iconValidationTest12() throws Exception {
+ updatedServiceDetails.setIcon("Test.name");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_ICON, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void iconValidationTest13() throws Exception {
+ specialCharsChecking("icon");
+ charactersInRangeChecking(46, 46, "icon");
+ }
+
+ @Test
+ public void iconValidationTest14() throws Exception {
+ updatedServiceDetails.setIcon(multipleString("c", 26));
+ updateWithInvalidValue(ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT, new ArrayList<>(Arrays.asList("Service", "25")));
+ }
+
+ @Test
+ public void iconValidationTest15() throws Exception {
+ // addMandatoryArtifactsToService();
+ RestResponse certifyServiceResp = LifecycleRestUtils.certifyService(serviceDetails);
+ Service certifyServiceServ = ResponseParser.convertServiceResponseToJavaObject(certifyServiceResp.getResponse());
+ ServiceReqDetails certifyService = new ServiceReqDetails(certifyServiceServ);
+ updatedServiceDetails = new ServiceReqDetails(certifyService);
+ updatedServiceDetails.setIcon("testnamename");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void iconValidationTest16() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ updatedServiceDetails = new ServiceReqDetails(serviceDetails);
+ updatedServiceDetails.setIcon("TestNamE");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.SERVICE_ICON_CANNOT_BE_CHANGED, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void iconValidationTest17() throws Exception {
+ updatedServiceDetails.setIcon(" Icon ");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_ICON, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void categoryValidationTest1() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ updatedServiceDetails.addCategory(ServiceCategoriesEnum.VOIP.getValue());
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void categoryValidationTest2() throws Exception {
+ // updatedServiceDetails.addCategory("someCategory");
+ updatedServiceDetails.setCategories(null);
+ updatedServiceDetails.addCategoryChain("someCategory", null);
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CATEGORY, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void categoryValidationTest3() throws Exception {
+ updatedServiceDetails.setCategories(null);
+ updatedServiceDetails.addCategoryChain("SomeCategory10", null);
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CATEGORY, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void categoryValidationTest4() throws Exception {
+ updatedServiceDetails.setCategories(null);
+ updatedServiceDetails.addCategoryChain("some Category", null);
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_CATEGORY, new ArrayList<>(Arrays.asList("Service")));
+ }
+
+ @Test
+ public void categoryValidationTest5() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ updatedServiceDetails = new ServiceReqDetails(serviceDetails);
+ updatedServiceDetails.addCategory("Network L1-3");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void categoryValidationTest6() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ updatedServiceDetails = new ServiceReqDetails(serviceDetails);
+ updatedServiceDetails = serviceDetails;
+ List<CategoryDefinition> categories = updatedServiceDetails.getCategories();
+ CategoryDefinition categoryDefinition = categories.get(0);
+ CategoryDefinition categoryDefinition2 = categoryDefinition;
+ categoryDefinition2.setName("ccc");
+ categories.set(0, categoryDefinition2);
+ updatedServiceDetails.setCategories(categories);
+ RestResponse updateServiceResponse2 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ validateResponse(updateServiceResponse2, 400, ActionStatus.SERVICE_CATEGORY_CANNOT_BE_CHANGED, listForMessage);
+ getServiceAndValidate(serviceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void categoryValidationTest7() throws Exception {
+ updatedServiceDetails.removeAllCategories();
+ updatedServiceDetails.addCategory(ServiceCategoriesEnum.NETWORK_L3.getValue());
+ correctUpdate();
+ }
+
+ @Test
+ public void categoryValidationTest8() throws Exception {
+ updatedServiceDetails.setCategories(null);
+ updatedServiceDetails.addCategoryChain("Network L1-3", null);
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest1() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(multipleString("a", 49), updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest2() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(multipleString("B", 50), updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest3() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(multipleString("A", 50), multipleString("B", 50), updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest5() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("testTaG", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest6() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("Testtag", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest7() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("Test_tag", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest8() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("Test tag", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest9() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("Test-tag", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest10() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("Test.tag", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest11() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("...1...", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest12() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("-a_1. Arrrrrr", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest13() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("Testtag1234567890", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest14() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("1", "2", "2", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest15() throws Exception {
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("TestTaG", updatedServiceDetails.getName())));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void tagsValidationTest16() throws Exception {
+ // addMandatoryArtifactsToService();
+ LifecycleRestUtils.certifyService(serviceDetails);
+ updatedServiceDetails = new ServiceReqDetails(serviceDetails);
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("testtagtag", updatedServiceDetails.getName())));
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ checkErrorResponse(ActionStatus.RESTRICTED_OPERATION, listForMessage, updateServiceResponse);
+ }
+
+ @Test
+ public void tagsValidationTest17() throws Exception {
+ // addMandatoryArtifactsToService();
+ certifyService(serviceDetails, serviceDetails.getVersion());
+ LifecycleRestUtils.changeServiceState(serviceDetails, sdncDesignerDetails, serviceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ updatedServiceDetails = new ServiceReqDetails(serviceDetails);
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList("TestTaG", updatedServiceDetails.getName())));
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest18() throws Exception {
+ int lengthOfServiceName = updatedServiceDetails.getName().length();
+ int maxLengthTag = 50;
+ int tagsCount = 1024 - lengthOfServiceName;
+ ArrayList<String> tagsList = new ArrayList<>();
+ tagsList.add(updatedServiceDetails.getName());
+ while (tagsCount > maxLengthTag) {
+ tagsList.add(multipleString("a", maxLengthTag));
+ tagsCount -= maxLengthTag + 1 + 1/* (50 and comma of each tag + one space, totally 52) */;
+ }
+ tagsList.add(multipleString("a", tagsCount));
+ updatedServiceDetails.setTags(tagsList);
+ correctUpdate();
+ }
+
+ @Test
+ public void tagsValidationTest19() throws Exception {
+ updatedServiceDetails.setTags(new ArrayList<>(Arrays.asList(" Tag ", updatedServiceDetails.getName())));
+ RestResponse updateServiceResponse1 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse1);
+ assertNotNull(updateServiceResponse1.getErrorCode());
+ assertEquals(200, updateServiceResponse1.getErrorCode().intValue());
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse1);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void tagsValidationTest20() throws Exception {
+ ArrayList<String> tagsList = new ArrayList<>();
+ tagsList.add(updatedServiceDetails.getName());
+ tagsList.add("");
+ updatedServiceDetails.setTags(tagsList);
+ updateWithInvalidValue(ActionStatus.INVALID_FIELD_FORMAT, Arrays.asList("Service", "tag"));
+ }
+
+ // ------------------------------------------invalid
+ // values------------------------------------------
+
+ @Test
+ public void tagsValidationTest21() throws Exception {
+ ArrayList<String> tagsList = new ArrayList<>();
+ tagsList.add("onetag");
+ updatedServiceDetails.setTags(tagsList);
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME, listForMessage);
+
+ }
+
+ @Test
+ public void tagsValidationTest22() throws Exception {
+ specialCharsChecking("tags");
+ }
+
+ @Test
+ public void descriptionValidationTest1() throws Exception {
+ updatedServiceDetails.setDescription(multipleString("a", 1023));
+ correctUpdate();
+ }
+
+ @Test
+ public void descriptionValidationTest2() throws Exception {
+ updatedServiceDetails.setDescription(multipleString("a", 1024));
+ correctUpdate();
+ }
+
+ @Test
+ public void descriptionValidationTest3() throws Exception {
+ updatedServiceDetails.setDescription(multipleString("aB", 1024 / 2));
+ correctUpdate();
+ }
+
+ @Test
+ public void descriptionValidationTest4() throws Exception {
+ updatedServiceDetails.setDescription("1234567890");
+ correctUpdate();
+ }
+
+ @Test
+ public void descriptionValidationTest5() throws Exception {
+ updatedServiceDetails.setDescription("desc ription");
+ correctUpdate();
+ }
+
+ @Test
+ public void descriptionValidationTest6() throws Exception {
+ updatedServiceDetails.setDescription("desc\tription");
+ RestResponse updateServiceResponse1 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse1);
+ assertNotNull(updateServiceResponse1.getErrorCode());
+ assertEquals(200, updateServiceResponse1.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("desc ription");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse1);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest7() throws Exception {
+ updatedServiceDetails.setDescription("desc ription ");
+ RestResponse updateServiceResponse2 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse2);
+ assertNotNull(updateServiceResponse2.getErrorCode());
+ assertEquals(200, updateServiceResponse2.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("desc ription");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse2);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest8() throws Exception {
+ updatedServiceDetails.setDescription("desc" + StringUtils.LF + "ription");
+ RestResponse updateServiceResponse3 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse3);
+ assertNotNull(updateServiceResponse3.getErrorCode());
+ assertEquals(200, updateServiceResponse3.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("desc ription");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse3);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest9() throws Exception {
+ updatedServiceDetails.setDescription("<html>Hello, <b>world!</b></html>");
+ RestResponse updateServiceResponse4 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse4);
+ assertNotNull(updateServiceResponse4.getErrorCode());
+ assertEquals(200, updateServiceResponse4.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("Hello, world!");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse4);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest10() throws Exception {
+ updatedServiceDetails.setDescription("\uC2B5");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_DESCRIPTION, new ArrayList<>(Arrays.asList("Service")));
+
+ }
+
+ @Test
+ public void descriptionValidationTest10_a() throws Exception {
+ updatedServiceDetails.setDescription("æ–‡");
+ updateWithInvalidValue(ActionStatus.COMPONENT_INVALID_DESCRIPTION, new ArrayList<>(Arrays.asList("Service")));
+
+ }
+
+ @Test
+ public void descriptionValidationTest10_b() throws Exception {
+ updatedServiceDetails.setDescription("\uC2B5");
+ RestResponse updateServiceResponse5 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse5);
+ assertNotNull(updateServiceResponse5.getErrorCode());
+ assertEquals(200, updateServiceResponse5.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("abc");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse5);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ }
+
+ @Test
+ public void descriptionValidationTest11() throws Exception {
+ updatedServiceDetails.setDescription("&<>");
+ RestResponse updateServiceResponse6 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse6);
+ assertNotNull(updateServiceResponse6.getErrorCode());
+ assertEquals(200, updateServiceResponse6.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("&amp;&lt;&gt;");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse6);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest12() throws Exception {
+ updatedServiceDetails.setDescription("æ–‡ test");
+ RestResponse updateServiceResponse7 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse7);
+ assertNotNull(updateServiceResponse7.getErrorCode());
+ assertEquals(200, updateServiceResponse7.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("test");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse7);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest13() throws Exception {
+ updatedServiceDetails.setDescription(" description");
+ RestResponse updateServiceResponse8 = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse8);
+ assertNotNull(updateServiceResponse8.getErrorCode());
+ assertEquals(200, updateServiceResponse8.getErrorCode().intValue());
+ updatedServiceDetails.setDescription("description");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse8);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ @Test
+ public void descriptionValidationTest14() throws Exception {
+ updatedServiceDetails.setDescription(multipleString("a", 1025));
+ updateWithInvalidValue(ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT, new ArrayList<>(Arrays.asList("Service", "1024")));
+ }
+
+ @Test
+ public void projectCodeValidationTest1() throws Exception {
+ String desc = StringUtils.EMPTY;
+ for (int i = 0; i < 10; i++) {
+ desc += Integer.toString(i);
+ if (i >= 4) {
+ updatedServiceDetails.setProjectCode(desc);
+ correctUpdate();
+ }
+ }
+ }
+
+ @Test
+ public void projectCodeValidationTest2() throws Exception {
+ updatedServiceDetails.setProjectCode(multipleString("1", 6));
+ correctUpdate();
+ }
+
+ @Test
+ public void projectCodeValidationTest3() throws Exception {
+ this.specialCharsChecking("projectCode");
+ }
+
+ // TODO Irrelevant
+ @Test(enabled = false)
+ public void projectCodeValidationTest4() throws Exception {
+ updatedServiceDetails.setProjectCode(multipleString(" ", 5) + "99999");
+ RestResponse updateServiceResponse = ServiceRestUtils.updateService(updatedServiceDetails, sdncDesignerDetails);
+ assertNotNull(updateServiceResponse);
+ assertNotNull(updateServiceResponse.getErrorCode());
+ assertEquals(200, updateServiceResponse.getErrorCode().intValue());
+ updatedServiceDetails.setProjectCode("12345");
+ validateActualVsExpected(updatedServiceDetails, updateServiceResponse);
+ getServiceAndValidate(updatedServiceDetails, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ }
+
+ @Test
+ public void projectCodeValidationTest5() throws Exception {
+ updatedServiceDetails.setProjectCode(multipleString("0", 11));
+ updateWithInvalidValue(ActionStatus.INVALID_PROJECT_CODE, listForMessage);
+ }
+
+ @Test
+ public void projectCodeValidationTest6() throws Exception {
+ updatedServiceDetails.setProjectCode(multipleString("1", 4));
+ updateWithInvalidValue(ActionStatus.INVALID_PROJECT_CODE, listForMessage);
+ }
+
+ @Test
+ public void projectCodeValidationTest7() throws Exception {
+ updatedServiceDetails.setProjectCode("123456789");
+ correctUpdate();
+ }
+
+ // ////US553874
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion01_isVNF_toTrue() throws Exception{
+ //
+ // //choose the user to create service
+ // User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //
+ // //Update Service IsVNF to True
+ // restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 200, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion02_isVNF_toFalse() throws Exception{
+ //
+ // //choose the user to create service
+ // User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //
+ // //Update Service IsVNF to True
+ // restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 200, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion01_isVNF_TrueToNull() throws Exception{
+ //
+ // //choose the user to create service
+ // User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //
+ // //Update Service IsVNF to True
+ // restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 400, restResponse.getErrorCode().intValue());
+ // List<String> variables = Arrays.asList("VNF Service Indicator");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_DATA.name(),
+ // variables, restResponse.getResponse());
+ //
+ // //get service and verify that service created with isVNF is remained with
+ // isVNF = true
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion01_isVNF_FalseToNull() throws Exception{
+ //
+ // //choose the user to create service
+ // User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //
+ // //Update Service IsVNF to True
+ // restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 400, restResponse.getErrorCode().intValue());
+ // List<String> variables = Arrays.asList("VNF Service Indicator");
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.MISSING_DATA.name(),
+ // variables, restResponse.getResponse());
+ //
+ // //get service and verify that service created with isVNF is remained with
+ // isVNF = true
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion02_IsVNF_toTrue() throws Exception{
+ //
+ // //choose the user to create service
+ // User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // LifecycleRestUtils.changeServiceState(serviceDetails,
+ // sdncDesignerDetails, serviceDetails.getVersion(),
+ // LifeCycleStatesEnum.CHECKIN);
+ // LifecycleRestUtils.changeServiceState(serviceDetails,
+ // sdncDesignerDetails, serviceDetails.getVersion(),
+ // LifeCycleStatesEnum.CHECKOUT);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //
+ // //Update Service IsVNF to True
+ // restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 200, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion02_IsVNF_toFalse() throws Exception{
+ //
+ // //choose the user to create service
+ // User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // LifecycleRestUtils.changeServiceState(serviceDetails,
+ // sdncDesignerDetails, serviceDetails.getVersion(),
+ // LifeCycleStatesEnum.CHECKIN);
+ // LifecycleRestUtils.changeServiceState(serviceDetails,
+ // sdncDesignerDetails, serviceDetails.getVersion(),
+ // LifeCycleStatesEnum.CHECKOUT);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //
+ // //Update Service IsVNF to false
+ // restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // //restResponse =
+ // ServiceRestUtils.updateService(serviceDetails.getUniqueId(),
+ // serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 200, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion11_IsVNF_toFalse() throws Exception{
+ // // Can't update isVNF when service version is 1.X
+ // User sdncUserDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // //String serviceUniqueName =
+ // ServiceRestUtils.getServiceUniqueId(serviceByNameAndVersion);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails, "0.1",
+ // sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //// ServiceRestUtils.addServiceMandatoryArtifacts(sdncUserDetails,
+ // restResponse);
+ // RestResponse response =
+ // ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ // sdncUserDetails, serviceDetails.getUniqueId(),
+ // ComponentTypeEnum.SERVICE);
+ // assertTrue("response code is not 201, returned: " +
+ // response.getErrorCode(),response.getErrorCode() == 201);
+ // RestResponse changeServiceState =
+ // LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ // assertTrue("certify service request returned status:" +
+ // changeServiceState.getErrorCode(),changeServiceState.getErrorCode() ==
+ // 200);
+ // LifecycleRestUtils.changeServiceState(serviceDetails, sdncUserDetails,
+ // LifeCycleStatesEnum.CHECKOUT);
+ //
+ // //Update Service IsVNF to false
+ // restResponse = ServiceRestUtils.updateService(serviceDetails,
+ // sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating service metadata", 400,
+ // restResponse.getErrorCode().intValue());
+ // List<String> variables = new ArrayList<String>();
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_IS_VNF_CANNOT_BE_CHANGED.name(),
+ // variables, restResponse.getResponse());
+ //
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "1.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ //
+ // }
+ //
+ // @JsonIgnore
+ // @Test
+ // public void UpdateServiceVersion11_IsVNF_toTrue() throws Exception{
+ // // Can't update isVNF when service version is 1.X
+ // User sdncUserDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // // new service details
+ // // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // // clean audit DB before updating service
+ // DbUtils.cleanAllAudits();
+ // ServiceRestUtils.deleteServiceById(serviceDetails.getUniqueId(),
+ // sdncUserDetails.getUserId());
+ // serviceDetails = ElementFactory.getDefaultService();
+ //
+ // //send create service toward BE
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating Interface Artifact",
+ // 201, restResponse.getErrorCode().intValue());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // RestResponse serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "0.1");
+ // Service serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // //String serviceUniqueName =
+ // ServiceRestUtils.getServiceUniqueId(serviceByNameAndVersion);
+ //
+ // //validate audit
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceValidationUtils.constructFieldsForAuditValidation(serviceDetails,
+ // "0.1", sdncUserDetails);
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //// ServiceRestUtils.addServiceMandatoryArtifacts(sdncUserDetails,
+ // restResponse);
+ // RestResponse response =
+ // ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails,
+ // sdncUserDetails, serviceDetails.getUniqueId(),
+ // ComponentTypeEnum.SERVICE);
+ // assertTrue("response code is not 201, returned: " +
+ // response.getErrorCode(),response.getErrorCode() == 201);
+ // RestResponse changeServiceState =
+ // LCSbaseTest.certifyService(serviceDetails, sdncDesignerDetails);
+ // assertTrue("certify service request returned status:" +
+ // changeServiceState.getErrorCode(),changeServiceState.getErrorCode() ==
+ // 200);
+ // LifecycleRestUtils.changeServiceState(serviceDetails, sdncUserDetails,
+ // LifeCycleStatesEnum.CHECKOUT);
+ //
+ // //Update Service IsVNF to false
+ // restResponse = ServiceRestUtils.updateService(serviceDetails,
+ // sdncUserDetails);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after updating service metadata", 400,
+ // restResponse.getErrorCode().intValue());
+ // List<String> variables = new ArrayList<String>();
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.SERVICE_IS_VNF_CANNOT_BE_CHANGED.name(),
+ // variables, restResponse.getResponse());
+ //
+ // //get service and verify that service created with isVNF defined in
+ // serviceDetails
+ // serviceByNameAndVersion =
+ // ServiceRestUtils.getServiceByNameAndVersion(sdncUserDetails,
+ // serviceDetails.getName(), "1.1");
+ // serviceObject =
+ // ResponseParser.convertServiceResponseToJavaObject(serviceByNameAndVersion.getResponse());
+ // ServiceValidationUtils.validateServiceResponseMetaData(serviceDetails,
+ // serviceObject, sdncUserDetails,
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/ActivateDeActivateDeleteUser.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/ActivateDeActivateDeleteUser.java
new file mode 100644
index 0000000000..c83018241b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/ActivateDeActivateDeleteUser.java
@@ -0,0 +1,832 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.user;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.http.HttpStatus;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedUserCRUDAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.users.UserResponseMessageEnum;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ImportRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.UserRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ResourceValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.UserValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * @author alitvinsky
+ *
+ */
+public class ActivateDeActivateDeleteUser extends ComponentBaseTest {
+ private static Logger logger = LoggerFactory.getLogger(ActivateDeActivateDeleteUser.class.getName());
+ protected Gson gson = new Gson();
+ protected User sdncAdminUser;
+
+ @BeforeMethod
+ public void init() {
+ sdncAdminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ }
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ActivateDeActivateDeleteUser() {
+ super(name, ActivateDeActivateDeleteUser.class.getName());
+ }
+
+ @Test
+ public void authorizeDeActivatedUser() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ // deActivate created user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ validateSuccessDeActivateUserResponse(sdncUserDetails, deActivateUserResponse);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+
+ AssertJUnit.assertEquals("Check response code after deActive user", errorInfo.getCode(),
+ getUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables,
+ getUserResponse.getResponse());
+
+ // clean audit before authorization test
+ DbUtils.cleanAllAudits();
+
+ // Perform login from WebSeal
+ User sealUserDetails = sdncUserDetails;
+ RestResponse authorizedUserResponse = UserRestUtils.authorizedUserTowardsCatalogBeQA(sealUserDetails);
+
+ // validate response
+
+ ErrorInfo errorInfo2 = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_ACCESS.name());
+
+ AssertJUnit.assertNotNull("check response object is not null after user login", authorizedUserResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after user login",
+ authorizedUserResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after deActive user", errorInfo2.getCode(),
+ authorizedUserResponse.getErrorCode());
+
+ List<String> variables2 = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESTRICTED_ACCESS.name(), variables2,
+ authorizedUserResponse.getResponse());
+
+ // validate against ES DB
+
+ UserValidationUtils.validateDataAgainstAuditDB_access(sealUserDetails,
+ DbUtils.parseAuditRespByAction("Access"), authorizedUserResponse, errorInfo2, variables2);
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ public User getDefaultUserDetails() {
+
+ String httpCspUserId = "km2000";
+ String userFirstName = "Kot";
+ String userLastName = "May";
+ String email = "km2000@intl.sdc.com";
+ String role = UserRoleEnum.ADMIN.name();
+ User sdncUserDetails = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+
+ return sdncUserDetails;
+ }
+
+ public void validateSuccessCreateUserResponse(User sdncUserDetails, RestResponse createUserResponse)
+ throws Exception {
+
+ AssertJUnit.assertNotNull("check response object is not null after create user", createUserResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create user",
+ createUserResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create user", HttpStatus.SC_CREATED,
+ createUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, createUserResponse.getResponse());
+ // UserRestUtils.validateAddUserAuditMessage(sdncUserDetails,
+ // sdncAdminUser, String.valueOf(HttpStatus.SC_CREATED),
+ // UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ // UserRestUtils.getAddUserAuditMessage("AddUser"));
+ String addUser = "AddUser";
+ ExpectedUserCRUDAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(addUser,
+ sdncAdminUser, ActionStatus.CREATED, sdncUserDetails, null);
+ AuditValidationUtils.validateAddUserAudit(constructFieldsForAuditValidation, addUser);
+
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, getUserResponse.getResponse());
+
+ }
+
+ public void validateSuccessDeActivateUserResponse(User sdncUserDetails, RestResponse deActivateUserResponse)
+ throws Exception {
+
+ AssertJUnit.assertNotNull("check response object is not null after deActive user", deActivateUserResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after deActive user",
+ deActivateUserResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after deActive user", 200,
+ deActivateUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, deActivateUserResponse.getResponse());
+
+ String deleteUser = "DeleteUser";
+ ExpectedUserCRUDAudit constructFieldsForAuditValidation = Convertor
+ .constructFieldsForAuditValidation(deleteUser, sdncAdminUser, ActionStatus.OK, null, sdncUserDetails);
+ AuditValidationUtils.validateAddUserAudit(constructFieldsForAuditValidation, deleteUser);
+
+ }
+
+ // US498322 - Add Status Field to USER
+
+ @Test
+ public void createNewUser() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ @Test
+ public void createDefaultUser() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+ sdncUserDetails.setFirstName(null);
+ sdncUserDetails.setLastName(null);
+ sdncUserDetails.setEmail(null);
+ sdncUserDetails.setRole(null);
+
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ @Test
+ public void createTesterUser() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+ sdncUserDetails.setLastName(null);
+ sdncUserDetails.setRole(UserRoleEnum.TESTER.name());
+
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ @Test
+ public void deActivateCreatedAdminUser() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ // deActivate created user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ validateSuccessDeActivateUserResponse(sdncUserDetails, deActivateUserResponse);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+
+ assertEquals("Check response code after get user", errorInfo.getCode(), getUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables,
+ getUserResponse.getResponse());
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ @Test
+ public void deActivateTheSameUserTwice() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ // deActivate created user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ validateSuccessDeActivateUserResponse(sdncUserDetails, deActivateUserResponse);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+
+ assertEquals("Check response code after deActive user", errorInfo.getCode(),
+ getUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables,
+ getUserResponse.getResponse());
+
+ // deActivate the same user once time more
+ RestResponse deActivateUserResponse2 = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ ErrorInfo errorInfo2 = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ assertEquals("Check response code after deActive user", errorInfo2.getCode(),
+ deActivateUserResponse2.getErrorCode());
+
+ List<String> variables2 = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables2,
+ deActivateUserResponse2.getResponse());
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ @Test
+ public void createAgainDeActivatedUser() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+
+ try {
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ // deActivate created user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ validateSuccessDeActivateUserResponse(sdncUserDetails, deActivateUserResponse);
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+
+ assertEquals("Check response code after deActive user", errorInfo.getCode(),
+ getUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables,
+ getUserResponse.getResponse());
+
+ // create the user with the same UserId(details) as deActivated user
+ DbUtils.cleanAllAudits();
+
+ RestResponse createUserResponse2 = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ ErrorInfo errorInfo2 = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ assertEquals("Check response code after deActive user", errorInfo2.getCode(),
+ createUserResponse2.getErrorCode());
+
+ List<String> variables2 = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables2,
+ createUserResponse2.getResponse());
+
+ } finally {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ // very not recommend to run this test, resources/services may be zombie
+ // @Test
+ public void deActivateLastAdminUser() throws Exception {
+
+ try {
+
+ // send get all ADMIN user request toward BE
+ RestResponse getAllAdminUsers = UserRestUtils.getAllAdminUsers(sdncAdminUser);
+
+ assertNotNull("check response object is not null after create user", getAllAdminUsers);
+ assertNotNull("check error code exists in response after create user", getAllAdminUsers.getErrorCode());
+ assertEquals("Check response code after create user", 200, getAllAdminUsers.getErrorCode().intValue());
+
+ TypeToken<List<User>> typeToken = new TypeToken<List<User>>() {
+ };
+ List<User> listOfUsersOnResponse = gson.fromJson(getAllAdminUsers.getResponse(), typeToken.getType());
+ logger.debug("listOfUsers: {}", listOfUsersOnResponse);
+
+ // build map of all Admin users from listOfUsersOnResponse from
+ // response
+ Map<String, User> mapAllUsersOnResponse = new HashMap<String, User>();
+ for (User sdncUser : listOfUsersOnResponse) {
+ mapAllUsersOnResponse.put(sdncUser.getUserId(), sdncUser);
+ }
+
+ // remove from mapAllUsersOnResponse map one of admin users
+ mapAllUsersOnResponse.remove(sdncAdminUser.getUserId());
+ logger.debug("map Of all Admin users exclude one : {}", mapAllUsersOnResponse);
+
+ // deActivate all Admin users from the userIdAllAdminList list
+ for (Entry<String, User> entry : mapAllUsersOnResponse.entrySet()) {
+ UserRestUtils.deActivateUser(entry.getValue(), sdncAdminUser);
+ }
+
+ // deActivate last Admin user user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncAdminUser, sdncAdminUser);
+
+ ErrorInfo errorInfo = ErrorValidationUtils
+ .parseErrorConfigYaml(ActionStatus.DELETE_USER_ADMIN_CONFLICT.name());
+
+ assertEquals("Check response code after deActive user", errorInfo.getCode(),
+ deActivateUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList();
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.DELETE_USER_ADMIN_CONFLICT.name(), variables,
+ deActivateUserResponse.getResponse());
+
+ } finally {
+ // UserRestUtils.deleteUser(UserRestUtils.getAdminDetails2(),
+ // sdncAdminUser);
+ // UserRestUtils.deleteUser(UserRestUtils.getAdminDetails3(),
+ // sdncAdminUser);
+ // UserRestUtils.createUser(UserRestUtils.getAdminDetails2(),
+ // sdncAdminUser);
+ // UserRestUtils.createUser(UserRestUtils.getAdminDetails3(),
+ // sdncAdminUser);
+ }
+
+ }
+
+ // test check the resource accessibility via catalog view, resource was
+ // created by user which was deActivated
+
+ @Test
+ public void resourceAccessibility() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource("tosca.nodes.newresource4test4",
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, "jh0003");
+ String resourceBaseVersion = "0.1";
+
+ try {
+ // Delete resource
+ // resourceUtils.deleteResource_allVersions(resourceDetails,
+ // sdncAdminUser);
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+
+ DbUtils.cleanAllAudits();
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ // ------------------------Start create
+ // resource---------------------------------------------------------------------------------
+
+ // create resource
+ RestResponse createResponse = ResourceRestUtils.createResource(resourceDetails, sdncUserDetails);
+ assertEquals("Check response code after create", 201, createResponse.getErrorCode().intValue());
+
+ Resource createdResource = ResponseParser.convertResourceResponseToJavaObject(createResponse.getResponse());
+
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncUserDetails,
+ createdResource.getUniqueId());
+ assertEquals("Check response code after get", 200, resourceGetResponse.getErrorCode().intValue());
+
+ // validate get response
+ ResourceRespJavaObject resourceRespJavaObject = Convertor.constructFieldsForRespValidation(resourceDetails,
+ resourceBaseVersion);
+ resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setAbstractt("false");
+ resourceRespJavaObject.setCreatorUserId(sdncUserDetails.getUserId());
+ resourceRespJavaObject.setLastUpdaterUserId(sdncUserDetails.getUserId());
+
+ resourceRespJavaObject
+ .setCreatorFullName(sdncUserDetails.getFirstName() + " " + sdncUserDetails.getLastName());
+ resourceRespJavaObject
+ .setLastUpdaterFullName(sdncUserDetails.getFirstName() + " " + sdncUserDetails.getLastName());
+
+ ResourceValidationUtils.validateResp(resourceGetResponse, resourceRespJavaObject);
+
+ // ------------------------End create
+ // resource---------------------------------------------------------------------------------
+
+ // clean audit before authorization test
+ DbUtils.cleanAllAudits();
+
+ // deActivate created user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ validateSuccessDeActivateUserResponse(sdncUserDetails, deActivateUserResponse);
+
+ UserValidationUtils.validateDeleteUserAuditMessage(sdncUserDetails, sdncAdminUser, "200",
+ UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("DeleteUser"));
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+
+ assertEquals("Check response code after deActive user", errorInfo.getCode(),
+ getUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables,
+ getUserResponse.getResponse());
+
+ // checking if created resource is accessible
+ DbUtils.cleanAllAudits();
+
+ RestResponse getCatalogDataResponse = CatalogRestUtils.getCatalog(sdncAdminUser.getUserId());
+
+ // validate response
+
+ assertNotNull("check response object is not null after user login", getCatalogDataResponse);
+ assertNotNull("check error code exists in response after user login",
+ getCatalogDataResponse.getErrorCode());
+ assertEquals("Check response code after deActive user", 200,
+ getCatalogDataResponse.getErrorCode().intValue());
+
+ // expected resource list
+ List<String> resourceExpectedUniqIdList = new ArrayList<String>();
+ resourceExpectedUniqIdList.add(resourceDetails.getUniqueId());
+ logger.debug("resourceExpectedUniqIdList: {}", resourceExpectedUniqIdList);
+
+ compareResourceUniqIdList(getCatalogDataResponse.getResponse(), resourceExpectedUniqIdList, true);
+
+ } finally {
+ // resourceUtils.deleteResource_allVersions(resourceDetails,
+ // sdncAdminUser);
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ // test check the resource accessibility via catalog view, resource was
+ // created by user which was deActivated
+
+ @Test
+ public void resourceAccessibilityOnImport() throws Exception {
+
+ User sdncUserDetails = getDefaultUserDetails();
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource("importResource4test",
+ NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, "jh0003");
+ resourceDetails.addCategoryChain(ResourceCategoryEnum.GENERIC_DATABASE.getCategory(),
+ ResourceCategoryEnum.GENERIC_DATABASE.getSubCategory());
+ // String resourceBaseVersion = "1.0";
+
+ try {
+ // Delete resource
+ // resourceUtils.deleteResource_allVersions(resourceDetails,
+ // sdncAdminUser);
+ RestResponse deleteUserResponse = UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ assertTrue("delete user request failed",
+ deleteUserResponse.getErrorCode() == 200 || deleteUserResponse.getErrorCode() == 404);
+ DbUtils.cleanAllAudits();
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+
+ // ------------------------Start import
+ // resource---------------------------------------------------------------------------------
+
+ // import new resource with CERTIFIED state
+ User importer = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN4);
+ RestResponse importResponse = ImportRestUtils.importResourceByName(resourceDetails, importer);
+
+ assertNotNull("check response object is not null after create user", importResponse);
+ assertNotNull("check error code exists in response after create user", importResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, importResponse.getErrorCode().intValue());
+
+ // ------------------------End import
+ // resource---------------------------------------------------------------------------------
+
+ // clean audit before authorization test
+ DbUtils.cleanAllAudits();
+
+ // deActivate created user
+ RestResponse deActivateUserResponse = UserRestUtils.deActivateUser(sdncUserDetails, sdncAdminUser);
+ sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ validateSuccessDeActivateUserResponse(sdncUserDetails, deActivateUserResponse);
+
+ UserValidationUtils.validateDeleteUserAuditMessage(sdncUserDetails, sdncAdminUser, "200",
+ UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("DeleteUser"));
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+
+ assertEquals("Check response code after deActive user", errorInfo.getCode(),
+ getUserResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(), variables,
+ getUserResponse.getResponse());
+
+ // checking if created resource is accessible
+ DbUtils.cleanAllAudits();
+
+ RestResponse getCatalogDataResponse = CatalogRestUtils.getCatalog(sdncAdminUser.getUserId());
+
+ // validate response
+
+ assertNotNull("check response object is not null after user login", getCatalogDataResponse);
+ assertNotNull("check error code exists in response after user login",
+ getCatalogDataResponse.getErrorCode());
+ assertEquals("Check response code after deActive user", 200,
+ getCatalogDataResponse.getErrorCode().intValue());
+
+ // expected resource list
+ List<String> resourceExpectedUniqIdList = new ArrayList<String>();
+ resourceExpectedUniqIdList.add(resourceDetails.getUniqueId());
+ logger.debug("resourceExpectedUniqIdList: {}", resourceExpectedUniqIdList);
+
+ compareResourceUniqIdList(getCatalogDataResponse.getResponse(), resourceExpectedUniqIdList, true);
+
+ } finally {
+ // resourceUtils.deleteResource_allVersions(resourceDetails,
+ // sdncAdminUser);
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ }
+
+ }
+
+ // test check the service accessibility via catalog view, service was
+ // created by user which was deActivated
+
+ // @Test
+ // public void serviceAccessibility() throws Exception{
+ //
+ // User sdncUserDetails = getDefaultUserDetails();
+ //// fill new service details
+ // ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ // String serviceBaseVersion = "0.1";
+ //
+ // try{
+ // //Delete service
+ //// ServiceRestUtils.deleteService_allVersions(serviceDetails,
+ // sdncAdminUser);
+ // UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ //
+ // DbUtils.cleanAllAudits();
+ // RestResponse createUserResponse =
+ // UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ // validateSuccessCreateUserResponse(sdncUserDetails, createUserResponse);
+ //
+ //// ------------------------Start create
+ // service---------------------------------------------------------------------------------
+ // RestResponse restResponse =
+ // ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ //
+ // assertNotNull("check response object is not null after create service",
+ // restResponse);
+ // assertNotNull("check error code exists in response after create service",
+ // restResponse.getErrorCode());
+ // assertEquals("Check response code after create service", 201,
+ // restResponse.getErrorCode().intValue());
+ //
+ //// validate create service response vs actual
+ //
+ // Service service =
+ // ServiceRestUtils.convertServiceResponseToJavaObject(restResponse.getResponse());
+ // UserValidationUtils.validateServiceResponseMetaData(serviceDetails,service,sdncUserDetails,
+ // (LifecycleStateEnum)null);
+ //
+ //// validate get service response vs actual
+ // restResponse = ServiceRestUtils.getService(serviceDetails.getUniqueId(),
+ // sdncUserDetails);
+ // service =
+ // ServiceRestUtils.convertServiceResponseToJavaObject(restResponse.getResponse());
+ // UserValidationUtils.validateServiceResponseMetaData(serviceDetails,service,sdncUserDetails,
+ // (LifecycleStateEnum)null);
+ //
+ // //validate audit
+ //
+ // ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ // ServiceRestUtils.constructFieldsForAuditValidation(serviceDetails,
+ // serviceBaseVersion, sdncUserDetails);
+ //
+ // String auditAction="Create";
+ // expectedResourceAuditJavaObject.setAction(auditAction);
+ // expectedResourceAuditJavaObject.setPrevState("");
+ // expectedResourceAuditJavaObject.setPrevVersion("");
+ // expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ // expectedResourceAuditJavaObject.setStatus("201");
+ // expectedResourceAuditJavaObject.setDesc("OK");
+ //
+ // AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject,
+ // auditAction, null, false);
+ //
+ //// ------------------------End create
+ // service---------------------------------------------------------------------------------
+ //
+ //// clean audit before authorization test
+ // DbUtils.cleanAllAudits();
+ //
+ //// deActivate created user
+ // RestResponse deActivateUserResponse =
+ // UserRestUtils.deActivateUser(sdncUserDetails,sdncAdminUser);
+ // sdncUserDetails.setStatus(UserStatusEnum.INACTIVE);
+ // validateSuccessDeActivateUserResponse(sdncUserDetails,
+ // deActivateUserResponse);
+ //
+ // UserValidationUtils.validateDeleteUserAuditMessage(sdncUserDetails,
+ // sdncAdminUser, "200", UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ // UserValidationUtils.getAddUserAuditMessage("DeleteUser"));
+ //
+ // ErrorInfo errorInfo =
+ // ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_INACTIVE.name());
+ // RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails,
+ // sdncAdminUser);
+ //
+ // assertEquals("Check response code after deActive user",
+ // errorInfo.getCode(), getUserResponse.getErrorCode());
+ //
+ // List<String> variables = Arrays.asList(sdncUserDetails.getUserId());
+ // ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.USER_INACTIVE.name(),
+ // variables, getUserResponse.getResponse());
+ //
+ // //checking if created service is accessible
+ // DbUtils.cleanAllAudits();
+ //
+ // RestResponse getCatalogDataResponse =
+ // CatalogRestUtils.getCatalog(sdncAdminUser.getUserId());
+ //
+ // //validate response
+ //
+ // assertNotNull("check response object is not null after user login",
+ // getCatalogDataResponse);
+ // assertNotNull("check error code exists in response after user login",
+ // getCatalogDataResponse.getErrorCode());
+ // assertEquals("Check response code after deActive user", 200,
+ // getCatalogDataResponse.getErrorCode().intValue());
+ //
+ //// expected service list
+ // List<String> serviceExpectedUniqIdList= new ArrayList<String>();
+ // serviceExpectedUniqIdList.add(serviceDetails.getUniqueId());
+ // logger.debug("serviceExpectedUniqIdList: {}", serviceExpectedUniqIdList);
+ //
+ // compareServiceUniqIdList(getCatalogDataResponse.getResponse(),
+ // serviceExpectedUniqIdList, true);
+ //
+ //
+ // }finally{
+ //// ServiceRestUtils.deleteService_allVersions(serviceDetails,
+ // sdncAdminUser);
+ // UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ // }
+ //
+ // }
+
+ public void compareServiceUniqIdList(String response, List<String> expectedList, boolean flag) {
+
+ JsonElement jelement = new JsonParser().parse(response);
+ JsonObject jobject = jelement.getAsJsonObject();
+ JsonArray serviceArray = (JsonArray) jobject.get("services");
+ logger.debug("{}", serviceArray);
+ assertTrue("expected service count: " + expectedList.size() + " or more" + ", actual: " + serviceArray.size(),
+ serviceArray.size() >= expectedList.size());
+
+ // build service list from response
+ List<ServiceReqDetails> serviceReqDetailsListOnResponse = new ArrayList<ServiceReqDetails>();
+ for (int i = 0; i < serviceArray.size(); i++) {
+ ServiceReqDetails json = gson.fromJson(serviceArray.get(i), ServiceReqDetails.class);
+ serviceReqDetailsListOnResponse.add(json);
+ }
+ }
+
+ public void compareResourceUniqIdList(String response, List<String> expectedList, boolean flag) {
+
+ JsonElement jelement = new JsonParser().parse(response);
+ JsonObject jobject = jelement.getAsJsonObject();
+ JsonArray resourceArray = (JsonArray) jobject.get("resources");
+ logger.debug("{}", resourceArray);
+ assertTrue("expected resource count: " + expectedList.size() + " or more" + ", actual: " + resourceArray.size(),
+ resourceArray.size() >= expectedList.size());
+
+ // build resource list from response
+ List<ResourceReqDetails> resourceReqDetailsListOnResponse = new ArrayList<ResourceReqDetails>();
+ for (int i = 0; i < resourceArray.size(); i++) {
+ ResourceReqDetails json = gson.fromJson(resourceArray.get(i), ResourceReqDetails.class);
+ resourceReqDetailsListOnResponse.add(json);
+ }
+
+ logger.debug("ResourceReqDetails list on response: {}", resourceReqDetailsListOnResponse);
+
+ List<String> resourceActualUniqIdList = new ArrayList<String>();
+ for (ResourceReqDetails resource : resourceReqDetailsListOnResponse) {
+ resourceActualUniqIdList.add(resource.getUniqueId());
+ }
+ logger.debug("resourceActualUniqIdList on response: {}", resourceActualUniqIdList);
+ logger.debug("resourceExpectedUniqIdList on response: {}", expectedList);
+
+ if (flag) {
+ assertTrue("actual list does not contain expected list",
+ resourceActualUniqIdList.containsAll(expectedList));
+ } else {
+ assertFalse("actual list contains non expected list elements",
+ resourceActualUniqIdList.containsAll(expectedList));
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/CreateUserApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/CreateUserApiTest.java
new file mode 100644
index 0000000000..8684910a18
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/CreateUserApiTest.java
@@ -0,0 +1,1451 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.user;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.io.IOException;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedUserCRUDAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.users.UserResponseMessageEnum;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.UserRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.UserValidationUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CreateUserApiTest extends ComponentBaseTest {
+
+ protected User sdncAdminUser;
+ protected User sdncDesignerUser;
+ protected User sdncGovernorUser;
+ protected User sdncTesterUser;
+
+ public static final int STATUS_CODE_SUCCESS = 200;
+ public static final int STATUS_CODE_SUCSESS_CREATED = 201;
+ public static final int STATUS_CODE_SUCCESS_DELETE_GET = 200;
+ public static final int STATUS_CODE_INVALID_CONTENT = 400;
+ public static final int STATUS_CODE_MISSING_DATA = 400;
+ public static final int STATUS_CODE_MISSING_INFORMATION = 403;
+ public static final int STATUS_CODE_RESTRICTED_ACCESS = 403;
+ public static final int STATUS_CODE_NOT_FOUND = 404;
+ public static final int STATUS_CODE_RESTRICTED_OPERATION = 409;
+ public static final int USER_ALREADY_EXIST = 409;
+ public static final int INVALID_ROLE = 400;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CreateUserApiTest() {
+ super(name, CreateUserApiTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void init() {
+ sdncAdminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ sdncDesignerUser = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncGovernorUser = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ sdncTesterUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+
+ }
+
+ // Story : REST API to provision new user (POST) - US429379
+ // must parameters: UserId and Email
+
+ // **********************************************************201***************************************************
+ // create user with full parameter set(UserID, First Name, Last Name, Email,
+ // Role = "DESIGNER", Creator details)
+ // expected 201 Created
+ @Test
+ public void createUser() throws Exception {
+
+ // user initialization
+ String httpCspUserId = "km2000";
+ String userFirstName = "Kot";
+ String userLastName = "Matroskin";
+ String email = "km2000@intl.sdc.com";
+ String role = "ADMIN";
+ User sdncUserDetails = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ String addUser = "AddUser";
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+
+ AssertJUnit.assertNotNull("check response object is not null after create user", createUserResponse);
+ AssertJUnit.assertNotNull("check error code exists in response after create user",
+ createUserResponse.getErrorCode());
+ AssertJUnit.assertEquals("Check response code after create user", 201,
+ createUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, createUserResponse.getResponse());
+
+ ExpectedUserCRUDAudit constructFieldsForAuditValidation = Convertor.constructFieldsForAuditValidation(addUser,
+ sdncAdminUser, ActionStatus.CREATED, sdncUserDetails, null);
+ AuditValidationUtils.validateAddUserAudit(constructFieldsForAuditValidation, addUser);
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, getUserResponse.getResponse());
+ }
+
+ protected static final String ADD_USER = "AddUser";
+
+ private User mechIdUser = new User();
+ private User emptyUser = new User();
+ private static final User adminUser = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+
+ @AfterMethod
+ public void setup() throws IOException {
+ UserRestUtils.deactivateUser(mechIdUser, adminUser);
+ }
+
+ // create default user(UserID, Email, Creator details)
+ // expected: role = DESIGNER, first and last name = null, 201 Created
+ @Test
+ public void createDefaultUser() throws Exception {
+ // user initialization
+ String httpCspUserId = "km2000";
+ String userFirstName = null;
+ String userLastName = null;
+ String email = null;
+ String role = null;
+ User sdncUserDetails = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+
+ deleteUserAndAudit(sdncUserDetails);
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, createUserResponse.getResponse());
+ UserValidationUtils.validateAddUserAuditMessage(sdncUserDetails, sdncAdminUser, "201",
+ UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, getUserResponse.getResponse());
+
+ }
+
+ // create user with one optional parameter first name (UserID, Email, First
+ // Name, Creator details)
+ // expected: role = DESIGNER, last name = null, 201 Created
+ @Test
+ public void createUserFirstName() throws Exception {
+ // user initialization
+ String httpCspUserId = "km2000";
+ String userFirstName = "Kot";
+ String userLastName = null;
+ String email = null;
+ String role = null;
+ User sdncUserDetails = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+
+ deleteUserAndAudit(sdncUserDetails);
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, createUserResponse.getResponse());
+ UserValidationUtils.validateAddUserAuditMessage(sdncUserDetails, sdncAdminUser, "201",
+ UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, getUserResponse.getResponse());
+ }
+
+ @Test
+ public void createDeleteOpsUser() throws Exception {
+
+ String httpCspUserId = "oo2000";
+ String userFirstName = "ops";
+ String userLastName = "opsLast";
+ String email = "ops@intl.sdc.com";
+ String role = "OPS";
+ User sdncUserDetails = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+
+ deleteUserAndAudit(sdncUserDetails);
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, createUserResponse.getResponse());
+ UserValidationUtils.validateAddUserAuditMessage(sdncUserDetails, sdncAdminUser, "201",
+ UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, getUserResponse.getResponse());
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ RestResponse getDeletedUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ assertEquals("Check response code after delete user", 404, getDeletedUserResponse.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createDeleteGOVERNORUser() throws Exception {
+
+ String httpCspUserId = "gg2000";
+ String userFirstName = "gov";
+ String userLastName = "govLast";
+ String email = "gov@intl.sdc.com";
+ String role = "GOVERNOR";
+ User sdncUserDetails = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+
+ deleteUserAndAudit(sdncUserDetails);
+ RestResponse createUserResponse = UserRestUtils.createUser(sdncUserDetails, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, createUserResponse.getResponse());
+ UserValidationUtils.validateAddUserAuditMessage(sdncUserDetails, sdncAdminUser, "201",
+ UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ RestResponse getUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(sdncUserDetails, getUserResponse.getResponse());
+
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ RestResponse getDeletedUserResponse = UserRestUtils.getUser(sdncUserDetails, sdncAdminUser);
+ assertEquals("Check response code after delete user", 404, getDeletedUserResponse.getErrorCode().intValue());
+
+ }
+
+ // Benny
+ // Admin Create OPS user
+ @Test
+ public void createOpsUser() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "aa1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ User expectedOpsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedOpsUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedOpsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedOpsUser, createUserResponse.getResponse());
+ deleteAndCheckUserResponse(expectedOpsUser, 200);
+
+ }
+
+ // Admin Create GOVERNOR user
+ @Test
+ public void createGovernorUser() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "aa1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ User expectedUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedUser, createUserResponse.getResponse());
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedUser, getUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(expectedUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from OPS to GOVERNOR
+ @Test
+ public void updateOpsUserRole() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ab1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "GOVERNOR";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(governerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(governerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Designer Create OPS user -409 Response Restricted operation
+ @Test
+ public void createOpsUserByDesigner() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "aa1122";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ User expectedOpsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedOpsUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedOpsUser, sdncDesignerUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 409, createUserResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create user", "Conflict", createUserResponse.getResponseMessage());
+ }
+
+ // Tester Create OPS user -409 Response Restricted operation
+ @Test
+ public void createOpsUserByTester() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "aa1122";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ User expectedOpsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedOpsUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedOpsUser, sdncTesterUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 409, createUserResponse.getErrorCode().intValue());
+ assertEquals("Check response code after create user", "Conflict", createUserResponse.getResponseMessage());
+ }
+
+ // Designer Try Update OPS user role to GOVERNOR - Response 409
+ @Test
+ public void updateOpsUserRolebyDesigner() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "bt751e";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "GOVERNOR";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // Admin create user with OPS role
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // Designer user try to update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncDesignerUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 409, updateUserRoleResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating user", "Conflict",
+ updateUserRoleResponse.getResponseMessage());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Tester Try Update OPS user role to GOVERNOR - Response 409
+ @Test
+ public void updateOpsUserRolebyTester() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "bt751w";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "GOVERNOR";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // Admin create user with OPS role
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // Designer user try to update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncTesterUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 409, updateUserRoleResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating user", "Conflict",
+ updateUserRoleResponse.getResponseMessage());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from OPS to Designer
+ @Test
+ public void updateOpsUserRoleFromOpsToDesigner() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ab1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "DESIGNER";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User designerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(designerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(designerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from OPS to TESTER
+ @Test
+ public void updateOpsUserRoleFromOpsToTester() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ac1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "TESTER";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User testerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(testerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(testerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Tester try to Update user role from OPS to GOVERNOR - Response 409
+ // Conflict
+ @Test
+ public void updateOpsUserRoleByTester() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ad1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "GOVERNOR";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ // Create user by Admin
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR by Tester
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncTesterUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 409, updateUserRoleResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating user", "Conflict",
+ updateUserRoleResponse.getResponseMessage());
+
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Designer try to Update user role from OPS to GOVERNOR - Response 409
+ // Conflict
+ @Test
+ public void updateOpsUserRoleByDesigner() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ad1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ String updatedRole = "GOVERNOR";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ // User governerUser = new User(userFirstName,
+ // userLastName,httpCspUserId, email, updatedRole);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ // Create user by Admin
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR by Tester
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncDesignerUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 409, updateUserRoleResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating user", "Conflict",
+ updateUserRoleResponse.getResponseMessage());
+
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Create OPS user - user already exist
+ @Test
+ public void createOpsUserAlreadyExist() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "af1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "OPS";
+ User expectedOpsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedOpsUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedOpsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedOpsUser, createUserResponse.getResponse());
+ // Create user that already exists
+ RestResponse createUserAgainResponse = UserRestUtils.createUser(expectedOpsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserAgainResponse);
+ assertNotNull("check error code exists in response after create user", createUserAgainResponse.getErrorCode());
+ assertEquals("Check response code after create user", 409, createUserAgainResponse.getErrorCode().intValue());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(expectedOpsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from OPS to GOVERNOR - user already has GOVERNOR
+ // role
+ @Test
+ public void updateRoleToSameRole() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ag1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "GOVERNOR";
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ RestResponse createUserResponse = UserRestUtils.createUser(opsUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, createUserResponse.getResponse());
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from GOVERNOR to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ opsUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(governerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(governerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from Tester to GOVERNOR - 200 response
+
+ // Admin Update user role from Designer to GOVERNOR - 200 response
+ @Test
+ public void updateUserRoleDesignerToGovernor() throws Exception {
+ DbUtils.cleanAllAudits();
+ String httpCspUserId = "ah1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "DESIGNER";
+ String updatedRole = "GOVERNOR";
+ User designerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(designerUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(designerUser, createUserResponse.getResponse());
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from TESTER to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ designerUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+ // Update user role
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(governerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(governerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(designerUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update deactivated user role - response 404 (user not found)
+ @Test
+ public void updateRoleToDeactivatedUser() throws Exception {
+ DbUtils.cleanAllAudits();
+ String httpCspUserId = "aj1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "DESIGNER";
+ String updatedRole = "GOVERNOR";
+ User designerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User governerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(designerUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(designerUser, createUserResponse.getResponse());
+ deleteAndCheckUserResponse(designerUser, 200);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role - user deActivted
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ designerUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after create user", 404, updateUserRoleResponse.getErrorCode().intValue());
+ }
+
+ // Admin Update user role, user does not exist in DB - response 404 (user
+ // not found)
+ @Test
+ public void updateRoleForNonExistingUser() throws Exception {
+ DbUtils.cleanAllAudits();
+ String httpCspUserId = "aj1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "DESIGNER";
+ String updatedRole = "GOVERNOR";
+ User designerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ // User governerUser = new User(userFirstName,
+ // userLastName,httpCspUserId, email, updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role - user deActivted
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ designerUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 404, updateUserRoleResponse.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from GOVERNOR to TESTER
+ @Test
+ public void updateRoleFromGovernorToTester() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ak1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "TESTER";
+ User governorUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User testerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(governorUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(governorUser, createUserResponse.getResponse());
+
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ governorUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(testerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(testerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(governorUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from GOVERNOR to DESIGNER
+ @Test
+ public void updateRoleFromGovernorToDesigner() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ak1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "DESIGNER";
+ User governorUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User designerUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(governorUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(governorUser, createUserResponse.getResponse());
+
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ governorUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(designerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(designerUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(governorUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role from GOVERNOR to OPS
+ @Test
+ public void updateRoleFromGovernorToOps() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ak1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "OPS";
+ User governorUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User opsUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(governorUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(governorUser, createUserResponse.getResponse());
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ governorUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(opsUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(opsUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ deleteAndCheckUserResponse(governorUser, 200);
+
+ }
+
+ private void deleteAndCheckUserResponse(User userDetailes, int expectedResponseCode) throws IOException {
+ RestResponse deleteUser = UserRestUtils.deleteUser(sdncGovernorUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteUser);
+ assertEquals("Check response code after deleting user", expectedResponseCode,
+ deleteUser.getErrorCode().intValue());
+ }
+
+ // Admin Update user role from GOVERNOR to ADMIN
+ @Test
+ public void updateRoleFromGovernorToAdmin() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ak1000";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "ADMIN";
+ User governorUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User adminUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(governorUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(governorUser, createUserResponse.getResponse());
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ governorUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 200, updateUserRoleResponse.getErrorCode().intValue());
+
+ RestResponse getUpdatedRoleUserResponse = UserRestUtils.getUser(adminUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(adminUser, getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(governorUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role to non existing role - Response 400 Bad Request
+ @Test
+ public void updateRoleToNonExistingRole() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "al1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "VVVVVVV";
+ User governorUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User newUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(governorUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(governorUser, createUserResponse.getResponse());
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role from OPS to GOVERNOR
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ governorUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 400, updateUserRoleResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating user", "Bad Request",
+ updateUserRoleResponse.getResponseMessage());
+
+ // RestResponse getUpdatedRoleUserResponse =
+ // UserRestUtils.getUser(adminUser,sdncAdminUser);
+ // UserValidationUtils.validateUserDetailsOnResponse(adminUser,getUpdatedRoleUserResponse.getResponse());
+ // Delete OPS user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(governorUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ // Admin Update user role to null - Response 400 Bad Request
+ @Test
+ public void updateRoleToNull() throws Exception {
+ DbUtils.cleanAllAudits();
+
+ String httpCspUserId = "ax1001";
+ String userFirstName = "Benny";
+ String userLastName = "Tal";
+ String email = "optBenny@intl.sdc.com";
+ String role = "GOVERNOR";
+ String updatedRole = "";
+ User governorUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ User newUser = new User(userFirstName, userLastName, httpCspUserId, email, updatedRole, null);
+ // UserRestUtils.deleteUser(opsUser, sdncAdminUser, true);
+ // UserRestUtils.deleteUser(UserUpdateRole, sdncAdminUser);
+ RestResponse createUserResponse = UserRestUtils.createUser(governorUser, sdncAdminUser);
+ assertNotNull("check response object is not null after create user", createUserResponse);
+ assertNotNull("check error code exists in response after create user", createUserResponse.getErrorCode());
+ assertEquals("Check response code after create user", 201, createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(governorUser, createUserResponse.getResponse());
+ // opsUser.setRole(updatedRole);
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // update user role
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ governorUser.getUserId());
+ assertNotNull("check response object is not null after updating user", updateUserRoleResponse);
+ assertNotNull("check error code exists in response after updating user", updateUserRoleResponse.getErrorCode());
+ assertEquals("Check response code after updating user", 400, updateUserRoleResponse.getErrorCode().intValue());
+ assertEquals("Check response code after updating user", "Bad Request",
+ updateUserRoleResponse.getResponseMessage());
+ // Delete user
+ RestResponse deleteOpsUser = UserRestUtils.deleteUser(governorUser, sdncAdminUser, true);
+ assertNotNull("check response object is not null after deleting user", deleteOpsUser);
+ assertEquals("Check response code after deleting user", 200, deleteOpsUser.getErrorCode().intValue());
+
+ }
+
+ @Test
+ public void createProductManagerUser() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Man";
+ String email = "prodMan@intl.sdc.com";
+ String role = "PRODUCT_MANAGER";
+ User expectedProductManagerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ UserRestUtils.deleteUser(expectedProductManagerUser, sdncAdminUser, true);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, createUserResponse.getResponse());
+ // Audit validation
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductManagerUser, sdncAdminUser,
+ Integer.toString(STATUS_CODE_SUCSESS_CREATED), UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductManagerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, getUserResponse.getResponse());
+ // Delete ProductManager user
+ RestResponse deleteProductManagerUser = UserRestUtils.deleteUser(expectedProductManagerUser, sdncAdminUser,
+ true);
+ assertEquals("Check response code after deleting OPS user", STATUS_CODE_SUCCESS,
+ deleteProductManagerUser.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductStrategistUser() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodStr@intl.sdc.com";
+ String role = "PRODUCT_STRATEGIST";
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ UserRestUtils.deleteUser(expectedProductStrategistUser, sdncAdminUser, true);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductStrategistUser,
+ createUserResponse.getResponse());
+ // Audit validation
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, sdncAdminUser,
+ Integer.toString(STATUS_CODE_SUCSESS_CREATED), UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductStrategistUser, getUserResponse.getResponse());
+ // Delete ProductStrategist user
+ RestResponse deleteProductStrategistUser = UserRestUtils.deleteUser(expectedProductStrategistUser,
+ sdncAdminUser, true);
+ assertNotNull("Check response object is not null after deleting OPS user", deleteProductStrategistUser);
+ assertEquals("Check response code after deleting OPS user", 200,
+ deleteProductStrategistUser.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductStrategistUserByNonAdminUser() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodStr@intl.sdc.com";
+ String role = "PRODUCT_STRATEGIST";
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ UserRestUtils.deleteUser(expectedProductStrategistUser, sdncAdminUser, true);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, sdncDesignerUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_RESTRICTED_OPERATION,
+ createUserResponse.getErrorCode().intValue());
+ // Audit validation
+ expectedProductStrategistUser.setUserId("");
+ expectedProductStrategistUser.setFirstName(null);
+ expectedProductStrategistUser.setLastName(null);
+ expectedProductStrategistUser.setEmail("");
+ expectedProductStrategistUser.setRole("");
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, sdncDesignerUser,
+ Integer.toString(STATUS_CODE_RESTRICTED_OPERATION), errorInfo.getAuditDesc(""),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // Try to get user - user is not created
+ expectedProductStrategistUser.setUserId("pm1000");
+ expectedProductStrategistUser.setFirstName("Prod");
+ expectedProductStrategistUser.setLastName("Strategist");
+ expectedProductStrategistUser.setEmail("prodStr@intl.sdc.com");
+ expectedProductStrategistUser.setRole("PRODUCT_STRATEGIST");
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND, getUserResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductManagerUserByNonAdminUser() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Man";
+ String email = "prodStr@intl.sdc.com";
+ String role = "PRODUCT_MANAGER";
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ UserRestUtils.deleteUser(expectedProductStrategistUser, sdncAdminUser, true);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, sdncDesignerUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_RESTRICTED_OPERATION,
+ createUserResponse.getErrorCode().intValue());
+ // Audit validation
+ expectedProductStrategistUser.setUserId("");
+ expectedProductStrategistUser.setFirstName(null);
+ expectedProductStrategistUser.setLastName(null);
+ expectedProductStrategistUser.setEmail("");
+ expectedProductStrategistUser.setRole("");
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, sdncDesignerUser,
+ Integer.toString(STATUS_CODE_RESTRICTED_OPERATION), errorInfo.getAuditDesc(""),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // Try to get user - user is not created
+ expectedProductStrategistUser.setUserId("pm1000");
+ expectedProductStrategistUser.setFirstName("Prod");
+ expectedProductStrategistUser.setLastName("Strategist");
+ expectedProductStrategistUser.setEmail("prodStr@intl.sdc.com");
+ expectedProductStrategistUser.setRole("PRODUCT_MANAGER");
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND, getUserResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductStrategistUserByNonExistingUser() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodStr@intl.sdc.com";
+ String role = "PRODUCT_STRATEGIST";
+ User noSdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ noSdncUserDetails.setRole("blabla");
+ noSdncUserDetails.setUserId("bt750h");
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, noSdncUserDetails);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_NOT_FOUND,
+ createUserResponse.getErrorCode().intValue());
+ // Audit validation
+ expectedProductStrategistUser.setUserId("");
+ expectedProductStrategistUser.setFirstName(null);
+ expectedProductStrategistUser.setLastName(null);
+ expectedProductStrategistUser.setEmail("");
+ expectedProductStrategistUser.setRole("");
+ noSdncUserDetails.setFirstName("");
+ noSdncUserDetails.setLastName("");
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_NOT_FOUND.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, noSdncUserDetails,
+ Integer.toString(STATUS_CODE_NOT_FOUND), errorInfo.getAuditDesc(noSdncUserDetails.getUserId()),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // Try to get user - user is not created
+ expectedProductStrategistUser.setUserId("pm1000");
+ expectedProductStrategistUser.setFirstName("Prod");
+ expectedProductStrategistUser.setLastName("Strategist");
+ expectedProductStrategistUser.setEmail("prodStr@intl.sdc.com");
+ expectedProductStrategistUser.setRole("PRODUCT_STRATEGIST");
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND, getUserResponse.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductManagerUserByNonExistingUser() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Man";
+ String email = "prodStr@intl.sdc.com";
+ String role = "PRODUCT_MANAGER";
+ User noSdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ noSdncUserDetails.setRole("blabla");
+ noSdncUserDetails.setUserId("bt750h");
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+
+ DbUtils.deleteFromEsDbByPattern("_all");
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, noSdncUserDetails);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_NOT_FOUND,
+ createUserResponse.getErrorCode().intValue());
+ // Audit validation
+ expectedProductStrategistUser.setUserId("");
+ expectedProductStrategistUser.setFirstName(null);
+ expectedProductStrategistUser.setLastName(null);
+ expectedProductStrategistUser.setEmail("");
+ expectedProductStrategistUser.setRole("");
+ noSdncUserDetails.setFirstName("");
+ noSdncUserDetails.setLastName("");
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_NOT_FOUND.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, noSdncUserDetails,
+ Integer.toString(STATUS_CODE_NOT_FOUND), errorInfo.getAuditDesc(noSdncUserDetails.getUserId()),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // Try to get user - user is not created
+ expectedProductStrategistUser.setUserId("pm1000");
+ expectedProductStrategistUser.setFirstName("Prod");
+ expectedProductStrategistUser.setLastName("Strategist");
+ expectedProductStrategistUser.setEmail("prodStr@intl.sdc.com");
+ expectedProductStrategistUser.setRole("PRODUCT_MANAGER");
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code ", STATUS_CODE_NOT_FOUND, getUserResponse.getErrorCode().intValue());
+ }
+
+ @Test(enabled = false)
+ public void updateProjectManagerRole() throws Exception {
+ // Update user role from PRODUCT_STRATEGIST to PRODUCT_MANAGER
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Man";
+ String email = "prodMan@intl.sdc.com";
+ String role = "PRODUCT_MANAGER";
+ String updatedRole = "GOVERNOR";
+ User expectedProductManagerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedProductManagerUser);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, createUserResponse.getResponse());
+ // Update user role
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // Update user role from PRODUCT_STRATEGIST to PRODUCT_MANAGER
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ expectedProductManagerUser.getUserId());
+ assertEquals("Check response code after create user", STATUS_CODE_SUCCESS,
+ updateUserRoleResponse.getErrorCode().intValue());
+ expectedProductManagerUser.setRole(updatedRole);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser,
+ updateUserRoleResponse.getResponse());
+ // Audit validation
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductManagerUser, sdncAdminUser,
+ Integer.toString(STATUS_CODE_SUCCESS), UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("UpdateUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductManagerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, getUserResponse.getResponse());
+ // Delete ProductManager user
+ RestResponse deleteProductManagerUser = UserRestUtils.deleteUser(expectedProductManagerUser, sdncAdminUser,
+ true);
+ assertEquals("Check response code after deleting OPS user", STATUS_CODE_SUCCESS,
+ deleteProductManagerUser.getErrorCode().intValue());
+ }
+
+ @Test(enabled = false)
+ public void updateProductStrategistRole() throws Exception {
+ // Update user role from PRODUCT_STRATEGIST to PRODUCT_MANAGER
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodMan@intl.sdc.com";
+ String role = "PRODUCT_STRATEGIST";
+ String updatedRole = "TESTER";
+ User expectedProductManagerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedProductManagerUser);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, createUserResponse.getResponse());
+ // Update user role
+ User newRoleUser = new User();
+ newRoleUser.setRole(updatedRole);
+ // Update user role from PRODUCT_STRATEGIST to PRODUCT_MANAGER
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ expectedProductManagerUser.getUserId());
+ assertEquals("Check response code after create user", STATUS_CODE_SUCCESS,
+ updateUserRoleResponse.getErrorCode().intValue());
+ expectedProductManagerUser.setRole(updatedRole);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser,
+ updateUserRoleResponse.getResponse());
+ // Audit validation
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductManagerUser, sdncAdminUser,
+ Integer.toString(STATUS_CODE_SUCCESS), UserResponseMessageEnum.SUCCESS_MESSAGE.getValue(),
+ UserValidationUtils.getAddUserAuditMessage("UpdateUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductManagerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, getUserResponse.getResponse());
+ // Delete ProductManager user
+ RestResponse deleteProductManagerUser = UserRestUtils.deleteUser(expectedProductManagerUser, sdncAdminUser,
+ true);
+ assertEquals("Check response code after deleting OPS user", STATUS_CODE_SUCCESS,
+ deleteProductManagerUser.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductManagerUserAlreadyExit() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Man";
+ String email = "prodMan@intl.sdc.com";
+ String role = "PRODUCT_MANAGER";
+ User expectedProductManagerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedProductManagerUser);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, createUserResponse.getResponse());
+ // create same user again
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse createUserAgainResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", USER_ALREADY_EXIST,
+ createUserAgainResponse.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_ALREADY_EXIST.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductManagerUser, sdncAdminUser,
+ Integer.toString(USER_ALREADY_EXIST), errorInfo.getAuditDesc(expectedProductManagerUser.getUserId()),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductManagerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, getUserResponse.getResponse());
+ // Delete ProductManager user
+ RestResponse deleteProductManagerUser = UserRestUtils.deleteUser(expectedProductManagerUser, sdncAdminUser,
+ true);
+ assertEquals("Check response code after deleting OPS user", STATUS_CODE_SUCCESS,
+ deleteProductManagerUser.getErrorCode().intValue());
+ }
+
+ @Test
+ public void createProductStrategistUserAlreadyExit() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodMan@intl.sdc.com";
+ String role = "PRODUCT_STRATEGIST";
+ User expectedProductManagerUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedProductManagerUser);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, createUserResponse.getResponse());
+ // create same user again
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse createUserAgainResponse = UserRestUtils.createUser(expectedProductManagerUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", USER_ALREADY_EXIST,
+ createUserAgainResponse.getErrorCode().intValue());
+ // Audit validation
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.USER_ALREADY_EXIST.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductManagerUser, sdncAdminUser,
+ Integer.toString(USER_ALREADY_EXIST), errorInfo.getAuditDesc(expectedProductManagerUser.getUserId()),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductManagerUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductManagerUser, getUserResponse.getResponse());
+ // Delete ProductManager user
+ RestResponse deleteProductManagerUser = UserRestUtils.deleteUser(expectedProductManagerUser, sdncAdminUser,
+ true);
+ assertEquals("Check response code after deleting OPS user", STATUS_CODE_SUCCESS,
+ deleteProductManagerUser.getErrorCode().intValue());
+ }
+
+ @Test(enabled = false)
+ public void UpdateProductStrategistToNonExistingRole() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodMan@intl.sdc.com";
+ String role = "PRODUCT_STRATEGIST";
+ String nonExistingRole = "BLABLA";
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedProductStrategistUser);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", STATUS_CODE_SUCSESS_CREATED,
+ createUserResponse.getErrorCode().intValue());
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductStrategistUser,
+ createUserResponse.getResponse());
+ // Update user Role to non Existing role
+ User newRoleUser = new User();
+ newRoleUser.setRole(nonExistingRole);
+ DbUtils.deleteFromEsDbByPattern("_all");
+ RestResponse updateUserRoleResponse = UserRestUtils.updateUserRole(newRoleUser, sdncAdminUser,
+ expectedProductStrategistUser.getUserId());
+ assertEquals("Check response code after updating user role", INVALID_ROLE,
+ updateUserRoleResponse.getErrorCode().intValue());
+
+ // Audit validation
+ /*
+ * expectedProductStrategistUser.setUserId("");
+ * expectedProductStrategistUser.setFirstName(null);
+ * expectedProductStrategistUser.setLastName(null);
+ * expectedProductStrategistUser.setEmail("");
+ * expectedProductStrategistUser.setRole("");
+ */
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_ROLE.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, sdncAdminUser,
+ Integer.toString(INVALID_ROLE), errorInfo.getAuditDesc(nonExistingRole),
+ UserValidationUtils.getAddUserAuditMessage("UpdateUser"));
+ // get user and compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ UserValidationUtils.validateUserDetailsOnResponse(expectedProductStrategistUser, getUserResponse.getResponse());
+ // Delete ProductManager user
+ RestResponse deleteProductManagerUser = UserRestUtils.deleteUser(expectedProductStrategistUser, sdncAdminUser,
+ true);
+ assertEquals("Check response code after deleting OPS user", STATUS_CODE_SUCCESS,
+ deleteProductManagerUser.getErrorCode().intValue());
+ }
+
+ @Test(enabled = false)
+ public void createUserWithNonExistingRole() throws Exception {
+ String httpCspUserId = "pm1000";
+ String userFirstName = "Prod";
+ String userLastName = "Strategist";
+ String email = "prodMan@intl.sdc.com";
+ String role = "BLABLA";
+ User expectedProductStrategistUser = new User(userFirstName, userLastName, httpCspUserId, email, role, null);
+ deleteUserAndAudit(expectedProductStrategistUser);
+ // create user
+ RestResponse createUserResponse = UserRestUtils.createUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check response code after create Product-Manager user", INVALID_ROLE,
+ createUserResponse.getErrorCode().intValue());
+
+ // Audit validation
+ /*
+ * expectedProductStrategistUser.setUserId("");
+ * expectedProductStrategistUser.setFirstName(null);
+ * expectedProductStrategistUser.setLastName(null);
+ * expectedProductStrategistUser.setEmail("");
+ * expectedProductStrategistUser.setRole("");
+ */
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_ROLE.name());
+ UserValidationUtils.validateAddUserAuditMessage(expectedProductStrategistUser, sdncAdminUser,
+ Integer.toString(INVALID_ROLE), errorInfo.getAuditDesc(role),
+ UserValidationUtils.getAddUserAuditMessage("AddUser"));
+ // get user - verify user is not createdand compare with expected
+ RestResponse getUserResponse = UserRestUtils.getUser(expectedProductStrategistUser, sdncAdminUser);
+ assertEquals("Check user not created", STATUS_CODE_NOT_FOUND, getUserResponse.getErrorCode().intValue());
+
+ }
+
+ private void deleteUserAndAudit(User sdncUserDetails) throws IOException {
+ UserRestUtils.deleteUser(sdncUserDetails, sdncAdminUser, true);
+ DbUtils.cleanAllAudits();
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/GovernorWorkspaceApiTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/GovernorWorkspaceApiTest.java
new file mode 100644
index 0000000000..a7552ad3be
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/execute/user/GovernorWorkspaceApiTest.java
@@ -0,0 +1,354 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.user;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ServiceValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GovernorWorkspaceApiTest extends ComponentBaseTest {
+
+ private static Logger logger = LoggerFactory.getLogger(GovernorWorkspaceApiTest.class.getName());
+ @Rule
+ public static TestName name = new TestName();
+
+ public GovernorWorkspaceApiTest() {
+ super(name, GovernorWorkspaceApiTest.class.getName());
+
+ }
+
+ protected final User admin1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ protected final User governor = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+ protected final User sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ protected ResourceReqDetails resourceDetails1;
+ protected ComponentInstanceReqDetails componentInstanceReqDetails;
+ protected ArtifactReqDetails heatArtifactDetails;
+
+ protected final String serviceVersion = "0.1";
+ protected final String servicesString = "services";
+ protected final String userRemarks = "commentTest";
+
+ protected ServiceReqDetails serviceDetails11 = null;
+ protected ServiceReqDetails serviceDetails22 = null;
+ protected ServiceReqDetails serviceDetails33 = null;
+
+ @BeforeMethod
+ public void initBeforeTest() throws Exception {
+ DbUtils.deleteFromEsDbByPattern("_all");
+ Resource resourceObj = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetails1 = new ResourceReqDetails(resourceObj);
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ createThreeServices(sdncDesignerDetails1);
+ }
+
+ protected void createThreeServices(User user) throws Exception {
+
+ String checkinComment = "good checkin";
+ String checkinComentJson = "{\"userRemarks\": \"" + checkinComment + "\"}";
+
+ RestResponse addInformationalArtifactToResource = ArtifactRestUtils.addInformationalArtifactToResource(
+ heatArtifactDetails, sdncDesignerDetails1, resourceDetails1.getUniqueId());
+ RestResponse certifyResource = LifecycleRestUtils.certifyResource(resourceDetails1);
+ componentInstanceReqDetails = ElementFactory.getDefaultComponentInstance("defaultInstance", resourceDetails1);
+
+ serviceDetails11 = ElementFactory.getDefaultService();
+ serviceDetails22 = ElementFactory.getDefaultService();
+ serviceDetails33 = ElementFactory.getDefaultService();
+
+ serviceDetails11.setName(serviceDetails11.getName() + "1");
+ List<String> tags = serviceDetails11.getTags();
+ tags.add(serviceDetails11.getName());
+ serviceDetails11.setTags(tags);
+
+ serviceDetails22.setName(serviceDetails11.getName() + "2");
+ tags = serviceDetails22.getTags();
+ tags.add(serviceDetails22.getName());
+ serviceDetails22.setTags(tags);
+
+ serviceDetails33.setName(serviceDetails11.getName() + "3");
+ tags = serviceDetails33.getTags();
+ tags.add(serviceDetails33.getName());
+ serviceDetails33.setTags(tags);
+ //
+ // serviceUtils.deleteService_allVersions(serviceDetails11, designer1);
+ // serviceUtils.deleteService_allVersions(serviceDetails22, designer1);
+ // serviceUtils.deleteService_allVersions(serviceDetails33, designer1);
+
+ RestResponse createServiceResponse1 = createService(user, serviceDetails11);
+ RestResponse createServiceResponse2 = createService(user, serviceDetails22);
+ RestResponse createServiceResponse3 = createService(user, serviceDetails33);
+
+ // addResourceWithHeatArt();
+ //
+ // serviceUtils.addServiceMandatoryArtifacts(user,
+ // createServiceResponse1);
+ //
+ //
+ // RestResponse createServiceResponse2 =
+ // serviceUtils.createServiceTowardsCatalogBe(serviceDetails22, user);
+ // assertNotNull("check response object is not null after creating
+ // service", createServiceResponse2);
+ // assertNotNull("check if error code exists in response after creating
+ // service", createServiceResponse2.getErrorCode());
+ // assertEquals("Check response code after creating service", 201,
+ // createServiceResponse2.getErrorCode().intValue());
+ // serviceDetails22.setUniqueId(serviceUtils.getServiceUniqueId(createServiceResponse2));
+ // logger.debug("Created service2 = {}", serviceDetails22);
+ // serviceUtils.addServiceMandatoryArtifacts(user,
+ // createServiceResponse2);
+ //
+ // RestResponse createServiceResponse3 =
+ // serviceUtils.createServiceTowardsCatalogBe(serviceDetails33, user);
+ // assertNotNull("check response object is not null after creating
+ // service", createServiceResponse3);
+ // assertNotNull("check if error code exists in response after creating
+ // service", createServiceResponse3.getErrorCode());
+ // assertEquals("Check response code after creating service", 201,
+ // createServiceResponse3.getErrorCode().intValue());
+ // serviceDetails33.setUniqueId(serviceUtils.getServiceUniqueId(createServiceResponse3));
+ // logger.debug("Created service3 = {}", serviceDetails33);
+ // serviceUtils.addServiceMandatoryArtifacts(user,
+ // createServiceResponse3);
+
+ }
+
+ protected RestResponse createService(User user, ServiceReqDetails serviceDetails) throws Exception, IOException {
+ RestResponse createServiceResponse1 = ServiceRestUtils.createService(serviceDetails, user);
+ assertNotNull("check response object is not null after creating service", createServiceResponse1);
+ assertNotNull("check if error code exists in response after creating service",
+ createServiceResponse1.getErrorCode());
+ assertEquals("Check response code after creating service", 201,
+ createServiceResponse1.getErrorCode().intValue());
+ Service convertServiceResponseToJavaObject = ResponseParser
+ .convertServiceResponseToJavaObject(createServiceResponse1.getResponse());
+ serviceDetails.setUniqueId(convertServiceResponseToJavaObject.getUniqueId());
+ logger.debug("Created service1 = {}", serviceDetails);
+ addResourceWithHeatArt(serviceDetails);
+ return createServiceResponse1;
+ }
+
+ protected void addResourceWithHeatArt(ServiceReqDetails serviceDetails) throws Exception {
+
+ RestResponse createResourceInstance = ComponentInstanceRestUtils.createComponentInstance(
+ componentInstanceReqDetails, sdncDesignerDetails1, serviceDetails.getUniqueId(),
+ ComponentTypeEnum.SERVICE);
+ // System.out.println("serviceUID --->" + serviceDetails.getUniqueId());
+ assertEquals("Check response code ", 201, createResourceInstance.getErrorCode().intValue());
+ }
+
+ protected void certifyAllServices() throws Exception {
+ LifecycleRestUtils.certifyService(serviceDetails11);
+ LifecycleRestUtils.certifyService(serviceDetails22);
+ LifecycleRestUtils.certifyService(serviceDetails33);
+ }
+
+ protected boolean isElementInArray(String elementId, JSONArray jsonArray) throws Exception {
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JSONObject jobject = (JSONObject) jsonArray.get(i);
+
+ if (jobject.get("uniqueId").toString().equals(elementId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected void approveDistributionStatusOfCertifiedService(ServiceReqDetails serviceDetails, User user)
+ throws Exception {
+ approveDistributionStatusOfService(serviceDetails, user, "1.0");
+ }
+
+ protected void approveDistributionStatusOfService(ServiceReqDetails serviceDetails, User user, String version)
+ throws Exception {
+ RestResponse res = LifecycleRestUtils.sendApproveDistribution(user, serviceDetails.getUniqueId(), userRemarks);
+ assertEquals(200, res.getErrorCode().intValue());
+ ServiceValidationUtils.validateDistrubtionStatusValue(res, DistributionStatusEnum.DISTRIBUTION_APPROVED);
+ }
+
+ protected void rejectDistributionStatusOfService(ServiceReqDetails serviceDetails, User user) throws Exception {
+ rejectDistributionStatusOfService(serviceDetails, user, "1.0");
+ }
+
+ protected void rejectDistributionStatusOfService(ServiceReqDetails serviceDetails, User user, String version)
+ throws Exception {
+ RestResponse res = LifecycleRestUtils.rejectDistribution(serviceDetails, version, user, userRemarks);
+ assertEquals(200, res.getErrorCode().intValue());
+ ServiceValidationUtils.validateDistrubtionStatusValue(res, DistributionStatusEnum.DISTRIBUTION_REJECTED);
+ }
+
+ protected JSONArray getFollowedListAsJsonArray(User user) throws Exception {
+ RestResponse getGovernorFollowed = ServiceRestUtils.getFollowed(user);
+ assertNotNull(getGovernorFollowed);
+ assertNotNull(getGovernorFollowed.getErrorCode());
+ assertEquals(200, getGovernorFollowed.getErrorCode().intValue());
+
+ JSONArray listArrayFromRestResponse = ServiceRestUtils.getListArrayFromRestResponse(getGovernorFollowed);
+
+ return listArrayFromRestResponse;
+ }
+
+ protected void changeDistributionStatusOfAllService(boolean approved, User user) throws Exception {
+ if (approved) {
+ approveDistributionStatusOfCertifiedService(serviceDetails11, user);
+ approveDistributionStatusOfCertifiedService(serviceDetails22, user);
+ approveDistributionStatusOfCertifiedService(serviceDetails33, user);
+ } else {
+ rejectDistributionStatusOfService(serviceDetails11, user);
+ rejectDistributionStatusOfService(serviceDetails22, user);
+ rejectDistributionStatusOfService(serviceDetails33, user);
+ }
+
+ }
+
+ protected JSONArray checkFollowed(User user) throws Exception {
+ JSONArray getFollowedList = getFollowedListAsJsonArray(user);
+ assertFalse(getFollowedList.isEmpty());
+ assertTrue(isElementInArray(serviceDetails11.getUniqueId(), getFollowedList));
+ assertTrue(isElementInArray(serviceDetails22.getUniqueId(), getFollowedList));
+ assertTrue(isElementInArray(serviceDetails33.getUniqueId(), getFollowedList));
+
+ return getFollowedList;
+ }
+
+ // -------------------------------------T E S T
+ // S------------------------------------------------------//
+
+ @Test
+ public void governorList_AllCertifiedVersionsOfService() throws Exception {
+ certifyAllServices();
+ String serviceUniqueIdCertified1 = serviceDetails11.getUniqueId();
+ RestResponse res = LifecycleRestUtils.changeServiceState(serviceDetails11, sdncDesignerDetails1, "1.0",
+ LifeCycleStatesEnum.CHECKOUT);
+ assertEquals(200, res.getErrorCode().intValue());
+
+ JSONArray getFollowedList = getFollowedListAsJsonArray(governor);
+ assertFalse(getFollowedList.isEmpty());
+ assertFalse(isElementInArray(serviceDetails11.getUniqueId(), getFollowedList));
+ assertTrue(isElementInArray(serviceDetails22.getUniqueId(), getFollowedList));
+ assertTrue(isElementInArray(serviceDetails33.getUniqueId(), getFollowedList));
+ assertTrue(isElementInArray(serviceUniqueIdCertified1, getFollowedList));
+ assertEquals(3, getFollowedList.size());
+
+ // certifyService(serviceDetails11, "1.1");
+ LifecycleRestUtils.certifyService(serviceDetails11);
+
+ JSONArray governorFollowedList2 = checkFollowed(governor);
+ assertEquals(4, governorFollowedList2.size());
+ assertTrue(isElementInArray(serviceDetails11.getUniqueId(), governorFollowedList2));
+ assertTrue(isElementInArray(serviceUniqueIdCertified1, governorFollowedList2));
+
+ }
+
+ // -------------------------------------T E S T
+ // S------------------------------------------------------//
+
+ @Test
+ public void governorList_distributionNotApproved() throws Exception {
+ certifyAllServices();
+
+ JSONArray checkFollowed = checkFollowed(governor);
+ assertEquals(3, checkFollowed.size());
+ }
+
+ @Test
+ public void governorGetEmptyListTest_notCertifiedServices() throws Exception {
+ JSONArray governorFollowedList = getFollowedListAsJsonArray(governor);
+
+ assertTrue(governorFollowedList.isEmpty());
+ }
+
+ @Test
+ public void governorList_distributionApproved() throws Exception {
+ certifyAllServices();
+ boolean approved = true;
+ changeDistributionStatusOfAllService(approved, governor);
+
+ JSONArray checkFollowed = checkFollowed(governor);
+ assertEquals(3, checkFollowed.size());
+ }
+
+ @Test(enabled = false)
+ public void governorList_distributed() throws Exception {
+ certifyAllServices();
+
+ LifecycleRestUtils.changeDistributionStatus(serviceDetails11, "1.0", governor, userRemarks,
+ DistributionStatusEnum.DISTRIBUTED);
+ LifecycleRestUtils.changeDistributionStatus(serviceDetails22, "1.0", governor, userRemarks,
+ DistributionStatusEnum.DISTRIBUTED);
+ LifecycleRestUtils.changeDistributionStatus(serviceDetails33, "1.0", governor, userRemarks,
+ DistributionStatusEnum.DISTRIBUTED);
+
+ JSONArray governorFollowedList = getFollowedListAsJsonArray(governor);
+ assertFalse(governorFollowedList.isEmpty());
+ assertTrue(isElementInArray(serviceDetails11.getUniqueId(), governorFollowedList));
+ assertTrue(isElementInArray(serviceDetails22.getUniqueId(), governorFollowedList));
+ assertTrue(isElementInArray(serviceDetails33.getUniqueId(), governorFollowedList));
+ }
+
+ @Test
+ public void governorList_distributionRejected() throws Exception {
+ certifyAllServices();
+ boolean distributionRejected = false;
+ changeDistributionStatusOfAllService(distributionRejected, governor);
+
+ JSONArray checkFollowed = checkFollowed(governor);
+ assertEquals(3, checkFollowed.size());
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/executeOnUGN/distributionClient/ClientConfiguration.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/executeOnUGN/distributionClient/ClientConfiguration.java
new file mode 100644
index 0000000000..c80f6c612d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/executeOnUGN/distributionClient/ClientConfiguration.java
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.executeOnUGN.distributionClient;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClientConfiguration {
+
+ private String asdcAddress;
+ private String user;
+ private String password;
+ private Integer pollingInterval;
+ private Integer pollingTimeout;
+ private List<String> relevantArtifactTypes;
+ private String consumerGroup;
+ private String environmentName;
+ private String consumerID;
+
+ public ClientConfiguration() {
+
+ super();
+
+ this.asdcAddress = "localhost:8443";
+ this.consumerID = "mso-123456";
+ this.consumerGroup = "mso-group";
+ this.environmentName = "PROD";
+ this.password = "password";
+ this.pollingInterval = 20;
+ this.pollingTimeout = 20;
+ this.relevantArtifactTypes = new ArrayList<String>();
+ this.relevantArtifactTypes.add("SHELL");
+ this.user = "mso-user";
+ }
+
+ public String getAsdcAddress() {
+ return asdcAddress;
+ }
+
+ public void setAsdcAddress(String asdcAddress) {
+ this.asdcAddress = asdcAddress;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public int getPollingInterval() {
+ return pollingInterval;
+ }
+
+ public void setPollingInterval(Integer pollingInterval) {
+ this.pollingInterval = pollingInterval;
+ }
+
+ public int getPollingTimeout() {
+ return pollingTimeout;
+ }
+
+ public void setPollingTimeout(Integer pollingTimeout) {
+ this.pollingTimeout = pollingTimeout;
+ }
+
+ public List<String> getRelevantArtifactTypes() {
+ return relevantArtifactTypes;
+ }
+
+ public void setRelevantArtifactTypes(List<String> relevantArtifactTypes) {
+ this.relevantArtifactTypes = relevantArtifactTypes;
+ }
+
+ public String getConsumerGroup() {
+ return consumerGroup;
+ }
+
+ public void setConsumerGroup(String consumerGroup) {
+ this.consumerGroup = consumerGroup;
+ }
+
+ public String getEnvironmentName() {
+ return environmentName;
+ }
+
+ public void setEnvironmentName(String environmentName) {
+ this.environmentName = environmentName;
+ }
+
+ public String getComsumerID() {
+ return consumerID;
+ }
+
+ public void setComsumerID(String comsumerID) {
+ this.consumerID = comsumerID;
+ }
+
+ public ClientConfiguration(String asdcAddress, String user, String password, Integer pollingInterval,
+ Integer pollingTimeout, List<String> relevantArtifactTypes, String consumerGroup, String environmentName,
+ String comsumerID) {
+ super();
+ this.asdcAddress = asdcAddress;
+ this.user = user;
+ this.password = password;
+ this.pollingInterval = pollingInterval;
+ this.pollingTimeout = pollingTimeout;
+ this.relevantArtifactTypes = relevantArtifactTypes;
+ this.consumerGroup = consumerGroup;
+ this.environmentName = environmentName;
+ this.consumerID = comsumerID;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/ComplexResourceBaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/ComplexResourceBaseTest.java
new file mode 100644
index 0000000000..14c7a37101
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/ComplexResourceBaseTest.java
@@ -0,0 +1,177 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.preRequisites;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.annotations.BeforeMethod;
+
+public class ComplexResourceBaseTest extends ComponentBaseTest {
+
+ protected ServiceReqDetails serviceDetails;
+ protected ResourceReqDetails resourceDetailsVFC;
+ protected ResourceReqDetails resourceDetailsVL;
+ protected ResourceReqDetails resourceDetailsVF;
+ protected ResourceReqDetails resourceDetailsCP;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetailsVF;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetailsVFC;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetailsVL;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetailsCP;
+ protected User sdncDesignerDetails1;
+ protected User sdncTesterDeatails1;
+ protected User sdncAdminDetails1;
+ protected ArtifactReqDetails heatArtifactDetails;
+
+ protected ArtifactReqDetails defaultArtifactDetails;
+ protected int maxLength = 50;
+ protected Resource resourceVF = null;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public ComplexResourceBaseTest() {
+ super(name, ComplexResourceBaseTest.class.getName());
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+
+ initializeMembers();
+
+ createComponents();
+
+ }
+
+ public void initializeMembers() throws IOException, Exception {
+
+ serviceDetails = ElementFactory.getDefaultService();
+ resourceDetailsVFC = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VFC, "resourceVFC");
+ resourceDetailsVF = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, "resourceVF3");
+ resourceDetailsVL = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VL, "resourceVL");
+ resourceDetailsCP = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.CP, "resourceCP");
+ sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ sdncAdminDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+
+ }
+
+ protected void createComponents() throws Exception {
+
+ RestResponse response = ServiceRestUtils.createService(serviceDetails, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("service uniqueId is null:", serviceDetails.getUniqueId());
+
+ response = ResourceRestUtils.createResource(resourceDetailsVFC, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetailsVFC.getUniqueId());
+ response = LifecycleRestUtils.changeResourceState(resourceDetailsVFC, sdncDesignerDetails1,
+ resourceDetailsVFC.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LS state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ response = ResourceRestUtils.createResource(resourceDetailsVF, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetailsVF.getUniqueId());
+
+ response = ResourceRestUtils.createResource(resourceDetailsCP, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetailsCP.getUniqueId());
+ response = LifecycleRestUtils.changeResourceState(resourceDetailsCP, sdncDesignerDetails1,
+ resourceDetailsCP.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LS state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ response = ResourceRestUtils.createResource(resourceDetailsVL, sdncDesignerDetails1);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+ assertNotNull("resource uniqueId is null:", resourceDetailsVL.getUniqueId());
+ response = LifecycleRestUtils.changeResourceState(resourceDetailsVL, sdncDesignerDetails1,
+ resourceDetailsVL.getVersion(), LifeCycleStatesEnum.CHECKIN);
+ assertTrue("change LS state to CHECKIN, returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ resourceInstanceReqDetailsVFC = ElementFactory.getDefaultComponentInstance("VFC", resourceDetailsVFC);
+ resourceInstanceReqDetailsVF = ElementFactory.getDefaultComponentInstance("VF", resourceDetailsVF);
+ resourceInstanceReqDetailsVL = ElementFactory.getDefaultComponentInstance("VL", resourceDetailsVL);
+ resourceInstanceReqDetailsCP = ElementFactory.getDefaultComponentInstance("CP", resourceDetailsCP);
+
+ }
+
+ protected void createVFWithCertifiedResourceInstance(ResourceReqDetails resourceDetails,
+ ComponentInstanceReqDetails resourceInstanceReqDetails) throws Exception {
+
+ RestResponse response = LifecycleRestUtils.changeResourceState(resourceDetails, sdncDesignerDetails1,
+ resourceDetails.getVersion(), LifeCycleStatesEnum.CHECKOUT);
+ assertEquals("Check response code after CHECKOUT", 200, response.getErrorCode().intValue());
+
+ // add heat artifact to resource and certify
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1,
+ resourceDetails.getUniqueId());
+ assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ response = LCSbaseTest.certifyResource(resourceDetails, sdncDesignerDetails1);
+ assertEquals("Check response code after CERTIFY request", 200, response.getErrorCode().intValue());
+
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+
+ resourceInstanceReqDetails.setComponentUid(resourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, sdncDesignerDetails1,
+ resourceVF);
+ assertEquals("Check response code after create RI", 201, response.getErrorCode().intValue());
+
+ resourceVF = convertResourceGetResponseToJavaObject(resourceDetailsVF);
+ }
+
+ protected Resource convertResourceGetResponseToJavaObject(ResourceReqDetails resourceDetails) throws IOException {
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails, sdncDesignerDetails1);
+ assertEquals("Check response code after get resource", 200, response.getErrorCode().intValue());
+ return ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/DownloadArtifactBaseTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/DownloadArtifactBaseTest.java
new file mode 100644
index 0000000000..bde68528bf
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/DownloadArtifactBaseTest.java
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.preRequisites;
+
+import java.io.IOException;
+
+import org.apache.log4j.lf5.util.ResourceUtils;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest;
+import org.openecomp.sdc.ci.tests.utils.ArtifactUtils;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+
+public class DownloadArtifactBaseTest extends ComponentBaseTest {
+
+ protected ResourceReqDetails downloadResourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+ protected User sdncUserDetails;
+ protected User sdncDesignerDetails1;
+ protected ArtifactReqDetails heatArtifactDetails;
+
+ protected ArtifactReqDetails defaultArtifactDetails;
+ protected ResourceUtils resourceUtils;
+ protected ArtifactUtils artifactUtils;
+ protected Service service;
+
+ public DownloadArtifactBaseTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+
+ initializeMembers();
+ createComponents();
+
+ }
+
+ public void initializeMembers() throws IOException, Exception {
+ downloadResourceDetails = ElementFactory.getDefaultResource();
+ serviceDetails = ElementFactory.getDefaultService();
+ sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ resourceInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+
+ }
+
+ protected void createComponents() throws Exception {
+
+ RestResponse response = ResourceRestUtils.createResource(downloadResourceDetails, sdncUserDetails);
+ AssertJUnit.assertTrue("create request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 201);
+ AssertJUnit.assertNotNull("resource uniqueId is null:", downloadResourceDetails.getUniqueId());
+
+ ArtifactReqDetails heatArtifactDetails = ElementFactory
+ .getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncUserDetails,
+ downloadResourceDetails.getUniqueId());
+ AssertJUnit.assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ // certified resource
+ response = LCSbaseTest.certifyResource(downloadResourceDetails, sdncDesignerDetails1);
+ AssertJUnit.assertTrue("certify resource request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 200);
+
+ response = ServiceRestUtils.createService(serviceDetails, sdncUserDetails);
+ AssertJUnit.assertTrue("create request returned status:" + response.getErrorCode(),
+ response.getErrorCode() == 201);
+ AssertJUnit.assertNotNull("service uniqueId is null:", serviceDetails.getUniqueId());
+
+ // add resource instance with HEAT deployment artifact to the service
+ resourceInstanceReqDetails.setComponentUid(downloadResourceDetails.getUniqueId());
+ response = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, sdncUserDetails,
+ serviceDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ AssertJUnit.assertTrue("response code is not 201, returned: " + response.getErrorCode(),
+ response.getErrorCode() == 201);
+
+ response = ServiceRestUtils.getService(serviceDetails, sdncUserDetails);
+ AssertJUnit.assertTrue("response code is not 200, returned: " + response.getErrorCode(),
+ response.getErrorCode() == 200);
+ service = ResponseParser.convertServiceResponseToJavaObject(response.getResponse());
+
+ DbUtils.cleanAllAudits();
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/SimpleOneRsrcOneServiceTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/SimpleOneRsrcOneServiceTest.java
new file mode 100644
index 0000000000..add06a587c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/preRequisites/SimpleOneRsrcOneServiceTest.java
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.preRequisites;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+
+import org.apache.log4j.lf5.util.ResourceUtils;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.ArtifactUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.annotations.BeforeMethod;
+
+public abstract class SimpleOneRsrcOneServiceTest extends ComponentBaseTest {
+
+ protected ResourceReqDetails resourceDetails;
+ protected ServiceReqDetails serviceDetails;
+ protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+ protected ArtifactReqDetails heatArtifactDetails1;
+
+ private static final String heatExtension = "yaml";
+ private static final String yangXmlExtension = "xml";
+ private static final String muranoPkgExtension = "zip";
+ private static final String extension = null;
+ private final String folderName = "heatEnv";
+
+ protected User sdncDesignerDetails;
+ protected ArtifactReqDetails defaultArtifactDetails;
+ protected ResourceUtils resourceUtils;
+ protected ArtifactUtils artifactUtils;
+ protected Utils utils;
+
+ private static RestResponse createServiceResponse;
+
+ public SimpleOneRsrcOneServiceTest(TestName testName, String className) {
+ super(testName, className);
+ }
+
+ @BeforeMethod
+ public void before() throws Exception {
+
+ initializeMembers();
+ createComponents();
+
+ }
+
+ public void initializeMembers() throws IOException, Exception {
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ resourceDetails = ElementFactory.getDefaultResource();
+ serviceDetails = ElementFactory.getDefaultService();
+ heatArtifactDetails1 = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+ resourceInstanceReqDetails = ElementFactory.getDefaultComponentInstance("resourceInstanceReqDetails");
+ }
+
+ protected void createComponents() throws Exception {
+
+ RestResponse response = ResourceRestUtils.createResource(resourceDetails, sdncDesignerDetails);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+
+ response = ServiceRestUtils.createService(serviceDetails, sdncDesignerDetails);
+ assertTrue("create request returned status:" + response.getErrorCode(), response.getErrorCode() == 201);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/rules/MyTestWatcher.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/rules/MyTestWatcher.java
new file mode 100644
index 0000000000..aa6131c8ff
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/rules/MyTestWatcher.java
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.rules;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.openecomp.sdc.ci.tests.api.SdcTest;
+
+public class MyTestWatcher extends TestWatcher {
+
+ SdcTest odlTest;
+
+ public MyTestWatcher(SdcTest odlTest) {
+ this.odlTest = odlTest;
+ }
+
+ /**
+ * Invoked when a test succeeds
+ *
+ * @param description
+ */
+ @Override
+ protected void succeeded(Description description) {
+ String testName = description.getMethodName();
+ odlTest.addTestSummary(testName, true);
+
+ }
+
+ /**
+ * Invoked when a test fails
+ *
+ * @param e
+ * @param description
+ */
+ @Override
+ protected void failed(Throwable e, Description description) {
+ String testName = description.getMethodName();
+ odlTest.addTestSummary(testName, false, e);
+ }
+
+ /**
+ * Invoked when a test is about to start
+ *
+ * @param description
+ */
+ @Override
+ protected void starting(Description description) {
+ // System.out.println("protected void starting(Description description)
+ // {");
+ this.odlTest.getLogger().debug("Start running test {}", description.getMethodName());
+ }
+
+ /**
+ * Invoked when a test method finishes (whether passing or failing)
+ *
+ * @param description
+ */
+ @Override
+ protected void finished(Description description) {
+ // System.out.println("protected void finished(Description description)
+ // {");
+ this.odlTest.getLogger().debug("Finish running test {}", description.getMethodName());
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/ExtentReporterNG.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/ExtentReporterNG.java
new file mode 100644
index 0000000000..5119263032
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/ExtentReporterNG.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.run;
+
+import java.io.File;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.IReporter;
+import org.testng.IResultMap;
+import org.testng.ISuite;
+import org.testng.ISuiteResult;
+import org.testng.ITestContext;
+import org.testng.ITestResult;
+import org.testng.xml.XmlSuite;
+
+import com.relevantcodes.extentreports.ExtentReports;
+import com.relevantcodes.extentreports.ExtentTest;
+import com.relevantcodes.extentreports.LogStatus;
+
+public class ExtentReporterNG implements IReporter {
+ private ExtentReports extent;
+
+ @Override
+ public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
+ extent = new ExtentReports(outputDirectory + File.separator + "ExtentReportsTestNG.html", true);
+
+ for (ISuite suite : suites) {
+ Map<String, ISuiteResult> result = suite.getResults();
+
+ for (ISuiteResult r : result.values()) {
+ ITestContext context = r.getTestContext();
+
+ buildTestNodes(context.getPassedTests(), LogStatus.PASS);
+ buildTestNodes(context.getFailedTests(), LogStatus.FAIL);
+ buildTestNodes(context.getSkippedTests(), LogStatus.SKIP);
+ }
+ }
+
+ extent.flush();
+ extent.close();
+ }
+
+ private void buildTestNodes(IResultMap tests, LogStatus status) {
+ ExtentTest test;
+
+ if (tests.size() > 0) {
+ for (ITestResult result : tests.getAllResults()) {
+ test = extent.startTest(result.getMethod().getMethodName());
+
+ test.getTest().setStartedTime(getTime(result.getStartMillis()));
+ test.getTest().setEndedTime(getTime(result.getEndMillis()));
+
+ for (String group : result.getMethod().getGroups())
+ test.assignCategory(group);
+
+ String message = "Test " + status.toString().toLowerCase() + "ed";
+
+ if (result.getThrowable() != null)
+ message = result.getThrowable().getMessage();
+
+ test.log(status, message);
+
+ extent.endTest(test);
+ }
+ }
+ }
+
+ private Date getTime(long millis) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(millis);
+ return calendar.getTime();
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest.java
new file mode 100644
index 0000000000..3f895a3780
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest.java
@@ -0,0 +1,293 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.run;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.testng.TestNG;
+
+public class StartTest {
+
+ public static long timeOfTest = 0;
+
+ public static boolean debug = false;
+
+ public static AtomicBoolean loggerInitialized = new AtomicBoolean(false);
+
+ protected static Logger logger = null;
+
+ public static void main(String[] args) {
+
+ String debugEnabled = System.getProperty("debug");
+ if (debugEnabled != null && debugEnabled.equalsIgnoreCase("true")) {
+ debug = true;
+ }
+ System.out.println("Debug mode is " + (debug ? "enabled" : "disabled"));
+
+ enableLogger();
+
+ Config config = null;
+ try {
+ config = Utils.getConfig();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (config == null) {
+ logger.error("Failed to configuration file of ci tests.");
+ System.exit(1);
+ }
+
+ // need to update
+ // List<String> packagesToRun = config.getPackages();
+ // if (packagesToRun == null || true == packagesToRun.isEmpty()) {
+ // logger.error("No package was configured to be executed.");
+ // System.exit(2);
+ // }
+ // StartTest tests = new StartTest();
+
+ // stop on error logic
+ // boolean stopOnClassFailure = false;
+ // String stopOnClassFailureStr =
+ // System.getProperty("stopOnClassFailure");
+ // if (stopOnClassFailureStr != null &&
+ // stopOnClassFailureStr.equalsIgnoreCase("true")) {
+ // stopOnClassFailure = true;
+ // } else {
+ // Boolean stopOnClassFailureObj = config.isStopOnClassFailure();
+ // if (stopOnClassFailureObj != null) {
+ // stopOnClassFailure = stopOnClassFailureObj.booleanValue();
+ // }
+ // }
+ //
+
+ // tests.start(packagesToRun, stopOnClassFailure);
+
+ // TestListenerAdapter tla = new TestListenerAdapter();
+ //
+ // TestHTMLReporter report = new TestHTMLReporter();
+ //// report.
+
+ TestNG testng = new TestNG();
+
+ List<String> suites = new ArrayList<String>();
+ suites.add("testSuites/" + args[0]);
+ testng.setTestSuites(suites);
+ // testng.addListener(tla);
+ testng.setUseDefaultListeners(true);
+ testng.setOutputDirectory("target/");
+
+ StartTest tests = new StartTest();
+ // testng.setPreserveOrder(true);
+ // testng.setVerbose(2);
+ // testng.setSuiteThreadPoolSize(1);
+ // testng.setThreadCount(1);
+ testng.run();
+
+ }
+
+ public StartTest() {
+ logger = Logger.getLogger(StartTest.class.getName());
+ }
+
+ public static void enableLogger() {
+
+ if (false == loggerInitialized.get()) {
+
+ loggerInitialized.set(true);
+
+ String log4jPropsFile = System.getProperty("log4j.configuration");
+ if (System.getProperty("os.name").contains("Windows")) {
+ String logProps = "src/main/resources/ci/conf/log4j.properties";
+ if (log4jPropsFile == null) {
+ System.setProperty("targetlog", "target/");
+ log4jPropsFile = logProps;
+ }
+
+ }
+ PropertyConfigurator.configureAndWatch(log4jPropsFile);
+
+ }
+ }
+
+ // logger.debug("Going to run test class {}", testClass.getName());
+ // logger.debug("Test class {} finished {}", testClass.getName(), (result.wasSuccessful() ? "OK." : " WITH ERROR."));
+ // logger.debug("class {} failed tests: {}", testClass.getName(), (failuresPerClass * 1.0) / runsPerClass * 100 + " %");
+ // logger.debug("class {} ignored tests: {}", testClass.getName(), (ignoredPerClass * 1.0) / runsPerClass * 100 + " %");
+ private List<Class> getClassesForPackage(String pkgname) {
+
+ List<Class> classes = new ArrayList<Class>();
+
+ // Get a File object for the package
+ File directory = null;
+ String fullPath;
+ String relPath = pkgname.replace('.', '/');
+
+ // System.out.println("ClassDiscovery: Package: " + pkgname +
+ // " becomes Path:" + relPath);
+
+ URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
+
+ // System.out.println("ClassDiscovery: Resource = " + resource);
+ if (resource == null) {
+ throw new RuntimeException("No resource for " + relPath);
+ }
+ fullPath = resource.getFile();
+ // System.out.println("ClassDiscovery: FullPath = " + resource);
+
+ if (debug) {
+ System.out.println("fullPath is " + fullPath);
+ }
+
+ try {
+ directory = new File(resource.toURI());
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(
+ pkgname + " (" + resource
+ + ") does not appear to be a valid URL / URI. Strange, since we got it from the system...",
+ e);
+ } catch (IllegalArgumentException e) {
+ directory = null;
+ }
+ // System.out.println("ClassDiscovery: Directory = " + directory);
+
+ if (directory != null && directory.exists()) {
+
+ // Get the list of the files contained in the package
+ String[] files = directory.list();
+ for (int i = 0; i < files.length; i++) {
+
+ // we are only interested in .class files
+ if (files[i].endsWith(".class") && false == files[i].contains("$")) {
+
+ // removes the .class extension
+ String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
+
+ // System.out.println("ClassDiscovery: className = " +
+ // className);
+
+ if (debug) {
+ System.out.println("ClassDiscovery: className = " + className);
+ }
+
+ try {
+ Class clas = Class.forName(className);
+ boolean isAddToRun = false;
+ Method[] methods = clas.getMethods();
+ for (Method method : methods) {
+ Annotation[] anns = method.getAnnotations();
+ for (Annotation an : anns) {
+ if (an.annotationType().getSimpleName().equalsIgnoreCase("Test")) {
+ isAddToRun = true;
+ break;
+ }
+ }
+ }
+ if (isAddToRun)
+ classes.add(clas);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException loading " + className);
+ }
+ }
+ }
+ } else {
+ try {
+ String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
+
+ if (debug) {
+ System.out.println("jarPath is " + jarPath);
+ }
+
+ JarFile jarFile = new JarFile(jarPath);
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ String entryName = entry.getName();
+ if (entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
+
+ // System.out.println("ClassDiscovery: JarEntry: " +
+ // entryName);
+ String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
+
+ // System.out.println("ClassDiscovery: className = " +
+ // className);
+
+ if (false == className.contains("$")) {
+
+ if (debug) {
+ System.out.println("ClassDiscovery: className = " + className);
+ }
+
+ try {
+ Class clas = Class.forName(className);
+ boolean isAddToRun = false;
+ Method[] methods = clas.getMethods();
+ for (Method method : methods) {
+ Annotation[] anns = method.getAnnotations();
+ for (Annotation an : anns) {
+ if (an.annotationType().getSimpleName().equalsIgnoreCase("Test")) {
+ isAddToRun = true;
+ break;
+ }
+ }
+ }
+ if (isAddToRun)
+ classes.add(clas);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException loading " + className);
+ }
+ }
+ }
+ }
+ jarFile.close();
+
+ } catch (IOException e) {
+ throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e);
+ }
+ }
+ return classes;
+ }
+
+ private void addTableHead(StringBuilder results) {
+ results.append("<tr>");
+ results.append("<th>").append("Unit Test").append("</th>");
+ results.append("<th>").append("Result").append("</th>");
+ results.append("</tr>");
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest2backup.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest2backup.java
new file mode 100644
index 0000000000..bab5afaa08
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/run/StartTest2backup.java
@@ -0,0 +1,410 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.run;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.openecomp.sdc.ci.tests.api.SdcTest;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StartTest2backup {
+
+ private List<Class<? extends SdcTest>> testClasses = new ArrayList<Class<? extends SdcTest>>();
+ public static long timeOfTest = 0;
+
+ public static boolean debug = false;
+
+ public static AtomicBoolean loggerInitialized = new AtomicBoolean(false);
+
+ protected static Logger logger = null;
+
+ public static void main(String[] args) {
+
+ String debugEnabled = System.getProperty("debug");
+ if (debugEnabled != null && debugEnabled.equalsIgnoreCase("true")) {
+ debug = true;
+ }
+ System.out.println("Debug mode is " + (debug ? "enabled" : "disabled"));
+
+ enableLogger();
+
+ Config config = null;
+ try {
+ config = Utils.getConfig();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (config == null) {
+ logger.error("Failed to configuration file of ci tests.");
+ System.exit(1);
+ }
+
+ List<String> packagesToRun = config.getPackages();
+ if (packagesToRun == null || true == packagesToRun.isEmpty()) {
+ logger.error("No package was configured to be executed.");
+ System.exit(2);
+ }
+ StartTest2backup tests = new StartTest2backup();
+
+ boolean stopOnClassFailure = false;
+ String stopOnClassFailureStr = System.getProperty("stopOnClassFailure");
+ if (stopOnClassFailureStr != null && stopOnClassFailureStr.equalsIgnoreCase("true")) {
+ stopOnClassFailure = true;
+ } else {
+ Boolean stopOnClassFailureObj = config.isStopOnClassFailure();
+ if (stopOnClassFailureObj != null) {
+ stopOnClassFailure = stopOnClassFailureObj.booleanValue();
+ }
+ }
+
+ tests.start(packagesToRun, stopOnClassFailure);
+ }
+
+ public StartTest2backup() {
+ logger = LoggerFactory.getLogger(StartTest2backup.class.getName());
+ }
+
+ public static void enableLogger() {
+
+ if (false == loggerInitialized.get()) {
+
+ loggerInitialized.set(true);
+
+ String log4jPropsFile = System.getProperty("log4j.configuration");
+ if (System.getProperty("os.name").contains("Windows")) {
+ String logProps = "src/main/resources/ci/conf/log4j.properties";
+ if (log4jPropsFile == null) {
+ System.setProperty("targetlog", "target/");
+ log4jPropsFile = logProps;
+ }
+
+ }
+ PropertyConfigurator.configureAndWatch(log4jPropsFile);
+
+ }
+ }
+
+ public void start(List<String> packages, boolean exitOnFailure) {
+
+ boolean success = true;
+ StringBuilder results = new StringBuilder();
+ Result result;
+
+ if (packages == null) {
+ return;
+ }
+
+ for (String packageName : packages) {
+ // List<Class> classesForPackage =
+ // getClassesForPackage("org.openecomp.sdc.ci.tests.execute");
+ List<Class> classesForPackage = getClassesForPackage(packageName);
+ if (classesForPackage != null && false == classesForPackage.isEmpty()) {
+ for (Class testUnit : classesForPackage) {
+ testClasses.add(testUnit);
+ }
+ }
+ }
+
+ System.out.println(testClasses);
+
+ // tsetClasses.add(LogValidatorTest.class);
+ // tsetClasses.add(AttNorthboundTest.class);
+
+ results.append(
+ "<Html><head><style>th{background-color: gray;color: white;height: 30px;}td {color: black;height: 30px;}.fail {background-color: #FF5555;width: 100px;text-align: center;}.success {background-color: #00FF00;width: 100px;text-align: center;}.name {width: 200px;background-color: #F0F0F0;}.message {width: 300px;background-color: #F0F0F0;}</style></head><body>");
+
+ Calendar calendar = Calendar.getInstance();
+ timeOfTest = calendar.getTimeInMillis();
+ SimpleDateFormat date_format = new SimpleDateFormat("MMM dd yyyy HH:mm:ss");
+ results.append("<br/><h2> This report generated on " + date_format.format(calendar.getTime()) + "</h2><br/>");
+
+ results.append("<table>");
+ addTableHead(results);
+
+ int size = testClasses.size();
+ int index = 0;
+
+ int totalRunTests = 0;
+ int totalFailureTests = 0;
+ int totalIgnoreTests = 0;
+ int numOfFailureClasses = 0;
+ for (Class<? extends SdcTest> testClass : testClasses) {
+
+ index++;
+
+ StringBuilder builder = new StringBuilder();
+ String str = "***************************************************************************";
+ builder.append(str + "\n");
+ String current = "class " + index + "/" + size + " failure(" + numOfFailureClasses + ") + RUNS("
+ + totalRunTests + ")" + " FAILURES(" + totalFailureTests + ") IGNORED(" + totalIgnoreTests + ")";
+ int interval = ((str.length() - current.length() - 2) / 2);
+ String substring = str.substring(0, interval);
+ builder.append(substring + " " + current + " " + substring + "\n");
+ builder.append(str + "\n");
+
+ System.out.println(builder.toString());
+
+ logger.debug(builder.toString());
+ logger.debug("Going to run test class {}", testClass.getName());
+
+ result = JUnitCore.runClasses(testClass);
+ if (result.wasSuccessful() == false) {
+ numOfFailureClasses++;
+ }
+ logger.debug("Test class {} finished {}", testClass.getName(), (result.wasSuccessful() ? "OK." : " WITH ERROR."));
+ List<Failure> failures = result.getFailures();
+ if (failures != null) {
+ for (Failure failure : failures) {
+ logger.error("Test class " + testClass.getName() + " failure test " + failure.getTestHeader() + "-"
+ + failure.getTrace());
+ }
+ }
+ int runsPerClass = result.getRunCount();
+ int failuresPerClass = result.getFailureCount();
+ int ignoredPerClass = result.getIgnoreCount();
+
+ totalRunTests += runsPerClass;
+ totalFailureTests += failuresPerClass;
+ totalIgnoreTests += ignoredPerClass;
+
+ logger.debug("class {} failed tests: {}", testClass.getName(), (failuresPerClass * 1.0) / runsPerClass * 100 + " %");
+ logger.debug("class {} ignored tests: {}", testClass.getName(), (ignoredPerClass * 1.0) / runsPerClass * 100 + " %");
+
+ // List<Failure> failures = result.getFailures();
+ // if (failures != null) {
+ // for (Failure failure : failures) {
+ // System.err.println("9999999999" + failure.getTestHeader());
+ // }
+ // }
+
+ addUnitTestResult(results, testClass, result);
+ success &= result.wasSuccessful();
+
+ if (numOfFailureClasses > 0) {
+ // if (exitOnFailure) {
+ if (exitOnFailure) {
+ break;
+ }
+ }
+ }
+
+ results.append("</table>");
+ results.append("<br/><h2> Tests Summary: </h2><br/>");
+ results.append("Total Runs : " + totalRunTests + "<br/>");
+ results.append("Total Failure : " + totalFailureTests + "<br/>");
+ results.append("Total: " + totalFailureTests + "/" + totalRunTests + "<br/>");
+ results.append("</html>");
+
+ FileUtils.writeToFile(Config.instance().getOutputFolder() + File.separator + Config.instance().getReportName(),
+ results.toString());
+
+ if (!success) {
+ System.out.println("FAILURE");
+ logger.error("Failure tests : "
+ + ((totalFailureTests + totalIgnoreTests) * 1.0) / (totalRunTests + totalIgnoreTests) + " %");
+ logger.error("Ignored tests : " + (totalIgnoreTests * 1.0) / (totalRunTests + totalIgnoreTests) + " %");
+ System.exit(1);
+ }
+
+ System.out.println("SUCCESS");
+ }
+
+ private List<Class> getClassesForPackage(String pkgname) {
+
+ List<Class> classes = new ArrayList<Class>();
+
+ // Get a File object for the package
+ File directory = null;
+ String fullPath;
+ String relPath = pkgname.replace('.', '/');
+
+ // System.out.println("ClassDiscovery: Package: " + pkgname +
+ // " becomes Path:" + relPath);
+
+ URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
+
+ // System.out.println("ClassDiscovery: Resource = " + resource);
+ if (resource == null) {
+ throw new RuntimeException("No resource for " + relPath);
+ }
+ fullPath = resource.getFile();
+ // System.out.println("ClassDiscovery: FullPath = " + resource);
+
+ if (debug) {
+ System.out.println("fullPath is " + fullPath);
+ }
+
+ try {
+ directory = new File(resource.toURI());
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(
+ pkgname + " (" + resource
+ + ") does not appear to be a valid URL / URI. Strange, since we got it from the system...",
+ e);
+ } catch (IllegalArgumentException e) {
+ directory = null;
+ }
+ // System.out.println("ClassDiscovery: Directory = " + directory);
+
+ if (directory != null && directory.exists()) {
+
+ // Get the list of the files contained in the package
+ String[] files = directory.list();
+ for (int i = 0; i < files.length; i++) {
+
+ // we are only interested in .class files
+ if (files[i].endsWith(".class") && false == files[i].contains("$")) {
+
+ // removes the .class extension
+ String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
+
+ // System.out.println("ClassDiscovery: className = " +
+ // className);
+
+ if (debug) {
+ System.out.println("ClassDiscovery: className = " + className);
+ }
+
+ try {
+ Class clas = Class.forName(className);
+ boolean isAddToRun = false;
+ Method[] methods = clas.getMethods();
+ for (Method method : methods) {
+ Annotation[] anns = method.getAnnotations();
+ for (Annotation an : anns) {
+ if (an.annotationType().getSimpleName().equalsIgnoreCase("Test")) {
+ isAddToRun = true;
+ break;
+ }
+ }
+ }
+ if (isAddToRun)
+ classes.add(clas);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException loading " + className);
+ }
+ }
+ }
+ } else {
+ try {
+ String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
+
+ if (debug) {
+ System.out.println("jarPath is " + jarPath);
+ }
+
+ JarFile jarFile = new JarFile(jarPath);
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ String entryName = entry.getName();
+ if (entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
+
+ // System.out.println("ClassDiscovery: JarEntry: " +
+ // entryName);
+ String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
+
+ // System.out.println("ClassDiscovery: className = " +
+ // className);
+
+ if (false == className.contains("$")) {
+
+ if (debug) {
+ System.out.println("ClassDiscovery: className = " + className);
+ }
+
+ try {
+ Class clas = Class.forName(className);
+ boolean isAddToRun = false;
+ Method[] methods = clas.getMethods();
+ for (Method method : methods) {
+ Annotation[] anns = method.getAnnotations();
+ for (Annotation an : anns) {
+ if (an.annotationType().getSimpleName().equalsIgnoreCase("Test")) {
+ isAddToRun = true;
+ break;
+ }
+ }
+ }
+ if (isAddToRun)
+ classes.add(clas);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException loading " + className);
+ }
+ }
+ }
+ }
+ jarFile.close();
+
+ } catch (IOException e) {
+ throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e);
+ }
+ }
+ return classes;
+ }
+
+ private void addTableHead(StringBuilder results) {
+ results.append("<tr>");
+ results.append("<th>").append("Unit Test").append("</th>");
+ results.append("<th>").append("Result").append("</th>");
+ results.append("</tr>");
+ }
+
+ private void addUnitTestResult(StringBuilder results, Class<? extends SdcTest> testClass,
+ Result unitTestResult) {
+
+ boolean isSuccess = unitTestResult.wasSuccessful();
+
+ String result = (isSuccess) ? "success" : "fail";
+ String fileName = FileUtils.getFileName(testClass.getName());
+ results.append("<tr>");
+ // results.append("<td>").append(FileUtils.getFileName(testClass.getName())).append("</td>");
+ results.append("<td class=\"name\">")
+ .append("<a href=\"" + fileName + timeOfTest + ".html\">" + fileName + "</a>").append("</td>");
+ results.append("<td class=\"" + result + "\">").append(result).append("</td>");
+ results.append("</tr>");
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/CrudE2E.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/CrudE2E.java
new file mode 100644
index 0000000000..00ff48e887
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/CrudE2E.java
@@ -0,0 +1,220 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.sanity;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.AssocType;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import fj.data.Either;
+
+public class CrudE2E extends ComponentBaseTest {
+ private static Logger log = LoggerFactory.getLogger(CrudE2E.class.getName());
+
+ public Component resourceDetailsVFCcomp_01;
+ public Component resourceDetailsVFCsoft_01;
+ public Component resourceDetailsCP_01;
+ public Component resourceDetailsVL_01;
+ public Component resourceDetailsVF_01;
+ public Component resourceDetailsVF_02;
+
+ public ComponentInstance resourceDetailsVFC1compIns1;
+ public ComponentInstance resourceDetailsVFC1softIns1;
+ public ComponentInstance resourceDetailsCP1ins_01;
+ public ComponentInstance resourceDetailsVL1ins_01;
+ public ComponentInstance resourceDetailsVF1ins_01;
+ public Component defaultService1;
+ private List<String> variablesAsList = new ArrayList<String>();
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public CrudE2E() {
+ super(name, CrudE2E.class.getName());
+ }
+
+ @Test
+ public void complexScenario() throws Exception {
+
+ User designer = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+
+ //////// create defaultService1 ///////////////////////
+ Either<Service, RestResponse> createDefaultService1e = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true);
+ defaultService1 = createDefaultService1e.left().value();
+
+ //////// create VFC1 (resourceDetailsVFCcomp_01) DerivedFrom COMPUTE
+ //////// type add all possible informational artifacts and change state
+ //////// to CERTIFY////////
+ Either<Resource, RestResponse> resourceDetailsVFCcompE = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC, NormativeTypesEnum.COMPUTE,ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVFCcomp_01 = resourceDetailsVFCcompE.left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.CHEF, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.PUPPET, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.YANG, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.YANG_XML, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.DG_XML, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.MURANO_PKG, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, resourceDetailsVFCcomp_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCcomp_01, UserRoleEnum.DESIGNER,LifeCycleStatesEnum.CERTIFY, true);
+
+ //////// create VFC2 (resourceDetailsVFCsoft_01) DerivedFrom SOFTWARE
+ //////// type and change state to CERTIFY////////
+ Either<Resource, RestResponse> resourceDetailsVFCsoftE = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VFC, NormativeTypesEnum.SOFTWARE_COMPONENT,ResourceCategoryEnum.GENERIC_DATABASE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVFCsoft_01 = resourceDetailsVFCsoftE.left().value();
+ AtomicOperationUtils.changeComponentState(resourceDetailsVFCsoft_01, UserRoleEnum.DESIGNER,LifeCycleStatesEnum.CERTIFY, true);
+
+ //////// create CP1 (resourceDetailsVFCsoft_01) DerivedFrom PORT type
+ //////// and change state to CHECKIN////////
+ Either<Resource, RestResponse> resourceDetailsCP_01e = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP, NormativeTypesEnum.PORT,ResourceCategoryEnum.GENERIC_DATABASE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsCP_01 = resourceDetailsCP_01e.left().value();
+ AtomicOperationUtils.changeComponentState(resourceDetailsCP_01, UserRoleEnum.DESIGNER,LifeCycleStatesEnum.CHECKIN, true);
+
+ //////// create VL1 (resourceDetailsVFCsoft_01) DerivedFrom NETWORK type
+ //////// and change state to CERTIFY////////
+ Either<Resource, RestResponse> resourceDetailsVL_01e = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VL, NormativeTypesEnum.NETWORK,ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVL_01 = resourceDetailsVL_01e.left().value();
+ AtomicOperationUtils.changeComponentState(resourceDetailsVL_01, UserRoleEnum.DESIGNER,LifeCycleStatesEnum.CERTIFY, true);
+
+ //////// create VF1 (resourceDetailsVFCcomp_01) DerivedFrom COMPUTE type
+ //////// add all possible deployment and informational artifacts
+ //////// //////////
+ Either<Resource, RestResponse> resourceDetailsVF_01e = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.ROOT,ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF_01 = resourceDetailsVF_01e.left().value();
+ ArtifactDefinition heatArtifact = AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT, resourceDetailsVF_01, UserRoleEnum.DESIGNER, true, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_VOL, resourceDetailsVF_01,UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.HEAT_NET, resourceDetailsVF_01,UserRoleEnum.DESIGNER, true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, resourceDetailsVF_01, UserRoleEnum.DESIGNER,true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.CHEF, resourceDetailsVF_01, UserRoleEnum.DESIGNER,false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.PUPPET, resourceDetailsVF_01, UserRoleEnum.DESIGNER,false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.YANG, resourceDetailsVF_01, UserRoleEnum.DESIGNER,false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.YANG_XML, resourceDetailsVF_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.DG_XML, resourceDetailsVF_01, UserRoleEnum.DESIGNER,false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.MURANO_PKG, resourceDetailsVF_01,UserRoleEnum.DESIGNER, false, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.OTHER, resourceDetailsVF_01, UserRoleEnum.DESIGNER,false, true);
+
+ //////// Add VFC1 VFC2 CP and VL to VF container /////////////
+ resourceDetailsVFC1compIns1 = AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCcomp_01, resourceDetailsVF_01,UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetailsVFC1softIns1 = AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVFCsoft_01, resourceDetailsVF_01,UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetailsCP1ins_01 = AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsCP_01,resourceDetailsVF_01, UserRoleEnum.DESIGNER, true).left().value();
+ resourceDetailsVL1ins_01 = AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVL_01,resourceDetailsVF_01, UserRoleEnum.DESIGNER, true).left().value();
+
+ //////// associate cp-vl vl-vfcComp and vfcComp-vfcSoft////////
+ resourceDetailsVF_01 = AtomicOperationUtils.getResourceObject(resourceDetailsVF_01, UserRoleEnum.DESIGNER);
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF_01, resourceDetailsCP1ins_01,resourceDetailsVL1ins_01, AssocType.LINKABLE.getAssocType(), UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF_01, resourceDetailsCP1ins_01,resourceDetailsVFC1compIns1, AssocType.BINDABLE.getAssocType(), UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF_01, resourceDetailsVFC1compIns1,resourceDetailsVFC1softIns1, AssocType.NODE.getAssocType(), UserRoleEnum.DESIGNER, true);
+
+ //////// download all VF1 artifacts////////
+ Collection<ArtifactDefinition> artifacts = resourceDetailsVF_01.getDeploymentArtifacts().values();
+ List<String> collect = artifacts.stream().filter(p -> p.checkEsIdExist() == true).map(p -> p.getUniqueId()).collect(Collectors.toList());
+ artifacts.stream().filter(p -> p.checkEsIdExist() == true).map(p -> p.getUniqueId()).forEach(item -> log.debug(item));
+
+ //////// get all VF1 artifacts////////
+
+ Collection<List<ComponentInstanceProperty>> componentInstancesProperties = resourceDetailsVF_01.getComponentInstancesProperties().values();
+ List<String> collect2 = componentInstancesProperties.stream().filter(p -> p.isEmpty() == false).flatMap(l -> l.stream()).collect(Collectors.toList()).stream().map(p -> p.getUniqueId()).collect(Collectors.toList());
+
+ //////// certify VF1 - failed due to uncertified CP instance ////////
+ RestResponse changeVfStateFailed = LifecycleRestUtils.changeComponentState(resourceDetailsVF_01, designer,LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ Resource resResourceDetailsVF_01 = (Resource) resourceDetailsVF_01;
+ variablesAsList = Arrays.asList(resResourceDetailsVF_01.getResourceType().toString(),resourceDetailsCP_01.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.VALIDATED_RESOURCE_NOT_FOUND.name(), variablesAsList,changeVfStateFailed.getResponse());
+
+ //////// certify resources CP1 ////////
+ resourceDetailsCP_01 = AtomicOperationUtils.changeComponentState(resourceDetailsCP_01, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ //////// replace VF1 instances with new certified instances (CP1
+ //////// replaced) ////////
+ Either<Pair<Component, ComponentInstance>, RestResponse> changeComponentInstanceVersion = AtomicOperationUtils.changeComponentInstanceVersion(resourceDetailsVF_01, resourceDetailsCP1ins_01, resourceDetailsCP_01,UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF_01 = changeComponentInstanceVersion.left().value().getLeft();
+ resourceDetailsCP1ins_01 = changeComponentInstanceVersion.left().value().getRight();
+
+ //////// associate cp-vl and cp-vfc1,////////
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF_01, resourceDetailsCP1ins_01,resourceDetailsVL1ins_01, AssocType.LINKABLE.getAssocType(), UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.associate2ResourceInstances(resourceDetailsVF_01, resourceDetailsCP1ins_01,resourceDetailsVFC1compIns1, AssocType.BINDABLE.getAssocType(), UserRoleEnum.DESIGNER, true);
+
+ /////// change VF1 state to CHECK-IN and add it as instance to service1
+ /////// container
+ resourceDetailsVF_01 = AtomicOperationUtils.changeComponentState(resourceDetailsVF_01, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resourceDetailsVF1ins_01 = AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceDetailsVF_01,defaultService1, UserRoleEnum.DESIGNER, true).left().value();
+
+ //////// distribute service1 - failed due to incorrect LifeCyclestatus
+ //////// ////////
+ RestResponse distributeService = AtomicOperationUtils.distributeService(defaultService1, false);
+ Assert.assertEquals(distributeService, null, "verification failed");
+
+ //////// certify service1 - failed due to uncertified instances ////////
+ designer = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ RestResponse changeServicetStateFailed = LifecycleRestUtils.changeComponentState(defaultService1, designer,LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ variablesAsList = Arrays.asList(defaultService1.getComponentType().toString().toLowerCase(),resourceDetailsVF_01.getName());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.VALIDATED_RESOURCE_NOT_FOUND.name(), variablesAsList,changeServicetStateFailed.getResponse());
+
+ ////// change VF1 state to CERTIFIED
+ resourceDetailsVF_01 = AtomicOperationUtils.changeComponentState(resourceDetailsVF_01, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ //////// replace VF1 instances with new certified instances ////////
+ changeComponentInstanceVersion = AtomicOperationUtils.changeComponentInstanceVersion(defaultService1,resourceDetailsVF1ins_01, resourceDetailsVF_01, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF_01 = changeComponentInstanceVersion.left().value().getLeft();
+ resourceDetailsVFC1compIns1 = changeComponentInstanceVersion.left().value().getRight();
+
+ /////// certify service1 ////////
+ defaultService1 = AtomicOperationUtils.changeComponentState(defaultService1, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ //////// distribute service1 - successfully ////////
+ AtomicOperationUtils.distributeService(defaultService1, true);
+
+ /////// create VF2 ////////
+ Either<Resource, RestResponse> resourceDetailsVF_02e = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.ROOT,ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, UserRoleEnum.DESIGNER, true);
+ resourceDetailsVF_02 = resourceDetailsVF_02e.left().value();
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/MultipleResourceUpdate.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/MultipleResourceUpdate.java
new file mode 100644
index 0000000000..8b8a793bc6
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/sanity/MultipleResourceUpdate.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.sanity;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.AssocType;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.testng.annotations.Test;
+
+public class MultipleResourceUpdate extends ComponentBaseTest {
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public MultipleResourceUpdate() {
+ super(name, MultipleResourceUpdate.class.getName());
+ }
+
+ @Test
+ public void simpleScenario() throws Exception {
+
+ // Creating VF and Resource instances
+ Resource vf = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource cp1 = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.CP, NormativeTypesEnum.PORT,
+ ResourceCategoryEnum.NETWORK_CONNECTIVITY_CON_POINT, UserRoleEnum.DESIGNER, true).left().value();
+ Resource cp2 = AtomicOperationUtils.createResourceByType(ResourceTypeEnum.CP, UserRoleEnum.DESIGNER, true).left().value();
+ Resource vl = AtomicOperationUtils
+ .createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VL, NormativeTypesEnum.NETWORK,
+ ResourceCategoryEnum.NETWORK_CONNECTIVITY_VIRTUAL_LINK, UserRoleEnum.DESIGNER, true).left().value();
+
+ vf.getCreatorUserId();
+
+ // Check In Resources
+ AtomicOperationUtils.changeComponentState(cp1, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ AtomicOperationUtils.changeComponentState(cp2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+ AtomicOperationUtils.changeComponentState(vl, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true);
+
+ // CheckIn all other except VF
+ ComponentInstance instanceCP1 = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp1, vf, UserRoleEnum.DESIGNER, true).left().value();
+ ComponentInstance instanceVL = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(vl, vf, UserRoleEnum.DESIGNER, true).left().value();
+ ComponentInstance instanceCP2 = AtomicOperationUtils
+ .addComponentInstanceToComponentContainer(cp2, vf, UserRoleEnum.DESIGNER, true).left().value();
+
+ vf = (Resource) AtomicOperationUtils.getCompoenntObject(vf, UserRoleEnum.DESIGNER);
+
+ // Create Vertex(Link/Associate 2 Resource Instances on Canvas)
+ AtomicOperationUtils.associate2ResourceInstances(vf, instanceCP1, instanceVL, AssocType.LINKABLE.getAssocType(),
+ UserRoleEnum.DESIGNER, true);
+
+ List<ComponentInstanceReqDetails> componentInstanceReqDetailsList = new ArrayList<>();
+ componentInstanceReqDetailsList.add(new ComponentInstanceReqDetails(instanceCP1));
+ componentInstanceReqDetailsList.add(new ComponentInstanceReqDetails(instanceCP2));
+ componentInstanceReqDetailsList.add(new ComponentInstanceReqDetails(instanceVL));
+
+ ComponentInstanceReqDetails compInstDet = componentInstanceReqDetailsList.get(0);
+ compInstDet.setPosX("150");
+ compInstDet.setPosY("150");
+ compInstDet = componentInstanceReqDetailsList.get(1);
+ compInstDet.setPosX("400");
+ compInstDet.setPosY("150");
+ compInstDet = componentInstanceReqDetailsList.get(2);
+ compInstDet.setPosX("150");
+ compInstDet.setPosY("300");
+
+ RestResponse response = ComponentInstanceRestUtils.updateMultipleComponentInstance(
+ componentInstanceReqDetailsList, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), vf.getUniqueId(),
+ vf.getComponentType());
+ assertTrue("response code is not 200, returned: " + response.getErrorCode(),
+ response.getErrorCode() == ProductRestUtils.STATUS_CODE_SUCCESS);
+
+ compInstDet = componentInstanceReqDetailsList.get(0);
+ compInstDet.setPosX("350");
+ compInstDet.setPosY("350");
+ compInstDet = componentInstanceReqDetailsList.get(1);
+ compInstDet.setPosX("600");
+ compInstDet.setPosY("350");
+ compInstDet = componentInstanceReqDetailsList.get(2);
+ compInstDet.setPosX("350");
+ compInstDet.setPosY("500");
+
+ response = ComponentInstanceRestUtils.updateMultipleComponentInstance(componentInstanceReqDetailsList,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), vf.getUniqueId(), vf.getComponentType());
+ assertTrue("response code is not 200, returned: " + response.getErrorCode(),
+ response.getErrorCode() == ProductRestUtils.STATUS_CODE_SUCCESS);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaCapabilitiesNodeTemplatesDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaCapabilitiesNodeTemplatesDefinition.java
new file mode 100644
index 0000000000..76c0c86680
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaCapabilitiesNodeTemplatesDefinition.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+public class ToscaCapabilitiesNodeTemplatesDefinition {
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaDefinition.java
new file mode 100644
index 0000000000..5ce4b8c618
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaDefinition.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+import java.util.List;
+
+public class ToscaDefinition {
+
+ String toscaDefinitionVersion;
+ List<ToscaImportsDefinition> toscaImports;
+ List<ToscaNodeTypesDefinition> toscaNodeTypes;
+ ToscaTopologyTemplateDefinition toscaTopologyTemplate;
+
+ public ToscaDefinition() {
+ super();
+ }
+
+ public String getToscaDefinitionVersion() {
+ return toscaDefinitionVersion;
+ }
+
+ public void setToscaDefinitionVersion(String toscaDefinitionVersion) {
+ this.toscaDefinitionVersion = toscaDefinitionVersion;
+ }
+
+ public List<ToscaImportsDefinition> getToscaImports() {
+ return toscaImports;
+ }
+
+ public void setToscaImports(List<ToscaImportsDefinition> toscaImports) {
+ this.toscaImports = toscaImports;
+ }
+
+ public List<ToscaNodeTypesDefinition> getToscaNodeTypes() {
+ return toscaNodeTypes;
+ }
+
+ public void setToscaNodeTypes(List<ToscaNodeTypesDefinition> toscaNodeTypes) {
+ this.toscaNodeTypes = toscaNodeTypes;
+ }
+
+ public ToscaTopologyTemplateDefinition getToscaTopologyTemplate() {
+ return toscaTopologyTemplate;
+ }
+
+ public void setToscaTopologyTemplate(ToscaTopologyTemplateDefinition toscaTopologyTemplate) {
+ this.toscaTopologyTemplate = toscaTopologyTemplate;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaDefinition [toscaDefinitionVersion=" + toscaDefinitionVersion + ", toscaImports=" + toscaImports
+ + ", toscaNodeTypes=" + toscaNodeTypes + ", toscaTopologyTemplate=" + toscaTopologyTemplate + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaGroupsTopologyTemplateDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaGroupsTopologyTemplateDefinition.java
new file mode 100644
index 0000000000..e19fcb3aba
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaGroupsTopologyTemplateDefinition.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+import java.util.List;
+
+// spec page 102
+public class ToscaGroupsTopologyTemplateDefinition {
+
+ String type; // required
+ String description;
+ // List<ToscaGroupsProperiesDefinition> toscaGroupsProperiesDefinition;
+ List<String> targets; // required
+ // List<ToscaGroupsInterfacesDefinition> toscaGroupsInterfacesDefinition;
+ List<String> members;
+
+ public ToscaGroupsTopologyTemplateDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public List<String> getTargets() {
+ return targets;
+ }
+
+ public void setTargets(List<String> targets) {
+ this.targets = targets;
+ }
+
+ public List<String> getMembers() {
+ return members;
+ }
+
+ public void setMembers(List<String> members) {
+ this.members = members;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaGroupsTopologyTemplateDefinition [type=" + type + ", description=" + description + ", targets="
+ + targets + ", members=" + members + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaImportsDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaImportsDefinition.java
new file mode 100644
index 0000000000..5363223c38
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaImportsDefinition.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.GroupHeatMetaDefinition;
+
+public class ToscaImportsDefinition {
+
+ String typeName;
+
+ List<GroupHeatMetaDefinition> groupHeatMetaDefinition;
+
+ public ToscaImportsDefinition() {
+ super();
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ public void setTypeName(String typeName) {
+ this.typeName = typeName;
+ }
+
+ public List<GroupHeatMetaDefinition> getGroupHeatMetaDefinition() {
+ return groupHeatMetaDefinition;
+ }
+
+ public void setGroupHeatMetaDefinition(List<GroupHeatMetaDefinition> groupHeatMetaDefinition) {
+ this.groupHeatMetaDefinition = groupHeatMetaDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeHeatMetaDefinition [typeName=" + typeName + ", groupHeatMetaDefinition=" + groupHeatMetaDefinition
+ + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaInputsTopologyTemplateDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaInputsTopologyTemplateDefinition.java
new file mode 100644
index 0000000000..62f859c567
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaInputsTopologyTemplateDefinition.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+public class ToscaInputsTopologyTemplateDefinition {
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTemplatesTopologyTemplateDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTemplatesTopologyTemplateDefinition.java
new file mode 100644
index 0000000000..8970467e9c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTemplatesTopologyTemplateDefinition.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+import java.util.List;
+
+public class ToscaNodeTemplatesTopologyTemplateDefinition {
+
+ String name;
+ String type;
+ List<ToscaPropertiesNodeTemplatesDefinition> properties;
+ List<ToscaRequirementsNodeTemplatesDefinition> requirements;
+ List<ToscaCapabilitiesNodeTemplatesDefinition> capabilities;
+
+ public ToscaNodeTemplatesTopologyTemplateDefinition() {
+ super();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List<ToscaPropertiesNodeTemplatesDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<ToscaPropertiesNodeTemplatesDefinition> properties) {
+ this.properties = properties;
+ }
+
+ public List<ToscaRequirementsNodeTemplatesDefinition> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(List<ToscaRequirementsNodeTemplatesDefinition> requirements) {
+ this.requirements = requirements;
+ }
+
+ public List<ToscaCapabilitiesNodeTemplatesDefinition> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(List<ToscaCapabilitiesNodeTemplatesDefinition> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaNodeTemplatesTopologyTemplateDefinition [name=" + name + ", type=" + type + ", properties="
+ + properties + ", requirements=" + requirements + ", capabilities=" + capabilities + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTypesDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTypesDefinition.java
new file mode 100644
index 0000000000..148e99c58c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaNodeTypesDefinition.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+// spec page 88
+public class ToscaNodeTypesDefinition {
+
+ String name;
+ String derivedFrom;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(String derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public ToscaNodeTypesDefinition() {
+ super();
+ }
+
+ @Override
+ public String toString() {
+ return "CsarNodeTypesDefinition [name=" + name + ", derivedFrom=" + derivedFrom + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaPropertiesNodeTemplatesDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaPropertiesNodeTemplatesDefinition.java
new file mode 100644
index 0000000000..cf0add050d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaPropertiesNodeTemplatesDefinition.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+public class ToscaPropertiesNodeTemplatesDefinition {
+
+ String name;
+ Object value;
+
+ public ToscaPropertiesNodeTemplatesDefinition() {
+ super();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaPropertyNodeTemplatesTopologyTemplateDefinition [name=" + name + ", value=" + value + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaRequirementsNodeTemplatesDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaRequirementsNodeTemplatesDefinition.java
new file mode 100644
index 0000000000..bd9f0f0f5e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaRequirementsNodeTemplatesDefinition.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+public class ToscaRequirementsNodeTemplatesDefinition {
+
+ String name;
+ String capability;
+ String node;
+ String relationship;
+
+ public ToscaRequirementsNodeTemplatesDefinition() {
+ super();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getCapability() {
+ return capability;
+ }
+
+ public void setCapability(String capability) {
+ this.capability = capability;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ public String getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationship(String relationship) {
+ this.relationship = relationship;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaRequirementsDefinition [name=" + name + ", capability=" + capability + ", node=" + node
+ + ", relationship=" + relationship + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaTopologyTemplateDefinition.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaTopologyTemplateDefinition.java
new file mode 100644
index 0000000000..549867a562
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/tosca/datatypes/ToscaTopologyTemplateDefinition.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.tosca.datatypes;
+
+import java.util.List;
+
+// spec page 104
+public class ToscaTopologyTemplateDefinition {
+
+ String description;
+ List<ToscaInputsTopologyTemplateDefinition> toscaInputsTopologyTemplateDefinition;
+ List<ToscaNodeTemplatesTopologyTemplateDefinition> toscaNodeTemplatesTopologyTemplateDefinition;
+ // List<ToscaRelationshipTemplatesTopologyTemplateDefinition>
+ List<ToscaGroupsTopologyTemplateDefinition> toscaGroupsTopologyTemplateDefinition;
+ // List<ToscaPoliciesTopologyTemplateDefinition>
+ // toscaPolociesTopologyTemplateDefinition;
+ // List<ToscaOutputsTopologyTemplateDefinition>
+ // toscaOutputsTopologyTemplateDefinition;
+
+ public ToscaTopologyTemplateDefinition() {
+ super();
+ }
+
+ public List<ToscaInputsTopologyTemplateDefinition> getToscaInputsTopologyTemplateDefinition() {
+ return toscaInputsTopologyTemplateDefinition;
+ }
+
+ public void setToscaInputsTopologyTemplateDefinition(
+ List<ToscaInputsTopologyTemplateDefinition> toscaInputsTopologyTemplateDefinition) {
+ this.toscaInputsTopologyTemplateDefinition = toscaInputsTopologyTemplateDefinition;
+ }
+
+ public List<ToscaNodeTemplatesTopologyTemplateDefinition> getToscaNodeTemplatesTopologyTemplateDefinition() {
+ return toscaNodeTemplatesTopologyTemplateDefinition;
+ }
+
+ public void setToscaNodeTemplatesTopologyTemplateDefinition(
+ List<ToscaNodeTemplatesTopologyTemplateDefinition> toscaNodeTemplatesTopologyTemplateDefinition) {
+ this.toscaNodeTemplatesTopologyTemplateDefinition = toscaNodeTemplatesTopologyTemplateDefinition;
+ }
+
+ public List<ToscaGroupsTopologyTemplateDefinition> getToscaGroupsTopologyTemplateDefinition() {
+ return toscaGroupsTopologyTemplateDefinition;
+ }
+
+ public void setToscaGroupsTopologyTemplateDefinition(
+ List<ToscaGroupsTopologyTemplateDefinition> toscaGroupsTopologyTemplateDefinition) {
+ this.toscaGroupsTopologyTemplateDefinition = toscaGroupsTopologyTemplateDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaTopologyTemplateDefinition [toscaInputsTopologyTemplateDefinition="
+ + toscaInputsTopologyTemplateDefinition + ", toscaNodeTemplatesTopologyTemplateDefinition="
+ + toscaNodeTemplatesTopologyTemplateDefinition + ", toscaGroupsTopologyTemplateDefinition="
+ + toscaGroupsTopologyTemplateDefinition + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/AddUserAuditMessageInfo.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/AddUserAuditMessageInfo.java
new file mode 100644
index 0000000000..9ca8adee53
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/AddUserAuditMessageInfo.java
@@ -0,0 +1,113 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.users;
+
+public class AddUserAuditMessageInfo {
+
+ String TIMESTAMP;
+ String ACTION;
+ // String MODIFIER_NAME;
+ String MODIFIER;
+ // String USER_UID;
+ // String USER_NAME;
+ // String USER_EMAIL;
+ // String USER_ROLE;
+ String USER;
+ String STATUS;
+ String DESC;
+
+ public AddUserAuditMessageInfo(String timestamp, String action, String modifierName, String modifierUid,
+ String user, String status, String desc) {
+ super();
+ this.TIMESTAMP = timestamp;
+ this.ACTION = action;
+ // this.MODIFIER_NAME = modifierName;
+ this.MODIFIER = modifierUid;
+ this.USER = user;
+ this.STATUS = status;
+ this.DESC = desc;
+ }
+
+ public AddUserAuditMessageInfo() {
+ super();
+ }
+
+ public String getTIMESTAMP() {
+ return TIMESTAMP;
+ }
+
+ public void setTIMESTAMP(String tIMESTAMP) {
+ TIMESTAMP = tIMESTAMP;
+ }
+
+ public String getACTION() {
+ return ACTION;
+ }
+
+ public void setACTION(String aCTION) {
+ ACTION = aCTION;
+ }
+
+ // public String getMODIFIER_NAME() {
+ // return MODIFIER_NAME;
+ // }
+ // public void setMODIFIER_NAME(String mODIFIER_NAME) {
+ // MODIFIER_NAME = mODIFIER_NAME;
+ // }
+ public String getMODIFIER() {
+ return MODIFIER;
+ }
+
+ public void setMODIFIER(String mODIFIER_UID) {
+ MODIFIER = mODIFIER_UID;
+ }
+
+ public String getUSER() {
+ return USER;
+ }
+
+ public void setUSER(String uSER) {
+ USER = uSER;
+ }
+
+ public String getSTATUS() {
+ return STATUS;
+ }
+
+ public void setSTATUS(String sTATUS) {
+ STATUS = sTATUS;
+ }
+
+ public String getDESC() {
+ return DESC;
+ }
+
+ public void setDESC(String dESC) {
+ DESC = dESC;
+ }
+
+ @Override
+ public String toString() {
+ return "AddUserAuditMessageInfo [timestamp=" + TIMESTAMP + ", action=" + ACTION + ", modifierUid=" + MODIFIER
+ + ", user=" + USER + ", status=" + STATUS + ", desc=" + DESC + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserAuditJavaObject.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserAuditJavaObject.java
new file mode 100644
index 0000000000..b34d474cf0
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserAuditJavaObject.java
@@ -0,0 +1,133 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.users;
+
+public class UserAuditJavaObject {
+
+ String TIMESTAMP;
+ String ACTION;
+ // String MODIFIER_NAME;
+ String MODIFIER;
+ String USER;
+ // String USER_NAME;
+ // String USER_EMAIL;
+ // String USER_ROLE;
+ String STATUS;
+ String DESC;
+
+ public UserAuditJavaObject(String timestamp, String action, String modifier, String user, String status,
+ String desc) {
+ super();
+ this.TIMESTAMP = timestamp;
+ this.ACTION = action;
+ // this.MODIFIER_NAME = modifierName;
+ this.MODIFIER = modifier;
+ this.USER = user;
+ // this.USER_NAME = userName;
+ // this.USER_EMAIL = userEmail;
+ // this.USER_ROLE = userRole;
+ this.STATUS = status;
+ this.DESC = desc;
+ }
+
+ public UserAuditJavaObject() {
+ super();
+ }
+
+ public String getTIMESTAMP() {
+ return TIMESTAMP;
+ }
+
+ public void setTIMESTAMP(String tIMESTAMP) {
+ TIMESTAMP = tIMESTAMP;
+ }
+
+ public String getACTION() {
+ return ACTION;
+ }
+
+ public void setACTION(String aCTION) {
+ ACTION = aCTION;
+ }
+
+ // public String getMODIFIER_NAME() {
+ // return MODIFIER_NAME;
+ // }
+ // public void setMODIFIER_NAME(String mODIFIER_NAME) {
+ // MODIFIER_NAME = mODIFIER_NAME;
+ // }
+ public String getMODIFIER() {
+ return MODIFIER;
+ }
+
+ public void setMODIFIER(String mODIFIER_UID) {
+ MODIFIER = mODIFIER_UID;
+ }
+
+ public String getUSER() {
+ return USER;
+ }
+
+ public void setUSER(String uSER) {
+ USER = uSER;
+ }
+
+ // public String getUSER_NAME() {
+ // return USER_NAME;
+ // }
+ // public void setUSER_NAME(String uSER_NAME) {
+ // USER_NAME = uSER_NAME;
+ // }
+ // public String getUSER_EMAIL() {
+ // return USER_EMAIL;
+ // }
+ // public void setUSER_EMAIL(String uSER_EMAIL) {
+ // USER_EMAIL = uSER_EMAIL;
+ // }
+ // public String getUSER_ROLE() {
+ // return USER_ROLE;
+ // }
+ // public void setUSER_ROLE(String uSER_ROLE) {
+ // USER_ROLE = uSER_ROLE;
+ // }
+ public String getSTATUS() {
+ return STATUS;
+ }
+
+ public void setSTATUS(String sTATUS) {
+ STATUS = sTATUS;
+ }
+
+ public String getDESC() {
+ return DESC;
+ }
+
+ public void setDESC(String dESC) {
+ DESC = dESC;
+ }
+
+ @Override
+ public String toString() {
+ return "UserAuditJavaObject [timestamp=" + TIMESTAMP + ", action=" + ACTION + ", modifier=" + MODIFIER
+ + ", user=" + USER + ", status=" + STATUS + ", desc=" + DESC + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserHeaderData.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserHeaderData.java
new file mode 100644
index 0000000000..06320bcb5f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserHeaderData.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.users;
+
+public class UserHeaderData {
+ String contentType;
+ String httpCspUserId;
+ String accept;
+
+ public UserHeaderData(String contentType, String httpCspUserId, String accept) {
+ super();
+ this.contentType = contentType;
+ this.httpCspUserId = httpCspUserId;
+ this.accept = accept;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ public String getHttpCspUserId() {
+ return httpCspUserId;
+ }
+
+ public void setHttpCspUserId(String httpCspUserId) {
+ this.httpCspUserId = httpCspUserId;
+ }
+
+ public String getAccept() {
+ return accept;
+ }
+
+ public void setAccept(String accept) {
+ this.accept = accept;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserResponseMessageEnum.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserResponseMessageEnum.java
new file mode 100644
index 0000000000..4db52c2886
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/UserResponseMessageEnum.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.users;
+
+public enum UserResponseMessageEnum {
+
+ SUCCESS_MESSAGE("OK"),
+ MISSING_INFORMATION("Error: Missing information"),
+ METHOD_NOT_ALLOWED("Error: Method not allowed"),
+ RESTRICTED_OPERATION("Error: Restricted operation"),
+ USER_ALREADY_EXISTS("Error: User with %s ID already exists"),
+ INVALID_EMAIL("Error: Invalid Content. Invalid e-mail address %s"),
+ INVALID_ROLE("Error: Invalid Content. Invalid role %s"),
+ INVALID_CONTENT("Error: Invalid content"),
+ USER_NOT_FOUND("Error: User with %s ID is not found"),
+ INTERNAL_SERVER_ERROR("Error: Internal Server Error. Try later again"),
+ ADMINISTARTOR_CAN_BE_DELETED("Error: Administrator can be deleted by other administrator only"),
+ RESTRICTED_ACCESS("Error: Restricted access");
+
+ String value;
+
+ private UserResponseMessageEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/WebSealUserDetails.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/WebSealUserDetails.java
new file mode 100644
index 0000000000..609ebf22b7
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/users/WebSealUserDetails.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.users;
+
+public class WebSealUserDetails {
+ String firstName;
+ String lastName;
+ String userId;
+ String email;
+
+ public WebSealUserDetails() {
+ super();
+ }
+
+ public WebSealUserDetails(String firstName, String lastName, String userId, String emailAddress) {
+ super();
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.userId = userId;
+ this.email = emailAddress;
+
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getEmailAddress() {
+ return email;
+ }
+
+ public void setEmailAddress(String emailAddress) {
+ this.email = emailAddress;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ArtifactUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ArtifactUtils.java
new file mode 100644
index 0000000000..12f8ffe984
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ArtifactUtils.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils;
+
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+public class ArtifactUtils {
+
+ public static ArtifactDefinition convertArtifactReqToDefinition(ArtifactReqDetails artifactReq) {
+ ArtifactDefinition artifact = new ArtifactDefinition();
+ artifact.setArtifactLabel(artifactReq.getArtifactLabel());
+ artifact.setArtifactDisplayName(artifactReq.getArtifactDisplayName());
+ artifact.setArtifactGroupType(ArtifactGroupTypeEnum.findType(artifactReq.getArtifactGroupType()));
+ artifact.setArtifactType(artifactReq.getArtifactType().toUpperCase());
+ artifact.setArtifactName(artifactReq.getArtifactName());
+ artifact.setDescription(artifactReq.getDescription());
+ artifact.setUniqueId(artifactReq.getUniqueId());
+ artifact.setTimeout(artifactReq.getTimeout());
+ artifact.setEsId(artifactReq.getUniqueId());
+
+ return artifact;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/DbUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/DbUtils.java
new file mode 100644
index 0000000000..22295cb451
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/DbUtils.java
@@ -0,0 +1,306 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.users.UserAuditJavaObject;
+import org.openecomp.sdc.ci.tests.utils.cassandra.CassandraUtils;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public class DbUtils {
+
+ private static String titanConfigFilePath;
+ private static TitanGraph titanGraph;
+
+ public static void cleanAllAudits() throws IOException {
+ // utils.deleteFromEsDbByPattern("_all");
+ // deleteFromEsDbByPattern("auditingevents-*");
+ CassandraUtils.truncateAllTables("sdcaudit");
+
+ // List <EsIndexTypeIdToDelete> auditIdArray = new
+ // ArrayList<EsIndexTypeIdToDelete>();
+ // auditIdArray = buildObjectArrayListByTypesIndex("auditingevents*",
+ // "useraccessevent");
+ //
+ // logger.info("Starting to delete all service topologies from ES");
+ // for (int i = 0; i < auditIdArray.size(); i ++){
+ // EsIndexTypeIdToDelete esIndexTypeIdToDelete = auditIdArray.get(i);
+ // utils.deleteFromEsDbByPattern(esIndexTypeIdToDelete.getIndex()+"/"+esIndexTypeIdToDelete.getType()+"/"+esIndexTypeIdToDelete.getId());
+ //
+ // }
+ }
+
+ public static RestResponse deleteFromEsDbByPattern(String patternToDelete) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_SEARCH_DATA_FROM_ES, config.getEsHost(), config.getEsPort(),
+ patternToDelete);
+ HttpRequest httpRequest = new HttpRequest();
+ RestResponse restResponse = httpRequest.httpSendDelete(url, null);
+ restResponse.getErrorCode();
+ // System.out.println("URL to delete" + url);
+ // System.out.println("response code" + restResponse.getErrorCode());
+ cleanAllAudits();
+
+ return restResponse;
+ }
+
+ public static RestResponse getFromEsByPattern(String patternToGet) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_SEARCH_DATA_FROM_ES, config.getEsHost(), config.getEsPort(), patternToGet);
+ HttpRequest httpRequest = new HttpRequest();
+ RestResponse restResponse = httpRequest.httpSendGet(url, null);
+ restResponse.getErrorCode();
+ // System.out.println("URL to get" + url);
+ // System.out.println("response code" + restResponse.getErrorCode());
+
+ return restResponse;
+ }
+
+ public Either<Vertex, Boolean> getVertexByUId(String uid) {
+ TitanGraph titanGraph = getTitanGraph();
+ Either<Vertex, Boolean> result = Either.right(false);
+ // Iterator<Vertex> vertexItr = titanGraph.getVertices().iterator();
+
+ Iterator<TitanVertex> vertexItr = titanGraph.query().vertices().iterator();
+ while (vertexItr.hasNext()) {
+ Vertex vertex = vertexItr.next();
+ // String uidFoundVal = vertex.getProperty("uid");
+ String uidFoundVal = vertex.value("uid");
+ if (uid.equals(uidFoundVal)) {
+ result = Either.left(vertex);
+ }
+ }
+ return result;
+ }
+
+ public static TitanState getCurrentTitanState() {
+ TitanGraph titanGraph = getTitanGraph();
+ List<Vertex> vertices = new ArrayList<>();
+ List<Edge> edges = new ArrayList<>();
+ // Iterator<Edge> edgesItr = titanGraph.getEdges().iterator();
+ Iterator<TitanEdge> edgesItr = titanGraph.query().edges().iterator();
+ // Iterator<Vertex> verticesItr = titanGraph.getVertices().iterator();
+ Iterator<TitanVertex> verticesItr = titanGraph.query().vertices().iterator();
+ while (edgesItr.hasNext()) {
+ edges.add(edgesItr.next());
+ }
+ while (verticesItr.hasNext()) {
+ vertices.add(verticesItr.next());
+ }
+
+ TitanState currState = new TitanState(edges, vertices);
+ return currState;
+
+ }
+
+ //
+ private static TitanGraph getTitanGraph() {
+ if (titanGraph == null) {
+ titanGraph = TitanFactory.open(titanConfigFilePath);
+ }
+ return titanGraph;
+ }
+
+ public void restoreToTitanState(TitanState titanStateToRestoreTo) {
+ List<Vertex> verticesToRemove = new ArrayList<>(), verticesToAdd = new ArrayList<>();
+ List<Edge> edgesToRemove = new ArrayList<>(), edgesToAdd = new ArrayList<>();
+
+ TitanState currentTitanState = getCurrentTitanState();
+
+ List<Edge> joinedEdges = new ArrayList<>();
+ joinedEdges.addAll(titanStateToRestoreTo.edges);
+ joinedEdges.retainAll(currentTitanState.edges);
+
+ List<Vertex> joinedVertices = new ArrayList<>();
+ joinedVertices.addAll(titanStateToRestoreTo.vertices);
+ joinedVertices.retainAll(currentTitanState.vertices);
+
+ edgesToRemove.addAll(currentTitanState.edges);
+ edgesToRemove.removeAll(joinedEdges);
+
+ verticesToRemove.addAll(currentTitanState.vertices);
+ verticesToRemove.removeAll(joinedVertices);
+
+ edgesToAdd.addAll(titanStateToRestoreTo.edges);
+ edgesToAdd.removeAll(joinedEdges);
+
+ verticesToAdd.addAll(titanStateToRestoreTo.vertices);
+ verticesToAdd.removeAll(joinedVertices);
+
+ modifyGraphAccordingToDelta(verticesToRemove, verticesToAdd, edgesToRemove, edgesToAdd);
+
+ }
+
+ private void modifyGraphAccordingToDelta(List<Vertex> verticesToRemove, List<Vertex> verticesToAdd,
+ List<Edge> edgesToRemove, List<Edge> edgesToAdd) {
+
+ TitanGraph titanGraph = getTitanGraph();
+
+ for (Vertex vertex : verticesToRemove) {
+ // titanGraph.removeVertex(vertex);
+ vertex.remove();
+ }
+ for (Vertex vertex : verticesToAdd) {
+ TitanVertex titanVertex = titanGraph.addVertex();
+ copyProperties(vertex, titanVertex);
+ }
+
+ for (Edge edge : edgesToRemove) {
+ // titanGraph.removeEdge(edge);
+ edge.remove();
+ }
+
+ for (Edge edge : edgesToAdd) {
+ // Element addedEdge = titanGraph.addEdge(edge.getId(),
+ // edge.getVertex(Direction.OUT), edge.getVertex(Direction.IN),
+ // edge.getLabel());
+
+ // Edge edge = tGraph.addEdge(null, fromV.left().value(),
+ // toV.left().value(), type);
+
+ Element addedEdge = edge.outVertex().addEdge(edge.label(), edge.inVertex());
+
+ copyProperties(edge, addedEdge);
+
+ }
+
+ // titanGraph.commit();
+ titanGraph.tx().commit();
+
+ }
+
+ private void copyProperties(Element copyFrom, Element copyTo) {
+ // Set<String> properties = copyFrom.getPropertyKeys();
+ Set<String> properties = copyFrom.keys();
+ for (String propertyKey : properties) {
+ // copyTo.setProperty(propertyKey,
+ // copyFrom.getProperty(propertyKey));
+ copyTo.property(propertyKey, copyFrom.value(propertyKey));
+ }
+
+ }
+
+ public static class TitanState {
+ private List<Edge> edges;
+ private List<Vertex> vertices;
+
+ private TitanState(List<Edge> edges, List<Vertex> vertices) {
+ this.edges = edges;
+ this.vertices = vertices;
+ }
+
+ @Override
+ public String toString() {
+ return "TitanState [edges=" + edges.size() + ", vertices=" + vertices.size() + "]";
+ }
+
+ }
+
+ public void shutDowntitan() {
+ if (titanGraph != null) {
+ // titanGraph.shutdown();
+ titanGraph.close();
+ }
+ }
+
+ public static void setProperties(Element element, Map<String, Object> properties) {
+
+ if (properties != null && false == properties.isEmpty()) {
+
+ Object[] propertyKeyValues = new Object[properties.size() * 2];
+ int i = 0;
+ for (Entry<String, Object> entry : properties.entrySet()) {
+ propertyKeyValues[i++] = entry.getKey();
+ propertyKeyValues[i++] = entry.getValue();
+ }
+
+ ElementHelper.attachProperties(element, propertyKeyValues);
+
+ }
+
+ }
+
+ public static UserAuditJavaObject parseAuditRespByAction(String action) throws Exception {
+
+ // String index = "auditingevents*";
+ // String type = "useradminevent";
+ // String pattern = "/_search?q=action:\""+action+"\"";
+ // String auditingMessage = retrieveAuditMessageByIndexType(index, type,
+ // pattern);
+ UserAuditJavaObject auditParsedResp = new UserAuditJavaObject();
+ Gson gson = new Gson();
+
+ String pattern = "/_search?q=ACTION:\"" + action + "\"";
+ String auditingMessage = retrieveAuditMessagesByPattern(pattern);
+ JsonElement jElement = new JsonParser().parse(auditingMessage);
+ JsonObject jObject = jElement.getAsJsonObject();
+ JsonObject hitsObject = (JsonObject) jObject.get("hits");
+ JsonArray hitsArray = (JsonArray) hitsObject.get("hits");
+ // for (int i = 0; i < hitsArray.size();){
+ if (hitsArray.size() == 0) {
+ return auditParsedResp;
+ }
+ JsonObject jHitObject = (JsonObject) hitsArray.get(0);
+ JsonObject jSourceObject = (JsonObject) jHitObject.get("_source");
+
+ auditParsedResp = gson.fromJson(jSourceObject, UserAuditJavaObject.class);
+ // logger.debug("auditParsedResp: {}", auditParsedResp);
+
+ return auditParsedResp;
+
+ }
+
+ public static String retrieveAuditMessagesByPattern(String pattern) throws IOException {
+
+ Config config = Utils.getConfig();
+ HttpRequest getAuditingMessage = new HttpRequest();
+ String url = String.format(Urls.GET_SEARCH_DATA_FROM_ES, config.getEsHost(), config.getEsPort(), pattern);
+ RestResponse restResponse = getAuditingMessage.httpSendGet(url, null);
+
+ return restResponse.getResponse();
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Decoder.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Decoder.java
new file mode 100644
index 0000000000..7d90eb0b6d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Decoder.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.commons.codec.binary.Base64;
+
+public class Decoder {
+
+ public static String encode(byte[] byteArrayToEncode) {
+
+ byte[] bytesEncoded = Base64.encodeBase64(byteArrayToEncode);
+ String strEncoded = new String(bytesEncoded);
+ return strEncoded;
+ }
+
+ public static String decode(String strEncoded) throws IOException {
+
+ byte[] byteDecoded = Base64.decodeBase64(strEncoded);
+ String decoded = new String(byteDecoded);
+
+ return decoded;
+
+ }
+
+ public static String readFileToString(String file) throws IOException {
+
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line = null;
+ StringBuilder stringBuilder = new StringBuilder();
+ String ls = System.getProperty("line.separator");
+
+ while ((line = reader.readLine()) != null) {
+ stringBuilder.append(line);
+ stringBuilder.append(ls);
+ }
+ reader.close();
+ return stringBuilder.toString();
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ReqCap.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ReqCap.java
new file mode 100644
index 0000000000..66d2a8450c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ReqCap.java
@@ -0,0 +1,630 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.testng.Assert;
+
+public class ReqCap {
+
+ public static Map<String, List<CapabilityDefinition>> expectedContainerCapabilities;
+ public static Map<String, List<RequirementDefinition>> expectedContainerRequirements;
+ public static Map<String, RequirementDefinition> removedRequirements;
+ public static Map<String, ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>>> expectedContInstReqCap;
+
+ public static void verifyVFReqCap(String componentId) throws Exception {
+ RestResponse restResponse = ResourceRestUtils.getResource(componentId);
+ Resource resource = ResponseParser.parseToObject(restResponse.getResponse(), Resource.class);
+ verifyReqCap(resource);
+ }
+
+ public static void verifyServiceReqCap(String componentId, User sdncDesignerDetails) throws Exception {
+ RestResponse restResponse = ServiceRestUtils.getService(componentId, sdncDesignerDetails);
+ Service service = ResponseParser.parseToObject(restResponse.getResponse(), Service.class);
+ verifyReqCap(service);
+ }
+
+ public static void verifyProductReqCap(String componentId, User sdncPsDetails1) throws Exception {
+ RestResponse restResponse = ProductRestUtils.getProduct(componentId, sdncPsDetails1.getUserId());
+ Product product = ResponseParser.parseToObject(restResponse.getResponse(), Product.class);
+ verifyReqCap(product);
+ }
+
+ public static void verifyReqCap(Component actualComponent) {
+ verifyContainerReqCap(actualComponent);
+ verifyCompInstReqCap(actualComponent);
+ }
+
+ public RestResponse changeServiceInstanceVersion(String componentUniqueId, String serviceInstanceToReplaceUniqueId,
+ String serviceUniqueId, User sdncModifierDetails, ComponentTypeEnum componentType, boolean isHighestLevel)
+ throws Exception {
+ RestResponse changeResourceInstanceVersion = ProductRestUtils.changeServiceInstanceVersion(componentUniqueId,
+ serviceInstanceToReplaceUniqueId, serviceUniqueId, sdncModifierDetails, componentType);
+ if (changeResourceInstanceVersion.getErrorCode().equals(BaseRestUtils.STATUS_CODE_SUCCESS) && isHighestLevel) {
+ /*
+ * // Add RI Capabilities and Requirements to expected MAP -->
+ * expectedVfCapabilities and expectedVfRequirements
+ *
+ * ComponentInstance componentInstance =
+ * ResponseParser.parseToObjectUsingMapper(
+ * changeResourceInstanceVersion.getResponse(),
+ * ComponentInstance.class);
+ * addCompInstReqCapToExpected(componentInstance, componentType);
+ */
+ }
+ return changeResourceInstanceVersion;
+ }
+
+ public static void updateExpectedReqCapAfterChangeLifecycleState(String oldContainerUniqueIdToReplace,
+ String newContainerUniqueId) {
+
+ // Update of container req/cap
+
+ Set<String> compInstKeysToChange = new HashSet<>();
+
+ for (String expKey : expectedContainerCapabilities.keySet()) {
+ List<CapabilityDefinition> expCapList = expectedContainerCapabilities.get(expKey);
+ for (CapabilityDefinition cap : expCapList) {
+ String ownerId = cap.getOwnerId();
+
+ if (ownerId.contains(oldContainerUniqueIdToReplace)) {
+ compInstKeysToChange.add(ownerId);
+ cap.setOwnerId(cap.getOwnerId().replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+ }
+ }
+ }
+
+ for (String expKey : expectedContainerRequirements.keySet()) {
+ List<RequirementDefinition> expCapList = expectedContainerRequirements.get(expKey);
+ for (RequirementDefinition cap : expCapList) {
+ String ownerId = cap.getOwnerId();
+ if (ownerId.contains(oldContainerUniqueIdToReplace)) {
+ compInstKeysToChange.add(ownerId);
+ cap.setOwnerId(cap.getOwnerId().replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId));
+ }
+ }
+ }
+
+ // Update of internal comp instances req/cap
+ for (String oldKey : compInstKeysToChange) {
+ ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>> immutablePair = expectedContInstReqCap
+ .get(oldKey);
+ if (immutablePair != null) {
+ expectedContInstReqCap.remove(oldKey);
+ String newKey = oldKey.replaceAll(oldContainerUniqueIdToReplace, newContainerUniqueId);
+ expectedContInstReqCap.put(newKey, immutablePair);
+ }
+ }
+ }
+
+ private static void verifyCompInstReqCap(Component actualComponent) {
+ List<ComponentInstance> componentInstances = actualComponent.getComponentInstances();
+ if (componentInstances != null) {
+ assertEquals(expectedContInstReqCap.size(), componentInstances.size());
+ for (ComponentInstance compInst : componentInstances) {
+ String uniqueId = compInst.getUniqueId();
+ // System.out.println("Verifying req/cap of component instance
+ // "+ uniqueId);
+ Map<String, List<RequirementDefinition>> actualCompInstReq = compInst.getRequirements();
+ if (actualCompInstReq == null) {
+ actualCompInstReq = new HashMap<>();
+ }
+ Map<String, List<CapabilityDefinition>> actualCompInstCap = compInst.getCapabilities();
+ if (actualCompInstCap == null) {
+ actualCompInstCap = new HashMap<>();
+ }
+ ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>> expReqCap = expectedContInstReqCap
+ .get(uniqueId);
+ assertNotNull(expReqCap);
+ // System.out.println("expected instance requirements:
+ // "+expReqCap.right);
+ // System.out.println("expected instance capabilities:
+ // "+expReqCap.left);
+ // System.out.println("actual instance requirements:
+ // "+actualCompInstReq);
+ // System.out.println("actual instance capabilities:
+ // "+actualCompInstCap);
+
+ // REQ comparison
+ compareReqCapMaps(expReqCap.right, actualCompInstReq);
+
+ // CAP comparison
+ compareReqCapMaps(expReqCap.left, actualCompInstCap);
+ }
+
+ } else {
+ assertTrue(expectedContInstReqCap.isEmpty());
+ }
+ }
+
+ private static void verifyContainerReqCap(Component actualComponent) {
+ Map<String, List<RequirementDefinition>> actualContainerRequirements = actualComponent.getRequirements();
+ if (actualContainerRequirements == null) {
+ actualContainerRequirements = new HashMap<>();
+ }
+ Map<String, List<CapabilityDefinition>> actualContainerCapabilities = actualComponent.getCapabilities();
+ if (actualContainerCapabilities == null) {
+ actualContainerCapabilities = new HashMap<>();
+ }
+ // System.out.println("Verifying req/cap of container component "+
+ // actualComponent.getUniqueId());
+ // System.out.println("expected container requirements:
+ // "+expectedContainerRequirements);
+ // System.out.println("expected container capabilities:
+ // "+expectedContainerCapabilities);
+ // System.out.println("actual container requirements:
+ // "+actualContainerRequirements);
+ // System.out.println("actual container capabilities:
+ // "+actualContainerCapabilities);
+
+ // REQ comparison
+ compareReqCapMaps(expectedContainerRequirements, actualContainerRequirements);
+
+ // CAP comparison
+ compareReqCapMaps(expectedContainerCapabilities, actualContainerCapabilities);
+ }
+
+ private static <T> void compareReqCapMaps(Map<String, List<T>> expectedMap, Map<String, List<T>> actualMap) {
+ assertEquals(expectedMap.size(), actualMap.size());
+ for (String expKey : expectedMap.keySet()) {
+ List<?> expCapList = expectedMap.get(expKey);
+ List<?> actCapList = actualMap.get(expKey);
+ assertEquals(expCapList.size(), actCapList.size());
+ assertEquals(new HashSet<>(expCapList), new HashSet<>(actCapList));
+ }
+ }
+
+ public static void addCompInstReqCapToExpected(ComponentInstance componentInstance,
+ ComponentTypeEnum containerComponentType, User sdncDesignerDetails) throws Exception {
+
+ sdncDesignerDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ String uniqueId = componentInstance.getUniqueId();
+ String name = componentInstance.getName();
+ String originComponentId = componentInstance.getComponentUid();
+ RestResponse getResponse = null;
+ ComponentTypeEnum compInstType = getCompInstTypeByContainerType(containerComponentType);
+ Component component = null;
+ if (compInstType == ComponentTypeEnum.RESOURCE) {
+ getResponse = ResourceRestUtils.getResource(sdncDesignerDetails, originComponentId);
+ ResourceRestUtils.checkSuccess(getResponse);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Resource.class);
+ } else if (compInstType == ComponentTypeEnum.SERVICE) {
+ getResponse = ServiceRestUtils.getService(originComponentId, sdncDesignerDetails);
+ ResourceRestUtils.checkSuccess(getResponse);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Service.class);
+ } else {
+ Assert.fail("Unsupported type - " + containerComponentType);
+ }
+
+ Map<String, List<RequirementDefinition>> resourceRequirements = component.getRequirements();
+ if (resourceRequirements == null) {
+ resourceRequirements = new HashMap<>();
+ }
+
+ Function<Entry<String, List<RequirementDefinition>>, List<RequirementDefinition>> requirementDefinitionMapper = e -> new ArrayList<>(e.getValue().stream().map(item -> new RequirementDefinition(item)).collect(Collectors.toList()));
+ Map<String, List<RequirementDefinition>> reqCopy = resourceRequirements.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), requirementDefinitionMapper));
+
+ Map<String, List<CapabilityDefinition>> resourceCapabilities = component.getCapabilities();
+ if (resourceCapabilities == null) {
+ resourceCapabilities = new HashMap<>();
+ }
+
+ Function<Entry<String, List<CapabilityDefinition>>, List<CapabilityDefinition>> capabilityDefinitionMapper = e -> new ArrayList<>(e.getValue().stream().map(item -> new CapabilityDefinition(item)).collect(Collectors.toList()));
+ Map<String, List<CapabilityDefinition>> capCopy = resourceCapabilities.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), capabilityDefinitionMapper));
+
+ setupContainerExpectedReqCap(uniqueId, name, resourceRequirements, resourceCapabilities);
+ if (component.getComponentType().equals(ComponentTypeEnum.RESOURCE)
+ && ((Resource) component).getResourceType() != ResourceTypeEnum.VF) {
+ setupConstInstExpectedReqCap(uniqueId, name, reqCopy, capCopy);
+ }
+
+ // adding entry for expected componentInstance
+ ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>> compInstReqCapPair = new ImmutablePair<Map<String, List<CapabilityDefinition>>, Map<String, List<RequirementDefinition>>>(
+ capCopy, reqCopy);
+ expectedContInstReqCap.put(uniqueId, compInstReqCapPair);
+ }
+
+ private static void setupContainerExpectedReqCap(String uniqueId, String name,
+ Map<String, List<RequirementDefinition>> componentRequirements,
+ Map<String, List<CapabilityDefinition>> componentCapabilities) {
+ for (Entry<String, List<RequirementDefinition>> resReq : componentRequirements.entrySet()) {
+ List<RequirementDefinition> reqListToAdd = resReq.getValue();
+ for (RequirementDefinition requirementDefinition : reqListToAdd) {
+ requirementDefinition.setOwnerId(uniqueId);
+ requirementDefinition.setOwnerName(name);
+ }
+ List<RequirementDefinition> expectedReqList = expectedContainerRequirements.get(resReq.getKey());
+ if (expectedReqList == null) {
+ expectedReqList = reqListToAdd;
+ } else {
+ expectedReqList.addAll(reqListToAdd);
+ }
+ expectedContainerRequirements.put(resReq.getKey(), expectedReqList);
+ }
+
+ for (Entry<String, List<CapabilityDefinition>> resCap : componentCapabilities.entrySet()) {
+ List<CapabilityDefinition> capListToAdd = resCap.getValue();
+ for (CapabilityDefinition capDefinition : capListToAdd) {
+ capDefinition.setOwnerId(uniqueId);
+ capDefinition.setOwnerName(name);
+ }
+ List<CapabilityDefinition> expectedCapList = expectedContainerCapabilities.get(resCap.getKey());
+ if (expectedCapList == null) {
+ expectedCapList = capListToAdd;
+ } else {
+ expectedCapList.addAll(capListToAdd);
+ }
+ expectedContainerCapabilities.put(resCap.getKey(), expectedCapList);
+ }
+ }
+
+ private static void setupConstInstExpectedReqCap(String uniqueId, String name,
+ Map<String, List<RequirementDefinition>> componentRequirements,
+ Map<String, List<CapabilityDefinition>> componentCapabilities) {
+ for (Entry<String, List<RequirementDefinition>> resReq : componentRequirements.entrySet()) {
+ List<RequirementDefinition> reqListToAdd = resReq.getValue();
+ for (RequirementDefinition requirementDefinition : reqListToAdd) {
+ requirementDefinition.setOwnerId(uniqueId);
+ requirementDefinition.setOwnerName(name);
+ }
+ }
+
+ for (Entry<String, List<CapabilityDefinition>> resCap : componentCapabilities.entrySet()) {
+ List<CapabilityDefinition> capListToAdd = resCap.getValue();
+ for (CapabilityDefinition capDefinition : capListToAdd) {
+ capDefinition.setOwnerId(uniqueId);
+ capDefinition.setOwnerName(name);
+ }
+ }
+ }
+
+ private static ComponentTypeEnum getCompInstTypeByContainerType(ComponentTypeEnum componentType) {
+ switch (componentType) {
+ case RESOURCE:
+ return ComponentTypeEnum.RESOURCE;
+ case SERVICE:
+ return ComponentTypeEnum.RESOURCE;
+ case PRODUCT:
+ return ComponentTypeEnum.SERVICE;
+ default:
+ break;
+ }
+ return null;
+ }
+
+ public static void deleteCompInstReqCapFromExpected(String componentInstanceId) {
+ List<String> entriesRequirementsToRemove = new ArrayList<>();
+ List<String> entriesCapabilitiesToRemove = new ArrayList<>();
+ for (Entry<String, List<RequirementDefinition>> reqEntry : expectedContainerRequirements.entrySet()) {
+ List<RequirementDefinition> reqList = reqEntry.getValue();
+ List<RequirementDefinition> reqListToDelete = new ArrayList<>();
+ for (RequirementDefinition requirementDefinition : reqList) {
+ if (requirementDefinition.getOwnerId().equals(componentInstanceId)) {
+ reqListToDelete.add(requirementDefinition);
+ }
+ }
+ reqList.removeAll(reqListToDelete);
+ if (reqList.isEmpty()) {
+ entriesRequirementsToRemove.add(reqEntry.getKey());
+ }
+ }
+
+ for (String ekey : entriesRequirementsToRemove) {
+ expectedContainerRequirements.remove(ekey);
+ }
+
+ for (Entry<String, List<CapabilityDefinition>> capEntry : expectedContainerCapabilities.entrySet()) {
+ List<CapabilityDefinition> capList = capEntry.getValue();
+ List<CapabilityDefinition> capListToDelete = new ArrayList<>();
+ for (CapabilityDefinition capabilityDefinition : capList) {
+ if (capabilityDefinition.getOwnerId().equals(componentInstanceId)) {
+ capListToDelete.add(capabilityDefinition);
+ }
+ }
+ capList.removeAll(capListToDelete);
+ if (capList.isEmpty()) {
+ entriesCapabilitiesToRemove.add(capEntry.getKey());
+ }
+ }
+ for (String ekey : entriesCapabilitiesToRemove) {
+ expectedContainerCapabilities.remove(ekey);
+ }
+
+ expectedContInstReqCap.remove(componentInstanceId);
+
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse createAtomicInstanceForVF(Resource containerDetails, Resource compInstOriginDetails,
+ User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.RESOURCE,
+ true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse createAtomicInstanceForService(Service containerDetails, Resource compInstOriginDetails,
+ User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE,
+ true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse createVFInstance(Service containerDetails, Resource compInstOriginDetails, User modifier)
+ throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE,
+ true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse createServiceInstance(Product containerDetails, Service compInstOriginDetails,
+ User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.PRODUCT,
+ true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse deleteAtomicInstanceForVF(String compInstUniqueId, Resource containerDetails,
+ User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.RESOURCE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse deleteAtomicInstanceForService(String compInstUniqueId, Service containerDetails,
+ User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, true);
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse deleteVFInstance(String compInstUniqueId, Service containerDetails, User modifier)
+ throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, true);
+
+ }
+
+ // Automatically updates the expected req/cap of the container
+ public static RestResponse deleteServiceInstance(String compInstUniqueId, Product containerDetails, User modifier)
+ throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.PRODUCT, true);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse createAtomicInstanceForVFDuringSetup(Resource containerDetails,
+ Resource compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.RESOURCE,
+ false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse createAtomicInstanceForServiceDuringSetup(Service containerDetails,
+ Resource compInstOriginDetails, User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE,
+ false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse createVFInstanceDuringSetup(Service containerDetails, Resource compInstOriginDetails,
+ User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.SERVICE,
+ false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse createServiceInstanceDuringSetup(Product containerDetails, Service compInstOriginDetails,
+ User modifier) throws Exception {
+ return createComponentInstance(containerDetails, compInstOriginDetails, modifier, ComponentTypeEnum.PRODUCT,
+ false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse deleteAtomicInstanceForVFDuringSetup(String compInstUniqueId, Resource containerDetails,
+ User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.RESOURCE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse deleteAtomicInstanceForServiceDuringSetup(String compInstUniqueId,
+ Service containerDetails, User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, false);
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse deleteVFInstanceDuringSetup(String compInstUniqueId, Service containerDetails,
+ User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.SERVICE, false);
+
+ }
+
+ // Setup of lower components - Doesn't affect req/cap of the container (for
+ // example, setup of VF for testing a Product)
+ public static RestResponse deleteServiceInstanceDuringSetup(String compInstUniqueId, Product containerDetails,
+ User modifier) throws IOException, Exception {
+ return deleteComponentInstance(compInstUniqueId, containerDetails, modifier, ComponentTypeEnum.PRODUCT, false);
+ }
+
+ public static Component getComponentAndValidateRIs(Component componentDetails, int numberOfRIs,
+ int numberOfRelations, User sdncAdminDetails) throws IOException, Exception {
+
+ RestResponse getResponse = null;
+ Component component = null;
+ if (componentDetails instanceof Resource) {
+ getResponse = ResourceRestUtils.getResource(sdncAdminDetails, componentDetails.getUniqueId());
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Resource.class);
+ } else if (componentDetails instanceof Service) {
+ getResponse = ServiceRestUtils.getService((componentDetails.getUniqueId()), sdncAdminDetails);
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Service.class);
+ } else if (componentDetails instanceof Product) {
+ getResponse = ProductRestUtils.getProduct(componentDetails.getUniqueId(), sdncAdminDetails.getUserId());
+ component = ResponseParser.parseToObjectUsingMapper(getResponse.getResponse(), Product.class);
+ } else {
+ Assert.fail("Unsupported type of componentDetails - " + componentDetails.getClass().getSimpleName());
+ }
+ ResourceRestUtils.checkSuccess(getResponse);
+ int numberOfActualRIs = component.getComponentInstances() != null ? component.getComponentInstances().size()
+ : 0;
+ int numberOfActualRelations = component.getComponentInstancesRelations() != null
+ ? component.getComponentInstancesRelations().size() : 0;
+ assertEquals("Check number of RIs meet the expected number", numberOfRIs, numberOfActualRIs);
+ assertEquals("Check number of RI relations meet the expected number", numberOfRelations,
+ numberOfActualRelations);
+ verifyReqCap(component);
+
+ return component;
+ }
+
+ public static void getComponentAndValidateRIsAfterChangeLifecycleState(String oldComponentUniqueIdToReplace,
+ Component componentDetails, int numOfRIs, int numOfRelations, User sdncAdminDetails)
+ throws IOException, Exception {
+ updateExpectedReqCapAfterChangeLifecycleState(oldComponentUniqueIdToReplace, componentDetails.getUniqueId());
+ getComponentAndValidateRIs(componentDetails, numOfRIs, numOfRelations, sdncAdminDetails);
+ }
+
+ private static RestResponse createComponentInstance(Component containerDetails, Component compInstOriginDetails,
+ User modifier, ComponentTypeEnum containerComponentTypeEnum, boolean isHighestLevel)
+ throws IOException, Exception {
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory
+ .getComponentInstance(compInstOriginDetails);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(
+ resourceInstanceReqDetails, modifier, containerDetails.getUniqueId(), containerComponentTypeEnum);
+ if (createResourceInstanceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_CREATED) && isHighestLevel) {
+ // Add RI Capabilities and Requirements to expected MAP -->
+ // expectedVfCapabilities and expectedVfRequirements
+ ComponentInstance componentInstance = ResponseParser
+ .parseToObjectUsingMapper(createResourceInstanceResponse.getResponse(), ComponentInstance.class);
+ addCompInstReqCapToExpected(componentInstance, containerComponentTypeEnum, modifier);
+ }
+ return createResourceInstanceResponse;
+ }
+
+ private static RestResponse deleteComponentInstance(String compInstUniqueId, Component containerDetails,
+ User modifier, ComponentTypeEnum componentTypeEnum, boolean isHighestLevel) throws Exception {
+ RestResponse deleteResourceInstanceResponse = ComponentInstanceRestUtils.deleteComponentInstance(modifier,
+ containerDetails.getUniqueId(), compInstUniqueId, componentTypeEnum);
+ if (deleteResourceInstanceResponse.getErrorCode().equals(BaseRestUtils.STATUS_CODE_DELETE) && isHighestLevel) {
+ deleteCompInstReqCapFromExpected(compInstUniqueId);
+ }
+ return deleteResourceInstanceResponse;
+ }
+
+ public static RestResponse associateComponentInstancesForService(RequirementCapabilityRelDef requirementDef,
+ ComponentReqDetails containerDetails, User user) throws IOException {
+
+ RestResponse associateInstances = ComponentInstanceRestUtils.associateInstances(requirementDef, user,
+ containerDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(associateInstances);
+ deleteAssociatedFromExpected(requirementDef);
+ return associateInstances;
+ }
+
+ private static void deleteAssociatedFromExpected(RequirementCapabilityRelDef requirementDef) {
+ // removing from requirements
+ RequirementAndRelationshipPair relationship = requirementDef.getRelationships().get(0);
+ String type = relationship.getRelationship().getType();
+ String fromId = requirementDef.getFromNode();
+ List<RequirementDefinition> reqList = expectedContainerRequirements.get(type);
+ RequirementDefinition toDelete = null;
+ if (reqList != null) {
+ for (RequirementDefinition reqDef : reqList) {
+ if (reqDef.getOwnerId().equals(fromId)) {
+ toDelete = reqDef;
+ }
+ }
+ if (toDelete != null) {
+ reqList.remove(toDelete);
+ if (reqList.isEmpty()) {
+ expectedContainerRequirements.remove(type);
+ }
+ removedRequirements.put(toDelete.getCapability() + " " + toDelete.getOwnerId(), toDelete);
+ }
+ }
+ }
+
+ public static void dissociateComponentInstancesForService(RequirementCapabilityRelDef requirementDef,
+ ComponentReqDetails containerDetails, User user) throws IOException {
+
+ RestResponse dissociateInstances = ComponentInstanceRestUtils.dissociateInstances(requirementDef, user,
+ containerDetails.getUniqueId(), ComponentTypeEnum.SERVICE);
+ ResourceRestUtils.checkSuccess(dissociateInstances);
+ addDissociatedToExpected(requirementDef);
+ }
+
+ private static void addDissociatedToExpected(RequirementCapabilityRelDef requirementDef) {
+ // adding to requirements
+ RequirementAndRelationshipPair relationship = requirementDef.getRelationships().get(0);
+ String type = relationship.getRelationship().getType();
+ String fromId = requirementDef.getFromNode();
+ String key = type + " " + fromId;
+ RequirementDefinition requirementDefinition = removedRequirements.get(key);
+ if (requirementDefinition != null) {
+ List<RequirementDefinition> reqList = expectedContainerRequirements.get(type);
+ if (reqList == null) {
+ reqList = new ArrayList<>();
+ expectedContainerRequirements.put(type, reqList);
+ }
+ reqList.add(requirementDefinition);
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ToscaParserUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ToscaParserUtils.java
new file mode 100644
index 0000000000..e5928b327f
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/ToscaParserUtils.java
@@ -0,0 +1,267 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils;
+
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.openecomp.sdc.ci.tests.datatypes.enums.ToscaKeysEnum;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaNodeTemplatesTopologyTemplateDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaNodeTypesDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaPropertiesNodeTemplatesDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaRequirementsNodeTemplatesDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaTopologyTemplateDefinition;
+import org.openecomp.sdc.ci.tests.utils.validation.CsarValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+
+public class ToscaParserUtils {
+
+ private static Logger log = LoggerFactory.getLogger(ToscaParserUtils.class.getName());
+
+ public static Map<?, ?> getToscaYamlMap(String csarUUID, String fileLocation) throws Exception {
+ String csarPayload = CsarValidationUtils.getCsarPayload(csarUUID, fileLocation);
+ if (csarPayload != null) {
+ Yaml yaml = new Yaml();
+ Map<?, ?> map = (Map<?, ?>) yaml.load(csarPayload);
+ return map;
+ }
+ return null;
+ }
+
+ public static ToscaDefinition getToscaDefinitionObjectByCsarUuid(String csarUUID) throws Exception {
+
+ String TOSCAMetaLocation = "TOSCA-Metadata/TOSCA.meta";
+ Map<?, ?> map = getToscaYamlMap(csarUUID, TOSCAMetaLocation);
+ assertNotNull("Tosca Entry-Definitions is null", map);
+ if (map != null) {
+ String definitionYamlLocation = (String) map.get("Entry-Definitions");
+ Map<?, ?> toscaMap = getToscaYamlMap(csarUUID, definitionYamlLocation);
+ assertNotNull("Tosca definition is null", toscaMap);
+ if (toscaMap != null) {
+ ToscaDefinition toscaDefinition = new ToscaDefinition();
+ Set<?> keySet = toscaMap.keySet();
+ for (Object key : keySet) {
+ ToscaKeysEnum toscaKey = ToscaKeysEnum.findToscaKey((String) key);
+ switch (toscaKey) {
+ case TOSCA_DEFINITION_VERSION:
+ getToscaDefinitionVersion(toscaMap, toscaDefinition);
+ break;
+ case NODE_TYPES:
+ getToscaNodeTypes(toscaMap, toscaDefinition);
+ break;
+ case TOPOLOGY_TEMPLATE:
+ getToscaTopologyTemplate(toscaMap, toscaDefinition);
+ break;
+ case IMPORTS:
+ // toscaMap.get("imports");
+ break;
+ default:
+ break;
+ }
+ }
+ return toscaDefinition;
+ }
+ }
+ return null;
+
+ }
+
+ public static void getToscaDefinitionVersion(Map<?, ?> toscaMap, ToscaDefinition toscaDefinition) {
+ if (toscaMap.get("tosca_definitions_version") != null) {
+ toscaDefinition.setToscaDefinitionVersion((String) toscaMap.get("tosca_definitions_version"));
+ }
+ }
+
+ // spec 90 page
+ public static void getToscaNodeTypes(Map<?, ?> toscaMap, ToscaDefinition toscaDefinition) {
+ @SuppressWarnings("unchecked")
+ Map<String, Map<String, String>> nodeTypes = (Map<String, Map<String, String>>) toscaMap.get("node_types");
+ List<ToscaNodeTypesDefinition> listToscaNodeTypes = new ArrayList<>();
+ if (nodeTypes != null) {
+ for (Map.Entry<String, Map<String, String>> entry : nodeTypes.entrySet()) {
+ ToscaNodeTypesDefinition toscaNodeTypes = new ToscaNodeTypesDefinition();
+ String toscaNodeName = entry.getKey();
+ toscaNodeTypes.setName(toscaNodeName);
+
+ Map<String, String> toscaNodeType = entry.getValue();
+ if (toscaNodeType != null) {
+ Set<Entry<String, String>> entrySet = toscaNodeType.entrySet();
+ if (entrySet != null) {
+ // boolean found = false;
+ for (Entry<String, String> toscaNodeTypeMap : entrySet) {
+ String key = toscaNodeTypeMap.getKey();
+ if (key.equals("derived_from")) {
+ String derivedFrom = toscaNodeTypeMap.getValue();
+ toscaNodeTypes.setDerivedFrom(derivedFrom);
+ // found = true;
+ break;
+ } else {
+ continue;
+ }
+
+ }
+ // if (found == false) {
+ // System.out.println("Tosca file not valid,
+ // derived_from not found");
+ // }
+ }
+
+ }
+ listToscaNodeTypes.add(toscaNodeTypes);
+ }
+ toscaDefinition.setToscaNodeTypes(listToscaNodeTypes);
+ }
+ }
+
+ public static void getToscaTopologyTemplate(Map<?, ?> toscaMap, ToscaDefinition toscaDefinition) {
+ ToscaTopologyTemplateDefinition toscaTopologyTemplate = new ToscaTopologyTemplateDefinition();
+ @SuppressWarnings("unchecked")
+ Map<String, Map<String, Object>> topologyTemplateMap = (Map<String, Map<String, Object>>) toscaMap
+ .get("topology_template");
+ List<ToscaNodeTemplatesTopologyTemplateDefinition> listToscaNodeTemplates = new ArrayList<>();
+
+ if (topologyTemplateMap != null) {
+ getToscaNodeTemplates(topologyTemplateMap, listToscaNodeTemplates);
+ }
+ toscaTopologyTemplate.setToscaNodeTemplatesTopologyTemplateDefinition(listToscaNodeTemplates);
+ toscaDefinition.setToscaTopologyTemplate(toscaTopologyTemplate);
+ }
+
+ public static void getToscaNodeTemplates(Map<String, Map<String, Object>> topologyTemplateMap,
+ List<ToscaNodeTemplatesTopologyTemplateDefinition> listToscaNodeTemplates) {
+ Map<String, Object> nodeTemplatesMap = topologyTemplateMap.get("node_templates");
+ if (nodeTemplatesMap != null) {
+
+ for (Entry<String, Object> nodeTemplates : nodeTemplatesMap.entrySet()) {
+ ToscaNodeTemplatesTopologyTemplateDefinition toscaNodeTemplates = new ToscaNodeTemplatesTopologyTemplateDefinition();
+ getToscaNodeTemplatesName(nodeTemplates, toscaNodeTemplates);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> node = (Map<String, Object>) nodeTemplates.getValue();
+ getNodeTemplatesType(toscaNodeTemplates, node);
+ getToscaNodeTemplateProperties(toscaNodeTemplates, node);
+ getToscaNodeTemplateRequirements(toscaNodeTemplates, node);
+ listToscaNodeTemplates.add(toscaNodeTemplates);
+ }
+ }
+ }
+
+ public static void getToscaNodeTemplateRequirements(ToscaNodeTemplatesTopologyTemplateDefinition toscaNodeTemplates,
+ Map<String, Object> node) {
+ List<ToscaRequirementsNodeTemplatesDefinition> toscaRequirements = new ArrayList<>();
+ if (node.get("requirements") != null) {
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> requirementList = (List<Map<String, Object>>) node.get("requirements");
+ for (int i = 0; i < requirementList.size(); i++) {
+ for (Map.Entry<String, Object> requirement : requirementList.get(i).entrySet()) {
+ ToscaRequirementsNodeTemplatesDefinition toscaRequirement = new ToscaRequirementsNodeTemplatesDefinition();
+ if (requirement.getKey() != null) {
+ String requirementName = requirement.getKey();
+ toscaRequirement.setName(requirementName);
+ } else {
+ log.debug("Tosca file not valid, requirements should contain name");
+ }
+
+ @SuppressWarnings("unchecked")
+ Map<String, String> requirementMap = (Map<String, String>) requirement.getValue();
+ Set<Entry<String, String>> entrySet = requirementMap.entrySet();
+ if (entrySet != null) {
+ for (Entry<String, String> requirementField : entrySet) {
+ String key = requirementField.getKey();
+ switch (key) {
+ case "capability":
+ if (requirementMap.get(key) != null) {
+ String capability = (String) requirementMap.get(key);
+ toscaRequirement.setCapability(capability);
+ break;
+ } else {
+ continue;
+ }
+ case "node":
+ if (requirementMap.get(key) != null) {
+ String requirementNode = (String) requirementMap.get(key);
+ toscaRequirement.setNode(requirementNode);
+ break;
+ } else {
+ continue;
+ }
+ case "relationship":
+ if (requirementMap.get(key) != null) {
+ String relationship = (String) requirementMap.get(key);
+ toscaRequirement.setRelationship(relationship);
+ break;
+ } else {
+ continue;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ toscaRequirements.add(toscaRequirement);
+ }
+ }
+ }
+ toscaNodeTemplates.setRequirements(toscaRequirements);
+ }
+
+ public static void getToscaNodeTemplateProperties(ToscaNodeTemplatesTopologyTemplateDefinition toscaNodeTemplates,
+ Map<String, Object> node) {
+ List<ToscaPropertiesNodeTemplatesDefinition> listToscaProperties = new ArrayList<>();
+ if (node.get("properties") != null) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> properties = (Map<String, Object>) node.get("properties");
+ for (Map.Entry<String, Object> property : properties.entrySet()) {
+ ToscaPropertiesNodeTemplatesDefinition toscaProperty = new ToscaPropertiesNodeTemplatesDefinition();
+ String propertyName = property.getKey();
+ Object propertyValue = property.getValue();
+ toscaProperty.setName(propertyName);
+ toscaProperty.setValue(propertyValue);
+ listToscaProperties.add(toscaProperty);
+ }
+ }
+ toscaNodeTemplates.setProperties(listToscaProperties);
+ }
+
+ protected static void getNodeTemplatesType(ToscaNodeTemplatesTopologyTemplateDefinition toscaNodeTemplates,
+ Map<String, Object> node) {
+ if (node.get("type") != null) {
+ String type = (String) node.get("type");
+ toscaNodeTemplates.setType(type);
+ } else {
+ log.debug("Tosca file not valid, nodeTemplate should contain type");
+ }
+ }
+
+ protected static void getToscaNodeTemplatesName(Entry<String, Object> nodeTemplates,
+ ToscaNodeTemplatesTopologyTemplateDefinition toscaNodeTemplates) {
+ String name = nodeTemplates.getKey();
+ toscaNodeTemplates.setName(name);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Utils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Utils.java
new file mode 100644
index 0000000000..962f140dbd
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/Utils.java
@@ -0,0 +1,233 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.ToscaNodeTypeInfo;
+import org.openecomp.sdc.common.api.YamlConstants;
+import org.yaml.snakeyaml.Yaml;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public final class Utils {
+
+ Gson gson = new Gson();
+
+ static Logger logger = Logger.getLogger(Utils.class.getName());
+
+ String contentTypeHeaderData = "application/json";
+ String acceptHeaderDate = "application/json";
+
+ public Utils() {
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public ToscaNodeTypeInfo parseToscaNodeYaml(String fileContent) {
+
+ ToscaNodeTypeInfo result = new ToscaNodeTypeInfo();
+ Object templateVersion = null;
+ Object templateName = null;
+
+ if (fileContent != null) {
+ Yaml yaml = new Yaml();
+
+ Map<String, Object> yamlObject = (Map<String, Object>) yaml.load(fileContent);
+
+ templateVersion = yamlObject.get(YamlConstants.TEMPLATE_VERSION);
+ if (templateVersion != null) {
+ result.setTemplateVersion(templateVersion.toString());
+ }
+ templateName = yamlObject.get(YamlConstants.TEMPLATE_NAME);
+ if (templateName != null) {
+ result.setTemplateName(templateName.toString());
+ }
+ Object nodeTypes = yamlObject.get(YamlConstants.NODE_TYPES);
+
+ if (nodeTypes != null) {
+ Map<String, Object> nodeTypesMap = (Map<String, Object>) nodeTypes;
+ for (Entry<String, Object> entry : nodeTypesMap.entrySet()) {
+
+ String nodeName = entry.getKey();
+ if (nodeName != null) {
+ result.setNodeName(nodeName);
+ }
+
+ break;
+
+ }
+ }
+
+ }
+
+ return result;
+ }
+
+ public static String getJsonObjectValueByKey(String metadata, String key) {
+ JsonElement jelement = new JsonParser().parse(metadata);
+
+ JsonObject jobject = jelement.getAsJsonObject();
+ Object obj = jobject.get(key);
+ if (obj == null) {
+ return null;
+ } else {
+ String value;
+ value = (String) jobject.get(key).getAsString();
+ return value;
+ }
+ }
+
+ public static Config getConfig() throws FileNotFoundException {
+ Config config = Config.instance();
+ return config;
+ }
+
+ public static void compareArrayLists(List<String> actualArraylList, List<String> expectedArrayList,
+ String message) {
+
+ ArrayList<String> actual = new ArrayList<String>(actualArraylList);
+ ArrayList<String> expected = new ArrayList<String>(expectedArrayList);
+ assertEquals(message + " count got by rest API not match to " + message + " expected count", expected.size(),
+ actual.size());
+ actual.removeAll(expected);
+ assertEquals(message + " content got by rest API not match to " + message + " expected content", 0,
+ actual.size());
+ }
+
+ public static Object parseYamlConfig(String pattern) throws FileNotFoundException {
+
+ Yaml yaml = new Yaml();
+ Config config = getConfig();
+ String configurationFile = config.getConfigurationFile();
+ File file = new File(configurationFile);
+ // File file = new
+ // File("../catalog-be/src/main/resources/config/configuration.yaml");
+ InputStream inputStream = new FileInputStream(file);
+ Map<?, ?> map = (Map<?, ?>) yaml.load(inputStream);
+ Object patternMap = (Object) map.get(pattern);
+
+ return patternMap;
+ }
+
+ public static String getDepArtLabelFromConfig(ArtifactTypeEnum artifactTypeEnum) throws FileNotFoundException {
+
+ @SuppressWarnings("unchecked")
+ Map<String, Object> mapOfDepResArtTypesObjects = (Map<String, Object>) parseYamlConfig(
+ "deploymentResourceArtifacts");
+ for (Map.Entry<String, Object> iter : mapOfDepResArtTypesObjects.entrySet()) {
+ if (iter.getValue().toString().contains(artifactTypeEnum.getType())) {
+ return iter.getKey().toLowerCase();
+ }
+ }
+
+ return "defaultLabelName";
+ }
+
+ public static String multipleChar(String ch, int repeat) {
+ return StringUtils.repeat(ch, repeat);
+ }
+
+ public static List<String> getListOfDepResArtLabels(Boolean isLowerCase) throws FileNotFoundException {
+
+ List<String> listOfResDepArtTypesFromConfig = new ArrayList<String>();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> resourceDeploymentArtifacts = (Map<String, Object>) parseYamlConfig(
+ "deploymentResourceArtifacts");
+ if (resourceDeploymentArtifacts != null) {
+
+ if (isLowerCase) {
+ for (Map.Entry<String, Object> iter : resourceDeploymentArtifacts.entrySet()) {
+ listOfResDepArtTypesFromConfig.add(iter.getKey().toLowerCase());
+ }
+ } else {
+
+ for (Map.Entry<String, Object> iter : resourceDeploymentArtifacts.entrySet()) {
+ listOfResDepArtTypesFromConfig.add(iter.getKey());
+ }
+ }
+ }
+ return listOfResDepArtTypesFromConfig;
+ }
+
+ public static List<String> getListOfToscaArtLabels(Boolean isLowerCase) throws FileNotFoundException {
+
+ List<String> listOfToscaArtTypesFromConfig = new ArrayList<String>();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> toscaArtifacts = (Map<String, Object>) parseYamlConfig("toscaArtifacts");
+ if (toscaArtifacts != null) {
+
+ if (isLowerCase) {
+ for (Map.Entry<String, Object> iter : toscaArtifacts.entrySet()) {
+ listOfToscaArtTypesFromConfig.add(iter.getKey().toLowerCase());
+ }
+ } else {
+ for (Map.Entry<String, Object> iter : toscaArtifacts.entrySet()) {
+ listOfToscaArtTypesFromConfig.add(iter.getKey());
+ }
+ }
+ }
+ return listOfToscaArtTypesFromConfig;
+ }
+
+ public static List<String> getListOfResPlaceHoldersDepArtTypes() throws FileNotFoundException {
+ List<String> listResDepArtTypesFromConfig = new ArrayList<String>();
+ List<String> listOfResDepArtLabelsFromConfig = getListOfDepResArtLabels(false);
+ assertNotNull("deployment artifact types list is null", listOfResDepArtLabelsFromConfig);
+ Object parseYamlConfig = Utils.parseYamlConfig("deploymentResourceArtifacts");
+ Map<String, Object> mapOfDepResArtTypesObjects = (Map<String, Object>) Utils
+ .parseYamlConfig("deploymentResourceArtifacts");
+
+ // assertNotNull("deployment artifact types list is null",
+ // mapOfDepResArtTypesObjects);
+ if (listOfResDepArtLabelsFromConfig != null) {
+ for (String resDepArtType : listOfResDepArtLabelsFromConfig) {
+ Object object = mapOfDepResArtTypesObjects.get(resDepArtType);
+ if (object instanceof Map<?, ?>) {
+ Map<String, Object> map = (Map<String, Object>) object;
+ listResDepArtTypesFromConfig.add((String) map.get("type"));
+ } else {
+ assertTrue("return object does not instance of map", false);
+ }
+ }
+ }
+ return listResDepArtTypesFromConfig;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils.java
new file mode 100644
index 0000000000..51b68d4d56
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils.java
@@ -0,0 +1,213 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.cassandra;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.javatuples.Pair;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.TableMetadata;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.datastax.driver.core.querybuilder.Select;
+import com.datastax.driver.core.querybuilder.Select.Where;
+
+public final class CassandraUtils {
+ private static Logger logger = LoggerFactory.getLogger(CassandraUtils.class.getName());
+
+ protected static Cluster cluster = null;
+ protected static Session session;
+
+ protected static void initConnection(String keyspace) throws FileNotFoundException {
+
+ String cassandraHost = Utils.getConfig().getCassandraHost();
+ Boolean cassandraAuthenticate = Utils.getConfig().getCassandraAuthenticate();
+ String cassandraUsername = Utils.getConfig().getCassandraUsername();
+ String cassandraPassword = Utils.getConfig().getCassandraPassword();
+ Boolean cassandraSsl = Utils.getConfig().getCassandraSsl();
+ String cassandraTruststorePath = Utils.getConfig().getCassandraTruststorePath();
+ String cassandraTruststorePassword = Utils.getConfig().getCassandraTruststorePassword();
+ /*
+ * String cassandraAuditKeySpace=
+ * Utils.getConfig().getCassandraAuditKeySpace(); String
+ * cassandraArtifactKeySpace=
+ * Utils.getConfig().getCassandraArtifactKeySpace();
+ */
+
+ Cluster.Builder clusterBuilder = Cluster.builder().addContactPoint(cassandraHost);
+ if (cassandraAuthenticate) {
+ // authantication
+ clusterBuilder.withCredentials(cassandraUsername, cassandraPassword);
+ }
+
+ if (cassandraSsl) {
+ // ssl
+ System.setProperty("javax.net.ssl.trustStore", cassandraTruststorePath);
+ System.setProperty("javax.net.ssl.trustStorePassword", cassandraTruststorePassword);
+ clusterBuilder.withSSL();
+ }
+
+ cluster = clusterBuilder.build();
+ session = cluster.connect(keyspace);
+
+ }
+
+ public static void truncateTable(String keyspace, String tableName) throws FileNotFoundException {
+
+ if (session == null || session.isClosed()) {
+ initConnection(keyspace);
+ }
+
+ try {
+
+ if (session != null) {
+ session.execute(QueryBuilder.truncate(keyspace, tableName));
+ logger.debug("The table {}. {} was cleaned", keyspace, tableName);
+ } else {
+ throw new RuntimeException("Keyspace " + keyspace + " not connected");
+ }
+ } finally {
+ // if (cluster != null) {
+ // cluster.close();
+ // }
+ }
+ }
+
+ public static void close() {
+ if (cluster != null) {
+ cluster.close();
+ }
+ }
+
+ public static void truncateAllKeyspaces() throws FileNotFoundException {
+ // truncateAllTables(AuditingTypesConstants.ARTIFACT_KEYSPACE);
+ truncateAllTables(AuditingTypesConstants.AUDIT_KEYSPACE);
+ }
+
+ public static void truncateAllTables(String keyspace) throws FileNotFoundException {
+
+ if (session == null || session.isClosed()) {
+ initConnection(keyspace);
+ }
+ try {
+
+ if (session != null) {
+ Metadata metadata = cluster.getMetadata();
+ KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspace);
+ if (keyspaceMetadata != null) {
+ Collection<TableMetadata> tables = keyspaceMetadata.getTables();
+ tables.forEach(table -> {
+ session.execute(QueryBuilder.truncate(table));
+ logger.debug("Table trunceted - {}", table.getName());
+ });
+ }
+ } else {
+ throw new RuntimeException("Keyspace " + keyspace + " not connected");
+ }
+
+ } finally {
+ // if (cluster != null) {
+ // cluster.close();
+ // }
+ }
+ }
+
+ public static List<Row> fetchFromTable(String keyspace, String tableName,
+ List<Pair<AuditingFieldsKeysEnum, String>> fields) throws FileNotFoundException {
+
+ List<Pair<String, String>> fieldsConverted = new ArrayList<>();
+
+ fields.forEach(pair -> {
+ Pair<String, String> newPair = new Pair(pair.getValue0().getDisplayName(), pair.getValue1());
+ fieldsConverted.add(newPair);
+ });
+
+ return fetchFromTableQuery(keyspace, tableName, fieldsConverted);
+ }
+
+ public static List<Row> fetchFromTableQuery(String keyspace, String tableName, List<Pair<String, String>> fields)
+ throws FileNotFoundException {
+
+ if (session == null || session.isClosed()) {
+ initConnection(keyspace);
+ }
+ try {
+
+ if (session != null) {
+ Select select = QueryBuilder.select().all().from(keyspace, tableName);
+ if (fields != null) {
+ // Set<Entry<AuditingFieldsKeysEnum, String>> entrySet =
+ // fields.entrySet();
+ // fields.
+ boolean multiple = (fields.size() > 1) ? true : false;
+ Where where = null;
+ int size = 0;
+
+ for (Pair<String, String> pair : fields) {
+ ++size;
+ if (size == 1) {
+ where = select.where(QueryBuilder.eq(pair.getValue0(), pair.getValue1()));
+ } else {
+ where.and(QueryBuilder.eq(pair.getValue0(), pair.getValue1()));
+ }
+ }
+ if (multiple) {
+ select.allowFiltering();
+ }
+
+ }
+
+ List<Row> rows = session.execute(select).all();
+ for (Row row : rows) {
+ logger.debug("{}", row);
+ }
+ return rows;
+ }
+ } finally {
+ // if (cluster != null) {
+ // cluster.close();
+ // }
+ }
+ return null;
+ }
+ //
+ // public static void main(String[] args) throws FileNotFoundException {
+ // Map<AuditingFieldsKeysEnum, String> map = new HashMap<>();
+ // map.put(AuditingFieldsKeysEnum.AUDIT_ACTION, "Access");
+ // map.put(AuditingFieldsKeysEnum.AUDIT_STATUS, "200");
+ // // CassandraUtils.truncateTable("sdcArtifact", "resources");
+ //// CassandraUtils.truncateAllTables("sdcAudit");
+ // CassandraUtils.fetchFromTable("sdcAudit", "useraccessevent", map );
+ // }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils2.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils2.java
new file mode 100644
index 0000000000..414ca2334e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/cassandra/CassandraUtils2.java
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.cassandra;
+
+import java.io.FileNotFoundException;
+import java.util.Collection;
+import java.util.List;
+
+import org.javatuples.Pair;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.KeyspaceMetadata;
+import com.datastax.driver.core.Metadata;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.TableMetadata;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.datastax.driver.core.querybuilder.Select;
+import com.datastax.driver.core.querybuilder.Select.Where;
+
+public final class CassandraUtils2 {
+ private static Logger logger = LoggerFactory.getLogger(CassandraUtils2.class.getName());
+
+ public static void truncateTable(String keyspace, String tableName) throws FileNotFoundException {
+
+ String cassandraHost = Utils.getConfig().getCassandraHost();
+
+ Cluster cluster = null;
+ Session session;
+
+ try {
+ Cluster.Builder clusterBuilder = Cluster.builder().addContactPoint(cassandraHost);
+ // authantication
+ // clusterBuilder.withCredentials(username,password);
+ // ssl
+ // System.setProperty("javax.net.ssl.trustStore",truststorePath);
+ // System.setProperty("javax.net.ssl.trustStorePassword",truststorePassword);
+ // clusterBuilder.withSSL();
+ cluster = clusterBuilder.build();
+ session = cluster.connect(keyspace);
+ if (session != null) {
+ session.execute(QueryBuilder.truncate(keyspace, tableName));
+ logger.debug("The table {}. {} was cleaned", keyspace, tableName);
+ } else {
+ throw new RuntimeException("Keyspace " + keyspace + " not connected");
+ }
+ } finally {
+ if (cluster != null) {
+ cluster.close();
+ }
+ }
+ }
+
+ public static void truncateAllKeyspaces() throws FileNotFoundException {
+ truncateAllTables(AuditingTypesConstants.ARTIFACT_KEYSPACE);
+ truncateAllTables(AuditingTypesConstants.AUDIT_KEYSPACE);
+ }
+
+ public static void truncateAllTables(String keyspace) throws FileNotFoundException {
+ String cassandraHost = Utils.getConfig().getCassandraHost();
+
+ Cluster cluster = null;
+ Session session;
+
+ try {
+ cluster = Cluster.builder().addContactPoint(cassandraHost).build();
+ session = cluster.connect(keyspace);
+ if (session != null) {
+ Metadata metadata = cluster.getMetadata();
+ KeyspaceMetadata keyspaceMetadata = metadata.getKeyspace(keyspace);
+ if (keyspaceMetadata != null) {
+ Collection<TableMetadata> tables = keyspaceMetadata.getTables();
+ tables.forEach(table -> {
+ session.execute(QueryBuilder.truncate(table));
+ logger.debug("Table trunceted - {}", table.getName());
+ });
+ }
+ } else {
+ throw new RuntimeException("Keyspace " + keyspace + " not connected");
+ }
+
+ } finally {
+ if (cluster != null) {
+ cluster.close();
+ }
+ }
+ }
+
+ public static List<Row> fetchFromTable(String keyspace, String tableName,
+ List<Pair<AuditingFieldsKeysEnum, String>> fields) throws FileNotFoundException {
+
+ // List<Pair<AuditingFieldsKeysEnum, String>>
+ // Map<AuditingFieldsKeysEnum, String>
+
+ Cluster cluster = null;
+ Session session;
+ String cassandraHost = Utils.getConfig().getCassandraHost();
+
+ try {
+ cluster = Cluster.builder().addContactPoint(cassandraHost).build();
+ session = cluster.connect(keyspace);
+ if (session != null) {
+ Select select = QueryBuilder.select().all().from(keyspace, tableName);
+ if (fields != null) {
+ // Set<Entry<AuditingFieldsKeysEnum, String>> entrySet =
+ // fields.entrySet();
+ // fields.
+ boolean multiple = (fields.size() > 1) ? true : false;
+ Where where = null;
+ int size = 0;
+
+ for (Pair<AuditingFieldsKeysEnum, String> pair : fields) {
+ ++size;
+ if (size == 1) {
+ where = select.where(QueryBuilder.eq(pair.getValue0().getDisplayName(), pair.getValue1()));
+ } else {
+ where.and(QueryBuilder.eq(pair.getValue0().getDisplayName(), pair.getValue1()));
+ }
+ }
+ if (multiple) {
+ select.allowFiltering();
+ }
+
+ }
+
+ List<Row> rows = session.execute(select).all();
+ for (Row row : rows) {
+ logger.debug("{}", row);
+ }
+ return rows;
+ }
+ } finally {
+ if (cluster != null) {
+ cluster.close();
+ }
+ }
+ return null;
+ }
+ //
+ // public static void main(String[] args) throws FileNotFoundException {
+ // Map<AuditingFieldsKeysEnum, String> map = new HashMap<>();
+ // map.put(AuditingFieldsKeysEnum.AUDIT_ACTION, "Access");
+ // map.put(AuditingFieldsKeysEnum.AUDIT_STATUS, "200");
+ // // CassandraUtils.truncateTable("sdcArtifact", "resources");
+ //// CassandraUtils.truncateAllTables("sdcAudit");
+ // CassandraUtils.fetchFromTable("sdcAudit", "useraccessevent", map );
+ // }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/AtomicOperationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/AtomicOperationUtils.java
new file mode 100644
index 0000000000..0456b2c121
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/AtomicOperationUtils.java
@@ -0,0 +1,627 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.general;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.tuple.Pair;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.PropertyTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ComponentInstanceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ConsumerRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.PropertyRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+public final class AtomicOperationUtils {
+
+ private AtomicOperationUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ // *********** RESOURCE ****************
+ /**
+ * Import a vfc From tosca file
+ *
+ * @param filePath
+ * @param fileName
+ * @return
+ * @throws IOException
+ * @throws JSONException
+ */
+ public static Either<Resource, RestResponse> importResource(String filePath, String fileName) {
+ try {
+ User designer = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ ImportReqDetails importReqDetails = ElementFactory.getDefaultImportResource("ciTmpVFC");
+ importReqDetails = ImportUtils.getImportResourceDetailsByPathAndName(importReqDetails, filePath, fileName);
+ RestResponse importResourceResponse = ResourceRestUtils.createImportResource(importReqDetails, designer, null);
+ return buildResourceFromResponse(importResourceResponse);
+ } catch (Exception e) {
+ throw new AtomicOperationException(e);
+ }
+ }
+
+ public static Either<Resource, RestResponse> createResourceByType(ResourceTypeEnum resourceType, UserRoleEnum userRole, Boolean validateState) {
+ try {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ResourceReqDetails defaultResource = ElementFactory.getDefaultResourceByType(resourceType, defaultUser);
+ RestResponse resourceResp = ResourceRestUtils.createResource(defaultResource, defaultUser);
+
+ if (validateState) {
+ assertTrue(resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ Resource resourceResponseObject = ResponseParser.convertResourceResponseToJavaObject(resourceResp.getResponse());
+ return Either.left(resourceResponseObject);
+ }
+ return Either.right(resourceResp);
+ } catch (Exception e) {
+ throw new AtomicOperationException(e);
+ }
+ }
+
+ public static Either<Resource, RestResponse> createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum resourceType, NormativeTypesEnum normativeTypes, ResourceCategoryEnum resourceCategory, UserRoleEnum userRole, Boolean validateState)
+ throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ResourceReqDetails defaultResource = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(resourceType, normativeTypes, resourceCategory, defaultUser);
+ RestResponse resourceResp = ResourceRestUtils.createResource(defaultResource, defaultUser);
+
+ if (validateState) {
+ assertTrue(resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ // Resource resourceResponseObject = ResponseParser
+ // .convertResourceResponseToJavaObject(resourceResp.getResponse());
+ Resource resourceResponseObject = ResponseParser.parseToObjectUsingMapper(resourceResp.getResponse(), Resource.class);
+ return Either.left(resourceResponseObject);
+ }
+ return Either.right(resourceResp);
+ }
+
+ public static Either<Resource, RestResponse> createResourcesByCustomNormativeTypeAndCatregory(ResourceTypeEnum resourceType, Resource resourceNormativeType, ResourceCategoryEnum resourceCategory, UserRoleEnum userRole, Boolean validateState)
+ throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ResourceReqDetails defaultResource = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(resourceType, resourceNormativeType, resourceCategory, defaultUser);
+ RestResponse resourceResp = ResourceRestUtils.createResource(defaultResource, defaultUser);
+
+ if (validateState) {
+ assertTrue("actual result: " + resourceResp.getResponseMessage(), resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ // Resource resourceResponseObject = ResponseParser
+ // .convertResourceResponseToJavaObject(resourceResp.getResponse());
+ Resource resourceResponseObject = ResponseParser.parseToObjectUsingMapper(resourceResp.getResponse(), Resource.class);
+ return Either.left(resourceResponseObject);
+ }
+ return Either.right(resourceResp);
+ }
+
+ // *********** SERVICE ****************
+
+ public static Either<Service, RestResponse> createDefaultService(UserRoleEnum userRole, Boolean validateState) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService(defaultUser);
+ RestResponse createServiceResp = ServiceRestUtils.createService(serviceDetails, defaultUser);
+
+ if (validateState) {
+ assertTrue(createServiceResp.getErrorCode() == ServiceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (createServiceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ Service serviceResponseObject = ResponseParser.convertServiceResponseToJavaObject(createServiceResp.getResponse());
+ return Either.left(serviceResponseObject);
+ }
+ return Either.right(createServiceResp);
+ }
+
+ public static Either<Service, RestResponse> createServiceByCategory(ServiceCategoriesEnum category, UserRoleEnum userRole, Boolean validateState) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService(category, defaultUser);
+ RestResponse createServiceResp = ServiceRestUtils.createService(serviceDetails, defaultUser);
+
+ if (validateState) {
+ assertTrue(createServiceResp.getErrorCode() == ServiceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (createServiceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ Service serviceResponseObject = ResponseParser.convertServiceResponseToJavaObject(createServiceResp.getResponse());
+ return Either.left(serviceResponseObject);
+ }
+ return Either.right(createServiceResp);
+ }
+
+ // *********** PRODUCT ****************
+
+ public static Either<Product, RestResponse> createDefaultProduct(UserRoleEnum userRole, Boolean validateState) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ProductReqDetails defaultProduct = ElementFactory.getDefaultProduct();
+ RestResponse createProductResp = ProductRestUtils.createProduct(defaultProduct, defaultUser);
+
+ if (validateState) {
+ assertTrue(createProductResp.getErrorCode() == ProductRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (createProductResp.getErrorCode() == ProductRestUtils.STATUS_CODE_CREATED) {
+ Product productResponseJavaObject = ResponseParser.convertProductResponseToJavaObject(createProductResp.getResponse());
+ return Either.left(productResponseJavaObject);
+ }
+ return Either.right(createProductResp);
+ }
+
+ // *********** LIFECYCLE ***************
+
+ public static Pair<Component, RestResponse> changeComponentState(Component component, UserRoleEnum userRole, LifeCycleStatesEnum targetState, Boolean validateState) throws Exception {
+
+ Boolean isValidationFailed = false;
+ RestResponse lifeCycleStatesResponse = null;
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+
+ LifeCycleStatesEnum curentCompState = LifeCycleStatesEnum.findByCompState(component.getLifecycleState().toString());
+
+ if (curentCompState == targetState) {
+ return Pair.of(component, null);
+ }
+ // List<LifeCycleStatesEnum> lifeCycleStatesEnumOrigList = new
+ // ArrayList<LifeCycleStatesEnum>(EnumSet.allOf(LifeCycleStatesEnum.class));
+
+ ArrayList<String> lifeCycleStatesEnumList = new ArrayList<String>();
+ if (curentCompState.equals(LifeCycleStatesEnum.CHECKIN) && targetState.equals(LifeCycleStatesEnum.CHECKOUT)) {
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.CHECKIN.toString());
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.CHECKOUT.toString());
+ } else {
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.CHECKOUT.toString());
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.CHECKIN.toString());
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.CERTIFICATIONREQUEST.toString());
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.STARTCERTIFICATION.toString());
+ lifeCycleStatesEnumList.add(LifeCycleStatesEnum.CERTIFY.toString());
+ }
+ for (int i = 0; i < lifeCycleStatesEnumList.size(); i++) {
+ if (lifeCycleStatesEnumList.get(i).equals(curentCompState.name())) {
+ int a;
+ a = (i == lifeCycleStatesEnumList.size() - 1) ? 0 : i + 1;
+
+ for (int n = a; n < lifeCycleStatesEnumList.size(); n++) {
+ if (lifeCycleStatesEnumList.get(n).equals(LifeCycleStatesEnum.STARTCERTIFICATION.name()) || lifeCycleStatesEnumList.get(n).equals(LifeCycleStatesEnum.CERTIFY.name())) {
+ defaultUser = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ } else
+ defaultUser = ElementFactory.getDefaultUser(userRole);
+
+ lifeCycleStatesResponse = LifecycleRestUtils.changeComponentState(component, defaultUser, LifeCycleStatesEnum.findByState(lifeCycleStatesEnumList.get(n)));
+ if (lifeCycleStatesResponse.getErrorCode() != LifecycleRestUtils.STATUS_CODE_SUCCESS)
+ isValidationFailed = true;
+ if (lifeCycleStatesEnumList.get(n).equals(targetState.toString()) || isValidationFailed == true) {
+ break;
+ }
+ }
+ }
+
+ }
+ component = getCompoenntObject(component, userRole);
+ Component componentJavaObject = convertReposnseToComponentObject(component, lifeCycleStatesResponse);
+
+ if (validateState == true && isValidationFailed == true) {
+ assertTrue("change state failed" + lifeCycleStatesResponse.getResponse(), false);
+
+ return Pair.of(componentJavaObject, lifeCycleStatesResponse);
+ }
+
+ if (isValidationFailed == true) {
+ return Pair.of(componentJavaObject, lifeCycleStatesResponse);
+ }
+
+ return Pair.of(componentJavaObject, lifeCycleStatesResponse);
+ }
+
+ public static RestResponse distributeService(Component component, Boolean validateState) throws Exception {
+
+ Service service = (Service) component;
+
+ User opsUser = ElementFactory.getDefaultUser(UserRoleEnum.OPS);
+ User governotUser = ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR);
+
+ ServiceReqDetails serviceDetails = new ServiceReqDetails(service);
+ RestResponse distributionService = null;
+
+ RestResponse approveDistribution = LifecycleRestUtils.changeDistributionStatus(serviceDetails, null, governotUser, "approveService", DistributionStatusEnum.DISTRIBUTION_APPROVED);
+ if (approveDistribution.getErrorCode() == 200) {
+ distributionService = LifecycleRestUtils.changeDistributionStatus(serviceDetails, null, opsUser, "approveService", DistributionStatusEnum.DISTRIBUTED);
+ }
+
+ if (validateState) {
+ assertTrue(approveDistribution.getErrorCode() == ProductRestUtils.STATUS_CODE_SUCCESS);
+ assertTrue(distributionService.getErrorCode() == ProductRestUtils.STATUS_CODE_SUCCESS);
+ return distributionService;
+ }
+
+ return distributionService;
+
+ }
+
+ // *********** ARTIFACTS *****************
+
+ public static Either<ArtifactDefinition, RestResponse> uploadArtifactByType(ArtifactTypeEnum artifactType, Component component, UserRoleEnum userRole, Boolean deploymentTrue, Boolean validateState) throws Exception {
+
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ArtifactReqDetails artifactDetails = ElementFactory.getArtifactByType(null, artifactType, deploymentTrue);
+ if (deploymentTrue == false)
+ artifactDetails.setArtifactGroupType(ArtifactGroupTypeEnum.INFORMATIONAL.getType());
+ RestResponse uploadArtifactResp = ArtifactRestUtils.uploadArtifact(artifactDetails, component, defaultUser);
+
+ if (validateState) {
+ assertTrue("artifact upload failed: " + artifactDetails.getArtifactName(), uploadArtifactResp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ if (uploadArtifactResp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS) {
+ ArtifactDefinition artifactJavaObject = ResponseParser.convertArtifactDefinitionResponseToJavaObject(uploadArtifactResp.getResponse());
+ return Either.left(artifactJavaObject);
+ }
+ return Either.right(uploadArtifactResp);
+ }
+
+ // *********** CONTAINERS *****************
+ /**
+ * Adds Component instance to Component
+ *
+ * @param compInstParent
+ * @param compContainer
+ * @return
+ */
+ public static Either<ComponentInstance, RestResponse> addComponentInstanceToComponentContainer(Component compInstParent, Component compContainer) {
+ return addComponentInstanceToComponentContainer(compInstParent, compContainer, UserRoleEnum.DESIGNER, false);
+ }
+
+ public static Either<ComponentInstance, RestResponse> addComponentInstanceToComponentContainer(Component compInstParent, Component compContainer, UserRoleEnum userRole, Boolean validateState) {
+ try {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ComponentInstanceReqDetails componentInstanceDetails = ElementFactory.getComponentInstance(compInstParent);
+ RestResponse createComponentInstance = ComponentInstanceRestUtils.createComponentInstance(componentInstanceDetails, defaultUser, compContainer);
+
+ if (validateState) {
+ assertTrue(createComponentInstance.getErrorCode() == ServiceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (createComponentInstance.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ ComponentInstance componentInstance = ResponseParser.convertComponentInstanceResponseToJavaObject(createComponentInstance.getResponse());
+ return Either.left(componentInstance);
+ }
+ return Either.right(createComponentInstance);
+ } catch (Exception e) {
+ throw new AtomicOperationException(e);
+ }
+ }
+
+ public static Resource getResourceObject(Component containerDetails, UserRoleEnum userRole) throws Exception {
+ // User defaultUser = ElementFactory.getDefaultUser(userRole);
+ RestResponse restResponse = ResourceRestUtils.getResource(containerDetails.getUniqueId());
+ Resource container = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ return container;
+ }
+
+ public static Service getServiceObject(Component containerDetails, UserRoleEnum userRole) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ RestResponse serviceResponse = ServiceRestUtils.getService(containerDetails.getUniqueId(), defaultUser);
+ Service container = ResponseParser.convertServiceResponseToJavaObject(serviceResponse.getResponse());
+ return container;
+ }
+
+ public static Product getProductObject(Component containerDetails, UserRoleEnum userRole) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ RestResponse productRest = ProductRestUtils.getProduct(containerDetails.getUniqueId(), defaultUser.getUserId());
+ Product container = ResponseParser.convertProductResponseToJavaObject(productRest.getResponse());
+ return container;
+ }
+
+ public static Component getCompoenntObject(Component containerDetails, UserRoleEnum userRole) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+
+ switch (containerDetails.getComponentType()) {
+ case RESOURCE:
+ RestResponse restResponse = ResourceRestUtils.getResource(containerDetails.getUniqueId());
+ containerDetails = ResponseParser.convertResourceResponseToJavaObject(restResponse.getResponse());
+ break;
+ case SERVICE:
+ RestResponse serviceResponse = ServiceRestUtils.getService(containerDetails.getUniqueId(), defaultUser);
+ containerDetails = ResponseParser.convertServiceResponseToJavaObject(serviceResponse.getResponse());
+ break;
+ case PRODUCT:
+ RestResponse productRest = ProductRestUtils.getProduct(containerDetails.getUniqueId(), defaultUser.getUserId());
+ containerDetails = ResponseParser.convertProductResponseToJavaObject(productRest.getResponse());
+ break;
+ default:
+ break;
+ }
+ return containerDetails;
+ }
+
+ public static Component convertReposnseToComponentObject(Component containerDetails, RestResponse restresponse) throws Exception {
+
+ switch (containerDetails.getComponentType()) {
+ case RESOURCE:
+ containerDetails = ResponseParser.convertResourceResponseToJavaObject(restresponse.getResponse());
+ break;
+ case SERVICE:
+ containerDetails = ResponseParser.convertServiceResponseToJavaObject(restresponse.getResponse());
+ break;
+ case PRODUCT:
+ containerDetails = ResponseParser.convertProductResponseToJavaObject(restresponse.getResponse());
+ break;
+ default:
+ break;
+ }
+ return containerDetails;
+ }
+
+ public static RestResponse associate2ResourceInstances(Component containerDetails, ComponentInstance fromNode, ComponentInstance toNode, String assocType, UserRoleEnum userRole, Boolean validateState) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ RestResponse associate2ResourceInstancesResponse = ResourceRestUtils.associate2ResourceInstances(containerDetails, fromNode, toNode, assocType, defaultUser);
+
+ if (validateState) {
+ assertTrue(associate2ResourceInstancesResponse.getErrorCode() == ServiceRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ return associate2ResourceInstancesResponse;
+ }
+
+ public static Either<Pair<Component, ComponentInstance>, RestResponse> changeComponentInstanceVersion(Component containerDetails, ComponentInstance componentInstanceToReplace, Component newInstance, UserRoleEnum userRole, Boolean validateState)
+ throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+
+ RestResponse changeComponentInstanceVersionResp = ComponentInstanceRestUtils.changeComponentInstanceVersion(containerDetails, componentInstanceToReplace, newInstance, defaultUser);
+ if (validateState) {
+ assertTrue("change ComponentInstance version failed: " + changeComponentInstanceVersionResp.getResponseMessage(), changeComponentInstanceVersionResp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ if (changeComponentInstanceVersionResp.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS) {
+
+ Component compoenntObject = AtomicOperationUtils.getCompoenntObject(containerDetails, userRole);
+ ComponentInstance componentInstanceJavaObject = ResponseParser.convertComponentInstanceResponseToJavaObject(changeComponentInstanceVersionResp.getResponse());
+
+ return Either.left(Pair.of(compoenntObject, componentInstanceJavaObject));
+ }
+
+ return Either.right(changeComponentInstanceVersionResp);
+ }
+
+ // *********** PROPERTIES *****************
+
+ public static Either<ComponentInstanceProperty, RestResponse> addCustomPropertyToResource(PropertyReqDetails propDetails, Resource resourceDetails, UserRoleEnum userRole, Boolean validateState) throws Exception {
+
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ Map<String, PropertyReqDetails> propertyToSend = new HashMap<String, PropertyReqDetails>();
+ propertyToSend.put(propDetails.getName(), propDetails);
+ Gson gson = new Gson();
+ RestResponse addPropertyResponse = PropertyRestUtils.createProperty(resourceDetails.getUniqueId(), gson.toJson(propertyToSend), defaultUser);
+
+ if (validateState) {
+ assertTrue("add property to resource failed: " + addPropertyResponse.getErrorCode(), addPropertyResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (addPropertyResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_CREATED) {
+ ComponentInstanceProperty compInstProp = null;
+ String property = ResponseParser.getJsonObjectValueByKey(addPropertyResponse.getResponse(), propDetails.getName());
+ compInstProp = (ResponseParser.convertPropertyResponseToJavaObject(property));
+ return Either.left(compInstProp);
+ }
+ return Either.right(addPropertyResponse);
+ }
+
+ // Benny
+ public static Either<ComponentInstanceProperty, RestResponse> updatePropertyOfResource(PropertyReqDetails propDetails, Resource resourceDetails, String propertyUniqueId, UserRoleEnum userRole, Boolean validateState) throws Exception {
+
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ Map<String, PropertyReqDetails> propertyToSend = new HashMap<String, PropertyReqDetails>();
+ propertyToSend.put(propDetails.getName(), propDetails);
+ Gson gson = new Gson();
+ RestResponse addPropertyResponse = PropertyRestUtils.updateProperty(resourceDetails.getUniqueId(), propertyUniqueId, gson.toJson(propertyToSend), defaultUser);
+
+ if (validateState) {
+ assertTrue("add property to resource failed: " + addPropertyResponse.getResponseMessage(), addPropertyResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ }
+
+ if (addPropertyResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_SUCCESS) {
+ ComponentInstanceProperty compInstProp = null;
+ String property = ResponseParser.getJsonObjectValueByKey(addPropertyResponse.getResponse(), propDetails.getName());
+ compInstProp = (ResponseParser.convertPropertyResponseToJavaObject(property));
+ return Either.left(compInstProp);
+ }
+ return Either.right(addPropertyResponse);
+ }
+
+ public static RestResponse deletePropertyOfResource(String resourceId, String propertyId, UserRoleEnum userRole) throws Exception {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ return PropertyRestUtils.deleteProperty(resourceId, propertyId, defaultUser);
+ }
+
+ public static Either<ComponentInstanceProperty, RestResponse> addDefaultPropertyToResource(PropertyTypeEnum propertyType, Resource resourceDetails, UserRoleEnum userRole, Boolean validateState) throws Exception {
+
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ PropertyReqDetails propDetails = ElementFactory.getPropertyDetails(propertyType);
+ Map<String, PropertyReqDetails> propertyToSend = new HashMap<String, PropertyReqDetails>();
+ propertyToSend.put(propDetails.getName(), propDetails);
+ Gson gson = new Gson();
+ RestResponse addPropertyResponse = PropertyRestUtils.createProperty(resourceDetails.getUniqueId(), gson.toJson(propertyToSend), defaultUser);
+
+ if (validateState) {
+ assertTrue("add property to resource failed: " + addPropertyResponse.getResponseMessage(), addPropertyResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (addPropertyResponse.getErrorCode() == BaseRestUtils.STATUS_CODE_CREATED) {
+ ComponentInstanceProperty compInstProp = null;
+ String property = ResponseParser.getJsonObjectValueByKey(addPropertyResponse.getResponse(), propDetails.getName());
+ compInstProp = (ResponseParser.convertPropertyResponseToJavaObject(property));
+
+ return Either.left(compInstProp);
+ }
+ return Either.right(addPropertyResponse);
+ }
+
+ public static RestResponse createDefaultConsumer(Boolean validateState) {
+ try {
+ ConsumerDataDefinition defaultConsumerDefinition = ElementFactory.getDefaultConsumerDetails();
+ RestResponse createResponse = ConsumerRestUtils.createConsumer(defaultConsumerDefinition, ElementFactory.getDefaultUser(UserRoleEnum.ADMIN));
+ BaseRestUtils.checkCreateResponse(createResponse);
+
+ if (validateState) {
+ assertTrue(createResponse.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED);
+ }
+ return createResponse;
+ } catch (Exception e) {
+ throw new AtomicOperationException(e);
+ }
+ }
+
+ /**
+ * Builds Resource From rest response
+ *
+ * @param resourceResp
+ * @return
+ */
+ public static Either<Resource, RestResponse> buildResourceFromResponse(RestResponse resourceResp) {
+ Either<Resource, RestResponse> result;
+ if (resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ Resource resourceResponseObject = ResponseParser.convertResourceResponseToJavaObject(resourceResp.getResponse());
+ result = Either.left(resourceResponseObject);
+ } else {
+ result = Either.right(resourceResp);
+ }
+ return result;
+ }
+
+ private static class AtomicOperationException extends RuntimeException {
+ private AtomicOperationException(Exception e) {
+ super(e);
+ }
+
+ private static final long serialVersionUID = 1L;
+ };
+
+ /**
+ * Import resource from CSAR
+ *
+ * @param resourceType
+ * @param userRole
+ * @param fileName
+ * @param filePath
+ * @return Resource
+ * @throws Exception
+ */
+ public static Resource importResourceFromCSAR(ResourceTypeEnum resourceType, UserRoleEnum userRole, String fileName, String... filePath) throws Exception {
+ // Get the CSARs path
+ String realFilePath = System.getProperty("user.dir") + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "CI" + File.separator + "csars";
+ if (filePath != null && filePath.length > 0) {
+ realFilePath = filePath.toString();
+ }
+
+ // Create default import resource & user
+ ImportReqDetails resourceDetails = ElementFactory.getDefaultImportResource();
+ User sdncModifierDetails = ElementFactory.getDefaultUser(userRole);
+
+ byte[] data = null;
+ Path path = Paths.get(realFilePath + File.separator + fileName);
+ data = Files.readAllBytes(path);
+ String payloadName = fileName;
+ String payloadData = Base64.encodeBase64String(data);
+ resourceDetails.setPayloadData(payloadData);
+ resourceDetails.setCsarUUID(payloadName);
+ resourceDetails.setPayloadName(payloadName);
+ resourceDetails.setResourceType(resourceType.name());
+
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncModifierDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ return ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ };
+
+ public static Either<Resource, RestResponse> importResourceByFileName(ResourceTypeEnum resourceType, UserRoleEnum userRole, String fileName, Boolean validateState, String... filePath) throws IOException {
+
+ String realFilePath = System.getProperty("user.dir") + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "CI" + File.separator + "csars";
+ if (filePath != null && filePath.length > 0) {
+ realFilePath = filePath.toString();
+ }
+
+ try {
+ User defaultUser = ElementFactory.getDefaultUser(userRole);
+ ResourceReqDetails defaultResource = ElementFactory.getDefaultResource(defaultUser);
+ ImportReqDetails defaultImportResource = ElementFactory.getDefaultImportResource(defaultResource);
+ ImportUtils.getImportResourceDetailsByPathAndName(defaultImportResource, realFilePath, fileName);
+ RestResponse resourceResp = ResourceRestUtils.createResource(defaultImportResource, defaultUser);
+
+ if (validateState) {
+ assertTrue(resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED);
+ }
+
+ if (resourceResp.getErrorCode() == ResourceRestUtils.STATUS_CODE_CREATED) {
+ Resource resourceResponseObject = ResponseParser.convertResourceResponseToJavaObject(resourceResp.getResponse());
+ return Either.left(resourceResponseObject);
+ }
+ return Either.right(resourceResp);
+ } catch (Exception e) {
+ throw new AtomicOperationException(e);
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/Convertor.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/Convertor.java
new file mode 100644
index 0000000000..4aa13b48e4
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/Convertor.java
@@ -0,0 +1,309 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.general;
+
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_CREATED;
+import static org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils.STATUS_CODE_SUCCESS;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedUserCRUDAudit;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+
+public class Convertor {
+ // ***** resource *****
+ public static ResourceRespJavaObject constructFieldsForRespValidation(ResourceReqDetails resourceDetails,
+ String resourceVersion) {
+ return convertToRespObject(resourceDetails, resourceVersion, UserRoleEnum.ADMIN.getUserId(),
+ UserRoleEnum.ADMIN.getUserName());
+
+ }
+
+ public static ResourceRespJavaObject constructFieldsForRespValidation(ResourceReqDetails resourceDetails) {
+ return convertToRespObject(resourceDetails, resourceDetails.getVersion(), UserRoleEnum.ADMIN.getUserId(),
+ UserRoleEnum.ADMIN.getUserName());
+
+ }
+
+ public static ResourceRespJavaObject constructFieldsForRespValidation(ResourceReqDetails resourceDetails,
+ String resourceVersion, User user) {
+ return convertToRespObject(resourceDetails, resourceVersion, user.getUserId(), user.getFullName());
+
+ }
+
+ private static ResourceRespJavaObject convertToRespObject(ResourceReqDetails resourceDetails,
+ String resourceVersion, String userId, String userName) {
+ ResourceRespJavaObject resourceRespJavaObject = new ResourceRespJavaObject();
+
+ resourceRespJavaObject.setUniqueId(resourceDetails.getUniqueId());
+ resourceRespJavaObject.setName(resourceDetails.getName());
+ resourceRespJavaObject.setCreatorUserId(resourceDetails.getCreatorUserId());
+ resourceRespJavaObject.setCreatorFullName(resourceDetails.getCreatorFullName());
+ resourceRespJavaObject.setLastUpdaterUserId(userId);
+ resourceRespJavaObject.setLastUpdaterFullName(userName);
+ resourceRespJavaObject.setDescription(resourceDetails.getDescription());
+ resourceRespJavaObject.setIcon(resourceDetails.getIcon());
+ resourceRespJavaObject.setTags(resourceDetails.getTags());
+ resourceRespJavaObject.setIsHighestVersion("true");
+ resourceRespJavaObject.setCategories(resourceDetails.getCategories());
+ resourceRespJavaObject.setLifecycleState(
+ resourceDetails.getLifecycleState() != null ? resourceDetails.getLifecycleState().toString()
+ : LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.toString());
+ // resourceRespJavaObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceRespJavaObject.setDerivedFrom(resourceDetails.getDerivedFrom());
+ resourceRespJavaObject.setVendorName(resourceDetails.getVendorName());
+ resourceRespJavaObject.setVendorRelease(resourceDetails.getVendorRelease());
+ resourceRespJavaObject.setContactId(resourceDetails.getContactId());
+ resourceRespJavaObject.setAbstractt("false");
+ resourceRespJavaObject.setVersion(resourceVersion);
+ resourceRespJavaObject.setCost(resourceDetails.getCost());
+ resourceRespJavaObject.setLicenseType(resourceDetails.getLicenseType());
+ resourceRespJavaObject.setResourceType(resourceDetails.getResourceType());
+
+ return resourceRespJavaObject;
+
+ }
+
+ // ********** product **************
+
+ public static Product constructFieldsForRespValidation(ProductReqDetails productDetails, String productVersion,
+ User user) {
+ return convertToRespObject(productDetails, productVersion, user.getUserId(), user.getFullName());
+ }
+
+ private static Product convertToRespObject(ProductReqDetails productDetails, String productVersion, String userId,
+ String userName) {
+ Product expectedProduct = new Product();
+
+ expectedProduct.setUniqueId(productDetails.getUniqueId());
+ expectedProduct.setName(productDetails.getName());
+ expectedProduct.setFullName(productDetails.getFullName());
+ expectedProduct.setCreatorUserId(productDetails.getCreatorUserId());
+ expectedProduct.setCreatorFullName(productDetails.getCreatorFullName());
+ expectedProduct.setLastUpdaterUserId(userId);
+ expectedProduct.setLastUpdaterFullName(userName);
+ expectedProduct.setDescription(productDetails.getDescription());
+ // expectedProduct.setIcon(resourceDetails.getIcon());
+ expectedProduct.setTags(productDetails.getTags());
+ expectedProduct.setHighestVersion(true);
+ List<CategoryDefinition> categories = productDetails.getCategories();
+ if (categories == null) {
+ categories = new ArrayList<>();
+ }
+ expectedProduct.setCategories(categories);
+ expectedProduct.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ expectedProduct.setVersion(productVersion);
+ expectedProduct.setContacts(productDetails.getContacts());
+ return expectedProduct;
+ }
+
+ // ***** audit *****
+
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(ResourceReqDetails resourceDetails,
+ String resourceVersion) {
+ return convertToAuditObject(resourceDetails, resourceVersion, UserRoleEnum.ADMIN.getUserId(),
+ UserRoleEnum.ADMIN.getUserName());
+ }
+
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(
+ ResourceReqDetails resourceDetails) {
+ return convertToAuditObject(resourceDetails, resourceDetails.getVersion(), UserRoleEnum.ADMIN.getUserId(),
+ UserRoleEnum.ADMIN.getUserName());
+ }
+
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(ResourceReqDetails resourceDetails,
+ String resourceVersion, User user) {
+ return convertToAuditObject(resourceDetails, resourceVersion, user.getUserId(), user.getFullName());
+ }
+
+ private static ExpectedResourceAuditJavaObject convertToAuditObject(ResourceReqDetails resourceDetails,
+ String resourceVersion, String userId, String userName) {
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+
+ expectedResourceAuditJavaObject.setAction("Checkout");
+ expectedResourceAuditJavaObject.setModifierName(userName);
+ expectedResourceAuditJavaObject.setModifierUid(userId);
+ expectedResourceAuditJavaObject.setStatus("200.0");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setResourceName(resourceDetails.getName());
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion(String.valueOf(Float.parseFloat(resourceVersion) - 0.1f));
+ expectedResourceAuditJavaObject.setCurrVersion(resourceVersion);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setComment(null);
+
+ return expectedResourceAuditJavaObject;
+ }
+
+ public static ExpectedProductAudit constructFieldsForAuditValidation(Product productDetails, String action,
+ User user, ActionStatus actionStatus, String prevVersion, String currVersion, LifecycleStateEnum prevState,
+ LifecycleStateEnum currState, String uuid, String... errorMessageParams) throws FileNotFoundException {
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(actionStatus.name());
+ return convertToAuditObject(productDetails, action, user, errorInfo, prevVersion, currVersion, prevState,
+ currState, uuid, errorMessageParams);
+ }
+
+ private static ExpectedProductAudit convertToAuditObject(Product productDetails, String action, User user,
+ ErrorInfo errorInfo, String prevVersion, String currVersion, LifecycleStateEnum prevState,
+ LifecycleStateEnum currState, String uuid, String... errorMessageParams) {
+ ExpectedProductAudit expectedProductAudit = new ExpectedProductAudit();
+
+ expectedProductAudit.setACTION(action);
+ String userId = user.getUserId();
+ String userFullName;
+ if (StringUtils.isEmpty(user.getFirstName()) && StringUtils.isEmpty(user.getLastName())) {
+ userFullName = "";
+ } else {
+ userFullName = user.getFullName();
+ }
+ if (StringUtils.isEmpty(userId)) {
+ userId = "UNKNOWN";
+ }
+ expectedProductAudit.setMODIFIER(
+ !StringUtils.isEmpty(userFullName) ? userFullName + "(" + userId + ")" : "(" + userId + ")");
+ expectedProductAudit.setSTATUS(Integer.toString(errorInfo.getCode()));
+ expectedProductAudit.setDESC(errorInfo.getAuditDesc((Object[]) (errorMessageParams)));
+ expectedProductAudit
+ .setRESOURCE_NAME(productDetails != null ? productDetails.getName() : Constants.EMPTY_STRING);
+ expectedProductAudit.setRESOURCE_TYPE("Product");
+ expectedProductAudit.setPREV_VERSION(prevVersion);
+ expectedProductAudit.setCURR_VERSION(currVersion);
+ expectedProductAudit.setPREV_STATE(prevState != null ? prevState.name() : Constants.EMPTY_STRING);
+ expectedProductAudit.setCURR_STATE(currState != null ? currState.name() : Constants.EMPTY_STRING);
+ expectedProductAudit.setSERVICE_INSTANCE_ID(uuid);
+ return expectedProductAudit;
+ }
+
+ ////////////////
+ // Convertor.constructFieldsForAuditValidationSuccess(addUser,
+ //////////////// sdncAdminUser, mechIdUser, null, STATUS_CODE_CREATED);
+ public static ExpectedUserCRUDAudit constructFieldsForAuditValidation(String action, User userModifier,
+ ActionStatus actionStatus, User userAfter, User userBefore, Object... variables) throws Exception {
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(actionStatus.name());
+ ExpectedUserCRUDAudit expectedAddUserAuditJavaObject = new ExpectedUserCRUDAudit();
+ expectedAddUserAuditJavaObject.setAction(action);
+ expectedAddUserAuditJavaObject.setModifier(
+ userModifier.getFirstName() != null ? userModifier.getFullName() + "(" + userModifier.getUserId() + ")"
+ : "(" + userModifier.getUserId() + ")");
+ String status = Integer.toString(errorInfo.getCode());
+ expectedAddUserAuditJavaObject.setStatus(status);
+ if (errorInfo.getCode() == STATUS_CODE_SUCCESS || errorInfo.getCode() == STATUS_CODE_CREATED) {
+ expectedAddUserAuditJavaObject.setDesc("OK");
+ } else {
+ expectedAddUserAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ }
+ expectedAddUserAuditJavaObject.setUserBefore(userBefore != null
+ ? userBefore.getUserId() + ", " + userBefore.getFirstName() + " " + userBefore.getLastName() + ", "
+ + userBefore.getEmail() + ", " + userBefore.getRole()
+ : Constants.EMPTY_STRING);
+ expectedAddUserAuditJavaObject.setUserAfter(userAfter != null
+ ? userAfter.getUserId() + ", " + userAfter.getFirstName() + " " + userAfter.getLastName() + ", "
+ + userAfter.getEmail() + ", " + userAfter.getRole()
+ : Constants.EMPTY_STRING);
+ return expectedAddUserAuditJavaObject;
+ }
+
+ // For RESOURCE and SERVICE same Audit
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(
+ ComponentReqDetails componentDetails, String action, User userModifier, ActionStatus actionStatus,
+ String currVersion, String prevVersion, String curState, String prevState, String uuid, String comment,
+ Object... variables) throws Exception {
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(actionStatus.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(action);
+ expectedResourceAuditJavaObject.setMODIFIER(
+ userModifier.getFirstName() != null ? userModifier.getFullName() + "(" + userModifier.getUserId() + ")"
+ : "(" + userModifier.getUserId() + ")");
+ String status = Integer.toString(errorInfo.getCode());
+ expectedResourceAuditJavaObject.setStatus(status);
+ if (errorInfo.getCode() == STATUS_CODE_SUCCESS || errorInfo.getCode() == STATUS_CODE_CREATED) {
+ expectedResourceAuditJavaObject.setDesc("OK");
+ } else {
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ }
+ expectedResourceAuditJavaObject.setCurrState(curState);
+ expectedResourceAuditJavaObject.setPrevState(prevState);
+ expectedResourceAuditJavaObject.setCurrVersion(currVersion);
+ expectedResourceAuditJavaObject.setPrevVersion(prevVersion);
+ expectedResourceAuditJavaObject.setComment(comment);
+ expectedResourceAuditJavaObject.setSERVICE_INSTANCE_ID(uuid);
+ if (componentDetails instanceof ServiceReqDetails) {
+ expectedResourceAuditJavaObject.setResourceName(((ServiceReqDetails) componentDetails).getName());
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ }
+ if (componentDetails instanceof ResourceReqDetails) {
+ expectedResourceAuditJavaObject.setResourceName(((ResourceReqDetails) componentDetails).getName());
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ }
+ return expectedResourceAuditJavaObject;
+ }
+
+ // Distribution Service Audit
+ public static ExpectedResourceAuditJavaObject constructFieldsForDistributionAuditValidation(
+ ComponentReqDetails componentDetails, String action, User userModifier, ActionStatus actionStatus,
+ String currVersion, String distCurrStatus, String distProvStatus, String curState, String uuid,
+ String comment, Object... variables) throws Exception {
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(actionStatus.name());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(action);
+ expectedResourceAuditJavaObject.setMODIFIER(
+ userModifier.getFirstName() != null ? userModifier.getFullName() + "(" + userModifier.getUserId() + ")"
+ : "(" + userModifier.getUserId() + ")");
+ String status = Integer.toString(errorInfo.getCode());
+ expectedResourceAuditJavaObject.setStatus(status);
+ if (errorInfo.getCode() == STATUS_CODE_SUCCESS || errorInfo.getCode() == STATUS_CODE_CREATED) {
+ expectedResourceAuditJavaObject.setDesc("OK");
+ } else {
+ expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ }
+ expectedResourceAuditJavaObject.setCurrState(curState);
+ expectedResourceAuditJavaObject.setCurrVersion(currVersion);
+ expectedResourceAuditJavaObject.setDcurrStatus(distCurrStatus);
+ expectedResourceAuditJavaObject.setDprevStatus(distProvStatus);
+ expectedResourceAuditJavaObject.setComment(comment);
+ expectedResourceAuditJavaObject.setSERVICE_INSTANCE_ID(uuid);
+ if (componentDetails instanceof ServiceReqDetails) {
+ expectedResourceAuditJavaObject.setResourceName(((ServiceReqDetails) componentDetails).getName());
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ }
+ return expectedResourceAuditJavaObject;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ElementFactory.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ElementFactory.java
new file mode 100644
index 0000000000..ba2ebd9b4e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ElementFactory.java
@@ -0,0 +1,873 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.general;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.lang.StringUtils;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.PropertyTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedExternalAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class ElementFactory {
+
+ private static String DEFAULT_ARTIFACT_LABEL = "artifact1";
+ private static final String RESOURCE_INSTANCE_POS_X = "20";
+ private static final String RESOURCE_INSTANCE_POS_Y = "20";
+ private static final String RESOURCE_INSTANCE_DESCRIPTION = "description";
+
+ // *** RESOURCE ***
+
+ public static ResourceReqDetails getDefaultResource() {
+ return getDefaultResource("ciRes", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, "jh0003");
+ }
+
+ public static ResourceReqDetails getDefaultResource(ResourceCategoryEnum category) {
+ return getDefaultResource("ciRes", NormativeTypesEnum.ROOT, category, "jh0003");
+ }
+
+ public static ResourceReqDetails getDefaultResource(String contactId) {
+ return getDefaultResource("ciRes", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, contactId);
+ }
+
+ public static ResourceReqDetails getDefaultResource(User modifier) {
+ return getDefaultResource("ciRes", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, modifier.getUserId());
+ }
+
+ public static ResourceReqDetails getDefaultResource(NormativeTypesEnum derivedFrom, ResourceCategoryEnum category) {
+ return getDefaultResource("ciRes", derivedFrom, category, "jh0003");
+ }
+
+ public static ResourceReqDetails getDefaultResource(NormativeTypesEnum derivedFrom) {
+ return getDefaultResource("ciRes", derivedFrom, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, "jh0003");
+ }
+
+ public static ResourceReqDetails getDefaultResource(String resourceName, NormativeTypesEnum derivedFrom) {
+ return getDefaultResource(resourceName, derivedFrom, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, "jh0003");
+ }
+
+ public static ResourceReqDetails getDefaultResource(NormativeTypesEnum derivedFrom, String contactId) {
+ return getDefaultResource("ciRes", derivedFrom, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, contactId);
+ }
+
+ // New
+ public static ResourceReqDetails getDefaultResourceByType(ResourceTypeEnum ResourceType, String resourceName) {
+ return getDefaultResourceByType(resourceName, NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, "jh0003", ResourceType.toString());
+ }
+
+ public static ResourceReqDetails getDefaultResourceByType(ResourceTypeEnum ResourceType, User user) {
+ return getDefaultResourceByType("ciRes", NormativeTypesEnum.ROOT, ResourceCategoryEnum.GENERIC_INFRASTRUCTURE, user.getUserId(), ResourceType.toString());
+ }
+
+ public static ResourceReqDetails getDefaultResourceByTypeNormTypeAndCatregory(ResourceTypeEnum resourceType, NormativeTypesEnum normativeTypes, ResourceCategoryEnum resourceCategory, User user) {
+ return getDefaultResourceByType("ciRes", normativeTypes, resourceCategory, user.getUserId(), resourceType.toString());
+ }
+
+ public static PropertyReqDetails getDefaultMapProperty(PropertyTypeEnum innerType) {
+ return getPropertyDetails(innerType);
+ }
+
+ public static PropertyReqDetails getDefaultMapProperty() {
+ return getPropertyDetails(PropertyTypeEnum.STRING_MAP);
+ }
+
+ public static ResourceReqDetails getDefaultResource(String resourceName, NormativeTypesEnum derived, ResourceCategoryEnum category, String contactId) {
+ resourceName = (resourceName + generateUUIDforSufix());
+ String description = "Represents a generic software component that can be managed and run by a Compute Node Type.";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(derived.normativeName);
+ String vendorName = "ATT (Tosca)";
+ String vendorRelease = "1.0.0.wd03";
+ String icon = "defaulticon";
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(category.getCategory(), category.getSubCategory());
+
+ return resourceDetails;
+
+ }
+
+ public static ResourceReqDetails getDefaultResourceByTypeNormTypeAndCatregory(ResourceTypeEnum resourceType, Resource normativeTypes, ResourceCategoryEnum resourceCategory, User user) {
+ return getDefaultResource("ciRes" + resourceType, normativeTypes, resourceCategory, user.getUserId());
+ }
+
+ public static ResourceReqDetails getDefaultResource(String resourceName, Resource derived, ResourceCategoryEnum category, String contactId) {
+ resourceName = (resourceName + generateUUIDforSufix());
+ String description = "Represents a generic software component that can be managed and run by a Compute Node Type.";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add(derived.getToscaResourceName());
+ String vendorName = "ATT (Tosca)";
+ String vendorRelease = "1.0.0.wd03";
+ String icon = "defaulticon";
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(category.getCategory(), category.getSubCategory());
+
+ return resourceDetails;
+
+ }
+
+ public static ResourceReqDetails getDefaultResourceByType(String resourceName, NormativeTypesEnum derived, ResourceCategoryEnum category, String contactId, ResourceTypeEnum resourceType) {
+ return getDefaultResourceByType(resourceName, derived, category, contactId, resourceType.toString());
+ }
+
+ // New
+ public static ResourceReqDetails getDefaultResourceByType(String resourceName, NormativeTypesEnum derived, ResourceCategoryEnum category, String contactId, String resourceType) {
+ resourceName = (resourceName + resourceType + generateUUIDforSufix());
+ String description = "Represents a generic software component that can be managed and run by a Compute Node Type.";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ ArrayList<String> derivedFrom = null;
+ if (derived != null) {
+ derivedFrom = new ArrayList<String>();
+ derivedFrom.add(derived.normativeName);
+ }
+ String vendorName = "ATT (Tosca)";
+ String vendorRelease = "1.0.0.wd03";
+ String icon = "defaulticon";
+ ResourceReqDetails resourceDetails = new ResourceReqDetails(resourceName, description, resourceTags, null, derivedFrom, vendorName, vendorRelease, contactId, icon, resourceType.toString());
+ resourceDetails.addCategoryChain(category.getCategory(), category.getSubCategory());
+ return resourceDetails;
+ }
+
+ // New
+ public static ImportReqDetails getDefaultImportResourceByType(String resourceName, NormativeTypesEnum derived, ResourceCategoryEnum category, String contactId, String resourceType) {
+ resourceName = (resourceName + resourceType + generateUUIDforSufix());
+ String description = "Represents a generic software component that can be managed and run by a Compute Node Type.";
+ ArrayList<String> resourceTags = new ArrayList<String>();
+ resourceTags.add(resourceName);
+ ArrayList<String> derivedFrom = null;
+ if (derived != null) {
+ derivedFrom = new ArrayList<String>();
+ derivedFrom.add(derived.normativeName);
+ }
+ String vendorName = "ATT (Tosca)";
+ String vendorRelease = "1.0.0.wd03";
+ String icon = "defaulticon";
+ ImportReqDetails resourceDetails = new ImportReqDetails(resourceName, description, resourceTags, derivedFrom, vendorName, vendorRelease, contactId, icon);
+ resourceDetails.addCategoryChain(category.getCategory(), category.getSubCategory());
+ return resourceDetails;
+ }
+ ////
+
+ public static ImportReqDetails getDefaultImportResource(ResourceReqDetails resourceReqDetails) {
+ ImportReqDetails importReqDetails = new ImportReqDetails(resourceReqDetails.getName(), resourceReqDetails.getDescription(), resourceReqDetails.getTags(), resourceReqDetails.getDerivedFrom(), resourceReqDetails.getVendorName(),
+ resourceReqDetails.getVendorRelease(), resourceReqDetails.getContactId(), resourceReqDetails.getIcon());
+ importReqDetails.setPayloadName("myCompute.yaml");
+ importReqDetails.setCategories(resourceReqDetails.getCategories());
+ importReqDetails.setPayloadData(
+ "dG9zY2FfZGVmaW5pdGlvbnNfdmVyc2lvbjogdG9zY2Ffc2ltcGxlX3lhbWxfMV8wXzANCm5vZGVfdHlwZXM6IA0KICBvcmcub3BlbmVjb21wLnJlc291cmNlLk15Q29tcHV0ZToNCiAgICBkZXJpdmVkX2Zyb206IHRvc2NhLm5vZGVzLlJvb3QNCiAgICBhdHRyaWJ1dGVzOg0KICAgICAgcHJpdmF0ZV9hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIHB1YmxpY19hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIG5ldHdvcmtzOg0KICAgICAgICB0eXBlOiBtYXANCiAgICAgICAgZW50cnlfc2NoZW1hOg0KICAgICAgICAgIHR5cGU6IHRvc2NhLmRhdGF0eXBlcy5uZXR3b3JrLk5ldHdvcmtJbmZvDQogICAgICBwb3J0czoNCiAgICAgICAgdHlwZTogbWFwDQogICAgICAgIGVudHJ5X3NjaGVtYToNCiAgICAgICAgICB0eXBlOiB0b3NjYS5kYXRhdHlwZXMubmV0d29yay5Qb3J0SW5mbw0KICAgIHJlcXVpcmVtZW50czoNCiAgICAgIC0gbG9jYWxfc3RvcmFnZTogDQogICAgICAgICAgY2FwYWJpbGl0eTogdG9zY2EuY2FwYWJpbGl0aWVzLkF0dGFjaG1lbnQNCiAgICAgICAgICBub2RlOiB0b3NjYS5ub2Rlcy5CbG9ja1N0b3JhZ2UNCiAgICAgICAgICByZWxhdGlvbnNoaXA6IHRvc2NhLnJlbGF0aW9uc2hpcHMuQXR0YWNoZXNUbw0KICAgICAgICAgIG9jY3VycmVuY2VzOiBbMCwgVU5CT1VOREVEXSAgDQogICAgY2FwYWJpbGl0aWVzOg0KICAgICAgaG9zdDogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5Db250YWluZXINCiAgICAgICAgdmFsaWRfc291cmNlX3R5cGVzOiBbdG9zY2Eubm9kZXMuU29mdHdhcmVDb21wb25lbnRdIA0KICAgICAgZW5kcG9pbnQgOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuRW5kcG9pbnQuQWRtaW4gDQogICAgICBvczogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5PcGVyYXRpbmdTeXN0ZW0NCiAgICAgIHNjYWxhYmxlOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuU2NhbGFibGUNCiAgICAgIGJpbmRpbmc6DQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5uZXR3b3JrLkJpbmRhYmxlDQo=");
+ return importReqDetails;
+ }
+
+ public static ImportReqDetails getDefaultImportResource() {
+ ResourceReqDetails resourceReqDetails = getDefaultResource();
+ ImportReqDetails importReqDetails = new ImportReqDetails(resourceReqDetails.getName(), resourceReqDetails.getDescription(), resourceReqDetails.getTags(), resourceReqDetails.getDerivedFrom(), resourceReqDetails.getVendorName(),
+ resourceReqDetails.getVendorRelease(), resourceReqDetails.getContactId(), resourceReqDetails.getIcon());
+ importReqDetails.setPayloadName("myCompute.yaml");
+ importReqDetails.setCategories(resourceReqDetails.getCategories());
+ importReqDetails.setPayloadData(
+ "dG9zY2FfZGVmaW5pdGlvbnNfdmVyc2lvbjogdG9zY2Ffc2ltcGxlX3lhbWxfMV8wXzANCm5vZGVfdHlwZXM6IA0KICBvcmcub3BlbmVjb21wLnJlc291cmNlLk15Q29tcHV0ZToNCiAgICBkZXJpdmVkX2Zyb206IHRvc2NhLm5vZGVzLlJvb3QNCiAgICBhdHRyaWJ1dGVzOg0KICAgICAgcHJpdmF0ZV9hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIHB1YmxpY19hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIG5ldHdvcmtzOg0KICAgICAgICB0eXBlOiBtYXANCiAgICAgICAgZW50cnlfc2NoZW1hOg0KICAgICAgICAgIHR5cGU6IHRvc2NhLmRhdGF0eXBlcy5uZXR3b3JrLk5ldHdvcmtJbmZvDQogICAgICBwb3J0czoNCiAgICAgICAgdHlwZTogbWFwDQogICAgICAgIGVudHJ5X3NjaGVtYToNCiAgICAgICAgICB0eXBlOiB0b3NjYS5kYXRhdHlwZXMubmV0d29yay5Qb3J0SW5mbw0KICAgIHJlcXVpcmVtZW50czoNCiAgICAgIC0gbG9jYWxfc3RvcmFnZTogDQogICAgICAgICAgY2FwYWJpbGl0eTogdG9zY2EuY2FwYWJpbGl0aWVzLkF0dGFjaG1lbnQNCiAgICAgICAgICBub2RlOiB0b3NjYS5ub2Rlcy5CbG9ja1N0b3JhZ2UNCiAgICAgICAgICByZWxhdGlvbnNoaXA6IHRvc2NhLnJlbGF0aW9uc2hpcHMuQXR0YWNoZXNUbw0KICAgICAgICAgIG9jY3VycmVuY2VzOiBbMCwgVU5CT1VOREVEXSAgDQogICAgY2FwYWJpbGl0aWVzOg0KICAgICAgaG9zdDogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5Db250YWluZXINCiAgICAgICAgdmFsaWRfc291cmNlX3R5cGVzOiBbdG9zY2Eubm9kZXMuU29mdHdhcmVDb21wb25lbnRdIA0KICAgICAgZW5kcG9pbnQgOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuRW5kcG9pbnQuQWRtaW4gDQogICAgICBvczogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5PcGVyYXRpbmdTeXN0ZW0NCiAgICAgIHNjYWxhYmxlOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuU2NhbGFibGUNCiAgICAgIGJpbmRpbmc6DQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5uZXR3b3JrLkJpbmRhYmxlDQo=");
+ return importReqDetails;
+ }
+
+ public static ImportReqDetails getDefaultImportResource(String name) {
+ ResourceReqDetails resourceReqDetails = getDefaultResourceByType(ResourceTypeEnum.VFC, name);
+ ImportReqDetails importReqDetails = new ImportReqDetails(resourceReqDetails.getName(), resourceReqDetails.getDescription(), resourceReqDetails.getTags(), resourceReqDetails.getDerivedFrom(), resourceReqDetails.getVendorName(),
+ resourceReqDetails.getVendorRelease(), resourceReqDetails.getContactId(), resourceReqDetails.getIcon());
+ importReqDetails.setPayloadName("myCompute.yaml");
+ importReqDetails.setCategories(resourceReqDetails.getCategories());
+ importReqDetails.setPayloadData(
+ "dG9zY2FfZGVmaW5pdGlvbnNfdmVyc2lvbjogdG9zY2Ffc2ltcGxlX3lhbWxfMV8wXzANCm5vZGVfdHlwZXM6IA0KICBvcmcub3BlbmVjb21wLnJlc291cmNlLk15Q29tcHV0ZToNCiAgICBkZXJpdmVkX2Zyb206IHRvc2NhLm5vZGVzLlJvb3QNCiAgICBhdHRyaWJ1dGVzOg0KICAgICAgcHJpdmF0ZV9hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIHB1YmxpY19hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIG5ldHdvcmtzOg0KICAgICAgICB0eXBlOiBtYXANCiAgICAgICAgZW50cnlfc2NoZW1hOg0KICAgICAgICAgIHR5cGU6IHRvc2NhLmRhdGF0eXBlcy5uZXR3b3JrLk5ldHdvcmtJbmZvDQogICAgICBwb3J0czoNCiAgICAgICAgdHlwZTogbWFwDQogICAgICAgIGVudHJ5X3NjaGVtYToNCiAgICAgICAgICB0eXBlOiB0b3NjYS5kYXRhdHlwZXMubmV0d29yay5Qb3J0SW5mbw0KICAgIHJlcXVpcmVtZW50czoNCiAgICAgIC0gbG9jYWxfc3RvcmFnZTogDQogICAgICAgICAgY2FwYWJpbGl0eTogdG9zY2EuY2FwYWJpbGl0aWVzLkF0dGFjaG1lbnQNCiAgICAgICAgICBub2RlOiB0b3NjYS5ub2Rlcy5CbG9ja1N0b3JhZ2UNCiAgICAgICAgICByZWxhdGlvbnNoaXA6IHRvc2NhLnJlbGF0aW9uc2hpcHMuQXR0YWNoZXNUbw0KICAgICAgICAgIG9jY3VycmVuY2VzOiBbMCwgVU5CT1VOREVEXSAgDQogICAgY2FwYWJpbGl0aWVzOg0KICAgICAgaG9zdDogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5Db250YWluZXINCiAgICAgICAgdmFsaWRfc291cmNlX3R5cGVzOiBbdG9zY2Eubm9kZXMuU29mdHdhcmVDb21wb25lbnRdIA0KICAgICAgZW5kcG9pbnQgOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuRW5kcG9pbnQuQWRtaW4gDQogICAgICBvczogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5PcGVyYXRpbmdTeXN0ZW0NCiAgICAgIHNjYWxhYmxlOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuU2NhbGFibGUNCiAgICAgIGJpbmRpbmc6DQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5uZXR3b3JrLkJpbmRhYmxlDQo=");
+ return importReqDetails;
+ }
+
+ // *** SERVICE ***
+ public static ServiceReqDetails getDefaultService() {
+ return getDefaultService("ciService", ServiceCategoriesEnum.MOBILITY, "al1976");
+ }
+
+ public static ServiceReqDetails getDefaultService(String contactId) {
+ return getDefaultService("ciService", ServiceCategoriesEnum.MOBILITY, contactId);
+ }
+
+ public static ServiceReqDetails getDefaultService(User user) {
+ return getDefaultService("ciService", ServiceCategoriesEnum.MOBILITY, user.getUserId());
+ }
+
+ public static ServiceReqDetails getService(ServiceCategoriesEnum category) {
+ return getDefaultService("ciService", category, "al1976");
+ }
+
+ public static ServiceReqDetails getDefaultService(ServiceCategoriesEnum category, User user) {
+ return getDefaultService("ciService", category, user.getUserId());
+ }
+
+ public static ServiceReqDetails getDefaultService(String serviceName, ServiceCategoriesEnum category, String contactId) {
+ serviceName = (serviceName + generateUUIDforSufix());
+ ArrayList<String> tags = new ArrayList<String>();
+ tags.add("serviceTag");
+ tags.add("serviceTag1");
+ tags.add(serviceName);
+ String description = "service Description";
+ String icon = "myIcon";
+
+ ServiceReqDetails serviceDetails = new ServiceReqDetails(serviceName, category.getValue(), tags, description, contactId, icon);
+
+ return serviceDetails;
+ }
+
+ // ***** PROPERTY ***
+
+ public static PropertyReqDetails getDefaultProperty() {
+ return getDefaultProperty("disk_size");
+ }
+
+ public static PropertyReqDetails getDefaultProperty(String propertyName) {
+ PropertyReqDetails property = new PropertyReqDetails();
+ property.setName(propertyName);
+ property.setPropertyType("integer");
+ property.setPropertyRequired(false);
+ property.setPropertyDefaultValue("12345");
+ property.setPropertyDescription("test property");
+ property.setPropertyRangeMax("500");
+ property.setPropertyRangeMin("100");
+ property.setPropertyPassword(false);
+ return property;
+ }
+
+ public static PropertyReqDetails getDefaultIntegerProperty() {
+ return getPropertyDetails(PropertyTypeEnum.INTEGER);
+ }
+
+ public static PropertyReqDetails getDefaultStringProperty() {
+ return getPropertyDetails(PropertyTypeEnum.STRING);
+ }
+
+ public static PropertyReqDetails getDefaultBooleanProperty() {
+ return getPropertyDetails(PropertyTypeEnum.BOOLEAN);
+ }
+
+ public static PropertyReqDetails getDefaultListProperty() {
+ return getPropertyDetails(PropertyTypeEnum.STRING_LIST);
+ }
+
+ public static PropertyReqDetails getDefaultListProperty(PropertyTypeEnum innerType) {
+ return getPropertyDetails(innerType);
+ }
+
+ public static PropertyReqDetails getPropertyDetails(PropertyTypeEnum propType) {
+ return new PropertyReqDetails(propType.getName(), propType.getType(), propType.getValue(), propType.getDescription(), propType.getSchemaDefinition());
+ }
+
+ // ***** RESOURCE INSTANCE ***
+ public static ComponentInstanceReqDetails getDefaultComponentInstance() {
+ return getDefaultComponentInstance("resourceInstanceName");
+ }
+
+ public static ComponentInstanceReqDetails getDefaultComponentInstance(String name) {
+ String resourceUid = "resourceId";
+ ComponentInstanceReqDetails resourceInstanceDetails = new ComponentInstanceReqDetails(resourceUid, RESOURCE_INSTANCE_DESCRIPTION, RESOURCE_INSTANCE_POS_X, RESOURCE_INSTANCE_POS_Y, name);
+
+ return resourceInstanceDetails;
+
+ }
+
+ public static ComponentInstanceReqDetails getDefaultComponentInstance(String name, ComponentReqDetails componentReqDetails) {
+ String resourceUid = componentReqDetails.getUniqueId();
+ ComponentInstanceReqDetails resourceInstanceDetails = new ComponentInstanceReqDetails(resourceUid, RESOURCE_INSTANCE_DESCRIPTION, RESOURCE_INSTANCE_POS_X, RESOURCE_INSTANCE_POS_Y, name);
+
+ return resourceInstanceDetails;
+
+ }
+
+ public static ComponentInstanceReqDetails getComponentResourceInstance(ComponentReqDetails compInstOriginDetails) {
+ String compInstName = (compInstOriginDetails.getName() != null ? compInstOriginDetails.getName() : "resourceInstanceName");
+ String resourceUid = compInstOriginDetails.getUniqueId();
+ ComponentInstanceReqDetails resourceInstanceDetails = new ComponentInstanceReqDetails(resourceUid, RESOURCE_INSTANCE_DESCRIPTION, RESOURCE_INSTANCE_POS_X, RESOURCE_INSTANCE_POS_Y, compInstName);
+ return resourceInstanceDetails;
+
+ }
+
+ public static ComponentInstanceReqDetails getComponentInstance(Component compInstOriginDetails) {
+ String compInstName = (compInstOriginDetails.getName() != null ? compInstOriginDetails.getName() : "componentInstanceName");
+ String compInsUid = compInstOriginDetails.getUniqueId();
+ ComponentInstanceReqDetails componentInstanceDetails = new ComponentInstanceReqDetails(compInsUid, RESOURCE_INSTANCE_DESCRIPTION, RESOURCE_INSTANCE_POS_X, RESOURCE_INSTANCE_POS_Y, compInstName);
+ return componentInstanceDetails;
+
+ }
+
+ // ******* USER **********************
+ public static User getDefaultUser(UserRoleEnum userRole) {
+ User sdncModifierDetails = new User();
+ sdncModifierDetails.setUserId(userRole.getUserId());
+ sdncModifierDetails.setFirstName(userRole.getFirstName());
+ sdncModifierDetails.setLastName(userRole.getLastName());
+ return sdncModifierDetails;
+ }
+
+ public static User getDefaultMechUser() {
+ User sdncMechUserDetails = new User();
+ sdncMechUserDetails.setUserId("m12345");
+ sdncMechUserDetails.setFirstName("Shay");
+ sdncMechUserDetails.setLastName("Sponder");
+ sdncMechUserDetails.setEmail("mechId@intl.sdc.com");
+ sdncMechUserDetails.setRole("DESIGNER");
+ return sdncMechUserDetails;
+ }
+
+ // ******* CONSUMER **********************
+
+ public static ConsumerDataDefinition getDefaultConsumerDetails() {
+ ConsumerDataDefinition consumer = new ConsumerDataDefinition();
+ consumer.setConsumerName("ci");
+ consumer.setConsumerSalt("2a1f887d607d4515d4066fe0f5452a50");
+ consumer.setConsumerPassword("0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b");
+ return consumer;
+ }
+
+ // *** ARTIFACT ***
+ public static ArtifactReqDetails getDefaultArtifact() throws IOException, Exception {
+ return getDefaultArtifact(DEFAULT_ARTIFACT_LABEL);
+ }
+
+ public static ArtifactReqDetails getDefaultArtifact(String artifactLabel) throws IOException, Exception {
+ List<String> artifactTypes = ResponseParser.getValuesFromJsonArray(ArtifactRestUtils.getArtifactTypesList());
+ String artifactType = artifactTypes.get(0);
+
+ return getDefaultArtifact(artifactLabel, artifactType);
+ }
+
+ public static ArtifactReqDetails getDefaultArtifact(String artifactLabel, String artifactType) throws IOException, Exception {
+
+ String artifactName = "testArtifact.sh";
+ String artifactDescription = "descriptionTest";
+ String payloadData = "dGVzdA=="; // content of file
+
+ ArtifactReqDetails artifactDetails = new ArtifactReqDetails(artifactName, artifactType, artifactDescription, payloadData, artifactLabel);
+ artifactDetails.setUrl("");
+ artifactDetails.setArtifactDisplayName(artifactLabel);
+ return artifactDetails;
+ }
+
+ public static ArtifactReqDetails getServiceApiArtifactDetails(String artifactLabel) throws IOException, Exception {
+ ArtifactReqDetails defaultArtifact = getDefaultArtifact(artifactLabel, "OTHER");
+ defaultArtifact.setUrl("http://www.apple.com");
+ defaultArtifact.setServiceApi(true);
+ defaultArtifact.setArtifactDisplayName(StringUtils.capitalize(defaultArtifact.getArtifactLabel()));
+ return defaultArtifact;
+ }
+
+ public static ArtifactReqDetails getDefaultDeploymentArtifactForType(String artifactType) throws IOException, Exception {
+ return getArtifactByType(DEFAULT_ARTIFACT_LABEL, artifactType, true);
+ }
+
+ public static ArtifactReqDetails getArtifactByType(ArtifactTypeEnum artifactLabel, ArtifactTypeEnum artifactType, Boolean deploymentTrue) throws IOException, Exception {
+ return getArtifactByType(DEFAULT_ARTIFACT_LABEL, artifactType.toString(), deploymentTrue);
+
+ }
+
+ public static ArtifactReqDetails getArtifactByType(String artifactLabel, String artifactType, Boolean deploymentTrue) throws IOException, Exception {
+ String artifactName;
+ String payloadData = null;
+ Integer timeout = null;
+ String url = "";
+ String artifactDescription = "descriptionTest";
+
+ // PLEASE NOTE!!!
+ // The non-default payloads here are real ones according to various
+ // types validations,
+ // Please don't change them unless you know what you are doing!
+
+ ArtifactTypeEnum artifactTypeEnum = ArtifactTypeEnum.findType(artifactType);
+
+ /*
+ * Missing file type: DCAE_JSON
+ */
+ switch (artifactTypeEnum) {
+ case DCAE_EVENT:
+ case APPC_CONFIG:
+ case DCAE_DOC:
+ case DCAE_TOSCA:
+ case HEAT:
+ case HEAT_NET:
+ case HEAT_VOL: {
+ artifactName = generateUUIDforSufix() + artifactType + "_install_apache2.yaml";
+ payloadData = "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ timeout = 60;
+ artifactLabel = normalizeArtifactLabel(artifactName);
+ break;
+ }
+ case DCAE_INVENTORY_TOSCA:
+ case DCAE_INVENTORY_JSON:
+ case DCAE_INVENTORY_POLICY:
+ case DCAE_INVENTORY_DOC:
+ case DCAE_INVENTORY_BLUEPRINT:
+ case DCAE_INVENTORY_EVENT: {
+ artifactName = getDcaeArtifactName(artifactTypeEnum, artifactType);
+ payloadData = "will be override later";
+ timeout = 60;
+ artifactLabel = normalizeArtifactLabel(artifactName);
+ break;
+ }
+ case MURANO_PKG: {
+ artifactName = artifactType + "org.openstack.Rally.zip";
+ payloadData = "ODM4MTRjNzkxZjcwYTlkMjk4ZGQ2ODE4MThmNjg0N2Y=";
+ break;
+ }
+ case DCAE_POLICY: {
+ artifactName = artifactType + "dcae_policy.emf";
+ payloadData = "will be override later";
+ break;
+ }
+ case DCAE_JSON: {
+ artifactName = artifactType + "dcae_policy.json";
+ payloadData = "e30=";
+ break;
+ }
+ case PUPPET:
+ case CHEF:
+ case DG_XML:
+ case YANG: {
+ artifactName = generateUUIDforSufix() + artifactType + "yangXml.xml";
+ payloadData = "PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8ZGF0YT4NCiAgPHNwb3J0cz4NCiAgICA8cGVyc29uPg0KICAgICAgPG5hbWU+TGlvbmVsIEFuZHJlcyBNZXNzaTwvbmFtZT4NCiAgICAgIDxiaXJ0aGRheT4xOTg3LTA2LTI0VDAwOjAwOjAwLTAwOjAwPC9iaXJ0aGRheT4NCiAgICA8L3BlcnNvbj4NCiAgICA8cGVyc29uPg0KICAgICAgPG5hbWU+Q3Jpc3RpYW5vIFJvbmFsZG88L25hbWU+DQogICAgICA8YmlydGhkYXk+MTk4NS0wMi0wNVQwMDowMDowMC0wMDowMDwvYmlydGhkYXk+DQogICAgPC9wZXJzb24+DQogICAgPHRlYW0+DQogICAgICA8bmFtZT5GQyBCYXJjZWxvbmE8L25hbWU+DQogICAgICA8cGxheWVyPg0KICAgICAgICA8bmFtZT5MaW9uZWwgQW5kcmVzIE1lc3NpPC9uYW1lPg0KICAgICAgICA8c2Vhc29uPkNoYW1waW9ucyBMZWFndWUgMjAxNC0yMDE1PC9zZWFzb24+DQogICAgICAgIDxudW1iZXI+MTA8L251bWJlcj4NCiAgICAgICAgPHNjb3Jlcz40Mzwvc2NvcmVzPg0KICAgICAgPC9wbGF5ZXI+DQogICAgPC90ZWFtPg0KICAgIDx0ZWFtPg0KICAgICAgPG5hbWU+UmVhbCBNYWRyaWQ8L25hbWU+DQogICAgICA8cGxheWVyPg0KICAgICAgICA8bmFtZT5DcmlzdGlhbm8gUm9uYWxkbzwvbmFtZT4NCiAgICAgICAgPHNlYXNvbj5DaGFtcGlvbnMgTGVhZ3VlIDIwMTQtMjAxNTwvc2Vhc29uPg0KICAgICAgICA8bnVtYmVyPjc8L251bWJlcj4NCiAgICAgICAgPHNjb3Jlcz40ODwvc2NvcmVzPg0KICAgICAgPC9wbGF5ZXI+DQogICAgPC90ZWFtPg0KICA8L3Nwb3J0cz4NCg0KPC9kYXRhPg==";
+ timeout = 15;
+ artifactLabel = normalizeArtifactLabel(artifactName);
+ break;
+ }
+ case VF_LICENSE:
+ case VENDOR_LICENSE:
+ case MODEL_INVENTORY_PROFILE:
+ case MODEL_QUERY_SPEC:
+ case VNF_CATALOG:
+ case YANG_XML: {
+ artifactName = generateUUIDforSufix() + artifactType + "yangXml.xml";
+ payloadData = "PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8ZGF0YT4NCiAgPHNwb3J0cz4NCiAgICA8cGVyc29uPg0KICAgICAgPG5hbWU+TGlvbmVsIEFuZHJlcyBNZXNzaTwvbmFtZT4NCiAgICAgIDxiaXJ0aGRheT4xOTg3LTA2LTI0VDAwOjAwOjAwLTAwOjAwPC9iaXJ0aGRheT4NCiAgICA8L3BlcnNvbj4NCiAgICA8cGVyc29uPg0KICAgICAgPG5hbWU+Q3Jpc3RpYW5vIFJvbmFsZG88L25hbWU+DQogICAgICA8YmlydGhkYXk+MTk4NS0wMi0wNVQwMDowMDowMC0wMDowMDwvYmlydGhkYXk+DQogICAgPC9wZXJzb24+DQogICAgPHRlYW0+DQogICAgICA8bmFtZT5GQyBCYXJjZWxvbmE8L25hbWU+DQogICAgICA8cGxheWVyPg0KICAgICAgICA8bmFtZT5MaW9uZWwgQW5kcmVzIE1lc3NpPC9uYW1lPg0KICAgICAgICA8c2Vhc29uPkNoYW1waW9ucyBMZWFndWUgMjAxNC0yMDE1PC9zZWFzb24+DQogICAgICAgIDxudW1iZXI+MTA8L251bWJlcj4NCiAgICAgICAgPHNjb3Jlcz40Mzwvc2NvcmVzPg0KICAgICAgPC9wbGF5ZXI+DQogICAgPC90ZWFtPg0KICAgIDx0ZWFtPg0KICAgICAgPG5hbWU+UmVhbCBNYWRyaWQ8L25hbWU+DQogICAgICA8cGxheWVyPg0KICAgICAgICA8bmFtZT5DcmlzdGlhbm8gUm9uYWxkbzwvbmFtZT4NCiAgICAgICAgPHNlYXNvbj5DaGFtcGlvbnMgTGVhZ3VlIDIwMTQtMjAxNTwvc2Vhc29uPg0KICAgICAgICA8bnVtYmVyPjc8L251bWJlcj4NCiAgICAgICAgPHNjb3Jlcz40ODwvc2NvcmVzPg0KICAgICAgPC9wbGF5ZXI+DQogICAgPC90ZWFtPg0KICA8L3Nwb3J0cz4NCg0KPC9kYXRhPg==";
+ timeout = 0;
+ artifactLabel = normalizeArtifactLabel(artifactName);
+ break;
+ }
+ case OTHER: {
+ artifactName = generateUUIDforSufix() + artifactType + "other.pdf";
+ payloadData = "aGVhdF90ZW1wbGF0ZV92ZXJzaW9uOiAyMDEzLTA1LTIzDQoNCmRlc2NyaXB0aW9uOiBTaW1wbGUgdGVtcGxhdGUgdG8gZGVwbG95IGEgc3RhY2sgd2l0aCB0d28gdmlydHVhbCBtYWNoaW5lIGluc3RhbmNlcw0KDQpwYXJhbWV0ZXJzOg0KICBpbWFnZV9uYW1lXzE6DQogICAgdHlwZTogc3RyaW5nDQogICAgbGFiZWw6IEltYWdlIE5hbWUNCiAgICBkZXNjcmlwdGlvbjogU0NPSU1BR0UgU3BlY2lmeSBhbiBpbWFnZSBuYW1lIGZvciBpbnN0YW5jZTENCiAgICBkZWZhdWx0OiBjaXJyb3MtMC4zLjEteDg2XzY0DQogIGltYWdlX25hbWVfMjoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogSW1hZ2UgTmFtZQ0KICAgIGRlc2NyaXB0aW9uOiBTQ09JTUFHRSBTcGVjaWZ5IGFuIGltYWdlIG5hbWUgZm9yIGluc3RhbmNlMg0KICAgIGRlZmF1bHQ6IGNpcnJvcy0wLjMuMS14ODZfNjQNCiAgbmV0d29ya19pZDoNCiAgICB0eXBlOiBzdHJpbmcNCiAgICBsYWJlbDogTmV0d29yayBJRA0KICAgIGRlc2NyaXB0aW9uOiBTQ09ORVRXT1JLIE5ldHdvcmsgdG8gYmUgdXNlZCBmb3IgdGhlIGNvbXB1dGUgaW5zdGFuY2UNCiAgICBoaWRkZW46IHRydWUNCiAgICBjb25zdHJhaW50czoNCiAgICAgIC0gbGVuZ3RoOiB7IG1pbjogNiwgbWF4OiA4IH0NCiAgICAgICAgZGVzY3JpcHRpb246IFBhc3N3b3JkIGxlbmd0aCBtdXN0IGJlIGJldHdlZW4gNiBhbmQgOCBjaGFyYWN0ZXJzLg0KICAgICAgLSByYW5nZTogeyBtaW46IDYsIG1heDogOCB9DQogICAgICAgIGRlc2NyaXB0aW9uOiBSYW5nZSBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3ZhbHVlczoNCiAgICAgICAgLSBtMS5zbWFsbA0KICAgICAgICAtIG0xLm1lZGl1bQ0KICAgICAgICAtIG0xLmxhcmdlDQogICAgICAgIGRlc2NyaXB0aW9uOiBBbGxvd2VkIHZhbHVlcyBkZXNjcmlwdGlvbg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbYS16QS1aMC05XSsiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IGNvbnNpc3Qgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycyBvbmx5Lg0KICAgICAgLSBhbGxvd2VkX3BhdHRlcm46ICJbQS1aXStbYS16QS1aMC05XSoiDQogICAgICAgIGRlc2NyaXB0aW9uOiBQYXNzd29yZCBtdXN0IHN0YXJ0IHdpdGggYW4gdXBwZXJjYXNlIGNoYXJhY3Rlci4NCiAgICAgIC0gY3VzdG9tX2NvbnN0cmFpbnQ6IG5vdmEua2V5cGFpcg0KICAgICAgICBkZXNjcmlwdGlvbjogQ3VzdG9tIGRlc2NyaXB0aW9uDQoNCnJlc291cmNlczoNCiAgbXlfaW5zdGFuY2UxOg0KICAgIHR5cGU6IE9TOjpOb3ZhOjpTZXJ2ZXINCiAgICBwcm9wZXJ0aWVzOg0KICAgICAgaW1hZ2U6IHsgZ2V0X3BhcmFtOiBpbWFnZV9uYW1lXzEgfQ0KICAgICAgZmxhdm9yOiBtMS5zbWFsbA0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9DQogIG15X2luc3RhbmNlMjoNCiAgICB0eXBlOiBPUzo6Tm92YTo6U2VydmVyDQogICAgcHJvcGVydGllczoNCiAgICAgIGltYWdlOiB7IGdldF9wYXJhbTogaW1hZ2VfbmFtZV8yIH0NCiAgICAgIGZsYXZvcjogbTEudGlueQ0KICAgICAgbmV0d29ya3M6DQogICAgICAgIC0gbmV0d29yayA6IHsgZ2V0X3BhcmFtIDogbmV0d29ya19pZCB9";
+ timeout = 0;
+ artifactLabel = normalizeArtifactLabel(artifactName);
+ break;
+ }
+ case SHELL_SCRIPT:
+ default: {// dummy
+ artifactName = generateUUIDforSufix() + "testArtifact.sh";
+ payloadData = "dGVzdA==";
+ artifactLabel = normalizeArtifactLabel(artifactName);
+ break;
+ }
+ }
+
+ ArtifactReqDetails artifactDetails = new ArtifactReqDetails(artifactName, artifactType, artifactDescription, payloadData, artifactLabel);
+ artifactDetails.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT.getType());
+ artifactDetails.setUrl(url);
+ artifactDetails.setTimeout(timeout);
+ artifactDetails.setArtifactDisplayName(artifactLabel);
+ return artifactDetails;
+ }
+
+ private static String getDcaeArtifactName(ArtifactTypeEnum artifactTypeEnum, String artifactType) {
+ String artifactName = null;
+ switch (artifactTypeEnum) {
+ case DCAE_INVENTORY_TOSCA: {
+ artifactName = generateUUIDforSufix() + artifactType + "_toscaSampleArtifact.yml";
+ break;
+ }
+ case DCAE_INVENTORY_JSON: {
+ artifactName = generateUUIDforSufix() + artifactType + "_jsonSampleArtifact.json";
+ break;
+ }
+ case DCAE_INVENTORY_POLICY: {
+ artifactName = generateUUIDforSufix() + artifactType + "_emfSampleArtifact.emf";
+ break;
+ }
+ case DCAE_INVENTORY_DOC: {
+ artifactName = generateUUIDforSufix() + artifactType + "_docSampleArtifact.doc";
+ break;
+ }
+ case DCAE_INVENTORY_BLUEPRINT: {
+ artifactName = generateUUIDforSufix() + artifactType + "_bluePrintSampleArtifact.xml";
+ break;
+ }
+ case DCAE_INVENTORY_EVENT: {
+ artifactName = generateUUIDforSufix() + artifactType + "_eventSampleArtifact.xml";
+ break;
+ }
+ }
+ return artifactName;
+ }
+
+ // ---------------------Audit message------------------
+ public static ExpectedResourceAuditJavaObject getDefaultImportResourceAuditMsgSuccess() {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(AuditingActionEnum.IMPORT_RESOURCE.getName());
+ expectedResourceAuditJavaObject.setResourceName("defaultImportResourceName.yaml");
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("0.1");
+ expectedResourceAuditJavaObject.setModifierName(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setToscaNodeType("");
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public static ExpectedResourceAuditJavaObject getDefaultImportResourceAuditMsgFailure(ErrorInfo errorInfo, List<String> variables) {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(AuditingActionEnum.IMPORT_RESOURCE.getName());
+ expectedResourceAuditJavaObject.setResourceName("");
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("");
+ expectedResourceAuditJavaObject.setModifierName(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState("");
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ expectedResourceAuditJavaObject.setToscaNodeType("");
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public static ExpectedResourceAuditJavaObject getDefaultCertificationRequestAuditMsgSuccess() {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(AuditingActionEnum.CERTIFICATION_REQUEST_RESOURCE.getName());
+ expectedResourceAuditJavaObject.setResourceName("defaultResourceName");
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrVersion("0.1");
+ expectedResourceAuditJavaObject.setModifierName(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.toString());
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setComment("");
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public static ExpectedResourceAuditJavaObject getDefaultCertificationRequestAuditMsgFailure(ErrorInfo errorInfo, List<String> variables) {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+ expectedResourceAuditJavaObject.setAction(AuditingActionEnum.CERTIFICATION_REQUEST_RESOURCE.getName());
+ expectedResourceAuditJavaObject.setResourceName("");
+ expectedResourceAuditJavaObject.setResourceType("Resource");
+ expectedResourceAuditJavaObject.setPrevVersion("0.1");
+ expectedResourceAuditJavaObject.setCurrVersion("0.1");
+ expectedResourceAuditJavaObject.setModifierName(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getFullName());
+ expectedResourceAuditJavaObject.setModifierUid(ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ expectedResourceAuditJavaObject.setPrevState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.toString());
+ expectedResourceAuditJavaObject.setCurrState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.toString());
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedResourceAuditJavaObject.setDesc(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ expectedResourceAuditJavaObject.setComment("");
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultExternalAuditObject(AssetTypeEnum assetType, AuditingActionEnum action, String query) {
+
+ ExpectedExternalAudit expectedExternalAudit = new ExpectedExternalAudit();
+ expectedExternalAudit.setACTION(action.getName());
+ expectedExternalAudit.setCONSUMER_ID("ci");
+ expectedExternalAudit.setRESOURCE_URL("/asdc/v1/catalog/" + assetType.getValue() + (query == null ? "" : query));
+ expectedExternalAudit.setSTATUS("200");
+ expectedExternalAudit.setDESC("OK");
+ return expectedExternalAudit;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultAssetListAudit(AssetTypeEnum assetType, AuditingActionEnum auditAction) {
+
+ // ExpectedExternalAudit expectedAssetListAuditJavaObject = new
+ // ExpectedExternalAudit();
+ ExpectedExternalAudit expectedAssetListAuditJavaObject = getDefaultExternalAuditObject(assetType, auditAction, null);
+ return expectedAssetListAuditJavaObject;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultFilteredAssetListAudit(AssetTypeEnum assetType, String query) {
+
+ // ExpectedExternalAudit expectedAssetListAuditJavaObject = new
+ // ExpectedExternalAudit();
+ ExpectedExternalAudit expectedAssetListAuditJavaObject = getDefaultExternalAuditObject(assetType, AuditingActionEnum.GET_FILTERED_ASSET_LIST, query);
+ return expectedAssetListAuditJavaObject;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultExternalArtifactAuditSuccess(AssetTypeEnum assetType, AuditingActionEnum action, ArtifactDefinition artifactDefinition, String componentUUID) {
+
+ // ExpectedExternalAudit expectedExternalArtifactAudit = new
+ // ExpectedExternalAudit();
+
+ ExpectedExternalAudit expectedExternalArtifactAudit = getDefaultExternalAuditObject(assetType, action, null);
+ expectedExternalArtifactAudit.setMODIFIER(AuditValidationUtils.getModifierString(artifactDefinition.getUpdaterFullName(), artifactDefinition.getUserIdLastUpdater()));
+ expectedExternalArtifactAudit.setPREV_ARTIFACT_UUID("");
+ expectedExternalArtifactAudit.setCURR_ARTIFACT_UUID(artifactDefinition.getArtifactUUID());
+ expectedExternalArtifactAudit.setARTIFACT_DATA(AuditValidationUtils.buildArtifactDataAudit(artifactDefinition));
+ expectedExternalArtifactAudit.setRESOURCE_URL(expectedExternalArtifactAudit.getRESOURCE_URL() + "/" + componentUUID + "/artifacts");
+ return expectedExternalArtifactAudit;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultExternalArtifactAuditSuccess(AssetTypeEnum assetType, AuditingActionEnum action, ArtifactDefinition artifactDefinition, String componentUUID, String resourceInstanceName) {
+
+ ExpectedExternalAudit expectedExternalArtifactAudit = getDefaultExternalArtifactAuditSuccess(assetType, action, artifactDefinition, componentUUID);
+ expectedExternalArtifactAudit.setRESOURCE_URL("/asdc/v1/catalog/" + assetType.getValue() + "/" + componentUUID + "/resourceInstances/" + resourceInstanceName + "/artifacts");
+ return expectedExternalArtifactAudit;
+ }
+
+ public static ExpectedExternalAudit getDefaultExternalArtifactAuditFailure(AssetTypeEnum assetType, AuditingActionEnum action, ArtifactDefinition artifactDefinition, String componentUUID, ErrorInfo errorInfo, List<String> variables) {
+
+ // ExpectedExternalAudit expectedExternalArtifactAudit = new
+ // ExpectedExternalAudit();
+
+ ExpectedExternalAudit expectedExternalArtifactAudit = getDefaultExternalAuditObject(assetType, action, null);
+ expectedExternalArtifactAudit.setMODIFIER(AuditValidationUtils.getModifierString(artifactDefinition.getUpdaterFullName(), artifactDefinition.getUserIdLastUpdater()));
+ expectedExternalArtifactAudit.setPREV_ARTIFACT_UUID("");
+ expectedExternalArtifactAudit.setCURR_ARTIFACT_UUID(artifactDefinition.getArtifactUUID());
+ expectedExternalArtifactAudit.setARTIFACT_DATA(AuditValidationUtils.buildArtifactDataAudit(artifactDefinition));
+ expectedExternalArtifactAudit.setRESOURCE_URL(expectedExternalArtifactAudit.getRESOURCE_URL() + "/" + componentUUID + "/artifacts");
+ expectedExternalArtifactAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedExternalArtifactAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ return expectedExternalArtifactAudit;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultExternalArtifactAuditFailure(AssetTypeEnum assetType, AuditingActionEnum action, ArtifactDefinition artifactDefinition, String componentUUID, ErrorInfo errorInfo, List<String> variables,
+ String resourceInstanceName) {
+
+ ExpectedExternalAudit expectedExternalArtifactAudit = getDefaultExternalArtifactAuditFailure(assetType, action, artifactDefinition, componentUUID, errorInfo, variables);
+ expectedExternalArtifactAudit.setRESOURCE_URL("/asdc/v1/catalog/" + assetType.getValue() + "/" + componentUUID + "/resourceInstances/" + resourceInstanceName + "/artifacts");
+ return expectedExternalArtifactAudit;
+ }
+
+ public static ExpectedExternalAudit getFilteredAssetListAuditCategoryNotFound(AssetTypeEnum assetType, String query, String category) {
+
+ // ExpectedExternalAudit expectedAssetListAuditJavaObject = new
+ // ExpectedExternalAudit();
+ ExpectedExternalAudit expectedAssetListAuditJavaObject = getDefaultExternalAuditObject(assetType, AuditingActionEnum.GET_FILTERED_ASSET_LIST, query);
+ expectedAssetListAuditJavaObject.setSTATUS("404");
+ ErrorInfo errorInfo = null;
+ try {
+ errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ String desc = (errorInfo.getMessageId() + ": " + errorInfo.getMessage()).replace("%2", "category").replace("%3", category).replace("%1", "resource");
+ expectedAssetListAuditJavaObject.setDESC(desc);
+
+ return expectedAssetListAuditJavaObject;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultAssetMetadataAudit(AssetTypeEnum assetType, Component component) {
+
+ ExpectedExternalAudit expectedAssetListAuditJavaObject = new ExpectedExternalAudit();
+ expectedAssetListAuditJavaObject = getDefaultExternalAuditObject(assetType, AuditingActionEnum.GET_ASSET_METADATA, null);
+ expectedAssetListAuditJavaObject.setRESOURCE_URL(expectedAssetListAuditJavaObject.getRESOURCE_URL() + "/" + component.getUUID() + "/metadata");
+ expectedAssetListAuditJavaObject.setRESOURCE_NAME(component.getName());
+ expectedAssetListAuditJavaObject.setRESOURCE_TYPE(component.getComponentType().getValue());
+ expectedAssetListAuditJavaObject.setSERVICE_INSTANCE_ID(component.getUUID());
+ return expectedAssetListAuditJavaObject;
+
+ }
+
+ public static ExpectedExternalAudit getDefaultAssetMetadataAuditFailure(AssetTypeEnum assetType, String serviceUuid, String resourceType) {
+
+ ExpectedExternalAudit expectedAssetListAuditJavaObject = new ExpectedExternalAudit();
+ expectedAssetListAuditJavaObject = getDefaultExternalAuditObject(assetType, AuditingActionEnum.GET_ASSET_METADATA, null);
+ expectedAssetListAuditJavaObject.setSTATUS("404");
+ expectedAssetListAuditJavaObject.setDESC("OK");
+ expectedAssetListAuditJavaObject.setRESOURCE_URL(expectedAssetListAuditJavaObject.getRESOURCE_URL() + "/" + serviceUuid + "/metadata");
+ expectedAssetListAuditJavaObject.setRESOURCE_TYPE(resourceType);
+ expectedAssetListAuditJavaObject.setSERVICE_INSTANCE_ID(serviceUuid);
+ return expectedAssetListAuditJavaObject;
+
+ }
+
+ // Category/Subcategory/Group
+ public static CategoryDefinition getDefaultCategory() {
+ CategoryDefinition productCategoryDefinition = new CategoryDefinition();
+ productCategoryDefinition.setName("Category1");
+ return productCategoryDefinition;
+ }
+
+ public static SubCategoryDefinition getDefaultSubCategory() {
+ SubCategoryDefinition productSubCategoryDefinition = new SubCategoryDefinition();
+ productSubCategoryDefinition.setName("SubCategory1");
+ return productSubCategoryDefinition;
+ }
+
+ public static GroupingDefinition getDefaultGroup() {
+ GroupingDefinition productGroupDefinition = new GroupingDefinition();
+ productGroupDefinition.setName("Grouping1");
+ return productGroupDefinition;
+ }
+
+ // Product
+
+ public static ProductReqDetails getDefaultProduct() {
+ return createDefaultProductReqDetails("Product1", null);
+ }
+
+ public static ProductReqDetails getDefaultProduct(String name) {
+ return createDefaultProductReqDetails(name, null);
+ }
+
+ public static ProductReqDetails getDefaultProduct(CategoryDefinition category) {
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ return createDefaultProductReqDetails("CiProduct1", categories);
+ }
+
+ public static ProductReqDetails getDefaultProduct(String name, CategoryDefinition category) {
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ return createDefaultProductReqDetails(name, categories);
+ }
+
+ public static ProductReqDetails getDefaultProduct(List<CategoryDefinition> categories) {
+ return createDefaultProductReqDetails("CiProduct1", categories);
+ }
+
+ public static ProductReqDetails getDefaultProduct(String name, List<CategoryDefinition> categories) {
+ return createDefaultProductReqDetails(name, categories);
+ }
+
+ private static ProductReqDetails createDefaultProductReqDetails(String name, List<CategoryDefinition> categories) {
+ ProductReqDetails product = new ProductReqDetails(name);
+ ArrayList<String> tags = new ArrayList<String>();
+ tags.add(name);
+ product.setTags(tags);
+ product.setProjectCode("12345");
+ product.setIcon("myIcon");
+ ArrayList<String> contacts = new ArrayList<String>();
+ // contacts.add(ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST1).getUserId());
+ // contacts.add(ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_STRATEGIST2).getUserId());
+ contacts.add(ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER1).getUserId());
+ // contacts.add(ElementFactory.getDefaultUser(UserRoleEnum.PRODUCT_MANAGER2).getUserId());
+ product.setContacts(contacts);
+ product.setCategories(categories);
+ String fullName = "This is my full name: " + name;
+ product.setFullName(fullName);
+ String description = "This is product description";
+ product.setDescription(description);
+ return product;
+ }
+
+ public static RequirementCapabilityRelDef getReqCapRelation(String fromCompInstId, String toCompInstId, String reqOwnerId, String capOwnerId, String capType, String reqCapName, List<CapabilityDefinition> capList,
+ List<RequirementDefinition> reqList) {
+ RequirementCapabilityRelDef requirementDef = new RequirementCapabilityRelDef();
+ requirementDef.setFromNode(fromCompInstId);
+ requirementDef.setToNode(toCompInstId);
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair();
+ pair.setRequirementOwnerId(reqOwnerId);
+ pair.setCapabilityOwnerId(capOwnerId);
+ pair.setRequirement(reqCapName);
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType(capType);
+ pair.setRelationships(relationship);
+ pair.setCapabilityUid(capList.get(0).getUniqueId());
+ pair.setRequirementUid(reqList.get(0).getUniqueId());
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<>();
+ relationships.add(pair);
+ requirementDef.setRelationships(relationships);
+ return requirementDef;
+ }
+
+ private static String generateUUIDforSufix() {
+
+ String uniqueSufix = UUID.randomUUID().toString();
+ String[] split = uniqueSufix.split("-");
+ return uniqueSufix = split[4];
+ }
+
+ private static String normalizeArtifactLabel(String label) {
+
+ label = label.substring(0, label.indexOf("."));
+ String normalizedLabel = ValidationUtils.normalizeArtifactLabel(label);
+ return normalizedLabel.substring(0, Math.min(25, normalizedLabel.length()));
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/FileUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/FileUtils.java
new file mode 100644
index 0000000000..b785563a4d
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/FileUtils.java
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.general;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class FileUtils {
+
+ static Logger logger = LoggerFactory.getLogger(Utils.class.getName());
+
+ public static void writeToFile(String filePath, String content) {
+ try {
+ Files.write(Paths.get(filePath), content.getBytes());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static String getFileName(String fullyQualified) {
+ String fileName = fullyQualified;
+
+ int i = fullyQualified.lastIndexOf('.');
+ if (i > 0) {
+ fileName = fullyQualified.substring(i + 1);
+ }
+ return fileName;
+
+ }
+
+ public static Either<String, Exception> getFileContentUTF8(String filePath) {
+ Either<String, Exception> eitherResult;
+ try {
+ String content = new String(Files.readAllBytes(Paths.get(filePath)), StandardCharsets.UTF_8);
+ eitherResult = Either.left(content);
+ } catch (Exception e) {
+ eitherResult = Either.right(e);
+ }
+ return eitherResult;
+ }
+
+ public static List<String> getFileListFromBaseDirectoryByTestName(String testResourcesPath) {
+
+ File file = new File(testResourcesPath);
+ File[] listFiles = file.listFiles();
+ if (listFiles != null) {
+ List<String> listFileName = new ArrayList<String>();
+ for (File newFile : listFiles) {
+ if (newFile.isFile()) {
+ listFileName.add(newFile.getPath());
+ }
+ }
+ return listFileName;
+ }
+ assertTrue("directory " + testResourcesPath + " is empty", false);
+ return null;
+ }
+
+ public static String getFilePathFromListByPattern(List<String> fileList, String pattern) {
+
+ for (String filePath : fileList) {
+ if (filePath.contains(pattern)) {
+ return filePath;
+ }
+ }
+ return null;
+ }
+
+ public static String loadPayloadFileFromListUsingPosition(List<String> listFileName, String pattern, Boolean isBase64, int positionInList) throws IOException {
+ List<String> newList = new ArrayList<String>(Arrays.asList(listFileName.get(positionInList)));
+ return loadPayloadFile(newList, pattern, isBase64);
+ }
+
+ public static String loadPayloadFile(List<String> listFileName, String pattern, Boolean isBase64) throws IOException {
+ String fileName;
+ String payload = null;
+ fileName = FileUtils.getFilePathFromListByPattern(listFileName, pattern);
+ logger.debug("fileName: {}", fileName);
+
+ if (fileName != null) {
+ payload = Decoder.readFileToString(fileName);
+ if (isBase64) {
+ payload = Decoder.encode(payload.getBytes());
+ }
+ } else {
+ assertTrue("file to upload not found", false);
+ }
+ return payload;
+ }
+
+ public static String getFileNameFromPath(String testResourcesPath) {
+
+ File file = new File(testResourcesPath);
+ String fileName = null;
+ if (file.exists()) {
+ return file.getName();
+ } else {
+ assertTrue("file to upload not found", false);
+ }
+ return fileName;
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ImportUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ImportUtils.java
new file mode 100644
index 0000000000..040cdba444
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/general/ImportUtils.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.general;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+
+public class ImportUtils {
+
+ public static ImportReqDetails getImportResourceDetailsByPathAndName(ImportReqDetails importReqDetails, String filePath, String fileName) throws IOException {
+
+ // ImportReqDetails importReqDetails;
+ // User sdncUserDetails;
+ // String testResourcesPath;
+ // ResourceReqDetails resourceDetails;
+ // Config config;
+ // config = Utils.getConfig();
+ //
+ // importReqDetails = ElementFactory.getDefaultImportResource();
+ // User sdncUserDetails =
+ // ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ // ResourceReqDetails resourceDetails =
+ // ElementFactory.getDefaultResource();
+ // String sourceDir = config.getResourceConfigDir();
+ // String testResourcesPath = sourceDir + File.separator + workDir;
+ // final String workDir = "importToscaResourceByCreateUrl";
+
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(filePath);
+ importReqDetails.setPayloadName(fileName);
+ String payloadData = FileUtils.loadPayloadFile(listFileName, fileName, true);
+ importReqDetails.setPayloadData(payloadData);
+
+ return importReqDetails;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ArtifactRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ArtifactRestUtils.java
new file mode 100644
index 0000000000..4360621364
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ArtifactRestUtils.java
@@ -0,0 +1,689 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.AssertJUnit;
+
+import com.google.gson.Gson;
+
+public class ArtifactRestUtils extends BaseRestUtils {
+ private static Logger logger = LoggerFactory.getLogger(ArtifactRestUtils.class.getName());
+
+ // External API
+ // Upload Artifact of the asset
+ public static RestResponse externalAPIUploadArtifactOfTheAsset(Component component, User user, ArtifactReqDetails artifactReqDetails) throws IOException {
+ Config config = Utils.getConfig();
+ String resourceType = null;
+ String resourceUUID = component.getUUID();
+
+ System.out.println(component.getComponentType());
+
+ if (component.getComponentType().toString().toLowerCase().equals("resource")) {
+ resourceType = "resources";
+ } else {
+ resourceType = "services";
+ }
+
+ String url = String.format(Urls.POST_EXTERNAL_API_UPLOAD_ARTIFACT_OF_ASSET, config.getCatalogBeHost(), config.getCatalogBePort(), resourceType, resourceUUID);
+
+ return uploadInformationalArtifact(artifactReqDetails, user, calculateChecksum(artifactReqDetails), url);
+ }
+
+ //
+ // Testing
+ //
+ public static RestResponse getResourceDeploymentArtifactExternalAPI(String resourceUUID, String artifactUUID, User sdncModifierDetails, String resourceType) throws IOException {
+ Config config = Utils.getConfig();
+ String url = null;
+
+ if (resourceType.equals("Service")) {
+ url = String.format(Urls.GET_DOWNLOAD_SERVICE_ARTIFACT_OF_ASSET, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUUID, artifactUUID);
+
+ } else {
+ url = String.format(Urls.GET_DOWNLOAD_RESOURCE_ARTIFACT_OF_ASSET, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUUID, artifactUUID);
+ }
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ headersMap.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+
+ HttpRequest http = new HttpRequest();
+
+ logger.debug("Send GET request to get Resource Assets: {}", url);
+ System.out.println("Send GET request to get Resource Assets: " + url);
+
+ logger.debug("Request headers: {}", headersMap);
+ System.out.println("Request headers: " + headersMap);
+
+ RestResponse sendGetResourceAssets = http.httpSendGet(url, headersMap);
+
+ return sendGetResourceAssets;
+
+ }
+
+ public static RestResponse getComponentInstanceDeploymentArtifactExternalAPI(String resourceUUID, String componentInstanceUID, String artifactUUID, User sdncModifierDetails, String resourceType) throws IOException {
+ Config config = Utils.getConfig();
+ String url = null;
+
+ if (resourceType.equals("Service")) {
+ url = String.format(Urls.GET_DOWNLOAD_SERVICE_ARTIFACT_OF_COMPONENT_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUUID, componentInstanceUID, artifactUUID);
+
+ } else {
+ url = String.format(Urls.GET_DOWNLOAD_RESOURCE_ARTIFACT_OF_COMPONENT_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUUID, componentInstanceUID, artifactUUID);
+ }
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ headersMap.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+
+ HttpRequest http = new HttpRequest();
+
+ logger.debug("Send GET request to get Resource Assets: {}", url);
+ System.out.println("Send GET request to get Resource Assets: " + url);
+
+ logger.debug("Request headers: {}", headersMap);
+ System.out.println("Request headers: " + headersMap);
+
+ RestResponse sendGetResourceAssets = http.httpSendGet(url, headersMap);
+
+ return sendGetResourceAssets;
+
+ }
+
+ // *********** SERVICE ****************
+ public static RestResponse getArtifactTypesList() throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_ARTIFACTS, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ return sendGet(url, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ }
+
+ public static RestResponse addInformationalArtifactToService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid) throws IOException {
+ return addInformationalArtifactToService(artifactDetails, sdncModifierDetails, serviceUid, calculateChecksum(artifactDetails));
+ }
+
+ public static RestResponse addInformationalArtifactToService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String checksum) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.ADD_ARTIFACT_TO_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), serviceUid);
+
+ return uploadInformationalArtifact(artifactDetails, sdncModifierDetails, checksum, url);
+ }
+
+ public static RestResponse downloadServiceArtifact(ServiceReqDetails service, ArtifactReqDetails artifact, User user, Map<String, String> addionalHeaders) throws Exception {
+
+ return downloadServiceArtifact(service, artifact, user, addionalHeaders, true);
+ }
+
+ public static RestResponse downloadServiceArtifact(ServiceReqDetails service, ArtifactReqDetails artifact, User user, Map<String, String> addionalHeaders, boolean addEcompHeader) throws Exception {
+ Config config = Utils.getConfig();
+ String relativeUrl = encodeUrlForDownload(
+ String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL, ValidationUtils.convertToSystemName(service.getName()), service.getVersion(), ValidationUtils.normalizeFileName(artifact.getArtifactName())));
+ String fullUrl = String.format(Urls.DOWNLOAD_SERVICE_ARTIFACT_FULL_URL, config.getCatalogBeHost(), config.getCatalogBePort(), relativeUrl);
+
+ return downloadArtifact(fullUrl, user, addionalHeaders, addEcompHeader);
+ }
+
+ public static RestResponse downloadResourceArtifact(ServiceReqDetails service, ResourceReqDetails resource, ArtifactReqDetails artifact, User user, Map<String, String> addionalHeaders) throws Exception {
+ return downloadResourceArtifact(service, resource, artifact, user, addionalHeaders, true);
+ }
+
+ public static RestResponse downloadResourceArtifact(ServiceReqDetails service, ResourceReqDetails resource, ArtifactReqDetails artifact, User user, Map<String, String> addionalHeaders, boolean addEcompHeader) throws Exception {
+ Config config = Utils.getConfig();
+ String relativeUrl = encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_RESOURCE_ARTIFACT_RELATIVE_URL, ValidationUtils.convertToSystemName(service.getName()), service.getVersion(),
+ ValidationUtils.convertToSystemName(resource.getName()), resource.getVersion(), ValidationUtils.normalizeFileName(artifact.getArtifactName())));
+ String fullUrl = String.format(Urls.DOWNLOAD_RESOURCE_ARTIFACT_FULL_URL, config.getCatalogBeHost(), config.getCatalogBePort(), relativeUrl);
+
+ return downloadArtifact(fullUrl, user, addionalHeaders, addEcompHeader);
+ }
+
+ public static RestResponse downloadResourceInstanceArtifact(String serviceUniqueId, String resourceInstanceId, User user, String artifactUniqeId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DOWNLOAD_COMPONENT_INSTANCE_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(), serviceUniqueId, resourceInstanceId, artifactUniqeId);
+ RestResponse res = sendGet(url, user.getUserId(), null);
+ return res;
+ }
+
+ ////
+
+ // update
+
+ public static RestResponse updateInformationalArtifactOfServiceByMethod(ArtifactReqDetails artifactReqDetails, String serviceUid, String artifactUid, User sdncModifierDetails, String httpMethod) throws IOException {
+ // TODO Auto-generated method stub
+ return updateInformationalArtifactOfServiceByMethod(artifactReqDetails, serviceUid, artifactUid, sdncModifierDetails, httpMethod, calculateChecksum(artifactReqDetails));
+ }
+
+ public static RestResponse updateInformationalArtifactOfServiceByMethod(ArtifactReqDetails artifactReqDetails, String serviceUid, User sdncModifierDetails, String httpMethod) throws IOException {
+ // TODO Auto-generated method stub
+ return updateInformationalArtifactOfServiceByMethod(artifactReqDetails, serviceUid, artifactReqDetails.getUniqueId(), sdncModifierDetails, httpMethod, calculateChecksum(artifactReqDetails));
+ }
+
+ public static RestResponse downloadResourceArtifactInternalApi(String resourceId, User user, String artifactUniqeId) throws Exception {
+ return downloadComponentArtifactInternalApi(resourceId, user, artifactUniqeId, Urls.UI_DOWNLOAD_RESOURCE_ARTIFACT);
+ }
+
+ public static RestResponse downloadServiceArtifactInternalApi(String componentId, User user, String artifactUniqeId) throws Exception {
+ return downloadComponentArtifactInternalApi(componentId, user, artifactUniqeId, Urls.UI_DOWNLOAD_SERVICE_ARTIFACT);
+ }
+
+ public static RestResponse downloadComponentArtifactInternalApi(String componentId, User user, String artifactUniqeId, String urlTemplate) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(urlTemplate, config.getCatalogBeHost(), config.getCatalogBePort(), componentId, artifactUniqeId);
+ RestResponse res = sendGet(url, user.getUserId(), null);
+ return res;
+ }
+
+ /*
+ * public static RestResponse updateInformationalArtifactPayloadOfService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String artifactUid, String checksum) throws IOException { return
+ * updateInformationalArtifactOfService(artifactDetails, sdncModifierDetails, serviceUid, artifactUid, checksum, true); }
+ *
+ * public static RestResponse updateInformationalArtifactMetadataOfService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String artifactUid) throws IOException { return
+ * updateInformationalArtifactOfService(artifactDetails, sdncModifierDetails, serviceUid, artifactUid, calculateChecksum(artifactDetails), false); }
+ *
+ * public static RestResponse updateInformationalArtifactOfService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String artifactUid, String checksum, boolean isPayloadUpdate) throws IOException { Config config =
+ * Utils.getConfig(); Map<String, String> headersMap = getHeadersMap(sdncModifierDetails);
+ *
+ * if (isPayloadUpdate){ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), checksum); }
+ *
+ * Gson gson = new Gson(); String jsonBody = gson.toJson(artifactDetails);
+ *
+ * HttpRequest http = new HttpRequest();
+ *
+ * String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_SERVICE, config.getCatalogBeHost(),config.getCatalogBePort(), serviceUid, artifactUid); RestResponse res = http.httpSendPost(url, jsonBody, headersMap);
+ * System.out.println("update artifact was finished with response: "+ res.getErrorCode()); return res; }
+ */
+
+ public static RestResponse updateInformationalArtifactOfServiceByMethod(ArtifactReqDetails artifactReqDetails, String serviceUid, String artifactUid, User sdncModifierDetails, String httpMethod, String checksum) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = getHeadersMap(sdncModifierDetails);
+ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), checksum);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(artifactReqDetails);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), serviceUid, artifactUid);
+ RestResponse updateResourceResponse = http.httpSendByMethod(url, httpMethod, userBodyJson, headersMap);
+ // System.out.println("update artifact was finished with response: "+
+ // updateResourceResponse.getErrorCode());
+
+ return updateResourceResponse;
+ }
+
+ public static Map<String, String> getHeadersMap(User sdncModifierDetails) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptJsonHeader);
+
+ try {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ } catch (Exception e) {
+
+ }
+
+ return headersMap;
+ }
+
+ // *********** RESOURCE ****************
+ // add
+ public static RestResponse addInformationalArtifactToResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid) throws IOException {
+ return addInformationalArtifactToResource(artifactDetails, sdncModifierDetails, resourceUid, calculateChecksum(artifactDetails));
+ }
+
+ public static RestResponse explicitAddInformationalArtifactToResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid) throws IOException {
+ Config config = Utils.getConfig();
+
+ String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid);
+
+ return uploadInformationalArtifact(artifactDetails, sdncModifierDetails, calculateChecksum(artifactDetails), url);
+ }
+
+ public static RestResponse addInformationalArtifactToResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid, String checksum) throws IOException {
+ Config config = Utils.getConfig();
+
+ if (artifactDetails.getArtifactGroupType() != null && artifactDetails.getArtifactGroupType().equals(ArtifactGroupTypeEnum.DEPLOYMENT.getType())) {
+ // YANG_XML and OTHER deployment artifact should be added through
+ // this API, not updated
+ String artifactType = artifactDetails.getArtifactType();
+ if (!(ArtifactTypeEnum.YANG_XML.getType().equals(artifactType) || ArtifactTypeEnum.OTHER.getType().equals(artifactType) || ArtifactTypeEnum.VNF_CATALOG.getType().equals(artifactType)
+ || ArtifactTypeEnum.VF_LICENSE.getType().equals(artifactType) || ArtifactTypeEnum.VENDOR_LICENSE.getType().equals(artifactType) || ArtifactTypeEnum.MODEL_INVENTORY_PROFILE.getType().equals(artifactType)
+ || ArtifactTypeEnum.MODEL_QUERY_SPEC.getType().equals(artifactType) || ArtifactTypeEnum.APPC_CONFIG.getType().equals(artifactType))) {
+ // return updateInformationalArtifactToResource(artifactDetails,
+ // sdncModifierDetails, resourceUid);
+ }
+ }
+ String url = String.format(Urls.ADD_ARTIFACT_TO_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid);
+
+ return uploadInformationalArtifact(artifactDetails, sdncModifierDetails, checksum, url);
+ }
+
+ // update
+ public static RestResponse updateInformationalArtifactToResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid) throws IOException {
+ return updateInformationalArtifactToResource(artifactDetails, sdncModifierDetails, resourceUid, calculateChecksum(artifactDetails));
+ }
+
+ public static RestResponse updateInformationalArtifactToResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid, String checksum) throws IOException {
+ Config config = Utils.getConfig();
+ if (artifactDetails.getArtifactGroupType() != null && artifactDetails.getArtifactGroupType().equals("DEPLOYMENT")) {
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceUid);
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get(artifactDetails.getArtifactLabel());
+ artifactDetails.setUniqueId(artifactDefinition.getUniqueId());
+ artifactDetails.setArtifactLabel(artifactDefinition.getArtifactLabel());
+
+ }
+
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid, artifactDetails.getUniqueId());
+
+ return uploadInformationalArtifact(artifactDetails, sdncModifierDetails, calculateChecksum(artifactDetails), url);
+ }
+
+ public static RestResponse uploadArtifactToPlaceholderOnResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid, String placeHolderLabel) throws IOException {
+ Config config = Utils.getConfig();
+ if (artifactDetails.getArtifactLabel() != null && !artifactDetails.getArtifactLabel().isEmpty()) {
+ RestResponse resourceGetResponse = ResourceRestUtils.getResource(sdncModifierDetails, resourceUid);
+ Resource resourceRespJavaObject = ResponseParser.convertResourceResponseToJavaObject(resourceGetResponse.getResponse());
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceRespJavaObject.getDeploymentArtifacts();
+ ArtifactDefinition artifactDefinition = deploymentArtifacts.get(artifactDetails.getArtifactLabel());
+ AssertJUnit.assertNotNull(artifactDefinition);
+ artifactDetails.setUniqueId(artifactDefinition.getUniqueId());
+ artifactDetails.setArtifactLabel(artifactDefinition.getArtifactLabel());
+
+ }
+
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid, artifactDetails.getUniqueId());
+
+ return uploadInformationalArtifact(artifactDetails, sdncModifierDetails, calculateChecksum(artifactDetails), url);
+ }
+
+ public static RestResponse updateDeploymentArtifactToResource(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceUid) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid, artifactDetails.getUniqueId());
+
+ return updateDeploymentArtifact(artifactDetails, sdncModifierDetails, url);
+ }
+
+ public static RestResponse updateArtifactToResourceInstance(ArtifactDefinition artifactDefinition, User sdncModifierDetails, String resourceInstanceId, String serviceId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_RESOURCE_INSTANCE_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId, resourceInstanceId, artifactDefinition.getUniqueId());
+ return updateDeploymentArtifact(artifactDefinition, sdncModifierDetails, url);
+ }
+
+ public static RestResponse updateDeploymentArtifactToRI(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceInstanceId, String serviceId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_RESOURCE_INSTANCE_HEAT_ENV_PARAMS, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId, resourceInstanceId, artifactDetails.getUniqueId());
+ return updateDeploymentArtifact(artifactDetails, sdncModifierDetails, url);
+ }
+
+ // delete
+ public static RestResponse deleteArtifactFromResourceInstance(ArtifactDefinition artifactDefinition, User sdncModifierDetails, String resourceUid, String serviceId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_RESOURCE_INSTANCE_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId, resourceUid, artifactDefinition.getUniqueId());
+ return sendDelete(url, sdncModifierDetails.getUserId());
+ }
+
+ public static RestResponse deleteInformationalArtifactFromResource(String resourceUid, ArtifactReqDetails artifactDetails, User sdncModifierDetails) throws IOException {
+ return deleteInformationalArtifactFromResource(resourceUid, artifactDetails.getUniqueId(), sdncModifierDetails);
+ }
+
+ public static RestResponse deleteInformationalArtifactFromResource(String resourceUid, String artifactId, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceUid, artifactId);
+ return sendDelete(url, sdncModifierDetails.getUserId());
+ }
+
+ public static RestResponse deleteServiceApiArtifact(ArtifactReqDetails artifactDetails, String serviceUniqueId, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_DELETE_SERVICE_API_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(), serviceUniqueId, artifactDetails.getUniqueId());
+ RestResponse res = sendDelete(url, user.getUserId());
+ logger.debug("Deleting api artifact was finished with response: {}", res.getErrorCode());
+ logger.debug("Response body: {}", res.getResponseMessage());
+ return res;
+ }
+
+ // *************** RESOURCE INSTANCE **************
+ /**
+ * Add DCAE artifacts to resource instance.
+ *
+ * @param artifactDetails
+ * @param sdncModifierDetails
+ * @param resourceInstanceId
+ * @param serviceId
+ * @return
+ * @throws IOException
+ */
+ public static RestResponse addArtifactToResourceInstance(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String resourceInstanceId, String serviceId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.ADD_RESOURCE_INSTANCE_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId, resourceInstanceId, artifactDetails.getUniqueId());
+ return addArtifactToInstance(artifactDetails, sdncModifierDetails, calculateChecksum(artifactDetails), url);
+ }
+
+ // *************** COMPONENT **************
+
+ public static RestResponse uploadDeploymentArtifact(ArtifactReqDetails artifactDetails, Component component, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+ Map<String, String> additionalHeaders = null;
+ String checksum = ResponseParser.calculateMD5Header(artifactDetails);
+ additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.Content_MD5.getValue(), checksum);
+
+ ComponentTypeEnum componentType = component.getComponentType();
+
+ String url = null;
+
+ switch (componentType) {
+
+ case RESOURCE: {
+ url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), component.getUniqueId(), artifactDetails.getUniqueId());
+
+ break;
+ }
+ case SERVICE: {
+
+ break;
+ }
+
+ case PRODUCT: {
+
+ break;
+ }
+
+ default: {// dummy
+ assertTrue("failed on enum selection", false);
+
+ break;
+ }
+ }
+
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+ // System.out.println("ArtifactDetails: "+ jsonBody);
+
+ RestResponse res = sendPost(url, jsonBody, sdncModifierDetails.getUserId(), acceptHeaderData, additionalHeaders);
+ if (res.getErrorCode() == STATUS_CODE_SUCCESS) {
+ artifactDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ }
+ // System.out.println("Add artifact was finished with response: "+
+ // res.getErrorCode());
+ return res;
+ }
+
+ public static RestResponse uploadArtifact(ArtifactReqDetails artifactDetails, Component component, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+ List<String> placeHolderlst = Utils.getListOfResPlaceHoldersDepArtTypes();
+ Map<String, String> additionalHeaders = null;
+ String checksum = null;
+ String url = null;
+ //
+ //
+ // if (artifactDetails.getArtifactGroupType() != null
+ // && artifactDetails.getArtifactGroupType().equals("DEPLOYMENT")
+ // && placeHolderlst.contains(artifactDetails.getArtifactType())) {
+ // Map<String, ArtifactDefinition> deploymentArtifacts =
+ // component.getDeploymentArtifacts();
+ // ArtifactDefinition artifactDefinition =
+ // deploymentArtifacts.get(artifactDetails.getArtifactLabel());
+ // artifactDetails.setUniqueId(artifactDefinition.getUniqueId());
+ // artifactDetails.setArtifactLabel(artifactDefinition.getArtifactLabel());
+ // checksum = ResponseParser.calculateMD5Header(artifactDetails);
+ // additionalHeaders = new HashMap<String, String>();
+ // additionalHeaders.put(HttpHeaderEnum.Content_MD5.getValue(),
+ // checksum);
+ // url = String.format(Urls.UPDATE_ARTIFACT_OF_COMPONENT,
+ // config.getCatalogBeHost(),
+ // config.getCatalogBePort(),
+ // ComponentTypeEnum.findParamByType(component.getComponentType()),
+ // component.getUniqueId(), artifactDetails.getUniqueId());
+ // }
+ //
+ // else {
+ checksum = ResponseParser.calculateMD5Header(artifactDetails);
+ additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.Content_MD5.getValue(), checksum);
+ url = String.format(Urls.UPLOAD_DELETE_ARTIFACT_OF_COMPONENT, config.getCatalogBeHost(), config.getCatalogBePort(), ComponentTypeEnum.findParamByType(component.getComponentType()), component.getUniqueId(), artifactDetails.getUniqueId());
+ // }
+
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+ // System.out.println("ArtifactDetails: "+ jsonBody);
+
+ RestResponse res = sendPost(url, jsonBody, sdncModifierDetails.getUserId(), acceptHeaderData, additionalHeaders);
+ if (res.getErrorCode() == STATUS_CODE_SUCCESS) {
+ artifactDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ }
+ // System.out.println("Add artifact was finished with response: "+
+ // res.getErrorCode());
+ return res;
+ }
+
+ // *************** PRIVATE **************
+ private static RestResponse uploadInformationalArtifact(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String checksum, String url) throws IOException {
+ Map<String, String> additionalHeaders = null;
+ if (checksum != null && !checksum.isEmpty()) {
+ additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.Content_MD5.getValue(), checksum);
+ }
+
+ additionalHeaders.put(HttpHeaderEnum.AUTHORIZATION.getValue(), authorizationHeader);
+ additionalHeaders.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+
+ Gson gson = new Gson();
+ // System.out.println("ArtifactDetails: "+ jsonBody);
+ String jsonBody = gson.toJson(artifactDetails);
+
+ RestResponse res = sendPost(url, jsonBody, sdncModifierDetails.getUserId(), acceptHeaderData, additionalHeaders);
+ if ((res.getErrorCode() == STATUS_CODE_SUCCESS) || (res.getErrorCode() == STATUS_CODE_CREATED)) {
+ artifactDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ }
+ // System.out.println("Add artifact was finished with response: "+
+ // res.getErrorCode());
+ return res;
+ }
+
+ private static RestResponse addArtifactToInstance(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String checksum, String url) throws IOException {
+ Map<String, String> additionalHeaders = null;
+ additionalHeaders = new HashMap<String, String>();
+ if (checksum != null && !checksum.isEmpty()) {
+ additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.Content_MD5.getValue(), checksum);
+ }
+ additionalHeaders.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json, text/plain, */*");
+ additionalHeaders.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json;charset=UTF-8");
+
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+
+ RestResponse res = sendPost(url, jsonBody, sdncModifierDetails.getUserId(), "application/json, text/plain, */*", additionalHeaders);
+ if (res.getErrorCode() == STATUS_CODE_SUCCESS) {
+ artifactDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ }
+ return res;
+ }
+
+ private static RestResponse updateDeploymentArtifact(ArtifactDefinition artifactDefinition, User sdncModifierDetails, String url) throws IOException {
+ Map<String, String> additionalHeaders = null;
+ additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json, text/plain, */*");
+ additionalHeaders.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json;charset=UTF-8");
+
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDefinition);
+
+ RestResponse res = sendPost(url, jsonBody, sdncModifierDetails.getUserId(), "application/json, text/plain, */*", additionalHeaders);
+ return res;
+ }
+
+ private static RestResponse updateDeploymentArtifact(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String url) throws IOException {
+ Map<String, String> additionalHeaders = null;
+
+ additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json, text/plain, */*");
+ additionalHeaders.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json;charset=UTF-8");
+ // additionalHeaders.put(HttpHeaderEnum..getValue(),
+ // "application/json;charset=UTF-8");
+
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+ // System.out.println("ArtifactDetails: "+ jsonBody);
+
+ RestResponse res = sendPost(url, jsonBody, sdncModifierDetails.getUserId(), "application/json, text/plain, */*", additionalHeaders);
+ if (res.getErrorCode() == STATUS_CODE_SUCCESS) {
+ artifactDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ }
+ // System.out.println("Add artifact was finished with response: "+
+ // res.getErrorCode());
+ return res;
+ }
+
+ private static RestResponse downloadArtifact(String url, User user, Map<String, String> addionalHeaders, boolean addEcompHeader) throws IOException {
+ if (addEcompHeader) {
+ addionalHeaders.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), ecomp);
+ }
+ return downloadArtifact(url, user, addionalHeaders, acceptOctetStream);
+ }
+
+ private static RestResponse downloadArtifact(String url, User user, Map<String, String> addionalHeaders, String accept) throws IOException {
+ addionalHeaders.put(HttpHeaderEnum.ACCEPT.getValue(), accept);
+
+ RestResponse res = sendGet(url, user.getUserId(), addionalHeaders);
+ // System.out.println("download artifact was finished with response: "+
+ // res.getErrorCode());
+ // System.out.println("response is: " + res.getResponse());
+ return res;
+ }
+
+ private static Map<String, Map<String, Object>> getArtifactsListFromResponse(String jsonResponse, String fieldOfArtifactList) {
+ JSONObject object = (JSONObject) JSONValue.parse(jsonResponse);
+ Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) object.get(fieldOfArtifactList);
+ return map;
+ }
+
+ public static String calculateChecksum(ArtifactReqDetails artifactDetails) {
+ String checksum = null;
+ if (artifactDetails.getPayload() != null) {
+ checksum = ResponseParser.calculateMD5Header(artifactDetails);
+ }
+ return checksum;
+ }
+
+ public static String encodeUrlForDownload(String url) {
+
+ return url.replaceAll(" ", "%20");
+ }
+
+ public static String getPartialUrlByArtifactName(ServiceReqDetails serviceDetails, String serviceVersion, String artifactName) {
+ return encodeUrlForDownload(String.format(Urls.DISTRIB_DOWNLOAD_SERVICE_ARTIFACT_RELATIVE_URL, ValidationUtils.convertToSystemName(serviceDetails.getName()), serviceVersion, artifactName));
+ }
+
+ public static String getUniqueIdOfArtifact(RestResponse createResponse, String artifactField, String requieredArtifactLabel) throws Exception {
+ Map<String, Object> artifact = getArtifactFromRestResponse(createResponse, artifactField, requieredArtifactLabel);
+ assertNotNull(artifact);
+ return artifact.get("uniqueId").toString();
+ }
+
+ public static Map<String, Object> getArtifactFromRestResponse(RestResponse response, String artifactField, String requieredArtifactLabel) {
+ Map<String, Map<String, Object>> map = getArtifactsListFromResponse(response.getResponse(), artifactField);
+ return map.get(requieredArtifactLabel);
+ }
+
+ public static RestResponse updateInformationalArtifactPayloadOfService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String artifactUid) throws IOException {
+ return updateInformationalArtifactPayloadOfService(artifactDetails, sdncModifierDetails, serviceUid, artifactUid, calculateMD5Header(artifactDetails));
+ }
+
+ private static RestResponse updateInformationalArtifactPayloadOfService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String artifactUid, String checksum) throws IOException {
+ return updateInformationalArtifactOfService(artifactDetails, sdncModifierDetails, serviceUid, artifactUid, checksum, true);
+ }
+
+ private static RestResponse updateInformationalArtifactOfService(ArtifactReqDetails artifactDetails, User sdncModifierDetails, String serviceUid, String artifactUid, String checksum, boolean isPayloadUpdate) throws IOException {
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = prepareHeadersMap(sdncModifierDetails.getUserId());
+
+ if (isPayloadUpdate) {
+ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), checksum);
+ }
+
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+
+ HttpRequest http = new HttpRequest();
+
+ String url = String.format(Urls.UPDATE_OR_DELETE_ARTIFACT_OF_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), serviceUid, artifactUid);
+ RestResponse res = http.httpSendPost(url, jsonBody, headersMap);
+ // System.out.println("update artifact was finished with response: "+
+ // res.getErrorCode());
+ return res;
+ }
+
+ public static String calculateMD5Header(ArtifactReqDetails artifactDetails) {
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+ // calculate MD5 for json body
+ return calculateMD5(jsonBody);
+
+ }
+
+ public static String calculateMD5(String data) {
+ String calculatedMd5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(data);
+ // encode base-64 result
+ byte[] encodeBase64 = Base64.encodeBase64(calculatedMd5.getBytes());
+ String encodeBase64Str = new String(encodeBase64);
+ return encodeBase64Str;
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/AssetRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/AssetRestUtils.java
new file mode 100644
index 0000000000..51bd2162c0
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/AssetRestUtils.java
@@ -0,0 +1,510 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+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.HttpClients;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.AssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceDetailedAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceInstanceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceDetailedAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class AssetRestUtils extends BaseRestUtils {
+ static Gson gson = new Gson();
+ static ObjectMapper objectMapper = new ObjectMapper();
+
+ static Logger logger = LoggerFactory.getLogger(UserRestUtils.class.getName());
+ static final String contentTypeHeaderData = "application/json";
+ static final String acceptHeaderDate = "application/json";
+ static final String basicAuthentication = "Basic Y2k6MTIzNDU2";
+ // /asdc/v1/catalog/{services/resources}/{componentUUID}/artifacts/{artifactUUID}
+ static final String COMPONENT_ARTIFACT_URL = "/asdc/v1/catalog/%s/%s/artifacts/%s";
+ // /asdc/v1/catalog/{services/resources}/{componentUUID}/resourceInstances/{resourceInstanceName}/artifacts/{artifactUUID}
+ static final String RESOURCE_INSTANCE_ARTIFACT_URL = "/asdc/v1/catalog/%s/%s/resourceInstances/%s/artifacts/%s";
+
+ public static HttpResponse getComponentToscaModel(AssetTypeEnum assetType, String uuid) throws IOException {
+ Config config = Utils.getConfig();
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ String url = String.format(Urls.GET_TOSCA_MODEL, config.getCatalogBeHost(), config.getCatalogBePort(),
+ assetType.getValue(), uuid);
+ HttpGet httpGet = new HttpGet(url);
+
+ httpGet.addHeader(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+ httpGet.addHeader(HttpHeaderEnum.AUTHORIZATION.getValue(), basicAuthentication);
+
+ logger.debug("Send GET request to get Tosca model: {}", url);
+
+ return httpclient.execute(httpGet);
+ }
+
+ public static RestResponse getComponentListByAssetType(boolean isBasicAuthentication, AssetTypeEnum assetType,
+ String... filterArrayString) throws IOException {
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ if (isBasicAuthentication) {
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), basicAuthentication);
+ }
+ headersMap.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ASSET_LIST, config.getCatalogBeHost(), config.getCatalogBePort(),
+ assetType.getValue());
+ if (filterArrayString != null && filterArrayString.length > 0) {
+ url = buildUrlWithFilter(url, filterArrayString);
+ }
+
+ RestResponse sendGetResourceAssets = http.httpSendGet(url, headersMap);
+
+ return sendGetResourceAssets;
+ }
+
+ public static RestResponse getFilteredComponentList(AssetTypeEnum assetType, String query) throws IOException {
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), basicAuthentication);
+ headersMap.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+
+ HttpRequest http = new HttpRequest();
+
+ String url = String.format(Urls.GET_FILTERED_ASSET_LIST, config.getCatalogBeHost(), config.getCatalogBePort(),
+ assetType.getValue(), query);
+
+ logger.debug("Send GET request to get Resource Assets: {}", url);
+ logger.debug("Request headers: {}", headersMap);
+
+ RestResponse sendGetResourceAssets = http.httpSendGet(url, headersMap);
+
+ return sendGetResourceAssets;
+ }
+
+ public static String buildUrlWithFilter(String url, String[] filterArrayString) {
+ StringBuilder sb = new StringBuilder();
+ int length = filterArrayString.length;
+ int count = 0;
+ for (String filterString : filterArrayString) {
+ sb.append(filterString);
+ count++;
+ if (length != count) {
+ sb.append("&");
+ }
+ }
+ return url + "?" + sb;
+ }
+
+ public static RestResponse getAssetMetadataByAssetTypeAndUuid(boolean isBasicAuthentication,
+ AssetTypeEnum assetType, String uuid) throws IOException {
+
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ if (isBasicAuthentication) {
+ headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), basicAuthentication);
+ }
+ headersMap.put(HttpHeaderEnum.X_ECOMP_INSTANCE_ID.getValue(), "ci");
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ASSET_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(),
+ assetType.getValue(), uuid);
+
+ logger.debug("Send GET request to get Resource Assets: {}", url);
+ logger.debug("Request headers: {} ", headersMap);
+
+ RestResponse sendGetResourceAssets = http.httpSendGet(url, headersMap);
+
+ return sendGetResourceAssets;
+ }
+
+ public static List<ResourceAssetStructure> getResourceAssetList(RestResponse assetResponse) {
+ List<ResourceAssetStructure> resourceAssetList = new ArrayList<>();
+
+ JsonElement jelement = new JsonParser().parse(assetResponse.getResponse());
+ JsonArray componenetArray = (JsonArray) jelement;
+ for (JsonElement jElement : componenetArray) {
+ ResourceAssetStructure resource = gson.fromJson(jElement, ResourceAssetStructure.class);
+ resourceAssetList.add(resource);
+ }
+ return resourceAssetList;
+ }
+
+ public static ResourceDetailedAssetStructure getResourceAssetMetadata(RestResponse assetResponse) {
+
+ List<ResourceInstanceAssetStructure> resourcesList = new ArrayList<>();
+ List<ArtifactAssetStructure> artifactsList = new ArrayList<>();
+ ResourceDetailedAssetStructure resourceAssetMetadata = new ResourceDetailedAssetStructure();
+ String response = assetResponse.getResponse();
+
+ JsonObject jObject = (JsonObject) new JsonParser().parse(response);
+ resourceAssetMetadata = gson.fromJson(jObject, ResourceDetailedAssetStructure.class);
+
+ setResourceInstanceAssetList(resourcesList, jObject);
+ resourceAssetMetadata.setResources(resourcesList);
+
+ setArtifactAssetList(artifactsList, jObject);
+ resourceAssetMetadata.setArtifacts(artifactsList);
+
+ return resourceAssetMetadata;
+ }
+
+ public static void generalMetadataFieldsValidatior(AssetStructure assetMetadata, Component component) {
+
+ assertTrue("Expected resourceUuid is " + component.getUUID() + " actual: " + assetMetadata.getUuid(),
+ assetMetadata.getUuid().equals(component.getUUID()));
+ assertTrue(
+ "Expected resourceInvariantUuid is " + component.getInvariantUUID() + " actual: "
+ + assetMetadata.getInvariantUUID(),
+ assetMetadata.getInvariantUUID().equals(component.getInvariantUUID()));
+ assertTrue("Expected asset name is " + component.getName() + " actual: " + assetMetadata.getName(),
+ assetMetadata.getName().equals(component.getName()));
+ assertTrue("Expected asset version is " + component.getVersion() + " actual: " + assetMetadata.getVersion(),
+ assetMetadata.getVersion().equals(component.getVersion()));
+ assertTrue(
+ "Expected asset lastUpdaterUserId is " + component.getLastUpdaterUserId() + " actual: "
+ + assetMetadata.getLastUpdaterUserId(),
+ assetMetadata.getLastUpdaterUserId().equals(component.getLastUpdaterUserId()));
+ assertNotNull("Expected asset toscaModel is null", assetMetadata.getToscaModelURL());
+ assertTrue(
+ "Expected asset category is " + component.getCategories().get(0).getName() + " actual: "
+ + assetMetadata.getCategory(),
+ assetMetadata.getCategory().equals(component.getCategories().get(0).getName()));
+ assertTrue(
+ "Expected asset lifeCycleState is " + component.getLifecycleState() + " actual: "
+ + assetMetadata.getLifecycleState(),
+ assetMetadata.getLifecycleState().equals(component.getLifecycleState().toString()));
+
+ }
+
+ public static void resourceMetadataValidatior(ResourceDetailedAssetStructure resourceAssetMetadata,
+ Resource resource, AssetTypeEnum assetType) {
+
+ generalMetadataFieldsValidatior(resourceAssetMetadata, resource);
+ assertTrue(
+ "Expected asset lastUpdaterFullName is " + resource.getLastUpdaterFullName() + " actual: "
+ + resourceAssetMetadata.getLastUpdaterFullName(),
+ resourceAssetMetadata.getLastUpdaterFullName().equals(resource.getLastUpdaterFullName()));
+ assertTrue(
+ "Expected asset subCategory is " + resource.getCategories().get(0).getSubcategories().get(0).getName()
+ + " actual: " + resourceAssetMetadata.getSubCategory(),
+ resourceAssetMetadata.getSubCategory()
+ .equals(resource.getCategories().get(0).getSubcategories().get(0).getName()));
+ assertTrue(
+ "Expected asset toscaResourceName is " + resource.getToscaResourceName() + " actual: "
+ + resourceAssetMetadata.getToscaResourceName(),
+ resourceAssetMetadata.getToscaResourceName().equals(resource.getToscaResourceName()));
+ assertTrue(
+ "Expected asset resourceType is " + resource.getResourceType() + " actual: "
+ + resourceAssetMetadata.getResourceType(),
+ resourceAssetMetadata.getResourceType().equals(resource.getResourceType().toString()));
+ resourceInstanceAssetValidator(resourceAssetMetadata.getResources(), resource, assetType);
+ // resourceInstanceAssetValidator(resourceAssetMetadata.getResources(),
+ // resource);
+ artifactAssetValidator(resourceAssetMetadata.getArtifacts(), resource, assetType);
+
+ }
+
+ public static void serviceMetadataValidatior(ServiceDetailedAssetStructure serviceAssetMetadata, Service service,
+ AssetTypeEnum assetType) {
+
+ generalMetadataFieldsValidatior(serviceAssetMetadata, service);
+ assertTrue(
+ "Expected asset lastUpdaterFullName is " + service.getLastUpdaterFullName() + " actual: "
+ + serviceAssetMetadata.getLastUpdaterFullName(),
+ serviceAssetMetadata.getLastUpdaterFullName().equals(service.getLastUpdaterFullName()));
+ assertTrue(
+ "Expected asset distributionStatus is " + service.getDistributionStatus() + " actual: "
+ + serviceAssetMetadata.getDistributionStatus(),
+ serviceAssetMetadata.getDistributionStatus().equals(service.getDistributionStatus().toString()));
+ resourceInstanceAssetValidator(serviceAssetMetadata.getResources(), service, assetType);
+ // resourceInstanceAssetValidator(serviceAssetMetadata.getResources(),
+ // service);
+ artifactAssetValidator(serviceAssetMetadata.getArtifacts(), service, assetType);
+
+ }
+
+ private static void artifactAssetValidator(List<ArtifactAssetStructure> artifactAssetStructureList,
+ Component component, AssetTypeEnum assetType) {
+ Map<String, ArtifactDefinition> componentDeploymentArtifacts = component.getDeploymentArtifacts();
+ validateArtifactMetadata(componentDeploymentArtifacts, artifactAssetStructureList, component.getUUID(),
+ assetType, null);
+ }
+
+ private static void validateArtifactMetadata(Map<String, ArtifactDefinition> componentDeploymentArtifacts,
+ List<ArtifactAssetStructure> artifactAssetStructureList, String componentUuid, AssetTypeEnum assetType,
+ String resourceInstanceName) {
+
+ for (Entry<String, ArtifactDefinition> componentDeploymentArtifact : componentDeploymentArtifacts.entrySet()) {
+ ArtifactAssetStructure artifactAssetStructure = getArtifactMetadata(artifactAssetStructureList,
+ componentDeploymentArtifact.getValue().getArtifactUUID());
+ ArtifactDefinition componentDeploymentArtifactValue = componentDeploymentArtifact.getValue();
+ if (artifactAssetStructure != null) {
+ assertTrue(
+ "Expected artifact asset artifactName is " + componentDeploymentArtifactValue.getArtifactName()
+ + " actual: " + artifactAssetStructure.getArtifactName(),
+ componentDeploymentArtifactValue.getArtifactName()
+ .equals(artifactAssetStructure.getArtifactName()));
+ assertTrue(
+ "Expected artifact asset Type is " + componentDeploymentArtifactValue.getArtifactType()
+ + " actual: " + artifactAssetStructure.getArtifactType(),
+ componentDeploymentArtifactValue.getArtifactType()
+ .equals(artifactAssetStructure.getArtifactType()));
+ // assertNotNull("Expected artifact asset resourceInvariantUUID
+ // is null",
+ // resourceInstanceAssetStructure.getResourceInvariantUUID());
+ // String expectedArtifactUrl = "/asdc/v1/catalog/" +
+ // assetType.getValue() + "/" + componentUuid + "/artifacts/" +
+ // componentDeploymentArtifactValue.getArtifactUUID();
+ String expectedArtifactUrl = "";
+ if (resourceInstanceName == null) {
+ expectedArtifactUrl = String.format(COMPONENT_ARTIFACT_URL, assetType.getValue(), componentUuid,
+ componentDeploymentArtifactValue.getArtifactUUID());
+ } else {
+ expectedArtifactUrl = String.format(RESOURCE_INSTANCE_ARTIFACT_URL, assetType.getValue(),
+ componentUuid, resourceInstanceName, componentDeploymentArtifactValue.getArtifactUUID());
+ }
+
+ assertTrue(
+ "Expected artifact asset URL is " + expectedArtifactUrl + " actual: "
+ + artifactAssetStructure.getArtifactURL(),
+ artifactAssetStructure.getArtifactURL().equals(expectedArtifactUrl));
+ assertTrue(
+ "Expected artifact asset description is " + componentDeploymentArtifactValue.getDescription()
+ + " actual: " + artifactAssetStructure.getArtifactDescription(),
+ componentDeploymentArtifactValue.getDescription().toString()
+ .equals(artifactAssetStructure.getArtifactDescription()));
+ assertTrue(
+ "Expected artifact asset checkSum is " + componentDeploymentArtifactValue.getArtifactChecksum()
+ + " actual: " + artifactAssetStructure.getArtifactChecksum(),
+ componentDeploymentArtifactValue.getArtifactChecksum()
+ .equals(artifactAssetStructure.getArtifactChecksum()));
+ assertTrue(
+ "Expected artifact asset version is " + componentDeploymentArtifactValue.getArtifactVersion()
+ + " actual: " + artifactAssetStructure.getArtifactVersion(),
+ componentDeploymentArtifactValue.getArtifactVersion()
+ .equals(artifactAssetStructure.getArtifactVersion()));
+ if (componentDeploymentArtifactValue.getTimeout() > 0) {
+ assertTrue(
+ "Expected artifact asset timeout is " + componentDeploymentArtifactValue.getTimeout()
+ + " actual: " + artifactAssetStructure.getArtifactTimeout(),
+ componentDeploymentArtifactValue.getTimeout()
+ .equals(artifactAssetStructure.getArtifactTimeout()));
+ }
+
+ } else {
+ assertTrue("artifact asset with UUID" + componentDeploymentArtifact.getValue().getArtifactUUID()
+ + " not found in get Metadata response", false);
+ }
+ }
+
+ }
+
+ private static ArtifactAssetStructure getArtifactMetadata(List<ArtifactAssetStructure> artifactAssetStructureList,
+ String artifactUUID) {
+ for (ArtifactAssetStructure artifactAssetStructure : artifactAssetStructureList) {
+ if (artifactAssetStructure.getArtifactUUID().equals(artifactUUID)) {
+ return artifactAssetStructure;
+ }
+ }
+ return null;
+ }
+
+ private static void resourceInstanceAssetValidator(
+ List<ResourceInstanceAssetStructure> resourceInstanceAssetStructures, Component component,
+ AssetTypeEnum assetType) {
+
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null) {
+ for (ComponentInstance componentInstance : componentInstances) {
+ ResourceInstanceAssetStructure resourceInstanceAssetStructure = getResourceInstanceMetadata(
+ resourceInstanceAssetStructures, componentInstance.getName());
+ if (resourceInstanceAssetStructure != null) {
+ assertTrue(
+ "Expected RI asset resourceName is " + componentInstance.getComponentName() + " actual: "
+ + resourceInstanceAssetStructure.getResourceName(),
+ componentInstance.getComponentName()
+ .equals(resourceInstanceAssetStructure.getResourceName()));
+ assertTrue(
+ "Expected RI asset Name is " + componentInstance.getName() + " actual: "
+ + resourceInstanceAssetStructure.getResourceInstanceName(),
+ componentInstance.getName()
+ .equals(resourceInstanceAssetStructure.getResourceInstanceName()));
+ assertNotNull("Expected RI asset resourceInvariantUUID is null",
+ resourceInstanceAssetStructure.getResourceInvariantUUID());
+ assertTrue(
+ "Expected RI asset resourceVersion is " + componentInstance.getComponentVersion()
+ + " actual: " + resourceInstanceAssetStructure.getResourceVersion(),
+ componentInstance.getComponentVersion()
+ .equals(resourceInstanceAssetStructure.getResourceVersion()));
+ assertTrue(
+ "Expected RI asset resourceType is " + componentInstance.getOriginType() + " actual: "
+ + resourceInstanceAssetStructure.getResoucreType(),
+ componentInstance.getOriginType().toString()
+ .equals(resourceInstanceAssetStructure.getResoucreType()));
+ assertTrue(
+ "Expected RI asset resourceUUID is " + componentInstance.getComponentUid() + " actual: "
+ + resourceInstanceAssetStructure.getResourceUUID(),
+ componentInstance.getComponentUid()
+ .equals(resourceInstanceAssetStructure.getResourceUUID()));
+ validateArtifactMetadata(componentInstance.getDeploymentArtifacts(),
+ resourceInstanceAssetStructure.getArtifacts(), component.getUUID(), assetType,
+ componentInstance.getNormalizedName());
+ // validateArtifactMetadata(componentInstance.getDeploymentArtifacts(),
+ // resourceInstanceAssetStructure.getArtifacts(),
+ // component.getUUID(), AssetTypeEnum.RESOURCES);
+ } else {
+ assertTrue("resourceInstance asset with UUID" + componentInstance.getComponentUid()
+ + " not found in get Metadata response", false);
+ }
+ }
+ }
+
+ }
+
+ // private static ResourceInstanceAssetStructure
+ // getResourceInstanceMetadata(List<ResourceInstanceAssetStructure>
+ // resourceInstanceAssetStructures, String componentUid) {
+ private static ResourceInstanceAssetStructure getResourceInstanceMetadata(
+ List<ResourceInstanceAssetStructure> resourceInstanceAssetStructures, String name) {
+ for (ResourceInstanceAssetStructure resourceInstanceAssetStructure : resourceInstanceAssetStructures) {
+ if (resourceInstanceAssetStructure.getResourceInstanceName().equals(name)) {
+ return resourceInstanceAssetStructure;
+ }
+ }
+ return null;
+ }
+
+ public static ServiceDetailedAssetStructure getServiceAssetMetadata(RestResponse assetResponse) {
+
+ List<ResourceInstanceAssetStructure> resourcesList = new ArrayList<>();
+ List<ArtifactAssetStructure> artifactsList = new ArrayList<>();
+ ServiceDetailedAssetStructure serviceAssetMetadata;
+
+ JsonObject jObject = (JsonObject) new JsonParser().parse(assetResponse.getResponse());
+ serviceAssetMetadata = gson.fromJson(jObject, ServiceDetailedAssetStructure.class);
+
+ setResourceInstanceAssetList(resourcesList, jObject);
+ serviceAssetMetadata.setResources(resourcesList);
+
+ setArtifactAssetList(artifactsList, jObject);
+ serviceAssetMetadata.setArtifacts(artifactsList);
+
+ return serviceAssetMetadata;
+ }
+
+ public static void setArtifactAssetList(List<ArtifactAssetStructure> artifactsList, JsonObject jObject) {
+ JsonArray artifactsArray = jObject.getAsJsonArray("artifacts");
+ if (artifactsArray != null) {
+ for (JsonElement jElement : artifactsArray) {
+ ArtifactAssetStructure artifact = gson.fromJson(jElement, ArtifactAssetStructure.class);
+ artifactsList.add(artifact);
+ }
+ }
+ }
+
+ public static void setResourceInstanceAssetList(List<ResourceInstanceAssetStructure> resourcesList,
+ JsonObject jObject) {
+ JsonArray resourcesArray = jObject.getAsJsonArray("resources");
+ if (resourcesArray != null) {
+ for (JsonElement jElement : resourcesArray) {
+ ResourceInstanceAssetStructure resource = gson.fromJson(jElement, ResourceInstanceAssetStructure.class);
+ resourcesList.add(resource);
+ }
+ }
+ }
+
+ public static List<ServiceAssetStructure> getServiceAssetList(RestResponse assetResponse) {
+ List<ServiceAssetStructure> serviceAssetList = new ArrayList<>();
+
+ JsonElement jelement = new JsonParser().parse(assetResponse.getResponse());
+ JsonArray componenetArray = (JsonArray) jelement;
+ for (JsonElement jElement : componenetArray) {
+ ServiceAssetStructure service = gson.fromJson(jElement, ServiceAssetStructure.class);
+ serviceAssetList.add(service);
+ }
+ return serviceAssetList;
+ }
+
+ public static List<String> getResourceNamesList(List<ResourceAssetStructure> resourceAssetList) {
+ List<String> assetNamesList = new ArrayList<>();
+ for (ResourceAssetStructure resourceAsset : resourceAssetList) {
+ assetNamesList.add(resourceAsset.getName());
+ }
+ return assetNamesList;
+ }
+
+ public static List<String> getServiceNamesList(List<ServiceAssetStructure> serviceAssetList) {
+ List<String> assetNamesList = new ArrayList<>();
+ for (ServiceAssetStructure serviceAsset : serviceAssetList) {
+ assetNamesList.add(serviceAsset.getName());
+ }
+ return assetNamesList;
+ }
+
+ public static void checkResourceTypeInObjectList(List<ResourceAssetStructure> resourceAssetList,
+ ResourceTypeEnum resourceType) {
+ for (ResourceAssetStructure resourceAsset : resourceAssetList) {
+ assertTrue(
+ "Expected resourceType is " + resourceType.toString() + " actual: "
+ + resourceAsset.getResourceType(),
+ resourceAsset.getResourceType().equals(resourceType.toString()));
+ }
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/BaseRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/BaseRestUtils.java
new file mode 100644
index 0000000000..9ffe5782e7
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/BaseRestUtils.java
@@ -0,0 +1,256 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.HttpEntity;
+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.HttpClients;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.validation.BaseValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BaseRestUtils extends BaseValidationUtils {
+ public static final String contentTypeHeaderData = "application/json";
+ public static final String acceptHeaderData = "application/json";
+ public static final String acceptJsonHeader = "application/json";
+ public static final String acceptOctetHeader = "application/octet-stream";
+ public static final String authorizationHeader = "Basic " + Base64.encodeBase64String("ci:123456".getBytes());
+ public static final String acceptOctetStream = "application/octet-stream";
+ public static final String ecomp = "ecomp";
+ public static final String authorizationPrefixString = "Basic ";
+
+ public static final String RESOURCE_COMPONENT_TYPE = "resources";
+ public static final String PRODUCT_COMPONENT_TYPE = "products";
+ public static final String SERVICE_COMPONENT_TYPE = "services";
+
+ public static final int STATUS_CODE_SUCCESS = 200;
+ public static final int STATUS_CODE_CREATED = 201;
+ public static final int STATUS_CODE_DELETE = 204;
+ public static final int STATUS_CODE_NOT_FOUND = 404;
+ public static final int STATUS_CODE_SUCCESS_NO_CONTENT = 204;
+ public static final int STATUS_CODE_SUCCESS_DELETE = 204;
+ public static final int STATUS_CODE_INVALID_CONTENT = 400;
+ public static final int STATUS_CODE_MISSING_DATA = 400;
+ public static final int STATUS_CODE_MISSING_INFORMATION = 403;
+ public static final int STATUS_CODE_RESTRICTED_ACCESS = 403;
+ public static final int STATUS_CODE_ALREADY_EXISTS = 409;
+ public static final int STATUS_CODE_RESTRICTED_OPERATION = 409;
+ public static final int STATUS_CODE_COMPONENT_NAME_EXCEEDS_LIMIT = 400;
+ public static final int STATUS_CODE_MISSING_COMPONENT_NAME = 400;
+ public static final int STATUS_CODE_UNSUPPORTED_ERROR = 400;
+ public static final int STATUS_CODE_IMPORT_SUCCESS = 201;
+ public static final int STATUS_CODE_UPDATE_SUCCESS = 200;
+ public static final int RESTRICTED_OPERATION = 409;
+ public static final int STATUS_CODE_GET_SUCCESS = 200;
+
+ public static final String SUCCESS_MESSAGE = "OK";
+ private static Logger logger = LoggerFactory.getLogger(BaseRestUtils.class.getName());
+
+ private static byte[] encodeBase64;
+
+ // ************* PRIVATE METHODS ************************
+
+ protected static Map<String, String> prepareHeadersMap(String userId) {
+ return prepareHeadersMap(userId, acceptHeaderData);
+ }
+
+ protected static Map<String, String> prepareHeadersMap(String userId, String accept) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ if (contentTypeHeaderData != null) {
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ }
+ if (accept != null) {
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), accept);
+ }
+ if (userId != null) {
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ }
+
+ return headersMap;
+ }
+
+ // send request
+ // GET
+ protected static RestResponse sendGet(String url, String userId) throws IOException {
+ return sendGet(url, userId, null);
+ }
+
+ protected static RestResponse sendGet(String url, String userId, Map<String, String> additionalHeaders)
+ throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ if (additionalHeaders != null) {
+ headersMap.putAll(additionalHeaders);
+ }
+
+ HttpRequest http = new HttpRequest();
+ RestResponse getResourceResponse = http.httpSendGet(url, headersMap);
+ return getResourceResponse;
+ }
+
+ public static RestResponse sendGetAndRemoveHeaders(String url, String userId, List<String> headersToRemove)
+ throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ if (headersToRemove != null) {
+ for (String header : headersToRemove) {
+ headersMap.remove(header);
+ }
+ }
+
+ HttpRequest http = new HttpRequest();
+ RestResponse getResourceResponse = http.httpSendGet(url, headersMap);
+ return getResourceResponse;
+ }
+
+ // PUT
+ protected static RestResponse sendPut(String url, String userBodyJson, String userId, String cont)
+ throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(userId, cont);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse updateResourceResponse = http.httpSendByMethod(url, "PUT", userBodyJson, headersMap);
+ return updateResourceResponse;
+ }
+
+ // POST
+ public static RestResponse sendPost(String url, String userBodyJson, String userId, String accept)
+ throws IOException {
+ return sendPost(url, userBodyJson, userId, accept, null);
+ }
+
+ protected static RestResponse sendPost(String url, String userBodyJson, String userId, String accept,
+ Map<String, String> additionalHeaders) throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(userId, accept);
+ if (additionalHeaders != null) {
+ headersMap.putAll(additionalHeaders);
+ }
+ HttpRequest http = new HttpRequest();
+ RestResponse postResourceResponse = http.httpSendPost(url, userBodyJson, headersMap);
+ return postResourceResponse;
+ }
+
+ // used form complex requests like import categories..
+ protected static RestResponse sendPost(String url, HttpEntity entity, String userId, String accept)
+ throws IOException {
+ RestResponse postResponse = new RestResponse();
+ CloseableHttpResponse response = null;
+ CloseableHttpClient client = null;
+ try {
+ client = HttpClients.createDefault();
+ HttpPost httpPost = new HttpPost(url);
+
+ httpPost.addHeader("USER_ID", userId);
+ httpPost.setEntity(entity);
+ response = client.execute(httpPost);
+ HttpEntity responseEntity = response.getEntity();
+ int statusCode = response.getStatusLine().getStatusCode();
+
+ postResponse.setErrorCode(statusCode);
+ StringBuffer sb = new StringBuffer();
+ try {
+ BufferedReader in = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ sb.append(inputLine);
+ }
+ in.close();
+ } catch (Exception e) {
+ logger.debug("response body is null");
+ }
+ postResponse.setResponse(sb.toString());
+ } finally {
+ try {
+ if (response != null) {
+ response.close();
+ }
+
+ } catch (IOException e) {
+ logger.debug("failed to close client or response: ", e);
+ }
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ logger.debug("failed to close client or response: ", e);
+ }
+ }
+ return postResponse;
+ }
+
+ // DELETE
+ protected static RestResponse sendDelete(String url, String userId) throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse deleteResourceResponse = http.httpSendDelete(url, headersMap);
+ return deleteResourceResponse;
+ }
+
+ /*
+ * // ------ protected static Boolean checkErrorCode(RestResponse
+ * deleteResponse) { if (deleteResponse.getErrorCode() ==
+ * STATUS_CODE_SUCCESS || deleteResponse.getErrorCode() ==
+ * STATUS_CODE_DELETE) { return true; } return false; }
+ *
+ * // *** STATUS CODE VALIDATION UTIITIES **** public static void
+ * checkStatusCode(RestResponse response, String assertMessage, boolean AND,
+ * int... statuses) { int statusCode = response.getErrorCode(); for (int
+ * status : statuses) { if (AND && statusCode != status) {
+ * Assert.fail(assertMessage + " status: " + statusCode); } else if
+ * (statusCode == status) { return; } } if (!AND) {
+ * Assert.fail(assertMessage + " status: " + statusCode); } }
+ *
+ * public static void checkDeleteResponse(RestResponse response) {
+ * checkStatusCode(response,"delete request failed",false,STATUS_CODE_DELETE
+ * ,STATUS_CODE_NOT_FOUND, STATUS_CODE_SUCCESS); // STATUS_CODE_SUCCESS for
+ * deActivate user }
+ *
+ * public static void checkCreateResponse(RestResponse response) {
+ * checkStatusCode(response, "create request failed", false,
+ * STATUS_CODE_CREATED); }
+ */
+ public static String encodeUrlForDownload(String url) {
+ return url.replaceAll(" ", "%20");
+ }
+
+ public static Map<String, String> addAuthorizeHeader(String userName, String password) {
+ String userCredentials = userName + ":" + password;
+ encodeBase64 = Base64.encodeBase64(userCredentials.getBytes());
+ String encodedUserCredentials = authorizationPrefixString + new String(encodeBase64);
+ Map<String, String> authorizationHeader = new HashMap<String, String>();
+ authorizationHeader.put(HttpHeaderEnum.AUTHORIZATION.getValue(), encodedUserCredentials);
+ return authorizationHeader;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CatalogRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CatalogRestUtils.java
new file mode 100644
index 0000000000..a265639804
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CatalogRestUtils.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+public class CatalogRestUtils extends BaseRestUtils {
+
+ public static RestResponse getAbstractResources() throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_ABSTRACT_RESOURCES, config.getCatalogBeHost(),
+ config.getCatalogBePort());
+
+ return sendGet(url, UserRoleEnum.DESIGNER.getUserId());
+ }
+
+ public static RestResponse getCatalog() throws IOException {
+ return getCatalog(UserRoleEnum.DESIGNER.getUserId());
+ }
+
+ public static RestResponse getCatalog(String userId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_CATALOG_DATA, config.getCatalogBeHost(), config.getCatalogBePort());
+ return sendGet(url, userId);
+ }
+
+ public static RestResponse getAllCategoriesTowardsCatalogBe() throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_CATEGORIES, config.getCatalogBeHost(), config.getCatalogBePort(),
+ BaseRestUtils.RESOURCE_COMPONENT_TYPE);
+
+ return sendGet(url, UserRoleEnum.DESIGNER.getUserId());
+ }
+
+ public static RestResponse getAllCategoriesTowardsCatalogFeWithUuid(String uuid) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_CATEGORIES_FE, config.getCatalogFeHost(), config.getCatalogFePort(),
+ BaseRestUtils.RESOURCE_COMPONENT_TYPE);
+
+ Map<String, String> additionalHeaders = new HashMap<String, String>();
+ additionalHeaders.put(HttpHeaderEnum.X_ECOMP_REQUEST_ID_HEADER.getValue(), uuid);
+
+ return sendGet(url, UserRoleEnum.DESIGNER.getUserId(), additionalHeaders);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CategoryRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CategoryRestUtils.java
new file mode 100644
index 0000000000..d9ae91b955
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/CategoryRestUtils.java
@@ -0,0 +1,298 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+public class CategoryRestUtils extends BaseRestUtils {
+
+ private static final int STATUS_CODE_CREATED = 201;
+
+ private static Gson gson = new Gson();
+
+ public static RestResponse createCategory(CategoryDefinition categoryDefinition, User sdncModifierDetails,
+ String categoryType) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_CATEGORY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ categoryType);
+ String bodyJson = gson.toJson(categoryDefinition);
+ RestResponse addCategoryResponse = BaseRestUtils.sendPost(url, bodyJson, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+ if (addCategoryResponse.getErrorCode().intValue() == STATUS_CODE_CREATED)
+ categoryDefinition.setUniqueId(
+ ResponseParser.getValueFromJsonResponse(addCategoryResponse.getResponse(), "uniqueId"));
+ return addCategoryResponse;
+ }
+
+ // GET categories
+ public static RestResponse getAllCategories(User sdncModifierDetails, String categoryType) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_CATEGORIES, config.getCatalogBeHost(), config.getCatalogBePort(), categoryType);
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ RestResponse getCategotyResponse = http.httpSendGet(url, headersMap);
+ return getCategotyResponse;
+ }
+
+ public static RestResponse getAllCategoriesTowardsFe(User sdncModifierDetails, String categoryType) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_CATEGORIES_FE, config.getCatalogFeHost(), config.getCatalogFePort(), categoryType);
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ RestResponse getCategotyResponse = http.httpSendGet(url, headersMap);
+ return getCategotyResponse;
+ }
+
+ // Delete Category
+ public static RestResponse deleteCategory(String categoryId, String psUserId, String categoryType) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_CATEGORY, config.getCatalogBeHost(), config.getCatalogBePort(), categoryType, categoryId);
+ url = url.replace("#", "%23"); // HEX
+ url = url.replace(" ", "%20"); // HEX
+ RestResponse deleteCategoryResponse = sendDelete(url, psUserId);
+ return deleteCategoryResponse;
+ }
+
+ public static RestResponse createSubCategory(SubCategoryDefinition subCategory, CategoryDefinition parentCategory,
+ User sdncModifierDetails, String categoryType) throws Exception {
+ // categoryType = service/resource/product
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_SUB_CATEGORY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ categoryType, parentCategory.getUniqueId());
+ String bodyJson = gson.toJson(subCategory);
+ RestResponse createSubCategoryPost = BaseRestUtils.sendPost(url, bodyJson, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+ if (createSubCategoryPost.getErrorCode().intValue() == STATUS_CODE_CREATED)
+ subCategory.setUniqueId(
+ ResponseParser.getValueFromJsonResponse(createSubCategoryPost.getResponse(), "uniqueId"));
+
+ return createSubCategoryPost;
+ }
+
+ public static RestResponse deleteSubCategory(String subCategoryId, String categoryId, String psUserId,
+ String categoryType) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_SUB_CATEGORY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ categoryType, categoryId, subCategoryId);
+ url = url.replace("#", "%23"); // HEX
+ url = url.replace(" ", "%20"); // HEX
+ RestResponse deleteSubCategoryResponse = sendDelete(url, psUserId);
+ return deleteSubCategoryResponse;
+ }
+
+ public static RestResponse deleteGrouping(String groupId, String subCategoryId, String categoryId, String psUserId,
+ String categoryType) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_GROUPING, config.getCatalogBeHost(), config.getCatalogBePort(),
+ categoryType, categoryId, subCategoryId, groupId);
+ url = url.replace("#", "%23"); // HEX
+ url = url.replace(" ", "%20"); // HEX
+ RestResponse deleteGroupResponse = sendDelete(url, psUserId);
+ return deleteGroupResponse;
+ }
+
+ public static RestResponse createServiceCategoryHttpCspAtuUidIsMissing(CategoryDefinition categoryDataDefinition,
+ User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_CATEGORY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ SERVICE_COMPONENT_TYPE);
+
+ Map<String, String> headersMap = prepareHeadersMap(sdncModifierDetails.getUserId());
+ headersMap.remove("USER_ID");
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(categoryDataDefinition);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+ RestResponse createCatergoryResponse = http.httpSendPost(url, userBodyJson, headersMap);
+ return createCatergoryResponse;
+ }
+
+ public static RestResponse createSubCategoryHttpCspAtuUidIsMissing(SubCategoryDefinition subCategory,
+ CategoryDefinition parentCategory, User sdncModifierDetails, String categoryType) throws Exception {
+ // categoryType = service/resource/product
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_SUB_CATEGORY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ categoryType, parentCategory.getUniqueId());
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ headersMap.remove("USER_ID");
+ Gson gson = new Gson();
+ String subCatJson = gson.toJson(subCategory);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(subCatJson);
+ RestResponse addCategoryResponse = http.httpSendPost(url, subCatJson, headersMap);
+ return addCategoryResponse;
+ }
+
+ public static RestResponse deleteCatergoryHttpCspAtuUidIsMissing(CategoryDefinition categoryDataDefinition, User sdncModifierDetails) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_CONSUMER, config.getCatalogBeHost(), config.getCatalogBePort(), categoryDataDefinition.getName());
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ headersMap.remove("USER_ID");
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(categoryDataDefinition);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+ RestResponse deleteCategotyResponse = http.httpSendDelete(url, headersMap);
+ return deleteCategotyResponse;
+ }
+
+ public static RestResponse createGrouping(GroupingDefinition grouping, SubCategoryDefinition subCategory,
+ CategoryDefinition parentCategory, User sdncModifierDetails, String categoryType) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_GROUPING, config.getCatalogBeHost(), config.getCatalogBePort(), categoryType, parentCategory.getUniqueId(), subCategory.getUniqueId());
+ String bodyJson = gson.toJson(grouping);
+ RestResponse addGroupingResponse = BaseRestUtils.sendPost(url, bodyJson, sdncModifierDetails.getUserId(), acceptHeaderData);
+ return addGroupingResponse;
+ }
+
+ public static RestResponse importCategories(MultipartEntityBuilder mpBuilder, String userId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.IMPORT_CATEGORIES, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ RestResponse importResponse = BaseRestUtils.sendPost(url, mpBuilder.build(), userId, acceptHeaderData);
+ return importResponse;
+ }
+
+ public static int getMatchingCategoriesNum(RestResponse getAllCategoryRest, CategoryDefinition categoryDefinition) {
+ String response = getAllCategoryRest.getResponse();
+ Gson gson = new Gson();
+ List<CategoryDefinition> categoryDefinitions = gson.fromJson(response,
+ new TypeToken<List<CategoryDefinition>>() {
+ }.getType());
+ int categoriesNum = 0;
+ String catName = categoryDefinition.getName();
+ for (CategoryDefinition elem : categoryDefinitions) {
+ if (elem.getName().equals(catName)) {
+ categoryDefinition.setUniqueId(elem.getUniqueId());
+ categoriesNum++;
+ }
+ }
+
+ return categoriesNum;
+ }
+
+ public static int getMatchingSubCategoriesNum(RestResponse getAllCategoryRest, String parentCategoryId,
+ SubCategoryDefinition expectedSubCategoryDefinition) {
+
+ String response = getAllCategoryRest.getResponse();
+ Gson gson = new Gson();
+ List<CategoryDefinition> categoryDefinitions = gson.fromJson(response,
+ new TypeToken<List<CategoryDefinition>>() {
+ }.getType());
+ int subCatNum = 0;
+ String subCatName = expectedSubCategoryDefinition.getName();
+ for (CategoryDefinition elem : categoryDefinitions) {
+ if (elem.getUniqueId().equals(parentCategoryId)) {
+ List<SubCategoryDefinition> subCategories = elem.getSubcategories();
+ if (subCategories != null) {
+ for (SubCategoryDefinition subCategoryDataDefinition : subCategories) {
+ if (subCatName.equals(subCategoryDataDefinition.getName())) {
+ expectedSubCategoryDefinition.setUniqueId(subCategoryDataDefinition.getUniqueId());
+ subCatNum++;
+ }
+ }
+ }
+
+ }
+ }
+ return subCatNum;
+ }
+
+ public static int getMatchingGroupingNum(RestResponse getAllCategoryRest, String parentCategoryId,
+ String subCategoryId, GroupingDefinition expectedGroupingDefinition) {
+
+ String response = getAllCategoryRest.getResponse();
+ Gson gson = new Gson();
+ List<CategoryDefinition> categoryDefinitions = gson.fromJson(response,
+ new TypeToken<List<CategoryDefinition>>() {
+ }.getType());
+ int groupingNum = 0;
+ String groupingName = expectedGroupingDefinition.getName();
+ for (CategoryDefinition elem : categoryDefinitions) {
+ if (elem.getUniqueId().equals(parentCategoryId)) {
+ List<SubCategoryDefinition> subCategories = elem.getSubcategories();
+ if (subCategories != null) {
+ for (SubCategoryDefinition subCategoryDataDefinition : subCategories) {
+ // if
+ // (subCategoryId.equals(subCategoryDataDefinition.getUniqueId()))
+ // {
+ if (subCategoryId.equals(subCategoryDataDefinition.getUniqueId())
+ && subCategoryDataDefinition.getGroupings() != null) {
+ List<GroupingDefinition> grouping = subCategoryDataDefinition.getGroupings();
+ for (GroupingDefinition groupingDataDefinition : grouping) {
+ if (groupingName.equals(groupingDataDefinition.getName())) {
+ expectedGroupingDefinition.setUniqueId(groupingDataDefinition.getUniqueId());
+ groupingNum++;
+ }
+ }
+
+ }
+ }
+ }
+
+ }
+ }
+ return groupingNum;
+ }
+
+ public enum CategoryAuditJsonKeysEnum {
+ ACTION("ACTION"), MODIFIER("MODIFIER"), CATEGORY_NAME("CATEGORY_NAME"), SUB_CATEGORY_NAME("SUB_CATEGORY_NAME"), GROUPING_NAME("GROUPING_NAME"), RESOURCE_TYPE("RESOURCE_TYPE"), ECOMP_USER("ECOMP_USER"), STATUS("STATUS"),
+ DESCRIPTION("DESCRIPTION"), DETAILS("DETAILS");
+
+ private String auditJsonKeyName;
+
+ private CategoryAuditJsonKeysEnum(String auditJsonKeyName) {
+ this.auditJsonKeyName = auditJsonKeyName;
+ }
+
+ public String getAuditJsonKeyName() {
+ return auditJsonKeyName.toLowerCase();
+ }
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentInstanceRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentInstanceRestUtils.java
new file mode 100644
index 0000000000..10587390f2
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentInstanceRestUtils.java
@@ -0,0 +1,276 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+import com.google.gson.Gson;
+
+public class ComponentInstanceRestUtils extends BaseRestUtils {
+ public static String acceptHeaderDate = "application/json";
+ static Config config = Config.instance();
+ public static Gson gson = new Gson();
+
+ // 'componentType' can be 'services' or 'resources'
+
+ public static RestResponse createComponentInstance(ComponentInstanceReqDetails componentInstanceReqDetails,
+ User sdncModifierDetails, Component component) throws Exception {
+ return createComponentInstance(componentInstanceReqDetails, sdncModifierDetails, component.getUniqueId(),
+ component.getComponentType());
+ }
+
+ public static RestResponse createComponentInstance(ComponentInstanceReqDetails componentInstanceReqDetails,
+ User sdncModifierDetails, String componentId, ComponentTypeEnum componentType) throws Exception {
+
+ return createComponentInstance(componentInstanceReqDetails, sdncModifierDetails, componentId,
+ ComponentTypeEnum.findParamByType(componentType));
+ }
+
+ public static RestResponse createComponentInstance(ComponentInstanceReqDetails componentInstanceReqDetails,
+ User sdncModifierDetails, String componentId, String componentType) throws Exception {
+ Config config = Utils.getConfig();
+ String userId = sdncModifierDetails.getUserId();
+ String serviceBodyJson = gson.toJson(componentInstanceReqDetails);
+ String url = String.format(Urls.CREATE_COMPONENT_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ componentType, componentId);
+ RestResponse createResourceInstance = sendPost(url, serviceBodyJson, userId, acceptHeaderData);
+ if (createResourceInstance.getErrorCode().equals(BaseRestUtils.STATUS_CODE_CREATED)) {
+ String uniqueId = ResponseParser.getValueFromJsonResponse(createResourceInstance.getResponse(), "uniqueId");
+ componentInstanceReqDetails.setUniqueId(uniqueId);
+ // Gson gson = new Gson();
+ // ResourceInstanceReqDetails fromJson =
+ // gson.fromJson(createResourceInstance.getResponse(),
+ // ResourceInstanceReqDetails.class);
+ // componentInstanceReqDetails.setUniqueId(fromJson.getUniqueId());
+ }
+ return createResourceInstance;
+ }
+
+ public static RestResponse getComponentInstances(ComponentTypeEnum type, String componentId, User user)
+ throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), user.getUserId());
+
+ String url = String.format(Urls.GET_COMPONENT_INSTANCES, config.getCatalogBeHost(), config.getCatalogBePort(),
+ ComponentTypeEnum.findParamByType(type), componentId);
+
+ RestResponse sendGetServerRequest = sendGet(url, user.getUserId(), headersMap);
+
+ return sendGetServerRequest;
+
+ }
+
+ public static RestResponse deleteComponentInstance(User sdncModifierDetails, String componentId,
+ String resourceInstanceId, ComponentTypeEnum componentType) throws Exception {
+
+ return deleteComponentInstance(sdncModifierDetails, componentId, resourceInstanceId,
+ ComponentTypeEnum.findParamByType(componentType));
+ }
+
+ public static RestResponse deleteComponentInstance(User sdncModifierDetails, String componentId,
+ String resourceInstanceId, String componentTypeString) throws Exception {
+ Config config = Utils.getConfig();
+ String userId = sdncModifierDetails.getUserId();
+ String url = String.format(Urls.DELETE_COMPONENT_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ componentTypeString, componentId, resourceInstanceId);
+ RestResponse sendCreateUserRequest = sendDelete(url, userId);
+ return sendCreateUserRequest;
+ }
+
+ public static RestResponse updateComponentInstance(ComponentInstanceReqDetails componentInstanceReqDetails,
+ User sdncModifierDetails, String componentId, ComponentTypeEnum componentType) throws IOException {
+
+ Config config = Utils.getConfig();
+ String userId = sdncModifierDetails.getUserId();
+ String serviceBodyJson = gson.toJson(componentInstanceReqDetails);
+ String url = String.format(Urls.UPDATE_COMPONENT_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ ComponentTypeEnum.findParamByType(componentType), componentId,
+ componentInstanceReqDetails.getUniqueId());
+ RestResponse updateResourceInstance = sendPost(url, serviceBodyJson, userId, acceptHeaderData);
+ return updateResourceInstance;
+ }
+
+ // TODO Tal CI for New API Multiple Instance Update
+ public static RestResponse updateMultipleComponentInstance(
+ List<ComponentInstanceReqDetails> componentInstanceReqDetailsList, User sdncModifierDetails,
+ String componentId, ComponentTypeEnum componentType) throws IOException {
+ Config config = Utils.getConfig();
+ String userId = sdncModifierDetails.getUserId();
+ String serviceBodyJson = gson.toJson(componentInstanceReqDetailsList.toArray());
+ String url = String.format(Urls.UPDATE_MULTIPLE_COMPONENT_INSTANCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(componentType), componentId);
+ RestResponse updateResourceInstance = sendPost(url, serviceBodyJson, userId, acceptHeaderData);
+ return updateResourceInstance;
+ }
+
+ public static RestResponse associateInstances(RequirementCapabilityRelDef relation, User sdncModifierDetails,
+ String componentId, ComponentTypeEnum componentTypeEnum) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ String componentType = "";
+ switch (componentTypeEnum) {
+ case RESOURCE:
+ componentType = ComponentTypeEnum.RESOURCE_PARAM_NAME;
+ break;
+ case SERVICE:
+ componentType = ComponentTypeEnum.SERVICE_PARAM_NAME;
+ break;
+ default:
+ break;
+ }
+ String serviceBodyJson = gson.toJson(relation);
+ String url = String.format(Urls.ASSOCIATE__RESOURCE_INSTANCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), componentType, componentId);
+
+ RestResponse associateInstance = sendPost(url, serviceBodyJson, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+ return associateInstance;
+
+ }
+
+ public static RestResponse dissociateInstances(RequirementCapabilityRelDef relation, User sdncModifierDetails,
+ String componentId, ComponentTypeEnum componentTypeEnum) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ String componentType = "";
+ switch (componentTypeEnum) {
+ case RESOURCE:
+ componentType = ComponentTypeEnum.RESOURCE_PARAM_NAME;
+ break;
+ case SERVICE:
+ componentType = ComponentTypeEnum.SERVICE_PARAM_NAME;
+ break;
+ default:
+ break;
+ }
+ String serviceBodyJson = gson.toJson(relation);
+ String url = String.format(Urls.DISSOCIATE__RESOURCE_INSTANCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), componentType, componentId);
+
+ RestResponse associateInstance = sendPut(url, serviceBodyJson, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+ return associateInstance;
+
+ }
+
+ public static void checkComponentInstanceType(RestResponse response, String expectedComponentType) {
+ String actualComponentType = ResponseParser.getComponentTypeFromResponse(response);
+ assertTrue(expectedComponentType.equals(actualComponentType),
+ "Failed. expected: " + expectedComponentType + ", actual: " + actualComponentType + "/");
+ }
+
+ public static RestResponse updatePropertyValueOnResourceInstance(Component component, ComponentInstance instDetails,
+ User user, ComponentInstanceProperty updatedInstanceProperty) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), user.getUserId());
+
+ String url = String.format(Urls.UPDATE_PROPERTY_TO_RESOURCE_INSTANCE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(component.getComponentType()),
+ component.getUniqueId(), instDetails.getUniqueId());
+ String body = gson.toJson(updatedInstanceProperty);
+
+ RestResponse sendGetServerRequest = sendPost(url, body, user.getUserId(), acceptHeaderData);
+ return sendGetServerRequest;
+
+ }
+
+ public static RestResponse changeComponentInstanceVersion(Component container,
+ ComponentInstance componentInstanceToReplace, Component newInstance, User sdncModifierDetails)
+ throws Exception {
+
+ return changeComponentInstanceVersion(container.getUniqueId(), componentInstanceToReplace, newInstance,
+ sdncModifierDetails, container.getComponentType());
+ }
+
+ public static RestResponse changeComponentInstanceVersion(String containerUID,
+ ComponentInstance componentInstanceToReplace, Component component, User sdncModifierDetails,
+ ComponentTypeEnum componentType) throws IOException {
+
+ Config config = Utils.getConfig();
+ String resourceUid = ("{\"componentUid\":\"" + component.getUniqueId() + "\"}");
+ String url = String.format(Urls.CHANGE__RESOURCE_INSTANCE_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(componentType), containerUID,
+ componentInstanceToReplace.getUniqueId());
+ RestResponse changeResourceInstanceVersion = sendPost(url, resourceUid, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+
+ if (changeResourceInstanceVersion.getErrorCode() == 200
+ || changeResourceInstanceVersion.getErrorCode() == 201) {
+ Gson gson = new Gson();
+ // ResourceInstanceReqDetails
+ // convertResourceInstanceResponseToJavaObject =
+ // ResponseParser.convertResourceInstanceResponseToJavaObject(createResourceInstance.getResponse());
+ ComponentInstanceReqDetails fromJson = gson.fromJson(changeResourceInstanceVersion.getResponse(),
+ ComponentInstanceReqDetails.class);
+
+ componentInstanceToReplace.setUniqueId(fromJson.getUniqueId());
+
+ }
+
+ return changeResourceInstanceVersion;
+
+ }
+
+ public static RestResponse changeComponentInstanceVersion(String componentUniqueId,
+ String serviceInstanceToReplaceUniqueId, String serviceUniqueId, User sdncModifierDetails,
+ ComponentTypeEnum componentType) throws IOException {
+ Config config = Utils.getConfig();
+ String resourceUid = ("{\"componentUid\":\"" + serviceUniqueId + "\"}");
+ String url = String.format(Urls.CHANGE__RESOURCE_INSTANCE_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(componentType), componentUniqueId,
+ serviceInstanceToReplaceUniqueId);
+ RestResponse changeResourceInstanceVersion = sendPost(url, resourceUid, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+ return changeResourceInstanceVersion;
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentRestUtils.java
new file mode 100644
index 0000000000..dcebe4afa8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ComponentRestUtils.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+public class ComponentRestUtils extends BaseRestUtils {
+ public static RestResponse getComponentRequirmentsCapabilities(User sdncModifierDetails,
+ ComponentReqDetails componentReqDetails) throws IOException {
+ Config config = Utils.getConfig();
+ ComponentTypeEnum componentType = null;
+ if (componentReqDetails instanceof ResourceReqDetails) {
+ componentType = ComponentTypeEnum.RESOURCE;
+ } else if (componentReqDetails instanceof ServiceReqDetails) {
+ componentType = ComponentTypeEnum.SERVICE;
+ } else if (componentReqDetails instanceof ProductReqDetails) {
+ componentType = ComponentTypeEnum.PRODUCT;
+ }
+ String url = String.format(Urls.GET_COMPONENT_REQUIRMENTS_CAPABILITIES, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(componentType),
+ componentReqDetails.getUniqueId());
+ return sendGet(url, sdncModifierDetails.getUserId());
+ }
+
+ public static CapReqDef getAndParseComponentRequirmentsCapabilities(User user, ComponentReqDetails componentDetails)
+ throws IOException {
+ RestResponse getComponentReqCap = getComponentRequirmentsCapabilities(user, componentDetails);
+ ResourceRestUtils.checkSuccess(getComponentReqCap);
+ CapReqDef capReqDef = ResponseParser.parseToObject(getComponentReqCap.getResponse(), CapReqDef.class);
+ return capReqDef;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ConsumerRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ConsumerRestUtils.java
new file mode 100644
index 0000000000..1b93d8d778
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ConsumerRestUtils.java
@@ -0,0 +1,206 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.Map;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+import com.google.gson.Gson;
+
+public class ConsumerRestUtils extends BaseRestUtils {
+
+ public static final int STATUS_CODE_SUCCESS = 200;
+ public static final int STATUS_CODE_CREATED = 201;
+ public static final int STATUS_CODE_DELETE = 204;
+ public static final int STATUS_CODE_NOT_FOUND = 404;
+ Utils utils = new Utils();
+ private static Long expectedsLastupdatedtime;
+ private static Long expectedLastAuthenticationTime;
+
+ public static RestResponse createConsumer(ConsumerDataDefinition consumerDataDefinition, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_CONSUMER, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ String userId = sdncModifierDetails.getUserId();
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(consumerDataDefinition);
+
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+ RestResponse createConsumerResponse = http.httpSendPost(url, userBodyJson, headersMap);
+ if (createConsumerResponse.getErrorCode() == STATUS_CODE_CREATED) {
+ ConsumerDataDefinition getConsumerDataObject = parseComsumerResp(createConsumerResponse);
+ consumerDataDefinition.setConsumerDetailsLastupdatedtime(getConsumerDataObject.getConsumerDetailsLastupdatedtime());
+ consumerDataDefinition.setConsumerLastAuthenticationTime(getConsumerDataObject.getConsumerLastAuthenticationTime());
+ consumerDataDefinition.setLastModfierAtuid(getConsumerDataObject.getLastModfierAtuid());
+ }
+ return createConsumerResponse;
+ }
+
+ public static RestResponse createConsumerHttpCspAtuUidIsMissing(ConsumerDataDefinition consumerDataDefinition, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_CONSUMER, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ headersMap.remove("USER_ID");
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(consumerDataDefinition);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+ RestResponse createConsumerResponse = http.httpSendPost(url, userBodyJson, headersMap);
+ if (createConsumerResponse.getErrorCode() == STATUS_CODE_CREATED) {
+ ConsumerDataDefinition getConsumerDataObject = parseComsumerResp(createConsumerResponse);
+ consumerDataDefinition.setConsumerDetailsLastupdatedtime(getConsumerDataObject.getConsumerDetailsLastupdatedtime());
+ consumerDataDefinition.setConsumerLastAuthenticationTime(getConsumerDataObject.getConsumerLastAuthenticationTime());
+ consumerDataDefinition.setLastModfierAtuid(getConsumerDataObject.getLastModfierAtuid());
+ }
+ return createConsumerResponse;
+ }
+
+ public static RestResponse deleteConsumerHttpCspAtuUidIsMissing(ConsumerDataDefinition consumerDataDefinition, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_CONSUMER, config.getCatalogBeHost(), config.getCatalogBePort(), consumerDataDefinition.getConsumerName());
+
+ String userId = sdncModifierDetails.getUserId();
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ headersMap.remove("USER_ID");
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(consumerDataDefinition);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+ RestResponse deleteConsumerResponse = http.httpSendDelete(url, headersMap);
+ return deleteConsumerResponse;
+ }
+
+ public static ConsumerDataDefinition parseComsumerResp(RestResponse restResponse) throws Exception {
+
+ String bodyToParse = restResponse.getResponse();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ ConsumerDataDefinition component = mapper.readValue(bodyToParse, ConsumerDataDefinition.class);
+ return component;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public static RestResponse deleteConsumer(ConsumerDataDefinition consumerDataDefinition, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_CONSUMER, config.getCatalogBeHost(), config.getCatalogBePort(), consumerDataDefinition.getConsumerName());
+
+ String userId = sdncModifierDetails.getUserId();
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ RestResponse deleteConsumerResponse = http.httpSendDelete(url, headersMap);
+ return deleteConsumerResponse;
+ }
+
+ public static RestResponse getConsumer(ConsumerDataDefinition consumerDataDefinition, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_CONSUMER, config.getCatalogBeHost(), config.getCatalogBePort(), consumerDataDefinition.getConsumerName());
+
+ String userId = sdncModifierDetails.getUserId();
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ RestResponse getConsumerResponse = http.httpSendGet(url, headersMap);
+ return getConsumerResponse;
+ }
+
+ public static void validateConsumerReqVsResp(ConsumerDataDefinition consumerDefinition, ConsumerDataDefinition getConsumerDataObject) {
+
+ String expected;
+
+ expected = consumerDefinition.getConsumerName();
+ assertEquals("consumer name - ", expected, getConsumerDataObject.getConsumerName());
+
+ expected = consumerDefinition.getConsumerPassword().toLowerCase();
+ assertEquals("consumer password - ", expected, getConsumerDataObject.getConsumerPassword());
+
+ expected = consumerDefinition.getLastModfierAtuid();
+ assertEquals("consumer Last Modfier Atuid - ", expected, getConsumerDataObject.getLastModfierAtuid());
+
+ expected = consumerDefinition.getConsumerSalt();
+ assertEquals("consumer Salt - ", expected, getConsumerDataObject.getConsumerSalt());
+
+ expectedsLastupdatedtime = consumerDefinition.getConsumerDetailsLastupdatedtime();
+ assertEquals("consumer Last updated time - ", expectedsLastupdatedtime, getConsumerDataObject.getConsumerDetailsLastupdatedtime());
+
+ expectedLastAuthenticationTime = consumerDefinition.getConsumerLastAuthenticationTime();
+ assertEquals("consumer Last authentication time - ", expectedLastAuthenticationTime, getConsumerDataObject.getConsumerLastAuthenticationTime());
+ }
+
+ ///// New
+ public enum EcompConsumerAuditJsonKeysEnum {
+ ACTION("ACTION"), MODIFIER("MODIFIER"), ECOMP_USER("ECOMP_USER"), STATUS("STATUS"), DESC("DESCRIPTION");
+ private String auditJsonKeyName;
+
+ private EcompConsumerAuditJsonKeysEnum(String auditJsonKeyName) {
+ this.auditJsonKeyName = auditJsonKeyName;
+ }
+
+ public String getAuditJsonKeyName() {
+ return auditJsonKeyName.toLowerCase();
+ }
+ }
+
+ /*
+ * protected void resourceArtifatAuditSuccess(String action, ArtifactReqDetails artifact, ResourceReqDetails resourceDetails , User user) throws Exception { ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject =
+ * Convertor.constructFieldsForAuditValidation(resourceDetails, resourceDetails.getVersion(), user); String auditAction = action; expectedResourceAuditJavaObject.setAction(auditAction); expectedResourceAuditJavaObject.setPrevState("");
+ * expectedResourceAuditJavaObject.setPrevVersion(""); expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum. NOT_CERTIFIED_CHECKOUT).toString()); expectedResourceAuditJavaObject.setStatus("200");
+ * expectedResourceAuditJavaObject.setDesc("OK"); expectedResourceAuditJavaObject.setArtifactName(artifact.getArtifactName( )); AuditUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null); }
+ */
+
+ /*
+ * protected void resourceArtifatValidateAuditWithErrorMessage(String actionStatus, ResourceReqDetails resourceDetails, String auditAction, String setCurrState, Object ... variables)throws Exception { ErrorInfo errorInfo =
+ * utils.parseYaml(actionStatus); ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor.constructFieldsForAuditValidation(resourceDetails, resourceDetails.getVersion(), sdncUserDetails);
+ * expectedResourceAuditJavaObject.setAction(auditAction); expectedResourceAuditJavaObject.setPrevState(""); expectedResourceAuditJavaObject.setPrevVersion(""); expectedResourceAuditJavaObject.setCurrState(setCurrState);
+ * expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString()) ; expectedResourceAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables) ); expectedResourceAuditJavaObject.setArtifactName("");
+ * AuditUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, null); }
+ */
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/EcompUserRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/EcompUserRestUtils.java
new file mode 100644
index 0000000000..a71711dde6
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/EcompUserRestUtils.java
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.portalsdk.core.restful.domain.EcompRole;
+import org.openecomp.portalsdk.core.restful.domain.EcompUser;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.run.StartTest;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class EcompUserRestUtils extends BaseRestUtils {
+
+ static Gson gson = new Gson();
+
+ static Logger logger = LoggerFactory.getLogger(UserRestUtils.class.getName());
+ static String contentTypeHeaderData = "application/json";
+ static String acceptHeaderDate = "application/json";
+ static String ecompUsername = "12345";
+ static String ecompPassword = "12345";
+
+ public EcompUserRestUtils() {
+ super();
+
+ StartTest.enableLogger();
+ }
+
+ public static RestResponse pushUser(EcompUser ecompUser) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ String userBodyJson = gson.toJson(ecompUser);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_PUSH_USER, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ logger.debug("Send POST request to create user: {}", url);
+ logger.debug("User body: {}", userBodyJson);
+ logger.debug("User headers: {}", headersMap);
+
+ RestResponse sendPushUserResponse = http.httpSendPost(url, userBodyJson, headersMap);
+
+ return sendPushUserResponse;
+ }
+
+ /*
+ * loginId - equals to userId
+ */
+ public static RestResponse editUser(String loginId, EcompUser ecompUser) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ String userBodyJson = gson.toJson(ecompUser);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_EDIT_USER, config.getCatalogBeHost(), config.getCatalogBePort(), loginId);
+
+ logger.debug("Send POST request to edit user: {}", url);
+ logger.debug("User body: {}", userBodyJson);
+ logger.debug("User headers: {}", headersMap);
+
+ RestResponse sendEditUserResponse = http.httpSendPost(url, userBodyJson, headersMap);
+
+ return sendEditUserResponse;
+ }
+
+ /*
+ * loginId - equals to userId
+ */
+ public static RestResponse getUser(String loginId) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_GET_USER, config.getCatalogBeHost(), config.getCatalogBePort(), loginId);
+
+ logger.debug("Send GET request to get user: {}", url);
+ logger.debug("User headers: {}", headersMap);
+
+ RestResponse sendGetUserRequest = http.httpSendGet(url, headersMap);
+
+ return sendGetUserRequest;
+ }
+
+ public static RestResponse getAllUsers() throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_GET_ALL_USERS, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ logger.debug("Send POST request to get all users: {}", url);
+ logger.debug("User headers: {}" , headersMap);
+
+ RestResponse sendGetAllUsersRequest = http.httpSendGet(url, headersMap);
+
+ return sendGetAllUsersRequest;
+ }
+
+ public static RestResponse getAllAvailableRoles() throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_GET_ALL_AVAILABLE_ROLES, config.getCatalogBeHost(),
+ config.getCatalogBePort());
+
+ logger.debug("Send GET request to get all available roles: {}", url);
+ logger.debug("User headers: {}", headersMap);
+
+ RestResponse sendUpdateUserRequest = http.httpSendGet(url, headersMap);
+
+ return sendUpdateUserRequest;
+ }
+
+ /*
+ * loginId - equals to userId
+ */
+ public static RestResponse pushUserRoles(String loginId, List<EcompRole> roles) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ String roleBodyJson = gson.toJson(roles);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_PUSH_USER_ROLES, config.getCatalogBeHost(), config.getCatalogBePort(),
+ loginId);
+
+ logger.debug("Send POST request to push user role: {}", url);
+ logger.debug("Roles body: {}", roleBodyJson);
+ logger.debug("Request headers: {}", headersMap);
+
+ RestResponse sendpushUserRolesResponse = http.httpSendPost(url, roleBodyJson, headersMap);
+
+ return sendpushUserRolesResponse;
+ }
+
+ /*
+ * loginId - equals to userId
+ */
+ public static RestResponse getUserRoles(String loginId) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.ECOMP_USERNAME.getValue(), ecompUsername);
+ headersMap.put(HttpHeaderEnum.ECOMP_PASSWORD.getValue(), ecompPassword);
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.ECOMP_GET_USER_ROLES, config.getCatalogBeHost(), config.getCatalogBePort(),
+ loginId);
+
+ logger.debug("Send GET request to get user roles: {}", url);
+ logger.debug("User headers: {}", headersMap);
+
+ RestResponse sendGetUserRolesRequest = http.httpSendGet(url, headersMap);
+
+ return sendGetUserRolesRequest;
+ }
+
+ // TODO !!!!!!!!!!!!!!
+ /*
+ * Ask Eli if implementation of users is needed DELETE ECOMP USER
+ */
+
+ /*
+ * public static void main(String[] args) { EcompUser ecompUser = new
+ * EcompUser(); ecompUser.setFirstName("Test");
+ * ecompUser.setLastName("Testovich");
+ * ecompUser.setEmail("ttes@intl.sdc.com"); ecompUser.setLoginId("tt0004");
+ * ecompUser.setActive(true);
+ *
+ * EcompRole roleToUpdate = new EcompRole(); roleToUpdate.setId(new
+ * Long(6)); roleToUpdate.setName("PRODUCT_STRATEGIST"); List<EcompRole>
+ * listOfRoles = new LinkedList<>(); listOfRoles.add(roleToUpdate);
+ *
+ * try {
+ * System.out.println("\n-----------------------------\n Testing pushUser");
+ * System.out.println(pushUser(ecompUser));
+ * System.out.println("\n-----------------------------\n Testing editUser");
+ * // System.out.println(editUser("tt0001", ecompUser));
+ * System.out.println("\n-----------------------------\n Testing getUser");
+ * // System.out.println(getUser(ecompUser.getLoginId())); System.out.
+ * println("\n-----------------------------\n Testing getAllUsers"); //
+ * System.out.println(getAllUsers()); System.out.
+ * println("\n-----------------------------\n Testing getAllAvailableRoles"
+ * ); // System.out.println(getAllAvailableRoles().toString()); System.out.
+ * println("\n-----------------------------\n Testing pushUserRoles"); //
+ * System.out.println(pushUserRoles("tt0001", listOfRoles)); System.out.
+ * println("\n-----------------------------\n Testing getUserRoles"); //
+ * System.out.println(getUserRoles("tt0001")); } catch (IOException e) { //
+ * TODO Auto-generated catch block e.printStackTrace(); } }
+ */
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/GroupRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/GroupRestUtils.java
new file mode 100644
index 0000000000..d79c8e002c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/GroupRestUtils.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+import com.google.gson.Gson;
+
+public class GroupRestUtils extends BaseRestUtils {
+ public static String acceptHeaderDate = "application/json";
+ static Config config = Config.instance();
+ public static Gson gson = new Gson();
+
+ public static RestResponse getGroupById(Component component, String groupId, User user) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), user.getUserId());
+
+ String url = String.format(Urls.GET_GROUP_BY_ID, config.getCatalogBeHost(), config.getCatalogBePort(),
+ ComponentTypeEnum.findParamByType(component.getComponentType()), component.getUniqueId(), groupId);
+
+ RestResponse sendGetServerRequest = sendGet(url, user.getUserId(), headersMap);
+
+ return sendGetServerRequest;
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ImportRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ImportRestUtils.java
new file mode 100644
index 0000000000..3ce48962fd
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ImportRestUtils.java
@@ -0,0 +1,339 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.codehaus.jettison.json.JSONException;
+import org.openecomp.sdc.be.dao.rest.HttpRestClient;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ImportTestTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.openecomp.sdc.common.rest.api.RestResponseAsByteArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ImportRestUtils extends BaseRestUtils {
+
+ private static Logger log = LoggerFactory.getLogger(ImportRestUtils.class.getName());
+ private static Properties downloadCsarHeaders = new Properties();
+
+ static {
+ downloadCsarHeaders.put("Accept", "application/octet-stream");
+ }
+
+ @SuppressWarnings("unused")
+ private static Integer importNormativeResource(NormativeTypesEnum resource, UserRoleEnum userRole) throws IOException {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("resourceZip", new FileBody(getTestZipFile(resource.getFolderName())));
+ mpBuilder.addPart("resourceMetadata", new StringBody(getTestJsonStringOfFile(resource.getFolderName(), resource.getFolderName() + ".json"), ContentType.APPLICATION_JSON));
+
+ String url = String.format(Urls.IMPORT_RESOURCE_NORMATIVE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.addHeader("USER_ID", userRole.getUserId());
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ return response.getStatusLine().getStatusCode();
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ public static RestResponse importResourceByName(ResourceReqDetails resourceDetails, User importer) throws Exception {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("resourceZip", new FileBody(getTestZipFile(resourceDetails.getName())));
+ mpBuilder.addPart("resourceMetadata", new StringBody(getTestJsonStringOfFile(resourceDetails.getName(), resourceDetails.getName() + ".json"), ContentType.APPLICATION_JSON));
+
+ String url = String.format(Urls.IMPORT_RESOURCE_NORMATIVE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ RestResponse restResponse = new RestResponse();
+ httpPost.addHeader("USER_ID", importer.getUserId());
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ HttpEntity entity = response.getEntity();
+ String responseBody = null;
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(instream, writer);
+ responseBody = writer.toString();
+ try {
+
+ } finally {
+ instream.close();
+ }
+ }
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+ restResponse.setResponse(responseBody);
+
+ if (restResponse.getErrorCode() == STATUS_CODE_CREATED) {
+ resourceDetails.setUUID(ResponseParser.getUuidFromResponse(restResponse));
+ resourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(restResponse));
+ resourceDetails.setVersion(ResponseParser.getVersionFromResponse(restResponse));
+ resourceDetails.setCreatorUserId(importer.getUserId());
+ resourceDetails.setCreatorFullName(importer.getFullName());
+ }
+
+ return restResponse;
+
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+
+ }
+
+ public static RestResponse importNewResourceByName(String resourceName, UserRoleEnum userRole) throws IOException {
+ Config config = Utils.getConfig();
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("resourceZip", new FileBody(getTestZipFile(resourceName)));
+ mpBuilder.addPart("resourceMetadata", new StringBody(getTestJsonStringOfFile(resourceName, resourceName + ".json"), ContentType.APPLICATION_JSON));
+ HttpEntity requestEntity = mpBuilder.build();
+ String url = String.format(Urls.IMPORT_USER_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put("USER_ID", userRole.getUserId());
+
+ return HttpRequest.sendHttpPostWithEntity(requestEntity, url, headers);
+ }
+
+ public static RestResponse importNormativeResourceByName(String resourceName, UserRoleEnum userRole) throws IOException {
+ Config config = Utils.getConfig();
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("resourceZip", new FileBody(getTestZipFile(resourceName)));
+ mpBuilder.addPart("resourceMetadata", new StringBody(getTestJsonStringOfFile(resourceName, resourceName + ".json"), ContentType.APPLICATION_JSON));
+ HttpEntity requestEntity = mpBuilder.build();
+ String url = String.format(Urls.IMPORT_RESOURCE_NORMATIVE, config.getCatalogBeHost(), config.getCatalogBePort());
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put("USER_ID", userRole.getUserId());
+
+ return HttpRequest.sendHttpPostWithEntity(requestEntity, url, headers);
+ }
+
+ public static RestResponse importTestResource(ImportTestTypesEnum resource, UserRoleEnum userRole) throws IOException {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("resourceZip", new FileBody(getTestZipFile(resource.getFolderName())));
+ mpBuilder.addPart("resourceMetadata", new StringBody(getTestJsonStringOfFile(resource.getFolderName(), resource.getFolderName() + ".json"), ContentType.APPLICATION_JSON));
+
+ String url = String.format(Urls.IMPORT_RESOURCE_NORMATIVE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ RestResponse restResponse = new RestResponse();
+ httpPost.addHeader("USER_ID", UserRoleEnum.ADMIN.getUserId());
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ HttpEntity entity = response.getEntity();
+ String responseBody = null;
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(instream, writer);
+ responseBody = writer.toString();
+ try {
+
+ } finally {
+ instream.close();
+ }
+ }
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+ // restResponse.setResponse(response.getEntity().toString());
+ restResponse.setResponse(responseBody);
+ return restResponse;
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ public static Boolean removeNormativeTypeResource(NormativeTypesEnum current) throws FileNotFoundException, IOException, ClientProtocolException {
+ User user = new User(UserRoleEnum.ADMIN.getFirstName(), UserRoleEnum.ADMIN.getLastName(), UserRoleEnum.ADMIN.getUserId(), null, null, null);
+ RestResponse deleteResponse = ResourceRestUtils.deleteResourceByNameAndVersion(user, current.getNormativeName(), "1.0");
+ if (deleteResponse.getErrorCode() == 200) {
+ return true;
+ }
+ return false;
+ }
+
+ public static void validateImportTestTypesResp(ImportTestTypesEnum currResource, RestResponse restResponse) throws IOException, JSONException {
+
+ // assertTrue( status != ResourceUtils.STATUS_CODE_IMPORT_SUCCESS );
+
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(currResource.getActionStatus().name());
+
+ assertNotNull("check response object is not null after create service", restResponse);
+ assertNotNull("check error code exists in response after create service", restResponse.getErrorCode());
+ assertEquals("Check response code after create service", errorInfo.getCode(), restResponse.getErrorCode());
+
+ // validate create service response vs actual
+ List<String> variables = (currResource.getErrorParams() != null ? currResource.getErrorParams() : new ArrayList<String>());
+ if (restResponse.getErrorCode() != 200) {
+ ErrorValidationUtils.checkBodyResponseOnError(currResource.getActionStatus().name(), variables, restResponse.getResponse());
+ }
+ }
+
+ private static String getTestJsonStringOfFile(String folderName, String fileName) throws IOException {
+ // String sourceDir = "src/test/resources/CI/importResourceTests";
+ Config config = Utils.getConfig();
+ String sourceDir = config.getImportResourceTestsConfigDir();
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + folderName, fileName);
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String content = new String(fileContent);
+ return content;
+ }
+
+ private static File getTestZipFile(String elementName) throws IOException {
+ Config config = Utils.getConfig();
+ String sourceDir = config.getImportResourceTestsConfigDir();
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + elementName, "normative-types-new-" + elementName + ".zip");
+ return filePath.toFile();
+ }
+
+ private static void closeHttpClient(CloseableHttpClient client) {
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ log.debug("failed to close client or response: ", e);
+ }
+ }
+
+ private static void closeResponse(CloseableHttpResponse response) {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ } catch (IOException e) {
+ log.debug("failed to close client or response: {}", e);
+ }
+ }
+
+ public static RestResponseAsByteArray getCsar(String csarUid, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_CSAR_USING_SIMULATOR, config.getCatalogBeHost(), config.getCatalogBePort(), csarUid);
+
+ String userId = sdncModifierDetails.getUserId();
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ // Gson gson = new Gson();
+ // String userBodyJson = gson.toJson(resourceDetails);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+
+ HttpRestClient httpRestClient = new HttpRestClient();
+
+ for (Map.Entry<String, String> mapEntry : headersMap.entrySet()) {
+
+ downloadCsarHeaders.put(mapEntry.getKey(), mapEntry.getValue());
+ }
+ RestResponseAsByteArray doGetAsByteArray = httpRestClient.doGetAsByteArray(url, downloadCsarHeaders);
+ // RestResponse getCsar = http.httpSendGet(url, headersMap);
+
+ return doGetAsByteArray;
+
+ }
+
+ private static File getGroupTypeZipFile(String elementName) throws IOException {
+ Config config = Utils.getConfig();
+ String sourceDir = config.getImportResourceTestsConfigDir();
+ sourceDir += File.separator + ".." + File.separator + "importTypesTest" + File.separator;
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir + File.separator + elementName, elementName + ".zip");
+ return filePath.toFile();
+ }
+
+ public static RestResponse importNewGroupTypeByName(String groupTypeName, UserRoleEnum userRole) throws IOException {
+ Config config = Utils.getConfig();
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+
+ mpBuilder.addPart("groupTypesZip", new FileBody(getGroupTypeZipFile(groupTypeName)));
+ HttpEntity requestEntity = mpBuilder.build();
+ String url = String.format(Urls.IMPORT_GROUP_TYPE, config.getCatalogBeHost(), config.getCatalogBePort());
+ Map<String, String> headers = new HashMap<String, String>();
+ headers.put("USER_ID", userRole.getUserId());
+
+ return HttpRequest.sendHttpPostWithEntity(requestEntity, url, headers);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/InputsRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/InputsRestUtils.java
new file mode 100644
index 0000000000..de7be077d1
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/InputsRestUtils.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstInputsMap;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+/**
+ * RestUtils for inputs
+ *
+ * @author il0695
+ *
+ */
+public class InputsRestUtils extends BaseRestUtils {
+
+ @SuppressWarnings("unused")
+ private static Logger logger = LoggerFactory.getLogger(InputsRestUtils.class.getName());
+
+ /**
+ * Add inputs to service
+ *
+ * @param component
+ * @param inputs
+ * @param userRole
+ * @return {@link org.openecomp.sdc.ci.tests.datatypes.http.RestResponse}
+ * @throws Exception
+ */
+ public static RestResponse addInput(Component component, ComponentInstInputsMap inputs, UserRoleEnum userRole) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.ADD_INPUTS, config.getCatalogBeHost(), config.getCatalogBePort(), ComponentTypeEnum.findParamByType(component.getComponentType()), component.getUniqueId());
+ String json = new Gson().toJson(inputs);
+ return sendPost(url, json, userRole.getUserId(), acceptHeaderData);
+ }
+
+ /**
+ * Get all Component inputs
+ *
+ * @param component
+ * @return {@link org.openecomp.sdc.ci.tests.datatypes.http.RestResponse}
+ * @throws Exception
+ */
+ public static RestResponse getComponentInputs(Component component) throws Exception {
+ Config config = Utils.getConfig();
+ //services/{componentId}/inputs
+ String url = String.format(Urls.GET_COMPONENT_INPUTS, config.getCatalogBeHost(), config.getCatalogBePort(), component.getUniqueId());
+ return sendGet(url, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ }
+
+ /**
+ * Get all inputs of component instance
+ *
+ * @param parentComponent
+ * @param componentInstance
+ * @return {@link org.openecomp.sdc.ci.tests.datatypes.http.RestResponse}
+ * @throws Exception
+ */
+ public static RestResponse getComponentInstanceInputs(Component parentComponent, ComponentInstance componentInstance) throws Exception {
+ Config config = Utils.getConfig();
+ //{componentType}/{componentId}/componentInstances/{instanceId}/{originComonentUid}/inputs
+ String url = String.format(Urls.GET_COMPONENT_INSTANCE_INPUTS, config.getCatalogBeHost(), config.getCatalogBePort(), ComponentTypeEnum.findParamByType(parentComponent.getComponentType()), parentComponent.getUniqueId(), componentInstance.getUniqueId(), componentInstance.getComponentUid());
+ return sendGet(url, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ }
+
+ /**
+ * Delete input from component
+ *
+ * @param parentComponent
+ * @param inputId
+ * @return {@link org.openecomp.sdc.ci.tests.datatypes.http.RestResponse}
+ * @throws Exception
+ */
+ public static RestResponse deleteInputFromComponent(Component parentComponent, String inputId) throws Exception {
+ return deleteInputFromComponent(ComponentTypeEnum.findParamByType(parentComponent.getComponentType()), parentComponent.getUniqueId(), inputId);
+ }
+
+ /**
+ * Delete input from component
+ *
+ * @param componentType
+ * @param componentId
+ * @param inputUniqueId
+ * @return {@link org.openecomp.sdc.ci.tests.datatypes.http.RestResponse}
+ * @throws Exception
+ */
+ public static RestResponse deleteInputFromComponent(String componentType, String componentId, String inputUniqueId) throws Exception {
+ Config config = Utils.getConfig();
+ //{componentType}/{componentId}/delete/{inputId}/input
+ String url = String.format(Urls.DELETE_INPUT_BY_ID, config.getCatalogBeHost(), config.getCatalogBePort(), componentType, componentId, inputUniqueId);
+ return sendDelete(url, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER).getUserId());
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/LifecycleRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/LifecycleRestUtils.java
new file mode 100644
index 0000000000..34651c1b7a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/LifecycleRestUtils.java
@@ -0,0 +1,360 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LifecycleRestUtils extends BaseRestUtils {
+ private static Logger logger = LoggerFactory.getLogger(LifecycleRestUtils.class.getName());
+ public static final String COMMENT = "comment";
+
+ public static RestResponse changeResourceState(ResourceReqDetails resourceDetails, User sdncModifierDetails,
+ String version, LifeCycleStatesEnum LifeCycleStatesEnum) throws IOException {
+ return changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum,
+ createLifecycleCommentJson(COMMENT));
+ }
+
+ public static RestResponse changeResourceState(ResourceReqDetails resourceDetails, User sdncModifierDetails,
+ String version, LifeCycleStatesEnum LifeCycleStatesEnum, String LifecycleChangeInfo) throws IOException {
+
+ return changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum, LifecycleChangeInfo);
+
+ }
+
+ public static RestResponse changeResourceState(ResourceReqDetails resourceDetails, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum) throws IOException {
+
+ return changeResourceState(resourceDetails, sdncModifierDetails, LifeCycleStatesEnum,
+ createLifecycleCommentJson(COMMENT));
+
+ }
+
+ public static RestResponse changeResourceState(ResourceReqDetails resourceDetails, String modifierUserId,
+ LifeCycleStatesEnum LifeCycleStatesEnum) throws IOException {
+ User user = new User();
+ user.setUserId(modifierUserId);
+ return changeResourceState(resourceDetails, user, LifeCycleStatesEnum, createLifecycleCommentJson(COMMENT));
+ }
+
+ private static RestResponse changeResourceState(ResourceReqDetails resourceDetails, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum, String LifecycleChangeInfo) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CHANGE_RESOURCE_LIFECYCLE_STATE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), resourceDetails.getUniqueId(), LifeCycleStatesEnum);
+ // System.out.println("url: " + url);
+
+ RestResponse LifeCycleStatesEnumResourceResponse = sendPost(url, LifecycleChangeInfo,
+ sdncModifierDetails.getUserId(), acceptHeaderData);
+ if (LifeCycleStatesEnumResourceResponse.getErrorCode() == STATUS_CODE_SUCCESS) {
+ String stateFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumResourceResponse.getResponse(), "lifecycleState");
+ resourceDetails.setVersion(ResponseParser.getVersionFromResponse(LifeCycleStatesEnumResourceResponse));
+ resourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(LifeCycleStatesEnumResourceResponse));
+ if (stateFromJsonResponse != null) {
+ resourceDetails.setLifecycleState(LifecycleStateEnum.valueOf(stateFromJsonResponse));
+ }
+ }
+ return LifeCycleStatesEnumResourceResponse;
+ }
+
+ public static RestResponse changeServiceState(ServiceReqDetails serviceDetails, User sdncModifierDetails,
+ String version, LifeCycleStatesEnum LifeCycleStatesEnum) throws Exception {
+ {
+ return changeServiceState(serviceDetails, sdncModifierDetails, version, LifeCycleStatesEnum,
+ createLifecycleCommentJson(COMMENT));
+ }
+ }
+
+ public static RestResponse changeServiceState(ServiceReqDetails serviceDetails, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum) throws Exception {
+ {
+ return changeServiceState(serviceDetails, sdncModifierDetails, null, LifeCycleStatesEnum,
+ createLifecycleCommentJson(COMMENT));
+ }
+ }
+
+ public static RestResponse changeServiceState(ServiceReqDetails serviceDetails, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum, String lifecycleChangeInfo) throws Exception {
+ {
+ return changeServiceState(serviceDetails, sdncModifierDetails, null, LifeCycleStatesEnum,
+ lifecycleChangeInfo);
+ }
+ }
+
+ public static RestResponse changeServiceState(ServiceReqDetails serviceDetails, User sdncModifierDetails,
+ String version, LifeCycleStatesEnum LifeCycleStatesEnum, String lifecycleChangeInfo) throws Exception {
+
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = prepareHeadersMap(sdncModifierDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CHANGE_SERVICE_LIFECYCLE_STATE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), serviceDetails.getUniqueId(), LifeCycleStatesEnum);
+ // System.out.println("url: " + url);
+ RestResponse LifeCycleStatesEnumServiceResponse = http.httpSendPost(url, lifecycleChangeInfo, headersMap);
+ if (LifeCycleStatesEnumServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS) {
+ String serviceUniqueId = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "uniqueId");
+ serviceDetails.setUniqueId(serviceUniqueId);
+ String serviceVersion = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "version");
+ serviceDetails.setVersion(serviceVersion);
+ String stateFromJsonResponse = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "lifecycleState");
+ serviceDetails.setLifecycleState(LifecycleStateEnum.valueOf(stateFromJsonResponse));
+ }
+ return LifeCycleStatesEnumServiceResponse;
+ }
+
+ public static RestResponse changeProductState(Product product, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum, String lifecycleChangeInfo) throws Exception {
+ {
+ return _changeProductState(product, sdncModifierDetails, LifeCycleStatesEnum, lifecycleChangeInfo);
+ }
+ }
+
+ public static RestResponse changeProductState(Product product, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum) throws Exception {
+ {
+ return _changeProductState(product, sdncModifierDetails, LifeCycleStatesEnum, COMMENT);
+ }
+ }
+
+ public static RestResponse changeProductState(ProductReqDetails productDetails, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CHANGE_PRODUCT_LIFECYCLE_STATE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), productDetails.getUniqueId(), LifeCycleStatesEnum);
+ RestResponse LifeCycleStatesEnumServiceResponse = sendPost(url, createLifecycleCommentJson(COMMENT),
+ sdncModifierDetails.getUserId(), acceptHeaderData);
+ if (LifeCycleStatesEnumServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS) {
+ String productUniqueId = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "uniqueId");
+ productDetails.setUniqueId(productUniqueId);
+ String productVersion = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "version");
+ productDetails.setVersion(productVersion);
+ String newLifecycleState = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "lifecycleState");
+ productDetails.setLifecycleState(LifecycleStateEnum.valueOf(newLifecycleState));
+ }
+ return LifeCycleStatesEnumServiceResponse;
+
+ }
+
+ public static RestResponse changeComponentState(Component component, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CHANGE_COMPONENT_LIFECYCLE_STATE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(component.getComponentType()),
+ component.getUniqueId(), LifeCycleStatesEnum);
+ RestResponse LifeCycleStatesEnumServiceResponse = sendPost(url, createLifecycleCommentJson(COMMENT),
+ sdncModifierDetails.getUserId(), acceptHeaderData);
+ if (LifeCycleStatesEnumServiceResponse.getErrorCode() == STATUS_CODE_SUCCESS) {
+ String productUniqueId = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "uniqueId");
+ component.setUniqueId(productUniqueId);
+ String productVersion = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "version");
+ component.setVersion(productVersion);
+ String newLifecycleState = ResponseParser
+ .getValueFromJsonResponse(LifeCycleStatesEnumServiceResponse.getResponse(), "lifecycleState");
+ component.setLifecycleState(LifecycleStateEnum.valueOf(newLifecycleState));
+ }
+ return LifeCycleStatesEnumServiceResponse;
+
+ }
+
+ public static RestResponse certifyResource(ResourceReqDetails resourceDetails) throws Exception {
+ RestResponse restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKIN);
+ // if (restResponseResource.getErrorCode() == 200){
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ // }else
+ // return restResponseResource;
+ User testerDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ if (restResponseResource.getErrorCode() == 200) {
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, testerDetails,
+ LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponseResource;
+ if (restResponseResource.getErrorCode() == 200) {
+ restResponseResource = LifecycleRestUtils.changeResourceState(resourceDetails, testerDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ if (restResponseResource.getErrorCode() == 200) {
+ String newVersion = ResponseParser.getVersionFromResponse(restResponseResource);
+ resourceDetails.setVersion(newVersion);
+ resourceDetails.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ resourceDetails.setLastUpdaterUserId(testerDetails.getUserId());
+ resourceDetails.setLastUpdaterFullName(testerDetails.getFullName());
+ String uniqueIdFromRresponse = ResponseParser.getUniqueIdFromResponse(restResponseResource);
+ resourceDetails.setUniqueId(uniqueIdFromRresponse);
+ }
+ }
+ return restResponseResource;
+ }
+
+ public static RestResponse certifyService(ServiceReqDetails serviceDetails) throws Exception {
+ RestResponse restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CHECKIN);
+ // if (restResponseService.getErrorCode() == 200){
+ restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), LifeCycleStatesEnum.CERTIFICATIONREQUEST);
+ // }else
+ // return restResponseService;
+ if (restResponseService.getErrorCode() == 200) {
+ restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.TESTER), LifeCycleStatesEnum.STARTCERTIFICATION);
+ } else
+ return restResponseService;
+ if (restResponseService.getErrorCode() == 200) {
+ User testerDetails = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+ restResponseService = LifecycleRestUtils.changeServiceState(serviceDetails, testerDetails,
+ LifeCycleStatesEnum.CERTIFY);
+ if (restResponseService.getErrorCode() == 200) {
+ String newVersion = ResponseParser.getVersionFromResponse(restResponseService);
+ serviceDetails.setVersion(newVersion);
+ serviceDetails.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ serviceDetails.setLastUpdaterUserId(testerDetails.getUserId());
+ serviceDetails.setLastUpdaterFullName(testerDetails.getFullName());
+ String uniqueIdFromRresponse = ResponseParser.getUniqueIdFromResponse(restResponseService);
+ serviceDetails.setUniqueId(uniqueIdFromRresponse);
+ }
+ }
+ return restResponseService;
+ }
+
+ private static RestResponse _changeProductState(Product product, User sdncModifierDetails,
+ LifeCycleStatesEnum LifeCycleStatesEnum, String lifecycleChangeInfo) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CHANGE_PRODUCT_LIFECYCLE_STATE, config.getCatalogBeHost(),
+ config.getCatalogBePort(), product.getUniqueId(), LifeCycleStatesEnum);
+ RestResponse LifeCycleStatesEnumServiceResponse = sendPost(url, createLifecycleCommentJson(lifecycleChangeInfo),
+ sdncModifierDetails.getUserId(), acceptHeaderData);
+
+ return LifeCycleStatesEnumServiceResponse;
+ }
+
+ public static String createLifecycleCommentJson(String commentContent) {
+ String res = null;
+ if (commentContent != null) {
+ res = "{\"userRemarks\": \"" + commentContent + "\"}";
+ }
+ return res;
+ }
+
+ public static void checkLCS_Response(RestResponse response) {
+ checkStatusCode(response, "change lifecycle request failed", false, STATUS_CODE_SUCCESS);
+ }
+
+ private static Map<String, String> prepareHeadersMap(User sdncModifierDetails) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ return headersMap;
+ }
+
+ public static RestResponse changeDistributionStatus(ServiceReqDetails serviceDetails, String version, User user,
+ String userRemarks, DistributionStatusEnum reqDistributionStatus) throws Exception {
+ String uniqueId = serviceDetails.getUniqueId();
+ if (reqDistributionStatus == DistributionStatusEnum.DISTRIBUTION_APPROVED) {
+ return sendApproveDistribution(user, uniqueId, userRemarks);
+ } else if (reqDistributionStatus == DistributionStatusEnum.DISTRIBUTION_REJECTED) {
+ return rejectDistribution(user, userRemarks, uniqueId);
+ } else if (reqDistributionStatus == DistributionStatusEnum.DISTRIBUTED) {
+ Config config = Utils.getConfig();
+ // String url =
+ // String.format("http://%s:%s/sdc2/rest/v1/catalog/services/%s/tempUrlToBeDeleted",
+ // config.getCatalogBeHost(), config.getCatalogBePort(), uniqueId);
+ String url = String.format(Urls.ACTIVATE_DISTRIBUTION, config.getCatalogBeHost(), config.getCatalogBePort(),
+ uniqueId, "PROD");
+ return sendDistrState(user, userRemarks, url);
+ } else
+ return null;
+
+ }
+
+ public static RestResponse sendApproveDistribution(User sdncModifierDetails, String uniqueId, String userRemarks)
+ throws FileNotFoundException, IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.APPROVE_DISTRIBUTION, config.getCatalogBeHost(), config.getCatalogBePort(),
+ uniqueId);
+ return sendDistrState(sdncModifierDetails, userRemarks, url);
+ }
+
+ public static RestResponse rejectDistribution(ServiceReqDetails serviceDetails, String version, User user,
+ String userRemarks) throws Exception {
+ return rejectDistribution(user, userRemarks, serviceDetails.getUniqueId());
+ }
+
+ public static RestResponse rejectDistribution(User user, String userRemarks, String uniqueId)
+ throws FileNotFoundException, IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.REJECT_DISTRIBUTION, config.getCatalogBeHost(), config.getCatalogBePort(),
+ uniqueId);
+ return sendDistrState(user, userRemarks, url);
+ }
+
+ private static RestResponse sendDistrState(User user, String userRemarks, String url) throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(user);
+ Map<String, String> userRemarksMap = new HashMap<String, String>();
+ userRemarksMap.put("userRemarks", userRemarks);
+
+ String serviceBodyJson = new JSONObject().toJSONString(userRemarksMap);
+
+ HttpRequest httpRequest = new HttpRequest();
+ logger.debug(url);
+ logger.debug("Send POST request to create service: {}", url);
+ logger.debug("Service body: {}", serviceBodyJson);
+ logger.debug("Service headers: {}", headersMap);
+ RestResponse rejectDistributionResponse = httpRequest.httpSendPost(url, serviceBodyJson, headersMap);
+
+ return rejectDistributionResponse;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ProductRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ProductRestUtils.java
new file mode 100644
index 0000000000..8e617ed75c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ProductRestUtils.java
@@ -0,0 +1,185 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class ProductRestUtils extends BaseRestUtils {
+ private static Gson gson = new Gson();
+ private static Logger logger = LoggerFactory.getLogger(ProductRestUtils.class.getName());
+
+ public static RestResponse createProduct(ProductReqDetails product, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_PRODUCT, config.getCatalogBeHost(), config.getCatalogBePort());
+ String serviceBodyJson = gson.toJson(product);
+
+ logger.debug("Send POST request to create service: {}", url);
+ logger.debug("Service body: {}", serviceBodyJson);
+
+ RestResponse res = sendPost(url, serviceBodyJson, user.getUserId(), acceptHeaderData);
+ if (res.getErrorCode() == STATUS_CODE_CREATED) {
+ product.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ product.setVersion(ResponseParser.getVersionFromResponse(res));
+ product.setUUID(ResponseParser.getUuidFromResponse(res));
+ // Creator details never change after component is created - Ella,
+ // 12/1/2016
+ product.setCreatorUserId(user.getUserId());
+ product.setCreatorFullName(user.getFullName());
+ product.setLastUpdaterFullName(user.getFullName());
+ product.setLastUpdaterUserId(user.getUserId());
+ product.setLastUpdaterFullName(user.getFullName());
+ product.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ product.setVersion("0.1");
+ String lastUpdate = ResponseParser.getValueFromJsonResponse(res.getResponse(), "lastUpdateDate");
+ product.setLastUpdateDate(Long.parseLong(lastUpdate, 10));
+ product.setCreationDate(Long.parseLong(lastUpdate, 10));
+ }
+ return res;
+ }
+
+ public static RestResponse updateProduct(ProductReqDetails product, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_PRODUCT, config.getCatalogBeHost(), config.getCatalogBePort(),
+ product.getUniqueId());
+ String serviceBodyJson = gson.toJson(product);
+
+ logger.debug("Send POST request to create service: {}", url);
+ logger.debug("Service body: {}", serviceBodyJson);
+
+ RestResponse res = sendPut(url, serviceBodyJson, user.getUserId(), acceptHeaderData);
+ if (res.getErrorCode() == STATUS_CODE_CREATED) {
+ product.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ product.setVersion(ResponseParser.getVersionFromResponse(res));
+ product.setUUID(ResponseParser.getUuidFromResponse(res));
+ // Creator details never change after component is created - Ella,
+ // 12/1/2016
+ product.setCreatorUserId(user.getUserId());
+ product.setCreatorFullName(user.getFullName());
+ product.setLastUpdaterFullName(user.getFullName());
+ product.setLastUpdaterUserId(user.getUserId());
+ product.setLastUpdaterFullName(user.getFullName());
+ product.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ String valueFromJsonResponse = ResponseParser.getValueFromJsonResponse(res.getResponse(), "version");
+ product.setVersion(valueFromJsonResponse);
+ String lastUpdate = ResponseParser.getValueFromJsonResponse(res.getResponse(), "lastUpdateDate");
+ product.setLastUpdateDate(Long.parseLong(lastUpdate, 10));
+ product.setCreationDate(Long.parseLong(lastUpdate, 10));
+ }
+ return res;
+ }
+
+ public static RestResponse createProduct_Invalid_Json(String userId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_PRODUCT, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ RestResponse res = sendPost(url, "kukumuku", userId, acceptHeaderData);
+ return res;
+ }
+
+ public static RestResponse deleteProduct(String id, String userId) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_PRODUCT, config.getCatalogBeHost(), config.getCatalogBePort(), id);
+ return sendDelete(url, userId);
+ }
+
+ public static RestResponse getProduct(String productId, String userId) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_PRODUCT, config.getCatalogBeHost(), config.getCatalogBePort(), productId);
+ logger.debug("Send GET request to get product: {}", url);
+
+ return sendGet(url, userId);
+ }
+
+ public static RestResponse getFollowed(String userId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_FOLLWED_LIST, config.getCatalogBeHost(), config.getCatalogBePort());
+ logger.debug("Send GET request to get user followed page: {}", url);
+ return sendGet(url, userId);
+
+ }
+
+ public static RestResponse changeProductLifeCycle(Product product, User userModifier, LifeCycleStatesEnum lifeCycle)
+ throws Exception {
+ String checkinComment = "my comment";
+ RestResponse changeLifeCycleResponse = LifecycleRestUtils.changeProductState(product, userModifier, lifeCycle,
+ checkinComment);
+ if (changeLifeCycleResponse.getErrorCode() == STATUS_CODE_SUCCESS) {
+ product.setLastUpdaterUserId(userModifier.getUserId());
+ product.setLastUpdaterFullName(userModifier.getFullName());
+ String latestVersion = ResponseParser.getValueFromJsonResponse(changeLifeCycleResponse.getResponse(),
+ "version");
+ product.setVersion(latestVersion);
+ String lifecycleState = ResponseParser.getValueFromJsonResponse(changeLifeCycleResponse.getResponse(),
+ "lifecycleState");
+ product.setLifecycleState((LifecycleStateEnum.valueOf(lifecycleState)));
+ String uniqueId = ResponseParser.getValueFromJsonResponse(changeLifeCycleResponse.getResponse(),
+ "uniqueId");
+ product.setUniqueId(uniqueId);
+ String lastUpdate = ResponseParser.getValueFromJsonResponse(changeLifeCycleResponse.getResponse(),
+ "lastUpdateDate");
+ product.setLastUpdateDate((Long.parseLong(lastUpdate, 10)));
+ String uuid = ResponseParser.getValueFromJsonResponse(changeLifeCycleResponse.getResponse(), "uuid");
+ product.setUUID(uuid);
+ }
+ return changeLifeCycleResponse;
+ }
+
+ public static RestResponse changeServiceInstanceVersion(String componentUniqueId,
+ String serviceInstanceToReplaceUniqueId, String serviceUniqueId, User sdncModifierDetails,
+ ComponentTypeEnum componentType) throws IOException {
+ Config config = Utils.getConfig();
+ String resourceUid = ("{\"componentUid\":\"" + serviceUniqueId + "\"}");
+ String url = String.format(Urls.CHANGE__RESOURCE_INSTANCE_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort(), ComponentTypeEnum.findParamByType(componentType), componentUniqueId,
+ serviceInstanceToReplaceUniqueId);
+ RestResponse changeResourceInstanceVersion = sendPost(url, resourceUid, sdncModifierDetails.getUserId(),
+ acceptHeaderData);
+ return changeResourceInstanceVersion;
+
+ }
+
+ public static RestResponse getProductByNameAndVersion(String productName, String productVersion, String userId)
+ throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_PRODUCT_BY_NAME_AND_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort(), productName, productVersion);
+ logger.debug("Send GET request to get product by name and version: {}", url);
+ return sendGet(url, userId);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/PropertyRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/PropertyRestUtils.java
new file mode 100644
index 0000000000..56ff5ec12b
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/PropertyRestUtils.java
@@ -0,0 +1,310 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertyRestUtils extends BaseRestUtils {
+ private static Logger logger = LoggerFactory.getLogger(PropertyRestUtils.class.getName());
+
+ public static RestResponse createProperty(String resourceId, String body, User user) throws Exception {
+ Config config = Config.instance();
+ String url = String.format(Urls.CREATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ resourceId);
+
+ return sendPost(url, body, user.getUserId(), acceptHeaderData);
+ }
+
+ public static RestResponse updateProperty(String resourceId, String propertyId, String body, User user)
+ throws Exception {
+ Config config = Config.instance();
+
+ String url = String.format(Urls.UPDATE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ resourceId, propertyId);
+ return sendPut(url, body, user.getUserId(), acceptHeaderData);
+ }
+
+ public static RestResponse getProperty(String resourceId, String propertyId, User user) throws Exception {
+ Config config = Config.instance();
+ String url = String.format(Urls.GET_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId,
+ propertyId);
+ return sendGet(url, user.getUserId());
+ }
+
+ public static RestResponse deleteProperty(String resourceId, String propertyId, User user) throws Exception {
+ Config config = Config.instance();
+ String url = String.format(Urls.DELETE_PROPERTY, config.getCatalogBeHost(), config.getCatalogBePort(),
+ resourceId, propertyId);
+
+ return sendDelete(url, user.getUserId());
+ }
+
+ public static ComponentInstanceProperty getPropFromListByPropNameAndType(List<ComponentInstanceProperty> propList,
+ String propNameToUpdate, String propTypeToUpdate) {
+ for (ComponentInstanceProperty componentInstanceProperty : propList) {
+ if (componentInstanceProperty.getName().equals(propNameToUpdate)
+ && componentInstanceProperty.getType().equals(propTypeToUpdate)) {
+ return componentInstanceProperty;
+ }
+ }
+ return null;
+ }
+
+ public static ComponentInstanceProperty getPropFromListByPropNameTypeAndPath(
+ List<ComponentInstanceProperty> propList, String propNameToUpdate, String propTypeToUpdate,
+ List<String> path) {
+ for (ComponentInstanceProperty componentInstanceProperty : propList) {
+ if (componentInstanceProperty.getPath() == null) {
+ return getPropFromListByPropNameAndType(propList, propNameToUpdate, propTypeToUpdate);
+ }
+ if (componentInstanceProperty.getName().equals(propNameToUpdate)
+ && componentInstanceProperty.getType().equals(propTypeToUpdate)
+ && path.containsAll(componentInstanceProperty.getPath())) {
+ return componentInstanceProperty;
+ }
+ }
+ return null;
+ }
+
+ public static ComponentInstanceProperty getPropFromListByPropIdAndPath(List<ComponentInstanceProperty> propList,
+ String propId, List<String> path) {
+
+ for (ComponentInstanceProperty componentInstanceProperty : propList) {
+ if (path != null) {
+ if (componentInstanceProperty.getUniqueId().equals(propId)
+ && componentInstanceProperty.getPath().equals(path)) {
+ return componentInstanceProperty;
+ }
+ } else {
+ if (componentInstanceProperty.getUniqueId().equals(propId)) {
+ return componentInstanceProperty;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void comparePropertyLists(List<ComponentInstanceProperty> expectedList,
+ List<ComponentInstanceProperty> actualList, Boolean isUpdate) {
+
+ assertTrue(
+ "list size are not equals, expected size is: " + expectedList.size() + " ,actual: " + actualList.size(),
+ expectedList.size() == actualList.size());
+ Boolean flag = false;
+ for (ComponentInstanceProperty expectedcompInstProp : expectedList) {
+ for (ComponentInstanceProperty actualcompInstProp : actualList) {
+ flag = comparePropertyObjects(expectedcompInstProp, actualcompInstProp, isUpdate);
+ if (flag) {
+ break;
+ }
+ }
+ }
+ // System.out.println("expected: " + expectedList + ", actual: " +
+ // actualList);
+ logger.debug("expected: {}, actual: {}", expectedList, actualList);
+ assertTrue("actual lists does not contain all uniqeIds", flag);
+ }
+
+ public static Boolean comparePropertyObjects(ComponentInstanceProperty expectedCompInstProp,
+ ComponentInstanceProperty actualCompInstProp, Boolean isUpdate) {
+ String uniqueId = expectedCompInstProp.getUniqueId();
+ String type = expectedCompInstProp.getType();
+ String defaulValue = expectedCompInstProp.getDefaultValue();
+ if (actualCompInstProp.getUniqueId().equals(uniqueId)
+ && actualCompInstProp.getPath().equals(expectedCompInstProp.getPath())) {
+ assertTrue("expected type is: " + type + " ,actual: " + actualCompInstProp.getType(),
+ actualCompInstProp.getType().equals(type));
+ if (defaulValue == null) {
+ assertTrue(
+ "expected defaulValue is: " + defaulValue + " ,actual: " + actualCompInstProp.getDefaultValue(),
+ actualCompInstProp.getDefaultValue() == defaulValue);
+ } else {
+ assertTrue(
+ "expected defaulValue is: " + defaulValue + " ,actual: " + actualCompInstProp.getDefaultValue(),
+ actualCompInstProp.getDefaultValue().equals(defaulValue));
+ }
+ if (isUpdate) {
+ assertTrue(
+ "actual [Value] parameter " + actualCompInstProp.getName()
+ + "should equal to expected [Value]: " + actualCompInstProp.getValue() + " ,Value: "
+ + actualCompInstProp.getValue(),
+ actualCompInstProp.getValue().equals(expectedCompInstProp.getValue()));
+ assertNotNull("valueId is null", actualCompInstProp.getValueUniqueUid());
+ } else {
+ if (defaulValue == null) {
+ assertTrue(
+ "actual [Value] parameter " + actualCompInstProp.getName()
+ + "should equal to expected [defaultValue]: " + actualCompInstProp.getValue()
+ + " ,defaultValue: " + actualCompInstProp.getDefaultValue(),
+ actualCompInstProp.getValue() == expectedCompInstProp.getDefaultValue());
+ } else {
+ assertTrue(
+ "actual [Value] parameter " + actualCompInstProp.getName()
+ + "should equal to expected [defaultValue]: " + actualCompInstProp.getValue()
+ + " ,defaultValue: " + actualCompInstProp.getDefaultValue(),
+ actualCompInstProp.getValue().equals(expectedCompInstProp.getDefaultValue()));
+ }
+ assertNull("valueId is not null", actualCompInstProp.getValueUniqueUid());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static List<ComponentInstanceProperty> addResourcePropertiesToList(Resource resource,
+ List<ComponentInstanceProperty> listToFill) {
+ for (PropertyDefinition prop : resource.getProperties()) {
+ listToFill.add(new ComponentInstanceProperty(prop, null, null));
+ }
+ return listToFill;
+ }
+
+ public static List<ComponentInstanceProperty> addComponentInstPropertiesToList(Component component,
+ List<ComponentInstanceProperty> listToFill, String componentId) {
+
+ if (componentId != null) {
+ List<ComponentInstanceProperty> list = component.getComponentInstancesProperties().get(componentId);
+ for (ComponentInstanceProperty prop : list) {
+ ComponentInstanceProperty componentInstanceProperty = new ComponentInstanceProperty(prop, null, null);
+ componentInstanceProperty.setPath(prop.getPath());
+ componentInstanceProperty.setValueUniqueUid(prop.getValueUniqueUid());
+ componentInstanceProperty.setValue(prop.getValue());
+ listToFill.add(componentInstanceProperty);
+ }
+ } else {
+ Map<String, List<ComponentInstanceProperty>> componentInstancesProperties = component
+ .getComponentInstancesProperties();
+ for (Map.Entry<String, List<ComponentInstanceProperty>> componentInstanceProperties : componentInstancesProperties
+ .entrySet()) {
+ for (ComponentInstanceProperty prop : componentInstanceProperties.getValue()) {
+ ComponentInstanceProperty componentInstanceProperty = new ComponentInstanceProperty(prop, null,
+ null);
+ componentInstanceProperty.setPath(prop.getPath());
+ componentInstanceProperty.setValueUniqueUid(prop.getValueUniqueUid());
+ componentInstanceProperty.setValue(prop.getValue());
+ listToFill.add(componentInstanceProperty);
+ }
+ }
+ }
+
+ if (component.getComponentType().getValue().equals("Resource")) {
+ for (PropertyDefinition prop : ((Resource) component).getProperties()) {
+ listToFill.add(new ComponentInstanceProperty(prop, null, null));
+ }
+ }
+ return listToFill;
+ }
+
+ public static ComponentInstanceProperty getCompPropInstListByInstIdAndPropName(Component component,
+ ComponentInstance componentInstanceDetails, String name, String type) {
+ List<ComponentInstanceProperty> propList = component.getComponentInstancesProperties()
+ .get(componentInstanceDetails.getUniqueId());
+ if (propList != null) {
+ return getPropFromListByPropNameAndType(propList, name, type);
+ }
+ return null;
+ }
+
+ private static void updatePropertyListWithPathParameter(Resource resource, List<String> path,
+ List<ComponentInstanceProperty> expectedPropertyList) {
+ List<PropertyDefinition> propertyList = resource.getProperties();
+ for (PropertyDefinition propertyDefinition : propertyList) {
+ ComponentInstanceProperty propDetailsToRemove = PropertyRestUtils.getPropFromListByPropNameAndType(
+ expectedPropertyList, propertyDefinition.getName(), propertyDefinition.getType());
+ ComponentInstanceProperty propDetailsToAdd = propDetailsToRemove;
+ propDetailsToAdd.setPath(path);
+ expectedPropertyList.remove(propDetailsToRemove);
+ expectedPropertyList.add(propDetailsToAdd);
+ }
+ }
+
+ private static void updatePropertyListWithPathParameterOnCompInst(Service service, List<String> path,
+ List<ComponentInstanceProperty> expectedPropertyList) {
+ List<ComponentInstanceProperty> servicePropertyList = new ArrayList<>();
+ servicePropertyList = PropertyRestUtils.addComponentInstPropertiesToList(service, servicePropertyList,
+ path.get(0));
+
+ for (ComponentInstanceProperty serviceCompInstProperty : servicePropertyList) {
+ ComponentInstanceProperty propDetailsToRemove = PropertyRestUtils.getPropFromListByPropNameTypeAndPath(
+ expectedPropertyList, serviceCompInstProperty.getName(), serviceCompInstProperty.getType(),
+ serviceCompInstProperty.getPath());
+ ComponentInstanceProperty propDetailsToAdd = propDetailsToRemove;
+ List<String> tempPathList = new ArrayList<String>();
+ for (String tempPath : path) {
+ tempPathList.add(tempPath);
+ }
+ // path parameter can not contain the same service unique ID twice
+ if (propDetailsToAdd.getPath() != null
+ && !propDetailsToAdd.getPath().get(0).contains(service.getUniqueId())) {
+ if (!propDetailsToAdd.getPath().containsAll(tempPathList)) {
+ tempPathList.addAll(propDetailsToAdd.getPath());
+ }
+ }
+ propDetailsToAdd.setPath(tempPathList);
+ expectedPropertyList.remove(propDetailsToRemove);
+ expectedPropertyList.add(propDetailsToAdd);
+ }
+ }
+
+ public static void updatePropertyListWithPathOnResource(ComponentInstance componentInstDetails, Resource resource,
+ List<ComponentInstanceProperty> list, Component container) {
+ List<String> path = new ArrayList<>();
+ if (container != null) {
+ List<ComponentInstance> componentInstances = container.getComponentInstances();
+ for (ComponentInstance componentInstance : componentInstances) {
+ if (componentInstance.getNormalizedName().equals(componentInstDetails.getNormalizedName())) {
+ path.add(componentInstance.getUniqueId());
+ break;
+ }
+ }
+
+ } else {
+ path.add(componentInstDetails.getUniqueId());
+ }
+ updatePropertyListWithPathParameter(resource, path, list);
+ }
+
+ public static void updatePropertyListWithPathOnComponentInstance(ComponentInstance componentInstDetails,
+ Service service, List<ComponentInstanceProperty> list) {
+ List<String> path = new ArrayList<>();
+ path.add(componentInstDetails.getUniqueId());
+ updatePropertyListWithPathParameterOnCompInst(service, path, list);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResourceRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResourceRestUtils.java
new file mode 100644
index 0000000000..16ffd2f9e2
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResourceRestUtils.java
@@ -0,0 +1,575 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.http.client.ClientProtocolException;
+import org.json.JSONException;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.RelationshipInstData;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ImportReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+public class ResourceRestUtils extends BaseRestUtils {
+ private static Logger logger = LoggerFactory.getLogger(ResourceRestUtils.class.getName());
+
+ // ****** CREATE *******
+
+ public static RestResponse createResource(ResourceReqDetails resourceDetails, User sdncModifierDetails) throws Exception {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ String userId = sdncModifierDetails.getUserId();
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resourceDetails);
+ String calculateMD5 = GeneralUtility.calculateMD5ByString(userBodyJson);
+ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), calculateMD5);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(userBodyJson);
+ RestResponse createResourceResponse = http.httpSendPost(url, userBodyJson, headersMap);
+ if (createResourceResponse.getErrorCode() == STATUS_CODE_CREATED) {
+ resourceDetails.setUUID(ResponseParser.getUuidFromResponse(createResourceResponse));
+ resourceDetails.setVersion(ResponseParser.getVersionFromResponse(createResourceResponse));
+ resourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(createResourceResponse));
+ String lastUpdaterUserId = ResponseParser.getValueFromJsonResponse(createResourceResponse.getResponse(), "lastUpdaterUserId");
+ resourceDetails.setLastUpdaterUserId(lastUpdaterUserId);
+ String lastUpdaterFullName = ResponseParser.getValueFromJsonResponse(createResourceResponse.getResponse(), "lastUpdaterFullName");
+ resourceDetails.setLastUpdaterFullName(lastUpdaterFullName);
+ // Creator details never change after component is created - Ella,
+ // 12/1/2016
+ resourceDetails.setCreatorUserId(userId);
+ resourceDetails.setCreatorFullName(sdncModifierDetails.getFullName());
+ }
+ return createResourceResponse;
+
+ }
+
+ public static RestResponse createImportResource(ImportReqDetails importReqDetails, User sdncModifierDetails, Map<String, String> additionalHeaders) throws JSONException, IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort());
+ String userId = sdncModifierDetails.getUserId();
+
+ Gson gson = new Gson();
+ String resourceImportBodyJson = gson.toJson(importReqDetails);
+ HttpRequest http = new HttpRequest();
+ // System.out.println(url);
+ // System.out.println(resourceImportBodyJson);
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ if (additionalHeaders != null) {
+ headersMap.putAll(additionalHeaders);
+ } else {
+ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), ArtifactRestUtils.calculateMD5(resourceImportBodyJson));
+ }
+
+ RestResponse createResourceResponse = http.httpSendPost(url, resourceImportBodyJson, headersMap);
+ if (createResourceResponse.getErrorCode() == STATUS_CODE_CREATED) {
+ importReqDetails.setVersion(ResponseParser.getVersionFromResponse(createResourceResponse));
+ importReqDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(createResourceResponse));
+ // Creator details never change after component is created - Ella,
+ // 12/1/2016
+ importReqDetails.setCreatorUserId(userId);
+ importReqDetails.setCreatorFullName(sdncModifierDetails.getFullName());
+ importReqDetails.setToscaResourceName(ResponseParser.getToscaResourceNameFromResponse(createResourceResponse));
+ importReqDetails.setDerivedList(ResponseParser.getDerivedListFromJson(createResourceResponse));
+ }
+ return createResourceResponse;
+
+ }
+
+ // ***** DELETE ****
+ public static RestResponse deleteResource(ResourceReqDetails resourceDetails, User sdncModifierDetails, String version) throws IOException {
+
+ if (resourceDetails.getUniqueId() != null) {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_RESOURCE_BY_NAME_AND_VERSION, config.getCatalogBeHost(), config.getCatalogBePort(), resourceDetails.getName(), version);
+ return sendDelete(url, sdncModifierDetails.getUserId());
+ } else {
+ return null;
+ }
+
+ }
+
+ public static RestResponse markResourceToDelete(String resourceId, String userId) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ RestResponse sendDelete = sendDelete(url, userId);
+
+ return sendDelete;
+
+ }
+
+ public static RestResponse deleteResource(String resourceId, String userId) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ RestResponse sendDelete = sendDelete(url, userId);
+
+ deleteMarkedResources(userId);
+
+ return sendDelete;
+
+ }
+
+ public static void deleteMarkedResources(String userId) throws IOException {
+ String url;
+ Config config = Utils.getConfig();
+ url = String.format(Urls.DELETE_MARKED_RESOURCES, config.getCatalogBeHost(), config.getCatalogBePort());
+ sendDelete(url, userId);
+ }
+
+ public static RestResponse deleteResourceByNameAndVersion(User sdncModifierDetails, String resourceName, String resourceVersion) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_RESOURCE_BY_NAME_AND_VERSION, config.getCatalogBeHost(), config.getCatalogBePort(), resourceName, resourceVersion);
+ RestResponse sendDelete = sendDelete(url, sdncModifierDetails.getUserId());
+
+ deleteMarkedResources(sdncModifierDetails.getUserId());
+
+ return sendDelete;
+ }
+
+ public static Boolean deleteResourceByNameAndVersion(String resourceName, String resourceVersion) throws IOException {
+ RestResponse deleteResponse = ResourceRestUtils.deleteResourceByNameAndVersion(ElementFactory.getDefaultUser(UserRoleEnum.ADMIN), resourceName, resourceVersion);
+ return checkErrorCode(deleteResponse);
+ }
+
+ public static Boolean removeResource(String resourceId) throws FileNotFoundException, IOException, ClientProtocolException {
+ RestResponse response = deleteResource(resourceId, ElementFactory.getDefaultUser(UserRoleEnum.ADMIN).getUserId());
+ return checkErrorCode(response);
+ }
+
+ // ************** GET *************
+ public static RestResponse getResource(User sdncModifierDetails, String uniqueId) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), uniqueId);
+ return sendGet(url, sdncModifierDetails.getUserId());
+ }
+
+ public static RestResponse getModule(User sdncModifierDetails, String componentId, String moduleId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_MODULE_BY_ID, config.getCatalogBeHost(), config.getCatalogBePort(), componentId, moduleId);
+ return sendGet(url, sdncModifierDetails.getUserId());
+ }
+
+ public static RestResponse getLatestResourceFromCsarUuid(User sdncModifierDetails, String csarUuid) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_RESOURCE_BY_CSAR_UUID, config.getCatalogBeHost(), config.getCatalogBePort(), csarUuid);
+ return sendGet(url, sdncModifierDetails.getUserId());
+ }
+
+ public static RestResponse getResource(ResourceReqDetails resourceDetails, User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceDetails.getUniqueId());
+ return sendGet(url, sdncModifierDetails.getUserId());
+ }
+
+ public static RestResponse getResourceByNameAndVersion(String userId, String resourceName, String resourceVersion) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_RESOURCE_BY_NAME_AND_VERSION, config.getCatalogBeHost(), config.getCatalogBePort(), resourceName, resourceVersion);
+
+ return sendGet(url, userId);
+ }
+
+ public static RestResponse getResourceList(User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_FOLLWED_LIST, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ return sendGet(url, sdncModifierDetails.getUserId());
+
+ }
+
+ public static RestResponse getResource(String resourceId) throws ClientProtocolException, IOException {
+ return getResource(ElementFactory.getDefaultUser(UserRoleEnum.ADMIN), resourceId);
+ }
+
+ public static RestResponse getLatestResourceFromCsarUuid(String csarUuid) throws ClientProtocolException, IOException {
+ return getLatestResourceFromCsarUuid(ElementFactory.getDefaultUser(UserRoleEnum.ADMIN), csarUuid);
+ }
+
+ public static RestResponse getResourceLatestVersionList(User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_RESOURCE_lATEST_VERSION, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ return sendGet(url, sdncModifierDetails.getUserId());
+
+ }
+
+ public static RestResponse putAllCategoriesTowardsCatalogFeWithUuidNotAllowed(String uuid) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_CATEGORIES_FE, config.getCatalogFeHost(), config.getCatalogFePort(), BaseRestUtils.RESOURCE_COMPONENT_TYPE);
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+ headersMap.put(HttpHeaderEnum.X_ECOMP_REQUEST_ID_HEADER.getValue(), uuid);
+ HttpRequest http = new HttpRequest();
+
+ logger.debug("Send PUT request to get all categories (should be 405): {}", url);
+ return http.httpSendByMethod(url, "PUT", null, headersMap);
+ }
+
+ public static RestResponse getAllTagsTowardsCatalogBe() throws IOException {
+
+ Config config = Utils.getConfig();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ALL_TAGS, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+
+ // logger.debug("Send GET request to get all tags: {}", url);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ public static RestResponse getAllPropertyScopesTowardsCatalogBe() throws IOException {
+
+ Config config = Utils.getConfig();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_PROPERTY_SCOPES_LIST, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), "cs0008");
+
+ // logger.debug("Send GET request to get all property scopes: {}", url);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ public static RestResponse getAllArtifactTypesTowardsCatalogBe() throws IOException {
+
+ Config config = Utils.getConfig();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ALL_ARTIFACTS, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), "cs0008");
+
+ // logger.debug("Send GET request to get all property scopes: {}", url);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ public static RestResponse getConfigurationTowardsCatalogBe() throws IOException {
+
+ Config config = Utils.getConfig();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_CONFIGURATION, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), "cs0008");
+
+ // logger.debug("Send GET request to get all property scopes: {}", url);
+ return http.httpSendGet(url, headersMap);
+
+ }
+
+ public static RestResponse sendOptionsTowardsCatalogFeWithUuid() throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_ALL_CATEGORIES_FE, config.getCatalogFeHost(), config.getCatalogFePort(), BaseRestUtils.RESOURCE_COMPONENT_TYPE);
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderData);
+ HttpRequest http = new HttpRequest();
+
+ logger.debug("Send OPTIONS request for categories: {}", url);
+ return http.httpSendByMethod(url, "OPTIONS", null, headersMap);
+ }
+
+ // ********** UPDATE *************
+ public static RestResponse updateResourceMetadata(ResourceReqDetails updatedResourceDetails, User sdncModifierDetails, String uniqueId, String encoding) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_RESOURCE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(), uniqueId);
+
+ String ContentTypeString = String.format("%s;%s", contentTypeHeaderData, encoding);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(updatedResourceDetails);
+ String userId = sdncModifierDetails.getUserId();
+
+ RestResponse updateResourceResponse = sendPut(url, userBodyJson, userId, ContentTypeString);
+
+ updatedResourceDetails.setVersion(ResponseParser.getVersionFromResponse(updateResourceResponse));
+ updatedResourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(updateResourceResponse));
+
+ return updateResourceResponse;
+ }
+
+ public static RestResponse updateResourceTEST(Resource resource, User sdncModifierDetails, String uniqueId, String encoding) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_RESOURCE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(), uniqueId);
+
+ String ContentTypeString = String.format("%s;%s", contentTypeHeaderData, encoding);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resource);
+ String userId = sdncModifierDetails.getUserId();
+
+ RestResponse updateResourceResponse = sendPut(url, userBodyJson, userId, ContentTypeString);
+ return updateResourceResponse;
+ }
+
+ public static RestResponse updateResourceMetadata(ResourceReqDetails updatedResourceDetails, User sdncModifierDetails, String uniqueId) throws Exception {
+ return updateResourceMetadata(updatedResourceDetails, sdncModifierDetails, uniqueId, "");
+ }
+
+ public static RestResponse updateResourceMetadata(String json, User sdncModifierDetails, String resourceId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_RESOURCE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+ String userId = sdncModifierDetails.getUserId();
+ RestResponse updateResourceResponse = sendPut(url, json, userId, contentTypeHeaderData);
+ return updateResourceResponse;
+ }
+
+ public static RestResponse updateResource(ResourceReqDetails resourceDetails, User sdncModifierDetails, String resourceId) throws IOException {
+ String userId = sdncModifierDetails.getUserId();
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_RESOURCE, config.getCatalogBeHost(), config.getCatalogBePort(), resourceId);
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(resourceDetails);
+ String calculateMD5 = GeneralUtility.calculateMD5ByString(userBodyJson);
+ headersMap.put(HttpHeaderEnum.Content_MD5.getValue(), calculateMD5);
+ HttpRequest http = new HttpRequest();
+ RestResponse updateResourceResponse = http.httpSendPut(url, userBodyJson, headersMap);
+ if (updateResourceResponse.getErrorCode() == STATUS_CODE_UPDATE_SUCCESS) {
+ resourceDetails.setUUID(ResponseParser.getUuidFromResponse(updateResourceResponse));
+ resourceDetails.setVersion(ResponseParser.getVersionFromResponse(updateResourceResponse));
+ resourceDetails.setUniqueId(ResponseParser.getUniqueIdFromResponse(updateResourceResponse));
+ String lastUpdaterUserId = ResponseParser.getValueFromJsonResponse(updateResourceResponse.getResponse(), "lastUpdaterUserId");
+ resourceDetails.setLastUpdaterUserId(lastUpdaterUserId);
+ String lastUpdaterFullName = ResponseParser.getValueFromJsonResponse(updateResourceResponse.getResponse(), "lastUpdaterFullName");
+ resourceDetails.setLastUpdaterFullName(lastUpdaterFullName);
+ resourceDetails.setCreatorUserId(userId);
+ resourceDetails.setCreatorFullName(sdncModifierDetails.getFullName());
+ }
+ return updateResourceResponse;
+ }
+
+ public static RestResponse createResourceInstance(ResourceReqDetails resourceDetails, User modifier, String vfResourceUniqueId) throws Exception {
+ ComponentInstanceReqDetails resourceInstanceReqDetails = ElementFactory.getComponentResourceInstance(resourceDetails);
+ RestResponse createResourceInstanceResponse = ComponentInstanceRestUtils.createComponentInstance(resourceInstanceReqDetails, modifier, vfResourceUniqueId, ComponentTypeEnum.RESOURCE);
+ ResourceRestUtils.checkCreateResponse(createResourceInstanceResponse);
+ return createResourceInstanceResponse;
+ }
+
+ public static RestResponse associateResourceInstances(JSONObject body, User sdncModifierDetails, Component component) throws IOException {
+
+ Config config = Utils.getConfig();
+ Gson gson = new Gson();
+ String bodyJson = gson.toJson(body);
+ component.getComponentType();
+ String componentType = ComponentTypeEnum.findParamByType(component.getComponentType());
+ String url = String.format(Urls.ASSOCIATE__RESOURCE_INSTANCE, config.getCatalogBeHost(), config.getCatalogBePort(), componentType, component.getUniqueId());
+ return sendPost(url, bodyJson, sdncModifierDetails.getUserId(), null);
+
+ }
+
+ public static RestResponse getFollowedList(User sdncModifierDetails) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_FOLLWED_LIST, config.getCatalogBeHost(), config.getCatalogBePort());
+ return sendGet(url, sdncModifierDetails.getUserId());
+ }
+
+ public static List<Resource> restResponseToResourceObjectList(String restResponse) {
+ JsonElement jelement = new JsonParser().parse(restResponse);
+ JsonArray jsonArray = jelement.getAsJsonArray();
+ List<Resource> restResponseArray = new ArrayList<>();
+ Resource resource = null;
+ for (int i = 0; i < jsonArray.size(); i++) {
+ String resourceString = (String) jsonArray.get(i).toString();
+ resource = ResponseParser.convertResourceResponseToJavaObject(resourceString);
+ restResponseArray.add(resource);
+ }
+
+ return restResponseArray;
+
+ }
+
+ public static Resource getResourceObjectFromResourceListByUid(List<Resource> resourceList, String uid) {
+ if (resourceList != null && resourceList.size() > 0) {
+ for (Resource resource : resourceList) {
+ if (resource.getUniqueId().equals(uid))
+ return resource;
+ }
+ } else
+ return null;
+ return null;
+ }
+
+ // =======================================resource
+ // associate==================================================
+ public static RestResponse associate2ResourceInstances(Component container, ComponentInstance fromNode, ComponentInstance toNode, String assocType, User sdncUserDetails) throws IOException {
+ return associate2ResourceInstances(container, fromNode.getUniqueId(), toNode.getUniqueId(), assocType, sdncUserDetails);
+ }
+
+ public static RestResponse associate2ResourceInstances(Component component, String fromNode, String toNode, String assocType, User sdncUserDetails) throws IOException {
+
+ RelationshipInstData relationshipInstData = new RelationshipInstData();
+ Map<String, List<CapabilityDefinition>> capabilitiesMap = component.getCapabilities();
+ Map<String, List<RequirementDefinition>> requirementMap = component.getRequirements();
+ List<CapabilityDefinition> capabilitiesList = capabilitiesMap.get(assocType);
+ List<RequirementDefinition> requirementList = requirementMap.get(assocType);
+
+ RequirementDefinition requirementDefinitionFrom = getRequirementDefinitionByOwnerId(requirementList, fromNode);
+ CapabilityDefinition capabilityDefinitionTo = getCapabilityDefinitionByOwnerId(capabilitiesList, toNode);
+ relationshipInstData.setCapabilityOwnerId(capabilityDefinitionTo.getOwnerId());
+ relationshipInstData.setCapabiltyId(capabilityDefinitionTo.getUniqueId());
+ relationshipInstData.setRequirementOwnerId(requirementDefinitionFrom.getOwnerId());
+ relationshipInstData.setRequirementId(requirementDefinitionFrom.getUniqueId());
+
+ JSONObject assocBody = assocBuilder(relationshipInstData, capabilityDefinitionTo, requirementDefinitionFrom, toNode, fromNode);
+ return ResourceRestUtils.associateResourceInstances(assocBody, sdncUserDetails, component);
+
+ }
+
+ private static JSONObject assocBuilder(RelationshipInstData relationshipInstData, CapabilityDefinition capabilityDefinitionTo, RequirementDefinition requirementDefinitionFrom, String toNode, String fromNode) {
+
+ String type = capabilityDefinitionTo.getType();
+ String requirement = requirementDefinitionFrom.getName();
+ String capability = requirementDefinitionFrom.getName();
+
+ JSONObject wrapper = new JSONObject();
+ JSONArray relationshipsArray = new JSONArray();
+ JSONObject relationship = new JSONObject();
+ JSONObject simpleObject = new JSONObject();
+
+ relationship.put("type", type);
+ simpleObject.put("relationship", relationship);
+ simpleObject.put("requirement", requirement);
+ simpleObject.put("capability", capability);
+ simpleObject.put("capabilityUid", relationshipInstData.getCapabiltyId());
+ simpleObject.put("capabilityOwnerId", relationshipInstData.getCapabilityOwnerId());
+ simpleObject.put("requirementOwnerId", relationshipInstData.getRequirementOwnerId());
+ simpleObject.put("requirementUid", relationshipInstData.getRequirementId());
+ relationshipsArray.add(simpleObject);
+
+ ArrayList<Object> relationships = new ArrayList<Object>(relationshipsArray);
+ wrapper.put("fromNode", fromNode);
+ wrapper.put("toNode", toNode);
+ wrapper.put("relationships", relationships);
+ return wrapper;
+
+ }
+
+ private static CapabilityDefinition getCapabilityDefinitionByOwnerId(List<CapabilityDefinition> capabilityDefinitionList, String ownerId) {
+
+ for (CapabilityDefinition capabilityDefinition : capabilityDefinitionList) {
+ if (capabilityDefinition.getOwnerId().equals(ownerId)) {
+ return capabilityDefinition;
+ }
+ }
+ return null;
+ }
+
+ private static RequirementDefinition getRequirementDefinitionByOwnerId(List<RequirementDefinition> requirementDefinitionList, String ownerId) {
+
+ for (RequirementDefinition requirementDefinition : requirementDefinitionList) {
+ if (requirementDefinition.getOwnerId().equals(ownerId)) {
+ return requirementDefinition;
+ }
+ }
+ return null;
+ }
+
+ public static String getRiUniqueIdByRiName(Component component, String resourceInstanceName) {
+
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ String name = null;
+ for (ComponentInstance componentInstance : componentInstances) {
+ if (componentInstance.getName().equals(resourceInstanceName)) {
+ name = componentInstance.getUniqueId();
+ break;
+ }
+ }
+ return name;
+ }
+
+ public static Resource convertResourceGetResponseToJavaObject(ResourceReqDetails resourceDetails) throws IOException {
+ RestResponse response = ResourceRestUtils.getResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ assertEquals("Check response code after get resource", 200, response.getErrorCode().intValue());
+ return ResponseParser.convertResourceResponseToJavaObject(response.getResponse());
+ }
+
+ public static RestResponse changeResourceInstanceVersion(String containerUniqueId, String instanceToReplaceUniqueId, String newResourceUniqueId, User sdncModifierDetails, ComponentTypeEnum componentType) throws IOException {
+ return ProductRestUtils.changeServiceInstanceVersion(containerUniqueId, instanceToReplaceUniqueId, newResourceUniqueId, sdncModifierDetails, componentType);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java
new file mode 100644
index 0000000000..dfcb9c2736
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ResponseParser.java
@@ -0,0 +1,541 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+import org.codehaus.jackson.Version;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.module.SimpleModule;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintJacksonDeserialiser;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class ResponseParser {
+
+ private static final String INVARIANT_UUID = "invariantUUID";
+ public static final String UNIQUE_ID = "uniqueId";
+ public static final String VERSION = "version";
+ public static final String UUID = "uuid";
+ public static final String NAME = "name";
+ public static final String ORIGIN_TYPE = "originType";
+ public static final String TOSCA_RESOURCE_NAME = "toscaResourceName";
+
+ static Logger logger = Logger.getLogger(ResponseParser.class.getName());
+
+ public static String getValueFromJsonResponse(String response, String fieldName) {
+ try {
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(response);
+ Object fieldValue = jsonResp.get(fieldName);
+ return fieldValue.toString();
+
+ } catch (Exception e) {
+ return null;
+ }
+
+ }
+
+ public static String getUniqueIdFromResponse(RestResponse response) {
+ return getValueFromJsonResponse(response.getResponse(), UNIQUE_ID);
+ }
+
+ public static String getInvariantUuid(RestResponse response) {
+ return getValueFromJsonResponse(response.getResponse(), INVARIANT_UUID);
+ }
+
+ public static String getUuidFromResponse(RestResponse response) {
+ return getValueFromJsonResponse(response.getResponse(), UUID);
+ }
+
+ public static String getNameFromResponse(RestResponse response) {
+ return getValueFromJsonResponse(response.getResponse(), NAME);
+ }
+
+ public static String getVersionFromResponse(RestResponse response) {
+ return ResponseParser.getValueFromJsonResponse(response.getResponse(), VERSION);
+ }
+
+ public static String getComponentTypeFromResponse(RestResponse response) {
+ return ResponseParser.getValueFromJsonResponse(response.getResponse(), ORIGIN_TYPE);
+ }
+
+ public static String getToscaResourceNameFromResponse(RestResponse response) {
+ return getValueFromJsonResponse(response.getResponse(), TOSCA_RESOURCE_NAME);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static ResourceRespJavaObject parseJsonListReturnResourceDetailsObj(RestResponse restResponse,
+ String resourceType, String searchPattern, String expectedResult) throws Exception {
+
+ // Gson gson = new Gson;
+
+ JsonElement jElement = new JsonParser().parse(restResponse.getResponse());
+ JsonObject jObject = jElement.getAsJsonObject();
+ JsonArray arrayOfObjects = (JsonArray) jObject.get(resourceType);
+ Gson gson = new Gson();
+ Map<String, Object> map = new HashMap<String, Object>();
+ ResourceRespJavaObject jsonToJavaObject = new ResourceRespJavaObject();
+
+ for (int counter = 0; counter < arrayOfObjects.size(); counter++) {
+ JsonObject jHitObject = (JsonObject) arrayOfObjects.get(counter);
+
+ map = (Map<String, Object>) gson.fromJson(jHitObject.toString(), map.getClass());
+ if (map.get(searchPattern).toString().contains(expectedResult)) {
+
+ jsonToJavaObject = gson.fromJson(jObject, ResourceRespJavaObject.class);
+ break;
+ }
+ }
+ return jsonToJavaObject;
+
+ }
+
+ public static Resource convertResourceResponseToJavaObject(String response) {
+
+ ObjectMapper mapper = new ObjectMapper();
+ final SimpleModule module = new SimpleModule("customerSerializationModule",
+ new Version(1, 0, 0, "static version"));
+ JsonDeserializer<PropertyConstraint> desrializer = new PropertyConstraintJacksonDeserialiser();
+ addDeserializer(module, PropertyConstraint.class, desrializer);
+
+ mapper.registerModule(module);
+ Resource resource = null;
+ try {
+ resource = mapper.readValue(response, Resource.class);
+ logger.debug(resource.toString());
+ } catch (IOException e) {
+ try {
+ List<Resource> resources = Arrays.asList(mapper.readValue(response.toString(), Resource[].class));
+ resource = resources.get(0);
+ } catch (Exception e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ }
+
+ return resource;
+ }
+
+ public static ComponentInstanceProperty convertPropertyResponseToJavaObject(String response) {
+
+ ObjectMapper mapper = new ObjectMapper();
+ final SimpleModule module = new SimpleModule("customerSerializationModule",
+ new Version(1, 0, 0, "static version"));
+ JsonDeserializer<PropertyConstraint> desrializer = new PropertyConstraintJacksonDeserialiser();
+ addDeserializer(module, PropertyConstraint.class, desrializer);
+
+ mapper.registerModule(module);
+ ComponentInstanceProperty propertyDefinition = null;
+ try {
+ propertyDefinition = mapper.readValue(response, ComponentInstanceProperty.class);
+ logger.debug(propertyDefinition.toString());
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return propertyDefinition;
+ }
+
+ // public static ResourceInstanceReqDetails
+ // convertResourceInstanceResponseToJavaObject(String response) {
+ //
+ // ObjectMapper mapper = new ObjectMapper();
+ // final SimpleModule module = new
+ // SimpleModule("customerSerializationModule", new Version(1, 0, 0, "static
+ // version"));
+ // JsonDeserializer<PropertyConstraint> desrializer = new
+ // PropertyConstraintJacksonDeserialiser();
+ // addDeserializer(module, PropertyConstraint.class, desrializer);
+ //
+ // mapper.registerModule(module);
+ // ResourceInstanceReqDetails resourceInstanceReqDetails = null;
+ // try {
+ // resourceInstanceReqDetails = mapper.readValue(response,
+ // ResourceInstanceReqDetails.class);
+ // logger.debug(resourceInstanceReqDetails.toString());
+ // } catch (IOException e) {
+ // // TODO Auto-generated catch block
+ // e.printStackTrace();
+ // }
+ //
+ // return resourceInstanceReqDetails;
+ // }
+ public static String toJson(Object object) {
+ Gson gson = new Gson();
+ return gson.toJson(object);
+ }
+
+ public static ArtifactDefinition convertArtifactDefinitionResponseToJavaObject(String response) {
+ ObjectMapper mapper = new ObjectMapper();
+ ArtifactDefinition artifactDefinition = null;
+ try {
+
+ artifactDefinition = mapper.readValue(response, ArtifactDefinition.class);
+ logger.debug(artifactDefinition.toString());
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return artifactDefinition;
+
+ }
+
+ public static ArtifactReqDetails convertArtifactReqDetailsToJavaObject(String response) {
+
+ ArtifactReqDetails artifactReqDetails = null;
+ // try {
+ //
+ // artifactDefinition = mapper.readValue(response,
+ // ArtifactReqDetails.class);
+ // logger.debug(artifactDefinition.toString());
+ // } catch (IOException e) {
+ // // TODO Auto-generated catch block
+ // e.printStackTrace();
+ // }
+ //
+ // return artifactDefinition;
+ Gson gson = new Gson();
+ artifactReqDetails = gson.fromJson(response, ArtifactReqDetails.class);
+ return artifactReqDetails;
+ }
+
+ public static <T> T parseToObject(String json, Class<T> clazz) {
+ Gson gson = new Gson();
+ T object;
+ try {
+ object = gson.fromJson(json, clazz);
+ } catch (Exception e) {
+ object = parseToObjectUsingMapper(json, clazz);
+ }
+ return object;
+ }
+
+ public static <T> T parseToObjectUsingMapper(String json, Class<T> clazz) {
+ // Generic convert
+ ObjectMapper mapper = new ObjectMapper();
+ T object = null;
+ final SimpleModule module = new SimpleModule("customerSerializationModule",
+ new Version(1, 0, 0, "static version"));
+ JsonDeserializer<PropertyConstraint> desrializer = new PropertyConstraintJacksonDeserialiser();
+ addDeserializer(module, PropertyConstraint.class, desrializer);
+ mapper.registerModule(module);
+ try {
+ object = mapper.readValue(json, clazz);
+ // System.out.println("Class: "+clazz.getSimpleName()+", json:
+ // "+json);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return object;
+ }
+
+ public static ArtifactReqDetails convertArtifactDefinitionToArtifactReqDetailsObject(
+ ArtifactDefinition artifactDefinition) {
+
+ ArtifactReqDetails artifactReqDetails = null;
+ // try {
+ //
+ // artifactDefinition = mapper.readValue(response,
+ // ArtifactReqDetails.class);
+ // logger.debug(artifactDefinition.toString());
+ // } catch (IOException e) {
+ // // TODO Auto-generated catch block
+ // e.printStackTrace();
+ // }
+ //
+ // return artifactDefinition;
+ Gson gson = new Gson();
+ String artDef = gson.toJson(artifactDefinition);
+ artifactReqDetails = gson.fromJson(artDef, ArtifactReqDetails.class);
+ return artifactReqDetails;
+ }
+
+ public static <T> void addDeserializer(SimpleModule module, Class<T> clazz,
+ final JsonDeserializer<T> deserializer) {
+ module.addDeserializer(clazz, deserializer);
+ }
+
+ public static Service convertServiceResponseToJavaObject(String response) {
+
+ ObjectMapper mapper = new ObjectMapper();
+ final SimpleModule module = new SimpleModule("customerSerializationModule",
+ new Version(1, 0, 0, "static version"));
+ JsonDeserializer<PropertyConstraint> desrializer = new PropertyConstraintJacksonDeserialiser();
+ addDeserializer(module, PropertyConstraint.class, desrializer);
+
+ mapper.registerModule(module);
+ Service service = null;
+ try {
+ service = mapper.readValue(response, Service.class);
+ logger.debug(service.toString());
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return service;
+ }
+
+ public static Product convertProductResponseToJavaObject(String response) {
+
+ ObjectMapper mapper = new ObjectMapper();
+ Product product = null;
+ try {
+ product = mapper.readValue(response, Product.class);
+ logger.debug(product.toString());
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return product;
+ }
+
+ public static ComponentInstance convertComponentInstanceResponseToJavaObject(String response) {
+
+ ObjectMapper mapper = new ObjectMapper();
+ final SimpleModule module = new SimpleModule("customerSerializationModule",
+ new Version(1, 0, 0, "static version"));
+ JsonDeserializer<PropertyConstraint> desrializer = new PropertyConstraintJacksonDeserialiser();
+ addDeserializer(module, PropertyConstraint.class, desrializer);
+
+ mapper.registerModule(module);
+ ComponentInstance componentInstance = null;
+ try {
+ componentInstance = mapper.readValue(response, ComponentInstance.class);
+ logger.debug(componentInstance.toString());
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return componentInstance;
+ }
+
+ public static List<String> getValuesFromJsonArray(RestResponse message) throws Exception {
+ List<String> artifactTypesArrayFromApi = new ArrayList<String>();
+
+ org.json.JSONObject responseObject = new org.json.JSONObject(message.getResponse());
+ JSONArray jArr = responseObject.getJSONArray("artifactTypes");
+
+ for (int i = 0; i < jArr.length(); i++) {
+ org.json.JSONObject jObj = jArr.getJSONObject(i);
+ String value = jObj.get("name").toString();
+
+ artifactTypesArrayFromApi.add(value);
+ }
+ return artifactTypesArrayFromApi;
+ }
+
+ public static String calculateMD5Header(ArtifactReqDetails artifactDetails) {
+ Gson gson = new Gson();
+ String jsonBody = gson.toJson(artifactDetails);
+ // calculate MD5 for json body
+ return calculateMD5(jsonBody);
+
+ }
+
+ public static String calculateMD5(String data) {
+ String calculatedMd5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(data);
+ // encode base-64 result
+ byte[] encodeBase64 = Base64.encodeBase64(calculatedMd5.getBytes());
+ String encodeBase64Str = new String(encodeBase64);
+ return encodeBase64Str;
+
+ }
+
+ public static List<Map<String, Object>> getAuditFromMessage(Map auditingMessage) {
+ List<Map<String, Object>> auditList = new ArrayList<Map<String, Object>>();
+ // JsonElement jElement = new JsonParser().parse(auditingMessage);
+ // JsonObject jObject = jElement.getAsJsonObject();
+ // JsonObject hitsObject = (JsonObject) jObject.get("hits");
+ // JsonArray hitsArray = (JsonArray) hitsObject.get("hits");
+ //
+ // Iterator<JsonElement> hitsIterator = hitsArray.iterator();
+ // while(hitsIterator.hasNext())
+ // {
+ // JsonElement nextHit = hitsIterator.next();
+ // JsonObject jHitObject = nextHit.getAsJsonObject();
+ // JsonObject jSourceObject = (JsonObject) jHitObject.get("_source");
+ //
+ // Gson gson=new Gson();
+ // String auditUnparsed = jSourceObject.toString();
+ //
+ // Map<String,Object> map = new HashMap<String,Object>();
+ // map = (Map<String,Object>) gson.fromJson(auditUnparsed,
+ // map.getClass());
+
+ auditList.add(auditingMessage);
+ // }
+ return auditList;
+ }
+
+ public static List<CategoryDefinition> parseCategories(RestResponse getAllCategoriesRest) {
+
+ List<CategoryDefinition> categories = new ArrayList<>();
+ try {
+ JsonElement jElement = new JsonParser().parse(getAllCategoriesRest.getResponse());
+ JsonArray cagegories = jElement.getAsJsonArray();
+ Iterator<JsonElement> iter = cagegories.iterator();
+ while (iter.hasNext()) {
+ JsonElement next = iter.next();
+ CategoryDefinition category = ResponseParser.parseToObject(next.toString(), CategoryDefinition.class);
+ categories.add(category);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return categories;
+ }
+
+ public static JSONArray getListFromJson(RestResponse res, String field) throws JSONException {
+ String valueFromJsonResponse = getValueFromJsonResponse(res.getResponse(), field);
+ JSONArray jArr = new JSONArray(valueFromJsonResponse);
+
+ return jArr;
+ }
+
+ public static List<String> getDerivedListFromJson(RestResponse res) throws JSONException {
+ JSONArray listFromJson = getListFromJson(res, "derivedList");
+ List<String> lst = new ArrayList<String>();
+ for (int i = 0; i < listFromJson.length(); i++) {
+ lst.add(listFromJson.getString(i));
+ }
+
+ return lst;
+ }
+
+ public static Map<String, Object> convertStringToMap(String obj) {
+ Map<String, Object> object = (Map<String, Object>) JSONValue.parse(obj);
+ return object;
+ }
+
+ public static List<Map<String, Object>> getListOfMapsFromJson(RestResponse res, String field) throws Exception {
+ List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
+ JSONArray listFromJson = getListFromJson(res, field);
+ for (int i = 0; i < listFromJson.length(); i++) {
+ Map<String, Object> convertStringToMap = convertStringToMap(listFromJson.getString(i));
+ list.add(convertStringToMap);
+ }
+ return list;
+
+ }
+
+ public static Map<String, Object> getJsonValueAsMap(RestResponse response, String key) {
+ String valueField = getValueFromJsonResponse(response.getResponse(), key);
+ Map<String, Object> convertToMap = convertStringToMap(valueField);
+ return convertToMap;
+ }
+
+ public static String getJsonObjectValueByKey(String metadata, String key) {
+ JsonElement jelement = new JsonParser().parse(metadata);
+
+ JsonObject jobject = jelement.getAsJsonObject();
+ Object obj = jobject.get(key);
+ if (obj == null) {
+ return null;
+ } else {
+ return obj.toString();
+ }
+ }
+
+ public static Map<String, ArrayList<Component>> convertCatalogResponseToJavaObject(String response) {
+
+ Map<String, ArrayList<Component>> map = new HashMap<String, ArrayList<Component>>();
+
+ JsonElement jElement = new JsonParser().parse(response);
+ JsonObject jObject = jElement.getAsJsonObject();
+ JsonArray jArrReousrces = jObject.getAsJsonArray("resources");
+ JsonArray jArrServices = jObject.getAsJsonArray("services");
+ JsonArray jArrProducts = jObject.getAsJsonArray("products");
+
+ //////// RESOURCE/////////////////////////////
+ ArrayList<Component> restResponseArray = new ArrayList<>();
+ Component component = null;
+ for (int i = 0; i < jArrReousrces.size(); i++) {
+ String resourceString = (String) jArrReousrces.get(i).toString();
+ component = ResponseParser.convertResourceResponseToJavaObject(resourceString);
+ restResponseArray.add(component);
+ }
+
+ map.put("resources", restResponseArray);
+
+ ///////// SERVICE/////////////////////////////
+
+ restResponseArray = new ArrayList<>();
+ component = null;
+ for (int i = 0; i < jArrServices.size(); i++) {
+ String resourceString = (String) jArrServices.get(i).toString();
+ component = ResponseParser.convertServiceResponseToJavaObject(resourceString);
+ restResponseArray.add(component);
+ }
+
+ map.put("services", restResponseArray);
+
+ ///////// PRODUCT/////////////////////////////
+ restResponseArray = new ArrayList<>();
+ component = null;
+ for (int i = 0; i < jArrProducts.size(); i++) {
+ String resourceString = (String) jArrProducts.get(i).toString();
+ component = ResponseParser.convertProductResponseToJavaObject(resourceString);
+ restResponseArray.add(component);
+ }
+
+ map.put("products", restResponseArray);
+
+ return map;
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ServiceRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ServiceRestUtils.java
new file mode 100644
index 0000000000..d62c74c3d2
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/ServiceRestUtils.java
@@ -0,0 +1,262 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class ServiceRestUtils extends BaseRestUtils {
+ private static Logger logger = LoggerFactory.getLogger(ServiceRestUtils.class.getName());
+ private final static String cacheControl = "no-cache";
+ private final static String contentTypeHeaderData = "application/json";
+ private final static String acceptHeaderDate = "application/json";
+ // ****** CREATE *******
+
+ private static Gson gson = new Gson();
+
+ public static RestResponse deleteService(String serviceName, String version, User sdncModifierDetails) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_SERVICE_BY_NAME_AND_VERSION, config.getCatalogBeHost(), config.getCatalogBePort(), serviceName, version);
+ String userId = sdncModifierDetails.getUserId();
+ RestResponse sendDelete = sendDelete(url, userId);
+ deleteMarkedServices(userId);
+ return sendDelete;
+ }
+
+ public static RestResponse deleteServiceById(String serviceId, String userId) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.DELETE_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId);
+ RestResponse sendDelete = sendDelete(url, userId);
+ deleteMarkedServices(userId);
+ return sendDelete;
+ }
+
+ public static void deleteMarkedServices(String userId) throws IOException {
+ String url;
+ Config config = Utils.getConfig();
+ url = String.format(Urls.DELETE_MARKED_SERVICES, config.getCatalogBeHost(), config.getCatalogBePort());
+ sendDelete(url, userId);
+ }
+
+ public static RestResponse createService(ServiceReqDetails service, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.CREATE_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort());
+ String serviceBodyJson = gson.toJson(service);
+
+ logger.debug("Send POST request to create service: {]", url);
+ logger.debug("Service body: {}", serviceBodyJson);
+
+ RestResponse res = sendPost(url, serviceBodyJson, user.getUserId(), acceptHeaderData);
+ if (res.getErrorCode() == STATUS_CODE_CREATED) {
+ service.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ service.setVersion(ResponseParser.getVersionFromResponse(res));
+ service.setUUID(ResponseParser.getUuidFromResponse(res));
+ // Creator details never change after component is created - Ella,
+ // 12/1/2016
+ service.setCreatorUserId(user.getUserId());
+ service.setCreatorFullName(user.getFullName());
+ }
+
+ return res;
+ }
+
+ public static RestResponse updateService(ServiceReqDetails service, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.UPDATE_SERVICE_METADATA, config.getCatalogBeHost(), config.getCatalogBePort(),
+ service.getUniqueId());
+ String serviceBodyJson = gson.toJson(service);
+
+ logger.debug("Send PUT request to create service: {}", url);
+ logger.debug("Service body: {}", serviceBodyJson);
+
+ RestResponse res = sendPut(url, serviceBodyJson, user.getUserId(), acceptHeaderData);
+ if (res.getErrorCode() == STATUS_CODE_CREATED) {
+ service.setUniqueId(ResponseParser.getUniqueIdFromResponse(res));
+ service.setVersion(ResponseParser.getVersionFromResponse(res));
+ }
+
+ return res;
+ }
+
+ public static RestResponse getService(ServiceReqDetails serviceReqDetails, User sdncModifierDetails)
+ throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ serviceReqDetails.getUniqueId());
+ return getServiceFromUrl(url, sdncModifierDetails, false);
+ }
+
+ public static RestResponse getService(String serviceId, User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_SERVICE, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId);
+ return getServiceFromUrl(url, sdncModifierDetails, false);
+ }
+
+ public static RestResponse getServiceByNameAndVersion(User sdncModifierDetails, String serviceName,
+ String serviceVersion) throws IOException {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_SERVICE_BY_NAME_AND_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort(), serviceName, serviceVersion);
+ return getServiceFromUrl(url, sdncModifierDetails, false);
+ }
+
+ public static RestResponse getServiceFromUrl(String url, User sdncModifierDetails, boolean isCached)
+ throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(sdncModifierDetails, isCached);
+ HttpRequest http = new HttpRequest();
+ logger.debug("Send GET request to create service: {}", url);
+ logger.debug("Service headers: {}", headersMap);
+ RestResponse sendGetServerRequest = http.httpSendGet(url, headersMap);
+
+ return sendGetServerRequest;
+ }
+
+ public static Map<String, String> prepareHeadersMap(User sdncModifierDetails, boolean isCached) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ if (isCached)
+ headersMap.put(HttpHeaderEnum.CACHE_CONTROL.getValue(), cacheControl);
+
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ return headersMap;
+ }
+
+ public static RestResponse approveServiceDistribution(String serviceId, String userId) throws Exception {
+ return changeServiceDistributionState(serviceId, userId, Urls.APPROVE_DISTRIBUTION);
+ }
+
+ public static RestResponse rejectServiceDistribution(String serviceId, String userId) throws Exception {
+ return changeServiceDistributionState(serviceId, userId, Urls.REJECT_DISTRIBUTION);
+ }
+
+ // Benny
+ public static RestResponse rejectServiceDistribution(String serviceId, String userId, String comment)
+ throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.REJECT_DISTRIBUTION, config.getCatalogBeHost(), config.getCatalogBePort(),
+ serviceId);
+ String userBodyJson = gson.toJson(comment);
+ return sendPost(url, userBodyJson, userId, acceptHeaderData);
+
+ }
+
+ private static RestResponse changeServiceDistributionState(String serviceId, String userId, String distributionUrl)
+ throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format(distributionUrl, config.getCatalogBeHost(), config.getCatalogBePort(), serviceId);
+ String defComment = "{ userRemarks : \"this is an test\" }";
+ String userBodyJson = gson.toJson(defComment);
+ return sendPost(url, userBodyJson, userId, acceptHeaderData);
+
+ }
+
+ public static RestResponse getServiceLatestVersionList(User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+ String url = String.format(Urls.GET_SERVICE_lATEST_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort());
+
+ return sendGet(url, sdncModifierDetails.getUserId());
+
+ }
+
+ public static RestResponse createServiceByHttpMethod(ServiceReqDetails serviceDetails, User sdncModifierDetails,
+ String method, String urls) throws IOException {
+ Map<String, String> headersMap = prepareHeadersMap(sdncModifierDetails, true);
+
+ Config config = Utils.getConfig();
+ String serviceBodyJson = gson.toJson(serviceDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(urls, config.getCatalogBeHost(), config.getCatalogBePort());
+ // TODO: ADD AUTHENTICATION IN REQUEST
+ logger.debug(url);
+ logger.debug("Send {} request to create user: {}", method, url);
+ logger.debug("User body: {}", serviceBodyJson);
+ logger.debug("User headers: {}", headersMap);
+ RestResponse sendCreateUserRequest = http.httpSendByMethod(url, method, serviceBodyJson, headersMap);
+
+ return sendCreateUserRequest;
+
+ }
+
+ public static RestResponse deleteServiceByNameAndVersion(User sdncModifierDetails, String serviceName,
+ String serviceVersion) throws IOException {
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = prepareHeadersMap(sdncModifierDetails, true);
+
+ HttpRequest http = new HttpRequest();
+
+ String url = String.format(Urls.DELETE_SERVICE_BY_NAME_AND_VERSION, config.getCatalogBeHost(),
+ config.getCatalogBePort(), serviceName, serviceVersion);
+ RestResponse deleteResponse = http.httpSendDelete(url, headersMap);
+
+ return deleteResponse;
+ }
+
+ public static RestResponse getFollowed(User user) throws Exception {
+ Config config = Utils.getConfig();
+
+ HttpRequest httpRequest = new HttpRequest();
+
+ String url = String.format(Urls.GET_FOLLWED_LIST, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), user.getUserId());
+
+ RestResponse getResourceNotAbstarctResponse = httpRequest.httpSendGet(url, headersMap);
+
+ return getResourceNotAbstarctResponse;
+ }
+
+ public static JSONArray getListArrayFromRestResponse(RestResponse restResponse) {
+ String json = restResponse.getResponse();
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(json);
+ JSONArray servicesArray = (JSONArray) jsonResp.get("services");
+
+ logger.debug("services = {}", servicesArray);
+
+ return servicesArray;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/UserRestUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/UserRestUtils.java
new file mode 100644
index 0000000000..a8f39b9ad8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/rest/UserRestUtils.java
@@ -0,0 +1,282 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.rest;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.run.StartTest;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+
+public class UserRestUtils extends BaseRestUtils {
+
+ static Gson gson = new Gson();
+
+ static Logger logger = LoggerFactory.getLogger(UserRestUtils.class.getName());
+ static String contentTypeHeaderData = "application/json";
+ static String acceptHeaderDate = "application/json";
+
+ public UserRestUtils() {
+ super();
+
+ StartTest.enableLogger();
+ }
+
+ public static RestResponse createUser(User sdncUserDetails, User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ String userBodyJson = gson.toJson(sdncUserDetails);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.CREATE_USER, config.getCatalogBeHost(), config.getCatalogBePort());
+
+ logger.debug("Send POST request to create user: {}", url);
+ logger.debug("User body: {}", userBodyJson);
+ logger.debug("User headers: {}", headersMap);
+ RestResponse sendCreateUserRequest = http.httpSendPost(url, userBodyJson, headersMap);
+
+ return sendCreateUserRequest;
+
+ }
+
+ public static RestResponse deactivateUser(User sdncUserDetails, User sdncModifierDetails) throws IOException {
+ return deleteUser(sdncUserDetails, sdncModifierDetails, true);
+ }
+
+ public static RestResponse deActivateUser(User sdncUserDetails, User sdncModifierDetails) throws IOException {
+ return deleteUser(sdncUserDetails, sdncModifierDetails, false);
+ }
+
+ public static RestResponse deleteUser(User sdncUserDetails, User sdncModifierDetails, boolean isForceDelete)
+ throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ if (isForceDelete) {
+ headersMap.put(User.FORCE_DELETE_HEADER_FLAG, User.FORCE_DELETE_HEADER_FLAG);
+ }
+
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.DELETE_USER, config.getCatalogBeHost(), config.getCatalogBePort(),
+ sdncUserDetails.getUserId());
+ RestResponse sendDeleteUserRequest = http.httpSendDelete(url, headersMap);
+ return sendDeleteUserRequest;
+
+ }
+
+ public static RestResponse updateUser(User sdncUserDetails, User sdncModifierDetails)
+ throws IOException, CloneNotSupportedException {
+
+ Config config = Utils.getConfig();
+ User user = new User(sdncModifierDetails);
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ user.setUserId(StringUtils.EMPTY);
+ user.setRole(StringUtils.EMPTY);
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(user);
+ logger.debug("userBodyJson: {}", userBodyJson);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_USER, config.getCatalogBeHost(), config.getCatalogBePort(),
+ sdncModifierDetails.getUserId());
+ RestResponse sendUpdateUserRequest = http.httpSendPost(url, userBodyJson, headersMap);
+
+ return sendUpdateUserRequest;
+ }
+
+ /// Benny
+ public static RestResponse updateUserRole(User sdncUserDetails, User sdncModifierDetails, String userIdToUpdate)
+ throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ Gson gson = new Gson();
+ String userBodyJson = gson.toJson(sdncUserDetails);
+ logger.debug("userBodyJson: {}", userBodyJson);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.UPDATE_USER_ROLE, config.getCatalogBeHost(), config.getCatalogBePort(),
+ userIdToUpdate);
+ RestResponse sendUpdateUserRequest = http.httpSendPost(url, userBodyJson, headersMap);
+
+ return sendUpdateUserRequest;
+
+ }
+
+ public static RestResponse getUser(User sdncUserDetails, User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_USER, config.getCatalogBeHost(), config.getCatalogBePort(),
+ sdncUserDetails.getUserId());
+ RestResponse sendGetUserRequest = http.httpSendGet(url, headersMap);
+ return sendGetUserRequest;
+
+ }
+
+ public static RestResponse getAllAdminUsers(User sdncModifierDetails) throws IOException {
+
+ Config config = Utils.getConfig();
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ // Gson gson = new Gson();
+ // String userBodyJson = gson.toJson(sdncModifierDetails);
+ // System.out.println(userBodyJson);
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_ALL_ADMIN_USERS, config.getCatalogBeHost(), config.getCatalogBePort());
+ logger.debug("Send following url: {} and headers: {}", url, headersMap.toString());
+ RestResponse sendGetUserRequest = http.httpSendGet(url, headersMap);
+
+ return sendGetUserRequest;
+
+ }
+
+ // US571255
+ public static RestResponse getUsersByRoles(User sdncModifierDetails, String roles) throws IOException {
+
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+
+ HttpRequest http = new HttpRequest();
+ String url;
+ if (roles == "/") {
+ url = String.format(Urls.GET_ALL_USERS, config.getCatalogBeHost(), config.getCatalogBePort());
+ } else {
+ url = String.format(Urls.GET_USERS_BY_ROLES, config.getCatalogBeHost(), config.getCatalogBePort(), roles);
+
+ }
+ logger.debug("Send following url: {} and headers: {}", url, headersMap.toString());
+ RestResponse sendGetUserRequest = http.httpSendGet(url, headersMap);
+ return sendGetUserRequest;
+ }
+
+ public static RestResponse getUsersByRolesHttpCspAtuUidIsMissing(User sdncModifierDetails, String roles)
+ throws Exception {
+
+ Config config = Utils.getConfig();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), contentTypeHeaderData);
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), acceptHeaderDate);
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncModifierDetails.getUserId());
+ headersMap.remove("USER_ID");
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.GET_USERS_BY_ROLES, config.getCatalogBeHost(), config.getCatalogBePort(),
+ roles);
+ logger.debug(
+ "Send following url without USER_ID header : " + url + " headers: " + headersMap.toString());
+
+ RestResponse sendGetUserRequest = http.httpSendGet(url, headersMap);
+ return sendGetUserRequest;
+ }
+
+ public static RestResponse authorizedUserTowardsCatalogBe(User sdncUserDetails) throws IOException {
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ if (sdncUserDetails.getFirstName() != null) {
+ headersMap.put(HttpHeaderEnum.HTTP_CSP_FIRSTNAME.getValue(), sdncUserDetails.getFirstName());
+ }
+ if (sdncUserDetails.getLastName() != null) {
+ headersMap.put(HttpHeaderEnum.HTTP_CSP_LASTNAME.getValue(), sdncUserDetails.getLastName());
+ }
+ if (sdncUserDetails.getEmail() != null) {
+ headersMap.put(HttpHeaderEnum.HTTP_CSP_EMAIL.getValue(), sdncUserDetails.getEmail());
+ }
+
+ logger.debug("headersMap: {}", headersMap.toString());
+
+ Config config = Utils.getConfig();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.AUTHORIZE_USER, config.getCatalogBeHost(), config.getCatalogBePort());
+ logger.debug("Send GET request to login as seal user : {}", url);
+ return http.httpSendGet(url, headersMap);
+ }
+
+ public static RestResponse authorizedUserTowardsCatalogBeQA(User sdncUserDetails) throws IOException {
+
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ if (sdncUserDetails.getFirstName() != null) {
+ headersMap.put(HttpHeaderEnum.HTTP_CSP_FIRSTNAME.getValue(), sdncUserDetails.getFirstName());
+ }
+ if (sdncUserDetails.getLastName() != null) {
+ headersMap.put(HttpHeaderEnum.HTTP_CSP_LASTNAME.getValue(), sdncUserDetails.getLastName());
+ }
+ if (sdncUserDetails.getEmail() != null) {
+ headersMap.put(HttpHeaderEnum.HTTP_CSP_EMAIL.getValue(), sdncUserDetails.getEmail());
+ }
+
+ logger.debug("headersMap: {}", headersMap.toString());
+
+ Config config = Utils.getConfig();
+ HttpRequest http = new HttpRequest();
+ String url = String.format(Urls.AUTHORIZE_USER, config.getCatalogBeHost(), config.getCatalogBePort());
+ logger.debug("Send GET request to login as seal user : {}", url);
+ return http.httpSendGet(url, headersMap);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ArtifactValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ArtifactValidationUtils.java
new file mode 100644
index 0000000000..21b520c97a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ArtifactValidationUtils.java
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+public class ArtifactValidationUtils {
+
+ private static String desc = "description";
+ private static String artifactType = "artifactType";
+ private static String artifactName = "artifactName";
+ private static String artifactChecksum = "artifactChecksum";
+ private static String uniqueId = "uniqueId";
+ protected Utils utils;
+
+ public static void validateInformationalArtifact(ArtifactReqDetails expectedArtifact,
+ Map<String, Object> actualArtifact) {
+ assertTrue("description is not as expected",
+ expectedArtifact.getDescription().equals(actualArtifact.get(desc).toString()));
+ assertTrue("artifactType is not as expected",
+ expectedArtifact.getArtifactType().toUpperCase().equals(actualArtifact.get(artifactType).toString()));
+ assertTrue("artifactName is not as expected",
+ expectedArtifact.getArtifactName().equals(actualArtifact.get(artifactName).toString()));
+ assertTrue("uniqueId is not as expected",
+ expectedArtifact.getUniqueId().equals(actualArtifact.get(uniqueId).toString()));
+ assertTrue("description is not as expected", expectedArtifact.getArtifactLabel().toLowerCase()
+ .equals(actualArtifact.get("artifactLabel").toString()));
+ }
+
+ public static void validateArtifactsNumberInComponent(Component component, ArtifactGroupTypeEnum artifactGroupType,
+ ArtifactTypeEnum artifactType, int expectedNumber) {
+ Map<String, ArtifactDefinition> deploymentArtifacts;
+ int counter = 0;
+ if (artifactGroupType == ArtifactGroupTypeEnum.DEPLOYMENT) {
+ deploymentArtifacts = component.getDeploymentArtifacts();
+ } else {
+ deploymentArtifacts = component.getArtifacts();
+ }
+ if (deploymentArtifacts != null) {
+ for (ArtifactDefinition artifactDefinition : deploymentArtifacts.values()) {
+ if (artifactDefinition.getArtifactType().equals(artifactType.getType())) {
+ counter++;
+ }
+ }
+ }
+ assertEquals("Unexpected number of " + artifactGroupType.getType() + " artifacts in component", expectedNumber,
+ counter);
+ }
+
+ // Benny
+ public static void validateArtifactsNumberInComponentInstance(ComponentInstance componentInstance,
+ ArtifactGroupTypeEnum artifactGroupType, ArtifactTypeEnum artifactType, int expectedNumber) {
+ Map<String, ArtifactDefinition> deploymentArtifacts = null;
+ int counter = 0;
+ if (artifactGroupType == ArtifactGroupTypeEnum.DEPLOYMENT) {
+ deploymentArtifacts = componentInstance.getDeploymentArtifacts();
+ }
+ if (deploymentArtifacts != null) {
+ for (ArtifactDefinition artifactDefinition : deploymentArtifacts.values()) {
+ if (artifactDefinition.getArtifactType().equals(artifactType.getType())) {
+ counter++;
+ }
+ }
+ }
+ assertEquals("Unexpected number of " + artifactGroupType.getType() + " artifacts in component", expectedNumber,
+ counter);
+ }
+
+ public static ESArtifactData parseArtifactRespFromES(RestResponse resResponse)
+ throws JsonParseException, JsonProcessingException, Exception {
+ String bodyToParse = resResponse.getResponse();
+ JsonElement jElement = new JsonParser().parse(bodyToParse);
+ JsonElement jsourceElement = jElement.getAsJsonObject().get("_source");
+
+ ObjectMapper mapper = new ObjectMapper();
+ ESArtifactData esArtifactObject = mapper.readValue(jsourceElement.toString(), ESArtifactData.class);
+
+ // logger.debug("got artifact details from ElasticSearch as json");
+
+ return esArtifactObject;
+
+ }
+
+ public static void validateArtifactReqVsResp(ArtifactReqDetails expectedArtifactDetails,
+ ArtifactDefinition actualArtifactJavaObject) {
+ String expected;
+
+ expected = expectedArtifactDetails.getArtifactName();
+ if (expected == null)
+ expected = "";
+ assertEquals("artifact name is not correct ", expected, actualArtifactJavaObject.getArtifactName());
+
+ expected = expectedArtifactDetails.getArtifactType();
+ if (expected == null)
+ expected = "";
+ assertEquals("artifact type is not correct ", expected, actualArtifactJavaObject.getArtifactType());
+
+ expected = expectedArtifactDetails.getDescription();
+ if (expected == null)
+ expected = "";
+ assertEquals("artifact description is not correct ", expected, actualArtifactJavaObject.getDescription());
+
+ expected = expectedArtifactDetails.getArtifactLabel();
+ if (expected == null || expected == "") {
+ expected = expectedArtifactDetails.getArtifactName().toLowerCase().substring(0,
+ expectedArtifactDetails.getArtifactName().lastIndexOf("."));
+ // expected = tmp.substring(0,
+ // artifactInfo.getArtifactName().lastIndexOf("."));
+ }
+ assertEquals("artifact label is not correct ", expected, actualArtifactJavaObject.getArtifactLabel());
+
+ expected = expectedArtifactDetails.getUrl();
+ if (expected != "") {
+ assertEquals(expected, actualArtifactJavaObject.getApiUrl());
+ assertEquals(expectedArtifactDetails.getArtifactDisplayName(),
+ actualArtifactJavaObject.getArtifactDisplayName());
+ }
+
+ // assertEquals(validChecksum,
+ // actualArtifactJavaObject.getArtifactChecksum());
+
+ // expected = expectedArtifactDetails.getArtifactDisplayName();
+ // if (expected != "")
+ // {
+ // assertEquals(expected,
+ // actualArtifactJavaObject.getArtifactDisplayName());
+ // }
+
+ boolean actual = actualArtifactJavaObject.getMandatory();
+ assertEquals(expectedArtifactDetails.isMandatory(), actual);
+
+ if (actualArtifactJavaObject.getServiceApi()) {
+
+ boolean actual2 = actualArtifactJavaObject.getServiceApi();
+ assertEquals(expectedArtifactDetails.isServiceApi(), actual2);
+ }
+
+ }
+
+ public static void validateEsArtifactReqVsResp(ArtifactReqDetails expectedArtifactInfo,
+ ESArtifactData esArtifactData) throws Exception {
+ String expectedArtifactUid = expectedArtifactInfo.getUniqueId();
+ if (expectedArtifactUid == null)
+ expectedArtifactUid = "";
+ assertEquals("artifact name is not correct ", expectedArtifactUid, esArtifactData.getId());
+
+ String actualPayload = Decoder.encode(esArtifactData.getData().array());
+ assertEquals("artifact payloadData is not correct ", expectedArtifactInfo.getPayload(), actualPayload);
+ }
+
+ public static List<String> getListOfArtifactFromFolder(String folderName) throws IOException, Exception {
+ Config config = Utils.getConfig();
+ String sourceDir = config.getResourceConfigDir();
+ String testResourcesPath = sourceDir + File.separator + folderName;
+ List<String> listofFiles = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ return listofFiles;
+ }
+
+ public static ArtifactReqDetails replaceDefaultArtWithArtFromList_(ArtifactReqDetails heatArtifactDetails,
+ String heatExtension, String folderName, int positionInlist) throws IOException, Exception {
+
+ Config config = Utils.getConfig();
+ String ext = heatExtension;
+ String sourceDir = config.getResourceConfigDir();
+ String testResourcesPath = sourceDir + File.separator + folderName;
+ List<String> listFileName = FileUtils.getFileListFromBaseDirectoryByTestName(testResourcesPath);
+ // logger.debug("listFileName: {}", listFileName.get(positionInlist));
+ String payload = FileUtils.loadPayloadFile(listFileName, ext, true);
+ heatArtifactDetails.setPayload(payload);
+ heatArtifactDetails.setArtifactName(listFileName.get(positionInlist) + "." + ext);
+ return heatArtifactDetails;
+ }
+
+ public static ArtifactReqDetails replaceDefaultArtWithArtFromList(ArtifactReqDetails heatArtifactDetails,
+ String heatExtension, String folderName, int positionInlist) throws IOException, Exception {
+ List<String> listOfArtifactFromFolder = getListOfArtifactFromFolder(folderName);
+ // logger.debug("listFileName: {}", listOfArtifactFromFolder.get(positionInlist));
+ String payload = FileUtils.loadPayloadFileFromListUsingPosition(listOfArtifactFromFolder, heatExtension, true,
+ positionInlist);
+ heatArtifactDetails.setPayload(payload);
+ heatArtifactDetails.setArtifactName(heatArtifactDetails.getArtifactType()
+ + listOfArtifactFromFolder.get(positionInlist) + "." + heatExtension);
+ return heatArtifactDetails;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/AuditValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/AuditValidationUtils.java
new file mode 100644
index 0000000000..882498a63c
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/AuditValidationUtils.java
@@ -0,0 +1,1328 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.javatuples.Pair;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.AuditEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.AuditJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ComponentType;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedAuthenticationAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedCategoryAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedDistDownloadAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedEcomConsumerAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedExternalAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedGetUserListAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedProductAudit;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedUserCRUDAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.run.StartTest;
+import org.openecomp.sdc.ci.tests.utils.ArtifactUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.cassandra.CassandraUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils.CategoryAuditJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.utils.rest.ConsumerRestUtils.EcompConsumerAuditJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.ColumnDefinitions;
+import com.datastax.driver.core.Row;
+
+public class AuditValidationUtils {
+ protected static Logger logger = Logger.getLogger(AuditValidationUtils.class.getName());
+ private static final String auditKeySpaceName = "sdcaudit";
+
+ public AuditValidationUtils() {
+ super();
+
+ StartTest.enableLogger();
+ logger = Logger.getLogger(AuditValidationUtils.class.getName());
+
+ }
+
+ public static String buildAuditDescription(ErrorInfo errorInfo, List<String> variables) {
+
+ String auditDesc = errorInfo.getMessageId() + ": " + errorInfo.getMessage();
+ for (int i = 0; i < variables.size(); i++) {
+ if (auditDesc.contains("%" + (i + 1))) {
+ auditDesc = auditDesc.replace("%" + (i + 1), variables.get(i));
+ }
+ }
+
+ // logger.debug("audit description - {}", auditDesc);
+ return auditDesc;
+ }
+
+ public static String getModifierString(String userName, String uid) {
+
+ if (userName.isEmpty() && uid.isEmpty())
+ return "(UNKNOWN)";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(userName).append("(").append(uid).append(")");
+ return sb.toString();
+
+ }
+
+ public static void validateAuditDownloadExternalAPI(ExpectedResourceAuditJavaObject resourceAuditJavaObject,
+ String action, String body, boolean checkAllFields) throws Exception {
+ Map<String, Object> actualAuditRecords = new HashMap<String, Object>();
+ // Andrey's comment
+ // actualAuditRecords = parseAuditResourceByAction(action, body);
+ actualAuditRecords = parseAuditResourceByAction(action, null);
+
+ // List<Map<String, Object>> actualAuditRecords = new
+ // ArrayList<Map<String, Object>>();
+ // actualAuditRecords = parseAuditResourceByActionToList(action, body);
+
+ validateField(actualAuditRecords, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(actualAuditRecords, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+
+ validateField(actualAuditRecords, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getStatus());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getDesc());
+
+ // validateField(actualAuditRecords,
+ // AuditJsonKeysEnum.CONSUMER_ID.getAuditJsonKeyName(),
+ // resourceAuditJavaObject.getCONSUMER_ID());
+ // validateField(actualAuditRecords,
+ // AuditJsonKeysEnum.RESOURCE_URL.getAuditJsonKeyName(),
+ // resourceAuditJavaObject.getRESOURCE_URL());
+
+ }
+
+ public static void validateAudit(ExpectedResourceAuditJavaObject resourceAuditJavaObject, String action,
+ String body, boolean checkAllFields) throws Exception {
+ Map<String, Object> actualAuditRecords = new HashMap<String, Object>();
+ // Andrey's comment
+ // actualAuditRecords = parseAuditResourceByAction(action, body);
+ actualAuditRecords = parseAuditResourceByAction(action, null);
+
+ if ((resourceAuditJavaObject.getModifierName() != null) && (resourceAuditJavaObject.getModifierUid() != null)) {
+ resourceAuditJavaObject.setModifierUid(getModifierString(resourceAuditJavaObject.getModifierName(),
+ resourceAuditJavaObject.getModifierUid()));
+ }
+
+ validateField(actualAuditRecords, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(actualAuditRecords, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.PREV_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getPrevVersion());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+
+ validateField(actualAuditRecords, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getModifierUid());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.PREV_STATE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getPrevState());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrState());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getStatus());
+ // validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ // Double.parseDouble(resourceAuditJavaObject.getStatus()));
+ validateField(actualAuditRecords, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getDesc());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.COMMENT.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getComment());
+ // validateField(map2,
+ // AuditJsonKeysEnum.ARTIFACT_DATA.getAuditJsonKeyName(),
+ // resourceAuditJavaObject.getArtifactData());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.TOSCA_NODE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getToscaNodeType());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.CURR_ARTIFACT_UUID.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrArtifactUuid());
+ validateField(actualAuditRecords, AuditJsonKeysEnum.PREV_ARTIFACT_UUID.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getPrevArtifactUuid());
+
+ validateAtifactDataField(actualAuditRecords, AuditJsonKeysEnum.ARTIFACT_DATA.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getArtifactData(), checkAllFields);
+ }
+
+ public static void validateExternalAudit(ExpectedExternalAudit externalAuditObject, String action,
+ Map<AuditingFieldsKeysEnum, String> body) throws Exception {
+
+ Map<String, Object> actualAuditRecord = new HashMap<String, Object>();
+ actualAuditRecord = parseAuditResourceByAction(action, body);
+
+ validateField(actualAuditRecord, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(actualAuditRecord, AuditJsonKeysEnum.CONSUMER_ID.getAuditJsonKeyName(),
+ externalAuditObject.getCONSUMER_ID());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.RESOURCE_URL.getAuditJsonKeyName(),
+ externalAuditObject.getRESOURCE_URL());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ externalAuditObject.getRESOURCE_NAME());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.SERVICE_INSTANCE_ID.getAuditJsonKeyName(),
+ externalAuditObject.getSERVICE_INSTANCE_ID());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ externalAuditObject.getRESOURCE_TYPE());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ externalAuditObject.getSTATUS());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(),
+ externalAuditObject.getDESC());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ externalAuditObject.getMODIFIER());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.PREV_ARTIFACT_UUID.getAuditJsonKeyName(),
+ externalAuditObject.getPREV_ARTIFACT_UUID());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.CURR_ARTIFACT_UUID.getAuditJsonKeyName(),
+ externalAuditObject.getCURR_ARTIFACT_UUID());
+ validateField(actualAuditRecord, AuditJsonKeysEnum.ARTIFACT_DATA.getAuditJsonKeyName(),
+ externalAuditObject.getARTIFACT_DATA());
+
+ }
+
+ public enum ArtifactDataFieldEnum {
+ attGroup, artLable, artType, artName, artTimeout, artPayloadUUID, artVersion, artUUID
+ }
+
+ private static void validateAtifactDataField(Map<String, Object> map, String auditJsonKeyName,
+ String expectedArtifactData, boolean checkAllFields) {
+ Map<ArtifactDataFieldEnum, String> expectedArtifactDataFileds = new HashMap<ArtifactDataFieldEnum, String>();
+ Map<ArtifactDataFieldEnum, String> actualAtifactDataFileds = new HashMap<ArtifactDataFieldEnum, String>();
+ if (expectedArtifactData != null) {
+ String[] expected = expectedArtifactData.split(",");
+
+ assertTrue("Audit field " + auditJsonKeyName + " not found", map.containsKey(auditJsonKeyName));
+ String actualValue = (String) map.get(auditJsonKeyName);
+ String[] actual = actualValue.split(",");
+
+ if (expected.length == 1 && actual.length == 1) {
+ assertEquals(expectedArtifactData, actualValue);
+ return;
+ }
+
+ assertEquals(ArtifactDataFieldEnum.values().length, expected.length);
+ assertEquals(ArtifactDataFieldEnum.values().length, actual.length);
+
+ for (ArtifactDataFieldEnum field : ArtifactDataFieldEnum.values()) {
+
+ expectedArtifactDataFileds.put(field, expected[field.ordinal()]);
+ actualAtifactDataFileds.put(field, actual[field.ordinal()]);
+ }
+ for (Map.Entry<ArtifactDataFieldEnum, String> entry : expectedArtifactDataFileds.entrySet()) {
+ ArtifactDataFieldEnum field = entry.getKey();
+ if (checkAllFields || (!field.equals(ArtifactDataFieldEnum.artVersion)
+ && !field.equals(ArtifactDataFieldEnum.artUUID))) {
+ assertTrue("Audit field ArtifactData dosn't containt " + field,
+ actualAtifactDataFileds.containsKey(field));
+ assertEquals("Audit field ArtifactData dosn't equal " + field,
+ expectedArtifactDataFileds.get(field), actualAtifactDataFileds.get(field));
+ }
+
+ }
+ }
+ }
+
+ // //Benny
+ public static void validateEcompConsumerAudit(ExpectedEcomConsumerAudit ecompConsumerAuditJavaObject, String action)
+ throws Exception {
+
+ String fixedAction = BaseRestUtils.encodeUrlForDownload(action);
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(fixedAction, null);
+
+ validateField(map2, EcompConsumerAuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ ecompConsumerAuditJavaObject.getModifier());
+ validateField(map2, EcompConsumerAuditJsonKeysEnum.ECOMP_USER.getAuditJsonKeyName(),
+ ecompConsumerAuditJavaObject.getEcomUser());
+ validateField(map2, EcompConsumerAuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ ecompConsumerAuditJavaObject.getStatus());
+ validateField(map2, EcompConsumerAuditJsonKeysEnum.DESC.getAuditJsonKeyName(),
+ ecompConsumerAuditJavaObject.getDesc());
+ validateField(map2, EcompConsumerAuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ }
+
+ public static void ecompConsumerAuditSuccess(String action, ConsumerDataDefinition consumerDataDefinition,
+ User user, int status) throws Exception {
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(action);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(
+ consumerDataDefinition.getConsumerName() + "," + consumerDataDefinition.getConsumerSalt().toLowerCase()
+ + "," + consumerDataDefinition.getConsumerPassword().toLowerCase());
+ expectedEcomConsumerAuditJavaObject.setStatus(String.valueOf(status));
+ expectedEcomConsumerAuditJavaObject.setDesc("OK");
+ expectedEcomConsumerAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject, action);
+ }
+
+ public static void createEcompConsumerAuditFailure(String action, ConsumerDataDefinition consumerDataDefinition,
+ User user, ActionStatus errorMessage, Object... variables) throws Exception {
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorMessage.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ expectedEcomConsumerAuditJavaObject.setAction(action);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(
+ consumerDataDefinition.getConsumerName() + "," + consumerDataDefinition.getConsumerSalt().toLowerCase()
+ + "," + consumerDataDefinition.getConsumerPassword().toLowerCase());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ expectedEcomConsumerAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject, action);
+ }
+
+ public static void deleteEcompConsumerAuditFailure(String action, ConsumerDataDefinition consumerDataDefinition,
+ User user, ActionStatus errorMessage, Object... variables) throws Exception {
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorMessage.name());
+ ExpectedEcomConsumerAudit expectedEcomConsumerAuditJavaObject = new ExpectedEcomConsumerAudit();
+ // String auditAction = ADD_ECOMP_USER_CREDENTIALS_AUDIT_ACTION;
+ expectedEcomConsumerAuditJavaObject.setAction(action);
+ expectedEcomConsumerAuditJavaObject.setEcomUser(consumerDataDefinition.getConsumerName());
+ expectedEcomConsumerAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ expectedEcomConsumerAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ expectedEcomConsumerAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ AuditValidationUtils.validateEcompConsumerAudit(expectedEcomConsumerAuditJavaObject, action);
+ }
+
+ ////////////////////// US571255
+ public static void GetListOfUsersByRolesAuditFailure(String action, String roles, int status, User userModifier,
+ ActionStatus errorMessage, Object... variables) throws Exception {
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorMessage.name());
+ ExpectedGetUserListAudit expectedGetListOfUsersAuditJavaObject = new ExpectedGetUserListAudit(); // String
+ // auditAction
+ // =
+ // ADD_ECOMP_USER_CREDENTIALS_AUDIT_ACTION;
+ expectedGetListOfUsersAuditJavaObject.setAction(action);
+ expectedGetListOfUsersAuditJavaObject.setStatus(String.valueOf(status));
+ expectedGetListOfUsersAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+
+ expectedGetListOfUsersAuditJavaObject.setDetails(roles);
+ if (errorMessage == ActionStatus.USER_INACTIVE || errorMessage == ActionStatus.MISSING_INFORMATION) {
+ expectedGetListOfUsersAuditJavaObject.setModifier("(UNKNOWN)");
+ } else {
+ expectedGetListOfUsersAuditJavaObject
+ .setModifier(userModifier.getFullName() + "(" + userModifier.getUserId() + ")");
+ }
+ AuditValidationUtils.validateAuditGetListOfUsersByRoles(expectedGetListOfUsersAuditJavaObject, action);
+ }
+
+ public static void GetListOfUsersByRolesAuditSuccess(String action, String roles, User user, int status)
+ throws Exception {
+ ExpectedGetUserListAudit expectedGetListOfUsersAuditJavaObject = new ExpectedGetUserListAudit();
+ expectedGetListOfUsersAuditJavaObject.setAction(action);
+ expectedGetListOfUsersAuditJavaObject.setStatus(String.valueOf(status));
+ expectedGetListOfUsersAuditJavaObject.setDesc("OK");
+ expectedGetListOfUsersAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ expectedGetListOfUsersAuditJavaObject.setDetails(roles);
+ validateAuditGetListOfUsersByRoles(expectedGetListOfUsersAuditJavaObject, action);
+ }
+
+ public static void validateAuditGetListOfUsersByRoles(ExpectedGetUserListAudit GetListOfUsersAuditJavaObject,
+ String action) throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), GetListOfUsersAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ GetListOfUsersAuditJavaObject.getModifier());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(),
+ GetListOfUsersAuditJavaObject.getDesc());
+ validateField(map2, AuditJsonKeysEnum.DETAILS.getAuditJsonKeyName(),
+ GetListOfUsersAuditJavaObject.getDetails());
+ }
+
+ public static void validateAuditImport(ExpectedResourceAuditJavaObject resourceAuditJavaObject, String action)
+ throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+
+ resourceAuditJavaObject.setModifierUid(
+ getModifierString(resourceAuditJavaObject.getModifierName(), resourceAuditJavaObject.getModifierUid()));
+
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(map2, AuditJsonKeysEnum.PREV_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getPrevVersion());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(), resourceAuditJavaObject.getModifierUid());
+ validateField(map2, AuditJsonKeysEnum.PREV_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getPrevState());
+ validateField(map2, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getCurrState());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), resourceAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), resourceAuditJavaObject.getDesc());
+
+ }
+
+ public static void validateAuditDistribution(ExpectedResourceAuditJavaObject resourceAuditJavaObject, String action)
+ throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+
+ resourceAuditJavaObject.setModifierUid(
+ getModifierString(resourceAuditJavaObject.getModifierName(), resourceAuditJavaObject.getModifierUid()));
+
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(), resourceAuditJavaObject.getModifierUid());
+ validateField(map2, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getCurrState());
+ validateField(map2, AuditJsonKeysEnum.DPREV_STATUS.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getDprevStatus());
+ validateField(map2, AuditJsonKeysEnum.DCURR_STATUS.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getDcurrStatus());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), resourceAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), resourceAuditJavaObject.getDesc());
+ validateField(map2, AuditJsonKeysEnum.COMMENT.getAuditJsonKeyName(), resourceAuditJavaObject.getComment());
+ validateField(map2, AuditJsonKeysEnum.DID.getAuditJsonKeyName(), resourceAuditJavaObject.getDistributionId());
+
+ }
+
+ // Benny
+ public static void validateAudit_Distribution(ExpectedResourceAuditJavaObject resourceAuditJavaObject,
+ String action) throws Exception {
+
+ List<Map<String, Object>> actionToList = getAuditListByAction(resourceAuditJavaObject.getAction(), 1);
+ Map<String, Object> map2 = actionToList.get(0);
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(), resourceAuditJavaObject.getMODIFIER());
+ validateField(map2, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getCurrState());
+ validateField(map2, AuditJsonKeysEnum.DPREV_STATUS.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getDprevStatus());
+ validateField(map2, AuditJsonKeysEnum.DCURR_STATUS.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getDcurrStatus());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), resourceAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), resourceAuditJavaObject.getDesc());
+ validateField(map2, AuditJsonKeysEnum.COMMENT.getAuditJsonKeyName(), resourceAuditJavaObject.getComment());
+
+ }
+
+ public void validateAuditNotification(ExpectedResourceAuditJavaObject resourceAuditJavaObject, String action)
+ throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+
+ resourceAuditJavaObject.setModifierUid(
+ getModifierString(resourceAuditJavaObject.getModifierName(), resourceAuditJavaObject.getModifierUid()));
+
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+ validateField(map2, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getCurrState());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), resourceAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), resourceAuditJavaObject.getDesc());
+ validateField(map2, AuditJsonKeysEnum.DID.getAuditJsonKeyName(), resourceAuditJavaObject.getDistributionId());
+ validateField(map2, AuditJsonKeysEnum.TOPIC_NAME.getAuditJsonKeyName(), resourceAuditJavaObject.getTopicName());
+
+ }
+
+ public static void validateAudit(ExpectedDistDownloadAudit expectedDistDownloadAudit, String action)
+ throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), expectedDistDownloadAudit.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), expectedDistDownloadAudit.getDesc());
+ validateField(map2, AuditJsonKeysEnum.CONSUMER_ID.getAuditJsonKeyName(),
+ expectedDistDownloadAudit.getConsumerId());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_URL.getAuditJsonKeyName(),
+ expectedDistDownloadAudit.getResourceUrl());
+ }
+
+ public void validateAuditDeploy(ExpectedResourceAuditJavaObject resourceAuditJavaObject, String action)
+ throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+
+ resourceAuditJavaObject.setModifierUid(
+ getModifierString(resourceAuditJavaObject.getModifierName(), resourceAuditJavaObject.getModifierUid()));
+
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(), resourceAuditJavaObject.getModifierUid());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), resourceAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), resourceAuditJavaObject.getDesc());
+ validateField(map2, AuditJsonKeysEnum.DID.getAuditJsonKeyName(), resourceAuditJavaObject.getDistributionId());
+
+ }
+
+ public static void validateAuditProduct(ExpectedProductAudit productExpectedAudit, String action,
+ AuditJsonKeysEnum... additionalFields) throws Exception {
+
+ Map<String, Object> map2 = new HashMap<String, Object>();
+ map2 = parseAuditResourceByAction(action, null);
+
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ productExpectedAudit.getRESOURCE_NAME());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ productExpectedAudit.getRESOURCE_TYPE());
+ validateField(map2, AuditJsonKeysEnum.PREV_VERSION.getAuditJsonKeyName(),
+ productExpectedAudit.getPREV_VERSION());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ productExpectedAudit.getCURR_VERSION());
+ validateField(map2, AuditJsonKeysEnum.PREV_STATE.getAuditJsonKeyName(), productExpectedAudit.getPREV_STATE());
+ validateField(map2, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(), productExpectedAudit.getCURR_STATE());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(), productExpectedAudit.getMODIFIER());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), productExpectedAudit.getSTATUS());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), productExpectedAudit.getDESC());
+ validateField(map2, AuditJsonKeysEnum.SERVICE_INSTANCE_ID.getAuditJsonKeyName(),
+ productExpectedAudit.getSERVICE_INSTANCE_ID());
+ if (additionalFields != null) {
+ List<AuditJsonKeysEnum> fieldsList = Arrays.asList(additionalFields);
+ if (fieldsList.contains(AuditJsonKeysEnum.COMMENT)) {
+ validateField(map2, AuditJsonKeysEnum.COMMENT.getAuditJsonKeyName(), productExpectedAudit.getCOMMENT());
+ }
+ }
+ }
+
+ private static List<Map<String, Object>> getAuditListByAction(String action, int expectedNumOfAudit)
+ throws Exception {
+ List<Map<String, Object>> actionToList = parseAuditResourceByActionToList(action, null);
+ assertEquals("recieved different audits number than expected", expectedNumOfAudit, actionToList.size());
+ return actionToList;
+ }
+
+ public static void validateAuthenticationAudit(ExpectedAuthenticationAudit expectedAudit) throws Exception {
+ List<Map<String, Object>> actionToList = getAuditListByAction(expectedAudit.getAction(), 1);
+ assertEquals("expected number of ES action is 1", 1, actionToList.size());
+
+ Map<String, Object> map = actionToList.get(0);
+ validateField(map, AuditEnum.ACTION.getValue(), expectedAudit.getAction());
+ validateField(map, AuditEnum.URL.getValue(), expectedAudit.getUrl());
+ validateField(map, AuditEnum.USER.getValue(), expectedAudit.getUser());
+ validateField(map, AuditEnum.AUTH_STATUS.getValue(), expectedAudit.getAuthStatus());
+ validateField(map, AuditEnum.REALM.getValue(), expectedAudit.getRealm());
+
+ }
+
+ private static void validateField(Map<String, Object> actualAuditRecord, String jsonField, Object expectedValue) {
+ if (expectedValue == null) {
+ // || changed to &&
+ if (actualAuditRecord.containsKey(jsonField)) {
+ assertTrue("Audit field " + jsonField + ": expected null, actual " + actualAuditRecord.get(jsonField),
+ actualAuditRecord.get(jsonField).toString().equals("null")
+ || actualAuditRecord.get(jsonField).toString().equals(Constants.EMPTY_STRING));
+ }
+
+ } else {
+ assertTrue("Audit field " + jsonField + " not found in actual", actualAuditRecord.containsKey(jsonField));
+ Object foundValue = actualAuditRecord.get(jsonField);
+ compareElements(expectedValue, foundValue);
+ }
+ }
+
+ private static void compareElements(Object expectedValue, Object foundValue) {
+ if (expectedValue instanceof String) {
+ assertTrue("Actual value " + foundValue + " is not string", foundValue instanceof String);
+ assertTrue("Expected " + expectedValue + " not equal to actual " + foundValue,
+ foundValue.equals(expectedValue));
+ }
+ /*
+ * else if( expectedValue instanceof Number){ assertTrue(foundValue
+ * instanceof Number); assertTrue(foundValue == expectedValue); }
+ */
+ else if (expectedValue instanceof Boolean) {
+ assertTrue(foundValue instanceof Boolean);
+ assertTrue(foundValue == expectedValue);
+ } else if (expectedValue instanceof Map) {
+ assertTrue(foundValue instanceof Map);
+ Map<String, Object> foundMap = (Map<String, Object>) foundValue;
+ Map<String, Object> excpectedMap = (Map<String, Object>) expectedValue;
+ assertTrue(foundMap.size() == excpectedMap.size());
+ Iterator<String> foundkeyItr = foundMap.keySet().iterator();
+ while (foundkeyItr.hasNext()) {
+ String foundKey = foundkeyItr.next();
+ assertTrue(excpectedMap.containsKey(foundKey));
+ compareElements(excpectedMap.get(foundKey), foundMap.get(foundKey));
+ }
+
+ } else if (expectedValue instanceof List) {
+ assertTrue(foundValue instanceof List);
+ List<Object> foundList = (List<Object>) foundValue;
+ List<Object> excpectedList = (List<Object>) expectedValue;
+ assertTrue(foundList.size() == excpectedList.size());
+ for (int i = 0; i < foundList.size(); i++) {
+ compareElements(excpectedList.get(i), foundList.get(i));
+ }
+
+ } else {
+ assertTrue(foundValue.equals(expectedValue));
+ }
+ }
+
+ // public static Map<String, Object> parseAuditResourceByAction(String
+ // action, String body) throws Exception {
+ //
+ // Map auditingMessage = null;
+ // auditingMessage = retrieveAuditMessagesByPattern(action, null);
+ //
+ // return auditingMessage;
+ //
+ // }
+
+ public static Map<String, Object> parseAuditResourceByAction(String action,
+ Map<AuditingFieldsKeysEnum, String> body) throws Exception {
+
+ Map auditingMessage = null;
+ auditingMessage = retrieveAuditMessagesByPattern(action, body);
+
+ return auditingMessage;
+
+ }
+
+ // public static List<Map<String, Object>>
+ // parseAuditResourceByActionToList(String action, String body) throws
+ // Exception {
+ //
+ // List<Map<String, Object>> auditList = new ArrayList<Map<String,
+ // Object>>();
+ //
+ //// String auditingMessage = null;
+ //
+ // Map auditingMessage = null;
+ // auditingMessage = retrieveAuditMessagesByPattern(action);
+ //
+ // if (body == null) {
+ //// String pattern = "/_search?q=ACTION:\"" + action + "\"";
+ //// auditingMessage = retrieveAuditMessagesByPattern(action);
+ //// auditingMessage = retrieveAuditMessagesByPattern(pattern);
+ // } else {
+ //// auditingMessage = retrieveAuditMessagesUsingBody(body);
+ // }
+ //
+ // return ResponseParser.getAuditFromMessage(auditingMessage);
+ //
+ // }
+
+ public static List<Map<String, Object>> parseAuditResourceByActionToList(String action,
+ Map<AuditingFieldsKeysEnum, String> body) throws Exception {
+
+ List<Map<String, Object>> auditList = new ArrayList<Map<String, Object>>();
+
+ // String auditingMessage = null;
+
+ Map auditingMessage = null;
+
+ if (body == null || body.isEmpty()) {
+ auditingMessage = retrieveAuditMessagesByPattern(action, null);
+ // String pattern = "/_search?q=ACTION:\"" + action + "\"";
+ // auditingMessage = retrieveAuditMessagesByPattern(action);
+ // auditingMessage = retrieveAuditMessagesByPattern(pattern);
+ } else {
+ auditingMessage = retrieveAuditMessagesByPattern(action, body);
+ // auditingMessage = retrieveAuditMessagesUsingBody(body);
+ }
+
+ return ResponseParser.getAuditFromMessage(auditingMessage);
+
+ }
+
+ public JSONObject buildElasticQueryStringObject(String defaultField, String queryValue) throws JSONException {
+
+ JSONObject query_string = new JSONObject();
+ JSONObject jSONObject = new JSONObject();
+ jSONObject.put("default_field", defaultField);
+ jSONObject.put("query", queryValue);
+
+ query_string.put("query_string", jSONObject);
+
+ return query_string;
+ }
+
+ public static JSONObject buildElasticQueryBody(List<JSONObject> listObjects) throws JSONException {
+
+ JSONObject query = new JSONObject();
+ JSONObject bool = new JSONObject();
+ JSONObject must = new JSONObject();
+ JSONArray mustA = new JSONArray();
+
+ for (int i = 0; i < listObjects.size(); i++) {
+ JSONObject match = new JSONObject();
+ match.put("match", listObjects.get(i));
+ mustA.put(match);
+
+ }
+
+ must.put("must", mustA);
+ bool.put("bool", must);
+ query.put("query", bool);
+
+ return query;
+ }
+
+ public static String retrieveAuditMessagesUsingBody(String query_string) throws IOException {
+
+ Config config = Utils.getConfig();
+ HttpRequest getAuditingMessage = new HttpRequest();
+ Map<String, String> headersMap = new HashMap<String, String>();
+ String body = query_string;
+
+ String url = String.format(Urls.GET_SEARCH_DATA_FROM_ES, config.getEsHost(), config.getEsPort(), "_search");
+ RestResponse restResponse = getAuditingMessage.httpSendPost(url, body, headersMap);
+
+ return restResponse.getResponse();
+ }
+
+ public static Map retrieveAuditMessagesByPattern(String action, Map<AuditingFieldsKeysEnum, String> body)
+ throws IOException {
+
+ // get cassandra table name by action
+ String esType = AuditingActionEnum.getActionByName(action).getAuditingEsType();
+
+ List<Pair<AuditingFieldsKeysEnum, String>> myFields = new ArrayList<Pair<AuditingFieldsKeysEnum, String>>();
+ Pair<AuditingFieldsKeysEnum, String> myPair = new Pair<AuditingFieldsKeysEnum, String>(
+ AuditingFieldsKeysEnum.AUDIT_ACTION, action);
+ myFields.add(0, myPair);
+ if (body != null && !body.isEmpty()) {
+ for (Map.Entry<AuditingFieldsKeysEnum, String> mapElement : body.entrySet()) {
+ myFields.add(new Pair<AuditingFieldsKeysEnum, String>(mapElement.getKey(), mapElement.getValue()));
+ }
+ }
+
+ List<Row> fetchFromTable = CassandraUtils.fetchFromTable(auditKeySpaceName, esType, myFields);
+ assertTrue("expected on fetching from data base one record only, actual: " + fetchFromTable.size(),
+ fetchFromTable.size() == 1);
+ Row row = fetchFromTable.get(0);
+
+ ColumnDefinitions columnDefinitions = row.getColumnDefinitions();
+
+ Map<String, String> resultsMap = new HashMap<String, String>();
+
+ for (int i = 0; i < columnDefinitions.size(); i++) {
+ resultsMap.put(columnDefinitions.getName(i), row.getObject(columnDefinitions.getName(i)) == null ? "null"
+ : row.getObject(columnDefinitions.getName(i)).toString());
+ }
+
+ return resultsMap;
+ }
+
+ // public static Map retrieveAuditMessagesByPattern(String pattern) throws
+ // IOException {
+ //
+ //// Config config = Utils.getConfig();
+ //// HttpRequest getAuditingMessage = new HttpRequest();
+ //// String url = String.format(Urls.GET_SEARCH_DATA_FROM_ES,
+ // config.getEsHost(), config.getEsPort(), pattern);
+ //// RestResponse restResponse = getAuditingMessage.httpSendGet(url, null);
+ //
+ //// get cassandra table name by action
+ // String esType =
+ // AuditingActionEnum.getActionByName(pattern).getAuditingEsType();
+ //// AuditingActionEnum actionByName =
+ // AuditingActionEnum.getActionByName(pattern);
+ //
+ //// Map<AuditingFieldsKeysEnum, String> myFields= new
+ // HashMap<AuditingFieldsKeysEnum, String>();
+ //// myFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION , pattern);
+ //
+ // List<Pair<AuditingFieldsKeysEnum, String>> myFields = new
+ // ArrayList<Pair<AuditingFieldsKeysEnum, String>>();
+ // Pair<AuditingFieldsKeysEnum, String> myPair = new
+ // Pair<AuditingFieldsKeysEnum, String>(AuditingFieldsKeysEnum.AUDIT_ACTION
+ // , pattern);
+ // myFields.add(0, myPair);
+ //
+ //
+ // List<Row> fetchFromTable = CassandraUtils.fetchFromTable("sdcaudit",
+ // esType, myFields);
+ // Row row = fetchFromTable.get(0);
+ //
+ //
+ // ColumnDefinitions columnDefinitions = row.getColumnDefinitions();
+ //// String string = row.getString(columnDefinitions.getName(1));
+ //
+ //// String metaData = row.getColumnDefinitions().toString();
+ //// metaData =metaData.replaceAll("\\((.*?)\\)|\\[|\\]|Columns", "");
+ //// List<String> metaDataList = new
+ // ArrayList<String>(Arrays.asList(metaData.split(", ")));
+ //
+ //
+ //
+ // Map<String, String> resultsMap = new HashMap<String, String>();
+ //
+ //
+ // for (int i=0 ; i < columnDefinitions.size() ; i++){
+ // resultsMap.put(columnDefinitions.getName(i) ,
+ // row.getObject(columnDefinitions.getName(i)) == null ? "null" :
+ // row.getObject(columnDefinitions.getName(i)).toString());
+ // }
+ //// for (String string : metaDataList) {
+ //// resultsMap.put(string , row.getString(string));
+ //// }
+ ////
+ //
+ //// String dataString = fetchFromTable.toString();
+ //// dataString = dataString.replaceAll("\\[|\\]|Row", "");
+ //// List<String> dataArray = new
+ // ArrayList<String>(Arrays.asList(dataString.split(", ")));
+ ////
+ ////
+ //// Map<String, String> resultsMap = new HashMap<String, String>();
+ //// for (int i=0 ; i<metaDataList.size() ; i++) {
+ //// resultsMap.put(metaDataList.get(i), dataArray.get(i));
+ //// }
+ ////
+ //// return restResponse.getResponse();
+ // return resultsMap;
+ // }
+
+ public static void categoryAuditSuccess(String action, CategoryDefinition categoryDefinition, User user, int status,
+ String resourceType) throws Exception {
+ categoryAuditSuccessInternal(action, categoryDefinition, null, null, user, status, resourceType);
+ }
+
+ public static void categoryAuditFailure(String action, CategoryDefinition categoryDataDefinition, User user,
+ ActionStatus errorMessage, int status, String resourceType, Object... variables) throws Exception {
+ categoryAuditFailureInternal(action, categoryDataDefinition, null, null, user, errorMessage, status,
+ resourceType, variables);
+ }
+
+ public static void subCategoryAuditSuccess(String action, CategoryDefinition categoryDefinition,
+ SubCategoryDefinition subCategoryDefinition, User user, int status, String resourceType) throws Exception {
+ categoryAuditSuccessInternal(action, categoryDefinition, subCategoryDefinition, null, user, status,
+ resourceType);
+ }
+
+ public static void groupingAuditSuccess(String action, CategoryDefinition categoryDefinition,
+ SubCategoryDefinition subCategoryDefinition, GroupingDefinition groupingDefinition, User user, int status,
+ String resourceType) throws Exception {
+ categoryAuditSuccessInternal(action, categoryDefinition, subCategoryDefinition, groupingDefinition, user,
+ status, resourceType);
+ }
+
+ public static void subCategoryAuditFailure(String action, CategoryDefinition categoryDataDefinition,
+ SubCategoryDefinition subCategoryDefinition, User user, ActionStatus errorMessage, int status,
+ String resourceType, Object... variables) throws Exception {
+ categoryAuditFailureInternal(action, categoryDataDefinition, subCategoryDefinition, null, user, errorMessage,
+ status, resourceType, variables);
+ }
+
+ // NEW Benny
+ public static void groupingAuditFailure(String action, CategoryDefinition categoryDefinition,
+ SubCategoryDefinition subCategoryDefinition, GroupingDefinition groupingDefinition, User user,
+ ActionStatus errorMessage, int status, String resourceType, Object... variables) throws Exception {
+ groupingAuditFailureInternal(action, categoryDefinition, subCategoryDefinition, groupingDefinition, user,
+ errorMessage, status, resourceType, variables);
+ }
+
+ private static void groupingAuditFailureInternal(String action, CategoryDefinition categoryDataDefinition,
+ SubCategoryDefinition subCategoryDefinition, GroupingDefinition groupingDefinition, User user,
+ ActionStatus errorMessage, int status, String resourceType, Object... variables) throws Exception {
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorMessage.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(action);
+ expectedCatrgoryAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDataDefinition.getName());
+ String subCategoryName = (subCategoryDefinition != null ? subCategoryDefinition.getName()
+ : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(subCategoryName);
+ String groupingName = (groupingDefinition != null ? groupingDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setGroupingName(groupingName);
+ expectedCatrgoryAuditJavaObject.setResourceType(resourceType);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(status));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, action);
+ }
+
+ ///
+ private static void categoryAuditSuccessInternal(String action, CategoryDefinition categoryDefinition,
+ SubCategoryDefinition subCategoryDefinition, GroupingDefinition groupingDefinition, User user, int status,
+ String resourceType) throws Exception {
+ // resourceType = Service/Resource/Product
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(action);
+ expectedCatrgoryAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDefinition.getName());
+ String subCategoryName = (subCategoryDefinition != null ? subCategoryDefinition.getName()
+ : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(subCategoryName);
+ String groupingName = (groupingDefinition != null ? groupingDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setGroupingName(groupingName);
+ expectedCatrgoryAuditJavaObject.setResourceType(resourceType);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(status));
+ expectedCatrgoryAuditJavaObject.setDesc("OK");
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, action);
+ }
+
+ ///////////////////////////
+ ///// BENNNNNNNNY
+ public enum UserAuditJsonKeysEnum {
+ ACTION("ACTION"), MODIFIER("MODIFIER"), STATUS("STATUS"), DESC("DESCRIPTION"), USER_AFTER(
+ "USER_AFTER"), USER_BEFORE("USER_BEFORE");
+ private String auditJsonKeyName;
+
+ private UserAuditJsonKeysEnum(String auditJsonKeyName) {
+ this.auditJsonKeyName = auditJsonKeyName;
+ }
+
+ public String getAuditJsonKeyName() {
+ return auditJsonKeyName.toLowerCase();
+ }
+ }
+
+ public static void validateAddUserAudit(ExpectedUserCRUDAudit expectedAddUserAuditJavaObject, String action)
+ throws Exception {
+
+ List<Map<String, Object>> actionToList = getAuditListByAction(expectedAddUserAuditJavaObject.getAction(), 1);
+ Map<String, Object> map = actionToList.get(0);
+ validateField(map, UserAuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map, UserAuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ expectedAddUserAuditJavaObject.getModifier());
+ validateField(map, UserAuditJsonKeysEnum.USER_AFTER.getAuditJsonKeyName(),
+ expectedAddUserAuditJavaObject.getUserAfter());
+ validateField(map, UserAuditJsonKeysEnum.USER_BEFORE.getAuditJsonKeyName(),
+ expectedAddUserAuditJavaObject.getUserBefore());
+ validateField(map, UserAuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ expectedAddUserAuditJavaObject.getStatus());
+ validateField(map, UserAuditJsonKeysEnum.DESC.getAuditJsonKeyName(), expectedAddUserAuditJavaObject.getDesc());
+
+ }
+
+ private static void categoryAuditFailureInternal(String action, CategoryDefinition categoryDataDefinition,
+ SubCategoryDefinition subCategoryDefinition, GroupingDefinition groupingDefinition, User user,
+ ActionStatus errorMessage, int status, String resourceType, Object... variables) throws Exception {
+ // validate audit
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorMessage.name());
+ ExpectedCategoryAudit expectedCatrgoryAuditJavaObject = new ExpectedCategoryAudit();
+ expectedCatrgoryAuditJavaObject.setAction(action);
+ expectedCatrgoryAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ expectedCatrgoryAuditJavaObject.setCategoryName(categoryDataDefinition.getName());
+ String subCategoryName = (subCategoryDefinition != null ? subCategoryDefinition.getName()
+ : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setSubCategoryName(subCategoryName);
+ String groupingName = (groupingDefinition != null ? groupingDefinition.getName() : Constants.EMPTY_STRING);
+ expectedCatrgoryAuditJavaObject.setGroupingName(groupingName);
+ expectedCatrgoryAuditJavaObject.setResourceType(resourceType);
+ expectedCatrgoryAuditJavaObject.setStatus(String.valueOf(status));
+ expectedCatrgoryAuditJavaObject.setDesc(errorInfo.getAuditDesc(variables));
+ AuditValidationUtils.validateCategoryAudit(expectedCatrgoryAuditJavaObject, action);
+ }
+
+ public static void validateGetCategoryHirarchy(ExpectedCategoryAudit expectedCatrgoryAuditJavaObject, String action)
+ throws Exception {
+
+ List<Map<String, Object>> actionToList = getAuditListByAction(expectedCatrgoryAuditJavaObject.getAction(), 1);
+ Map<String, Object> map = actionToList.get(0);
+
+ expectedCatrgoryAuditJavaObject.setModifier(getModifierString(expectedCatrgoryAuditJavaObject.getModifierName(),
+ expectedCatrgoryAuditJavaObject.getModifierUid()));
+ validateField(map, CategoryAuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map, CategoryAuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getModifier());
+ validateField(map, CategoryAuditJsonKeysEnum.DETAILS.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getDetails());
+ validateField(map, CategoryAuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getStatus());
+ validateField(map, CategoryAuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getDesc());
+
+ }
+
+ public static void validateCategoryAudit(ExpectedCategoryAudit expectedCatrgoryAuditJavaObject, String action)
+ throws Exception {
+
+ List<Map<String, Object>> actionToList = getAuditListByAction(expectedCatrgoryAuditJavaObject.getAction(), 1);
+ Map<String, Object> map = actionToList.get(0);
+ validateField(map, CategoryAuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map, CategoryAuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getModifier());
+ validateField(map, CategoryAuditJsonKeysEnum.CATEGORY_NAME.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getCategoryName());
+ validateField(map, CategoryAuditJsonKeysEnum.SUB_CATEGORY_NAME.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getSubCategoryName());
+ validateField(map, CategoryAuditJsonKeysEnum.GROUPING_NAME.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getGroupingName());
+ validateField(map, CategoryAuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getResourceType());
+ validateField(map, CategoryAuditJsonKeysEnum.STATUS.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getStatus());
+ validateField(map, CategoryAuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(),
+ expectedCatrgoryAuditJavaObject.getDesc());
+ }
+
+ public static void GetCategoryHierarchyAuditSuccess(String action, String componentType, User user, int status)
+ throws Exception {
+ ExpectedGetUserListAudit expectedGetListOfUsersAuditJavaObject = new ExpectedGetUserListAudit();
+ expectedGetListOfUsersAuditJavaObject.setAction(action);
+ expectedGetListOfUsersAuditJavaObject.setStatus(String.valueOf(status));
+ expectedGetListOfUsersAuditJavaObject.setDesc("OK");
+ expectedGetListOfUsersAuditJavaObject.setModifier(user.getFullName() + "(" + user.getUserId() + ")");
+ expectedGetListOfUsersAuditJavaObject.setDetails(componentType.toLowerCase());
+ validateAuditGetListOfUsersByRoles(expectedGetListOfUsersAuditJavaObject, action);
+ }
+
+ public static String buildArtifactDataAudit(ArtifactDefinition artifactDefinition) {
+ StringBuilder sb = new StringBuilder();
+ if (artifactDefinition.getTimeout() == null) {
+ artifactDefinition.setTimeout(0);
+ }
+ if (artifactDefinition != null) {
+ sb.append(artifactDefinition.getArtifactGroupType() == null ? null
+ : artifactDefinition.getArtifactGroupType().getType()).append(",").append("'")
+ .append(artifactDefinition.getArtifactLabel()).append("'").append(",")
+ .append(artifactDefinition.getArtifactType()).append(",")
+ .append(artifactDefinition.getArtifactName()).append(",").append(artifactDefinition.getTimeout())
+ .append(",").append(artifactDefinition.getEsId());
+ sb.append(",");
+ if (artifactDefinition.getArtifactVersion() != null) {
+ sb.append(artifactDefinition.getArtifactVersion());
+ } else {
+ sb.append(" ");
+ }
+ sb.append(",");
+ if (artifactDefinition.getArtifactUUID() != null) {
+ sb.append(artifactDefinition.getArtifactUUID());
+ } else {
+ sb.append(" ");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedMissingInformationAuditObject(String Action,
+ String resourceUid, ComponentType resourceType) throws FileNotFoundException {
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceUid);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion("");
+ expectedAudit.setModifierName("");
+ expectedAudit.setModifierUid("");
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState("");
+ expectedAudit.setPrevArtifactUuid("");
+ expectedAudit.setCurrArtifactUuid("");
+ expectedAudit.setArtifactData("");
+ expectedAudit.setStatus("403");
+ expectedAudit.setDesc(buildAuditDescription(
+ new ErrorValidationUtils().parseErrorConfigYaml(ActionStatus.MISSING_INFORMATION.name()),
+ new ArrayList<String>()));
+ return expectedAudit;
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedComponentNotFoundAuditObject(String Action,
+ String resourceUid, ComponentType resourceType, String artifactUid, User user,
+ ArrayList<String> notFoundComponent) throws FileNotFoundException {
+ String desc = null;
+
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceUid);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion("");
+ expectedAudit.setModifierName(user.getFirstName() + " " + user.getLastName());
+ expectedAudit.setModifierUid(user.getUserId());
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState("");
+ expectedAudit.setPrevArtifactUuid("");
+ expectedAudit.setCurrArtifactUuid(artifactUid);
+ expectedAudit.setArtifactData("");
+ expectedAudit.setStatus("404");
+
+ if (resourceType.getValue() == ComponentType.SERVICE.getValue()) {
+ desc = buildAuditDescription(
+ new ErrorValidationUtils().parseErrorConfigYaml(ActionStatus.SERVICE_NOT_FOUND.name()),
+ notFoundComponent);
+ } else if (resourceType.getValue() == ComponentType.RESOURCE.getValue())
+ desc = buildAuditDescription(
+ new ErrorValidationUtils().parseErrorConfigYaml(ActionStatus.RESOURCE_NOT_FOUND.name()),
+ notFoundComponent);
+
+ expectedAudit.setDesc(desc);
+ return expectedAudit;
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedArtifactNotFoundAuditObject(String Action, String resourceUid,
+ ComponentType resourceType, String artifactUid, User user, String currState, String currVersion)
+ throws FileNotFoundException {
+ String desc = null;
+
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceUid);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion(currVersion);
+ expectedAudit.setModifierName(user.getFirstName() + " " + user.getLastName());
+ expectedAudit.setModifierUid(user.getUserId());
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState(currState);
+ expectedAudit.setPrevArtifactUuid("");
+ expectedAudit.setCurrArtifactUuid(artifactUid);
+ expectedAudit.setArtifactData("");
+ expectedAudit.setStatus("404");
+
+ desc = buildAuditDescription(
+ new ErrorValidationUtils().parseErrorConfigYaml(ActionStatus.ARTIFACT_NOT_FOUND.name()),
+ Arrays.asList(""));
+
+ expectedAudit.setDesc(desc);
+ return expectedAudit;
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedArtifactNotFoundAuditObject(String Action,
+ String resourceName, ComponentType resourceType, String artifactUid, LifecycleStateEnum lifecycle,
+ User user, String currVersion) throws FileNotFoundException {
+ String desc = null;
+
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceName);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion(currVersion);
+ expectedAudit.setModifierName(user.getFirstName() + " " + user.getLastName());
+ expectedAudit.setModifierUid(user.getUserId());
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState(lifecycle.name());
+ expectedAudit.setPrevArtifactUuid("");
+ expectedAudit.setCurrArtifactUuid(artifactUid);
+ expectedAudit.setArtifactData("");
+ expectedAudit.setStatus("404");
+
+ desc = buildAuditDescription(
+ new ErrorValidationUtils().parseErrorConfigYaml(ActionStatus.ARTIFACT_NOT_FOUND.name()),
+ new ArrayList<String>());
+
+ expectedAudit.setDesc(desc);
+ return expectedAudit;
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedRestrictedOperationAuditObject(String Action,
+ String resourceNameOrUid, ComponentType resourceType, String artifactUid, User user, String currVersion,
+ String currState) throws FileNotFoundException {
+ String desc = null;
+
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceNameOrUid);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion(currVersion);
+ expectedAudit.setModifierName(user.getFirstName() + " " + user.getLastName());
+ expectedAudit.setModifierUid(user.getUserId());
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState(currState);
+ expectedAudit.setPrevArtifactUuid("");
+ expectedAudit.setCurrArtifactUuid(artifactUid);
+ expectedAudit.setArtifactData("");
+ expectedAudit.setStatus("409");
+
+ desc = buildAuditDescription(
+ new ErrorValidationUtils().parseErrorConfigYaml(ActionStatus.RESTRICTED_OPERATION.name()),
+ new ArrayList<String>());
+
+ expectedAudit.setDesc(desc);
+ return expectedAudit;
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedInvalidContentAuditObject(String Action, String resourceName,
+ ComponentType resourceType, String artifactUid, User user, String currVersion, String currState,
+ ArrayList<String> invalidContentList) throws FileNotFoundException {
+ return expectedInvalidContentAuditObject(ActionStatus.INVALID_CONTENT, Action, resourceName, resourceType,
+ artifactUid, user, currVersion, currState, invalidContentList);
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedInvalidContentAuditObject(ActionStatus actionStatus,
+ String Action, String resourceName, ComponentType resourceType, String artifactUid, User user,
+ String currVersion, String currState, ArrayList<String> invalidContentList) throws FileNotFoundException {
+ String desc = null;
+
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceName);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion(currVersion);
+ expectedAudit.setModifierName(user.getFirstName() + " " + user.getLastName());
+ expectedAudit.setModifierUid(user.getUserId());
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState(currState);
+ expectedAudit.setPrevArtifactUuid("");
+ expectedAudit.setCurrArtifactUuid(artifactUid);
+ expectedAudit.setArtifactData("");
+ expectedAudit.setStatus("400");
+
+ desc = buildAuditDescription(new ErrorValidationUtils().parseErrorConfigYaml(actionStatus.name()),
+ invalidContentList);
+
+ expectedAudit.setDesc(desc);
+ return expectedAudit;
+ }
+
+ public static ExpectedResourceAuditJavaObject expectedSuccessAuditObject(String Action, String resourceName,
+ ComponentType resourceType, ArtifactReqDetails artifactReq, User user, String currVersion, String currState,
+ String prevArtifactUuid) throws FileNotFoundException {
+ ExpectedResourceAuditJavaObject expectedAudit = new ExpectedResourceAuditJavaObject();
+ expectedAudit.setAction(Action);
+ expectedAudit.setResourceName(resourceName);
+ expectedAudit.setResourceType(resourceType.getValue());
+ expectedAudit.setPrevVersion("");
+ expectedAudit.setCurrVersion(currVersion);
+ expectedAudit.setModifierName(user.getFirstName() + " " + user.getLastName());
+ expectedAudit.setModifierUid(user.getUserId());
+ expectedAudit.setPrevState("");
+ expectedAudit.setCurrState(currState);
+ expectedAudit.setPrevArtifactUuid(prevArtifactUuid);
+ expectedAudit.setCurrArtifactUuid(artifactReq.getUniqueId());
+ expectedAudit
+ .setArtifactData(buildArtifactDataAudit(ArtifactUtils.convertArtifactReqToDefinition(artifactReq)));
+ expectedAudit.setStatus("200");
+ expectedAudit.setDesc("OK");
+ return expectedAudit;
+ }
+
+ public static JSONObject filterAuditByUuid(String action, String uuid) throws Exception {
+ Map<String, String> actionMap = new HashMap<String, String>();
+ actionMap.put("ACTION", action);
+ JSONObject actionJsonObject = new JSONObject(actionMap);
+ Map<String, String> uuidMap = new HashMap<String, String>();
+ uuidMap.put("SERVICE_INSTANCE_ID", uuid);
+ JSONObject uuidJsonObject = new JSONObject(uuidMap);
+
+ List<JSONObject> filters = new ArrayList<JSONObject>(Arrays.asList(actionJsonObject, uuidJsonObject));
+ JSONObject body = buildElasticQueryBody(filters);
+ return body;
+ }
+
+ public static void validateAudit(ExpectedResourceAuditJavaObject resourceAuditJavaObject, String action)
+ throws Exception {
+ List<Map<String, Object>> actionToList = getAuditListByAction(resourceAuditJavaObject.getAction(), 1);
+ Map<String, Object> map2 = actionToList.get(0);
+ validateField(map2, AuditJsonKeysEnum.ACTION.getAuditJsonKeyName(), action);
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_NAME.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceName());
+ validateField(map2, AuditJsonKeysEnum.RESOURCE_TYPE.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getResourceType());
+ validateField(map2, AuditJsonKeysEnum.PREV_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getPrevVersion());
+ validateField(map2, AuditJsonKeysEnum.CURR_VERSION.getAuditJsonKeyName(),
+ resourceAuditJavaObject.getCurrVersion());
+ validateField(map2, AuditJsonKeysEnum.MODIFIER.getAuditJsonKeyName(), resourceAuditJavaObject.getMODIFIER());
+ validateField(map2, AuditJsonKeysEnum.PREV_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getPrevState());
+ validateField(map2, AuditJsonKeysEnum.CURR_STATE.getAuditJsonKeyName(), resourceAuditJavaObject.getCurrState());
+ validateField(map2, AuditJsonKeysEnum.STATUS.getAuditJsonKeyName(), resourceAuditJavaObject.getStatus());
+ validateField(map2, AuditJsonKeysEnum.DESCRIPTION.getAuditJsonKeyName(), resourceAuditJavaObject.getDesc());
+ validateField(map2, AuditJsonKeysEnum.COMMENT.getAuditJsonKeyName(), resourceAuditJavaObject.getComment());
+ }
+
+ ////// service audit validation/////////////////////
+
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(ServiceReqDetails serviceReqDetails,
+ String serviceVersion, User sdncUserDetails) {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+
+ expectedResourceAuditJavaObject.setAction("Create");
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ String userFirstLastName = sdncUserDetails.getFirstName() + " " + sdncUserDetails.getLastName();
+ expectedResourceAuditJavaObject.setModifierName(userFirstLastName);
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setResourceName(serviceReqDetails.getName());
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ expectedResourceAuditJavaObject.setPrevVersion(String.valueOf(Float.parseFloat(serviceVersion) - 0.1f));
+ expectedResourceAuditJavaObject.setCurrVersion(serviceVersion);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setComment(null);
+
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(ServiceReqDetails serviceReqDetails,
+ String serviceVersion, User sdncUserDetails, ActionStatus errorStatus, List<String> variables)
+ throws FileNotFoundException {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = constructFieldsForAuditValidation(
+ serviceReqDetails, serviceVersion, sdncUserDetails);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(errorStatus.name());
+
+ expectedResourceAuditJavaObject.setStatus(errorInfo.getCode().toString());
+ String auditDesc = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ expectedResourceAuditJavaObject.setDesc(auditDesc);
+
+ return expectedResourceAuditJavaObject;
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/BaseValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/BaseValidationUtils.java
new file mode 100644
index 0000000000..eb3ee331ed
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/BaseValidationUtils.java
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.testng.Assert;
+
+public class BaseValidationUtils {
+
+ public static final int STATUS_CODE_SUCCESS = 200;
+ public static final int STATUS_CODE_CREATED = 201;
+ public static final int STATUS_CODE_DELETE = 204;
+ public static final int STATUS_CODE_NOT_FOUND = 404;
+ public static final int STATUS_CODE_SUCCESS_NO_CONTENT = 204;
+ public static final int STATUS_CODE_SUCCESS_DELETE = 204;
+ public static final int STATUS_CODE_INVALID_CONTENT = 400;
+ public static final int STATUS_CODE_MISSING_DATA = 400;
+ public static final int STATUS_CODE_MISSING_INFORMATION = 403;
+ public static final int STATUS_CODE_RESTRICTED_ACCESS = 403;
+ public static final int STATUS_CODE_RESTRICTED_OPERATION = 409;
+ public static final int STATUS_CODE_ALREADY_EXISTS = 409;
+
+ // ------
+ protected static Boolean checkErrorCode(RestResponse deleteResponse) {
+ if (deleteResponse.getErrorCode() == STATUS_CODE_SUCCESS
+ || deleteResponse.getErrorCode() == STATUS_CODE_DELETE) {
+ return true;
+ }
+ return false;
+ }
+
+ // *** STATUS CODE VALIDATION UTIITIES ****
+ public static void checkStatusCode(RestResponse response, String assertMessage, boolean AND, int... statuses) {
+ int statusCode = response.getErrorCode();
+ for (int status : statuses) {
+ if (AND && statusCode != status) {
+ Assert.fail(assertMessage + " status: " + statusCode);
+ } else if (statusCode == status) {
+ return;
+ }
+ }
+ if (!AND) {
+ Assert.fail(assertMessage + " status: " + statusCode);
+ }
+ }
+
+ public static void checkDeleteResponse(RestResponse response) {
+ checkStatusCode(response, "delete request failed", false, STATUS_CODE_DELETE, STATUS_CODE_NOT_FOUND,
+ STATUS_CODE_SUCCESS); // STATUS_CODE_SUCCESS for deActivate user
+ }
+
+ public static void checkCreateResponse(RestResponse response) {
+ checkStatusCode(response, "create request failed", false, STATUS_CODE_CREATED);
+ }
+
+ public static void checkSuccess(RestResponse response) {
+ checkStatusCode(response, "request failed", false, STATUS_CODE_SUCCESS);
+ }
+
+ public static void checkErrorResponse(RestResponse errorResponse, ActionStatus actionStatus,
+ String... expectedVariables) throws FileNotFoundException {
+ // Expected error
+ ErrorInfo expectedError = ErrorValidationUtils.parseErrorConfigYaml(actionStatus.name());
+ String expectedMessage = expectedError.getMessage();
+
+ // Actual error
+ ResponseFormat responseFormat = ResponseParser.parseToObjectUsingMapper(errorResponse.getResponse(),
+ ResponseFormat.class);
+ String actualMessage = responseFormat.getText();
+ String[] actualVariables = responseFormat.getVariables();
+
+ assertEquals("Unexpected error message", expectedMessage, actualMessage);
+ assertEquals("Unexpected error variables", Arrays.asList(expectedVariables), Arrays.asList(actualVariables));
+ }
+
+ public static void checkErrorMessageResponse(RestResponse errorResponse, ActionStatus actionStatus)
+ throws FileNotFoundException {
+ // Expected error
+ ErrorInfo expectedError = ErrorValidationUtils.parseErrorConfigYaml(actionStatus.name());
+ String expectedMessage = expectedError.getMessage();
+
+ // Actual error
+ ResponseFormat responseFormat = ResponseParser.parseToObjectUsingMapper(errorResponse.getResponse(),
+ ResponseFormat.class);
+ String actualMessage = responseFormat.getText();
+
+ assertEquals("Unexpected error message", expectedMessage, actualMessage);
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CategoryValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CategoryValidationUtils.java
new file mode 100644
index 0000000000..cd2297fd19
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CategoryValidationUtils.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import org.json.JSONObject;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.CategoryRestUtils;
+
+public class CategoryValidationUtils {
+
+ public static void verifyCategoryExistInGetResponse(RestResponse getAllCategoryRest,
+ CategoryDefinition categoryDefinition) {
+
+ int categoriesNum = CategoryRestUtils.getMatchingCategoriesNum(getAllCategoryRest, categoryDefinition);
+ assertEquals("category " + categoryDefinition.getName() + " not found during get or found more than once", 1,
+ categoriesNum);
+ }
+
+ public static void verifyCategoryNotExistsInGetResponse(RestResponse getAllCategoryRest,
+ CategoryDefinition categoryDefinition) {
+
+ int categoriesNum = CategoryRestUtils.getMatchingCategoriesNum(getAllCategoryRest, categoryDefinition);
+ assertEquals("category " + categoryDefinition.getName() + " should't be found during get", 0, categoriesNum);
+ }
+
+ public static void verifySubCategoryExistInGetResponse(RestResponse getAllCategoryRest, String parentCategoryId,
+ SubCategoryDefinition expectedSubCategoryDefinition) {
+
+ int subCategoriesNum = CategoryRestUtils.getMatchingSubCategoriesNum(getAllCategoryRest, parentCategoryId,
+ expectedSubCategoryDefinition);
+ assertEquals(
+ "sub-category " + expectedSubCategoryDefinition.getName()
+ + " not found during get or found more than once for parentId " + parentCategoryId,
+ 1, subCategoriesNum);
+ }
+
+ public static void verifyGroupingExistInGetResponse(RestResponse getAllCategoryRest, String parentCategoryId,
+ String subCategoryId, GroupingDefinition expectedGroupingDefinition) {
+
+ int groupingNum = CategoryRestUtils.getMatchingGroupingNum(getAllCategoryRest, parentCategoryId, subCategoryId,
+ expectedGroupingDefinition);
+ assertEquals(
+ "sub-category " + expectedGroupingDefinition.getName()
+ + " not found during get or found more than once for parentId " + parentCategoryId,
+ 1, groupingNum);
+ }
+
+ public static void verifyGroupingNotExistInGetResponse(RestResponse getAllCategoryRest, String parentCategoryId,
+ String subCategoryId, GroupingDefinition expectedGroupingDefinition) {
+
+ int groupingNum = CategoryRestUtils.getMatchingGroupingNum(getAllCategoryRest, parentCategoryId, subCategoryId,
+ expectedGroupingDefinition);
+ assertEquals(
+ "sub-category " + expectedGroupingDefinition.getName()
+ + " not found during get or found more than once for parentId " + parentCategoryId,
+ 0, groupingNum);
+ }
+
+ public static void verifySubCategoryNotExistsInGetResponse(RestResponse getAllCategoryRest, String parentCategoryId,
+ SubCategoryDefinition expectedSubCategoryDefinition) {
+
+ int subCategoriesNum = CategoryRestUtils.getMatchingSubCategoriesNum(getAllCategoryRest, parentCategoryId,
+ expectedSubCategoryDefinition);
+ assertEquals("sub-category " + expectedSubCategoryDefinition.getName()
+ + " should't be found during get for parentId " + parentCategoryId, 0, subCategoriesNum);
+ }
+
+ /// NEE Benny
+ public static void validateCreateGroupResponse(RestResponse createSubCategoryRest,
+ GroupingDefinition expectedGroupDefinition) throws Exception {
+
+ String response = createSubCategoryRest.getResponse();
+ JSONObject jobject = new JSONObject(response);
+ assertTrue(jobject.get("name").equals(expectedGroupDefinition.getName()));
+ assertTrue(jobject.get("normalizedName").equals(expectedGroupDefinition.getNormalizedName()));
+ // assertNotNull(jobject.get("normalizedName"));
+ assertNotNull(jobject.get("uniqueId"));
+ expectedGroupDefinition.setUniqueId(jobject.get("uniqueId").toString());
+
+ }
+
+ public static void validateCreateSubCategoryResponse(RestResponse createSubCategoryRest,
+ SubCategoryDefinition expectedSubCategoryDefinition) throws Exception {
+
+ String response = createSubCategoryRest.getResponse();
+ JSONObject jobject = new JSONObject(response);
+ assertTrue(jobject.get("name").equals(expectedSubCategoryDefinition.getName()));
+ assertNotNull(jobject.get("normalizedName"));
+ assertNotNull(jobject.get("uniqueId"));
+ }
+
+ public static void validateCreateCategoryResponse(RestResponse createCategoryRest,
+ CategoryDefinition expectedCategoryDefinition) throws Exception {
+ String response = createCategoryRest.getResponse();
+ JSONObject jobject = new JSONObject(response);
+ assertTrue(jobject.get("name").equals(expectedCategoryDefinition.getName()));
+ assertTrue(jobject.get("normalizedName").equals(expectedCategoryDefinition.getNormalizedName()));
+ assertNotNull(jobject.get("uniqueId"));
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CsarValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CsarValidationUtils.java
new file mode 100644
index 0000000000..29f9e84dd8
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/CsarValidationUtils.java
@@ -0,0 +1,274 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.ci.tests.datatypes.GroupHeatMetaDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyHeatMetaDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.TypeHeatMetaDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaNodeTemplatesTopologyTemplateDefinition;
+import org.openecomp.sdc.ci.tests.tosca.datatypes.ToscaRequirementsNodeTemplatesDefinition;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ImportRestUtils;
+import org.openecomp.sdc.common.rest.api.RestResponseAsByteArray;
+import org.openecomp.sdc.common.util.ZipUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CsarValidationUtils {
+ private static Logger log = LoggerFactory.getLogger(CsarValidationUtils.class.getName());
+
+ public static String getCsarPayload(String csarName, String fileLocation) throws Exception {
+
+ RestResponseAsByteArray csar = ImportRestUtils.getCsar(csarName, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ assertTrue("Return response code different from 200", csar.getHttpStatusCode() == BaseRestUtils.STATUS_CODE_SUCCESS);
+ Map<String, byte[]> readZip = null;
+ byte[] data = csar.getResponse();
+ if (data != null && data.length > 0) {
+ readZip = ZipUtil.readZip(data);
+
+ }
+
+ byte[] artifactsBs = readZip.get(fileLocation);
+ String str = new String(artifactsBs, StandardCharsets.UTF_8);
+
+ return str;
+
+ }
+
+ public static List<TypeHeatMetaDefinition> getListTypeHeatMetaDefinition(String csarUUID) throws Exception {
+
+ String artifactHeatMetaLocation = "Artifacts/HEAT.meta";
+ JSONParser parser = new JSONParser();
+ String csarPayload = getCsarPayload(csarUUID, artifactHeatMetaLocation);
+ if (csarPayload != null) {
+ Object parse = parser.parse(csarPayload);
+ JSONObject jsonObject = (JSONObject) parse;
+ JSONObject jsonObjectImportStructure = (JSONObject) jsonObject.get("importStructure");
+ List<TypeHeatMetaDefinition> listHeatMetaDefenition = new ArrayList<TypeHeatMetaDefinition>();
+ listHeatMetaDefenition = getArtifactsByGroup(jsonObjectImportStructure, listHeatMetaDefenition);
+ return listHeatMetaDefenition;
+ }
+ return null;
+
+ }
+
+ protected static List<TypeHeatMetaDefinition> getArtifactsByGroup(JSONObject jsonObjectImportStructure, List<TypeHeatMetaDefinition> listHeatMetaDefenition) {
+
+ @SuppressWarnings("unchecked")
+ Set<Object> typeSet = jsonObjectImportStructure.keySet();
+ for (Object type : typeSet) {
+ TypeHeatMetaDefinition heatMetaDefenition = new TypeHeatMetaDefinition();
+ log.debug(type.toString());
+ log.debug("{}", jsonObjectImportStructure.get(type));
+ JSONArray array = (JSONArray) jsonObjectImportStructure.get(type);
+ heatMetaDefenition.setTypeName((String) type);
+ List<GroupHeatMetaDefinition> groupHeatMetaDefinitions = new ArrayList<GroupHeatMetaDefinition>();
+ heatMetaDefenition.setGroupHeatMetaDefinition(fetchArtifactByGroup(array, groupHeatMetaDefinitions, true));
+ listHeatMetaDefenition.add(heatMetaDefenition);
+ }
+ return listHeatMetaDefenition;
+ }
+
+ protected static List<GroupHeatMetaDefinition> fetchArtifactByGroup(JSONArray array, List<GroupHeatMetaDefinition> listGroupHeatMetaDefinition, Boolean openNewGroup) {
+
+ GroupHeatMetaDefinition groupHeatMetaDefinition;
+
+ if (array != null) {
+ for (int i = 0; i < array.size(); i++) {
+ if (openNewGroup) {
+ groupHeatMetaDefinition = new GroupHeatMetaDefinition();
+ int groupNumber = listGroupHeatMetaDefinition.size() + 1;
+ log.debug("groupName={}", groupNumber);
+ groupHeatMetaDefinition.setGroup(groupNumber);
+ listGroupHeatMetaDefinition.add(groupHeatMetaDefinition);
+ PropertyHeatMetaDefinition propertyHeatMetaDefinition = new PropertyHeatMetaDefinition();
+ propertyHeatMetaDefinition.setName("isBase");
+ propertyHeatMetaDefinition.setValue(false);
+ groupHeatMetaDefinition.setPropertyHeatMetaDefinition(propertyHeatMetaDefinition);
+ }
+ groupHeatMetaDefinition = listGroupHeatMetaDefinition.get(listGroupHeatMetaDefinition.size() - 1);
+ JSONObject jsonObject = (JSONObject) array.get(i);
+ @SuppressWarnings("unchecked")
+ Set<Object> groupsKey = jsonObject.keySet();
+ for (Object groupKey : groupsKey) {
+ String groupKeyStr = (String) groupKey;
+ if (groupKeyStr.equals("isBase")) {
+ PropertyHeatMetaDefinition propertyHeatMetaDefinition = new PropertyHeatMetaDefinition();
+ propertyHeatMetaDefinition.setName(groupKeyStr);
+ propertyHeatMetaDefinition.setValue((boolean) jsonObject.get(groupKeyStr));
+ if (!groupHeatMetaDefinition.getPropertyHeatMetaDefinition().equals(propertyHeatMetaDefinition)) {
+ groupHeatMetaDefinition.getPropertyHeatMetaDefinition().setValue((boolean) jsonObject.get(groupKeyStr));
+ }
+ }
+ if (groupKeyStr.equals("fileName") || groupKeyStr.equals("env")) {
+ String artifactName = (String) jsonObject.get(groupKeyStr);
+ List<String> listArtifactNames = groupHeatMetaDefinition.getArtifactList();
+ listArtifactNames.add(artifactName);
+ groupHeatMetaDefinition.setArtifactList(listArtifactNames);
+ } else {
+ if (!groupKeyStr.equals("isBase")) {
+ fetchArtifactByGroup((JSONArray) jsonObject.get(groupKeyStr), listGroupHeatMetaDefinition, false);
+ }
+ }
+ }
+ }
+ }
+ return listGroupHeatMetaDefinition;
+ }
+
+ private static Integer getArtifactCount(List<TypeHeatMetaDefinition> listHeatMetaDefenition, Boolean isEnvIncluded) {
+ int count = 0;
+ List<String> uniqeArtifactList = new ArrayList<>();
+
+ for (TypeHeatMetaDefinition typeHeatMetaDefinition : listHeatMetaDefenition) {
+ for (GroupHeatMetaDefinition groupHeatMetaDefinition : typeHeatMetaDefinition.getGroupHeatMetaDefinition()) {
+ if (isEnvIncluded) {
+ count = count + groupHeatMetaDefinition.getArtifactList().size();
+ } else {
+ for (String fileName : groupHeatMetaDefinition.getArtifactList()) {
+ if (!fileName.contains(".env") && !uniqeArtifactList.contains(fileName)) {
+ uniqeArtifactList.add(fileName);
+ count = count + 1;
+ }
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ private static Integer getGroupCount(List<TypeHeatMetaDefinition> listHeatMetaDefenition) {
+ int count = 0;
+ for (TypeHeatMetaDefinition typeHeatMetaDefinition : listHeatMetaDefenition) {
+ count = count + typeHeatMetaDefinition.getGroupHeatMetaDefinition().size();
+ }
+ return count;
+ }
+
+ private static String groupNameBuilder(Resource resource) {
+ String separator = "::";
+ String module = "module-";
+ String groupName = resource.getSystemName() + separator + module;
+ return groupName;
+ }
+
+ public static void validateCsarVfArtifact(String csarUUID, Resource resource) throws Exception {
+
+ List<TypeHeatMetaDefinition> listTypeHeatMetaDefinition = getListTypeHeatMetaDefinition(csarUUID);
+ assertTrue("check group count, expected: " + getGroupCount(listTypeHeatMetaDefinition) + ", actual: " + resource.getGroups().size(), getGroupCount(listTypeHeatMetaDefinition) == resource.getGroups().size());
+ assertTrue("check artifact count, expected: " + getArtifactCount(listTypeHeatMetaDefinition, false) + ", actual: " + resource.getDeploymentArtifacts().size(),
+ getArtifactCount(listTypeHeatMetaDefinition, false) == resource.getDeploymentArtifacts().size());
+
+ }
+
+ public static void validateToscaDefinitonObjectVsResource(ToscaDefinition toscaDefinition, Resource resource) throws Exception {
+
+ assertTrue("check resource instance count, expected: " + getResourceInstanceCount(toscaDefinition) + ", actual: " + resource.getComponentInstances().size(),
+ getResourceInstanceCount(toscaDefinition) == resource.getComponentInstances().size());
+ assertTrue("check resource instance relation count, expected: " + getResourceInstanceRelationCount(toscaDefinition) + ", actual: " + resource.getComponentInstancesRelations().size(),
+ getResourceInstanceRelationCount(toscaDefinition) == resource.getComponentInstancesRelations().size());
+
+ }
+
+ public static Integer getResourceInstanceCount(ToscaDefinition toscaDefinition) {
+
+ return toscaDefinition.getToscaTopologyTemplate().getToscaNodeTemplatesTopologyTemplateDefinition().size();
+ }
+
+ public static Integer getResourceInstanceRelationCount(ToscaDefinition toscaDefinition) {
+ int count = 0;
+ List<ToscaNodeTemplatesTopologyTemplateDefinition> toscaNodeTemplatesTopologyTemplateDefinition = toscaDefinition.getToscaTopologyTemplate().getToscaNodeTemplatesTopologyTemplateDefinition();
+ for (int i = 0; i < toscaNodeTemplatesTopologyTemplateDefinition.size(); i++) {
+ List<ToscaRequirementsNodeTemplatesDefinition> requirements = toscaNodeTemplatesTopologyTemplateDefinition.get(i).getRequirements();
+ if (requirements != null) {
+ for (ToscaRequirementsNodeTemplatesDefinition requirement : requirements) {
+ if (requirement.getNode() != null) {
+ count = count + 1;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ // not finished yet
+ private static void validateCsarVfgroup(String csarUUID, Resource resource) {
+
+ List<GroupDefinition> groups = resource.getGroups();
+ for (GroupDefinition groupDefinition : groups) {
+ List<String> artifacts = groupDefinition.getArtifacts();
+ assertTrue("group description is null", groupDefinition.getDescription() != null);
+ assertTrue("InvariantUUID is null", groupDefinition.getInvariantUUID() != null);
+ // groupDefinition.getMembers();
+ assertTrue("name format mismatch, expected: " + groupNameBuilder(resource) + "[0-9], actual: " + groupDefinition.getName(), groupDefinition.getName().contains(groupNameBuilder(resource)));
+ // groupDefinition.getProperties();
+ // groupDefinition.getPropertyValueCounter();
+ assertTrue(groupDefinition.getType().equals(getGroupType()));
+ }
+
+ String expectedCsarUUID = csarUUID;
+ // String expectedToscaResourceName = "org.openecomp.resource.vf." +
+ // WordUtils.capitalize(resourceDetails.getName().toLowerCase());
+ //
+ // assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID,
+ // resource.getCsarUUID()),
+ // expectedCsarUUID.equals(resource.getCsarUUID()));
+ // assertTrue("toscaResourceName : " +
+ // buildAssertMessage(expectedToscaResourceName,
+ // resource.getToscaResourceName()),
+ // expectedToscaResourceName.equals(resource.getToscaResourceName()));
+ //
+ // RestResponse getResourceResponse =
+ // ResourceRestUtils.getResource(resource.getUniqueId());
+ // Resource getResource =
+ // ResponseParser.parseToObjectUsingMapper(getResourceResponse.getResponse(),
+ // Resource.class);
+ // assertTrue("csarUUID : " + buildAssertMessage(expectedCsarUUID,
+ // getResource.getCsarUUID()),
+ // expectedCsarUUID.equals(getResource.getCsarUUID()));
+ // assertTrue("toscaResourceName : " +
+ // buildAssertMessage(expectedToscaResourceName,
+ // getResource.getToscaResourceName()),
+ // expectedToscaResourceName.equals(getResource.getToscaResourceName()));
+
+ }
+
+ private static String getGroupType() {
+ return "org.openecomp.groups.VfModule";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ErrorValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ErrorValidationUtils.java
new file mode 100644
index 0000000000..ac5a05d7ca
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ErrorValidationUtils.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ExceptionEnumType;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+
+public class ErrorValidationUtils {
+
+ static Logger logger = LoggerFactory.getLogger(Utils.class.getName());
+
+ public static void checkBodyResponseOnError(String errorType, List<String> variables, String actualResponse)
+ throws FileNotFoundException, JSONException {
+
+ ErrorInfo errorInfo = parseErrorConfigYaml(errorType);
+ JSONObject expectedResponseBody = null;
+ if (errorInfo.getMessageId() != null) {
+ if (errorInfo.getMessageId().contains("SVC")) {
+ expectedResponseBody = restExceptionFormatBuilder(errorInfo.getMessageId(), errorInfo.getMessage(),
+ variables, ExceptionEnumType.SERVICE_EXCEPTION.getValue());
+ } else {
+ expectedResponseBody = restExceptionFormatBuilder(errorInfo.getMessageId(), errorInfo.getMessage(),
+ variables, ExceptionEnumType.POLICY_EXCPTION.getValue());
+ }
+ }
+ actualResponse = actualResponse.replaceAll("\\n", "");
+ logger.debug("actualResponse - {}", actualResponse);
+ logger.debug("expectedResponseBody - {}", expectedResponseBody);
+ assertEquals(expectedResponseBody, new JSONObject(actualResponse));
+ }
+
+ public static JSONObject restExceptionFormatBuilder(String messageId, String text, List<String> variables,
+ String type) {
+
+ JSONObject simpleElements = new JSONObject();
+ JSONObject exceptionType = new JSONObject();
+ JSONObject requestError = new JSONObject();
+
+ try {
+ simpleElements.put("messageId", messageId);
+ simpleElements.put("text", text);
+ simpleElements.put("variables", variables);
+ exceptionType.put(type, simpleElements);
+ requestError.put("requestError", exceptionType);
+
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ return requestError;
+
+ }
+
+ public static ErrorInfo parseErrorConfigYaml(String error) throws FileNotFoundException {
+ Yaml yaml = new Yaml();
+ ErrorInfo errInfo = null;
+ Config config = Utils.getConfig();
+ String errorConfigurationFile = config.getErrorConfigurationFile();
+ File file = new File(errorConfigurationFile);
+ // File file = new
+ // File("../catalog-be/src/main/resources/config/error-configuration.yaml");
+ InputStream inputStream = new FileInputStream(file);
+ Map<?, ?> map = (Map<?, ?>) yaml.load(inputStream);
+ // System.out.println(map.get("errors"));
+ @SuppressWarnings("unchecked")
+ Map<String, ErrorInfo> errorMap = (Map<String, ErrorInfo>) map.get("errors");
+ @SuppressWarnings("unchecked")
+ Map<String, Object> errorInfo = (Map<String, Object>) errorMap.get(error);
+
+ String message = (String) errorInfo.get("message");
+ String messageId = (String) errorInfo.get("messageId");
+ int code = (Integer) errorInfo.get("code");
+ errInfo = new ErrorInfo(code, message, messageId);
+
+ return errInfo;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ProductValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ProductValidationUtils.java
new file mode 100644
index 0000000000..ba5114c1bc
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ProductValidationUtils.java
@@ -0,0 +1,239 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest.ComponentOperationEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+
+public class ProductValidationUtils {
+
+ static Logger logger = Logger.getLogger(ProductValidationUtils.class.getName());
+
+ public static void compareExpectedAndActualProducts(Product expectedProduct, Product actualProduct) {
+ compareExpectedAndActualProducts(expectedProduct, actualProduct, null);
+ }
+
+ public static void compareExpectedAndActualProducts(Product expectedProduct, Product actualProduct,
+ ComponentOperationEnum operation) {
+
+ assertEquals(expectedProduct.getName(), actualProduct.getName());
+ assertEquals(expectedProduct.getFullName(), actualProduct.getFullName());
+ assertEquals(expectedProduct.getDescription(), actualProduct.getDescription());
+
+ List<String> expectedContacts = expectedProduct.getContacts();
+ List<String> actualContacts = actualProduct.getContacts();
+ assertTrue(
+ "Expected contacts:" + Arrays.toString(expectedContacts.toArray()) + ", actual contacts:"
+ + Arrays.toString(actualContacts.toArray()),
+ expectedContacts.size() == actualContacts.size() && expectedContacts.containsAll(actualContacts)
+ && actualContacts.containsAll(expectedContacts));
+
+ List<String> expectedTags = expectedProduct.getTags();
+ List<String> actualTags = actualProduct.getTags();
+ assertTrue(
+ "Expected tags:" + Arrays.toString(expectedTags.toArray()) + ", actual tags:"
+ + Arrays.toString(actualTags.toArray()),
+ expectedTags.size() == actualTags.size() && expectedTags.containsAll(actualTags)
+ && actualTags.containsAll(expectedTags));
+
+ assertEquals(expectedProduct.getLifecycleState(), actualProduct.getLifecycleState());
+ assertEquals(expectedProduct.getVersion(), actualProduct.getVersion());
+ assertEquals(expectedProduct.isHighestVersion(), actualProduct.isHighestVersion());
+ assertEquals(expectedProduct.getNormalizedName(), actualProduct.getNormalizedName());
+
+ compareCategories(expectedProduct, actualProduct);
+ assertEquals(expectedProduct.getLastUpdaterUserId(), actualProduct.getLastUpdaterUserId());
+ if (operation != null) {
+ assertEquals(expectedProduct.getCreatorUserId(), actualProduct.getCreatorUserId());
+ }
+
+ Long lastUpdateDate = actualProduct.getLastUpdateDate();
+ Long creationDate = actualProduct.getCreationDate();
+ Map<String, String> allVersions = actualProduct.getAllVersions();
+
+ if (operation != null) {
+ if (operation == ComponentOperationEnum.UPDATE_COMPONENT
+ || operation == ComponentOperationEnum.CHANGE_STATE_CHECKOUT
+ || operation == ComponentOperationEnum.CHANGE_STATE_CHECKIN
+ || operation == ComponentOperationEnum.CHANGE_STATE_UNDO_CHECKOUT) {
+ assertTrue("Last update date:" + lastUpdateDate + ", creation date: " + creationDate,
+ lastUpdateDate > 0 && creationDate > 0 && lastUpdateDate > creationDate);
+ } else {
+ assertTrue("Last update date:" + lastUpdateDate + ", creation date: " + creationDate,
+ lastUpdateDate > 0 && lastUpdateDate.equals(creationDate));
+ }
+ }
+
+ // Check UUIDs
+ // If just created, no way to test the UUIDs themselves
+ // If updated, we expect the UUIDs of actual to match the expected
+ String uniqueId = actualProduct.getUniqueId();
+ if (operation == ComponentOperationEnum.CREATE_COMPONENT) {
+ UUID.fromString(uniqueId);
+ UUID.fromString(actualProduct.getUUID());
+ UUID.fromString(actualProduct.getInvariantUUID());
+ assertTrue(allVersions.size() == 1);
+ assertTrue(allVersions.get("0.1").equals(uniqueId));
+ } else {
+ if (operation == ComponentOperationEnum.CHANGE_STATE_CHECKOUT) {
+ assertFalse(expectedProduct.getUniqueId().equals(uniqueId));
+ // Assigning the updated uniqueId to expected so that it can be
+ // passed to further logic
+ expectedProduct.setUniqueId(uniqueId);
+ } else if (operation != null) {
+ assertTrue(expectedProduct.getUniqueId().equals(uniqueId));
+ }
+ assertEquals(expectedProduct.getUUID(), actualProduct.getUUID());
+ assertEquals(expectedProduct.getInvariantUUID(), actualProduct.getInvariantUUID());
+ }
+ }
+
+ private static void compareCategories(Product expectedProduct, Product actualProduct) {
+ List<CategoryDefinition> expectedCategories = expectedProduct.getCategories();
+ List<CategoryDefinition> actualCategories = actualProduct.getCategories();
+ if (expectedCategories != null && actualCategories != null) {
+ int expSize = expectedCategories.size();
+ int actSize = actualCategories.size();
+
+ assertTrue("Expected size:" + expSize + ", actual size:" + actSize, expSize == actSize);
+
+ for (CategoryDefinition actualDefinition : actualCategories) {
+ int lastIndexOfCat = expectedCategories.lastIndexOf(actualDefinition);
+ assertTrue("Actual category " + actualDefinition + " not found in expected.", lastIndexOfCat != -1);
+ CategoryDefinition expectedDefinition = expectedCategories.get(lastIndexOfCat);
+ List<SubCategoryDefinition> actualSubcategories = actualDefinition.getSubcategories();
+ List<SubCategoryDefinition> expectedSubcategories = expectedDefinition.getSubcategories();
+ for (SubCategoryDefinition actualSub : actualSubcategories) {
+ lastIndexOfCat = expectedSubcategories.lastIndexOf(actualSub);
+ assertTrue("Actual subcategory " + actualSub + " not found in expected.", lastIndexOfCat != -1);
+ SubCategoryDefinition expectedSub = expectedSubcategories.get(lastIndexOfCat);
+ List<GroupingDefinition> actualGroupings = actualSub.getGroupings();
+ List<GroupingDefinition> expectedGroupings = expectedSub.getGroupings();
+ for (GroupingDefinition actualGrouping : actualGroupings) {
+ lastIndexOfCat = expectedGroupings.lastIndexOf(actualGrouping);
+ assertTrue("Actual grouping " + actualSub + " not found in expected.", lastIndexOfCat != -1);
+ }
+ }
+ }
+
+ for (CategoryDefinition expectedDefinition : expectedCategories) {
+ int lastIndexOfCat = actualCategories.lastIndexOf(expectedDefinition);
+ assertTrue("Expected category " + expectedDefinition + " not found in actual.", lastIndexOfCat != -1);
+ CategoryDefinition actualDefinition = actualCategories.get(lastIndexOfCat);
+ List<SubCategoryDefinition> actualSubcategories = actualDefinition.getSubcategories();
+ List<SubCategoryDefinition> expectedSubcategories = expectedDefinition.getSubcategories();
+ for (SubCategoryDefinition expectedSub : expectedSubcategories) {
+ lastIndexOfCat = actualSubcategories.lastIndexOf(expectedSub);
+ assertTrue("Expected subcategory " + expectedSub + " not found in actual.", lastIndexOfCat != -1);
+ SubCategoryDefinition actualSub = actualSubcategories.get(lastIndexOfCat);
+ List<GroupingDefinition> actualGroupings = actualSub.getGroupings();
+ List<GroupingDefinition> expectedGroupings = expectedSub.getGroupings();
+ for (GroupingDefinition expectedGrouping : expectedGroupings) {
+ lastIndexOfCat = actualGroupings.lastIndexOf(expectedGrouping);
+ assertTrue("Expected grouping " + expectedGrouping + " not found in actual.",
+ lastIndexOfCat != -1);
+ }
+ }
+ }
+ }
+ }
+
+ public static void verifyProductsNotExistInUserFollowedPage(User user, Product... nonExpectedProducts)
+ throws Exception {
+ String component = "products";
+ Boolean isExist;
+ Product nonExpectedProduct;
+ RestResponse getFollowedPage = ProductRestUtils.getFollowed(user.getUserId());
+ JSONArray followedProductes = getListArrayFromRestResponse(getFollowedPage, component);
+ if (followedProductes != null) { // if any product exist in followed
+ // page
+ for (int i = 0; i < nonExpectedProducts.length; i++) {
+ nonExpectedProduct = nonExpectedProducts[i];
+ isExist = false;
+ for (int k = 0; k < followedProductes.size(); k++) {
+ JSONObject jobject = (JSONObject) followedProductes.get(k);
+ if (jobject.get("uuid").toString().equals(nonExpectedProduct.getUUID())) {
+ isExist = true;
+ k = followedProductes.size();
+ }
+ }
+ assertFalse(isExist);
+ }
+ }
+
+ }
+
+ public static void checkUserFollowedPage(User user, Product... expectedProducts) throws Exception {
+ String component = "products";
+ Boolean isExist;
+ Product expectedProduct;
+ RestResponse getFollowedPage = ProductRestUtils.getFollowed(user.getUserId());
+ JSONArray followedProductes = getListArrayFromRestResponse(getFollowedPage, component);
+ assertTrue("check if any followedProductes received ", followedProductes != null);
+ assertTrue("check if any expectedProducts and followedProductes are the same size",
+ expectedProducts.length == followedProductes.size());
+ for (int i = 0; i < expectedProducts.length; i++) {
+ expectedProduct = expectedProducts[i];
+ isExist = false;
+ for (int k = 0; k < followedProductes.size(); k++) {
+ JSONObject jobject = (JSONObject) followedProductes.get(k);
+ // if(jobject.get("uuid").toString().equals(expectedProduct.getUUID()))
+ if (jobject.get("uniqueId").toString().equals(expectedProduct.getUniqueId())) {
+
+ String productString = jobject.toJSONString();
+ Product actualProduct = ResponseParser.parseToObjectUsingMapper(productString, Product.class);
+ ProductValidationUtils.compareExpectedAndActualProducts(expectedProduct, actualProduct, null);
+ isExist = true;
+ k = followedProductes.size();
+ }
+ }
+ assertTrue(isExist);
+ }
+ }
+
+ private static JSONArray getListArrayFromRestResponse(RestResponse restResponse, String lst) {
+ String json = restResponse.getResponse();
+ JSONObject jsonResp = (JSONObject) JSONValue.parse(json);
+ JSONArray resources = (JSONArray) jsonResp.get(lst);
+ return resources;
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ResourceValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ResourceValidationUtils.java
new file mode 100644
index 0000000000..a3440df8ad
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ResourceValidationUtils.java
@@ -0,0 +1,354 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.enums.RespJsonKeysEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+
+import com.google.gson.Gson;
+
+public class ResourceValidationUtils {
+
+ public static void validateResourceReqVsResp(ResourceReqDetails resourceDetails,
+ ResourceRespJavaObject resourceRespJavaObject) {
+
+ String expected;
+
+ expected = resourceDetails.getDescription();
+ assertEquals("resource description - ", expected, resourceRespJavaObject.getDescription());
+
+ expected = resourceDetails.getVendorName();
+ assertEquals("resource vendorName - ", expected, resourceRespJavaObject.getVendorName());
+
+ expected = resourceDetails.getVendorRelease();
+ assertEquals("resource vendorReleaseName - ", expected, resourceRespJavaObject.getVendorRelease());
+
+ expected = resourceDetails.getContactId();
+ assertEquals("resource contactId - ", expected, resourceRespJavaObject.getContactId());
+
+ }
+
+ public static void validateResourceReqVsResp(ResourceReqDetails resourceDetails, Resource resourceRespJavaObject) {
+
+ String expected;
+
+ expected = resourceDetails.getDescription();
+ assertEquals("resource description - ", expected, resourceRespJavaObject.getDescription());
+
+ expected = resourceDetails.getVendorName();
+ assertEquals("resource vendorName - ", expected, resourceRespJavaObject.getVendorName());
+
+ expected = resourceDetails.getVendorRelease();
+ assertEquals("resource vendorReleaseName - ", expected, resourceRespJavaObject.getVendorRelease());
+
+ expected = resourceDetails.getContactId();
+ assertEquals("resource contactId - ", expected, resourceRespJavaObject.getContactId());
+
+ // Validating deduplication of tags
+ List<String> expectedTags = resourceDetails.getTags();
+ if (expectedTags != null) {
+ Set<String> hs = new LinkedHashSet<>(expectedTags);
+ expectedTags.clear();
+ expectedTags.addAll(hs);
+ List<String> receivedTags = resourceRespJavaObject.getTags();
+ assertEquals("resource tags - ", expectedTags, receivedTags);
+ }
+
+ }
+
+ public static void validateModelObjects(Resource expected, Resource actual) throws Exception {
+
+ compareElements(expected.getUniqueId(), actual.getUniqueId());
+ compareElements(expected.getName(), actual.getName());
+ compareElements(expected.getVersion(), actual.getVersion());
+ compareElements(expected.getCreatorUserId(), actual.getCreatorUserId());
+ compareElements(expected.getCreatorFullName(), actual.getCreatorFullName());
+ compareElements(expected.getLastUpdaterUserId(), actual.getLastUpdaterUserId());
+ compareElements(expected.getLastUpdaterFullName(), actual.getLastUpdaterFullName());
+ compareElements(expected.getCreatorFullName(), actual.getCreatorFullName());
+ compareElements(expected.getCreationDate(), actual.getCreationDate());
+ compareElements(expected.getLastUpdateDate(), actual.getLastUpdateDate());
+ compareElements(expected.getDescription(), actual.getDescription());
+ compareElements(expected.getIcon(), actual.getIcon());
+ compareElements(expected.getLastUpdateDate(), actual.getLastUpdateDate());
+ // TODO compare tags
+ compareElements(expected.getCategories(), actual.getCategories());
+ compareElements(expected.getLifecycleState(), actual.getLifecycleState());
+ compareElements(expected.getVendorName(), actual.getVendorName());
+ compareElements(expected.getVendorRelease(), actual.getVendorRelease());
+ compareElements(expected.getContactId(), actual.getContactId());
+ compareElements(expected.getUUID(), actual.getUUID());
+ compareElements(expected.getVersion(), actual.getVersion());
+
+ }
+
+ public static void validateResp(RestResponse restResponse, ResourceRespJavaObject resourceRespJavaObject)
+ throws Exception {
+
+ Gson gson = new Gson();
+ String response = restResponse.getResponse();
+
+ validateResp(response, resourceRespJavaObject, gson);
+
+ }
+
+ public static void validateResp(String response, ResourceRespJavaObject resourceRespJavaObject, Gson gson) {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map = (Map<String, Object>) gson.fromJson(response, map.getClass());
+
+ // De-duplicating the tags list for comparison
+ List<String> tags = resourceRespJavaObject.getTags();
+ if (tags != null) {
+ Set<String> hs = new LinkedHashSet<>(tags);
+ tags = new ArrayList<String>(hs);
+ resourceRespJavaObject.setTags(tags);
+ tags = new ArrayList<String>(hs);
+ resourceRespJavaObject.setTags(tags);
+ }
+
+ validateField(map, RespJsonKeysEnum.RESOURCE_NAME.getRespJsonKeyName(), resourceRespJavaObject.getName());
+ validateField(map, RespJsonKeysEnum.RESOURCE_DESC.getRespJsonKeyName(),
+ resourceRespJavaObject.getDescription());
+ // validateField(map, RespJsonKeysEnum.CATEGORIES.getRespJsonKeyName(),
+ // resourceRespJavaObject.getCategories());
+ validateField(map, RespJsonKeysEnum.VENDOR_NAME.getRespJsonKeyName(), resourceRespJavaObject.getVendorName());
+ validateField(map, RespJsonKeysEnum.VENDOR_RELEASE.getRespJsonKeyName(),
+ resourceRespJavaObject.getVendorRelease());
+ validateField(map, RespJsonKeysEnum.CONTACT_ID.getRespJsonKeyName(), resourceRespJavaObject.getContactId());
+ validateField(map, RespJsonKeysEnum.ICON.getRespJsonKeyName(), resourceRespJavaObject.getIcon());
+ validateField(map, RespJsonKeysEnum.IS_ABSTRACT.getRespJsonKeyName(),
+ Boolean.valueOf(resourceRespJavaObject.getAbstractt()));
+ validateField(map, RespJsonKeysEnum.HIGHEST_VERSION.getRespJsonKeyName(),
+ Boolean.valueOf(resourceRespJavaObject.getIsHighestVersion()));
+ validateField(map, RespJsonKeysEnum.UNIQUE_ID.getRespJsonKeyName(), resourceRespJavaObject.getUniqueId());
+ validateField(map, RespJsonKeysEnum.RESOURCE_VERSION.getRespJsonKeyName(), resourceRespJavaObject.getVersion());
+ validateField(map, RespJsonKeysEnum.LIFE_CYCLE_STATE.getRespJsonKeyName(),
+ resourceRespJavaObject.getLifecycleState());
+ validateField(map, RespJsonKeysEnum.TAGS.getRespJsonKeyName(), tags);
+ validateField(map, RespJsonKeysEnum.CREATOR_USER_ID.getRespJsonKeyName(),
+ resourceRespJavaObject.getCreatorUserId());
+ validateField(map, RespJsonKeysEnum.CREATOR_FULL_NAME.getRespJsonKeyName(),
+ resourceRespJavaObject.getCreatorFullName());
+ validateField(map, RespJsonKeysEnum.LAST_UPDATER_ATT_UID.getRespJsonKeyName(),
+ resourceRespJavaObject.getLastUpdaterUserId());
+ validateField(map, RespJsonKeysEnum.LAST_UPDATER_FULL_NAME.getRespJsonKeyName(),
+ resourceRespJavaObject.getLastUpdaterFullName());
+ validateField(map, RespJsonKeysEnum.COST.getRespJsonKeyName(), resourceRespJavaObject.getCost());
+ validateField(map, RespJsonKeysEnum.LICENSE_TYPE.getRespJsonKeyName(), resourceRespJavaObject.getLicenseType());
+ validateField(map, RespJsonKeysEnum.RESOURCE_TYPE.getRespJsonKeyName(),
+ resourceRespJavaObject.getResourceType().toString());
+ if (resourceRespJavaObject.getResourceType().equals("VF")) {
+ validateField(map, RespJsonKeysEnum.DERIVED_FROM.getRespJsonKeyName(), null);
+ } else {
+ validateField(map, RespJsonKeysEnum.DERIVED_FROM.getRespJsonKeyName(),
+ resourceRespJavaObject.getDerivedFrom());
+ }
+
+ validateCategories(resourceRespJavaObject, map);
+
+ String uuid = ResponseParser.getValueFromJsonResponse(response, RespJsonKeysEnum.UUID.getRespJsonKeyName());
+ assertTrue("UUID is empty", uuid != null && !uuid.isEmpty());
+ }
+
+ private static void validateCategories(ResourceRespJavaObject resourceRespJavaObject, Map<String, Object> map) {
+ assertTrue(RespJsonKeysEnum.CATEGORIES.getRespJsonKeyName() + " is missing",
+ map.containsKey(RespJsonKeysEnum.CATEGORIES.getRespJsonKeyName()));
+ Object foundValue = map.get(RespJsonKeysEnum.CATEGORIES.getRespJsonKeyName());
+ List<Map<String, Object>> foundList = (List<Map<String, Object>>) foundValue;
+ List<CategoryDefinition> excpectedList = resourceRespJavaObject.getCategories();
+
+ assertTrue(foundList.size() == excpectedList.size());
+ for (int i = 0; i < foundList.size(); i++) {
+ CategoryDefinition expCat = excpectedList.get(i);
+ Map<String, Object> foun = foundList.get(i);
+ assertTrue("expected " + expCat.getName() + " not equal to actual " + foundValue,
+ foun.get("name").equals(expCat.getName()));
+ }
+ }
+
+ public static void validateField(Map<String, Object> map, String jsonField, Object expectedValue) {
+ if (expectedValue == null) {
+ assertTrue(jsonField + " is expected to be null", !map.containsKey(jsonField));
+ } else {
+ assertTrue(jsonField + " is missing", map.containsKey(jsonField));
+ Object foundValue = map.get(jsonField);
+ compareElements(expectedValue, foundValue);
+ }
+ }
+
+ public static void compareElements(Object expectedValue, Object foundValue) {
+ if (expectedValue instanceof String) {
+ assertTrue(foundValue instanceof String);
+ assertTrue("expected " + expectedValue + " not equal to actual " + foundValue,
+ foundValue.equals(expectedValue));
+ }
+ else if (expectedValue instanceof Boolean) {
+ assertTrue(foundValue instanceof Boolean);
+ assertTrue(foundValue == expectedValue);
+ } else if (expectedValue instanceof Map) {
+ assertTrue(foundValue instanceof Map);
+ Map<String, Object> foundMap = (Map<String, Object>) foundValue;
+ Map<String, Object> excpectedMap = (Map<String, Object>) expectedValue;
+ assertTrue(foundMap.size() == excpectedMap.size());
+ Iterator<String> foundkeyItr = foundMap.keySet().iterator();
+ while (foundkeyItr.hasNext()) {
+ String foundKey = foundkeyItr.next();
+ assertTrue(excpectedMap.containsKey(foundKey));
+ compareElements(excpectedMap.get(foundKey), foundMap.get(foundKey));
+ }
+
+ } else if (expectedValue instanceof List) {
+ assertTrue(foundValue instanceof List);
+ List<Object> foundList = (List<Object>) foundValue;
+ List<Object> excpectedList = (List<Object>) expectedValue;
+ assertTrue(foundList.size() == excpectedList.size());
+ for (int i = 0; i < foundList.size(); i++) {
+ compareElements(excpectedList.get(i), foundList.get(i));
+ }
+
+ } else {
+ assertTrue(foundValue.equals(expectedValue));
+ }
+ }
+
+ public static boolean validateUuidAfterChangingStatus(String oldUuid, String newUuid) {
+ return oldUuid.equals(newUuid);
+
+ }
+
+ public static void validateRespArt(RestResponse restResponse, ResourceRespJavaObject resourceRespJavaObject,
+ String interfaze) throws Exception {
+
+ Gson gson = new Gson();
+ String response = restResponse.getResponse();
+
+ Map<String, Object> map = new HashMap<String, Object>();
+ map = (Map<String, Object>) gson.fromJson(response, map.getClass());
+
+ Resource resource = gson.fromJson(response, Resource.class);
+
+ Map<String, ArtifactDefinition> artifacts = resource.getArtifacts();
+ Map<String, InterfaceDefinition> interfaces = null;
+
+ if (interfaze != null) {
+ interfaces = resource.getInterfaces();
+ Map<String, Operation> operation = interfaces.get(interfaze).getOperations();
+ // operation.get("configure").getUniqueId();
+ }
+
+ validateField(map, RespJsonKeysEnum.RESOURCE_NAME.getRespJsonKeyName(), resourceRespJavaObject.getName());
+ validateField(map, RespJsonKeysEnum.RESOURCE_DESC.getRespJsonKeyName(),
+ resourceRespJavaObject.getDescription());
+ // validateField(map, RespJsonKeysEnum.CATEGORIES.getRespJsonKeyName(),
+ // resourceRespJavaObject.getCategories());
+ validateField(map, RespJsonKeysEnum.DERIVED_FROM.getRespJsonKeyName(), resourceRespJavaObject.getDerivedFrom());
+ validateField(map, RespJsonKeysEnum.VENDOR_NAME.getRespJsonKeyName(), resourceRespJavaObject.getVendorName());
+ validateField(map, RespJsonKeysEnum.VENDOR_RELEASE.getRespJsonKeyName(),
+ resourceRespJavaObject.getVendorRelease());
+ validateField(map, RespJsonKeysEnum.CONTACT_ID.getRespJsonKeyName(), resourceRespJavaObject.getContactId());
+ validateField(map, RespJsonKeysEnum.ICON.getRespJsonKeyName(), resourceRespJavaObject.getIcon());
+ validateField(map, RespJsonKeysEnum.IS_ABSTRACT.getRespJsonKeyName(),
+ Boolean.valueOf(resourceRespJavaObject.getAbstractt()));
+ validateField(map, RespJsonKeysEnum.HIGHEST_VERSION.getRespJsonKeyName(),
+ Boolean.valueOf(resourceRespJavaObject.getIsHighestVersion()));
+ validateField(map, RespJsonKeysEnum.UNIQUE_ID.getRespJsonKeyName(), resourceRespJavaObject.getUniqueId());
+ validateField(map, RespJsonKeysEnum.RESOURCE_VERSION.getRespJsonKeyName(), resourceRespJavaObject.getVersion());
+ validateField(map, RespJsonKeysEnum.LIFE_CYCLE_STATE.getRespJsonKeyName(),
+ resourceRespJavaObject.getLifecycleState());
+ validateField(map, RespJsonKeysEnum.TAGS.getRespJsonKeyName(), resourceRespJavaObject.getTags());
+ validateField(map, RespJsonKeysEnum.CREATOR_USER_ID.getRespJsonKeyName(),
+ resourceRespJavaObject.getCreatorUserId());
+ validateField(map, RespJsonKeysEnum.CREATOR_FULL_NAME.getRespJsonKeyName(),
+ resourceRespJavaObject.getCreatorFullName());
+ validateField(map, RespJsonKeysEnum.LAST_UPDATER_ATT_UID.getRespJsonKeyName(),
+ resourceRespJavaObject.getLastUpdaterUserId());
+ validateField(map, RespJsonKeysEnum.LAST_UPDATER_FULL_NAME.getRespJsonKeyName(),
+ resourceRespJavaObject.getLastUpdaterFullName());
+
+ // validate number of artifacts
+ if (resourceRespJavaObject.getArtifacts() != null) {
+
+ // assertEquals("check number of artifacts",
+ // resourceRespJavaObject.getArtifacts().size(), artifacts.size());
+ int iterNum = -1;
+ ArrayList<String> myArtifacats = new ArrayList<String>();
+ Iterator it = artifacts.entrySet().iterator();
+ while (it.hasNext()) {
+ iterNum++;
+ Map.Entry pair = (Map.Entry) it.next();
+ // System.out.println(pair.getKey() + " = " + pair.getValue());
+ ArtifactDefinition myArtifact = artifacts.get(pair.getKey());
+ myArtifacats.add(myArtifact.getEsId());
+ it.remove(); // avoids a ConcurrentModificationException
+ }
+ // assertTrue("check service contains
+ // artifacts",myArtifacats.containsAll(resourceRespJavaObject.getArtifacts()));
+ }
+
+ // validate number of interfaces:
+
+ if (interfaze != null) {
+ assertEquals("check number of interfaces", resourceRespJavaObject.getInterfaces().size(),
+ interfaces.size());
+ }
+
+ }
+
+ public static boolean validateResourceIsAbstartct(List<Resource> resourceList, Boolean bool) {
+ if (resourceList != null && resourceList.size() > 0) {
+ for (Resource resource : resourceList) {
+ if (resource.isAbstract().equals(bool))
+ continue;
+ else
+ return false;
+ }
+ } else
+ return false;
+ return true;
+ }
+
+ public static void validateResourceVersion(Resource resource, String expectedVersion) {
+ if (resource != null && !resource.equals("")) {
+ assertTrue("expected resource version is: " + expectedVersion + ", but actual is: " + resource.getVersion(),
+ resource.getVersion().equals(expectedVersion));
+ }
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ServiceValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ServiceValidationUtils.java
new file mode 100644
index 0000000000..19bd7b9488
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/ServiceValidationUtils.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+
+public class ServiceValidationUtils {
+
+ public static void validateServiceResponseMetaData(ServiceReqDetails serviceDetails, Service service, User user,
+ LifecycleStateEnum lifecycleState) {
+ validateServiceResponseMetaData(serviceDetails, service, user, user, lifecycleState);
+ }
+
+ public static void validateServiceResponseMetaData(ServiceReqDetails expectedService, Service service,
+ User creatorUser, User updaterUser, LifecycleStateEnum lifeCycleState) {
+ List<String> expectedTags = expectedService.getTags();
+ expectedTags.add(expectedService.getName());
+ List<String> receivedTags = service.getTags();
+ if (expectedTags != null) {
+ Set<String> hs = new LinkedHashSet<>(expectedTags);
+ expectedTags.clear();
+ expectedTags.addAll(hs);
+ }
+
+ assertEquals("Check service name on response after create service", expectedService.getName(),
+ service.getName());
+ // check size of list
+ assertEquals("Check only 1 category returned on response after create service", 1,
+ expectedService.getCategories().size());
+ assertEquals("Check service name on response after create service", expectedService.getName(),
+ service.getName());
+ assertEquals("Check categories on response after create service",
+ expectedService.getCategories().get(0).getName(), service.getCategories().get(0).getName());
+ assertEquals("Check tag list on response after create service", expectedTags, receivedTags);
+ assertEquals("Check description on response after create service", expectedService.getDescription(),
+ service.getDescription());
+ // assertEquals("Check vendor name on response after create service",
+ // expectedService.getVendorName(), service.getVendorName());
+ // assertEquals("Check vendor release on response after create service",
+ // expectedService.getVendorRelease(), service.getVendorRelease());
+ assertEquals("Check attContant name on response after create service",
+ expectedService.getContactId().toLowerCase(), service.getContactId());
+ assertEquals("Check icon name on response after create service", expectedService.getIcon(), service.getIcon());
+ assertEquals("Check LastUpdaterUserId after create service", updaterUser.getUserId(), service.getLastUpdaterUserId());
+ assertEquals("Check LastUpdaterName after create service",
+ updaterUser.getFirstName() + " " + updaterUser.getLastName(), service.getLastUpdaterFullName());
+ assertEquals("Check CreatorUserId after create service", creatorUser.getUserId(), service.getCreatorUserId());
+ assertEquals("Check CreatorName after create service",
+ creatorUser.getFirstName() + " " + creatorUser.getLastName(), service.getCreatorFullName());
+ assertEquals("Check version after create service", expectedService.getVersion(), service.getVersion());
+ // assertEquals("Check UniqueId after create service", SERVICE_PREFIX +
+ // serviceDetails.getServiceName().toLowerCase()+"." +
+ // serviceBaseVersion, service.getUniqueId());
+ assertFalse("Check uuid after create service", service.getUUID().isEmpty());
+
+ // assertTrue("check creation date after create service",
+ // service.getCreationDate() != null);
+ // assertTrue("check update date after create service",
+ // service.getLastUpdateDate() != null);
+
+ if (lifeCycleState != null)
+ assertEquals("Check LifecycleState after create service", lifeCycleState, service.getLifecycleState());
+ else
+ assertEquals("Check LifecycleState after create service", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ service.getLifecycleState());
+ }
+
+ public static ExpectedResourceAuditJavaObject constructFieldsForAuditValidation(ServiceReqDetails serviceReqDetails,
+ String serviceVersion, User sdncUserDetails) {
+
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = new ExpectedResourceAuditJavaObject();
+
+ expectedResourceAuditJavaObject.setAction("Create");
+ expectedResourceAuditJavaObject.setModifierUid(sdncUserDetails.getUserId());
+ String userFirstLastName = sdncUserDetails.getFirstName() + " " + sdncUserDetails.getLastName();
+ expectedResourceAuditJavaObject.setModifierName(userFirstLastName);
+ expectedResourceAuditJavaObject.setStatus("200");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ expectedResourceAuditJavaObject.setResourceName(serviceReqDetails.getName());
+ expectedResourceAuditJavaObject.setResourceType("Service");
+ expectedResourceAuditJavaObject.setPrevVersion(String.valueOf(Float.parseFloat(serviceVersion) - 0.1f));
+ expectedResourceAuditJavaObject.setCurrVersion(serviceVersion);
+ expectedResourceAuditJavaObject.setPrevState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setCurrState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ expectedResourceAuditJavaObject.setComment(null);
+
+ return expectedResourceAuditJavaObject;
+
+ }
+
+ public static void validateDistrubtionStatusValue(RestResponse response,
+ DistributionStatusEnum expectedDistributionValue) throws Exception {
+ String actualDistributionValue = ResponseParser.getValueFromJsonResponse(response.getResponse(),
+ "distributionStatus");
+ assertEquals(expectedDistributionValue.name(), actualDistributionValue);
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/UserValidationUtils.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/UserValidationUtils.java
new file mode 100644
index 0000000000..a75434edcf
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/utils/validation/UserValidationUtils.java
@@ -0,0 +1,279 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utils.validation;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.users.AddUserAuditMessageInfo;
+import org.openecomp.sdc.ci.tests.users.UserAuditJavaObject;
+import org.openecomp.sdc.ci.tests.utils.DbUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class UserValidationUtils {
+ public static void compareExpectedAndActualUsers(User expected, User actual) {
+
+ String actualFirstName = actual.getFirstName();
+ String expectedFirstName = expected.getFirstName();
+ assertEquals("check user first name - ", expectedFirstName, actualFirstName);
+
+ String actualLastName = actual.getLastName();
+ String expectedLastName = expected.getLastName();
+ assertEquals("check user last name - ", expectedLastName, actualLastName);
+
+ String actualUserId = actual.getUserId();
+ String expectedUserId = expected.getUserId();
+ assertEquals("check user userId - ", expectedUserId, actualUserId);
+
+ String actualEmail = actual.getEmail();
+ String expectedEmail = expected.getEmail();
+ assertEquals("check user email - ", expectedEmail, actualEmail);
+
+ Long actualLastLoginTime = actual.getLastLoginTime();
+ Long expectedLastLoginTime = expected.getLastLoginTime();
+ assertEquals("check user last login time - ", expectedLastLoginTime, actualLastLoginTime);
+
+ String actualRole = actual.getRole();
+ if (expected.getRole() == null) {
+ String expectedRole = UserRoleEnum.DESIGNER.name();
+ assertEquals("check user role - ", expectedRole, actualRole);
+ } else {
+ String expectedRole = expected.getRole();
+ assertEquals("check user role - ", expectedRole, actualRole);
+ }
+
+ UserStatusEnum actualStatus = expected.getStatus();
+ UserStatusEnum expectedStatus = expected.getStatus();
+ assertEquals("check user status - ", expectedStatus, actualStatus);
+ }
+
+ public static void validateDeleteUserAuditMessage(User sdncUserDetails, User sdncModifierDetails,
+ String responseCode, String responseMessage, AddUserAuditMessageInfo addUserAuditMessageInfo) {
+ String action = "DeleteUser";
+ validateUserAuditMessage(sdncUserDetails, sdncModifierDetails, responseCode, responseMessage,
+ addUserAuditMessageInfo, action);
+
+ }
+
+ private static void validateUserAuditMessage(User sdncUserDetails, User sdncModifierDetails, String responseCode,
+ String responseMessage, AddUserAuditMessageInfo addUserAuditMessageInfo, String expectedAction) {
+
+ assertEquals("check audit action - ", expectedAction, addUserAuditMessageInfo.getACTION());
+
+ // String expectedModifierFirstLastName =
+ // sdncModifierDetails.getFirstName() + " " +
+ // sdncModifierDetails.getLastName();
+ // assertEquals("check audit modifier name - ",
+ // expectedModifierFirstLastName,
+ // addUserAuditMessageInfo.getMODIFIER_NAME());
+ String fullName = sdncModifierDetails.getFullName();
+ if (sdncModifierDetails.getFullName().equals(" ")) {
+ fullName = "";
+ }
+ String expectedModifierId = fullName + "(" + sdncModifierDetails.getUserId() + ")";
+ assertEquals("check audit modifier uid - ", expectedModifierId, addUserAuditMessageInfo.getMODIFIER());
+
+ String expectedUserFirstLastName = sdncUserDetails.getFirstName() + " " + sdncUserDetails.getLastName();
+ if (expectedUserFirstLastName.equals("null null")) {
+ expectedUserFirstLastName = "";
+ }
+
+ String email = (sdncUserDetails.getEmail() == null) ? "" : sdncUserDetails.getEmail();
+ String role = (sdncUserDetails.getRole() == null) ? "DESIGNER" : sdncUserDetails.getRole();
+
+ String formatedUser = String.format("%s,%s,%s,%s", sdncUserDetails.getUserId(), expectedUserFirstLastName,
+ email, role);
+
+ //
+ // String expectedUserFirstLastName = sdncUserDetails.getFirstName() + "
+ // " + sdncUserDetails.getLastName();
+ // if (expectedUserFirstLastName.equals("null null")) {
+ // expectedUserFirstLastName = "";
+ // }
+ //
+ // String expectedUserFirstLastName = "";
+ // expectedUserFirstLastName += sdncUserDetails.getFirstName() == null ?
+ // "" : sdncUserDetails.getFirstName();
+ // String lastName = sdncUserDetails.getLastName() == null ? "" :
+ // sdncUserDetails.getLastName();
+ // if (expectedUserFirstLastName.isEmpty()) {
+ // expectedUserFirstLastName = lastName;
+ // } else {
+ // expectedUserFirstLastName += " " + lastName;
+ // }
+
+ assertEquals("check audit user name - ", formatedUser, addUserAuditMessageInfo.getUSER());
+
+ // String expectedUserUid = sdncUserDetails.getUserId();
+ // assertEquals("check audit user uid - ", expectedUserUid,
+ // addUserAuditMessageInfo.getUSER_UID());
+ //
+ // String expectedUserEmail = sdncUserDetails.getEmail() == null ? "" :
+ // sdncUserDetails.getEmail();
+ // //TODO: esofer check with Andrey. Audit return "" but in user we have
+ // null
+ // assertEquals("check audit user email - ", expectedUserEmail,
+ // addUserAuditMessageInfo.getUSER_EMAIL());
+ //
+ // String expectedUserRole = sdncUserDetails.getRole();
+ // if (expectedUserRole ==null){
+ // expectedUserRole = "DESIGNER";
+ // assertEquals("check audit user role - ", expectedUserRole,
+ // addUserAuditMessageInfo.getUSER_ROLE());
+ // }else{
+ // assertEquals("check audit user role - ", expectedUserRole,
+ // addUserAuditMessageInfo.getUSER_ROLE());
+ // }
+
+ String expectedUserResponseCode = responseCode;
+ assertEquals("check audit user response code - ", expectedUserResponseCode,
+ addUserAuditMessageInfo.getSTATUS());
+
+ String expectedUserResponseMessage = responseMessage;
+ assertEquals("check audit user response message - ", expectedUserResponseMessage,
+ addUserAuditMessageInfo.getDESC());
+
+ }
+
+ public static void validateDataAgainstAuditDB_access(User sdncUserDetails, UserAuditJavaObject auditJavaObject,
+ RestResponse restResponse, ErrorInfo errorInfo, List<String> variables) {
+
+ validateAuditDataAgainstAuditDbInAccess(sdncUserDetails, auditJavaObject, restResponse, errorInfo, variables);
+
+ }
+
+ public static void validateAuditDataAgainstAuditDbInAccess(User sdncUserDetails,
+ UserAuditJavaObject auditJavaObject, RestResponse restResponse, ErrorInfo errorInfo,
+ List<String> variables) {
+
+ String expected;
+
+ expected = "Access";
+ assertEquals("ACTION- ", expected, auditJavaObject.getACTION());
+
+ if (sdncUserDetails.getFirstName() != StringUtils.EMPTY && sdncUserDetails.getLastName() != StringUtils.EMPTY) {
+ expected = sdncUserDetails.getFirstName() + " " + sdncUserDetails.getLastName();
+ } else {
+ expected = StringUtils.EMPTY;
+ }
+ String formatedUser = String.format("%s(%s)", expected, sdncUserDetails.getUserId());
+ assertTrue(
+ "check audit user: expected start with - " + formatedUser + " ,actual - " + auditJavaObject.getUSER(),
+ auditJavaObject.getUSER().startsWith(formatedUser));
+
+ expected = restResponse.getErrorCode().toString();
+ assertEquals("check audit user status code - ", expected, auditJavaObject.getSTATUS());
+
+ if (restResponse.getErrorCode() == 200 || restResponse.getErrorCode() == 201) {
+ expected = errorInfo.getMessage();
+ } else {
+ expected = AuditValidationUtils.buildAuditDescription(errorInfo, variables);
+ }
+
+ assertEquals("check audit user desc - ", expected, auditJavaObject.getDESC());
+
+ // expected = sdncUserDetails.getUserId();
+ // assertEquals(expected, auditJavaObject.getUSER());
+
+ }
+
+ public static void validateUserDetailsOnResponse(User sdncUserDetails, String userDetailsOnResponse) {
+
+ String actualFirstName = Utils.getJsonObjectValueByKey(userDetailsOnResponse, "firstName");
+ String expectedFirstName = sdncUserDetails.getFirstName();
+ assertEquals("check user first name - ", expectedFirstName, actualFirstName);
+
+ String actualLastName = Utils.getJsonObjectValueByKey(userDetailsOnResponse, "lastName");
+ String expectedLastName = sdncUserDetails.getLastName();
+ assertEquals("check user last name - ", expectedLastName, actualLastName);
+
+ String actualUserId = Utils.getJsonObjectValueByKey(userDetailsOnResponse, "userId");
+ String expectedUserId = sdncUserDetails.getUserId();
+ assertEquals("check user userId - ", expectedUserId, actualUserId);
+
+ String actualEmail = Utils.getJsonObjectValueByKey(userDetailsOnResponse, "email");
+ String expectedEmail = sdncUserDetails.getEmail();
+ assertEquals("check user email - ", expectedEmail, actualEmail);
+
+ String actualRole = Utils.getJsonObjectValueByKey(userDetailsOnResponse, "role");
+ if (sdncUserDetails.getRole() == null) {
+ String expectedRole = UserRoleEnum.DESIGNER.name();
+ assertEquals("check user role - ", expectedRole, actualRole);
+ } else {
+ String expectedRole = sdncUserDetails.getRole();
+ assertEquals("check user role - ", expectedRole, actualRole);
+ }
+
+ String actualStatus = Utils.getJsonObjectValueByKey(userDetailsOnResponse, "status");
+ String expectedStatus = sdncUserDetails.getStatus().name();
+ assertEquals("check user status - ", expectedStatus, actualStatus);
+
+ }
+
+ public static AddUserAuditMessageInfo getAddUserAuditMessage(String action) throws Exception {
+
+ Gson gson = new Gson();
+ String index = "auditingevents*";
+ String type = "useradminevent";
+ String pattern = "/_search?q=ACTION:\"" + action + "\"";
+ String auditingMessage = DbUtils.retrieveAuditMessagesByPattern(pattern);
+ // String auditingMessage = retrieveAuditMessageByIndexType(index, type,
+ // pattern);
+ JsonElement jElement = new JsonParser().parse(auditingMessage);
+ JsonObject jObject = jElement.getAsJsonObject();
+ JsonObject hitsObject = (JsonObject) jObject.get("hits");
+ JsonArray hitsArray = (JsonArray) hitsObject.get("hits");
+ // for (int i = 0; i < hitsArray.size();){
+ if (hitsArray != null) {
+ JsonObject jHitObject = (JsonObject) hitsArray.get(0);
+ JsonObject jSourceObject = (JsonObject) jHitObject.get("_source");
+ AddUserAuditMessageInfo addUserAuditMessageInfo = new AddUserAuditMessageInfo();
+ addUserAuditMessageInfo = gson.fromJson(jSourceObject, AddUserAuditMessageInfo.class);
+ return addUserAuditMessageInfo;
+ }
+ return null;
+
+ }
+
+ public static void validateAddUserAuditMessage(User sdncUserDetails, User sdncModifierDetails, String responseCode,
+ String responseMessage, AddUserAuditMessageInfo addUserAuditMessageInfo) {
+
+ String action = "AddUser";
+ validateUserAuditMessage(sdncUserDetails, sdncModifierDetails, responseCode, responseMessage,
+ addUserAuditMessageInfo, action);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/webSealAccess/NeoJavaObject.java b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/webSealAccess/NeoJavaObject.java
new file mode 100644
index 0000000000..0c19627bda
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/ci/tests/webSealAccess/NeoJavaObject.java
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.webSealAccess;
+
+public class NeoJavaObject {
+
+ String userId;
+ String firstName;
+ String lastName;
+ String email;
+ String role;
+ String label;
+ String elementType;
+
+ public NeoJavaObject(String userId, String firstName, String lastName, String email, String role, String label,
+ String elementType) {
+ super();
+ this.userId = userId;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.email = email;
+ this.role = role;
+ this.label = label;
+ this.elementType = elementType;
+ }
+
+ public NeoJavaObject() {
+ super();
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getElementType() {
+ return elementType;
+ }
+
+ public void setElementType(String elementType) {
+ this.elementType = elementType;
+ }
+
+ @Override
+ public String toString() {
+ return "QueryUserNeo4jInfo [userId=" + userId + ", firstName=" + firstName + ", lastName=" + lastName
+ + ", email=" + email + ", role=" + role + ", label=" + label + ", elementType=" + elementType + "]";
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetAssetServlet.java b/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetAssetServlet.java
new file mode 100644
index 0000000000..8ea8524489
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetAssetServlet.java
@@ -0,0 +1,351 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.externalApis;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+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.HttpClients;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.api.Urls;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedExternalAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.AssetRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.gson.Gson;
+
+public class GetAssetServlet extends ComponentBaseTest {
+
+ protected User sdncUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+ protected User sdncAdminUserDetails = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+ protected ConsumerDataDefinition consumerDataDefinition;
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetAssetServlet() {
+ super(name, GetAssetServlet.class.getName());
+ }
+
+ Gson gson = new Gson();
+
+ @BeforeMethod
+ public void setup() throws Exception {
+
+ AtomicOperationUtils.createDefaultConsumer(true);
+ // consumerDataDefinition =
+ // ConsumerRestUtils.getDefaultConsumerDetails();
+ // RestResponse deleteResponse =
+ // ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ // sdncAdminUserDetails);
+ // BaseRestUtils.checkStatusCode(deleteResponse, "delete operation
+ // filed", false, 404,200);;
+ //
+ // RestResponse createResponse =
+ // ConsumerRestUtils.createConsumer(consumerDataDefinition,
+ // sdncAdminUserDetails);
+ // BaseRestUtils.checkCreateResponse(createResponse);
+
+ }
+
+ // @AfterMethod
+ // public void tearDown() throws Exception{
+ // RestResponse deleteResponse =
+ // ConsumerRestUtils.deleteConsumer(consumerDataDefinition,
+ // sdncAdminUserDetails);
+ // BaseRestUtils.checkStatusCode(deleteResponse, "delete operation filed",
+ // false, 404,200);
+ //
+ // }
+
+ @Test // (enabled = false)
+ public void getResourceAssetSuccess() throws Exception {
+
+ List<String> expectedAssetNamesList = new ArrayList<>();
+
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("Resource1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails.setName("Resource2");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails.setName("Resource3");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails.setName("Resource4");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails.setName("Resource5");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails.setName("Resource6");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.STARTCERTIFICATION, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails.setName("Resource7");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+ expectedAssetNamesList.add(resource.getName());
+
+ System.out.println("7 VF resources were created");
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ResourceAssetStructure> resourceAssetList = AssetRestUtils.getResourceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getResourceNamesList(resourceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ AssetRestUtils.checkResourceTypeInObjectList(resourceAssetList, ResourceTypeEnum.VF);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_ASSET_LIST);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_ASSET_LIST.getName(), null);
+
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetSuccess() throws Exception {
+
+ List<String> expectedAssetNamesList = new ArrayList<>();
+ ArtifactReqDetails artifactDetails = ElementFactory.getArtifactByType(ArtifactTypeEnum.OTHER, ArtifactTypeEnum.OTHER, true);
+
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setName("Service1");
+ RestResponse createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ Service service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service2");
+ createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ expectedAssetNamesList.add(service.getName());
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service3");
+ createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service4");
+ createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service5");
+ createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service6");
+ createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.STARTCERTIFICATION, true).getLeft();
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service7");
+ createService = ServiceRestUtils.createService(serviceDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ service = (Service) AtomicOperationUtils.changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true).getLeft();
+ expectedAssetNamesList.add(service.getName());
+ expectedAssetNamesList.add(service.getName());
+
+ System.out.println("7 Services were created");
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> serviceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(serviceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_ASSET_LIST);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_ASSET_LIST.getName(), null);
+
+ }
+
+ @Test
+ public void getToscaModelSuccess() throws Exception {
+
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setName("Resource1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails, sdncUserDetails);
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ HttpResponse assetResponse = AssetRestUtils.getComponentToscaModel(AssetTypeEnum.RESOURCES, resource.getUUID());
+ resource = (Resource) AtomicOperationUtils.changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ String artId = resource.getToscaArtifacts().get("assettoscacsar").getEsId();
+ String url = String.format(Urls.UI_DOWNLOAD_RESOURCE_ARTIFACT, config.getCatalogBeHost(), config.getCatalogBePort(), resource.getUniqueId(), artId);
+ HttpGet httpGet = createGetRequest(url);
+ HttpResponse response = httpclient.execute(httpGet);
+ InputStream inputStream = response.getEntity().getContent();
+ ArtifactUiDownloadData artifactUiDownloadData = getArtifactUiDownloadData(IOUtils.toString(inputStream));
+ assetResponse = AssetRestUtils.getComponentToscaModel(AssetTypeEnum.RESOURCES, resource.getUUID());
+ inputStream = assetResponse.getEntity().getContent();
+ int len = (int) assetResponse.getEntity().getContentLength();
+ byte[] res = new byte[len];
+ inputStream.read(res, 0, len);
+ byte[] fromUiDownload = artifactUiDownloadData.getBase64Contents().getBytes();
+ assertEquals(Base64.encodeBase64(res), fromUiDownload);
+ String fileName = assetResponse.getFirstHeader(Constants.CONTENT_DISPOSITION_HEADER).getValue();
+ assertEquals(fileName, new StringBuilder().append("attachment; filename=\"").append(artifactUiDownloadData.getArtifactName()).append("\"").toString());
+ }
+
+ private HttpGet createGetRequest(String url) {
+ HttpGet httpGet = new HttpGet(url);
+ httpGet.addHeader(HttpHeaderEnum.USER_ID.getValue(), sdncUserDetails.getUserId());
+ return httpGet;
+ }
+
+ private ArtifactUiDownloadData getArtifactUiDownloadData(String artifactUiDownloadDataStr) throws Exception {
+
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ ArtifactUiDownloadData artifactUiDownloadData = mapper.readValue(artifactUiDownloadDataStr, ArtifactUiDownloadData.class);
+ return artifactUiDownloadData;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetFilteredAssetServlet.java b/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetFilteredAssetServlet.java
new file mode 100644
index 0000000000..3886c5aa3e
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetFilteredAssetServlet.java
@@ -0,0 +1,438 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.externalApis;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ErrorInfo;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedExternalAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.AssetRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GetFilteredAssetServlet extends ComponentBaseTest {
+
+ protected final static String categoryFilterKey = "category";
+ protected final static String subCategoryFilterKey = "subCategory";
+ protected final static String distributionStatusFilterKey = "distributionStatus";
+ protected final static String serviceKey = "service";
+ protected final static String resourceKey = "resource";
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetFilteredAssetServlet() {
+ super(name, GetFilteredAssetServlet.class.getName());
+ }
+
+ @BeforeMethod
+ public void setup() throws Exception {
+ AtomicOperationUtils.createDefaultConsumer(true);
+ }
+
+ // RESOURCE
+ // Success
+
+ @Test // (enabled = false)
+ public void getResourceAssetBySpecifiedCategory() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + ResourceCategoryEnum.GENERIC_ABSTRACT.getCategory() };
+ List<String> expectedAssetNamesList = new ArrayList<>();
+
+ Resource resource1 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(resource1.getName());
+ Resource resource2 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.DATABASE, ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(resource2.getName());
+ Resource resource3 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.NETWORK_CONNECTIVITY_CON_POINT, UserRoleEnum.DESIGNER, true).left().value();
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ResourceAssetStructure> resourceAssetList = AssetRestUtils.getResourceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getResourceNamesList(resourceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ // 2 resources has the same category and different subcategory
+ @Test // (enabled = false)
+ public void getResourceAssetBySpecifiedCategoryAndSubCategory() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + ResourceCategoryEnum.GENERIC_ABSTRACT.getCategory(), subCategoryFilterKey + "=" + ResourceCategoryEnum.GENERIC_ABSTRACT.getSubCategory() };
+ // String[] filter2 = {categoryFilterKey + "=" +
+ // ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS.getCategory(),
+ // subCategoryFilterKey + "=" +
+ // ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS.getSubCategory()};
+ List<String> expectedAssetNamesList = new ArrayList<>();
+
+ Resource resource1 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(resource1.getName());
+ Resource resource2 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.DATABASE, ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS, UserRoleEnum.DESIGNER, true).left().value();
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> resourceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(resourceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetBySpecifiedSubCategoryAndCategory() throws Exception {
+ String[] filter = { subCategoryFilterKey + "=" + ResourceCategoryEnum.GENERIC_DATABASE.getSubCategory(), categoryFilterKey + "=" + ResourceCategoryEnum.GENERIC_DATABASE.getCategory(), };
+ List<String> expectedAssetNamesList = new ArrayList<>();
+
+ Resource resource1 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.COMPUTE, ResourceCategoryEnum.GENERIC_ABSTRACT, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils.createResourcesByTypeNormTypeAndCatregory(ResourceTypeEnum.VF, NormativeTypesEnum.DATABASE, ResourceCategoryEnum.GENERIC_DATABASE, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(resource2.getName());
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> resourceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(resourceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ // Failure
+ @Test // (enabled = false)
+ public void getResourceAssetCategoryNotExists() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + "NotExistingCategory" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(resourceKey, categoryFilterKey, "NotExistingCategory");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetSubCategoryNotExists() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + ResourceCategoryEnum.GENERIC_ABSTRACT.getCategory(), subCategoryFilterKey + "=" + "NotExistingSubCategory" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList("Resource", "NotExistingSubCategory", ResourceCategoryEnum.GENERIC_ABSTRACT.getCategory());
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetCategoryNotExistsSubCategoryExists() throws Exception {
+ String[] filter = { subCategoryFilterKey + "=" + ResourceCategoryEnum.NETWORK_L2_3_GETEWAY.getSubCategory(), categoryFilterKey + "=" + "NotExistingCategory" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(resourceKey, categoryFilterKey, "NotExistingCategory");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetInvalidFilterKey() throws Exception {
+ String[] filter = { subCategoryFilterKey + "1=" + ResourceCategoryEnum.NETWORK_L2_3_GETEWAY.getSubCategory(), categoryFilterKey + "=" + "NotExistingCategory" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.RESOURCES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_FILTER_KEY.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(subCategoryFilterKey + "1", "[" + subCategoryFilterKey + ", " + categoryFilterKey + "]");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FILTER_KEY.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.RESOURCES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ // ---------------------------------------------------------------------------------------------------------
+ // SERVICE
+ // Success
+ @Test // (enabled = false)
+ public void getServiceAssetBySpecifiedCategory() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + ServiceCategoriesEnum.MOBILITY.getValue() };
+ List<String> expectedAssetNamesList = new ArrayList<>();
+
+ Service service1 = AtomicOperationUtils.createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(service1.getName());
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> serviceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(serviceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetBySpecifiedCategoryAndDistributionStatus() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + ServiceCategoriesEnum.MOBILITY.getValue(), distributionStatusFilterKey + "=" + DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED };
+ List<String> expectedAssetNamesList = new ArrayList<>();
+ ArtifactReqDetails artifactDetails = ElementFactory.getArtifactByType(ArtifactTypeEnum.OTHER, ArtifactTypeEnum.OTHER, true);
+
+ Service service1 = AtomicOperationUtils.createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(service1.getName());
+ Service service2 = AtomicOperationUtils.createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service2.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service2 = (Service) AtomicOperationUtils.changeComponentState(service2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ expectedAssetNamesList.add(service2.getName());
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> serviceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(serviceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetByDistributionStatus() throws Exception {
+ String[] filter = { distributionStatusFilterKey + "=" + DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED };
+ List<String> expectedAssetNamesList = new ArrayList<>();
+ ArtifactReqDetails artifactDetails = ElementFactory.getArtifactByType(ArtifactTypeEnum.OTHER, ArtifactTypeEnum.OTHER, true);
+
+ Service service1 = AtomicOperationUtils.createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ expectedAssetNamesList.add(service1.getName());
+ Service service2 = AtomicOperationUtils.createServiceByCategory(ServiceCategoriesEnum.MOBILITY, UserRoleEnum.DESIGNER, true).left().value();
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service2.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service2 = (Service) AtomicOperationUtils.changeComponentState(service2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ ServiceRestUtils.approveServiceDistribution(service2.getUniqueId(), UserRoleEnum.GOVERNOR.getUserId());
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> serviceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(serviceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ // Failure
+ @Test // (enabled = false)
+ public void getServiceAssetCategoryNotExists() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + "NotExistingCategory" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(serviceKey, categoryFilterKey, "NotExistingCategory");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetInvalidDistributionStatus() throws Exception {
+ String[] filter = { distributionStatusFilterKey + "=" + "NotExistingDistributionStatus" };
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(serviceKey, distributionStatusFilterKey, "NotExistingDistributionStatus");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetCategoryExitsDistributionStatusInvalid() throws Exception {
+ String[] filter = { categoryFilterKey + "=" + ServiceCategoriesEnum.MOBILITY.getValue(), distributionStatusFilterKey + "=" + "NotExistingDistributionStatus" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(serviceKey, distributionStatusFilterKey, "NotExistingDistributionStatus");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetCategoryNotExistsDistributionStatus() throws Exception {
+ String[] filter = { distributionStatusFilterKey + "=" + DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, categoryFilterKey + "=" + "NotExistingCategory" };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(serviceKey, categoryFilterKey, "NotExistingCategory");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetInvalidFilterKey() throws Exception {
+ String[] filter = { distributionStatusFilterKey + "1=" + DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED, categoryFilterKey + "=" + ServiceCategoriesEnum.MOBILITY.getValue() };
+
+ RestResponse assetResponse = AssetRestUtils.getComponentListByAssetType(true, AssetTypeEnum.SERVICES, filter);
+ ErrorInfo errorInfo = ErrorValidationUtils.parseErrorConfigYaml(ActionStatus.INVALID_FILTER_KEY.name());
+
+ assertNotNull("check response object is not null after create resouce", assetResponse);
+ assertNotNull("check error code exists in response after create resource", assetResponse.getErrorCode());
+ assertEquals("Check response code after create resource", errorInfo.getCode(), assetResponse.getErrorCode());
+
+ List<String> variables = Arrays.asList(distributionStatusFilterKey + "1", "[" + categoryFilterKey + ", " + distributionStatusFilterKey + "]");
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.INVALID_FILTER_KEY.name(), variables, assetResponse.getResponse());
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory.getDefaultAssetListAudit(AssetTypeEnum.SERVICES, AuditingActionEnum.GET_FILTERED_ASSET_LIST);
+ expectedAssetListAudit.setRESOURCE_URL(AssetRestUtils.buildUrlWithFilter(expectedAssetListAudit.getRESOURCE_URL(), filter));
+ expectedAssetListAudit.setSTATUS(errorInfo.getCode().toString());
+ expectedAssetListAudit.setDESC(AuditValidationUtils.buildAuditDescription(errorInfo, variables));
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit, AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+
+ }
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetSpecificAssetMetadataServlet.java b/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetSpecificAssetMetadataServlet.java
new file mode 100644
index 0000000000..05d305ad9a
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/externalApis/GetSpecificAssetMetadataServlet.java
@@ -0,0 +1,773 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.externalApis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceDetailedAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceDetailedAssetStructure;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ArtifactTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedExternalAudit;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.general.AtomicOperationUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.AssetRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.BaseRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.LifecycleRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+import org.openecomp.sdc.ci.tests.utils.validation.ErrorValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class GetSpecificAssetMetadataServlet extends ComponentBaseTest {
+ private static Logger log = LoggerFactory.getLogger(GetAssetServlet.class.getName());
+
+ @Rule
+ public static TestName name = new TestName();
+
+ public GetSpecificAssetMetadataServlet() {
+ super(name, GetSpecificAssetMetadataServlet.class.getName());
+ }
+
+ @BeforeMethod
+ public void setup() throws Exception {
+ AtomicOperationUtils.createDefaultConsumer(true);
+ }
+
+ // get specific asset metadata
+
+ // Resource
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataWithResourceInstancesSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = ResponseParser.parseToObjectUsingMapper(
+ ResourceRestUtils.getResource(resourceVF.getUniqueId()).getResponse(), Resource.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataWithNonCertifiedResourceInstancesSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ Resource resource3 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource3 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource3, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource3, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+
+ // certify resource2 and add to VF(VF with resource2 0.1, 1.0 and 1.1
+ // versions)
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = ResponseParser.parseToObjectUsingMapper(
+ ResourceRestUtils.getResource(resourceVF.getUniqueId()).getResponse(), Resource.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataWithResourceInstancesVfInSubmitForTestingSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ Resource resource3 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource3 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource3, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource3, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+
+ // certify resource2 and add to VF(VF with resource2 1.0, 2.0 and 3.0
+ // versions)
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataWithResourceInstancesVfInStartCertificationSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ Resource resource3 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource3 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource3, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource3, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+
+ // certify resource2 and add to VF(VF with resource2 1.0, 2.0 and 3.0
+ // versions)
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.STARTCERTIFICATION, true)
+ .getLeft();
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataWithResourceInstancesCertifiedVFSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ Resource resource3 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource3 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource3, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource3, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+
+ // certify resource2 and add to VF(VF with resource2 1.0, 2.0 and 3.0
+ // versions)
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getResourceAssetMetadataWithNonCertifiedResourceInstancesAndArtifactsSuccess() throws Exception {
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ Resource resource2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ Resource resource3 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VFC, UserRoleEnum.DESIGNER, true).left().value();
+ resource3 = (Resource) AtomicOperationUtils
+ .changeComponentState(resource3, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFICATIONREQUEST, true)
+ .getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource2, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resource3, resourceVF, UserRoleEnum.DESIGNER,
+ true);
+
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.VENDOR_LICENSE, resourceVF, UserRoleEnum.DESIGNER,
+ true, true);
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.APPC_CONFIG, resourceVF, UserRoleEnum.DESIGNER, true,
+ true);
+ resourceVF = ResponseParser.parseToObjectUsingMapper(
+ ResourceRestUtils.getResource(resourceVF.getUniqueId()).getResponse(), Resource.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.RESOURCES,
+ resourceVF.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ResourceDetailedAssetStructure resourceAssetMetadata = AssetRestUtils.getResourceAssetMetadata(assetResponse);
+ AssetRestUtils.resourceMetadataValidatior(resourceAssetMetadata, resourceVF, AssetTypeEnum.RESOURCES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.RESOURCES, resourceVF);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ // Import CSAR
+
+ // Service
+ @Test // (enabled = false)
+ public void getServiceAssetMetadataSuccess() throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.SERVICES,
+ service.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ServiceDetailedAssetStructure serviceAssetMetadata = AssetRestUtils.getServiceAssetMetadata(assetResponse);
+ AssetRestUtils.serviceMetadataValidatior(serviceAssetMetadata, service, AssetTypeEnum.SERVICES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.SERVICES, service);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetMetadataWithResourceInstancesSuccess() throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+ service = ResponseParser.parseToObjectUsingMapper(ServiceRestUtils
+ .getService(service.getUniqueId(), ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER)).getResponse(),
+ Service.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.SERVICES,
+ service.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ServiceDetailedAssetStructure serviceAssetMetadata = AssetRestUtils.getServiceAssetMetadata(assetResponse);
+ AssetRestUtils.serviceMetadataValidatior(serviceAssetMetadata, service, AssetTypeEnum.SERVICES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.SERVICES, service);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetMetadataWithNonCertifiedResourceInstancesWithArtifactsSuccess() throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.VENDOR_LICENSE, resourceVF, UserRoleEnum.DESIGNER,
+ true, true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+
+ Resource resourceVF2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.APPC_CONFIG, resourceVF2, UserRoleEnum.DESIGNER,
+ true, true);
+ resourceVF2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF2, service, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+
+ // service with 0.1, 0.1 and 1.0 RIs versions
+ service = ResponseParser.parseToObjectUsingMapper(ServiceRestUtils
+ .getService(service.getUniqueId(), ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER)).getResponse(),
+ Service.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.SERVICES,
+ service.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ServiceDetailedAssetStructure serviceAssetMetadata = AssetRestUtils.getServiceAssetMetadata(assetResponse);
+ AssetRestUtils.serviceMetadataValidatior(serviceAssetMetadata, service, AssetTypeEnum.SERVICES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.SERVICES, service);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetMetadataWithCertifiedResourceInstancesAndArtifactsOnRIsAndServiceSuccess()
+ throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.VENDOR_LICENSE, resourceVF, UserRoleEnum.DESIGNER,
+ true, true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ Resource resourceVF2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.APPC_CONFIG, resourceVF2, UserRoleEnum.DESIGNER,
+ true, true);
+ resourceVF2 = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF2, service, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.MODEL_INVENTORY_PROFILE, resourceVF,
+ UserRoleEnum.DESIGNER, true, true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+
+ // service with resourceVF 1.0(1 art), 2.0(2 art) versions and
+ // resourceVF2 1.0(1 art), service 1 artifact version
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.MODEL_INVENTORY_PROFILE, service,
+ UserRoleEnum.DESIGNER, true, true);
+ service = ResponseParser.parseToObjectUsingMapper(ServiceRestUtils
+ .getService(service.getUniqueId(), ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER)).getResponse(),
+ Service.class);
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.SERVICES,
+ service.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ServiceDetailedAssetStructure serviceAssetMetadata = AssetRestUtils.getServiceAssetMetadata(assetResponse);
+ AssetRestUtils.serviceMetadataValidatior(serviceAssetMetadata, service, AssetTypeEnum.SERVICES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.SERVICES, service);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetMetadataWithResourceInstancesServiceInSubmitForTestingSuccess() throws Exception {
+
+ Service service = AtomicOperationUtils.createDefaultService(UserRoleEnum.DESIGNER, true).left().value();
+
+ Resource resourceVF = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.VENDOR_LICENSE, resourceVF, UserRoleEnum.DESIGNER,
+ true, true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ Resource resourceVF2 = AtomicOperationUtils
+ .createResourceByType(ResourceTypeEnum.VF, UserRoleEnum.DESIGNER, true).left().value();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.APPC_CONFIG, resourceVF2, UserRoleEnum.DESIGNER,
+ true, true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF2, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF2, service, UserRoleEnum.DESIGNER,
+ true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.MODEL_INVENTORY_PROFILE, resourceVF,
+ UserRoleEnum.DESIGNER, true, true);
+ resourceVF = (Resource) AtomicOperationUtils
+ .changeComponentState(resourceVF, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceVF, service, UserRoleEnum.DESIGNER, true);
+
+ // service with resourceVF 1.0(1 art), 2.0(2 art) versions and
+ // resourceVF2 1.0(1 art), service 1 artifact version
+ AtomicOperationUtils.uploadArtifactByType(ArtifactTypeEnum.MODEL_INVENTORY_PROFILE, service,
+ UserRoleEnum.DESIGNER, true, true);
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.SERVICES,
+ service.getUUID());
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ ServiceDetailedAssetStructure serviceAssetMetadata = AssetRestUtils.getServiceAssetMetadata(assetResponse);
+ AssetRestUtils.serviceMetadataValidatior(serviceAssetMetadata, service, AssetTypeEnum.SERVICES);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultAssetMetadataAudit(AssetTypeEnum.SERVICES, service);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_ASSET_METADATA.getName(), null);
+ }
+
+ @Test // (enabled = false)
+ public void getServiceAssetMetadataServiceNotFound() throws Exception {
+
+ String serviceUuid = "notExistingServiceUuid";
+ RestResponse assetResponse = AssetRestUtils.getAssetMetadataByAssetTypeAndUuid(true, AssetTypeEnum.SERVICES,
+ serviceUuid);
+
+ // Validate audit message
+ ArrayList<String> variables = new ArrayList<>();
+ variables.add(serviceUuid);
+ ErrorValidationUtils.checkBodyResponseOnError(ActionStatus.RESOURCE_NOT_FOUND.name(), variables,
+ assetResponse.getResponse());
+ }
+
+ // Add to service VF instance imported from CSAR
+
+ @Test
+ public void getFilteredResourceAssetSuccess() throws Exception {
+
+ List<String> expectedAssetNamesList = new ArrayList<>();
+
+ ResourceReqDetails resourceDetails = ElementFactory
+ .getDefaultResource(ResourceCategoryEnum.APPLICATION_L4_APP_SERVER);
+ resourceDetails.setName("Resource1");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ RestResponse createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ Resource resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ expectedAssetNamesList.add(resource.getName());
+
+ resourceDetails = ElementFactory.getDefaultResource(ResourceCategoryEnum.APPLICATION_L4_BORDER);
+ resourceDetails.setName("Resource2");
+ resourceDetails.setResourceType(ResourceTypeEnum.VFC.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+
+ resourceDetails = ElementFactory.getDefaultResource(ResourceCategoryEnum.GENERIC_INFRASTRUCTURE);
+ resourceDetails.setName("Resource3");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ resourceDetails = ElementFactory.getDefaultResource(ResourceCategoryEnum.APPLICATION_L4_FIREWALL);
+ resourceDetails.setName("Resource4");
+ resourceDetails.setResourceType(ResourceTypeEnum.VF.name());
+ createResource = ResourceRestUtils.createResource(resourceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createResource);
+ resource = ResponseParser.parseToObjectUsingMapper(createResource.getResponse(), Resource.class);
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ resource = (Resource) AtomicOperationUtils
+ .changeComponentState(resource, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ expectedAssetNamesList.add(resource.getName());
+
+ log.debug("4 resources created");
+ String query = "category=Application%20L4%2B";
+ RestResponse assetResponse = AssetRestUtils.getFilteredComponentList(AssetTypeEnum.RESOURCES, query);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ResourceAssetStructure> resourceAssetList = AssetRestUtils.getResourceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getResourceNamesList(resourceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ AssetRestUtils.checkResourceTypeInObjectList(resourceAssetList, ResourceTypeEnum.VF);
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultFilteredAssetListAudit(AssetTypeEnum.RESOURCES, "?" + query);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+
+ }
+
+ @Test
+ public void getFilteredResourceAssetCategoryNotFound() throws Exception {
+
+ String query = "category=Application%20L3%2B";
+ RestResponse assetResponse = AssetRestUtils.getFilteredComponentList(AssetTypeEnum.RESOURCES, query);
+ BaseRestUtils.checkErrorResponse(assetResponse, ActionStatus.COMPONENT_CATEGORY_NOT_FOUND, "resource",
+ "category", "Application L3+");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getFilteredAssetListAuditCategoryNotFound(AssetTypeEnum.RESOURCES, "?" + query, "Application L3+");
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+
+ }
+
+ @Test
+ public void getFilteredServiceAssetSuccess() throws Exception {
+
+ List<String> expectedAssetNamesList = new ArrayList<>();
+ ArtifactReqDetails artifactDetails = ElementFactory.getArtifactByType(ArtifactTypeEnum.OTHER,
+ ArtifactTypeEnum.OTHER, true);
+
+ ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+ serviceDetails.setName("Service1");
+ RestResponse createService = ServiceRestUtils.createService(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ Service service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+
+ serviceDetails.setName("Service2");
+ createService = ServiceRestUtils.createService(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ RestResponse addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(
+ artifactDetails, ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+
+ ServiceReqDetails certifyService = new ServiceReqDetails(service);
+ LifecycleRestUtils.changeDistributionStatus(certifyService, certifyService.getVersion(),
+ ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR), null,
+ DistributionStatusEnum.DISTRIBUTION_APPROVED);
+ AtomicOperationUtils.distributeService(service, false);
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service3");
+ createService = ServiceRestUtils.createService(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ addInformationalArtifactToService = ArtifactRestUtils.addInformationalArtifactToService(artifactDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER), service.getUniqueId());
+ BaseRestUtils.checkSuccess(addInformationalArtifactToService);
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CERTIFY, true).getLeft();
+ certifyService = new ServiceReqDetails(service);
+ LifecycleRestUtils.changeDistributionStatus(certifyService, certifyService.getVersion(),
+ ElementFactory.getDefaultUser(UserRoleEnum.GOVERNOR), null,
+ DistributionStatusEnum.DISTRIBUTION_APPROVED);
+ AtomicOperationUtils.distributeService(service, false);
+ expectedAssetNamesList.add(service.getName());
+
+ serviceDetails.setName("Service4");
+ createService = ServiceRestUtils.createService(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+
+ serviceDetails.setName("Service5");
+ createService = ServiceRestUtils.createService(serviceDetails,
+ ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER));
+ BaseRestUtils.checkCreateResponse(createService);
+ service = ResponseParser.parseToObjectUsingMapper(createService.getResponse(), Service.class);
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKIN, true).getLeft();
+ service = (Service) AtomicOperationUtils
+ .changeComponentState(service, UserRoleEnum.DESIGNER, LifeCycleStatesEnum.CHECKOUT, true).getLeft();
+
+ String query = "distributionStatus=Distributed";
+ RestResponse assetResponse = AssetRestUtils.getFilteredComponentList(AssetTypeEnum.SERVICES, query);
+ BaseRestUtils.checkSuccess(assetResponse);
+
+ List<ServiceAssetStructure> resourceAssetList = AssetRestUtils.getServiceAssetList(assetResponse);
+ List<String> getActualAssetNamesList = AssetRestUtils.getServiceNamesList(resourceAssetList);
+ Utils.compareArrayLists(getActualAssetNamesList, expectedAssetNamesList, "Element");
+
+ // Validate audit message
+ ExpectedExternalAudit expectedAssetListAudit = ElementFactory
+ .getDefaultFilteredAssetListAudit(AssetTypeEnum.SERVICES, "?" + query);
+ AuditValidationUtils.validateExternalAudit(expectedAssetListAudit,
+ AuditingActionEnum.GET_FILTERED_ASSET_LIST.getName(), null);
+
+ }
+
+}
diff --git a/asdc-tests/src/main/java/org/openecomp/sdc/post/Install.java b/asdc-tests/src/main/java/org/openecomp/sdc/post/Install.java
new file mode 100644
index 0000000000..f0494fe177
--- /dev/null
+++ b/asdc-tests/src/main/java/org/openecomp/sdc/post/Install.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.post;
+
+import java.io.File;
+
+import org.openecomp.sdc.be.dao.titan.TitanGraphClient;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+
+public class Install {
+ public static void main(String[] args) {
+
+ if (args == null || args.length == 0) {
+ System.out.println("Usage: org.openecomp.sdc.post.Install path_to_titan.properties");
+ System.exit(1);
+ }
+ String titanPropsFile = args[0];
+
+ if (!isFileExists(titanPropsFile)) {
+ System.exit(2);
+ }
+
+ if (!createTitanSchema(titanPropsFile)) {
+ System.exit(3);
+ }
+
+ System.exit(0);
+ }
+
+ private static boolean createTitanSchema(String titanPropsFile) {
+ TitanGraphClient titanGraphClient = new TitanGraphClient();
+ TitanOperationStatus status = titanGraphClient.createGraph(titanPropsFile, true);
+ if (TitanOperationStatus.OK == status) {
+ System.out.println("Titan schema ,indexes and default values created successfully.");
+ return true;
+ } else {
+ System.out.println(
+ "Problem while creating titan schema ,indexes and default values. (" + status.name() + ")");
+ return false;
+ }
+ }
+
+ private static boolean isFileExists(String titanPropsFile) {
+ File f = new File(titanPropsFile);
+ if (!f.exists()) {
+ System.out.println(titanPropsFile + " not found");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/asdc-tests/src/main/resources/ci/conf/log4j.properties b/asdc-tests/src/main/resources/ci/conf/log4j.properties
new file mode 100644
index 0000000000..d313e92b55
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/conf/log4j.properties
@@ -0,0 +1,34 @@
+# Define the root logger with appender file
+log4j.rootLogger = INFO, FILE, stdout
+
+# Define the file appender
+log4j.appender.FILE=org.apache.log4j.RollingFileAppender
+log4j.appender.FILE.File=${targetlog}logs/ci-log.out
+
+# Define the layout for file appender
+log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p [%10c] : %m%n
+
+# Set the maximum file size before rollover
+log4j.appender.FILE.maxFileSize=5MB
+
+# Set the the backup index
+log4j.appender.FILE.maxBackupIndex=10
+
+
+#############################################################
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %10c:%L - %m%n
+
+log4j.logger.org.apache.cassandra.service.StorageProxy=INFO
+log4j.logger.com.thinkaurelius.titan.diskstorage.cassandra.CassandraTransaction=INFO, FILE, stdout
+
+log4j.logger.org.openecomp.sdc.ci.tests.utils=INFO, FILE, stdout
+log4j.additivity.org.openecomp.sdc.ci.tests.utils=false
+
+
diff --git a/asdc-tests/src/main/resources/ci/conf/sdc-packages.yaml b/asdc-tests/src/main/resources/ci/conf/sdc-packages.yaml
new file mode 100644
index 0000000000..5d1a3e1537
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/conf/sdc-packages.yaml
@@ -0,0 +1,12 @@
+packages:
+ - org.openecomp.sdc.ci.tests.execute.general
+ - org.openecomp.sdc.ci.tests.execute.user
+ - org.openecomp.sdc.ci.tests.execute.property
+ - org.openecomp.sdc.ci.tests.execute.lifecycle
+ - org.openecomp.sdc.ci.tests.execute.resource
+ - org.openecomp.sdc.ci.tests.execute.service
+ - org.openecomp.sdc.ci.tests.execute.artifacts
+ - org.openecomp.sdc.ci.tests.execute.imports
+ - org.openecomp.sdc.ci.tests.execute.category
+ - org.openecomp.sdc.ci.tests.execute.distribution
+ - org.openecomp.sdc.ci.tests.execute.product \ No newline at end of file
diff --git a/asdc-tests/src/main/resources/ci/conf/sdc.yaml b/asdc-tests/src/main/resources/ci/conf/sdc.yaml
new file mode 100644
index 0000000000..c9730811b3
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/conf/sdc.yaml
@@ -0,0 +1,88 @@
+outputFolder: target
+reportName: index.html
+catalogBeHost: behost
+catalogFeHost: fehost
+esHost: eshost
+disributionClientHost: disClient
+catalogFePort: 8181
+catalogBePort: 8080
+disributionClientPort: 8181
+esPort: 9200
+neoHost: neoHost
+neoPort: 7474
+neoDBusername: neo4j
+neoDBpassword: 123456
+
+resourceConfigDir: src/test/resources/CI/tests
+componentsConfigDir: src/test/resources/CI/components
+importResourceConfigDir: ../catalog-be/src/main/resources/import/tosca/capability-types
+importResourceTestsConfigDir: src/test/resources/CI/importResourceTests
+errorConfigurationFile: ../catalog-be/src/main/resources/config/error-configuration.yaml
+configurationFile: ../catalog-be/src/main/resources/config/configuration.yaml
+importTypesConfigDir: src/test/resources/CI/importTypesTest
+
+
+titanPropertiesFile: src/main/resources/ci/conf/titan.properties
+cassandraHost: 127.0.0.1
+cassandraAuthenticate: false
+cassandraUsername: koko
+cassandraPassword: bobo
+cassandraSsl: false
+cassandraTruststorePath : /path/path
+cassandraTruststorePassword : 123123
+cassandraAuditKeySpace: sdcAudit
+cassandraArtifactKeySpace: sdcArtifact
+
+
+stopOnClassFailure: false
+
+#List of non-abstract resources to keep during titan cleanup between tests
+#Only 1.0 version will be kept
+resourcesNotToDelete:
+ - Compute
+ - Database
+ - ObjectStorage
+ - BlockStorage
+ - LoadBalancer
+ - Port
+ - Network
+ - Root
+ - ContainerApplication
+ - ContainerRuntime
+ - DBMS
+ - SoftwareComponent
+ - WebApplication
+ - WebServer
+ - CinderVolume
+ - ContrailVirtualNetwork
+ - NeutronNet
+ - NeutronPort
+ - NovaServer
+ - AbstractSubstitute
+ - ContrailAbstractSubstitute
+ - ContrailCompute
+ - ContrailNetworkRules
+ - ContrailPort
+ - ContrailV2NetworkRules
+ - ContrailV2VirtualMachineInterface
+ - ContrailV2VirtualNetwork
+ - ContrailVirtualNetwork
+ - VL ELINE
+ - SecurityRules
+ - VL
+
+#Resource categories to keep (including all their subcategories)
+resourceCategoriesNotToDelete:
+ - Generic
+ - Network L2-3
+ - Network L4+
+ - Application L4+
+ - Network Connectivity
+ - DcaeComponent
+
+#Service categories to keep
+serviceCategoriesNotToDelete:
+ - Mobility
+ - Network L1-3
+ - Network L4+
+ - VoIP Call Control \ No newline at end of file
diff --git a/asdc-tests/src/main/resources/ci/conf/testngLifeCycle.xml b/asdc-tests/src/main/resources/ci/conf/testngLifeCycle.xml
new file mode 100644
index 0000000000..aa390dc213
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/conf/testngLifeCycle.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="lifeCycle" parallel="none">
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_cerificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceResourceLCSTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_UndoCheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_UndoCheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_StartCertificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_CheckinCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_CheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceCertWithResourceInstances"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CrossCheckOutTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CertifyVFWithNotCertRIs"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_request4CerCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CleanupIntermediateReources"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_CheckOutCIT"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_cerificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CertifyServiceWithNotCertRI"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_CheckInCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_request4CerCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_StartCertificationCITest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- lifeCycle -->
diff --git a/asdc-tests/src/main/resources/ci/conf/titan.properties b/asdc-tests/src/main/resources/ci/conf/titan.properties
new file mode 100644
index 0000000000..42e1998f89
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/conf/titan.properties
@@ -0,0 +1,8 @@
+storage.backend=cassandra
+#storage.hostname=cassandrahost
+storage.hostname=localhost
+storage.port=9160
+
+cache.db-cache = false
+
+
diff --git a/asdc-tests/src/main/resources/ci/conf/truststore b/asdc-tests/src/main/resources/ci/conf/truststore
new file mode 100644
index 0000000000..bdec93d862
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/conf/truststore
Binary files differ
diff --git a/asdc-tests/src/main/resources/ci/scripts/startTest.sh b/asdc-tests/src/main/resources/ci/scripts/startTest.sh
new file mode 100644
index 0000000000..ca3add59c5
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/scripts/startTest.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <jar file>"
+}
+
+function exitOnError() {
+ if [ $1 -ne 0 ]
+ then
+ echo "Failed running task $2"
+ exit 2
+ fi
+}
+
+if [ $# -lt 1 ]
+then
+ usage
+ exit 2
+fi
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+LOGS_PROP_FILE=file:${FULL_PATH}/conf/log4j.properties
+#############################################
+TARGET_DIR=${FULL_PATH}/target
+CONF_FILE=${FULL_PATH}/conf/sdc.yaml
+DEBUG=true
+MainClass=org.openecomp.sdc.ci.tests.run.StartTest
+
+JAR_FILE=$1
+SUITE_FILE=$2
+
+#TARGET_DIR=`echo ${TARGET_DIR} | sed 's/\//\//g'`
+#echo $TARGET_DIR
+
+TESTS_DIR=/opt/app/sdc/ci/resources/tests
+COMPONENTS_DIR=/opt/app/sdc/ci/resources/components
+
+#sed -i 's#\(outputFolder:\).*#\1 '${TARGET_DIR}'#g' $CONF_FILE
+#sed -i 's#\(resourceConfigDir:\).*#\1 '${TESTS_DIR}'#g' $CONF_FILE
+#sed -i 's#\(componentsConfigDir:\).*#\1 '${COMPONENTS_DIR}'#g' $CONF_FILE
+TARGET_LOG_DIR="${TARGET_DIR}/"
+
+# mkdir -p ${TARGET_DIR}
+#if [ -d ${TARGET_DIR} ]
+#then
+# rm -rf ${TARGET_DIR}/*
+#exitOnError $? "Failed_to_delete_target_dir"
+#fi
+
+debug_port=8800
+#JAVA_OPTION="-javaagent:/var/tmp/jacoco/lib/jacocoagent.jar=destfile=jacoco-it.exec"
+JAVA_OPTION=""
+case "$2" in
+ -debug) echo "Debug mode, Listen on port $debug_port"; JAVA_OPTION="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${debug_port}" ;;
+ "") echo "Standard mode";;
+ *) echo "USAGE: startTest.sh [-debug]";;
+esac
+
+cmd="java $JAVA_OPTION -DdisplayException=true -Dtargetlog=${TARGET_LOG_DIR} -Dconfig.resource=${CONF_FILE} -Ddebug=${DEBUG} -Dlog4j.configuration=${LOGS_PROP_FILE} -cp $JAR_FILE ${MainClass} $SUITE_FILE"
+
+#echo $cmd
+#console=`$cmd`
+
+if [ $DEBUG == "true" ]
+then
+ $cmd
+else
+ $cmd >> /dev/null
+fi
+status=`echo $?`
+
+
+
+echo "##################################################"
+echo "################# status is ${status} #################"
+echo "##################################################"
+
+exit $status
+
diff --git a/asdc-tests/src/main/resources/ci/testSuites/artifacts.xml b/asdc-tests/src/main/resources/ci/testSuites/artifacts.xml
new file mode 100644
index 0000000000..24229b8586
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/artifacts.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Artifacts" >
+ <test name="Artifacts">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.DownloadComponentArt" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ArtifactServletTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ValidateHeatArtFieldsTypes" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.PlaceHolderValidations" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ValidateArtResponse" />
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.CrudArt" />
+ </classes>
+ </test>
+</suite> <!-- Artifacts -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/category.xml b/asdc-tests/src/main/resources/ci/testSuites/category.xml
new file mode 100644
index 0000000000..f2a0b3790c
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/category.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="category" >
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.CategoriesTests"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.GroupingTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.SubCategoriesTest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- category -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/ciFull.xml b/asdc-tests/src/main/resources/ci/testSuites/ciFull.xml
new file mode 100644
index 0000000000..9877670a90
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/ciFull.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="ciFull" configfailurepolicy="continue" verbose="2">
+ <test name="Category">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.ElementsApiTest" />
+ <class name="org.openecomp.sdc.ci.tests.execute.category.CategoriesTests">
+ <methods>
+ <exclude name="importCategories" />
+ <exclude name="getServiceCategoryHierarchySuccessFlow" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.CatalogDataApiTest" />
+ <class name="org.openecomp.sdc.ci.tests.execute.category.GroupingTest" >
+ <methods>
+ <exclude name="createProductGroupForNonExistingCategory" />
+ <exclude name="createProductGroupForNonExistingSunCategory" />
+ <exclude name="getProductCategoryHierarchySuccessFlow" />
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.category.SubCategoriesTest">
+ <methods>
+ <exclude name="createProductSubCategoryForNonExistingCategory" />
+ <exclude name="createResourceSubCategoryForNonExistingCategory" />
+ <exclude name="getResourceCategoryHierarchySuccessFlow" />
+ </methods>
+ </class>
+ </classes>
+ </test> <!-- Test -->
+ <test name="Distribution">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.distribution.AuthanticationTests" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.distribution.DistributionDownloadArtifactTest">
+ <methods>
+ <exclude name="downloadServiceArtifact_ServiceVersionNotFound" />
+ </methods>
+ </class>
+ </classes>
+ </test> <!-- Test -->
+ <test name="General">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.general.ManageEcompConsumerCredentials">
+ <methods>
+ <exclude name="createEcompCredentialsByNoExistingIUser" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.FeProxyTest" />
+ <class name="org.openecomp.sdc.ci.tests.execute.general.UuidTest" />
+ </classes>
+ </test>
+ <test name="Resource">
+ <classes>
+ <!-- <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.SimultaneousApiTest" /> -->
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.ComponentRelationshipInVfTest">
+ <methods>
+ <exclude name="associateOneOfTwoCPsToVl_ThenDiscocciate" />
+ <exclude name="disassociateRelationInVfNotFound" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.UpdateResourceMetadataTest">
+ <methods>
+ <exclude name="UpdateResourceCategory_negativeFlow" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.VFResourceInstanceNameCRUD"/>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.VfComponentInstanceCRUDTest">
+ <methods>
+ <exclude name="createAllAtomicInstancesTestGetReqCapAPI_suc" />
+ <exclude name="createVfcInstance_UserIdIsEmpty" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceApiTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.ValidateExtendedVfData" />
+ </classes>
+ </test>
+ <test name="Product">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductUndoCheckoutTest">
+ <methods>
+ <exclude name="undoCheckOutNotExist" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductComponentInstanceCRUDTest">
+ <methods>
+ <exclude name="createServiceInstanceNameIsNull" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ChangeServiceInstanceVersionTest">
+ <methods>
+ <exclude name="changeServiceInstanceVersionToNonExisitingServiceVersion" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductGetFollowedTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductToscaYamlGenerationTest" />
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckinTest">
+ <methods>
+ <exclude name="checkInProductNotExist" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckoutTest" />
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCrudTest">
+ <methods>
+ <exclude name="updateProductDescriptionRemoveSpacesFromBeginning" />
+ <exclude name="updateProductDescriptionRemoveSpacesFromTheEnd" />
+ <exclude name="updateProductDescriptionValidCharacters02" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductCreateWithValidationsTest">
+ <methods>
+ <exclude name="createProductDescriptionRemoveSpacesFromBeginning" />
+ <exclude name="createProductDescriptionRemoveSpacesFromTheEnd" />
+ <exclude name="createProductDescriptionValidCharacters01" />
+ <exclude name="createProductDescriptionValidCharacters02" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+ <test name="Service">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.GetServiceLatestVersionTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.CreateServiceMetadataApiTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.ReqCapOccurrencesTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.ChangeServiceDistributionStatusApiTest">
+ <methods>
+ <exclude name="approveCertifiedService_byDesigner" />
+ <exclude name="approveCertifiedService_byOps" />
+ <exclude name="approveCertifiedService_byTester" />
+ <exclude
+ name="distributionStatusChange_approve_Reject_AprroveBysdncAdminDetails" />
+ <exclude name="rejectCertifiedService_byDesigner" />
+ <exclude name="rejectCertifiedService_byOps" />
+ <exclude name="rejectCertifiedService_byTester" />
+ <exclude name="rejectCertifiedService_bysdncAdminDetails" />
+ <exclude name="rejectCertifiedService_bysdncGovernorDeatails" />
+ <exclude name="rejectNotCertifiedService_checkedin" />
+ <exclude name="rejectNotCertifiedService_checkeout" />
+ <exclude name="rejectNotCertifiedService_inProgress" />
+ <exclude name="rejectNotCertifiedService_readyForCer" />
+ <exclude name="rejectServiceNotFound" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.ServiceComponentInstanceCRUDTest">
+ <methods>
+ <exclude name="associateAfterCheckoutAllInstancesTest" />
+ <exclude name="associateAfterDeletingResourceTest" />
+ <exclude name="associateDissociateTwoVFs" />
+ <exclude name="associateInstancesInTwoServiceVersionsTest" />
+ <exclude name="associateNotCompitableReqCapTest" />
+ <exclude name="associateOnceAgainExistingRelationTest" />
+ <exclude name="associateResourceInstanceToResourceInstanceNotFoundTest" />
+ <exclude
+ name="associateTwoRelations_CheckinCheckout_DissoicateOneRelationInSecondVersion" />
+ <exclude name="createResourceInstanceWithEmptyUserIdTest" />
+ <exclude name="deleteNotFoundResourceInstanceTest" />
+ <exclude name="deleteResourceInstanceWithEmptyUserIdTest" />
+ <exclude name="dissociateOnceAgainTest" />
+ <exclude name="dissociateRelationNotFoundTest" />
+ <exclude name="dissoicateRelationWhileInstanceNotFound" />
+ <exclude name="dissoicateWithEmptyUserIdsHeaderTest" />
+ <exclude name="relationDeletedAfterDeletingResourceInstanceTest" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.UpdateServiceMetadataTest">
+ <methods>
+ <exclude name="categoryValidationTest6" />
+ <exclude name="categoryValidationTest7" />
+ <exclude name="descriptionValidationTest11" />
+ <exclude name="descriptionValidationTest12" />
+ <exclude name="descriptionValidationTest13" />
+ <exclude name="descriptionValidationTest7" />
+ <exclude name="serviceNameValidationTest17" />
+ <exclude name="updateCertifiedService" />
+ <exclude name="updateCheckoutCertifiedService" />
+ <exclude name="updateServiceByMethod_delete" />
+ <exclude name="updateServiceByMethod_get" />
+ <exclude name="updateServiceByMethod_post" />
+ <exclude name="updateServiceNotExist" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.GetAllServiceVersions" />
+ </classes>
+ </test>
+ <test name="Artifacts">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.DownloadComponentArt" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ArtifactServletTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ValidateHeatArtFieldsTypes" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.PlaceHolderValidations" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ValidateArtResponse" />
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.CrudArt" />
+ </classes>
+ </test>
+</suite> \ No newline at end of file
diff --git a/asdc-tests/src/main/resources/ci/testSuites/externalApis.xml b/asdc-tests/src/main/resources/ci/testSuites/externalApis.xml
new file mode 100644
index 0000000000..5db368ecf4
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/externalApis.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="externalApis" configfailurepolicy="continue" verbose="2">
+ <test name="ExternalApis">
+ <classes>
+ <class name="org.openecomp.sdc.externalApis.GetAssetServlet" />
+ <class name="org.openecomp.sdc.externalApis.GetFilteredAssetServlet"/>
+ <class name="org.openecomp.sdc.externalApis.GetSpecificAssetMetadataServlet"/>
+ </classes>
+ </test>
+</suite> \ No newline at end of file
diff --git a/asdc-tests/src/main/resources/ci/testSuites/general.xml b/asdc-tests/src/main/resources/ci/testSuites/general.xml
new file mode 100644
index 0000000000..bd7e6b6650
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/general.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="General" >
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.SampleTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.ManageEcompConsumerCredentials"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.UuidTest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- General -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/imports.xml b/asdc-tests/src/main/resources/ci/testSuites/imports.xml
new file mode 100644
index 0000000000..ae4a5bc1f8
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/imports.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Imports" >
+ <test name="Imports">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportGenericResourceCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportCsarResourceTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportNewResourceCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportToscaResourceTest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- Imports -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/product.xml b/asdc-tests/src/main/resources/ci/testSuites/product.xml
new file mode 100644
index 0000000000..4ea495d0d7
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/product.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Product" >
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductLifecycleTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductUndoCheckoutTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckinTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckoutTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCrudTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductComponentInstanceCRUDTest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- Product -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/property.xml b/asdc-tests/src/main/resources/ci/testSuites/property.xml
new file mode 100644
index 0000000000..f94f89737a
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/property.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Property" >
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.property.PropertyApisTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.property.PropertyServletTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.property.AdditionalInformationServletTest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- Property -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/resource.xml b/asdc-tests/src/main/resources/ci/testSuites/resource.xml
new file mode 100644
index 0000000000..5f23325790
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/resource.xml
@@ -0,0 +1,336 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Sanity" configfailurepolicy="continue" >
+
+ <!--
+ <listeners>
+ <listener class-name="org.openecomp.sdc.ci.tests.config.InvokedMethodListener" />
+ </listeners>
+ -->
+
+
+ <test verbose="2" name="Sanity">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.ArtifactServletTest">
+ <methods>
+ <include name="upadteArtifactWithPayLoadToResourcseTest"/>
+ <include name="createAndUpdateArtifactToInterface"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.CrudArt">
+ <methods>
+ <include name="addHeatArtifactToResourceAndCertify"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.LCS_Artifacts">
+ <methods>
+ <include name="LCS_inherit_artifact"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.ValidateArtResponse"/>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.category.CatalogDataApiTest"/>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.category.ElementsApiTest">
+ <methods>
+ <include name="getAllArtifactTypes"/>
+ <include name="getConfiguration"/>
+ <include name="getAllPropertyScopesSuccess"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.distribution.AuthanticationTests">
+ <methods>
+ <include name="downloadServiceArtifactSuccessWithAutantication"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.distribution.DistributionDownloadArtifactTest">
+ <methods>
+ <include name="downloadResourceArtifactSuccess"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.general.BasicHttpAuthenticationTest">
+ <methods>
+ <include name="sendAuthenticatedRequestTest_success"/>
+ </methods>
+ </class>
+
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.general.FeProxyTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.ManageEcompConsumerCredentials">
+ <methods>
+ <include name="deleteEcompCredentialsMethodDelete"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.UuidTest">
+ <methods>
+ <include name="testE2EUuidHeaderReturnedAndPreserved"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportGenericResourceCITest">
+ <methods>
+ <include name="importAllTestResources"/>
+ <include name="testImportCheckoutAndUpdateUserResource"/>
+ <include name="testImportWithUpdateNormativeType"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportNewResourceCITest">
+ <methods>
+ <include name="importAllTestResources_toValidateNewAPI"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.imports.ImportToscaResourceTest">
+ <methods>
+ <include name="importToscaResource"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CleanupIntermediateReources">
+ <methods>
+ <include name="test28"/>
+ <include name="test29"/>
+ <include name="test30"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CleanupIntermediateServicesTest">
+ <methods>
+ <include name="testCleanupIntermediateServices"/>
+ </methods>
+ </class>
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ResourceLCS_cerificationCITest">
+ <methods>
+ <include name="verifyAllPrevVerDeleted"/>
+ <include name="verifyStartWithDesignerCertByAdmin"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ResourceLCS_CheckInCITest">
+ <methods>
+ <include name="checkInTest_suc"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ResourceLCS_CheckOutCITest">
+ <methods>
+ <include name="checkOutTest_currState_READY_FOR_CERTIFICATION_ownerDSIGNER_performByADMIN_suc"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ResourceLCS_request4CerCITest">
+ <methods>
+ <include name="req4cer_fromCheckOut_designer_admin"/>
+ </methods>
+ </class>
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ResourceLCS_StartCertificationCITest">
+ <methods>
+ <include name="startCer_suc"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ResourceLCS_UndoCheckOutCITest">
+ <methods>
+ <include name="undoCheckOutTest_import_outAdmin_undoOut_designer"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceCertWithMandatoryArt">
+ <methods>
+ <include name="CertServiceWithHeatDeployArtOnResource"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceCertWithResourceInstances">
+ <methods>
+ <include name="certServiceWithCertResource"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.property.AdditionalInformationServletTest">
+ <methods>
+ <include name="createResourceAdditionalInformationTestDuringLifecycle"/>
+ <include name="updateResourceAdditionalInformationTest"/>
+ <include name="deleteResourceAdditionalInformationTest"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.property.PropertyApisTest">
+ <methods>
+ <include name="testPropertyApis"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.property.PropertyServletTest">
+ <methods>
+ <include name="createCascadeVfResource"/>
+ <include name="createPropertyTestSetClearDefaultValueInetegr"/>
+ </methods>
+ </class>
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.GetAllLatestVersionNonAbstarctResources">
+ <methods>
+ <include name="resourceCertifiedTwiceCoutCinCout"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.GetAllResourceVersions">
+ <methods>
+ <include name="getResourceAllVersions_version15"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.GetFollwedElementsTest">
+ <methods>
+ <include name="getResourcesListBolongsToSpecificDesigner"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.GetResourceAbstractApiTest">
+ <methods>
+ <include name="getAbstractResourceList"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.GetResourceNotAbstractApiTest">
+ <methods>
+ <include name="getNotAbstractResourceList"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.GetResourceTagsApiTest">
+ <methods>
+ <include name="getModifiedResourceTagsList"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceApiTest">
+ <methods>
+ <include name="updateResourceMetadataSuccess"/>
+ </methods>
+ </class>
+ <!--
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceInstanceNameCRUD">
+ <methods>
+ <include name="updateResourceInstanceNameAfterServiceCertification"/>
+ </methods>
+ </class>
+ -->
+<!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceInstanceServletTest">
+ <methods>
+ <include name="updateRI_suc"/>
+ </methods>
+ </class> -->
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.UpdateResourceMetadataTest">
+ <methods>
+ <include name="UpdateDerivedFromSuccess"/>
+ </methods>
+ </class>
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.service.GetComponentAuditApiTest">
+ <methods>
+ <include name="testServiceAuditCertifiedVersion"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.service.ServiceApiTest">
+ <methods>
+ <include name="createServiceTest"/>
+ <include name="getFollowedServicesTester"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.service.ServiceInformationalArtifactApiTest">
+ <methods>
+ <include name="addArtifactSuccessfuly"/>
+ <include name="deleteArtifact"/>
+ <include name="updateArtifactMetadataSuccessfully"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.service.UpdateServiceMetadataTest">
+ <methods>
+ <include name="updateServiceSuccessfully"/>
+ </methods>
+ </class>
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.user.ActivateDeActivateDeleteUser">
+ <methods>
+ <include name="authorizeDeActivatedUser"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.user.CreateUserApiTest">
+ <methods>
+ <include name="createUser"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.user.GovernorWorkspaceApiTest">
+ <methods>
+ <include name="governorList_AllCertifiedVersionsOfService"/>
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.user.OpsWorkspaceApiTest">
+ <methods>
+ <include name="opsList_AllCertifiedVersionsOfServiceApproved"/>
+ </methods>
+ </class>
+
+ <!-- Product tests start-->
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckinTest">
+ <methods>
+ <include name="checkInProductByCreator"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ChangeServiceInstanceVersionTest">
+ <methods>
+ <include name="changeServiceInstanceVersionByPm"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckoutTest">
+ <methods>
+ <include name="checkOutProductByPmNotInContacts"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductComponentInstanceCRUDTest">
+ <methods>
+ <include name="createServiceInstanceTest"/>
+ <include name="deleteServiceInstanceByPm"/>
+ <include name="updateServiceInstanceNameByPm"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCrudTest">
+ <methods>
+ <include name="createManyGroupingsDiffCategories"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductGetFollowedTest">
+ <methods>
+ <include name="followedPageTest"/>
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductUndoCheckoutTest">
+ <methods>
+ <include name="undoCheckOutProductByPm"/>
+ </methods>
+ </class>
+ <!-- Product tests end-->
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- Sanity -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/sanity.xml b/asdc-tests/src/main/resources/ci/testSuites/sanity.xml
new file mode 100644
index 0000000000..11b4b79fc4
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/sanity.xml
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Sanity" configfailurepolicy="continue" verbose="2">
+
+ <listeners>
+ <listener class-name="org.openecomp.sdc.ci.tests.run.ExtentReporterNG" />
+ </listeners>
+
+ <test name="sanityE2Eflows">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.sanity.CrudE2E" />
+ </classes>
+ </test>
+
+ <test name="General">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.general.BasicHttpAuthenticationTest">
+ <methods>
+ <include name="sendAuthenticatedRequestTest_success" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.FeProxyTest" />
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.general.ManageEcompConsumerCredentials">
+ <methods>
+ <include name="deleteEcompCredentialsMethodDelete" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.general.UuidTest">
+ <methods>
+ <include name="testE2EUuidHeaderReturnedAndPreserved" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="ArtifactOnInstance">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.CrudArt">
+ <methods>
+ <include name="addDcaeInventoryToscaArtifactToResourceInstanceAndCertify" />
+ <include name="addDcaeInventoryJsonArtifactToResourceInstanceAndCertify" />
+ <include name="addDcaeInventoryPolicyArtifactToResourceInstanceAndCertify" />
+ <include name="addDcaeInventoryDocArtifactToResourceInstanceAndCertify" />
+ <include name="addDcaeInventoryBluePrintArtifactToResourceInstanceAndCertify" />
+ <include name="addDcaeInventoryEventArtifactToResourceInstanceAndCertify" />
+ <include name="updateArtifactDescriptionToResourceInstance" />
+ <include name="deleteArtifactToResourceInstance" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="Artifact">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.ArtifactServletTest">
+ <methods>
+ <include name="upadteArtifactWithPayLoadToResourcseTest" />
+ <include name="createAndUpdateArtifactToInterface" />
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.CrudArt">
+ <methods>
+ <include name="addHeatArtifactToResourceAndCertify" />
+ </methods>
+ </class>
+
+
+
+ <class name="org.openecomp.sdc.ci.tests.execute.artifacts.ValidateArtResponse" />
+
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.artifacts.DownloadComponentArt">
+ <methods>
+ <include name="downloadArtifactFromResourceTest" />
+ <include name="downloadArtifactFromServiceTest" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="Service">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.GetComponentAuditApiTest">
+ <methods>
+ <include name="testServiceAuditCertifiedVersion" />
+ </methods>
+ </class>
+
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.service.ServiceApiTest">
+ <methods> <include name="createServiceTest" /> <include name="getFollowedServicesTester"
+ /> </methods> </class> -->
+
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.service.ServiceInformationalArtifactApiTest">
+ <methods> <include name="addArtifactSuccessfuly" /> <include name="deleteArtifact"
+ /> <include name="updateArtifactMetadataSuccessfully" /> </methods> </class> -->
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.service.UpdateServiceMetadataTest">
+ <methods>
+ <include name="updateServiceSuccessfully" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="Resource">
+ <classes>
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.GetAllLatestVersionNonAbstarctResources">
+ <methods> <include name="resourceCertifiedTwiceCoutCinCout" /> </methods>
+ </class> -->
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.GetAllResourceVersions">
+ <methods>
+ <include name="getResourceAllVersions_version15" />
+ </methods>
+ </class>
+
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.GetFollwedElementsTest">
+ <methods> <include name="getResourcesListBolongsToSpecificDesigner" /> </methods>
+ </class> -->
+
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.GetResourceAbstractApiTest">
+ <methods> <include name="getAbstractResourceList" /> </methods> </class> -->
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.GetResourceNotAbstractApiTest">
+ <methods>
+ <include name="getNotAbstractResourceList" />
+ </methods>
+ </class>
+
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.GetResourceTagsApiTest">
+ <methods> <include name="getModifiedResourceTagsList" /> </methods> </class> -->
+
+ <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceApiTest">
+ <methods>
+ <include name="updateResourceMetadataSuccess" />
+ </methods>
+ </class>
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceInstanceNameCRUD">
+ <methods> <include name="updateResourceInstanceNameAfterServiceCertification"/>
+ </methods> </class> -->
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.resource.ResourceInstanceServletTest">
+ <methods> <include name="updateRI_suc"/> </methods> </class> -->
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.resource.UpdateResourceMetadataTest">
+ <methods>
+ <include name="UpdateDerivedFromSuccess" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+ <test name="Product">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckinTest">
+ <methods>
+ <include name="checkInProductByCreator" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ChangeServiceInstanceVersionTest">
+ <methods>
+ <include name="changeServiceInstanceVersionByPm" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCheckoutTest">
+ <methods>
+ <include name="checkOutProductByPmNotInContacts" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductComponentInstanceCRUDTest">
+ <methods>
+ <include name="createServiceInstanceTest" />
+ <include name="deleteServiceInstanceByPm" />
+ <include name="updateServiceInstanceNameByPm" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.product.ProductCrudTest">
+ <methods>
+ <include name="createManyGroupingsDiffCategories" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductGetFollowedTest">
+ <methods>
+ <include name="followedPageTest" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.product.ProductUndoCheckoutTest">
+ <methods>
+ <include name="undoCheckOutProductByPm" />
+ </methods>
+ </class>
+ <!-- Product tests end -->
+ </classes>
+ </test>
+ <test name="Catalog">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.CatalogDataApiTest" />
+ </classes>
+ </test>
+
+ <test name="distribution">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.distribution.AuthanticationTests">
+ <methods>
+ <include name="downloadServiceArtifactSuccessWithAutantication" />
+ </methods>
+ </class>
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.distribution.DistributionDownloadArtifactTest">
+ <methods>
+ <include name="downloadResourceArtifactSuccess" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="Category">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.ElementsApiTest">
+ <methods>
+ <include name="getAllArtifactTypes" />
+ <include name="getConfiguration" />
+ <include name="getAllPropertyScopesSuccess" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="Imports">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.imports.ImportGenericResourceCITest">
+ <methods>
+ <include name="importAllTestResources" />
+ <include name="testImportCheckoutAndUpdateUserResource" />
+ <include name="testImportWithUpdateNormativeType" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.imports.ImportNewResourceCITest">
+ <methods>
+ <include name="importAllTestResources_toValidateNewAPI" />
+ </methods>
+ </class>
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.imports.ImportToscaResourceTest">
+ <methods>
+ <include name="importToscaResource" />
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.imports.ImportToscaCapabilitiesWithProperties">
+ <methods>
+ <include name="importNodeTypeWithCapabilityWithPropertiesFromYmlSucceed" />
+ <!--
+ This test require the onboarding simulator.
+ <include name="importResourceWithCapabilityWithPropertiesOverridingCapTypePropertiesSucceed" />
+ -->
+ </methods>
+ </class>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.imports.CsarUtilsTest">
+ </class>
+ </classes>
+ </test> <!-- Test -->
+
+ <test name="attribute">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.attribute.ComponentInstanceAttributeTest">
+ <methods>
+ <include name="testUpdateAttributeOnResourceInstance" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="inputs">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.inputs.InputsApiTests">
+ <methods>
+ <include name="testInputsMainFunctionality" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="property">
+ <classes>
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.property.AdditionalInformationServletTest">
+ <methods>
+ <include name="createResourceAdditionalInformationTestDuringLifecycle" />
+ <include name="updateResourceAdditionalInformationTest" />
+ <include name="deleteResourceAdditionalInformationTest" />
+ </methods>
+ </class>
+
+ <class name="org.openecomp.sdc.ci.tests.execute.property.PropertyApisTest">
+ <methods>
+ <include name="testPropertyApis" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.ci.tests.execute.property.ComponentInstancePropertyTest">
+ <methods>
+ <include name="nestedVfResourceProperty3Levels" />
+ </methods>
+ </class>
+
+
+
+ <!-- <class name="org.openecomp.sdc.ci.tests.execute.property.PropertyServletTest">
+ <methods> <include name="createCascadeVfResource" /> <include name="createPropertyTestSetClearDefaultValueInetegr"
+ /> </methods> </class> -->
+ </classes>
+ </test>
+
+ <test name="User">
+ <classes>
+
+ <!-- class
+ name="org.openecomp.sdc.ci.tests.execute.user.ActivateDeActivateDeleteUser">
+ <methods>
+ <include name="authorizeDeActivatedUser" />
+ </methods>
+ </class-->
+
+ <class name="org.openecomp.sdc.ci.tests.execute.user.CreateUserApiTest">
+ <methods>
+ <include name="createUser" />
+ </methods>
+ </class>
+
+ <class
+ name="org.openecomp.sdc.ci.tests.execute.user.GovernorWorkspaceApiTest">
+ <methods>
+ <include name="governorList_AllCertifiedVersionsOfService" />
+ </methods>
+ </class>
+
+ </classes>
+ </test>
+
+ <test name="ExternalApis">
+ <classes>
+ <class name="org.openecomp.sdc.externalApis.GetAssetServlet" >
+ <methods>
+ <include name="getResourceAssetSuccess" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.externalApis.GetFilteredAssetServlet">
+ <methods>
+ <include name="getResourceAssetBySpecifiedCategoryAndSubCategory" />
+ <include name="getServiceAssetBySpecifiedCategoryAndDistributionStatus" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.externalApis.GetSpecificAssetMetadataServlet">
+ <methods>
+ <include name="getResourceAssetMetadataWithResourceInstancesSuccess" />
+ <include name="getServiceAssetMetadataWithCertifiedResourceInstancesAndArtifactsOnRIsAndServiceSuccess" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+
+
+
+
+
+
+</suite> \ No newline at end of file
diff --git a/asdc-tests/src/main/resources/ci/testSuites/service.xml b/asdc-tests/src/main/resources/ci/testSuites/service.xml
new file mode 100644
index 0000000000..18d5630c4e
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/service.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Service" >
+ <test name="Service">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.GetServiceLatestVersionTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.CreateServiceMetadataApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.ReqCapOccurrencesTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.ChangeServiceDistributionStatusApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.ServiceComponentInstanceCRUDTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.UpdateServiceMetadataTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.service.GetAllServiceVersions"/>
+ </classes>
+ </test>
+</suite> <!-- Service -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/testngLifeCycle.xml b/asdc-tests/src/main/resources/ci/testSuites/testngLifeCycle.xml
new file mode 100644
index 0000000000..54f1868470
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/testngLifeCycle.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="lifeCycle" >
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_cerificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceResourceLCSTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_UndoCheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_UndoCheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_StartCertificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_CheckinCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_CheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceCertWithResourceInstances"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CrossCheckOutTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CertifyVFWithNotCertRIs"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_request4CerCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CleanupIntermediateReources"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_CheckOutCIT"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_cerificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CertifyServiceWithNotCertRI"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_CheckInCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_request4CerCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_StartCertificationCITest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- lifeCycle -->
diff --git a/asdc-tests/src/main/resources/ci/testSuites/user.xml b/asdc-tests/src/main/resources/ci/testSuites/user.xml
new file mode 100644
index 0000000000..948993c144
--- /dev/null
+++ b/asdc-tests/src/main/resources/ci/testSuites/user.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="User" >
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.AuthorizedUserApiTest_extend"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.AuthorizedUserApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.OpsWorkspaceApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.UserApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.GetAllAdminUsersApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.CRUDUserTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.CreateUserApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.GovernorWorkspaceApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.GetListsOfTesterUserApiTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.user.ActivateDeActivateDeleteUser"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- User -->
diff --git a/asdc-tests/src/test/resources/CI/components/apache/apache-type.yml b/asdc-tests/src/test/resources/CI/components/apache/apache-type.yml
new file mode 100644
index 0000000000..360b1a2d60
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/apache/apache-type.yml
@@ -0,0 +1,50 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: TOSCA simple profile with Apache.
+template_name: apache-type
+template_version: 2.0.0-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-webServer:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Apache:
+ derived_from: tosca.nodes.WebServer
+ description: >
+ The TOSCA Apache Node Type represents an apache component
+ that can be managed and run by a TOSCA Compute Node Type.
+ capabilities:
+ host:
+ type: alien.capabilities.ApacheContainer
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ tags:
+ icon: /images/apache.png
+ properties:
+ version:
+ type: version
+ default: 2.4
+ constraints:
+ - equal: 2.4
+ port:
+ type: integer
+ description: Port for the Apache server
+ default: 80
+ constraints:
+ - greater_or_equal: 1
+ document_root:
+ type: string
+ default: "/var/www"
+ interfaces:
+ Standard:
+ create:
+ inputs:
+ PORT: { get_property: [SELF, port] }
+ DOC_ROOT: { get_property: [SELF, document_root] }
+ implementation: scripts/install_apache.sh
+ start: scripts/start_apache.sh
+
+capability_types:
+ alien.capabilities.ApacheContainer:
+ derived_from: tosca.capabilities.Container
diff --git a/asdc-tests/src/test/resources/CI/components/apache/images/apache.png b/asdc-tests/src/test/resources/CI/components/apache/images/apache.png
new file mode 100644
index 0000000000..8e9f402d90
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/apache/images/apache.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/apache/scripts/install_apache.sh b/asdc-tests/src/test/resources/CI/components/apache/scripts/install_apache.sh
new file mode 100644
index 0000000000..a77f9a13f8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/apache/scripts/install_apache.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+echo "Using apt-get. Installing apache2 on one of the following : Debian, Ubuntu, Mint"
+LOCK="/tmp/lockaptget"
+DEFAULT_PORT=80
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "Apache take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+
+sudo rm -f /var/lib/dpkg/lock
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo apt-get install -y -q apache2 || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/apache2 stop
+if [ ! -d $DOC_ROOT ]; then
+ eval "sudo mkdir -p $DOC_ROOT"
+fi
+eval "sudo chown -R www-data:www-data $DOC_ROOT"
+
+if [[ ("$PORT" == "$DEFAULT_PORT") ]]; then
+ echo "Use default port for Apache : $DEFAULT_PORT"
+else
+ echo "Replacing port $DEFAULT_PORT with $PORT..."
+ sudo sed -i -e "s/$DEFAULT_PORT/$PORT/g" /etc/apache2/ports.conf || exit ${1}
+fi
+
+echo "Change config of apache2"
+if sudo test -f "/etc/apache2/sites-available/default"; then
+ echo "Change the DocumentRoot of apache2 on Ubuntu < 14.04"
+ sudo sed -i -e "s#DocumentRoot /var/www#DocumentRoot $DOC_ROOT#g" /etc/apache2/sites-available/default
+fi
+if sudo test -f "/etc/apache2/sites-available/000-default.conf"; then
+ echo "Change the DocumentRoot of Apache2 on Ubuntu >= 14.04"
+ sudo sed -i -e "s#DocumentRoot /var/www/html#DocumentRoot $DOC_ROOT#g" /etc/apache2/sites-available/000-default.conf
+fi
+
+sudo bash -c "echo ServerName localhost >> /etc/apache2/apache2.conf"
+
+echo "Start apache2 whith new conf"
+sudo /etc/init.d/apache2 start
+echo "End of $0"
diff --git a/asdc-tests/src/test/resources/CI/components/apache/scripts/start_apache.sh b/asdc-tests/src/test/resources/CI/components/apache/scripts/start_apache.sh
new file mode 100644
index 0000000000..478c56edf5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/apache/scripts/start_apache.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+service="apache2"
+
+if (( $(ps -ef | grep -v grep | grep $service | wc -l) > 0 ))
+then
+ sudo /etc/init.d/$service restart
+else
+ sudo /etc/init.d/$service start
+fi
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/images/relational_db.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/images/relational_db.png
new file mode 100644
index 0000000000..a7a632effd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/images/relational_db.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/normative-types-DBMS.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/normative-types-DBMS.yml
new file mode 100644
index 0000000000..4a924672fc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/DBMS/normative-types-DBMS.yml
@@ -0,0 +1,36 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-DBMS
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-softwareComponent:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.DBMS:
+ abstract: true
+ derived_from: tosca.nodes.SoftwareComponent
+ description: >
+ The TOSCA DBMS node represents a typical relational, SQL Database Management System software component or service.
+ tags:
+ icon: /images/relational_db.png
+ properties:
+ dbms_root_password:
+ type: string
+ required: false
+ description: the root password for the DBMS service.
+ dbms_port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ properties:
+ valid_node_types: [tosca.nodes.Database]
+ endpoint:
+ type: tosca.capabilities.DatabaseEndpoint
+
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/images/volume.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/images/volume.png
new file mode 100644
index 0000000000..16fa17bd70
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/images/volume.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/normative-types-blockStorage.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/normative-types-blockStorage.yml
new file mode 100644
index 0000000000..df942b9f4f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/blockStorage/normative-types-blockStorage.yml
@@ -0,0 +1,40 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-blockStorage
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.BlockStorage:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA BlockStorage node currently represents a server-local block storage device (i.e., not shared)
+ offering evenly sized blocks of data from which raw storage volumes can be created.
+ tags:
+ icon: /images/volume.png
+ properties:
+ size:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: The requested storage size in MegaBytes (MB).
+ volume_id:
+ type: string
+ required: false
+ description: ID of an existing volume (that is in the accessible scope of the requesting application).
+ snapshot_id:
+ type: string
+ required: false
+ description: Some identifier that represents an existing snapshot that should be used when creating the block storage (volume).
+ attributes:
+ volume_id:
+ type: string
+ description: ID provided by the orchestrator for newly created volumes.
+ requirements:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/compute/images/compute.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/compute/images/compute.png
new file mode 100644
index 0000000000..7d5297eed3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/compute/images/compute.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/compute/normative-types-compute.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/compute/normative-types-compute.yml
new file mode 100644
index 0000000000..3a972a81e0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/compute/normative-types-compute.yml
@@ -0,0 +1,77 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-compute
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ # Infrastructure components
+ tosca.nodes.Compute:
+ derived_from: tosca.nodes.Root
+ description: >
+ Represents a real or virtual machine or ‘server’. Informations specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing informations are specified the cloud’s provider default
+ machine will be used. It is strongly recommended to specify the required cpus and memory
+ at least.
+ tags:
+ icon: /images/compute.png
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: Number of (actual or virtual) CPUs associated with the Compute node.
+ mem_size:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: Size of memory, in Megabytes (MB), available to applications running on the Compute node.
+ disk_size:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.
+ os_arch:
+ type: string
+ constraints:
+ - valid_values: ["x86_32", "x86_64"]
+ description: The host Operating System (OS) architecture.
+ os_type:
+ type: string
+ constraints:
+ - valid_values: ["linux", "aix", "mac os", "windows"]
+ description: The host Operating System (OS) type.
+ os_distribution:
+ type: string
+ required: false
+ description: The host Operating System (OS) distribution.
+ os_version:
+ type: string
+ required: false
+ description: The host Operating System version.
+ attributes:
+ ip_address:
+ type: string
+ description: >
+ The primary IP address assigned by the cloud provider that applications may use to access the Compute node.
+ Note: This is used by the platform provider to convey the primary address used to access the compute node. Future working drafts will address implementations that support floating or multiple IP addresses.
+ requirements:
+ network:
+ type: tosca.capabilities.Connectivity
+ lower_bound: 0
+ upper_bound: unbounded
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ properties:
+ valid_node_types: [tosca.nodes.SoftwareComponent]
+ attach: tosca.capabilities.Attachment
+ scalable: tosca.capabilities.Scalable
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/database/images/relational_db.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/database/images/relational_db.png
new file mode 100644
index 0000000000..a7a632effd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/database/images/relational_db.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/database/normative-types-database.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/database/normative-types-database.yml
new file mode 100644
index 0000000000..38bf5d11db
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/database/normative-types-database.yml
@@ -0,0 +1,41 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-database
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.Database:
+ derived_from: tosca.nodes.Root
+ description: >
+ Base type for the schema and content associated with a DBMS.
+ The TOSCA Database node represents a logical database that can be managed and hosted by a TOSCA DBMS node.
+ tags:
+ icon: /images/relational_db.png
+ properties:
+ db_user:
+ type: string
+ required: false
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ required: false
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ db_port:
+ type: integer
+ required: false
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: false
+ description: The logical name of the database.
+ requirements:
+ - host: tosca.nodes.DBMS
+ relationship_type: tosca.relationships.HostedOn
+ capabilities:
+ database_endpoint: tosca.capabilities.DatabaseEndpoint
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/network/images/network.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/network/images/network.png
new file mode 100644
index 0000000000..c8bf18f31a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/network/images/network.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/network/normative-types-network.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/network/normative-types-network.yml
new file mode 100644
index 0000000000..bb860f82be
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/network/normative-types-network.yml
@@ -0,0 +1,39 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-network
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.Network:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA Network node represents a simple, logical network service.
+ properties:
+ ip_version:
+ type: integer
+ required: false
+ default: 4
+ constraints:
+ - valid_values: [ 4, 6 ]
+ cidr:
+ type: string
+ required: false
+ gateway_ip:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ network_id:
+ type: string
+ required: false
+ capabilities:
+ connection:
+ type: tosca.capabilities.Connectivity
+ tags:
+ icon: /images/network.png
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/images/objectstore.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/images/objectstore.png
new file mode 100644
index 0000000000..2b2063c4f7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/images/objectstore.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/normative-types-objectStorage.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/normative-types-objectStorage.yml
new file mode 100644
index 0000000000..a56fad5363
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/objectStorage/normative-types-objectStorage.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-objectStorage
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.ObjectStorage:
+ abstract: true
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA ObjectStorage node represents storage that provides the ability to store data as objects (or BLOBs of data)
+ without consideration for the underlying filesystem or devices.
+ tags:
+ icon: /images/objectstore.png
+ properties:
+ store_name:
+ type: string
+ description: The logical name of the object store (or container).
+ store_size:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 0
+ description: The requested initial storage size in Gigabytes.
+ store_maxsize:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: The requested maximum storage size in Gigabytes.
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/root/images/root.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/root/images/root.png
new file mode 100644
index 0000000000..170f1c3c27
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/root/images/root.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/root/normative-types-root.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/root/normative-types-root.yml
new file mode 100644
index 0000000000..7f4c16c260
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/root/normative-types-root.yml
@@ -0,0 +1,168 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-root
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+
+node_types:
+ tosca.nodes.Root:
+ abstract: true
+ description: >
+ This is the default (root) TOSCA Node Type that all other TOSCA nodes should extends.
+ This allows all TOSCA nodes to have a consistent set of features for modeling and management
+ (e.g, consistent definitions for requirements, capabilities, and lifecycle interfaces).
+ tags:
+ icon: /images/root.png
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ requirements:
+ dependency:
+ type: tosca.capabilities.Root
+ lower_bound: 0
+ upper_bound: unbounded
+ capabilities:
+ root:
+ type: tosca.capabilities.Root
+ interfaces:
+ tosca.interfaces.node.lifecycle.Standard:
+ description: >
+ This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.
+ create:
+ description: Standard lifecycle create operation.
+ configure:
+ description: Standard lifecycle configure operation (pre-start).
+ start:
+ description: Standard lifecycle start operation.
+ post_start:
+ description: Standard lifecycle post-configure operation (post-start)
+ stop:
+ description: Standard lifecycle stop operation.
+ delete:
+ description: Standard lifecycle delete operation.
+
+
+capability_types:
+ tosca.capabilities.Root:
+ description: This is the default (root) TOSCA Capability Type definition that all other TOSCA Capability Types derive from.
+ tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ valid_node_types:
+ type: string
+ required: true
+ description: >
+ A list of one or more names of Node Types that are supported as containees that declare the Container type as a Capability.
+ tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ - less_or_equal: 65535
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ tosca.capabilities.DatabaseEndpoint:
+ derived_from: tosca.capabilities.Endpoint
+ description: This is the default TOSCA type that should be used or extended to define a specialized database endpoint capability.
+ tosca.capabilities.Attachment:
+ derived_from: tosca.capabilities.Root
+ description: This is the default TOSCA type that should be used or extended to define a network endpoint capability.
+ tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_intances:
+ type: integer
+ default: 1
+ max_intances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+ default: 1
+ tosca.capabilities.Connectivity:
+ derived_from: tosca.capabilities.Root
+
+relationship_types:
+ tosca.relationships.Root:
+ abstract: true
+ description: This is the default (root) TOSCA Relationship Type definition that all other TOSCA Relationship Types derive from.
+ valid_targets: [ tosca.capabilities.Root ]
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ interfaces:
+ tosca.interfaces.relationship.Configure:
+ description: >
+ The lifecycle interfaces define the essential, normative operations that each TOSCA Relationship Types may support.
+ pre_configure_source:
+ description: Operation to pre-configure the source endpoint.
+ pre_configure_target:
+ description: Operation to pre-configure the target endpoint.
+ post_configure_source:
+ description: Operation to post-configure the source endpoint.
+ post_configure_target:
+ description: Operation to post-configure the target endpoint.
+ add_target:
+ description: Operation to notify the source node of a target node being added via a relationship.
+ add_source:
+ description: Operation to notify the target node of a source node which is now available via a relationship.
+ remove_target:
+ description: Operation to notify the source node of a target node being removed from a relationship.
+ remove_source:
+ description: Operation to notify the target node of a source node being removed from a relationship.
+ target_changed:
+ description: Operation to notify source some property or attribute of the target.
+ source_changed:
+ description: Operation to notify target some property or attribute of the source.
+ tosca.relationships.DependsOn:
+ derived_from: tosca.relationships.Root
+ description: >
+ A generic depends on relationship.
+ tosca.relationships.HostedOn:
+ derived_from: tosca.relationships.DependsOn
+ description: Relationship to use to describe that the source is hosted (installed/ deployed) on the target node.
+ valid_targets: [ tosca.capabilities.Container ]
+ tosca.relationships.ConnectsTo:
+ derived_from: tosca.relationships.DependsOn
+ valid_targets: [ tosca.capabilities.Endpoint ]
+ tosca.relationships.AttachTo:
+ derived_from: tosca.relationships.Root
+ valid_targets: [ tosca.capabilities.Attachment ]
+ properties:
+ location:
+ type: string
+ constraints:
+ - min_length: 1
+ device:
+ type: string
+ required: false
+ tosca.relationships.Network:
+ derived_from: tosca.relationships.Root
+ valid_sources: [ tosca.capabilities.Connectivity ]
+ valid_targets: [ tosca.capabilities.Connectivity ]
+
+artifact_types:
+ tosca.artifacts.Root:
+ description: The TOSCA Artifact Type all other TOSCA Artifact Types derive from.
+ tosca.artifacts.File:
+ derived_from: tosca.artifacts.Root
+ description: This artifact type is used when an artifact definition needs to have its associated file simply treated as a file and no special handling/handlers are invoked.
+ tosca.artifacts.ShellScript:
+ description: A shell script (.sh file)
+ file_ext: [ sh ]
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/images/software.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/images/software.png
new file mode 100644
index 0000000000..dc9c53245d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/images/software.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/normative-types-softwareComponent.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/normative-types-softwareComponent.yml
new file mode 100644
index 0000000000..04e04af640
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/softwareComponent/normative-types-softwareComponent.yml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-softwareComponent
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+
+ tosca.nodes.SoftwareComponent:
+ abstract: true
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA SoftwareComponent Node Type represents a generic software component
+ that can be managed and run by a TOSCA Compute Node Type.
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/software.png
+
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/images/network.png b/asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/images/network.png
new file mode 100644
index 0000000000..c8bf18f31a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/images/network.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/normative-types-webApplication.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/normative-types-webApplication.yml
new file mode 100644
index 0000000000..ded008ebdf
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/webApplication/normative-types-webApplication.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-webApplication
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-webServer:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.WebApplication:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA WebApplication node represents a software application that can be managed and run by a TOSCA WebServer node. Specific types of web applications such as Java, etc. could be derived from this type.
+ tags:
+ icon: /images/network.png
+ requirements:
+ - host: tosca.nodes.WebServer
+ type: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/components/normativeTypes/webServer/normative-types-webServer.yml b/asdc-tests/src/test/resources/CI/components/normativeTypes/webServer/normative-types-webServer.yml
new file mode 100644
index 0000000000..1c2e4b2ea5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/components/normativeTypes/webServer/normative-types-webServer.yml
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-webServer
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-softwareComponent:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.WebServer:
+ abstract: true
+ derived_from: tosca.nodes.SoftwareComponent
+ description: >
+ The TOSCA WebServer Node Type represents an abstract software component or service that is capable of hosting and providing management operations for one or more WebApplication nodes
+ capabilities:
+ app_endpoint: tosca.capabilities.Endpoint
+ secure_endpoint: tosca.capabilities.Endpoint
+ host:
+ type: tosca.capabilities.Container
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
diff --git a/asdc-tests/src/test/resources/CI/configuration.yaml b/asdc-tests/src/test/resources/CI/configuration.yaml
new file mode 100644
index 0000000000..cb91316a21
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/configuration.yaml
@@ -0,0 +1,382 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+
+# catalog backend hostname
+beFqdn: sdccatalog.att.com
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: /home/vagrant/catalog-be/config/catalog-be/titan.properties
+titanInMemoryGraph: false
+titanLockTimeout: 30
+titanReconnectIntervalInSeconds: 3
+titanHealthCheckReadTimeout: 1
+esReconnectIntervalInSeconds: 3
+uebHealthCheckReconnectIntervalInSeconds: 15
+uebHealthCheckReadTimeout: 4
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+neo4j:
+ host: neo4jhost
+ port: 7474
+ user: neo4j
+ password: "12345"
+
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # PLEASE NOTE: the timestamps are created in UTC/GMT timezone! This is needed so that timestamps will be correctly presented in Kibana.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+ - indexPrefix: monitoring_events
+ creationPeriod: month
+
+artifactTypes:
+ - CHEF
+ - PUPPET
+ - SHELL
+ - YANG
+ - YANG_XML
+ - HEAT
+ - BPEL
+ - DG_XML
+ - MURANO_PKG
+ - WORKFLOW
+ - NETWORK_CALL_FLOW
+ - OTHER
+
+licenseTypes:
+ - User
+ - Installation
+ - CPU
+
+#Deployment artifacts placeHolder
+resourceTypes: &allResourceTypes
+ - VFC
+ - CP
+ - VL
+ - VF
+
+# validForResourceTypes usage
+# validForResourceTypes:
+# - VF
+# - VL
+deploymentResourceArtifacts:
+ heat:
+ displayName: "Base HEAT Template"
+ type: HEAT
+ validForResourceTypes: *allResourceTypes
+ heatVol:
+ displayName: "Volume HEAT Template"
+ type: HEAT_VOL
+ validForResourceTypes: *allResourceTypes
+ heatNet:
+ displayName: "Network HEAT Template"
+ type: HEAT_NET
+ validForResourceTypes: *allResourceTypes
+
+deploymentResourceInstanceArtifacts:
+ heatEnv:
+ displayName: "HEAT ENV"
+ type: HEAT_ENV
+ description: "Auto-generated HEAT Environment deployment artifact"
+ fileExtension: "env"
+
+#Informational artifacts placeHolder
+excludeResourceCategory:
+ - Generic
+informationalResourceArtifacts:
+ features:
+ displayName: Features
+ type: OTHER
+ capacity:
+ displayName: Capacity
+ type: OTHER
+ vendorTestResult:
+ displayName: Vendor Test Result
+ type: OTHER
+ testScripts:
+ displayName: Test Scripts
+ type: OTHER
+ cloudQuestionnaire:
+ displayName: Cloud Questionnaire (completed)
+ type: OTHER
+ HEATTemplateFromVendor:
+ displayName: HEAT Template from Vendor
+ type: HEAT
+ resourceSecurityTemplate:
+ displayName: Resource Security Template
+ type: OTHER
+
+excludeServiceCategory:
+
+informationalServiceArtifacts:
+ serviceArtifactPlan:
+ displayName: Service Artifact Plan
+ type: OTHER
+ summaryOfImpactsToECOMPElements:
+ displayName: Summary of impacts to ECOMP elements,OSSs, BSSs
+ type: OTHER
+ controlLoopFunctions:
+ displayName: Control Loop Functions
+ type: OTHER
+ dimensioningInfo:
+ displayName: Dimensioning Info
+ type: OTHER
+ affinityRules:
+ displayName: Affinity Rules
+ type: OTHER
+ operationalPolicies:
+ displayName: Operational Policies
+ type: OTHER
+ serviceSpecificPolicies:
+ displayName: Service-specific Policies
+ type: OTHER
+ engineeringRules:
+ displayName: Engineering Rules (ERD)
+ type: OTHER
+ distributionInstructions:
+ displayName: Distribution Instructions
+ type: OTHER
+ certificationTestResults:
+ displayName: TD Certification Test Results
+ type: OTHER
+ deploymentVotingRecord:
+ displayName: Deployment Voting Record
+ type: OTHER
+ serviceQuestionnaire:
+ displayName: Service Questionnaire
+ type: OTHER
+ serviceSecurityTemplate:
+ displayName: Service Security Template
+ type: OTHER
+
+serviceApiArtifacts:
+ configuration:
+ displayName: Configuration
+ type: OTHER
+ instantiation:
+ displayName: Instantiation
+ type: OTHER
+ monitoring:
+ displayName: Monitoring
+ type: OTHER
+ reporting:
+ displayName: Reporting
+ type: OTHER
+ logging:
+ displayName: Logging
+ type: OTHER
+ testing:
+ displayName: Testing
+ type: OTHER
+
+
+additionalInformationMaxNumberOfKeys: 50
+
+systemMonitoring:
+ enabled: false
+ isProxy: false
+ probeIntervalInSeconds: 15
+
+defaultHeatArtifactTimeoutMinutes: 60
+
+serviceDeploymentArtifacts:
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ OTHER:
+ acceptedTypes:
+
+resourceDeploymentArtifacts:
+ HEAT:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_VOL:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NET:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VF_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VENDOR_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ APPC_CONFIG:
+ acceptedTypes:
+ - xml
+ - json
+ validForResourceTypes:
+ - VF
+ OTHER:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+
+resourceInstanceDeploymentArtifacts:
+ HEAT_ENV:
+ acceptedTypes:
+ - env
+
+resourceInformationalDeployedArtifacts:
+
+
+requirementsToFulfillBeforeCert:
+ CP:
+ - tosca.capabilities.network.Bindable
+
+capabilitiesToConsumeBeforeCert:
+
+unLoggedUrls:
+ - /sdc2/rest/healthCheck
+
+cleanComponentsConfiguration:
+ cleanIntervalInMinutes: 1440
+ componentsToClean:
+ - Resource
+ - Service
+
+artifactsIndex: resources
+
+cassandraConfig:
+ cassandraHosts: ['localhost']
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: false
+ username: koko
+ password: bobo
+ ssl: false
+ truststorePath : /path/path
+ truststorePassword : 123123
+ keySpaces:
+ - { name: sdcaudit, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdcartifact, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+
+heatEnvArtifactHeader:
+ ""
+heatEnvArtifactFooter:
+ ""
+
+onboarding:
+ protocol: http
+ host: localhost
+ port: 8181
+ downloadCsarUri: "/onboardingci/onbrest/onboarding-api/v1.0/vendor-software-products/packages"
+
+switchoverDetector:
+ gBeFqdn:
+ gFeFqdn:
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl: "http://localhost/crt/CipDomain.ECOMP-ASDC-DEVST/config/update_network?user=root"
+ publishNetworkBody: '{"note":"publish network"}'
+ groups:
+ beSet: { changePriorityUrl: "http://localhost/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"name":"AIO-BE.ecomp.idns.cip","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip","no_ad_redirection":false,"v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_be","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_be"],"failover_policy":["FAILALL"]},"comment":"AIO BE G-fqdn","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"comment":"AIO G-fqdn","name":"AIO-FE.ecomp.idns.cip","v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_fe","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_fe"],"failover_policy":["FAILALL"]},"no_ad_redirection":false,"intended_app_proto":"DNS","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip.att.com"}'}
+
+
+applicationL1Cache:
+ datatypes:
+ enabled: true
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: false
+ catalogL1Cache:
+ enabled: true
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 60
+ waitOnShutDownInMinutes: 30
+ numberOfCacheWorkers: 4
+
+toscaValidators:
+ stringMaxLength: 1024 \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/csars/FCGI_with_inputs.csar b/asdc-tests/src/test/resources/CI/csars/FCGI_with_inputs.csar
new file mode 100644
index 0000000000..a702c78d25
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/FCGI_with_inputs.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/LDSA1_with_inputs.csar b/asdc-tests/src/test/resources/CI/csars/LDSA1_with_inputs.csar
new file mode 100644
index 0000000000..a551ce3263
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/LDSA1_with_inputs.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts.csar
new file mode 100644
index 0000000000..a6678a47d1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_a.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_a.csar
new file mode 100644
index 0000000000..ca0ab91e39
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_a.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_b.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_b.csar
new file mode 100644
index 0000000000..890ae20cb3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_b.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming.csar
new file mode 100644
index 0000000000..04464af741
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_a.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_a.csar
new file mode 100644
index 0000000000..68819afc4b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_a.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_add_update.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_add_update.csar
new file mode 100644
index 0000000000..528599e0c5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_add_update.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_delete_update.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_delete_update.csar
new file mode 100644
index 0000000000..6a708d335f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_group_naming_delete_update.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_update.csar b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_update.csar
new file mode 100644
index 0000000000..035497a52e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/VF_RI2_G4_withArtifacts_update.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/jsonPropertyTypeTest.csar b/asdc-tests/src/test/resources/CI/csars/jsonPropertyTypeTest.csar
new file mode 100644
index 0000000000..2aa2ef4582
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/jsonPropertyTypeTest.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/mycompute.yml b/asdc-tests/src/test/resources/CI/csars/mycompute.yml
new file mode 100644
index 0000000000..c8a0c03384
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/mycompute.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.mycompute:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ properties:
+ propertyForTest:
+ type: string
+ description: test
+ required: true
+ default: success
+ # min_instances property should override property from tosca.capabilities.Scalable
+ min_instances:
+ type: integer
+ default: 3
+
diff --git a/asdc-tests/src/test/resources/CI/csars/mycompute2.yml b/asdc-tests/src/test/resources/CI/csars/mycompute2.yml
new file mode 100644
index 0000000000..9d479a1a55
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/mycompute2.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.mycompute2:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ properties:
+ propertyForTest:
+ type: string
+ description: test
+ required: true
+ default: success
+ # min_instances property should override property from tosca.capabilities.Scalable
+ min_instances:
+ type: integer
+ default: 3
+
diff --git a/asdc-tests/src/test/resources/CI/csars/mycompute_failed.yml b/asdc-tests/src/test/resources/CI/csars/mycompute_failed.yml
new file mode 100644
index 0000000000..a4d8e448f1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/mycompute_failed.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.mycompute:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ properties:
+ propertyForTest:
+ type: string
+ description: test
+ required: true
+ default: success
+ # min_instances property should override property from tosca.capabilities.Scalable
+ min_instances:
+ type: string
+ default: 3
+
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2G.csar b/asdc-tests/src/test/resources/CI/csars/orig2G.csar
new file mode 100644
index 0000000000..609cd0a8b4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2G.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2GV001.csar b/asdc-tests/src/test/resources/CI/csars/orig2GV001.csar
new file mode 100644
index 0000000000..a7e04cbf10
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2GV001.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2GV001_a.csar b/asdc-tests/src/test/resources/CI/csars/orig2GV001_a.csar
new file mode 100644
index 0000000000..07371acaf2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2GV001_a.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2GV006-remove-all-nested-artifacts.csar b/asdc-tests/src/test/resources/CI/csars/orig2GV006-remove-all-nested-artifacts.csar
new file mode 100644
index 0000000000..ebd95548f8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2GV006-remove-all-nested-artifacts.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2GV008-change-nested-oam-fileContent.csar b/asdc-tests/src/test/resources/CI/csars/orig2GV008-change-nested-oam-fileContent.csar
new file mode 100644
index 0000000000..dc75ae1549
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2GV008-change-nested-oam-fileContent.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2G_a.csar b/asdc-tests/src/test/resources/CI/csars/orig2G_a.csar
new file mode 100644
index 0000000000..2f6ad121a6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2G_a.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/orig2G_updated.csar b/asdc-tests/src/test/resources/CI/csars/orig2G_updated.csar
new file mode 100644
index 0000000000..1968eabf4b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/orig2G_updated.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/valid_vf.csar b/asdc-tests/src/test/resources/CI/csars/valid_vf.csar
new file mode 100644
index 0000000000..01bf159071
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/valid_vf.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/valid_vf_a.csar b/asdc-tests/src/test/resources/CI/csars/valid_vf_a.csar
new file mode 100644
index 0000000000..01bf159071
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/valid_vf_a.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/valid_vf_b.csar b/asdc-tests/src/test/resources/CI/csars/valid_vf_b.csar
new file mode 100644
index 0000000000..3c0b683321
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/valid_vf_b.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/valid_vf_c.csar b/asdc-tests/src/test/resources/CI/csars/valid_vf_c.csar
new file mode 100644
index 0000000000..57e3551682
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/valid_vf_c.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/valid_vf_d.csar b/asdc-tests/src/test/resources/CI/csars/valid_vf_d.csar
new file mode 100644
index 0000000000..d66dfb60d9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/valid_vf_d.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/valid_vf_f.csar b/asdc-tests/src/test/resources/CI/csars/valid_vf_f.csar
new file mode 100644
index 0000000000..a9a4bc75ab
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/valid_vf_f.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/vf_relate_by_cap_name.csar b/asdc-tests/src/test/resources/CI/csars/vf_relate_by_cap_name.csar
new file mode 100644
index 0000000000..f2a7efb69e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/vf_relate_by_cap_name.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop.csar b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop.csar
new file mode 100644
index 0000000000..df98e259d6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1.csar b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1.csar
new file mode 100644
index 0000000000..d4561a0640
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1_failed.csar b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1_failed.csar
new file mode 100644
index 0000000000..f145ebd5d2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop1_failed.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop_failed.csar b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop_failed.csar
new file mode 100644
index 0000000000..6e23829e8a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/vf_with_cap_prop_override_cap_type_prop_failed.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/csars/vmmc_relate_by_cap_name.csar b/asdc-tests/src/test/resources/CI/csars/vmmc_relate_by_cap_name.csar
new file mode 100644
index 0000000000..ada1451cea
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/csars/vmmc_relate_by_cap_name.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.json
new file mode 100644
index 0000000000..1bf593852b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "LAN_Connector.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.Connector.LAN_Connector",
+ "description": "Represents a LAN_Connector Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "org.openecomp.asdc.nodes.Connector.LAN_Connector"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.yml
new file mode 100644
index 0000000000..4fff368cdc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.yml
@@ -0,0 +1,23 @@
+org.openecomp.asdc.nodes.Connector.LAN_Connector:
+ #The LAN_Connector node connects Router and VNF_Container
+ derived_from: org.openecomp.asdc.nodes.Connector
+ properties:
+ network_id:
+ #or called: vlan_id
+ type: string
+ required: true
+ network_type:
+ #The technology types used by LAN connector
+ type: string
+ attributes:
+ network_ip_address:
+ #ip address is generated only after the node is instantiated at run-time
+ type: string
+ requirements:
+ - connectToRouter :
+ capability: tosca.capabilities.Root
+ node: org.openecomp.asdc.nodes.Router
+ - connectToVNF :
+ capability: tosca.capabilities.Root
+ node: org.openecomp.asdc.nodes.VNF_Container
+ #These two explicit requirements specify the LAN_Connector has two "connectTo" to connect to the node of Router type and VNF_Container type respectively
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.zip
new file mode 100644
index 0000000000..1b02f06386
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/LAN_Connector/LAN_Connector.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.json
new file mode 100644
index 0000000000..35b426c831
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "VNF.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.VNF",
+ "description": "Represents a VNF Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "org.openecomp.asdc.nodes.VNF"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.yml
new file mode 100644
index 0000000000..22be623303
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.yml
@@ -0,0 +1,16 @@
+org.openecomp.asdc.nodes.VNF:
+ #The VNF node is required to be hosted by a VNF_Container
+ derived_from: org.openecomp.asdc.nodes.Root
+ properties:
+ service_name:
+ type: string
+ attributes:
+ #attribute means the value is fulfilled at run-time.
+ service_id:
+ #In this case, we consider the VNF node only has the service_id value when the node is instantiated
+ type: string
+ requirements:
+ #the "host" requirement can establish the relationship with a VNF_Container node
+ - host :
+ capability: org.openecomp.asdc.capabilities.Container
+ node: org.openecomp.asdc.nodes.VNF_Container \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.zip
new file mode 100644
index 0000000000..c01081f0ab
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF/VNF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.json
new file mode 100644
index 0000000000..c1347780e3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "VNF_Container.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.VNF_Container",
+ "description": "Represents a VNF_Container Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "org.openecomp.asdc.nodes.VNF_Container"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.yml
new file mode 100644
index 0000000000..b8dd8355fa
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.yml
@@ -0,0 +1,10 @@
+org.openecomp.asdc.nodes.VNF_Container:
+ #The VNF_Container node provides the capability to host VNFs.
+ derived_from: org.openecomp.asdc.nodes.Root
+ capabilities:
+ host:
+ #The "host" capability allows other TOSCA nodes (VNF) that requires such a capability to connect to this node
+ type: org.openecomp.asdc.capabilities.Container
+ connectTo:
+ #The "connectTo" capability allows other TOSCA nodes (connectors) that requires such a capability to connect to this node.
+ type: org.openecomp.asdc.capabilities.Endpoint \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.zip
new file mode 100644
index 0000000000..a8e006e728
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/VNF_Container/VNF_Container.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.json
new file mode 100644
index 0000000000..4cb0880297
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "WAN_Connector.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.Connector.WAN_Connector",
+ "description": "Represents a WAN_Connector Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "org.openecomp.asdc.nodes.Connector.WAN_Connector"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.yml
new file mode 100644
index 0000000000..741d585488
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.yml
@@ -0,0 +1,25 @@
+org.openecomp.asdc.nodes.Connector.WAN_Connector:
+ #The WAN_Connector node only connects to Router
+ derived_from: org.openecomp.asdc.nodes.Connector
+ properties:
+ customer_id:
+ type: string
+ required: true
+ vpn_id:
+ type: string
+ required: true
+ vpn_name:
+ type: string
+ required: true
+ network_type:
+ #The technology types used by WAN connector
+ type: string
+ attributes:
+ network_ip_address:
+ #ip address is generated only after the node is instantiated at run-time
+ type: string
+ requirements:
+ - connectTo :
+ capability: org.openecomp.asdc.capabilities.Endpoint
+ node: org.openecomp.asdc.nodes.Router
+ #This explicit requirement specifies WAN_Connector has only one "connectTo" to connect to the node of Router type \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.zip
new file mode 100644
index 0000000000..e193820659
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/WAN_Connector/WAN_Connector.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.yml
new file mode 100644
index 0000000000..32989237d2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.yml
@@ -0,0 +1,4 @@
+org.openecomp.asdc.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Endpoint
+org.openecomp.asdc.capabilities.Container:
+ derived_from: tosca.capabilities.Container \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.zip
new file mode 100644
index 0000000000..869be32b53
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/capabilityTypesWanLan.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.json
new file mode 100644
index 0000000000..99be86c707
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "connector.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.Connector",
+ "description": "Represents a connector Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Abstract",
+ "tags": [
+ "org.openecomp.asdc.nodes.Connector"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.yml
new file mode 100644
index 0000000000..d9c9361daa
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.yml
@@ -0,0 +1,10 @@
+org.openecomp.asdc.nodes.Connector:
+ #Connector is the parent node of both WAN and LAN connectors.
+ derived_from: org.openecomp.asdc.nodes.Root
+ requirements:
+ - connectTo:
+ capability: org.openecomp.asdc.capabilities.Endpoint
+ #the requirement can establish the relationship with a node providing such capability
+ relationship: org.openecomp.asdc.relationships.ConnectsTo
+ #occurrences means how many times this requirement can appear in one node.
+ occurrences: [1, UNBOUNDED] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.zip
new file mode 100644
index 0000000000..3c37303a3a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/connector/connector.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/deleteResourcesLanWanDemo.sh b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/deleteResourcesLanWanDemo.sh
new file mode 100644
index 0000000000..9f992d56dd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/deleteResourcesLanWanDemo.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort>"
+}
+
+function deleteResource() {
+
+ ELEMENT_NAME=$1
+ echo "###################### Removing Element ${ELEMENT_NAME} Start ######################"
+ curl -X "DELETE" -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/resources/res_${ELEMENT_NAME}".1.0"
+ echo ""
+ echo "###################### Removing Element ${ELEMENT_NAME} End ########################"
+ echo ""
+ echo ""
+ echo ""
+}
+if [ $# -lt 2 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+
+
+deleteResource "org.openecomp.asdc.nodes.Connector.LAN_Connector"
+deleteResource "org.openecomp.asdc.nodes.Connector.WAN_Connector"
+deleteResource "org.openecomp.asdc.nodes.Connector"
+deleteResource "org.openecomp.asdc.nodes.VNF"
+deleteResource "org.openecomp.asdc.nodes.VNF_Container"
+deleteResource "org.openecomp.asdc.nodes.Router"
+deleteResource "org.openecomp.asdc.nodes.Root"
+
+
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/importResourcesLanWanDemo.sh b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/importResourcesLanWanDemo.sh
new file mode 100644
index 0000000000..90a06d53b5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/importResourcesLanWanDemo.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort>"
+}
+
+function addResource() {
+
+ ELEMENT_NAME=$1
+ echo -e "###################### Adding Element ${ELEMENT_NAME} Start ######################"
+ CURRENT_ZIP_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.zip
+ CURRENT_JSON_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.json
+ JSON_CONTENT=`paste -s ${CURRENT_JSON_FILE}`
+ curl -v -F resourceMetadata="${JSON_CONTENT}" -F resourceZip=@${CURRENT_ZIP_FILE} -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/upload/multipart
+ echo ""
+ echo -e "###################### Adding Element ${ELEMENT_NAME} End ########################"
+
+}
+if [ $# -lt 2 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+
+#Add The CapabilityTypes
+http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F capabilityTypeZip=@capabilityTypesWanLan.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/capability)
+if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding The CapabilityTypes status code:${http_code} End ########################\n\n\n"
+elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+else
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+fi
+
+addResource "root"
+addResource "router"
+addResource "VNF_Container"
+addResource "VNF"
+addResource "connector"
+addResource "WAN_Connector"
+addResource "LAN_Connector"
+
+
+
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.json
new file mode 100644
index 0000000000..54cbb798a2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "root.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.Root",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Abstract",
+ "tags": [
+ "org.openecomp.asdc.nodes.Root"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.yml
new file mode 100644
index 0000000000..34e7d0fb62
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.yml
@@ -0,0 +1,3 @@
+org.openecomp.asdc.nodes.Root:
+ #Define the root node for ASDC modeling
+ derived_from: tosca.nodes.Root \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.zip
new file mode 100644
index 0000000000..ff604d55a7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/root/root.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.json b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.json
new file mode 100644
index 0000000000..19e4669a38
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "router.yml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.asdc.nodes.Router",
+ "description": "Represents a Router Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "org.openecomp.asdc.nodes.Router"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.yml b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.yml
new file mode 100644
index 0000000000..0598663c64
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.yml
@@ -0,0 +1,13 @@
+org.openecomp.asdc.nodes.Router:
+ #The Router node has a property "routing_table" which is a list. The entry of the routing_table is the customized data type "RoutingTableEntry"
+ derived_from: org.openecomp.asdc.nodes.Root
+ properties:
+ routing_table:
+ type: list
+ entry_schema:
+ #"entry_schema" is the TOSCA spec to describe the type of the list item
+ type: RoutingTableEntry
+ capabilities:
+ connectTo:
+ #The "connectTo" capability allows other TOSCA nodes (connectors) that requires such a capability to connect to this node.
+ type: org.openecomp.asdc.capabilities.Endpoint \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.zip b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.zip
new file mode 100644
index 0000000000..57ff8e0e2a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/demoResourcesWanLan/router/router.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/error-configuration.yaml b/asdc-tests/src/test/resources/CI/error-configuration.yaml
new file mode 100644
index 0000000000..6d4e2373fa
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/error-configuration.yaml
@@ -0,0 +1,1576 @@
+# Errors
+errors:
+ OK: {
+ code: 200,
+ message: "OK"
+ }
+ CREATED: {
+ code: 201,
+ message: "OK"
+ }
+ NO_CONTENT: {
+ code: 204,
+ message: "No Content"
+ }
+#--------POL4050-----------------------------
+ NOT_ALLOWED: {
+ code: 405,
+ message: "Error: Method not allowed.",
+ messageId: "POL4050"
+ }
+#--------POL5000-----------------------------
+ GENERAL_ERROR: {
+ code: 500,
+ message: "Error: Internal Server Error. Please try again later.",
+ messageId: "POL5000"
+ }
+#---------POL5001------------------------------
+ MISSING_X_ECOMP_INSTANCE_ID: {
+ code: 400 ,
+ message: "Error: Missing 'X-ECOMP-InstanceID' HTTP header.",
+ messageId: "POL5001"
+ }
+#---------POL5002------------------------------
+ AUTH_REQUIRED: {
+ code: 401 ,
+ message: "Error: Authentication is required to use the API.",
+ messageId: "POL5002"
+ }
+#---------POL5003------------------------------
+ AUTH_FAILED: {
+ code: 403 ,
+ message: "Error: Not authorized to use the API.",
+ messageId: "POL5003"
+ }
+#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }
+#---------SVC4002-----------------------------
+ MISSING_INFORMATION: {
+ code: 403,
+ message: "Error: Missing information.",
+ messageId: "SVC4002"
+ }
+#---------SVC4003------------------------------
+# %1 - Users's USER_ID
+ USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: User '%1' was not found.",
+ messageId: "SVC4003"
+ }
+#---------SVC4004-----------------------------
+# %1 - Users's email address
+ INVALID_EMAIL_ADDRESS: {
+ code: 400,
+ message: "Error: Invalid email address '%1'.",
+ messageId: "SVC4004"
+ }
+#---------SVC4005------------------------------
+# %1 - role
+ INVALID_ROLE: {
+ code: 400,
+ message: "Error: Invalid role '%1'.",
+ messageId: "SVC4005"
+ }
+#---------SVC4006------------------------------
+# %1 - Users's USER_ID
+ USER_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: User with '%1' ID already exists.",
+ messageId: "SVC4006"
+ }
+#---------SVC4007------------------------------
+ DELETE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator can only be deleted by another administrator.",
+ messageId: "SVC4007"
+ }
+#---------SVC4008-----------------------------
+# %1 - Users's userId
+ INVALID_USER_ID: {
+ code: 400,
+ message: "Error: Invalid userId '%1'.",
+ messageId: "SVC4008"
+ }
+#---------SVC4049------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_CONTACT: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 contact id.",
+ messageId: "SVC4049"
+ }
+#---------SVC4050-----------------------------
+# %1 - Service/Resource/Additional parameter
+# %2 - service/resource/label name
+ COMPONENT_NAME_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: %1 with name '%2' already exists.",
+ messageId: "SVC4050"
+ }
+#---------SVC4051------------------------------
+# %1 - resource/service
+ COMPONENT_MISSING_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 category.",
+ messageId: "SVC4051"
+ }
+
+#---------SVC4052------------------------------
+ COMPONENT_MISSING_TAGS: {
+ code: 400,
+ message: "Error: Invalid Content. At least one tag has to be specified.",
+ messageId: "SVC4052"
+ }
+
+#---------SVC4053------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 description.",
+ messageId: "SVC4053"
+ }
+#---------SVC4054------------------------------
+# %1 - resource/service
+ COMPONENT_INVALID_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 category.",
+ messageId: "SVC4054"
+ }
+#---------SVC4055------------------------------
+ MISSING_VENDOR_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor name.",
+ messageId: "SVC4055"
+ }
+#---------SVC4056------------------------------
+ MISSING_VENDOR_RELEASE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor release.",
+ messageId: "SVC4056"
+ }
+
+#---------SVC4057------------------------------
+ MISSING_DERIVED_FROM_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing derived from template specification.",
+ messageId: "SVC4057"
+ }
+
+#---------SVC4058------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 icon.",
+ messageId: "SVC4058"
+ }
+#---------SVC4059------------------------------
+# %1 - service/resource
+ COMPONENT_INVALID_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 icon.",
+ messageId: "SVC4059"
+ }
+#---------SVC4060------------------------------
+ PARENT_RESOURCE_NOT_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Derived from resource template was not found.",
+ messageId: "SVC4060"
+ }
+#---------SVC4061------------------------------
+ MULTIPLE_PARENT_RESOURCE_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Multiple derived from resource template is not allowed.",
+ messageId: "SVC4061"
+ }
+
+#---------SVC4062------------------------------
+# %1 - service/resource
+ MISSING_COMPONENT_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 name.",
+ messageId: "SVC4062"
+ }
+#---------SVC4063------------------------------
+ #%1  -  resource/service name
+ RESOURCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource was not found.",
+ messageId: "SVC4063"
+ }
+
+#---------SVC4064------------------------------
+# %1 - Service/Resource
+ COMPONENT_INVALID_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description contains non-english characters.",
+ messageId: "SVC4064"
+ }
+#---------SVC4065------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name length
+ COMPONENT_DESCRIPTION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description exceeds limit of %2 characters.",
+ messageId: "SVC4065"
+ }
+#---------SVC4066------------------------------
+# %1 - max length
+ COMPONENT_TAGS_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Tags overall length exceeds limit of %1 characters.",
+ messageId: "SVC4066"
+ }
+#---------SVC4067------------------------------
+# %1 - max length
+ VENDOR_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor name exceeds limit of %1 characters.",
+ messageId: "SVC4067"
+ }
+#---------SVC4068------------------------------
+# %1 - max length
+ VENDOR_RELEASE_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor release exceeds limit of %1 characters.",
+ messageId: "SVC4068"
+ }
+
+#---------SVC4069------------------------------
+# %1 - Service/Resource/Product
+ COMPONENT_INVALID_CONTACT_ID: {
+ code: 400,
+ message: "Error: Invalid Content. %1 contact id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9",
+ messageId: "SVC4069"
+ }
+#---------SVC4070------------------------------
+# %1 - Service/Resource
+ INVALID_COMPONENT_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. %1 name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4070"
+ }
+
+#---------SVC4071------------------------------
+ INVALID_VENDOR_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4071"
+ }
+#---------SVC4072------------------------------
+ INVALID_VENDOR_RELEASE: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor release is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4072"
+ }
+#---------SVC4073------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name
+ COMPONENT_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 name exceeds limit of %2 characters.",
+ messageId: "SVC4073"
+ }
+#---------SVC4080------------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CHECKOUT_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for modification by %3 %4(%5).",
+ messageId: "SVC4080"
+ }
+#---------SVC4081-----------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CERT_IN_PROGRESS_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for certification by %3 %4(%5).",
+ messageId: "SVC4081"
+ }
+
+#-----------SVC4082---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_SENT_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is sent for certification by %3 %4(%5).",
+ messageId: "SVC4082"
+ }
+#-----------SVC4083---------------------------
+ COMPONENT_VERSION_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Version of this %1 was already promoted.",
+ messageId: "SVC4083"
+ }
+#-----------SVC4084---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CHECKED_IN: {
+ code: 409,
+ message: "Error: The current version of '%1' %2 was already checked-in by %3 %4(%5).",
+ messageId: "SVC4084"
+ }
+#-----------SVC4085---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_CHECKOUT_BY_ANOTHER_USER: {
+ code: 403,
+ message: "Error: %1 %2 has already been checked out by %3 %4(%5).",
+ messageId: "SVC4085"
+ }
+#-----------SVC4086---------------------------
+# %1  - resource/service name
+# %2  - resource/service
+ COMPONENT_IN_USE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is in use by another user.",
+ messageId: "SVC4086"
+ }
+#-----------SVC4087---------------------------
+# %1 - component name
+# %2 - resource/service/product
+ COMPONENT_HAS_NEWER_VERSION: {
+ code: 409,
+ message: "Error: Checking out of the requested version of the '%1' %2 is not allowed as a newer version exists.",
+ messageId: "SVC4087"
+ }
+#-----------SVC4088---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CERTIFIED: {
+ code: 403,
+ message: "Error: Requested %1 %2 has already been certified by %3 %4(%5).",
+ messageId: "SVC4088"
+ }
+#-----------SVC4089---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+ COMPONENT_NOT_READY_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification.",
+ messageId: "SVC4089"
+ }
+#-----------SVC4100---------------------------
+#%1 - property name
+ PROPERTY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' property was not found.",
+ messageId: "SVC4100"
+ }
+#-----------SVC4101---------------------------
+#%1 - property name
+ PROPERTY_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Property with '%1' name already exists.",
+ messageId: "SVC4101"
+ }
+
+#-----------SVC4102---------------------------
+# %1 - capability type name
+ CAPABILITY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Capability Type with name '%1' already exists.",
+ messageId: "SVC4102"
+ }
+#-----------SVC4114---------------------------
+ AUTH_FAILED_INVALIDE_HEADER: {
+ code: 400,
+ message: "Error: Invalid Authorization header.",
+ messageId: "SVC4114"
+ }
+#-----------SVC4115---------------------------
+# %1 - capability type name
+ MISSING_CAPABILITY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Capability Type '%1'.",
+ messageId: "SVC4115"
+ }
+ RESOURCE_INSTANCE_BAD_REQUEST: {
+ code: 400,
+ message: "Error: Invalid Content.",
+ messageId: "SVC4116"
+ }
+#-----------SVC4117---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_MATCH_NOT_FOUND: {
+ code: 404,
+ message: "Error: Match not found between resource instance '%1' and resource instance '%2' for requirement '%3'.",
+ messageId: "SVC4117"
+ }
+#-----------SVC4118---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Resource instances '%1' and '%2' are already associated with requirement '%3'.",
+ messageId: "SVC4118"
+ }
+#-----------SVC4119---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_RELATION_NOT_FOUND: {
+ code: 404,
+ message: "Error: No relation found between resource instances '%1' and '%2' for requirement '%3'.",
+ messageId: "SVC4119"
+ }
+#-----------SVC4120---------------------------
+# %1 - User's USER_ID
+ USER_INACTIVE: {
+ code: 404,
+ message: "Error: User %1 was not found.",
+ messageId: "SVC4120"
+ }
+#-----------SVC4121---------------------------
+# %1 - User's USER_ID
+ USER_HAS_ACTIVE_ELEMENTS: {
+ code: 403,
+ message: "Error: User with %1 ID can not be deleted since it has active elements(resources/services/artifacts).",
+ messageId: "SVC4121"
+ }
+#-----------SVC4122---------------------------
+# %1 - artifact type
+ ARTIFACT_TYPE_NOT_SUPPORTED: {
+ code: 400,
+ message: "Error: Invalid artifact type '%1'.",
+ messageId: "SVC4122"
+ }
+#-----------SVC4123---------------------------
+ ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Artifact logical name cannot be changed.",
+ messageId: "SVC4123"
+ }
+#-----------SVC4124---------------------------
+ MISSING_ARTIFACT_TYPE: {
+ code: 400,
+ message: "Error: Missing artifact type.",
+ messageId: "SVC4124"
+ }
+#-----------SVC4125---------------------------
+# %1-artifact name
+ ARTIFACT_EXIST: {
+ code: 400,
+ message: "Error: Artifact '%1' already exists.",
+ messageId: "SVC4125"
+ }
+#---------SVC4126------------------------------
+# %1 - resource/service/product/...
+# %2 - field (tag, vendor name...)
+ INVALID_FIELD_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 format.",
+ messageId: "SVC4126"
+ }
+#-----------SVC4127---------------------------
+ ARTIFACT_INVALID_MD5: {
+ code: 400,
+ message: "Error: Invalid artifact checksum.",
+ messageId: "SVC4127"
+ }
+#-----------SVC4128---------------------------
+ MISSING_ARTIFACT_NAME: {
+ code: 400,
+ message: "Error: Invalid content. Missing artifact name.",
+ messageId: "SVC4128"
+ }
+#-----------SVC4129---------------------------
+ MISSING_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing PROJECT_CODE number.",
+ messageId: "SVC4129"
+ }
+#-----------SVC4130---------------------------
+ INVALID_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. PROJECT_CODE number must be numeric from 5 up to 10 digits.",
+ messageId: "SVC4130"
+ }
+#-----------SVC4131---------------------------
+# %1-resource/service
+# %2-srtifact/artifacts
+# %3-semicolomn separated list of artifact
+ COMPONENT_MISSING_MANDATORY_ARTIFACTS: {
+ code: 403,
+ message: "Error: Missing mandatory informational %1 %2: [%3].",
+ messageId: "SVC4131"
+ }
+#-----------SVC4132---------------------------
+# %1 - lifecycle type name
+ LIFECYCLE_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Lifecycle Type with name '%1' already exists.",
+ messageId: "SVC4132"
+ }
+#-----------SVC4133---------------------------
+# %1 - service version
+# %2 - service name
+ SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION: {
+ code: 403,
+ message: "Error: Version %1 of '%2' service is not available for distribution.",
+ messageId: "SVC4133"
+ }
+#-----------SVC4134---------------------------
+ MISSING_LIFECYCLE_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing interface life-cycle type.",
+ messageId: "SVC4134"
+ }
+#---------SVC4135------------------------------
+ SERVICE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service category cannot be changed once the service is certified.",
+ messageId: "SVC4135"
+ }
+#---------SVC4136------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE: {
+ code: 500,
+ message: "Error: Requested distribution environment '%1' is not available.",
+ messageId: "SVC4136"
+ }
+#---------SVC4137------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_FOUND: {
+ code: 400,
+ message: "Error: Requested distribution environment '%1' was not found.",
+ messageId: "SVC4137"
+ }
+#---------SVC4138------------------------------
+ DISTRIBUTION_ENVIRONMENT_INVALID: {
+ code: 400,
+ message: "Error: Invalid distribution environment.",
+ messageId: "SVC4138"
+ }
+#---------SVC4139------------------------------
+# %1 - service name
+ DISTRIBUTION_ARTIFACT_NOT_FOUND: {
+ code: 409,
+ message: "Error: Service '%1' cannot be distributed due to missing deployment artifacts.",
+ messageId: "SVC4139"
+ }
+#---------SVC4200------------------------------
+# %1 - Service/Resource
+# %2 - max icon name length
+ COMPONENT_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 icon name exceeds limit of %2 characters.",
+ messageId: "SVC4200"
+ }
+#---------SVC4300------------------------------
+ RESTRICTED_ACCESS: {
+ code: 403,
+ message: "Error: Restricted access.",
+ messageId: "SVC4300"
+ }
+#---------SVC4301------------------------------
+ RESTRICTED_OPERATION: {
+ code: 409,
+ message: "Error: Restricted operation.",
+ messageId: "SVC4301"
+ }
+#---------SVC4500------------------------------
+ MISSING_BODY: {
+ code: 400 ,
+ message: "Error: Missing request body.",
+ messageId: "SVC4500"
+ }
+#---------SVC4501------------------------------
+ MISSING_PUBLIC_KEY: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'apiPublicKey'." ,
+ messageId: "SVC4501"
+ }
+#---------SVC4502------------------------------
+ DISTRIBUTION_ENV_DOES_NOT_EXIST: {
+ code: 400 ,
+ message: "Error: Invalid Body : Missing mandatory parameter 'distrEnvName'." ,
+ messageId: "SVC4502"
+ }
+#-----------SVC4503---------------------------
+# %1 - service name
+ SERVICE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' service was not found.",
+ messageId: "SVC4503"
+ }
+
+#---------SVC4504------------------------------
+# %1 - Service/Resource
+# %2 - service/resource version
+ COMPONENT_VERSION_NOT_FOUND: {
+ code: 404,
+ message: "Error: %1 version %2 was not found.",
+ messageId: "SVC4504"
+ }
+#-----------SVC4505---------------------------
+ #%1-artifact name
+
+ ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Artifact '%1' was not found.",
+ messageId: "SVC4505"
+ }
+#---------SVC4506------------------------------
+ MISSING_ENV_NAME: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'distrEnvName'.",
+ messageId: "SVC4506"
+ }
+#---------SVC4507------------------------------
+ COMPONENT_INVALID_TAGS_NO_COMP_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. One of the tags should be the component name.",
+ messageId: "SVC4507"
+ }
+
+#---------SVC4508------------------------------
+ SERVICE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service name cannot be changed once the service is certified.",
+ messageId: "SVC4508"
+ }
+
+#---------SVC4509------------------------------
+ SERVICE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the service is certified.",
+ messageId: "SVC4509"
+ }
+#---------SVC4510------------------------------
+# %1 - icon name max length
+ SERVICE_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Icon name exceeds limit of %1 characters.",
+ messageId: "SVC4510"
+ }
+#---------SVC4511------------------------------
+ DISTRIBUTION_REQUESTED_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested distribution was not found.",
+ messageId: "SVC4511"
+ }
+#---------SVC4512------------------------------
+# %1 - Distribution ID
+ DISTRIBUTION_REQUESTED_FAILED: {
+ code: 403,
+ message: "Error: Requested distribution '%1' failed.",
+ messageId: "SVC4512"
+ }
+#---------SVC4513------------------------------
+ RESOURCE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource category cannot be changed once the resource is certified.",
+ messageId: "SVC4513"
+ }
+#---------SVC4514------------------------------
+ RESOURCE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource name cannot be changed once the resource is certified.",
+ messageId: "SVC4514"
+ }
+#---------SVC4515------------------------------
+ RESOURCE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the resource is certified.",
+ messageId: "SVC4515"
+ }
+#---------SVC4516------------------------------
+ RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Vendor name cannot be changed once the resource is certified.",
+ messageId: "SVC4516"
+ }
+#---------SVC4517------------------------------
+ RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Derived from resource template cannot be changed once the resource is certified.",
+ messageId: "SVC4517"
+ }
+#---------SVC4518------------------------------
+# %1 - max length
+ COMPONENT_SINGLE_TAG_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Single tag exceeds limit of %1 characters.",
+ messageId: "SVC4518"
+ }
+#---------SVC4519------------------------------
+ INVALID_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: mismatch in data-type occurred for property %1. data type is %2 and default value found is %3.",
+ messageId: "SVC4519"
+ }
+#---------SVC4520------------------------------
+# %1 - service or resource
+ ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED: {
+ code: 409,
+ message: "Error: Maximal number of additional %1 parameters was reached.",
+ messageId: "SVC4520"
+ }
+#---------SVC4521------------------------------
+ ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED: {
+ code: 400,
+ message: "Error: Invalid Content. The Additional information label and value cannot be empty.",
+ messageId: "SVC4521"
+ }
+#---------SVC4522------------------------------
+# %1 - label/value
+# %2 - Maximal length of %1
+ ADDITIONAL_INFORMATION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Additional information %1 exceeds limit of %2 characters.",
+ messageId: "SVC4522"
+ }
+#---------SVC4523------------------------------
+ ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information label is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4523"
+ }
+#---------SVC4524------------------------------
+ ADDITIONAL_INFORMATION_NOT_FOUND: {
+ code: 409,
+ message: "Error: Requested additional information was not found.",
+ messageId: "SVC4524"
+ }
+#---------SVC4525------------------------------
+ ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information contains non-english characters.',
+ messageId: "SVC4525"
+ }
+#---------SVC4526------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found.",
+ messageId: "SVC4526"
+ }
+#---------SVC4527------------------------------
+ ASDC_VERSION_NOT_FOUND: {
+ code: 500,
+ message: 'Error: ASDC version cannot be displayed.',
+ messageId: "SVC4527"
+ }
+#---------SVC4528------------------------------
+# %1-artifact url/artifact label/artifact description/VNF Service Indicator
+ MISSING_DATA: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1.",
+ messageId: "SVC4528"
+ }
+#---------SVC4529------------------------------
+# %1-artifact url/artifact label/artifact description/artifact name
+# %2 - Maximal length of %1
+ EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 exceeds limit of %2 characters.",
+ messageId: "SVC4529"
+ }
+#---------SVC4530------------------------------
+ ARTIFACT_INVALID_TIMEOUT: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact Timeout should be set to valid positive non-zero number of minutes.",
+ messageId: "SVC4530"
+ }
+#---------SVC4531------------------------------
+ SERVICE_IS_VNF_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: VNF Indicator cannot be updated for certified service.",
+ messageId: "SVC4531"
+ }
+ #---------SVC4532------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found on the service '%2.",
+ messageId: "SVC4532"
+ }
+ #---------SVC4533------------------------------
+ # %1 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"/"YANG_XML"
+ WRONG_ARTIFACT_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for %1 artifact type.",
+ messageId: "SVC4533"
+ }
+
+#---------SVC4534------------------------------
+# %1 - "HEAT"/"HEAT_ENV"
+ INVALID_YAML: {
+ code: 400,
+ message: "Error: Uploaded YAML file for %1 artifact is invalid.",
+ messageId: "SVC4534"
+ }
+
+#---------SVC4535------------------------------
+# %1 - "HEAT"
+ INVALID_DEPLOYMENT_ARTIFACT_HEAT: {
+ code: 400,
+ message: "Error: Invalid %1 artifact.",
+ messageId: "SVC4535"
+ }
+#---------SVC4536------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"
+# %4 - "HEAT"/"HEAT_ENV"/"MURANO_PKG
+ DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact of %3 type .Please delete or update an existing %4 artifact.",
+ messageId: "SVC4536"
+ }
+
+#---------SVC4537------------------------------
+ MISSING_HEAT: {
+ code: 400,
+ message: "Error: Missing HEAT artifact. HEAT_ENV artifact cannot be uploaded without corresponding HEAT template.",
+ messageId: "SVC4537"
+ }
+#---------SVC4538------------------------------
+ MISMATCH_HEAT_VS_HEAT_ENV: {
+ code: 400,
+ message: "Error: Invalid artifact content. Parameter's set in HEAT_ENV '%1' artifact doesn't match the parameters in HEAT '%2' artifact.",
+ messageId: "SVC4538"
+ }
+#---------SVC4539------------------------------
+ INVALID_RESOURCE_PAYLOAD: {
+ code: 400,
+ message: "Error: Invalid resource payload.",
+ messageId: "SVC4539"
+ }
+#---------SVC4540------------------------------
+ INVALID_TOSCA_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for TOSCA template.",
+ messageId: "SVC4540"
+ }
+#---------SVC4541------------------------------
+ INVALID_YAML_FILE: {
+ code: 400,
+ message: "Error: Invalid YAML file.",
+ messageId: "SVC4541"
+ }
+#---------SVC4542------------------------------
+ INVALID_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid TOSCA template.",
+ messageId: "SVC4542"
+ }
+#---------SVC4543------------------------------
+ NOT_RESOURCE_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Imported Service TOSCA template.",
+ messageId: "SVC4543"
+ }
+#---------SVC4544------------------------------
+ NOT_SINGLE_RESOURCE: {
+ code: 400,
+ message: "Error: Imported TOSCA template should contain one resource definition.",
+ messageId: "SVC4544"
+ }
+#---------SVC4545------------------------------
+ INVALID_RESOURCE_NAMESPACE: {
+ code: 400,
+ message: "Error: Invalid resource namespace.",
+ messageId: "SVC4545"
+ }
+#---------SVC4546------------------------------
+ RESOURCE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: Imported resource already exists in ASDC Catalog.",
+ messageId: "SVC4546"
+ }
+#---------SVC4549------------------------------
+ INVALID_RESOURCE_CHECKSUM: {
+ code: 400,
+ message: "Error: Invalid resource checksum.",
+ messageId: "SVC4549"
+ }
+#---------SVC4550------------------------------
+ #%1  -  Consumer salt
+ INVALID_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 length.",
+ messageId: "SVC4550"
+ }
+ #---------SVC4551------------------------------
+ #%1  -  ECOMP User name
+ ECOMP_USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: ECOMP User '%1' was not found.",
+ messageId: "SVC4551"
+ }
+#---------SVC4552------------------------------
+ CONSUMER_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: ECOMP User already exists.",
+ messageId: "SVC4552"
+ }
+#---------SVC4553-----------------------------
+ #%1  -  Consumer name / Consumer password/ Consumer salt
+ INVALID_CONTENT_PARAM: {
+ code: 400,
+ message: "Error: %1 is invalid.",
+ messageId: "SVC4553"
+ }
+ #---------SVC4554------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested artifact doesn't belong to specified %1.",
+ messageId: "SVC4554"
+ }
+#---------SVC4554------------------------------
+# %1 - "Service name"
+ SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND: {
+ code: 403,
+ message: "Error: Requested '%1' service is not ready for certification. Service has to have at least one deployment artifact.",
+ messageId: "SVC4554"
+ }
+#---------SVC4555------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name length.",
+ messageId: "SVC4555"
+ }
+#---------SVC4556------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name format.",
+ messageId: "SVC4556"
+ }
+#---------SVC4557------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category name"
+ COMPONENT_CATEGORY_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: %1 category name '%2' already exists.",
+ messageId: "SVC4557"
+ }
+#---------SVC4558------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ VALIDATED_RESOURCE_NOT_FOUND: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource.",
+ messageId: "SVC4558"
+ }
+#---------SVC4559------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_ALREADY_VALIDATED_RESOURCE: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use already available validated resource version.",
+ messageId: "SVC4559"
+ }
+#---------SVC4560------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_LIST_VALIDATED_RESOURCES: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use one of available validated resource versions.",
+ messageId: "SVC4560"
+ }
+#---------SVC4561------------------------------
+# %1 - "resource"/"product"
+# %2 - "category"
+# %2 - "category name"
+ COMPONENT_CATEGORY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested %1 %2 '%3' was not found.",
+ messageId: "SVC4561"
+ }
+#---------SVC4562------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %2 - "category name"
+ COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY: {
+ code: 409,
+ message: "Error: %1 sub-category '%2' already exists under '%3' category.",
+ messageId: "SVC4562"
+ }
+#---------SVC4563------------------------------
+# %1 - "Product"
+# %2 - "grouping name"
+# %2 - "sub-category name"
+ COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY: {
+ code: 409,
+ message: "Error: %1 grouping '%2' already exists under '%3' sub-category.",
+ messageId: "SVC4563"
+ }
+#---------SVC4564------------------------------
+# %1 - product name
+ PRODUCT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' product was not found.",
+ messageId: "SVC4564"
+ }
+#---------SVC4565------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+# %3 - parameter name
+ INVALID_HEAT_PARAMETER_VALUE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Invalid %2 value set for '%3' parameter.",
+ messageId: "SVC4565"
+ }
+#---------SVC4566------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+ INVALID_HEAT_PARAMETER_TYPE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Unsupported '%2' parameter type.",
+ messageId: "SVC4566"
+ }
+#---------SVC4567------------------------------
+# %1 - "YANG_XML"
+ INVALID_XML: {
+ code: 400,
+ message: "Error: Uploaded XML file for %1 artifact is invalid.",
+ messageId: "SVC4567"
+ }
+#---------SVC4567------------------------------
+# %1 - "User Name and UserId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: User cannot be deleted. User '%1' has %2 projects.",
+ messageId: "SVC4567"
+ }
+#---------SVC4568------------------------------
+# %1 - "User Name and UserId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: Role cannot be changed. User '%1' has %2 projects.",
+ messageId: "SVC4568"
+ }
+#---------SVC4570------------------------------
+ UPDATE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator is not allowed to change his/her role.",
+ messageId: "SVC4570"
+ }
+#---------SVC4571------------------------------
+ SERVICE_CANNOT_CONTAIN_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Sub category cannot be defined for service",
+ messageId: "SVC4571"
+ }
+#---------SVC4572------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_TOO_MUCH_CATEGORIES: {
+ code: 400,
+ message: "Error: %1 must have only 1 category",
+ messageId: "SVC4572"
+ }
+#---------SVC4574------------------------------
+ RESOURCE_TOO_MUCH_SUBCATEGORIES: {
+ code: 400,
+ message: "Error: Resource must have only 1 sub category",
+ messageId: "SVC4574"
+ }
+#---------SVC4575------------------------------
+ COMPONENT_MISSING_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Missing sub category",
+ messageId: "SVC4575"
+ }
+ #---------SVC4576------------------------------
+# %1 - "component type"
+ UNSUPPORTED_ERROR: {
+ code: 400,
+ message: "Error : Requested component type %1 is unsupported.",
+ messageId: "SVC4576"
+ }
+ #---------SVC4577------------------------------
+# %1 - "resource type"
+ RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES: {
+ code: 409,
+ message: "Error : Resource of type %1 cannot contain resource instances.",
+ messageId: "SVC4577"
+ }
+#---------SVC4578------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "artifact name"
+ DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact named '%3'.",
+ messageId: "SVC4578"
+ }
+#---------SVC4579------------------------------
+# %1 - "Category"/"Sub-Category"/"Group"
+# %2 - category/sub-category/grouping name.
+ INVALID_GROUP_ASSOCIATION: {
+ code: 400,
+ message: "Error: Invalid group association. %1 '%2' was not found.",
+ messageId: "SVC4579"
+ }
+#---------SVC4580------------------------------
+ EMPTY_PRODUCT_CONTACTS_LIST: {
+ code: 400,
+ message: "Error: Invalid content. At least one Product Contact has to be specified.",
+ messageId: "SVC4580"
+ }
+#---------SVC4581------------------------------
+# %1 - userId
+ INVALID_PRODUCT_CONTACT: {
+ code: 400,
+ message: "Error: Invalid content. User '%1' cannot be set as Product Contact.",
+ messageId: "SVC4581"
+ }
+#---------SVC4582------------------------------
+# %1 - Product
+# %2 - "abbreviated"/"full"
+ MISSING_ONE_OF_COMPONENT_NAMES: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1 %2 name.",
+ messageId: "SVC4582"
+ }
+#---------SVC4583------------------------------
+# %1 - "Icon"
+# %2 - "resource"/"service"/"product"
+ COMPONENT_PARAMETER_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: %1 cannot be changed once the %2 is certified.",
+ messageId: "SVC4583"
+ }
+#---------SVC4584------------------------------
+# %1 - service/VF name
+# %2 - "service" /"VF"
+# %3 - resource instance origin type
+# %4 - resource instance name
+# %5 - requirement/capability
+# %6 - requirement/capability name
+# %7 - "fulfilled" (for req)/"consumed (for cap)"
+ REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification. %3 '%4' has to have %5 '%6' %7.",
+ messageId: "SVC4584"
+ }
+#---------SVC4585------------------------------
+ INVALID_OCCURRENCES: {
+ code: 400,
+ message: "Error: Invalid occurrences format.",
+ messageId: "SVC4585"
+ }
+#---------SVC4586------------------------------
+#---------SVC4586------------------------------
+ INVALID_SERVICE_API_URL: {
+ code: 400,
+ message: 'Error: Invalid Service API URL. Please check whether your URL has a valid domain extension and does not contain the following characters - #?&@%+;,=$<>~^`\[]{}|"*!',
+ messageId: "SVC4586"
+ }
+#---------SVC4587------------------------------
+# %1 - Data type name
+ DATA_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Data type %1 already exists.',
+ messageId: "SVC4587"
+ }
+#---------SVC4588------------------------------
+# %1 - Data type name
+ DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM: {
+ code: 400,
+ message: 'Error: Invalid Data type %1. Data type must have either a valid derived from declaration or at least one valid property',
+ messageId: "SVC4588"
+ }
+#---------SVC4589------------------------------
+# %1 - Data type name
+ DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Data type %1. 'properties' parameter cannot be empty if provided.",
+ messageId: "SVC4589"
+ }
+#---------SVC4590------------------------------
+# %1 - Property type name
+# %2 - Property name
+ INVALID_PROPERTY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Property type %1 in property %2.",
+ messageId: "SVC4590"
+ }
+#---------SVC4591------------------------------
+# %1 - Property inner type
+# %2 - Property name
+ INVALID_PROPERTY_INNER_TYPE: {
+ code: 400,
+ message: "Error: Invalid property inner type %1, in property %2",
+ messageId: "SVC4591"
+ }
+#---------SVC4592------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+ COMPONENT_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found.",
+ messageId: "SVC4592"
+ }
+#---------SVC4593------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+# %3 - "resource/"service"/"product"
+# %4 - container name
+ COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found on the %3 '%4'.",
+ messageId: "SVC4593"
+ }
+#---------SVC4594------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+ IMPORT_DUPLICATE_REQ_CAP_NAME: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains more than one %1 named '%2'.",
+ messageId: "SVC4594"
+ }
+#---------SVC4595------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+#%3 - parent containing the requirement
+ IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains %1 '%2' that is already defined by derived template %3.",
+ messageId: "SVC4595"
+ }
+#---------SVC4596------------------------------
+# %1 - Data type name
+ DATA_TYPE_DERIVED_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The ancestor data type %1 cannot be found in the system.",
+ messageId: "SVC4596"
+ }
+#---------SVC4597------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties named %2 which are already defined in one of its ancestors.",
+ messageId: "SVC4597"
+ }
+#---------SVC4598------------------------------
+# %1 - Data type name
+ DATA_TYPE_DUPLICATE_PROPERTY: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains duplicate property.",
+ messageId: "SVC4598"
+ }
+#---------SVC4599------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties %2 which their type is this data type.",
+ messageId: "SVC4599"
+ }
+#---------SVC4600------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_HAVE_PROPERTIES: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 cannot have properties since it is of type scalar",
+ messageId: "SVC4600"
+ }
+#---------SVC4601------------------------------
+ NOT_TOPOLOGY_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file %1 cannot be modeled to VF as it does not contain 'topology_template.",
+ messageId: "SVC4601"
+ }
+#---------SVC4602--------------------------------
+# %1 - yaml file name
+# %2 - node_template label
+# %3 - node_template type
+ INVALID_NODE_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file '%1' contains node_template '%2' of type '%3' that does not represent existing VFC/CP/VL",
+ messageId: "SVC4602"
+ }
+#---------SVC4603------------------------------
+# %1 - component type
+# %2 - component name
+# %3 - state
+ ILLEGAL_COMPONENT_STATE: {
+ code: 403,
+ message: "Error: Component instance of %1 can not be created because the component '%2' is in an illegal state %3.",
+ messageId: "SVC4603"
+ }
+#---------SVC4604------------------------------
+# %1 - csar file name
+ CSAR_INVALID: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. 'TOSCA-Metadata/Tosca.meta' file must be provided.",
+ messageId: "SVC4604"
+ }
+#---------SVC4605------------------------------
+# %1 - csar file name
+ CSAR_INVALID_FORMAT: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. Invalid 'TOSCA-Metadata/Tosca.meta' file format.",
+ messageId: "SVC4605"
+ }
+#---------SVC4606------------------------------
+# %1 - property name
+# %2 - property type
+# %3 - property innerType
+# %4 - default value is
+ INVALID_COMPLEX_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: Invalid default value of property %1. Data type is %2 with inner type %3 and default value found is %4.",
+ messageId: "SVC4606"
+ }
+#---------SVC4607------------------------------
+# %1 - csar file name
+ CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is not found.",
+ messageId: "SVC4607"
+ }
+#---------SVC4608------------------------------
+# %1 - artifact name
+# %2 - component type
+# %3 - actual component type
+ MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE: {
+ code: 400,
+ message: "Error: Artifact %1 is only compatible with component of type %2, but component type is %3.",
+ messageId: "SVC4608"
+ }
+
+#---------SVC4609------------------------------
+# %1 - "INVALID_JSON"
+ INVALID_JSON: {
+ code: 400,
+ message: "Error: Uploaded JSON file for %1 artifact is invalid.",
+ messageId: "SVC4609"
+ }
+#---------SVC4610------------------------------
+# %1 - csar file name
+# %2 - missing file name
+ YAML_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error - TOSCA CSAR %1 is invalid. TOSCA-Metadata/Tosca.meta refers to file %2 that is not provided.",
+ messageId: "SVC4610"
+ }
+#---------SVC4611------------------------------
+# %1 - group name
+ GROUP_MEMBER_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Group %1 member list was provided but does not have values",
+ messageId: "SVC4611"
+ }
+#---------SVC4612------------------------------
+# %1 - group name
+ GROUP_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Group type %1 already exists.',
+ messageId: "SVC4612"
+ }
+#---------SVC4613------------------------------
+# %1 - group name
+# %2 - VF name(component name)
+# %3 - actual component type [VF]
+ GROUP_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Group with name '%1' already exists in %2 %3.",
+ messageId: "SVC4613"
+ }
+#---------SVC4614------------------------------
+# %1 - group type
+ GROUP_TYPE_IS_INVALID: {
+ code: 400,
+ message: "Error: Invalid content. Group type %1 does not exist",
+ messageId: "SVC4614"
+ }
+#---------SVC4615------------------------------
+# %1 - group name
+ GROUP_MISSING_GROUP_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Group Type for group '%1'",
+ messageId: "SVC4615"
+ }
+#---------SVC4616------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - VF name
+# %4 - component type [VF ]
+ GROUP_INVALID_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of %3 %4.",
+ messageId: "SVC4616"
+ }
+#---------SVC4617------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - group type
+ GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of allowed members of group type %3.",
+ messageId: "SVC4617"
+ }
+#---------SVC4618------------------------------
+# %1 - missing file name
+# %2 - csar file name
+ ARTIFACT_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 is defined in CSAR %2 manifest but is not provided",
+ messageId: "SVC4618"
+ }
+#---------SVC4619------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - existing artifact type
+ ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 already exists in type %3.",
+ messageId: "SVC4619"
+ }
+#---------SVC4620------------------------------
+ FAILED_RETRIVE_ARTIFACTS_TYPES: {
+ code: 400,
+ message: "Error: Failed to retrieve list of suported artifact types.",
+ messageId: "SVC4620"
+ }
+#---------SVC4621------------------------------
+# %1 - artifact name
+# %2 - master
+ ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 already exists in master %2 .",
+ messageId: "SVC4621"
+ }
+#---------SVC4622------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - master name
+# %4 - master type
+ ARTIFACT_NOT_VALID_IN_MASTER: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 can not be exists under master %3 in type %4.",
+ messageId: "SVC4622"
+ }
+#---------SVC4623------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - env name
+# %4 - existing env
+ ARTIFACT_NOT_VALID_ENV: {
+ code: 400,
+ message: "Error: Artifact %1 in type %2 with env %3 already exists with another env %4",
+ messageId: "SVC4623"
+ }
+#---------SVC4624------------------------------
+# %1 - groups names
+# %2 - VF name
+# %3 - component type [VF ]
+ GROUP_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The groups '%1' cannot be found under %2 %3.",
+ messageId: "SVC4624"
+ }
+#---------SVC4625------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_ASSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already associated to group '%1'.",
+ messageId: "SVC4625"
+ }
+#---------SVC4626------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_DISSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already dissociated from group '%1'.",
+ messageId: "SVC4626"
+ }
+#---------SVC4627------------------------------
+# %1 - property name
+# %2 - group name
+# %3 - group type name
+ GROUP_PROPERTY_NOT_FOUND: {
+ code: 400,
+ message: "Error: property %1 listed in group %2 is not exist in group type %3.",
+ messageId: "SVC4627"
+ }
+#---------SVC4628------------------------------
+# %1 - csarUUID
+# %2 - VF name
+ VSP_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: The VSP with UUID %1 was already imported for VF %2. Please select another or update the existing VF.",
+ messageId: "SVC4628"
+ }
+#---------SVC4629------------------------------
+# %1 - VF name
+ MISSING_CSAR_UUID: {
+ code: 400,
+ message: "Error: The Csar UUID or payload name is missing for VF %1.",
+ messageId: "SVC4629"
+ }
+#---------SVC4630------------------------------
+# %1 - VF name
+# %2 - new csarUUID
+# %3 - old csarUUID
+ RESOURCE_LINKED_TO_DIFFERENT_VSP: {
+ code: 400,
+ message: "Error: Resource %1 cannot be updated using CsarUUID %2 since the resource is linked to a different VSP with csarUUID %3.",
+ messageId: "SVC4630"
+ }
+#---------SVC4631------------------------------
+# %1 - policy name
+ POLICY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Policy type %1 already exists.",
+ messageId: "SVC4631"
+ }
+#---------SVC4632------------------------------
+# %1 - target name
+# %2 - policy type name
+ TARGETS_NON_VALID: {
+ code: 400,
+ message: "Error: target %1 listed in policy type %2 is not a group or resource.",
+ messageId: "SVC4632"
+ }
+#---------SVC4633------------------------------
+# %1 - policy name
+ TARGETS_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Policy %1 target list was provided but does not have values",
+ messageId: "SVC4633"
+ }
+#---------SVC4634------------------------------
+ DATA_TYPE_CANNOT_BE_EMPTY: {
+ code: 500,
+ message: "Error: Data types are empty. Please import the data types.",
+ messageId: "SVC4634"
+ }
+#---------SVC4635------------------------------
+# %1 - csar uuid
+ RESOURCE_FROM_CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: resource from csar uuid %1 not found",
+ messageId: "SVC4635"
+ } \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.json b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.json
new file mode 100644
index 0000000000..2560a8acfa
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "HSS.yml",
+ "contactId": "jh0003",
+ "resourceName": "att.nodes.ims.HSS",
+ "description": "The home subscriber server (HSS), or user profile server function (UPSF), is a master user database that supports the IMS network entities that actually handle calls. It contains the subscription-related information (subscriber profiles), performs authentication and authorization of the user, and can provide information about the subscriber's location and IP information.",
+ "resourceIconPath": "defaulticon",
+ "category": "IMS",
+ "tags": [
+ "att.nodes.ims.HSS", "IMS_TAG"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml
new file mode 100644
index 0000000000..060059508a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml
@@ -0,0 +1,18 @@
+att.nodes.ims.HSS:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ icscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more I-CSCF functions
+ scscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more S-CSCF functions
+ requirements:
+ database_endpoint:
+ capability: tosca.capabilities.Endpoint.Database
+ node: tosca.nodes.Database
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml.old b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml.old
new file mode 100644
index 0000000000..c58191e85b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.yml.old
@@ -0,0 +1,28 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+description: >
+ The home subscriber server (HSS), or user profile server function (UPSF),
+ is a master user database that supports the IMS network entities that actually handle calls.
+ It contains the subscription-related information (subscriber profiles),
+ performs authentication and authorization of the user, and can provide information about the
+ subscriber's location and IP information.
+
+node_types:
+ att.nodes.ims.HSS:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ icscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more I-CSCF functions
+ scscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more S-CSCF functions
+ requirements:
+ database_endpoint:
+ capability: tosca.capabilities.Endpoint.Database
+ node: tosca.nodes.Database
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.zip b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.zip
new file mode 100644
index 0000000000..ed8949f445
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/HSS/HSS.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.json b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.json
new file mode 100644
index 0000000000..89bf58a2fb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "ICSCF.yml",
+ "contactId": "jh0003",
+ "resourceName": "att.nodes.ims.ICSCF",
+ "description": "Interrogating-CSCF (I-CSCF) is a SIP function located at the edge of an administrative domain. Its IP address is published in the Domain Name System (DNS) of the domain(using NAPTR and SRV type of DNS records), so that remote servers can find it, and use it as a forwarding point (e.g., registering) for SIP packets to this domain.It queries the HSS to retrieve the address of the S-CSCF and assign it to a user performing SIP registration. It also forwards SIP request or response to the S-CSCF.",
+ "resourceIconPath": "defaulticon",
+ "category": "IMS",
+ "tags": [
+ "att.nodes.ims.ICSCF", "IMS_TAG"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml
new file mode 100644
index 0000000000..a00685bc03
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml
@@ -0,0 +1,13 @@
+att.nodes.ims.ICSCF:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ pcscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more P-CSCF functions
+ requirements:
+ hss_endpoint:
+ capability: tosca.capabilities.Endpoint
+ node: att.nodes.ims.HSS
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml.old b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml.old
new file mode 100644
index 0000000000..183f8db3f4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.yml.old
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+description: >
+ Interrogating-CSCF (I-CSCF) is a SIP function located at the edge of an administrative domain.
+ Its IP address is published in the Domain Name System (DNS) of the domain
+ (using NAPTR and SRV type of DNS records), so that remote servers can find it,
+ and use it as a forwarding point (e.g., registering) for SIP packets to this domain.
+ It queries the HSS to retrieve the address of the S-CSCF and assign it to a user performing SIP
+ registration. It also forwards SIP request or response to the S-CSCF.
+
+node_types:
+ att.nodes.ims.ICSCF:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ pcscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more P-CSCF functions
+ requirements:
+ hss_endpoint:
+ capability: tosca.capabilities.Endpoint
+ node: att.nodes.ims.HSS
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.zip b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.zip
new file mode 100644
index 0000000000..228372e2f3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/ICSCF/ICSCF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.json b/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.json
new file mode 100644
index 0000000000..bf8ee08d30
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "PCSCF.yml",
+ "contactId": "jh0003",
+ "resourceName": "att.nodes.ims.PCSCF",
+ "description": "A Proxy-CSCF (P-CSCF) is a SIP proxy that is the first point of contact for the IMS terminal. It can be located either in the visited network (in full IMS networks) or in the home network (when the visited network is not IMS compliant yet). The P-CSCF is at its core a specialized SBC for the User network interface which not onlyprotects the network, but also the IMS terminal. The use of an additional SBC between the IMS terminal and the P-CSCF is unnecessary and infeasible due to the signaling being encrypted on this leg. The terminal discovers its P-CSCF with either DHCP, or it may be configured (e.g. during initial provisioning or via a 3GPP IMS Management Object (MO)) or in the ISIM or assigned in the PDP Context (in General Packet Radio Service (GPRS)).",
+ "resourceIconPath": "defaulticon",
+ "category": "IMS",
+ "tags": [
+ "att.nodes.ims.PCSCF", "IMS_TAG"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.yml b/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.yml
new file mode 100644
index 0000000000..3d0e38eefc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.yml
@@ -0,0 +1,13 @@
+att.nodes.ims.PCSCF:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ pcscf_endpoint:
+ type: tosca.capabilities.Endpoint.Public
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more UAs
+ requirements:
+ icscf_endpoint:
+ capability: tosca.capabilities.Endpoint
+ node: att.nodes.ims.ICSCF
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.zip b/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.zip
new file mode 100644
index 0000000000..37a6bac251
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/PCSCF/PCSCF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.json b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.json
new file mode 100644
index 0000000000..0f893ddb9b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "SCSCF.yml",
+ "resourceName": "att.nodes.ims.SCSCF",
+ "description": "A Serving-CSCF (S-CSCF) is the central node of the signaling plane. It is a SIP server, but performs session control too. It is always located in the home network. It uses Diameter Cx and Dx interfaces to the HSS to download user profiles and upload user-to-S-CSCF associations (the user profile is only cached locally for processing reasons only and is not changed). All necessary subscriber profile information is loaded from the HSS.",
+ "resourceIconPath": "defaulticon",
+ "category": "IMS",
+ "contactId": jh0003,
+ "tags": [
+ "att.nodes.ims.SCSCF", "IMS_TAG"
+ ]
+}
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml
new file mode 100644
index 0000000000..a2672ddb13
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml
@@ -0,0 +1,13 @@
+att.nodes.ims.SCSCF:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ icscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more I-CSCF functions
+ requirements:
+ hss_endpoint:
+ capability: tosca.capabilities.Endpoint
+ node: att.nodes.ims.HSS
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml.old b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml.old
new file mode 100644
index 0000000000..d8fb1b0d33
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.yml.old
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+description: >
+ A Serving-CSCF (S-CSCF) is the central node of the signalling plane.
+ It is a SIP server, but performs session control too. It is always located in the home network.
+ It uses Diameter Cx and Dx interfaces to the HSS to download user profiles and upload
+ user-to-S-CSCF associations (the user profile is only cached locally for processing reasons only
+ and is not changed). All necessary subscriber profile information is loaded from the HSS.
+
+node_types:
+ att.nodes.ims.SCSCF:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ icscf_endpoint:
+ type: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.ConnectTo
+ occurrences: [0, UNBOUNDED]
+ description: Connection with one ore more I-CSCF functions
+ requirements:
+ hss_endpoint:
+ capability: tosca.capabilities.Endpoint
+ node: att.nodes.ims.HSS
+ relationship: tosca.relationships.ConnectsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.zip b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.zip
new file mode 100644
index 0000000000..7a579dc5c1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/SCSCF/SCSCF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.json b/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.json
new file mode 100644
index 0000000000..7425778dd1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.json
@@ -0,0 +1,15 @@
+{
+ "payloadName": "compute.yml",
+ "contactId": "jh0003",
+ "resourceName": "tosca.nodes.Compute",
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing information are specified the cloud provider default
+ machine will be used. It is strongly recommended to specify the required CPUs and memory
+ at least.",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "tosca.nodes.Compute"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.yml b/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.yml
new file mode 100644
index 0000000000..b110157df3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Compute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.zip b/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.zip
new file mode 100644
index 0000000000..95a1325d92
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/compute/compute.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/deleteResourcesDemo.sh b/asdc-tests/src/test/resources/CI/importAttResources/deleteResourcesDemo.sh
new file mode 100644
index 0000000000..549a4ca78d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/deleteResourcesDemo.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort>"
+}
+
+function deleteResource() {
+
+ ELEMENT_NAME=$1
+ echo "###################### Removing Element ${ELEMENT_NAME} Start ######################"
+ curl -X "DELETE" -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/resources/res_${ELEMENT_NAME}".1.0"
+ echo ""
+ echo "###################### Removing Element ${ELEMENT_NAME} End ########################"
+ echo ""
+ echo ""
+ echo ""
+}
+if [ $# -lt 2 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+
+deleteResource "tosca.nodes.root"
+deleteResource "tosca.nodes.compute"
+deleteResource "tosca.nodes.softwarecomponent"
+deleteResource "tosca.nodes.loadbalancer"
+deleteResource "att.nodes.ims.hss"
+deleteResource "att.nodes.ims.icscf"
+deleteResource "att.nodes.ims.pcscf"
+deleteResource "att.nodes.ims.scscf"
+
+
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/importResourcesDemo.sh b/asdc-tests/src/test/resources/CI/importAttResources/importResourcesDemo.sh
new file mode 100644
index 0000000000..92beb9625a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/importResourcesDemo.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort>"
+}
+
+function addResource() {
+
+ ELEMENT_NAME=$1
+ echo "###################### Adding Element ${ELEMENT_NAME} Start ######################"
+ CURRENT_ZIP_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.zip
+ CURRENT_JSON_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.json
+ JSON_CONTENT=`paste -s ${CURRENT_JSON_FILE}`
+ curl -v -F resourceMetadata="${JSON_CONTENT}" -F resourceZip=@${CURRENT_ZIP_FILE} -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/upload/multipart
+ echo ""
+ echo "###################### Adding Element ${ELEMENT_NAME} End ########################"
+ echo ""
+ echo ""
+ echo ""
+}
+if [ $# -lt 2 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+
+addResource "root"
+addResource "compute"
+addResource "softwareComponent"
+addResource "loadBalancer"
+addResource "HSS"
+addResource "ICSCF"
+addResource "PCSCF"
+addResource "SCSCF"
+
+
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.json b/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.json
new file mode 100644
index 0000000000..d74dc3a3d5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "loadBalancer.yml",
+ "contactId": "jh0003",
+ "resourceName": "tosca.nodes.LoadBalancer",
+ "description": "Represents logical function that be used in conjunction with a Floating Address to distribute an application’s traffic (load) across a number of instances of the application (e.g., for a clustered or scaled application).",
+ "resourceIconPath": "defaulticon",
+ "category": "Infrastructure",
+ "tags": [
+ "tosca.nodes.LoadBalancer"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.yml b/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.yml
new file mode 100644
index 0000000000..53c053f0a6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.yml
@@ -0,0 +1,16 @@
+tosca.nodes.LoadBalancer:
+ derived_from: tosca.nodes.Root
+ properties:
+ # TBD
+ algorithm :
+ type: string
+ required: false
+ status: experimental
+ capabilities :
+ client:
+ type: tosca.capabilities.Endpoint.Public
+ description: the Floating (IP) client’s on the public network can connect to
+ requirements:
+ - application:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.RoutesTo
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.zip b/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.zip
new file mode 100644
index 0000000000..45edc204b5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/loadBalancer/loadBalancer.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/root/root.json b/asdc-tests/src/test/resources/CI/importAttResources/root/root.json
new file mode 100644
index 0000000000..6f96cf8b02
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/root/root.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "root.yml",
+ "contactId": "jh0003",
+ "resourceName": "tosca.nodes.Root",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Abstract",
+ "tags": [
+ "tosca.nodes.Root"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/root/root.yml b/asdc-tests/src/test/resources/CI/importAttResources/root/root.yml
new file mode 100644
index 0000000000..81aacd56ab
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/root/root.yml
@@ -0,0 +1,21 @@
+tosca.nodes.Root:
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.Root
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ interfaces:
+ Standard:
+ type: tosca.interfaces.node.lifecycle.Standard
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/root/root.zip b/asdc-tests/src/test/resources/CI/importAttResources/root/root.zip
new file mode 100644
index 0000000000..bdafdfbeb6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/root/root.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.json b/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.json
new file mode 100644
index 0000000000..22379bf695
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "softwareComponent.yml",
+ "contactId": "jh0003",
+ "resourceName": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "category": "Abstract",
+ "tags": [
+ "tosca.nodes.SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.yml b/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.yml
new file mode 100644
index 0000000000..287a376fc3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.yml
@@ -0,0 +1,15 @@
+tosca.nodes.SoftwareComponent:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.zip b/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.zip
new file mode 100644
index 0000000000..8e39b87808
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importAttResources/softwareComponent/softwareComponent.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.capabilities.yaml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.capabilities.yaml
new file mode 100644
index 0000000000..073eba5ee7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.capabilities.yaml
@@ -0,0 +1,13 @@
+asdc.capabilities.NetworkInterface:
+ derived_from: tosca.capabilities.Root
+
+asdc.capabilities.Container:
+ derived_from: tosca.capabilities.Container
+
+# binding a VDU to a CP
+asdc.capabilities.nfv.VirtualBindable:
+ derived_from: tosca.capabilities.Root
+
+# linking a VL to a CP
+asdc.capabilities.nfv.VirtualLinkable:
+ derived_from: tosca.capabilities.Root
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.json
new file mode 100644
index 0000000000..e1727aca5c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "asdc.nodes.Module.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.Module",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "asdc.nodes.Module", "module"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.yml
new file mode 100644
index 0000000000..b95156b5e1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.yml
@@ -0,0 +1,2 @@
+asdc.nodes.Module:
+ derived_from: tosca.nodes.Root
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.zip
new file mode 100644
index 0000000000..08003f1af8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Module/asdc.nodes.Module.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.json
new file mode 100644
index 0000000000..c52bbeebc2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "asdc.nodes.Network.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.Network",
+ "description": "This is the default AT&T TOSCA Network Node Type that all other AT&T Network TOSCA nodes should extends.",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "asdc.nodes.Network", "Network"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.yml
new file mode 100644
index 0000000000..0e56ef7bf2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.yml
@@ -0,0 +1,2 @@
+asdc.nodes.Network:
+ derived_from: tosca.nodes.Root
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.zip
new file mode 100644
index 0000000000..9b7fa35c2e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Network/asdc.nodes.Network.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.json
new file mode 100644
index 0000000000..5e1dda7106
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.json
@@ -0,0 +1,13 @@
+{
+ "payloadName": "asdc.nodes.Root.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.Root",
+ "description": "This is the default (root) AT&T TOSCA Node Type that all other AT&T TOSCA nodes should extends.
+ This allows all AT&T TOSCA nodes to have a consistent set of features for modeling and management (e.g, consistent definitions for requirements, capabilities, and lifecycle interfaces).",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "asdc.nodes.Root"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.yml
new file mode 100644
index 0000000000..ed8e1e46aa
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.yml
@@ -0,0 +1,3 @@
+asdc.nodes.Root:
+ derived_from: tosca.nodes.Root
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.zip
new file mode 100644
index 0000000000..8453bf3843
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.Root/asdc.nodes.Root.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.json
new file mode 100644
index 0000000000..7a122a57cd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.module.ECA_OAM.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.module.ECA_OAM",
+ "description": "Operation, Administration & Management of vMMSC ECA TRX",
+ "resourceIconPath": "applicationServer",
+ "category": "Application Layer 4+/Application Servers",
+ "tags": [
+ "asdc.nodes.module.ECA_OAM", "MMSC OAM", "ECA", "MMSC", "vMMSC", "Mobility"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.yml
new file mode 100644
index 0000000000..054c98439c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.yml
@@ -0,0 +1,9 @@
+asdc.nodes.module.ECA_OAM:
+ derived_from: asdc.nodes.Module
+ requirements:
+ - oam_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.OAM
+ - internal_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Internal
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.zip
new file mode 100644
index 0000000000..389ef18bae
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_OAM/asdc.nodes.module.ECA_OAM.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.json
new file mode 100644
index 0000000000..338099286f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.module.ECA_TRX.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.module.ECA_TRX",
+ "description": "External Content Adaption Transcoder. Transcodes and adapts MMS content according to the capabilities of receiving MS or UE.",
+ "resourceIconPath": "applicationServer",
+ "category": "Application Layer 4+/Application Servers",
+ "tags": [
+ "asdc.nodes.module.ECA_TRX", "Content Adaptation Transcoder", "MMSC", "vMMSC", "Mobility"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.yml
new file mode 100644
index 0000000000..e2a520dc1d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.yml
@@ -0,0 +1,12 @@
+asdc.nodes.module.ECA_TRX:
+ derived_from: asdc.nodes.Module
+ requirements:
+ - oam_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.OAM
+ - traffic_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Traffic
+ - internal_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Internal
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.zip
new file mode 100644
index 0000000000..f7911beb8f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.ECA_TRX/asdc.nodes.module.ECA_TRX.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.json
new file mode 100644
index 0000000000..8ce0b8a961
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.module.F5_LTM.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.module.F5_LTM",
+ "description": "vMMSC Local Traffic Manager serves as the load balancer for traffic coming into the vMMSC. Traffic from MMSC designated to external network passes via F5. Also, internal communication between ECA and MMSC is via F5",
+ "resourceIconPath": "loadBalancer",
+ "category": "Application Layer 4+/Load Balancer",
+ "tags": [
+ "asdc.nodes.module.F5_LTM", "F5", "Load Balancer", "vMMSC", "Mobility"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.yml
new file mode 100644
index 0000000000..c50cd0a481
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.yml
@@ -0,0 +1,12 @@
+asdc.nodes.module.F5_LTM:
+ derived_from: asdc.nodes.Module
+ requirements:
+ - core_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Core
+ - dmz_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.DMZ
+ - traffic_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Traffic
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.zip
new file mode 100644
index 0000000000..2bb9bbaa03
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.F5_LTM/asdc.nodes.module.F5_LTM.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.json
new file mode 100644
index 0000000000..4103880abf
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.module.MMSC.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.module.MMSC",
+ "description": "Accepts MMS messages via F5 and distributes among MMSCs. Messages are from user mobile device or from another Message Service",
+ "resourceIconPath": "applicationServer",
+ "category": "Application Layer 4+/Application Servers",
+ "tags": [
+ "asdc.nodes.module.MMSC", "MMSC", "vMMSC", "Mobility"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.yml
new file mode 100644
index 0000000000..b65b20f5b2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.yml
@@ -0,0 +1,12 @@
+asdc.nodes.module.MMSC:
+ derived_from: asdc.nodes.Module
+ requirements:
+ - oam_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.OAM
+ - cinder_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Cinder
+ - traffic_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Traffic
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.zip
new file mode 100644
index 0000000000..abf8dd8ced
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.MMSC/asdc.nodes.module.MMSC.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.json
new file mode 100644
index 0000000000..a289e4dd1b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.module.NEMS_BE.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.module.NEMS_BE",
+ "description": "The Back End of a Network Message Storage allows external multimedia content to use SMTP e-mail mechanism to submit MMS messages",
+ "resourceIconPath": "applicationServer",
+ "category": "Application Layer 4+/Application Servers",
+ "tags": [
+ "asdc.nodes.module.NEMS_BE", "MMSC", "vMMSC", "Network Message Storage", "Back End", "Mobility"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.yml
new file mode 100644
index 0000000000..b460bf57a5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.yml
@@ -0,0 +1,9 @@
+asdc.nodes.module.NEMS_BE:
+ derived_from: asdc.nodes.Module
+ requirements:
+ - oam_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.OAM
+ - internal_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Internal
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.zip
new file mode 100644
index 0000000000..2ee35f5cde
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_BE/asdc.nodes.module.NEMS_BE.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.json
new file mode 100644
index 0000000000..4d80b25712
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "asdc.nodes.module.NEMS_FE.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.module.NEMS_FE",
+ "description": "The Front End of a Network Message Storage allows external multimedia content to use SMTP e-mail mechanism to submit MMS messages",
+ "resourceIconPath": "objectStorage",
+ "category": "Application Layer 4+/Application Servers",
+ "tags": [
+ "asdc.nodes.module.NEMS_FE", "MMSC", "vMMSC", "Network Message Storage", "Front End", "Mobility"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.yml
new file mode 100644
index 0000000000..256aa7e4a3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.yml
@@ -0,0 +1,15 @@
+asdc.nodes.module.NEMS_FE:
+ derived_from: asdc.nodes.Module
+ requirements:
+ - oam_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.OAM
+ - cinder_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Cinder
+ - traffic_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Traffic
+ - internal_connection:
+ capability: asdc.capabilities.NetworkInterface
+ node: asdc.nodes.network.Internal
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.zip
new file mode 100644
index 0000000000..28eb1ccb60
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.module.NEMS_FE/asdc.nodes.module.NEMS_FE.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.json
new file mode 100644
index 0000000000..4217cd4f07
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.network.Cinder.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.network.Cinder",
+ "description": "Represents access to cloud shared storae.",
+ "resourceIconPath": "network",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "asdc.nodes.network.Cinder", "Network", "Cinder", "Cloud"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.yml
new file mode 100644
index 0000000000..54b34d23fe
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.yml
@@ -0,0 +1,5 @@
+asdc.nodes.network.Cinder:
+ derived_from: asdc.nodes.Network
+ capabilities:
+ cinder_connection:
+ type: asdc.capabilities.NetworkInterface
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.zip
new file mode 100644
index 0000000000..5210a39f29
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Cinder/asdc.nodes.network.Cinder.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.json
new file mode 100644
index 0000000000..343a785b6f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.network.Core.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.network.Core",
+ "description": "Represents Cloud Core network which provide interface to other services.",
+ "resourceIconPath": "network",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "asdc.nodes.network.Core", "Network", "Core", "Cloud"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.yml
new file mode 100644
index 0000000000..ac4ceed23e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.yml
@@ -0,0 +1,5 @@
+asdc.nodes.network.Core:
+ derived_from: asdc.nodes.Network
+ capabilities:
+ core_connection:
+ type: asdc.capabilities.NetworkInterface
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.zip
new file mode 100644
index 0000000000..7482fee585
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Core/asdc.nodes.network.Core.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.json
new file mode 100644
index 0000000000..50041339b6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.network.DMZ.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.network.DMZ",
+ "description": "Represents DMZ network.",
+ "resourceIconPath": "network",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "asdc.nodes.network.DMZ", "Network", "DMZ", "Cloud"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.yml
new file mode 100644
index 0000000000..df41ae4072
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.yml
@@ -0,0 +1,5 @@
+asdc.nodes.network.DMZ:
+ derived_from: asdc.nodes.Network
+ capabilities:
+ dmz_connection:
+ type: asdc.capabilities.NetworkInterface
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.zip
new file mode 100644
index 0000000000..fab96c9a64
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.DMZ/asdc.nodes.network.DMZ.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.json
new file mode 100644
index 0000000000..a2cf1c8026
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "asdc.nodes.network.Internal.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.network.Internal",
+ "description": "Represents Internal Network",
+ "resourceIconPath": "network",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "asdc.nodes.network.Internal", "Network", "Cloud"
+ ]
+}
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.yml
new file mode 100644
index 0000000000..01b6dece24
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.yml
@@ -0,0 +1,5 @@
+ asdc.nodes.network.Internal:
+ derived_from: asdc.nodes.Network
+ capabilities:
+ dmz_connection:
+ type: asdc.capabilities.NetworkInterface
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.zip
new file mode 100644
index 0000000000..2317ce5ad0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Internal/asdc.nodes.network.Internal.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.json
new file mode 100644
index 0000000000..161897ac22
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "asdc.nodes.network.OAM.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.network.OAM",
+ "description": "Represents AT&T Operation, Administration and Management (OA&M) network.",
+ "resourceIconPath": "network",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "asdc.nodes.network.OAM", "Network", "OAM", "Cloud"
+ ]
+}
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.yml
new file mode 100644
index 0000000000..0a0875ad07
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.yml
@@ -0,0 +1,5 @@
+asdc.nodes.network.OAM:
+ derived_from: asdc.nodes.Network
+ capabilities:
+ oam_connection:
+ type: asdc.capabilities.NetworkInterface
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.zip
new file mode 100644
index 0000000000..b2fbb78a7c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.OAM/asdc.nodes.network.OAM.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.json b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.json
new file mode 100644
index 0000000000..fb8269039c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "asdc.nodes.network.Traffic.yml",
+ "contactId": "jh0003",
+ "name": "asdc.nodes.network.Traffic",
+ "description": "Represents Network Traffic",
+ "resourceIconPath": "network",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "asdc.nodes.network.Traffic", "Network", "Cloud"
+ ]
+}
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.yml b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.yml
new file mode 100644
index 0000000000..b50bc8e612
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.yml
@@ -0,0 +1,5 @@
+asdc.nodes.network.Traffic:
+ derived_from: asdc.nodes.Network
+ capabilities:
+ dmz_connection:
+ type: asdc.capabilities.NetworkInterface
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.zip
new file mode 100644
index 0000000000..3add4e6ade
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/asdc.nodes.network.Traffic/asdc.nodes.network.Traffic.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/capabilityTypes.zip b/asdc-tests/src/test/resources/CI/importResource-MMSC/capabilityTypes.zip
new file mode 100644
index 0000000000..a1f0cdeee5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/capabilityTypes.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/deleteNormative.sh b/asdc-tests/src/test/resources/CI/importResource-MMSC/deleteNormative.sh
new file mode 100644
index 0000000000..fb46daa544
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/deleteNormative.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort> <resourceName>"
+}
+
+function deleteResource() {
+
+ ELEMENT_NAME=$1
+ echo -e "############### Removing Element ${ELEMENT_NAME} Start ######################"
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -X "DELETE" -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/resources/res_${ELEMENT_NAME}".1.0")
+ if [ ${http_code} -eq 204 ]; then
+ echo -e "\n############### Removing Element ${ELEMENT_NAME} status code:${http_code} End #######\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n############### Failed to remove Element ${ELEMENT_NAME} status code:${http_code} End #######\n\n\n"
+ exit 1
+ elif [ ${http_code} -eq 404 ]; then
+ echo -e "\n############### Element ${ELEMENT_NAME} not found status code:${http_code} End #######\n\n\n"
+ else
+ echo -e "\n############### Failed to remove Element ${ELEMENT_NAME} status code:${http_code} End #######\n\n\n"
+ exit 1
+ fi
+}
+if [ $# -lt 3 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+
+deleteResource $3
+
+exit 0
diff --git a/asdc-tests/src/test/resources/CI/importResource-MMSC/importNormative.sh b/asdc-tests/src/test/resources/CI/importResource-MMSC/importNormative.sh
new file mode 100644
index 0000000000..9e568dddc3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-MMSC/importNormative.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort> <userId>"
+}
+
+function addResource() {
+
+ ELEMENT_NAME=$1
+ echo -e "###################### Adding Element ${ELEMENT_NAME} Start ######################"
+ CURRENT_ZIP_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.zip
+ CURRENT_JSON_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.json
+ sed -i 's/"userId": ".*",/"userId": "'${ATT_UID}'",/' ${CURRENT_JSON_FILE}
+ JSON_CONTENT=`paste -s ${CURRENT_JSON_FILE}`
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F resourceMetadata="${JSON_CONTENT}" -F resourceZip=@${CURRENT_ZIP_FILE} -H USER_ID:${ATT_UID} ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/upload/multipart)
+ if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding Element ${ELEMENT_NAME} End ########################\n\n\n"
+ elif [ ${http_code} -eq 409 ]; then
+ echo -e "\n###################### Already exists Element ${ELEMENT_NAME} status code:${http_code} End ########################\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add Element ${ELEMENT_NAME} status code:${http_code} End ########################\n\n\n"
+ exit 1
+ fi
+}
+if [ $# -lt 3 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+ATT_UID=$3
+NO_CAPS=$4
+
+if [ "$NO_CAPS" = "nocaps" ]; then
+ echo "Skipping Caps import..."
+else
+ #Add The CapabilityTypes
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F capabilityTypeZip=@capabilityTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/capability)
+ if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding The CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+ else
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+ fi
+ #Add The InterfaceLifecycleTypes
+ #http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F interfaceLifecycleTypeZip=@interfaceLifecycleTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/interfaceLifecycle)
+ #if [ ${http_code} -eq 201 ]; then
+ # echo -e "\n###################### Adding The InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ #elif [ ${http_code} -eq 409 ]; then
+ # echo -e "\n###################### Already exists InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ #elif [ ${http_code} -eq 500 ]; then
+ # echo -e "\n###################### Failed to add InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ # exit 1
+ #else
+ # echo -e "\n###################### Failed to add InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ # exit 1
+ #fi
+fi
+
+addResource "asdc.nodes.Root"
+addResource "asdc.nodes.Network"
+addResource "asdc.nodes.network.Cinder"
+addResource "asdc.nodes.network.Core"
+addResource "asdc.nodes.network.DMZ"
+addResource "asdc.nodes.network.OAM"
+addResource "asdc.nodes.network.Traffic"
+addResource "asdc.nodes.network.Internal"
+addResource "asdc.nodes.Module"
+addResource "asdc.nodes.module.ECA_OAM"
+addResource "asdc.nodes.module.ECA_TRX"
+addResource "asdc.nodes.module.F5_LTM"
+addResource "asdc.nodes.module.MMSC"
+addResource "asdc.nodes.module.NEMS_BE"
+addResource "asdc.nodes.module.NEMS_FE"
+
+
+
+
+
+exit 0
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/VCE_Brocade_Tosca.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/VCE_Brocade_Tosca.yaml
new file mode 100644
index 0000000000..da71792698
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/VCE_Brocade_Tosca.yaml
@@ -0,0 +1,834 @@
+tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
+tosca_default_namespace: Need to figure out the namespace
+description: vCEBrocade and Base Types for VNF, Connection Point, Virtual Link, etc based on VNF Modeling Guidelines
+
+metadata:
+ template_name: VCE_Brocade.yaml
+ template_author: Kazi Farooqui (KF6593)
+ template_version: "0.1"
+
+#####################################################################
+############################ Data Types ###########################
+#####################################################################
+#data_types:
+
+###########################################################################################
+###################### NODE TYPE DEFINITIONS ############################################
+###########################################################################################
+
+node_types:
+ ##################################################################
+ ################### TOSCA VNF Node ##########################
+ ##################################################################
+ tosca.nodes.nfv.VNF:
+ derived_from: tosca.nodes.Root
+ properties:
+ vnf_category:
+ type: string
+ description: Router, FW, LB, DNS are example categories
+ id:
+ type: string
+ description: ID of this VNF
+ vendor:
+ type: string
+ description: name of the vendor who provides this VNF
+ version:
+ type: version
+ description: version of the software for this VNF
+ #device_config:
+ # type: map
+ #entry_schema:
+ # type: string
+ #default:
+ # p1: 1
+ # p2: 2
+ #p3: 3
+ #requirements:
+ # - virtualLink:
+ #capability: tosca.capabilities.nfv.VirtualLinkable
+ #occurrences: [1, UNBOUNDED]
+
+ ##################################################################
+ ################### ASDC VNF Node ##########################
+ ##################################################################
+ org.openecomp.resource.nfv.VNF:
+ derived_from: tosca.nodes.nfv.VNF
+
+
+ ##################################################################
+ ###################### TOSCA VDU Node ##########################
+ ##################################################################
+
+ tosca.nodes.nfv.VDU:
+ derived_from: tosca.nodes.SoftwareComponent
+ #properties:
+
+ capabilities:
+ high_availability:
+ type: tosca.capabilities.nfv.HA
+ occurrences: [1, UNBOUNDED] # this is default
+
+ virtual_binding:
+ type: tosca.capabilities.nfv.VDU.VirtualBindable
+ occurrences: [1, UNBOUNDED] # this is default
+
+ requirements:
+ - high_availability:
+ capability: tosca.capabilities.nfv.HA
+ occurrences: [0,1]
+
+ - host: ##Is hosted in a VM
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1] #this is default
+
+ ##################################################################
+ ###################### ASDC VDU Node###### ######################
+ ##################################################################
+ org.openecomp.resource.nfv.VDU:
+ derived_from: tosca.nodes.nfv.VDU
+ description: A VDU is a compute component of VNF
+
+ ##################################################################
+ ####################### TOSCA CP Node ##########################
+ ##################################################################
+ tosca.nodes.nfv.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualbinding:
+ capability: tosca.capabilities.nfv.VDU.VirtualBindable
+ occurrences: [1, 1] #this is default
+
+ - virtuallink:
+ capability: tosca.capabilities.nfv.VirtualLinkable
+ occurrences: [1, 1] #this is default
+
+ attributes:
+ ip_address:
+ type: string
+ # required: false
+
+ ##################################################################
+ ############################ ASDC CP Node #####################
+ ##################################################################
+ org.openecomp.resource.nfv.CP:
+ derived_from: tosca.nodes.nfv.CP
+
+ ##################################################################
+ ############### IPAG VLAN Connector CP Node #####################
+ ##################################################################
+ org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector:
+ derived_from: org.openecomp.resource.nfv.CP
+ properties:
+ ipag_network_name:
+ type: string
+
+ ipag_network_type:
+ type: string
+
+ ipag_network_uuid:
+ type: string
+
+ vlan_segment_ids:
+ type: list
+ entry_schema:
+ type: string
+ description: list of segmentation Ids.
+
+
+ #attributes:
+ capabilities:
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.IPAG_VLAN_Connector
+ description: This capability has been introduced to model this Connector as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ requirements:
+ - virtualBindingRequirement:
+ capability: org.openecomp.capabilities.nfv.VDU.VirtualBindable
+ #node: org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU
+ relationship: org.openecomp.relationships.nfv.VirtualBindsTo
+ occurrences: [1, 1] #default
+
+ - virtualLinkingrequirement:
+ capability: org.openecomp.capabilities.nfv.VirtualLinkable
+ #node: org.openecomp.resource.nfv.VL
+ relationship: org.openecomp.relationships.nfv.VirtualLinksTo
+ occurrences: [1, 1] #default
+
+ #interfaces:
+ #standard:
+ #configure:
+ #implementation: configure.sh
+
+ ##################################################################
+ ############### VPE VLAN Connector CP Node #####################
+ ##################################################################
+ org.openecomp.resource.nfv.CP.VPE_VLAN_Connector:
+ derived_from: org.openecomp.resource.nfv.CP
+ properties:
+ vpe_network_name:
+ type: string
+
+ vpe_network_type:
+ type: string
+
+ vpe_network_uuid:
+ type: string
+
+ vlan_segment_ids:
+ type: list
+ entry_schema:
+ type: string
+ description: list of segmentation Ids.
+
+
+ #attributes:
+ capabilities:
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.VPE_VLAN_Connector
+ description: This capability has been introduced to model this Connector as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ requirements:
+ - virtualBindingRequirement:
+ capability: org.openecomp.capabilities.nfv.VDU.VirtualBindable
+ #node: org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU
+ relationship: org.openecomp.relationships.nfv.VirtualBindsTo
+ occurrences: [1, 1] #default
+
+ - virtualLinkingrequirement:
+ capability: org.openecomp.capabilities.nfv.VirtualLinkable
+ #node: org.openecomp.resource.nfv.VL
+ relationship: org.openecomp.relationships.nfv.VirtualLinksTo
+ occurrences: [1, 1] #default
+
+ #interfaces:
+ #standard:
+ #configure:
+ #implementation: configure.sh
+ ##################################################################
+ ############### OAM VLAN Connector CP Node #####################
+ ##################################################################
+ org.openecomp.resource.nfv.CP.OAM_VLAN_Connector:
+ derived_from: org.openecomp.resource.nfv.CP
+ properties:
+ oam_network_name:
+ type: string
+
+ oam_network_type:
+ type: string
+
+ oam_network_uuid:
+ type: string
+
+ vlan_segment_ids:
+ type: list
+ entry_schema:
+ type: string
+ description: list of segmentation Ids.
+
+
+ #attributes:
+ capabilities:
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.OAM_VLAN_Connector
+ description: This capability has been introduced to model this Connector as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ requirements:
+ - virtualBindingRequirement:
+ capability: org.openecomp.capabilities.nfv.VDU.VirtualBindable
+ #node: org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU
+ relationship: org.openecomp.relationships.nfv.VirtualBindsTo
+ occurrences: [1, 1] #default
+
+ - virtualLinkingrequirement:
+ capability: org.openecomp.capabilities.nfv.VirtualLinkable
+ #node: org.openecomp.resource.nfv.VL
+ relationship: org.openecomp.relationships.nfv.VirtualLinksTo
+ occurrences: [1, 1] #default
+
+ #interfaces:
+ #standard:
+ #configure:
+ #implementation: configure.sh
+
+ ##################################################################
+ ############## TOSCA VL Node Type #######################
+ ##################################################################
+ tosca.nodes.nfv.VL:
+ derived_from: tosca.nodes.Root
+ properties:
+ vendor:
+ type: string
+ required: false
+ description: name of the vendor who provides this VL
+
+ capabilities:
+ virtuallinkable:
+ type: tosca.capabilities.nfv.VirtualLinkable
+ occurrences: [1, UNBOUNDED] # this is default
+
+##################################################################
+###################### ASDC VL Node TYPE ######################
+##################################################################
+ org.openecomp.resource.nfv.VL:
+ derived_from: tosca.nodes.nfv.VL
+
+
+##################################################################
+###################### ASDC VNF Node TYPE ######################
+##################################################################
+ org.openecomp.resource.nfv.VNF:
+ derived_from: tosca.nodes.nfv.VNF
+
+##################################################################
+###################### ASDC Routing Category VDU #################
+##################################################################
+
+ org.openecomp.resource.nfv.VDU.RoutingCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+
+##################################################################
+###################### vCE_Brocade_VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU:#paharoni - renamed from org.openecomp.resource.v/VCE_Brocade_VDU to keep convention
+ derived_from: org.openecomp.resource.nfv.VDU.RoutingCategoryVDU
+ description: Definition of VCE Brocade VDU
+ properties:
+ vnf_category:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ vce_type:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ vendor:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ version:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ image_id:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer -vCE image used to boot VM
+ flavor_id:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer - VM Size, SMALL, MEDIUM, LARGE
+ username:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer -login user Id of Router
+ password:
+ type: string
+ description: I Input provided at DESIGN TIME by Service Composer or Designer - password for login
+ domain_name:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer or (perhaps pre-defined for each Cloud Region) - login domain
+ time_zone:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer or (perhaps pre-defined for each Cloud Region) - time zone to set for vCE
+ login_session_timeout:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer or (perhaps pre-defined for each Cloud Region) - user login session timeout
+ ipag_vlan_bandwidth:
+ type: integer
+ description: Input provided at DESIGN TIME by Service Composer or Designer - bandwidth allocation of customer VLAN requested by customer
+ vpe_vlan_interface_qos_name:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer - QoS Policy name to be applied to this interface
+ ipag_vlan_interface_qos_name:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer - QoS Policy name to be applied to this interface
+ hostname:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - Host VM is created first and this value is input when the vCE image is later on installed by Cloud-PO
+ host_ip:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - Host VM IP
+ availability_zone:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - Cloud availability zone where VM is created
+ oam_loopback_vlan_ip_address:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - OAM Network is pre-created - already exists in Data Center
+ ipag_vlan_ip_address:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - IPAG VLAN Network is created by Cloud-PO before the vCE is created or installed
+ vpe_vlan_ip_address:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - VPE VLAN Network is created by Cloud-PO before the vCE is created / installed
+ vpe_vlan_id:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - VLAN Id is created first by Cloud-PO. It is required to apply VPE QoS Policy
+ ipag_vlan_id:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - VLAN Id is created first by Cloud-PO. It is required to apply IPAG QoS Policy
+ oam_vlan_interface_name:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - This interfce is created first by Cloud-PO. This is the interface to which QoS policy is applied
+ vpe_vlan_interface_name:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - This interfce is created first by Cloud-PO. This is the interface to which QoS policy is applied
+ ipag_vlan_interface_name:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - This interfce is created first by Cloud-PO. This is the interface to which QoS policy is applied
+ #############ATTRIBUTES of vCE - These values are outputted by Cloud-PO when vCE is created / installed ##############
+ attributes:
+ vce_name:
+ type: string
+ description: Name assigned to vCE by Cloud-PO
+ oam_loopback_vlan_ipaddres:
+ type: string
+ description: IP Address assigned by Cloud-PO to OAM VLAN
+ ipag_vlan_ipaddres:
+ type: string
+ description: IP Address assigned by Cloud-PO to IPAG VLAN
+ vpe_vlan_ipaddres:
+ type: string
+ description: IP Address assigned by Cloud-PO to VPE VLAN
+ vnf_id:
+ type: string
+ description: unique id assigned to VNF by Cloud-PO - used for AAI metadata
+
+ #############CAPABILITIES of vCE - #########################
+ capabilities:
+ ce_routing_capability:
+ type: org.openecomp.capabilities.nfv.CERouting
+ description: This is the base capability of vCE
+ occurrences: [1, UNBOUNDED] # default
+
+ static_routing:
+ type: org.openecomp.capabilities.nfv.StaticRouting
+ description: This is the optional capability of the vCE
+ occurrences: [0, UNBOUNDED]
+
+ bgp_routing:
+ type: org.openecomp.capabilities.nfv.BGPRouting
+ description: This is the optional capability of the vCE
+ occurrences: [0, UNBOUNDED]
+
+ virtual_bindable:
+ type: org.openecomp.capabilities.nfv.VDU.VirtualBindable# paharoni - changed to org.openecomp.capabilities.nfv.vdu.VirtualBindable from tosca.capabilities.nfv.VDU.VirtualBindable - was it intentional??
+ description: This is the internal capability of the VDU to support Binding to Connection Points
+ occurrences: [1, UNBOUNDED]
+
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.VCE_Brocade_Routing
+ description: This capability has been introduced to model VCE VDU as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ #############REQUIREMENTS of vCE - ###########################
+ requirements:
+ - nat: # A vCE may have a requirement to connect to a VNF offering this capability, during service composition
+ capability: org.openecomp.capabilities.nfv.VNF.NetworkAddressTranslationCapability
+ node: org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU
+ relationship: org.openecomp.relationships.nfv.CnnectsToNAT
+ occurrences: [0, 1]
+
+ - pat: # A vCE may have a requirement to connect to a VNF offering this capability, during service composition
+ capability: org.openecomp.capabilities.nfv.VNF.PortAddressTranslationCapability
+ node: org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU #paharoni - renamed from org.openecomp.resource.nfv.VDU.PortAddressTranslationCategoryVDU. Resource name can be up to 50 chars
+ relationship: org.openecomp.relationships.nfv.CnnectsToPAT
+ occurrences: [0, 1]
+
+
+ - firewall: # A vCE may have a requirement to connect to a VNF offering this capability, during service composition
+ capability: org.openecomp.capabilities.nfv.VNF.FirewallCapability
+ node: org.openecomp.resource.nfv.VDU.FirewallCategoryVDU
+ relationship: org.openecomp.relationships.nfv.CnnectsToFirewall
+ occurrences: [0, 1]
+
+ - dhcp: # A vCE may have a requirement to connect to a VNF offering this capability, during service composition
+ capability: org.openecomp.capabilities.nfv.VNF.DHCPCapability
+ node: org.openecomp.resource.nfv.VDU.DHCPCategoryVDU
+ relationship: org.openecomp.relationships.nfv.CnnectsToDHCP
+ occurrences: [0, 1]
+
+ - dns: # A vCE may have a requirement to connect to a VNF offering this capability, during service composition
+ capability: org.openecomp.capabilities.nfv.VNF.DNSCapability
+ node: org.openecomp.resource.nfv.VDU.DNSCategoryVDU
+ relationship: org.openecomp.relationships.nfv.CnnectsToDNS
+ occurrences: [0, 1]
+
+ - hosted_on: # A vCE needs to be hosted in a VM
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1] #default
+
+
+
+
+########################################################################################################
+###################### vCE_Brocade_VNF Node TYPE #####################################################
+########### IN THE ABSENCE OF NODE TEMPLATE, the vCE_Brocade_VNF Node Type is modeled as REQUIRING: #####
+########### 1. vce_Brocade_VDU Node
+########### 2. oam_vlan_connector Node
+########### 3. ipag_vlan_connector Node
+########### 4. vpe_vlan_connector Node
+########################################################################################################
+
+ org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF:
+ derived_from: org.openecomp.resource.nfv.VNF
+ requirements:
+ - vce_brocade_vdu:
+ capability: org.openecomp.capabilities.nfv.VCE_Brocade_Routing
+ node: org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU
+ occurrences: [1, 1]
+
+ - oam_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.OAM_VLAN_Connector
+ node: org.openecomp.resource.nfv.CP.OAM_VLAN_Connector
+ occurrences: [1, 1]
+
+ - vpe_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.VPE_VLAN_Connector
+ node: org.openecomp.resource.nfv.CP.VPE_VLAN_Connector
+ occurrences: [1, 1]
+
+ - ipag_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.IPAG_VLAN_Connector
+ node: org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector
+ occurrences: [1, 1]
+
+##################################################################
+###################### ASDC NAT VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU: #paharoni - renamed from org.openecomp.resource.nfv.VDU.NetworkAddressTranslationCategoryVDU. Resource name can be up to 50 chars in ASDC
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ nat:
+ type: org.openecomp.capabilities.nfv.VNF.NetworkAddressTranslationCapability
+ description: This capability has been introduced
+ ###############Further details described in NAT VNF TOSCA Model
+
+##################################################################
+###################### PAT VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU: #paharoni - renamed from org.openecomp.resource.nfv.VDU.PortAddressTranslationCategoryVDU. Resource name can be up to 50 chars in ASDC
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ pat:
+ type: org.openecomp.capabilities.nfv.VNF.PortAddressTranslationCapability
+ description: This capability has been introduced
+ ###############Further details described in PAT VNF TOSCA Model
+
+##################################################################
+###################### Firewall VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.FirewallCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ firewall:
+ type: org.openecomp.capabilities.nfv.VNF.FirewallCapability
+ description: This capability has been introduced
+ ###############Further details described in Firewall VNF TOSCA Model
+
+##################################################################
+###################### DHCP VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.DHCPCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ dhcp:
+ type: org.openecomp.capabilities.nfv.VNF.DHCPCapability
+ description: This capability has been introduced
+ ###############Further details described in DHCP VNF TOSCA Model
+
+##################################################################
+###################### DNS VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.DNSCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ dns:
+ type: org.openecomp.capabilities.nfv.VNF.DNSCapability
+ description: This capability has been introduced
+ ###############Further details described in DNS VNF TOSCA Model
+
+
+###########################################################################################
+###################### CAPABILITY TYPE DEFINITIONS ######################################
+###########################################################################################
+
+capability_types:
+ #####################################################################################
+ ###################### TOSCA Capability: VirtualBindable ##########################
+ #####################################################################################
+ tosca.capabilities.nfv.VDU.VirtualBindable:
+ derived_from: tosca.capabilities.Root
+ valid_source_types: [tosca.nodes.nfv.VDU]
+
+ #####################################################################################
+ ############################ ASDC Capability: VirtualBindable #####################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VDU.VirtualBindable:
+ derived_from: tosca.capabilities.nfv.VDU.VirtualBindable
+ valid_source_types: [org.openecomp.resource.nfv.VDU]
+
+ #####################################################################################
+ ##################### TOSCA Capability: VirtualLinkable ##########################
+ #####################################################################################
+ tosca.capabilities.nfv.VirtualLinkable:
+ derived_from: tosca.capabilities.Root ##paharoni - changed from tosca.capabilities.nfv.VirtualLinkable - cannot derive from itself
+ valid_source_types: [org.openecomp.resource.nfv.VL]
+
+ #####################################################################################
+ ############################ ASDC Capability: VirtualLinkable #####################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VirtualLinkable:
+ derived_from: tosca.capabilities.Root
+ valid_source_types: [tosca.nodes.nfv.VL]
+
+ #####################################################################################
+ ############################ Capability: HighAvailability ##########################
+ #####################################################################################
+ tosca.capabilities.nfv.HA:
+ derived_from: tosca.capabilities.Root
+ valid_source_types: [tosca.nodes.nfv.VDU]
+
+ #####################################################################################
+ ######################Capability: Generic asdc capability ##########################
+ #####################################################################################
+ org.openecomp.capabilities.Root:
+ derived_from: tosca.capabilities.Root
+
+ #####################################################################################
+ ############################ Capability: GENERIC ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.RoutingCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ routing_type:
+ type: string
+ description: some basic routing descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.VDU.RoutingCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: CE ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.CERouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: PE ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.PERouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: P ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.PRouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: BGP ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.BGPRouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: STATIC ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.StaticRouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: Network Address Translation ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VNF.NetworkAddressTranslationCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ nat_type:
+ type: string
+ description: some basic NAT descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: Port Address Translation ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VNF.PortAddressTranslationCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ pat_type:
+ type: string
+ description: some basic PAT descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: FIREWALL ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VNF.FirewallCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ firewall_type:
+ type: string
+ description: some basic Firewall Capability descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.VDU.FirewallCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: DHCP ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VNF.DHCPCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ dhcp_type:
+ type: string
+ description: some basic DHCP Capability descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.VDU.DHCPCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: DNS ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VNF.DNSCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ dns_type:
+ type: string
+ description: some basic DNS Capability descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.VDU.DNSCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: vCE Brocade ROUTING ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC BROCADE VDU ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vCE_Brocade_Routing:
+ derived_from: org.openecomp.capabilities.nfv.CERouting
+ valid_source_types: [org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU]
+
+ #####################################################################################
+ ############################ Capability: OAM VLAn Connector ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC Connector ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.OAM_VLAN_Connector:
+ derived_from: org.openecomp.capabilities.Root
+ valid_source_types: [org.openecomp.resource.nfv.CP.OAM_VLAN_Connector]
+
+ #####################################################################################
+ ############################ Capability: IPAG VLAn Connector ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC Connector ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.IPAG_VLAN_Connector:
+ derived_from: org.openecomp.capabilities.Root
+ valid_source_types: [org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector]
+
+ #####################################################################################
+ ############################ Capability: VPE VLAn Connector ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC Connector ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VPE_VLAN_Connector:
+ derived_from: org.openecomp.capabilities.Root
+ valid_source_types: [org.openecomp.resource.nfv.CP.VPE_VLAN_Connector]
+
+###########################################################################################
+###################### RELATIONSHIP TYPE DEFINITIONS ###################################
+###########################################################################################
+
+relationship_types:
+#####################################################################################
+######################TOSCA Relationship: VirtualBindsTo ##########################
+#####################################################################################
+ tosca.relationships.nfv.VirtualBindsTo:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between VDU and CP Node Types
+ valid_target_types: [tosca.capabilities.nfv.VDU.VirtualBindable]
+
+#####################################################################################
+######################ASDC Relationship: VirtualBindsTo ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.VirtualBindsTo:
+ derived_from: tosca.relationships.nfv.VirtualBindsTo
+ description: Represents the relationsHIP between VDU and CP Node Types
+ valid_target_types: [org.openecomp.capabilities.nfv.VDU.VirtualBindable]
+
+#####################################################################################
+##################### TOSCA Relationship: VirtualLinksTo ##########################
+#####################################################################################
+ tosca.relationships.nfv.VirtualLinksTo:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between CP and VL Node Types
+ valid_target_types: [tosca.capabilities.nfv.VirtualLinkable]
+
+#####################################################################################
+##################### ASDC Relationship: VirtualLinksTo ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.VirtualLinksTo:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between CP and VL Node Types
+ valid_target_types: [org.openecomp.capabilities.nfv.VirtualLinkable]
+
+#####################################################################################
+###################### ASDC Relationship: ConnectsToNAT ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.CnnectsToNAT:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between a VNF and NAT capable Node
+ #properties:
+ #attributes:
+ #interfaces:
+ valid_target_types: [org.openecomp.capabilities.nfv.VNF.NetworkAddressTranslationCapability]
+
+#####################################################################################
+###################### ASDC Relationship: ConnectsToPAT ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.CnnectsToPAT:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between a VNF and PAT capable Node
+ #properties:
+ #attributes:
+ #interfaces:
+ valid_target_types: [org.openecomp.capabilities.nfv.VNF.PortAddressTranslationCapability]
+
+#####################################################################################
+###################### ASDC Relationship: ConnectsToFirewall ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.CnnectsToFirewall:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between a VNF and Firewall capable Node
+ #properties:
+ #attributes:
+ #interfaces:
+ valid_target_types: [org.openecomp.capabilities.nfv.VNF.FirewallCapability]
+
+#####################################################################################
+###################### ASDC Relationship: ConnectsToDNS ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.CnnectsToDNS:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between a VNF and DNS capable Node
+ #properties:
+ #attributes:
+ #interfaces:
+ valid_target_types: [org.openecomp.capabilities.nfv.VNF.DNSCapability]
+
+#####################################################################################
+###################### ASDC Relationship: ConnectsToDHCP ##########################
+#####################################################################################
+ org.openecomp.relationships.nfv.CnnectsToDHCP:
+ derived_from: tosca.relationships.ConnectsTo
+ description: Represents the relationsHIP between a VNF and DHCP capable Node
+ #properties:
+ #attributes:
+ #interfaces:
+ valid_target_types: [org.openecomp.capabilities.nfv.VNF.DHCPCapability]
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/capabilityTypes.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/capabilityTypes.zip
new file mode 100644
index 0000000000..75f56d4c76
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/capabilityTypes.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/importVceBrocade.sh b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/importVceBrocade.sh
new file mode 100644
index 0000000000..76f567bfe2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/importVceBrocade.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort> <userId>"
+}
+
+function addResource() {
+
+ ELEMENT_NAME=$1
+ echo -e "###################### Adding Element ${ELEMENT_NAME} Start ######################"
+ CURRENT_ZIP_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.zip
+ CURRENT_JSON_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.json
+ sed -i 's/"userId": ".*",/"userId": "'${ATT_UID}'",/' ${CURRENT_JSON_FILE}
+ JSON_CONTENT=`paste -s ${CURRENT_JSON_FILE}`
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F resourceMetadata="${JSON_CONTENT}" -F resourceZip=@${CURRENT_ZIP_FILE} -H USER_ID:${ATT_UID} ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/upload/multipart)
+ if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding Element ${ELEMENT_NAME} End ########################\n\n\n"
+ elif [ ${http_code} -eq 409 ]; then
+ echo -e "\n###################### Already exists Element ${ELEMENT_NAME} status code:${http_code} End ########################\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add Element ${ELEMENT_NAME} status code:${http_code} End ########################\n\n\n"
+ exit 1
+ fi
+}
+if [ $# -lt 3 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+ATT_UID=$3
+NO_CAPS=$4
+
+if [ "$NO_CAPS" = "nocaps" ]; then
+ echo "Skipping Caps import..."
+else
+ #Add The CapabilityTypes
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F capabilityTypeZip=@capabilityTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/capability)
+ if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding The CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+ else
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+ fi
+ #Add The InterfaceLifecycleTypes
+ #http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F interfaceLifecycleTypeZip=@interfaceLifecycleTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/interfaceLifecycle)
+ #if [ ${http_code} -eq 201 ]; then
+ # echo -e "\n###################### Adding The InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ #elif [ ${http_code} -eq 409 ]; then
+ # echo -e "\n###################### Already exists InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ #elif [ ${http_code} -eq 500 ]; then
+ # echo -e "\n###################### Failed to add InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ # exit 1
+ #else
+ # echo -e "\n###################### Failed to add InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ # exit 1
+ #fi
+fi
+
+addResource "tosca.nodes.nfv.CP"
+addResource "tosca.nodes.nfv.VDU"
+
+addResource "org.openecomp.resource.nfv.CP"
+addResource "org.openecomp.resource.nfv.VDU"
+
+addResource "org.openecomp.resource.nfv.vdu.RoutingCategoryVDU"
+addResource "org.openecomp.resource.nfv.vdu.VCE_Brocade_VDU"
+addResource "org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector"
+addResource "org.openecomp.resource.nfv.cp.OAM_VLAN_Connector"
+addResource "org.openecomp.resource.nfv.cp.VPE_VLAN_Connector"
+
+#addResource "tosca.nodes.nfv.VL"
+#addResource "tosca.nodes.nfv.VNF"
+#addResource "org.openecomp.resource.nfv.VL"
+#addResource "org.openecomp.resource.nfv.VNF"
+#addResource "org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU"
+#addResource "org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF"
+#addResource "org.openecomp.resource.nfv.VDU.DHCPCategoryVDU"
+#addResource "org.openecomp.resource.nfv.VDU.DNSCategoryVDU"
+#addResource "org.openecomp.resource.nfv.VDU.FirewallCategoryVDU"
+#addResource "org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU"
+
+
+exit 0
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.json
new file mode 100644
index 0000000000..390fda0257
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector",
+ "description": "Represents a virtual or physical interface between the NFV and the LAN.",
+ "resourceIconPath": "port",
+ "category": "Network Layer 2-3/LAN Connectors",
+ "tags": [
+ "org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector", "LAN Connector"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.yaml
new file mode 100644
index 0000000000..03ecc8ea3e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.yaml
@@ -0,0 +1,48 @@
+##################################################################
+ ############### IPAG VLAN Connector CP Node #####################
+ ##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector:
+ derived_from: org.openecomp.resource.nfv.CP
+ properties:
+ ipag_network_name:
+ type: string
+
+ ipag_network_type:
+ type: string
+
+ ipag_network_uuid:
+ type: string
+
+ vlan_segment_ids:
+ type: list
+ entry_schema:
+ type: string
+ description: list of segmentation Ids.
+
+
+ #attributes:
+ capabilities:
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.IPAG_VLAN_Connector
+ description: This capability has been introduced to model this Connector as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ requirements:
+ - virtualBindingRequirement:
+ capability: org.openecomp.capabilities.nfv.vdu.VirtualBindable
+ #node: org.openecomp.asdc.nodes.vCE_Brocade_VDU
+ relationship: org.openecomp.relationships.nfv.VirtualBindsTo
+ occurrences: [1, 1] #default
+
+ - virtualLinkingrequirement:
+ capability: org.openecomp.capabilities.nfv.VirtualLinkable
+ #node: org.openecomp.asdc.nodes.nfv.VL
+ relationship: org.openecomp.relationships.nfv.VirtualLinksTo
+ occurrences: [1, 1] #default
+
+ #interfaces:
+ #standard:
+ #configure:
+ #implementation: configure.sh
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.zip
new file mode 100644
index 0000000000..31ddc8cbf6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector/org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.json
new file mode 100644
index 0000000000..b4f9a8f03e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.cp.OAM_VLAN_Connector.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.cp.OAM_VLAN_Connector",
+ "description": "Represents a virtual or physical interface between the NFV and the OAM.",
+ "resourceIconPath": "port",
+ "category": "Network Layer 2-3/LAN Connectors",
+ "tags": [
+ "org.openecomp.resource.nfv.cp.OAM_VLAN_Connector", "OAM Connector", "OAM"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.yaml
new file mode 100644
index 0000000000..6f7c86f676
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.yaml
@@ -0,0 +1,48 @@
+##################################################################
+ ############### OAM VLAN Connector CP Node #####################
+ ##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.cp.OAM_VLAN_Connector:
+ derived_from: org.openecomp.resource.nfv.CP
+ properties:
+ oam_network_name:
+ type: string
+
+ oam_network_type:
+ type: string
+
+ oam_network_uuid:
+ type: string
+
+ vlan_segment_ids:
+ type: list
+ entry_schema:
+ type: string
+ description: list of segmentation Ids.
+
+
+ #attributes:
+ capabilities:
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.OAM_VLAN_Connector
+ description: This capability has been introduced to model this Connector as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ requirements:
+ - virtualBindingRequirement:
+ capability: org.openecomp.capabilities.nfv.vdu.VirtualBindable
+ #node: org.openecomp.asdc.nodes.vCE_Brocade_VDU
+ relationship: org.openecomp.relationships.nfv.VirtualBindsTo
+ occurrences: [1, 1] #default
+
+ - virtualLinkingrequirement:
+ capability: org.openecomp.capabilities.nfv.VirtualLinkable
+ #node: org.openecomp.asdc.nodes.nfv.VL
+ relationship: org.openecomp.relationships.nfv.VirtualLinksTo
+ occurrences: [1, 1] #default
+
+ #interfaces:
+ #standard:
+ #configure:
+ #implementation: configure.sh
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.zip
new file mode 100644
index 0000000000..2076fb039b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector/org.openecomp.resource.nfv.CP.OAM_VLAN_Connector.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.json
new file mode 100644
index 0000000000..819335d0f3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.cp.VPE_VLAN_Connector.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.cp.VPE_VLAN_Connector",
+ "description": "Represents a virtual or physical interface between the NFV and the WAN.",
+ "resourceIconPath": "port",
+ "category": "Network Layer 2-3/WAN Connectors",
+ "tags": [
+ "org.openecomp.resource.nfv.cp.VPE_VLAN_Connector", "WAN Connector"
+ ]
+}
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.yaml
new file mode 100644
index 0000000000..f5f7380374
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.yaml
@@ -0,0 +1,48 @@
+##################################################################
+ ############### VPE VLAN Connector CP Node #####################
+ ##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.cp.VPE_VLAN_Connector:
+ derived_from: org.openecomp.resource.nfv.CP
+ properties:
+ vpe_network_name:
+ type: string
+
+ vpe_network_type:
+ type: string
+
+ vpe_network_uuid:
+ type: string
+
+ vlan_segment_ids:
+ type: list
+ entry_schema:
+ type: string
+ description: list of segmentation Ids.
+
+
+ #attributes:
+ capabilities:
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.VPE_VLAN_Connector
+ description: This capability has been introduced to model this Connector as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ requirements:
+ - virtualBindingRequirement:
+ capability: org.openecomp.capabilities.nfv.vdu.VirtualBindable
+ #node: org.openecomp.asdc.nodes.vCE_Brocade_VDU
+ relationship: org.openecomp.relationships.nfv.VirtualBindsTo
+ occurrences: [1, 1] #default
+
+ - virtualLinkingrequirement:
+ capability: org.openecomp.capabilities.nfv.VirtualLinkable
+ #node: org.openecomp.asdc.nodes.nfv.VL
+ relationship: org.openecomp.relationships.nfv.VirtualLinksTo
+ occurrences: [1, 1] #default
+
+ #interfaces:
+ #standard:
+ #configure:
+ #implementation: configure.sh
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.zip
new file mode 100644
index 0000000000..5808fc9338
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector/org.openecomp.resource.nfv.CP.VPE_VLAN_Connector.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.json
new file mode 100644
index 0000000000..76c3eabc26
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.CP.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.CP",
+ "description": "ATT CP element",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "org.openecomp.resource.nfv.CP"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.yaml
new file mode 100644
index 0000000000..ba6bda0c8b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.yaml
@@ -0,0 +1,7 @@
+##################################################################
+############################ ASDC CP Node #####################
+##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.CP:
+ derived_from: tosca.nodes.nfv.CP \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.zip
new file mode 100644
index 0000000000..0fd24994f9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.CP/org.openecomp.resource.nfv.CP.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.json
new file mode 100644
index 0000000000..95673b81ff
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.vdu.RoutingCategoryVDU.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.vdu.RoutingCategoryVDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Infrastructure",
+ "tags": [
+ "org.openecomp.resource.nfv.vdu.RoutingCategoryVDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.yaml
new file mode 100644
index 0000000000..5bbc64cb2c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.yaml
@@ -0,0 +1,7 @@
+##################################################################
+###################### ASDC Routing Category VDU #################
+##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.vdu.RoutingCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.zip
new file mode 100644
index 0000000000..3853a341b0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU/org.openecomp.resource.nfv.VDU.RoutingCategoryVDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.json
new file mode 100644
index 0000000000..91b692357b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.vdu.VCE_Brocade_VDU.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.vdu.VCE_Brocade_VDU",
+ "description": "Brocade Vyatta 5600 vRouter used as Virtual Customer Edge (vCE) delivers high throughput on general-purpose x86 server. it is based on open standards, open protocols and is fully compatible with legacy networks. It has advanced functionality to be able to deploy in a variety of network environments.",
+ "resourceIconPath": "brocade",
+ "category": "Network Layer 2-3/Router",
+ "tags": [
+ "org.openecomp.resource.nfv.vdu.VCE_Brocade_VDU", "vCE", "Brocade", "Gamma"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.yaml
new file mode 100644
index 0000000000..2ba732ab3b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.yaml
@@ -0,0 +1,156 @@
+##################################################################
+###################### vCE_Brocade_VDU Node TYPE ###############
+##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.vdu.VCE_Brocade_VDU:
+ derived_from: org.openecomp.resource.nfv.vdu.RoutingCategoryVDU
+ description: Definition of VCE Brocade VDU
+ properties:
+ vnf_category:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ vce_type:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ vendor:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ version:
+ type: string
+ description: input provided at RESOURCE INJECTION TIME in ASDC TOOL
+ image_id:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer -vCE image used to boot VM
+ flavor_id:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer - VM Size, SMALL, MEDIUM, LARGE
+ username:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer -login user Id of Router
+ password:
+ type: string
+ description: I Input provided at DESIGN TIME by Service Composer or Designer - password for login
+ domain_name:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer or (perhaps pre-defined for each Cloud Region) - login domain
+ time_zone:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer or (perhaps pre-defined for each Cloud Region) - time zone to set for vCE
+ login_session_timeout:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer or (perhaps pre-defined for each Cloud Region) - user login session timeout
+ ipag_vlan_bandwidth:
+ type: integer
+ description: Input provided at DESIGN TIME by Service Composer or Designer - bandwidth allocation of customer VLAN requested by customer
+ vpe_vlan_interface_qos_name:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer - QoS Policy name to be applied to this interface
+ ipag_vlan_interface_qos_name:
+ type: string
+ description: Input provided at DESIGN TIME by Service Composer or Designer - QoS Policy name to be applied to this interface
+ hostname:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - Host VM is created first and this value is input when the vCE image is later on installed by Cloud-PO
+ host_ip:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - Host VM IP
+ availability_zone:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - Cloud availability zone where VM is created
+ oam_loopback_vlan_ip_address:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - OAM Network is pre-created - already exists in Data Center
+ ipag_vlan_ip_address:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - IPAG VLAN Network is created by Cloud-PO before the vCE is created or installed
+ vpe_vlan_ip_address:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - VPE VLAN Network is created by Cloud-PO before the vCE is created / installed
+ vpe_vlan_id:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - VLAN Id is created first by Cloud-PO. It is required to apply VPE QoS Policy
+ ipag_vlan_id:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - VLAN Id is created first by Cloud-PO. It is required to apply IPAG QoS Policy
+ oam_vlan_interface_name:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - This interfce is created first by Cloud-PO. This is the interface to which QoS policy is applied
+ vpe_vlan_interface_name:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - This interfce is created first by Cloud-PO. This is the interface to which QoS policy is applied
+ ipag_vlan_interface_name:
+ type: string
+ description: Input provided at RUN TIME by Cloud-PO - This interfce is created first by Cloud-PO. This is the interface to which QoS policy is applied
+ #############ATTRIBUTES of vCE - These values are outputted by Cloud-PO when vCE is created / installed ##############
+ attributes:
+ vce_name:
+ type: string
+ description: Name assigned to vCE by Cloud-PO
+ oam_loopback_vlan_ipaddres:
+ type: string
+ description: IP Address assigned by Cloud-PO to OAM VLAN
+ ipag_vlan_ipaddres:
+ type: string
+ description: IP Address assigned by Cloud-PO to IPAG VLAN
+ vpe_vlan_ipaddres:
+ type: string
+ description: IP Address assigned by Cloud-PO to VPE VLAN
+ vnf_id:
+ type: string
+ description: unique id assigned to VNF by Cloud-PO - used for AAI metadata
+
+ #############CAPABILITIES of vCE - #########################
+ capabilities:
+ ce_routing_capability:
+ type: org.openecomp.capabilities.nfv.CERouting
+ description: This is the base capability of vCE
+ occurrences: [1, UNBOUNDED] # default
+
+ static_routing:
+ type: org.openecomp.capabilities.nfv.StaticRouting
+ description: This is the optional capability of the vCE
+ occurrences: [0, UNBOUNDED]
+
+ bgp_routing:
+ type: org.openecomp.capabilities.nfv.BGPRouting
+ description: This is the optional capability of the vCE
+ occurrences: [0, UNBOUNDED]
+
+ virtual_bindable:
+ type: org.openecomp.capabilities.nfv.vdu.VirtualBindable
+ description: This is the internal capability of the VDU to support Binding to Connection Points
+ occurrences: [1, UNBOUNDED]
+
+ dummy_capability:
+ type: org.openecomp.capabilities.nfv.VCE_Brocade_Routing
+ description: This capability has been introduced to model VCE VDU as a component of VCE VNF, because Node Template is not supported by ASDC Team
+
+ #############REQUIREMENTS of vdu - ###########################
+ requirements:
+ - oam_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.OAM_VLAN_Connector
+ node: org.openecomp.resource.nfv.cp.OAM_VLAN_Connector
+ relationship: tosca.relationships.ConnectsTo
+ occurrences: [1, 1]
+
+ - vpe_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.VPE_VLAN_Connector
+ node: org.openecomp.resource.nfv.cp.VPE_VLAN_Connector
+ relationship: tosca.relationships.ConnectsTo
+ occurrences: [1, 1]
+
+ - ipag_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.IPAG_VLAN_Connector
+ node: org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector
+ relationship: tosca.relationships.ConnectsTo
+ occurrences: [1, 1]
+
+ - hosted_on: # A vCE needs to be hosted in a VM
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1] #default
+
+
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.zip
new file mode 100644
index 0000000000..1d5b041a88
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU/org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.json
new file mode 100644
index 0000000000..9623090743
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VDU.yaml",
+ "contactId": "jh0003",
+ "name": "org.openecomp.resource.nfv.VDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "org.openecomp.resource.nfv.VDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.yaml
new file mode 100644
index 0000000000..f5e5aa9195
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.yaml
@@ -0,0 +1,5 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.nfv.VDU:
+ derived_from: tosca.nodes.nfv.VDU
+ description: A VDU is a compute component of VNF \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.zip
new file mode 100644
index 0000000000..569a8b2a44
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/org.openecomp.resource.nfv.VDU/org.openecomp.resource.nfv.VDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.json
new file mode 100644
index 0000000000..e17b474385
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VDU.DHCPCategoryVDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "org.openecomp.resource.nfv.VDU.DHCPCategoryVDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.yaml
new file mode 100644
index 0000000000..64f150e73e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.yaml
@@ -0,0 +1,10 @@
+##################################################################
+###################### DHCP VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.DHCPCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ dhcp:
+ type: org.openecomp.capabilities.nfv.VNF.DHCPCapability
+ description: This capability has been introduced
+ ###############Further details described in DHCP VNF TOSCA Model \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.zip
new file mode 100644
index 0000000000..d60b15698c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU/org.openecomp.resource.nfv.VDU.DHCPCategoryVDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.json
new file mode 100644
index 0000000000..8c419d17d3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VDU.DNSCategoryVDU.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VDU.DNSCategoryVDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "org.openecomp.resource.nfv.VDU.DNSCategoryVDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.yaml
new file mode 100644
index 0000000000..58aa9f3e81
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.yaml
@@ -0,0 +1,10 @@
+##################################################################
+###################### DNS VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.DNSCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ dns:
+ type: org.openecomp.capabilities.nfv.VNF.DNSCapability
+ description: This capability has been introduced
+ ###############Further details described in DNS VNF TOSCA Model \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.zip
new file mode 100644
index 0000000000..e927ad3d2a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.DNSCategoryVDU/org.openecomp.resource.nfv.VDU.DNSCategoryVDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.json
new file mode 100644
index 0000000000..6c0b033552
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VDU.FirewallCategoryVDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "org.openecomp.resource.nfv.VDU.FirewallCategoryVDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.yaml
new file mode 100644
index 0000000000..f22dfcb35e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.yaml
@@ -0,0 +1,10 @@
+##################################################################
+###################### Firewall VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.FirewallCategoryVDU:
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ firewall:
+ type: org.openecomp.capabilities.nfv.VNF.FirewallCapability
+ description: This capability has been introduced
+ ###############Further details described in Firewall VNF TOSCA Model \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.zip
new file mode 100644
index 0000000000..548b64ed17
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU/org.openecomp.resource.nfv.VDU.FirewallCategoryVDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.json
new file mode 100644
index 0000000000..be8f6fd133
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.yaml
new file mode 100644
index 0000000000..133ccc33b5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.yaml
@@ -0,0 +1,10 @@
+##################################################################
+###################### ASDC NAT VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU: #paharoni - renamed from org.openecomp.resource.nfv.VDU.NetworkAddressTranslationCategoryVDU. Resource name can be up to 50 chars in ASDC
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ nat:
+ type: org.openecomp.capabilities.nfv.VNF.NetworkAddressTranslationCapability
+ description: This capability has been introduced
+ ###############Further details described in NAT VNF TOSCA Model \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.zip
new file mode 100644
index 0000000000..93ef2905f6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU/org.openecomp.resource.nfv.VDU.NetworkAddrCategoryVDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.json
new file mode 100644
index 0000000000..26a2dd9a08
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.yaml
new file mode 100644
index 0000000000..04aad2f848
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.yaml
@@ -0,0 +1,10 @@
+##################################################################
+###################### PAT VDU Node TYPE ###############
+##################################################################
+ org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU: #paharoni - renamed from org.openecomp.resource.nfv.VDU.PortAddressTranslationCategoryVDU. Resource name can be up to 50 chars in ASDC
+ derived_from: org.openecomp.resource.nfv.VDU
+ capabilities: #paharoni - added to connect to VCE_Brocade_VDU
+ pat:
+ type: org.openecomp.capabilities.nfv.VNF.PortAddressTranslationCapability
+ description: This capability has been introduced
+ ###############Further details described in PAT VNF TOSCA Model \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.zip
new file mode 100644
index 0000000000..0bad67331b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU/org.openecomp.resource.nfv.VDU.PortAddrCategoryVDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.json
new file mode 100644
index 0000000000..7ea24e09f5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VL.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VL",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "org.openecomp.resource.nfv.VL"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.yaml
new file mode 100644
index 0000000000..0709072040
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.yaml
@@ -0,0 +1,7 @@
+##################################################################
+###################### ASDC VL Node TYPE ######################
+##################################################################
+ org.openecomp.resource.nfv.VL:
+ derived_from: tosca.nodes.nfv.VL
+
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.zip
new file mode 100644
index 0000000000..b6d19de95b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VL/org.openecomp.resource.nfv.VL.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.json
new file mode 100644
index 0000000000..6e68301bcb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Network Elements",
+ "tags": [
+ "org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.yaml
new file mode 100644
index 0000000000..bbd0fe0945
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.yaml
@@ -0,0 +1,32 @@
+########################################################################################################
+###################### vCE_Brocade_VNF Node TYPE #####################################################
+########### IN THE ABSENCE OF NODE TEMPLATE, the vCE_Brocade_VNF Node Type is modeled as REQUIRING: #####
+########### 1. vce_Brocade_VDU Node
+########### 2. oam_vlan_connector Node
+########### 3. ipag_vlan_connector Node
+########### 4. vpe_vlan_connector Node
+########################################################################################################
+
+ org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF:
+ derived_from: org.openecomp.resource.nfv.VNF
+ requirements:
+ - vce_brocade_vdu:
+ capability: org.openecomp.capabilities.nfv.VCE_Brocade_Routing
+ node: org.openecomp.resource.nfv.VDU.VCE_Brocade_VDU
+ occurrences: [1, 1]
+
+ - oam_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.OAM_VLAN_Connector
+ node: org.openecomp.resource.nfv.CP.OAM_VLAN_Connector
+ occurrences: [1, 1]
+
+ - vpe_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.VPE_VLAN_Connector
+ node: org.openecomp.resource.nfv.CP.VPE_VLAN_Connector
+ occurrences: [1, 1]
+
+ - ipag_vlan_connector:
+ capability: org.openecomp.capabilities.nfv.IPAG_VLAN_Connector
+ node: org.openecomp.resource.nfv.CP.IPAG_VLAN_Connector
+ occurrences: [1, 1]
+
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.zip
new file mode 100644
index 0000000000..158f917ee8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF/org.openecomp.resource.nfv.VNF.VCE_Brocade_VNF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.json
new file mode 100644
index 0000000000..18ce8bbca5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "org.openecomp.resource.nfv.VNF.yaml",
+ "contactId": "jh0003",
+ "resourceName": "org.openecomp.resource.nfv.VNF",
+ "description": "Node Type that represents a Module",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "org.openecomp.resource.nfv.VNF"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.yaml
new file mode 100644
index 0000000000..6872cfc7fb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.yaml
@@ -0,0 +1,5 @@
+##################################################################
+###################### ASDC VNF Node TYPE ######################
+##################################################################
+ org.openecomp.resource.nfv.VNF:
+ derived_from: tosca.nodes.nfv.VNF
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.zip
new file mode 100644
index 0000000000..2b8e33d71a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/org.openecomp.resource.nfv.VNF/org.openecomp.resource.nfv.VNF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.json
new file mode 100644
index 0000000000..e5bcfb204e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "tosca.nodes.nfv.VL.yaml",
+ "contactId": "jh0003",
+ "resourceName": "tosca.nodes.nfv.VL",
+ "description": "TOSCA VL base element",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "tosca.nodes.nfv.VL"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.yaml
new file mode 100644
index 0000000000..2fbcc9c46d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.yaml
@@ -0,0 +1,16 @@
+##################################################################
+ ############## TOSCA VL Node Type #######################
+ ##################################################################
+ tosca.nodes.nfv.VL:
+ derived_from: tosca.nodes.Root
+ properties:
+ vendor:
+ type: string
+ required: false
+ description: name of the vendor who provides this VL
+
+ capabilities:
+ virtuallinkable:
+ type: tosca.capabilities.nfv.VirtualLinkable
+ occurrences: [1, UNBOUNDED] # this is default
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.zip
new file mode 100644
index 0000000000..fc7ba8d27b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VL/tosca.nodes.nfv.VL.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.json
new file mode 100644
index 0000000000..bd6047734c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "tosca.nodes.nfv.VNF.yaml",
+ "contactId": "jh0003",
+ "resourceName": "tosca.nodes.nfv.VNF",
+ "description": "TOSCA VNF base element",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "tosca.nodes.nfv.VNF"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.yaml
new file mode 100644
index 0000000000..5cf9453884
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.yaml
@@ -0,0 +1,30 @@
+##################################################################
+################### TOSCA VNF Node ##########################
+##################################################################
+tosca.nodes.nfv.VNF:
+ derived_from: tosca.nodes.Root
+ properties:
+ vnf_category:
+ type: string
+ description: Router, FW, LB, DNS are example categories
+ id:
+ type: string
+ description: ID of this VNF
+ vendor:
+ type: string
+ description: name of the vendor who provides this VNF
+ version:
+ type: version
+ description: version of the software for this VNF
+ #device_config:
+ # type: map
+ #entry_schema:
+ # type: string
+ #default:
+ # p1: 1
+ # p2: 2
+ #p3: 3
+ #requirements:
+ # - virtualLink:
+ #capability: tosca.capabilities.nfv.VirtualLinkable
+ #occurrences: [1, UNBOUNDED] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.zip
new file mode 100644
index 0000000000..3c10c5f388
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/resourcesToFix/tosca.nodes.nfv.VNF/tosca.nodes.nfv.VNF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.json
new file mode 100644
index 0000000000..bf82a9a390
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "tosca.nodes.nfv.CP.yaml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.nfv.CP",
+ "description": "TOSCA CP base element",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "tosca.nodes.nfv.CP"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.yaml
new file mode 100644
index 0000000000..271a4bf026
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.yaml
@@ -0,0 +1,24 @@
+ ##################################################################
+ ####################### TOSCA CP Node ##########################
+ ##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.nfv.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualbinding:
+ capability: tosca.capabilities.nfv.vdu.VirtualBindable
+ occurrences: [1, 1] #this is default
+
+ - virtuallink:
+ capability: tosca.capabilities.nfv.VirtualLinkable
+ occurrences: [1, 1] #this is default
+
+ attributes:
+ ip_address:
+ type: string
+ # required: false \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.zip
new file mode 100644
index 0000000000..2d7281a915
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.CP/tosca.nodes.nfv.CP.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.json b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.json
new file mode 100644
index 0000000000..2ab0340e59
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.json
@@ -0,0 +1,11 @@
+{
+ "payloadName": "tosca.nodes.nfv.VDU.yaml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.nfv.VDU",
+ "description": "TOSCA VDU base element",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Abstract",
+ "tags": [
+ "tosca.nodes.nfv.VDU"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.yaml
new file mode 100644
index 0000000000..d1cba804e6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.yaml
@@ -0,0 +1,28 @@
+ ##################################################################
+ ###################### TOSCA VDU Node ##########################
+ ##################################################################
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.nfv.VDU:
+ derived_from: tosca.nodes.SoftwareComponent
+ #properties:
+
+ capabilities:
+ high_availability:
+ type: tosca.capabilities.nfv.HA
+ occurrences: [1, UNBOUNDED] # this is default
+
+ virtual_binding:
+ type: tosca.capabilities.nfv.vdu.VirtualBindable
+ occurrences: [1, UNBOUNDED] # this is default
+
+ requirements:
+ - high_availability:
+ capability: tosca.capabilities.nfv.HA
+ occurrences: [0,1]
+
+ - host: ##Is hosted in a VM
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1] #this is default \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.zip b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.zip
new file mode 100644
index 0000000000..3ba4eb7d80
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/tosca.nodes.nfv.VDU/tosca.nodes.nfv.VDU.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/vce.brocade.capabilities.yaml b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/vce.brocade.capabilities.yaml
new file mode 100644
index 0000000000..af55754cf3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource-VCE-Brocade/vce.brocade.capabilities.yaml
@@ -0,0 +1,191 @@
+#####################################################################################
+ ###################### TOSCA Capability: VirtualBindable ##########################
+ #####################################################################################
+ tosca.capabilities.nfv.vdu.VirtualBindable:
+ derived_from: tosca.capabilities.Root
+ valid_source_types: [tosca.nodes.nfv.VDU]
+
+ #####################################################################################
+ ##################### TOSCA Capability: VirtualLinkable ##########################
+ #####################################################################################
+ tosca.capabilities.nfv.VirtualLinkable:
+ derived_from: tosca.capabilities.Root ##paharoni - changed from tosca.capabilities.nfv.VirtualLinkable - cannot derive from itself
+ valid_source_types: [org.openecomp.resource.nfv.VL]
+
+ #####################################################################################
+ ############################ Capability: HighAvailability ##########################
+ #####################################################################################
+ tosca.capabilities.nfv.HA:
+ derived_from: tosca.capabilities.Root
+ valid_source_types: [tosca.nodes.nfv.VDU]
+
+ #####################################################################################
+ ############################ ASDC Capability: VirtualBindable #####################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vdu.VirtualBindable:
+ derived_from: tosca.capabilities.nfv.vdu.VirtualBindable
+ valid_source_types: [org.openecomp.resource.nfv.VDU]
+
+
+
+ #####################################################################################
+ ############################ ASDC Capability: VirtualLinkable #####################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VirtualLinkable:
+ derived_from: tosca.capabilities.Root
+ valid_source_types: [tosca.nodes.nfv.VL]
+
+
+
+ #####################################################################################
+ ######################Capability: Generic asdc capability ##########################
+ #####################################################################################
+ org.openecomp.capabilities.Root:
+ derived_from: tosca.capabilities.Root
+
+ #####################################################################################
+ ############################ Capability: GENERIC ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.RoutingCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ routing_type:
+ type: string
+ description: some basic routing descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.vdu.RoutingCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: CE ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.CERouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: PE ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.PERouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: P ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.PRouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: BGP ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.BGPRouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: STATIC ROUTING ##########################
+ #####################################################################################
+ org.openecomp.capabilities.nfv.StaticRouting:
+ derived_from: org.openecomp.capabilities.nfv.RoutingCapability
+
+ #####################################################################################
+ ############################ Capability: Network Address Translation ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vnf.NetworkAddressTranslationCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ nat_type:
+ type: string
+ description: some basic NAT descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.vdu.NetworkAddressTranslationCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: Port Address Translation ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vnf.PortAddressTranslationCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ pat_type:
+ type: string
+ description: some basic PAT descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.vdu.PortAddressTranslationCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: FIREWALL ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vnf.FirewallCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ firewall_type:
+ type: string
+ description: some basic Firewall Capability descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.vdu.FirewallCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: DHCP ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vnf.DHCPCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ dhcp_type:
+ type: string
+ description: some basic DHCP Capability descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.vdu.DHCPCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: DNS ###############
+ #####################################################################################
+ org.openecomp.capabilities.nfv.vnf.DNSCapability:
+ derived_from: org.openecomp.capabilities.Root
+ properties:
+ dns_type:
+ type: string
+ description: some basic DNS Capability descriptor, if any???
+ constraints:
+ - valid_values: [TBD1, TBDn]
+ #attributes: ???
+ valid_source_types: [org.openecomp.resource.nfv.vdu.DNSCategoryVDU]
+
+ #####################################################################################
+ ############################ Capability: vCE Brocade ROUTING ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC BROCADE VDU ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VCE_Brocade_Routing:
+ derived_from: org.openecomp.capabilities.nfv.CERouting
+ valid_source_types: [org.openecomp.resource.nfv.vdu.VCE_Brocade_VDU]
+
+ #####################################################################################
+ ############################ Capability: OAM VLAn Connector ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC Connector ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.OAM_VLAN_Connector:
+ derived_from: org.openecomp.capabilities.Root
+ valid_source_types: [org.openecomp.resource.nfv.cp.OAM_VLAN_Connector]
+
+ #####################################################################################
+ ############################ Capability: IPAG VLAn Connector ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC Connector ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.IPAG_VLAN_Connector:
+ derived_from: org.openecomp.capabilities.Root
+ valid_source_types: [org.openecomp.resource.nfv.cp.IPAG_VLAN_Connector]
+
+ #####################################################################################
+ ############################ Capability: VPE VLAn Connector ################# DUMMY
+ ############## THIS IS CAPABILITY IS ONLY OFFERED BY SPECIFIC Connector ###########
+ #####################################################################################
+ org.openecomp.capabilities.nfv.VPE_VLAN_Connector:
+ derived_from: org.openecomp.capabilities.Root
+ valid_source_types: [org.openecomp.resource.nfv.cp.VPE_VLAN_Connector]
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/DBMS/DBMS.json b/asdc-tests/src/test/resources/CI/importResource/DBMS/DBMS.json
new file mode 100644
index 0000000000..790a30ab42
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/DBMS/DBMS.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-DBMS.yml",
+ "contactId": "jh0003",
+ "name": "DBMS",
+ "description": "Represents a typical relational, SQL Database Management System software component or service.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "DBMS"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.yml b/asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.yml
new file mode 100644
index 0000000000..28919d38e4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.DBMS:
+ derived_from: tosca.nodes.SoftwareComponent
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [ tosca.nodes.Database ]
diff --git a/asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.zip b/asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.zip
new file mode 100644
index 0000000000..84bddbce47
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/DBMS/normative-types-new-DBMS.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/blockStorage/blockStorage.json b/asdc-tests/src/test/resources/CI/importResource/blockStorage/blockStorage.json
new file mode 100644
index 0000000000..72ef5c009b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/blockStorage/blockStorage.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-blockStorage.yml",
+ "contactId": "jh0003",
+ "name": "BlockStorage",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "objectStorage",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "BlockStorage"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.yml b/asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.yml
new file mode 100644
index 0000000000..a82965215f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.BlockStorage:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.zip b/asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.zip
new file mode 100644
index 0000000000..f690e9d8ea
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/blockStorage/normative-types-new-blockStorage.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/capabilityTypes.yml b/asdc-tests/src/test/resources/CI/importResource/capabilityTypes.yml
new file mode 100644
index 0000000000..58d661b17e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/capabilityTypes.yml
@@ -0,0 +1,148 @@
+tosca.capabilities.Root:
+ description: The TOSCA root Capability Type all other TOSCA base Capability Types derive from
+tosca.capabilities.Attachment:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Node:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ cpu_frequency:
+ type: scalar-unit.frequency
+ required: false
+ constraints:
+ - greater_or_equal: 0.1 GHz
+ disk_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+ mem_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: PortDef
+ required: false
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ port_name:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ default: PRIVATE
+ initiator:
+ type: string
+ default: source
+ constraints:
+ - valid_values: [ source, target, peer ]
+ ports:
+ type: map
+ required: false
+ constraints:
+ - min_length: 1
+ entry_schema:
+ type: PortSpec
+ attributes:
+ ip_address:
+ type: string
+tosca.capabilities.DatabaseEndpoint:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.Endpoint.Public:
+ derived_from: tosca.capabilities.Endpoint
+ properties:
+ # Change the default network_name to use the first public network found
+ network_name: PUBLIC
+ floating:
+ description: >
+ indicates that the public address should be allocated from a pool of floating IPs that are associated with the network.
+ type: boolean
+ default: false
+ status: experimental
+ dns_name:
+ description: The optional name to register with DNS
+ type: string
+ required: false
+ status: experimental
+tosca.capabilities.Endpoint.Admin:
+ derived_from: tosca.capabilities.Endpoint
+ # Change Endpoint secure indicator to true from its default of false
+ properties:
+ secure: true
+tosca.capabilities.Endpoint.Database:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.OperatingSystem:
+ derived_from: tosca.capabilities.Root
+ properties:
+ architecture:
+ type: string
+ required: false
+ type:
+ type: string
+ required: false
+ distribution:
+ type: string
+ required: false
+ version:
+ type: version
+ required: false
+tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_instances:
+ type: integer
+ default: 1
+ max_instances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+tosca.capabilities.network.Bindable:
+ derived_from: tosca.capabilities.Node
+
+
+tosca.capabilities.Container.Docker:
+ derived_from: tosca.capabilities.Container
+ properties:
+ version:
+ type: list
+ required: false
+ entry_schema: version
+ publish_all:
+ type: boolean
+ default: false
+ required: false
+ publish_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ expose_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ volumes:
+ type: list
+ entry_schema: string
+ required: false
+tosca.capabilities.network.Linkable:
+ derived_from: tosca.capabilities.Root
+
+
diff --git a/asdc-tests/src/test/resources/CI/importResource/capabilityTypes.zip b/asdc-tests/src/test/resources/CI/importResource/capabilityTypes.zip
new file mode 100644
index 0000000000..50b66e61be
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/capabilityTypes.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/capabilityTypesCi.zip b/asdc-tests/src/test/resources/CI/importResource/capabilityTypesCi.zip
new file mode 100644
index 0000000000..550cd756f4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/capabilityTypesCi.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/categoryTypes.yml b/asdc-tests/src/test/resources/CI/importResource/categoryTypes.yml
new file mode 100644
index 0000000000..d88a326859
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/categoryTypes.yml
@@ -0,0 +1,89 @@
+services:
+ Mobility:
+ name: "Mobility"
+ icons: ['mobility']
+ Network_L1_3:
+ name: "Network L1-3"
+ icons: ['network_l_1-3']
+ Network_L4:
+ name: "Network L4+"
+ icons: ['network_l_4']
+ VoIP_Call_Control:
+ name: "VoIP Call Control"
+ icons: ['call_controll']
+resources:
+ NetworkLayer23:
+ name: "Network L2-3"
+ subcategories:
+ Router:
+ name: "Router"
+ icons: ['router','vRouter']
+ Gateway:
+ name: "Gateway"
+ icons: ['gateway']
+ WAN_Connectors:
+ name: "WAN Connectors"
+ icons: ['network','connector','port']
+ LAN_Connectors:
+ name: "LAN Connectors"
+ icons: ['network','connector','port']
+ Infrastructure:
+ name: "Infrastructure"
+ icons: ['ucpe']
+ NetworkLayer4:
+ name: "Network L4+"
+ subcategories:
+ Common_Network_Resources:
+ name: "Common Network Resources"
+ icons: ['network']
+ ApplicationLayer4:
+ name: "Application L4+"
+ subcategories:
+ Border_Element:
+ name: "Border Element"
+ icons: ['borderElement']
+ Application_Server:
+ name: "Application Server"
+ icons: ['applicationServer']
+ Web_Server:
+ name: "Web Server"
+ icons: ['applicationServer']
+ Call_Control:
+ name: "Call Control"
+ icons: ['call_controll']
+ Media_Servers:
+ name: "Media Servers"
+ icons: ['applicationServer']
+ Load_Balancer:
+ name: "Load Balancer"
+ icons: ['loadBalancer']
+ Database:
+ name: "Database"
+ icons: ['database']
+ Firewall:
+ name: "Firewall"
+ icons: ['firewall']
+ Generic:
+ name: "Generic"
+ subcategories:
+ Infrastructure:
+ name: "Infrastructure"
+ icons: ['connector']
+ Abstract:
+ name: "Abstract"
+ icons: ['objectStorage', 'compute']
+ Network_Elements:
+ name: "Network Elements"
+ icons: ['network', 'connector']
+ Database:
+ name: "Database"
+ icons: ['database']
+ NetworkConnectivity:
+ name: "Network Connectivity"
+ subcategories:
+ ConnectionPoints:
+ name: "Connection Points"
+ icons: ['cp']
+ VirtualLinks:
+ name: "Virtual Links"
+ icons: ['vl'] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/categoryTypes.zip b/asdc-tests/src/test/resources/CI/importResource/categoryTypes.zip
new file mode 100644
index 0000000000..29984b8a06
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/categoryTypes.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/compute/compute.json b/asdc-tests/src/test/resources/CI/importResource/compute/compute.json
new file mode 100644
index 0000000000..48775d0f14
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/compute/compute.json
@@ -0,0 +1,24 @@
+{
+ "payloadName": "normative-types-new-compute.yml",
+ "contactId": "jh0003",
+ "name": "Compute",
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing information are specified the cloud provider default
+ machine will be used. It is strongly recommended to specify the required CPUs and memory
+ at least.",
+ "resourceIconPath": "compute",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Compute"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.yml b/asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.yml
new file mode 100644
index 0000000000..00b07fb908
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Compute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.zip b/asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.zip
new file mode 100644
index 0000000000..f7a7c31bf0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/compute/normative-types-new-compute.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/containerApplication/containerApplication.json b/asdc-tests/src/test/resources/CI/importResource/containerApplication/containerApplication.json
new file mode 100644
index 0000000000..1713e56cb4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/containerApplication/containerApplication.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-containerApplication.yml",
+ "contactId": "jh0003",
+ "name": "Application",
+ "description": "Represents an application that requires Container-level virtualization technology.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Application"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.yml b/asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.yml
new file mode 100644
index 0000000000..8813f26e04
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Container.Application:
+ derived_from: tosca.nodes.Root
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Container
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.zip b/asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.zip
new file mode 100644
index 0000000000..1da3bc1707
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/containerApplication/normative-types-new-containerApplication.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/containerRuntime/containerRuntime.json b/asdc-tests/src/test/resources/CI/importResource/containerRuntime/containerRuntime.json
new file mode 100644
index 0000000000..233bf39bce
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/containerRuntime/containerRuntime.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-containerRuntime.yml",
+ "contactId": "jh0003",
+ "name": "Runtime",
+ "description": "Represents operating system-level virtualization technology used to run multiple application services on a single Compute host.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Runtime"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.yml b/asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.yml
new file mode 100644
index 0000000000..86a10a0185
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Container.Runtime:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ scalable:
+ type: tosca.capabilities.Scalable
diff --git a/asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.zip b/asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.zip
new file mode 100644
index 0000000000..5140472147
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/containerRuntime/normative-types-new-containerRuntime.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/database/database.json b/asdc-tests/src/test/resources/CI/importResource/database/database.json
new file mode 100644
index 0000000000..0f09e278c0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/database/database.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-database.yml",
+ "contactId": "jh0003",
+ "name": "Database",
+ "description": "Represents a logical database that can be managed and hosted by a DBMS node.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Database"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Database"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.yml b/asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.yml
new file mode 100644
index 0000000000..5166150c73
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.yml
@@ -0,0 +1,27 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Database:
+ derived_from: tosca.nodes.Root
+ properties:
+ name:
+ type: string
+ description: the logical name of the database
+ port:
+ type: integer
+ description: the port the underlying database service will listen to for data
+ user:
+ type: string
+ description: the optional user account name for DB administration
+ required: false
+ password:
+ type: string
+ description: the optional password for the DB user account
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.DBMS
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ database_endpoint:
+ type: tosca.capabilities.Endpoint.Database
diff --git a/asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.zip b/asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.zip
new file mode 100644
index 0000000000..9e8b178554
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/database/normative-types-new-database.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/deleteNormative.sh b/asdc-tests/src/test/resources/CI/importResource/deleteNormative.sh
new file mode 100644
index 0000000000..fb70f71418
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/deleteNormative.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort>"
+}
+
+function deleteResource() {
+
+ ELEMENT_NAME=$1
+ echo -e "############### Removing Element ${ELEMENT_NAME} Start ######################"
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -X "DELETE" -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/resources/res_${ELEMENT_NAME}".1.0")
+ if [ ${http_code} -eq 204 ]; then
+ echo -e "\n############### Removing Element ${ELEMENT_NAME} status code:${http_code} End #######\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n############### Failed to remove Element ${ELEMENT_NAME} status code:${http_code} End #######\n\n\n"
+ exit 1
+ elif [ ${http_code} -eq 404 ]; then
+ echo -e "\n############### Element ${ELEMENT_NAME} not found status code:${http_code} End #######\n\n\n"
+ else
+ echo -e "\n############### Failed to remove Element ${ELEMENT_NAME} status code:${http_code} End #######\n\n\n"
+ exit 1
+ fi
+}
+if [ $# -lt 2 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+
+deleteResource "tosca.nodes.Root"
+deleteResource "tosca.nodes.Compute"
+deleteResource "tosca.nodes.SoftwareComponent"
+deleteResource "tosca.nodes.WebServer"
+deleteResource "tosca.nodes.WebApplication"
+deleteResource "tosca.nodes.DBMS"
+deleteResource "tosca.nodes.Database"
+deleteResource "tosca.nodes.ObjectStorage"
+deleteResource "tosca.nodes.BlockStorage"
+deleteResource "tosca.nodes.Container.Runtime"
+deleteResource "tosca.nodes.Container.Application"
+deleteResource "tosca.nodes.LoadBalancer"
+deleteResource "tosca.nodes.network.Port"
+deleteResource "tosca.nodes.network.Network"
+
+exit 0
+
diff --git a/asdc-tests/src/test/resources/CI/importResource/importNormative.sh b/asdc-tests/src/test/resources/CI/importResource/importNormative.sh
new file mode 100644
index 0000000000..7c420ceb43
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/importNormative.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <hostIp> <hostPort> <userId>"
+}
+
+function addResource() {
+
+ ELEMENT_NAME=$1
+ echo -e "###################### Adding Element ${ELEMENT_NAME} Start ######################"
+ CURRENT_ZIP_FILE=./${ELEMENT_NAME}/normative-types-new-${ELEMENT_NAME}.zip
+ CURRENT_JSON_FILE=./${ELEMENT_NAME}/${ELEMENT_NAME}.json
+ sed -i 's/"userId": ".*",/"userId": "'${ATT_UID}'",/' ${CURRENT_JSON_FILE}
+ JSON_CONTENT=`paste -s ${CURRENT_JSON_FILE}`
+ http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F resourceMetadata="${JSON_CONTENT}" -F resourceZip=@${CURRENT_ZIP_FILE} -H USER_ID:${ATT_UID} ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/upload/multipart)
+ if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding Element ${ELEMENT_NAME} End ########################\n\n\n"
+ elif [ ${http_code} -eq 409 ]; then
+ echo -e "\n###################### Already exists Element ${ELEMENT_NAME} status code:${http_code} End ########################\n\n\n"
+ elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add Element ${ELEMENT_NAME} status code:${http_code} End ########################\n\n\n"
+ exit 1
+ fi
+}
+if [ $# -lt 3 ]
+then
+ usage
+ exit 2
+fi
+
+HOST_IP=$1
+HOST_PORT=$2
+ATT_UID=$3
+
+#Add The CapabilityTypes
+http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F capabilityTypeZip=@capabilityTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/capability)
+if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding The CapabilityTypes status code:${http_code} End ########################\n\n\n"
+elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+else
+ echo -e "\n###################### Failed to add CapabilityTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+fi
+#Add The InterfaceLifecycleTypes
+http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F interfaceLifecycleTypeZip=@interfaceLifecycleTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/interfaceLifecycle)
+if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding The InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+elif [ ${http_code} -eq 409 ]; then
+ echo -e "\n###################### Already exists InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+else
+ echo -e "\n###################### Failed to add InterfaceLifecycleTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+fi
+#Add The CategoryTypes
+http_code=$(curl -s -o /dev/null -w "%{http_code}" -v -F categoriesZip=@categoryTypes.zip -H "USER_ID: jh0003" ${HOST_IP}:${HOST_PORT}/sdc2/rest/v1/catalog/uploadType/categories)
+if [ ${http_code} -eq 201 ]; then
+ echo -e "\n###################### Adding The CategoryTypes status code:${http_code} End ########################\n\n\n"
+elif [ ${http_code} -eq 409 ]; then
+ echo -e "\n###################### Already exists CategoryTypes status code:${http_code} End ########################\n\n\n"
+elif [ ${http_code} -eq 500 ]; then
+ echo -e "\n###################### Failed to add CategoryTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+else
+ echo -e "\n###################### Failed to add CategoryTypes status code:${http_code} End ########################\n\n\n"
+ exit 1
+fi
+
+addResource "root"
+addResource "compute"
+addResource "softwareComponent"
+addResource "webServer"
+addResource "webApplication"
+addResource "DBMS"
+addResource "database"
+addResource "objectStorage"
+addResource "blockStorage"
+addResource "containerRuntime"
+addResource "containerApplication"
+addResource "loadBalancer"
+addResource "port"
+addResource "network"
+
+exit 0
+
diff --git a/asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.yml b/asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.yml
new file mode 100644
index 0000000000..1b67118934
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.yml
@@ -0,0 +1,11 @@
+tosca.interfaces.node.lifecycle.Standard:
+ create:
+ description: Standard lifecycle create operation.
+ configure:
+ description: Standard lifecycle configure operation.
+ start:
+ description: Standard lifecycle start operation.
+ stop:
+ description: Standard lifecycle stop operation.
+ delete:
+ description: Standard lifecycle delete operation. \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.zip b/asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.zip
new file mode 100644
index 0000000000..9bcf93ab7d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/interfaceLifecycleTypes.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/loadBalancer/loadBalancer.json b/asdc-tests/src/test/resources/CI/importResource/loadBalancer/loadBalancer.json
new file mode 100644
index 0000000000..fbddc18c07
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/loadBalancer/loadBalancer.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-loadBalancer.yml",
+ "contactId": "jh0003",
+ "name": "LoadBalancer",
+ "description": "Represents logical function that be used in conjunction with a Floating Address to distribute an application’s traffic (load) across a number of instances of the application (e.g., for a clustered or scaled application).",
+ "resourceIconPath": "loadBalancer",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "LoadBalancer"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.yml b/asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.yml
new file mode 100644
index 0000000000..86857656ad
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.LoadBalancer:
+ derived_from: tosca.nodes.Root
+ properties:
+ # TBD
+ algorithm :
+ type: string
+ required: false
+ status: experimental
+ capabilities :
+ client:
+ type: tosca.capabilities.Endpoint.Public
+ description: the Floating (IP) client’s on the public network can connect to
+ requirements:
+ - application:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.RoutesTo
diff --git a/asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.zip b/asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.zip
new file mode 100644
index 0000000000..fefed6f0f4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/loadBalancer/normative-types-new-loadBalancer.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/network/network.json b/asdc-tests/src/test/resources/CI/importResource/network/network.json
new file mode 100644
index 0000000000..cf1eb03ff0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/network/network.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "normative-types-new-network.yml",
+ "contactId": "jh0003",
+ "name": "Network",
+ "description": "Represents a simple , logical network service.",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Network"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.yml b/asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.yml
new file mode 100644
index 0000000000..e1f9d3db70
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.yml
@@ -0,0 +1,41 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.Network:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_version:
+ type: integer
+ required: false
+ default: 4
+ constraints:
+ - valid_values: [ 4, 6 ]
+ cidr:
+ type: string
+ required: false
+ start_ip:
+ type: string
+ required: false
+ end_ip:
+ type: string
+ required: false
+ gateway_ip:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ network_id:
+ type: string
+ required: false
+ segmentation_id:
+ type: string
+ required: false
+ network_type:
+ type: string
+ required: false
+ physical_network:
+ type: string
+ required: false
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.zip b/asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.zip
new file mode 100644
index 0000000000..b10daebb2b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/network/normative-types-new-network.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.yml b/asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.yml
new file mode 100644
index 0000000000..2d1540b27b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.yml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.network.Port:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_address:
+ type: string
+ required: false
+ order:
+ type: integer
+ required: true
+ default: 0
+ constraints:
+ - greater_or_equal: 0
+ is_default:
+ type: boolean
+ required: false
+ default: false
+ ip_range_start:
+ type: string
+ required: false
+ ip_range_end:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - binding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo
diff --git a/asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.zip b/asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.zip
new file mode 100644
index 0000000000..f6903ea8ee
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/normative-port/normative-types-new-port.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/normative-port/port.json b/asdc-tests/src/test/resources/CI/importResource/normative-port/port.json
new file mode 100644
index 0000000000..7b62d7ff0a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/normative-port/port.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-port.yml",
+ "contactId": "jh0003",
+ "name": "Port",
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "resourceIconPath": "port",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Port"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/notmative-network/network.json b/asdc-tests/src/test/resources/CI/importResource/notmative-network/network.json
new file mode 100644
index 0000000000..636499fba4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/notmative-network/network.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-network.yml",
+ "contactId": "jh0003",
+ "name": "Network",
+ "description": "Represents a simple , logical network service.",
+ "resourceIconPath": "network",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Network"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.yml b/asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.yml
new file mode 100644
index 0000000000..e92e87716f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.yml
@@ -0,0 +1,41 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.network.Network:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_version:
+ type: integer
+ required: false
+ default: 4
+ constraints:
+ - valid_values: [ 4, 6 ]
+ cidr:
+ type: string
+ required: false
+ start_ip:
+ type: string
+ required: false
+ end_ip:
+ type: string
+ required: false
+ gateway_ip:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ network_id:
+ type: string
+ required: false
+ segmentation_id:
+ type: string
+ required: false
+ network_type:
+ type: string
+ required: false
+ physical_network:
+ type: string
+ required: false
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.zip b/asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.zip
new file mode 100644
index 0000000000..79c5fb2573
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/notmative-network/normative-types-new-network.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.yml b/asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.yml
new file mode 100644
index 0000000000..9c338c3400
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.ObjectStorage:
+ derived_from: tosca.nodes.Root
+ properties:
+ name:
+ type: string
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 0 GB
+ maxsize:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 0 GB
+ capabilities:
+ storage_endpoint:
+ type: tosca.capabilities.Endpoint
diff --git a/asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.zip b/asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.zip
new file mode 100644
index 0000000000..b9046c1a66
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/objectStorage/normative-types-new-objectStorage.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/objectStorage/objectStorage.json b/asdc-tests/src/test/resources/CI/importResource/objectStorage/objectStorage.json
new file mode 100644
index 0000000000..247b468952
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/objectStorage/objectStorage.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-objectStorage.yml",
+ "contactId": "jh0003",
+ "name": "ObjectStorage",
+ "description": "Represents storage that provides the ability to store data as objects (or BLOBs of data) without consideration for the underlying filesystem or devices.",
+ "resourceIconPath": "objectStorage",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ObjectStorage"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.yml b/asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.yml
new file mode 100644
index 0000000000..7cd71360f2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.yml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.cp.Port:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_address:
+ type: string
+ required: false
+ order:
+ type: integer
+ required: true
+ default: 0
+ constraints:
+ - greater_or_equal: 0
+ is_default:
+ type: boolean
+ required: false
+ default: false
+ ip_range_start:
+ type: string
+ required: false
+ ip_range_end:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - binding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo
diff --git a/asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.zip b/asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.zip
new file mode 100644
index 0000000000..e1688a7304
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/port/normative-types-new-port.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/port/port.json b/asdc-tests/src/test/resources/CI/importResource/port/port.json
new file mode 100644
index 0000000000..38106903e3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/port/port.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "normative-types-new-port.yml",
+ "contactId": "jh0003",
+ "name": "Port",
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "resourceIconPath": "port",
+ "resourceType": "CP",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Port"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.yml b/asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.yml
new file mode 100644
index 0000000000..e9b1de9518
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Root:
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.Root
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ interfaces:
+ Standard:
+ type: tosca.interfaces.node.lifecycle.Standard
diff --git a/asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.zip b/asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.zip
new file mode 100644
index 0000000000..07583e89f5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/root/normative-types-new-root.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/root/root.json b/asdc-tests/src/test/resources/CI/importResource/root/root.json
new file mode 100644
index 0000000000..53f709bdca
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/root/root.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-root.yml",
+ "contactId": "jh0003",
+ "name": "Root",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Root"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.yml b/asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.yml
new file mode 100644
index 0000000000..a154632c74
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.SoftwareComponent:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.zip b/asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.zip
new file mode 100644
index 0000000000..ea61eec01f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/softwareComponent/normative-types-new-softwareComponent.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/softwareComponent/softwareComponent.json b/asdc-tests/src/test/resources/CI/importResource/softwareComponent/softwareComponent.json
new file mode 100644
index 0000000000..43012d7adc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/softwareComponent/softwareComponent.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-softwareComponent.yml",
+ "contactId": "jh0003",
+ "name": "SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.yml b/asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.yml
new file mode 100644
index 0000000000..5f9a775fe0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.yml
@@ -0,0 +1,15 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.WebApplication:
+ derived_from: tosca.nodes.Root
+ properties:
+ context_root:
+ type: string
+ capabilities:
+ app_endpoint:
+ type: tosca.capabilities.Endpoint
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.WebServer
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.zip b/asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.zip
new file mode 100644
index 0000000000..516d9d9feb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/webApplication/normative-types-new-webApplication.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/webApplication/webApplication.json b/asdc-tests/src/test/resources/CI/importResource/webApplication/webApplication.json
new file mode 100644
index 0000000000..fc25c6966b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/webApplication/webApplication.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-webApplication.yml",
+ "contactId": "jh0003",
+ "name": "WebApplication",
+ "description": "Represents a software application that can be managed and run by a Web Server node. Specific types of web applications such as Java, etc. could be derived from this type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "WebApplication"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.yml b/asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.yml
new file mode 100644
index 0000000000..7c957b5247
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.yml
@@ -0,0 +1,11 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.WebServer:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ # Private, layer 4 endpoints
+ data_endpoint: tosca.capabilities.Endpoint
+ admin_endpoint: tosca.capabilities.Endpoint.Admin
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [ tosca.nodes.WebApplication ] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.zip b/asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.zip
new file mode 100644
index 0000000000..a88761fa5f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/webServer/normative-types-new-webServer.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResource/webServer/webServer.json b/asdc-tests/src/test/resources/CI/importResource/webServer/webServer.json
new file mode 100644
index 0000000000..bd74fb18f8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResource/webServer/webServer.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "normative-types-new-webServer.yml",
+ "contactId": "jh0003",
+ "name": "WebServer",
+ "description": "Represents an abstract software component or service that is capable of hosting and providing management operations for one or more Web Application nodes.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "WebServer"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/VSPPackage.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/VSPPackage.csar
new file mode 100644
index 0000000000..b4aa77511b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/VSPPackage.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_1.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_1.csar
new file mode 100644
index 0000000000..4b167cfa69
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_1.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_2.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_2.csar
new file mode 100644
index 0000000000..1f7bc5ac8d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/caseSensitiveTest_2.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/csars.zip b/asdc-tests/src/test/resources/CI/importResourceTests/csars/csars.zip
new file mode 100644
index 0000000000..8a7f5de6e3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/csars.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/fiveLinesAsBlock0.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/fiveLinesAsBlock0.csar
new file mode 100644
index 0000000000..870a777c66
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/fiveLinesAsBlock0.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion.csar
new file mode 100644
index 0000000000..273fab3a0b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion2.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion2.csar
new file mode 100644
index 0000000000..aa0027f48e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion2.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion3.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion3.csar
new file mode 100644
index 0000000000..ce9a80b240
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion3.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion4.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion4.csar
new file mode 100644
index 0000000000..88ccb448e7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion4.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion5.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion5.csar
new file mode 100644
index 0000000000..a429110ea1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/invalidCsarVersion5.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile1.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile1.csar
new file mode 100644
index 0000000000..3361bfd867
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile1.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile2.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile2.csar
new file mode 100644
index 0000000000..200fbd95af
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile2.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile3.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile3.csar
new file mode 100644
index 0000000000..5ffdf6c4c4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile3.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile4.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile4.csar
new file mode 100644
index 0000000000..30da05f951
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile4.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile5.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile5.csar
new file mode 100644
index 0000000000..e5a3fb862b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/longNamesInToscaMetaFile5.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/missingEntryDefintionPair.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/missingEntryDefintionPair.csar
new file mode 100644
index 0000000000..63a58bcd27
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/missingEntryDefintionPair.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/missingOneLineInToscaMeta.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/missingOneLineInToscaMeta.csar
new file mode 100644
index 0000000000..ac949ce734
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/missingOneLineInToscaMeta.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf.csar
new file mode 100644
index 0000000000..4b37f44c73
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf2.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf2.csar
new file mode 100644
index 0000000000..ec1a65ebb9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/mock_vf2.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThanOneMetaFile.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThanOneMetaFile.csar
new file mode 100644
index 0000000000..084ada11cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThanOneMetaFile.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThenOneYamlFile.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThenOneYamlFile.csar
new file mode 100644
index 0000000000..9615c62e73
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/moreThenOneYamlFile.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/noCSARVersion.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noCSARVersion.csar
new file mode 100644
index 0000000000..65a21c12b8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noCSARVersion.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/noCreatedByValue.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noCreatedByValue.csar
new file mode 100644
index 0000000000..cb08b19463
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noCreatedByValue.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/noEntryDefinitionsValue.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noEntryDefinitionsValue.csar
new file mode 100644
index 0000000000..a3a0e8fd1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noEntryDefinitionsValue.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/noNewLineAfterBLock0.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noNewLineAfterBLock0.csar
new file mode 100644
index 0000000000..4b37f44c73
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noNewLineAfterBLock0.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/noTOSCAMetaFileVersionValue.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noTOSCAMetaFileVersionValue.csar
new file mode 100644
index 0000000000..c2f4d0442a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/noTOSCAMetaFileVersionValue.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlAndMetaFiles.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlAndMetaFiles.csar
new file mode 100644
index 0000000000..74058d5f82
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlAndMetaFiles.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlFile.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlFile.csar
new file mode 100644
index 0000000000..31cc2942d5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/notContainYamlFile.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/someValueAfterBlock0.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/someValueAfterBlock0.csar
new file mode 100644
index 0000000000..e600a39693
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/someValueAfterBlock0.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaFolderNotExists.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaFolderNotExists.csar
new file mode 100644
index 0000000000..17a7e5e39f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaFolderNotExists.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaFileNotExists.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaFileNotExists.csar
new file mode 100644
index 0000000000..55e7179505
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaFileNotExists.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaOutsideTheFolder.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaOutsideTheFolder.csar
new file mode 100644
index 0000000000..53d6987ac7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/toscaMetaOutsideTheFolder.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/underscoreInsteadOfDash.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/underscoreInsteadOfDash.csar
new file mode 100644
index 0000000000..c217dd5006
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/underscoreInsteadOfDash.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/validCsarVersion.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/validCsarVersion.csar
new file mode 100644
index 0000000000..60ac6e3dd2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/validCsarVersion.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf.csar b/asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf.csar
new file mode 100644
index 0000000000..01bf159071
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf.csar
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf_zip.zip b/asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf_zip.zip
new file mode 100644
index 0000000000..66922ae15e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/csars/valid_vf_zip.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/empty_category.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/empty_category.json
new file mode 100644
index 0000000000..7ce1fd5e5f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/empty_category.json
@@ -0,0 +1,22 @@
+{
+ "payloadName": "normative-types-new-empty_category.yml",
+ "contactId": "adminid",
+ "name": "tosca.nodes.empty_category",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.empty_category"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.yml
new file mode 100644
index 0000000000..0ab5ac9322
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.yml
@@ -0,0 +1,16 @@
+tosca.nodes.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.zip
new file mode 100644
index 0000000000..537787f955
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_category/normative-types-new-empty_category.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/empty_contact.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/empty_contact.json
new file mode 100644
index 0000000000..6e043f5606
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/empty_contact.json
@@ -0,0 +1,22 @@
+{
+ "payloadName": "normative-types-new-empty_contact.yml",
+ "contactId": "",
+ "name": "tosca.nodes.empty_contact",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+"categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.empty_contact"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.yml
new file mode 100644
index 0000000000..3d9c111871
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_contact:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.zip
new file mode 100644
index 0000000000..bee0ffef8f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_contact/normative-types-new-empty_contact.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/empty_desc.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/empty_desc.json
new file mode 100644
index 0000000000..7aa4c167b4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/empty_desc.json
@@ -0,0 +1,22 @@
+{
+ "payloadName": "normative-types-new-empty_desc.yml",
+ "contactId": "adminid",
+ "name": "tosca.nodes.empty_desc",
+ "description": "",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.empty_desc"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.yml
new file mode 100644
index 0000000000..0ab5ac9322
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.yml
@@ -0,0 +1,16 @@
+tosca.nodes.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.zip
new file mode 100644
index 0000000000..c105c0989f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_desc/normative-types-new-empty_desc.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/empty_icon.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/empty_icon.json
new file mode 100644
index 0000000000..e1980cee79
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/empty_icon.json
@@ -0,0 +1,22 @@
+{
+ "payloadName": "normative-types-new-empty_icon.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.empty_icon",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.empty_icon"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.yml
new file mode 100644
index 0000000000..0ab5ac9322
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.yml
@@ -0,0 +1,16 @@
+tosca.nodes.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.zip
new file mode 100644
index 0000000000..a500656ff7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_icon/normative-types-new-empty_icon.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/empty_payloadName.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/empty_payloadName.json
new file mode 100644
index 0000000000..2e46c69243
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/empty_payloadName.json
@@ -0,0 +1,22 @@
+{
+ "payloadName": "",
+ "contactId": "adminid",
+ "name": "tosca.nodes.empty_payloadName",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.empty_payloadName"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.yml
new file mode 100644
index 0000000000..0ab5ac9322
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.yml
@@ -0,0 +1,16 @@
+tosca.nodes.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.zip
new file mode 100644
index 0000000000..901d2de441
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_payloadName/normative-types-new-empty_payloadName.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/empty_resource_name.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/empty_resource_name.json
new file mode 100644
index 0000000000..dff8e13ad5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/empty_resource_name.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-empty_resource_name.yml",
+ "contactId": "adminid",
+ "name": "",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["empty_resource_name"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.zip
new file mode 100644
index 0000000000..940a74ab05
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_resource_name/normative-types-new-empty_resource_name.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/empty_tag.json b/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/empty_tag.json
new file mode 100644
index 0000000000..5028baa786
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/empty_tag.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-empty_tag.yml",
+ "contactId": "adminid",
+ "name": "tosca.nodes.empty_tag",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": [""],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.yml b/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.zip b/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.zip
new file mode 100644
index 0000000000..cd75d64a89
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/empty_tag/normative-types-new-empty_tag.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/import _multiple_tags.json b/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/import _multiple_tags.json
new file mode 100644
index 0000000000..ec361827b0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/import _multiple_tags.json
@@ -0,0 +1,18 @@
+{
+ "payloadName": "normative-types-new-import _multiple_tags.yml",
+ "contactId": "adminid",
+ "name": "tosca.nodes.import _multiple_tags",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["tosca.nodes.import _multiple_tags",
+ "import _multiple_tags",
+ "import _multiple_tags2"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.yml b/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.yml
new file mode 100644
index 0000000000..0ab5ac9322
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.yml
@@ -0,0 +1,16 @@
+tosca.nodes.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.zip b/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.zip
new file mode 100644
index 0000000000..1de1fac68c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import _multiple_tags/normative-types-new-import _multiple_tags.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/importResource4test.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/importResource4test.json
new file mode 100644
index 0000000000..c6a4b47903
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/importResource4test.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-importResource4test.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.yml
new file mode 100644
index 0000000000..bce6af3db4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.zip
new file mode 100644
index 0000000000..eb69428cf1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4test/normative-types-new-importResource4test.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/importResource4testCP.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/importResource4testCP.json
new file mode 100644
index 0000000000..417d8a2854
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/importResource4testCP.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testCP.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "resourceType" : "VFC",
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.yml
new file mode 100644
index 0000000000..fb0cef1068
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.CP.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.zip
new file mode 100644
index 0000000000..be77395250
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testCP/normative-types-new-importResource4testCP.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/importResource4testMissingNameSpace.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/importResource4testMissingNameSpace.json
new file mode 100644
index 0000000000..3a14cfc1c8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/importResource4testMissingNameSpace.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testMissingNameSpace.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "resourceType" : "VFC",
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.yml
new file mode 100644
index 0000000000..693ec75b51
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.vfc.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.zip
new file mode 100644
index 0000000000..3160c73fe7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testMissingNameSpace/normative-types-new-importResource4testMissingNameSpace.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/importResource4testUnknown.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/importResource4testUnknown.json
new file mode 100644
index 0000000000..3059d99eec
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/importResource4testUnknown.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testUnknown.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "resourceType" : "VFC",
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.yml
new file mode 100644
index 0000000000..115a61a12b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.Unknown.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.zip
new file mode 100644
index 0000000000..67b81c3d7f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUnknown/normative-types-new-importResource4testUnknown.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/importResource4testUpdateVendorNameAndCategory.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/importResource4testUpdateVendorNameAndCategory.json
new file mode 100644
index 0000000000..4e9affe462
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/importResource4testUpdateVendorNameAndCategory.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-importResource4testUpdateVendorNameAndCategory.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Network L2-3",
+ "subcategories": [{
+ "name": "Router"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "vendorName": "ATT (Tosaaaaaaaaaaaaa)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.yml
new file mode 100644
index 0000000000..69d88984ac
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.yml
@@ -0,0 +1,12 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: update update
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.zip
new file mode 100644
index 0000000000..a092a1c14b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateVendorNameAndCategory/normative-types-new-importResource4testUpdateVendorNameAndCategory.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/importResource4testUpdateWithoutReqCap.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/importResource4testUpdateWithoutReqCap.json
new file mode 100644
index 0000000000..f6ab834378
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/importResource4testUpdateWithoutReqCap.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testUpdateWithoutReqCap.yml",
+ "contactId": "cs0004",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.Update",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test",
+ "updatedTag"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.3.33"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.yml
new file mode 100644
index 0000000000..a580e8b7c6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.yml
@@ -0,0 +1,12 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.zip
new file mode 100644
index 0000000000..3005f9231f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testUpdateWithoutReqCap/normative-types-new-importResource4testUpdateWithoutReqCap.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/importResource4testVF.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/importResource4testVF.json
new file mode 100644
index 0000000000..6ebba43604
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/importResource4testVF.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testVF.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "resourceType" : "VFC",
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.yml
new file mode 100644
index 0000000000..2879ab8cc8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vF.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.zip
new file mode 100644
index 0000000000..46c652fd24
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVF/normative-types-new-importResource4testVF.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/importResource4testVFC.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/importResource4testVFC.json
new file mode 100644
index 0000000000..b5e2e329bd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/importResource4testVFC.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testVFC.yml",
+ "contactId": "jh0003",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "vendorName": "ATT (Tosca)",
+ "resourceType" : "VFC",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.yml
new file mode 100644
index 0000000000..cdf347e872
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.VFC.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.zip
new file mode 100644
index 0000000000..0ca8098ff9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVFC/normative-types-new-importResource4testVFC.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/importResource4testVL.json b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/importResource4testVL.json
new file mode 100644
index 0000000000..158530a3e5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/importResource4testVL.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "normative-types-new-importResource4testVL.yml",
+ "contactId": "jh0003",
+ "resourceType" : "VFC",
+ "name": "importResource4test",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["importResource4test"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.yml b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.yml
new file mode 100644
index 0000000000..55888da2bc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.yml
@@ -0,0 +1,30 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.Vl.importResource4test:
+ derived_from: tosca.nodes.Root
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature2:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency2 :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.importResource4test
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.zip b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.zip
new file mode 100644
index 0000000000..9a1a28b1f6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/importResource4testVL/normative-types-new-importResource4testVL.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.yml b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.yml
new file mode 100644
index 0000000000..72fe26e0d1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.yml
@@ -0,0 +1,141 @@
+tosca.capabilities.Root:
+ description: The TOSCA root Capability Type all other TOSCA base Capability Types derive from
+tosca.capabilities.Node:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ cpu_frequency:
+ type: scalar-unit.frequency
+ required: false
+ constraints:
+ - greater_or_equal: 0.1 GHz
+ disk_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+ mem_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: PortDef
+ required: false
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ port_name:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ default: PRIVATE
+ initiator:
+ type: string
+ default: source
+ constraints:
+ - valid_values: [ source, target, peer ]
+ ports:
+ type: map
+ required: false
+ constraints:
+ - min_length: 1
+ entry_schema:
+ type: PortSpec
+ attributes:
+ ip_address:
+ type: string
+tosca.capabilities.Endpoint.Public:
+ derived_from: tosca.capabilities.Endpoint
+ properties:
+ # Change the default network_name to use the first public network found
+ network_name: PUBLIC
+ floating:
+ description: >
+ indicates that the public address should be allocated from a pool of floating IPs that are associated with the network.
+ type: boolean
+ default: false
+ status: experimental
+ dns_name:
+ description: The optional name to register with DNS
+ type: string
+ required: false
+ status: experimental
+tosca.capabilities.Endpoint.Admin:
+ derived_from: tosca.capabilities.Endpoint
+ # Change Endpoint secure indicator to true from its default of false
+ properties:
+ secure: true
+tosca.capabilities.Endpoint.Database:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.OperatingSystem:
+ derived_from: tosca.capabilities.Root
+ properties:
+ architecture:
+ type: string
+ required: false
+ type:
+ type: string
+ required: false
+ distribution:
+ type: string
+ required: false
+ version:
+ type: version
+ required: false
+tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_instances:
+ type: integer
+ default: 1
+ max_instances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+tosca.capabilities.network.Bindable:
+ derived_from: tosca.capabilities.Node
+
+
+tosca.capabilities.Container.Docker:
+ derived_from: tosca.capabilities.Container
+ properties:
+ version:
+ type: list
+ required: false
+ entry_schema: version
+ publish_all:
+ type: boolean
+ default: false
+ required: false
+ publish_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ expose_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ volumes:
+ type: list
+ entry_schema: string
+ required: false
+
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.zip b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.zip
new file mode 100644
index 0000000000..4f945a7d1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypes.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi.zip b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi.zip
new file mode 100644
index 0000000000..550cd756f4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_MissingDerivedFrom.yml b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_MissingDerivedFrom.yml
new file mode 100644
index 0000000000..31fac955e3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_MissingDerivedFrom.yml
@@ -0,0 +1,132 @@
+tosca.capabilities.Root:
+ description: The TOSCA root Capability Type all other TOSCA base Capability Types derive from
+tosca.capabilities.Node:
+tosca.capabilities.Container:
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ cpu_frequency:
+ type: scalar-unit.frequency
+ required: false
+ constraints:
+ - greater_or_equal: 0.1 GHz
+ disk_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+ mem_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+tosca.capabilities.Test.Ci:
+tosca.capabilities.Endpoint:
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: PortDef
+ required: false
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ port_name:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ default: PRIVATE
+ initiator:
+ type: string
+ default: source
+ constraints:
+ - valid_values: [ source, target, peer ]
+ ports:
+ type: map
+ required: false
+ constraints:
+ - min_length: 1
+ entry_schema:
+ type: PortSpec
+ attributes:
+ ip_address:
+ type: string
+tosca.capabilities.Endpoint.Public:
+ properties:
+ # Change the default network_name to use the first public network found
+ network_name: PUBLIC
+ floating:
+ description: >
+ indicates that the public address should be allocated from a pool of floating IPs that are associated with the network.
+ type: boolean
+ default: false
+ status: experimental
+ dns_name:
+ description: The optional name to register with DNS
+ type: string
+ required: false
+ status: experimental
+tosca.capabilities.Endpoint.Admin:
+ # Change Endpoint secure indicator to true from its default of false
+ properties:
+ secure: true
+tosca.capabilities.Endpoint.Database:
+tosca.capabilities.OperatingSystem:
+ properties:
+ architecture:
+ type: string
+ required: false
+ type:
+ type: string
+ required: false
+ distribution:
+ type: string
+ required: false
+ version:
+ type: version
+ required: false
+tosca.capabilities.Scalable:
+ properties:
+ min_instances:
+ type: integer
+ default: 1
+ max_instances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+tosca.capabilities.network.Bindable:
+
+
+tosca.capabilities.Container.Docker:
+ properties:
+ version:
+ type: list
+ required: false
+ entry_schema: version
+ publish_all:
+ type: boolean
+ default: false
+ required: false
+ publish_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ expose_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ volumes:
+ type: list
+ entry_schema: string
+ required: false
+
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_WithoutRoot.yml b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_WithoutRoot.yml
new file mode 100644
index 0000000000..12c7d5a977
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/import_capabilitiesTypes/capabilityTypesCi_WithoutRoot.yml
@@ -0,0 +1,141 @@
+tosca.capabilities.Node:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ cpu_frequency:
+ type: scalar-unit.frequency
+ required: false
+ constraints:
+ - greater_or_equal: 0.1 GHz
+ disk_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+ mem_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+tosca.capabilities.Test.Ci:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: PortDef
+ required: false
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ port_name:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ default: PRIVATE
+ initiator:
+ type: string
+ default: source
+ constraints:
+ - valid_values: [ source, target, peer ]
+ ports:
+ type: map
+ required: false
+ constraints:
+ - min_length: 1
+ entry_schema:
+ type: PortSpec
+ attributes:
+ ip_address:
+ type: string
+tosca.capabilities.Endpoint.Public:
+ derived_from: tosca.capabilities.Endpoint
+ properties:
+ # Change the default network_name to use the first public network found
+ network_name: PUBLIC
+ floating:
+ description: >
+ indicates that the public address should be allocated from a pool of floating IPs that are associated with the network.
+ type: boolean
+ default: false
+ status: experimental
+ dns_name:
+ description: The optional name to register with DNS
+ type: string
+ required: false
+ status: experimental
+tosca.capabilities.Endpoint.Admin:
+ derived_from: tosca.capabilities.Endpoint
+ # Change Endpoint secure indicator to true from its default of false
+ properties:
+ secure: true
+tosca.capabilities.Endpoint.Database:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.OperatingSystem:
+ derived_from: tosca.capabilities.Root
+ properties:
+ architecture:
+ type: string
+ required: false
+ type:
+ type: string
+ required: false
+ distribution:
+ type: string
+ required: false
+ version:
+ type: version
+ required: false
+tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_instances:
+ type: integer
+ default: 1
+ max_instances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+tosca.capabilities.network.Bindable:
+ derived_from: tosca.capabilities.Node
+
+
+tosca.capabilities.Container.Docker:
+ derived_from: tosca.capabilities.Container
+ properties:
+ version:
+ type: list
+ required: false
+ entry_schema: version
+ publish_all:
+ type: boolean
+ default: false
+ required: false
+ publish_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ expose_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ volumes:
+ type: list
+ entry_schema: string
+ required: false
+
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/missing_category.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/missing_category.json
new file mode 100644
index 0000000000..23a7d76103
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/missing_category.json
@@ -0,0 +1,12 @@
+{
+ "payloadName": "normative-types-new-missing_category.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.missing_category",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "tags": [
+ "tosca.nodes.missing_category"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.zip
new file mode 100644
index 0000000000..51a01c1bb9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_category/normative-types-new-missing_category.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/missing_contact.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/missing_contact.json
new file mode 100644
index 0000000000..8f3e5ae2e2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/missing_contact.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "normative-types-new-missing_contact.yml",
+ "name": "tosca.nodes.missing_contact",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.missing_contact"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.yml
new file mode 100644
index 0000000000..3d9c111871
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_contact:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.zip
new file mode 100644
index 0000000000..21d14a9a73
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_contact/normative-types-new-missing_contact.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/missing_derived_from.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/missing_derived_from.json
new file mode 100644
index 0000000000..37a55bf17a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/missing_derived_from.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-missing_derived_from.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.missing_derived_from",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["tosca.nodes.missing_derived_from"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.yml
new file mode 100644
index 0000000000..82069ecb7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.yml
@@ -0,0 +1,15 @@
+tosca.nodes.missing_userId:
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.zip
new file mode 100644
index 0000000000..22e3d4da29
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_derived_from/normative-types-new-missing_derived_from.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/missing_desc.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/missing_desc.json
new file mode 100644
index 0000000000..bec3896cfd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/missing_desc.json
@@ -0,0 +1,15 @@
+{
+ "payloadName": "normative-types-new-missing_desc.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.missing_desc",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["tosca.nodes.missing_desc"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.zip
new file mode 100644
index 0000000000..bd5174df62
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_desc/normative-types-new-missing_desc.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/missing_icon.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/missing_icon.json
new file mode 100644
index 0000000000..9e9b7572bc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/missing_icon.json
@@ -0,0 +1,15 @@
+{
+ "payloadName": "normative-types-new-missing_icon.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.missing_icon",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["tosca.nodes.missing_icon"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.zip
new file mode 100644
index 0000000000..45d8e192f1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_icon/normative-types-new-missing_icon.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/missing_payloadName.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/missing_payloadName.json
new file mode 100644
index 0000000000..58df0f7400
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/missing_payloadName.json
@@ -0,0 +1,15 @@
+{
+ "contactId": "jh0003",
+ "name": "tosca.nodes.missing_payloadName",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["tosca.nodes.missing_payloadName"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.zip
new file mode 100644
index 0000000000..3a72360c25
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_payloadName/normative-types-new-missing_payloadName.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/missing_resource_name.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/missing_resource_name.json
new file mode 100644
index 0000000000..dbf7ef82f1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/missing_resource_name.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-missing_resource_name.yml",
+ "contactId": "jh0003",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "category": "Generic/Infrastructure",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["missing_resource_name"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.zip
new file mode 100644
index 0000000000..24294b9647
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_resource_name/normative-types-new-missing_resource_name.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/missing_tags.json b/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/missing_tags.json
new file mode 100644
index 0000000000..36c845f001
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/missing_tags.json
@@ -0,0 +1,15 @@
+{
+ "payloadName": "normative-types-new-missing_tags.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.missing_tags",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.yml b/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.yml
new file mode 100644
index 0000000000..bad0c73a1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.missing_userId:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.zip b/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.zip
new file mode 100644
index 0000000000..1d619e8c93
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/missing_tags/normative-types-new-missing_tags.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/myCompute.json b/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/myCompute.json
new file mode 100644
index 0000000000..af216b7c21
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/myCompute.json
@@ -0,0 +1,24 @@
+{
+ "payloadName": "normative-types-new-myCompute.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.MyCompute",
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing information are specified the cloud provider default
+ machine will be used. It is strongly recommended to specify the required CPUs and memory
+ at least.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.MyCompute"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.yml b/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.yml
new file mode 100644
index 0000000000..dff93f621a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.zip b/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.zip
new file mode 100644
index 0000000000..a4fb8979fc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/myCompute/normative-types-new-myCompute.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.yml b/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.yml
new file mode 100644
index 0000000000..eccfd4ddc5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.yml
@@ -0,0 +1,29 @@
+tosca.nodes.network.PortInvalidDefaultValue:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_address:
+ type: string
+ required: false
+ order:
+ type: integer
+ required: true
+ default: 1.5
+ constraints:
+ - greater_or_equal: 0
+ is_default:
+ type: boolean
+ required: false
+ default: false
+ ip_range_start:
+ type: string
+ required: false
+ ip_range_end:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - binding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.zip b/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.zip
new file mode 100644
index 0000000000..fed020cdd3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/normative-types-new-portInvalidDefaultValue.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/portInvalidDefaultValue.json b/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/portInvalidDefaultValue.json
new file mode 100644
index 0000000000..6b6e064a30
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/portInvalidDefaultValue/portInvalidDefaultValue.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-portInvalidDefaultValue.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.network.PortInvalidDefaultValue",
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "resourceIconPath": "port",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Network Elements"
+ }]
+ }],
+ "tags": ["tosca.nodes.network.PortInvalidDefaultValue"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.yml b/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.yml
new file mode 100644
index 0000000000..de109820a9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.Compute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.zip b/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.zip
new file mode 100644
index 0000000000..bda162b32f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/normative-types-new-userCompute.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/userCompute.json b/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/userCompute.json
new file mode 100644
index 0000000000..ab10c0b523
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/userCompute/userCompute.json
@@ -0,0 +1,26 @@
+{
+ "payloadName": "normative-types-new-userCompute.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.userCompute",
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing information are specified the cloud provider default
+ machine will be used. It is strongly recommended to specify the required CPUs and memory
+ at least.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "tosca.nodes.userCompute"
+ ],
+ "vendorName": "UserVendor",
+ "vendorRelease": "1.1.1"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.yml b/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.yml
new file mode 100644
index 0000000000..5c73365640
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.yml
@@ -0,0 +1,29 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.Compute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.zip b/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.zip
new file mode 100644
index 0000000000..3ecead7579
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/normative-types-new-userUpdateCompute.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/userUpdateCompute.json b/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/userUpdateCompute.json
new file mode 100644
index 0000000000..6e63c434fb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/userUpdateCompute/userUpdateCompute.json
@@ -0,0 +1,22 @@
+{
+ "payloadName": "normative-types-new-userUpdateCompute.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.userCompute",
+ "description": "Short description",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "vendorName": "UpdatedVendor",
+ "vendorRelease": "1.1.2",
+ "tags": [
+ "tosca.nodes.userCompute"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.yml
new file mode 100644
index 0000000000..af70663fbe
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.yml
@@ -0,0 +1,39 @@
+validateProporties_happyScenarios:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test1:
+ type: boolean
+ required: false
+ default: true
+ validation_test2:
+ type: boolean
+ required: false
+ default: false
+ validation_test3:
+ type: integer
+ required: false
+ default: 1234
+ validation_test4:
+ type: float
+ required: false
+ default: 123.123
+ validation_test5:
+ type: string
+ required: false
+ default: cooolString
+ validation_test6:
+ type: scalar-unit.size
+ required: false
+ default: cooolString
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.zip
new file mode 100644
index 0000000000..4104dc3618
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/normative-types-new-validateProporties_happyScenarios.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/validateProporties_happyScenarios.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/validateProporties_happyScenarios.json
new file mode 100644
index 0000000000..1235ba57e6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_happyScenarios/validateProporties_happyScenarios.json
@@ -0,0 +1,14 @@
+{
+ "payloadName": "normative-types-new-validateProporties_happyScenarios.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["tosca.nodes.SoftwareComponent"]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.yml
new file mode 100644
index 0000000000..14778745ef
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeBoolean_valueInit:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: boolean
+ required: false
+ default: 123456
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.zip
new file mode 100644
index 0000000000..6eb2d7e7af
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/normative-types-new-validateProporties_typeBoolean_valueInit.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/validateProporties_typeBoolean_valueInit.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/validateProporties_typeBoolean_valueInit.json
new file mode 100644
index 0000000000..c4f4b37911
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueInit/validateProporties_typeBoolean_valueInit.json
@@ -0,0 +1,14 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeBoolean_valueInit.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["tosca.nodes.SoftwareComponent"]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.yml
new file mode 100644
index 0000000000..4d529ad285
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeBoolean_valueString:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: boolean
+ required: false
+ default: abcd
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.zip
new file mode 100644
index 0000000000..538ba0e3ef
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/normative-types-new-validateProporties_typeBoolean_valueString.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/validateProporties_typeBoolean_valueString.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/validateProporties_typeBoolean_valueString.json
new file mode 100644
index 0000000000..f1ff279a84
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeBoolean_valueString/validateProporties_typeBoolean_valueString.json
@@ -0,0 +1,14 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeBoolean_valueString.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["tosca.nodes.SoftwareComponent"]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.yml
new file mode 100644
index 0000000000..fee8c9b68e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeFloat_valueBoolean:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: float
+ required: false
+ default: true
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.zip
new file mode 100644
index 0000000000..a577aeb1e3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/normative-types-new-validateProporties_typeFloat_valueBoolean.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/validateProporties_typeFloat_valueBoolean.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/validateProporties_typeFloat_valueBoolean.json
new file mode 100644
index 0000000000..f1769769d4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueBoolean/validateProporties_typeFloat_valueBoolean.json
@@ -0,0 +1,14 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeFloat_valueBoolean.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["tosca.nodes.SoftwareComponent"]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.yml
new file mode 100644
index 0000000000..88e5bb85c3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeFloat_valueString:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: float
+ required: false
+ default: abcd
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.zip
new file mode 100644
index 0000000000..a9ec4d791e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/normative-types-new-validateProporties_typeFloat_valueString.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/validateProporties_typeFloat_valueString.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/validateProporties_typeFloat_valueString.json
new file mode 100644
index 0000000000..3eab4bb311
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeFloat_valueString/validateProporties_typeFloat_valueString.json
@@ -0,0 +1,14 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeFloat_valueString.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["tosca.nodes.SoftwareComponent"]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.yml
new file mode 100644
index 0000000000..69bd3ba41e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeInit_valueBoolean:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: integer
+ required: false
+ default: true
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.zip
new file mode 100644
index 0000000000..9e76fbee46
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/normative-types-new-validateProporties_typeInit_valueBoolean.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/validateProporties_typeInit_valueBoolean.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/validateProporties_typeInit_valueBoolean.json
new file mode 100644
index 0000000000..bb145d2f28
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueBoolean/validateProporties_typeInit_valueBoolean.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeInit_valueBoolean.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": [
+ "tosca.nodes.SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.yml
new file mode 100644
index 0000000000..60ae3b6333
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeInit_valueFloat:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: integer
+ required: false
+ default: 0.123
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.zip
new file mode 100644
index 0000000000..91b69dd746
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/normative-types-new-validateProporties_typeInit_valueFloat.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/validateProporties_typeInit_valueFloat.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/validateProporties_typeInit_valueFloat.json
new file mode 100644
index 0000000000..2e7f5f232a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueFloat/validateProporties_typeInit_valueFloat.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeInit_valueFloat.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": [
+ "tosca.nodes.SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.yml
new file mode 100644
index 0000000000..7bd723e915
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeInit_valueString:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: integer
+ required: false
+ default: abcd
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.zip
new file mode 100644
index 0000000000..747e215071
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/normative-types-new-validateProporties_typeInit_valueString.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/validateProporties_typeInit_valueString.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/validateProporties_typeInit_valueString.json
new file mode 100644
index 0000000000..29e4b29be0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeInit_valueString/validateProporties_typeInit_valueString.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeInit_valueString.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": [
+ "tosca.nodes.SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.yml
new file mode 100644
index 0000000000..217f3405e2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.yml
@@ -0,0 +1,50 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeList_valueUrlCredential:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: list
+ required: false
+ default:
+ - protocol: protocol1~!#@~$%^*()[];:'"|\/
+ token: token1
+ - protocol: protocol2~!#@~$%^*()[];:'"|\/
+ token: token2
+ entry_schema:
+ type: tosca.datatypes.Credential
+ attributes:
+ validation_test_list:
+ type: list
+ required: false
+ default:
+ - protocol: protocol1~!#@~$%^*()[];:'"|\/
+ token: token1
+ - protocol: protocol2~!#@~$%^*()[];:'"|\/
+ token: token2
+ entry_schema:
+ type: tosca.datatypes.Credential
+ validation_test_map:
+ type: map
+ required: false
+ default:
+ key1:
+ protocol: protocol1~!#@~$%^*()[];:'"|\/
+ token: token1
+ key2:
+ protocol: protocol2~!#@~$%^*()[];:'"|\/
+ token: token2
+ entry_schema:
+ type: tosca.datatypes.Credential
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.zip
new file mode 100644
index 0000000000..10b1ce10ac
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/normative-types-new-validateProporties_typeList_valueUrlCredential.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/validateProporties_typeList_valueUrlCredential.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/validateProporties_typeList_valueUrlCredential.json
new file mode 100644
index 0000000000..59d6bbff21
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeList_valueUrlCredential/validateProporties_typeList_valueUrlCredential.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeList_valueUrlCredential.yml",
+ "contactId": "jh0003",
+ "name": "resourceListValueCredential",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["resourceListValueCredential"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.yml
new file mode 100644
index 0000000000..2ce14b21a9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.yml
@@ -0,0 +1,29 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_typeMap_valueUrlCredential:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: map
+ required: false
+ default:
+ key1:
+ protocol: protocol1
+ token: token1
+ key2:
+ protocol: protocol2
+ token: token2
+ entry_schema:
+ type: tosca.datatypes.Credential
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.zip
new file mode 100644
index 0000000000..9a8f093d95
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/normative-types-new-validateProporties_typeMap_valueUrlCredential.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/validateProporties_typeMap_valueUrlCredential.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/validateProporties_typeMap_valueUrlCredential.json
new file mode 100644
index 0000000000..3fb0bec243
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeMap_valueUrlCredential/validateProporties_typeMap_valueUrlCredential.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeMap_valueUrlCredential.yml",
+ "contactId": "jh0003",
+ "name": "resourceMapValueCredential",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["resourceMapValueCredential"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/normative-types-new-validateProporties_typeString_valueString_special_chars.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/normative-types-new-validateProporties_typeString_valueString_special_chars.yml
new file mode 100644
index 0000000000..cc109d1528
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/normative-types-new-validateProporties_typeString_valueString_special_chars.yml
@@ -0,0 +1,19 @@
+validateProporties_typeString_valueString_special_chars:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: string
+ required: false
+ default: ~!#@~
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/validateProporties_typeString_valueString_special_chars.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/validateProporties_typeString_valueString_special_chars.json
new file mode 100644
index 0000000000..90f66e800e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_special_chars/validateProporties_typeString_valueString_special_chars.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeString_valueString_special_chars.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": [
+ "tosca.nodes.SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/normative-types-new-validateProporties_typeString_valueString_trimming.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/normative-types-new-validateProporties_typeString_valueString_trimming.yml
new file mode 100644
index 0000000000..4ecd161682
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/normative-types-new-validateProporties_typeString_valueString_trimming.yml
@@ -0,0 +1,19 @@
+validateProporties_typeBoolean_valueInit:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ validation_test:
+ type: boolean
+ required: false
+ default: true
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/validateProporties_typeString_valueString_trimming.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/validateProporties_typeString_valueString_trimming.json
new file mode 100644
index 0000000000..34d9321b7e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeString_valueString_trimming/validateProporties_typeString_valueString_trimming.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeBoolean_valueInit.yml",
+ "contactId": "jh0003",
+ "name": "tosca.nodes.SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": [
+ "tosca.nodes.SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.yml b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.yml
new file mode 100644
index 0000000000..feb13b367f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.yml
@@ -0,0 +1,32 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.validateProporties_type:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ myprop:
+ type: tosca.datatypes.network.PortInfoComplex
+ default:
+ addressesMap:
+ key1:
+ protocol: protocol1
+ token: token1
+ key2:
+ protocol: protocol2
+ token: token2
+ addressesList:
+ - protocol: protocol1~!#@~$%^*()[];:'"|\/
+ token: token1
+ - protocol: protocol2~!#@~$%^*()[];:'"|\/
+ token: token2
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.zip b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.zip
new file mode 100644
index 0000000000..e8f0470948
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/normative-types-new-validateProporties_typeMap.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/validateProporties_typeTestDataType.json b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/validateProporties_typeTestDataType.json
new file mode 100644
index 0000000000..b751545f11
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importResourceTests/validateProporties_typeTestDataType/validateProporties_typeTestDataType.json
@@ -0,0 +1,16 @@
+{
+ "payloadName": "normative-types-new-validateProporties_typeMap.yml",
+ "contactId": "jh0003",
+ "name": "resourceMapValueTest",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Abstract"
+ }]
+ }],
+ "tags": ["resourceMapValueTest"],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03"
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.yml b/asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.yml
new file mode 100644
index 0000000000..120f33d5f1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.yml
@@ -0,0 +1,23 @@
+services:
+ Mobility:
+ name: "Mobility1"
+ icons: ['mobility']
+ Network_L1_3:
+ name: "Network L1-31"
+ icons: ['network_l_1-3']
+resources:
+ NetworkLayer23:
+ name: "Network Layer 2-31"
+ subcategories:
+ Router:
+ name: "Router"
+ icons: ['router']
+ Gateway:
+ name: "Gateway"
+ icons: ['gateway']
+ WAN_Connectors:
+ name: "WAN Connectors"
+ icons: ['connector']
+ LAN_Connectors:
+ name: "LAN Connectors"
+ icons: ['connector']
diff --git a/asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.zip b/asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.zip
new file mode 100644
index 0000000000..68b7aa9114
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importTypesTest/categoryTypesTest.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.yml b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.yml
new file mode 100644
index 0000000000..484ed4dff6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.yml
@@ -0,0 +1,13 @@
+org.openecomp.groups.MyHeatStack1:
+ derived_from: tosca.groups.Root
+ description: Grouped all heat resources which are in the same heat stack
+ properties:
+ heat_files:
+ type: list
+ description: Heat files which associate to this group/heat stack
+ required: true
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ url_credential:
+ type: tosca.datatypes.Credential \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.zip b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.zip
new file mode 100644
index 0000000000..ee57b02826
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack1/myHeatStack1.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.yml b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.yml
new file mode 100644
index 0000000000..94fbb0451e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.yml
@@ -0,0 +1,14 @@
+org.openecomp.groups.MyHeatStack2:
+ derived_from: tosca.groups.Root
+ description: Grouped all heat resources which are in the same heat stack
+ members: [ tosca.nodes.Compute ]
+ properties:
+ heat_files:
+ type: list
+ description: Heat files which associate to this group/heat stack
+ required: true
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ url_credential:
+ type: tosca.datatypes.Credential \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.zip b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.zip
new file mode 100644
index 0000000000..1d75c7f4d0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/importTypesTest/myHeatStack2/myHeatStack2.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/other/mapping.json b/asdc-tests/src/test/resources/CI/other/mapping.json
new file mode 100644
index 0000000000..3310d776b9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/other/mapping.json
@@ -0,0 +1,182 @@
+{ "order": 1, "template": "auditingevents-*", "settings": {}, "mappings":
+{
+"distributiondownloadevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "auditinggetuebclusterevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "distributionstatusevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+"distributionengineevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ROLE": { "include_in_all": true, "type": "string" },
+ "API_KEY": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "D_ENV": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "useraccessevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER": { "include_in_all": true, "type": "string" }},
+ "_all": { "enabled": true }},
+"resourceadminevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "PREV_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "PREV_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DPREV_STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DCURR_STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOSCA_NODE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "COMMENT": { "include_in_all": true, "type": "string" },
+ "ARTIFACT_DATA": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "PREV_ARTIFACT_UUID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_ARTIFACT_UUID": { "include_in_all": true, "index": "not_analyzed", "type": "string" } },
+ "_all": { "enabled": true }} ,
+"useradminevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_AFTER": { "include_in_all": true, "type": "string" },
+ "USER_BEFORE": { "include_in_all": true, "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" }},
+ "_all": { "enabled": true } },
+"distributionnotificationevent":
+ {"properties":{
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "categoryevent":
+ { "properties":{
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CATEGORY_NAME": { "include_in_all": true, "type": "string" },
+ "SUB_CATEGORY_NAME": { "include_in_all": true, "type": "string" },
+ "GROUPING_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } },
+ "authevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER": { "include_in_all": true, "type": "string" },
+ "AUTH_STATUS": { "include_in_all": true, "index": "not_analyzed","type": "string" } ,
+ "REALM": { "include_in_all": true, "index": "not_analyzed","type": "string" }} ,
+ "_all": { "enabled": true }},
+ "consumerevent":
+ { "properties":{
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ECOMP_USER": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } },
+ "getuserslistevent":
+ { "properties":{
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DETAILS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } },
+ "getcategoryhierarchyevent":
+ { "properties":{
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DETAILS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } },
+ "distributiondeployevent":
+ { "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } }},
+ "aliases": { "last_3_months": {}}} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/addYangXmlArtifactToResource.xml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/addYangXmlArtifactToResource.xml
new file mode 100644
index 0000000000..0415385bd0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/addYangXmlArtifactToResource.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<data>
+ <sports>
+ <person>
+ <name>Lionel Andres Messi</name>
+ <birthday>1987-06-24T00:00:00-00:00</birthday>
+ </person>
+ <person>
+ <name>Cristiano Ronaldo</name>
+ <birthday>1985-02-05T00:00:00-00:00</birthday>
+ </person>
+ <team>
+ <name>FC Barcelona</name>
+ <player>
+ <name>Lionel Andres Messi</name>
+ <season>Champions League 2014-2015</season>
+ <number>10</number>
+ <scores>43</scores>
+ </player>
+ </team>
+ <team>
+ <name>Real Madrid</name>
+ <player>
+ <name>Cristiano Ronaldo</name>
+ <season>Champions League 2014-2015</season>
+ <number>7</number>
+ <scores>48</scores>
+ </player>
+ </team>
+ </sports>
+
+</data> \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.yaml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.zip b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.zip
new file mode 100644
index 0000000000..c8a2726421
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat 0 2.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat_net 0 2.yaml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat_net 0 2.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/asc_heat_net 0 2.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatEnvfile.env b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatEnvfile.env
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatEnvfile.env
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatInvalidFormat.yaml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatInvalidFormat.yaml
new file mode 100644
index 0000000000..b70d5a4b0a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heatInvalidFormat.yaml
@@ -0,0 +1,9 @@
+heat_template_version: 2013-05-23
+
+parameters:
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heat_mini.yaml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heat_mini.yaml
new file mode 100644
index 0000000000..a545569129
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/heat_mini.yaml
@@ -0,0 +1,13 @@
+heat_template_version: 2013-05-23
+
+parameters:
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+
+resources:
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidJson.json b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidJson.json
new file mode 100644
index 0000000000..48a3e89deb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidJson.json
@@ -0,0 +1,11 @@
+{
+ "glossary": {
+ "title": "example glossary",
+ "GlossDiv": {
+ "title": "S",
+ "GlossList": {
+ "GlossEntry": {
+ "ID": "SGML",
+ "SortAs": "SGML",
+ "GlossTerm": "Standard Generalized Markup Language",
+ "Acronym": "SGML",
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYamlFormat.yaml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYamlFormat.yaml
new file mode 100644
index 0000000000..5c51039931
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYamlFormat.yaml
@@ -0,0 +1,787 @@
+ heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYangXml.xml b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYangXml.xml
new file mode 100644
index 0000000000..8978e0d5ed
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/invalidYangXml.xml
@@ -0,0 +1,35 @@
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+
+ <sports xmlns="http://example.com/example-sports">
+ <person>
+ <name>Lionel Andr�s Messi</name>
+ <birthday>1987-06-24T00:00:00-00:00</birthday>
+ </person>
+ <person>
+ <name>Cristiano Ronaldo</name>
+ <birthday>1985-02-05T00:00:00-00:00</birthday>
+ </person>
+ <team>
+ <name>FC Barcelona</name>
+ <player>
+ <name>Lionel Andr�s Messi</name>
+ <season>Champions League 2014/2015</season>
+ <number>10</number>
+ <scores>43</scores>
+ </player>
+ </team>
+ <team>
+ <name>Real Madrid</name>
+ <player>
+ <name>Cristiano Ronaldo</name>
+ <season>Champions League 2014/2015</season>
+ <number>7</number>
+ <scores>48</scores>
+ </player>
+ </team>
+ </sports>
+
+
+
+</data>
+</data> \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/jsonArtifact.json b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/jsonArtifact.json
new file mode 100644
index 0000000000..d5ca56d195
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/jsonArtifact.json
@@ -0,0 +1,22 @@
+{
+ "glossary": {
+ "title": "example glossary",
+ "GlossDiv": {
+ "title": "S",
+ "GlossList": {
+ "GlossEntry": {
+ "ID": "SGML",
+ "SortAs": "SGML",
+ "GlossTerm": "Standard Generalized Markup Language",
+ "Acronym": "SGML",
+ "Abbrev": "ISO 8879:1986",
+ "GlossDef": {
+ "para": "A meta-markup language, used to create markup languages such as DocBook.",
+ "GlossSeeAlso": ["GML", "XML"]
+ },
+ "GlossSee": "markup"
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/other.txt b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/other.txt
new file mode 100644
index 0000000000..5f8f77ca2c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/HeatDeploymentArtifacts/other.txt
@@ -0,0 +1,3 @@
+cmd1
+cmd2
+cmd3 \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/bluePrintSampleArtifact.xml b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/bluePrintSampleArtifact.xml
new file mode 100644
index 0000000000..10c46b7269
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/bluePrintSampleArtifact.xml
@@ -0,0 +1,3 @@
+<test>
+ dfsfsdfsdf
+</test> \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/docSampleArtifact.docx b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/docSampleArtifact.docx
new file mode 100644
index 0000000000..c281f532f8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/docSampleArtifact.docx
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.emf b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.emf
new file mode 100644
index 0000000000..9c478f6ce1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.emf
@@ -0,0 +1,2 @@
+This is sample EMF file
+We currently not checking the file content. \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.xml b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.xml
new file mode 100644
index 0000000000..10c46b7269
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/emfSampleArtifact.xml
@@ -0,0 +1,3 @@
+<test>
+ dfsfsdfsdf
+</test> \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/eventSampleArtifact.xml b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/eventSampleArtifact.xml
new file mode 100644
index 0000000000..10c46b7269
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/eventSampleArtifact.xml
@@ -0,0 +1,3 @@
+<test>
+ dfsfsdfsdf
+</test> \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/jsonSampleArtifact.json b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/jsonSampleArtifact.json
new file mode 100644
index 0000000000..b749a9e89c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/jsonSampleArtifact.json
@@ -0,0 +1,3 @@
+{
+ "test": "This is test"
+}
diff --git a/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/toscaSampleArtifact.yml b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/toscaSampleArtifact.yml
new file mode 100644
index 0000000000..10ccf71d51
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/ResourceInstanceArtifacts/toscaSampleArtifact.yml
@@ -0,0 +1,5 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: org.openecomp.resource.cp.root \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/addHeatArtifactToServiceAndSertify/asc_heat 0 2.yaml b/asdc-tests/src/test/resources/CI/tests/addHeatArtifactToServiceAndSertify/asc_heat 0 2.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/addHeatArtifactToServiceAndSertify/asc_heat 0 2.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/downloadResourceArtifactSuccess/org.openstack.Rally.zip b/asdc-tests/src/test/resources/CI/tests/downloadResourceArtifactSuccess/org.openstack.Rally.zip
new file mode 100644
index 0000000000..0951d5cef8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/downloadResourceArtifactSuccess/org.openstack.Rally.zip
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/mysql.yml
new file mode 100644
index 0000000000..f512f8071e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getResourceArtifactFileContentTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getResourceArtifactFileContentTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactFileContentTest/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListNoContentTest/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListNoContentTest/mysql.yml
new file mode 100644
index 0000000000..180e247ea2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListNoContentTest/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getResourceArtifactListNoContentTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getResourceArtifactListNoContentTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/mysql.yml
new file mode 100644
index 0000000000..b8f9bbdc69
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-GetResourceArtifactListTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-GetResourceArtifactListTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactListTest/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataNoContentTest/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataNoContentTest/mysql.yml
new file mode 100644
index 0000000000..72ff4f37e0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataNoContentTest/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getResourceArtifactMetadataNoContentTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getResourceArtifactMetadataNoContentTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/mysql.yml
new file mode 100644
index 0000000000..527e4a0081
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getResourceArtifactMetadataTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getResourceArtifactMetadataTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactMetadataTest/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getResourceArtifactPayloadNoContentTest/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactPayloadNoContentTest/mysql.yml
new file mode 100644
index 0000000000..7177a65387
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getResourceArtifactPayloadNoContentTest/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getResourceArtifactPayloadNoContentTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getResourceArtifactPayloadNoContentTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/mysql.yml
new file mode 100644
index 0000000000..e0a0c6458e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListTest
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListTest:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListTest:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource1/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/mysql.yml
new file mode 100644
index 0000000000..dc5ff158c8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListTest2
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListTest2:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListTest2
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListTest2:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListTest2:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/install_mysql2.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/install_mysql2.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/install_mysql2.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/start_mysql2.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/start_mysql2.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/resource2/scripts/start_mysql2.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topology.txt b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topology.txt
new file mode 100644
index 0000000000..cb3c3e8546
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topology.txt
@@ -0,0 +1 @@
+{"id":"6a4b2f9d-7fe1-482d-af11-97f483dff5b7","delegateId":"9c063349-2259-40fe-97f1-7c40e659e1b0","delegateType":"topologytemplate","dependencies":[{"name":"tosca-normative-types-DBMS","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListTest2","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-softwareComponent","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-compute","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-root","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListTest","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-database","version":"1.0.0.wd03-SNAPSHOT"}],"nodeTemplates":[{"key":"Mysql-getServiceArtifactListTest","value":{"type":"alien.nodes.Mysql-getServiceArtifactListTest","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"hostedOnCompute":{"type":"tosca.relationships.HostedOn","target":"Compute","requirementName":"host","requirementType":"tosca.nodes.Compute","targetedCapabilityName":"host"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListTest","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}},{"key":"Compute","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}},{"key":"Mysql-getServiceArtifactListTest2","value":{"type":"alien.nodes.Mysql-getServiceArtifactListTest2","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"hostedOnCompute":{"type":"tosca.relationships.HostedOn","target":"Compute","requirementName":"host","requirementType":"tosca.nodes.Compute","targetedCapabilityName":"host"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListTest2","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}}]} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topologyTemplate.txt b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topologyTemplate.txt
new file mode 100644
index 0000000000..f0d0849db8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListInvalidVersionNotFoundTest/topologyTemplate.txt
@@ -0,0 +1,2 @@
+{"id":"9c063349-2259-40fe-97f1-7c40e659e1b0","name":"Andrey","description":null,"topologyId":"6a4b2f9d-7fe1-482d-af11-97f483dff5b7"}
+
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/mysql.yml
new file mode 100644
index 0000000000..4ee2c8ca88
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource1/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListNoContentTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListNoContentTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListNoContentTest:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/mysql.yml
new file mode 100644
index 0000000000..b564dd0c4e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/resource2/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListNoContentTest2
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListNoContentTest2:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest2
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest2:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListNoContentTest2:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topology.txt b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topology.txt
new file mode 100644
index 0000000000..279351879a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topology.txt
@@ -0,0 +1 @@
+{"id":"3293c9c8-a162-43fc-b8d1-431399f89cb7","delegateId":"25845cce-05c8-4502-b5fe-abfd6bd6f28e","delegateType":"topologytemplate","dependencies":[{"name":"tosca-normative-types-DBMS","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-softwareComponent","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-compute","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListNoContentTest2","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-root","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-database","version":"1.0.0.wd03-SNAPSHOT"}],"nodeTemplates":[{"key":"Mysql-getServiceArtifactListNoContentTest2","value":{"type":"alien.nodes.Mysql-getServiceArtifactListNoContentTest2","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest2","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}},{"key":"Compute","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":{"dependsOnMysql-getServiceArtifactListNoContentTest2":{"type":"tosca.relationships.DependsOn","target":"Mysql-getServiceArtifactListNoContentTest2","requirementName":"dependency","requirementType":"tosca.capabilities.Root","targetedCapabilityName":"root"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}}]} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topologyTemplate.txt b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topologyTemplate.txt
new file mode 100644
index 0000000000..3c342f6cd1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListNoContentTest/topologyTemplate.txt
@@ -0,0 +1,2 @@
+{"id":"25845cce-05c8-4502-b5fe-abfd6bd6f28e","name":"ServiceArtListNoContent","description":null,"topologyId":"3293c9c8-a162-43fc-b8d1-431399f89cb7"}
+
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/mysql.yml
new file mode 100644
index 0000000000..e0a0c6458e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListTest
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListTest:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListTest:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource1/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/mysql.yml
new file mode 100644
index 0000000000..dc5ff158c8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListTest2
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListTest2:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListTest2
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListTest2:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListTest2:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/install_mysql2.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/install_mysql2.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/install_mysql2.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/start_mysql2.sh b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/start_mysql2.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/resource2/scripts/start_mysql2.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topology.txt b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topology.txt
new file mode 100644
index 0000000000..cb3c3e8546
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topology.txt
@@ -0,0 +1 @@
+{"id":"6a4b2f9d-7fe1-482d-af11-97f483dff5b7","delegateId":"9c063349-2259-40fe-97f1-7c40e659e1b0","delegateType":"topologytemplate","dependencies":[{"name":"tosca-normative-types-DBMS","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListTest2","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-softwareComponent","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-compute","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-root","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListTest","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-database","version":"1.0.0.wd03-SNAPSHOT"}],"nodeTemplates":[{"key":"Mysql-getServiceArtifactListTest","value":{"type":"alien.nodes.Mysql-getServiceArtifactListTest","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"hostedOnCompute":{"type":"tosca.relationships.HostedOn","target":"Compute","requirementName":"host","requirementType":"tosca.nodes.Compute","targetedCapabilityName":"host"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListTest","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}},{"key":"Compute","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}},{"key":"Mysql-getServiceArtifactListTest2","value":{"type":"alien.nodes.Mysql-getServiceArtifactListTest2","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"hostedOnCompute":{"type":"tosca.relationships.HostedOn","target":"Compute","requirementName":"host","requirementType":"tosca.nodes.Compute","targetedCapabilityName":"host"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListTest2","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}}]} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topologyTemplate.txt b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topologyTemplate.txt
new file mode 100644
index 0000000000..f0d0849db8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceArtifactListTest/topologyTemplate.txt
@@ -0,0 +1,2 @@
+{"id":"9c063349-2259-40fe-97f1-7c40e659e1b0","name":"Andrey","description":null,"topologyId":"6a4b2f9d-7fe1-482d-af11-97f483dff5b7"}
+
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/mysql.yml
new file mode 100644
index 0000000000..e0a0c6458e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListTest
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListTest:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListTest:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource1/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/mysql.yml
new file mode 100644
index 0000000000..dc5ff158c8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListTest2
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListTest2:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListTest2
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListTest2:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListTest2:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/install_mysql2.sh b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/install_mysql2.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/install_mysql2.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/start_mysql2.sh b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/start_mysql2.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/resource2/scripts/start_mysql2.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topology.txt b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topology.txt
new file mode 100644
index 0000000000..cb3c3e8546
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topology.txt
@@ -0,0 +1 @@
+{"id":"6a4b2f9d-7fe1-482d-af11-97f483dff5b7","delegateId":"9c063349-2259-40fe-97f1-7c40e659e1b0","delegateType":"topologytemplate","dependencies":[{"name":"tosca-normative-types-DBMS","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListTest2","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-softwareComponent","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-compute","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-root","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListTest","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-database","version":"1.0.0.wd03-SNAPSHOT"}],"nodeTemplates":[{"key":"Mysql-getServiceArtifactListTest","value":{"type":"alien.nodes.Mysql-getServiceArtifactListTest","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"hostedOnCompute":{"type":"tosca.relationships.HostedOn","target":"Compute","requirementName":"host","requirementType":"tosca.nodes.Compute","targetedCapabilityName":"host"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListTest","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}},{"key":"Compute","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}},{"key":"Mysql-getServiceArtifactListTest2","value":{"type":"alien.nodes.Mysql-getServiceArtifactListTest2","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"hostedOnCompute":{"type":"tosca.relationships.HostedOn","target":"Compute","requirementName":"host","requirementType":"tosca.nodes.Compute","targetedCapabilityName":"host"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListTest2","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}}]} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topologyTemplate.txt b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topologyTemplate.txt
new file mode 100644
index 0000000000..f0d0849db8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service1/topologyTemplate.txt
@@ -0,0 +1,2 @@
+{"id":"9c063349-2259-40fe-97f1-7c40e659e1b0","name":"Andrey","description":null,"topologyId":"6a4b2f9d-7fe1-482d-af11-97f483dff5b7"}
+
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/mysql.yml
new file mode 100644
index 0000000000..4ee2c8ca88
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource1/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListNoContentTest
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListNoContentTest:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListNoContentTest:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/mysql.yml b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/mysql.yml
new file mode 100644
index 0000000000..b564dd0c4e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/resource2/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-getServiceArtifactListNoContentTest2
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-getServiceArtifactListNoContentTest2:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest2
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest2:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript-getServiceArtifactListNoContentTest2:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topology.txt b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topology.txt
new file mode 100644
index 0000000000..279351879a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topology.txt
@@ -0,0 +1 @@
+{"id":"3293c9c8-a162-43fc-b8d1-431399f89cb7","delegateId":"25845cce-05c8-4502-b5fe-abfd6bd6f28e","delegateType":"topologytemplate","dependencies":[{"name":"tosca-normative-types-DBMS","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-softwareComponent","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-compute","version":"1.0.0.wd03-SNAPSHOT"},{"name":"mysql-getServiceArtifactListNoContentTest2","version":"1.1.1-SNAPSHOT"},{"name":"tosca-normative-types-root","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-database","version":"1.0.0.wd03-SNAPSHOT"}],"nodeTemplates":[{"key":"Mysql-getServiceArtifactListNoContentTest2","value":{"type":"alien.nodes.Mysql-getServiceArtifactListNoContentTest2","name":null,"properties":{"bind_address":"true","storage_path":"/mountedStorage","db_port":"3306","db_name":"wordpress","db_user":"pass","db_password":"pass"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"database_endpoint":{"type":"tosca.capabilities.DatabaseEndpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.MysqlDatabase-getServiceArtifactListNoContentTest2","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":{"scripts":{"artifactType":"tosca.artifacts.File","artifactRef":"scripts","artifactName":"scripts","artifactRepository":null}}}},{"key":"Compute","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":{"dependsOnMysql-getServiceArtifactListNoContentTest2":{"type":"tosca.relationships.DependsOn","target":"Mysql-getServiceArtifactListNoContentTest2","requirementName":"dependency","requirementType":"tosca.capabilities.Root","targetedCapabilityName":"root"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}}]} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topologyTemplate.txt b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topologyTemplate.txt
new file mode 100644
index 0000000000..3c342f6cd1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/getServiceListTest/Service2/topologyTemplate.txt
@@ -0,0 +1,2 @@
+{"id":"25845cce-05c8-4502-b5fe-abfd6bd6f28e","name":"ServiceArtListNoContent","description":null,"topologyId":"3293c9c8-a162-43fc-b8d1-431399f89cb7"}
+
diff --git a/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDefault.yaml b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDefault.yaml
new file mode 100644
index 0000000000..6aad589bdb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDefault.yaml
@@ -0,0 +1,603 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_missing_default:
+ type: string
+ description: Unique name for this VNF instance
+ label: be4 port 5 OAM ip address
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDesc.yaml b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDesc.yaml
new file mode 100644
index 0000000000..d51a20d77f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingDesc.yaml
@@ -0,0 +1,603 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_missing_desc:
+ type: string
+ default: This_is_the_SCP_name
+ label: be4 port 5 OAM ip address
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingType.yaml b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingType.yaml
new file mode 100644
index 0000000000..29527495f7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithParamsMissingType.yaml
@@ -0,0 +1,603 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_missing_desc:
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ label: be4 port 5 OAM ip address
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithValidParams.yaml b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithValidParams.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatArtifactParameters/heatWithValidParams.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_1.yaml b/asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_1.yaml
new file mode 100644
index 0000000000..7d4a85c2b6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_1.yaml
@@ -0,0 +1,144 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: string
+ description: city name
+ default: Hulon
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+ private_building:
+ type: boolean
+ description: home_number
+ default: true
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_2.yaml b/asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_2.yaml
new file mode 100644
index 0000000000..2c404f0721
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatEnv/artifact_2.yaml
@@ -0,0 +1,469 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+parameters:
+ city_name:
+ type: string
+ description: city name
+ default: Hulon
+ address:
+ type: string
+ description: address
+ default: Narkis
+ home_number:
+ type: number
+ description: home_number
+ default: 14
+ private_building:
+ type: boolean
+ description: home_number
+ default: true
+resources:
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/heatEnv/yuli.yaml b/asdc-tests/src/test/resources/CI/tests/heatEnv/yuli.yaml
new file mode 100644
index 0000000000..7d4a85c2b6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/heatEnv/yuli.yaml
@@ -0,0 +1,144 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: string
+ description: city name
+ default: Hulon
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+ private_building:
+ type: boolean
+ description: home_number
+ default: true
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/BindingAsset.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/BindingAsset.yml
new file mode 100644
index 0000000000..5117247373
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/BindingAsset.yml
@@ -0,0 +1,14 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp:
+ derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - VirtualBinding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo
+ occurrences: [0, UNBOUNDED] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap.yml
new file mode 100644
index 0000000000..8309df24b5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: tosca.nodes.Compute
+ properties:
+ type:
+ type: string
+ required: false \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap_DerivedFromMyCompute1.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap_DerivedFromMyCompute1.yml
new file mode 100644
index 0000000000..478e742bc5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPHasNoReqCap_DerivedFromMyCompute1.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: org.openecomp.resource.MyCompute1
+ properties:
+ type:
+ type: string
+ required: false \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPWithAttributes.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPWithAttributes.yml
new file mode 100644
index 0000000000..afafe7510b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CPWithAttributes.yml
@@ -0,0 +1,59 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false
+ ip_address:
+ type: string
+ required: false
+ description: Allow the user to set a static IP.
+ order:
+ type: integer
+ required: false
+ default: 0
+ description: The order of the NIC on the compute instance (e.g. eth2).
+ is_default:
+ type: boolean
+ required: false
+ default: false
+ description: “If is_default=true this port will be used for the default gateway route. Only one port that is associated to single compute node can set as is_default=true.â€
+ ip_range_start:
+ type: string
+ required: false
+ description: “Defines the starting IP of a range to be allocated for the VFC instances that are associated with this Port.â€
+ ip_range_end:
+ type: string
+ required: false
+ description: “Defines the ending IP of a range to be allocated for the compute instances that are associated with this Port.â€
+ is_tagged:
+ type: boolean
+ required: false
+ default: false
+ description:
+ attributes:
+ private_address:
+ type: string
+ default: "Hello"
+ value: "HelloWord"
+ public_address:
+ default: "DefaultValuePublicAddress"
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - virtualLink:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualbinding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_1.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_1.yml
new file mode 100644
index 0000000000..b9e6c4f9b5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_1.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ capaBility:
+ type: tosca.capabilities.OperatingSystem
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_2.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_2.yml
new file mode 100644
index 0000000000..230e4fa924
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveCapTest_2.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute2:
+ derived_from: org.openecomp.resource.MyCompute1
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ Capability:
+ type: tosca.capabilities.OperatingSystem
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_1.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_1.yml
new file mode 100644
index 0000000000..0d097a8f20
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_1.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - requirEment:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_2.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_2.yml
new file mode 100644
index 0000000000..9cc1f2737b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/CaseInsensitiveReqTest_2.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute2:
+ derived_from: org.openecomp.resource.MyCompute1
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - Requirement:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromCPWithOwnReq.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromCPWithOwnReq.yml
new file mode 100644
index 0000000000..3514acfe9f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromCPWithOwnReq.yml
@@ -0,0 +1,14 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.LAN:
+ derived_from: org.openecomp.resource.cp.CP
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ occurrences: [0, UNBOUNDED] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromWebApplication_HasNoReqType.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromWebApplication_HasNoReqType.yml
new file mode 100644
index 0000000000..7fbf4ec132
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DerivedFromWebApplication_HasNoReqType.yml
@@ -0,0 +1,27 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyChildWebApplication:
+ derived_from: tosca.nodes.WebApplication
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - diff:
+ capability:
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ deff:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentCapFromRoot.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentCapFromRoot.yml
new file mode 100644
index 0000000000..fe79bb3af9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentCapFromRoot.yml
@@ -0,0 +1,26 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyAsset:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - test:
+ capability: tosca.capabilities.Scalable
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqAndCap.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqAndCap.yml
new file mode 100644
index 0000000000..798cd5b9cd
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqAndCap.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - DependencY:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ FeaTurE:
+ type: tosca.capabilities.OperatingSystem \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute1.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute1.yml
new file mode 100644
index 0000000000..c72d0ee62a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute1.yml
@@ -0,0 +1,22 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute2:
+ derived_from: org.openecomp.resource.MyCompute1
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - diff:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute2.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute2.yml
new file mode 100644
index 0000000000..7132ca493a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqCapFromCompute2.yml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute3:
+ derived_from: org.openecomp.resource.MyCompute1
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - diff:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ deff:
+ type: tosca.capabilities.Container
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqFromCompute.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqFromCompute.yml
new file mode 100644
index 0000000000..e9438bae3c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/DifferentReqFromCompute.yml
@@ -0,0 +1,32 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - test:
+ capability: tosca.capabilities.Scalable
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/FatherHasNoReqCap.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/FatherHasNoReqCap.yml
new file mode 100644
index 0000000000..039ab61939
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/FatherHasNoReqCap.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure01.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure01.yml
new file mode 100644
index 0000000000..f20a9eb48f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure01.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_boolean:
+ type: list
+ description : another description
+ default:
+ - false
+ - true
+ entry_schema:
+ description: This is my property
+ type: booolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure02.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure02.yml
new file mode 100644
index 0000000000..f1af89ca6a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure02.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - false
+ - truee
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure03.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure03.yml
new file mode 100644
index 0000000000..974d96ba5b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure03.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - false
+ - 3
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure04.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure04.yml
new file mode 100644
index 0000000000..52377e4b02
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure04.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - false
+ - 3.56
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure05.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure05.yml
new file mode 100644
index 0000000000..c66b4347f6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure05.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10000
+ - 3.56
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure06.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure06.yml
new file mode 100644
index 0000000000..79b3c03cdf
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure06.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10000
+ - aaaa
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure07.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure07.yml
new file mode 100644
index 0000000000..5556e9ddfb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure07.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10000
+ - true
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure08.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure08.yml
new file mode 100644
index 0000000000..a3b21a64a3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure08.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10.50
+ - true
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure09.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure09.yml
new file mode 100644
index 0000000000..dc28591499
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure09.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10.50
+ - asdc
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure10.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure10.yml
new file mode 100644
index 0000000000..e465448064
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure10.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10.50
+ - 500
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure11.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure11.yml
new file mode 100644
index 0000000000..a428b51974
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure11.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10.50
+ - 500.0@
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure12.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure12.yml
new file mode 100644
index 0000000000..d840253120
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure12.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10000
+ - 3#
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure13.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure13.yml
new file mode 100644
index 0000000000..4eb59886e4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure13.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - false
+ - true%
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure14.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure14.yml
new file mode 100644
index 0000000000..ad263f3d00
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure14.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - false
+ - falsee
+ - true
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure15.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure15.yml
new file mode 100644
index 0000000000..93e8caa0a3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure15.yml
@@ -0,0 +1,19 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: list
+ description : another description
+ default:
+ - 10.5
+ - 10.6x
+ - 20.5
+ - 30.5
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure16.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure16.yml
new file mode 100644
index 0000000000..ed8ea4d70c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/ListPropertyFalure16.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_boolean:
+ type: koko
+ description : another description
+ default:
+ - false
+ - true
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure01.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure01.yml
new file mode 100644
index 0000000000..c7ff0743f2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure01.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_boolean:
+ type: map
+ description : another description
+ default:
+ - false
+ - true
+ entry_schema:
+ description: This is my property
+ type: booolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure02.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure02.yml
new file mode 100644
index 0000000000..d9abb87db8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure02.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - false
+ - truee
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure03.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure03.yml
new file mode 100644
index 0000000000..e8f9b6eaa9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure03.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - false
+ - 3
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure04.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure04.yml
new file mode 100644
index 0000000000..d9dc4f955b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure04.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - false
+ - 3.56
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure05.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure05.yml
new file mode 100644
index 0000000000..aba6ff1992
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure05.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10000
+ - 3.56
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure06.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure06.yml
new file mode 100644
index 0000000000..f27904d6e6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure06.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10000
+ - aaaa
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure07.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure07.yml
new file mode 100644
index 0000000000..ea123f3b1b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure07.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10000
+ - true
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure08.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure08.yml
new file mode 100644
index 0000000000..87b51fb2de
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure08.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10.50
+ - true
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure09.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure09.yml
new file mode 100644
index 0000000000..2fc8ded544
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure09.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10.50
+ - asdc
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure10.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure10.yml
new file mode 100644
index 0000000000..3ab449d72e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure10.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10.50
+ - 500
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure11.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure11.yml
new file mode 100644
index 0000000000..e437de822e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure11.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10.50
+ - 500.0@
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure12.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure12.yml
new file mode 100644
index 0000000000..f2fbc7c435
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure12.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10000
+ - 3#
+ entry_schema:
+ description: This is my property
+ type: integer
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure13.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure13.yml
new file mode 100644
index 0000000000..e375aae197
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure13.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - false
+ - true%
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure14.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure14.yml
new file mode 100644
index 0000000000..e087212f1b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure14.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - false
+ - falsee
+ - true
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure15.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure15.yml
new file mode 100644
index 0000000000..3923ee18d7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure15.yml
@@ -0,0 +1,19 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_property:
+ type: map
+ description : another description
+ default:
+ - 10.5
+ - 10.6x
+ - 20.5
+ - 30.5
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure16.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure16.yml
new file mode 100644
index 0000000000..ed8ea4d70c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MapPropertyFalure16.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_boolean:
+ type: koko
+ description : another description
+ default:
+ - false
+ - true
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MyFatherCompute_NoReqCap.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MyFatherCompute_NoReqCap.yml
new file mode 100644
index 0000000000..dfc564b458
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/MyFatherCompute_NoReqCap.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyFatherCompute:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameCapAsCompute.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameCapAsCompute.yml
new file mode 100644
index 0000000000..533333ee09
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameCapAsCompute.yml
@@ -0,0 +1,26 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ OS:
+ type: tosca.capabilities.OperatingSystem
+ BINDING:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute.yml
new file mode 100644
index 0000000000..9d8b2d8375
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - LOCAL_STORAGE:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, 1] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute_DerivedFromMyCompute1.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute_DerivedFromMyCompute1.yml
new file mode 100644
index 0000000000..a5413e516f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/SameReqAsCompute_DerivedFromMyCompute1.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute2:
+ derived_from: org.openecomp.resource.MyCompute1
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - LOCAL_STORAGE:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, 2] \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap11.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap11.yml
new file mode 100644
index 0000000000..9cea0b9dc8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap11.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.vfc3:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ occurrences: [1, 1]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap1UNBOUNDED.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap1UNBOUNDED.yml
new file mode 100644
index 0000000000..24efc27711
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/computeCap1UNBOUNDED.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.vfc2:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, 1]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ occurrences: [1, UNBOUNDED]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromMyCompute.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromMyCompute.yml
new file mode 100644
index 0000000000..237bec1f0c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromMyCompute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.DerivedFromMyCompute:
+ derived_from: org.openecomp.resource.MyCompute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromWebAppDerivedReqCap.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromWebAppDerivedReqCap.yml
new file mode 100644
index 0000000000..0679bff4b1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/derivedFromWebAppDerivedReqCap.yml
@@ -0,0 +1,15 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyWebApp:
+ derived_from: tosca.nodes.WebApplication
+ properties:
+ context_root:
+ type: string
+ capabilities:
+ app_endpoint:
+ type: tosca.capabilities.Endpoint.Admin #derived from WebApplication's tosca.capabilities.Endpoint "app_endpoint"
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container.Docker #derived from WebApplication's tosca.capabilities.Container "host"
+ node: tosca.nodes.WebServer
+ relationship: tosca.relationships.HostedOn
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importAttributeSuccessFlow.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importAttributeSuccessFlow.yml
new file mode 100644
index 0000000000..0fa9a302f2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importAttributeSuccessFlow.yml
@@ -0,0 +1,53 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyComputeTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ myProp:
+ type: tosca.datatypes.Credential
+ descritpion: hey Desc
+ default:
+ "protocol" : hey1
+ "token_type" : hey2
+ "token" : hey3
+ "keys" : {"keyA" : "val1" , keyB : val2}
+ "user" : hey4
+ attributes:
+ private_address:
+ type: string
+ status: supported
+ default: myDefault
+ public_address:
+ type: string
+ networks:
+ type: map
+ default: {keyA : val1 , keyB : val2}
+ entry_schema:
+ type: string
+ ports:
+ type: tosca.datatypes.Credential
+ description: this is my description
+ default:
+ "protocol" : hey1
+ "token_type" : hey2
+ "token" : hey3
+ "keys" : {"keyA" : "val1" , keyB : val2}
+ "user" : hey4
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importCapabilityNameExistsOnParent.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importCapabilityNameExistsOnParent.yml
new file mode 100644
index 0000000000..4d6db6c8c0
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importCapabilityNameExistsOnParent.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ Binding: #"binding" exists on parent
+ type: tosca.capabilities.OperatingSystem
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateCapability.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateCapability.yml
new file mode 100644
index 0000000000..fcc3952f94
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateCapability.yml
@@ -0,0 +1,42 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ - LOCAL_Storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ Scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateRequirements.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateRequirements.yml
new file mode 100644
index 0000000000..7cab3ac590
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importDuplicateRequirements.yml
@@ -0,0 +1,40 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ - LOCAL_Storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyBadDefault.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyBadDefault.yml
new file mode 100644
index 0000000000..a3ff088a35
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyBadDefault.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.TestResource:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_prop:
+ type: list
+ description : another description
+ default:
+ - 12
+ - true
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyGoodDefault.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyGoodDefault.yml
new file mode 100644
index 0000000000..2f864a727e
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertyGoodDefault.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_prop:
+ type: list
+ description : another description
+ default:
+ - false
+ - true
+ entry_schema:
+ description: This is my property
+ type: string
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertySuccessFlow.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertySuccessFlow.yml
new file mode 100644
index 0000000000..fd52df5f9b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importListPropertySuccessFlow.yml
@@ -0,0 +1,198 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ my_boolean:
+ type: list
+ description : another description
+ default:
+ - false
+ - true
+ entry_schema:
+ description: This is my property
+ type: boolean
+ my_boolean_array:
+ type: list
+ description : another description
+ default: [ true , false ]
+ entry_schema:
+ description: This is my property
+ type: boolean
+ duplicate_boolean_values:
+ type: list
+ description : another description
+ default: [ true , false , true ]
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_null_value:
+ type: list
+ description : another description
+ default:
+ - true
+ -
+ - false
+ entry_schema:
+ description: This is my property
+ type: boolean
+ my_integers:
+ type: list
+ description : another description
+ default:
+ - 0
+ - 1000
+ - -1000
+ - 50
+ entry_schema:
+ description: This is my property
+ type: integer
+ my_integers_array:
+ type: list
+ description : another description
+ default: [ 10 , -1000, 0 ]
+ entry_schema:
+ description: This is my property
+ type: integer
+ duplicate_integers_values:
+ type: list
+ description : another description
+ default: [ 10 , 10, -1000, 0 ]
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_null_value:
+ type: list
+ description : another description
+ default:
+ - 1000
+ -
+ - 2000
+ entry_schema:
+ description: This is my property
+ type: integer
+ my_string:
+ type: list
+ description : another description
+ default:
+ - <b>asdc<b>
+ - $?^@ecomp$!#%()_-~@+*^...;;/w#
+ - uc
+ entry_schema:
+ description: This is my property
+ type: string
+ my_string_array:
+ type: list
+ description : another description
+ default: [ <b>AAA</b>, ~$~#bbb%^*_-, qwe , 1.3 , <b>500</b> , true ]
+ entry_schema:
+ description: This is my property
+ type: string
+ string_null_value:
+ type: list
+ description : another description
+ default:
+ - <b>asdc<b>
+ -
+ - uc
+ entry_schema:
+ description: This is my property
+ type: string
+ string_space_value:
+ type: list
+ description : another description
+ default:
+ - <b> asdc<b>
+ -
+ - uc
+ entry_schema:
+ description: This is my property
+ type: string
+ duplicate_string_values:
+ type: list
+ description : another description
+ default:
+ - asdc
+ - asdc
+ - uc
+ entry_schema:
+ description: This is my property
+ type: string
+ my_float:
+ type: list
+ description : another description
+ default:
+ - 6
+ - 1000.000001
+ - -3.0f
+ entry_schema:
+ description: This is my property
+ type: float
+ my_float_array:
+ type: list
+ description : another description
+ default: [ 0.01 , -5.0 , 2.1f ]
+ entry_schema:
+ description: This is my property
+ type: float
+ duplicate_float_values:
+ type: list
+ description : another description
+ default:
+ - 0.0
+ - 0.0
+ - 4.555555
+ entry_schema:
+ description: This is my property
+ type: float
+ float_no_default_values:
+ type: list
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: float
+ float_null_value:
+ type: list
+ description : another description
+ default:
+ - 6
+ -
+ - -3.0f
+ entry_schema:
+ description: This is my property
+ type: float
+ float_space_value:
+ type: list
+ description : another description
+ default:
+ - 6
+ -
+ - -3.0f
+ entry_schema:
+ description: This is my property
+ type: float
+ integer_no_default_values:
+ type: list
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: integer
+ string_no_default_values:
+ type: list
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: string
+ boolean_no_default_values:
+ type: list
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: boolean
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importMapPropertySuccessFlow.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importMapPropertySuccessFlow.yml
new file mode 100644
index 0000000000..f856603397
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importMapPropertySuccessFlow.yml
@@ -0,0 +1,452 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ string_prop01:
+ type: map
+ description : another description
+ default: {keyA : val1 , keyB : val2}
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop02:
+ type: map
+ description : another description
+ default: {keyA : "val1" , keyB : "val2"}
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop03:
+ type: map
+ description : another description
+ default: {"keyA" : "val1" , keyB : val2}
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop04:
+ type: map
+ description : another description
+ default: {"keyA" : 10 , keyB : <b>true</b> }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop05:
+ type: map
+ description : another description
+ default: {"keyA" : , keyB : "Big" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop06:
+ type: map
+ description : another description
+ default: {"keyA" : aaaA , keyB : null }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop07:
+ type: map
+ description : another description
+ default: {"keyA" : NULL , keyB : Null }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop08:
+ type: map
+ description : another description
+ default: {"keyA" : "" , keyB : "abcd" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop09:
+ type: map
+ description : another description
+ default: {"keyA" : " " , keyB : "abcd" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop10:
+ type: map
+ description : another description
+ default: {"keyA" : " aaaa" , keyB : " bbbb" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop11:
+ type: map
+ description : another description
+ default: {"keyA" : "aaaa " , keyB : "bbbb " }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop12:
+ type: map
+ description : another description
+ default: {"keyA" : " aaaa " , keyB : " bbbb ccccc " }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop13:
+ type: map
+ description : another description
+ default:
+ keyA : "aaaa"
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop14:
+ type: map
+ description : another description
+ default:
+ keyA : " aaaa "
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop15:
+ type: map
+ description : another description
+ default:
+ keyA : AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop16:
+ type: map
+ description : another description
+ default:
+ keyA : AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop17:
+ type: map
+ description : another description
+ default:
+ keyA : AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop18:
+ type: map
+ description : another description
+ default:
+ keyA : <b>AbcD</b>
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop19:
+ type: map
+ description : another description
+ default:
+ keyA : <b>AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop20:
+ type: map
+ description : another description
+ default:
+ keyA : aaaa
+ keya : aaaa
+ Keya : Aaaa
+ KEYA : nnnn
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop21:
+ type: map
+ description : another description
+ default:
+ keyA : NULL
+ keyB : null
+ keyC : Null
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop22:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: string
+ integer_prop01:
+ type: map
+ description : another description
+ default: {keyA : 1 , keyB : 1000}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop02:
+ type: map
+ description : another description
+ default: {keyA : Null , keyB : NULL ,keyC : null }
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop03:
+ type: map
+ description : another description
+ default: {keyA : , keyB : -600}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop03:
+ type: map
+ description : another description
+ default: {keyA : 800 , keyB : -600}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop04:
+ type: map
+ description : another description
+ default: {keyA : , keyB : -600}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop05:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyB : 0 }
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop06:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyB : 00}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop07:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyB : 100 }
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop08:
+ type: map
+ description : another description
+ default:
+ keyA : 100
+ keyB : 200
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop09:
+ type: map
+ description : another description
+ default:
+ keyA : 100
+ keyB : 200
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop10:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyA : Null
+ keyB : 1111
+ keyB : 2222
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop11:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyB : Null
+ keyC : NULL
+ keyD :
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop12:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop13:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyA : 200}
+ entry_schema:
+ description: This is my property
+ type: integer
+ boolean_prop01:
+ type: map
+ description : another description
+ default: {keyA : true , keyB : false , keyC : false }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop02:
+ type: map
+ description : another description
+ default: {keyA : TRUE , keyB : FALSE , keyC : False }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop03:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyB : Null
+ keyC : NULL
+ keyD :
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop04:
+ type: map
+ description : another description
+ default: {keyA : Null , keyB : NULL ,keyC : null ,keyD : }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop05:
+ type: map
+ description : another description
+ default: {keyA : true , keyB : false , keyC : false }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop06:
+ type: map
+ description : another description
+ default:
+ keyA : true
+ keyB : true
+ keyC : false
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop07:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop08:
+ type: map
+ description : another description
+ default:
+ keyA : false
+ keyA : true
+ keyB : true
+ keyB : false
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop09:
+ type: map
+ description : another description
+ default: {keyA : true,keyA : false,keyB : false,keyB : true}
+ entry_schema:
+ description: This is my property
+ type: boolean
+ float_prop01:
+ type: map
+ description : another description
+ default: {keyA : 1.20 , keyB : 3.56f , keyC : 33}
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop02:
+ type: map
+ description : another description
+ default: {keyA : 0.00, keyB : 0.0 , keyC : 0 }
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop03:
+ type: map
+ description : another description
+ default: {keyA : null, keyB : Null , keyC : NULL , keyD : }
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop04:
+ type: map
+ description : another description
+ default: {keyA : 1.20 , keyB : 3.56f , keyC : 33 }
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop05:
+ type: map
+ description : another description
+ default:
+ keyA : 33
+ keyB : 1.2000
+ keyC : 3.607f
+ keyD : 0
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop06:
+ type: map
+ description : another description
+ default:
+ keyA : 33
+ keyB : 1.2000
+ keyC : 3.607f
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop07:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyB : Null
+ keyC : NULL
+ keyD :
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop08:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop09:
+ type: map
+ description : another description
+ default:
+ keyA : 3.5
+ keyA : 0.01
+ keyB : 3.6
+ keyB :
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop10:
+ type: map
+ description : another description
+ default: {keyA : 0.0002}
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop11:
+ type: map
+ description : another description
+ default: {keyA : 0.000 , keyA : 003.56f, keyB : 33}
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent.yml
new file mode 100644
index 0000000000..e4d626fca6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - Local_Storage:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent_DerivedFromMyCompute1.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent_DerivedFromMyCompute1.yml
new file mode 100644
index 0000000000..7ae9552d6a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/importRequirementNameExistsOnParent_DerivedFromMyCompute1.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute2:
+ derived_from: org.openecomp.resource.MyCompute1
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - Local_Storage:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInCapDefinition.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInCapDefinition.yml
new file mode 100644
index 0000000000..301116c985
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInCapDefinition.yml
@@ -0,0 +1,37 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+description: Template for vSCP -- connectable to OCS-FW -- temporary model for 1602 demo
+
+############################
+# The model capture four sub-components (VDUs) and their connectivity to six
+# networks.
+############################
+
+node_types:
+
+#The node type for vSCP
+#used for substitution mapping, or to describe vSCP resource in ASDC studio
+ org.openecomp.resource.vSCP-03-16:
+ derived_from: tosca.nodes.Root
+ requirements:
+ - sigtran_connection1:
+ capability: tosca.capabilities.Node
+ - sigtran_connection2:
+ capability: tosca.capabilities.Node
+ - ocs_connection:
+ capability: tosca.capabilities.Node
+ - oam_connection:
+ capability: tosca.capabilities.Node
+ - firewall:
+ capability: tosca.capabilities.Node
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: org.openecomp.capabilities.networkInterfaceNotFound \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInReqDefinition.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInReqDefinition.yml
new file mode 100644
index 0000000000..d100dafdf9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/missingCapInReqDefinition.yml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+description: Template for vSCP -- connectable to OCS-FW -- temporary model for 1602 demo
+
+############################
+# The model capture four sub-components (VDUs) and their connectivity to six
+# networks.
+############################
+
+node_types:
+
+#The node type for vSCP
+#used for substitution mapping, or to describe vSCP resource in ASDC studio
+ org.openecomp.resource.vSCP-03-16:
+ derived_from: tosca.nodes.Root
+ requirements:
+ - sigtran_connection1:
+ capability: org.openecomp.capabilities.networkInterfaceNotFound
+ - sigtran_connection2:
+ capability: tosca.capabilities.Node
+ - ocs_connection:
+ capability: tosca.capabilities.Node
+ - oam_connection:
+ capability: tosca.capabilities.Node
+ - firewall:
+ capability: tosca.capabilities.Node \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildCompute_NoReqCap.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildCompute_NoReqCap.yml
new file mode 100644
index 0000000000..cc5b202bc7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildCompute_NoReqCap.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyChildCompute:
+ derived_from: org.openecomp.resource.MyFatherCompute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildWebApp_DerivedFromContainer.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildWebApp_DerivedFromContainer.yml
new file mode 100644
index 0000000000..b7859d4c14
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myChildWebApp_DerivedFromContainer.yml
@@ -0,0 +1,15 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyChildWebApp:
+ derived_from: org.openecomp.resource.MyWebApp
+ properties:
+ context_root:
+ type: string
+ capabilities:
+ app_endpoint:
+ type: tosca.capabilities.Endpoint
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.WebServer
+ relationship: tosca.relationships.HostedOn \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myCompute.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myCompute.yml
new file mode 100644
index 0000000000..98bf9b7902
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myCompute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeDerivedFromNotExists.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeDerivedFromNotExists.yml
new file mode 100644
index 0000000000..2b2807395a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeDerivedFromNotExists.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.RootNotExists
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectDefenitionVersionValue.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectDefenitionVersionValue.yml
new file mode 100644
index 0000000000..2fa3ad0c08
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectDefenitionVersionValue.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: toscaSimpleYaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectNameSpaceFormat.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectNameSpaceFormat.yml
new file mode 100644
index 0000000000..d1a613fff2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeIncorrectNameSpaceFormat.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource2.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeNoDefenitionVersion.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeNoDefenitionVersion.yml
new file mode 100644
index 0000000000..8084d049db
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeNoDefenitionVersion.yml
@@ -0,0 +1,34 @@
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeOccurencySuccess.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeOccurencySuccess.yml
new file mode 100644
index 0000000000..40ae610c06
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeOccurencySuccess.yml
@@ -0,0 +1,77 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, UNBOUNDED]
+ - local_storage200:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, 1]
+ - local_storage300:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, 10]
+ - local_storage400:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, 10000000]
+ - local_storage500:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [2, 3]
+ - local_storageNoOccurrences600:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpointNoOccurrence :
+ type: tosca.capabilities.Endpoint.Admin
+ endpoint200 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [1, 2]
+ endpoint300 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [1, 1]
+ endpoint400 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [1, 10]
+ endpoint500 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [1, 10000000]
+ endpoint600 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [1, UNBOUNDED ]
+ endpoint700 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [2, 4 ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeParssingFalure.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeParssingFalure.yml
new file mode 100644
index 0000000000..b243add14a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeParssingFalure.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requiremens:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilitis:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeReqNameExistsOnDerived.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeReqNameExistsOnDerived.yml
new file mode 100644
index 0000000000..43d92d1681
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeReqNameExistsOnDerived.yml
@@ -0,0 +1,34 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute1:
+ derived_from: tosca.nodes.Compute
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - Local_Storage: #"local_storage" exists on parent
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeVF.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeVF.yml
new file mode 100644
index 0000000000..90e771dab1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeVF.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.VF.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithNodeTypesTwice.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithNodeTypesTwice.yml
new file mode 100644
index 0000000000..73b201eab4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithNodeTypesTwice.yml
@@ -0,0 +1,42 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
+
+ org.openecomp.resource.example.TransactionSubsystem:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ message_receiver: tosca.capabilities.Endpoint
+ requirements:
+ - database_endpoint: tosca.capabilities.Endpoint.Database \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithTopologyTemplate.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithTopologyTemplate.yml
new file mode 100644
index 0000000000..c451eb2514
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myComputeWithTopologyTemplate.yml
@@ -0,0 +1,44 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+topology_template:
+ description: Template of an application connecting to a database.
+
+ node_templates:
+ web_app:
+ type: tosca.nodes.WebApplication.MyWebApp
+ requirements:
+ - host: web_server
+ - database_endpoint: db
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myFatherWebApp_derviedFromDocker.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myFatherWebApp_derviedFromDocker.yml
new file mode 100644
index 0000000000..43a9a174f8
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myFatherWebApp_derviedFromDocker.yml
@@ -0,0 +1,16 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyWebApp:
+ derived_from: tosca.nodes.WebApplication
+ properties:
+ context_root:
+ type: string
+ capabilities:
+ app_endpoint:
+ type: tosca.capabilities.Endpoint
+ requirements:
+ - HOSt:
+ capability: tosca.capabilities.Container.Docker
+ node: tosca.nodes.WebServer
+ relationship: tosca.relationships.HostedOn
+ occurrences: [ 1, 2 ]
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myLinkVL.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myLinkVL.yml
new file mode 100644
index 0000000000..7c796d2801
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myLinkVL.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.MyLink:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myPortCP.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myPortCP.yml
new file mode 100644
index 0000000000..ff255f2394
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/myPortCP.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.cp.MyPort:
+ derived_from: org.openecomp.resource.cp.Port
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.network.Bindable
+ node: org.openecomp.resource.vl.MyLink
+ relationship: tosca.relationships.BindTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/noContent.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/noContent.yml
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/noContent.yml
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure01.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure01.yml
new file mode 100644
index 0000000000..f007a0fed2
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure01.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [2, 0]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure02.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure02.yml
new file mode 100644
index 0000000000..0487b63cb3
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure02.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [-1, 2]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure03.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure03.yml
new file mode 100644
index 0000000000..7afdffa59d
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure03.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [1, -2]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure04.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure04.yml
new file mode 100644
index 0000000000..f8a582f401
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure04.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ , 2]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure05.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure05.yml
new file mode 100644
index 0000000000..f1c6a93e1f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure05.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ 1, ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure06.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure06.yml
new file mode 100644
index 0000000000..b0ef54b0eb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure06.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ 0 , 0 ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure07.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure07.yml
new file mode 100644
index 0000000000..afd999f575
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure07.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ @ , 1 ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure08.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure08.yml
new file mode 100644
index 0000000000..60efc99273
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure08.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ 1.0 , 2.0 ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure09.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure09.yml
new file mode 100644
index 0000000000..d6ec7eabe6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure09.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ "1" , "2" ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure10.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure10.yml
new file mode 100644
index 0000000000..27de429186
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure10.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure11.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure11.yml
new file mode 100644
index 0000000000..5bdae3a5f5
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure11.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [ UNBOUNDED , UNBOUNDED ]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure31.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure31.yml
new file mode 100644
index 0000000000..8c3034afce
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure31.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ 2, 1]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure32.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure32.yml
new file mode 100644
index 0000000000..4b5eba9110
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure32.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ -1, 2]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure33.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure33.yml
new file mode 100644
index 0000000000..9c42e22aa1
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure33.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ 1, -2]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure34.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure34.yml
new file mode 100644
index 0000000000..49953b5834
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure34.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ , 2]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure35.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure35.yml
new file mode 100644
index 0000000000..8d8985d7f7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure35.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ 1 , ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure36.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure36.yml
new file mode 100644
index 0000000000..12944507f6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure36.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ 0 , 0 ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure37.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure37.yml
new file mode 100644
index 0000000000..618c980e16
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure37.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ 0 , # ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure38.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure38.yml
new file mode 100644
index 0000000000..d0c4575c83
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure38.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ 1.0 , 2.0 ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure39.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure39.yml
new file mode 100644
index 0000000000..024338d380
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure39.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ "1" , "2" ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure40.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure40.yml
new file mode 100644
index 0000000000..a4a32ba772
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure40.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure41.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure41.yml
new file mode 100644
index 0000000000..c3024a3ee4
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/occurencyFalure41.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage100:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint100 :
+ type: tosca.capabilities.Endpoint.Admin
+ occurrences: [ UNBOUNDED, UNBOUNDED ]
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq11.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq11.yml
new file mode 100644
index 0000000000..b9bda183e6
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq11.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.vfc4:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 1]
diff --git a/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq12.yml b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq12.yml
new file mode 100644
index 0000000000..9676bcdb6c
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/importToscaResourceByCreateUrl/softwareComponentReq12.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.vfc1:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ occurrences: [1, 2]
diff --git a/asdc-tests/src/test/resources/CI/tests/testCsarAPI/topology.txt b/asdc-tests/src/test/resources/CI/tests/testCsarAPI/topology.txt
new file mode 100644
index 0000000000..f46af26605
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/testCsarAPI/topology.txt
@@ -0,0 +1 @@
+{"id":"c25811fc-e03f-401d-93ca-45d9bd312703","delegateId":"17710a88-f3d8-483d-aded-afee2906a8c1","delegateType":"topologytemplate","dependencies":[{"name":"tosca-normative-types-softwareComponent","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-webServer","version":"1.0.0.wd03-SNAPSHOT"},{"name":"apache-type","version":"2.0.0-SNAPSHOT"},{"name":"tosca-normative-types-compute","version":"1.0.0.wd03-SNAPSHOT"},{"name":"tosca-normative-types-root","version":"1.0.0.wd03-SNAPSHOT"}],"nodeTemplates":[{"key":"Compute_2","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}},{"key":"Apache","value":{"type":"alien.nodes.Apache","name":null,"properties":{"port":"80","document_root":"/var/www","version":"2.4"},"attributes":{"tosca_id":null,"tosca_name":null},"relationships":{"attachToCompute_2":{"type":"tosca.relationships.AttachTo","target":"Compute_2","requirementName":"dependency","requirementType":"tosca.capabilities.Root","targetedCapabilityName":"attach"}},"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"host":{"type":"tosca.nodes.Compute","properties":null}},"capabilities":{"secure_endpoint":{"type":"tosca.capabilities.Endpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"app_endpoint":{"type":"tosca.capabilities.Endpoint","properties":{"port":null,"protocol":{"value":"tcp","definition":false},"url_path":null,"secure":{"value":"false","definition":false}}},"host":{"type":"alien.capabilities.ApacheContainer","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null}},"artifacts":null}},{"key":"Compute","value":{"type":"tosca.nodes.Compute","name":null,"properties":{"disk_size":null,"num_cpus":null,"os_distribution":null,"os_arch":null,"mem_size":null,"os_type":null,"os_version":null},"attributes":{"ip_address":null,"tosca_id":null,"tosca_name":null},"relationships":null,"requirements":{"dependency":{"type":"tosca.capabilities.Root","properties":null},"network":{"type":"tosca.capabilities.Connectivity","properties":null}},"capabilities":{"host":{"type":"tosca.capabilities.Container","properties":{"valid_node_types":null}},"root":{"type":"tosca.capabilities.Root","properties":null},"attach":{"type":"tosca.capabilities.Attachment","properties":null},"scalable":{"type":"tosca.capabilities.Scalable","properties":{"max_intances":{"value":"1","definition":false},"default_instances":{"value":"1","definition":false},"min_intances":{"value":"1","definition":false}}}},"artifacts":null}}]} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/testCsarAPI/topologyTemplate.txt b/asdc-tests/src/test/resources/CI/tests/testCsarAPI/topologyTemplate.txt
new file mode 100644
index 0000000000..f3fdf0297b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/testCsarAPI/topologyTemplate.txt
@@ -0,0 +1 @@
+{"id":"17710a88-f3d8-483d-aded-afee2906a8c1","name":"MichaelTest2","description":null,"topologyId":"c25811fc-e03f-401d-93ca-45d9bd312703"} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool10_false.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool10_false.yaml
new file mode 100644
index 0000000000..b6faadd4f9
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool10_false.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: NO
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool11_false.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool11_false.yaml
new file mode 100644
index 0000000000..7fb2923b04
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool11_false.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: 0
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool12_false.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool12_false.yaml
new file mode 100644
index 0000000000..fd451aa3af
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool12_false.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: OFF
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool1_true.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool1_true.yaml
new file mode 100644
index 0000000000..a253e4c8d7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool1_true.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: t
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool2_true.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool2_true.yaml
new file mode 100644
index 0000000000..ce273b3522
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool2_true.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: true
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool3_true.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool3_true.yaml
new file mode 100644
index 0000000000..4680eb6996
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool3_true.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: on
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool4_true.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool4_true.yaml
new file mode 100644
index 0000000000..1ed7781831
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool4_true.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: y
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool5_true.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool5_true.yaml
new file mode 100644
index 0000000000..c0b42e6288
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool5_true.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: yes
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool6_true.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool6_true.yaml
new file mode 100644
index 0000000000..24b862b751
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool6_true.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: 1
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool7_false.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool7_false.yaml
new file mode 100644
index 0000000000..7bad8efd6f
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool7_false.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: F
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool8_false.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool8_false.yaml
new file mode 100644
index 0000000000..8b35e2adf7
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool8_false.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: FALSE
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool9_false.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool9_false.yaml
new file mode 100644
index 0000000000..2768e0d480
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_bool9_false.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: N
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number1.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number1.yaml
new file mode 100644
index 0000000000..fc7f84ca81
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number1.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: number
+ description: city name
+ default: 12
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number2.yaml b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number2.yaml
new file mode 100644
index 0000000000..28ec3358be
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/tmp/positive_artifact_number2.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: number
+ description: city name
+ default: 12.12
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/uploadComponent/images/mysql.png b/asdc-tests/src/test/resources/CI/tests/uploadComponent/images/mysql.png
new file mode 100644
index 0000000000..8e02f49b7b
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/uploadComponent/images/mysql.png
Binary files differ
diff --git a/asdc-tests/src/test/resources/CI/tests/uploadComponent/mysql.yml b/asdc-tests/src/test/resources/CI/tests/uploadComponent/mysql.yml
new file mode 100644
index 0000000000..a2eb4d423a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/uploadComponent/mysql.yml
@@ -0,0 +1,85 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-uploadComponent
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-database:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql-uploadComponent:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/install_mysql.sh b/asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/install_mysql.sh
new file mode 100644
index 0000000000..400bcf40cb
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/install_mysql.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+echo "Debian based MYSQL install 5..."
+LOCK="/tmp/lockaptget"
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "MySQL take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+sudo rm -f /var/lib/dpkg/lock
+
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/mysql stop
+sudo rm -rf /var/lib/apt/lists/*
+sudo rm -rf /var/lib/mysql/*
+echo "MySQL Installation complete." \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/start_mysql.sh b/asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/start_mysql.sh
new file mode 100644
index 0000000000..648bd45756
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/uploadComponent/scripts/start_mysql.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+echo "------------------------ ENV ------------------------"
+echo "ENV VAR USED VOLUME_HOME : $VOLUME_HOME"
+echo "ENV VAR USED PORT : $PORT"
+echo "ENV VAR USED DB_NAME : $DB_NAME"
+echo "ENV VAR USED DB_USER : $DB_USER"
+echo "ENV VAR USED DB_PASSWORD : $DB_PASSWORD"
+echo "---------------------------- ------------------------"
+
+CURRENT_PATH=`dirname "$0"`
+
+function StartMySQL {
+ echo "Starting MYSQL..."
+ sudo /etc/init.d/mysql stop
+ sudo /usr/bin/mysqld_safe > /dev/null 2>&1 &
+ RET=1
+ while [[ RET -ne 0 ]]; do
+ echo "=> Waiting for confirmation of MySQL service startup"
+ sleep 5
+ sudo mysql -uroot -e "status" > /dev/null 2>&1
+ RET=$?
+ done
+}
+
+function AllowFileSystemToMySQL {
+ MYSQL_DATA_DIR=$VOLUME_HOME/data
+ MYSQL_LOG=$VOLUME_HOME/logs
+
+ echo "Setting data directory to $MYSQL_DATA_DIR an logs to $MYSQL_LOG ..."
+ if sudo test ! -d $MYSQL_DATA_DIR; then
+ echo "Creating DATA dir > $MYSQL_DATA_DIR ..."
+ sudo mkdir -p $MYSQL_DATA_DIR
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_DATA_DIR
+ fi
+ if sudo test ! -d $MYSQL_LOG; then
+ echo "Creating LOG dir > $MYSQL_LOG ..."
+ sudo mkdir -p $MYSQL_LOG
+ # mysql as owner and group owner
+ sudo chown -R mysql:mysql $MYSQL_LOG
+ fi
+
+ # edit app mysql permission in : /etc/apparmor.d/usr.sbin.mysqld
+ COUNT_LINE=`sudo cat /etc/apparmor.d/usr.sbin.mysqld | wc -l`
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_DATA_DIR/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/ r," /etc/apparmor.d/usr.sbin.mysqld
+ sudo sed -i "$(($COUNT_LINE)) i $MYSQL_LOG/** rwk," /etc/apparmor.d/usr.sbin.mysqld
+
+ # reload app permission manager service
+ sudo service apparmor reload
+}
+
+function UpdateMySQLConf {
+ echo "Updating MySQL conf files [DATA, LOGS]..."
+ sudo sed -i "s:/var/lib/mysql:$MYSQL_DATA_DIR:g" /etc/mysql/my.cnf
+ sudo sed -i "s:/var/log/mysql/error.log:$MYSQL_LOG/error.log:g" /etc/mysql/my.cnf
+ sudo sed -i "s:3306:$PORT:g" /etc/mysql/my.cnf
+
+ if sudo test ! -f /usr/share/mysql/my-default.cnf; then
+ sudo cp /etc/mysql/my.cnf /usr/share/mysql/my-default.cnf
+ fi
+ if sudo test ! -f /etc/mysql/conf.d/mysqld_charset.cnf; then
+ sudo cp $configs/mysqld_charset.cnf /etc/mysql/conf.d/mysqld_charset.cnf
+ fi
+
+ if [ "$BIND_ADRESS" == "true" ]; then
+ sudo sed -i "s/bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
+ fi
+}
+
+function InitMySQLDb {
+ # create database DB_NAME
+ if [ "$DB_NAME" ]; then
+ echo "INIT DATABASE $DB_NAME"
+ sudo mysql -u root -e "CREATE DATABASE $DB_NAME";
+ fi
+
+ # create user and give rights
+ if [ "$DB_USER" ]; then
+ echo "CREATE USER $DB_USER WITH PASSWORD $DB_PASSWORD AND GRAND RIGHTS ON $DB_NAME"
+ sudo mysql -uroot -e "CREATE USER '${DB_USER}'@'%' IDENTIFIED BY '$DB_PASSWORD'"
+ sudo mysql -uroot -e "GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'%' WITH GRANT OPTION"
+ sudo mysql -uroot -e "FLUSH PRIVILEGES"
+ fi
+}
+
+# Create a new database path to the attched volume
+if sudo test ! -d $VOLUME_HOME/data; then
+ echo "=> An empty or uninitialized MySQL volume is detected in $VOLUME_HOME/data"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+ echo "=> Init new database path to $MYSQL_DATA_DIR"
+ sudo mysql_install_db --basedir=/usr --datadir=$MYSQL_DATA_DIR
+ echo "=> MySQL database initialized !"
+else
+ echo "=> Using an existing volume of MySQL"
+ AllowFileSystemToMySQL
+ UpdateMySQLConf
+fi
+
+# Finally start MySQL with new configuration
+StartMySQL
+InitMySQLDb \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/artifact_unsupported.yaml b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/artifact_unsupported.yaml
new file mode 100644
index 0000000000..764b8d4c58
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/artifact_unsupported.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: number123
+ description: city name
+ default: 12.12
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool1.yaml b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool1.yaml
new file mode 100644
index 0000000000..19119f3d9a
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool1.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: K
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool2.yaml b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool2.yaml
new file mode 100644
index 0000000000..f9c09a4fed
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_bool2.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: boolean
+ description: city name
+ default: 11
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number1.yaml b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number1.yaml
new file mode 100644
index 0000000000..9a489879cc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number1.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: number
+ description: city name
+ default: 1.2Noo
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number2.yaml b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number2.yaml
new file mode 100644
index 0000000000..4c0e07affc
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_number2.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: number
+ description: city name
+ default: 1 2 3
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_string1.yaml b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_string1.yaml
new file mode 100644
index 0000000000..f52ef04712
--- /dev/null
+++ b/asdc-tests/src/test/resources/CI/tests/yamlFieldsValidation/negative_artifact_string1.yaml
@@ -0,0 +1,140 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+ city_name:
+ type: string
+ description: city name
+ default: שלו×
+ address:
+ type: string
+ description: address
+ default: Alonim
+ home_number:
+ type: number
+ description: home_number
+ default: 8
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
diff --git a/asdc-tests/src/test/resources/config.json b/asdc-tests/src/test/resources/config.json
new file mode 100644
index 0000000000..71c9d35e59
--- /dev/null
+++ b/asdc-tests/src/test/resources/config.json
@@ -0,0 +1,12 @@
+{
+
+catalogBeHost: behost,
+catalogFeHost: fehost,
+esHost: eshost,
+catalogFePort: 8080,
+catalogBePort: 8080,
+esPort: 9200,
+resourceConfigDir: "src/test/resources/CI/tests",
+componentsConfigDir: "src/test/resources/CI/components"
+
+} \ No newline at end of file
diff --git a/asdc-tests/src/test/resources/logback-test.xml b/asdc-tests/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..03ce9d1243
--- /dev/null
+++ b/asdc-tests/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+<!-- only one line, shut up logback ! -->
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>
+ %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+ </Pattern>
+ </encoder>
+ </appender>
+ <root level="off">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration> \ No newline at end of file
diff --git a/asdc-tests/tarball.xml b/asdc-tests/tarball.xml
new file mode 100644
index 0000000000..18bc00854f
--- /dev/null
+++ b/asdc-tests/tarball.xml
@@ -0,0 +1,71 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar</format>
+ </formats>
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>${project.artifactId}-${project.version}-jar-with-dependencies.jar</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/scripts/startTest.sh</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>startTest.sh</destName>
+ </file>
+ <!--file>
+ <source>src/main/resources/ci/scripts/postinstall</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>postinstall</destName>
+ </file-->
+ <file>
+ <source>src/main/resources/ci/conf/sdc.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>sdc.yaml</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/conf/sdc-packages.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>sdc-packages.yaml</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/conf/log4j.properties</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>log4j.properties</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/conf/titan.properties</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>titan.properties</destName>
+ </file>
+ <file>
+ <source>${project.basedir}/../catalog-be/src/main/resources/config/error-configuration.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>error-configuration.yaml</destName>
+ </file>
+ <file>
+ <source>${project.basedir}/../catalog-be/src/main/resources/config/configuration.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>configuration.yaml</destName>
+ </file>
+
+
+
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <directory>src/test/resources</directory>
+ <outputDirectory>./</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/ci/testSuites</directory>
+ <outputDirectory>./testSuites</outputDirectory>
+ </fileSet>
+
+ </fileSets>
+
+</assembly>
diff --git a/asdc-tests/testng.xml b/asdc-tests/testng.xml
new file mode 100644
index 0000000000..8a47ef6d62
--- /dev/null
+++ b/asdc-tests/testng.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Suite" >
+ <listeners>
+ <listener class-name="org.openecomp.sdc.ci.tests.run.ExtentReporterNG" />
+ </listeners>
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.category.CategoriesTests"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- Suite -->
+
+
+
diff --git a/asdc-tests/testngLifeCycle.xml b/asdc-tests/testngLifeCycle.xml
new file mode 100644
index 0000000000..aa390dc213
--- /dev/null
+++ b/asdc-tests/testngLifeCycle.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="lifeCycle" parallel="none">
+ <test name="Test">
+ <classes>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_cerificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceResourceLCSTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_UndoCheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_UndoCheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_StartCertificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_CheckinCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_CheckOutCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceCertWithResourceInstances"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CrossCheckOutTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CertifyVFWithNotCertRIs"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_request4CerCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CleanupIntermediateReources"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_CheckOutCIT"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_cerificationCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.CertifyServiceWithNotCertRI"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCS_CheckInCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_request4CerCITest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest"/>
+ <class name="org.openecomp.sdc.ci.tests.execute.lifecycle.ServiceLCS_StartCertificationCITest"/>
+ </classes>
+ </test> <!-- Test -->
+</suite> <!-- lifeCycle -->
diff --git a/asdctool/.gitignore b/asdctool/.gitignore
new file mode 100644
index 0000000000..72f53d03cf
--- /dev/null
+++ b/asdctool/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+/build/
+/target/
diff --git a/asdctool/.pydevproject b/asdctool/.pydevproject
new file mode 100644
index 0000000000..40e9f40a0a
--- /dev/null
+++ b/asdctool/.pydevproject
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?><pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+</pydev_project>
diff --git a/asdctool/pom.xml b/asdctool/pom.xml
new file mode 100644
index 0000000000..0166c88c2f
--- /dev/null
+++ b/asdctool/pom.xml
@@ -0,0 +1,434 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>sdctool</artifactId>
+ <packaging>jar</packaging>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <dependencies>
+ <!-- ASDC dependencies -->
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>common-be</artifactId>
+ <version>${common-be.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-dao</artifactId>
+ <version>${catalog-dao.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-model</artifactId>
+ <version>${catalog-model.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>catalog-be</artifactId>
+ <version>${asdc.full.version}</version>
+
+ <!-- Comment Out in order to debug in eclipse -->
+ <classifier>classes</classifier>
+
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- ASDC dependencies end -->
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>9.2.10.v20150310</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- listen to file changes -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <version>9.2.10.v20150310</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.core</groupId>
+ <artifactId>jersey-server</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-jetty-http</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-moxy</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-multipart</artifactId>
+ <version>2.14</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- slf4j + logback -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- groovy -->
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-configuration2</artifactId>
+ <version>2.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>tinkergraph-gremlin</artifactId>
+ <version>3.0.1-incubating</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>gremlin-groovy</artifactId>
+ <version>3.0.1-incubating</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.tinkerpop.blueprints</groupId>
+ <artifactId>blueprints-sail-graph</artifactId>
+ <version>2.5.0</version>
+ <optional>true</optional>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.tinkerpop.blueprints</groupId>
+ <artifactId>blueprints-graph-sail</artifactId>
+ <version>2.6.0</version>
+ <optional>true</optional>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- TITAN end -->
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>${jackson.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>${jackson.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- Spring 4 dependencies -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>4.0.7.RELEASE</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- Spring 4 dependencies end -->
+ <!-- JavaConfig need this library -->
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ <version>3.2.4</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.elasticsearch</groupId>
+ <artifactId>elasticsearch</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <version>${jackson.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.elasticsearch.plugin</groupId>
+ <artifactId>shield</artifactId>
+ <version>${elastic-search.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- CASSANDRA -->
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-core</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-mapping</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- CASSANDRA END -->
+ <dependency>
+ <groupId>org.apache.poi</groupId>
+ <artifactId>com.springsource.org.apache.poi</artifactId>
+ <version>3.9.0.FINAL</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jdom</groupId>
+ <artifactId>jdom</artifactId>
+ <version>2.0.2</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>3.0.2</version>
+ </dependency>
+ <!-- Temporary, till building the populate task which adding all components
+ to cache. We will use Serialization Utils. -->
+ <dependency>
+ <groupId>de.ruedigermoeller</groupId>
+ <artifactId>fst</artifactId>
+ <version>2.47</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- testing end -->
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.5.5</version>
+ <executions>
+ <execution>
+ <id>create.jar.with.dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <configuration>
+ <finalName>${project.artifactId}</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptor>${project.basedir}/tarball.xml</descriptor>
+ </configuration>
+ <id>assemble-file</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>3.0.2</version>
+
+ <executions>
+ <execution>
+ <id>copy-sdctool-cassandra</id>
+ <phase>install</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.parent.basedir}/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/files/default</outputDirectory>
+ <resources>
+ <resource>
+ <directory>./target</directory>
+ <includes>
+ <include>sdctool.tar</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/Utils.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/Utils.java
new file mode 100644
index 0000000000..3b7d3ec11d
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/Utils.java
@@ -0,0 +1,152 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+
+//import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+
+//import org.openecomp.sdc.be.info.errors.ResponseFormat;
+
+public class Utils {
+
+ private static Logger log = LoggerFactory.getLogger(Utils.class.getName());
+
+ public static String NEW_LINE = System.getProperty("line.separator");
+
+ public static Response buildOkResponse(
+ /*
+ * ResponseFormat errorResponseWrapper,
+ */int status, Object entity, Map<String, String> additionalHeaders) {
+ // int status = errorResponseWrapper.getStatus();
+ ResponseBuilder responseBuilder = Response.status(status);
+ if (entity != null) {
+ log.trace("returned entity is {}", entity.toString());
+ responseBuilder = responseBuilder.entity(entity);
+ }
+ if (additionalHeaders != null) {
+ for (Entry<String, String> additionalHeader : additionalHeaders.entrySet()) {
+ String headerName = additionalHeader.getKey();
+ String headerValue = additionalHeader.getValue();
+ log.trace("Adding header {} with value {} to the response", headerName, headerValue);
+ responseBuilder.header(headerName, headerValue);
+ }
+ }
+ return responseBuilder.build();
+ }
+
+ public static TitanGraph openGraph(Configuration conf) {
+
+ TitanGraph graph = null;
+ try {
+
+ graph = TitanFactory.open(conf);
+
+ } catch (Exception e) {
+ log.error("Failed to start open graph", e);
+ }
+
+ return graph;
+
+ }
+
+ public static boolean vertexLeftContainsRightProps(Map<String, Object> leftProps, Map<String, Object> rightProps) {
+
+ if (rightProps != null) {
+
+ for (Entry<String, Object> entry : rightProps.entrySet()) {
+ String key = entry.getKey();
+ Object leftValue = leftProps.get(key);
+ Object rightValue = entry.getValue();
+ if (leftValue == null) {
+ if (rightValue == null) {
+ continue;
+ } else {
+ log.debug("The key {} cannot be found in the properties {}", key, leftProps);
+ return false;
+ }
+ }
+
+ // if (false == leftValue instanceof Map && false == leftValue
+ // instanceof List) {
+ if (false == leftValue.equals(rightValue)) {
+ log.trace("The value of key {} is different between properties {} vs {}", key, leftValue, rightValue);
+ return false;
+ }
+ // }
+ }
+
+ }
+
+ return true;
+ }
+
+ public static void setProperties(Element element, Map<String, Object> properties) {
+
+ if (properties != null && false == properties.isEmpty()) {
+
+ Object[] propertyKeyValues = new Object[properties.size() * 2];
+ int i = 0;
+ for (Entry<String, Object> entry : properties.entrySet()) {
+ propertyKeyValues[i++] = entry.getKey();
+ propertyKeyValues[i++] = entry.getValue();
+ }
+
+ ElementHelper.attachProperties(element, propertyKeyValues);
+
+ }
+
+ }
+
+ public static Map<String, Object> getProperties(Element element) {
+
+ Map<String, Object> result = new HashMap<String, Object>();
+ ;
+
+ if (element.keys() != null && element.keys().size() > 0) {
+ Map<String, Property> propertyMap = ElementHelper.propertyMap(element,
+ element.keys().toArray(new String[element.keys().size()]));
+
+ for (Entry<String, Property> entry : propertyMap.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue().value();
+
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/DataMigration.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/DataMigration.java
new file mode 100644
index 0000000000..a78ea9bc18
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/DataMigration.java
@@ -0,0 +1,830 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import com.carrotsearch.hppc.cursors.ObjectCursor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import fj.data.Either;
+import org.apache.commons.lang.SystemUtils;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.elasticsearch.common.collect.ImmutableOpenMap;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.dao.cassandra.schema.Table;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.data.auditing.*;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * Created by mlando on 5/16/2016.
+ */
+public class DataMigration {
+
+ private Gson gson = new Gson();
+
+ private ObjectMapper jsonMapper = new ObjectMapper();
+
+ private static Logger log = LoggerFactory.getLogger(DataMigration.class.getName());
+
+ protected ElasticSearchClient elasticSearchClient;
+ @Autowired
+ protected AuditCassandraDao auditCassandraDao;
+ @Autowired
+ protected ArtifactCassandraDao artifactCassandraDao;
+ private static final String dateFormatPattern = "yyyy-MM-dd HH:mm:ss.SSS z";
+ private static SimpleDateFormat simpleDateFormat;
+
+ /**
+ * the method exports and imports the records from ES to cassandra the flow
+ * will check to see if the files are not empty if the files are not empty
+ * the export will be skiped and the flow will use the existing files. the
+ * flow will check if the tables in cassandra are empty, if the tables are
+ * not empty the proces will stop and exit. if the tables are empty the
+ * method will import the records from the files. in case of a fail the flow
+ * will exit and clear all the Cassandra tables.
+ *
+ * @param appConfigDir
+ * the location of the dir in wich the output files will be
+ * stored
+ * @param exportFromEs
+ * should the es be exported again and overwrite the old export
+ * @param importToCassandra
+ * should we import the data into cassandra
+ * @return true in case the operation was successful.
+ */
+ public boolean migrateDataESToCassndra(String appConfigDir, boolean exportFromEs, boolean importToCassandra) {
+ initFormater();
+ if (!initEsClient())
+ return false;
+ Map<Table, File> files = createOutPutFiles(appConfigDir, exportFromEs);
+ if (files == null) {
+ return false;
+ }
+ if (exportFromEs && filesEmpty(files)) {
+ Map<Table, PrintWriter> printerWritersMap = createWriters(files);
+ if (printerWritersMap == null) {
+ return false;
+ }
+ try {
+ ImmutableOpenMap<String, IndexMetaData> indexData = getIndexData();
+ for (ObjectCursor<String> key : indexData.keys()) {
+ if ("resources".equalsIgnoreCase(key.value)) {
+ if (!exportArtifacts(key.value, printerWritersMap)) {
+ return false;
+ }
+ } else if (key.value.startsWith("auditingevents")) {
+ if (!exportAudit(key.value, printerWritersMap)) {
+ return false;
+ }
+ }
+ }
+ } finally {
+ if (elasticSearchClient != null) {
+ elasticSearchClient.close();
+ }
+ for (PrintWriter writer : printerWritersMap.values()) {
+ writer.close();
+ }
+ }
+ }
+ if (importToCassandra && !importToCassndra(files)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void initFormater() {
+ simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ private boolean initEsClient() {
+ String configHome = System.getProperty("config.home");
+ URL url = null;
+ Settings settings = null;
+ try {
+ if (SystemUtils.IS_OS_WINDOWS) {
+ url = new URL("file:///" + configHome + "/elasticsearch.yml");
+ } else {
+ url = new URL("file:" + configHome + "/elasticsearch.yml");
+ }
+ log.debug("URL {}", url);
+ settings = Settings.settingsBuilder().loadFromPath(Paths.get(url.toURI())).build();
+ } catch (MalformedURLException | URISyntaxException e1) {
+ log.error("Failed to create URL in order to load elasticsearch yml", e1);
+ return true;
+ }
+
+ this.elasticSearchClient = new ElasticSearchClient();
+ this.elasticSearchClient.setClusterName(settings.get("cluster.name"));
+ this.elasticSearchClient.setLocal(settings.get("elasticSearch.local"));
+ this.elasticSearchClient.setTransportClient(settings.get("elasticSearch.transportclient"));
+ try {
+ elasticSearchClient.initialize();
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * the method clears all the cassandra tables
+ */
+ private void truncateCassandraTable() {
+ log.info("import failed. truncating Cassandra tables.");
+ artifactCassandraDao.deleteAllArtifacts();
+ auditCassandraDao.deleteAllAudit();
+ }
+
+ /**
+ * the method imports the records from the files into cassandra
+ *
+ * @param files
+ * a map of files holding
+ * @return true if the operation was successful
+ */
+ private boolean importToCassndra(Map<Table, File> files) {
+ log.info("starting to import date into Cassandra.");
+ if (!validtaTablsNotEmpty(files))
+ return true;
+ for (Table table : files.keySet()) {
+ log.info("importing recordes into {}", table.getTableDescription().getTableName());
+ if (!handleImport(files, table)) {
+ truncateCassandraTable();
+ return false;
+ }
+ }
+ log.info("finished to import date into Cassandra.");
+ return true;
+ }
+
+ private boolean validtaTablsNotEmpty(Map<Table, File> files) {
+ for (Table table : files.keySet()) {
+ Either<Boolean, CassandraOperationStatus> isTableEmptyRes = checkIfTableIsEmpty(table);
+ if (isTableEmptyRes.isRight() || !isTableEmptyRes.left().value()) {
+ log.error("Cassandra table {} is not empty operation aborted.",
+ table.getTableDescription().getTableName());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * the method retrieves the fields from the given map and praprs them for
+ * storage as an audit according to the table name
+ *
+ * @param map
+ * the map from which we will retrive the fields enum values
+ * @param table
+ * the table we are going to store the record in.
+ * @return a enummap representing the audit record that is going to be
+ * created.
+ */
+ private EnumMap<AuditingFieldsKeysEnum, Object> createAuditMap(Map<String, String> map, Table table) {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<>(AuditingFieldsKeysEnum.class);
+ switch (table) {
+ case USER_ADMIN_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_AFTER, map.get("USER_AFTER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_BEFORE, map.get("USER_BEFORE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ break;
+ case USER_ACCESS_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_UID, map.get("USER"));
+ break;
+ case RESOURCE_ADMIN_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, map.get("INVARIANT_UUID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, map.get("CURR_VERSION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, map.get("CURR_STATE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, map.get("DID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION, map.get("PREV_VERSION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE, map.get("PREV_STATE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, map.get("RESOURCE_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, map.get("RESOURCE_TYPE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS, map.get("DPREV_STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS, map.get("DCURR_STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE, map.get("TOSCA_NODE_TYPE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, map.get("COMMENT"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA, map.get("ARTIFACT_DATA"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID, map.get("PREV_ARTIFACT_UUID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID, map.get("CURR_ARTIFACT_UUID"));
+ break;
+ case DISTRIBUTION_DOWNLOAD_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, map.get("RESOURCE_URL"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, map.get("CONSUMER_ID"));
+ break;
+ case DISTRIBUTION_ENGINE_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ if (map.get("TOPIC_NAME") != null) {
+ if (map.get("TOPIC_NAME").contains("-STATUS-")) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME,
+ map.get("TOPIC_NAME"));
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME,
+ map.get("TOPIC_NAME"));
+ }
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME,
+ map.get("DSTATUS_TOPIC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME,
+ map.get("DNOTIF_TOPIC"));
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, map.get("TOPIC_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ROLE, map.get("ROLE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY, map.get("API_KEY"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME, map.get("D_ENV"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, map.get("CONSUMER_ID"));
+ break;
+ case DISTRIBUTION_NOTIFICATION_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, map.get("CURR_STATE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, map.get("CURR_VERSION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, map.get("DID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, map.get("RESOURCE_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, map.get("RESOURCE_TYPE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, map.get("TOPIC_NAME"));
+ break;
+ case DISTRIBUTION_STATUS_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, map.get("RESOURCE_URL"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, map.get("DID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, map.get("TOPIC_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, map.get("CONSUMER_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME, map.get("STATUS_TIME"));
+ break;
+ case DISTRIBUTION_DEPLOY_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, map.get("DID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, map.get("RESOURCE_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, map.get("RESOURCE_TYPE"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, map.get("CURR_VERSION"));
+ break;
+ case DISTRIBUTION_GET_UEB_CLUSTER_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ if (map.get("STATUS_DESC") != null) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("STATUS_DESC"));
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, map.get("CONSUMER_ID"));
+ break;
+ case AUTH_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_USER, map.get("USER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_URL, map.get("URL"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_STATUS, map.get("AUTH_STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_REALM, map.get("REALM"));
+ break;
+ case CONSUMER_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ECOMP_USER, map.get("ECOMP_USER"));
+ break;
+ case CATEGORY_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, map.get("SERVICE_INSTANCE_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_CATEGORY_NAME, map.get("CATEGORY_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SUB_CATEGORY_NAME, map.get("SUB_CATEGORY_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_GROUPING_NAME, map.get("GROUPING_NAME"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, map.get("RESOURCE_TYPE"));
+ break;
+ case GET_USERS_LIST_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DETAILS, map.get("DETAILS"));
+ break;
+ case GET_CATEGORY_HIERARCHY_EVENT:
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, map.get("TIMESTAMP"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, map.get("ACTION"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, map.get("DESC"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, map.get("STATUS"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, map.get("MODIFIER"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, map.get("REQUEST_ID"));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DETAILS, map.get("DETAILS"));
+ break;
+ default:
+ auditingFields = null;
+ break;
+ }
+ return auditingFields;
+ }
+
+ /**
+ * the method reads the content of the file intended for a given table, and
+ * sores them in cassandra
+ *
+ * @param files
+ * a map of files from which the recordes will be retrieved.
+ * @param table
+ * the name of the table we want to look up in the files and sore
+ * in Cassandra // * @param store the function to call when
+ * storing recordes in cassndra
+ * @return true if the operation was successful
+ */
+ private boolean handleImport(Map<Table, File> files, Table table) {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(files.get(table)));
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ CassandraOperationStatus res = null;
+ if (Table.ARTIFACT.equals(table)) {
+ res = artifactCassandraDao.saveArtifact(jsonMapper.readValue(line, ESArtifactData.class));
+ } else {
+ Type type = new TypeToken<Map<String, String>>() {
+ }.getType();
+ Map<String, String> map = gson.fromJson(line, type);
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = createAuditMap(map, table);
+ AuditingGenericEvent recordForCassandra = null;
+ try {
+ recordForCassandra = createAuditRecord(auditingFields);
+ } catch (ParseException e) {
+ log.error("filed to parse time stemp in recored {}", auditingFields);
+ return false;
+ }
+
+ res = auditCassandraDao.saveRecord(recordForCassandra);
+ }
+ if (!res.equals(CassandraOperationStatus.OK)) {
+ log.error("save recored to cassndra {} failed with status {} aborting.",
+ table.getTableDescription().getTableName(), res);
+ return false;
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ log.error("failed to read file", e);
+ return false;
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException e) {
+ log.error("failed to close file reader", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * the method checks if the given table is empty
+ *
+ * @param table
+ * the name of the table we want to check
+ * @return true if the table is empty
+ */
+ private Either<Boolean, CassandraOperationStatus> checkIfTableIsEmpty(Table table) {
+ if (Table.ARTIFACT.equals(table)) {
+ return artifactCassandraDao.isTableEmpty(table.getTableDescription().getTableName());
+ } else {
+ return auditCassandraDao.isTableEmpty(table.getTableDescription().getTableName());
+ }
+ }
+
+ private boolean filesEmpty(Map<Table, File> files) {
+ for (Table table : files.keySet()) {
+ File file = files.get(table);
+ if (file.length() != 0) {
+ log.info("file:{} is not empty skipping export", table.getTableDescription().getTableName());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * the method reads the records from es index of audit's into a file as
+ * json's.
+ *
+ * @param value
+ * the name of the index we want
+ * @param printerWritersMap
+ * a map of the writers we use to write to a file.
+ * @return true in case the export was successful.
+ */
+ private boolean exportAudit(String value, Map<Table, PrintWriter> printerWritersMap) {
+ log.info("stratng to export audit data from es index{} to file.", value);
+ QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
+ SearchResponse scrollResp = elasticSearchClient.getClient().prepareSearch(value).setScroll(new TimeValue(60000))
+ .setQuery(queryBuilder).setSize(100).execute().actionGet();
+ while (true) {
+ for (SearchHit hit : scrollResp.getHits().getHits()) {
+ PrintWriter out = printerWritersMap.get(TypeToTableMapping.getTableByType(hit.getType()));
+ out.println(hit.getSourceAsString());
+ }
+ scrollResp = elasticSearchClient.getClient().prepareSearchScroll(scrollResp.getScrollId())
+ .setScroll(new TimeValue(60000)).execute().actionGet();
+ if (scrollResp.getHits().getHits().length == 0) {
+ break;
+
+ }
+ }
+
+ log.info("export audit data from es to file. finished succsesfully");
+ return true;
+ }
+
+ /**
+ * the method reads the records from es index of resources into a file as
+ * json's.
+ *
+ * @param index
+ * the name of the index we want to read
+ * @param printerWritersMap
+ * a map of the writers we use to write to a file.
+ * @return true in case the export was successful.
+ */
+ private boolean exportArtifacts(String index, Map<Table, PrintWriter> printerWritersMap) {
+ log.info("stratng to export artifact data from es to file.");
+ PrintWriter out = printerWritersMap.get(Table.ARTIFACT);
+ QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
+ SearchResponse scrollResp = elasticSearchClient.getClient().prepareSearch(index).setScroll(new TimeValue(60000))
+ .setQuery(queryBuilder).setSize(100).execute().actionGet();
+ while (true) {
+ for (SearchHit hit : scrollResp.getHits().getHits()) {
+ ;
+ out.println(hit.getSourceAsString());
+ }
+ scrollResp = elasticSearchClient.getClient().prepareSearchScroll(scrollResp.getScrollId())
+ .setScroll(new TimeValue(60000)).execute().actionGet();
+ if (scrollResp.getHits().getHits().length == 0) {
+ break;
+
+ }
+ }
+
+ log.info("export artifact data from es to file. finished succsesfully");
+ return true;
+ }
+
+ /**
+ * the method retrieves all the indexes from elasticsearch
+ *
+ * @return a map of indexes and there metadata
+ */
+ private ImmutableOpenMap<String, IndexMetaData> getIndexData() {
+ return elasticSearchClient.getClient().admin().cluster().prepareState().get().getState().getMetaData()
+ .getIndices();
+ }
+
+ /**
+ * the method creates all the files and dir which holds them. in case the
+ * files exist they will not be created again.
+ *
+ * @param appConfigDir
+ * the base path under which the output dir will be created and
+ * the export result files the created filesa are named according
+ * to the name of the table into which it will be imported.
+ * @param exportToEs
+ * if true all the export files will be recreated
+ * @returnthe returns a map of tables and the files representing them them
+ */
+ private Map<Table, File> createOutPutFiles(String appConfigDir, boolean exportToEs) {
+ Map<Table, File> result = new EnumMap<Table, File>(Table.class);
+ File outputDir = new File(appConfigDir + "/output/");
+ if (!createOutPutFolder(outputDir)) {
+ return null;
+ }
+ for (Table table : Table.values()) {
+ File file = new File(outputDir + "/" + table.getTableDescription().getTableName());
+ if (exportToEs) {
+ try {
+ if (file.exists()) {
+ Files.delete(file.toPath());
+ }
+ } catch (IOException e) {
+ log.error("failed to delete output file " + file.getAbsolutePath(), e);
+ return null;
+ }
+ file = new File(outputDir + "/" + table.getTableDescription().getTableName());
+ }
+ if (!file.exists()) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ log.error("failed to create output file " + file.getAbsolutePath(), e);
+ return null;
+ }
+ }
+ result.put(table, file);
+
+ }
+ return result;
+ }
+
+ /**
+ * the method create the writers to each file
+ *
+ * @param files
+ * a map of the files according to table
+ * @return returns a map of writers according to table.
+ */
+ private Map<Table, PrintWriter> createWriters(Map<Table, File> files) {
+ Map<Table, PrintWriter> printerWritersMap = new EnumMap<>(Table.class);
+ try {
+ for (Table table : files.keySet()) {
+ log.info("creating writer for {}", table);
+ File file = files.get(table);
+ FileWriter fw = new FileWriter(file, true);
+ BufferedWriter bw = new BufferedWriter(fw);
+ PrintWriter out = new PrintWriter(bw);
+ printerWritersMap.put(table, out);
+ log.info("creating writer for {} was successful", table);
+ }
+ } catch (IOException e) {
+ log.error("create writer to file failed", e);
+ return null;
+ }
+ return printerWritersMap;
+ }
+
+ /**
+ * the method creates the output dir in case it does not exist
+ *
+ * @param outputDir
+ * the path under wich the directory will be created.
+ * @return true in case the create was succsesful or the dir already exists
+ */
+ private boolean createOutPutFolder(File outputDir) {
+ if (!outputDir.exists()) {
+ log.info("creating output dir" + outputDir.getAbsolutePath());
+ try {
+ Files.createDirectories(outputDir.toPath());
+ } catch (IOException e) {
+ log.error("failed to create output dir" + outputDir.getAbsolutePath(), e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public enum TypeToTableMapping {
+ USER_ADMIN_EVENT_TYPE(AuditingTypesConstants.USER_ADMIN_EVENT_TYPE,
+ Table.USER_ADMIN_EVENT), USER_ACCESS_EVENT_TYPE(AuditingTypesConstants.USER_ACCESS_EVENT_TYPE,
+ Table.USER_ACCESS_EVENT), RESOURCE_ADMIN_EVENT_TYPE(
+ AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE,
+ Table.RESOURCE_ADMIN_EVENT), DISTRIBUTION_DOWNLOAD_EVENT_TYPE(
+ AuditingTypesConstants.DISTRIBUTION_DOWNLOAD_EVENT_TYPE,
+ Table.DISTRIBUTION_DOWNLOAD_EVENT), DISTRIBUTION_ENGINE_EVENT_TYPE(
+ AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE,
+ Table.DISTRIBUTION_ENGINE_EVENT), DISTRIBUTION_NOTIFICATION_EVENT_TYPE(
+ AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE,
+ Table.DISTRIBUTION_NOTIFICATION_EVENT), DISTRIBUTION_STATUS_EVENT_TYPE(
+ AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE,
+ Table.DISTRIBUTION_STATUS_EVENT), DISTRIBUTION_DEPLOY_EVENT_TYPE(
+ AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE,
+ Table.DISTRIBUTION_DEPLOY_EVENT), DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE(
+ AuditingTypesConstants.DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE,
+ Table.DISTRIBUTION_GET_UEB_CLUSTER_EVENT), AUTH_EVENT_TYPE(
+ AuditingTypesConstants.AUTH_EVENT_TYPE,
+ Table.AUTH_EVENT), CONSUMER_EVENT_TYPE(
+ AuditingTypesConstants.CONSUMER_EVENT_TYPE,
+ Table.CONSUMER_EVENT), CATEGORY_EVENT_TYPE(
+ AuditingTypesConstants.CATEGORY_EVENT_TYPE,
+ Table.CATEGORY_EVENT), GET_USERS_LIST_EVENT_TYPE(
+ AuditingTypesConstants.GET_USERS_LIST_EVENT_TYPE,
+ Table.GET_USERS_LIST_EVENT), GET_CATEGORY_HIERARCHY_EVENT_TYPE(
+ AuditingTypesConstants.GET_CATEGORY_HIERARCHY_EVENT_TYPE,
+ Table.GET_CATEGORY_HIERARCHY_EVENT);
+
+ String typeName;
+ Table table;
+
+ TypeToTableMapping(String typeName, Table table) {
+ this.typeName = typeName;
+ this.table = table;
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ public Table getTable() {
+ return table;
+ }
+
+ public static Table getTableByType(String type) {
+ for (TypeToTableMapping mapping : TypeToTableMapping.values()) {
+ if (mapping.getTypeName().equalsIgnoreCase(type)) {
+ return mapping.getTable();
+ }
+ }
+ return null;
+ }
+ }
+
+ public static AuditingGenericEvent createAuditRecord(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields)
+ throws ParseException {
+ AuditingActionEnum actionEnum = AuditingActionEnum
+ .getActionByName((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION));
+ String tableName = actionEnum.getAuditingEsType();
+ AuditingGenericEvent event = null;
+ Date date = null;
+ switch (tableName) {
+ case AuditingTypesConstants.USER_ADMIN_EVENT_TYPE:
+ UserAdminEvent userAdminEvent = new UserAdminEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ userAdminEvent.setTimestamp1(date);
+ event = userAdminEvent;
+ break;
+ case AuditingTypesConstants.AUTH_EVENT_TYPE:
+ AuthEvent authEvent = new AuthEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ authEvent.setTimestamp1(date);
+ event = authEvent;
+ break;
+ case AuditingTypesConstants.CATEGORY_EVENT_TYPE:
+ CategoryEvent categoryEvent = new CategoryEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ categoryEvent.setTimestamp1(date);
+ event = categoryEvent;
+ break;
+ case AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE:
+ ResourceAdminEvent resourceAdminEvent = new ResourceAdminEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ resourceAdminEvent.setTimestamp1(date);
+ event = resourceAdminEvent;
+ break;
+ case AuditingTypesConstants.USER_ACCESS_EVENT_TYPE:
+ event = new UserAccessEvent(auditingFields);
+ UserAccessEvent userAccessEvent = new UserAccessEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ userAccessEvent.setTimestamp1(date);
+ event = userAccessEvent;
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE:
+ DistributionStatusEvent distributionStatusEvent = new DistributionStatusEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ distributionStatusEvent.setTimestamp1(date);
+ event = distributionStatusEvent;
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_DOWNLOAD_EVENT_TYPE:
+ DistributionDownloadEvent distributionDownloadEvent = new DistributionDownloadEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ distributionDownloadEvent.setTimestamp1(date);
+ event = distributionDownloadEvent;
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE:
+ DistributionEngineEvent distributionEngineEvent = new DistributionEngineEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ distributionEngineEvent.setTimestamp1(date);
+ event = distributionEngineEvent;
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE:
+ DistributionNotificationEvent distributionNotificationEvent = new DistributionNotificationEvent(
+ auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ distributionNotificationEvent.setTimestamp1(date);
+ event = distributionNotificationEvent;
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE:
+ DistributionDeployEvent distributionDeployEvent = new DistributionDeployEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ distributionDeployEvent.setTimestamp1(date);
+ event = distributionDeployEvent;
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE:
+ AuditingGetUebClusterEvent auditingGetUebClusterEvent = new AuditingGetUebClusterEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ auditingGetUebClusterEvent.setTimestamp1(date);
+ event = auditingGetUebClusterEvent;
+ break;
+ case AuditingTypesConstants.CONSUMER_EVENT_TYPE:
+ ConsumerEvent consumerEvent = new ConsumerEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ consumerEvent.setTimestamp1(date);
+ event = consumerEvent;
+ break;
+ case AuditingTypesConstants.GET_USERS_LIST_EVENT_TYPE:
+ GetUsersListEvent getUsersListEvent = new GetUsersListEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ getUsersListEvent.setTimestamp1(date);
+ event = getUsersListEvent;
+ break;
+ case AuditingTypesConstants.GET_CATEGORY_HIERARCHY_EVENT_TYPE:
+ GetCategoryHierarchyEvent getCategoryHierarchyEvent = new GetCategoryHierarchyEvent(auditingFields);
+ date = simpleDateFormat.parse((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP));
+ getCategoryHierarchyEvent.setTimestamp1(date);
+ event = getCategoryHierarchyEvent;
+ break;
+
+ }
+ return event;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/EsToCassandraDataMigrationConfig.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/EsToCassandraDataMigrationConfig.java
new file mode 100644
index 0000000000..2c0471fb17
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/EsToCassandraDataMigrationConfig.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class EsToCassandraDataMigrationConfig {
+ @Bean(name = "DataMigrationBean")
+ public DataMigration dataMigration() {
+ return new DataMigration();
+ }
+
+ @Bean(name = "artifact-cassandra-dao")
+ public ArtifactCassandraDao artifactCassandraDao() {
+ return new ArtifactCassandraDao();
+ }
+
+ @Bean(name = "audit-cassandra-dao")
+ public AuditCassandraDao auditCassandraDao() {
+ return new AuditCassandraDao();
+ }
+
+ @Bean(name = "cassandra-client")
+ public CassandraClient cassandraClient() {
+ return new CassandraClient();
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLConverter.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLConverter.java
new file mode 100644
index 0000000000..bf62072235
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLConverter.java
@@ -0,0 +1,694 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.IoCore;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.openecomp.sdc.asdctool.Utils;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanGraphQuery;
+import com.thinkaurelius.titan.core.TitanVertex;
+//import com.tinkerpop.blueprints.Direction;
+//import com.tinkerpop.blueprints.Edge;
+//import com.tinkerpop.blueprints.Vertex;
+//import com.tinkerpop.blueprints.util.ElementHelper;
+//import com.tinkerpop.blueprints.util.io.graphson.GraphSONReader;
+//import com.tinkerpop.blueprints.util.io.graphson.GraphSONWriter;
+
+public class GraphMLConverter {
+
+ private static Logger log = LoggerFactory.getLogger(GraphMLConverter.class.getName());
+
+ private Gson gson = new Gson();
+
+ public boolean importGraph(String[] args) {
+
+ TitanGraph graph = null;
+ try {
+ String titanFileLocation = args[1];
+ String inputFile = args[2];
+ graph = openGraph(titanFileLocation);
+
+ List<ImmutablePair<String, String>> propertiesCriteriaToDelete = new ArrayList<>();
+ ImmutablePair<String, String> immutablePair1 = new ImmutablePair<String, String>("healthcheckis", "GOOD");
+ ImmutablePair<String, String> immutablePair2 = new ImmutablePair<String, String>("nodeLabel", "user");
+ ImmutablePair<String, String> immutablePair3 = new ImmutablePair<String, String>("nodeLabel",
+ "resourceCategory");
+ ImmutablePair<String, String> immutablePair4 = new ImmutablePair<String, String>("nodeLabel",
+ "serviceCategory");
+
+ propertiesCriteriaToDelete.add(immutablePair1);
+ propertiesCriteriaToDelete.add(immutablePair2);
+ propertiesCriteriaToDelete.add(immutablePair3);
+ propertiesCriteriaToDelete.add(immutablePair4);
+
+ boolean result = importJsonGraph(graph, inputFile, propertiesCriteriaToDelete);
+
+ return result;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ if (graph != null) {
+ // graph.shutdown();
+ graph.close();
+ }
+ }
+
+ }
+
+ public boolean exportGraph(String[] args) {
+
+ TitanGraph graph = null;
+ try {
+ String titanFileLocation = args[1];
+ String outputDirectory = args[2];
+ graph = openGraph(titanFileLocation);
+
+ String result = exportJsonGraph(graph, outputDirectory);
+
+ if (result == null) {
+ return false;
+ }
+
+ System.out.println("Exported file=" + result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ if (graph != null) {
+ // graph.shutdown();
+ graph.close();
+ }
+ }
+
+ return true;
+ }
+
+ public String exportGraphMl(String[] args) {
+
+ TitanGraph graph = null;
+ String result = null;
+ try {
+ String titanFileLocation = args[1];
+ String outputDirectory = args[2];
+ graph = openGraph(titanFileLocation);
+
+ result = exportGraphMl(graph, outputDirectory);
+
+ System.out.println("Exported file=" + result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ if (graph != null) {
+ graph.close();
+ }
+ }
+
+ return result;
+ }
+
+ public boolean findErrorInJsonGraph(String[] args) {
+
+ TitanGraph graph = null;
+ try {
+ String titanFileLocation = args[1];
+ String outputDirectory = args[2];
+ graph = openGraph(titanFileLocation);
+
+ String result = findErrorInJsonGraph(graph, outputDirectory);
+
+ if (result == null) {
+ return false;
+ }
+
+ System.out.println("Exported file=" + result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ if (graph != null) {
+ // graph.shutdown();
+ graph.close();
+ }
+ }
+
+ return true;
+ }
+
+ public TitanGraph openGraph(String titanFileLocation) {
+
+ TitanGraph graph = TitanFactory.open(titanFileLocation);
+
+ return graph;
+
+ }
+
+ public String exportJsonGraph(TitanGraph graph, String outputDirectory) {
+
+ String result = null;
+
+ // GraphMLWriter graphMLWriter = new GraphMLWriter(graph);
+
+ String outputFile = outputDirectory + File.separator + "exportGraph." + System.currentTimeMillis() + ".json";
+
+ OutputStream out = null;
+ try {
+ out = new BufferedOutputStream(new FileOutputStream(outputFile));
+
+ // GraphSONWriter.outputGraph(graph, outputFile);
+ final GraphSONWriter.Builder builder = GraphSONWriter.build();
+ final GraphSONMapper mapper = newGraphSONMapper(graph);
+ builder.mapper(mapper);
+ final GraphSONWriter writer = builder.create();
+ writer.writeGraph(out, graph);
+
+ // GraphSONWriter create = GraphSONWriter.build(). create();
+ // create.writeGraph(out, graph);
+
+ // graph.commit();
+ graph.tx().commit();
+
+ result = outputFile;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ graph.tx().rollback();
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+
+ }
+
+ public String exportGraphMl(TitanGraph graph, String outputDirectory) {
+ String result = null;
+ String outputFile = outputDirectory + File.separator + "exportGraph." + System.currentTimeMillis() + ".graphml";
+ try {
+ try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile))) {
+ graph.io(IoCore.graphml()).writer().normalize(true).create().writeGraph(os, graph);
+ }
+ result = outputFile;
+ graph.tx().commit();
+ } catch (Exception e) {
+ graph.tx().rollback();
+ e.printStackTrace();
+ }
+ return result;
+
+ }
+
+ private static GraphSONMapper newGraphSONMapper(final Graph graph) {
+ final GraphSONMapper.Builder builder = graph.io(IoCore.graphson()).mapper();
+ // Different failure with embedded type info.
+ // builder.embedTypes(true);
+ return builder.create();
+ }
+
+ public boolean importJsonGraph(TitanGraph graph, String graphJsonFile,
+ List<ImmutablePair<String, String>> propertiesCriteriaToDelete) {
+
+ boolean result = false;
+
+ InputStream is = null;
+
+ try {
+
+ if (propertiesCriteriaToDelete != null) {
+ for (Entry<String, String> entry : propertiesCriteriaToDelete
+
+ ) {
+
+ String key = entry.getKey();
+ String value = entry.getValue();
+ Iterator iterator = graph.query().has(key, value).vertices().iterator();
+ while (iterator.hasNext()) {
+ Vertex vertex = (Vertex) iterator.next();
+ vertex.remove();
+ System.out.println("Remove vertex of type " + key + " and value " + value);
+ }
+
+ }
+ }
+ File file = new File(graphJsonFile);
+ if (false == file.isFile()) {
+ System.out.println("File " + graphJsonFile + " cannot be found.");
+ return result;
+ }
+
+ is = new BufferedInputStream(new FileInputStream(graphJsonFile));
+ System.out.println("Before importing file " + graphJsonFile);
+
+ // GraphSONReader.inputGraph(graph, graphJsonFile);
+ GraphSONReader create = GraphSONReader.build().create();
+ create.readGraph(is, graph);
+
+ // graph.commit();
+ graph.tx().commit();
+
+ result = true;
+
+ } catch (Exception e) {
+ System.out.println("Failed to import graph " + e.getMessage());
+ e.printStackTrace();
+ // graph.rollback();
+ graph.tx().rollback();
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return result;
+
+ }
+
+ public String findErrorInJsonGraph(TitanGraph graph, String outputDirectory) {
+
+ boolean runVertexScan = false;
+ boolean runEdgeScan = false;
+
+ String result = null;
+
+ // GraphMLWriter graphMLWriter = new GraphMLWriter(graph);
+
+ String outputFile = outputDirectory + File.separator + "exportGraph." + System.currentTimeMillis() + ".json";
+
+ OutputStream out = null;
+ try {
+ out = new BufferedOutputStream(new FileOutputStream(outputFile));
+
+ if (runEdgeScan) {
+
+ Vertex vertexFrom = null;
+ Vertex vertexTo = null;
+ Edge edge = null;
+
+ // Iterable<Edge> edges = graph.getEdges();
+ // Iterable<Edge> edges = graph.query().edges();
+ Iterable<TitanEdge> edges = graph.query().edges();
+ // Iterator<Edge> iterator = edges.iterator();
+ Iterator<TitanEdge> iterator = edges.iterator();
+ while (iterator.hasNext()) {
+
+ try {
+
+ edge = iterator.next();
+
+ // vertexFrom = edge.getVertex(Direction.OUT);
+ // vertexTo = edge.getVertex(Direction.IN);
+ vertexFrom = edge.outVertex();
+ vertexTo = edge.inVertex();
+
+ BaseConfiguration conf = new BaseConfiguration();
+ conf.setProperty("storage.backend", "inmemory");
+ TitanGraph openGraph = Utils.openGraph(conf);
+
+ TitanVertex addVertexFrom = openGraph.addVertex();
+ // ElementHelper.setProperties(addVertexFrom,
+ // ElementHelper.getProperties(vertexFrom));
+ Utils.setProperties(addVertexFrom, Utils.getProperties(vertexFrom));
+
+ TitanVertex addVertexTo = openGraph.addVertex();
+ // ElementHelper.setProperties(addVertexTo,
+ // ElementHelper.getProperties(vertexTo));
+ Utils.setProperties(addVertexTo, Utils.getProperties(vertexTo));
+
+ // Edge addEdge = openGraph.addEdge(null, addVertexFrom,
+ // addVertexTo, edge.getLabel());
+
+ // Edge edge = tGraph.addEdge(null,
+ // fromV.left().value(), toV.left().value(), type);
+
+ Edge addEdge = addVertexFrom.addEdge(edge.label(), addVertexTo);
+ // ElementHelper.setProperties(addEdge,
+ // ElementHelper.getProperties(edge));
+ Utils.setProperties(addEdge, Utils.getProperties(edge));
+
+ // log.info("fromVertex=" +
+ // ElementHelper.getProperties(vertexFrom));
+ log.info("fromVertex=" + Utils.getProperties(vertexFrom));
+ // log.info("toVertex=" +
+ // ElementHelper.getProperties(vertexTo));
+ log.info("toVertex=" + Utils.getProperties(vertexTo));
+ // log.info("edge=" + edge.getLabel() + " " +
+ // ElementHelper.getProperties(edge));
+ log.info("edge=" + edge.label() + " " + Utils.getProperties(edge));
+
+ // GraphSONWriter.outputGraph(openGraph, outputFile);
+ GraphSONWriter create = GraphSONWriter.build().create();
+ create.writeGraph(out, openGraph);
+
+ // openGraph.rollback();
+ openGraph.tx().rollback();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ // log.error("fromVertex=" +
+ // ElementHelper.getProperties(vertexFrom));
+ log.error("fromVertex=" + Utils.getProperties(vertexFrom));
+ // log.error("toVertex=" +
+ // ElementHelper.getProperties(vertexTo));
+ log.error("toVertex=" + Utils.getProperties(vertexTo));
+ // log.error("edge=" + edge.getLabel() + " " +
+ // ElementHelper.getProperties(edge));
+ log.error("edge=" + edge.label() + " " + Utils.getProperties(edge));
+
+ break;
+
+ }
+ }
+
+ // graph.rollback();
+ graph.tx().rollback();
+
+ }
+
+ if (runVertexScan) {
+
+ Vertex vertex = null;
+ // Iterable<Vertex> vertices = graph.getVertices();
+
+ // Iterator<Vertex> iteratorVertex = vertices.iterator();
+ Iterator<Vertex> iteratorVertex = graph.vertices();
+ while (iteratorVertex.hasNext()) {
+
+ try {
+
+ vertex = iteratorVertex.next();
+
+ // Iterable<Edge> edges2 =
+ // vertex.getEdges(Direction.BOTH);
+
+ // Iterator<Edge> iterator2 = edges2.iterator();
+ Iterator<Edge> iterator2 = vertex.edges(Direction.BOTH);
+ if (false == iterator2.hasNext()) {
+
+ BaseConfiguration conf = new BaseConfiguration();
+ conf.setProperty("storage.backend", "inmemory");
+ TitanGraph openGraph = Utils.openGraph(conf);
+
+ TitanVertex addVertexFrom = openGraph.addVertex();
+ // ElementHelper.setProperties(addVertexFrom,
+ // ElementHelper.getProperties(vertex));
+ Utils.setProperties(addVertexFrom, Utils.getProperties(vertex));
+
+ // log.info("fromVertex=" +
+ // ElementHelper.getProperties(addVertexFrom));
+ log.info("fromVertex=" + Utils.getProperties(addVertexFrom));
+
+ // GraphSONWriter.outputGraph(openGraph,
+ // outputFile);
+ GraphSONWriter create = GraphSONWriter.build().create();
+ create.writeGraph(out, openGraph);
+
+ // openGraph.rollback();
+ openGraph.tx().rollback();
+
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+
+ // log.error("vertex=" +
+ // ElementHelper.getProperties(vertex));
+
+ GraphPropertiesDictionary[] values = GraphPropertiesDictionary.values();
+
+ // Object property1 =
+ // vertex.getProperty(GraphPropertiesDictionary.HEALTH_CHECK.getProperty());
+ Object property1 = vertex.value(GraphPropertiesDictionary.HEALTH_CHECK.getProperty());
+ System.out.println(property1);
+
+ // Object property2 = vertex.getProperty("healthcheck");
+ Object property2 = vertex.value("healthcheck");
+ System.out.println(property2);
+
+ // for (GraphPropertiesDictionary value : values) {
+ //
+ // System.out.println(property);
+ // }
+
+ break;
+
+ }
+ }
+
+ // graph.rollback();
+ graph.tx().rollback();
+
+ }
+
+ // Iterable<Vertex> vertices2 =
+ // graph.getVertices(GraphPropertiesDictionary.HEALTH_CHECK.getProperty(),
+ // "GOOD");
+ Iterable<TitanVertex> vertices2 = graph.query()
+ .has(GraphPropertiesDictionary.HEALTH_CHECK.getProperty(), "GOOD").vertices();
+ ;
+ Vertex next = vertices2.iterator().next();
+
+ BaseConfiguration conf = new BaseConfiguration();
+ conf.setProperty("storage.backend", "inmemory");
+ TitanGraph openGraph = Utils.openGraph(conf);
+
+ // TitanVertex addVertexFrom = openGraph.addVertex();
+ //
+ // addVertexFrom.setProperty(GraphPropertiesDictionary.HEALTH_CHECK.getProperty(),
+ // "GOOD");
+ // addVertexFrom.setProperty("healthcheck",
+ // next.getProperty("healthcheck"));
+ //
+ // //next.remove();
+ //
+ // next.removeProperty("healthcheck");
+ // next.removeProperty("healthcheckis");
+ //
+ // next.remove();
+
+ // GraphSONWriter.outputGraph(openGraph, outputFile);
+
+ for (NodeTypeEnum nodeTypeEnum : NodeTypeEnum.values()) {
+ removeNodesByLabel(graph, nodeTypeEnum.getName());
+ }
+
+ // GraphSONWriter.outputGraph(graph, outputFile);
+
+ GraphSONWriter create = GraphSONWriter.build().create();
+ create.writeGraph(out, graph);
+
+ // graph.rollback();
+ graph.tx().rollback();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ // graph.rollback();
+ graph.tx().rollback();
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+
+ }
+
+ private void removeNodesByLabel(TitanGraph graph, String label) {
+ Iterable<TitanVertex> vertices = graph.query().has(GraphPropertiesDictionary.LABEL.getProperty(), label)
+ .vertices();
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Vertex next2 = iterator.next();
+ next2.remove();
+ }
+ }
+
+ public static void clearGraph(TitanGraph graph) {
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+
+ long erased = 0;
+
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ // graph.removeVertex(vertex);
+ vertex.remove();
+ erased++;
+ }
+
+ }
+
+ System.out.println("After erasing " + erased + " vertices.");
+ // graph.commit();
+ graph.tx().commit();
+ }
+
+ public String exportUsers(TitanGraph graph, String outputDirectory) {
+
+ List<Map<String, Object>> users = new ArrayList<>();
+ String result = null;
+
+ // GraphMLWriter graphMLWriter = new GraphMLWriter(graph);
+
+ String outputFile = outputDirectory + File.separator + "users." + System.currentTimeMillis() + ".json";
+
+ FileWriter fileWriter = null;
+ try {
+
+ TitanGraphQuery graphQuery = graph.query().has(GraphPropertiesDictionary.LABEL.getProperty(),
+ NodeTypeEnum.User.getName());
+
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = graphQuery.vertices();
+
+ if (vertices != null) {
+ for (Vertex v : vertices) {
+ Map<String, Object> properties = getProperties(v);
+ properties.remove(GraphPropertiesDictionary.LABEL.getProperty());
+ users.add(properties);
+ }
+ }
+
+ graph.tx().commit();
+
+ String jsonUsers = gson.toJson(users);
+
+ fileWriter = new FileWriter(outputFile);
+ fileWriter.write(jsonUsers);
+
+ result = outputFile;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ graph.tx().rollback();
+ } finally {
+ try {
+ if (fileWriter != null) {
+ fileWriter.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+
+ }
+
+ public Map<String, Object> getProperties(Element element) {
+
+ Map<String, Object> result = new HashMap<String, Object>();
+ ;
+
+ if (element.keys() != null && element.keys().size() > 0) {
+ Map<String, Property> propertyMap = ElementHelper.propertyMap(element,
+ element.keys().toArray(new String[element.keys().size()]));
+
+ for (Entry<String, Property> entry : propertyMap.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue().value();
+
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+
+ public boolean exportUsers(String[] args) {
+
+ TitanGraph graph = null;
+ try {
+ String titanFileLocation = args[1];
+ String outputDirectory = args[2];
+ graph = openGraph(titanFileLocation);
+
+ String result = exportUsers(graph, outputDirectory);
+
+ if (result == null) {
+ return false;
+ }
+
+ System.out.println("Exported file=" + result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ if (graph != null) {
+ // graph.shutdown();
+ graph.close();
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLDataAnalyzer.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLDataAnalyzer.java
new file mode 100644
index 0000000000..77163b4cc5
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/GraphMLDataAnalyzer.java
@@ -0,0 +1,364 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.filter.ElementFilter;
+import org.jdom2.input.SAXBuilder;
+import org.jdom2.util.IteratorIterable;
+
+public class GraphMLDataAnalyzer {
+
+ private static final String[] COMPONENT_SHEET_HEADER = { "uniqueId", "type", "name", "toscaResourceName",
+ "resourceType", "version", "deleted", "hasNonCalculatedReqCap" };
+ private static final String[] COMPONENT_INSTANCES_SHEET_HEADER = { "uniqueId", "name", "originUid", "originType",
+ "containerUid" };
+
+ public String analyzeGraphMLData(String[] args) {
+ String result = null;
+ try {
+ String mlFileLocation = args[0];
+ result = _analyzeGraphMLData(mlFileLocation);
+ System.out.println("Analyzed ML file=" + mlFileLocation + ", XLS result=" + result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ return result;
+ }
+
+ private String _analyzeGraphMLData(String mlFileLocation) throws Exception {
+
+ // Parse ML file
+ SAXBuilder builder = new SAXBuilder();
+ File xmlFile = new File(mlFileLocation);
+ Document document = (Document) builder.build(xmlFile);
+
+ // XLS data file name
+ String outputFile = mlFileLocation.replace(".graphml", ".xls");
+ Workbook wb = new HSSFWorkbook();
+ FileOutputStream fileOut = new FileOutputStream(outputFile);
+ writeComponents(wb, document);
+ writeComponentInstances(wb, document);
+ wb.write(fileOut);
+ fileOut.close();
+ return outputFile;
+ }
+
+ private void writeComponents(Workbook wb, Document document) {
+ Sheet componentsSheet = wb.createSheet("Components");
+ Row currentRow = componentsSheet.createRow(0);
+ for (int i = 0; i < COMPONENT_SHEET_HEADER.length; i++) {
+ currentRow.createCell(i).setCellValue(COMPONENT_SHEET_HEADER[i]);
+ }
+
+ List<ComponentRow> components = getComponents(document);
+ int rowNum = 1;
+ for (ComponentRow row : components) {
+ currentRow = componentsSheet.createRow(rowNum++);
+ currentRow.createCell(0).setCellValue(row.getUniqueId());
+ currentRow.createCell(1).setCellValue(row.getType());
+ currentRow.createCell(2).setCellValue(row.getName());
+ currentRow.createCell(3).setCellValue(row.getToscaResourceName());
+ currentRow.createCell(4).setCellValue(row.getResourceType());
+ currentRow.createCell(5).setCellValue(row.getVersion());
+ currentRow.createCell(6).setCellValue(row.getIsDeleted() != null ? row.getIsDeleted().toString() : "false");
+ currentRow.createCell(7).setCellValue(row.getHasNonCalculatedReqCap());
+ }
+ }
+
+ private void writeComponentInstances(Workbook wb, Document document) {
+ Sheet componentsSheet = wb.createSheet("ComponentInstances");
+ Row currentRow = componentsSheet.createRow(0);
+ for (int i = 0; i < COMPONENT_INSTANCES_SHEET_HEADER.length; i++) {
+ currentRow.createCell(i).setCellValue(COMPONENT_INSTANCES_SHEET_HEADER[i]);
+ }
+ List<ComponentInstanceRow> components = getComponentInstances(document);
+ int rowNum = 1;
+ for (ComponentInstanceRow row : components) {
+ currentRow = componentsSheet.createRow(rowNum++);
+ currentRow.createCell(0).setCellValue(row.getUniqueId());
+ currentRow.createCell(1).setCellValue(row.getName());
+ currentRow.createCell(2).setCellValue(row.getOriginUid());
+ currentRow.createCell(3).setCellValue(row.getOriginType());
+ currentRow.createCell(4).setCellValue(row.getContainerUid());
+ }
+ }
+
+ private List<ComponentRow> getComponents(Document document) {
+ List<ComponentRow> res = new ArrayList<>();
+ Element root = document.getRootElement();
+ ElementFilter filter = new ElementFilter("graph");
+ Element graph = root.getDescendants(filter).next();
+ filter = new ElementFilter("edge");
+ IteratorIterable<Element> edges = graph.getDescendants(filter);
+ Set<String> componentsHavingReqOrCap = new HashSet<>();
+ filter = new ElementFilter("data");
+ for (Element edge : edges) {
+ IteratorIterable<Element> dataNodes = edge.getDescendants(filter);
+ for (Element data : dataNodes) {
+ String attributeValue = data.getAttributeValue("key");
+ switch (attributeValue) {
+ case "labelE":
+ String edgeLabel = data.getText();
+ if (edgeLabel.equals("REQUIREMENT") || edgeLabel.equals("CAPABILITY")) {
+ componentsHavingReqOrCap.add(edge.getAttributeValue("source"));
+ }
+ break;
+ }
+ }
+ }
+
+ filter = new ElementFilter("node");
+ IteratorIterable<Element> nodes = graph.getDescendants(filter);
+ filter = new ElementFilter("data");
+ for (Element element : nodes) {
+ IteratorIterable<Element> dataNodes = element.getDescendants(filter);
+ ComponentRow componentRow = new ComponentRow();
+ boolean isComponent = false;
+ for (Element data : dataNodes) {
+ String attributeValue = data.getAttributeValue("key");
+ switch (attributeValue) {
+ case "nodeLabel":
+ String nodeLabel = data.getText();
+ if (nodeLabel.equals("resource") || nodeLabel.equals("service")) {
+ isComponent = true;
+ componentRow.setType(nodeLabel);
+ String componentId = element.getAttributeValue("id");
+ componentRow.setHasNonCalculatedReqCap(componentsHavingReqOrCap.contains(componentId));
+ }
+ break;
+ case "uid":
+ componentRow.setUniqueId(data.getText());
+ break;
+ case "name":
+ componentRow.setName(data.getText());
+ break;
+ case "toscaResourceName":
+ componentRow.setToscaResourceName(data.getText());
+ break;
+ case "resourceType":
+ componentRow.setResourceType(data.getText());
+ break;
+ case "version":
+ componentRow.setVersion(data.getText());
+ break;
+ case "deleted":
+ componentRow.setIsDeleted(Boolean.parseBoolean(data.getText()));
+ break;
+ default:
+ break;
+ }
+ }
+ if (isComponent) {
+ res.add(componentRow);
+ }
+ }
+ return res;
+ }
+
+ private List<ComponentInstanceRow> getComponentInstances(Document document) {
+ List<ComponentInstanceRow> res = new ArrayList<>();
+ Element root = document.getRootElement();
+ ElementFilter filter = new ElementFilter("graph");
+ Element graph = root.getDescendants(filter).next();
+ filter = new ElementFilter("node");
+ IteratorIterable<Element> nodes = graph.getDescendants(filter);
+ filter = new ElementFilter("data");
+ for (Element element : nodes) {
+ IteratorIterable<Element> dataNodes = element.getDescendants(filter);
+ ComponentInstanceRow componentInstRow = new ComponentInstanceRow();
+ boolean isComponentInst = false;
+ for (Element data : dataNodes) {
+ String attributeValue = data.getAttributeValue("key");
+ switch (attributeValue) {
+ case "nodeLabel":
+ String nodeLabel = data.getText();
+ if (nodeLabel.equals("resourceInstance")) {
+ isComponentInst = true;
+ }
+ break;
+ case "uid":
+ componentInstRow.setUniqueId(data.getText());
+ break;
+ case "name":
+ componentInstRow.setName(data.getText());
+ break;
+ case "originType":
+ componentInstRow.setOriginType(data.getText());
+ break;
+ default:
+ break;
+ }
+ }
+ if (isComponentInst) {
+ // Assuming the uid is in standard form of
+ // <container>.<origin>.<name>
+ String uniqueId = componentInstRow.getUniqueId();
+ if (uniqueId != null) {
+ String[] split = uniqueId.split("\\.");
+ if (split.length == 3) {
+ componentInstRow.setContainerUid(split[0]);
+ componentInstRow.setOriginUid(split[1]);
+ }
+ }
+ res.add(componentInstRow);
+ }
+ }
+ return res;
+ }
+
+ private class ComponentRow {
+
+ private String uniqueId;
+ private String type;
+ private String name;
+ private String toscaResourceName;
+ private String resourceType;
+ private String version;
+ private Boolean isDeleted;
+ private Boolean hasNonCalculatedReqCap;
+
+ public Boolean getHasNonCalculatedReqCap() {
+ return hasNonCalculatedReqCap;
+ }
+
+ public void setHasNonCalculatedReqCap(Boolean hasNonCalculatedReqCap) {
+ this.hasNonCalculatedReqCap = hasNonCalculatedReqCap;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getToscaResourceName() {
+ return toscaResourceName;
+ }
+
+ public void setToscaResourceName(String toscaResourceName) {
+ this.toscaResourceName = toscaResourceName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Boolean getIsDeleted() {
+ return isDeleted;
+ }
+
+ public void setIsDeleted(Boolean deleted) {
+ this.isDeleted = deleted;
+ }
+ }
+
+ private class ComponentInstanceRow {
+ private String uniqueId;
+ private String name;
+ private String originUid;
+ private String originType;
+ private String containerUid;
+
+ public String getContainerUid() {
+ return containerUid;
+ }
+
+ public void setContainerUid(String containerUid) {
+ this.containerUid = containerUid;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getOriginUid() {
+ return originUid;
+ }
+
+ public void setOriginUid(String componentUid) {
+ this.originUid = componentUid;
+ }
+
+ public String getOriginType() {
+ return originType;
+ }
+
+ public void setOriginType(String originType) {
+ this.originType = originType;
+ }
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/PopulateComponentCache.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/PopulateComponentCache.java
new file mode 100644
index 0000000000..812d534f49
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/PopulateComponentCache.java
@@ -0,0 +1,388 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.dao.cassandra.ComponentCassandraDao;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.cache.ComponentCache;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ProductOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.resources.data.ComponentCacheData;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.util.SerializationUtils;
+import org.openecomp.sdc.common.util.ZipUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+/**
+ * Created by esofer on 9/1/2016.
+ */
+public class PopulateComponentCache {
+
+ private static Logger log = LoggerFactory.getLogger(PopulateComponentCache.class.getName());
+
+ @Autowired
+ protected ComponentCassandraDao componentCassandraDao;
+
+ @Autowired
+ protected ResourceOperation resourceOperation;
+
+ @Autowired
+ protected ServiceOperation serviceOperation;
+
+ @Autowired
+ protected ProductOperation productOperation;
+
+ @Autowired
+ protected ComponentCache componentCache;
+
+ private void exit(String stage, int i) {
+ log.error("Failed on " + stage);
+ System.exit(i);
+
+ }
+
+ public void populateCache() {
+ populateCache(ComponentTypeEnum.RESOURCE);
+ populateCache(ComponentTypeEnum.SERVICE);
+ populateCache(ComponentTypeEnum.PRODUCT);
+ }
+
+ private void populateCache(ComponentTypeEnum componentTypeEnum) {
+
+ List<String> list = new ArrayList<>();
+ Either<TitanGraph, TitanOperationStatus> graph = resourceOperation.getTitanGenericDao().getGraph();
+ TitanGraph titanGraph = graph.left().value();
+ Iterable vertices = titanGraph.query()
+ .has(GraphPropertiesDictionary.LABEL.getProperty(), componentTypeEnum.name().toLowerCase()).vertices();
+
+ Iterator iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ TitanVertex vertex = (TitanVertex) iterator.next();
+
+ // VertexProperty<Object> state =
+ // vertex.property(GraphPropertiesDictionary.STATE.getProperty());
+ // String stateValue = (String)state.value();
+
+ // if (false ==
+ // stateValue.equalsIgnoreCase(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())
+ // ) {
+ VertexProperty<Object> uid = vertex.property(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String uidValue = (String) uid.value();
+
+ list.add(uidValue);
+ // }
+ }
+
+ int counter = 0;
+ for (String componentUid : list) {
+
+ long time1 = System.currentTimeMillis();
+
+ /////////////////////////////////////////////////////////////////////////////////////
+ // Pay attention. The component is fetched from the cache in case it
+ ///////////////////////////////////////////////////////////////////////////////////// is
+ ///////////////////////////////////////////////////////////////////////////////////// already
+ ///////////////////////////////////////////////////////////////////////////////////// there.
+ /////////////////////////////////////////////////////////////////////////////////////
+ Component component = null;
+ switch (componentTypeEnum) {
+ case RESOURCE:
+ Either<Resource, StorageOperationStatus> resourceRes = resourceOperation.getComponent(componentUid,
+ false);
+ if (resourceRes.isRight()) {
+ exit("get resource", 1);
+ }
+ component = resourceRes.left().value();
+ break;
+ case SERVICE:
+ Either<Service, StorageOperationStatus> serviceRes = serviceOperation.getComponent(componentUid, false);
+ if (serviceRes.isRight()) {
+ exit("get service", 1);
+ }
+ component = serviceRes.left().value();
+ break;
+ case PRODUCT:
+ Either<Product, StorageOperationStatus> productRes = productOperation.getComponent(componentUid, false);
+ if (productRes.isRight()) {
+ exit("get product", 1);
+ }
+ component = productRes.left().value();
+ break;
+ default:
+ break;
+ }
+
+ if (component == null) {
+ exit("get component", 1);
+ }
+
+ long time2 = System.currentTimeMillis();
+ // System.out.println("fetch resource " + resource.getName());
+ // System.out.println("fetch resource time is " + (time2 - time1) +
+ // " ms");
+
+ boolean setComponent = componentCache.setComponent(component, componentTypeEnum.getNodeType());
+ if (setComponent) {
+ counter++;
+ }
+
+ /*
+ * Either<byte[], Boolean> valueRes =
+ * SerializationUtils.serializeExt(component);
+ *
+ * if (valueRes.isRight()) { exit("serialize component " +
+ * component.getName(), 2); } byte[] value =
+ * valueRes.left().value(); log.info("byte[] size is " +
+ * value.length); //System.out.println("byte[] size is " +
+ * value.length);
+ *
+ * byte[] zipped = null; try { zipped = ZipUtil.zipBytes(value);
+ * //System.out.println("byte[] size after zip is " +
+ * zipped.length);
+ *
+ * ComponentCacheData componentCacheData = new ComponentCacheData();
+ * componentCacheData.setDataAsArray(zipped);
+ * componentCacheData.setIsZipped(true);
+ * componentCacheData.setId(componentUid);
+ * componentCacheData.setModificationTime(new
+ * Date(component.getLastUpdateDate()));
+ * componentCacheData.setType(component.getComponentType().name().
+ * toLowerCase());
+ *
+ * long averageInsertTimeInMilli =
+ * writeResourceToCassandraComponent(componentCacheData); log.
+ * info("After adding component {} to cassandra. Insert time is {} ms."
+ * , componentUid, averageInsertTimeInMilli);
+ *
+ * } catch (IOException e) { // TODO Auto-generated catch block
+ * e.printStackTrace(); }
+ */
+
+ }
+
+ log.debug("The number of saved components of type {} is {}. Total size is {}", componentTypeEnum, counter,
+ list.size());
+
+ }
+
+ private long writeResourceToCassandraComponent(ComponentCacheData componentCacheData) {
+
+ long startTime = System.currentTimeMillis();
+
+ // call to cassandra read
+ CassandraOperationStatus saveArtifact = componentCassandraDao.saveComponent(componentCacheData);
+ if (saveArtifact != CassandraOperationStatus.OK) {
+ exit("writeResourceToCassandra", 3);
+ }
+
+ long endTime = System.currentTimeMillis();
+
+ return (endTime - startTime);
+ }
+
+ private void deserializeByThreads(List<ESArtifactData> list, ExecutorService executor, int threadNumber) {
+
+ long fullSearchStart = System.currentTimeMillis();
+ // for (int k =0; k < parts; k++) {
+
+ List<List<ESArtifactData>> lists = new ArrayList<>();
+ for (int i = 0; i < threadNumber; i++) {
+ lists.add(new ArrayList<>());
+ }
+
+ List<Future<List<Resource>>> results = new ArrayList<>();
+ for (int i = 0; i < list.size(); i++) {
+ lists.get(i % threadNumber).add(list.get(i));
+ }
+
+ for (int i = 0; i < threadNumber; i++) {
+
+ // Callable<List<Resource>> worker = new
+ // MyDesrializabletCallable(lists.get(i), i);
+ Callable<List<Resource>> worker = new My3rdPartyDesrializabletCallable(lists.get(i), i);
+ Future<List<Resource>> submit = executor.submit(worker);
+ results.add(submit);
+ }
+
+ long fullSearchStart2 = System.currentTimeMillis();
+ for (Future<List<Resource>> future : results) {
+ try {
+ while (false == future.isDone()) {
+ Thread.sleep(1);
+ }
+
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ long fullSearchEnd2 = System.currentTimeMillis();
+ log.info("esofer time wait to threads finish " + ((fullSearchEnd2 - fullSearchStart2)) + " ms");
+ // }
+ long fullSearchEnd = System.currentTimeMillis();
+
+ log.info("esofer full desrialize time " + ((fullSearchEnd - fullSearchStart)) + " ms");
+ System.out.println("esofer full desrialize time " + ((fullSearchEnd - fullSearchStart)) + " ms");
+ }
+
+ public class MyDesrializabletCallable implements Callable<List<Resource>> {
+
+ List<ESArtifactData> list;
+ int i;
+
+ public MyDesrializabletCallable(List<ESArtifactData> list, int i) {
+ super();
+ this.list = list;
+ this.i = i;
+ }
+
+ @Override
+ public List<Resource> call() throws Exception {
+ List<Resource> resources = new ArrayList<>();
+ long startSer = System.currentTimeMillis();
+ long endSer = System.currentTimeMillis();
+ long startUnzip = System.currentTimeMillis();
+ long endUnzip = System.currentTimeMillis();
+
+ long avgUnzip = 0;
+ long avgSer = 0;
+ for (ESArtifactData esArtifactData : list) {
+
+ byte[] dataAsArray = esArtifactData.getDataAsArray();
+ startUnzip = System.nanoTime();
+ dataAsArray = ZipUtil.unzip(dataAsArray);
+ endUnzip = System.nanoTime();
+ avgUnzip += (endUnzip - startUnzip);
+
+ startSer = System.nanoTime();
+ Either<Object, Boolean> deserialize = SerializationUtils.deserialize(dataAsArray);
+ endSer = System.nanoTime();
+ avgSer += (endSer - startSer);
+ // Either<Object, Boolean> deserialize =
+ // SerializationUtils.deserialize(esArtifactData.getDataAsArray());
+ if (deserialize.isRight()) {
+ exit("convertByteArrayToResource " + deserialize.right().value(), 5);
+ }
+
+ Resource resource = (Resource) deserialize.left().value();
+ resources.add(resource);
+ // System.out.println("After desrialize T[" + i + "]resource " +
+ // resource.getUniqueId());
+ }
+
+ System.out.println("After desrialize average desrialize " + list.size() + " T[" + i + "] "
+ + (avgSer / 1000 / list.size()) + " micro");
+ System.out.println(
+ "After desrialize average unzip T[" + i + "] " + (avgUnzip / 1000 / list.size()) + " micro");
+
+ ////////////////////////
+ // maybe register most frequently used classes on conf
+ // write
+ // byte barray[] = conf.asByteArray(mySerializableObject);
+ // read
+ // MyObject object = (MyObject)conf.asObject(barray);
+
+ return resources;
+ }
+ }
+
+ public class My3rdPartyDesrializabletCallable implements Callable<List<Resource>> {
+
+ List<ESArtifactData> list;
+ int i;
+
+ public My3rdPartyDesrializabletCallable(List<ESArtifactData> list, int i) {
+ super();
+ this.list = list;
+ this.i = i;
+ }
+
+ @Override
+ public List<Resource> call() throws Exception {
+ List<Resource> resources = new ArrayList<>();
+ long startSer = System.currentTimeMillis();
+ long endSer = System.currentTimeMillis();
+ long startUnzip = System.currentTimeMillis();
+ long endUnzip = System.currentTimeMillis();
+
+ long avgUnzip = 0;
+ long avgSer = 0;
+ for (ESArtifactData esArtifactData : list) {
+
+ byte[] dataAsArray = esArtifactData.getDataAsArray();
+ startUnzip = System.nanoTime();
+ dataAsArray = ZipUtil.unzip(dataAsArray);
+ endUnzip = System.nanoTime();
+ avgUnzip += (endUnzip - startUnzip);
+
+ startSer = System.nanoTime();
+
+ Either<Resource, Boolean> deserializeExt = SerializationUtils.deserializeExt(dataAsArray,
+ Resource.class, "");
+
+ if (deserializeExt.isLeft()) {
+ Resource resource = deserializeExt.left().value();
+ // System.out.println("=============================================");
+ // System.out.println(resource.getCapabilities().size());
+ // System.out.println(resource.getRequirements().size());
+ endSer = System.nanoTime();
+ avgSer += (endSer - startSer);
+ resources.add(resource);
+ // System.out.println("After desrialize T[" + i + "]resource
+ // " + resource.getUniqueId());
+ }
+ }
+
+ System.out.println("After desrialize average desrialize " + list.size() + " T[" + i + "] "
+ + (avgSer / 1000 / list.size()) + " micro");
+ System.out.println(
+ "After desrialize average unzip T[" + i + "] " + (avgUnzip / 1000 / list.size()) + " micro");
+
+ return resources;
+ }
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/ProductLogic.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/ProductLogic.java
new file mode 100644
index 0000000000..9f15c83dd8
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/ProductLogic.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Created by mlando on 2/23/2016.
+ */
+public class ProductLogic {
+
+ private static Logger log = LoggerFactory.getLogger(ProductLogic.class.getName());
+
+ public boolean deleteAllProducts(String titanFile, String beHost, String bePort, String adminUser) {
+ log.debug("retrieving all products from graph");
+ RestUtils restUtils = null;
+ try {
+ List<String> productList = getAllProducts(titanFile);
+ restUtils = new RestUtils();
+ if (productList != null) {
+ for (String productUid : productList) {
+ Integer status = restUtils.deleteProduct(productUid, beHost, bePort, adminUser);
+ }
+ return true;
+ } else {
+ log.error("failed to get products from graph");
+ return false;
+ }
+ } finally {
+ if (restUtils != null) {
+ restUtils.closeClient();
+ }
+ }
+ }
+
+ private List<String> getAllProducts(String titanFile) {
+ TitanGraph graph = null;
+ try {
+ graph = openGraph(titanFile);
+ List<String> productsToDelete = new ArrayList<String>();
+ Iterable vertices = graph.query()
+ .has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Product.getName()).vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex vertex = iter.next();
+ String id = vertex.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ productsToDelete.add(id);
+ }
+ }
+
+ graph.tx().commit();
+ return productsToDelete;
+ } catch (Exception e) {
+ e.printStackTrace();
+ graph.tx().rollback();
+ return null;
+
+ } finally {
+ if (graph != null) {
+ graph.close();
+ }
+ }
+ }
+
+ private TitanGraph openGraph(String titanFileLocation) {
+
+ TitanGraph graph = TitanFactory.open(titanFileLocation);
+
+ return graph;
+
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/RestUtils.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/RestUtils.java
new file mode 100644
index 0000000000..c256ca09a4
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/RestUtils.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpUriRequest;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Created by mlando on 2/23/2016.
+ */
+public class RestUtils {
+
+ final String DELETE_PRODUCT = "http://%s:%s/sdc2/rest/v1/catalog/products/%s";
+ final Integer DELETE_SUCCSES_RESPONSE = 200;
+
+ private static Logger log = LoggerFactory.getLogger(RestUtils.class.getName());
+ CloseableHttpClient httpClient;
+
+ public RestUtils() {
+ this.httpClient = HttpClients.createDefault();
+ }
+
+ private CloseableHttpResponse exacuteRequest(HttpUriRequest httpRequest) throws IOException {
+ log.debug("received http request: {}", httpRequest.toString());
+ return httpClient.execute(httpRequest);
+ }
+
+ public void closeClient() {
+ log.debug("closing http client");
+ try {
+ this.httpClient.close();
+ log.debug("closed http client");
+ } catch (IOException e) {
+ log.debug("close http client failed", e);
+
+ }
+ }
+
+ public Integer deleteProduct(String productUid, String beHost, String bePort, String adminUser) {
+ String url = String.format(DELETE_PRODUCT, beHost, bePort, productUid);
+ HttpDelete deleteRequest = new HttpDelete(url);
+ deleteRequest.setHeader("USER_ID", adminUser);
+ try (CloseableHttpResponse response = this.httpClient.execute(deleteRequest)) {
+ int status = response.getStatusLine().getStatusCode();
+ if (DELETE_SUCCSES_RESPONSE.equals(status)) {
+ log.debug("Product uid:{} succsesfully deleted", productUid);
+ } else {
+ log.error("Product uid:{} delete failed status {}", productUid, status);
+ }
+ return status;
+ } catch (IOException e) {
+ log.error("Product uid:" + productUid + " delete failed with exception", e);
+ }
+ return null;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/UpdatePropertyOnVertex.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/UpdatePropertyOnVertex.java
new file mode 100644
index 0000000000..b480091723
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/UpdatePropertyOnVertex.java
@@ -0,0 +1,180 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.asdctool.Utils;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanGraphQuery;
+
+public class UpdatePropertyOnVertex {
+
+ private static Logger log = LoggerFactory.getLogger(UpdatePropertyOnVertex.class.getName());
+
+ public Integer updatePropertyOnServiceAtLeastCertified(String titanFile, Map<String, Object> keyValueToSet,
+ List<Map<String, Object>> orCriteria) {
+
+ TitanGraph graph = null;
+
+ Integer numberOfUpdatedVertexes = 0;
+
+ try {
+ graph = openGraph(titanFile);
+
+ if (orCriteria != null && false == orCriteria.isEmpty()) {
+
+ for (Map<String, Object> criteria : orCriteria) {
+
+ TitanGraphQuery<? extends TitanGraphQuery> query = graph.query();
+
+ if (criteria != null && !criteria.isEmpty()) {
+ for (Map.Entry<String, Object> entry : criteria.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+
+ Iterator iterator = query
+ .has(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name())
+ .vertices().iterator();
+
+ boolean isFoundAtLeastOneCertifiedService = false;
+ while (iterator.hasNext()) {
+ Vertex vertex = (Vertex) iterator.next();
+
+ Map<String, Object> leftProps = Utils.getProperties(vertex);
+ boolean vertexLeftContainsRightProps = Utils.vertexLeftContainsRightProps(leftProps, criteria);
+ if (false == vertexLeftContainsRightProps) {
+ log.debug("Ignore vertex since properties it does not contains properties {}. Vertex properties are: {}", criteria, leftProps);
+ continue;
+ }
+
+ isFoundAtLeastOneCertifiedService = true;
+ break;
+ }
+
+ if (true == isFoundAtLeastOneCertifiedService) {
+
+ Integer currentNumberOfUpdates = updateVertexes(keyValueToSet, graph, criteria);
+
+ if (currentNumberOfUpdates != null) {
+ numberOfUpdatedVertexes += currentNumberOfUpdates;
+ }
+
+ } else {
+ log.debug("No certified service was found for criteria {}", criteria);
+ }
+ }
+
+ }
+
+ // graph.commit();
+ graph.tx().commit();
+
+ return numberOfUpdatedVertexes;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ // graph.rollback();
+ graph.tx().rollback();
+
+ return null;
+
+ } finally {
+ if (graph != null) {
+ // graph.shutdown();
+ graph.close();
+ }
+ }
+
+ }
+
+ private Integer updateVertexes(Map<String, Object> keyValueToSet, TitanGraph graph, Map<String, Object> criteria) {
+ Integer numberOfUpdatedVertexesPerService = 0;
+
+ TitanGraphQuery<? extends TitanGraphQuery> updateQuery = graph.query();
+
+ if (criteria != null && !criteria.isEmpty()) {
+ for (Map.Entry<String, Object> entry : criteria.entrySet()) {
+ updateQuery = updateQuery.has(entry.getKey(), entry.getValue());
+ }
+ }
+ Iterator updateIterator = updateQuery.vertices().iterator();
+
+ while (updateIterator.hasNext()) {
+
+ Vertex vertex = (Vertex) updateIterator.next();
+
+ Map<String, Object> leftProps = Utils.getProperties(vertex);
+
+ boolean vertexLeftContainsRightProps = Utils.vertexLeftContainsRightProps(leftProps, criteria);
+ if (false == vertexLeftContainsRightProps) {
+ log.debug("Ignore vertex since properties it does not contains properties {}. Vertex properties are {}", criteria, leftProps);
+ continue;
+ }
+
+ if (keyValueToSet != null) {
+ for (Entry<String, Object> entry : keyValueToSet.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ // vertex.setProperty(key, value);
+ vertex.property(key, value);
+ //if(log.isDebugEnabled()){
+ // log.debug("After setting vertex: {} {} with key value: {}, {}",
+ // vertex.getProperty(GraphPropertiesDictionary.NAME.getProperty()),
+ // vertex.getProperty(GraphPropertiesDictionary.VERSION.getProperty()),
+ // key, value);
+ //}
+ log.debug("After setting vertex: {} {} with key value: {}, {}",
+ vertex.property(GraphPropertiesDictionary.NAME.getProperty()),
+ vertex.property(GraphPropertiesDictionary.VERSION.getProperty()),
+ key, value);
+ numberOfUpdatedVertexesPerService++;
+ }
+ }
+
+ }
+
+ log.info(
+ "The number of updated services for criteria " + criteria + " is " + numberOfUpdatedVertexesPerService);
+ return numberOfUpdatedVertexesPerService;
+ }
+
+ public TitanGraph openGraph(String titanFileLocation) {
+
+ TitanGraph graph = TitanFactory.open(titanFileLocation);
+
+ return graph;
+
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AddGroupUuid.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AddGroupUuid.java
new file mode 100644
index 0000000000..db8fee0fa2
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AddGroupUuid.java
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.GroupData;
+import org.slf4j.Logger;
+
+import fj.data.Either;
+
+public class AddGroupUuid {
+
+ public static boolean addGroupUuids(TitanGenericDao titanGenericDao, Logger log, boolean inTrsansaction) {
+
+ boolean result = true;
+
+ try {
+
+ log.debug("========================================================");
+ log.debug("Before find all groups");
+
+ Either<List<GroupData>, TitanOperationStatus> allGroups = titanGenericDao.getByCriteria(NodeTypeEnum.Group,
+ null, null, GroupData.class);
+
+ if (allGroups.isRight()) {
+ TitanOperationStatus status = allGroups.right().value();
+ log.debug("After finding all groups. Status is {}", status);
+ if (status != TitanOperationStatus.NOT_FOUND && status != TitanOperationStatus.OK) {
+ result = false;
+ return result;
+ } else {
+ return result;
+ }
+ }
+
+ List<GroupData> groups = allGroups.left().value();
+
+ log.info("The number of groups fetched is {}", groups == null ? 0 : groups.size());
+
+ int numberOfUpdates = 0;
+ if (false == groups.isEmpty()) {
+ Map<String, List<GroupData>> invariantIdToGroups = groups.stream()
+ .collect(Collectors.groupingBy(p -> p.getGroupDataDefinition().getInvariantUUID()));
+
+ // All the groups with the same invariantUUID should have the
+ // same group UUID since update VF flow with CSAR was not
+ // supported in the E2E environment.
+
+ log.info("The number of different invariantUuids is {}",
+ invariantIdToGroups == null ? 0 : invariantIdToGroups.size());
+
+ for (Entry<String, List<GroupData>> entry : invariantIdToGroups.entrySet()) {
+
+ String invariantUuid = entry.getKey();
+ List<GroupData> groupsData = entry.getValue();
+
+ StringBuilder builder = new StringBuilder();
+ groupsData.forEach(p -> builder.append(p.getGroupDataDefinition().getUniqueId() + ","));
+
+ String groupUUID = groupsData.get(0).getGroupDataDefinition().getGroupUUID();
+
+ if (groupUUID == null) {
+
+ groupUUID = UniqueIdBuilder.generateUUID();
+
+ log.debug("Before updating groups {} with groupUUID {}", builder.toString(), groupUUID);
+
+ for (GroupData groupData : groupsData) {
+
+ numberOfUpdates++;
+ groupData.getGroupDataDefinition().setGroupUUID(groupUUID);
+ Either<GroupData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(groupData,
+ GroupData.class);
+ if (updateNode.isRight()) {
+ log.error("Failed to update group " + groupData + ". Error is {}",
+ updateNode.right().value().toString());
+ result = false;
+ return result;
+ }
+
+ }
+
+ log.debug("After updating groups {} with groupUUID {}", builder.toString(), groupUUID);
+ }
+
+ }
+ }
+
+ log.info("The number of groups updated with groupUUID is " + numberOfUpdates);
+
+ return result;
+
+ } finally {
+ log.info("Finish updating groupUUIDs. Status is {}.", result);
+ if (inTrsansaction == false) {
+ if (result == false) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AllowMultipleHeats.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AllowMultipleHeats.java
new file mode 100644
index 0000000000..561cfb5a5b
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AllowMultipleHeats.java
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.slf4j.Logger;
+
+import fj.data.Either;
+
+public class AllowMultipleHeats {
+
+ public static boolean removeAndUpdateHeatPlaceHolders(TitanGenericDao titanGenericDao, Logger log,
+ boolean inTrsansaction) {
+
+ boolean result = true;
+
+ try {
+
+ List<ArtifactData> artifactsToDelete = new ArrayList<>();
+ List<ArtifactData> artifactsToUpdate = new ArrayList<>();
+
+ String[] phLabels = { "heat", "heatvol", "heatnet" };
+
+ for (String artifactLabel : phLabels) {
+ Map<String, Object> properties = new HashMap<>();
+
+ properties.put(GraphPropertiesDictionary.ARTIFACT_LABEL.getProperty(), artifactLabel);
+
+ Either<List<ArtifactData>, TitanOperationStatus> allHeatArtifacts = titanGenericDao
+ .getByCriteria(NodeTypeEnum.ArtifactRef, properties, null, ArtifactData.class);
+
+ if (allHeatArtifacts.isRight()) {
+ TitanOperationStatus status = allHeatArtifacts.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ continue;
+ } else {
+ result = false;
+ return result;
+ }
+
+ }
+
+ List<ArtifactData> list = allHeatArtifacts.left().value();
+ log.debug("Found {} artifacts with label {}", (list == null ? 0 : list.size()), artifactLabel);
+
+ if (list != null && false == list.isEmpty()) {
+
+ for (ArtifactData artifactData : list) {
+ String esId = artifactData.getArtifactDataDefinition().getEsId();
+ if (esId == null || true == esId.isEmpty()) {
+ artifactsToDelete.add(artifactData);
+ } else {
+ artifactsToUpdate.add(artifactData);
+ }
+ }
+ }
+ }
+
+ if (false == artifactsToDelete.isEmpty()) {
+ for (ArtifactData artifactData : artifactsToDelete) {
+ // System.out.println("Going to delete artifact " +
+ // artifactData);
+ log.debug("Going to delete artifact {}", artifactData);
+ Either<ArtifactData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(artifactData,
+ ArtifactData.class);
+ if (deleteNode.isRight()) {
+ log.error("Failed to delete artifact node {}", deleteNode.left().value());
+ result = false;
+ return result;
+ } else {
+ log.debug("Delete artifact node {}", deleteNode.left().value());
+ }
+ }
+ }
+
+ log.debug("Number of deleted artifacts is {}", artifactsToDelete.size());
+
+ int counter = 0;
+ if (false == artifactsToUpdate.isEmpty()) {
+ for (ArtifactData artifactData : artifactsToUpdate) {
+ // System.out.println("Going to update artifact " +
+ // artifactData);
+
+ if (artifactData.getArtifactDataDefinition().getMandatory() != null
+ && true == artifactData.getArtifactDataDefinition().getMandatory()) {
+ log.debug("Going to update artifact {}", artifactData);
+ counter++;
+ artifactData.getArtifactDataDefinition().setMandatory(false);
+ Either<ArtifactData, TitanOperationStatus> updatedNode = titanGenericDao
+ .updateNode(artifactData, ArtifactData.class);
+ if (updatedNode.isRight()) {
+ log.error("Failed to update artifact node {}", updatedNode.left().value());
+ result = false;
+ return result;
+ } else {
+ log.debug("Update artifact node {}", updatedNode.left().value());
+ }
+ }
+ }
+ }
+
+ log.debug("Number of updated artifacts is {}", counter);
+
+ return result;
+
+ } finally {
+ if (inTrsansaction == false) {
+ if (result == false) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AppConfig.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AppConfig.java
new file mode 100644
index 0000000000..b529935a38
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/AppConfig.java
@@ -0,0 +1,538 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import org.openecomp.sdc.asdctool.impl.PopulateComponentCache;
+import org.openecomp.sdc.asdctool.impl.migration.v1607.CsarMigration;
+import org.openecomp.sdc.asdctool.impl.migration.v1610.TitanFixUtils;
+import org.openecomp.sdc.asdctool.impl.migration.v1610.ToscaArtifactsAlignment;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+import org.openecomp.sdc.be.components.distribution.engine.IDistributionEngine;
+import org.openecomp.sdc.be.components.distribution.engine.ServiceDistributionArtifactsBuilder;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.CompositionBusinessLogic;
+import org.openecomp.sdc.be.components.impl.GroupBusinessLogic;
+import org.openecomp.sdc.be.components.impl.InputsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ProductComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.VFComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic;
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraClient;
+import org.openecomp.sdc.be.dao.cassandra.ComponentCassandraDao;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.be.dao.impl.AuditingDao;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanGraphClient;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.cache.ComponentCache;
+import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.IUserAdminOperation;
+import org.openecomp.sdc.be.model.operations.impl.AdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.impl.ArtifactOperation;
+import org.openecomp.sdc.be.model.operations.impl.AttributeOperation;
+import org.openecomp.sdc.be.model.operations.impl.CacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.ComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.CsarOperation;
+import org.openecomp.sdc.be.model.operations.impl.ElementOperation;
+import org.openecomp.sdc.be.model.operations.impl.GraphLockOperation;
+import org.openecomp.sdc.be.model.operations.impl.GroupOperation;
+import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.HeatParametersOperation;
+import org.openecomp.sdc.be.model.operations.impl.InputsOperation;
+import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.LifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.OnboardingClient;
+import org.openecomp.sdc.be.model.operations.impl.ProductOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.RequirementOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UserAdminOperation;
+import org.openecomp.sdc.be.tosca.CsarUtils;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AppConfig {
+ @Bean(name = "serviceMigrationBean")
+ public ServiceMigration serviceMigration() {
+ return new ServiceMigration();
+ }
+
+ @Bean(name = "vfcNamingAlignmentBean")
+ public VfcNamingAlignment vfcNamingAlignment() {
+ return new VfcNamingAlignment();
+ }
+
+ @Bean(name = "derivedFromAlignment")
+ public DerivedFromAlignment derivedFromAlignment() {
+ return new DerivedFromAlignment();
+ }
+
+ @Bean(name = "groupsAlignment")
+ public GroupsAlignment groupsAlignment() {
+ return new GroupsAlignment();
+ }
+
+ @Bean(name = "csarMigration")
+ public CsarMigration csarMigration() {
+ return new CsarMigration();
+ }
+
+ @Bean(name = "titan-generic-dao")
+ public TitanGenericDao titanGenericDao() {
+ return new TitanGenericDao();
+ }
+
+ @Bean(name = "titan-client", initMethod = "createGraph")
+ public TitanGraphClient titanClient() {
+ return new TitanGraphClient();
+ }
+
+ @Bean(name = "resource-operation")
+ public ResourceOperation resourceOperation() {
+ return new ResourceOperation();
+ }
+
+ @Bean(name = "service-operation")
+ public ServiceOperation serviceOperation() {
+ return new ServiceOperation();
+ }
+
+ @Bean(name = "component-instance-operation")
+ public ComponentInstanceOperation componentInstanceOperation() {
+ return new ComponentInstanceOperation();
+ }
+
+ @Bean(name = "capability-instanceOperation")
+ public CapabilityInstanceOperation capabilityInstanceOperation() {
+ return new CapabilityInstanceOperation();
+ }
+
+ @Bean(name = "property-operation")
+ public PropertyOperation propertyOperation() {
+ return new PropertyOperation();
+ }
+
+ @Bean(name = "attribute-operation")
+ public AttributeOperation attribueOperation() {
+ return new AttributeOperation();
+ }
+
+ @Bean(name = "application-datatype-cache")
+ public ApplicationDataTypeCache applicationDataTypeCache() {
+ return new ApplicationDataTypeCache();
+ }
+
+ @Bean(name = "requirement-operation")
+ public RequirementOperation requirementOperation() {
+ return new RequirementOperation();
+ }
+
+ @Bean(name = "capability-operation")
+ public CapabilityOperation capabilityOperation() {
+ return new CapabilityOperation();
+ }
+
+ @Bean(name = "interface-operation")
+ public InterfaceLifecycleOperation interfaceLifecycleOperation() {
+ return new InterfaceLifecycleOperation();
+ }
+
+ @Bean(name = "element-operation")
+ public IElementOperation elementOperation() {
+ return new ElementOperation();
+ }
+
+ @Bean(name = "additional-information-operation")
+ public IAdditionalInformationOperation addioAdditionalInformationOperation() {
+ return new AdditionalInformationOperation();
+ }
+
+ @Bean(name = "capability-type-operation")
+ public CapabilityTypeOperation capabilityTypeOperation() {
+ return new CapabilityTypeOperation();
+ }
+
+ @Bean(name = "artifact-operation")
+ public ArtifactOperation artifactOperation() {
+ return new ArtifactOperation();
+ }
+
+ @Bean(name = "heat-parameter-operation")
+ public HeatParametersOperation heatParametersOperation() {
+ return new HeatParametersOperation();
+ }
+
+ @Bean(name = "product-operation")
+ public ProductOperation productOperation() {
+ return new ProductOperation();
+ }
+
+ @Bean(name = "lifecycle-operation")
+ public LifecycleOperation lifecycleOperation() {
+ return new LifecycleOperation();
+ }
+
+ @Bean(name = "group-operation")
+ public GroupOperation groupOperation() {
+ return new GroupOperation();
+ }
+
+ @Bean(name = "group-type-operation")
+ public GroupTypeOperation groupTypeOperation() {
+ return new GroupTypeOperation();
+ }
+
+ @Bean(name = "attribute-operation")
+ public AttributeOperation attributeOperation() {
+ return new AttributeOperation();
+ }
+
+ @Bean(name = "titanFixUtils")
+ public TitanFixUtils titanFixUtils() {
+ return new TitanFixUtils();
+ }
+
+ @Bean(name = "populateComponentCache")
+ public PopulateComponentCache populateComponentCache() {
+ return new PopulateComponentCache();
+ }
+
+ @Bean(name = "artifact-cassandra-dao")
+ public ArtifactCassandraDao artifactCassandraDao() {
+ return new ArtifactCassandraDao();
+ }
+
+ @Bean(name = "component-cassandra-dao")
+ public ComponentCassandraDao componentCassandraDao() {
+ return new ComponentCassandraDao();
+ }
+
+ @Bean(name = "cassandra-client")
+ public CassandraClient cassandraClient() {
+ return new CassandraClient();
+ }
+
+ @Bean(name = "cacheManger-operation")
+ public CacheMangerOperation cacheMangerOperation() {
+ return new CacheMangerOperation();
+ }
+
+ @Bean(name = "component-cache")
+ public ComponentCache componentCache() {
+ return new ComponentCache();
+ }
+
+ @Bean(name = "input-operation")
+ public InputsOperation inputsOperation() {
+ return new InputsOperation();
+ }
+
+ /**
+ * Returns new instance of AuditCassandraDao
+ *
+ * @return
+ */
+ @Bean(name = "audit-cassandra-dao")
+ public AuditCassandraDao auditCassandraDao() {
+ return new AuditCassandraDao();
+ }
+
+ /**
+ * Returns new instance of UserBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "userBusinessLogic")
+ public IUserBusinessLogic userBusinessLogic() {
+ return new UserBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of UserAdminOperation
+ *
+ * @return
+ */
+ @Bean(name = "user-operation")
+ public IUserAdminOperation userOperation() {
+ return new UserAdminOperation();
+ }
+
+ /**
+ * Returns new instance of GraphLockOperation
+ *
+ * @return
+ */
+ @Bean(name = "graph-lock-operation")
+ public IGraphLockOperation graphLockOperation() {
+ return new GraphLockOperation();
+ }
+
+ /**
+ * Returns new instance of AuditingDao
+ *
+ * @return
+ */
+ @Bean(name = "auditingDao")
+ public AuditingDao auditingDao() {
+ return new AuditingDao();
+ }
+
+ /**
+ * Returns new instance of AuditingManager
+ *
+ * @return
+ */
+ @Bean(name = "auditingManager")
+ public IAuditingManager auditingManager() {
+ return new AuditingManager();
+ }
+
+ /**
+ * Returns new instance of ServiceBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "serviceBusinessLogic")
+ public ServiceBusinessLogic serviceBusinessLogic() {
+ return new ServiceBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of ComponentsUtils
+ *
+ * @return
+ */
+ @Bean(name = "componentUtils")
+ public ComponentsUtils componentUtils() {
+ return new ComponentsUtils();
+ }
+
+ /**
+ * Returns new instance of ToscaArtifactsAlignment
+ *
+ * @return
+ */
+ @Bean(name = "toscaArtifactsAlignment")
+ public ToscaArtifactsAlignment toscaArtifactsAlignment() {
+ return new ToscaArtifactsAlignment();
+ }
+
+ /**
+ * Returns new instance of ArtifactsBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "artifactBusinessLogic")
+ public ArtifactsBusinessLogic artifactBusinessLogic() {
+ return new ArtifactsBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of ResourceBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "resourceBusinessLogic")
+ public ResourceBusinessLogic resourceBusinessLogic() {
+ return new ResourceBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of LifecycleBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "lifecycleBusinessLogic")
+ public LifecycleBusinessLogic lifecycleBusinessLogic() {
+ return new LifecycleBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of ServiceDistributionArtifactsBuilder
+ *
+ * @return
+ */
+ @Bean(name = "serviceDistributionArtifactsBuilder")
+ public ServiceDistributionArtifactsBuilder serviceDistributionArtifactsBuilder() {
+ return new ServiceDistributionArtifactsBuilder();
+ }
+
+ /**
+ * Returns new instance of DistributionEngine
+ *
+ * @return
+ */
+ @Bean(name = "distributionEngine")
+ public IDistributionEngine distributionEngine() {
+ // This dependency is needed for initializing context but is not used
+ return null;
+ }
+
+ /**
+ * Returns new instance of ElasticSearchClient
+ *
+ * @return
+ */
+ @Bean(name = "elasticsearch-client")
+ public ElasticSearchClient elasticsearchClient() {
+ // This dependency is needed for initializing context but is not used
+ return null;
+ }
+
+ /**
+ * Returns new instance of ProductBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "productBusinessLogic")
+ public ProductBusinessLogic productBusinessLogic() {
+ return new ProductBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of ProductComponentInstanceBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "productComponentInstanceBusinessLogic")
+ public ProductComponentInstanceBusinessLogic productComponentInstanceBusinessLogic() {
+ return new ProductComponentInstanceBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of ToscaExportHandler
+ *
+ * @return
+ */
+ @Bean(name = "tosca-export-handler")
+ public ToscaExportHandler toscaExportHandler() {
+ return new ToscaExportHandler();
+ }
+
+ /**
+ * Returns new instance of CsarOperation
+ *
+ * @return
+ */
+ @Bean(name = "csar-operation")
+ public CsarOperation csarOperation() {
+ return new CsarOperation();
+ }
+
+ /**
+ * Returns new instance of OnboardingClient
+ *
+ * @return
+ */
+ @Bean(name = "onboarding-client")
+ public OnboardingClient onboardingClient() {
+ return new OnboardingClient();
+ }
+
+ /**
+ * Returns new instance of VFComponentInstanceBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "vfComponentInstanceBusinessLogic")
+ public VFComponentInstanceBusinessLogic vfComponentInstanceBusinessLogic() {
+ return new VFComponentInstanceBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of ResourceImportManager
+ *
+ * @return
+ */
+ @Bean(name = "resourceImportManager")
+ public ResourceImportManager resourceImportManager() {
+ return new ResourceImportManager();
+ }
+
+ /**
+ * Returns new instance of GroupBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "groupBusinessLogic")
+ public GroupBusinessLogic groupBusinessLogic() {
+ return new GroupBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of InputsBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "inputsBusinessLogic")
+ public InputsBusinessLogic inputsBusinessLogic() {
+ return new InputsBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of CompositionBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "compositionBusinessLogic")
+ public CompositionBusinessLogic compositionBusinessLogic() {
+ return new CompositionBusinessLogic();
+ }
+
+ /**
+ * Returns new instance of CsarUtils
+ *
+ * @return
+ */
+ @Bean(name = "csar-utils")
+ public CsarUtils csarUtils() {
+ return new CsarUtils();
+ }
+
+ /**
+ * Returns new instance of ServiceComponentInstanceBusinessLogic
+ *
+ * @return
+ */
+ @Bean(name = "serviceComponentInstanceBusinessLogic")
+ public ServiceComponentInstanceBusinessLogic serviceComponentInstanceBusinessLogic() {
+ return new ServiceComponentInstanceBusinessLogic();
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/DerivedFromAlignment.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/DerivedFromAlignment.java
new file mode 100644
index 0000000000..c1ddc4fee0
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/DerivedFromAlignment.java
@@ -0,0 +1,232 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+public class DerivedFromAlignment {
+ private static Logger log = LoggerFactory.getLogger(VfcNamingAlignment.class.getName());
+ private Map<String, String> newDerivedFromValuesHM = new HashMap<String, String>();
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+
+ public boolean alignDerivedFrom1604(String appConfigDir, String dataInputFileDir) {
+ log.debug("Started alignDerivedFrom1604 procedure..");
+ boolean result = false;
+ try {
+ if (!getDerivedFromValuesFromFile(dataInputFileDir)) {
+ log.error("Started alignDerivedFrom1604 procedure was failed. Missing data in the input data file.");
+ return result;
+ }
+ result = changeDerivedFrom();
+ } finally {
+ if (!result) {
+ titanGenericDao.rollback();
+ log.debug("**********************************************");
+ log.debug("alignDerivedFrom1604 procedure FAILED!!");
+ log.debug("**********************************************");
+ } else {
+ titanGenericDao.commit();
+ log.debug("**********************************************");
+ log.debug("alignDerivedFrom1604 procedure ended successfully!");
+ log.debug("**********************************************");
+ }
+ }
+ return result;
+ }
+
+ private boolean changeDerivedFrom() {
+ Map<String, ResourceMetadataData> resourcesHM = getLatestVersionsOfResources();
+ if (resourcesHM == null)
+ return false;
+ Map<String, ResourceMetadataData> derivedFromResourcesHM = getLatestCertifiedVersionsOfDerivedFromResources();
+ if (derivedFromResourcesHM == null)
+ return false;
+ return updateEdges(resourcesHM, derivedFromResourcesHM);
+ }
+
+ private boolean updateEdges(Map<String, ResourceMetadataData> resourcesHM,
+ Map<String, ResourceMetadataData> derivedFromResourcesHM) {
+ log.debug("Updating of Edges has been started..");
+ for (Entry<String, ResourceMetadataData> pair : resourcesHM.entrySet()) {
+ ResourceMetadataData curResource = pair.getValue();
+ String uniqeID = (String) curResource.getUniqueId();
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentResourceRes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), uniqeID,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource, ResourceMetadataData.class);
+ if (parentResourceRes.isLeft()) {
+ ImmutablePair<ResourceMetadataData, GraphEdge> value = parentResourceRes.left().value();
+ ResourceMetadataData parentResourceData = value.getKey();
+ log.debug("Deleting old relation..");
+ Either<GraphRelation, TitanOperationStatus> deletingRelationRes = titanGenericDao
+ .deleteRelation(curResource, parentResourceData, GraphEdgeLabels.DERIVED_FROM);
+ if (deletingRelationRes.isRight()) {
+ log.error("Couldn't delete relation from resource {} to resource {}, error: {}",
+ curResource.getMetadataDataDefinition().getName(),
+ parentResourceData.getMetadataDataDefinition().getName(),
+ deletingRelationRes.right().value());
+ return false;
+ }
+ ResourceMetadataData newDerivedFromResource = derivedFromResourcesHM.get(pair.getKey());
+ Either<GraphRelation, TitanOperationStatus> creatingRelationRes = titanGenericDao
+ .createRelation(curResource, newDerivedFromResource, GraphEdgeLabels.DERIVED_FROM, null);
+ if (creatingRelationRes.isRight()) {
+ log.error("Couldn't create relation from resource {} to resource {}, error: {}",
+ curResource.getMetadataDataDefinition().getName(),
+ newDerivedFromResource.getMetadataDataDefinition().getName(),
+ creatingRelationRes.right().value());
+ return false;
+ }
+ } else {
+ log.error("Couldn't get derived from resource for child resource {}, error: {}", pair.getKey(),
+ parentResourceRes.right().value());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private Map<String, ResourceMetadataData> getLatestCertifiedVersionsOfDerivedFromResources() {
+ log.debug("Getting latest certified versions of derived from resources according input file");
+ Map<String, ResourceMetadataData> resourcesHM = new HashMap<String, ResourceMetadataData>();
+ Map<String, Object> props = null;
+ for (Entry<String, String> pair : newDerivedFromValuesHM.entrySet()) {
+ props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty(), pair.getValue());
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> highestVersionResource = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (highestVersionResource.isRight()) {
+ log.error("Couldn't get resource {} from DB, error: {}", pair.getValue(),
+ highestVersionResource.right().value());
+ return null;
+ }
+ List<ResourceMetadataData> highestVersionResourceAL = highestVersionResource.left().value();
+ if (highestVersionResourceAL == null) {
+ log.error("Couldn't get resource {}. No resource found", pair.getValue());
+ return null;
+ }
+ ResourceMetadataData resource = highestVersionResourceAL.get(0);
+ String state = resource.getMetadataDataDefinition().getState();
+ if (!state.equals(LifecycleStateEnum.CERTIFIED.name())) {
+ log.error(
+ "alignDerivedFrom1604 procedure FAILED!! Derived from resource {} is not certified. Please certify manually and repeat the procedure.",
+ pair.getValue());
+ return null;
+ }
+ resourcesHM.put(pair.getKey(), resource);
+ }
+ return resourcesHM;
+ }
+
+ private Map<String, ResourceMetadataData> getLatestVersionsOfResources() {
+ log.debug("Getting latest versions of resources according input file");
+ Map<String, ResourceMetadataData> resourcesHM = new HashMap<String, ResourceMetadataData>();
+ ResourceMetadataData foundResource = null;
+ Map<String, Object> props = null;
+ for (Entry<String, String> pair : newDerivedFromValuesHM.entrySet()) {// filter
+ props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), pair.getKey());
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> highestVersionResource = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (highestVersionResource.isRight()) {
+ log.error("Couldn't get resource {} from DB, error: {}", pair.getKey(),
+ highestVersionResource.right().value());
+ return null;
+ }
+ List<ResourceMetadataData> highestVersionResourceAL = highestVersionResource.left().value();
+ if (highestVersionResourceAL == null) {
+ log.error("Couldn't get resource {}. No resource found", pair.getKey());
+ return null;
+ }
+ if (highestVersionResourceAL.size() > 2) {
+ log.error("Invalid response. Found more than two highest version resources with name {}.",
+ pair.getKey());
+ return null;
+ }
+ foundResource = highestVersionResourceAL.get(0);
+ if (highestVersionResourceAL.size() == 2) {
+ foundResource = foundResource.getMetadataDataDefinition().getState()
+ .equals(LifecycleStateEnum.CERTIFIED.name()) ? highestVersionResourceAL.get(1) : foundResource;
+ }
+ resourcesHM.put(pair.getKey(), foundResource);
+ }
+ return resourcesHM;
+ }
+
+ private boolean getDerivedFromValuesFromFile(String dataInputFileDir) {
+ BufferedReader br = null;
+ String curPair = null;
+ try {
+ br = new BufferedReader(new FileReader(dataInputFileDir));
+ while ((curPair = br.readLine()) != null) {
+ String[] pair = curPair.split(" ");
+ if (pair.length < 2) {
+ log.error(
+ "Expected at least two tokens in every line. Usage: <resource_name> <new_derived_from_name>");
+ return false;
+ }
+ String derivedFrom = pair[pair.length - 1];
+ String name = curPair.substring(0, curPair.length() - derivedFrom.length() - 1);
+ newDerivedFromValuesHM.put(name, derivedFrom);
+ }
+ return true;
+ } catch (FileNotFoundException e) {
+ log.error("Started alignDerivedFrom1604 procedure was failed. Missing input data file.", e);
+ } catch (IOException e) {
+ log.error("Started alignDerivedFrom1604 procedure was failed. The input data file is empty.", e);
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException e) {
+ log.debug("failed to close file reader", e);
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/GroupsAlignment.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/GroupsAlignment.java
new file mode 100644
index 0000000000..aebcddfee2
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/GroupsAlignment.java
@@ -0,0 +1,201 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+public class GroupsAlignment {
+
+ private static Logger log = LoggerFactory.getLogger(ServiceMigration.class.getName());
+
+ public static String INITIAL_VERSION = "1.0";
+ private static final String DEFAULT_GROUP_VF_MODULE = "org.openecomp.groups.VfModule";
+ private static final String MODULE = "::module-";
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+ @Autowired
+ protected IArtifactOperation artifactOperation;
+ @Autowired
+ protected IGroupOperation groupOperation;
+ @Autowired
+ protected GroupTypeOperation groupTypeOperation;
+
+ public boolean alignGroups(String appConfigDir) {
+
+ log.debug("Started the align groups procedure ...");
+ log.debug("Getting all resources with resources");
+ boolean result = false;
+ try {
+
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allVfResources = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, properties, ResourceMetadataData.class);
+
+ if (allVfResources.isRight()) {
+ log.error("Couldn't get VF resources from DB, error: {}", allVfResources.right().value());
+ result = false;
+ return result;
+ }
+ List<ResourceMetadataData> resourcesList = allVfResources.left().value();
+ if (resourcesList == null) {
+ log.error("Couldn't get VF resources from DB, no resources found");
+ result = false;
+ return result;
+ }
+ log.debug("Found {} VF resources", resourcesList.size());
+ for (ResourceMetadataData resource : resourcesList) {
+ result = createGroupIfContainsArtifacts(resource);
+ }
+ } finally {
+ if (!result) {
+ titanGenericDao.rollback();
+ log.debug("**********************************************");
+ log.debug("The align groups procedure FAILED!!");
+ log.debug("**********************************************");
+ } else {
+ titanGenericDao.commit();
+ log.debug("**********************************************");
+ log.debug("The align groups procedure ended successfully!");
+ log.debug("**********************************************");
+ }
+ }
+
+ return result;
+ }
+
+ private boolean createGroupIfContainsArtifacts(ResourceMetadataData resource) {
+
+ String uniqueId = resource.getMetadataDataDefinition().getUniqueId();
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> allArtifactsRes = artifactOperation
+ .getArtifacts(uniqueId, NodeTypeEnum.Resource, true);
+ if (allArtifactsRes.isRight()) {
+ log.error("Couldn't get resource artifacts from DB, error: {}", allArtifactsRes.right().value());
+ return false;
+ }
+ Map<String, ArtifactDefinition> artifactsHM = allArtifactsRes.left().value();
+ ArrayList<String> foundArtifactsAL = new ArrayList<String>();
+ for (ArtifactDefinition curArtifact : artifactsHM.values()) {
+ String atrifactType = curArtifact.getArtifactType();
+ if (atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType())
+ || atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT_NET.getType())
+ || atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType())) {
+ foundArtifactsAL.add(curArtifact.getUniqueId());
+ }
+ }
+ if (foundArtifactsAL.size() > 0) {
+ Either<List<GroupDefinition>, TitanOperationStatus> allGroupsRes = groupOperation
+ .getAllGroupsFromGraph(uniqueId, NodeTypeEnum.Resource);
+ int groupCounter = 1;
+ if (allGroupsRes.isRight()) {
+ if (allGroupsRes.right().value().name().equals(TitanOperationStatus.OK.name())
+ || allGroupsRes.right().value().name().equals(TitanOperationStatus.NOT_FOUND.name())) {
+ log.debug("Not found groups resource related to resource {}, response: {}",
+ resource.getMetadataDataDefinition().getName(), allGroupsRes.right().value());
+ } else {
+ log.error("Not found groups resource related to resource {}, DB error: {}",
+ resource.getMetadataDataDefinition().getName(), allGroupsRes.right().value());
+ return false;
+ }
+ } else if (allGroupsRes.left().value() != null && allGroupsRes.left().value().size() > 0) {
+ groupCounter += allGroupsRes.left().value().size();
+ for (GroupDefinition curGroup : allGroupsRes.left().value()) {
+ for (String curGroupArtifact : curGroup.getArtifacts()) {
+ if (foundArtifactsAL.contains(curGroupArtifact)) {
+ foundArtifactsAL.remove(curGroupArtifact);
+ }
+ }
+ }
+ }
+ if (foundArtifactsAL.size() > 0) {
+ GroupDefinition groupDefinition = new GroupDefinition();
+ groupDefinition.setName(resource.getMetadataDataDefinition().getName() + MODULE + groupCounter);
+ groupDefinition.setType(DEFAULT_GROUP_VF_MODULE);
+ groupDefinition.setArtifacts(foundArtifactsAL);
+ log.debug("Creating new group {} for VF resource {}", groupDefinition.getName(),
+ resource.getMetadataDataDefinition().getName());
+ return createGroup(resource.getUniqueId(), ComponentTypeEnum.RESOURCE, groupDefinition);
+
+ }
+ }
+ return true;
+ }
+
+ private boolean createGroup(Object uniqueId, ComponentTypeEnum componentType, GroupDefinition groupDefinition) {
+
+ NodeTypeEnum nodeTypeEnum = componentType.getNodeType();
+ String groupType = groupDefinition.getType();
+
+ Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeRes = groupTypeOperation
+ .getLatestGroupTypeByType(groupType, true);
+ if (getGroupTypeRes.isRight()) {
+ log.error("Couldn't get grouptype by type {} from DB, error: {}", groupType,
+ getGroupTypeRes.right().value());
+ return false;
+ }
+
+ GroupTypeDefinition groupTypeDefinition = getGroupTypeRes.left().value();
+
+ String invariantUUID = UniqueIdBuilder.buildInvariantUUID();
+ groupDefinition.setInvariantUUID(invariantUUID);
+ groupDefinition.setVersion(INITIAL_VERSION);
+ groupDefinition.setTypeUid(groupTypeDefinition.getUniqueId());
+
+ Either<GroupDefinition, StorageOperationStatus> addGroupToGraphRes = groupOperation.addGroup(nodeTypeEnum,
+ (String) uniqueId, groupDefinition, true);
+
+ if (addGroupToGraphRes.isRight()) {
+ log.error("Couldn't add group {} to graph, error: {}", groupDefinition.getName(),
+ addGroupToGraphRes.right().value());
+ return false;
+ }
+ log.debug("The group {} has been created", groupDefinition.getName());
+ return true;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationCategory.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationCategory.java
new file mode 100644
index 0000000000..5605f0980d
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationCategory.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.category.CategoryDataDefinition;
+
+public class MigrationCategory extends CategoryDataDefinition {
+ private String oldName;
+
+ List<MigrationSubCategory> subcategories;
+
+ public String getOldName() {
+ return oldName;
+ }
+
+ public void setOldName(String oldName) {
+ this.oldName = oldName;
+ }
+
+ public List<MigrationSubCategory> getSubcategories() {
+ return subcategories;
+ }
+
+ public void setSubcategories(List<MigrationSubCategory> subcategories) {
+ this.subcategories = subcategories;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationSubCategory.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationSubCategory.java
new file mode 100644
index 0000000000..f1886622be
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/MigrationSubCategory.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import org.openecomp.sdc.be.datatypes.category.SubCategoryDataDefinition;
+
+public class MigrationSubCategory extends SubCategoryDataDefinition {
+ private String oldName;
+
+ public String getOldName() {
+ return oldName;
+ }
+
+ public void setOldName(String oldName) {
+ this.oldName = oldName;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/ServiceMigration.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/ServiceMigration.java
new file mode 100644
index 0000000000..cb7f05ddfd
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/ServiceMigration.java
@@ -0,0 +1,1703 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import static java.nio.file.Files.readAllBytes;
+import static java.nio.file.Paths.get;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.AdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.impl.ComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.LifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.ProductOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ProductMetadataData;
+import org.openecomp.sdc.be.resources.data.RelationshipInstData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+import org.openecomp.sdc.be.utils.CommonBeUtils;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.yaml.snakeyaml.Yaml;
+
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public class ServiceMigration {
+
+ private static final String[] NORMATIVE_OLD_NAMES = {
+ "tosca.nodes.network.Network", "tosca.nodes.network.Port",
+ "tosca.nodes.BlockStorage", "tosca.nodes.Compute", "tosca.nodes.Container.Application",
+ "tosca.nodes.Container.Runtime", "tosca.nodes.Database", "tosca.nodes.DBMS", "tosca.nodes.LoadBalancer",
+ "tosca.nodes.ObjectStorage", "tosca.nodes.Root", "tosca.nodes.SoftwareComponent",
+ "tosca.nodes.WebApplication", "tosca.nodes.WebServer", };
+
+ private static Logger log = LoggerFactory.getLogger(ServiceMigration.class.getName());
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+ @Autowired
+ protected ResourceOperation resourceOperation;
+ @Autowired
+ protected ServiceOperation serviceOperation;
+ @Autowired
+ protected ProductOperation productOperation;
+ @Autowired
+ protected LifecycleOperation lifecycleOperaion;
+ @Autowired
+ protected PropertyOperation propertyOperation;
+ @Autowired
+ protected AdditionalInformationOperation additionalInformationOperation;
+ @Autowired
+ protected ComponentInstanceOperation componentInstanceOperaion;
+ @Autowired
+ protected IElementOperation elementOperation;
+
+ public boolean migrate1602to1604(String appConfigDir) {
+
+ boolean result = false;
+
+ try {
+
+ if (!addResourceCounterToResources()) {
+ log.debug("Failed to update resource instance counter on resources");
+ result = false;
+ return result;
+ }
+ if (!updateComponentInstanceType()) {
+ log.debug("Failed to update component instance type");
+ result = false;
+ return result;
+ }
+ // fix VF
+ if (!fixDerivedVf()) {
+ log.debug("Failed to fix VFs");
+ result = false;
+ return result;
+ }
+ // update instances and relation
+ if (!updateCalculatedEdges()) {
+ log.debug("Failed to update calculated edges for VF instances");
+ result = false;
+ return result;
+ }
+ // update instances and relation
+ if (!updateRelations()) {
+ log.debug("Failed to update Instance And Relations in services");
+ result = false;
+ return result;
+ }
+ if (!updateCategories(appConfigDir)) {
+ log.debug("Failed to update categories");
+ result = false;
+ return result;
+ }
+
+ if (!AllowMultipleHeats.removeAndUpdateHeatPlaceHolders(titanGenericDao, log, true)) {
+ log.error("Failed to update heat place holders");
+ result = false;
+ return result;
+ }
+
+ if (!AddGroupUuid.addGroupUuids(titanGenericDao, log, true)) {
+ log.error("Failed to update group UUIDs");
+ result = false;
+ return result;
+ }
+
+ result = true;
+ } finally {
+ if (!result) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ return result;
+ }
+
+ private boolean updateCategories(String appConfigDir) {
+ // String categoryMigrationFile = appConfigDir + File.separator +
+ // "categoryMigration.yaml";
+ String categoryMigrationFile = appConfigDir + "categoryMigration.yaml";
+
+ Map<String, List<MigrationCategory>> categoriesFromYml;
+ try {
+ categoriesFromYml = createCategoriesFromYml(categoryMigrationFile);
+ if (categoriesFromYml == null || categoriesFromYml.isEmpty()) {
+ log.debug("updateCategories failed to load categories form migration file {}", categoryMigrationFile);
+ return false;
+ }
+ } catch (Exception e) {
+ log.debug("Failed to load category migration file :{} error: {}",categoryMigrationFile, e);
+ return false;
+ }
+ for (Map.Entry<String, List<MigrationCategory>> entry : categoriesFromYml.entrySet()) {
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(entry.getKey());
+ if (componentType != null) {
+ switch (componentType) {
+ case RESOURCE:
+ if (updateResourceCategories(entry.getValue()) == false) {
+ log.debug("updateCategories failed to update resource categories");
+ return false;
+ }
+ break;
+ case SERVICE:
+ if (updateServiceCategories(entry.getValue()) == false) {
+ log.debug("updateCategories failed to update service categories");
+ return false;
+ }
+ break;
+ default:
+ log.debug("updateCategories no changes for categories from type {}", componentType);
+ }
+ } else {
+ log.debug("updateCategories failed not supported component file in migration categories file {}", entry.getKey());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean updateServiceCategories(List<MigrationCategory> categories) {
+ log.debug("updateServiceCategories STARTED");
+ Either<List<CategoryDefinition>, ActionStatus> serviceCategories = elementOperation
+ .getAllCategories(NodeTypeEnum.ServiceNewCategory, true);
+ if (serviceCategories.isRight()) {
+ log.debug("updateServiceCategories failed fetch all service categories ,error: {}", serviceCategories.right().value());
+ return false;
+ }
+ for (MigrationCategory newCat : categories) {
+
+ if (newCat.getOldName() == null) {
+ // add new
+ boolean exist = false;
+ for (CategoryDefinition catInDB : serviceCategories.left().value()) {
+ if (newCat.getName().equals(catInDB.getName())) {
+ exist = true;
+ break;
+ }
+ }
+ if (!exist) {
+ CategoryDefinition categoryDefinition = new CategoryDefinition(newCat);
+ Either<CategoryDefinition, ActionStatus> result = elementOperation
+ .createCategory(categoryDefinition, NodeTypeEnum.ServiceNewCategory, true);
+ if (result.isRight()) {
+ log.debug("Failed to create service category {}, error: {}", categoryDefinition, result.right().value());
+ return false;
+ }
+ log.debug("service category {} created", categoryDefinition);
+ }
+ } else {
+ // update exist
+ for (CategoryDefinition catInDB : serviceCategories.left().value()) {
+ if (newCat.getOldName().equals(catInDB.getName())) {
+ Either<CategoryData, TitanOperationStatus> updateSingleResult = updateSingleResourceCategory(
+ newCat, NodeTypeEnum.ServiceNewCategory);
+ if (updateSingleResult.isRight()) {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ }
+ log.debug("updateServiceCategories ENDED");
+ return true;
+ }
+
+ private Either<CategoryData, TitanOperationStatus> updateSingleResourceCategory(MigrationCategory newCat,
+ NodeTypeEnum nodetype) {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.NAME.getProperty(), newCat.getOldName());
+ Either<List<CategoryData>, TitanOperationStatus> categoryEither = titanGenericDao.getByCriteria(nodetype,
+ properties, CategoryData.class);
+ if (categoryEither.isRight() && categoryEither.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to get {} categories, for name {} error {}", nodetype, newCat.getOldName(),
+ categoryEither.right().value());
+ return Either.right(categoryEither.right().value());
+ }
+ List<CategoryData> categoryList = (categoryEither.isLeft() ? categoryEither.left().value() : null);
+ if (categoryList == null) {
+ log.debug("No {} categories, for name {} error {}", nodetype, newCat.getOldName());
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ CategoryData categoryData = categoryList.get(0);
+ categoryData.getCategoryDataDefinition().setName(newCat.getName());
+ categoryData.getCategoryDataDefinition().setIcons(newCat.getIcons());
+ categoryData.getCategoryDataDefinition()
+ .setNormalizedName(ValidationUtils.normalizeCategoryName4Uniqueness(newCat.getName()));
+ Either<CategoryData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(categoryData,
+ CategoryData.class);
+ if (updateNode.isRight()) {
+ log.debug("Failed to update {} category {} error {}", nodetype, categoryData, updateNode.right().value());
+ return Either.right(updateNode.right().value());
+ }
+ log.debug("Update {} category {} ", nodetype, categoryData);
+ return Either.left(updateNode.left().value());
+ }
+
+ private boolean updateResourceCategories(List<MigrationCategory> categories) {
+ log.debug("updateResourceCategories STARTED");
+ Either<List<CategoryDefinition>, ActionStatus> resourceCategories = elementOperation
+ .getAllCategories(NodeTypeEnum.ResourceNewCategory, true);
+ if (resourceCategories.isRight()) {
+ log.debug("updateResourceCategories failed fetch all resource categories ,error "
+ + resourceCategories.right().value());
+ return false;
+ }
+ for (MigrationCategory newCat : categories) {
+ if (newCat.getOldName() == null) {
+ // add new
+ // check if already created in previous running
+ boolean exist = false;
+ for (CategoryDefinition catInDB : resourceCategories.left().value()) {
+ if (newCat.getName().equals(catInDB.getName())) {
+ exist = true;
+ }
+ }
+ if (!exist) {
+ CategoryDefinition categoryDefinition = new CategoryDefinition(newCat);
+ Either<CategoryDefinition, ActionStatus> resultCat = elementOperation
+ .createCategory(categoryDefinition, NodeTypeEnum.ResourceNewCategory, true);
+ if (resultCat.isRight()) {
+ log.debug("Failed to create resource category {}, error: {}", categoryDefinition, resultCat.right().value());
+ return false;
+ }
+ log.debug("resource category {} created", categoryDefinition);
+
+ List<MigrationSubCategory> nSubCat = newCat.getSubcategories();
+ List<MigrationSubCategory> newSubcat = nSubCat;
+ List<MigrationSubCategory> subcategories = newSubcat;
+ for (MigrationSubCategory msubcat : subcategories) {
+ SubCategoryDefinition subcat = new SubCategoryDefinition(msubcat);
+ Either<SubCategoryDefinition, ActionStatus> resultSubcat = elementOperation.createSubCategory(
+ resultCat.left().value().getUniqueId(), subcat, NodeTypeEnum.ResourceSubcategory, true);
+ if (resultSubcat.isRight()) {
+ log.debug("Failed to create resource sub category {} error: {}", subcat, resultSubcat.right().value());
+ return false;
+ }
+ log.debug("resource sub category {} created for category {}", categoryDefinition, resultCat.left().value().getName());
+ }
+ }
+ } else {
+ // update exist
+ for (CategoryDefinition catInDB : resourceCategories.left().value()) {
+ if (newCat.getOldName().equals(catInDB.getName())) {
+ Either<CategoryData, TitanOperationStatus> updateSingleResult = updateSingleResourceCategory(
+ newCat, NodeTypeEnum.ResourceNewCategory);
+ if (updateSingleResult.isRight()) {
+ return false;
+ }
+
+ CategoryData categoryData = updateSingleResult.left().value();
+ for (MigrationSubCategory migSubCat : newCat.getSubcategories()) {
+ if (migSubCat.getOldName() == null) {
+ // create new one
+ boolean existSub = false;
+ for (SubCategoryDefinition subCatInDb : catInDB.getSubcategories()) {
+ if (subCatInDb.getName().equals(migSubCat.getName())) {
+ existSub = true;
+ }
+ }
+ if (!existSub) {
+ SubCategoryDefinition subcat = new SubCategoryDefinition(migSubCat);
+
+ Either<SubCategoryDefinition, ActionStatus> resultSubcat = elementOperation
+ .createSubCategory((String) categoryData.getUniqueId(), subcat,
+ NodeTypeEnum.ResourceSubcategory, true);
+ if (resultSubcat.isRight()) {
+ log.debug("Failed to create resource sub category {} error: {}", subcat, resultSubcat.right().value());
+ return false;
+ }
+ log.debug("resource sub category {}", categoryData, resultSubcat.left().value().getName());
+ }
+ } else {
+ if (updateSingleSubCategory(newCat, migSubCat,
+ updateSingleResult.left().value()) == false) {
+ return false;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean updateSingleSubCategory(MigrationCategory newCat, MigrationSubCategory migSubCat,
+ CategoryData categoryData) {
+
+ Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, TitanOperationStatus> subcategories = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceNewCategory),
+ (String) categoryData.getUniqueId(), GraphEdgeLabels.SUB_CATEGORY,
+ NodeTypeEnum.ResourceSubcategory, SubCategoryData.class);
+
+ if (subcategories.isRight()) {
+ log.debug("Failed to get resource sub categories, for name {} error {}", newCat.getOldName(),
+ subcategories.right().value());
+ return false;
+ }
+
+ for (ImmutablePair<SubCategoryData, GraphEdge> pair : subcategories.left().value()) {
+ if (pair.getKey().getSubCategoryDataDefinition().getName().equals(migSubCat.getOldName())) {
+ SubCategoryData subCategoryData = pair.getKey();
+ subCategoryData.getSubCategoryDataDefinition().setName(migSubCat.getName());
+ subCategoryData.getSubCategoryDataDefinition().setIcons(migSubCat.getIcons());
+ subCategoryData.getSubCategoryDataDefinition()
+ .setNormalizedName(ValidationUtils.normalizeCategoryName4Uniqueness(migSubCat.getName()));
+ Either<SubCategoryData, TitanOperationStatus> updateSubNode = titanGenericDao
+ .updateNode(subCategoryData, SubCategoryData.class);
+ if (updateSubNode.isRight()) {
+ log.debug("Failed to update resource sub category {} error {}", subCategoryData,
+ updateSubNode.right().value());
+ return false;
+ }
+ log.debug("Update resource subcategory category {} ", subCategoryData);
+ break;
+ }
+ }
+ return true;
+ }
+
+ private Map<String, List<MigrationCategory>> createCategoriesFromYml(String categoriesTypesYml) {
+ String yamlAsString;
+ try {
+ yamlAsString = new String(readAllBytes(get(categoriesTypesYml)));
+ } catch (Exception e) {
+ log.debug("Failed to load category import file exception : ", e);
+ return null;
+ }
+
+ log.debug("received yaml: {}", yamlAsString);
+
+ Map<String, Object> toscaJson = (Map<String, Object>) new Yaml().load(yamlAsString);
+ Map<String, List<MigrationCategory>> allCategories = new HashMap<>();
+
+ Iterator<Entry<String, Object>> categoryEntryItr = toscaJson.entrySet().iterator();
+ while (categoryEntryItr.hasNext()) {
+ Entry<String, Object> categoryTypeEntry = categoryEntryItr.next();
+ String categoryType = categoryTypeEntry.getKey();
+ List<MigrationCategory> categoriesPerType = null;
+ Map<String, Object> categoryPerType = null;
+ switch (categoryType) {
+ case ComponentTypeEnum.SERVICE_PARAM_NAME:
+ categoryPerType = (Map<String, Object>) categoryTypeEntry.getValue();
+ categoriesPerType = createServiceCategories(categoryPerType);
+ break;
+ case ComponentTypeEnum.RESOURCE_PARAM_NAME:
+ categoryPerType = (Map<String, Object>) categoryTypeEntry.getValue();
+ categoriesPerType = createResourceCategories(categoryPerType);
+ break;
+ case ComponentTypeEnum.PRODUCT_PARAM_NAME:
+ // TODO
+ break;
+ default:
+ log.debug("Not supported category type - {}", categoryType);
+ break;
+ }
+ if (categoriesPerType != null) {
+ allCategories.put(categoryType, categoriesPerType);
+ }
+ }
+ return allCategories;
+ }
+
+ private List<MigrationCategory> createServiceCategories(Map<String, Object> categories) {
+ List<MigrationCategory> categroiesDef = new ArrayList<>();
+ String catName = null;
+ List<String> icons = null;
+ String oldName = null;
+ for (Entry<String, Object> entry : categories.entrySet()) {
+ MigrationCategory catDef = new MigrationCategory();
+ Map<String, Object> category = (Map<String, Object>) entry.getValue();
+ catName = (String) category.get("name");
+ catDef.setName(catName);
+ icons = (List<String>) category.get("icons");
+ catDef.setIcons(icons);
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(catName);
+ catDef.setNormalizedName(normalizedName);
+ oldName = (String) category.get("oldName");
+ catDef.setOldName(oldName);
+ categroiesDef.add(catDef);
+ }
+
+ return categroiesDef;
+ }
+
+ private List<MigrationCategory> createResourceCategories(Map<String, Object> categoryPerType) {
+ List<MigrationCategory> categroiesDef = new ArrayList<>();
+ for (Map.Entry<String, Object> entry : categoryPerType.entrySet()) {
+ Map<String, Object> category = (Map<String, Object>) entry.getValue();
+ MigrationCategory catDef = new MigrationCategory();
+ String catName = (String) category.get("name");
+ catDef.setName(catName);
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(catName);
+ catDef.setNormalizedName(normalizedName);
+ String oldName = (String) category.get("oldName");
+ catDef.setOldName(oldName);
+
+ Map<String, Object> subcategories = (Map<String, Object>) category.get("subcategories");
+ List<MigrationSubCategory> subcateDef = new ArrayList<>();
+ for (Entry<String, Object> subcategory : subcategories.entrySet()) {
+ Map<String, Object> subcategoryInfo = (Map<String, Object>) subcategory.getValue();
+ MigrationSubCategory subDef = new MigrationSubCategory();
+ String subcategoryName = (String) subcategoryInfo.get("name");
+ subDef.setName(subcategoryName);
+ List<String> subcategoryIcons = (List<String>) subcategoryInfo.get("icons");
+ subDef.setIcons(subcategoryIcons);
+ normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(subcategoryName);
+ subDef.setNormalizedName(normalizedName);
+ oldName = (String) subcategoryInfo.get("oldName");
+ subDef.setOldName(oldName);
+
+ subcateDef.add(subDef);
+ }
+
+ catDef.setSubcategories(subcateDef);
+ categroiesDef.add(catDef);
+ }
+ return categroiesDef;
+ }
+
+ private boolean updateCalculatedEdges() {
+ log.debug("update calculated edges STARTED");
+
+ Either<List<ComponentInstanceData>, TitanOperationStatus> allInstances = titanGenericDao
+ .getByCriteria(NodeTypeEnum.ResourceInstance, null, ComponentInstanceData.class);
+ if (allInstances.isRight() && !allInstances.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug(
+ "updateCalculatedEdges failed fetch all resource instances ,error " + allInstances.right().value());
+ return false;
+ }
+ if (allInstances.isRight() && allInstances.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("updateCalculatedEdges - no VFs");
+ return true;
+ }
+ List<ComponentInstanceData> listOfInstances = allInstances.left().value();
+ for (ComponentInstanceData instance : listOfInstances) {
+ // check if already have calculated edges
+ log.debug("start handle instance {}", instance.getUniqueId());
+ boolean needProcess = true;
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> vfci = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance),
+ instance.getUniqueId(), GraphEdgeLabels.CALCULATED_CAPABILITY, NodeTypeEnum.Capability,
+ CapabilityData.class);
+ if (vfci.isRight()) {
+ if (!vfci.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("createCalculatedCapabilitiesForInstance failed to fetch instance for resource {} error: {}",
+ instance.getComponentInstDataDefinition().getComponentUid(),
+ vfci.right().value());
+ return false;
+ }
+ } else {
+ if (vfci.left().value().size() > 0) {
+ needProcess = false;
+ }
+ }
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> vfciReq = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance),
+ instance.getUniqueId(), GraphEdgeLabels.CALCULATED_REQUIREMENT, NodeTypeEnum.Requirement,
+ RequirementData.class);
+ if (vfciReq.isRight()) {
+ if (!vfciReq.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("createCalculatedCapabilitiesForInstance failed to fetch instance for resource {} error: {}",
+ instance.getComponentInstDataDefinition().getComponentUid(),
+ vfciReq.right().value());
+ return false;
+ }
+ } else {
+ if (vfciReq.left().value().size() > 0) {
+ needProcess = false;
+ }
+ }
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> vfciReqFF = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance),
+ instance.getUniqueId(), GraphEdgeLabels.CALCULATED_REQUIREMENT_FULLFILLED,
+ NodeTypeEnum.Requirement, RequirementData.class);
+ if (vfciReqFF.isRight()) {
+
+ if (!vfciReqFF.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("createCalculatedCapabilitiesForInstance failed to fetch instance for resource "
+ + instance.getComponentInstDataDefinition().getComponentUid() + " error "
+ + vfciReqFF.right().value());
+ return false;
+ }
+ } else {
+ if (vfciReqFF.left().value().size() > 0) {
+ needProcess = false;
+ }
+ }
+
+ if (needProcess == false) {
+ log.debug("updateCalculatedEdges : for instance {} calculated capabilty/requirement already created", instance.getUniqueId());
+ continue;
+ }
+ String originId = instance.getComponentInstDataDefinition().getComponentUid();
+ Either<Resource, StorageOperationStatus> resourceE = resourceOperation.getResource(originId, true);
+ if (resourceE.isRight()) {
+ log.debug("updateCalculatedEdges failed to fetch origin resource with id {} error: {}", originId, resourceE.right().value());
+ return false;
+ }
+ Resource resource = resourceE.left().value();
+ Map<String, List<RequirementDefinition>> requirements = resource.getRequirements();
+ if (createCalculatedRequirementsForInstance(instance, requirements) != true) {
+ return false;
+ }
+ Map<String, List<CapabilityDefinition>> capabilities = resource.getCapabilities();
+ if (createCalculatedCapabilitiesForInstance(instance, capabilities) != true) {
+ return false;
+ }
+ log.debug("finish handle instance {}", instance.getUniqueId());
+ }
+ log.debug("update calculated edges ENDED");
+ return true;
+ }
+
+ private boolean createCalculatedCapabilitiesForInstance(ComponentInstanceData instance,
+ Map<String, List<CapabilityDefinition>> capabilities) {
+ for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
+ for (CapabilityDefinition capability : entry.getValue()) {
+ Either<CapabilityData, TitanOperationStatus> capNode = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capability.getUniqueId(),
+ CapabilityData.class);
+ if (capNode.isRight()) {
+ log.debug("createCalculatedCapabilitiesForInstance failed to fetch capability node with id "
+ + capability.getUniqueId() + " error " + capNode.right().value());
+ return false;
+ }
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capability.getName());
+ if (fillEdgeProperties(instance, props) != true) {
+ return false;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(instance,
+ capNode.left().value(), GraphEdgeLabels.CALCULATED_CAPABILITY, props);
+ if (createRelation.isRight()) {
+ TitanOperationStatus titanOperationStatus = createRelation.right().value();
+ log.debug(
+ "Failed to create calculated requirement from component instance {} to requirement {}, error: {}",
+ instance.getUniqueId(), capNode.left().value().getUniqueId(), titanOperationStatus);
+ return false;
+ }
+ log.debug("CALCULATED_CAPABILITY was created from {} to {} with props: {}", capNode.left().value().getUniqueId(), instance.getUniqueId(), props);
+ }
+ }
+ return true;
+ }
+
+ private boolean fillEdgeProperties(ComponentInstanceData instance, Map<String, Object> props) {
+ if (instance.getComponentInstDataDefinition().getOriginType().equals(OriginTypeEnum.VF)) {
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> vfci = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource),
+ instance.getComponentInstDataDefinition().getComponentUid(), GraphEdgeLabels.RESOURCE_INST,
+ NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+ if (vfci.isRight()) {
+ log.debug("createCalculatedCapabilitiesForInstance failed to fetch instance for resource {} error: {}",
+ instance.getComponentInstDataDefinition().getComponentUid(),
+ vfci.right().value());
+ return false;
+ }
+ ImmutablePair<ComponentInstanceData, GraphEdge> immutablePair = vfci.left().value().get(0);
+ String vfciId = immutablePair.getLeft().getUniqueId();
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), vfciId);
+ props.put(GraphEdgePropertiesDictionary.SOURCE.getProperty(),
+ immutablePair.getLeft().getComponentInstDataDefinition().getComponentUid());
+
+ } else {
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), instance.getUniqueId());
+ props.put(GraphEdgePropertiesDictionary.SOURCE.getProperty(),
+ instance.getComponentInstDataDefinition().getComponentUid());
+ }
+ return true;
+ }
+
+ private boolean createCalculatedRequirementsForInstance(ComponentInstanceData instance,
+ Map<String, List<RequirementDefinition>> requirements) {
+ for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
+ for (RequirementDefinition requirement : entry.getValue()) {
+ Either<RequirementData, TitanOperationStatus> reqNode = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement), requirement.getUniqueId(),
+ RequirementData.class);
+ if (reqNode.isRight()) {
+ log.debug("updateCalculatedEdges failed to fetch requirement node with id {} error: {}", requirement.getUniqueId(), reqNode.right().value());
+ return false;
+ }
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), requirement.getName());
+
+ if (fillEdgeProperties(instance, props) != true) {
+ return false;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(instance,
+ reqNode.left().value(), GraphEdgeLabels.CALCULATED_REQUIREMENT, props);
+ if (createRelation.isRight()) {
+ TitanOperationStatus titanOperationStatus = createRelation.right().value();
+ log.debug(
+ "Failed to create calculated requirement from component instance {} to requirement {}, error: {}",
+ instance.getUniqueId(), reqNode.left().value().getUniqueId(), titanOperationStatus);
+ return false;
+ }
+ log.debug("CALCULATED_REQUIREMENT was created from {} to {} with props: {}", reqNode.left().value().getUniqueId(), instance.getUniqueId(), props);
+ }
+ }
+ return true;
+ }
+
+ private boolean updateRelations() {
+ log.debug("update relations and edges STARTED");
+ Either<List<RelationshipInstData>, TitanOperationStatus> allRelations = titanGenericDao
+ .getByCriteria(NodeTypeEnum.RelationshipInst, null, RelationshipInstData.class);
+ if (allRelations.isRight()) {
+ if (allRelations.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("updateRelations : No relations to update. updateRelations ENDED");
+ return true;
+ }
+ log.debug("updateRelations : failed to fetch all relation nodes , error ", allRelations.right().value());
+ return false;
+ }
+ for (RelationshipInstData rel : allRelations.left().value()) {
+ // rel.set
+ if (rel.getCapabilityOwnerId() != null && rel.getRequirementOwnerId() != null) {
+ log.debug("updateRelations : for relation {} all fields alredy fixed -> {}", rel.getUniqueId(), rel);
+ continue;
+ }
+ // update capability parameters
+ if (updateCapabiltyFieldsInRelation(rel) != true) {
+ return false;
+ }
+
+ // update requirement parameters and set calculated edge to full
+ // filled
+ if (updateRequirementFieldsInRelation(rel) != true) {
+ return false;
+ }
+
+ Either<RelationshipInstData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(rel,
+ RelationshipInstData.class);
+ if (updateNode.isRight()) {
+ log.debug("updateRelations : failed to update relation node with id {}, error: {}", rel.getUniqueId(), updateNode.right().value());
+ return false;
+ }
+ log.debug("Relations was updated with values {}", rel);
+ }
+ log.debug("update relations and edges ENDED");
+ return true;
+ }
+
+ private boolean updateRequirementFieldsInRelation(RelationshipInstData rel) {
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> reqInst = titanGenericDao
+ .getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.RelationshipInst), rel.getUniqueId(),
+ GraphEdgeLabels.RELATIONSHIP_INST, NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+ if (reqInst.isRight()) {
+ log.debug("updateRelations : failed to fetch capabilty component instance for relation {}, error: {}", rel.getUniqueId(), reqInst.right().value());
+ return false;
+ }
+ ComponentInstanceData requirementInstanceData = reqInst.left().value().getLeft();
+ ComponentInstanceDataDefinition reqRI = requirementInstanceData.getComponentInstDataDefinition();
+ if (reqRI.getOriginType().equals(OriginTypeEnum.VF)) {
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> vfcInstInOrigVf = titanGenericDao
+ .getChildByEdgeCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource),
+ reqRI.getComponentUid(), GraphEdgeLabels.RESOURCE_INST, NodeTypeEnum.ResourceInstance,
+ ComponentInstanceData.class, null);
+ if (vfcInstInOrigVf.isRight()) {
+ log.debug("updateRelations : failed to fetch VFC instance in origin VF with id {}, error: {}", reqRI.getComponentUid(), vfcInstInOrigVf.right().value());
+ return false;
+ }
+ rel.setRequirementOwnerId(vfcInstInOrigVf.left().value().getLeft().getUniqueId());
+ } else {
+ rel.setRequirementOwnerId(reqRI.getUniqueId());
+ }
+ // get vertex
+ Either<TitanVertex, TitanOperationStatus> vertexReqRI = titanGenericDao.getVertexByProperty(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), requirementInstanceData.getUniqueId());
+ if (vertexReqRI.isRight()) {
+ log.debug("updateRelations : failed to fetch veterx for instance {}, error: {}", requirementInstanceData.getUniqueId(), vertexReqRI.right().value());
+ return false;
+ }
+ String[] splitIds = rel.getUniqueId().split("\\.");
+ String reqName = splitIds[splitIds.length - 1];
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), reqName);
+ Either<List<Edge>, TitanOperationStatus> edgesForNode = titanGenericDao
+ .getOutgoingEdgesByCriteria(vertexReqRI.left().value(), GraphEdgeLabels.CALCULATED_REQUIREMENT, props);
+ if (edgesForNode.isRight()) {
+ log.debug("updateRelations : failed to fetch edges for instance {}, error: {}", requirementInstanceData.getUniqueId(), edgesForNode.right().value());
+ return false;
+ }
+ Edge edge = edgesForNode.left().value().get(0);
+ String reqId = (String) titanGenericDao.getProperty((TitanVertex) edge.inVertex(),
+ GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ rel.setRequirementId(reqId);
+
+ // change edge label
+ TitanEdge newEdge = (TitanEdge) vertexReqRI.left().value()
+ .addEdge(GraphEdgeLabels.CALCULATED_REQUIREMENT_FULLFILLED.getProperty(), edge.inVertex());
+ titanGenericDao.setProperties(newEdge, titanGenericDao.getProperties(edge));
+ edge.remove();
+
+ log.debug("Edge was changed to CALCULATED_REQUIREMENT_FULLFILLED for relation between {} and {}", reqId, requirementInstanceData.getUniqueId());
+
+ return true;
+ }
+
+ public boolean updateCapabiltyFieldsInRelation(RelationshipInstData rel) {
+ // update capability parameters
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> capInst = titanGenericDao
+ .getChildByEdgeCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.RelationshipInst),
+ rel.getUniqueId(), GraphEdgeLabels.CAPABILITY_NODE, NodeTypeEnum.ResourceInstance,
+ ComponentInstanceData.class, null);
+ if (capInst.isRight()) {
+ log.debug("updateRelations : failed to fetch capabilty component instance for relation {}, error: {}", rel.getUniqueId(), capInst.right().value());
+ return false;
+ }
+ ComponentInstanceData capabiltyInstanceData = capInst.left().value().getLeft();
+ ComponentInstanceDataDefinition capRI = capabiltyInstanceData.getComponentInstDataDefinition();
+ if (capRI.getOriginType().equals(OriginTypeEnum.VF)) {
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> vfcInstInOrigVf = titanGenericDao
+ .getChildByEdgeCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource),
+ capRI.getComponentUid(), GraphEdgeLabels.RESOURCE_INST, NodeTypeEnum.ResourceInstance,
+ ComponentInstanceData.class, null);
+ if (vfcInstInOrigVf.isRight()) {
+ log.debug("updateRelations : failed to fetch VFC instance in origin VF with id {}, error: {}", capRI.getComponentUid(), vfcInstInOrigVf.right().value());
+ return false;
+ }
+ rel.setCapabilityOwnerId(vfcInstInOrigVf.left().value().getLeft().getUniqueId());
+ } else {
+ rel.setCapabilityOwnerId(capRI.getUniqueId());
+ }
+
+ // get vertex
+ Either<TitanVertex, TitanOperationStatus> vertexCapRI = titanGenericDao.getVertexByProperty(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), capabiltyInstanceData.getUniqueId());
+ if (vertexCapRI.isRight()) {
+ log.debug("updateRelations : failed to fetch veterx for instance {}, error: {}", capabiltyInstanceData.getUniqueId(), vertexCapRI.right().value());
+ return false;
+ }
+ // String[] splitIds = rel.getUniqueId().split("\\.");
+ String capName = (String) capInst.left().value().getRight().getProperties()
+ .get(GraphEdgePropertiesDictionary.NAME.getProperty());// splitIds[splitIds.length
+ // - 1];
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capName);
+ Either<List<Edge>, TitanOperationStatus> edgesForNode = titanGenericDao
+ .getOutgoingEdgesByCriteria(vertexCapRI.left().value(), GraphEdgeLabels.CALCULATED_CAPABILITY, props);
+ if (edgesForNode.isRight()) {
+ log.debug("updateRelations : failed to fetch edges for instance {}, error: {}", capabiltyInstanceData.getUniqueId(), edgesForNode.right().value());
+ return false;
+ }
+ Edge edge = edgesForNode.left().value().get(0);
+ String capId = (String) titanGenericDao.getProperty((TitanVertex) edge.inVertex(),
+ GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ rel.setCapabiltyId(capId);
+
+ return true;
+ }
+
+ // private boolean fixDerivedFv() {
+ // Map<String, Object> props = new HashMap<String, Object>();
+ // props.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(),
+ // ResourceTypeEnum.VF.name());
+ // Either<List<ResourceMetadataData>, TitanOperationStatus> allVF =
+ // titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props,
+ // ResourceMetadataData.class);
+ // if (allVF.isRight() &&
+ // !allVF.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ // log.debug("fixDerivedFv failed fetch all VF resources,error {}", allVF.right().value());
+ // return false;
+ // }
+ // if ( allVF.right().value().equals(TitanOperationStatus.NOT_FOUND) ){
+ // log.debug("fixDerivedFv - no VF");
+ // return true;
+ // }
+ // Set<String> finishedResources = new HashSet<>();
+ //
+ // for (ResourceMetadataData metadata : allVF.left().value()) {
+ // ComponentMetadataDataDefinition metadataDD =
+ // metadata.getMetadataDataDefinition();
+ //
+ // if (!finishedResources.contains(metadataDD.getUniqueId())) {
+ // Either<List<String>, StorageOperationStatus> processedIds =
+ // handleVfGroup(metadata);
+ // if (processedIds.isRight()) {
+ // log.debug("fixDerivedFv failed to process FV group {}", processedIds.right().value());
+ // return false;
+ // }
+ // finishedResources.addAll(processedIds.left().value());
+ // }
+ // }
+ // return true;
+ // }
+
+ private Either<List<String>, StorageOperationStatus> handleVfGroup(ResourceMetadataData metadata) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), metadata.getMetadataDataDefinition().getName());
+
+ List<String> finished = new ArrayList<>();
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allVFByName = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (allVFByName.isRight()) {
+ log.debug("fixDerivedFv failed fetch all VF resources,error {}", allVFByName.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ Set<String> nonDuplicatedId = new HashSet<>();
+ String uuid10 = null;
+ for (ResourceMetadataData mdata : allVFByName.left().value()) {
+ String version = mdata.getMetadataDataDefinition().getVersion();
+ if (version.equals("1.0")) {
+ uuid10 = mdata.getMetadataDataDefinition().getUUID();
+ // break;
+ }
+ nonDuplicatedId.add((String) mdata.getUniqueId());
+ }
+ if (uuid10 == null) {
+ uuid10 = allVFByName.left().value().get(0).getMetadataDataDefinition().getUUID();
+ }
+ props.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+ props.put(GraphPropertiesDictionary.UUID.getProperty(), uuid10);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allVFByUUID = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (allVFByUUID.isRight()) {
+ log.debug("fixDerivedFv failed fetch all VF resources by UUID {}, error: {}", uuid10, allVFByUUID.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ for (ResourceMetadataData mdata : allVFByUUID.left().value()) {
+ nonDuplicatedId.add((String) mdata.getUniqueId());
+ }
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("fixDerivedFv failed - No titan graph ,error {}", graph.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ // Map<String, String> derivedMapping = new HashMap<>();
+ for (String resourceId : nonDuplicatedId) {
+ // StorageOperationStatus handleSingleVfResult =
+ // handleSingleVf(finished, derivedMapping, resourceId);
+ StorageOperationStatus handleSingleVfResult = handleSingleVf(finished, resourceId);
+ if (!handleSingleVfResult.equals(StorageOperationStatus.OK)) {
+ log.debug("fixDerivedFv failed - handleSingleVfResult failed for resource {}, error: {}", resourceId, handleSingleVfResult);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+ return Either.left(finished);
+ }
+
+ // private StorageOperationStatus handleSingleVf(List<String> finished,
+ // Map<String, String> derivedMapping, String resourceId) {
+ private StorageOperationStatus handleSingleVf(List<String> finished, String resourceId) {
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = titanGenericDao
+ .getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), resourceId);
+ if (vertexByProperty.isRight()) {
+ log.debug("fixDerivedFv failed to fetch resource by id {}, error: {}", resourceId, vertexByProperty.right().value());
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ Vertex vertexR = vertexByProperty.left().value();
+ Iterator<Vertex> vertexDIter = vertexR.vertices(Direction.OUT, GraphEdgeLabels.DERIVED_FROM.getProperty());
+ if (vertexDIter != null && vertexDIter.hasNext()) {
+ // move edges
+ // must be only one
+ TitanVertex vertexD = (TitanVertex) vertexDIter.next();
+ String idDerived = (String) titanGenericDao.getProperty(vertexD,
+ GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+
+ // TODO clone resource
+
+ // TODO add instance of new resource to VF
+
+ // add to vf instance of vfc
+ finished.add(resourceId);
+ } else {
+ log.debug("No derived edges for resource id {}", resourceId);
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private boolean updateComponentInstanceType() {
+ log.debug("update component instances type STARTED");
+ Either<List<ComponentInstanceData>, TitanOperationStatus> allInstances = titanGenericDao
+ .getByCriteria(NodeTypeEnum.ResourceInstance, null, ComponentInstanceData.class);
+ if (allInstances.isRight()) {
+ if (allInstances.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("updateComponentInstanceType: no instances ti update ");
+ return true;
+ }
+ log.debug("updateComponentInstanceType failed fetch all resource instances ,error {}", allInstances.right().value());
+ return false;
+ }
+
+ List<ComponentInstanceData> listOfInstances = allInstances.left().value();
+ for (ComponentInstanceData instance : listOfInstances) {
+ String originId = instance.getComponentInstDataDefinition().getComponentUid();
+ Either<ComponentMetadataData, TitanOperationStatus> nodeResource = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), originId, ComponentMetadataData.class);
+ if (nodeResource.isRight()) {
+ log.debug("updateComponentInstanceType failed to fetch origin resource with id {}, error: {}", originId, nodeResource.right().value());
+ return false;
+ }
+ ResourceTypeEnum resourceType = ((ResourceMetadataDataDefinition) nodeResource.left().value()
+ .getMetadataDataDefinition()).getResourceType();
+ if (resourceType == null) {
+ log.debug("updateComponentInstanceType failed, no resource type for origin resource with id {}", originId);
+ return false;
+ }
+ OriginTypeEnum originType;
+ switch (resourceType) {
+ case VF:
+ originType = OriginTypeEnum.VF;
+ break;
+ case VFC:
+ originType = OriginTypeEnum.VFC;
+ break;
+ case VL:
+ originType = OriginTypeEnum.VL;
+ break;
+ case CP:
+ originType = OriginTypeEnum.CP;
+ break;
+ default:
+ log.debug("updateComponentInstanceType failed, no supported resource type {} for origin resource with id {}", resourceType, originId);
+ return false;
+ }
+ instance.getComponentInstDataDefinition().setOriginType(originType);
+
+ Either<ComponentInstanceData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(instance,
+ ComponentInstanceData.class);
+ if (updateNode.isRight()) {
+ log.debug("updateComponentInstanceType failed, failed to update component instance node with id {}, error: {}", instance.getUniqueId(), updateNode.right().value());
+ return false;
+ }
+ log.debug("For instance with id {} the origin type was detected as {}", instance.getUniqueId(), originType);
+ }
+ log.debug("update component instances type ENDED");
+ return true;
+ }
+
+ private boolean addResourceCounterToResources() {
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allResources = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, null, ResourceMetadataData.class);
+ if (allResources.isRight()) {
+ if (allResources.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("addResourceCounterToResources - no resources");
+ return true;
+ }
+ log.debug("addResourceCounterToResources failed fetch all resources,error {}", allResources.right().value());
+ return false;
+ }
+ for (ResourceMetadataData resource : allResources.left().value()) {
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = titanGenericDao.getVertexByProperty(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resource.getUniqueId());
+ if (vertexByProperty.isRight()) {
+ log.error("failed to add instanceCounter to VF {} . error is: {}", resource.getUniqueId(),
+ vertexByProperty.right().value().name());
+ return false;
+ }
+ Vertex vfVertex = vertexByProperty.left().value();
+ if (!vfVertex.property(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty()).isPresent()) {
+ vfVertex.property(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty(), 0);
+ }
+ }
+ return true;
+ }
+
+ private boolean fixDerivedVf() {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allVF = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (allVF.isRight()) {
+ if (allVF.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("fixDerivedVf - no VFs");
+ return true;
+ }
+ log.debug("fixDerivedFv failed fetch all VF resources,error {}", allVF.right().value());
+ return false;
+ }
+
+ Map<String, String> vfUuidToVfcUuid = new HashMap<String, String>();
+ for (ResourceMetadataData metadata : allVF.left().value()) {
+ Either<Resource, StorageOperationStatus> eitherResource = resourceOperation
+ .getResource(metadata.getMetadataDataDefinition().getUniqueId(), true);
+ if (eitherResource.isRight()) {
+ log.error("failed to migrate VF {} from version 1602 to version 1604. error is: {}",
+ metadata.getMetadataDataDefinition().getUniqueId(), eitherResource.right().value().name());
+ return false;
+ }
+ Resource vfResource = eitherResource.left().value();
+ if (vfResource.getDerivedFrom() == null || vfResource.getDerivedFrom().isEmpty()) {
+ continue;
+ }
+ Boolean isVfDeleted = vfResource.getIsDeleted();
+ String vfUUID = vfResource.getUUID();
+ String vfcUUID = vfUuidToVfcUuid.getOrDefault(vfUUID, null);
+ if (vfcUUID == null) {
+ vfcUUID = UUID.randomUUID().toString();
+ vfUuidToVfcUuid.put(vfUUID, vfcUUID);
+ }
+
+ // handle lifecycle
+ String vfUniqueId = vfResource.getUniqueId();
+ LifecycleStateEnum vfcTargetState = vfResource.getLifecycleState();
+ if (vfcTargetState.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION)
+ || vfcTargetState.equals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS)) {
+ User user = new User();
+ user.setUserId(vfResource.getLastUpdaterUserId());
+ Either<? extends Component, StorageOperationStatus> checkinComponent = lifecycleOperaion
+ .checkinComponent(NodeTypeEnum.Resource, vfResource, user, user, true);
+ if (checkinComponent.isRight()) {
+ log.error("failed to checkin VF {}. error={}", vfUniqueId, checkinComponent.right().value().name());
+ return false;
+ }
+ } else if (vfcTargetState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ vfcTargetState = LifecycleStateEnum.NOT_CERTIFIED_CHECKIN;
+ }
+
+ // delete VF Properties
+ List<PropertyDefinition> properties = vfResource.getProperties();
+ if (properties != null && !properties.isEmpty()) {
+ Either<Map<String, PropertyDefinition>, StorageOperationStatus> deleteAllProperties = propertyOperation
+ .deleteAllPropertiesAssociatedToNode(NodeTypeEnum.Resource, vfUniqueId);
+ if (deleteAllProperties.isRight()
+ && !deleteAllProperties.right().value().equals(StorageOperationStatus.NOT_FOUND)
+ && !deleteAllProperties.right().value().equals(StorageOperationStatus.OK)) {
+ log.error("failed to delete properties of VF {} . error is: {}",
+ metadata.getMetadataDataDefinition().getUniqueId(),
+ deleteAllProperties.right().value().name());
+ return false;
+ }
+ }
+ // delete VF Additional Info
+ List<AdditionalInformationDefinition> additionalInformation = vfResource.getAdditionalInformation();
+ if (additionalInformation != null && !additionalInformation.isEmpty()) {
+ Either<AdditionalInformationDefinition, StorageOperationStatus> deleteAllAdditionalInformationParameters = additionalInformationOperation
+ .deleteAllAdditionalInformationParameters(NodeTypeEnum.Resource, vfUniqueId, true);
+ if (deleteAllAdditionalInformationParameters.isRight()
+ && !deleteAllAdditionalInformationParameters.right().value().equals(StorageOperationStatus.OK)
+ && !deleteAllAdditionalInformationParameters.right().value()
+ .equals(StorageOperationStatus.NOT_FOUND)) {
+ log.error("failed to delete properties of VF {} . error is: {}",
+ metadata.getMetadataDataDefinition().getUniqueId(),
+ deleteAllAdditionalInformationParameters.right().value().name());
+ return false;
+ }
+ }
+ // delete VF derivedFrom
+ GraphRelation derivedFromRelation = new GraphRelation(GraphEdgeLabels.DERIVED_FROM.getProperty());
+ derivedFromRelation.setFrom(new RelationEndPoint(NodeTypeEnum.Resource,
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), vfUniqueId));
+ Either<GraphRelation, TitanOperationStatus> deleteDerivedFromRelation = titanGenericDao
+ .deleteOutgoingRelation(derivedFromRelation);
+ if (deleteDerivedFromRelation.isRight()) {
+ log.error("failed to delete derivedFrom relation of VF {} . error is: {}",
+ metadata.getMetadataDataDefinition().getUniqueId(),
+ deleteDerivedFromRelation.right().value().name());
+ return false;
+ }
+
+ // create VFC
+ Either<Resource, StorageOperationStatus> createVFC = createVFC(metadata, vfResource, vfcUUID,
+ vfcTargetState);
+ if (createVFC.isRight()) {
+ log.error("failed to split VF {} to VFC. error is: {}",
+ metadata.getMetadataDataDefinition().getUniqueId(), createVFC.right().value().name());
+ return false;
+ }
+ Resource vfcResource = createVFC.left().value();
+ if (!createVfcInstanceOnVf(vfcResource, vfUniqueId)) {
+ return false;
+ }
+ // update VFC to deleted if required
+ if (isVfDeleted != null && isVfDeleted) {
+ Either<Component, StorageOperationStatus> markResourceToDelete = resourceOperation
+ .markComponentToDelete(vfcResource, true);
+ if (markResourceToDelete.isRight()) {
+ log.error("failed to mark isDeleted on VFC {} . error is: {}", vfcResource.getUniqueId(),
+ markResourceToDelete.right().value().name());
+ return false;
+ }
+ }
+
+ }
+ return true;
+ }
+
+ private Either<Resource, StorageOperationStatus> createVFC(ResourceMetadataData metadata, Resource vfcResource,
+ String uuid, LifecycleStateEnum vfcTargetState) {
+
+ Boolean highestVersion = vfcResource.isHighestVersion();
+ // Resource vfcResource = new Resource((ResourceMetadataDefinition)
+ // vfResource.getComponentMetadataDefinition());
+ // String componentName = vfcResource.getName()+"VFC";
+ // vfcResource.setName(componentName);
+ // vfcResource.setNormalizedName(ValidationUtils.normaliseComponentName(componentName));
+ // vfcResource.setSystemName(ValidationUtils.convertToSystemName(componentName));
+ vfcResource.setUniqueId(null);
+ vfcResource.setUUID(uuid);
+ vfcResource.setAllVersions(null);
+ vfcResource.setArtifacts(null);
+ vfcResource.setDeploymentArtifacts(null);
+ vfcResource.setComponentInstances(null);
+ vfcResource.setComponentInstancesProperties(null);
+ vfcResource.setComponentInstancesRelations(null);
+ vfcResource.setResourceType(ResourceTypeEnum.VFC);
+ vfcResource.setIsDeleted(false);
+
+ vfcResource.setLifecycleState(vfcTargetState);
+ // vfcResource.setDerivedFrom(vfResource.getDerivedFrom());
+ // vfcResource.setProperties(vfResource.getProperties());
+ // vfcResource.setAdditionalInformation(vfResource.getAdditionalInformation());
+ // vfcResource.setCategories(vfResource.getCategories());
+ // vfcResource.setTags(vfResource.getTags());
+
+ Either<Resource, StorageOperationStatus> createResource = resourceOperation.createResource(vfcResource, true);
+ if (createResource.isRight()) {
+ return createResource;
+ }
+ Resource afterCreateResource = createResource.left().value();
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = titanGenericDao.getVertexByProperty(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), afterCreateResource.getUniqueId());
+ if (vertexByProperty.isRight()) {
+ return createResource;
+ }
+ Vertex newVfcVertex = vertexByProperty.left().value();
+ newVfcVertex.property(GraphPropertiesDictionary.UUID.getProperty(), uuid);
+ if (!highestVersion) {
+ newVfcVertex.property(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), false);
+ }
+ return createResource;
+ }
+
+ private boolean createVfcInstanceOnVf(Resource vfcResource, String vfUniqueId) {
+ // create VFC instance on VF
+ ComponentInstance componentInstance = new ComponentInstance();
+ componentInstance.setComponentUid(vfcResource.getUniqueId());
+ componentInstance.setPosX("550");
+ componentInstance.setPosY("300");
+ componentInstance.setName(vfcResource.getName());
+ componentInstance.setIcon(vfcResource.getIcon());
+ componentInstance.setToscaComponentName(vfcResource.getToscaResourceName());
+ Either<String, Boolean> handleNameLogic = handleNameLogic(componentInstance, vfUniqueId, vfcResource.getName());
+ if (handleNameLogic.isRight()) {
+ log.error("failed to create logical name for vfc instance");
+ return false;
+ }
+ Either<ComponentInstance, StorageOperationStatus> createComponentInstance = componentInstanceOperaion
+ .createComponentInstance(vfUniqueId, NodeTypeEnum.Resource, handleNameLogic.left().value(),
+ componentInstance, NodeTypeEnum.Resource, true);
+
+ if (createComponentInstance.isRight()) {
+ log.error("failed to create vfc instance on vf {}. error: {}", vfUniqueId,
+ createComponentInstance.right().value().name());
+ return false;
+ }
+ return true;
+ }
+
+ private Either<String, Boolean> handleNameLogic(ComponentInstance componentInstance, String containerComponentId,
+ String resourceName) {
+
+ Either<Integer, StorageOperationStatus> componentInNumberStatus = resourceOperation
+ .increaseAndGetComponentInstanceCounter(containerComponentId, true);
+
+ if (componentInNumberStatus.isRight()) {
+ log.debug("Failed to get component instance number for container component {} ", containerComponentId);
+ return Either.right(false);
+ }
+ String resourceInNumber = componentInNumberStatus.left().value().toString();
+ componentInstance.setComponentName(resourceName);
+ componentInstance.setName(resourceName);
+ String logicalName = componentInstanceOperaion.createComponentInstLogicalName(resourceInNumber, resourceName);
+
+ Boolean eitherValidation = validateComponentInstanceName(logicalName, componentInstance, true);
+ if (!eitherValidation) {
+ return Either.right(false);
+ }
+
+ return Either.left(resourceInNumber);
+ }
+
+ private Boolean validateComponentInstanceName(String resourceInstanceName, ComponentInstance resourceInstance,
+ boolean isCreate) {
+
+ if (!ValidationUtils.validateStringNotEmpty(resourceInstanceName)) {
+ return false;
+ }
+ resourceInstance.setNormalizedName(ValidationUtils.normaliseComponentInstanceName(resourceInstanceName));
+ if (!isCreate) {
+ if (!ValidationUtils.validateResourceInstanceNameLength(resourceInstanceName)) {
+ return false;
+ }
+ if (!ValidationUtils.validateResourceInstanceName(resourceInstanceName)) {
+ return false;
+ }
+ }
+
+ return true;
+
+ }
+
+ public boolean migrate1604to1607(String appConfigDir) {
+ log.debug("Started the migration procedure from version 1604 to version 1607 ...");
+ log.debug("Getting all resources with resources");
+ boolean result = false;
+ Either<Boolean, StorageOperationStatus> resourceEither = null;
+ try {
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allResources = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, null, ResourceMetadataData.class);
+ if (allResources.isRight()) {
+ log.error("Couldn't get resources from DB, error: {}", allResources.right().value());
+ result = false;
+ return result;
+ }
+ List<ResourceMetadataData> resourcesList = allResources.left().value();
+ if (resourcesList == null) {
+ log.error("Couldn't get resources from DB, no resources found");
+ result = false;
+ return result;
+ }
+ log.debug("Found {} resources", resourcesList.size());
+ for (ResourceMetadataData resource : resourcesList) {
+ String resourceName = resource.getMetadataDataDefinition().getName();
+ log.debug("Checking resource {}", resourceName);
+ if (isNormative(resourceName)) {
+ resourceEither = changeNormativeTypeName(resource);
+ if (resourceEither.isRight()) {
+ log.error("DB error during name changing");
+ result = false;
+ return result;
+ }
+ }
+ if (((ResourceMetadataDataDefinition) resource.getMetadataDataDefinition()).getResourceType().name()
+ .equals("VF")) {
+ resourceEither = setVfToscaResourceName(resource);
+ if (resourceEither.isRight()) {
+ log.error("DB error during tosca resource name setting");
+ result = false;
+ return result;
+ }
+ }
+ }
+ result = addInvariantUUIDs(appConfigDir);
+ } finally {
+ if (!result) {
+ titanGenericDao.rollback();
+ log.debug("**********************************************");
+ log.debug("The migration procedure from version 1604 to version 1607 FAILED!!");
+ log.debug("**********************************************");
+ } else {
+ titanGenericDao.commit();
+ log.debug("**********************************************");
+ log.debug("The migration procedure from version 1604 to version 1607 ended successfully!");
+ log.debug("**********************************************");
+ }
+ }
+
+ return result;
+ }
+
+ private boolean addInvariantUUIDs(String appConfigDir) {
+ log.debug("Started adding of InvariantUUID ...");
+ log.debug("Getting all resources with highest version");
+
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ List<ComponentMetadataData> fullComponentList = new ArrayList<ComponentMetadataData>();
+
+ // getting resources
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allHighestVersionResources = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (allHighestVersionResources.isRight()) {
+ log.error("Couldn't get resources with highest version from DB, error: {}",
+ allHighestVersionResources.right().value());
+ return false;
+ }
+ List<ResourceMetadataData> allHighestVersionResourcesAL = allHighestVersionResources.left().value();
+ if (allHighestVersionResourcesAL == null) {
+ log.error("Couldn't get resources with highest version from DB, no resources found");
+ return false;
+ }
+ log.debug("Found {} resources", allHighestVersionResourcesAL.size());
+ fullComponentList.addAll(allHighestVersionResourcesAL);
+
+ // getting services
+ Either<List<ServiceMetadataData>, TitanOperationStatus> allHighestVersionServices = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Service, props, ServiceMetadataData.class);
+ if (allHighestVersionServices.isRight()) {
+ log.error("Couldn't get services with highest version from DB, error: {}",
+ allHighestVersionServices.right().value());
+ return false;
+ }
+ List<ServiceMetadataData> allHighestVersionServicesAL = allHighestVersionServices.left().value();
+ if (allHighestVersionServicesAL == null) {
+ log.error("Couldn't get services with highest version from DB, no services found");
+ return false;
+ }
+ log.debug("Found {} services", allHighestVersionServicesAL.size());
+ fullComponentList.addAll(allHighestVersionServicesAL);
+
+ List<ComponentMetadataData> reducedComponentsAL = reduceHighestVersionResourcesList(fullComponentList);
+
+ // getting products
+ Either<List<ProductMetadataData>, TitanOperationStatus> allHighestVersionProducts = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Product, props, ProductMetadataData.class);
+ if (allHighestVersionProducts.isRight()) {
+ log.error("Couldn't get products with highest version from DB, error: {}",
+ allHighestVersionProducts.right().value());
+ return false;
+ }
+ List<ProductMetadataData> allHighestVersionProductsAL = allHighestVersionProducts.left().value();
+ if (allHighestVersionProductsAL == null) {
+ log.error("Couldn't get products with highest version from DB, no products found");
+ return false;
+ }
+ log.debug("Found {} products", allHighestVersionProductsAL.size());
+
+ List<ComponentMetadataData> fullProductList = new ArrayList<ComponentMetadataData>();
+ fullProductList.addAll(allHighestVersionProductsAL);
+ List<ComponentMetadataData> reducedProductAL = reduceHighestVersionResourcesList(fullProductList);
+
+ for (ComponentMetadataData product : reducedProductAL) {
+ if (!setProductInvariantUUIDIfExists((ProductMetadataData) product)) {
+ return false;
+ }
+ }
+ reducedComponentsAL.addAll(reducedProductAL);
+
+ log.debug("Reduced list of Highest Version Components contains {} components", reducedComponentsAL.size());
+ for (ComponentMetadataData componentMetaData : reducedComponentsAL) {
+
+ String invariantUUID = componentMetaData.getMetadataDataDefinition().getInvariantUUID();
+ log.debug("old invariantUUID {}", invariantUUID);
+ if (invariantUUID == null || invariantUUID.isEmpty()) {
+ invariantUUID = UniqueIdBuilder.buildInvariantUUID();
+ componentMetaData.getMetadataDataDefinition().setInvariantUUID(invariantUUID);
+ }
+ log.debug("new invariantUUID {}", componentMetaData.getMetadataDataDefinition().getInvariantUUID());
+ Either<ComponentMetadataData, TitanOperationStatus> updateNode = titanGenericDao
+ .updateNode(componentMetaData, ComponentMetadataData.class);
+ if (updateNode.isRight()) {
+ log.error("DB error during while updating component {}, error: {}",
+ componentMetaData.getMetadataDataDefinition().getName(), updateNode.right().value());
+ return false;
+ }
+ log.debug("updated invariantUUID {}",
+ updateNode.left().value().getMetadataDataDefinition().getInvariantUUID());
+ if (!isOnlyVersion(componentMetaData)) {
+ ComponentOperation componentOperation = null;
+ switch (NodeTypeEnum.getByName(componentMetaData.getLabel())) {
+ case Resource:
+ componentOperation = resourceOperation;
+ break;
+ case Service:
+ componentOperation = serviceOperation;
+ break;
+ case Product:
+ componentOperation = productOperation;
+ break;
+ default:
+ break;
+ }
+ Either<Component, StorageOperationStatus> getComponentResult = componentOperation
+ .getComponent((String) componentMetaData.getUniqueId(), true);
+ if (getComponentResult.isRight()) {
+ log.error("DB error during while getting component with uniqueID {}, error: {}",
+ componentMetaData.getUniqueId(), getComponentResult.right().value());
+ return false;
+ }
+ Component component = getComponentResult.left().value();
+ if (component == null) {
+ log.error("The component received from DB is empty");
+ return false;
+ }
+
+ Map<String, String> allVersions = component.getAllVersions();
+ log.debug("found {} versions for component {}", allVersions.size(), component.getName());
+ Either<Boolean, StorageOperationStatus> resEither = updateAllVersions(allVersions, invariantUUID);
+ if (resEither.isRight()) {
+ log.error("DB error during invariantUUID adding");
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean isOnlyVersion(ComponentMetadataData componentMetaData) {
+ String version = componentMetaData.getMetadataDataDefinition().getVersion();
+ if (version.equals("0.1"))
+ return true;
+ return false;
+ }
+
+ private boolean setProductInvariantUUIDIfExists(ProductMetadataData product) {
+ Either<TitanVertex, TitanOperationStatus> getVertexRes = titanGenericDao
+ .getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), product.getUniqueId());
+ if (getVertexRes.isRight()) {
+ log.error("DB error during retrieving product vertex {}", product.getMetadataDataDefinition().getName());
+ return false;
+ }
+ Vertex productVertex = getVertexRes.left().value();
+ String invariantUUID = productVertex.value(GraphPropertiesDictionary.CONSTANT_UUID.getProperty());
+ if (invariantUUID != null && !invariantUUID.isEmpty()) {
+ product.getMetadataDataDefinition().setInvariantUUID(invariantUUID);
+ }
+ return true;
+ }
+
+ private Either<Boolean, StorageOperationStatus> updateAllVersions(Map<String, String> allVersions,
+ String invariantUUID) {
+
+ if (allVersions != null) {
+ for (String uniqueID : allVersions.values()) {
+ Either<ComponentMetadataData, TitanOperationStatus> getNodeResult = titanGenericDao.getNode(
+ GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueID, ComponentMetadataData.class);
+ if (getNodeResult.isRight()) {
+ log.error("DB error during while getting component with uniqueID {}, error: {}", uniqueID,
+ getNodeResult.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ ComponentMetadataData component = getNodeResult.left().value();
+ component.getMetadataDataDefinition().setInvariantUUID(invariantUUID);
+ Either<ComponentMetadataData, TitanOperationStatus> updateNodeResult = titanGenericDao
+ .updateNode(component, ComponentMetadataData.class);
+ log.debug("updated child invariantUUID {}",
+ updateNodeResult.left().value().getMetadataDataDefinition().getInvariantUUID());
+ if (updateNodeResult.isRight()) {
+ log.error("DB error during while updating component {}, error: {}",
+ component.getMetadataDataDefinition().getName(), updateNodeResult.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+ }
+ return Either.left(true);
+ }
+
+ private List<ComponentMetadataData> reduceHighestVersionResourcesList(
+ List<ComponentMetadataData> allHighestVersionResources) {
+ List<ComponentMetadataData> resultList = null;
+ Map<String, ComponentMetadataData> resultHM = new HashMap<String, ComponentMetadataData>();
+ for (ComponentMetadataData resource : allHighestVersionResources) {
+ if (resource.getMetadataDataDefinition().getInvariantUUID() != null
+ && !resource.getMetadataDataDefinition().getInvariantUUID().isEmpty()) {
+ log.debug("invariantUUID {} ", resource.getMetadataDataDefinition().getInvariantUUID());
+ continue;
+ }
+ String curUUID = resource.getMetadataDataDefinition().getUUID();
+ if (resultHM.containsKey(curUUID)) {
+ int isHighest = resultHM.get(curUUID).getMetadataDataDefinition().getVersion()
+ .compareTo(resource.getMetadataDataDefinition().getVersion());
+ if (isHighest > 0) {
+ log.debug("version {} is great than {} ",
+ resultHM.get(curUUID).getMetadataDataDefinition().getVersion(),
+ resource.getMetadataDataDefinition().getVersion());
+ continue;
+ }
+ }
+ resultHM.put(curUUID, resource);
+ }
+ resultList = new ArrayList<ComponentMetadataData>(resultHM.values());
+ return resultList;
+ }
+
+ private boolean isNormative(String resourceName) {
+ for (int i = 0; i < NORMATIVE_OLD_NAMES.length; ++i) {
+ if (NORMATIVE_OLD_NAMES[i].equals(resourceName))
+ return true;
+ }
+ return false;
+ }
+
+ private Either<Boolean, StorageOperationStatus> changeNormativeTypeName(ResourceMetadataData resource) {
+
+ String resourceName = resource.getMetadataDataDefinition().getName();
+
+ if (resourceName != null && !resourceName.isEmpty()) {
+ log.debug("Found normative type to change - {}", resourceName);
+ String oldName = resourceName;
+ String[] splitedName = resourceName.split("\\.");
+ String newName = splitedName[splitedName.length - 1];
+ String newSystemName = ValidationUtils.convertToSystemName(newName);
+ String newNormalizedName = ValidationUtils.normaliseComponentName(newName);
+ log.debug("Setting name to be {}", newName);
+
+ resource.getMetadataDataDefinition().setName(newName);
+ log.debug("Setting system name to be {}", newSystemName);
+ resource.getMetadataDataDefinition().setSystemName(newSystemName);
+ log.debug("Setting normalized name to be {}", newNormalizedName);
+ resource.getMetadataDataDefinition().setNormalizedName(newNormalizedName);
+ log.debug("Updating tag in metadata to be {}", newName);
+ resource.getMetadataDataDefinition().getTags().remove(oldName);
+ resource.getMetadataDataDefinition().getTags().add(newName);
+
+ log.debug("Creating tag node with name {}", newName);
+ TagData tagData = new TagData();
+ tagData.setName(newName);
+ Either<TagData, TitanOperationStatus> createNode = titanGenericDao.createNode(tagData, TagData.class);
+ if (createNode.isRight()) {
+ log.error("Error while creating tag node {}, error: {}.", newName, createNode.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(resource,
+ ResourceMetadataData.class);
+ if (updateNode.isRight()) {
+ log.error("DB error during while updating normative type {}, error: {}",
+ resource.getMetadataDataDefinition().getName(), updateNode.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ log.debug("Normative type {} was successfully updated", resource.getMetadataDataDefinition().getName());
+ return Either.left(true);
+ }
+
+ return Either.left(false);
+ }
+
+ private Either<Boolean, StorageOperationStatus> generateAndSetToscaResourceName(ResourceMetadataData resource,
+ String toscaResourceName) {
+ if (toscaResourceName == null) {
+ toscaResourceName = CommonBeUtils.generateToscaResourceName(
+ ((ResourceMetadataDataDefinition) resource.getMetadataDataDefinition()).getResourceType().name(),
+ resource.getMetadataDataDefinition().getSystemName());
+ }
+ Either<Boolean, StorageOperationStatus> validateToscaResourceNameExists = resourceOperation
+ .validateToscaResourceNameExists(toscaResourceName);
+ if (validateToscaResourceNameExists.isRight()) {
+ StorageOperationStatus storageOperationStatus = validateToscaResourceNameExists.right().value();
+ log.error("Couldn't validate toscaResourceName uniqueness - error: {}", storageOperationStatus);
+ return Either.right(storageOperationStatus);
+ }
+ if (validateToscaResourceNameExists.left().value()) {
+ log.debug("Setting tosca resource name to be {}", toscaResourceName);
+ ((ResourceMetadataDataDefinition) resource.getMetadataDataDefinition())
+ .setToscaResourceName(toscaResourceName);
+ return Either.left(true);
+ } else {
+ // As agreed with Renana - cannot be fixed automatically
+ log.warn("toscaResourceName {} is not unique! Cannot set it. Continuing...");
+ return Either.left(false);
+ }
+ }
+
+ public boolean testRemoveHeatPlaceHolders(String appConfigDir) {
+
+ if (!AllowMultipleHeats.removeAndUpdateHeatPlaceHolders(titanGenericDao, log, false)) {
+ log.error("Failed to update heat place holders");
+ return false;
+ }
+ return true;
+ }
+
+ private Either<Boolean, StorageOperationStatus> setVfToscaResourceName(ResourceMetadataData resource) {
+ String resourceName = resource.getMetadataDataDefinition().getName();
+ String resourceType = ((ResourceMetadataDataDefinition) resource.getMetadataDataDefinition()).getResourceType()
+ .name();
+ String toscaResourceName = CommonBeUtils.generateToscaResourceName(resourceType,
+ resource.getMetadataDataDefinition().getSystemName());
+ log.debug("Setting tosca resource name {} to VF {}", toscaResourceName, resourceName);
+ ((ResourceMetadataDataDefinition) resource.getMetadataDataDefinition()).setToscaResourceName(toscaResourceName);
+
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(resource,
+ ResourceMetadataData.class);
+ if (updateNode.isRight()) {
+ log.error("DB error during while updating VF tosca resource name {}, error: {}",
+ resource.getMetadataDataDefinition().getName(), updateNode.right().value());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ log.debug("Tosca resource name of VF {} was successfully updated",
+ resource.getMetadataDataDefinition().getName());
+ return Either.left(true);
+ }
+
+ public boolean testAddGroupUuids(String appConfigDir) {
+
+ if (!AddGroupUuid.addGroupUuids(titanGenericDao, log, false)) {
+ log.error("Failed to update group UUIDs");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/VfcNamingAlignment.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/VfcNamingAlignment.java
new file mode 100644
index 0000000000..27b9351ebc
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1604/VfcNamingAlignment.java
@@ -0,0 +1,185 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1604;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.utils.CommonBeUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+public class VfcNamingAlignment {
+
+ private static Logger log = LoggerFactory.getLogger(VfcNamingAlignment.class.getName());
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+ @Autowired
+ protected ResourceOperation resourceOperation;
+
+ public boolean alignVfcNames1604(String appConfigDir) {
+ log.debug("Started alignVfcNames1604 procedure..");
+ log.debug("Getting all resources with resourceType = VFC/CP/VL");
+ boolean result = false;
+ try {
+ Map<String, Object> notProperties = new HashMap<>();
+ notProperties.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allVfcResources = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, null, notProperties, ResourceMetadataData.class);
+ if (allVfcResources.isRight()) {
+ log.error("Couldn't get VFC resources from DB, error: {}", allVfcResources.right().value());
+ result = false;
+ return result;
+ }
+ List<ResourceMetadataData> vfcList = allVfcResources.left().value();
+ if (vfcList == null) {
+ log.error("Couldn't get VFC/CP/VL resources from DB, no resources found");
+ result = false;
+ return result;
+ }
+ log.debug("Found {} VFC/CP/VL resources", vfcList.size());
+ for (ResourceMetadataData vfc : vfcList) {
+ log.debug("Checking resource {}", vfc.getMetadataDataDefinition().getName());
+ boolean wasChanged = false;
+
+ Either<Boolean, StorageOperationStatus> vfcEither = fixToscaNameEmpty(vfc);
+ if (vfcEither.isRight()) {
+ log.error("DB error during checkIsToscaNameEmpty - exiting...");
+ result = false;
+ return result;
+ }
+ wasChanged = wasChanged | vfcEither.left().value();
+
+ vfcEither = fixVfcToscaNameHasVf(vfc);
+ if (vfcEither.isRight()) {
+ log.error("DB error during checkIsVfcToscaNameHasVf - exiting...");
+ result = false;
+ return result;
+ }
+ wasChanged = wasChanged | vfcEither.left().value();
+
+ if (wasChanged) {
+ Either<ResourceMetadataData, TitanOperationStatus> updateVfc = updateVfc(vfc);
+ if (updateVfc.isRight()) {
+ log.error("DB error during while updating resource {}, error: {} - exiting...",
+ vfc.getMetadataDataDefinition().getName(), updateVfc.right().value());
+ result = false;
+ return result;
+ }
+ log.debug("Resource {} was successfully updated", vfc.getMetadataDataDefinition().getName());
+ }
+
+ }
+ result = true;
+ } finally {
+ if (!result) {
+ titanGenericDao.rollback();
+ log.debug("**********************************************");
+ log.debug("alignVfcNames1604 procedure FAILED!!");
+ log.debug("**********************************************");
+ } else {
+ titanGenericDao.commit();
+ log.debug("**********************************************");
+ log.debug("alignVfcNames1604 procedure ended successfully!");
+ log.debug("**********************************************");
+ }
+ }
+
+ return result;
+ }
+
+ private Either<ResourceMetadataData, TitanOperationStatus> updateVfc(ResourceMetadataData vfc) {
+ return titanGenericDao.updateNode(vfc, ResourceMetadataData.class);
+ }
+
+ private Either<Boolean, StorageOperationStatus> fixToscaNameEmpty(ResourceMetadataData vfc) {
+ String toscaResourceName = ((ResourceMetadataDataDefinition) vfc.getMetadataDataDefinition())
+ .getToscaResourceName();
+ if (toscaResourceName == null || toscaResourceName.trim().equals(Constants.EMPTY_STRING)) {
+ log.debug("Tosca resource name is empty - setting new tosca name...");
+ Either<Boolean, StorageOperationStatus> generateAndSetToscaResourceName = generateAndSetToscaResourceName(
+ vfc, null);
+ if (generateAndSetToscaResourceName.isRight()) {
+ return Either.right(generateAndSetToscaResourceName.right().value());
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+ }
+
+ private Either<Boolean, StorageOperationStatus> fixVfcToscaNameHasVf(ResourceMetadataData vfc) {
+ String toscaResourceName = ((ResourceMetadataDataDefinition) vfc.getMetadataDataDefinition())
+ .getToscaResourceName();
+ if (toscaResourceName.contains(".vf.")) {
+ log.debug("Tosca resource name {} is VF-style - setting new tosca name...", toscaResourceName);
+ Either<Boolean, StorageOperationStatus> generateAndSetToscaResourceName = generateAndSetToscaResourceName(
+ vfc, null);
+ if (generateAndSetToscaResourceName.isRight()) {
+ return Either.right(generateAndSetToscaResourceName.right().value());
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+ }
+
+
+ private Either<Boolean, StorageOperationStatus> generateAndSetToscaResourceName(ResourceMetadataData vfc,
+ String toscaResourceName) {
+ if (toscaResourceName == null) {
+ toscaResourceName = CommonBeUtils.generateToscaResourceName(
+ ((ResourceMetadataDataDefinition) vfc.getMetadataDataDefinition()).getResourceType().name(),
+ vfc.getMetadataDataDefinition().getSystemName());
+ }
+ Either<Boolean, StorageOperationStatus> validateToscaResourceNameExists = resourceOperation
+ .validateToscaResourceNameExists(toscaResourceName);
+ if (validateToscaResourceNameExists.isRight()) {
+ StorageOperationStatus storageOperationStatus = validateToscaResourceNameExists.right().value();
+ log.error("Couldn't validate toscaResourceName uniqueness - error: {}", storageOperationStatus);
+ return Either.right(storageOperationStatus);
+ }
+ if (validateToscaResourceNameExists.left().value()) {
+ log.debug("Setting tosca resource name to be {}", toscaResourceName);
+ ((ResourceMetadataDataDefinition) vfc.getMetadataDataDefinition()).setToscaResourceName(toscaResourceName);
+ return Either.left(true);
+ } else {
+ // As agreed with Renana - cannot be fixed automatically
+ log.warn("toscaResourceName {} is not unique! Cannot set it. Continuing...");
+ return Either.left(false);
+ }
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1607/CsarMigration.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1607/CsarMigration.java
new file mode 100644
index 0000000000..b141eecb87
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1607/CsarMigration.java
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1607;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public class CsarMigration {
+ private static Logger log = LoggerFactory.getLogger(CsarMigration.class.getName());
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+
+ public boolean removeCsarResources() {
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (byCriteria.isRight()) {
+ log.debug("Failed to fetch VF resources by criteria ", byCriteria.right().value());
+ return false;
+ }
+ List<ResourceMetadataData> resources = byCriteria.left().value();
+
+ try {
+ for (ResourceMetadataData data : resources) {
+ if (data.getMetadataDataDefinition().getCsarUUID() != null) {
+ log.debug("VF {} with CSAR {}", data.getUniqueId(), data.getMetadataDataDefinition().getCsarUUID());
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = titanGenericDao
+ .getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), data.getUniqueId());
+ if (vertexByProperty.isRight()) {
+ log.debug("Failed to fetch vertex with id {} . skip resource {} ", data.getUniqueId(),
+ data.getMetadataDataDefinition().getName());
+ continue;
+ }
+ Vertex vertex = vertexByProperty.left().value();
+ Iterator<VertexProperty<Object>> properties = vertex
+ .properties(GraphPropertiesDictionary.CSAR_UUID.getProperty());
+ while (properties.hasNext()) {
+ VertexProperty<Object> next = properties.next();
+ next.remove();
+ }
+
+ }
+ }
+ titanGenericDao.commit();
+ } catch (Exception e) {
+ log.debug("Failed to clean CSAR UUID. rollback");
+ titanGenericDao.rollback();
+ }
+
+ return true;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/TitanFixUtils.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/TitanFixUtils.java
new file mode 100644
index 0000000000..36ac98e578
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/TitanFixUtils.java
@@ -0,0 +1,387 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1610;
+
+import com.google.gson.Gson;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import fj.data.Either;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.operations.impl.CacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.*;
+
+/**
+ * Created by mlando on 8/17/2016.
+ */
+public class TitanFixUtils {
+ private static Logger log = LoggerFactory.getLogger(TitanFixUtils.class.getName());
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+ @Autowired
+ protected CacheMangerOperation cacheMangerOperation;
+
+ public boolean fixIconsInNormatives() {
+ log.info("starting fix");
+ String vlName = "VL";
+ String elineName = "VL_ELINE";
+ String elineFixedName = "VL ELINE";
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ log.error("failed to get graph object.");
+ return false;
+ }
+
+ boolean operationFailed = false;
+ Map<String, Object> vlVerticeProperties = null;
+ Map<String, Object> elineVerticeProperties = null;
+
+ try {
+ TitanGraph titanGraph = graphResult.left().value();
+ log.info("look up vl :{}", vlName);
+
+ Iterable<TitanVertex> vertices = titanGraph.query()
+ .has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName())
+ .has(GraphPropertiesDictionary.NAME.getProperty(), vlName)
+ .has(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true).vertices();
+
+ if (vertices == null) {
+ log.error("failed to get vernice for resource name {}", vlName);
+ operationFailed = true;
+ return false;
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<TitanVertex> vertexList = new ArrayList<>();
+
+ if (iterator == null) {
+ log.error("failed to get iterator over vertices object returned for resource id {}", vlName);
+ operationFailed = true;
+ return false;
+ }
+
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertexList.add(vertex);
+ }
+
+ if (!(vertexList.size() == 1)) {
+ log.error("failed to get 1 vertex for resource {} with highest true. instead got {}", vlName,
+ vertexList.size());
+ operationFailed = true;
+ return false;
+ }
+
+ TitanVertex vlVertex = vertexList.get(0);
+
+ log.info("look up eline:{}", elineName);
+
+ boolean vl_eline_found = true;
+
+ vertices = titanGraph.query()
+ .has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName())
+ .has(GraphPropertiesDictionary.NAME.getProperty(), elineName)
+ .has(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true).vertices();
+
+ if (vertices == null) {
+ log.error("failed to get vertices object returned for resource {}", elineName);
+ operationFailed = true;
+
+ vl_eline_found = false;
+ }
+
+ if (vl_eline_found) {
+ iterator = vertices.iterator();
+ vertexList = new ArrayList<>();
+ if (iterator == null) {
+ log.error("failed to get iterator over vertices object returned for resource id {}", elineName);
+ operationFailed = true;
+
+ vl_eline_found = false;
+ }
+
+ if (vl_eline_found) {
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertexList.add(vertex);
+ }
+
+ if (!(vertexList.size() == 1)) {
+ log.error("failed to get 1 vertex for resource {} with highest true. instead got {}",
+ elineName, vertexList.size());
+ operationFailed = true;
+
+ vl_eline_found = false;
+ }
+ }
+ }
+
+ if (!vl_eline_found) {
+ log.info("look up eline:{}", elineFixedName);
+ vl_eline_found = true;
+ operationFailed = false;
+
+ vertices = titanGraph.query()
+ .has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName())
+ .has(GraphPropertiesDictionary.NAME.getProperty(), elineFixedName)
+ .has(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true).vertices();
+
+ if (vertices == null) {
+ log.error("failed to get vertices object returned for resource {}", elineFixedName);
+ operationFailed = true;
+
+ vl_eline_found = false;
+ }
+
+ if (vl_eline_found) {
+ iterator = vertices.iterator();
+ vertexList = new ArrayList<>();
+ if (iterator == null) {
+ log.error("failed to get iterator over vertices object returned for resource id {}",
+ elineFixedName);
+ operationFailed = true;
+
+ vl_eline_found = false;
+ }
+
+ if (vl_eline_found) {
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertexList.add(vertex);
+ }
+
+ if (!(vertexList.size() == 1)) {
+ log.error("failed to get 1 vertex for resource {} with highest true. instead got {}",
+ elineFixedName, vertexList.size());
+ operationFailed = true;
+
+ vl_eline_found = false;
+ }
+ }
+ }
+ }
+
+ if (!vl_eline_found) {
+ return false;
+ } else {
+ TitanVertex elineVertex = vertexList.get(0);
+
+ vlVerticeProperties = titanGenericDao.getProperties(vlVertex);
+
+ log.info("VL Vertice Properties {}", vlVerticeProperties);
+ if ("network".equals(vlVerticeProperties.get(GraphPropertiesDictionary.ICON.getProperty()))) {
+ log.info("nothing to update in vl");
+ } else {
+ log.info("updating property icon of vl");
+ vlVertex.property(GraphPropertiesDictionary.ICON.getProperty(), "network");
+ }
+
+ elineVerticeProperties = titanGenericDao.getProperties(elineVertex);
+
+ log.info("eline vertice Properties {}", elineVerticeProperties);
+ if ("network".equals(elineVerticeProperties.get(GraphPropertiesDictionary.ICON.getProperty()))) {
+ log.info("nothing to update in eline");
+ } else {
+ log.info("updating property icon of eline");
+ elineVertex.property(GraphPropertiesDictionary.ICON.getProperty(), "network");
+ }
+
+ if ("VL ELINE".equals(elineVerticeProperties.get(GraphPropertiesDictionary.NAME.getProperty()))) {
+ log.info("nothing to update in eline");
+ } else {
+ log.info("updating property name and tag of eline");
+ elineVertex.property(GraphPropertiesDictionary.NAME.getProperty(), elineFixedName);
+ List<String> tags = new ArrayList<>();
+ tags.add("VL ELINE");
+ elineVertex.property(GraphPropertiesDictionary.TAGS.getProperty(), new Gson().toJson(tags));
+ }
+
+ log.info("print current properties state");
+
+ vlVerticeProperties = titanGenericDao.getProperties(vlVertex);
+
+ log.info("vertice vl Properties {}", vlVerticeProperties);
+ elineVerticeProperties = titanGenericDao.getProperties(elineVertex);
+
+ log.info("vertice eline Properties {}", elineVerticeProperties);
+ }
+
+ try {
+ Thread.sleep(30 * 1000);
+ } catch (InterruptedException e) {
+ log.error("exception", e);
+ }
+ return true;
+ } finally {
+ if (operationFailed) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ long time = System.currentTimeMillis();
+ if (vlVerticeProperties != null) {
+ cacheMangerOperation.updateComponentInCache(
+ (String) vlVerticeProperties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()), time,
+ NodeTypeEnum.Resource);
+ }
+ if (elineVerticeProperties != null) {
+ cacheMangerOperation.updateComponentInCache(
+ (String) elineVerticeProperties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()),
+ time, NodeTypeEnum.Resource);
+ }
+ }
+ }
+ }
+
+ /**
+ * in 1610 we encounter an issue that if a capability property overrides a
+ * property of a derived capability then it was created with out a property
+ * type when it was first imported as part of the capability types. this
+ * will add property type to the properties missing it.
+ */
+ public boolean fixCapabiltyPropertyTypes() {
+
+ String propertyIdSecure = "tosca.capabilities.Endpoint.Admin.secure";
+ String propertyIdNetworkName = "tosca.capabilities.Endpoint.Public.network_name";
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+
+ if (graphResult.isRight()) {
+ log.error("failed to get graph object.");
+ return false;
+ }
+
+ boolean operationFailed = false;
+ try {
+ TitanGraph titanGraph = graphResult.left().value();
+ log.info("look up propertyIdSecure:{}", propertyIdSecure);
+ Iterable<TitanVertex> vertices = titanGraph.query()
+ .has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyIdSecure).vertices();
+ if (vertices == null) {
+ log.error("failed to get vertices object returned for resource id {}", propertyIdSecure);
+ operationFailed = true;
+ return false;
+ }
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<TitanVertex> vertexList = new ArrayList<>();
+
+ if (iterator == null) {
+ log.error("failed to get iterator over vertices object returned for resource id " + propertyIdSecure);
+ operationFailed = true;
+ return false;
+ }
+
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertexList.add(vertex);
+ }
+
+ if (!(vertexList.size() == 1)) {
+ log.error("failed to get 1 vertex for resource id {} instead got {}", propertyIdSecure,
+ vertexList.size());
+ operationFailed = true;
+ return false;
+ }
+
+ TitanVertex propertyVerticeSecure = vertexList.get(0);
+
+ log.info("look up propertyIdNetworkName:{}", propertyIdNetworkName);
+ vertices = titanGraph.query()
+ .has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyIdNetworkName).vertices();
+ if (vertices == null) {
+ log.error("failed to get vertices object returned for resource id {}", propertyIdNetworkName);
+ operationFailed = true;
+ return false;
+ }
+
+ iterator = vertices.iterator();
+ vertexList = new ArrayList<>();
+
+ if (iterator == null) {
+ log.error("failed to get iterator over vertices object returned for resource id {}",
+ propertyIdNetworkName);
+ operationFailed = true;
+ return false;
+ }
+
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertexList.add(vertex);
+ }
+
+ if (!(vertexList.size() == 1)) {
+ log.error("failed to get 1 vertex for resource id {} instead got {}", propertyIdNetworkName,
+ vertexList.size());
+ operationFailed = true;
+ return false;
+ }
+
+ TitanVertex propertyVerticeNetworkName = vertexList.get(0);
+
+ Map<String, Object> verticeNetworkNameProperties = titanGenericDao
+ .getProperties(propertyVerticeNetworkName);
+
+ log.info("vertice NetworkName Properties {}", verticeNetworkNameProperties);
+ Object type = verticeNetworkNameProperties.get(GraphPropertiesDictionary.TYPE.getProperty());
+ if (type == null || "".equals(type)) {
+ log.info("updating property Vertice Network Name");
+ propertyVerticeNetworkName.property(GraphPropertiesDictionary.TYPE.getProperty(), "string");
+ }
+
+ Map<String, Object> verticeSecureProperties = titanGenericDao.getProperties(propertyVerticeSecure);
+
+ log.info("vertice Secure Properties {}", verticeSecureProperties);
+
+ type = verticeSecureProperties.get(GraphPropertiesDictionary.TYPE.getProperty());
+
+ if (type == null || "".equals(type)) {
+ log.info("updating property Vertice Secure");
+ propertyVerticeSecure.property(GraphPropertiesDictionary.TYPE.getProperty(), "boolean");
+ }
+
+ log.info("print current properties state");
+
+ verticeNetworkNameProperties = titanGenericDao.getProperties(propertyVerticeNetworkName);
+
+ log.info("vertice NetworkName Properties {}", verticeNetworkNameProperties);
+
+ verticeSecureProperties = titanGenericDao.getProperties(propertyVerticeSecure);
+
+ log.info("vertice Secure Properties {}", verticeSecureProperties);
+
+ return true;
+ } finally {
+ if (operationFailed) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/ToscaArtifactsAlignment.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/ToscaArtifactsAlignment.java
new file mode 100644
index 0000000000..347a5705b7
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/impl/migration/v1610/ToscaArtifactsAlignment.java
@@ -0,0 +1,461 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.impl.migration.v1610;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.AbstractOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.StreamUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+/**
+ * This Class holds the logic to add Tosca Artifacts placeholder and payload.<br>
+ * This addition is done for old version of Services and Resources (pre 1610) that weren't created with them.<br>
+ *
+ * @author mshitrit <br>
+ *
+ *
+ */
+public class ToscaArtifactsAlignment extends AbstractOperation {
+ @Autowired
+ private IArtifactOperation artifactOperation;
+
+ @Autowired
+ private ServiceBusinessLogic serviceBusinessLogic;
+
+ private static Logger log = LoggerFactory.getLogger(ToscaArtifactsAlignment.class.getName());
+
+ private static final String ERROR_PREFIX = "Tosca Artifact Alignment Error: ";
+
+ // API that Fetches Resource
+ private final Function<ComponentMetadataData, Resource> resourceFetcher = componentMD -> getComponent(componentMD, ComponentTypeEnum.RESOURCE);
+ // API that Fetches Service
+ private final Function<ComponentMetadataData, Service> serviceFetcher = componentMD -> getComponent(componentMD, ComponentTypeEnum.SERVICE);
+ // Class Getters
+ private final Supplier<Class<ResourceMetadataData>> resourceClassGetter = () -> ResourceMetadataData.class;
+ private final Supplier<Class<ServiceMetadataData>> serviceClassGetter = () -> ServiceMetadataData.class;
+
+ /**
+ * This method holds the logic to add Tosca Artifacts placeholder and payload.<br>
+ *
+ * @return true if succeed otherwise returns false
+ */
+ public boolean alignToscaArtifacts() {
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ List<ResourceMetadataData> allResources = new ArrayList<>();
+ List<ResourceMetadataData> resourcesWithoutToscaPlaceHolder = new ArrayList<>();
+ List<ServiceMetadataData> allServices = new ArrayList<>();
+ List<ServiceMetadataData> servicesWithoutToscaPlaceHolder = new ArrayList<>();
+ log.debug("alignToscaArtifacts Start");
+ try {
+
+ if (errorWrapper.isEmpty()) {
+ log.info("Fetching all resources");
+ fillAllComponetOfSpecificType(allResources, NodeTypeEnum.Resource, resourceClassGetter, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Filter Resources Without Tosca Artifacts
+ log.info("filtering resources to add tosca placeholder");
+ Either<List<ResourceMetadataData>, TitanOperationStatus> eitherRelevantResources = getComponentsWithMissingToscaArtifacts(resourceClassGetter, NodeTypeEnum.Resource, allResources);
+ fillListOrWrapper(errorWrapper, resourcesWithoutToscaPlaceHolder, eitherRelevantResources);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Add PlaceHolders To Resources
+ log.info("adding tosca placeholders artifacts to resources");
+ addToscaArtifactToComponents(resourcesWithoutToscaPlaceHolder, resourceFetcher, NodeTypeEnum.Resource, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ // Add payload to Resources
+ log.info("generating payload to tosca artifacts on resources");
+ fillResourcesPayload(allResources, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ log.info("Fetching all services");
+ fillAllComponetOfSpecificType(allServices, NodeTypeEnum.Service, serviceClassGetter, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ // Filter Services Without Tosca Artifacts
+ log.info("filtering services to add tosca placeholder");
+ Either<List<ServiceMetadataData>, TitanOperationStatus> eitherRelevantServices = getComponentsWithMissingToscaArtifacts(serviceClassGetter, NodeTypeEnum.Service, allServices);
+ fillListOrWrapper(errorWrapper, servicesWithoutToscaPlaceHolder, eitherRelevantServices);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Add PlaceHolders To Services
+ log.info("adding tosca placeholders artifacts to services");
+ addToscaArtifactToComponents(servicesWithoutToscaPlaceHolder, serviceFetcher, NodeTypeEnum.Service, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Filter Services for Payload Add
+ // Add payload to Services
+ log.info("generating payload to tosca artifacts on services");
+ fillToscaArtifactPayload(allServices, serviceFetcher, errorWrapper);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ return errorWrapper.isEmpty();
+
+ }
+
+ private void fillResourcesPayload(List<ResourceMetadataData> allResources, Wrapper<TitanOperationStatus> errorWrapper) {
+ if (errorWrapper.isEmpty()) {
+ // First Only Non VF (CP, VL & VFC)
+ List<ResourceMetadataData> basicResources = allResources.stream().filter(e -> isBasicResource((ResourceMetadataDataDefinition) e.getMetadataDataDefinition())).collect(Collectors.toList());
+ // Filter resources for Payload Add
+ // Add payload to resources
+ fillToscaArtifactPayload(basicResources, resourceFetcher, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ // VFs
+ List<ResourceMetadataData> complexResource = allResources.stream().filter(e -> ((ResourceMetadataDataDefinition) e.getMetadataDataDefinition()).getResourceType() == ResourceTypeEnum.VF).collect(Collectors.toList());
+ // Filter resources for Payload Add
+ // Add payload to resources
+ fillToscaArtifactPayload(complexResource, resourceFetcher, errorWrapper);
+ }
+ }
+
+ private boolean isBasicResource(ResourceMetadataDataDefinition resourceMetadataDataDefinition) {
+ final ResourceTypeEnum resourceType = resourceMetadataDataDefinition.getResourceType();
+ boolean isBasicResource = resourceType == ResourceTypeEnum.CP || resourceType == ResourceTypeEnum.VL || resourceType == ResourceTypeEnum.VFC;
+ return isBasicResource;
+ }
+
+ private <T extends ComponentMetadataData> void fillAllComponetOfSpecificType(List<T> components, NodeTypeEnum nodeType, Supplier<Class<T>> classGetter, Wrapper<TitanOperationStatus> errorWrapper) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.IS_DELETED.getProperty(), true);
+ Either<List<T>, TitanOperationStatus> eitherComponentMD = titanGenericDao.getByCriteria(nodeType, null, props, classGetter.get());
+ if (eitherComponentMD.isLeft()) {
+ components.addAll(eitherComponentMD.left().value());
+ } else {
+ final TitanOperationStatus errorType = eitherComponentMD.right().value();
+ if (errorType != TitanOperationStatus.NOT_FOUND) {
+ log.error("{} When fetching all components of type:{} a titan error occured:{}", ERROR_PREFIX, nodeType.getName(), errorType.name());
+ errorWrapper.setInnerElement(errorType);
+ }
+ }
+
+ }
+
+ private <T extends ComponentMetadataData, R extends Component> void addToscaArtifactToComponents(List<T> relevantResources, Function<ComponentMetadataData, R> componentConvertor, NodeTypeEnum nodeType,
+ Wrapper<TitanOperationStatus> errorWrapper) {
+
+ // This Stream contains all create tosca placeholder results
+ Stream<StorageOperationStatus> addToscaToComponentsResultsStream = relevantResources.stream().map(e -> addToscaArtifacts(e, nodeType, componentConvertor));
+ // Execute the stream, and collect error
+ Optional<StorageOperationStatus> optionalError = addToscaToComponentsResultsStream.filter(e -> e != StorageOperationStatus.OK).findFirst();
+
+ // Handle error
+ if (optionalError.isPresent()) {
+ errorWrapper.setInnerElement(TitanOperationStatus.NOT_CREATED);
+ }
+ }
+
+ private <R extends Component> R getComponent(ComponentMetadataData md, ComponentTypeEnum componentTypeEnum) {
+ R result = null;
+ Either<R, StorageOperationStatus> eitherComponent = serviceBusinessLogic.getComponent(md.getMetadataDataDefinition().getUniqueId(), componentTypeEnum);
+ if (eitherComponent.isRight()) {
+ log.error("{} When fetching component {} of type:{} with uniqueId:{}", ERROR_PREFIX, md.getMetadataDataDefinition().getName(), componentTypeEnum.getValue(), md.getMetadataDataDefinition().getUniqueId());
+ } else {
+ result = eitherComponent.left().value();
+ }
+ return result;
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> populateToscaArtifactsWithLog(Component component, User user, boolean isInCertificationRequest, boolean inTransaction, boolean shouldLock) {
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> ret;
+ try {
+ ret = serviceBusinessLogic.populateToscaArtifacts(component, user, isInCertificationRequest, inTransaction, shouldLock);
+ if (ret.isLeft()) {
+ log.debug("Added payload to tosca artifacts of component {} of type:{} with uniqueId:{}", component.getName(), component.getComponentType().getValue(), component.getUniqueId());
+ }
+ return ret;
+ } catch (Exception e) {
+ log.error("{} Exception Occured When filling tosca artifact payload for component {} of type:{} with uniqueId:{}", ERROR_PREFIX, component.getName(), component.getComponentType().name(), component.getUniqueId(), e);
+ throw e;
+ }
+ }
+
+ private <R extends Component, T extends ComponentMetadataData> void fillToscaArtifactPayload(List<T> relevantComponents, Function<ComponentMetadataData, R> componentCreator, Wrapper<TitanOperationStatus> errorWrapper) {
+
+ final User dummyUser = buildDummyUser();
+ // Stream for all fill payload results
+ Stream<ImmutablePair<Component, Either<Either<ArtifactDefinition, Operation>, ResponseFormat>>>
+ // Filter elements that needs generation of tosca payload
+ fillToscaPayloadResultsStream = relevantComponents.stream().filter(e -> isGenerateToscaPayload(e))
+ // Converts ComponentMetadataData to Component
+ .map(e -> componentCreator.apply(e))
+ // For each component generate payload for tosca
+ // artifacts
+ .map(component -> {
+ return new ImmutablePair<Component, Either<Either<ArtifactDefinition, Operation>, ResponseFormat>>(component, populateToscaArtifactsWithLog(component, dummyUser, true, true, false));
+ });
+
+ try {
+ // execute and the stream
+ Optional<Component> optionalError = fillToscaPayloadResultsStream.
+ // filter in error
+ filter(e -> e.getRight().isRight())
+ // convert the result to error and execute the stream
+ .map(e -> e.getLeft()).findFirst();
+
+ // Check if error occurred
+ if (optionalError.isPresent()) {
+ Component component = optionalError.get();
+ log.error("{} When filling tosca artifact payload for component {} of type:{} with uniqueId:{}", ERROR_PREFIX, component.getName(), component.getComponentType().name(), component.getUniqueId());
+
+ errorWrapper.setInnerElement(TitanOperationStatus.GENERAL_ERROR);
+ }
+ } catch (Exception e) {
+ log.error("{} When filling tosca artifact payload for components : {}", ERROR_PREFIX, e.getMessage(), e);
+ errorWrapper.setInnerElement(TitanOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+ private <R extends Component> StorageOperationStatus addToscaArtifacts(ComponentMetadataData component, NodeTypeEnum nodeType, Function<ComponentMetadataData, R> componentCreator) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ R componentDefinition = componentCreator.apply(component);
+
+ // Fetch artifacts to be Added
+ Either<List<ArtifactDefinition>, StorageOperationStatus> eitherToscaArtifacts = getToscaArtifactsToAdd(componentDefinition);
+ if (eitherToscaArtifacts.isRight()) {
+ result = eitherToscaArtifacts.right().value();
+ } else {
+ List<ArtifactDefinition> toscaArtifactsToAdd = eitherToscaArtifacts.left().value();
+ if (!CollectionUtils.isEmpty(eitherToscaArtifacts.left().value())) {
+ final Stream<ImmutablePair<ArtifactDefinition, Either<ArtifactDefinition, StorageOperationStatus>>> createdToscaPlaceHolderStream = toscaArtifactsToAdd.stream()
+ // creates the artifact in the graph
+ .map(artifactDef -> new ImmutablePair<ArtifactDefinition, Either<ArtifactDefinition, StorageOperationStatus>>(artifactDef,
+ artifactOperation.addArifactToComponent(artifactDef, componentDefinition.getUniqueId(), nodeType, false, true)));
+
+ // Execute the stream, and collect error
+ Optional<ImmutablePair<ArtifactDefinition, StorageOperationStatus>> optionalError = createdToscaPlaceHolderStream.filter(e -> e.getRight().isRight()).map(e -> new ImmutablePair<>(e.getLeft(), e.getRight().right().value()))
+ .findFirst();
+
+ // In case error occurred
+ if (optionalError.isPresent()) {
+ ArtifactDefinition toscaArtifact = optionalError.get().getLeft();
+ StorageOperationStatus storageError = optionalError.get().getRight();
+ log.error("{} When adding tosca artifact of type {} to component {} of type:{} " + "with uniqueId:{} a storageError occurred:{}", ERROR_PREFIX, toscaArtifact.getArtifactType(), component.getMetadataDataDefinition().getName(),
+ nodeType.getName(), component.getMetadataDataDefinition().getUniqueId(), storageError.name());
+
+ result = storageError;
+ } else {
+ log.debug("Added tosca artifacts to component {} of type:{} with uniqueId:{}", component.getMetadataDataDefinition().getName(), nodeType.getName(), component.getMetadataDataDefinition().getUniqueId());
+ }
+
+ }
+ }
+
+ return result;
+ }
+
+ private <R extends Component> Either<List<ArtifactDefinition>, StorageOperationStatus> getToscaArtifactsToAdd(R componentDefinition) {
+
+ Either<List<ArtifactDefinition>, StorageOperationStatus> result;
+ List<ArtifactDefinition> toscaArtifactsAlreadyExist = new ArrayList<>();
+ if (!MapUtils.isEmpty(componentDefinition.getToscaArtifacts())) {
+ toscaArtifactsAlreadyExist.addAll(componentDefinition.getToscaArtifacts().values());
+ }
+
+ // Set Tosca Artifacts on component
+ serviceBusinessLogic.setToscaArtifactsPlaceHolders(componentDefinition, buildDummyUser());
+
+ List<ArtifactDefinition> toscaArtifactsToAdd = new ArrayList<>();
+ if (!MapUtils.isEmpty(componentDefinition.getToscaArtifacts())) {
+ final Collection<ArtifactDefinition> allToscaArtifacts = componentDefinition.getToscaArtifacts().values();
+ Set<String> artifactTypesExist = toscaArtifactsAlreadyExist.stream().map(e -> e.getArtifactType()).collect(Collectors.toSet());
+ toscaArtifactsToAdd = allToscaArtifacts.stream().filter(e -> !artifactTypesExist.contains(e.getArtifactType())).collect(Collectors.toList());
+ result = Either.left(toscaArtifactsToAdd);
+ } else {
+ log.error("{} failed to add tosca artifacts in bussiness logic to component {} of type:{} with uniqueId:{}", ERROR_PREFIX, componentDefinition.getName(), componentDefinition.getComponentType().getValue(),
+ componentDefinition.getUniqueId());
+ result = Either.right(StorageOperationStatus.ARTIFACT_NOT_FOUND);
+ }
+ return result;
+ }
+
+ private User buildDummyUser() {
+ User user = new User();
+ user.setUserId("migrationTask");
+ return user;
+ }
+
+ private boolean isGenerateToscaPayload(ComponentMetadataData component) {
+ final String state = component.getMetadataDataDefinition().getState();
+ boolean componentLifeCycleStateIsValid = LifecycleStateEnum.NOT_CERTIFIED_CHECKIN.name().equals(state) || LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name().equals(state);
+
+ return !componentLifeCycleStateIsValid;
+
+ }
+
+ private <T> void fillListOrWrapper(Wrapper<TitanOperationStatus> wrapper, List<T> listToFill, Either<List<T>, TitanOperationStatus> either) {
+ if (either.isRight()) {
+ final TitanOperationStatus errorType = either.right().value();
+ if (errorType != TitanOperationStatus.NOT_FOUND) {
+ wrapper.setInnerElement(errorType);
+ }
+ } else {
+ listToFill.addAll(either.left().value());
+ }
+ }
+
+ private <T extends ComponentMetadataData> Either<List<T>, TitanOperationStatus> getComponentsWithMissingToscaArtifacts(Supplier<Class<T>> classGetter, NodeTypeEnum nodeType, List<T> allComponents) {
+
+ Either<List<T>, TitanOperationStatus> result;
+ Stream<ImmutablePair<T, Either<List<ArtifactData>, TitanOperationStatus>>> componentsWithToscaStream =
+ // Create a Stream of pairs : component and its Tosca Artifacts
+ allComponents.stream().map(e -> new ImmutablePair<>(e, getToscaArtifatcs(e, nodeType)));
+
+ List<ImmutablePair<T, Either<List<ArtifactData>, TitanOperationStatus>>> componentsWithToscaArtifacts =
+ // Collect the stream to list.
+ // in case getToscaArtifatcs failed, the first failure is
+ // added to the list
+ // (the collection stops after first failure)
+ StreamUtils.takeWhilePlusOneNoEval(componentsWithToscaStream, e -> e.getRight().isLeft()).collect(Collectors.toList());
+
+ // retrieve the failure optional (it may or may not exist)
+ Optional<TitanOperationStatus> isErrorOccured = componentsWithToscaArtifacts.stream()
+ // convert to the right side of the pair of type Either
+ .map(e -> e.getRight())
+ // Filter in only the errors
+ .filter(e -> e.isRight()).
+ // map the error from Either to TitanOperationStatus
+ map(e -> e.right().value()).findFirst();
+
+ // In case failure occurred
+ if (isErrorOccured.isPresent()) {
+ result = Either.right(isErrorOccured.get());
+ // In case NO failure occurred
+ } else {
+ List<T> filteredComponents = componentsWithToscaArtifacts.stream()
+ // Filter in only elements that does NOT have tosca
+ // artifacts
+ .filter(e -> isNotContainAllToscaArtifacts(e))
+ // Convert back to Components List & collect
+ .map(e -> e.getLeft()).collect(Collectors.toList());
+
+ result = Either.left(filteredComponents);
+ }
+
+ return result;
+ }
+
+ private <T extends ComponentMetadataData> boolean isNotContainAllToscaArtifacts(ImmutablePair<T, Either<List<ArtifactData>, TitanOperationStatus>> pair) {
+
+ final List<ArtifactData> artifactList = pair.getRight().left().value();
+
+ Set<ArtifactTypeEnum> filteredToscaList = artifactList.stream().
+ // Convert to ArtifactDataDefinition
+ map(e -> e.getArtifactDataDefinition()).
+ // Filter in Only Tosca Artifacts
+ filter(e -> e.getArtifactGroupType() == ArtifactGroupTypeEnum.TOSCA).
+ // Convert To ArtifactTypeEnum
+ map(e -> ArtifactTypeEnum.findType(e.getArtifactType())).
+ // Filter Out nulls in case of Type not found
+ filter(e -> e != null).collect(Collectors.toSet());
+
+ boolean toscaArifactContained = filteredToscaList.contains(ArtifactTypeEnum.TOSCA_CSAR) && filteredToscaList.contains(ArtifactTypeEnum.TOSCA_TEMPLATE);
+ return !toscaArifactContained;
+ }
+
+ private <T extends ComponentMetadataData> Either<List<ArtifactData>, TitanOperationStatus> getToscaArtifatcs(T component, NodeTypeEnum nodeType) {
+
+ Either<List<ArtifactData>, TitanOperationStatus> result;
+ // All The Artifacts of the Component
+ Either<List<ImmutablePair<ArtifactData, GraphEdge>>, TitanOperationStatus> eitherComponentArtifacts = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), component.getMetadataDataDefinition().getUniqueId(),
+ GraphEdgeLabels.ARTIFACT_REF, NodeTypeEnum.ArtifactRef, ArtifactData.class);
+
+ if (eitherComponentArtifacts.isLeft()) {
+ // Convert Artifact Edge Pair to Artifact
+ List<ArtifactData> toscaArtifacts = eitherComponentArtifacts.left().value().stream()
+ // map ImmutablePair<ArtifactData, GraphEdge> to
+ // ArtifactData
+ .map(e -> e.getLeft())
+ // Filter in only Tosca Artifacts
+ .filter(artifact -> artifact.getArtifactDataDefinition().getArtifactGroupType() == ArtifactGroupTypeEnum.TOSCA)
+ // Collect
+ .collect(Collectors.toList());
+ result = Either.left(toscaArtifacts);
+ } else if (eitherComponentArtifacts.right().value() == TitanOperationStatus.NOT_FOUND) {
+ result = Either.left(new ArrayList<>());
+ } else {
+ final TitanOperationStatus titanError = eitherComponentArtifacts.right().value();
+ log.error("{} When fetching artifacts for component {} of type:{} with uniqueId:{} a titanError occurred:{}", ERROR_PREFIX, component.getMetadataDataDefinition().getName(), nodeType.getName(),
+ component.getMetadataDataDefinition().getUniqueId(), titanError.name());
+
+ result = Either.right(titanError);
+ }
+
+ return result;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/DataSchemaMenu.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/DataSchemaMenu.java
new file mode 100644
index 0000000000..e29cd7eb75
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/DataSchemaMenu.java
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.main;
+
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.SdcSchemaBuilder;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataSchemaMenu {
+
+ private static Logger log = LoggerFactory.getLogger(DataSchemaMenu.class.getName());
+
+ public static void main(String[] args) throws Exception {
+
+ String operation = args[0];
+
+ String appConfigDir = args[1];
+
+ if (args == null || args.length < 2) {
+ usageAndExit();
+ }
+
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ try {
+
+ switch (operation.toLowerCase()) {
+ case "create-cassandra-structures":
+ log.debug("Start create cassandra keyspace, tables and indexes");
+ if (SdcSchemaBuilder.createSchema()) {
+ log.debug("create cassandra keyspace, tables and indexes successfull");
+ System.exit(0);
+ } else {
+ log.debug("create cassandra keyspace, tables and indexes failed");
+ System.exit(2);
+ }
+ case "create-titan-structures":
+ log.debug("Start create titan keyspace, tables and indexes");
+ if (SdcSchemaBuilder.createSchema()) {
+ log.debug("create cassandra keyspace, tables and indexes successfull");
+ System.exit(0);
+ } else {
+ log.debug("create cassandra keyspace, tables and indexes failed");
+ System.exit(2);
+ }
+ case "clean-cassndra":
+ log.debug("Start clean keyspace, tables");
+ if (SdcSchemaBuilder.deleteSchema()) {
+ log.debug(" successfull");
+ System.exit(0);
+ } else {
+ log.debug(" failed");
+ System.exit(2);
+ }
+ default:
+ usageAndExit();
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ log.debug("create cassandra keyspace, tables and indexes failed");
+ System.exit(3);
+ }
+ }
+
+ private static void usageAndExit() {
+ DataSchemeUsage();
+ System.exit(1);
+ }
+
+ private static void DataSchemeUsage() {
+ System.out.println("Usage: create-cassandra-structures <configuration dir> ");
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/EsToCassandraDataMigrationMenu.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/EsToCassandraDataMigrationMenu.java
new file mode 100644
index 0000000000..17008b31a4
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/EsToCassandraDataMigrationMenu.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.main;
+
+import org.openecomp.sdc.asdctool.impl.DataMigration;
+import org.openecomp.sdc.asdctool.impl.EsToCassandraDataMigrationConfig;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.AppConfig;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.ServiceMigration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.SdcSchemaBuilder;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+public class EsToCassandraDataMigrationMenu {
+
+ private static Logger log = LoggerFactory.getLogger(MigrationMenu.class.getName());
+
+ public static void main(String[] args) throws Exception {
+
+ if (args == null || args.length < 2) {
+ usageAndExit();
+ }
+ String operation = args[0];
+
+ String appConfigDir = args[1];
+ System.setProperty("config.home", appConfigDir);
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
+ EsToCassandraDataMigrationConfig.class);
+ DataMigration dataMigration = null;
+ try {
+ switch (operation.toLowerCase()) {
+ case "es-to-cassndra-migration":
+ dataMigration = (DataMigration) context.getBean("DataMigrationBean");
+ log.debug("Start migration from ES to C* ");
+ if (dataMigration.migrateDataESToCassndra(appConfigDir, true, true)) {
+ log.debug("migration from ES to C* was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("migration from ES to C* failed");
+ System.exit(2);
+ }
+ break;
+ case "es-to-cassndra-migration-export-only":
+ dataMigration = (DataMigration) context.getBean("DataMigrationBean");
+ log.debug("Start migration export only from ES to C* ");
+ if (dataMigration.migrateDataESToCassndra(appConfigDir, true, false)) {
+ log.debug("migration export only from ES to C* was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("migration export only from ES to C* failed");
+ System.exit(2);
+ }
+ break;
+ case "es-to-cassndra-migration-import-only":
+ dataMigration = (DataMigration) context.getBean("DataMigrationBean");
+ log.debug("Start migration import only from ES to C* ");
+ if (dataMigration.migrateDataESToCassndra(appConfigDir, false, true)) {
+ log.debug("migration import only from ES to C* was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("migration import only from ES to C* failed");
+ System.exit(2);
+ }
+ break;
+ default:
+ usageAndExit();
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ System.exit(3);
+ }
+ }
+
+ private static void usageAndExit() {
+ MigrationUsage();
+ System.exit(1);
+ }
+
+ private static void MigrationUsage() {
+ System.out.println(
+ "Usage: es-to-cassndra-migration/es-to-cassndra-migration-import-only/es-to-cassndra-migration-export-only <configuration dir>");
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/ExportImportMenu.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/ExportImportMenu.java
new file mode 100644
index 0000000000..6b6f11c5a6
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/ExportImportMenu.java
@@ -0,0 +1,169 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.main;
+
+import org.openecomp.sdc.asdctool.impl.GraphMLConverter;
+import org.openecomp.sdc.asdctool.impl.GraphMLDataAnalyzer;
+
+public class ExportImportMenu {
+
+ private static void usageAndExit() {
+ exportUsage();
+ importUsage();
+ exportUsersUsage();
+
+ System.exit(1);
+ }
+
+ private static void importUsage() {
+ System.out.println("Usage: import <titan.properties> <graph file location>");
+ }
+
+ private static void exportUsage() {
+ System.out.println("Usage: export <titan.properties> <output directory>");
+ }
+
+ private static void dataReportUsage() {
+ System.out.println("Usage: get-data-report-from-graph-ml <full path of .graphml file>");
+ }
+
+ private static void exportUsersUsage() {
+ System.out.println("Usage: exportusers <titan.properties> <output directory>");
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ if (args == null || args.length < 1) {
+ usageAndExit();
+ }
+
+ String operation = args[0];
+ GraphMLConverter graphMLConverter = new GraphMLConverter();
+ switch (operation.toLowerCase()) {
+
+ case "export":
+ boolean isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ exportUsage();
+ System.exit(1);
+ }
+
+ boolean result = graphMLConverter.exportGraph(args);
+ if (result == false) {
+ System.exit(2);
+ }
+
+ break;
+ case "import":
+ isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ importUsage();
+ System.exit(1);
+ }
+ result = graphMLConverter.importGraph(args);
+ if (result == false) {
+ System.exit(2);
+ }
+ break;
+
+ case "exportusers":
+ isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ importUsage();
+ System.exit(1);
+ }
+ result = graphMLConverter.exportUsers(args);
+ if (result == false) {
+ System.exit(2);
+ }
+ break;
+
+ case "findproblem":
+ isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ importUsage();
+ System.exit(1);
+ }
+ result = graphMLConverter.findErrorInJsonGraph(args);
+ if (result == false) {
+ System.exit(2);
+ }
+ break;
+ case "export-as-graph-ml":
+ isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ exportUsage();
+ System.exit(1);
+ }
+ String mlFile = graphMLConverter.exportGraphMl(args);
+ if (mlFile == null) {
+ System.exit(2);
+ }
+ break;
+ case "export-as-graph-ml-with-data-report":
+ isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ exportUsage();
+ System.exit(1);
+ }
+ mlFile = graphMLConverter.exportGraphMl(args);
+ if (mlFile == null) {
+ System.exit(2);
+ }
+ String[] dataArgs = new String[] { mlFile };
+ mlFile = new GraphMLDataAnalyzer().analyzeGraphMLData(dataArgs);
+ if (mlFile == null) {
+ System.exit(2);
+ }
+ break;
+ case "get-data-report-from-graph-ml":
+ isValid = verifyParamsLength(args, 2);
+ if (false == isValid) {
+ dataReportUsage();
+ System.exit(1);
+ }
+ dataArgs = new String[] { args[1] };
+ mlFile = new GraphMLDataAnalyzer().analyzeGraphMLData(dataArgs);
+ if (mlFile == null) {
+ System.exit(2);
+ }
+ break;
+ default:
+ usageAndExit();
+ }
+
+ }
+
+ private static boolean verifyParamsLength(String[] args, int i) {
+ if (args == null) {
+ if (i > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ if (args.length >= i) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/MigrationMenu.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/MigrationMenu.java
new file mode 100644
index 0000000000..d7ed4600dd
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/MigrationMenu.java
@@ -0,0 +1,251 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.main;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import org.openecomp.sdc.asdctool.impl.PopulateComponentCache;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.AppConfig;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.DerivedFromAlignment;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.GroupsAlignment;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.ServiceMigration;
+import org.openecomp.sdc.asdctool.impl.migration.v1604.VfcNamingAlignment;
+import org.openecomp.sdc.asdctool.impl.migration.v1607.CsarMigration;
+import org.openecomp.sdc.asdctool.impl.migration.v1610.TitanFixUtils;
+import org.openecomp.sdc.asdctool.impl.migration.v1610.ToscaArtifactsAlignment;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+public class MigrationMenu {
+
+ private static Logger log = LoggerFactory.getLogger(MigrationMenu.class.getName());
+ private static final String SERVICE_MIGARTION_BEAN = "serviceMigrationBean";
+
+ private static enum MigrationOperationEnum {
+ MIGRATION_1602_1604("migrate-1602-1604", SERVICE_MIGARTION_BEAN),
+ ALIGN_DERIVED_FROM_1604("align-derived-from-1604", "derivedFromAlignment"),
+ MIGRATE_1604_1607("migrate-1604-1607", SERVICE_MIGARTION_BEAN),
+ ALIGN_VFC_NAMES_1604("align-vfc-names-1604", "vfcNamingAlignmentBean"),
+ TEST_REMOVE_HEAT_PLACEHOLDERS("testremoveheatplaceholders", SERVICE_MIGARTION_BEAN),
+ TEST_ADD_GROUP_UUIDS("testaddgroupuuids", SERVICE_MIGARTION_BEAN),
+ ALIGN_GROUPS("align-groups", "groupsAlignment"),
+ CLEAN_CSAR("clean-csar", "csarMigration"),
+ POPULATE_COMPONENT_CACHE("populate-component-cache", "populateComponentCache"),
+ FIX_PROPERTIES("fix-properties", "titanFixUtils"),
+ ALIGN_TOSCA_ARTIFACTS("align-tosca-artifacts", "toscaArtifactsAlignment"),
+ FIX_ICONS("fix-icons", "titanFixUtils");
+
+ private String value, beanName;
+
+ public static MigrationOperationEnum findByValue(String value) {
+ Optional<MigrationOperationEnum> optionalFound = Arrays.asList(MigrationOperationEnum.values()).stream().filter(e -> e.getValue().equalsIgnoreCase(value)).findAny();
+ return optionalFound.isPresent() ? optionalFound.get() : null;
+ }
+
+ MigrationOperationEnum(String value, String beanName) {
+ this.value = value;
+ this.beanName = beanName;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getBeanName() {
+ return beanName;
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+
+ if (args == null || args.length < 2) {
+ usageAndExit();
+ }
+ MigrationOperationEnum operationEnum = MigrationOperationEnum.findByValue(args[0]);
+ String appConfigDir = args[1];
+ String dataInputFileDir = null;
+ if (operationEnum == MigrationOperationEnum.ALIGN_DERIVED_FROM_1604) {
+ dataInputFileDir = args[2];
+ }
+ AnnotationConfigApplicationContext context = initContext(appConfigDir);
+ try {
+ ServiceMigration serviceMigration = (ServiceMigration) context.getBean(SERVICE_MIGARTION_BEAN);
+ switch (operationEnum) {
+ case MIGRATION_1602_1604:
+ log.debug("Start Titan migration from 1602 version to 1604");
+ if (serviceMigration.migrate1602to1604(appConfigDir)) {
+ log.debug("Titan migration from 1602 version to 1604 was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("Titan migration from 1602 version to 1604 was failed");
+ System.exit(2);
+ }
+ case MIGRATE_1604_1607:
+ log.debug("Start Titan migration from 1604 version to 1607");
+ if (serviceMigration.migrate1604to1607(appConfigDir)) {
+ log.debug("Titan migration from 1604 version to 1607 was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("Titan migration from 1604 version to 1607 was failed");
+ System.exit(2);
+ }
+ break;
+ case ALIGN_VFC_NAMES_1604:
+ VfcNamingAlignment vfcNamingAlignment = (VfcNamingAlignment) context.getBean(operationEnum.getBeanName());
+ log.debug("Start VFC naming alignment on 1604");
+ if (vfcNamingAlignment.alignVfcNames1604(appConfigDir)) {
+ log.debug("VFC naming alignment on 1604 was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("VFC naming alignment on 1604 was failed");
+ System.exit(2);
+ }
+ break;
+ case TEST_REMOVE_HEAT_PLACEHOLDERS:
+ boolean check = serviceMigration.testRemoveHeatPlaceHolders(appConfigDir);
+ if (check == true) {
+ System.exit(0);
+ } else {
+ System.exit(2);
+ }
+ break;
+ case TEST_ADD_GROUP_UUIDS:
+ check = serviceMigration.testAddGroupUuids(appConfigDir);
+ if (check == true) {
+ System.exit(0);
+ } else {
+ System.exit(2);
+ }
+ break;
+ case ALIGN_DERIVED_FROM_1604:
+ DerivedFromAlignment derivedFromAlignment = (DerivedFromAlignment) context.getBean(operationEnum.getBeanName());
+ log.debug("Start derived from alignment on 1604");
+ if (derivedFromAlignment.alignDerivedFrom1604(appConfigDir, dataInputFileDir)) {
+ log.debug("Derived from alignment on 1604 was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("Derived from alignment on 1604 was failed");
+ System.exit(2);
+ }
+ break;
+ case ALIGN_GROUPS:
+ GroupsAlignment groupsAlignment = (GroupsAlignment) context.getBean(operationEnum.getBeanName());
+ log.debug("Start derived from alignment on 1604");
+ if (groupsAlignment.alignGroups(appConfigDir)) {
+ log.debug("Groups alignment was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("Groups alignment was failed");
+ System.exit(2);
+ }
+ break;
+ case CLEAN_CSAR:
+ log.debug("Start remove CSAR resources");
+ CsarMigration csarMigration = (CsarMigration) context.getBean(operationEnum.getBeanName());
+ // TODO Show to Michael L fixed return value
+ if (csarMigration.removeCsarResources()) {
+ log.debug("Remove CSAR resources finished successfully");
+ System.exit(0);
+ } else {
+ log.debug("Remove CSAR resources failed");
+ System.exit(2);
+ }
+ break;
+ case POPULATE_COMPONENT_CACHE:
+ PopulateComponentCache populateComponentCache = (PopulateComponentCache) context.getBean(operationEnum.getBeanName());
+ // TODO Show to Michael L No return value always returns 0
+ populateComponentCache.populateCache();
+ System.exit(0);
+ break;
+ case FIX_PROPERTIES:
+ log.debug("Start fix capability properties types");
+ TitanFixUtils titanFixUtils = (TitanFixUtils) context.getBean(operationEnum.getBeanName());
+ // TODO Show to Michael L fixed return value
+ if (titanFixUtils.fixCapabiltyPropertyTypes()) {
+ log.debug("Fix capability properties types finished successfully");
+ System.exit(0);
+ } else {
+ log.debug("Fix capability properties types failed");
+ System.exit(2);
+ }
+ break;
+ case FIX_ICONS:
+ log.debug("Start fix icons of vl and eline");
+ titanFixUtils = (TitanFixUtils) context.getBean(operationEnum.getBeanName());
+ // TODO Show to Michael L fixed return value
+ if (titanFixUtils.fixIconsInNormatives()) {
+ log.debug("Fix icons of vl and eline finished successfully");
+ System.exit(0);
+ } else {
+ log.debug("Fix icons of vl and eline failed");
+ System.exit(2);
+ }
+ break;
+ case ALIGN_TOSCA_ARTIFACTS:
+ log.debug("Start align tosca artifacts");
+ ToscaArtifactsAlignment toscaArtifactsAlignment = (ToscaArtifactsAlignment) context.getBean(operationEnum.getBeanName());
+ boolean isSuccessful = toscaArtifactsAlignment.alignToscaArtifacts();
+ if (isSuccessful) {
+ log.debug("Tosca Artifacts alignment was finished successfull");
+ System.exit(0);
+ } else {
+ log.debug("Tosca Artifacts alignment has failed");
+ System.exit(2);
+ }
+ break;
+ default:
+ usageAndExit();
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ System.exit(3);
+ } finally {
+ context.close();
+ }
+ }
+
+ private static AnnotationConfigApplicationContext initContext(String appConfigDir) {
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
+ return context;
+ }
+
+ private static void usageAndExit() {
+ MigrationUsage();
+ System.exit(1);
+ }
+
+ private static void MigrationUsage() {
+ System.out.println("Usage: migrate-1602-1604 <configuration dir>");
+ System.out.println("Usage: migrate-1604-1607 <configuration dir>");
+ System.out.println("Usage: align-vfc-names-1604 <configuration dir>");
+ System.out.println("Usage: align-derived-from-1604 <configuration dir> <data_input_file dir>");
+ System.out.println("Usage: align-groups <configuration dir>");
+ System.out.println("Usage: fix-properties <configuration dir>");
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/RemoveUtils.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/RemoveUtils.java
new file mode 100644
index 0000000000..ddece759c0
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/RemoveUtils.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.main;
+
+import org.openecomp.sdc.asdctool.impl.ProductLogic;
+
+/**
+ * Created by mlando on 2/23/2016.
+ */
+public class RemoveUtils {
+
+ public static void main(String[] args) throws Exception {
+
+ if (args == null || args.length < 1) {
+ removeUsage();
+ }
+
+ String operation = args[0];
+
+ switch (operation.toLowerCase()) {
+
+ case "remove-products":
+
+ boolean isValid = verifyParamsLength(args, 5);
+ if (false == isValid) {
+ removeUsage();
+ System.exit(1);
+ }
+
+ ProductLogic productLogic = new ProductLogic();
+ boolean result = productLogic.deleteAllProducts(args[1], args[2], args[3], args[4]);
+
+ if (result == false) {
+ System.exit(2);
+ }
+ break;
+ default:
+ removeUsage();
+ }
+
+ }
+
+ private static void removeUsage() {
+ System.out.println("Usage: remove-products <titan.properties> <BE host> <BE port> <admin user>");
+ }
+
+ private static boolean verifyParamsLength(String[] args, int i) {
+ if (args == null) {
+ if (i > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ if (args.length >= i) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/UpdateIsVnfMenu.java b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/UpdateIsVnfMenu.java
new file mode 100644
index 0000000000..e620510916
--- /dev/null
+++ b/asdctool/src/main/java/org/openecomp/sdc/asdctool/main/UpdateIsVnfMenu.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.asdctool.main;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.asdctool.impl.UpdatePropertyOnVertex;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UpdateIsVnfMenu {
+
+ private static Logger log = LoggerFactory.getLogger(UpdateIsVnfMenu.class.getName());
+
+ private static void usageAndExit() {
+ updateIsVnfTrueUsage();
+ System.exit(1);
+ }
+
+ private static void updateIsVnfTrueUsage() {
+ System.out.println(
+ "Usage: updateIsVnfTrue <titan.properties> <systemServiceName1,systemServiceName2,...,systemServiceNameN>");
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ if (args == null || args.length < 1) {
+ usageAndExit();
+ }
+
+ UpdatePropertyOnVertex updatePropertyOnVertex = new UpdatePropertyOnVertex();
+ String operation = args[0];
+
+ switch (operation.toLowerCase()) {
+
+ case "updateisvnftrue":
+ boolean isValid = verifyParamsLength(args, 3);
+ if (false == isValid) {
+ updateIsVnfTrueUsage();
+ System.exit(1);
+ }
+
+ Map<String, Object> keyValueToSet = new HashMap<>();
+ keyValueToSet.put(GraphPropertiesDictionary.IS_VNF.getProperty(), true);
+
+ List<Map<String, Object>> orCriteria = buildCriteriaFromSystemServiceNames(args[2]);
+ Integer updatePropertyOnServiceAtLeastCertified = updatePropertyOnVertex
+ .updatePropertyOnServiceAtLeastCertified(args[1], keyValueToSet, orCriteria);
+
+ if (updatePropertyOnServiceAtLeastCertified == null) {
+ System.exit(2);
+ } else if (updatePropertyOnServiceAtLeastCertified.intValue() >= 0) {
+ log.debug("Number of updated services is {}", updatePropertyOnServiceAtLeastCertified.intValue());
+ System.exit(0);
+ }
+
+ break;
+ default:
+ usageAndExit();
+ }
+
+ }
+
+ private static List<Map<String, Object>> buildCriteriaFromSystemServiceNames(String systemList) {
+
+ List<Map<String, Object>> systemNames = new ArrayList<>();
+
+ String[] split = systemList.split(",");
+ if (split != null) {
+ for (String systemName : split) {
+ systemName = systemName.trim();
+
+ Map<String, Object> map = new HashMap();
+ map.put(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), systemName);
+ map.put(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Service.getName());
+
+ systemNames.add(map);
+ }
+ }
+
+ return systemNames;
+ }
+
+ private static boolean verifyParamsLength(String[] args, int i) {
+ if (args == null) {
+ if (i > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ if (args.length >= i) {
+ return true;
+ }
+ return false;
+ }
+
+}
diff --git a/asdctool/src/main/resources/application-context.xml b/asdctool/src/main/resources/application-context.xml
new file mode 100644
index 0000000000..cf5ae5a767
--- /dev/null
+++ b/asdctool/src/main/resources/application-context.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
+
+
+ <util:properties id="elasticsearchConfig" location="file:${config.home}/elasticsearch.yml" />
+
+</beans>
diff --git a/asdctool/src/main/resources/config/categoryMigration.yaml b/asdctool/src/main/resources/config/categoryMigration.yaml
new file mode 100644
index 0000000000..b62dd0a649
--- /dev/null
+++ b/asdctool/src/main/resources/config/categoryMigration.yaml
@@ -0,0 +1,113 @@
+services:
+ Mobility:
+ name: "Mobility"
+ oldName: "Mobility"
+ icons: ['mobility']
+ Network_L1_3:
+ name: "Network L1-3"
+ oldName: "Network L1-3"
+ icons: ['network_l_1-3']
+ Network_L4:
+ name: "Network L4+"
+ oldName: "Network L4"
+ icons: ['network_l_4']
+ VoIP_Call_Control:
+ name: "VoIP Call Control"
+ oldName: "VoIP Call Control"
+ icons: ['call_controll']
+resources:
+ NetworkLayer23:
+ name: "Network L2-3"
+ oldName: "Network Layer 2-3"
+ subcategories:
+ Router:
+ name: "Router"
+ oldName: "Router"
+ icons: ['router','vRouter']
+ Gateway:
+ name: "Gateway"
+ oldName: "Gateway"
+ icons: ['gateway']
+ WAN_Connectors:
+ name: "WAN Connectors"
+ oldName: "WAN Connectors"
+ icons: ['network','connector','port']
+ LAN_Connectors:
+ name: "LAN Connectors"
+ oldName: "LAN Connectors"
+ icons: ['network','connector','port']
+ Infrastructure:
+ name: "Infrastructure"
+ icons: ['ucpe']
+ NetworkLayer4:
+ name: "Network L4+"
+ oldName: "Network Layer 4+"
+ subcategories:
+ Common_Network_Resources:
+ name: "Common Network Resources"
+ oldName: "Common Network Resources"
+ icons: ['network']
+ ApplicationLayer4:
+ name: "Application L4+"
+ oldName: "Application Layer 4+"
+ subcategories:
+ Border_Element:
+ name: "Border Element"
+ oldName: "Border Elements"
+ icons: ['borderElement']
+ Application_Server:
+ name: "Application Server"
+ oldName: "Application Servers"
+ icons: ['applicationServer']
+ Web_Server:
+ name: "Web Server"
+ oldName: "Web Server"
+ icons: ['applicationServer']
+ Call_Control:
+ name: "Call Control"
+ oldName: "Call Control"
+ icons: ['call_controll']
+ Media_Servers:
+ name: "Media Servers"
+ oldName: "Media Servers"
+ icons: ['applicationServer']
+ Load_Balancer:
+ name: "Load Balancer"
+ oldName: "Load Balancer"
+ icons: ['loadBalancer']
+ Database:
+ name: "Database"
+ oldName: "Database"
+ icons: ['database']
+ Firewall:
+ name: "Firewall"
+ icons: ['firewall']
+ Generic:
+ name: "Generic"
+ oldName: "Generic"
+ subcategories:
+ Infrastructure:
+ name: "Infrastructure"
+ oldName: "Infrastructure"
+ icons: ['connector']
+ Abstract:
+ name: "Abstract"
+ oldName: "Abstract"
+ icons: ['objectStorage', 'compute']
+ Network_Elements:
+ name: "Network Elements"
+ oldName: "Network Elements"
+ icons: ['network', 'connector']
+ Database:
+ name: "Database"
+ oldName: "Database"
+ icons: ['database']
+ NetworkConnectivity:
+ name: "Network Connectivity"
+ subcategories:
+ ConnectionPoints:
+ name: "Connection Points"
+ icons: ['cp']
+ VirtualLinks:
+ name: "Virtual Links"
+ icons: ['vl'] \ No newline at end of file
diff --git a/asdctool/src/main/resources/config/configuration.yaml b/asdctool/src/main/resources/config/configuration.yaml
new file mode 100644
index 0000000000..294424fa9a
--- /dev/null
+++ b/asdctool/src/main/resources/config/configuration.yaml
@@ -0,0 +1,376 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+
+# catalog backend hostname
+beFqdn: sdccatalog
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: src/main/resources/config/titan.properties
+titanInMemoryGraph: false
+titanLockTimeout: 30
+titanReconnectIntervalInSeconds: 3
+titanHealthCheckReadTimeout: 1
+esReconnectIntervalInSeconds: 3
+uebHealthCheckReconnectIntervalInSeconds: 15
+uebHealthCheckReadTimeout: 4
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+neo4j:
+ host: neo4jhost
+ port: 7474
+ user: neo4j
+ password: "12345"
+
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # PLEASE NOTE: the timestamps are created in UTC/GMT timezone! This is needed so that timestamps will be correctly presented in Kibana.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+ - indexPrefix: monitoring_events
+ creationPeriod: month
+
+artifactTypes:
+ - CHEF
+ - PUPPET
+ - SHELL
+ - YANG
+ - YANG_XML
+ - HEAT
+ - BPEL
+ - DG_XML
+ - MURANO_PKG
+ - WORKFLOW
+ - NETWORK_CALL_FLOW
+ - TOSCA_TEMPLATE
+ - TOSCA_CSAR
+ - AAI_SERVICE_MODEL
+ - AAI_VF_MODEL
+ - AAI_VF_MODULE_MODEL
+ - AAI_VF_INSTANCE_MODEL
+ - OTHER
+
+licenseTypes:
+ - User
+ - Installation
+ - CPU
+
+#Deployment artifacts placeHolder
+resourceTypes: &allResourceTypes
+ - VFC
+ - CP
+ - VL
+ - VF
+
+# validForResourceTypes usage
+# validForResourceTypes:
+# - VF
+# - VL
+deploymentResourceArtifacts:
+ heat:
+ displayName: "Base HEAT Template"
+ type: HEAT
+ validForResourceTypes: *allResourceTypes
+ heatVol:
+ displayName: "Volume HEAT Template"
+ type: HEAT_VOL
+ validForResourceTypes: *allResourceTypes
+ heatNet:
+ displayName: "Network HEAT Template"
+ type: HEAT_NET
+ validForResourceTypes: *allResourceTypes
+
+deploymentResourceInstanceArtifacts:
+ heatEnv:
+ displayName: "HEAT ENV"
+ type: HEAT_ENV
+ description: "Auto-generated HEAT Environment deployment artifact"
+ fileExtension: "env"
+
+#tosca artifacts placeholders
+toscaArtifacts:
+ assetToscaTemplate:
+ artifactName: -template.yml
+ displayName: Tosca Template
+ type: TOSCA_TEMPLATE
+ description: TOSCA representation of the asset
+ assetToscaCsar:
+ artifactName: -csar.csar
+ displayName: Tosca Model
+ type: TOSCA_CSAR
+ description: TOSCA definition package of the asset
+
+#Informational artifacts placeHolder
+excludeResourceCategory:
+ - Generic
+informationalResourceArtifacts:
+ features:
+ displayName: Features
+ type: OTHER
+ capacity:
+ displayName: Capacity
+ type: OTHER
+ vendorTestResult:
+ displayName: Vendor Test Result
+ type: OTHER
+ testScripts:
+ displayName: Test Scripts
+ type: OTHER
+ cloudQuestionnaire:
+ displayName: Cloud Questionnaire (completed)
+ type: OTHER
+ HEATTemplateFromVendor:
+ displayName: HEAT Template from Vendor
+ type: HEAT
+ resourceSecurityTemplate:
+ displayName: Resource Security Template
+ type: OTHER
+
+excludeServiceCategory:
+
+informationalServiceArtifacts:
+ serviceArtifactPlan:
+ displayName: Service Artifact Plan
+ type: OTHER
+ summaryOfImpactsToECOMPElements:
+ displayName: Summary of impacts to ECOMP elements,OSSs, BSSs
+ type: OTHER
+ controlLoopFunctions:
+ displayName: Control Loop Functions
+ type: OTHER
+ dimensioningInfo:
+ displayName: Dimensioning Info
+ type: OTHER
+ affinityRules:
+ displayName: Affinity Rules
+ type: OTHER
+ operationalPolicies:
+ displayName: Operational Policies
+ type: OTHER
+ serviceSpecificPolicies:
+ displayName: Service-specific Policies
+ type: OTHER
+ engineeringRules:
+ displayName: Engineering Rules (ERD)
+ type: OTHER
+ distributionInstructions:
+ displayName: Distribution Instructions
+ type: OTHER
+ certificationTestResults:
+ displayName: TD Certification Test Results
+ type: OTHER
+ deploymentVotingRecord:
+ displayName: Deployment Voting Record
+ type: OTHER
+ serviceQuestionnaire:
+ displayName: Service Questionnaire
+ type: OTHER
+ serviceSecurityTemplate:
+ displayName: Service Security Template
+ type: OTHER
+
+serviceApiArtifacts:
+ configuration:
+ displayName: Configuration
+ type: OTHER
+ instantiation:
+ displayName: Instantiation
+ type: OTHER
+ monitoring:
+ displayName: Monitoring
+ type: OTHER
+ reporting:
+ displayName: Reporting
+ type: OTHER
+ logging:
+ displayName: Logging
+ type: OTHER
+ testing:
+ displayName: Testing
+ type: OTHER
+
+
+additionalInformationMaxNumberOfKeys: 50
+
+systemMonitoring:
+ enabled: false
+ isProxy: false
+ probeIntervalInSeconds: 15
+
+defaultHeatArtifactTimeoutMinutes: 60
+
+serviceDeploymentArtifacts:
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ AAI_SERVICE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_INSTANCE_MODEL:
+ acceptedTypes:
+ - xml
+ OTHER:
+ acceptedTypes:
+
+resourceDeploymentArtifacts:
+ HEAT:
+ acceptedTypes:
+ - yaml
+ - yml
+ HEAT_VOL:
+ acceptedTypes:
+ - yaml
+ - yml
+ HEAT_NET:
+ acceptedTypes:
+ - yaml
+ - yml
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ AAI_VF_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ OTHER:
+ acceptedTypes:
+
+resourceInstanceDeploymentArtifacts:
+ HEAT_ENV:
+ acceptedTypes:
+ - env
+
+resourceInformationalDeployedArtifacts:
+
+
+requirementsToFulfillBeforeCert:
+ CP:
+ - tosca.capabilities.network.Bindable
+
+capabilitiesToConsumeBeforeCert:
+
+unLoggedUrls:
+ - /sdc2/rest/healthCheck
+
+cleanComponentsConfiguration:
+ cleanIntervalInMinutes: 1440
+ componentsToClean:
+ - Resource
+ - Service
+
+artifactsIndex: resources
+cassandraConfig:
+ cassandraHosts: ['localhost']
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: false
+ username: koko
+ password: bobo
+ ssl: false
+ truststorePath : /path/path
+ truststorePassword : 123123
+ keySpaces:
+ - { name: sdcaudit, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdcartifact, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdccomponent, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+
+
+switchoverDetector:
+ gBeFqdn:
+ gFeFqdn:
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl:
+ publishNetworkBody: '{"note":"publish network"}'
+ groups:
+ beSet: { changePriorityUrl: "http://localhost/",
+ changePriorityBody: '{"name":"AIO-BE.ecomp.idns.cip","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip","no_ad_redirection":false,"v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_be","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_be"],"failover_policy":["FAILALL"]},"comment":"AIO BE G-fqdn","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "http://localhost/",
+ changePriorityBody: '{"comment":"AIO G-fqdn","name":"AIO-FE.ecomp.idns.cip","v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_fe","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_fe"],"failover_policy":["FAILALL"]},"no_ad_redirection":false,"intended_app_proto":"DNS","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip.att.com"}'}
+
+
+heatEnvArtifactHeader:
+ ""
+heatEnvArtifactFooter:
+ ""
+
+applicationL1Cache:
+ datatypes:
+ enabled: false
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: false
+ catalogL1Cache:
+ enabled: false
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 60
+ waitOnShutDownInMinutes: 30
+ numberOfCacheWorkers: 4
+
+toscaValidators:
+ stringMaxLength: 1024
+disableAudit: true \ No newline at end of file
diff --git a/asdctool/src/main/resources/config/elasticsearch.yml b/asdctool/src/main/resources/config/elasticsearch.yml
new file mode 100644
index 0000000000..e1808ad7cc
--- /dev/null
+++ b/asdctool/src/main/resources/config/elasticsearch.yml
@@ -0,0 +1,392 @@
+
+elasticSearch.local: true
+elasticSearch.transportclient: false
+cluster.name: elasticsearch_1_5_2222
+
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+discovery.zen.ping.unicast.hosts: 1.2.3.4
+transport.client.initial_nodes:
+ - 1.2.3.4:9300
+
+
+#plugin.types: "DeleteByQueryPlugin"
+
+##################### Elasticsearch Configuration Example #####################
+
+# This file contains an overview of various configuration settings,
+# targeted at operations staff. Application developers should
+# consult the guide at <http://elasticsearch.org/guide>.
+#
+# The installation procedure is covered at
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
+#
+# Elasticsearch comes with reasonable defaults for most settings,
+# so you can try it out without bothering with configuration.
+#
+# Most of the time, these defaults are just fine for running a production
+# cluster. If you're fine-tuning your cluster, or wondering about the
+# effect of certain configuration option, please _do ask_ on the
+# mailing list or IRC channel [http://elasticsearch.org/community].
+
+# Any element in the configuration can be replaced with environment variables
+# by placing them in ${...} notation. For example:
+#
+# node.rack: ${RACK_ENV_VAR}
+
+# For information on supported formats and syntax for the config file, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
+
+
+################################### Cluster ###################################
+
+# Cluster name identifies your cluster for auto-discovery. If you're running
+# multiple clusters on the same network, make sure you're using unique names.
+#
+# cluster.name: elasticsearch
+
+
+#################################### Node #####################################
+
+# Node names are generated dynamically on startup, so you're relieved
+# from configuring them manually. You can tie this node to a specific name:
+#
+# node.name: "Franz Kafka"
+
+# Every node can be configured to allow or deny being eligible as the master,
+# and to allow or deny to store the data.
+#
+# Allow this node to be eligible as a master node (enabled by default):
+#
+# node.master: true
+#
+# Allow this node to store data (enabled by default):
+#
+# node.data: true
+
+# You can exploit these settings to design advanced cluster topologies.
+#
+# 1. You want this node to never become a master node, only to hold data.
+# This will be the "workhorse" of your cluster.
+#
+# node.master: false
+# node.data: true
+#
+# 2. You want this node to only serve as a master: to not store any data and
+# to have free resources. This will be the "coordinator" of your cluster.
+#
+# node.master: true
+# node.data: false
+#
+# 3. You want this node to be neither master nor data node, but
+# to act as a "search load balancer" (fetching data from nodes,
+# aggregating results, etc.)
+#
+# node.master: false
+# node.data: false
+
+# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
+# Node Info API [http://localhost:9200/_nodes] or GUI tools
+# such as <http://www.elasticsearch.org/overview/marvel/>,
+# <http://github.com/karmi/elasticsearch-paramedic>,
+# <http://github.com/lukas-vlcek/bigdesk> and
+# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
+
+# A node can have generic attributes associated with it, which can later be used
+# for customized shard allocation filtering, or allocation awareness. An attribute
+# is a simple key value pair, similar to node.key: value, here is an example:
+#
+# node.rack: rack314
+
+# By default, multiple nodes are allowed to start from the same installation location
+# to disable it, set the following:
+# node.max_local_storage_nodes: 1
+
+
+#################################### Index ####################################
+
+# You can set a number of options (such as shard/replica options, mapping
+# or analyzer definitions, translog settings, ...) for indices globally,
+# in this file.
+#
+# Note, that it makes more sense to configure index settings specifically for
+# a certain index, either when creating it or by using the index templates API.
+#
+# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
+# for more information.
+
+# Set the number of shards (splits) of an index (5 by default):
+#
+# index.number_of_shards: 5
+
+# Set the number of replicas (additional copies) of an index (1 by default):
+#
+# index.number_of_replicas: 1
+
+# Note, that for development on a local machine, with small indices, it usually
+# makes sense to "disable" the distributed features:
+#
+index.number_of_shards: 1
+index.number_of_replicas: 0
+
+# These settings directly affect the performance of index and search operations
+# in your cluster. Assuming you have enough machines to hold shards and
+# replicas, the rule of thumb is:
+#
+# 1. Having more *shards* enhances the _indexing_ performance and allows to
+# _distribute_ a big index across machines.
+# 2. Having more *replicas* enhances the _search_ performance and improves the
+# cluster _availability_.
+#
+# The "number_of_shards" is a one-time setting for an index.
+#
+# The "number_of_replicas" can be increased or decreased anytime,
+# by using the Index Update Settings API.
+#
+# Elasticsearch takes care about load balancing, relocating, gathering the
+# results from nodes, etc. Experiment with different settings to fine-tune
+# your setup.
+
+# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
+# the index status.
+
+
+#################################### Paths ####################################
+path.home: /src/test/resources
+# Path to directory containing configuration (this file and logging.yml):
+#
+path.conf: /src/test/resources
+
+# Path to directory where to store index data allocated for this node.
+#
+path.data: target/esdata
+#
+# Can optionally include more than one location, causing data to be striped across
+# the locations (a la RAID 0) on a file level, favouring locations with most free
+# space on creation. For example:
+#
+# path.data: /path/to/data1,/path/to/data2
+
+# Path to temporary files:
+#
+path.work: /target/eswork
+
+# Path to log files:
+#
+path.logs: /target/eslogs
+
+# Path to where plugins are installed:
+#
+# path.plugins: /path/to/plugins
+
+
+#################################### Plugin ###################################
+
+# If a plugin listed here is not installed for current node, the node will not start.
+#
+# plugin.mandatory: mapper-attachments,lang-groovy
+
+
+################################### Memory ####################################
+
+# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
+# it _never_ swaps.
+#
+# Set this property to true to lock the memory:
+#
+# bootstrap.mlockall: true
+
+# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
+# to the same value, and that the machine has enough memory to allocate
+# for Elasticsearch, leaving enough memory for the operating system itself.
+#
+# You should also make sure that the Elasticsearch process is allowed to lock
+# the memory, eg. by using `ulimit -l unlimited`.
+
+
+############################## Network And HTTP ###############################
+
+# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
+# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
+# communication. (the range means that if the port is busy, it will automatically
+# try the next port).
+
+# Set the bind address specifically (IPv4 or IPv6):
+#
+# network.bind_host: 192.168.0.1
+
+# Set the address other nodes will use to communicate with this node. If not
+# set, it is automatically derived. It must point to an actual IP address.
+#
+# network.publish_host: 192.168.0.1
+
+# Set both 'bind_host' and 'publish_host':
+#
+# network.host: 192.168.0.1
+
+# Set a custom port for the node to node communication (9300 by default):
+#
+# transport.tcp.port: 9300
+
+# Enable compression for all communication between nodes (disabled by default):
+#
+# transport.tcp.compress: true
+
+# Set a custom port to listen for HTTP traffic:
+#
+# http.port: 9200
+
+# Set a custom allowed content length:
+#
+# http.max_content_length: 100mb
+
+# Disable HTTP completely:
+#
+# http.enabled: false
+
+
+################################### Gateway ###################################
+
+# The gateway allows for persisting the cluster state between full cluster
+# restarts. Every change to the state (such as adding an index) will be stored
+# in the gateway, and when the cluster starts up for the first time,
+# it will read its state from the gateway.
+
+# There are several types of gateway implementations. For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
+
+# The default gateway type is the "local" gateway (recommended):
+#
+# gateway.type: local
+
+# Settings below control how and when to start the initial recovery process on
+# a full cluster restart (to reuse as much local data as possible when using shared
+# gateway).
+
+# Allow recovery process after N nodes in a cluster are up:
+#
+gateway.recover_after_nodes: 1
+
+# Set the timeout to initiate the recovery process, once the N nodes
+# from previous setting are up (accepts time value):
+#
+# gateway.recover_after_time: 5m
+
+# Set how many nodes are expected in this cluster. Once these N nodes
+# are up (and recover_after_nodes is met), begin recovery process immediately
+# (without waiting for recover_after_time to expire):
+#
+gateway.expected_nodes: 1
+
+
+############################# Recovery Throttling #############################
+
+# These settings allow to control the process of shards allocation between
+# nodes during initial recovery, replica allocation, rebalancing,
+# or when adding and removing nodes.
+
+# Set the number of concurrent recoveries happening on a node:
+#
+# 1. During the initial recovery
+#
+# cluster.routing.allocation.node_initial_primaries_recoveries: 4
+#
+# 2. During adding/removing nodes, rebalancing, etc
+#
+# cluster.routing.allocation.node_concurrent_recoveries: 2
+
+# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
+#
+# indices.recovery.max_bytes_per_sec: 20mb
+
+# Set to limit the number of open concurrent streams when
+# recovering a shard from a peer:
+#
+# indices.recovery.concurrent_streams: 5
+
+
+################################## Discovery ##################################
+
+# Discovery infrastructure ensures nodes can be found within a cluster
+# and master node is elected. Multicast discovery is the default.
+
+# Set to ensure a node sees N other master eligible nodes to be considered
+# operational within the cluster. Its recommended to set it to a higher value
+# than 1 when running more than 2 nodes in the cluster.
+#
+# discovery.zen.minimum_master_nodes: 1
+
+# Set the time to wait for ping responses from other nodes when discovering.
+# Set this option to a higher value on a slow or congested network
+# to minimize discovery failures:
+#
+# discovery.zen.ping.timeout: 3s
+
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
+
+# Unicast discovery allows to explicitly control which nodes will be used
+# to discover the cluster. It can be used when multicast is not present,
+# or to restrict the cluster communication-wise.
+#
+# 1. Disable multicast discovery (enabled by default):
+#
+# discovery.zen.ping.multicast.enabled: false
+#
+# 2. Configure an initial list of master nodes in the cluster
+# to perform discovery when new nodes (master or data) are started:
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
+
+# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
+#
+# You have to install the cloud-aws plugin for enabling the EC2 discovery.
+#
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
+#
+# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
+# for a step-by-step tutorial.
+
+# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
+#
+# You have to install the cloud-gce plugin for enabling the GCE discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
+
+# Azure discovery allows to use Azure API in order to perform discovery.
+#
+# You have to install the cloud-azure plugin for enabling the Azure discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
+
+################################## Slow Log ##################################
+
+# Shard level query and fetch threshold logging.
+
+#index.search.slowlog.threshold.query.warn: 10s
+#index.search.slowlog.threshold.query.info: 5s
+#index.search.slowlog.threshold.query.debug: 2s
+#index.search.slowlog.threshold.query.trace: 500ms
+
+#index.search.slowlog.threshold.fetch.warn: 1s
+#index.search.slowlog.threshold.fetch.info: 800ms
+#index.search.slowlog.threshold.fetch.debug: 500ms
+#index.search.slowlog.threshold.fetch.trace: 200ms
+
+#index.indexing.slowlog.threshold.index.warn: 10s
+#index.indexing.slowlog.threshold.index.info: 5s
+#index.indexing.slowlog.threshold.index.debug: 2s
+#index.indexing.slowlog.threshold.index.trace: 500ms
+
+################################## GC Logging ################################
+
+#monitor.jvm.gc.young.warn: 1000ms
+#monitor.jvm.gc.young.info: 700ms
+#monitor.jvm.gc.young.debug: 400ms
+
+#monitor.jvm.gc.old.warn: 10s
+#monitor.jvm.gc.old.info: 5s
+#monitor.jvm.gc.old.debug: 2s
+
diff --git a/asdctool/src/main/resources/config/error-configuration.yaml b/asdctool/src/main/resources/config/error-configuration.yaml
new file mode 100644
index 0000000000..5e5009e54f
--- /dev/null
+++ b/asdctool/src/main/resources/config/error-configuration.yaml
@@ -0,0 +1,1671 @@
+# Errors
+errors:
+ OK: {
+ code: 200,
+ message: "OK"
+ }
+ CREATED: {
+ code: 201,
+ message: "OK"
+ }
+ NO_CONTENT: {
+ code: 204,
+ message: "No Content"
+ }
+#--------POL4050-----------------------------
+ NOT_ALLOWED: {
+ code: 405,
+ message: "Error: Method not allowed.",
+ messageId: "POL4050"
+ }
+#--------POL5000-----------------------------
+ GENERAL_ERROR: {
+ code: 500,
+ message: "Error: Internal Server Error. Please try again later.",
+ messageId: "POL5000"
+ }
+#---------POL5001------------------------------
+ MISSING_X_ECOMP_INSTANCE_ID: {
+ code: 400 ,
+ message: "Error: Missing 'X-ECOMP-InstanceID' HTTP header.",
+ messageId: "POL5001"
+ }
+#---------POL5002------------------------------
+ AUTH_REQUIRED: {
+ code: 401 ,
+ message: "Error: Authentication is required to use the API.",
+ messageId: "POL5002"
+ }
+#---------POL5003------------------------------
+ AUTH_FAILED: {
+ code: 403 ,
+ message: "Error: Not authorized to use the API.",
+ messageId: "POL5003"
+ }
+#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }
+#---------SVC4002-----------------------------
+ MISSING_INFORMATION: {
+ code: 403,
+ message: "Error: Missing information.",
+ messageId: "SVC4002"
+ }
+#---------SVC4003------------------------------
+# %1 - Users's USER_ID
+ USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: User '%1' was not found.",
+ messageId: "SVC4003"
+ }
+#---------SVC4004-----------------------------
+# %1 - Users's email address
+ INVALID_EMAIL_ADDRESS: {
+ code: 400,
+ message: "Error: Invalid email address '%1'.",
+ messageId: "SVC4004"
+ }
+#---------SVC4005------------------------------
+# %1 - role
+ INVALID_ROLE: {
+ code: 400,
+ message: "Error: Invalid role '%1'.",
+ messageId: "SVC4005"
+ }
+#---------SVC4006------------------------------
+# %1 - Users's USER_ID
+ USER_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: User with '%1' ID already exists.",
+ messageId: "SVC4006"
+ }
+#---------SVC4007------------------------------
+ DELETE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator can only be deleted by another administrator.",
+ messageId: "SVC4007"
+ }
+#---------SVC4008-----------------------------
+# %1 - Users's userId
+ INVALID_USER_ID: {
+ code: 400,
+ message: "Error: Invalid userId '%1'.",
+ messageId: "SVC4008"
+ }
+#---------SVC4049------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_CONTACT: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 contact id.",
+ messageId: "SVC4049"
+ }
+#---------SVC4050-----------------------------
+# %1 - Service/Resource/Additional parameter
+# %2 - service/resource/label name
+ COMPONENT_NAME_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: %1 with name '%2' already exists.",
+ messageId: "SVC4050"
+ }
+#---------SVC4051------------------------------
+# %1 - resource/service
+ COMPONENT_MISSING_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 category.",
+ messageId: "SVC4051"
+ }
+
+#---------SVC4052------------------------------
+ COMPONENT_MISSING_TAGS: {
+ code: 400,
+ message: "Error: Invalid Content. At least one tag has to be specified.",
+ messageId: "SVC4052"
+ }
+
+#---------SVC4053------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 description.",
+ messageId: "SVC4053"
+ }
+#---------SVC4054------------------------------
+# %1 - resource/service
+ COMPONENT_INVALID_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 category.",
+ messageId: "SVC4054"
+ }
+#---------SVC4055------------------------------
+ MISSING_VENDOR_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor name.",
+ messageId: "SVC4055"
+ }
+#---------SVC4056------------------------------
+ MISSING_VENDOR_RELEASE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor release.",
+ messageId: "SVC4056"
+ }
+
+#---------SVC4057------------------------------
+ MISSING_DERIVED_FROM_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing derived from template specification.",
+ messageId: "SVC4057"
+ }
+
+#---------SVC4058------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 icon.",
+ messageId: "SVC4058"
+ }
+#---------SVC4059------------------------------
+# %1 - service/resource
+ COMPONENT_INVALID_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 icon.",
+ messageId: "SVC4059"
+ }
+#---------SVC4060------------------------------
+ PARENT_RESOURCE_NOT_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Derived from resource template was not found.",
+ messageId: "SVC4060"
+ }
+#---------SVC4061------------------------------
+ MULTIPLE_PARENT_RESOURCE_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Multiple derived from resource template is not allowed.",
+ messageId: "SVC4061"
+ }
+
+#---------SVC4062------------------------------
+# %1 - service/resource
+ MISSING_COMPONENT_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 name.",
+ messageId: "SVC4062"
+ }
+#---------SVC4063------------------------------
+ #%1  -  resource/service name
+ RESOURCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource was not found.",
+ messageId: "SVC4063"
+ }
+
+#---------SVC4064------------------------------
+# %1 - Service/Resource
+ COMPONENT_INVALID_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description contains non-english characters.",
+ messageId: "SVC4064"
+ }
+#---------SVC4065------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name length
+ COMPONENT_DESCRIPTION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description exceeds limit of %2 characters.",
+ messageId: "SVC4065"
+ }
+#---------SVC4066------------------------------
+# %1 - max length
+ COMPONENT_TAGS_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Tags overall length exceeds limit of %1 characters.",
+ messageId: "SVC4066"
+ }
+#---------SVC4067------------------------------
+# %1 - max length
+ VENDOR_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor name exceeds limit of %1 characters.",
+ messageId: "SVC4067"
+ }
+#---------SVC4068------------------------------
+# %1 - max length
+ VENDOR_RELEASE_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor release exceeds limit of %1 characters.",
+ messageId: "SVC4068"
+ }
+
+#---------SVC4069------------------------------
+# %1 - Service/Resource/Product
+ COMPONENT_INVALID_CONTACT_ID: {
+ code: 400,
+ message: "Error: Invalid Content. %1 contact id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9",
+ messageId: "SVC4069"
+ }
+#---------SVC4070------------------------------
+# %1 - Service/Resource
+ INVALID_COMPONENT_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. %1 name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4070"
+ }
+
+#---------SVC4071------------------------------
+ INVALID_VENDOR_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4071"
+ }
+#---------SVC4072------------------------------
+ INVALID_VENDOR_RELEASE: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor release is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4072"
+ }
+#---------SVC4073------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name
+ COMPONENT_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 name exceeds limit of %2 characters.",
+ messageId: "SVC4073"
+ }
+#---------SVC4080------------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CHECKOUT_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for modification by %3 %4(%5).",
+ messageId: "SVC4080"
+ }
+#---------SVC4081-----------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CERT_IN_PROGRESS_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for certification by %3 %4(%5).",
+ messageId: "SVC4081"
+ }
+
+#-----------SVC4082---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_SENT_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is sent for certification by %3 %4(%5).",
+ messageId: "SVC4082"
+ }
+#-----------SVC4083---------------------------
+ COMPONENT_VERSION_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Version of this %1 was already promoted.",
+ messageId: "SVC4083"
+ }
+#-----------SVC4084---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CHECKED_IN: {
+ code: 409,
+ message: "Error: The current version of '%1' %2 was already checked-in by %3 %4(%5).",
+ messageId: "SVC4084"
+ }
+#-----------SVC4085---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_CHECKOUT_BY_ANOTHER_USER: {
+ code: 403,
+ message: "Error: %1 %2 has already been checked out by %3 %4(%5).",
+ messageId: "SVC4085"
+ }
+#-----------SVC4086---------------------------
+# %1  - resource/service name
+# %2  - resource/service
+ COMPONENT_IN_USE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is in use by another user.",
+ messageId: "SVC4086"
+ }
+#-----------SVC4087---------------------------
+# %1 - component name
+# %2 - resource/service/product
+ COMPONENT_HAS_NEWER_VERSION: {
+ code: 409,
+ message: "Error: Checking out of the requested version of the '%1' %2 is not allowed as a newer version exists.",
+ messageId: "SVC4087"
+ }
+#-----------SVC4088---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CERTIFIED: {
+ code: 403,
+ message: "Error: Requested %1 %2 has already been certified by %3 %4(%5).",
+ messageId: "SVC4088"
+ }
+#-----------SVC4089---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+ COMPONENT_NOT_READY_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification.",
+ messageId: "SVC4089"
+ }
+#-----------SVC4100---------------------------
+#%1 - property name
+ PROPERTY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' property was not found.",
+ messageId: "SVC4100"
+ }
+#-----------SVC4101---------------------------
+#%1 - property name
+ PROPERTY_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Property with '%1' name already exists.",
+ messageId: "SVC4101"
+ }
+
+#-----------SVC4102---------------------------
+# %1 - capability type name
+ CAPABILITY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Capability Type with name '%1' already exists.",
+ messageId: "SVC4102"
+ }
+#-----------SVC4114---------------------------
+ AUTH_FAILED_INVALIDE_HEADER: {
+ code: 400,
+ message: "Error: Invalid Authorization header.",
+ messageId: "SVC4114"
+ }
+#-----------SVC4115---------------------------
+# %1 - capability type name
+ MISSING_CAPABILITY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Capability Type '%1'.",
+ messageId: "SVC4115"
+ }
+ RESOURCE_INSTANCE_BAD_REQUEST: {
+ code: 400,
+ message: "Error: Invalid Content.",
+ messageId: "SVC4116"
+ }
+#-----------SVC4117---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_MATCH_NOT_FOUND: {
+ code: 404,
+ message: "Error: Match not found between resource instance '%1' and resource instance '%2' for requirement '%3'.",
+ messageId: "SVC4117"
+ }
+#-----------SVC4118---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Resource instances '%1' and '%2' are already associated with requirement '%3'.",
+ messageId: "SVC4118"
+ }
+#-----------SVC4119---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_RELATION_NOT_FOUND: {
+ code: 404,
+ message: "Error: No relation found between resource instances '%1' and '%2' for requirement '%3'.",
+ messageId: "SVC4119"
+ }
+#-----------SVC4120---------------------------
+# %1 - User's USER_ID
+ USER_INACTIVE: {
+ code: 404,
+ message: "Error: User %1 was not found.",
+ messageId: "SVC4120"
+ }
+#-----------SVC4121---------------------------
+# %1 - User's USER_ID
+ USER_HAS_ACTIVE_ELEMENTS: {
+ code: 403,
+ message: "Error: User with %1 ID can not be deleted since it has active elements(resources/services/artifacts).",
+ messageId: "SVC4121"
+ }
+#-----------SVC4122---------------------------
+# %1 - artifact type
+ ARTIFACT_TYPE_NOT_SUPPORTED: {
+ code: 400,
+ message: "Error: Invalid artifact type '%1'.",
+ messageId: "SVC4122"
+ }
+#-----------SVC4123---------------------------
+ ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Artifact logical name cannot be changed.",
+ messageId: "SVC4123"
+ }
+#-----------SVC4124---------------------------
+ MISSING_ARTIFACT_TYPE: {
+ code: 400,
+ message: "Error: Missing artifact type.",
+ messageId: "SVC4124"
+ }
+#-----------SVC4125---------------------------
+# %1-artifact name
+ ARTIFACT_EXIST: {
+ code: 400,
+ message: "Error: Artifact '%1' already exists.",
+ messageId: "SVC4125"
+ }
+#---------SVC4126------------------------------
+# %1 - resource/service/product/...
+# %2 - field (tag, vendor name...)
+ INVALID_FIELD_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 format.",
+ messageId: "SVC4126"
+ }
+#-----------SVC4127---------------------------
+ ARTIFACT_INVALID_MD5: {
+ code: 400,
+ message: "Error: Invalid artifact checksum.",
+ messageId: "SVC4127"
+ }
+#-----------SVC4128---------------------------
+ MISSING_ARTIFACT_NAME: {
+ code: 400,
+ message: "Error: Invalid content. Missing artifact name.",
+ messageId: "SVC4128"
+ }
+#-----------SVC4129---------------------------
+ MISSING_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing PROJECT_CODE number.",
+ messageId: "SVC4129"
+ }
+#-----------SVC4130---------------------------
+ INVALID_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. PROJECT_CODE number must be numeric from 5 up to 10 digits.",
+ messageId: "SVC4130"
+ }
+#-----------SVC4131---------------------------
+# %1-resource/service
+# %2-srtifact/artifacts
+# %3-semicolomn separated list of artifact
+ COMPONENT_MISSING_MANDATORY_ARTIFACTS: {
+ code: 403,
+ message: "Error: Missing mandatory informational %1 %2: [%3].",
+ messageId: "SVC4131"
+ }
+#-----------SVC4132---------------------------
+# %1 - lifecycle type name
+ LIFECYCLE_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Lifecycle Type with name '%1' already exists.",
+ messageId: "SVC4132"
+ }
+#-----------SVC4133---------------------------
+# %1 - service version
+# %2 - service name
+ SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION: {
+ code: 403,
+ message: "Error: Version %1 of '%2' service is not available for distribution.",
+ messageId: "SVC4133"
+ }
+#-----------SVC4134---------------------------
+ MISSING_LIFECYCLE_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing interface life-cycle type.",
+ messageId: "SVC4134"
+ }
+#---------SVC4135------------------------------
+ SERVICE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service category cannot be changed once the service is certified.",
+ messageId: "SVC4135"
+ }
+#---------SVC4136------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE: {
+ code: 500,
+ message: "Error: Requested distribution environment '%1' is not available.",
+ messageId: "SVC4136"
+ }
+#---------SVC4137------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_FOUND: {
+ code: 400,
+ message: "Error: Requested distribution environment '%1' was not found.",
+ messageId: "SVC4137"
+ }
+#---------SVC4138------------------------------
+ DISTRIBUTION_ENVIRONMENT_INVALID: {
+ code: 400,
+ message: "Error: Invalid distribution environment.",
+ messageId: "SVC4138"
+ }
+#---------SVC4139------------------------------
+# %1 - service name
+ DISTRIBUTION_ARTIFACT_NOT_FOUND: {
+ code: 409,
+ message: "Error: Service '%1' cannot be distributed due to missing deployment artifacts.",
+ messageId: "SVC4139"
+ }
+#---------SVC4200------------------------------
+# %1 - Service/Resource
+# %2 - max icon name length
+ COMPONENT_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 icon name exceeds limit of %2 characters.",
+ messageId: "SVC4200"
+ }
+#---------SVC4300------------------------------
+ RESTRICTED_ACCESS: {
+ code: 403,
+ message: "Error: Restricted access.",
+ messageId: "SVC4300"
+ }
+#---------SVC4301------------------------------
+ RESTRICTED_OPERATION: {
+ code: 409,
+ message: "Error: Restricted operation.",
+ messageId: "SVC4301"
+ }
+#---------SVC4500------------------------------
+ MISSING_BODY: {
+ code: 400 ,
+ message: "Error: Missing request body.",
+ messageId: "SVC4500"
+ }
+#---------SVC4501------------------------------
+ MISSING_PUBLIC_KEY: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'apiPublicKey'." ,
+ messageId: "SVC4501"
+ }
+#---------SVC4502------------------------------
+ DISTRIBUTION_ENV_DOES_NOT_EXIST: {
+ code: 400 ,
+ message: "Error: Invalid Body : Missing mandatory parameter 'distrEnvName'." ,
+ messageId: "SVC4502"
+ }
+#-----------SVC4503---------------------------
+# %1 - service name
+ SERVICE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' service was not found.",
+ messageId: "SVC4503"
+ }
+
+#---------SVC4504------------------------------
+# %1 - Service/Resource
+# %2 - service/resource version
+ COMPONENT_VERSION_NOT_FOUND: {
+ code: 404,
+ message: "Error: %1 version %2 was not found.",
+ messageId: "SVC4504"
+ }
+#-----------SVC4505---------------------------
+ #%1-artifact name
+
+ ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Artifact '%1' was not found.",
+ messageId: "SVC4505"
+ }
+#---------SVC4506------------------------------
+ MISSING_ENV_NAME: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'distrEnvName'.",
+ messageId: "SVC4506"
+ }
+#---------SVC4507------------------------------
+ COMPONENT_INVALID_TAGS_NO_COMP_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. One of the tags should be the component name.",
+ messageId: "SVC4507"
+ }
+
+#---------SVC4508------------------------------
+ SERVICE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service name cannot be changed once the service is certified.",
+ messageId: "SVC4508"
+ }
+
+#---------SVC4509------------------------------
+ SERVICE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the service is certified.",
+ messageId: "SVC4509"
+ }
+#---------SVC4510------------------------------
+# %1 - icon name max length
+ SERVICE_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Icon name exceeds limit of %1 characters.",
+ messageId: "SVC4510"
+ }
+#---------SVC4511------------------------------
+ DISTRIBUTION_REQUESTED_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested distribution was not found.",
+ messageId: "SVC4511"
+ }
+#---------SVC4512------------------------------
+# %1 - Distribution ID
+ DISTRIBUTION_REQUESTED_FAILED: {
+ code: 403,
+ message: "Error: Requested distribution '%1' failed.",
+ messageId: "SVC4512"
+ }
+#---------SVC4513------------------------------
+ RESOURCE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource category cannot be changed once the resource is certified.",
+ messageId: "SVC4513"
+ }
+#---------SVC4514------------------------------
+ RESOURCE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource name cannot be changed once the resource is certified.",
+ messageId: "SVC4514"
+ }
+#---------SVC4515------------------------------
+ RESOURCE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the resource is certified.",
+ messageId: "SVC4515"
+ }
+#---------SVC4516------------------------------
+ RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Vendor name cannot be changed once the resource is certified.",
+ messageId: "SVC4516"
+ }
+#---------SVC4517------------------------------
+ RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Derived from resource template cannot be changed once the resource is certified.",
+ messageId: "SVC4517"
+ }
+#---------SVC4518------------------------------
+# %1 - max length
+ COMPONENT_SINGLE_TAG_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Single tag exceeds limit of %1 characters.",
+ messageId: "SVC4518"
+ }
+#---------SVC4519------------------------------
+ INVALID_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: mismatch in data-type occurred for property %1. data type is %2 and default value found is %3.",
+ messageId: "SVC4519"
+ }
+#---------SVC4520------------------------------
+# %1 - service or resource
+ ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED: {
+ code: 409,
+ message: "Error: Maximal number of additional %1 parameters was reached.",
+ messageId: "SVC4520"
+ }
+#---------SVC4521------------------------------
+ ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED: {
+ code: 400,
+ message: "Error: Invalid Content. The Additional information label and value cannot be empty.",
+ messageId: "SVC4521"
+ }
+#---------SVC4522------------------------------
+# %1 - label/value
+# %2 - Maximal length of %1
+ ADDITIONAL_INFORMATION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Additional information %1 exceeds limit of %2 characters.",
+ messageId: "SVC4522"
+ }
+#---------SVC4523------------------------------
+ ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information label is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4523"
+ }
+#---------SVC4524------------------------------
+ ADDITIONAL_INFORMATION_NOT_FOUND: {
+ code: 409,
+ message: "Error: Requested additional information was not found.",
+ messageId: "SVC4524"
+ }
+#---------SVC4525------------------------------
+ ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information contains non-english characters.',
+ messageId: "SVC4525"
+ }
+#---------SVC4526------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found.",
+ messageId: "SVC4526"
+ }
+#---------SVC4527------------------------------
+ ASDC_VERSION_NOT_FOUND: {
+ code: 500,
+ message: 'Error: ASDC version cannot be displayed.',
+ messageId: "SVC4527"
+ }
+#---------SVC4528------------------------------
+# %1-artifact url/artifact label/artifact description/VNF Service Indicator
+ MISSING_DATA: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1.",
+ messageId: "SVC4528"
+ }
+#---------SVC4529------------------------------
+# %1-artifact url/artifact label/artifact description/artifact name
+# %2 - Maximal length of %1
+ EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 exceeds limit of %2 characters.",
+ messageId: "SVC4529"
+ }
+#---------SVC4530------------------------------
+ ARTIFACT_INVALID_TIMEOUT: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact Timeout should be set to valid positive non-zero number of minutes.",
+ messageId: "SVC4530"
+ }
+#---------SVC4531------------------------------
+ SERVICE_IS_VNF_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: VNF Indicator cannot be updated for certified service.",
+ messageId: "SVC4531"
+ }
+ #---------SVC4532------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found on the service '%2.",
+ messageId: "SVC4532"
+ }
+ #---------SVC4533------------------------------
+ # %1 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"/"YANG_XML"
+ WRONG_ARTIFACT_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for %1 artifact type.",
+ messageId: "SVC4533"
+ }
+
+#---------SVC4534------------------------------
+# %1 - "HEAT"/"HEAT_ENV"
+ INVALID_YAML: {
+ code: 400,
+ message: "Error: Uploaded YAML file for %1 artifact is invalid.",
+ messageId: "SVC4534"
+ }
+
+#---------SVC4535------------------------------
+# %1 - "HEAT"
+ INVALID_DEPLOYMENT_ARTIFACT_HEAT: {
+ code: 400,
+ message: "Error: Invalid %1 artifact.",
+ messageId: "SVC4535"
+ }
+#---------SVC4536------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"
+# %4 - "HEAT"/"HEAT_ENV"/"MURANO_PKG
+ DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact of %3 type .Please delete or update an existing %4 artifact.",
+ messageId: "SVC4536"
+ }
+
+#---------SVC4537------------------------------
+ MISSING_HEAT: {
+ code: 400,
+ message: "Error: Missing HEAT artifact. HEAT_ENV artifact cannot be uploaded without corresponding HEAT template.",
+ messageId: "SVC4537"
+ }
+#---------SVC4538------------------------------
+ MISMATCH_HEAT_VS_HEAT_ENV: {
+ code: 400,
+ message: "Error: Invalid artifact content. Parameter's set in HEAT_ENV '%1' artifact doesn't match the parameters in HEAT '%2' artifact.",
+ messageId: "SVC4538"
+ }
+#---------SVC4539------------------------------
+ INVALID_RESOURCE_PAYLOAD: {
+ code: 400,
+ message: "Error: Invalid resource payload.",
+ messageId: "SVC4539"
+ }
+#---------SVC4540------------------------------
+ INVALID_TOSCA_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for TOSCA template.",
+ messageId: "SVC4540"
+ }
+#---------SVC4541------------------------------
+ INVALID_YAML_FILE: {
+ code: 400,
+ message: "Error: Invalid YAML file.",
+ messageId: "SVC4541"
+ }
+#---------SVC4542------------------------------
+ INVALID_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid TOSCA template.",
+ messageId: "SVC4542"
+ }
+#---------SVC4543------------------------------
+ NOT_RESOURCE_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Imported Service TOSCA template.",
+ messageId: "SVC4543"
+ }
+#---------SVC4544------------------------------
+ NOT_SINGLE_RESOURCE: {
+ code: 400,
+ message: "Error: Imported TOSCA template should contain one resource definition.",
+ messageId: "SVC4544"
+ }
+#---------SVC4545------------------------------
+ INVALID_RESOURCE_NAMESPACE: {
+ code: 400,
+ message: "Error: Invalid resource namespace.",
+ messageId: "SVC4545"
+ }
+#---------SVC4546------------------------------
+ RESOURCE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: Imported resource already exists in ASDC Catalog.",
+ messageId: "SVC4546"
+ }
+#---------SVC4549------------------------------
+ INVALID_RESOURCE_CHECKSUM: {
+ code: 400,
+ message: "Error: Invalid resource checksum.",
+ messageId: "SVC4549"
+ }
+#---------SVC4550------------------------------
+ #%1  -  Consumer salt
+ INVALID_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 length.",
+ messageId: "SVC4550"
+ }
+ #---------SVC4551------------------------------
+ #%1  -  ECOMP User name
+ ECOMP_USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: ECOMP User '%1' was not found.",
+ messageId: "SVC4551"
+ }
+#---------SVC4552------------------------------
+ CONSUMER_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: ECOMP User already exists.",
+ messageId: "SVC4552"
+ }
+#---------SVC4553-----------------------------
+ #%1  -  Consumer name / Consumer password/ Consumer salt
+ INVALID_CONTENT_PARAM: {
+ code: 400,
+ message: "Error: %1 is invalid.",
+ messageId: "SVC4553"
+ }
+ #---------SVC4554------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested artifact doesn't belong to specified %1.",
+ messageId: "SVC4554"
+ }
+#---------SVC4554------------------------------
+# %1 - "Service name"
+ SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND: {
+ code: 403,
+ message: "Error: Requested '%1' service is not ready for certification. Service has to have at least one deployment artifact.",
+ messageId: "SVC4554"
+ }
+#---------SVC4555------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name length.",
+ messageId: "SVC4555"
+ }
+#---------SVC4556------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name format.",
+ messageId: "SVC4556"
+ }
+#---------SVC4557------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category name"
+ COMPONENT_CATEGORY_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: %1 category name '%2' already exists.",
+ messageId: "SVC4557"
+ }
+#---------SVC4558------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ VALIDATED_RESOURCE_NOT_FOUND: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource.",
+ messageId: "SVC4558"
+ }
+#---------SVC4559------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_ALREADY_VALIDATED_RESOURCE: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use already available validated resource version.",
+ messageId: "SVC4559"
+ }
+#---------SVC4560------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_LIST_VALIDATED_RESOURCES: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use one of available validated resource versions.",
+ messageId: "SVC4560"
+ }
+#---------SVC4561------------------------------
+# %1 - "resource"/"product"
+# %2 - "category"
+# %3 - "category name"
+ COMPONENT_CATEGORY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested %1 %2 '%3' was not found.",
+ messageId: "SVC4561"
+ }
+#---------SVC4562------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %3 - "category name"
+ COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY: {
+ code: 409,
+ message: "Error: %1 sub-category '%2' already exists under '%3' category.",
+ messageId: "SVC4562"
+ }
+#---------SVC4563------------------------------
+# %1 - "Product"
+# %2 - "grouping name"
+# %3 - "sub-category name"
+ COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY: {
+ code: 409,
+ message: "Error: %1 grouping '%2' already exists under '%3' sub-category.",
+ messageId: "SVC4563"
+ }
+#---------SVC4564------------------------------
+# %1 - product name
+ PRODUCT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' product was not found.",
+ messageId: "SVC4564"
+ }
+#---------SVC4565------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+# %3 - parameter name
+ INVALID_HEAT_PARAMETER_VALUE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Invalid %2 value set for '%3' parameter.",
+ messageId: "SVC4565"
+ }
+#---------SVC4566------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+ INVALID_HEAT_PARAMETER_TYPE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Unsupported '%2' parameter type.",
+ messageId: "SVC4566"
+ }
+#---------SVC4567------------------------------
+# %1 - "YANG_XML"
+ INVALID_XML: {
+ code: 400,
+ message: "Error: Uploaded XML file for %1 artifact is invalid.",
+ messageId: "SVC4567"
+ }
+#---------SVC4567------------------------------
+# %1 - "User Name and UserId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: User cannot be deleted. User '%1' has %2 projects.",
+ messageId: "SVC4567"
+ }
+#---------SVC4568------------------------------
+# %1 - "User Name and UserId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: Role cannot be changed. User '%1' has %2 projects.",
+ messageId: "SVC4568"
+ }
+#---------SVC4570------------------------------
+ UPDATE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator is not allowed to change his/her role.",
+ messageId: "SVC4570"
+ }
+#---------SVC4571------------------------------
+ SERVICE_CANNOT_CONTAIN_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Sub category cannot be defined for service",
+ messageId: "SVC4571"
+ }
+#---------SVC4572------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_TOO_MUCH_CATEGORIES: {
+ code: 400,
+ message: "Error: %1 must have only 1 category",
+ messageId: "SVC4572"
+ }
+#---------SVC4574------------------------------
+ RESOURCE_TOO_MUCH_SUBCATEGORIES: {
+ code: 400,
+ message: "Error: Resource must have only 1 sub category",
+ messageId: "SVC4574"
+ }
+#---------SVC4575------------------------------
+ COMPONENT_MISSING_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Missing sub category",
+ messageId: "SVC4575"
+ }
+ #---------SVC4576------------------------------
+# %1 - "component type"
+ UNSUPPORTED_ERROR: {
+ code: 400,
+ message: "Error : Requested component type %1 is unsupported.",
+ messageId: "SVC4576"
+ }
+ #---------SVC4577------------------------------
+# %1 - "resource type"
+ RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES: {
+ code: 409,
+ message: "Error : Resource of type %1 cannot contain resource instances.",
+ messageId: "SVC4577"
+ }
+#---------SVC4578------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "artifact name"
+ DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact named '%3'.",
+ messageId: "SVC4578"
+ }
+#---------SVC4579------------------------------
+# %1 - "Category"/"Sub-Category"/"Group"
+# %2 - category/sub-category/grouping name.
+ INVALID_GROUP_ASSOCIATION: {
+ code: 400,
+ message: "Error: Invalid group association. %1 '%2' was not found.",
+ messageId: "SVC4579"
+ }
+#---------SVC4580------------------------------
+ EMPTY_PRODUCT_CONTACTS_LIST: {
+ code: 400,
+ message: "Error: Invalid content. At least one Product Contact has to be specified.",
+ messageId: "SVC4580"
+ }
+#---------SVC4581------------------------------
+# %1 - userId
+ INVALID_PRODUCT_CONTACT: {
+ code: 400,
+ message: "Error: Invalid content. User '%1' cannot be set as Product Contact.",
+ messageId: "SVC4581"
+ }
+#---------SVC4582------------------------------
+# %1 - Product
+# %2 - "abbreviated"/"full"
+ MISSING_ONE_OF_COMPONENT_NAMES: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1 %2 name.",
+ messageId: "SVC4582"
+ }
+#---------SVC4583------------------------------
+# %1 - "Icon"
+# %2 - "resource"/"service"/"product"
+ COMPONENT_PARAMETER_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: %1 cannot be changed once the %2 is certified.",
+ messageId: "SVC4583"
+ }
+#---------SVC4584------------------------------
+# %1 - service/VF name
+# %2 - "service" /"VF"
+# %3 - resource instance origin type
+# %4 - resource instance name
+# %5 - requirement/capability
+# %6 - requirement/capability name
+# %7 - "fulfilled" (for req)/"consumed (for cap)"
+ REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification. %3 '%4' has to have %5 '%6' %7.",
+ messageId: "SVC4584"
+ }
+#---------SVC4585------------------------------
+ INVALID_OCCURRENCES: {
+ code: 400,
+ message: "Error: Invalid occurrences format.",
+ messageId: "SVC4585"
+ }
+#---------SVC4586------------------------------
+#---------SVC4586------------------------------
+ INVALID_SERVICE_API_URL: {
+ code: 400,
+ message: 'Error: Invalid Service API URL. Please check whether your URL has a valid domain extension and does not contain the following characters - #?&@%+;,=$<>~^`\[]{}|"*!',
+ messageId: "SVC4586"
+ }
+#---------SVC4587------------------------------
+# %1 - Data type name
+ DATA_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Data type %1 already exists.',
+ messageId: "SVC4587"
+ }
+#---------SVC4588------------------------------
+# %1 - Data type name
+ DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM: {
+ code: 400,
+ message: 'Error: Invalid Data type %1. Data type must have either a valid derived from declaration or at least one valid property',
+ messageId: "SVC4588"
+ }
+#---------SVC4589------------------------------
+# %1 - Data type name
+ DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Data type %1. 'properties' parameter cannot be empty if provided.",
+ messageId: "SVC4589"
+ }
+#---------SVC4590------------------------------
+# %1 - Property type name
+# %2 - Property name
+ INVALID_PROPERTY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Property type %1 in property %2.",
+ messageId: "SVC4590"
+ }
+#---------SVC4591------------------------------
+# %1 - Property inner type
+# %2 - Property name
+ INVALID_PROPERTY_INNER_TYPE: {
+ code: 400,
+ message: "Error: Invalid property inner type %1, in property %2",
+ messageId: "SVC4591"
+ }
+#---------SVC4592------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+ COMPONENT_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found.",
+ messageId: "SVC4592"
+ }
+#---------SVC4593------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+# %3 - "resource/"service"/"product"
+# %4 - container name
+ COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found on the %3 '%4'.",
+ messageId: "SVC4593"
+ }
+#---------SVC4594------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+ IMPORT_DUPLICATE_REQ_CAP_NAME: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains more than one %1 named '%2'.",
+ messageId: "SVC4594"
+ }
+#---------SVC4595------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+#%3 - parent containing the requirement
+ IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains %1 '%2' that is already defined by derived template %3.",
+ messageId: "SVC4595"
+ }
+#---------SVC4596------------------------------
+# %1 - Data type name
+ DATA_TYPE_DERIVED_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The ancestor data type %1 cannot be found in the system.",
+ messageId: "SVC4596"
+ }
+#---------SVC4597------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties named %2 which are already defined in one of its ancestors.",
+ messageId: "SVC4597"
+ }
+#---------SVC4598------------------------------
+# %1 - Data type name
+ DATA_TYPE_DUPLICATE_PROPERTY: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains duplicate property.",
+ messageId: "SVC4598"
+ }
+#---------SVC4599------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties %2 which their type is this data type.",
+ messageId: "SVC4599"
+ }
+#---------SVC4600------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_HAVE_PROPERTIES: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 cannot have properties since it is of type scalar",
+ messageId: "SVC4600"
+ }
+#---------SVC4601------------------------------
+ NOT_TOPOLOGY_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file %1 cannot be modeled to VF as it does not contain 'topology_template.",
+ messageId: "SVC4601"
+ }
+#---------SVC4602--------------------------------
+# %1 - yaml file name
+# %2 - node_template label
+# %3 - node_template type
+ INVALID_NODE_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file '%1' contains node_template '%2' of type '%3' that does not represent existing VFC/CP/VL",
+ messageId: "SVC4602"
+ }
+#---------SVC4603------------------------------
+# %1 - component type
+# %2 - component name
+# %3 - state
+ ILLEGAL_COMPONENT_STATE: {
+ code: 403,
+ message: "Error: Component instance of %1 can not be created because the component '%2' is in an illegal state %3.",
+ messageId: "SVC4603"
+ }
+#---------SVC4604------------------------------
+# %1 - csar file name
+ CSAR_INVALID: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. 'TOSCA-Metadata/Tosca.meta' file must be provided.",
+ messageId: "SVC4604"
+ }
+#---------SVC4605------------------------------
+# %1 - csar file name
+ CSAR_INVALID_FORMAT: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. Invalid 'TOSCA-Metadata/Tosca.meta' file format.",
+ messageId: "SVC4605"
+ }
+#---------SVC4606------------------------------
+# %1 - property name
+# %2 - property type
+# %3 - property innerType
+# %4 - default value is
+ INVALID_COMPLEX_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: Invalid default value of property %1. Data type is %2 with inner type %3 and default value found is %4.",
+ messageId: "SVC4606"
+ }
+#---------SVC4607------------------------------
+# %1 - csar file name
+ CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is not found.",
+ messageId: "SVC4607"
+ }
+#---------SVC4608------------------------------
+# %1 - artifact name
+# %2 - component type
+# %3 - actual component type
+ MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE: {
+ code: 400,
+ message: "Error: Artifact %1 is only compatible with component of type %2, but component type is %3.",
+ messageId: "SVC4608"
+ }
+
+#---------SVC4609------------------------------
+# %1 - "INVALID_JSON"
+ INVALID_JSON: {
+ code: 400,
+ message: "Error: Uploaded JSON file for %1 artifact is invalid.",
+ messageId: "SVC4609"
+ }
+#---------SVC4610------------------------------
+# %1 - csar file name
+# %2 - missing file name
+ YAML_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error - TOSCA CSAR %1 is invalid. TOSCA-Metadata/Tosca.meta refers to file %2 that is not provided.",
+ messageId: "SVC4610"
+ }
+#---------SVC4611------------------------------
+# %1 - group name
+ GROUP_MEMBER_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Group %1 member list was provided but does not have values",
+ messageId: "SVC4611"
+ }
+#---------SVC4612------------------------------
+# %1 - group name
+ GROUP_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Group type %1 already exists.',
+ messageId: "SVC4612"
+ }
+#---------SVC4613------------------------------
+# %1 - group name
+# %2 - VF name(component name)
+# %3 - actual component type [VF]
+ GROUP_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Group with name '%1' already exists in %2 %3.",
+ messageId: "SVC4613"
+ }
+#---------SVC4614------------------------------
+# %1 - group type
+ GROUP_TYPE_IS_INVALID: {
+ code: 400,
+ message: "Error: Invalid content. Group type %1 does not exist",
+ messageId: "SVC4614"
+ }
+#---------SVC4615------------------------------
+# %1 - group name
+ GROUP_MISSING_GROUP_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Group Type for group '%1'",
+ messageId: "SVC4615"
+ }
+#---------SVC4616------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - VF name
+# %4 - component type [VF ]
+ GROUP_INVALID_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of %3 %4.",
+ messageId: "SVC4616"
+ }
+#---------SVC4617------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - group type
+ GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of allowed members of group type %3.",
+ messageId: "SVC4617"
+ }
+#---------SVC4618------------------------------
+# %1 - missing file name
+# %2 - csar file name
+ ARTIFACT_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 is defined in CSAR %2 manifest but is not provided",
+ messageId: "SVC4618"
+ }
+#---------SVC4619------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - existing artifact type
+ ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 already exists in type %3.",
+ messageId: "SVC4619"
+ }
+#---------SVC4620------------------------------
+ FAILED_RETRIVE_ARTIFACTS_TYPES: {
+ code: 400,
+ message: "Error: Failed to retrieve list of suported artifact types.",
+ messageId: "SVC4620"
+ }
+#---------SVC4621------------------------------
+# %1 - artifact name
+# %2 - master
+ ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 already exists in master %2 .",
+ messageId: "SVC4621"
+ }
+#---------SVC4622------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - master name
+# %4 - master type
+ ARTIFACT_NOT_VALID_IN_MASTER: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 can not be exists under master %3 in type %4.",
+ messageId: "SVC4622"
+ }
+#---------SVC4623------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - env name
+# %4 - existing env
+ ARTIFACT_NOT_VALID_ENV: {
+ code: 400,
+ message: "Error: Artifact %1 in type %2 with env %3 already exists with another env %4",
+ messageId: "SVC4623"
+ }
+#---------SVC4624------------------------------
+# %1 - groups names
+# %2 - VF name
+# %3 - component type [VF ]
+ GROUP_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The groups '%1' cannot be found under %2 %3.",
+ messageId: "SVC4624"
+ }
+#---------SVC4625------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_ASSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already associated to group '%1'.",
+ messageId: "SVC4625"
+ }
+#---------SVC4626------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_DISSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already dissociated from group '%1'.",
+ messageId: "SVC4626"
+ }
+#---------SVC4627------------------------------
+# %1 - property name
+# %2 - group name
+# %3 - group type name
+ GROUP_PROPERTY_NOT_FOUND: {
+ code: 400,
+ message: "Error: property %1 listed in group %2 is not exist in group type %3.",
+ messageId: "SVC4627"
+ }
+#---------SVC4628------------------------------
+# %1 - csarUUID
+# %2 - VF name
+ VSP_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: The VSP with UUID %1 was already imported for VF %2. Please select another or update the existing VF.",
+ messageId: "SVC4628"
+ }
+#---------SVC4629------------------------------
+# %1 - VF name
+ MISSING_CSAR_UUID: {
+ code: 400,
+ message: "Error: The Csar UUID or payload name is missing for VF %1.",
+ messageId: "SVC4629"
+ }
+#---------SVC4630------------------------------
+# %1 - VF name
+# %2 - new csarUUID
+# %3 - old csarUUID
+ RESOURCE_LINKED_TO_DIFFERENT_VSP: {
+ code: 400,
+ message: "Error: Resource %1 cannot be updated using CsarUUID %2 since the resource is linked to a different VSP with csarUUID %3.",
+ messageId: "SVC4630"
+ }
+#---------SVC4631------------------------------
+# %1 - policy name
+ POLICY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Policy type %1 already exists.",
+ messageId: "SVC4631"
+ }
+#---------SVC4632------------------------------
+# %1 - target name
+# %2 - policy type name
+ TARGETS_NON_VALID: {
+ code: 400,
+ message: "Error: target %1 listed in policy type %2 is not a group or resource.",
+ messageId: "SVC4632"
+ }
+#---------SVC4633------------------------------
+# %1 - policy name
+ TARGETS_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Policy %1 target list was provided but does not have values",
+ messageId: "SVC4633"
+ }
+#---------SVC4634------------------------------
+ DATA_TYPE_CANNOT_BE_EMPTY: {
+ code: 500,
+ message: "Error: Data types are empty. Please import the data types.",
+ messageId: "SVC4634"
+ }
+#---------SVC4635------------------------------
+# %1 - csar uuid
+ RESOURCE_FROM_CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: resource from csar uuid %1 not found",
+ messageId: "SVC4635"
+ }
+#---------SVC4636------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST: {
+ code: 400,
+ message: 'Error: Data type %1 cannot be upgraded. The new data type does not contain old properties or the type of one of the properties has been changed.',
+ messageId: "SVC4636"
+ }
+#-----------SVC4637---------------------------
+#%1 - attribute name
+ ATTRIBUTE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' attribute was not found.",
+ messageId: "SVC4637"
+ }
+#-----------SVC4638---------------------------
+#%1 - attribute name
+ ATTRIBUTE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Attribute with '%1' name already exists.",
+ messageId: "SVC4638"
+ }
+#-----------SVC4639---------------------------
+#%1 - property name
+ PROPERTY_NAME_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: Property with '%1' name and different type already exists.",
+ messageId: "SVC4639"
+ }
+#-----------SVC4640---------------------------
+#%1 - property name
+ INVALID_PROPERTY: {
+ code: 409,
+ message: "Error: Invalid property received.",
+ messageId: "SVC4640"
+ }
+#---------SVC4641-----------------------------
+#%1 - invalid filter
+#%2 - valid filters
+ INVALID_FILTER_KEY: {
+ code: 400,
+ message: "Error: The filter %1 is not applicable. Please use one of the following filters: %2",
+ messageId: "SVC4641"
+ }
+#---------SVC4642-----------------------------
+#%1 - asset type
+#%2 - filter
+ NO_ASSETS_FOUND: {
+ code: 404,
+ message: "No %1 were found to match criteria %2",
+ messageId: "SVC4642"
+ }
+#---------SVC4643------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %3 - "category name"
+ COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY: {
+ code: 404,
+ message: "Error: %1 sub-category '%2' not found under category '%3'.",
+ messageId: "SVC4643"
+ }
+#---------SVC4644------------------------------
+# %1 - Format
+ CORRUPTED_FORMAT: {
+ code: 400,
+ message: "Error: %1 format is corrupted.",
+ messageId: "SVC4644"
+ }
+#---------SVC4645------------------------------
+# %1 - "groupType"
+ INVALID_VF_MODULE_TYPE: {
+ code: 400,
+ message: "Error: Invalid group type '%1' (should be VfModule).",
+ messageId: "SVC4645"
+ }
+#---------SVC4646------------------------------
+# %1 - "groupName"
+ INVALID_VF_MODULE_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. VF Module name '%1' contains invalid characters",
+ messageId: "SVC4646"
+ }
+
+#---------SVC4647------------------------------
+# %1 - "modifiedName"
+ INVALID_VF_MODULE_NAME_MODIFICATION: {
+ code: 400,
+ message: "Error: Invalid VF Module name modification, can not modify '%1'",
+ messageId: "SVC4647"
+ }
+
diff --git a/asdctool/src/main/resources/config/logback.xml b/asdctool/src/main/resources/config/logback.xml
new file mode 100644
index 0000000000..298587bd1d
--- /dev/null
+++ b/asdctool/src/main/resources/config/logback.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+ <property scope="system" name="ECOMP-component-name" value="ASDC" />
+ <property scope="system" name="ECOMP-subcomponent-name" value="ASDC-TOOL" />
+ <property name="default-log-pattern"
+ value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{uuid}|%X{serviceInstanceID}|%thread||${ECOMP-subcomponent-name}|%X{userId}|%level|%X{alarmSeverity}|%X{localAddr}|${beFqdn}|%X{remoteAddr}|%logger{35}|%X{timer}|ActivityType=&lt;%M&gt;, Desc=&lt;%msg&gt;%n" />
+
+ <appender name="DEBUG_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${HOME}/asdctool/logs/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log</file>
+
+ <!-- accept DEBUG and TRACE level -->
+ <!--<filter class="ch.qos.logback.core.filter.EvaluatorFilter">-->
+ <!--<evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">-->
+ <!--<expression>-->
+ <!--e.level.toInt() &lt;= DEBUG.toInt()-->
+ <!--</expression>-->
+ <!--</evaluator>-->
+ <!--<OnMismatch>DENY</OnMismatch>-->
+ <!--<OnMatch>NEUTRAL</OnMatch>-->
+ <!--</filter>-->
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${HOME}/asdctool/logs/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <root level="DEBUG">
+ <appender-ref ref="DEBUG_ROLLING" />
+ </root>
+ <logger name="org.openecomp.sdc" level="DEBUG" />
+
+</configuration> \ No newline at end of file
diff --git a/asdctool/src/main/resources/config/titan.properties b/asdctool/src/main/resources/config/titan.properties
new file mode 100644
index 0000000000..bfb0ebc542
--- /dev/null
+++ b/asdctool/src/main/resources/config/titan.properties
@@ -0,0 +1,3 @@
+storage.backend=cassandra
+storage.hostname=localhost
+storage.port=9160
diff --git a/asdctool/src/main/resources/es-resources/README.txt b/asdctool/src/main/resources/es-resources/README.txt
new file mode 100644
index 0000000000..a7006efa80
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/README.txt
@@ -0,0 +1,43 @@
+ASDC elasticsearch tool
+========================
+
+This tool purpose is to ease and allow updating elasticsearch indices.
+
+In order to use the scripts, you need to verify Python is installed and to install the elasticsearc-py library:
+ Verify pip is installed: $command -v pip
+ if not installed:
+ Download https://bootstrap.pypa.io/get-pip.py
+ $python get-pip.py (see instruction: https://pip.pypa.io/en/latest/installing/#installing-with-get-pip-py)
+ $pip install elasticsearch
+
+
+Tool contains:
+ - index_ops.py
+ This script includes operations on elasticsearch index:
+
+ create index:
+ $python index_ops.py -o create -a <elasticsearch hostname> -n <indexName> -f <index mapping file>
+
+ delete index:
+ $python index_ops.py -o delete -a <elasticsearch hostname> -n <indexName>
+
+ copy index (assumes destination index already exists):
+ $python index_ops.py -o move -a <elasticsearch hostname> -n <indexName> -t <toIndex>
+
+
+ - file_utils.py
+ This script includes operations on files
+
+ - audit_migration_1602.py
+ This script run full flow to migrate audit information from previous versions to ASDC 1602
+ It has 2 inputs:
+ 1. config_properties.py - this file holds configuration (hostname, index name, index mapping file etc.)
+ 2. folder of fields mapping per elasticsearch type (map old field to new field)
+ The flow of this script is as follow:
+ * create temp index with correct index mapping
+ * scan the audit index to get all records
+ * manipulate fields data and insert it to temp index
+ * delete audit index
+ * create audit index with correct mapping
+ * copy from temp index to newly created audit index
+ * delete temp index \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/auditMappings.txt b/asdctool/src/main/resources/es-resources/auditMappings.txt
new file mode 100644
index 0000000000..7de77cccbd
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/auditMappings.txt
@@ -0,0 +1,169 @@
+{ "settings": {}, "mappings":
+{
+"distributiondownloadevent":
+{ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "auditinggetuebclusterevent":
+{ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "distributionstatusevent":
+{ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+"distributionengineevent":
+{ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ROLE": { "include_in_all": true, "type": "string" },
+ "API_KEY": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "D_ENV": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }},
+ "_all": { "enabled": true } },
+ "useraccessevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_UID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_NAME": { "include_in_all": true, "type": "string" }} ,
+ "_all": { "enabled": true }},
+ "resourceadminevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER_NAME": { "include_in_all": true, "type": "string" },
+ "PREV_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER_UID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "PREV_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DPREV_STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DCURR_STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "COMMENT": { "include_in_all": true, "type": "string" },
+ "ARTIFACT_NAME": { "include_in_all": true, "index": "not_analyzed", "type": "string" } },
+ "_all": { "enabled": true }} ,
+ "useradminevent":
+ { "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER_NAME": { "include_in_all": true, "type": "string" },
+ "USER_EMAIL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_ROLE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_AFTER_EMAIL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_BEFORE_ROLE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_AFTER_ROLE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_UID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_NAME": { "include_in_all": true, "type": "string" },
+ "USER_BEFORE_NAME": { "include_in_all": true, "type": "string" },
+ "USER_BEFORE_EMAIL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER_UID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_AFTER_NAME": { "include_in_all": true, "type": "string" } },
+ "_all": { "enabled": true } },
+"distributionnotificationevent":
+ {"properties":{
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER_NAME": { "include_in_all": true, "type": "string" },
+ "MODIFIER_UID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" }}},
+"categoryevent":
+{"properties":{
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CATEGORY_NAME": { "include_in_all": true, "type": "string" },
+ "SUB_CATEGORY_NAME": { "include_in_all": true, "type": "string" },
+ "GROUPING_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } },
+ "authevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER": { "include_in_all": true, "type": "string" } ,
+ "AUTH_STATUS": { "include_in_all": true, "index": "not_analyzed","type": "string" } ,
+ "REALM": { "include_in_all": true, "index": "not_analyzed","type": "string" }} ,
+ "_all": { "enabled": true }},
+ "consumerevent":
+ {"properties":{
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ECOMP_USER": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" }},
+ "_all": { "enabled": true } },
+ "distributiondeployevent":
+ { "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER_NAME": { "include_in_all": true, "type": "string" },
+ "MODIFIER_UID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS", "precision_step": 4, "type": "date" }}}}} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/audit_migration_1602.py b/asdctool/src/main/resources/es-resources/audit_migration_1602.py
new file mode 100644
index 0000000000..8b61ebfaf0
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/audit_migration_1602.py
@@ -0,0 +1,132 @@
+import itertools
+import string
+import json
+from datetime import datetime
+from elasticsearch import Elasticsearch
+import elasticsearch
+import elasticsearch.helpers
+from elasticsearch.client import IndicesClient
+import sys, os
+from index_ops import createIndex, deleteIndex, copyIndex
+from config_properties import getGlobalVar
+from file_utils import readFileToJson
+
+def updateFieldNames(client, queryFrom, fromIndex, destIndex, addUTC):
+ typesDir="types"
+ typeFields = {}
+ for filename in os.listdir(typesDir):
+ print filename
+ fieldNames=readFileToJson(typesDir+os.sep+filename)
+
+ type=filename.split(".")[0]
+ typeFields[type] = fieldNames
+
+ client.indices.refresh(index=fromIndex)
+ res = elasticsearch.helpers.scan(client, query=queryFrom, index=fromIndex)
+
+ actions = []
+ for i in res:
+ res_type = i['_type']
+ fieldNames = typeFields.get(res_type)
+ if (fieldNames != None):
+ action={}
+ for field in i['_source']:
+ updatedName=fieldNames.get(field)
+ if (updatedName != None):
+ if (field == 'timestamp' and addUTC == True):
+ value+=" UTC"
+ value=i['_source'].get(field)
+ action[updatedName]=value
+ else:
+ action[field]=i['_source'].get(field)
+ i['_source']=action
+
+ i['_index']=destIndex
+ i.pop('_id', None)
+ actions.append(i)
+
+ bulk_res = elasticsearch.helpers.bulk(client, actions)
+ print "bulk response: ", bulk_res
+
+
+
+def updateAllrecordsWithUTC(client, queryFrom, fromIndex, destIndex):
+
+ #scan indices
+ client.indices.refresh(index=fromIndex)
+ res = elasticsearch.helpers.scan(client, query=queryFrom, index=fromIndex)
+
+ actions = []
+ for i in res:
+ print i
+ i['_index']=destIndex
+ i['_source']['TIMESTAMP']+=" UTC"
+ actions.append(i)
+
+ bulk_res = elasticsearch.helpers.bulk(client, actions)
+ print "bulk response: ", bulk_res
+
+
+def printQueryResults(client, myQuery, indexName):
+ client.indices.refresh(index=indexName)
+ res = elasticsearch.helpers.scan(client, query=myQuery, index=indexName)
+ for i in res:
+ print i
+
+def main():
+ print "start script for changing fields"
+ print "================================="
+
+ # initialize es
+ es = Elasticsearch([getGlobalVar('host')])
+
+ try:
+ mapping=readFileToJson(getGlobalVar('mappingFileName'))
+ res = createIndex(es, getGlobalVar('tempIndexName'), mapping)
+ if (res != 0):
+ print "script results in error"
+ sys.exit(1)
+
+ print "scan audit index and manipulate data"
+ print "===================================="
+
+ print "start time: ", datetime.now().time()
+ updateFieldNames(es, getGlobalVar('matchAllQuery'), getGlobalVar('origIndexName'), getGlobalVar('tempIndexName'), getGlobalVar('addUTC'))
+
+ print "re-create original index"
+ print "========================="
+ res = createIndex(es, getGlobalVar('origIndexName'), mapping)
+ if (res != 0):
+ print "script results in error"
+ sys.exit(1)
+
+ print "copy data from temp index to original"
+ print "======================================="
+ res = copyIndex(es, getGlobalVar('tempIndexName'), getGlobalVar('origIndexName'))
+ if (res != 0):
+ print "script results in error"
+ sys.exit(1)
+
+ print "delete temp index"
+ print "=================="
+ res = deleteIndex(es, getGlobalVar('tempIndexName'))
+ if (res != 0):
+ print "script results in error"
+ sys.exit(1)
+
+
+ print "end time: ", datetime.now().time()
+
+ except Exception, error:
+ print "An exception was thrown!"
+ print str(error)
+ return 2
+
+
+if __name__ == "__main__":
+ main()
+
+
+
+
+
diff --git a/asdctool/src/main/resources/es-resources/config_properties.py b/asdctool/src/main/resources/es-resources/config_properties.py
new file mode 100644
index 0000000000..d0973001dc
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/config_properties.py
@@ -0,0 +1,11 @@
+globalVars={
+ "host": "127.0.0.1",
+ "origIndexName": "temp_audit",
+ "tempIndexName": "temp_audit2",
+ "addUTC": False,
+ "mappingFileName": "auditMappings.txt",
+ "matchAllQuery":{"query": {"match_all": {}}}
+}
+
+def getGlobalVar(propertyName):
+ return globalVars.get(propertyName) \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/file_utils.py b/asdctool/src/main/resources/es-resources/file_utils.py
new file mode 100644
index 0000000000..743902084e
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/file_utils.py
@@ -0,0 +1,21 @@
+import itertools
+import string
+import json
+from datetime import datetime
+from elasticsearch import Elasticsearch
+import elasticsearch
+import elasticsearch.helpers
+from elasticsearch.client import IndicesClient
+import sys, os
+
+def readFileToJson(fileName):
+ print "read file ", fileName
+ fo=open(fileName)
+ try:
+ json_mapping=json.load(fo)
+ fo.close()
+ except ValueError:
+ print "error in reading file " , fileName
+ fo.close()
+ raise
+ return json_mapping
diff --git a/asdctool/src/main/resources/es-resources/get-pip.py b/asdctool/src/main/resources/es-resources/get-pip.py
new file mode 100644
index 0000000000..30a6cd7f4c
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/get-pip.py
@@ -0,0 +1,17759 @@
+#!/usr/bin/env python
+#
+# Hi There!
+# You may be wondering what this giant blob of binary data here is, you might
+# even be worried that we're up to something nefarious (good for you for being
+# paranoid!). This is a base85 encoding of a zip file, this zip file contains
+# an entire copy of pip.
+#
+# Pip is a thing that installs packages, pip itself is a package that someone
+# might want to install, especially if they're looking to run this get-pip.py
+# script. Pip has a lot of code to deal with the security of installing
+# packages, various edge cases on various platforms, and other such sort of
+# "tribal knowledge" that has been encoded in its code base. Because of this
+# we basically include an entire copy of pip inside this blob. We do this
+# because the alternatives are attempt to implement a "minipip" that probably
+# doesn't do things correctly and has weird edge cases, or compress pip itself
+# down into a single file.
+#
+# If you're wondering how this is created, it is using an invoke task located
+# in tasks/generate.py called "installer". It can be invoked by using
+# ``invoke generate.installer``.
+
+import os.path
+import pkgutil
+import shutil
+import sys
+import struct
+import tempfile
+
+# Useful for very coarse version differentiation.
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ iterbytes = iter
+else:
+ def iterbytes(buf):
+ return (ord(byte) for byte in buf)
+
+try:
+ from base64 import b85decode
+except ImportError:
+ _b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
+
+ def b85decode(b):
+ _b85dec = [None] * 256
+ for i, c in enumerate(iterbytes(_b85alphabet)):
+ _b85dec[c] = i
+
+ padding = (-len(b)) % 5
+ b = b + b'~' * padding
+ out = []
+ packI = struct.Struct('!I').pack
+ for i in range(0, len(b), 5):
+ chunk = b[i:i + 5]
+ acc = 0
+ try:
+ for c in iterbytes(chunk):
+ acc = acc * 85 + _b85dec[c]
+ except TypeError:
+ for j, c in enumerate(iterbytes(chunk)):
+ if _b85dec[c] is None:
+ raise ValueError(
+ 'bad base85 character at position %d' % (i + j)
+ )
+ raise
+ try:
+ out.append(packI(acc))
+ except struct.error:
+ raise ValueError('base85 overflow in hunk starting at byte %d'
+ % i)
+
+ result = b''.join(out)
+ if padding:
+ result = result[:-padding]
+ return result
+
+
+def bootstrap(tmpdir=None):
+ # Import pip so we can use it to install pip and maybe setuptools too
+ import pip
+ from pip.commands.install import InstallCommand
+
+ # Wrapper to provide default certificate with the lowest priority
+ class CertInstallCommand(InstallCommand):
+ def parse_args(self, args):
+ # If cert isn't specified in config or environment, we provide our
+ # own certificate through defaults.
+ # This allows user to specify custom cert anywhere one likes:
+ # config, environment variable or argv.
+ if not self.parser.get_default_values().cert:
+ self.parser.defaults["cert"] = cert_path # calculated below
+ return super(CertInstallCommand, self).parse_args(args)
+
+ pip.commands_dict["install"] = CertInstallCommand
+
+ # We always want to install pip
+ packages = ["pip"]
+
+ # Check if the user has requested us not to install setuptools
+ if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"):
+ args = [x for x in sys.argv[1:] if x != "--no-setuptools"]
+ else:
+ args = sys.argv[1:]
+
+ # We want to see if setuptools is available before attempting to
+ # install it
+ try:
+ import setuptools # noqa
+ except ImportError:
+ packages += ["setuptools"]
+
+ # Check if the user has requested us not to install wheel
+ if "--no-wheel" in args or os.environ.get("PIP_NO_WHEEL"):
+ args = [x for x in args if x != "--no-wheel"]
+ else:
+ # We want to see if wheel is available before attempting to install it.
+ try:
+ import wheel # noqa
+ except ImportError:
+ args += ["wheel"]
+
+ delete_tmpdir = False
+ try:
+ # Create a temporary directory to act as a working directory if we were
+ # not given one.
+ if tmpdir is None:
+ tmpdir = tempfile.mkdtemp()
+ delete_tmpdir = True
+
+ # We need to extract the SSL certificates from requests so that they
+ # can be passed to --cert
+ cert_path = os.path.join(tmpdir, "cacert.pem")
+ with open(cert_path, "wb") as cert:
+ cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem"))
+
+ # Execute the included pip and use it to install the latest pip and
+ # setuptools from PyPI
+ sys.exit(pip.main(["install", "--upgrade"] + packages + args))
+ finally:
+ # Remove our temporary directory
+ if delete_tmpdir and tmpdir:
+ shutil.rmtree(tmpdir, ignore_errors=True)
+
+
+def main():
+ tmpdir = None
+ try:
+ # Create a temporary working directory
+ tmpdir = tempfile.mkdtemp()
+
+ # Unpack the zipfile into the temporary directory
+ pip_zip = os.path.join(tmpdir, "pip.zip")
+ with open(pip_zip, "wb") as fp:
+ fp.write(b85decode(DATA.replace(b"\n", b"")))
+
+ # Add the zipfile to sys.path so that we can import it
+ sys.path.insert(0, pip_zip)
+
+ # Run the bootstrap
+ bootstrap(tmpdir=tmpdir)
+ finally:
+ # Clean up our temporary working directory
+ if tmpdir:
+ shutil.rmtree(tmpdir, ignore_errors=True)
+
+
+DATA = b"""
+P)h>@6aWAK2mtey7Dt5400EW_006Ei000jF003}la4%n9X>MtBUtcb8d7WBuZ`-yK|KFd2brwikWo_B
+;u!rn<*pQ}JfvyeG_Vod&A;=bObCD&Dl#;mHzWcj7k|JfvSvC!;CGmK7Jl_3ycgo4LuUaL)T8i>3Uf!
+{K-)yULvX<43rRlnDTFKLtiCtaEhGk1t6>Y;){XChN_eHhYh;m~eE7jfAO`S=_?el#mOCVI;OttT5C7
+)=ywWt&Ru;O(is#00muS(TqMUmmlODQWEvx{oC%gWq5U5T3R9Fw*YMK^!Ln^b5XJWq3>8Yz}7iHK>im
+euCS+?>~vuSm3`xLY~iqKVm#%T+5yR>VxT%R4R=kjHG9ea<q;*a<7E-!eCSnJG@Tk(SFvRj$S6EiL<s
+Mq>7%&OvOsdBN9NTQurtUeqruxyzb{dn;XOOY|12T6iY~H_KCECyGp_mh|{!`wT`}HI6L3<7HmSMDpK
+St{Rop+3GgiaFw*OD8%yHkxdIH3@+F@4yNJPdge#%1o0%AOeQBRQ%Y>g9WNWUt|VI**)9J!Ybv(nY@5
+~f9*N#>g<@;*z!l3_4crO=YisuGe#=WE4S2FUk%8apXYkt@aA)_NW#C*VOq8O5{SgW&n=Qv>v0at71&
+`U(F0?blF0b@zrVNBjT!IpJ$Ox>%Kr;@T1iRXxviFs|W#B?Jm&G0=0sjo#TQn`XO=7*A4Bl~`xLY<q9
+HV7(W;_X3(kh;7-kd4kv~^PNNf~^RSKyE<@F93PNmDZoj4)@@?~=3Rn<<zZBNaSuZbLQ!*LY3u!Za<j
+9Rz!Xf`1k{C$5Kk_Y>_MBV(C;pV+8Q)}jEnw2$Ew_O+6H8Z(F;zNzGXKJE(W2svM~tZgdrzPjKI52JH
+(p6PUI;+$5hjl&ET-lEf>rn?L*V}?y&g>Ht5h#S5Tjlu}++`dPZOo;BX%$5ab%RN(7D_6MWs^qL%lPF
+KR+VY}cY9&PtY(t3ZEdzx<t1YlH`qS?Tdr_4^W9M=v*0`S%d|cssk^|d|98!OIqTrenObD)CU8O&Lc>
+gxNc>BxM>&y3-0XZP9TqOYDLRO`=8(tE8M3(fp0td~}$sFBHfK1YlZ?9jx3l@p03(<tp&Dr_F&43A_Y
+{J2$3!D`!v{4(mcQRpjCC2jHh`iB!X6#I})gS?WmGcL}>#?apptr%_ra8NT;Gpe>Uj$jiH;T0$+=Y=A
+<l^dPVRpc=8|v-GC=PJiJqf!<3C4V|1@!3YwYd5EFBdlC{RX^AFVVOvm`ieqk`&!I)i1+nptoI>b%+D
+bP9#hMm;|(g*i-zR1>mQ3;Bx}M1K;T}9B0brb2Htsr&aC2D;~gS&Z47Y0c<fnSiz}_J7tbvhR3fW>$a
+tN%lw^rM3HVnK*5aOZ~P{K*RoOfqS=PQEQtErI^mr|*sEBf`3;kzy6-G+FH{s&w!U^Gj|gd@X;l{c`F
+q*&0}afga7uE^_rAKtDx%fcF&oq-gdik`ta^(|@Hi^-&@$-je}ZxWxFnr~xa)cYy6G`n;}$I8M90Hy0
+J42cuHD3;TE49wA;YVdh%HDQ4aR-v8i^y^Fh5PXIdGMQw}Tc4p2Nn6@PKDt3co3CUAYOP7)g>clN&Wu
+#I6ZQ`jMaLAWby;6g@qK#g6aOnnYj_v1juxEl%EjxVpN&eD^N>`SSJEV&a97K1x~DLdqcdov2y`5k$e
+Y7QDj&eGq4N8RT|&U>!y?&c(G73_ci)59UDxs71?q=((B0>czl(XuXG+-9<Fd+;&GUVdI1K+fM9aWJ!
+h|&Q=TfqO99yRN!qV5bqAe-TEP9ctuX4*){ZJH?QxB=mlUnUa+|#uxReo){(`)Xf8A$soaXymSHX|D5
+^H<Fh&f&vysqsV!<bYagRVpMMt7Nk{7BE?%V2KXFTTouzDf*=%l#hE3OorkUpXwfl5qSMu!G4YJExge
+ov+rtkcGhZDiHfYB6A7(P|h$(U`EX(gGT@kNV`O2w@rz9`N!rhUcW`VYEACk=1CpdIl3u4_du(0Sl5}
+D^Z3w4=%ubtuRhT9XHa8?;#(h#bcSlxtBboxA$PB-#bNuRzY1Bz@h)CJjJq>{l`fEf!!EKSp>18&+<I
+QWu|%%X1>h8;XI19tvO-r(V7&{)XhR9Mm07?waf}%B5DOAw(hpG{yfNuh9z<M<(J)}Bab+NV+0Wp&Su
+a8a1vhTDaK`JJOrw%w7%WIaUpdYTxZP|rb*N?JocO|75+kXgo!v*M^R6zEzTf}v2;8&fDFH(Zl%C&6t
+GiM3FGCJX0fkSOgnGJZqC157`*|3a$VtVc*!NCRzAqXJB#nGbWC<oL#KWahTfoIqX%NMlHRJ&A9}N`l
+*r$ogz#t1Fx4SUsz^(Um(E|<ZCTwbyAtim_UNYUkg7?{8MrQ@Y_?%f^DeIkNe6j8(RuR_mx5ryp2N@S
+?3?ct7@uq}V)m<vd$^+$?~wbV=Sw}F5Je`Wz4yivy23K^Ot{hmS}UZC;=J$DqiMb|&zq`&oY|0K&aBu
+YZXH{TORgXeb#W?`H8m!5LgAT3r#?}d4hWrBSlCr#e#Mp?Ohx7Yu>QdRIXEdeH|`vsONpTZ41gji!<_
+Nlydpv8upP5mBX%6Jnfbqhm<2Y4huhf<t6;o)_2$+4swz}q%QdT5YR~Q-BjB{~#iVpb0?AXDA8?Kx2k
+gUWFQ6Z7Yn>VzX|tILQd@)_Ftai2)qfD!s>wm?<X(z~hkf*jjuQe56{6&d*L@;7AUcf@dj9x>ekl58K
+j_1VK;6U%=!wahTPG%Q-4mvRU71IlT=3EjM>u%qr2^JOhf>D%qkfh1Ap}?*<GZdP_BEMUg}ZTE4*4m+
+Q)bgbqGRFVpv@Ut9$ucXQyYScwnT91*s|%Vnj&7<DK2rrO-3R8TnPBsOeWZ6`z2@ls%1N#K}u_R(KJu
+C%mmop?_5L1Fta%WaEu)6Lja@YC|EI7z+#|p%Fc>Jqpf&j`#0uwBKfYm<A;VY$YtNg9lcFDwtRJ)3RM
+*VSGf;m_8rKhG@;wB>1<}lrnp@c{LY4M8lD(EE$o<)jo?TU=h!E)V%G+?6m?G=W3zU2JGi~029`0QY2
+#I`WG`F~pMA8SA&^QRKHXqy+LyQh?{)Ix5|X})pWeTE`{tdK-L*c!?*R97i4#t9of=K!G0xov>+GhLT
+0xJpV{6*r3X7y>3rF#0%XAA@nZwU1XkgDuNj04f!hUOYGSasjID$n&ca-^kF8jF!`_k}g0CrqTgJILl
+>+84I=WrNLAs~QufOn4eQM|LQfMY#?E^b5XPxAvWwo5ZfoQ(lf(39JBB=m9O<ey(Sn|*{;>3%uqpE)&
+iA;4Be27vWaR-bu!txHJ!{t;HoU-_y5e>a=E#%~#LWT$DUc(IO|-XN$tW5#pQpmWEhKFn70F!B%@CnN
+@3JHYJ*fCapVfBUa3Qy8<ieF7`X*dA}SvME;lk`q&;s$r#}3}!O?ls}KY2(-b|9CM!$vhbU)KjrQ}nm
+{vtBwOt`pkLlu1#@g_y@jg+&ThP;NCPD1<!TR=WO)nk*8(!UCIc8Dobrr=jXIOv*+jpDj1_y9gUp{=Y
+IZ{8+|o0J*H0Y#JP5bu8=PFF((Si@yZ{YVs@Q_#rim4HQv2tQw@ZYi{#Y-j?WK2nM1xS9&dTvj)-S}n
+-uA&a*g^0+10(n(28+VMI9^lDf|?beHhq+<P)p`%NbvmMi+6|u4adqVX47$Y*uq36wu{-8ouPC&0&on
+<4kizKe%fBtqxWCKHsQ$N0Qx(N4lb&;&~57+pjhqz?SN}tDbZAN?>rN-d8l68-aJ8V>yb7o8CT%sggt
+x4o~TCfWNT5FWUA*~Lt4*ub?(p&^cq}`lKvf;*f3QZ=@Po{jRLZ8{PP7!b!kwixs?9WGvuO}7(*s-Go
+8=Dec<2ubP=;pgM|f`4=@AMn=_hA2^+El_k~H{cbc5a?{$A1Z!tq0^3uv^T<b5#wHrG9&K8FhP+K;uE
+h2QG5`)W$;+b4+!$HM?SFQ_EZ<2xOM`s~ub;2-*>bMLeL*11Y*+9er)`bBK2SZ=M4?u3-_$LG;@W9-o
+4<nLc2giefR*w*PeDv^C8KoRm9)ru*Z+rz$0S&wYleC^gWnUs|glgpPAOoS2Fax}O{6of*5Q3xuO1ym
+azh5pvT!$2$#1Gf6O?uq34I83G$4`Hy2k10^HF7>qJAxPkVE6}%u_qTE4|rr*Xu;+P*iNnYy))-jokL
+_E<>#kOk1LOtA1C7r(LC=kGAv=D`)+6BERWm7u?l`aRevJBxVZcarjKM5jy1w=aH@VhRVRG2nVSaNQC
+)ohKOSVD@$o?fBoL)DqVbP~c)7PWt1pz!-D+83TAe<cJlk{=+@uk!gM70u-dWzH*CeH*af(<-bwCQ4Z
+oP*DH~Ex*5$JT@k&87OaS|_uzd6_2NFNVE?@O0iF{Sxl>7W{EDhE$zy@j?2@P+p(fW8uD-)G!(RQ;Jo
+xa*Q)S2yfc3lX+tL7NbPX@O5j4F#4!HT9nzQTr*?ebLnXr9oHuQDp|X_x}M<O9KQH000080P~d=M@vb
+hWitT)07wD=01p5F0B~t=FJE76VQFq(UoLQYU6D^u!!QiR@BI{(c4&u2#~En{j$9G<D748MktQK_Rrc
++%vy=^T%Afb1?O$w;Gbm*jTC!0J+?--2&>tB6LPlNBO;hh9ZachS)9m2Y!fHTg19=I-g)>-5)UZy*_y
+g#11e(nC2o%NFCruN3-zSHly^kvv&4T)DAYEvR0g_9P5B)m%Bk#ZkBv#&T8)v!gq=!%TaNnmBwC!^Py
+mm5=^JN<Rn{!lZ)-EX3r%TmZmyuv}Iq2EIAVrX}O9;_Idv9nDfM$qcya`0Xv2D$IGq`vX2r{8mm$6DP
+huttZLgQ^Uy%a*4?%@DN!8+)$eF#@6`to|>e<-@X_TBwIZv^)H;@Wb1=4di?Plpn;|NU2C%)Ny8rJE~
+SiK$#S29>XbnQuoOn|gWaxXKSuO9KQH000080P~d=N1nacs-_D70C_0@01^NI0B~t=FJfVHWn*t`ZDD
+R?E^v9x8f$OcIP!ab1))V?Y2mutI~;HbVC=O?x@&Ce1bMW32ns^BXq#JE^pcbl-=hEhX86#PN?P1LP9
+GA9oEZ-1J*1;3dXp+fPQ`Yc78zOQsZwKtuaf1dX7FE>niot(QFOGDVoNApmAaISQo^@8AvH-CO5~+x)
+P5fwx$#`A*SuK!PoeyqI#jP>r+2AT%zr<mvH%=)WEb1<onK|S%>bB-LRC|fTr5*F<bh;aAUZ9Jj7hrZ
+QkQAYihYfeiH9N=Y34Dm`0j=&1w?8X_PAub7Tx$wnmHkj$*fR1&2zKzxs*bV$sMfP9XujWJdT({zmja
+vMXBDDJkQR!G{Z^F&{Ax7sU~f?(u^<lsVG)_T|+KFokpP2LtBVTv!5$71MKcHV6<4Du~k~;TK&lK-CH
+5Isn$%^#3cLGQ*nE~X1|tPvMnq0n3#2_tcMz!0cc$+x)d3!g=i#lb9Z`reseP>=hxSl*TBpCr}^cFId
+z}!XV<rPuP@H$@9F&VmcG5bo1a-xUR|HRpIzSF&^LFpi!*vQyLLE74M~Bq=GB60rs+<K^_Ud6xt#xpQ
+IO-N13t;vPBLpr9`%}S#hzue0_@|TAFNS!(VQM?5oI6)7zjwxB7iGp#;F_z%+NiPXnb_JVarcPM@N)`
+n8CNE<bLFgWb_fnaWPQp)N6kNo4YV$F{MSi1%FHc%y_W3u2?L7VN0Ey82KR(;Lk<D%y+5Mb!%Y5GuQ!
+)yiIduhhQ<Pv}Ea4!FU|8M}6}0CHOBWID_p(W6~aDQbxcB;%00G!GafDQyMFluf~K!pg;jk%?ge$e5}
+dRPY<xlZ!O0e88$~J1Sw3+N87VTs9~5N9V>FIM&y{pUhi=-wrIHZq=UBdS_0fB{UPpRrddWe06}d$0(
+`Tb4iv8G*~a(IH2B3*a!<fOQJRS*r5!*oGl251HN@wm>eY~Nu0IWUef;gWm6Vej?Hg|_il~h!^Nhguh
+>NsD3!*n%xo@(DY_O|PVbBavXIZELyP{D;we!g`-EnQ)FHYM0LQqF3qH+05FtH?lK+17rQ{(n}tu4F;
+mi2~#!xv1Zxo5wEfN{S}okk|x^b>PBq=s<Zl6(Ww(X~)*{WLkOCX$d=CTKUU<vd$Whgo0>pvtBs=QfU
+wF(hRzIa5<~*)h?4%fJWCSHqVaF6_0=ZJnK^5N)P$FG01VWGvJK|4)7qyokLh3@)S|&_}e<dKV>0j}B
+jv9ynp(B6X~(B-NIt-=O=Cl&V(+{AokM((+3<fHomW$83pJ%0?)yrXE(yQh@{6(Wbr{#`GgNL(a$&AR
+&4$(-(^p2NW^U9L=l*ZB_`)%`&M^I@V#tKY=LER|jk56=-15o0|(L!Z_p+f)ISgAuX}sS_X;^FNrhF1
+#9@gy3UkTXeBpCg!EwH1wGu9^exNJ0om$xNS_X%Z6+ZFnGgp$&IkniM=*g9_c(P3GwiT>DF+{G+b3-@
+{gTdrsi)ySH+er}kWsc!P$Q2Iu!*!l?zcN#Sr&oJMA8yIp_*`235<ZSx-LFJsEI|#LS$+r>RBlSB;Q@
+xP&ar=DGQHX2OV3;rE;xYz`@r>MEQoS(xnzCY1AC@rx{du3!Za*=##HQUW2lsS+BWGc@bW%igq{-d#d
+U62z7J~g(&M7XLpuNr{vX1Pmej<vmDlnKD@r3&*ncw&l36D6GTi|+9rV({shM9_3U<b`ud_qVtHd#2p
+<d4B~T%soxi#Jp@}&NIh((|jB3&K8;Q8>36ZN#!N6a&jXKJ>N*6h!?2#)pK57e8^o2k*6s>r!?QFo;p
+x?^Nj8WhG1)&UNoWNYvE5Rr4(#H$j<Xf{$6z;tuBL^Vhv7EB|(4RXV+}(^X4F~qaO(*coF8b}l0M~Xk
+!03n73g$T8f>T3noTz9J3Jj_lAF#G(kGq^Np+YyJQG=)^Ku7n-v4J!7m-<MaT{Zb?(QZs^9}+Btd?<n
+0uw*L@<NpD9nieT+MyDbd66&Aq#`HWk(F~fCZHyvUy*FB=`|myu>9TTz`@Yo=B6$Nrrk!m)E7+#ID8u
+uFC)~=5tqOmCo#<Zu{!+lCis9)C_HRS!*YExfL(Z;+Hf<zCA@+;_zz`U^QKNJKTA1gfml@l?EXq6|!p
+&Ho9su!HmhATwR@WD$5aA&9oSQYhIEVBQTeFXC+j2i3UFX|F!f%`M092*wX3j>zR^3M*@ne!aDF-OpY
+_20|aqzi=ESTbWw&gsAlm)a13QM|8x@W`~l9f+~m?r@02b$T0+-{gMl^<CMy)nl{bkB+mtH8qcwTo0T
+bAF>b#7rr+!7>uiA2tlYlz{RX>dUh5eTyh1s9_;fY?CH!9oJbKi-3NH3Fn;td4_+&4rZ5fMl+no3|O!
+N>pq}eTh#CZBB?2B#=h?Q_#~#yWSx?!`vme9K7ruUZ+=lF=0L=-*3c$$2vw5?Rp2Y0n?ez-@V%5djQ+
+jDdV`D4;et1i!rPUf&n**t=?gx3@d8M^@FZSX5@5j!?3)$DAB;lbkEZy*^Bg9i&^L#pefdSW>6F+D0^
+*9tNx)e{$E$6qR>yaHd3Je1v^?N})S8)I3DmfQc#N)uXZm$_zO?6A-#75BEV$la`y!bV()3F}OsEMf_
+FO`b1z#JHXO{LPC~x0VwS`>MxF223uIT)d&gNHlx1*1EDE4X;BxUMmMWL^P&!_2~|2(_7y`is9Z)cZt
+OO%Y-r!p;SVOw$M^k|IxlTH|!tw3uyfnB)+%!oOXMiN|`J{1ylo*&QvRsupxw?)F*a_IggP6Ex32|bg
+g<k;rs;jl0|HD{7Ydj)w6%#h$F_r@k``Xu&TCzH_^I4Pc1gUoasx^8I54@|Cec3|!`d{+wFja%(%(h=
+<2+0XCJPYB4(n`L#(>bhmv5HjKjchHT~ZNL$^IWNgf{^ZKsH5E(d&rk}+GUK9$Ei~y}z0~lrT?9xJI-
+n5WzqdKDmto;6DguE5<~!YHN3T%X#Q@YfCv;F5Ta@c~bnb85fHhmYpjV8>NS)%)6pNjPVjBDo-3xVh5
+P#vl9jWj7lK+bh26xlEK>Q*uM~2J^-jSG9|5R>&VuyuDB?Nq^17*3>&+vM=W73{{EzxjuD3r+JV2;N|
+e<tJ4iPSImg-f*VIx7GOY#3O(LHX2&-v>65SRk5|1qlxCfRy&>m57zOCQtV%&#|QJ`hVPP`u(v@@9NR
+Sn&uf}^MRBL-Gr=TU1Tz0#vY-1v3Gfac!CMn-E6gQH3sW0Pk#`=-w%8@4B~G^wvZnGG47r!C&r~6BYl
+?f>4_$G;7RW!)!k)R_pXiWaeoEtBgu(H{uf49zT7gXMQjE&2e4vSqE?%i=^!l~cfM_sX<P_lP8>QoYC
+UcXV_UjoQeinYmop8;;cq<r`@2-CO`78^7&H&iPngqKNE`x6g$7$dSx<r#un_ibF4l$BO$7YWG6hndV
+igh0PY#N}PYc3E<LQSbP_JZs(u-^Aq~<xAK52GNa??tJ4cSe@a`aC}d*%YLF^1Z0*i>WtDC?7>uOZc2
+yZ?iY30BAybs3f=kQ>_;xxT0OI`ac`E3yG$4~XTNVl2G>s;tep#&GpV3B%JL7D#X+vp*A{`(=Hz*cb~
+V{Y5f`N*rYa(VC*Ow0_-TA;MnT^u0gWO`q|~o(F!7r~+OYk_m=~|IvM22V}0N@Eosa!#LPmyThORk-~
+5O*CY7vb2p)T$|x}O=TEA?$*sccz<Bg;QIc&5)xw(ZA%<&`vlPDYfT$V7A*%P=CijevV|(CF;%$0>?(
+cv!Sqf-)yg{nzrzjXU6BT`*jL7j2oH>F*5sZm}pZ5CPddo?t+zwx`eWe(NcK`ya#h@6oTgP7kRpSwEj
+o9CFG;v+k=#FAyU@1BJFHlPZ1QY-O00;o{l@>?ZFvLBk3;+P(C;$Kv0001RX>c!MVRL10VRCb2axQRr
+omy>g+_(|`?q5Ny5lBkc>LrJMG3rCpyxbWmF2SMc7snO25_gxiBnl+8>%F4?y)%4~NJ(ouX9Ui!G#t(
+h=jEB9SF6?EsbWNQnv0fnR4OKkV!H2EtJTF;iiQ-$w%5I6ML~Ge38@L)C{g#C754kZg?p|}%6|lghS~
+6acq4~rnmy{Hq@CTS;!5|L*DA-~ek-M=78@bz3rp4UE-wzOt%S@Ke+<ZYyDRjmV``BwZ^VHqPhN_4%X
+e1Wk-QcqMKcU!=>>tGn(b(LD(sqK%WJlF42liduB8n;kbT>t1btgESsb|3J*`=LSQ{3KgRuez>LMGrH
+A)Yr?YfFfwI(~J3(aK1TUr;2|IRKhE(&1CrD%&zvMqlsBqKLq&%6{(2V7IR`uCUHFU4w&4{tMAQPxx`
+@<lSOPrtCb`y^yTHSkUS0p+(KajRJsz0R(U+~5Q6NQbLAlYa)e6*(}u5#TX<ASYW-kDrD!&(U?v`igD
+AeR#`tQKX91+ckMj<qp(){rcgEpWH_Hb7^^Y0<4A3*z`)14I{MXyOvet$n}2UmYbbmN0nDhm696=9(Y
+;icS%DZ3*=gOuE{-^?8hHwgxpvGn3s|@@X64<nUNiNrT6CD?0nMf?uUDFMcxjJo;Gp*jA2F#S}PYeMh
+ole%B`{w11rK|^|B@SHZPV}+OKPpkiWiCKPB)1{ssG;Sr%@%Ce4A$ZKH1nCU_RCyD_{*anKd~=ijVo6
+r!hT=oL^?Nx>|nhR`Qs>$8yCkkGaw4bv2MQu)Qe<{FnZj`*4o$W=AN<*C}c)UzhFXm|K2TN9S=u1T6)
+ZBewVE2|vnJK^UPy7pT-vpZ?vVA+Azy^)tHPfcEV@&)lgK@iWWDdeI}kPN)(?tavddqd=fQ3%gLjTZH
+*a!3`B-qJZO_+hrh4Q=v5W&`Pz7vwY~QW^P)yjkGGTS%!oedGNhThdF@2c`-%Cqzxm#2f#{)A|QCzu*
+&ycMZAv2uUnEtH|tp{E9#z7b;1T|3`4A)-3Awj8<^|&imcBKxoj)JtQ|bB>8M)R7tGWu+wyJzw}Qex&
+GEBv)y||cg*c=KA#D#kFA0VP4u3@Z?1@F>l6H9q-DpN!~YwmkBqgJiHR(rC0i&p+6xh?G%#uf3G!>cx
+Ot;McanT-zasN%@=Ad!V0n{+Tw+BbRO%}}oFP6<tx9XM0*2YtHo$gfk3be@mEcKKOKL_BpkQCuSgf@a
+H30-5LwceG*dtUsco`oco-$RCx24(ERv!uZq@lb`CO(WmHQlfpt@0joU0bOnB4M>+Pb~T{$`P--BuX@
+lRH7WR<R10JO2TRFj~nWUqiYi#F$mSaiH^2LhFuEBm}D>7bBm>K*wOfu>S+0gHkWqu>BiPg*65BcxP|
+qlCs;o3aiP0R{cg@Ld8HsMy+5bsd}5bd3iBmEGC%M!Crq830hkj$<Jd1Ra&1dG_iFomad-Rb`=pHQYl
+TEBuicA4RPYT;h851fg-mDi5#mMjfwyLLuDY6Q>wWjrOtAXau56ATIF8`<YgYkkZ+9QqmiDz)G0a_nz
+9Y7M4+s-L_yUyem67bdf$TP?e=#BNaL7ORkQ?D&kDG&jm`p1t0OVR=!3cZoASN)o!`o&JI2KBf&$IXl
+PYSB`5xBrXO36wMSsM+qcla8C5v<6)z~doU=7rny92kVd57L#*=bHQqg&{uWfG97lPM)<26JG8g&XV;
+rSobM$hm*9&5n6lfQo^TBLF<bMH#}0^Nzr$XIj<g{YBs=_w`MKmEn=DfjH?MCK1y~RI>(_C$-`o2_VB
+5OR#m+BGKe2c9MY=16Jf?%AaG#HsA3+?!HaoppZ=|rq67NVU>(N|y{G{1GnR>M@H0boJ^lt7w?<(!mv
+!&lSJ-fH_}obsT1A=|4^=>(ch_SmV^ncuGS_pOaTK_VMi9qCW^>(va`c^9q^5Isnniv*_Bif&xgSEBL
+nMzOeIn(al@BhO4IA6f_tMM^h-vFh5p+#Dc9BpY0eW(}fp<grawTfhM%<836cX+qXqAl(?f$?eoMz;|
+(WAA6v%<u|a9RONEJx#k2|h|DWPywU>B>J|t*z*jRn`X153oTFM;>W5OH1bYLMF1yatXLJ9SO(vaNro
+ZgF^F;J4)FvZnS42PXgxBfo9aw`XE>cTtj0ufIed{a7qC|v|-k#DibxbLg&Kig?Amx%%X404m}*1G4w
+F3=8{*CNRc~QTskt09}P}MWfd?A51eeN+K0#^Pzbv!2rlcNctB}_^P=~kfBkg(bMfi+A0Lz1!NOTZ91
+M$!9a?}Ni%=nKWiK(EqRntnMvlw^N7|muMC|IwD;9^*m@b?vp<UA1_$&*zfSh;Lqvmlywva?ZEwJ)Mz
+CA<up`IAXplC<+9<mV#q0->>0^m1t`R+3$eF5&j9Pk9%%FY{|G{nvukH$`lqbG5VzySwi#PqZa>^jMX
+<QhGbS~vl~$*>}R$eBN_!F=c$*nC-T+~Ord@A+>3LaCi8kli!#&femWKR^Q=7b)<^ja+l|A+InzNLL;
+2R@wUOy6FsfkuaM*jeOViPyqVIf$^-YV2;qTZ#t71urZIo{kg&Obg%+|=1|OX!4u|DCwOh<%yZ42R6U
+!<)g+Flk~|koGx`}2up{*$jhjlnh&9*bft}V+lj~qpEl-Py%#E8YH#@8vR*xOiW63%_Ej<51>W@+a=A
+jB{;tjyjp%q=dgZ|6Vh_dVf{#T^x&8#tU0i36-p!)<av1Rdqz@$3;F`V5lq4)IwWMcI9HRxU}<Q)KyD
+OW~mMa)eF(ge|Mpw4JQc<IXu`5^EuWNxj&%~Z!qoJ>GSS2Ze!K(K`JO|j9z`x-!=VF5Q6Fe&DJl|>j6
+vki#26DB#=LOd8=xVH%lqC_uC!*622Ni77||L^|-wr}!Cvk0;(Xv#1SLD)3e)nY?ysF4HmnE0*(F7^x
+-Ldq<|rl{t>vjV)ybyorKa2uxX|13KbTbJZ*ehzAWN7;)deo{bx`Gn*6G|NI!-hl%*J+jByepj<c?@0
+((bdbCaH|JM3W7wF0aeK{sOk`Z(_}ySjon}*?fQQx(oclPig>8%7D>_pj#dxSre*hSbnx@Q4!gsCd<e
+Ltv(u7lBUwAXmVK|!MZO`JeDWXvon$<qR)H0oZ-P+x~Qn(W&WQ9`{GbDhz>p(yGnD25RXs@h`!i!!{b
+pdE*-`*pt#A=6;4*=~*TRjXUAn8Iq!ViFB@e41}rzXrwH`UCi=5YFfX!9w<d8>?912hOXXwf7qp!PAm
+xv}52h)(fqDf(6~O<tXw_<Q3tO4s<q>e+TUxwq9OImZxwyBXEIq+5zR+q*=?CoVggalZp<2TSf>B2@{
+;u%YF{f?PvX;26T++~DQNTtIk<*LdTtigP@7w|7=0UjjvYz%h$0f^OZU5c;1>^2wA7`oIdS$BYEKu7J
+7IurQCoJ`2*=PHkb0djYG0j2>tm5)X$FDqwT@LQ@!@%@S(&`RJKLM<YdReSW(Oj_lrg*u^wYp{oq%e7
+(#D$lQhWct2rtl@TF3lY22fr$EZ{9Pz|LF<29mEn_GV-0hl5T5)ssRGw@y9{5x!6SmJg=wZ!Y6a?k}x
+M!x4V=gyjN4$nI5Ms-1HyxC`vEZQt49Pt1AwvWa2oXVmX;Cox#n#5NxWV7KWL-^5mL`R{PfliV7xOd{
+LgkZMq4sHVb+weLEK_*-O5yzieuelkcXx=q!4Uo$J1#q;9{%vrcP3{!5-komMx22sI>9o-WYEJO91g~
+yDzvMg=eT0Jr)pro0FuJLP>9N}p`EJX#|d8i2!+fCn$`~yf9_bz%)PsHKrCl7xa5$6ZF7kE+nGSEsap
+iIlSRA$W|;qlPOcc0<=)iyxd)s$t1FT&E$cVwYGotX-(82<#M{G#tj*0!7WwRkNoI5@NCS!4&N$sHbH
+w~~i-RWK;)F?a>JB~v`SHNC6H4(7w)(TSBO{gY_ae}fLKYD6A)h$SfK59o0Jy7(GE+46xvtiDR#(_$J
+pgR~3)!$eJ#fJN`I3u0122aqg9ThVU%mqI-@s|R698ZntG$e|rN=-1FL3aP!1Wx;o4Jff$v1-xW-NEM
+n1j){vL^&7$gNpNfBv@MW#3=^&()}fo_MIHZq50tA$JOs12DH)*ao{))TrHb(rx->|F;m(^(tM#ib-K
+Jt?=75S>bgpHq6NA;{Q-f0|XQR000O8^OY7yRWz-g0}ucJZ8-n{5dZ)HaA|NaV{K$_aCB*JZgVbhdCe
+RBZyUGucl{N_1;a^&bm=w}!;E^EYr9FXG%;eQE0zWa>O@JLPj}bjogHo1|GxJ@-jOF+a^e<ffYuWE`1
+rp09{ChS(a5Z2trnuJ8eJAf$YLQ@b=g*CG`d+UQPsN0bd@WyR<#n^h^7<^wbX^`)|5r2refYU;;W);z
+7o1HjVu~18zq{x+Kg6tIhT39O~hMRRx4@BLd=yeRxmG{4$-}C)<S0Z<usO6rHVx?GFiZXb-fjUwNazF
+YVMUPL?vsZOd={>+0SC73RTOz!`IyF3}`MIRTsGbl-M)tvdtGo+>9z|ka|X(h?^3K5*e)2rcp%$;Cb#
+52Va(Do|pGPH3(v%j9wMVXcU1Mmvy-jX}WBiwpM8>^rkB7M##A-^R`hbe;+a2vMsWvEOX<)fPN5H&a<
+!~@#7}n5&<tzDN}L%{_5)8<@I&?^78f7)Xt48<h`s55UR1;fMgO~EYv55X*?3}?`nJXYAU`j>y2#A%c
+7~vd@7ckCe4~WU4w*jm2Q9!nWySgmbVMlPZwo6*M+RNQ$pnpw??J+Yo+o@zj<C}l7(jpI6q6jd;jL*^
+<{c>cJp#7-d|t7OV7{FUtXpcuij0Csk5}I!MZ+;MkBB?v5~hbWeuccmg5!6fh1H<PKXRt0RDZaK>7tJ
+*_W9p=l@n&Ljbf`h}lel=GzU}eM355=&WJuiwn6?G1*CMH6M#xwKcS!cct}3ucs>-4Jh$R7N<TrW5zL
+MT0Zc8A15r%vbHP1XdQr~;v@4q#Gc5-!b)gt|4bNL&C>9JtyF6TR(WUj<&?*IwK$BGtX>LmY3Bn;r{t
+cNj4cS5bU;h8tkh0)fctD^Y=I`0uxzce+mzx*o~QFx=Zlnshoft1VeHq_H)WxEE4a``&LLJ2a)Z=Ys-
+77TM3q!q2qT_9L)a5A1OAJPPe3hl<0el8#y-{>YqyUnrD&@K#tVO4Koqa`7SQa}3DdMym20p);8?bKg
+CGOdnk29$y$m<NFpP|QS_q~)T`+Gb*_CS0(#FN4Ut|pgrexF9;{eB06jR!RZ3)*R-VYHg#X?0>5wU*!
+5vM;l^wBXPi|u$L!H1ccDWjcCo@Pzfa640SATAmv_+i`)<G+{<1rH!J39v^|E}hjZT7^o3lgmxaX2kW
+($u|y>JqG}mLI;6ERt#<|iKlqbWVgAxQ~Bwi(B_}@|3=R`i+L7|kJc)$Qjmx}B$yMnO^(9xYz?^M*$g
+4$snxk@PGfJt9jo3Demsq@*KnZW$0P=lG(-k4?nSyO7wXWa5in?BBX7$nKoA9cN2V#^aBS*UbxX(tPQ
+RDAQ65pfwxV}!K}Wo%xbCnE#10;{D#{wQsERvXm&FE7ZE+`SjZuJ;nHHTl>hXb@EcL3b>0nBDVlo{8>
+$Uo&)j%I$!ov?x9^(I*K+4J@-=bxMt~JO@iheO<L{jJ0=@YA{ncZa8!?EhRaT@Vd*iqF_I92xIj1P-5
+NE(3CJ5|q1b5OnU%o1i{1De7yM^09@MKhq}Yv0md=sN|9gScC^P1QDu-4VkXoMO6xc}~$V=bMxJfdVV
+^#y^WjL##;R5z}vQ-9h?)?C3|>A^qOigW~l3!|jN~sZY8gUjfCa<}eYzFcAb9kl>Geo&dqHjT0bJk(a
+9@ehLs6mg&Q6sdMGN!FkA&p8xjWH*hY1FP%n}Y}S!SM|1@Ps1zxjC2_Kjo(Su~tC%jxAK|3SbdyrdGl
+%`@pS5nlX)*31Pj-nzp}^5>REF8u`4h6_KxVuokn<iYel0J8qemB@g~ME3fSo}dU~Vb9qXmxkqMXt9f
+MAF9*P^nj%TL?GMmi&yfC_)4*;Wb(s0Ta8zMDkXmCE#T3pT+!LcB&@;qfg#U=TjRYU_US_m4E6z{njA
+LRpYR6n&dotAi#&VUnN*5z-%HYW0pIed81RgZ!j7?M4*sW{$ZXro_CxlGz%wkV28*ZLugIK~kHl@eUF
+ZjV)^N2ls(O)P&f<kL6+80Cp@C2MLR%9}K)OHYy9)4#Al1WnRi5<A3xykFOzOp<Yax-72OS$=u}ywkw
+MTaDy?%4yJe65g*3H*I{rm)8%w#3sv=o+(1FFrz3hQRI*A$Z0?HArJWpLvw&fbh|jUip+lHkU8VL^#e
+BiQmlf2=2l+Y^48-LkolEc$Q3)TFV*w{-{=|%y2kbmR$5U9N8VZgl1IRdVjBRdhQ+;Nt&#s=i6Btroy
+ivjHm%LVTv1K$JAn?R^XN=cU(Q^~*>di7A*}Bx3@`ic9oPLN68o$Q>-Q)k}@kbvuGyTCE8(a_d6P=^%
+(7--_Va<}KnPa(!XDHviba8AZ_qxf}Q!&;P%r+rI8Q)KIg@5N0NavxnM28<`sy^oK2ol(UH}H_c2UjL
+xOhD>`DBPXQIo)|RN}QjGd5d>CL}dnrX91PdBWNYaJtX%Sj!=%(<74T)zJ4tjvp~#xsWTuRl0ji0sep
+<e(-w*(QWt!0sDfZz)%p(Ey;WOE(s16xgvZg<<qv|ZNfHI!jHPXz9~Ok>;7rgnfab5&w$l8~6HSM47`
+orTdp(d1-oYhCZ>n730zX23Cn2AL6?iILZJRaN>J^xoTq*I2F7tL`HOFY{{7o&>j*uhnPhJfReC~GqI
+zVXlIb6@)K?mLE&CK#8Zh|};WV*84(x!@UTnuTDiv$n|40$R^_~<|xQ1-Mq9L-(kwHO3sxAsdjulN<k
+hmd|TrV=qP%Uil_Xn^n~%*=9K+#2Q?FA5<3cxpt5lpv03aLESQ<J?hUkL*je)M+}j5O(2LOYl;3BRJP
+97k&z{ZZ<i2?G$~{Uf0v%&zu94=fC8`vd-2};h01aPO48BC9FbJC(obLPWWgLL(M1_aCT;(pY3#ri}p
+mR)0Pgi%SyX-K#XZXF&x5C^b#-lp=97UgCpXHDvID|4n7(PKMK)BI|_(c0ny_W5M7;}|Kse%Wi(X6nS
+GW}!rnU3^#*#A?CcSWpMvfx8Z5XSNno5q*{w^{-|C=Tm<hbnbE}S2oGcW){2FlX3&qFxr58nq7|(OZy
+5VOgyz%we?$~syhy8FQ#fjsJx6m_$-ch@c$~;EvorYh&y}o&K_QT~7i*R*JeanwgxDiyDj)y608%~dQ
+wMLCEN{|BHKCk7S5_dA!iyghAqZC{5^5*91I`P#1OuSQ@5|+s&2v7Va`5NPFXxT>$0s`({NBJy!SfcW
+e7)1C!HVVjhb_tfEvbOg5={>(Kj9jY6`h8nKl><2j+EG91Hh3T!_=|ow@qoR-Pkrr;YbPwcOtqBrBa7
+VjMsmnTSfr1&kb&@q7BZNAqJ@lhN&jQ=4PKf+bz;(mLS?8T+wuW=ga^B9)-U5DIbh`>++Q35FAGvfL$
+CFO8#4`1b-x3jxrrC+@Rb{qE)8|((G(-&6r*3VlVW(-o}6RgK|f3~;&ZowyHzld6S_KJrO}c-8Yo#vy
+l0yD24kdsTJ<lNH$Vn;H`v?dN%un9*X2j$QwLZ0xMoK{KEB^ZLa*4*1wFp^Qt>x_x>(%3%~6pk7Kwx3
+Ei2pw(36FM`Qh_DdFi;52;E3x3jk(+Zew~tf|@=7fzbOf_hW=Fd%#Jwz&qozBKT;+;_Y%%i`V6-Qv7&
++EvO=ybdk~2*^z1lAXr?cZ0k(<pJ$M%4!?8e8r~(<SxS&$-PsR05%vuhe&KS_rNi#A8Xp*XSy?_YeQr
+u;K(T+<hi8TN_Js#Z^esMFk!=3<9QKX&F2wAd-%j00Wq2{f_!_SRfCsxg=3xEPPd}YtXakZZWjZxSZy
+@2XaO!lu1xJJqa5I7n1lg^AY!P?^LkH83qXhf?5XZ~bFCm*w#lv)u<BO8g!|$P3q@y0K1WrRHtE->r*
+32mS4$;?@=+rLbxlZhrxLp%aA}%gLIOk_Kmlw$VQr>}0^ZPn*#?ScVIWF9!E7hc71Hc$k*B0-MIcoK-
+A1@T-zaj*-tv`wDqZl+igs2h}Ww*2KgX-H07>IyVIA^AURu3AH=mHT1bJ}gf^U2=5aLK{Ot3IUK-P{I
+A--y+Z;Z1`b9rEE#{oXk7NZZGs<D}Qb(bT*%CdkP-=L*dNhFzA09YJ%*hrI;a4V~`Tz?82#AvLS_<mW
+*{-5Ac4@nkCK&t&H+ntcs#wHR|(s3zi^kKwLKC_IY!d3;#7(W4d4;|-Z{&!n9SN#cn(%Q96#5LX&zYj
+Fa6p6t7ZS=c5jJUa*EO~pxpeMh(q-@~^2mZX{~9)BhFgw{LwHvXj;Hxf~+T~=;3P_epxPV5v-#G8_uM
+xk0a<wot`>I!<S<(pb(w|r)mn+h+8Q8*j9h0vmN9JfabhppW!qCWZ2h7+IC($QT9`Hpkc@DTV{HNC($
+etqNPoZ@$Dd;{`+d*i!u)`cLL0S&QPcK{;6_U5hM7`)Ho1{OBNwrg=2$Wmb@ccjoHILrlu%CcKMs2B%
+bwn2^<_2Bj$A5#W?K0$P1AsdOO^|zRaac2hvM4mfZ(LavPhiiCivDPL!qHH0%DDR8Bl#Aok;H$C)W}3
+o><p9rvuUHQ?fk0|^tZe~Afl!>u^B1nnhrJBc(5BKo^Pl}35pm~<Gf3wHK58;LZs<H>bMb7>#1pjlu`
+f`%uY2l~ZjXm6ls%P5TOiT>B`#JU6oLpHeC3j0&~AD}6=7HA;QcN@G!T4HmUaw;u=YZRp0i}VYPsy@r
+hzV~+6dWoB|8oI&zTgSfYULC>0IBcoqp2OpQC=rWVMFqdiuOI^>eCCpZnM#Z2cTf67e38bs>F-&~I3#
+A4YVLJ}M7r$N)XtY|EC;X1<+~4y*lXM}Vq-guxnd$}*~zd+&2UouFB=)Uea|h@OLn+(6ACnkQ=6)~=p
+d4yig&G38fk>B6lu1FL%C7$a@2<dIHCinIv!#8AOowUzZRrymz6?9rT7x=QUWT*{XPhtC1KZU*aIH|C
+7i1&6|e*uVH*l$okjt;>bZ=*fr4#m9I48B?R`4gQh|PHRd_q$pJ1>>1eolDm*0^Spzjp3Wn@yKrX*82
+^TKPL|$3F_Y+?i3_j(x7W?s$Xt(PelNEsZHxct>ad#s#c`Gw%xFPi*FL64wYKhiEAji$`FCJJJ32Ta|
+Hi8OY0H0^I{F#QiHKj=pJ2TWJf?G?6Y(KPN7Ft#4I0P$;oYw6e$IBE1_P#(82+O++x}}_u7WSa*3f>^
+wPE%n)VAo;MGb@ffNiDQ$<47}tsi`#>#GL{8mT<&-h-l7FsR!9*0<*PHHA&K0^VrUBlRKYV~~%d|G9p
+d2h<*Rs60aXT}O$}dC+Py(8a<RQs2_xT^|SUk;8HSaWTMrco{!k?O8CKc<5E97vTQ^P)h>@6aWAK2mt
+ey7DocJ&cxFq006*z000jF003}la4%$UcW!KNVPr0FdF?&@bK5qSzx%JiD&ry5%8b)?Z)V-`?xktE^<
+A1|;`C$3^)a*vNvtVSOOUqJH21gP`v5=yq-3W%H#@U;noc8;z{A7C!}|ujXzH~{(?!>IjZ9NftT%Pji
+tI|&W!K7-e;*v^=ksM@zRGo0lu6eXr819JnOc>_mHD}p?Iu(9?JugX%=fZhE{keuey)qPY_}U}+t&8m
+rp($!-K@>;Mw)MGg@fB)Tid0T>&>Dl<-tMQY)=jZd`u%%3h_u(_1`mk{O!~8^zDmtaVp+6ojj0tbGd2
+788v?1G<9<lH2Wqi6~J8})i%W@NpED8*G-~|yJTJ8NR@O=2_!HBZn8#6ktxBCQ~Lez?v4C=CspeJ&`<
+1w02Buuo<LDo%0M`)RGXDRLwHCtnu%{=mA)&~I&0^vzU#WqWof&;-o8HT-_atQSFzfZMJw;xnP}E+Bj
+rrs`ebdYieKSJUO<Cv3sY@ZGa}!}=PH|DcbhaX0G~`<Q{z<L6{=M;(N!CGo#GPi1Tcq5rK-EyH1%96=
+=Ai>vv1FSc%DB0>Fx8E=Vz~8o=<}bBnS}L1lYsOWxkSgJ$QXv$tLWf+0-;Is$5oW3Qxm^U18dM4S>oz
+%U?J3vH{G#%3zA&!}Cp1dBc_UJoDD=wgDQaup|=xS!d0)Y=|Izmz%k53||L46|fLc2$*chdFFMJ<=Li
+{jfMGDrsPw81}yXT?Q8vGABe79neI>FJA&Z8@U58RqdmQS27Dvc2FNk8S8NB>Hr*V>mhJQm1_Xwel`7
+ieMt)t)+dch}$+BhNn|Ijf!yfYt`J}GerY`%T;Mp^Nwv#e$NB&_4`v%sMU<`W!FI?kNo2D<|H*MWLa>
+KN}TbIpdp3chx81>-bAWd<fq#(I2A`r*KLMdVduOn}BMTJU%0?|xF@K4&-v@P%_Z4dZOZ^JHGGhYFz2
+{85Ly$c+fmPK_PbZ~aI84O@x<NjCwa;)pBZ8sKAeuTd+VXnAYWdl>yyEcIdzo2JvnpW8w)Y}xA<#Hi9
+1=|VMzG~wsi}=Z8LjS&zz)_XR1aKGb{6=no!Jr}jXjhWD3hJ789rLWsVC?r6=}Ez&S2mLq@qU5`z?TU
+u-()5xH?Y-Uzrzz+&s6_T(+~Z$C!1}%;x<|dCj7*-j;Bsz;DA!D0WT6HIHo5%4FVx3zncqDWAfpEHRy
+t-coB|sdBKn`#p$Vtp1mfh(TRn!tCJxB^xHi>f+EX@jSV_$>kJH?K-dGw&DK)4s1`K{fKq~>DCLbTsp
+F!kveM~^pFuZ3a}?ag$>*20CkQb4JK=m3!6wNh`K2zZ*c<Btcs9P9qB<6Lpsa?2_n3YhY5yZ!)kor4j
+U8Gk{tD7m{S%h^tf{L3xBV~SWp)6qoM{ESyJ^*J(XQeN$hz)sGIiJ<Oqm83I|n5cgG>Vj;jdPit*`P-
++?|NKizk=9q4R68iS=Y1Spi>*d~!MK5v5^M8mqYKcmq2-csjLTL9@;OF>C;Wi;R!P)f}m{YjB_(Ht}}
+`Q;6WsiTwD*?ZOR$PPB~thC1=r`xhwBn%kn9Ohs1ZJ>u(3<9%^_8Hy}tkY0MfIu}0$;(H8MFEe{&$nc
+D({xFG>Ehwa{E8BR&O#uA+57VC2haiT)wMhF2=(>kLfCV!g0cN)<s=){slli9ef!LM|q+x|Rc~8x)PQ
+1Lp`CWc^Jb122d-*}UN00HtBw_a=_69!1|Mkp<El=@(N#3nDDyFwWN$|I_wr<pEJelFTolK_i7%ZjqT
+5i=TdVQK@(KMKs;I4}AfLz7bHMqBxwOeCj&%*p!-Gdd8f=n&iG>t)-EMPl;b+$n}fx-5K!q<s?i?lM|
+gC;QgZQbMwS?+^74tYCf957%V;N(%}(8KjG(T$`pF*L>>dNJ_xNc>f{WXe>s1zS_#2B|x6GOH~(^;a^
+_0p7fk=<5{V@7kWa{iz-^;onI^4q-g`cOdYS>BZ5LOLwqyJs2P%>IHo@UTs_3H!coiL+fdbOcRYjoG9
+aTaq{F+d@5o_Z8nAP`o(7_mmU$mN533KQ@|end;zHp955+X*^MM++O7(vXKx{(9f&Lp>#mm#HM_TrXn
+-0_MEtU@pfUb`>LK#{u5Ge;>tSG*0hG8Q5$r~TQw2aY;;z6nC<(w8$MHqyK7@fnsWX1;EwF>FI|$Il0
+P+WdHAmE}wM?x^?CR!TYoBBnJR}N~c!;oriD51Pi{o73Miu0MA}jHVnOKS22c7|J&nJ!DDNQC0edzZT
+_mH<@Pumt`9QX*Uy1qs(Ucv0ZU;E<t3BYH+)o0yCE<u}s&w)MSiD@*M@$|WW3jLjNwBUs9y(H)xT3Mh
+rGx5nM*SA@-R8!Xw`Vr<+m9U*MSc~imcbA9Y4A~#(1QH3&R%EW*tfF1{OXpDSkq|@?plMTg%N5ZZ7`D
+qw3(RE@e1@vG?G$*LxfFjm#eq+PWzdsTn*$$b#I~w20tplaTv%52u9vEXA~VjwV2Lus1A+LG_W>x}D@
+|c;p*VdVV2BbU195SC5BJ-*r=pMl71gE#S^B*Jbg01U5Qiezkv+b4E9+=$qW_I2ug&fh6A#~hckaXy2
+@fv_0}c(_IMT<-f?#)hv$%hgqcvZt4hGO63qn#`DKvw+DVFHHVS`G}(E>)?L0#6jqV5`;qQ}XO?rCJF
+p(vU|B@<`!!9wdn{fA9<YwRQpq*(8O2Y8z2X4u`7b#$OBC4ProLVOsIzrrXM4I1G{@GQliFr1mCrW}}
+yRgTimktl<12}o98ply<EHr!lohEmJ4?nT#7$*=^|9PRn-U`n9o<5xp1rrqdq^W*r^Ymd-Q2%72X!*g
+&p4H6CTgaD4o4AYX;c{ul8(OZ-9MGY3*H|IJ~7(205*PbNuTN@7-ZH8eenXOM5W?QT!$Z`D(4CP|6)j
+#I4(a-(qf$+O4q@q`z)!44I>~W%fu^u)Mahh99NH(7OJHnOqr{c5YWB)aqZ4TrfuuadH4}wDLw^jr-H
+fZ)E>J-hZbBc&!Vg#Oqf_;G07sp==o4`(nSI~V=_-0+_#iAgM;N-isM*>cUj0t5JE?wj5%#b&h>y}*r
+50sWBDdxEVoK1NpOAJ)d<9@!t#<a^Z&+-^tD{x(h6IjVC$B6yuSl~N;Kj8DpGe+U)EsO=OtBcVv(PCS
+_kkztXL3=w@O7sjnyvS60^qr;{v^T`_n|=taIA<+tkSLZ0;H@=RlW_l`z$$Acjr2lsOb<p3Cs+JX8`@
+mnE>Ozu(erE}ZB#zC--7t?Q$eA5us6|Zzy{)WRe&`J_ND>|Msvy=S-~60_DKt}6#SsPXh70}@D%H8OW
+@E*xk7UalUN0sJh1AR`$6-k4>0d?IX^)eJ!=1{ZUchGn-d$G)5O9EdwYr(Gh;Xz;Hi$xg^W#{eh$b!z
+?f)2qOM_L*w+p$APr{Ma&fiot)zL^G^o+#ot$^%7h@n?oGnBFgsP<?+E7Z+0fk0tQI?#zBVeFc5KNC&
+2kNE7hKmBVV5M;<kOVGq;PG$;4>m;7Lk=HT7K6M{xJC{v?8QVtg<sZ7umXXwNT7a5ZJ36MM_SLsPGag
+yA<k?h8dyBsd2hXii4IkSd{lre9gruS>tqOxCZd;z(;eE&;@Pc0=w3-6g(g9sh}Ue9jlgmiN;d04DN1
+r8K$<s)1TqIfVu2Ar{!);Wr@9<e@V4%7JF)YEk0K?Gz7>lyTPBmCl=2DfzaQ~0TjK4We6S;45>M4;`Z
+PIC<?S8^#1$I^_zE{Nh6e82!^EDzZ+TrsEqnf1kIIbsv0oRps=G2LaRN(xRBt@43-B2Y5wPUkqo1{C-
+6LFZUIV#ex@+Yf+l=PSDDs&&dqrQSVJrb$!h-Y=Fb<gYTkw@iS<NN8_>wzwY-FAxomiIkbP3>@O$?36
+&lTz#_-Rf-%O#uYTvFhLs(|H-MF7e4xy=-gD=Xd%UMtoxX3^EZ=%TCtU-aFJ&NUqWlIyOg_Ww&CEWNf
+U(IN*uDxK<?*!k|de(1iX?9gdWLAQK{&Y5xa*vE6|g)I{52w0;R5IuYT=56}&)ywBm&?rl>ow8a;Blx
+=5oJ&&4v8{^H4-9hJ<s9g*2OFZmMGZKh0Dxa5n|gy0P<jd;_HoZH%-McJdN$E<^Pa^uWz$uy+7Pz?68
+c`ak$-0Ae&!!#73;_^ngkS~dx~`+yFxk~+GQW@f+BiH)Sl91ZUxGY5ikaX`+zIMbnLe=qbWw;AB_Uwr
+{ftc-@I=mIIlE}E_+Jez>;hQCE{?qu~$Q;E=SWkg+tF_3)O4z{~TZ|cuT1Xih}E<O-4At8!8UAmq`@0
+b(@u<>eg3m9-8iCCT3{R0!g|uYwTu)tsTHZ@#$|&f_kc9%|bixsrcjZ=PX<V6<J~ZqX56Sn3D`z%dAq
+p5A+Q<y72Vq&*BxuA45UW$0C<?OX95QV8>HBN7ZW#w1b`N4hk=T7i5!_0W6StJsJR56~ImaDFb;8I2G
+hV5eQYoB1EuU%UWc}b|~BeXbrD%>(fTZN)~l9mnAqzrx$?O<&a1@4y)D~vc$RuKMR)!D>>m`_#)d|^a
+>Uk2<+U(i^YIEb(1q2ZyNNHQQ586vM~i#urY3md9Zvq3Jj7iGSq0N$H`}ZbSe#k6oN@(q?rFmwOFxv4
+{!+u3kaa-LbmWS*KuT(VE4Rsz>(IulJl(N6bb8`o2)D_`r{=ZkQ$(5jQHuHJK%Vn8R;FX?AdbxzR->Z
+Ds_|ns0&qBzH8}#Ln4Eg2mhzSbd0<)!h)E%?*)(+#hthCphD?g7_TSEsf$4=vNWi39QvOG=+qCO%-K)
+?vSF2PibaiXy50S(v%6I9%zlTu4IGPN5TP4j>}uN)$R4x20{LgOwA<16c8VUJoAt&Rxj5^?6AmO$D_l
+{mjReq(ly$q2(|%_n4;3vZh~OvIGM+qsEWUs90$gI{K)aQ=#}p-~v6aGRsLo7oI=aC{Z>~`iz!qCpHS
+HfPQYUHP0)ye4Wv>PV8#7b(6OMEZNq~4_(4$<Y3wgS;Ikyfm@sr|YTv4nIrw)okje^q+09+7apY9S0x
+CUr4S4U=1(G1kYpp^ldTj9l8Fl$_OmrJ=OS<d+Pw8h@ENJSM`C?iR+fPC+dKRmXTF4$+flR{{IX#}<P
+wU+Iw&b76;&&7C3njG9Up0&`$LDtHvsq!_jEVOOq))MLVCV#DedL;k4P#Zj9;HQhjq?hAi3{5gK$-UF
+l6}@3`b4-orAXGuIfiTo*5<NFJ9Wt3r{wiDTK<G3hN^)YpFLA$Obcq}-e8Qn=;yc{9GZ5(nh8P>4B&L
+X1iP)6&&kW=#yw!1aRP9|=V1^<w(PeVo2*aVz)?@{+E{5*Oaq^|`WHA4!plBN`^An>ca&axUlt2W^by
+x;@A|gFr)Q8;kGSc}F5D%#WLB5tC4uA!)+&iZ1kVcnUIbmmNi|Gw~o-CWX+r&?%39ub*N>}^DrY%+Am
+NFux*ss{Ye8Eru+O6)3koeIw@FkgGg99)_^%cEg`XEIV$M+AhdSn5X7MQ{cR~4Sj`ba;nYZ(my9g4U|
+koeUvf(&OIlnQ5LyhAUYfs9TfN@+g19BYUFz!uq**<8la&p%^D{Nu>am)JGLB%cDGZ<Xc^mg~b}(mTq
+w_<e?p8Vu|&t@u;`o>XTA_-Mp`+lRPcT2cMIt6Mpa2ZMCh8RoYWEqH0LD6t-P6A#G2sYq<H$Dnv5zB&
+8pyXPmM%8__vuLn96KRPL@w-3cV?Y?l^B*cbjT&HYj$3c`C`J%W>UjOa$otbHqN9B5-1e1bZ)8Qm`cX
+o>!ypRut)F}gp^FXs=;lnh-@l-5;@3P#~YwJUEuf{X7588#%Gm2(WVgfKQ0;}JUCjxUChnNnJ0enwp{
+$}#>Ec1DS2M4|6qHG~deZ2u#aTEPlj1uEFQog?-{3&iVXmU0^7}+af_s5^a7unITm(!nrv)xz?@gbU+
+a=2E#7<JHs`HRa<r8k&-FYEdmgh_FY$_0dsqXuwKRpdNUQ!k!^Q{{t*_`r%>Y!^t5<4aHS8hy(>0Q3b
+*PLLc#3jAUY@+{yvmQvo#RYGU&sFN}WXq=(}WEVe43L5T4;wjQzPf}G5?@q62rj+EfSKdaaei7I}J)c
+beR(BM|qpCKV2t!BX5S{GW>SxfrNV~&kiltM1F+*y~O=g723?HN;#a>LrzTYFa_!3s?<ulzMJXkD(p(
+kpkOV^0W%{qoPEVhOgBsR!wAh510r53(~bZRC(Gg)z6PpT!Ix2z=BczK*7TP#_3tq%=IOJi3GER?sDS
+8a9p=L%hoT-r0iq^FS-1dGIX&do74qx|*NGpg~p9jrb0DQjXH^8kR-Z^DwNGhXLQ$6YjE%t8to>U?G=
+9Lj}$f4V2uMmH**1;({|hi~7ZzR$eS3;Jb3gCV+E`wav$>I8j2mZ5_d^fn;?#%8elhDTNh+nkOi54OG
+DgZ@#w82>jD#^ltq!nYVaK`sS50L?`3fb!INP93LV>x98u6V^z$Js@~Q^n%QYFiDgj`lsUt(TR}iI_X
+7`8CJ^~U-kI2a0g9BlrB-q>C&&gK56KptD9fdbs6uOn<L=O0mTt01|zfJV=~RqkxKyZ$b6jZ<H7W6CL
+FkU490z#<i%3z3LGq~)hF(gO5QR#@BtlmoVrbWYU)ygc~f9uxeUArJQ^;zpP(Uh#lAi&D-LrJAF3$@K
+rnK(wjQ3Jv7gAhjhv%gKzvx)9jZSB>G+*9V-oQH`x&_bKfBmuUAQOO<LFyE<XS%?p0slyL!#&T0f|@g
+j&T7W>JO(q5+6mxkZ@`j;e1LBiNuuHiJ?QJ2hztkyuQkEM2-d@QYql6nL{=n$B_xt3LPJotX%TMhxlE
+N_H0gXT4FCdyS8ThLsoWDClakkJRL01Xu-_n`$`Av0`?5%KN;{Juf-UZ`@&Dw<Gn5Iks)fXBDSnIj@`
+XNhZ6;TU~aFv>Kcvh4O`n^9RKOx1|AaE5<$bE`F{nyuJ`P>ZbS8p)Z5dRUNf`88+~&@T2wg(nZbew9o
+Jx#Zdb@jgEP$xBnuuzD4$<o9yjHPY>yv|@rNz9HTr{|Q@NG5R6R8YS?!NyIfw0fuKURHtOs-4pzD4P#
+E&*ny?IiyD=n#R6Q&5ZaqJe1UPPMIN8BX3+`YU0Q*QU{vtXcO#)U%vxhXOS_Rm^y+jyxbBq2;zm&}>|
+w4o*>^mx8lU<U0*zQ9x3Ac+5LPxYU@{N|Mta9Y*3mO1SOEi{|bq&OIr2lw$N^=APaPkbVt93LM~cZ0B
+Y8bQEa=|Xu>Ki7pH$MC?uViTDyDE-@w4XaH`B7^oeY1HYS9q~w<g9<9}5JjfXe3(S7*A8E>P%5gv_?8
+RQhfGh5V8<W7$Atp(7?95;hCxm2@|314R$y(rsW&Gt=L4b8zD60HnKcqk9|MyDON+0zEjWb$16XSZ5S
+uZ>{39@-DaVoM_R}&H<N<(Y-vPm5&{v!wBXIQwhnLahQ{8$805ey&sdd4$L1D0w!`P^v2l^x6!C~4GH
+2{hhf<bO`J)d`tjzR1I!1EVbo}=Gmiol;&utFG3W*MSmp*uk2<P}q<6>9@}s$%U7%K1gH?7)>8DlP7R
+B+fDF#(fl{52P&SP8~AekV3{(wgZf?)GACei4(5~uaxM_TS!^yp5zGOaIY3sjgae2yB*NwAD_wY!Aso
+CTW9Ha&0-G-OfB198D;XrDV&Jquf=933|{yL&Xe-mdZPB2A=Yo;=p%7`GYFyAX4fX$LR5;Br2*9)-@6
+BdNhRow3*#;`5L)<Dahb`^p5s*6JNq55zS`PM8y_Ght)OKco>i^EAlH>0x4RHpffY$*_+_y!Dl9nwwE
+*0_b|jp_JBD(S-pMGKG4VPQNZ%H+%vorMrq9!sZ<rV}fAB;hP&R9FTCyXGXVzE0fCBf?*=6bC$5^}08
+9QHr4SMXie(^K3fWujGh-zTkVx(mFv^P^w@0vi~Db)%kVVsXOv{lYTN>9Z*Djb8U`T#sQP}y=#;cGpZ
+X9}EXoym10_{w6B!j`R_ku%7FRWoY7;(@ji+tplMhHR|8*ojWXI@i9%F8JxL>W!V$9=SAah{`&<J9UQ
+%MoG}o==pY#5^<*)%!()UJMh%^xW&%kyUk7yf$u3bs~612z(@T7XdgK3a(UG)<H&7DmJO#cBjdr_NS!
+t4MjX7}kR;!8Cg>k8i06A#TF4D+jqme3@#DIG30;5Zws^3^cVArXUQc&kZ?+u5=+pg!JU*Y9bK@G=%{
+AyPY}40?f>SKJhe=a0gW2ht#(XPcJb>rb*y;+j)9^CIAlmcRm>Y-sER;$_TuiZi2xb%CP79n>d^M%(8
+AT)x{bh8nb{s(ndPW82j&AXPSCOCm?vXqd&@9X?67_md%x)#G+ikSh^Nzm8f^^;-(d<5S{rwK)ZX}+;
+A8EM<qpJX;9AlV7;#236%Y86M<}na`(N_Qp=LL&$NOib4!9lb8*5{rM2KBx2n{QoB#wj)H=WqEKm)7-
+csXIP&_SYWrxbiPL2WvNf;Np5o5_4@}kR)+NhE>{KBlP?0a>DWd;j$dO+!m1q{xbL6ChO}QzsJ!L4@b
+8)7Mr8U<}c5%pvlY?kCo7QRdoC5fG(98_pT7z+NQar!+(wZ3&GNF)BbI$b9`=a&LwM4s@;9H)ONeYLF
+5}A^^u|-kouu0%%p+uXgtt7`%Y$Cc=m<?Urj#(R}38$OJho^w(AWNdx+I69JHT$z^cmB#F>S+YszcQ^
+>sQKsm}&nbw$amY#2Rp4kS|`Any$X4Nk*Qbku0exo$2-=7A*5i7zx}2VYk6LK~UR#h$S<$-R2h%wrE@
+eyOQS>cN{RGUOPuk!?Wt$K1yuuA=WTc46(oT00;24gy-Gt0+6$GZ0%Kbais<EUGK6ycFYJan&cUzh`#
+99$XD!*PdFO1#|*3gUNSO<T{){{Wf5jw5l%%n1RtvzBtl~!00d(wdTMDN5x#&i9NE8&CD)zOGb9OJ+8
+ni$nhsfi4dZqyWoJiV<#2L2#wS!c^GdrG@+a|8R{TMX>kiPh`TguyT<1#xcXtIFeLi8%IyWMobr$T+0
+t$oNED`?oH7+!89bt$32pW29aJrfX3gcScq%~+%xcHyF!M7>lCcDzO9U?~=vv@Dq+sL!9;HMZpBR?Px
+Bn$|lCN3ZUY49X(elxX>;I1G2o-^2&is?~hT~r1-J-meJef79-T^`S3{{_d!z=9v%tnZJw&Odb?>p1^
+UBdJ^t8QwfJuMww<Y3P=Ky!WN=1{OE;(4-6PMk~8ij8fZj6VJE$GN<Dye!(IMs8~Lm@m*h;+xlw+PW^
+U3mww3y5SLQ&Q_jqssjGgH%<V9{qX$F-^BTgS3lw+;q?x>?yjt<GM{eR=8F+EAV*%B916%epfsKv1nz
+Bg0GcAE?*eK5F40ha#DIL!nxoag1|@MjBFEbztAxU(AZ9Uh#|X6naW17juLIOrbyrw53gp;ikAYr~!S
+o0EKmP2=pZ;jY#=WX(pqBJtK2G#dfi~Er<Vr{MJ=tuzOr6@Kn4QPF;PIMVzEA@*^Or#jbG27=G}oGD_
+|9TCF@IVEhr^XdDFAF&6~D?aQ?_K%)5GS=-jkubJYrK$Yx2XU6ZkGzIRQQl{xgjYjHxa<cHp?>@6FFY
+W1k5Bna+gi1y69aXiBA^hCaJy715)?q{XLrfBw-CJ~%@C+i%oMH~7YS{7qNUr8s5*G|%vDemVlDEkWu
+`T-pC=3HOTK;ICZ7JxU;BZu3(|a!{ODwK?7o=BCxHuxBjDGVCQqKhfRcDR_*hLOt#TT{0ccoJ~-gJAp
+jZb-6TcdgMsBf$$F+_)=8+<~U)td)-6qKha-3c#8*Okr!j{uOKA$c`sC7v*imi<sG-h3J}}wo$&7o7d
+J(QP5To{OgA^jVgC%k@N7IT?OjR{bL-5}vB<2IcyelxkJ*8K62Ns~xJTo0G^)hid5xvTC^(~++FEi4i
+mu2ETk3F#Jpr-nY#N?ro0-Gw_F}vAs?N7`v%yuZh7>U9$1Yj}Sp7wN;r#&jUKQLrw^R%YBKmvP-be10
+Y0-&@=t4GFpy-3|I`k1(k3I=#Lwy#FZmssFKM2R|{%Dj=^}qIBz~MD>29vRKCvH1JQ_~-?*53k9d|aT
+vBiI0sol$NFbyD^&2|Q7{`<}kNRh0OdV1IqO{ZQ%j;ZmnmN6V#PjKremTmi@lt36RoB9`xiKP*}pqCi
+K^O*7`}U8fEmhuvCLXWD@yH}HpN=k&`V8`=x*2s5l7<v&e?spZh1CGSz%2{_2omA_guc+mpW2Gv!K-j
+*Pj0u8SKm!rM?@qJ)p&-yry4jlwI2YrK?;(2E~4B<!j&zLcd1b#$^du7gd=wTgre|)&uRRxZpd+O<pI
+wjOCM9K*4C<=FgJ}97Lhd>k39l~`7_l2VI(+g(jb{FT!Ss0M^x8$I_&|>O6^;UIk_^?VgTurC^b;lpN
+8Xi1&c!bn$F_Q-6O+~k;BhT3wdPk9X8wb;n3@7Q@T|ZNAPFOvP-0NmN9Wrt>(4$cg*QcE=hkH6lIqH<
+46m)5ByY*}023ImO5PeFU!*~RVQq6t!kgX4J>%>Mb>d3@R))W$kh0TAPZIyQ3>Bh%FX;<M^b|nF*kFD
+tPDz^ey;sKRh!-}l|ki8XLABC}r84QNFzhY~&GV5Mcch@9+bR||_aWxqAK^0izl~zMoedX0h%_gj&qH
+DYP)*n6w2=MIYkfuiQM;SHYd{*^_ptz+}bluRZXmstA_!d0QMOT(4%s9B`gF#W|oGwb>nnu1U34<)l0
+z+k*a4yTxC8Oc?8n5dk?%LK}WG=#2<WbSp6afUMr@tkr1l28F#am_<XbLFazX2c{HCdSD=DTC?<gP9@
+LdLnqn0#4MNCo-?K)}@HLQ5>c#k2vf*lpch`_jPJvhOE>oWnn`V-|DrA<!$my*qvaWBjBwzI(V;de6C
+@d#uqt*5}THBW!l!*C3Yb*6Wi7u{HbH00SL5N@0@dMn&1*XsW;_$07u6R(xy1X!W(o{Jn^`b<}uyG|F
+`vRsR`-E0`esSlU0PW$?_M9{bRB+C@%HzyOF-piV(k{L4LS^ugxKLVF(!KOX7aQ`G9vckk-v-Mf(@a(
+m;FxeTTf6Veb9Oeu8kO)s`4+!B?ra&CO-iO=}*+|A=?ir4U(Uz!=c6QZ{2$J~{m6N~V!CkXcmqx<j_@
+B0Sp&Sf1h@#I1A@ck4oK5v5&`?@MLdH!%}uMP-NF@YjG4f}U7+$$d*iU8Th!8u_HK^Ze`X1h)MfEk^x
+vR$$0NT028>&Tp_{lktO9k4L5r^`o@_5S(I`?5pU6xBCsdoCIs|7qk+QFp5J3NEw)fqBmB27}v_%(Jt
+h%$a#(tdK?B(O<{Vr>+#Ay(UM`ron8O%Aheuci(6GxazbZ^~y=UK?Y{Vsuhu>3?ro^{}Re!4k~^_q{!
+4*T?$tMd1JfmH<H@)Cch`U=>ZKhocI|;jfl%0QheC&CDKdo;mje#`zpim6TP-PbQJOspY0S^S+3K;!&
+_?zL?dliJY+ReT;@^EtDiBSVU2cQ{;*?_%*Q=J89W<~={Ei!P)h>@6aWAK2mtey7Dw4?P5ssZ008L(0
+00pH003}la4%(eV`Xr3X>V?GE^v9JludKfFc60C{uMjEv>cKjz>UxrIKX5&0~|9OYaQ<(OJ-LJ<liIt
+gDB93&fuHneV=!~<hri!e>OA(_D&%x83>*Dc<6i#Fktfy4^-E6)r#+7w`*gFLc1MsKX?g%d-XO3+D-3
+ORW%k>Vavn4h?m>n^jEJdNKayIPzeSefR=CBU{QW>8qiwI=M!RKoeA&Ak)S;Tz(iqp04~HO^t0pt!AA
+ES_>MIQ?_)my<imrH&fNZ0!r9^xIrsQSM-C87p@s&X^8xk*ZKgo*pb4SvDMbo>k1W)a3#I+z`B)#=(i
+TN&D(}Tf9G0qc5csBvqG|G?x$+BVg*JJBWk>5Wiw$Y`LP_A=8!Lq7G@FxoqRXXw=m>hR0phR<eq}Ha9
+48X^Ho4Cm2&{k;dc^R;zsv*8Ti^HS{$+b)2@x&1BczfMnn{&i@(9NMm*AQD+}`|Akxc2jxufhB{wjA4
+P!4hIX6djMI<k+tiEPas%ZsgUG<RuMCnMKdZbBa2KBLl#2{pmXsdRgf+I?j0F*Mz^MJcy?nthj)a6}t
+P!FYEQV1%qMZ*U4=^R*+gc_|(Cbj@Xs&qa|PZL(s0d4Dwy`3FN2_8P8;osc8x7f?$B1QY-O00;o{l@>
+>WU|OI>F#rJPx&QzS0001RX>c!TZe(S6E^v9pegAvgHj?-6{wrAdvXUB!iM`vqJ>R;$Y?@sCylI*zPT
+PB*tWQTvkj0Hesw5Rh`|WRk=Q9I<00_#?Cht@3T`H5n02mAg^9_T+VDP%!HF=@xq*CjwQH!K0lUMs!7
+n39}7V3kl2ZO=UvMM*pY_{AryGqSwNxs>ZRg+}bb-CU(Y9@al9qIS$^5!NlZp@$an_M8w%k^5#o4hP)
+1ER<_YSHYrYh_C3jjEcmT-WABy|+J0`>V3$H~B_2`>nE{wq>1v*k;YjytvJ(g1S>5NiVkfHl4j!#iFc
+IDw%G|_o_~J)q0&@n{I8hs#Zx>C-Qp6f8W1)rGD9|x^V#LH%=Gk<%T+GYUJB&v8YsCw*_ck))r5rqlB
+J|VmV3X*?grIvu#yw2~+z?Qk2yuTjzgKGhlO))Y~;7@k8T&0X`-S<H_r%-(CFlZ1(KG-#mMHdGW)`%S
+m$i<EvLcyngfSTj%XK=t;Uz+e*zdOrPo1YlYD-E{a$A_V+&vn!{?E80<ias)}YtZ`*?E!(44WvXJ3^o
+7YX1U+?hgW!XG0cg13o{E_hZld5WZ`D9(GY_Y#6>Lw%7qK_BFdm8ZK=ao|H=lNPOMi|(SMZMbsxRS4v
+BWHw*^0ru)*}}B{-IL4Nn-_!`n!Z_63i=ZKi@)e)TXj<|R69>tI_<YxG-N(Ws|mlG(e$*%wrcyk)32K
+B#)7e3o#E{y_<L5ASv98)-Kb`^rDeOMdG1iBfwgKj>#v9gCx$Eu-uB%Mkz`%&s<{IA{6m+xx7qw%hN(
+;63${CpgMZBQ*De6lTx5PPFJC;Xs<JvdI-1RB>WHVDCvS((L9flzvS_MuJ)9)N<))d<oAqp!6^pf+ZH
+NJ7K;C@4TPPHG#hp~oK~~l9ir^h5Z?bo)NVq#sSM;wM*2$VkDJhppt>(nSlCsKaWOXvSRO*Z{)NIclJ
+-W`DbiIFc@=LY9{69~}N0-l@{`mUY?1$GE|8?>55*=hhI!<WmH6<~#NwO;IMiw12Kztqz0InWPlEIhq
+&v>Fg@Zallo~=<Y_VMKO-_pnU-=p8hKA)YPJUTfIKP`#QO>-~HLeU2x0t-)7)NQ>2Q=*m^{BAU3PBEK
+}sn~p-)peqm5U=j1gzS7lo585O{xk9G@!1i#v`|ah1o9$pX0uVP*2Iel6DToBG!aY^3=cr4QlcLeO?6
+4?j{fC;HY9fL6V*unckGuojqu>)r(a<9PVMRA$ZdM!wKVY|jol$u#Od@9oncgpcE2JS{2`93zdrt=`u
+imL>xum5l>RgRdXO%eq(>G?h>(N5n%HVy|5jPO>g^vsjw}+qdh2%L%4VgiZe^**TT?~Tk3ux1+2(qYC
+B!Gnv!qV`kd!`D^;w{v2Vm7Np9Ac92=?Z)^!Dox*w2F6uLG#XXX)%0L1#^A2k;~zA!=1FoCfkGi4U}2
+*Ez_&m|N&k@^>may`xS0VnedshPHgQI0`#2ZMMX!J7Wk}vnG$lw=4;8NH%@5%IjoF->@BV!8(<O_zV5
+tMrB0<v6j~1e3huI-p_Iw18oeAny-qSHmZbHDshwY9fTG7%`gneNpihUAZy^`7H<+7xm!HRwvfoGoRC
+4gP4SMpp}W}2J<;tMm(|iXB#V0DVUi!}YIzHpK~+VzJ|B%I`qi!=#*r^`wZO~1ir8Rn<>%SDRukt7Np
+&Rq6IxWQKX&0X)vkv0fqkn!$0ozPpM-~cMslHoP&ePpSbBj_1K@GGx-do}M&9iCr9(u7wx@(|!e!<YK
+-yB#8BuYz2>_iXPZCdzNQ7c&%jeB8O@6FJ%h46QeiHd%Nr{re@&b!UmJX6f$#q_2)xLd0@>~H}^={oj
+{0+hJ#0s^vRADH?{LYsKJD^zfGgwbqP9oPEQgc{r8g-C6@K?#0)FJ{$q<O+pUtZCeX;s~p)gsBNn;ll
+<Su*It8R~uv{cqzI?Z?R<clmnZP;Z)CENO%ma6*f#Y6vxXvr7I!v`t!Cwok5=10&;|d!0ad5#2L?uP`
+kc2~``FEU3%#rmXg9a#<4n?X5E7BKa7wfm=c#{Zb!t7H@ZiEa2YiKMNzEq)t`CAV_}CuT@3Fw%+qhs_
+MOTXUbjAZ&nR;9s^d%pJ^dbHytq7goDMp?H`eBv2RvDKH~vD7!z6Yk2GyNz-+HYBIL^}`;nOuy(aQ;5
+bYBMjTvuK!8V#04gWC=I5D0=-Ew_u!q*5#csuTtC%|Q$qChGb9T^A_;CKvhU8*bWIJ821lGufA*+)jp
+0|{9BhhHdUH-a~BS{2k2lJc)f%qQeav1Ucu_9~-U%|vUYDW#RmQu2b<`9`hE+dycQkG7p!HKVZ4KKG$
+Q!3&y4j~$H|$V6aytLob_xmCiKNpQ`UiV0izdv?86<HYcAA~rB$wcyoLf8+Pa-z;>ueH+237&C<xATP
+hX*QK9VIqjIJQ8PlhsBllcgW|uUQlYbEJ3@C4^sU{sbniTZxwETn^-w&PUOzF*kEfg#xaqdjF2Aiy`^
+gOdAS(ZM9v?;DECN5DK9lb5P5A1*^qb#gP;7bV)R9}_<ir;@d20q^bn>nR`EJL%#CLDryt?!omua0DP
+1$I_<`In)ofl=)`-m3s33E2`WC!pT|Aiu9fmUz}z+23hv;`=xMT5z2h&BjU#9u82_3EanG((<e1yg6W
+bCk?u`hNuqCFWjJ-)>zRrmbQl@-A&Vt;tE#{QE3lW7d516pBUwl)7`nlFz-mzblw7e)Z0KOR>{^A?8~
+xKMPSa>5=fKrbcS)rCLX_^a$5^-)&8cl}XFz1xrCIebT2G7^0toO(MC}64$L75*}G*fRgY|SwmU4B5G
+};(7#-O7d*uaUT(b0ym@g+lLp)`CYYom`T5D~mzXJ<|Lr;>sgfF|Inqjm1B;N86UwM50@4Z$S|ii8$+
+OOiGxCD9pr`W{F@sUCw@$(>IkKs<5e6jT+P`7s<{$lHgVamwIT59T@~w?wJz@h}qf*V7x<aja2HSiKj
+!k>&GQZhj4Y5FF%p{uKX+bw4)mlJ=w?~(kFTl8N6;Zwj{PjB7l#AUO!g}(GHV+ZQf$RgY?Al7>g}MmB
+b4{|xyH+F27K;v@YV9zbn`8Rl^Nauf{@EGznz~o7%H4VadL!BNW?f!0GlX<XUs>pu5=2Mqy$6LvFxq&
+C*fqu=v)9mefDNtK2uka%nu9w4!^(rvS$FTV^-je)i+y&2#i6^?RMY~^B?%QXHqwsrRHY)czwii)wW0
+%S-KQ$EzcD>UG^B4^hVp`YtifXcTV=I~gT~3hY#L-noZ&h~QiDo-A1sz6;u%XcS4#(7?KkEs@k{yi5|
+wpR>@`|=uC>hbpR^4$`CM!%t)-zZD|lJ-0vXv~6WTeqG_)BhSfZ)>TDSduDV{vD!t#RXg-y-%AQ{2^K
+nhYlW-JZBR!ADaZ!zsyC>haHUV*kinsqW_2oqEoN1NsEzAmH{0$<AC?iwaad}Z#3-#65!LR3m<FVbmz
+@QpKdyR`yWj4D=#@K3-E0sh?e{79it5wl*%^kxTA#qSZ7CT2@p^1RWge*5?eTyY5Z+#@(<hF#8x;TkQ
+8e)~;9TIyC|q<M8bMG|Aki*_>4j{NS|Oekx{0Sz|L{W^W1@@Q}-sG)y74HTcwhU23ULw+l8PqkpWBMK
+|THxS)n_~XJ)aYXr*jAySqMBN30iM*!O5~B{MY`d)DMVzJ$SlSBKE#39j!W7b;zQG()RnYGp-7!x~*`
+~=qmn0B~HxnSo@m5LxSRS4q^Bg2r3#!E<ARg*@uomw@`<)(yR%YQiT+TIu8_TtD%6Dq7x7~Tfr0p`O=
+et6zgF9_Uyio-kk3{|gG+5eNiHxHg9J?Px&l|RF3e(cv<o%N+MG{6T$iZr<D#$uHl-RcZg@}QW-`KPS
+e{!uw%$l9VFjgCf8a98gZnPeYK^$RGEfkif144?ph(Afi(#CFmQA29y^J%Jq3p5zt@=V;{sdi+~L>%1
+^F}id7+te6b%?4vXzAPJcmarPjf?%BsXqo`ExyBF3L?lt>^Oo(u2#rxmBF>Mz25PpLx3YqDf7b{i7Wt
+B}&NfVFQzC=~AXxTfjAbO`S}fhNbi=YgHBUl=(@T=G9NU6RP6i>xZUb+oB5~dG^?TBgrA@~JdY)Xoyn
+OTI#fxX(&VG9K8nGMxBB5=MAfMQGydt}hXmQSX+WgwbtdffTITl>ui(nk@a4vH_inOuRwL1GJDEii=5
+VaUZScLQ=4CRPx4uc1^k>ay9fI|0CBnJ9y!Y9(?qEFf%&_G1WmgSWWk_Wo8%NQ2$j3ZMp9o_b=fw40?
+#N>c>roSi@hh)YfvvZJc36tu%?my;t`lHb`pb_MAC^Pm;5BvLLC6+Z}chm^25W{FjEHCrAA18YKk$7J
+;vbtcFYUuwxMZ$6sW)%2Ai@R2|a26R`{*~DA7v&scl=ig!FAKw#Z~w0+>ns$tLOX(3_m_rQv!%b32sa
+cXF+C?nw=0OFU>Jyxh>q9zS%oCM`G_FRm*}Z;=h{Kr*=_ui6_n9hoxiCZ9m<_FUU*#JHOm=Ik@e_k?8
+R~Nt)h))1J?yiZ$Jv<Hr~846AnWvSy4+gotm$*BCj_V!X2gnnis2FIAkpSLkNav5||hZffG0*FVMPg4
+tM)an}Pue%dH>AgT2alVw+V}?rv77;J{jC)nZ;Q)S@R2Xac&sRix`>HJj&1EzdC`QmM<r*@cc>8$?+M
+K0Vi4V%+7aN0#2Su~{^TE_xWVx{gC${B)&Acc#@<D?OjeMk9%TTh$?J6<E+bzBu>$fp=K7otIQ_@MXk
+k1E)q_?36FuH`Tsd^f>uhC9pH1w~JCRwNRLaWC8r}&P|NGQr7q>-kAs}TBcl4T)CveLd`3!t>!fSkal
+Vzo)T^(6Ihw%+nN3u-!Js=Pp+b036cE<DmO^NK;fvm6p9`zgX5&Edh6Rt#{2sb1|_ziTL7t`cr@5G%i
+j;WHM(yK)!dfVyAW0Sb$rY|!SrLy!An>rL{I<N!kQm9M2S62eu8w(zVjYZbu72nB|B@G<t7>>P*}|?j
+T#z38G$9%<hd+V5WQaW*$o^sjSU&ByCmB#)|1F$J&7&Wlm5ku$1d0qt36nd6&MU1tvX|Nd5;NN?gA5Y
+Ep~iut(C3c0E!Ntmn;Vz6{g%B!@3W9l#taZYc#9%G=lC^M)y6)|I|#u{mW~yG>Cb*Yjo^l7}5mYlubh
+na7C(x)!$k|)7t<Kp4x>n69DpePnO%Pbymweo*;t9eSg&7r$%%7ZJ9&zv&yC2ys^q0kMXT*GG=Ql%j<
+Jqv^hu68*;QN56>?Y#1tgUm9#nEfkiSA)zmg&yTiaI#%?%PU7MB)54j=+Z&*vmxdH^FNJk((HHoWYXt
+!o!cYASxvVqix2lZNU55yzL@iUAzrxnOK#>2o0H2Y|QlP<K1IO1jlQ!#J1u%zqK#Bo%%(1CTgP@eYXj
+-zpEs4nx+v_0T%7__19;`QS&oBeIJ*HJyg=~Rz*%A<Tx4@0YWDCynrW7mG;cqLgBM$mif&2ntqUPv+r
+5BNIFYlbHaqcv<VajJk39QeuFjxbxX)@Ft*!k=Qb21e~loZSqXM9gs;_SKrPIVefwZ-GHmMZI0@<{U8
+4_Q_L3JX7`N9V&14+ni0&uy^_VgJ8g1ZH~Z*y^rnC1}~=daljOw6WwmI5BX-d5hF>HU+3$**~^5xVUv
+?qa1%p&07@Ymll>y3SP%?ELano!)@$-)O%fXf^hScOzz#It;A;v1u}+C26>Nl%LjY_8$^oU3YJfcE03
+M-oL45!zH({uH)^$)9oN;=i*o)hh#d<$RGyrw2XqU{(Dh+oNumQq<is6eb{820#OQs0_K1`@YjLXu;c
+HYB@3stdSc4ebRF4?DuX{o&MBI8PH$-76ja7A&C;OAw9h`jY46NSSzXW{<zLxK1?2E)rjmH@;+{_*4M
+7hr1aqw#8@aJ!;ucre>_1<!!?gW-_c5($9SJc{+|(AyU9qZ^gz@E+zccro&O)-hUfB`vWhhE!9MTrjy
+>j)8t0fa#LN9j;3}gXgr8guTOpn>?sw|6#ioyumx#z-oPfZ@0avvPGXKe6WW)Et7)1RLn_ku%wRo5|+
+oaPb298_NeBy_2{oC_jk69dBivg<~J6moZQDh8kseox$g3iS>yl*^EQrDCbTXgO+32k>2TO?q_YqO!e
+ZW%?DX9c45I<{ZzCIk>*5Q>(kf=A{*|+lGJ+6~6Bq;gcTku(c&l<G6J3{k;nr*i%9gmf?4fQLiWB>a#
+nTDbG4-ENgHAKpuCvPcP2A`^m<z==sDl`0UhlMN5Dfm70~^KH%yCjIa5@2nUT`+|xs@1PHW3v>JOl<{
+*ri5AorwRL><9uh@Y@G%x6qbr2NeBr2ON)>g7_aVB!DU2kvX<bGx+O{cA{Ymt~*)-v#9iY#tSke>M)+
+Et2zdl^>E`3p&ghX=Bi}D!>&Y<H=5;rkoUiuBnpIwNNmB51O7$K5MPwPpCMEavgCBiotgp`jbu6nx~D
+!}bHjxCbw1CruP#bn{L&RCXn79Kz>zY&ON2q0I6WFIe2Wl}V9S(PSfThusf5F5Mc>l|TY*MTvYjM9^p
+m}shXT$z6ErgO8gN!`mfhtL;koyL(XN9-0K6&=O^*-7;}`*|&k}EK@LmLy6SAT;%!Rt%-He70T77mT{
+t{7Xxoehdl}p;;&xY|0h@FMrOpOEH?MH!F{h3DXRSPu;x1V1^vvWKPEl85c%OXd7hTe6(GsP*wTV?0<
+x@1ICw`ZHI0g;)rzWU855?((ET6K?wGWO1C55SM8I2_hCXOLX3YMElYbr?<;?kPT*cescgU|CD_Dy#+
+K(e@8?cpw}KA5NFU19Tn+a83gDj<#Z~ZtLxdodYK#&lZ6cT>C`UD<5=;=Sh*R_kU@RV8{t^G{QuMUuj
+3KW^J?05)ktOd&4j>aXpr|=*)4;dhZi##mva=E@;^9M^Z(c=fpS3Nq6GRFJeBCJ{&-OXEei3ye11$XF
+L<#(nNt`I(QpJt2s<%{rV;d&t?iJVjb?lx*7)@*OwFSMz$|bbSE=k`jCR4Y4=kKNoM3aV#gnY-xu~Y+
+oz${4NHpzP()*_cr1i{TV9xqGq{hye0wgAk0Q|8(vHw=mD66=?5(ptR4zvS$W0zbS?j}qBR%vW9HJn{
+H_m{aNM^0Y(3^=-gncdMO|`dbG>tKOo|*AUAX>aU`>-XZR}0*>+UvGqIRYguQjz4EL?V^7vZ1j*_O_{
+ysM-qOXL}v-63c{w;s<h}L{%AA+w&D=PY<Lqrhx=;oILl$%229=Ax{oi!?<AL##@WL-)wN?3+>q|smQ
+P0H6~juC=G(S4RDj=y&UtE!Q8@rk>py<Ggw@$;X4A34?)Qx3@mF{7Z)6?LLJ;~S)vsa<-~ZJ+1XI57_
+d&dlvGbl)Gc%zh)camS6V1_8=MvaC&9<Ec5jJp+CwdZ6n3P*OF*EA*fL()6@oo(VxtksDAmVW>ZaW4l
+VWW0b-NC6;C&9yLwC|!c<i}El&}WF0Ufu+#GH9rRMY}{tTH}d%XYJI*Gu>o?>`vi$gcW99}To5cGYFO
+?jiRBqC)(PQmnYq2jZul*l`_o<o7BW?XO5&QAY<mPM*M@Jb|;23ULsXV2$G~6!B{oq%>W7;Z1lx#4*R
+i@-a*$y6pGLGh^}cC0<tJ9NFx8-*3hO7m;I+*<!&&U_-!c7u?t}Xh0}sH)#)A1W_r@qgG9KiXV4rBA&
+-3I@^XJ#x=2U{5JG1TDlXi88dVC|78$1Ye!Gi;rVO34*qzh;Jja)?i@3Z-5+A%f*wWi$x>~E#?p_p-#
+N=30s8jwRU7F_M0H1`E1tt*8lK^^Aa=RU4v>r<kTQ;(A@FJLw$rAZ^3IGg4CBzu#Mka=sved7{)BR$$
+oJ1M)M)4XMmh?yVx|3>WZm&6j~P##2;QDXSh|lQn{lR49ZHM<jp|Nw5^rH0h=})3IXJ?^-6JMJk6n(*
+Su{#Oe~Zcobp)Hk5o^M>FFn|Uq<KJpi0@*NI92aEiC$pvxxa<x;YQHo^uU9tRxe3cyy#tlS=|#tJ?E-
+>3IE`^(ZMAg!ae8gN$RMa^Vl`p|7+fQdc0hG%36|`B)bBhV&<VE2w2l83jtGm5d6U*NL+RtJ0gguf1|
+k*U+q!uPr=w7`5ZjV^^DNRigUtT@bZ}u(61OpmhVfQ{RynlJteQ*Nmv4;ttSsy<?CtmvusgLlwHx0ed
+GjXt`9a|JJIzYV~M!9_pUbDW^EJ1h9aD*mowdyr?>2?m+M4udUAFZjh7u1#NG#o%=AJPhty(sK4C$R$
+uh~;$;n4q*M6*>X#|}kV~&zg^Sju54^e$yl=kvo$#zAv*k)k7gUUS&j>7t>!!aB+K!QliuI756e%!X)
+%fs|}pKZw|E9UvV%#65C52xwIpM^aPJ?J*XK<rM|k2SHeGkqrsIcpu3dPCAX$Hz!$*^-Y<v#E0;MvC@
+uvknu0xrL;RrWN{ie2#n0P|SF{ddJ8x=M`-z6m)Gh1&)fE1fTbLv+vDLCebW<WUxXWigG$me#&lk$fm
+W0#;+3#a2(f@R~KoT(k_x$Vrs$A)1bCa9B`d+e>deKU$Pt1$pOOMa{H4!Joloh;sHI8Se_t`=dfTnBt
+4OZKm|*egwR}PwM-s6HF~0jf@ATBwK0CPAm33pKF@Wvww?>WqudEM@DDE-9;XPC<Hp$cwme^v^LMsGo
+gM*HG>+{~_2V_{5Qe4$v6z*HVY^5sJchf({1%s7JY1`-_-{i+#f4=KO5Nqea>@Sx=4wp~l)Xjbt(u7^
+2f};1B3<{xyL`KKE#pCk65jX~P9fB?*W;WKL=v5T`Vms!F*W+$!O(>3`sNXCI*J5{)JSJxa+aBwPK;M
+)ntNQww=1Mm@LKJJoSJ03Ua53jZby$hl;*mj(Z>Vv@9s+!O$lA2h$|XTe7(1YgI@G6d9MfFmp$l#Yx?
+gy>yy+2oylb!1WD@FM{ca0juVXJ8|&LMTbx9bg!LV*?@e3P3HCS7-Af$LfCO*tc>^&&kZ3DW7mOL+ws
+s()4(ItmG!{wdIxkzFmcX4O4BuDv#1o%XJeUz?lO}}!K7xze4O_AA1PGpkbKYu6eJK_{VJaltE^o9`@
+6Z$!W+Y6|82emyJQJwSY0C)a0U)8y_&F!>Q2Mf7GVY&wpJ8=VQ_i;5_c*J-Un<2HHz12eHJonuqqirg
+zrFfX`pw(N(|^DEX8bS1&Qa?_Ih|a_Zrp=v-6mubS3UB6z~_n!9g3;CvGEi*b}&io3y1gP2=zqsZne%
+gd6BWEx!AF6->CTJLZE82GQ$rJ5Sh3^BGEm?Wq<uyN?V$bvHX_1_hlC2Wk$W4EvxK?v)4wa8vXX?u~V
+WqF^nE|yt)=J3~C;7OU_hxLKB<`w{Zz>`flV4?$7z^23Iou#4@$Lw;NtG=#}fl16&B9@B6(pZ3hi5-g
+gio-yH-HgPO7YqZ6h&2!O7Y)!w=E4pju%Ru6?0U1}*yAUvr5L&vDOEd{be0No)qX8=(gx})gKqCHe}r
+%&HqWG3YV&7OOKhnrfqRmMFh;@#^2U-DvEvS1jL^b3AyY?%-3r-<uFL!7u(%oQ`Eh$4YB%#qcp2{oh=
+!R%0u&|$k6_6hu9OX!X!pV<91_wGS_yOnP3+Ds!hfKOcGXWC#$y|{0O2a-B`&=Nf#Vl)wu;wNjMfZ01
+@KFc6pmXfp_{)sJeDl{ewh73XX10%%&sCy4*lg-Qe!^z`;;ry&-y5kt8f62Fz6laHN!N5i8g^!|!!<d
+VvDZt}rt>J;h^VHzc=gDKKa1Mywy<^P<j>$@+Aw@>rGjcupQD5+_WSsFrZ<8<14`>uj3N}vmkScLcBE
++Kyds~&bK0)6E!*+oY)&lIJ_o2hXIJjpDp-<?)NYH4<9FSZqlMM=aKsZL|<L1tZwnM~lvrkr;9AV!g6
+tQTG=N9|jALZ(SpC!2#%n%WHY-ZaPYxuLY$e;Y27mM<ilNfxT%{@4&=|BV&Qa74ht63tkLgs!rR?@Dy
+TO?AJ*`hXC7$bm?C@W2VC^+Y>IeTDJ%}7F?LPImh(UuYW|FOA!!Q>ZXzO(xv))hp()NOXIyjV#lFCsX
+as7-8yBw}lbo>z#c6Jbf$@aFQtv~DPplfjfX@~lBB9Hx{(`_0_R6kWBw1lRus59?zlZ<U*S@ir&&yHj
+Ju6Rj?c_ah-NpP#Suc+dcz@j<oNL1UnH_g0B#B0AXHwv{xNEz)*XpWbY(+s={Baewm181F&$w)5!4Z3
+nV8ek^Cg)2~CDXJ8ls4h~MWCtMSLzaq~ynMouZ#hAF#keU7ix_y5}%N}OfO1*+h&*6ecb4#770<QQil
+0V6<ExdV@?`C>jeBf}R!#QE=t9L3n{kO-Dy@c8W*)s2v$-d~7EbcpDtt@8aCO=jb(^zx6k6vpm1*)M%
+rIFufIxonm73hQ3!oy4X(v9%xs)-*t^iSFSBSRNKjiXp$SG<qO&acr|Dlm(O{py@xvVHnEBkui-w{BJ
+*$tLP#GT|HyG;mXwv-7m6TrevEummE377jEJ?gBPm1Es#tBzqE3C(33R(ZBC!dG4L)k8~@Cb^6MM7o*
+&Jw!1U#m~cOO5+w>+uV#2V5d;ECy}^l#NoN*2^L$`l^;Hv*M6CkLrowa_bm^xobZcln97}YVNDf<eLo
+$(*2lkkdYqU*eO1n2nB5OCQ^)XKU>+Knr5<zJ+_H&)JEl~w2cT<(S?dYWIacYYq*VMjFZ3Dl(`Yd|BK
+BM69?7YxX0e7p>zKKl^m}vW&z1{Q7HMCiLhL*v2SaoNwu`y4f9nL!MTGLzi-ox+SeE;GVd~L0(Pn+oE
+jyd<j4qPP&$eo?gHG6woqIO98g4N8{uhhBc2N=E0rhj=n{r6dV^>93G|N7$LlwJ&BluR!SQy2^euXb{
+8i=8S7lh`#9`XQ-K^+k93k~`D(8K;ld$xxvsvJ9L&OK(zI$mR4jJ#ni!!;zZC8SmfpzzM0=B`ybQ%2Y
+V3bH^DqtaDZKaazvBY);6FqQvpu^zk~s*_FFmB0p-=(U#||^LI-6IX%^fhuZ7aO2R+h!+D4z^jGQ*2G
+b#<&`i`K!k&<y=#$QJ<uu~^vV2V(sJ)93|25pgol(Ok%qv+iTEq(YguhnW&|n#}nN(J3q|bKQbj;}1y
+vy;NcCxwx*OF*vW;*V2n#UcOZPB;wPss{08cv7fGvQ%6{KSXl?RO+@n_Dx1>;d+Xfz;#K_s;>%Ij6a{
++?&`I&`YKGx|`8(hS0U?&;a*DL489OV{U^})JV6iF!4H}d6q1)S`Nn|*(;8TGsCwyv-Yir$zVFL(}yp
+Pl56UAS&EN{w}=gF3k%A8>$n$lqAx2oe5rF!>1$jpQj1@&P>IBpWLR_h#4~;P@%4*C*M-83sgPBVgUe
+PbTM)kVZJOq&c#u*sNTX|wFStO9_J)NV4G?Wd$7JBRjfUOwM^m=zkI+RW&D~S!;S?8DvQ=1r^|v=Y4$
+k|@`G2zwe2w#)Je9a|sWsA--}+#$x`EJx`3m)#fka>cUUx%v#vn!B4t2HRl@|`VOmzp*C;8=nwi%Jl&
+FX2y0O11i<u}ujbMwRh%(<6{H7k(PGwllw?^F%$!?rhZ_^-{}oOdRaKO0y7xuG7H@`$tcUNLRj)ECe&
+<p=lA*ll@^&;z|Uze5UMo!DPeawYDau)cfn6YoQ{9k}OJsCVCcDoAF<!o{?63ksZKqv0`exf9&<;@T}
+FyBc`bPfsrsNrtV1!gSgfWA_Bl*=;2u^|jnJ?4*+B3<~)+eLt@e$cYL+&Ff34+ZIC(O1XoJRzzDsXW{
+O8=+S-6K3!%h#l;>-MVKe*cUOQse0Ub|?gZPTNCy~}&X{RqB~4E7xT8LLqNn4;0j0Qf6~7!uZ%3YQr(
+2M)cAYJtpJH%NUO)Zr;-_Z^U5dH~24rhC)Nl@1`dlsuPP48hZq0pv*zPCS3t54;?mvrzr{5Zf>mZy)V
+}#&bSaipgGjhLK-`D=&3-jDB&dEG&EPzfc(w_TXChoS;lZ~O%`kkg!Jl+O&uyjAt@q}6|_rO~`|D~G7
+8J&d71{)wrX>b{_e3xR1NWhRRQ0nZ_Bdj*fXgDV?9}m`dE9b%4=8;*DV(*?La#4_Mmy_g|ovQXEVe4>
+$F;VTGI^&Dm=406tN9`?vd}-9M*XHkE!=zi#cQm4nQ+$;eR$Lksv(q2*fnz@%sgdrrU<RWK0mI1(o6&
+&Oi<pmXi9^h^^txdcdTgu@9S)Lr5EjO+yPvxEF0?6KhwYq46nvkOgdR*1PJ6rd1Uq#jv4T<RBHV1-8!
+w@5cUL7k(|Kv+)4xVXJ$S+y_XcOlz_OMeSHj?FMuale_a~#qCi^g@1#$kbj~_?s{QcnhAR11}(gWoB8
+Sc*8)uZl;LM8&cWt+Vv$*#-yimy~kkes|WImR(job(oR9WmBHII?M|u@ly!3G<!l3_DG}vo}&^*3F!C
+V7Wo`C11J@GE086ra$p>WXX5Wo_uTWG3AuDNJ`ZKhmN<l$9DxTEyl#Li6Gxd8Wb@zFP(AZqUdzMm7Ko
+49g9xxAehXS4ZlLRa1-#(w_Ewf#$aFM3DdO1)Hih5`jBIE(C^Uit{3558=FIY|J)IW&#(dYZ#suJnqH
+fpj|f~k!16_R1dLp(R`OrWSX`v{C)IT$pZnIF4wo?(>ZV9b&LTSmT>2Qq`4QEC<(*1I&pbs(%A#fso)
++qiZvmkV<9CmLCpk7weA<B!C268(u>5GFAVJbfGdFO~|MvHhrmawXZBTztds-UIZCyF%foUIbnAlu%*
+S=QY&~htx&Hb9e+C=`0W)O#ZdHJHh?-C-3+?&X35AOpfhtXjYa>ETw1~>M0+&MCgEIDrYtk+>Yup=Gc
+XAIH{Z(dF(2{_ETUUsb+jz`uJ9kqf00zY^a>b=@N(QS7XQM`_8q5S3C;}VWbLV>+Fc?0;!nCL#Bieze
+!3k|~H)kCA{=m@SF;K!zt^vF}_@*!jV)#*(6m?CS({+8igQ5Tg818FcKsE9rs9=6j`pZ2$phPWUaY@9
+EO?w%ynX8}3AySbJCdy?6Uz6r`_HS)jM_4iGTVunN4Y2nE?V0wa4aq3k{M5Q{&2zRF((S}L;+m~<5qN
+O}A(>;fnvzXzcS{lLL&Um)Xjprz|#}E#(Zf+naw~u45;(U=bsL>#O^avFPoelGY^bnG?^vs%6ag#UoW
+2*bn(#BP=QnEk+j|N@Zh1-m{3QiYu%X|l>iI|Gs<1?~0pNVs)Pc%nlU3X@qwI%do8>b3wet8G(wZ6sv
+qfw2Dy{KMQYB?CY9_=`0rFEEL_?JbP)Q_^oSi!rQX<*vxpr<{|;<FlkR+6u=FlSc8$8_VxzOy)8%vU(
+7-jA~^920UcaIOAf<H36zL+ljoQLV&CizIyqysJ_Di)$KMnQ+4gQ;3=;b|0Ud`4E3U;v)2QFfa{reh3
+yPB9Ia%@!mUlOGr=>yz9%CNVL^g@Z6dUu23=x3N8Vsxyb!Yg^k*XCN{GP(OPz;@2d)4eogXSTou+=NH
+~RSE^T5boc@uR-P|hGMT;4a7k^{>-c7n286NUB_M4tu;;Iy>4F^LN2L>+tCOUPs%{Xo*ija>SL2dH{o
+0J36#<}_7xYOISs3tAux<02}S06OS&TEvh;rNa<Z9{8Ccpy-6aL&Ikb8S*Q;%AV9`qP)bMftrL0;hMG
+IeqG#f$kKzOI6~D8>EKy$S=9YGG+Lj7)Reu5KZ`)goWh}D3}8z1~9gyoB{lXtHxK({TUygk6?bf_&>=
+2@x<T83E60t?Cml~Fr|y#)0c1mN8&90<u?x}kIw$%{G0zwj~~qb^ryc~uT02Ay9}IhN>lztEJVTI6KA
+Z&){$A34F9=ymExMN<c%0-CQOydkp5S%5Lmz45_wo?jSf+m$X!LWemIW&w#v;NX%9{xCnGtXiH*M`b~
+nX6K5p)S2at2+Mk6>XvpL9boW?Gvl{;oNp3-`E?N8=9+gvX)9mS9gA3S()`eDfM7bD;6JvJsl?at;2v
+aak_3Gp4%JcDh7wRTBct`zl9+YTNxIqG2hEcx-}Q$m>>SEy;&p0gPxzc^1WiluQ$c}|Ky(GzMALhDdj
+kXG5;nG!gBd;F(fgn#>oU&z@w%um0-$Gch0R@ychmDPsGGsL~qA+#eim`PawK9XNk+4^oc7E#o;-xWO
+k!wU%#`&wdn*Gspm`o+=+6S6->Rso%xQ&D2VBo~)xUzW{<$z9{*Hx49M=qe1V6=S6{h58jb^F>c**3G
+ZbnLqUO<<~&WA9@gT^J^gH>z=;+8i@J22QjOxUiB_Rd<<7_S9vpT>JZ2*wC&J~S+7B-ZN}6$7l3q=4|
+Vbmv5SD)3Tyj~6>_Vce+E-*JBb8mj)rVYt4wAJSlQYGl$sg&{WNFz?d?=u6p3}J2g`Th!-jlx!OD>TN
+w6oqVELAS`vBGGysM?tD~I#jGu}NxK<0B~+O=>W%zZCi9%zKac<o{H1E@e6DSa)ZJ@6g^{WL>nFFl3D
+cZF2wai{C{w2ez-T=$rMMASW|@8_ep|MQ!xFUP}vG&(>!mO?K;K4{LwzII0#kAHQ>?wJB2fs`j^gv`F
+ek+n64BNFyb{zm_tp8gL1`R(udKc`>)EB^D<$?4xVi?7D#2(Dft)yl)M_{)2-VOe4?7Ic>mj=q27?kG
+>=alHq6HjDA$|6koZ`KazeqlsBSb=AJ*9*cl8FTOD^dS|OW8xk=t+TSKc4s7wh`Aq8%_RNSln~h~-BB
+{>#F48t$L%iOSh>b(uB|=s^!xu*RkLN^erFu`ur|r!g1r9`Tc<rQ6=SVO{(vx{gzYo14Z=8Xf%i-D^s
+3jtsZwCExlkLB3F~f&%u6N;u;EALX!su}V$GbrtDK&MnG|ZW#5!w|Cb0R?9c7Q#3bz!dv@;*kr0j_QE
+`~dcxa=nMDeOPcXIB(((h1QEOX#+|lrX(M<AC;&l(Jf-Gg@w_}c}-&PNf)*|p*T;Mv(|51>wu9rv6=4
+r=7D~F8G6#DD~L^;GfBxQkxblT#mm81m|-KHud{r^kubYP*3+=INx@<#?4mIVTEPX2JB*Ef^47VM`EJ
+IHd8$7C7##?Qp_E0PF9b2@ao5J}UUKigkB2SZub&g=;uKmez<acZK@RB7RgEjYKwB>Refh&1XF>N3L#
+}sd#}H>T_hKzL`yD}l%Pr;RyWF4t?gj2$#-G<SK3RQ801R`x9DtP}P$8Rl<R6!HTeEK6ayW!WN_O%hq
+3Sz04m7A_q;R^5<~6IXv-O&Fn0(oC@5Zk_$gWA~rHTW6^`?Y+vGm96fJ4CTjb70%#^Su`XZJquHp-hm
+;~?rR8sgoZ3Fj@7`_a*H^2FYzL?fxpffYIX1Bw`!6d#f6B>3c-cY^ddk*wHkt*dRE+=lI}Sb`YTD!+l
+Qd(=srZmt2R+Av2x=OZdQ22dX?S~NRSr*T-(`8MQaQYHxiLiO}-lACe&7iL=z5}c#Ch0+K=2dBN$)j-
+=$)%57-V#(>LV6=u=nDDsQ85O}DYb94D3CI2G@QiSCHvBu6(1&zIwxjn)`q*7<b|BJOYQ<?>u`tDLDr
+R49FBPX4_7Z7di#pWi5lxeA48ipo2koUCzF6t><D6eQ=erO<%;wM^Yu)-F<PpvbO9v)8+Y51eaq^h*9
+P|j$P%^&oNg>;%w&^22z{JNb&3&Fa{ARPms-opPU+)%b#IF7c))ejdzI_E;@;u=JMu42?=sq!->mj2L
+RJNsbv75jS?w;6|Ci-3@6P$+UUVFR-<hN(^k2yG}%Onqz6VEk2JVR1?>Wz+Q@p>dI^=Hvb2}cE+!f)H
+VdKY;;&#HxlX9~L@s^qq4zrx~*GgW?t)o5;K_nOt`-Z->ccP|Lbh~orLC;RRl*Sp5Ab(N{mqYKP94(4
+^PVUJ$S{jd7(&YiC^5}QB~UcI*_wc5lc^tiGIID?C7aW$eqPMswlW@fZq9|}B89K4D41GF5ZADm@I7M
+w%PpV+Yoq>t^9IYL^Me?d3_oVX|*&XEOcW=YS=;Wcl&pgTJ0c5@QLL8SFvt+d<_??AVM!apQ}tmaz;o
+c>IwML8vyQ^~`I2zhLO^Gql8b-Z|-4a)H8B@K{Nbm!)bVe*RafJ@{K6auUF6j{^QI90YtY3$$X7HIeU
+S-RDV^R&Cp-|3)8jQ-MGZ|v@S@)8@KIaS}1rn%Z!cD<+B7X8GrH)1bxkEI+nms9yiM`uvFkp4;~2ACY
+R=KIere7zf_Zh70LRA{^4ZZ%#F-8U(3Q|(B`=QbUMX)C$3>3%Za^>`w&OBvFIRArU}eqWb-g)oPfV?X
++TP)h>@6aWAK2mtey7Dr3@xpO=U008tE000mG003}la4&3cV_|e@Z*FrgaCyC0U2o&K6@B-wU_B4Ar;
+d!p0)0pk6xoSqC)msf##zi3=|DqEbj*z`YDp@NyV(8hJ@=B5B`29}i=ugnO!8jdd+rAhIUbL%OfJ1Pl
+@q&-vP$ffD2=%jYhy)js<pm#GodTzWmyX6oAr7ol+VZG@n~($R%F?_@r_kkCiJ#8)(g3Erfj^*!tc>2
+j^8R@OXu33ruUWB(r?<IPJ7jT@7*ZuQ)q1Nb?L-mD%f{4pXO$}l~s|pM#uH>=S(bLy;;0nWIrtaz7*n
+xsLU@i?osQSq+v`e^y%{5#rsdo!}P!eS=WWOuyFK3ygC=(HC0h6(IGl|b)J3q<GYKiMfTzR`pr_DiJO
+rSwCROfX<5yGTs4(%=HIKz8$+<BJU^b}bM9O@&z1FVp34*Z&}$9N3LaWNo{gfa*Qzj9&Og3cT=j(OJ3
+aTMJ1AJOaYNDfwW^jsE*Hm}cB^Z(9j^XvdGUmW!*#E%Qdl>?u=3VaugiUuXq=swz^0tLjkM}unl;9sR
+PM;HH)g9=R_%nT#QWt>kCc!B1|}UD@tJ~g?!DU9UU(yDu~$O&0^~OcF!DT?m7t77YG9#Nzck8uWILud
+@}6W$Bo)+|wWy7AdIe!qDJM7`?xoeHag>PI&86aU9ko`*i?yx_T8!OPh4F;SN0Bza(T*&8N3+g!<`il
+zo05&*VMyI8+ZiPw0_mov*^T7jj9x(5I|Ju1c42nNZ>!|?l<e4>M(niP&=e{|uGnoTk&QQn@+zkc6;O
+n7+t%e?G)_U?Q-6<*DGV@}l=@CV5L+b#R*B7vJplFjMitNKYE`5h{FGbHWI7EXMN3qneK5AvjL<W>7&
+k1XeVzF7Wms(}HUjv|mvr=KqEZijGI`)0_kie&<3iQzwkf6R0Xg6%57X(46;8R@&+EF>q0GgFwt&-Et
+=tI3^f7yNjt-MuTz<q>u|=eS*Jil{G#P$R#^@zuQgyMoT3j!(x91;!faKf7a(Vv!0&+=`{9|-YAgncF
+hyQgcb5#g#(yKi+BML6EjGF=>2&fvHqlHVXZv3dXG#!oJS9#2VTIP51mX*3D&y{oQrUdY@Lt+zN7PaN
+b7vvJsccn&!Dv>;em^4)hInFbyw&p&nCS#}R2-c52Tz<%g-o3uOTD&`d%kIIZlc4N`1W$?x;y(2cJ^?
+VO2C=g^3bL>)+e&)}3~7f+w|aUS`T|ZP`u~Xp7z%<X2#O#WaimITOAi|kG|(!FJ8Wz-;Af+&!;UlYyk
+k>VvC|Nz&NOM2%|x<GrUGGUov4G|^w$lg?C#{rjF?75Y*SUby3NqYAak#+Z)B;e`^gayAJHppC9Z9wD
+4EfIkP1<l;;aj5ZwL9Acr8nZ?gs2E8V9<|Kp%8$q?6v;3ECa}LD7|2ZPi*oB-4X4_OLIHsS=WqNW)U`
+r!%4f2R-AJbSml@&w4AL(K+f3u*DIVgC|*KvfI+EWcde}pzMJFB)AJ!CiZcrA8m=aVRFRYz17wl>p0I
+!bE|XGb+90<_uN_d<RQGVE{a#?B-nOByPR~pAm=&rco|J)TT@V@=%pckRdTB)?QhU5`ahxNX~uPtWz&
+#?9R@snoXh<U5X!F@l}y{{Kc9FEU#nj`uFv8A&&84>mt{zZjM^i6cZgYxSAbcq*K}l|&9p1&P^Ou@u@
+>}6)YecZOZzDs+1sP-BNI*WpVUpFmx0vPyP}_E_(|*PU84)a9^Z;Sr_;3FWn>8)`6@tRduQ{Q=a0hCB
+|U<HWdL0vvrk((b@|iV#i_VcswO3rYx%C=46DpeOmG4sCy8=ML97R4&{6`Hup>C;?fwA4^L7O1pnEdy
+4B;An$Q8KM`{jbt{-EiFxP&RF-$5L9#@<oVcMX#o2aXP$@0bT%NERDgb8)Z=7~rJ!lWv5w;2Mg9Cw$U
+?HNqN|H(XZK;+cpMkE@93Rkv8UA`wI(9e28>bYyd2#9Nue6Y-PybFX6i9V%jpbgGrbp=cFuGxtnr0@4
+n=6m0+o5CIZ%0&wolyx#kbspczP&C@h8vs0uit)y<rr%KT<Tl3qR0CJJr!e(0K_FFKXJ2!eVMqRyklk
+66!2Q#uNK?>wKeD|Ck+}lVY9q2}cf)s-qAC0&dv_G+1cdMdY$bwH<saL2GrZC(c&sa;!WM_GKy$Dmoc
+BLL_GzKoO<acD!aXA7fsQ+p$EOV=C?_wH~0I53`>CAd0@j|Q`T^6SC)HR$)pdum2KY=xw9g%SJx3*VM
+(2qD@FtOLY{tcUHi|Ih%k8BPSEE15<=uG4m7kCmbvN{P1N_8vqedbVCh}RL~2hE|cLo}Ka#iIJBj)cz
+Q$2-irxg+z!M88@6NBI12u%Y4q2AuT&g-+YWjm?=07|KP_yT`&uE?02xs*tugw5YX(9_ptpGj~zhdFi
+O#v9{mmS9(P)e|}|d-JDg?4$MNSF~2euI+#jv6JeQqt#-<`Y0tqarH9z|=JYS0#TkHw89OQ!z8-ebyE
+*+P&d--jmAajiCrTt4x|qaP(?i*H?;!)Q17SWwGenAf<2Fj%EnckpT4`Vs^Zs+>7u~y1=5YDi(%r#%K
+DTGgCC->e&cp_7{fsVnGePeW;d5{rb$=Ro;>uM7(WhCa>_1qwuV-w)@VYU4x}vX^hg{&>>QzH`%#cMv
+9;p5L+o+9SYs(=5xZ7Hpv6s@`63D-f9rs24Jw6pT<C7DRKJxw~U*C>D{}#D=TgT6+3CjjU2?3d$iVoD
+HHEB~9FmH0CEtW+A6@51+y#+jvw6JcNG%Z<!C=;_RKKEs2_46c&P$x(roVi||5z}l>gLm(*L8P4WOdq
+)SAfYJm%u)CTjf>GqAe%%mSxKbuVIfj1Y72D+zCjF#GBfTSUsH?5lHgibvef@nnW=ev(i($}uy{|eO-
+UiB!k|mGG0sbRm*N)=GL3F02g~BEM5nQRww{E6HZ$n^5Kf(TgreogDLZr){9<wXigQ#0nB#vuM_+O78
+J`G)J4Me3{U_EQ^JLd<umoMCy9R1B<A_|AfR$moYntsi_=j+{%Jo`<%RoOrg{8<{;Wnlzi>}08(nHmr
+>DkLp319i?Eo7ool@g8f@(%3>h~%nJ^e$rPeQSl=uc#ZVXg{P>R6y+!Bhyqcr<(zRzG0w*FJ-xtd-sy
+;5_0WTn<I~r`YXNknF_>mCo6vIqpdC7$Js<3ZZ%yXUvT_Erb?Y_&wP|$&<F?Q1{;5CDAQITaU!@HyJw
+pTNzdGb;0uKlY(i1y{!lp6<9M|d%*!G;X9EOgEkeWL>9lK7e{?tMWk<465{q2&`PoC5S06aVU>ttrWm
+Bl+XqZlk<FP<)Dd$KVqTm3>&FQzFhwBdKMgKn0)zv!GNW3=Csc|~AH25`+evQBG7yXgG$0yIDYrwJI>
+CHE%N2X5)?<Mj0`4>=20|XQR000O8^OY7y?@^De^#uR`OA!D75dZ)HaA|NaaAj~bGBtEzXLBxad97G&
+Z`(Ey{;pra*$70TvgX8D+8Hy3uIRRWS%bFN3S@>#%e2ExqBN39Vif)FyQ4&zvfQ94W*~{om%HbldsRl
+G(QjOG#SAA*uE~9={vr3m+>oo+SLFQc2lA>cDrUq=<ihO8d$!fdXfz5osw_yFZfa92o~A?;RjCYND_!
+Qb;c4?d2-<bU-ILyF_ju2g6mqMBz^L6kAn@5A%1XHr+kii0yfWm{uD@2QRC7Ycq*82KFe1yWyyMDV9h
+0k_xhW<2iJT?ReQ9f<O>IQ3lTI#S+EX$(hrfd$E%ignRCbdB17rb4BpI+Fa!yr9lV?#%|6cEZ!$KGZL
+9pf<vgIaatCd0?avC+p0dH?P@FfXu@jC&muMuR<3oZ??4Co~*xMkQHupu{0Gh-A5v@r>*UA0TWVhu-A
+Ava~%%EnK%Ls<Yctg6ti@*E_<&7{hi*_5hCv|-9<^e_!?*&RE6|91C<`(y#TSuVn3w?wB^^JDv2yRRA
+&nhlRI{EEg6`8wdJC7`3J6&QxeZ7C!zSVgr_u|@8abv-{@M!t)jSwoFwtw?7y`h<k{Le9=Ya6&F*{g6
+JqxJWO~Lx*8ZK79B9ND2tZeI$`Juee-uneBX}{YXptP!h%AOU7xKptdyhRwnI=XM?UQj9$K`t*LtPM=
+5y>$$_!E_?3Y>h7m||z(7iHue(nSBsT#JgtP3%kxX>OGqDjEsnQDh==6L{0=qD;m|}&ypy$4Dqaee(t
+r*wL4JVe?>(OjBipgkpHd@AAynuehpOE)>PACxMpxlc*Co8B_TGs^vE**O7ub01ECeBRzcW>H)TrY#x
+Pk**xNKu1q5VsX~TsBotaWvPUWQ18MzTpbI$6Y!)ONh`G=04859jnW&7VH)bS`0&Rx<4}}?^-Ju*jHg
+?C~JTxBwTHanv<S%x6o0|5h00Ua?9yJ$>}mClT-Zh(%c7|unOao4pN6&;BliNiZE;1ii)XxP~Mt7wi4
+PSx~u8d6D906G&D8W#(eXb-Z4ePcW=Ob5ZJbWv_=1oE%;cgxvlz_;CD{Or_<->on+TU5`3VePt(t%1f
+z=?%A=UPt)&qK|EoC_4FI7J1Lxdk5&NKh>J`hZvW}Se1rsg4p3bLBG#kqsdFF0b&@M4uBk;kp<7b8hX
+>Mrb_b2!hIFIUjvk?#IQdmuz{KgfosizpR+GPl%9?Ariga^L1dK0!@Q}0fL4-3p0LiAji@CSn-p_`(D
+nM|#SE(q)?7dR3K45N-hS(>)+cr?McHfZ)$u4l_(hgmZP)i&(gG47JRU>UUL$>}t?m<VZjP9Q_}i$6}
+9+T6Ph0bx<}ntEbaDSnN5rva4R>Lb7p_JWxB*%1?#nVRKkYp1=KY*krTnnnWw-eGR@#amboP6H5MI>Z
+GH#q7le_JOL(4t;%8N$0B#GvN@>V=CGv_arUWBTv+WrhKD1FlzRd+CU3P|8_li<y+{3r-qchr45uP0j
+Q<GvdxZhYnllQs$cr)^mBS5(i0uJI_QP$k#Owda2R~H#q8S)*g%_p0Cn!Lqxg3i-su@}{On%8GZO7Mf
+#w4_74@<9hf4PuH^l`~*>F2otz~H1T&F*O_n5i;d>e{CJn6OTSG9tnpqoo#lC=vjuPzB))gm`;Y|5ae
+?<^@ep)}3hW6rV#JqP)Ms@g|jH`|!{5{=!6;~1pji3Z;zi`bmwuGK@?hkgiJVk^uQ^Twjzp~tR-WJ+)
+t($*yyj+MOD+|EJm5CZ0TEq$oD+O?WJosHV2P4qR7q4`?lzqiXVS=IQW_-`#>3bS<e$NqWOBt>TxQo|
+(-gZKv9sf`{*d7K{)WXR+$jCvhPU|X6V_9J*3-okL%b2SplapuibsrAI+%-YxJoFB-bcJ92zQ52bO=E
+cG6-4Em^Ilb=yXWMZPZ1H~qB5VEqKk<DS74g%l{tBs&nAyH`bwYxmdra*+&*I=kDmpljHWSU@T>FOgU
+e!Db{sT};0|XQR000O8^OY7y+pKr?eE<LeoB#j-6951JaA|Nab97;Jb#q^1Z)9b2E^v80i$My)Fc3xe
+bBY{6TemK16ERq4B+10(pwNO~iNxgo7R2t}f1fwyG%Jy*mYPQq^_<@+7j=Jp(yNwHejC7Z-y84MUTwf
+QM{c1lFrJZaD1XyHv@UQr7>t4x<VcKqIa;_n6D_fq1KUB1wr;I<fFzeL^e5xjqG{m^P)h>@6aWAK2mt
+ey7DuA+%h!}7008QB000aC003}la4&agWo2wGaCyBwX>;7hvETJ8Cd;T6u$CYtA1}q2R(wziE0(1a<=
+82T55_KLmk6=I1_xK`#Q**EH3yDe(aw7hRT6Q`^z`)f^mO<1^iC#|M_27;Q&x>ymX*qix=HdpD^@BgQ
+Z;Y0JjI9RR;$VOR_lDC=2?+cJGDs`cgad)wRO@&k0t<WS(R%Q$IG^9D;>uwTNC7DUYB{>=$M}$J(Bl}
+a=poN4Gr2i^}V^(RaKPcVc8aorY!T?ytqy3+dP|_$Glvv08jH+*6uFaB+adPthBkSZ`&rb?Lw~`d%v@
+_8@=8v0Yme=O{xN#sULAq=Si(U`^5CJt@1iq>UjRyC%Raaspdj@on$#6QPsLKMPJjCK-pxQD8AQ4T2@
+h=y*Fj9nhKh_d}XNx<y?id+!lG6q^2}>GH%KkkeI2qz`?<LS3$pD=uMNAMQy5F7WYY>r9a@PzJN{^$y
+(3U_eIV8p!th~ke7?310c=n28me5_2O1z<=2<5<8Lm$y?A>Ozx?veUoYOoFD}2m`0mS>7Z!{~SZ_Xg_
+Gyy<F>MP3%oGS7DYzEE9C6*wH&waNbv;uW@B`jTvudW+$(@d==atR@w|hWgk!+Cp(ztHYvTbGrrCK+Y
+)?u%uNVvwdm=&omni$>=2L}5J{1(-VD%&&`$t!w%33yb4I<}{GE5NTV+X{LxI^@y1yw?`yqAZr#Y9lQ
+2h-O`^-#6-9O{48?K7I7)Zx?T_E?<2YfBov^>o4D4e)a7Id<>q>)M*Gcuv@@u=Vn$!E8Toc?}9kSQI2
+CQw#bvZRy4?8Coq6PIsb=VG~pSM45ss!KumbA%B3PCY7!AVxs9Z#Bb@hCBh^co`&p4SaU9e-U(UEbj}
+?oF7&_NfWiE|KLtj@qX|zhX8LkyQvL()d7RlNHJ5z4~tEwy;L=8og)aX^{HhHGLB)a1$zmHzfx3Bq=;
+-0IfRC%_}8Vd+75_cI4&s-y~0II2?K^$oqX<_b>0#R&{hT#xd1XFPX!nK9r4eaMoWlPsX1;p0syRy(d
+aNRBe%<c;}5CkfXiJ&qVAl2_?Mr;!wc%x8h7}cU%twV-hcMP~Ow0eyIs}XB(T__r3)u0E;u^s(=!}ug
+1WGWb%5*8d7of>Y0=&9k!tRS{T7{W7O=|uw6#Q}oS3H*EMGjLMu6i`;xIHzZ7s}%^B`z+OdYXk^d<QV
+{v*3i(57Dad{aFW(-4lHw<G#-vy{FayH9kNQh5fO$UZnLHq{C<EbRpdLILK+vDTU=7$37WG!h3o(iJ6
+P!M0l^$Pl+AOk?sZjz3??{5yPm@%3_{6xKv&YTn34!-k~J`Hqdkz@thqI<NqL}6Q8K@`MfT5DN9rP4M
+JhTm%lkT8-8PIxTK){u7w4Rr{Mejk#i4Nu1A$8ns#yLsI*p<T3qZF3ODx6}WGrRs<7iA%knt%HPeGSq
+NvDAT>}%j95fB<grw~Z7Tt$5VCMdktX$DJ|P<mW)xM{{y8h8aT-;s3it07Bq4ZJh5hS-MsyuHm9M9>;
+Yo9Bkg#A%V`X1Y1TLEk0?$(WTcbVUNMK&1g+A-h5kpf3XDPVZ)JJWb7iv~?r!Dt#<GuD*ZsE%bzW9>&
+2Nu@a{Mywhu%Cs=q{C95?ob42~E7HGmYI9HN6ESpfYtVOT^DzL~_u;`tuYno<&bd1ad1!Dv6hRVXAPz
+hAeX!a{04saOBGHTL(_P;neWE+c;%?9`&n4C|df0S7fTz3J_9Hg5tw1^@-&(-8eDp_!$8HWo}&4|+Qk
+?2qJqP|U@J^O?h5QdUTo7FyDk;Q6Q9`d9TShZfXRo3iKfNWqY<0^qt3P64O*$lY6Vb}@0LXE0|uP|?*
+@n!?x0+uFiE#Puca2TczBlVYZ3w&PDprzSz33Igo1ww`jRUd$x^BU;BBV!6U>>{GR04-IaDCFTpiUGf
+$8x=a@o(Dp`h;`Q>45M59KFwCTZh~-xiwpQ5$g%Xp-q$4zIegqyI7f*K$(De3BnX;v1Kqp_wNm)btGg
+v}y)`1IY6~P3)&l*nQxafNLocqlOO`viZ`WtfZf4TY>t|=5xQ9>9K64MBo!#7+eq9zdE&wvcQLk4<-K
+DACS#m%Lr4^hg%x&WnGMs>;^#TMW^cQq5=ojNDi>%QDh(o<)kPWPQGE*NJo_#b}%Lgm)!L|3y%u1YTP
+7L;!eT3wfB*}NE5Y4@;W}F@b4ogF)vSqSpK3Ku;!~VjHq$rCFECiM`=Uq)ZB4Z4{1Rauq)(`7mz#@Z*
+hrHud!{r*N+2o?s)Ip!X-DX3B_k``@33Y&#60E)-eNtf0Kd2Y+0$A-FAhrmw@tU>D99AT#5FkV@&(@x
+Lb#+lysPfdxqaz@zBQmcM&w%cti|<}teD`*y?9<zCF5h?x;#d#pU6R$>3`d%!u&&WmyfO6k(8cCF<Q?
+!%_iPsd3|S|Es}tH%(!(($whIc!Eh}2>cOQp{fJWBX4SIb&QeTmhLK>qLu)vx-SoeyPq*~mb=w@-aDM
+u2soCTd`W8%0NG_@d{d87%$?z}Zn4$cMBQVoEGy;00~h6f28WRjUz{`&Oh`sq#R3n(`izD17od<%3Bs
+5TX7B04>PTeVifuQUo)DglaezADeByb?}fDFkVHS7$%#^QY?b&(*V2r|yfUA7Eu*d0nia1tL3PPfDO&
+s|vjh5Oiqlgj;VRvmfaTLxRtz^hgUatkQ)bIIZT>5GTgc5tXn+qbfoxFR)05-qZsg<8obgk=CA^;~m!
+wI|Xi|c7yaD`*yfpUW3v4XL6oT)svyK$ysZ+kzKX%F^p181*YgCDV|QlsH&SP+XU0|X=m62u4e~9#ub
+T1;%QOe2dsLn0iIi1>Rla)vJVWsEP<oc=jvlepaQ4zzkYFa1~~xTnNL|(fvwvwJB6_X-qzDIHJuJBPC
+Kh9j;TWnXfsDk?W2#1-1K0Xy^q;(!~E;<9#~eOG#&kpr!{lQ(eF;M*hy2K@N3^9MUKZ#qgxPkp;|3&l
+VSxHhvCR0_2Z8}sy7#3zk2fxs~Tb+6dE)L(2eF=F)O5ofM$MXHA3}qJjl7+r7j;XvkDzm1ulpDop=i0
+1YM>R;bJR9VA6rofvpAesiDZUCl)=zzew_0cRuR(T2^9Y33ytxO#)V{(JApQ=tI~^xCN_(-oyM3!f<a
+d5EtZl1N;fEc%XjjjR8DK>P41Kx<D$PH69f_3Ji$4JwQ|eu?1aU2Nt(*e;I7reXbnm06)T7O|t|v6~G
+R(a`A!(6>!jh2hU%Bj@^X$^o6<qB6{*X436;;U%yN7IeZ=t2Bv?#Be&%-w)T4fKEu}jcJbybPz$WkhS
+eYj)ocSs2gp#FNVF26S8CKLW2OMA%KCj%fktO^W4pqmbqz*Oo2P13vTsA0h&mw29l0#?luQzQf{~(08
+BC4=<4HWRni?6h*n+3>^f1K>w5|opeV@`1y$le5BO7#q%cAy9chRaUTM&$h2e8BWxsP8@L$M6hSzQf2
+GeQ>l>W9BvTzoqXiMM*%vgLMmUpe(?SOhPb4>VUrhc%LXoE2D4*Vv5-j`ML=AIl(|;R>!N;|<~E^Q`y
+GbDmiTW^xP~QMKDZ$5Brf^{Z=iIHxBJ$Doi0`d`1yaE%Zd`WB7vOdL$c2;Y@0DlVWHfT0XVHSLg~NF_
+94#zSo@)U~Rb{WAh@t&>8$UgTC=Cp!gX$3KWA$YjzZ)#Z1WB--nU96w2eTR;t&&xlS_8q*)?4hv<Hn!
+PfZ2N`XgL9?J%ZE#&dUaeBH``KbA!_c-Qu(-XRmSPD-I?Be83s#N<*uZQLd1LSMkzd2q65%Amp7<u*S
+7`FC@Wd&7sI|TePQ?|l*Z>axPDS)4*?vvWytyTKEjA>1mzw}#n95~Qm$`OI!K%&N13JPt=-7#Cu=qi#
+2&Pg&7V+9&lpL%yTQmXGBzM<Hop2v?XnhPaBh`c2DcjMWq`eu|lNo~BI&31xG{z!bEB#L=VrS^1sm^I
+)&D3qV*5~LEn~`MZ+wn=d*~!8|O889i9-LIp02S0Q7_JlJm+ZJT{#NUsl*!6490~YYWU0T^*WDZ7Zc#
+t*Kj{YkC*5G1!y;vFERfJ*@u2kxFZd|iDvZ(fRH{sGMzqS2(trV%=B9C-;1XiGBERAr&zcoq^SQ;x$X
+_GmKqU!TK{1a;p1lDbyh>H7n*{eb#nD7uL2QyrIc0q7v>x*JMY+kex9P+yt?U-0plYC_-)ssX2vJEL0
+C{HX#oOZ#%y#C{?^tp)yA66a@?B_w8E>@%hr0RBd!M$Ii4hRk2C$XIGWWc$$z*q^*4kJsev8-EMfWvx
+JGs?_d@uzBCE3oHf|OpBvM9{`t{VQbv!<tC_qL4#)&=Q)WZgG$0MP=qhS)szeF#+E6a_>ZUD=}3Y*mz
+%o(`987ZqVtLuA8%nUDN^3p3~c*=D*CBiyn&?ev1tH*Z$~_W+e{Sae$S>bfmLXnu`s8r0QdrZ&SBdAP
+l9idr4b<8;t<PNp9Cia0*DV~%xkux7-vd<O<ww#<5B3NH{$FO!WzFAFqltSM}YJnRCL{j>hZ$lrkltx
+X&tepk6=D^Z-`ski;o4pEUGp6+P6buF#jU_}PeQ81;4J)e}=j7gf>Mm*9j@^U`cZpc%z%0VikV|s?>E
+J#`w^vqTC;E~S-Nf7uD=MV7GTD;jfccxjf$lFv;iEwVS9lz7ERJU`KelwY2tafjc{7%s7@j9Z$@)Q2Q
+Mkk0Vb`#}8DcRLKG%53aPZf7t01u2;AqQ_ea+#>!<XO{Y%BSJ==?&P4DO<MF2<7^iRXcFr8ER19g&M<
+Zh$MeJh(6GBIMc^_f%^J%N^czShxCBZF{_qwIF2;Cx3_q~61r4@ht^&bq3+;h3{AA64^%orEt-M`Kns
+dCRf&NY>>Q?jstuUVy24GP=LZ@#=8xyHIA{#_+p_X&dOQWW6;JK1K)8plU~%AHJ^S`qRe*{w+q}s(uq
+e6y`x{l$akEV7n-Ow@91eJINxD2vz#&>k>go<`khZ`bIn)oZ*cgwoj39>M+hDn;mwUNF<mQ0IDY9qCV
+MeBXU`mYCJo17m78n+U5*}D4Pk!D}50Yggo1)YJcZh7TIjB)1@W?zwap<<wjh2F@-3}Y9|LEPLi8)1+
+a0FOeSiv6+r%qSu(twV@_(AiwY`NJ{7XoA|)B$JoZiaR#-aHFMiy<G2LfB*7lfaLJLpcx-Qlqn7irdJ
+6ZEr~$sM#kTCq7hTkA+5Kq4tQ_Zx6v5{i+L#n#Ew7H7LDgwHiBqE4#>RismsfSXg`u&__U72-xtVX;D
+L^mZ{coBsjP;GF=|39-{;EwE;-%W!p3>CPP@X748E;`xGC5fjlFiWO1#jA~5lqCHxxiM$m0|qF@xN!F
+6=C0JAk|nhI99nVPf+Y_ex212p>X=`f%SLJKir1IrOT5JKT7H7=L5zfCLy9d+K^8Am{oKLbm676_Fdf
+#tb^$GQc`CJPMjbOQyievDuJbx-deMlig<_*iFkO<}9&YX2%B<iHJte^WGYbQNFz`2E!z|MAanUi*))
+-u`72-Z&<J?&202@J7T#Ze$3YX*Be-3*kr;+#||88xRV4YG%LWLlkv^X8boj{ZzE$nUd{5EF%l1TP!D
+!>#=2${cp>%Ez*g4j4QP<`2MUVzC<s<G)>vbr`XBICjKTKTVza=p!dNN?Yjn}=6xdum+!3s0DWJkfcA
+89(1=`tSnS%M=gK@%7q4gV5BEN3!e6{TmW=@rEhsXL!R>5ZUl1SporKC5l?HZkyW!Y(Z{$gfF=~*fhe
+5-A)sQ^fvM+;wuFsA?xpAgKz8{*y{Q)qao<0cZ1fV5-sr4ZgYkj!9N5D%mt39e6J5mer`*v>Nz-88t$
+KGM@Jur%}1DQy$GN7&wAFRslaHJ^*xOF0UMGfn}MGJ%NhwjeCo^1tbxUZ*<wkW(}%oE%}7T4v-CWHWR
+d!-{qqywXKp;O#?$`1F!SeZj}(0S^(#7(EfxPdG_R5p+4e`_D}9~nPfxv(2v@P5G7v~dm^GTnG#gQWd
+{v2Uj^PG{5>&|%)_B9f9<TC3Zp+0<tzCoACic8+eGlg)0EoNThq2?+dF*C$W^aC+*niKIt>6>k%ETD6
+L+7dg69_eqr{xZy!8BuLF|>@)A5B>B3uF{6bxIu^yx&C7I0ida2{`#fvr?cz>1QCY1ReK`cdT{!vp(-
+W@)78~~D1yeAr!mz)c1LC~}!Jk?@DMEFd)mjN~*7|jAdqfIn;sq)X)rnt=_%k1Iz?RqjF6pnb^#-Kaj
+$?k=o5>c`d)5Y%MPv7qaD_-P99#<4TUbfUtqJTzvV$N8H5udztg?$c+$~!CM(jU1`AyY6KCx?AjLRLP
+8aH5F1T>q+9e`D23$D>;u3?e>S7$O#fO>7NCuh8PfuRix_WSOx#e~ncknwE`*6k7siqmEiP0&-*tA^*
+kdgWb-U%crY_4|a8b-h>qp!fQ8jvmJ&b^Ot>;)sMZ)i%rHKj52@vtv7R0_<_Xoe%BloyURK)ey>Mcq)
+cZ-H-^5<8_i1aV#<zV<M7jb&o+a6b=CLE~ut~tiA7Gx!nHy9j@!)^WUSh0{(e*LJLH{2P!@ekYZttj$
+SYL{A!~YS(1y3CEE}uyCt#{lrE7xxLqL5dj?`>GMWQ*%iEzwlqm78)L>;`TT9+Wn2j_RqV+4lC=?sLY
+xosaBs_7!hk+&*k4Z=KQ^yGNS@d5R1^n8D=Nu_`N9}zaeeAv|c$z8QEF$$4g|%y-KnEU*mN_s*N>K^|
+%Wbn*GZ1=8ryCU8Pt>W>gh5E~L+x#Gptd8>67`V{ApFQb7a=%OAcc<;#S4Muzpct*MLW@!U=^y(B!$>
+6Ww}cBIq2vM-L+lWfq;?{Z(*4PrkWdIb-TFrhTF%0gV)*Cr<LcHoK?_wK<uS~{wz9$#!V;hlPvdT@?{
+~oz_LIjvpm8ved8^2-*rYO7w`~+Km&_f`o0fiHq&<!Y{i@Uw&xkTprKC&+@vis1eD|xb2cc*A|1_q7}
+}joL0ioXmmm;Eorz&i9eL9Vd4whavKSaRhAmXg&J;5_?z0Mg8oIcj2{2gkO}H4sw8)y%CF-i2p<f1rA
+j-`uK{p_bO=oCk?sx~PDHR5|<ACg;yPOZ<-Xd03A~XZ`gW41zk<~JVYmV;OnWGNJplz*j=t+O+;D)G~
+F|7w&W28)}l9Y~G@o@p1EVsCB5|-pbrp<eNj~L84p%xNdYgS)N>&7BX<BR9C<V2YI>8FeDuD*YBfhSm
+By?%T7>bt9-euAnnG-ZM3TLc)XImlFe_=nH^hzE;bnd$8;y+-w%hNhQ;CeM?+k+M=mfxLT#hs6N@AEU
+q1zyMWu##}$fmquJTONWkHnMU`vHf)MpAu;;n>{mAwf%#!)7+#_up`UDHJh_bqn~IkQl%4~uc!PkIEh
+}8`!YMtV&o>t@zWn~%w_X?Cdg|C*;{g<m9P_sgv3I76ro(<$y*9_lEoV-$jp!B}2!l3bv?({i6u!V3O
+*?W_5_g?#;Z79(WYBqgdZ5JJ6=J?#C?c$`7y^sz41d%U4|b=Z9U@Q+8r)`J#RvbVCE9oTu?tSx!_DGg
+1hW~aNTK>2Xi9DnCVP>4jE2DDdJ5WN;Kc`DpFO>qc_@d!8Rv(z_3@sz9?*^{3LK_$EfyCar*rZX2Vvl
+qGR&cIfUa4<-1&|LjB+sni521>yi0?3(YrK!&MMwr=&W5^n?d%1x^!-!Uf)UBi^r^c_|q@aXVj7E|63
+gEgBI2uY3)Deht6z0$QdK8|L?O%r`Myr(m{inrbC1ea*z)oIDVHhD={NkNFyB8Yxp2*S(mI+2`n}!Bm
+eyUWmlUJD}c0Wr&ky@!|(v99;z?Y>Dhj|_xyfl5aP9Ly$07)Z<O!uxqtT_xg>Niym8bBeMHc)_n#s$@
+RjYy3YxPR4MY3>g%O6+Gjc`z()sFRPq*y1KkWVH*ckD7qqQ49yjFw@8P~L7srDQQgTccH95!z^5k}4}
+*h(G(7;*okQ*{t&g>j?g5re7`KLoa!?!rMatEMPF1P<(QTfHi`I1~(Y<bXrDWpmtVHYhpy^-MmGZqP|
+hWIo<88Q+}2VowFqGq(Hb?9@KU+$HH*xy5pOVg)Sg4jabPRj#R#z4v}A!E#2G`iC=2Tb4Q_QEz=vE?#
+PR!%mE;iJz6tTHFEeatQym`6D<%L9i^uQS~RA6evPZMU7vqi%SZBlvJx44+Gt8J@;0Pi30qHL(?sDz+
+hSIsVT!u<z(GMU_%1nTMF;$9yu`AF4K8RU}P$jzm1WG{(=GY>YG<*tU9ksIatjHq~{usD%ZY?f?QCK{
+v;H7B>ned<S2@-4$unj9qITxAwox$Z*{Mh#M^O1Slw{khO!ZN$C3izINIzMk348LOwWL|5o97*)C&Nq
+6EK$0=fgY9=Hm<vKNeR|6G595gc3m5Z9~cM@V&wKH8gsq8=`rr8j#L4=QEI4(KrTBbhI`msNG{I<0*(
+<02Dk_N~UjCE!sRWS$%ley28m6Cpx1V9UDJPOy`vXD~>~Vrp1UN$3kO=HVL9Rkv^0_J4<(8S>tmTZf|
+h7jh6zfW#Ki(zF0bd)+tEAVCPWw`t11Q?q19&x&=r1fR2aFE_zX}9=bfYIYwTzCfU`A_ntq(;iLn<$A
+5FKG_c`&XOU(^gR-W?DZ(zV%yz@9bD~Akk}UAN5_-GnNP{{t5MiQr*`;${ndSy)Z7I(bQr0^M(U(~P>
+kv;uv(e<bcmpHp)L5p3obed&QDBY}jh8nkFF4y+-N^B%*gJZGm+?@Xd$q5p9g0qGqW;MuSF34J<yj{i
+km}9Md=TE5MwY!j$>yi6;gj2g%?AHy5lqv?U5wGs!0_`7;O+teF;m{2yJZYKgkIy>^|Gqb5r|>dHK$a
+v&RAp-^dd6QyvIYrAg{rslXGj8UeZpQ^BW{RwnO*E6rFJE<R7>Pr!j7dz$CYrCW;bgCCEP0T6ql<Hi3
+>-<U##HJ&m4*f8>NLfHx%(Vg=R9#^>C|=nYzDMQP0pX^j9rjh+cW>5!B;0|?KKY2=_|6lIB_eyc&Bf|
+uBrnR<yW@Ho<$divj{0#kH6n==FffhY-pvMJ^`hm54)sd#MqdF9SHSJJ^TLAT8+yBBJh!IrZsrelte2
+#J0>UDX_eP{>P9xp!PqMY#|8V3wpgeB6yJuqK1CAwkXiLJbqK!TVp0(zNam!s9wL`EmMuLpp&O`AHX4
+Nv<ZuT@KE`Y-EXyvlV8vovr>Hm6Q>=>)dG1ywfy%#6Zq6bh|UXbWBUDYJy?$pqvkTIPIE02XLKvxONi
+PjpVFj{zRq1+W=#AYMt!nnnEo*hgD=3iVoDEgBZbcMR#^<a9Y=m@xf!e{;A!Mo_j<O_&_ghDVdMY>D6
+Y3<!$9;p1B9yt)wRl=N+a5{I_?~H>9J!o^&-wejf?_@XtuDzLAre{$UC;k4F=3@LV>kp2_=UCKRX8a1
+MYzXr?kng`IP3c!%!d=&<<tDvnL$LDhTqBTc?Be23<`$OUpSD^CpA{zN#b%)=hLoKUv}I3E_L0`k#e-
+92>dLepb3l8(K#{z#Hj0eiLpV-io~SnK3?qSBTmF=Jg|^0$?Qvv~D`hXkfP{q(fgr@q4AV-4R9B255@
+rA-j#5C(k)C{BauR);AtUJ*VVsD_hePlNEKThSxZmtwUv$qI$<Tw+^ma{v!;>4dr3VUIDJso0er775<
+Z)NZDJ=Cmg7*=p>NWnf_MY$57AzWUkK!;bacK{%y{X=QkBBVzqCLjDZ&6l)CFt2r0yd)~jn6WFD<%{}
+G*$9?)(XkcY!`d-^DMZ5YjfiS>LY0NUno9B|F@ei&X;p;>grP^{xkiAHdXe27amT{+qjuf3NO66%}I_
+J=%RCJkdK+DXx?T;t)L~Ny1%2`K8w=qhvT-<fWPuRd^xQ)6La1fEmoGyuq(Q~k1Hd)x=cD#iOEp-+io
+E2uGb;yrL5Gk9ef$nmN_W1;3eGl=+Wi8naiAtWbWHw<biO2K*TOvvC=4FYAOq&2dhCr$YrjDH8?oui2
+=?Iv|3C;7qszC2;Vo+99-f(;xZup`z;ZSbd$`y?PFI+%@;MI(yL}K3XtoNPZUghOH$vdSw4k|t%<u^d
+;N9Rpn0u`x5aN<q(qa+!Vlw&<Ok(^QIWVSBHYY&7Ni*(Sf4?(_3QtDyl4oxtrOXnH*<NBssp-Uf((C)
+tADsF59V4>aU%p}jE=Sb*`Jc<F%#^t3NW^UuGpLQuXX#puAnD`a|0Tv|gTS`Z3_K6R4ZKB`Z<P?wAcP
+ZY}hVd~$_1DcoN;C}QrE&23O1&h&BL^i05X|KzWb34lV?fuZHwS8sd3zk0J2}ioq4>_;4iIX*Q-9_)I
+Sx$h5M0@V=@sS%Chv>&5uV}xONR|c!H9%$tqn@`T^1v?2Z7gP?_p|U+(S=KrR3y7eP7&+@^pW+(ajd4
+uX?a?*v-L&YT05s8kOmVEd4QNtvfz{T>Y_UTq3N%m_L-G#1sxCyOL7_-@U8ey(>@`#6K{UJy@nD9?$@
+=(=e379}(daZ``4(8})=1_FzOVczRoh7v8i*K?<;mE1Xj@AW`XI>JndIb6rmDoSmP3;~i}3^OTI1smQ
+z|!{CPGiMpP8O_Oe%n%Xz&U56-7MnvU)anMVQA2<wVYOzhviI&#OI76q_R+1aYY5Os5wfTPV8smDQ5P
+8~y=?ju~?q?!CNMNri@%P~yWOsrgjGnYjX<&|}2JlzzVV-#Y^@B4!z{Xh;ge$%5%{B$K%c;SxmF^VgL
+>C>gkIr3y2MyWWu>O{bmxJD}B$6%5Y=(%!!B8cjF5iu>8FGlKzy3m_flnieu<fq%zTL<q;m&hdBJPbh
+Rg2M>FiL8mP4pLd?zEOXHlD)aO+l!`ddesS@41dB8$}&>m8maW{kj{hfcVj7EkYlxiO6HznFMsg2OjH
+}dcy>^UIFq2ctqZll*<!4qXqDq;z8{V4%B0qonomcISS34<RE87Wbd3I=Nx{*^yVne|Nl@PicZ@FM%|
+z^-IJ(DsY9I_@G+L9eCzmtJu)xGGYFdtbAmkpx&i1!<AXr;51`@I1HU(E+a2r6Q73J#e{oh1KC?g_CB
+Foo%h;RO&1GoSs}(1?BQF`uDTc<|Oq4*_+G2Z$=SY21>KZq`C?qz|D7U!r?;p$IBiq@5Gj9mbo%BYB7
+3HEYO0`WW!XF!HHzW)zO~0a0Cxsq4M{TaXX$gcH{nX3*b2;yA0xwUhMZKiY2kY%iJ8ik%CYRVh1P_{&
+cugKOG0kt5Ob)~12@G&>{-zQF5l!|<A^+MtCPt8(^cDcB8){0S`BG>!zqC7sg%>AcPQA6$t(AMJHvOu
+jlCT49QRca({b6D4nw7b3P24o$9~>}5@K+&&_)TX|!rYM-;eJM_pAU*vySiQQPZ{we+KesjE?nQt{Iw
+6Sk(Pg>AaX{K8s;4D>kcz3u*Ml+uW5;wmj>fQme;{F<^9XsbqXe5jj5bsf8(f54o_KgnIQ)8yPwrN<J
+4j6eKihb*BM($@al|DPvIL>aQh>+aL*}li1OLe12B$Fhf_b;<p(h!UX&a8%$^2aLwQiJ(-zjd1tl2c8
+PriW_YdR|9a5q_(!8W&U1htvrJtW@bNZDH(zDFLOHP1w0NPqNx8+D)7gvIQJqC9R=Nd%1Y{b-L&XpDZ
+D4v{qrZh6g2LPGh$H*F3TuGQGsr)q0{`N%$bgGNhXnILjJC2nP&V#<Cxc^7o4lQ5=iFVw+B%=k4A)3@
+5ZhwM(cZ36dgOXwZQPxRyr>n{Sd1gbey<Y;dww?3uqm_41HwCi<Q+3*Lj2YG*{fk?!B|QbJL@u30vI0
+W{I$?G}?}~C;97aLT8<y2wm-PYa0e0;10xOw3aZRMy1N5D~nZ^M2N^C~=d&*xfdoIPDJ&Jf3S<!`izb
+<fCweo&?z{#w^4-o9DF7~r;L3~JaH6dT#?vVLmt}#h$F87m*AI?O^zuVHEcC4{;4ucO28>NgnA00mVI
+3&sbm1qC*=ILM{W$-Y(tOzGk+E?p3OU9_vXk59SUU8LWn_FkgKr5qyA6Cpv^S*Y5S2#pedhlxy6>k^n
+*EHvUGnUf!ek_mrsn*eX#0nt)Xj^S*=6PTE7eVmnC;V0@jrr-L{{c`-0|XQR000O8^OY7ynY3ELV*~&
+I%n1Mh7XSbNaA|NaUv_0~WN&gWUtei%X>?y-E^v8`R>6+jHW0n*D+uLKyLIHGKw4md92(e*E)eukw8$
+Y?2$VFkSW}`(Qr5ab{=IKV$!o{nqB^W4&TwY<=Dp#jX`1w^&+-Fxm9auOQR63msct7K>hVrpsmKXa3p
+fjJL~jW`UUWniyT#=sdJEGDXIk<No9M}t?WE3<<X&k+QJqs2O~Fo?U1J7aiGoCFf`g?>sJ0!vEHnv;u
+#=1F^bvl^&{xvK3FREfI)!bbwx$(B@4O}1W=V?Ug{3^N2R~TKbHwb8^+FcT=)qGSzmsHI-)U3K@5W`l
+^v(R~jxI@FiH#Y%m_VRLg;!M!-dHEbMwN|d<bi-&F?5wid1kPeC{D+a+8IPc0_*`)P8_5jh>rshyd5Y
+LfAI3Cw8ppE?a0CrSX7nrIIh%jH&8|(>?`HqNCeoO&}vU2b?PadLdt_wI)do}^vP!X;m_YTAGaIvUfi
+P|;^TcdqTE^z+OLE*W{-Zw@+xJi#5s?FB2wUhs2pv~#zu=e^fn?eQYae1P%3BVstekwu46`E3!n_pVi
+mzqk@wX0o~>}7ThtxHSt1UI#zmMeK;(u#_*Cc3czw~00KkR{CA&bMdjiiSN@Una<(dE)wtB{llog02J
+~c!-s^F?~@RzD$K&JL`amlA&x7&~T=HYjsI58#zyZCo8e*?QtwzL2bfm5C@mr0-!)e7g4!nG1jZb<j&
+aSdR(zagJi=oVKJbpk@QrW$mthH=FKG&Dg0pgJs_TzMG~iOzWF5a3Ldg1jWRA@p@b0x$Q}1@$0G<9+Y
+$*6W?}%}`{eY1goJ(6c4w|5;o5dm!iCx*xP&|M>2wpED@GKBZm63i-gxyF}obEjh0Nru563ui2NkU(*
+aYx6%U_w&eLrob64*UT=>v2tWBG*F0Zb#9Rg4g2Ia>v$mYyFs1bSH_Nb}o=WPycnATu)*5>=?sEbECn
+!Hm=BX*4FRFUPQO}Lfp&w713+a%kAz8usVs~a;ua#e<S-KK$&(e{HT<qdJGDmrgVOB4tjFfw*A<yq1I
+RmQ6pg8bZo((L-ANmy&sS8S$nDgM;4rB~vvX-WC#AAmC5|eaN_61e7Dy+0D9dK+UP#nKJRM?p%Hb%8E
+(W@#BF%B?~hw}*-ha1aeCT?k}d0a|R{I$UybnF%Sq>oHvGon-WaztEAh!T?l*zibHKEe<#ALsdQ8|6{
+R(i+Fg;=H|<p84vs1VTFv&&Z-z;+N77oO+&`^DXy0A5bgwUY!q21~UxFhT2KdQ#gnbMNEjE07+)T@z>
+`&W*Cz+<3Fcq^OsTG;*@qJ((gHAx?GAMPJ5GYtB#Xq!uO(!=QA+7(DNcKW!Vrw^VT52w??Beaa(z1;4
+Xk0SL=;$^}AaJsBh1JT^aa(yRioTAIQCI$p))$<>_U)m$;{PFI%$dpN;?Ua6Of(R`9!a+V65p4)~Tg>
+g%o9!Fp}5gfV!lta%p0@GnqH0|XQR000O8^OY7yhqJe*GdlnPD>nfE7ytkOaA|NaUv_0~WN&gWX>eg=
+WO8M5b1ras?R|T98@aLP|N0ad={h8BiXthRmKDoc$FUQgGx5xwagyEjIy_CXDRoSCbGn;)WV83PZ&l&
+_=w?%rJ)Yd*z46#2P$(1%g+ih5!vTApt#9+>;<9AJe*YQ!W0qYkV|Jd-dV2?qeVfeUw20@d%-HuozGa
+(q9_K8AT4|Xl(@mM>?A1jc$E!FkdwYBP`}?ohA}Y!*TO!fxm`I<0e>FOSKgMhorOA4;jLIZSp~y6k@>
+`aq?EBmDGE0HN`^%)zKNM^uTA$@{RK`c^>{EQCh{QyhMd_Ro^=LlN<D!U*J^U(-%j+!vRB*ob_7-`zV
+w1^YQ*QEjGGWPTo#iF;DVb&Sc(P2&IFFXa-rk=01-~e>489Uy6lK1dm3w=W$yJ;e&;_7!##)13zc(1Q
+_CR8<v(*|pH%)-rEn95T8TPc;o6P8kNs^Y6@^&2;P_GUD=yvwX{PuK@!KbneTW0A+XD_~)#p{y2i&pW
+QJkN4oYaJDZPXjoG+M;U}HSMzSCw&X#px11ATLJ^$@b8lao3{Q>zxAR2k)55f{wZSzV#wGrD`FUsGMN
+=re;zLsWg;>uGy2Qq3WVE+zjb)?_{n1!ixeB%i<d=wdS?nB_|Wt=={lNyYEN6={Jzy?(++aH041HGl0
+nUnVR55s=q5lK@MTYZSx5oCD!JC1QZ%{sNg=oR-!6;Oc@m|dod)wl*6Jq|Zflxcw2bZ{H+}VQ>dTer3
+=-DF41!6Kq(vE}v$#FI>iUY+u`2O4+W4OrFW5<kJ%J`%0mH8_G0e`2K`Bs0ffLZuIK3z@{hs<v$r=@m
+)r$p`k++0jk97axdrp_VV0{L1690O6#)j<iV|F~WWtB%s5sN9^i*Z2P`%opm01KCGN|r64NO}>I%v<L
+z{O>9%8P2Hvjw>HQM{3_WXdiE%myDMd+k<g)z6ZmT`!N2W|G69IP`;~Jc*({i^{?Ns{>{Sq$ll4htND
+AEmFKJVl2(RzP7|ixK}&KO6%ouMZrNa1%L$iDOQ_2MR;3B3e`8$++B1t;c?tcv&e9hCnSa8GFzh{J*O
+ze~lbK}kO;VHrQ>19M6W@F!Mq9vTg)It`Kq=$g0anz4(VJzPv}||mfy0qTnbL$qGiv=>JK1PJ+c16dk
+|r3)qm0*Q1AB_$ckHDw7YP}4s6hX^zD!_UPe8|@wr=<~iI;PhXk}zi&WH@OKU)G>HhG_w(ek?-7XBo?
+_;YfTr0s0_k9a1n@MKafv$CM25M(FD@K5n_4f;sqg;lSh&*f#zGWa8B)T=xxVExFT7`X!lL(?g$PbSL
+}DB?0cnZ!R$CgS_Q#VoqYk~v>mNz^6sH^~HpY#PrZa2jAN*gT61)Rapgvy8QJ8F~qv<T><ZdCP9&lC>
+|(a(#MqG~HYjy*2kqvi#yGDT+-z==Vp%j#dlet6jv)MVEK0(_jwl8THe+vVQ#224)-#1^n6*E%7p@-Q
+cCbOR^hs%mvpeyNEjhQkZf?&EyY<;6D9ZR`+j7W?goV8uGhGg}mOOi9~ks*I-X;WF|^rRg=++>W<!n(
+Orh&=NRucu?^BBvK>nxpJn9k$R{7Iz4Pz?I{Nm_yFb4F6EuH3#D9;~zhRM77V8z?1^hKyZn&e?)_)Zp
+p?wN{%jj!p4!0Au(?BaFHWEGBHwd!t;&K%gpYB68<s<}I6m_zmh_>3|znawh=o4^~!qx`YXs~TZuqcv
+A6vBbuxFyCVAL$EY#?Y7Q@$r0>Ul_AQv~kK_36jF!K;NNDB%m?bdB)x(WUXnFmF33h*}E;+)!l;KWyN
+N_UMBJUw~}PR6#*6jSf(F8J|42`WVtlxOp)ibNWgY;DNV(eZhDKF7;ToN=*JJ#Wuq&vQYtA18~cGwse
+h7OWL-2?MpZp-r>Lyh?Z=`sk7+*zyd<=8#9ROoBLU!l#|HPJ&Ym1F`mfqw7+P#Fs)m_6PJCa{_Efg(t
+D7+gY0p>J+W7Odh7OGO;sxz4-z$2HgFg+e>EC*k(*jLXJX){gC>IJ4rBdT8J?pWSP2dmU2Ha*F;t##A
+NNif$xS>@s1vcz3WI2vt<ybf*ppMuh3yt|*i#=jWXnVRhMmKim9B6u;o~b|`Z45-rLdzDX>8w0^pKs!
+y-!cg9wps=mdHFdF!#mD{91Hc&9SwsJ#_m?3$G*!Y5g`xU`moEaC>d-e3RprVAKPrlyRcS#W*hQ(TfN
+7$65`xpTA|wIi%1)-pMNg>qE^{+DL**|#ur~~Lh&ZRUo=~IPtv^loo0x<H4xW4E@pYc)knDnxJtmzvr
+J~2Wu#>-R2@{D9JSk49<ymwB(rD<JHrU~tJ5UqQESkGDR{ip89`T%JgAMdG=h(YQ~cc1(9b)$KUaJAn
+AU2NHl5g4BOW=-CK$Ym=l<mWAzs55F->S$n}HD?F<wJ>4B#wC6WyLB7l%CZ*kKe@*fPaaISoO&qA{f>
+vWLcWub5}3#*&WYQ?N+vHs`Z$3ZC;RC>w$eS{h#fw^uZ;%PeCsDK26)KDI3i^rKFzy*RO2M@C&9&b3v
+}3}|-QcUPbb%k0|KG*7?w<=hT;Wx=&;w6BzAd{?CzjK8GTjPI>Az;AO#?>3Ty@s}UUu^P%r0SjR{$<`
+>jXgLwvYkx##FfwCQ!`zGWl|H;-!6hq=<9R%n(m~4X%se?$jrE9hyLBXF!Q=h(bBDcT!|mIn{qIs(Y_
+c?FK!gFqtE9x>0Sw=xeBa!c7-NIqNpUQabS}dQB4Vt(2Q7@ZPzsi`BK~O;V@Mq|uE=t12s*lfPRuA|0
+g^s=WvO3Km%-Ekv1L+_dt_7+j)AQFzcA2jIu@*iw#YFlqa;O_07qq!<OTP>VD&7GDZtg390<X<qqs1v
+XB2=p`b<A?l4qRY84T!6*-nHPiz=0@dFb9gky-a9@WVH}8vXqV8`uFX9y=%UT`Fe%Dw|E#I&>D9-}u#
+4$bSO{iVo#5ay|KHoM(9WP_)y{YKkr-{zDJwYb?)ontsQDz&d=Fk#VW<Gk&DU&=Cv{DX)Ht21r<yp85
+@Goq`K`ldLu?qakVo&*zbeIy=>zH|b~d8tyPJBqjR<MV=%l;_OHKdzGYZ{H_~1#rZEm={HPBmL0M|rz
+6cvv01I6Jo#tr%qe5Y^dS5w3&U)X#+IMLU4S7=w<)*i<(r!bW1+fRFJHc-$<?l6#tbdB2G57Reh>cF>
+U0%}Ua!Y$KS2_MV<6J$G^fJY!91J6>QJ7tC-1)d;m@zWJ^$ZtCV%_W`TI9Ne*fzA8%64axuw-S9}S-Z
++bsCwV0dzLJTQAzQSP{ZbUbWzK8Dnr#D8jxL{0oq*cy^?&ziuTfEd7r^(*&QlyZVeG(zRoD3kqdkN!3
+8C~dOsDY<#Z41>L>k(8i>v<NAlpu{5B<)Bw?E8_0;iSuX`Gm#aeX&BCDh^|46oKc{4L3uto-_5GFgIt
+N$r0fKfasW6v+Fgu&8`7o&5F?l=CiUd^Q4xzl>|l@WjfhcU8F}S9o^zvyrQLF5xY}SCD7IwZC6;CIO#
+2aB1hTtwO%ngEZgT%oLG3-#BPG?$7%X%w?8vP7F>RgA5BM}R9je{AmSg2qpRF+_Vy(_zxU)`*#~PMTW
+8!nCa~Y~N_G^vpT7Jrc$@?b#lxEiy77|nyk84_tfhKQs0*m4VeE3Cj1M7-*IdGey3yDP^MIwHXjs<<s
+kLPv07iX%du|A7-aZ#)B5)_rL<khsB#wV~OD*g@)X2A35(9)<miMb=w4m1WOiBV(m;I0l-vTZRZe#Qo
+vca*B7+f)R>%K(QIeX>{IemnW@-J2h6b20^m`lQ{b<uY0qYN3n^9{z(JLdYHdHp|u+Jj=7q#ie=phx1
+X;LhlqD)!8P`aa#x-nJ%;0CvVj@RBm8vs<qCITgGAOdRSF*JaUU3R~5ze(J4EsEc<-O%c{b)BKrV+!L
+c3UAIJE|=p$&uOJe{k7xa-@iSa|JoyBFgS<W%&hf&FBxx{@(!c#DQi%vY05no>=k<p0HWBbF$&bF<|D
+|2fid2AKVsX(i!TA~|Phpp2Zchwl`O<^))l2Q!+pT|~+ES@_n(E)o)wsaN42AA)@vkCnJ4Fvjq&Ss|A
+OhNn52>jpwXBZAy>(rT{^84O8Ter1#6dTxG@)Gc8PyW*=L0MGToio{F?;=Jol$W%#5@cCA@K0lVZi?z
+3WYv$nQQgwgB=HUXXj3>~e<q1w3_G4}C@>(XTIJ^P(r?Wwp~Qi4$aL;>=K%L9(kFvm$Q4Crip`QZlgo
+0K6`zvz8oz?Zz+nSTHMtR+JQRf5Vxp+QBjnrcwiM_UE;mKn^cpZ|fc1s<;!E>@{V4L_;#*qPInAz9h}
+IUtv79itrr>D1Uqf3F-=x@Hmf)o~&9i7e!*xN{c*zNQKlu4<xVul%jh+SmN}#soa{n8c=mE3+T9mSa4
+~~!Q?WRIDq;4}(=@#Lq+7Lrk*F<`s1~#+WsM#Bbt*4!p@x#ClM2bjnKq_cCVP?GnLHAso?&z^uB{yi%
+rN?fOGJ|Vq**<&}ewOhMVe~$Hl=+1`kY~k*%$$EGx}$|>1$m5&<hA3=`<a_--U9sh!{Oj#Z({i4=$Ux
+#xcqz$w_NeH?n;IXcS}f5TDfFx*!86dZYhUVdRCSf`%CY=6~zvUq-RQnBvYN@UrofZrKX&V*b^ctvCz
+`5OpKBvM~<{k8EPaaA6|yR<|$%q%vQO=fvW&tbW{b5Ok3->TuLZFqC>kp4y{7d6cJQ`H7K!?yF{RFXJ
+8u}RiqY*g(FAKbqTaMz4)f&&cOC?pG*l-=OXo6jRLk#C_))g1Q%*TmXK~cOg_c8m`ZJ)Jn@)5gI}QIN
+f}L-{M-X2!?T;q>o`{DIa~?cD54BPnWxci%mxV(b)5t7TY2=&jprtvN0^jJN8s~hv50B&!%xUzy5O-D
+Dw%fht74JpdDJX-BJortYp-}d2J|0qp7khr3_NpR2UNRERxxJLT*p@!3g?;sm<ERF5!Xp^Dcopw8G)W
+9YpH3Zv%q=51&TGGmFwuXhYJ?|6OVeZ;v9RJfTIUr60up7;ypC_R^92s6E+kC4AmKMcd<cP(Ge4$1EH
+-648o~Sb}-g#H*FCMJF7L!!aQ5&3A(1xrXSKWRx=<YmNU_Yf8zm)^d_i}Qj<s<jms)Qa{lV*l9O{sQw
+!($s_O;k-wUEhza6-3&^c9I#dCy}xIuumc_J<T7`lY;KTu$CUfy#3igBB<US@MLP|JnU2|e{FDw(Ahb
+Qs|T7>!6)gg!IRWa-HvF#5`qqeW{I*z)E)Mw6^3yizrb7?PpMyoE_c^zK}mr<l}M6;C-%HL!B)jI3LL
+F*@MEg-0+UYOp`N`;m!SH0x{zyDaWq^lX!i2OetD_KfuC{lV}V7>#~!u;1x999b0TIwll@;z@dl)q7F
+Cj#>?y8TR{w)A{t|^t53^&HwMDH|@=lHTu^OnjH()Oj(AN$sL2r!4m4=4$`W?y?pBtbg!U1#57ZyQ>^
+1zvPj}NCcn0EY2vlJ#>I)sJ6%B7ygD$ujAx%H!$L}wUn7V-%K<h<?M&UfP@LQSM|0MO-#T(Xtk1+zwJ
+A;Fky`%tEechK>_{0psdE}hx<V42PKdjgqEI;YXt0Vm_8vceH2?hkTF!d?Hr4sK{dGr<r;mgOuZsp0)
++hKKn(cUd(+O=SRN8~(5Nml-De!kqS|O48jw_~;wXrq`5ePIDw3|3ir0$<QPGkZJ<W`~1od(&E)J}tI
+iquzaZZK%!T*c74xgL9-BFpbCbG>TM@Y1r<F~{7?RfzvCFSY`S3<a5#*Vs>yVWW+k9nip!-f$WzQC**
+OPZRNi7)05~Sg5n5&?!BS+22{){sI;S-QzlW@lsq~Ty|Vu{O`d<+ZGjJ5nwkRVfE^y#i{$ElBrCfwx9
+vv6JUy(pv$lRHWuF&**ifW`~>q0*PD{3bFRSmSi-cz(<%7RZ!o>72#!z*lVb^`C!*+CT_OyZ#7=WVe`
+w4t3#w<S5f%xQV}~xAI*bV>0>8%o(a{*4T!BJWRe)`kEZn98FwG4fc$tRNq78;mR&@@+(0)~Nessi6t
+m5;y1jY&@n@wNn=h9UatsFo35e|jO3I;n`;>MSOiN+nSGvcj)8T9O*;K;kJsn1qxW@H{CLI?1D6)lsw
+N(H1@DgJ?uZonhT<suR>sw<15Gi20sT_Nu_pmv0|+YIq{u^F&Ut;1%m1&J^j3bsl!;pR88wb;NZ<z$I
+me4g$*%Tib?Xm2K~Jh-v&q~_X;;7A1R#Q~#x8p~yNjoTbCN7$32L-YZz<K+_nTWp|}4L_N?zDz)$!NG
+~U29+Yiqq;bSk^)|U-$|fgIBZGHiHN+>KKQSr-sBH&UZ4N@)wh#Bod5Cs{f|AoBECi!y=+$vC4OjXRw
+%1o&#;WQ6(h-kN~-FKh-Os|&K)=rB53z9qNBNZ5wbY<@Aod#-&v5&iN*gJa9|>-Uq^T+i>#QK5W<nMG
+<(a!tmc$Q^J|}7iqnc|_I;F7>?_c<Wuop*sZ8}strbh6&=u3;6r&{u%GF=bU2d*elA_U@_xUXYhiG<*
+<E%6QcD)$eyT;rE9Id0l<No6#9zc<vR1Sl3v+nc^Vt5yc+YA&>c@0Xlf|YWUZ3?m-u$=${bx7+r1#00
+Rd|m8dK+~X9Qc4tUUj$2aqBOnPfGaAz(58k!U*6%ooRg+X?-aKqz<O!R?Wq4`@Z^XoO%R(T&9fNozn|
+GY_m}EZW3AwL2YEc-%p~T4AKesS2V{9BlP+s}p|fsBM>LH<Y1<BO8llWr>>^HS<yObmVs=xS41CF{iT
+wD&%6Lpto^OH?XKo1#T-|1uN~;JqZF-BSIfUotWq8&`PPtHUe%4DQS)1i+;83bYyDjP9DI`^*e|UM9;
+$!V-jVf}F>zR(4!@P>HwC+v~`iWQv%@8@3=rV7(x3{>RX7#bOmt<OhDu|K2R`5vB?54Hc+Y<lAmH55b
+k$IJO;fUYOP#O~yv?}>R7j*UbZL|azkG?%%KdP8IT{Y4ds}g?TL#L5b*wE1dP>;`4zNiWct2p9^Kp3s
+1o4RyTDkvNowdtalJteu?!IyW?I}b>07u5?|ImGAUlb<~ijF__ny7INLc^F_5Z`V~CG0@NUqr5{3Xj6
+s1;c*7qde79`VkoQ0yES{l(XrMwsiZd9%c!`lq>DecFXNlGUEd_&p2UR-wXNCB1BUln79}(8YA6qWo2
+`cSg@@0vDjLY3f&pvUOSI+pk1co~XVL63=2@6uiM9dxqepX1-APRm;b1qsiFDD4D*}glf5agnkX4l<h
+E{D;P~HI5;*oO+3*w~u5|Arw5J03r02~alDjUdbxjxWLjpTg%Akd{uF!VAU%u&OSR2jY>4)tHJA)2aD
++RJ6s<#8>zp_~#6i%&NBgbliyren}}x+5QLIUi}z+c&nWMFMii;OFa=(Id+Kzgw@s3PHzKYBHr?sBgI
+bV3MkHsK_(0#GpX|O}8rF>Ku&84TrU}el*RWEdZl|x@54Fu06QinKDnoYZ1gAjytP?&R6T~`bjIg(+}
+Y6nxC)F@omnXo5-!0bbhFIj=c==Fo96ZwCgup#4l=Fq8cyk3N`NdefN}=yTtn4r7M!N3ZYg~G0hovu|
+o;A@TJx9&cp6ixBH8gR+jzUXW23h($}gShyxZaucO<7EpYTuNf5&TR(}D_UMkpE{BGR4`!q9^M7KVVq
+b0@nlM-CLlsBb(5~H!*%*_ssx%0#~_y=AYqm7!ud12}fy7}-;z1sQfqA{B2z+uK}#Ykdg91l*!z~Fcx
+Skm%VI@}zMf^$=qs^r}-5f#v*v&~(Y-<k^e3D3raHy*B{s|6e5es*O-l~>1|W;Y5Bm39SmQL5$DYFP&
+SCVW{+ld>1?Y6baM?E>OyhYZ*{dEN&59|El7ovj;puy3jaCfjHvCt3Kx#KUdw-<_PTn(itNcIzb?0Da
+nZuT9y+OF9X+CIi?tZ57+vk`k(IsA>dvYDuYDRa5C^Wn9Ga_n+?+Vu;#n(xI%lh0Cweh0IWmEo6t)`w
+iUbRPpL4oWtyaILo69ef0RGb&*;3ZP-6w${X(r=L}DQ`1$q1@Zk^GD$508Ur}ODr}1TUm1G-OXl8j<6
+c(nW61wWFxiA|y0g`l{TqScvkR}Yu0OUg`Nff%9%@Te%C8&`M?w2d-OL*H<a%PHN)8xp2AGO@f<LagD
+hj^7;#U`ki$e|#3shrWBZX>|(7NN1dtKIm)`U9TjlLqcIoH(g6SbSJfd_Z_5KJ}j4Eg}NWq-XD|ZW1E
+h3OJRa3#KAI!NQ{>go3G{baFI&Ze8*g7Z4i=%t*8j%fJ<xSz!<q@-9-VrR<9)Tzm+{pEWN&g5u-m#ZR
+Dk@EU5b*K0=Nq~GtKo*o?^TVv!WFwkr{Z3La`ZL*u>W;&NlE3TG`Xf2`yz#O)Stdew72q&&S3BtaJN=
+_kKKjEn7bVY&RW}=hqfLG4s1|5tl2Z2+&7oI-h2B^)dPV$9S6C-WQxQs1-8V_mFw1oqkB2cnrOIsg}c
+&NLAb~NHaV>!rTg?N!K^IXShk_&<R0h4UuE*!cc3yxHdg|VR#&Wzw&#NCa?_$Z!n=QFfTG}9;kj2GA_
+o9sGn(Lzpdod|$C)wzY#W?54-B8OHTa2eiKaPPBI7A&Wr{Y^ut#Q?fKlpeMKDKH=*IMPHhP`h+~AUah
+4_M)xc__%>&{D@%k(ECC2GYD{lAG)XJH8QMjWKh${u(A<%FOQ!CoZQzjy8u<f8;F#wmB%eg7}{LWRYk
+g*=o@Nr34mVgfq5s?k^@eHON0sA3B6+kD;Q1g!1aaTr&SD2agrB9`j4>bkE-G4-A|iKBOGxYD6<nh4A
+A38qk=N6D?Tua`>~Qc$@a6z^<Fad<f(jCZWCg4`Lfz+ea%Z!a+wEkfggF)mZf}InIWPH`07)93XCH<=
+(m<E{9nVvb|Gj38;rQ<iN-L=p-^dWii!rd2Y&Hz&%hjyC{vg3;D`dA&i0tRd3znDde=f=lDyN3B;CcO
+d$@5_B+$_~CAg+J>`zq1)}NsGE0OVmfB!xHz17t3vCo_F6Le?J)iq1$B@lwD?`Oybwky_%!tsd@2P)3
+?A~aKAYFwOF-kW}k*Q)C=NE~V=q`&F$f{O@#<_PHpRJx+pP}Q>P^i`MS?BAhF$ii9uP4QiObQTJhZmn
+k3(WCTow;su1fO<89co&hgDrA}MJu@nXoM*#*mU#W9qN7?XB!CK!^?;+j(>#|q>bYr-3_t95*4pwJ&)
+V*D40y23?`G3@nO)N&b?SAf;o(~F)ZuWjmE6l6$PL^sH9qCq(!tP~-VPn2>Mt8q*R{nLeJ9z;LNN%06
+8X=Ev`8YjhD34jt-bNy2fq!1`9yd-TIjTliR<;zI0%%t`1nGppdtujOW}F!0|WaSGD5<8AZCOjL^z>I
+QTFf`g(V?+;|e2`$JHqO%TbQG*UP7XzKVhK{A_@ir>;fR?F<9*{=I(*Nyj(u|HyEW&p3=nAY5ypg{10
+^4VN?-S(Vw9Nig*D?^Tty69h!PJsv8If84DHO5odYl7hsL?{tMH8X3_EI@9|Hf|O>P+}V`Z-KRoP`L{
+BomBQt=FOo3p!vPWT(m|5qBiB4dt!JVol2OB#Ul2xa;ndgTLu$4xGXAhoxD3CeGLMS{-7@&zxnLqZJO
+R`iIc<qRsQ}Av7b^s6%ms;##_Uyae*71A4^-JTN2Yd%c8W47^km;*-gb%gy&5o+4}6Az`fI&O;P39f`
+=Lfj1o)F0FkG<cEg9@S;RMQ`9J<_q2yqMXD{0FaCWy*marK1P;?BE@c?&Disf<1a<`e&_1}{wKht-Bk
+C?CsI-Z12$UF?O+QB`)78S^ppT&gyLyu`WVfB6~+&d8ui3$Qieak{d8$ejkAvS(&b55q?uVLm+s4WC8
+(`Y6?s!g$I024k8Be#OvLfhSJm6(fp8Uv5_xk5NzBA<iG+(@{*^?2#Cobbup+c|m{A;!Z{SbO;QeOKy
+~s7{jPGOGczOna_+I4|Oieag|NX0WMBib0I7OlJqk*-M66K`<B8esFzx0II%5Bc5P`m83B9_3o53!Af
+RIJ;Vua5S?A>s16<ijCVsf37gh#GEzCfNUWw<ixe}WPfnD*OgS#@q_SAF;)#Xt&L19fg?vfWMy3Z4cJ
+Te1~=1>TWsZfLQQ?>Y!{}ardr&`;A8|?IOus*(p>9O-(+~ctrL|1c|kMz4e&aGAEe4It{+%sp5*GXC#
+YrT}pWzsGt)<xGjlS}R$mPywj>tpPDE`cu}XbB_;v9E9qbY*#H`bG7tRJHzAuTk5VPJczRbqT4!*gXP
+EEM!2~gCQG0$v_^xw#~e2R}Iq~z5``*Se@YV8x}~FGNDI!bM}dnB`x(!b-r9uOjd}Qjmu#ODlRTc8Cz
+0GcxdX1IKPVdTCVEjjh(H=4@^V0gz4L5_)2boc4QFr?y2MF$2ev`ynW4v$Ip)0u=k9Z5O5?-w%(4Qd!
+`72iNay0!K&wN(d7x}FE|0WeHj%|S?2PjwdDw|6?k$5K5nf*Ze=)mH+*1<NwvW!SXD}QRqp7^vx<T2Y
+!lA*gkdkt`InS}NBELF&LFN6(utx#615QXjH@kYTP+Nc0&U?eQyQ0dNF`mBUls466z}B&Dc*~JU&RZq
+CvtIUzMK$Eg6%ke^Zu<`UGBe@U|%soJ(XCe&hU#>j2`fm=}jEu`i={UBDvc-BCFMj71ZO?1oq6)@{a2
+?5;!CR_ZO@pj4yTKVmul?AKX!9x??*^O;@aw+d5CKqB5=(nm#GuQA{LS3bBd$?lt5G5}xe~Mrm}Itgl
+9g1o9d!5A*mUDG<#CkNCu|WAE2H3SRbXH>rzSbt1o5X45TdS}XC*beYT^P~hm~e_^4kJU}h68i8$6h3
+eaUavPQIBqE-Pf<z5UCLA>|CvxsAVutViv^Tt??paOlq<==uT639Y>uEIeZbekfGh(ui^OEyDG|#8mR
+ItZ)wWg}Q-E66-=3qhmb=2nJDFglizZWNXHs%B3m*Ncx^kXEX0|tX6kb_Z&?uYOok;FVAA}W2!dLPC>
+exScNC`j~`I`(HH`r?4S`Dp|5ZYhBNhfhIS_5W~g9}mUDwhK0BiA|=4mj%*8_V_Vy_Sez3Z{Gd!{hu_
+{rvoR54+rMiKk*5Kvfb(rhR36)<7X$&WeUduN4}uwH@>=B(j5|Uu_IO3Zbgruqk_B^o25D^6^wCMECR
+R`G|W~%K%ctM$r1iBgg-_n_(vc97{Ygh=kVQN2;cQT3Z@*$(KDl?g6a3_EeB#NqDOy`GY}Z>43A<G?_
+Gh-$gwt{zbmxrg3>iU)rA>m^wc<L1E_C|9DIt^J*kcWYm;AuQ9J5j`omXIs(|x@$b=S01V|X=7q<1Fj
+Nyp2MJXBnA&_nHOt!)c7U5@+JYYx3c|HMV$xRKX{?K^O8FB8M=xoO<C;<>vMxcz&+hO!J;`X!s;b?!S
++A^{&s~hvDZ%}eVtI$`TU@pMntikl+q(&M#RNM|ZgdbC7p_27`IQ=*Hz7SW;*9fCdGfkrlCLVZemal^
+KJ^bNn)L2v_kA}ER{?0>!+<{wRM<nO-41v`Tf7(RkORfBHJ}1R92cp|5fZTAs+79c9=yg^yduF{l<3U
+SuY8qkGIpy*d>I1YSo+vcuq8a)$kqZ5~O<~p>L6qtlfBwA<PO7Ig4xY~O-3A~|IHj)yt**@Q<3DV3PT
+re^1=79`yB<1g660eYIng6dM2O?<N>ox>(qDPrQs7dI=uIGc;WgK8y^?nqd4PTzvv{>GZ_SiQwFU}l&
+=Yg*R?ohgj5ic{$qq)XM$Ovae<Psz@kMIh4&FP-@pg-cQd)&<VcIg%Aq42gRlM+zC|1#$&xSR^xAOJ9
+E1K?jfZiUOCXOzA*|2kujNY`Nn$qZcKy1w!p9b_-e<r-<(7B)?bMVqvu&Jkl?nJ27d_~$-etAlmF=D6
+56yUAzqW+HFmx>5iedGt<zcqSD>-~p*4;ROe_0vFiEihP?3ZlY`hOI|4_RX;2-w-96o^7N<)5{0}3&9
+iaqF+6u%LDC>fxFG~ZKugUqa2^;DmRNo&t;Jmu^KNsH14Fs{Arc%&{dRznI{+MVCx4zxZ>4!o#V^vX3
+Xx!{JpPOF3t1|3T$h@x17s9^Gg%lJ}HT?*n(a<ka%anEQfriYuri7Utqvfarpugc;k7{q!4rLB|8qv;
+Z+GFVO)-lQF%B)$XN!wD`yd$Z+rz^3LX9SWqg6bF<Fn3;<(NPR3b^k-k@)if0f=!!$--Uusam^(Iq}*
+`*?+~JUm9RUFX=Jlwd$+WwbQG+Hq^c^GxR%&K#l=-$XQfRL-~D2vT)x*LtAz2>X)9|KK(m=duKDHU~!
+c?j?K1uXo7)edvG0L;hC36&@>v1KlPrjzqg1Mz8+z!E$Ed9f!{06AZ>SL+~A$0D7?qi+3D8R5KRfvV7
+=G{!(Yz12~tJ&!fEON!PG-KAa|{(mgWn-1Ssr25d%7(suJlQcR5UMsAJ~U5T{s)qtl!Yz3T%9%e?{z|
+pjI?QK$gbE!RTee?TPmrXmMSwT<P{n(qGsd%m<bfLum;I0Omsw|6^YHH@3d2fS^qz_?YMJ0s|9a7=h8
+o@Hdtwk}beTM~@p|&QSJ9xM23^6nkfqLP*v9n7Iu7cHyfu}G~oh#|T>Ja7pjpG{)&WK(de~KkRf0HBQ
+$n*~{e53Vd3p#r6I8`h}#0I&x_%CX=S!Jq-X7Ppi>*3&IuqF!Coggmo+x901jkgd(uMN^`g!;)rJdpN
+gKfgLDK}vYwnwv7rke}trlrO74ynQ|BA3tTsy{DDaoU^O85MGGLP=ODpry!b26JgWBEYX(pkRi9cUWn
+R?zSW|rSS72-RXikU5fWuogxB1Qi)gP0V7{jYP(+DnVO>ROvfeE5@JPjP^+qtRdg`-<H$1@<O(xfI^a
+;8&nY1kQx(YPk;3)bgMP8~>HiZsw#1iF&xz4}0Z+Ozamyo~l4c)G}Y3D&Y@lHAhI-QEar*sGUf~5TIc
+sLvkhfklMJngvgl?)a>azP1N89AtF1DEKkHao$4lvSm74g{Ppj8}RCu>gPG+Sd)=EEdTup~$BU2jhFt
+xPel;Gg;aR5X!4j+k%7wFU)(|(#yKgh4D#)8q7ZvFyggj8}&#)67gpkJpvW{^Oe7vXXjBzRc~Z-KD{z
+c7ZH^UKaVnO>u*hf0%!-H5%b0I3O0r-`DBgxu<|rECLz8MEw1U=*Vhe7e5W8@-YMj(K72n`Qj&&z9am
+Mt9#T-sHsM~!VIW#ZErP!mH5!IW{*uZ^8YKZ9Hu3KbXtBz_dci~4b~UIL4SdCez0)L(^4s9;e|?r7!d
+JT*@tKlM4Elsl`Gs?M!v0(HEFL#~$I9Os+4(#54nEyoRGt|bq6l1yp6a%|q5AKhR#Gn}n6+&wD$F7i7
+k7e^6Wvi)dw)R*$BJAy&G>lmY|uQ#xKg!LD-Cq(7nct5^cgwa+a;qeBpQ-2no}6o)w22O@;YP&-0|^o
+|BkvLX*E!duPO^r)6!CSK74jpDF8Jhro~`vRL&(1r|J31y+Nb5Kq}m^Hn~nAx6<*^jCOk!jrp#YyyRs
+grRXKts}Q)%X>S0{8i7l?p)OKZ!p^<<fT<#86%lh#7gSz`ZW{TWz}<=@IdDjZ5A2%>qN%VorUISZoKf
+^5JXGS4+9c+I->dQ}{N+^eUT6%B6kQVqx0M17lWfiRohtRjQ>ZP&^+Bce+)o%4l9yczNq-mFHIth$rF
+VHlr}n|7I@~>ri_n`pwJ#e8i9!(s>pgQu%ByJ3RYFj-s%N?L1tDvj*u23YomEE_s5+Up)x!akQ571zb
+Z{W!_GFml-31ebShj-Q`0l;u!XNM>AbvW#NqzKUgt&;x7UGrgBO`ns!68vbpW;FwS(JH1hj?Q9IHO0&
+dm$lsk5|+@sv^a^B=j9;guQy^2wOFqjb+6nER6>9#eK%|Z!vp3&mQyBVkZ+*qhQOuJz?PB@VFxy8b<8
+80We3>;D-f-Gbd>`8>rgnGt@@>@18BQpU#l>$wmwiF-!TB8d_-U-JG8Ot<p(bSFmQr;I{6;n*-Jt4`l
+&A53v2ct@48d;NOoU11R6{PUZ4yhtc0DI9;Mjk(@*zvg=4r;e^x+dAe$$1UxaI9fgCfrgL%1J_yKzLZ
+uCdAncwYvb5}u2Syf-5}RNns`pOWeh>c-Vajz;FU`DyzJ(^PaK-GhR`Rw5`lr|HQ#O32^ThGL2kbRT8
+Z6ePh(!`IKHY3!Ny~h6KJlfw<|AZT`fVEx<x$~Y$vLw)=X;!lF53`}Q#}C0{f{=-hu=u@AKjw{8>X@5
+M>FHMq8yYw;)4&R+%ojG0vLFStcIr=R^A6?x3eFURkDoo<*hsSjDw2GUyhu-{+g1YXh8;x%;qpw_XfE
+4!2zX!>Dw+_L<wEfEZrj@uuNC5x)El`RC>eL3G|*cE;fMbxSPay_U?iKU%fqtnt(0khwJz%Exm-bC}V
+y?dL&=hkOB7&^2k$9o#+gcDG(|nxQkCZVwdzI=0@)G0)Hj6nj#E(l4dErip~~<Im5HVX%t8g(4Ray>2
+O#TD@lMzJH)Jpm`Ap6-PquiyS%g)43-Cx+|`k9_NM9;cP>nYS#S2<?V;SuKzoRyf_^%aATK23F6I6$q
+Rp}tBS-<b!VY)S=U!)uK1!(%x@w#$IusfmDeAC_JXF-*G&@!dm;ZbRizXdEb6!pzjmv|S6!4U50MZ)b
+F60iGcN}PnhH=~=g(75KHgq*}vxZ&1r5t8l%ybnh152uOlM_f|ao8ID^1@#7fHHJK=^>F?%XDoNjOXW
+9M|O;RHx=<9EBa$MasX!IW)+`G0UXJ3p-b=q^zpmL%^C&4@D_|ZQ2sEmQ^cw;>i>ps5O6SVac?NCBB9
+&Z79ngtY+ktU4kPDX{1AHC1f)#}SJe1T;AzHh&cv_2^b$Eg!&hg54@=;aNg4*dh)die@Ec-o{qT@Al&
+|bj&Ca6s6L|2^>f6tICNHi^g^uQTiElQLW9`8@YOnp~h@7RgVh$a5TFp;$X8EJMeU9CAl9-%^G#6n|+
+?1c-{0YqzoGOjAHE_l5uG6K#AlCF_m-$ik2&$B*I}T@Z?{8mvJ@j77R(t$HgE8{=zW@F44z}DlN2<ZG
+lUq6MuJ-%A@d&pAM}vL^T2J^Y`ZiKwS^=;H;~qQGdo7O{o$KuWp(AtOG#EdJ-G|^{EFD|#$kwYie8x9
+KM<=R|mjd8c8V*OIl0yIY5-Tw0SP8(Y-Pa9v!zBg{ikN&?m2pH;RqPq;1jfA7u)36pBlPKQx-EqU&-%
+_|cs1`&)D#+wPquRZ{G>M!rJmN7dOGN#mew{p>>nfhgX1m5Mt!XSRb}je2Y)$`ii+E;UKW$5W@am*P9
+O=xvD{L>u*#lw1ftIHdAQt|Z4Q{kxEf4id<QUz@fQG-81pYes1Rch72;2C{^$EQ?<cRn`^&rcQ1V25y
+9FoW)$d>b;mzA<I)n8@#5P0d45C4aDdE90OgHK5(g0@|e?f4DU*Qz~+rTN@2lc=L0vJov^6v%&z{5&o
+b!C2stIt)J{uQ+*Pb=UZBvs8ysiSK5zVbi*d9M7+J6HBKt<#nj9eb5p-KJNK3VhNGJZ5mlhhXWJ_&QS
+inA0<EVoX*^I+2YWyL(A-)#`;&tm@(~Z|c!paIA93^|gCVL#a_CG3hs%d2i2c-~W<lQs5eVSd_Q;0_0
+`^^PnsD@EF|(WxR}UB5^0ux^B_xQIdPbjj$k&3hpGtm0~;S0VI0sY>i6?%plvYh;#(EdqEt7I&sut8J
+P7sH_>_>r*qiM-q2xiTe1G~B^!)!C*8k!3!f&HRb8V}jTVAdw*=5Mh2$yu;Y8`;b59p(c^3&SR0>f;V
+(|#}rjU!v{^{wyy45O>VYm}yPy%(Fi=YpX;|ExDB!Z2a>W?mmP=7L$mOt}^4JxL@OQUM-U3i!gqQ~w<
+_~08521DRt-EX4&At_*+8|8B;IT%d3eFzh9k$`*HIz4Uiqa_4i71xKS;4{W?Y7YxNWid}?bW~5*<0F=
+D=)4J~8uMv5g?g;`lz`gIb$$E-)Mg&vfLstdUj2e&m-r7yK(`G^`|%24YWa*!K87Qsy3tC|dfma-Ayq
+{LRg_S$jx)-#Y-o`A5Z1(KD@M{SY{@Y(2bv@?xlCj-yMPSI#Fbb6VCDK&9GpzEtN3UdFEa52q}4}BY?
+(D8Mz60O=~G&ZR>NVCuc<Dw4V^C`4O+`n0d>I-Xz9>;mf<$VR_2~>xlk5oM&p4J(7`iX%?w@-*`QkE`
+T>notsb5+{|#Sx_ZSL<HBe~Nx58+_`p)+^TAN^YAqgA?(z5`ThWykhmB#WxvH$%P9K@eC36z7s*UT|*
+RkXxd-{oai+vWGtIDer&pZ<sL^Xch-1QbKqui$!c$l7Y8pQsK7&T~vcFG8nfqWcPTDbmXY$H89fEP=Y
+t*LS=f>cSx4FofPX{!ZR*>#EqTn0RH9YyXP_6Ol~l{arUVQq5DCy!d)eQDTOO^mV(igD)l061ZpxwbT
+(wBslpUV*I{*<fONHzyMyiYX*Qrv-plHiXLAmen{z8xN2L?%Y)g6FJv1YZgM~1#HKYt>q*s`(66-#{T
+dLo16bUyyBwyACvKBIt24yG@We!raddbsb`*B|%0=R?k?ctzs38cvcmd{kT=na}Wcb3IZ_<(Kgc#!V*
+kfA~bvzzQVRl*n3-Bpo9K517Xg?oboZyziCH(I-h;d@i#LHCxDAoo~bPEpv94FYgCvqG=g!ho+fVkud
+eMe{L5lRmClvP-ApAq*>2dhoYx8TrerLo|u(6nLqDzM)`i41CHm%b8gH-kIa*^X*Aw4?iiyM;yZttkN
+h))@uw`G%5zFZZ!BxJ^i9Zm<*8KsV9FC`a9##hoYRKMP}R&fCJBw7h6c>V=93`Xx#lS{p3JCniwN<Ml
+F{$)3<7p9O!cBuOoFw*pqP7{?!K@Avm*`YRD6w{U--C*;k^Cm?T1HUsXYUZxO8A-+h`R3^cca{r{>A?
+IeN*vu})L&uvbZ@x4VC}%LWu-N|TKF0x)LgMb)I?wQu2%nr%1ak?}V?J!?hG`qvH_8I{WNEIkv@PYWZ
+cip~C{-b3!N^SJ*=D+oDTFhTuWuS}g?nZA8G&BJx74qzO6w}_N2NZZ4#Z$W@fG2??dvFh5~-3w@V`)#
+Pb&6r0nU;2su;?TrEupWOLZ|kRJiH*)uQpY>j7@~!>u9FS26eAxau$GoW~^|7%rj;80d<DcD`v=Rd&|
+<&W;!dPonA+rcSV+bEee~cE`)>^h)tpW!n`!dJ^n@6G^(Mt@xlR9~0kN1rwJ;T_R@Bj0G-q7AWlp%z_
+WC*2j?T`4H~zX{bRwkn`vS`eRt+9ebswV!ygGoCo$qexZk6ERxpucY(+Zy`3v?GM#M>_hyaLmoJ5qX?
+w#a10g`6I7GI=L+nNh+BCVikcf~V1ohAcy~&<dzOTg(9dXw~mu0=DRprJ|jDT^4(yslId$TfaV~f+;v
+d(J6`|<SRta-KwQWIcPtw;UiA)Ny`J1tY^_(75IzzBr)=JW>CaP*P<Fbx~YcZ&TkJ02p)*T=m8IqHq)
+p2?Z)YIN(gWqT#Y<+U9i(13w?bF=0b9r*LJseCN0iMLX7lzk_yjft0P4qQFb$q@W51y<8HGJQ@1K#(<
+0iZ830Q1@XPH)7&|_zPWSJyY@Jyr|5PcF)clD6@h%b@jJKb?fBcHZQukvI@hN8W0akr+LK6%eAha1R}
+(xr@({5m4>u&2e{JXr_aaq=2JafDQ-1YR<fAJa}H11<2$JsrPO7umM;t!uat-hxYG6bmjG3&vV}x0;e
+DV=$Da+V)EL0vtzW&&e1==U4|(}eiMuuI_Xnr*>B;G7tLhS<Dm)rK9rd4$$D;u(`opKshbK?R&-=&EP
+e#w54#rQ9Pln^C<Gy>I+vKLAYC_yo4D(lcO)*^jF1Hjb?<j6tlYRw1y$ARyMy|{{zoMW1ihkM%{d9Z>
+^wX~lpfWND2Dio%s!}{2`RohBRpy@oEcPpV@Y#eyH4y(jvAI_-YH?7%DlA9yzF5N-op(WUtQ10#fZ4r
+|5RiZ;gm)PwXovulE~2OuYeXzliF$K1Ec`~^^p*ozo;-W5XW3ssRgD*u<@4%+t9{~Vg3|{b+@#4*8w|
+A03}iOrW{SpD6%CID&yPRDa2`J$@P}o`cN|m{V$(tW1%e~*`oQz&0!RMY?vj@GxJ&x$>)7hE%j?+MZ&
+JxLzYNUd9mUW98Cr^APz|ehHJYq23qPa;M!<#uU3kH!eLr7#ksRJ(k5X6i?79}ARz+hJ$JQ=Sb;MHnr
+g$OYcu3iM{Bl7BP-N;I_=oTk)D;D~rcfo{5|J6RkcQD6JXp!%)ij=C{FN7lwzfacCF`Pi+hjywt3O*N
+gjodyaC=1JvDM8YGS6bZ)dt~4x!7Grk_$OrZCQ+;385osQK4=B=@E#vnf#xRh5WH%A3lx8z`|{?XpTo
+6doX;oL9_0Eg-5V*2#e&V%of>ZOGJ}YbzEj6J)#Zg$$PW*p<A1>eMRKW9@bD$D=@5>_z~eYbt`vu12-
+59^~eqHG;=I^C;g5)gN&f01xqf2OkWz7T()~hSaNwP^3bs4TY6Z5O1`BtUpp#!Ls2U!{K7%W>qg5Os=
+Lc#7dcdnQ2@VuH>nxZ`IiYvZj95fkmUcCkmSY){|ZTNllc{ryc&{R9gJ-ajQ<KrZXu4<GW2snl2?p>Z
+44z8jG!52>7`BM4#A4hW<S`BBw+d3Ho*G$o&f7IEsu8mPOPjJRefxpALF+0Xn=4|TR_!AhtF8K5?NhW
+I6Yl#2UV}2zo-IM*X0-RIo<k4zXEU_`tE=!JqYBwWN+Ih%7t8)ELP`U$RnT6kb9vOiSfiBXVzk_CTJm
+--jNs$51L(%A1Ky|2WGB*rhr7%AN5teOZ-h>+Q)RA)xtt|sAy#LU9r$zb~G|N^y5JRwUrfVl+z*$f5w
+~^*@Vn#3G!E+(-JIh3rt{(>hLZBnq#cL%i^@y<l>m@I_8iMbQ;EG=n9^n5IBg}K;@##e4EH2*Kjl8-U
+HA{%PdPZgtkWrscgwVzI%d>N#$Wc#4Cu0LBy}4S`=~o`Ln}_Z%;--L$+IXPvf9fHBzivn`(nspA}8~D
+?|t14ea|qh~3Rc#*4)LHwODwviTM4yC!ZJ3<g#RvNn<I-xTfJ%5e=&pkD^scawhXGC_Vev~S#d(9eMr
+wLg+y2j1*?iT{O3hImY{?TMz#nAf75OS!sRo~*osiPzMbY*WU>U}Gu|$AYKLKay8{_(QhA%Lp*F?aaW
+Scc3Hh+l{l@WSXVDou@9Z(&23AGTG!4MA^|apLnBiOHclA@mt%$_Dx+#<JZiwuPbvbs8u~{pmsolw=3
+Fk?&DqI-1Sar5ufyNbKT#D%5GFy^s&lGV@P&gm2YEQBz5!mEk9EP26pvF&t0T)U1~t=ja|fWXLopdG(
+4^^F@!RsYG#bk#IXzK?vxrJLpA+c%!uV@)dln@)kB-t%AO$XL$a#vq+rbce#B*5S?`2?e;R5Jf2bat5
+ifA;)g0>|Dw_<~e6_9S>#CY>wsHHmU(wIECtC#c_6-$y?hdGzRq9XJnVztF);X7pJFm^c-v0+sO9KQH
+000080P~d=M_?WYjb;G=00jd802lxO0B~t=FJE?LZe(wAFLGrqc4cm4Z*nehd3933Zo)7Oz4t3Tc1Sw
+WO6-WlXY7Pf6u>PWA+3^tK-$O8Nz+nkEQi!_-gBOxOP9WL0XkpM5c$Ux_SBDIYr6e}I9&soqBx;L*f6
+#3bvV^}@8egF=Oa0*FDR92$e>bXSrkWX;OcdYvRoIyt9N9BTztjc8XQ(Pw8zfX{R6fKgWCQKiQS@1cv
+e^Aoq}BDY6&z}YdQyw1+r0;As<+Sj570mTv^sk_;*J(p_WW8Cm=Nwf_yBLnX_6+QF5bpm*5a5OoF_Fr
+#p}b&4FzVLUdvYwhNr<_2JB2CnP^~wTA3jz7Qq23L7Fu0|!SY2dS?wn!#H^)(35jU`iEu&-BX0Og1*$
+gV>dmTcaCnSsZa4Hz_`9Y}<-#(}`KP|8!cgQA)*FDcEd4C>8Z6B?c;A8?-V`PP_J2!rKNupH=hkFhWP
+zcC|~z(tO1ep68v;XkHM8K4*;4u1xXx_w8R$O9KQH000080P~d=M<IK>3eO1u0Q4pR02crN0B~t=FJE
+?LZe(wAFLGsca(QWPXD)Dgy;}co<F*z5-G2q){4hD^Q=D9j4%u8ZyGzkvZ89WjZ^i8(&=M8%PL?#1ie
+q&B-}fG=FQi1-xgwi!xLD-l=Y4;Cq<nNl-WKIf@Q1Y`XN%J}ui?M1$SvKGABrqtVsdl@|HzL#X1Qbus
+d5-3YR$++Nn`j^&&<g$Oh{hj<Sbl}012k`)O7Y2gxD1o*#K@{C{jrV7+jK+XN<6?n3am~oW#YZ%s9<s
+Mz&n7HJ=(KM2uf+jAErIkW*Mt!c%G!5~^yU*GiS=CnwwOHl$kEun-R?nIV%WKVH4PyuQ194FcB-e$F!
+{B@yi36&IlFYDZ`Z0>yL%VrFzp3PI?DU@)f&6nHDR;`zgz$Rbr+Dj32fTq?m=m2&LWhzDBR1Yj|mlj+
+4Bxw@N@zg^s2-OUl?{nh;sH$UH#_ZPRf7uWY!mv`jmmb|^Wes^_$b#o0*-;;~$U&-IEuHVfG18V^v_E
+ZYg0L0{INtS4{-7)5fkQPQHS+ba?JO<VBhl)Nh@=!c7k%LC0WMac5dQ^gd3BqK2!xdF}XrNa(nE=Z|C
+?bHo*i2GUY)Hw=FnVM#pe4d-$)6fZxs%OT#W$?|E@;eFH2yr9OpcH!+|VbUD{?~4z|tJU;~zV<2GhL>
+&qz`*ng2<VHGKr<Tl8TVPJX)hXY~H!>Ygmg>EfF=-+XoY=d(9}o3Ir8ti&!jPN;a0b8>u~eBRE^Cj>s
+z=~VxH$Kpaz1;VFQ9;4=<GR07pisp(_#iTaEEkdE$iq$6;E4}!;k_x&Z2LGIth2)4pGXewEhUH3<l1g
+cUX&(Ygc1}LC-L?>kclDSIjUr&is?^3^6_8ioY1PrM_19oVs3eax;|UlrSM*6Q<0;8l9%v0_WC=FZYh
+s#Zm=b2<!{TFYHvFJrw}O_D#0X?T3UlqGRtf?jL<$LW&P=nkWJ#0;$BkQBuh<`ux)OP-b1(~0s33&})
+;gDSy{2W+%~~^!+^uScie-`wmHdBc=f>zusS0TjXAnXi9}_u^PAG8{@tmtD3M9+Y`9M%2RZ%XliySt9
+E#=C5^^--Al@V2nZAum8)n>)SLGpxUbT>wY45O4kv1BiWsYlU<=a!)DD1F+y7>bQxm~CL52$EhX8^oD
+yANB%5iH2FM9a*%gGR3h>3mep-7#C0HZB*ou**>AN9HfN30XU9|htc1GeNQ3e$9#k3aKBp)5q#nh38G
+cd&La%9Fd7LNDCA#bT^gH)?G^EK!j2J*anjMIcZ!9>!i@Wo^;{3I3rqv1(X2rSsT=p9bioTy9ga0T1n
+VaEA=0P5aMsK?XuUJ^K!<7Ki0hdiau^aF<kUWNPTNOxfM@&Q<Ll2`<lAx`AsKS@6CUKMJ=QifUanr(^
+(cINx@>g(W(^NvyU8wfkI4PayBk6iXcE@YqU(0VWD;31=+Jl}q($vabb>V{MuIdSKDOig(;g2ND5ATa
+c32_qg?XZPvQQ7x6cwnEtR2*w`+pQ(19-r3(H`6e1uW2#cOWdQVH&@Tug>UZl~7V^3GIRw=o}ikU62O
+7;h8=gq<FOHG@IFc<sMcK42}m%m|j5x%npVIhLSfloQZb4-CT5N)Vf)Hkm<D-PRzKK*Kh9**YTrX70H
+aT(siMy3OB%b`tsD%FXCG-9b@dTrvm}T%itFY<8m7jX}tIi#PU?EFOtVzKPJvIvP%cpvLo+hm!2Nau6
+p%Yc7a}dv#V3>aCX(K-0Z?ZC%ep%4o;|T3x~HW%Gy_W>S-GHs2F*umu8A)Te_1#(4I8+$nVD)4i0LCK
+8o>lXK1lttajwG#ZbGFNuLEwpSw@}n&LUS*QB-LIeUbf{-t`i7`>5x&+E3}7@W>PcPtxjUv*fNf<e8k
+WN&}012EE;Cr}H8R4v6uS|`xybb5!yTx08Ng$A*Saq{hQY!?oj>jwPP2h9<IG|Ij{->a)u;(w+o-a!$
+4ZG}&IsaI*uz^Cgz&K`x$(V^8dFHALiIsM)hcrT_wK@mc;3^r6KL99(EpB9TwBy0P7L8}{i3^<{iqJl
+jM?{LxlE2g%LK@p?z+DPdWv470!B(^usmlxTfkICHc3Gk|{*9drYF_U`7=ebGa!P#%_Kfuj8y@z}B%W
+2<!ug#Em-0(8fHHJwi?Y<fhJ3;7Buzb_5W{&kSXf%5mOa60?YOZDNl0AAteMN#Xg05LQCc#(<dreMfG
+k1H@eE&)o?rzY^vluwv+6K^#gVyGSya>3!{tQ~=F{r)98)@#25!YjYZ}%KwM=UeG;AIVL@87j;4T)u}
+da1qu>sC4~&w-`)qOw-Xbx~!ByRtcS+Dma7bTE7^t!vtFO=Z^$^bMdF|7SVcaW%bY_(-#geK`HtMeeY
+D8HF<nyI8Zq<#OE~wFc1l)kg0$pZAYzmJM`XM!JqH306Y^wIvB~AjJQI8N?jkSy_2ExE?(1YRnp6FQP
+MC^+1{7oqMy>FUdvSQKfm%7j;hh38=m0-t-%r4>_n62Kr9I0VakDlNQ138B(a#kSVIvyYc6#KI@p{xD
+#;0Q0uv?@g^KF0$(56<FvQSIpj7dnw^vT3bc0#!5=JhK4bKoYTRI+kx|X(2+v)hfO${bxJSR8ruJFO*
+c?I~-)+NrO%>1=z~;^+OXg%%Df69ts-$_<O(pX(G-S3<hEX!iSa-F4p>rujAzJTG4zD+j;<7t*ewiD5
+UhrNq`Xx8>ct+|PqGF>%$BW_xX;sjFU+H7`VqRkpCDajG@3VTvi4N0n(F^%rjd_Jdrg?eR<Z1iv3>P%
+r$VJ}F6PCxcgzBl^8B<*oF-RzZbO0~+-Z0~@92iC_&}yW-5F6;W>Xg-m*E*{-Yw`C9xphQN7+#y`t2X
+FQ*LD9@#Bn9?2M$Rka1J5Z6N@Wc6SfMp_1k?3XqXR+l9Nn!QAh6?ftO~jJ2fca`(D;KOn}yT?j&wX+1
+&e9YoxusHMT`-IA+$rFX64H>$m^0zR;?()v|fm;$5XZ?s(sz1lrEUlg*1-gyxCnNNus|WNuDxD~4V(w
+I%eb(xl#=0>}H(dG?2XF+t2{RTBOb3Z|C_o%=s5KF+OZfF8toJUh^mbSFO(L{@kTR*E`2E#E%F2sP@o
+c7;4Azb$^B6Rqv}zw+Nsf1idaW?sEB_G5lJ4L=n;589us)YOq$1+HqdXDHVf;}={E-w#RAahkdB^o`d
+pdr8?ACp)y9@`Nb>C~6q;=9gl196s<z48*EDTr&_Y8g?RDa(hl+sdkSxo>U$mH<s%(`2eQ-=-SI+KKU
+<DO9KQH000080P~d=M`CG)PT?E?0N`@~01^NI0B~t=FJE?LZe(wAFLP;lE^v9RJ^gdrHnP9#ufS8MW9
+m-jOPsdzUZ%dOVk?c_`Rhqe)4p7WLy?fgnj%?(w5_|j|NZSQ022I^EXR|H2w-=ySnMtqAh{e4haa;b4
+6=X=wu)2sn8J0mW?t-bmTlaOrFkS+9I@M7wt=5h=0-kyGaL>FgKyY*ob1wIy~&v6jo9g{*RQ^V|9)Wa
+c=U(64FHR0JQZ;?_y(YFdAbdR2!Ko=*oLQkxnt|pjWX_!*(&9n#Vh7*+;q*yEQ_J59ZLwG#mmeMB4o`
+4=8_$d1K4Z>ghaf`9^I4^XRZ*j7q~!?`LUO8d6c;s_P7c{7^9VKI2$e$#&AU4_}mSF9<Yn8x{*BwFyb
+5}z(tk@9#R<tkQe4Y@}Zl<U>m4D5S_#!fE*Cc1&jl^9kXrh2P^#LG`J*RhJo0OnI9nWWu8H$z)Futhy
+`Q$PnZwELu3tT;0)1-OAZMN`$<r+nG%)2n#WDNt&S!@o>qApLARW+{1`+>z5c<y3~LZ!6^CK`D0Lw61
+00ulOVf7;t?n{@;4~yT?NOWo$C3}6i=>=l)g(4<7_uc-l7p^6&TeH?De@pP(1XAYSrVtzX??V|Wa7h=
+EpFc5eVWXtY_?#x^P6917t;$ioGhR`9J5ceyAL-X?-)SLC)ansvzzy9a{W8|`|SE+%%;EH&Zmn7yO{&
+A+12gkYznor>+{Qx7qjbM*gL?yzPV$Uv#Z%1kiEO1E)}uabb;iqrt|X;P@KG*UC!=)9|NiPv%71g`~G
+ImChT@Hznh(ZyqwJ0?Z^4;&0-2HTma$g+4cK5^fSGhUf<c!FVwN=uTWr%50lGF><p+)KEfF0$R#_!x&
+3`U`{lzO`*3r4F@?%^Q{Zm$?s6(SgQ1;YPG(nQb}_k{{4yo18=!+DL_o<S`}ARomDuM5{+-{=Zmw}K=
+Qr1Pb105stn<49`O|DM9ka=Nwm`AGpWj^JFj268b3<eR^Li@jpwO#x10Y!bxR@3s*~N5n3Dg#dEC;H=
+?ZIG`g33D1D$nwiI}QuBs0pBPA`Zbk99bR=RQ)QCyey7Gp(_ILG?j`Nl+n%NRF}oBXv%g87lVQ0xH%Y
+c3ca1NVZ&*$pO&iX=Y+hU9jEKR4FT-|+CfJp-fR26?N{IqVK^TJU*#crQUKY3hP$cYOo4;3uU6poP|s
+1t?ZNHur$7N1vo*yaTE(AVePL&3>=c^cbT_{NQLGq%yd5z3LpKtw9U2)TW$Bpe1MseEo)!&ImJMDAzO
+tysUs<2=r;Ms46{=eXk(=&x?Q)l)^swP-^4ntee^bTf)_oGeKly-%g12(DbOo1H?aW!o9Sh@VJ!;|6A
+|~bJ+2`1F2bZG;*Lf7+`qjcYjz@O9B8HRD&0w;&Hh7;j6X?>d;UDgUI~+;=@$Vb<KXUDJ!$)@~(5nmf
+<`hV0!Y-gnNx&E^>oxo7C-&y`h<(RiOAbq+NTRds1pH+T&T<)Y^u8MxTr0{iURfjp_8(`ue7kc5UOL&
+R?2@FzezRh7;0DL|=kpa<Q^-L{Em%cXwPyTFIhb0+6stvZmhP&sK*ajZ8jXrd{^ao_V>h6cD_AV1X&R
+?hly5*l-+_QCm3u|h6qBU&<M#*A`tePTmd``>8yF_Z!u5RzLIMjN#~6bHB@qZ^CVo9yz~Do=7_?K2b`
+TURYzL{3<-@NgF8zuFt9%}ED{{BIq&|~AN4AZX)Nz5u)lJqicLgqoDqv1Cpqv$*I-0m6=?;vkBcme<;
+xjgW0e+-bIG64}cNbiEX{jEAQZFEs%9LV^nZYjpVWo|AFhu+@@z%UV1G-?0iGc}`QXXb#XGp?<VjMnj
+Yh;iH`DVn@xa?=F5JzYg(L;O>_T@MNyRy5r5(5&5IN+a)rpWSj2=Ns}Qm_&*_aP2^fx)YiG=i-mf&}M
+PqmuG%{7?Y-D6$}dWy7wSQ>Zxy4Um4xF~-m^j!CBCx)$q50NW56O{Ek;F@IMOXZcrBoyxh~Gl?)KAw<
+#<e>}U6qspA-uozkfQOWwqE~r!oVXiDj9Z*JrBk`s?!A7MqW;KZjloZ?eV~?F1fG`YWpjwY=ZcGWTW_
+y~-sHV0!dxfyDU{F%HJ&OC_hB>w?LYAYgnJ6{%yX<8wQAc&IskUmZYPRL_CJVH_9d5WZ$p)=GlPm{Yx
+HDB$x~&_`&ix>5t|mB2T%A7;6$ZrMaEV>P^_LplUjY!Wq=Ugg2}`&qklk-lMIBcgkN8p-9FdVsLHL+2
+iCu6Q0yblz&j3A51KPX>(#9Gjk=HL(Lo1B+nYM;%K=vjtum<}f>y!0GvE3Mpj*qrS?77z_9c6p{c8O4
+J)aC4Skq00>=58R(2SK$kf`3hK(1NMEY7Rq_R<H*<nYK~yA5a0&CDl5=dtF)Ke>$C+hE!Id{m|C1b6f
+hN!lx{Prz-%vb+e5OL3mmX6?KF$VOxNMRVmx3T^Gh8vEG4+;K@cC612^Spcl96D(bN1*k~n|-2zg`F_
+6M_`F48y=G7@%1(6RV5j~7uxP+Z=rY&}f=LuWr9OGc3hGBSd-emtE&vn8OR@Ok!>6F?H(Gh`!ds7)ui
+^;+ulu(_XaHj@Ug}IO7$4E7!6ZzM_jfutR(wH<l@ENnez-}-^O2X(6ot~~o$5+6#XlkX;&n2sWWkY)i
+|7%>>5n_)9Ll~)57}B8474&&Mm3`o}w)Zh6Wvcr<FfMc4&2Fm#Fu1F8jD+i9>At}>0?6*smt)niDyaK
+A-?fBusT;s1U)mToTSGDksp}BTgStd<#;_A@>ZVAIPz6z*KN32-^k8p|2I0cMkWA}Xjs3zi2U&?(r2|
+y~gCYEKM8=#4B8c#G&*K&ug%O3;Hl>m*{NEOWHGMe&I#S9-1XJ2<+gO#E)WlM;oIp5<>)pHSo*B@N2L
+I-gMwaHF_JKUJL~vhEHSJIoN#!r`?-PJ=uFg%t0dHbDl?7>nKTK0Rk`)Q}m~`2Kykdc@yyF}Ddj?!`b
+hO|9VC;NN2Gt-pt|(&Dj#;C?4J{V}H$y2A6c1E-<uR{Yrj*T}d644ieqag~MN+I2+&7u+;STkjKLAD!
+i@{D>oiNR=)k|O*sfjpGJr%;!Xpo#hSLBMKZZL3)1@NqW1=W$QvCxu2&oaIhX7F7uvRFr!jcXTJC<UZ
+?UdwI16g)+z$lKOU?~ykpGw7~0k|pF?5Igi1bjUWl!C1Uf2x9!ds2Po0@mE0zV<mWfs)~hW4)tKn)KJ
+2xt6o|w7tFUouv(}=n#B&<L6qc~+;1q}<o3g4G<6@H0#06ZQiJ}Y7INT9x^T8`(k~_jfIl51Qa4)jJ%
+e~EfaFCNDQ+wvEhIs?i3Wjn6Y+De4ZApU@4^(DkUu$p=5a>oNdP-pk{{ONg>34;Hr2GIvK<&71y3&mk
+7m}q_Tn()badCrDuQ1^IH^(f^?|68KaE_L4nTkUiu3)?V2}1G0Q~7D;oxaAm*ej-&l;pz1llEWb&gSC
+#2l*0h=%pzXcepzyjwyp<($g5vfPf4>{6~|p6ndFZiDleTW1~R2)Bl7{DmJItJ=UFKi_V5PPR!o7zhH
+_%2j|i5}PbbU@?jB1OA7bN(QkI21_hpvt2gtp&+C%@Un(1I#9QvgEyEmg^Lwb-oZ*<)Yt_`Cy{b4KHO
+bh-WDZ7Pr4^YG9L!egXXw0a@o>`$+vD0I@<tN*KH2E0-X>Es<*FZSJSIzHG<nE_#V1Z9CeU}SfPu01!
++$QcpIup_^whz1PN#lA-q#oCG@URLxc#ZjS#|qxVyVu@bp1WTeF6yL&#zqjq_h-RfI;xJ_-wp@v1m&b
+^GXew}E#r*E8cXkymX9f95<VnE$1VHjtDjsnSclY=sfAch56X@=oPNH=~B1Fg!+&ugLBx=0LDJb=d>v
+_<<Y7<nu0yE7fMJwmx)x1g0eM9!gCz2239*;ib6J`C8u>s;sqLldHh>5+uDSxs!U*<gWtPM=%R0;<%G
+P8XaqUv9fytS3epHihWqHdeP%6LQZ6*sA-gyy)zv7l7_k{W;g0pAmAX3Q*RT;%1dYW=U8*DYZ_;m244
+nde;3;=J`q&T$T`*MIqK`e!0eU&eN{!X`xhzcDvp#gzAvs6fw*H0(x_W51Z=vNvC^I^S$-E+w8?<#A6
+8wvv-_%kcgt?CzUOHOeoQXcKw584>D-P^ZS5L_!jNltA4!gT>Ryr>LX(u`Zjyb(dfR%qOygyomBC7_s
+2DSA+h{#)!!+@Xutip=1wt13LKlTEYRlYjSC{kK^Kzk6gnE{`#~@Nst)ngh>dU~qPu4gEP*2+EJCo&U
+guDZ(Zn2j)Dyv6w@+M{bnOt!gVw>mN5@RSQ2+uK#UPc3a;zHi?F!*TR)x*P;dfi|kZ!(-!4^$dDF&qu
+r`Jz_M*l=Xr;iCh6dA1Ktj%g4_oY>IWwZ11(wJv-rc2J>z_?Zob*mHUcKxyuNi#2!85N2n`qf2K06fe
+OIk3j|)*qNm6`bgwI;vhN_)r5a8u0U}giJM0+={b*%?z=ddDO~R1XigI5;`z9FbY!Or@z`!t*!ALx?(
+<Pey0C*tcSqu8g)ToAGq0>#2ai>NPeTjZs>SQsN`(X>o`tb@G&(@I+oQY(p_}r4K6Z)^X9W*?EvmMV_
+WJ4Ss@l7Tw5+q`I@pWZzB2amHRxFV_N;Py!opEA1XY1(dkksTYWeP6a49_eS_`g>H7g4)RUN+Iew<%U
+s{!n3Z!Bw|;`zm~xaY9<07Jct<HbhbxoS1~f=avEwvYBOwI$r6)Rr)N)wZ3l=c=vJl`gfdig=&e7W<S
+o5_{D)<=~w}_O%pOk*lh%sytkI0WVH?)UJQJi_ZI85c`Lrg9Lbo3hyC-r}_R%2;j|5oZC|XZ`J-i_yB
+dPX&i&U=2?=)PwJHXx!7~ne9XviNN5)U-r?_ZAbUul`?=sN7h-*KA0OPv509J(q&4S0vGY<o@<zpN8h
+c!bBgY2VPrH{eLMPoX9LO#BgU2}auhw6M#-||LsQH?6H{2dYbFvg!>UtTBFYqRLwt49wzX~CmuCGE#k
+MAPDTZ&&}fM16ajqvL+^-}!BO9<d`>WlDS#{aqNZTJgme2nN>y_brS{N~F%6+opNO8nud6$qfbkh@Wy
+9D@zPYkl>7#x%N-w|fx<TswEpa)(a^?!#{nU%Min!va`^eN+gmaC?K;F7BQi#ul+-XCPY>^uAD5gV%%
+Eg3JNotVrVM>z#aRdi8)-)*c?wf;9C_?-p%ReRQZJ;Cf*!_yz)yV+r7^wJ*W#4rEL8E>q}`)uFZ^(T_
+9+iM+=eIv9MuIcVMMv4^U(_Zx(QZ(4*VwBIDk+e2T?DvUc$Rl_J74!4atBH`j)K8JJAjjj%T>tW$Ql`
+AP6r*$QMzvA_9`fS}ZC%Iewns*BKYG8FHy2<U+!?sudlmeco+|4+18;VAcnv2Bsbh;KU_;!gesNi$x`
+Z4hm>fpzX5r`Ns-%3J38xLl;9Sy2i-7&a25Ko`NAf8&p4#X^%Jc%pK{b1z6I}mm`Plpny&MXq?c}Y?|
+k^cFFFiytogvN~*Y8diTGDX)V{gW@x)b(!tK2WDgS8+J9uLpT^wfe%m8)3?gExxl*Dy3Gh*1ORNq8tU
+0Q+?12*m7;Dy*WRp7TFD@2WfUxc^5{%q+RK?d>>`co7s)mYS53#-f&jE%yB+H`>mjAXVx$RY31IA=#J
+U9>5zTPXw>5o%ENrm)4`WQ9DL{+vy3=&<v2s54V6r`;T5*YMuNnEC{MX;SA_yyLf~5vvKg+bZN5CN5V
+BpDsxa#?Ub>+IQ3Zr3-_)rNA=uJo+X$cuFS`a{mfJ>96uO2`spuGkS;iH+&<EFlz(*w<d>k4R%FvspP
+uW0C|252>G5^wRsu)&~HJw3_*+|A34e$qu*0D?;JU0y8CA}l6FMImeAo#v#vtu{fSvcu<2J8$mGPuWO
+snBwIL-Ag|%^6X;6`}*}+ccJMzL<KdUlGcqWt>O8qtj+ss-nq&YLJud(bDEfT=gE*nM(3<MMYc+CG4!
+60U3N5x^^@hw$jVeiMOz%(uTCkv(9S?OaGPc)T?zu1_633wfr2K2U3le#4SdjmVRX%VfC6b=v0>?14^
+5ejAa2_>n}W$$-q=A!$fEFZlOw8v(v&WKmZFBgVsxuL_r!SOQm9>SCj#SUZg5~!^p%^9-$jUjuJ6+2Z
+pC;MKire9aB1x9y9=`;TV3U8&3n(a7%a7jjCYU%nUulJ>Lln-!1;j{bQR`0Xn`-Km=jLCfG)KC-&nju
+r>+-J(snQfP`wp4+D|y$Cpqh{zJeYaagA8q|Ts*1JV#%hX%A9L^r%_H!+CoZUkxu_^Q)6cv1?$7fg%+
+`l886Uphf0<nty;Lp*Du3Yce1Rsn)Ih40gI02{TrUJY5XutQKLvoO&Z$l>`i^P`*YY<%+2q$63{LrIN
+oA1&h4bi#c!EnrV4mVr%LknX2XQ}sOhpC#*Gdb((OsHG*EDkE(9FqSpM=X9d)UaMUfBIK<IRDpTiHn`
+Tq|5Tx}wpEhhz5c>}I%BWRO~49sf}LWPIxA>jJt>2oWd-9=dv3NOv(yq7zSAeRa)nIL<;pCd#oNm=U6
+@RZNiSrd{<WZ^x#-r>EOv;M;h4P!Jz559B^j(e1$+|~RYWZmuhf&gk<XuqfC&dM2r3}}S_9y<tW*gTV
+=C!aEfFHNb%~nZA*OCo1sOYI&Yd(nhX^2>{+YWW#?SS-p$R&tx3%Yd&7VqGqp}KI1Csz%uiLI@137=6
+;`H84Wqg9^MKd?@-7t<gTe{x8fcR_EuYf9rpe5{-C!y<cD?RzgKVV;uKX8Gs$}1R$3(rmTK|-Ok@?uc
+a16@TUP;+c8TNsE_AL>89`chg-IosAUUyWJNft9a52MW!c{^IRjd|nGPty;zFoi@IuJVxx1sUQ_yFw~
+?}3td@Nn;z|;J^AysBiXAS$(5UV8?dC(?JdU`$|AEYBp(-2j9Rqk)Q)c~Rod0TM{Niy_rYJ>bS>n{h*
+xR`YDsw{XhRAktBzgi%NXv_of$Ot_4cxUI(snJ1OCK4$4Htc&r<Yj!61DEAc<=gPdS~MSXrW$w=v6>E
+oo$AI=0pXO*&IsmRO?(3hb5e83T@908`A$ye!$W?c_!xVAhQi$I6yQDy9-_P;*W+a!VeofY30a7mmsb
+=7h1Q3e^l6Q$WfyE^}{VlH~H#);3dQ1S?IkT>`6AB{PDh63;KGY#GTx$dnX?GYM|5^tPh{g<TQ5-N~?
+2dr1e%buxI6M$P5PS-6LEK~^zpoVjt&0&(CklM<$T8xl;5S6Q+~a1i4l1(TCnX&xwIZPCZuQ_96_y&1
+F9(c6~(keq{OmzJW3*NzO`-s+&HtZNFx)UsMxo{5&ol2H<EE2DpV5uXQiY?{CU6dOh~NMIGodR+!VK)
+6IxhEt+YDssdAWqrd%RV)rOnn(QcJCW@|E-}g@4-6cNBxK)H60%eKhl(Yx5@1ylwv#xaTqk%V5~VLPA
+7aOf3j**~U6-D=62M{}es1}0=GFpOOv?u@%XoncfyPE=<(5Rh2qLo|OOh3UZ`e#GEkfooOAzh}C|<`j
+&GL%tfJo+M3o^UTlx1+V0_#CyBNSSLnFi~MbUXAau{AH+UDrIP{-YZz!q6RLV<>A2g=sLe(s;e;3k%v
+q`6+;5mx%F4Z*~mU$BIIg0RS-OxoCQHBRroA)Y;T~On_4~3SQSasA&ujJpfNkfi<+qm<9kXXQ3&88b5
+bwUQKyO4yUG+L{e0etm;2$@pw+EZiyhL+gxOr2@qMu4+6L<bNJB6{}uVQk#l-Sx-oJDJaFUy`n5++Bo
+7=pa(ckXp%XdmwlKvY7ewGk10N4B@WE3CD7nnUh~J1n?`DmvWRZ7skhMSp*-6(Fi!^#~3KL-Zgn5WS_
+1`k(?!C!qH<RGM!yq!!zFWh8M8m3~$@}951RM6{l`Baisa8rrp~cvm>L$&Vuv%2K2``mgF;K2EFcHC6
+&jpWQR$;485*N+&A!t!#ax=h8RW7m`s2I+Ns(_7AXT0+hg%NL5$BtY!&(wl@`1)%up5U^37td9gi%os
+4#X5i||5$=bewteoLqL)mF6gTs9|_)>04?+QP{og9d3Rb`ATF15Cr+h`AGL>yuS(f{KPuyt$J9-Ph5s
+j*ClB^l!8+ppS-D;RG@snwPUp^Kv6%gGjUNtK)NkVFiI1PWX+ipUdoj72E)2QrPB8~@{pQlDa!$~}+E
+{lt0{kjE?5`9!Jz1iz+fLvbDLoZSMifciHyM(<SlX9i+FH7qX5kggHbp@#Pt9Pxy6r{*jLQntBA`>Je
+yL1ZsI$t)xS2~rH=={Sw-hKX@o{$+_}oS90pGfH@39w34W(!m^vQV9w}<=zwn6xTBXwt0eep(pIf$O!
+p`#{&Id<_bSdsDZ8|v2eF!Q0oIkib@RY}xl8*dOkI*#RqLg%i=q?43m2x|OMTR2K2<&z1l05lH<@D8M
+{Nf-T(*-6In_z1tcK{Y31W{t+|U;nN=Q&QoVu8e7;A^F^k<+(IUkZ#GU&p-9gqp9C8Vq<V|8mwUhLV+
+B#6!*Jr%phidr67#u-8~IY{^`VVsKIegs(}_k9~x>Qa94bw`~6H8U#g~s$C%VTFhoxrVxdW+<Qbi0fi
+Y?dfdEdl3{L8SR0Hl2mgJPLT0jkjk#j1AD<44`D^ZgUVX+`w(I##FQbpwFcAQha<U3`CXzRYJEc81uw
+1rFwh`<7hpOMfM9CG+Dhxi!)m2Xu*M1UE3i)|he`)Jm^!LLc^489vsK%Ma#pW{jBn6&z3G_9C0=j%`%
+=HS@JiYI8QGGR!8zCwZLk(BW|Ny44}C`B<i`H?Qe{<R3`;O(?>N_L_;I*~DI{V|WWX!2Lwh^uh&=bl+
+$o*N-i2waiRfcvbuC#w7nf%R_%Ab4&rxRz>kqUTCW#TwZ|_H92cJr13TwO(J&`B_m%#2b)uBF<RoZkN
+7G*=OJC9JB=$U`cK1PV#K^-G7}_G?z#X<k#@<u29C+hg~q4N%cOFJy45`JmP~jMm}bNQJ?;rok|*vPt
+_%u9ym{Qm3sb|h>!y|z#-&`%`C%>$2--sChv#bPH!3JWk)r`>+gSfO&EksNW}w$m?T%`7iMpM{E_`U=
+ajw1pHMJ>Bg0@vogIRpD3SxYCLEtt8#Vo+ZnKLd@{Ztc>TT%YP=5JMGp%@IKch*alK8*6X+RINe8bek
+fnb$jts;18Nn($lHi+oaSiWOY@sczK{H?InOt=xy+Mx4^$eRNpiUQ;#-twu#GLy9W(jAXfl5OtW%vyj
+VBov{LPUE-#kTkjMc*_CQ6)^clM2(En{78wY=)OoiTnX_j@Rg5Jec&lS5k(Intn8Q_p9}GpO(H3lp<Z
+}~`fGY5D5*1B{DPlB5Nvq`W0I>Z-8@4XK`+1{xUWYtdazeA-*QPCp{{xx?%m@^40=U(jd?Mq#h5=$5j
+<!Z($}yIV$jG_{Hi0~u-1|yS6h5k&Gd@O1JV=KmWCe{E(6bOhi&PdhmAw1;<;P-iGb2H8sFOrtCRaVs
+IK7Uo)$rk3CFJo1`+189%F2z?=KghVIXf(lOe-5UF<=nLz|S?w~9xFM}z+dP)h>@6aWAK2mtey7Dsmm
+g2n;?001-s001HY003}la4%nWWo~3|axY(PVRCC_a%^d0FJE72ZfSI1UoLQYjZiyo!!Qipa|!|->;Qo
+dP#|N6E}gmyicPmdB~hSgCqdD>SGE<w2|ARMNIkv}g|E*$$Yp&b7BEScc3|=lZB%-~&=V`|4Syj!ds6
+Ew-Bx-W%;-d8m@HH97~}vQ`Dh&;zy?8%jIzT^hf&~l*?M6nF3JNmS+>bg_;7SGF17phYc4i6;nHQHzr
+^mqC?f~>{;3PaNV}TeK<CeOsE5#Q_>ONBvT>S_+4i`!o4vW7pg|wy3b#`jMtJ8D8~DVLZ4%qTnL;BuR
+1)JH;6>PE(U{a7RdF0h3Ykj6Hn#hUswS1zJ#|{cvCJN0S7c3JuV3;X;Qv7j>B9lDv0qS20|XQR000O8
+^OY7yE)(3O#svTXiw^(*ApigXaA|NaUv_0~WN&gWUu|J>Yh`k5X<{#JVRCC_a&s<ld97D%Z`(Ey{;pq
+fa}Y>sU13evtqA4;+3F72vZQE=wm_B%j6^wHWKtt(JI2!gzB`hVB}YZSOh8~tyf@xG_waH6&z?MkLX|
+=;j$vi1XRqi>G?`3JrRBQO+``-WTX^x^OW<-Xw316MEi9S7<Jx3VH2?JW9b5_dFq=JmGkcKAf{@qXKP
+&yfWO)r)hLW3GuDRd1;kA+r16Bc(TTm6)TV?oh6ahZX7T;vPMh8Mkm_dAKvO^QYAtYvNvapd0S*fdUu
+cz?n<MZpZZ>d_<b<bQ+JTpx#Y?7iGr=UzG*^;ABSh=sH(A3OUN-u+c`|90<mu%-_|5K6<C6}zY6_WQQ
+!>4`8a0an#YEHRlcF)78K(NA~(wu{_8Y?`7THJAnkFxLMDJTu`M#vXOF_k-t@|<Coa<qIEm%L)D+UC8
+Iqefh)Wg}`%OVPq3zK$Yvt+WMZY`avZU3G~H@0h%Hx#8?i^9o(xtVC^v%!31rA*`av>g};RN~P7Zvj(
+m8v9m|~zTl08QztXmT4`@WRJ#JtVDdLxvx(oK=Ky|l*5D|63AfD9d0?r^Rn4bxv$8<g{#w?dTFHWzS!
+WlMIUZ+{?wr$u*<5J}6CcLJ?eUu0qLX04U{0a~l%<*$mX~0#N3ny}Obd2Xa}(tsPTpN&4*!k&NR5vNz
+@6?~$HRt0!Ck{WYOUj=>z?WXygB{!#~d1lB62b86k%M-G_PG?K8e<`fZGB^FMVShwk1wj;E8y)$gmPW
+WJgb@ND?I}Ix0d{%qTWW2^(Q=`w>1qQor*<0*0L*1ETpk^?yGh0VSvrR)cKqwKhsU?)FiI(BXZBuIWb
+=Mqxwh<8bQR;P+ND6g^kSGML$0&TGJrTn;kzIf~E|fd^?D`N7!4!6&=pTa%<|*En$&lLQLx;OHc;@Kd
+Ha#$50OrH~x*IK4*vb=yc0)C41}(zXCH&^wR*LN)F}2*UpMdG!(&wYp(7)?T>wVtShqZT0$wc6{(@6c
+sfy22L*Df4mizyK`YXXXmQq?=_Q#(#v)5PQm2}Z(;L1F}$wuO2M4E)b@ImcCv1RE`n}-0F*^eR0B(H3
+|n9yI>nQ-vx|@O*C_1*_yu{5Y3v1h`B|y@qVek!i~WoZ61L6>bZ&K~Sm{@nR(n=I0MZ22aP2B@2A-Y<
+S0^&)U36;RyD&&{Us0a_<8^<v?eE+#PG$Ht?eDn?GH2aykj0(W4lYk+8g=Pj3xnO~b1ktby)<K4=hCb
++@8ws?dW+CNqzj3}T-R!Y{ZERfgFyhQn?R6^P+1}(NoMLx7tXlPy2Pn)xkcOe?yg8;47T%um>(N_{*;
+G?{*WUF)<&LWh@0tG<&l%%i91|Ys5)0wg%qE{Q;7eH({y(kc-W7?I(wAey~O7o+P+wVH^@Oq7C`h-^E
+HwOHYID-fE5M8e2{y|H#ylssJ94CChj7c(B$UMvu5>*XQK2TGqRV4bz$#E&qjoh*Q)Rw*E;FW<!)EJe
+R%FmllY%-7)#TSkH-mx-6`Gcd=_(zC-z>+aF8CbMlhExxa4@@=03F~nfA%2Rrdjf>7@<Hxi0fTWI=C#
+^cl(`m;U$|eLoS$y3j|lRAQSTnc>wc?CiZ>QI_%o?-@E<n7Oi#`1^s1@w(&FBpn1lOk|&+-F6v*0UDI
+LsmI}>u`~*>VdD@xcW~*3x_%~Hh*#dKpT@%+_dDPAhB$f}B=`1WqJH;E8I43uf2yM6_`ST6NiUokU5h
+va<9)3KB7t7_cwyq6u>p;8Ix?KKi3zpa4Y30l?7=kszhZ5-Vbzq#G^U^Y{d3S9+=}BkMFx)_A*qwVLG
+tsydcU$?w9prtjo<#!7vwX4ilhx99jgPiK(d$hY2S+1>XMq}d8rEbFOL7_wuiXu_E3J%r$cnQM(O?sN
+=AJ5%cKXL)0RFNMEW04O9KQH000080P~d=N1Xbo@2&s<04@Ol03-ka0B~t=FJE?LZe(wAFJob2Xk}w>
+Zgg^QY%gD5X>MtBUtcb8d2NqD4uUWcMfW|$q#JgPE?k;uhzk=v1DVtT8lclmi^SVo1QHi)|9gM(M{8Z
+8ZK-6RgnMv9U`cTIiKIqLMyV%u1TFz#{6OMOsf7oybJwoj$)%RZ1U>r(JPs5?z7UPlTC1wUF=MGJI74
+w0XW@Yo*%uknfUaK%WpZ)sg>7e8ni;=IVvZK!V2(#~DwO&SFq2>iB-^V0XI2AW7}CCRUC=77fAsrqj@
+1`XO9KQH000080P~d=NAZw!T=@h50AvsV03!eZ0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gJCVQ_S1a
+xQRromWw7<Tem~-(Mjvl=X(iBwU{Y`_Sfc*Fq@=-QylgF<N^zE26brNzQir>pLUavTVx<)DYseXFiQ)
+zHc-Z=SFGEj%{69rKDM{j#`~rBbvN;g}PEY*YI}>#+sa0ys@AS6W-~U{r>Au{#oaim#Vf}NeQ|)dgSE
+^9>e3_j-K`=pG0Y$S@-fNBwRFanixgO1211b<E!uf)oQgWB{ycYLKjXJg|>NEF}y_m%B85d74>s*276
+MKVLN8PW`C}~e%r6NZ2jr!x2OHn`mfE(xm0juMImZoiy|{1k6Y&A-u<p>*gBKZ<z5zG`Lo&Wb(=H^i6
+~s~OHc;1z;OQsy<~BMRtsZAxnyw5^|RTs+uN5*mjVaeV(PL)%C?MlxXHtYWB<V&ZMx#^{C=6zLjkjiW
+e5_j!~H7QMA<lU<e$D0>Lf+9z?4Mea0brp5|G&_HNytRDT`|t-ESw(p(?}eU=q>BpmaLQbn=;1;=_jz
+;r<UaXS_Gf**62sVs5(!NZzB;XXLJ`nN^66+Bq^|0|5MksYPMd&dnEwBc{LeP^M9}agG;YH9$pB-l)!
+#VWDXsl2;7d?b!Ou(|-LhPhM4oC3e%o<iWzBW!?YH2nAQ006olDt--ds_V9;R$iX%Xd7gkIq_KQa11h
+}8Nx_@d$oYohi}HnGF_@!=OCPT)BsZ^IXFM`&PFgj{4htjaK9ikk-Zt2bAe%)uOP#7KL6zsS=zP=pi-
+M)SQx)D5yQArtpfQNcHDL|Jo+N!j@sRU4k)OFVut5Bu3C=OUqt4;V&in;?^dJXn0Ii`%f!p~I<^CiLy
+9g;6X{c?`KuR(;PE+{j_u(NMWudmHTzMV3kmE}TVMz(KveB+O<OOj|Sc8<1SM<1|P8*@Ujzd2*Qx)`)
+<i>K_8p8c}_v2g)H|#sGbBh@|@mHV?TU>-a?fLW2N?d+Q)nb~@!0QBvJ#mXIVT}+6fZE513E@&B-L91
+W&V(5}j-$*C7%l)@n^cjrU-3p@u{7qE$bg;~j=^ESv6>Q5B_O;WqW*=MaO-0H{vl>$=J$T<@wmmraIQ
+PLW<;J@O*%qXv1t?-GSOwZkI@y8X~(GB%%NR{xdv9@8ILTtCzlF5d|olRgW9N2j3?Y1WsEq;sQn)lmN
+Y9B7E(}6JeHSG0>E#>H3AwlQ2&kycx2F<1F-wMyC1O8XxJ>&Rf7cHoo85<8cIihn&uoK@Nt8a=e)E*2
+DH1Zf>KpjMl3AyylX0N`M6cdzRnUwiwH9*-c-C)0eRt96CI`>@9qP_SiT^a1bjbdLGqMLoM`=%&19(*
+vu@a9IHRcJ3~k1%=`5a4wijD>o+LmHfB-;pb&Xf`eRpapj+03*&Go51gL&?HV>awCr2!oH$1@ClA$U1
+()_*DZ9z_R1s8a$f3gvPFKQ0-I3_D{FGrj?0MNE5!>J3U3M><~<UnNU@W$;VSBjE!qZ@8udDh))}+pJ
+k0cX~R#Vel5jTx|bi;FydLsR1d`e4~YKa8~YTSaWj6TKKYqctGeK<}<A5=Evk0p+DA5ldG4+j9au?E=
+{-eu9s|%_v3sG-yL~{tqBLe;56FZ7x6kC{rPC<uvU{85r8$I8dO0`$Bi8oA|IVB!;g1HyHcudqKm&5h
+q(Rw=nL<?V)qXJ0Z>Z=1QY-O00;o{l@>=5s>bjv0RR9N0{{Ra0001RX>c!Jc4cm4Z*nhVVPj}zV{dMB
+a&K%eV_{=xWiD`eol(7RgD?=@{S+rABBeY)qzwI8B1KV$?#Q)|;Kta<K3YXl-o0l?11uG-2N2wSzs|B
+uywwOwDt`L+4^#w?uRExG0MoP<8+2scaoC6jq-~9r>@0zaAc5+DQi~`n1mhkWY<xI_Hj0dQGMo#Mu}B
+qsb6ZVpOzi`~-m8-kLRlFjJjsa9NhKeAtTsXbA6A1SQXbJ(YdGPV2XH$<nuu`lwEOXjI$w>rIK{2uQ`
+!#y0M}@d@L$Tx`Zypbpq6vq^S*FJL*9GxY2>mr&QMw2)ccRr>h|E>G(#E}mWzaK-zS@xjkF}mqdOk>W
+-r$T)gpDljT}W<>|5*&EbjQrP(7L)|IZ8-UCS@bFHEr+D6Y%y;Wv6yrDcvj3e6Yh9$Md~fGhD0P)h>@
+6aWAK2mtey7DvLpK6;V>001`u001HY003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7PZ*6d4bS`jtl}^1
+5f<O%3`xN&Cx45~R=tc}X!K0YUL2G*ueR~m)7!w`r)O`O*?^|&5waUF4HAXslsnyU?FRbZ_U6=;ITR=
+(fu%J6t)=%SC6F^!EG7}04T|`%#ljZvvoDEq8uoMZ2uO=KCR<C#+(UQ%3Lsy^C{A>SaW=wc^&)Jt{am
+?61Ib))@W{_2_R3Wlq0}oJ30|XQR000O8^OY7y%Rm<*LJR-^j3)p9CIA2caA|NaUv_0~WN&gWV_{=xW
+n*t{baHQOFJo_RbaHQOY-MsTaCy}kYi}F5@w<KnE90P5BduS7do`d-b4~&n$XyOeuJ^%p1ZtO(81F+z
+a{aiX|GhKA$8uMa<23Du3P`-mof!`2J;UK}IQY00Y;DY@kc$(>i&d?pS(ln^YGp*umbGGy7VHjInG(M
+@LK~e9hJYzcgn>o>l<`7TIamJO$WjcJsxFx*xh&Gg$U-ysal@4sIX7Z%{uo%OjohU3TOd)ZG;F1fYBJ
+L(5il>f7MGP4m6k@{idQl-jzF66436XA@WTZzxQ|*W$qV^M0Qle+)L=09_5CHA!C}&@E;q6ei5h-NlB
+-YWhc6DUpN-GQFOtJUc*oa2kKyg=6FnvWz(==skfiu`I0k~bSlUC*8zmF?H$ERQ_<`tt!DY=j!%Z74s
+}(cAw-PIH&sK1ziph$-+-yJ=@}B+3>Lq*s=7OF6_4~7QKveLTXgFLcCX6>`je2{Sz>a1;VIVsNFH6N&
+rKrppPS$0`b3%Vts%|zKV1S*p<R)7aOlsE?3#<evp<;<$ygI&~IJ~RV_VIc9c-B6Ce~t3U3a&LHg<RB
+?QFT!WmDG#RA~UUMUgFV>P)nW(W@@GdsB7shkLh*Y6geY3=L~GKs!iHC42pd|mz6a0d7?$JoLK1D&vh
+l-^IRCd(zB1Mft6quia;DI_(N=GQZtK)I|j3Ig6Tq9(jMNvb-~*G04jO`(yc)PZNmB+Nv#y1-@H9uql
+&6lWs((o!dlC6V)z+8SvyCg`t|)=lw+m@_!^j;=1akD!~=OY0uZBKdjAcx!XKy%G{Y<<r>B-)%Pv_Km
+k?lH-)CZDWL+i0cUAGg_(Io3WAFf%;3^7uFN*q3<SCvQcK%Ke*>`mM)+5}0Gxv}pf{OGMH~^6F9H9E#
+<Dt8(;tHS+4~H%A0lOfZ%)wY6D#P#D0s>UdDyIgHhe8jw+PGv30iqWXK1LjfjV=o0as`&2f-wmAml5_
+^dXk<XjSnVgXJ=_Zc78FpA|d@SUgd1~VwireWtCh-n!6sOCU7C(gE@gR9lDd=Gd4UwJRHK`R$R~God(
+hN6ck!2k65jB2r%7UX6p#Smo!*+tJ*e{S|XW^AI`nkg6Bf%t`mf3inXnwGl%Q3+|DR`4x%S<a}XlsyI
+WqsnFA^BF}YkWg#rhFCxsfi6*}FyIS>aLAliTas})+7&F$u}J|d>3j;T>>fg=hsQx_{om=PS$U7PHX>
+*91Qx`o?E2}Eo#bh(dL0pmK=P#>6NG#QPzG3F?%Iad_+t~%fGXPOOU)2n;T<Z_dYy}OY(?~xg#;&vF$
+h7)!?z8-IpS(52O)^-RQ&on(|f0|kRrL1y9?_yr>Vz=JUY*KLu8$C{uqn-R9aG#&qjFz9oPcRQOyreJ
+8XI*~BeP^-QgRX<+bLXheIo#VG=Xh@^ai373%G%UWTH#rwVd7qDJXqEG&xF<RtnMIW_xX9l3oUm0LGX
+P_c_CL~FmMc}@H~!O!~^bfdynEBKaRy!T}=@h9a<Ymd9C|a_1q_O%>3kIRZY->+zWOUa0Gjhjw_b@e#
+%z?3L|WmU4`Xq)cR4^nR>g$JpKs9FsmD5^NeMH-Ej>kHr*U#Nu2kMCP+J`dI|LUy3#c(gwei=k_57<D
+b>`Fh$8eQ=zh&aRX3}3q*=Z3nrKTE@C%-6uZBsa(R3wD5_CK6Xy#%qvYV)+yMw-i--_1wY$@1J!pOoC
+1+atW>Cy4TBjD-FQsfe>qIgKzZxG4rYGh*T5r6|M8mu+zMzs!s?oO5;635~&vjUCqp>$Oe`V#1bpoAF
+`6MRvKAp`AN>Hi|2ET|xPq!AwE`t#_OQAegB3!A!zlqiK+rnOofu1#4Ss%7?<lhfk^ZHt5HS^7LZX|Z
+FG7ZS5n#=$5cr@_ILuzE%tq3p&M53sm_;;`*PNUaeW%7xZS7mX%BF9bMNqZ^1}i4?(>$7!rY6k&?4*l
+2VU(8x}8B3P?UPk*+n+e?UK@cy2`cvMi>2>Z(st?tWt2kbXI9ReZ-*6WkjgQAFtLaf9s7{EecN1XS_U
+`%k`2>|2*{0?f>4iH8X+~9GJ@E3WMr4%{U6MUhn??7_cey8|fQ~=XCXQ5N4&#N-&XUWi+C%zW2qKxu#
+6s#MC#n2ogA&`ccm>f-rg(+><2?8JAy?S@fK7t<!bOw+q6pBqrg+GX$8)t&LDGb<MQJ^q+$2IT=awz2
+DwAGG{9Vt)-swICQ2?OeannqbcGkGVr$Yi9hjj9*mq)<5D)-ngZfU#CaT8{vmfV5+W5Y9uk$OJALKr1
+i^-Qe-zFU1pS(oR5~Kc3xoWwjhkK$Wr*HcVGSM^&j`nHO>?P@zEmXh-A)!McP6M}`iLcalY~Fy5Q~{0
+Y$B-E))(Q>meUfkJNFHR_#ALbOb_NsN0j-8f@`NDXn_fk7irhr+N7MkMYx5+_7G-bFz!XFu+*pgs0%k
+Lh~?y{Z&wdrUAoG&r0eY&)|Bn!7{yUC)siJ8#*pGj~bOIax$`kGa@9i`_HS7G%JI9xG%ijkf`a`j1pR
+-9tex**@i74fmAWl??X$?C+R}UMp3rCz3rHba|E4l;EDuEuhS8CXAR2WWJuwnBGx8GvuEeS5CN4$<PN
+O@!}F-+Cz4mx+tbTo3-R)En^+EhB#yQMKx!pp~8bl;|0%EvO|(T#QSvT9|Zk`L=Dw}7%4&KPdKTo!>F
+`!)mX(|Q(nyq!4=j)Q2cf@_~oqlXhw{<aZDPT)j=expf*`UtSX|gu$?rieX6TmQu|-HwpZ`%p}w}C`Q
+|#>6RO1ByseGmse-Gtx>Ea^GHOq$@>rP?adS1gTuw3mO@G1xGPXovjxc)t5hz`6?@(}Ure&SWr4;$sZ
+S|AGnts6K-repklJx-bzE1HGtzP#$_o=+A`AJCe6Sk=HhyI{AZgpS!D-_C2z7=h6P68m6DpdW@Re*<p
+p1fPr?A+ymy}>&Te{plpUXi=mOn!27HqxQ$!DxWreA~BGzO>dtegc6BYDC4WRp<qD-^!3bU(-9Uy*fF
+9jC68zI)M`Z2>(02-W5Lv9j8D-^DkQ|?YC{IS%%Y&uNoD!sE?y{S&R34qU%RuenkNb;~rf^wI!GDg7K
+mUyEBTG0H#f?uo1<|CmvtWI@S;w1qhs8(9(zoyH*<gFTH_+B|*)~zG(BG1zw8!qgyPw;V||R3oA3<P*
+v<kh>b7rNxCs>TQ?}kVjF_`3fapelsU;(@CY1Fc6VA)NY0@di9&ME@A1EJmq<qlFLOIS2pZ<q1A2mX(
++2ngPH!&&SW(wEwx!^4w~ffJuuXGEZ36(6vwip5)l&cO<uU#W8OUWv>me!SqC_P_dlbt2c$9*X#rCUi
+Y)wXVN1Zr?GY}>S*HCoj3!J7=H4LvkSz0;tdICEb_@ZGQxFg)*)+ThVp^y5ZSM1T4-HivWBl^mByq(f
+;Ah%T?Bgt*hrzrIa%I%Q)7NhsSUe)0HwQyaVh2Cs}ajg`*(x}4t8W%qFh`5#np=pNEEylNcy2n-MB;5
+y!y1M^6abvmCY^Y5QGyliSmTs-`4k!MnN6%3cG-Clvg%v1^uuH^iM(?6DkwGZXL|i)9-EB)_=VW%6h?
+ByJEo#s&{Wun?rGDY#ETG1#UO!dCsVR`Uvwn`bL{qZ_?E@5$=vINosy0UWV{qLGFyD3s(M4!QySIGr)
+i_r2`@(TI(?-Lz{uHyfdp|5;gC+B}!|IPK_cd<xEeeot@pd56rdK?Td{L-=Ayh7CCGM2*$QmorJ@e{Z
+@uivK+7zJ{QcpL}r%}5#Jvj@*?C0rmOEn%GuF)D4T8bS7>1MO3wH?=kndCj2CF?5rO`KAqu`W6p#161
+pnWyZ>#-wS=s7=QS0!L>A3$)`$-E<NY?uYk-bb|wyt;4{Nb}r>=O`}H~D;6a;GGloox`nrS*x(&OH2j
+Ry{C!q5IViuai@O(<Lo8FlGp2>8R%ShHt?hcR`T7btwaV^VEnPn*HxqViZ)Nc*j;sM$8d2(G`!W=*_9
+k5Hx{jcKj-BRy5W)+G=HZBLReacR$7HvXx4PSVKwl3bnV5b*R)tTGMfg2Y{R@%2$5Ir<$6HNk4|ecxP
+)h>@6aWAK2mtey7Dqt>BlV#J004Lh001Wd003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7SX>4V8a$#_A
+WpXZXd7W0lZsRr(z56Q$$ssn7%0-U>a!9vbpqHk=7T7~k2oyE4xTZ*fq+<K;`-ZY5S=&qNgY8(uH*em
+&8Kw!oBjd?*y$^|gz9f#ny>B)fZB>l)Nm*Mdef(@Je+{bdIc$CPjdj|+YzVK1hll+6r9&j0#t>4$53V
+LfO;2Muom!(?IvGSIX5tSHm(&_}punmo<B}(pp{k*2I6RXA!l`9Psa1&@Fd!32TOS$mX~g0)>me~x=_
+^(Io?4vY45qzH>{2A(j5s5DvB^*0fS^(erUFI;ZKgi>vq1vT0S2KA4)!8EGlQ|3>+wI^Oh|+pBF=Z*R
+UG2TKvC_7%3dL&_5k0O<&G@s6}&!hW6l&&cg%3kCrPWcHZ?e-o(siR)&2x6W}d|)eEw2%LuF~4NoBc>
+Y@27O{yfQX?UTIZVNsS)fL?E%-tN|q(Xv+{tbd;5eVw3}`460g;6%Zxju)vPp%v4IIt(fp&fPeuP^kl
+>L)Jo4;~n%<E{<)Q$(Xo65s8#NsY^seDhOpjdQo$tkTojHY#-@hAIyhJyaWNazkd81K-QlkS?`a6F!V
+Wo!T?=6aK;JB<Rr>1VBT(lzlPw$XjRy<_8i?)f-_A7!|aimynven%4;xj3l!rT=^dlj0agw<k0>0)3)
+h+umAPpZSrUb1IkQx)%9$$$6Om^QT7*mGZAyK7+3#zwW6@&{y(@e;?0?$BlR$Tx_k4yyV?<wK(WcHms
+^q&LvNKI%;OGf6JLbybz6sl>@?SQHaDAG67dQ>U-Adhxd9!=&>>Y8Y9A`f}Y~igGF7qD?z)6=PyNG`G
+ZkCPDn)|fs;XO=`x!(MTEF-)BpW%_dGJPAOSV-Eal}5|qD0JQx%Tap&<^6@;>KePFWBB)Q5Yvb)ilSK
+S`dQh?vzD@LuQRH;^eUn(w5(N9b2Cv?4oBd3YXV!OI0YDYD`$3d8K${~+%;`?U-}$%+a~wV7nyDW+eK
+!WB~Ryex5zw+2w5>|GH`i_q{ghyGe$!ztZMSmG@Oh~lT1~1U%rD@U?^X<Fj}aO7r7g-(7QN}V4lcNf8
+^<rvm7umc!$im(5%<TxI(*SA4mDRPr<SEHs1(}rtuTr(%tcj%$Mg148x8CWm&Vuwv$L-7xaZwk`s5eS
+198c$Fvu1d4(h!?z#&lWVM*F+JIBzUxf4|0fO#)ko*2p+#!8`Q{<6b8&dqT`3F!-0|XQR000O8^OY7y
+n;li0umu1B)eiswCIA2caA|NaUv_0~WN&gWV_{=xWn*t{baHQOFKA_Ta%ppPX=8IPaCwziTaVf}6n^J
+dc&QM9g#-qs)uQF0yKQ$Wb=uvQQMIZ@kx3kI$BDDHGYqZz-}l(D9m3o?JcL}%&EL6vHqA>e6iP57MT$
+j-*3XLOq|?a+&k@NnWnrZ#lgNI}xX7_eu_7Z>Vj;=VZLGdoW&NaWIZgLGG9FUG6z+6ZZ_i)HZ_Za}iM
+l;JLcJ9A)~NR{>YXim=ZoGa^w;^S+v#*tk|A<aQbA&zDIx-6U}UT=-}8bD799jX{S3y;9~a(fSnypiF
+cN~ON))xkugJz>>^tFD@pwycs=X*{*J?G(4;*fxR@vkrYVK53G7=0112CE}EG7B_OL9gkL8YQez}H_%
+qOI29g4-Py1uZs#BrF?&BU$o7y7lN^x7+piAL}?Uy2gyAXnJ$w^idtM{!kGxEE7T3`n}o`NOH0z-Uv>
+=R#N4pP%`ZJw=0ly-Vv3k6#SZ|Bt?pIgc;*I=Sm_*uLzn>XI^=$RJn*E5M?r?L}eitn`o<Y7Kto*aWb
+1fldx{ei}7iA8qU#fO9VkXf(k-XZ$@#QWkwUK*d9r|Z$-qfVSOiawB?n6CzghHVq|mGpH3%ejJh8WEP
+PUswQj!wzbZ}D*4W7hb8ulq4V*qGtA}<}Z4oZ^u$rBFc>wME53AP;09Z$G_Tl2)E5LJy_s~^E!m5-Y$
+)Qm`w-c$j*ROw)ZVH<1Ap)%0VeGK1Q=FppzD_u!klqn0xH_@|0^l0)%*gp9`=B2{^!twnI9!%&e?MNf
+QY^4>U6f0t<wIBSUhTk7{U&tL)afy9!lP7q?M9tucl!m{0SBbV#G-}9eg3}pZ7%wYJ1zbg97E$Q`L6#
+^kXLyBr%p=HngA9g2d|N6XA?41r$rY7F6`WsXbKZ*4<)0ejc!(fZxXibJ*B8!^)7v8EsA~XvTtys#Vw
+%PFxsK(Ngy~D|I*j5`<H{3np@YhJ=r}y1h8DWoDN1u6@2H<5P_TE!BF3QSv<cSv|ajm+yXBxSscxM`}
+rPa(`w^{7fNtuO!UiY4O6RsTozWvSH<hv?n|@K$ID_kgt<E$g0=rDB9+wq1G67;Obb1jHNBQZP@XnL;
+Hw~xX+c#S+x!oQSG$AUo0c*#L--BkM(XL~@t#yRPHr{K_ZGB1AB%^w(Jq9eG%S1)8nGWwU|E%yOlPuY
+jhB_;!1NSY9*D@!R*im7hR8EYPB&YH)-b;{uiVPMG1)tS+SkE5EY&$rX+}x<U>&?QV_Mi5=IX~$1ZDz
+m_9K)*m}rfYE5r+9eC(_nE0uH`fvxj{^d`-Gd;)dP!;|pDC;?FeG)cvD>tk3jmb}1gM(X{C<-Ru%Y{q
+XOPRUWqA#hD}YOA9$TL`5o5h~aRA|aBLTocCOZJ6_Klrd}qBE|UPQ<U;VMxV(#I$N#&j&zsk+5G0^KZ
+i?UH*HmWKNf^RoFtO4e@IAF5Va%XDuQ9Fc*0qv)uTe5!JkX<8Q>cxW%+FO+Y963qA-D5lX?TgXiinV5
+<ul-KHiG<NH*1@v5D9F7-|t$OvO+!s*<K<BvHW^Uppt0$p|hpeVOaa^AVcqceBZKgy#C)d~ynx=|o@P
+@pLjZ+Lu4mxOa|!{UP+T%6)GU0rkV|m_l1y1E<&S%>!*9wDkT-`b#rPf5wf8yGGJ6m1(vI5&x<94PQB
+%E_7)BKGV&W+vYl!c+|9I%Hy-c;qfUr=FDbvYTvQ*-6CAC(+=;lwKp=(p-S~WNUx=B&VG1w32>6KjA5
+b=8W%|=1Z+BEywPjS@l7xp8S$}k-3U6`F#(Nlq0x$~(aIp(+k!|LGn&;$OYbG8MSUVf5z>7DhlC-TJ)
+cb4yTdz7{JuPl`_a`mL}w3>w@uz+x&v}Eut_B{1D!kl)<@!Slbg4W?FJ9B*Fhg!KRSDz{{c`-0|XQR0
+00O8^OY7y3?_n=#|HoaEf@d*B>(^baA|NaUv_0~WN&gWV_{=xWn*t{baHQOFLPybX<=+>dSxzfd7W5o
+Z`?K#{_bBfC*!bMpH7#TqPQB^4{e&><$xx|#b`em#+s5wUdCEc6{+=Yi~RS_kkpELv7PH8i1k7~oEgq
+M^H6f%sMbIxG=BZnnRItj^;e&?s{8BTDw&;~m95%CBb#)4z`9Uv+TyPrYNJza+AcR;iyHilH-!4G)>v
+z4<N?2zx$yvLuJ(;E{lI@;UH$Z5)QzIS38Clr6`q})6<ESH`}(Ub*10Nhn;i}6I9p$y0sUH}#cFSQO2
+gbJdY=|(uOgvyDWmlQ6d4=qG0=P^oqEWqGvz;{^$5tV@tjTGlI(=!O<IC_%%;&wdhpH}ML>Baw1$t^N
+>Rz*u#Hvr33EeG7K_&@x_v{Fu__brJH{7%$TL+OH}6%AetBntd%NACD6rMAS=!LJAC~%I>$4MrEG;ld
+4U9cN^UuE7oK~Sc5=HD?%~fr%Hk%(rr7-~H-#NVhaP{^QzWL^_peftH><FL!6$Ai0J%x9xJ*c`mLWSZ
+W>pPHUg5iEUr1{xDa(D-Ys#gYf;(#E40&U=k28@EHRR>(0ElmpF2`zI`RY!^;mWtpyO$#oLzs*IyL-w
+szRa(jlA6n5g*rsm?Artxi5A^I8_v|)V#IVlpDbOHlz_R4(AtNb@Z0!z#VabiUs3;CsyJgF7mlg!QW3
+*hhc_)o_OL06QJ1(Nol7@rJ%i7%(+FtwJ13SIe-5!gCUo3)<<y}Ptgp4HMmVs_Tw0AVkVO)snK^*l3G
+?e97EN?|kL9pT74oOWxpclY}v>eRap=44nklS=L?39|@vY||sD*cX2@ZE=)jSSMV5Ju2|-$%2fetL8{
+d1K!)1ie|HH^xrp6MT8moPx`4Hh_Qel;ZB!$&B_6L_SNCDVN|rfdj{ETGXjDxYzM~;*zD>2-C5lClvQ
+&Vd#OdrK-cR{?Wo<VhC-=PCW`aghnje)Kb*OHt~~%p<@mTr;Oi>r+yx8KM$F44sU3tZH%D077n64f{*
+DaeXb*Z72R+eeGUoKJrDP!=i@P*(lCidz}3^?*DFR^-K4syq={F_YP_4g{cb2PHio;LMahrc_zN4&W+
+6X9->%)*8*7|xL8tV(Lle8}#I}lc7{a%9r)~Ux4&ukWN1e#E06bFpQ{zQqC12A8^U;4ket7>_pyze;h
+4sVxa$stzm9gdmQlEmuJfM9&g)7f$DR8#e4^a}OpOmcQ>v7MF@_h3u8fPBsGOMR($Mu{SSi+^Q{V$e1
+o0d@28qo@)TD^&B@dTpFh<w-75w`bu)SIie!+Ei+CkkmiBDlR-4_c&BqL^tBHX{%p=kPJ?MjY`6Tv2n
+*5No(2@lo3jxNppn8xg3|LWHHmyTi}M+3P|a=GXUVu-!}A#)lngQcpmP$ldIePp!SRT>n6sHRM?U^&r
+jebZgq<q((WteY4;{z7#VX@RvhE;Qw-L3?+Nj;oG)V?F@zhY%aWrLPvfS_t<M9;RuHrHsVwjB+Xf;P>
+<~$wEa=D>=971(h8s&<s=?hxh^2_9h&=T9|bA~6#Fy`IYD1vzP^FKc>AKU<=%I8;BVvYdeX0dd2Q1hU
+U&P9#;VTrn?zHFGIux!W+flrjg9a1-(TL$j|M>=NKI#we8<GXpe|%KU15)ArwUus{K_Sux99N<_vK9N
+CJc*voAS}t7QpuK`-{&}N{D-5VuD()<8d`i^Wj^<kXdJza^jq{o|D1D{yyMX)!JpD?CRX`Yg(BLoa?H
+pCSadB62QE@RC$JZRKQ-+8G}>CJH@>BXkl@g6CV+$41#Q0@m8)CLCr@|^fMJzd7QOMQoOt`yn24>r-G
+npd%CtF0(^Z!3D1(?4nk?TL`X3W=2XLEiMBliRu?PRVh&t->^FJ8M^S4ICN&tQl)jPzpyI?J(&wY~?b
+L&LUo_E}$Vi;K9HJ+E6CAbs3)}16eA^aX*GNbfte+#YxAjo`Y{jTs_NhBH&4rdC9sqgEPEQ7HkoeF%Y
+GdEs#<r4Y&$fIf4GTV38&g+VBX3*rKxOW%xE|E@P1>j?cKSLf?^$j7^XEj>GwQ7Hu8z$UHOIPR$497|
+-3-rop%x)dIlKf{?GU?99P0_3JvzcqN1~b<UZwEu>6*DV=JNqx(4aMSnyO;C)71`()y@P?^wVGSEQq^
+WS|Q~j5^8snw%#J;qVqK%GCJ>)xf8Hb$jY|2NemVaRztX8MQg2yh0tjw-8J@mk3BJVWofl0l};&g{KC
+!Y1;>g7$7(&-PGSAWGtdqHH&!XcQ_PIxj}GG@7Z>w1o60x)HODjii)Ymhoy<YC@=n&=Ven4mN0&NI7`
+V*hxG{gTWr8XWYt`ND0u))hgkn191MrC(k$M2_J*N{9SB-oE@2TGNx}~5XXU;vxr;~ury?gnLce7ISl
+(X?)_EIMR7kk`enrF%YezVz-bk=C5EoSb*t3U06mDA1vE4*1xc<F61CST|Nw1emHi`zKfSchrL#?q^!
+crS?Uw?=SfF=)|w!d^R0<de_*v*LoYOLK#ye`;|5t>URuR`AjDbjk{zO}|ovr>Q5GTsU4mWvc(EFpUb
+E@N7)_xyci2J$u@GHV*&)X>+{x7o0c0u#ElyP)h>@6aWAK2mtey7DtPF#X`IQ008m<001KZ003}la4%
+nWWo~3|axY_HV`yb#Z*FvQZ)`7ja$#_AWpXZXd3BIK4}%~O#dkgho0u5gO&A(Gck12{JS&NcIgX{Dz7
+hYRMp-!We)sNi4ceaPs#YTba?_6%iC0RuP}zadc8^(NO#t<ztk~0N3MgZ&hUU;F95|OTem=oKzU%;Z;
+s;25oqVIeLL8t`o%sRYnY7^1I5Ii_Y021T&E;}ijNwHZfOQ4(>i1$B6Z*5`b4k3bBe2UG%P7aNTQ!Sw
+sA-9LZ~Z_bX^F<ND3WZAdX2af6CecgI0Tmc0Z>Z=1QY-O00;o{l@>>y9aj9y0001S0RR9i0001RX>c!
+Jc4cm4Z*nhVVPj}zV{dMBa&K%eV_{=xWpgiIUukY>bYEXCaCv=@F>eAf42Adninm=^_yI_rDk`xQ39)
+plqQobx-X)Raqww#W<TwNinViqhPtWcpj!?SjpV$N*Qxt_KPeF}Qxo!-~>#J(r^FTk&x)UwJ&q6*bFm
+!R06uZW^B2nha5S1O_{pR`BbzS3M`{Q<lT|g910*#n5L5ua>6W2IloU-C%&(x-d=zePoaL8(hf&3cZM
+dfT*5Lj_$i^OS8fLh+Wi$iObFsoK1;gW66x-t)o-}V&poF6XH{?Dg4RP_25$>wbS0Z>Z=1QY-O00;o{
+l@>?Arw0F91pold4FCWw0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eV_{=xWpgiPX>4U*V_{=xWiD`
+eomXvd+cpsX?q9*VA1Y^t5_DS!7znT=O@IPv45Znzq9_DfIz@~nQXuI#9{S^VM^X}HC)+T6u_)gA^W2
+dZO{=tJ6*pBaRukcu%1kDuRt;-Kn`TcS3#HRq<?B+^z=F^<uCH+Yx@6j{3!(S*KGDZs*=wh1&hx4lIp
++p0=tIA&q}8gfL3bi<`=b9mnM?{OS!N*LXvkCxGTUllAtBe2H!x>qqY9WUCJdi9>>iks^^R@1w9G2zr
+0K<0!Z6aBGou(;V!|>ZgM?EeOU_PN^9RFDv0~{myT4uDTz{Da2kz6w?d91W`|cN)e_mX(t*C2Oa#1r;
+qAzf+7_T+(V#nZF7;Bv8t@jnIc|8}+k%aA$Q#0w1jA<bk<<5t{f}C#*kc}=skOnJ*+uS<0lmhLy+!%%
+`_$?T7J8B3njWYdy`bu71)*lp;%CZtsQCntqO)ccwPynq4<cG!78c-5V6SNIq3;Do!C+Xq~rZBi*hRX
+s0@YrJYT@^U<lo=w#dLoaxwK`E)!s#-*Szax#ub21Jfcjw`okSP@QnGu2B;Fcy{}wCWaJ?QV!8q1dNU
+nFUcs&1jb9VdXb$@=p^L7rU(hdcelG^A%ya=}fYCzg?8OTB9a-ldRg$bq*EbRfbdN+7Sry3gdH1xsD?
+ZI;nt!2r_<)zk2&)M>`dx-*n!|pnEHXCCdtS*9%tQ|{pF4-eVn6+pbuW*k7r<5?*-X0OHFY`EEVj9sE
+N%v1(6H^#V&x0||a|}YsLRg_BBGRID>H)h2bVJ7bGB_$ZIFW76Guq-ZSOO}6_q77FD-0AVqSQCj#)?|
+}g;rQU)toU?sZCwP{pzEjFgw1Z7%~N#)<K8`!WGZgn0RQg!p{+^TRgRhKi%*;1XzRJXi0iy;WY~+;&w
+HSagISflOau&V>@XmaIRsj6cYh*^?^nm_qeOoBjh$FQ(h~BTq<Tm08}L$zj;lAgl4)M%}GDQ1Et(}&V
+&>;%Mt^1IcIndpn0W{Nz?~Vpii%OZ769pLkXXrsFU|6C-dH0|DbLs@GA-seKP!(5iX}Uim;{E%!TDXU
+>v;}ge6uWY>3_@<`^=l##@U~7%p-42d_8aS|^!aa7m)Xm67#5i&i}VwJSpOT0mn@k?!gK@fjHy2Yndu
+$jqSy!X)h>L6QD_EE1qXf$+As9iXHA@Ot1P?1#7U_9)6>G4X-^W-PZNZ?J%>=y?S)M?1;$?4U2g#U?c
+sfA{X^<T(wZvsqffb0HqU*d*ZK7+T6ePB$y##<^MET`q7YVr{+quImZ&yyEh~9S_v*cxvp>9JV9P1iU
+_^ksbeh23WAl+SV+Nj~|4sHY>d4G{-o>G$wuQ4!3aaITprjz#PB-sW(G;0SydSvyjv<-sx$%wyuUavJ
+)@=b@9u$8T*la^~-NBOp%t`Dt%PKmm!T>^vD^EYK1HgwOYe2Diwv%MMm|c-I%9Tug;Ud?<9N0jzS_D4
+;<ZMq1?fe_~H0guinn6>Sgt!c3Gu#RK*CJIO;ijqW=B2;3M=t@ipGH{4oSWi-e0?FF2af^kBkx*8x;8
+NtZk>$equAKI)F4VE^Hy_GovfYhS0mcJgkhScek^Z;<|Sizfij?S|K`c}FtN<_i~WvgY!cj|aeuxX^>
+3cuimX0UCeR`QMz1r_(|4!!5|Si2t*&(RF53M)778e}W(U(UV8#)^s|(^<?TV>;B?NDr|+Poa=Gg{Qd
+Si_1U;7Q7G_dM}Kb#`kpI4bmrYJCWQIz{j2f+iZYCgV)F@i%<jS*%JDZ)O9KQH000080P~d=N7IPLRH
+gv{0L=pc04@Lk0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FLGsMX>(s=VPj}zE^v8uQ%!H%F
+buu>S8%!1PE#-Ih8zm?w98J#FkojvvFKW@ECmwNHvfL4qxfT6bg*dhy~hvJZ45mq)gI{>Q7N$fAw&XW
+-)yqM7ey-oqY26OIEM3kQ4|JSpg`KGgiY`!m1L@i0{E;}tDh(8f`|Kh2TkY?n)Kc}OD7Hu5ZgkhlO~E
+x+rZ62=_c#QiO2<dAX6`sRmA}56Ff?=w@nN=HU!v~5UbbO4YvX``jlV|_uu&Ob+_GC#VkwN3u>UQ;md
+Zt-c|7VY;ebo(<#9(G&X(LO^0RJzhFaL?4Th7QObHtN|gy+yW!TopY<ZnT6|oXTodmZDN|nY^l$egu@
+;~}CTf~fH}Iz25jTJz2OBXxJ_V06Js!Icbcs&>MRj#LC&^;fh(ece$;m?8&&9eMcck{=y{xXMz>jm1<
+#)|G)tG#2)SzS1f5$ZGX3%m(jYAy=;*7(a_ygSmBeWr&A=W>Gb`GdRNsz#L(9)0I>x4D@P6+Rox>2Li
+N8+F5(`BpWh#HfHJC!ABWp!=dSBmC+{=4WI$0oUX|9HlK08mQ<1QY-O00;o{l@>=YN2j8v0002t0000
+W0001RX>c!Jc4cm4Z*nhVZ)|UJVQpbAUtei%X>?y-E^v8Gj6Dm&KoCUx`xOfoY2p#R+CpPQMQkEihC?
+@I@p2pXP9^`|_ytzOJl@O*a_)MeOA}$s-p0mySX+q>dzfYuqhV1l!HjcfZvny434Op60wU}-d9g1GsL
+7#LwQviXJVmmO2AA57CBloOaK=x{JHN8^fqvSGWw>3~(=~-M_HHQoO#;iG8~H)rOn;O4fDU|oA*HJ7j
+uMD{Rly9BOdD+yrJhhr0|XQR000O8^OY7ym!pF1Gy?zt00{s98~^|SaA|NaUv_0~WN&gWV{dG4a$#*@
+FJW$TX)bViwUyCw+Bg)3@BI{K$c14_i4ATu%?=mFxCtqN4v@)qb~_%`96+s+8A)uy+pi=$7>7}3bFsb
+vOP~IuqvK!F1w{Go!0CD;;Vx%HZU}>0!exdB=z4yy1B3Yxdher*>;*zbQ)B|$AP`|ZisH#Uh84}Q<G8
+NtEH+fYHec>Dgf%kcM52IjG9N*@A)KTVIi&egb;=xeoH9w-TJ>Z(H(iQvDI=NaICI2n*hsm%zPv=%d7
+{7Q4wsa4a=yOQtxFv@L6MRjCOW7|!HLvleQ^KwdcD6~XF88wTHjq~5RZ$Nccn%;OI(?35}7ADKhe~!*
+0lrb;VAc<fe!Z$gyaQN@9tJ>(~JnUG$v@I*VD{@{KQn2S{*n^D5Ek-ngX*`yI6IJ^|Q)3FtSCU43@Nr
+e#-SjSHN;<hINEgUqw7oxoNtI`)Z(m{vp{O)#}zLi7e9<Z6j`-4Ho5~A~!vz{i+fsT&q=I>7I+6pD*>
+{>r0@%f0Ef*1(_~iBWxUn!|CJXEz&_H{XCsLhO^;Y)KR4#gwdC`NQag5>vUc!pZk6r4!@TljQo#A*?c
+z5+eYC%^%W9H)n03w-@5U%bJf_c(HgG)m4Qp!W=MD<QjW-)YR6%}YxC{4^ljs=ajMNI!~d%M(y001bI
+!4l492REC%)=K&of{a4{L@~z&GG#7RQr1=o;`X9*?JwHP9>Pc!*zx3JkapZ{o=!JOv*Nh`xoBSBj4YJ
+l>8LaUEQh>CWRt9ei>Oxq!t6)yYRV!irFy9pDMG48(r7%elm58-DDi*{3_TLm^**+@LBSVN1f79TQ|c
+y18ALS|#zHCj2L_0;*O;w*h_yOs$TJZUu^38KOY%bdHC(jHN(OfwESbDe$2Jx>l9qV+BmDj+0;23@{+
++FPi}Zn*jow0Ro!=0-FH>n*jow0Ro!=0-FJX|JMM%XLEq>*(~6DHV^oo%>=$@bAj*KY~Xt~ANXF~2y=
+PJUXFs99$f}7J;Txcv6@BVlj-a}9G5-as_;oT))%wqIQnQ&Z`HkeJaC}pXxp7b)7x@PD(AH5ZaKx>^}
+>sbtmXU*P)h>@6aWAK2mtey7DxVZxGdud001r|001KZ003}la4%nWWo~3|axY_VY;SU5ZDB8AZgXjLZ
++B^KGcqo4d97M)Z`(K${+?gK+kB9mQ;fWPI3PWva2#)&ZJGi}id-)Z1VyG}ZDmqRQcZn(``>Sdq$G-x
+72n<&D595{hcm-xhC{0RWX6k)$eve<{K8qHRtY2T6QOdtAty)2r(<${dqGYQXL(XeO7bkFOwx!*O3Bs
+bY<_(^C(A6S<GpNMaG{8x)s@`H>XOU7y`|u5GESJxh<!SR>z}y*G`u=b(r@PKR&8>4sQ*~Cmy9VY)|K
+Xc#_r%Y!iyQtxzJah!s$sR4mXQD-`m@(U?$|6Gca^%Uy)1_#+810wnyOCNZPT&5$G@I5=7%9R%#PUC1
+|o98F@ifDHvHLGEqv{cM%CPyx#;PlCT9aPsiq&22ArrO7Ik)-;1Ous0jJfuQXK_HwXg#7hg(}SO^lzG
+F_2G!u^7mY7~(qRT+E6M+s2^!dxVRGDQl(e{9H-3z9$x5DE><o=QbQC#5ia0-}oOxUH#L@dXK9M5bss
+3L^5d%3vgj&w}RkIZ<>$6eqkC8f(VccPb!KBx6z~ECnLCW@EtOIAfWL<51Fk34q$oMMTm{*Sd<oeFE-
+0<@B&*3o2v^>>w1RHfn|5!N`Sxd+-Y*x1tgzO05V>)^N#}4o>YQJZD6$Xf<#ZLKEh3t%BB#<G7NVRPw
+%nH5&ptX<XQIynUt$2d^DQ;~W<)Q>;|j9@a`XaAqP5kfJ}71LWH?HnLi(rWWSY9lytH=qJ|Yy2$AoB8
+tSR=e;6=p8W_T2oMYYB4PeA$QOuVR*>oS?IlT9i2(9ZLkLNKD=E-G8a`p%(V$54AMy>zT9iuJ$posZj
+fJ#+pbF$UQ@qMU$m3<nfZkw53M7_oilGtl4F28BVMYWUr+i&NMGAwjN2jM>AHwkM+1JIt-;54M;X`ux
+uj%1GA4iaoh9*E@C3sG5t=;4j?-yli{n6;3Zf9H29}#_w;BQCa*B?iJtB?))C&=eSJA1p43jCV!^%^3
+vw`+1tup&h!-1a&tVXmp9YVvcEOWI1FyDrT`#>E<<w!wNlHsyi<dF>q)V$^0-Hvyah<cgO$(1H?e0+<
+QKSK?HHGf9~vi<VdpbkY(_NpFPO0~n*ec1KCUawtzo9#g)wB^XAiJC;J$zc#Y<dE!;nI;to%YDVHLel
+fHL6)wk^dt@>p0aJl1MAv1GmCu!KRFT=i-TdZb0JKFI%D!iUv;Md_nd9Y3<J;R>6EemqKml$Rr`l)zs
+$&eUWIa}QLZx-}-z}z^t12-)n=wUgdg`of8C~i0nq9r@HQdhCIL)~P77qAhN=mgFTRo3z%~1Br^>mS?
+0K6o+CrSB&Y9hBQju{0ONT3Gr+6$tVjkWt2N_+%$J$4?4BX>ktk}<F~olOG1ZG(cV=6MBKWh4Z%$=ar
+bm}$~0h^`_|MW|{d{_aFcacW>CmBcI#Z~>LO)YXAex`42g%OzQ2U5POL%))6%*%)b(Oc!mOnkQ%^mP=
+S!^zsFzMZa;FffAXNrOrTJ<G4}4qGWrp+9Cl|%LLkVhbC*(XUd?W;@c-$1L-=5YZT+`(5_RY0IopSvx
+XI`WI47uyub-f@_KHQJX0HJX}>j4O=*Ho4#t28pczzR-&0Do_S;J$o;6djayzxn4Ypn6TO7oq+hksK=
+M_MlP@h8Hq+(5Qagm_5U2W%?HRAWyL;TfE_V{LgJHLykS662wv>0P;O$;MPWE2hPp5I))dw=IBO4GMj
+owyYk#JITpH~?_{>E`3~Y7oGP{8<d#eT>hqrn3(Y!cuQZs-1;-J*0m#zu2w~FNbvB-OT6L+qL!UA^k7
+&tE*4<+X2k`VedcBhn)XBAJYCfeK)_pn{EfN0z(+HFVle#;M*bX`}dc30}en0zYI?vBtS=fNaG4gtw|
+Bj|M3cKB3*S~MFWxyV!`_zku+fjv9!dFXofw5a7JWDJY>@=LHj)45z7*55YEc%h=@!ZghPQHu{0}Rn}
+;uhV0a$VsZCG%kYuOYr1jgi$<y9mo3yT9n>_8I+NAY6waEh*tWDPYR&DAyzqK}L?QU)I0ETOm#_(#B2
+QW~Zw6<THx)Iz~o9yJZYm-qQ(%4p;`e^ey`TA>9mqa@v_0*<5O6`c&SDShXwj*A*HucbLN37o3)K9)0
+5xcdihl)F5_0*;wV)lal;WxOrg$j2&uBzJBcKd6aSZ`lzc0B+ULvvf5aM_csN{B+!->YW!kM3dER-t8
+H%9Yc?S2os7dCvfKd#n#|E61Cs!`*rhAU64?-w%Uz5RvdG8o6!)0`7}ihC93C<~-UF7`+-9um5*_=|;
+YHLf!q&J*w!<;&b*Lw~Zib&xySQDSMcuH+~7*=u-t}|2uGcSnc0n^JneSr-;>Yq1kcPP|iuAZ){o$sg
+G}u>POn%No%HJE>n*`c#6JG6rP9My`6LTI9|fe6QUqOFHYJql?ZTyGCWt)5Mx;+EbQebMjy7X8H*7qG
+!ZaHM+T`!t+E;qGr|Ki#IxXKA;VEyymlX~@sIunht4T8zIDms=rml39JjrUz~|)e_+qCNe$WbMApW>R
+HaEt#f?^EQM>$AA_Kc^j`Z#vibTzsaPrxob_>Rrf&|<Yl=$oe|+dU1}a1Yy<-aA6ebSr`M4Bjt;E!L~
+D$Y}^U6BZ-X!g+<Ozd%Chs!EPE(+h-gh62XsnF@n9fk&v9RhCnOVjO{)J!y}Pzy11*xcjcQ&0hSKgvZ
+fPt?zf{6|+5O+g?=ftP2wm`0sr1;{VHsb<h=R*^ZUIBcThv3p72SUA&5f(ef8(^VUs=hI_q?>L(K(i2
+JVSK%-6*?B0OXGX8OCzdv{~7whA-Nk`*BV?A!w1C?6+;izBELN{{9XZ?cqkZ)odoccs|(mtT=PF`h9c
+!2s5IXEy#-g@5Ri5J?cC!->fG?uBLH7mg_{1*3`F`Xek3=WZJjQ)=UV6D+__N&#U;4;%+><&rtBzayc
++G_%KO-3cIRIDw3$`F$)TMQ92PZYE#EkjscbbQk6lPM^bMMTcw?}ERuh|J&$xDRs*Npk@=*g3bfeney
+gkiQ30umcjD9I{bFf^&PPZ)SJ{Xh(R1_J)W0?vDrS%VTe>Y@CwCuccHW4m1>_?kb24O>ZZ+rLQrcJ^v
+4VjqR@VN`9tQ9-+ePTkHTHdn$W-fv&-;j>fOtE(h>4tfeDPBHn`j`TXj&RmWW}etPp$H@o*?fjq<y-s
+~9~(@eptrw4@q!(NJ-PZXSCgX+li;T$(Owf2Iu0NVVr(En!zKhyMSo_H2)t-8o(9gm}vEvtu{gBq)<0
+>@65W!Dm%tiAsLP)h>@6aWAK2mtey7Dw)4%f<=<002D(001HY003}la4%nWWo~3|axY_VY;SU5ZDB8I
+ZfSIBVQgu0WiD`ejZ{r<+b|5h`&SUyp*D~jTXP%W!<J>(hM|`h+hH97wWebMieymovi$o|@|Wy%O$`L
+G`0@BiKI;u^OmiZ+uN{0dTDV$h_#((DJi*P|ck3KJ-hG1g^+t)-A}INU+Tsc<B7Fb4+1}o5p^^&oL>?
+ML4ur$M(hVnjvPn{rIY2J7mChWcUf)3P{lKPs^V6G>Br!~5XI*Jp2ZhhfyTj-}qRGLBBsr338Z4j9ih
+SpZ9m`#5G_vQjP!^d%i4_o9bUUF`n!QhepNM1{QOxovvd@)Tx>U5z1XmGYSMX%?A0$`Yaxos9BiZ!to
+?>xNEpn+)OIDzb3w98?<lia%H3E)MJ<9K%p?^q7Ue6DqTHF#DO0NQVpy1Og-kh^=l&<!uU|fv^g7%^Q
+Ro2ztEAuE6%*VQd`6vgb)2u;S@EuKb&KBd{!HZ02ii<Fc^Q4C{Zop(8p*X~gSWaaH(ZGC8wjk`)@Dkx
+pEiV)9V$PB-6Xv*A5RR7-Nj26;WS-b{MpoutT8EU563rGNgYy0Id-0YS<lAeCtHj?U&&wB`&uq55D0&
+Id5B$FX-Rz?1@l#wdS_tGasd>UPFt4Mw(0|7+-fs7Id9kxjmg4i3;>)H7ok?oAOXh`&Ww~N}n-)JdPP
+wg3tIC}}rqLY|`VK|Mo;fxcLJXm6N%tW<g?~^>0|XQR000O8^OY7yv#FkMF9iSq0TciL9RL6TaA|NaU
+v_0~WN&gWV{dG4a$#*@FL!BfGcqo4dCgaCZ=*&K{_bC~N^~_`3Chd2sQKVHPUFgFN5-e*bUGmnL!ia1
+wY$XeRsHXsWic!^kh4yeiXtn_JoC!T?CdES()E_bFOeXB(9{!=my!pMi3GOf^8Dh;Cb!NVxjGvrUdDk
+Wu@5N+i*Nuk84pKOXGFp{0lU{bB_X3LvWe5B$H9(z&f;$Nc|mT-*~Nu?8O`VU%Vn=8*!H?d@QGJz$^`
+Mnb`3nI<Vw{0jys8$%wyXL^#Wiy2#o|ug4${<Ly6f>ae6nIm~HA%Seb)2AJ&2x10h(v%mj=Wqs$`X9G
+AbC*L8bSnnF%-Kn!WhX#xv)6Qu@(B;INnc;usdt<aEgxNh@hxn&Sq#1Sm>MaD3*zUxOGI`KFgPZy(kF
+P9w7X7fALy>s1mbBbd6iN`rYrp|0Ka-HFPG@81%pYQKSb9X$wpBbh0A{zOeoSg8SOyX3k^=WEHHd0H3
+8^(|X+(olrOQL*?lVIb--{Hhkewo$*dzdj!*(2q#h-umi+?09ah$(}6YQl1cpEaNx9q4=-OeXGpG+Z?
+J@X33NSJ_HQwfOt3{4aozxGtu|bqx+lsHI9i3^0Ew=^GyctMkw7lMlqG$;wBnv{l;dFIMgK`2bw<>uJ
+;k_ZY1zv;?<7c6qfIbiGfSKq&TUw^Rta+NaLt2x$-G;9F?19P;Ynn5xR%-$6Jcc<`_jLA3(5r2QAL6q
+2h;TW22%*dBXPji*<0Sx9%X<WyOFYY=9@B~EX^hr#r2GOAoFhJoWVfb=#CLtx`Hq|J`DfZtR0P7unas
+<?{@Vzf^Nb3f!Z!wm_zoy{gi`0cZBKlT=6r<x<9u1%3zd~N!FG`M=xbw*c*b}KqEzI=qW#I>imJFxpW
+Nea1HCf1o4;<@Ab0@$D-whr{W>W+-CH~~AmbT1Qnx!2+K67O>I$ZU0$c{}($Wj|l+|8>TW@0&%8>*Q1
+SG?0)%v*0@^nLG}i#k{4@m4Z%3qkP0m<$ssT)#iF#KLrd$)wTz<(>B16TEPBMM&x$UK3)x6JHT7Had1
+=EsFsgtcit6#el4-p?&9-hjEIg6&@8b$^L^kl+>Uu)-(QqvOYN$LG-g)%ip5A$t591eZ55|QJS6fX<1
+B2o4RT}WsM6i!PJKY?{;UfcN(X;tT-+>Jrtiz^YV7A-WPIFFs$86saY#f26*%UEW`fX=FfV;U*8)mXD
+xx@ju}iTyECuW@<hL7gPAIEL&pD}S9n}mEa;Hg%f@+cC{noIrH934PY_bG&h@79viyP)*6XA`H45rRl
+25_Osc3tYyCh)cfUr0&g&DxqZIlL@#Vr;E9vyxMZg}^<?r564fxv=!u@!ixjrIggOQ49YyhK;;h^*P}
+_qj+mu>&I}}2Mn!ubU@LF<%U}mK8OSz|MdoS$|d?8<a2?PyS0|mIq+-+SR8ESq05ZQF2&sbWP|J#eAy
+xUZfV`pen#s3qAsZkq%R`ERbF^Z96Pm810vLEZ^$1T@ERI`p^X=1K%d<C8D%JXg-f%QyJ4{qkRF21^%
+k8{JhDaP3H-t8#y_+ioJqq$UO;*M8m}C+1YqLS!^@N1G(S6*iRJ;?kh9%X)%-skt{T&R^^y!aK340nZ
+U0#Pe!oi(i3*05PkH@LiSulOOnGTTCZ#M;ePoeee3=aXRD2Q>Z5&k`UVXc&mK;NJ{>SyPYr&3$Hr7TN
+@DCM8(@86L9Ye@p?b|jSnfXps0DJ!eP)h>@6aWAK2mtey7Dtq_Cm|~Z003kb0018V003}la4%nWWo~3
+|axY_VY;SU5ZDB8WX>N37a&0bfdF@wQkJ>mCe&<&l326sfCW4u6w_1rx3`3{Wox%zjweu8(#6VhOBgZ
+Mjsy}{xOJaw>K)18nhvfwy`}o}aozG4k`gFZz@nXs010?}p1_a&*jAOKgi+1PIgR99kT%PxFkR}Ll93
+qmS79<G4-EDs`oD5(dW8~H5jIO|gc(taC!zLz|7qwcg4r*SAAtm0j7UCdD;8RTc6jRoqvo9#*&EIPve
+ph$B{vYDM3vDCc4?xKsCEtt(gQ1cyl-z&l4fXn^lE;H<(DExKf9%~1hSQ#qe^v6s;O=hpNv^-n>^*4x
+-wp&i;aiN{K!>C8$KD+{KN0j}yA5@S;uUc8$XjYO3&O7pMpF{$l*YF=@23?z--w+veGX4-_OC>hZpNe
+g;kDWP#&v7VSeuJvriiVIT$I%a&B6C$5_8{gBnZz<lJX}-&leg&in;KmPSv|XyiZ9&F`9%7A##<@=ZM
+{sIc;c#$@F@3KW%!O9_K8cr5q)$$pmpf5Nk=p@zAVx?9VAf!n>Z7!X7=Da;_(ZJ$i{e(p*tVMJ*OiY*
+sNC;wd9m6!6k=1uvlUI-e=obn|V&w*aSTr_Lr#wFtimP`b+|yM|tW`xag`E3~rQGAoS?!k)q2cPPzRf
+NB;YGo*sX*n~`pFdZSrx<g8KVn6OcrB<ks(@@&ol6LN0mclreL*=t2*hc`n`!2wefr^0CgcVV&_d}e3
++*d5fpMxbEVAY4=tO&y@1lOWn_wU_ub@x}GNFX0s6_BTGDj<g^S3seqvI0(**sW8IH)rKXDq!L3M~k6
+Mn`)@BDaXMI=y)ni;1YmC3wzqBV(o<RElr(-M*k5>D8=d|^pZ)UV6CF(#9bb#;B8j6jpv?S!@K(i<8i
+KGCi6RBDlVOz=No#jNwDd%>Y64ok0~)V(5hCt+4?aTjoIu)n|{g?%KkKab7OV5LZN!I2slP`lR!u*i(
+>J{K?ww+x!dQnAVE>f+9HhZ<mcK4^tcw6X969)+uMoW`wTC$7mG9h;Le-tBwH?_ML8{3EEA6bPF~DP?
+9^}1RzN5R;fNu^@fJ7}jUJN)@FmK3i*}=OLbEBMV0y)Ktyw3RqcM-Y>FDaT|7DE2XPARD3(>-<DSQxp
+NfU0${@8-;p=v)*4hL&$;GZk&cv0kAyBap~WCk`u;jwvYwRU`DQ>GwA5fKxu;U}tRO$}ExLf!VED%PP
+5$&#ixf)JyC>G!w<qdpVh3W<P3kJ7msY7d@eqH~mq8Ct}INUZNQbm1D(j(N;7-In5t75!%0Zgg6m=ce
+B<nZ+j%q?M8YkjjIks~KW4^>7)8rsqNk5ys-0Az85`C%{ZuD?Cdb@u6T2Rs<nq5ez(u|3U&&FC6)y<a
+xi>8B%Yp%&RHqx=iTWy;e5-xIY9J!m0tPS3cVZB0&mW@kv#BG71#=>h>rc?zwe}KsohQ8^|rC!kt&;Q
+WZz%KpYwX{n2QA-4K1%ZXOduwMT?4pBF#dXP&nOh<S|hZchpOd<iogkgrLJoZg8I2I-@euT$R7K$O$d
+8Jx9BF|@4k?M@(cBX3#F1q$S6iF&c<Z&e7wP(2kEc>7cwyDy*ebL_aF_Mbo<gd3a0HtISi2t_DzG>_z
+Q!BWs^?$9-k33^VlmiB)FRhP}|Cu&#ozs1JSNHz{3o}WT{C(z9vnth58o`~f~iRd3iqbv14IaLJZe3I
+~(=bx`?Y|+8aH=)wiTD89cP)h>@6aWAK2mtey7Ds~_d-q)d002b-0018V003}la4%nWWo~3|axY|Qb9
+8KJVlQ7`X>MtBUtcb8d4*BIZo)7Oz2_?|aiE<>1!#w<5|?#MLqaf7wrdqyx6Rfjm1{Qm`?;k|+?GRPz
+dS!bCnK2LO(1odu|)_+&L@u@qDE>2k!#Lmiju*Le3(yX^NDN^@L{YzL##hd69{c>QnW2*zz(=Xcsq0H
+EW|G7A591^?r1a3MlcDGKubT%WDZAmk`;tlqvcL3Muhb$T5jTHz$XT6Gl;gE{ciR2zTfWR4iug;b>oP
+kaz!D=l}eItq*hua2`ped4d%gYtW;X*wuNWYa%B$7Q;JPz!~2W;P=TyQSV}RSFKJG$zDmQ;X>+WqS8-
+DzDeTK()!!_M9EC7~hICQu5Ui8&#7!@CF>q7t4Z6XQ4-UGOVGd=FdM#G@JrJgjGC#cdyQN`m_+L^=s2
+SHT{pzVg>5iZhK;CHMEGpHxksn!H#|;YPb&q*TVs(v4;t!-iXW4a#+kt#v{Q^)+0|XQR000O8^OY7yx
+xzwfPbvTa)SdtU9RL6TaA|NaUv_0~WN&gWWNCABY-wUIV{dJ6VRSBVdF_2$d)qj&;Cp@rjyyS{9GQuo
+nZ9^Oy_0sFY44=toY?8^J&s3Bi<HeaMXGtRqsivKZ(RTo06|GfPk(!6zvG8Q5`jXYs!%r+z#Xx-yC-H
+voaWbuqN(S5U*Rtgb{_1Aqhh%#)9agB1V<xr@MQn#9{lr1@k5$ND{&V6k}gAN_A;F%d6mSXE<}Bkh}W
+z7rpU!vF|Y5VG7&F|CXb^!h2P*qQi=%1%j+_|YU%>MU6)C+Nb(wCoh6BQd2)38>g+hI-`66_V{vr)>i
+o^gw{Oo+-<;u((C)#!EEZxqoi}w;Cex`%7t5lA=2umbHFYxO?++fx--}9pDiigoT2=6Tj{k>uNm&7Y(
+=?wK7f&w5pT*NdA$G*FjII~ap~wqB85%th@JC~x0si^*$*CGyeWw-`eVNv)Wm1WWxQeQTe;U){I(c8y
+LwMHYDG)zt0dpq_^+lQ`=h!f+#2h|TFKR0rOs7{(n$>9zG=MhpOJz1WD)M=H{W>ZuBz=Xnhwn@J-VPg
+1ucFy4bx`!VY3ej<TQp@xoz!#neT<))y#4%3k>>QFT4rf2AJb<GSe9NXPyu``6QEFX2OxfJikfCTrB4
+7LS0BpkU>Vgnd9+BFl)gXJ;sBbDar!j~*LhK#-N0DKWm%MCLFgbcM-K>2oXmOLpc)-&$RHhQ1>~%wd<
+IX(id#k#VmI)y3X>ck1w&dx!;x+Q6Kl#`P6og~Fqy-2IB*Gbpegev`LzMzp}7~!1PJ@~%}Y6n@6OL(^
+T%FVP@8X~DxE!R>YMLiIc16L`8ukq`=X4$zuwgU%<5--d6UFxnapbSV?Q7i&3KAaNXn5(f}6SqW?Y#+
+y<cSIa_0Q_A5GGbSp13~T8e#l{{2fS-Lhr-i#^UF13^gZ6)>AuNEzM3l3zA8Fi?MPA*Xdx)<u!&<(R@
+6ta%mjr_F8WQC1~w2l~nqFl_+AWm#Oq;IE22(Q-(#i4TLDqN)v98w>{RvmyR_akxY-#+QG=rtl3$z8s
+Fk-rhIDZZI5+q1h$`U>fjsR$N6{i;=kiS~Sy&@U&&WRjcN&=G@98oE3{DXs=-S_aNLI{RRK|!%!{+{9
+8oz>;{_ZZXx}`^EZ*9pAXfNqHp1KSv1RFe=H6<YYx#J3xx9EK>~!|pwrW08*L6>yJ{SM^ualYCMTyqm
+eF#l#ipyoeOkMtrTeuqtdr2v%)tiLtc6JG+BK_|{!IPb(dm{hb<rwWdzpx_60;P(spGcL@9NLe^BY_L
+Rz+xl`KRbZ`PWs8CjAoB?yXu=>fW#cKJ(^k*AP@Gv_bjvC|ieB(8x4OmPR%Jc~!KdnNj1iWNx9FL(<J
+DEHOfJt*+jrr%hc;typbOl-7Pwm#f3Je^q6wC#jSvY7CM=aRiV-lh2|$VbUh=XUVb_C)AvNI_%RavkU
+~Q10nb!${PM?sCLxS4_a4b8s`-vFl9X$=d8sTr$zLBdJj@Fi^{l~?Ar$7d+Y$}N<_06$TWTexBShEBk
+{%l!JlBUf^ecI{zdr5@M)`oQ7#T2KPD=MX;RO_qP%{LY<OJGXV`T|_T1y1#*oG&YE+OHm!`_ji(16FZ
+8W5Me$g63?p{*=I1fps1jEp9qq1aPFACHWWwMA;3M3}n2R!z%{Iq{~*#gu=f`+O)*rFx}(7Qv+u(VCT
+vFU3gz9UYm3cQe={V%`%>dB#q?us-9j^!y#MHFDWlkyJ2A#t?=H0Mo)^j{Q?b=-k4Dar*HCq1q}`!Gi
+-!azufXQFCK5Tq3Xg=HCCWodPj#15Qgk)^Yhh!zEmb9VL;#W<s2isG#b%tdhzky%=!6kLHL;s7X<s48
+-Ro`~5^0hot2qoNedN=Jk{T5SM=19Zz*1|id2%-W9P`KvR5@_Q%-;t`CyEJ3VCh7+&@h9m)8n<cEF1<
+XjyAZ`-(kBb(lD_A{jlJW(Dl)?1NU?e6Jy@1qj=Eyzk1HLv_pj{SXKQvmOLy#*q;n{bmZ(lwauTIZJR
+71lh5t{v(2-HGSK`UfHjOuKjq9)A6{Y^T%F#uxJtR-P~6P3{n^%c5JB`^R~qR67FBohU+ELnQ64+`}j
+N;N+YP4Xy#$Eky~yK?;FO_Jm9=+#A9@=$Kmx=OP77-fd$LBr>;@Nsr45f^oqRFwlxi>~!MDufVypF9q
+R7k>{g9~rAI$qXz*5OG+)_i23-4BzK_d*H-^<<mUtK6WRxk%dn~r^jz%qXX+6_>%PT5lEaYu97$g2sj
+0h0AS#=J;_rS)?4*pj?GdSxXfueM_Kw001+3c|7bDwlN-G5q?$#`Bv2$CnWV20mwu-xTK6eBiYOME^|
+@xbbcDwu39mzOe}BLGDWijfvCJX}qs!sDU5rLh9zY9Q>oAIAA|k7JRGvz(AoxzNbI_#1(lWa5ykPx_%
+S#)G!z~cRP9_r2$_`op-)BQqS;KeXFhmbJKu9D0=|6{PkiwI{zB+w#eDv(>xD$jxV<VhFu8j;!Z#x_T
+YRuNw+ot`8q)g{4T>Z7!#fG~gfZ4_g<{e_B002Ypi3f(Cu2#A~7>BcBcAM1Uby9<#EfGL48fiu&*>}~
+zyI_KFK^=Ux{}tun)aq&{*JLOiz>j&6p+=3z;LCzz^ctB63#O8fQB8v{0!|a-Xb#d+-8C_|X2|%&U?#
+;pO0x#2EKYM+&H$_eaX^IUO-^gwp?QU)q8AjJ^mB~|QoykrWbRrAEf}!Q1ANf4YjGSqd=oiXF4U6)3t
+s1MfHt}!J|Iy1hN)195=k+LBqasE3Chv&oAc@E>+_S-SI=G|Ao}6W@qfHMd2{@H(2AgRBOR}-0<|)s>
+7XzKL(q`FfX;lD)vus22y|fR3S`-BvKos!BnvJt=<ehI=GcM^4On@{)W`;oM@zuGf^PAHxY>E6W~CN0
+0UNpORjph_hiYHW7^SSpvlZy-1b7mo8YjyNa+{(MiTKqS(nNp;Dpw95B!xve>p9X59xFG<qBN7ZJ7*8
+An=3zQcEnFV{nT~xal!J^AlnZf)eJ=z!bI|@I#{;g77erq53zwY7kb+mY-R_Iq{WY(Z2wtYSARX+SAU
+zO07A$>wHkqAzwcY^9p`qS7xhEmN*u&RQsqPG9H|uxw)$b!ZTwIKZQO=NkL|}IS_bF=kM$%*qfy5*GL
+Z+O{~p*sEH~h+Fn|zit^d%64J;vGPg#H;i$OpuH>kBd=-CSj;`Jm0y?URJooVuTfVR_)-mMLjGYWvPZ
+?yzx1Fa!uNWdLP{IUCn;9d0%3c|}a$olg$agF&KSZ6cUfz~pqlSPx&DImp*x*}5{qcLTuy+A1sFa<BD
+o=yXH$#-{e@1yd%a`zwLVK|+}=?t2N%`yg32eM^{T%15u^x{b5QwXzz&5Rh6p~^kKr!q5uxD)7LwgAU
+q0S?NfsKOW1)4z%TBjD3d@M%mLya^_0ZMyz7y#u;MBrV{4aQ`v+A(43f?EE|JiAom>^&P@;0xQg+f>T
+tfjmc|*i@?ySpsh*(HbzlIBv56Tx*t4^78WLM4S(flgz_`aGXnO~2%Y(b;%7w4ZpGeh1z$BR&>aPCAK
+j&8k&~SmAYOwJVKxI}4WfedFH;C1a`qH}I-szTpGj02c;bi#o~A(Uh$A#}>_A468aOXCMns;K3{nVFu
+r8R*B$}BK@Uu8hIU-TUlcD~M;C^QM;TW6@ElMzH5t>@@BlzXTePw`BO)&F^Suh$G=)uS^@X%fq{Afy#
+f_cvJG4iGW0)Rv$N#3VbT|w&+$&ntQ?e8@-*cPXvz@!!6i%_hv<piWX8J0@IA}Nf_CrtxNQH@1b6t@^
+$H%s{lNLWTFcveOr!Z>D*OlRv(a&HhT1V<*tEXyow;zSY(+m=ZN3-d0K(q;giHNZ`Xb`7pjC>K7iW@W
+l;B{H4s2}4^BOcwE|<Mr8`&`aH8*CjikiH}RP{bi0$^z-gF(UXaP!Hn@|kPvLsaR>27uZZZqHv>FPos
+*D~5LL1qStw%J00gu(Y8W)ngZngpdNA;Ln;p7~I=V%1hp!2&=viVcjnE~s6h8n0{n01v&O~7zrg?>7-
+IH<K!@!y`LQ)<7bWV#jLH0$6P$8FbA0h}I!xTU10Ul6)Fc^7=W{-{q9;|0g1!QI$3Vxcr2j3NAMq&vJ
+o#a@eQFXI8Fp>K-%S426DEO~9nd*i%(b96(q&3@N9&WHCO9lEM8MvTe8AD_AkQkvc2M0B7i;#Mw?ZiS
+QN?^^4n@BFSPIN%cy4-{r;zChLNFZg*X)Tzc`f)_g15#UZ8$SA?g*4;H4&Yr{PrLq^+g>~(_}y_@K;9
+%#E>n<7@++|_8Xy_EluQc@yDO2Uw~3ZCq)!42f~K9cSyZzGRLy*zzLz-zV9u<;oHvlWD#R}snviWMNQ
+Hnc*-IVL+Qeu>-6X+Sw*o=6L)zZ8l*6p>;Y6!I`~_3d0j(y7pSU_z7mU|nye{SO*D#9XK(_4K8bk>QO
+QcA7QB?luh<y7NIf^F$<XCn0+p&(amRm`FZLR*KhRn^V(NftDcEo?C%NK-oCF{CSp1^I3t~u9z5Pr#-
+A*2+2|Kb6cZ~Y@(@_gA9Re-=z_!g)wzfy_TO;km7T?WSQ5KB#`n3OH2)8VM!T~$yg7SL9<doVeEUpvU
+iUmh6ILHlc9H!?`-@#3)@Y#R?A+vy(-HO?UA+zF=N_rWKfUhmCt0{ng|dGld2%L)*ASAAQB6|gi(qX~
+{5Fc>IiWb9(vJ|L}(!C!O@^KmF=lFduAf6)mlRB%gQp@ETDZ}f(2w#74C)7M7_Lk>x{+>lSZDN?@K1W
+xU}!cW=R#`$@H1a=_zhVhGJyr#BKR^-yQm7+F`&`H$}U$0)ThNN8EN=>gka7)LFWqmmj!(XDiXz1_vz
+X%Vjr!khb`E&w)52NjFK`CHH3s-3lkJN*fLv1xGm;8JRANndcHLP}+8F)vu%{$Q_)kv5vQ9=LOk!D4f
+@!p!ww;U;0S}qiH%$OJyk`yq?DdG88_qX#D71vU-nN#27Bi`;%kNBIK$+McPijxgFjH!f)ayTJYgC|Q
+icU9FWiTq3|2eh4L2au)3)vPjP2_NH#=zt7S)@L>I-u9vFW}rq2R2#exHEb!UCLAC}PM*}RNL3BMrmV
+nA^$VnGWTzLsP;^DxdPu6&)u76_o!sU$Ayzc2{T1@^8GX!lU~wIJ3J<G+c(^t_jCi&r0z*lO=IO{JB#
+XzV5OPZrY(dN|SQ1M7{;LwwNwgNXNuK^gyGe0U!;n*j`6nwbocc^_l5bDHpT0eR@f9aXnx&~6OXWPkl
+Qfx@iM>NRU<57>CztQ4-QZ&H-F<lZXf!ZHVG0X;ir#mW(e#3%Os^4j+hCn6QZPb5L8m!dA2t0loP~B;
+?Uc5VFmQ>kil*kB$+U!^W&fZp126&>7#9_oMB(Ca|KL&wgle07FvW;^Z(t=dsN+NosK$JNe1>04`@~P
+6msjp&KNliQE*N0aDBs)1LDQ<(`|7JdecgUj52+~QoeA0*U&Gn60qr@E0K*;>jlo^xp$OKp)e*s<Ork
+hT-F&kCjL8_c%Sdk`V+hm8b1dA76A?=BQwpHEDVnTJV#y6N?m~tsQeI6}H03Pe#IX=3D8;`vX^G1-hh
+`;}HDC&xQEp2`Q_f9SD0lbK3S$ubuH+9errn09E=nWQdm`OYw7(iCV6%wcr;BDmX_fnQCiCb<;#|)UC
+!)axQKkL1kxad3DTp*xrK|)(uLPBM(7_W@cIXq|Pys>|!!&H{n530GCMy<9-&2hZX2JlzJco1z&+vdy
+uG?#PtO;;rxy6aaIS|e#%e^NDUpz&E&!ImhU=a*YK=mrscy&v43{m5>qSHt~BXPC@fAl>UBM@P6zlJI
+j7`QbJ2^eGG+~DI~l%-q<R3*r2QrZ@1fb|MbyO3<9_S69y?Rm90^4XHTD!rz<Ewz1Q(it-nEncZXAIc
+Cg%jJdy(pnx@X{V@R`WBEj2<lIn4N}M~b6`L)bb&b5Hk4i$tq+)$lm%i;i4ySOg)ZtPpsg2Y^-vR>sQ
+Fo;8Dcsu%4t;>%fL}Xof+bvTrFybPS7Z4b_UQ+YFw9{_{4y5H5}S;;ylgcDa%jbsIFdocEoe3Y$^dZb
+MLEA9H+80FrlT%qS(?0lv5TlYTcUj9bJ>iC6}pEb0`qY23E_nv!jy}+K$4iKv^k3BYS{hDXU_DLZDkw
+fLKmtTsWa`${XsOpfZU3mJeX@^gSYke~Sts4%G4kq!(#LmAF1~bT^^Xc`J!>C#$;;VGlO^@R3q@AO5Z
+UI1ITGD}YC14XK-!V{KsQ0_@;q>s4NS(z-q&3JmD@z#yPED=GW-N}{!3h{Ww;>bOf17?uHS7Ef~Qkg4
+Rz1q!a0p)aXT4<}`I|0=nO?ou$Pz{o{9tE}7sW{E(V*G@^MB!tPxO-vj#`E6d@x2uLrl{>aa&%m2yUj
+t8z$f5Y~aiGLRM+|G7bUzXCZyPyE=dBX;mS+*mI9Kv-T50xG9{nb!$N{%wOiRq6CgQRzuCiny4=WG7{
+rXw62BN_p2JLdlpIYyse+MNs+9Md6Ot{be)xxG9av(8rjF#!bQw!4xSD}=fXf({|dD%GSYH}F*|KjPP
+y`kjVQyYJ&aHy-l5TCn`u?W#;yG<KNP~4!0m*YBu0c`4N?fuLOnruJm5N&R@0dE&}5AqD_^RXXGY>bi
+os2B}*#0xAFDe^n4z5<l~UHp0P8}aYaV);KrQqMvsjK@PgDzN6<lP_{N!u0FZY3@5Te|UhW=7XnW@kc
+3kP0r1MMD3|B2yoT!?k0?hmu}<W?#n>lL}~tgwET?pLkHIPyy*zgmU<0Z7LGb%<(_ePy-a2e5aJGv^O
+6pEPVJvK6WBvyxw_mwsB<zZ7@G{kFm!Xx!=bG`&>y0JkdH0gVw6GH=-9T+JTX{Oc2SGG1rB4#&Umgup
+nw;BsQh}Wh6(Ugss84z16h{SvdMFqdZm5YPAe)O<3qE)?H~0=)u<wkxy_i8HnhbM^lpM;7dj!(qOo!N
+Q`5|^{tZyH#|uOFhxhtQEtrZGtVKbdnYR<D9jv}u2hl(bB3nE!(T|)Cai}!S<B1(bxW^CJu}slHLF7>
+tj06?UT_v*!9~bCr6g7CJ(GskC)g)C1oMbM9ii;v{GQ7cKg6yMRUW)>>&#M{@ySTTI;$pS|47FGbgEO
+CNfpC+xP@){{3Ln7R0L*GR9D`fv@$Z;361>eCZ5%o)(cEoSDl)M3o3W~3eT!$houDK2Ra?1=ThNsVKx
+1(A$ASS=BTzRmIkR*P3yHwq;sqW8V=mmO=lyKjeY#7Ndm~+e2|2nGCZjz;1(z~3V698e>Tu>M#O6`WS
+t#%tm(5j{&M31pGqNtah!L>mfn&kvFzJK~3{makAurBC5YV>dlc!f<4%onwW0XpkS`r)|((+^qDAQ~O
+4Y0~KVud3UIc*SplJgZf5nA&5GOd$Jk*VSYU7TbI+a{_(Dwdd7&~>Zi188Ux0|Vosn`LkdiIEIln{on
+eQaWV@m}_acTA`jM*FuuduBVmrw?lkRL|F_4H~S9Mb3E3fA>@(5ltdQm&}0fWe9$pJ=6Bk^M^q?tP}K
+oV2RVN6;p1i7ZbwAfeYAq%L2Qz!q6#iV$|a6W5NP_Kn)GeB{4H6ri%)vg0Rk%l4;rokf;Lx@go}mbG`
+dQ>*H-AOF9^y7jB#l_-@(No74=YBJK1>16{*sbM51?t6!ucoH2o;hS#=LivWTUnB@)2Kt!kFML)~1FY
+NB&z5$1;Hn6xrF+r-5}Lrv7Nnu1$8?%=}2X^lSqMv<ZF?=ldgytVZv8C7M}<inzI-qj2ojpPK&q`^r^
+-E|XHH>&I(t6B?~oZc*N$4xgCx?qFekzHu=2tu_^m}Ol?z6z9xuxwgV*?|mZJzI+>0`ztNi|gHv9LVI
+Wtb;uK@B~DQWM(?mhWcahVgKVu$LSqu<@x*0-5B&U@uUG3V9|=$!Vvv_pi~viGCYWiSzanm-1XG+43C
+uk(;8G(f=i``D^D89xZMCzKtpd{0LBflU#Ky0k#M**E0!xCJ%_K7`xY@iqsCr>L^6lx*ctseBoJPQ#y
+dvyild7qU9;dtQ>p;Tr&bmKNJtogm)^=Qf-%rW(Os^{W5N&id;qQK>?X~mvw!HaTK(;C>FM)dqWroT@
+(CU+tz9<NjT08wq%fvW3{uBh(fmbJ=9OT&t0DsUndHp=!$)(nrkG*L7zFDpYlh1Ri^Qu9^IJ1RE4k5~
+AW51c`&JX^<rFX4s%!7$$wgneofHFB$`fwqLIkI0g9Y{4Md;QY!<TICw`-1hAHpZjX;Ho`HM}lmO%Ls
+4KcebT2pEF1@vg!*$)dPR%&pjn)cgX(AT14E!NL)5EacEfQVWxA762E@VduI{ENe|F6uqhW?Wy&uKvq
+?2s1**o#4*Ljt4Jl0cA>?tMG}*4DMThDOkFz7Wmj2wtS0^Wv<eJd|KH2k3|v}oeq?xrPt<(J==$EtNQ
+G81=I0}@B|T0xDQkk=DFI3sOMO9EG`mTr`0JD_9X_>yv;zRIRk%Ut?y9+-%JXiC>*Jx*Sn_F}_UyzkT
+(0c<d~6{8O$MjUgcK0|H0_7vC^kB@fLZZbOUv%wL))zAl!VghmPfM57U|B@5(3X8evaPzmIU9~5g$H$
+P**(RPEQEj2Vaci+*z@B4DMOFOsdC!`tpk}zJB^x1m)MG2Rq_hRagqXfH80|l8FWK-OGl8RJHeW5dLu
+t|NII6`2zp>l2+?-wOru^udmtb=f?053>clSshYHAeVFF6qFiD=15bm1<)uCCh+4@IIqb>I);C;v!lc
+saK+4JLZyVFG1S)P8i`8^HfIZkuqbp>f>J3Z2`XNp3$9%zr{CooHmp*Pp%eW$psc&)wT^2K+^SlkInM
+uh4ub?UMz#uLlDrLw1tw8RGXSj2BlVCa`W+#YZY*Ovu`EelQUWDZIkU@TB#=~P3TaB&ogcBXm$_3fp{
+=%*}rw<6-y$G)qHamQqUhka>l~S%#yzPQ=+dz4<yDq7U_<5OJH(6A+$p<`B`Jgj7x_l?ov2C<8vlSM3
+vNXS~I9(MN%~eJ<Qh2EZC9fR7bTW;G?=%&>lSjls4X2|Atewxn)k(6%zG_L{Y2iEvR~A-3(zoJl62K2
+?+`6r>;2vv&c+)n@t_$Gc&0=JNJO=elXi*Un$Fo!urmDbzqt|pyxJ>Y3&5PG>j(-@7SI0k{kHz1P|Mz
+9MJ_FUBYlt^wqNJos31iZSLY$@7d4$(LBZ3Zh9p|>oX+Dw!tiy{Ru^6wRONR7~HJ}iS1^g5{SFWifH;
+hLIzO1!GMK|SDT9=67`V^MD;elL<?$s28wo~Fe)F&dNns4{mZxJ7hYpnH+hvH!icZ*UDDc5CD*vX(Rs
+<oW%xX;S}U0k}&=x?W$&|H=CTl0n97l#-dT;RvC;D6C>U-k`*>v1AJeC#?h)2Tc~WeeK&G0d3cMKzdE
+@NKkrMYf2|r$IUvtC9HgWb%zD;*0chva;Ea2Vps-${{DY{+tu2<WIErxGL)!kEBMb95q%)WLarAaB+F
+EX01Eg*+{a=%(y=3k0Zk5AM@rQ1|glJtGCi;l{q4h#4`W;WD5VXs8W&wZl?F~#r|d6q6G)6R<YwnMtO
+6Vk4~&-vaK7LZV_@y&dVo?^SYsz^DP*~b__~^^{Y#l@y{u<kDSMIWxy@U1|$DDb=XQa7!@oo(7cw?kh
++)eQC(+A!4jn_h`DOVLz^X8WgF9(Zz`*e)s_X_Zxts_3g4uH5s+JL5c(8TBV+!UC-0dj7UNXx)YF$au
+KRZz;jb(Vy3h7GRA!KTE}26K)gm4_S&;SeiwFOdWbWHC2aL)1oWH6V#8%xEvcmgpsNKauSNi1u^mzZ$
+oneXQyh`GAvozYsnW|rY(`=vo-m`sjxov0*)0DBP*!zZz171}pvnl9oqd(35H#FTM)SpY?*QsxoSd<W
+Uc-;!&7Ed;07l+-sZJ`N2)7<IE+<d&HweD=(kZLn%-Jic2lWLw|*;v<E148RW5^^QD1#LSTx0$V2YA3
+L4fuV~%X67~g!d{rZ#1Q!M^5n&-a<S%YL(t~~WH5vM!g@-#K5uruxbn+u#hg@)Z8$02=Is7&;I>4$I}
++*iHO5fsI;r>8wrQ=VU(y`yX+HNCOPa{-_Y9o#jUa@ar;Y5}tJ^I&jA3U2Y)WW^1GgqIbA1ErIeRh_7
+IG`y=siT;%B2|u?(K1Ajy40#h%_$vlEGw24p}a}Efp?;Y|bb)+1s-h746;hC^mXVD@esy6kEaqC}MBE
+p7y>yD_1J6o?(<)GY3+el)Xb;v^%QumO`a49o0<=xNm5FXpP)a2y3z6)*6@5W@O5B6xYuQB~HO}9UW%
+Qh^HG^Ah%{aEHLcHnz1F>`^N6)3QfD;6KBiIWb%+?6OXt-FIpDMxasGjh^9iK=@8HxzWGd#Y<P@~UYK
+6NTY3O^$&4Q8fyZjsR2-98df^y~lQ}$~n@lK~rB3yZ=~5tbPfkzn_z=DPThv|0Nb3%?8%mm}wax0oh1
+Wup?bZN2B`bF<5BTW$6msRYpb03j$-&ar{U^D)!4gmY;1MyrQ>-#~KMz|JCA1f69h*=;CEIbZMYcn-b
+tLb~QaO(UtfG*2&k29=zlBJf+6f-UT(LC5BuSw089R9rI?+wn*TK`zLX=C5+U$%7o~V)Sp{OEdLIb(R
+eMfwcWxV(@oKhE+-6!xHmKayfV#(VAeByXOg=_XvCaz_hh@f*isAa`u;^T$tNq9MS-9GJ3EjeE9zs{D
+F12**g(XC;ba5K;LZDw2oB*(<!rpm30_Km8R)aMWGy-cffRqvKl>{b$YL7}eGYs?!A^lYQ6tQXc1^9R
+fbC04<e-tdwwFa?A%bX9_Mp^Kw3wI<j_RNj(I@I9!(`rTYrpQZZsg$%*%#W=nsoqab}F+zKw7rE#hR;
+CLnt7VbX;S1jPb#<;RxzyXMfRA}v7Dbp=qj{B^xw$S+`EJx`hTGxWL6-I0np>SF%7OJiwF|PHq1Abu)
+W{bhp*Kb|ecrFPBCbkm`TLf|cgR+wQ_EfW9ZUFb$1#M35TF$Zn~leW7;>vJi>?>3na*~-Uq`>Hm7-?2
+?c}YZw6U0)<@9}X*L`&IW$8&XN%LYsJhZ=dliGMBW9lAwnD(7`Nat+_Al-ERc?VTvhX+lphgQD}1re#
+azd5n8Hpn)IeF$2POtM{mq^tbKy*2UJ^<lZbGcORSOl?TLM<{S*_RPI2BylrKdUA5|{wvyN7irF=9CT
++gXqsg@yUnmH*XA6#(-U!V(R#+0wlD1%WU$3_M;uv$Qo8lB+RpEgQ!{Hn-(zJoTvpVand(THZq+MQ4r
+9h5U_TYaS9kWRq`rPz);VILkZ3?*jHqt-dQer`*b~lq3K)M&eJG#c@@b3V@br0G%z1VJZCO=f&0crEl
+Q(L($ckP+Ye7RJDPzUt*9IJ8+^rV@Zx@)iPZK5^EE}Gf#Bx)Q@US*f)AOvGekVt%+RlA_ziJgr*4XPR
+$Y$-1+GK(|z(>Vm8I@^;*_Q%{z<%rLb9$`<RSQ&jX>9EtZ<G0JySLsZ)&=?s2=4MqFq@r%u5)ZGwEl~
+cGkM|PXfnZ%ObXtjG1M3O>+I9YAG{>y{i+UOUCQUlTFR@tKcx!SLvNKGqk0WIw?dcWq(*Vjmz|6l9tr
+Bp(F`zMrJk#moYZ?%0n4tatT^GVRBVgMI9T0Wm@)9pNmb%_Q)lpdf8IvnW2VrsK4AkU+}w*xTMN{OO>
+eSBA!8)fK7l&a%ZrjONm3ZuMQK@aeT@~Je8r%?zALH{e$#YA*&@<?X)G(GKA|-5<oWTd^OF}R$8X$s$
+6iEx|CnClzfbnQo?h<$LB4v)e4UapsP!i21psRy%l9!=R>>gUnc+M1W4sS&_=>Kz=Zi(uWe575V29#i
+iN1&dSCW*D+cTD2taGG~YvXHNRA`3FMxOJ-FH{?f$|T13iOJ#g8z!o;geUj<SM>5hx{X>M2B(Xfu&|D
+g-sxl_2T~FY-Xot#NZbz(bt@1RUr$O00LPTG<-(-8S>k=md=M-xCDyTogBM-Fa9o%|uSSd1+ruikiLK
+tPW(=ubU{xk+bPdpWhe)>KfNw`WqB<o>G<9ta-9#P9cj&S_wnzTNO_9ZFcRZTaU@zP0x9wqSbl6p&t*
+5I%tz(s%hrSZL!C>&rJe6<Sa(Pk|Z&q@Y^5!#S*0zSfgR`iOKr}R`7)tEiH|G#B<PBGv5h^)R!NBu*Q
+qtXJ71EhDP;ny&YwuihsC#iy6{4~}q2Y;lB`CV4kROle$~v3=V}l<ru?A*iZV><4KIbUWz3-vIQA0kV
+AxXKeA;)R(uTQ<YN31{Xe4E97f4BeU|Gy0X*W}I|-uLfjdieh7Jl}>@zXuOC(|;@4_tM=%b60sIq5)d
+AHOiYdD^0bn8h)zQrdr_t*4qEts}}$4m7B)zCwu$rcy6`*XYhJkHvJdp!4?aY;0Lucin~PV`9Eci5~S
+a6k^X<eIH%#(i-)=EhKuXpuXoNmt#!^RD3QmORg#M?q^*ht-N-v{GM)IcuQJtNj!s|wfUiw`{p|ew_{
+}Tfy!7|)!T)^y=Vb|S-+zNK=jEf(;p4~nBcIj!CfptU!93OY<LR5{y}<BZ>UZ~#M)1F}2mJiBfB1Kyz
+t735=f^+w_qs58y}NXVJNfI$ngQcS)_h1vr^=MjrS%`|>=&4ip*W=fRlA`rgTvYUnwr4>o+cm7WYUj`
+SftlCU}vMeR^eCF8ZLhO61}jDa}HU0)%q{=3++&1wmqG3CMuXt`HANRD%NonmM-sWLO9Jaal<{?qpyR
+d4%GUwFn~o-WOs=~z^!dD#UKCPaBCe!;|m9A$yD-o8=WVR2^qTaSjD_HmULBNxOO;s4R0mi=QRwS$_2
+{nP52$;Xxg4^an?lLJ;)hN$uPVWDei9Yn6LB3LHtUJu)zBiLwrFuw)Zw$d-0aSN?p<<w~Xnv;NDJK7T
+c=7ZD#vfxTxF7u#NR_-62BVW-RI}!SB9MFUbi;csRJz6w6P|6xb16TBXESJjAqD#anQ&JV#wb?{Gr}P
+fg)24t}Z2XqMnhOX#i#LZv%9O1(K{{)VEF%f@%z0Lb!lxj(M8mwLe#f&ev+Y0<iv6<O#CzPS&iR;cgf
+h@sFv7Zm`p0hOxu?xif$A~H>m%xPk$j=!<!zh$-i%Ja85r3;NT;=^(P?De=Z{?ZtUH~x3Az(d->$Rk#
+<?KeS7zI6<w@X2Mj`OXsbUM=D=!R<WeEggL1Ux}JzhRN-vm`S<*g^d%4H=&Jx8!n?&m|2E>BW|1VqZT
+n9JGXFL-@e_m4=ry&;)Aiv`VsMuDFbcS54N5z>Tj*`HvjNFe;^`+BOZz>>CFjwgU6ob)d~TX5aRYo)^
+iP_e=7O<iO^(N>FT<`?Qq^esr3xrZk-WwYbo8`<>;z_&6Sq=Z}<?C?Q$zbz4Dy|*-2Iq{_REcbh5&yi
+(_}ke+#W^eV3k_+5w@xV(Yc!7-xmOn;Z}VIcU)vTIi@9?|v9zXq3?r0J>fw6lc;~qdg6L?;^b_#PJ!`
+3sG0bzJ<Fdg3g?|FQU7|#d=3hu4rRhT5`m8XL+x=+p4FsQ-#WoPwX)fy0zCKHe2bs+jY2NnA?Y|lf&!
+Bl`?+6Kdu~JBeL)OtEb!9O>*?lfs-P?eG*+9IKrTJV7WmF{%L#V#Lv<~^kCGl$>P>P#Z-&rZN-5&Y0D
+vh9{N>X>t;zb-7uCmO&El)rpZ89_gUlItD9W_E&kktmW!J5@OAXbPx_6Lk^tQ_;PJdAAfC&$lrR7(=)
+Ljl;$F}pDmRlD<qh&43cSU34=-}euts+t(HpHh&g+bE#Yh<A!(rgozRLxqUl%y@$l6RXQF-Ds8wP6d$
+{8pe`q;W7j_CE)=zH_-eY<UMpNZu6Y|5RM-~ub(q=$N|jvHZ6s;VFCl7rjPWlxH57WiM`iPu8nEP4*6
+(wpRTT{yMuFAT@ON(vSaJT@VCMa7rbj;bjsqV*>BZJlOmy=t!*mv4|!g#&Cyu|j~%Xh%Z4%4dTPulux
+~CHVgrP)h>@6aWAK2mtey7DvvPfCGLm004Br0018V003}la4%nWWo~3|axY|Qb98KJVlQN2bYWs)b7d
+}YdF_4ubK|&?=<oh3m~l;!a%9bZU&?pRoipRzo4TEkH=a%Qm3K;}B*^BPBDExCTgkis{q+aF0aEhpY%
+W)KtYpU`fd<fMG`btzjl<;l=s20zi=w(dNjBZ`_z(Ez;PBuud0wx#O>uqGCE4>)^2KMLe{l@|`6_vTB
+a*k<?xwDii+b7J=Z#3dtvA&o?~1ybK+OvwlJ8$WKYx93KItC1B(E09^Ea>GzkB(oAKt%tcY!aV+`*G4
+Pu`xtO}_l6Z<1oQF2zbz9Rq<c2M5chUM17%a?@=ZF`Z(Wy6KWlRX~Tuv@AN&<YjwsAV19VR($i7`h{i
+Yt@%}T;-M>xnR;`Rw>Q?$vcA3sB&eUYuDPz;;$fY4H|j;Z)!+VFtobk5*@^1oi=xH0&L8GtjRP7djcD
+slGZ(EaIj>g$RF%Kz8fg6G8~O20G%eIpU#~?sZRa<DpK<a-)o#}KRxJL^rOunCZe+!k=yI5cT-AIfe~
+*(_=kL#6oV`Dre*5zK^Vesu&Si~FSCp!Bw$7VYOdIjbrf6t3#>qT~h8NRyQ?EtSZN~|hsq&SW!n{spS
+H}lQ_%tG4OvMAjZpX=e0~2O`KEL?$JNPad`tvXNu^Am4989NqS;B0eCYLFqu_<O7L}5Bk(m%lrzpUC0
+N{EH~(QZHODU+n<*ViwrWj#>U{`3}RJ^cWELgEk&bV*Xl1ZL!WdY4TZ6;tT?`HyekynBD4F%0F?^>zU
+OkL9Q*Key1}!Sgq-UY)&u5iJRHu>!oRq6aTtUc9Gr7f>imU%tM0fA;<N=kL<Ul)?btoj-r`?nO#}{hu
+GsFW#TONd38iPZw`~c=!DL0?PTFhME`ue)bNkjkxibufKgWJ^%4N_Hja8EC7Shvx9k=w{0?gjuevBv!
+BJh8=X*FxP0;7v!tbk%~X`E^7R_9mmtPk;tB>}f=jGTx;j}{GtfHV7Geqfps0#&I?Y;9E@g9e8GK$si
+6SqHRzM?3fy7YF#RNGQ@T#Iss=8SLeQcAu63Mb|5&`{7{HdQrfn&Q9904HYZ~y7O#xX=*UyBMj6|7kJ
+_APKMaiE7YFGb!AjpaEONV*$|!!bbYIz(d8A+n0DO$r`v0>kXYj!sXw!BN=n_?LmM=Zgh(d`vUe)BUr
+>BFUX8#a`>-nBJZMU&&Wg@`Sb+zYa7PA6v1cvW)zy2A*1(QJpxnuU}v48XsU__#-Sw=mKoRSo2NUWmM
+%>01LeQFq!}<i)ukHM|v&z6ck?-@5CB*1K_T(25>r)dg&59lPvNtAW$enBg?w&x|kEy!VW}zA5Dgr$C
+zrKz6L5E1I&53fo)S<U!T%U_7NO#?ao;^3GEczjd4=bhDQBhkVuTBxX$O>gwTOIO8d+rqi(bA>KfkZL
+5h((;D8Kh>WB<z>@fYC7RHbx#$a7blr@0cx+z3ENnS2#>f5!L7t2B{qWe*zF^5b5ATEN2Ei9cN(D%&-
++U;(N)}Z6QC`;H0>RVzR$i^BNV^c{i#9Nk+JI^qldjY`UUy!GOU7-j=lP=|=UW!+Pz`1p4T=F0_(kO!
+pKTOrYv4c<o$K6u`Py+?5Uk6@6X7;CQXBfpkODMxds(e-w=ZW-&<stZHgrh8#OBJ^+LN+c4{O-W>xvi
+YMNXr(KHVS2&+~wuQUP<x(Qz?e#e^P=~7It(TX?Emo9sG6Y2GSDCEkFitVCnk2H`pl{YJ5j9^QXUCc-
+j`CTudD*AL`uy#p1RU0ITU4EUF2B&*)7rr?9~sCVvr0B><toDWG@rTU;aeBE1vPX#ta41FdKgJjqKGO
+@Wio#5@N+04vUdMTNgFs?7tgqE4*ZBomYC34eGYW<_2-`(d`Jx(&>11HZa<ga`)V0+b;e(sorA)h(>@
+n*sz35J_OA*H|)H7NtO*LK7pZyP~Npl(;7rwxQ#v$Um(qK#s#}zju>c7S)1;RXyGbznVhjDVEP@^KdE
+wx@5g5Qz6O)6$+3doDbLzSENR=G!5V)Uv3iUH~ldw+9^WH3lJ>!Z3H(-zyxVMSu@cCbK4?8uId`xn5a
+$&-tR6I9|gtiUN;kiZ&8IHdwVSbMc6BD8PxIwM2PmjfElqR?NPiDrQ1yv6IY7q<mbAmcqkF2ARHY&tZ
+jJMa9W7hlbGS^@qRi885{{TuN2e_*8@mPrwn<Lw^La4B41^ro~=Cc@HTLo@;;o#N5Hz=WFcmo>ntV1M
+e?)-O^7EtUJ0juf&%YQA>w_&hVE&Wrz8@}3S&LyQ}xIG!$S6ON=!7UBFccmaAS=SSe7DBW!>!&O$QHw
+Nk=_2`dcHnmYa)*Tt<$)=d;$bdw0B;=gfj>Q6UHMBs3Vnx?XQel!>+4k|0Vo3>MwGo4iBz4?tkl@qG&
+Vr35UJR;|5OD_TxrmXhKV>_MO%IfMfAp<9K~NuVbI<D>#=Q;BZlktqk@MXTgu68^_Y)b`RtE;a&?09c
+#yO9@a`cGjoi8yNm3q;L?ziB5tqb6QXW?8Q0~0HPZIMpO$7;HF+^V#QwJs)X(}sB&iWvK^C&1f)wguJ
+zC^sz*my@QCQ)NR~cINR<&F>$OCXz>MJ&b_N7MQof7E2o%6~5yr7-=lL4S05PmUTbzgqK;0%+SLtzjb
+p=ut0G(@?jaZlY+@dpF5m<PNg;~Ub6~Su@tt>!V@YUFlpcHmmezt@j>^vDb@DtftNd3fL$}C!KRx{u-
+W|B|zh^fX23NdEXXgomUNVUkDg=<$J__M7X4%&ZXWJV7nquBsUsBsIdjzOAR!vHrc(G+tVJ%kBp5TKQ
+zM4%suSW5#11z*Viw`32FjZflWaEJ;|3}upBtaCIx_jUyy5QI^<UDRlr#fgC>ET<6%+cVZdkeMLsH^C
+g~2J-espfMPcq|mtnovm08AqyU^iDO=fs?}pAEYn9HlNwV#GFjnkPqYPLOIWuGV0~53UUAawN0uSE0y
+3DzaROv-_?A>C;R8!%m(F7TFiL)(*y5hqw@?GzTXlNd)OW>#?5GKygup;}6GGEO+q0L+v750HOFFrVa
+&1`1O;9poLch|sk=9sKanZxv0tv0`l`3(odkjT(^Z~jZ5XOF;=`)@LJ16VbpJ-ELNyV`#hC3KT^yCz8
++X{QYyA;X%WK!7GzQ2D3f<#!mr%~F~b~GGoJVw^ibb_m*hqk}h6r58rk89UPWV1-?JsRnjn`$o0lv_p
+ZRC-x7Fe*?Tf!VN(M)t(2PF8gzc(by1A{r-hS@Ez2E+H1m$aq86_g2BUx<YjswljFe?V(vQAoyV-9}i
+M=fN4OCYBq{LIm%Pg<fGiHtnY<w+WE~_g|gD$ifwYrorqJA?t5UBSI-Re!rNcDws5?N4sU72yQRD27b
+VXA;qtQ&PL%<rEYc>eto_^7#lyDczQeW5@>|P|qHhiAorw+#`i|6yUju)yp)d&t?%o2MF`5vhq09XyJ
+0N9MvrWTt$}Oq2&?RA26&;(|XM*hX48QCIABFJ{+h4c%L3Ef4XZ>qvxOXw1BVpI!03Dr8DZzmb6E#TH
+q*|PD5mgH&xslZ5xrN-p0h7g?&~1smgVK>avxh!_fP^AENzi=KhCc}?^y}^U+v6|4`WpDgUUKK4AZ61
+BG%&v*tE0nmBwNw`3^Chc*K9-5H}Gt8|3tpd(x+{bJ#9y6@-)d@UCGrSr40IKoJ?|*h^(?44~l({jVv
+uHAb~=z+1i@6r_C7t4^8iBMhQCTSaTh28cKj3D^)8*^2do<+@a*KMxd&AIO_G3d$mRn4muKwxpidgsB
+u)yPOq(4mg7c-`8_@nk;#8trmvoU(?HNV@n6|-rlX9;-Xq97s#}hzgBmrP*=-)%h!WJ=JO0wcWZy9Z@
+2q3bTwC!;Q;(aKq)91CK}3AcKF<Ufn>k(yD12?!v{<sVszZf&0o2bN50{?{U+zIfo7ffAdq#+h1Q%`W
+A|n=4G+Y*<mDoczcwM7q8ygzhq9pc6ab-5GKqe9-t_VEFZn|Q=Df31H5BRkH!iUi67tTm4@@9Uc4;%3
+G*{{sJ6!r>p9L-yj>IZ4R#Oawb$)KV{$Jp2&&nOzjA*Ab(Fa78t;aN$z1dzcrm#WZ*7!zPwhWEcgE}(
+>-2p3Y1Txl%RJYtn2UhW9?4!y)Qau@a1=<{E~1NnrD2MYBVq#Z#f63btsVRIFpsyrytI4`KweZD2ZX0
+>V2{SeP4(Ilb|1mGtTMcNV(%Rlj0Q`*_E2e7?E7dCVDwN#y@KW3W(B=N2Q7QPVZog%9FR{QPP<GK*!J
+cO1>vf*Mow^e;ljy5yF#F{g}V+5F305h}=5*VC$?p~5FJA()E;T(CrLkUH+wkMJN3`nl)Qjo3Ck$n_)
+aVcM2X@53Qwe$fJ@<gCP0wtYlpnsLLT;12r!kQ)KtMl9ZTEJY`UN*BWN|}w_Pn<~<LY;Ete_~KEn&{C
+*d~Z}IALpdz^AZJx8IkruvCJmw2<-|w(awQ7*?3x*C2v#hz^T7=ET{f-3rGc0eVjN}Gb%yao#j3y)lB
+?;$m9#_=@>>Ks<yzLAR#~4rf6%+C`I*^%xJae3&K>ctmkA%@+43CwkWPe8|cCGNj!87OecTh_lM|XR<
+L(dvJ^SG9k+&V-TKm@*amQDpW}n5(cA*jp}y3Rgy!}P7F${8i|M8*+u)Sn>U_)og!Jj(l(UE?Ik~w4k
+RekcQjQcNaPLC2tM-#0-hEFKLoQCc3B`7n(eq+@As2%+I6Ajip9x8Sf+cv5W@>hU26`<E7*fv&1BrBC
+oM#F8o&hlJRb%wgb(wElbEbg8pw>z%lnG$YZh(RGc$g@guB~@yG9Nn>JL(&XlGTTY5OD~!9A;%3MpZ`
+MH86M5*nMIEKcVqws<Tg|R@XD`b!o|#dcw8Vp$QhfgZILXs(~gF<*iALczRArUI6T5*#`0@efFHZEVp
+$fJi@DK-gUN&zgqaC(VyJI>+htWtqqp<cFiY22?RF3m~=I4S+IWXZvH|m7y5<mXRC$PnQ$?6P+$^_fq
+;w?=2WK^*V_C1UO5gZZZ+zq&1O@n#sm6v9OQytrgqh|w*f@j$F|_q?QeqRpO0+At=!)T2qxXfw&B$6Z
+(<?tKDGtBs_g~IdT(o&HcO<>H6q{QDs_EdC7>i*Zb}fo%63F5s)icKFYt{;7po9TnFPq@Q)FtQ(!h{H
+{$*rU)rT?Z1uRSUxdpzn8ePG7r01DjQ^_%#J+MC|`*75)?Y~FM{hsCUH3(a5i0p0@9!x)t3B^V+BG>T
+9h{vk8kJ3w1Be0BFRx#HehCXq+Spz^?zj|VZT-b-nk3aq>_vGVMy%4M=R^$jL@<p#HQnF3h(ktMXSvs
+#bC3~k(HMPq=Z2;YyR>gmijxp?s<X{gUDSyKI3GC+ua9rQ?-isi%QXF`YlsO2+R>XiWogP^0PMYG4>V
+l6pw6QOy+!np9?4vAg#(wp?KI>W3>o96f)xD3|zk<@CsqHazL_>zP;<GIOSYiq*W!(%1@+gx|f}7U{r
+1D0EPULLo_t?`o3Pqj97&+8CFWM}Z#REz($>r1b1IjO6d9$Fx!zNuh4TQyiu`LXV6px;^e;ksK#uB3K
+&Nc<fL1~qw+4ga4AvDK!<i~;~-<Bj_6eI~sz^mzPJo<*!G<JNOB=5;e02~LV1U}lzmHVicyb~c~0nyz
+;_AhOvJ+KrbK{41&O+?hSwhC+DySEEO9rPk&6f4sfz4B<3CppRyLJH9Z&6J}}nh~Rvk)AQTny{*5VPd
+GBrFZG7=3O9$h6@x!WQP~%b0CXNCn%Vft*iIBQo59=K$ch}6;#Yn1RB(^5RyR@`pTF$N{Mh@+9Ohyz1
+}#{EaH@?IzdN>l3+ZupRoYBbet*Ek_uy%{xa-<>|4OF&*q41m)U46<E>DJA3B(QtQqO7+F?bKDl^-Xc
+>el#X-Dg16w<k^j<BRvKEqIkLie<fYT=QgW}bnsk~ToZh~qM6M~m?s%A*r`0sw`z%OUzDqTht}gT4wK
+E39#;6J9osh(7(AdjONf9ne&y5rB0SlKuF=fex5xGm0w-c{*Aq2V9L|rv|a7cOgih>4_#jZLM^TP`v?
+T<b^H2!sy+z9KAZn36nCD{tz91hbHM8$w}%Zo5r~^3ysdf4VB8v>$-vc2;Eherg!ShB|OwIc2uG*wsl
+k27QXW!7oBZ8%g7<2@K_iQOS0(4z#P!YVt`w@6qnFSBNGnvioBLn&m=j2$PpH~NAfYW<&SW6m9{te=j
+qi|FEB3h#TQ>;A^GdeKkzU34}Sal8})6q_*(ko4ukvp^Doe2mHnU;I|I73j=6Zz@u&Xvg=P?n16AvEh
+YO%u41MraG^g^<FkM@q$Hl6sSaX{RjpA|g>c!VP;Bj0ggCi1D1Q!S$Et7*H-cFL(gne9OcW}ju2T)w*
+MTb$mL$Rvt<-$jT3|6_rfB+2QA-t7`h{6Dp4Mo5$>k>`zM2G2V>g(OE&h5!W2ASb0pWf1lCwR^8TURc
+O36P<yIMGO5(K-~bG#F)TQ|nBA8TG(?+Ma4j-|M(OHsJRe@kMEmx2*KK7$+W?pea9Jis|f|uSA9EM=~
+xknlwnv>ud(72RMShrFlCqiiq3r)Angg8UpuqEZZ^CO<c0+W%Ztz!|~k~`5Yk^KkC^r%|56N&TIi8So
+$!s1`+=yEBw4<^aieC{TA$hB*VKfxy2QV8YmZZ>I9FMIS2{pYc)oO2&=Fq&QG=%fbdEuI8pm!24$9;l
+J;qY#~V?zJ#IEt!gh=n)(x&-T+_WmSt@yAuVCiIadA|AL^>mi(m|Myj>g;(iK(*EEN1_V|G@+W)2_)8
+5e^LKm<$8E^W%{zrb)&$n$M9)QtLp_D|bn$6G9U@xjvdCG*KrLFX(Vbz~W)@Uf_^(Ai-mrWSMI~?ybJ
+jWWXsYh*echhcT-|P{>KfIFyl(%u^|gAxh=y4YRWh*}4WFA`rKaw0ccaqshFiQA)}hJ92V_8yNqHc-R
+dDVRD^u7*OenmyZ)IEse&4G)Sv%w=E)6$ww?ef#oZhZ<7)yFp-`qi6mmt+H1+ts<(X{8rwH!x7kbEjG
+{P2>nYG!-oYPiK5~svy|yGJ!#n0AfZN-FC(z8(_Q~$^P3zokj(!$HW&$PLtJEDvpXM<5n2r_Nb<TACZ
+lh#lrYKei&@NQ`RZIZmos7p+AHt=FC6g7UH?yQd>ItbH%D2m?loSe(&p~txq0u_48{VVX;TZJRj8*A^
+5`7dpsmG|#076d9nE1-_>Gu8lC{$4GC7H_iWa#kMF$SuwGjnBJM6v^_#FIU3=zGXi4$1VAkWYp*Jo3>
+x3KBS9R;q_Z5lX6uC`wIr_0y#0EG82b9BC2s-gWa?OZ%+zJ*<yY-_-Zy5H~zZd{#wEYAMXtYn4T+pt^
+Z=EwWNn82}rNlP^c1LmNI!xs<G;-5{?;f;^=j{9ZwiRy>qW)oOS}3j@qNw^Daf5}g)?YUt3lXXzNhv?
+Ai>>(^AxQAnGEO6Kcv2+C7mp5$k-QIC2|k*$iFbyLu3?jt=L_c%b{<R|P0kF=p3L-%%`zyp4C3<4>n)
+-8{9MzdVObX-MqsutqKGP|8qt#gdkx1;DBPcA_|aUgte@-hS_+m-x^gEw!*mIp^pSOeU~O;)yp+4!mA
+9C@?pF?!2=(If)Qb{d|Y(u0$mFVs}9MMqi>mC<kl0*H)$a3ZpP?W~qjkMQ4cR{u+;@Yjo&x`XbF@@}$
+=3Y>J2QU#O0dgy_2NXkVQ%qq`Zlrl77NLwyf_*nYrmGVvIvPY3gFFZp&`%%j>N*aif0kR+>0KN;m8Q?
+#?PiexJ9;G-_rO~wXJpA2Q0OfwNJ1pY$oA>7@Y*;^g`;z@QZg7{?!WLzSwU)TW_F-?VOSClhL{u0%$T
+f7M(jC&UyeL!c)?)SBWU4a*!lsO{Ggjsm)%FfukYwaeG|p+6Ip7S&r49*919Xx_7SDl0g5A=kN)-Bc6
+fmWkq*wYPIZa}DjEe<&Ev3zBkLvg!vs3eQdg@JP@6@Q+7Sp0ZBpC(%Lu`#nAVOujO`0=M>C(Qo(!z2(
+M}4m^at2w-RF|aHJh*4Nah%Gt9(4#%epGmhl_&sxFE(~+hO4X0=v-2MlX??{{g*{0!p*jIBmP4W(a!Q
+r(;iZ|K5wjXdh#yEcf|LR7>ZbK1xBR}t<84YY^uTZn6$~XKIRapI;b*7l`Tev<FIF?FgDCda#k4?Qas
+SIyY`{cxeDfH!J`74EF4ECDhs#LmaPseFm<jv?O@9)yt(H+(PkEXp1#U5?g;t{HFjP%Ivn3iH6t})L)
+K0W9^Xx1GGL3K#8mvD7rItA+&)MRO(naGZ)>4;EPd6mNS4mg5{{_n3{?iA8!pF0;fuG#3Y@S4#o9QPb
+!Nk(M4$9JjHXwXSelw>+`Lir0M)GMC9<NE-)p^{V=5B(x9&~Qb(kz_&fvD`SmngU&I?f+DUQGVr*ERQ
+$svRjBb+LQH4T~AK*A<j=P5BYH87Yt{18b?=-`&b5at|dGn10Yx}RUWy;USaXVc3y=_J`GY)&RuwMi7
+ok5Hb41U+zQ8h?D?lKy+<%TvB#8_-7vV_GWV0GQAw{7-$KS<IHPp|x(ODn-8#^Ws$_xlo6QbIqYkd#w
+a`im3tiTJ?uweT|JM7>!dtO^Gc;lnGtPN|=gPhsSJ|&&f=|mPCE5+-gH(tAsk8%hTfFb|O&rgp$?<RU
+8xJK-+g{V|=GK+iYF8Z85`G(bc-!j+D+|U=^)&tePa}xZ&!ImvrnVzj8HoWGwe^I<Dj0MuU?hkl3Wn2
+$^Au<4cj`Qyg@f<a0!*Oe%mL(V!bGs#H2-Yj7Ds5EaXxgm5zQHkcW%!#xQB%RdqFXsjZ2DZ_#W#LHG?
+2+QZ>{Yfw5yO%+WYd_t8ulmRxMX~U3foYi~Dx-Pp<S{nAK7(A$4yRH@la#9mxTi0%*D0oszFt{`=^Fb
+CRdF(Hmae=s4W+Zs5w3gMVOp5Vodh(8EV_+E8N1%_Y#_hmt*qC~+cPR!A!A9;7Q9HyG&(~by*f#Hnj+
+EcDv6KdJi&#fPdoq_C!YtN@un|*d$R-999sEL|5n@&R-%VaV}h+%?!<9_4G%*$px>SDg(-acXOXy@yL
+PQM!i~j1+F_h-Z(s*lXdC&Cpg-5p#C7#43PwU>bjq#Rvj3N4_88EYviM*HQl#q*Y^}u%x%e8CM)@b2l
+-A7KZ%8wyKhbKM(uzna#t;()THZmF`=D+>rCQe&aeti^E3~c9R+3w`u%(qfF0it*p}Na&Y3D?{4D2wQ
+s-rxMBnu;*(54{wO$`e}wG)aIH__$^MX7U|?z;lrPUsqyV^l{0XV)7FkQz;rmz4txBR<q7XrUmXdmgC
+M_Y}@TyQs3?x$2a&6_5$0t~*W>so<%wbD8ivIB;+f&bf+cJXBQ2vf?@7d@fLp97|hIDem&BbIvvjJMt
+o5nCzK+t&8JP>z>B3qtE`fW=Z0)?MUyh_H{DWI`&w-1!)wq7x7h26Jk16QRF=-x)bpIRi`Q4U)9TukQ
+7ilPz;CpCB4mDrbAvP1t%?~?;$Xv52ag>&BNiovCT(jKC`H7C;^&%^N4@x2wytz2(^nrUILsC`ZfadZ
+NU5<<zm`yvbVj&=V%VW%7{9TJPm0^-MxMDe6G#r<_vyO&o|l`*WpGdn1lD^${$_mS+10S`zjC;N9Ex*
+*%N>}fHGax+HS);NX$<_KH8R}o3l;3wgWpG54kvCs>j#+BNyU;HN;+#A7u?utId+daarLafa%HfzLlh
+MgR*udYp<dW%Flsij<Q>#X<uj!4JKSJB27{^)w)8TQ0cuzOU`$^!U53jJkpK^GE8PI=W&wxYP<_x7nQ
+A4t)+wS;B@zUa=Im}%31nTCDYGRA3{mS0w1R8bk70IqPK>pGz%HSZ|*6J<&^5ru0}98)2G>tkm^tRt>
+gBSK5f`7id|-Nl@5O5&)sf6%!pFB!x^)0a6gdu8ge#3C>=Wz3>+rUd{qCWX|WyFJX?RLjz%XY^nYm89
+a6;UfM1aXYtY@-vPl>=Ly00x45l)6M^DHo(;**@<rqHjEWNrqmf5|9oOb)HY%t2G*D0CqUvlNKf9?kZ
+Ok6rU7j00F<k3YfTC{4AQL7)2J~Qn1mPSJC2ar1<K{oMl9@*T9lH!mQMy4AaKkU4J$QCRU3mJ@AMt$Q
+IIMN<BW-}mWp7EDD6Q%SQO8$77eC9`xQz9w9k$-gx`RVS*j%N}JQL>54<1bDE-tN~Y^<Awdzy9YR_D=
+U0ovYMkNqA(36hb_2Cd%66*ku3AILAtDVc%y{5&ueSQ#h0w0klmqvojGMQ2#e!#6F|z6H{UboDSFfwC
+9_wYC6Z4b~KsJ#fXX+^t6*EQ0Ypd7^;p?jI#q{Zf#UvbiBB+-OTJu>~@`+y4t2a>iw;p#$4%n9`19%%
+FF`!gJk4&BZD+h)hrbYp*X3(r*+dlgD_(bLGuBzEAb2l*Gp2(tV>R)NZ&0d(bFI+j#5QoTQttMCEs-2
+`sCR&&{$yDbhq-PZmyq!j4T?orawbD7=u~LLrzpUQPJ51MEyX#d7o9#P$aMTJ|RQ9qlHW~t-CMN`cOe
+b1WsN2`N*Iltp@Q~)K~>s7fZ`+hwyttg0}K|$xVO8E_lk+Zka|195r!u_4Yr$JI0$CU^q%jqQed)7e(
+_KQpN$AOuEsLgPuVl#9(;96sA@LzJVvKu;7HuD@ag$71YZ1)*FHpi8&&RBP`r7TiQ6O#>t(x7+RIeuk
+v1V{=Uk}+xLjHeGJ0n?Yr|XhO{<%_k$lxY%T9chcW4k3ib$P(q;R?nYf2Bv^t^saufIS6k0(hfn~<(k
+72v&r7ifY=P-FKbPy|AerIBddCPGizNh%F>9WsX_l?b=VY-+#F*>?q6g3oKMURLX(s3smTTv}YWx)#0
+GAI+DovEpv5)?00CqA10*|*NB?4Kw|NPlAmh%US5G|dj2BZPf)qPSm2spKeX++2=(46JgonTtgXn%61
+M`0|TqXTe9AUpTMSuP}7dXJz7~DAuTPQ-0UmaC+%mZ8ShHUDOYiI{YOdCkc!`$h?=d^PDoT?{{|{0*a
+p_#nXt#vv<XzsJkKe8{KP4gEg<OtD;p2WUV%MNCW*Hx>>T3=>YfZ36*uT8Ir%GMQnJsw4ba`cZI0r29
+b*v6_O^r*~4Dm^>h=-R~g7S9nND6+jHJB$?b_GMIAjo=Y5ODX!oKoOyw8b3TX0JHj+^y1n*>58_s`e2
+f2&%a``28yz$96jDjz@4DE}2LDt7{m_1lbG}BmM#Z(=Whc%Anvd4EzHWy>kJ$_p<i1X!}ee#j@&YjXG
+v`?M9>oV}vh&bfM<7=ZPd5Mkl?^w`8q$r^?vXWw9x(MM^!;ggNMliZ>S8UpdLsHx7Aep<<6J5WD$+s_
+me02`Oa<wSc<(jQZc=Oj{t&UTWljL`KQKHxpfWd;$8~BIgbSL^R(e!u}$}nfBbwMW0mv7?c9|X-#K!|
+mXre|ilbTS7Z>p!Fs*N>qnYx(aU^A}xe_(;%w9o;dR`1DVxmWu2_9!E#HYyEi*IR{1L91~f0d`;v>F1
+{TE|633_J_AkmEb-puy<=rZ(#-YP_oz=~JQS7G<28B{dDZ%O&u#y@Rt7##Lm{6~#!+0)L1iGwZcODv7
+aOI8Sz7RpnE8lC@edj?|Gz${|MPrM<NnXV@w_IAW11X~H(8ud`eE7u2Jx_haWaMfJ<1PNHl@s!gFd79
+4CyEP`;K}<^*I009?!f_6&)3smft#5%IE?+0;L?aW4c;=9YZimN4~%2Zb4q!DJ*{}7idwThf5?nj{*W
+k7S)xkOkbI7U^KZA4?4A$J0RoD8}&EAW4<o$xCH!3<qtmXveSNV)_Q)-QjF+BcKE_${MT$_O^>hbKE@
+h+))qoAQ6-5reHbF+L|=Z0CzEGS_~u5we1{Ai^6HbB=<Yf3r<3ia2U1SilPr%;CkJS}Ru7XIxC1J*&s
+4{9zv$#?C;311B*c)7t{)wDrJi1XsS!aZME5AmE%_Fxrrun$cft~MpndQlMdN|4NFPJZXvdO!z*Z<%-
+6*hjI5iwH=2U2OB(XEBzwbPEtepl1-3fmkht`L^Mvk@)eIIw&Yq9y>GqAnRdX2k`!T`A&9;sjCt#WmR
+n;)jFq#o<kkg~f2G}kqG)uOj0^_=rpz(7jZNn?_{(R4THiUWGay_Af|Hr0oI=Qd<LR&r!9!;}|)MYh2
+G&b>bgi*Ij>wLHVq?>-UR-odgTVOuB9;mNZemti>sHxs%2>=M%uKu-YTC)z5AYwmeYZ`IMXGRRfs!G3
+R<;h+}-0sH@}T>~P@=s%=mKqc;p5l`7c;56>ja8S$Jy>+q3_{^1x`fj}MntXi|zg6*_av*eX(@U<gik
+2FjOzV<gQtD<?fnA~C^G&hl$QYA^Ek{i_U;LcUvFH@H9pFl&Mz8WUvy<QoYa@7r>@KIX%x9uRxm_lmW
+Ah{wpzkuranVY*DQ$jGYNJ?OlO9T7lq1E}75xODbeUpswTiwDdQ4Gk8{qhuJXm>Cj3&uh+16v?FLZmg
+ST2isL09WEIYP?IvEJtdyx6+EySjS7y~K<cW(1g>XxqA>^yRl`&nvh$S62;!z>Hr|`tsoe=72t{cxbc
+<c(8NCRvn-izRc+I#=MdS4KB(v3R}7?!C8~5t&;`KITgXFmRD{A>bBn0UtP(HI7E|zPxeQL3}{*yq66
+o=jB4ED^aIZ%aP}>1*kitT(j#3(`xaxdygK~Mu`bPe5jUJn%S=Y(vq?;6JCdOJEpq?6{mPZN&SWt7kz
+M3S`S8$EwS^c?nr;<6(H^e&7VqV2znCDiqWll>Jl}&H8c2NzVuUMQ+2a{P@}=GJ53aHopnRzCp*;h!g
+3f&4`jPYSFiy6T)kKoy<6Pvu>+R@)rp0}?J@OJ8X?RD}%26<R4<9pQNgc^H{?XIK9ohb)bc7If0_+5e
+sLLZbP3Yc)_3%M!Olp2D`RFiNfo@cwSrq4%`C87I+UYjn86Jr>nBLkpK0GK_-;g%9p2KsjgzAxf#SoI
+CB-;pQ7~5>HFNPIqRA<FJvML<uz({u0b2-X?A0~35Zm2WP8<I4l2yt(deA;T<kjF<qdZXOjjs|qAT48
+|>6tmgT*$Gy%W;Sw&LZ=vXP(m5n`4ofx3{No#WQAvf1|*O-&{=u<xl15#n}WQZ_(o~af@$lp5mj$GOh
+yCzkv-u|f2pT#{Fp72=_L^1=H5D36y_FFs{t13osy_;G4P?A%_TfUnz}M9lSrU7?o{9bYWEU+l_p1wu
+H^TW!hlBV)&f*%%*bV|kgkv*0el`*q#K1*eHEGdQ<X;hB_2AuWEHAietz;HypTXgl5qu#4!U}}s1;qo
+|4@xY%!0aE(OJqVYSk`5$OKw>^5V^VHTRl9kOm-S3@uxc@#Sb~vkJ721dWlQq~)v4jlG19dC-#PNM#x
+l$#gl3bXv&l5|ZF!@*K;W(Dd59VTPE)@&rxX*uHd*f_`A{uda^E;#Qy;zRBJ8xneLD<Sl3&>0vH*QA@
+JGM+E>rSrm*;M{@q#Pt_AQ8i2_bmQdTXo+bco6-!uxIgYX32=&*`NaInq1a~a4<)go<bwNuYu%7tq9o
+vhZSYgY0;l>xOjh*xf9@!rMF`<7iS@OcaKm3FtI)j^t%^=M!ZsGQ>o_^~?v^PIhJ{LBPjGP0(D7I-Ha
+BA$AqvVf%w3cpL@nn~%{uZzcU4hYjnml=7Rd~{0es(>t%bH^^7gQ{mX5yzsl?;=OgDymp#!<w_$BKjP
+>DPZcpS;%=k6T0UuZub6cY!56bbk0&G-zqXlR#4|!CLB&TPjYff|ZMy%nM_AIFA`11YaeEHH{K$1I9a
+q$N@8?E$vHa^SrS(O0)v7yok53Coq-kqvBXN*%SC{o^RNO%y++q;O0fcmw34<J>BY<Nc7FO^Kw_>YFq
+D0UgfvwW6<7^_?*n^^;V%=b2B$@a~|r?6res%?jFSm3C`s$C04k!;uQg-ux;Tk<OflqUGJVv35Y-#(W
+<@^IyFeMaicOijKroAfeWVHVeCeh+x;9tcY1l{U7RE7#uQt=jhk~9zyZ|A{e6LM%~Wt=et&=A8<k73q
+(#p&krib0=`fmM#I){FfqFt8ZoKH3Fr<^6xRZ{gKuomlMPJ@O!}wUUe3{{8?jz<~W4!01LJ+V%lwPI>
+@A>3A8o?`&ZuoeKDVh%+`!}9T$1i!I1axyLCiTZ{K6->F<vm@h=g4ksQxv8i%0{EzE?KZr-$m}|m5eV
+g_PY}PN6I6vknIV~o_O6a*dFj@>9=A^{iTYnal?};SZWN~uPA|0#Eh*ek(J(<HQPG*qjqe^k5E`O+A$
+RLGl@UKP!dg8`B<gcWGm;s$v|`~tld2b@UHMvbP&A~-MR#wQF$61D~w#@D>8Airl2vGl5Eh8?uaUKqS
+4NML<iE4uTEK{W7up{lnYA<q4V|v{sXk3uYk9s89lMZj&I;iJ}|kmgzFIdTGj5kJDeA}38~xf?r@jUk
+R`X)oku=1mj6%3ZVM7a4lq^EHbHPD%k<!(SRY|kuovTU(nlffF5FqZw=4p~cBuVtlmExaBHwrOGQLA$
+L7LT|`a;}IL-$k8HvNko72{4u)K9AS#`)m(`o~_P&e7HsXxSa}MRrRia8dZGe`*vV0qy59rz~c=s-mc
+;O>!*LR`XS+Rk__AVigPx9~n^!-=x7Inu4$wT`a{1#GjZu>oGPJvEs*~x|NilYwR?<;FN5Xz(=iyP;m
+e<(9|r8HFlLtgb@*}@ac8b*Ily)ME_<%u&Dh!mS+5>7zfc}cu^mg>IY)j7@bt`#;h<hj#Git0UaCNTP
+zgir2HuBhn+K>Qub=@m+p<++^L-0&?z!PnRbXLw`ym%jG-jD%r}7EI{~WIwW!F|sBcr|9iEKY?U6PkN
+dd_+BT<PXXUpGfyEi3Iyt8Et+g;5B`NpXB`SwfH;qHaZ5y{s_$lXfqxH5x^l2m=>+Y>_&$}q?|RA`Rs
+&dwNuRLX)l>WKQTc6(p`(qbq2qOf(B5}9i(huG`tMU7$w5le7-N7WVTQ9z`i{q}}<Bybk=1Lrnbi9={
+stUUn6H<NX3JM2Jrv?<HoRNBp);t*rK`4CvusI;0M5%J2Qww0U<g$;VBN1u&wbW&rax#lB7u^y8XLC0
+5xpg;gU;pCBc9F6{MwRDWoH(ji)6(l#s#ou88siyE){eRcbCt5!!+n~0^<RKBfW9h_BRzBh4u_Xi)9O
+CO|E&V#;T?$tam6sC)P={9#7S;$-L7}!HNC!oplzen_3zF4!>p3=^p`RVhbV0!<m}9bDu#ZlD%U9SF?
+Jza`j3HS$3nZv)NZ=C(V!K(b&{yR&;RXq9seDV3qj-_uO<k(=a_SRfaJ8`G%5<;gH=P6o{!y>2>>ZY5
+2_H;P)L$5w1I@=;x6x_qjoAut{r>?_O9KQH000080P~d=N8$#~TnQBb0Ps-&02}}S0B~t=FJE?LZe(w
+AFJx(RbZlv2FKKRMWq2-ddF?#?bK5qSzx%JiC6^)9%1oTzcJ{RAo5{NF)-!49vEAL<$z~*qge24y$pb
+*k+ME5~_q_*z1ixf8$=q~1<z!-t1RfsV_j>^NoSlAg%90`#`Fh4Gy*m9pJ~?@Q@|;~1<z9;QMzio@!d
+|~R`-=Tp<nf-}#y^QNf);N@!gIw_rVFMwoL%qrrpVcCvC_L(a`tUe<!P)1JcAo9SqwwvT8d?*3;4B`o
+Nsxq5!Nl|?CsUX<-6O<NIz;8=PA4R;oaTM)gOPn`{CvmA40nmu`LU!*(O$-Of22ctXQvM3hrm2+@0D~
+T4eRFYFWx6;i`VG`L<k%jGt(^pPewcR<hW_Y$T77D9pCIqX#GaG2x|VSM=~w%0hab(rUZi&&Rr+P@Bj
+-{YEGa6SyRRKq31&DYj*-U8C^3ySwW@#!4h_D!us*Xp(U`WjFkPE3WjE;p^*Isa+w{@7Holxb&_{W@R
+iDpR#2ovUFaQJV!7JIFYaCdS7xivEw7P+~`T1Y<M~cij`dI{gnM(l=D<T&s#1ZxV)Cd<No9XX%D<TcT
+9-ZT)(Af;e4LQTRxvpPENkL{PxX{Z|~+;@4mVGA7~tGv@X@`<;!wkil`*Xi3+)<-$C8{=JL(k@3Fage
+3E1^U3MKO_wkxvflxh$#quYf=!s#(vuDrf@7+cyW*SRgO0Gc66v$s`dNP4snW45=W}NMW-hcpKUrjwx
+CQx=TMx#abD0H(V3731oa2cG|*WKny&EcQ^jswAn(Q_Jc4jta5@y%c7{~j0!DPOTUycBvq4;9Z=Q{>n
+AyCUayO8&I)x`MVM&V=HiBrp-B<2>QfN!?%u+>W;hW(IWN?8lq8tXR>s2#JVYtyo@wI+Q#SE5TDUXKc
+3uy+kT7p$3Ji0(FlD(L&rK7!yc={{x&B%iHZxBj#WWR$^UAvJl~<X#rxl1!>K>&WdCTJkuNeA10-y3`
+AdoEUsj{HbaqYhi>==j9A$|hzs??8|nvt%^*Yuk+YD*F_>Dh3_Q}~C1a#^S7pOM<PX4z6h*910&LoT#
+$Kp^y<mNG=t*gnb-ppW3^PDZw;L&y%|X48Gs6wm<$CP-d{e-zhF;1tK~;X-ci)oaqfH146{Y+kud)mX
+u?r?FR!oWfzD*1i5oL%51HMM0!QU>)@TzTU_mT-9(HP?hc#+)ClQ_%5_rPM%B$&Sf5(gj9Or=Go&k>O
+k12=v_UoIu}oecH`Awgu$*{fkp2}ncpVWq%p2_k*pdu#p054WDL=w2ujo-F|DIY(nB6Ht)t7UYCvd&C
+(0iEGkbv@&SB6|<d)O(NG^#)(F6R2w+f(mT~?^GtGG$$UltEtp|lH}1|22~G=~z311+!3%3X2iqe8t_
+R|Db8%tDtGlE?Z6B6r9}Jr9eqlOkXgFY94v_E$T3eeB#EBD>$Tp2g52_Neg<xm7Z>BP3z%C?j4UK?6J
+d~P{C{8LJXAE@yJ~B?JEXpd2HCJr00Bb7ILoOCm_~ncP{Hnn%7BHD?yI4>_1U+Pi_u9|}fY@+_w=o`O
++jGR$6O{%{#IJ5f%84>RB#R9jlBh9zt_PskykDWlB+;DMVZekPd*}#&4ObL7gn@e&jMz@Ut$WsOOGNV
+<(gQSA6i0NiAg|@Mb<=_ljUlhaOT`cTc=5W0)`!I_Vt*|vwyhL~wa{1rlYH4=zFzoZ;>*F3uTTrohN8
+A>6tyjfASU%9gE#9~<Wo-qpiyN5_*!6f<pg%z&s&2nc?xkA1S860?B|xUxdK*jCqON+nYJav<53^M11
+!40#(-hmr+b^;#GWPRIr3;q?@eeBH@1$u@Bn=agcrk9gm-NP08XG+rqHA%?e@iS0Z9!GQL90aXe~*wJ
+kc*vK{^?PNBDUYfX(ZFha;|X8^tN8x`h`us@T9f%)ge94FpR=`A}GHTeg1<RDq`^mIf7wAq9;`ZNF&^
+nL+9~8&>2t7pRocCe9#Mc@Qb+iUZk?xZRr>d&6dwe9RW#+h>b9Qb#l)-UqNCZyAq)H!(wMj0ZQ!O&bx
+hyxJ}SVRY}#@z-dBsx1$IakTM6_}b}Xrhw#yR;q-!fWu9~#RJTkd<>bd3f^GgwiSwyF=<6_L}?P#k2D
+}Pfho8KSLwY&>tDauMJf{QzkMIfFx<hw5tBxWqW6IpIS^jX^6)$<Qa-m&!!#0_Zvm4jd;h@?H!MADFG
+wZRx{v)kjsg)&e=-Ub@f;Ywi67D1*5YrKOv6LaK?tLl)tC`9WeO&%Jh$TI>KNcqz`+2kNe*H!aK`|%f
+F}TT#bsY1aCjqGS+N%2o9Ky4Mh#jlBoBlG+5!S-U$&n_U0|lU0cX3y;Dj_HX2YwU$TFtESD+`{W5zGa
+QI!BUobyCC6)d1oS!o9z@bqWf!^qInP7pK;p~NL*QYM}x)C}=-F{}%_jmS8=lU2vQE0hmHf>LD!61Ec
+<VN>{iPSMXq2pXhzC~Dv%u*w%qB8i5h?0_{>ip(rR)qZIzJpgD~t;67ja>;IJ#cbjkaD#l36$&x0#S?
+wK66?H`+dwuCWUxTsB^9Tzk@Elt^1ge%r}*Gd#pK4Q5Assz7^^*gl7u6XH6|?jLhNuee8yq~39*$Jd~
+58kK|SruEzQXE7G&-yv3E*9Z{070!2p1o6b=ie%8iV{Dmh1B00rY2s<}!j9>Am`-B_)7mIS?#9dVnfl
+Zb=U0pCDzKL~KLp{g!KmWmL;PUA?>m?CGUz>(Mhc(5Qf4#c_A0Fx+J_3-22I<Uo-pg<%3&avK!LyRRt
+(&SoFMqDZ~z?co^=si1ejn$OJu*N4_bYbsZO@WFXFd1E`(^ZNI3KdjNPxInb>wWOSj~E?0hGW9|?RmQ
+zGz6jD6L5<7qehK?4?|-o;4t9fnty0j)mrk1xFGBL_;jEyW1Vcw+#2b%bAA<gBWNU$tIJ2*`xNi}#Bl
+}h3l*U_Mn4rI4>ib(d$0tksGp%z+X?%U1yQULFZAu8mO?<0Y))}r2EkZv3u$g4XpOT|3|NC<E949ov^
+CxXlSa>^c{6L<Pmw<q_q6T;RuuXJmNXkcZqjr8@DRazac<SMq0~g=fWaK2qk{&q%4sdeNWGvR;E7V^{
+Q8sxoE9-~DQ0irGKAYGY2Q==3P!OUXmrq3-m@zWmNQiSmfB<9YV#EcEcTk)bqZ%>{9?<%MJQT(47S#p
+>DKfHtic<mtCAc;u0a1O3b6*d*6RRrJ0J&o(W$c&#j1~tart#%(u1;Cn35(|eaPy3A}B89V#M0q?_#+
+|_y2z_*nvSHnEBDo_0{#|wAT!PA8Pgw-!L?69k&VwqxT3}m=L)G+Y2EOM>Lin{AYkr`&rq6L8WX`hA+
+M_qxrRozd*_avAIU~Kw?_u=!GCiVH?PdKyH#4U+p;C#(PLx$;x+%8%f5&f0>eMug2<CPaB(d21uF+JJ
+f&@*XO48VWM@A13>dUbbJBzi_EPne|r`|pTWCW`gxCjeKdIFrv(qUQ%g}F{Ocb0BUwv8MIP-WBr##rV
+=~yP6&jisyI8blb!H4=3%AY_YaYwU+!)>TNC$KC3U}zZ&@LYtbsiO5-a?n(V#oj2*wGC+vX0qAhvT3=
+DQt|Fs>rZ;%}|d8FfFtrj;s*bQNY$mg{U*Kz+&9JSd+BY0bMtD`uMDA@NyjUv@7W<lf%UiKM>iUIaCW
+zTLh^y)0W|p3H!I+>fBiQ6TvpQT@2(&jZNmb{KkS~unZ6vo7l>vUxTqsX{-vw4Mm*#cEuH_`WA)^Nrz
+45*&u4z9k*wk2MX(aXt?*b4hWBmN`jCdXQh55BtiWo$C6`XdzxJVzld_CXA%uv2aMu~QT2832?+nfX4
+Q#=85HkY6JK)VL_Kj+6BFuSI^AD8HkKJ8s<cWl{=t<I1V3dw@=)qt5RD=%u&6MB#bSv+=3owa=EtXYB
+DEx&nK*fX$=GdV1_@J0GcpT*apCj;Idu`SZ((rK;qLAp%wr5}n`i8bDoYPQ0}+BLk(yzwUH~p~hn5Gj
+0P2e^kRs6RtLXK>B!9(D@RzYxV9TH``-cQgY1QQZ$GHuJu)>{OuAo`>ue*QznuO=YTib4IA%%B#g}Az
+|t>E;_yS9h`i4C$Jk*~{Gps;I{y7&QHA62hT4KNBWgLWLwGsXMRyD>)-GJnQkqaSEAdytx80=YC>kXD
+`jN6^A#BInHPB=gsf(%;Qfu{J9#c9UoX55v0GB--#t)5Pb}CVu_9ulrri^QU^JQB{&qbkc0M5;S80K`
+?f?Q%DMcZ8|Av&zG{T&$?}#*@u_zwhLw7^snzbZ5rx8qvl69>-TAa$-n>GOZ}VqBT86hcpnC6$v{nmL
+lwDc!^6pkjzW;IwHe+BhkHLR9K=g#H6|Z5wr+`oHnGj?NPP%zH|peojJq)>?V-%}<4A6`<UDkc!TPrW
+`%&hR%Y!tDk6IzL@oHKm6>h$6Hn8}CE4;<6zK5g97KmS9FxkWJ(Bk-LuxLp!9Bm$Q<l_OCkC%<n)W!u
+AZLk=7jRI<g4U2^#{YGzbkJum){*`>WfM8OFeL<vw4tR1ex&nVL(VsgYjls&hYY4UpSeAy0!IN=D{t*
+!dVFW#oiY<&Qbn@-R8RkXP)wHPl(N=w91MRN;nvz&YfRQSH7>FCQ)LL-ow15=Y#rmv4;|CMNje2IE0T
+`t)a6YKU*)#BiUc$`7q3g9iYzYm*pJE2Fnqu~cbk<R7UodZC5yY+ec_`HR^z~?i)S(sWK86SCi_Nmh;
+WgFfs=@b7p-+a-HKvJ9nb}g+8SvM>0NwEVw<YMKm!Las?Iy1y6!)_&#Wq&NT9A6^&tOB2u^@k@VbOxr
+C1x}ozZr~QwZV`TM-6H(E$qNz*waFQ1ZMT@TZpUpu<jo$FN{Sm{>)jGH{}JlSc->Zjw`Nvz1#aS{6&t
+gTmB@6>|LL*z|mckaOcKeP1)IzF`${%$1hJju`pqeF*T+sEp~Z#WulT9&<+A2lSP}9-Y!buu?ML)MR9
++~^0<OB9p!!Cx!{<#nUe-&gWRx90Au%!=6Jxck;-?4+~O8ZSBu4co3!Vn=~AbY*ruq~pthBi5I1J~CO
+|ICUVp@C3;CPPd*D|nA|5$-2@JpFHF7q&siDOAo|f$<p6x^fV&?_We!@K|nYcIk*=$wi$qY~90QIHd4
+;&IMcx3kd=Pg@+il4?3w}LKZjAs|^sW2+5<Nlf~D|X5<gR0imV9J`}`GB;d%`k2vl>QO{jO~o=aj<5d
+s3}dVft#>+N~LiASem`Blnfkqe$-oB@rDQ?`hXlWJ&<BIHse9WC<~Nw{^|10#rggHzD`U#ZlhUV^_hu
+y3H4Ntk0*Bq5_}^36FZSYtO#P&lc+U(m=G@7MKrZYnl5(v1=zeUBxw8A*!l3#Qp#e1wYp%jnAk%oG%E
+ND3#-e!&WsvWw)-6ENQffc7!uu%#12ueBgCWL#Y*vDcxc}B54Gae4#?gW4p7Yz&t3FO*SJGPIUgI^N+
+k<1j-DExOa{)UI+oT|UvqOrncQ;roJy*}*d)Q1p??o;F?njcO*-|+_SC=W%-gL=7`XmcCgkj@K;vN&h
+?G<S)JcF;*u8Han<KC5U%1=ZglDsawl+B4GgTNXvNJZ*SR>vNkLQkHzEJ?_f-Y-spL<ibZ5&Rpvt`?K
+Rko@6oW&{lj8%b$@$42O^SKBJ^}%lCF@9tKY~t(avC|Py-f?p_3ug?@SIhm&mB`V(w9B!pa_|&~{h&g
+Kr<vNk89c(gp<Pw9tA15Rh;-OD2+OQU?v?mECPTkJ`_I<{WomkNdNxo-i!mO0H4HPawu3K02cFrw$|<
+matvAn5u`RO0XOAroBG;j9_x{<1h4I$F_|TskhCq#l83f$C4;|9eh9pxOxDR;jI7Dmymh4p6uOW_}?n
+XJHR^#WphmUTXSpXxxJZC^oGt}@fAk22t+kr;4ZS1c2wIJCRZawJ$yjm6jzrG0B5uaj{ndHx9l~t{Ql
+@}mHZ(rYY8=yI}vMx-FvfC6Hx7!K;Rby~`S3<)1-yrJdpe6F+zv$sn@pccnz#76B>`RVzE#n%33q%JG
+9I+QEo_;~2_9CT?$EqKYzhEKaVTs}!V>1IXLNRj=2n;Jl5EOPr{@jK|!5+RnZ{%QTwXp>PcSo6SeRhm
+xA}{J-b1M|#Mv(y?=&1vXqtZDd>kB*u4@9(QfoiAfjQ+8Jx|buOncd_FpXt(ITgGu7v(AWGHs2&f@M~
+;eC#S91-N2_VwppLCK%Zldc9W1&E+%wSPjJgB{Qw)rHsP5obps?s@u)?bW^E=MKoZ;D*rt==Sj`@)Rq
+f5x?p$*B>}^}PT;KYf*ie^A52*iPlOq87%^P6m>qYp%9q(gC;TH}<FE2I?A__#1+l@GMmK5BcV1Hp@S
+TruFqgyngWS*8bP>IUIoF2fclg)AzeJyEdb1?Qd%(bwr1b9_oFh^#w!>$B9oqhDrDH3*WoQxka%u|&W
+TE8b7IWdEdeQ@GU8kgEFC7O>f0d=Of3Ws?I@(62KvDeZAfZyYpy?luTk8ok?fn%wf<A<_dN(hllu3nz
+~$JbxI`r6w`9!uSkYwtr%BwOxx-2i!aZ|O^4x3S#mT3ssqG^vJZ|8%Nh;Q~(P?!!r|39b83k&)i^`_q
+(M)w}Q8Xo|t<X~4d$r#j5LFmUl5_yzjT1#Q6t1h%JGD)<Af$ellXp`L*`A7=mkq`PHzc&ueGa%SqGx8
+--zW(42+duNF#h9_B<I8PAfX`=iLg8ZSzX`DV`3-`$plp3Ux(ep`1rt5ijr-irpXo}GMZp$JyA1C{p{
+9A5UH3*bfeAU!oTV-0{tC!x?9sF~Du*QYdc~EEHlcT8Jv6OUo7+ti|6x?D2SuzFLfUYMC&FLa*%x^qf
+9Z@k8drTcBEngqYWMhpwL{j+aF#L{PtP9z#Z?>xkwsGt0cx{=Z7C39O{5>-dT1TNepWGTOhx9(n<$YG
+qo1qq4jvv7KDta}%&|QOw?4&!>#of38c&T7=@=s7p0|XQR000O8^OY7ypI>kZFERiCtHb~R9{>OVaA|
+NaUv_0~WN&gWWNCABY-wUIY;R*>bZ>HVE^vA6eQTH7Hj?P~{uNC3cu6H{+U`7dMw52d<2W1NlZV%KW^
++%wqozbk&1s2LNXl(xod1671rPv1O6_Fs=ALtT61PMFg#u8h7Yc=w;Pk`OU|yzqv77|kW^wvwd~<Yib
+P_x%H@hldUN=GXWE7k|czAXS|9l$!Jui}7@G|*FzKNmOH~Bm(>MRYKGH9-|;Q6k(E{ou0xoB>aDhs|U
+w?&#XIlO}pSrsJEUeQ$fY}=IZZCPd6Ix8B4^)kzXZ=OAQ`rXT?ar3qbk|GVBeE;367tj9o!>jLKyu_D
+K?&v6AZ_26(mcQhiqeWG&gS-sn-QRXiRzLe*75PV97V7h=TrQ!b>T6l6Pn)vN-)@rTTD_>Uqo&$Tjsk
+e(RdZcs369fI_I93an&26|e_B;#Wfe@f>-BD`iw8+<)Ri*7tn%3rV;-w|U*>fKub&ch7%usFUakQ%RV
+dn4tACVvF%ICvCaLO-K5dKk^BH!eNY?O8eL92JW5TiX{JhP!Q1a((*<ke|tLMoEKGiTo#d6y0Hd#FmX
+4`y~PRmVJWK|EKuV1}-{vu2BDw{W7!^Ev{s_WoyNu4>bdjP!10GGNM6C@heaqz>7Z|KuV1_KBwnE^y|
+%!ra;yvEkI=g{?Wuug8WX$r5U{yJ+CgrVwxE8pd3sJYT3Jx}J>SvuWR(Cw<(;b_7TPv=#Z0=eeN3dRK
+}e_LhKdX?1I-fq>KRo+Y&`6}ZXS!NAF{0EH26dP7hE9g9o5iSHT94>`0rZ9-pH(6CvmtSTTe12Y)Z+B
+{p<Tuq1Y_W!(VCz2sx*L3(rGMvAG`dpp_BzXS_x~HejDx(MG6CkZRd#fQD+uQGTrH&-pnpT}qA6oH9U
+UEg{rKhAPhU*&4-~C3qG1?<DEujUef!bqJi16u7Y|PVa{19H934IR`tggGPhZ(ZtMIR{>ksGG*zX2Do
+x}f8{NV`x{3#q`$!DXZuU~!p%@i5L)9+qQU;XdrPo3tP>}_-Zx>>K@B^#KCIkBPpZ=>sG^u>AwO@I0H
+tH(cl^J@C+yDy*q2$jR@rrAvH-`@}ui_2;WA2)e;bd+Wb8p>p~B7#zMM!l|aS)PAa7TJhd!SV6&5z}<
+Dt%?9D1!kpyDg`%1d0Pat9q_pz2j2KLrb-i->|~8gc@n$=jtYw)C>O+B%~BP?o&=AB6@bPP*bKw$HWp
+iW<qHH4vl}4N6HRgW@;tCcg?^i_a-dB3$)Gpv^EhbobyjYh^N-^PBPz;hNsSm$n{JZ%O|pbh0@|3vQm
+F%nBSao9;f&<tfNO%3xh<=kAgNHUfH0YDmoO|rU9Pq4`8Ym{-vT1548S@)KfxibvaoKe;=mG9Q7dNwa
+*5y(AgLB*@iL}2X91M?VYV%rZSZ}O<yAf(#lbTe|KOXFX)L$~exFrRm4~Db2*UkjHivc9K$H2Z4rbXr
+*&;)0f;!pNgbXj&APl8GOw+7fZh_yx5(R3SH(QwM9o4xmx2rTj2w1aX9vaqC6G;bq(?X3&$jU(Fb-m4
+klZPLF_Lt9in2^kI4NNm+4AYMvoJDoETHu2Fc^oV@V^kJ^ldS9ISUOpcTKWMIj^kDnmDE9$7xS{hNnP
+zmiZa&QRg-U4nLAju0VS%-0NCU#nL#&epx<Cw!h*rB{P5x#jCSw?^mvh1K=@oc*0qc5mdO}NTffDsH`
+xy7I|^6QGBA>GTjinA16dtXR4|)#Sh8+JN?bsN%TB2oi2WP=7Qjli&bn(Cj=2<p4>UEjp+tSe)kh{oR
+V)^wIR_>d+JH5#qB}$|m&mxMi!CrSco~hxfeTu|Unh0aG*u+sgz>7f%_^B^Q-0$P9X?Vvwo67ZLjGp5
+Rh{)hp@9Y5xuPy5wB+F%@uB4F*Fpv#Mes*d*J|h?(t`QoWB0|syD!cZH>8D7R#7=ay`aPRh&ORbq~aB
+qu7gup)*yqCXCw{3&MeOFu)ez7RxlkP6G3gkp8=sLjGLs2m%rdrppUa(&eUi63o15$S7m0uggk#UFa)
+Yy2VS;ZQo-zPMt^UwS5g5K=hZd^oyx7UNf+T*M)kxHXiWmW1DQvX8&v7r>wJD4ke*TpyK)=cCLonSB&
+?HWejSsv1P+z}hY?tBk`-_`0EO#~ivb@=mPuaJ4Y5UPqt$VO+q}7EUb-y`RM{C2b4+6MorZ%F%2(VLE
+D=yx7{@8W1_qa?NmC3R8c&LOh7gjrw_D(fz%(~IV4mY3ndR_LHNPH7zq=hKa7I(Y^O%!^9<VAwyM<2K
+1Ky>Bk8l!jp423u7?@7;0(94@tRUtu=b?<Rx$qT%;qwgYfNq=v$&yyX9c{U@C^rmk68ufXpC;;6S>6z
+tuge<kusP}-BA8)pVW4+?3%AJF3Plx&W>Cp<VU6$;G=)?G{I}j@^L&wKbssbYFyDgkg7%+3eSQk4lxx
+t>fqoUhQ<A$^SK%sKCi7i*b=3zfXR1*ciWUwF0;V1b+gi0qG0%6;pT*t*#@UFcWSqcU{_|J+2PFjrMM
+I>jkp@Boy{FbXlW3OL%sr`b^F6Te2Bu?Oj}2J28TB0KC_rtK?ZTi+Xc+)EDuRD(VfMB)_6^uJ7Km*`r
+Ovr!T1Kv3I2swPK3kaPQ#3*`ItN7v#*_>g=71>75OH4Xl_o(05Kcd=QL3c0CNaLi(5l6mp}?`mq*SFp
+BXOETalJz}jF^BbPvZh&Rgt07X@!%uPGPCdK~t@GPHD9nYs;AKn2JT_5^aq;x1I<{t}evT)~oG$W9D6
+*W;Hd@&s<->Q-*OgHBC$dw2P>kIBpTGk{%xN75asOW&Q?~U8zKtGrR;*fNKQg4ovW7L;jUFphzQ^(`*
+7q-}D8v`jXHB2uA&mxdE%uGjc4fh!TJZaEWYZBzdhWK!_&YC7M)}{yny*`!)x|Y?cwxA;;H%Q~{-;n}
+mpL0z^{10IiAS%WckPf`W!5<Yy68f<?b8n`doj$k30%=VTMLy;Sy(>St(78qS%=)O85WY}hb&5ooW;O
+RmDaR$TvI-itpl<sl)}*7`sgX{iSht=%zsLW!s9KBEDPYylBjB|D&^5(jqUA50KHrr_u?Kxz=|C_AX6
+IdC*&x^T8aQwFWFyfDEIf};`_SnJXN<ZAv0<A6^GFu8wfrTsBHqDXnGiJ}IxG$Yf8Ndj#WKSlf}&;n{
+QXh9`hPt>uN;gh(SUza7&<fh6Npy0zhu)SO5V$mfm<)H9SSYz6KmdS+X01f_m>$i^8%czO*dt2JjY|n
+#N)wY#`c|B#1-?;`EgL+e=0vwrYyU>p+Z%>#7-|`kl<U))T?2$0VR$U#rvjoVCbooGBaWz^H=z)t{k6
+cKrJub2a*hkljQ)rG2)W*7p24movvWeg!d(eAm;R#PMS`w%o-Ig@RsDJ}e%z#M;xZweGk>)AL12)%EA
+3H#`9%!L%oijM0N?%1+p3OWXESJ4aaPhZY7Sk<wQ^x_AX(NlbwA!m?8SHIYByT`*tH~CvC-?7jCrW5M
+5bx($<JZzJ3OZCuxgg91`d{!R+dj&p!agb>DmD1|DsOf{1oYF2Jt{FGp4Om43kN`2B(~Bc4-{*_8FE}
+84{f5XA1V&gE_5vAu#Yousk4p~1i(Fl(p?{giPA&2mi+huBiyabzVtZFKpl@lkaGb$Opu}9tJn^`hc>
+uDmk0PwY_Km52ieCetsM+mD(!}b+RPXbs*jr{TW?sSL0~}Ksy;4d#SR&sS_4QN;-!>VeN@4Eu8n06eu
+l<f767)vIiEJ=R2j=)@-E7j9URm>vJP;?qO&z;u`C=w*uH|U-LZ-8bkwkeUqidb?LuI9bMo-cvJB1FD
+%ogdvj}k41MHM#HnMe67}*@wpQWk*K0HEDpQisN+ttA_cIS8;9Lvxiw>;%o4c#$K!7+@&@n2g7bj*$v
+1FU~qh;nd0$duj>S@$|~e1_&v=;_0+@v6K<`%hl0ukIS7vgq@qQUeRrl6?617tcw{b~9Oyz@)>|5O-<
+9Y3R}`dV5M3cn=^{51I^=I2I3<KcXSWZeAEGA@0YW2Sl)m0E;nVG}7Jf{^ap1TkF~aZ@I~p87I<}c93
+b0GFjKSOPp0;jx4HV*{hzwtl;lz#$xU)D;3#tdG30QZGLsHp6Fo$)s{4S1c%~lLcFWrZELPoMzY0f4f
+q<{l5Cnqb{W8YZR3`#mgN{oVhJ`K09eP%s@!hs$W(=0GIHwR=#K3%E~|Wr<BAF##vB;!(`#IJ_~GL8;
+U)R=?>i2Cqw>WB3b_zEYMfnNM>d#`QUzIQvvo)VyshDhqUGv!UFZrqV}|@Th*hB=cd>8vs>i>Iy8tX!
+M3}}t@z8^|!0B&Ducd$XQztwjwh~SP1A*c4vJ!L^we3G#OR*GPO-sY!da#V4N*hfQMJ_i`lzZpB6NiC
+>;b^o68P@kt+Yz9>1lqpZ+xSHoOHp*KM{h39CYPfTN!~ZeP<T|x&3-w6$FJ5e67{3Qb1=B2U;VmGD%?
+DX!n}Bsta4t^gaO-7!96r>>}5t~wY`36W%YRSM_#6&H7l=m*j?9bSTx#_?`f>t(GIRqxM$_t{0i`+vf
+b@ce6;QFXmlco4q2`RsM1aUVs-Uy*~7ts5b*bf>b$dLKEv$CQfL7T(i`-yt1LLUTU-)D5lrS~3pqzE^
+*smjCVUis+RrPfbh|bW;!9M=;7x~dC93r=<V^V7nLFWg5=vMLhR7j*s!??u(oX`HrMM3+DI}8N3B`ve
+^}^+n=l~-KUI3kF1R)L7H&AQwdEzcWBb%}^69)IF7&gLh_L+`T_8?<Xv8m9!>T|S-mzDG;@v9q#4Cvp
+uHArsPhOHRcp~BrM4JAYj6~cyj^g=>#lHlx=_F03-RMbYKwqiulm=-lV*MleQL0Pq1j)(*9k%4W74CC
+e3kIz2+Y|L(qZ@>I>kLRKjk%BmINJ@R303;N0>-1AsW5Z|^&;vVD;hIAdwSuEOU<*mlu@psDba|UiDS
+flM-Jp%DVi75vQAb-~(ZT8dfhxm%2wKKelTEzHi)5vKByupF!6P<iTbGhioegZ+-<`Mom%X;FZGBy?(
+wb1L)`{hvTduOCXm@wCNTui_vEU2su~IHP(_!bQ1Ch(vWV|Ui5%b%2_?6*f7Ew#vGE;Pjr<xUewh<HU
+3crxTwzyrVw2JFShr|w;Gy+ct(?*VDlh6QT1A265WAs)VOZr3W{TKE@l%#8MI1>>t$%ad3sa=Fztqw0
+Q;CBzlLR&A5B@zO`ePF?xIQW|-1v|riZ2iug30_tTKLAE?oi!3f5w$~@HPL7kCuwS1(=<M^O(re7eO2
+=-W85xK*_G4*P&&XEUEeA?^8Bj==DTk@EN2(36^vmyrg>4RxEd@<dmILix><u8OAv_hBZje>yPpPc2c
+@AbgXm%WU=-pK++@{tvgwW6pognGIuD@MIQTp`!#^M3*N;b`i-s{86ZE59U7@{sbv4F4xbhZ%Ka~P{q
+i?YFro@dQ9{w~AzHgmpcLs$U3I(}_U2mHa|2v^g&&id!^LVAV98o!fKB4Tnx?&wOsJHX$ZoAgZ&ee{%
+>c<^KTSeNw6W1OYpDk`y^=wDd{;zu~f<5I)z*dz-LH8<@i{4gr(1H^%(P39km`3|aqG#nrRK?zQY}-N
+#qZtuI_v%qNRshE+hq6e%jp8<VUP013%EE5UozN<_?H?FJG^$jUQSc}@n*`{DM2<uHo$y0eoRFx162T
+_4ZIKg-{K1IsdfXy_rNxT1O4lnE3m3Lzc{5+t?Z_#Y#gs_2V|m`72vaS2ZOj{1kBD`Ga-{frkI{oCi}
+J$bEb{uiqzPPK3SIUK-VE4OuD=q7Hf=%h_KKe6oA<FB$x)_R60TkZkK+o6#d$q#e;u|IXolisvjH1D>
+Oo~cI^9m!H#rADnCvVZ1YkW%sljytjGm0_8$D{V-?(@EFMfnw_Y1|^GGy8WtBp;teY1q2V7Mj*Mp0HC
+6~Sfqu#1&iCN&+q;i6+)hv&jl;A3~k5lijc+hd3nJ}RCH#-mfM)hnsMh3PU@k7nd*HFotzZ83JD<*PV
+*+f<2X1#<=Y8Ff<m=+Td~>uE|{tHlI)pXLDn{B=a%9nHzsSM9(^nuW9jP_Yt%+2KqSreAnb^YSSvU#E
+V$OqCv5^Cxfrg-3%P*C&r`DQnwh5=@Sp^e>)2Q8DH6*9j_rWkg?L1WO(K`0Y2RP^}GobRx-vXA~|CN^
+e;)3iG5uzNA86XkFD5YY?n+Ty`~Da$u~Jwal-`mLe{VV$%*!_rx5NI{o2?H?W+oZG}Suj$JL!I!?u*z
+%<d)@lJ#?H#!NP>wr*>Fa!0I2O4?-1GJs@2Cz}t$es`0EJ1-U9o4<FryM=DQ+;Z~{_ViSSb9+Zr{nlk
+Bq}?BV*kmoI|YljK22r|6IjBAa)4jk<?m`z(!Ml3RdQC0T2%epxzJk%ejP;^l?hhB5u;FcvcsM5{Heb
+Zxlp{7^ug70eP%V<Yj#V)So{w7`GS28m;QpSpy0CFujntn;-lYEoVlvJ3wuxjVRu_rX>HX}FM$<bT=r
+C~x9c@}cg%Y7&S^`$6{ay1DKH&D+r!U`Mae6#zIf1;l`T@HSHS0vFmZ|x^=?>|dTf97C?9~f{@nsd!P
+K3+=;<sz`+L9$-K_%#f<E6zzt_VDV4p|Fk%p}>{9iA>|L(5R5bK3?fK_2snygwRAX7jF!b?~bJ5ajTx
+rB%>wgvkhG186T(FVRcy@)ars2hYM9fx>tnb%4aqeJf@PRt|gJnDZKLD3)ZO*X%dww8k%ExhnwCq3+w
+lh$C4(IbB!6HK7rfifs?ka9ct9kUbK-lzq{XI|(}m;8m<WBC}9M?@n~`|<E%Gy<DsK}O+iN9#poVpLS
+?sLSri4STTk5CqbT5Er*A0@D3LB|TJ}q<0Y{>D@(0`dfuZ+ATB0mqjZ`71Q(R`WNl!csaMpwKbeB>po
+QQnAS@_w>5-Rt<9CgIDJ2tZ#%l5-S@Kk1F$#&#&JtWH8>zysdb%U)z~16S_W8^2Hb(IuR$O68hM(a&k
+gb(_Czsm$qLu-E=W!LO+Dq^7N%5D3s63Ji?j<+K6Oj^2)=YFUY9&n9cf9s+^dz)Jp2~5q`V7}8x`J*(
+v4oe&wh>%%%S9d(l>&v*PXcV-EAv#96N;%(op@i9Gd79IsjFC4nT5v@P}B<AgH_Yn*&0@#8a2G+ZLot
+E;0%=0!1cE#b;-%?jDB@SgHQh0DORq3RRgn^lLh$!~x?vP~4LO1!e^|x6mnCuQ1CO?uaLOwL4W|7FD*
+)-twU{j(SiV-QvDM->tyI-SA?5jmZh};zn(?V<<cR(x`yy<LfG090yrW96DdGvoued3|ijc-eQ`i0<W
+}xWmKU<m?|eY24;QSLgiEcZ6kaNUruKUJkXq0Nu``rX}Lu$k`y)!;D*lXuk3ACUrwbbARJOW+b7^~5L
+Q{S1X!|9nZ3o~Z%-*qv8RERj0i_R0@PJ5qc0}M(HGA@hc^!&UHo)>`QhmJT_|tP;7tf`!guoGV|elCb
+^W@4m!sG9N24!}umuMep6ak+;Rz257QC=v9Yp}mi`6F`tUh(I`pkp`C_xuFGgI&m|GeZsKQe(&DUF4l
+;|VH!j;{d+qdOPj@#Pm0v~~RG@}rRpv_C@u=hNyx%IEhKwh148C~{P=X~nceE~k{(ZD?j6n4|nfQ)J+
+rzEYyikO$Wwqwif;-&*q_EDL-N<4qzBbtr7zZ9(GJqlYqfQ#?psOe=Rf!i%4h(_bE({^d{q_R;wM<ge
+#n{6~CpZ~FT6yVFaZbBiGShm#ylPYJI7Lo7xa(ORXgq9uiOIb8-#K~n+;=JV4qL%u+rWL9p;EuB{`TN
+v}<y9{jibzZc|LqGweQ@RZ(WMEUa`k~Svc*gC*lq8P;F18h+L@ZXD^KK{AnSQqG?8!NPm4hO<-5?FZ2
+*k(s)cxl37x;)E848$WbCp6{_r^MUnpgf>ZcU%rZH%K43$^jqo)X4I9xpJV3rvea%TBnF+CFBo#n7%O
+>>O%A!j4#Ve;8gO-{vo8=9iD@i?4tD`zL1MPt7l%g^n4?ONouKwn1dD=9iBh9d*!s(3v+WElC)=^J*{
+*y@tWKokgo;Jxded13`H2-o3N8=#T(&I8rPB!D!SQmqbbu?;k+Ft&AK0ozb?X<R;*lzUz{<elf<Sq7P
+1ZP$#VAMD3jbuPho5)$qosf{#0+(w$}!G#3vp5if+Ao!icNzq<62wxC|h`W$xrJ=R`7Uhk|p)ve-GoB
+Ls!LCC4TPP`xKW7W2(+Z<<{`LmWY{WuEG?tlMRjA0^fhVloG3^u4@)X0y5Wm&Qs1fMtgCPW30b}BF!3
+wNs}2~t_z@DVcnF9ChC+yZ7veXY^KUbYG!O1-K*uAXMeiW%#hq{^}G6P9?VPYccmE4-*yiQ!N-R<#vK
+j>&*lFgD?YGMbWyYE7C@%Tq#3(h3D+PG=L!+8X~S{PPt`9o!KF?e<*qwniza?d<R}U=+ZKEf_QY@z-8
+uz}gSvLs-RjJ;h;zUYtLC@ZKAX>UVcvu`0{P-CXR738^KRaZUL$W=08`M~az_D2R;mR`QHVZjrisQcy
+iae_{<0$?2%C2y|L$MQ1)rM;)w;5n{~X#SAk`ECPP5>DkOh1%wSmJuF2!s6Bje=N;FfT~_a^|6RP}R!
+KF6=V~<VpQp$(dUG>PYdp>Cib?*dDW_~3p0lzM{}=w-DK<;e>2=9L0v<3Jm_vtlD&PoMhJrioGR7Q30
+ppv~6mY#8?59dy9KV23y@^cmVB|&m<kJaeg;EKwW+X3i-l)D+XZ#hj1yVpZWI&>$u-w#`(_|8Bf~6oy
+4C2KWm_0Bflu2OQg$=E-7F7cnFituOO)GF-f*i~X&L4*ft7tn1W6KpOpAv4Q>djFpbP>aFD<IHJQaT%
+CC~ksH?@+bO$$>GXA-@v5D3=5y#32+kj%K15o&SpS!?3Cg<u#}oMQh+_ND5LimUjf2f;qtO&ey>I0Xo
+bYE<*rEEDu(65aqGK?}$E0k<Ml;97h9|kLsx%AV{ttimOSArG#J_B<LPgwFZ^PC2C*+XAQeYdCk~LO@
+l6>FOzIt7Llzl$gg-F**QsWVXQ*E8gu?`6JQ{ROdg*2RtAkB{Po-f(QF6p6`UFzKLKTeGRq0La2_=*Y
+*RU1*1~A((m&?t{%$hP1leYqyG7-t+^~}aR1N%v7f+q+SdlQ*G(wm2OQ9n@OHd^e1g$A-j|-|X@fe_*
+r^TCX6H#(xZs)x~HhiG7!#7PTYp3h?-2>#%CS$RCdaD6C35aw7>r@|~+&|n>z>UV<O~!UKP{8OcaowL
+==`>Wx;Z)B0@x{^FVTmNUIM_1u7#+skne=f;Ifnx9p<_ZT4^CXwTDHzE9xOUz=K8D(e5G<APSXrI0|#
+GOj+575g5|~b?P-+1PXGMbySIP-Z2H+J?~-bb`Oe=R78_fIx6r?j&)%IT>-4ivM(@_iysY1ze(^f}X!
+Lq!dtivnF&4Ok+uoE`Ut%g{_C5r3h=tRTDoCgL=#;W}zVL!EGYodIP!20Q!T=ZL9LxiqaAhyDD!WS2r
+dO%Ac|&mkI<JbAQ^lJUrOK9Iu4R?!Mp9YhJSWi19FO+7GwpKfoG^o?Lee=51X`xd4bg~kPTIUiKCE+j
+lU-J}2{aQ*P+pd3<!f+Mb5DXdKTUM~P!i*0MU-{}!KD-js}1GMAvV=vbbTiyTr!{|5~q(e%aEgkX79R
+(8V$BgOc~#ukmSRy0!epjGG|Kki2DFN+@kM5V{DygbQb`=G*fF*D#<Bn@j~6aDI$sCDt5E<EKTuOVva
+fXeI`_!&mHn{M217*_vj@hZ>4=05)*>w^X)3>ctwpwmxN&2W2IW^oYy5e<^N1KBqC}}nJ1TlFZVFZx=
+u3bkNTM=)$nAK9(96kSSH(M5an6$*-JQ-7_NmKG4s|~!uSqt@WCyf_1^;?Iz164ort+E*BKH3or;lV<
+Q|I|8~{EN)xv8UO7yMM?k(W^T?e#>`w(Ru2+HL^XzrrEMAXMwA);^s2tFLSv6+~!zN8IlN<BombI|5o
+;oGpQBlO_l$HS^%C8TjqWf<#UpZwyrYw$S568f&?F_{Inxi?K4t4Kqp2_LW*2bF9@s|?6=rG{JugsMN
+j(@xMaWgbMPlsB!HfOl(XlSPSPBl96HB_|PLck9G3d4V;PY{xXzI}EVBF4^g|EllTtpF@Em1KI%2Nhd
+s8yUVo_xy)Sa&#TjNY;a#blVxU|%flV|-Y^ehgv&PH5xzTNCr7eCl?0?c9aHM%cfF_D@LA|xcVrKkpA
+0wrX?6yWn2}hK)!$kVdk&PfRt9bw*bKj?oe0}driG*Y5dh2P-P5uuTe;KizQz8m8NGIjTAq4g_D*IOC
+(DZIj!3V8f<a-oc;idTv9aXwP2QAwii==b2b0si22qpLH&cu$^JK<@QTGz?i9HH0l>-826}R~E23=)w
+*lnD%LTHYoDSvMYU6Y|m@&sT*V@yy~=NKccG8^W_={j4Npn<cM+H!~=fBZ3c_5GLMlSVyJ8P_G1ae>=
+Ittw5nEgrn4aP6e7w`+8UfEKR7QLr-Z&EJ-&g(J%Y`3y`%vYX;&^ny;paxzx!xl};|NYxrzks(=&8?D
++JDIwV5J(cP`{<0I5cXb2f8#mdCZW`DYd9y<k^Zu9Fn<p3yUEP10)VpVh!fJIQ=T}bYN~lxdsCQS`wo
+cwY;|dwvl?jQ9mo;S+j`KRrE6R_mZk6NR<B^Z&DmO;Qoep{h-(iKfa)rhwvT=mY@l^~ah;EB@kJY)EP
+*HB0>z$z+His)}f0HueKN&AwB{;gT+N;0MwenIOyv1Y3Te{H#>N2#p8~F8*OtDq=GoUBe?y&(U7u=lx
+GkiS9$hFf<HUlxiQBL>XHM{WFE^p{PUu}2bt4trn?eh)E*m<Mm=@uWgsb_A;_`!JE21YR4w2XiXy(in
+&Us583=!(QZUj)U$<!UeW73PMbj8c87LFWoPIf8CN9}sOMUS#iz5MCmguX<3%r+cuVx2T%@zTT;w;-2
+0~4;7SQ`7Ha(YSz_u^M$H?86cy?$VnyLC|%PqOo_f?Se_FRJ)I5dBGi7T9foGfrG?7PWlh)6Xsm~a!N
+9_Z+UhIayXw?q#Gk3-BdUdgk>K!F35PQ-IK4wF;R<)$P9KKi^Iw3-nkB|Io%?8ZN#U*>%BP9DAJ>i)&
+~-eQ0XQAh_l)+Wa>NJeH>Rk-56**69(=MlFN6{C`;`;yYl@ow1PAKM(*dM@b5r=Z-m^VJtYs8w{#_Ac
+^k83#%-^L8y?S+vvZEVldI-q=!kZX!xfd+LW<gOIw6f`F9HnV-11H1J!EgnO{ruvmN0%QyiY|Wo{PLs
+G=YOQTRQw@&UPIRQPMHcCC8vZUhvn({p&%zc;D_opOlEK|=z;AC6*LCBjTj`;))O&k1hN@{gTOg^CzN
+BwzNE{hz}`6Ed??r8Sg_i;u!}qtsRD+vrI{0g<%v*Ywfy%9LPA(Z6p}=x+Za*)eUetcDc3=7oR6&j#e
+lT<5Q7A_RgG!$PLZO}XT$ma#8kptLbfx7c%D%hM8EK1aC8p0Dd}=Pa^;OVV1oAG%gyNu7W=9ljec-+F
+&K_emX5vCA-e&p59cy+oT^X158X5Z9HZn?+X3N8k~vq|V9Bh;zfKxoS(&z>Mn&$DP)iSwN~C2s(ym-6
+7)a)FP1@p^kwn?uOCl1C;pyAq<1Gmz&*F^2?`yC(@XSWyy;5P|R+TS;$8~T^ff$^Uu0_ON6>^UcsBU;
+2q?yJa2w_u|ZvcZ7E0x>jHG5QQK80dl4rpJmGMEEHC;LAv!SlZ?paI7a83m?4#Y<fgFXCcAL$}H5CbD
+MRAtUnTl2?|hd0nTxyJ8c?q8L-7J=V;kFbJKmsU-{-g<pTsjs_j#&Q1%owhqk#tUMWKXfnBr3>rQl4K
+P0;H%qC5{d(k`_EGm_qxWTZRZss;rhgMtfNCeLW@6nY4$2Dq$5eFwKf&|!M<sZQ4tqa>H*`NfJIc8Te
+t!yQP3V2HiBP0SvI>Z`V{n4b=0lq!ZDI?#_?555%6YF%Zl}-bsFXf8&;)9Cb-qIk%hgtew4^NHCGV)d
+S@(#$L`f@k?&9`LgM^A|sJT8vsSi<Q=?5rTcjcB~t?|?-NWWy67bIGfM%AcS>grQ7(aHZbYxpCk7ZKN
+q0klm5WodZ$w#$}ujtHIBf>;!1!SFIFULzYyT7OCR>~Y@uK%ikx`8J(A{Z1hpp6zpddbcL&@K8#-D|L
+gErE+i>Hz?_4z!BS+U%HzO_oUsdndRs-kmM&oXGOMvYU!4OVcPZJ0nx6I&X1ks)@DW%$&%h?E;)cdN=
+MJTsYc%Ca)R(VUhRar%BANzZ^<F39FR(4AUSti<nT;aC%jF^iSgYWP3i$U3{@t3$pFDDj{wUro_r_x*
+og`vN|!%&YWjK^TdYk|tkd)unS6cOAa$eJTfDT}t}E|)s!4(Ysy&qM#VG5^YhYHlG8-84u-DO1H_t)F
+-$8Pr)1_vQ16_6O@pzE-fQcdPyRTL7B%mlK1>t9vqx$i3S!GK)jv+`~?M;b=P5&luiR<rB0Q-_><50B
+;w0(|~?g=Dkhz~IC(b-aARHAAqYCGy;;~3(qpb(R7jPDmw!t*Rpu`8ZEG6<^;Umbji%p17%w|hs8N&F
+M~*5!M;O}j9z6kdBR_qrVY>Zxzw=>dV=g?d)CU;EaZEVwS2@AoZIws}5UNB0RQbh<OWkIvDo!cb4j^^
+1C-QtI|Doex#X-)-$Efb4E`5&A<o8F{qG!AR~v*Tcv@p$agsw{r~L0It<9Ou0KMMrEg*tGdVLU6|;BM
+AzSdY;GgYB@6+zTMZ0PW)b=+BAr`#O#IjZ%?zEx;<2zDmGllo7GM2fm)Dq_+m-_rAV$|E?Ezk=>5_tS
+csIt?`1gS|tJ)@g8<t}u@8;rRtH6fSu9(`s6JRW7u_pGwO!XZC^z(1P!vREP^gESNL$M1f9WQ`Ak4;?
+P(<b@#JrGY@VzBNZ=(?#;Nc8(hi0*ovA>@!zJ9KWw?;b@^rOP%e^=I`~4;~SCOF`b5TylXyU$<4>WR4
+Ox*57r8C@(~5pPg=zCwCg5yYO|d*Xh9nLRU8|2Esq^4uvtND5e_y>FXT$bg#k?=oasbo6F!GQ0rjP{d
+Wko!1`dNY0qT#j*_AQ8mFGr>kL`X4%hH(UiWsuI_UiH&&1?W5TL5S9GgN>_#{RK=7hSbtOpudaq9383
+q26^4PKp|@y3-pT7|c9P`FgyO|{3c618tyGZJy|m2p!%-%IW8JN1B)Bf^~VOJBJo!PQp3f<-S7b??p@
+RK@;;Alx<pIpZu!0OJ;n{i5V<@teBr0Y)yRdONEzM6GCADJk{_=A+528+M?@9$2@u(y4E1Gsf&9Wz@h
+DbdwwA`y#ugua_JyK!NmlMUvfXb@Zt0`rkZw!3dC(MD`tH>&#d#cg?{hHyD+MN5Fdk5p(6+octLgG#8
+fJ1oqy)%T)c|x9_Uz-NO}jDY*q^VD);iKj5O4gS%lqwg=cjfFSMjL<i>e4a4C7pW8w?OkMCsr3ut`wk
+F}}rW3i!`o70&+ihZF-Z12vW#o^<RS8BEsG0MfQn~2Lik)7xl*&lQPauOAr#w~WssK!1D!{yh9i70->
+B2G?=I2kJ2cMjM7To7gpFD_<oM3PmV2_dLr5tn5p~vm#&w9j%xiGSPx5;C^EHo~w<$b=V<NlpIlIG<S
+{(9nW40)8$Yc8pi&fQ3OrX0|HzkB@c)9K%zzIgfU`|qYNp1S9%G0`BB#iNVYx2KmMjb7Iu?m0_cg|8z
+_WO$C51V>*W0GO*s7e9@#`sh!HmH5Q8!<aKG`EzW3Bgvmk=OG<>vTjGwRr>63={IE}x|0Du=CtwViDS
+I;$C2cHx3=`#g)J4TPn0gtnkBnN?;UEkU1QiKNt)UorPiI-GX{^-)Cum;$-`)lV)mC%iV9Zf+I%kk2?
+B4gOBK$63pQywzKN9XP<rL^PE-NXo<nDLaJl$8t=bg7m|8b7sif)XWV$hONor4AJ)P}{5nkXw9cOV{?
+n3SfG5uAePI<c2T5Sr(Y7A`FX<g<SsEhk3_6{^!6+;khY{qPNALp&ngG}+8WBETEyBA^GL!!H-Dx0ov
+u4%ZuBO8iAY*n@fJMND3_JS(8B&Ux>?j^d`J8GmA`0Yf;eK~~gsN`><Je>x^Lw!Ak{G|7Po#tfjXtLg
+$@RS!^0dP7_Wn!L=F7276RD?lSEzyJLv|;Ic>Rw5v*ed(Ek7D^0uNpa}%X3hib=j&zYzR^!MB7qrAv(
+muWq^*__w6O4Zj+IjL$~zUr)pzG#UluS_*({W)#J77XphDnoUIsYFXF_Z(&hEhg?hW$P+rNubE_Ve@q
+`W)qOOrNr)v!HhdGo>%_tEPasXf6d|67^vK#^H9R3={+4h@>rYb+SWP!|QDl}8o`zb?T)@$$7NswcL7
+uBp5$io~$IfzmWlkxyssWZ}{JvRlLcr<-Yn7wjF(p)R{)T`?d#6HBU&~c9ENsVVDYmBLE@;Bz~dH#@i
+S)?vwXgU3Yo8mYI<~r8gTbE3z`7?|2`tLG|-qSqn;yHINDOH@e>&d#qB5<2vW8LS$r4s(iMhKSbuis;
+LkogP4fKg&qw!z2;6)Z1s$Lv+17c;O2`*yP)H%+$QG%QRB5N7ymr-L)56a*~R`3Ks|;jR3Hlz5LqNZw
+eHCS(hs_|fTJd84tG9ESQz>((rd{uc19)XK5;M3>ROJscT}@+%7YP`gyBGklHr)s^bc)fM1SV?+&#hw
+j>{jlSSAyt<;ELp529b(aq)*vt#sk>fnFFiWzzuf`XLv?F$?nGS@|2cUbXL8f@3<ND3LwlTXhNGp>NZ
+c?-Q%|itb9g=UzkzfvIO9c|xy&5<ukOa*Z=UeRs;^`DHL6F@+pF-erjes69vCa7#8*`#_&I=#)PGsjH
+m!U-xw7Y?h37KTi<>7Py16KDfW!2Pk+2TSA#YQyq`tuymqvcUjSKxB^<<Fo(68KhTZVTpZsn_j9;U-o
+0O56e2P<ypAn}Mo6X&tu9-85dr+-fTtLjUHf6`WyFvz<1Dy=u@Zuc(sE8=G`GY;VB#0gUJuyfCYYG-4
+x1M?E;_&WS|~`+6YL+BLtZVsF+x4;fHD1}5BS4QiW)VmnZVu~>Vo%#KgOFR}cYetIMpYHF>1rk3iM;v
+1)Y-?5~iHtzLiSN)JK=UAo46p*z$fmkXM7#)>fWlrJK(dopWv{UXS#|j<vvtgIV0a+bps8at4HDfGLL
+sW4i_&Si=p+U@DV<_mP$zbBg!DP10S76fZ%MVkRfqCvmOAkHNBtWjNa4M01QJjr+ce(cFQtINt#+3L8
+PI+D6uG3ijzd{~H%Y&e{#R{Z4T`GbCk;QgGIZIS5vN55`$FGEfFK(@kS>r8HRoK*Snc0}Rmt2cMcd<<
+tM^mHn7~(}iZzzFqJLZ^SZgM5Wm0p<{=dy-Vk@N5)=Oh@o@zrv9<#D;XG6)n(Z*;KG)fFwX5zQdW$9_
+bJa;E69p0;tpLs%Je(K-(?KXO?&rxFCo-W^N(6Z!98?ToJ0JKFJ7`U4$vt2s9{gJKmz*T1?7w?z#t*X
+Z{R0aK+Lm<w4QWD{|Z6Q-(YNO*UWY+CEpYU~PS{!Jm<^Jo}HZgO+pE9rT!+)AF`iIemD*5+$@h3Et38
+(=!-6^|H8Jo#`!fy5@0u{DME2j$J7N;cC!2_{&OI1Z>tThNb?25_$=pXYc(mK^O#`+(#A7R3yP%*617
+jIc5Km}YM#7_4hzZ^VCE(!*TX?hPrdPpQlMd>$QqoSd7yl<r|lB(n-2R@$Diu0r56Xu17)<H5pxknMR
+MQ!E;vyRk*R0jT)wKZq`SBz(TrKzPW_$aN%bj5AQ=;gowBvd$i}UiYp}+mvZ3FDX9Nq-JI{Q?;C^Oz9
+M2%Mqb?X9<6xKtH=%jt7|D4fsc7$f=f@CeB*WJ?dFU$?AGu{jmclu-5RxnN2$TEB=Ej-V_$r+|6)?59
+_7p9<)4OT<H$i`GALEAbEu2grK<Nj_bkpljMq(F;#5wavrhl-?axUrX806wQ1F5>E$h$se*MUjiFv2g
+-u1>UQ*0Ow&T2?$fuAuQKOVhG<)Ob{nV{va-7M%dV=f6+(mHk$Of6Qr$6s#R}sat40U*~)&n4o*}cW?
+sj-lef2W4=oq+Aw737A~H9Zjo%G;`_%JIQ^+*&FvhAMMN7Od||qa_YcLF|AnfdS$7oN4Dt5Az)##zWO
+}f*NtrqM_r*B!~?tDV2zZx?}IPK#X?Ax-4H;P^c4to&hskrTi-@@vf<}_ma%rhN61~!~i0Dv-`*dKH*
+&gR<d@36DJs(CcdfAVLgAv;ozzQgFer72aut+-h$Y-YvFV~LI|TSXCIEIBGIzn&nul~3O}$s<~JtZ#c
+P-%IuB!K78JASfi2_H+0K?MdrfJByi3H4o(^|k)053e?V0Z3BO=2$@c{F^tHf4Mk7LF1sPv5DFs<HMP
+8*%k@_I8o?|7?7)>e=JQ4!yivSTysL+}Xw&5*=56|}ZvG2cELjcf;|B4iH%8v@@kS4A68Q)!W*Pw@Mp
+>`N(mPZBRv<x|RdYM_9@r|~|Tr^fqf+-{HJWie$fi#(4~EnXDrXFK#q{|8V@0|XQR000O8^OY7yp$jB
+<Ko0-_9yI^}9{>OVaA|NaUv_0~WN&gWWNCABY-wUIZDDR{W@U49E^v9x8{2Z*Huha#fyA33=}JtKzU;
+VhGG52o^)$9Kw!7_2>{TEVl2}s&OOTc|(~sYC4geCo$Z_H}Q$55IE(hm+1G2*=yAu}4ILQ`!tSaY|f8
+xk!XSBl(Wxgqr#j<4nVZdH~|KjBY{`rA@*py3|u?smb*SrwyovgB$mx;_m*mWTUdw+a*bb4_VmUkuNS
+<DX4PA|`o|Mm0b+4%(y!n)DZr%y+RDOZXWA}@pzSqV6>aw%AvsFKM!n<uGIEXf#WF$@*StkS29j()F_
+Tb{xmR!W+LnN)?i19y)ZEQS4duwTiQV0j^DsaUCy&P`UiEMPuc@iJQ4pQ&6d00sL~D*LSvqtU#OD;6>
+{@)piWliAT-ByvQ_Oh<B+^U^Mwt5^VMg_%SQ?NlVQTTzr#SiKyLM$;)z)9IA$vya{h&yu-NrFT6VAx5
+HrVS`$@5aoLs^QY5{uf%j3jDY8yU9q`LQ@Iu~n{717D(4Z9{~bFzdq0{Up1ps6@ZsWU`r+X6^62~&fE
+I!f2A28W)z#H{_bT(oIC>J``jeBR)61i`)Az@xN1fF>;qDv%&Hhyu{Bf1RmwyCiBl`pYTmVv|h-7Q2@
+#X9{5tYH-h`|SBaL|$RtV+@naR<#3cX=ucs|_^2QKeXsF2T=I6htS%;(4BKaP2KG5?Lt*JcR|BLd{q&
+K^$v@0XqR0AZHO6(U(}v*>sv@NjaVRN~Ckp9InLvsmz2y)~>hTk704b0eb=XfOe{qXA!`y7wqM{;46Z
+&$1Z^a{02%|00U&PLA*4Df`Q?PA{;yhkqK$%KB!`dniBGv!oR-#oylTF1GG*SxDZq%TE~761Z_}YV_*
+*etO5KTu2&*&F9cQ8BH3rih4!SKQKIw%P4bTZmwl*aX%ex6565P>W-xLepb~%EOnrxPfhnT|#10ZV;f
+LaegV?7}7N<|2kbAfQSRvD@Y|ukKph>7kr{?IGhPg{736iUg^<sK_aenmgF@1S^e6GK|KmBR6l{?PYk
+Jt68Ldw$Ra<jeU(G9M}70vow=C~XuL-^i|S87>LfRc+WZcte-;b1-2@Y6XR0Qo^{LbHZc2@Mal(Z{vH
+sI_ViI)p3H>C%oibXroSDJ^N#@;8}e1Z_;4Ixw|%9Nfwz2E7NDq%`9r;GbfX3Hz-=N014&PCyf4IR*j
+q?1pV*^`r-hw$jn~&9~SDy@f0AyqbR{ES=)d-kM}?<?iEr(5DtZR?bEWzV++P;r-Jg3eBx5Y1pLAnJl
+ed-FG6&-XX0vBp%1cNhRP8xA>=n7)**Rz}m{nIN`pJLNMA#S@Xd?a7+k;i2@mAY$m{`rx4$7Yk;WN!*
+&H!BaH|b1yq!3oq&IGJWz*ZT-jBn+I9?`11=~;R`lE;t#0#c3dC<5rm1_m){aRJVxMaBPdN;Pq~^;xh
+$cu(0Ta~lTl~^a2#D95sYzAf#Dvw3HP6t()&4u4s%>%XT$ELjnT<`Z)x;@TfH!V!t$$eNq8q`3#w_m0
+6-NorxNRIdr3qs(s}{azU?42O;hFLfP+LFdEM`v}f7-VgkOd@yV>Wg4Nabl#cF)ESBqfk|nLS!pGv<4
+6u=m^>Sl%L1?{fSb<gHa&nt0e~XMl4%pg1=Ws36(PH&0v(vEu1-td6Yi1Zu<_(bT8)8PG=3vH*UeLi+
+QLdonoxu}>#Dq>ws%Y+2$iew~(8o`M5IJeuEtP~SOgC2R`;zc!m2I)dv)d!tnFVjw>_#BoXxB1msRFR
+iAAy;P~4nY5<bfx12vY66T#Q?2n}oW^?U-&+jlgWg9~iCKvOIa)H2V2+9MFlC3yLonIsBr!F#PoK=-7
+!$ZctmLg2+HF?#oluR0c0>(M;A#ws#+$39tkM|W5!F;65~zO$0}#`dOae6%LkHfJSb)jN!Rhh4ql?Qh
+$$%8XYp);j>9Z`$eDC@5SVo`{R5pgPSUj(xJy)_SB7widWw}as;9PLGu^MDN<e0nwpQ>xb3<xF-12@U
+4e5}n)RfS%lE7z=A%|IgH(%0b5lmiO*K?T8>0<+P6+9*<S3l^$(gp7GS!Wz%REj(TGjSB`82{4KG00~
+c-dZkc$GahSk1vtNuj+ilthdXe}WdVgVmeU3z{KafTVL?O*FCp<Go&)9pGStfEfUt$nnEB*Wfh;=|ka
+FR$83~%;qXCbd?En{6k|CHuo6thR3IcE}rm>ZggUQ)XkO|5)R$e#wx5P56Ap#i*)jQ;ah$Ado@>>#`1
+7c8omKVrS@+bt@0EJ9aLB;0+!Qe|K0PG|+UO}a|;wckFA&W`06wwWq)vj|k(DwJ4mt;{|#lqX`xns)~
+up`U1zFRiAkY=2LH8sn9^B1)*TFPSovZ%IYgC=CId65CKzL!b2zG2kEzykPL;nh-f#6hX3LlxxUkD*E
+*YW2YYyPvFnIbc{ru=+>rY@r$w$XhJ&)6K-N)8;MSFpSQ}`#Od#QaR)4<XdV(H*a%44_Ai)whVCv667
+^{LY@6JeYP)Q&No#m&7HrER=FJ4gt#1iAdoL+Jow$JP~dsLjbiHJZC{6mZ>5>KAJkMNuEE+c^&l2~8}
+)4S=x?l_LwtP@tcUY{QSf=$;rG3ohP}p`g-)W<XVd_XEGfxwRQ#N>Imd0MC@-q4t3fS_x_a>_iiV`#r
+}DFA<h6sHOK3wU_Ngg>Ecjx@_kbkG|8Je#lF_tVLu3NxA#cNu82Dv{Ie1fEM7+Z6ty3ZHFq1x(VwI=7
+6jRLO+O;XiwtDw=#qUuGshLS2wS0g$_RRd~wxx^Y7S0;#OZT$bht+@o#gh@FQvfz8<O8a*TLZw^r0b7
+8cEEITE$8>=<yOgt3niT_?>p$}QoIp5C~hh;pB4tF-Y;}6j=w*Cf88`jY;YMjiQ{L@srTuoP+t$>5jg
+Y%=A(c-h8yG?$JV8E9KsITpV8|DoCGS63M?)wQ~HAHa_r#LvT1GCRcn%Ao6G-x+^lk3oAqk7yz80z@@
+(H|o7N($ZuU-SpkVq^ZRzPmPsZI&aZ6=iv6p>6`cU^l=R-6UcRARHVy_H&FJ9MQU%qBx81}r%0G~d(m
+$`(V?sicJ0Jx%g{9^C=dN{Ta;|7#-NZCHak{|v=mVne>e+6S|t?6+EcyJv&j)^btXX5>K)n?X{{XbKI
+I8zW~!Ee8EjOF!KCbcbU3#Zxv`XONw@?~tdWcNfx+O2zC4~j2Nu<RymI#>=B{B_bp8PInBn5bzX7UFK
+`n&LuWyO&{6lr0qV@rg*a;<I!^$uC{C1iClsHT_?YIzlxZN`62cG>)Jdoc)|7cN0}^sIi&G5eNP9(eV
+V96sN=b!73>B<uD}vUL~b=!V2%oFz<m>Myy0u>X#Qd<(YRSjbC2LxJn@t@OC{4v)&ueBA3Z*qCklfp0
+a2OsY3)Of-H3X_|!}}7i@}Q?u5PDiS7`>*?UZ-TrIIZhV=c2-EtHyjp_#Hj7K+iQ4Q?v*~Rl;k}Q^M^
+&_pKeF)}+N9^pvVa4$r8wDt0>VuXbX68H5C?NIG?K!L3nnGB;l7)K@jv^(jpYDeF=Bd8-)r+6brKB-@
+zk?jdf#)+`;9!Vo)NovM=BPkpHj|}ouqxqz?a-mMlQya<hs;RC278&g`o-u56O(6Z9@>31EI%nx^+2m
+^QcX+*nK*lD-J{<;+mw`!CwMJbu7zl%0T`^tF4k=tlSL-+-AjT@rn^}sI)qt+szo$zAduc!%2A&UJW`
+K_sTN;qb}KMBI1y6Ty5Eehjs%EqtK<n5EHFmLVvm_SZia^F-48ZrA*(41o~e}E9&k(rlJ2?VS{S0Y0O
+p-hgB9Wq*$<@qYLY02Xze9n!uko~9w4hP6ajJP%DCBPu0GLMVOY@BH1g!07b83D*I$2Sc$1;Y*N=|)O
+oF1WZGYRfWP<pn-fw#d<m<Zm7Kp8GFIXmR?SEPKI<6gPtN9*_ly89eRC3ZoEG6s(7~XHYxm){s4ET>8
+d*H&^zTj~Va-WK{!1SVSLu04}FQTPS&l>vCw{yKe{I0|D5rx+cX*B1AtZqgA*M_Wfy1RQ=$a12UHuk;
++I{JD>mPdu9!0<r2>P8_HhSv?y_$Uwb(=ARf?d!@`%p?yTC-X#m7l<#B2)7zZTcaH;sXqW^HpM9Y7!2
+P+;Il*~!bQkP914vebn@cx1ibG|-&xZ%guKi#jOd2rn@6Uv2wR=$;i~tqwXo(Ca~qF*bnpBhp!f6pm0
+n-|!m39peV@9=tNs0oe`!y|;II^FI!TioC%>}!0BS?++|lP(DQ}ugkV}KXDE+|K4xTsnvhGH47j`2ag
+*!KvX6<06dm&(pH5@`mM;;%<MHjd>x-}vIaZ^yz+_o`@`8qf%yUmSu!Pd7?7Tun%B+5P{ZFCVZ8PFFa
+b>}&5ucJrY>=g~p=2miVFfe27Jp;yrx$?A>Fw{S|e`pJU=QSsqm#sjCP!Z3CzwHH>=LM~{m3hYsBEa;
+pAFm()CYVV=7KU!d>Rz2&x4E>+%Z*ct^+@zOZ-Vsc+xVd$xP9E4V4>N99bC7-D3O~-jLz#@L!cg|mh;
+t>H{>vIZ-JBdu=;-<`;M7czOX~H)vwY2imp|-iy)g*f_@A6z7MsvjPM{E1XIV-**GIMPx+$D2t76Q-f
+J77%}lS_?b{2I;J8gkjxB%rAXkT*+iuQyw|l+XP%2$#!{FfFCi6K)drTRc{J>O~VeRK3(CeZAatW+3V
+=1(8n3AerUN%0-*AJI4pQy`9T0c*sEpDvE{ysAOi<BLzcd7o^HJEldHWLxPck)8BENs@DAC2|nDhJ&0
+$$!q!0jvuN90O%?;o}oMucX3GD(&SpA9VGb=n{GCfTQww1ClC5M%f9RyD_&J2+C})w(wHlrnTU0uy%&
+(MB+NAb?F5gR{Gl4#UP?mH54{D!d3@ReF(jq&if^X#(?=zRp7(KbhBq3Q<DpE<TvPVKI3b4*jjk_e-x
+Bk;t{G@l2^tEXmqS!-mpwKMVp3!|Ji|vHqTyd7ouDzkr?oa=+|7&W;sSPFFyY7nYzZNmP<!VAu-viW`
+5!M{+m}%uC4+f;RU<FRS4;@fi`yCSl3fHPUQbkO9KQH000080P~d=M?Dd)0*wa%0E!p@03HAU0B~t=F
+JE?LZe(wAFJx(RbZlv2FKuCRYh`kCE^v9R7;A6aw)VS!1#f~ta_b0fw&7kF%Yd%Ph5$obEZM#Ufy!94
+%~=+8Nh*mq^uO;MJ|t42bBo~$NK8@Z{U%Sz`KxoX5*2SYOVa7}`G0U{b~-yH??ro%e6!Uge?KP|Z{A*
+<!@qCH@4TT0azp>%Z2@4v@D*zmtB4jvZyEV?&|A@v8?n|;R5J3D=$eXZ4tua*l2G8+XvrTsE#Pe<8QZ
+f)Bdr_8$S>FLuRh#d75Z5d+EnEI#}Bu^UjO*p?Z;nla1-EWCnqPLs8m2~4J42&YaY224H6=IDtAmOvg
+a!)&THCiI=W$`Vr$-joV+2Qu0D}(zWuHMI<pzyw?YChrTtPUzfy;O+16A8xBYCkmSRtemDsmb6aUp{U
+BX*!v@r@Vhyp#EO{otpQx31wy!KeUDIY=d*{m#SU6&=fBzGBaG?T4lI=i3EW~-ViMXnxc-BGYtE*}11
+D?MM%2s|i1{=OqM-*XMhF%M`g<9xxMft*qxkt%vTvk)zl2(5tgUuFlNjQuxTlA7)xDoUOg<gg^q<nod
+n76ZsePeFbdL9hf!Ooca75b#e3QYRRFKY~aw`aWjVh>*;Zz&n6O-&0EeYeFR@F%lc3{$foF4i!(VD$y
+ZqWM2rY(#I2!gV(~p2HadsJwhyl>i{l0Q^lFe$eF=#`z3GI;%<3yKVY7c7P12ZFQQDiGLlMGbNW!T1%
+V92<bfnLpNPPhfXWl(Ib`*QX~K$4K`x4a7Z<M=CM`{>LmC05Ppl`dx1nScP1YR*`Jhe!TBx?>I?qV9A
+aCb)Zvtg9p~{Bt(Zxa)xC%|=bSqsZAyqAN_m<D+DOfN7qY4T7t-@{PN%%qH9^P%G;rDd41%rg}ZYvF+
+tR0A*+V><}l!5I@3EFdAmbqf}dSR+7d)Aj9M8jO2z{l4Le69hW)0!&=zJ$<Knl>v|%zA(&1Q6YOQcH5
+ZCaPsCzUHi25OjhfSlnwNcHsH0#rW4-2!=gvTZp_O$rlAI98t&!zk*8j*OwSbiB7H<z@Qd`st}6hTG9
+=g!>KFQYsK^tMSo@{!Z9C!&DyLT>8CAUZ83aTv@^LvNh<~9#Xc>&XIs{+0lD7383cyc>`Y=_=7I2cR<
+fo2A+I9}_wyc|uSpUFsvP_eDF#YfzOR9CQ50Ff4PB#?4N<Yhwvezjlg?m`sF>+UFW8I{htoIM-u>j=&
+8?w9kVe{8P7L5pwf20oN|d&G^%5+Md*_3+u6?mU)CE~{umgnLOA+lFB*e4#uWh{;Tb7D-^5@T<+UL&@
+0&FN!6^9x`4+zAhgqE0A4ZOvb&zRhj4!iu=M^P{%c=UM(Bhtn+hinhuu*RbavM&)7@bps0)CUrGnTN>
+kft-JdptU?q(TyiBar!B+X?T@hy|Q%Y5dwQ&u~w6t0~Dj@E3^_Il7vvGl2JWMSXQ`Xoxn?qwJ4yQLyy
+E4>kHz#-hkMLkh5x0mS$2aOIV@iu)zK+^8v?)bRe3~j3FmNM|%V)5*T{Ru~S0lCSklOvfM#o|D}(V*V
+usPS!~=|eYGWx$}3TqDT7Xpc~U_`(PY<%C$fb<XzD)G$QfGq1Z!6^He8;n`Fxz+q3z~z!NIIAckB?Pa
+gZ0_CWgcBIE^p{-)Va?U~?!K)W6s$g)tUJUsg@Z-7ptf!<dn);FaZEAelfwC__Erw1D7RGt$KZTC!Hm
+4-jFaOoy+$0%!e1!H@lysZ+r$p-X#HSu>_6A_lbAsChz8gxm43Iab6FTK<n1fmz%#98L>Mmv_J^%8P_
+RBkD|bwf1T{kUK!Tf59*@Qmj#o>)dV;BF8=mQHwV1)tG+YX$IFSm^%YKRVk#eE}|U@45m89z(*Ov;!O
+Mk@Vlm(#y>I;(qV@N&K*JgvtrK?Ft;eUYX^DrCAK2=YaHoFsMp)&{j{<iv&XbLJBb|Ykw9L~qZ0Y&K{
+C4g3Llz-wLFrV2%{*BuZMEAWvg9YZ<Vbc(k_l<<*n@EW*GPJo(lTv2Fis|DFBdTfT@n@0)WpcoVbh$)
+BJ{-7h~C<mjoFwNAeil9dJpO8pl1sZYPhR$fGEIl|pWCUPcmQOCKph`22(ZhEJk?UAy{a<A<xEeyRCz
+vT?fmT&>w!hu<dYFtJsiHc?1n+KIN0LQKFvd7Ha>pj;=iqL~Yq@tpW#Oh=%i1?FLmIX>|?^8<#vqA<G
+kmp+M_Fg0t}sIJA^Dj5AOrtJ*oyd&Dt=#j8sQi-_u^b*~P_8!$Ys!#M7O3YO9xVjA997p5ee<#;hRHU
+IJ9_uXj)HUrZ528B6qX&8_O(k7FjM^1WGcUK?qlGTriyh6dA164)P52)%9QE-D-~ZEy;^<C>)Py&cmM
+^-|kb)ER4B`9B*xcSKFMu~&b(jNX;-bZ$6t6uRoJTF9Yn_Gz^){+l%bLm@Q=9M#+ug*tTNq1VNp9g7?
+ufwv-%DoCsY`Ph@}Qt&NMRzgCFY084zKUc-~hpC?sU2~q?$l?*F2HZPP^f*dV)6}(!)wx;L*9w;c)Rc
+P)h>@6aWAK2mtey7DxD<?~7|A0063$0018V003}la4%nWWo~3|axY|Qb98KJVlQoFbYWy+bYU)VdF?&
+@cigy*zx%IX<K{_|otegNdUti}YrV;2>)XW1vz`5@*Hz>ZZL8Kuvy#S+yUqW80QfCZnsKsy?e(2syRj
+t_1VIo4KmgiilP424ud=+n+GDHsa`IpJWNUkCn?0|V_jP`C-Lm-ki0wYz*=48KoV~qoud9-sRF~~tT6
+6ZYT9sMa=2ba`PA8nRSBKAMM<=ss`=MoNnX%_@j!usczkGN4=J*63Lc6U;j~*Q^mIYt%vZc<fx@0YYf
+6dzzz@_Z6s?}J_bbgaw@rJ#fy=|sjTPLgKvZ~vLrA5IOR)<9j596Kb(=pqb?%;oR$ArS}^eN*Xmb}hs
+z+yB-47M)oYQd7^a@DSCo+K<^AY8U8b07&%ioE4@S~Oc*@&!JfH=-L~q<NuwEO^rZh6%n&F7tw`9)E4
+BQvF_3S64u1^=HkuwgjlD0`nqo+9JQ0eVFql5@ig?-SWDDUz0?(omY!x+NvHWZ4Dp~-;7y9za{Pck~d
+@4@((TjE_*G~`UU{0Zg~kaTh_dluUD;^3-qkhd7E6^CpY|l%&vHw&@(ylsl-UN$M?<r8kTa53-s*S)8
+w1k@yX$vqvUwDwS}|hHQQIypF;ar^ej%2GF<@OM?i>qkv5H9{nvTZK-*beS9RQ_%4lzk!4E9p0TA)WR
+bKPVutDCih3Jhd+y(r+DldzC-mZn*wtTT{SzEynX~uvV?`mKNi`{ZwWDW2vEC%j_xrDq*i)m=&cV*4z
+)m54Qjc4Bo3a&%qU3pVhccmFqQNCO)E_fXr=CHg?i#%ITIRi8;RvgKei>?H9n=MxtFdcUA_E2n1q-&L
+}fVGnZ_8sh&eRdY9VWTmN-hTa6a(MLeO)@)r{^rHu(O1sHx5u-Wv*Y903&kDLISw-`h4f*o238R_JxT
+%SHNjpC_foUvL58s-l2#F+TJuX@BNs>lP1&ESCBIx1#XY;rK}cM{`dp{w70;kKtSJZvR+V#(49kA~>^
+C_5Mo1Na!U4?v-8F|D+K)b>073>3NM0Qt&8WfQcks04v_}E)IQpSv;^&9IN29G|_t{SJ@^JR*#R;^FH
+Ss2rACO*0up{ta>(SdHZBe*I<1Oa?L?^2S?Cg7N^@2C^I;Uk0H2j+1-&J+iV9P&M3qDzbxWYdURv=<)
+^V@{vS^&bUe9p^;!!)*%U++Box#uBiKQWE|DYJu?&v8HHu)V3@3)p}~m1dK7$FK13Hy2G+@Rm0LAaCp
+HHqSVraV!K=qv`ef;P`9c93q$nq^DO0kM2+%yl9MPzxg*?lh-L6Q}BPW5aut{!CE!1?}m#wK?xaN$-n
+Y>tJ<q2lFiLJ_4U{9g$CnvnSFq>w@eGYYBp!A8z}(V=Z)_4LW7T8`uadZ^tQ=@*gxC-kN@Yn|5H3ifb
+-k5<z|sQND9?#@+A@dgtx2Z#N$K+TK0g@*w0XN)Xd_tdIt}$*SL#{&!uZo!;ueOy)tat@HQUV&!(#-F
+fEU*(>eU!oO(U)wFC~oH@gLpntgYAd;kivt1#426p^m|L-S;RAN~_RiqHP?==_V(qrXS+TLiyFBWQ#w
+bH*>3G7ol19W7w`v3w$Rz5G&Zjr*jrJdO4=8NmQp)g`lLPOo_3_1X#aGW6){wHxZCsTA(D8^qAij$YM
+i-thIRwIYQg)&*}`MX^{Y@J1zm0c<vJN&Uoj8mWvgc}XfN{DE(!=8p<1so>b3z+b0LA`Gn<EGMwSq}4
+St^FakyJOy=Iz29^A8t7qKAe=pq?Il%C>lN=(>Lu8#U|8Z4?2AhJ`IZ`)>^Z6LmvSCcuxb`fY~hFtqT
+0k~=dhjL9esWD=G!CKSw~gLM=nBO__TRxVS?jM(<M?3=`a%5EGrO>7X?@JC)9`^$AZDef@8*l1<pmUZ
+NRdI#Q+%)fw5KJqxRl#jz~%y40ZRiYS}UcV-!pkz)(38<WTnfeWC&+e2yS;XD^WFfH1@t9{w&Byw}s3
+FRELx0T95bZ!}AjmZR-_?r1v$qZu7hj<)-`qwV&OW@!gB+V1C4a+fG6`J&s5_z=$OhW!X8I3B~=9Bh;
+%EO;4v;72HSc07^ewL5!yE*KIWUhEe}di6tnYC&}*mg|OyJVPH2DKk^S`;zIAyH{oYBWzD4B^rT}m}H
+aeBs<DB4N{vH!<SXIkuox{cG>;_pY*~qYORK~n>=Ov16<Gt%hDT0Y9T=_i^Wt&><jbPuJsoTH)8+oEb
+Pz{d|$1E3nDLthYfK<FXFFY^1nj=p%9m5S?)NyPH#C0n)!7FY9TrtpN`R2M2<)J7y8N8j7{(@bb*Poi
+@LhuC6kJ+lw%jGmVx?u)3Cd|y(Z^oTUABlK-j_c1{s*%R(a;U*#$5kc%y+LI2^6W+ZLvhW)-Q;Rv(;I
+nq}zNso_}TDY`+$I=~?41Ysq=Q#7Y-E!YcN=s$PuKY{9o;6$T&wJNeQY5_ZFmX`OdS@JpT&$(n^TYL<
+-Th<E>xpDhpXzrm|cspLWbL=*|p*Fjb-~jam85)($)6?T!5_iDmKT(TF?PrB>w8sKQl4y&rP<$^8Ch^
+Sb)@i*|=Fy&KPet01mDsI9rj=?h%&R@i(h_en%i7ut8DG(jvbgqa>r1@51`8}93vACZ$Hd!Im}Gm#IJ
+2K8!rs~o8f?+RSZR9!JJxz{;P|$jXj@2oZm(<FMRQ}%?FHO1Q6??CWL47$uEi(XRLbg0GBi#0dhEbxD
+J>t}tYiL(R?^#TT7_)Fs6m@3@!AGWr4HPsFdg^$+zh5AR~F@7(9CpO+l{(2wkPXR%^{f86AfiWcE@g3
+Ey%RAw_Vuk)gxue#l1b4WpAQECXLBG-{>_fM|PXDbkd65`Uv6ygwcz`=Le@V+hO9_t-j~R<pVG*r7Sx
+M!cmK5+S$$Y+(+oMJ~_2s+3ptCk1^!%wB41q8*>w1$EI$s4o`KU*dCJ}=TR@)2NBYq2it`})H;3l_SM
+WLnLRqX8D_6$uV+W6C+ZN0Es2Ql-yI!3fAhiyVcb%#->Jvpg+N{(o}7pakm>@5kMZHl!&kGDgO?p=LU
+i_*gUO%M$={w%emgz?!uSmE#*S{@;NaHgFg_S7jd_gb5?y5C<9#wJjir1nPCit8P`K1Ns2X%F1#N}yq
+V8eDNqKnbfFlhCOxLkGLQ|$*O<=a^Qd|Trv~iug{cQ!~B`8@J6^uxLQ4vw*0BRcb3e?I9RhM#-7R&3j
+TrGGFng{fXrjZ3_ex2f_Cti`_WnRtU=$r!{LK9<nod`NqG>N8+9b{RK^G)%335K!2G)w4(H)*!{DX(V
+P1&5X4&cKBAKGO?0ZB`dCj54M-#<J25l2B7cY>9LnF=U)+G#WX2%HwA8xQW<f<}S6le0{~Q()qp8Ph!
+DeX4UcXzn~_@PN~oeuX(}KhRem)m-AY$SNHL>JgBF}aK=}YjQlM;8%-H1-}6+creMLfY1!r=tc7Y#xY
+0To%O3Rjw7y#5Wq!kASk2P-B_`|%sPETL#?&2uC4Jexxh=SAiNC`{pRlVORC)1kQsg&WZZ9#g?h5KOn
+wc=H0<cJzOIX||$kZup2M`EWchySVkIP1yE$AVww4$+8&QIUGc*C-4P7|bQ$YJO@7Re|*GU#_oS4E3<
+_m*))C3%^*NfI}_xEvD+_R(=OW^%gnF9}rq<vE!oe*2LeBXV6tX9!k9)WFU`{H(wNBS#VYJ=^W*=wyS
+P-Y>bdyBsJqH8oY-hy265ST(RTmX`r1HBF=w4pi-r?i;P4(ABZ=f0?erp~uU4PVW&@w}o7oeMtd(*2G
+TI;e;LC*+~S_EXH;QWpWd!Ij-+bsK>H>2a2N3Fcj%>mW@Y#1JDtZu5}WR3=0L{V^50-?1eb<?V59xR*
+uKj?qH3TP-KO8E?HJ{l@wLzj<mg~;`Nj|#vs}D*yHAh(iU#i!yW`rJ#=;IZ9x*&Gw=vUXc`LvZ1ryf{
+e9GIFpUJGTG`MtUJPMF#7N}vAm|2y2M8%n?$|vZ+WOvQ0)Xx=kAY`)1E;VM21*L;#n47o?|f<~Z`&Gj
+#^S&)-CbMNp0w$kW4Eu3gwr~R1-dOH0@jU8&~2c_F#brw<3HwD#j`d_qS3Ud?sy%Kx^uN$0Gfl;L2i(
+i(ljBKSdX=(r{giF9y_{ba6XpUQyPGB<6GDW*J@IL=9yp^P5wcm)Wg?ytc%b<e!!bMpTB_sY1`I)?BO
+hoCR=bK*#FGsT)~_Ntx!F=$D-Tv9DS=}nB?QFZGoY40DuLz1U9;lNinuxjv4NH=aDr@Y>hES7W>helU
+}+SGyZ&n){H-gmm1`QwPt(Of*L;&R`{FF$AZk-L_9Zhv1Xh%j+E}KDGY2~OqnGnod+Y!j2;E*ijC%`a
+?aYSoh|L)xPbkY4`KSL<C)E9#-rojtUew5opnz$p~E>5cMYRxO0kNhS2!zoxda47yy+C!vy2sRkU?l8
+av3+bFXP|_4uh^<u{3QjbFyOG_Ls})yr>#(>jS2{eL#Q0M1@}LOm)#*!Cbu%TB!hq6~7*R13;Z=uj@p
+)f6mQ@+GgKg^OB|m#G~L=;b=Gay6Cf(wg^!J#IC$&95_Tszr#lI@(U30uWh}XQm3cOV1DmyiJ7={ARb
+==K~Z!!USmmlq6i*F7_upDyhmPJ4+GoNK_;~T)~j-QgPyf3KrN=fr_B;Hoj4kUJxavXcgv#rxd%$?PK
+1>_TpAZ|4&pULB59~2QNlElOhTajq0QB|nX4fI`^COFM8hNq;@)B5b-K6EYF&{6^7$rTCM$7K7&>0Qr
+SN2D`>7D+N`u*0#N2&?h*4@h$ru;mPmnkkG{+_Y>G2R^m`Gwu4`4(X@(1*ZqqS>97&}tI55f_rsV}2C
+u@id7XtN`*M}aBr`8rz?G;Aw61A{fr^xxgV@MpaU8qh2?bsUS$UKh#ru-T`7t@1LKTi2I4!+59&f+2C
+J8)MLr?Im3!o5U|ht0(={1Zw|1@{u)iO^K{w32w<x9~lG8rBaV@boYth;uBY>tFTLlf!JvkY@Ay9h@L
+#Txl8M-#@$~hybYi7xaH85Rx-++!8`Q#1nxbtt$<}IxL<NO&GL$(7|3t-9$jeCfAM|~6=2n0+?>CEk6
+{7t-{bJtRc72)(CD6gMnzSjE(~T(O=G@)Pn!|CokKU&Mw=$TDmlI((DKz(gPMUn8LPLlk2RqRaC#q`S
+gH{=kuUAJeU0%~lpm5+cmWRLXMXKEB9Ut-<sogwu@2*4-pRdOW|)r#mw}lA6Pw!WU|yoph3AfimZzb}
+@F)AKTFa6iK0=TIH5tRYTqcr3;Latox$w5dL!=}fsW4|TP3cD9u0vo6OXRw}VDZtMj0wz`?wy;cGee5
+-!U<BNDK?1%B2bV?a{1ZKx$R)<Dis70CaR(KgS&MJAvnDsn%oIq@LeBR>69DvJpn^J2YS{O>9c4#w;s
+DqA7Xde6N=-o^Fg~MP!8*hw-IudGbs@uBE0)^sT+=2jHl(8k-j(@`7G=Au=CskbW=*++3gftE@LTIwZ
+ydaQCI&9tCSkvxyz7o$V1be^FLwM_{#(5Wv{Zx)|=+dtVD5^y{zh3CGLHDaC~%l^i^kgbsKN-l5#4zh
+nM!Mn}osGa<_Xrk0!8pb#4^$L6gK?_pn-kH-CJ7BP1~0*@?pp4-)cO1bazAClCi>CDb$_0nh9BR`J;H
+qS4vYkQ%0bB)LHrltAIQw;ldOkLx}5xK?u(9u=W4!sAgOI^O=VmkA_C_kvN-;^pCYuV*6HujbgiQ5Q{
+Paz7!FC?)t09S*aXs~e!O;cE5J6r>XHKHiQeBDy_rwDnN4LonrpQrB-AlP*j_u`hip28r?W&_dZE|B^
+Hax^|tA2T#oDV`Er-SE5QBQvr{4R}XO<MdUeFdcWhmb<R@hmfu$a7Vfe7)#sk1-C0Wu$HvqnkGk81wN
+qI6#8HjZ*zWa$&z{nyDVsfX(cGo5DI6L^kX$`<H%6anx8Vr3qb9tE{A&>Lv~wklKhmrkI4U~Jw%*E<W
+2rO<0PCBT;e#1}w%I9&+Ncpl5KNrWx?yk}IB;lZp6#5Ef=dtWw|W+3!1mvKT^_8w*xX`iXIq{3fsyrY
+rbArrXB=(bhP*=0>8Q^yT`yUVF&zsSHZ9vJ8k_<EKBs#OQl-U>Q+{bSe8o`7LGAY&6>$VQgNz2u9_&!
+sX5W4H9c!*JT3YKQ+yv3Y5^eHY-C<aFmBKiNO0wv|m`Twu_<Zarc*ss@MhU-BH#Kv#hiK@E3K#b(JHE
+TZ1j3SF{*p>$6klQo_k@r%rg#t07M-hS=PoF5GZ3j3Ua<$f_Qy+qigzMcifr0pd?)bVsVxB`jQZ#k${
+_W1_0J`S)If25nk6Yn3i+YrTsUwAN64ZkPo49m&(%oYYBvXuh#_{9$1c|KxlU1cybi$io~qUaRg`u5_
+|KwMJ85u%7nC#SyK$Q_xgx-HE=1y89D8=hI6T+UMw(OH)r&XkwN4#=;Mkuvw2BuDtg3c@hBO)caZZmb
+b3H;)Vj)Hd-@@P@W|;={V8zyI*G1pf6>Kp97jY)VSj0m*K2eLkhqnqce_NruQXM?#q?e5c0s!Q#Ose7
+qEsx<#;r#}=+?cv0LYV}q2V!8=h+B<SnZatztC~+J7-gQqi>AFVtY9@@6slZv$*%90aE475`3HbPP2+
+sZr%Wek*p0Q+8-+cM8=Yn{@GRM(oG;g|js3KkaUd<G!`iV(kWm!{ytfvpUI>i|t5y-#UK^9N;?xlE+N
+GU}0(~tKT{yr=Wbs-TrT~eqw9;*3r{i=(!@F&7tWpR%4BczBv;yUg@2=Zw?j!_Q=>a~5c1ldQ2!hqxK
+11-pbHX@vrm__TN6FHfr+iBi&kbw)F(s6A?|w^5Awkyw%7bOc%WZVn`U&s&V{qQ}_JN?7o2KJsLIebS
+?4|1js)7vn<5oF{KwXkhUpGO?PfQ8*=_Eh-i6MO>>GYm%&5=<1MNi3&b*;_L^isn*J>3&(Eqm_vt2Up
+(q7n?=;h<vYBti9_=YxD5>d<eKcEOqJ%aw4kuhbmuLC!m-=9>Mp)4P9mdiKxKN;}diNQ2z(R%aUITQ~
+pd^SEN?AI24%2`}aNfyPFENwZ8P3Q{hG)MV^OZqVif*H6VPMe=$Rvp}?!^8{47Cyes5D9>39+jxZ4`e
+mndRxtl8pb*+O^kExK5Tvl^=VlF@b>#9n`(ocAlgbKzZ4x!)6H4ZwENCP*-4s6oU9(1igQZH+b!8{QU
+alEAz8?}~;zZ)&fHDlXQ;0;~2NQ>dZcFcpW=?8}xE|@j^Gsi~_kg!NHsa#52adSK*3d@l!6m@P%fK{M
+==1L%H;n%G-=y)2O_h*hlO$7Bh*FF>C6Dkr8;M$4IaIG_rw1<%P7f$S>*(M$CgVlRdp!B3SOqllN;P^
+JR`l<w?VED)tKd~m6+G>-sbCQ_l*k|d&&iu3A(M1>s$r<p=E=E5l~Uw`F{}c+tJ5W#B5w8<!BKuZfRo
+R!VF#BPrqrMT0t+w^wp6G7>QqmLe$iA>7U=7P)8~Jh9d|Mn|MEljMf}4Q{yTc|OI2b4iP+cv?CIpU2a
+`Xa$MTnCIyrx$9*+L-%Ls+o^iV?b2M-h~#fB&EUcWv#{=R3_=}$Z3-KWp~^P|GBJ3Ce1LcINIb~HOaI
+DLa)qD++HWbxx>ge+(mb``J=j$UBn?~|`*-=F9j0{Ek91H{$|WYq?69-Cx-uqkLsqp^JdQD$Qvy_kKc
+1`%eJz7LXC2$N8lWpOZJ&#)-4F&tZmDbs@~>%uZLjVaNDAvqR=B=d{e+ZiBz^!$5+wx~#p8J;@Rowb;
+3r2o2D<wa)x42>p-<hQ1o(kF<o&0{-Hd;pXzy#N|^d7IZ&Nk@a4#>w|brw899-yFO;q?PV0a-dj5+_!
+zku;})xD+aN*%-H6D9?tQ>hPzl?)!8DUE|Srbq$w((MM4Xfs8%dWRK-}~Vv5}3>fb(fQ6|xbG7l4J4R
+4;4xGk$NFSCo^;eW_Ag9V&<?0AXS<_&r=y#^`r|6x4^2}I=;j#(&l!<&q3?KG0PXX`4s^<kt**vVN|Y
+|%k*e-KYKy?7`REVM9^w8#fOq9S7%!>U!*#sx-2HLOqj6a&co(LhXrWVEpJ0(;7&$UuL*OqN$&;mgu(
+b_m-p2SPL|>AkEl#kye=^gCW){h*Bd(J;<J{}h~9NU%}2u5b&hT+tN=!#r)NRt)vi=c?b~V~1=ZV^)_
+k($|!ToK-Tt$D=GKiT_M9j&bv`7Z|U8f}6>s7pjF*JnR}m!3Se{cw5i_>feQOlYN7FTF-XEpKV}@mhY
+|Ij4Z1-Nsk^Vc?s$!Xp|HPYzAd<j#t^KU){b3j?gycRj9aG3j($r)Gh#2p{?<;>Vg%Xtf>yHO_!=^--
+Mdgd`=f|DBHo1rDP!{mjYEHj_}9$>Mkw&poq}2iqZ_zD_FyUW$u$}>hM$L{sq5mc8}8;XILO=R4k!Lj
+ev*?P*!D4NsL4_9(l|6Zn?2-546%Blx2$L8t4KGRReLN({+}4i8mwWCH^J5;syXUOd?|GPqr#Z>TkEE
+Oy|NZaur1C5-um(?Px2E&`nRqYT~G(J#8Efv!{(va|t)EL`@ehQJ%jK65T3Md6M3tNOe_jk>u7}Tz}X
+Gy|0a4Pslnogt~|})vBIzS-&anR!S!It3#%)UK}blZT0)86w1Pyslhvtr%ZXoTJ6N}b|WhV>+6a;)1F
+R|wMDEWLGbJP-WFf`B{iidk!P+1FK$qIy`j%*i1*|#>7M<zYzdx+#ZA9zfIq;WBF2nvVo964v7-6$oE
+4AaheSDPs5X9dJ6zmslI^E1l|KO~w^C_rxO;5&!PxAmv9o>Fid|<n^u{STlP9g>%FW++04cXcQN^^N>
+s=W)HhasS{pPnlA|BJPV0&0)q}E_5I)pVNe;TG}S`$!@@CS9${+kB5K<HIAl4p?a8r4i7AnwMd*2?x!
+RsE2Xtp~N`+2FXDH)IC;J1_d?z1jSGW;MheW*$RVIAN3(&;Jup&M#FD{056;v;I*u^gwW_wWWnD?nXY
+zuEUa;N>)2?`{if92<t%UF3G_5_W~b9TkFu$#(-Tz#L!t=y`5s+<}9H>J$F<muLCH<>vF6d8KPo$xM%
+RxiKWW|M3EgmrdG&+4MfagRSq%!l)}H_9tM)O+@eRuI&pu_myse76OU<qr3{JJkwLxgy}q9a%?bdh3W
+?AagIJ1B(9r!KWQuMnBB!`YFzK&jI0{|~?Z$s?{U-TG>o&wBU;*pz(Q&~egRZBu;^y2FLip1aL_e=?u
+}-rv(nMVdhcN6Zov^V6(K+vd8>OGsIrhiwr;ojw*9HfBcIKVo;R!ip-|X-fC!mdZ=j;w6FVO0xY(I4e
+$23gwz%e&D^z^PAkQoE#ltooj>N&Ki503iBAEm*|(0`Gy*{3B8Wbp-HQK)bj*06#UNcfZp42v2^Iy`X
+m^=(^f_(yfD=mQ{F$th5)tGDWtxZ!rLu*Vb`J?a}$U5j;xdboCU)`=WDAK<=_mgH<86Z~^#<#D5;5<P
+D8U2ET*-7tE!i^QNIL{uzsGpM5dQDKFswlcHbqoIFZ50Y|_f&szMrKH7ShHBxoxSoKZwi%00gEsOCj+
+eJx4bzSj{u1MiMUe*dexmQxEv+WXM9{Y`FwQ}KVpMg<&%C5<-dx_3_Z1X1@%aKcpsc%9QH}LOYdpbw%
+d}1x0^vP&K)xpg-&BB04(IFyUA3UFILP!!1K0!C7#*S7w)hDkdGm}|S(tbjh-NkldIA_w0EENZwHI6^
+w(zF4i#sSN<t>2dnY{HeA&%=Pzrq_@jm`EcQEij`*b$DBB>UF4Vq^IXP&|{><)-41A2*kYZHFrsI$DG
+iG*bIB6JL|D2w`6Jc}G?yFp8?$96n%5Fh?>J{LWXE`e`wMVE^GlS1p2E6T?&!V5{QZURv|A-$OEt@G#
+OYfK0n=@KbHVICC~*XTGxM`e{<L3=3bUn7N>L`B>&NSu^g1B^lgcX{~#|yF!0NYuI05XrRA{9E*M=--
+7Dv;Oyl=L27Uog9@wt`{zF2OUow~HxXzFWc(KY!P|q7dXI=kdyktvJk>BRFMu95>la2*yi*@l>ha2KG
+yJyld{tuv1DFIwF{Tg7<gL1x19C0$603BB<~QdO=^kKMyXS&a;XNERGq-UAoJJBM;97j#m|GJ;2lx2z
+Uk{aj<Q{SE;U+oslMQz~7Lj$<>Iklah4&IDo$C7ss#tbzES63bt^De=hKju?SSRAckXUO%hmI1$%Kg~
+5d#jDVcS66EerluIRkOTlJW$p8l-XD;Dl2Jq3v7jN@OA8wzQlKslrII_d-q@?Oaur<QiLY7_G5`&DzS
+Uc>i~DlAE;c3ZtP;XsF7=`%9#bvdsP>Iy`RuR*7@L|O+xWHm{%+_fvr?<MEqw;Dt725i$1CK3=$~puc
+KNmPU5W$!_IZVwoMs3lp*f;twk2Sumd}5Vx=dj-u`lQYmX@6?CDhi(wgCk&}#*)hxL%VVv}GQFO#{G$
+oWa4S5i=daY;C!N3RoqCRVqvl!JRSV4nb;xui`4(%y|TQ8~5N+2En*26cU6FxN&wTqby{SY%z^v?)t%
+wkdDMFs5AvS66~t7o%cjgl0oumS*nfzyhKTM+3Bj0_~>bmF#g!mN%^9ynKKeLY;5CS7I5@O7d0%erGp
+EqTk$!XX1FVzMX9=Q&h?h@jr{APIU(A@06LRv5cb-$Go{S<B5=+IFR({#O?%hvO*s<8+$Q4BUkQLAU?
+~R@OO0aTbgRsXd@tXs+)74ybcY|ezL!6Ci{H!iQqU{h?CV5vm-@7y_5TD(i7e1fers^_SM02jKhBWR(
+uf3PZ+R2CP>eFKPX6Vdq0IoZ+kxzNN?AEDv(;aVM6{l^*&m%tgU(^ne?U2d4qBGZH;=K{CCuqv&w?#`
+ht-=gry`p25c=9%_yQB<JfOj-dc?YZPQu?ym8wq^jaZiRDpt;_xc=AY~$EUgcE*4*XBTpX_{qm{V7gy
+ZG=btm!tXRIwV|3lEC}y(c|V3DqQqn5PW&N#+={G>UZ%#FENo(R)c_1)R()5!y(oOS~vp%+zpl|`kho
+d<62|USI6nc)~ed#obTdeRnF-y_L6_TcOp&AQ02nC=W`@$i3L<|v;pACqFw_)$l@||OiH;~k&uNJC~g
+h1nEl(`@-(oM|MP`F#)+%6?BLx#Ae~Gh3C51{^a}1wLmn0-`#X`EbOO+a3FDv}RCViSnCf2jj^8*Gsc
+LuGHC;ps>YgmYE6a~GE38E<K4%6RXk!f!R4whI^u1X*g2zPdlTML#QrrfzaNOjZUpQanCbY=hGEv=eH
+2VN}7x(I`aP(y^WO@5LTbyS+>n7j?@;xw4AfV!}z4Q~$6Hi@r8iwVB-_;y=p7|9{G0`C8hp+-43J4eB
+-vBG8n!iev+5mL$3VE1_h5}&+RzJ2q0!ohBn=nHe0NHA>j58|>4D_8<&6Bj5=Q*+K7=#tN;P-cjj8xo
+k6xFit8_r>@DZR%2?t=Br1(i`UkSOG0|H*iVaM!#2lj;Jjzqy=xa2<$47PBqqd+&oCGs1TYr^Q`*?|t
+$Qe@GpKBZ2(!Hk6(VsleTn=q|3Noa-QZ5nf7-5VZ+5xu|)1)8%a0uQ#qq!~Mzm2D44<PO4Ow#?!n+bs
+~N`mcTY)>>96fg|;m^IoBWT@AM|T;x6WPKkH0`?oJq7IOX3i#ahY|jZs`yqE4;RU(GjF?U(>+4ULth)
+IAqXB>@UA7%nHURyo8Ut+Uu}6<y+8MJoI+P)h>@6aWAK2mtey7DsQxo_Tr;004I>001BW003}la4%nW
+Wo~3|axY|Qb98KJVlQ%Kb8mHWV`XzLaCxm-dym_=5&z$xg3aP!`RY|ScP&sDU9?G>92Q8ELmmZM!%%3
+8wwaYBL8N!rIox;e%<v&nlD*!$8bMZ%nc;ArKa#p&m)~EqRh7$fGh=(bzWggb8C{Gn*sH2OG;*`mEO|
+9yKU_Wkk^Nhi{J=i)ud+^o;;meXQi+`Dis`LjKOgkAD%r<st?zjw*qdr!=3Gm71{b1X9EK`&BbR$!!Q
+G}2Vkb(CvOWsI-rl@={qE!IR6l6O%bdM>|L)U=o0p$Iz5nnL9|GNI-Bdf4W$V4(HzLcJ+|^a1LB51ph
+^&xWG`vuwk$th0O02ZIla;$Fs?7!_?QSdOuIk&(UdzJWs%><4@O1i63xD%X*5+zt6il67|CCA>a{2mU
+C2BN;eT_05$%?PGBG2lkszsv@Q??O0qlej&D=}p?*V{~2_E|2QDSL&NqY+L5%zEu+lLG5odX{8a$#-C
+&$!IhpMxc6Em4d<Vf~^|CH8}2mD@s-hk&7JZ3a%9UAXK$)R)WSz=>N%VgnYSJgXv`{b(SSc6zeHN4X;
+tICBdIu(_<zTjo7|0V}FKMB}B!YU<zWP++g_dyGrK3q4q*Ch?gJIhZ$4C$J7PH-=DA2NE8in4X#VkLh
+08^mPu^8WHQAul5u0_bV={VlZh9k_F(lSnltdg+$O1?DvAO-Iul5hDa{LEP2gl3M+J6x_UxIzzEPAR#
+uLQN){p}DYy*}ky_CO?JkPA=4d3}UGxiCnXXK>W7pL(TwiIYX6Gds48C5cjc*UwU4e3NOS+;5gyp4eD
+DuJ)r3v_3zI{1{dPX}jtVFDAx-tj`()CU&Mw!Fz1g!r-$J2ty)?Qe#nky;Cb7dys1V%DDoRElsqwi1!
+D(-WqAF<?kp!qm#updqXQF{jQnVyI@_h_!q$0Rj>0IMcPnvY56(E6a72_&J9eWkd|;6fo=y4T4d=e<K
+QYj4nzUG<0ez4PBa%u;4(cNzC2E26rS5UXwjqdG<+S28yX+4rQf1sZ=Q4GE8DU@dtD}ek*`Ld8$+L!~
+;}X<wowyGdXu{0xf*eH&aMX7V!z+N%gKiMAVmc!R{MqOB5u_gT?~aiJ&lQp_Z>Gae8v@C1!9fN;}Ib8
+{aR-6UG%=_eaIwI;D}l-YE^=OFkp6*8pB(1+A7}p?@B4049W|tm8(qn|TLTG#Bwp8#?%>H*ylHV5Pcz
+dch2BBOo*tpa-me9Lh|2S@YE`--w_htNm(=_TdbQ5eGe>azQdf7D+|YFoepg+3}({*aDy>wZ`Rv?G<Q
+`umbwrUb6;#4iDf3C*E!ac|)`O0!>xIs5@SQE-G?#8qEa43gHl!B^l`rz!uTQ%uARIpr~Ad20I8hxs(
+8I4sDU-7?se+cjSo^1u_aXGKS$x+xlotLgwJiK@p5gOhP6h&z5(x+T(P%#(NVwD6ACsR$yCtAds1syC
+db$hFuUNR06}<7}fsK!A5@W=*SFnusionQ7w64GAS&dzI*knr$a|E?&&3oDetZ`J*FTfT35Lv^A2bqk
+*ef3ai-9gv=8iD&<&+oCnyO4oDq;mRd#`Qt}#0FJ>^Z3#bwFGmZP!th7cA;3c_|R+V-#7JRI5h0E)CO
+xCWM83RR%d3d?OW{>tz8I4o?pvaVsG;z7gO2g<YpD<$K!KCH%2kl}Ade@W}HiK4j0oFgGXo1J>QtMYx
+(tF-1R$T1i3%cA0@41;~S1~_bpV5}@N@3IU=CKMlYb`Z8}?*nouAyLX0!F8)V&%f@KZd0`fl5H5cA)o
+>jL~I&tD+udQD8~6R>olGVfZDK#U>AS6n$gMzix_o^1{NH+Xv6Mnz;NkS7qHq01QZfVo>M5JWD<2)Or
+&y9F^SZenzWzHj2yMC_C*fEKx^4@@OjBPV;Mu&VB+QYPvaBF%@Bn&HHK(_B}su))sT{4p?VS%rl2sSW
+pXub9UE4-Azb)9arWvU@kf4jXs>k^=HFTFwy3S+><%72x)QL$FmT)VB)F9osB$c1EGjn>YvU{?5B-l_
+(omE1&J-hyqHZVrij{{1LD_}uf|6D>harKH(0(Bwn&25Iw<qy&L53x@Hj5S<9W)twGidFnJn6t~>qsa
+l&2rp{jkaM93X5=ZPt*Whwxh4H`s7JxFyBZ|DsJN$7>GU^xFay`aD!3_-W5f4FY;vF3oRNkS+hS}W95
+amL8+3BY0nH|^T<*_l)33?6mk77G#N;Boy^y!jp8a7W?4YK;=(PSAjWa4?;_qSSsjVeBUm6#g&{*m6q
+vZsjpS-4pp@rHw7v`#!F@qsc%&A1oP6CPlM|Jo-2*V#xsNV&l~*8a?kE@0TUaU-f@lVl)2V_4X<gOH)
+ucyukOG>FBksKpluScs3@;eng5I;x@@z1kASj4fwtH8p5A9*;Rv$DJ4Ro9ef3U8|!bRdSy5Z7xL=v=6
+)7|Ec>Dc)HqHk6ATY-b}C>Wfu<2<<BK(;gN?SAB7f(Us+yvL<KK3x+LB*w$%|H`_9pyS&!khFI$x;*_
+RYr@tlUelh1SZT|(r%`X))iQcM-oTvhd}<o#n%bu^aJ73_1wgKA_MH8Z!Bt`@TY(pV|9(EOyb`J$Yrt
+qAoHN3*u&@P$i+Ig%?W2ZRVGq?F7K6wD^^t}B9UQ;qilA|zA>v~aIi7Suoq-TIT0AH7@FX4#S-pyB2A
+6GZG$3kj=e>wcgf@S`adBz}B3X&Q1;59j>jVG{fWy4ws@Wp6BoWFooazyb6>aGaVl0w{zN29;pD(a?L
+Z<+r+)t%ILkJv06$?bXPRB~A`IEE;9oiYx&EW*}YI)~!5iULZf&`r+u^S|Q-89ueQSoAFH>nF+&D)sE
+oFS#TSnfB;m^{bU9L$~1*mn*58=E;zqX@Oz!Y9l=B=^2*Zl5Oi1g1P^g<yBhPgjiYt~>>aD;idzAU0j
+Z2YdQYf~O(au^J&4Zcv`Yjb~zLS*0&gft&XxEL)8{&yx`DpmmOCZUp5hYv;(ZnE$wVG7Ns=&`(Kplik
+<d^sY_EAje1P7o@8W_6DC)3Jlhh>~AE#rJMB1m0w>TEerj`UqW!)#yz52T;_FbuXbK3&!Jq~-2-EiDS
+JMdUo9pK+azsa$lXrlxHoQ~MI+)93W?@?{#fG1=>GrEA%2ImpW?h;4S#C=ex!CEsoNbZW(JNy?rdNkM
+vI?ch!*#?5_1gS*(sgU`xYStaAwv=g`;0bBR^I9eL}Wws&Asq%xxi;W)ap=W!@u8KU%?scuQq&5o|QP
+X-|aCkc2V}$AAoL+2n@E$+lDWLYvP_1l`-{ssRZDA~xI7I2ep-22UJRv%26ekmKs<N5(b`lZ8LsGCZx
+1+_AzhzuF<~NcNDb2e`@6!C7{Iyg9Q>25bAHeR^gNr^AEknAja*_fNdtd6nDD^j{=I0{m&!+C4{97R_
+?jFO$*xxn(rj8$*RaES5b4d<SLsvpaw<vk?JH17}44-fw^h-0`qMX&KnRrf+QeyWk#&4j#L6s2aU5e3
+J2h%mM+Yv!U-3B3JrBPp;wYZ%mapih*&f-EZ3152H13l#6aU;R+)>GDgJwB%^#T`I5Fe%=vZ{-+%?#c
+dX}AA57t>y;b3Gq#4+NMbk)%&$+XFzuO%$A2jqWSNboy{e*L;kA^Z!meG+$ki!`$owcVFPt0V5yyl45
+9uWAGZ2Ns2?HAa?*}>0veotmQZl`X4r}_edw<ur%TpmpDZ>#E7(PwFjx!z$uwP}x8?N@VzHXTjS0ez&
+>IUDAhBg)!y)t20UIB;fk`pBI13qs`U&g;}2Ty@Vr?0)gMTVT>QvFNKi9)8x)iPtDn;|Tm8P)h>@6aW
+AK2mtey7Dtv^Z6!Pr003t<0015U003}la4%nWWo~3|axY|Qb98KJVlQ)Ja%pgMb1rastvhRT+c=Wn^(
+zqjTqGwFoyYFfoNJUhPsXm&mM3w=&TRIQ91Ef#3o%7<0noOl_Q!8G0FnS7vS)9YR3Z_LMx)=|fDXy*y
+BSG~70+)@NTt@ZAMnlOaB@iA7UfRxn~fsj+lah+@$&Ty{`o8UjpuYnuIM*j#sGG~Q<h7%BB~&2!^q`M
+ZHk;+6>D`*1taH0m9MDc@D4sOK`5-3E5VnQD&X6VU~J2Bg}AO5BNvOeXYa1gV)dX1%~#~@`*+tL7C(Q
+ye*fVLUjp1@Es8DS1>xJW5Q_Y~Q%o-2PwdyMxVeG-)L#o(e-f<zB$cRA)qLB@iNP7C#kQo1v;#ovn%_
+*xiYb<=gyn`=Q}VyOJm(p+^8}McB~m8qscVj#wWF2dSv?TqtoJ1o6t+JlHw=(I;0WMWVmC$Z+%fnHbZ
+n**(tpepmR5=`Gd3kWPwrTL7fmL(bKvm2W<d;~7y4b8Bstx(Bmt=8m$UQJj~Ca;ucz-8=Vw>fFd+zn$
+&WuhY%>CDOAyxl;AQ;cfYke)AAGz%pZ#zEoCBfItjO8?V8`UaPj4naQYqPXne7P7&*l7}68VWtH*8Dg
+Y|B$oNKl3uu=a$?ZG89gfNW{b*G#J4+{NBZNP}vz0&Z}%^T@$J2NP0?0vIg|aVpXcN3m1_sQL86_b`a
+NoF5!X_^5Uze7xuR>sJTIZzh2K*=$A(=Jif1D=o{6r$##^a8%%ug8j4NLMx?=$!vynfxbw!$k)Xa_xM
+`xrbdaM5i9JNtu6(>0~NSo(tCZTB?^RGusfEK4F8=UP`SwOid!ZQNX1W6-1q!oO*6@GOXSDn5wyqd(#
+H+;Agtq>#cx37F&F|bLr{uC0-H&1318P1%q2-_mVsr%7i89&3klZ6b4KpD+Q2&i0o?`NM-ffV7ay*!l
+Z(Z>v*g1W=#*gEoPels8T{Yjv-rDGW4{0C^NZPk$6vn-Ka(%&Yy4gG(|-lg<m&B*#pQK!efI0+#p(5#
+QOd&+2L)u-8cpT^nIu~Zc8~-o+G1M!oubw2cPj>S&`z^E7H*4Gg|@S<@-#YWtSm{)%p|c8Q8NVJfh52
+fn-Tfh{QtFqD5+2qU=U|4L9tXA#6gPz)~+%I0AQ6=DS=B&&8)aA%J4<h!mhzJmSEXt_{4JqJv`<GpmN
+s&qsT3&**`d!77y-&ikrL7FTNT{$!ZyjAe_k*6heK8%iaCIzQo{WP&iW%#nhb>O<GD}w}7JIY;aVwPU
+5_yP_tONd`K;m!iXG_ci>83qlyVoB+z3*C3&G?*+a@oMb7kpXrVB|P6*5?a&R`XDp6FR7`4W}nUh!X_
+ud|*0xvA$doTqg?MQz4k_Y7Ih_DVlFDLaB_5gwtX*5BfIGLmw7!0}6Z26UfC5FZF8~7ZnH3tU=`u|g6
+03iyd5I70QFBOa`Mhr(K(V@UZiRK+bfsLUbH|v)y<!kGaWbCbA<|K-3%M9_(NylO)mTPrn00_-oU>K^
+C+^{9hZ=_=l3C}s|x@6gUYH3SW9Kw<UK|`3)pXq9qWEAAJ0be}778M%^ODnNUM3v9a(Kmrzfz=n~9M^
+Zu3)hL;Wze3QFFsJaJ^-~}d?1f7fb+ilpum6>8KTMahtjNAE_KxO2M@_Y8BN=Q*d+i%nuGF{OrbYmWX
+Vy4Ik|j)wfGcwb_oP)pg9Q(8KbMxj;bsqe+VL!y!ZYay`uppJVOzG{{v`}E`FbwJ1RKMwQ~Zx!Vs-F_
+!R#mh&q6YksI2l`2b|P`ATTT963saqM;esZw^}NBqmm?+)Sm=b}Su@06%VDVy+SfHx&okv>+m#{<%EL
+Rp5KVRSue#1GNB!>5}!j?UIoPu0hv1Bx_(nDfj~ukB}KQG6ntw&z9;nz7;$V&B{H0+fFbHg6*M8L4yS
+i+=4qocaG6BG!l=rKPv^j+0qk|qw|1_r$MV3%w+<$n`J$QMm{%cM^Qh&fIAgi4A(#g25{?Lz{ffpgBV
+V&Z}Db_n59EZ#o@zeI_yV#m-FIF{pduYC|T|b1s(ai>;pTDT$dmvx(Z)KTJnd_f{tKxXb9h5oe6<y^y
+0n#5cQFooH&MBB!^HD2IrJ#rdmO=$Pq@VF@<pmGQPnV%$h$W-?ZZFQ~M50s1n-L8nKRo;&Vv;UWi*Nu
+!_=d{BKQmmKTcTjIB(DcER)N0ksVRgp7N5Y(|7iH|Y<q^+23Ke2A|!3N*({0W1<~7Y5b}10xN+RIpcm
+jv;t*%V4Q?EY6C12&hra@>OjWdV8371cTi82tN2;K;8}>8-QGvz~8?^)m7ZfF(HmnWk-YKN{Ztp&yS&
++Ld76QJ%esMo{grpw41UJ{I5z(Ix}CQpSA9-8wy$e&#KT-90RKB54Co*>z<5zqz<GH*x=WiYq<v*N(I
+hTXYThr%g7SGKy=F?4S`y{Shw_{q)@|}8aM`pHEuFlRy<pU(H_$L8a0f3ytZQ9i@|fZEfI(q--m(Cp@
+L|zNNyhM+Ko7UJmzBp(J>6pKApMIkO9x0N?dRG@l}3ay!&PGVSwa@N<(Kjj^eko4_Ax#?;hLsUNX#PB
+UkIJnm(p&<I#R7&QCnO{{IBR5o(4#@Q^#lLPKn6#=#a+Dvjglx{E;>=P(avjG$eZ*jh<&NGra|17+jv
+4)PZ-`{9r_(jA&y&ajZDVh3T!K?P|)AVI<$LXZY&;;1Ysc9=pmOUQDI?bh>po;+*&#~dnvKis8dkCX%
+Kopd~(vsv&Tj2!;;<q268xYY_dDYg599ZH@<mVM7irgx%x8`LcZwV}%7BgThfEL$<aph>7CR&=`3kB0
+^0#1FG@7-;ao6${Uen2R-8h$1%?BDm)aBp~V9FoxwC<yt^&lXXGqaxrw3R>!_H<AGu7?9<7)o)~C9iM
+4v``#lCX;8m85Pw6;AXM{YTlVw2K1>2<EauCNMM<UN%PCfT=00`8+4vTEnTvCC$Yb+A;B22uRoY2Zcc
+mF`?6wh2HD|GZaL$ocLhldBS+~StAm|Qy|-cXGi!_CH3D5s|clA^lom}=$dV+4Wb$hM`604sY1@oULI
+M6l>p;i*=;ZmVHfh&vec)>>8TrLh)OtPu?8n$1N&<{Za=(NH=W?R0G@x}M)?R+-U$*a%#Pg-0T)lRTe
+vV;ee}8Q1}DWMzl_Ic*_2-=!q2qyhwWwn9QQU+PTnGd-IAQGPn>Hw&qvh<)#52cCgRnf>_O8Z0I{lBk
+h%Yqlqe<7py!qF93AiCS7i>_99g%p0_A0vYv6N(<6WKkH`DgKAPx;F;q?>gl=}(P<qWL8MBR?bFTm%(
+xT`qUobah5KL+&(4<B8fM#oUw8MhE!aJ4X!n8Js3F^;3Cj#;7fbZvZ7jyG2octhgWGM^KhpCZ*6BW=W
+u9FV(a0%jBw=~A#4{z+$FdN>YKJheoTk@Qrz3tpYM^`3%tnVNS!s4=zl;Vmn~1wBc080rGSXso^?9?*
+a}Q)slB=sTWg1DlfeNp?W37wz%P>&g{z-3vCm`uIVWfh91i`mH<_rB{of+A~xX{2(2_nHR!|&_<^1rW
+5I~ZWY=G-8L?fSC!SNWZn$%-n9H0w1mr@lka(IKZJi)OFS2gOpOZM*4^{ArDW+(7G{Pk&J<qfedk$Zw
+e%rH;Ci9akWHAUR^)P8xa_yJWvHg1X}6@yNr0n%$>6?~S)(!I9L3KiI8(*#jPo3!3jv`_jlafl3}yZ2
+hj@#Xb?^(>-z+Uh9z9CK9fL{@hFS$xLVZ`s9461qqDrH)QY`?@j3)_();vCX|kZo6ZwpY==;pF>w0~m
+*Q#A-!VR`wIhIf#y%<FgEgbo752cTlA%E}q5=2-ykJ|X>uS)3pYOoG(G0smdiQJK0|6}^URk5jQJ|4(
+gYOVrr5K<3=V%E&R&*=2tUz$5C?cIY>&~((IFmm=L1e+xgf(gb4QpHu4?;do%ke4kNQ)p_rNVZRQD)T
+e8{jkS6Vs+AxO}55ZYb2@5yP70xzhrSO&y~i^(CmTwqMdn4(E+5@9Jvp)fzI~qoCn+yT*ET%gYjFt5M
+<y)FIPQ9(9`dU}K%<RbNB@9PO-Ii1i876a1z0x^;S1_}MP^wR472Cqyy!VDnWQAKEMn&&&KI%U*49vZ
+;}_s9UjOQQO+hSdR*#9tK+P6sbK<7731nL_UA*#X}tFGsof!Yy&s|yVNlj-}jmA(~B^pX<S>Yw^>i!M
+Yh0R`?%#%(@twpnR>#*<P-#^6c{DMT8~NGJ^yZyXbIV)LN5yqbQ{S#Q5;G{t$M6hp>G3-;nN>Q5i~7P
+tNS|{+dDyc521rS{>OiSe>S?n$?LC!T~d?VkDXg~yK5*YNT=^xT^{N#TPUZuqh;$?EG_Z9iltYpI0lI
+~5`2T~&2gxvt8bMq-a3cnwbJ!dPxt14dwtCk?C|iZ32d+6>tU*oC)(3}&z0A%EoXSbRByL-RzCCnSdJ
+)=Vd&A1Bnhi>rivM4=`3nW1m6*u+OutTAClATp6;YMVKi5#u&uSMR2?`m!>I&b7tHbM#7tDVxl=Hkd9
+4(x2w<9$dlo?W1@;h{rj@`Ve$CiwNzIk5d)*u}ojbq5X6W71-dbzt$?}FrfmrstTlRdlS1mUDwig3Nn
+k>B)(e3Xuhvqy@*Ge#<bN@12>+|9MsWiqtb{oFvMlAt;Rks=Eq@5b}Vgl}`1%CWA`AD9ilHega)BxL3
+?MxOBN}C;k&ODa-Y5tibn(Y>pSYPG{qbV6wHqFzH!r`-}D&3&by0`FR{WffO)ZBeo24BP%y!xQK!cmV
+Adg4HItbK!bYFR&iG}I{K8&Onc_)_0^!Fb*^V1u!zSObN-&d+48;C8KDJ35^#buGS&|B)2w&rgqPh`z
+F0o#Oer2!q=5pffY9v3_Xw#-x^ZW1~vdaixPA?(J^Wa`z=Hg9qM&x#+UFTZ6uCpKWRG3`EOu&h=*XC%
+LzSnqV@To)5&_&-qs;)*9b|#@UUwoo(;cUp@3crM+I!#Az!D9Sui1xgDWdO!gZps<MZsU3^obyH(xGc
+pZGeEDhG&UK+iAbx6+I8n@|k*(0>r0lR1$qQV|vY?LbH$?@@$t7VnmG8Gr%=J*bOR}f45UBR*B+cImr
+RmYu+H(tZg?CzH5tAU#~`ksHB(kzwycgTe=%@vj(PBd-b|78X=^aQ2v^}&X}|4ZlA(Vzm>(63+l&~1_
+0wzWbebE>EK8dD#wzHUxdH&m=Jf5*T=m$Jwzg@?iEExlpHq^gilbdaptD_{6?hm0zI$Le2ZK;9rA>93
+J^Q&&*JW_+2{3(TAA)v;)<GEd3N2$Jqj?qe?_n;$%c9Yj%#?@b{P``u?VyLV0bkX%;FjHl%EazWMwHi
+eukt5{mrDXe=}cC?^$Z>iz+-R593ZguLUq=IgJgeT_s(qH3#!y(4vsTx-s{ZkgO7LM2Hyo2N6N7q|EM
+iPQ(wk}o1K<Fc=>N~?asM5w24WL`@9z$j0jaj$8pPUtjz$orAsjk7vsXjx3&v=cYXiFpg;~o$jBDF-^
+upF<C!mxwRh#v7|pfdXO7cyK&)78clqV)-{a*hyPsWy%nl<;$_oQB==-Vba-PXe7g6F3FLi#pUP8@03
+a>L_0|+Cpkiq&HaH_Q-f+q*wIUHzR9M-zbtTSP(C>LF*Xrx44YFHkwn5!^fZ#60KX_eReQ|?R2jpNms
+-}_uKzdZJ7QMZO|9sJ=4QBflud1A_vhO6FGabi0)_5@$^q_TS@&JP)h>@6aWAK2mtey7DwX#G~lPI00
+0170RS5S003}la4%nWWo~3|axY|Qb98KJVlQ+vGA?C!W$e9wU=-E0I6nI$$t2li7DzDs4j3#F)aU{wT
+>=}(hA0FVVs=R}0iPJRE43JA09yiyvuiUM)*r3DSKrfCiuR#x-@_jWR>55sv!GV_VKEBTXj8q*L=6&?
+1w!U~&Yjs!g3|il_w)Jw^+hvt=iYOFoqO)N=iGbF-O3+6#F!X{F~gs#GR$5^`{!W(`9A`G*^~a1&Agc
+Y=gE5wOa46h7VnSN=dWw{$(;?i{W$;j+wQ*mCw%_D+>zfP+@1fUyYtI#uFC)MPuARVZB|xBfnJ71{y4
+AV^n;5Ne<z>1V)2deuG(0z*iGLf7B8ajsf*{pyYv_17gti)2Nqjt{A3+ISAUPB@8v(b-HYWV+UjyL%o
+0Ni^UDuMR43v(7^5N6kijs2f{$NoaYv@Y!-@eN3m7&sObTP5>4`V<q=6#slkEn;pQD)qB5d-Ve9?cqz
+G9fU7$`C@Pr1GYnGYG5cUgvMI?6EP{}Ix^xMXagoesn0!LQ}-*u=xzw^wfv_SHNbCZAzeU)!+eHvTq9
+ny&YK2K?<G4hQu*uGPSpgV$n=K`Zn0H{e3oHmq;B9m2G)&}U{jyw872c@1~0{Rse0$<}Z{%Ufc&6mG%
+gd-(tV`+xJVTJCD@G6O+u_Zv$Y+v}3EUu<L<!5MT~U8-OS2Kq0ks#gUb;YGS}kzc^biwASG1eZ7ew1X
+jUr9)iE;UdpxW1_`pP;;3h4o0r*xBZ#ik;X8GBT$oE$$D&Ru)N>J?N141molzpWN{|V5eV_)UdAXn-O
+dU|IUcMq$WFE}94ss9SKg7Mt7@?Xv1^9M>wqeQ&VHNfT|ExUkQ)1?#%}T!)M7hqk8+QMVdguSWh7r!R
+iDwL3jJhY1h6-Fr@{L%$w^ZB2UJx&SRhq*NzS-E+Gvs*<5g~ejU(7Q8u%+_AjB%@jKGptvM`F3yNON3
+toimRKSA#HrPKr)-#z=Z+{hM2P0sjZayOJ21Ac(B$7*(y@i40;XO9e(GEDb{n!)-KwcV!G=EjP8$Z}d
+y4^;F9v{CN1h3tLYqap7a=#wOLxyX$e?d&Ra_V`ku#u@;AOlt#HO>O`@Kx@Q<GYW;yLZPGZ?7c=?ua9
+wKRlQbP74*qNVio|z;@ifB`r5rMP`-<FU8Hykmd#V^Rw~)`vi0SsAfetqf={ckN7sL$m?UR67WD|UT6
+T8VtfE8fmb=wlD-5}K72`Ad3{W4C59?BM9Y_b=fnLF)=FUNYc&cD1m)q*J0YzdPx!pUVlmQY*_c4$o5
+1?!zhnC(MZ|*w9B<S;?<S%GvuwoWQ%cgofQdz-xHMa;WX%ChaSY5t#@Y>|^rB|uB(;ym(wTPPwtm}jn
+C{!`pLf${nt{R}L&D#oEa2gabOic~V$!j@T$mxBH!oA-QWK>q?hQyg37diiZO8*jHFioB{6Tp1E>IoM
+lt|2`zGEs9u7tQut$OdJ8ttEQNds-r^U}8W^?9md}l0RvQ1vJsCB^tbqBOOe%tl(DUvuf^Ks0)DeFvk
+p+$K!!P^r$>EO3k%E8U%B^4qC!hP=X9lFtH#uZwI0mIyGK{0(_NWyHzcmqm>KOoqpzH&)3jmKDvMnfo
+Q8Xo!^21Sq6BQOXeJPFt4-#P!$ZaGBsGv0^{KJF!g}#Nn(5oKi0>($gWI8Yak?80IXkqkCMT0TPV<r1
+pNrD<7ZF@kZX<D#@37Tr_>0OZJ~N`-V|>;By*31T%-mdY@wQZDOGGU)`(G~4v^mk0FMH|9NJ7}1Xfw1
+SIK7rFt;Za8YFxsJ1tTXu#mcylvbO&3QR*89?tt*GxV=soHK<t`|_0(-xM)qu36?H`%H;0L<1dw)txZ
+5N<na)QLFCljBlw<+KrGJ^Vp9p%I$;UE1(sW;P(`sm39+=QL>@|eE$nXio_^CiXXPa^^#>lFwhNQSDV
+Hs34>^g1L{=hGEmCeE=1nE^U(9)&#@+3Z;kwpJ-$A-bwtzoEZ)*|K7&sywI6EuQZdSB5;*tMY-yOSrY
+acdxrkMyGKGC^kB{}nE+U=HpcB>>vYoV_#XP%cp5y0m6zV0vfSGD)g1hluU;G5V_%YCT#K-=09Um;<q
+rP~e@EIMfE4`<>^wE^hQQ|hPQnE2aFp(j{)Q9-9M+8E%O!itn*R!mkSv2_CZ-Ea6x)4=v=?Q!j4>U*$
+bRs50#ZTbd>-bzJm8^4GECPDhCSv>IfLxkGJeU`(Fap;zMeFUyh2FyO(YV|#(+a+kAO%k0JfkD{&0L3
+MN-#keB;-Lt;3Om*I2tz{G6gZ8C&*vVy2(BmyX3%ncYcnzz7yJ`QX*0*nW($F=#%#46o9zh9`c$Q`B0
+IEoPg1MD^>u#wr3$t3&yjN*Z`~|K8yFr7qB|#<pO!AQe&qrB-gU`kDwxZRLCJaa~LMdj0W~c7DLb$Qh
+o*yf`zU(u)KW;15rFr3m+qQjlgw`g`8RBZkjU>aw>;m`K>Ru3;CobS)6Ym=<IfRu`xs*P1kp2J1hrQc
+NMvdN(ffT*%@`>BCL{h%FjE=OOQW;OYI?$w`(G@N|zOZ1)edA^pApKN7qS3<Vgt0ZAyW+iNUqZ3PK7Q
+Nta}clnR7lhJK`xw-AbfHHH=g%SF9hZBf{I`y2d7F$7bUtq&X)y6cOrJn$183>PE*8fXH{*I9WZOJOo
+p&=o|;bCaK9iQU>7<|5PKt42yqRIp6Eqn~kuP=YcO_Q8AwLZe{QE94bB`}t9%7_uPYk|HdrvR`6h5j?
+EruY!RQFLZ4DiQ6NVV9kt>M~t8d_$HDiV?p*(ag=Vof)f8p-YiEH6Rc*g!G<ouSsM?i!c{&?U2)3hyi
+$hGsv@aS2C}bb04IA#!Uz_T=jaRBp^{TY{yq)>$P*)R>_8v@Dp*+?0cD|-iEIyDi@YnR4)Uk%MJ-54w
+YN2<$!$9@#b*ZIJP}*8<Ee?bd<zqOX16p8(}-hOLW*zfin@sH*vnY1lCmA6C5urE=Zgo-&F7kcP&S|T
+4b%aI6(BtFfp9!UHV2wu-IC9XpG^vXmMWXm#JI|{_BP%OMH;2+A^U>nbL$|(=C@bbLUj?+nhzRONg3j
+gPa$q-gE|ZAA`^hphL9BPpS`<bO7p;KsC(mB`R&3(;d3cs*t(1yn#?fGODzo0SB;V(w&lCkTo+(>2r-
+HAoW{{&$m$~hF&T;1(p;gz1BlZihVornmcbmf=k{>Jq!L<TUh`^;x>Z#R!_ZM$(7pLIyO#HsA$coVEb
+?4V<0w53ZRAbg1b++v!K!bm0xDZB-%`NZPPX1!VCg%63O+x&HA6gLxZzE{K_mwL1}#=NEfRCXNRNCW{
+5RJ05x%UZkCbt%VEN;-6FD4^L-=ZC1Y!R!XV~6+O&u86Vmc?DQEl(w2!6~M49$Fpd{aL_UwMPVj|s=D
+5VSzhs2o}qT%fLu1Q!gf!skW$?1v}qV9=R|WGv)pa~UU3(NyFSd5KxsWd$U_46%UZ(P)(_<5F#ZK;_4
+ZjX4aT1Jn2l@>ed&%9=PNV!GDLLSCR3q`yO|?O0!5u`_4GWbuN^TdI7Z9o%rOrg-%7;X#0nySe{v%;_
+t)Sb!~qRv~}HvvPSJ$nQ#8)Y*gDDKKjP2T+j?#FxqCK#zP0*YXuIvJ3Y122fw@js4uuUx5gZIvZslAx
+`FB!!TEGF&pL?8smi@0S~;ov0rZNjs*b9Ufa`ftJv7X@RME!UbfYC%>Mp{bUEAyia=ONlOq_26dq7A_
+hPcWeWOv1*q!|wR|3O{!zu#mdX0;O3KPoI&v34+F*xIe?N>Ydl>#?;vj9LacDdNt&+t>^%DC!`w-}&%
+i&fx8&Yr+YE^w8K%0*7>Rn(6fL3+e7!Qo+v;Tl>vxhETOJq21Fdk~?PTJq&+A<XVR!5Z^xF;=AuR-bN
+Mef~Wgg%e5WEEcT%b;UvsKfPGU6Rx49b5Ob)3x8^cR(MW!av=9);Xsn#WYc9KkIX}{SVCOp4WpL#sLG
+NvWJO8QRuqM1`)Pl2>L;^-=nENtNLP)V+6Xk&`Xw~=ORVvvRErB`BWu5aOJ4+v*qW7W)D#E;xx`+ltg
+^V)LSgt*QX}gjkE0+8#M!&Z;t+n~A>S+1Q9e$L5AdVqBjNKY@*(l#N#T!EO_oQIyD2FzGv-pJx3plZa
+5aLCLC_qTa-)t(;f_?3Ij8~drGSotCO(IJZb`Q2Aqa|<ooaDyr*Nf*{G|{V9@O4J=C*jqp&7VrcY55!
+Ls2~b(q(skmcHyRyO2cGvc0$$SYPa91z1iG!+K!QKG$2hJx*56TANzRaC^+9$^=~Ve1@Qtb)nL~LR&6
+PlTjPMze>xM;lqh}Y^j0?3|z^60gyU<unT|A)R1}y)B=)C_tn@180vWODl=%xtIR$_m7Dx*4kZx~*zF
+N++XZaC2bdcRnFO|QkvTRMMX5CTW>#)5d|#^V={;cL=gKD#={XeH;-{0spQiSf-C%Bl(Ku>9vfe6pH~
+k<(Ff3ePKhf|t<iWWijFTfV)H40YHhoF%%S^O0_H%f^*cv$hi?nug;F!QpuI`CFji0q~S%?>gO$WjkQ
+lO^b_$RPV5Se5Xt+fbhy%%(22ARQsQ7zH}Q&`BC7qP(LOox>8ra2jcNp-RgSSil-EwbR&oZ`S7l!M&+
+eSLc0n*!)k=Fl}6MrET_zS+_nW_h+RjGHvPNnVskE5=&QJQfLS5HJGS*<*6{*R2BP2t;3mM1MYmQnHf
+;N`|4!B@bsL2WDml9T;UA(#L*~N^4Ng!H?;<pni)GcP2xpCeyiLE3RB_*(nNETB24smV2kz@uk>kHAv
++aA=^#<0yEhFi`^Yorp8TdC_<H6YW$&lS+S!ZV3g524gAQRET8=hc2h`;mggY~v`OtId%j1x;PTnH69
+kBY`NTaM4wkBQbdI~QyP21E_qXZE?mE-8X3ducE369{;(&oq5eJOI2ekivdjH)z<C&yOo!`WQlCERnV
+&5<<c*J*95D0U~HG`ENVHqYfs+9GXTSR`2o6Ln+w;U%S7;HD`ybAZ+x@S|NnGXCrsxne8ACEexi#WfA
+Ce)7yE>mw|P^6<BU56c=lCJRu6zN&2{n75ez$R;Uvs!nvplS!7MT>yOUyF@*lXrR(-6eC?;e(C*Re2Y
+Rb(8Uf9TZEtKvgw@;B_P2&|=9ixP;{TTMLSV(6}7$L)f?gi;F_>tS3P@6EfXyp8<rd)xbRVR{jt<O$z
+S%@+q)tD6_ZEg>ds;jKN)e;q=RylpD3=L5y%p+{?o|d&6<t;o@C{L6t5nRd$j!nJ8|9EI*#qV*AOW`F
+ezvETIu+?7$j@ev&Z)6Ygbw<H+N%*wm=Gw?a#HJdT3nY=GI(3H>7X{6L3d$p(ZXr@ya9q?0=^!e=0Jv
+6~>ErU4OR)b<%5itZ?ahfAGRfO7h*e0Y+jU+I+7Q7m){sV?#XFkxbVEXuXAvrA^>bgA-`YZ;k48}SyA
+?;x`C6o8A3oeaW<5WWMKC2dG#X9wAk2IEFfK`&#E;ufJww>qYQn71R&0VB%l!DntaJ|}eHa~8p8Nhdt
+(x8+)`_{uWV*MtrlnR1H0rnb}9HSfaf@dr~_0Fj-{j(!f`<{b=t!wRs&g7H-AjiyvUngRaXj)^<^0fW
+($Zg{4fnNT#PC%cp$q|i+mDbNmCwztqAs>W*WIvfB86ah$mdOji-RGhWAGxhjU2L!#ikywHN!21EF8D
+YFwwzdliLh%}SC08En_W0OR#&)a<W+AR1Qgx419ha)R6%%e$i1YG!+)fbVxA8?{yn(+`jBn(xAor&vC
+(G3-$TwoQ0lRs{060~$Y$wIaeuI+!O1c4OTE9`-0<s-0_0-!BaQ+hz9lUqzpYZ%5aG3Lthi`6ANQ_r;
+5B?G*@~tAt=ls3M3rQxwyn<gSl05zzk!0|bLDc2~+YQLx<6Ph^21h!M7!%*L#v<^}{3!M+E=Lgu7x*K
+Mcf@{A!MYJF_83hxa)E~^PwE%{HULaieAAd2h5pA-c@`VvjKC+$%~$Rg$u0a3MUu@sD*zABs9cexa)B
+`b&=+AsfNW>x2g)K8R0VtU9sTNSFx1W-%&%NeW2dH9_a|Z|>3K#pzlq9`!tJH<b33T$hZSRHdveQ+3!
+I|jW;et6pTzxx{8iex6wl-1K9k$~6bw2kU8JvmTmbiM#kSNzAQ-_CK}cpv@%{mFxXHS5J&TYjwq-ywV
+A)VgeXMscO<p!M*^0?VMw#ME^S(usZ%8JO6Wg*e*}x2;7n_QejU>03l0;%g!Wi#uGSD?C=)`?b)<rtY
+=$5g*Igt<YwVKj_XB{ARS6Jjqi?qos&5<^-(x&lJg_Y_qKC5>Za>|t^lu7CK4bb@vy6sTgj{<dvovPY
+g@C}?usOAREyNdJpl`<ncaJ8*?2GK5*2FovsaqGsB!E(?ur;Vf=0-XPmL@ssaE;pHS2KU(Lw`|_k>wv
+Y$;&PLFX>_^rupEa5D7X9Okjco{<SeSfo1X*Ocd7SDPA>|wY8oj|<v<!IjDiZt<6l5Qn%+NtmohF1h+
+49ib?z>xL0V<8U*JbK-$~VJko=@otTahlHD0{gswS47Or+>2xgVxtM5bt&J0iJb1ai)f9N@9K@~APuA
+$z@YEv&+z1g2GyC3Y0gpGJPg_?86e2A~gC+dPoEf|QMEoGBP6x~TDM%AKS@2ktNiPvP^?c6>hZE<Sf3
+!RNlWa9?!WqjCBQcG1@phv+MCkiMP{(bpeeh1cT`2D&dI12+dN%~B9|Q=_y#WS&xhVA_rSQm_*PKq*0
+Nmt1Lu7}}PuB){}Aa$IAQLE3DW#wLM$tpSi&^iF$zfWueWzsmOKmE=uDRgp)+`n&%MV4iNDk-Sb%#y~
+3@%1G;U1!`6f-E7*dovWB?)s9QGvHynVyk!fy@E?h48dh>-Y=`aUKR*Ox!H+5x&t21SCRp*gcy7eTY+
+u$27%=mnA2H&{rIg{xU3QKA(74c*VKKzkMo4rPUA<HH#!aP61mYs(#Y9xU7G==|E%FJjh?VRzi6!^pN
+-uYlSK%!c)#<w=ga5p%uW>MI)LD24B$pLfrL_gtAp8|}RJlErY_50q+g^7`cMy=kfsg7$C^_RnXZM;N
+m@gt%cCV4LFj%t+Ca?VW+GUA)^z|o?ZePgjS=%fu(<7(0IaNkyl);}_QR&N)mzquQ*er9%%+t{Rr53r
+I)xHbx+sXDi%=H>GhVc(p6RE0SUJ0uikt(~y3j_SB;ElinR?186&k2^@Z<U<gGN>Q%z+uoQ-y>KRRB8
+x>HaWH~lAUq69cmTcZfaB+zCcbL-h}63S`*x~2|pm)@!Q#>^)7;2A$0Q>u-sgH`}!Yufbv@MtBJVpmh
+Z(kxqSihJlq-K<Z4)=m#euLFCGM4#{x0r5d+;hD_&vYM|w4+9G4G~f7PPp$~dydOIkJVl2&EXR&~o2E
+Oc-><iv9{wYbW{`EQ1RUhJEs6p~_NkTUV~>Xrg440VpYq#&<)xog=nZ_{;28b};7mKCsTq^UYp6v-=C
+fSzF+Bd@cF0|UGr%9tshAK=aQuQps8oVg)*>kC#n6Iv|(%*+UHc@5Z$g)F&YA;Y|kV~ihV@S*Hl^B{`
+PxW5I+j6kvGzEX24#Oy^l-Ujut&>`C)at%>|dc1k+I*Yo_s;<i+^E8d4mcl?cB;#J(#fwx!F|2&Gde3
+<Eo;>xQX(U$%JYZ(3-JUA>G#=;Jj@f!`s(N%KstNLG`HUO}-ud7H6l@^%Fr=&Mr??yK_V}3P-m8l66n
+);W7zSF3r?G1b*yYRcn33!r(936m?RarnM*+=`2e*e|ad|Yn19zq6aB>e5h;W>*!**;%-x<5pvYsP_C
+?)DU&b1Nlzbj#SoB`d)P;Lu}105Sjxqa-=y=|E2H;5&;t<CV`-Q?O7ZD0GC>;$!lPfZl1WWn5k%2J~S
+j7aR+db_CDxJ~0*x!zMpDQj!dzEgm4sYhxPn?x)*gj@q7wPQ~Z`Cq_u#RvqBW@t-|n+#ls+Z1l{h#3{
+Nt)TC^$$^C!{v&<<wiKUoDIM#&K_w6x$stpgv!R_v^{CipwhNYqu0&hKl85L-8so(`89Sf$l+U2;!Kf
+PY6bKaF)zAlT1xiNSvAKwiTihP+6}LgmS^(JVfVBN(@TigF_O|t$oCd`h)gxlmY#(SiBbGdZ^{lGn$9
+XYCcFogjviKOfO_7VSI6A(3?-7(m-Z>9!nI->;C_v577IGA)EP)~7{J34RqXL4hPU~-R172*jFkAqoI
+dxVGuJ5yO(NIsw?NaTLz)9Y#&hn%1aRMcacct1>Qe(ULr5VEZ3k{3O_G>}(P2)d@k9q3%F&lXRqNFtS
+xK<uUC5kAnJtd?RJKOneET{H}<ZP#<%C$$7-$$LDYXAq$jh*Y@wSEMsxH6+7!r5t7K8-p%cnZ+5NeAc
+{1n8gun%t33Wo9cQTpgvo#`y~9c#TD!5vXP#N(qq8qmK9t#zx4;Ko;7t`WS5+^}qG0lZ>V2C66Uy(R9
+Mc5J~&SPlzBt)jw$-5;JJ}^$ob<XbLu6c4;c;`<1NS@_Ne$ph0Ii#Q5ljFX#!E`SoodI0ogSe#7uHaN
+M476Wo24<F=DE(mpfPX}`ev*8-r=M8=fU%}fL5PtzjUa%~D`i6wY|0|{S1OPUAz;mP@D1CaONNg&Z2U
+{SBR0AC)`;7p3}XpiQ_gX{hGL6W<wen)FgseVlU$^|1`*(Z4)fdZ=B<Ohj-yd@!8Eb7DgmI?_DXw}>e
+)7|785Pi_>v>@j>RQR^fbo88e8x&CZewB-G2^n12=k-J6er%T8L+)Fc5b~c^cVm|!?(SsV!(MB?f$`3
+P3?8DCYDbxMRroJ<FA6C=YVJ&WUT#0jmx}``Uup}rS{AizOJN7_czpaKo^_Pq+S@>?f&r|+6%1gStEK
+h=$tuMiD?I{4u7@<Esa16-on7+@N@sxSUzcg9=X2rX;W7>FlXSETqrHf6GIap$v@kRd_vVI(!<+Jw;;
+=dkKav6*M;RZ^`4nn!Gk6l(wZt8Cz6dPxag;0_zWh2jS&QegH6X)<1s1n!Irt7f!~mi1pgWzjczD_mt
+e-80QpAc2YJJq9G8s~Z2?CRtj5fz)_^`E9Tqn<>yRjih&~>4?4GXZJX!vuLr@pxIQ_i1FB>-X%bAA&B
+^Td!DDAW|$&rnmUxc$S;M)||y30Pg@U9b7!g)Jm+8Fbr#Wnv1Z<p*16jx>9C{q=gA#MLIIY4xNgakY)
+n+6T}43~1bSjLbokOm9P@>_;1p0hiRO*!){1Ri+`E8aTP70YxVUx9V?2;lu;O)tcTLHt?0bVOGcll>J
+IWZi(C{emY5xhT|#WPg6}ZwbM+@ETw`V*~+w(o2lv)EVm?8CeDvqy;{zS&bWDz>g=~)*!+O)q<j{Opl
+CyxNaZuJ?$oBG<|gHywsLcG;G~e^>s_H_1Q)7(?c+jX=xdiwnsHH8`2_)$pXW)o%{CP&MVcqZjRKb9P
+}0S?Niai@{YIPk8PFaj1Hq!H$O#vU7fjqXFN9P&=CD#wldV-N>kdb7N3mC1>^!z!WK&~5MxIgwO33$$
+Q<{MOHcSFie+m_q`w7fKyB2AJoN_mGPIyDx$xIf4jFT!C|J8>j;ne)jWkoJ2OE0Ol6wNMbuu(ILofiH
+(mKN6cSgqb}ToCu4RaJUm1aj2FCQUW>nN~@S9rhB`%^Po#`>1wXJboANOeofzi+E1s<|>S%Np*ILYR=
+|q!}aGU6KXN)&Z|EfqB}2AwqLs2+z73Jg@Vi;w412X_uS<}O&^<VC!5Y01;;aZQo<L5R^gH76~+VVmK
+h#U@$a+JP4g=&v;&E#z(GEL=w43jxQKi1X?VaVzNxN)jAFeRh__CTtgIcBXFzC6cN3JJYn1EUWHSmQ$
+Wf3GCiRtDaQ}<Xag#At+{%6s>Z$2#uNfCYQ@lF5C2Z{mTsC|KxWsJ^sqqwaW&w6aY8<}PAr9T?n8Vk}
+3`M9Lx^Y_vGCkX|1yXf?0+(`yr8g|{*A-W@!Uz|+N!5`)5s<EoDQ;wioZhez$KyIZNid~5K@#aFz&Dt
+3#cxw(p<{5D#78TMBnDt46$n@Z3<7VGou|mzGulY}fuYi(fgTZ1X%Q&zwHBZyaqib;X5`uvg66Ls5R&
+H5`KA_$(Y)k@v%pePu=zOf<W+SMv48@iaC=5ptK<nt9Pa%X2LlQ7dX%`uY~Zsz>TwU5`BxAWu42%fu6
+bZ}3UIGe6mpe}wlV1lDVSX9@xk;@>sBV03U%h|2%NAe@L9ZbaA@A^#-Uk+7DOkDC!kC&<dHQX+4v^XD
+XA?x`+E>MKoG&1w86y_d9xf<e(cL5pX1RY2%#4Qj@-~aDEKQbUj|vhXcMP+qEJY2;Yr{@xWYqjKZRTM
+7r#tog2|?2O7oO752?VUODDs+ZXALPa<!T~n1GJCgIdqTd5ka$h+U&mf%T0b&)_+SI(xW{huD>0<*}X
+grP)F?K_JQ6j<dfnuFZjNucs}cyM3b}i0dHTVQsYH*0TKqKgNDm$R$e#!M^;0f+=^=qkGBOX^(F-0WM
+Wxe9(#eCsAkD0&2^YBMHO><gQk;)I=%=go;`A$mS_(W#^_ekV{+glo61}RYhjdwGN3nMRw*W9G*pYw%
+j^T<(>$M#7Iy0bepCe2HNxL7s>Xl(%MrSsgi50RP2Mr!wR$5S}a(Ek+x9mSpW<4@@aA-GFmIyn#@d(K
+*>+w8(Obz{n#&Tm@dy*SmpN4_XDl$qmpVKZY?Y#<yh66DMAgI3n6Ka;*#?`I^ZK3U|ZQdl|kq+AUdnU
+6=bBIXDVSaPo6S95W06<TPg*zlCz&<ThRJ|{OjkmBUCxnRkyrLEJ@C3l_adRaERRGp-dDbEO@rK^FFB
+YNm?Wl5M^~RqSpa-cRKhi5=hpj{H@A%a;JeGE!CNKS~))=LSD*>9RpG}ut_Kk&oU1gAq)^e&d;EAh%k
+ff`T&{`%C~p(7V;DgJM7&;7MiU+yId3H(%i(2L1321qdlA-x95T1%JYI11}R%!npYUVQCe!YU)VCigC
+=H#UywKqS1QFr`A{p8*u8S4MLdW02ItV;AS`xFl8=VZr%*G3M~9ganDhA@pRuZH89C!(m@5we!67wGg
+<4xH*%nqA4Kv<@=lWP(O0v4*#(rQU8lDt9Ri=^zUHOxfO+M2GY#%yyJCOi)5_F_uEp(&83w@BY?OD^Z
+OBsHooYj$8$^<Ib32Ef9l?;kS<z{;d$$-Ss*!yHUOX5{nju~Qm9qWujj##nI2$dL=44H+J9cD~ytHTY
+Ce=>oeSCTWKTG1x(tHUPnc$mO>L>>gYRtI~89<R{33KJFPX){Vd^Eae}iiuNONe!Ck!~9N8Y#>F~(7E
+h}xx4`9at58ts<R_8oolI0Ee^o9%ymaHk<N81o$K{d*_MIJO!jw9C)?g|`kzhq|GH$d|00<=CSwWro&
+MIzt~?2I_~F6Hej}+ltxeooa`S0A*>9nfodS9X(5@Lk>!l@I28K@eEA?F8BV$g}={^yr`vkOs&ekTp@
+pN*+KOMVd!t3HkF8OOL+2bY%#?I6EW7#Qk{x2Dv%Us|yd>cV<rn80GcZi43@<5xYAi^A5iXu!3(3q5}
+Zpc$Nux_#@jY>y%rfT$$re2&M@q)Gm{`>Hdt=w{-unct6G~L*z$W5-*!UKE(sMM3;19Sj)a4yvV$O!m
+`iCKe_GOcEnJ|kNl=7Rg?-Y2A?>0v(T(Oal#gOH2e7;f<Za+I8{E;Vy68A_#WC5OuU9ab@9O$RmrA{y
+Xma3LEnSJQ#_4XluyT~c)?aG!DHH3F#2XUJ0|$@_r98&epj3hll(vvw-kH%uar5*!xtvm_vyNVNH|Gz
+Bejp=%yAchPz$r-7o<x}?Yq8j+AgR%n^$B{M7D=JPmLHZGNFd+f(Ke<~CLLKqjg2>mOrweYiA%Zdt&p
+(F7Y=K!}qRN*t%@M9{3Z23Un5#+z(Cj(NoRj##2wcW~3wOC;c`QkHL)4QJ~31K4ymD%!vqeQB-+QSVd
+$!SFbUk&22+zt{)H%x_z<hP$CL<{mP4YE2i?uG_wyD_+ulrM(Pa#C6LG@Q=2WY3T})7!nJ<_bB&|Fre
+Sl%{hTd;{J6S*uoBn;Hk0mQ0xoE;Q(y{B32~DH*&==319zPeouS1)kr+%A7;XfuKeWbZUk$m4aS{pkp
+a$P68CbOd8l#89pXhNtQvkTymN^GgZyqsjHEjOn6K{0*ASlAEPEyY2|*t2IJBUl|8|-RC6!W#Q8g+X;
+8C~^XK4}%V9pF$(|vkG_hvI)RccguI)yPp@>uq9IVIQ&WT4Ediv06m^+s9|2KTh&EhRBriG<|<JiMg2
+J5M?A6lOmEH$7tQ>GX;LQ<P4LlFS~%8ym272511)M!i{-fAI3Gp~jaV21#9<g{LBY6YLu)Tn0gFSnVp
+<h0gGwPi_aiZaz++YQPqCOiq_q-sWezrK;0%Ft*tz%PUb6+5lmedR!QO0Jxy_1%G;G%6{r257t-DRg$
+@06-_X0D3<n1`ok|acidk@TSy-vHPjK>fl~FtZ-u7ymb^*-jKbRdnwb=^fJoP3{P4|aWB2W?U(Q~a9G
+G9i~f%EgdUa3PfP8G1hc7KamZY6+4az+sS8_fxjqFH@XhJ1qhzk-mh7nuQ3p?aUA3^VT^ZTe{+Q3O99
+^l%L8_ryLC7tv?LJCyU9M7vZXYwmZv(A3L&D62m3mhvsx_5ect~OgEjP_YMjb^_+}Q=(i3`YdtEDF<Y
+H#@p+hKczdo*kcK|#`z(M3ud&_rgv_>YN#o^}9^_pstQAdB~L0-ej^N7PH1V#Fwhj4<-C-IQ{A#PEPU
+ynZ73ONhl8LKfZJvHCJ%58$#2MC8ZZPODL;5@XOAzbuukq*PL!ppt3zVq-jmf8Lk1qFyo~rNog^FmMh
+6Q{}X_vec_Gl<7!0X&P{VlFk4k_;zYwVAbN&wL+l%2?8361T+TbG@otmO+^CAHY(}$GNXaPpa4sY{Q|
+WS+CE=OoxKfKxYEBRIp>Xk!&AtZ50}mOkGa4PF>ps06T21HM{WPaXZ5Z?rR!e+mU;yY0K2ySO=H(ze=
+?L^zl4-}auaK(yHgEgN$e91?Cm5Nxd(#EF*yQkI>DmPYIcxv8n?f!$Sigk_%_X|*l8UXa-d0^Ud+!wN
+ib7oPZjC9UElUcbQgifsms|_<QL}>hH%OiWcgs-Kh(-WE3|}~X3^EO!=K>3cr9B+X091}c28~8xMaQL
+`Hyw^MRr&R7yTSq^wE^!HtIe&ihKxAns>;3xYyLCR8WpN8f2V?5pLQ{mOOliwtX8-S~Oor&*3Z-_7#9
+#pG_XYlBU#1tb9g!-Zy&1r6<PnEd>@lvkM&Ke`E`_mKCu6kTAOWJPPa$*<#7s3Dox>7Vv@`FP{_7n;W
+k0dtdz6#9IoFwxo$sgBUXiz4D=O2WtxB%L=bI#7|7&n2}FC`+F!jLxVewa8SXj2t3V$0*{-#gS*9KZM
+nYPxtT0^Th}otRyZ>E?S!7O8km-5Y@*w%3rnj!KnU$)KpCFTyn@1nkY7a>ccF=28lZnvupATv35B2)Q
+h6STJW7^4U)sV-^Q5{w&}~3KF^-@%_KubM9?qcm%=b`;#!Vk9o?yAj-Jn1<*IQIn>gw~*J5;P^nHtE`
+TWWFesp7qmsX)EayeU&2(PC5$+)E+F0_*-V7peG4mwN?MdTK;XO`Kdb885NuuxQ6>9=Q=oG8lYnh&4t
+h^#*&9KD+7j5PcH*e2YF`rBAfYAag)CAP;_o-;cmI&{-jA`Y1_`S3<C9-!4dD_zXGl2R!jvz3w7T#_w
+lRS<oTWf&7OO`=3y(8)E&9hTn2`cp!yZfA6N+m{ER%ntuPsP}6U;`#zU4#Pr)JA3(crE^v>EVzm8z&V
+MI_T1V+dC_!q365I>VmPfyYr~eq|e;|Wl7TS8HC(ziWcsBxa+wO(P;DV01D>ic6{sY|prtXst5Jov1M
+{d7AY&`{hD=i#1*GPeF=usBtex46`)PlgHD*!TxXan4~Pz+PN?WXwLO)<HfVt6-VG8O|C0Ck8YpT9{Y
+CVsX^Sbl0n1=^IExWF4|o*G1WHnu+*HpiY%h7GabLAaTQaeBr6EtzDD$q;TjKV6t1M$OtF%Uk=Ueb^T
+J02f?4SYjWwy@~x0Vq`k@cZD*z{ci-z*drP(#O6biv>SUCo2eIj)$n48UTiKcmf|3mP75TL6i7??0xx
+nne<NSrbl%ICHJx9}=QW+*$OTTYiT<FF8H*yAeXTHhp&kiyMjNi!AJ|0ut+6MQ;r!Sm5N<Wioh?jk`k
+7&-kO%9Ip0_EPcOAgYwNZ#0G>E4W0$qo6h@}XzhVz%>`$wFA9=?CV`Df!>;QZI(dlTos3g0U@|3o}R*
+^TrtnT~BRfHTa+y>!4uuDY4-VYCL2?rQicauDZTkkT8;>jx?|E!3*@gG{vxjhBW(^j?7W>`&83CBBAx
+@Fld8^sg|YhE>Hqh}Tu{OGHx2TY+>6;E%1S-~zt~t)b~ael*ZREu|Pnqx+Z}^7YYqUIcCC0(h&(5R5;
+_3{4g?29snus^U3SNTCimF#Z~#75hF&326Go`3KDC2P9-}Uz3V9s~9QMXOwagYJm9``ezcr-V~6!q1{
+^x!<-*`hwWD&QKqGSOGSJUw`Wnm%ZvJ-?xPzF;1qX}$~C$nI6o;$*%mdl9M!t0za^lVj<xHNT1KsHvC
+UXrT9T$AyIrm@gJ?HXs~a)Ey~apEgywS|LqF9aUHLv_*G4`z1Fwo$o$R%XEJnAYST1%pzK(ma#E8Q;l
+@8w>+%^-9zmt1l4^^3e#BUbkck|bZ@t=S~4tVLM)i-IV&>D~G7r#~?8T2_^Ee0^vEYp{~uAi)3tV!tq
+0;;4|LOyS)mjg&q^-`+BYC$P=5J$0jDM^geq8(ao87Pm?h`un+A<o~Sd69U`Vw8<pjF;pJQh#Uacp*J
+O?EnK$QvQnM(`boO27uN)icc8PbpB4^b}fpIlc$)5x1?%(qM7m)G$GqkBGUqz+iKAThU^2my_Hfl@Zr
+7Mh*@>J!`)b)+^%G31!Hz-F14<J7E6I9OkUJ41^k0ERiz9YrkF~r(Wn4opQIp|qspbu+L?;R6zUOqA9
+htdIKKHa<h%R?kv5bOa`fV=)LAd;ur)PJ`>>sN3VARgh5+mu1h8KtjL^pudbd!+TU9Jfhn(m`E(LLoR
+;G(&J?Fn0*aS=y{|c0kakY5GkQj$3UCY16Bxy$*M=LctxB=i>DS&xdTL6tRi&_GIp}BB<LghM^JwV6W
+itHh0fKI^Pa6X@>A*DAALJ9ZUqJG7UlkhQR6yOpgO^?AEd2A49Y~a||(VNYI(0XfM`=}Fr9}M<TpHmM
+(lh4cTg3)#fc~DvyD^jl9r}^*<qYF8b85L3|(<L=1oYfW?l`^P6Xlp7a^Vi6<+m9`Gd8$Ys>b&71=?R
+TA<n!XW;w`BriqV$rrOLjb^K4;Tx!x`$U7A_iO{e?60CA<sZ#NYe@g9|O0GU493`GuQ{*Cnzp9VS{Xt
+ZYwDG45pE7K_VTYU-lQ_^m1ngi0W17N?_V6~dWeK-#R;b`NgO*h2hEEoqolT6pf>KbJ%o}=qug^C3a=
+SFB>`Pv9lQ#!h87t(?YbqVfCTB1dmD#w)xLl>A``8Shapp(o#{8D*yK;8P0N>MBCLkUlQf&$Y@fX&iH
+>EMh6b+I)y{Iy!p42AE*dXzC*(ej(Sif$&eE-CzP;yz@Qj5a>J5D0C#N8YWW;#W8c_oS*p47hx$L1zz
+C`4E1HZAP-LPG|1I>|z?rU#sqZ=+c(bJhYh;(4lW1mIZo%@H^#7k&1(=GFHm~6TE}=jY5A}NKcGtQ<=
+>-=$+!P);skLtlipk3D(4Y$cY$!l)4)O<ZuW8o~Cg%GF{<YO)XRm@Q3(Oa&Q}MInI~XBe90Bzg2M77<
+JO(#r_&vygYQB%fVc`)WOuj->dM)-t1sT!CwvhNsu-~75d^9ZP!(>lJ}1f(T$Z#55!T!0hgNlhlCvjn
+$V$cj`A*^v%1LP<G4iv8OG7AOx@{R(~yJcvzLD{dFH<f_3}5457Emv9oIDF+M#vRgMwjt`7#}Bb`p%d
+f9$*H<yB<EEtk`}dk2PS-KUNvOYS^&S*^S3ctY#mf6K5t7Cdh9RF%Ge?b=Q;FkleUdz)}K#vo*(6WsV
+-N11^i?K6^lt8iyjj+1qmr^X&di_A3`c4=433~tiEd<6dICZXl}Uhyyl*R(;GK$7Au9^yEr4}=lDw<*
+4Vh4}vAu_5?owZ4(hj}FDR|5AK^d{hH#OM;Of9sO?jPOZ8G-+<}A=+2IyS@VMxe+z!k9!=o4u4)*5=~
+)CnaVN`<RX4EYfqhgf?$d6zky26HE;kRkU_fS?=TNii>Eb4<NhpvVIVJfizZ}EomQ2Q1cF7ccrIj#_#
+|%OW=^9P#Xb+ef?}IBHy04TilrV^IO=ajEOt3;C173QoT+Pa1PqCHH5<`~Y%Xorqk2ZYS9Hyc0db4&f
+23;9kY~YxXW(&o>#$KOK5iH_r7B@p<oixIrS%1ovmMXb!wX}3Kh?e3xmJ8tal&_#h-g2wBrkI5l%F=u
+e%isLD%*b2Tlsv#NAt60lz6Q-Ylr1F>CBE_YEamQ!hZEm8zDixmFB9MBF|RBw2`0Yr7)F^_@?hc{H*1
+w^OMa2~ZpH73CA*FKcM!j`OJ2n9H2B7?M|Hy*AFCd#k+!T+(y)We@S|?^N_yZ_8xc#kp|vdF+Bz2<G-
+shxpvQ(g1bIk<xc5D`gf}d5lR0Bx?1Ox&p*7VX5>ETfi#(+0B(;I6kxI}d5Gejlt%NGY<|b*7p=n-8O
+>l*QpW-uA5#veR`M~WMe!jcPMb@uK7$hzNam94DX&xgSQfUEX!7D)PQ!&Vh-(#XFO%pZCR3NOCtLh@^
+EUb`>r%)KA+_5uZM#;RLup3<_S6ZuVA-9w!e<fF2qPVGx&c%E>&ORq=mPSd={-$|T1U!Ir_P;QNdn8n
+j2helXw7@Z*ZB1=4E|97%trNMIBCc|>ZiQw}JW5SNV_;zWm6q02)oE!lltTqjo&O|%19iB5rs+aHpVD
++8UHA=@WNuraGB^dHo18xe&1g-Xo6no)rVFX%%GssL-)I8v)6+etbi9!R8)#Jxwv!9dxvadjY2K~;Ic
+)7!coH><-g#gNmK&PpOcBg-w~~c6@6ZkC1oYu(43wdfH`2<Dw=RGV9Tv`DUtk3JYT8B?ny`3oI=?7zk
+}u)*hg<-BC6PT^C$m~pp?g~A)U?5#Dp|y<gP0%zlBzA`F*h&`$ez|_WDsMR!5Yh^ew=LGN`wXOr5c7f
+&f8A9J;f7{1c3q{C{EbUc8o#W*_H{NW6^477g~*>+p08cE{wE2!lyys=Hv+`N$8iW`*AD1sw&vnLo(<
+ca%f3cf=AfvZ^E7VbL#p9ay<N*F=*!O;fC2RGOHbLpsTT;1u;R(ZW{2ov4F!D69rW4K$ljDy0_Xwib*
+@59ny5Sn)@ofp3T;4|B!A_Zmci1@m7#2LADg)`n%gBm+VE$CbzpV;yxO8xT@T4$=Rd6hbEtG{2Ukg-F
+uV|_ruq8w_6qBE>-9ODD^$OpbIaho3&TFBce*peG%&f>P*+ei9115_ukMK4rV_-Jl|><zp9Qx9YUx>{
+|E|g<TpXl5j8giFKT<>R%h|3`s5x=+<P}poz*oqVRS2ZCjk!OXIoul`?^$ZpKvtjU_D7ls3;3l)a|^U
+o(-$(Td3w`(E?&$zK`bk-o3a=&U{l<y?K96I61(Q6D_}WQ>TM&x1#OVmK<rzy0-iAP-|h^{fki%i<Wy
+sP>?T+{N;m$YySqXntKEaNUUu0kcV5yiwGb!_Tx=z<!jM$YCGC+Kv><dmW=;CP<SFoF0Qtx-++Y>s>O
+5XEb_$rXp05KJo0|R98QxC)LGa=5F`#_)*}>MOTBm$=`)avf1$)z6X@lC;MO43S=~eM1H+N$18q4@?V
+Z)!cErt6GwA(Fx|>XY31V%%P}92*zX~ypUK?9+mSH{U2mZ$+co+sLfW{6)<L8GVmAcFO<J5$p0GB`)`
+Ri^3ET5r$y`z22)V{*n*Ie!E1?|hBeLbyxm1$r9rhQdtUy}B9YY7e$Mr^_eK-Z%8_0f>Gc=4Va=)x&Y
+sN$Mc95Fu4Ll*BIV*KXOMk)J!EsknQTh>C}D?KC|UC8Yv)Z`|oS#@Q>+ak_q1SmwFJ!Ie}GPw0{_C>U
+}n+|wDa;C%g;d^w{AZ#JWw6N+KN^E%TR63b?k)B~iU~NRd8t*Lt;89O_EAYL8;J}^m&ZQq++Q&@#L5K
+8&Sb`9YJPW7A4x9%3Xt4wV84yxRx@iC}ek$0GGm93n9cM!{1v8f42C4z2jvd+*!U?%UpE;aAj4M2yhA
+#C4ZXNAHDxLinS}<dFdDbTCoua|$jn$}4Z>%;o^~TaV$kY>bW9D|b1nJS6b~_R@&HA2tWJh9FmnG==n
+&U}&E*ehH?@?MFhqOGS|594UODogJdsI}cTQ%s=jt3y(9dWu)R(W1q`l;iUSMUyJkjAqD5k9@|$PV<V
+SMG2l-&S+GC|l^a32`--AV2KZYQc9WedDEVYHkPRn>{vXx2@aWC#3Da8A?{HfwO`|Zp#|6h+)DV*352
++Ix_a1kY-^m)C5VKx(-jj5>1>5i&n4G+T@nIrP@<1wF6RZJnDQ)GvYkLr%Sc(wmDU+k`Z;jOSf=qLAg
+Bz^GUOc;{qS1;(gRjAlo~w!pJ_Sm2spo=;(8bH#b$PO|sJ}c9;qyFGJ=KZWK513oH0(6?{JFcpa@l#B
+7yBUQZki-uXHn4aPpC=gd9IIA|LPH&`qK#dc{vjSj^mRrkow9G|tOFwB1+{-(p<1o*Rx#34LgT-`%Oh
+Aw4d)Y*l07)mY#aBE4iK!9%Un50^e;;nyzf|)B1yo{zhp4U(+vxm3Z_-U{(7r-!GQG$Ch{4FJT1GjKZ
+)JbS5w?F`IXjT?OfL5@egh>IwnI#Nh1hO9BuO<Tr6FvO+V4{bCgV;m5%tf8rEgfkPpzOeDjXJw=J1-F
+BN6T5`VYTi%dSzvv|L~?X{my`$4t`{FWjBP(&6Pdq?Jib!nV+FYd8YKn?w$F3x|Yr=nLDj~n)YEO|Bj
+CGZvw5_Bf?~|<{(wRq={tFK~$FTnm{rao5sXeYch^p(oG$fRiQgdg;iA`LFZeroOi*Bj<%by=yyPA>p
+(6+PodD2hwO)hLKrdwKehRe9^9g**&!F?{bt7R%d;=1&krQ5Bi@c6tNU#PTLv;ltlo?b=}0(92xb4Y4
+0Qi%iBMm`4qfx@75+$Wj$pshAy|1*^RTE?lO7h8DvQvw$&7_CEUEGlJnl<gQG`}uHLXD0hYc-DjOK5o
+LvpapSkRQ;)KYn|>5hvPEtRUUwKv?v`F8^K0D_GCI-ju$%0Kai?kr)yAdDez9{`ne4r*Gw3lfdpsv1+
+N`9V8_ra0{wd-#32r2u^&TQ4@Yj}=DA(Z`l!$}3AW_kak0fLcV&CL6z?un@DW*QrAN)1dy>aEiW6Z@g
+%<3Y_dbb!AjYNw2gjrgWzji15m&J<OMtAaU_mm*Caq{FD;BcY%HxwJ$S6HCr<yOl)%w7?eB^kN4uG8<
+Yw%AkL_m;mPQem-GJtZ$MR{9S*6nlM6hd*|Si_aAmjz=e5T2ODLDP#~$Va_v&s&QSSb`@dA&yaYMEkH
+*Y8u<0)KVB_2cVcMjOp3eG<fPi?rsA_LlgcB%uw&W*8==mlr=cmNgC>3wz9%me&e9vU%bs%}zik8u76
+0Lwm8(AkN2yt7@$8t30Ytw4;t59opmpvypWW9OHzyv7JCu-7r|o@3g;Khx6_BX|Fg(62Xc=6>$c?zYC
+t{!pOT&iT>9L2JRkX^~eRfSLlR-^S9Y$lo~?Z}ut~fQK*^;Ld-JbKut@orOq3JhlotR)U=r#(8{3fC+
+Xb+k61`g%SPpXf5lt6y@ImE!c9OX{8(~jJkqlCA7D1=V}HPxCH+kvYO)dkGB35SOw5E-f$*c=mEEO(O
+Iy}P|(WWP&v@_@zx^tau8^IW(~)zp^wv=KG~Y_MpNTOriB$IQ*J&Eb7{xtdPXgmxrxmm#!0qNPO>REZ
+4<o2XF^s2q%##c486=V7g-f>l%!GBzrCiam#qgl!)$UKU3O#J@yg!W^4mjtK{<$I&IZjXR()PosUsoT
+ydQLMUyheO0}|+6ujy_ucyCjleif@Qid@*A;PSuOPfc8EfcJwU-pJ~gwH7yy=V8L-Ox=nH9PQ&m4*m_
+0*oT_KBMr)J<kq-;VP3~jnOz3H83n}?R+Jg9QRyA(FK+DQrxiDLXqZjjNvHQ`u*W}fB)7Y$KkLw2A;@
+HPs1ZKMF1ApW2TiK{c(09I%LYygW69Y4Nh^ze`&5-$B+(mfs9`eyksFVfD!a+Q9;H72;-(x%C@uVm+h
+ewi@zRDYG5*7itQgAY_H-*oxtrUQhS8ZAol2uaV$`TsTKTeikih2G+q?OQG6!WIZjT9s@0;*;o^I_Mg
+mLdpbONFM8qEgz`3%gNf;kUyd*J`J!f;b#KU2qp{vH=^TX%8L5WB=R4&A?ndvUJ>dWu_Gnz8Z~q-bBV
+r^jE_d#c<nS6Tvx`A-VZK##I7>CqAB(GhNs;Tt^)(;h*)j$ylw@DbXs1BGE_l#dy7gAnOfv31Z=$I75
+04xhLW!(th^P(0q=omFo)3M>>X9xy7OLL|2*qcB`M>^=*~qTkbH;I(2(=bFOJbLjP4U)ql87j#wRagQ
+934-B)QN^Wpg%r!wvOLMB++@5<<ZO1&pG8S*^90$WKS6J-l8q(zztm%FGg@%j;VrZKEL<6BJ{BVOTjp
+PQr(_rpscX@0FyfQAaiygecBW`=$mAtJ(x`P1OW5|nFijnD*I`N{4TcBOn&}+uD6c<flA>{|MqaaKs^
+}$+fNZhzJ<jxQ6QVe==m+e?HUS1ZWJ;!^yba3>_zBB{2U%PFg8rM|sukLYBv$VI|3{#sZ^wRqwZlG6J
+RP@^pk*wd*`ys5dYua+zRj{1o;4*QL{l@hWc<Du1fwkHNh<fs#OYgeC`{d0UZUAZ;-gSYuT&VmH#LWd
+WR6f<t7tGjjO>pK0+46$4q`-pLTx@;;R_80UdoIdeV9AY)9$1A^&mL^H-dp{m3tMQ7Ouw7&HDenDtGv
+u2FSisgV}%sjwMf;nSDHX6QZn^`jCCz$rG;kcE=%<?*NU3jnw6lHsz*>?uRv~D@+aV4Q$d!I9h+dBM$
+<ivBT5=5O+%1R?2GWaJAjRN_cS>4U7H>KpnDB^Ni#5EcbM_P;=?kobjbC+a#jxLs_tT!8F=iOyLv4qm
+?I&Jy{)0^uEQ2nC0l0?uTQ~bdYiO53(L^<De(3LFShmJmy-^T?est+bL(P0SC06MsA)`rKNkKVEG#7N
+Q`rpw@lYMNmv6z{K5XK4d??lG##`m%+QoLkb+A|}%jAeiIQ|Bar1Fz1_>ts|y-6<fV+blQ@5eT6$A>a
+{KX!CGK9r38*wyX$fK?m^!lgAF)(2ZiZ0s;_{u6X7xI;+unTQh>hhk?ZZdm;K5N@&H{;7C@<ujm1e2R
+F%ER2(HUM*F=D=k_L@xna$<~35~k?5i|+mIX}W`>-_0y?G2Q*xFC0w(%dTzRT-j9hsN5Gs3amC8o2s`
+HfcSMsl2)Rbe$QiWwqQa7%tQmZ>^lrexzxv?Fu#^YHZL#~VB76nZk-mDR_Gts70*?B}-w8j=9j=hNs8
+tBbY!$y5a-r(o5Dz|22;Yr*-q1`@#_gVC-C-4FQ?mo)p2MR9)7nwmyYY?hP4zNVI9lmSq?dvP{QeXG>
+^+q{jir6C!oC`NCS4Z^gJ4Oc=g2dP`kH+4l$MV;ORii67bFWUdv~mGdD7s_u9{wtV*{!~}gEq#=YR6~
+VMyJp0%O@`#pl+Gn?jiXpFswx>zPuH65%DSpZ_oBwye&wlv6s-D63+|tyFU9bbJu6e9zY!-e@Y+_VIh
+hNAARjL6}6h<tE%pKh;Ddd_0rEVEJn?uI<4W;Rdtu%$3*#Em|j?2&}tCF27icOsznH2(u;F~PZbP#$%
+a4CyQq(d-Ky<?7zT9}uc9V$X+Pw=!QaUjJOf%C!;g9f)lA-~{5e>vVl9Q?NU(Gu8M>GZ_3I&BS}7PbH
+N{^YfmtCtEp89sgY?HV^KG$YJg)YVlL(F8gLGf4hwgX>PI7)Lvd9v;BewVN_=6em;r<hKP@<boF7<?W
+CnoH`Le%&0`w{w`pZwlLzY7xIJ7$cdKbK<luGUJ#xM?_x@gY|djZ#6=T2!2cu>5=%(klk#INqEB0@aM
+bNq!nOh948GJhjYc0pV(y&)`yz<9#~4&bUDUZQqJN52e_)V_WP_!e34I&-f|o5y{zwdAAqnDV&zVqAH
+Gw>MYtHjI&Znq!XoWOhgh*9{E@NM!SM6aS%P|ik3^#UQEcmzkfm7*#MHl>wWMz5)HlnDxatqf57mmAg
+tUh^gypZD?EyVjAYIL5eDTM)6K#c`0EP70mwjhzc6Ut{j4_4tMO{_9Jl5dcTBJrS5v<@Drw*ujn<e^<
+fktr8Z(hLM)@n`pzSa!M~1Y@sJts55HElPEPsW<95yCfZ_M|gG0GJAOyO7D9!qMy#Nycp-d1!oZ(1xU
+c!>jgzOexn^iwJ|U_n+84SlY9sqChK6|prc+PE2s@_e!<F`uem_ep=}KrdAIy3olUjaSrO2D#5ZpXkr
+h=ab0A%anu^MD0iNiNYiJGYNQ4zt+kmD&Iv^`UV@IygGQz)24~yIpY?Kc+Mo`Y{v^>Ya{d}WoaMehUb
+&((qEoS822aa$AwH(7-GK$hEp3sQ*vNZS;$vz{WV=`luD0ML~n_55Br&hcw!qm@zVzmH_3cOyNF<dIM
+A`-O*gslCscJ`cy7@B9J@?@`y6h|CX~D7&mpf%RX!oD!{UFS#miCU4cgefonz7Eeg05ObmR+&Wmx0`G
+yjfec5~>n2%g1_R(gj1U#WP~E=(fxo<ko=Qt(_78_`{wTb-PmFqIgn3Xa!I6-?T1Aha%-Q(!!N<ec<G
+0hHMioDa{Yb4EU;>6}6M9uQ&QQ9SLOKst751`x0BN*3>hU~B@E-CH)toX>N3#S`v0|2T&#I&Lz4w0<+
+>V`%hf>Mct%H*x-L@IfM<qJ*{9cDT4ACm6_wTDeEUF4?5cOq0*3xy(_#_X{s<Sq;$kGrVOEZ5roy;qA
+=1{Wv;LP?XTjATk7IAW}dxy8p@)v`2IN9WD1TlRv}-?gXCMnknxtpukP%GH}Z^F#hkTc$=c)ra26s+v
+=LxQr**1`L(!u4uft&JuYQ2w?FJ6Z%)w;Tz?=3vChIbO_iW>I_!tI`=6$pHo-vI-_hie+&pD)f8rNrh
+FPf6hA88FhAIunMGA+&pi3a0ocmIOl|8^LgF%E@=2CZKxAfd<?q2=qkMozH4<Qqzy>+><jdB1xB{P6J
+?YbYtcm~?%(#&!Ysl^nQ0!TZ>yD=lTt=EK>Uf6$z^W$bvvP|5%8!WWV*^@P_;OA&Ygfwv5cEN`l!17g
+EdJK01=Yk41*C<Tp_SkHP#R0%{7<FFS7^Acs`zHmlW*u<3O5@1Jxq#$)J4F%~xDo52g&OsC@>lz^R=6
+aZPHq|oJf5~=4s8dv2DnZ!t&h9!+(l>>i*Ab9t)n!UmRq=)a<){)&53<!#EZR=H0K~`ZimmNeMmEm6x
+52|iOVXbv|>=%@4twY{HCq9kP<>LPytVSrFH#Pt)mu}N>AXRHJ#4@rbFA&#$-9lTda=q<^gpS!!-Ow?
+$)vtb|b#0X=6xZzI_-MVs#YGK$~uF<F?r`E>cJtf*sw+)}r&k)MV3*T;R{c8(o3M(@!HlpzT`Akz}#j
+I&AzxZqF#who-l+_KVQ|v4-8)eqYwgdMQt)(WW-TZY)B3)=O4XTk}2)4q(sr{!e@MBf~d)cAMdno-M+
+jEvAs#T%VEXl0eNq@l)^EkZzsRyJeRjrNq8xq=8bs>22&<w$`c3c4sFrw1$wa*X{OJgL)YZb}KL;gF!
+5|FiZ{EU2@pv)_o-D%xt%R$oc<GpGCCq(2X`%&_4?^e;WdLi)l9k+CSX-rlS9jokrRabE`}{TBvt=P6
+8d7Xo@)>+JZUJMOWR85bDf1$zpRgoQhAEQ6i>!hnFwXHG~n?iu))Ge~mAtu9!|iA%}eWY|_5%i)YcbP
+?@h!&#&OKM!7!G7i8uRP+e&oo92wAW*7zJcQil#-YEI6XEm$AL(l-3Hl&bFij~@3v32A(kJ8)7tlpid
+BpiHph*e&LmX~;*{fAbNi*>M~!B!uC=DS(ttt-5o73AIVAy$wlo=KMc;WL-Df~<TtVWqdXklxc{^&Y}
+Lbw88r(<mZ8Gqf+;hxcV9Svy!0hkf|qvVBOUeR#Wy{9*c#KBPh)uCF53P1lSj%}42pQ>H%j&>6FMM<+
+vd+A(A#HmtH!V#(J#|B?A2|4gD|A5Bm0$1dw#XmPmZZW(QX_$={fAZ4Y2sA5P}=84~<_cU%P0>QDBQ7
+bL-n`8}Me(7Yzd8&{t&Yv!D@|(Nnlck5~@4+xz|AM9iDYCO)N=sb%@f|$!{&4Y9kG!21|B*)?_2APy@
+_q+m9J%<P`2F}r-6QXd|4fQC5C10`7Vk|M7Qc9L&?E1*As%^;zy5#GBd-@3itdp|?Wd*Pnti?IAcywV
+gMN9>XHXxAp<a2?aId_VnnHh1dgaMMEaa|GI`@*F?SOb68z$yD&1n(Xu>VUuUP`MO;;C2l5L!DWeD$a
+wUjn0thTyn6iR0L3gT8td=&Lugu#Ng!<xuB~!;qRzUHkqId+LFjBmIv(^~9&CGfK>Mc~?CwH)n{e9(#
+FLz0WV>s`r=Su6k{kch&p-5Ldn53~|-_<#%w^`^i6b)vMH9^_;q^UWx9iH%oWbyGD1_o2t9&O~4jBO}
+#C|r>P@CkQ&=-&U*J=@eiE!?mBphv)-eZI_upC5V2Q=IP2}9&U)4V#941=!dY+I#bM5RRsY{O>s|N`&
+U%Lv&U)jAIP1NjJL{4ErL*3XGln?pJ$3QBI_us04QIW%nzP=l@8qmE6T6}feQesL-g-}<n|EwP!dq`P
+_12q`@Ycg$d8XcaFEE<7-X`?cn@+v;{&uOip2p+;iKpIyQJSaTP5<0eFM*)%?y2`z05=WDi{i4UKbC%
+}r=IS#_AL&3=rQwu%vEpXABVW=J$Z4ss~*<$-CgzQJXNWm?KfTZes%GG>Z(VJ`$w*N34nj@s&|m~hPE
+e&3%Bm3$Lnr-D-&*d`-iycA>3u$^kz{vJp{PCo8E{aZhBYz@7?sq1&(dKg1YIA=xY~`G5^?4PsiT>&`
+~e&ogMX-j?^9XG{Q(a>LDurxuf3ggN}MDzTv3nxUpWG!z4WQzWU#L>ft>6zv!v=tBe1@Q;+PsjHez-e
+&6P)H}>lUU%Av%59=B3sV84j_&@U0L$5pPsYjjj5}tbKL`yyO+O8VnsfV%Xsn>F8O9wsm)>B_PEbE(|
+dXHbc)Kkw%FVg%rPd(bHZ(!~DSHIz@hm44N>S2IPl2FH_ao_aR<FpBD>uo&}Tl00&R}(Y^8c5>;!Jcm
+wu`lu6+xm0V4}&){Cx7l>+5-+I<9-KI4}Z_Y-+uTrL;UO(-Ft7@GJUsHa>iw+)#C=eflQtNXnjYmu}6
+gwB;)afsv}3`_mrEc`A7dUYV}VY`p_*PDvU~82E?bkNdJGJ*89%oJ^7%rYH06aymgkK&WAbBbP?~3@1
+VczIv|WE+y6t;RPX<fWE&##aZL*hffkseYqa<3aCJ#IVt}A>l-kM!=Tc=s?$)(I5bl>;Zx%lWjn+keJ
+^m7ncKq-kwbU#&B<Crqx<eU5`u{yygITLoYw~av@RVHLLGFfh`r~sSj87_o+7&SLhZ@N0-=W=kB`cMp
+X+xHHqlJx@(p%+}5%6ISmgYbbL?IuROIh(YGb0&>{`l>Hz>&L>4Z;KOAy^qnT!U>r`xs#3!*AM3fnIC
+?z1UG3x#lSlEchuRG4of5B#XCH@FrU*veyhUN@VXy`ZTlnM5%?ID9YW`?!cx7%g1|M<VSmPlbG9cNDf
+O|%y?1F7EAbJYi2mO#rpf-|NdpPm=_OP0J!k&*Dzyij-kcsxLN26-ammJtJ63Z;+6`1tLhR;?f8^oO0
+6dtAm?$aZZNC4Ea>ahpcjRkeu{(^e+mwT!yawuX$^b<pltz+$cH&r6nx@Kh~Qp2#Jyy)AKEy&Fzj#zA
+IZXWU;c9PyFGLhqjjMHbO3)yIN@W|4VGY_AMeWBV0J;>2<oE01<*1be-Ww*;vp7hpeLE^flO7zel;?u
+G993QC;*jGDbQ#NRDgKf=EQ1{pJz8MNu6MA-IjvIDkG4o=VDYoMnOR>+for>N?Oa~G=P15O<As?F&gN
+EVtzw!sJWVk<wx-u^k-wT=}7R0`Qn8!8#9`gWKXaLm*p#2;`uQfE%=!qT-MQIRZMLRQ)+*wp)K+IM1>
+;VbFz5Zuh1UnS9n3s+h`fFo!$((XdMVVTh}uD74k+eb?L|_AN-oSbP(^a@E+hSa{fOP&J-KHlF(_NMX
+wi~9q8r9x`+5x3}}88ZgN({%eE0nTI7`}G-PuR_llvW)ep!*CsYvn-QX`Ec3G=nWY9{j8@J;o6pcei+
+5ur=GzEjy(mB@(v@@r+pLgm;W6E4{;G!^cD2N3>l#}8>zc3Pi-o{}bOjHhH-Zb>RLLS^U=xLSz%^esI
+S)=`)L$B1x+6RPeIYR!_kJBZFzbywHvxSQHc4y!(c%TC#UPc}&DnYC6hR>Q+auKC99w3|57!7+-;oB1
+h4xDpT+ZlF43|`dc(Iy2(eIKIe3=J+E?{<;HFG7XK7T_k`&&<+#OK~;f?#DUP)c6Gyc^vg0;mg)+|6$
+JGhVtP{<uFqY$MC>@NHO=lNH@Wtn7a-aNkT{PIVO{ijZ)ZJj%ET^b1%)q+oJ$w9j#4W&?&aeJ^@NIEV
+9QpoR%9|Sh~@3@(2u^>g*8PSh2Cg3?($g<S4W)x)5*QWyp^%z^qJ_7FvP}twE^=3i@Hae0{xCgjR7O<
+4yk$dv5|zW%2co51U+<1XB~2@}{^Hsh54ZAQxE_0!2~KTnObND4TFEYAK*sRJ>yP%@#}3T*}J&nz^*8
+pypEUmYG_qsa?d<(gc&7|M|@GJeLciS#R(A{(k@8_knX~o>|YCIWu!+&O9^5U(DCU2N{(ci5rSz9UgY
+nX)6kDAo)VbP8vvo;g5WD8$_y9x@hA~bGW0W6-A*`OA}O84yE}jxzCcis%OzvRlOPLi+Xk`8G2jj0<D
+8<mtxTn+NAeHL+Jkk`No~$7LPBaZ9=ClxbVxIoKAIanFI+YQl$-dbeWZes!&6PH(>?Zh1ii0MUEfg3O
+#BP=3Cd+n7T6DQ)j`zK4L^^<QaT?Q7bkqo)#a3$*rW7IVwFwYA5lQFN!ha8Izu14mm6~qTGnewRb7G1
+c~1Xt<%n><Xfz<KVX@>T4{pej;nOogAlG-6(P(Ps|Iw$XM?50cr{MbUDM35;jU?=Eh2r$SyyEdJ|SR&
+=?~0{b?Y>D?im$4DkTrJi0RZ}xuJ)dJJ-?_eiylDwN|uSi`HyK{6T^E!*EOBUn?zIe0iZEWPe$S=3Lb
+cX9}oi+>&A?SWXu~d0g<k^*}sFll~0$%vw0PS-%c`aKay!L~NJY<22v;9j^6AeqWRnu0T%W$IWxr3u(
+gO5RaD+ZC^IpzLeL!lqJXSiBw_AI^)@z^Hj!$re+7(&-839q_c2G<$b3sk5d^bfiEe%(?+qc>v-MKxj
++L~nL`H%)J}`7P5(=1jTvd)lRVn?>VnxgliB-Jl6I#dCu9J=T}wC525RImCeY3JlzH}t(px)vYaMUV7
+E(2CMl8!5TmwA9JbPok03GTA{Lvi&_-64Dn0=jl^>ndSYIeC4F@hrM%(HjVTQ4-_c*}7LV4jU#%iA&C
+_Sh3|*(sb3%Ws>eBQeCcyggk=aOT+{G{k~p=@|GjZ~`_CU8%R-3@^(Jk!x9@wGfM>LTqJrCM22@62!H
+m5GXLY*6^UYzhj+qB%DoqM49eMwD{q*UL9lWv{sy}Xz)oiYqZsdo=K$vmI-L;*ln9=`5EGTUp4}Rp!h
+hD2Elx=uzAlIv6)QV126#n&Y^|WQnu{Iz6famj>;v;t&j%bPL>sL&droZ;h`@{qwu+f7=>F{3@elmOn
+vboqG@+#qGbe^x|{R$+Tb<{owXdXjpyrs>}I{Es09}FlUz#t!~)EYcPa2E%F2H4M9cf{in|<c8@eP~)
+{x!y?By5ez3%MgDtq(Jc{+FZqF{TMSr&%W4DZT!L{LKMHVKVtkWlPh60&odwOl0RVUtj2n}k|7NXX%i
+gl^~@iw&d2cKz!UoUm~@-ZIQhSxR&kSF=}*_U3`puNG?fK*uQ$nw!jv4Y@v7Wo{`S3Eh(9>yYa-Xga;
+F938&5zjpf7X@}FWMNn;ua=bm|0dq_27b>+gHL<UH0Qb66gy?Rf?I`2U+fy;_NZ%Unwe#K==oE|bX>o
+<QqE6&NmEh25L)*a7t_IhDM<BJRh)Jc*Oy1?#jwtWmKk>TQy&pKdjqwpFP!pHUEh&_(RD(|*zYsg|H#
+dp5p#6!BkkcUx&Bcc@rki$!-2&rWr>VV<298#_ys%>kmDk|J2~ByfqjVB(NjZha)+tx5h?1uc7-MJXX
+fDPJW#%#~P3+<pB3R{9;J=s4l;e7Bk%1;jEB0cEoXT+)FC{&}u6}MVB|(_be=OhXRX3CSN}9>lef>c0
+D2anBFd>Lknd7dSOYvE@%NED5ZdlpGHwD&D3ij=^rl=CPbKx58y8V)g*bd&j+}P6`D{eJx;eE9F7xM}
+104MXRd1rMhx$iaahUsvmsz2FBkqeG7r7%Y-kDNMk>Bx!Z`;6DCubHn>s5TTL!ris{qQjBAmRbroS(W
+FarLYA}2YQf4$D!_Ryibu$MYa~zC#z4GPBQc7=40k>kk6PMuF?$uhEAOCIYq?`9mM&?eZ;4&AAx<-v=
+v4tQ%gR2CvP(PPt}y+iki}wIL2iQ^px==$i9f}`H?DnQ(sP<7gUw56GF+oP~7DLUFG4%bd^&a8|W(A$
+ht~ww42_dtZXC1v`t^)ciDOTmaG%MHRAV#_`NQEPmAC6;`cE9T7IX=hh?x?=qhi?y2^fLtFH3GJS?P?
+-;x(nKD||^tNaPq(%;bDHXL%<(ynxqMc{B*{O%IJYw`QS8Tw7r9(S3wx*0Yyjc~YGx^=}ZasJ#bXcDJ
+Ln#43y4RncH8tM{7T9PhNW2;MqGjxfA#OGzsE^{7`G>PVQk|q)4V(rd`-V-(@P-88D$}`X;ZfT%N^r(
+<EiJOHc5y30B2>l$aj+O6N?RJ&Rg(eYpp-Ehb7~-|Egsf?%-yb)6m>o<z9hJ%fBGmj!)9MneUIxd?p+
+fC<;+CZL+ajy|gjb+YB!Vnf*-J^cTfKCz%jvch>53Alv==E}Z;&G1D}Pi;gx5qH2AgWq4LVazroqirG
+ufqNze*uV=(V=0kY8w~v6aJ3+1Ztx-%u&|yH>tpwW})^h=iR@HGanSX{I83ALE0jJ&tljDx=CQ1g`Ox
+e7z*!HSmQRv2u{`l@I$g)yOJDsB~ue2#e8WmWQxRahcUcScbdIYA@7?U<;xbO<_ox;IKWSmc=Zo9h!6
+HRcD#(RF~&Gg*Iusq7B4-=eXlM%-~84@Ram@gG<R-nmn7J4SZQnN#bg=C}N3dUD?*!TZ+<V>s3)5XbU
+C#TuK+%W2pgQAuCmWCv=F2B<T?MvAUAm%543N&a%6CtH_CSqtkYwJ>2PdLxfUGRc{_yTe=LHy3D!>)!
+`}Y`Z*j;Yc7jY*@PDopck}q!ud4NmpVIP0}{FqnuZRJw>1A<-obAd6~zSsfm`uA?QrF3Mc?>6;SXKo3
+&7jr>@w^6ZERd}Df!qD1q+7sz7eJ1x9PA=ueACW?sc16viBBNj*)G=Ss1%Z_5nq{ew|P)!au&O1SJ}8
+=`Xg+kRUPDrF0Woa+Pyhsqm9RNJL6qPo*C;k#%iCY^fBQNN=fFt(r*0F+8}$V@Dl-RQ>{G;+n&viQ%Y
+En=*{Iw8g<2m4_W<J>&(UM?PRD>mj!(ddMFko7Q9)vZ04`7kbE*LJukWhtf@89K%?-EkFaAXhoK4(Gp
+QDn((YCLi;F+P-q{)$!Vny&_0Te)?tt6)CAhqjtT?m_LLy~W>zkew2n)#nDw1GyvCeRTltk8CVkX<j5
+j7pTq)LQp49FwR7o9!dGCyyLT~6__P(SsJhKo>V@P^LY027KipH?6+|4!67$UOJ7&^!r!=04kLU@PQm
+X<88t8~5zElFq$uUpxJTrZ0th>>KgF<b^xpfUVG(HM$u5DGz&T#~KEFwL|Th(cv}$~J_J$}m)@3`O}6
+dHtpKnpD~THpsay`V5Sp&L|gmp*DQBR<QW2rHb0{pjbXcC`n-`YF8?KNntn*>!h0D#HssH03xWVd@#K
+qyd|z;2e&3cV_2f}r!FOUuWQ?ePLf3@l@GLs;X->DUc02Ko$N<Nt7s5KDA~DDEuuoPoMk;Nq9{>l5v?
+52!6dq5MT-b`Xb}a6$XY}YQMT!p(9wzErc4@w>J*765J{2fbc1MvB60q$dWu98jHE~`K|(^8s5B0#4=
+oiPmtl<1CGIW`&=?Z9XuoE`fl@!Un`x?oMaP$FZM2eqT?4ObI!I_7n>bXIa2yQ^?;|x*E~c$$P*e$P#
+dc7juvTaAcuOb|9VhgvYPr^qOpmH2YwZnfCa6cGm6q)n$)A>!H*dv2?IAi$kF7$^5UjYih0(q05ou7G
+pq56ZL6&(!aRoacA#`Bh@lA+h-U>O)yzEQS7WPX$A%a=v{H9bdG>3g8tD~{IS>#gHVS+|V+q`p-<%x!
+=pEGS09h-5Yd6^iPP8Ft@1>@K2h?O3AwP=G+T0Z|(QtFh<7ZqFR&Nr<VB`eSq*;&f3bMCMe0Y;RZDq3
+IF+f*hpuIeVWXo(^&YEhv}MBFTE4e`RJdCLSD9+wHaqsWr3H!L#KR`f<fZ3v6Sl8NC?xQ#FiG1Dws1x
+X{`H%dz+S#&qC(c#KTzd;6tya2r+OlOx8yWbiWcco6(I>qciQPvxFS*>GTR_fSLf`1d-B5+=%V{rr#t
+7W|*t`|!xDW5Vz(l+G}mQD${DsCW>)NL*m7n_)ZTXLhXd_yi)!;O!aEX?4F9ZAO42@|eYsS+mqR^^Z|
+;RbK(c7zglF74FByL@ETLzYEFR`u1AB4tyo^$t;UoJt(Vru|~tNbLgxhXiv4^o_qXxCKd4>-m|2HQb<
+4F6({R;|qgpv_Tbp7c5LIn$S$Ms;#AY9Tu&N#8EG{7hlP0I5$gmp`?L4U1+miwVpn%gXNeU=0j^3%Sf
+_TeRbyv3pXNEg!2Kjqe`s$q+`|R8wzWlt`Zm7iYsmFZnKp?81+6CI7VgkozToOe*s3d(Y1JQf5%0#Mu
+*bT=XsP$S@6*?T={g~@UTc*`ou+6+m=NHAYQyUjOe`Ol|r0IaEj2NOa_+OUhUZEK@5m1-C?<Z{{*-6x
+uByJiXra2_y+g%gyxn<%H&HR#a>Z-7}DIbzf3yk4408}olBV(-Ivc+B#h5FaG*%3@r>C~xHyTgIj_2y
+Uc{&s<!EOdX)2^?YXISBLPFlB!l^8TTgN*K!>M%i=_&_Y^{cOCmY%4(>8F{oUD=ox2@>T<)6g+NBE_^
+@S7kC!KVdeWq4Z<sqYOlZi@PZpO{P60JS%QDq2fH_<Z!jrc;19-Ca3nVd;-UX;Ev?AbFFLqhLo}vB1W
+m5d|$G;)r=j2rXBh1cIzCONyT^ToSNY@OjEgzB2|4`@uO4e^z#xg95S8lTya*ZvE5*uB+KqUq*8UX-J
+q;|F?wtomIIqx=9_`N{QY|p){o3_uHdl>^AU&L+S>&xZH>#!6;M3ggp`ukNx502byA|~M;FsY7wzpa$
+wsHj2injGyhnT<+NPq?-gM5vbW@f0*tIp>{e`RV6Z7o(7@np|$GjG%GY$@WX;$k<?%chMZP2lWWX_`3
+g>C3ZN03U;^~k&?bDif!+0E^5H!sSblAYV%-n84nv_~Yq-<mjeTGp^4l$~iej*CF3H>{x+N?}a95e65
+#knddEG=3!GOM?9by9hod*hsLBU^QrnSa7~H;<@u=TFA7Jd6~@1WL_onDw)^GyiR5fnKfkAl382kh=W
+d<!&QeGr8GE*RuQxz=(z!nOEo5W-_C`=(AF+dFS`(q^-6?_LkU}E@T@JH7Fa9SfqPvYJn+Z0HMNxQ{1
+1?C^*2<~!5;{El{&d8)rkk{WZ?!;CkVW71=)8ITqc+o<<RxW-2M)xpGE0i5v8}mCTZX%hpDTppDCoMg
+}F8%7utlQKy`KYvxDghP<EM#b2;m$ts`RqK|H~5f~N`6333TcABei?L}lHHOgA#!$ZSJq8!}zVbS1L|
+nJvgvk*Olnkxa+>bt9G0Qu<v@u#sTz2R3yR<v>{5)P;DjUk|o!_3AFo<x2VB1Y8B`LanmMyu);^sKVl
+G@RvWJPID=;BM5WFj~YK3{OIr#kDsUUlUr7bb~wOzxa6q8-@K#3;%IQGav)?7zbx8yfboXuoQt88d58
+C@ic9teRcZUOXvY|14OOtA$=vo~1B}0q9KO?hqWY9+r>n;?@A4t$W5g-sXBQS@{Jo+IaVRCnCyg~vRS
+rXc)r`08M1$fgw0uR2R=3`qWS;J-y=H8sy;bmlw32LJ>PAE7v(k$Lbzb6r&Z2_WmTi-jZ(YYu7T@kBa
+-$m-VWZ8;a<oecjwVhsN4EJ07n-JND+>Iy*9<O{eDkD)M2o|VVvB|)f3mVjY)MC2@>;Qc{tH&RDOPvw
+t;uT30OZ;Qxn3^d9N|jjHRVct+&j04yZ|m8fhN|ZgSm99D#8dYa$JkS2}y|)?8@TW3{EH=-9oH5e<)5
+QGPDUC-NL0reEb`y(tcssT54DIk+dDkRP&5W3C=IkUUQk15A&AG?1?xXibx4~L6A~8Qu%DS!KJy+ixu
+lOil{LR5|_<~(31+3!fab03e#KqY}j$GebqVhuO3(Dspht5qMIc?7`{ePTJ5%H7HvV@=fb<CPjBlM#8
+s$^t)*f?oQ8ty^M<vVHPr55654Pz*Ms$z$h$_m%BQzGc$e!MT`PAKtK}1z<rQ9k?cFUQ5tqW+B|zU@D
+lTlZWPJm5xkVh;RXXf4bR{EjF)m7C@Hd#icl{g(0o~+=Fbq`FT|KUL{M}_Pwki~<sC<R0Qe32!FhRxJ
+x*x$e{cU5#4z*fbVr>aETCbS&F@k3VpQK|w0lBobgMy<pvZjPjYgXi_y_V-_-dSaD-i>o0#AWq6MTG%
+1za_|hN|T~vB{z(R%*LzSSc4!(gp!29<4jr^g#kF#@szl_&~nItYgV0%%`F=Y%HedTgS^yuy=trWR?(
+zslx_aRggPcjJi)ngE3nI~2(%<}Eh3Q~-z%vp2kDrH0ZU3FuXf#0I_x?$=8stttjSwO8tT5FE^R`%b~
+Ro%-KNxVa(#k0vd@A<tG>3)>KkztUL_1kA1v=>5u5tT+DP(sGg%BhI4{iIzC6ig^HfKdZROG?=p!{VZ
+izeR<H;lkLq9Tc-duN>_RR1IOh;x6DUNn5Ha9Da^>m-ts`%WD;?M2owyNq;-*sm3=k`^W;uOcCst4+P
+iXSa5fjDk-76(+0-igBK#>yE9g*s+)gowD$E;Qe&0yvk4#xtA(JTR&wiC!vke3JO09EuUs<u+-7kt*q
+W>4Zd~D!Oc0H5;eWUN#SG<9$l|g|RKATQ*>On(1cy$&P8#Nl;Z!eu!RVf*qpgKZ0WQN)wV86ni5KPnZ
+`#ZSAtz(XL8eHmr6Cbt%JCL*fTp4mKBO^hf~>j`Uj_FuDe9>N?sKIMwpRM`D#qHQ^ykZ?cZK;pEdYjM
+*DnO|VOE%b0G0gJnUk;8C)<PgRZ-m-NmLnk=dr0pEU$CD?e&^JB!f{dHgbx3#GAti`i)l6)dSy|4Rw9
+uc?E+gtvaVwL;{n?lwT4!4a?mc`VvUdIbKP|}a?+lh-&Yw?M8oN6#J>=R;dlHnZmpZjd|XM@Xd*vDSt
+<ilO}ns;?$SE;Yuo7c=-F=DZql{&ib?1h$H!m29oR7RVJxk+=uV}+e*;4pt49pGkA>8OfsnD%k=4v&f
+f+8xvFNOE>79bFSR+TGY(ob8Y6sw3UazE6l_sRKv%L~rBf@<KV3q>i6SXYd%^`El&+xvIS-P7WAC&V}
+I<&UhakUR`afvCs1_iwISP;B3*!tu3iki{@Q4D0JGx_V345&Unkp(NtRG)N~I+>6^+`rIFWhHtF@i(b
+o;@lEjIbIHwIqwz*5^;+}z|?yI5FN=T3bhz{X6Hrwu$&e0XenU=)48Xhx8s^N%5j=;zom*-YVh<CD$D
+DL$%Pp{G5D(q*2ydRL)R!n}#@^&-wuhrf%r0;PC=B7QWst)m%3!@q$FI^=;0Yj<Jqb#o4<boqf5)uMO
+tBsBc2~|zFQ7x{lu1QJ&Wn#c23X&}PKg403*Da&|5CiRqGW~g+v=vwDeZaEyDeF-rb)!-b6MDX$Wsz5
+rlALbc;dX?m_yEJbv4LptU>ijv7)5YFCtEJ)RL2Esa6ucv1+H~m&<R{XEPxMoiwc8h@T<5z*~hl-)0^
+H|_iv26qxepgoHBe|<`^13fhAac%0zQT`G68|Kqpa*t%$n#1aa`;aH-T1gyvQ%tjpHIS~#MS!ulN&m(
+g7mR)b=y5<*3%#)>IXK5=^-&IKrNwB$k?U!bz23$umgX<@orh6}TeC0>|rmH{yREMekRZ3z^nyTwzOJ
+uQz3^9jqN!qiyW3)9EaN|>E2O@$d?u@h#n<>m-rsN)4X%g@-GQV?OeD9izt?}Qm^IWEk2%Ryldwd@w=
+aLYDfj<ReP=F^t<gqdo2LzwB7*MymAc~O|TmIcBzSY``ziltDP(<}ut3Y&|Cd<oahxctI0T3E4X&oV?
+<ag>&&zpz4XwFC<*KDoAd3hT?Tb{E#Auyz#I6|lMr>uOk?gcYYISbiUF-3N7LxOErPd11vC49hpd3Ng
+%bP+0ei9(%ZqGPXcjHVKap#r`K@#UZAa<-&Rz*2Th#UdB=?thhtOQYft0fn&)R*2{u^VHJC%MhojzLB
+FtG7xW8jji6sxYX$wniqX*0U088bf~BLdVuPo}RY4i|p<3*P2Tp0SRO3|D&23=4EUa#@o)uPTGcCu3R
+SoMG!s-rdxv=(xb(63@0qfhss)2R6u=>EdSXcvKEfv;aSPO+!2W!5tM!=dbtOH;jog||ii?>0-BOcZW
+Va4fimOx=04r^~=9R+JwVSO4_H<5_jn9;V)&M?B<MC&AW*wN_xTcS9V-cjomE4%z4xu~>G@v_Si$wi}
+enj*Vw7cK=pTHk3cr43~7!liyjF0-&V)Ew19>oio3H%CgzRqHfdb}>pWZM06KWS4Q0i<{QzY1w74<kC
+s&lq$P~N-k=xQ@ZTZOLB47I%Uc(9VM3lty8h=(o}K@);i6SU8;vkXZLHJ=E^P?B$o)S(|p<GnB+1*>$
+FgIsSqv&v0C57GWGu+W<&j8Oji>v9p_-0(Sqn7v391hy~QKLx?nr7g<1$Ll_7TG5bg&oErn+nXj#s#w
+KKFcr8%;-t}e5%W?&wuGM%|y+OjkXiNQyl06)Rrbmq#)<~zyxEpgB96BfTj`Bq><3(Mn)%8kGe;Dja3
+L1~qakUf2XtkqF1&@_^y{h(Cc=DuYE>`Pmk2fBIGczjVh(6NLUKAeODaV<Bu$|o+=Rt+@mRa1tQAz}{
+N0&}E}rknV*zQS~-v-wEH@6t!};^^usrw_49?}pA%ZD^Z-!-oc2jQi_Ee?u?iJ6e@i>Q?nI4)m!-cII
+2cX>_e+hBe>7>9vOD)?%x2Ht!Ut8jI_*lgzutQin?kWEJxf)7hemOU?#;Rg<z;rS*=SCIkar=_gT5KV
+z`o;Y=_HOCU*D0!b#PoHQN%WXID_!PE3pm`fhxu`H5<Ws%8P7AeXt6FqgA*bN$ETvf8q5KUBH!u8PYZ
+1XE}E-P>?D{_r7UNN0@F}Ry6aNY9*=7&n#l@&Oa6*)#5ui-cX2SZb=v_<ugHdYTGlRSFo$54QJ9QOEp
+uz80P;GwzgM1TrQy(9)0uZ^f|uFUC49xW*^Y#xuB(cZF?CWjc3<E>LX-d1x&S|PW2&|Kxaf#@+0Yk~D
+9`)1{arluWsF7G(HSN%e*nWS5CH&xipYa+aM+qtZAbkF~ps(qJwjIDfa9_sFBj&)a6?-1*Q!Djm)J7a
+H()IBK0&e*NldBejM7T2IKdt-ZYY!#H^V01NAIGT1iHal-TYNnas&V?PzkDM`;+wH900h!3)HqV}Lp=
+5p}1Srz!>K)iQYFM3S#-X+3agbbhBmK`P{jH`7`>MHrjXpCMgtbrz`=u<3OO#>0pz@%KaB-;UxUYG|h
+{}(z;lLT$)md>}eGRkN`1lD9^q)@X>C#;<;b1_VCcO%im7U6zg|ex+O&y7J61CK6;MCw21rM6zRB5I_
+W@xG-BD9e$@_uPhUDEn_f;rB$`94D&FrVIHOV|fbZB~P>?#<56b2juKO1csyrag8+Kjt+F`eCxY{l%a
+?WZOx$-9cxJ&2cCB5$}CLXADiT#gOOOSAS!!7>V`6hUYWlXxUhI*XDb1I;*iMt|qTOyTg?wRG*c%9j3
+asagc0h9gdA8FW#+~2>Z|Tf8z552~t4>u>@%ZQwZi0tR&b*aFXCEL6hGY>p~Df5KAzMAd_Gg!HWc+6C
+5WvNANqrgEts+C-5VPC3u=(8o?_B?-3j)xJYn=z`mL>SAs4Cegypph7yb=Fc8cqSWK{jU<1Kkf^!5l1
+P_w*_Xxog1VIE51fvKt31$+!La>ow7r{w_>jd^UDIS5EKts@vAdz4!K_<Zzf_Vh55NsycO>lzXJi+yw
+Qn_vT?as37)IrBi8g;Dy5*>SWqmCUJsk8b$yGO?kbkN-yiJs@bYc&ieda*GC1AZnTY56Mk4+C7n*f{#
+jC--4&D2rmfSb&Y)llie&7Aevh#s;!z7S0B-2;mZ*lNUK9!w{dBGftmR31m?!<5&*MV|x1Q%~Dw={V{
+qsvTSBx85Ba#)C76-hj40<?gOluFo#l3gIEIn*@lsP6Da3_tbZduR3{WGj)jR3jrrplLUd@DH~qo27l
+k8HivIM3Q!bfmf*kra5)HsHPUIL%Hc*r*+|oq4St4vYQIk)$6#C5<sf;CBr;<x5rIAOz*=#($Wm5=HU
+LaDaOAG!P!Y75|Ww2-Da5?1Oj7maDAK_BzcbxFo%Xz9PH={@&{%XoMN8}2>T>8tj#*k9%O+L|rN-5PW
+a?Ka-2sx3`m2d}Lz%EbZWF%LF$sx+sBE3uzUZN~T<gAoaFF_m1GK0eC31>Cy$@-9MCZ+b6C_Qk&I4e%
+J<(blGNo5Nh!9C+d?c@nu^6JHm5IDtA+zdgdQs+v2ds*u{n_?IQe`Jf&8c(#UDTiE&J5H`&DfLV;C2E
+zLl6<mkO0;1<o`1quch<Mwro6)`ouP8Q<XL^B63!8&hW7N|!50nb&{*kEa8k;xF$z)}#P_1ULy2z`H9
+A%<#e0<k$^iJ?*<QVv|KF^?$%4Xrx3k9b^ZOh5flE{WW`4fb{BlK2A>9HBpGUEbB8|pcj?xzEQdH9O`
+5X8ri&XV&iom>&%u&)CYEG%eJMs5qf%mNM9Lh~EV=T4j9N8beV1eMq#?!6ON5BRtjT4xq+VIT1`+H9t
+{%Jg&EV-A<5G^iGq?&5&11A1y`hSOC>gSXy$~#3)T}ijD9imKg1V8i?{b;%vbI{WD*0Pnj;m-D`v8kW
+?#}6SS9z>mGiFZRi{4_T8bN}h4(C>H(p`@692mfkp`s*}N2U6>iMx0!F&k!-yVsy$CE_vcz!9t3YTCX
+e=*{A189d;kF!{6w{!pIad5kf;Iroksp=vXVblldUr$!rbxuXW;*v2-%qlj%lg2Qt?l*RhUd29PPvxc
+o}TaLM6XGCPxb=1ZNBaHf#?D4BDQ>WKdsOC@tE<S;UyA@d0`v9jn+CiVt6k{Ku3MxGd_G5&(KMA7#vy
++Szipg%R0Tz%;(mTY4wCQ5s%puL$~4!y-6W5HBzNO`;nl_ih8Qf#C(QIzIn`t2*%P5m4qtu5kFk#>B&
+)Pe-fJt>A!aOGnnjh@-Y%uKR2!M`2J<IJH6Yhv%}7#SJqgvVDy=^&swAbz;Biur3aN%77kM5|F{O!aj
+7)AUeb=1!qUMIo3oQ>n;M(Vw%5{pZ1>zT>Z__3uu<JO2hy&D`s6RwIA)L(H)Gv%Akf+xRnW{_OAbPl>
+Dk-!A_D1D-qUH2r^?1MKSl?)Rz30d{qN_V@W$#{vJ<zdxfNe{JZ`_RZGszDbtpuK#|&^w!bc&_&#<&W
+dNuG|eiRJ!fv|JoEgr1<x&9^sna^zwqKqFE4rJ)uqc`TfXA;m8({-dE?EsZ@vA_x_96E_xkrYe6VrT<
+}Dw7^zkR3Zr!%Mykf`BU7zjVvv=R;U+h0{@X+BSM~{8^)$y-Sd~@>D>2JUL{><5PKb*gC@kdMLrK-z6
+{e0!>FTY;9{@ab}nw!7hs{P|OXLj}uj!sRSRZW{UZ}C7&7uQw~wr<n*p>}TVJ9O;y@FShok9O(m-mQC
+&o{v55@kFoQeKek4-afv5{sDnO+Tf7Bp}Lf@<5Kl$<I^)HOw7#6&dHsWmv1l@OrA3JnUSMLk4b*|?*7
+kCn_gJ-zfJ%DcK-k6@(=459uXPUe?WB1ld*9F2gMH_k}z~wV$$#tPf6u}cmMw*${%;6HJ5wDhso|J+h
+4S?myunO8y48upR=(qw6QO;vH#1){=AKSqkWxylZ_qPiKbF|Vx!)ZBt;Ej1L-$btWxx55oC)MVWTqhq
+w@56m<f7AVoIh_*!$bq!!k2-#vv>@%Ij}Q8QG~hlM@UnhK!tSxDU(M=M5S=K|jtAot>7GmnA%sq&TrD
+`G&A@hKvGzd`_-0SGY;<QZg)#QcM}I@0T+ri49{30^5do;%QP`JiB;CF$r`Ivx{fXEN<iEL=s&|NeNY
+eMpGPLJeQ=io;?EMlZr`RBcU!n9=i<GJ&WfSQ!GYeU3z-DO65EyH+OPwZasN!0R07#L<yNPU#w7)>@5
+F1!Laru{i&@kQ{_J<-1`XkKC%lV`7%zJVX_$^W`fe%9cFoW29hLO(7<hwy#A!PB#@aaukk2uVPfWE6l
+(!^VubWN)oRDwQ_4G6tPW-h93i8R!##m4iu}ilwXQn3Jw~hm)wxLcDmW^5DflQjD0wUSO8HE+=B?zb<
+f-I?bW+5sL!OwEN;pIbZ0p{!0)-IyHf~8`wv{bbp%mAl6w3BJPRu3i;v@*%Y~ND^EmD3;K1$k3dP+J<
+e4L*gl$8~fFPI~ho+s3C^r&H(N)L;R>yw(=XR62~o~k{JAVNP*pEXu5DtBN`fz>`}oFNC+1bwbSaSSt
+#H|86le9aVT#2B+hMTX_(Wr#XV$r~r?Pd_%#n35-jNy!#g$_MEqU8IS$kPgzIcvF!E;v+8Nu!IzYF)u
+Ymq!E>uA#6j_jd}U9TarFCTQ9#1HA=R)oNVEcV3f@i1L7hc!Xq5Qz+aT}fA_HPC&RMP9t<RaegWSHM5
+P!~GS&LLyqr9BMz%UL#h5)VU7r`E?v^hmt$Bh&5iTQBpQ<+GsB?1l*=qe1{Wzl`Wo#xYV^}sU@Jpl4O
+P!LfPRXS}sT3ZyL!DZ0n=pC$lvH(EUQQN;7SUPPN2R_jCTA?+VC^c^lZ^VjsTtYh)oCL0JN)zY27`?^
+%QvJF38}{1%#3kT2*pSE<roc(dy*d&K;wROc#k8*4SIF1)Z43#`N+CQxBMP9WiwuHprXkzsK@1`>eV@
+EN@&devGyhPL6K8(X;6@2NF_$$v$9giMx};iiHsh-sf>HTmY=R4n?jD68QJ>YO21#1hCVMVBRfSD1d&
+L{_L^kOAw;R1Y^v#Q2@SQ0F2$RiVMs?&<r`A+Y@F{Sxw>)?RLTC0(5v7q@tkcs^}?p=MKRj?%f&uyXj
+HF&JJapawTC)I#vG+zFXcw?=<#R|qKk&|$)Ya1-cj6*#JPJJG*Yj5<BeJRY(u`fr@nw%O-7nppOtHv`
+q;hm8LL+-OqT*V18k=-h$w_CeO3<jZ(Ue7>fyW4xZ6#vNY;Oso&oIdKiJ#4{%4(#orWYtBz(4+9sRn=
+>9~KzT<q{(!G~{Pq7xo83UW^Vs6?vJsiR01g20nMDnBDDH&dUi^e3Z+=ri>x`T9}evC#>_D^~XDotrw
+=CeNpP(cGyIV=E>#juSpNg!!dxD(?&3boccC?vZ=@JFD;QKdj5W{YQ4ax4+iCzW;t3|9^GUu{XNa_dn
+bqe);3|{mUEp$9vy9{=UGw=MOVMtFxAIXR_aoj^n}i&S!eez5TC^xVL}8$ol>#8u*VITZh|38+*fWkE
+GW5J#J%fNOy<qyXW`hBpn+w>F)kt=jqs~ynENnE<=6)hUFPnaPRWCIrHBBM@{wp8`5`T(Y^Bze&OEnT
+Q0eG{C_RIxBt5L>ih3);Ge%<^2hV<f9u%gU#+ifHTGZ}X3YJ1o!d6-Q8~gIeg3o8<-fMqrM;c)c9J=E
+G$#$0hMia%b&}X5N16`X%w<U)ljgT<EejXYkunvgb|7LK^YUo;Q&PO?n3R%Xh|0-Jpy4)CFUK8EA+z;
+)6gxa8J3l8=A8yFYjHKa`r2dCkSbl!S_-r|ap*eL~vhR@t5Dq6H!MX>gjXk-><)j)j^-&~^3{1%)#Mv
+5$!H^{z#eKX(oH5go(Qm3jKQt#PBUK-sPIH16WQwfe!f1Yjv=a=e1E@qGx!T*yGx<n}&y4j#`PdL4h0
+9|W79oq4dO7M47M_`tue?1OIb>jDthZP1)J&SvMA{`7^5{RB@J<*In-mrwEj$j{574LNiiHXGg|JJD5
+I?iXY!ax#^D+#SMP>rwiUkN3=n$(<DbU~Ho*_|@M|CiCYA#A=c7vJ@W5-1L^=lgoH;S}aSKh}Qhh?V=
+GEyU_jH8Jgg`|QvVDh#G<#Bg!hv@tW{aEAp@%p^@JbgYY{0IB!Y(h6BGvk?th=*mTCgf&hhvyh6+$Ew
+JvSEBq254uWW3FfO?&dhekU1=y#JW^=K+q!9CY7$eykZlUnwlplaI51OsXnMgx;Z6L?t+6@4ycfPq|D
+hhlo$L^er%jno+NS_b5UGL><D3#sW;qF^cx%#GW9eMEq53~b0Cp8OGE0gDyXy8cXBZCUWy%RJ~F!?BQ
+GZ#GNn|5)YMuzDNH*w$I8j<q-Y(D1ZST)4z;dd?Sb98uC*6$))lYacoXb)g=&RRqZkwQsnOXq5y}#+G
+atCy@c(BvZGLDv=6TUHg{rcRP1U5tep70b@%lW$V%g)Q;-w;Z9wjg|W86f!yesbPODN%~qTHwE8}wO2
+GpLO6MU9Y+JtwzaI3%%9huEAH;_I<_DS1<g{-c5)hUldtctzrUsm;dIcxCfWl|VI=WEYib%uh#R{fud
+8B<s?wScHm4b+dHX!w}aW7!8VygS{DXH<e>Xs_30k#_QP}l$K5J;YcB5YI5?T^jLe)EA1cwVI#9s2c=
+0|5@TPt&Is&joRwNAwG$efByN?lPGY?rV#DLeQ&f3U<74H2p8q$VavWPOo04Wqeq8Sii#-~4cRJVexB
+mxm`_J?L{|Edu@S^UM_uFreP~Pz+xWY>gvI(6K_W0oD4ID0j!#|;~!LfqS*QjK(scbfr{hHhOJtW74=
+_04=D!XA{(StI;{ZZN7SAGwZ-^1nicsb4x**!rvljQVA$a!bV?pch9-4D<MU@uahjB67)e6fu44B4D1
+`<rC{S#mnKr2{*5UX$Nnm+@IG=l7=UzD36GpzQy(?C<6*m4#Af_}ZhmZX4F$dh8mQf7ShN{(m+6-TeR
+a{Qc+8_D9$^cpT(MiLW}XV>PFAEcaU-%llp@_>IeRdpv5V=iA%n_s++<`-1<O$Mvp{jQw4o{~-kbo1e
+RDizNTMYn%Re@%|^D|JQjye*Q0K1>ECfXYZ)OZF5i0oH|ohDY7@dm(17KNal>?XRy_m=?aU5xt79LtU
+9?$eV24++?Gt8Y;L;G@L{sKnZh4hd46RFc44Ju<;c6IU--JP4JH9-@X0YJ)hzksgp*!(hCg=VyE8W#^
+DLBNVjUGS%iKyGn?+DeFpXdefq@{GAf4c8g5d<Q1UiBMf+q-i61Wqn2|5wD5wsz2C1^pQB5)*N1hp0&
+yG~#sI74v4B85Lp=3atwf{g_02v!g*CRj)?pP-l^li+EBrjB)E_;7lUCm2AWBM2bS5cEXa1S*1>A9d_
+J!C``Of^`I|36>HpCYVn!i(ndofgqD0m0%RXP=Z*3V1k|mod{gz@QwsE7l|H%F9~)LtS4AXu$W*z!4!
+gcf?xtX|6Q^NZUp)#-|g6)xZ)59<-vGebC&4)f#|2WxETnKfyQv#IbLrWbQE`J$3KT6`H7(L!#my`fQ
+y}+wk@8wZDa@WJn7Wr&eHnN^H(2f&z*yqZ}p_<NJ}Q>D#e&{&6TDnHsPE_ova{t@WO{=BHm##;cp=m^
+E3)njLLB%6LT?*UBh(j#EypnjkqKIryFs{GsT7>Y~APH#r@^GxUap7`-gXN-+LGL6L)d9G~$lFt%lrH
+UUm!@UY(aWJ6`p%V>m%Zd3W=(V{27)@8x~%Sh`c)d(Q~+?^gGIID_6@>)xk4L*X6k-p>@;iG6I!`+y=
+l_LNP2IDS}7pc7asJ1`r6diU<l`uFe8l9G~GMn(oR8jVs#6&KTgF?;#tm)V<dzR5oN=p$BMUe3P$`fG
+Ok_H8CN-C_}#UT~ZbO+=sDU)+MZ?3q0Ika#`BuhbT|sGU4{+B5{X!uQu6;`^(~dk?*<=;aE<rT58`@o
+qV^fB$~*E~XVK`nkeu(}e%zX;)R3DKg(JzfV4-szLm;v|8!C2;ui1N=rMmM}DuB{4>eD7VngPE&1c2F
+hx@O(tBpL2)}>-q1}>0wMhSa<S)Hb{+yc+L1_v%;8%O-=J)h=1O5X4nYHuw*Y3Z8H1K|V$f#%vP@75V
+)4S8B``SFVbpIhDAr0lDI`Qs|)>ouC6b?R8{ZZo8Dt7-N3NatWrK)}F70&=EfOxMtaA<Xxb^AqF0J&c
+d3+6QxU915R{s&hL-Ct9CsJL8&Kg9VT!?xGd3U7p`@~vE2!-cmfe<Wd%yb)fU3voj77U>JzByXmgD?g
+GqtL<D~bjG1Llvtp%4LA_k6*H-$xeM6I;cVlD-UW&Xcrmjha3F9da3Sc-!RM|rum2gJ`}p{<xVSi$kd
+VNhdFGjVJic(@LiYCCZ?laXHwqp<e*8GQaN&ZL&vUWdvYz_T^;?`#6VK*8%QvTOXGPnuY)_k*!-}Yi(
+zbJ6w7n{sIALPZ&%Y&aCQH$47e;b^D{cC|3&VyD+nyHd^A4HG6JsU~;(SvwFD&h`edMrYR>W6r-!u&A
+eY61S7jgbmb@h5CxcFJd3Oje^uFjn~bC7}E1C%h%GPp?Pgfh~Ja=Q1|Tiv>KV;&wJOrz1TkdP1-78b^
+$qoYOH4IVt0B_<}a5hF&h(W6JR!qg}>e*AbgVZsEKm6gTv^72IgF@5@U_PQ~Yz4J^6`=C(EOyeWj{E6
+XgaZVU}HBZMrnGwX^Eskcd%oxkIyqwE+Klda%ylNurxs9_Yc5vpoo3qeeocVsvS@1#5MjhZR{7cTBJj
+vPM?>WmS7<P`ckw0=a?hI#RFLO3y#tc?kTFMqJTEt#@=_R&g$r847=~A|0#R|4&%^Fd^>({Smn>K9{e
+c7j<e#(yRT+e3y!r7*)oPGA$XKdfTeeA%21MJ9=BkapBzhoy*o@C#C`z^b2;RHMOD`#iVo)vsiSy{=h
+U%AA7xXIbot5;cdb+zCXJobHPe28bP2{DaoA!E&mZ<<qMZ%GYU%^zeT+>?#ugV|I*iM`Ahu}%Da_9Z`
+}^rx&1#qUD#H55OD;>S|_5fpzs#h=1kv3V4KDaC(};+IqWqZI$VP5kZ@Uq|tuqWA`izkuSuMe%n~{38
+_q1jYZ3;{QPLD=GdJihsi<exL4)#gllLOVgKy2FBJepgGN3G^XvOMtHOp<0m{B|8_9r=O;0KX%XXB-)
+H=WP5d?#zYE3JQ2f3WKaS#4T30hD{#1%TkK!+*`2VK(6%_v{#lK(^pM;NiYRb7JY~a4I_01@L3!I-p@
+zoD9-k0K!qWI5H{Ff;HW{Ur%O?=jp#j|$QW*=n>nFlpnKX!;X|0;`Sywy{Td*(7ecsApcmNLF*1LN=S
+W&FtZHu2RI-;d(QQv9(Le+tE4K=D^o{0}JpHj2N8;!~cBPE-5~6#uF<{u7kKlaxXRr7(w5SVJl7q7=?
+j3Rhck_Nymn*9UWUV-jc8i#WUaK4-U%aBKXI6u%e652g4+DE>H#UqJEaQv4+p{|$=2k>c;5_=hO|X^L
+NE6JI5lKwFC6k>Wo}@w-#}Cn$bjia(s<PontqDgGN2e>=rLX%oNxlPo>9PT}GG!uo}Y^?ermc+YO`UA
+lDdZ}ko97ZV*F5fKv}8X6Xn+^c7g9^Ksg_wTBt5D`t@^dC)@h=^p5#}J@@f3@r%6BiyC6H3WPhKGgr3
+rl_i0lIbT@~G?|9j6P62@8)1r})6D_Y)o-eUs~ujERd;o~Vv)?UH-<mQrx<@~FCNf5fNsV`8F1W0D_k
+-QJD-DL_v-K=)*d9~y?p;W5b_Teog6`3oG~yQm4tXlwe(ZQHbN{bYy;;2{D$sz&_q2>6D<zfJ3hpOjI
+HeH=MZ{N&KExPdV-kuk{~kU)F4cI_T&(V|7CSmXfzIPp;W9qI*0MlrxYJTfjiCMGU2u5-Nr<lp0Qk$%
+6ph?vN@h`6XnI<%Do!2hwHy<(Y^ez?d&DYfKa1dz*5r0=PXqau!rjENIOh#ZviC;XEmJas@JhSKN|71
+g?RYYJfXPxfn)tm_j=84M)<N1~zx3Z1O};Vw=t0WCF=F%fZ8%2AyK5f4Yn>=7Ci7Ut#P;-m>pj!TY<j
++C*CQ@ATS+^1i7A4j`p17#1CRdj@PrW>6c*vGYL6Fa+B0|-N)7#9;tj8x|z=I^WVZt9?VFq%BW`w=hu
+_=Jbo`6nm$3mc?u>Fnf3*6?Jc9vxAae{wj24AMSe@2rD=cqp-J46E};`4EHYnzw2>C^<YXR7<?Zm~Hy
+fGr+Y~U|blLW3-Jo(vJ|{<nHesu8pqmE%6VfA3=;59qAj?z}qH$Dw^o9pa$M7rv4Mxz?<{(q8gXt&@&
+`Rl>amGTu~#?s4Q2^LL10)_m)!>d2Yy%AwnK0)c3c_9&b+R%jRZ8vX}G1*~XcXZ2c>R%zrP*6x7cR_?
+olCbCS$3h0GUUe38BS>Z@$ovSsY`*I#F=SFaYb!UrFGAmqW1KmJ(A3SX7K$zCOSV?D_VyLRnj2M-<;v
+cdUt-?MY)&aw07&$FtkDt7tuW%lbYSA=Zv+i$<IKmPcGEvn}1eR@A~%ch_0^C%5;{xr~0yIeSq2D+&<
+&^<>3-5abG-^e`qHa3{=Ws~?Zwuql*@AC^ZeqXhTPXl5+&FFJ!uwD2B#rLE5I*K1d@kdhpNfdt$#a~A
+8H&Fb26u)kq`;VORA35d!Ryn1E+y`{%&_OKyw-;K+4sPB0^y!1kJUXa5ba3nN=;NJTTef_J_^f@Gp54
+0j=+>u|tIJ~#(0hkRyY}coVOzO&?&HzNgLUfic+Wl$H|x`i0<?VKfk!$$+O21w=FNIOB0H$nJs<C*Qn
+lzwaUOfH<pUku+CAQ<xk}ZnsgqNS$6Q=GI5z3<_`}Ur&EU|-wR2l%JLfKa@I2hq3F$xNX5X6Lns;s1w
+5bQRuSdIebm&CB6y;%hr~ExSwdv8-qfZ|XT$)JvKknh-?&0yMhoG+$J<vft`lzJoC~{#e7>jTYGTd#U
++Ri2n!egUEXK8&1*L6KDf~fD=&)&zQZ0TrX15R?T{}a#OS*8cR*h0~^bLY+|Q~*Aa>)y0!Q|ej7alKS
+h;>a25Da1AmrNsZK$3afF#eIfHxng((`>U5PU!Fwc&L>x{T)Fu3&p-c2W6!yte){PGncrW(e*Fr~OV6
+J^efs@9d-j;^?Cd;(gM<6XG`Y#?U`q<h;jfe*>KX&{gWbD#?@A`hs^xvgbsITyWT+oKWbgR(*Iz$J^J
+u<(`*!~0k3Vu6Px<lV$FV@gzyJPwPV;XLf1-iYIQy-|VyPrr&mKK`^tFnLilTn~`gt>{p0EX{9h0`m0
+2kD2GZ|o4wgh48&BF+rm6hdPQ&aOhLpON;__wF>K16NM2Wj~G`*#O^gqOho^Upu$AAR%@C;a$_AATtC
+zHs3Jzj*N?KYaKwKXT-V2!}W%Bf$MU#aaIT`|nTRvSrJ364@e6CextY+}wx-3l_waTn*f?J;<Y7yLR5
+CrKL}Kd3mAi+#(_(dLrGwivP81*Eq>Ci@J90>Puy~;L@c_yu7^JTF!?K9pc}7^Nqlh>PO%X`<XLm_^D
+H;#5-tGfMgzF63<=#<(FS-i0^JunwH;x|DFH(>#x_o`s%BXe)!>s?<udF2uE~2PwmZ@a9Du2e-(d{r;
+wKwfWHEuK>^f>0>EFPL$NE+kQN09XDd*hTfx>H@OSXvxpU`ys>d$G6SF|WjvYG${@{<3Cr^qxID7W2c
+n6MX6V!%<dF<FR0cabj516Pov>n9x;)^c?{-{H0{~Yyj`}S>a`_TI>a<7FywPCNng8z*hH#iMLg8u^o
+0=k2b0l@#jfdl-LPd?#P=bU6_(S}j}sE>X7_6Y#)d-v`YZRNy?6T%J}&<B9u&`wZCz-{x-oa;|=9(s^
+-pM9M7`3(0AbKbk0^C6#e{`LjVf4gzx7`2s7e+B<-+qNxocX#(C8GZrvm#7ax1Mo$eqg+7`>H@gKj=K
+NuyYB?RjxtAi17Gj~XaU|Rf7J2H%bZ6X=UjJ`bL}C{{r7Y3y_aa%#rfkzL-(zmclns}1*bW$t*u@9SM
+aC4-U`590i`Y!_&e<c?Ew6e^d0B@PjDXo6_r0|pgQpTf^#pTA^U62Z`5+0c7$`yHmhplzVNTEt_Cm9_
+xASgLcA~wG*CU<K?`sJ9|6b4=s>xn%uz?+8}L8+U7E8Aga#4*Po9!!==UY(eF@hfqQRH=CijGdf5tJ+
+-9N3rf9=okr!j*o{Xh7h#v=}BSSJdfqg+uJ;4AQkLI>J7_zZlHcA=PP7kCoRaz2n~h#~%u`dXqP<S^%
+f2P7Ia)E1uD!FkW^e;t47ix+k4*3FmdVFB71`j*D%0I%PTzfk`0$GCua=x1!7#PiffLBqG4M-vT^M8o
+=@`29S;5WI3X{BPd8$!VOa2cSU#g^tGRq%k^f-MYn(e6)<OnHIxenH0)j%+U!Xp1R2Supc-de1`Kl(D
+04io{49ImG-<(YR~ACx^I)(bFWXOHcdV5{=4B%^*rCl$EOSR=d*x6_@p5n;Ct|OLpl^%!0X_n@Aq%yZ
+%z*8tBfK1wfw$<20WuHIUga@5dS^rv4qorZ*1B#Xpq};Z}dsjr*@-0=~1Gg^M_L3O3#eO@bA*4i<SRr
+jK?^CM}46EVmwgl0__3s3Jt)Y`aNF$QYwFEYA}CeatL2ZG%O<;@T5@xIf`f)PIwGGC()p^XV9RuXY@&
+)d#K!Za{d@-pz>FLMD2gCNakQ;_)~u`_@CO^0?0#Xa|#^_EuaD8!QFTZ_@kfPG$V|^_pFw`O*E__8eT
+U@_~&2Y{OKy9!NPgc1*tuwPl`EJ*PcOx(w;$s(w@;L_54I6Q{51Mv@?t^BxhLxG$_!Jj>g)ELJOWPMS
+*<7v>^W8Gg|)E6r!OZg#UK+694*liH2mNVdRfQgH3ymk=t|FF{w`qI3SJBN}tq&Xz2P$qy3V=pXB@bz
+P`R)NJg0j{LwGM1kZp5$UF)yaKl)u$Yt=ucng^U&&S1qd~;zSf1hZ0muOf^G<>=whhM)^d0(APCDEBh
+brPVoXIaMRv8{3Z(O)S19~2bS9d)4q+AHt|Zoplk1${sId(3OlZ(^*%*oJ4@%mDro(Xgp7h_9cf<?D!
+shVA(U>XQ~_h4J|l`|-IM;oLMnoEN1=aI^6#{@vGy1r4?`MsHchxEub|-wXaHd=>z2-~|&n0dKSi;H}
+gN`gmLU7~?N!1pn`x+m~-88a^Z%HclrRs4bv9gNBtd4NGJi7J`Ne{rH@8q9HAU7p6uE8t|Y`3bK(gd_
+QZP|Iue)41*j2Si5#Df9IWdIOHM)Z0qD+?c|R?{@{mSOW_qJfBxwVqJi4N2Smfa<@Ss|>2+gYzAUdVe
+>pdlFV5ES=Q6{%nP`|zG|VI#rV|Z#8p;@Bjy2x@0Ds~I!T-e9vp~!2*|Yh&b?XE@_d|z53*uUie86|i
+^5^A5!zV<8XwO6g`Xr@2ub@6@X<jIQDObmzCmI$I4fBYG65^v7dT|w~=##>1WQ-508t?yszrz1ot+qR
+G0Dg1l&b89ekdAxN^2Z;w{A|T5{ImJte7h-te<HW%4L0q0wM@gSG7bMCK3Xs_jF)CeG~f|3293|L1j~
+LD<L?*j&Zj-<oW|cpg9i_e$<EIH9Jr!Aq74)l777|{`RmW>1SZ;4!#P%C&)2)&v&tA~&t-CZZt&bw{s
+I1|`vC(6pdR>JZ@ndCFUT^brKP-}pukGQz38}Co!~*Aw6X9$+B4D6NI!G8r#;GP;lhO#)OYe8J$mru<
+Yc~P%^HqzIX^$28w>_MfBt;YuD|{EThZ@87HW(Z;Ek~XeHCOO$fKAK;6Zyn^oEhw{!a6aRRz}etjHKk
+<o3Lf+QOgV|LLcnx{$0hRKdSjuU>v>X=&N1si{@ekBWL&xpE~RG-wb{NJ!viWo7)u7hmLy7cUn0V15s
+LK?BADj1vkSn8Sbu^rzrMjD^T!mpOtTeG~ZK;GeH9sFN`YBpCzk8GX_#Q{sUBpU%I6gM&-s;^Jn2e~8
+x?(Y)Vx)22=9s10-S<5Q+g;nXHL-k*H(Ng*SA_St8GAAmb>MI9-8rPK%L1Mgv;1Gx@$f%bqlE!s2HgK
+c|$t3i9-zhyb(z*p~wc>)6i7cW_|M681#Z@De=;^JbSn3%}BcJ0a^d+aezvbDekc%W_A0%%a6AuRy#l
+#(|3)&tAN@=r^2G(Jl*2FB-irib$_&yVM)zSzuf{Px>1s_*+=e|Y)jm-(z&v#u>#w204~IrF!to_dOB
+W@d_Vr+KB|CD4F&0Xl&j=x9g_)*vtrgYPj$qpe^)0`o|?K}JH|ftE%dBLC)hD5Jl+{)5QWc30%)=6?U
+|v(MfJo=Hhb0??)v8XkZAaXxwSWKj<oGnBO*g%02g{FOCd%ne|oJ)n)CJv40Z7_)8X9+*F~zqtN}wgv
+ttUV44Tj2T~|oyNq(@M+VgiFyDHz`tL=eth`w;ldqt02&kk4a&S1b1}%*;CtW+6FdakY<UYZ6~a?r@-
+KIHcfXArH$I0s#NWXmya@m=VZ9k{zzb{6Mx#-*-|+BouG8sw@7}#dJ-{Dv8q(1iZz*j<F~LXZ55QOGk
+5PBZ`Uc?yb=&>wF9iNX_k2G;zb;gUv(TQmZrv(qK>1+ZklKf!Atfb+$HvAA-deqSwZI=`jWUNFc%vPl
+|A9MXQ}7RDZL|Zl0kjFEi$0iezfI}Pa&vRjQN6F+wQCoyD22|)`F`<7zP9p@Qmz2Z7l9AeEhpImyNX4
+7p!(*-Z-NIfMxl(AILdk=`cafA`u`0ZHt_e}drypk|Ni&CMLd)>m6L_~w$U_3P9xlRkQ=TQhu-6U^*2
+I(gZzX3pW5RByraFr1TLs=v{~X)AxmUsWr@C(+PWCu(AR*!6+T2eK)XbpfKN~d=#y>bA=r^F>In6L@t
+o+z7h32yo$eENEB>~;YRkj6ybaty7kCJ5Va}X6ocb&=PTM}fO##F~KZCvq5BLXjA^>m)UCWm*#~R}bd
+wcu8in|qmMgFJqo&_3|ez7q+P~PZ+XzUjA1n?Bb+D6v&1^%cTMTUT02Ye4+LYboty^PKLf4$u6;cv_T
+=p!1Au?=Z~i8c07qek(UUV2IJw{5vAfN<a?@H*Zx1^{<OKSFZtvi}0^0)N~2FO5A5z?T?<K!>v4kNyL
+F9mXSDI#9NoH*e;zyz+|RXQd4TK)13s54j2N;2A}KKz-($4h|0g72K`(+m4I2GK{TkbFVr9tXQ!^#0O
+mn588k)cnf_eXi;z{`Sgvy$?tdI4_v5y3I3<?dzLc)x)&|Vnx~?_L*2j)^`PkFP-p0ipi6)b3bX)s%H
+!|!Gxx;bcKt`0OWmtZAm2m&!8`PF0Pq&f-Me>-eivnc_6mLn{%8lpkE{Pd+y(x&>z~w5Sl53*1IA9sF
+X;ck2N;hqcA|_i=S10~9+dT6)C1np=cBJhKSpy3q1Paq|7qL>{@~qv@%i2O-L@}+zp`$Dat4jyb>gGj
+BqLa;t}w24_=j=FgX2gZ`fmp0fqf7E$k9E#m<E^alPA|QXMQ_$^5ip&g<h3Rvi-xRgKXN#Ci{m>y9Vj
+o<XbG8<^Kf!MY72y(@r)WWYbwTU1YPfY=+8ak!*6ww1ZjyyB#(A8xQvPILqL~?jb8;y^mlY!Hov<MZ8
+0!(r`WSZV&QuBiUC&V}6WYuOCeEegyUT*oKX5XOOwuG8;Zc+TYf2UT}`{0mmgB8`grMQ}Ei!`KW`OAO
+BV0axWY}YbPfsCx4pTO`<kkgZ_+U&floNt)zbGGWAO*Nj_gmvh4(tf$<H4CqWmIH?E97FX_p!779I|R
+$j~f@*3xde&IZHm&pI;d*uHx_2H{1&l+2Oy0X_qSwEL${@VoCsLwq{{pQC=7&=qv8=>cdeg$jdSd0DY
+I_HOfy)RvFXY|uEXU<Gnym)aUaDeQD`2)sB^y?Ul&{sp2g`5IE*zt_C)aeVM<AJ^tYueE7U`-x6wm-`
+sa{!uyX48COf-=9yJQw*wjz*a%a~{Y|wh#1WPyHzA`=Be^c(t*mi+azM2K*rMx83{DaL)tE1mo9F7f$
+jwO^IS1bAh}DIQA#blP^hn(}Z)9-dEW_V9Srt4?)iuOy*bhd9pLc6q>)Lker)nyAK2P4;dJ^VEp-HNt
+Rdx!+J2*<dysZV=hU0Pw3kOKc0|uZP4+6AEBdyt`mB~5nqU?dzJh#j?;WUd;0Y06KwU}iaZMwvh{m~;
+bJWj>$zA*w9Oy7SLlzE8t|h|*3m%M2OSgm(POLBKPdS_{;}1SHq^fZ7d-1_{luyQX+8G(l}miaFZFQ%
+KPEQd$52^k>MiqQ13jI{pXNy^OO`B2w8aB?gU?~&fxZoEs?aZBO?dN5xqqrNy!1Qgd4x;I5lPpEHUWL
+7ZT^rercRxjT~t&w!B+PQ9I$SJvcQA&b?BPjmGynlGhsax{0JQrbQ>mFzcg)}1TW6a5I6{aJY1*yZkT
+^VU2sDkFzUZzPN>j#L05z|O{|S8{D^gN=vSbd5IQb7|LLhwBCq~7{1~#oe*HrxD=jTeS-Ny-BHAM6`;
+gB;H*i52z&yGtL+A~mufn?OJM!B0a#_ECHUa$zbcMzG2x}SOvF)RwyS3$iw5?~Jc_y3s{|SnI0P_OO?
+NBD@dr%hFE#L6ZpN|)M0<2H3m)F-<+q4Pjnar}DtVqTIkKjkjcND?t>iX?pnV+E@Lgu&Cv)JnHP)>iQ
+Gdi~8-$KU%UEM6gp-|Q}+B|pkf5_i#HmAJy+G~l>4M83U9?HC0**6G0P!{Nq;D-nL+xMQSYZHy}qYbY
+$+W$kwo;Gb-_MAC$vQQ4sJ@=gG_t5WR>_h(#UO;((7m>HE&gtiKU-F;M9j7+YfFCo%th%y$<&QB1>l|
+3qMHyp$MQzc#mflborqm7U;<H8Z{Ob8H`IR4z*YTsFjvrqw9D(c`9senPdnNzG#Kg0qp`l{k6}st+j0
+`?+-aIkC15Qd`p!AifAC%2C%SnFl^@;o=Q<&Hbpz!0$sZo5x3%R`N{AtebBLDR5+t*vC(@g~(g@uJVW
+5<r&qtR&i#EBEdI`D!83&h%mGWS85!$kQjdmB+64aa0WNRz@HCEmKH{MV~juVK{g#XJyWJ@hirEs>nX
+M~)mR=)T)pbHln(#&KnDB_)r2&-ebopUNM3ll*ZBx<srSM@L7C^;h69Y0@N7?&uE`9f`sN${Mo54>V5
+HW@cuNCtl7c83`YDW6u9)bE?iLD=Pbi>FMdaq2s|C{jg!fIO+oWH5!+#`T~@@!iUNp5yhmj>US#7XDO
+}aG(Z1|NuO-|+0$8B<3}2h?NHuW%LNZ&EggCdv=3W70_p($0^$J|lm+^2^qIhO`}Xb6vHR0ei1t5!{`
+{2JUw=JOnRBC_fy4Om<GF{22Ol_apukO8_fzyk2nW7V`U~t!+^}H-j>K$xA9;)Xktg~elgTsz>0wPCV
+?O#E=oUtg9xZgtXcq{J2W6nF@j_mPP6cv1(XjL{<ZaEr;To3hngqt9yu3WoH(;--vaX2wLRo`u^vTDL
+9ed+1<}LC^KGfHU{=YFi6dCya_uuEsmMs%&#poNc=A`Hp|7PAIf8?W#zu+a%tH|JJdx~BXIDp5X<G}j
+UmMvS9eS&{CZ!7)xvj&QO27MpudHwqJ;J=@!EJ~TQe)4x8d~4$RHv#SI&-%B!KDC{TL(kaN`}P*_XW#
+7Fe!FXp$0TTY{>oqYZdZL*JZU`ad^bPh$MEILmv3CTa^(%`qkdkqX3f9ec;k(F{m!~|>#k55Id8xHws
+5B~j;mI!ie9l|#kP0feOJ_{GKQg^sZAfZjSqcVMMVYHS#MDqXNdo<qa9*y41Rr&<{$~Dq<v7KBwxRJP
+9(KOiI1@0X~=MjJfpNJ^kvDF(wqtM=}{7EeD>6nj~T{b<b}4T%!zDe!IYn*Iq;TW?-+)EBiM|-3GE2D
+D&Kb;{&Y28lq1Q8<K(#$=8u@aVy=L>iiYM&O9(IMS}`XF?%;pq`&MDJka4cj9PHKWQW}_xWA25yALf0
+ScLvbB6EX~BD&UQN3lHWnkQYCAVUkG0_Nlrht?6JL0dqE#AL<EXkg^B$li86%-h#XbSr4)%WL?ZR)-5
+d+X<*JVfbc;5jTkXP=(*8{p&ZWadYx}B4iWQ7$nKEaF>k=!X}Z3@$Rpu<&eu}8y!z^^LLS5(5$M%Wm*
+AI%viRYO^+N8SKF%t0gV;?dKN=TAzXhEf_75myDW3NW`|?X?j*0Tb9I7Nk<Zr#v2E2#99(;<r95Q5xC
+{y&)sLStmtmGSJ_80p<mrZ$+fBx~Ch`&jRzirz#A*(6-anNr==0bg-KefdLc8uk={EzTT{VRKtkQT;C
+<d5+P^8}=gewoJpBHQ@rr-3)pL0Ldo1e!2rV?7CRG0(=>y=&L5dU5d-I<E_JC14IRuzP?K##sgzxZ5#
+jrdGR()1GkRnjKDb6>m6i&7R?$R-7Dw)7BgqPIbi_&RTP18jaLD;iNSuIiy;gv(`iosTQZKIkVuD{Cs
+`Z*vzTwDOs7>`5|45dD%hv<I?q6Dfzv!GREcQ<maRrdX3A;3QEb(>RsU3MV*zBosp)`Hzexw@-uR>L%
+MkO)^zFnKr^*E*pO$;7k8`NXKLP3Jc^mGA7{+VFie%-$t6!e$w;~AQ{(e83NkYF<MsJgPh0Ouad90X6
+00xJXR0&t8`32uKRUZ0XQDo@i`tkGHVzj|gmg(u$;{Vx=^NbVE|Kdp?{oLugZtFOHMozJXnGIsqm*ag
+_^|k}h=}Nc{f7^iE&qA{=iV_0<HM6?nlQ~%nrAd+nvI%mn&X<cJ)3!T^@{Tv>y_;_$7`|I+g^LTzVrI
+c%gy^??@;fd-jlo+crWvQ$9s?WVegCHKY3SsJNP{4)6VBHAAg@<pJ#nu@%hB(mQPb(jc<zY1m8)%vwc
+_ke&~C}x3ynazjVJ>{Vw{|`aS2r!T*r|_x_^-Is~c%y#f~p?hM=)cp|V}(8EFgK~X^oLCHZmK?Ol2L5
+qT33)&I%Wze~x>L5F95A9Rh3~iqFMeS$W(S22+(?U(5YeLtBLX5z|j=yG*rcyK7^AFF<USD~~`Q-T&`
+ONou*=M!S+diNA?D9F}^N?>x-)_FizNx+w3Ac;BmwkWrt?|9>>*&|q??J!zermrSe!cyC{X+aA{o?#a
+_>J`&@0aB_*{|5o?Dvx2QonV6@B3}>+v<1B@1$R)-x~oZ0;&TZ3Y;3aG;kYHzyn=^0)t|Ll7hwrWd{`
+#{$B<C7_>)wMEjk#Q*hT{O|V~Ze(-a_8-hOzwhMVABrhbk@6f)PedqN3uCG&Qt59e>FaUJY#A!xomTT
+5%{;jcSe%4fLZfn$@FL*BVT<`gbXSwH5&(oe~iAra$hrN1w^`qJv=B4+V=rzr2lh<~y3tpGKns`6p9p
+HVE>W6zr`Hb;N_c8gHeHQsF_c`Ws#^*Pm?!Mu^&-rfkJ@5OAZ?*5Ee*KBs$$p>u?esh0*V+Fu|Dpbw{
+`36b^Z(BOs{bPaVFCRECIvhbFgsvt!0v#f0nG#3P`w2PJ{34Fa9-d`ME%==B|&S0J_tG<)J>bI&DT!X
+KBs*{yHWeG_FL_F?H}4!!R><8!QR11!7l|@1ve+UjeReNUJGSHVTD_(3p9nA=QXcs0>OFSCEgu<VttP
+I&hcC17fINB;eW#a-GD6tNr5i}?g_jS7_Yskt<ioIJSyawkQE`UuS?&@`!aDk1MX%G_4M`H=iS05z-N
+R{h~EnVO9GY!ydJP7;H`jn1Ktm~7;r71bzrZ+<iI(BZG(mcjS4aZEekqC9ClQDLhBI{MD;Q)WNXOYkf
+R|#g!~-xUg-AFJ)s9ekA|KAzE4Ya6rqXHq-hptPHHY{e$)J}>FBBU4D%e|In?th&os|WPow9vp0hj`d
+cNrSn&)b&x6PhAJr8&u^ZeG6d$#ck@_NRrnA+BQugzZjz0P|z^Pc7XruX~amEOO22l?oH5`9McjPuFx
+$@MAlnJ((?Up_BU?XC6M=<|`!Hsa;IJ_o5Tzw!Cb=O-Tr-=@B;d>`_C#J8)j2emwZ-zeWFeFytK<@=0
+pvG2>iANzjmd&&0(@p%*Ca;;yu-$1__|0w}K1l$N{8`vW-C@?l~e&D*m8NsW9PX{}MbPeeh5)qOSGCw
+4+?+DPG%UC%Xdo>3%-)sKRwDN4{Ip6aa;y|O<b6zideeCs%SG89yQQ_j<#=E`uW8TBP$9SiCzw5oh`!
+Z2f<Nb%Xqfb-nC8qey@R{S|LYVgU_4N($eb;w`?<c;W`~F1ju-dn!Uz%U0U!I@4e}DfW{=@y#{j>ZF{
+b&2n_kYoUss9`P>-;zSZ}<P4`i>L+=lp;2?-|f9U{t`EAbrrppo*Y9LC1ocX}f5{wJWue!TR7~A%!7d
+heQJZVo5LBS#yQjk)P*esugc)F=bxP-k#orz2|r@@_y3iXP*|nUcN8-zUTX;@8y6W0u9<N+8?xz!9Rx
+Ja`<%=KS7hE8L3IujMI$QOw{CPwrF-~zSZ2)_<JUMR(i&H<$C?#rS|sse$(eepI?1A^@8nu)xMAWj`7
+X#&G(&8X}{%rfclz1zgTK(-}rU$U*x~uzgvK3Ktw<ywXmrHGXh>F4m}WXJm7S|g@8)|Hv`mx(bUee13
+wOI8Z<a)O;9@Zel3H01_uV`1-}`*H~3PpH*stCzMg%*>KhJPS4d}dRBO(8{_gpRR}bHbesB0!`nL`64
+^Ri0srPxF+Tg3f%c-v44E`keVDR_Bjv=8TLqaBm%nSK8#HsHieS`Xr@4L9~^1cW9p6YwCZ}-rEP($dF
+&^JO4BK`Fu3&u1mO_b(s&1ubbDgg&im1hf2SI<ByiRr}mbBR9|dM@^S*>kDq3eQWPS3R$L)_B%>j`hm
+&noB(M|Fw7iEKUSr7~g}4DN<Y!GzTeCqzIT|cV@G@&OS4n!Xib)AQll)Y!NizAt$IDctzk2Nf9AMiU<
+dnB1OOyK_XmnLW(OC4m?odiYp=<x<!hBDXxg%n@gpiVV@5$@BDe*-@`OBZF0>%`^bK=4W8m@p5a-pbC
+Z|_{+c&=i??})9}u}e<i6(a+YjyU_PK34Rp-6)>b4^eXeW-uXAuaKSaSnE;mci_Tryz1I_v9fgWY11-
+D58($KJAcRP77<!G5!A{3bcn<#+jG^QrkK_r!i?zqEJkH})e(yL!xc`8RISC{Zt$jfzpDZeFgUDcWLR
+^u$1nsP?fqp<Fr_7h)>rVj&`K%#FKAH|=I!)1?6>t2-`KUImm@8L!~gyoT5F`raom@U+=#o>4ucn|ev
+#(p&maKc)S=)FZ?a7<sDg8+EEYHYSF~QY_DWqDGeuuAYBi)8cEZbMUPyW$UKZvf5UMQmSk9tUhJc&>C
+5R2;8YVbLYg%g{vWgC}LPg90??e$!YRZmXgVY10at(D4>WE9-xd0d{j}xCbsY#+t@)JduX7E7P{!6j{
+$}l;Sfg{<Cxrdf>V6M8Bt_{3j~;AhB+>=fF>g{Dr0h8#$`e#WlE-HMrNfhP3cI;JQ1fL`|^;udnN-JQ
+wfz+DV0_krK^G}suE>oSyhy;s;Wk*xvh3oUG1rcYN{6bwWI#OLxc(yDpaUYq5johP)h>@6aWAK2mtey
+7DsGqy$n^j0001f0RS5S003}la4%nWWo~3|axY|Qb98KJVlQ+yG%jU$W#qjJcvRIDFnT7FNhUAOAPfW
+$i7;q1KBB>zI7H{bjGU1fi4`<eP&62+pwe(gv4SL?OwF*J7F%t#Pg`5HRqLZwUMiUc^57Zp6+i_Q;S2
+{f_(&!^a_(AdpP7VL+wb1*-tW8L&qrp?+3&sf+UvE~+7rBGxnLCpAszk<Ll9O9{GUho@Bb3;H)zc2LB
+hYYHlMrF64-q1^tnG?Sg@ex{<~}LxVNC{j{EMrUn#iru7aA-eFZ<hufRL)#)5nApL5qGd3o6`vyF5A_
+>brAGS+n^|GHnF*)<2gAG!1Xu7~OSzAig`-_kW7zUM!7bJuJtyS}TO((f_L-)4UQh`xXL<EpvX-kHW;
+;S+>8Kg$rF_;m1$WLlRnR>(=q7KAPE@-j~=ErbsT2Z=>6pVI`vh95%zx3I@TKz!l$G=Qh4E?Fu0b?W;
+GkHW-Ui!c!%7FdM8R>1dci(t<lP@uyi%)tSCAqZ#x*OeLYZ;v1}q@6kY66LN%3Vcu4WDW@Do_=DP0vO
+FDHFNGz?hu4eC-63fAHd)C6U%wv|0TSbP+y7_VZcM-+ip3zT;nARctM&Y%vH#R@3T)S=eeY2VNDf06H
+EX$!VlrQ@O0(wntwkOya<uGgRh0~(^JY#Ix{5x&-@o|58WGJANwOWI)ahO`N7E40=e#>4A7IA?S&y0K
+`vP*N9tY0kP=`URwG>f(aHJV=p;jq23*B*#Oo^1zP~~cwAndOChCQ>s61PaI$t{oiGKp5T7~OiIG1k~
+giXzxHp!8hF1s92T#lP%^<V+BT{;f>VMk<kxMWv=wFjb;j*0Jf_*uJlZXR~qCWjl9(ae@f<+ey}i(mB
+a@Eb9I5ft{89n(8xrnpLF)-1D`(7(E_;Pz(f$ASIi%aO&df*I0uX@+!@bhFG3Lcg8>iwB~%A{Zo$b?@
+cSbTnA(E7==}+*PdYwF`p8Hia7@&jCfmCu3SipJB)h%D^1cO15hSLlH{BVl8I~e+X1{Nzr$YL9wtQiS
+jXDNhAA6>m7n~z-o(K7Kwc%txN}a_}ONyN~CX@b!Zy3-H8(&1LN3(eGJ8nhhKrVs|<h9aoKt_{wM%)4
+Q$kOwoxm~;=MwbK)a5Hp?+6?ud3Ormo@v@M*ubL`$1TK3pV;w=nZBEbDOS1BFvI?iciI~ua3hgpm&+Q
+C#}Sts~vlpy2pA&eJ}#;8UReuk}mAHXvx94uW>H@z2=#{Cg=DOPIK`ff+h4e=!D*Cp||U!GmDI%`;(f
+H;tE?1WLHaNRwX-Fv>Or^exqB1><fbNOW1OBrpx1J>-_B2Qa@V=j}Oa!_Jwr2bel9wnmOwf-15k5r_6
+c{=M|fwV|AR+3fPI%@Lj~n0E3aV1=vQJeFQZM)j6&LK+_*XS=JeD4CNZmAUvyy4AJ{3B3LEIuL{5~58
+{`4un_joSuF3LfIZ=M#a^=AaP9<3x)Pg*QiaVX4lZ`(&yb_@UA3^eYAv+6u5*~1%eMfAShBsO-EeN%g
+p>5`kyb6o1^^!b4JuY=(aT2KYKHR}C<JISxNfnl0*i$*X@BWyCG7y>_u{Z>=N&VQx?q=3Jg~WBGlp~c
+Cc^nPD9YNj-lK+5vK=?`0@&4awP%3Duj)1o1Aw()F74c_slp5dm-bu4)Tr|%UiodPyy|->Y&b{oLTy+
+m#0x#o3;hNQeMg19+en2vuuugr^k-hE<|w}9y#R@;aXziSmjw%UzHK94`N{ymQbvd@@WE687#<mxtW?
+0yVgWu11;9dB%#wB(dI2XoCDfsFW*pbM75d4Be!Q@Xb7eN&RSEF$Mn->CY=NSMSd;}^vkm9H=3}M00S
+0YsFyG|pbXSGp%;aqr^MS8AVi=GK>90b$O@M<8R<#naa4xL;0w6w%3OMP3#b02h#SacjQP$a39Oh!Ex
+^^s#xVo%Vu|=o#>6zk+t+LvfF0R;yz<L1)Z%MZ|T5X376v_%jFaB&lEc&=WbZ(kHL}KrYE84U>a|tyD
+`PKDllDNVrR72S>0?{jMmso^%a7&i7``L$Hw%%}Fw*fmV*DtmRtRs{Ig+7NzSv`;@M;~`pq~TVKOt`7
+VwtTcjsL)=;S#E(@-x43Vhu`kSRa4Umr~42|-JJ`E*OuieVr^Nrk}VrC+1*z2m2L$jhx%b$@k^lzth^
+K|dm669CG`{?4X|dI#;W*nLGZGzk@>Dd6X9QL2RLsbQam0zuS#I$Nl>{uqAd2{zVcL;DK3u!L?j5L=6
+&r)IRuDo830jT77{}Hs?{aJIGVuVI49>8Xh0WD$QAMmc~~I{xV>2CT&k02*4dmGQ0Kes)6{{&p8W`n&
+9Kh^Pu7ejO0x$k`{mVT3+&28)n%1-<vekPY+r3h8v8P}6B-ItZTpeE23&TaBd|Vt3)>P5W1GkJagYUI
+0Dg6yy}E3c5E=)>@@y9k(6yfr@FK{^b#p<XG@5;?$PN2>6InQivv}8+WhW749U@Gws~QN}UR<X`P-B|
+LYkbs$YtIKV&rEBEw30@@+HL@!_OoNrinQu)UX|H#xo2A-Dq$+T`{`Xa+5^^DGNtdN6nOPVXWBCXd9-
+uWXlGrknK+j4h))3wv>DJ_08pYd0ED_Y5Y3yZAyk1JzbCV;fyf+0miZuv<k}GSTeW&X*G;$=fktcdj`
+5e&zv^mnOg+gzvO6!9Z4p9)<yg#7-4ip6P};Z6a;)7EzZkgl02$(D+{4fuNIbwNe6XS<vG__3No>6wZ
+Wo_wghd>#?aT+-B@9F8-VKi>jd4&01S+$g$|xVhOSQM`LjJZz>l(^wJz#qUZtgto*VINnQuVb^@{-V4
+O-(fm@W~Z8L4x^?5QOE+OB(etASU^M@Ni!L&R{H%K#<lD#c4&xRf$Y&Ht?llWS79fHf{3Uh$5JuHG*&
+_vmG#xkW(8DeXYT+BKg{L!(sC^>n>?!Ep<v_**>J)5i8<!I`o~XeTk)z;}zW6yh+chb~h=5Wi<xd!h*
+ZvYe)#C%PiJh(g=JLSml+}Z-B*Qme+BPML^Cew9q~nuKi{>0R;HrdZMt`z&rHDTThM|=)H%;O^?=`&j
+ARPN0mW>;?$-OCk#?@wCiwa0hcFYNAWP(#z`_LPF?NY^WfsO#JR)mvgqrDp<Mw(Lzon3Q^o*SJO@|x6
+VSg_8Td4A1ooTffI`4qa0<Tln9_wKxv0$c3ztPu2H_&i7{f`0{*tzO7%n8Pp!RC&QD!arS=yygO8adp
+5qTL(vn1Mbkrsp~n;(d6Glv1Qy*<$)tU*Z)ddysj%BXI0H;aL0xvGt}c*OP$6wzh^8ImKmdoRNIG=^N
+!3INB($mqqGc@593Ff%h(jzr9RfaHl?R!EEtwa97{$kj2sY;07rfP;;L!umesw;*#s8DCVk`aznCI@>
+R>faKRG#}YR6voU^_-Xza!uMDuE7QcGLP;%h2Pq78V+d~<xX@E$E-)O|v{YHD}9cYi1i1zJ}@q$oWE&
+%@#6r1+9u>|dZ6j?~LioXfsqlpW+e9^pO9IAUq&D8*;e`nxx=Yy9cg;@F0Kj`0ua?v)q=YSkL0^DeW9
+E)EQ4GeZ@>&^p`wt8N^Xm%Z)N*ctivN3*1Gthu!A3N3Fp-YCSF^BfclWSa;sxh^h%K#wPq~Q=Y)La3>
+8BfD$d+-8r^-rKy(S`wI>WQB;pm@o6lpbTn&|Eq+hu!9ht84N_=sP|PGqFQs{Qf5DD+rjCTRK>QwTbz
+Sw$OL}o=$mGqdYFwya{wW(&^NLrJ%MI`XiIO{A!gZXlpJa3ANOenG0szbaP5SqMaEJu)}gBfO67?AnO
+ex!TUyzOm{87{ze926FY<9?$G&~HY7#yLPL^hy9fc2l+16xZ5EK~w_QX+!iF*k<W05B@b`QdaJM~hfv
+ol!iX*^UO17iEkbfapRG{E<0}3V{e98}+s`%>eo+<gcByi|ThL=rq=^4^0G+uaNxU=Q(F0sB6vdvP`Y
+Cy52%58zF2kpLaw^AWCplIlKf8`Y?eeYvi0`7M4u>-`Vfd_3vx;j^T@dwb{1rORK))IjIH&-swWJ@OI
+E%2g<7_fH9MQ!mvQ#Hu<v6$bj)y(w)SPEbo?gJ0xNUX`nzL9zk>#$+gd)fE?o-S|EH+nWcx)1ooDc?Z
+zvOD(R{m8QNpi7_G9SCVa_jXUfeJ$1_t5Oq&G7olDkrsczKwWXGCSSypD`{9rRnM0i2q3bt*>AJ}lO>
+2C&zy@(31tq&cO&gafaj$E{z4%v-Pp5m6;WD=UjaqbY54_m(I)_ZP#}B0ix=`Vhkn4%TH_<(tzXVv03
+32rejqZtfOAMcI~-&h@W%lA43T9vBFn9a6}UQ~u`<$u>;P*5CAtXvetvKYgG%60zm#MF;M&UxHv=Gi0
+RKd7a+7vF>ZqKx{pJ`xwu0tEDCa>+Da#D>q5zu%+>RI>>1iN00JjXHw$~(sOs{zmh8+F*JSt!gvON&-
+z&zxF8*w)v4MNqd8=6n@_k8V$BQ@5?;1RGhzMB*46n_sy=eTBTcYva?yn9N%M-2Z=;8Mwj6Znc<s3!W
+9@p4rM?fPC@AZq&+@Cx<wG4j0am455cHXvuO13A+N!^4Y6*BD4uw)>z^_oULLq4C(tNNmLkgdP~HNo|
+qR`mMQ*-BM|-GDN$+f2_Meno+kHuwgI6kyK1TB{#@6k;X)d&{nMkFc6p*`v_oby#fRaOuQVX0aywY7x
+ytT&uW0BooM1pG#QI4c2>LFLMRCMR-(!VZwnp3B3=ZdC$4B0SES4Od8DHTK(zIs5>@PFF~fQ3yQq)=#
+BAaU$Pib=#1$LI#ga_=%zBhb@_{kuYxjt$Ira?V>v0S8rM-JOygY0d0#$pKq^nhS5HTdS2{c@b_6rZr
+Gu*gnJG(KzrwPrIq3oiCcGeOf1JBG2ptg<!z3A(rd5-QTt1=M~&kv(Xv9v}6bhp=TuZ0C*lO1|?ZE1E
+W%~&r3FLPUK4g;J=vGwr|7*_rEMTfO@gOa0+-vX%D-DDB#UlsTWLzyLq;(vjcqK6$pb}-1kvp#J1$NF
+rN`sJ9ZEWIl@?@*=G?X^K~i<7fR)7q~Q1b0hl9QMz)%GS2zAX;e<v6{m@lk?Gdt4~Jb%!g1<d+HjZC=
+DON{v~MvsOsSE$@z9A2MdFa$r6f{J?sLWWf^M`u7lpG=X3?yE$WGSuMo_wHXpZ|0un3iw@M=RMCwg`;
+y`+1Ps&yUHjq<Wm6uwuYx8Kq^pV<x1a5Y~yG$4v-`%SqiOa2~^kd3w^OFKONkCG*#1g=m*p3&~kVBi9
+Y8>0WN8R&v$)O08Q4^L>mi7;z9qc%eZfHJj6zZ9`=KJb*J(Mk}UmWvBZq3(6O5M#CWhi{wl|hoY`itz
+E%+|>+1Qk~LaWPhECF<<Inl>|NDC?s>!xYu#qZLv?%Y-Sf4X{Bb0V1(Z9B}UmP<bvl1(o7lKKhHpyOe
+D8s70}<N7F)|Xbw<<mU~$<`w;i#R|u$1d|Hm`Su+RJ$$j%4)vboPUxsQ|^>@??N^ef9er>muuPRq*T_
+nzKWC?A{HGH~1y_DqdqgNx=JY<7r^dT~vT8O+RTbuA5($K0xL9NOc_(~O@H17d80F?(!c&PS9PHG(2z
+$1|H^I#kvB}2U<U(ma>vE3#{Twx?J;sr?3KOBG&W<S_P@yg&WXwa}-xWh0&E7_%&%2l=>?I5GlF#NK{
+>Pqq0C-lXt9JFqf$@!tql3nUbWUqrB$SQjp546OmnxRRLHk?c;p|NWDYam6Wf&A$M*=E(*L`xk>4FK)
+aTA@E#G;9_fiAC`=MSCq(v>O`K)<gfxwTFigJhNAg!i+Kp8i0yckqo{LWE1TrFFhARYuXEw&9bDg=Rs
+NRNspO?20iVFXd2kHMR}xTWWtJlDWY6r`}JVZOd_`53`PdxTm)Y%?>_j{zU)GnqIm`QF;-X%6|`T#hd
+!Jo)Wx7$h3t_06V!pV$KZ20Utk%Ts%iVw{v~rOXZ!&a2R6}i%XJ4ENJ-5*Hy4d+Y$X}Ia`RA)@VY8VW
+0($t0NT?u{WU@0dN1@@At;jE>UEWpVh);}j9JBG9>S+WAWEBZcsn2y1dz>pk#j+FCGFZX(3U<{X7w%_
+K*aWRF52|XSG5sHhiyD2G20{f6|sGtjc@g?<-B|swB2fJfyAtIp|RE02v4LAGTR@H@Nt4LY<tFhUSK$
+Xy#f`iVyMSg;3gRvn&Et41wml#DqPt+;6*LZ&LJ+t%UvR4#<OpsQ5L$AS)1X!9P8Ar!@&Yx)0xbzc7J
+4EzCW_C0MBOz%IBUSd&nMSmoX{b&mJlb7WD+#X90FVX7LxR#qc3$#9#Fj*&mtO7K}^=F{8zOrKr}S&}
+;?x&uq<QlM?}Vm-y(5P?SwRhR^Eni6Ny6V*M}i$!$?aFe{z%_}K@3Ypc<Lydwg6k0^CZgsO%2?g<sjY
+I(*vsG*$$Gd>Qwn$|gm8<kdb;B7XX`yiQ3O_#_nPHk<~9<}2mEG@e+Jx~=SkQQP>q2KyZfUU=a5pMsh
+061wX6bF%3ZFb;HRv;rSGtdMThh-OnOe!cj<SRK8ENXqRKyjCBFB|><C|=X-OoF*J)?_jZd_9nBfcO`
+4xQ8#y*S`c&(AvlXtq!1z8Ek%Qu5R_RIKoY4pSrJHI72p?<=7YLvUN9`3<^kBO&N#U!pnT&LyFDoE>b
+ST2lu>VN}=i&LL(3t7e%kNyl#<T?6Mai8??4cMn`h!HE*uuf)Jf#+zNo52j2?^9%I}-z&<e_`G(>u7`
+XPu?24Zc2Zg(&+t1>@=p)<~wG%WpP-cAWLm&IJ+FN#2C>wfAgD1DrEv~R~*sF;<lwvheql{A%4=Tg8i
+!)M0c(sj;Rg-}<uC-WUE9FV7Ps-`i?Q63vC_AQMdlKt_#;Q>(X@*n~dF#JJs=1;~?Y(j7L&`l;(N?j3
+8+H}XQKlH}%GGK-UpZfmXDcJr-t^GpKByCZK|o!nL*43#{|&3J{yH;`*yrhP1#uZMOx@~<FQZbO`eH~
+<3q7Fj?^E|3Q@5sxtG~+B7eGUyJE0LN$Iz?P_;h8O8Xu%ggf1_{zK5&58De-O;7Cua&<K0)2hhtPy$m
+$8P?3+diL1K@=|%W9n%=$|q>naB=0ZtP7sb{4a~Ikq_Mx5*3As3ZbME1EXx4{{=DTPqN$-a=?13F`g3
+`ItO1S@?cK<YzxWZ46+SQyfuyD2NE*HZO@|;uy&S$9s8$YeletkW07hKJ37{*!p^-v^AMeJsgh5bbwP
+-L)P1VtXDBAI59itG6>vUz8NpqS%?p?J$m#PC5n{|lfxy9vY`*r|Asf(KSwbAUIn1Bw{7unjgeki$qQ
+KjJ&v8wr8t6oQV{!<Pg83c0ce;?M#VNfwbRb{hOP?Qg{;yP{Jqn%fC{ro_O*T6g*97l8H<J&5D4>H)I
+*#t5|gOzaYrY8j-`Zcn}%n$hFQ7iwX}0?{eQ)r8|gM|7G)%N@$q=GSQM&WErf8~P3EGG7@+fQ`O;1X`
+%lzr{9`T$y!1)^8w7x5<cYMr-^bnB=NT+!oK#sc9RXf!Z#?rXslslz(QRg=Y1$B2x0VUZ({(M`n3B)L
+U+^HcY3!j^;yOs_UqryP$zX`IMW$x`uPi>&T@?cVZEC_E8k0KtEPS*HzK1P-y}A%0VZWT+o_&w!t}wg
+N~e=2dKR!UykHG1lzT_U+$cNnr!rCmjln&>JE01f!wy~TY(=yh4-YRbs9uy5Yc)K5MG{;1UyVzARs0v
+pl_^v5_^0!pOdWxa@6OLqd^Bhjzi{cIREw<&g5^XNg;O!l7@<<miu>Zg}u@|r~~Tg!B%IZs8Ebgm3X=
+jFNN4*5EVYq(}9_90=}f5Ewiv4#~TUrmXmKU(#p~sOmP7qj3P&TwT9F78Zuy(Fr44MD$oRE!@1{GS{+
+B&2<56r4G@qvYA<`Rg(yy5F4?4gzaJJW`g<2b2gow;p@qdrl4st|?FWUNe6Ai2+**pt7VY<FxO9H{Pc
+scM1Oy@37(ee-ZvQ(~R{`xH4hXU{(*p3`;YX478<3A)MU7%(0L3d1Z8syh&JCbQ6NpR)?$>d$x<%_O!
+{(dKT`z7kNcK_>mdbR1?JH>q(BpZs_F%t}BM{9yd>9G5pKbF-J$84tQe-sxtsQFO;+k)w(jhf5_`!^o
+c_@9<#k^2)B`Hz8pafmp*epLSF>fs0l>2U-NXr_^0)fo>Ehm7D4u0N^`R)Pv`@b>shf=<2x89)*A5f+
+_RVI?J^|=wxoG*RDQ71hRt#H7CxnW3BBrRpYKht?m-N8;+(_l$s5YLDKAOVysw1($Ny23oOHdG-o8)a
+62biNnLIsC1$0^6@f=ytQr^8UiS{but_2|&CWO^$TbOfCh6v-1@aBP|(ZMprH{obN))#EgX_VcB({nB
+*KItwPr%JS=$nm4TiD1NAjODt1SZ%8bU7`AK`^p2M=cb)l#&2E0q24(PLF@zq}5LfDW+Zvw67#-0cBp
+}&Wrzi5RW@XBz$`Z9<0#cKIc6hoB}s5ZtjXj^PVlJiEZ72DMRfiD^S<pg~$xo*JU{IB9<kMcI5si^b(
+SBXZ#elQc$*+xLz6VGd4%~zl%x6yDu_7d$8QfcDXqKaWYUo{Ayd3i0Xj8k6LsSV2DT<jdAVW^i*<?r{
+H-+eN7kiZ_}+hjbqb8lF9aX&!mtZs+Q=*TJ!$JT6@-MecJo$N2fi=$19DnKj40Y;$o=HP0i8QYRI<0p
+V;KwXfB_nn1Sq#a5=Kx-E~Yb_H=BDUk?H(<gA0XoOK`W4a>MEKGk0pLJax4`>HSHk;OIJfBNFA${B^1
+bk-4?5ih)bgd+9l8zWJ%DZhh<7F;6=;{D6={a^Hb_sNCJmS?@n6LI{1bEwkSEzrFYDzh=kq`lmaFBDT
+0t2S>n+e#tL;83*NnUk<E!8i1flTzgfV#^4@RZ$t$*-iFjC);6+g#w-=f^7|G^I@xf<7vqbLk84c~!=
+eQhUiE!o{yvp}-}B;>NpP@29E6K<E8XgDjWey67W0QXy_@gB51hg!ax=fHb_-Y<dofcx8!E#TIaw*%~
+3oHocN^j)0l(;SQbTUe5fFVT{`3}4y`5{H~^JZTjs{S}gy`@(w_kwd2;2Sn8H-q3liwpnQ$C_Ll-9Av
+9owdP~!YPHpH{_;gcOE$A#BJvE7h=^pzt4WWBXIM5%ys{4+b4uFLW*MJKS(`|wf+W@yFY8O4f5F~*0c
+a5H1P_YLC5>cqz(%woyj~z``^mpK{qF$UKZuUp9BN`%&EXOx@1O~R?$u~d;<Do;-2>=q1=+xCztn}K>
+xu{a?A4w{J*=A_z>4)>!3d+*TeY#*qq+eo6EYJ9b15}XEx#6;fQ*@tQD+=O`M-W8@%M`_2K<uV9%@Av
+V0-ONQXkQu<Htd6DHg*+Etq%l53nFPnN>%wxXg+I)CZt<zkst|?Aj0gTnH^(4J~jVw89{53THoeq`7G
+Y2>=fk&~Zm1R1y;(n1TsA12l+l0cz4JlIEiU-`g^a`HR-e+Y)jS@b8bby;D!$Zd(tu3)NrzHwqN*!!)
+$ZsAxzEphq61jNvIG0mmj_H!B?lC?0MmdPa)T$<6)*n>{&k=D<Xr<i{k_<*M#^zTIG$AhRY6Wg7dC&d
+rzQNDcPtvJz#m(L`sk&1rQ<z5|ffe2J2i$J6j{S4kS$38!$=eKE;)=y1UrsUO36j2eHOPyq$0mp|o#6
+e0^3q~?JjwXhV0sg&#w{F5N6^-U!lQHJx^uQ@zMtL3gZU`S}_Dnt%#KI}PeL%Z`h2{eTy=U!k-or4o?
+&&#*rp1<mO&VwB2>ehkwu$88JoO!f-f^4?fm7ns>TFw_4r&SJ(tVa1idQTgy$2nG93PAA=WTSRGW0;p
+l%>H9EA=v~pU~Z1&{S)8Z9SeQ1xw2&Sx*nstb&?@udm}Ygc)C=`Zkco$zD@Bq)J#UZ5>`-~D&^>PJ-i
+w8mRb+=3^G6`Q(Sk7^%Ll<6OB)}zs|!AP*t!-u;Zf4O%GbXEXx-I`R<Kk^f8)2`823Zqeoo2GmXPzE^
+~_5TeN|?iDX(oh&YdD;ecZcB{^Eb2V8sSrL`vvx;T5lfHO}WY&&zVz*f{&yZgPx4SEI}{X!OsxP9SWk
+1Y196J3Tf7r*+HJEU%(U{P+tFMDW^pXIuMAO4kgkWa4q8i?%UP?eM_&?VI`$KZ*JJnvAYY`rK38k-BG
+d_h{~Lswl#gU@a_C;pY|dOt>a+?Q*#`i=GIALTx}xZcP1`Prv3E5DM)H_>gNQ_4hlpORf0aNBF+VIq=
+Vm(k*N+v|EdSEASBsCG5>J&<(K3j&SrIUI>b+a`!mlu6DR#>eay--3N`ky}lqg)*^8kDje2tRWjd+X0
+o_4pTvV>N!5lCe)t2?p!^Oip#C(?miOIn=2(_om_Vm^&IhW83m=h*>=R|zdVOz8~4<5GM?QE(&0SVR}
+Yhdk~(ua-B}s2hVne%OC=!a9G28g=*X)HpCo;aLxo{OaiN87CiKF}KL@Jnjs8IcboaV<OH7x<DF-F@!
+BE?PlMS{QRgr#A>;K$;iGgkdERLPiStBYr+Tia~L%6WaHV?XeU@S7p5B>r@NB_hJF#1L$^6V%K;+W(<
+7Ha%2gE;dTx=Eaia{zpV16VgD<uY_BIvT(zz7%K_TMXwk8pR*YQEV1)6yL%qzLVVFh2H&dpg6_0VMM1
+{wA(@IUmm^Pis#wx(Y6s5VaZ)`)we(ccG#1KjS89NcF|#sLtC@YFc9l9j8-60F7@cp#g{?hm+fRg=;8
+*1KmM7-s^9R$!`feVlFi{UJJ}q*6^v&67u;U32U&INDdvJxeDS4x1lA6hbz)lRnjR@1@lp8!D=T^k)a
+OSo*5*2~#~U49@}+$PSh~A4B(ll*^^FgS>Yn3~$@zMwIyJvQ-2$MV>OdO?YvC?Tk(?7{=6>28Zb#!43
+hx+RqTNlRuwuhQumU`n1gQqkSvP9&p>!ZMa+)CYbdGB5C+hw4=%?ufeJ_ibbi)!{Z_z$ZL-W!uN!@Dj
+RaiP4O-$&N5~%tDfIAIT^Xt@!djw_fb!_OK?n$r`w?VH*chOOl?NZEVxqE1dN+k8TC3Ked15`}it;#r
+~1-f_XqkQ2*p<z%Y4OVdk&y++jTD3s(bm|${#?a59S=Yyq+Z<njwkBF6H@d4ptD?7$uT=$kog<pfHZ5
+ql+o^Coo9q22&zqcgqT}EH0^Nm6cKO*cb*bGFI!~@@;k2klj^@^a(w)|aydt+vj(|x0{U3>-oo9do-|
+A40x)!%{y|Ui{4F=&caa^uyMtM<ntwSwz*Xhn>*Sd%mjSj##SdYw#<nSS7l=`sUf(r=*=Zimb_Vo2fN
+dC~i)$EXsEur_J(wHwWZz`GDa#Xh^lwmmkh2^Qt*3cKHKWvq&+F@QhKgUCkcGwSXvN3+$ACT9b2%{N<
+c2zsx<+^wIv~Ktg4cF#?&=kFXw07*fq`WcdFp{aGvK3^Si|e3azq<H1;9@#!(H~lcp<$YOBt^+Y?bEL
+YXy=uyFt$mn7RZreBd;2|*yIT6AGot4VzvIEVQ7EpuiLHX`6Dxr3wnmu@gbpJ-d{h*O5MK)K}J2@qRc
+wMegKmLF`(l!gv2hOExU!#VD0-pQZVgNd9b$rTO4)n?*0u4uv-56Q(#3gGdy0VqGVT)?Fpa_>l<Ldo!
+;o=LUbSMl_Rq{v^nWyga|pcoriE9-2W9GaSuNb1ck{ahr5-}0xT#8n9s8cow5S%&m?kL6{p0Y$&OZ^H
+i?enLVMt^BjW3nj1N$9?N&;{jf>7Tf}M-(GV^sb<m(QMA}DFserMq>HR-HXA5=E0b5g_1^9=D1&C$ty
+XsxW^40B<MVJ725&cG|6Ox%w?Fc>!y47ZE*Xn_vm5j|3=5$NO--a!i-|B?(ZP^6t6wu|+R$f4aG4+OR
+M5274=tp!AyC`h#|p9Hu7KhrAY<~!g-WWj4d*Q!KcXukGGsHK*U6~idVY0Kd)lCM9aJ$8^zvAt8#$hz
+6f3e3iuPtzF66D{5`hJY=OL`SbszIH8dH&micgePyLK))c|_)uX>CY2hkop%t|41K@!9BstG)B--Q(*
+mBs<D(c!0oomt#>hf$fz<xgm$Ex@I0KC50*n_FMC7DBW&?m&8wp#1P0g39y1HYJ!VdhSj$PxiHn9*8v
+r+c#j(L<k?NL27#?MQP?wCax5qa3&9dn5F5u#L+9qMO&a>N>$nh*R4^3*+Ft8Hn(k^YW;fT2sZweRD&
+8jEFjr!q);oxjb7w@?m<Md6!hN1|V>HXfdV@sKoDAH^fmBIJ5S^8oE8S)da(*=4jvCfmb@9vR_f>!JG
+dQ_y)vzgcZBfDy#60R%@cyK!n?pK=bxT><5Fz6?|s{j^A~Z3nqFw@J^0wO#iK%u})#D8oy(vn}M`^Ui
+dnP6<P-|C)lsraG2+UBwL+eb_rhxX_IXin8@u5q0f8E{4}bN%1YTx^Qoh`TgwvB%a$M;bFt}&{A#i`M
+BjKgTA!9k#^q@nKi7Wr>S<??fm-zpm@VrZ5t$oZS(d4DD7JM9hh$grg+PjXrh^HS4NQ|QUevs^J1|qE
+w7T=wqsT|wU8DXs#2M>mdW`RJyUd~eJd`t4D`)D_1^&dF6iHC!d`Qu_||s+DvSBgFTT1Bl07x&$UQsM
+&oY&aKv7JNIAm*EkhS^MJ=geS@r+Ozf60Uwt1`H{x_bF?If8!bcJw{|4qX+q8wQ(RwB60(7>|I3;J?4
+F7sGBB7GwFy4L~<;a6ml|e0Rd%9;gpr8vJ#^Un110KWW;R#pc2A2Q}b>SAmC4{Gdwkt>H%(ek4L2UA4
+I`AwK<dy#0Ts#hr*QVchrEYpJcgYd>w}_zOhNOmh6`Nfg&#cMN*I=7FqMUndf1J#%t2-34?wNw$$n1F
+1=o<(WX1{YaLFaI%b_Xhn(mI`p9?g~s?Yq|oPLCYDVfK)ydVGm(6YK)$OdMP^p_{SWl}B$*=_oZ%}ub
+P5GOd@2S1lvD5nNeaFtNx|2f6ud$^Yk!J@@2*Nx@I?bD_<|JC{h>+07ib^vGb#Av2=p!ckb=ii!ql4f
+5fT4Y)j%S?Xdn^qZx7w#tG$u=xAzUC;<dc(&;;#)eVmG42vqz6D0nF+<G1e{K*oQ#FSYi&zf6(w3Zx0
+Ai32$$9-^0rg5>tM@(i>RAA*)`DgQ-PtViPbqDKs)(euSL5Y*ZVB2s=Fp{=%A;E|KvOs%s529zl?3D(
+MjClBMY@XTw9OJ+AaK$=fv3PNOxJ!vN<lJ{G9=$?}QXy|sV@Qs}_`HA@QCB5`1m*PvO9cjW=IeNtpd-
+0^WQ;r?AX-#(Qpi@%67!#bgt1@6c`%^tL5FLG6CkWWfCfi+)PJA?E6Ib7#pI5h(ocltfpf|hXgi-ZOA
+xB8^C>Oo42#!-UrrE`}EEvre5%r;{d#HOHYNI1-O1^rxT`-}0QmjT~y32v*OEYd9;9O4|xKgf)NvyNg
+w+Ge#$gI7%fFrbb_Zmit5h>N4+M8sA@%0&iss<ow!(ZXaZM!Z3!&0B76Opa}O9Y~MSH58w{+MA?_l=p
+#9`7x<7SzFc-IdF{m)T#mi1qWKY~4}Z&kqdOZiSYig9&?+>l(j31A(<4y&0~~z@-$!Kggi%pltIln}1
+oUv=UFOd})WIj^mQ}?q+Ypy|ayWWhl_h9M;^^@uEd}QT?JXz`oIEo9|C=(<OJOBu?rCW;*+8)JiERW|
+X$(i=>#Vb<iUn{Oqv-S_S2j?c|?Z6hzZj-t5=0UguO+Pg4p+W)^#rFMjr+v=Zg(Zf`Nr*81PU<FY_;m
+ULB{SpRcmK`<S3yOtUTCF@A)21By$^ha;9_@c9Wyba#sEMNU0<<4XQXrK%lP;LnCQi2WHse0C(ve5)p
+5?G~7ss+98k^yzD!aAd&&WQdxyh1Nj#X46dmn9Gle(XhqLGERA%3(P5C%9#(0R`x)86N`?YFLbZxXrS
+=LwxKf>12hT@fvJ+>*vsV0-M1}*NXL5;L&~U@ieji@ig?sQSzkPtFzG*Rcue!ZMAcX?MMAp>-_A~Ap6
+R1Z2_{Zk)zirR5ahl!(KF|O&lrKBPEosTBqbSSY@NxZ>*1RB5v!`WTP$M))o%;M|~FbFV4W=QvHzt)8
+e>WVP=Ws%z&m>;?zt(FJrwq$IK_Ve~RHe{tJ4lE?e@katF+7$*YDja8j}|vUW*jwlc=sFfQ5hGGAqb_
+p&VA?RS3~%E9cAt;L$H7XiXZ=K1LcL+idy5HJ&3Js(<CMBMSVv*8iif;k1;_iEy!xv&lNF$25waCoKZ
+SqKZ7zL$OBE817PWVS^NZ%d;C9<ja+?Wo4aZfh-wh@iH=j{X2-Sr*kJ=}U*h=xWY_0!EYgmUU9;+d|j
+*7D3r7zLhgce7D|$Z+McZt=@xJe<2dOjYd2Kts0H)CTO#&Nw#+CvrYtu8@g%$yjG8-EgrngnuH0adM_
+(ek61&eq+W$B6?N)E06slCO$b$O*FNwAR{8b@*u51}(Wl!qFWVSoEy#RIwo}wC23W#sNk2z=um>5Y;S
+Bto3@$zxG!AzIH0Ew*v-W_Z9ec$vt}_Sg5ua-HS@%X37t8VI$uDd*ZcZhyS}Gg%9}#jkA>>{C2uYE&Y
+65Wb5<K0J-0KuaHAeq6ry=IWdE}1|*jHm8BIZay%(Ut<M<@%j)8nt8H3x9>lT&fClj9~H)?-U1a^CZE
+Ldijfb9R)wIQ<$50D^MCA8@Gi<&%-rO|z>mo6)Iwfc=hJ=Ic!Mg{ZpBw`0I-S+Z`!^x~`Z0AjHtRX=2
+_jttSSgr$;On~)a(0kmot%79zhy!Tt6cj{@Fo|{a&_GOX}#vxDuuV;&oqTsKOJuP16a*%Qma@2+eL^K
+GxzgO&j&|U6nf#p5gCyCyVS+o8l!#U$|<gqrO*EydiIgwUCkLZ^cmyV;rb8je}?bhD}{1^{e@r#b5Kc
++rX>-mIuOvfjws<S@f5{SC4vOgy5)>#AZ=pbM5fgxxTvbO_rRj+CwZ~O~1QL-zNuRWA%HjdtRCA*L}{
+sErc<(C{r-uMKe!2SU1!61r2wEW7$Xjj_{vcTTTsVuz*He{f{u6|}we$1Msx&*RD@z>lf-Hl_<*M@$Q
+<d6EzAZBFUEeOl0YPYw*Zczq%S&N^206D`kN}*GCYM+0cnr+X=*aqwu$bax|ik}@x`Oxs1{$ldSDX}a
+ikrfO$6Q=khlEQxWaY_ms!Yk=kCY?RdZ=_##^P_VS$iGKG_@$_vn|LD<)J#V8Zr+6<1NuZ%dCrpE@73
+f{F!hI8?vG){8+mBf@^4U&=vLOt?xPEHH4D}!;qqPb4LU{OF~_mk<=|#W&@#1BJjrO&E||i(+;4CSrR
+!5_IE`<JcHa7aRs2CBScYxvC!~Ut3|SS3j(-%-ovRXlb>cB0R7BF5mH{%lKaze7MT>knI=O)K*oc+(c
+x18*G*5kmpXDCI5Dtumnrz1}P;8-8PTkb}w85qePBMn97>ygH{kHsYyK*_H%srFxsVdv4oGr5rS~I9t
+byfMsSFjvauB3~eBN+o7-u_77baqkD(O_}dPS3J9clq)x+iYyq&Mkxeb`7XO;ayAjpCOdwOgc}2)a}X
+lki#Xk?q%crOk>}Ga;W6`)kO{=WRom|^{ZikFsKRAvY<dce_(9U{!vP78hQ?$rMfQ+4dywoU!yIDoN)
+KjZRpi(MM`su7h1syx<mfJ_|fRguHruOHtgU&1GjBVdK<P`c!1~*9*+nT@?6j<YyGSv$PNQ9n&V{}h6
+zw`0=lN4`_@eOo32S9Mrw<97={l|xqZ)KDB(rgyd9~nk?6#{UEb1JPb)dVB9^PqqM_k9s=cc-kpyftx
++mv*K(w^IU>IWfZvuB8`qeS=^YDPkgO>b4)Go#asv4o-$Ik<Xu{KHFGX?|og7X?HgVy?XB(ge2FhY@4
+Pz08sbZO`UtbGpF9zn5vO&HKemL3f1doo6(c~WVua*_7$&eX7_GYnf|t#NB69z1WyIET5oS6h9{fG&R
+H1-N<eLzdX~KxD>izIu>Qnn@>^wNhGx8jas*MA*#@cZ&^w&)^50dnQ&sc$Qy$cO6i`$L+OWp)E?(trF
+XSG6R~=hqgi1liANgR_sVR^e~%kK)v_|7>EIi@=xvjd#w5WnVG)H{9a~$D;e}19)~?chiYPu_=|;PPP
+;D+d5a|0my+prvRMBCX?;F^wyh?ni1nlC;YLMK6P059hxB%fGF(mEA=c;7Ta{RkSG2{8@IopELTL+Lj
+DkvGr5&Bv&J)){$E&~0S$Nhnt$;qb$%~q{CF0$9ss|IB9zRGoBaDu3=2-CAdC=SkkS6u4FNW#Cz7ba+
+%JH*vB)7p@$zl!qAoc_$9Cn-wdd5R<$qr=EJNhrYx%lV8DJ}KaRL=K_E3Qw_ojT~G8f48{WG<~W`qV*
+tGr4FfExR@WZ!*3$_@MBt#&{`=5z-c6np6uLQQhpp42gXkHpIv|X_W2FK=8~YVBaM^ZnfgW-H&Wn6F=
+tSzM8m?`$(&a`#EJ+6AP3oHL*~v&qV8GBE;<>YGRQXZia?do{wXyFxslUWf*R*#?pZBcF9E><!!xs0R
+RF4fcFyt0Q795&09KH$pobtN_|tb5q7*Be;DHn93<$iJ|vQVI&OQ3!lghPduV3*4J}?*MX?mU;hP4p>
+jr(AL>^U`=kev}pa-z45tLfudJ|xP3g~6ky}m#Jaq3DvEplZNf^ocX2LU9}X$+h03amumYHwHzWg>p$
+F3bvY^wBS&1;SQ_!ly^c42o;7P3Xg6so={&X&J#Pcux1RYZLUI2fOkiyT7Lsr(?%fv#He@tB*B?jfXP
+nLJ2)bQornx+(#vG+EJjNbMStI=pQgo?&%!WK5hfRCcw5DTf^;3ZMf0LpgFJ64_mV5aDX*NCl#xSvPa
+UaEvTSu)ZZt@5DjAsz;L}q%^>?@#ECDgi}k}Tbefk{>n;}Svn`~z7?9awEsh_>vo42Jd+hGeWW*90UY
+R*GY^u&cweFGg>RM-E&8n^1v%B%O58rFJzcOXk$$E4<R>g9#^<<Wpu@OaT0G{~nF}bQ63a>PruRn@L>
+R_#xZ6$Ya7C>`X3p#tlqgQ6{p|3ZTSZQ=f?0DUNG?OXklr)w>+o565Z0iO*^!hQiGozOLd1&Ax!v^L5
+iSqpe^K0D~hwQW#7`y|!I%gcLIsgk0g(7mwrn_c}VT=%_^}7sL%f2uZQSM?`rtb9?#epwHv~U2BfIb*
+f(-bOFplcu>1iTbp4$Us`7R`Z1i}pj2+|XGR-wbn<*!!;+Xo(&)+xwn1qn;^VttDyVyEH)<Ygh8&GyT
+M>#o9X<(z)0r6ELPLcZZEfs$}-9S6nejjy}~$&T_#QrIonHjjgiUlBQiZ$J`B}OWBMU!`qcY>KpBf<^
+ZBpk9vQ-huA2<@nyYGVm)4%s0V}R%+d0Jnpci#Dld&-40kU&7YD42>Tj9PHXDb`0S(O%vWR$b)u7c>8
+Mze9$V;pY-P<{r0{;2XpP<090%T#~DZQWpmChhW7+kLYnHVAZ4@6OLKnJs_D-o!_cNJL4p&JE^fwxQm
+e)yGA_?)X;0H5>GBy#M*(4U9^>rYFyrSn7Cz#8#AS8o8y;Lsn1&jJehkd|?>H1P)^r&PPhG9fg`uQsM
+pTD%epOf2CUm!~qm7wM>H;vBL5O8f@mdMSPbNj)FGfrgI3??tdJ@f+Bgh-x0PR>WFKd}|Z2BP})+kA4
+AcMHnZsfL!#XWt%kHr2<7x%=IoCD;rHDGZDWZZs8gIMR#73LiYHt@Q%OQV;6=%U?6&ln;ZGVa=QG_z$
+N#_Rd|W!u0<9dT`YG{+Wl1E2#(4dzcbm)D2bCvl+J+UM*NteZcgv%)I|)#jONx-D_&>?UgH<=iUo=Up
+W|So)gCKS#=_^t$`JT07Q>mm1$(^jIBzQcJ%6yoF%Su%o*Dm~C#A(P@Cb*r2tQ`xM<sp)eC$ASHk_@$
+1_ib0TT}AYXIl)zQyO|Y{>N^lV<+0Vt9J0%myOyRmHfCZ<j{t1O&vpmru9Q0U=%=4u3wWM!Lv3jXE&U
+eEa9315?es^F;Gw6gxdy`si?CNJrT@%8O&R7YUkMP$CIJbC)$c<a$9jyj5al3FZ<lofb+FyVIpdQpe@
+>xQh<Mfmd^G?3*F@<(AmOyGTR+sU;AUnLG8W514-%^W8`_eQ0x5)l-_(&dVQdlmR53w_N#7hzNyc`Hi
+nE{6jrVe)6PouqMgq><_hiem88&)(gvHuiEmC7d|>kcjW%^9!0op+pWvI&3$1u>=FnDt1nR+WJ`;phs
+IsYsC6wi<Ux}xWp=@_=Xmqz{wnZ7PId3+DoYC*wj&<G5$~n-^v75}$gEbE)ZH&)6j8|L0vh0HV;+xPG
+up5sEKzzpo0gP8b#|W0Y0nY{|wRlI^1rQi>0S9j5<L_4nQ}+(!C)dWr8uciy$e_&}1p79nR-}%v$X)K
+yf8GNtG7H^D*taqnBF6s{lGOdMB1Wa3rGE!2Qc5dwCG`C~jC47sc6&=<MRFjimF_~&2WdY`wGS!`WQ@
+I;0_I0)(M@~+ccltw-)tOk*h6|O!k?e}qT>?iUdmW_u%jO9cWC$hn-tj|yalomk90wW#uHo9SC;NTvD
+B4Puy2vNC_g<ko_Pw&F-%5rmY<7XgUZL^S7y23dBzgT(29l&!Y1;}DopzO;(aFMjeP4*NdZjWjd`+uE
+W=*x)sB$b)1GuGf1C*2@@HxNH<Xnr4mtWLomHzb2l}8MOuoC@7N$eK)z8}LT<?eIEXyQ&5S#JgwLfUp
+$_qsNb}8yPM(zcEyc>dide5VNUZUu-$iMJQeJI^7hS5K0busm>(je3(M<5bNOUQLcu-nD^>&kvE2#st
+Fiqj#nRVA@nFrB#T%9cZBG8qvh{&Pxx0+VxjG9GX1KdmeK)qphgPt~jI%Ay0(@CI1@hjnGY7?6hN9Qx
+IDWxpJdhOx8yg>_|bruC<xJyRc6SN3m6W38)k5Tds!LV4_0_(_tsWOPJmCZOR0pqM`$QsfB1CH{UN;m
+PSt6sL=MQ_HvE?KmNu_Ix>MTQ_dR<1EzOM<K}n4Ve?m@p2Iq)^5WaD55j1mSxuZ#?b!g6bs#9CRep-z
+x#k+`xCLnI=DkK3}UnmTB~)F=j2AMU<AIDM;BO>OXRArlHuptY^)l|#5xPf%=H1vC70Yv1Y;^XF3VLd
+vb7T$3ax-fU)#VH!s)N0RcidR8)(VT7g}uxJ{2g$BX!@<Nl|$x2jDfe(ltqWgJ8&-jW=h(J7MAi-Qn~
+f?zag*+p~l0u%9I)^)AHX&td0Etri~cwOz>!MAFdHx#Uo-du(VhFrh=9x5kQ3HiF>GG!W?xAKkT;xy;
+%CI#{dtRte^8(VR4KV8SOAfLu>~w=ybxDD;Y_wy!|RfX}SZpRr6<%M?S<QS~V41~7^BUkC_m(6?&cSs
+@!#)eloTtOvOl*!_`-cn76jGBV7V6&;Hiv0C>`r5lI*gZd4j(cx|dB-+WA+S0KhioVT3(1p8U9;|CyT
+pw(-WB(Z~nKNOCIOK49NJsQgriHsh_K5|`l!;l&WB|yO;=65H=`G}k2D4~X1}($G9GV+eG^eTOkQ!q&
+V;aH0XpaBLAeRa3M=*=Whe8^8O88Zm3rc4Ew~KFxPw<c#j`|;d%u)ZPtH>?C<3quWSwYhFQH!nVBNX4
+Ai4g6K_K%V5l&5VWlLA6k2w8!l(@hv8X!8J%q*VS6218Wg?nBcA{u?8^4=<c4R|U}Fr`K3`gB%@tSax
+r#k<ntgV;kQ=F#Xa%G`*|8U3q}4fA?{IJ+23aGG#LogN-GP-pJ@H;aRu#dl4wy1?=|!;7jK*oL7X9kn
+iQ)iuHfz9K70F20FGGd_FGJoq9TmH+h!IMV<IvEN?pssxO`iPqmb^d-0M_OfN8;A1X)`G?>W^xWSDMo
+EaW4D-yLp)v+2~%UIGr;4<0YDYWf#&=G#&D`?#NPDao3hp|c4qUX@ty-ISE4|JG-1)ocCTQi#n$TQK=
+w?9eiTlwUfV)RawB~taS<n@aGhkAf9#`v2T^4MbFr!4P>YcM?5Q1T4ten?mIJdi-qG4XltQ_R;X;g3<
+<=m_Fi9WpWX4u{Yx(UtqHb*N|IQ%ju!Uz1zvdhiP>1lCXm6v*)2CPDkn2jq~dVoH}`CO!%suA(#jMU)
+*`L);kDOPH2z?mBEkr6k7iON(6z!Va%-ExO^IA(ql`Zu$wZro8IaD0CanuWC|W`*Kvatj050t!``T>h
+uzp=U$CPIt=FqD!UO<hm~yCx+q?I3#O@4?MB3^XKKnzc%p>M{ZA@8@bY*w2K6UK%DdMdl^t5N4c|>yH
+IgtcuyCi@{(D53zZ4ypZQ9V6$Zb#NZyEfplD{SXL2q;U+X4QzfWLjr-xl$=E%e4}380wp4o@IKBx;-b
+7>PZ2KPIHF#pM98_wakPN{qYlVdNH6J-f@u3Jr%k58_=z3{#<EhBhB6E{95QZ%?k`8@zgQ6;UJIsy&0
+@mhST3HVJ|<5?Z+|hVKbwka~>}%EA((`d4+qb{?tg_y=Fzq@rWOb;LT*(LCG8&3`t^hO|6a{sgbnd1x
+hf_K|6Mxo7+zHmBD3GM0DqN^_0(Eg)8Q4pBud7lUh@2NtB(n4j!qY8#J{%rfxq*QB&*Ykos8FA$g4Uj
+CQe7e7;kvuagx<sOiEY%jloH=)uUIEHh@0`t_%aQ<}xc^LUib{Wn^1D@-#2$U{OCd^xayc+-zDA^^i<
+a8QR#&yaoOU&*9?%e^=3p8)^51==H&wKDR;SJtZ^7Ka?AH)b-K+)JLe$ka(v0~(Al{`_lwv{y2mC@Z+
+xvTlzRyX7W2mQ8rQ=m$k2m(ofZ4X#ulDNV%)F-Z}%(yNZ5b}M%WzJzY<ObZ^YqCK|wl)Ra9W|n4#6Ye
+t^tm_Ie2zV^*Z`+Yq&)!^3q<ppqDVa3wR?E5#@Jv{%+GHJZXjmbsEEwtifLndI@CQDGg6~46<>;H6Z}
+rJ87DeN)g44sFEOMyYbADAW;<Jb8s3ELjoxjEEa~(#EUA<uOS*jZn-zOxNkUrT&@;V}yAs}I_|pZQ+8
+fjPDgFhS_-i)MR=6wV=xGr=nZSz6e4UEbmwlXV;W3@KKN_WR?GirJDz_T#iUfb9+Jv=ugstWnTK3xhW
+7A*1%Wqoyy$cDZT(=H~I?(*&WnKCRPj`!5u|t;vba_a^Lu!w1^|3A%ob8Q-8l`tg*Nr=ri}7O|evH5m
+QSJ4F>V57m_L2BxlRj8t+w@$CeV}K8Zmy@Xk66np0iES$tQ!}T=&nWOTFKq0l$E0`O}Vfff6GTXryTv
+)l#9yotX?TB7c$^$kUOS~j=L~Qj(w>Qk>0j(yY$=W+l8b1Y!yx>65J$(yOd$eY?IatLU+el-`p2#qf(
+vdZG$YBnMWBWu8xnC#MNIp)D7t{6iCNeiL3YJF3gZ}%yJ+;Lb*|THat6&On5#UGPLC~UgeDDlXJC>ca
+kuE{vA|bah0{-!6OsmPm{~D&*NpjbutT9v=Vz_zD;<(T#QaDfoM>Z*oR&&5xDL}Lg(8<4%1<V?249_w
+Tz3oW1-1J3Ow~)N*T0~Y!oKZJ?69x;)D$6*7^O7hOUyU@REGJgPf2D_;7>H-hy_WKErv=Jo1(q4s+19
+zYP;9jsGs)G*Em#&(t@lnZN$0W<I(Hn_->Wmbd8_NA?)bkMBX3w$R_Thu=m}A(_{IFS*x;GNjtA9z6@
+g4%6Y$F0&bXSdP|!zQ@$lRjHm#S0L!mmz}`wuv>4#Vms(RIj0>=X|1%Z)g?FhI>a!lv%Y#mjQkBvuJz
+w5A(rD~>(TY<1q@X1wIP&7Xh+r<MqMeER)R1P>sN*mM+bJ6BS&Yv&&N<5S@OR2$%hn=GQ3({^8Uz>L#
+~?92IblHocgbW)=}Kmt~<y*g#X2>(h$bN5LV-kUWK7y;cr5D%-1=tDU_abSWnC8f<<ovbe~*~LXC1kI
+sSB$GPWFlxCys|2fofN$DarylSR4^8trZHRqDf6;k{QT#$<aN?yc0*k=*c|EY`nFhX$>HlS>*SOWMTn
+pD=;75~hudl_Q4n<H>TOCJWp#G5oN_^dGCi2ltl`I3)F0`U4lkr%epw5sUb4u+q!+<Eg3B!cR@F#akA
+9ZAKqxii6PO79kC=ILpOV-%Uy2SX{{%fhUq6ZsT`PX^(tBa1-nCeks5dWfbDb9d%3EzFxCd2<3UBo<5
+v5-CirM@U@lq0b>e{gf*B3AlJ@XouYf}X#9Pg6^<t~hWZAoq`h{)?7`WGPImWJl361;v|sXEy&P{#SO
+hZ{e|8p{*kShKV|_GQKB@9?G%2zETb@X@3&)w(*sJ4s3j+WHM=iZ$r5^lM>!7Y2oo;LM6|K{XhM8W&k
+E8Np-Q#l~T)I<F$8}1&zvXzB-BoFBgWA1IPr-Aq9X{o%AtxQ$v)mR21qPQcjcJa?J(+gz;8IWNl8#UT
+5&4m)-#7ugy}%?tt%yuw_$lNQ^<6MR5`u9@XWvcqEJ=$}WC(~-&lsddc=Z-eS7IL-PVZb39#<I7mDNO
+LUI%F+P&LZWn#rsQ13-Hw4q#I4oP2v9Eac@9AQ1hq3eIc;&8#;$vn_HYJ)5=0FYd(yn*J*V7K0X;wE_
+(nNjcm@@)7Doi}4O=BY73_*DLy2<HIYEHIhME6$jk(sq9b}yA}z`5UD4&ly03xbbAtW*5d8dh3Mo_;A
+I==w*WpONlV*_XGxuqF##$U11rNzk>o1Tyy#etI_GTUaWoi1!_u3Ga57QLn#4VSYw-M9G~)LdM#>WEA
+zf8HSSqm?-Qn$H9RUz80Ol4`Oonm5nr-)kFln3|!*m+`7BpKw-E)n>)odLZ88>h_Jv36=_Ezc$;x>3h
+*>7q5CDVHI8C^8sXWO)UHldgEOjm)gtZHcJ8o8)PTygf*)#_2HCL2C)c`&_NZ7vX3WLB#QN=i^tV}Y!
+;rpd-;#ZwJ(oV(iHskG=5=zwrKtl+MxxT{5a5m$6-8}T|BwIwZCD;sOsu+}DVMVq*yxhUo>1=ShvLxU
+wSkKX=+2Fzu_l13CQ*^V;XFI#~)Jcwu4KQ-t7X8-)(H0RIlzBvCt(ay=@irgX9YA?;Imu3|!02%eHYV
+`=E9ig<Q0>9dn<~Q1K%<VYl4az!kMU%Lqy{L`Fs7b(IZAPDYIRwUf<b&kOe|$nYtJ<9w%0p)bnYR<#2
+RY)3?4npwAMs&y$Ibw2hn<v=x`p%r+XfiRJoaE!MS4$Xz`9MYY730p=C5kLjtzBCq?2rytyAsYl2uf4
+#kVA@x~EHiI$&)Jlx#rrGlet%;di)?08T+BmBJ3h1&`q6S_7_q;&*B>3N>x$Fx9+tW1B8$UtO0hW!li
+Ukqv!X6{4$c`T7BEMDil|8p&J6zZm~|e7$*R70Y`FQ;hK=tI&vW((jt2u9n<E*YC|P>1Ny118JuJ$AL
+8EPW%YsN2Pkes@$j^NLMaV4`e9Aj19^a+L!-It#u7N>YhM!yoG)XBku@k|8XRHkkq%1l6v4vUkva2`1
+@{rpRc8NCCxb89A9&S**{QaD|$!}%6^ot3_-g;{lvtcblKX3Cji<BuM2HhEDiLY=pFVTyU3+uO!Qpd+
+a2rhhD(7C_h$ngzLOk={TR)iS<)CkMi$INp=>D{v}y|gZ8w1iYzGAUJt(cq)uVQ0uzEB{$yJZ0E13b<
+5rz6i0roCh#{(4Ptv-*>0+b95@v^tjlHU3={6r||HZSfZ%q(Mh!9_sIw;|<WHXl4&+uYlhTqsv<pa|x
+l(bUyBZFa$E!rS0j-q}z{e@{Dfh#VhXXugG!r8R@<kCt-xZV-ijP4@h)lJS9VV;kYiy&IHt7;+X2Sq$
+eJ_**(OUiWS>oJFV6G|Kq#K29#@l5fMwmo@@A1)EU^HNW}ZAq#!<x_qto9jx3&Kiu-lY{Jkjt@O3Fl}
+wr;TXb^1o`J5CC*Q@QoOl-t*}o5KyZ*(4pC4<bdvs^>A5vm@_tX0gv&k6bcdrv4{T<;*!e4YO5WU|HY
+iSoB#TZ)r!GJhj_0Z*auU|NW?rf9YO|m$tL$-FvpzjXGGO>5i6oi;J5)TgL=uHK(`@<SP=Kbv*uFg)B
+V}@0Zo}D0Ze&g*lwSz6#$ri)8hTb86s@-t@lV`S4=BL=AHtJv8a&+SF6r6Z(I?@3hmjL67dOD4^<Xtx
+dY=-k*0PM}C>E;+tpV=!~42`WJ{X|3B>EkEmo*nYv(eFwTtCufx$VOxRcIEaLGeh6{R~miv?-BnRfk#
+%>$;=VN%MBat0qX|2XS=^-n}6JU3UIxO1k}5szFV0h#|}8)xeT5!HJ{Ig=i&S@yS_WLN{)IhMl*i)(d
+TjcJVu{K>GKGE_R?npaBIeZlW<@`wvl+(1^SSB(d$tQkc+Ff_W&(i+34m*!T>S+0old!7KRW<oVU~3l
+X&?n@|tm87cv#H%WgB}&YxP$l%82sBA-ftcx1b;5gW1JX6Apy^Y<vp{F^9Wn`P#&;Q7y{@~@`+wq*Wq
+W+4W5LdF%1T#z3N#ec+$8_s8w<?f$F=wpXMI{uJ?v6dT%ZQX0>!qA?z+(vb00Cx5)s^X=(d7rGM`5Mj
+%v&>%|>ju2V8#s**@#{j8uOzb3q1Q+$Nj*~ZhVP*j$#@@+M!LN&k0k`!`yr<esG9c^>TRns!Ek;$Q!u
+YK>`47sCCjTc3k)%w@0bPb<c!6yV!L210^vC`$Khq0gXBC<Mfg!W9$1uY((ZqfICw?Mo^q>6W5@*-Pj
+r<*C_H2#haX+-jSSZ2ztS&)CSE4%3=tOcP|PBxZ)^x1#z^OkYF}iqzS&>Y1~P_FJt>lIK_4Cz5o}l}j
+)fdpNE`M_($)RNl1BZNRNOR%-FBc`{UDitY!4McflF~@!*OJ1VWzD*GD6pIjAz4>*8Xz;5N8Y+B1laE
+*5p@vjnLV_qMb0PwCc%`^dTti;Gix=Q6;Vy28y<Uq?A6gT8dcxcv6C4$|w%mFlu=(dHF-$C=@@#_u=m
+;VUs!4k(oF2Nl{OQz8ps}`DJrT@sL4>k}+|GL#Fv}4}B*W#qdruqpd$i{LC;<ywpFT`j`4A^uIv%?U#
+~}Z34ixn!lmBs6g#adn8ZowT6Z<o97kCK9|`_;X9IdIlc|i)R$5chU2AVFiBzaDRHYi&Cu0sJn|X_2&
+LbHy=Q7a`xQLTl+`199-ItVdFva=%QwkuwUbVY0(e1V{FWrm2;E<1+XxB^KVE*~Uu5VklGUSo9(V^+m
+O={I?$7jlUD{?ce*;5I{}JhIA$X5i^$)avCo}X)%9u$RcmTEL)Zb%@zU=Dw5`@O$Q84?Rpwr1kzy@vh
+-<uRMTrd(XralUoId`$kD_8CC;SYJ?B^5lBGc-oK1G+f3RJ-aQDL}UVoyUaZO`rjGG{C-)qqgjKfoa8
+#*(7!U7`*HT&(bP_^NdPqnIe{=gfrWd*6r!&^VIgG(HXHNlqIniiu?Sn)a@1R1xjAYcD9jjF}QsQ-Ah
+>L<N+WX=m-96e|ty3-xk>e#WOLI@n3k5)3A?dIcO|1K@x7{=Nk{dgEHSx90};GqWEO9;k@}rys;%1^|
+(vP^3->W^$ReP&N%_H00N-jeu9AmzA)qj)6n$SEr#o1y_e;bG}iWJg9eJh1<=7r8p_Z;lDc?!g`gBj%
+e=z_4FT63JW{N|>zXa8b0CX;wm|ob@3w2Vx#-+sf}gd>Ma_QedKqsz{P`;uZv6M~s(AhP-f^aB;tg`c
+ZPw2=EfVZKDycOm3p`=r2NB8NGX<hJ6OU+TbMHPn2PxT(+A7@)ZZw+*nj3C5!}Y}a>n;3Tr-C~oz~su
+`#+2MPJm#feFP)7*^s#k@bJEQmh-1;dbW|!jZbc8evN^6w@o^iv*e`Yk1pFPP3a$mD+KjIegccZA9%0
+7`4fDAf&V4uWwp#>hr_v%m{sv}vU0$PA|0&`OVNP1;9C3ve)M(IZv#aqJT_y3!brh!tSQkK98NLP-zl
+we+NW1N7{FNan9=aRUSu`-6Yeo;UP1RV{qq$Q>piAtSrox(~qV<xyW#M`81IG<x8OUqS5qNIe3X}O<Y
+Ve8yK+DgiR&Qnn8JuUj0`XqLtVuJ_tt@^p%}mufChuA;(4A0(_<+fMX&90gWM2}-p*sZO;Akrd$v5VC
+(FInHzFB}l-R%-vXAb9lX<53<Z>*b`9m;7KCZRJ%3kI7Tv4#wMtm`#kAJuG;&z*@b)8PV&*>3R`HR8n
+q6(H~2NavopF>-`Nt%Mc?mq(VPPhok`8)xukK?hYUUR}l;l8d_JD*Uw#cZ>LVE?Rfly43_t8l3p(wNO
+6(Ex-fUKn!;#Cncj977LzfT9*SqNT;77#RU$D)sbNm+8@Xc5u?BANCv&u_4eTmo6kJMpRD@tGrX4{{?
+rHi&OE~lEvL>9Z<me_wwUw6-<g-_E^PW$Wc<mc<cuyoaYng-ZI+~I0spGa*SaGwLccXF19vq2ba7D|?
+&k%#k7qy+EJmTX&lEM4Y~Pmff~0MZm~ZYwHzE`gI3-kZ@NPD5YI|30k)u!MVQC+01prm7<N%`kcsN{G
+uH9SIVmN2qNCYd#3W|r<-M4h54qD2BA0kHMt5agf_=JHDNbWxIQ7bBm2;;CJhOuj)voqDBw$N0q_qmj
+>;h?|L{T3^G(JFB+HK(`Zs>nct3up*FlJyMddq3nW^RHv3XZtSwufP`$LSyko_#?vRDl_~U6qX(%&fr
+mUq}nCINU{EMD55Q;p6Mreo_vlRALHeaUIh4nKDAL~gD6Lyzl{5cH;ke~I%JzC{1V|Ad~E{*d~GN2_O
+;ySPUCCa`qnAFw(q}nW?$P)7oFm3>nC5^Aqmsh_W8f0K)U8H1AJ|-y4du!{Tol!mh>0Q{>y*zwLQsSm
+1gD8u1-~t|AA&BHlUB8yZrk%VMa^P*@*R+zN%^cGctWu`HV0e_DE66R~2Ve1W5~vOy#BNhNL5|qax{u
+3&`WtKKnBTz^?vt%9oYqc+*8R$06FEQyJPNfBp|XtdsIRe%7hv%IZ%kxX}>w@y2!bMR_tyYXCJkO?)e
+++mTH#eUpwLLg`snJ<Uk{fb4&dSN!KMh5sR5vG+g6D?afSpzNk+Pl{Ju^K44|lAk?uyy7m3SKN|}SDb
+O*z<9+knkim!`L8fuvE_t##f$Fyukngm_YH_wZ22GK71R5N{luTn6tCD4cky^d3Rgg_q1C%=><RITJ{
+UqWUUBaU@rudN28>r+b85WeTaVi-5(DEEV{$aN(O=alTidP`->T1A0J&03OUFe3i7jCl>yd9stj&jXr
+^*aWeB2CNoM3)`Yo>o{e*fP5t~Uc1UH{(%FlPGE4fX$C0At}(94!j)sP%LKjAMYL4-8;z{f_~RpFJs@
+F@Ul4gaF2ACk8OKU3%I8#wq`Q3t;U0mGJ*|0OK_XO6|&FsQ|{`{XYv}B=o@m#@3Sq7^m<6#wh~=7^hG
+G<COn%0vLBc|DOUFAtmqsV((1=qO7{c@jEOsEINaNf})NJisFKTiK5BKpfflq?p79{3_`I?!;E6NfIx
+vGhHbBvmX(#QZ>w9qiu;C&OJ!z7X7v!3RElWo{hxF1eTD%+?fu^6`~JTMAJ4hlx#!&V+&%7bJO{A1=>
+f(%1Nk2wU~Ka^Dy8cgot6$Te!UXiOPt+rIKUYHljB_7fBFF9ux&Rvz?i-5W(OFrZvE2-7)!(c!~w>-t
+xXOv*6T{GH#xw#W9w}kU^HxP`2b@=L6ZZF{3sr(*p3s78`CI-j*gvR9Ez*yI70_rkO#Cn4YxYb_|TGD
+A7~uK4m1vQI?!0o4>XqMkI1yOg|3sGXRMjeHdq`|*JN`<Q)2>TnTvd=(J%iYJky8+6@I31EI-qT_Cp-
+@7gaR_#gWF|;z(mRaip=MIMUco9BFJLjx^$ajuwtI{?fkXBaK7wB;M^EY4oUf<VsC}(~(Bjg33o4m#`
+Cy*Rc@jYAh`rX}r(*NaG}@BaN)#l#et<-Qq}Nz%7q7)?X7x8h@6LG@g}@G=Awg(s)!p(zx$tM;f^_yr
+X{sk2DrM?NGtmJuQwjK3wm-PeUAM#N>>EmZQ46*fZiQ#XT+S*7%%y?F>lbC`{s!*tVh8o*Bowu_*N30
+mh3u-T*`KkB6YO`yGcGYw=0177jJy_$}(jecZPiXH`DM2R7@Ta*RwK=i{t-(W8<euH5V<<E+frTOMcO
+_T$v<O~zU69pkJo*|O%z+D_6aM>Nu89<!d0LaH#WU>Fi4jSw8QZ}j>CTbF2Yti{J9)ZO|thW~<JOOvw
+}`<qxbHX&^b#|J3I^)s~{z~F}W)PMz>A!OIC#<E+|*)pEv%w6H->$bMiiYH|rYR5p542*7TTn|aon_b
+o6pS+$l+e3&sqS~rcDhsP)YTHW_9l@p3Plzqhp*wH5M~1H~-W8;`*KAd=3V2Y5Ps2-(y}>knec2+lwr
+qSGt!m>=UFp3(#?pJ;(tdwaUp8u>9p_fAEpzM6?oGq{yMKpf8kOHIxM6dd+wc;0X(BxU4lSM;lkW%S`
+)a1?%hu=dhwAeALv_81<)N6F?WFwGZg9i-#zg9$981W@M8W43M)H9g&exPEXaz(Xz0-1WnJ#!bbbDry
+Z(;EC+P<m{GZZ@2h6-Kuj`<z6VbE<?#FrV}%y{kK`x@!_*D<5_x-9c3s<0imkM&`*H*CTNpE9zKb<^1
+o@|S3falTocY~r2Fv_b6Mych!B?9+YN%R*&wW_)Q#&-a%CZ#w(Vi2XRfFAW^fg7;#adTJU%%iif0fiC
+xFssc2R%%?u=Gh^8rGn<K=(A5~WM_ccz0JX_ey-erzU2Qusx>(iuo;azk{H~+`6RXx^PXT|{mTt%8Bv
+7qv`)WtyJ7W{NLOQ&lEsN0<HU!RB0-3Tgnpz(%n%V)CvN6@oXh7Qy34PpxvoB`xmfq`m{0W$R{+3>V!
+Jexz*dGox*uN8VFRa8#&m3mwk>neL+phrSse`_76HfdStEb++3ipcDX7VE@-Ct&V0bbxI8Zd>TyMD7A
+JdVM^QkOdF4H{LM`|1+Xc(lP?TDbt@aNn>T13k*Z+TGVL=UPN~9p&5kc?|z}k+O4%%ChkqY2DL~<$A{
+%ZC2M~@Ei@kQFu&x7u3qakO%SQc03Love$j~OZ*rO^*<3qNd7`7Jk}}B`>AT<3jerfYroe$%Hi`R@L7
+AJ@Q<rDKWsxad)<SCn?1&@_G3=O2c5#jK%1?%#qc)|<arF_3`DzF;8-MwTK1b{pzMfC1HQY2C!Q2A%&
+6(ew3Y$y*>Pn?Z?84jcWXbv2!}`U5v<m=(%^ayujneg>~5(kpwcxQE4nK1y`~z5Un01@><IP$oxdnw^
+aGnM#XTxuxkp&Cb%U}ALHLLywk*;pNV3w!Zl(A6;7Wr=^$6P!3H7UY)U4p%)&Ws771mJIhA7nry(0SZ
+eE+Z`Kyz4EU?|_q&ER^%5LRish^v-S0k-4%va#JZUk3vDN~OVd7jv^ua4V=;hr8hW6xM)}S6I{5s-pX
+?Zg>aVCO0>1gg3QE;Iqk1iQ92D`GTo*tdFiVO<C(&dY@8Xwpa}<-DXrxp_ya*V~2Oe^ERsDP1pH~*{9
+6~OML|k{nRW-*sh7oa0Ts*+dpt?{lqd|KQ+$CL<Lv}N8P2c_E&B2Rc+8JqO0ZyLIoevRlCep>v}?~RG
+@8wYh`rRymL4*jS93KF%(uP6I@Rl!p_+~hIA`9zm5(5)0Ea;h3DO@oeIylwR+Pdw$2}O(LUy)eauDs7
+)3LNeFn)5%&NziE$nw;OB<GRZYnWTZ3sI717Ii`mMi>VuMtWP)Z*`~TrJIKI>W9QSs$%*+7D@lb~`V?
+SemTimoM8dgIYtSiUVvPm!!sPwd~Jht%DwR>s35hSREbf#-9Y+tid%u-7~Cf#uLF%US_l2=V@L~_h~m
+VFAF+D{zSuQOKY!`l<at-#GbNco>y#QTNy%LF2eI@o;F`$Wr(iC`ftOY_<4R<&9(j!cJa8X_zHk}_ZL
+d>88Y@*_EvUo>aeu=c{~ag>sIRi_vi6lY-OqY8w~L&b$<ns&y8j7`|gH93?;qriu)pe{hR};jk@vbG;
+L{-z9LouO=9gC`{(Fha0QFTq7L9?nJzGBOi7WVJdAnx7B|@zP^{&PI>HEbSy3SNndoC}fAF!l_G_rjq
+y)KEqDXBoJSvN{6*H!^>vxYbk$`9SHK3q{q2ksX(&4#6^mv->z>ppXzmj3O<0?b<2uQs1TKn#1+mMWG
+ILPjU7W<G2M+IWLX7k&OhpHq`e0I{^^(DvgXH+EWs6uZqP%HSx4p-Ki7HFCp1DYDcosDJMnS20<dv37
+Z9-df-<0LiC9D)xQePLb$Yx@$FjaC&J+!q0buwbSFlOR7?fqT;MEFMk)X$$w$I-SDPi8D7aE1g)A%>m
+8K;Z4lW+&%QD;Lb;IhhDrmdu?U*gdOMafXtsxQIgCG>h>G=Dink=moG5V1^TkKIjBxWZ?|UY?S(S)DT
+PtQHcM}}bME(BI~ESd*E}{_*eO7yAZ=uG@AiPEKAv*&(U<!`qS7KK`v)0bC`&#LBO3#bIJ2oQsKLfnO
+5px+25%c{4Z@B{!?|6ef*bpBBa)H2aeO8l;=3CkA?$c!=fcgH49M5py%{g)LHhMUoXLK8=2o82?!M@K
+__#W2d%azSvUs5^?$UnrSGc4BH3a)arIX9wnaf@c>YiKKD5c33jGD(DWM^UJcMncsPH8dxNTc1?7=6q
+-2(R?FACLxK!{gq^tlbS@c$*E;MgY6Ol)ePd=v6`88^Hz=JDuK=6XzKZ?PkrGtjmlArPUhX&}G_5Z!`
+B*Xsiw6CnP64iwaE{I}fY3`^Q3KBu(eXO}U2w&p7ELek+^~p2}w!QHR%vMIP+^FD-EN{^7VxqPJgBZT
+wYLT+E8u0qIRVf?76U=EJPT?6I1kv>A$Hr}8)14fZ(aa)j-*!5dS=ZR&VV*6B9&1lOR`wXR0{SK4K<Q
+Nc?_y6_oo3>g0=KdZw?j&dd$-jrmIo0C+iinb6=^3ofe<X|-X^advxjE1k?XhIUt$=W4+Gwy+(XBu4l
+pRSL?_4rbumjpAdO{dy;m_My9bq-{A$5vRqHsOiP8uU0EbnL}<>dP{NgXu(NUPmZ|cqcS8a~`S<O7^g
+UM{PUR)&zoYKWd)CyG`*7_CIj!vpX6>FWjrzxTiKoKW8G&E?+O5sDyD_Z=jP_I=B`(i*ZW4x0u~_pQi
+${X{9h;_He;K-uMvC#>3LZP+5hAZg^G}-;ry^ffIi;l&|sG+j{2lmUzEHKfJBaDt4-3XA6C^{AL6@tx
+zY9f<r;?fYY??bWuHRA#BHany$15-@=rP^(UR3_Q0M2qfG?Wy)2;`_pfh$4wr`St!C#Hda(zCRU1GMS
+6!5;fvpJD2}PBkzkOS^K?P%$YN?tuUIL3}V089Uyxmn>cv-PHTpRuC!Z5sQ1OzDlQuS1Y(Y|+!C!bso
+Wjms%|7#IEAZWAg^>Yf?P0-PGmfpIkxeBYs&Mc4#RnZ-+A|=!bwYvuAv80${`R45#I7OY#g{>Ct;yZ0
+P58ARx_<}NjpdqfBv0>M{QU9(H{E#Vbp00hh65njyUy~25Lz6LrwBlL5unpyI!<Tw{Nep)KpnTmGf>Z
+bo)!ym61yk%RZyamr{z*!$ubFm~HvNI`Tv6O7v)$%i=uMp#NpE$3t!ksVzgD#o@2`Cdms|d=_t(bq`)
+h}5RU4fyu<gpWJMMPD8*KgY23x<)qfsAZ?(7Utas86Y+qlHGr8QyARW=d9-iWumKWmjIBC1@S)`au)r
+PK1EYLxRUY>i-*2;~I^`qhqgVZB%v&crohX~8NeiytnP*M&Fnb>WwnI%fC6m&yyQ>scXP>r_Z*d7*T1
+`B--k#;G=jYVF73>{sIK2XKBGms!)|ihouW_3&bAs}+TZY1xFkbY=JTz#W;!(ztwlV4%U1U9`SCO&hi
+oGJn25C?B)++)lN@sI0zWnRUC~bsz3015Z!_eH7a5HEwu(D;;XDQdM*w3EKC^p%xY|R#p={IcyN-R|G
+5<FiyZee8sgmQ82YR6@-o6u6nG!3yw60x{B4-N_n+)l0vMuZjo18-{Y&Ts-o>|wQr`X=wTPW;yRJ7xU
+QC0Tye9T4hn#7+W>3^hEEO7R2AQgOT6{FVpK(g+}X0;S*5D5-VKkbE)1UKSVpPwcXwPhYb?d`19>o9U
+ak#i6fv-OZb#KNqASZe@r(`%yABs^b@qnZl{(e7s5tviY!Vt@aqF5hpD*0bt>P;T_J*1yY=kcDVvDY>
+ao*nzhrZ5W@3x7r-8MMljw||3RWyY!-a`K4vSklO4OdWC;Io6esE(>4HO2R!+ClSME~WgzmrOwW*3&(
+FkC`r-F8r+Kr|V54g<N1#YS}|mxGyi6Ng~H=jV#dxr{dDTu4HPk$v7t;TV5kn0_<s1@sS`Y_p*p>fj5
+TDtaVE(jjPg^-CGrBzgDvd#}jcSjC`DH?dG`z&*J0KM`0x{1y|YYa9$r5f3@3CH^fa}`kFQJVld9svp
+~_?7w_Gi!3zvqDP)VnyTD>|j<99(GYw|Yq;X;RO$=!hPWVW`<~(jFz6iw2w12PFLy~gIF;$P-F!D@X_
+t{zNLyx5ChO2SqxWc8F;>z((J<HviH$h{Rmxa&qWnsP~jE%uo6fLeS124H;urRH#ViLaY6Fdu-h%tHZ
+t9?LTBR;!e67#L|{-M?=Zn|aZE_pVH6jc@FLy|ye5EEB+w+8dWJq`2{mr{?t(8jPu<8oJ?lzh>c5!C(
+zZ!mLOKd!bqZoVCk<<`eihX$k<$YR7gAzy+U#4o}1XLe4P;5K@6UPwNt1oCVl`E2urWKV73*-O#aRTH
+n{IVdw<M?TW}I<lt4b!1nqypVj{$eQx9;v;+|S<hCIAxCt@KS24v%Jyt~>Y~C_MXuPM(}Pg*btPdn7o
+j@XeA9H%HMY@O##_zbAP=J(wA*l9ST(T$scEC>;y#Tl$xoFyR+3dk>DRc@e+jog>T6wfcFM2Ay_CzZ!
+{v#i+Io#IDOXVcHDPSwvz)IdCzR;yM{qeAtU%w{qqiT|u}3MYwDzlFIk`)mSS#L=i?fBZ7=dZ!Y^|8p
+049OFpj^g04RiLy+NGGNXtt_+PF_{MaF7)yY?s`5TZpY<%}-ZwQFoWZs@fDLs{X8+E8Nn>+F_GMwk*s
+0mMtxN;%YLp*72uGt-*P5VV87ammq|^plsjA)`dTlUgbC9ZgMOr&vIH&{^}BoMrrK=|GU}znWxR+tUG
+6+)A1VG;-78M!_mbldix(*d|5QEoHsni;AGJdfgTO2!EYCMql(u=Ee^p&#XQ_R%(qiZXX`<6aJ`20se
+Z{8#&+!h1_}@0<W5X&F9)M6`o_iNq2e}N@Kk&(FA15o-o=^KG&1{fJ!;9=UPc_#eyrhDFGegZL(|PRn
+(dS7X7hD&vA_)7HG1O#YKG!OEz;oTTD-tzcVUCUnpoJ7ZjIfX$yzRJz~-5(iJPw~_I2S6kavv-UIU{Y
+u4HbG+iK7G)cnF!?n-sd_qg5F`@5Qx3ejk#X$zz7uyq8G=fZEkbz}B58x%;ZFe*Yg4skeMP}MvGj+?N
+{hS;FHUWUdDhY_zo*p%ZiV(YXCTi-JMcibjPfvv8F8zw6_)2bqzZP2mZc9?N|H5gP)dBcYke34~B&0%
+~gO<Q=apDk2ebISx~H4p1A_9A$q(|#Rn^z3mmwl=(Rw^$q2*Wh+$u{Nx)ImlQ=di}+Hw@&oLGVxn%ny
+J$5(n+=|j5{eEtHnT9UM<ciT%XZK@3h`o*wD*5WOEqLuwI)7aKw)l@96D3lV$)>W$h#+T{{;3SJcK}`
+g=h0Tv+K+8=)(mR)Sn+I<FL;;gWHsLXAZU$`BrJPe(P85k@tUX@$iNm})TAYXe%Fhi#NDY8ulp6V}Qb
+6L#qBWvJfb(DU2-_zZBkJKLCmYOwRh1nJlL4vls3{FXIV`<~i&Zq)n5ObiT+=SH`E*iJ}pl77x*ec6D
+=aQ@4F32#W<e%&2!NrfTLMSL9FgW94CN40};rLcwI6c^oZ^``r6ftxSj2+U{mMFj4f>)H9}x%i?b3t1
+ZE5m5m{ow)gqmC(EA^9{C+7u#+!PGPJ9pASa2J-!V1%wP}e4)}2y(*J|qx<K7=EgE9GM)<OG9IzWeA;
+(80r6aq2oY?Ks0=p5%sQ(5wB?ct)g+8v7as0$Q2gmE@wZO4_Z?IS^_Lr7&U((%<fVBz0`rzoc0myn^{
+hXtq<@aZ-J8$&F&DQlgHE<4Hmz%maNQx4a)Qv2xv56IMcIK*u`Tyx+`+?E^9UdMsmJQue>a<cH`2*`=
+3l~QC5L;x~L{Dkrqj;E_nL9({9ibNQB0mt!p7uW0>k8FH`0yu`dp!cZIJK(A+kJijzkzD%kLPNd+@p*
+yA>(9)p0Cfdd}I=jC~Zk%Afs06X~?=><c9a5<-H0qyej<=7x8o1eRw_a2gU5h8V~y()+B;WDR5REL1A
+Qcyo9MA!G$zNRc1$Zc+RDGdr%!tB(N7K*`j5<-K|dVPP}pA;-sn&9}cdMRVq})crQfZ!;lywxya#2m|
+67DmyMSbXF@N2d7-W>U4aT@Xss)a6^V4ci1&`E=%4!U?8!;Pn4g*P&>g6@AQ5^Ry$781r4hz5_s+oh_
+%L8})64dOTcnrl1NE0`jrKE;jcx|W>Fv^JqieN&@AhhUooh9_9jO-YZk&m<@V)Ivp{K$t&UMj0sEYCJ
+CAzl;Pcd)7wx=w9pHz)IoN!K(-N34!gKH@3a6O~ct*X#{0bm+~-KR=lu$?E&_T0{UVsJx5_?{R~ufk*
+>s1_A78HL)<w*TmkDiSDT;xZ6%3y8t)R<TE?R4VL8q<gMmKB;t|V*ZGpH+y3OeO1WjcbZLbu_-nA1on
+;RrHwGk4?C(_`n-Z&6s)wQ)Nl8&-hs~#doOdtW0-7%L;ZFat18V77KM_02Bdzwo3$;sa$`1b`26hl8h
+6_yj<{O=peTCjnIn6GwhmvX#e<eggMBA^e-`3k5#XA_?`WCEe=>hg@6&>ckUItm8tqfPaQ0E7FV$8X@
+mX5C&Z`b>8`wLw+Bze?QF{U+>t2I596Hl0*B4yDy(*TEm{z9>aPv|H=)7Vecd$bPHqPk_YS71~7?wUh
+JU*jpr>7Agx;5I3UTU-o0HZ;Tb^&OoLAy|V0T=D7(OyD(&KTW?wsl7K3ho<*0tsU<6g03$I17<U{xa_
+7>x|uI^21kdaNXBDgY<O2oJTB!^o2DpPMO3q2<+S(nIvaWVq*ps)+jMeS;8qAGsuHy5OIp)HuFd4)qo
+P$0cFe}Xg(*Y(H0WUk4K}0frGE@Q<gosw3)mtf9fcm&+`ZJBHuZic>Zj6${(YM57V~+)3*-oB(%d!Fi
+2=ujdmx|t_t{KIfU6aNsac5GoC@1&AvCwpDowL9s6lGJA|iL`RsGD!QOz66|<>l8=rc{S!b6Xz7Z8Q%
+Lre&axL_v2L2w~Jb$S?r}3O|P!4;rELCrxhK~?eOA*C<7Ft{Qupdzs<MBe(#<-}>l(6~CdGUDky**xD
+*_Ivc#U<`-5Jno2BhLa$g8<E6)IjWnX+d?4OK%*@-pTB|5Ks!o%S`)9*Rm-sOX>*>*|TvA-l7Iok*V2
+S^E5D*v|;n&dvVJI!6;Nk{>+<W#<|8Fr%X+upn8sX)0f4C@;w{upqQ~NE=oS|$!-+EJB6u#byt3IbK#
+;;kT`<fviPO-#ZuSWHPrv*ARGcbq$=)0`KxWsWZwzhgLG%MIDHKQ9LVCrtBTfLZ_3$|>>{0WQQ|fC0r
+E{qVhs)QsV%M_DA@^jU5P^aF{?47a<iOIxbII0+oxSdVoTf<23k=h&V&DqWuRS&uk=oqd>VW=C_M*mc
+!xL!k8>YobKqM0Wv#tV8d=Ea!9#H#TwAozwX3Z=2<y-0#+~QId+iYqGq|%=h?Zu^oi8%R%c1N?wVT;X
+w-FT)`)e%x4enO%=oU)sPZ1RFCmyG;|0>x&!xAa6R}B)u&IMm`my$li)B~$gdiz)Uu#>0{tiWkX>R%O
+vvs+k$%4R?#voFrRm-@TnnKhsSX**b2k?D9Ww=fg$MQrs7>Lk{KZNZrxi1ceV9<SEfcN(I<(c3?bQzh
+&L>zB9lwKO07D$dE`F124uhD>}`+TG?Yb<dRV7StCWq4*xeVd)9RYDf?^CA)!1R99zoh8&x=;NCc#1^
+*O0J@hFTQdNX6mxG1rBD)I)^t3Lh&gr{Q{iSd8xY3(ZTZpvb0XZV+-~-|y{Fy+!84^A4?ZrUcz=(Hs5
+WmFkp0Bwiw=qVXo$h+q5<(LAktc&V@)U3PIXGsSP7yU$wPBV*=~AIB_zr8dPPMg4d-MvlpwKp2X95IU
+E4HY3QD7)S7h=^QbR#zXn0%u`nGZVK!7=%%B&q<+BuY^m-pqa}vv;Wt0Fy$wq*g7v`=GlbDnG9HOPdF
+qb*oirFP-XA5ke{maYrUFLzP00$7>5ZNzdNz7*_QGls&ar>t%Ppb0MUU!g&v;I*nbQDwg*)@s*r?s-i
+atn`Ek%RQZ^;_f}&QY3rlfI89wPW?LHW=401tVeN0y@ToTLs5PrLCaBrw&9vwPxgl(aqvUlrQ+(S%3#
++ajg+BE5LL8#2Hn=Hk``gF+K?|ngO%}V<4O%cYpDo$gmaUXQK&u=4e`BQlz9F{4DBp!O@gsiYj@M?E4
+2fW@cOQN~WRBI3A2HwJPe_cmOz$Ffm`f?U%2nE(E$-T4&%8^MGaBDNPj?!<uzTj(&N5@;iWV>km>9_J
+)n-qS)VzUYDp}I9M$q22<bJFWFv#+eK2l+pwVlUpDC8=pdTj`AD#cl3oWKzW;jaa-Ci@H3MjabqMm4#
+Xr=|OO3eTyHrGCLiEZ+$cUmAg0fHABpK{e*M$!HG>!71g0=;OH^)3gO=NL@nNbEpky_qd|14f=Kni)9
+KTQ{$e+X-?OBCF06<jK1_<_-uN8=_0&WXUAPQ+~2J)jZ@n1O^%=m_Fv=d4W-ivG!OlB_TvWob}c;+6N
+NX!(TX@|x$E#DZ9GZ8gYJvoSvFI#(8YdIXE){9FU1v~wR!063Y|UK?a~@n<=893U5;t(mDrUPo>B65c
+Xv?#lm0kLlEg(7^{8QQs^d2#tBNZY--laL$5b0GU4+{2VwpI;%s4=;2Sp0n==uHhWm**s73RC*402(0
+orJIF;=LGUFc8G9@2$Nam6h!35Ot#C;(M>X<g2eRmG`RAPYLIa6>?xu$M1<}&jLq_3oDFl#mGi_SiuS
+YgO2EMoc$2=tL$FJ;7&L(Tqj?opxuPr#T7SLhuIU%xEh8NlF`4a9&=+8lX2--I~?J)cuNpF=zY2)zJg
+9L&c$=N;QCATOz@e||J|WBMDLqFR2JGx??@;n`<`n;cI+K+z==;7?{$^spXj(eyPYimPBpvd3iO9;XK
+qTZj>Y!)j=1X>@4d3enQ^6(U21(8AJD+bYaE8MscTg8YTJq>@^mH+BqjA90tslZgTZHA!ElVz<}SHT!
+2Kr^8!diIfZk#cGzLn2#39LSWX%=yZ%{bxP&ms>YI(-#-W%SzHKR7kbb~~L9gjUonumc}QSc^K>Q)C=
+ux1dPbr3EFwJUJM<AQ7`3OKv5jnC?af^jVix7Yp|Yqxe@1@&lQaJo<$l0|D}gd{c_?Dwb*_S^u2{kx>
+*bu0$!Sa`EKcHeZljuE>p9dBx>Nx-+At`yhX+CX)&o82zmYlh^98q(z$D}hi^u#SA#9_vW8s3Ul}<l?
+!G%wJ-RuAV;=>P4`lUJP!nh`juVqlkoKw!h~n9={qL#Y5#(JlN9{;ISmZzKh+jp5R&`EqVy=k3J;V8T
+(=ODuXC7%J!_W^Ecu>*H9~Q$L100Z)lH~_ctjBSH?q5?S*_NnD2S<e<t6P9r+%k#g~p7q|gPpnP@f3P
+sqnrBHv>$-@|#qEDA2?ZIKZ(nD<O^*I5qF6giye)E}AIN&wUUF@CJaXQ7Xo(s;$cQo3V2yQN<(4d6(C
+6wDEg)P*CVQhSbsOP&nzmK59~Mrw%XZv#l@Ig%*-z>%5KR~$)}PH-e$I>?bIX$MEl(q@k2N$<uxUI&o
+Yal8caRUAjDrR5yQbdmDo9ghb{R*qw`N$DKN>LJbI_$Jo(avbZDq~kcIk`%>pyslE}&vBebmoyy5mQ3
+>FxP-Wp<8_E%H88vZ@j8y9z9F6ExDxS`9A_IAD>;ta8>I@4<7P<dLqp?6#`T8A9f+$rj(ZrTa*pG$Kq
+@jgHZn?i2FC_QDU;)Pph8OKI37)wrgA(VaXrUz#abH4@p8n&Ilc<<V2-CF9>DSSi2HE-UBn5;Q9Y0@=
+y}>AE^!<W1xlwmUWIrS$JZjhi{rRnBW>b%HR5k^Tta*;$LkP(lH(1Cmv9^xf~AEVS0ZlaxEk?Pjt3w<
+ljCdywvpqZh{td|9PvnwM<E`{@fgH|IF7f&NNSEJBJRfVnTR)xV|X&+=Qy5@cs0lINQ-oW<9Ueh<9I&
+epK!bc@pn01j`%u`uR?qk$JZjhoa5^e&*%8Nh+8?1cQr}r<JcgV-wy#J+_c8J?x~M%`)Lx46WpaSEW~
+&v*c{|G;oL?IHru&PH*PZ%Y%XydA8wNjHa~NlD`Vx@Slfc7->Gd00FMi~$D?C;lL9stZnKTsgn~^vw|
+Se}goDj}+~zrM69qO?xlJjzi2<8<ZZn_T=)oq2+nBgbI@nC$HdDBb8EkaiM$2vTz-9!u88p_>0D<KIZ
+rORPqa^}M4Yzb3>u8d|vIDoQi*rhMdEF&IzPLnZ|5k55ptqlr>R>2ipArD7R{f5ppxs;Vb=)E{K0w2F
+aAJk8-w|VND@|vvY+|80g4XX)SOZHRR@U!OtBTlE6CSxcb6sERS~?}b9vc8|h1KT^_qmkD2E5K5oF61
+T!Lo=wQg3Z16>}4NpxztCq<8IO10uG`{_<W*FU1C6v&HZCwukBMQ`BJ(s6mT!fk^DJ!G#jgRu=!fI8b
+Nb4dEATW-t5AC_JD67HUS~T+nrD?NxaFy0shL9SK@dng0?qzvNmz-RJ7Fqt-)T*7k1`K~SwyW9@`f4D
+6b<@;&k(`5c?giB+fRN;S1X?5PPHk^oVNnq7{im#%EW=%cTxinrp{AJxWPj)?`Q`&OFvyWxg8+YR=U_
+A4+xDBiyqhHzCVJzmSemK$-QHW{=-9oGu+B+%zN`#!wbl6n*3bmBlf8`bjoC+w)vegLO%yDU?TS?1?C
+Vux+Io^CIyu+Gq5+K$_5Y(JDYUQZfd=I5DKaE7YV3a&J$<{t!QD&Fn~Rd$DMn_-K5ovG0khI)0BO|wV
+rFcj~!?yXhnyTHKVB;6TNfs?l7MaQgtit4Ss#092Z@B&kTs{r@rs9RCJ&5p04h`=rI&t{=}q6mIz<;x
+T!+`Mf4`3yeT*?tjI%eL+~2dd)kK4OOdS3QvKQh|ia!IO)17|5fK1|Rq6?OJGdbY4oE5l}UvYTGD40}
+u_c4a5n5Tthg7^TzW0^>cFgF7gt?FF1o;TZl%-XZP{dtM=(t`$2K!v9~%tMW$^$lNvJtW$n(h3j~22n
+}OF;PJld%m-Fn~W*K$VDWi5X-U=mUV}{N1#r%S76WO)B)<^G@>bIX)B^Zp)E9m*;D*P8p_!|jOm9F+;
+FHPcZ^B5f;ihZHP#X9%L?`AW=dif2^wgq?dK|k)Iy?ou;hWd~5Ao2y=>`8<Qb_HmfZn%PtmDxpL(K{@
+^{Jyi2hFEZ$V~Cagzu1FKIN(0tX<{M~-vE&I#NwF%sDPmMGM8}fk}SO?T*74+5hh%Eym=Vzq5vh?u9Q
+|SZ^4znsdOm41*gJ)U`tM{-fTOX^m<Ev^m7t4xcFxG@0N`f+wXvMmlG56uUT~WwMfd|_P}_YC<$sq$P
+KQTB*V_MBmkQ_^hs|PX`?%vGCeo<CQLyJMeHw#awnuh_2hQ@c6O0`lXNd!6i%!WdiykU;i5=_?+<v1B
+}ZeK``P;)?+@V0BY%IuODg@gg={KZluXzK;`hvLK^94IR|~fGgcxb$x2=lq+Y%)%b$wb(PHc(z%hsJL
+(ppVRws^YM$({W5`~S~RrC^|)fEocu2xt^AS-^P$J}%%p0`3y<lz{aD`ltn60mB815pbq}_Y3&EfNu!
+6LBPWT{vcq3fL^{Fb`x-ffHMWm6fj@FX9avmz)u7`E8qnI-P&{5S-?;M?+{Qg;4A^t1$;=r5&@qT@O=
+Td2)IYUlLG!Cpt6IIN5Cin#|k)A!21Nu7O+Ubasl5EaEpLd0{$dmgMdmu4m%3iTfis*jRMXTaJGQi0_
+F=?F5p@L-xqMJfQJQ?e~+k}np^vA>Vl6q5i&BLL%&x!Oikmk`7ihjZoew1W%rhTE5G)gh7eWd+3-av6
+eQ|*?p7V4V07{$Ih+H3xd5k;sbnk}K%xW<**uusL5w8ML3<jRKnx_7Oe8vH8=GT^%geAPT5{%?a)D45
+P@6+?h=rKoZy-q}nFP0WT8NEg6D!F8A10!K&jNqwPs8Z8C0-0ph1e#NDe&j)M|Oo_CB!~~jBCXeRCfr
+SK(x$POMWJUJmDk){?KLs_|XDaR`8ic%m6j;<-q?okOTQlU@;m2L#a}kT^ggA#r&p&M7e;a!2h|7%50
+E#0obGh4GXwtleu8Z1|KN>JVv3B7P@CJmlOz>K^_wRa=_jTQbVSX{!-!pIn3Q8;?zKFHbx)aH4twOix
+u6>@R#Wb!6^;|7Xy=$Q_TX~TxLd}4**@xJIaOJSy)Uquth&PAi0Lo%Vhq!WGO6WIh_VD*)T0Lz>f)d)
+{sy#2y8QfT3?oWSQ6$qcydn9lonGeoAHPxXb#IA3**JoB&3e<lmKBfn4Hb?dx#^yvmu0)m5pqcT5~~G
+4a8uEuyaKIa_X4?xwLXlahEKo6m>3p&hZVtMftih-%U>G9t(7)iu|!STzE?7u++o4_3tPj&C@O1A&1P
+9oPI4yklIRm41sn<rek9{JX@sd%~Au?0{L{NDbpMFPv+-*Ch5)VT}$!Z@lV8ur8f1SjBltTJ~PWPq&p
+A%TOgE;(P*hA$#t`lqD(LHpWr`<Q8ke~#`z#ovbYIzPR_*}`5#P%-!#8-AU2cWnAh(d;f}51JXR`AH<
+Tr3`c2Cda)lJ<Fb-3l%1&$jM00w>Zb2_gw1OF|CR!NvR7V@}K&$b{^#7Ui(=?`3mi{Ttq>$-1)>BOD9
+9B+3S$mt#`X+1%OpY|?rKI)x8t#;Tx6d#7;AIr^G>e(7E%F=glz+ERKL!4u3qEAZxh>?VOuw<cPh)w+
+Ya8#e%wW!76f~@NGcy|tGt1oYFsV1Bk&BF+<NBCV7}wTvv!^h}2(uir?k8?m3v;<J`wDZoFt-=x>Kbn
+DAk6u~93aeUVGb1L3W=LL3v;3{caiD+$jw1={KDK#nDxTkU5@VuZq~^033Cr&)|}<$JUKmtd4U|?cij
+At%%3n{73NLCOoe%sFek9OZ((`~>Km9B(^(%Uw|cQ84E{8b(@ix>BVe;3B$g7+g_l#l4P<|ycSBwy^|
+{Pvme4mi1*G=?D*^N2|2tW@=CQ;%YIzSvKd}k55svf<g-|wD%ldM2wk<Og&^Gu_f#txp4Qb<|c8iOPb
+I0G%aG-@AHBpH(J(Ofvc=(J&4@hVYD`Lvz^kn!!f}8W;rvx9wgD90sEP_hnp(KC(@Ta-qFVyjOquY&t
+ddQiZ{S~$F*VIRW)1Trt|D3}wb^3F;%|AJ;=D(f%{|kOM=4twWx&$a1|8Do!qy#7$|6FeKudxLDPycQ
+uKmPf_pR>u??Y2c0ayK>KuDp$MH@Cq%${K(91xpKyiXVBjq;#2mdD)7`%2z)A#FI}w{kK)mJp0`0HP6
+4W_QjW8UiZqYudRRmjW^$V`<=hP``-H>eE89Zjhi-a`S_E6Y~5C|eaFsSyZ7wfxBtMw%0q{bR2@Bb{K
+Ti9eSY$bQ(vC`>g#W+zdiHa+3$b&QL6c=_T10EoUi-!x8E;ZtiRN7`N|(xuU)4^;o|D%-o``e>E-Ryw
+w+4t+rERJf5%P%ft|Yqb?w$&)1zmv;E>*ZLi_d$>px)Npzy&%A|i+0F)V8Mi0F}{?i?MHGJ8&{DQ#|g
+#{CauW@YD?AGG9JZS&^mEqLgjS@+(Tl-#=e!wVNJUh?0T|Gypo|2X}%V`6o2W5?+Y@#BpN6DB53nmlF
+dwCOWu-gP%m|JL3ASEN6#2YEZyhpu3Db22~O%v=U$XI)}NGxKB3%;n9@E1Q`gZ)Sd?nfXJPrsj{DnQ>
+VYb?OTEa5-q4NuFz*k&&I6Gk=OT#hQ_mjrP-WO_qtX?>Eh{8nV-JELqH9hM)uP#*|#Ec8)b;o+&ZMY%
+@FTxg94*5`c2bT+^7GJRWiynF2cBWHy5|kmx|mG!o}uoBaGo@(c1op>usC|B<Ene(vs|(G?dLL&ArL=
+O^ZufOZzzCn|A9KIm(p)Fme3E)q>>en~!rBB0i#r>85G9(iW-e6#rmeNGR5BS5o6-7=TW)bd}d`0Gz_
+?v4Lx95xHYA7S=`nEfDO17icQ!=F|_{OQ<em(O3~NLKz$0PS{O3-%MmT%2q>1)y2Xb2<CevJst)&4u7
+Eo@vDk9CYHvc$+x<o7t>srZ6WkeSQw~;ruri{LW@`fsJ}Tr<1}oZrPU0Bl9owEAuJyCG#Wm!Q)%t;6s
+jIj#rKc>7=lk7z-PhH}Wx-ao=c%Gj!-Hx0$~gY-E|uX6l?>rh;#0y973tZuBvQ@#$<%VRG?!<@jX!GC
+i4&93IM*eneK*h+Id3N`+s4O#=Khh%A5~Pyd8Lsi}jwArXp;RumbXX^tstw#iW-CgjX(VxBn1nuEqEC
+bLy`(AwtOa)H@+TPC9yZ_8#GuQgjTFb@(^EOQ+BY?^JcrC1yZ7N3&Mj1Vuzh4hg&(nNYl3&JlzT1W@O
+V;C|e#cH#prZ75VEg1rXbV;{aavdf}FH>r^$;o1>jhhm3vYF)+n}8`+q=Vrx4EjSq=+46ZyStlPboXp
+!W&(V;eiMu`r&v=mH71KC$D+x|)?}vGvgf3mEF&}_xomK3VWk@VWn`LCHP#$Wj@gv0G3A-&*sLkDGv%
+5&EgKE!o(2u2CM8>wVg`??5C(G%8qk5w{8>yXshTuPP8NjVWO~Vy2biBZvw<H+Gp%{hX0j~E$eyc7V~
+pJ3o@=sNot#OoH5J53wV5+B=5Qaf3;%(8j?LP-2XKc(Y2Ce1jyWKR)ub`=UPoigMK=0`<o0QnZgWjmN
+VW{CW=>A3Nt2T%hv4)EvN^@Zb}lZ@3>_5@$x{@<Wo4xR2C0fsfOvfdLO%3CEH~XWI|VE=GqOcO-$2J?
+$;!x1VTlBy1M>qOwB-QfkcLj#c~fFo?w!c+@bfdQ>6nzc))b4A^=*=EOdlqt@Y90)GXK1sI@7u#y;4m
+qiJkq3Y?(H7?0~42)3;BrKAIFE1?En(7;g06qYucKnVB=6<#(?evZ0lHY&}g{$#=_KTb3!?nyU#l&4Z
+ekk)|<anXL=@-a5Y7CXHN3Q-IMqSWM+Y#1bXTl$9fz(4Hg&dX=7_8-%dg;HG9mCQs2#IsGE-o0I4}Zg
+YkGwF$=JJ~)ty9We{_s@z%AA#)bY0^JK0tlU|-8Cm8`Q<B`C%*wkXa@J&1rYR-YG%MC<n8KWm!gZiIb
+vC)ZAI7O@<3idDaY^*;pd=kqeMpB$Z_;71s;X^;w|P}xJ_q|!AR)Wq%m0FhI{^6CPG&(PT;Z)C-VnLM
+&x`m)s)*kZbzQq^pDM2krP(97O(OF*8R81`B7wWSNuU*AswWAIRFc5S{#6(s^0ZciH+&QFtf223<W7R
+d1XOf1`zQN_6C(RdhBmHSPbWUQ`jD<xZ_+i@i*%jpNxA~vu0sN=J5~8t_^RB9D$;|fhH$wsd>J&_e}n
+HsIlP~e_@%-()dTo%gZ!I){n~Il2#aPp?0X@gCx<kKZHfL1y$N}}ccULqFE`>9=}EjMw{iHfLethEl#
+s(;aH#NcAwCLUqR{vdO{y=^OjQ$2WILj<1l4t}4yfu_;b&F^`H-Mg6$zT!js!&lJ~^PSW3^vZ`wHM!)
+rP1bO;sSfYH}klk3%c*{vblm30?vRLi(icf;8Gmx+=Dkb`?Hm??li|77<eIE~f?LBc!7r(DIwCHY?l|
+#0~S;!-aS#p5x_5{+Rr2nEbv$UYtMRJIK<=o6Jj}I|vy%j1Z4gf|mi_q)X}~(q(EQ=@L1SbWy|;9)k0
+O2LlvA-XzEx05W$1x*bW7qCN4i?oj1h0d82HtWb_1p8_H80)dafA(-bar$zQ?Le7kCObb>Xtv(PQ%4D
+P$mc=@xESLX&S_lJQnBe!<7(yNe^F6|>@K+LlD}1RQ#2@1JAL3i#d!wDBe9w&~<Q4el$>CZ25I3ZE;8
+aI_s8DoLl1{*Xr>Ov;Y;_va!R)BFUA#yas~XZx1!>lfba|YmSfV1piv&PD4w&jq0zj64$vh<$L7pV&C
+6KGyw@Ovf*6fq)9qtAB?Td84e;26tU50e3<7ujLq*ecEgxm+;lQOUBHbiapAZn0FJ;bL%;psv=hq#9;
+0+bLhe31SD;1)2%&&<*Q=z{FkyhZU!mg!!D0mGowM!LSfZHO=M>pP^WzsI00{s6wmz7+AZJoSb=^<sI
+dV0Eeu@fy;`?CXTfd3Oh>iZSrLE#jF5_(J#&$?<k@gEr5DbQq#?ggKo9b=BM$rVHSc&5dDroJtmtC)S
+BEjcw8}P`>YnukI9wieN7i92rQ0f9qTqP~EZ0ucE!#H(8y?dG#V*md3VV(L>Po!Pj1vm6eOOP%fZsP8
+DU-;$P7`&S#*GtXafygub1;pxm^BGSilHn%tpIPJP+`;3b4?gYTX%oy)&dTReo90@?pd`Glkw5VAn#3
++rq*s8f%7S9?}@RJfbnl3f#B!m+OE(d`az5)%2KYltGvwP_E)>$9c{#Cemlk?|NI&pmeQvJqWQ$ddBL
+vJn{WO}a;>lJ1JTNlWqWbGdoEM^_W#zNRtWz(Jm*oAo2oE%igvE%F1>P4O;K3tDDqBY7TrILaH+cxNr
+pc#)9xa#~@ZXK@J+?5>15qb6!gBUeZ#N>2Zq^oM@0KwtApBTZk?-^%VwUMJ)!_;$+a&_hYuPgRlj&@Q
+yMu$Da;;|iDCja|@YSfR~GZ433@2ip$OZomiaPXNRZ^_R69JWXGPG+o_dnpU}qItlgO(f%`eKn`gC0#
+hCBakb3v;!mJ&gzxJn<*yz1g|ZI(KpC%gZhzPOgOFYDT^9azfPcRg<UHM29u@x3#zOk~zu(T>HrXe!N
+jqVIcA{n7pbm8dg}V!Jk5mvvdpAh4wxs>z-iZoN1@RP$6zlieV?Wh4uDi88q?<3)Ej8&7+wO6%I%Rd6
+Dz^$3ksf+S8|4detBLBEw;93>?JnBtPLL-ZBHCG+hYNRvQ$e_PMD;%K+|Grx8{!qN@N&U27OrTI`RWB
+c0vy8gRne@ElhdO)WHKz3<7mvQ!2T|zv!ayq0C^c00{x6N68MZDofP3j)(P-{qoTVH>2CEU-BZ<2CRC
+(*W8a<_IHWDnSZ$!QSV3n2tvAv_G>UA(wJ%O1*;ViXZC9R8<I+JM?&qwAHnUx$-Q!@FcZRh5`)l7ZgM
+aNi=J3C@@3_0dc{^^IVw}ku%ZG$U7!l4Oi`}M}oae{mb7u27bF!YqvY8HfdL7c1g|=BNFg%qhD&1$KW
+LU@MSf;?RIMXD;W`WOalLbP@=49vQWSU~FmdrR9&RQWwh&DGjV{W#fFg2$!O7a87fDFgNfZnl(xU&n`
+Cgh~rGEHMMGEEawvVd{&n(HK-BWjWN4A%r(rZr>C0;_3i&WwyyQ*3&Qg*+uBa`*%N_yMFn#hR*z6v0_
+3rHfb@h{Gu|=)W<2oP^;1EaV)~iK!x!2s)X>X6EF|rtxu;C&U>eh73&2%!E<8Vv5xQ{|qzmAgpnQHqp
+QwK6lZZQp{s5CW!es!?ieYC%=)nY^%u<Yss)e6q!?iS6tK}(XK{Q$~@Bz_GT`H1@d6(0yC!0@@6@$C5
+PSQxtzw7J>xblJDo9{8kaZ6WJYR`#8#Z1{QxCz)!D_>kgGGzw#}VuvLsqextO)R-6mTzr)OBKwv^0i+
+4D29L3i$scE+5X2W;lCF!)CbAR10W1fWZ7irH$jU@5Vh@~k8T?Re0*JW}go$Ob`EGBX}(F0wW|b&5G7
+J2uA#@%{p-g)=~jIT@I0<RH!pJDI;^DciK1Lrumsn{Kk?GSjEb4=*23J6*)Ok~THf!cwYRV;ST*3~3z
+cJ_ER7Ws)q!)Z#9jDRKpK0NpT0$o)KrQXp~6$i@tE47kiRS#QY93b!elCX<=$be(KUNoB-|6{#aCi$n
+r%tP@b0=PIx)#bwXSu;gUpED|phP<b`(ay6Km<ER_tTUOs%@J{YljLpo&1WL)YnaDk8nj)L-LK7>r`N
+)@3Z8_-FLQGA^8D9gGftl!^7i{u6ueFIL3(Jb^IXt;Qq-Rm0sTp$~5b3w~#?}Ui<+F5LkZUz%P0awAb
+6E-lMjjKj8!cuKt*bF71xo&GONwOy$a9M2?_?8aV=b>8yjFwu@V=875*1SXUf|i5n~ucB*wWIVn1Bwz
+d?jM`k!-}Z81_!&XkuIf;yzF-AdNCoS^JeT*F>Jg6mn``QqUZiojNg%=j<ex#uZHD5Jq;qv_hFDB!ZV
+P!PyKl$kiB|0FEr1W{|&rxBTVn`MH+n<|eyB)Z~^w)bSO!^wreA%PoEV_517h*Y9ROxy}uF>6-!1gb{
+M+Ud^$z6O=l;;~jW%eRCBM_pspJXnYC{PZZ-DE4Xh259{N;5v72h0(uEIZzs2oA}m6cpsN;kxKF#i_|
+U$GupcVST49bA=0p)@vap{b;0!_kE)jR8u+Jj6bCIyU3AjhwB6w{h{O1dv3j|y$+zW+!k)VV3RpXB1=
+Y{!2!OuDo-)q8tqu}qbaQ|Gm2YB$bkkgEcU;d3pC;p|s=56h*+TMS)|34f4-%bC|hX3pLfAe>%{jo01
+ey-vthhOyzKQ~b#AgSZf<u`G@f;P6D`19G%?f+Hq-}_Uy`s41JR)7C?*8U^E)_41G_ttj{{df8PSN{H
+AF9Xef|Hq4jxvv<Hxwx?{uK50Y^3nxMdkFVezv1x3S2!$qp_&~Ti&>n{;Ck@C{pC|HYg(aSaGk>#0YA
+D;|5^bzfd3;eoqee@`Tiy8rF&YZzxYLlO#+2;(y9ASX-Lwk`%ewI(f{$2UsqoyWbk4h@;bD~u}<!JS9
+v>X70@hTrhw@JrV5xW;7kD%1=I@|C19w4K?14;^bt@gpqqe1z^i|7*dX8q0qX=jC!i$YSplmBJSpHl0
+Y4G&!#|qRe@no10<IRYT)+|m^8`#6Fj>G^0wxL=C19w4o^DNbCXKKU5Kt|kQa~cheMQJCV6}i%0#*pP
+Uchnz^94*7Fj>Hv0wxNm7cfS^C;`I-3>8o#V1R&X0hIzaTo(KZSS4V^<)-xC73Q@9mJ66CV7h>b0!9h
+gU%+4i)dETlB0d5CQ+nyTflcRi{<pc`GA$xn2`}`u@(UuJ>qWka_{@WwhI12oojlhxP<%4|x*;5%js4
+faCE|BM(3N8UIj9hPHcx}=Vth%)ySKSn^4<0ipZbAsm$!mg0e%VJhk&03co+unb%0j^jP3w!j+cUr1e
+gq8q>qBZ$^L|#06$X!eiZ=iCD>O3{3(F@!2{Rb;j2(6hz8&_ylNjO7dyikzca2ULA(IpgD)NEd<gKOu
+x|kPP8ZJ4rY`JGA7+nmTM(yT0kBtBZXXPAn!xd`6qjxw9^h^OqczYr06r4ncko34eiontI?_5<@B?s2
+Pv8OUBLP0!lk>A0;N)N^Z(u(a;9K|sj_|$^sMmle13U%aS-?*NoZg#|qBaUL6X4d~IO`330vr|!?H1r
+s0OLbB|3-jM_a$U1*yCP_r^2AEMjW6G3P2v<c>s?LBIHTHPXg>U82Smog8@z(%<0SoxEH?DV80LG4}-
+Z};n2k<4&i<fngxz<!w{ZURRAx**MP6pL_j)4V8wy_0@wpSH^9RI4u($&cqG7O5uDC)fW0HRyrBS7B0
+0@efDZ}$Nr0aV{3Jl{p^zp(#|L2CP##tf@RGnA06sblSHK|M0RAwHhxLmh<iQcpCxE>b;KmW63;_In1
+ov|R;GR*S4}hP20LR`5eIwv{fCmL$32@SAj!y-69lo`AKYt7%3u1VATL^Gp45x|kLoJw*AAq_sVDGMA
+@ZcC;2F?OJ0E$Ww*jEBn>$ofk>)~4n_6-1I#sdF<BfM`c&!c33KaYcY1@?6SSL%7Z?*e?*0A&E|R|DJ
+-&*^Le=x&7a0Jsw1-3grMSpb(zh4KmZ`2e4r2K5@l0^Bi;r`0Zi<EL}~Mu1&sK;DBN4Zs=jxna2l_&R
+((fWHM$I}_@xC&&x1ZZ^~frGmjn=5RVC0OzJc9}ND}0S246><E9v)k44}fZfx$TpEBWX`G)_fC~kV(3
+sBC6XC)PUY-$-e}LCPBfv44p#K3)9l)q8NH-4!gXSzAFT(W#{}A98S)Bf9fGe|s4%j~lFd&EfK{zsp^
+M)|T4C#h+0RCzg<qF_y4?-OQ`)~`CL-^bvUMs*L7)k^Kj_?=wMgm?3u;;@(e}e&zSjhPt3Gl>1(Kaq3
+WX>W{UjP;?;`T)VUxP0X{I3VNTi7=M?3WMiAkqQ2B%g=HgK^6WpnQV;a)2)vaR2K7o+{un)&X3#6!-@
+}2-g;YZV5QTkYY|J6yPO+`#l2m9})BcRy-o=8o(zW<@x?3zzZuN9icoRJpLHS4)_UxZ<a&*3HVz8e=8
+Sh2yo|fy!`9}c-Lyq+bn>i*8okhj{*3^8qsb5{Cy3N_X5ED=Xu#cXkH7t9Qa4r??q^@pnUcR_z8T$fF
+m5d4)ij>BLTL573v+}egH?l%4uf4M#%T;xhxXE(XaD(V*q{xUmp111aRY<kamDq0gQVa`~#i{@Ts?XT
+&n<nBk*c~o!;Se0sv;b!|PEdz&3y9^+*YD_7><b08a(z`!Scx58zh#l+e#s0F3;E^NH{a_yWM*?H@pI
+8^{8<2H?~k(2fC)FklxU%K=B&e-|%%2+MYHe#!xQ@8<ou2H>VW&{hDB@R7YxegH24xN$F!cN4&}eZU*
+omm}QA?GbwJ=Xe0X2jM%7egJ;GAD;_?bO889CGY_FTL8x#0{$SKbpT&F40=1@>j3sV0{s)<!2sJIhdK
+hdAHe+My#GbG>;$)84zSB-koSNG0bBrI5#Z$jV?KvG0z465r;|{=0S^cGJ$&mie*yOT0_4TG0FM2F^N
+;5a@QlJP&`A*DKL2n?D};xIJ;HMWM~Hjg5l0v%aD-C?j(f`I2^`^*0!R3^z!B~fIPOW71de--aqlqV2
+yuTe@`G@Jut)fqz!9z&IKrI*NBFhC5ndKJLch}-$Gw-40{`px|MricX?JDogP@Y;w<BzAEg#niOZak{
+lRpnOE?EoqIIh|R5W`ggM0W`wt}BA4d>o_%0K~P!a7A-E3U<Oz--11x6KKI6e|b*D)!DwJ75i1K*spK
+Jep4&<`&zL-*^0f?f<4Z&;Da1W3$#g)cxT0B0(DmCe-(}9fO$}#m5t_##h_ccH=09>z&)hVT=hDb)s5
+!7E#Tj+(OkV%!On!sX8kq=x!Wl|$aA70Oa%rmoo_g}@i%bbKr(LJIM5eokc^BBVzb$JMu8%d58tX)tH
+^7wy+$@~-b^YgD#+)be@?DnzfQ!`N<Q<9FNUJYhdfr9??Y63=g&XFj7RACtNA`x=g(ib5Ivlyl~<3@%
+1hw9w+`pwz#YPZdH#GfOGhdzE18)Mi<R)-d3rUCxzAr%r#uIN=^kO8e?-}U;nUKta`O`OUwI@g?Z{qX
+zRKM*!Tu_mf&SGa{7qNzvxL*<=FCgXe`V#7J>23FqyG)Y&&?1&rS>D0$Qsxoe^-xO{sv6-=+5|GdUaX
+l)yjILf#&OzXBoi5)l8rdX7^9_`}JL2c?3jA!}L*}e7m5-c|He#59lbb6%X+>RvrN#%Q3l>S6_d2a1{
+5z%ngT*tn2w!CG(3P*q_smqzw?9tyu&AhhLsr*>Lqpeg*S?gwku%wl!R3&gdV~w`O$%WzH=9k%YvZ(L
+cK`{3Lf~^cip5nJ7!dk2{mA-7A(<yXKFEBGBFGbA?ZlPk2Mq%BK+ioWqT7<wMn&<`V^cuJC!lr-H9L#
+d59|W&Kv=JTfwpBqSt|DO0A9haP&UNf|FMFDGxj@do+u!w*>*|Ln8R$oJoW?<nVH+#c{Qw4v{A^uU~W
+*!(cvkhYC1*>-+g+5<Uc31m^)HcFRlt4)HE@W7H^E+%aNWXbd2-$UsiX^ZxMKW*ByZE42HHvuL+5dYv
+rN<T`Xi%a`#yJuPwSwdgl_R%z?w|NE9Uqb26moB|aSSg0_#^UbXsoJAEC9dKlH~C~g9^zB+biy=>Vd-
+@9+tNZpLP%Ix7zqy#C!<D<B3iAM7z_rMc9SMeBGac&CwJX-7rFP|d&%O|v1IPtx#a%)?<ZMVS;S(ou=
+ZooqDACI+i3FUL!-zCi=#>5+&HrQfmrfnj+Q)Ui6I{sj39648_2T-v&qI)X0qq8@uceI2T15vO8W1hW
+bhtJM(?6z=mAPb9;Rg0AxdITP%{1$C6m6PBp1GEXDGSnM@r^YQ!@J;B?Scqq_nh@tX#R0{OxalBhNhZ
+3|YN;HCelMEqUdYS6KeO`|i8sqmMpfZP_QEd_s=ze3vZ!m6DI@DA~PxH`%{`KRI;h5IK7EC^>QB1UYr
+;6#43_ugG~&JdgiI$+zEr%gRMfO%1tl{wMO?WlHMm>d2)_msnZBpUWWV9};1FfMZP<BY8o&@rKIY4l1
+yQwkM<LU~&(gL>ABo$tt>%d_><PC+JbRJtck+z9)nahw!5yyb;3R1>xsH_&n-MmO=Q{5dIwqUjgBdLH
+M&y;d?{)7zlqigttQY6%hV)2)_fuABFHIA^g`6{yPX?1L4m@_<E=CgL;DwuZ6OH4~*@sguDx5!6Ptks
+@n;b@R%>5CkGSy)g(gCK1k?KD+#T8kI;Ij@O}`!Cxj1&@OMJ^1PBkb>R{YY7eM%B5Pl7W|2u@=4&jeM
+`0t&<gYuCGRoRShNO(ecF9`30XHg)$rahr|Likw_{vineHweE0!k=&oPuh`0(h2Hp4^mFTpxWL+jzGz
+;BL+f!?<RDxnb1j(5c=S1LRY>|=zIGJJ^GDPcnyTV1Hv02{A>uH2jN#h_;nEe0|>tr!taIf5a-I%5dM
+1zU*`zlA1I6m3K>A*QK0Y&P}l_&z6A<(zLfknn34;VD5-ytl1nQox%?g_e;lQb@LeGM00=)C!cT_qb0
+GXY2wwu>pMmhNLii6M{0<0z1j3(&@U>3il_CZFA$%7I-vh$;hVcC%{GAYfCWL<w!Y_yLuR{235dM@?_
+@=)k{&Tj9jUA&Mqt!9o*e^6BxM$Da;~cKqG4Te2P8T0LdbC!TG$6E3pOD~j<9f*ybOvySe+EEwx}>na
+=waMAjc|`oh>eRM4dmluwWG&qllr5FkdU4|gu5XjMjNk<)x|=1<ZEF6u&_Im8kvkwh?jq3y99Jf8aR+
+s2=3WK(`y`t2m0~xhSBj!T{{E@fID~y6&`vgLHN;H3>+Ju)TKj*K<>_X4DP7`CJheyN&bEvI*cF1JcK
+b1Jv10TR)?-ybocAfb-ZBB*bifX@JXY!2@~Su<KmM#BZ0txPMtdX`1k}FF$Q!`U_YSWxrv7)Oa^q1jY
+}}Z$0x)kbZ_DT-23!n^v5LV;^Pu@31ho;_7@(|y>I9MBjNO8Srl@rC5=Q6BK;Ws!O;nj#0hcn2}}qUg
+Pi`rf0Ax+46+apG&+wR+o3}T@ZfMyx}!}}%%C`kU;?;z8#|V<5ae)=Rk^F8+J(o(>k=T9$988zbR8>-
+$LJAS?GRU$d-&+2gro#RoZ!-NQ<EV!a!l+XH-*;(;ecsn&}kj+Ny7)JJ=-W0zIxygSxkr@4F##uT{~=
+Oc!Z~`vb_NuW5+;Qj*N_rZFEmc8l#;U-Oj`P4nSj*kh($F7=KbMdYKsA*2N<R-D5{Xag8U9?wCGMz+$
+|8+f7W0O&A>wWsMML`uwL?`wmagLOL3poRPkcIfMPMh}dXDQ)ga&fW8h2q9JbRh-S`C^dV^s+7Zp1Nq
+o~^LNjMdE0#2<@<&&LB2n?L(dU-5@U^JR<&)9P^tqesDY8B{dGchYk1RHgbLbwg<=sh2GUCW8ODy?tX
+&ia?*~MhoKF}$kozs6#$@DW^XUGHi^wUq1=bn3xtXZ>$y!hgaWZk-TOjr2egAbTKxMj;0rYn3}@fvv!
+^o@5xSJ<^{7dd?RFw+grp81BHIdg`bJ$sha*4C19=gyJeem&20gNqk0l55wlk(HMyc@NA-|8Qz&2lar
+CZWwfQP%q2pKu5O#I=aW8qkEP3(htdCx|K|#`^bazI9W+gllSQN(0|uCg@+C?5k~Z8=xodTL-;!&d<=
+w-hw%46_y-~UqY!=#gnu8x?}zY>{oG%A%3pfQ|5ZIjzURAh=gvJ42C|F5I|qae8Z@XMVw%pK13LHU*I
+nJNT{kFafjvV*Li&UZ@>Q$)wgq$N9=-bX0l&WL?t{Vxg^{41{Xz$I^%~?09@@2S+byU^Na!GMuh4G7L
+Zb=oH%O`U3576y+qY}mIiOR&LEcKGm#4eCPhXX)vs;_a{knQ9z0hKiy1T!J!lUOP{B`woNBSKDTsnZs
+yO)=zXBgD49wA*^gTNJnbOkfS9~R`-r&rjZL1B2a7R28#EG#%ItVbA=F9?3Pr>{p3W+Y2gxGqKb0RUI
+h`sNFTlOObt8~eNS`5`>tp3J;xZraY?#!o(i(8lR=7qR{)e*esbH}Z>H{{6dm?~aLzA6^;P+tbq%S{C
+-)C|(DTSI9$4!Hx~cDgLkexeB^Ix9K0#6}v|){+btFcwq+gJ0G7vfBuJGe);7`=zGrm{PWM>1N`Q~g$
+w6lTzdBO>C^A+-MiPWP$<GijvP5i$P^&x;1LQ;hd+~knAg}bx9{D%cQ1gLR_$&xY`{JD+%x(P_yOMK^
+Uptj493xP+qP}=#~**B(4W%JKKl$OsOUG}d_!UUP0<}>pwQ2LB}q~Z$olQEW5=G~zJ2?WF=NKy&IZgU
+JTlUW@FN|_3+Ahr_z;(mu;5XVuIM)_D=VU*q2UU_X7Kj;4}|nS0(CGFX$%`StT*xpd@=qH95_HXZ{AF
+SKe}ntCdT*o-+xbk_~8dyRaHfg9zDwZVHnU6(EcohdEveH-dnVB<Hkjxvc(k^7EUyq&AJsURwRO6jlA
+J;matBpIz^P0mfk&N$Pi4s0G&=3igf=h|G)qKI|W^4Wv^bnhC&*y`01ygXhlVZBb|>NIYPhq;tR$n<P
+YN=@#^Yo`sJ5jGBe5~f1vX)2xacVufP7<0OjrvpebFsa)ti(+i$;r`st^ezx(dHZy>JA=nv$40_xjP;
+9&)Z{j>aoK80~PKIC8iPzL$KJdr=-UzS70<*&Ic@&`OSKFD*&=WLJPKg0jdojaF9KK6t%QG_z=*s+80
+kLBaksZ%TuzWw%FW=0;dPCy-I@c8lL?8CZ&`GFAg4eJhuIe73O;~(=7>OaMNxPD#UWyXJC{sZi<qC3>
+#A%BMd`uchb-4HAPQBhI7u^i)v{2w}Wh<^O>#}x9Mg3iq9Fs47|$Nv5M*@wLE+qaL^m6In=G8|>VHUP
+^T))UMl<ZZ(*l$uUaI{GlBk^3nfw42hf?UW9zpmg#9O5gaN(u?)=$DytS-I8-B{<m)3x-vL8cqr)bE1
+<o^{9rO5znJEjt|$-Y1@ewK=Ka@Sf6YF`G0ic(kzXtaC=2q9>5qB*(m6_XpHUifjMC^Mln$$;G-4mfu
+#3`uAVcqeP}*|~r7KQTdiCnn)qjS6XzLvx@-KgKUdZ1+Q%|rSV0oGGHKpTDQX2ayq(904d2q);N{4_9
+*`HHdf0fd-qm+hkbu3NX7XOznUBa@wJR%~ZCzORElmYVL23e37EJw&=OLAbkW13?gVY$KbkL@mu*%(6
+;f&cMeav8>)p!81Qbp*&T6v~bHB<DZlIHkd#i2awh$v^ZNRBr#V{6l|4@oSzZayiFz#k|0Bg=IsQ1M4
+`JGc5O5FJy@I0)JD!rE~(w5D(>l?B`sDQB{-<Kg4AShq};z2c@Cg{yhKC7OxBm2^k9cumbBDwk<8mfn
+~k5@`dS-?$|G2IBaK}f78!G9Yq<wqSOE~#DNU&{!DM@_bYtQw#NVE%a<wiGfh5}LH=YpTFR4_<oM%{K
+j_iTYv?Nr<LR>xj;2rN#4slA{(;hI-%&cLn$iT6;R{ipq0Eew>+^nIpRrBqy_MJJ0iW<X4J~d<@9HxC
+A)l8=Mn?98_PhxB$8yqK4lMUruA9pt%YtPc%h5NLAJW(6kEAc#M$zYU?_@II@7@|p?-DX3enY7dc+!9
+2RG(1>QJ)85n*?oY2((E(K!)y{c-soUf|mI2*|Vpk{6im){rnC2f%O;r1353SKA>5a0r`h^k5>FGmA<
+)PBz<-MDEbn}um)tnUkbFJvp|NKz{k`xTn4#5qYQF=#x`m2UP$+yl=ej#ApJF)q5kh<WDd8)KeYF({6
+k$^f%*{EIav-_7L)<|gVxFx@{jH0M+I8?&co644Upj#kl{ru=Rfy6rOCA*gGA|!?|FU3HYxtg#`=sh$
+n_azkn1zHNueJzGMAe3kM#`u7tpgDAIczq&E;sRj>xj$Z{w2T^!<e+=sOQZ)7SGrhIymt#k!y9=U2E4
+Ng%^LKY|QS^*LVDXYFy`CPf|M{j=OA^#K`recWQZ#P|pOe)-U$LwkabQiS|tyND3W49b8yk1Pw?VP7l
+jW$1?eE$R&T+mb(=Zdg2=z6UbA4Kl0;89sR?hh8{eb6br~4ak`Vc@ibpXQ5;C+1fJy*j~uxf5eCpy)i
+H359=%Pjl3c6vMkv4V|$O|8f-VQufo0!e_NME(aj*kM~g?$cNa#}w?KyG_4z4ilU8PF>GB80(2|T;S~
+xeBE=ko<yX|iJ_2*Sg24@{(pwKZ|;~(04R{nvX705U8g%Ei{zOg<a-*TQ{8}F<iWB-dXV)@@$awq)<$
+gl}y_;3-(0CfTDGs^Iikl`62LpjQD{}}pcI>?Zwql;7Hm<;&CHfe;Djxlt1%jF;24D7>DkHByJ`t|h9
+H{YbF7s;P<p4_aST)TFSRz06Ww-*khpA>)$P!~P`8U8NnGqy=D+U}%lEO*jX=F#-Y>=^o3rk2`4hDSh
+#r69v1kO6<qb&UItx7_|9|4<fK`G;~{gt9#H$RqTvx87p%+>RWwEErZg`T^ZhG>le&3?G9GtUiMb*e1
+#Kc`dX_t1YAH-^?-e36Nn0$gm7#D28%WVA3&0u}#uC=@=i>w%q<B|8n_{j*jk)JRpB1B_$3Sn#*xBS*
+~5XO26IyEZx04mToJIq92R;{Jv9tUMFODPRQ^$l%o|7XlZE%mjQoF$AJFX2;Z6?S^s;G-1rN`Jcs^w<
+)lfI;<K}}4<N5tAF&QBUc8ve;9S0Ll_v<XPBkB6we<UZ*E<d!1M72{sL#!QH`RZTf6RNmUXS@eUw{2|
+rhB0-Q(9U|=gphvkl|)>+$>M<hi%e_i*KVogA6USGp+psF`dfG%eO<@N&EEaLz9w{=qs<hLa|@Y&CR7
+&tCcQazMR$TufF<<wL7Q_wImDjjeP^QRj3Q0K8oW3{9%1Q@~Vwqy#nKnm*+X^v#eu0BkFTG)P-B||H&
+txs6f}5D)T>Jz<@i_($cb1Q&Vf99cB6O(n~MVi4!N%DO0A<va&Mz^wUq%C!c(h@q^=g<QHYYz5x3PSq
+>b-pbXfaVmZXV5aZZo*U@9IA^-LCz`A*jI>tP%V_<#8HtE^C1Z4k~<FAn;N0ufeBotuzfwI05#{EM-`
+sky#pbk@Tqj`CG6zT*;^Z4=OnU1h~_ik1mkay%2^GGgNa(<wESoUz7gL)n21=a_w)2u#2J~-Fs*PGSn
+%8f6e9{Akt6rSP3hd=quGtaPj5R6;YWtyL#Pp40xPJ8w0Mf>*cOF_40ydWP~H=I9|LH?S{f*+PCnKrh
+qht|xdpA^SH|IBp^?4RFU6iYWgF_(UMa09Ksc=0&o_ifKVtXj2-78Mo!zH;SCx^(H%i+A6BH_gn<Wa$
+p$N>-Lo2CNq-C-R1JG?xYEAg~|Ca*us9))kzOz;Ptnp^k)khqAQr1Mx3^jcN2}=YKFT)ZMjav-z8cAA
+a~c@;PJ14EAB2mSyPIuOFR1e?H3x>@(!K9a#?K7x|axd~s}m5bFch5v&i*>pS+@&SMW8Ka)Q>|Auu7%
+RiK*|F6As500w3<M=|PVLH>1f9zyB?PMq8AIva7tR0%Mc_AnXWtdclG!&>TaV3E~AR!QF2;tENgaNc#
+-sbKuVq$mqh7^HndD#@H?buY1Nv&;DYNrv4prFmD!|CsH_MYWt&5M{$|MAS6oZQ`e@A;kI`JLbEd+y!
+9`t|ECuuf;roT=*S>hwG?2I#;4{`*x?QIY<fIbaN?G6oa(5{uzqBlplriwrR~w`}24alYu1C-1)d?kP
+u(9N9q(F&z4lO)9cPzM0RUi=1;!O^sf^)22;Rxw*M&(xgdx9{8SX635Y(Y?-xTTI2|QfLx)EnLCr;5S
+oD7hKgTk`eoePrc9ZVCOq84dOmUDgdPL@A#W(_Lyy7d^Ql?0W@*_vc<`X6A6~<A{)cYX0s4o}@lBB*d
+~MbNYk)PuebK=}`%Sserj(SFT$%S^dwYAgCx-bX50!rUb^AYtuT<hi=#jZq;#;t*Sg!|}Zzb}kWdIum
+kIgkEpNJlXr|AE-wl)=wMzsyJZChUluZ2(DqT3dTjjR*eUzX1rJu%#4sQ8Wc-|&CX|FRypbIf|9g%;+
+UH7jzeeTnk&a;;lst!w*+u0g(y9I_5rm&^%r!W^KJ-Tn~&b6@6&`M{pbczMGGdUMPWX^+$ImQ}Y5yJZ
+{N7#A|cT4-!+RHC!Aopu*!GnH%5Gw31;@<U8Sg?7f()YL?d(HcE^^zG6fr{DPh!tYIt!RW=l#sR<4L1
+MdgoPbPWYx~IQYx<cR<3oV!Aos`;JZBB<W;6fo^Su}SZuv(?B-+@-vCxuZpFe-T+O=z!mTx!TP31gf3
+0dbD8vyMF9}&Oyz~`V{)9;Rd#rAARF0nz3!{qzXALu&lk$W8Q_UO^0YVY2?TAs}srZR4mo5yeB7@0Bn
+f#}TVl9G}>5AAXK-FDIK!?=B$f#!s2S(d(@adAFlgI;6{-N{%C?c$#v8m7E|hJI*~^`+%s?Dr-Ue+@J
+ilk+tA9dpBH%!9$?m@{+{SOORnV}W+*V>o(dK>FSJ9}`OrG$;7?_&*$j%TbXn+OucRYP|~&Sg*)C^s^
+2`9uJNn?V5gf{!{csJpaQOu$}lX=s)BDdxY(T$HbiQo_R3&UFLyfbUwNkJti@Q#x-QjBTc)eAK4v9&i
+j*hw=Uv)led7+j1gHEIl3u6LbuEncJ*r`Ogn|wXMXj6T<L>-4<qtAUezGR<<zQGpL&wj&74)Mx;&noY
+q3`T8{sy|X-7G&XN1~O3HNvP+u*dPM}q!!POD<=D5p(w+GMA_(`i$kHpgk#IjxGdqiB0Sx8wB;qp-gx
+*{NeZ1Ne&M`=tI>>PHFjBF7*qiTS|(3Vo%B@0%_*f2QB>|DO2!(?#dA4V!Ie_}q7STyo02|I(w>%1cV
++U5sID<buHzzWHmV=D()Y#Sb(s1JS@(zdmNnm~TsLw_MhA5Bf}e&JRW3z9V|+-=de^6#qOZzU>n6fq9
+1E9;s>KZ(Lpcb_^#Y7Yfez9VeH2;kr_PcwecUcHRGb1N8q5(cvNKv&W66o4qC`fA0AFH>F+|oqJyN=8
+N1I%oMy4oC|z~oH)7IH*P4^@xhR=-c<DTh7B8hPd)WiAvEAS5kFub(d*bEbTz&#ehT05KV@F`ExZtn2
+fUM<Huw%Xc`&xY`X>gE7_>s-g(W7wC(fl`{AhS$Vjlb^cL6W^!M|d7A6VIuYkj3IdX=jQ@}T>7?|n$z
+^8in<UswM5rh0B;q0VD&cXEJ>t|;}$<rr@I@TC~;YxWPg<q>=coH0|{H+sutDmF#pSD*N~h3<VA%s)O
+bv|xW;+EcD`VB~|z$(#PEKfD~nJ;B?wJa)#gHZUIK5sV6~6P$4F@AReTO#j$%iT5kku3fvtjdvS=mKN
+VST0c$a63OS1M|AfO_6mOd{RDZ;bub#RJ}@Taal(n1elY#x|G2TzMEo6ED6I}Y5n36`$KJSlS*?G+Hx
+0;RVS+s7IGE|Zjyxvdbh>|ulYD#j>?w59LEp$ZEd{)doGSPNIpL$bmJNy-UjA6ADxoFok1?!`H38n~?
+jOHm_3G6X>(;GX;>KQ~fxHR4ppah&Yl=8{A2<{FP~;Je32bAdgD=$;$Lh~ElxiBZJa*i|z7zXT#DWts
+VCMf!V~)nVz>3Iek{dVjNM0O#1#CiNTu%RMAAL~wHNz#3S-<Z+|M+BEwruh3-@m_*wMe{=f6lm}1s>4
+89xBziA$S#e)t@`L?Is6bU`>FJfE70Qr^k6faqDQXTetkPw$`j!Qz80)iNObm3yAID3AzVf-01$3diA
+FT8Ydt>-Rk7m54zR_IMY@KCtK&xK+*Cj{mz&A)_;1he-odv4)OWjIEx#*gHMBDM(1C)HO2x~w@GNIcd
+$lR`HcRf|E*iM`kr~_nL@B3{Bh_oakbeu2p#YOeZ+SZ@Z0E`TWg{(d34EIpZXskyRNRTqOq~D96s#Wu
+|w-U^e(m!{f{ib4`h+P-I&vRmoBI)moCbhNRY>}sd21qp#HHb<T=Rc!einqS&Q*pdLkBP=7zcW%@YOc
++S?b@)qh;PC66m^$>YBIxpd#B{g?ZXHvJbC7XCdaCr9U9!KO<~OV#6#Kd$3DXfnFM=t|}X-dyi~Q@s{
+gs(!g~s@@A=<T1GVLDly3GWG7;Zz<J}|C61aeQ$1V?rO$SUteFjXwjlq($mw`(xppv9(eoq?K*d1Vjp
+-;3;)gDM);9vlPTO&&U;;CYe4^R;>3w_WZmmH5L*u}1GXf7mYO$jo*s99x#q;VF?QVSt(4od?|I+{{D
+uGYE&j)4utf64`T6-e{|XJumoL|RM;{oB#K?fjAscxRJMn2*S=nNd<!bSfc(9w8f3TP;6<!JNcb1fto
+CV_{M?YuI9K~FKuZdlb;|1{D$f4OIVp_3P9}7Q!B=>5P`230|_Q=LyCDr8kxd*--{3e%+43bL+*I<3P
+aRlZ7y})(Q0x!_p=uGH5b?Vd(&(JUmz5cgt+vW=d0)-~#W}cy8@#4j5!h{KG_Uzf3Hk0==_z>qIH%4E
+uFR`tyjkkf_HH5x(|MZFe*|>4z67EM%9-EKO0b5wGV1dS%Sr?p3fd?kXi@yw}g5NG<*nb;+$NNvrVYz
+b>*rTedDy<vXt7`I!%on_7-00-<=g%Ly&AxU2^dq`P>;Jy!Fh20%!-v&@0|#`j7~M$D$zY1Z>|6IwKg
+RwdON`g};H*7^OF{!O2F5}D>DaMjW}o12`;L!)C^=B{47!hbZf$Kv{;miwws>Osli?PgH5oU|%K92iz
+x7)_O>M~O8rAFBTk2rX?7E-bjbG=9)x`I&+{Sae-hSy6dzjkacOs9|nwpx91cSkkL`S{%?6c24dFarg
+UV0}S4qugfR6hUw^ZIi+=Z;V)ly6zq$w(xk=hN6Q=2_PCMfdgKX=l!yA<z1W+@nk6?*{9T*cf?@N(}P
+wpJV%=a>T#hcS+ysn7N*Fk!gH5<Ik8ig)V#Komk9-fBL$(HQ#!rmw)WR2GbX7&BR1*U(k0Y76Tvq;4^
+0UL#ap6O{^nmHP3f+{OX{3qB7<i7CW&M@gwmou>!G5y2MI*gf6gFVsdCl{^_@+K41Gd*ChtqcO!NWVs
+T<GVn5<O;?4|-JMm%gsh}IZMIna4U;M?>%k@3n<=szWIUVu{#BA`7dBO&nJ*Y4J<N@t(;qT$=;cMdS5
+^sd}Z_)Q4=ExH|n7_Gm=W3iA9R?q|+5_rnLza#w@!j#;i5rNW*7|4YJ|6y?Qq97bef#!le~>*Q;A+ez
+@{;I_cbsX}e*fCyxX;aCUx0sN7qs32lVksYv89y5_1Wrj*LlrPVyGXN>i%z-`A7E9^~fo6`OrfTX`Z5
+|najVv98_%^X6XH&2Wr2oUVZU7u7AN?fAZu>?W>vnIOuJBF6IM$>ZXPNvE^?0=X^8&W=|6L!cNjZ_J}
+xvd!v`d_OEkakDi8Z?gKBtiWn0%n|u=25@%z(+uPfFT}!D?4o@8?)sy5@&j8h&lkC(x`FxZo+2i$&({
+Fp?jccR$g$#Yf`_@Lwcd<7I@V2!i55JPZ5${^N!;_vK(@wl;ZH#lKSHEZNOU{{I{g$<4Po}TB+F!n?Y
+_+$xysV-+E3Kxg;=bzQ5`Vd`dSZELaaCn?<zp)*7FU+v=c_KCwDO)bZ@I6c^f7<6`Hju2w0kC{r)7WT
+%U*BhimIAw{cD^<yxYC8>*UI6e{oG!>5A3Pv3yeHUtS}<_#Z8(DqUGx=3nfuj(_R?`hi-xlwa7K<zMM
+9^Oo^9E6rD(U$L@sslO`CTT?o<n4e+HN_)&#R_#yA&YaxO#kV@2+<)(xlY7ycIXOOPInJDH_?cZWwP5
+P>>G`u~6csuDd~rN-$NXkDNo_0G9Bd694|W7QgWk~iP)WEpyg9r(+#Eh0J{Rr`cZYkzV<Rb%@sW&3UZ
+f~e5~+=Bj_i&!M~+84Ugt%P);Q~0b5FCUWo(PLWqeC|OGZm>OI}MsOHs?hmXa1L(i%M;Jsmw4?TB_py
+Q1CEYtf#l#~y2svr}xZJ>E{YGwfVD&n~cw?1grTz09t)8|=;YPJ6d)+0AyVecV26pR+scPP@zQwy)Ve
+wnx8M($f8>SYB(qm2PENxmKQ4U=>*ltrBaQRckd^o2{MJZZZ4KR;zW~I&Gb^I;>8s%j&kSSv{5~Fg7q
+QkP`3)#s|^^8G+nDUZ5aQ6j&H233LW}0x7|aU{P?l=F#ckIpI@RuqWsVjSY<prG(&EdMG258_Ek6g%*
+ZNLd!z6p@z`r(9TeQ-pd!|i}L?3{{>J>0|XQR000O8^OY7yf29?aEI9xGKFI(88vp<RaA|NaUv_0~WN
+&gWWNCABY-wUIb#!TLE^v9}eQkT(Hj?0X{R+lfFWGX7lAX!!tu}3sqbOtT*p^n3GqcyyFjSMR=7>#p2
+)17GvcLW61pomMl;q6p?%i|!Byu+i6bgkxp{h_QY;J4@`}OL+DXtc65bckGSGzA?ZNs161!s#qIKFQe
+brqb}^Y%7v^5DCAU1e!o)Kv^MPxCx@f4G0}@$?{W@7f@(vS9z{<JrmKH=oXqPEPS5l-nqltGa1}S)Ju
+Gu`zGzC44W-e1?ETAYW(skL$cETebOJTNao4iMZjaS7m*v>J;=N6@h58gnp>vMXkR5Rn(RGUe;GvMRl
+cq)<S(--6ykpxhhJHL8HHk#kws@{i~i`=dJ!Fx)<xqRa4J$p@E8f{k2V-xdFV*mn-_+HurlQ0esYpMU
+$r)qL|;!@>Lrg(({9+sT-?cwq7pp6I~o>(A8DIie<jRM2l6uZwrA`Js{{ne)$?ZpSG${B-#exB<=kw7
+vlg&g1(O>Eyh8U-X=x0TDRlil*%3+jS0QpXO_+ud4jDZqFy&M0J3h%dX-n>V9~ZKKxQ1=Ez4##qpt#<
+wA7d%-n+B2<9EPmW!{YG$Eo~<B={y3x%q4W1{T||t9fy?Zql{}KsRYw00(7x1wDdSZCX^=z>G?*(nbs
+d`*iZ2hG!fs({{E<7PV-rbP4_3&zrWG7qhg@Wr_b?tiFR`A8l+PCj!nB#gg$=-oB@2QIcTwB!N<!Fw0
+J0UKCBf1k?gV1V<aEQZTQZ080ihf||xMEfHTBJ3zh_=3^MZCKUuOkFVk&oY!^mdJ?>hUyXyWgIDoxFu
+hCx!OQfgi;&9U^l$R3{0>I23PheSgEY7-+F+3u%{`1$9o*)@<+>=d0G_T_LA{0<H4nrxEpa$jMGlhzA
+l)vC*&;yku}rJ<Do-!V9L2)M{?UgIZ-E3&crLca@PEjUl9K~`n#XcVM)VZOaro`Q$1|!Bo!|a4jkhmG
+FT#z3KcAhvP0V*&zwF_k^V=7ry<b8g1iyR{grjid`-79y<AZ%Ds-8moTW`XRjgy1-N5}9yjHX||o_z7
+27n9L1@C^u}DKak_fwBP@I4DF%;O(H82kE6C{<$s6GPulP(qHElvSs+66C~<4Z%+;q5Rj*U@8QR@Q)n
+s*BgBteModO;fDZIb;fYw&111;z#9v-sG{E?vjJ84`;<tw<2m5D0<L{`eI5W@1i|Ebq*BP+c{B^uF8p
+$3X6zu{=68->V!1$TmRS!HbD&$h2!y*?r{2NY(f!Glu2kp&XzD5*#WC2`h)C^2}{pBv6kA5LQuLc1@p
+%M0)ndJT9De{QPF_NB|y3RYG8P`2!Mx9FB^E+zN0INsu92n;0;NL$1_8*uhVU&@)6;v^p(HJG!!V8`X
+qc=z)&mcQ}8@>4l=$g?~Z{is5q%`>?@+@fUK;W!~6{B8XEodN(F@%D|NqSBZ`t|Cy!>W=LH`QyG(?Sz
+~2P^#!u*&i|$+bLbI!PCa(cT7iA76uPOgj9c<!Db+CIisR_zL>5%8FSVjr5x)Z`Vx~WYe%-g%@M|a#M
+vD47JI{x@@8D$GXbt$r66pq>M?P117xg&V%Jsnk9)m!PYWtO+7~R`SI*!c!9GAzrHd`Km%3XQbnAH;l
+-Xc{IF=6=;9|YMQ#_`46yDDCDc5JNjS@`*AfW}NC&%XImY#WWpqm7hV}QJ1d&GzStqofH^M`kem|f+g
+fQK`pmsr~gkQh@ViNB4#GXU|B`k0r{;~nJIj!(CxF$!&^P<Y|ZG?43zD`YhY)4D(646m`FZ#U;pNk-R
+E=FPSJcw8k2;sl@uXRyHQ+)ee;5&d!@bF)Nhz(tgdL##0<##SG@N)h*JLqz1S&d1%iX>2TuE^=2RUg8
+9!sr{KVS?Ys67Yoo8PJOqWId-RjX<>lb-;-rEpP(=kK}x$$;x<auuwqq!vm@yk}Makh`QfRUBgn_tjk
+<Taq#Tfv+rO~11wooDH|6^VO=Reom)UTTeQG(o`Ckdh%P}d90&N-kT9Dt4h`ru#ZLeLO&rD*z6T}ElU
+1H=?`(@4gcxXg8P(Ud5hTzR|B0J?Ri?8%szr=tVyFU)u@iRVfRCXxy?wlNDnKi3xwHTaqZPM>>_txuV
+xei7SNwMGB4*qp0;tGg&}|a@{3|_00y161dOk1i##9bDmT2G6X$7LAxPyucVPbf9z-;6YIA~1?Q3<CN
+ns5<A8ri&z#mL%o8c@|0e)d#W4G>{<0}9rtFHaP{39<^bW$}mG83lRN0|T>{$s}lw?jak&EU0gCT5Mr
+9H1}8tH0N8Gp`c~u-dSXhNxZ68k%ixwfcIPPOM<R@+!eQH9TkWBr0P@0CWP8Blg5p~Uyt={YW3=Zv<2
+DD3xtO1hQ!pA7A6QOGm0v?$*Y@4jx11kk&3i!n+S|?zzi&8fJ6KhT66rSXxeoO6XYg90HQA%&zd!lkx
+kvr;P~J;_}%VTL-nBHSk8?vi0TG9NOFbF*eLn1P|WZ0*}6qD3j4{1Ns=7@$M**xzfa!3{q%AF-N8wc1
+X&6S0J31ZVwJqS2U!LdXN9I`mFHQW!GNQUHe00dP@rar{3+V?Wl*N;YKHp%GACUzC67kjwH~D9ZF(<)
+Rn1zXRU1~dqtoEe@RY}aI#;GG%t%nRV42L)=C-Ip5~)$>!9V1>kA7fU1N#B-i>9uKAnp(t@2B3DF1Qk
+pY`?zUZ7yIBj`2iI1za^|Fusvpdn5;vd_{Q%!Va`zlqF?Z-{x5YBZ2E=jt+x_UrunAquiaR&=&oYyt~
+51g38cOu(V-RkWoASL{YB2HPOmgYwWsmm{8~7WllC{%MnuF_=Q=^)e4bjpuEM{r6#fk0P1!)W>$y$(&
+`eX>a|<Zq)NZainOoU9w=Dn6)RcM%qHbncG;R>GdU!r&dl26@;<rF?<1HS@Q>(3Ng)bQgm6+t_yQ)Z;
+U+Vr3BCkW@E@sgspcd<8A~e^LSkUqJ(HsHd1m*LAxz=r1?kcF#V0DTH|PumQGpIUfeyVqU3OhTp1jE$
+0iqK)V_u`C{dy97X{kNHo+J@2VWyK4FkmnpZ_!V{gk8Jn!@^c!$isCz-~M0WNHx5{AkSdcYf%EF8Tv*
+3?eys5IG6(`pi}EIon52YDD$gyc8}Bn1B^A3v`vwv2v&?uNQ!C%loK+-qWn&~Rt*T~3y!gM3omPIuq3
+L-ZC)XDM0ha`SL6ndB{A`d{%WZ*WDkyMbuV&qg)rC{%!Q?h9Cd?+e;nLkIa$SDka{XDi;1)zBCP3I`A
+M;u$Yla?Oz2T0`emj1D0LA*Q@>}(DWHJd^ctCtio4pSAqA!1T05Bc9Bu&>A-|4x$H6XRG+Pn>bZ*v|J
++sI3BhtpVTE}NAUdA)h70g9^wu<$0YkchmTNazItTGSGO{gH`UWGX__FPnBt^p_@Y(m|n<vN$Zc#NUm
+s4rWsAvUVoqxaNNe$ohU((5=YNUIZhIq$WFgOAAA<L^^XZX4*GmOAp^aq!<JeyIHc@$)23vnC~@-=CB
+q$d0}}0;MSjiAANc8tozQa0!#!(kx3P14Y-i?ITB~3T|Sq=T3cG^JsY3xY726S3Yn_)*$UM^x6ZG-}W
+T8nYG7OtG)G`U(U<)%Cm4QHl98g7X(<C4iKa2$c9VPtVOxTWtjVg8oQW=RuAaL#_zj5!dQOg{e^}&5x
+3Z~qEQ5G(PO8!-QwkShTkL4N^xK0Wd_BW8^F9MOXDr?f5pub(8!}U8$F80Y=Ky05{=ny#s)0#Dtz}L0
+54S?8!10Vx;TJ)0$F(bGQq9E8uUTn%eu<M<mFciL6~q8SqhPq%AP9k!B>_Qy|NPalX@`-1n|LL%}S7E
+yqB1vDz^f;b|2N3e}#D^F~{B|AWe!kNg@<upjm(cV@xP~7~?bOA^y|NXL1w3>Vi`dZ4uspL#|DdWu2`
+{{VS_y@YRw91X)^j)GU4-YfBGXQqM?rA8i=;q6k;$LU+c&tQ4k(FJ~OQ^3Q!Kge|eni_N6#FA}O72Bu
+n^Bob?ApIX+%V?}`8;<E__;VrtnlVsYjfD6PUtxJTd6t5Y;C9oE>?1;cW2&?nyop6Ovy$MlsOxe#)Mk
+#H#-PR2$k7$3QOW-DFXWzYmi5@e>(N1PZkl<}*tbpzXG<U!{VhyydVNB5MO|Md5hc#`u(hkYGK_i+6k
+KF7v2yf~4tD86oK9Wfd3)U=M3q}CZBu$VEW9Z5%5VNKL4Jnl_A8AC3^$Jje+EDgW1)^TG_vqn4+!iVN
+jc6s5#NtgHgjv?JwB)IauPR(-pdfH`jvKVN%>y>g8mC);NI{*Y!pERc5S*likPFxMI5@OrjTEZtY8x@
+yF2o|>;sSZn7?GSe3fT!6)D5d$F!=-YNWy3dn7@ly4XIKCKz0H7$Q!T|?zt?Yz^l@zy{Ko87FNGev*^
+Lco*JEuo)K4sxDW!pupQJnE=66g+x!`<VVWy@;J07?Cip-bCJj`y8(_O0ehTRb#>di()Xp?E^ilM$8(
+>A=bcHI0Hx_@!>~f5B!I4W#(IccT>+@;Q623(hJE@Ej4M!{94|!B1XU%=mtgFd)X(@77Hp`c2h4DB0H
+CSZpCeKK=m|Jf2lAD29SvR<>cI}(bv83B5zXG6ylCxOg1W8cy0CUjaE>KD&Gf+xe12f$^15Z*7M3TYT
+V?dR-!^JTRdWfJ#8L^L6H&o^5X<^_h&IV>9h%%!Yn0jY92uo-Vr$4_Z!vJfeG?j9S07$8cpA)&|(Kcw
+sz|`jwF!AreY+!#8fLWr@fj+RS5f;v3juH*wYEU7!3acqdmjO_!Opw*NsKVBR$s7j@>tEI|i>R($wGi
+ADTqZ#K!Pf5UHF`cZEb6anKV|_K9K#xrVnhzIHOoK~OT>s~4>f5Z_OWi|deknkZR!2Tq9CyEK?u(<*e
+Ks)sT(MkQA>l8T|DHdIq|~AvS0P&8Fb}YY!0Z&L)e&_s!Yy^@ph~V7Eo>|SoDncJi_OV+f9&AJvHC+Q
+B8y7q_teO-P_Z2Q0%cOp~!-VobDbG1uIRf;KPq(iTYyjTECJo^{Z~8%<9#>bP>>G0rQh~Qb5_{FJ#qC
+uwi@VV4NhZWJDvdc~@xBfo1@bY8z!1G9N6uGgwv8xbcsXA;uEa*%c+=JBs;8B4Ln><HTY#lA{n&z&4+
+gT@1@f5B(mkO}T1Qcqy)YJNM|GMGEu{J6^B}-$Il{b*<6$?=^7)XbR#M1ZWORtb`_^tk-uml|~zqJ5(
+D)V3T_aL8JItIG`%i<X7u5ZD=5ekilRNapl<^bXRtS{~`!wjN5I!E>ZmpV9{-)uy(e;qydRsNazoapR
+pd8%ZDy!Gc=pUnx*-t!PI#*t1&dxH7%rv$fhfCrWc>Topq(p#^PWH+t1)(@<njZhFv27R(pyt?ljU=3
+2!f*;qiM)ObF7~<N^u}8mlihXv=6GU<c5iB8wibn;Jho<GF`sJS%IFyPC>Vn2G&&m(W6ENl6^)G5N+_
+_2~hYAQj~^Mw1j`A}lvY!-Lk|yx-Tk^apkR{s;Q$=t|s4ifZ<Du^Y<&3p*PVZh53p{M_DyhKfE7)$Pj
+y9kk0-T#s2ZaU2gK=7=_sqN`dIcOhz0k@@(q=}pQuG(1h5(vDolLjx^`Ky=&4hysATrHE^}hl1e1rZ2
+M*n4k^_;Qr2h4R9qUJu|jv380Db09?wunhg(*`j{f@8SRmyq(SOor1Nv}3~2T_;TKeMLgXVgWzHH3KK
++HrFbe*W#^N_&Ln5_CoW}IjA9z6yLwFtOQdCS>fdz@gv`m+m8LZs^b1y(^UGmOu{nb}rjf36#pW#D9(
+EMf05wEQy!%tjg(T+KhCbo+Hv&=tN5s-?4(+u~it7aHYW|0h#H?UzlyI;(m{h9sRUg}V@XEiO9rhU(Y
+{;+J2!?e7{ro8nBtwoKjX_xv8#>;z<fhr=`lz<r1zAS#q*+d_+QmcOO_JFHJWO7V<XFa5BqtTLJ2lRt
+)0G(|{$I)=5T<-D5qnZJ0;~)m86dTAUFt1D?#|U-OKZCO(y^_84@0AYD{efsbGvMce#&rCi3XZcQPrL
+I=I?CGU>R3Y>lSMV6I-63RrTB=)77>U3Dgui_vfQc9UZ-r()$#bmwM-2?2|OkMnIQwCDfuhQFV|O5c!
+Efg!KPiexQ7#f!LHfN8^`zfR3)-m8$*M@9-OzbjEYt4O=EuzJ!sa9m!7{qB7O$#+jav)rPk1-B3te=*
+q<tq&GpoQ;z;Q+E<S(b;M8K69um-J4~*rJdt_v6@12E;o;w8>pr}6Y#L*E7wx5z+!AJ)#FgTHhXPX5b
+il;L?#V7>`{QgsN^v6EY`T<%TWN56WmTv6grAo7GdrAP97KM5nhB3Fo5EMN@3@(e-_4u03n0+cCU9_N
+A$Qa0Rz)q&Q^4<b3j%6#lt}N^K@<>T<uvC8#`+PC(&th9BJ09*w`rp)LiThbD1s<I=T5&FcU!p#q{&m
+ygviX4M=w_m$H`B=#Q05opLP_}mUA|nk7_xdhR$)b}yji3x3hQ^OC&}ub^^XLP;vzJsM3ZC433|6>Fe
+{hB=-51M)Sej_7DXd@%!WXPac>;A-=f}Nja38eJM7VvT)kMqW8ak=d;`?MZyp6xQke=qs9s1=lK=x<H
+ATV;Zyk}j(e$VeBZTSh#h~@jEJ?TbV5nbaO2d%jIZum{Ij`ELL4A<3W%#0~OW+f^{m#)KSq&VRC58+`
+PhD4nr1L#!&h*HjG(3=Tyz<r5sKvywmoBc5Bzz(|NlZNSE}DT(-~FVgI@I(uu%fNfi3vmP^2On(4@HF
+&0JJezrt<*1hW(FRwvq=hTZhehaQFd$^2eI<*4_a{KpjwQ*<(9<JVGjdNF5g8NFT?IBG1TG_MQj4&}T
+@TASQY#L!c%Sw*8XY!MG0mm!;tEop2G2IK>{ga<L--v7Ld=*lgYk-e~ewBm3Wn6n<?`FOYG4d!*F7=C
+~+Dim}=G2TxOZKL&PVgBTX3`Hx+c32xQ*7X%}?nM#b!!S8L(cvX?U@}~<%#<))y1^xvp{XmB;hER3n?
+?-AWPEobw??-A$7E+ZY5xcJRmf0j(q+$W<&7_N|;U_SG4nCfp{73KTYZHcam=^o~nhvpV#}}g)epwp?
+=VEK34okzDh{_?>dNGQod*|^5e%pGWZlsSZQ)ga7`DlWb-b8rZzWWJK=-~3`%>Xb!8`rn^`-78jj!q9
+o)@c+o0F|9_Gdh$bxdy%FiW;$ir4|p>TIG~HMLt}5B&cO+<8JIUBuQ@qK|?>h=s$nb&S!X7arCAQvTv
+&RNQ3*O&jU}7?D_DNBf}I;)2-D~;l-1}a`dee#U&rqqore3uPazO_Cm`?qRLEyU7HTJn3h1skDVv&Ab
+v3kUJh-K{ava1FohrNj#^Glny6(0NJ?Mr^Vj^=yOH!aaoztYZB&=14;{GHh34w)x8Xpi{cK6haX7l@P
+f$}&ct{WMsv7!)Kk2HMbYzNZnQlg9U&&}dbpOjpQ9fdeVQ@q_Qni`N?2i<2aLDm4o#zL`8wi{ZaTw!K
+%tD7{#F!|7!!DSmK#901&`O-^#23PV$g8$-Yzp0%VJr~|<x$T9gIP2v89Z`S1%E24tiA=y6N<e*+JF2
+0?eX!qZ_nN$LIAI*x;PPejBQDYM&LokaJhLQ)=Q2Iq?q{*JKj!FX-Oyyl8o<;A~lmpo>x(b9Uq+@{+V
+mn8ciRTbX1)&CA2Wqun9%5sHKY6wX`imD)CI--3mZs(J1`Sa0IU&!I40jc+!J2{Qyz<>sqwVaGL{B`C
++F*YD;b2EQ#RhC77ucr9@kG99Rm1TJJaU^B;a-4*B7SUU=hB%)XXV9bT%6xvy5WysH+P#18G_j-JJ`v
+&50OH)$iH@E<5j6bxP5K@5GX(`N9NxF?`(JL+}~hMOGn$6C=txWTY)i2;I;oVOuu@#krVOct_(0JI4e
+B@1P5(+VeytgMVh)WY&msF)i$3ShXKah|rDn9(z|)wQ4<M$@gO44<B3ODZ5-#TJj!LwTDv6|im;eyjO
+LMy*}0b+niA;_b2bQljJ`c`qOUw|vs;9DM|lS*_O@eH591vS$RevMx#35Z<3Ef&c=7<(!Ek`B>;Qj>m
+zao9;CJ0di>$88~d=C8v~`T8OVJ20NCrqDM(zT&n7ZHq>NlnRb!1wOn2FUV^=<>~mCgfWgu-?3a}<pp
++p;SpeV)u;zRPDK9aG97@Zp8W>=)#3C3JDbv_&#+IN@t9y_fpql*fgAz$ugF)-SMDk3%mM`J<_I9Ym3
+&42`b?X^Nqt9sT_lF-ik{W?|>zmc#K}=YOTpZnBRi?Jj6-Cg=;O>hOhLLyUwsVQt3U8(}1VCTM0d<AT
+((z+6e*mWr(C^g_Q}6<cz6cb~>ZmU7G*MW2WE{~Y*Jg~7=d`(o0n<IhTyRnWvlX#n<{^T)fr;P>VD+f
+VTi{9w1jU?G)>p9D-g4ZqR+>>q{5=V1u4OoTq&CFQ(e*)Jlv4G3Ak9ll5COH+-nVZz(nQ4el=M|QRd#
+IR%(XfVS+_3<(yyeDHxxFR?l4(n#DY3#Wv&qXVz++vih=7#o~djeT`oi-k5&sz3W+`m_~JI8rC?zW0?
+y6?gaDtag%u5hZjI^&qZlra;ltckleWJDKiw{cbifw9gO*MQk57*N4bz%^eEZ=bdHeC(1QTZ+9(}YO1
+z|Kzw}0B*{+Hz9MTD8|@Mm(NA8vukzWr1yp#v7b*uHo%Vq5-jv~m0&XYY<a`Y~w2?bUsBo}I^U&a*d^
+M2%07A<rc0Y)hoR(8+pqf{=^Ur@9~T@p^S4mXCizHO?_Ae+QJ0GSz2if~cwkeHvT(w4@Qx0|Z}DanY1
+lE%zM>T-HhdWm7}BDikKZG++vhwCf{d66+33^w;U$l6U?iX+srri)f_n;U2$kGI$;Eo_|+p1k%eey_Y
+;_oT?1I<YJbt@?MQ5JiiRbf!&vWfACXLe?Wvfc^fnlyY5sM9mr`#h+W8WQ2p!la$5%`dJoJ5*_)>z+d
+0|w2#odjFGnUa&NKvt7ZXqbJEt=~p+!TWv{G1AWgdcz6uh#y&Vy%Xx9|g2*8RH4pKTD{5@(O6Py6|%=
+eI$O^NvznJ~&g2?1+H(>*-(4#Rw~oFw>v0MA30OV3$O4f3$KY+uH!+5sLOcSmrRlGFC@Goxu|yn%SI5
+$0AT_GvDDe$`+ai$855SZS-HT|6G>Z!_#leV6_J=>O<}+C2!y?qtVNFcf`Hk<0051MX84ao$71XD@+b
+*_5?Y_p|oU>)u7{W$k$Sq+lU!Z0-N7aO>@8Zg7fgXI1edki$VK+c3oyc_SY$wiAs0Exdy@hy)eC`m?|
+y8bZEJt-NN(+>>SDn<E(4lAtPN{KJ9*kl$**OIt@n_>V6xAP^H)kIU|LHVd_Qlk=mOLxB5F!zf8K8^g
+`PVK*WhpHtcrjo39j))^tr!+<#+6ML|Vp4PIAhB@!u`=}Ra4399S&Qf<=64h9Bk^J5L_@2pHQwvdKtP
+e$%3kiF0a-ImVqF%(dtDB1_*^{zI5N9-G)_%<nT3q;EIit8*oxj<&5lrja+?{bP;5G9zim$s86L!*jV
+CRi@QM1Bq%%RCsCmAk2OKL<<7IIc{648vGsD)0&<RFkv8g$XytMpZ*J%e>@;5Fl_iUw-*d|ME|9@Lh4
+oLqcX`ZS4PVN;4A7wY6wLtf?k~1?P#5O>RfaM4}n#1cmrnP;MJas05`raM4<vWC1)kk0%~rz^iuq3>3
+<6gdX!S#Us9K;_hH78fS9YaZsPC{0<Kp<ry^x#9-4Cljf*77e2&KoQmhc>^unAiNlbYa=4_-wF4+o%(
+<#(*U%%bv+D>aEZ^dbE(Z^{AzdR*+8A68jQ&W{<ee=44p^sK(G)jwq8}vFb0HltFofeEf7tb`)Ft9&<
+M=rzMvX#DR4(>*c5ZKPfh5^>26Gx$`kmGNs@RdW#g4VO!&<Tf|H+pzjkn6`g1NEQV0Y7ELG3wNXa=U(
+kikTKpo5Ef*9WZ{{~bHSZEQamkMyT&+roLMTPogQA8mX)(x6jlxJa=C4FTQXazbMbC(9>033KL-lDsP
+2d3Sk&otX}`8?hkS37SHaQVu3iPjDlETEpaw6wJyhlF>#@NgK6LDmWl*rcQv7x^;Qad&x3H1=G4=i-^
+kYBF{_tywi7B>FCE+aMDgaAZD=HA(SgXJPXvO0%gz|2UDenh9Fq;vUe^J6ApWlw3Hz`mvT?VJXc}1`!
+_jx8NkyWz4&nskFvwpAq;*@FRGnw6d>r67xYFSzW7Hv$v{GS*DI4fo?liLm9@WWiW?wS=w>*j$O4%J*
+7cm3nF8qnRW;%^*h_=nOXD<!n;g{+X(T@;l2Wm%%FF8WWXfqFeV9IgMs@|qi7La^HLkM~%;g8eEwR93
+?ExuO__)1gNxPAi-26~Ma+<hqX0^(VYttpcV0HFKz31}!@z79>Hbv~yr*mI8Bz}xQN2fjfSO;C1lLsN
+$k>xJ5;dE;rlE&=owSdSGHcEBTUzkqhLvaW68xYBKvR#l!4p!?+7?;J4SYKj>lS?IO9lx?G+}zl(MAv
+U*HZ@%jbyuDQ`3>!Vc16e1jqN$Nt-rs`GPH4Z9Zn8hF|fYCq{oJ>3UaM0?3C?*as{Iixfz?GS{<Rii=
+iCooX5Rem<<p41E?{SFp{=lKlGaBe(wc;T5#4HoagiixfNz9eQkR<rPWZwB)jRTZdZYVO4ZJ{Ljvo3J
+5*7@&E`OwBA7d+4t7Bq=gby7nNk@_afy0vH0?lLJf!i8A1OLe18~hNNvmGZ#gA%cbPYY_)P$~A=JVF7
+t9e!mfdRH1;M5b&6#YNNtV&w^R}FFR6n*%yHn2d&oq}2j%*bk44y=-`UXr7ZX+Se+KS*zqfoYf5Uo*Q
+YjqD*99s%w?S+cylWW#uHrKR+RJ>rr@DY+*aSt<fc2AjdQ(&h@3&8@BLTl_ZA>tn78Y{m$8x$QBvJ<j
+-A#>xu$oK@Pwh>JIIe26JENhH2~F$Q>YQ4Bzkx9Y&dHLCigZsSIjN_s`$LeZO$eVm+Tm>}HHb$(B~VZ
+W`Ffp+X6mDZf2#~2e%ohEX5*5y|Gcoh|$ZB18>2AJS8)rP*s-Pz{Z8vUhmP-#Tjso_cWuJG#}mQ?;Im
+b*@#IolH=d4^1S><UFvkL6g(ADY%^^ZQ3k(uK*wmF=JcM$6tMz-#VUzmZ#!c)!}FRFF>P?&M=>uX;KT
+tjn2`D^|_<+06T*0y9M@=P*6Tdnxc4nM+k25X>C{V5v-0RmJGzd2bgP0z;0$syhzetzB+?kTTg-E_xZ
+f$Ue(*&C(3lS)h#ur3kOAgX*|=AoTu`q!j~}Cz6<gBo-Mbl!~4OnvY=dc{|zt9%NxdCJKHQWG?5-uuZ
+j!<9U}N7rkaxmf7x@mF$#EH#>Yj`DWl~a$cJ3HNC!gN}Jr>zr9U@gKc`$b;JkNo|&1AQ=NxK=gSupdh
+44dMo!QO%ZzynjQC7@W4hGM4jHv%>s>8EWI#pxY*c$vS<P*8D6ob-8fv1O#KW>ikS>epKj!zIz^=Xu^
+_=$(Qi7Zdfwn3tf%okKL>9#eQ@0<+TThn8-S%LzYQ4(@^>@LPRX#by)VzH-7EH;N%5Mi>`(Zs1KJKIs
+4+Bnb>oL>aIXy`Ivx<?3a&<~5p|nZL!835GQX%V>@l)edbzRlBmC|{+(t|2QTW7WXMC6|Agjh@){5Di
+RGmHfs@r*Q>a7{j3=2}o4m3)VuH_6E2Lg+b7OLXzyQ|bPqyh)x_-1|}b7TyFRYPu8$<ACMCO!&Vn^8S
+czvJEXyT~dTHv%)Bd<CsD+aZIEp@2O5yA#BHde<~)ERnAXL6wbRw?paf=j1f|O)liY>iwQ&bOHF%kkl
+6X5a>_RCl^d{1bD1L25BM>l`0>avS`nF6(Wqa8c!uJ1SStHYPVqSD;4doh``+n1(KdB;g%__?D(k+ZL
+v7*?#@@~jbn~{jE>?L~q;cI`?clGSv$XkZT0MXD&lE%kpvrp%3~w8$mn%?3LEBGssAe~dt1Q1u=qL?s
+oK1I)Io4uwU~Afy^)0#v9es^}*pJLvtRG%A>1tuG0Nu-u5EAvsQQe3RIOF9}h!v$Kz?PZc$t|7b`br+
+Ulod=mjI5<yQYdOqZhSJuI_P(?C}EPp?Dx}3yDx(mli;Nzc7U{sAq^e0>*qdjL4li@ddaY-o<FymE{n
+%rk=l&#XBt8lm}y_9>Q*swcBsN$O{yy^og<2remMcQhpw<ld86Sz$Lz!)_ihTXlXL%xMw@kXt9~id8_
+s1>MfSt7{;6Sk&WDp%+a?E&>`>p>y`oEZS+Z%SQQm6`a8|n%M*8hOFg4W!J=Fs})r07CfB(1loo9yGa
+fdq(=fMwHl*6e5yc<qhO0bAJUAoFEibzFA5@6-7uP#0lb6)ld>5*%Wlu;kw)llZA8;FUVkEj6niRNjw
+^PfJ*Y8HP$=dfFQo=hLyO4lO_3yVDM_M1Ugs2i2D-ME;>{Mc)t0nPH$D07x%M=w@~-aa~wJwm*f&(=I
+s++Po~x|hYITTjBQ;C18=!Ox-VWtz<#b@;a!Ds$dO$YdzG3#3oYFu=p^+ppp8zmiICm{cP#eFxnI9>4
+_Bw&R^rzlJ^4BRsD{^NfhP3%SAo--412S%FT3-A~1eQfR}UtDoS{CH%Qvlo$l{_GJIv;r9p0!Jp3#K4
+MHFU*#P`>G<j=y6gnMUjFn-eW!l`;*f6&OR1Wd{4cbnVm{Xr(L9p>CSBAuUV?}_vSWE+mb@&^%3`c}i
+Jo>BEj$<!&nJQ@OD4v|JGzIFmTWLO$UH#e?s;>z5skY)d*;dv2~>q?8+J7Xflytbp3O%`9y7BDUrdk+
+YJ_=lq_VqK+Sub(Jg92~s35ivg)ltZej$g*IT*r&(~pBjgUK1w?)XY1c}8(V{{k_DISE26)tTevV?a$
+lg@1?P;#=#LObZz{d*+*`wWz1V<%3OL4mODZp4ukQy5UyfrN0%kg<eG6koB%0Q{Wl#lZzSihNY)=Trr
+VDXt&le(*G`2n2p{u`vKpcjXsdRn{OhSD6n+Sl>C7GHB{YOrlR5p)Kf#6*m3%O%5{AK%G0Sp3a{*UsG
+qm=={*7ql^*>U^r#!FFXUK}Hs`y?>}&HHGyGuA+uROWefiHXzxuK}myU)R38)!gzb)_v7?i1F2aqJ5x
+~Y;v_tT;j==+cjyo&GG3$x6ZxWmM0RLwvt2Bn+AoJ?;|_YV&-sTo4WZOUzQ!jyT+H?ZNx=n_L$i`IdH
+Swv~EUo*n;ej19ZXYUDv1|Ymj^po^LpOADE_1q04WAD8RvA<MF5Hgw0(2}J-W@%<f{c~_BQ<zD@gE4J
+LeJa(<%H4@4?^Mt^x_OBQmg2;+DnhG^wSiYw=oY1|q)6#Bz_h92@JQyL>792f>R6VLb0RZVE!`5mp=~
+53Xw*^KXSdHNC-qN8Qfi8|r0cIS#7-uSmVsHfX?fl2f>}|`!GWaA^L1Iep<CRRJss7j$+0+=m}1ZN`f
+1MOW!26>;c^JzPb$?mV#3OmH|W;b7X*d<3LcB#GHp;f{PgkgjP*fW?f!^=KH#4}gyZlG{yz@8c-LcD*
+WN!|%S8nw&8Lrl{CM=IkKtfsDA=nvX*t=&6yA6E?d9%nf9HbVDeVIlDEZ{|L_+BNT&Rr0Gi4Zn*&PfN
+O3}^5fb2J<wdK~?Sp6<9)0LoiyY`C=F;hn@b$8d8vOsOl04_I{OWn-0h&X>ek!AcP(mRs`ToUW3w>Co
+Da|a#_49biGWCi}Q<NvMBd(+4`<N;_8ESe6zu<30Jg0VqT%^He|)>lBxnm(QIDGs?qd4tB(=@(qxNEu
+Iu7JWO}W>pw~p5=uvOgQz*laJCI9CdQmLkX*`)JU0ZvQKwmOy81*O2u-$9LU$kZOEzTU_~s01Jtp~Kp
+XH2uRlt!dI{!q!g<dfeo|bAFnNRLj*jc%F;q8r^Tw|0veONef(GK<U@Pbbdt1+*QLqze>YC378hrj`7
+Ce8B%I?iW9u1@l>rP3AHy~jw2{WnsX%i_@k8x)1a?)qt^zG*8M58)Hn>TOv=)aCUn?&2LDf3M2rd@e^
+dyoEm&@#|jFHkawwwaT32%o>(d;VeX`6(;_^yZ3gBhp<Qd3H%2@&z57y?r`c>12xGxA4w3cXSF`DPC-
+~j+0&ntL*Y$@eif_N4>`scYPXk?E#uj2CGN;SRu7-Zw+)aK+8d33c7;AH~t=tueDYhKju&Fv<Y-x)@l
+3reBt6d#%kJ?^umo=DDiRxlhivv%B3aGH%SMZk^9k^YVBxvr)`zzy*u%I^d8`K5G7BU5<{}a+dWFIQW
+fM?B~7Y-Mv~!vjk0XFKaPE8vaGN}($B<MlM>{Z_-2QRsZ?J?Li!2+P}Z0IlaMEe`|pxJyg&NJJ@<Y7b
+2R<SuNPaRb96-}`|l3+|CpQ{?0-5rMW>;!%JeTg=jY>}FV45lw|>Dt{NUG%4#0=Q(+_XYpvlvNGq?I*
+rhobQV(a|ZU(SD~mj6K~YbdS&cXH(=_^mT4^ifZId}(?>RdVDNU49*GZIJ~2xn8v|$MEMB{`vCP*vNC
+`?V?Jb;I@q)zLg6g6f*f8z7L)^d(bW56M>!rdh~2->sheGgkI`MRa%1Ru!1aoS90;K8{DjerAf!%hU2
+mjbpD)T<a1Q6n`<z%!=LGDobcE1^U+hs<J{qrqu6`SXg0-ck?4njt!7E%9us<})4Sjn6WD%sO-wWKvZ
+pNi%7HNWa_qFL>vdwNH8L>{6s9ySEnh_|@aAWoUeFWzC-d%0GKA8XujL-X(P^K2F>HLNH{Df%KW$k>N
+8`7;bUO;wJ{}t1HX9g`exrptYuIntq*;?@#VjqQN1Z?_k^pX2b|8s!ml-z<`;B*Naok&obzA<b9V(%d
+tzux|#KV13T+nTt4?vXP;*v8a==*G+R{H$hDXy4<ToE2mMDMJeCQoYJL8K4h`LBoQ&YsC(x4a3AfW+0
+uefVK!m{%~NX-Ro^c6NNauR~nq{ed@mgP8?IFoOxm={$3>L&63gX7v&scDVMi_rL)XVOR`a`|dnfH@m
+0FEa#l#q(ZCckwVA2tBhx~+fGHv*%-yETwX!P{!vCm8e0ITuJ~^0jC{4K88Z#i1~W<p^x}&%$zORa6_
+U5KBdpaD9k$@un>EGX$eZFhf1ME}6tSAIs4<r(=Z*%39LewUI(f@lzp^fE50iEcu*w7>z>IUln+XN7y
+*oVmFC~!3*yNS&%0();HBmxir>E~JKfqnvzai~AV7FKCojYw=i;~p!gaY6d>P`M5?dXa!-aj}wgC+Uj
+KOLSNeEWoH?5+Rt=;OgK1)$VzldcjTk_i8i<i5`N_CWcQVAF5!<nVnoIurfv>6HO9l^AD&SRS7oogM8
+Ty-$GbH?O|@3@jwMCl>J^GJ<A2s**h2Fh?FdM!uSHO>sy96Ru#LqhlBauHfegMO$xl2tmYT3xs(xTcq
+kRY&RzMkl9G-GZscPo7MU-Kgz~QrroRi<R0eC9@4V1$_pu?S&BFg4Tpi9p4>Fu-n~6dPCp(FOl!Nd(-
+71jXqW^6C-_muGCmoc4hr%9l;|J9amm*?IZO%rgyTQ7HfT&EjSp_2fOXd(^hgOoEY>HMeb(k(En2k>W
+io@tKxG*^x8JQ*p73vrN->^g&g$DLl2ZKi-6vp$Z~t`CN2{ykUS_I3q*_~R;XBUe5`B}3+<Y*6X@weq
+v9+|hr-)s6v3<J7c0?NMtorH{uoqGDr{U(ul`X+U25NzN+MzzmU4iK<_>^&%=`{_ywX#bS0n2lPp`rJ
+9IeOTc;dBhns={Q(eY`rsz1uP8ylvGT?Lj(g*93_=WJOJ<T$>X~Q+{SnZ9`4S%=U5cRsb(zQV)R|<z-
+GcpHpcJCD*_JC(sAixWgo)wXxusv{0S8;^>dGy9(%G)Ump*NnQP;VL_cOG%vSwhZ)b?JS<C1xhnWniF
+BU3N*fjKV37((_<~|3Y+VWH_8s3?FM}D0nv^k{4m*ym%PD4yy2hj#1QB&L<6xpB|05?3UDa^#FyO<2T
+!vN|ENvC`mbJON$MKK3iYYq{by4ztW2NrwNkP+lFKHuBHgz+Ifpn`j3$9Nm?@3@esfHv~LPG~<fp|}Q
+?Rm!8{$Xv&F+Gx~eO^a4`(4p!CSaBzd95)O{?Ew$&v}k0*;oFfPeOY&Jt;TQ3^}7?gM9k;3XO|4SYp1
+1D`FrZb_zE2jQ56#rGhBGOL5y%V8Xu-X|;V=ZJ#ajZ2<P6$THp!y}Os2{{#htO!A6i6v#&W-S57n{Nj
+|Z1|!y&PymJrtq3BAAOq>cdljR=nSNx@|K0sh0mfSL;SZSL-K1BEjiAVA3@|{Xfq0>0ww(T_#*B{$O{
+V?Q)14+73Nk*KM2R6y3dBUMLp&bRTnjn`T`gb$DNh1r2Ex*EbwD>~4brhFGh)Xa<rGOsOh5r}RO>i=)
+JoA3$Le#XMCv1;hg#6g#b<1`Wvp!2`{8-R45?V&$}UDI$b@16*`ldQ)f5J0wr(Ea%dlG}_Bwu)&eK1C
+c)xveyf3r*uo*@+EGl;tG!snvaWs3cE;csMY1b8+1ktN;@K4H`)r7LlUh`7g{%VgiWP;oB$cHje(zBj
+Q2|&zB?(YE2vfW^rr@+R@P4sG)FSa$WqQK{UX9L0MpMaJJKR|x142i{bOsIbmTeAqjKoq@#HVIB`)Bp
+b$A}|5Y8bAo&Mt94yS<R3P`q{ANCs`3Q%u~*^93M=~*a_2dAb0lQn||$SEq2;4K^xoYxF(sNi*8(u))
+(U-hyU-s+Wi-`jN&0gEJYI{vc>&Fb47hD?I2oc8ch5y5^sh|eag}3019)&hR()1@20z+V+&k}VyTiYs
+bb#u9QIn81k4R4zndPgfxYS;Rxiy<Y%D!Qp6my8&&OES^Zyp+vReEvU@&drKZV6`^koW-N8JR<nwDMb
+tZ28dpUt|bQY1LC>;E~%wS2d-J5P2^4SxIyKYq(p&{-}}MLkBf&YTI|)8TorZVK<>;^%(&#wF?Ok!Bp
+GOxK-FzGwrl=ixOl7wm$b)k_%|xhm6pj)HZWZ4-@SeT9$7g9?e^AS5tjFuoM;`=h-Xj4_R%i#DarIob
+7yQo@?Apj)2f)zk#-%#bqc*`8@<CGwml7M`H=%WD2AdaA0oG+Ak6pjzaYX146H2t}i-U?CKrT<iF!C)
+F&jVKs99^m}^?KGv=deKnU(5KK|fd=J%3+{i4OXbOa_R=-t<bsW5_Z_!CW9t4#&G|SVHRaRMyu9Lvzo
+hJwTM<?I%UQ?_yB11E*d(SJ5Vehd$j_za8;^ZWK0fR3ugtCWHjZ&54VKa71hSoJELl|`VRZqW1Usf+&
+4eD(qzI9_^mY2m6vr&b6WbESB?3my21cZkoNE?;vv`h3!G{n<2H=U{%{<yB&90SZ1xMv19c%U64FkWw
+u4`C6A@1)s;wqXYdyUurXVV*7p?Ys&{!KNB4X2)O6pxGQx9}nKuYKJL8={tiRx758zt^gC5PkCtsa%&
+==aGqsJ?qT`p5sm12V8G;O@U6D|TFaKHD&Na@=VQ3n6Zd=qj{%OF&{xON9tMN$uWhRN;k?pZ#3L1ST?
+h6+@T)G~O)%AG+^h^QoD(oqsmpsUZsd>1fQD3OZ;|AC>!RG^>1<NbwvJm+F0=Y&Y-YKBU|f2~M&v~{D
+(IlQ%cgh9nE}~37{QSdj~$3$fYXAG@?*x%p;HjvJ8-e5bO$beG3LOZ8p>t&=o~<KHKZbV#vhA^x9*rz
+(7qaT{_)f?=hF49a+}n*PBh$82CMPs22g_sK)u8D*+WGD<tXLVa8h={NgavB(vCJrQS6P6hOwpxQi2y
+>N-R7tuGTU)q8kb)$J{7GTQKGWbqbXdy(N4~sAct;Zq{(i$8N!qxB4_Jsu{0@@I}2CJ}xn%COAp6c}E
+cuVT}pQO(b~hYt-o{T-~4Dnf~Dn!5|T5w^?K`*-SzO-021a3mwN_BRdh*eQ%6;N%uI2O1ij|4#)cfPr
+9IFR8;FxG*zGrzTUN<V3`ZoY20fFN60U4!f3=dQa&VE^frJ?gxZ%w!l%7ju$--E^`ZP`58%?lm|kD-e
+l3$3em==<?)F6vl5aP{l#uwDaeL5vHf^F&*t4tEMp!g+NIg?+s=Mip)LR(SD;(gl$70%7dvXB>GZu8g
+9hg8yA{A-Eo1mr_p`GrB4z;=Zh^)v-8m1j(s6BbG?@Aa%rfhR4F{A6T9tUn02V>NYB|pSw3GamPj;@V
+6h_&m1#0Ww5q|cf?xC2I1^c+@`+4AVMN|EPt3UH`_iX5)GKHWSV;LidIqy@s(GU__N_h}Ty6q-U2>*8
+u-x+*Ma&hq`Sa*q0Ls&Ro*WbuP@_K}2{s#}IMmfhkMpVqFmy7yDdbDee<s_*UWAm!V7R+q84$=SOIAi
+Wfk3mrcy_vlb2cqi#7nR`;FB~^polZH7CXt6D-tBG1}ao2F}C;|N|PUeGz+N2$`yMk}5Zq`*Lb8gF96
+>|;>nWd|Ct<sqk4W=+yt=n$VJ=GOFiP}92Akg^|%oWbwIo5|4dut(5NtT5U$#3>+eeiLj!I()Ewcb4r
+loBRGWCpV^r+5uV1wB{yk?GEbvg6A5K+@y|B|KLf<7tP5f~Z7l5Eb*z=|c%5NXzNYZlYp`=O!rry(XP
+cw9tWpWX(p`EmG(L9qCL;cC0p#jYnw9h|C5gM$~f>ktg#h2On6n$qo*Awao6|HJECQp0FaS)S%os|Eg
+<Hk`LCx^7J{Yey_f8trJh^P*-POuEoM2p=+|^Gh)|ug-L~$X{B<O&X$?iF2STG9bd-BBq^vcG)bE&#>
+>OwgGW5NjAyTsXPs(a;u^a^zQ9ZR&gdH+eTVg9Vpy7-;dl~Z@|29+b|HT;N!7koH&J$vUi}96fIGv7F
+Bbe)J@~8mHZ80MU$R8DYB^6-c*^5^Y!!<_JPwD7P>3-n)-_oQq?c{`>`H*ntG*Sy+W0?EO9KQH00008
+0P~d=M;R7wBCHtz0H0g{03HAU0B~t=FJE?LZe(wAFJx(RbZlv2FLq^eb7^mGE^v9>JZpE`wvylVD-b(
+5Bx8z^WT$DgvD4nH+nYVD>l3HlyDKTW6a~p_B$6d5*>2tZ_MI63B*2Gk_i=mAx{XcZF&GR6^TL4ptoy
+Xff_NTA%OTt3i|*%G(%J9qvo~>alZMO7oY`+2Ht6*SUHEmx-d}Qdev@Cu5xa;N`IVn?_Eo%z=6)W=kq
+135IA`CyeRFztaq8vQIrF1Ad-L7d`*&~u_Wk?s-d$iJwCn8c?RDO+lNDd{D5u^mUNE06R{T1gtvK8AG
+=ow=j%C3mG-F8`Z^JniY?35SbGC?6c7A%^J^cJLw=)m3JPl`?oOgqGod8S(gX3p>?MHbSXfQoOpcAeM
+&MICmp-{c1ywh2v@tS!85R>HyKUz-nn}lbb&UEUpR?{gvW+Uq?PS^e_{F%>x5JOll(+a=!bC^PExgA#
+iSl{raAKYjiL_u@o3pod^xhiVz@O>nyRN!=)rg4fLmpq>enp%@i2Pe-{n1-Z+2i4zDnLV9G{+dsxPN&
+leR(_VT7O3_Q{%XVN(HRmNK;HM4VaDKpKVqA*n_2`97XVu(0JnfIf!guxM;_!7)|@ZcbQ(rsKAqYbUo
+Bjg$twK85^pLN2d0tXKM!9M+o?24{4~dU^_NQFCF6Ehwgp5uEO(fNQI`8rz-?;pvU~#!TWh!Ck!aws6
+V~e#C`-6Cd{aNnI6I5;g8%tkP|<>SAL>F?!NLgkM7Eg`bu6a@K85uQK2A$cmjlH)u!1&NxM0YyRO_%u
+$KtKpszmEOR7+$6KZ@d<(GalDpQN;zo}?Cg!i>>$w5I&0Vv(w&h&8AoE}E6(;rS+w%mq6Zu;jZwT*O<
+4lYnpz0+_%}4yVCb<$pZNuUau)wvVP^Y)h00H_?uPw5w0VoXMTkxGz`(<NHEg-VdtK5|??N44*xl$3f
+;Lq*J^&T|P_sf~Ood-7`=s{yLuX)xMAh0P51uE~irsHBlhAyM`53+S;NRlS+A#w)Wh6lG)Z1*l>G_*I
+{tlgVU*}q@rs`9s@WOwZhgri9TO|W=lYl+!PSPY?`D%1`-D~IPLP|yg_~_>A^G4Dh0Vz8m6F?qFKxo#
+N$~UaXBqeu0S#<OF3!95OzVj_rV@tjmFvd%JRCCr;a_gZSScwcAVFHVs~KEdOz!f{p$Gk<oNd0?aA$<
+<J;q32JYdnx1Wxk*W>J|J^C=t+{uCS*b+dZ&AJK3cz$4yJ^1ZBeQXu1wM9*YYKMo&av(kf(9=HJ0>F|
+`#BlY5v9adBc5~`D#TD@LIK_GYQqbCZWewTNU(e<~+qx`pSu$i>*xf;?ZLO2WHYY}#SI3QwK(YmhR}J
+J{%}waX?ahr=_i47zW;e)${$`cCOf#j9PuR(^SAzTWFdQE>H-A(N{;R%&TD3hWG}=KXuflpGvo>k8(l
+jadN0#s)T-*Q$NGMF{SJm}iHpN<55OQ~TYur@2B>~8yFkjz=7i?x^+;BdW`c;FmOyf<02&_HvXj@f8i
+KCBY%TaIAZ2f%XHwt0w;H}~-5CD!?b2Oy`piLKS9v)?39&Wi*{b3eISph0o!DP|so#?ZaLk}kAOathd
+!*nCEwnl+MObmz8sp#ls$tuik%e9<|0i$TD0W(K2)<Q}gxfX#_YuUUe?Q8QkqLo4W2au5FPk>BWhh1N
+*dYd@WmkM_Z?xb3?tZdbvG)*G&cr?$hK+4&c_tbKl=K~F4K%6ijZ=;}aX)CrgZzjPQ1{hle1!O6(&74
+-Hj&$pf1=Q2l4`~=Db=u$$dY_xsoXx;_BSSs(SH(WHxwrJh-~*gAySfa6%N96S;3wt63<q1i-0iS-G5
+g4Q!oplsNNwB%W(&<h7catV<gn4OJD9laeY)Xo<Sy(@p|&I=ZlfI(-)H}|flZaij4VyiQs8uMBXXXET
+u`XDf~d<x9J1D&uQJ|@2N-3SME4eYU4b|NPcBHgpQBsd0z|n?v_`0HOBr$x^42GjIWGIk2drzoNf3E{
+lE5~w?Kp8YSWcVcM;@F_T{VME%5Bm$a(HdEl%tA2Z)kncsk&4FiPEJNaCj!?U2wbCDf$lq5r3^Z3@M<
+Zdc*P)0Ol`!BqM-92U-%eRJ=oU;Ytt}c}iLe=shHw4gV><=zlhJ)+ZA#bRV}|Y%R=DVOoW1<5DTg6+M
+szlv(9!1SwWLG<+s=sD&D&w9GIvVXHTjx6P?6c1}V;2WWntr*hpFHGsI07Y&*wLyN#hiAG|zG{V~jqJ
+^Y*LSoM@DbnIsH0YIflOM2YM!a0lI^i1#BApX+tI0x=b}gO?zxIQCD#xmOJQ)~~%8}48#E^;Zn4!_wm
+$AB{6tp8MNKJ2{0b7k)s39sHHJvGEAzJ>i5tPEqB@6743RB?Q1A{A>2(D!R!r%(og9>GQsnGw=3%mU5
+0{}2RhaLRRh`^Kd&yNRm&QH&ej(XD{PTyU;{qAh~?zHCjZeQEr^geRvkMe*W+xBl~x4~^1I9LuY_?_l
+iYC&&1i9r|2F8Ds@+Xq27ut$FP=P$eeJDyJv9qr9|4M4~@?+_glo=kCauo||K%5~QYP5BxXOy8<*!;1
+~p7M>;tt71wG<!+N8+e~+`;!48-WT?Fr-pf|=UiyXi;#pF9gIx+PGs-yxfRSM#-|%hJ?N4xX^-A>RC{
+#Oy>W)6m!dWQW^CSjC0YFfRY1OGZM7HQEuu4+RPdplaCWIt%Kw11^_;MmycdPjzy<vYK%2#m!7I|57I
+8n$Fc+ia~?p+f48oP_JB~?v~MHT!8L|wugpZk+yu?2>$kOltDphX@2DS_jQ2Aw9_23$~~5w@s;-vwlR
+)&L7i)nXHhfXxU?OI7o)k=?KuIga{j+-9noeKaD$ecu2<$%z1l;%OKtJ(Hnbf5PyLEr^$?pMdw|GJhL
+~^9mekwu4;>a(m6@VG06xb>p(xCWjFM5X-QkXjcdyOb(mX;H20SJP2^)DlS$?XC%27X7g|f^Jy?kXxc
+!jzFawYt(Zs69*yePbLr9SLL^q6gITSXMr?PfBP6c46@f3y!S+>*amHbZ&>!Gk8MAfKGFXHW0Fx!O`~
+5}E(`s~xSQKl4d#cM>hiuM7>?z9!>!{mzN*+LRtImF}6b&_EDF{DXu2T-HAP~Oj^XnL7Qin(J70@gG$
+o!l<9x%Rh3asiA@(k)hMSa^^Q!)0wfi=uY?`qIS?n#RX44tJ_k)+5Y7T8kVlfsm8HGwW!M=D;DlP<|z
+GK^iq$VKR`U<A-x)2KMbor(aOuEmOQdhGz%{=x7g!R`7IbQGR*m(dLA7k(Yb^Kx!|Z=kI||7jC~*1}L
+etjQi!dc8fpt=H4v{Jv3l=nu^IS*1Rxyn9E*`}^<y^SdFiFswcKq47rA&VTcDY&BNeXX`Kuft)PAijk
+}{n5KbU*-~wa72om|TVwR&(%+&;Q8gLJEm#2)eMQT{v}!$LRc=9L{q*4Mezg)7JwxvuY>5?0zMpv-bM
+(N8y{r3gAA`ml-0I+X214Fi8H+f_kU&Ka|HJzqR59?!>lF{$+U>Ky$5%iL@Cw27VZdI>M<%-mu|dFqO
+g<!i0Dc#`f_m8A{sc~xcjP_ydQK6Fn1>58r$KfjPFfpiRfYw2+g!!^;yPuUAOKW~uDfEM!nT!^n+kgU
+24hOBXw8c|e|P%s^qcAXFTcJZ2QarsBnhn<{tEC{O21)8TNCHE9kz*K{piNdr8i2G(#NJyS0$K1ZewB
+i5+SEzMeS>s-N+Fr=#{7TwV3CPe7z}j&NVX2O%0tQ{wSs6H5kx1Dn)~D>gbnlJ;rX(D!nm8;q@pYixf
+!SAkx%Xq5Ru=SXdE(f6|E@9XnZ)q@|~8>4$Avi&bt_j_Op+(x`c*gEhgO=v7Oj>R7MhKUuZYHdW`Rvk
+(MJdSg|O#8it&0oKpK{P~Zt=cc5M%mC#baUEfeLz$`W$<LZ9E5|yp)X_i*j5N2>P%@y>)xs%t8v@J}@
+a|hcFfcnSt!tc8#3?hc()8^@2Rlb`j0j6zA?ATR0QNPIq0+e!z-5dTAw(7sOz@(&gE%5QqsG=KaTkH`
+Y^H=%p+5jFx(c6PNxK7(RDB0zjL~6d#5Jpl#|GxmxJm7Z2XRtEt0LTHu;(stI&svLPL8#f)u>)$G_s;
+b^wlvdOB-{6A{NA&_hTGJ_6VhGD0EJUY;Sg()aZ#!U`p>|mH^tR(RTT7LfcpW4{2LOX#Wb@n)}P7Z=D
+PNjW9jgi7Ef-ujN<iQ`huVVZYmj0MzGE!l-Mb()4fUttKdKM+6n!{UBPY@Gk5MbuxyNz;_*m>R{1d$v
+Y2Pkw*_+ksUk!;j2<RxcQ4k?(v->*T@>sh4uYp^?u7L?cX)jZxQbDkj|{7Fm@Hg3T(4L1hZA+->WfpY
+71~!&%gQd&FQzNXYbJ;w5hkPot;=C@1Q$*Y&qZ(;;*J33{9+Kur7-zK(dK~JgWgqtq&t_Vvpwzf=aEP
+H||$~?lbQUV^KZ$<Kz7SuodfA0i5n+CsHL=K%e<;q*My#V}Vu^QW6#hgd?zmRYb1>XlqNUMLC&xAS59
+UB*Yq0MUN(Msm-^opdJVg7IN~-!2Oko<~36dqZO+>#Q1c&i86FA(Yvi<HlEr3YT@7d$))d%X9Uw<!Ak
+)b16x^^@D-*%K}9nVjF3|GEj-bKfYzyEW<kAYj?jQTIz)3noriK$R>reuJon`)TljphWyWoC@%>j{z5
+T~McFTv+@RK)%9dodQIq%Dv*2sOj=h&n1WEZO!&^FPPPbn3_rJpuI`!qb5GP!M9IDnufD?i{YT&?&LU
+Bwn?e<VA3y9Eq|*OhER5V$61-+z1h?(LiD`MXnCo9c<Lan4(wre>Svl7qTZ*PF^Lj56p<f8VDR`;+Z4
+-A!GH0?E53@mprX;#-+iR}7*>N{gIXOc%opJHQlHEMXXZ!bn+uNjI!?6v{LWb=0fUPpMj*aDjfs1w&0
+>uxCS97aDunW@cl1R~8!wHKM4YCkSfz&r-puGj)i8D5%pip<rB@5P>OMImcYVc#|`{7$*CQsBo5xsqV
+{LV#$2=ANb45cs_-d4!Cx%U0G=vVQs))JK8SN;}q(uBHJuPg|v{ctah*}T9mO_N)ImLtM!&649n_DS=
+5wDygb>K#t_&qhqXf`s8A<tJ;PkmJA%nn=~<c5XpKp(wlmxnek%I38x~@~(cy41!8DLo;07gRr{UqGV
+JYqyO0!)G^u&Xa>VTmlD%B1Z?_wjtduu<-{G|wPNx(&IeK$48{1zIT8%q>BMm9nf;c`#X%p156wh)r6
+Yas{<#*2F?G$b~~Kx7*{ST5{;AlvJhgc-^{5JzeQ?aDl>I1{D_txGpuh+Y(evuvl`9Ef28AYO4Lmf@C
+{uD6Wu3F^IFu$y?JB9~?paYhtkWU@RYv_p@34N-LjCc~I)6@Em)<lx{D!C%v@UCf=v_&T7q<QR_C4-#
+!ms7I{j5kTcsk|dls^IM=UFApl|LMO#weR;fxYA~Y=$bvKkEi(xsX6dpS822K$kJo)?8Be#x)n7S!#)
+3^M&#Qn*(5Ha<^PBUx9{ZYr`E0&fC(wle<(EkQBD}jHa-QG3ftNwA_tIrYgU??ed`x2yE81m)!~THT&
+kwzqhfm~DSwm17)fq~BFdCwz@{CBw;mrI^9z##i7Ltk7+H~3B(TgKyf8O_cM+NvRT%b)R78}S|c2Z{^
+)cFgMADppYfM&n4FC!MFwFOdqMI8iRXRLm_T#y~sTcoyxi&AONdCt>y7$JSRsjTx<J8{}^fT|)KB#`_
+SSiSR`0t+Drt^=OrZE0D0(n}`O3saE~ri=rrX(^q977<8CQy|V*`yV-oMtM7h(v8z_S;{@zwuDKJhk#
+@uLjf7fbVx|=Mt#jzmp`^u=_Sq;0|`>*A&}A64x$SJP4(K*N;<(39F%yqk}fz0bmVo%h<KzR3|dOED4
+v-H{C>D=t#m1ii<M@cG_&oJ`o+qjhKrHIk@NAwir7Jz!GT%|vo5Y+GtK9)uX`m2fc;BaXb)OE-UFEU(
+8CAGe`YHHCtAUSYE>ct1CZD)5h1y`H|q}e5Q{zEg9n+#0ys)>ApsO&1}UUj63>A8Hw;<A6&|6>cpk|1
+D~@RZDvAIQ=YDpH!_4En0d;%i&w>f2ZH~s>F+~&(95I=3e>^ZJ10tmhQ|zXPFZvi*fOikx;Y~K^d418
+~#Y+)*=wtnhmkk5U;V|7(Jibm}YlD0B(cb_YMng7$L4EJgrH5JHg;8F5y-zZiq%w&2Y4Tc3(HVQM<+K
+{XZhHuG+m~4pux@FlA8T7%tIm3({%~TzDg02Y2de-=Q0AdX8(n0zY$7mnVWB~dXW^W8`C@@NYPiUFzs
+O~KpgRt|-gB?_a!*1>6+|gc*^s?>!R${1knO+%Dxu4q_juMjAY$6DAC2cu`@EU|Z1yY=n<9#fvq5%>_
+nh{C82%nEBt7-^Jaz#xH_798g?T9pm0+_^xhh@*s)M8F%zoiL|7;hwh^bg3$#~0G{gcW*LQb}bwa(7Y
+ESCFAaXkrlGQ2d=z<X^|hg<kP_EKa3ox!lQ!eIfC1c&M>jR|W+cabnrZO&KWI&7XpSL8oCJNJ*zgwg{
+giK0U6{yF9#?a}Qfu$zRopJTdV%m_)Da8=zs-ndJ<=a%|wlj_|u=EDX81`=aAg^;^r5R%nmYC=yGLkg
+iZZyJR<-HP@!mpyX;yr^WQ7P-X43SHuX43GCkCZ9^&!cy6ZfoM4L`(E1b$!!a)EX%M@zo#{P=^Y++`!
+5EALD>+?L`y8MHcnOR*2Z;)OmDll-5M)muSRJ{bH?lhkSY}OA@5~_uGg@F&OsA8(A-2F_A!<X4mE;ED
+Gr5tGjAE@MY|o%W<RHQH3K!^<FKA=C=i4)v!O0G*r)#U9!R;Pol;Kh@LRz27SOoI2A4$j&=g{}=1!5*
+AxtE2U4Tm+gX}>A(FX{nNI(>G8U%g4MK>rD6zdIuQUGBa@+(0XNxh(%BA*6*X7{|$cFTRB0KmMJaBsV
+YKng&x4o)f?D_A{y{}BaU#`coyJ#}$1OwDbz_S26_+*eGb8YdfyW~Vi;%&Get>S@;GQ-kfkPsdQ=3r2
++llB99KGje+D#?Lc3(63Wu9G-vq?tLxxiPuRc2e&ZATabew1}(HOy|+$B%T%PWMv*3l0d`ER3S>f+C0
+D~J>*5<py{;+(qh4=;TOHoNQ19?UZj^ZQiq~ING+{-vH*U9JIeu=5vV`o$4Ap9)KLu=blE`pf;S^QU<
+T18VB?aA-Ok7sEVvR{*^;~%j%JeKuX-G!H{sbSamsayFVH_;+e#W$5`}2ofFQy34Q;Qw2q*gCTQ6%I$
+o#qPbsqjK}9uQZ_T9CAP6_6r^ky*P)k1g0MrUtLiPQ*jyxTqdwbWvXNksZ4&1eH@v;-vDafV;r<<WB?
+KMwpF6mIM^}xGD;*5<rdblU4HBcjsP<!&+lI{Kn*4xtM0M60Yg)%fql}wss%*x^f?w=y_dTcPy>Z@>*
+N_#%r?@!^STH%u?=uY~xmg%J)mLHC9AW)X0*Zhe57DX!oX_O3M-E&`xke*0X2@RCy3m`|6JMp>`If7V
+`IHWjs6^LKPMt{wQ%)dFy7T&0Sw*5#Pb{uKW}~5NKO(${V_%wDb?*h=?BLtxKGrWV=6xA?#)?<K#(ZH
+Il|@ny|rJW~RNKE*jF2B<+iO(rM<U#i>s<&vbWo1@2v;-7o6jQ6&9+ar!OjpQfv-WSNXT{ZBicK-qUu
++xNOJi;JZ2+?^n+?YN6dmr&a~J}wLfp{C1I10}T7{EK;%mrd0<fjVNX>UNAkPKEf)TrgZS9_1!mw|$$
+aNT(8=++7J&#pen!+|hJ;hd=x5zv&YlR(Y$OzPm$*m?Zr&5lW}ecOKG5Jr<k!5)Tk6fMz!oPw1kWr>i
+IOT$D2TGwhg!>&=Qj1X8(TSo_dNF;PLKQE5F0<y&0-6DL7Qsj+F+^&}u&+vDcf*HcA~9&|bQjtjtR|3
+~~-131_CC!xLHa7-$1uM7_-6BW$3jOl0yz2#dW0nh@<V)=a+b>Km_-sC;WmC%e77oBaw)g19Dc}xy+I
+B^lA<2KYi$GWGMq2KL>IXJF1N}-J^pBeoPTEDVYe$S;F{gV!T`WD2aW1M#l+xuzH%G|2o9KE0~qTc!C
+QJ9u1Z&%?bJj5{$Pme3thP5wl;XOg4SoWr+r6;qxfJtY@^0}i^j_$D=AKft5!f^Sj!?xccFt)3qbnfd
+O8W0-4B~nf1EM_CId+2QbZSXI3_!Pe`)G!bI(>vd1@*7&3i;Tw;co^v8cQ4o?<g0ns*je8mHTqfe&3b
+)9pQK>+lgyEyI4BU79JS-idL{qu)?CI%U3O&snm`*45EV_Q7jOQ4`t9jO>ByJS3h=hgeOifRxb^#Lt;
+bo#&yMqdc^GC1C#F7n>D;)c3Gb?J97+NYfyNS+5{-3ey3lm1V(qSZv?hW%6q`^~9a-|Dj%50mr5cSV(
+%vaQ5N#tLNghGHid-dcj423JHLHI(ZWDcs;;Tq~!JEE|BR`=>fKBsDVuUr5&i?^WO9KQH000080P~d=
+M}9?s1w^I*002$_02=@R0B~t=FJE?LZe(wAFJx(RbZlv2FLyICE@gOS?7e?{RK>Y4e)d;#l5BDoNHF}
+45G)$h=n_j@f(v9rRDz2!yF>`+EynFim4<TwuLKfLmgZzwYg@dnZ|kiT>D8-kxm5(K;x22mL{S4;#ey
+2!RA)U@qQqo@kn??>IcGNk_1^pWeE)d=dHJyCoSAuM=9!u2dFFX$o|)9#{xE0ZIL-<`P2;%zoc_<v{m
+=g>{PL##DUW+D>y1nIo0hzB>1~1UuP<8Hbl=@gcmAO0dw1S@?|ov?zuZ;SB;8x|{d<clZ>cT%!F_A)x
++*s}yVxjWU(VX!pAMSOr+#-7|DW?;!kZ30b^f31{j2js?EU2VU%~tA*q@vqV6c|+|H<N?G2nk;ynoH!
+w|@V70W2?ttJlMEOH4NI=O2!#OU3nYW>bzSo8z8=z`q-Cm%+mUzlhNb_-*Dm8)stasW<nqiFMlHWhO{
+=w=zJ4O~2DG_V1a$aojxoC^B(Rc)y`DTTI;JJQN${xXJ%bpU?X6qOk!yt4-eyf0cOGCK28fpEeM}wpu
+TQDdM<0u4-Cyr+6pFU3P`h_Sx{;b0Hkm>%K|{<9e@xN1svViLb#$u4-D}^gZ}yZG|>-x5E46H<Z_O*V
+_94Fe8s+eZb0_Z@L(6;kUQ&|NZ;_^RJGa^aV=)>gKe9%7~jIeLKb61zad1O$ZH$*9GQ5icc$WL((oiL
+7W)apTluMF5m!G2zW88#0NR8Tg?JUJ3u-ks~TcZaO+7cP})vwA4L6G@eIk$JSWWwdmLU(vWG)M=QJ&#
+f$#|+L3*j_T8`6Wz4R_k)8tnRgC?zjL$qp!oNv&^Opfcqf=LbUcWU9PA+K=2);;XzyvwL;=Q#OQJ0Lj
+e1&ktDE?zQ|WVqTyJbXzsTv<A#zD44z8EW#$+5SKPa1VQioLV4|vw-s|%|l9aAAJLAadx@l!j5QQ9Y8
+Fj*8nm>vtN^j=oMo@Ov2kahT|SpJk&NcjEEL1bte^1(iLyED9y=QAHXIN?A#az{-PBKx)vHE@$_^aM)
+9PNI$0ZwTyb#<=?mKG!_DuU-c6eMlDNf_e3bM-nF)|9+yP(;tB>-~>x!qJK&8NW=juo5OVt+EYx7}6{
+j`Hs)DIQC1~`&FXT&uqJQ4|Phc+qFNiV$}qdg}}JpDl%)L0L|kLoyJ)pRNdM!-gXb#{r=TO#$8oW9@e
+90&qAKvmQAs-R7NdMQ-Iitn5h89Wx)1Lb?^`SYw_!j<#2RV&oIM&fwkFeEg(#)z5KuK4=TRg2>3!=fT
+k6!G-cuVzE*BYj#yJ`A~h85cAMO;8_D3-8qmmN4=R4M=vaU>*V_GbB?L>C|bB(bm|V>vS5;3UZKRGNV
+Pep5B{mJ&AHkC&yQnw&G(PR?Nd_IW@musVttX70kg(j)g0W9p2zNcy00qvud@1*$@rI+U3o~j&+g^3R
+TU{NZ=jT)UnXi*6qdZ$WEmkS6|O^ih52Sat8Vt+-E~TMwP34kT}QhrA;m**nCASo<+*$0$6ZBd)EtzY
+iK`=Ox#+We;l(t3)!H|uk^$KeNRs;Wr_c$C-&=!Yw4f##3d~8em&6?`14pd7q2W{iF21$Pz`kfa3SWH
+?e+WpFo=Fl1f;YA2c*Ggejva~_%4({016ljVheX+_d=tJbSUUAFXFA~--UX)z}+lZj4j{Jis?Ith(NS
+MXXnE(Aj_cNNrgM==3d+bK(#Q)>dbHz4{}1-$2CH4Pm`0=#fd@QOHbrrw}Mcz1K5!E9wURdosrN067)
+-~j_ptfkZZl%$v4V3Os|(Nbw(QH`O^d32{$L~h<IrbAe@o<MkPb;G}p^<vjI?z0FMB`jSNk73|9Glqe
+?LcfQ5Y-fROYl@z|A85Fx8pbm{V~*fLtaA>S8Rp?!_=-07k<Sftv5)8&Y@ewmjZu%wz05A{H=b^}SM#
+o={ky}Gm6-%y>h7a?`waR5b>55#Q2ELK4UhNp|XvX=skni~({I|z|7HIqj$;OuHt><h!8KKS-_>T;5D
+*iA|3It`i(l(ORl#@=!kS|0up*5n+hCvUlu>kB%@w4BWq?JZ}s#moh+LrtetGx;Lb=g+Wgg_y0rHXQ0
+dk5y!FC4)YHkPjrzBb}8phIK}q$6co}&oP##_Y96gqtYf}ruzEuUVJx}KZY-H0<<0b<FhQIKX~Ym#`3
+Asr);oh1gswzqxq{djJVCK)jZ7bLMlVHWf1$%6_Y5-a%o^rD$Dm-79C#s2KaF3BzBcgc?{pw4+trtUh
+K(8`D6GF1U@iGW$GC{76Ai0Qn5V55|#tzu5h&(q@E?-=z3ckD2X0Pl0L#J_*#MzI)Qw~M)2#o4iQE$V
+J{>ULPF>`Bpf`Fv>dX8F`qvyUc>w7Q!t072R6|C0rvH#Bup`kh%`nf+TN4w$=YK55T<sYpAOnk4wd@p
+yD*xEX)(0=uqzjdS~8!GCCWg$2kn7AfTr4bpbt%H?sY~;1Mm6>YI4P;e7XbT;~YFV`dtKxs1E>yWM>l
+&%x@pUNR-dlzmHP?IGo6M$eBxTXE_TYr`iScZ)3SjDx#iralzrRr_UQeWN1?%o8Z-*FdsO4wbbvxutV
+|m#yvO-Ycz`q^j`WHWR2lm+a<st$B4$}6^RF-F^<v#D0ciRrIc=mFVd+N2U<9s%p9PU(6MYzwksJx8C
+GaVCf$!v985B-7?>{_Nu6Eg8(pu7W910&D&H9DlKL9U9U=%611#`T6g8^b^l76Y@C*dKx*7#x8{npO8
+=#2iA*i(;BX#K*<ms}Bh&g=RH$iWLj}}b9Y1T*I1@pj1J>&5CBK!U=d;W4FKHDZ@uN1BtX*IM$0BPbN
+d7&=RtR0a%tuD#lbTZW>x#Gms1so?%2;iHpm;n_cUod<ys-Etc4oX@8+7R%<gYHChQI_52^9O!D4`Lh
+uu)iA84lIF3JxRJc*9#;QiZN^5a@^_~Zu&y5=)rncuM#H(@QZ#iU9ZU=#F-wt3uCbhgGB+yM^Ng+`7l
+&G^<WpscMaiOUT$+}z-(<ecki;pXNSk}8y?<rvz-${kK;&|y#wWtvgJ5Qy-SWM4c(#RVn8eFhG>XLD$
+VaG4JVZ5WAZ60e7nvy-AwmS;<)lm@t+WwuYDJ@(cKWGWNL5g<zZB+i1LOLlC9ixOw7Y_8oCwFF;*&R=
+vJSOdwSPGAG9|2Zi3hPF+h-nGdjZ4YgIpqdwMoAfSwiupl1Z2hXLs1Tm+4gn;o2Vd7QNxjUwodhD)G3
+kZN8=6${cu9%2_`Y>fT}$U+}h=v34*=^vPaYHvu#GA;DcR4i&(L5?bV7=sW&|J4YxJoNn;Ol76halir
+^viTnX!NWTFfZ`=ysha?0TsHJi!AeHvQlXDNFcxW6*VnwX1VZ&nMvCy3;h6eB0Y`IyvB3<4MkRhgDlG
+R5iR0+?kOhg6UV|mo3@JP)d|i6}T985Vxt@*p`TTMP*33zT^hzirIF)X^6eyNq`g5iP0R>n|Otk<Bc}
+Ebv181mYNRuuL+E<m^tPm;Y)>3%_BTVTjVE-3DI|W(sNA|+0U(K(j|AGMY-^ZYs0<!}Mb45e!F#8AEl
+D!r%zLnfo%sY>FtSq(<9>fI}aePO%e9(05>td5kP2#nBtn`^otxaS7<XrUcyyYW&IV>NklU9S;Am*iV
+xFLu1<%$@>{)1#YUw=g#9^PU(BY&Ye-;xhnTR-MZrdF|s+%yExGp<#|3DJZDKJD;nRu8QXFVt4V!V8C
+2<MTXw4#AUkW7t!OT_WY{A`k~+(|4E<NePIP4luZ&x9y;@H9M4=q|z`H(!_~!b3P~LgY>(E{?w740m>
+ZMAxuXiQXyE8cKMLzJUTd7?#bV9seDco?X^L$s+vl5gKa>JjsR@jEBv83f3V7K2WkgvjC>^Wq^c0?9y
+K%W>BrRpnBdy}3KbbZVkN1vLWfrf?O&q&Kj%8u1ZJPBc}V!tixA=0u0x$sO41))&2g7+v6|+anv*3T!
+PIei^AKt7ON0Q*)zIIxQf}_&#Az=8X>WBNb-lkKi$n*(fQqV_B!(Zcl7ni_eoS^9+h`^+muG0>3LxI3
+kJ_N@dQbqaN*SRK$Vp9HWAY?Rj$Q5<Qj2|b+;>onLF}z^^AIOaCpAgUlWaFZ^KRCFvOWEw<3i{%4UMw
+=#7oG5&7gJTnCP6m;A&Ply-dKq243V6KSrpf_97B5i9ruajzpUt<4{k+q+N<q^d<q7Iz{R&mmK2N<x;
++QMY&WcUCBxppmZM=-m7CyyYkWlxi$|6lHMY)*|d=IJhCtLW#I+X@*Xqw@<&a?Hhm&iS++_(Jqf3?fy
+n?FZ)cNjeo7qd$Mwg?at|-T%-a>)j{*;a>m9;A4^MJ$8vx0_&$>XJf-)m$3VV1jE)cBI#f{}sv>fnPW
+1kW2O3|*p7Kj!&(RB`RQFGI+dk8*(?h<!FEuaNeUWu{?nwKnJZ3U}(wKZs}^-<F`tXn|2cg2M5=dfLV
+U}zpPNqos-YhosRkxX(6PmYzmuQc=z9JGk@$h*<AHj@9y<6thypG=E>k}*(ut+hoQPmZ{{*E>jG%k|l
+kY0*O0yG>s~9;63p5{V^nR%<)D=@ea*#xwCFcnmDIn}oLKSnbx(QHj5_u0OFGgAGX{B}<}~gVA#~s3|
+=8F{~4`D`H`_mO`!fv)L6GV2;jlK(<m5S<C}dTMnsNEpxLai{|0ou4vQg!Nqo54piNkgK>>^wL#MN+n
+~GDx%+W76h>vEgWO^ti1H#o7{&D)(Lxp%vWl@*tB6H{wgkjaJpC5W(5lrS5P&AVNR!Xok+h%`&<SDa3
+h1r&^uT<T!v;p3g(R}WX3YH<xB`f){u}hY-`Nd)?|s_^`W~sWKPgKNR-#@wmV39{a|)(SlTu}u@_h6!
+K)oi9UB1h~)%)l_ptX>#zAbV;FZT=qj5_{FlQ{NCUd($6n<-_+s|v9TbVBygoD#<Q$F9OvBj_mLaN&_
+?c!37f95i<wHaDJgQFFhS#)xJZ&Aow04v>Y}@~}y?$-`#pudMx6qy0XE*`^r^>eqYWKXQy-c-wkGFZk
+uRG?-BH>F3$$N`C^MIdQdepvo?bbA9x4z}`oa^!;m4hn#x_F2`jt(s!_de@j!xlB&sQpL(ectO~htGT
+_x{=Wt474Sg6J`XQ7n*Ys+r{qxLHWX%;vYQ7{}Z*DGB?G|%c5rF(>5FPW;-Ii2y={)Vi!A6;)z5~Vj=
+$Dps1Lcazs2NE%I%kruq%==R&HRt`<IE4pEf4HL<Q703Vb3o>4_}81Lj&byhd`Z^a(up^$)^>pg~<Ii
+x1-Fl;hM8(1DUB_w{sqRx9-On+(VE)dm)F(rS?LIkxt0_MR@0Jc-z@kzK3#X2u78fUi#rgR9rwSnoMi
+4CG?hjBf>!!vWU~rA0SO>@8y_qKOdY#!vkms6a;X!^;J}npC|)0Hy>I^Kbm7e@$_zlqRX!}BC@CtBZ4
+M+B{taADQ0@<A_nsp7{US1t0pl6gyJF963nh&=Jf@zEp*(MVBLCp%B5Z@!%Mrqf-dfZhk6z9oFqKSQf
+f|km(d&9f>w;&MTzGGfXmIj9K!zr;opLXF+8Be(#wDnW3(MmPM|u;#;5h<EEud^vp5h+xyAS_D8lEI$
+@nZQ#Aii5JR7$cxY;Xr4tq^;u$RrwUNd?2y3z`-U;WrN7hc4}?V8Qr)?KCWhS^}(JPb2yId0p;G-eaG
+6BBpMhEUvA1kWrh7m3@74Skv|-YB7CkYy*&KGCGs3R-Xo)&UT7>oRCjJ=!OmaYgGLX4!4R1;`2nfO!D
+B9$^Alw!V4^L?*%uW-$kp8rvC}imNyItAGMBnFLicT%FCCq;nUh1g%~=7fYz$^+X<9eV-b%`jj$Eqth
+-(umvYBUj=jh3s|md+lkSN-7H6oP!Bxw@Rx3G^Jl@~RRB>05PotnnzRvX2&V;dR{nHa^wSJt%|tzf2T
+}peNir+fK=y^LXIdb`<~LV6BdcPxaW==bRG?)Aj6loE(5}L|XeJWbh;(7?bM-Y%ZyjC-b#I(V-Yhv3J
+!6xjj%Ad9NNQbb=fJ?OSF`2LB4D#N=$28|w4BUu9xq26Ub-p=<(R#-T89S^kjjxFubyQj2WvUZRnsb1
+g@vtm*tM;iRuYAVvV!Jiv+R1__EC9jcr5Zve)BjZ4?{BdTfjl>-I<>Q7U|JiXeg=xN?pHFmsINdR12<
+3$@7auTsxAJcZ#KQvPqmFCpU_h&~3J~w7cAff<JL54ET#CU<5VSd0eg;GO1ZFW|>e_4w?1!K4;egf1~
+T5(Dp7whwtC|C*1T3bqQ^gAuQ~R$jMsa$3H_eVyjGxgth^k%xI2yYqhvYriJ2_GR+n*1^Yk<?KI*1oD
+@Qb0VeKABALstI}$N?=X{iS3H6DXTL}G*#k&(vGq64cOFYUF%|hs5HmMBBe;<ZfP=4K<6NmOET9`$e<
+Rmd&ZjpMgOm7o!mua5psfK<4qzYu3A%rFXz+epb5+zU0U)9B^T}!UkyN0w^P;B&Le)Sp_J2R_pC>1l!
+$TOz(b)*AT*w2*yW1`jBrJ8e&rPoJ=&<Um^_i;iS+Wo<z)%v)U&li(Hi!X2j*D9W!XQ(ei2p8z(&WsT
+t7{LljN@uYJdLf68{>^1%k#gkDY(N9%eRV;Q5A?C**yv;jCYw2RdN4C!J&4Iqq?0Gfoq3pS;zrSnQ^U
+%}(mO3_BC#T2Ob+1YCnyzR4=!@^Uh2D^?GG3WQAJ=BXtql80%iqQ+ewXG*<@AbDw}v^(`2RE!3?gTBh
+Zagoezq19%oB+Ae^jBac>wxE1<{4q>2!J4W%D)blAG5yii<0IO0Zisro7Gc4<Ml>b#tEY#bY|0+!-3n
+MEwL?MUU)=IrsY?ScAw7ADp`18z_z>|P&zh(%YaKfzUzUiEvyxwHW15t7TKmG!?|F>z_&alt4Gvg*vY
+ohkAxP8tUl&|jW{f^-o#d5=0N4Tu6k&pK}p)S#@kyUvN@TkmF)15|fqHLpxlR!^31aik_Fnu8P_r>$m
+OAYt8-pOIE0K&<SlhQ2jeU!S!`E$ov9P@}+d&8(%1(9)B4<0QicmxS4BZxGa_U6<nSB}VJhNjOw`URT
+xFl%j-sa5*`A0-ui@!{=k~;B#*`K3_hJi{RTINwQb?Bzrw}h`mCuve)hid;RVuc>U_fp}zAt>srG#Rw
+ax}=W$ja&Ui|QV%m*EO1Kw4fKtMU7pZYT48!GQdXfurB&pAAVTQI>pWAT3Fa#>6(deaD;qcWAt#-b#f
+_{|HG&HY3Z8~rX%y_<_nf_T{F!Eh6`x%RnS{Zh&R<K-O{j@Y_Jr|phY&ppp@!~%k*UiH8$T+)P);Dls
+6UQx(&s^E`MY#H(@|iIk^Mbj{VZf|!?D!IewyhGkY4hq`hbBd4MCAzI5Tk`zOzFzlpF9n-FvP{EEfqD
+SM;VJ;!1k!d)bPs`o<4{Q8tJ1i!CNU^m0H`ue}RXuc5`dAGTiJVmBkKaZLuQ^zmlF>pPy08HJ%~opS{
+Xm6s#u@p$7dao@Ch5w`LdSi;<eXHA*gi)~^P>CI4txma4*-dJ5Tc3t7)Q%dBh~&*=1MoFOB_4>;uHu$
+W7hS}kum?Q>}j1=N??Nfob$D=_Tg2i(?1T@J(0hwG?PJ49B%L`IdGlk&M?adr4c;Qke4$+rA(<u-@n=
+_6n|CPQ6fK6#H~RZyuZ6xrn7x|n#9<QUW{z1h;NabhvaxDbWcKGjk1F%+(+f5Nb*UvFIumu}b^KXh<w
+`S;fUphvn~u6R5T*PV+3_@;kn`}eq#Bak{!sczK@FkXHY>{L6%&>beGACxb*i(><Nr@T!L(SOsUNlg-
+^;zd}^xd^LbhE*S_=AnUCKu+8*)yiw_Lfb7G?uoEsU#ANoDKP<w5_c(YD|Wz8=aVJHg>|=jmn{qIxh7
+2msqN&-Vt$P>)1Zn{vYZF#+0F@Mon0Ot7F|%r9Qo|9Xmx$L;i~YQ4dIo~I!F#+EI(l7q{9Idwqg-aZ~
+ZREy?|qkfi)0N_pJFbnm)K52b~#%Vy%M<tRT9${Rk&Ip*|iO<UB;HKGA@B0)^T-ySC1ut;?tLb!)MK!
+9X(<^L|`)jMYIgJh@!^;biTHh1w5i(E<bTpp~oh`D@8%xNYY=>Kt%t+L0A#;*rnD7bFT|^AFddP6Mf3
+kgjQ;;7Y8|ALMQg_^w57i39Dp$iD)2-PacLw=To&WV+W_Z$8L#AUOQafmggB6XbbcS^Lp^n-aEu#le>
+{mrV;=sT?m%%?F~rN**rW{I)aVUcD+N7c)TIWBP(<wPeaK0LXU$<NRcPNE64&TdXEA*RQ?prz=2G$(M
+1sC)_$*^c85gCm7^vg8MsJ2q{^-+S?=PpR8JumOI*<KO=BTDa1`MoLkT0HnO%DcfpRAPr-#`sgNyb1g
+BCiCuMvVeS&i!D9GVC4B_&rqLswe9|Q|&*5w@AA~`1s^mZsLwT7&EgV}UBr_a^pQ>7Bt7yeW~NSF9&2
+Su}PW;&D7(5vPs^VLj0y$_QvUK98kK*pfd%eA!94h{2#^_I!=VV?EU8JNgS(=jv$OVO@NtLdQiIC`Mb
+HYkWL!YxMkCg{5HY)PhD&4fWg<1kh4Ua(T>RID^GZ4P$b!)Ql6Zdt&M%(G8GXxEeYcm^MPEDJ^{_awL
+f9T?AYuNB0=7xxU<EfNnh+SS=~@}4fcrG{VSimta|GTUs^%CHQ5YQb%zQpC0XEDjLr1<l9U7D3L|o5c
+bW3!3XoqT+P;@$iFhX%QVX<E|+H!ep0^%YnB6QyoAQcj7~>(>AUoZ_@_sqVPJNWT?wXOs0Z(E$F!7rP
+ZLx{rn7$2GiuOhfno|1Bla3e5m6OU_*D}L(M*bP2Gu)xThC+*Q+BAX0<aSH}{x8`{z)1?~yWt7P<r|s
+NB=bta)7caz@aF>Krd-LyJV4e9kIOBDdV3)V!lCz60W=`Q(;0N=<is@tW-@FCb<%$)(VpV9Jx+lkj0-
+!Sb3D%@at?3FuJbU#V1%;Wf_*^#h9Qi%LdnC<}}-rfD13)M|A-_38xZOwxP|mjFaQ$kBID&5N<L3(*=
+wJSm(qh^Jdwyv7+}d*dTSu>XQl-}Tak!N^EZD~ni0DGvhMI>lW>8}sa8+l2<5#bCyO7l<$rF<M#iS4e
+<Qq`ZbEX=R7fC6T@~z>^s4Tov1i{Euewc&}F0orcu9<y=hBb`~4yc`$6xp!uK63P@}KatAi?BI$iN2$
+M+u!PMR_uz$R2<#OnAxsm5kXl%(>wyf*ib_hCmQRlXsfeIGIs{)QQporztmDr(-vo-{@f;~_`YB1)be
+PgFC1W=lX&<~|*Ewb!R9<su>wqY&(5}2=k^zhLgfKCjm1w5emVsvi-F-9ky)1?8rtKiO@R)#GkN%|^g
+?PlGzG>{Lh0pkAgVzw||9~uy^_vw&Y+2K+B17lGb1j8pe%$$4*_G8~HwQ5R+n&qR%{*I(O05v@qLBZR
+(ifv|ZMP&=?!S%3zLl{hfZrp_3`11isWebRHNt9$&#ZU?G0bzhuv-ME39-5<v?$bl_^w6Dp$gPKJ^-!
+fA^5~&zJ@j2Yw6X$+2_r7U2<Wai&l;mawghmu3!3nfE{KIz4M$7_se9&cqol51AEigm>Tzru-?A3+&h
+XP-Ck&3O0*6b?Z`DeZ@V3i0a1s=9=jef(%Hb01PAr$c^OdF{IkVxr>kJz;__ot}{af>oCN|v2%%XG7q
+i4$Mn2(g9yFtJU0DkS=KqbC+y@~_(ZeT789MS`&EWn2JUAY1wI8ugeu?yJ%WPJq!a$>GrahQGJqEYcq
+<SbUiPUMET4Kp6T4ych)$1a_PkRf*&oFlYF+4el<@oMj)If)L&blOMI?lR0y${uE$_BxD#tWIqPvN~%
+T$g(=<GeG1pl;0y2NRNT=TqIDI^*yb7SBk4EQ}o<@X_}r(FQn)97%fjiTAn?0F)gFZUnYHzxsa_|ZR}
+Xp`gAt~f67Nd_YgpzgCXr*(&suYJcu5gWVqy9c(GMZTAQR=dbf*1AH&7gde^b_)%!WH&f&vMVwRXI)+
+Bh*DRRT5hP&AK@FEAq&S$ZA7_s8jQO)cP&)IKGu&i>4Cl##w-a`jBt517x7emIwqpzaA9}6~{HCRLs7
+hyRl1V{2SU|>jFuy8r3S(l*~FX}vxBSU7K2Fkqa1JI_7t(c*2*@~GCfH7M6K}yym>^=Y(JchoZ_0=fV
+efxVpbaMyr;r~WYk=0B_?57At{|6{PsG$31eU;KRlpo<YK*-vxrcYL{O0WDGEBhu~OaUYo%(FyW=duO
+;Q_iEtl74leu;U;(INIDhoK{S#=UEO3`xfNa`h<Ny%y1s{hbwv9-JV36?WEf7I@6Q|TEFFe*SV(bg>q
+z;>)j^Gy8VLU^zwqkfe3V~ZcFY&?cOV&<VOt1Bp8rsYf)k96@KXp_g64g9Sx=10Iw_A#)O{YWhp$QTu
+<D^Q95m~TkbKXR_I6BoVroQ7W$n>TXDN1!dgB`m2*bEAp_!$3B1ex=bqO<f@Qnq<oKq~S(jS>-20k)W
+CaDq-)PrJ+==p0+*~I~=ka>wWh+$UIw!QP1wc@DZkoro$3Y3o)FaC0>0THhb~q>o5<Ul9T8FVKgtqGd
+C@}djbYebOG_M>32KLikuJmFbo?{81KjVRH>jKVK+qi3N#sb58^2KvzwgxaQaPoCGSL>tqr1FXO)FQ-
+`QyNTD--QG>U^Q>U6+U_e1j1I&4ir>}O5O}wj+_bfLIEZ3*LtZtwOS{=95?}y2M{fvpFUfjS`OT;?Zq
+ZT+^^GdKM6RBOh5;a!A~d8)g9bc)#AUz{kWXbuNBN?`ikqc2xgEbE^tOV?2FsC+xTJhnw-2?<lPl$sy
+ETva0n~#hC`U<ZErY7=YJPBCyfZ;_K5CFzj_s}<6LqK*Kwey-@0D!da(ck2d~$=eVWnj()a-OIGs6+-
+un18iB0sNb_H-{7PYTeaX+mKy8V&pE;NWpG|hLRE|91_&ZEl*zi@f%>jfJ4e8l2uhzWnFvHQpuAa_hv
+pac*1Y#iqc@}vE80F`Uy3Utlk#K}JT=~ue3a+G)+FkTsH<0+TaIH0nOxCtZD&TS`)YW>dRK`>P6!(b6
+K^qhXWydfVfww`~c)pi*^QTJzy-pvkN?RT9MC%8^a1@wgxu$4NPdJl5}0paILZnS`zp-Gd&9$eCjdrs
+mx6|f`}iFy^ON2}wRhhjdAUYRSldAe59yD5`+dfE%sF_6bwOJ|!<6ypqKBc6O!a7M}-dfQjd*M!F+GB
+vYZbHmRnp96d@AHnDFNrq2Dtd=x57=MM1;~ui)D3|QgSZ5^hp8y6TDicH$=mrjY@?<JAb1R@HAPm^+t
+8uP#8?GR87uEU#Pd@;{<Yi{D7vhr7V)`srHFvsHPoIJ>Wv=Qag?<BYhYr|TIbY)tdV<{S(4<S~RwK_$
+Dq)^Nb#f?j|D?_g2IQc3FkH}!g<AB6Fj;BL8if~mB%RSKNx6#P5c%lC>_mhe_v@d05Ngb2MdDP1J~23
+=S3~bU=@xV8mujju_3yRL<4>Bz@yaUelMbN`JAy3b<(^?BPpQJf&_Cdi8NLAmCi17y%Sf6{uk8gCB1N
+t~(M~U8-)>i*l#5=Y)0XNylj)<c*TCmsl^s1q{6bp`Q(t*9KU{56^2pM{lH`rbQmgCSmMMO8d!z9sDF
++j^Mm|&owIYe#PipM)8BLrdpBWa%lc?M?jU0)dwMBa}ELP@LqT2Q^A8>O6ZflX4A2iq2E~AfBa@>rAK
+yXM+GojXw8or%Z$AbajM_(DNZh5-8^5!8i%jhQ+JvC+`bFW^J@~*>m0`f&?l^r?odvtL>j3G3ndMz}g
+Isk1Td9GZh&W<IybBBRZtJg`H^vV)6B1x6iWt)2*631if)9E~Y3MMS2$_lXq?saCcxvSTip%RmtO?W8
+TZN<dSRSnFF`)?U|+GlA_IGE5G_@xUNcrq|>Ax$RIV81lLcCZ~U#;d@nz^ARi2HFi9vcN7!)>bpZ)*F
+Do(~J$I=pD_=KH%kr$jjM`mo-mMik%ynPdg64H}JYUoyd6I!Fat<soXOBEzCY|1ow~V|HSOSzlhm2nr
+3!_C7hz)$m}`)20Z+e5oW)Uc2b>j8|X`vG5c+d*%@G97|#^|Xrr=Z%kXG+f7r<N9lDJ&cApCDJ_XMp<
+mn85A5Am-Sb7n|8{XLk^rke@^q-$<TH-9@AN!NC&~}PLUKT>1;@d3d=-k45rsPBD&7(6a*qn=R!u@6&
+(3p~;Z79?>@IJaGley{KouSh|x(o_!*e}d5c@XzKtLzU-%Y5|uOv6vF)JLz<zlX(Q&|NNtKs0Idh3B!
+|bUFsYz?k*Ol$rIb4UX(^TZ<o@_n?%C8{eR3w{BtXLQ(-X<3cYYm_13Jw$v&-Z(5+_srjfR%RLS`;>Z
+Fq&}c<RnURTXGO1$&9~@q;cup#Hy&!!i(b>>TauFc&X<GIHg*SsrSBvK^HuJ8s7C^RX8a024!$RpNDL
+_g-(fMJi4ISa3X?}De(_2R}qn~7SEGe}PL`_mY{qrX*^V8|fYM}Kj^2)}gN<+WvZJ{j#3K@u+h0u9uU
+wMOFEbFK&EjCBGlkJ{i;XtH1Xss6~R7-i};9xh(U-@stN}Yo=*p-Gp^<VW^X#!oL*R<f1G$Hi<L(|mj
+$iX92sd2cXO%}!DKmw1BLp!P-vI8qjrNSpE#e!bKK6#@v?zN)fm-2`HY(C@?l*+tW$j-Or%_f57&4C5
+hY7!Iwwc|!x%b9GkiMds;)oL6q&BI)K4iUnOOvdI*XJwu(Tl5m4V@cjj1oklC8`^myxb++eYSuw#W=k
+^}=w%2xk%8u?Kmp96gI$&#<ia&{88pjFGPOAwTEQWv`v`5DEVz9`LtR4q58%^P;}Cwd2IDeKHT~hr4C
+?^bBDCRpBGhaa+R)4eJt(`yl`Yv?c&ln@DLO|Q`p^q6rZj*A>vtU!<Rcv073wg}n<%vX0Rr=KMSHtt(
+E{k>#7~$t*I(^Aw7xLBz=R%xIdaqtNu8E#Rf7Ikf1r6BfU}1(M@;QPtfg#yVF|vVcPR7@$s7PwtHu15
+W-VKMq0^E}GCOLt_9YoMb*8JKPs(5kkHa`=x^w8l3g&*sqOAbG2oNgwID`kQfb7%)lBu`djg2&`whj{
+@PhusWJ{$mOq!8*xfe1Z_i)}Y|<g|5d%2<@x#<WGZ@O+mlfIYr-98}(vceC((j=SXrFW8QpNIS*}&%Y
+)dP%s$kl5**De@A-4lhNwGE^r-^td?V{n+OAy*FckIE^5E+8k?LnZ_er%M}+p<@@6iQlNKozx@=L&F?
+H<Vu}6cZTk$9(4KSAk2bjwx4Sh!_&dar$)Cc<X$hj%>298K`Ql9hmxF#jnoW#v5P@{2~YHK&HTX{}`b
+P_^@ZG!bDWc^Jpadx?4pp9E1P>`}@e5u+AG?CLN|Mx_}cA=n;_wn)>AdB~rfzITLV;YqlIcAn4W*GUz
+UPd|na&*`gT|X7iE6L^AV5LQ@kQ&TRJcQFK5K$ZE_BhN2m6*ex<hN4E3PvS$DJq%OC^sjw#Xkmfmp3X
+Fq?9C53Vxh{kC`O1vohndZ1oDHoJ<`!#7JitJ2;b>a}GT|OD_cQPZ7{WB%lev(_)@=AOi^~&#Y!O5>5
+w$K>?AL_$gY!48Ndlm8%nGxZi)AmYmOjj2pv=AWoaf9|$2lN-N)Wl1r={W~*<gMFky!9$Y(l4Pe<e#4
+w2K=8tu8{VyMn7T3=sr5@kJyQVN%J(2nhFxN<xANBy&M@bCCbV@{@)_sdfrf{IL)C#7)*r|J`dmNJ@Z
+kSC4qnQ4llrUBI)zZp3!^sl6yVPnD4L!S-{`5@Bxm3M`zS5JfJKn>}LC?<Af{)!;@VVw=T<U4yYw7m7
+(e4z??{jmyUOKJE&|iq#F*51r!=#ViEqAixyW{B10Hq(!I`t9sTBlmgB<6U~ahk^X7@9m)ChB%_Jnb3
+(9kxGeXRt2<?D{-v!jh)fD?Ir^{bO+a^3*0ixvkiad#ItK;`hOnt}N!;BGUNQv#6*y<;fMBa?#MVCAR
+oLe<x?;v(~0-2H%%IwuttUBkh@T+$1MV(f~OW?cps^d^zCtn*29QG+`DqPCpF=XX|jEAske&ItEW`Y8
+qeS3wxe3d{Be#V9_cct;j_i5Lh-U@kkB=i16^kIDhS8-rQ^kcO6`RogA^r)#tRvxLf10m1+xoOyjaU-
+IqeZ*<O7$DPtbz5qH>ouC<dbEWX?HMy<cGyyg?3Est?Gbw!0X3w{<dmL0Oh4j?2RC;xD>nS5Bzh_Lyd
+SDt_uVn`pe8_SN%WE-Xxh1*$<Ob`Iq7&ysySh$uurnSg-I5{0Zcot;?a#zE23n*FM1f*O?nvTkGt6s(
+C->4aceL1zz@hwg0V!+mFSE9z{CpAMIS)hH0H3Lyz%7HHH*7txa$RH3ti32A9^;_oX%z&A}@bC<EaP3
+uAwsjh=vZuX@gf=t|v?^ZqB&~}z&yeff=7*fe$!RQtbsPE)XrNX?-5D)Qtu5*gJFBd%pjz011IyLy@F
+KQ|DM!BY8f!91S<}RYAx)g`PvhofR+KUyrUMB}ajRK47g*tw{~Au>Y`{m&MzDAWa@8XFoJH6kKx>4Jx
+mIu(S5|a+f_B<KT3hzf{AIeH3Yt*<2cx8kTH}W}WDKn7ofM{cnh&KJE(YU5mzP>Uz)1x%Ok&EP;Uucd
+lrWwgC^~O=cRK>y#~{hL4@OC{XHV#|<rgPLOR^moOS0_-*bmcSH2=i^Mv~RiUwOYxP7Mr?l2ezAlsxU
+kx5_EyL`qKeded^M);J|V4z+x2aUO3uW0u@cq2(@?gYn=e^GlK;ZOd%G*Qalud1+yVeuN?}36QTXa=#
+$=oX1lFnaq@aU0V$qQFT``R*~36<_(x8=rdBUUhSis^-Xt?9jcf%2xc~_9{E1{;#s`^qU%;%MmSbKDT
+3pLV~`&x!ZFx<JiWPnB50zQ_vkx<7r6Gx+$h(+x#B9qG1ojFG$Zs5nxM|33zfQ|l=B^_whu&Q@#^wAU
+K-=2Hz8)o3R5-c-wEZ-yp#`$y(oC;)kczJ$?}|lS}Cg7Rcf14BT~<ZTi`>il2~d8#v*Mr^ClPu(Ly{Y
+==r~*SAvQD>_fI8X<~;OMxR*l(eExbj4<y`MwG)QDQlnwRSFZRqQhos@_@S%jEJC_+LxkwPm<JvdFow
+pTqYdEWf-r1Kf#EmL6Xp$nvd4j7zWP;G+*BdRK@{6+x?WjZ}0}&I$<nOjop9#{ZTBi=Y5^FA9;T?3$$
+I#0*V24ZyJn_dH;XM0*}^Q!~#J4e=)XRRDn(&srVa6{@>nDk^C1mUq2TkP6capBG|^XtW(ok8|{Po^$
+1Fa0(Ntib(D`b;acZ1pRFt8O%97xOx*buMYf+!5DO|U#aCX%bbMu2aPCL(C`SAPED4uz0+W4i!*SjjN
+jcidAM1O+Lff@C4J}<s>Ua|MmpepIBJJT9a2eJWZ#vZ)Wna<tR{d->?$;78A37>!IwOg%u+?X6l3l)o
+M~yzw%OXtr*<ey*uO+MQP?p|7s;%-FUI?L%6)dhNTUN?z%6U$X+FQ?H`J4YqII?9;1*okNDJx#J2DwM
+wQt@yqj2n3By%j%6g>ih<RTV!=h4J{Hy0jvk3gcRWI=|w_sW4g`>Qxm#O@%u!Jhfu4*$9U*oLBK2hBG
+0I`m?rSO_0}))+<}qfMkINF2lg8J65n&lLlkstGwbE9$>N2!Kt`q`>L^lcOdzWo9oD!SF{my8}TY2=;
+C?Pr7UMxNE3EM$r<7Kh?nlTML&FPs;W}o2GGo^jttO*zG6G{GAkHMerzfp3@XDTC{Q7u%S94L`*2=`X
+j<%}Cjg*6EM}Nk_RoUW#eVt&b`?Q=y;9MK>wcQ}x?V!9>h#g4Aw$djiu&+!lQ=zSs--vJaiw|%H}`Jv
+)q3gJTT;6ti$PVeoNk%VNryC60PTnYE6v1DPW}!Tw;cvFP0XUJU$J^sOe@0*=^6%wpKLztP3`I2_?Oh
+7<z-;{);c3TC6jzf>g;heeDF}4n1#&k;jPLz(2QE<PnU3!&NKAvbYVxN3KwbTX_=tm@f{iMW{{!wj;X
+@)F>e+1=qteDI4u)TqKnx;fE^i{$KGzLf(oFzw&UWp?4aLME$50vTg$mD>DN$_wR54yA%{aVg|-Bsfg
+>kc=VeJ5RqE-b>fc#Hw=P3DK+vNj=+I2g<BQNIzG^|s{FUMv#P%{=t(nHom)gTsV7O10tfWuPMSneL1
+~dZNa3le?;Mf~k<>r+Op+Q~J7uXgUL9veE$O8&D&&v`QhmMOC!hwhvfM?J<YV=0tc4R>F^u}qKBP~^P
+X@vn|fdojZvsWd2Ad?__W~Z6M9>WZdL>@ac#IIU`upk^Y)2IWR&f`9R`4oe;CrwUm<nh_Kk=H)Oo`ag
+vG4ugPl2^(&pa)k>%!IbhEtD*ZG(_r#(A=!84LA4GY<AC45@@OdmwDIUgx2dBZT&)$jDBhkTLo9N={h
+fMGBL*h*J<%`BfDkT@52JR&Zi2f-qkyd9u9OE1)6Hn7cobn?$Zi-aiTfq9B_TeOq-33<xbH7wsT{7sg
+yL{eST8WgN}<nUrEe&B<c3n`g~x7Yww{N-YL%Y(!Zmofagv-#VdSZ`Xs%Y)DKYFdw7WqdIK~^a3YX~Y
+PEtBSSL_tmhqk5?>Ek|;6AK;SCSE0t^bv!pSWLq5%2qh$uc(-6SD@pcj4(L^)4T_TPx^i&Pzj1DXA6o
+VR?PJF9g0%vNzt#p%qY8Z@<&i=j?M0N|^_4fs$26=(J=fow;Kca~#-5IeiLqVjg@~DZ^UWGPgW)Gg?~
+4oZxp3pdB|eg1f`@Ax-*Dxy=E?ykQ!2XcC>%w^3aD(j&3kNgwJVujvYEx)7%IvQvP6$C6Nt{IAQ3T~w
+A)(%){<g&7nyyWC?J|Cgmj)2ed-^@cwmfhqC0(tzg<q<3-L8is<F)k<@Zs{)m_=G(|1Q=rNx?-4zz!x
+W%${if5<#w?(X$y)yASr?v}_(kd{1fG|;=fJ4*5{uQH7kg*49nlr9iGNA!Tk^>{z;6Q{M~jd2DGevu8
+-|sJWZZLDcQ5T0vy_H+Iz5_0&5nECK{46hpoKjry!6v0`k9c>hZ%TnBpA>kk3$+e=y7oFOb#!|I3ZeF
+YP1&OamYQElGqE7Ir1lXw1|tU#aY#25xut?7klMAjV|j>S^i(Wg_eKfL$-|6uTBEoKrUmk929#}cO`e
+L7NxGAc=Ce|bn5&r{H}oC6!<x0>Xvqw*Yz`LcT$}i_ngFKR<!^=a9LKh!v{NSWKkMGzj6EtkGIS?_yV
+rX`(MEc#TDJ^6lZ}<DFzO?qyiW4#oH?IqD<+^xQDV*Zi5fJNKw5RK3D|{D>xef&Z*#_M<DA#@p8KTty
+Bx|dn?t#ajb=mL*pL(U~49PFn=+RBkt+L#fVT`98Yq4K+p^xnNd^N*0m|qIHmQZTO8Y3gLf}lTWk99e
+34vp()tu`Cy17;=DsJ3#4J6XS96|ph?#o8LB~|$i7idZaK)rc>6dRZwN#l(|MDiPk$4FYeGNVdm$*Y0
+&!l3A9TCx_zBrp_HHn5jhd~mZ1Yv{>0`jT{N?Qja6+JE@GY+{9NhL63CUIu#UH!Nk#j-;#$out--zC?
+5oAEiETKru3Ci=paGR#0cCcwIaB70Icw^aE*mVwvijDM5evD0JN1Ow9V=m&3LyV1o(eOx!#YqYe%UZe
+2{eI4BmY6Qt3-Ez;gkyHJ+=~LeU#NB0TUt+d?Egh1>mFD7>qL%iW^DTFsuWql=q^$$d7NPA)Gakxyn8
+kHLb1jsAd7mMwT<4?-bS2=_Iu~_N(F=+6OHE5?Y<2M%hb!&JF!r;5F#8wUKCw}5J~mMrN8*p(iYXsg>
+Go+%JjgaYuA{G=Vz3aiXTYOD{j;F{SCB<tK$A1n>;O~Nb7DqZvSrmcRIuzEK!h{muBcd9fy5<V4thPj
+rdQxeHx@GMAuGppek&(U?eq+r)Iylq_v0}~Mg`drXI8E7<P7E&+MdA+#x%gg4Hm2rdQ4xgW`bcxv;z5
+B_X(_EQsRfMs1Ul}Shb1^+wR39lS%W2JUMCIP$DO7LTCjZk38TRc52l^+f)ct3!%j(+}`cghC$&sC&r
+>Tk=gHut~gc9)`fEpiiiCy;=^KN$J`YY+8%;l4qC#VUhK#AUZdB9whe5N*DMBsE`$(XpW52od+NnkIB
+7YyI-y@9$Pj#k?I)UrZQn;vtfW==kzc<$5-a;*s&=6bO>7;5m3riigHTflHUC5=(^h(C;$;{W!|;$M0
+$fiY^1$rZ4H2S9$;4`CSOqpxn&c0f5$2z1oDYJ=!n%JxtEE#<QU4Eg3t}F$tRS(HxHnu`!CJd;w!VIf
+TcJOKtd^wfqpcr+r~tag!@R^EW}F2Sj&yA*?%=Pj8E*M_YbpP25V*JGh$bA7k272Twl({;mge(ZJ1<?
+zr1>Pwg|O+2dM;}VUvz;a*}^2rrZn58WJ%D1q6A21CQ2A~e^3F6Dv&7YJ(HQQY1+5e1Crr7x&+9qKCu
+&T#Ji5XIjR**f+*HJQ2G*eXElvEWYO#{Fdu^Xfp(nG`@4*F7`*DB(75PK8b_6`l$5`+i>+YSgX{+zqM
+0|YLMv~cECR#j&)hn!yG6Ri*Oca=(cUgdgQSzLOd9v9^o(}uCLlNCpfkgavbT%5cSHT<&AsBR^5!1BX
+Yu$o8wjI4BQ2&C>4gAx57g*$c=S?jw2uv_jm}7|A9qaJ@FX!9Mxo=<MEc~R^qT(7@X0nD*coExcOZTQ
+s%n0?rjPFXfE~4xH|2BEf|8GfeO8y8T+oy&CvV@#gYF{i>r>67PuQ1<(K#5M!J;E_+^p3&#LC9dfOtc
+rt51xnb5Y$C_F262-z)Jvbe|s1^wNha83W<=6w3zr#ca%J!<>hNeel1tB-#R2%_<R0kEDF(y7MD`78O
+fCZR32>{b=SvOPRMWTdcc;9*Cz~dS~2dsro%Icffr5TgeyDqP&Y*)D11_7WSFG)}knD5#V(c@#+?146
+lPFQFUC98}V|I*}A49z2}dYf9d|JXhQN>MgbI$mt*EOy37&}1<MD`>L(B>?8`2Rva3#Oef~NW{sDS7y
+PKEJuI0Ffln>{{=f|#PN8750oQG(}9(I1rLA<kWCEns&X+GOm;lFl#3=hRs7CY*^!QnvP-4~yQ!+S-o
+y6-AL&BC*AcnVGvua`F$&(_2YmsmV|!<FGV8;Jc`Wl6CekHKwz7N(I)^mA~P&+_y}&iGi(cC8R?!yDB
+&2<MEfAuQZ_zZG$i9AufD+-fgh#!EKVv{>!37at!0t0u?zAXwMUyt2ru++(j>=3QRjP`~1!mD9Ro^pC
+GIFLFLOo<0Yk^#e9g!8x-yCTI!(+qr`l`f-d&QTY0=KY}FC|3QEI(ccK<9ckuClpL{ix`sFKuB7nb<A
+6o$8Md=DIkmGi&hL{y735C@*YK*<k}YxXLXI1Zv#Y@JqY1P8H;a6RZ@T>SZmsx{Jwd=Z{wGY76V@g>X
+wjBKT5;&r@58tjha-^uFuW`A{SL%&@KFrE8Am!Y+A}+<Ozn$J9aUy1wtcZ#?zG5pR`}s`;o%!cU>TH2
+?nFL9sNbfc9@b&xPK3dYEl6!2UB-XFYX|(c!><i~o8c$IZ!P@RU>hnQ>QyR#+zW3CZwl{|@IDFeK6v-
+RyC2^D@J_-z3GX3z4~4C`HpW@{EXh=FXxQjO@OuY-6k|O6s?}YwhY`ck-+Jqf7{Kv@9)a#i!G^_ahQg
+c6hm0{O9vOq&^QU(YLA^`XV!ea!Lr)Lg!`hn~lQa65l;D_n?_pztzz^R6-)-=F27Vn?mKjId7hB}dS?
+`@<z4ymaMI&wo%*a@M4v=gV+DAb?JPHngM#k)Oi+l=E79Pa4mJ7?~;Kjqw0lz}{6~S*7{7T`sXe}F?$
+<Wu6;av#tLU`xHJ0IQ-cst;2hqoQxJiK{$Tj6cJaBTEGS_#iJ@Dt(JzIN2uR9OJ)QDecv7mmZ|xC_Q^
+WJ6gTD^tF8?gt;B|CE<*`2(<NLwX~p49`p9RbPJXUGgJTB5`TSi-G;41J8{PL`DMP)!w*=N4ePgKr#Z
+nFJ=*oktRPdgiCshtf_F!q1Esb6z~qf+ptK89dTzvH1L`_RO1n~m)IGR|4V3r+{a7LM{Rg*!>v%~aX;
+7u_f;$h3sVe`ZHO8~tST>*ZYSIJ>!C{^G|o%M{T}G*dCWzREo4VWwmo+Nz?Z*E11x0#+Vkva&tFI4-}
+)Up5W6j+XRL&bZnEuF2wgdhh|=E!0NIAB#w#c?K|Q6vWKrElD9j4O{Pe-!v5Asw!-iHI!fu&V{tLtjv
+AJzN7>Lhf60K*ohPR|UajV{mV@M_-kpoBoCCx#g^3_A)ShCnU5<LZ@<v8opxR2&x@PaW$eaEGf<{Td>
+bq$KM{K{M!L^n@78RDa#;}sC|xq3-x9cD+qGJSMC?%$C3Dz>%6juk(N3gxa>K*9U?5N?g^WbU$6>%{5
+umbIKXnZAb6>(;X4az*s+XV`H$`ur=nnq#LkUrikwEJJ@>xpS?L*0U>GM;*{%OBM$JdTI=FOTDoxp8z
+2SRZy#ViA-}vd$nkBMq>N%&i>f`vFwRAhs5@ev9g6anDqPD1dUhkPxgE1o)2-K2UHmnRkm1_ExfYD9{
+t#n6Af>1JpJ_3FW@0Lm@omj<jq$wV@JNJ-Qm7P8Vqlnf-AZli{l|~sWg~6tkZgOvh9MSBmQuR;-;v!!
+KxK-Q8&kyXmCq^h%NoMx3OFJ-L803e@EzZ0PPH6L{VErEDb+)3L=E(4++m(T!%J}FNwOn;T^e{9xS?*
+{__Jir*|wefd<(Yk=_M$Yy<vn0(5TNVD&=X2<m0GqJF*z*`^laAr?l~pHB8erdqmlA!;X;2Efp0fB?H
+P4uLi^pknN|?H;TKT^#aSmSjwEbZoa_vFaGyZY{v5B8-B9+P7z5hpCzEzhWQQ)>qV9^=^!ZdZCzK<Ds
+3d%h@-11fSuyJWRH9hi@;E&rR5v-LfQaiX*(NNX?bcPS|M2U{QEkPdixsogBBo^;T17>W+_dyV=<gd*
+Be#X6DO4L7q-rr_gD0@j7veycKBk60$L1kY)E%I$2J88Obu2J`_!zpWGNwq+VAU^9R2U7-OPu)KSV|{
+V1i6{vPx1L?C5xfG}vt=DYA9EL$vl2*?*IxGgej71L1*BZGFw4M#LhnNo-sp$`vBQ{y)LWNTLiJkZYE
+h9S{om;vfMdHB3Eb~K0;K-A;%@Q^eX?}>0*M-tUnF>fYr1l+OiJn0-+UoX1grWiZh!lrBVlTHIlGw&L
+f@<@#SiQOcg!26k?VPJ6Vcb~_*jBz&&7k4A26}V2<^l7U`&*K><5%otc!M+3R9;moiE3ij(e-<C!6y>
+2ndyFYC>brO-3K=cDXtK{s9q21?bRl|5JYZGU+so^yunl>prTKFx@@@DEOQ$;W+PZ``AOKEyz6v;{EA
+bR}K`~u_#nWTGP|Q7DrHa%O{x7dSHfjmX)WBqy3(wCNo{y-|Wj!4&^B|G0bWMTMOp9H~4WHp3k?1tzH
+eNRjoaX6~J9)Xe#|kAhB}g1_i!VZ_3yyvtk5XqSi|pY=j<8Y*1>N3Au4z<C(awpOZ<4+sr`Z|I#B)Fm
+ElVs@tZr9y!vGe)MdO4}DRg>x(bN}kMrq-B_l?pSm>%gE$O5G*wY+Lk*7JtU(nZw+D5})|C@;ft7DBf
+TGe5QTA*nBa!x$kyK#>uKUx3t_9*xe8;pi&OCn#Q;Lskc1%;^vDCLa&)T4WX4{5TXv!1Q$Dr^pw_LdS
+)+Du{dmub~2#Am>i+mS?cPdqAH%uS@G5TuvWiCg=%v2HfD1F{CO`q4SBCZu>26AFK-0l8Qa-xaUCCI<
+pY^46+T_kULF62vj6IP@Z!j#e$2hyJkWA$Rd8%Z1%SAD#g3aA}~eBNn5j{kt|u{XjJT;$q?;UDNCt9B
+52O-SW-B-y}k6D8xj(F0&+w%2&_VsFzxMghcN9Ejd5)n?Qx8o(}=&Z8xfy^akbie7fej0fNba`WM_3I
+?w%C2p3`&H(w}24dhPzS2cuQKs|Qy>=<^Vl-l`rOJWqW%v&L=5(wo95Gn&W46sPv3&H111Gn^4GxVG9
+6W!HR|X$DrAy3ZNy<tV$rxwyk5N0Hu_>Jic@b}>u%iQ-W&V)_%io4H%=)0_w8sP!p!H8Uj(h9Kv)ZM|
+YKu$KmgAaa=|n$<VL3pA{yBpM4Z7*2nkPk#*=Uxu_P9+5Wq*GqtEh{x{p!>G_N59n^Fa>X26)=)fDY7
+Xo<hC}izwkwH5=s**@f}IV2;usqW6dmlK1v`EXDq~;=ChWpOwD&OF!oqXY;WaEgFBRTZnZy3<iX*T@F
+A?M15VsQ_q=xFpq+L;Qad|GniZ^)iurz*>Bwno!n$+yjad8$RBTfj{oLCmLgEU_jG<mhR@tog)Cux$h
+T5BBm=c-iaPQ=CKq2lH2B)K?U>sCA`G4IZKMv9=P@VMuIiCP(J55_szl1ncxD`FyQVrhmq{aU}>D&>n
+ba-^n!KC?HqxaZxgZ{dJ4h{q1mBc)NvP(L8=Hp<s?VupOse2df%t@^a&2(H*D)@+brxNu{+Mfx27N`r
+I|GSJ)ij<~7*r*7)h;Z@uDKK*>pQOQwW$IkaK%@g<0b!5iTX=!9)&#{%p4<HA_!>o3X!pf|^Lk`O4G^
+vn$iK|wKOrC+vcK{i6I{BjHOJScqqfy~;e;Q9w-XdCVmQ-|p#1lMd%!KY|r-HE{2dS|Ky^TucO~cC*I
+L|>P^x@~yElC75<N8nb=Ms!UrLRgn{E=jJ!?$$qEzhOebNbnI=i(tx$%b9~5qY<y8~@q@-gc>DneNJW
+uq%ZT1k{&Cj-PItDxWcLvCC&HQvOc7EVd!WUP`VWpzl4KmJ9#-tRWW$7TDb|i1+ir_1cZrO*v#tyIC{
+gBxe^6M3#A}7&p#xNtUdPn3iE2OdMEbcl$^mL0<$hSAGB_4`>W_Q-(TU{to*i1zSp?Q#v@U#!g<RJJ8
+kB!^`tEDNnxP3P~WZ@410)(b(PJ+}6L~1ravl8B#J+$MRZFwwN!vn$E8Ol?O0La3wb!erD9Qa=q+tm^
+{gzee8LNJt=!0X3r<s6EB&gW-v5p=N~crD+q&TS3>vwG2K76!)MFO`ymDG&Ja#ps0*P>(49H#1eIG_<
+)cYZ!Rr5nhoNxedJ=lnjwgrYkEfBo=&&vNafZddmz`lWlh+0o6mOM&iEtATE<d$i&rUjq4XA?y?&209
+^h2b;0oR8@+uiWhF;3abZqwV#ZqwWQ5_>)l&-O=7!L#kC(Duh{j$5QWhBxPxGhku6g0}!K?3uTGqaYk
+OC_LX%^giI(BQPNl#zO9iWC_o=@otC@@=<DCrGzlr)hV={LbD;W-W3&oR0QQ{#i3Vzgx(f%7&{btZTq
+MmJv6G1`_g@!_%sW*tPaYwNUWBrMVwR8DHc~(i}?@`LS2xWwHLt?2jEi<<EJ(8$Mm-;@mu(AW#4EgCw
+`tzGA9Upx17B~nk~nz`rwnTL(0pD39zuTcBI5XcDZ2Uc8DPi(|aP>!hzSqm3+63dEy2H-i__z5>BJs%
+PuUJ8s+A*a#<H7cvd34s6-4W{>fu0H;Q#FX9Hqo%h|PJVawT#Lg+7iszK;hp6Em{*IH@(A|n!HzCMPD
+r}<O^9Eo42zl#z-gYOQ@yz8W?Ef1LHNQGd`8F^dMdG7<5c}@m#tq!ppA@Ckp1EK~Y)(CALe1BhPtHAe
+tLR%TWC86y~d~XukX5xFf&^84z*p0LSFC1Gg975*v3eO+((kly?e?&(JiLqXsPQu8NVMd1Zm!Oe$SPa
+r^gZcLnR3k@2+>dJRi<vsDB-aQ(-iH=ITb&uV7;A+eqa%g5M5YTw2M`Yu5MNy_gq{SQq~%~yoKYfk*I
+>+$sD*rkj0{TwYaxVx?s8P0|Ck${EM<=*5jL*!8BMaWzl;DBpaVMa2*3y};PsY5+c_)#fd(mO=N$BtV
+s~B=_KY*)XP3-&S@G^#=)cLJMFHMhOQJ&Cv$%H)_@he&VWoaUMPjM2Z}E_BOdEf6DjY)Y_tH<xjN8h^
+X>rDX%w!wav3RIGq+gGD%!t%8>bNED$LccER1MkZk!mZ<EoFM$*aN~VoDxQ8G5=Bg!%L%@G6$QY4}D@
+5{;k8(bhEuQidPjRaPdS=t`mNIof*e(Ivc;cgzfl;59Q?D!b8upP2=y2o8;uZ;uUi8J|XmXNE=}Fx9E
+ML%Q$hZF)UDqdWRgs6f0pbvTlfO7^AO>+=Pn?EO(J;ZzLfkC!9ff{Zq*p>7c}XTe`bh(Qdt;vQZmk5r
+a`=A))O9TuB%vi<87Zl*lq)luKr!fD5xf;>GT_Rvu&borZn^&t`j^2>@JxQ-ji&ma}(D-_xVmDEZ6T_
+m&J@PPDSZaQ|pa1=-7DIvY%QE7Du<1J^Kmu2({j>*M7xZdTokHIZX#wq7u1Cz-n5DaUyw)Pl*2hm=qo
+@|e!2+Fs-mU@(nUqtgJy{(^yEj#{r)=KhM^Nz_eV#<t3@PHz1a1+O^8Xtk7Y6j!U2Eiqv0>sww%Jnxn
+Y@n*RZfa(zd*Ok&3V?a%8n!<L}CSGN&Vfch`;#RMa>3X3}1OWjI(zXH(RGcXem{NlfXA?Xgf-;CkA4L
+31svx`ozJmc+mvx5l%GuKP&|g?CoSM-5N{kt11MNV;aLF)Zy@6<vSg3c%KooQ#;g!WhsukJqQFR>jBS
+u;tg-P@12+*X^(XE$kwuU0>#|$2mk8*>59ciAyP7edlXUQ?i?9`0jOA!a7go!ef$%FbInYe%|NGvBVW
+n9XT4oj!?DMn+$DHPe7fyv^Pgf;xpTfP2TYR1107A;jC(+Pu|mCuxK$*{0KZO>b(z8v<PF6pc?5JQp;
+>52qn_3trn^EX?{OU30H<9@=Pou*Q^>b|j2o(t9~=%Jw3ep$jO@JLdf#YA7}MWRp5ys-tKZEmIm>(t7
+sl3zwXgg(dZN}b)X8_QrQ@C-_pK3rF-6LE>q2$iUITpb*vhl=z;WS(Pe)-GWMml#^dFIb6oX(makQ%2
+7&dx~zMg<l~C7}7**NZb06wik1emr?U0k1=3Y3CLJBBTf7&C0F?Rdhsg#(+Y*ZjP<A!^rAt7Exm=l*n
+eT+f0tioQc@ovQ3^%2{E+O`yW(Nw!F?H87(cwhjIgI4s%*!A+-at{rG{jet}AD;;#Jz-_KPuPc^I1OS
+=Cq;S6?svR-GZUOP4iuqMiZxyN9)nL3`LAiO^ByHeYKrO1#`?)Yp3LZ>KNnHTh)}L}1TrO!uUpf=(~*
+W^wg|P469+cBTWg;nlW`S)2nM-HA1<|C#mxmIC|86hh(tZ&a~$(VaF@S3ZnJYWPNO@m4oi+3e;v!0#y
+hrfzX_SHN!_{C>05t>2-sY#FW)fRUhhl3-E!ePB1xzu+m#x3qd!TpB~){%z_Y35k>U)SH+O$Ivq7b<B
+>Cu|GB|jY}Qa6|=nb&$teyzV+>A$Dp!0z?ZF_@qcr3Gy*x${}BIZS`WMXe^{DKgTK{}$J{c4LyWwg*v
+a<5Ht{Z-ag1i60r#CW9DQv!4ABwh@DiTKj0MtX9HRl{e#tdf`D2sV>7}<$yyzIs<O~0Rk?wY(cupvFJ
+?aGd=iTWVaLo#xBpJ#V9<Vt<>U!w4fDHSiD-TUfd)Dmlg>M?Wa<rLd41nJW^gibwIMvK$&r*1nH#^vg
+mfvUKxff{~UA`Ny-JtV#rwy-<pU`h=AQ`EHOT)F{$@;+|i~dI)I+n~WHg|QOQ64LXGTXy9z_aCyS+up
+BF-hMcHIBg}csO7RJ^O^-j~_jeF8)FIOiY2Y2P)@Ui$nqcU=Ut|cYw`z4j-MLy8r!AocS#Sm6_HSp=~
+=ZZ(j9#piB2!XIFVOOmjt0tFR;LB^GT?Ciy}uD2(FOP<ZC_4uE!jA=>9M)Py!K?uZoRLpphkbqUMtj3
+}Ym*eM=|ux*A7rLy}UID=s(P?SS=<DO9t*;~wjTh3&on{R0HKhW9B5|=lDq%G+1&S|gfZ?E}EzGE(jC
+(rx6>dnG|sF!wU>E{!!Ct<9!<aJAp!x?eA4hh?KGe^O2sPZ2;?bF+`MttIa3Od{(jUl2=3YuzlATKQ)
+1%tOt;6an1EnL&TENBXc5oQ@k8*G-5TPx^^==YI=b%EDXfHsP)_4*J;62e9i4!TaistbEO8#PpxnS_y
+AEKwzdv{SwpGYZ=WP>*~W+YggHIv}RYM0e4VLOVVExfc}tUo0a+nuP6pAfSb?e2tZ!z^&_fCeb?2EL|
+$>b2_`^Vd&{7`dKk#%*tMDpAyEJ4ZvG97DqQ-0iE3FVx1&}Zp3<6p=JY5@$z8qa<9VD>CG1aPiA<`Wq
+2SqAg;<;eZqrh&ZBjSH(%s+jMHJ-ZxiN_Jf%{Yn>c00zrTc}ITuNDCj?twMw$WLT`&4c<XT2)<uyQC=
+aG_McMg;=LI{Vd;pwVztiP;d+@b}_V>oCnXS1ao9gj{f*Im`_a96bsYvVYs=`W;D&r;Hd{XI(`Ll*PR
+!?*~mV{j&hx~o&z?!vfO2@?n|yqRDv8qhP-sGEh*8y6y7jn>0%><_?Q#~eu(t6hc2FB0~RvwUdz|Jr*
+KfGDf4fBdp3u3%ZBSx$;e;WEH5Gt96I0xAfKqN2GV%AhElFyqo!1p`VNHT||}X_`yfZsk&`3GSM?q?M
+_an${qelp7^^|L1d`=NSeBv%bCW`+NUy51hI8x$8OSp1Yp=+<Q>MzmV{6=duC#PiiqTt;EMBoZWW2Y(
+NsyZ(51B+wM7AVZ8~;KKQegrMuipS&B;?%KCz`{vxDQ*SjkuS;g1~uQFjyWc@0WWwbpjgniRpE<)<v4
+uZ6n5>XA3vz8jP+|s~n!-h>9+JfeN^D8T;Le8I$Wc|v-R4&0*mM}~{c0<(6<5*pKUam!gb|PbU!u+Bj
+M6Qnkmga675YYHd{wEdk)#Q?BlglWZAk<GLTCcF98O9^wgbQNg)EB-1A-G2GNcJ9yr-f^xgb<6kwbL&
+YeF(E=6mAt^EIlT9j0qN1(AwJ0mODBz&*eC+q9R<bp3Rq|<Mvl*L1S*Bk%_>uK;KS$wd89reF^c~BmU
+l(q|d7N5&|}Xh@YqwBPDI|82hJB2ql^K#BS*3zk12(8`*g;$=`p!blcCw&2q3<M=<-3%kJzOS?gU5eI
+}k&<@A|&<uXUgRm&RuOnlr+$~UuH@2a`BP~5vBPCK;6VRN7@-m@eE+df(p#x(cb6CsXrs%S+o?(-vBl
+NUK*Vr7f)EKG4{B`(hQ9o@8g*~dGO!R}6{Va{Flel>VjEF}(nbL$RfqTYq5H;u0SftTZ;&E{B$P4X(%
+ib?Y%=+!nWtFb+Y*+*;<pA_A+bR1@nC%f~RR-yD>bEZ{=t8K@VCSTwD5Ld4vJ40&ix5(ckQ9|BM3Axu
+v=*s5$6LRq?S}qbgy16EyJ)3J1+Pt}TLT}uX&^42Lfn~JVCw*m-2M$V2u-iK+%Ri09HIfyh18H1UIa|
+#KxKF>!Mx&$9D|0<<E#;%OS`rIGuMA7Dzx9rCB-`G;##xm!52xY0TvcHo_nXsO*jhq=QQ}hA$hD#c?x
+3UyZG6T4CuJahv<Q+feP6fNg$6+${h&sErp#7Wvoirjg2T8h_XLmjws`9wz~%x^;|`0YTnQ}2mq7Bi>
+4J4)+jQ{o*4784K#jaMwxm$bn-6dp@h4&n^u|UBcI=@N$8*fWmW8?aLZR?im)V`Ed7Y^W{4991r`NOF
+2NS;r4V%=2=eSEJ3>BABXsB|$jUq~}N<YTMa-XdLFO->AQJOe4R_v50$5oy7w_Z?=L2^b0_1P$NLn5b
+AoN+`+Pjac9n^&;`jowG{HN-We@6V;tca1QUJ2)}*9qikQ_O!)U+DdSS6Yl=(jMXS?=63Am-nYY%qNg
+}s7FYh&oVPm++nbw}TD#fe#4R4p13$a|v+X!`G?Q6r+i^XW-1pjc!*n}R(U<I_$OXs5P?#g-M@}BOaO
+8Nieby`2FWV|9RBH+m<?DU@d$%LGEsYdznrE(;k-|=GGQf{K?i=FUI`AafJjvGl`ibku3r{fHe%n#oS
+IB3~c5i7`cFiPoOw&D?rJXnzs+Tx)`a^LGs<F)KQP>h1>PDmgP_-1PsFs3AV!Sp%w?y5R{W++SqCH&;
+!}tiR7t~AMlfub;wm8KB`lZA5>6fOv*U>Mvmi0?e{LN}UfBt&xrr2-{zst_yxA;x*TP1#vi{C5a_muc
+uCw>p(28oq#(NEf9p<lWo>z9%iJLVz3J&(z6=?!`EyX8iWe(A@ixW(3Z({jjbQ=5_vc7el1@w-#}uEp
+=Or|CD%_=Q)|>ZTaYk8u07Wb29>7%d})*SzVH_9v~d3i_W-b@e|YElK~QcGmyE8Ty}r;=7*4UQe`;v_
+EgWENOp0F4kL|=soVlfhq?F%1=Z4v#E~u$FEG*{%jQ59|SMoBy=jUx|hG<u)CBm7up}#h4yD5Vu;uBV
+zQ<c{`RodkLH9s+?B!sB2@iK(rOaCz6_4#Lxig4_zg)_vq@If2(MtF`~g|4yoZu*=UVArk<)D{(iI#i
+cNHmKsgok!%YX2c2(OB<3@WUeXfYL5WmtR)tEPDsAMjL261s@3p2#me!`jO2SJ|1m*Ya0975u%+UvSt
+}<qJf@jSH)^*0yPd`K}u4U4?tx<%q<i@=OG-3Y2^UB;Pggh3cn#pzuxgD(WLFvf2mB@I@M7v3eEx3Cn
+b^qK?8c+^eXqQ2jtGh+b5M!SjWL?GdFcHZ!$BbuO=LER&t;^_0KR0Bln<IJnvm*ZD<Qyh#8ak?tk&Dn
+3J#-a=?_7RxC~Qf(6~mWVdZ-&%b`S!mx{DT)Jip?IHH$pTmG2t-&cx0in-^gf6r>3#OG8qRHH&VJU$v
+b$}o$caXg9@~V*XNUVW5lST$y=`Q5$ueZ>RrD*=JtrM&o^UK&b5U?*173=`*Me3aIMV<+uEri1g`@fK
++6Wzbv){zk?BWm#E(i$Tir<-s%TFmfqHhU*=!c#K-uA{`MOSWO^zK#skvkX*fegIXN5OByVW(DU^$nc
+QG`D!~4b1P5Esft{>@MC16#2S0g*piS3G<7=(FA*6F=oWAw_YV1P?IYfr<DjlDTG9%)bx~VF*mQN6Jl
+LKXpaIVwmP&&h-0~nrW`f$QT{XL#cK|WD)xh$Lw8gu&?r6$;W_Lk>yFL~-QTA!vhL_(MR)WA7HF!{Em
+_bV`3l|9N})Rv?L)~12##f(To<4<8s<QjO3@xAu1@eOnl3a(f`vk31W8USaf8N4G_-DeM589qzV5CFk
+ZDgb(r@aJB1u!U1e&66Y>`#A#Om_TaU|<q)Ot*?CP`8$*7^Gz_s;f|8V1|mIll^Bk8b|El9s1@8+NcF
+=@BKxYi}r8o*KT}s-WdTWTEA8leIiMD8+^Fj;t;zex|0-`3BS^q2;;a5D#*_D1snHlCzd)8AySa=Q~B
+qBbq@daYS-S&RU+d!mU6QYMztMA)M4a;X=(L_(SCNmFjC!dE1*H=Za`E(0?`tUwnnC=U>%A#EX_Hs-A
+;lmoGv|N*+<V689w~&rIw~P!A_bJpcxXpoOJ_=>6agaqA$YH3?dtVx>LxD#m+F-L|xsEGCIR(D+0OjZ
+b9tl8QF6AF)=^`iM}nbG@1$g<?6&TAClhQD}Y~649<uG|7tQ2ky}P2nmrjKOkcMh8se!BD$OTQWsRC`
+~gEG<&VcT-0rF_SvK!RE#(gwBPoB1k&w{;D3wEMLrX-%Wf>#%Kf4R`YD*#)_17jOP->@kGj&CXX!taa
+N-O!-RPc%>1BI5Tky}|Yw_f%W-bbpWTne|MLQx^C6>B0uVXevH_nJ`rxKHX)(Q>T|nSK?G*1B3+Pg0F
+YE17>lB!5ay-nJDTwV!A({kDp6h7iTI&8@x_4@jNTB$d=F4YWTglnt=+5khbA4c~w`wyhY4*_Is>b>V
+>25+a!WiK|NSLfg|zvbtNl*hDTB?Ix+Ev~4>E+Pl|H{cPb@(Xd%3+m?xr>E!Gbn-Kh34YAS!uNHN%zk
+UBN(n5UkJW;T-effsfg0liWk)6Ht3g>QH5nx2|$^3N{JqzcHj4L`zHCm#Gi&9kR5)n7bnjXBcnSnAvm
+WO46Zedx{mAWh|+=|vnsCr<rS3ipGPPmP*iD9Nq)CwAn1YRpCmd2vHNsJDcPq~UQD8>uW^}uZGRqS%L
+-r=s)=sKpDIa6g_Pwyor*86!Avk)92xNotEbtT9oNL?c9dT?xeQgP|@5z>M}-XQ7o>`HO6v!o*ND*L`
+sVMt4E4U(^Tz#cN|1BG^G@y0F^>)nZyu2gs?PP$s*mN@Aezo$Lc=o^=`ZxmQMvf^I*EAt#H6mQQ{R)!
+o$r`X~>#Q{FXPiBrZwh%ZZ+Hg0s?dLkDOG~RY+QJYux2TodChqt9#Nr)e@r-Ezi^ArOXr@_lk6l%RMY
+nn4j1A`l#pRV2AA55NL;KG*r}d{=OD^r~cAFIDQ%4wk4p}QczxC7hYf+wr^QSg<Px0kWKdg6^Q&@cai
+32Ic(Udi(Qpy*-1)dBZ<7o|=)YLv>p>*~O-rL@CAf(l;Wb`@S$5UCAQZpQ-ea>>fNL#tQyIA_BTi;y1
+2^Z^DuiNk`_(?*8GBG~oeA{2WU%)qa+;u(4XW~Rq(Fsh5KPNtFKQ*bDy~R`Vt$<>8A9N$l?3<sGP8%$
+lb-a+9yyhFxcxkpGS!gtHjzfv{w9Q?(c!={yE4>PzL!Z^h-Nibxa5ha^^@L$A5(+#SNt_UF_ua}0kCM
+@+D%@~jr@5Mn7zH=$6tlLOKc;!2L^sm3+?Oa3Qn=exQD~cW+-5yZ=?5<W2SkMLcT+H$O8ZH8mfdVb%s
+lPkR#{>_SBT3yX7nxZj#C<N(&_t~92@a1Df63)7$q+9si(G9bG8ebw&%6kZE|OYo_x2-qbZJOD=alpq
+>8W0e()%nbxx9mLxpELl$}w^Yc~WZ$qpSoxa#h__^2GubG`&$&NQ?CYd)|~^SV?zC=qu*cu|CW+xEh<
+`DOOT7M;A)Jk2ZLg&@KkKPvpF@ngbI0)EEgCue>MhB5uDhl@Y6=xp0*K5Ox+aHHk~@#n{Q_p@FrJnLm
+?Z`&Stvh0GZ#j~XC{22FGYgOTCH%p_rZ6o?we;YY`hwb?FlZ89H{f-8f4z?XFJPSXUh*;}yWfh1+DY^
+HzRy|Ta6s&B@?@>{0U4>7c152?myin+W=Q<XK@3xPf>%1WRa_t4-yX+ATZ~M6?s4kvpa3Q#nSO`9tXw
+Oit&y$XkcCq*;+ApD>AffUVU&vUS$?NCQ=kOW9IVSEhY-|7F3GszXiKl)46Sq52F>0=|I=sBv`P(C`M
+aEa!cP<qxR?ThkSDqZ&x@d^Y{nV=BOyaw0tzmVF_LdJ}bqg#t)w`JGE|#+<G5ZVLd2!4(6pg#n9`O5~
+Q1(o3dtB@R50rLHyU%s4IBWaGukuOHxy>7yio{;&%M_*6ZdYbM|9H(#>D|)#mNg$rG{aF4OD*EtS}a@
+^U&9?Io^~&bxP!7HKB+=2uIm|A>*I-TW%3PlV}d<!S&zju%x+vDopE}9;my-iKN)8C!@GUL<24rmA#3
+|EpkApQ+tKK8v@wk;&f3Fjx&Krw+--X*_B-xK9)_DiToUmOO^G<Q+ur<Je1c;ahq{yuy@-X~5x5-%6j
+XkL*+!@RI4<H>Zi&EG`?I|LD)0NvYc4*2%J(#0_7dO5ZpHvPagry$M_ndvvmPsirW%*9+Fvbp+%vGW7
+zz||kkdp=evr#OF5)=DXyV1prupv1%en5h9Tl#&-MGOG0gitwv=Tr~y9BvUZ<K$OIC{utt>o4!1UVvB
+bSXS8SCUZ}g=5!Fiu3F3x?<e5;bCoNZ&$1wVN`gKmsqb<Y&G7<pE8rO&6}K9v&MS$YtEIcVZ4fPQ8rv
+ZtCA{5cgjJ!psnAMlIY4#+e?OC!Or%F?3;^}<n23(YD$BO!CKkLdZq9trG~?J6UD(!b|m^_t*pKh=b@
+LwkUl_`x2uay4D(w{wYjO>XC8bq!q>Gl$!p^bcdw62rCoMMs^;9lIYpKPGReWxn@pTa)CHz1vpfLPom
+oQ*V%!UCP3On?`##yK;Ov}&{VsD`RdlWG`dGn!*9v<<ihF)ViyEJT2MdZZcCa=U$C!=Y0cLb&rHuM86
+LY$lR2*vlYN2DjGPV%a50{X)7~70`3QuuXhu8yxRoudht<!>|J*7K_5{HR(?Thx87UOcui?#u+15X-1
+vED=J&hNJ^t?<{j)7;af^Fk{6e}IX=Bo{=_djM?x$|C9#v=AdK-E9l-owV0Rcb5v){Gru@C%McY>Nz-
+Jko}^#McYRTXrb9#bhUnT72208XkQ?yNw(dhOYtf$L$h|I@JxQ$g~k?hMWgvIO8rW9BZ2|cuVfFSekF
+Sh`jy561JTtap{q$Ir|dN9YO=GatI3{9T}}4v9P*fe?k5}F&op#D`8o49q7>(gPr75RtBUtoVr)msF5
+vw2HqQCwH=aM$dw#xmto2gi883@34H?T_EiG*Kmb95a)qQ@xdyMrmz9De4G{I30efq{&uMZ!SJbK4Rm
+;w78_S-+mwp|Hu@7y*bK$*Q(68)@~N0dYL>U1C9p!spWo_-Y1%dPCbea*^7ls9e4n8UTP;(7%un>A-S
+dlikxPKBxN_Ul>rQrOdeNtn&;KM2#?{;e=u+rJd1kNpr#t$nX}RoP30>1+Q$nBDB_gxTG`R+wu03Sny
+Oi-p<VzEGHY`&?m$*bB1IikSFRliezuqU@Q%>}NL%GtNF*m<jfw!W?3c7v^w#v@l26Lxnlk9wf|Edk<
+kww09O}hW&nF=GgBMrp4YunA7bZ!klUUJySwKl(79$Cce%u5*@a%;yXwCSHg<>i0uc3b)jfWgcV9)`v
+=0h7}mFhbt$YXg>?n2&kO5nSmy~V_Ho$fWI7HOn40N07GSclLLX)yE3D{p?L&q2fT*wv$~aTN9wIz&)
+Pdb!SaFn>y^FA(g7rROJq@e3u;PqiyN9sag~9?mHa1=q^b4yvbl{w@Rtoxs^@^ZhSgQp6!YZ!S`#@Mx
++wE@&D~>v~uM}2%6l;GzLq-|58`(>Q2R`Am&lc9!u;vM?53Cb~6-&$Z(ZZ^Nb+EAd!rE6@yTKYFthj;
+B?k}urSi1<T2G;w8RS&DTu!g|uA*?1?f13<z6s(towI8hKCd(+t;q43IkpSx_!ip=l?WMvx9M%oOItt
+d;g>@{f%S9qSV`?3tZW&=~Wb_b6Ry0nq&zvl-?sYeMjFMd@NiH<C8!NjEmt53FkGZl-AK@}pV+@*iQ~
+L5;1DDzvc@^P%T3esyMvqiEUP~z@Z==UV*`<1taA|Gy$dFwwNG?7`j~v<Mxa88_=wXpvc1tcQqsMgFW
+xeF$YxI~YyR4L4^hS?`vdcorCB*3QjO;R7axob_7RxRfl1r4)W2x-&h~(1G=&?d}=_g#K#u<ZF%hYQp
+IZ<D0FM(~Sk2IA(I=h1{y0vY8jNZrMX`)W+TH!u!+wND^&$xY-3r&4|N=8=&kM^}T6Q_ma7Pn|$Taa3
+uL>oN18^&KgUeA_7$L+ogGh!MH`SLID1$CwIhS>c$n4D)vN>@c*FRXIS)y<Cz_YB3EoYU^M;}kaAPU8
++s`#tQy0c^HNuwPFn#L2129)>ev3QIjpqOagg&nv;BuUOtp5+_aKd@~$%<||z|jJj+K($Ih>F;NO2hD
+cvvf9nqEf?{zDXmPx^<sn<N3XWK#3XZPwdTNz~czfrF;<_8#tSaM;*}a{R*8+KC*|dA@FaAoHR~v6wC
+hlnr%nSE;R<uj78*V{fI-H&YhSMmJvZ!>*1z)%(CI*jIS=|#8D;jaDO5EgDm6Qm|#MULEAjy8{198aZ
+6+141u|-#nm~TFZgTLad13$HQO?7<PS~F+yUWl<^xB1bPNJ&n&=1{l<`+2bk)VIEA%Ai3`dSVEQkb?H
+kQqaCe3RIAS)<O!rYowq(q<};Kn~w5lhs@!XxKvEzT=pZXZY}#86K-MO@#2$~kLJ6FCro0A_Iq9sgKY
+2zlt2R73pTbQ>Jk#gfq|(K*AneZHYv=y=wQ}w<LY77cMHHQU%{+8Y^o5u7~0pzrkX7vNi^RuUbeO}-k
+91#+T7t<;zNZ0OL}oD^yK0y!u+Xs+f6f+solBhVroa@M;{<DW`VLp#H$#+X&P0{4{_^Anz3xE)_B?CM
+aefyN*reQULu%azdBvnw_(44wB+p@@>zHo6H=@$#v9XA_Wjc_HAJpiWJ9=Od7Fp0k9KY&c~6RT9IV(T
+62hg^u@Od`zYyPiP-4<B8j{Ah9+X%zy1Cc{a$20uXlWfhy17@e_&zw^)A)&HYl%z6hti~?u<B8-Vq9T
+Nv#_E(E-nXt9Q^@EiC!#7DIdw@iA0_IL7x`vZ>{W=C=4y7*6pU%!`>a5HN0R#Bkc{^w*!~0Rb!8Cyyq
+a?<{}mcEN8LzXJ4(`nx*lfyWd`#93Aeo*S51WBl$G$&24Hc8}W>dmAL!v=z+;%F{>zdzMAyvbC`!JSC
+h82=T39JnpB`s9_k`rPC9g&<8sn7Yh5gDaDND{CdCO`b*?7GrGPlYdy;G6=}RM<?QmRs*xml`?3(*YA
+J49JKWY9~(y9}+_I|L|+)rA_9QTvD%J-A{eNr;Oy_hQ(ly*5TZ=$QN7$9G{7%GbIPh3!XPa^h{53*lA
+R5LfU^gzC&J=02jD(=S~?P_FayCIxLSKHtIQpp!Ll{Ry*t)j7Qhd3P7`Kr>^#ig^GCEzNYTo-BTjOv-
+-kPm*3MjY-S&g&Z{-v>aCZD@0xN9NkJ)Y7DIyNlNw?!Fa2(*S_FH*zm5b9u6n@Y?O-waVQ$?<XR3r)r
+F|jM#?wy4z^7aDBTt5+cOrYH+dkq)1&1DK6H|P0v~GFSC0aB3!L)$+49o#m(wnSms{1-M#5K>t{Ba9P
+e0oU+Iz4g{3Y#u5TA7GWtB}O1MxmKM(>G>D2Y@*i2|yoo2&nkK}QXTz4b=U6lUT!ZO#2xe7jaMxTuf!
+kR0D{XD-9mnaik*xl%VB4ufss5*heBXG3pDpaB!N>!q$RX1roAsx%A+zcKZ-F)g@RI$?vgPEm?i4ZeJ
+H_!dqc|VpoFEP;;@7-*lB_5c}YQ8Dr{YN&cfM=Jc=bmhA=}MS&BFqZ+xEOxOZDjavnyc&ghCO84LAKq
+7)7ECVgX>7(KEr8C6Kwh7xvtm0vXza*d2zL??*VaiWt^{fv%NU|(b@!8aa}*N-J2y|KO=7gOZ9E-CfU
+xo9UVztyz?_e)c=h~O;egMf&_v|1k(wgBzTEnJ;7muiv+Ic8S^2~5X2IUA;=<_O|Xz)1;Gad`w4Cm-1
+R+U9SM387zpAB#t>Kt9w&H#U?agX0y_cwfzl^15hM_dBCrq?5iBHliQr9wjRXe?z9G0m(46LBJ_KC|0
+tliA1{0(bJWB8k!RrJ&3BDotiNL*_u?Gmc5NHTY1hE9e2qqF(2yhk0a|EvtY$y1F;2gmff<`nqxc7p@
+zwgiA%G0#2CibJz#7d`{*pOFDY|Jo|!>`vzCf25#>DEY2&-3538VVD&ehfj*Hv|V6i_FfAo}O+=$ju&
+a&dXygljX7TESu#rGyU~ssVsy37*lZ*n@%vE{wNq5%7(B$tOwIO+5MT8#j$9S-%vJy#jr>=kVOfX{`5
+PT+@dK&APb<BqsSH~!uH9?>yv9X!%Q?=hNWa!g}twnJt89`dpyGCGAqks7M4z!nwg3qm;QjSN~G0-H5
+KL%N_!wnq(A2{l5ZkmHh}f5$A`+AV#Tuv5u!eSJcEf&HBk{LTzgQs2*Suhp)*+ynJR*8`n3|LfMdMKF
+^+8DY^rce6X|A(uoH=zJhG+GZ=OhH9PxYxxujAWx#XM0CeT|Jg#hJKMG7@(!9QL2q)@zc_NW{#o7|fc
+CzSLNE|q@A3xBhmr;2j3iuB>HqI|POuJFsDzYIqVDaD@T6C<dUQq3gSJn@c@lPO&ZchCjwaz##7az&V
+IqFg1?%Mjru%2Gtm3ZHri+Q7?n3S%amRjeE9MXni?+CzeSkb?0JoSgZYQZJ{n1dfoN@uGBc1unU@Vnz
+v^;wf&rpi?PxrMv?i<()+_EJ8lA1h*y-tt!ePhvJTx%U4Q0gG`B9rKBXEOec=k&By-_<m%S)Haqb<lF
+}I>mrJh0N8)g{;2P@ne}!Dsr9<tYL%~VmTYVIy)`=fLZHp4$DoS*m%*ESr0XzVHx7ODHru&QKH%(A@`
++8Pie%il~AEY$(FXk8I$S+5f6w;ka;d3dLRisg0%~9%NO^Ql-n!kX5vPjj;rVGq_$r2^Kq2!cOycK_c
+7JS?C&ZgYVGR9JU&X)br3QiT$Sbw^;<p|gyrSSr@R419aeS7ckBtP}1lPR}y>7vHvid0h_ZNTI|O#iR
+QOYNLe1;10|)RlB=>LGZVE##q_Xh$cCJ_j}3?BJ~=4Y$@uwNv@rIerKs$so!sQ@mSh;iq;gpF2-Cg?=
+Yc2qne5TjW>m)Ly5FGLUMI)Z^sPd%B3J61`K7aLE<#3KmkF)LOhy#!v<OP2w{6Ze(H#et4frgqTYvv`
+1<(Tfu#=iQ%%!xn$lAcQSD+PU#*KYm0FhneC7$nfH;gaJNZ(AZH@;0l1UdfeigFlNjL~-f3bFl6iVNj
+mOB`LgoxISCIKAnMGtm2{4mPEZ~eHGhWn<T+vUX{{?NsMBA^l3X#l@{#3-d+M}yDvW=sd;PwnbdsCSY
+J;fhmA;dSNJVAuYl>1&OHqx3bxH*k}!{oB5okO&vMm#9ePN<cdLD2kv{ZZZW*Uj;FtKY4E{YWBj_g7T
+UU+oZcoc>(y@XtB^V@`jrclf8oRsB!1|NnvK)-s*=-z@<yHGg;d)RF*~nm^Y&{Hu|G|Mc&V=*M4c`g4
+AB_Pb+}Wx8v>-zmK{bk}tecZsrsIgb?<6+iyO+>$44^X4yjYT=@PKmE+J&pp3*$qO$oUG~!Q6)&$`wR
++7fudaRV^*7#p>+N^ez5Cw#>o;uN^udQ8ZQio=<87s7+js2TwR_LrefvK-@ae%rhmU-A^w{TLe0lt<6
+DLo7{mr+h&z$}4-1+Z+u$Nz`xcK8wmnwh$<?@xQ*REIn`rD1_-*0l};_Bw^(WtR!lcvp@w`l3*-RiEp
+Ti<hU8=toA?rVSl107TkcI@Qaxl7k>4?XPHy+_YpYX5*hO^{Zn4>lM>Lc_vMDdWbcn$spsOrJD4BQq;
+IXG(6K#X5D`^cjzi95s4O^4Ny`|21>g?EL>}`v0f%|Bv|}(K|9Kx=-JJF|qyQ;s*>&7&JI>$k1U)!$&
++K@xNjJ|0nqGLBpnIa*KFB+1+LPb53?#nd~Xs7dY9Ua<VUUvM+M7|J%v_w38jzC@c02PIj!$H<8j4d-
+w44qdx<UTrhUY6DwtmMvu~8cZM|%=}+ZwnIZo%;oeKQ_mW*0jfdie86lfdV#Xn@5n(ogX8?_urq*#AD
+6exUE{SC3$ZH9TTZ9;4Tg99cPpp`~%y8H-$C2{R5vx!c0!NGm$1^PD_aXmrVojxHd>AWM8fsi5d=(rO
+ycB#C9F)A3e5HJ5IPzBVRq|BwK{_d7cAG2aJ`xUn1hzHrSm{8BJSVp#F{;lJs~w8#5DMk|9xvuBHE|L
+JZqDy1f)*)1B_AbiB|Rk_B|griG-PHP@`P}t($ifPPmc<g8T7FD_+F{0y=I6^5~$cC2%^m6&6(rOqHq
+UfPj%P_j<;mPnrP0kD2@@<3D!Id_Qqw1G-9n;q97x3a??c_rsR$n<!2t3YfZ_O!lYygE9HaqkuK6iT1
+W?JP`nvP1Mv|TaadxC#hRO%BGTxSn=WiaCR%gzWVa-9YL;1k8Df=e@!46zA<-(EDHg;<JcLI$gn>WnY
+gX2#lhU(Nv!^9mQY`7&!hL9-Id|Z=N#^mEn5?wy+)Uw-MEQ_kTuPoLV!S1NsyQJ$$C@MDq<1M97EdXr
+OfdJ(o}R>pQpsR-y6(DkHzCQG4L>BkXIV2c$l3`1)FXQ{w??dytG9b}bhHPaAT`xj1XSr0hBx+PI<-0
+}p)n2LRHDi796NC=J&aAynNE)<g<y@DrzaVn^w(HvR~i)*JYF!TfHH7<yx{T23R-)3P!7e##nb?*)dd
+L!b18JUuKI+e0*X(WBqU&CoT^*F+yaWlD4U5BCwh7|o}QC4Ehp!e^#7~7kw56hg;rny0rUp=?7&I|85
+qzrHG_n+rd)=kXPUFEmI#uo6iZ5mDmpheJ6DySrHV_jW{sa{#=LQyRNqt5Gt8+fOSURI$DE}yPdAUZT
+2jVkK<I~N!2-WDDtJ{&mMSHO0;N)T)B+NSo=#zM%_*s>wA}1W3N50uP7g{vi_IQKc{{{RHN|Sqosph3
+L6s&lzr{b#Y_T|bvph>Gp`L2Z$w(hBg;0Ei9}!x=C;6FkbL;o3!FxO*ZZWHJq?)3#<{|5@o%6an@n(Y
+ALaa%*sK#ffnpN3pN@&dGv6dyZ_0iLFs6t6GB#u$|%*+(B5!a9`k<qm$@wh8&c@xd!QphnQJ<Hrv7B8
+nX%(<EASt)`ML?R*EV~RDK5G6i26;neA4RMMt#haFHnFv<pSyFPHobMpH8a@cBWdC~TRdAMM%{iS~VN
+=b5jn4iu+lLP6(?fr2x?MYURi((7gZs5ot_P2<4|XNGGBUEKiL&f;3%l!y(=ZR}Dc9Ty)=YDjB~R7OJ
+e6uqdYa0dnPZvp(CzaXXI3dfmjay}#7+?q!Guh6W;XRB9a(4UNjuW`rn6XKto<%M{n%fBuu1IjHzqL#
+8=M%G_-c<$v8Lz~aZhVjJ=$i?nX{NMMH)t-kH{M}j0$wdDC$ShS>%n%OV7;7Fehio?Z~LX<_vR6o_SQ
+?p)rZVOKD+x=A@#9S=fuQb^eT1PN*Lje9r^(m~1NVU-_77`Ipx5-*%trw(-ZPZtuUZ<L&(qb-KO(JHE
+C3=Q{b{)y2fRbgAusxK8}5-EW`%cY(G2k30E?YfLOsb9?@D*ESp<=2@M|VgK35UN_w8;2OVmPWHO^4h
+d;EzZ;<@)-vq&{D>ZKd;cE?*Y-b9$A88U$zP;3#MEFqr-$A?pI=7bKA$r4?aS?_?A!Zq$i02~PfWeNe
+^kls@o&B8_W2)JRoj1W9sfmZYWVo+>W29k@T!T~&pLv#<JfmmO2fBvHEzF<o2iV|`~0)d<?q?&($>X!
+3(6BNG**w4x|BHTQIgmc_e8Vv7@x*k(zu_EbHT=uxb$(kn7gcHkzyRAOc<yzMOxO}T<YkQ^nZ0vN=di
+$$<9rrjy1z9r!;{=W|?y-c4T%|UUr5#(vq7IO&uf6qwZx9d3os*vg8zoWY-|TzC{i|Kaz$6j$JgZUCA
+{*JJp(D?nC2_0V$b823zAc2&2Y&DVg9FZ_Tiz_nu)f56MnSPc=tQq*>OpGDVJXG#XAt+KHCbe#9S)QC
+(f-`HmPMQ_JBh&zT}pQ*#A2A#QQmDXB7-D1{#2rxTe{O1aF=qU2$wSx$T~i_FN*Q{MVV4;~O5r;OC2T
+@o$1^dCc%CH9L;ib#kN9zHd+OYz8mKjxKSP<DD24Y1fD*M8=d9I@$#eIo4A2A_0^wp=QiAv1Ci^YJ>;
+5W$X$@U<m^vGGz&8eCa(;E}{i-IeiUO}QL(ADT5$Ae9<DeLPK>DJ~TdP-1Jqm4?0DV)CNQ<E#@Vm~#_
+|tq_c#STv}K%uTlt(=rmNd;~7RZgJ+6spebUzjKYrBBzv$^hfJ@Mr5TX=A>suW?L!U3zV)iw|v|OTQY
+`b(LgYj9U)F;m@T&?9`0Pm#J64ULma!N_Q3AgO|=(qj(t|U@g@r55@AQVQH;sv)R-)qQe+D0&jVka<Z
+G8xof<L`Go~1tj8r(srovWYclJmkOhqkVpNf)`B%11`+hB?-JXkjJkfcRo0lSP`;0ZZkY4FFJ!V-4(t
+u>4!J4Lhy(OFZ|bF;I6fFw)Q6twbCD%Fr|M@?fVMD497ID1v9s|n^@A!J$OCB!J}r;+oJ^zoBrOv`R<
+V~8O$ggnm3vzRk6k&Va`w2+Ox=bo5hHs`RX<hl)yB$g{uNr33GiLggRCNQQ$Dg&eAVQ)$jNM)9uDq0$
+vBeP>tO(1EaK0&G{Qk|yIZ#umbn<&H+;Gt8y;ZEtv_-5z!F=HLatVni}OP>sD-bA3*+nSa}V`#=c6S)
+mGOG1^1)S|Oe2c}6S80%WIQiE{R%DBZv#uIvirNmKRNfKn7lIrhL|9Sp@@L+$$fbUJ4-)*}s3h$*YBb
+Z`Jnk@P8u0O8Jt<JUlUH>jb|9SpTJdh`RTd&BDn`}a7guOVpaueqf;BZ6eb8u`L^f{ig*+e#*%6`q9{
+O*<G!t|2U^_JbR|K~0l;QpZO9wfg<$nTNzdx9Kiu<V{Fn@Mu|Bjmg@WcN(Q#O?^_QLtwySH`uG9KJxt
+d5&y8Ci@r4{zY;+xGWee+%L)RFU$C>mh*d6cHbo9cTo2KQug<0Eb&6&nZk2tll@8ehW!6*_=f!dCm$|
+9t)`jSoxuP0=a2SFxYl{x<VT60@TrLnK4cPnD`<MVobK#Vc|6_L`Mqv`5-FSgj+<B%fr%i5Ku@3{P!n
+_`P!aeLG$&vLRbQIeMFKm)IfBy!rwEP{93wbPaDZSh!A^owf-MB=3Dy#<AXqGiUq~>Qpn$+akU=n(U<
+g41K^%dJpgVzzpox2pjQh~LH-RSsBdGeq#3~8w1jh;X5_~|gnqVP80YL^qD#0j%Ap~&*Q3N3b8iMWwz
+69+FS`#!UsQ#SNBsfKI_;V@zPBK3rSWEB>K@mX?!6<?u1aSm<0&jxqV<t&H{_`TO_bI(*`di;!*sZwY
++yLc)oE|@7V#ALTk14LY%EbQbAMXYq$zRpr^WT8nf5X$Two&qLSX=o&i}&C8{9l&=`T1Wi3b>x$#ns(
+&HT8UBADi)*ta@gzd@GqRuaV3-%THsAH#5yH5awD6U$*MRDpdpN&bcX>CfVF@hv6e+b0dX6wDR1_cI^
+C0`^u3Gr$75;VH-pP>Om*QoKUgk6O&E^+#3Gq@o!H5%9#IbDdutNJNF*1S*IUDeZBM3Q-%a;YZID9{2
+}Da&kzbfgx(thrR{c+(^uMd1rNv5-=k3jtorA<%_Hr}a})EeZZsWf$;4cx0CTRn()7eBTw}ri737Zb*
+au`H-eEG~ZzmJ;GzwIJ!tfyzb1}6`-E>^UPKJK<xFh|s^|<4i?$oh6yU%UFeQ^WsYa4L?paJ*24Y(g~
+z};StJH~!h<nGzgh1rRD=j6?hmA)<vhcYSeK3!bcTF;vI(w;7CqDRepHx2oBu6aK^g5JGr-ltEZ@a{G
+5r?XtdK0xKYU$zT-#3?@<u&N?32`rTzn)N?Dd-h~~`}So?Nl7d{J)K#tR;i#03h2LpEnd8sz542_?86
+T~WTmC0?8`5|WH)c#WO7w45P|6hCuGq?^r-^{&6(GpY10mg*F*eLbwTs$Y13xTM1V{DK=mPh;5vElp?
+6PuxkPd4ecCj<+YcQ$a6r6c%E3J8=Mt|@6aLd?R(f8f$b7f_KJAca72>C*RZH*r2!G&ETH2vK@_V)9k
+29{S@lNSilRq8`lP{$&y=Pn(;SU@*v|DnxF4F%N`AhGVKj*eXP^!WW_*EbJ^;>$o27iJ7W7SU{s6KEF
+Y2f|l;88IYpgM!nr+1Gn`&vJ=^uQq^Ar1WTJpR_4yDv#|Qk*5``Fn9dtGEM)D8xLl%d`5m7yR{70P$Y
++>7msf-#j3~0?7ShL<p~<=wc0k@ISa}$bqWrLj|QG{2|VNAG)ooT6iNo@wa?w6&K!u|470vc_X|yXW_
+WyEz%db$(w9F=gN=d&8j<;=AU*e2qzKf-~?_2E(J_#Xc_<)Ih=F6@CG2CfEO?q0yl!j1YQIkION=0mi
+0f9bB#vB;^X64VqzkD^wCFa$@s#B3)$<hzs}aLUoT|*i!Z)l=g*&a$axNyTh>t<x^7crl*GSs{>3+@Z
+DaY{E^SMjoXzs7h|;!kp1-XknIvIy{!dqvH<Bg)rSl^>zmYa;-}#|Khi*%Y)4V}u^5obl13BN2%x9N$
+-8OP)GRx<ywrv=S^gdjG^z%9Y@%r_3Oi1y+7@OUp1NUy+fipK5*lj=w(^v*CiBI5>N$|<DVJ6YjxpQa
+c=jX@NYBdWD4P_A#5iBMqM(}RXph0ZduwiV(h!Je`=+SI;Y9BUX!UQ&H(j=CdnaOf<b4B|xYt}6GvNf
+E&@n|S}f3}eoPKajnCP%VovLo1wxhA%Gj)A>Z5W`-WGmdRqoWpiM)t?<+HJNq$n6vKNIrHDmS@=%Qg7
+$M3a*(r8pK=y?jI;hHI2-gWXL$re&vG{M2hPTy=4{+W&gRUS!%9j@*rG*?*z?an&z3A%!j>*w%2upc!
+Pcx<Bg%K(x^-;Bh7F=E+p=W~JGx^Xd+cY<HdJ!9Yu7HeZ{I%l>8GEvBS(&~W5<rM6DLlvufP79T{?f9
+9sPx~GiS~SxhO9$XICy=VBh`9S!HD<yMFz;kQF?xy{LakV5|`djpstfnv&c!qsrcrDzJ**#X`A18_5T
+;8GH&`%oni@{9Sg8A5q#<)|%pXr1)xzA4>7#DE<hFKY`*;=dIY26n`njf1Bc$QvA;-{yC@kT`0bZ;y*
+(1EfjwN#ea?BZ>RW2DE@JZ{|&|ej^dY7{7V%7np6B<U1+=)L9$N6&4m`m)-9kh=xfxc?Vw8dSu4hm`!
+oLaAjZ#4Vf?}(#w*`t{F+nz))c=Z#aC1OFp3{f@hPp!bc#QN;y+38mr?w8D1I5m|BT|FcZyGgj|8g9I
+W*Y7eMsw?QhaKEDrpeIRd+ESM)5~c{6{JN^Avw0#Xsg0pS5HOtPR!K2iZdAN7Yu#4w2+nvKYo&J;Jzu
+4&#F!XMD<1#uvTE_`7=<Kk}_pd=<slQv5iIKaS#0r}zsf{%VT<KE?l-;_spOl;@&T6#qQMuXM!kPAT-
+K6w)b$Cn$wAl)_F*;S8lv*^0AY{5iWah_h=`IJ>@xvtQrk?8Xu9h<_i&??LgyDgI!JKc3=GrTB9x{t}
+A+3dLVf@wZd_LlplM#jkLR?<sTO9*Tb-#eb0EccJ**DSjBmA5QV7Q2com{}qbAjpCnhieLLlmL6xP$j
+IIiy(5HviN!tKt+Q{(j$QgXd?R|t#>7NL#YTpQM?@v}=+?DsXWzbkJ1Hqd#gI4s$B-o|D%tNL1nApWC
+Hu$5M@GknQ}WS~5#hZflDi{7=gu7;l>KAkO%bsXkx`KpA9(fb?&lYlT!Un6e5~^HxzDFfa?hSp3cej5
+RCVf$_>_KZY)p7;^8I(W^&x)>&`l1|C7I%fM<8-!Z1R0~-`!U77dZNMR1uOfj`Wl7X?^$I{X<0nKM~+
+T72-!m!8Zc_t?$0Szl>Vk!^nZ+Cx=JG4~UJ8j!kZd1lszvX>)J$=FQv3AqV)!i-*#0S1Uj=*Z}{?==h
+k}*!bx94z&W1f7gda`n}_$Vx!}u;`=<%?jAV+{2%JpBaTVwM~W;It|f;cfXqLUzP~Y^SR5Z68!w0uIV
+k)m{F9^nO+X=*(rDMG&)s+5O#vMK$=XKAre4vM!2t4qpiduxLVJgQq?d=6zNI=kHY%RD+^2&e;{HCec
+!V1wA_CmJJk;UI@yYQq(K43tigd+9YI;ZZa(8JuK=uHyVxl4({>j0;yqh#~acR|$Fa(P6vEd|0HU1I0
+Aa!69H_y9b$TPAx$+AWh8Cm0>oZLHNps{6R4=q_ElaYE%R89WLkq9!-*uu533I37cB(AZn#vlA40W&p
+g)pB5RWPG@hWQ{TB^rfetcdOv|2;yUmlQ+_j65iyl3yd_z)b^I-htiKCL5ztGGSu;QN}pI06Je<1&0=
+dm@pZg8FU_y=DhNMK1BueVXFQi*57axBD`4Su#&fqHrzqpO!Gi~j@yKj*U&q+v)#+huZhACZoEyp3KN
+ii_y)c{U_R^Sw+PQvTayIO&G-jAi=5x<I$6kE#MYe3&GWPPzFSFIFSBtU2`|rOm#)BVy^pO}Vd|vu0d
+y&Q)>u9X7bLUQW@Zdo)HaK_oTXy#BS$6K+IaX0o!7g6B$bR|xk{BCYy?T}X{`>E2(RI$=rS~H@oZ8u5
+4^l^`qmGX1<-+mQ(aoTa?kVc%USX~HdgjkRW`p=%HiaK$i})$_E<aEGccoK&>JSrXMxR5S?ZWO9UrX^
+#6hD^YkEHlhDE<=^e;LJpkK*s6_%;39KjV~t#wq_>$0_aPHlSU*c4Dcvt<WmA^Xc5HS1+85+D_H3olm
+<5AMW7YvgHFLXKg!n>)g3(=U%P6y&h^o@9iG!)U_*xZROpemtQYG*1qGz-Fn^Mv{x$%(6U8~2iiZ_xm
+&MhO}jlHJE&CM9`5Do*}NOYdFZZ|E!z3CdAL_IPtT@JJUp5|<mJ`Qy-~Y|?{DVW6b`+-JKWRQrE$kzc
+<yiFf%Nb7alM<~nssX0q=_HZuLnEd=hmKlDa!ryPWk(_Z{4+%U$0(%xD<}^f7s8@*U#@kKS5u6dZ06S
+@Ih&1I^T=25G=yE$<UA@&&4SW!egUE2Wfo>*Aa~sK~%SGXYb%qwsbUd0uMRY|BmOcs4;;rwou&Dp+g5
+S6@X9Tx-@ChgjyDHfGcrI91TM)h1iCnaQyFj+~jnd|4~5s0bkKQLi|<BmoHDEerNNgOP9X?>8GE5puX
+qqk3ar+p3HBrT)A?I=B4LOojUdIo;`bPE-o&9At52XWSV^Bbg(4_eE2i`Ls_F^zN<@@E}h5(uUg(=T%
+VC6M}}+ZA^Uw_e);87G>_)nwr%4-{O|*({*-_5#TQth;@^JzEvNZ6hd<H4sh|DYZnu{ct!F;_?6a53%
+F6P4_wF6Yq;kR*oHk6_A_H7du1#ftUD*<ZtvB~0Y-VO=U{zJsZw$@go#Wq@_<e}#payB^bh<9UkMI)s
+@87?lfB4~tobclxeDHz5`~3Oy{QK{}=Z6m;=0}bk5#bPr#t3jfM{$<F`|i85Hf`E8i-v5`g@uI!b8>Q
+`7A#nhK;vrQhV4OqZQ8U6EGa2@Bp@IFyz_~Qit2`R|1AEOFJI;~mRZ!PQ>P%};erbnF7VRQQU{+89Xi
+Cn`syozCzX%D9rn|wPxF%}Pl|WYqyUY1gh?`Y<>#M&t|Ga+L2255`|UUW%P+rN{`~XLKm6{y@4ltHen
+mK<^J%JYL4?Bs#Qn4Q(|8JbIRN-802&lPnJ57K6*?5V0(EIofN*vImAM0)-2s0E{~bGa%%gJbNHS3b8
+n$oWF7StZoH%hpl);%ZXT&>jM4g~IEX<=vj|xEDKzYDKxuNbL&L^LIBJf8UQvK&BhnqKVa_5KMZ;*R6
+{HYEH{2BbOUAxAq8xr!b*Xz4Ljsd{`(@#I;n>TOfROXz<%%Tp1|0s`r`}PR{?tAy{6?NtK@#De{8qfw
+n-cV0aM!;?3Pn?@ia2|e;bIm@^d+owKs+{*M<$Umd&R;*z`PFOJj#6D||7Y<3_~VZk`TF_>(HMRKwU;
+OlK?Cpw&%syFgR%hbu%qn1`Q{q|u!HB|H}HiVfEM5l{-canUgSLL3(ifSac(@sx$XexfqRLDot!^RG<
+4a@dB=}9UvP@^>gwvHe+GYQ>m3066;R4Tfxl8uP!Aw4N#AhZ_c-T~pA-K<1C@dH6V3yOhO93+zgEq8+
+7ZsxA3IbNcZC1->(?R6^8y0{JCZCEfd(pvTWA3;kR#w&A06O3c#blH+(7=(?$Vr1AT*Hh?|)LFq4zP)
+!w6Rc(GWy(lXG0cKm91@zFVX^ai{oGpTU*(AM#KA5eL*Q6GhI!SCj?h3bLWlfjSO3gWRKDC?@I!p2Rb
+p4<H(1N&fqMDbWymnDgLIB^uOJ7rJlfyxX=vk3Y4=i#m7i97N@?0QC%QOMP@e)*F&9@E`u@7Z4BajPo
+<>9Mw_K@HOW#L_;*uu<l2GC(q9WFEoVzufP7vsh_C@pg{qJj{3@^K00pPxWSKnxQwrv8OvXo63(B?HV
+Gsi`JVHk-*G<ZH0SZ4;VZd5lgxxD^?9FEpV20D`B<vYJ+?@7np)fg4dGAaJWr$1bfosY2>3%z>e2zZh
+g{dCL!kw-4mtYvz<U1bv=F|^8p>bF3llWp8C}l#2$_b2Z#j=6ocev`RG&eET%UWQO`<lnGqp(%5)B<b
+klI#y=G2FO$BrEx@=tv{`uSVR1N9gEfl?Nz4|rE-0RGhO@zUp0`5QAr_$$*w`AVW;8PR|zh1$<iM8j~
+xW5`*F2BkiO2BkiuP4eGEeBZ(OL!g29ulkVc|6Y;I!TRv0_Fl+8)wKl}524N}bSSid2J{CF$rkWOJGo
+&_1b_QqM*cd{u!d-O*&^YecZu_{6-0xb^Q7}qeMXxUd$OiJg9fEOg9fEOqfP3zStN74F8-)z=wE1@<p
+9v2KwUcOt0M|6csAt+^Y>;N_}h;f`D@dOhN+?aYUKs~<!=%V$wb4*ABYC0`W!3Q=ZK?Ho236#>YtT1s
+VmXYX>+~plE9zF_w#~+f;!R|r3m<=U4#jl0Sy@QD73&0eXTMsgCF`^j2ZBJR1nNJ&JO195)E$=4Qq*p
+ElaZbl}qJ!)Yz00otac7dZj+gV~nmJ*N;Ei3q}47216H=g#xIrz#F&$cZC+T{b=touR*(sz6yOCo{t~
+X^ACxJ4YLh=-Ap5YlW3@0pP!{RX;EebpEtQTpPL@Z3nxVK{M0CJvp&MV`SP%!!Fi0)Qyyb9gg>?SLjD
+P#1;874!30jg8}$KrD`kQ<-g$hC{ueYt{&&m`<6DV_4~T~Kvxo+&3#iYaVWmvN5}AgDpkY#P{=`J0Au
+Wo}PK_2c;6a;Ya2jI-?W$k?(Pp3z!#D!4cI{gJ#v5;NjEfX-E|c5Uliz>;ogaQFg_jlT_?9_D1J#B1i
+H3LN`iwT|WosB;mK(+w=Y;cTvP}G`j0kQc8XhMa9wQoN5e<0ijxoj@t-t*N{v-=R{z<NjK+EHgKhEEL
+^G!j|ozS7sg1Gi0@AK_NI$laNY$h5+eI^>vCMorK1+_^_bHn-bIVS!z(XfDMc#>!+COMj87Pp6rHYvh
+sjPZU&{p~;SSLEMlG<E?Fz;Eu{xeglY(s4Ule*e9ipDBBR@0u6Mw-xI7X1PAU=Tx6p%QU<w)9`PSqXm
+;AcuBfM10FHPp#C|IVA&6%|NVsB`m{xvQ~$eY(4axFSy@^8fh+1G>cH&Tvjq*#^7Th$0uyzr?i{PW=g
+XaMJH{BO&-3N_T<5v%_y_o-?ECfWhjQSrz4n?Idtoe7Qc}XFPMzwY;dXS~u1xTtO<F(u4(c<}P)|G4(
+9;%tTDWjw8MU3fYuBzkIXRiHS+j<tU(U<R;}(mB&zm<-)a$Rm{#vv<7z@=$3-CtYfVK)_A&f^cAHak9
+eCQP`ul|kZ8>^-|>a#M&SR&Wwg;W>*2>&fxws_H4XNZD-j~+d=X=!O$si~<I)Q*aBSh;c~A2@IzPfSe
+Y^XJd!&pr1Xf99EI1U{JG17Fa9z5xA%LI>tBpaJbE<Pd!!^4MvM;-9?={IBu-tEbkCF{Vmm4Af_|NiR
+%~2l{_F{|X5SDT$Adp9A?JSzko+{-6yTHoQr7n3Er$K7Bf;I>GVYzkh!*M%cA$myieG4qQ=2id-q>0s
+0_&nCD<zhq6F@K%Ex#naaVrKEGC{J|Eb$9OJ+j??iZlgM*)0vSf)^2SMI)UFHP^1$@}BVZ2kPPW+*V9
+^y2%7PtTp)D33<4GPqy1pt{+(nj0*>9TQrOR<UiXK9Rq{`rkrk$ltB6ZpwbHu7s%uO6lHzT@?W#fum7
+qN1Y9ixw^7k3II-)khwAglA-A2)@(2Qpgf$K)nE+zzuZNr3Gsc=!YTq=%Z0rupWVVB-}7YLfL_qdLAP
+G*Kfe1KfC^e$W(V%<mBXh`>%if>n89_N=g!dI<3&~@WT)DY15{OazLM<tnDat0AJv*todSY02B2Abp-
+XHZhc3e?L7Cu{F(j9^*7Wl$Un)_%X8+;Ifi-~8ym}K&YUUA0W<*r-o1PC;lqauca#BWPyjS2^IpuwFu
+sP|15cQcA<*V5TNqOzJhdhN_VxAEu3x|YDa;}M3jUBy0Ava4&2R%=SaY^ot)l)$Mn-az$;5m1>?z6t{
+)khTj{0OvsT+z3IYN5?xk7u4vQySK2q&o9?o@vv@F%+GX|>vp#KR)g=dD|}3L3y4tQ%7O5HzHur0}@7
+I3ZiBSFaZMgV*3W?7$oK0PPRlF*b$#V62ULfI5IWfppOZ6Ye)DogyC}9}|`P%AGrLx{lEKG`>^(k+1X
+kN8u{~^F`o8Wy@)7fnCL-K2Ujck~bj(=%c`6C62P5h;|e_Mf?BWd++hL-+o*4f$zNYj)(_d6QAtVwvD
+Dfawg%vo!oE}IrJWPs=pEAH;jMK{!@KifOph4n7{?)jXFzmD#j9-nVF((rMfQqH?%d7Z$%DK4^S^rCX
+f@90or8e@eu4t7iEO<Kz~m3;)@!zn;v(Fy90k`S#_3SXW0gBpbIjDy70skPjG6pL_h8P05=5?2ki{nB
+0P{E%!vTN9ds>Uz8q_eD_mV&|19nf{FU)P@w*5#DD7f>bb#MzgQ)Ko^90Bg`r3Nd^acJX8)Xauy$<9a
+vIL%^4lTxJ{y)$6TKGH5KiY`8eQaG?U}BAZ)TmMX`RAV(^6kub1rQFh1X;&B`T*dr=tpQ=yX-%}UEuF
+L|E0cX0pt>W5a>|W`_X=&twVq0Ob2+oapOk*!V515c~<H$0CX#B^B6bb9WtZn52($2)y>WAKf&FBzjM
+FnJce-|+uW{904r9k5b;45!h<&83)w>330f4~X?*(1UzGP-@CPnbzl8i#|6Qcazivm1vgWDi?@%^yLp
+dlqIg}aNBIpvJg90tUo$~lA?aXcQcV7Qd=2EvS6O8XM{=qx+asbE{%-y?pi*^@0Kz)V01Ao*5lE>A5B
+klr!=k-r&CmicPpaFd+#xH39AP4A=(078zm~(>nC<kSI7v+F=wE1Xj(T>raLg+P!=D!<vfj?yTc5>d3
+ygRo=@K@F?z-Q11StmKVNn->%l@<Ecc7HSOcyJuaz5m64Jh1QKZ#k-m7f|Q2ZQ8VI)|lT6pEm6@W8sy
+ONw&Y)bdyaN*<^o{=~5?sr+f=!v-I!4KVLSvWV*<vn`}0gO)uH(Ae-T`nJ=4MGF@QS{%%Lb{=$R(J&k
+4XV7D17V!e-GAHlUc^F_Qvqf&Q0u%QQe`Ow%`O?`f>*=!y}<NYXV^RW#Z+s-iNzK1!<DboJBiu0*wIq
+&y{q+`QcFmwt5J2)S8kn=Bo5xCqA2hiHy!^1;IbGs>2r>oGO(U|ipwYTNeE?uN{=>(0>SJK#a5{-fJ4
+TC>HM;dQjns83ilVL3sdOoAPmV4|n=ZAjgJbb6f|EJsJe?PV1t0>PZXMMV|*F;%Am&g1!2`*Ecdz9ME
+kB~5QrqDM+&jtMo*2J+E`}q~l5C3vUy50_GryqOlv6N??d1e@Jz}N}%2lS6<*U=ZDt;Sdu;}rP8j%TF
+3MqdaW5A>Z_(}sQrYx2;s{Zamy1JE2ai{=ZHl=(g8xyTpeXz)at^I+WM{6Js!$Pbdf54y7TmGv!M)Os
+$}kq42#^WKNLdmg|O^j|-oKfzxu946K=7szXX<9_5k`GTZ3O*|{<eU<$K&hiNT5cG^8WPV;-COe={q4
+{eHjdO=N@54a(V+;&j(En^+k}1}}upW#xc_n|qm<y8L6Z$qGkH;ll8+1I7N9d@a>x7<g#3v%^UL}9@<
+22vTnl)?IBxilMGM<HrvGv=tBgI-G)^o9r=$t=vuh1VS)saV&tfPUh4>~5uqu*AkeNghp_{UjST37!L
+T=1-u^%JY6O6###E?wYrey)uJ<Z)OXc?_3zrh&3N*3r|6{Ar$)vSi7UVa|9UZ^$`JJkYmcO%?hDtO;*
+?KIad0h8KS0JeP0@JtFDaP$!`8bj}~+iWxIzWaa1QPjc410tc*{fEReMz7Ac}Te7|ndM2!gLLQ-Gf^M
+Tw)-TN*FTrz<r3)N{JRYvmeb>#ut}eK)4jAQM_C&bQcR^Q#HBGFIEAohSap+f|n-DrKIsaLyeMDY;o#
+Zj}K<)C!n5?9vBxUK+rNdAcG2h4d9CQO0@Brp#tI~zu5c(>ttG*$xZ7-Mg3#b#&k3d&gV2*O|0FQGU4
+c)D?{G)C?`skxs)c#LW^aGd|U~UJVpzQ%KuGqig`=3q_dIGFZuano;S3A`S=$UM?o-AL+0gsSJ%6AmO
+sq3}tzcN2VJ;a#bS<m9Ey91y8NN03(`#VC%0$p7Z;V@g)H99@Fw13FoX0xTd^wLYipc}$?9C#@6YGvO
+b@BlB+9>EU}^tW$6T2m+LlSe06tGE5f7<=Z-nORRf@kA#0@YGXJiFOa|F8V&S|Bwan1G0#`opnw>ojt
+~XJo^RJi8}I_5#i94-7bIhDOl&gnl5;Z`4!bg$69(_U6@ifD2rW-5_sjgWBk&0U)0E>rA8iKoIL{B*W
+3S7`mRd;!-fqz6CNHe)?J~SPESwgPd@pinBM^>r7cj}N|X<HbJ>1^AAET-|FAGZ>;+KdapjCY{Jm#$c
+*VI>oHsE32@4AgG?`2@K*#Lav$MyI8@ETTR`bb|CyRC91q&94wF_nL1D?YK|CPOs;78p)84uE=aG#NE
+-FE!fqeqXSRPV(+5Pd!LGSDs2IE#-QIa1Kw&{}idvQheRWp5=VkA2U#|G=O4kGyI8aRItStQ*I~#EA7
+*;4o#%6v2122a1kFkpX25S&;|oC#o|tGA58L=g}AmA9iET|3`DG4&W8>e&NK46L&+$gEji0Lx*yd1@v
+pwFFW)F;JYG+${rELq`vAm;^)68t>rX7|B*?bZ2ZyFL0RKR8W`Jw-&o6q3}P)EdJWVMXFUSS0PO<e0T
+=KB?Kav>;JIzvwx`&g=_o|~pEqw_%F8dmJWQE$qnv@mgb5S4pPwHeFkpbdO<DI-^g{>-xl!5+>`Q#_z
+4ve=<~?_ix5yuPqWvi>ES!Y&uqKZ_A8ihF3!_Jm7CL6s3xvf39w=+P7%xMof^j?1u=G#l?a06G8kX~#
+1p1@g++5K%V6UpOu88siuR%B3<fBKAzVavY7WpF|YHLLMUmqUI82H_H-{s4eEfZ_SXdAKSr05j?V%{Q
+u<fHVzkR{NojKNX&6ul&HfQ&)Mf%T_Nn>H!?1pjK@4*Kt84HWGR+CG%?x^?Rye?Jm0N|>~M@>d^xYvT
+PE0rl&T+P4Ou>JA0rr(J4&dkgrZZ+4x(-F3uc64X6^<xhOKt7;HW>JK|K<VW%txqSKZ^($Adyhd%*Pi
+xk!`S&ZYyi%*(dGpOTFHsuVufP7faHlZtt5&UwS+Qcp$8Wv$mMBl94?{Uqo&Lf(KJ;m2Wo1}ry+LW5C
+i%O9dWg9(<n?WugCw4m_CbZy`1-}OBB@PEe1wHeV+^N^XOudHwk)|^nloX1`WX#tG<#}|j~V)4<b}GX
+%!!=Gf+;^rbKp(C+|mtSCD@3z3H1oLD&Kb;-m;o6%9h54<K?*%=8u@aVy=L>ikjw1O9(IMS}`XF?vQ`
+v``YXnF~+$}bFde$NNHd$j=2}+ewg=R-l?Z~C&n-sQvq+ZTX-;s!FcigXQzlXoS%vt(wYv|5in;1|4>
+fogOoj}n;(xB<1LK$FxJCZ6JuS>H{M)YBGSN|qaWdc@*6Q?gwS)N4Fexe?|hkWEC?0zNsQewZpXX<bE
+jG6z9NsrZ#iE}e0lN37sYrGdqkjDLs>#z>W;+^m#q`y{#oN4V{Q<;0sNzWL9|=Y$zlJ1(wE|ScXk-Ra
+Qdj=C+1Ma=_3CtO8G<f(AGmvQI>-T4;DN{JB_mZX8TJ1-eY~m{?BF8`}6%Dy^8o7l=vTi{IM9TDf@BI
+Zez@a@<4m)j0^1O%bn#P;g#}N_9P)K^pnUR{SoE~NE_`k_5JzI@zG8LZ=?fWKvx8s&}U;k32`ycM&G@
+2=gwMj@yu@gW`s8ZbCZGH29z+3W$=Q#3v0|&DsOSx6HZ)n!3mt=4d<=7GMuZ4lLK(tnj6C@oOr`oYwk
+>~mRcvAwB{j)REcxe8p$D5;*_<<EF>i_&zw0fV}@#aW=2+CXh&;qmLYHaL~~|JUXRT5@wwS~*=d#@<F
+hjjDS4Sar}}qPWu|1Mr<wCC!_2vP>DgJK9sPT%JBGDrs#1kma;<sd&XhY$El`R_G4ssht-0xz8S*>1<
+eH~gDHn5ULT>uh^bGR^bDqP~**jWXNJogonWvgFR2ldU?U<4mlQlJavN^Y-%9<WA9#=+$c1%mj$TN2g
+3+dHB<eJQTHJp1$uUfc<^l}hQ?;*VueugDPBt%3-#SG{>e7J1+=l$<{M<;xm>Y0bSnffku2Xz;<pW38
+OP>)xyRBu;*uC7)$@_)ep7yrQllLMX&cs*caz@dOs0lx%z1$GGx4ICS24tyeTY2b%}Uj&{Hyd3DI@zM
+0u1Zl!G{WXthk~LE_`I;il4$WDOTTo=sprGkNuLZpq^kLA!pbJ4vtJ99uPS&o|ey8oJi_(3m`%UMg@1
+|d)PYNC#oEDrJ{8X^Jp}C=h;bB8BLxiEfA;FMgm};13c;4`?VT)m>;h5osp~B#6>}ecg9BH%~XBuBM4
+hR_%k`}Tz<a)^OA<aX-4#&^}SA@Nx-k~1tf5d-Ez}UbTO_}C<%~j3snx;Yb26YVT7GwzO9TXq*Owf{`
+RY6yR(zF@cT<vu2Y;BR&rv0~eiFSqdRqfl_4ce{RUD{8zUun;3f6)G@y{=`tX1X@I_PXvmf1OSjs!P;
+8qD#?D(9O{;)GgD!ru$G=syn1Rsk7^v>mSf}*GK64>!;{f>)+RJ(Vy0P1wRmc($K=#-Pqsww(*GZvhk
+)-6A~4Y6_Ot^FXZWvRUsQfJ_>0X+CH>8v{BgIVS~bkhnd4Bhpi3U8TLciZ(;u71HxB^Goc|FOKsUqb&
+-0hdXu`Pe~^EKe_X(ffP#Q01C|E7A5a#sC*VTB&jC#X9|-gf>=9@RObuKeSQY4_>8Ke&<&>())lAdO)
+XdQ=(v)g;YYuAe32GNKBxpp?*dTLIM$nX?{Gii8<w2K%rfDD7KC4}+U8~)rJ*YjQJ*~Z{{Y862>!NF_
+yO%iOt9w|d)rII{h!-Pt<8{Tl7l;Q;-$<|4YxN=e!TL%1h5Dw!J%V+?*}>C-OM`a@p9($`TpoNW_*!s
+vu$!TYp_ReMaKE9mp{K!M2scC-VhsZfLkuGf$%a%zo}t9>6iL8R!)n88BnKM}TMY*cM+{#YP8*o9k?{
+dzgt4D7!B}eCWjtv7+Q>pYLYjs=7-9{1G~~sQ6(PGqJ_$JzGCg!w=u@F@g_ecx2@MX53mX%*CTwfip0
+KlF7s4vTZiWSfhldXdFDCB(1pb<EKQv?Q)zRuf>gUzV)UT>r2ZRQ!58NCWrs<;@sY%ngXdl*Ub@96Ib
+glJEf(H_|EsX7rPa9VltyG#FLPt}6n?k+AXM`^gUlV>P92ZEer!-ru@1v>yH1!<yo9dnFvsBx({{8%?
+`Y-Wc?tj|<NB<T9T?0l0%n5iS@L=Fqfz^TiH7hmWYRWY(K@SIw3MvXp)|#~+>psyP)3woe(T~wj)W4=
+*um4zoNPj~Ag5eFr`-Tq<A5&dX8@0y4#z&32jh`96H#P~mH>6|8!yy47VIdPjvWYh@khHxM@_opqkQ*
+VI(9qDB&~c%Kq4PrD4=oMd6?!yuIruPE!nuR`N#em4^+EL+^<{OSf1-akO{}I+^SWlg=9s2J;~f-8(!
+zA7bP@V^{Vx5tdb_?|@TB0S!Rv!J2k!{p8+;)6aPY6e9SosVy8R4sRI`T|h8sp1#u{EXtTU`Pd_eWPq
+p=5-<Up$1!;Pbiv#4%=Wjt*>XS5qH8Y_)gj8(>JW1o<?kOZpn!$bCm+#7195_O@v@k`jG@S^Z%!w&-I
+9LCm?F-pBey-uwP@TYccdcb1=3j>w}tP1!t;B3If0FOYgz>a}^0%rxj7q}}hNHbXTBDD+;1-%sXdeGL
+OL)2FN67*Y8D=OhW+JV}k+A-Sk)IKcHZr2{wdQc0np4zUnRL84yzw27+@71@blISj^y^nsN{t<mLl}f
+IDmi`0%XZnl!pA1(GHw>GMkAzGQc`3v-v|Xqvl!-|d?i3lW9_W8Mpi|($z?{Iupl!Mxx;<37hjd3NkC
+VDiREuKt6ZEU}pXkQ~Zw(%6C@|z0ml%IBjtw=0H4Qh04+{Tx_`9HcA!8wA3{V%S*Q&etKk5IL{~mw4f
+75_OY7zPb4hp;)I9}66>#A#~3)A&A*o?bFnuG?0CWV^9aj@@nw3+_z_`mPJ$$zu|Hvb(I`=tL*{@mX;
+AUPnH+LhM>eh=sz7#8>l^#JPww*~$fcq7nV6QvoZ`L|}1W~b(&=7FFtL8(E*wWGBYw2QRwQk(g+wzs~
+Yeu&;i6m$sg61+0_HEQGi4f%$CM#^v<(ND&D^=|cX^#$sCI{It;`}hy{pX5K&e}VrB|M&cN`hP)G{N{
+gmz^H($0lNajHRCilG!v;+I<7shb=M8mk0NR7K&{8a1~v5>_afa=>HgXwl+q~wvHq$4IsO*^WB$ke&l
+xTnt{AEf?#AZE)>L18sV&wRLyS?zIFh?j##Cd5(L()7k#Qc<IV{arQ^O{Pd4_ij?-xEXd~W#K@SWj%!
+|mYtX-V(ysrFTOQ}<I(R9n;wsYU)keT?X-Qv3Mp{i7)V0-~gpYBvi|1(*WHQY$?#U@dl4S5kX`yUNu*
+>TW^ZgVaHqAbn6skSQoCs9#WAP=a=dcCYq;_OSLCQG1Ho-g8<zwZE0xE7~e;HT6dBI!|45owu&F&WGA
+!6}86QsBcp1G&()C$tLP$`|09z3A!P=;kr?}vAR^<L|uk1M^~UL(#_S)qh9M7-D2HR-3r}m-CEt7x^*
+PmALzDF3%*mgS9d^nnA(}+RIg9#&XK%d)K%)PkknUG`|hsy)Hm0A>s#x6Nd8rNU+T%{>KE(RlGN`dsj
+t#k>shdSuxGG7R2`-XGf`hQDr{^Rc$pEF6J`mU9yT*<ZrHrAg<;QxEe=~6wjyjb^>lUT1OGh#JpVlZJ
+pVlZJpVlZ`Sbq)P)h>@6aWAK2mtey7Dv>_3eOd_0001R0RS5S003}la4%nWWo~3|axY|Qb98KJVlQ_#
+G%jU$W#qkkd{agC0DjXXZTjMdM<6^Z5rU#ssaqedp_)q*xRF#*K&yhH)yl4@Y<nZ9Ag#C2hB3SBuDY(
+PyX#|j@vXZnEvVSE&<D>_QHm&3L7Z?w3TPiZdVlB4Bx!lL`~Cg?{qdo>cV^DK&YU@O&Y3fFi*8>b7zI
+Hv!Jn=RLao66xrP7zuLu4Hja@TH_)qHQ^J@+M&F9aW|I2d6!m@|&FT49eN6Fm}J@l~RxaU4cS>PeZFC
+TJvZk*|O@Zo#!yDT#^%^54>yvtv<-9F>k)c9Y^c@Ipz1D?|6;;A0?ylv`L?D^o-ned!Bao*Is80^uhM
+ixIe2LH?0^C9-U`Ija0vAmya`wXuj-1}6r@ciz<v*U4{!Z;y4DNPXSA@C-T%ex4EtQ;i<A@-gm2xj~d
+`kz9Ffgy3kegnj~E905skF&$4y$W)ML70p$3k|~SGvGPTAnZ#W0MKd>?u9C>dj(<4{~ntDe;tDGLekG
+CU#8r*Sb^ttb+HCv-%Y2(IG{C`mEC){ayQg{B`;IB3jP{ThjYXK%Q%=&orf8r!NcHr@+`Qz%NBA#)<@
+{8kcRae&MvR4ysQM?8BPE<!p-m;buPI37Ca1qmmo2>^05#WoCP=K=P~jB&ws%dF^EI%CtvwOGp$9TLV
+HnYx?|?8a^*o8a3~Y=H=;q1bJxkCDrb%fAvdf+?E1omb~)^K=6J$W^juvId7KV9`)bG%&ivvaa1-0aR
+?OcHMf~GRC?Z%FxI_-_Qm&W7w)F=wX`^mCxfNbQ=3opUokcE#NNv17Y+i+6;aSccX|+`_2)Qjy9_Pf`
+bU`Q$Epa;Z*3~$`y6tL!-_)>44$W~|<dEXD-YTmH9mKq29F$6q%j8JzE<b7Uho_tr*S7jd3;lc$cBC1
+qRI-TqJqEW$av6N0XQxk(__6`mlYdfcm5JialSzY2<~Va?b)DlbUXK;)vCG(u+0ra&wsebht4t0;y>3
+5=Leqz`rrTcn1r!}F%JJsz^M~%sp$9F3AdyYMI!No$1VMZ@LKpYxx=a8j>~SXLw$ayzA##dx=x>Me7k
+^2o6kdB0z=FCY$|t?Kbz}!!It=@O*_JpB64@cungAU>vYCz;%$_o7rIeN3jvXBf?dZTdhG9YpOcuFB_
+hp}wjVGc{0v<nt31}i4sUefs3RMD6JQ0Nao&B|@#A@}B1|Qi0Sfg)P5q=96`WVy(y@S3@hQ2|iP__76
+gg$=?S^>4o<TI%jIRf2x533&YW%oe~XiA14WH0T+inEs<tUQc;>96&7LwT+7KJLJ7=2#gnp|-9A18bW
+IwcQY&ldTuIzAhUj)|ih$a;cPWRMLvF_dsO%QO#IH_A(q#!v2KkINd(7&PVRZ^O16ReO&gDz0zIMozh
+%s&fJDgXV8{gCSS^=SGRq)2`W~{3qY4S$Ys|W*j$2^H2cX$ne2cZ6V-d24xrMDHVXo22O`hVZKoig+L
+J7Lb|ZlmbNZSA^yNnSQV&iHwgiTd%u=A9V2fhOZPRVbAxkY54^R^uVl=qKX`d~J7dR_mx>gw2bp1s}Y
+`S_ELKAb_a$9uU6PvJ;-VSN?3W3i_AcGleQdwXlo4UHq2S7k3gDaOfXCPQ0g-tIlDYpes{81D}ZOaK=
+uPo{mat0O`FGjb0x{=ZR`v6Ls=_>%7+lDM+A+U(~^c7e-R(I*T4#-+KpH18!Scch%F8U%;YS{J%p7~z
+Ly!s~q)@>~us2PC*9OyS3=sE=YkpaEVfm#u01_ugppiw6<l=)AHT!Z~-^1cnM1S<dCMn3Yze!!)S7+L
+6rt^zXLG7MRsfWJ8c{7e*p3SlrA|8a1hvv&jQt0#)%-30ag>$t9aU=-)eWR|lS@Zkw%{UgT!pzR1s{L
+Xp0?b40>wOHK%jeakdP>eiohHk6jW!ibe?}h>)5#n!!_)S2A1V*(OsBk`v{6b(piyfTxz~C=5vcbPHE
+y1hj8L^p8$olCxXmM$No?;H)*r%n4HCtu1&Lq}sLu9=NtUR~N6E3wN|4sFWFKzjrbKm(%+E9sX7i*g7
+{tQMMgM8}xBuT9C3Z($M*B`#}lS>W4CpaZ@TYTgT4_U9<j<;ZC1+PI0X$_<U(2MXYs|S+g@H5UCNjMc
+lSKQKQ&dV|gGw6HR%Pr9BDbay*_}*R|HPysux(|ue)m}cLB0p0REArEnG+B?xuI92IG$Rl><PYMAkAN
+aD^M#PvU41nUsXP0GpEN)>R!2_>f`@DkEpSeZ5&S%8DcQnUaVAz?(u0`~^yqqNNLk{>dF3w6SDbDIm`
+D+@n(g%KbcTpD84yvL9}oijOVy>qc-Dbl%GvoFP=F@JF!wgkkck=Mh}(yG&S!akiscCm%(M7hc>s9g_
+lS(mx@{{A>PO=kr|Ce>{)^K5g%;(K()?nJa)DSQTh>^R$L6qd0>dEdq5ZgE`<)iu_It>zHIgkwK`e9M
+evYyqyLH{CuCtWp&lLjWfmx1mvIb^+&l|WM(sA6J0F=afp8(1Ko)_@}fM`Q_)gPwC31%G<Oop=*7}`D
+@r$dnAYRYpg>&CI?4Vf8>djR5c>wId94(qg!oD9!MDn0t1Oisz&+x%e(V<GHgVOekS8zb?UzB6JV=nK
+!WqyX{IsF}^I%V><odfei3fCG98_>mt-BF_(uI>#T*^iaequ;b5Uveh5D7l~y72sU-ei2JRy3dnU6&P
+CwS^nsH+aMr)lQgLh*ldoh~<}1ktAuw2uM69LV5nT@?9cz#yE!OB}5oUG2EO9H&WhgdBhFTq77+EH{c
+ykX)WW5Y>+H-X<m?P+KMgtEMh9j0o!)tC`6qF5t!L%_L<tqr3dh##kp)K_F5uE1()z835o=LA~W!RCs
+e+rP71;){<6UE}F3!E`I=*JMfVnuG9HWtJsFEAj!=Kq?BC_RYU<4j0t!Ue1t7qfY=KIP!D2`kyAP41Z
+}iup(#h>bGY3H=Dz=*Ce%zgShsPH%;G(x5q|+6q=F6VLV|2M-yMwoOoX3jGs8;mYT@qhXVlTI#A-2Fq
+#$riTG%$6<&Fm}C-Z$gOLL)d`h_Gv_uV6Z~{N8+K(Rbj{U_wKVItCu5Ny^60Ixk~byt(i&J*a828_j`
+7~rNY>HwvM|8khm65@4o?dltW>}<?HbEgL;BPR{@fi4Z;3tc$>NJH))>7K3zS_O$}eRC29sOaxbKt+>
+wXfpt-&I@_LLnuQwHVWxH7Q}P|66v#ZZ}~1DYY{ws_9yz?_=~<dxNevdO?=$xa1i`ON#_o!&lyO<fCi
+{uYb~IvGv8+Q>xva(K`u069?A?GT;>sr1Y7n3gq-ffUH_&<35W18PA!R)?Z!W)hZt$jK@bVSUYQkzlU
+twgWB@^y|**E0euEK*D^YFnJpUF_<UhOAqN~V?v)mYE=ua8etF&VwC}^YBG<*03>><CyapC;4U$^ol#
+omd>5|HZ#H0{6K6*bG+3;9ji*jwsn0ZULdO-uPr9%b6a9o@I8Rm&TbKqQyZXu&(){xPG(RPfr0vIuyJ
+RBjHb2YXPRC$kC`AXhlG%9;mi&u`n0A!e&cfp$>~|dtnEfuQeC#L3u-g-5{>(|#8EcF7A`Ho-^=wG2@
+SrtJ#I{|+qp~pSb%<Kw4enD!j-7cU1p$JA3!2P>hjXOxglQv@fo!D>CsBOgs@wKQkSt*c7UGOvjB;sQ
+7>4n{!D@?^_963wcNjK9w73t2z1$X%^bFDIENK%Hw;+ntNAoczF}Iq4#9$`O1FC`it99gGPzW8$^C9(
+K&0zf`0{D4<JWvEkz?OQpo{gYnI76LoTNPoFQZ9snTbw?JL2h${d<P-`JvM=(8ZwX0hX}<+<>7qxr_*
+cU4OpE!WI;7AUd9=^B+f6jw`0+gPniTBY>`E8FEq_I9`)9ifP)?bN?v^)VlxCQGwTz54@FNHZ%{8mM#
+L@@ML4mcE}U=P0}09?WC|^#`I4`_Mc=g{BSsEPUrW5oq){6}Ujr!mn?z)MmM|4HHo1$Sl#IN=iUkBDp
+WYn!(bwHBkFJx)M;bPPj*q%M^d}FL_ldqxVW&?mp#l{zVOm9AOwV!5zGdJXbD%;?j>sWD%C;MdNN*7?
+PDka?EayV3Z&VQsn5`(-6}XUg4oj>~sGssCA^|xP1a#+ISYAJNGIk%-l|i5*sLi^s`$xa4`H_ocwNF>
+9e$trRhC1GYi@0(LfUgYz>{;}jk9_45-`mqY&7Q%8L`_NdkQ<#^va}ivSsrNaJQ-$tRWT&TprkdxxRx
+n*`b!pByumJIhFFc_ywCN6N1XDLmu&I7TEwRhFv$w!hHbb8&Zj3qaq(QV$Rd$OKj3=4oK0mz3Z^aepj
+huWcFNh!(N|bDNcWP6&qd4TdjTyD=!WaSBk2;U_mZPh_Yo~sChI-qCtr7`C;O<DhOe#zUUAw{C|-6&7
+Cnq&#@$e*H&z`8)=1-5AccrkC|0OFy7B-hnb|b@h>j$-mA;hCql;Oya0Xo{RWpRh`evWr2!avA2+BG0
+aVP=iP;?KjvxxA_1j0{T2t#-AP#i^+Fr!xjh<c;lA!mOL_y?uG`^V@+9%t1K_()T96wca&Zn6-hnZ<U
+0Xr6;hGd^;p2zVDc?kC?MvCKnaxdW*JM<*~&R*&mSnxE9?w$Wu+_q#(96y$<eZ4%CpQy@>SV6^E6fe~
+aKv;b_PHz3pFVn}&96GK2-WhCduSs}{=bXh;S7bFuVlrY^IWId2<icmwZmq8DsZp0xc|4pTkW?}rJ7R
+}*e&6lOF<^T#I-dg0C5GuEVqV=)a0EsPPjY-xnV0yA2#A`P*R(VK7w>|I)@;>OWS*(Esu_hwcY#bknG
+nk*(<zn0hn2MdgIyixgN$?8PAH5ywqEm7q@VG_rmvk&OsU;R*j}qC0yq6YU4eYElShu*1%(QpcTL3(e
+mR)WkjnT2V(!2a&^M$~7$l>e<tX=g+WilYr2d#<pnALB_`iQAcexRke%VV)rC^nfCTD7!_ytF`)zFyY
+rWS6n*2u$?RWPP+1npM@d_y|2|jkhv-JD{?w9@gIX1m40xO75ZPs}RV3+*(8q7Lgx~k6V0^KC`5LH+D
+KPbrwBvs95Uqn4z{M@m?g+rB?}pt1&Ph>nB@fV{^O_O{@`-vLoGvb~H?Dg=ir90`gJcRcwY-e*yGZi)
+MM?5`()6?G_~+fkDqDJ&KV$?v%sk3FjMx>!Eg5vk8QhrtI#p=Sm?)It!3=6p)sIeu^3MVyr91AFVil;
+<DOKptrbiZlg!U#DF~^vH{aZQMUz&7icsETE?k$Jw$PtImWqT>&=3=GQd_!$u5x|V6<fCN;P1m&n61T
+a(|_E9L_xy0vNT&5J;tO!dxJyfE7XU=Us)m?j3e-)via=Bz5mePv{Q2HcIMhFet;|*P;xP#5H@<%2Jv
+Noro&TbSMWiH8E~;>I%kAKucO5_Uf@^W&w&H3R*|_S~JiN8b^pk+OfgCr(tE+pafRt58|!AG`LGiQ%@
+Kavw9*a@HHKi0yy%J2J!`RvmX#q?ToLbpnAX&b!OeXMs=$mn=iv?S${=(rXZv@X;Zr-yQ*A6Gp=NvBt
+3LW9`A17BqlpNd^J+dqh=^Z1JQZ<MC6@mba6NGgp!GZT4IllRL&Xw9zX+-c|eDU(F2)@b}Y)ok=8DNc
+DR*f^=`YMb<)d{V-#_v9;b+ZLzMQ#0E&p!gNFFOF|Ew+%+YPm)v$_vRCGHNw6ALirQDOBbu~OeQQD<l
+E|-`~As`!@D)VrQD{x?SI$B)vWqV*x?k=?!mybb@q~^bXn|I=K4WXBu?_)C;k72UaIJICfER=?TnT~^
+))wtC}Ye&Wfn%zfrosJwH2i>2}LAyaNS&f@VG@JnaH-df%I?xL0nZ!_>wt6%sbfHzxHmjD|{WnhnC7`
+;1y@#MQS~WQa%LLO6084+JAB#deBdvib#&7AB$507TQeY5YMYzo{uw5SK0)tosf{C;mca%J$cY~kQ;m
+Wm@%AJU8l%8SX!ECvhZ7Td|qezeXF~fuqut3TLh}=Y7@Vf#B|0O5xi^`Sja3r`6h5CO(0k_6RZ)SokB
+MBI;YBpS%zfMQ7{;e8jn45!ES*W@gEs3y5%wu*xdg3)f;Ci_VE0v%~auW#o%y0mbtIU-Wlwt90A~3Eo
+>AW0R2mQFTc~I$w;&NN)8&H-uP9{}O)_{<iB5XJoRO^7fm;)F^%v)F~=IhW}2=FU7yc=Sh%)=ov)g;t
+4nXPGDNq`Jr|HRt~0-$+k?0q4~->X3IHdg=~ACl4nT=aC?^Q#yJx@%GUu7rSEaC;hCi8$P~1(>k%Lo^
+;kRZJhhc@@JjHWm=f_M`5w_(BiZeW7v(?oFMh_Z5*xEk)#VBAI;T(Y&JU?jrJypB#`$bY-a+JOqXKN`
+5W-LerazLWQj$KD(|K)k+i;je!3-O&O%H$M5PCpIix`r0^uZt1q!d%Or?ZzsEP1K^X~3EKWQh`P^r0(
+pzzT2|?NuO65|ar2N5-z(i1L`z}GH_B`nEPw+<V!@2Qm4M*N)-L@N|$#j#%?Dwp!t@QV4_zFWyW}5sZ
+MGU1AF=C?6xWiA@;|315Q!WHN(LVq<h;eFz6?;;Hi*3G+rm`qfZ4r?ixre;Dhl;YBRyq_{Zd?9{N3N8
+^H(H1UeXA==AqIFnlA(jh6S2BRl-srMK>RV*5r2~vcxN(M(3GJWJtT^FlgVz^)#bBgy+MxbHOa<3q!5
+Je&a!-twerio!9$AK<H}Yp#~0TFCzXk+O9+faT3j5SYWTn)LEB{yV7AEEB<Zd3rk8zK%Y`Mj<8lWe_5
+pa758Q6y^DAtwJO-2y(;3arW*dZ$aOVg(PYPSzHM6Th^f$;Q&CKK&GXEMt=mHlbW$$RdpXdzF0JUyoC
+~Fob-pCUt#u8I%N1+Am5w^cE^cbcMGstQ^F!~7SPa72w)y*eH83b)Vo3*xdfboW9qYq@qu<aYW0S2(H
+UvR2N!O2gC8GI<yro(TaVlE1{1(KVRfI;X!5M7{jpWYJq1j=KQAmbzBVdbqTSe%ShWTqdwnEm0&Q3lR
+j4s|AUXPFtb<{w47;?A;b0Z9jOGP>Fq{Q@nPwQs%2{#`6*Z<D(Z$dThlP}t;1^t!Nru$9gO5)#)uU>9
+Rmho`e8?G9O=VEZ>H{P8-zRC|Xl9SUSZFFP~G^@$vN|Hbzt!nU19un8N=fZWV;S#z2fT_mpgHRQ_PFr
+ZD{(K8wp-D*L`6v=_&UV`GV+DvgxnO%gsqyNH0tWck@ELK-sMzW*O;TXsda>$kM4l@Ye+%6x9dc#j}`
+{|dUxq?#XC0}^SZdfR<38VoPCc&Fa=@M&<d_Jl@cPlw+Pnj}a?OCLZpqC~m7VkA?W?(G@a{JU^1X{_I
+NS~D6saZZvg{<n9BrH!Ntx#AgYG(})8*CG+e#F(ardjQsx$IHpS5o#?v1%Ju6-`&B=`G5&YSgY=s7BM
+2k!r6g@QfGo1pg_Z&ey7LwMPGr+1DISi6TdGcQv`iAaWgbt2?@!LA7g3AUY}Vi28k>y8ooQHA!6aLyE
+Q#3JTl<g-GeTR-#5{DL1OoLCR#P@?xxegxZ@d21fxcb~g!iz>lwjS_WzPps5D3y`))O(=|xT#!wau{V
++(&ia}-oq^ODFn(s5p%@X-SGeJZKc0bnlpb3igqQbovXO*cR(?GJPU<9RFBjbmk(LIGsQV;%)sh<tW1
+A$9bSAiH@#8VOl*w(QE%zUSjCbI(AXr@6khG;hc$OjC>5(C*gB>`dukil9ufc%4jq{Kk#rtoH@@yZ4P
+nDw-#c=B__;6Y|s^P^h73B+UI-na>fo2@ig;sxvk5Z!ze=vu(O=Z6CJkavtH6tMV10jOv_Jgo3Hkt@9
+*MklOlIj8fy(`-&Txx2#C4fHJ(6?1hoGr9_Xa{*|b;e*)zl5Sv5cMeA$F}YJvN@dVyy4-d(FuU7r7b;
+-H{NZV*)E?_1YxqVhU1H|ugZEkAZ4)pf8Fmz;qU4jh?k8D8ha)#L9m6t|44Je-67FwkW*MnXZ;Cz&om
+^dv)8gqXxwRYj@6BJ@P?pH(LAiN0&gM0L6LEEKdzTHsdBDnQ5QJp((s2h_UH^ozPj#LUG$$1BC!X4ad
+376k7p*8+RR}`HoIvp#okp@MOIWW^*#)`W%68qkfIfXpOdzaa{wfUv)PKM*vma-E0=%`MUtgIGyh9GZ
+?X;qSuJT}~nF%>P=(2&Er@;;r8bm?$0@1!#2c8^8edu22LMDbQpr_V8i#2W;&X=-<a@cE?!$nrUm5c(
+NZu{UJ?8)Cz14oGumN~SRQp=0n@_2P@lNnY_H<mgN#m5|cD@M;02u#Ehi_lQucRE18YyK0=t1&VOTCl
+yLATK!{TFFM1g`kTIfnXFl<gL(cgWo}+jP&R>{cWyC$hz&|+iY~KK|PS6p3p&x-bh#GVhLgFyqvp<O5
+ed?g<o<ac3`On*`z!Nm+3ioaXZRH&OX<S00~YC%Le)qE^@ZEw_<TfA+UY{|6cU=>HNE2#|5o23IuYV*
+#P+7=|ffMsBRnaHfo|9{U~~aO0*f#b-o|PTz_a5`u&`#rn7ODVe-x6Zb*0POdeMc=E=<Y!kgQ|jMjj}
+Mg7Jee>n5&ACSTO$Tm;dZE>Y3*?OJN*sAtiT6Q!n9a4J+FG{Y>M2WOA;(?5{O!@8w<#}pdgZzR-JP{M
+e1-&qZ4Qn73R>8xEIRmV-a$gNhPZ^N@Sv-9hOHbIv_a^tlyqACp+3AL4q%+$e568Bw{NWi^7%&$!NeU
+$;jMnGcf2=&%4&zyrTUUg>4S)<k%9*tKk4)Oev{+8a;z2?}9R{#$$GsM-uPNd%|6!zNiNUPs2j=CU&b
+OFD3QR3D8iyxzB67QOclpRkb(zHwxIivx<jkQ_4rf$ACoeOihLQo)F6y>d-eh9)1t4^)O;+`Ub4d&Lc
+j-5BlO@~&Ak6`}uK}c2;3Df_0JUA`vY&kFWO`KAd}s#&c9|`MusT|O++sk(7Kr87y~*TrO1?*$Kz`C-
+mGv!w?U2d!1Ex)9Y%LvBfk3iu6A_-lYz^!^w^2kc0b$bIbqE!W7T{{lK>l@aAYM<$(QGDamF!&0J$ec
+@GWBQ#eLF)C!Z&15Pg-1&{?%Tzah{NkFc~j>84C8POHP5HXd;c;qpK$(NcW_~{)R{&g2gDlKcE#eEtr
+-H+}^;m1}=@U=t*zz4HaArpWY9;@GtuFc4?Wu(413(mP{8w66DUTJA|2z0;AE?a}CPe)9f<PIXPT#5P
+7O`7c5eHgur0>Qy<$nw1nls^w4+Mv5Y<awiUqDGb<4tFkkLq%)GjlZ?`BWeVZoGTPAabo6k9rE4DG`p
+NCg6x!!6t#8R;xnW-2IWv1~*I)7xqgQb(q-Xw_8C%pMjWKL)7!zaYboowlym)k|QsRxq8s`bbK4kRh}
+;7<|$6sre}%1rfuNx4itkgN>XHz-%q=m|t5lOZ30SIrI6p%UvOnUBFE`V7;7COpBT7QsVX0^w2~z6-+
+(==P+<W|0L=zh!KnV+FGYkq!P5^GBZuLjG+gWhm-&Ocm(>Wvw0^W9VtYax;P@<+k|4cZ1q|iBrS*Ne!
+&FZ{WeUwXmbQ?ZtoKvPFIBDMKIygkN;tEeakAq)Fi-BhA<coHeK`7GR}T7|a#w35zmVJ&~@!GGbCv{6
+Gy8wM+bDEv`p?C=wLpf8e_Skb$8dvI<3zrl;^1v7nhf=rn+d`6Tn}k725A!}X4sKZkc?vuoSZi{+9HY
+&Jc`x~|^aY!UQ&TnR|#+W@3}Mn6HlI%IM}%Tl0akAUFvDN2H_J%LQ}Jk~|FO8V!TnQVlIYmYJro16+w
+7K3&jeLz@yU3<h}Dg-?t*2?H9<~_-Lx?o8?Q(yyf+AJ`+;`C>~u0}FH7SISZ_3xZ<ThUFf6^&N4G1t1
+78n_9Vvp(UUf9LBY&^F0Ds^L<jE19~j2IAGs4v5g1Mkt=(`=n>O41~>Jufds6iFi`)whB$s80PX;*$l
+t<)>x^)qp-2E8y}G2r6Vi@nzvSGvOSlRY_h8u)2YWIFF;2-wk0`+C5Jw$WAjoE3<9}HW@V@>iV%Fze3
++;!c#Z?db^Yi34|Lm)uffn{CTnP%*KKQGPcSkWB#Jf7FazkHvg46TF3H?=fO&}koif;s5Ia1#jdn8G<
+1ZMePPgbGqn1Md^Np_O?!x`-CtgoThOuVE{5F2A$K7L*Ct2xj-(Wbl4U7ies*4PqdH3%F6JH*3Br~1G
+fTznmG?j;nc_@#E=JU|SJhYI9#_-T$9vZ|#q$0-X%=A<Ti{M6IbNXtgQA7N95dUc=BW3#BaW+bXnI)O
+r2wBH;>Z!oY!{#QG69~pa#$@_-2bwXr(s{k{QM`p`kB_2~jpot8Xm@ZGJO-JSQBcZYTmfA@FjcM-KqL
+W(Ke!U3It=~uVA+__!ACc4w2tBRyRTq1+1JeswDW*Zf53((<u$a^jEwTFN|_BW7u`R+C^5#Dlgw@!8{
+<{4GLhgswBK%|8K`F2*1Vb+V?K#e=*E+12s>l{pIL>%*W2lB7YQ-n9Np%9m8}#Hjb!%EyYLa*6)=Yj?
+OHNQ0Etr0l4;_5bAED!9^S<l#0y8_<o@L=+!2ptiY$EFE37X**E;dZKeIW}<I6tj4?k=Ho@x=Fe45z+
+H_@FQ#@>Cd_2skI;Fh)Qs+Ywnt+KIIcD0rbMws)V?YX;<JVybu*d>Q=amcPO%6yn+zKAoG$#O(D%Hc6
+R%#2#{3hUuB%s910x6Nc>NC%ABrt-x5S>kRikxtshDgN|5)6@67a6Q(L6?V6euIp~sn`3KmKmQ45{W-
+$6B%Y1@obONn+&e8_InTG?|82g#`vu|CG=|=E-S*($crP&*2xr>q3}?VUnM79O?n!CBMKR`r5MLrsmW
+|E1b(Q&Sza(P~-!HkzE<Rg#tYMSCM4AlRgP*kdjS)$#aS!u~HO0x-hy8-x3wr5!<faV2tF0`JNETzgA
+I43T^a!lS6TPmDW#3{ABr>24h)X|-fa>w&S{<Dk>93eCl`^g<JL2QsZ`H_@&?}0#oe?^=yH)Kl#Fh#s
++xWxg58vc^ODpIsgP2*-s`d;oJA(DSfO0YM)LG)Z)_R7`40M7SOtdg3?-t_j!fuEmz4UKrQ|{tZQLOs
+GfYPXu4xffo)*w~|ViD$POmfUr3)Xxsps&>&NT}3NRuHRdnO=J7wq8)8KOSa8Nyq%-TTIkOAKJN7`Od
+f)9A?0@OTR=w=Gb2vy7KcA%tEf){`E3?1ub!Y5A|FOB|HfwaKHaCQyEvV^=oykZ*=|<SQZgr4~!)46R
+SOwADM;`TfD3hS3?ofYG#EB`@J8-$oR6?%iDV7Y?wPcXuId!<yx^^$bRrmY}$6z7$z{#faJ$h5G-aWj
+{(MMTl^;W$LcUJ<>IIWf&?1$&MbB{+I7#2e0^XfkNO=(#oXLKd5Q58qz=#^fQjBnzF<C1%hRQ5OKE<t
+GFY!?cIJkp%Hux*O3S`Ojo<CA{*N;kSJRK&+*Kim*(A`(6$C}BN4G6}84G`gkpdb3+@Es;01^u~06dW
+6BjtH$1W4GdfSwsf=}`}(5oH7q?&bK%QVU+#1r!n(wi=0pE&v|R^_qK5F-1Hk%R_$|4`eR(>?nn9>v@
+TBs}D|bK6jCB+YK?3lgmc|ul@5Wfq8f3NUQOv7dltfzBKTJ#6UU2hnvaAagEHv6A4o~<nAM~tEpV1?#
+=<)(p*`5ua}n))Me4rgL6*rSY(I#vZ22442z#M>9(i;%=Pq3)q>FwEhr;#bu@P|-DV@2tNztw#4@!vF
+_6pyXYC;mSYOx~B7S~oM((a6(&0zD$Wb|5)b0rvPDIz7UO6<kmCiA7xyweMI-F23hQcdusy|W`+^3l3
+V3+cZpA^Y{;&rb^hbO=58;QB@iqj%cWNVX`PGN#WpacF|L*8}}ijk_il-yw5;`8;Q_Qe*Ncw4LOniWq
+~fJpsk17EdGq)8i;Kev5K^_&ZI@#ThaVINw+W&nWzn#+Mm9?yvp$lRavbK!nLQ6K0w>>N!I3R(x5o>3
+RD;a2`gbY>3jWWy5uj*CWORSSD*6RS{e@wr+bDWVlWAdOBnfQ<27AutF#T7l;K`QF(?ZW1CpxK*ec-I
+jX#0t@K%kW0-QCkAmXrjsEQvTIM!+#lFck(UZ_LEr2lj#yz0=P1ktg*AG{>H?NH3Z19|c6#&>D>sl!=
+^@l29NI;}x<@BArZA{1+I9%X4E?IKbh`dfVgPSG#0KzZyian_+<~^NxPfaTw`$S9_9bjt9M6E``GDhv
+j*uKz3^o9Y=@3x&$#lD1(%BVxGBAQ(&#K1#balGjfx6`tFuYw6w~|RO>u=+?xq4T`po|PXZt03x#i|g
+a;zFzXO&=__q3L!~Xoobl<FMMC6e_g8fj)MDN%XpX*seMdKiic-^l~1W2cbYZ*+R2!Wt;)`<>)832ts
+$E9W9w+RTyPk5JhKNvmXGmn__@UNTE}24i#F0hn^VeA?qRgo3l|@&~8;*9MFR3bwJ?g!8Ts)>r>8S=g
+=DQTn@}j^_#J{IfuJ)vN5AxOM$V?=zxAo7Kbt-w~cIJzDBkp<QP4=Saq1Gd(*AUfvr><wBesHR)8*D0
+7}-=NvvwuDKWSnAjMU<InO;)7W7-39RvK{L0xE+(16_*SVlv~(5bx;bifr%?hXx=Ni`SXr-Q6xGwcIS
+GMrSPt`;;GM$r|4{&+bTY>0Ur3dQ+_7G*SZimGN{nPU(vl}4^WutEzab+HnX0>e}WlhjyfH)tuMHR+h
+R%rMZ$^_=!XSQFJAOW90uRhw_MA@<KFzPAmc-DT&=-8<E9Qj}zWc0>+YL2@c0%|5l`I$tE39LVQ^6bK
+lV!KJ08D?n95KYa`CdH#s*e`(c&W1gv94dPh0fI#rySJ{iln#*$#K6DfCi<_*F&kfIZ`0IfD@SyP534
+c9-cJ0}iy;5u#41bUVes~smX~!Ry37!;xI`O9`(Ars%;S{2?xPAElBM)vzG6~{*Pdbp8!oQ8ODIEPLV
+`DKky!#AJ>#GD!MZfNm)Fy8`GG;BMFl=%H|BbV2<j%li66|;muwx&x<Dr}#<1b1!A#bZTEXJ7)Cy}k`
+%K4axu%-cQd*2D3h-_N~wp}_UG^e!h|G>A;u&2fkr+9M@oyED2oXxp+bIyGr&bhb5IrsV)=bk|uX@YZ
+qR2=8riwAP<g$ZVRbBuE@q$_BQbEEBuK1q>tM=O|}jWXu_aPdIqy?7w=?k^9w<4ZlE1$Xrg<la~CvIA
+Gpiz(;c7X$ac2mmK>_C1siVBg6!G4_A>KEb|cATKz_IheEj$$R-xn7Z#1r4+$ybSV00O7@kiScOb+rC
+SW5;bvu$lCCy8LXtg-*jAej@XFb33SE2?YQZv*V5|%%iHuLh{gY`<ncQk6&9o;4)b44P_}K==>RyDGu
+4(os19xGDqZYonPsYG^EYPP+!azIj!jr9X_{yW*xa-(1M^2b&y#*_1m(;yuiyqij>^F|tt)=+GS?_)c
+lJD|Di}T6JPX^55nuqO~mCKleVPG`WW>IX=s_tpb8IyTwi=Idb+bQZdTEtZbJk=KxwPC1dsvTCf&YC*
+SuHJ7EV%R<1*B!Shthiq>d*%R#bvA*E<&ua*+MB!`Xm|+C-G>7>l9ufU?M4sfQQ!Xf0vKJN45X?9kyg
+JBZ!U8(^ixz{VCTI${iFv}wH@z+sut1B>i)6Q$uqr<sUX=u&{e$LbGc<D=spVow(<nd=SN0R5lV&%=y
+&mPjoy`v$od|4#;;Arp%jBxv5lTKWt(UDg3I$j_d)d_>5$ZVN)p#@_EbMOPj69%0nbb)4c)CP4a!P&Z
+=atW)#k;*FKp8!SGy!mX@^C$X9SWwvlEY|4}TXIXlWIDWrvkI%s^H}a&8;*5hhF>$`szm-Prc&q>6RP
+ADWZnjt6|?3#k^>>n=|Y@YX6cWGwgRq)OK`i&ekDr3bq66g;KsLGe72x<QwWU;4tg7`)+m-JWVsPO7)
++kaABP01C*50+gFTC@!i_OXM?tDeLu+r3bU*OBJUKfy)Nuxd!uOL7tKQd3c6i$clNci4TiET=bO(tr;
+1Yvy(Zx?V}ZZ<GUIK=aSi90TZfTVq#9Eu2%8sUz?a__|v;E;f>!y={;BscDh2Wx)Qg6D^4YeRnH`$Ux
+$(@Ra~1ENU8AWSWKF^;@%v~317)NAK6_*e()I^fh|+!NH!YJ8hnhlI%&CAm^@0XLQW`MvrfsZHp+T~P
+hTJ1#8z7`mGx%7i<Xb@g}ny!aZbiVw%QYZLZdiap=Ukuo&irU#;(Qu$n^EGKJNG}_jS;1fBy}8E6rc}
+m2x-qD+~HHuv4-!s$ywznljc?Jw9IYa&K|9=kip|<#X*0q+@cx+-OYGvVmZvm_D^Zr?-GqjEPWc(>uD
+Zh&bcTAHyq@1%2|n9;D)v888jBu>-4gb9^OfsfY{KeGl2|&E8+Jbe=&BZcAc^lEf-B2Fm)zE@K6VhI&
+Kv1E`pJ&}pK2+_Y>2w61Ij0O<AND&v&AkA=<)4T7>yT$MgWTw7(p5T1Uc4}FVNe=#z;je0ZyrRsIAdM
+LA`UN*LCb5BQy3#zIHx>k=TEg8Jr7{>|5dM?jbj~fGL#a@FYWw&cX0YBYe#xPXUMz4gGo_P29$%8Yb?
+A_a_hioh&jkx&awz1R4c+eC^3pal}qyraB-S*=%%nP~q8(2Z0xf`G`R|A>bA&1R(zpd-n#~SMvpKJ0O
+_l1_^$kBf=AEPxmIhD*(DS!BXCCGV<An)rZNOq!F?E#v+3@t8_Yn@^(Wu~(vq`W9UP2+{-tF8|Ta}*F
+}Qfa<5kP6AB=sN=29*TZ_Hf^?Z+C=3YONtTaHDN}QgLIqa8SXCgJOBWJa)BRcXz^QT5~+*zt~7skyW)
+oBcl>g1d%P|r)#cuu1A_VSyw$UEuF?ER#nwdrfVng@RJ$66N^YvhwE!5PkuG-wZKd(rkG~1B0M;~I%Z
+SJAf0M}v;}I!9*Ym_DQQ+6cos+IJI7&GRIcdWHA{i99eo`zx&{-~Sf#E&TCyAb~NP~8pZacLc*H|;~>
+reN@S0cKdy`qm(R62zM&w~LI*`s|3^sy6?qL-XPGq*O1zO{#~F;)Fp2k+q$h`O%0|D4qwa|hm4!u-_-
+9_x~Dc{?DN^r||pjkoN<-I9=<UYaO2ik@7#yKrrM8Qxq42M*)f_&Y`d-}^~79^~+c3wA)Xr0xS*U|;c
+cV(Nwo>33MvZw$&WNrP0`gG;0MJa>&hyc<O}+PpiyK5Dn3%yEYxtYBHYJPwON8SEjAKJq!FjOf9m0Gf
+?HzdO;}w^$i4Um$(v9Cm(q6ibJKAMOV``E^1pOGspn0ULO151C0}*Cbj)n|LP8NTe|X?MVHyn-3jTK>
+j@r!Y?}+bPF%!C&(E;)m#bh(5lI(@(ht(pOs~@L*-x4gIUnyjr_RNileAUbSdj)*NO5B`XT0L!sS}#X
+L6RnGf*b)-;=03xPlurak7~~MPGwHTH#+clg;!_E1Dg<WYU`0M1CNN8P%M)xnma60ePE4nM2rCPiwhX
+R_jw`_4;nTv}uYiq<KPRMtGYfq%}^t97EGQ)n$dCjDCj>4r+a|9KODr7lWJ5>q%xaB$Rsc73V!-)fJ4
+Bpwl=mz!#vgx5V)g3MBsUd{dF}yL`Lox4Slq;ip*-3jTN~)_Sy?zAAFF-+W>#$J)ZxteQ}Y@$-=FZj5
+n7$|X5GgEzS1o=Fv_H99A4K!a1xZfpy&jb^=Qvba85QmrMEtn@?Z_SF~M^~4gNI@zf!^YPKA+%0u^1%
+q-sJ}iMjK9b?oZDrNWTI`ie4ujgU{U9_Qy((m~Ui~g+cQ{lm8!w6Yh{*|zn27@D(N*oW=(hhn&Bc;mB
+2V&W=uNPi<NXV+6HBVRWIu3lnG`HT=LvPPOUJ|RlU;pET7}<bsfY%VkB2=@z0u>cRCc%5;;ky9Thz!;
+xRQFBb=z}KVM{185f>pAWwtrAmes1b#8sd|UgA=Fl8}kHEN(4L?J)+-_-+AGa#>>@BAKyf*5lTj$CaU
+FGH|)cg!V7=nk<&|b#mniX2E$z#^WrR>u{smcKy?+0;2tR1>5jKHS>XlnmO=1)j++dWGDB#+eq)cm78
+S(R{G~J6Xq(InKygLR(9x%b?MksXp=!W!0JDOd{Z6zm<n9;HOQxJ!o&73`NFmwypxD~Ys)cj?TULXmQ
+(TLJ*VpybGU91*DPWj(?h<EaZEdPLh-5t_{`1(!~88u#x0A>T?L<l#8>`+O!oN6VPE7Fu*oamhbYjPW
+YCy^)%^fW($1Kq7ueXoTQ~#!q03{B@jr=dhlE}1D3mswKEJb{|LtTIa|U?xt&9PVrqA(M`d~lc@%8~6
+FtMd~*PUnIl_lAcP3&|UCnp82>z0^BPPvNYXFqBU>Q7usTiGuDOsj|8b#noTI<BUGNDA#$b&rZ_$Ei@
+ET}xrc#4XH(hJKo)k)t$}e)@J?zkdYq%`|=y!WG!u`}tOw8AsWHXZB#3Mpvzk^d`I|rkOqu%mH=7e#e
+P(?b9;P9S@1-cIE2f8w_+lZrA4SlGLp_o*;E)O*;X^8b+&uzhp0Pvk^cT*Q=8|1ZDp9WLQVn6uYY23A
+JjiY@gTM4Fh*Q$>?Bb$#Nz1j2S|&LgzbIlQLd!gzBBzXm9XPU^svzDONg?C&GfSGZz?Q`!_3N*tbyZF
+!Wk#h%VIQCz+aH#IRJ+)5mFX2D=ps?Z5M4PA5297;rSL-*y$7GQB_rIkWg|QEr3wPNSe+qZp>@hCr&j
+surz`fizcdAgjwg&!CK;hFP(lcf6b6$C=0x8<g{)96D3r*JG$p#&wFDp2T~raI+ZF$IQgFe-D}jBE4o
+b0KmlB!7irB*nqZxDUF_y7SIlr4Cv{&9egoOI_*LSY`Z-^w_>v%q$8M7ZC`K_e)EgOjH=`_>ONQ2gHT
+KPTy%va$7CjWOt=%G)bC+N>BYche}oy8$7a;kQ1|blr7JMD%aaE)DjlMl*!fFs5WPN8KF|at44TCb>5
+Zm0#Tsx?0)Vdie1LI>`5xl@;WUf{0d%a4gBOc3MkYQhz4lXGuPm{5pI|--AQgrRt!SBz`4yndK@637?
+C?;wy4Y?COdxJY0lHZ#wu1BVF{of1K2iz<_n!=bWU9e5-qgSU8}ohpfb9roR|-Nb>0CaLGVsz~y8J6x
+WO7^L+q%!3=17*GuUO^qlguWqMy$9t9*l=w&E+OYH~L5mv*X>2i#U-iMOcg%kIthMo9PevEK=Bgl5Hd
+S@H{G$H7;QFyv00|L;uEy`l!hw2D?zT%VD*vtPyIx)gSUF^~jaSvDziySLTBvTSq=ZFRXyrq~cQ*VjH
+QI`LLqY#iNlxqQ7C$zr*MZ9*yQ~?Xk-IKMaV&byF*?%nuKU!<+B4n=A8wJ0K428rrp$`M)0!hvzP|i!
+1X#OzMwA{Y4vIng1V%BTZ|t5u&F)#L~zg@RuE9l=RjR>xq_zh$21{#K<9rm+1CBMknWTCLM6ci=JOl4
+_FJB>Eq)NZCTHDG4G&Ho<kvNmn?ePD_o96sTD3k$qnktMHQ}bfmB!sb@ACc(gR8|eRmdH`%(Xf|61#0
+0Ed_5#;Ur&u`_NsJh!j5T2O!^2aV`DpEY8~HZ+}~2hC_4?*BmI<N`cQjV$|SY-o6n(ZFi>K0M99JaXi
+cX4+)tyVxP~>}_1g0|aD+;2qmP19J}>hk=6dLW6RdT=GNwC>?!<IlHG|o^mEqe~y=!?Vrh*)pQi*<dR
+0v^P!+X4HSCCHs&QpSAK-j$^_>&Ms61hP3BvE;x2KC`vT7JCO^}0JZ&4h<{<OcR$vOZ@#3CC_tdDsoX
+Gymd44~XJ+Fuy@sS=$y$@&5x4<=|CIjDUZc|eHp(K?4au4CS4Tf<$<X$xnh7JaZP%U55z06N5Wx26BM
+b?|dRk@h5h3>qW4eHP{0U*^~)uoJvCFvb^MV~`ShTqh{D+rU?I87Hcpo5LMU4SOBYOjE}b|~fwS8BkF
+#BqehZxr}ehQ$|}jQ4(7Bt1EHr;CPQdZfZNN9n>QUsbgskOivDAURxUsK^@^V5dgX5p}^XB*D&YQEjl
+^g7qgirp$pRVv}M0*N{Av8-rZ|%VdW#ZE~to2ne}aT-!`f-o(ArM&n`1<w&@!Z<sOGO)VXLDIo#l7!F
+`vy^XoguzG|Tcyt)Vv0WLT>U08R^8Y&M&(<jJGQ&xK$}vv*C(dK4$`xP7eO8dI?r1b$fLk8AZNU;YGk
+(G&dZB{nx3KAnm=yv>5C++)UUpLickqZ^zDPeoXNVG<`M6<^|HjI$Bjt1C5<hM#_Uh#~$>Cu~WY@MbX
+z&JVPD=QJ<oUy<&i-=Q1zoXH??1(_By>Ylc5OyRv@y5N6Uy2M@0ziH$3o%T%y<2l{d%`<d*g9bCin4b
+#i}>B^Z?49Lm3u3G&MfZm3TXdx2NUF+3onuk++>-=deBD>4w}E4_>T>@u0F^|2Q%Q3O%_ACwMknGtii
+=NUQ{w9ka13baGn;T;L}@&Zd1j-bmI|gYHy6%INNS_%SS_QA=l`Zyt+J@`0N(FyQmqy+(7mJQ7<i&By
+RyI=lE${`42G?zxnCpMsc|A$J4MHh)!%Cq`q3@oOH7Y3aJ{^~czbh%a}SZnF<~uX>E@=6Ugm2Oh)uKL
+`4iyGyR+V+}Fm+vVEaSap8a9zQU2e<-UIYV&n}25<M)3bD&6IEPjz+=c|#@N?tkAO(Ng1^qrGlOvM42
+`^fu6KfLAt`lK?IPnO?$wy(SDtHJ$J>i$B5gx8R644}a+Cj;6Fwi_;O#+O`1J?<zzx`bOurA<55K*k0
+-JXdDdFU^{N;Ki(d~^4gg7C;V9HYRa*nq$C24wBQZ6tC68gWu`oeb3dk4F4_8@iZdbd)1t2R6WQL)_6
+cFbSCiwBpyi6<dIA7Go>^5^Ke6tQE(g6+cR@9|LRu7b?y=zo&<9G*E2|=n&z%j9l~B{DUAYy-zNI1=s
+w_ZXtGf59l(ZlPMQg`qxkRhHbLm1e$|WJuz&_<p8YgW+HYcKP30mB7vO~;E_k@hX1g0bvN!t>C1Uc02
+cmRu39YE3?gsSSx4fyev3ze_}v||Pv1bRJc!Cst}cC8A(108`LfAN@wPxu_<loZX}i06X|Wty+Uc#@t
+XM)zd!!nxmf{KB*W+1^*Zj8At{b_f_o5WMQ69<)b_T57je<J`SZKbtT`_voPLVC#7Y~(_I%#~1gfA7!
+9eRr*!CxL-w*k$ZYPwEe+|X}YyI=#F*6>aS^lp$V*I`o!UM}S!o!UruSEEI-K$QYic}PM-rdu<5Nhc|
+q=LrSsq)(WB6tyas;?H>e8Hqol+UpKfd0m}khxly0HdrFtv<!)SuBCuJt|gHjq;a)?8>+L{jgLs|P9S
+Bf<f>Eh3vi!PxwruD(^t+bz&%dok^;2nDiaHYWOxj6MU<?l6D>aEJ8h`+G1_{ybm?Q<|HIaOvl=@SD%
+vCkJC)(f&F4WEyIRM2=dY{?OO>Lh8Iqu9Ze_T*COS$I*Zg2rH<+L)5RbhQ*X++IPnObSaIowH+-NNg-
+mOXsypMqdDo?}f($HjbKAqPR$MIDysPf<_(`<MpLi7dZ4&!xuh<BY#ie@x1d5L&8p{kvO8yo&`5tYan
+9?r>Y1GwmUJD4*~kf8X>hL$vrkGLX%Lbg-luIg0sp^SK;(21_G=PVFAq}xgc6dJfjF2M_B@88KBRR;L
+?fS%WgwwylQ_G&rXQTb=HuG4$qhvY^7Y>FwhYn7Os$cl0Nw_@&K#ejBvXEVE<yF<4vEXQrnz#BBZ8Bv
+A1DcVbtYkeSDs@Uq*QqhEFf>(=7W_OTu>Mq2U4QpD}9<M33T?F-c(|WKvtkzS#!~$|_`Z*huS0OELa>
+~`-RxyYwkhe+~Lw^TSld67F0wmo_*5mfvzYoGddYchTBkA%c&_VMMS}B5tY(1>&QFLx1>2i4XcHV~4(
+9-R6bqDiV98sz+-99Q{l}lzfBfO8h1FVD6QB0w?9OO*t-#ja8!Z>Kc8l2Iq(M%IO8ptHx_VM)rQ~D7t
+DZLX0y&lNDumGiO<)Q-oe1bBr06(69)4>go^9%5rex~M{gg}<3+FPuRSdHiGV-%C-seZ6nGa<X-Gg+*
+98#l7Lnt&#k)`gZfi^0EQ1ZiTFHa=2-6ed<>LSzBsCJS8=G5EM4w#SGksJyQ49<fU5Nz)^j!mn8j&PC
+JZ+M;3)`CbPV*9Jj&2B+ffk=|y#4<!6S=#_z(23*{50asZz#gS8RLEL}rcX1Rq^Rw>s_B{-5Vilfe2T
+D;!BaPf$xwQH4N1qCTOi$R|hux0uLDjLw+g#8G!a!gYjKSz1fPP<HpWu7sM07j$3fmJH%jyQML_a<dt
+HIWX8?g@-GgUa=w7>9FtpIO<Tns%IpBjReDd@fUbRTOi@6@9cSf|9QV?2`OzIQ^*0()%~Z<hpQV5_B1
+jI0J<$vVg@hi93az1i#N(92?;CC{MHY3%U24lesrGvPSJw@A}H%kL|WO@oSk%g&;6j|G0^lA$uGSHF!
+-gEmOcXe0nIIB!{mTI)JeES|x6?!2X~0S9C9qs~2X{1%HN#(tWRn8e_7xK31cLJOJXiZl8z4>Nui=S2
+xN1Wc)WEb<~e>W|%($PV4szA%Q5D|OrChZ&cV*1^JCGTKKPm?;bzfHp8}z?6!6?Up_m$OG2_0hQrFbL
+`m$*0bJt&$h@RQyOWCUfRnHGznMmB_O^3sU8*1xDCL~WKtBQmf+oeI=^blPM0<GFOBI9xHK{&v?>m`k
+xJRAE^#gvKuzq9EMvE$A-O$^DWBr4fD`eoufsz&u=_T5FzJ?liI+3CL&6o1IR}<9JcKQmh=6N`=&-Hh
+Gwx%F{*}a3&lo2WX~fOFL(0%IQ`Dos()EP(%zYO>Pm(8*2)jGcOIrOPUI5M+jo`^Y4}mdj^MTT?pXuL
+v4!s045+A!?5CvVbjv1ylFtxfRFpAz?pV-*@Ap#x8oyh20F|GR>wrS=g+vp8np!@_9nm4~>Sl~K2yIZ
+Uob8V@5LMlsx-`f|NO4SC3Sd&t!_OPfP7FFkv)utp_->kSxLCbTMy4saS?FzAG5IT)I=-6pEt3?(NYu
+c$e4GE|*DV{40bD1&MCb6bjtZB%Oc=ABy!TT3r2-H_l!a4O{G#73Q%&o)YDP(89xo;~lhef#Q_*ks}k
+^cVI#rn@}h1h?8_LqfXO~%ktwU_m(m-Q;*ki?oHrRs4Ocbvu5JA7(=l232OHn(7#Hz@1GntHLOCA*nK
+Sdl=#bar3NsUQ6T?YVUg{DNj>NU19+kcl@o%e<Vxen=5((y}9QTdEg50KfE;7MMvrQkP)zlWjn;#BC`
+mnPKW~_Zzp#CC&cv+k7Pr*OOsZ<^?NTWaD&8mt+)`3~`lYR69E57yQO%f9?h}S+cE#M_<Qz1b8YU63F
+ixaKJ-&n18@g0e;5m`v!jSCT%st7=>V$`SSn3nxzbjU3OrD^+gKHN%TFC)A>n<-j!$F0^aXW3&SF3Zx
+f&V3pb2f%I@xpq$pRRTXp(!QyO5R8ol1SB1wu^N+3d|Nu?nJVyY`*QZjjziN%=Ey*3gx2X5joM$o$<U
+1o{Q#kLc@ZxQtt;7H8B7V2OXAj*$nWGVj_Z{Gm_TZbT&Pr(BHWLBC5igd^!sl$(Mx5SgyiLpJ*4_E5+
+qYv(OsBCN$Pf!jlZ-rcW96kHWKCE>hm$94DM7t-=EmuZRwuNSZdJ#p4S~tT_2CvUAHl+H|k=IZBX;zG
+~L(pf%I6KBUFm57x_+oqq##4;v4bz)3w$snNeBbm{QmnyO*~6R+Rcs{p{9WD0ntMk7;ctb{*nH*y$$C
+qCjMxClYBXuUAh7|0I8$Pf;sZ3Je}L4e1#!v{XXqcG6h1)0fGYlv<Ks!|!5)Jq7{vjy`_WYzVgYf(cr
+}v69~vC^oDEO8enx-U_;l*}B0fGa78L`BiH}cY!1(xM^&x(H5Wk%mM==fu!ACK!1LN8;t`*wFMhJ)RC
+M(9Xmhl0?VIKV7#%B$TPp58>NhN*^3MZqnQ-{^M(s6pwetIxxU@$BdOSQ?^xv*`?`{*@4qZ0;`Sdr-g
+1n{86JpwY>8J7O2ItR`XSZFHo!r)H50>4Vd&j9}sB>=TW@AMc_2eixtVatG~#l!Y9!a1jf2exkad$HD
+K#9D_SX}o!v@#e+ic>6ZQ+vhP<&ts;tNW)pa#g!9T%1*5GQ8}DBf}J6bC=bd+jW_;oHBSi4kqP0gZ&8
+M;`FKOave((^V=RH5z;kqj*iVO;I{@NA4uOSI?|}Gx_O8ZzJ`F1a+9i1gMsj_~H*FkVhNw@G2IcIn)W
+G9r7<lmLYb=Px(Ea?qm>n)O|L0w{JEe?LS0fs*T}RxkEeJqYBXIcf;<X&n<^Y1{PwP~M$V6l}d}3eB^
+mhoSE(3IfY<8n%K*}PP(&WJVK-MxBg40|CqPM=rxPIc!G#n`j^H27Z!G2epG6|0|kPY;*Q)qf_Q^xur
+@XjfezOTXWL4?ikzmK-QR>e%OVVZWsfcpSq=cs2#n7u51+T*es(CF;4T3d&6Ss6sHWa%$pP|M~gvmg5
+yH?70$9KK9!Z+(F0z4<YB4#4&<K##SAr#tBUR|gz;Kli?mLk{0IMkdj<21ZpIJ$O|#{cJNzth?m!QmY
+i6o(v*GgC{gM!ylS!Dn0g=94?sh0gmCR<)*RKe9vi1;Fuf^XBkj!grDW6e6}*l`Uc#8I@kVJ{7e&<_h
+!rCm5cd>O%?pYrfGP4CcCg{A=B`W5T7abDV;0s!w*^+xc3Tva{w>!^E($-8}vG;)YVY-({hjVmPU_rA
+@pyF^A_zQan-E`kGQJN<EmdYT*_{hNZs{ezX?BHWcZhZe!GbgMAGt<s=B~uWNHSxz6p;YF2q|p$yCsF
+Hu76KgA0>lzN`FuLJnFfv-_jm0)UZQGT2T2Vedk`*<a>%4|bV&Lv9P&3F+-q&jB99&)=P%Lt-}}#xhN
+qiljb!p`Sy_*<bj>FJnvO@C{l1>_(}}Xi!osQmxB9X?wLn5KL066{!wsxyQ1%x)PD4r8HLZD?dSEP#Z
+u<-56-ruER5vH)H`G{wjst4z*|GA`27~p5o9lBn%CLQo>U@&L~5#M?`;C)+Ceg!q}ZshhF4IN-OxCQg
+$CH2Lb%?i9WJ-ZbSby3v}*UcCmMy?ymwF3~xD!(LFHX7f^@M7VzsSo?gxTH2%sDQz3?2S;zhbfKzbuW
+pt;W9vF`E4VggvS_CtF?yp#fdBeXj)6*<3F@MZLFrI8(fueW~6KV&{nJfO9I5MB`7h?0(jdyV1U0rST
+Bzk;K$XbW0*onTtgeLQ#b$q{3x2?QK5Gs}c@hCF~%K4gCJ#i{9q{+OmArAQnKx*676DI@1(2>s3WNwb
+75w}VEh!>K9Njd7@SRrgGySZPCJ!aMAA1pJE{9A0cu+K;wr9PcqkI{qYxN&;`&k;q3C$S^h>p#FRG{{
+8rk@eEYSYdu1cj1$an|#KDc)CyRHJ3#zb$SZV?itzn?<jlkX2y)Ix~=JMKL7SJCXDyBK;(<D$n`AphB
+0`V8gdp|iKfUWOrbk7Jo)#6!t@l=(_wyHkT=J9fpTeHu|b(AuF4Zv$p%;NB3t%WDD&zpXi!C($!FZ|%
+ia|D2^yA^XAgWPho_#m8kd<0i^XSL$I{3RrpoB+Xq~+fgf?Zg8qHKhy+tvh|7fiVNK_kII|LsYwI&N*
+O;bA<LZPV{o=~YpGluSj)%yu6aJtpFtN@#1cANaHtLve|!P-840_^iMJGQbJiGJ3DfY_{if#>@o-<m!
+_lkbgT&&5_BX_oTr%H?^t8I()JRf9o|F}PY6jf4sQi!5omQ8I3qEC!4%GH!CUK5`IsgS?S}tv<ERQe^
+zfmwhns5#X)zFm?@Fs*G0;o0Jji;Y=l6-x?V85%1bZylWrvu6=}E^Jjkn=!V8xr-zqZ{NtKrHgYC0rh
+)jfw+A|rG|G;1Cj~!H4$R^U|A-R+W-(Ww+;PkMz;BeD`5ge#E2*1xTD1nVc2es}eEtPz9ob6Pp2UsH4
+p*UxUoo)Sfk)LH?1HFv^e_N=xrePj&+~cOEY&iQmyZI?xbU-rWD~Wp?M}+BbY#H4L12G)Ld}~f(51qn
+%cFd*O*sGmjOQ``!Y{fpc0I)oONznc@aQr(yoc8su#1G(vO8C{(DOgUYg7tNA@dz|cxSLBWWI&HWQ5E
+&;mZzx*!=bqXb@1kdfdF!CYNjwYdq7frBbL$t}ZkHOVqw4J21WSIFiQFp#ZYnGjhz7>MBFsBnI#e8h<
+KvoaIZ0%OzWvSB=Cq6Cqmhb8=1EZkWu&-o;j1uuGYicjH~Ee-XQs)%_0Z614fjf9txIjW(+b01xlIfq
+-8r?C9<D086D05^mV$TtMDo{EpOr`FsM?lhuMN@e4YEVXM(Al%@jRH<`z7Pq<UW@4REj0qO4d6DEiU-
+p3`hnOg|>w_Ud5XA;_C_XPfRF8|gNZp}ou{Hb59KJ9#}+`UtN1Mlzzjd{g#tE|^mwJCS4Oac9=R_|l~
+p76oi&;C_{U|ob45!PA!#tm|Jn=f~pZ~SKp=(IC>Am2?@UCJ~$a=;4j`S3m|_I^IRkKnIqRb7GAz=sS
+V#K3PK`#r^ePqN<=?DshP?Pb3`4ept<2Y8OofDwD~#RU76B?9`>Q9p@N(<)@ba(>p(wQNUU40867nf?
+ea0Dr$Dz8fx9{eelowr>Nm&5X$HF`MmUhFHw~cQA<e8N?PjWM2CT7V^UFvGi#?eQ_fFA1r-wJpDqRo|
+j1f4NEVMr>ES33Sm2buk{ly*pCD7BMJl8Z8>qcJ@^tb7Xy%nAIrG$6MngidFm(Za?fd>@ICc={H{I9T
+$aVj@@8K83G1(J8+Lo_>vmnrWOiGo{slfRX0}O*39=}9<uQw)6u#*v<O%wAbO-73INgRovbg4uO#?d4
+jC_o>ZB?$&ZEJ56VpmDDCO(QvGFQd`hU&JLV*nO*Q5XL_)1x0F64Y3V)k8KHvBL=rgu6DPlLqib@%PO
+7H6yVXc}MJ?DQ|9@pLx(JgHTUMMGHVysV6j;*1gkjzV*Mub~c$PUh2so3~~+{%s@Uqf+xt6OTD4N+Gb
+yNGYH#4>6DPk082m;n%B$-6h$B_0?}W+6F*9@GPh29C!wd{vF27hwlGNMKWzaA08GM`jlh--!9;UuXr
+xB5jbq@AW({cK8*leF(NA{zNxe_))dORSvcH5zC6yM2OheI9h>e<r`f600=+E9(2#d-n;DSaJDlrW$9
+*P-FR{Pi~3P>A`rrY2{cuzJKnO+*2b5kG4n=`Z(*or^C9qUpwpfg9Th*)ElS^wJtKg!t=yvIdv?)NYL
+d7S&-?(b0hTm2pSzhKrHZ-!P6h-->{t97wM?M-?jQ|&bdh7)ruEH~Qu#M}(eP-Z=bhSD8xCD`D-Z^aM
+m#P9y>nqqhRNIQsf>SI>WJ|$hyCg|5&7K31xRBe&<Bym+lvxsY3=+L!nODAATHEKzE;-go=wkQ1O(%p
+Qd*GD?Zm+s|Jex0g&`DT414We!SI$bdZcP;z=XFl)ob7#OEQkUEAW|tX5=^irPM<`=vN`_Be%$jc)tX
+->)7obsBNXv@^R`cij7CMOCg1#PV4h&IU7Y7FOln<_>KjOuZUCXxNE%HV@d3Bagp5wH$)b@cM3*on&I
+eknqY~}YFUG`jDF>L0l9|-2`n@6zp&X>bQ6+V~-q!(E6y&y2)XBpUcA#fW6eIn+--z=IBn5K?5QO#|W
+L*Bok{C^1*-%O0-OK&h0*poN+1vD>tSHxs+6asgppvh>9-c@LK`@@;fDY`BO|IQY$ja1#pzSUNNHypV
+dm5F%Tdc41+4geP@=*HA6sU2*asOW*Z;v!>J3m!Vt@GD6G;sX#Eg1lvci!k?jn0usL(t5_Wneo{OJE}
+G%uR^(m7Qc~b_IE4K-E4_8Mx80><JE?j63@q3)8tt`P9jG=fHyb($X;kdX!iTws(oVBK6XZWZmskY`-
+BYWdx+j(mstH#GC!cwF}Zlr5TCeq-4t=vGZx25+%FO}qeM2yB@sECQRgeEla0+&#Z^_QZb+3P)P#dD5
+7bJFSTz;*DoL{!7rBxYwk=!rjD<h1h&_+R;&;cMFU6i!$$T5viA{kPs6F?Jzb$9?j69TtTa}VnmB;S1
+DHN-4%~gB6T*Fa&rioQq>}95+s6EAE)fX&uJIK^Mck|t4wWma^dK=kMHhv48r1;SF6uF*EK>P)@^8)c
+xsCdnH>E%QI)CBZ|!clhpww`Dg6VqooXGIUPkM?HCf9GW3=mrJsbBL3=*XLN!+Ys^}O81emlB<t2v8#
+MUPXucM2&)DAlFBS>yW$ndq9@D1i;I3MoK;ePPUS+cSaV|!`}i{62wg-PsN)(o`1qkrC~powdn+xU)&
+n85(h1dG0G?YH&4V^V++vKADqv=+o86cokz+w!44s$6l0B$RMCUMM-zPp}G~&zsPqe8$zvSnk)t-m=h
+2(0_!`#TK_AFFN)Shy&Dg_x-Pat;TYR_UZ*Z>99UWjd)p*OST5%(yPfU9@P*&F3;y_$o2LTB~7#t@*T
+>CK+J!RQ|v0jQ(d8-WGL(Z}&JfBaYtqC{ku!6P4#*avL9Rfis(W4fu)<D8KLQpM3~kMkz&Mv1v`W18E
+Wu4yhGxeifD|2-szFtO^-`02#1^$rCV&#Seh(A9AaM)Awch#>w!8kksq<`Lxy(m)E*M@D&S5how*GB!
+Y1$}sqKD=9_dr_*}05inHnu(G)1q7ryFdC9aM7S4oq?NN)byB)h@!BW#$sbxklsSE0lrm(ABwRB1Su3
+K`Qki;8L07JbOuZIo4jA?kOU(5InfEz!k=Uam<%gnfeI2P|af%(}YcOUVSy6}`7wI~0HBx565^EPVR*
+-8-(VhNzIc(T!{97S7iP!p?0FvFXYTH(qOtC%jPZ`2{N(U=oGfvSU5syKOnpb)8qH7`HbG%Tu8$8+yb
+Tu|ABo*AG4(GfdPt?|AuGjCtgPP#P<W<@w3eyA_g{72kg1LTQoPs$}-09>ovT5m*gwy45Gwz4}iK}=i
+hCylt_4X+;18c}!zh+4f>BBv_9M}1Z~FSjlq$_@;NVjG`F(XX17GrNKbe5~Q4f;vn83rn9rFulTcX~4
+q90*#(9tFJ!>5{7~fzisT3DEYITbHw27%#Y>&Ved=eqO8{c&#=hI;($uxKJF-~C}=1yjLg_TkhrCl2F
+M~50>O-8xu8IaBc^3#yOovQx?L-CtE;$cnOmE;l}c-f7TOS(Jpbo=&UuFc!K`2Rw|xHh9bTTZpXYhbb
+N2H-hstjVN=skp1Cbj@#Z<RXXA6WSOw-AmP!mM19ySrgSmUHgS%O(i%Al0i9F|UrhJ8&z{OxVU5#R*z
+?bypk=htV%0j_oGYC3UY(0)w^O1F9;v3gqw{r74dHQf0kcK;XjwhCrUvCS^yi_)x)pUq-#`~FHX6-nG
+6H!}E9Y-&3{AEs)9eXwKd2I@RQMcz=nq7bMM@kH+H&WL`M`1oQ?sL4@hrV&OAZZA^UN<_7T%mp$u*jQ
+Fq)B_0HIya;&sXbfgVl*WqgvEf51ODz1k<o1tMP|{rnW0Kc_FL4Wp2^{yIy!V}Wy5e110z)Gf_$jeAu
+gGtriz|2ag|!B!-s7Z7+GM@-{~&77=m*m)+Ku}0&zc$Nb(YA8%_VSY=h{34s=Ldm}g%l8swVt;*mM_0
+O~qn^{<{M4(GfbSe+{k#_9|*EcXyi)u`e2_QiMTB5B-CBW&SRjUIATgm53~a{tv(JqYgU)Sm%TbK4D&
+8riD=sa3^5Y*dk<v=tzAa5xKuJ}CuqY%**<EIKIXtp;cUCvJp+YxzRcplSJGac7p~<MN(bzb}Ra9mG`
+BJ!t1&U8PdJ1gD^iFAT67O|k_=bw`hZLn$W5MF@epx_~Sd{yWiWS1hm|LzwDYM1XU^%3M8BOQy3)Hk=
+s1Q6pR)by0B5>&(d-18&Bu@Glmrm?;&qR)@%lMw^NL_oAv-d?C!%ll~91wWI&xhO#=bKl8S}`a5axsy
+-zRy6TT$Ag23NAD2!Z)dzo*Tr0xA8TdCH{}L>YZ!~ITNB;x`biVh5YX~-rj4@I6wbgglxu5Y-pTB~gQ
+PgtG=%UUW1vwvjLi-*K>nAnSh{aK0@hG%ym_L7eo#@7*5FGQ^c-a7x_&-ijYYw?Sf(_fqn*9hCi^x%z
+ego9pH?RqA-@qE5kRQy|KkoWs4|iwLlxS}B=-2<jSYd9XZ(v{iNAusnLf24L=SJVawiP-6v~OVXJxK@
+WYF9SSXCD_iTjf`<3E-7uPfZu+O6l&*YzX@EV`|;0`R`zjP398EPIJCB9Cz(rkE^f4s%S>>@iJ{jVeE
+vH(H5aRp$FQNw0~K!p$|ocQmv1@yR$WA-XqHS#bd4_&wEU+4)4NM4vzB)jxVhh-j8GDZr25GQ_0ue@q
+sD6bx2CQjZZf%w;7_KJv}AHqb}yy#hBuY9^xAwoxL@U(}fP8hfQ^6gWhNhpoZ}+p~>!5Tmku(^9>)l!
+v3b%`|%#}623h3s-oaN&}#E47EUj$7wb+HB)3+_vP!v2YIq6y<l6(2IETN%ux1Yr?i?=@TL=v}AK6;R
+l-3zav8%*`>*E+B4or`8e1b`ng+~6Ri5@uOTW2UM#TK7mL}l?=U&ES8Ospt_m;Nq3f~_)@Ea;HLY}k7
+0kgzPFcdFDHvP_Kp)5%L*kyjIk3GX03A<B_))ar)qiSyiNg(ljSe-~X48h;TL*9vX&RYwqC5hD%~26g
+h#@s0>7?og8lH4#8wzaoy+(Qse}b{bM-vn7tsN1VW^G;!k)-=jV_dEBa0w-`#V*U8PKB>ABj5qBK7t2
+q1*qDz`C2mli;j=eniDyw8-Pn;t?ZwmX(lLQC59McUeyfK@gE}dbaHA(G@XlX8_G^yK92@ulJC}Bw(5
+8i&QOnW~Q=}gxdR=ffa8pH4dHFrQ}gyeYGyBW54S|Uzmmel)ZmV-{in!Q422}xZ7^zajK3qDd@T<h#M
+2)Eu+aQp0%10Fa?+CyoQaU9M>)EMXafm#sC5*Z6p9D7V*`)Gz**thx{63peE)cXAW1;XqA5%Yy<nc0v
+)V)=#XfY~QWY-!Etolt#NNY<wod~#g<t%q{0k&>MEg8U9!2tG;jMQnb4l;}7uBo3de2^s84J#oe40JG
+@rD_%1~!Pg;^W3NJ|pJ};Q?>N49l&{@y*;OWdEVp0mL+@JF3dSX8c{|1mj#zul!052dDD-#jH-}ZCjZ
+Csu1bi*-4e{S)M&E=R4@7~Wc&s!yLU<nBYrHI$!PLWg;&Tz1kTtB<nuePlqxTzD_+zZ#*t=bDQy^zpi
+XOfCXW|FSz)*HlBg%>`Wdv-Atf3~u7B76J=YayY8#2qUGU1$|IB&R1yqV>#gmuZ;i=c2%%M^NF0fi=u
+q%iSf%M=dKT}mNtt*H*uDY~##Z4!6vi2Zq70jyCpxG_ckByBqF2q9jzOJ!YhZUg08u;0B#qc-P<9TeO
+1sK--r4eKtUhUuZO-4Q2_*P9&&HH>BD-em?C>cwmh^}kLsLsrGKBi4#F0SIkz93Tdr+8`Mubh1jmIQ;
+eIu>Cx^zf>db=V_yXdGy7_TWj<@*dNMiPr@2(a$JxEQ#(Ktd&5#sTtERH(@>}kSyMYZqvX7|{W|B94>
+o*<{-Drxxb>{i13gZBlYa=MVx>0NC{VD7b!i^_oV;JH^wkS$5dTrldg>R}Pzu#WLUF$=<?ezAuU(>Nh
+_U&ZiWjm@hSH%FMC|b$VB-GQQ4fk9wR$wUhmkyB&8`C7Acnu4@07#eWu3+F7u{x~o8GTr4Ds;;vd7-^
+Kr?zY<~?_b9*wN$TGpfSXuC`FXvi@{yLzQZ<9b&v&a?;I^l13DS9&xO6*_$y0={y8OL{cq`YrdKg54d
+*zl2gl=j?+@(<_NVK*@C>dfc)ch-F~1cv&lAjpQJDEujuJR%pu*>g!!P0!q$av<En!x?e+~W*o6&P;@
+}nv&4?R;+CP{lxJC!?^iIQGwd+y^(9p?HEo?~uISF%HR_6lP}jGuIlYZa0Rv74oL}tPo(jiuwAFFWu;
+oWXS()hP-gF+_j5W)FhnI;R)zi<&FGPl6+_q`6(9VhZ-cjdso5h9nnx@FLo3SVB(iEBK+2!LJ&jiOOr
+sc6wy%&%2kgI8kn7dhiV<M7Fbx(3i<0LJYPf}+n-Ogl^omG-UU}>Y0><x>qagsm1M#ABQ-k#gOn;z$R
+^~ZG<^e^S=+(K`0s)OdfN8}ZR&bq!rFBZ)b#VzsG2snb;(nVve6*;|o^9lN*tE>(K^xf&K4j;p2y-?c
+fQ`5$<BauPZe8RI(XoCA){)w|u`=TlKf&+#v`)gvXX%n!rb+LScp31y0rIT4cq$b#!cC8*d*@Py~BAV
+IyWR}xd-Nyrw{Gi->%Ms_>eYGPlR5gp|$%FU#SVu3CN5;jN*AWn~Nbb}g(pg6YR6Z%b2H4Zg-o2&|SD
+do1RoBeW)V>{-h}xK=dfNMl+nK0o?jgUiO~I-}miS5mqX1-^M9n}&VinF^pMDli-OMU+&c2sWj&9gYg
+m{{xGU~-@bgf#AmUnyn-LTm}Q_?DD6_ZWcgQ92?tIUpaD5^7+oYgG~H%0%rAPj01h=8(>4EI+iI1X%Y
+Ef@Ae#R#7V7mk(@X{g@Tnzlq}xr?qX=xdIet+N>&!U0GaN{5TXa*4HSvR{okv@_&hO{?G~m||4#Ay%s
+Q%8{eQ!Bl~>%m<|MSxc2b!(sK}!PTSW$C0>bsODLkXIUZ+RTony+UN|CbNe&0p(eT8{1J`OV{iz7I>a
+0MVENE>^vCTx0der>@TF&w^8aK?E$>ZT<=l!Jfgt;t(Yx&4C4Ss(GwS#Dq6}NqUX)=A_M-0px9>&8%D
+t!&G@fwVjOr@38uxi%H!2vrQSG;l#kR8W7vG+g)h#}J1shV$?ME3ei2W#Wm!iY_@jJBH<1tUS{iq^q`
+P5>n8mq*1)Q<_|l!S}5X^P?6ld`HksT}N2IiGo&WO2nk+Md)_xhHk!J+5h{{vK^}XM+&Zb#6jB!xz+9
+icxppvKY36nj9xAj&l~rn^+se7Eh+7>^nng4_~pbQ(1C^S6qLix#E@{*bPl6w-iHpYJF>=L~>uIDQpi
+F{=8rsNYqW0ZidYX`l?H|cy?Jm4`O~a75A}k_SczqReRyHqb#budPC_CNYHV}f|J(qYF8(*wS@J`{)~
+dF6tpPVPww`VB`Kt~r4zByyA1cV^}z1PFi*A9b6DHynW$4cJ=?XNo|olLkD+vz*lCz<D817|?)FR&yF
+HI;yFHk^nMnZ5I|rf}7%`=Hj-hNOwwLPm#?V0DTWl$u(i=+ZyzqJDg5EP+OSsj+;=p68xRU+)gmTCam
+B9#Yt0-Kgh{%40R~dFPb4A{}51C2W&Db!aF=@?evtehH#qo_;0K@HjUE4Y2rcuqua&^;DU!9Cb=*(WR
+;p1uX`*H;Jb#abvTjjn{y(@1^>6eDm$#Ua}${$-M`<5d$?B1+|>MC>8Rff_)4juPxsQF#xOg%4`YlZ%
+p{c_G^j^+#Aum0v@<4ma(1eGSCJRkGS6hVoWvn`Tl`RUkpG%uUdJ0l^j7%guCRRZ+1=}>^~EIg}no9~
+CXIW=CH<(6aCikZhOj^C;m;=GY%nMmH^S+i|6!#CuZN|hYOHq<dkE!GsV@nqVG^=V9EtksWc8&Oy#6p
+^BLEjq9*TMCTWr4SoZII_7dPq{v&Hl<R<rqm0FX;)rQfV!<nTFQQ<#4AkysId}DCFS7iymDlogEi+pH
+U9i)rg=mlcC9Ks$~bnd_E<&fR=+^gV{J?7Te&4Ax1-P)>;P$&ifp5$^wj(XnI)AIp}D;G3~Wz<y|>id
+rtMFinm<v5Hv4^5lMv?^<0^4y>w3jeLuoOw1lP`6DsD2OJiIe$T;fsgL$jnDv0=5!Q?gQSScwEp{|s-
+ucU5I;ohYTbW(4Hczn~Ttq}Ng8h&mzmug2HcbfLC}K(KIYm6$eO>|c$qZwTnViS@0Xk{6p;r<!hJwKk
+QUIzuz22^Vo^V7}bLI@<Ccma*AAEKifRiS<r`Xv!<fj>=sutJuY&5;2#3Me=_^XrpUwjtVoBdZIn&Z;
+|AimxWdTO4Y$0lxdExwvROl+^Qd>GK{V_?ZlpxVM0ApGez@7{Tg<$Ccfd?#WIv;{U#6gpTTtATH|SUa
+B3cCWEQ;uJ6WpSOoko5$?dF49;_QCHeFW9y{yD#X2(%=NidCf_FEnAn1#aKV>G{1TUnhgYM<)jotUs!
+Mv8r^Rbrn?)Br(&wwZNY1bW0hkZ6};qN2r)*0<V@R?$fzOlX(9Wm||=u^JcsQEBRIhOJ?$>d&Y?CmlU
+(?ZoFs!m*<0bFr<}8ar9S*(}xF*?JdQ!p@k(&QJ_(Gs|&M>`A@vd{%B|ZFOyC&2Zby`s9qrMsMp(|Gn
+(NBG9&Q(VdIfS=d1;`_4{19AA`bb(}ZhMp(-#+3+OL99}j=G>)dB!B4-)Mipvl7WKh~MiCan<no~NR2
+>Z*Jby!ds$0BWpj|UifRZ<H_%o)amkUvmeZxl9Ftw3Ifyz!4B?DR8jS^X71G2BzVU&#aGU9~kb)&3$F
+=|tbnr?B(<)HK3d^x>Wn_JXfBbba(Jq%l0NJH9daCzCjlKMDXQb~Q5Ep}UuXt``5;zsY1`gw-3AP?C9
+`BnSi3N+IQz3{WQLGKR_|LX6>ko$08^=T~M_<dP@Ql}cNOjAkJaQjsRiaa7k6ffaky;+B}N}?i^=TMj
+DFNW%e$a5>YY|4$g>*cU!yR7(Ze`}u0iq;7#CRl~_OX-rVL#u0)mPtB^w4oGd70lwK4+>5$>C#YB+iZ
+6B{#6u0^$~mvXe#+_fSpD{qOlYNi<*b}i(UkmRyi(`i&b2_AoiuU>{I(v*6NdD9aQW~S*za?s3N_qO|
+o%xOHXZ4;m8Pa(Cf@eu_Fbw%C4O%N>|&dDrRy#V~XBm8&gu>%Qkddm@Kef+XhNQdm-M@yJR6vqeS&J6
+OnYyc>1rai2?t8sCh0q>`@b8E}yy#SmwCzQhgyQlY@#th!V*VKFiL+Fi|9oVWJonmeqr6G}miIqc$J=
+Xmiw5F~Uq-r)`SvwmK>>yv5YhJ6o;_g?o!tA#tC&+p3WBP^oLgdP8aRBUZ=$nwMJm9?9_`O^jzp2i$x
+=V};4qw6oTVfs=9M%W($UAa`B#hDJ!5^85+~J3ibVT{60D9&RD)gU{IbA)6mRWbY{MRIBmd_A7w*wmP
+9tF#;Fvh?wPFd?G4os?)X9wTd#dR4y2~Zlk!&JVl^Nc-|fz@ah`EGh1AUJn)4zNdLUh7Qnr+hDEvk#^
+iF9h0qP*RN|x4a)E9?H*`BUgD!48TN*A9{*6fW_v8ZO+e%y*7nd}Hv8LlztuqBXZ<3*$LtPPT5()Jo(
+E;`M5$gS|X~$`lUwa>NazA8-#lt#tAYo2jmsem-7iXYK$+^^pf_<smsqf~#54b^XeI_`*#AaAR#jv~X
+b6eqy{7Uq&B?}__nLV;%LTl%PWw>})*gH|<9ieF}P97xG!=C7Mj+-O=<<-}>QIVB#o8dZ#|5p^YV;~O
+>9RzKkO>VHpVue+%?TK;}6koizIf^Nnz(6bS;~dB?xz}4;<BXA2=_^abcNMol73%pCp=rg(v0pTa<Z`
+?eYH2ymj2^sK3_p68@EAjd1H<98Gx3ftwOB|Hx9f<Hn&KT^wVL|slZFyphE^A=*BQ#7&!FT^V#X**au
+E_14n3?D*J;cdH&7}|GgoBkFn|oL@#L|JN#jdo@0gD8JlJQTOq%BW!p^TIfv|!i1RA@aysYIB2^HR-u
+4e4IFhX?e(~d#goliLiS<loYIKH4_^opaB-sK#d;92E3u&c`3>{%tW`qV&I+;pUc{ZEQt-Cd}b{>o4W
+z4H7(HEtS}I?Uq_Iv;%%gC<<G1&xo^G<;)`wOU;Ee5}O#Mlu=W9RA1f$+BR~uEI|HW3ZSSzCWh5Z%K+
+jRg22$B88gwcYW=RArcM7loAW2y&}%xRk`1+*XtZdoj3o6ax$1{i1{@FZ}S6#`YaXeJ53h2#FCm+7}$
+(XTd}$w==^lCYgPL(v8ruY20f(FqaRyERcj%X0@6@K>a$(B$|d(opRHWkhKl^%xOrLXvqe)8Mo!V<Bb
+tLe_^zupWxeYMXv%tNUK#9MvREj^zsZGzT<h-kn@~^R&S`RaIFBr9PS0)QZ;NAa>C<tsu4HnYH92aX?
+eCLoOQBd>GL<gy>}u~`RtKfcG~97tYwERMDdzSTD|^kYI=L$&grX3~ae22Rw4xlUE)aT;)%Uj4E(Ty7
+QZG)ao0sXF^A<H6>h@gZeoFmNV%R~`au&PHOBcs0OVyd~@32wdW?7JP%p&bX_daSo-)!!#rVrJ1O}<z
+N|6(>ZzqcZ`k8={tGpr@&s^-Pg2-?S?T|g;SvI>ob2{e!n{%(Nw9fv({g!k7p+m>ohkBe5p7G^CuUp4
+oiaM?*N&c7{exRtW0ZgOYELNRsV!COOx4w_+PFZZl@&uP;<o{%10ga;HMzTUk46s0ShDEuaZLpS5KI>
+(RBIiFzkDRsvuk(&o@%5zTp1g$tZjkY>Iv4)*Q+q4o(COp_U05ffDa8yjwlRR2*M`l_mD0YJ4A<hpMx
+Gp6+x&XSxT2#L&HC=DVER!<*RLl*k%#J;A(Ns;|vltQ&kn^>l%5{Bz>spD)qRurRIqzS9>$|+|e$Lw#
+X!^OVB}X~*s2_3OCs4Kb$EbK8A&IVYOFt_0sq<$F{e<t~JGYM|k%m&-5C{FL^{R$N_D9!Z?aZxbKC|M
+$8%+TFI6Yh$IXf&+%<tI`+SL$19<#FGo?av#>`MHM(D_>pAY?58D`j0*uV5@u9{r9}N5A78{%?(2Zq`
+MOH*B7v(|c5!mVAk!q1mwGnCbYrCFg{O^yvi2)dtNDP_`8c=)r78Krd#ujw?>k>5HlVcxzm7I*VFDb{
+3_p32)-QRJeQ8gpf_AUtANY`TN6P7gcO2`^fGiRsd=YrZXRQuI$4q8S@4bC{Vo`Q=qDJaDH`%Ylv4!M
+scM@jjzM|j&kCU&V4T`FjJ@?R9g*O<*M>QL+Rg`nDACP|FIIhomo|KXM1MvXV@||uwvZKOniAPRMQkM
+tY=~<sx8?Yw(PFSH*84^6d#&rM!#9uM|_R#?3N`u;Z}w%mcW|P2w`=UV7g$~?4_@{+HqZbYlX!qSMh~
+nq=X7fpq#mR8pNtSMYMU*|4TCk2eRaD$Kj@r+xKfF-v%a>x9^>Ra!-u-D4}9X?~sJBV~K`w?_?x6y7a
+-aPh#{tg*01Q@&&6+tat?4I!yaL;cBl<a8O+EnsN#Rzp-k1ibmZoo%)VAy>t64d6A{_WoZa;Zjc6}^9
+gAPbv_^s;m!(S@N<?)hZyGqX|OsAr6I|At29h^rb<JKbGkHSIVVU%lykf^<U2=8Ly<Fbmg{DFXQ(tUg
+Sm?|<5~!3pfs<A*=v^T;(KR(sxU)Ew)0zQ#z}E!l{Dj{P3ODPTnY0*X+}vn-;ieLW_E6rX3W!^>!rC0
+=C#u7gn5ND*NXA7G}psylV-?}GfSFrErN4~Gz+B{Nzx1z8BVh_L!pK<Dz!lw#?`3}S}csxj4uzI?WGx
+Aq*I^jQikEYkm6E;;jEQroRV;!l4cyccb=5yVwew0Ggd^MmD0Ql=1tPPR(yjj%~>!%D$N^UUM0;NVJ?
++s^kB{+X~uzFXO1-Ez^gMwnvcOeMVi;aY?WrL2RKJba}~_t((Ht}w=~zn93sv2F#Ah078;#Qn)NXMk}
+UZQvs0QwVE$N|#RtI0q&XDkz0!=^qn%r&ISS?%r5V>+JJ(4wPSZHoNHeatb}p0V=`b&l<`kIor8x`cb
+ZN%nH|KO|E`m8hnu}qMk>+JEM@sW5m_wy`EzDh{c^%Aw(!2p?FKONgbNwx{EMWfj7V%l3_|6L>pUj53
+-r5=+Frtv!esAY~5d$~L1`L)io1{x1x%81PPf3^Q<Z`ogStVUk$Yr8*S>(!1v@MW+x3(pO0#+4>fKz2
+uBe}dUU1Ft6D7hS#E<>bCIJxYWF5RU|6uE4bE&<XdhFo5fF6U>;28dkNOP6Ztl0`1BNS6<!OFp@5kS_
+bAOA)y|E?r)o>1u$;akX@Oe5R`<BF9qcSU%I$B$4Ah>1dnT!1tJ`ZdYd|VV6I~T0WCryVY905a0K1zX
+eMl`>f>_y<=ud1W$DQXmQk+Ph~Vb8DMt26X)1v;%~-8LAx8Tw9xpm78fSsl-zE9FnUkLbln0E$4Rpzq
+sVc_Qg+JjV|D1vjufvmkBKf{+$PuggvoIjH;R;ep_lio?dHL2yJ5!YREJROsKDa3)=JINhO){<x5z8c
+<Iem_qYsaQ3D;F53=Fg`ISU1~^9EQeOa_`Z&GW=+dP!BS^Clb+3af%rlo)78e(`|M;ltt+M&}GLNhlr
+nJ-|Ns1ijgTK!jN)u@jvvA!wF5y2bTx=ex>{`vkFjW@kNY<VF8z0&&FRc$<dELbu|n?pP$R)ecFTwgN
+j#S-tHVM`AwY5R0?XKN{}w5{tB!EcDK<_$GX<EDmu!+8wuDVxCpGXts9Odz~PBI?Y>#2gXGooHtA(+S
+lr0l%V~VdPO@%r<i=>s=5Q78vQqmAs*;&qtU-75OXbT;^n$yQ*Lt|Jn-#~b9xkKl*5vbU0b0H{D`=im
+L@-#l8a@0e2Y>OppFY<337;)Ja_<=F3lV&uB%-#0(siKogdvqh*V5yaCnm(umzB)w?@3}TsBn<muP%V
+!s#I4tjGw|R^Qz2_Eqzne5mObbGoEB2acaRW6CJC(%xHz)IHj}lF74qNoI-{gG1)ziOyI|%gMY!tXzu
+~?ohI>6Y&l>-WTJhJv2w9$zGP4?4_vD67C+(k8|7{80RPqiF16J+_a9xppJz%sbe?Xtkp5*uxH^Ec6B
+1|WI9(?XKzK-#gXrIW@bLIpK3_w6G8&1qM(l0I-`zMsX77$k&9-#3V(4zbk)4+R4;nF>cx<jL?pV-B_
+iP{_Lp7aap*>uco^KoL#X|yfXfmcdxggTM9)g+{aExAACikY<2WKT;wy<c*hw593{HTqYO0mcZgG?cc
+kGOtt{Mr#iRn_icOpZ%ubTWv%00zZ?lC6ZXjkvVoCO7r@}zQHtI9nF<sL2tvs7H>MYTNv1uRF#mW!f<
+r>hc9avKlli(L_FLsPVK68cPxt8wC94);?iW1%P>$BOLoSP}X-j~+o(PRqqy`jE}Dq&1I|_Xy4ncCN^
+6nCge*ij|CXt7B@6m@AYll_!i$m@F8|a1@$`>rvY{6K7TC#zUn*TQph$VtNksCO(*R56{(3_&CGJi1E
+Fn=?LGY@+3u^ZKRaKaS?cb=h-=&S0wi&gzlb$PdgUY8%i^poFk%I$(OYfXPyptiZu|*!cZD4g1Jr;Re
+R$&OhzcRqt|)SY$R0b>k)@qX*Lq29Znb5O5uJA9;}!0(ynA-C{Y|CuD|%m_GGzd&0{?Hz7TBh++ir|E
+Zi@l-H-U?963j=fe-=vb0qPG()AY`W41;p_xv_W9*K1h)U>mT>6`Fy1kAV%{5gBk?(cj^oP7z1moA)U
+j+oii^pVlkSz>Hhse|m*9R(TxGHfx61@)*#>**od!kXFfxz+Ke)$yTo6io{qlS8Q6sN22N-qC5xajkM
+)7h;qr^Dxk^+Z|&IaNdyPy1ct8g4gZV**ccrsju4|Xebq8hGUptT(p*ZmQN0G#D<VxN!1S}2R+JT)kW
+KbowL(GaV3prPTJZyZ<IC~I-Rup5hpG0i^-)>u5$W4&d<b(Thye!VMiElPYb&(ka{E!%ES@dyTnOp>&
+w1d)Y0tNNAZupD{iNnR`RBiP^caSIk?Dey-F@#v~`0%4r<{mf^S<OmF{XMK0W<+*Z5c~0wR)$P?O$h>
+wx7N@j1w-!`gzwx1uCt12b`{c^9Gj9;~NCDQj_v-|CzsvXB1FP__d{It*L(x)y0#Tn`M7OWPIaIO#Y?
+W1X@?i~MlWB=q*;3<9>1umP7sJ)K$haL@qtf!T2o%0hnbz}ThN5?nf84m+OUcoQl)J1^IbTi(9)$ldl
+EtbA8#rEQw^%q|?2uz$6xLETGyMf=v7OTOTS%q8dQ4fEcjP9fgWo`%W0?K|VPYwBSVJWp6lYPnFG8nH
+XBY>(|gjltTP20$nIo`^~);apXE!q%^}&elsQgy{^0Fv@Hov!672mDbrE6hn*L4E{o^Xit#PK6BM_-A
+FHA`_)n-E4kUfSE@qo-O=_Lq`$w?*!rWDlI~oIgjZ1@58E|Fk2RV?+-!B2XsB=Y)!P%q&F^Fkw2vlZ2
+#g{2K~Ufd1siWe52&V(^bsw7XH8}7_s~dMQv$2&b@&Ha4F|1;L(~Dv+*@4F0Md3@NDYMmTD!~QB0{Ld
+ra|4u^;Bl@TA6*`q@a$w71Ta~TZvQOP+)U|P+nAQs<?L5_*>n$e&uDg4AG>%U#o0X=^r(=LSH*oBmZ!
+$Hy^?Y;uy0WQhweZ8!+BWGeyg9)$VW$xII%gY&aQt_M)v751!DDX$pmfdiF}8QYF(oLMO)j;$wyA-38
+zOyyr0Qvy=k!KBeOS%63BW5zcN-RSUAvNA7$)1&Zyc@=^Eg@+tWzIw9(s#Hk<W^6VyI!e#6fF;|CAxO
+;5!33rZfE?tO*<G4fb+_bt`-+;<#Xr_N^%h;tjElgoHeZ(Q>g{)@cN*#KiyLmOHRqCbO)m&8PJqe9sx
+nmn~Xi1beW0i(AyBxcO66!`#!N6gsnj<AG(TEKoM{u|KXoT9o>0FZB1h+=$iM7L?&7tQ=jBy_SvN@7P
+obP8hQ#K_F!<j9D1}ym{3nLkHs{5+#sDujd6?0uW)v+rjb*lS1=X}{rQA)NgZ<<E~$Mc%?5<7lVTJ{<
+-yTx)6dtn{j{Mmn=|J#qzpc8PAf-wr-q+phUcPO}0!SxDOD)_O2KPjjWl(4sgBNe<}!CMr}Rj^pWRSI
+rUaJz!vE9em<VOs^eDL6pEQ3_tKV2Xk^1y?Efl7hPxd|$y@1-;s;d=wm@;5Y?uRqzf4?^W<A1ve_VL&
+1Xzo>cIA1<xtis-1*g6da&nl!9gjZ%{B@!MO^qQ1Dp=-%#*<1#1;#?IpZQ!Egnm6dbSMBn4+En5Ezy3
+f`sQ0}5_XaI1oc75q@aZxp<spntH!M?LQcHnz9)th*W)$uRcr7zx8mCES!OVbdq_ZRuV-uzCOHo(Db+
+nhJ<v@+^9bbUIe_we*Y4E3g!0+mZ_M(lQDQ8JoijSsKe@1uTPpgIGGtVK`bB9;p-g9aAukeiWQdWmDL
+AHjqWByfpg|Hk>6eiwlRT?0Oc*V%Y>{7On-%&T^TJWmAqB%t&tm{gAIwgb!f80#2b+Ca}r$a}T5WGIE
+cjl&@{Um*k9MCo+==)m)fKgl9O5pdVZYQWz6qWuuUDSUy1`y?ONCPSgU=M3G|xnZTuV;g%`VoFl?!5k
+-Y$N~QmWB9&Rhzxm{nPH7a7Z!Vinwp<DU>gS3S8q$J)w(v=%c-ia@6)un5eTf@d`Usa!|I>tjhRV}Ox
+!Fbf@HbMvc_LT%<<l?66+@;th<xG%l`_>i<XR}~2zeW&D{%*1z^*{#WG7dI$s@{*BE1|DUQ(7Sa@P1X
+P|yZmW>c69!r90|*<f<bq15^d?m-gLTsXP&GqqVx<q8}jL206N3Ir|%jbfSwPKgvZThQ6Gyob8VJC|b
+EglyyrZp|iIjg&(^#Z6P?D^t%QD5=#-O8U%k<EXprx#u_JcjW8Ra?fz%dn~0hMU_v1%SUoLPjC-)_1_
+^MP5Bn?qC>+;<6m<Wq_@D2q10Yz>DWaH&r)2y95=uV;OU;GmfkggvOMPrN-tmUn#*tapU4kVoBk*B8|
+KO{Uz8ZqolD^hD3)EM(OgZ^>SjZVT6&Rx0{>)@Y6dG3m=9KxC2c4<trRcCe+V0K+49b#+%go#vVP|&f
+3%8og;Zu-B1`V+H<l-0g%r~ShUspy(^5atG`(wnC%rkU70ebju|TAr?rI}$YcU@!{XZi=jdMyD{7-E{
+g_eFpJq54xgq(zm_BKoOO=t-+Ts)Uj(sF$bcPqau#}^@_jG|2E2)nIWe8b(!@5<9prT?=jgqCt)GbO5
+}-_YJ?iZYV5P4-y%WX~2U7)9@vFI)<QUBgDkNxy_g9tsA2E5~DcW#3pM?X8tPN!eX7l|4{d*L@@HLCP
+MZ>}{33wp!ZTDf=pA4^eibvUgPWV@_%Br0glm-dRiUYiaMI<*)4BlzqCgch~a!O4^NDe#+iM*~3psdy
+&QuWuLF*_ocMoq2Z_O7nJ>=vU6qMpzMjF?iYx03C0^JiyK5Ar?q;qER23eD(S`}$^<gaqL`2piG{CQx
+eXFOnqN^_BlX!L<Qz5LkSQR&+k^znqyJ+>yr#KWT(!K1NI$7jY9n3z`p+XD<0ZdP*Y8rlOMh0XF_-(5
+HuGy7Vu{;NcZEOq_)Fb>9#{C&;u`;_+5f-bxwK5P{<|eW*YLa2r;!Bc8h##E_-l}W|LX7e=*O=;{oHN
+tepgJgqPwyEO6hH&yQvHAiD-C=mn<zQExYUPW#!8qD=JprvugFd_pQ1Afd|(<^zb8(KKA$%>z;h->Gj
+V%``m`-|MtR*e}Cy88()6q)lIK$-m-Pu_SfI|=Z>9~yLRu{yKn!2gNNRH>+su0jvhOH;+=Qjd;f!z|N
+8Kwk3aeJv#QU(`0~{0ufBFxe^c}Aci;a|`{PeP|MF|ynfkNm&R_WLB4;`ePcQFQKKj<ae*OV%41qyy+
+qDnAszXS}PMy1S?bhAcqi3()efstb?LQ#w>Vbm>hYuMV5jkx5HBlo*MvodjW^7FAthDru%-LDlx89aB
+CpRzu_JTs2eeS%X`FGqrW9BW%DJ}coxnSX<#sAav|4--tU*^AQT&&qL{#t8X{B;S5*H1{AIBD{fsW(i
+Ue&bD&|1JCfui!uSd;Hw$LsznUx!E6RVy_^(-p#(UiT$1?_Ek;ntDD&GZDPN#iG7ntWBY4O?AT_+7`K
+vMe>Zn4{YKIV2xFi^u~I0XboINMVU0-sj4qe?>c@opVBtPkxzHGz-0+wb#A6n-GP%x%8JG54PouE8&A
+3lcYet&uWP<r>J;ptxNz6R#V$O~yUd;FAyV8kQ^FrndpD$MTa+Ez$j62h)Z<Eh>3Oh@zSv8DDWja{JX
+>gVFX!vXRYItgRY4~V($o%HJ@X+$t^40P|I;mpCpg_zY8}Jw}uy1g~N)kd9HVHRP%z$#mijup}6bkL`
+mMG?Y4Iw5AJl*Z7f-ae_mY<fsmY$Z57N6wGI&#jOk%caSN~h;)V<J6980OO>`JXsAJ$<mWB$2q7l*pJ
+f(lX}E%5Vw9^?7p}*(apg@?f2uk#EyHO!nFKLP9p)o+HwWx95t2H{};(qYM&L3({QWoH47wo?75ySbS
+=(uu{Ir3+W?mq>1#97R8^Bw2%(sBMzIKYO@!lr;2pO7i23F@g>V%Q0TG|zcSKuGu#}e*rhEoFIPBDwk
+w!wLpq3$I0%O@@E7r}&CvD$re)`*=gpgJOSNT_Ke<mW%qW;J>(-1kTU>5tUcnsUF-_%1{t2mtHdC4{d
+u~QjUcNow<u2W1ax9TjPMw`GF0V+&oJu8Xq1hBxVh54bC(sX1sHhECjp#4!x%Ql#{x0!jt?;MAyxFrA
+YvmE>WwBVi@eB*6_5l%$QAyK%^z54O@M%dtG~6_b=1QNEl|m0qQ1grE(NhTK!}NMRrtEs=qqS+Rii__
+mUQ$dMc-~cf*V5wl-rkf$Sy>r1)ZyX9NyW=3bZEb*q-n(zpE5~G!pUo6Xz{XQip40KtgI}(-lr%(e_n
+q6CF%dWySv{RZipS2>*>WlO>e@-PK6sWbWnPZYPcH8X-f8-j6A!|q~to)mYQR<6cpqY7_)PY390tnw5
+$xweP=2E?3|2rqb<*vm!FYq%qYr8v)fW<<!CMB)LdBLpGg8?OwBc>=2O6Qii6rif-<N{xPpw-bYo^g-
+W-Y{lj)@`HlSYR%_2Np;%L0xo>4GAJ9oA*Qy_AQe_@8r=H|@`ZRtctx;;N9J57ereB?v^M10Eu$Umc?
+pk@CCI?{*`TZS=T)-I#H5NP!4Q`oNw-)3jnh;7+6V_IH%hA}Twiy_k+B-S7s`Ws78K2=B=Q*xBz&6$%
+*CgLixAoBVRqH^d5Q(;!dtW<K$$<9@bz9gNDf;ri_se(yFIw3#sc6%NnPCRrg&dVl7&lI<0Wc+#Awk)
+u+(3V=@=6r?8Ht<JKslqfvzlOh*PxrJg;a7TwV6l4`RV-7dj2{@)Jb(N3>Ss(<RG@U4<Zx;D9{q^MoS
+eLQqP%-uQVcDWW6L~gq1+2*+vjBD+6s-K8FQ)TWoH^Q=H%Pv_y7I;W@Q*PAx$Mj(;%jr5DBKt$(WO;y
+0V_E5B2UnX^hrKtR6SEGd5|mdGg;LJoHuGjxFQwon2CsJE=!6O!Q($&OrZBIO7H?ocS}Tk3mOLIHNFo
+PJT{Ca*k?8W)uyNoG~dQCnL2mW5%^p<0cDlt(6&+pFWFS*(26a+v!=mbbr<^(vP*9WH=U3>6gEDfLyE
+f=)_nbdRM(I!w(?y7&p7l;KdA)t(ak`Uw%**U)Ckvz`8`XVO<JBYOktlf2?g~phye(ywioT&uPH^H!Z
+HO2lE};DqQEMV}69NuDvg7A8BCihX&TRsq#POTdB|YNokd&YvsXOP4Y+z>gdZl?)76GZ3NR>vyPE^)^
+SqsF)jTUdos3<-XkjgAc32Yz)cI2-G|yDdYxLB_N`d^bUo$kgM3}#D*7_^Xy1l#I$tm5OLP*x`OT$!C
+pAxH{Tjlz4)A6HksVlou03l-eY_S9pGE7bfA%;cq0Zlf`Rjt1&gjpK=|RjmC6F13f5w6?wVkR$j$KvR
+K3~_xU-ESd@imgnlNxv%WFX!XFAT&B!=wg$o*ly2{-HEJJ1X(%IH)!2n!cBHjoib!>UOX;mHzpDN%+W
+-u_|xyo_LT>{GZaActPe#fnsE$^YLIlx<^Est~XfEf^GDs`><f*N$}91%AiZ#T;)=84P*VOnJ^wvyz$
+a8uW=1Ld7k|K9wp6yK^=Tqhx9hALu3H!FsWT_P*sCWB8+JyV@K&7qtXj<D;rI2uNp=9(K|zh83r@G3$
+!@xyhz@CSi7MHSDZISGgdviA<lU+4;|wc2lD4Gdqab(+8qn3)bJV!^^q}*J=g-B^Jr}FH+nZ7k+9=HK
+h`;YBI`UQiFJ;gz&h*VnJiUVF8E+kC-Nkjr?Tld#U=BVI;4#{(?L(XYRx)KAxQLe7}_phtJk{DzO1t?
+kn%84`fXU}dqwI=x)5I$LiIgliXRJ!B=aPhtFB9H*5#?-+ICez#|)JL`Ti+>;l3mzK}d(fcc%8C^Uw~
+pl4^siJXYMm*baJ6D7tlB{8$%T2+3;)lGm$P7hPK><4{_V6H15VLzSv;imT4u%-GJG-O@q5AFArT&7X
+Kcd?0*@{#uRiD7T(78Ox-%M#Zb1!PsK5cUH0=6x50ZQM{m`jr05L7MhPIGd5g>({=S{U5VFSiPv3;*I
+kL%U59q8>TrzsE$W#c)iYmF&t!X`7cwWyh{}fAk&aV*2**~CjmnnlW+qMGKFn^Yo4Ve<taoHb*88VUw
+INkk9cy3NHa{pOFiFa_FY_&E<%+AL307}<Z`I<8yaOn2;?EStp8~>H@-AGo@5-#D@xh&peMj%BTH7$V
+HS1=3jde@k#JWYk%DU+`vOv`~<a=xN%g0r}kj9&f80)c^+FC7*?s^s&8OQ<)8rp5dkJ9SRW!mlV62?9
+*X-G?x-;UjgZ}<9DwLa!k>7DPD;+f<Tjx??C57Cnh(@XpgA%2GpZJ*yU+>dpSOlRG7H?hV&h@|D9$K~
+a3^#jy@JkTJ29f$j|K9RS3_R(c}HkY2U^X1cf?{S(lJ>iyK4?Sx;#lYH9U2Iz*THq9<6Ry?Oy;M(aR8
+P|bsIK{=zRJF@fXY$SS$at>Lx_Jwuc)(<=4XlKN1LVjn3vYJ(@VT+PrO1uC*+yzuD)B85tR+K;T_XmG
+F+w6vi%jtKBBi;t5ZVm+E7@MJ;Ie_uFAbm{_9oxw}~;YV={a(*?YW3vU}|Eb@Cq+=I7Vk>ZGVc4S2Rt
+M{7kL_4Z)ikvgVp>qR^cU~TX9OA_s5TS}*m2WvCbH@xG(084jUTjFsLwU>dcU2L0seQWhqt&VwBdMKV
+*UHJx4zJbhe!Y`lV4ePGj(hgL9?IPL~G>sSTif5pBZJ6N|!Z(=uLP{g}l{WbSDgH^i);iW2?RHzpf-m
+7oaHy08U6b}gs|QV?plPX=muiy=2*;sb%&0489rHs(Jqf3>@7Ra>1Y0EG9Kky1!kO0RL!p4KyFcr03u
+4{V14)Jqtb4<lEve(s0A{q=X-s0HF$s-eBMT_)TqavXnMR7I!iD;{W&RD@6bEsZvx>1mLxbBF<hJY!*
+5dP@bI20@=Nz(x|69%>ch|We_pQ)fn_-KUQ@#Y6@l9ikye4P3uk*#{&5~<>EK4`OAR{9odsYF~GdHkU
+v96&l8q(-oq-8HCpvkC~zTSIUYPM~BUcqFV1m|R^l;%*#+>8Q>9h;Y1n3t0gYb(gH(Bzfs;8o02SeQL
+KSEVo|uK@w}6><Rju{3>momlSVL9U5;>GqtA@!2^U*Qd@QGT3vT6R{%HEG5~ViS`^@_PF`Bj4653veP
+qSvr-G#{fZ)2I2zI3hO{T!(yhcFtU>8L)Ox2_2xBb4L#>;d($fnBHe);!@>0_kmnem3@Y9V<nNk7!mY
+LPkQHDx<5{u2rE7WY)Stea?Nzhj2EV{|I0{V-a2IUnA(@aTm!lPRQ?J^$uzlF7CFeooO*G4&g;9<>3%
+}3eMj8&LTnC!EBNm~Jx%#`{0h`FMPG?>_}GE8cL&7KcdPGd*Bv=yF)68OSvYHpUuKHXB3mXQx`Q&Avu
+_A2Oa(c8l_uF#w@%RYN{MnMuW5F*gmYmzPJhHT0*HD_w>yzJa`l3KVW<mKIF&mV8kO+yNnTpN}qLf{j
+dns2igK$>hBMK;z4ZZfK+i2cMY;6rRdwvE`GGdV*<r;_nZ$Vi==afy44M_evBrRHSc(bUtFn?5-|J2y
+7ZPI!J#>ALgz1o1s5!*)r6CU=P-F5jdZpHoO|y&*Nnp22R0ZL(&&5jIg&#o(Kpq`l=<yQXAeZ8VPfG9
+BS%ak5D7X_T|51MDMF&W&p_&CyVrH)IqP3fp@kjFfrC_IOUBG(}{#0C`J!lBBY8$Q_=L6lQ}zY#QmT<
+E3?wWPi43F)X=rvkUTau|_4OjT#H1w^r?@<hkl1`&`uTW`eWlWo=H%C=lf2rb)yo>xYr^l<c(I6s89*
+ZH0(Y#X^qf7uquBU|HK#C}<%QtMr<jlaZ0n?oqWL9@AKnNF@o}W4SP!VriT)2B}Q2B*N@Zl1pWloi5s
+<)Y%#AW65)p8phVinj~vIg?`1&PHdtOYrsRdmSrZDq`fc;*^jelW>O(h8!u9vlpzZ^6)`Ni=@T+#DNX
+cf*t0=6#s+&P#3m97f^EdL2q_UN-)Zdshetb)IrOQ|20j$V*xNIWCo<pV+Oa|wW0l{pG_^N6l@iE)_d
+!0o-wo9`yvOf``p@&9=ReQo9@IIgO3~_*r-C>mgR{`M&I;cO<DLwhOTowaIG3VVu(g7|%FoZu?<y4+(
+4f){RBkwz-B!JD@1fj>DZ5G8W0gHg#hIksCo4EjrGKN!J4d<CVK^Dd#2F8q$u3a1wo>7X70yc(T&ny_
+lz*v82RetbUH-VTKdJCpukw3Nxo=VU9Z~)tDE|;2$qS8V?tE`r4wrlW>hOOw{=Yl@KN|o4*9VHG-0f@
+B_pN^w{J;ME-uYXPCXc6jwD{|OkbHRRNA>-LdWtXowCTU}Xk(YyChp<qWxiJ{7^+}z1&s=JQ7}ZoKn3
+**G6ieTNm!-ey9ypuaIb=u3cjJ>Rs}aHxKY6u72KfUdIi@h_^5(w6<noYv4S=QbDE@|uHXy>rzmJuFh
+;>}1&s=ZDA-;>y@Iu872OK9_G;XwoK)_|6g;S4rGi@(+^FCN1=lLLOu-@rQxvo+7^R?5!4L%l71S%p6
+s)gTcq`~suu8#`3LaDNpn{bOZd7osg3A;vs&AZrmV(n2v?>^`pix1+hR+!ZPbv7Zf(I2`r{FRL|GV0d
+l>Uw99sk?>f0q{#&Bxc;pYUH)x&A6)zREBB>dWW**FJ$QKGNUV;`85z;=lE2`ShRkZ~64#|192r<@5i
+$3^aNEkBh{*fogrm!%Iv@*3-x*W$FB-J(T~mpGo-SGZHR&qDp*H8ndWazzr0B*V7+9ZEPX^B^M=(QSh
+}Z3~y3!Glf6;)TyUBvC~gEpSro_^cOuTOcQB>I`P9>J~XoA4{!Z&=%wM`Ir%A#0oag5GUhQgE~-@Uq8
+d-HSL3}w>ebrk+MyD@8~fM5#p?M*rF$~=&p{2>rhUOhHC|=cdADkT<$vs<TK*Qjhy8TyFu@3#<gWA8u
+}Fe_+c9>5+(QZ8NADYCUPEvzy)phe0sj_EV;FLOk>Ie7G!7#7D1zfU$}lMe7vm#q4;?EeII|ODUSv)o
+_$s|8eRS+yf&rbSIgnsqWsV_OqRgcPw{*stD9V@MkMxFm>ew#?pY9^f>j_3c7dC~-A$V^$8FvlA<Mb{
+d_jd^fcbD!V1dnu=^nOdQjghfZ3KK~1+MbN9AhVU=5A?oB=30UQy@-Bq3PW&iFPUZ$!9jf(^QSb!2`;
+1eV{!)^*_Xyvt#xb^!OiqC!gDLZq5UMzkpv&6H<HYc5**f_#%^SeA{gIa@-Ttm0|RJ`P3|}+asSnn2b
+tGhE%X`*S^%FLD8p|c=r>5Z`x6|k%r6owAA&YP$5s&hlB!cBnZG4?O(cy;kuSmQNSRI!!H?+Gd+FH61
+kXiE+RejgJU>i^0W4K!z&D4<ys8L#4X56N;sz27#Y^UJf<x%Nn#?f-O~Y|cfzl*+klsi#15UU`@-2(t
+^-(mACHEwPYoa9I0JqR<BloQY-;0ts=tmIGqp@G0V^av;9j#=5;I?QP<_&_kkD|E|g|QK=A0^WS>@r%
+02b?g5{E#of<71Sp66`Qm$q&JvF+>aTq&LCn7@7Vkg0>i$Cg6+84ETjI=bIRNncf)`w~F9sGszN}V+f
+X*6+a1nsLUS|oNJMBiwNE_o@9)|rx4t5Ey*yM0ry`k%jh7%sc}@kWS&m&Y@DiJ@l=lVo+S4R1g}bv>4
+Xr>NM!66GG`Ioe!Z+0ZxH-!D#>;$!js_n8)&Xd<_iRSPLsIxCiucM8U96ri>ND{LSc#tUUjo{4<UGl-
+h6VeCwTh|>24!fIYZ(G_~J~GPYSb@pzRjLLxR^NlRS`n6v4b?89txj%gGXlO#}}sGvK4CQhoq~XUTLz
+W<kfilxM&W8M4lX5Nwl4VZa}PUb6`&G6Qa*Hww5B9FxVE8SN6mJ<7b7U|hB|ClIWocLtg339h@H>IRt
+s3knD)GTR6~TOi8__ZI9ZlzCMWoNiO)PVj4+q!H(AyzNT130jpoh2WYZYI}jhe3G9#B|l>bK6s~$yOv
+<U0^$ScBKYV6#S?-n77?9#oq*#PQ<;+4O7JAT>nIFh;!?_w+>;0nDy2G4=5T`3OC=6~tCe{p+{<WwN?
+`y~?xy_640!r(MGL_#%TzrgIJR81^8|~_WjcWSmHR=09hVcYD4i|@U#52h;kJoj$^BH`<X%d!;{&pMy
+Ab5`)<W&_gM`E5RG%q4;MgZ9eKN-oyzdFsCJ_Aj37PLNFh9vy4u$c5ipCXBQyWX>6$FnzEqU@T!OrWY
+xeLL`>t#Aq2tKo3;<<s~xMw7uW`d7zr1B;6I)aHWOa3PjJV$RmnJ*B`e?{T|82l>9C(4na^)-?`G6Qz
+sOmlrQ8wozLS@y*n2?lMEIJ77D^%f}uPJ+eTXl_Yn!0WdYugIK4aPfA<XM$5-r*R9p=M&sPZxxv<3BI
+(G`T{a<B-o`=_HlrHcS-k9f<NscK9l(ug3I?(zfbtAAo$HbDho1yOYqkHj3tpdhhX?YDi1OPnh&aegy
+3WJ=94>Aj1PX3Xhax-74)uvJHe#4h%Pdx5bSrDcuVFOg1^$ck<9f3Bi?3g6PY6k-te}h5#K1_TZ2cb-
+2gnI%z!wjzn<s<#94f}0~(bXaEvko&QNB+dCH7);SVY^;NO)Q=etiRGhmG}1GYOV%{ZGpT$uqUC^O)#
+$_$7zy1*0gh;j$4R%Sq)v4k10yE5aP<yd9Dl1F|s?J4$$LP>nuFtN8%j6I@day!K>oR1int|ND>b8f}
+Zgdk;C41Xs<>?=_qTsF=^2x9LjT-P)mo%lk_+Kjtcb8E&OPmvo*?(WN4a9`Vk`-T?Wx3=JZum$&%Ex0
+?IamP9(E<Ms0kn9ojPN|R8Yi(4obPe{9xzv{E8|;;fs9*MOu!okCf1d{XvFFJi*kCW(PT{>8>{UB-;@
+fJ?Zr!P4H@W3UWzLLDe7-<fx_?1`=`(21Aa?Dw*RpBTrm^hoY-YFHWkD4e(_b-LyLK&m?z!jKwr$&3W
+o0G%;DZm?#ful2nu-;Rz+}ONTg;!`bGX=_84k>wcT`x9@*ge~`(KzhZ@~ft_<<k3aFicDL*56-t|!Y6
+6qoGt=E3efdid~RVHeXPJ^lNEU&s{x^A^<Vzop20zp~Fes;@`<%*+eYz8K*TAI;1>dO+DPNdFvizW_U
+>e}Vk*P?*IseQD1*Bf=j(e00BbI3v>k4Eakt<<Gg}=wYBnZou!t(X*eCtq%SI|D_j}AHHz74r#!Cang
+)93UDEZ(kHw38;9EWfAsKCA|Vs}(VzVLl6F7HIUp`6)BjdBG$`TlQ3|mF?9yL&{^21}GJvqxzkPIl&l
+eAiuz1P+Thl0BPtnDmCE<VM=_!ZnFB~nd6yc9@{@c`@^%sOU!V`b1AFb!YTks!AIHfnj3mwrXrMF05;
+HH!t=$ENSdb11ND;HOJ7LO$n=<fD<(yJ?GvY~0=)v0jq@y53BE~b|kGabF2^!m_iptn1RoChjd|2;X6
+jErQ7iHU6T<jL%gJML&C<EvJ!Vt@PF-`J*2n}m$N|Ni^z^y$+sInT%TF!lNQ8@KqNB<{?=lW)%4$rkV
+YVQ1!Td2BHiQRYt07w@b|CP}z$@%O(bZzjXy$4}qP`T5L+hfYtOI(27eLgWhslW&W^eFEpNCG$n){dV
+3wHJL5uPw#wfD$?7w66r7I{JS$}HZmc_H11o}y*m%|>CTy_df8=OEsT$P4U$jbQH<b|zGWuS)2B}#78
+Vx9!o$Pa=+UE@$z)=2adCoo6DLk&H{5UoyYa>w+02<U*`oCEZ1(Kg?ABXvWpn1tp>c76Xg?M%T*#iZk
+7Y01F`B))D4Lbbwy+hq#j-VdCiX}{410aaNcQ*QIQH<8S!~PNe766d>)5fUZ)2f5IJ<f`XG8XLHg+#(
+!`|d<)Dg~Ryv<qcyPRG3A!idm<E)V0sb6q*^VgiERdF`!Th5j&S;ET8%h~GHtJ#AOKFA(==ppv#qmQz
+6>(;Sno_R);@5YTA*=w)8Cfc$$-gtw(vu7h)`Xgtr)pE9P-#&Ke&>{Br+i$bu$B(mj-+h;T_~D1_lTS
+WjKb$_v-ua2M&p-cM$VGK^HT&g<Z`hY-IjgO$WoOQu5we2EV=(m(Nlbhvs9(jHFUgG`RrWSifsMQ^8_
+kEXoB2dGpWn{b^407${xW-)AJ^Jb)}G?`r1;?!e>BBUp!hdZ{Mi)0hzGId6#r3*{}RQor1&Q&{wcTke
+JOqn#lMN-+bI4@ivK*t-%at4Q~Z+@|5J+pCB?6%_&-qmI=A?P`_dSV=CJuRuUkd)m5nPIJNi8JX*4$E
+CxRG1IfU^~CNh5NcE-P1&3Nt0jMurvZ%^@iQv7g=PxI@IH2*wGY1PskiqEI`%PIb26#pL-e;36+LGe$
+!#izkXk{9Nd6u&jby%e9?pIREkaARA>$58wk6#ov2{~*QRO!41!i_hAyB-VlIY!9}Ig;BK~&W@7g*Rn
+XqgKlDcNIv5e?_&J+M;TxJ3ga&yWc>JNZt;y2e>lZYp!l;Wei6lAN%7ZH{8uUd4vK$(;!~chKc@JnDS
+oXh{?(Mib(BIjrEoW;@C>D}ms0qgQm75$?581|{W6iWy4yKBvzoKBFLQSOICsVGOz{U&{IL{&62(uW_
+;V@#GK&8Y#ebIKZ=(3SDgIH4|1rg{af`2490;cPohg0~ir<&wUrq7HQ2gl>|8|PMg5p0*@pn@E58dK7
+ev;+m?i3q4&NR+s7UqNjp?!My?AiBPm#=ADd|aH_93MM&tjU}_FtlI4KE1EKwwIQIIgY&PFOCdmb8=X
+J1i1EEqw<eWjJ3p%rQ|KKrm^Ep$yXylpFTZ%DF3*`7*o6{)*MUmf!Cm`!@|ZSHy|0G7_U9!JBM^g9yC
+a%(7R_3W3OuwpVE(yj~g4G+_hcD5b~!0p(;S%WQsr5gvhb+$(`G^>nQyNj=g&t3CTED`pLoV+qJuHv<
+MI;0`xE<eykb3Ciu5+*Y!GuTEYP2K=G5uni8*%kGI4pcR~UkLppT0%HQ9=O9FC$f1-FO{Z5SnB!dm`k
+F_Mm#m6UF61z7FK>qy(i1f!Ln&T~r=EU*cIt8l$@b4cwFoDVRV?`Dk*OEsefa0G>e@Jv9u{hBZpD2hB
+IcWSR{FBW?Vt_(CrO|2p_;&5uQ2>{J^6*y4F@r6X!S&?dZTxtFLKl~Rtijt5)h65$Z%!mGkMAys=sI4
+B$Jmi3(@;-?clg-k#N@;{i^9^S@hC1fa$M|SFP-o8$^*QLGn-ug$s+~_wr-`<1z8D0pqLmxmISH6-*n
+Be@QBu)`nGZ889R<-IWjUfw!uF+d7NoNbQ>S<;be?WM(T0qhWwLb5oAJifQL^E{A0(GxW=;vfAEI{EX
+FUW&4lFG#IeyNYmB+4FCS}Q(1=76@iETL8|j;cH@ROE5gQ%X*jvgErEexdjI#_I*~Hr|ePT_VX=D>`7
+T@?uY~sy%<>Gon@z^RFNL2nc<GIDnyv>f~irLsE<GIU^Q?&8iq)C&+cw|w=wXU(pb46p=vTO@mTM)}O
+Ew!+X4=-ZZ9HcP?wR6@FIJ@BsIc6v#_`m}Xuty$wggy4yW9-Q%pJeOTuNPy5S6_Wqj0d-G-!8@q?^Qm
+>9-;BZMj9*Z-Mg0^IdVjd4NiUW8T;akFW9M5r&vu*4g2=nZ`n^j{vgH%zyA6w`|Y>i*y=N!y-fDw=iS
+=b!9A#>yM{VCs+X(MsH2-t9o;?D(LKw8_$D@l?_d-8L3TTThppxxvzPg4>c4B<;!}s1L^Jw)>TFkCP4
+S0Q{1}QKPw{W2__tI1yD9!-6#o^9e~98Y^mG3ir~GG}@_%)lqUm++)Txu$3hyWsKX(f0GkEY|-1*qa*
+r`)Uryc{k2exU`jpVFj&(J=7`t=zc6lmxlK=w{OdiCo^VS@s@4-Oj~#=7(z5IVT4@8BQ`&?X?DTbCYv
+LI?Z#hIUg9Mq}uJ!Fs)aD8=dDwoO2%kPZU|`|0(*t-Zbd`x^|Myjpb{(A7`x3x~mh-GhB}K0OEH>Dt;
+G>0cG%(T;3>y?k4@4x{?jqfckgF62v5x{{sp59`vtU$3yigTtU1iSi#17S=l~tVfujuM0gmqtT;>-1%
+Q@U~Ci?;XKvb(z2k=EeyirV0U-9K7{X^Q$!HsW!u>+c(fzvt=wL3mFs`U^Jk7&0bd;X4({H)JD7?WD#
+7};ZrvI?ySQ2s>c62foLUNTY+U2`U-fvZbp3xXpnAYpbdL~!;}cIjF^&42*MIoohp)c>{`;?~@A=}p@
+4h=t@Uvfj`Q-<im!A6g<BwlHaNvMLr_+Uv8Z~OLqA5hBgCq3d!yn-v${HPW+rEAK_96&gwYkE$AvfQA
+^Vs3^kh${*AAE2R&7=9wojdv0Uw_T1KjrVg|2`I|_-CJe#%cb|;ZHPh>SsT3I-S)->*ps<oOpcKu3d}
+AjT;xiWI5rJfeuU_=?5+-S6}tQtbM|OPX)Rn?3_7sBI@hw&oMNESB`&2;`dRigONz%nrp7<3;YN#f&Z
+ItzR9<3+r|k$zIE$Xf%oatr}<Z3eZ`L*JI0S6KQ6){4vi7uev0Be@$$<rFWj<a%R(BmSxQPuCgkVmn^
+&$}nMC7i;D*l`!a8*55K&%Ue$&vQL&3Wcv)LSqbpI&+KmYtQr?JfHUcGt^BOb2&=9_PLWo4y{&qt3Q<
+^THEzXYCCJ_2``tE#H_M<0D8?4U_|Y0M)a$=okL{`g}($=!KM(|PXPIsVg6KmGjPd+%-g^2;wjqrA={
+9MO3n)wf}U!%D>cqxjQ!3VFF+;IF-)L3>dq+6(+OIyAHPHl;;-31`<!W$t?2-SPez{P*nHvx3U8C&@%
+9XxP1bx4<9r@!^LbiZb~8^UsAHIHFEa9TxDNcis^%>ITXK5aoutgE()!^_IXNWk~g(qZ}??)J_@6hwS
+Ic{Q~@{4iEh!_}A6daq5PI{6|Ga^@SYc1^#cp{WgF7_18I-Ij1qRsKekt%Hz<XL*fPQ2M-<;b>-yAlf
+n!d&;~%>P)|@sz-{yQoM(K<`Pd_zM;_vQ@IKDNc5yzalJiM#a{jl|oc~%^_YT#SF29Sp8~!_X>{#8qc
+kf{|hF?kTCCWq40DQr7@D=o+EPy-ADEm)8{Zza#gXiEk@P!<J7T^v3ql};WmUHv_oX4EtJo+f-*Bs_N
+;vmtmm-7KcL*IXL-g7(WD?jG^!i5Wu{t^7Ct#`e^UwgH((B3~&Pf!maFVjBd{MwV8$G%7W2MtsP!{6e
+3DAADn0q1oWIL|!JdH4?3*2ERzf9A{?$nuJah=`sf3#Fie%Ha}PfD7aZI5tNI_zs?<j376Vf3&+aXA=
+lbApEcUNYXIwUCzf4t|N(tVI(*CCnf&b?{MDx4R!wR3h}2tgKO<S<e&N@j<;!<XmSp|qAVa+kPVFv)N
+#le<R0}xgQypHCV$TP^+ZEF$^ZBdBn_jFaX#X0Nkcf*g{yaS9=h|7<4<kz>OOt?45M;biF$^%r8znv>
+n+I__z!>d3y6ny#{IeB6xC7C@CoN}M1zHB*!Ug4lIKTyA8rZ%vuDq8>Sr3gph0^zI+`n!=IA(o{yaav
+?J@q$f_VP$?PK`^c`*Wso4(?F>X)2Ptl~ToH2h1|XOfvwT75nw>oeM<zB^=n9{7f=)70WNcbTfdpUQb
+fWMpJdYR^l7Kjfq-9gus-byGStS|IC?qt6a+;?K<+#h<p1=8qSS5j5bLS<U&4iiV`mI8Pv)tp9SW&!9
+oo=Rs(bs7>udZBh@Sq5D?Zw$ihtIsAL}?CFw!>f_PRUs4{ZzvvINvOs-+U84c`Q@h72A57;j%pb*{oj
+004MKnA{G~h|4_Hzc&FrDz2@`a>9tIwc8tIueYh8!Ti@8P^ZXdwO@w^98+D3UqS9RAea3;CzIwi4qZ)
+H#g~jTX>={-7n<0{&<xUt410FWni<|3)-CLo_^TllT|@z<Ejy(ct8K+G$yz(I&-z)KH&6gI1qGgI1r>
+CWXE(k~z~9f7CPdFEq|_y`VvRo6^x-9nomPvt{uJ{>p-p{G~gh`SV3Y!`#vQ*V=FR2j?UW$wb4=UlR>
+(^*LVEXVW{fO^SM3_Rm_I)Q@QB^?I}IlE9zF_bY}C8`hJ?D5bz3?IIv#1~g#IqtOC4^tIZ!41VZuF=o
+KCy?6xQyl4b}nP~Vs(XfGNc;lfw{>u;5SJc>46P<IYOro^<ti~Aqb~KMa+6ztoM~)oX7iFQnsIR~qxB
++*K7PS3n?=i1IyNSLEeH)$~OQZNUqT#hgBl*S!(fmcCp=o`-pW39=b4+~2ZR7Z|>{wnhJC-j_H*<&mC
+jRLM#{>=TV~jy+jL{PQ)ZPpECwx`{Z{P(8oPam#1Mt?$1Z}+g_!#{!XoURlSvH3MlW5pVG;CT(G*DeY
+eFhCrDH<M9G^_#*w~pg?XAupVX1*xhB51&aHff~W7-QJJ=H(x42Kq3JBk*q6uz|nu!V4VZBJFiAlgrh
+U-+udzAA3BN?<%>5zp;d9pt|rX(eMvdpV1~gX&=KMD;UGq=8xrTa%1>CIVSEP8tx(*mJ$sMi3U7P#~8
+P~(|r2_{7Dvs{F7Xlf|k4Px{JT~;){ZwE1^T91#z9nU*)??ui=$M!|OzYsLw<L+9a($ucJ2U(Sou3!T
+cD0AJMRqXjo1(l#v`Q$uJ8<(I%PP#u%^GG~fOMe@*_Qqoex*2jI7C*)kUmP3gECEx-MCfq%a1VZLugE
+Z<oY#a~zT`4zYNyk61ph@#<MlB1QknRt1&qydi@V^IH`K<{H;i~jd5cInd*WlsI?>WLF4#^>hdz6o4W
+A5jMuEm|aKaF?&&D-%G}sit$R=AIAszT_HXpgvcq`rPEX?Dz-xqwK9#E6RaC|NQe}?1iyRd3iaXJ9n;
+&hRe}$xiZ0nHfhtME2z&zLo@A6OHW7eY1OJ#yQuBt{rdIe$;rw5nP;Bi=$8u%3%Si^<11FI5cT?#Pd*
+Xt4#q;w(E_~DH=wP;SP0`$%m?tGJ|BJ7&M%y!`Nq?8UG-TTV?3nl^D3$fzlZ-DZ@gikvCb3?|A7Ms4$
+sWY%uP>Euc3BSl*3a`J;f(Xn7}7bp3EyMD)<8rJiyniStIbl{2us%2J{8!Cp0=RhXD;}Pa%is3z5fOh
+nb&v4*1vcH`mW?7-P(pV+_=1v`G&aB?A55oqvrQHL5%@F>wjxhh%*<&HIPF_S$PNQXS^x$BT-JIMoRb
+`*qh{C&mc-_U#k$0NjBq%1Dzdtvo;<WDoNkjO$Pqs1K;qqCQhOxYy_Bo7CsSTb{r;@R2JKo)IHPta<3
+6hr~Jv@>X@37Z(@v8*aFP_v+P)_wV1I)7V<z0z6PR++Wb3y-jJs3z^c=M%()KW3%`hWiix0%P|J}=NA
+^n@-6qx<{!Panb-aL>pN85SG@kPcI{eTT3Y(^>eZ|H(xpp(z3HZ#cur1^;5*GLg)D&v)C<rF+(1WDTC
+fIzei(9(J{ole>k*ho!VP02lpSbk<{|RWehMD_(e)ohrn<W>KR^GoJMX;nBJi9xZJKydr!^V|3>d)Y&
+6_960eyzHwxiJje1X5V=8L%jAnF6^2<k)A`i?%^eeQwzGy8+<Z>U?4f0Csqmn>QGF6wD~d^}&UV1XzH
+&;b0$jT^_OPoFN_Q3jwvdqIOX@5NjU<7>!0@C1Ymfi`#9!k7x-sV%v;ckkZAH*MN<59Sbm27kyVUdR&
+Go8bn$u;y&H+eQ72jg93oF)@75ph2P>;Ey;>>1a;2w7Q`|$PwBD$Q9aSl%2M|K{#Q%?MnAA1pY+#is8
+eD_aq*cqCWrgpZ^pzfInC_r1~LfNKH-U2?+^8w$`s-FYpJi!E=~_H|hb}AGl*|3i-iU8}$Hn0CfWCq7
+5e8FH$<CAt50#RPImh-MiPx<erbumEw<l-N!!~U-4qT2z;n)IgKrFs#w$qDsN8mCS(A86nLz~(bf~uj
+)JFX|6h6K75>soFNr?zAOHA=hzDL1pPbaT&7?kZ0pY%z-0o&_-{VU6Z^ZZw;~%vDR3BHuj`{`&Tu|Pq
+vm~ctEHP)!9MQH?T^Icu+8W5WCWoj8sFx@c$O*~-ZL<4#2xg><GD3NvKPP(e#WLDW?<>UJg}=M3y34S
+;Yy&sY1sOtJxclzAIkj1$pLTzMoAx3O+8MM(cpyKR6X6B!pzDbzp1>O8Iu8$zKZ?5xe{K9v{4NCzTD#
+aB9pE?GAnLotJOMI=zP6b)eSts9MjJz5Uk7pzSpv^dht}dS{~zajBmCXvA8ka_KDH?>fLLRnF=GaQ@W
+BU#e7o~qdl3$@1X+h2eE@LR_D5)3``CX0cY(kA{FnNkm5@vHL7+oh???NAwhsM~J00Nd=FOY=!w)|!<
+XNl3ctN+eHji-=?2s94|A5-e=R7?<|0}q=@OSSQ-N!KQW1GvB3Ep+<)`|F_3*kW<@P%xl?F20v?leAq
+_D{<DCHMmus$W9>ssAq3=3keiMO*XK_U}+Oa6>t0dvYi<v_;rUz#bH60q&H?pJ`_<i@*E&k2aUOT$x~
+ekMR%e*q6f#*#g|Zf4^vV!2{G+$UE>yJs^2p{}<ve@ONMTq;|r!{sS7&cVhg4_78G^{s?_1c#JtGc#m
+?>)^|}3u%pdKTZ?v#<`iOIgJ}M%aToYQb}uLAEy=rkTLgb?-2!|DjgWPcql+{~a8g;JU+wf4<BkXF6R
+-L=zsLjU9{v(_tGt*xm!0$GU0^=^;@Elfsu&wvD<PTwV(6)$PC@n;L0yyd-SRC~u=20Kf3bpGLY;!13
+i>E$P_Vm#V-;MiAeT@F*!a60Bl{B%&iDAJ_y4tbuCY;ER~#RbP*A0+{!+;wT6yJ9NQkhMN=2!k3JG>w
+As58fl#sAEi@nCrU9fD5jf>l29s;K&Eyz6Voxpl$cIiSAaFUisN*gz=QWq2C(zJCcRUM}W2b|POqE7G
+c&Rl!E!8V{u{lb;TyX&2~=ghh1p4b1*-PzhSu_E<8ar{OcAExAsd<IcTtp`rkpp`CSU$5}_Uk?NV-xB
+fuQepF1hRw1wV(u?!E<L61KaW{f-8IW9xME{$)PlhjzWhtedg?99x^heMG93@x>lbIwp8Zvk+trFWJ%
+&9KG3N)u-o7jB(!YdVx+>!Hh=^^=MGVX{42#5(DdLTrrPpnoj9Mr--`AX4?&aH-_51fNEBB(Le`6Z@U
+lKN47c|G*c)D6^qUz^P%>N&8+!i*sPuR^@=@`ruyb+uWe1)1gwb-{uEvxs|U17Z$*y-)txBK_+-|vG5
+Vkhzk{3CW9UxckDmL*Q%JKodQ3{K#MU_9WR)U?5OsL6w|%|xFZK;)oRB3~$1`8|0qbcv&piOPA1o7@e
+&>^uLo@jkG!mqzYXx|q~%rs#vD?_T?my5<3y;J=3dan*{p_+%Y(uTukDKWtgg4B5Ep%4;_6tJV*=^$~
+msoH0whzCBqdGw>-QfAxzv*XLe`!T1vc!wdfBwS$$i21Y%Yn!KXV@$`_5dxEz~eeAcfHZUIa5sV6~6P
+$4MZ{?*uiavf^<oi{Pjg94Qyj#Vyyb@cVXnI`M5~=4>M|9H%dj&uKc8WgcIT#IC9~cw*IR6*6eNgm?f
+81DUD*g^Hw4{Si=yi5IcJ$_u^}Y8e^MF43QuHy`!AuuB`j~>#N%|sB@*g~S(C6j@+UPm2H1IZRs^AOM
+girspW=70#=p)Mt30|`QU}J5}3Ghxged3CShK8zb+qRXvu~&GYZh|am)Yrk95)R%6&O|*FeFS3y+h}p
+{rL86Q@ssUA$%E9#-U;kGm3}G~oQeT6{ug%SO1ulKh?*v~aix#c#lcs=CM3q?&~JS9Nl9y&OCPg;H+l
+St$#(AC=|6PnkdL`YzE6D4eZvbf;PsLol(->y6?Ik9scj#1@CD`s_y|~Gb71MX3}|i}4R-6+f9BSfEn
+BLD{V!Me0C@qq9Wuf8AdAt#KU#18xJcpz)Tfh9ef<U3oB(Iq?cikF93E&=9|hf~#PQC*C(nPCpD_=K`
+Q12+8@oeJGhs%3um4P9EMRrt7d$jMSfi_b%Kkxr_wL>PBS((-z=nv&;X~!sYTY1wAPejf-_gKtPi&c(
+6L-=_m#*E>{u5(w-MY1E$BrG9$YI~UebVk>ckzALe{=zPpo`FUV@@}&UABg=T@iC4MIS5j$FZ{M=;Kq
+Ub5PSo#^hIGE{@mIQ?W2LHjKruo-49Ou3xrp{{6~?K5m-O$M&YxkiVn<7yUn^==*%WzvbrU%DOAqbTA
+mScJ10F^E-G_wm{iR#s}Hl9=vM3)w$7nr6pgk1yK4JX?W5)^?Z%>-t~7ZYl`?MCnsldUS3`U_tDhURK
+0%v`Zv5@ueEXGMp*~myLYdwU8vj#ne&SL)!Ihnk?NCa^eN7}By?-q_;1021*^oomw6z*9$W@&NyJ&!n
+l)?WeNR<uP8}QN$JN?O(LL*)r+>ho?NS775q}JUB~mvoEG(4uS9qwct(9`e9w>}N>42&sD}4}t;**Ms
+ic+D=VG$$oU^hAcOgU8svJ$dyD=RDeH5d;y`c<n|S&Rkvn()ixcmZ-(dZ^Zjs8`{uJ`(c$uITHi$j^s
+0`;m>AT866e(+9B~@}`!H4pK`8*I<6QaRkNyyTEnuf-JDx*i865ckbLi?d~uNIsf<U+2il*?DVOen{k
+GR($Z3E{`~pY^5x4VZ>sL6@FC7aZ<M`YUE--zr#dw|SGb!d?H~51rKP2u{;0|0^RYQ#3v1V|l^8Sgf^
+%ueK-GAOm%&ts+r>Q`x(C|h^iykC?wSPtC=?1w+rU~?Raa!ZkTv&>P44UKJARL}C4J}!TO;lNo%m2O@
+X3=Wt;2^8%UUtEk(!gj6z`L^qz@hCf6*oGSH<AWJ%vlc13CuALH+5>nKNpg;Qi7bzyG_bfnsN{eT;K5
+nMD7Fg)DYzcKzgj8_$}g-RHsln#sOR**?i=&K;OF>DgP3nV#8oKf61Aon{}Y`>))?bGx1?`4s*zW2*0
+jK0bc*=+T!Vk;sR_M%_4e?AVWvA3r|H?!@Eqo1%|u!!YFk#W|1Yx?b4P(Q!7BNXYS2K8$e|bNY(=dho
+Oh7cNj|{aExdAoOpPc}Q-IzMc>{$jU$2>!5N)eBFLcc6CNw&$;L{F`SBL)SSYWJ@c-eGZCL&5@F3(-k
+200YxrPjG1pX1<c<aX!*&jQ=GLd&@CV{JjcsBc!K-?{qxa`8SkG14v0;gmJCQ$<zmhAEt9V7ObWrdG)
+=Ewe@8~~tV@-uJ#<?wWu=Y{A4{~vGFLFQfKJv~bBJU)IA*O<F>=un2hIsMC=WAsj?)Kitc1?#m0y!J<
+W1R3oY7OdZKYT*QTf}?Bdc>N<y5t-2Lpx<3<QxTp55{lx>eUkG#)ct>fs38i>E>*iPZGNmx05%JJ2eK
+DNg6BvYFS-EF755@G9F}&2)G(!iN2)9;=LD=GVX6I8IQTS*_V-@@C(vzfyuFcK>1SI$)+4@XrNEZlN{
+;?K}mm9jX%1Ftw&E8%M~kDNSR`%8Oy)C9<fesUnbXo9<Kj}_2#Qlu76owfA;KI8LO%FIM{7sF2)0U>g
+I*__;R=YbG{mXwI+$a@RQKTACV`}H+EV0{%!8-vD5HPKga^Ch<n0kQ%~Yr@@#zf#fujwT}x}46VFc*h
+xV{@Xww|(oH@?%dHz33o1=L=X>zwGc3hjqj_2|d`>j2sv5z@B2e8}P!y3Du^NGFI9?`sB+d8q++HB`c
+kKAYNGtQYFxy#xdEz2Jc2P)TBG<fPOE2_fTnVUmZkA+Lh0+s&og34e?s5)G|Vbg+=>dMFb;mSwr7G-)
+W{Z+vYfw208!R*XMk9sq6KKEIVCu>t^b6EZs;a&E&*uG9&84i?e4h1(gIG@FTLV?=Nf=b}oqEN6dSP>
+`<gvY;hfBi(gxRf70{AQpoP~oZIZ+50XTv%0Cy)h8V^lT31m+<3x*_j*s72!Z;PS(OHE}kI1aBAvV3n
+%fKwQ&5V#pkSrN}f4I`9=9lmliHxR$T17dGPsv_nF^mrl_5abVtrddLjdn!AP;bPOp!*#t+21;@$C{c
+z=8_J{F&wNKed5EJ+k3iW6mt`b2BuK%y(ro#;tuo>}bqsChcP9UalGXfoOzJs<6f_D1`o1JS|gNY_|b
+O{_lF9BYlW#SX+eVqLLhtUGo-))VWE^~VNcgRzm=SWGkK8fiwl;W6eJUSo-oXA~GkMzOKZC^Kq|dZXE
+BHQI~=Mu*X5B#mz4ywPtA7=y-$F=lAyTr<r~H$CP&(`zm<^UMOX$SgM3nPp~;S#LI*t!A5f!0a%)%%s
+_Eo;Q2UUbEjEFbB;MbIjD@bK`08^tdNJPuzF6xaYBWy13ICai`7VUfU9y+`y9Gv+wQfjr0pSjYTwluA
+Zi+>mGfc?$wv*d3u44^vd)ay<TtDTlF@5s_5lGd(i&>?Y~e<0|XQR000O8^OY7yP+wat{UiVYgO~sS8
+~^|SaA|NaUv_0~WN&gWWNCABY-wUIcW7m0Y%Xwl?LGZ>+cuKF>#tzz%_9{{%tv#*eSYiiIf>KyHnII|
+r`=u0Whe=<8A~J|Nm)^L_kX|n06>5QCE0EF_TDXD8%q>000x7>d||NT4IdA^aW+ZP>pgE#O^3h7H@%(
+Sj`u3dS4DDtQ+dIwzW4m;vu}p*&kOIzB#l?zdHgBKLn!t(8H=<O6R*m=>PC2{tLi38z4L5ZE#pFXZ?Z
+)?iK_(O!3R-zF|?OfMKW4c8GO4ggqVx8LRjZQcyEth9Uh+_hSlfFi_?ks>g4$1?C9nDi<7f+d<o@x(;
+}ODQ8Zmti$X+^m(265r~rNfLlMy|sYDUa%3e?YFp5j@?F;n{%Zy9)4f-akWG?jgq^uTIGAl#;mzxsvI
+GL$d=Ata)YZ2iu(KMOqnm2KIGfPJ5EBsL(Kb2XkKF_l2YrwYpn(2<@S($v!<LXAeC^Y2qhWetut#qAA
+%yY8~UlRVx-40ckqv%c)B}_pS4ZPPdK<MV-=ds9fT4dRAHiyQ&x(%46X>vXA{+i@(0Or7(mVh_F-2j^
+XTt(F?$Ct?rkS$>VE@Q;Cf^w9WRXm%C3ARZyUbi<FRgC3T*?02$z<YOiaq#-!;vjl+^!D)h;N79DfjC
+g51MJdC4$}-oG(d`i5mphRZ{WQ;|M5)3Kx_2zrvk|VKE~r4G3a^iXN<p0qP)leV6_@}*P@E(WrU*{z)
+ZxGNPNbY%K;&Mk`#T}Es0vyv*WCo$Ft;%nEc2;47~SgxybQH{qC?RvZB{RT7VU?uZSXqhTqb=Ad1p>4
+z%6x^{6LM=r~ISc6Y}cLx%vEx4aRlmkKcv6QK1eQea$FMF6um@cew0uk^$x=`{2Edo=u`cc;<m+2Na`
+KLb=h&;6d5L2alN=Cin(!U#f`I2i0Qscr)QQ+yY@>wa1}>U#U|%XpUft{MroRtnc|WB9H2<KbC!{{9W
+W(qtIYl+efME-nJ!7+RW5vdZrx0@AGVY5;qLth|V?=kXr=24n(ts9~!W|55p<^u0$k3KL<Md(W?Wr+<
+OMgs7G8?HV<E(0l&L7=~4#cX0OV2l(CQDKr*^te_tj0=|qz;17M6wa7Q#gz^nQdU>=KALl1ff{}nlk#
+oXdYZ}KnvF4$gN(PCgbbGqUfM%m_Ow~k8y$IG+awnpAlmrq8J~_4lpf0h_z(By2F2W?z(f89;^!o7S`
+|o`NriL5Fc`njP;7_1CTY>xO=;DXy^e^uY-o8C~wI1etTbR(r_s2)CPF^2I=SP1%^!rHUFV=xx7@!LQ
+8&&H0e)uU%(trx~jXALJDzQB?-}ZWn7e;4?P@xdSuYm^!h41^l;E$(&Nax>O{ye<e?e~WG^)CJH!(<o
+VJnr`bdNnd$_5aAl^3``Amb>se_z?1k{$mMYl;BVDOI+X=E`%jMgv*=R?|RTUvd8e~`{R?d!&e9Ahu+
+`uJJ7((lk-C$T<SD@JwG}*{`ES)P0Qy$9KJj_{=U`dsPI3Ok9WSh{P{!iA-#GG6Qzp2hOOu5t*ge*0o
+4h<zWn*?tKF~v{^5ML-~UkV2I1rW?+gg1f4TVK<QS@t{GG3OHTjJnf3kjE`oy}kGAu<-tn-OvoK+T)O
+o01CJRePBFWd7nV@bLT=+(?12M7?b(Hqa=vh?18I0oBckc~cxan(20HgLS8ilU$tv#GIY@M{Qr8%kQR
+-$3io+~59T{|@qme68WdlL>(uc(^I!W;XCZYyepixNEt#flF263SicC+D(Rw9L2Z5fLnK<5OZMnjXoI
+d)|HU4wmK2!Ssv(Wx)$(k?{?tbc}Z$Ouo(eN=PmxO`?=SWI&gjKpD%B(>TXYybP~<8$zrDZK3&Xaj6d
+woK8gXxFrYD2U`?BaC>OIz4zxyrCZ$$6Y^BM+_{X}dvn=lD>YDz+7RyySdsvsaR5h>GblGeG_y6^XFX
+9B`(<74AND2c^oyMb?K>Y=AU(YJdH46rMT<$Id;e>>Y$qGs(<Dj0hdXn{6TtLDoLI9!wZKg3v_^2IL!
+3c{o2Q7yMglGLGK4wB~GTwLIGY1k>n|VCGMO_9Pr3#l%uexSEy2-LzD5p3`%~O8D1!HrRufP79{=T9D
+fZEX_nN3J6NmwcHNP4EKs6>d;3r-JDy%*2_vma^@^nuo+lJ=kj!81K(RJo#CoHgK^<xTwjKfd*A`m|U
+YsVV}IM=J<sCb>@c-+-D3p8<F>UGX=AcGW2GIRa1#2=+idams^8$HH5bAh&}~8{ENflfG^wbOIr2z=m
+;>;ZNg-n`|+gMBEuD!5{20gvUEl8er?Re#%$;{*oltEBi+fB1l3@VeEeMw_<!lKgQ{*`5P!Yw6gYbhX
+Sc+yqpA<%%leFB_%Npo7xD51mI5=^ZCke|6Wh6|1^B&C=lsJB&Sy2*RX{-GBI2B2<LG%z6n(2*0PzmR
+|;wY0B9aw7a43e$r#q0-)|K&he?WqpUw?Ts%OVL6GS<cAX~{S8^<$G4Ieh^nDkjU_!>*x!d>cL)slOJ
+BKxqbYX;SD<x%SB-j=v_;~O7IEcH@jvcYXDWQOI&Jw7;JhLF2<u4rah+jGnrKFsEurjEcrN@<;X)Hz`
+9zO#16<E!56d5;R(7S{DnBE#Lk_SzyO#iJ7c2J)AV*)*rfIXl#YUzKt@DexE5o8nqr4s`6RQ15LW-DO
+@T0Vms3U<SuE;=-;$RsKsEe+)T{5!;l9O?LiLZ)$R{jBCoL?F|<>=#NRZC_PjrfIp_7BRYYh{%PXQCL
+ELR577=`m0iOJ-5Z9&I$(fx(t0jx95SBmBJj>Az<6zxfR}ZV9Z#|-Z|I>J!i&7P1UzkGeKoC_q}R5Uv
+~=u8<q*v|L-il(skl}HP3Z##e4vrzGz+>P!qEV6Gw^t*9Rwsk1NSML<b%uGSnxI4H1D*SZfkt1Vp3ue
+Qcya3P{r4+_EDzg$yahReY0r>3*qFeBJjD*#;6byfG!1J+}v)J2v{dg%*k#ytgpKF%0TSGV1sRLk3}-
+7O429tXp)9WZHAl!oFf9*TrEU<-jFP+P+Ni2g_i}|fUqok8c=L1$Q}*42D(@bs--0%E$l(9DEo!q$93
+{$Y9o|IVQo5>ivx{S_AkMgsm%(PcTm_abLo@tJ(MPClPR`z3PhwKfme$hx_g<VRgm<F5QtWmcULW$%B
+uMXDZ4v}iKIVe`L}mvx2!=m+O7h#NL8%b_0;gqO;o#Hn3B|NgB+8#cNChkFVhgnH4R$OXxc_o_c~Zka
+Tb_7g3+`M8y~GzDvt;WL7-jjFB>%k$u-`e#ELZ8E@k(}c(xEy*H!Wlkpl2PF^(5d<WY&@`xe5GKTk^3
+cu4w5TimH>A#hM8>tbKb75&dxl=qr;|EC`g58pPI>HX%M=DL#sv}Rv=6;Wbq`WFLp-X)?*o|7f}8yG2
+$>sMu>_hed@?P(LU>L4T-)EGt^l{aD(r`M8-u(mNMR3U1kuhbx@j45O_GzLM&gL#NXdwWV`Tii?(Pb*
+QV_R*(<EOZZQU2%a+__$}wxI5k{j6)Q6XfjyAo_D=KPdA7^2#bWn1WhS93|iP)AM|tND+4j@u-tGs%a
+&++X|HuzAmT~}CZy4M3zoV8L`%JHS<NNNC7f;aYrsiK=5P;XTB)p*LH~02>}rc1>*~I1=8)v<A?;EPt
+E}2cWtCmWY_E~7$Au(?5N(IS$p26@l#to9St9jYm;o?<@SdVV3ZK4%I_;rCf95n)sJ{hfPyt1%92a{4
+a@Tu)g}w#sAJlj6PIU`KvyJX{8WnvPZX3RlU9P~GZZS4|XYC(o<1^O#pf>tBOyRXb&}oh`?S;gR^b1|
+AB{X0W+MA#=<Xx9>9!5v%`IKAoLB-(?!m%-TnI?=V0fOwsv+Jw?Ry$Yb21k3Ew{kC;+W`Z=7G)JUig6
+@NxF}|2JQdOC+ZQ6mO)B6L{jk75jU32y-v>Ap?-BTMIZl$cuBH1kXz}YM23<r2)Gw49h`*HTgN)U{4j
+EHOTZ<0b2#lh%wGdc`vQlJAWRkLpHkER{kpE=M0RFevV2)C)bf*PsM=ZpQtTF8lH-zg3^Z=c1sGs`%z
+8fW_#0hTA0}tqc#12%7(BX>>nI1A(2Tim!Q$%rxs~d6<e$*wp3D+il+qo4(=W|4+)tiQFgR?}k5RSAM
+@xJe1T9N@wLll1++ba_(3vc@}{z3@|wC3%d8=o@yVoEGf65j-Q)1yM@Ql9t#N5qW{2!fgvHSd-HRHM~
+DJA8F=_SztO>Yhc}nx=C#i-vK;NmETpQ#E26RW#nMLF6Vmk?A7(0&BJDW$hxPF0pM1q$&f)!oMD!!gl
+`V?ZL(2Yo{=`Es5a6;Z49rMvLpf|Eb6-(ln}!^o^925TB42=WS)^i$r!Hf47Hj3>#yz;_FiSbu4#LbB
+l0T@3;cn>(~*6;#Mxk$ut30EE_;0-=bteuKp35t?`S|SQ8ZG=1GcPAr$b~&^)Ej)KI7K4`I1?E&8SQt
+`lmL(XBuq(G2J{;2^xf2m-({$ess2dbObfawH5p!jJua-*ok%&rm|#uM0K&tYw6NaKTQ|PuAG##WaQ;
+Akt1K4vxBV7Lv}WgO^7gze|S{hEJ_^{nlNoip8cLHQZv34mG`Fu0Ba8navbb8#q(QmM}gI-T=m}UNl$
+<w_4YM??w&F(rTKqjSBF>tWldIC5`Z|K!k9~6KwSSfoeC&$A;PsU!5BFR_eK%>+w#ff7|iiBn5HrYB7
+b4^d>>8W2wFUz$1pzlU=Vy3e*6Ch5~_4odoi~AuYVbkL4KToT_Zbhhgjn)RLe(7#jvKC+Wg=x=70|Bb
+zYYfLohx0V`~|bz4=7EUR$KD#}64k)ZZ6p51oUQ+224o90^XLey2<lVvooy)$vs)3oolP2p3Rd!`bRa
+^}R{wbty0il7FFc@PitHI1*|9J^}CNk!hKrkxl%&T@{Lzl@L!#b?nZh~~4k?9+VK$wD1vnYT1dA8oN{
+eHWM8cTsJ)+=Q|Ij<aQJ^)*-Cz{{!|GHb*sZg)m?B~&qpdIywCh8a~$vTa5oJ!EOX%wrXtdi=GGf*sJ
++XmhTz+?$CzkOZ=H24q;qccShJwhg<YX#3`Oh?2_N1SF!0%B9)TxvUfDIIT{RF6J1yF3`_gcF@}-H5c
+i`VThLFo1`WM*KBqT9UB;Stp2XHs3zbCJAIk7-MQ)+0fFDPO*LgH<nanAU)Oy_kYZeA#R?Q?5UxN49?
+;^P;^7g$X93&D<#y23iiS2XTU1^uK3873NGgi(nhBg%*ssT$d|ajzjd$RLHFg2LP!Tr|_zMW`z_4?7z
+^A2bXl~moTC7z_6}N5IbMwc-R2~>bu%h)`gx*0}0!h*to{{jTK<S`Xh9bo7qJ)XtI4pUQW{c~adi^pA
+_l;6of3lUz&iO)Ir)swilr{tyJ~}?XIC%T^@C?74|8Q`2NUx<~zK!_qEv=?F=eD+$n&z&#Sy98M<+Ho
+AAlY8HJ@3&Zd^90R%#M2lEVBM-zU|wNYyW$Z@{yQ5np|M4GJw%M0c2M>0~#D2mBUk3682;TDO%TsudE
+4e`f{}s7=_byK1&@Re-U3}Tf<uNY<bpel&@3MoxYlS_j7K3YNJDADg(58*}tzEFFSEBEgm1awZ;|fE-
+2yh0tk)YPe~vK^upc>>L>kApjp!NhPZleP~AlR$eYKvup~=7l2=1%87|5oXxoqaU6!b3Z&83*)j^%Od
+wzlKk4~QH8T%y;raHET3>;mijix<Pm8&fUGS<|#6(EXWjE^7RF3}s%8Slwqs>(hfk2f|91EF^WvL(Xt
+K7NEhu>3xLgvC=2MU@|*c)$^!$YWz~4A3#~cpycpWdg0iFJn3}$7EbBYjYks-H{R|$vCc*eStftnFJa
+dfKiv~^d-tBx|jx~kF1fPMiVz669my%g7G;(9^*pfaXMb9VzQPIzI1_YjX~TSVIVB;9|>$FY$ic;KsU
++Y!$xm>E0o3CfMehgC@?|+jIiikfL1>sfsA?}4H-|QfkN&-Fiu11lqU`cdgxN;17c&*pWO)bb1i^q_G
+|*fZ3$ORCDO2G3|c6HyF<@9<7<yXq4z#Ik34=f;)PS_a!no|KV}jcu7Q({3~6JGhbk*a!yiAQi2U)RZ
+B?p8>0%WX3zQ^@=tKFv=9NUT@G~z3wkq&TgQKdhHOi^iurG7qbnn+${r^&S3GM9P6f@8YMKN|HYj4>I
+skNg?|HH<5+F)I`yhSL|M*DDAERq=$;TR%EMJyd^ryT~<jA~)5Zx;<g(C+GD|D2enz^eMyzDqD8{=zH
+CfdG0Rw5tuQUiEv;mh~_jQkzfST{eO`?C`cBP}_sI@vyh*O55<2-M89BWgHEop_)`;5OQanl=1-b3GS
+dodY$i&rc{Axx7+%q{ea!Y@IjFdikcSi<|7AmnO|{Pd$>g8`Sf#pIo8IkX0$aeRA(Jy2{-ESmbI}ZWz
+=E-7K9z*{IEf#Vp;D(pd)Bg4d!FWS13Br9^sdR1_1QCf?&CzV+%>uT0|M5(>#wK$Zcj{{%x<jEDssMP
+!qR0Zm7(#A(SVIY&ILk<6DaIm*?9}7^5*%#O)ami%me^j##=Ktt!$08Q$w`fd{)%aSig)od99&ZFVPS
+tFLTe8E`_W$X;GiMI<F=rnLWKx`qZ$umoeBum>fcs0b2iuS<-$5qy?eamy=7C2a`jw-fx_umu7hMCDb
+m&k8h`xa>|lh$m_F$Agy9EMBBzm{F3o<e4UzHq#2n^<;LAx3at;kdpj{1OjWvs>G5*nJtR3ko%?_i=!
+BKTNbmdclbC1G7*(uNC<<K3!DN!l2@z4yaGCa+zF|c`+SOefPRuvk$oVm8VM}c7|9k<-IDy|EZZFy(*
+Z??RWyTv&1Z+?OgtkNW0DhEHGucjuW+&eQ%s-kz;(7s0*!bRsxe*^g-9!q;Q0F_Ls6WyM&h@PRc&BT_
+unuwK9O4Lr)A51Vm_UAFno|x?`tlrTX8dFvf?f;pI@=9wJL&o>LYUL<1Wlay8F`=ctcSI^Q5HUH6q7*
+oeILoXCYM&qtjT6eSEg2j~q?I;{CoXtz?+}whUuF)=sPXu`U`}J<=>S+US{x26fOzJ5*!f*})`^(aYK
+}S2V))-Rhby3FMbe7(Q~+^pthYnO1)&20*U|Beh1L_V$6647!ZN_}Fm~3j7{95prCUk8msBKuq|oU13
+QX$sKCn3!L4qrHl3cBzly;%*7DcZ`O@LXX_eM-H3`PnxnuBJSbxrV0cA50W>}D?z`OBn@T84vIO|bP8
+}3<`X1Lac@VNhoU-@F@`;7(RztltpzYhCK@CjJZ-ZNMEh(%)hO+osXsAiAlaZ0hUQt+g*B;E2q?CxlR
+m3_#Fq#h3C^pWyHo0!Bpv6Rxhd|&&Ba7rPDr$!<K(=|a10%}bLPr8OuzHOi!j0^w;Zoj&A$+!G-RglA
+1&Y8g;Ns?hHK4EdCC)bY7@uu9`-8Z;dVCObNhx^v2BAUmMzN8UFlc{Fb1#dUEHYnhiY#zS$IS;lm-}j
+Upt=*OBsCkmr=I2#0g&q}$)ILEQ=5B<iwXtJYxGkra>5Msb;y0?Y=F}^nU$LlUha6K1!c`c`#Itkl+2
+qrFUg2P3lub$wp+gW$URruqyzt@t2<)i-{T}GKY$#{6}FFh>_avWI>@GYGDa8Esbh;Dujj`2pho60xJ
+pgU?PS)Ke`O}EI@2K#;4?u}mpO3W(KphtL(81BJ@{t-up^~5faAZxI$X#hfeIdhP%~as=%T?SH$vwkD
+7V;sD2dF(tkSAuNS1CacZrGY13)<u73>cE5zX2z6&#;#dff4(2U#d@iF*n`J1xiqh&GCRg^h)n<kB$V
+ltR=lCDW^7!$Fr2hHJpeE*T)VPmVS6Wf7n70uf$^>bznHEZDGPZzite@rrHk=+VqDJBwWQ#7ZV6XL1W
+@x<efZ#HV3C<^)odV4z`w?vkKv0Q;W*EPUE=GKYuv9yU!mIxEh<`CuBcH6})p!bTJ2@U|*3p=t*K;j?
+i^vN(sxyG{P>K1$KWOs(nm1(=U9F%3I&^RdD|fti-sOrU-AdXa3}hw-^(?@*+}1s@@_ZKNW~{KdoDP8
+w2Uy7bW{5<OClq2YP5d7$Qi@9MTMZ?8g9vdb<zn(HZf*n86Fb{FP_m?ogs-i7623ZJ%66y*b`xC^JVR
+yfG!F6=@dWE6^our+|m<z1)}$931bpFGo)9Nz16VMx1bMM_ose)(kPI_E9$O<<a+LMR4!Pf$9X`2iBh
+{g(NbPQDh5jZVMjV=Q*uTPVXCdjHt_TqoaK!@$&oM=T|@`z=cHobpE%*N+DDP`zB!_OD81|9yUP>{;Y
+;Kk{f2*Lrf<3SE+^Di+zo6mhef^ZxB0-KYYlydDg4?e!q2%XWu=AsT?!qhFfB3|A+&ZhO|&L68;zJIK
+EVy9E<&XKVP8ay3ZSLc!A5!~=9$Uxy)W7==T=uE(%>!<C6K8#57t7Czr<AJPU(*o5F;xeX!M&1<|v*k
+_R2Ro^W5Zz6%M2@W3_`h$Bs$t8R23*Efmo47Vw+#b*P{1RMKC)VM6pa!=+tOp{$6&k86nmVj;oqpsD*
+9!8Ewst%^m+Wyovb#I-{1PST;4np5Rl4GC;`AD0H%s=nQbBhQ#cX}RhO3~C&gkshr7z<}n#V4zk{%~5
+mqDZD4JdGv2y=GPqy{pQ(&^t<53Gi~n-W__s0}D*mt*Ht%8u&rKMEBpl|7*xXatcw>tEg5%+onkosgx
+pr?IvDcjlruA1MZi+9)cAWE!bldG1tHg$$f<n1X40>DhHsfb5X{_`Zp-m{)}m%7$bdz>R2kSfNRRlFl
+@<%UsX0Q9R>&74%Eoq3I?A=NVU4Z&M1?Ko2j)w`N{^mn9R9DTNI^^`3{H_sFy$0~vPkN|L9x7Ed=&G?
+;>YqeIXikQdpfF%0PkzrUF#jQasD_r7TahN){B=$?sy(gf(=C6O>fY&6z#?Ekq$XLV#lx3%H$&liWsn
+1A)$4AH+whBTeJ!pMDa&qzh5esgc5=T^zPtfb<W*5WoaNt)rbp3PO|uEQ)0afgMZh8|Ufa7V)OR&$YS
+eK0J(?Svt&_59gVZVh&g=mAwwKET6cW(Bux4NuWY2PmC*va_vkmUAZT-f9m~Ug%;weYuVG=SR&^%gm1
+0$Oc&p`dq~0N=CVLtBPyYbHWH33-OvF9u=k#=Jw%ZG^hKEwymx%JPP#(jIG5F0Ck=Ry6gshhtCgwA^#
+zz7*lv$Z%QCHcUM<}#}wCtU$^K&ja>ukclBV`&8Mup9_uhTSxyaZ;BBH^d}Mxgb?S#nnWpQW3H(q@Y;
+GdWR69u-@!d_yXtp+6?Q5Bug6#6@SWCi*X}PvMbb{v$7AYCbVaJ2D=FV9m`{QWe>f9Xj#m5iq8=o2lN
+f{Wk2IP%aUIGffK2x@?d!$nBsDruQus-TBfe+|fxOn-x?l3hX&kOPVwX(uH_GOi~dFy@J%1B3ntmHqi
+P}$%u^AZ!9v3CIzs9>%3)B(;|GzxLc)+eGY>U6u`0e)w%>N(udDQJEk7q>iNl%HGG{KfszXqUH0w9j+
+f4m0RYJmaHkGLwUE+ykO3Ixm#qnRf`rD>dO#YjJM39oh9%U6AO3rh})h-ca?{#UxEZv2`M$>n0NUGhf
+}H(q-WN8UkaloCo4nS9K9JRsz!IdGtEO+g@rx%y}QnBCXffZ@HQ@Kx4dH(29}qV@o>G6Dn_-anELhav
+5FLO*8E|;6j_gVvn~1rzzkevoCugXJ5}I-%ataptAh?+}FR)ef<;8eeHM`C$CTR(OIJZgzaJfua83A=
+lI6fM>igLY-5`v8{N9rLyl@dNBuCQ486L#8+upJV<IzhQ&<MVxu0W?7NANHYJ`lpfIRp>$**yk<lzTN
+I^&wRK0xxz50Cst2S+v^8rg7Q<bj7p9u)4kRiyF54~X37aL8|ZFvNK%gv3|AYLGR<c{V|BYQb7PZo@M
+9<c6dY?+FNL*htuv3_LRwUObISKKf+rE#p;4YgRQT59?`QRu&1FD1g%^aRP@iq9rcs#N&IUHQ$Proij
+lu)fh4dlCB-PsSQeV3=!uY8I{>=Q8DXa!vL2y1)icYI{pG2qs+5(LU*Of^d;C_B6R);O7%nou4OPaaE
+t;h1Vuwg|5+1*Dvd?yQD7UTq8Q-O$XdD-OWh>j*n@JddWBaZ0KJGhxH9DI79iT(WD`oxQL6&d?u3m({
+iJC3Mdu8O>9}WB;5;b**??wEgR6`=)@dtJ$~H<#FW6NWQ0#M7mZ0sgBsCg~$(U|vl>^`T&@X2l#c;}x
+9~HH7k_jU>38g9(OLHdK0HaPna2)4w$VuLo30hIHQdT6_=tin*pkK?KHKReumW2j){7Mb0M&W?7+K>s
+Rk#|JS3<F_8D{=E?erO_9mFY^7Arp;D(T+smXpX6Rv<um0Mz!!L6WFzaL^(Gr@RBD|i`hzFz^9#m61&
+o7hII?4fBJrSbo}OovNp+PTp-tilLJu&+W9N(aqFu|u);oBeU)O?kcBy2iOOz(C2hp>jBDImB^zj0#I
+fzc;yWtYxQ#t%>5m#s-oEN6r|NfJr-yWkx%xXqNZm!4ncZg;!stK?KHYMtzD^r;l9F{2$^fNYkBPVIJ
+=-%cG3)a8<BgufXIB_f`D}|0tFTYy;!N)2C$Db-0};zqrT8XNr{&woc`xUu!w_zpYhH0Q8+RVoN=hd=
+W(l6b!&^j$FJ3(DZ`%8PMb3{fPp^|K5=Ea5Q{a5!UI&hX#>IIOTzh;RMaoGH*vX`%<P8i%HX$=*t8NS
+=#C`tLu3{rpB43jOCBuGHP^xYNI~%A$Z%Y5G_Mm&Yu<vbRQ5D(T4qH1qK>2P04wG;gOSZSID4Zb=c87
+ldJ6l3W$BnA{=CnRAKf(5jJwCsUa*t1^g6}G9_$$9~Za*En<0>vUefNQ}dmRYYazm|$OKGB+IOR;t#e
+5`hKT_MRRYpmabxtI_oqB_S)@bDosR9xg0@R9En99$T3GFuJTe=BK7BE`TN?mhvP}N*HXwNegP*LGwm
+1TmN8imZUDsLXB#q$;bX=RBlf#{xikiBexA`K&GzJx{7M103;-|-G66AZ4F+pm3-CyWklOn8AckGtax
+d_(SY4!1p;9q%=$EE8d9ni_}Rb>?aeEelfXaYvr1XF8#?(Gt%_lAe;1^ziwYeV^_Vy?6K^J{(uG&ntS
+!2pB#L6dD}XE^S!T<5^aUVCpcqbn{?mc#lHg8t&i4|BeAzTxWMyY)nIR0<0&&PWdV;b;=9sX<j8j+oM
+`5Dt4${tt%<E=7?(+8CPqkRBe@XWMZ`#b!UoG3qYM3RpM%6wXIiy4E@9-z2tS#4Yb{mQn^!1u-8L<u8
+v5E==xbnG|C8e#&z1cepPzYbrB5~B$bU66{Q~!&!Y49Z{8gJnH8aV{3%2EuYRxm8z?@_lQg62Zwus4f
+f3Y62e~gnUmAMP2J#>Cwo;t|RV6W&8qXy)$T6h^y?>>$p}+`@i<?N5BStbnV`+rFNai`&N%_5fUJvht
+rq^cT6dx}RY1+%N9n~#WrFMZm@58A2x<g?JDwnk3LK6oskI?MOx1ZEtsbn3j0Hq8F3Il0<1lf$#<bp7
+<oK~&A$OT7)e)Cye0W#)v@O2Uw%f!5uZcdhcd7Gdgd|2-Gqu|5D_!r*%4#tIWE$4sZib0lN<?PVR4Ai
+C7^<9IuBtXQ0H)&OJD(#=KOFSlo)4JnB<-!Kh@0;I$OE>Q1W0!A30@8S##?6DcozURV=ih%DefwfdxZ
+kV;3;+SpeL-Tu&7IvIB${kz_W%i>xJr!@9>^ALEz$(naolWgd4nD?p8xOmT^1f<9^_hpK1r}0TvQ$(R
+{xRa>LNyh(y7ywxGa&z_dN2$$#5pJ#dhw+yy`SBa99gyh(k09(QiB6yR58~HFyksFIyBUz+MYv$Rtp5
+q?@}HHsmGY$PsEpm=}X{XKW-jq9YX(4UXQOMyF?oz^%2xp#;9&@7p=yWuI9BOf%}(6K-%K5Tr=Qs{tM
+LuOS*g>CPtuGZA)uDTiv?x`A)&E@O9@E5cvAE6R>eN|56{eK0~KKAH_koB%e=I*1uH7r)K;{e0#B79+
+Rdc8r<plQ>a|N?l;t>-AopygNO(IC}Z^FuFMS{+v&g+CmA+DzoP69!oHG5T^7(==<d)Qgj9^Z7q=nDG
+tTFbdOY%eSXh{`aD@N-MQf<m5=T^%PymLQu2GK2Hh9L$;;sygVvMWzNW#xp&_AAE`1xHyvd9fI<IYhj
+bGI2H{QZG*Mz#_-RoOn>^SEC0Z>Z=1QY-O00;o{l@>=M?`QnO0000I0RR9g0001RX>c!Jc4cm4Z*nhW
+X>)XJX<{#5Vqs%zaBp&SFJE72ZfSI1UoLQYC6B>Q12GIl@A-;R&JYB>@drI1@dqfIc)NzIQ^gL|{yi?
+e&5S&w=NRKhud|V&^ea=vI{J>!!?rFsK`l$oqoVOL@?g>@tbKsRXh?3DO6by#6vA05|8kw4mX=k0)5}
+<=6yq-L26=gU#)A5mzLs2mu6<bASxW-WzABY!n3^vHR#-=Fw|ppgzhA;OmW#p*>f%9rO0N%LTQ=<f|G
+c}QuuN|D%<`gE{R8ynn~EuJ+!_|T(>u)iph_Xe82$iIO9KQH000080P~d=N3V)6WzGQr0Luda03`qb0
+B~t=FJE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|kX>(&PaCv=G!EW0y487|shy;b5Eity9h5|XPK?VdE
+(xFLD!yw2qooG`eL!!6%?<d(&>!#Rr5l!+vJwC}(SiD<+w3RZ4J7}q1e2N)1Wm8z$rgQ3WB*<4Yxc%_
+)7WPMkZyg=2ft{`Ck8lWIY-=h(%9<j8c8KtGvs!O=>w?Y%!c?$&*zO-U_fPwW$6ZW@J~o+5?uGo-SVt
+ae>p+=G{Z>^gG)OJHN8e-X*2u{1i-2HEogxCPPm%9DW1I`EIfo^D&!mt?<h8RW?rrNNW^)KhtwpK(xb
+|qmz?l7DjFIc^7=#r85IEt&`-X<$Sz<%5>FaCftQ(LO)^EG>rsK8JI1lBrJuErzcg|-6C@u{4EQfkOY
+9=!XMr1a7ZgEJhGjJh;_YpHzo#qNWDNH)I;)ElW{e04Djf0(O&Q*eqW*IWMEq{*GUZg0mj3;4aU!OnY
+XRpk>SR7<oU!<?3>@$&5WlMNvV(QD|tR~1Gov!Nf$*ExuWk={oeIuo*>BVpTFBVVk{~X0dS$J*50V$?
+KNO<b=;or$;v(Y&ouh@k02>Y%XM$RXAZo)kb>f@aU0dp{x;Konj<`wpOQDAv-sNg*A;a#!6P)h>@6aW
+AK2mtey7DqIx(aK01001&%001Wd003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mb7*yRX>2ZVdD
+T2?ZyU*x-}Ngx6bueG^u*R37I$9g4vyDGj3jHrkL+Fqff|xcYNFu`GY`x3-v0NiXFq0!lGX`uh(6dPr
+@OkUy53zqr{?iDkIkxD=jHa9>DtZX|G-~PPEStFi)w$U^X+wOk{2^`_T=fA`EYH`+e3R@mF9i5X>YUI
+npag<uCq3;$`mHOx7PgW^^2E3ynmUtpIeiaYxCmG4<FvW{{6=fZ{EGfA7R|dWHR~K=0)Bf%%&?>IK45
+Oss>#42SA1<X7%bi|AgN*d0`v+ZJpP4)mHVvw6(QOdUEp9Pd^c29?rD|yw+7YZ3&!d;c0D~s;gHP7r8
+O8PzAr%W@p<Bu+I#T^yh2^-~h9elYF<Y>ef_^ergW>W1F=no4VS;<8+_3*G3*>%Lc#dxJ|js+Limds*
+1ugg8GxK>#|}{sRC)U8i_qQY3swY69a$pVEN^YzAk?M*R#d@k8j_;dH3Pv_vYMus5^UNKd<b*HLq#x%
+et=WXXB$^WknN0r`7kjYh7k*+x=~s?T$j!0RZcVsvLmY*82d|w!Q}x4RD|qi>xRX3;2F9!JR`+RLj3j
+=Ei;1&%0{vKI4w)XZ`<dGN(1<kA6dm!RNc`6a8QB^cO-3KfK@DRh|{EU?ur2g7WgSUAeKl?8YuwMhOU
+zqhA*wWw85AZ+dOFdDGguHw-6t<<W-`2wbEq_u1;kuMq%_9`&XMOZ0}}Gx{N*Cf}A-Z5JTXFrjQFmnS
+EyB5N8$^d~RNPkCLHJ6rOO&Uj1rFhU3Rd3kIOXxzIjZ|vIKUfUA%nhZc&feM?<+~lD3)y7nNTa%eFiA
+}eu8j2ZA%2%L+4Z$iaP|$9Vf(GzlaAq1tgKUV*%?wB#Jm2SgJ0pb`(sX+th-wB{>~?6DR#jK5Y4xQw7
+Ql3{egOHM&DOsPaw*o&npIW`+!}yHFmX5QgTN&v1O6OCRX6_-u<|ws^M&TonOd5Q6tP6$PUyrOOz<Yl
+i-vIUdjBKrP<7i78mj9*0Dl8b4~IByU)zldv^?1~^=fW5>!zKX!j{|i`uyp)-#mSC_V1n~vgc&5$=cx
+5$3=c)4FCY^Y7hpB&hP<%MZ6IXNDPR=(`QaQmfZ%h!nrB(IN{Z2?s>l9t?f3?LX9#@7=7b@!6nk$I&W
+<PBWFzDq5)1IHVIJ&Lxh-9^f!6gR?YR?d@S?N>4_|Loi(6zbt3L%Zl(%jI`iv0_Um75+paFvDW$`8-D
+ugPH}8WT>Om1V!H7OD3UQbFB5ToTG|$ZH+OBTMW#dW)*F>daDg=s>kb)`Mt+K|tAH-K8a<iE^KaX&@c
+NS7Jo5d2;=Pzvbi&$7hGy*}$@7NzA0~JUN-sI|0b48lLA0Wg)#UbL!F_}lk!<aGekUg8y<fXaLN0)bO
+Vbg*b0<q}Ol1=%r9XmB>Va=|B*(mV^&|HD4qQu(Ipt;d#GSChzY;qqMhEqRYXBf^@T`T(@+R)~|fzE1
+mW7`G@vY+x5gkfnqi{Jofk%;vD;`RGiuV1}MKsB0nfnIwSuju(a@E6STyou~J@)=pfnvC`f%vs5tpVr
+Ii%w!FzvR}rJBHJ>()lu*@MrPa`1(JpY9FsJBW|n!|+#zmWw*imUHB1S0I@*6!4flj#pF5uZ`OWt)Lz
+cR6Fqxn-rTokxuVBZJhro-VgRE{UlQ>%D20X>i0xbS-Q)Js_c8tPt0v>{VpEn&Y7xD(6(r-+e%op4Q7
+=D?JgB!4Vj8WqgiDDEOP<?$wVgyUdvpZCp)}dj%5~IpoJfhwxnzz?IIDl7z8m$;m?f^)<eDn5)Hy<#<
+2Nc2o_Eif2r8L(3=A09b)K594ie6;yw5rGvFZ9V|wI`84d6=cFpqQAgZPVr@2OPWXfRRk*^Eeq8=!jb
+Tyn#VUY?0S0VKm}zRW3TMzbwIBpsirXauVuv&k+u_*;9RXFWEhHLDC=pt5F}D1E#(g^(WuxC(npil4p
+ba1NtF4MUpxUG(m1P%}Qnh3LH)i7y%3+^{oJ8yWZHpceaGI93uQ}Q7s=g?V+G(f(FqHS_l0tEdmAy)T
+QXw>~<Sv9R*u`^f`V)mNWqydDMDAzhkm_>0>xhKbehQTLJZSF`S>;&#f)jiKP1}AO%wrdNx-8Gq39s8
+a1{}A~>@!aOxA{MU$ATn+E3Ouq*QNra7m8Z7!&tV}L!U@e3`BoReVBV}+Mx)>*j)Fxy28F32Pxp{X0l
+Mj*JL5IpM@VvY#BAGGU0cF}-k1%}uM4ox^(e7zA12vZVpF4YQFn_~{d_7EhcmQ)5kc?Mw)00W5uNKI8
+XvN(Xjh^!(w5UeOM<g*Y+TX&XNwycVLW%yx^f^6A)afJx!!Fo|wpn8vwkEp~6-+}J33b(k_hPMMpuBs
+9Q1Po-w_;D?356auJI4=)w4T8YypuhvGbGuHJGRk0e;*#rHLL`NJ<&<MRVlDB{u@yK677K09dJU6>2<
+4+iHajNgr6hsEeh&on5T{wr$eB10hJ9T@45-`iKUGIo0VF)$k7L5d`vZ)?{Xr9Ws5*}Bva-eB8=xC)M
+0*XKb=ahkMz%TqOnAXFaE>5j#%?y4@>NWS%UO6J*9E0UK(Pv=5lC<kW7vvqhxt02tQFY)Y*|>^mM!Q+
+32MiThv)cI1r@2k0$siGMsV)R6)Z)nA2fYwG2Q-@H@R%Ex2AK>Jjha9#V}+O2?DRC5;sSS%oc^=0kt?
+99Si8TGX+W!+ZDD(=J9{Uc8-U?&v7F{EAL3Iy^RpT7qBXOC%P6@X1Ek<YzCABwgk5*Z@lEULI7}Hl!H
+Z<ZFg;9bFoN*(&M<@1J_(*amJO0mQJrmc04kfM>d|JszRYtVmDQ>HV{Rqs)W4`>&u1M8mb@T)~XE(?F
+v6^92!k(et$qmmw{=TOQb?ufJn^EvcrNq%hD$D(jxASPgxt-+zc3NO+sZ73icos5BzyAHc@XG2$75Mb
+B!Dj8Dug#pyLjgzlY++b}LFp`;*XPO4<h~VDS}Dpr~4Avk`Od9Hk5AqOl_w0qBi;60Oot220qA&ieGM
+UeUB(2<FJ}P9BgN8%ZuZ9yv%Dq7V=bQzFZb1nc-1#Ew=$uNn;>jK-W?OzseruvG>m40*1E41o<x2l{u
+aqM(M}`KVmnn7CuH&m(A}1Su29nKgnCXJN-2$v8gvH`vM;VJdRmVhy4Yr$w4faY)0Z<}=o0u2i6BrE2
+H+T7e1C-8jNYTMQ!p<GUS(e-TU{25JD<i~ahGj&0qk`7t1HP>dmU>PlZ*r(mQ#>}@lO=tDSg3f2ZfW@
+5N3f@K9ht`leCs0NEEhL=j>ArRSIsq;|5ih`_ue(kbr*u0nlY&$fRhi@s^+~(rR<se7zm+wcDbpe;{_
+XuumzaWE$Su@ECA|o)8ng#10s}X=4MMd_&4ztcQc?nR#?c&1oXmC%~_~=)UT}`&a;Lc!irJ)QK&j9df
+ML_(T@?y;AFpr5Dl50#lUW?^tlCttCo}_wW8hC&vMZg36PFc}-PP4Y4`1V*<^Li7*MdpGxnybsfP+k1
+UcixuF$&{DgQ0Oeq5F{V~^L-8bQ*bqz21}NsPTwS`N}RYckst~StUKV@7an!vb26yTGtb#0;6dk{4!h
+yo4u1d6LDA^Zg*dIK*QS@=Q_`K&bKLmvP?}UA2@4L6+HPOiDA;TcxY0Z&nWaNEDpM#|A|VxS;g6ibMk
+$O-1BXSE2yRd>v2F*FZ&0IqlJ2CH?Qp#M!@+RPY@o<PPmZ4l%I4A_ETxM0;ZYk(6&T%R4jCJy7!8=fW
+w$k_Prv>4KZnb!VMrz~_`DG1OLRldLBl+DGG<hQ>Zni~<3AVqi+47*=iGE52H8S#6lj6DuBsc2=X(W_
+D8!TqF5>1$_(|*x>40bUC?QTYjuUZ5$hhf>Vl1Td6{b64Iw^o75`45Hz-C$l?|YdcApw{tp@g|pmhWG
+`OGb`Y#5(GVJqzh<W8mioMteXGF;?x1CnmP^cM;Ot88uWM$+69e&`^W+D`kM@9FqV!uysY$KwD4EbK`
+!WnNL}rV{L--@C~FP*cMdhd$y@9Xe0-|MTV!luuc2g!fdH`okGspH5kZb#cg(Iun~q+`K&?h+3f-4ve
+g=k7=6?w6wts%M2D?|A~1cc2`{I}jos_PDQ(`c&K<f%90Hbu!hm4q1DdL=<VdTiR^0ssMCFb4VKn(JF
+S1(may;KQlijC@HcZ4NyD;e`Z{k<NP{$<@xU7FR4-#qhC5SOru$p{r;|fbUC{B_a?AIlI0qr=7Q3<o5
+v|GZh2s?0^T=3_d^%H~zb>2ec1HdpaL<y-%)+7<E#sYKNeAV2^ADq<H2KK2&F`-x&LPonIj+DlMlga>
+#$r~S-;5mgWvgjT~6mmrZK@mq8!gJVcpsAwDB=);3sm;z(!(E!DJOWHS*6YpgQ@X<1%PnpwPLvuIu9Y
+`UXB#fs<vWNRFa+VHw?)-7@C^CYbM(OVV!>x1YdO}w=fLE?1Af-VAFbI@qvKLsB{m(P5Lu8TDlY{r?#
+<oX8jFjPw2+6$$^&S18V&TDJZ#7CI_yhw0ja|Z!eZo{r^gJ*nib4gz@mK(-}hBw`ly-qWE>CO9jtL{9
+8edJJQib&WxhQ;ar;;-;n}hM8xSM`kQgs-N{BA(xB<;<Y_Xvq(th7Y2sS=&QL=(l7wo<4ZRAcT+~rb(
+L&lg{0Y|%1%Q@Ogbd(jS&*vuWso`28$xqlcqdG%va`h?&E7V}N5&4Ip9|7S0)=MBf3;|%f(>(wXgAoA
+YvDS1Lp4Bab2q?ua$8Ek&f=URvCaDA)csA$SJASFFZciat8l(iSxm3b=<6^shcrVycQ`$hZ<s9hT!Ap
+FsBa;0`d+3R_o8(~T8+W2KRv;A=c*w=s<w+ke&z9~uTT0V~d;!y6kFkZ@cmH{8A$b3vY@tS6kfSA}JP
+F#y1xJzekpe!9sQ^lIKG}ZB_mjB+=d!FCd;SE}s6H&}u7r;RvjTc}3&Eotr;?n>=c3wfZNH-M0(6<G2
++1CNSiYpgQeN1uXV}`!p%hiQFg^3ffnB+_TqT$))>H9*Q-v|*U<I5MQ(+Qz+jJZ-o14iJem$E|!jFjQ
+e4=4WK7C3wOrukKOgq6rQk{tD<qikYD-1dHa6(&B^^}GWtd?C~v~(JVJGif_PY`^q<qSw-6c3l6614C
+HB|MAi+I`o0Zs{;?0h_x0otobQ*Hah=Bs+%hi%z=+fS^5Kvxiu{wQeWS%~Ab{J|&ZM`-NFHNta*FT<e
+A^RqHWAY<!EO>J+TM2w|U_pQpGz(`Uf#FVi`Tw3$wSq2W4o0XRfs0S_3WS`qi{2mM9yY8O-5^XVmkf{
+$lYH!xf$8kN#8$wR0QN$y>E5`byjI)iAEj<PIAXPAx9;hQ^2_UB`YwE`JbAKe4|6_V=^yMBr9FImxP1
+K=`SL$U?m2}iF9v|%C2+{sShCnIVse-mH6erA4t^2;RUd5MD3zD;exmyF-Z9EjW$B@SgTq#*?}=zz>&
+vu6<)(B>eJgKrcog^SGG&X1gZ1YqLWmaWjZ?ZiKRo>GE4S)p}8eSL&i211x2NOJ6K5eJQI?#wNkETZJ
+VQwGe&_~Ze2hB!`NI3S??vdUDJ0N(~obUu(Q3ZuUDPcUd83nP=b1)HbPLjX*r(}<4<;3yMiM#`(ezvE
+9uRJ?f;*xf^5Gi}`w*qs)hkmSB|ltGdF3^owid9HVhQfh?_r4M^z{}-d<M*{<+pD*tyZ^4{2Tdx!EZ@
+CHTJmkEe555LuV+6(uQa`I_yh2`xrnq~oY@*UxP@`f2mMvHqzAe;FdoM5N0P5+(<|b}<#^NgQ?$)}Vl
+N)RITz=+txSooMt}<GHdoo~c?jyi5<>YbAhA0mH`(ysjgD4_cgR*yRUPu9}(R)<i>+kM*%GewT@^4F>
+LSKea8~vPIOgIMexb*p^{Jiu$lmDX#Z-|&*iusDk4LD`)z#wEnxx2i%1_#pKS_o4oR7t?9F^I)FnC=)
+qMF**MqY1eaB!EsM?0Vw!Y)Jy$9i9p34Yg}jY@Dp2X&6&r=J%}UDJF7<$L3Vm())N|%@6~+et{L(4yK
+nFF2s`&d#qHofL~Ui-^0Z^zT-HVfo6p)4g*&KJ|4J^#;Ny2pN`67apo|_4B{>~`>wcalA;Ku#(cz5jc
+`n(#EDQe(YvC;wFHzV`vkj&yMoQigx6~@UVnZ4mS#<qVFU-q)1f6Dl;OT_@UE$;v;&U<U(j8&5mdU6Q
+zqTQwyFv(NV<jREGNNbuQZPK$k<Bh)$@c^4y|Vt{e_#_3lx|~4~PTYq<{S&0GKbEubZxgQO|y`NNkAs
+nr~0hHJZ>e@xX?OUdl3|IU3wAl?@_v^wOD7s6P*~-e2!soMw(TF@5d+a1jn|%6jd-Q|W3lmZv&mN38h
+l<UxCjihr(*N&lMfvG*g1K~8S(Sl#~x0mQij`1<YQ`<Ji&^c?b;03-a9Qw=`n5UptDF5jY_Tadilthj
+NTF;WB!Pw(8^@tz>6b9zn0i)K_Ki)*cl66xl!1xgs|i<o4_N6vVAT$Ve64BMYy9RYuz`1JoGaSDWIGP
+V|CGZuBlzH0IO-HSiG{>#h7t2gid{QSdv%v-s%u>FF`&@+=nHJG`%NWxr=bKKeGoC_CIEIMTMWAYKRx
+l}zK28k0{0>vN4U@;_f4=nPYmvr~pS7}_?!2#p++yN7D24FZ99alIS68Iy@d!L6FLNVs#52g8D%^Twh
+hZ-Yy)045ns<zh#eO^48w9CJcfo}-A?-EA5?9~hSP()fn+*OaDUnG&fDBG;+eD7P;)V=EB=fD#lTFa&
+9i?hp_(|G^0Hm?VNea@$#d^@22GF%~TfZ8OwgCS3($!Pp?z96MNGuWEb*4YAkM5Is{kaj+uhAd}*O?c
+ItfeJIVD%+1qWRP#vkujs~$(%V$nNOl%S*@XxoiEpU)CX5{I2;qNhvk?V?_U$QLSs7(Ujy>ZOLDmAn&
+u@dcTCYzq~gKlNPN7s0lD1&t_!J55iq$3F)HDXNpaMM?PU?Qu~`kCxn30UoIx9%-}K<s9_b4qba0FK!
+(lH~ttm#^j~cz1E|y(6gh_bZw>ezeBRDq+?ZlikcXrpR0ac~7YuXyRjHVks^-VCU!+9AnBkrH>VZ;;q
+=wOf!UPK?y%x})m#*QM#(Ue)-3I(nKeq4k?sFk6CZN))kSjQBzXKqNnkG_2)JZf3n;>c3cP!YTjB+TY
+ig6pbkF?12jpplGx^_kw#%m=C8R~<+M7KUGfu}vx5!xSv5wd5V9cw=@a4|D0c+2v)2*6SbyKBi0J8@a
+^fO6=d6DL;2WV{7-!&?(f$NL6CO1^o%RM^;zNwkgt>D5lEFX}?`0vPa~8DvWo1JISVhnDL#-ZHkqj_O
+Q=aRIXTEW1>bU#lcETyP?j9pw1b>NyDR?!TlcT)P!Z|z%zWc5hUr{mWg--AVMlI9iqa#-Q=H<2+Zo;O
+OaSQgVsU0-W6cx$)IB(zsj<LNLW&7PPVG(;3nvwIhrMh!ga@?le@x6FC=k%%)(-Cbi+lRS98}<Qf%3q
+3{-b@yXN2?d;)w<oEcRv9J!_P;9PBOm)#bUwd<_hA_*)i9AxVnKG60>0iSW`p?r}0_l)bY{F{U`@vA6
+px}`&R=AO{<z()AY({66X$^AT)<sj6RPY`%AweS;vkkhFs8)qHnKH&4Ab>zpFVU|8}EIc^&>B^fDwz+
+31n8WBE>8rkC1Dl`ELqsl~_zOTlA9)MP34<X|gV%5{7cSr*^cgVN@l@9R&AFrj{V5{lmxJ28K4;0lbm
+syi4D`h#q-4Ab_{}#AP#ko>etxfHKTx{wknm-;qV_uxa#gcI(+W$ce;ftWE}?WON&F_OugFN;TZ+Ci0
+|_Hr=d$`Cg5Y!{BN3645C0aWCEOoBOi4ce@b>wOKmJ1*$%0<Mqa2h%eiG6!7W@RU0ZzwDBpJP_t#K)i
+MDV5X7KA}*Kb2Nj40K>WYAl4yeH$H1P5K@XHxJOQUBOFC0iOgcJi2IvesO9Waqk$rA}fR|(0xAMjo!7
+B;d&SF5%rP<G5{it0eR3R5J$efF$2iGp6zCfr|yq9sgkC;9)~86n#nYp#U?R+!52b$Wc&_4q!4!(_5A
+U27FV7BX$ek9qh_kwzvA!GPPOfN{MB?Lnt43ezKb6ZO2c{SI18sM*H?Gt;a46zd0COWR_`7Vs8t04Ot
+Y`J^H<`mcrhjJ>fuSfAnlX-4}+`M5W!h=qdLY<bMIfs+bJFn>3Nx4N%DyUbu+uPQ7;L)x7q~`o|0_^>
+#GY_ez&gvW$O!lclFOJFMOpn=ee<YblLBHvm-Az$b*M^7-|hR*EVuH!KqAmeN)~wnEKbV6yu1BHv>$&
+n!+7UH^MqPz<5cI^;Hk_R+%U}L3O*rDOcLDtn2b|!pO0kuZH&y+@z75hsCdq^0&_AsbDY6(xY5|U_#t
+pwZVx6zWJ6_7BlcG9eRa=c@+%5ibU!kIRaB>qHmy_ekt;0{HDygzGYIZ3HfYI$)>S~&;y;k8PGfM34Y
+PZBi9w?PpB!F*SWmN_$ibhG++igPz@CDI0HYU0Zw{{Fd_xiP~#)faKx#+8$>n%8>g$HYxx3BL)R!v5P
+G_unWmCt@A^SDbqEGs&C}n}i@D4+O{aRVZ0v5gTu_gm02I6$)Vl)^2nnPHyxzXZTUu)H8b0JW!+GecL
+tW(gqo8Sd*9DFkj9o2vavJ{8%d}{(`K_DL)(p?Y@D-iZxy0DWd;A8&MsKUpd^vZ+Wk;ZoH6{6}dPp|*
+6ipif9Z$}pBeu|3NH^94jRlPU9rj3VztDfnFK1t8J799P{9C^nEatix6CLp(tM9QW5nCcn4^Gx7TN^u
+Ri;Tb#gj1moJG?_Sjq_aQIJWm3&&c`Z1+{gi)UXR5;eSZzdOIa&en*cm-LWGBI}YK22aW&+KL=or9X`
+^;JI)?^em6XNyx@l5Be>q1VjMqlTwq63FN)~-iLcM}d;&I_?_udio3)*7a?{4{4{?G`J|Xz-?-<1I#~
+#;Nsl+p_y-K`ju@9(S#P4)?B*o~B4K;jhxVVep3L;mX15oV98_n0(_q6TbRyDl>+Q(V$N}-@n#9sZX(
+tc3RH*ae9#v(2x#HleQSOW~>&ETjVka87yS5xzng3-M+c?OjYbDNEnD>8kHnoQPorsR8pB@%_NEx=|p
+$Qy#nW%!B$+4v|SSheF7+VEjqxb9UVM@x4DkJl^8_Ow5&$@1by>awH=gWBvzsm(^ovYGz;<d>;yr<Ve
+4(B?BzrG0uCJssC6tbMNYm4f7%XP2|FUaM0)Ucnp{-J#w?ZR1je?tBx}6MJd8=O8lgE-MyRh5QB$>#B
+oT_~ViKcFaUwX2`}PTN<*|mG+5y$0nQwZQXZof)=3v4^T@31QY-O00;o{l@>>D767gG0RR9w3IG5r00
+01RX>c!Jc4cm4Z*nhWX>)XJX<{#5Vqs%zaBp&SFLQZwV{dL|X=g5DW@qhH%Z{5s5WLS<jFf9)q}+4LD
+N+uRHaS@q8lZvJ;%PKJY>X`D+tWPOARcSjW0Z^K>F(;9Di`=z65}N-9i(_UsSo-q*%Y(N8INRjf(~L&
+4uKC^KhYU9rP}AfpyTGm-Wkktuc`8VOP0j_66wbdOE1`;In>%nB_*fvTo!N!>(OQnM3a0t{bC%Nr~v+
+`EqU!pRz;vIhNQnqQERmN=zytmF@y!h?M3-?Pz|X}=`T6}AFIT8(;q8#0p~)l3XDcIIVtwWLthhqVf@
+8%l~;zl1GQ?n3K{zo9NMqJH9?V&QPtewV9gnBO7<nPR>rQ8VYoFML0S8p?6s`J7jP`YA?M`$O_hc51E
+4u=0g11hDFQ|hpGp%yn+&)m+{HG*qm7Fyfq}a(tm3aG!n80McVb8Qfq%!2=10AgD|PJBn@bW;Z#X~kd
+EQ=kbnMuy_rQ&*;>DTn0pXz^p`FRJelG93gm+0-Tb~M&;(8L+MrK$4))kg@y*6ipp1Q(J6!GVOQ`Ibw
+@&a?a-v(PjgDKk(>yEt(KXPoqKYF6ab`Gd<^6%9){b6%lcJ#-L!cszSxVa8ka&m2Yi;Ekviw>@07Xmq
+;rlB0W*u5&(jr<?YcSMu;5~fc6`G1pp-em3uD<2uR&!!!w?9u)gP)h>@6aWAK2mtey7Do|3NFeAQ002
+&D001fg003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mb9r-PZ*FF3XD)Dg-97zx+cuKF`>()C$s
+?6WbmF+aoLjfgv7Oe}B=)o1G<&vJp(w~=Op#iG^vCvkfBVf001_ZU+u3&Sht+#+BoY`5fcc&Q+-IX_B
+Nk<|IKA9vWicPU#wTn2wLaU+R(E-PxhR;wH((ntUT(0{1!u>1#Ue}DIGYzYVb0mxtW0NN5oc)toyMHA
+cZYiiAI1kkaa*u3ow2>652q)GZ+<yFIvL|bXt&nu^>(9(3&Dy^f%F6*lFsAHGAD2>PUl&^l%S#g+FHC
+^WqE<<@<`}kS@kXF`kS~DYx6u?!pmS477M1Huflv5=R;^2Cir}<$nUn-7<?+whyXMbJYVCt5nmPTkX{
+|+d6rvk_o(eLwPm5O`>w4`CSj6HCTxeDQ7`Y37ZcfLvY5={gs0(>d&Abt>o9je73Sk*c*PNj6B5p?`F
+nzmMDwZrAPEaxa_brG&E%Tr0`|dtBccUgavLnozM3UGy?~XN@j07_aLy-s-F^JuK(1!B1^CH%QRXSL+
+tFLK8AoGdBa9xjn?NCR-1~sv@~AArX~Jd0$??h2e;w?dPTuT}4`4?WSp+}~h`!&z&IU0UtnoxBNc{$U
+7KJIBa>kdd;*P=aVSagi_TqvqfFh=xrz~28=_Q{LCn(;Dg@RS41Or$Arjf_|8omRIAtG#*XP0@nWQd@
+15)GtbPOvM{&CCacaUtLV_GLRetd|x&LZG*bre&PWdca&Zz_Zco?TaAEZg}nw+^g0ekQVVv7N@?|cBm
+K)5b3kyz4edd{|H`oqI7<~iq6lkT`0YL@u%a|#BKNe{?_KbxY2^0pNGrY)}|B5&3`45l6_*K8IiEj0|
+B$LISNYV_r|~oE1+mm^oC6RO0p<SdV`u<<UDEgSfJ#%7n$DsY*r>*0Kj=VFu)j$cbb=yJk{^FA@B-Fz
+vI82{&e(V^5&Prcl)petqDMrXnu){GK1Hu7fA}9iZbWbTRqpyB%1<TRL|GceA29_AJ!<6_z!}di>%B+
+(P*hXkJA~*XWeWXMpyV8gkf|QUUJ+m5^0l3kc(9k7rqyGL-uk2OkMSwNGQ-)e({@N26a<F4Hp^#%f<o
+z<9i@Sq?`<(g;#e71?Z2W>NjL|OAv&x#loxF-^8Hbbn74#6H&lQEd2rMBw_E_WW4v&!TW=8fNM2h`C?
+#FhP7vQM<g^U?}^yFRVL!w$tnjGbPKDaelIdmLk%*qh84W<B^c-vr%_VQIClR{<Pnhj@9T%^+2D~dP-
+B^JKxls9Q85h#|9w3GFxEb3Y9pEj0#qR3iK)ATlkmpW?Le!OkGm(6lY`@T&@|^klr2}tJGpoMn}7DV-
+!7j0FgX7mN0S76y8_`ckpwGz30sSX7IkYnR$IKl9fT=>vh_U$!dz4JsKPmj3%(RaQ>iU0g=O~vp#XTR
+Wl_Tzu=Ep!745owgxjv)TH-{%2eDb<U{e6VhCNbJLW9j#*lWnH!=xmdQhn{N_Oi%6z8Z0;U^wWlHKKY
+br&u&0C@kchRkNHgvup0yi8ZCV)9i-HjRw=1k)Q=Id5j8OFrb4t1*H!YLxqlfi|i@;y4JR}0rjkEs5@
+*HM}==~yy`J9NS%l<=fE2gP%jwJ(IT6EX{?jfbxgkvF7vEh`7Z~}^`~}#V1-3{xs~LvTAX2@i#11+#%
+M1Lq@6_JoN0@?I=8iA+d!04zgiN#-!OW=$*fu?AZC5`_VCmD12zM@7rl$*j=^T3{im#9w%|EPp|}uCd
+Mcpiz-~ZQlYo3m3+czqcoD`4!pr7tPuhS(Rtj#ynxMDCkSO5Ug5Rpgs7NOt4^GC1M;~Cjj1Ga+PNbKN
+Mw9VRM<=It)3fbYjy97IN0a>{Y-+qh)AhOaU<13J9Kcc-hI0zjtxRR920%(4d^*^Bpe<U?ld1!FhHnp
+l#BD+${2@#E+T@q<!HG7U=!te9%Bd*G4I-~#D0z+^D3Sml@lf&`s6gb(&)D!Yx_h+oU}s-}_+?3a#o2
+EHlvPkHpxy6MheAgs0dlY!R+;2%NsDAnAmnAZVxQ}gKL?V659jz6ykQ`KG;@Ife0iwsd%^>Zf8`nk8v
+C_JaqokiYjACisnd9?UpIJ`Ck7DMNS<EDd6qhX)UO*rI+@;h*~y~P^Q$y!!GTFh`W4_|f%l2ufzxr->
+AUtO<=ZQMN9wL_8{i{Dufb14(A030u(54sXiTEK2L6DGGX_3`<`;kqjkRW*2t>`=m@z0hCt?5F@7gxN
+S4djO50+vunpum4uZFA<KJc}YXwSd0u9T=Hlv;j+CJ1H!p~aOJO@mLutl3{qwM0Y8P_t^5dU0B^!en>
+FKeRp;@c;m|pi=x!sHN3@`_Jd8PHuAW(H~*~wOop1h@SX7EE64i(L`!HuWD~<4ErQ=R99`qBMDGLBrD
+UJZ=!CS`X=6LOanjMWHh}sVjvbK_*-tU5)v4t@O#qLKv|EX9vn<0ZAG344&70rnP<RB!q};L@FsOxo~
+-OvS!*{7P|o2rD+}gjVuLS<c=H$>E8(#p9LtC?fO^!9S?nGk@9&=O_6Df9d;i-r$wg1zNh<oKA#gidR
+ttg6!6ldGLr=|2#k+Liju4-P`AwV}$j@__(@mCN!3=DXp!ebJ$?p4uUyn|H?pYLHb!=JX?#bOZLH8tG
+6NPnzdA^IMd6?f}=s{xvPoK_$r?Vb=+5*!2G%c&M?Tw3i&D!X@(6-Taqp3qInhRtAR0n5$9h{ZYXorZ
+rGFHgFWFdR6T8~6=mvC|vlv%M$20zRJR)UmF7^i4aAjowoXjfq@b%VqYtO>G4!ju3=OBh@e0}e7g1{u
+gZ357J6I6$9a@Uld^6>VA_r7UGjQK0V(25iVueq+G1t(QJK;g?|Kff>a440@8Ffr%p8dZNt7;F^e+aT
+21z3-f{=Lj{1$vn-jZ<)BCwEs7Y(u_oHnnB=#%L*Dbx!qGo=NB@1H{(Uj}&&kE}!MS+01ONHKvw@;;i
+p2n&mn|51@BGQR@1H?$fN`PzB|y&x=gs~b&i=o3bpPGp+sEj1@H}|^E6s}f0X-qRRi;CNY{gT9-gqjr
+Iy)X^bwJ#444jLdo+#nVWms@7qHx7~6eyb4C?w#yA`Wwk645W;FdXm!nU?s%vJmh|Ai(q~1R~~peUAj
+AdY;ECe?S`Qp#Z@WeG+4n1Ja*9!m;Gjvf<JKm!T5Z+Iz@0Y<CvlUTaLOH+}Z`$rF#<H$b=GIk3!Ue1N
++9%9diqawP#NP3?Qo6|{*NZBhvKo>0ITd$i@A+Rn3-tiwkLN-I;+oL8KJ)z=Q2skF&QlogH4i9LVda1
+1*dlXo^WFmBHOi6d&~o=5|bR?@SqiXq^1c)rRK=|Q7wMUBi@ND_;}SBt2u`%2$`tlANagv<kv)??C&(
+NP&Kr31`sf+R+X1-_XAF~tTmkk8lPyc13c3N45NjR^&+SOBeEE?6C-Ro%xc9>uVbQK-B^DGrN_$q1l?
+Qq7m_7>X&NrUToZU-MLfipjY&nt(`_Vw;Q=jaH~|3Y3wCFJ}{T#e8q??YrF{$GBVX_N%XZ$H(%!TE3e
+F%4iTIe${!vzGp8CivqY>o5M1sShsY00~N8iLPJX(8CTLc5IoF*+oko3^23z1_S+`EEv5fmG=!@Dq^5
+Le6YCr^**ZMje2ESKPIu;nOfA55!njM3`--?-{Gj2e1?gf(u+vZn!)AvJT_OJW67(|55k+Sgjl9gHbd
+THR%-2H$8^(py!pI@pY#t^FB}ydqQfIUh?pp;jC~&cM4?ZnA?O5D7^Rc0&Uy!E<n5gSC2eHf9tL+QtN
+RG&HV-{F+;CfByOOf*Y5!;C$dw)8hb_q)ZrHZ_&;V%ZR^?g|G-ZMwE{SjX`h;Uv|?u&TvYzZ5UNxBPX
+Th0Hn?e<dcpFhATBO&lyg6rCJr9r7p_u5#}rHZw<THe3Lof@_|9n!XK5oC?@IL)g+ZTy{v%H!wa2HM%
+Y;tG}eAZ~L|mj3*c42hqInDW8Ss81A(d)w-u4QhmYMYs8Jtl1-MGq4x3%Tm+|9aJ$GEolVK4i7E^)|1
+M4=gB(wWa}&C#eeGn^raL9o8{RG#W46y0OE$~OgwnZ!=%;a5xacf?)E8MzG7lk=5Ypd5G%0iL>(zavs
+x{mGddazbPq#7)W04gu!dkI{H`u6<6Dm$RwXZ@ObVe*GnMSGy-aQWiJ)A~z;_`TXob=6rSig^^-+H9c
+%R4~I1p8bWzxXX?rET%+3uWpBMRj%o>1U=Rf=~Jim(9r4aOpvUEwsIC*h@)-BqEPS)5N4p?mVt=&5)r
+Joc3Ntvt?<f*l%~EC1UGwHeQ=C!_1bd`}eM15Ulcun7jHpr`0n@sBu-p(BP2{bpt+F2Ej+{~`B#NtGq
+&d4{VM&^nN3A)}JVN8`g!I3=XCppGjfSvaGvL~Ka{#<U(@4x)ElMY$XVK+P-}-;CGjFDiO0*HV=M9+4
+DBl#Qibic8d?aSD7F*9Be@MRH!YjVdqn(b-ecgMHK^nT(|A1LJ&H#ew7X8q6dI#Q#5MJ?1|Z1B6D(g4
+)+48phz$D6D<xFz8;$)tyeW%fWO3pB*q<+`E+IXi*SCH*W?O@M9MU`tyJNLhG^q{}=iwWxI!;hAK0Ne
+&7w*!<Qs;sO}XvA&Ju~j>R<~7i;<V-lUE`8;$U(;HoQv$~n&Znrk>}L1eYlkWIlMyjk$H-ed)eqJ3&G
+a!tXwN-6yX(l+N&k?D|LxAdb#2Y8U<pLS0U_C2X~@X?$4nI#a?Ed66b;lpVy2n4uB(@?~bo?C@@thAQ
+G@9@q3!T6NWYl}0Z;hVz`?zR|KceFqJu=nnl{e$DbHk!v)@seyiKB7imu)3Rh#`A+u2kHgC<>m_|+Ua
+Fi%HGC(|M0{!b7+q(YNaCYRXI&yC3lYx1^kwK*5uLEz0qqeIsP4@*N+X$Qgu%|`rMZ<7)5`r(C_mB!{
+GCIs*C~ft@ykRVEydb1?$sGHxPKgeqLpNWS|Da6?XI%gSV?Hdh6R+%&ed`wy>K*<EvWMVx^Iu<DV6Lb
+^2Bvk;_N;PDlieNE2pX?O1BtR>!I7#dprkJ-cdvI(&p9y|~8!btujI4A5EyKf?UHFI7qJ(+JHpLnt^c
+)Z;xP-`8HN8j97?VPMuRZQ*H4M3<eZdSWSOwE&eh**s;?Ucpd?A5G~Z6&!XFBx!0JQjISImLkgwKJ$&
+TQeAmBFo&;CPCR;WsiW<o`|S4i7UUGitpjiFCQLD4E3iT=qDZ!hVTF_wfTB@v+r&@TYwS2{1PtLlR27
+RmC^8(=S%TWdSr`)vD+@v`s*>%WKg*w=KclcTtVvy9K(PUo<tT7KnP0~<J|oF|6DNtaAMWY|s+%nH9K
+)VEzimtF?pj(o&6c~?U3TN3Y1N0yY24*8&3*8tSM(=s1_=hckW86+|HMXE>!xPbBp->JMr)u#^(}!$i
+)U740N7vA_D{1)1B;;^Bs6t5>R&+2I_0Hp8I_~}vzA{Yb99pc^#I%Ymo&ab;~J*D<Y6i>Wdeg#5g4Ne
+w>n_f_8q^~;C_wMS#~4n+{gPcVn<^jRu2<`%Q9LpvBZ8@vs)+>Xwt43BRk_<$;1&d(Iev<vW_sxvMb!
+qawW+wjA}Sl#jte}uAWne;i3;m7zLG;3Nec;TZT=e1XCF}Qe=I=qAE1bXH1n+2+~wC^M&hVRCAIuv4D
+qnNT!SMntyA6f?3T&EZCE7Oqg?3rwJXx+<;H^+A<Tg;dE>xtqL<GC_Ld(7I7jrl)n%-Cv)a0X(<h9Do
+X8WXv{*kHtJZrVBb|I;u?nfgB>Pf0jkGs-#^?r&1PAvv$JT|4GN1}9<e0IZFSQ}tVezBLPKvD4ESl8f
+HsgSAa<e3m-U0%Mv->mF8J;-c(^Xxuy6GQm3BuOXj;ny%&CDC(gH;Uk44Gz>PT?2ATlj5lbVT*W;tZj
+0XE?eT;N<Kd^Mo^fZ*xdqCm;w=}J5*X`rH!DNnK+DOzy$Qk7U73aWWtrt(ArCHhF>lvKIXJf8~9jlNR
+r#KlPW56p>~1@qs-<Tiq>=G3e;uW%<qvdRjr_4nkVAy0?*O(^%lSKYeFv~3A@1F(H;cRK@ZJXM`uW1)
+q7iOx@kMpif#SppO-)jkH*3k-sA^gC0I2c;{SFX2exoZLxYw-pYeQ+7r~hq0fs8&r@(HY>3>-rPPekZ
+OjcbNOwErx%83r7r38H?U4#rqUXOwN6K7+z|Du0gCh+>ijunKZ<5dNo}2dIuJF9>CO<1H#{0ufa%^a{
+5ajq=w>E;Abm2{<z=>x6x+S&41)QXGGAF?9HfNJ>)Lu}GK?nkmmM6yk%R76v|(&3uF;KDoQm8@f8vr=
+OYDH80wi_ZCt$lmfTdwP!f_Nlq8bO7Y6!vuL)RKdO}9a-muk>(L1h(d7oRj?bn;#MYze)y1w^wl){~X
+74dHXe^OPrA!AjVr^w&X+;_QE>7TQK({_9{fcx5BI48LT#0Rv+iHsGK-Oo}IV+NEMPgzwmE7h!;zs!Y
+>Qz?LvC&R)I<Hirxh_~;73jIcU}c4-vD{PQepmud8M;8#`yVTp~Z2BMBKLx}XSRHa9E*rC0{eRhh*cU
+{~>0cWBi6c%(IL0OGx76YcQR9?pcO6r-Am1&HN0LH8oVH6rcDbpTb9b=FR*r;bh*`N!ZTAhqVU}?FWf
+=LfU%y<sN&Wt>%T@wbYeGjA+#*v_1xk84!;Yrd)R)35VEU?$=A9d)wqbx<*Gdng<;PHujTf@HLsREDa
+;#@pGzj=Nxa6AV8GZKam_Se+J&i=;Ei;HKEfYHJf<C2y=ho7!|`fj2P7S#Wg)B(cC<C%0Fa6aITM+Mf
+>No<LPT-2VLRZG<#y9oNj(dogqBx_k)7ouz|a&R;v*x>0M+g+`wJQ0}i6O!~dP6^*ft{`6qx(L*}VWQ
+G5bX+t{FU#-}4~oV@*|IarpgACs{|GFPt}#f@Hri+l?VjxYBx&g}MP4K%tRgJ~KnFqn+cH~c@2}%Hv&
+TQnbtVk??{n;fMN$B$vr9LlImqb{1CSmtO=i$AR%wS=)rAV6<~hu$>55nzib`3~ao0-FYayPp@&3<}Z
+dE-dc-O*JG1x~e%rMU~1h&QkbLu(;e@5~?0>Klr61JO5$ZeU2v4bG`PUXd=)<wV(nq)HOBvl$NfT*;R
+3?Kzr?vgLlSZ1b_S)4OtX}7l3I#e&Qc-M)))a514Pv&o_Bd(-c#M<r#p`s#-4oIYh@3OKA1b)I30*_e
+}We?aCPbZxWMsienFBQql(%f2C0OkAV<L3kDCi}O$K3uemA2GYSzjRlDGR<;%PlgX&@2T_rLsCty;-4
+nk6u7UEZ}|CRGL9DEtL2>8t7Rdeyc@&Di~o5tc%;n3&jUhCUae$Q2Vct@EzW8z&8l>}t!^KnOP$XA7J
+XAWoOv=;W%Lr!of)u#t*$V+gz8HA$>cYsqfx*lypHfni9<P&I&iMD<G}a?t$8t+Kk!sg`rz+n4A4=O0
+=2c8)syYY2y`_!QXR^rawWvn!Fwt4W`IRJkJVX)jzIotEpO4YjMIby$Y>V<q%szZs>ev+nTmNFHKH3a
+sUc~Ex!V@CorV5L0K?v`;z-6%WR6lI31(Jx-xjJu?T=}-Xjz>;Ua(VwJs3-o?A4y5H;`f@Oz>>R<wAM
+kVN{s6+Uc=jDjexjXl-~j{+98}fGGO#L`7X&<HrI^LRVp4#8H`qIZlQyIl}yjkNp51Jj_e!f{lMXBtV
+NWpW)Sg1F2?kkauxf-qHwHwzJzFLvo`YN0Xh58c}|rB37CIsQ4zqF-Gw>uea<cVX|6;%>SWcKas&eu^
+4RxTO(1Xo27mc=ke|6o8XoCduwyF6>R8?j7;HRhOx>vr^sj(65#)j=q4}bZ*5M&%Fumau5=X`?CMp>Z
+eMRrw!p7GObedimxK3Po2Ns@i)dhiibE%)Ls*))T$)W^Z2)9Ii%P8o8!=6wNQ6T>R$<h`s6fG6yd8y#
+v@ifCOC+lR_E7T{>@^h+%g%ysj;f$~tx^{=;+z)NiE)v}b31a2RAN$Jt&~*$XNUC|E`x8Jf^OVcLKk1
+@%Rj0{b8NT6MlYS`pUo*$@4vVJ_XfQoi)+A`W^3Go5<kaNO$vkUFV(eeCNi=cED4c`=S0e@$jEbcjph
+E|(-{e;?(3RsbC>>7MMlwDM@vUnF=kjzOoGRZ9rW3!PoK<#?@jsf1N(4_q((H3Fn5s=hXw$h1{Li6*6
+S;5a(ui;Ob|SbIvCh_OO&IU!xD2)?AKYDlf1w@F7?QSOrS3^Q4F=aJJh0RSosEKh)$2C!|H|*$FXUDB
+LbtsTHd;cS2E@xiUF?b?PCx=0-t7?VR1QoGu~f68iRZ!xu1urK<`Ku#xoyzp?8d+afvX5FBYnjM$Umy
+q{O=-u==dzx?w}t6H?#mW@t2dlZV?8xREz7$c2tZY=3h>v2+r#J(DVS3%rR&(AhE)`|Csi07e1#bRoe
+i&oJ=-lO2r=0P(BWTMZ&;(V8SBHa^}xkp%NYlVDiysTj!$U0-j!LO@qzv}Xg_AQQ%H;<+XhOo4t6Exz
+e(8yweX<1z(u_y&8ArCMPd7E@Lm)>CSy*Wg(KkMHsB0f{e0ck6KnAk1G?^m?|jePO2L=<u9AB?@F#rM
+%_9Bh_uB_ZW6o`pR{6gtZczawSmpSyN@wD=)z=nsWJmYqR&4=CV6hU18u@^;dGN(8|VGfPx0BidVaVZ
+b_ig-KBj?MZFQ~l`&#6$$8_Hy?1s#3of1;-l+6Kv8gKTvv5%4vTTZEy}jDG?8dltuSVb4W>U~)&Q@g(
+LYfQApLwwJq0F|BK};DNihoz;73RwfC{=BP$5zl{g*9o{l%OHrdcV7OH2yT%KRAAO^w;+XA5JHyyC**
+$kQ5iZFaQ-<AT4Xm)hPQr!%Rs$jgz>z12Uv+4r%Nq<`G0lN#ZEPD2tsMu3|ERU1q8dN$mp(NGxGTUsY
+x;Z}eGh*|`hnT6jeBOI2Rm@;~am4Wny1bEq22phOpJ&&~tc1+o&Y-Q*^`6POD}v$oKOkR&yz^FTW5sp
+dayfT&M&&p7CkB`wihoz+q<${co$!b9kzNVOf<&Nu)!h>)h)80SasBAtMp@K9ET;XbdZ=!otwm}L=_6
+7g9B5X@LPg)UO`u{x3bPSsz!DD$j|54QP{V`vE|GEBouo<E|}caAy{PltHxV;Xo}-SD!$2jn;A`E?A+
+M!ox}=18;hoLO6S-olkR_*I8DL$s0c<Qj1<n`XB|`!KUndgzMl@kMwI=1SfUz_hB5zw*WgJ7Si5-(B&
+$?~f_XQ2Ziz{xKHuG~sF;zFWuF=i-_BP6qn}z@!)cy&pXLVes9$`h`)LK$b~5j=lqpf*;b6c?zN=6Wn
+nU^>NWVi^RRalbPT;nrEg)>6)G<fWBPb&b_O5%Hn&(N^;b8ljWo^sS&gKx%%A8qqXXSmzlWjwX(auZ!
+E%dT!aDD!eGQV@SG_mnLMC$jyo=6#boutN@qO*M!K7MRq~PBLP~}ND2|#sAgN<{X7ah+%7uY)1vvU1Y
+;%O7sDY8d;y@rXsvXoacU0W1+7Z(}x;v_~XP)lxU@BhFj&{}tG_`2Gk908e{*`tdhsHfpQBZV3fk5l5
+kB)su9nnwS*D6TIi%jXoVBi=IolKCSvH@BpFFRRIO_-XRcUS8VnK%jsycz_}db-Ma6?LVcA6mvPkTRO
+`2&j1LOY(TQi0apX2c%y;3>}~Xw6BC~dh?G&`|HQ4iQX|**IfYecw&8&#_!T0y?wgp_6lz{)`P0sPgU
+$k>^#lM+@~MICV|p?D|B%?al##SESMjYm+;8;$mnLAo0T$@4;GCy)l8eThV*J<geEM7&++6JUYk>2m{
+XNFHR7fYK-ghx(*)*XJ`8g#!&`wT7D0Y*JbU(P<H2oH`+Lt~QTmAj3deht$ic6ZDz@MvjXpb$K@o=1a
+HqA>CG*F}dmW*=x-<Tr7P&txYF^X#N9HPt`YXuO?yDBb@-aL7RU=$r<R+}YR-!W$I$_f)x7L^Tfq_)0
++hyYdiyvoU5gLDE0q^Yx+tIHtO&#E#@e~Z@9B(5~!W+BkD-ujqjGhsKK(2UUuLF(kdr!qSqvQRAfMKD
+j_vgh^K|u)7qh<ys)!BQ@o)}4){TS>j(6eYXnMsH0sw#Ogby}ZNQez#fINo+OoSpU=SdHbmW3}RqE{)
+x=I_{}_ka>h#omr`&I1ka7VAO4V#twSN^3U2B=UX1?QQa2ix$n%yKM$h+Dg+6(39a>HqJ*1T`L+K8P)
+h>@6aWAK2mtey7Du5&I9p>`007fz0RSif003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mbYXI4X
+>4UKaCzOmeS6zBvN-y`KLuAlIhGPxYdJ4Xee2%iBu?t5j-AJLHtD9hDkae}H!`V1QgPhf{qA?(0OF05
+oV4fO^V4o)i3A1%U@({&3}!aBzx}hZ8C7v{na-2J;&x;6kAe6+U)&by?5c{o&wJ7C&dzRh92aHuYFSo
+sGEU~v4|DkaUo-W4Fin1hdI$4)MD@z3NXn%6oJ<EBoABaIQe3BHndVs(rDYUdB}Fp6jb=rhRmrp;MVC
+dAL{WYjO|If%mcWxLkD@rcjTT8!<{7}q$5otWX*P@ED4GC<SUjq(0A872RyT2xV2x=Mmt{UlV*ngY^U
+3l$$*Q<Y2?mZ6G~cbRpls({s&#tOUNlYOIbfI}Tqtp_;YK%Ub(JqGfB`+Ligbby`&f1|UrrG#RV13H*
+D1FsU}Q+Jg=Lxa0S2PqkFN7+dWnA%>hfYao~PwiKbof4!gyK1!xA4(k__r$=l+@(QJKu=3?s$P@F*DA
+_IVhvxdo1U70Do$+{>G*{2FTjjJkt3E0@b60|a2)sN59(D|3JbIQ%1-RQ!~hy3FVE{04eWcx2NQdt2_
+&Twg$ec$|MusFzGeQI=QG3&sm)j4@-1kay)(4CE7y5icH9Km?`&pGse`^UE>NTvnwpQH#8wc5w(0E!7
+*Qxr?6;qw~|37w-<v4x^*<=*`*b-;Q1!zKA*p=Mg;U^rLr27eAf8y@;U5*}=)hFVX4C=-}j+DEj&6<O
+R;t;rlmdhv(<f=~;C2`pxmt;fsEBbn^W8?Te$6S5fo~>YkilM8`+3k1hc6#c4#%3cRDkbASObqt}OL&
+wqj^2hWa<k1l?JF@1S-ae|QH8vu_EqUg=R*~QWGx5o$YGkWv(?9J)<A-p(w0WeNZPmWGro<XCBuMbZy
+27v!b1W%*r@Ne)tI{)e5_?X*{4&Fki&HysNeSZ4pm$Rc+KV1NfpH7co9OCn5hk)?Gv*SZ<n>zgb_~7V
+uKYDTS`ry?e*F6PjfC?66qyfgepAPvcG=BgX96Y}`Iz6E-K0iIVID_AP7=^Qo8p6Ay^F!$G!P(I{pbs
+!!o}EHVIP6dj2lfQ(o*XhLoDzHLpa}kcdyZdmJPCsrhX=<1?;NX}#c}R03n1{(=yF*ti)1v4(rcuYXi
+=nD1y3_tyBiywPUkF%rx6uL%DgGkDnTL#jtV4RT*p-u7n7^>b5ahl_{PR)^cj{CR0pv8QRhF-lFupr{
+1131ZzBA_yPd)JgPji4h?l?@3aEfjH6Q(TxwDfzLSEYScxex-O@V`9C93uxFW}c6u;SrxduMlh_i;4*
+Ztv0V-p(+3umcP_>b@+}egw~7CSxkri~a+lOg@+Cl)xaAqv@WpT|`fxK7RVd0{WPN{yn;ej&H-Dz-pQ
+&*W+a|6L>`etG(pz(|+(dk51C$I=M}XK7N}!{p&|(DRT8`bdlcxAIqi#7_3cmzd51_D$R#7XXS0FzUK
+NHG+L?eNl|3E`cb9VNnNN|PINJ3Me2KzY;07;?cN5Ctvs6*3t+%E(~acIBw18~(P2^K1($;Y&|@e8|D
+NO-fPfvT9F(`$^ECSu!L+|ivuS=)BAEf70xEhk*olfbErD^J<P|`KflP3}Bd{A%b;K<q;p|7<gGx9*m
+Fb5n_G;ueRipGG>JA?cAN61#0**OOxNTse%p51cN0S1WI-mm_H@eE_Q`!X>(0TH_NPx!9Z?DHW>^R4O
++M6Q%43siUuuuGrO8b~U0Mt!X`aLy}C-HS+4C4YeXB;|Ob3%z~4({Vxna_!|K6EbP;w8KUE{$K0vdbL
+5rsYU#$qRzr`M9wG-2`G8sO6bLg%3L)qaUJ&)<lm+<7GOpQe41s8Oe`w1IRY93hFl-qkNIz<ftbD`b~
+od`&1Hb;qNWnK?P?ju<;GT7z=f{v(jEyeha9Rx5tQk=hvN16#8rub!1E2Wnq=ZlPW1To*kb)|M~pr?}
+t$J@o+a#X`W=W>I!$>MUhVu*vUZz8qf2|r*h-$@cHT43#nd%x(}Sjt6e0MyqK07uTI{MVDCA4PRJr(D
+Wb^0c@sF&ES*HNY)NB-5)z0Xut^@hcPsw-Wd}YTv)Upr(=TSljpM_UR~J8xP7YopmcyMLuZ!35m-Kph
+Z6F(y0GR<vtLm@^{d|Y&xNOYoAYHsXdXE4eKVDfK=V?(Sm(Y<*V6)Q=80c3Qzq~o5o`<Fvh1J<|j=LG
+)y>Vcc572{C{P^T&U3wUVPOPsBOCp^nYBl#Z&VPBW>+J^XmBM5Qz{c~R&UE>Q!Sb3gqjHf<Q1qL^zB@
+^PPHPk&1*;NCy47AB)h#{_R-2|km{nffZoE8td8!LP@e89;ahba<pfsdweHW?)i@Qo^%Y0e#XyXdN8l
+&UWlUG8O<IZu%r~)O3gy(rSBPueh2qlg?KQ~pO!B$nyLEJp&HqJX1Vya5H0I?(y<lg`ELU-p&aKvaO4
+u;zxj0~gXO9lHpY(7^YU;#gQ|LRy*pRKMwo9AN~OR0Q*dW_QJ$i(4&$E<vw1JMHib(`IIdwy|nHUcU4
+^}z+ya~G-6?(qAkPoplekDkbW#FKR0VF+tAR#M^3!TY*`&o$j{b{Fv%-Pnu6mj`c;FY5Yr>)*bW&^G&
+rY$qVUBdqFn0dhoj1ro9XUoICU#qDjJzkTxtbb!MbBb={ulxu|Z^rHj)P1s9+!yl(eSO_R2yi)s-T86
+>mYPIzv;U)b@I76_wyVClRyO3I|@zUvS1ja&%UKPnwXo>))Z!*;Z@@&-c+J?q&yzbx{NT5CsYqwH+hE
+1dcB5U3HoyQ`G0PIGk=uy!07g>LdP7Wejfg3~|i69rnO3EGZk~um&enHrD7IAfjk{$9Y^}S4gCu#2*#
+ruvsI-O-Y#kVv5vqZ*7zv;^w_~CHHmzTiqVZz3@(bcrT{iaMR#tuiNMiCx&!)UrKn@yt`BiL}ZydHzZ
++9xYV4zpG8-VSS&-W!C(f=ek#y?y=c@T^AbpEN3+n1*OCx}4{6#Us_31fE?7p5Zv|0jBEr%=t~zKo2(
+l{f!*y8EBAb3M2atlIV3lO}OLz=yN<@CNyOEq>AT*Z)xg$boBC=_QjoixQkL@&5CUy1WUsD<B~geNlP
+lig1}s;;La1fAPXBJ0$@R4UtI8!U9c7}uoN&YF6CfYi#-NN;DcOvXctB)WMkvv{O!?;D54UNc1)>s6G
+=D1x1g^jUy{j^tRNTXuXKgoK!sO+g}+_AmkL9tf-?F79&gVM4_;_IBD+*f3oXCNV2{3d`|j-M;!q)Am
+1KjAQS|Wr;d9-zRg@7-{#qtzu`%krsu6)%2qH2o^5p_b)`;xvHXx#k)`-A7tUyHSoEnJiJPMBpG5{!P
+AhNR?Mnn}g5ZM{7K!odj+tN+56m75R?_yH_dRU;a=j8x-C9d^uC##ToPJnZ=9)5W8{FKbB`@oy3%k8H
+fLrFJYqSil4Dl+ZgmKB;xCOKN<yS;Dh>v;~i0h_v9&Ld?u{3hXPW13t7AJ4kL@L8|xM+)0MnzHhul-_
+qbo#%N5TPN%j!urvs6qBaDU7__ni+RVY&@xUI`hlv-W*Quv0l`nYUNcmrNUCL#MdgP*M(1PnAnJ<7xs
+51x%U%x@_mj8Bj66t>i2OQ8w;{@1;cnSTF9_+&G@EuwM`b*LRu<7;_M`3Lp5Fecdmr`|AFZAYrb$D81
+^=|$fGuvsyazFMi`(i7PbcVwjU*j%pxA&r^i7V|g)$w36sB4&QP^^z_JN-&Yg-C5&;vnnp5F{q2TL?p
+K_4eqMVDA%ukNe1_^A)tqhKfQ%6xR<a+0PbEYaCir-x@HogH}cw5KbIN$zjB!5TI>^&xv<bd<pcoKAl
+tL4`_nJ1IXC=|$$GUW^K%9sLV?pW?DE;%t_5nUH{@hCR0%Km4#CJ$0Tv*az&V-SR^U<T(6jk!r^Chy$
+46N!biUiT$U25pMTwEh;q8*0ic6RnL+;(WJ1UednQ45{u-&m#}@8N_ImSW0`s-^2>tFF9ixoGJ>rfp0
+Thg%LLa(pHIeOd|4_fXt@CJ0;K!&{{1e)*dF$J+rxnt?WIx)A_caqFq@I|NaAKDKuGcgYReKd+Qr4~A
+Uc4B9@7yf@4$!*%-uAd#1-ijfEDT!=>(-9fm5$9l;8AuV5IPq<xawNG)`yRpqQr`iy~Q|tRM%o16WSs
+o=4r?$4}f|s|B+Y0qp<<MNgyq_aiCe5LroD#l4;Tc_+GOmgz@Xucw%!ET0~b8*)0Ix}h~W5BiI#nByY
+Lq8~xmfBd8whk9+*E3ex@wFt!T8(bwO0iw!cG>iS)v0-gS@87?V-htGdUW525G_X|y6<)=k(|lRbQ8Y
+~P42Jh}tz<Y*XksV%5*Qw%cPc0mh69h^@QKP`nJwbUr*7vMn2S8(2lEaMDzLE)Yy6QC;-ZLeyS*j`(K
+v49Ak9Fgthze@JNi$c(1+iB*K<(&@kf(~m{pjMS{nrOk4ZeAj3!r~%H?(09WO5pP62O#&ha1+Pff0p2
+^JE5ex2Y*!cdDI2P+s}UL)ZHy#fI%EVSreDz1wF7$U=?jppVm1<?$q!O-*4O9dpYqMMk^gL8OAb6Ubs
+Cnc=e37XACC@_`$5~d$Y=(#RYhN<w-v3tJE=$P{)d4JKP5#W9bMhrilWzZI)uR$=9%Cs>}L02wd>B_;
+9Y=fjSD|HSA6v_}p1q2^or8AI2p;xF|gLD<$BnBF|k*eW_Ep3?zE`YM`3_Y<~v??dF8S!F#fb-IID0c
+Yh=`)~F!0y8yeE*2+-6`_$@e@^K`1r}*<Kgbd9@EcVS{biyWgKorsMP6H-)>z<IA7eN&<he(w=5>G8J
+3k2EBEn;(%k(Px9T8)`7+r;q;>?gk&M(&fTI`x(WR=FmUQmfXV)8?PN0FbAhTixU+q~*b-n$4lYeplo
+FW3cE(H_^!qi2vOl)od1BQI4m<&*3>JFdWAMWfvve^Oh%p8yJF73KhAF6$_o?>4~ry?fcqDjA>y?rT1
+Swmp*5w=Lr`LL@GWv^q{ErH(mVR3xE&Zk`^+e=l3iws~~vplYDusf%7R^58M2v67(<aJPjJC!X4wp_F
+Ul64kf!s42CYIsi9)tuDb`HMAk4tly+6N0+HABUnjGgt$yMx!c5!5emYdfn}c$*0c{U{mS|BkPKZN4?
+p$-#k0kb;kCa7e{C6&D4JL{3rcpV!t_hd8*zlIz4zL*s)ziUPT>{rTbD3-;v(0>fPAyP|^PvnRigiuU
+nHx3gWr(hU)0HzF2s?YQf`G3m&fok5@h(ubS|9)q=-q3m&H}c$^9zf4g`OcvL1H^hiOxFy5Sccx;GTs
+Hmxn|Ae##ECAa+*(nX(RhigPI8;=-UEF-(|7$M#wq$8v`3KC?vJzoP>%E{Tqu;|v%`2F&fnV69UN0wN
+C@JuK+<R&3wcq0h)+FKlpS14+FNQxpU&O`-@nQk1v5UZZUJ09X+QP950auo}nRPmYf8=S_#WFD7lX+Z
+{s|2%vL*+zL5BswiScG;i8R#KG-x02T_@zSeiXkXaHGo%1mK30H)eb5+RtZ-UgFpx@x`Q>rJhWgfGvt
+CeL8N{uC$Rv`=lM12I_Q+LF6j7koX%<XCv|0zJwb7u2HQAC@$q_H@N!TGB$>mM#NtAkAxQ(2?RoksQI
+r~N%`^@8t=W@DO^UGGJno0ri|W<{YkY6&M}l<(bPy<r4;gB((>nsLRJ}Ifho>*^im$QLwEzy7U)BI_8
+)(8bvK*Jme=k8&yCZCJ(?zfAY^vLF4%(dtTTP^7Lk7htgZ0T2$G<N@v1zZ!{TLnRH^<56WRCRNWpiSU
+I=tMT<69nRF^fXy`D~gj?W!{<S!nwse2yxVMCDkjtk5P;PCMrDkFsClz%cgcOe6r}3EEU@L72Ya_A(l
+w*?ctWmdX4Q6xsMXA@A?R5dVlxtAjlCfempFt>mYpvlpi)$G_MrJZy>b_T3r2jsC^Ip2MDcp?+MPy*+
+te*ZcPeZ0MsYv|Ys|9>{icG$fvmo*kVWoc+>q6mdfDU;7M`i_{u7^lEU4o{>2_$93x-q7MG#$&+3^7?
+XKkCNvU86Hu{!4zP^!ByRG+;SprdRVJ{mE(2AiSPg(BNP%FtS-;|ndV3l%OZDjMIlUz#8Xa|&WbXsLd
+Kxd#(l$=2n<U8;|1_1?*kZ7XGRa-ue*ye>eyeI`Nirof<5m1QA%cY7l7*M?@5$c4OSBX^OmxenYTSU_
+Js?p8se1N%x<CM9_`7RuGidA+bGBX?2O$jEax_k4Z;jDSb&pPC$_mz4ZGqq?`4S`*7z02hp>I{ZNaSE
+MN~*~~8;xY_5fkJL#An#XN^28GMzfIl?Om2zpL&X}5`CrZk{0gJ6-$1svjd_EAM=bwwP`JZ5wJz2MDL
+UOYA!SN#?yB3Q&+S3o53n<yO`hJ%NV3G%XX+bi*n!A{Vmsuc$krl0YXuRzFuh3F$rOsRLP{?6|WMfy2
+rd5`op95rpUkCcIC0%ml{n~4bSt`)2?9;rC!sCA%6C0X6c~AnFwN5Tm-+`FOBBZSBH}c?lnI4dm)&JT
+!QJV_db%=$1Y~DwA8yD2Q6fOQ+ngfq<8q^Ol@cOd&igQy;{09`#1kSPsiT-ChvFm!&<y2+IA2q4*3!_
+7xpvP!feOaDyZXBXFrS&k4!O<_xSbUZWl(?hov+f9FQU~9vU1o2jron+3#H+kgwbXsuSbo`mIwl{(bi
+=<_2p0;Zp-J?=UlKkLHx^LMEYmWhU2nstX7Vp8b9IMWUDtV0Guww?I7)0pfA;77TXSWtx6F=K2?bB*1
+rNsX8nkKQ#E}Ml?<4vJV;iiy?FI4%K0ZogVGr8dZL}*MLCR5OEx$blQWB>PWe5@OCYtFWdY{7BDz}%M
+QoGub1sWPv!U@-S7Lxix-D4j}I;mUj!W~8$a7MfY%4_NAI2;U7X(yf`!58_2KK$@!{VNkM9O#r=D~4l
+Pjy3yKNqJ{eZ_`cmj_l2KZ06*X!A%H5!TWvh(ZC!|#6G9X|YZXSjp^b)p9b=m+GEzq9-6!|%T9SoT!C
+;SE3xDsi^DodK|W_moWUi~IHW4}04WYdL>2I(zwi`2FMEsElt*JbAfZmK91=(b4(o_S2`2zuz9}L#mh
+LfNmU!dwnu7<1^H>%mobkAr#4C9-}i4pQ)}Pjsvm1Bkv|Lt(EF%iv^7^RjM&+-w%y##GqZ;OUk}}>&z
+Z*)erW?38uNhzd(ak?DEJ`H3ch1uM!iEJ(%WF9)0BiA!;#R?gb8?B{wYfn@FXs{@#i(jm>~`-Ll$|wZ
+jOM`YE5w(1^(UUi70S6a~gzu~UO?=xofa_A$U^<-kFew_5qQLY*W{4W85RIPyD4-?iZ@Dz8M1F%g)8e
+2x~e6gJ6A5N~hdVp^^szyPl{Pm#>BdS|`q5W`=Kpv(zTgmpPzmRH^0nk{%oVlC6tI3Mmtzup4G58n*W
+*d>94K3tJ0sn7bb_u=j~8mdvLnBjfW@v;g4!dZR8empk?TAd4&oaX5ytw1-N#AT)KCXlI~K|c=w!fP=
+DygA>;>5f(N<NQm_Gp^&?s58&=Puiiv^Fb%iOj;YX9FY-YO#=Dkkp@{q!s}=RX%HGFK>Bl@f+}BpqT_
+oYzl@#A3ck4?{l<IIZz9o=`;()AF|3rvz-p7w!n6GROH%snhls-4`1IiXXSDO>Qa-%~0e>xE$ZZv{m+
+<L1ZZIpwx1i`2RIx;EbA%_Q5UiA)v<cA`oB*(DeSP_9nwSz)Nc?ej3`5*Dw)QIilw?dDh|}N|{6pp^_
+US+xiszNG^?Tf6M4j*M_K^3~!iuwoS+S^(NIIlL@F38ITz``kz(WlfB%csaUt2LxDf~ka(?{xZ9?v3i
+7_{2-_F8)W#HK5%{h!ez8>=sKy6_u-3DOMtdbPHv+oU%LtQTnWsoiFi&jSHdM57xEDQvVxC`JY{VGz2
+)dX#;XSK~8$^8Yi0b^{AslWM(ut7JY8tlBd+4%hP07GJakK1e1j;fGD?j&c_h-c&bNCdnre&fxj}j@{
+~cQnfT$k*hFBAj&fjff)w~)PM3F9;ef3lWd00&^bEGX~y~EzUw2{;+$gF?OWoTJ&lf&p69V*%}i~A55
+lU;1UyH4rJwnNd9K-!v5Lm9bC5u$?HKG%Yw(}O$#c#UOgAZ?Y%<ttsF?E4%r8yvsgP<Ck;fF(5#*rNG
+{X=_f)nk$><NEqfG{Dj=j-9=%VvpF8u0DNRqaEdK5gsRkg-QACQzDg^sORro_`hLq#Of0xssb#{sC1R
+LHL+j7`hr;z?%y}7@L0cu~JP1G|nrge5E$(%kn09j?CWZQmCnFHEU{hhNp89{Sf*3mN`hP=JRfTazTr
+w(Hv?cwJ_t)w6b2FHdrn_;X<7>8{*Y8uHsIBt4u{L!ud5{PJ+Z>AU$P06!p)blvNiJrBh=YBB-yTD4{
+0g8v0aRyl3$~JCc#5Y|u@yIOCh5eBE#@%CrdTYv@VIwtpy9nM&=QXRm4UT)RQz)NMxAS+8^S%Li8O`S
+1BPGVleyliH+bCUlfmHl3xZ*1aRO=-fF(i^u~l+A98af2+6pXj7f1?b$<6pT0F6U<IdDhiX~t^j+`hA
+y~n!*jg{<5ua}#SLFv7kfCDBr)@@cZJ^58Z1%g)k3B%o{(klSun7X5*|_)<7hhp?3TmK=$PA*=g!b(3
+yI)0R=zT++!th8@Scuw01lB3`urFi*>fG>mB(G8~+>on4wZv0&RXvVw^5Rom<V&c-cOHyzh9C;o$4DU
+DLI+UIRFxT3zAn&RX5=hj>7zY7_W%ZAVp+Fr`q#w8i~=`0OMpdC<e{cF_KMZvx!SC=M#LS*KRy;SJCL
+tFz!~U<IuZkb<Vw39VrCxM&~KOU{o_9^*q8^#o|!LfE&P3#JGlDf%_3dWaCX+R0Qz!Tux0C@ZVlG*Wy
+NmwCAeD^@lDHar6?!3UqRKiZVP|}qW2c+qbc+NkcL5noeZnmHU_o7;TuEr`{o_)&Z4lb(b{|%;ryUM2
+Buob+ImK#ZmXFq^z}xC%{&XCf!b^ACy!%*7-XjOJlO>UCQ6#fc3vMav^Nr?w6Bcy-Vg>F*P@BqB3gVZ
+F-$3YHj`jsYzfU(1UmBMqb%r1d4SggYxBD72r)*2NJ~09HrX|9PpV_EZGBFs=ozP@(BR!ZT0874hj&Y
+%$WnGNRnfh}zLKrymzO2_lHmgA^WK1A|6Hwp9d^&4(B?;9?3c3P_?|w>ghb5Qqz#QrSWB;8gSL+M6p1
+l?G)=mu1KM@GU`sBKA<V<?Ns-HD=$y|&*%4Y-zW5$S2*$Dw01;H~)&gsH&d-c-(Qi>AFh`vv0@WGM=R
+yYcN7Yb+<p}Un7J@FhKJ`_FdWvaE18c{uizbe`hBW>_s6+bYz)-z?;LNT)Iu<B%npKe>YB~w9$hQ9Jd
+b*1iTSj>=z<MZtr{O_j2q3DKE8Oy>Vf%CdDM@x{Q;d_5A&zW$xR_kp6cz}}J6U8W%Yt+yv5wxs5Sw!B
+UUQJ@y(=~DkqDe~C?N41)MAxOsvM3h)TFz%{btv(BgSm8sP*&a23t*fiu-6zwCxOrDi{#PxaxJ9b(zK
+*fp`LlDdn-yMb&<E4b5%VMeIJ_5r<s>|Ktn3D);ru_+WFbj~);l8-#$D{q2eQfneUn61l>;tmd7-4~D
+^Vdf%_vtRbqx(|~!rp$(Jk;z!+=HKz;u=g*Xb503H*Pc~X5nu~SqVch2N4V>PZ?go}b&8RvSzTnsj2n
+<4dUwY_M==Of_m4)R4uZ(HQ@#L9}4SiD#f1Z+P;R!kT%JONq&UZ6)uS>4|*B3=fY23cmC-=*6q`E!(I
+<%#?wyCTVOp(6ep4u$Nn8KS1>8-YbMiMkW(tzynBU-C>sYeugrwSBL)DgTzn^t8gs;N~#^l;E{?dSR|
+pPi+xcQVi>rj93oCAe8{#Q16)u`7Nb+KbIy($ZO43LD<tZQK01o9&ugQCvmWd07RIG>CSbgDZ1pB~}Z
+b<Z=iAwnES6yumHADxKqbm3WxpCGM|qmhvK`9R2mwaMXhT%bbg|LCc0^*!?cu3+SzTZ4J(-ALgwVp_p
+%*8KDK$)$GH-%?hUH+sd9a^6piKY{5z*GAMI`>saG(>2{L?OQILREssv$iF&y#DbNtb0GJN-g(jvVdI
+n=bc4dq1=V_Lhw7)$JP(J$dNvH}e-$M36=jM>GKX$gQT)*mbT;+vHikVtnrXEbAL)$o8MA7?eoUs0jw
+upIBF-HHBYsFCQn5uNBwTxVKhi3a!*mEt|L<4S?-0)zcS-^H8Tl2hmZ4;fQGFcRXvm(J)PM+vq8+M4a
+)26fP^+&>KB|Iv386h7H)7W{L1n%F{;E_!0Eq^C0UJbOCBWnX3!SPuU6!2|~J_kY_-ZwO^zx2&@TQ+N
+mc;--6va&Fx6nTu`Vxx*pdSbbpJMIrnq(~F9UmGilj|2_F_+~GlO>k>Ut;bScuer#dq!s@;+~F>|HnO
+!On<V>rIXFK&{CRYKc=7G3_n*@*fXc|?!Zq*>7;6m45ZBLj{H05s-RT+Hv$=-`VLyL+=1CoFC<?F*uU
+%-<SzD3du!f)!>O~OR`n@?kd9h-o0At+_F(t1J3;QO2FKcea0Yseyc3hz&D1iBDZ^+EC-n{`cf<Ta1Z
+AHX!Djua(QuuejC^|_+QUE#RqCxd@Qs@S^$~GqV2Qf5%U-lT>Eybt9(n`BFHR<4S1RX?(&o14QPlCa6
+>i{qq3*OVo@gYW9gxQMc){!!1EMh5fnpAN*FEP#B630?uZtLsO48CU^WfP;csF-~yX6WHnA?X<n4p<p
+d?q^BGP`kapQP+M~cKHUM@j{ePc>&N><z-0+@6kje|25udAC1a+E*ZDF>VUa}dND<vx1!&-hssB&96=
+7tpy!2(VXvS%G3-MCrpf2)_#b(JrSN;2;rBp6HY{K<!^D6;7x9;o#0?>w$u1BCVWfH*aj_8}icrN7C8
+4S?U!>*^4cGtoI~`}08^<d)2@LpFdCHWQFN6>+%S03@?@h-t<g*7caOg|x5#{z_`fRjz?Bq`#J<_FF9
+MJg^OS=jXK;&dL*+hM+h|;NDauzB{!O6X%VnMMZn_ns`u!A$wnq7F4Q(IyHS==BfEt^$GM^W4{>d5+3
+F(B<4a${d*FtWzczrwF*n!HODe9BhjkNMImSP!6Ib`~njL+BS)E5+#MrV4n$FW9fxuZ3$WSc_grExVd
+FteYc>6l!ojc7dYgtD(vxac|hEE6a{VFU;BwwQ!~&PK>A8mWMJ$Y0cFg{h?+C0N|hhrn*L^46~w`4~{
+aU054N!i|pv6`I*F6rRb!X{BA1sIeSN4n0^jw_5th!AGI0e^kh?o(x|}v#x>g-T!3C+aXeTmP<pLyyQ
+<B;24?azc&YGgLrNzXGZC{Y_H_a>7eQmeSYT9j7g_bZZ#7~uVT|$Y%RkDW?uUD2bgw|e$-T`lKT>dJL
+S!_eEq^qEZ-~Wc)Yl`}PuWsno{|DCl<Tiuvl12$q^wiVaO9q7%9(sI5O!E0;Zu4iEECb!o;|-#Y$n>E
+jUp0;Zzul_K2BLqy;7Hj-%g2{Q)1?okn}9Ngm0hMSzjWPb30{3KG=&aPVE#T(ZOhoBI?*h^eWu53C!(
+zJ(4vucKhy>nFY#dsbtUQy@6sRu<Y5)R+sv>r8H6!N$%}ORESjGI2EOV-xGQtjT`!Zbr0Xoa%d=l5}w
+kHWej6M4(g5acL{~IuX8Z$t6Uf=)xmzJT*8+bh?Pk>i5E$y!OCK64R+Le%K1oP;9Vb+z|!D3QOnS|Ud
+KVxVd%_EY|XbJSQs8r8aCM$x(GIqal%$w>xSy+>?@m@Wy`N@q)zm<vXOd&a~ipuI;`$PFb<};8yYhvo
+Q6&{F-hfmi=@B_ID;CKM*|-^lw%W@NMz*Wv<LxBgIrcKhL0FKz(}LHY6SEag`#<$)7@>y$WyoCwSWOF
+LW`0UYIZ*M?M13?(NNw{&^_j$P~a;YeAsd}k{PjP-K75zp}Es((B?N!CdP(0r!wL|kdr1GoHM67HS3B
+aw>!_VdE7KI`i(Pdj-ClHke{w;G{1%@)<-FX4kyrSL8mr|qc>S2vD7?l9a1qkOo&vzcD>V`qzXjmjLu
+ao?N{gTz|eenmEb16?c@0wn!_d4of9Ke6P*c%@24*plHuBv_V3PbR2f^PaJ7s9#;-TGsm<sjDJ!-4T}
+rM%(6b~F317YVGD)bflBQ0l<t=1@3~-ow+ZdlTiw@48A06olse6X4aD=}r{-XloXA;ib{M5D|$v9{ba
+jzeBEkllxn9x{$J>SUEt!+N)%fnp)H*;)3_#9}-H7Z`I3|__<`vyi1{$c2KcoBp$rKi=ce>z6VakFJ&
+w+288ieAGZht#Dva@Hw%kZn-GTZ=?Oi1bG%{fm)3$*LrG7vE+nmOi994c8crG5A0OgEU$<xMn|1rbYF
+TWie^oy-AnO&0I6<&^g}ziF7Fg8kHyKDUK(E$%i<D31yWGQMsgfLGtd3<?<=hn(<w1m_w#Qv1H;;SE#
+;>{s7X84k*-$%Esf9Vm*|e$6oUc!eBK|yjbEXeGwK6Brw7OPs^0@^-hv5l<M=S2HXox0#JAY9b=`y4Q
+{(P#&kZX&7^^MTM47g&9!ml&8x`9J~hM0?CC`9S4Q=IB>A1!laK>(P}baBZAq+N`#NG7L;NqYL$Du8X
+vgIo)e7`v4jnLhv(~1IBW<O+UQ0ZaZbFOek1VI(?o%Sgr<SeU6L5n@EIiG{BABlX*BpvNl2;6Kqv`BF
+eA-JQwK-dK^uaapN=Ue0lG>iFWFPiq+j?_$`10sI<$=>B`4mjbBW(WqVS7jk^gz3I+#wWXDYpj&YqBw
+Hb03iHa-p8#=+ItYma`Osg*%r3*sX%5uipf88)wS737#Aw(h_fGP8=!?l06GnYiba&BMPdkET+ATg*W
+p84~<4G!KpSm(%?8DPb@h%xvw&)>7?WhL_s#^{pf==c$h+yTsvn;Rf|O(*f4+?16{Pmp46%>2WdOK+B
+g^Ke1fetc>_bofc47H?pR(nLaZ>{U8{!LEL*FL_1OiK+%z(r;ePLF$P8G{3gz_00cg*LDTS^*BXti*v
+hSO6eQRq=v#E!>D_gQ_&+b^;9&O_!bp1)Kk4#%D*?5YPLXGI-$8DynkFJ_-MO?c#Ez#Jnulo0K50(5Z
+xYmqo0Q!*5O_ujzTUX!vNcsNk2I=1F_QNNSD4lHQ-p-T!*Bxw?VT95y64Zx}ziSDy(a!4@@h!+l)2{q
+&iv@-Hf)ulI>+z*Jbd5Hrf}`>1ctEvfG%u!2C%^5f!8DHj$cVV0$;C{;<z2T?ct6;W>TnGpGS)$rBwM
+dDic+sdv}v+-kYvN+I|t5Ra|3bhBDgcUrkkKmK&cDip*42v#?;1=#?v)a|MTCD8A03nI)lFs{`z0X(C
+cI1Uaqx4+5hcxW`0LJ`lEPo(x!hGGuoyIl%Bl(fcxoM-vgrGU5tzX_E%qlvE)czMBI_pv=WdzT|wBTn
+g(hiem@v)@9yk8tw#eW!IqCh^g?<ls$sAIO65BWV#cTB_6F8ypEXy&DJ>}QMdGTm@KF*9bB6|51au_a
+E9quyEy;)?tr5^?FJ=x<6jCWy=J?hCM?pKw=xSP!{G3$#oLGf2MT2Nw<P#j?ZjbZufO;J^kd0H4$K1O
+xSjia#TUQ>WRRSE{(J6yQ6RNKHHfa%mfVo5??gdh(x3<4?kzmTx(ziP^6xT`p&5|SW)uXHY0r>einWb
+4~<Z$Cbu?+yKP4qeM><;~@`ZY5Y(61bDsRA97Z}vEQZhWEsa-YRKRwQJLVSTemp`S4aopbUoW6yshJh
+wt|Xit)4I!{UpGhL#6mqY4a)oIRo-2gdGu8hsqhs9&DZ&Ojtp8WGlo1lVzu0fcVeycunVb3zU$OiEzR
+m2|;skdA3zX#FQKK=V^wq=2!Tfm<Er|qT#)|g96XpLC}?pIQJ=Y4>DmpiP8M%GBAD7{E~+O|RRRVTuj
+^NQ5)!9G6VQGCGnCCrWK9c9}=MKCU=oY|ar$v5#AP8!w`Qvg~3>GWWlTlPUChZYfHmM{TvMs-B``Wny
+74SUjh`*e-L*KX7@V$FMX@EB9u$P8QJ0~!5q+^PdssI@n1-I?b78m~N|KLKsd@MV<{*X$k1x2Ix54CY
+vtEnd=QT3G+y4BJ7x1b`hgy`LRfSOfdZel0(TfCdu)cHH!SHgJp-Hp!P+)y1R*h;M5XP8z_DpXASEMp
+~iDs+9Do;iIQ}!^clPS|~^<_AhBlRBodYNE+A~$5XA_n@I5OVW0%VKOCLqm0(@5inp@|iu=blJrgU5z
+pC!f4pw{m(JsUus&yIY^YDpNW@eWeKG_@YK61*4Q>res`)F_Y&;_KtQFW<@PdE#=RjO8PuPj$nd?%i!
+V@b|EeEi)<>%HMBrv?kk?CnCiRl~J(>g_&xytn)P>UuM`-uK__J>30%Wj(_KRgDK!+k5!%yN`ZBaXQr
+nA3__C9vcN~1FfI|ulF7|P>I1cPeJJV+jrz`BnCk(hOk8uI2gz)w`>%bY>TFttRtDn7+^7-Q}PR7b63
+_;L_K%=(Sh<4M!{QAcbkWnG4bN$WgVR1{sp~2M+)(4m7e62$#Rhb2c#hDvehlKrICG)ePJhwy8I;fRw
+x%M*%lrtn9OsRg?am9Gdjnlf8@z~m0VM`c}(%EK|M$~H77u3ZGi!)=mUX)Q5yL9n}f4+ho$kY!G}+uI
+F=}KxR1QJ!cKJPM}ecM#ynTrTUk*X-Yld*JkfCXqc17p2M;c3v$1MX4XJ8z4D}Ipq_(u*Mc9(g>+n4)
+xRAmnAk;}XAxRRiKES1wQVXml&*y~yqo;0XQCxTU+@q%t_a1%!*ewW?>fDLI`yukRZ#3A~K&@4ImR#q
+bQ6rcxvuT`FyrNWC0JSJnlNN=R(h}afN{q3E==D~hKCqzGX16>s<O}vZL5q`Z^ufc$`h-W*uxyKHOhH
+0EZ^S|>KfvQ4_*cDg1vHEV)-E{7B@`B(<VSnV@Q}!)5`x;%I`N}|cZ4m8WKebDdw9`WjqdZ_K2Q=L34
+5w~&878|z^0+srrRoAVC_ZQwMY~g%mFhZxhtiHOI-I`visig!|;ePU*;J9ot!|5L}kflRik$OhuOpxO
+zJ+*6j2lA_Z3iblZQaU&IH>LMeWB^FRM#Exs5i7Gdk1y3}Z;a`jIRZuyQ$;xUG2<PeBj^DhDE9K=*Zg
+gK`iZcn_o3pc@Dr%xc4)QI*+Kd0VIN-jgT8L4+9yyh|No!PZwcZ_pOSSLyIID=%yk=?f@cf@HM9F<>w
+_sEmcMh0SP}n^eRi)YIG`tq>l|tx~B`H|cy{w@Y{^_zwrsS?yejCP5YcnjK=D+gr7b(wsCaxQ?UiT;=
+M*C$Pz@%G_6FLQLAREssJ*$f1zVqO@5Ds&6r&`-|^2=-S4^!HLBy6}vXemdG{hHKvM^ud;Dzs(sh@0g
+Di^3NCg-yKHC=gY(nlgR`Uak-S;i3$$+rTJjvCy^7;yI<GKmfW>0!nOE;yrv44jG;BgrSeQb=DXBhX`
+HeiHC2t~x1dGeRlG~f6=(;MhoPFCW%w=niYz0g+s(Q4Qme4+@g{~(_w#)OkZ{D1qT^zm$ZJHoreo7ZM
+hcWqrZO>C-#FTRnm8oxj8>tg*j-W$p8j`>pZ8>zdO!f-}!FMVL07fPMVwjtv5Tv#FEgeqKl>~)|eJZh
+XPjojogzXZUT7zp48HxVPXNisCZD#S!R>F@t&N<6ObZ4>cl4*;85S|}P6nmMd2_UwtE`yLu4Wn&xG*6
+tTN-Ca<Au9O`nuTz~KoK%yoG-_Gz*AY447&G<%(sZps8Q-t0TW9LP3gElW>=BWjLP_0%|@8Qu<{_ZD8
+szoV7`jg`$Op1UhX2WHS>D4j|7_YEf`<39uSnf9%SoSi{~23v`ofgzNFyxg>pY?JcxUT3C%c}<pfw3O
+fPT>Qo%h{8rh$ch7G;ARZ-HOi?)KLD^HVoxPXD;bH*!x5D}t#;_I?nK3k}f3wb}Ts}|tLjq{5iM}yxT
+{-3DA*KvsH)}CO{5C!zlRreq7n7y;FPJ0w|cTvIHa6J$YR_)p5<$tvp*4YOa60&C1qR(2+_CbW2Cgr3
+^$8>4J98c3bpot|=thf3cx8bDxn|8|s^W=w)SWDyb3iQNilNMK!qN3vLFSU$ChRv=T5t7_ciLeWpM`~
+RjG26UnaetA+U8f=QLQFs$Y7x9w)(zaJ(QXoz?RalEX}G8Il92={x{Q{kGi3U_Iae_8=6QVwo$czlzI
+o?npA5|OTVN&?BZU&dE49}K8VKHAyxe{&S4yUsa5e&IW0Ke|DV1fB)@nIx-vmao(9vPJI(@%JqQ*=B;
+^-jmB``Ej2PtpRT%u*<g3=|%xT&X_kJ&K{;-E}0(oJ_<bbg&aFr5;*AO4?TvyTsYzh)ge=(7%%7=YxZ
+^CL!@_Xs*QOgayxssjpi(H-_E=|$+Y9Ry0rYec6o<Ael>n*<XoZ{bxk94Cp{bBp-}U`9!mCr9!QyHs>
+c<2PB}v-xeLRsN+MBSSwVUK+ergFn6%KLntKxBxa{Qc?#KogbY=!<{FOo<uOMJ3P=7Cy?Ra&_Oyqt+6
+m@b>H&lAm2=5q^!|4Ywhd4*+Z`Bdf$`3T9%|yi)X+Ve{nwv2hc!klN?uLM&MFz@4#s8nZtB%8r>`R@0
+A!q@m?gsp8JkxOK+ertFdllZ55kprRqSJ(k`s5`I3+)QJW#KUrt()fd^*JSsW5caiF~yaG^6LAVG~`>
+YPRFxp9GJOcA$X?I1>|qrl?n!s_Woux9pQ$?O}`=L$<rjvn=r1n>j;#nLndTr^9{B>^hZ6n@Kk?2Nj-
+21_Tlmku4CW>qjaw{n(2DTLvme6ve&F8BBWg?GVJy$=+)?Z~|5!1JLwDz54YrB43``T(J?2ooc+Jd0;
+joE7mDbD*iBDAPs20;Y4x`Hj%WNrlH;IBb~TSS)nqrvWk<2T<}QMmi%a8XYwLupbQvyAP2##f+oaGOj
+Y*B+B}7s{>3i<D_QA!p0T<?k;grRj}>AhQ${lh^aY%JZ~{slf8jc@ExL6VEouZF)$pinvd058IC6HtZ
+0d)AZRy<1MQM>jK;79i>Vuf8EuvJj96oC<5QCy%S6oW$(}+hwW~RSb_C?VL7BQqbHz`py)=;pV^9TDk
+Vh|%4v$})`^!X8aRXZP+B}Q)blM<-tB`DWKWoQjodF89-?%Z$aD7ycZ=Q}<$H`x1=K0@d*AX4UFdGC#
+e4wcglo8De^DT5*uZV6EM6H(C$NQh5ILc%@gQ2}fY&*PI0d|I6969?UVBH6@)tI=EPud@W+|D}4?fA%
+vjA&!%NjTPM;(ZtLi|PzHgjQ>Dy6M8j-OTT6b#1s-*M`Ba*#`qc8V$!6?WY#24Ar!Uxi@C<@{VtMb8X
+Xiv`KsLf|f{^$97f|8h7*PGT0aKNv!2Ome_oDwu$?8marBC5??%@EO{eEF`iMiPXYrVtYCG%FL7;ms{
+23kvg=9Dsf=+jLciBO7;&0>AZ?0~fWC*rm9ISdXfdH$D^@P!hSzP2T@^(jcWH6mewb~TwiHIz@%W(|2
+&_P2Xln|VjuC8?5^Ah^uD?&xwN^wOKMig|8QM^6wN*^R(q<S9zYZS-pcNxkXg!o@>XqpZFPwCLPZwe?
+EG>Is^Xv|rX7}4CcGz(Gs4)J3z5Bw~O1?OJ;e)$-ejL#4@NTEy>3v)^1pMT~-rnxVzO?sYh`-u~V%S3
+HYaAl1GbY0(Vi!XY$F2;FlzX>vU7I_@?bkOVWfaNLz8?+UlA^rv4>#W_T_WK0InfWQ!u@F13y8sK4}`
+aclW=o@qq`g(aKw;xx>66Fxs15JyL|3s+WsbLbGBS2HH*krU1_B4PmQ}1v&c33vVZ)dNIs|e(z;}31=
+euQ-wo9HbvKF(y$VGylNVAbMUN^<Bb#v@V2BMpjxy8?n<G%Jf-Se&kuVtk`VC;V#y`Fh(L9@!&VY{k_
+Vu&Fv&IVeX*@N<k5U~ySX#U521Z`vzTRP&*^nQ##u;Op?=T$unw9C%>6Wyoz_Av(34^Rtg{IR4TD_iX
+&G0>CAFUo}^caO61y=P7f{Rfvq3m3RJ5tLRmMU8;!|T$UUlHhdl*Bx4UT||SO4^qs8JCd$900pUM~&g
+D548FbU2Ji98>RJKF3<-Guk%n5G^5v-%TdWwF-j{-=1&H*fokFD!$+X!{YcsE?tCepT3vY?-Z=tiUm7
+b@qY4?oukIU;s(L_J8EA>3tdzsE!&h%XDP+CCs7{8EminS?7&XvadR2pUa9N{bd3(K1#q(cYI~C{Ir*
+$eGpZx4poLsGi>hqsmRLAqRT6}iwws@KPv719RUmm?Yb%&>RHfgA=>_qjEmpLuT=XL0yE1m5t<og<zI
+#0J)1vcv37Qv+27i>TvM{vrJVI<4WI0M8bN*d@r8y6~_Md_u)9xw6m$BN`HpUvnsrx~M@Rb*y6Q8&w@
+>vC515bndMi{@eat-uw@CHzN8U-$~S0S-+9lvkPDm~%iequcd(_$rxp#M!rT8>)d-;vCs0qbeT(0h|F
+NlFqWcNJbcjqjWJb5Gg;Ui!$Q-f#|>@fpKNgo5x4Zh^VM-rQz)>SP%=HC!Z6%Hav7I0v0>SZ&B=HX3N
+KuDDK9H4A0IQC|qMSwHaX0#T>3Qr9Z-)_9*V=kJt*sUjr1Lf-NxL%2fGXlhZr7RPaWFr_|Cv6~s8Q<4
+FrBcBrQk018iaFdYzwsj!oxs^O{+BUKz~1G7W*76sX-+haU(QBe`<Q@VQGl_KVK@z6Z3;JZ`2b>dWtk
+|%&9;|fL<5L=xI=z`M6T*jCyOug5x`p{oBO00*ViErP^eB%MJUIrjFWhCKDns|Y7zk|_$q(ylyF~8}D
+MFG+~DQ2u)d15Xid87k{`_P&KhFkiPZVPBsfz*p6kZ<%9G(RMi?vnBx770>j&*W&)Z#V>7gXlNx{}wW
+6c9n^?iY*vE6GzH5%3>LM4}t2(S3L+&KiZ17=#mAgn~}QgU=%fBaYiR-<%TLvRLH|Dxsi83N|?S?NL@
+FV<MA^iYr+#6pcx0uEn_9D;@@OWse_b^sE>z^DSEghQ!Cjpd{ymYA0+s$2~X~uOp^*T!DQ&JSdX4@3k
+761-EK+rWuax^gu5c=ui|WOdUHbO3VPmpr5k}`kh@_-;Yp_2s@f^y8%Lk;<K0mO6?g?kkLQwSr{lP!$
+vtEIW4{>5n}2pNrhlgx<6&D&Umwu-8~lDV?)>`$mh{8+$#8i3v(fB6Lew$;^KyXd(tv2A>^gxG)knt)
+$bfXPq{{0@{-{E&-sE^42K2-4N4mh+E>S0l9?z?6lrJPMQQxTN7;NZy@};V)+Hqw315h1$l{3%M*j&X
+WsedX5wIs<1jE%S+D^ri_`A*I=9xw{i7XW6Bh5~8PNkbp4kTW`oN3^deI3AajIE6zwsxFh@V%rLnxtW
+(+zL`gtM;S)W)QLx|vr5@)8@;j^y4th(i&<aocI^P_JyE`yAWQ+U9gHn18ox|%mKfI#&yo1dpC&{tW3
+)pvL=n%6iVG+aLnJG+4bviFG()6q%|UbWhjz<G%W?`-{HL9Wgyq9klf!C(Er8<i)e`NB0W?M5`iDo#x
+I=~Rs&m?lmc;CmC6Dat8l;NXrC!FRbI9r>JR4!ulez^4aygG@*jLhR1ke>;oF6Ai#+jonAojUwgVnj|
+v#B$+x`|Ddd}BpLWm3=bX`)kH2^b@o>Li|ADYrN&oBK(QeN60>ho!z20qR;Zt!K_NSb1Aq5wxn(;1s3
+gH*}9lTFNm;1&-V8dFRpY-Wu9IU*qL;(Pkv7iZQ^UqvvsQse)dyGZm|dPVy4<X^Y)tbjeL5%5)PZkyf
+K`&4EpZY<bs^F`^NGxq#H^ia=>K6x$(cni2ep4&x_$>B4B=Xq@!XW(-(FL_dC(5HXCE<$;eMi+oVj^d
+wE)nVM15saZpyTAd=Ok990_HE)f9z+H%9&<+ovHRdS)CwBg9uWap!;N{xp;r8XUyz-=<#)Zy^aAw?qk
+Z7m6HDA-z{aKg%pC$ABhKw9!+Ni@csKl_b1XQ0>234|ROMD63?hWhIrdDtW3Sago5m{fpS9w3l=RB)@
+&C#ocbvA*tSdQoE1U0$;OO!fFL|85tNzvIhFkqKWOjgULG8jsV0G|mUAd~-IBGyoMY}Z5$F{T*U>4a_
+;D}5TboMox@i)&y9byNV-l~3Oso{U}}Ui>82=vDXek_EItE0dh&OYUV}hb&M-q`X>T(p5vyJo>$=or&
+GEsmCVT=w=Skte!x#MKVJ@h;l@r@)P6f%jY<q<G={KF>PEV>ggB)(mP!2Mz5$sFlGnMr;b{?!p;Iq2+
+s=l&kmoTp1t6J$oKDmx`~Tf8M10vRt!Ud?`!oPr)(kd0#xV%bFIXr{uTM6NO96_g0z?v;%qHdG>)KtT
+<qQ7qHbCy1a?J3-(?^e6iy6cv7zj3=^q{xU?X#&tk3AzY}#)e2(#a}njjD!v)|K23j*WccemQAkD>Z*
+V|_6b#MlpAUk`e}wpQva-e&#^3)GER94t_3D%A>T!s8}13})r_VYmqmgH_!h274U;bzje9&g5BQK!R(
+MmST0O^bVBqFNZQH4nj=?VerQ6InbQpiS6-dfakz)#wd0MWgQ?lYj(>Rf2aPs9chNJb~i#GI%b*^+0^
+xww?->gAjL$^m#%=bA{I{eJS9?fiBEQb%eV%Y&FE!{vN&GQpq=-=Y@WEi8B7iPfN#t-Xp7)j0{gB`*)
+V32=I*|HatYG~a>q<u42Zj1RX!+Sf$PKRrM8MS>c8qkF(*cR|7V3(*eLGHDI(%5rZ;orG4>a)wusDKE
+d%$X=k-#hC%qOS`Ys)%QMIIf)$_3Usq-9JJ#{4Z4l!H47Q{*8hzyXpmlI^am&^J5w&VH4?~!w|x8D(h
+__SZs3LAlyOX8mIf2Qil1kzU^woYN&NezJdD;5J5(%Eo`{+uN?%3C4?kCRJ^e>_1i5nfbIHM01dyw+X
+<80zzXU<HJ)ukg&d#D)1Z__~xSXcC6^3QFK0ZP-ot1Yb{Ffa;ye|JT<}{=fbQCSR76eYg?QI9H!<)JG
+e(X2ooOKN_9WH*W+LhQ?Tm)N1!V$d4+H2j>ChR-1THNSCZ}4JwFw3~ZcQ4;HA7#Ljc5qFKE+0xQFG8R
+r|T>CK;`B}k0r1hmE!l`}qLXi+UxgGNq_CdHyfiLgWuQ=j0w>%mjO|L@P?>TzFdyo2L6`+eQvQzOJ@%
+-^?;rz?xyc}U@NptJ|IbIOhKuO0Wh4|{+;_(t371*+fJVe2_Q7`%diUYd4iB~2VrEs-Y<)G-uG6Co`9
+U4cdz5MIvp36a*^t=-&Q>j6=uxo34Xt1kwrVI)sH(X}W;la0d)B?{)648j?(qtk$?RHSaBhbVimC2qC
+Z)ohV1T;FG_p4>$<!V3Yxd5)(4BYm;)bXkA>!(}N|3&#Mp>Ca#pp7Qm-nr95wVHz-!9--HaVLd(oFxF
+uM>*yghVT_*refLd~f4Qws`)wx$UxGE99Dd&&Kz)Z-xWr)nY8$I>V;o)K9DO`=3GvsJtH)o{OsUSIZW
+pWUWwjX{%;#EfQHFE0rW*!XypzdftMv&ys;mhaCF4E%LxzJs0d@tMy7=sOD2;z~Y1D`OeY&mz6NwGTd
+hc+J&@!be|Nb)r3X5gmK4VaJTII02XQW}L*>qfqF|zDj1(ub{gXkzDPLgahNmVyFOl_8yWR>CkbK=HY
+SR9{?o3($Z_Q5ooGnb!nToaO>{VUPU@^;{i>g4Tlbl=QBcR$+g1<s4=(<-N|qbOfuFxBNkpY`f&eA~V
+;52IjF*qWTYTBGgp?hY|)m4U_v-g4={KoK)rD*L~;KeuG#YagE)rj?+t=b7@_&*xgt<^tz(wX~g7R`_
+E-r>{=G=7!3|y@^lqm8T}UGtyUbL}cTRiRL9;B(dEf`q9tHEuZ}2`42L<DM5-AbWER6l$6@c1V-VY@~
+qC8paEinY!DMT@j{_}mOyY=Ui0!8C$2O4b&dgQ7TZ<6Jw@xJb{95AqGql3IsK>}L?^qS<$C4sZ_LhB|
+JgD|g2g=!X+z_MCKVg3;@#q<<<DgqU^GVjdF2LTyGw9*V&!P#c-PP=sEIq3yOP*ECE`q}E>*N1)r37Y
+E%A;=l!m6;3zIz`((mE(O?-2cL^qg@0BzJ`3>Q~p?k@|f4cnT0HckH@3Q@$KAmj<;*ylo@&8S4`#%|q
+Q<?}%2%o`%ojx|XXS?JrR2wtR|1rw&{h-SlHAEAk%%8Vd~LNkb_7oH#K&IjezphaIz+tODP)JLnwQqp
+&AhmIM)Oj+I8CY2X<fC-6LxIV`d7L?bI%wds=wTp9?&&cy~aEZxdF&+{!5CX-qCKnY5X@lsOe2IL`<{
+H!V^47}hA_#7khBK#fJGuo3cts|y_+Z}Jy{A)h@Zq~1;t2kmjL%k7E|N)lnertd<-}kqA`V02Bh5Q5K
+it`^fI9Ec;oFeY1uqHqAqhyWbd}nx953QJIa=3|`9qGs2K;Bsbgo0gQCS&x*({oR3^n5kisVzB!-Nt)
+(8Z97cLf+lvw#^8@?c!%^JT?RCAq3BAqrwL=eeMlR(ECo!t7tY<V>~t)wKBBH{K~%V=`z@3uvb+FKcF
+5mzHk>nV~M!E|Wz+vTo!0#pmHHEGPL*pCzw`ur@|n^Mboj>qHTzbGR4wbF-t=5~~rfHz{ah_7*ly>>I
+7v0>94jd*9CC0b@`hRz^jMjaJ@X<E^qXDz9RW(hu50KL2cL=Bw;3h7WoUJ4uHy==Ah~tLqLOvX#eGK^
+~u~1C9iNpYY*~RW(pE;6ZHGAYP}GX_6eX-1I;*!hf6R%4Ky;G2a*zv^%og=y71-)!=+|bbfaDs*A<lu
+mc$%y>ty2@T_mboF<=xkr6%ra&o+`>tz%PAMGai!q1FYx39J=@uJ1zYEk@U-+c9f{(KAuLZaw^ocxj+
+<~v2=TpOq$Oo^>%|F>5_4x7vix8}!~5g)%b2!{1m>3+$+zwE+>{`j!t(G|&1j#rG=dXr`kcg2S-p5Mf
+`Wwhg$l`cqPHSaYxgzbOW2@EKH{EpQ;%AjnM%w8Owg~-gw(-y9acXV4STi1#G+AvWqmCXy+>SIhHEI}
+j_eBv!!vytaNwRO+DW9`>{cHD-bdCi*ZHsn(wqrAaEU$*QAC|ft|2^b3$mIp|K-bOI6cO&n$oYrDrym
+y>9(qIDcz$-L!3(br|sHMpTav@x2yab2}`d>q_!rA)c=TzMB`z3iOO?HZp{C$w8%t~3S-Wf!tzM5TI+
+&I>q@DZ>YO_dn!W{Tqc;s!>uYYcS{mafff*s7^?SRXRpIX3Z`6^lFIqmo)eEyQP5%nW>HUyYA9_n|xd
+e!LuOw^v~f&3kS9f&&^hcgo1P32cDAn!91)ZGUcM$-;@1@oRJCCQAVYN0JyRNiojLWFO5!ZU8MzZHC^
+gc<!BFs+;|s>-!d)o94@k%{~H#Zj6$pxlenGtB`U~T}xzVX?lT;iRFiPnG236kVU`E%V>MPwT%`%OOs
+nxizO9Z6!~;HX*yJ~?F;&GK!f!rTzVCtqJjf)v8zPaWK=!8Og-J-inaoYAgEgBUisI1CB@CQ*IkVfuC
+?Y!upUv&-0HYATJWe1Qv;6Y(K9P^q>caqn_U?0a1T27ru}=<PJ^`NevB@wRnYfHyVr(#Sh%q<Bb%AYe
+b<<h>Gr*yUH&J+fA;i0&3(H^A?ye9d;%IdeCt|_z4u}7$w#B*4Zm4y|9cRj{$<Y%CDG*jq@7N$30NDY
+THr8PO#8fU|Jdn!Tg$qXgdt_x!Hb-{ej8lR2v!7gt6@fREl5Rzwqg*n_nCc=r~1^eFI7nqFfScUr=I=
+DIYm*AuQ_x|oZaf|kgkcTtFskjlbFaI#(kXQc@g#&u&O45u${*QvzE!H-OdA_7j=?WPC-F(zaA$L|Hr
+o~Mu8;QkSGH2=RBos7Zw*UlZt%Lf72jnsEFsoAjyE!J`mR)kaR<)yh5i>6PWBzI9F`Sizin)__`SC1t
+M$>zw!Jzu<b1-Mwf*Jnyh_eVO-3Xlqa)}R>elq$ra`{vM{r&_E|^Ll}`B9Pe24FTY?Elp<kyK`&w(<9
+nDFG7=Fk&kbR2n#U@a}u-POh&H*=ZfzI0YNt=2&z%Sj-vQ(EVai;1KPqVTDb*CY^`3p?-Js~yW9mDIp
+2Y@x6P0<u2$ohEJ-R;vMG}6(Y>ibljQ+@Lae%upJ$##g!RDPX|^C>KLT0yAdi!I)q%7LG3N6nLUNjw}
+jV1K`^5;)fL`-I3XTDs(Rrv@{W6Fw^wPm^t35p1jR&am!V4A~~!)?+XWy}FlgGYp`avvsz1FJ9eUOfG
+~A8f6+q-2P&9?R-eDjZ2{JG?xtDs$JF5mMQ&8GG+H7&ZZL=W^P6^rLV_vtX-Qz(K$XSfzTirHzja>ug
+)z59_)CIx3xZwwyhM7j1!;;2Xf%dT{fVA2O8dsq>Me~0CgQ~qJeFg&2`oK{70UuZeN1bWjz-9rZ=rJu
+f|V{AzA|AKh}^0<2`soV2VNdKDZX9+D_1vh~q5s-=wN9?>MoEOyoprvFsZhew$r}R4}HLw^|lCnrIO|
+a<ZmkEjee;a*5SoG<I&3(=2+IW>dVTs_gxshS#{W*rBYJ^>q`nSGiGOwu&*wibCNhN{b@p{9TfgN=sb
+W=R5D<H5b7P-+b%ao?86q7}9L|$uTLV5nZHj2QpDVo#6kgZjCqH6gPOwk!1GXDm63VtaE)8n<({|X#3
+e7c32A4etagTHchSVrDLml4H2rp#Y?C5OtPJHTnM3fqk++Gian}}HidA|%byaUPO5l5*P*JbtNqRZ6f
+3D4U;&3*$%=pCN~JnDpad_u$%{|UNwwB)B<@WW<u@6|{-)FLbRrq*Qn`A#z))o5AX+-9(Wj<ratRC(P
+hU%r^RYA5kv9~*q%)tYZrzgmb2TFH6@##G?>Ouk`GCc9zECcHu`y?oQGOIAtgrBpFgrrw;LTN`U135J
+K^^SIKHzkWQ&P;cPql8M_(9p9$Hl~vE%i|uUnULl#f;J`<A}6!wT8mVq`#AYw_``1-i%)4a@bdm(5PC
+P$n?>V1-*Yz(VFIx;%Zrt2MBBdnZN9WFlqsX(?M$R`<y(?8c2%|o+aoE1}q<yAah_s^FP2+nEYyg9$$
+~AaTM=G@xT`6%;E(Dw{v5jCwTq3z*2f`>Gf3KS<AxMIJ9GwvFQU|;7}MY1|;9`@R*jVHLj4ci{TX@Ta
+UBA4+Q~-!b{U&wyN;t%3$*ig;gjUp6<3-GD<}=LRtM74IA$1diN@*twtkcgD%77-o)qN!oGQdD>mLS$
+<sH?Yd)@=Ue;vdHSe?pR~E0x=V>_?m!0THxj>mmcxkE7#`qj1-fLYJl-|yS2QL;mF#Pd6(X6<(o7LtW
+id*97WFaRj-Un{OyMSQNTgD(F_iMxPs>{pA1V<}Lt`CsBirc_4n`5q!-}JE82N`Qfd4*%9NlUbM#Mvb
+2s);Qt*U#VAqs51m|Fn5>)K$5xodPXcs4zHeZH<drBJR5Jr-V+Ndkv~!z<{e>j<DiLfVHv=WzlWBL!r
+Hyk(fr(alO7*@uM|PM(XvWzj;?N4ztf`k!NV2YGM~bRuK>#eKrwh5opF5=oQdtB&dg|iIWWQnl0TP`6
+HN<SRJ}G*uH^h&mXyVCc7?!U3b*(%2W-_SQgVA`6v0=68lqp$>C!bzgxl5P?1)($#PvJGYZs%)%tcqP
+<*t_R~J}6r<h1Aiv}R^aY9^IGhx*bbdV*}42^X`wc{1-!N!oZjyE+8&^SkX1{pL>)XZ`hq9gnLh^N~e
+2p!b|71Z8JcWyCizFV_L6}!EDBn~r)`Mty+6q|xi;+;4g{|i|5DqEcCO)ko5qU8qPdSDsX*3M$LY2LU
+%svxI`GLldaDkOD91{&V`o%$wG5B{(Y$d+WEP6tjg?HRO93KBCRs2v?d<u&<#E<pyEVVYhLmEwZ#si+
+gSNnA>ZH#`iWlOGhO1JDdLG>*D}W{-AY%}usV%X*A!Y#T{e$2PN}g+dnjnwn%!rLuC3gcBBPR4hacrs
+i04o4f`OY5-eSeIy8!8kvLG?*e!lRN%7d!3Z<+nG%LpAlufIWunS*wo=7qnNL!pQk{2G*t~fzJGnwnn
+Z5&WJpY7G`GB@>I>4zDES!$Gxu>4X97Xnb<Q&Il%RP3r(q^$gV8vD31r3+c;_9|cC-L0fS2*Fr)Mzg(
++M20=1J^C^F-NVjT+P_CdW`P*R0LRim1e9rsB=ekalmBso37hEc0$>4Ye(`DrP>%{HCiW8(h;_os$PW
+>P&JknKY1ujawoJCJRGCThYK_^gj8;N1#e12GKQbn=WgAYb-Ago(hxLW7x3b12~MU(hu9N}{RjwSLAp
+1GEN6WNqmok?20ELV$vN#)f;br1H0)gK@o8I<1Hp6q$H;MoVnXqhiB`{c6RK^wSbLjgyDN0pKFtcv-k
+RrEGIn=@MVXG8F@u+PfwrD!SB-4)`A*>PFy&hFTp#}0vWsp&gq2lo1gWQe=1nTnEII(iv4Dlz7bv#Tv
+B57ZWczA@smnWtHLn|gcI@5SNJE>BNbz)m^p@{j@iz21jitW3KlV7!h_aAT<Up<@P{ct=csJcBR<Lx0
+TG&O>Ye~Nu<zydL9z*dwt!|CXJ8Gde?#uQ*{2GdrF-n=B*4$S;BM9GOGVJhre>DUZ{7F~ouI1te=7_e
+TVD@af`Q{Cz&(A*zN751pS>V6YG(tJYLGbzuWdg|Z!QtWiqw|X<Mc*3m(CPJx5x<KNQ|8NJqVLMML1p
++Y$23%{(8x9GiVc=GggJPaStWGcJCIb)aj?KUYZ6v~j?dG~sT*`3M@^lOZi={Wp0IK!)u?hiZG5n;Zu
+nT%#Hh7IRlw2%D(!l2EawnK8;X&C1$l^SbrHInk|}l8zDNO|xLQq6>&pj?{!H9B;VBMH@V?2rxMP{xE
+%n-TZ{|pg&eGCXt=AtrOmVi}((*1se8Sr9g$H6bN@7`K7iK-yG%R8~giARK)Q(`|TfBNuCD$FN(<)Qr
+`hBjG%;ln91F+BOWNjWJ+qg3rSw8KMROG*7G~JO3^_=t`VET+uxoQybBPXin0c%+J4TykL_h0+^>e$O
+?$3M5sl~?@vPiHkEH1iqDESghNEE}KDm5NbIo~`!+>?tRK@@{k9Ri4;AJ{<99S}vf74F#kOc1kjxgS%
+Qiou}90AJn5r60pUi;BhSk1`Ji{VfxPM)O8oP3=5p;GDK_v=>hL$G98ElC=^n=oAccy(5>mNvlm)+r2
+vWwF<+F{(^LEFH%X#RhNSZ(sbxCfKm_<W8gS_7slkJf{QL|o%_{9O^QzO5fbFKmT(!o@l==kOB1>N~<
+|Cb#v&HeFVdk)Y=yX3|ow(DvT8+GO)@QyAkEwx@TfK$XG`k!8IaAX})vGQRhS>NrQwcpJx&U(>Sn}yq
++h)w2j1|Z=dD^*hy%HO-GH{vUru9%a?O=<WsegqLASKImDz_VfX5>MaoYi++M)WRG(Q`;G%X5m%GtEK
+H9;}|snR&kxsG+{!NtE?r_l`F_X9j_p_k0@>Seh4JO$3%-hrrT6V7U$g{(|<WWKN_Jx>kxG<bT@upf}
+xRKbqZz<564;U=V711HG=3m%GXJmq0LUqv>^8USF}qPiQgKA>$|F-jB3zuO1b%ZOzp~qPXRt`1K3PoU
+JXwWXnB149z_a-?bwxnL^y3K8$8WYaB&3Al+dUeZS9d7)DoW-Xe6|p)Bl_Io`;^sA@Zy9##8#rZLKNT
+k~sdAnT5=Jv^O1$r6W?N7?jRLyKVv9a=9CUhKlT`G&SQv(p!K`d@LRH^ShmWJYpkCdYGZ2k|2K>A0}#
+xLC!up+(F(bvD69Q_FR21k$Cg0v@P90z*01ySDv8<7qvjP6|QGm%KSsUZvdvtR|Zh54OchxLel1VB)_
+O{*q?u)m08gc)4lk%f0u?OUI6+nY87)tNr9__xMoZV73tzwK$__Hveb!jruoN7_020o;;>LO{o$C!*z
+(+BU&>+es!xI&amkOCpOx?5ZmA#ChL#v`Z?YM`0ph!`PJ>O1D11L1}rZt^6IY2+SzpK*^s|76#3(iKl
+wRMs|py^RWiq;apkaOcs_)}%j%6+Zslj_Up&VId*p9xZ(Nj13h%#LDQwXK#<$?n+&j*z3Z2@}|6yS%9
+7opZ%e6IjWm=YOSfwzT6=qKa`D6hqa@FJ&ahR3MLOg-ODTH}0>iYKW4l6#Uxse>B70Gl|DHH52-$cQX
+#{sEn9lENUmv0~T6qVrxrZ1Yv+>G}!A2u{693hsf?=agpSL#@da=l?H9&)ZPLvw32{-dUb`E}riLRi7
+Ep&6CESQ=bp->ebf1tJj`-h4#y6l^vmfRV6J<jdKW#IUZ?!cGQFO5Z%sKWT}|^$b(f7o^Zep~039B;T
+^rXM>o+F3ENd-$vd;W56<-IN;_Ie!~gqibb$tL!2xtyw!t3WnYD9tT^{-LBjJ^>0Q^PTQcP$&!%+ZE5
+M}SS42K11}_o%F-{L1JySv4=(*P;I$$1+;2V<0Xk-bd>X_C~4sRxM@H1ICO^?wctUAt%3ahi9wgzWtZ
+NA~lcChrhs$3e!^UYcvh?VgXPcU?3Lo2wdCBm<WG2d#Z#H<5!Cs+8LzbtI)ksUs-*^G%<VE)>He;dt=
+3=O%iBY20L49{!q)M_=-h_eg!IGMytI?UJRcIVDC0Qv`BK8)+506So$9Uii==WF%J+V=5dr(8F-Wy_p
+Tn=-*LcV+TKv`AcQT&qKxYs}q{=Dy{)iKC9v3b^nnz*OI`4>=AZmO=MG&nq*u^i@3Zkcx+Zx~4?ddT1
+%}Yd~dI)yyS{$^x%KNknvA&g>q%!&}Bsc)Xm=C}u6kG^+!wB}LJaDh6*>{IIX$q5=>bJr14(jdEas{T
+il;<p|bXXn^gwX-=2x>#73dOIitte#DvRK4VgvKIW92B;$DU$pEdx?lp!3adQ}i7Un?~=@Swa>wh{xY
+cyT+B@+w^Y643wQU^+k_v6rHBZqf%@f(9tZReNUdcjM^4ZOI($)~-;v_CJ2`etM$-QL*HKZ)v?;$?J!
+M2^%_cy6bQcJ74=wC-Rqu=#SigaJ??ZFE*yS&ErC&1_w8Hg<K_L`+F^zTUDXmCZupJj*xxHA$;Fg60a
+Qa8|G5l?<VQm{CM(gRPA|(8#siK&(X%7O+GcvdDy@sfSeu+ycn`TMI;>n~YqA!g141ztE(fzl=7A!=0
+T+yWgXi3C1@l$k8q>Cd+HS6H&^cwo(0xR87`rOx2`zw~MCBg71ckAYz6@=ae^e5u5iuHQUI%KoJA(lU
+&CzUdjB{(p&UGk+}}DW8bPpWy8{|;Z51F<eawD(sIkH+Z6fYNcFje1+8h0qlT3VRugpaROtxcw4N}Ak
+u%nb6@4OP2O!ej*L2P<_|cnNpw}$g9eml?2wDn&4<%p314X$~mJLO59i`<+*&mJAJM}7}sBcQ$BF&rQ
+YteO=I?5JM=Z&5yieM*4s#m~`@d6!1N0fdVH+I!pu_lSGuBw<uX09xk6&sQ1HjL>38h0kgY<Ocsy9r7
+=8}eNi^?;uJzfem91QY-O00;o{l@>>)n)@t@0RRBX0ssIY0001RX>c!Jc4cm4Z*nhabZu-kY-wUIUte
+i%X>?y-E^v8el22>gFc8J>`V@ms3Am;`?4^)H3;A<sA%}%Mlp-X1>>VOYW+cbieEN=T?`}hUku`cV@2
+6*Lt<}^1_b(VNCkj1ya%}ko&L(Uj2FOQ*!_$xb;SqFE?zBoHHq=pLGsRHDUKFJr87M)EiTWVDIf6rI;
+xLNNd4!qr5%7&t7Nga*i&JQUR+pK43%2Zs(c^$2Cz#RuFD-UdCL(iWU~J+m3SvImti;h<?$Mt<Sq-s8
+4h85X$5BQ(a2#jhOtn(?Z?@RLlx&aRl@im)8IF1II|)H`a0hY3aHEHlX=5`Lpt>CETa_76&d=g{x&BR
+etF+{)I*tPv(@l9|GzPZO)}`pDjAq?e(9$a<iLinxP$RU)Q%1HvZ8H|T-8tKJO3CecsZ?v~+w#D+FNj
+-bA#{@@Dz`ni_L%ol{<)U_6VBGZeAXS7>LwFEvYy_rAeV|###ry=qId8|U-@(edO^FqoEb~uBaE&TTp
+#^`pr|iq`meaj=P$V}j`~~u)yYr!4^T@31QY-O00;o{l@>>jmU58HNdN#@Qvm=W0001RX>c!Jc4cm4Z
+*nhabZu-kY-wUIV{dMAbYX6Eb1ras&An@z8#j_J_+7t(quE(WXH+)tMVixnR+dVaZe2XOxNUcPy~)fZ
+nXV+0m6@z6Y0muj1LOto8vv3lb@x8I{q$oMBLYAG2m~Sm0pu#rw!PneTOG<n9{=`RFWK(1yzB+bB1;c
+t{9FC|aW6`)lOoA>k9&t*5@u2STbh({9;C&+drg_5%#+>v-o1-=&wGF9z0G#9`dj_=U-x?IfA@EXG(8
+J9K^}yv;Qj9^XoY#JFg=6mB|G*1{h_Kb4z`bbdAv`9FphdRNxA8L`uFG0pFjKN{^PpZWOp5;Npz-p+G
+okG<eFdA$euy!%Ij*Wf-uYTI4sl8XLUPgs_#j3D7dB%HK_OS+ebYr(Q7zU)t159t@A9*&vx-mn(X4Uc
+vmLnXUSee)_eNC*O2vUGP~EWpQG|Ljn_eXc9ZO)?4~#y3@1ZR<rfvCcPQE_nsU9}VY^hr{E*A5)>d&I
+@51<zX(tSJivMhP9p~klDir5Q=xOI|jcNE+Lw!i2-Gj2XjC)OC#Xy<S1m8#DhnSg9|Nd_eHO-Ua)vkQ
+hpWQxkdTfZ#R#|>`mefe*aiM+`y<lgC!BqAgGvsh#n2^n#=^@Py<yjGL)p|&?-4O<&q#BWU*UZF&#RF
+4qe7jcypoGq<f2rzTXXRO(t&Y^(mj0&qBC8w7R=rh{#!4cKG7h3s?cAa6o?@l9Q_H!x$__i!xC^$bx;
+qVrru@3Wpt?J2lAYHanu>x_DI>*qT%094RZn!LmZw}DkzTP{M9V;pl~QY_zbM?+_n?T=)mfNkh0<_mt
+29`jWv1E(cdVA{=;=D#ZmXhI*Ujwb$$C?sE!D!gjD=>qXlvE2=_2(YJow{-?wNOLzdFQ4sg(NJHpnmU
+Qv3fz?Y)ODKfZb0i?X=T(@9IHIP9zaV4r7UTvWZ)h4MqF+bX_!)XCT#SSYe>TyAuoaatHDcWP{>O4oe
+%v1}(tt8!;Jd#~2JOz&{L2mkTyA3ZYbYtvf2{+2#LMrZ}!tpTm8zN!VN;RpZrFbc}xQSrc9*otYrYJ9
+WeXSLe?HZi8kVcje#DS+1SbPPoA!daPBGp?l9jLXLE#=flCwJzFGJ6^xBH`thwRkt-qhkcrafvUvdxu
+$1%w%;o@4H;FFNn^tdverjx1e|65wWIupUY55gZ$lUW)!oBCV>+vP?%vSprEFK-r5zu{R|jRp>S;aft
+`3=})d&7A{Xx%dTL$g5^+NGW;rYr|gd}yg=8@B2RsHuaO95?Eg}T;)w?}p}RbRfk9c%jSWn52wlpU67
+d{zy?*DmU+hTg-!-G6kd6|FS?!B!reY-E?!4HSP@3%(gqPFFJ*jq}oI@H;ARy-Aq0^T#{P_?=p@C;Zz
+ZZJCy$px7w0O&R!67M7p)eyiZY+1UfFrXD<d_w?h>KU_TcMtPj2wUH73%Rp_MHS3KW>2@^xVYc1I(ru
+(J{7b|7*_r0OrFH8s_R3fLdm5+;9&3brNw*?+kPXt7yyJ~qVsN~UYJbt4`l`x$56>QX#;Y2fzhk%_sA
+;bUtCE0fzVE;~wehvwpE;|U$D^M2q73e;E$%SJm8|{)Ra(pT^q`7OrtP`q?8T0dJB#yeCZxFu5H)cjT
+B+`muj{&_>qCtH5v-F=X&UM<MZJxEt>xlxqMk}_;BFImhuXfXBDd4^%qg8JMzy2<3#ps5mR0^vZ9y5I
+!6R&q{dp&jZB9Khd%PYYvrfP3u}gNlINy}pl<WBEr=NNiqViwWzUk9WN1JY2t3AS)7Kw7Db#rHVmI;y
+7z@A=0g^q+R<H}oFC3&HPoPqL{y=HbRNT-GL;+T-q7+>zP8~>E&dA5tQL(wflHaB_#y6hJ{5Vi1_X)2
+B|*4aiiI7bW&GkCp+|MpO?o=3%_zo#8ffXEWrySGtS)o96NtK2u$pe+zbYj5<YmXU`|1&?~VhF+Y;a`
+TY!vO7c|PHHw!XWQ1wo9t|(e5c|p$h8ft^hjLlUY46+cc$d$<SgmZdcDTCNm1%eVS~s@?6upzerAqTX
+2j`Chbonh*nX}$^cB*jW+hO@R<P5nSoNgtz?<y)M2lY|z->L-q>7&T#^6njIyPHY>EcN7!dKoqZnZcT
+C#FU8s`~M^89fRUHdS#K)Sqwf!i7G-sa)tT{UaF2)Sj2@g0!u=_pvVC3yPlVc6?JKOp}p5E(n7rFnO|
+tcI3Y4J<D_?^q|9%<-Z&s##%<dqHbHNO{H0NvJ{CmAE>ptk!NiFMWcw#sN6p)U2SQ5e%hwXEIW&XJhh
+IX9Mx9Dv2p>ktXC0}-l;flTkNjB)^uOjGoNm|bANnub%%c0^A$wdz6!Mm>v$J_J{x>9I8ozulBG%r3n
+dQmzBhQx6SP;G>N*eho8F)b(y5)IZJ<{-&<oPF68v(rJ;K89pTffMA7NqiPhny7_p>nmX8ctwj5}Bu-
+<<__dpoVsNOZqepY1ZAy8gIHiq@Xs-fmPayUbaL#B*!Yw7gd#Amv?maNdyn>>P7!pi1}ls%39ztE;`U
+Wfpz@2OHH@bhli4BF&apw|SjqQI>s$*{j<lfvKC-$Aj^$wj-+J`MT2HHjBJ$Y?3I7wKuarlqW~K%ABY
+UR<(uOtIO7LH)`3}%qq*br>fU9cdE=a8SXHCUW4l&A3q~Fcjv?}H~%0XVVtISlHR6rMeZLGwxYg5nsx
+IYO`!dHYSGhmYVT&FLk)R+bx3lIQ_)i%L9ekmk50iIdjAz`_L#BXsW$TL<{#7=s)p`i{)+bQ-p*04Mr
+rY|QVjK9U5l$il0Y?0HSnq(A>r6n8j@psLaUpOve2e=9pT`FE;-dlQ+1hLDz#?@*4q^K_OuInvhnWOl
+&v0Vsuq}LSN8S$lzy(|rrp-N^eORVbb06KOP-v;_&SbGj?rm;s!h3$t{hP!U0ZQwp;h6P<H{0af3m4g
+p?gH~^)+qF5Yinq<v*$2Ht@R2RW(It(A^!-wm>W4Pl@6#(U{Y1e$^P;>>OEeEh)3$4;yz6dt1)8FH2T
+>4^80N8{weEbz(feV&=bMG-zp^X#d#qA|7g~`w9;1Wp`(>xP$P21sie&-f=Ge-(*2A+&jx9)_Nmvs)(
+C(r_V^bvdHcp%0HU0#!dV^kx&}Pzo*HsjU9hQb8UuP_rB+ku9Mr~o&le<F5LQ>U9+RYI|!~@rIFDtF2
+g+8m-y@J_^pa`@~aku+Az-2U>DsXHHMk4OSTpBf!uYLM8<s4j`3NU2T7_!<DNYKj}5NZ;FSEAy&z4q8
+y%zE8AGNO8lStaPXOBnsaGv;9Br~{6RqjQpI&>U*t(iWG{rL3EuGy#L6G%T7wYLkW;TBM>8G>9E{a#l
+PFJ7jnWY>WRIfty9+o<kR2;%fonNo!c%!cX=<|_PavMil^ZfU{d%D3&j}*8PQnRZ6E%)Dj`|Zum%{Mo
+rZ?b&-?O;Bie|uZu+vAL&+#I2vefvfcRR4MNx-C;&ul)sv{eJ&jjco{T)s$Zz<D|m3#9eIs^yYQVis>
+sA*%e1vsqo+4*W*-#*<Q(CBN=+IR(bX%-l-M&@UK;E4>6Q~t%vZ}$7-wH?^CtXA3Z)o69!de@;FiT$5
+D!HkY63*_&8Cv{9P-vqcpYtJWdjwz;C?xMp`OvlamzLNecg%F-38bqFTNuh=QHc06`U>zU`O>Ew-ApQ
+7iAd$GN49;vzgsP$p$P55GRTcTb1-g0lR(WbpW0El;46^$GFKq7%{*Z;#_wN{Jq)@C%DU@3LinoWd^$
+KyQ_LKS5IF$>oWTDw=F}Vz|l^T|L}?R84rHc84TLzkar(D36O1{k@A?3i9}b<S5fH2~Vktph!;1$5L6
+{rzuWL$#Pxj?6?y|X%=4AdBn%*w1a<~;)Kj8gSR}T*6*bILnQ$5iN?b0g#0Rkb)Fser>M5u;M5RCr|@
+y9%(W9D7$qlq6eZUu2u}1kIw3Ccb{U`O$tsD{s5rG!R>}I1pAe^2mYtkKt>aHnXfNOnBqv$k3{K#OC-
+9>a`0)w+<OF_t0<ZIfPEwp2@XaawNuw(fGxD!G+dZz6k&<n&j(f@kZ!&kaqjD%sTvcgz9mBt0S?Ni2S
+ufZ%m9*ZBNovv_!eL+CFw$kqAiR96T%lf4nwx&wIEeGd_mA<aIXcmu?TOv1Ix9FqQY6Y1xjjylp4ibV
+%l-sKm8<qou!+@JA5Kw}C$_>}a6(z_vOB1oT~@E&Q&f$M{|{UGr}m5!n}trZIH9oiX^`wXO>B^zQlEF
+}aS?~7_tGYsbHa@Ke9{5BgO7CDf;j0$sewCfhLw76`Mdlv!k^Tq#v3_8azdRRPKevhDU0fM>zBw5=c}
+@#b>UY^1lM`x!2O`#7PzZX?YMyd-*|vkKWbh+Dv9_f^ydF(-gni(Ao~4Kl+WwYdtxs8BQ8~id%g?8@=
+)Eu={;-$M0$1_;Nu=QRHJ`=^jMV7!#qpv@(u6~<u`e}l7%bC9ZI^*-nGqQqmvc5WCgiH$y$O$(HeA@v
+ej(_QB*^Ym)xe`Rqk9>PR9uw_s%M%=by`@!EViEs^2Hd)UiA&R0EEesserw^)on++uu$B1(QX-Ya@{l
+O%XFFzOp)Ec-VCdbd-c@g+6?^D<1chI~t^q9`~+vMUQmIy#DiX@1fciSIKQdqFSns<tw(1|F5IrZ;~?
+J77riQ(FMgZ9SFOs<Nn`FRQqo`^Vgx@?>}vS^$a+_UoGKkSA3-^$;&rc6dzGc%?8?(ueZwfN41@`KQ{
+D^MA<YG6k(FQ&Tis7)W^46dwr|zn~LJMv?X~|7x}c`pDJ=_Ntz<BtE&{nkv3|ANB5#+os?d}8uRGhCc
+b^vNu-c19WgtsdI^ci^5|ixgr)lTo2q{gA3b{1EfZRt!sdA;PcQXo{%h~08sqxyj%L2}ov7wYlxiu?<
+LKU|rlBc)SWk-bB%A3`K4bgW$ow@ne@)C^)BE>SyZQv^n1s}7qyD=MOEf>@>naf^JLCxF^I1S;ueYt`
+?O#jtmv(d7sw%6z`B59R&5z3PGVPlakJ<xjen=KdeSfbO(NN#V^Y`vSrGOti?mcMDy9c^NexyiM{e#L
+Zeek&XQ5nSTFJ&0Df7Tvp`>Sy~@UwO>@Vlv}VtSdW{V4G6y-N2HKlhV7SHf}~>`G|L+G>_``q)Gbuc6
+TcZ`D+6gFOg>LzH9|NpL7L<wtzJU&W_(rP?(~L0>0P{B<ST15!~W(l2dQra<v|7lOErWl3t)s~yp(iC
+gDMWD#u-X_@S+z4m^T6k6fYP_!Z~%hZX%qJ-|lCJry_KNX*Wsu%Sr`Ic3vIo$+N42sIXbqA<8EVl_&W
+MW;`zz`owR-<f}enz1>ss1iSjPJ#Rpo-T)$**a%iK})O(Y;$`;eqD7UbS~)vrVIwRmo|v#J0^5xKsV2
+P9r23lS6q&UUiZG)(X6Oc`J#FztnHXRZqw18w0fp|E<yCzg6eJ=DzDDxQ?rr*L1Yzo8GHZRitnG)JM-
+@(7YBGfRAtP`5J0>g4t*~e%#aXtN!h*-&6k`_MbfM^)5cWf7ko)>c!iNYC4&WMvoh+0XC{nZ=YQ}fA#
+j_SzXs~Ff(-x>$(O{da9bYFJ52tUcdXx*}V6kpWc0Z^6}NXx4kz{-v7BOH|`I0vAWzym+KGas{Ct3_q
+O-p&6C%!d#^7(e!O_!d*1uYtB)^xFW<fUbJOH-Xqp^1O<weR&)$9d{#cW<;izxQP3m&PX-`%B{?#w<-
+hO=Yx_9yV^{XE~y!z0#gJGIB^lG@CJ$dos;(bdpF(l^=$-uBel;?v!`a7%oJAA4tee>?wtLLv&FPqLj
+ef9q7r#H`EU;Na2^7QG&+mB5<W3WE2+ZicVeo!Ueyn3ygGSfDi(7-;a2X@+T2lnZcA71H>Hm!ZAhxX}
+{_wU}e4b4qM-!~1-F`XCBKc4;Y?$ujW*Nfhh3g#K<(P#!9p4L1}h7J#}UOf5u>HS6T-GwV~&$<L|G#j
+Aqi@NUfXC3|f#qOVI<9SybOf~Nx5&da9R`Wq4hiYo|AhiSL6~n=~Db}tSHN@{<y?A-N+|bCteAJYC+B
+DY)oWIz>6l+(@v$naT<<Pr!wJ3JK|D?pNmC6CCZWhQuadz=mN&bhIEsa^M^Jaw%6dM<BKyyBV$j+O!@
+l5rs)&d`2y?#~;$ZR}AUG*v$XqG;{fAZ|2;+&Vk2z9p0VBpm+o<%UhNVkgsI;|SHJFS8_SZG(l$YSBh
+8kiqh12kam8o2Ow&RGGY^J&kNecqIvb*!JC>(b9&J-K*)q4lD&J(cTx(QGc3xtx{@Z7i?re}e74{&$}
+h)gSsOcTri;G0Hbq>ywK#sdA0}b{kGxE4>0P9O#=LH0b}N-wO!{!s;=&s@On67Y=xtD<7o>ANqs7@r<
+5?Ngg)T61Z@opW6z?gT8^T@*4U{dE$d;(l-v)lXb3k^rn`+gf1L#y59tC^`ki$-v(h0zCpi>8t5>X81
+L>$w$65-1}oWqCAb{T3~IkmKR-Y0s!)AfWq%3=w42&+I}yNz11&-^7UOvcRFSMr^}`7`E0Z*82Wcds3
+kQ6tbIKHK%&>(G{9gHeMH&<v)8b$XTz+kaE<PvlO>D+=)CWCJyBU`6<LWY-sed#9Cf{X;G);;YIT(gO
+mNd&ow7$262(>vgH#aC2>0xAM^m{W6vgyNiA72@U$Hq&3`jUD$Gz0SV-48$4;~LBvws3&7<h0aHf!Le
+oq9H+(GDx2#tLiD`GEsjUcE=NtDev_&jiCt&JPm`Kr91>8j*?bD&Lwo=g3TJ&mCOLocFC^9#F{1dG|E
+a8FrkR2y85)4)!QX1s**&WwPkLXXnK|Vy0uA+WCWm9T&A(9MKLs=OWd1ecPN0JO6(6QBPSC1G1<n1MW
+Z?;=O!uQr^;8)4taCHkz!DWM5@RRdHVTT^>)~Q>O>x+Mh1~fXHlRx`Z;8@a4z7cKyW?=Vz<r@G0hdU)
+>Ao&!xbUCJsM&;m=&j{2OEU~=UH?JQ7QI-r`bWz_32?Vn(S$w6=viEV$V^b;OjuEX+%(7W@9tp&wPvO
+nG&Qt*cdf8heCVyC(;7w_OlNRMn%6c*s%XBSg&Iv+{#A+B8SWe1THqDjLp<OvzB-a1Sw;X#~9=OIYC+
+3g4$NLD3PemR+%hekUk+ZVmbB@U`Z<7JNFXk_3(ss<1mUmgfEOi@c($QAF&ha3PK{;W-tPVAu;LItVS
+_BbGDQvP_4EKW&|Vk(<ctH@<P7~i(S1Bl<s@yT3bmvf2%d-X7?n|vm4V5NPbQ6I$o6)Mc<`(ub*?X$i
+^<&$2j&0V~tnZ#%b()HS<%s4W6$8m()i#mt;QHhaM8*eP|Ekr@d9dz{Yz9dLn6ljPpt!m7Ro%BpSQ?e
+B|J$=zQR!Z7x@$c46&|NN*{e8Sx+Eu*}d13uH~DM#-j$Z5l~1SVn#V?#*P%H0^xYEv+0PtEq6%K_S{Y
+XCg7}V+$k4GY|!Xu4U!yKww*^Q(qdbjQ#K8?F$1P8!^B5__AKrSdj{}aA54m%_Vf<faS(8Kx5>`N#Me
+TlDTUp_kyiEcncTyW?`1r1-o|^bm4&OV<hZSzz7F#Fa&&Ge*U1-;6ErOo10mltd@dAtxGRi05ahe4$!
+i*4;a;cu`d!>V`}p>OHpY2;9SCauTr+ikdR55Ec4{Bg{-47_E%tTUlhp}4H@~o;62?)^aVV_CBqgDP(
+P^$o41d8Z}-Z~tTxRHY?`Kl=PWd4ojtdu!wBkg{r!~4JAa|1>6}0#u+N=|ih#<Wq-6Z`fjH!eQrS6d*
+^$vM&&7xv@C#uAnvr?&QN#X-p9}dr*luY`Mgkrg1nh<vG8Qv13it(iY6>wN0>Qr805!2Sh=zG356y_Y
+u;<E*?~64IYcqi_oIwYl)sr8&G=8QNANq9+Gk+K@Et2PlG_`6uw@8|Q%t8o;7J+Et+%Enh4R)oqX6=5
++Rg0DO!rlp}ZSuKHr(QmJ&*u5ijPJ`JjLo(&gbV&MsH^$E4M5B@mf1^knBnffOwy&2@DDn3257J&{J0
+3<Jo%zOJCnXn;8sqsa$fS{pz7r2H#9rM%fq%ZpY*@beKDiu9FNV0`ii%rff1lre=-44)e?N|SMPXznq
+~WnH_%8z7Y>;A$yfjvE|i!vyT&VCUC`FAXo_H*z2ZGGl+c9(-uh|~+aqL3!S(BI`R7+q*5I*z!OCMWz
+v7@kNKnf|Su!eb5}Dy^QGNP*Z<VhDEja8?M-G9Awe+W`LF~>5L%g!}&lqE!@g8V2)+-iV7-z3a_8<pC
+jMCwf<!K7I*-KXpTiC!qiRC&P15Sct*!z>Me~nE3$?2+@{y#Z7XaZE6GykSvf0F$&Z2eipIZZu(7Q3F
+2i9ZMXJ&m#vyg$oLYBU9cdJS^B*JgtM?DX2m#GjpBn^s;6Zq4AnuJ-8vz|V#JEkm@3A-@i`%ZR$U1Y&
+1)#dG9cy$-6c?Xg(_ll3(Rc@VgIEp2ufFR!hkMB_Y^fNJkse|6;h`dN0cHxne%4edKPa;66G@(tSKaN
+|@;Q$3BVrw>}}s4tqYy=8~RsidjgGs{0j^s=R~-NTWeKzN69rg@%bC_jS5(-9Myj~xPwN9=1>ykr=RE
+R5xRjxDt991Gq)=1HJu+SI-r`q12Qs3oS5JMU63QAa&&J_eG+GfjO-VTTJ=KD3x3Auo3E&_#;EVjzN!
+$d|@dpD+kIVqV&6tj5N&FNaaUBpw5G%6Pl<xxlh%8UzDI`Rvk&2`r-&y|(o!h8s-cEbMjajuLvfO@uL
+ns{FCOiH-M#@6MUXM=)gqhdqiYws?0UDVC^-d<0Mf&NhREs?(J{DswIfe0l;r3Y!D7gp=375>hEmXzc
+Q@$(=AgdxK+*ISwA)@YZ0~${V$j9Jay}=p)?rCQg;Y7Ot=vgEy8u!EUO~)x_}`T+BDlpqiESM)=k!al
+j^P*nTTEbJO#;a<|5Wy_GvDq7!fBPKpDcw>(^CfxV5_L77~~zF~vM(^7rhB;n;9h<2xFbfn`rT~Wr7-
+|;0svnI0@3`x%0_y)e6xhnD2o@pa~Z?o@JW0!^_X!y6;u0pBpo|wK3h=80-cyMoVU=qY3K|N=dLq^Nf
+ylF(ZBVqU{kvWmZIU}Vm@7pLlfTzV3sUn)n3ZIP$=gip3MM?(F2QGy#CgQNsDI;B!GD<Rc3A}-luG*~
+4iKo3I8sOCA3kx_zUQ-ChxJ4nYp`(?_mW71MH5FSJYySEc+OY>_7J;>5Jxq4k23|^Q9#|NU>5?FD1>3
+Jwl-1%;{*cE?+fv@dtna}}{sVV9T0*zC!<Z|4A7ASLgtM%9&gugvM^KhsBrGXW-(gMATCA}~ssq*^4$
+A_b&T!^-mFcYNh90uL?l0xLxC}~4tI81*TD%RldXI(<g{)#G+B$@x&d>*T;K9o2UV2z=2z}G-tI!f{&
+!F1lG#hz3=Nq9Jt3ONQ4{VgBqsBJgRo7&dG&EKl+`M-?LnBTc1Z6325XaeXId71_WPBL`es{r9R`^x#
+D4dF8(RUn9B{0INaIW4_IF+GfiW$B7E?TAmf@nCgerKC2V32~Ukp*2iU=cqZhkz@i%!~@g_1!iJb3XY
+qoA^7&<~3%=yM4Ut&H)P@GK;{7^*g9&WQe|VgdhMat-e%Qb$KSC3kS>+Gm+4R2TbAG0NH5oOmH-sGHl
+@hmD(n$?&J8V!sKKEk0J+0v7-^978?6Qpi_G`ae@3&I$QfeKLJK*KSN}4{lM~PX7vYK%#3LN!1;)#TR
+#xc$g1{|C=Ky;X;z+o4?#IC@m{GtI-~)=!hSYV_yYuCxdhPKc+UeHW$(H+-lKibLz~D_y)p1gCNQ5EC
+@U|pPt9igEEh)ykR%|98~A8HIAV{1C}<LleCw+T74$ws78b~!ZX;X&DQ3cq?0-6JnDKi5J;i(HaGc*?
+ylC8{m_Q2$CcRuN`1d08yabq22>T2!Q?Owq^}TeK5p{bno#hFYpz{GV0J()K>$1w0Hi0km!6y2&_mIu
+uKE3C&HL$SBAO&W@)zKPnlzc8}WJG7^B1N2h;^N)N5pobKDko4bIp~O#pIbz}Q28Wsi0oMTu!BMbQ)R
+ELX!%kgMXY;hqpV=})W+NVTXj3(?vWQS?RGDT74ViF^hUgK9No}(*bOZbJ7^ty@nWc@XxOvW_h3bFIU
+qqtKID6$#2_U46-w(+cn;_Fy)RUb84@8*0#k?+%kPlJ&*C)rjH@K`mFfD2m-4Q~90&#*P4(g3^N)-$)
+9}I4okm@MXiZ7Aae`WELWF%jSeh^ff=_lQR1<MyI{m@kRE+q4*r*-uSZ-)VaO~8sA~>qBDhfMy6c?yI
+Bx_1f!YNgia#df0!0wBYcZ^*O@l{TD00=Czw+0g7%_E?3@VnAJv|QoyflcGM&wc`{nxUlnv=e+nf*o@
+JGE<TdhZ9r9w1GlrSv|E8mcvSwS<d+R(ni^yxQ+LjfRRmPd*6iLeB1=S4gUkS?jb_UANbM+Lv7#PMp+
+|pPIxhJrp}Kq-@Sj^I3ckBE*z-7NNbh~((FD8HH5Bx6wBUB#>b$U*%A;q+Boq2XsI7U2=#+VxKMh4+G
+@rO%E$JV1en=l!g03D3~=XEW3tl!#W{@Utiot)DIv(5^#x5v0_lmEOay@yE&<1mPQnc3`pF7$O#?V*$
+*zDB2XZ*u{lqblKtdNTnA1L149?U(0z<<L-lwI=+=&U!nW0lNs-Gy#vJ}9D3nkexBmYT;S+MtivW3bF
+_a_ds%q4W;fJK-EM^XAC5XO-CO+sY|VC5i13}7PA-XK5(5|OMn5^!;%Im6J4G&G#Ie{zHllhV28HW+c
+=V`w2=s{j{?efLr3g!Q^h=*nK$>JtIC40S?U_8)a7a<5BJxxM!ceJTii2Fy7q$e)jf4CWks;uW{AwXm
+V%_DaX@_emxJP9f!pXN3$~*uWp3@pWnhBYs?BmIHX}KXSCm^y$Y)R5BNE?Apkd`jKW5w_|>cb859eAa
+dAd7TAe0k8G?tGyY#T=0mI>A|r?89MOc9#?sn-!ut-rjfVKqRzMgOXX%+0{-ZO~7@KQ3#-^da2%8><W
+q*mIH4U<1{4X|RsL5f>h~Qrw#?0LRWY=e8<tK<gvIc*08ZjF3C#MlJmp}6(4Y+6cGv_knzUR+kvoKBn
+%)4JCp$i8rhjj`#Rc*TZvpooAef@0r7e@g<JN-42_p@25x^W|vzpx>Pb?}RH8*vW)i!{+-5c4%(*jvA
+F+7wLiU*bIbOA?ivt^|)?geeBL_&z-Pz`i`rgnn@b!|26;<_(8x0nZzOtrtOz2pKa$sfZZB3xZaMaZ~
+`eaFIh6z+}NGXFw(kLk-)Q*}!`NIiUGK@_i7SXH}p&E8xO`nlYs}J_1`R(Qh(>a4a3L5k%lLUKO7a=)
+{{C3bWsM;r!|elvp5NJz;oi;)wwb_Ex`GhqqyZCiwyYx+#q(E)+E2cT^^XAaF?FBF*-=B#do9p>UBM@
+fZqa4-ryUP*cYoj*^T43i541{0}4|2vFC|LM?%sTc{AIz(OH*g?+w35#OPOS^*VVs5MZrh1vkMA{571
+B>Wojgk@}D!1$b5C?I@8_>&sZUOW$J>epY$p&8+PD3FiqpoF8j(FElHVQTh9mjrM`f_*^FQh?}xuqU$
+uGqq135pV>nSC};T0U-N4yY;f65&R+yD#x2Y2>b{1<_|#~bqrP0jPCyc;PciyAR5k$VD9M57Kt4JpV<
+=Fn6k*~4`9j?@Wy`Q1$EpIJU|?0DKh#De@o@1sk*Sn;T>sIY?<)L{ngmLJWrf3j_$q=N0Ei0p$Z9?Uz
+4%CWQ2IwXd)5NM2uSICLNM|PYo^w3LWYi2HvnRoYB=GGhN4av#efKLeD1>;d^dr8S7inM)%I)^DJX_t
+Z3}32by47fLAt8V7MwU?Ojw&c@xL*vk|RW+S{pF%gbaPl1{)Ol}5mh98O|Ugo23h<`p7~NGfrIQDgSx
+<_Fk7HA<#7^qGg^irNZrB}}6zygMaHN!Dd^^rYQn(*cq{P@wL~lKXLx+>Qx#?R5YZ;-<rGSzZR@X3uf
+UOb382Q~it`ZN6|=D|FFy1-rtQ)-l}#cA3D-frz~{I<eo-kQ^{gbu5L7W<<0zYmkCm%k*+EH1zW!!^e
+OR2tFb>)suy${t2?-mW_Y>2fb*q1t{><A-Tpv7}$77=?G~8OJ~~z1N7V#!X64*W?L`frc6i{z;Jf^?6
+v6#9?sD4_kfQXo&r8)cn&!6dHWLJK7&mL&Sc8fy!-_CoZ%7R%+AXL;LQFf>Q=;X>Q=yT>Q=~b>K60!B
+ftX&qq|eC=0~tI<<a>O>`Zxhe%t`QWH{KwX@G_kn=67ZVcE<>=0BqGnJ}EjhZ~5{FiYrdgogXvVesE)
+@(oQpH@l%}XX(UCT$7=w9@9QzaoEbY-gqeX;-R;gFhbuC1LpTY!^HD&pkZQu4>U{yj$s-wzt<Qh9_?!
+qBbL%@5+fG7Yhs6cc8zHm@$g-P&7ok2*aUs29oF%_;xfNp>hHj7qY{}OHFV*C9s3x|xQMrjX&2$GWry
+)s5SwiH6<O2WzcC1&AAB1P-xN5IJttLpz%wF!_(&RzV+eupzILF&@NNPDCpTs#;8z|*Xgw-<4g}go0)
+>LICyzX3g;?fn)Qq_dRTjXB(g-fWZL`INAg@9c#MR#ptGG|doQ4o7)*|Lc&timJTL5P$v>wGH1m3-j&
+n0x>g2lU+7!B2BYBdz_0x$7kn+wHzm>4}U1Cxb$VW?%h4}sBNfUlO4fGvE$IFCH9Y=)K<j(AGAA*Y09
+SAR{wEDa>WR`6|*`*H9vXa?cPPmt}Wpk`^zVaM})P~L<F)5@4|<zX6E=TU4#4wiw3LYR<)<x-NgyCG#
+|Hi!?#yo8EqP_0{E?K5_T?8FS`7DtgdnLva_VPk(sS3BX3_X)K{{3C$3q!0z4Im5CJ)5Frp_Y_u21wS
+OE4`p5?{Z4?0*|9`ic`@zFAddy8Zt?#j7!(^EHVt0E$;7U!5q9?JKq7DH2^8$V(ovr<n3Wsu9$LW?oR
+fr(1y7yUucd<RJ|R9gGB;<~!UrrU@eX2<bG`wB6CppTdQSAt4vaCRy)eK70ve1MFHV9L9J>NimNb~iG
+0-KA=a6Ld5UCy@Wf|9cY7C?own%u0_jE}=CU}7b7f~Cj7^SI1;AS=K3yw12ogD>L`zLRc#I4*vr;OnJ
+lPlvPnQ_}VTn$Jl54^7i*1md>l5j2&NDY8(80y<6@zp+$L+@Z#*y0fShg4xjI6@Y*0uo#<k9UoDk0o^
+B0bl!c`_v_{R6F&)i%ALtGJj4q6cT(R;8+Ns)39u}P(^R%F?vE1f?HD2OUeX+V-#7h1^0nSMa*pm1g9
+FIe%e?1gFb^1mO$!fWXFhBz)+SDz1AC9rWXouc+<=x4v*;B-N^3lfH2f8JX9H}#Zq8T3GljG6gS~{L^
+~B|;llU@WJIeX+wKA@tE7Nb&5`*S+13<_Yyv1VdZR|eefjuN62QH|je)51J>v!=H|Z&33oqQ<)`$2cY
+w=hiO|bq*wrP7s%r>KRi-$cFENKrVY-k5@wv$eH$oOn`>qGobqJ>yRl!jSuI2|E$t`K=<l<o-LL6oIw
+v;6)K!0eqHi_-h9wSuy7(-&~o=?E7os0>%oGR{g7u0YH{OT37|4g;!C-xqODEimOyo))PHg)?}VMYPx
+yQc3JS%XUKbh9eJ!)V(Czxusf28Rc@~R)i1}XEv+IF<j<^BH_T?nNWbfc7SoQ-tEPR3JYaL@Z1C@CV0
+?DgI9n<^bWkz@k~y1*s$J5JvSnZK1Z}9`ojG}fiKCL_xkgdLB-D~>y<R5_+$cvGUgPKxr8oUaKWzzAk
+i+iULjrr0pDYY_;F0RAb|ibT&VaA0_HYmpA{e86t5Pe_qekL|2AHUF&P4GsdiwHYXM&_=9G|`gf3h#V
+hTw3QBZa@kd%#g`~$o~6zj)(;vAW-2xi0xS`FZo$0;>BA*g5+mB<&EQH|at&3NI+tAcY9zJ<E>B8vTB
+WcGd>aaD*h5TXRYV2Uz75wVJQOpN)HFF#{M9rcf?*;9@r(p)vTfNsH|dn}Ib@Sux0dtFW#%qQX!b8X;
+IBJq`oDG;YuolE74BdB;*QNi`)k9C>*WU@x4{Q3t8k51UH5q}_9OtBECEBiQ1!0}y<0Qq|H)wMOiXgL
+?F76Z=;aZUp-zWxKA3b9M~0rw9T+l^Q$Wpmkn)hE!xfgurN$*_eB%#j6ve4_X^tZDxRkc38>R>SQ+4$
+FWqRneQYF`o4H>BCi^p9Nb>Bb)T%iZ!lTuq&l5P~8+U;MFR@gA%JHm|U#{oC>4@fhKH)jH+Z4X3|%`x
+A4{P29^e7FGm22D$v`s+!@KAz#~}gvUbn!)2+Leqe3vQSsr-bBv%L%LmlE_3=Kx^9Bj_jUO#3)@juwK
+t2_t?!&;n{7IfhS8{Ip=5~>j6hCGzeg%><QEfay50x=he84ycG42hw!Ku{Y~MvPicV+mb&!Boq+KoCQ
+sZ(}MkW5k5|76=4YB?kMP5X=?yTc6fbPT3M8*d8m#`ZeRXMlLQ6#n%_G9cHFyYk7=>ZAChQ$VORPuhv
+IEAfkl}iEHaEd!ujH;+O?jk=GnBK&#@(db9Q@o`k5$nm%@fYfo!ZVN82<<r}MJv%^jl16BB;h=~OV(y
+qm}65u7Q8j%4rv@kSTV?uG)WVBBsi8owHUEA6c7{s}eWw2iRUi)7w_Z+4q9_d&MFC6nlmq$U14@U%0l
+<PFWz+qz2-vI@~JCbbhwqwx?7|f}i0)==q(s$N$K4F2cyv?#{0GoU5Xns`Xe#vSrUegDMTsJ;Bu;w%F
+0JqaLoLmD4MRiE{NBmq^zgcR}2~3%`ctvI{+~J7m(;vu@g;lsNg$WoSS6JDY_Uud<$EMqi5M2!9@m1>
+olthISNLGJkp8CHJp)eg&a2@BRB@)(3ycS|AksA>KHj=#wlAK+X0El$u?AV)No1}=Z;B0vl<Q#DwQ#B
+kI9y3^6L7g#%?g64{x>!bW-i`b+g^_?wtZWntKUxS8vLu|c!7v!OQN4-tkUSd1dp5R?LtU3eEFw+<bt
+6$|C=(GIbTiwQ2y6}UBKs!GF5Rb;u@Pvz=M@bkP;MNpL1pD_K6Jn_0WeMC>#jUe)@M45z;8Z_6c=2|-
+hAF~kj6{L2MNcDA_-kMV54a0;|~eNePRJzxKOeSjFcuE^}%UiqCKR#282fw<2DKC3YVy`91~lfjASGn
+-3cXh;ebt)kUqYVsQ<CiPSoNU8AF%sRtUb&BPxIoT_|7H2u$+3_&y4TTYf8KNpM(F6#mrkls*DOAI;D
+x?SA-6nX{=reLX4J8b?Vj-E)BV=Gd)I$JKP)fDZ`NkEKDZiJFFJh^T)Br{vc;4Ax|qu~kWo@+>R~8uH
+6Q0U_cwh2Q8dUKN+?ER_cN#`t>yf|Clx$pHb5XOoPrEif!Uk+K5Z)+uP7Kam1GnshZ>?c>chw35(;19
+sFBrlasrs{&)o#M|r|E+X&~#$emV3qlnfv8C0B5!b$^k$HxU+Qj-K3>XrhPJ#cKAfmHk*un<>E<Xt~0
+i5)hY3_Gh4H+5vofjEBn)kai<wk&iccukg$xp?=VEtY8*9`Ne`1lK?x`I=b(&&pz>r*D@Kv2g~kKJ*@
+(d9bFrIJ1!eP4>CH8Zf6!a*?ted&x7Q1&?C?nL^8%>umekqV=Z$jcx>%o4{SsdyO>?|Y<VH%<Y>DZ>m
+^h?D^%xvBLb8?YGIXWC<+)b54&;D9O<AHP9>Ar&tW&KOKL3<nHHNKh0Y$87@W)gf#4>7@WJTqv8heFm
+GvCHjU<1z+KbGlU~Fh7l1Xpry&8(};8rgy3mU*-N4X`4n^b6sdTlaS+f9z);{8q+C%8cs4c&A_miw-I
+4T2(xni_5OBK5c8(WM(xrKr<-Y{1CjKkIF0t>D<SKZPLQe3CSB*IUgC1C)NWzM-^h!tw3=w^TC=HvGH
+t^0G2wX-Xt_|Z-TPiQ+g#u1CIv9-dNSyMX6XsAbYD|su<9Qf)3U@)K9j}AdL5pK~VewM<=ER=wWYH&m
+i2@mG^bsD7H?2_Gz&R6m9H05vPe4^n9g2n#qFFjL#nmBMTTYhfO}SAUh?Q+Z)ll#Yx|3sv#5l(Qh+8{
+$NX-|1C`Vyt1n1ZxQjJnOb~vywh~41bG=<`>St>vwId;gU3Oi0SVWf!1Oc@hj(K(_ROpRkC?`jIk#m+
+^QA|L}Hwo<Yno2!nmd!@E@0R_7DHNB>|CB|CET6`-;!4JNbqTx{5xYv_zmMPsPgnS9h1uW8sKTwC*2x
+;|I*-*&QpnRxQbYPAEaGG*<3%;b3%CAA;U@zTf2L$;>OA8T%o7jisAQ^;S#B%2hmQqM43W%N)^r{1V<
+!x5PaxwLpuyjI#b9@MspYfi_^>{yJHL>x&FIAm0(Kgr@So-IykdeO9#6q@r6KBb&Xz?<4E1(uA^<hqG
+z%iw1I7IftkPz1U@sRrLhA&VIsP{KZ`<=Kn`(gwYnQGP@W{JTg95`lA+m1bC2m-8gQivj@n)ayNUT9F
+)Ic@U1W#w`KVsMa}-6!85r)4>W@0Jz;n<QjzI(`gvm-q8yX@`E5XATj=$ul8_u9lW1SO}wOCg5Z=VV<
+lHp@Ekw2Js*Gxv+loPC6wppJ!l%%8u=;vJrgdfn_&PO{{?Dw0GDiAXHZ_4o#>Csb6GQYA`)tF=jZD4v
+}h^9`Z<<mjR(<<{{d44>x9(sUQwFEaaAx4To?(b=<Ln-r@}##3BWdQ%8mb#sla`gV*2~k6s82d1h0Bl
+JayA!UV=?SdElgb1ac$8si94--v@knBWT8Hso(12e3Z2?1>n?yOr7dXgUsxLmo%gt4L;%Z*40F)3L!I
+Af!}%*rc8iL}4r7!iCanOCy3hPueILuRR2W6hbY&rs8u6UASP*lSY7|g5$X(-1n{!^y=75A`TdQ3`!c
+(<)tOSxZ+MHQQO_#|K-DN7EyZTki^K@CLs`eUqz1kMz@Qyea5Kj9V)`}!Z19y2d%c~Rxs{&Z4Z9<?gi
+%n{7rmXX(zsI|2OeTC55{dNKw%JAS*6u?>fH7it9N$@%>V}YW_f`#nWr%$@-$&&huJ6ZvO17H$4d<-;
+e~8!4l~VLtM(<SzoLi10fzjvsmZnvE~Hj_pz)FS?^vsSR*%z3$9W=f<>4TZx>9bv4G=X!^OZ!$9d&$S
+?n8)-)Gpu1s?l4;Lg4*Z=uDdyAgrKw?T<sej9WOgt!TMF5vPisLcW2?!$DldWYmo^$vb1*n@$IMWh)l
+#e9}D#|e~1P=r(z@V~aZi)A9L)io=37oCgsIXgI@5;B9<u1{vJjwQnPCC#0^uNhsv6NztDLzF9qWZ)g
+Ag-P($v8tT(Fo&Wzf)sW(R>zu@*ovUJ4=nOXNK1-D4^m7Ua^3>m-`c^YKo-d^p`*2spwKJ`3+zZ7s0t
+Ch0I@hJA@wg#KCJPQL4K->cV-MD8AEtIaOVk=1_J5J&WMTP%3rI90oshap+JYCIEwUrX>=$3G^~rX^i
+s3i7zYB5kwR!=CyYDP?wBu-#nz5PtiZfzC1HH{I~Q<1{k4Wcqvav6hu}Kss+pd{L@1@k;MN4fBnrc8A
+{20L#llc4vBXd-kaxaC0;^JkgEXZCR*6IUXRwO?CLR&naW;lp57rp%vsAnkfqA8C^GHus(C|Rg!3a8G
+vUgr_Z(P(C(_0;Gxm;)k2$l%8;4O^`j^|lNBfC10yEeDimG3x$>K5xo!f8Y>mGr&<BJm0;29OaLK>It
+WJICgSIZ)`&_;jqz3U^F)#1=eYFxgRK0l)Ghx)|t?=i-W=ilEQgRv0qkV(KV;5N9@KFwEZ2)&ZiKnSd
+Yrz-@!4&N^jD!~2>I{j;yXlXG=sp6M+KL{c@F`e<(_V!#jp*YIQCxoz;=N4u=(qup%D$=oPOFVEs)?2
+ccaWiTJCP}QEt9(j*qJn>JPmG>z~UbcdO>%amgr<s8Dg85eh#JG$#z?<<3!HK)rVy>n0h^nt&iUW6u_
+%-``ku{6fXA3X{*Wwn0U8+s_w&7MzA@;0mHqqvNDk6@b?fTkKtP7W6OB<hy)<%So1)YwGN8myWdn4Q=
+%+-x_lSYi7okkS!f3{?IvlIS~nfJ{1G*m{ZvdBrxI8~Ox-WR0!TS4@6RE8W{q`_sBL(5QdK+TM29j{J
+h)|ZzDK)~b=peXpU-63u#XZq4TjyVvQ%3|?-X>@<Z8R<Ca$ZWq4HBtH_W&UTBstb=N<5XP+<G2S0B^j
+r?&k+F4Hm&gSx1lrtGf<g)Y6vrzijES!c&cfRysM0}ap{Z68J$%jTP>5Hw#LMYPg~;<BeOnjO+!!H{v
+2=ZWY%+=K-kU#A{gQj5fR>OAJd~tNJ!1(vz?d#GB+TB^1UMr8;;om98M}I!t(>1Kvr<1Dof0&@oEBwE
+j++&4O<G>!UZO4&Wgqe=9I-TA+Ys*1q@(LpI1Q7`F0U?guV;Dnn~!w1>-W>5Kj?gYyeCobm0N>!5N}W
+H6oysVD#1G)UE_<;Q+5`k&X~q6E^9Dpg}VYQ-&>kKxt2N9kl%fP~EnVMsRk{444(NS#4@5gpDHkH-aT
+iX7)D(+aj&2k9*y`mgpI11ukPejNM1Z``%WqV4UA?oDvjCzIX&-zcJ7DRq}>m+0dhaQ=Dn<sd8|il;N
+i1D)gS)1v_c5!^@O(*`-F%HJF$&xKu5QH2UV>86rMC0kc7)==dpSxF|7XOk~VRdf2Bz(7Duv5m@l&qN
+Z*0Y#MH(*U*Iz%o01CGN!yiG8qh+{no8U?{FfH05%<!Po)mKs8Nw40bDpxO8p7|gj2L6QVR29Bawj%B
+!kJg1wlh^i*aZr9B~VA*t~@2Z%2kY7-45IVV|q8?1sVCs8Aw3X~rlK41@&AH=Bn7PG>X3!0y7^I45K=
+znv`6vzD~1)Q%dYq^5^4qu8z)4Tg*=4*3cwn*NMVO(O=EnHSMDP=}m!JhEZ-PAmq4LQl8A#@`=yA$x)
+bab;^))A*JDBG{F)mduD;$%)1Of90@imiW~nDC1mz+mfC;jTkGIG?i;JSN^8}@naw>V45iKeTFO~D3|
+vqmVh&O2(Zj^Hv^Q*`xAzVTwIDUx|Kg9IvmyEzyTq}r@|*dM7Xp_AF|G!n-n9UI9qVeV7eDEWH`O@HD
+Ng4m!OKw%?MbxIo*t4C_A)624g|b&nJwaD-#k<HzSZ^V+Uaw&Q~d7M(|aNfDwF^B4h+zr5FpY#8rwhW
+B4kCV2gvofJ-4GuF3?O1a@f!--#1$l#zES(1^TC0j6-5;=cu~CjOORe3v3*xVTFZOAPK(;8H*?55kdI
+oLgVJKPLp=rJw>$s205Q9W1mc?oy!ID3w<uP_eva5eaziMQ}zVhBrov!hqC=0*h;{OUB4+t)dW3Ihu|
+`e1j$+aPtBw4iR(n0x7|)<aLy{lGnkay_-P$iHvjvF>;ZS4nsS)GSbmilOLw`H>xJ1n|%O#PE&?1Qr<
+ur2?F00lyKl9p+r0ChbHh{LK!<0?hTSq(0DJR%ms<(f(a=TP2sy?Le1cTI=U5+>-(wBQ(G7&nct6&r6
+RW&t)!Gf3<0qc^#>9m6ZL`IiKpZtx-vIDA~@&96TQfdhZ>N1<x_%NR?vu0BCnj<u=2{WW3-JtVh9EOa
+3d+-+oz?FXPN#D$*_{JF|UqUhJLfk!f3bI)4vjgmEl~#b05M=UxrjzLJ2O#d!};+a|oW{^v>yw;hb(T
+M_!6jG&1EFg^W8ifT>VeMw!um#ZnB0=>Z+WBE`B0$)y<s${x*(z`QNOeU&G<(9rTAm!M)3T>-&2zb8}
+;J6=-)Gn>>=>3Ge7u%&hcl<<9sCEBggUykF$>Cs@sIs!qCfj}yGAyd^b&Q_}0gb`%2iULC05+-ajPtF
+;(aDmN-gcgA4w_m^ohB3BYWgbi!we!Mnk(~}Zv9LC3#OI2xH{Eo;zM1qXvb~Cp{3Oeq5Cq1+IJp8Nfj
+Sb}EH>@uCn5qZ99XiZ=i!j1P{CI~3<cuai{NuSf{F~wfDn|jFk<kPA5L%d#tf#<F!c#e&uB#qraOQuf
+>W9%GDj6$Zct;w$j7ZP4jp&di>;cfV^X;diVZXc{IFtcKS~QOI%H#;!0j9{oNl291f`H#$Y8o`9?Bac
+MYbwgWKpa+WcZc<^og!9gE`udxp2${l-UOnc$(rcsz!)Ms2JxvJ}9pE16#NlR>A3nV`TN4KO8!Qtx>_
+L*w`*6vSC&N4#~r<)CwW8K~^@3)`=`f{0Rmq>jaj4h2Vy)GFC%W4xjF$O~m@Hs6@clAnCh0c)=ZUkHW
+FTpe#G}dgpr3-+INV9U^V=8?XYr0vxhb=0TN~Vt)qNq2Obfh_S6JLwBfz#0&|_=~Dz|J&ObwnNT%lFc
+%=re^~09Ccq2`LnaPx(`h)h5fEHx&n0(FYR@@gWS*BqaPrgCMqT;GMdA2^Rf4}7cjfOCAGE;S*9iKWQ
+Y4z<?<UcRhF+`o5ITnwGe$ax6A@!YS{fsq!-+s(#o@$MpgIgvh3c?74}cCxOyEvVX~RG42epVV=L}nT
+fbEONLxwFpz)=GmF>K)hV)_}%NqPhpD=B7#n0cBkPUt&luvlYb!ffa?{ydxu3^&5H$-Ai}NPD?rd4zb
+yh72c;xhAn<LlP`2H&G&ZeVyB=EB`3l>vHMfuKcWvb;*+;25u}6v@XGe%r3(mSlMM$M$o1t3JA}JFgx
+L%U^RS6lP0i;FK<=Ag$-4k&Kg5M!oF4p$asWo$?w(!Y`Mh~AcUif;Eb^Ej7afS>po6?$~dJX*b&Z?OW
+S)gdZOIKd2GH2F+^5Ng;-_uZ5)B)2k&MAUVKJ2S2Pf)YcC=RFYOz1GlBIxH3}uZLoo;9l2bpauFjgWy
+gB<vfUN>994J3!H^k$SRV=8HnR5YLc%kI`_a%ZZ7SSwe6g?$KWxS7EmRXE50;F`6bjpic*9uCXKp(;9
+RtRz3ct8ltJD4zvvP}X6Wy`D}o-kY1;X2sLg9xVzaO>L~WMi))l-8m+_Dx?D@+C;qn*bg8(X@IS0*Wb
+zHcq5YQ}nMZb6PM@t9lZ1)v>=t;l&1slPZ+hK}O6*L5^TV@RA><z|&o2zK_2ourWrsc2GKQz?@K|w;)
+j>#DYL%DW9movtH#*f^g6fhbrYwwuytta}O=HNPIWZ%<?&iy~oJ+L7aRW0XcBqaUh`!7tFg9s$Pn>A!
+CB`)BbqB<Ukq8O95QCP#SY^Ex-3S0bFDx<G$uWo+E-q>vW9}*6v~?<iJV8a|vBIVDV``EZc*)g$YyeA
+fI}|q*@=X3nhY|m0^_lEE*b@?>8A$dJyTrc<;%iSq4OE9!dsGJTi+T?1Q6O&DuHaElJ0O?LFV3!#wYO
+ADW}#ZSmG8J~7TBYj{R*&fJ+W*gU2>LjK9Y^2@nI(>FRM#7J3RvWp~wXOmH4Ho-C<7!D8;K8Q0L5=lq
+HVSUT5*+Kf`YqvOtKZs+Y34`hQ$80tSp+IOnkjNV{Y~cXy@7@`udM&h`;pF;zvOSQ4roqUDhDU1M$N{
+}-Hm8%o5Qw}xHlvX88C;C0LhUkb?cvr_pUe5fIT}ImDZ{(c+OCDp=BoG*A9m2S&RiCu`yrz|Q4blAWF
+iiKc&J=Qt(%BFHit^u?h*IJdx2~@^iaqPV({^=IRzU9<z*|t>0A{?s)CXcJVX+fLa^x-0L~vc7~)L*I
+yO)J|1W}-7R27Y&>L~vn+8JOs<duFUd#K4*8Rb2N5NxRc4T2*%eTtFo}-*FRCbim5(7I}=%8i9!h?>x
+CGktL{t}qAI>+Uz8*ylhY`L2_Vz&pUyz(R0NT#mfBnopN63W&e;BmAYdy<Wwy>TW4g}gOEG?9~algD^
+LTp*k|GV1V_k3OI{ZutX*6TEeP%SRq?T5jdnQ;pEv+8s8deanwsoWo$fWiyK&O1z~nE1IQsn~F+ifQw
+4dC_Rlt?=0Sm$1#lf-tz6Y0HSs4%+;Jw?D61Zb+?&tY;axVHWyt$P4e5XX5+YZrV@iK4x|E{RoosVS7
+RW=v|&2M*VNGo>vQjBL_V`i5=rR7111N-NY-b$EYATaYBM*V&DZ581X?&STmH<LeCCgjAT9E<Gq*-EK
+RY9D<nXibc?fKbq!q&!Ht>r~jlrdbFEUaGXNSKTjO@!V{Fp25FMNsP5{Fd-Y;s>j0L_fe7kkvu06WeY
+OzTs{1ALI<i!+{P3conxiSf8GUkd9Z;r7eTj=;Ki|9$VCGUk;37;ocU`BbTnAk<&Y?fs$mUsaj@ZNH*
+_{`~o~rWyr`>JIe#fX(_%y-%J3HfZR~BLE$+VN-VS1h7#v3+A{|!Nx7!1z?i~W+KZ9Hf>>jz|LD3wL7
+cZ7(S)4^M-CjWuLTO0KvX*>E?hvZRjS%^Ru?UG*%a_gc0m{OZVKs=KTgXN4xVuI|ehr%=m$q`KX0qKI
+Ufp(C*xfA9$X(<7cv^6s#RTb304H+VOivZOq$6LiL&P!`RQw_+ifHX8fq#cKqhVvl&0g!Tfm(o7Hk1@
+4_rn&IV-pfm-MfzUw^*_xg{z;DgzuU&Hht1~zPAL%=32Yy#MM3qyU=w!SIqn+{so0I;@=DcYEhTG$A%
+w%sY(olaZW6tH;<o42rFS0uNi@nAlkX<Sf*N#fvUQx}&9%Oup`q{wEo$^6VTQ`0#Gifwk@pLt6xlXca
+D>R*CwE{m{e5%rG-8!8_$E@F*aFE0&r-*2a{kLe5d1GVyNaP=49ld%Tx%lc0PA5G64{*^=w$Bk$ViPk
+4D;eOI@rda=Bpy%h!MCm^abT+7_<wn2ObW@xShLEUQ<ZRiih0d0~TI_83s|C-Nz*_Wd8LWlRmcm;6Y&
+on2(3V8~W86Z=40R>67EN15YvHt|G~CZeQ)jZ*xrZ<*+#VEukqir;;Dtqe2rrwe=i|A6?=Pd(w){QV2
+d(%i;BUY0L9|}#i+DD##YTC2+hAA8|7L&I{4nTbx%G;!Ms!H8MtMb2BfRpb5ndUo5gswA5I#w%5guWw
+5gy5?5gy^F5gz%d5grk#5gsWilxESpl$wuP&FCLub_~H#Qv5b_XlfB?Nz^YUn9S;Vp7ouXR@hYE3><V
+WYD3a8QE5XDe#H2Ziyt$7?BXXD3m%dwS25_j1V<D-h{Df#lBSYu;*#l4P+c<3xl6N&mw9}{M6^sUvt(
+9Hi~i$`rbO)xlWBjTwyJZ_!rg~T;Vy%;tT~kWS7DWItseW;Uu{1GsjF}`&2(t-C-tEwQPxDf57x1FPf
+!GYy<N2Xk7p|?icPXAf3KBV_4jZ3he029iEeZ-IFzfZP4zF0>d|RwkU^0IJ5Ss3GK`Wi+%IECleDNXa
+5R=dZ8}SBVn=7Ss9oAw;i2e6-IOjW+y;Ze*ekX*ZswK4IL*rWumg7pYsDf}zJIrfZ_9WW#gUqz+gdEb
+%Q#wP`8Fukf4g^(^>Q#%jzz6j>OT#}v$J%lA^oQX>0Emi#^2>5i}C1&w)p~PH(y$4$0pag#VLf(66@I
+aG}yE5IyOTE_E}?kXX!S_s%_G>$11S9use3!j=i>Hr|sBhE3j*ob!@&JTW`n4+p+C-Y`Q15+|wKGiS1
+TFrsHXnUk~Q<fh!9Yw$=?cLd$FCCfwrt{{7FORjb3}B<-GatQE154@Ndx=&8ZjK`SO^$4MpvJ+aYpm!
+CT5VZT2(O5(RvT4Xx}T?BqRrbVjKvttB)E2l-;rELN+GWNhs?9w)Y7&v=Srgpz=0)afYk#=dDKrlA5k
+v8Wxfvv>saha<Mj?sj1Yc6@EF3AVAR_zdbjURN$ghtN0rd%QAs<fINPP+z8BRl6qAxE79rjX-tmmFy1
+?8r!I{M<1BYQ#kFBb0~2&gVzQcR1``wMjmiS^Ikhb>lQ07PJ0!FyI5gd0ZQVqA!hg-39kr-N3nHlXj}
+*)KbW%)2UwrrGC|t`gKOy=@6wIeo)$>caM8`HMXlK-JahOr|*crcih!G=H*qmV?C7eX%@w{aKka%;xg
+F%o_PI~ed%{^x*ea~Z&w~MIgdI@Wm2d;uCZOtO)JN}Cy!Ne=!hh#a`hgoE5KA&;M#wx5ohWL<5I?@jG
+MIx)4^tP)!a56ss3`11A`p6$e}?FUF66hM=o+~kYg7)G02IFoEqfRMV=evxr>|`q&*0(_}!k}o{4DQf
+8V=TW|#5q<L7;h`2PK8Sy(E_{qK7Hy5xPFXer>J0e(>aS}ZWb7E=a!`7zLU2}TX*Vi(nbaSPDz9Mzyn
++e)_8$7c#MZ6H4cc@baad8WVcb*_&i%3%GGSzhJ(gM39?KKv`X@>g*1>*F578#aEe>+krVDA2O`>#vX
+Q-MhEX<5hB7wXED~)#$_P$Gyu(J>{<UE+6-<d&#c13$}5g$ENruDdTPN@R2Io7X0h|O<C^0`}W(Lo11
+TLM&D%l`de+fe)}dUH`RaMyuSads{X$I2>X52WS9m;(R&t@!RxFj{u1Q7WVe3!sfhFD??>NNo!KigL;
+k+Ho*Q4>hVfp%DD=?$#nk^BP)h>@6aWAK2mtey7Dokc9IXFD003jk0RSTa003}la4%nWWo~3|axZ9fZ
+EQ7cX<{#EbZu-kaA9(DWpXZXdF_2`ciT9!;P?Crj&f!r-?5ZO_q?+0-gRC*IXAD9cpkgHIaykSWR59P
+O;T3Wefqy|RRJLJBtcnroW8A-6H6pe01AadRiUadFN<aL>C=2&uS@pnQ<N@OMOjDjSyg1~ntkHG52GZ
+#OsllW52JOS&WeP6%F>#ZaaP@#BZyVHntr-uc~X?qD*Y_sY|{Gt(~{NkEQ_n^&K>c%-mVz@qMB1#HqB
+S-x~fYSFO4^Kal!KRCsvwOpfEdIr&+>E(T}P6X!_|au9z(RIxa745bHXXV`!euioB}hyq3?K-?Bipif
+8QUd0fUbKtgpG#nmiL|GrwW^3khe1Fd)~vRN8u&oj1U9q`XtoTT}puh=Y!>sS^#j7sgduM#2i1SjAyd
+e2sIS+VDzXKYo|?B?;3(dhhrtC!i!yk?6s#wzcMG_Tt}EaUoonVqoDwXcj1Zk+skUDYp&lBJ9MxUS3e
+3<$6S)cRh%j#oZl&+pv1ldyS2!y6~X>`<kZqY=Yjqr-^hK+AyXqc=s)4(~+Gr@_PDPF}x~T|YW0*X-a
+i6&sC4?+|ztM?kXxA}Y#go@Q*4r57wJ&i>72NYixzC8)L0h$_}+(UC^D(*>(f;P(^$d#t=Wpc*TvfZd
+?>;QiU5r+dz)97f4tf#=a`s$irUy!>*YV(mp4FAxAP3mC5BG)LS4D;Nz$XE0iC19p5PZ@zV)4tU*pk$
+g(ntN_|C%9i<!=j(iit3iyE9!Af2L6(e>CyH12L13Rg<!tlm(->hK@(MVJ!wA`0C7wJ>XSGrfKXV|(S
+T2({RHOL`B-z%R0uNtSW1~8L(s=~@o>tY2jhotsoOMrMh_RgLomM|>i${?hdJ72fWmaf<b*lCKBh+sm
+XqCjQVUMQas8T1q!9L6$pAUtS_!4fHJHFyeqnt)RFrb5&_!E&UGJ*9BH_?b#5^)9C$)aK&jaNlgrDxf
+;>HRX!V2+k7IiLoSHQZ?@J<Zb^hElO?&es`VPVF{$)O?#_^%MEavEq<7&FZ5Uuu(JL!ocMRJ3K6_1uv
++~sN`BLtY{LIaf*yDkC@yzqRlzWVbFx0h?ON!U|P{KS;Ib1B8DXxLf1s#O)O=x8gPY`MV28ut<M=vEn
+<+W)aNk@6j>IXwRFHIk|om!83Zz1jeA5h=;ayobp|_H#S#Eg7VE`%Qy+J;KW6FKw2$Wyr7MGwIH!^D-
+ctuxWpSA%L>DauP-!So@KLo*O&fqG=YX$eyv5#@Y?Z+zCA5&)bTLI>F;E3HxH(T}=ZLCHR>F=~6-xja
+FA#j&J=lP?Ic$F1jo#a{2mi2R^R%pLF?;INH3R?@ITciJB*>wKoQipZaUi|wT!G$nb9S+d9O%s{6~`S
+Mye%&$<`bWkBSm`D3eqs~{prU$`kU1`@(g&B!1%_eW6nrsStXWa)31XAnxW=BP@j_($`1_q9l;*cVmp
+xR_v3-G{P_kgS|SQM1h~L$aL{rgo7=J3tiBQRW%(Ldqgd<filo<s)#AZ_{Kv&6E*BN7d>t1}%dbJ^2b
+46w^<qtbph)xGGciayj+q+E!CHH(OOWXLw4-gl4F#bfsQtQAKguIMl?{e<tdy)`^;lsnu(~rA;jEYoX
+lw7Y1Y!xuepM8!@qzXJ3^013J>_fO68t@&wca6h@(1`%*&~|u(3IES5Lfw7$7OvIFJSVCg#Bw#dyy3j
+dWw9j$g?d@OhQ^E->;%2kfp-gMnA68@}e3=_oGpkE@6Eok@6@n<S&zam#erIB(9cHnVY>Hbgb#`C6za
+;0mcG~;n)y-EVv|}sd}7eYFZHhWHb`8jK^FX{D3cY3do#omiuWm-r&{~*6PtwbPqN<(2=0Zy<MAZAh;
+T>m<{%B#398Qtd3-h`xOqIKXsK3>V_NH_*tRl!x|TDG7=2maZX2P4E7WDH;_q2A3aq7f522~EF&h%So
+iJ1eMu!@btA1RrEIiVq^qyd?oOk)n#R~;W9`Nj-|nCSZbQavKGye7p8?`Jip@*h^jZD(KjA&HMgIPOg
+PgU3(QO;n1j^bP(eYgD8YY(lDd+@b9Ner)B!llWX~AjsoK}D$VKc5c!vVn!HM)dRH8NDmy}79Yg1}=?
+86N*X9K`=4X$WBeb>&43)uEXs@PQ=SvU++)?Uu@9uq+@06&@i2+-qp;o}p<8C5Dh@JhNyP`#Qz(O7xs
+Q_}H@bw8D8eEjFS9jFXwtIP`NYo=yp$u#!h{4t=M}lg~iEAYdleQcx?AAcr+ulhf&ln~z2x-6I7NQS^
+R<EK5R*6=RX3QMKW==xGb<BWzxAUS+U&$KEa8wD2AHguU?ynbCbzEJlZbmendtXM~sgpYh=z5IYB5t5
+J^D!q%aNK<q)MHBO!1*ML~i6diUs?<^1LMu6F`=}aT97z?zur;<V|C3iAao{rkL3TynJ+hJYxj3R1fA
+(A+2iFZoz=Xv5O%-<+QpB76TvvU~yl~VXwF+(-iRam@I3X^H#IZ8{e^6gud8Ka6qrEsE%G2}(Lj5FX2
+2|=+8Kxj&-RruOlE}^z%1c$SAKK#tqWeMYb!$qrBQUvCmOVT)<Jp9-QLe^VaAUczC0DseR6!o#fX`lh
+MGr>?D0R6BmU}V8KYO!@X5;N+uxbebA<}ADIYkUuGN{*E#P!<_|JZxJ<ReMa2FQEdkrJsK}u(btq>Yd
+ne%T!_j<Ba8F_cR{hz#hOZ>E*EQ*;I)JxJSIs>h^61G~SI)#-}uR;N3Ymu(6yPooRGd5=Nra5t7X4BP
+|kGf%CHX3Dz7;2yAzYY`Z$gZ35ThB45Vk1#Er?T>}#|1(3!D=R0)r!-H;cV;|aG+j94St%VaP6?pK4)
+vL?JsJr`Onw|1X_yjj7)#mrm+>^W*cCQHh*dnt>K=CarHxQD$#fjKcP%-(Q;#-%JdX0|z8&uf<S35x7
+MiXiap=CI3PC^=Avuqq_+o5#>%&;z~GrLRMqW+*p6`^5=CVA~a4XUEYF4N1a4{A~sV0LIyR9BlcEhK*
+KD_I6EjHg*qkvQ+{0~SUOY*#p*UF5|kW66Rgmga#UbEh5Uz|Ag;G?@rFc!CD02`CXMo?J|-B1@BXWm{
+#f&(ECKJ=QtjF9)i*{-1cVf@)_5GIKNsZM!-LO((7IRnhaeFBEZIAXMtf@<n6LR*Y+0hHI+mW9Aj6pt
+-O-8Miz~rv<e=#XKQN_w5VAE|JcC?a}m9Xsb}I3}U<FooTfPOLjH12wU21)L^~UZOXF5kcSE6Sf#?&i
+9ol=4>PdqW(eswK(-cXwves@8<u4gemF>n9*Q}HA1C2?6Z#pbZU!)B;e;T}jq(7EngF<GjCWBvhC^dW
+LB`)WYlefRfCkYdvRqd+9i2(g+?<gosLit0qJ_=K>eDECIp<C;^OPlryiNmL9Nv*l<xnBq1z;*eN-E~
+CPPfsMx36(XsF0Uvc*$bEtHW-;o}HsdNQ#Gzc-E_^(OiFZ8dhkQ<;jZgOeOH#!F9UeJ})>>hNxI&P?8
+0C{F#{eu}Nu-`+-hzmIp?!vxVMUG0JetoQr<=D6=Wh(o}m4G|>!gVsZe)y^;YM>RJ+LYjo5MdiWuY5E
+*vF*O-w0@xeX7z`fsA#x<=B&cZ_dUb<+!A!U=s`9>-%Pxu{;t4E`M{p(+obq=M_P&%O(K<H9$Jp!$@C
+D*$Vl%}~#6BeTt0%mg=U$7P+{LB;d!r4ys0rT@Fxx<a!*3uP72jsMJp!BirjiNwH+XX)nH;j37+TLpP
+<L9Rhu#eI2TX2poj&Dc84}>-)ZM6~&|4|avtf@y7(Rv%uW+;}3<_eI`rQE@75-4H0jbO%6q-tf5$K=|
+>)5nh|4?ikmjC9o@V^T9v?$^gP^wzD<GhpEq!EHiEAUYA5&<w513}h(f!QbD%8vUY)1Q&<VB^`4qP~6
+^6fRzh!BT)xr3oQD0FF#6CS@Y<^mO~Sk*@X;jeE7!?gHfS1G-_<zLbe=f?o@_U+P)+jP)j2fn5EI1YF
+@G**RcM#SQ;O^XdXC916xO)EX%$VKaG;JIGf>7E*8V@cw%Lh$<oydXk`s^Tv0)I@D3jQogSE_xl&xs%
+5+uBv5AKt=%H(TvR1v#m{|#*x#}e8d=7kD`k)de@<&hQBcleY8gjJw-8EYF8CZOdXOva3E<tEUB&Q{)
+D39Bh@8!$)@}&U<a1A8^2G-&~ppLs#gIE63iB+;!m28g1==*#4Zk8$IBn5=gp$f_iR9HMbX&!oNHNB7
+zoc*|f;j?TR1F3$-!u<DZ{@Ym=;btsf)aPw^1pSIXv+8VDXhN!Z;q5#A!!A{=K;>hy*az{;E?1|^rql
+_2H_Gt7aUOu>`0r!>+boNo4Y2tF$+0+J@YN+B0@1N!dT7*IlK=ZTOBd(V0{{It{@Yo$nG62YITQTVCQ
+UeE`OkmSPpf2gA=Z)txVjMLD-ZPQLM!Ynm9Lk@47O2@Vfgh{{4&ZR_h@MVf4OHL-r)K&J1ZV{^;Z<pN
+MAhjQoeX^Hsgj*bQsq}^!&$h{V!jsty=2;_)7hxmioU|DRd<$FIk*{CQc>AgJXK&D~&=)O|q$0haSJD
+$5u_E-%HlwK|XO}0+<jX0sdO!hi6;y&KfoBoh+`&w~-gYpHJzhvt+U^+j7s~hgFJmUS!ERM#xmWIDh)
+Qed?(<2e50wpyjg{@|jTwv9|`1U)^ftGv8h#-?PsvU<`Vl=kl3V2R&oBD?2ZbzhCp;W?2*xi}@T>H7b
+ff-}0YU$s}QyDM_ciX!5N2ZIw;y*?A3;LdA>X&lCD-mRy%}Jfr}rf!Lt<{ega)Ww#Ybd>OFJb-duE>B
+%ek#3*0KXBoSUvo-Gp{rm_2X_YKN-TBN*;a7_$`QvR{!lF!c8)&%`{&I7t%am=-3Tg|#J}Imn4<GYJW
+T6xO%P9AGb{>QNoq?1I%d#Ls|NN9c`yihgb+(Q=+nzc<6$NTytC;2;e;wz^JNVTU(}ne-OcyDLH*|^w
+qIi~8hJrE0t#@pmyMX9!R#io1hh_J2A&z*}a;GW~TZ-2ucmcwe|GR<Ms(@bJ&<d>Br!dW+BXFt{;VxW
+X5!G|viL0Vp0ds9AFS&CpY2R;1r}kjGJs2pt(b{sA)m`umQ}x|QcSyqk0OGM|%vcsxooyp{2n#9&ZZ?
+g^FVW^7XH~(y(o#AXP{2t`0I}3*Yo9<*Jj<ysXB+I4LxJQ=8<%RD^CfMus)WCN%CgLQci!=?<nP`W8|
+&3sk!<<f6Xdf`@TUbDwxPZ<^P;GG%FN=G+=QT?;)kUPW{a{|uSB6DTQBn;=z(jKP#QGfw|#bXr5&~Ft
+#p3mC0@v1W((+QhWa)49>w-Nj5SB2SepC%aL8(7xB|6aFu8Pp(F?H6r<LlyslFtK#>DXA(3*VUFs-O(
+L3X@>{0O4fj=CJZ?tpSPdV0=g7lauQsghLK&CoJiofqpY;Xdc*WG58M%@F~?Va?2=eKB~+9>;QB9V*0
+iDCpx*%%bD;(TxqerJ7@epL1%SPrvK(p>eoufqs+bYvyR99gXB3c6F5?RDT6o<5YtAXtQWXyaJvy<G{
+ijl55Ea-eBJXLIcgPRC5RA_uQ{t-3PdZ98DT}4CH7uqTs-E7D3(PljHmEkN<h{{9h+z1V&?joGpN**X
+PTojshu~h-zr(7d2Jb&38c;YE_~#Q3pwi0~#F|Lnb=lZsgL1+&p+k2R8*Uy5dN<4$tx3!U8;Q=z{_q^
+nuC!{GIJ{<DPVlY_NyuLql=AW~kQVhAOgRVTSUGsd-f_7Odp_qr}M4q!Ph;yghOF4O=dY;USGkol!J6
+exhY7jp}1kJ`N+0z<8(`d^wEhVRx)q6A(Xb4f@jDtxPkHXHz13O9W~E2gWFA@n9-gAPYHkjJSoae)=>
+em9Ih<i1A1aaYXJ`5qbj>l<52Dfyac7QYsWdcXaE$E98l~k<SY{0uUzJ7ICLsz`KV}?tT3Di&dS+Ke@
+ruQH}McIVBN5DL|)1uW&{o4EuZAI<-k)`1_v2V@s7Ofz<RfD)HP{BF$8U2dG(+E5M*AJebcNp<M-lMO
+5Kc5xWjwR&n6e1fIL8+t_McE_ee0iQ-~WwR;Khlh;iq0Fd6>ya0z$^Ys(9qQ_ZiMV(;+9T^7<z#W`d3
+8m+K=E1YX0>^q1m$5b!;*N*N6g4kMU~5m1<#8~`xz1U(0_G@=<@t&Ea@F{F#|@VRRY-6VH6BMhxO9Zc
+b6V!%@&}Gxg}+DNGy3A#cG@^&y&=V+$<W^jadf=<rl?=a!LWqRd7VI{d!1XI#LwLbv~^Pcq~8_=W*1p
+jY*2PcF-X3I-KCzQXIRO&BN#bL+0pSFVsXjJGEEYePu=4d!hr0Z5O4g>`UlRzNk=~a;54qI_|vU(_8A
+SsESV%C<iXv9;`-UsiZU$p{no8VZs4YK={2+<T}b_)RWFK46#pCSr5P(vH7CK3mdu6?HB~^F=x9YK4C
+;L7QM!t2rQ(gv70u2q%XE{(Dw7gb@&kh^(R<?!V+p2DAT^Arxh?=4<I!_6Aps2vQyd71fi2S-1AEiT0
+7)hk9`G=oqQ8w?8|dwI3D139J<u11%%9SVT82feD{Tr^l+KG@rB?|=>xk~?sY!WKP^6$<dRLk|{@Tgo
+xW922buua(K%0CIc*&zg`Hn*W8_8}4uR@Be-Hpozb3W;vxhu!x(UJO6(g(3V%hK7)q%9<$m=VBqQlS=
+<PmF?MduYpv2WEHx`c+f+ekWHpYMf1fl&CIW{6*6xwJdUu<^_U|&x&>ZIAe==ww>@mdzYERzc-+FcJA
+63#3<A*yMxdaB&%YCixT9NdOXn^NH{VFD^yyNUzOleD~8lDBSqcJp!8&JcyKdQR=di~srs06+Qy?l+`
+nI?*)lHE?Ed|vPAG~Li8{J`P(FMZJ^brm|NP+LgNOI;`+V3(ll%AM<T9>`c^zeSqJa5k`XGv{4NE{C`
+9stmi}nDkt_&h&^;`k+2!Lell4ViPIK1^94X!70<Luh3R(;pjhgKU{{vjJw;hTXCd^3H7F%EGywAQfl
+gG%%H7&eYwNMM(F=XHjSM<cUg!@2^2UxB!_s=JJJNR{rgpaz%eS}-f|BO>>foQFB~QSLIGl?Ad*4gyn
+`lsT(`%dN7a1Qiuma&Xs!q3r<-9dz~Z;9fxA>~(bC>}Pb}1da|WRkL`-V255Vr?^kArlEx6uPdm#A4G
+AlWT4=0NyY-<x0t0EZ!m<N_k*C_HUaJG;T~a?EQ>#fz{FYlaUBCv;L9d1nSfT*Xn*1|%AckW9zO;V<*
+yI^^x)y+$E|pP$|JS=ZD=zN=-&$He+K#I!H&>Z+Xd=8#`Y|+(Np<HqgvCua7n0I=Mg7*62vQwQj7ZE@
+nsx9ZXr;DX?OF@Ox=G`#Qfvr!8emf-wvt=(m_(j^B%roR|*g*tV#omSHpV-loZJ${-O0qt4Kq39K9}D
++R7AlS|BNyg+D#`^TR<y)c}B=@a>}>IiRV&`6h@^3axa%1i4{nHJhEwJ-PS^m{J_b5B+tap5s_S(~y1
+ne_W#w3xx*5^3F~nzWh_oOiz`zD)%6`qHgFSwuie?=|huD5Bzx;RBM7W^N1KrQ%<!s$mDv$Oj)lULvJ
+>lX?nKA<bL<jZihX)fBEp?eRxQjm>y2l-c8|kpJ77J6KJLq#1Hu^jCaI0^$Iu1q4;hqTKIO<9zqaTA&
+Dy!h0DM+XoidwkPg$2dIs7!1SrqUq<oqgFwVF2IpyiEEa$%Nj=4zZ>irpfO5(7$nI|!mvvoaL6!ip?$
+5kUou0ah8W{v<e?9b?Gz=_D<juJ^v>UiO&OZDT;!LZkX_L|lQ4-;g`TQ^Ae8R_t$74zK)t<4X~4;`K~
+OT~Tea918n^F#DX8<d@B(>FH7e*C^D>M<T{r>x2RG9JlffK5d0fn#{#OCLQ3m8E|-D(Z9POgnH!`Vxs
++I5lA3*fGmKj)a0)Vjxzo#M0Wp>tTnh=1HFA`?{YI^ztgJ|KhlF+&Ht6TeEa(#oy(yGoOYv2rf6_lt1
+C{LO33Ng1DaB?I#G6Zft6-2k_xP@(lN3c_)0d{N)|8jp0841Vfh{O?e>Kv$O(%WCo05d}@uVVOcsLcy
+?f8znONuFg(-I_0hrfj6O>DWXb>J>$k+<?CRPf^q>KD)9RrL9$kC3VZ+h-V-AMDG=>t$tW~T!_7^b}-
+~Q6a%5Rjz?qw(r?iJ|!qWUlx+pBaOw^F`!y4v6{!uLSK<Jw&mkw78m%54cQN35NMh%`3^2z59_o?mJT
+jrI0ryAgy5msl&dtauL7Hxb?9K2TSa)s@}lW&Yn}XQ?r3gIW3nAG(U8&l%77ffJt>+(v=lDWgZj*Cn7
+g1y2R?rr@zvl;gwMj|Qq%pVxNx1hbvcc7iEWiky0Mwy27Ap70Q$CC0?!*DTF1Mh+l)Tj)>w3Q9iZ6`j
+4rg95XU=&O6XHtlHlU0dt40E1n2&p@ed>&V*mjUA~DIsTLKI(GKE<ic38IEzSa9lGTYQFjfn;hXVJ>i
+#X>X&wBy-V_rgn`#n+1_7!HC=-}rV^W{T`J~7#XJgNb+xI4~ZsyaKb$?G^qEhWT472OF=x=*}@uOVbU
+<7lU3a2`|R;o7bVvC1dpRTz_#wBwo)c|iq_DojOO%g%gxE+n>3)WmaqFyl}+xTefjr^cMCYLgL(UGj0
+PB$i)DC9D{lG-Pw{zvWDM091s-7MXRxEslFVz@zEMSnkeG5HI3-A?h&yYqN<f!E;@-^;eqhRIM`>l&O
+ECi2Pn4A2QYEBZUXUs&W;JfveD%wm9{Fj~C34nt(+!6XO#{ziVOU0feVFmR}hedfK5F_;1a=`*?k#@j
+1O4oQg)St6VqV1&mXp1yoJsn#ooQyXoH@}inLLC~#2N9w)CQ;EGNTg>60k%~;>J>@NqN9%e%`HTAmN8
+T-FK(}x(#xA%4U>mb09!6XX>n=Kk`U$LupOV-%gn{d^x58@Zh-y_Eaf)=E0XGzcz;F0Tf$lcv4EJ$-(
+gY|Gg>n%P6Fetrh2BDm9rGPJ1QAQ?83P}|9{IOBZqU#VmABOXGv|#~Y7KY1t+Lbaq@T(-To-17bh4df
+npT+Xa_3jBF_Bmc*cwHsb`zG*?Y%-heJ)I$bm@1rDC+3<)hPPCC*49Q5=r@V>G`JQdyUB4Re`q6zj$L
+wZ>sd*6Io@d-9`f$BF%%8Ue0kBYk5H|nJ<P3@%ZkBHPB^pk~?hhmK@?O0-DNCpdb&t<?loZRlAp*%+~
+UGsbe|C{|(Fl!bYnr#PG~nxXGGgRFL^ocGA~LNym#?xQeYC7ld)Zv3m-`0~F$xj9xYWMOiF;D(y|F>{
+jhig?&7BThl|#UY?IzUB6M=`)`W4(HvF@<m=k?jh<U2hYK>@=53Mv#o2Yq1NJXmb@!>s-Q?{TU-nnqE
+*saENwKac#eA~dPB_oFlHu8n9Zl#_FAFh`FnVyN97*`7P_4gP9xMEa6h?*!HYEObTC!zv$;JrAtt%NR
+hW22-a!qfrJal7n^M7Y~(!nb%xl(S6SM>mL=C<-OT+<)wCDr!&LbZOgoAbEdEI`3q+{dJX6*@{!V2xM
+z#hq#sw0qpYyW%OWCU~dkB+YS@4*Peuo#=lqS-Fj7W&G1tzrg!?W!AgVe=@cL`MqRwemg?SbCXcs#5j
+x2QFcHnzL=w6U(1^#^XR)($-Z~s+<AQdrpVuw?F2f@d`W3~@Yz%&_BsXJ=DcmFsQZp%hHE=tO6-r5q{
+NI>cvWv%r!zi+xJna_p{$r){J1V^`ZdG+E_AAt06t)H+$d>Y;!Oa&INjJYN=PK0<IPjNqwswpvr+P2>
+(b01DS%>LOWrnaTjE=jMGzMtpQn6H>Pbuu7Sy1bvr^6+o5_Z&HOzQP9BQ5Q&m0=~**h0Pv2VmL`G)Dx
+Rq)an=S-|h#wo5sP1-uEg(dZhzjDt$WmY$49-g_|%2JvRBJrfQ+QNoj*34OX)Lb}kHup}xa8|PpW{*8
+<lgKVz@P){!kS&Qi#7d>UbH#HRXBl65WqRQu9C%mc1g@s)YiqcOr?~c_HZSNT)i^mnE8R<WUDrfY`l6
++$o)x%SWqdIJ3I4cdG-947%IfJcm3__2y`?hFUVL7z%r5+ExpI3L@T9GVhXFqHbQkt8q1e*QoH3%JbV
+(?_VA}<wCAINrzFIr~DAJ@G`#^7a@f1xm-St5e3A@OJy+W|y2P)(oDIFD<BYlYv-NpuM(*Ne3@^neL(
++#-z&|MrPhJ<~#D!@hnkKJ<wi%Q^KpC<9#-41OCRx`96_?$<Y=~T@-jZcY*aADSWJB-L=uUd@{^wiyY
+NsPR7G_!i|sZumM2Ylmufm!*j&p!q4^tDRkT}<xcbQ7;RnoAGKDqGr&Z$WCtTRATyuWBl?K_614=|h>
++`H+%JAIdz??uw;7CXk#k+4ybtM%5%84#q3zsG?r0eeFTIX;^n7=ky9UaaJsDQp}OUL9fK)1K%O@Xvj
+yz^vW5$tWU}qt@LtEd1>4VAv8RrA6<foThwX~H@q7P-@d}c46*lX;)X}aAOWOH+-T9&wcUk?0~}JQFw
+jabVsg!GY=72dx^I4l7K3(}YV^<{S)d;BKaX5l`QYtTrsbkS{Fg9#J=D{Bw*@@8yoE`{HoWq@+AJS)s
+I4c$0m79p9;q(|L3hPew6Go`B8#$SiPe6arPyEnkzV~#P<5Hq50H<49-&(QQ@T=|FvJ_thQ|X&WcYiC
+Xd*c7Vx8!5o*KwlG~ND`BtAmAqUl;*$=c~&uGLry%9bVc8>J<RZPRQ#LoY0A7=311JW72{w~4tiZ0Cy
+S!;-#YT}v^in>^*ksBZY?aHWZx+kUkw;(Q-hjalm;`tB(D#$4v^l1EU|6N%J`NNJTeA@WqM17V3#HkZ
+aa%y~eWai)=J_aPejTe0L5YYAsw7C*5Z&ta?$klQN^5isQVLVP^JB8i654R{bdo>t*n4k1L`oQa@*Cq
+!o$?Tou!@_0FKD6Rb1>*sFtp)OXDu<3>b3Oy@})f=|KOAk9;tUeS=MmHcvW1h^lm~)frVf394>AvTle
+&0z|@B4^^Q&<)oGoSG$&gmBD1X_osM3N5P-#~XSYGs87p5L*w!$R9_zvVVwb6u0aot6;)zw(hgyMdqU
+a?+r1O2Z^}7`2+Pds6xBA9WY>^SBazg4X?63d-=$>BGmC8!kTY;!!2P2en+!>SGU!ad;V;`xYfs=Dv$
+ADhxVRZ-Z3jMa4(a=)cwv;Nv*+<99JNE^yLmHO@BiwxW!~QNGSHbSh1OgB2S)`3v9GJ^Xe!UYs~Fl1!
+T;z<Z;_<>Y5^LR<<Y3RGF*d}dRt=iJCeu5rE{pQ5*HliJug&oC>{hi=xvN@fP-k1o>d@f!%>M?s>|^-
+P7<JLd_<t_~XAN8`?R2RLX9Icnycr{NHQgp`SE%59cIQq4mrb>P99n@vd`i1}`L*iQGgNMb1No0&S|y
+Tg1`@UweHaDk$Ltl$s(vw}B0@2PEM26xg(s3*)WbkZEf?LaFxM;ERv_zfvIayy#=3~tJj`iHao&<uPX
+l=8i)CB*!v=V_LBFnDST{P@{G1=cHX{LY_(!rwWEud`mbR}F27@5PIXpG$TN1#RbqxcxH!FoQvHd);M
+RrXXG(Nn@yQn-V5H+tFj?^y~<@a!cPg<XPueH5T8bhByK4B7`gIhlj_(&K`<El#+zQ+D@5DJe&~C#X%
+WI_A#A1OzFUB)r%`Q3p6CDvm=e$yStL=WkGqRYYkt=E4m#lOJN31@z2L@g&&O%Sd!ZylBB<UVwC^I-4
+jHL(ktVZ46j9@WS1CR=Hv42dq-NU0*V;2)c{ihc~R-9f4rkN>8WWH16io9FJR4WuH#o82pA{pRmOYI{
+jnNDqd%}QvW+R)p_e$2LaOhwB3=()9g1MbV%5-k^=(BusQfB+mC$Im#$U&{GSc+Ty>+{-Uy@X<M8+}T
+{`RgeFK~U@dPKrqNcFg5D&jdie(pHFXlBK$){jiP8ER%Nw5#Dxq<X1_WoE?B&m#izM#v0wJ_|tF-#+Y
+{C#p74#V&rVkJ_)2?&2D+03Oz`??j@n6Md&5-VR;aBIG=r>mq7pqOQ?UHMm>o%Xdv3>g1Z_Td-SN!2K
+^e>FA)WJ9^|>dis8Dtvfz8yRLHYUCpXm+%{g9skk~=x$AMSR`%T}gS&0+x+?~XM>jRYt$UL)$Kv6Rvn
+)s0lNM5a(P56<;{nP5i%XxSy~FkPhRp3j*=hO?w7Dz6ARC1f4gXrMZkLpJWlRq-(p)vO1^HD6;LlE){
+<}77`Ze25de;1n8pT44qJ&eQMO1e%zfB8vPwAuvpx22Y@_+BZiw4KtfZYcV_8s`3Bv6!b$vRW=nmx2q
+r@Z#Zu4^WFuq)jF?V|j<Jc$t9{(Z!YMM=S`{9YaL^gfYiu$@R(>bR{DZMed1PgSOw3c6(FufTe8s}>&
+yiF7AtDntb7bn3XgJXD0lR8{ySb_eWPpPxrEwULw)WsJ)xXPfrf!W0~}!wPv)Z_S~I6>4IEcgT;?p7k
+DS>|h5_$}At*6F>TR;MvPXz|Icm#|c}(CPOn~o|nkrAp77r@TA7!g@ffkA;Qoe|MM><;8Dy&0xSk>18
+*Un%32>y|FZVY4wNCH3-(1CZGWBN=6$`P2(X@z^*$I`e8xKOzTg`1t6~;s=}&ZpW@N5)oFr&yxvE1Zi
+I=peML^J}#jTDtAV%a<MVSpN7MHLap3xBnkmxjePUl|yd33?Ho1#oApkXhT6h|ei$)EW?F3hec@EvHZ
+tpF=K;v;!1dS|Q<e<n?kqE}nnE~Aotm)}XkGA)0kle&!=8UoKbEouRIM@GvWc44_9_Hkb;tNnUC9M?B
+OpH{+`*)^|nU*p8JeA|SK(fo4h49i*4k;?Fjeh0qJ1&cuB)3V2GN=9&Mr+rVB@wYx2i8xveS)%CJNQT
+DZ6CM~l#_hRDfyZ)6e|G>?ufc-wwlgtiz>yj1LLzQ<__?h``U285*<=PDnth<?;#;ERhmovd@ZpvYJj
+L&{bp-NUH;ktu5cP^sifp~if1tqBt3jeM@vJ^qg7d!ETy;z=%V75jSQcJm6eq!#jZsR4((j2I0^4Wl!
+YKJ&HS=<P=5!`)Yb;pKO4vYNat6TJfy)vfz=6<|Ni7bF^SSz&ItKS&Cvy;ejuTARjg7s2b=-(aeSCt~
+f`(YS$h)g4jtLZnK*C}33rGc18_?~T12Co*ZF52|7|;Doos1YxL{z4W^P2p%@C~Mb#+=b5B`0?r_}q=
+oDQ|cixQmWT?8i0U3sF#|&&w>YTItfHTbGE~MXLcGx>vG*eYm_<L$XE?AO7*fm!TodckvqmT@L{@89B
+NI&!}+#q(gEMl63|sLl^jx<rqdtS6|9dUQzNqzGP9XflJc)oR!?^1Sb#8448WEe?}7X+bG6M3>G<%k8
+XDlO=qGcAakWQab;^U<6MJMorp-*CM}JLx83ceV)R{JU~Zv2Bs1gn$emh$q}9Ar4AA2vuVSP(!VcX2n
+?A)laB>_OD*<k|Lzu5CZV%l)j|7kCgC`NYN<XV%qR&qFP;>Q%ay1?*)4CLQ^)&>(gE)Ia^Lpvi7xxq-
+HR8u%Aa9c<^voMI?lg<{MZ-UO?AURd0UIvd-Pr+J<~iK*rg34OZ%by~+6|hUd+!EnFX;+ecp`6sKavn
+n*MQX4wEuB4Hwh#~30%4+d83h16#2yq&qIQpoo}-j6giWNyUAV<=d``|AmHZ)NaNKdCIw)KM-7cRIED
+P!Aqt_oDc!c=N;}@>=juG={3j;XSh1MPis%^FARdQ;U?RW^uMH9q-88}XYLZ{VHQi43p>Qa70^>eMHy
+6LdEDoK6e@E9dMQ8Esf=+iZ%LqxxN($a4Gq08|Sd3N^QOLfBc@umra!<(~I6~e2=N#8sx2)qA0pQ9uh
+cM2q^(EXNQ?AFlC?+f}vu$X6K-U2Gv@!P#DYPXA$@WAIT2PhqXpiKo$H@UXSu`v&9^pHl?5jfvpcF(q
+dDZUVLpwZ@ao<)mndxj`S{##hTkU4ZcG`^qeYpOs0vuEp5dG-nZ5Y*P^+9MX(GgD;GYXicwu+k`JLpg
+ARI4tDzgMKpFb1<D<+0DRY@M*94&E#faCN5IF>o&dx;U=4aB!s7t>?J?;ddwwVUCTUh_@~Y2!V8Y{|@
+6@__4*t+p{O%I1UN^KvKgGCpWCEX@b4oylT6S`&z7^x=te{rj1&zXXleSOa|T)ns5a(ih+n`+bGT!MV
+Z#;OVcpd+_zC(=IVVL8+OJo+9>e0xSCB-kGm_u9&@NAyG$!=<<Ecl)1M8n=uY*7Za9uwx`~z`)<T!62
+!NLuUJXolYTl3Wp=K*1TTihDK(C5o^|V+E`Q?GL+9T~DpHHjuchO%S-?1h_K={UbVb%WwCKK}2|JDim
+m9W6in+a`lU!HN3dlrMfC7-2sVlSTg5cgFyIP*Xg2RLqgcDYwiP_jE?^@cI7-=IDRpc=54wyo(&g=@?
+KWl0e%QbodghZInmZx17(EH=sYWC;ft(x^9S#SWbg=Ga?2Zy<2E5>fA@B`F@#I<POiott*tfuXt1)!D
+yIkC9Q>ZF{T~W4ol+WaZB~&?y^PEf7CD@Y!MR;`NfXygxZFq6&CP#PgHFV_+j^MM(r);q<bV@3vwjp)
+`jQae^d@vz0JBAV8NLK{F$u6+ZtSAi~Ztf-iNs1(MB*jr(q6#a_UT|DH%Q{0NJqgVf%(w3sSrARNR33
+wV1oo$3e`$pJ=e7Ged=F6dkcrm|U?0+-Ut6+2)k2Cn}ZaoL?ORY527vREn$&%Jc4$+D40wpN=AJ5z%V
+Q+(uBG2uJV<l*$ukg)y+bWn7)HInNf0nQ#fNg&RuqK^hRB{fs(fI>@<PXsPzlWcS%;p(Jkw-DZJbZdA
+ZQS8E2u0|Q(PQM*W89@_SxA(z2c3nA}4j9cQT<LR3%&ltXWh$6vHw45d*|YhLWero+CesB1kp)fUxV6
+&p{2@;_iC6l9B*(9-mtq|-S_g~`ysLq9#3-7WZa)FI)LHyExyA3gk}dV54h-TL-vlcae7q!o{jppAT9
+>#6YM$3u%3gJ`S{7B^93Q!7)xDxQPk_0^?8&7@G0s&P%gHcF@=y|3ocGIt^0GLLUjZ7}PBC*Vko%Hs)
+<|?Pv?6ErB*$|`5i@>0h1}aic&bDuS=U`A94b_l%5Z!XBYV|xK*to8<4e-yr#iE*KK3g=j+9Is`$S}a
+auS&#MCr8FuyGds{g@KuIv~u`63T<3h}pQQERtl^?)zs&UBg!)T>GfZ-gO!PDj$m0EA-uQOA%>~Mk=I
+FAGy+aS|P<X9GmZI5=^Vf1XZs15}9L2T(%9_(vWXy6EBS0ZaR^D+}U!#TFz)KA3tecq1kW|iR~sB5w-
+u>dVzN{7Wr{L1H={`S@#-dGl|TL92EF}@nyn3D~bzLIJqpgS``)dXTv)x@U$*Xpp|sKm7Z%Drpf(tob
+oxYBuYz%_Ju!A@xYUuw4ff##R)X<@(pXih)Q&_>fyw+kj@y)H{VOVn)wtDx|vwTd|VEYvlv~%E?Lvjz
+fNuRH+VS;p#U#G_sz#&trut#c*Cu#!nmp}D|_8xvfEC34KEe254mqS7M41*neSY8S2#QL2VX*H^he;Q
+V6B0MCRYjv8fW!1MkMcRq0>BlN|isl;ffPFOHCV=wF*BW$3?!nvjP@fzP5QbM;|Al>7P1>a5RWRy3;m
+l&Do1YG}n@@L9`vs?XbKEWziqJL?ycU7w79)h6k>EZB6M1_SnPo??&$#Ij(X{k}^PbcHANCi{QfRs)I
+*w+mL=})IO9zgWD5}%wM3H5}Vuc*YCMx)6fq|E_m$%PlR1byJsKab*9_Kk5N#|ur1%YNrC8vrxpq+-V
+FKS1Lu2#%X{|+NA<hW3s9iAu^)M60}2pf2n!z57u|pgLM6KRz#nvBBM~!BxM8`(eK{8n?Q?3(Z9v23r
+5XE;>8OLB(76el7Z-Sa#7&Y+)uT{e_^SBswLaIa^PSIP%e!<a{K%j19ZF?a3szI~)9bX#p%*N{-o7iU
+6jf{wXA$F6VKxKRo3iWsZ3hN2+~n#dHJhJGW@#IvRL<!I_{e&r?Ful<91Tx}d;gfAb^x>wjC6qNfcs#
+5YrWa45j2RQ<1CNm7I+x-b@`7St+-^1LESWXKXq-u_98`Jbv|g5wujZ8xeiM)*L6az9~}LPob2|7fgM
+#}P1_#g8M0%0ZQ)r4$mxg~anRF~-k6jipQJsc58}B+mE1vRjcLxALTJyiGPXBwf6j*i?mK<qKYO|@?<
+m~mY7ahOqWS&!fi)f5s}r33blvd6XRX_X5<k}MHuB@UXS3tLqBo=emq1cG7J5Ac38;!NImtV8;vduh3
+f&@gfBKZ>Y5nPwAZ^|)AaTI4LE%(;+Ew0OV8&(R9ruM_`KofO_7!d_7eR;GjmjVB*}?wqQ66&9pUL!k
+Z)fYit<<|sOZr19^Ix_ZxIqZQ?cpxotsx}ien;Gj+@6SRwVKYWiHC%Giu<=H(+6xZCuPd@MuBwUqJ&O
+GzbBLM`=`&2Pmb@4tS=PbF^YaKR`fkY!aZk94JWxFZu&hq6CKyKn~^KRc6a1*(|XA&;iHAuHPVtJ%{<
+EhIqUhHo5G7c+eYW4!2q7-2Argrtw{pIgJu?Oit?h`WoN!7ic&bNNCo0%vW*YqX0uU!AaT2>&=YdPT_
+O3&*#w7q|8AO3NQJ+ZtlGgaDGu+@00hsRch$F{=304?v&LPejYZe%i08gaj`FUeW3Qd$zyR>R*!<vYa
+4Z8P&!u_AsEk6|_5@pU_E#Ehv^qpt?$)Y$%Z)r@1)I%%!1rR?a<wP(#p!*-htA?6Z=+TkrFET_uDp-$
+btBhM%Heb%RMJ3qE(AgAy_a`)UDAD^b?GH}S@q*_xSVm94l`LPcyOih6Vf(6;35A>L0F+XlSQgDD}-@
+G#TADo3TDIuer{Z)gBz%=ZzN$CGd9;+HFirL*y}pE4UnFND%j|@lAAiF>8>KE43t!wX$jd|?neKV{u!
+E&1Z;}m;aI<q()u1b=QKBYl3SKKl|sCr5K+f;c8h$0(eIw?F}(s~F>kRO61^0Bb?NYn+B-x5ZRd8yHx
+H5njI?Gbi#rqHswwu&9{2?)yRK)ruH+y?bc$aEQhM;AXiI)XkZaY3Q63|iDF>b1#4Y0>r~>Chc{WE*l
+~0tI?U`)e0QijOEaHrD886b=?>hCl7iF;|y!B-rZ_&&{@%?xRZLTA4r;y0{WNYs}viEyThNouiCe<cQ
+qG$YK*WWt=_BjMKN27NbeIu|&Y*|xP3CL$t_2)@2#pm0MQ6*QUFQdN{<3rKf4BJ9$BSh|w)4uy=0tBW
+jUPNVFLe&Uy${kevne!%r0hwrpoM9bYnOB@QB84w^eiuF!wW$NE0*;%6FxI!_%}rYBuEdpnceLDwOH=
+|ViC3PGYUz^<8&F{@7e74W6NJZtqB^--5-Oy-OXMBV^|~FfYu0+wXapb(2jN4M??wo}pYcGnx}kU8ay
+<y&J6TtE;#Y*e$z@R_MG<;x@&&{+lNE$E<`B{pYsRiAfhD7Easdh=yvZcNyveJkJ)!f8p-E5L%vmF!d
+7$fKX5<=fJqR<71Yv2ouR&;Gf&UV-e!i4>MPJ(vq;7tC*R*Gt1%eF%W{0!{fR2^HjcR*wjtI^bI^aMe
+%q!z7V{bxk+nu>-=P6(E;Mz1zSixspO_5qR$)}iM?bOg2=GnSBH_S0c_fR~;q(-CZzhMI&pJJMiMzmA
+&nYfy?ldi@2mcwWq`HeJiDoAX_<vGQZ@Er}gu?PSB(!FBMD-Nu~Y?GtFa1d<2oI@b6sADSoCrR2+iX{
+n4JuBCo(m*}4_dMF6k_4RMUsrGJ)A1p;#Wt&#pkq`l{%idJK8{_?k*?J>bJ9H2VfYL;38Oik)%BL=2H
+h~3dOK&AAOe>xo}JriCj)xLVT+IIDB$j|lm~Ag&2d)#<@+VK1YxV&?;*Q5>ASv-#cvb3_H*`e&pBpSQ
+g(-+={&#GY*0>*>)Ul6Y`7k9NL`KxJ02hho*M6n_3J)0rjrwb{#-?nYY!6ZwL718i*-}e+}UZ(CxAlL
+D;$Y+8uj`PT>QpaX@wcA61Z-W{F{g=PS&6rV1kAyD?qZ3WQ_7W#l&0dTBMpGH#AHK;<)rdonuK5u6e(
+r)mBcw+it5ixQn^APum(Wr=5mcW-W#O?DkG%x|HPO6YeTE6FvbyuF>VKN|$MdJ{N^*?LoPHByXR(K}(
+IC#(}n)tLHv9KAPdCVJ)}TbQ6HK>oeX2MU^;fh98v$-GKJx>_9snj~)DaIOT1(hYjG+x`9d8588w9J?
+jVrSRdH0MMv=Y$lhiV-f#sL`*NToa8vq5<Yv`>q=V=?c90|KlQ%?C4!j}O<Lftw6FYC(acuy-98}_}^
+VX*T{}jjl9YOq4nUpKqzIGewl}5QISvtT|Xn?oPUQdO;LaZ{I33MOQy2taygekR9-(3U)PZPt$N;ful
+5MlDdCA!eQu$##86;Y#hq=wSeHH0ZW&`|hmiBqE6$#gSk*%SBUw2R`E(^oUlnlVZC=4H1^Bz%hnx&D|
+dMStrGySm;WpQO_`ds`!UaGiHV^9+s3F(zt|t;Ix{v}b>b2%q70p5Pts8f9*U25J#odu6k^Jb~}En}?
+sMcB=74d;5-M8PayB<-XU2r*qZafTKDvMW2g$v+rEZ-ZKLT&DxrT-_sf7pJeQ;4sY+y1TKM&@qLmUKU
+6tBiH`FvW_*pSsK}%9VuM!q>O7Vghmyy!vbp>(bM=q?=U<Y4nx|i`D?B=&2+kP-r3aGOZ%s%($Ik(T-
+qdky-tOF5=Ct>4(Z1_B)fczN6up<jDwBuypH}JU?^i$vT!h!-E4&NIT%RJ@PZZ(C?_KyIeuh}g1H8I_
+V>eoz-`I^TaV;l4dldq9u(u51iydb~KpOkCew4-L*J%2<X^Tdma{W~qHWa&&;7U%b(Ll4so{S|s+dsT
+Ur<*o>L}L`#*EwFy&`RdyM<OB}^zsMV0=4!QK-L}&C1sao;|g{SWHdX78hf^PXqZ&(&_Y+<#W#>p=64
+JH&E#>DCXZ07LDwp}iJS+}MOSi>LCGa{HyQLD{+yVHXWCxiv~w6@CO1l;5h51B2^gfnGX!Ouc-y#h$_
+v4C5!13wd~rX^9aPuO8Swa6BIZMt3NOj(<g|Z%RHT+rA>202p;!OBffewc$#j}((k$|37LLuOyvsG(#
+Fawh$*o;WU)m}&WCpB}s4oT?QEpB-4VIE_p>B$s+MXR30phPyJH6q+rrWM@I@EQA)A*J30ji+$nA@$W
+23|d*1H_a{*Qq@kN?=j7a_*pv87_-xH?wVIYMbXKCeNja=Pr}%K3hM>)~`d+`WiyAdS(1>3;Ui7<F5J
+i3$T)Cze<zzmkHMj(P;NCW_5*MX%Kgf#nONbi^nog_Xj0<lb;=K?m(YvcTe#%G?Bf5vZ42<$j{JwKZd
+J?*BpSwT|oLfA1+J}jP3byQAH#&-0+$99yxGTELoi{>CWIzUOYspx|v9;4}i_tO=af!f_lcNaNbk!)}
+Ji^Dmbqxcq^VeD^-L}vkFJDTf~-$`$rdS3)>7y)a)}<WU+ncfH$ANe#uSKF266}_9{EZtO&vnpOdCS=
+#20RVm_`%McGNc=e-;ttzT;jc!9Gk=YZc?QP@w)p&@rH=?tOM$MB!+<+7-VZKLn*b29!O`~6+>y|Qiy
+m=2vn9CqOgIS&?Kdd_WpcnFPjy7vBuH^iC44Wo*4lPe}cB^chdXYnrE1R(3}n36c4k4+w6XBRt-9Ssw
+1q%#Sl)8f%TR<rUz(Jh>N>09az=91oThTUfJ_HS7a$fa^K>|!5Ip+m5wVM_Mji=;lTdY5k}RP1~1;W$
+fc*kwp#sha(z!@|{zTyK(bAB;yXHrr`2>AgbdWgW#?RW!qD2mPgZ|J~@x8u#h?9B<`8D!N@{#Rupntl
+m9nZ|r#&>khXXwT2y(OhpTB+srPFs59}^B{<$^n&Ex&c->6Uc{k!Mw-#szje+M{K|YiE<$kwU*S<Ccs
+P3-Upi=L8a>YsZX3nn6wuYQuU2WBKUkSm`U6P&>PCFo!PvuI1;`cRsotY>OJuuoQdMy?^7`fNw1~h1<
+x@T(-)@N(!`R&gyijt*^oV@pPj~$v$c8axB#xd*nyexiVu)o*i)6p3+sIzoIf3jkB@#DIviMdJMb{3w
+Ma5Q6hZwmLDNC-+o956{Q=?}S7B;hjLh~Y)6@c$X(q4E#u|3~!yH{oE;zoi;~r2qd!|I6Te>TOE@EyE
+B0Mj2Jf(h5(o@GBs9&HvUh{>_Uah~v&C<Ylrd33286jL=`vf6F+_!lA)VMjKUCmj&+~d`mC*6}js({$
+~{quifxylcRC2smHL+ENkHP$T0{xPX4{F>JR_;0Tu)%tnA)$R#`l2X#VF>oYjkLyE;dO0KWe~-@8%J)
+Pcs;YR_2T)$dJByk9`A`VxQl)y>moT(en`6(!Z>zn{|Yu)52F8v>dKuZw`}pt3Msj77H`w*XF>XFV`x
+S-JvAitvoz-^CtxGo{X)0fFTSR++OW^tHDJp$B?ark{B|!q02|AfWbgRpdY%e5|aLFvQ~%`8W(x1@o*
+xpq~LX^UET;WcncZv(RxQ>3qJ1Rgf;ui4zf!XZ+zS`7i)V)vT6fypmAbhp*#4cEO?4u<24?8W8w|I?`
+W%o)v|lffw|(zs5SB#i9m$Jr1jJ*4B6uR%6lD_#v!D+17a9Ut<xks|o~`vurIY{ve(_!6&`-VTCRP{k
+^2Gy)~Aslr>({*RUFBTTp7fp<TYWW>K!rVR0_DqH6oQw{9_?gP_9ez2)C~>#XqlHiZi5I=4U?#IweA7
+MJ>h`XHW$uc&HOs9pE~KLdLJI;lYMN!oiA7nWa_XKP@YGg+(s-B*{3kHy))$(YnUc*`I3)TZsN<QpS>
+f8W0=W@*mia#6-f3QPrS@+UvYCq4DCSe&W#`IFH4=o}#TCj3r&(=WQEpglh2$^=G3sKSJ;O6bLpw9=>
+AOV4#X`#;q+iekwZFJ3&;v!s)g?O4@9<|Rs1tbY5S9xFHAsf5?QbX}=L@6D?@z22O5%kmy9wdgy~Ro5
+!McD+|d95TP@GMidAJ|gBUGNHPb!sYOWHLBuN&OwRuoGqZHrXh2B!M4@-;2`=R5!d3ls0i}-Jf3kWKm
+7^4D*LeRQQXn-q0!qxOBZ}$GUJNo9#Van1-X&C?-+((HU7IJU-NBsVH<nXRPr6#M3H@GZiN=<moQXdG
+<4Gr?jId}I~WEXM7#6Xuh6MtNE?os)Q22k<#*dH@Z(8UJXaToG6g`e&5!DI1On{&G7E9Q<r*Bvz%j_)
+>Djtw&YRkKuR?oJT`oGCw>3vklkN^f#s1#f)r5VfzpAo4Xhg)bodfSUp$od`w%tas;?oRNmAo5uL%fh
+}(8sfjyx3$cS+Img<xMI5yzibZls#YO3HuCCGH=3~gJ=>xwCW9F?Alpdj}vwH*;0^KFMhh*#MR#Q-Cr
+Hx{>?)|DS1R%<@ahC`H50+bcR>wAoF6&D#{toQCx1FaHM5cch&9;{zd-R<&|z#?YUyDv#!f+yyNaQeI
+~kA0a6G~h<_aFsiy_RSu1CoPc(O~+cxFL?Xcf2k;xIP0_S`GQfO-1ms_iJCwJkF?beMB6d%%jZ4byFX
+Q1N3p26PBR{hwLDLEv3)v$g%2m10Sh`!}#wSLcTt?U;(Ue&uOYGDU&>df<Q^r0?RGz+jrX|Gm506X4^
+dN(@ZjFdcFSrVPGS-b}3L-*REM{5C(BA$JNyTBr^{SJ}weI&$f10vmJ24g2N5-OlqG~LUIS7dSdAlpW
+`il;W}6sbV=p89?0V*n3KQ~nSIHDD)1&2SRq>f+hcc1?TYNF33BfQl4$1L1bp;(DmyY|m+D_mA&(J&V
+y(_CU8omFWg_B^R+4NfQFd!?gp{oKQqi1I^~(zLsH+zDQrgq&Fwg!hEyeybaPQ^!0DOuHYIuI<tbrrN
+5H1hO>BH@K($kZ^ZLkBNGJRhl6Uc)7$gwkQ}Zxa;O>bmx~<=-Ng0YIyXQ$QM=!x<Olnu?q-t9=4+KB)
+M?v$5JSL(U+r#)UJv5{T*%?8sMtkZ2n@X%Tjt+p1Vh6JRQ}SgJr|Z<&Z7;xS2pQ5+cuY||D!rVX~T>F
+JJMXBM(hCDwkQ>qXdLI;B__W?KtS-D6j=GRdrta<D@j7vE?ez+At-i(YmkHpHQ)R?g`lrlK=D{HzKCG
+5lSyO$YJHzjaMca;OUD2jqul!*+GC^`uBqM_N8LYW6sG!{MRpK#>t`2NFmnc*7L#JO#y8iRTiTGQ_nc
+bVtFSfH51QNAx}GeGn%LlPCe2W6H9Yn98n+CJ={=wxH2y*9#=e|s@7UT&eq}6pm{9uST7}Zz`1Tj%+q
+Z$kN&@=|#N%sa>R$`{y`#*?&kdvzvb8kMpu$W3jrd*L)aZ<%OXc6l%oROpet3%Eq&CIihgnR_JGSO{&
+3$}Cp*Mv{b|wqIDR@#A6DEoC0_?H;ZZ}Lcj<6cxTzQQi*6<QNee9Y%ANb=)4U4x6oNy2?+h2CUry?>N
+i;3@!LPfS-=0ET>7g(_U?P<ChntornRuHpJK31;N(woG~IUcJC&9&DbL=Fc%kD#N-O9fZj6T5;Q5K1*
+1F{0))i;?O|mJU~{b&t)nJ`<Kw(v}*`!pCOo-Yz&4+Q&ZG#3X&HEB@C`6o0vM=MG)qV?_Sq@&*>OU9q
+DP=Q<z=#Elc@5jthL`3ABe;rMt&PV9n3rE{e|+F21q%e|C$N72u}Xk2B4{gABZ1in|N_~avom~y7XyU
+Sg}A1ko*E^7XskJX+c8o03cuH~A~KhGFXCCIxu8ja`$+LMX7CXtjA;1v}i+2sENP)h>@6aWAK2mtey7
+DqbIDGUV^006~6001BW003}la4%nWWo~3|axZ9fZEQ7cX<{#FXkm0^cx`MhaCz-pZFAf<lK!q=!5weP
+GqEI-!&m6mx69Y6txB?cd8tjEr075rw8l4@(G4kEcS-*H0Z$Xayh(PFyWKCVD)#`5?nd{62GAUiyUqH
+}xw+{chKG%~xp5YE_v_8z@VZ|w9|mzl{Zpr1ykGQ-_3G4lSS^}$D{hvHL2UT4KRFq7HbS>P@XczmdMk
+%IW&XTbulj+nhF+U5_`ttd_x;~U@Z_ZCy~uC*#(CpRCX<uP7muzRcxUP(Os}1v9B?Ay#Fmr0;1rSJ6b
+U$$;*gzc<WyS5bD`z=p5gcbaZ1CX*}WjqcDx`3C$=&P*~xvw$qR5Q*KjJbT!qF?1v_&u)-v-DcIIhfI
+9Ucx4i9C^Poh!XPY{luq-H1|wfv;SP-^1xQy-kX)U*2q=JE+Kv{K#3b)y^`rK|0fb7)n*wiCF(CvbB;
+SKwmyATT%wA<2YRCM21}Gj<sK3G%?m<S}s)%gKmi?NAg!nHf7JSq9D|L+c(?OmjjX{)C~9Jq+2X9#U$
+BiAki8rU*-uY+*&5YOXoa99of|YBwTEn<y|^QB0i7EO(Tp#4(8!Q5r{fnIf7!D*Vyas0g)i8Dgk3t}+
+)K=Ce_l`Ne35g#dv?CM8%x!bxs96*$Z!En`R<mYU>eDST#`tEb53S~!bn$l%P19Gs#s>IF?vS~mzxRv
+Au8QKW8aInYWySKCQFIF`;$ZlrTKmior|)SnZ_w;ZzUn=>Kx6XF=3(~yBfxr$b)hhj1q2S%MROp{CtC
+xsR!FjHpZA}~`6W=deDne{oNDKhJGMpI;ZP1CFx8){d=PDY4jb`NHRSmqha86lQg#4<uG^9`yQA(mOh
+GCv}Yo;~w*fLVye%|f%dSxA%G9LorwEJ-Z~+cUE{mNA-?nS(K7j5zva%UEuNM|E?yWOTA+sacdPO^9P
+BO-U;=XF`_6#4+oh6NWi3JRuIWa*JW^!PVSim=lJ%MJ%Tr%Yhi#iICyBg(W9ga=Z0%f+e@G<OEA@vn8
+i&$!%`rlpDFpjl9sgnwK<bX%5(e0xWEj6ck`#11u=O!UkASfQ1dGpl}KsPDxilIWI>yN?+d>C1q6UoB
+N^+pk4-MEy{${letz;0y7lTP-2GUC3S*^&cl-7VlA0DS4+yD(k4ln(iEvaMawiC?WD1hNl7NNS_N@TT
+$zcB_GyW=fSt_vLuYttFS3e4u7cQd;9(VMR6TjdovDvp!{WIGajNl>@?^C#4tcJcU4Cd(WiupBY=oH+
+&S*%lyKI)=cylf6=ZHc!%8?Vex{>RK9tw>cVE{W}sO{)$RJJM%MV=mteBB6jBi0S$YE+O`Y2u10(%9E
+_Vy(`!KWU;HnYNQsfN5pyV86?cMBB+yEu7^T$_*N2Ha02^LZ#y>Q89WY(@El$v~Z=VE~FMXm1mZ+@)F
+|cqtdJ9BokPffMkNga6*y^^_o@+UTS2tUR78aVE|smRy`s0EHE5g(DEbK2D4K8lLO}}x57CIn_A1OC?
+TuDw;YlwG80!xGNsk3$Z};ZSEN;qZp^*;Skuau+Jo{VGehHnHm4d}e(0jhr{@CSW%}gz-IO?$KKXsx7
+rr;w{-ilpr-<cIpdK(z7Ohyn-SGR{MMKXVQYfHhW>q?7wlAMG^sIB5W6nswS>M$Q_3WaaT6iNLg|;4D
+e0sp}?P2HB>FL|kGe~fO1zQy-qZ%|dXLm@KTxhSWkRA-etk5NFR_URf)OAA3R$!`9XhoqOiei1DMOaJ
+uVXlX$D2StXkt{W-7%k1w7L_q^63d~Yw$zCzt5W#GTzytDUtj0U#|^{+U2L&HAES((8CYoMV$qyRSaX
+*!x;a>)k9C&ix>4$ehK13&%yLsGFnYjX_5p*@0|v`As;to01EVJ&R_eJ@cTVA?{y1qmo}RLyCr#<-PX
+qI)kOnbvY!yh0lA4__D@@XgZS6>rrY6!<MM~-!Fig$OA)7QsZi`xqE6vRm87)d?79}%_lF_1Mfvp0`=
+Pa<bBl(;KTg4w$ilMpWvV;n|#M&WOv)rJO<)oDxG_stkQnLh^S%RE=&fTqIi^`_JtZ5!Z#IYqBJ+9{#
+-aMqTDKtASCs*^tq*hL@=JpoP$<^FuOHQul7S+5U%Y}KsC|s(jtj`7cTv&Ju^0}}+7vyt6@TyiA>(a_
+Ir4`jZzA1A{TBb6v^@^vfu8f530aDTdQf6kqmw7^kiwTGBE&0JlH!7cQU)hLtgXu=88#?(bdg84TvpQ
+805y#Y66+QM>Y&>aYD>g$^B&8<Ds*DU~u@=t8(qT^3lKjw@id%Z-#7^nNt|$t5W3cg5XDQC`cJX#GY=
+7hA)3-_XsW75rJBloao?9dPtpbba`8P7>B`b+jnnE-snKZH-l1Z&hMlzYTlhG-gS(%(<a%(3iJGqr9N
+T#rMO8S1O3~d(B*9&D~I8>R)kMVvJkAqmX=2la%WaHY;3s*NhXZ#9k9E!&JQ6^(FHu6Ycr6|3zGCtj<
+zCOC-iWGFGmxfd2bf=f(&r%HI9V-PU=kL~w)$~$-Yk2BxUFX-clam+D-xkB|`eAT}TXC}DcVbVI%&D(
+6WeS{1_C%61MQ$WHyxdPuoWnnjk(^OYQ7tz8@WedQtnzE4zvSGAb2-WVVkxGZ$&1J7<yHGrdRATk7@P
+(_&nB}`@@u|&EB3{&E)NvW4melW(uhoP(K(GY^v$r5;e5OPV$ly%#pq`j2&vJt^m3ZZ#USp^dntQAoj
+Kil<0yu+SV62m@9&q3VT$->*HGWQS8CMWmnX9q2Dxtiuxh{LLvt&2D!%N+*3d@A_*OjLE9L2CT-7ZQW
+*0jva5tJErE_$rYVz5R*rFRH7rnDu56)NXmAE)q%FX+8et$2Qa(a0y9=~1xc{Px&oseMY{OxA_aNkd7
+*RumNa<ZjZO*ii^&%EoI^X84?9nMAbDGB90H9mLd2XZ@LeY<%O2NLy0@c%SiIk^hZs_jnA`(A7Y_(zc
+`?@o3^woq7aS`veAGrQ8O_2!N*7qsh;OPlxOuoxaTD=9T5-JxVNHntK^b*0sQy%|Jn1&(Hu8nH2r@hE
+{yiQvD!p{9G7l?&N)(fV*J_mAxV0Wj>jqyFAG3Y0@UwCLqxYt^;2S5j@yqQh6MW0jQ1wRFw-gI;mv@~
+i#qutRSSl)dX6(Ra?PZBG}f`)+?3<QXWOcD;J3-pugj^1q$_;T~^a8hxtu4zJhmg*qgi?90D87t@nF{
+&+LK;gNnU<W87W@=qR$UZf9{yG7s2(}ec-K9D=ucQz6k|L^2VTpYpo(mmalQ=(^%cfFMdrzRD|>fugo
+_#o`6T&V4K)UHksiS_9&rrzEx+n$cAAidoJ<EZ_870k1xbbWkp$ZauvyZ#zSmpquCL-6@sAiW;bUhU^
+G+WoB3-ip5A_kw1XQ*NfC>TG6{TF68Bm+Q;8<=yGLI(pbiqem4zGw}$|<npurdg6R0>kYa*)zLzLnNw
+LdZshf(fg;6)9oWv&ooRdAf1_T1FPa5k=5`yrai&xP{GiD175Q9|SBm^mkslTLd2+N4pufqb61i67Cq
+<@;%&4xgsjL=JE#`p_7IyMgor`FkZ^ZqQH<a+w+SQeW8g^zU+c1!{+V`^VxtFC6-o2`=jZJ>CTCcvpT
+Ye=A6}i<3mfgzY<>i&E^3G0QU!48v>gw|B>gtF8xVrlB?CP<O<iA8Ux~s=FzIyE9s|VNf=V$ovK|6E;
+7nCU)cZxOv2AW3E&O@hzN*7T_3D4y-hx!PV&YUxJYOZL5Y32<^TIzJF;|DO{hv-BY@skj=25lAPb48)
+<w=slT1`44TLM?`cr)UR{1J?yr!$F94h;}jug{2@-dO;JS(?p<*Ivlj64#Y%*j({18J^++K$5kiv)ih
+xU(_xr^(ny36nlxnSAg&M?g|$$$0l)D<;Zlf+Ls6q6(CK{DiRMZ;@-Y(mY9tD21b9XfCXOO>YOGFFGf
+=Q#Icft%1^7Zh2(v^K5d(<KRZ`3+makxE0fZTVWnoAig$0WMKa12tve-o@fmi}FNlPphGGj<6#uOHeD
+>qo}fg%=G!%9sv*O<9h@H4`PVaXUR8N-sXhCFLA(k9e_W{W8W`6pnxL)LNE1NEsBfY#JeI^2Vs+*5Ay
+xpI^HQ1Kz_$Dr_%10U`yFL^)~g9wyShl9dPjwInoOdetjasbC;#K!>~2XH)TK;aMo;{c2UFkVB3km5j
+!0~`)iIHAI^Pq?6P00Hl?L^W2rM(nlQ5=%{0Q|vWjuMvBV*lQm^)_w@esiP*Y0~$ftHHBRx?3%)^L+F
+GFb)XYsGCBonU{F)?)Cpz-26a*sO956h3@~iOnkAsH#W2iC9s8{2A$4L<vce%&!%G6LMsn2+maT5!MT
+3wDzZzLpQ&!ai9m1~xehu(zaOgCia<2hm4f3QRkQ(Gh6NI1`CRmyfQgnVa*v1V3(g2W#0BHb713(%e(
+Ey1C>Chk%8j7buJPiVA5J*FTG}!nJou>`X&<45D2$~SN(NJtHwnvNDTEx~OwidCq6jh6;T3Y%R!L<mk
+rQq5y0!^r+=4vUp7QwX?oB)nuuEqtOZUU#9z||t?JQdg%BJn{(Vnv_~loYXJM1uJRgD}!WaH#VD6nY$
+f2|y?~^f)HO0WE5>fumCZPSM1mG=S4ov~aHTV$iXpM!Gq5kOrL}fhM5ifC8@zz@n48i<OTZ?!zv@&C}
+tw>KN1!NXIdKS3|Z23oGx*qfOB!LZ?Bei_uY+uji>Xl{5fNKsjiusHb*-q%|m5KGXsa0|5pC3<MYmX(
+04LBkI5|mP~#KMWZ<=ri~&{2t_0mHJ}|jF<gux8<Rzb85xbM%`7RS4!q|Lbs$!|=+u}--F=e6?}nliP
+!Xge5Jg8(bnppxpw|K~!5}68tDq5qk<No_SanGSbubdpNPv-mMu1iaWV(=pHlPZgE+(NELhAC9v;{@T
+UE_gb0JVwyfNUF}(_*A;sDlwfVjVO)jCYDPM^?<;mO8=(RXcL-%|p<bIt;X-P7B(hqui90pF-1jRVqm
+ufhJ(78pU^gq7hgDIxIv-<*=kRbsEqP9S*Us1_cYL>dX0MC%~Gpjw_qIR-HEH3V}}kUB8r-wJn$Nz25
+UR^M1Yjk1LaP{TpBXzgNK+{)g1RpIFoa<S3vHPQ#DYu`PUx1QoTIY_E20K*!pZ%2fdIn`_gZ3X@DCGE
+-sxwxaS#oftz_*NMNlLWF32SQY;E60iXs3%$eI?(b5_eOe*5Q(}EmJ#|<qeXK0nu5@;b8xJ(tE^(s2c
+X0zBYVrXov0v#>b%MHMt2+5-dGXKk;$!8-uPY)B%LkOsdxe884}L{q@WT0Kad*EI8)vx1e{ztO!e1Zi
+MT>V;M{0!0<qubn(v?TIkpEJC_CM*_zq@Db>g?*}boT1>{Po3~KmN<_K7aA2-`{*jukD&8?|bLZt8U$
+kp;{~lv6-&xccL-h5z}{qcVaahn{qSx?((X|+raQN{CP5dQ>eZcy}4Pe7Q@ZWv=__nR2x#h_ixneu_N
+Dbwwv|6tc6zn7wd+jBzl9_ch8BY!?5|Pe^Ty#Bi>8ji}gdj6b0{Z^Yxty=HnVtHXTOka@9U5KL%E<H+
+Pi~t^K0)je48*^aS6?HC>8-dsq(wt4b3{*zNbT61ek2Oymk2GM`W)bhb5!vp30;C`rvH$BvuWgKf*e-
+pHN{Pr`_Vdt&T(82iXtANEI%`;hbIR-ydk&o-Nzwb(R5fw!n11~DeJdK*u)zn6;0>)Fn0lc&~Dy}46*
+=l#GpL;qnh+)gKtO6Fp+_X=A7C#Q4Om`;ojz0>fOdKX)Zw~LjdOgOMGIo<z%PrL7s$M5l#vs*7@q_fk
+b$s=9eOJWnSdK)yk9ftdh*RMZ(_;CKgKVNU&zMi|T`x>si{uRNfw-)Mcf;3=f`gJiKykr{RV5l&M_4w
+E|Rg=v2m%3cy(EGH7{snf&4KUhmWlTpB=E*&`2PR$(PtMLJ3NvNv@OJ=vfSZjy!riP_1DDFYeJA5u4b
+n<~D?XY+h)D{llSO`*S4YYh^&-mAucEflQGy3(n63OgAWxnEy8+`Zrov*QUKvl}TC;voFPG(wm``T2j
+{@|qcpUb@`R<iW==@`t9K{3EN<^G3JYG0|k@>xG<b8golH9%~{}jk@a2-cWb^k)|<j9Ym-@WKJf`?_q
+dAC@#e7T(N**ls)(g*d{P&xnomtQ#d%GAeqr>P>;>qfPrZ-p!<4A(5^l-@V)<GK?Sb@pCYZcQ<JYCj!
+Z4C)l#vZ!E~PF_xC>USRQvpA6cUY_)@@BEjV;8U{Yxu~QYr_Up$96h?p$db()dm`DxY-^Rbe^RE}`o}
+GL>}z3%ybC*ahu`TO|MF`5p_DPbvm|%FEW?zMkD+NMGLSwC-4VYIFN;H)hI4WN(2k5^Y%Po}eBswHJ@
+SU!Y(cHRwY1xtciY<kJ&eC;pFRo2r|;J>iT9CzwP(tv_EX+bqQl3X%f$RDV!zzmuM-Y%*cQg`aju_9o
+g@16D-q@49(4LgJ66Spr+!)S?B3n}*^bJrZCC9x+fH)r-+|+gR#A-VB~m8G)8}Ee<vD(<cXYX|&s*Y0
+S78^M$#<W*;rHs-IO?~6yU09&yL})4Sf7I9P7u~B_0y@tDh|IKTG8(am4rI<@Oz)x$^QaSO9KQH0000
+80P~d=N1bYj3DX_`0I_=j03!eZ0B~t=FJE?LZe(wAFKBdaY&C3YVlQcKaCLNZbaG{3Z7y(m?LBLA+sK
+jM^($bgwnWAxmy+Kq%PH%XxXx?4{J7*w)~P^pNMQv6SOC;xll|}4-Sa@t0~GDu)Lqqum3YO$Oixcwzo
+!SBSH&{AxR|e+RV6MiqI6jnRTCwXy2w_IxRBom(JZ}A>$J!R(JD`;#Y|jeX(Os6tGDI|qD;%t#kI(1M
+K!9^TMefXx6MU!SBhY-(XzM}weEY_H08xKOGVynZK*b#X<F-VmDrMSM$;m%n<Q_{;FI^KgQzZ(sW`e!
+s$>ed)q^Ohr)m0DH0;}#vJ}-csYMqgk;9~EzJLKAMVVB!IJupQvVp~`A-d<PNn!c2F)eiKr>26@Z{BZ
+hHPzkzRs<iX$KhQgYUm&cZuEv)PpYb@WOwyEI@ko(YCad0n7w&_h->NsIn9!~b~^Wp$<Jci^konHl+b
+BwYdd;Z<U_U1(KV0`mR?82JZfn*FREx&izv;b=2ApuC5A+*YRlOJS_`ZM(Ee5AwP<>0z&FbgQCak2+L
+O3X0Pz7rbU>W<xA;DO4@5p*gENfq-2e>)07*F4uk%GF4gv2g(IChmG5*PCn#8Z-K{Sb9#sCD^KEF<~b
+atVIEH5tV6>Q@oX#~KF&%Un4<MHm0{_~vvJ*0n6sA=~U|9y5!UtYkM)8pgu5dU+6UrtX?=-<<?Ul0m?
+*MG^jJ=3;l+8&v<N2cwuX?twio|v{LrtPU|durOAnYL%9?YU`tZrWa$wil-DwP|~8+73<Ip=moZZAYf
+<*t8v+wiDBKV%knk+o^8b9qTsw>-0Q^t(r>(FsfH2N?U!A6}f1)tCn)L9en{gfhQ5D!YYaCMwBQ^X+B
+FU5I>NuD9LBhv=X?-?eOR(Z7vlEWQWUY1cC`#rv{Y(QkT{ijGJPW&7h^JirH!^0A`eCnOGzlgMk9A^f
+sUrm08jNS1;=*2f%6_2)$lSFC$!z8rxzuq867;D`d_}fHGZ{X(oDW+4!N+H|stEAoO<PuU}Wk&&R;S@
+LwQ&ywmS*iL4g;zZ(w_x4g(NCd=|dcf&qqk>*l%Q9Q@DBqf7L{&9+b?CF+0-9i#F*kD<*D~)-qp*+?t
+Pjm~3&t~AB>M>6>lxMo-nQnQ$Id;$Wm=_w#3*GWsx4hmQyF)$ZP(wM=El0ZLcysKI^_UY4<wUof>Xy^
+Zv6DhQ(0`qx0w%qFQLgf7vr15|JeE=__=Qx#$N2Bwn7-_h@_qE0zC0e|zfTV7%TxODOn!NGavt~5${r
+<CFr8>sreGoJFF9zFUVp3o@=+{KZejL+{05f6w&36xh-7J7@Hmd;W0YV7TT`bRbVbrcvx0>DGPxGf#B
+>8i1ok_vqT(jsA}F6P_46ntpC}Vc@W{jrY+jUr_NW_j1uj?lRgFpn?LVNlT9S)IlM$ewL6C5YWs=_^D
+(F&yPRp`r0%|TRZX!@*ps3OYCwkw@|MaK5zFvsLWhUm}n*x~T;-XiJY(7wHVm=Etk7O$fE&X9XRI53N
+&d%)zWl^VuGdvpZ0wbKGuA`Mj`D!^4)c}?{2Sw1L(}*l1RJhwzpuQNJ?{Z5K3OX|_3TOM)v}fb<^L>j
+`5zxbfC=+>)#`m|JC-(T=c0H8<|2@ZUEvmH;SBh%T&hUlZ65u)_%$Ex0#Y8a{xhTDMV!3`;9)i}5skP
+jjOOOjfny)oI??*4yaC?;s<nSOg#_E~kns>W5E!F#T-mV(<f2LMF!L_h;gT*F0*Uo4oridsBf^d|SNj
+Rpf$N*5!-OA{|TTdSiyQ7NS9SjTuUOeyjzY{fW89N7_Li`}rgdW_An#}|bmupC?W$z3zMpxMJ+uJpqb
+Fd8G@W#xNCW#KXD353xoz*e;qXc~KeAdH}{V;>zo^-Phk)9}TgHWr)MrQ?#aN$){VwsT2(*Xr_XdhqO
+Do8HmO+FL1kt^9_x7&LyhMgKfN+AdOGRTiEnh!vfZI)5$!RhykNqmc!m>sjdQ~b-2P=cvH>D&eTQf>l
+=){lnPs`niciX>q_7aw6)$T)L|UT05hF!TmCc^`7f=N2SivV27DTgW$}=7Br+=V#k(@j8C0Yb_-l%9%
+ylHOucidc@kc;Q{Zv72piRvE%lsxBdGa2zz?Y9@g(~xdJ<h;}Losz3Lj{Xq4K&e*Wq0o4i~#TK5#IYA
+OaM!E^8q0vxc7n78;zG|9o|Y6aiX|C))0zA5Pjyi;&iF!wPP1`f+tjsE)i^M{Y3E(?h2nM&A>{#I01Q
+BoBvkh~9f%_Z#fWVML4_b~k3m)2_BkEU5si<*3?X_4nLrW-LhINAP>Kf>60R1%y~Q7wK<!SNCVt0gix
+k22Cbh)xq=+bzkzu!zf~PMW4ND~NIWm|I9T)?4vNAv<WZdi^b5HrB$h#<sLoBP>&`w+X#NG~2ja4!<N
+Rgw~${7G=76@lldvPjOpj=`?M!I}GA55;m|frUoxo$HLMrjeT}P-cd;TEEHNb8+VxINp-i5^BB_^e(M
+__zhiFK*Z)uz*Xc~u_Hxh-Cih(DoT8>6wnC_%iz|_*zk>PHzCO@aPTC|_0BV{*xWk9HlvAby=ESg}g1
+^fwnZg$ph);_OQxT*VhPC)*1tSqT7zRvOM5RV|T4bxG%z0#KE}{n0WdKLvWSsvCkl|#NHAm<Pe*&z5p
+LWN4kH_7yf!v*TQvlW*=#WBiucgaT3Zr)CyVS**CIL9K!&ktD)e_+S3IUNKH)UjHZz}}A{OIHkBzKuT
+$<hg%N_GZn=NJnK;7Oo#63xMa59Kz4_AjPMMooFL6iD$zu`2_F!CFCa7xXn-1<VCQaQBugh}K{VJyeE
+*m^PXw+*TrG^zcy>y@BAN6w`E`irGMpVG<-GI9T0mz}SBQT%3uT^28YN9lrkwk<JptV<v#hDhjB<bH?
+OSQd1f=0z4DuzEZ5o#L>tJn9{dJaYckpqL(=AmF|x!vytY3X^k8+TQubsX9&w0p|;p0iKjo?(I-T{&6
+^Q5D5g#_RF8KcI%0YS%s~M-bauwKX)u90t46K`G@@L<7!cK2Ayaz<hBljzC%Xue2bh|yGU+~aH{a=eo
+!dLJlF%Q;0u-d%pX>vA9C4fFjQbd!?Cx)l0zI7C&0mf_J^mK7Sgu9o0MDEx;5lUhTg+Y0?0Zn$`%V-+
+mrUW@vyeUnq<epUKy62CA^m^${`~TR*R}6=q$M+d;H}ET+<@v3z&$slOhNjl(^X}MJn-->Jp%`G%p7t
+<i4!d|2T?CxHS^)~7<_{%1OXsau@hclWc10TTd<`7m(8({_K-?oF162NbZl_qON9=mO%6a%1MSKPj02
+CDw`n~TdfU&=IrU_A2kF<tjm$t<_7wp^E0vYFPK#Bol3Y#z2@l_`mYSmDAXI7NYJEHg7PgVbPUhR38G
+;iW6<KY2&)d<l05wl8%+bf_?J3z%c3@D0+XGB-Ep+;6f^KJ0rNUW*3G@wbYjt3@h;M4Aas43A8fs;`B
+5Aw0LEOyfMc-ufV#XZX(QBo5El6C)yH4*8_TO>xD{TVNw&MZiqI>GIS)b{3rbOl<TW>{2dWxblb;S_T
+%-^!Ld6a8@qh`=PtGwy$e9d<T(bz!Z*+QBK2br7!=i7%0pc#Q2iY*_Cc9Loijzae&XfODvgJ`(xr85b
+QBkA!S`277)=U2Pw?E%s0!>Grne;Dn0Nt_<1!@z;W)RDC>Hiv&CNHxqwAV9ABT82T-R_J_~_+1IRN?>
+s=HG?$`Y*yeZ%wGiQ4OpI>y~P?ecL3vvk`Z{`*gEkfkS%T~SKyzo1nyySoupYZ$;2p<i?yhK^8OS}Oi
+q7*TLQzuGntJ#gpuY>$Q(&#HZ!N_S?l@H^AeypYKW6KKY9B6)q$h_ZQkGsU6g7H6?%O?PoroFi`cTI&
+CaW_Ce|&ftmkK}mvf+CgXG^}|2+MGknOy+#9wL$vt&wB*jen>8p4+NaWOZRlHJ@;$}!MfHw;2nsFP_C
+m}~C;02?0647Z+#%tV9`xYtHbDvW`nWLg!qXW%sp`u5Q#FvlujOCQSgF&}Y{+11_13rChl0B-2<Hz6Y
+ph!&OwIDOa$v(RD)|94Nm7@x@`kfpbE+B<sXy7#D4F;QJqGPzEN5w6gwPOwwA<73n&<6HD!DVa-;Ug9
+HkWqncXri7l(8=}Zf&-bx|w_!nNf775qod7u0{wTeFU1Gp8Do)3D1q5?n9**II;y45TYa|0IgV5-#OD
+7L?L>ds3P9PowQq5=JkvnKIs%QYvx%!%;^Zu3l?HxT$zp(gPJnRW@dvfBcHO!mZR>}zmm4c(u?H<uV5
+|XS+@`dQp36oddF+!Il$uc>$dq~DSew|2;6^x-N?TGv9klPC*2XTL~6lhe-l%jjw*PE(<m`MeKDh<O%
+{8zDxFss|r@G32>oUC5<GHIs7EgcKSf}Ng&k>-AY>B|`AL(+U@X9ElclyuXqqeWG$%3j|Vae1IEJJd~
+5HNC#W^dDfen>$pj0%-yN)|XU(gswecoa8y?f+#&*N7%u|T&7*p`jghpACgqA@(iZmrvvY3?>u14TTP
+g-bV>=sANZ7j9lijf!g6$;hfVjnIMPH3%<KV6=Pf+gt?ud}A7}h*%AVdh$u}fdyj`9{x7+&aK<i<uCo
+}C*8K0h>29Bf6ZZnAn*LpWV_U}PId}<aHgm0b35@J<6qdlj#0Jtd0X%PoBndd1>=iX#T?Zmn3HLprc&
+`rRN1q<DppnRIGREGSH`P{%DJ1wvsw36U{j_gL2pEbY|oyr6Nly=o=Ny1|%&37<idRfe9i>Z*0LJx}h
+)21~|?IgCV&h!(OCBl4EgjAsai6)1R3sC3Kgq8SZm4cNH89!5y%%_yWqgB6j)(h<iJIhoh%l{$nSiQ2
+LE++YBVzOFXT=<Mcg*Hg(c8aj*$s_F^s=X<erR?rKcaJw0bPTUg5zpeUUvGaH@7>1H50M%cn9X;M(0S
+8~48<X8n*aWxM#%L2_fXNTP5Yrfe`o-6+kEZhz+|}q>J-<_Jk`6Ofq4_apJWH<T%i*4)JAuyQ5{h)78
+wjY&)%ceG9120G~N&`FJ%Q;gF)v&F@1EC)cFp|%{0L?mK%XSLsYEV!Zg1Ca2uC|Ihmpi>2)s(d9Xh6)
+Sn4xhN}%!9;h+2s3TNozJ}MUm2SmlkqH$(o0TiyN`Rc-5m5p#q<*6GVuDvMsOm4BA*l#XXx)g{hD&u$
+=VdJH(rq7}2P`Lk0)d_g+M^Ivi9#)PWI4c0CR-^PW0A`lN*5KH2>eEMikV0T&&6%yWMDU6Xjt^`Peo9
+Y1hJ%SM0n+`*fN!bg$<@%vI9#WtUy*9W&kbc^?jb*IVmBKJfKH$BTt?1JTG4qO@pB^h!NIh4v}VPpct
+SDl_%7zC6<FOU<;sSj<G1(zWJ)6obD6}4L}JcJ8h9v-KY5>DLn9slhlqPXat3G0WM&s8q@-n$nNMwEp
+2wtt}hBOl_`W|@@Oppi6ZG6EYct^7ON3p7n5sBYEM#m#)6MkFgLI0XuHYo0>BhDv`+HDj1nIOdVeYg(
+a-1rfL=v*Blu<;+N}bQpx_zD(8Nfy4wp^GbA;8yP;%MtPC=emP}Jegxtlh0$Kgj6m93E|Dut9eF;)oS
+7gL1GBmV!?MRXQEd{1SQg8c6)iGNo~bRDpLOl~4|;5(E%rld;lK`rYQXu<Nv9O-^MMV7f&!3{`AbFc$
+wykby-M3hwu6tS~URdTcWxpP=aj9j^~;QEO$`)Hi*qP_#3>1p!ytz&YwOf&2aqi_58ojE9`B1$3{I<n
+}YX0U(y0*Bmqvw`M9n1mACL~8~4YDx8y#2N?Dlij@yk&t)HKv!TPBIg(RKq->&WJmHLRcnnyG*J_g#}
+pML)ocY?-sgCBZO@=iG1#8OHz1|OO+DP*d$JeX#{h^sqtTqE)1`q?4C*zn=?ZNIUUe2*^@bRBXCT(xd
++B*hffeQ|f(d0J9sW^EK(usgFIWcHF5(D1DZoWkWQ-hn*u1L3s@afNsz=~Jp`WU%+1+*Zd$w$Ba--^=
+&yGqx@AU7JQnrwP7bI+6kf5%O`^wNu)u<zCc|l12P4}93RTX7rx@NL(q1CSX>?nY6m|t$=hE59RB`U0
+<Y0VqhPWJe-u|nilQIJ<+-3?l8G_yeY5;==G&>|?E(3!>PU^VC9pJ7CF&}w@&u?$QrAtM`_N*D7PWVs
+rb7kkaz3{pssJB84wYsjn`ve{b{UXdm__IJ_`_VOU`Xkh+p!58?K6*?@0wA!43DjdjCEF8EOS2b$?+a
+?ZjZf7HYNX*GapNSE8DfsGwB$Fh-(<S-FgfIHRv3lSqYdT({&^aGW@q6ZzhoQB7JxG5HxC0+jAeAKZd
+US-WI*n$*3|l*~Ifc~9eq`>@xkf#l*-^0_EB2Rw^%be$AeAr)-NUm`^in|%)$BVi;kgVc<{+4zJxwpV
+j1izM6E4#iGzz#RXFK}Yj=zHraY^fropX{6IIiR#0I41H4$vM%^CZn`a;i|-DL2X2Fai1e4w>{-jOCS
+a|K-zaQa5R{YUDL%JWT+lfv&81$x4fWZh}F0=Q&R6V)*>|lNZC?c;9jKe%lJIX@u!4kO>T#7_d$S$@-
+kRFwQ+=*52J{sbn2O<qP4Mwp`rk7XS9G?{)2-V=C4+F$|1$-zW;<;5j{be;|b{>D_&b$NloJ;Y6@6nT
+Q?V!=6x81b5C5Vx-EhB$H(=9O^+}{p0ido#>#m8Z1`sDhppLhn8b~FD`6FK@^A*bVtd3kx(x-a3tr<q
+%rw{!Z3L@)Ui~)>wVX^Lf6GF34Gl&Vp%o>TlT{wFFGnoqYgOnz(A$al|u*$re#@ngG#x}2&Y=6xyD?3
+SxQ<cy-pr80rfpDiu-PmHfjVi5qG2~#qv0vVi@@kM@Lck0&`t_`F#3(KiVPw+!?Itwfpqq?TPA19KSR
+0dmOSJ6R{TFqrDd1qrDJ3=yAw;06YWkH?AT7QpoiCBF%Gk0!>kaIi~;te(YQ%R!OR`)qdP}6%D_GM<b
+0{=H7F4$HINRnKIg!=>dKk&W#FFsA|hbbP_?m&h{Uj_oH?ipv>?&i%kV4rXN|VrD%<VdouY>-;t3#jK
+Ci4=@SV2w)?b8*ZZisXgzXofDmr>&6TzIZ9t4YB*u0KRW{|GWkE%wPU10~(Urz-T!W7GO;XF8$)J@j1
+_%@LX`_?g0fFyGwn;T<WxEMaEc){!KI3w9#C#NhgsQCN%i>smRV71V!zXw2fQL}Q$a`~V7O_|-CLP|Z
+e8Cw^pjY#O8a#jU9y`N$L_$OsF`h#|=-Par9T&b4@7^$V=AsdiuYlmMH~?D9D-Xjgy)vm4d7&0xh*lR
+<OxiLwMP|89D!S+*Uw$QvSEMRa2fA(fd`-vO(SlxtMfEQ^Q#e?kw1Bzj7#|IiwxuwT;E|=BX^xPmBw#
+E}vKKs0E2H4=JN59kU0*QA)=>bYb{YGf$20ECb~SZ`7bmQ-uzEE%@cIpC-Lh7A+xVSJuV&kCc*suaFg
+JqrWBP>=?%PMmiTTtN<r`gcWczky^wK@3buMRz)^S$umxu;?dkZed#b7)7aJNTwmPr!=Gcr`*cRJJ!t
+NSVQzd;0VZ%X?AkZs9&I)OU>VBaRv!0ej>&P3n0Iv-n2I)l%`!RKFqMG%flZ_X9o26w(4r@i{d7g-f0
+D2TfrTibvWzHcr6NFL6k-drQP^3+?;V!scHZSk}P=3v66Ve?}}B`f}@4?-`Vv~BJuEW#v5g1^*D58#H
+?h>Sth{>TCwjU+|-WXX}^hx#6~fhH4FC3`!K!vj+_UCZ_SDVC9t6Q=6$0tJ6QyLw&p4IJx18F*b#7>v
+g@dJo(()H<YjJih!RNb-M<98`<dzDUY{T@tVuJFC`}5$$=T8=IPg7wtLNQ4v;XkpIi?A;>a|*)^JX+u
+Kl%Fv5tEx%4+$e_58kNAKMrm8ytkiH5)ebPHsXIU#KKj<d@jYES89KSUw+Qnzb;YlDbTqp#(@6ANFda
+~;TYv`XcG!FIQ-x^6e@=hd?OTx_sergYBVGgB&>M2qwq>n&9mJDs3!JgA6e?GDl1Y1$?guFzOcX7I>9
+*j|pKWzXprMSyaA<SXy`>UQ+^zyEz-9xTECRtu~x!}9Mpm;~9u^<&LQo=$;#Zjw7$I(<v^5F=MYUGed
+6%LavP?C<Oa-`XBu0_P$s;t$z6>l#78A_gq58yl#&$9*BqPvG?GAPM=T%_juPrYN0Y9gD3iaB?ocKvx
+wM3}wa}#R;fSg?c~;N;bEFWFudv6uTD^l@!_`PvDJmERM2u91q+awJjr^;}i@rvO4UPMdscle)(WHj8
+zQ^ggUcK92H9{>=}4nkx+JDiedeZKia*3HR^PdiIaTxIavhyKd}3g!V7f!^Jd+`lYc_xh10iW#SJ2v)
+^82;?>k-zQ6i&Bx_g&j5W<xJfmHV)0FX^`U%0dsl163b3BUyEbn(-9(&!^7Cl4GN=}-4TmOA{gWNy=G
+!*^)9=TsRi1|;xOJEsPj`P6=X$Ku>TYKPoR`AQ1e$8s$>Y}yuOgeLKD_=;chX_>w&yrALSZ&prX$P;$
+;Jixe7SIS7#XzaXD_0bco<sdLvSxgnXNWW-NxOMm;70Yj{$(y8hO&4bFa!SnABB?-CkU!LbdIM9#{F}
+?9{!k^0W#a8R9)AKRCytiVkGpSrVA7Xg_aUQhrf%IL?^OpD92}q>9Z-m`_4uAwAWuCzuY81vho5lOu>
+z-FygG^T{dhw<V2s1E!|6vOchyKNi-g4u@tt&WaHy==QKPB&!l8I)O*{X%ro;i#InKJp?LMT$gGC660
+z}h61#_m$xJ6MT*q=@{tW!r1Lg(?m9Q$3gp`m{mMdSu|P((fbpp&~t@;kDz@^fteKvAn7UUrDS)(j(Z
+kUH!{Iu8KYh=AX{Pioq4*ef-{NW}V{`1!Wo)t|My%s*iT;eLEADba9DjO(<l$>lh14tAEeOuJx8FU*B
+#Ql6vWn}|DRqVl+|iS#UZBY+&gb~g8g8r+U{nNaW7S6oA)0&=n+(yfz>l&G-~mo()4%Nomckr#AtJay
+ltFh`C;OM|cOp{1z!ze(`q8EA_KPk63Er&-B#T2!j0*nz|xRo4tjn)n<;%Qt!Bz&&io0q%9jSLVUgMD
+T5P$p57nD=3{xt@Sc?sjQFIWn4U`a!C3$5p^{Qy(}yVikq8)9>h_SEs6?=zVtwqRe>+y(WkvHY2U0~U
+F7h^O~3aCYcGAr1wR}52+~#2ZbG*z+5lYeWfXk#*2uN(iCTWBHCyN)lb|G2`!Jw$A9^u?oi}M+EbEm~
+1I`6_q?@)Ux|LTS(J9E=5J>=2>_$DhXD`!`VTSB&^7}+T#@yJcQ?scl@Wk6I_<IHNU6C#pdp2j|F{b>
+*qd?I-Pa`^D+7bbz9ojd$SQ1<Rlc;SqtI>}c#c^Zqb&XpoJcwlqQO(*FEb$g{K&G389t_E+ftOcUd*v
+&nb#wP{<M4;2Ub&~j_;FvFt=HGb5pf5|z^`V7MH3<Evdn3FJQ{*}PcY!U(ll)Ay-D5Il@NuWSdB<rGQ
+GCdRehh?eEUH1f<5y(d$A#b)og7^OgBa^Iz-s(xH}HX+zv9sPV8PY*oN*S`?lsUtD+&UMHeZco_%pKf
+cAnc99WYz>XFy1<<FoJcJI~z#?SgaXxm6}bl~>bYyL)gz>k^a9-%O^J_*d)<>UImonI<Zn(S2^)VB9=
+=r=5PlVz@x`;g50>t45BEdeb<cd_gmzO=Nr?WcXKB3;X4-=SDHVWK}?v%1vQ#{qt%Y@G<)pT_qqaP%w
+f{GWYc?=WLU9u!MEZkU&}-Hwj%5SpUvOXXftZ7RKHo3bTNf44Z*dK_>Ob<QPFGc1h+JF(gA=o7H<{GA
+eBY<3VbNfLeMMiMxQ5OK+zrFFX$2;u)+N*90p?$vCwtI4dMcp{n`A@cU<kd!`rt*&a98evM1fdkWqBD
+WLrb_n860Fe9397H(OyJxZZDYBV!^k|O?SvHS<40;`>5N>F4#%A63=kybK-NxhFl)-lNB`0KOvZU?zl
+cV#1#pWayIJ-41|00TQd?d52-Xi9`oCaArOr?7<^xloN1`n(Df`0ZUa82DD(K1}+kI1ORAE`rnQw9En
+jlQML#Z*P0N{JFYbp$&r1ig-_9ZqA_VTZX`qhnY5w{+TDHEzX$#~Oi~J0rlI|3t4#H+8r8n0f`SRk`f
+XDsJ13(<CndPLipxGxQUY@=cv_`v#7_HqLH>!CZWz!f*?=3W{k&@W*8c&;J2XO9KQH000080P~d=N69
+J+`$iN10Q^J%03iSX0B~t=FJE?LZe(wAFKBdaY&C3YVlQ)HZfSIBdS!AhaCxO%dw1J5mjB<Mf}=DmDV
+CI^)15i%#K}ohPp4;-CMR~=nUx$ZL_!j3zCw_))bW1zy$^r{C6QEieOeJ9fqQ|A`}pA!%rZ+NZ$4iZd
+6CKa+*47SWVt66Ithzh&bdDF0<}?ECGp5BV&x}+oQEoxnF#gSndy_svn5j7M&U$@E&gBRD%6&y)V@e1
+65tZI)@nP6l8r2z6<HXn#XJ?6mY&d9r}7!MpXAA%jMX19vmO3^^Zwn9y=fZxNvv}b=OtC@b+bz)jQ0$
+l<}%LbIn?LbYU^FX;Gs{Hs!$HHe!qX_wf}wYYVgQ=FLHnF3C}R|keT;Vq-m&pk%K5-J$;GyUUiWgy#D
+yo9r3H_Sd9O8Gybn<<Nui4o<FTuhbNCL-g*97WWtAa^t6OUj{{G}u(&E-br0$S{(FH0pz9@+ongQz@F
+D!a5L*7_yW#M4=de8QVZTo+gA+(T^4@4&MAFOG5*AqJp3+_*a?#asG<nk1<eRPrphTmNHlTCAUVZxh!
+}Zk=l`<4slQLh6Smmh9@6}etgJki$^z-2bQS0@3p62JIL|(G={(+s&VO7^~x&HM>T1dFB;!wrX)A=q0
+OUm<1Ex@Gp1ZbQQG2#0%&BY>=a~aA=qP<*t)4u4ByuMg0GP?R%67M1^1_6jsQ!$gGeS**=SAHm|wN?Q
+aia;e;SkRAJ(Irg$yDvqOQxoP2Bh8bzgM%;PjiBLxKS60w5vC!DhOd|+x+}hh=CaC?BJCocL=gxQd!W
+v&GF6sCrE@Ct9U1Z^7vSxvxa=BvphTF^<O7xQWkVMxMbst9Ty9`KW1SS4Pm@?G83tex9SoLg<;VnT;N
+Gc}Bt|+*a4CGvh|yY#fS;_NcMxBHOU?dFzu(dCztitu=$D8n`iW?cDo%@>3%Y~!o!qS?jrndt(hbFe<
+r_+Pz_*At4LTAjU1VJFl_uB9Au2-9OIC$gkkn(j&3SeCc2eYKz)6}LU7@n-R7q-?vaHiASy_Aef<IK!
+BBDkbjM^-Z2J7>?R#ZnKq|t#cupBC-(5diw?V!XY!P@VnDJAi$i+Di>sd=X45~#39f*lozVGeR)!-uk
+pxCv@h2laeSEm6a$KqJjbz_e@y&1)m*{svq{(!XU76@gQW!n${gL@79Y%<G<I3H6Sa5YVWgng`X7NC~
+u`sWhiWiI`l%zZ;fil!C*P0T<VRCX%R)Cak(kF(bK9;hwIvNL>1o@gs80T*3utrVWqH+C=tRE>gsc6X
+EJ`fkzKo(q;^%ft_re#L`M#Z&t?wLaadE(kZ@8LYBIzzfW2$#5TIgM69jT@k5n<G>hn~%!G?TF4>QQK
+*<LJB22YxB8;AFyfnfsE8|#1dv|n3<j5zj!d*&i*LN~&95V(`k^Pv7KuLC&c!yOlyEAG6jvu1}Yn|4B
+p2mjyaiXACJmd13gu4}MG!`uj<SIg!dDiSE`V<q;+!J;$8=9uRR{-{`@v`CO#<;DoD0I2<4kD-k1FJv
+@2e3s|8XJUW?`RD!ybJpDD-(ubx-tc(QRE3C@lYC7gZeV@3q~DV)e%5|_Cok~=(O2ME<`9|R*l8Vw+g
+#hfpbnJAQVZ27fIG?ES5AitEg1f0Mudc7EElN^F6>a<V;kTBSzc-+FrKhDtZoEWm8#0ERgX^no4G`$#
+U7vU`?&KgIPI$E&V%obtP&-Jjw-W81THi$R`Yf@M;tc*Hie+h&M7aP^JIETUpr&<k!8#*4*&A3`<;=i
+j~v=oNe9MI~1i2SKwMi5q1PDk3gNE$XDVsjpJojnOLpt=!0Es*ud5aw`W`oIyqDh%fK=Ah!DScq$^zF
+Y-b?U3h=N&XN1$bWwe%7|JD#uOA~@hzyNCv&*_@UrM-f!5WBSrRvkP5M-eFoPXzNK1Gbem*bLl(6^n!
+AmU*ev9X#xiEa(ma&Dg*YChY9+>Vw7SwsOh>U^@+X#&o(j7qgm?*qRF(nuQtQkd4b<C*2$(#jpawM#}
+19gHoJWvB!yCi@@Bx&xFC6vIA*)3`nCQGLa;wFa|~ADbhsY#=)3FnrNdx=@)*S$uJS!a{xaXvA@SSl$
+2-~U|^8M;f~88Us5UZv5}musqTqD)6iH$CchLaV>`<vq4Ny&*l~e?r?kD2gXyAPgzV(CpRgV>-ex8sd
+~?%CszlJN2S!6Wcz|#@>RLdj3)nqoJ1(ZF-3EZuQ-Hd*T+UWq`#1#85`v+@T~+K(QYS`O`zm6dne|N&
+*vSYxI25cnn=%wbsgPXAlqI%F!h&^oQzF?-7KNSU?^VF&W0iXwg%@Pib&$UgI0z!xsg+~%aTxLOYV$y
+jcSE41tEwAj?Twg&Mw&OFIVKO`AuR(26-!M3g%+WqSJtf#FJ;{BW(mEPw6Jnt`t>qYRIVOpl&_V47aN
+5OI~_S~qUx9l@01`1E2IbPT@R{THk>vFu<bX-mB1!2GsaskMoZbOPz!PP0<Lc`i<XsI%Db{qZrDw+jd
+X0~p;am}k+Te74=n*k)B0BGYztc$<=ut#=Umfk3@9!1xtUdOFbUQUDqPS*cQ<xm8<^!9SL+Tp3J5cHd
+j_5%R~utEuytJk{_z`!(}#&Jls~o*L}=gaH%hiY8%-;3+|a<A2x1ENBY;uGYP(n@<nb1A#Sw8qlJvnY
+-rL-qNXzLoQFRchO2>BdR4OVJ#+k%jZE41YHmSI6A<TwLKQvHIdLYU$y<Ph*h?~hp!4#W4AUz!?sGj-
+GwjN_Vvc?Si(nxU-rE04!nI^1@RTtMej2+XYy6Y`k(ii<D&3j2BSv{v;O7hW0w%kgu%wF`DSaFtP4Q{
+**#JMOm$<!ZX5p2fWstP~QpYIqD8At#bB3&yAzjw`~0a$)Sj6(x~%5_@$bEw?_;AanL@FQ>7$8E^awu
+w~W^e8bR*Z*G5i0eic?IeG*z5?AR1Z6Ns5gPwV7q$y4XV{>%rFi>}N`VB8j<PJNw2>LgVhpHHwR*a$X
+)S7i3r(~fKj(6ltaw*K;nYaJi7p}@nQi17or1IdgzqZIDKoBH{=;1V$yY>*ZyCQg5#&Zc6ovw$#H%fs
+H@=qC?%lsZ!y=RBUORw;h>;twnK5i-%obXx2=Hrd3mYyZ>9~C>%x#=S;%scbh6uY_JFBw)#94%9(pgd
+^cJJgk5CCDbOzigVSsf~G;xhX!aL{e{d!k~eYlQE5Lj@P-W;wqOdS#r{I~3wOu*X7h>cT=`&Q6U*?62
+9E=Sef!+)W^s(3f<zAfH8f%@g&%YFje^^OD;sj)CjS-pKVhMPJh5Pzgg5EwC_cR(qr5{r4OT@pbw_g+
+@W@pn{P=GwrPt`L{Ij?Y9;50N`90l{PmrKTE=vxN;1@aF05iDN&2vhPWW6kvO1z!~+K1Tv&gM<r1PD#
+XnGO(XJg$d`A=iBT1q+aqtn4pG{-}P?^IT%$1%OnIhC=CunwdHqlk(BHh$NwPCJf<1(+*m{+edLRB`Q
++LT+Wdos5|+QWP_1rZ{lIW(nd9HO6bT461}$h65QwLz0=xX{cD$w_G+jeL7?@q`=;#Xo&lIgQ%<a9Z;
+)fk-%C+sTPA|IudV8(%oIGTna}(eUD-5|jKXGVaKVqMiQXI{_#Aa+tG6&t_LEr{!)ZZ=Ck=q`a^7yvk
+g&#QsoG!Q?AV?FdjVKh9W>IY~X<%WY#l6e(M8b?fU$W47;rFlBWP?mDrxOgx{!sOnNKYoM~~1*-$!Ve
+_{U<$Lik5hYeWiBmIZ|0+0s0w-rvFHyb5&Ni0<R@DGO+|pz)Q&BX&DXTM}mSeHBc|oc!Bv=S%4DE{}D
+pcVwQ~tUYqPRQ&{RuXIER3r&23(Je2+xpJH_{@e=+*h6y`nE^aJhv<Hql0S!e$bv7DzwI%B81Un2S4c
+;VleUs6d&hP9VNwvPqOL9o?`AvlEEluOSwV%V@}G;JEd}*5)4Hk+AI-Il*f2rJ%g@A_Nb>g~S$$6NUi
+h$8S-<3#`qR-ge`0oaBE0T#e%s5NXO&;y?ftIpex4{I%vh`pUr8RVMkJ#Gm-Mo-rAPM{PKgf(to;Nod
+~kK!i%n2>}@>PLb0K?h&2&*l|6JSoNH=dq`Q6PCm5zF=kU_Cbe&~*48RDP~#M{3e8LQTrx*`N88DTfN
+m->6G%#^N-DB8=w&A{j2=-I46%Emf)PP$TH+L&+uwmy0Do_zab!zzGW~LVhckm&;XC4xCnbY#Nz<1(a
+a`xsl{|rJ18dAN5)k;wkYdSs*;SI7L}eT^y<CPdN9KFEpv`+b&25xYKWJ)bFC{C8Nw*6{YMJR>Oe&s>
+u6JR5ty#=ws#&h-D$HpKd?|XwX*fqmSz-$D&eN1b30HnkrBhp}WmSRcrd4ebrPg~sg^;RB!2QV6#ey<
+)z$FqpFOW+-eV9X-$~^EPp4kU!Jeml1we-qUx;$o7WEr?8PmUc>JU=)TbY45KQlnMNn;t*haDL?d!}(
+s)wMO#*gEiu_gKm!VZgA_hE(a#uXx_DMu6-OcoBFW!)T2pr>?+&CsdT9Je4R((`G}8p12#dbUb!xDl6
+xxl-n@JF;kT<F=2!2o-e3KEb3MZIsa9t|!YTDkikx~sa!#u5Q+RW8^XaGWf4RB3o_N=YvkVod2Y5@5j
+*ADx#uIe#7cdJ1A3WqdLZ+$o)l$KY#m-Q$9Nu1E&p&?p@bT)?%}-a?M8SRZpI86<?Zc-Za0<Z3h|n|H
+o5|s5aO^PV`LA!@&40R{|MKZ4>Il+`V>?28iR$4K?XN*_x*DFL=Li6XcJtxwhj-U5f5X?b_U!RsImg8
+r^k4Gpk5^uh%wqxYbguJaxf~3=mru>3{;=%qUB2@EEzDmucEX3YUn5K5;fC(A_q*5#ZhHal!G~SrVEC
+f<>Mca|I}aSJ^Z;#o-#mZz?B!FjKa7{6sUbB3|5EX#<NX6I!{vyN`VTM8N?-a2l{$%{1P_9EyvfrZL{
+NY20p{fnq~%_qmP?5NH{6xooyV74nf9>OyDgijrP~S?3=b;|s&5V^VBUH#G-tS*2~*=~b^rFPnT11s+
+PlV``i)riZbvSq-c=m%-Il;oYagZgj(Y5k)v}-VFh||H#Sx&mC;MLe;(${oa|n6$*1TTyJ96e#KhiHl
+E!)ZAshm?O%CIiN%bLu4Z$Uv|{Jq;c#}opZLt=kGW-vtPtW?YTm>jLFjqJi*+a5;m9!iy3)VZKXLEjv
+J8yidqxB??@Bf=qBMOlU3UCTUWKDX10i}CZ@mew#Nq<gT;m2<nTFBXmjA67l;8MwoR1Qyi6IC+}nVJr
+N@K&0YA9y~<y_H&|uzv2NDGU!eJd-m+vv+?ZNch6?e|N7VQ>{}@4+w&ekms2n(|5?fd8rl?pZ->JP3I
+hDt0hLF<9?uSY<~R$gQ}u<7_UOx`YP8jp&S}-^6GMK`n}J7O1~8{y?{GAn^t2yOO>ALMqjlH~7Uk$-R
+nI0Ir|h!Tw4;O=#|hwHAVbfQ=Lw9!P#N1c6OCZ1`yX{pBK*v0fP^sp*8H^)syfUiY9@c`xDB;vLZ_q#
+eRoLx;ZcdzFxJ<Npxyi$GRVzl*lvOj<I2u5*vo`7{bV+PpXujEU}DqH!`t)Mv*GDh2KyTCYg!+-%Kcz
+9I3BHz=AA7Zt&Pda&zGVhyoPg^A_LXxe=eszdGa+=aQ=7_F1Oo#n1FxP+Si)y&)S?nYgtP}gxld#vRE
+vRg00A)-lQ(sS`fj_<g|=Fw+F$9Q4o=vs9Z*ffT|%M51j`Q{iNvG9M&xWI)c2MY4lz`eXL*gyvKEra|
+AosPR9duzdyk=$6(s`9_!2AW8H&-H@Gu9B=EjNj@fW$QwDoWvqRRsmyfj>WnH^R%-@AN`#9wot(N)P*
+-ln6AM|hu-i8T&*w8x$VI8`AAOkQ4?4^58U-b@wtOe0u2barUFo(Bg1nqTCz3f&4eH8`ApQ5oHm&<XT
+tyvH2D;bY7{zs)^V@R$dK7X58`~0}>Q)im|Db$DX(;t%GMTcovxVxN{C}86vb63<DbAUL<!d|cUc`&^
+gy?FF^GSlVPw12vNnB9+O_YeK)OwaCb&-)KOQ|dj;2GdzELhv*j4xfAjBo1fih{pMJ`hYvS%fWPvbfI
+)H{e1D__Q~J{lpnq!+Fg{(+xTWEM)y-K4O$iEL&*6Q1YCjhQpEv?eb6&}QHU2b3}QZPU}=U<ln#C9Q8
+)@+kiA8Rhk?G989G7~r!)~M9$g<`Lol5F1;oa4Ga;zH=<jckELWX|Dj)R6{o(Z4tviP4{^u~}Ll?@Cu
+TIOz?)~-c$>ZC@S&(nzR2Filk(<FCiE2h}wnGgcHN5q}2s~>Y@}ABO9!){T#dvvp{*bK}U{=BT<JY5E
+@C1He(`7b%{V)su^=$O*|I8+@O=;jq4-)>{KB)L1iXKw`A<ZAs?Ze}T(ZdWsKSzBv<7${2{w*>o?hc+
+F-`e8e|4$E^w6C5m{J(48z9$4v#1W4OG<?QBsQn8Kqa)wA8~LF#ozE3Kna@i{3i69a<-bdC`EwEjz@W
+X%dzp*No2<ZpQ$kDh<$=uk#-V6QGQQCAiW3!hqV@7`0@h(vJ$nOj)O|o~GU8ct=5O)bqx7(gM~a3SO!
+H}x#(%WpUUFWKza*|0c$gr=e|q6w>>PSGYo$H<7dP;e{veS$?&q+AGV)X)ML3yE%GNbuPCCTpNXivbr
+_mpi))6_C65`A7%w0wZt7cq|?9f3__(T;wn99*E_{UxL(W;a4IoJRkNu%K34nLna0sMafP)h>@6aWAK
+2mtey7DrMibL<Np004n30RSNY003}la4%nWWo~3|axZ9fZEQ7cX<{#QZ);_4X?kUHE^vA6U3+ueHn#s
+ip8}<4YH4e!PItGDv`#09?P+G4G~2j$v)A?b(j+9Yrbv|_?Wk?O`#lFBNRR|YQleflIh{t9fCJz>e&^
+r-1V({BCD+%ZD2xJneNCL{%nw3h-Z0;dLV7L!J|ebr=P<|jj!5J=mT%K**9mE0y6n-TFql7iMBuR|0X
+g9X;KKnKx#lhX`U&y;-_1w#y+vmsd2LSVSrGVvs4+7c5Ex-6EZ=ph6+#<K_QIyWM>PI<Xu%Jm>4nL!Y
+-U>Y#l#Fu3wpxJicv3gLWh?AZZgYpexxDNI1+#TGMmxhiy!@a8kE(tov{-_`3Zf08h<?d>Ab9T=-*QB
+Vm_l#EHuX#$)9Bv0y=YHbW`ZOGQzmD<ISRwg#k6EviuJhuYP=qU%%yF%aUQ>P+8;xK0kUSxq*Lytni<
+n{I37#(Id+RiEwhflg=MB==nFlfnfSVVuoRmi;2UCC>er?;j<Y~;*Z2Qoj)9J%t;I%@hX2KjJl(t6$J
+q_#p&|z3HiZyZJ;rsq9%MYLxQwLQFl&mC=#0mzC{_Mc9tUWETkDyCU9;<p{|7_Iprx0C%%n;-2gAVJ7
+~xr4u|C3yLUNaoO6QO8{a``K&2dKiD1T1@<NcX=?IL-@tpAbx`)(`h#+?a>Ic>WV>o&3d-MntK4A0}4
+b78_AcDUm##Pv+q4*+hUwK?qL(63J%;oxk78F-betH&$f$}|XB;YNA)Dn`)FhTUWf%WnoR{E-Tv!5j=
+ngVj-L)zE@c|(JdAAn&)2f@0%J5XV??(ee{bmIehy01$r%F(N}<acU^-qJ9dah*U(Ii@T16$YTHQcRy
+R%bd}3*Bk@kb3nR$`z8y|{U|`jFeq|)mFF~&gzxzuwi%jl%i2ijE1kW7ZpLO18;39j3#1fMJ4;42Eg*
+)1PYXl<X1$C>AMslKVWP7Aj2Z*h#A+Pde8%qKzxxT8m@qDL>{w)C0$_S<nD+lal-NE2=78ozrVEA~n=
+lR`oCJO}o^S%3Nnp30jZ{5yPl4JU1w#%MoFHh40f;Z)CFTxwZ3Q;&C*W~F13^of+E4_*=JIkLeIMp1!
+Z-r-FDyh@QHGgiEwA+Nh(;(S?vQ$ySAA8hLKDz6ZFNZgg98C?Aeu0(3AI&?xOZGv4koKs2cVeH9n(fE
+MN{T{!T!6xKNA%}I8(<1I%%I__%2mi=qX|={EkLGr09X!lLFWX#{Ox9;G2vmircz0-k^PR&uv^C5d-x
+ZN)(9>Xa1~r-2YJ4gOs0T&F6|CL6?p~RY}obMpODan%>ah4Bgi}hFypKK;LK8mTT=O@_1|!GoBIZ0AB
+<0%V+23=cmLEIGZi$Ysn2@2#5h)QYs&tTFw+;14@J6H|3w;tOLrx{!DCPNM1rv;W8g$1hDP^ypr*)L7
+WkZOQnK)IfB?Il;0Ny&aKMsRf^+7A2m(7wu~w#e}D}FG}@@cJX}eB2u#~~4_`k%PAy|3*q9YiN+F4PG
+QTH;zD!$1eu8~iw*np&MSmeAh*YzjXCer|fWiT3nckSj>N7QO(Wn^&ssI8CE!+1FLo#uo7Pq^U1(t;0
+SVggwUO#6z7B(bDhX!XMJ5ew|E6amqJW@7RKl{7Bz3jNb4~7E!Xo!Zc3_aFs9<9Jw993Dc*faJL^yCD
+8=o!ZFH;7VtNuT;hLI&CPpfMB1r!+FANqh;Q3uZukGzF&0KWz+!pbJKejDdb>OI{|#t}=C&Rrdsaim{
+UBF#t5_*mMUtX_;YIH-M;Q-~pQh#~Se1cW!9nE*&jw_YS%Oz5CNhh6R5mpFey^HG;ap$zJdH{pr`o$K
+<;cg{$xfd=1dk2QbOC-Aju-g;I}?&#_!z%W&h@2=U8!XpCh!?*VRj;z#H_W)!rVhj}=Um{7nT=1=Lwz
+b8F91n)fc?`UFvXlF#uzOuF)%kKF2__0QI@b%Xk*@5)<Z)e&nk9FdXt9*5?t@2H2mGje6ol>2jp6RP(
+Od6(#e?Y`W!`>zD@AD(9dVYQu|9g)AKIUK45+FXJ0<ZanB=v>-=I>2u{ax1lhoton`OQC@()y>Y`K6@
+wrTpfrrnJ72HNTd$zLwv7)0EaXvgR{M>zVxKc~e@?Wz83o)(iQ~X;WIKvgVnjbtb<#Z%XSt(WYZbOd#
+4-9@6kx8oFxts0qaJ@-4z#(tdrY!zanFsSnH$gZbxiR?P#@!{CLHUtYW&d_msK!wJN$H<9Do>`-Lnz+
+d<tOvLW};Ff8~VI<)2c$YkX^=7EW4}x$Lqz)?E&$u)HW4e)=<5QnIq5{@{D+4@>8jIcMU!-?Oo<4;*w
+cjV7$zNW`Up^I((a!`wry>C7WBOK&z{yV-@!UHMlO+)#Vh900cnT5kli&Gt4?@Ar%nZY8k>=oo!@?rz
+k@5;7C-TV&2K-O9;gYjjIY5#6Q8-|9>R7((dqz&P)L1owNIS%UHk}%XIV#GcF!jnzy5is&L%ul8h3Q9
+3!8kWjNKg1ISF!d3@=_#VKmamL2ei$|g@8B;Q6gYtA|c@UB7-}}pnPhEIGaug#f5?4pBg2zj$$}|rHU
+&JpD&NE&@<+;$~f1{7wN&g00i@$afI#m3!;H4J%uH5Hi4Ys^9%L2lambjJSJ)cq?S&{%wx?=M}e|&VC
+2K3pE0|TWJJ>WE=Yk<%BOzeA;T#i4kMo7W@z9~`FvQ0>r%OkoG%1eX@>`o1#6A>$klg-#y5zDF)gBni
+^{fhoXED#MEH0kLc@_0usFwGhzYS9e3qE+donHVlV7#wih6;HP)b2e%{4<mAkRYLQWH9bO2o^7iA0`#
+1ktUuN#6DQi4SvHk7q{o?zKdnpbr{VmIfx$jGD9y3?}CV3c<M;!D~MrpXm7ZTt6}B`xynTxAR{7Ly-X
+sIL0$w{RKP&Z~#wLoTsZ+Tb#%|3&q@p#~LK|$Z>j+Or7y06d6*_#6*GU`H-B+Rjw>COrx;S51?dBY3%
+wprb{3Kam*Xg6hp557+IHpkfS6MHcBFa>##qaVIu;?1)Q6NMK{1R%+}zd(1hk_#ej|@V6tD}3qmDfn^
+FJ=0G?|M7$wl3W&0^|IK7-LCHWhTF*zdZrmlnyCnmeTrE_+TF7q0~gpu=JlVQ<N!{-Ln<F*x0$_hVCh
+R3|%sWFBLXC^W?IJH$olB33pPtCdD9A=aR=X1p%kVLVKoFv%{>}H`9lStF^_IU<c5JQ2R5D?+gUydHg
+{8Qkt!=1Rp=hha@N=+NmiuU4_oQ!zl9+e8_@(H3>(G&&6AE`G6UY!7fdcCv}ZSeXpIxh)bGri+0EP2<
+Ulu%s0RsqwZTwiq|qkJ|KI?Bi3uu?mKjEB?Hj)wz8CKAlV)as*%Qk5C^is}NrF0sL%1@&XE)3heWPbW
+s1$uHp-@Tv9<`5Jn6nxQ34<uaAR)fzhe*-5d#8J<14suKxpPM;M&uPF8Ay*wC5#tJkczPf;WDaLt)mU
+Icm1bi2bEeIQ+iMT_>DNK=J35&7&Z?mP18Y0ZpFRR)>&dEn*Wl;?0gR<xs$3P<PeCZ|ZEKWi->wobsk
+a&TD#Zw>%i_k}j7)Yhba~OQR-dM0UoSh4+vtZ?Oelm;`7-dv|tl<kq`kHljbpQ_TaT!w&!&V3mJllo9
+2*-eQLJ=cS++)Pjbx1~)rMhI%9X+^UHq3e~Ts!CS!XYm2^M7PgX@v8`cs(APWKdjLM(6F8t)PeThJ>>
+HKRo2IZMpl$SF$f+zYbp!Rwn%DYeHcpT#Y!uXLeNe3x-j;%cMlHFS!o{(kI!!hhboP%*6%v{4TM=#5U
+W<lamapoq6_!IX14ekwK_JK+6$9vq(VBmQ1O-Y~XkRqV@n7UJ?kzy~iHDaDChku_J@P<_4DN(qQD8<A
+JXoOG&r*H@Q+J`+Iny1M-}L%JKOZ>NHNb{D|c@95J4a#f=70ywZ!1kkHCw0sgRfMcWz0g}7hR=8}|73
+)nj?Y)#_Hq=e3Qm2`lIKLMK-5&1R3Ja1cB(R$fXDSOAeGhN3{Gx*vxXw7~o?yhM+VjkpIh&hh{*9*H1
+U}h%Nh3_kYFwf4-eYKm26q*&G04B%-2$0-)4DyK&k!U~nBf?-30P{VbACD25k0?iq31=y0urTcuGuV7
+Km2d0d`%z8&m6My3;uJFWDj=B|)D5XSA1*Die1Ow1lH{!XtYCEP7tB@kasxc#`N@7xzj*SCM;pMvXlA
+47WOonZhPopH;AE)@NKTwjx^v`2R=|bM*mLfViC}<eAxoNXt;2acuXGb++^-;KdUNvKCxZcIC<lY5Fj
+nxv&=saARXz^1;$4p=eCYxF$Q*gB_x+ZPZW-zEo+(m^Er%zQ(@-o6hk5kXgnacp!oo%aYh!Sq<cWTkd
+!%1_KU*1FZi@V(_dh~degVPgTW7q;3A7Gyonw)Xg>M4VJC@D~bWWgi0=1n$IY_SP4-}xU@CfF8NZ*Go
+xP%(`E!wIH;2qdjfNh&juCcA{m6<UsaAsj?tkt%jr_FV$UcVoms+T=YRDI@}Ai`D<y^EkXsOfpF+^A-
+isD7gKRZ6F(<W>XMPb$H6Y6iE_?37wAhUuV(Rms55tEZ~2&*;VCv|?^)xMEceDC;K%xSN8f<7=plSP6
+K-+fSFOE^Ad)bK*~gK{l-R%)0gbdzac{YD*FyDi{fRfJ+^Vr17+{N$}mfcjP7Cmg5I=+#GAk&2hNj1h
+)x~;Sc86_~s_68sEw@B+sKTN6Gea6AP#qz5<F{MZRwbWtwe^P1b>-t9&hZa1(`tu^$cs+)p*Q0b9ML?
+O{)fE8jgW*>bxuQIwU>_YYV;{QX=&lmTK)_3mYCw2Iv~{y1VU{3)&i=Gk7{jfbZM>;gyDp_}dSv*xU}
+fauG6rFMW{uEHK=QN0Evr?9U@^)*29rnOEkl%n?4hsxThrd#aVg&|UZv<^G_d^v7u!ALOBGVwSFa(#F
+`94?;|T^AeCY?pfRGv*lnlYa-QZLBW~niI=h37!^a+%?^#BR`<Y7NMokp|Fpr?ke-9G{zs{_K}7DtWj
+80J&91TSw{K$o$<-;5{!2#eWrN}ykzoPR$sG{(>vvdD&VfLbov>uP$b*M;=qz=)^6@7M5b)Q57_c-@l
+w&qWbvy;xHsCyBM#;wSRI+3HLsY_mMk@{Ho4U*QAlxd$_(buqtS>TZe(b-HfoalBBapGSzFH~v=+YAU
+zX4fah*Y2UJN=#T`UP5kgh`#We$E5q9}D(Fp$8X1&*3kQNv<1%TiPj`z3pQG!mUK<nOJxNAXf*+TB$H
+`uces-Ox5L18C^)KQ|s6hZ?%>rw@Wd3qm|S7Z=XKR>t2Jv9wvR1b8bm<hed>7=}w0&vzsw5xv{Xm_*q
+DQA-57AC;*6+_$h~Icao($zC{>E->i=lT|s?1tyybOlkx1g6&Sb5uBtQERRnrHzBO(9zUthK`Pzk2Q_
+n(-=8=tx5=)q&g^_NmGA1F1yn1Er3<U}!X<WLRToyR%Aqc-+DKTnEF9+|g4F`6LHhyDV9EIA)MS%d-g
+FC`PWtvSaNEYw>?pVGkBiHToJo_nM`DuQOC<IC(ex#fdTwpGS4IZ=^xjChB7Zby*_q~!nPyEUFJNp-_
+Pn}M;y<g7tiE0DiEVDoyjI!4Tr!XvuTqqSJe}$1`_Y+xE2h8u>2=Z1>MVPW{J@GSZHRly1XCPhqb%^L
+AKkcg&r|d(XyFzU_D%EkYKQkWZNIXpx+MNyBw)KFewV~wl|x+;f5S=qWx!b<K&<3P^M`_wzm!Y&dH@e
+cXaRlgg+FZvzw!{|5`Vz0Q7}W3#;O~5<v6Zawxxu(u^_c>cv{n_wy4A(L;5~5o~1H{>xuBIJKN<T>-S
+@+<$;a8t||naero4U?$(j22{P<Src%${1!L+E;=v$9BUU#~Sa#sjEPc9w+f8w4Pq67D0G4ijCi8Tl*M
+Z(PKyM@KM=M#iK6$><nv}|cGq0elZO^6K5tZy{mlUevcZ=zuTA21=l0r=txopK^?MGYHyB}dGQR*&Al
+tR74KS6NDRTWRo^&hp>`Vm$9cJxC)h(qJ>2(KpPH!G}w6f5}hBubJpMSq?!wfumu4NGtL1IdL9kwIX4
+OxcBl7qe<odG_LRq2SsTO$)fi^39>vrRE)=0p9ufHtq{=BpEP(k6aDSp6ae@{z)bB<eWo#;c08I%<r!
+~P7atOpZ!|q`}6)nZ7lCBN_e#BfcugIu19$EF|v~%YsYWgNw2!(mlRpPcSnOzT&QCb+p+TJC&w3LgR8
+}3?5qj3@HR(&+s=fFi|r8r;CH+YGKf6J8$}DsQId!1NjnwI@ssjQ;yNtbvZ@7H^{9RElSbX~{-*{T99
+nd4`-|3!mKTm;#HWF-<~)3R&b<jQHXi`waAIlhwdvcMX-}Nms4@QbyxCG*Q+-9gP#z<nWwDSS*%|o2K
+)^S=-VOK-qZ!j7xxlO5CY-k{!Mz+ch5*1%4COpWX(iXY`Q#40597xgvk<Q-!=voY5yeaC;ycZ9^bnAf
+4jvKs2Z0rjv&TE-;MaiS1#=vMXDC?#y8OCmXG*h&CKoP=#<A;fh^~~!3F&E-r>f$j+oiLLgtvX8f*lF
+AaiA|H{sgEOM<EBia3CkV6+IwqCTNt8VMjC9v4A(Jjp;6+#^(ltiCc}kKX=}9&Y3s<9W5N|?~GEHS|a
+oXvp#(=sQ0G3JNa%uZd~!O>Rn3)bP><m=cnt6fL4uexH0cohGQ2d^FVw6!B3q_6!vK%UkY-&jo!Z+l?
+Y{K#w0Sz|Bn1{;gVsEyQQ;qfAMrQ^TX8Tz_aZhHf;AT0B@b~Z2Nwd6i9do*Wiwo+PUZb^esI2d|W=8U
+QY3H4(24el=%O)<Y4IU5zNPM&KP;W^A6`#Vi}GV=7B47YX>|`nW?Pb&9u+i#Ytm5BCoa~ppHk#%u_1k
+d95p$1-?ZY6SrW$^b%@4h~pj(K<$R%<a1HNY;3Mq0}eKuwVMWc{~qe=5fQh>7msPFVLpWbU^Vlp5r3@
+HdZ7KzdR81-E7rrfTJ5~`9Ij?O8K<?w_n$R1qNk-sv{G)WCvp^==i84cBQL#@PhFE)TYTBU(e~iT^92
+;KO=D3R^b|&;?rQv0>}r%ry@J80a7F&gV@IPb`vwy=%fr_$?J3)yYAb*RT__}2%R#Dz3yN3UKi)P<uW
+{&=nyKFaU7|p%#nM`}&!UdfuyK{&*^<=R{7t7#{Lu>9&wOxW5sR-$eMri`j*%~?`-K~W^Gbg}UYWO)n
+8c&^#1aMLJq@_+YwX-n;)KLx@#?(kx?&qx;!YG&5x-Rxe;zpj3&~UB2dKBCr(C+qca<@|c#S`qQRq+2
+(6NBBa%buRZ=P>wbHZVjjmCaE6C1}x@t<KpXD;}_p7hK|Nr04=FHxI}SoHhs1=lp2?3K}+nPHZ1#C8J
+E9^4W#;@8N+k6c=b6z(UEHQ_r;9QZLHra}%+o59t84|zDujzN!Ib!<tiLQ^xijb^9Ra-rv;<9nqCb77
+0{gLMlnTo$`>rQ&E(7K1YFTaHNyDTyM*!W$sfR1;RzIf+LjS;P3Cr<Y-^l~`Q<@HQQ*<#!zJvqRt$Ga
+KT}!_&juObm<$Ov%>y6|^l0PMk#V&_^0S9rllao`s)Zh|eK@zJSk*L9cf>$AbSAm1g+)jrdIQ^Vwnk9
+iOEnsFfn_)Pga{@#xVxWkz;Co%$5F(l{eoaRntg^)3EwO~0C)*<{qbQ=S;J=*D%dmo`*-?Ryl@YMQf<
+PSbB{60-aNF$Zd)%G6P0TMVwD4)=<7c*#%4dZcNOZ)Xb-=TOo(oW&)Fpg(lzgF>CT6s?RkEsvcV@|(V
+KVnjYUDHaFdGb1;AEekDgdw?S7Zuqv6XZXL1<kO|0*~`|B(ZHCtVlY`0<Q@h(0ZyY@gQ^^Hi}72!9A}
+$sLfOC%2C=3as@nz+?9!C8Rj<FLqMP|MRZN;_E+3aI_Q3^zvW5c%N*!3$NhJH3EUU<pZE;(L<Ha~Vh^
+0{To&Ca4S2~(+>si7r48C?IvF0MWwUmLc^VASs_cZUUe<bET=<K_*?`7>93@AS0dkt2;N#lNy{(JkcP
+wl#X$4g)h*5gaA8(O65s`_>AtZjF;kPkbL{Cu}PPQZ57MAqj5RzH5HK>*Wg;IQvD)Q%CYdWm;C>rOYO
+H;$K_;ti2#PF2pQ1T>$U-om%LRyl>Ro#FDqH6CuQ`P(kSqcX;ABIH5g?b7Wmur4cSF!?p2cw#mCiR4<
+NUckvuYU1X{J?#KKe1xGG_a?miI~<UK2DWX?>KfPP_u0kEhr9k}Y|Ny3hg@aFCs!S>w2Ms}*RAz4&7;
+6Dt_(@ElB_Lh2C>vepi;G2@B(nY&ia&nW-_Kx<Xw1IRjbI=FRJc}O7F7=71z3v+V8^J9W-v!?Iz-?;c
+ZJrwaDXc>JXWdVU_;2=<K2jK5)FR;nHjFAa1*_9VnHtxc(dZJB~^su2(Us9UR(A#jKBV`GipU+d2CbM
+YU?(rCQxsm+Hj4n>Lkg&UrJ(dCYK??M?o@OMDjWI>e{A>(_{o)-MKb)HQDK^K<dp#m_$)Rl9!i=@||d
+ekm$i`1ysQ+wF@_weIVNU2BJLmI39P7V#-=T#TD5-Mom;VsHM2Z^Hc-o!|R`<{%*X==Q|olRLA8As^q
+iBjh>K`md0Wq>G<JvD>tskSP{E6BVcU`PD-d@>{;Ix{;7G1rErw!uv5$+O*>txcEt+2RTH(9Flu+Kxm
+H6r8)TH+2DTx)0mI7=7&&wYz8;xm>-duzAh5u7k^&{PaG|fvK?lbflcjiiSM~{UKRZ^3zfG~rZ@w}b4
+m}N9>#C&<j5d>%xo|`juZ-V^bT_;neGz2%lVne#P~sWmajs8O!*EPt4aOO%$q)~1X#uW!Kdox`}&bd5
+vjJ8)qE4bxd4oCMh*>1Lks1g#GN>Bpeis}Eo)bsExR(0*uPZ9It5gnV%65)%0xkbVpdjk$@@h-H&=81
+n;&A`vlq2z5Urs<JAQVef;aJ@qx)cL9WXWF)^5C*-a5A`0u@<yQRcT2JT`MoU_*DjkLgVXOLz8#rw$7
+rC>AbK)#_|)UB|zXGZQ-~>7b;8l8ta{4Ny{P!kf*Yg!hzsneIIE?6>im53*mZg;`D8tG|cF;kM1bdB~
+d_rqN`&nU}^&)8B`{pgQyM4*$Ao<x<niMv(bn7VJv_dDrl7OK=1I>ENb=o91w{c*%Hct8E1v=vLbrv8
+9VXwzJx{SU}aQZQA-fyV~}U-R%yfh1TLsy1dX1aH|VcN?&cPLXV9ux7GE>`<NqFz_iPecUZVvShz@4Y
+cID|dj3r=w{=j`K}n%j2PL}>C6y-pU{bW53VuA1{YncM`QrO`oRChH`PotA?A6PQ<#I+F(x(h4=ns72
+WR#=qOXPEzUUDK1k4G}u#;|1B+5Yk#n=2!C$bX$mPxF=V$Lbfg;=2R1t0DLl-?J(*5Fg+n4Z?&Lbf-`
+xRuz5^-{6ZU;%11es}g&ZMdK3)c&#UcFRrwO^>2gedn$=bhGLhMQ~zU88piN92-R|`c|cC7>5_XVoDe
+T^T{58oC4tFr=m{sLCtuDIw@D=Ru<Q#z^1^sDLks^%E+opBaa#5%InH(;U;lHkiNFNA=_MsKuj;Kgw8
+f`>cpI~n54_G@OtGRYkNT<mZyKxPKT7ZG0E&tj{|``00|XQR000O8^OY7y9v<BwV+8;J!wdib9RL6Ta
+A|NaUv_0~WN&gWXmo9CHEd~OFLiWjY;!Jfd5u<KZ`(Ey{qA3JP*GIM6gSC=VGEW4TUui+(5*mI>_Zv|
+v~;%F$fQ70iQS_AeeWpQmYt=mQB;xc?(z8U-jOqBmsAw9Hnfh5g7mVnE|8jfTepFW?meeU-)XO{$*DE
+Ev=tY%4(wFzN28fwgmuGy6aLm#ZOvEs)LR?fjEg%qm33pUR~x^;t5AgK7To%LMELcl)v_LQaLnWKRn1
+E_;R-LLJXJhXZ5@he9r3E<CeZ5`^~yQx9)bBL%4k#+s;)u61zjh5wj`(IEr*4zUTWW{P%hZXH|^iISZ
+=NYzf&5Y;<EiO@k85{y#!;8)h0;fGO{yKWw37DKOTfPlD;o$Yq;2g`Q!$vKB0HD3N!1Lo{SCDJ8Ls1a
+i^U-r61duKcAc&<1v{`GV%-C))h?|Af;;Oi$K#gg$3hNZSPSKAI9|B(437_7bflLz7Ha0*3|TC1EKY@
+=7+1v@##;(sScjlpgZKVoo!l$d~o>Ub7WR7;E2#SYcOiYbS3!sH)W*bc>@L{u4mDpJXd5*Oj106D=@&
+=?K4Mam3TOo7-MP4<w6<l(U_KItrHuuFIB^v@b1lzcdb{fP=3Bsb^0v%*fEzp%er=&MXI3z<AX9Kr^P
+DY^>22*E#DLRhsiKkJ$E`Rs7;dX4w3&Ngy#JD^JzQx<4+wC*3F-5?^`}Q`SF)6)*zwwFeCXX@7aALt6
+m*la*y>CmY6`tkP`m;U)L+wid@PXYtx}F(n^=1=ORgxmw4+4I3wS1snJl4vkh|A?9WHh?{7oe=%7`I<
+K>pu9?U7sAQZFKK1km%I?Fl=ZBru)v6R-_0Y_-I5oZa-YM46&Z%LIU``Dq<wjvBrFI|Me1?A|jrL5C)
+ZB!jGWVz#7-{SN16C-)Yn2W|o&!y~9;7||YATC;gk_Rk&Nt2niBK4_qNp_CM=j3bFM_fna4P9JNG;~P
+6ZzfU)PxlKAt(3}$?y!HP;4B0iQ`xwx0A)=vr^g%0J22QW)P>ffNJU9`x9N*C+w0U1k)A@T_R0WCCOA
+5YRV7?i+JS?5Ep4XfMqDALjWE^Jwqbx3?sXX_>rBZEc~>)(OO?eQ9M4)*o}J7MY124o1aHdtgknOgsK
+R^zhnj`RwupMXQ^YV=%<7x11wdrflJlNG)Hm+=d^lJ|$D)i>JTvi0rRI6pSvldb`pgD&$DYgfV>hb^t
+b_{2LyyBO$Es;?@TIAQ^~q(~Uf51}{|hqM;Q&ND#|H%;hVMMZHr}+)1^oqC3|nT-LDJlUKuZG>IyH2w
+!E@)6@8M?gjCyaE7}!DMp0LIQHl^s>O9xdL192+cvv!q%NTF!kVuF9Te1ArFUY;)i(ZtzE(^l6N?irH
+e!Ks^8DyTlgKD)GL(|s0&oyp0tN3C^LG+&?GWc1qwovdW4#sg%}^lLQzF_}D$aJogfJVZF%BFMQr>XL
+u#QZU>Fq~HqF7sW|WR;MTU(GQgFrFPsWozW5gdLbSUx+mRB<+x9>@0p&e&012f?#f2}b!7WYjm^7*On
+dVz&>MB9a(Yd-#@=TL`%{iwnF_%+o!^v2wH5TFOC|Fe_bRfO)p2s+_E1939UuR75lUy>v!|nTxm1{|V
+C2i!U9;{5r*ci`wvf}2a&p-?I=a1=zXlDgXT!5{cT0tKL|8?UchezBvIFgl83`oCQ(yEMCiIkc%WfgI
+VGmW^gG|2%Wtek}!@wDku@BtWb6~%gn453p0$(}0wiI*|alg-}pJc|)zJuaJXzW9#L{W^}22IGRU_J2
+KPZmqz+avoLjrQMnr62tZP)h>@6aWAK2mtey7DoU80006200000001Ze003}la4%nWWo~3|axZ9fZEQ
+7cX<{#CX>4?5a&s?VUukY>bYEXCaCrj&P)h>@6aWAK2mtey7Dpn0`^1$10015V001Qb003}la4%nWWo
+~3|axZ9fZEQ7cX<{#CX>4?5a&s?VVqtS-E^v8ejzJ2;Fbo9meua(+gg&6qYri2JM-D2ERV=yhuR}~Rb
+Z^?#jueX=fEcUQLWsb}TZ(~SRZ=y=YhGaE4=YPvpk@xT5hgY&KB;pqB(DljPR*HQeNnLa?x1O-*yx<u
+5GSP+7f`7sP|r`D_U&@v(PKBZ8@0EGR-yKBhBKT7!aO#2GH+IXr?WrkAJMl)N?&zfP)h>@6aWAK2mte
+y7Ds;$kWL^0003|T001@s003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?XY;b5{Vr6t`V_|GzbaZ
+lQVs&(7b1rasb&pMJ!!Qhn@BS4crvyUSxzO7>*lCCER)SGtWtci~kd@N>``Jl5+O^fmAMYbQ>e%<7bv
+Mbxq&3)n^eli)@NN>)>%9bHPc~RTlrRmp^@g;wLX0jb)ukTQ71a#}Qc8H<D2TQEw%$2vB{m4x+!q^SG
+Vit(Mdy}AfzRb<$L#rr^=r_?;4@aK)?o<nVO<o;m+{qlDb_BAy0~J})jAL4y36}A!83?I(g1dVTef;d
+q_W)#9Wt3m%IXB?*<O(48-KDAvRyyQoX-EqR4Bp$wzpg4#X~BY4cJo&Cv+2~5h3nQCclg}D45k%wCqD
+(ln)JF*OBI}!@e;H=M9_>Z}k()Khs09sH5-V@sPW-iK&ITOU;&?S##<aP)h>@6aWAK2mtey7Dp9#c{;
+cP006oQ001)p003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?fZfa#?bYE>{bYWj(Xkl`5WpplZd8
+JlOZ=5g?z4I$9s%SwXwCAmIOpd)Yw`gTKFa#^cCbmhsit^v<F<ut3EFsC9_?UU~@ty}Ntr|jU)!9x1r
+Gz)F(w4A$qeN$c_V*bn`7<|M$&7T87pep*xCPCGiJ}TI@<S-SX9l7uDg-lz+;HLC68|voZ_;f<oRv_y
+#9VSqX<|TB88NEU1!M%I%fRJB@I)H}<g;Pv;EPZ50|vPo;G%I}hZ6usS&U^CTtwWuVbs9NczD%-WwcV
+AEZzHLnt~hhN6AqIEy$4jZWj$tE2W85k01$`Ck@TkgJ$<FmgEEPV!VrY69--eA6NHZ%e1{^5AhV^;MQ
+YE);Nq@s29gf>f?N`r!kY6a2ZB2aRV{Vf8Dfp|AqWG-81=r5Dl$h1(2d<+JGf5+}4B@wqxSR?ObTh46
+UtgDLi%jnHEY~kd}st-Z=5XY#b7mV3nq2s7Jg^GxE$t2gqIMqhzk=UGAuBOp?x5A37hvP%Bbdub1-?X
+jm<m-F$^;e)0%Bp5!JBX|oJI>EQ09*PR>BMB<BJlcRB~7fMFE*T5b>Zpt@<<Xw95HiS8N&%m_Zp3_U@
+^n;$4)o`|Ma4K2kb_o}YPLS)2#NkDpo=E6ZT)js_t~M6_*`Kv-#P7o?xw<eLa`7z|Pu%;nzPv<y%c$@
+p-nnzlr2*YLx@)Tl;PxO0GaIqzAWHJu@4)S7*|~A}a<-v{Y^l+HklX24-&Q3Yw%aJ7-(0H_z7Ev&48`
+eoO3&NMDr7m=^nCETvpMXVrPbc2{mO^MYa)CTSBn87NrKw|3NVK0ed=*4(V2Sq!aj9yrl)>v!7p!BoB
+T>=9WliUy&f^YD+sjjisGf_0+wg`sUfV>$?^Y489Vnk;AR<oMn4Rv<sAxQEnX#FKIuE{&28Pj9q%6f2
+T)4`1QY-O00;o{l@>=3naaSR0{{SU5C8xq0001RX>c!Jc4cm4Z*nhabZu-kY-wUIW@&76WpZ;bY-w(E
+E^v9>SM6%sFckgor?~SdIFRK50%MdegF#oy+Ks{}Mz!Tut0Ngn>Bq+2efLVT6DM+-bQ>G<pG1~)bk6<
+Kb*0rD^1NEuh30t<V%{iifn6C@FD%dfcnqbu6GkXGhJ_S`DtTTD%QdS_l2n8-X}9KA%<%ATQYdLGlh(
+AeMae9i)qKwJ<rwtZ_)dwk9VLSxreOs?x?!3X$iXCQV|Wc|I{B)E%+_W`Ns<&bGX{=@v@@-h&Snn<Z!
+Ah1O%uSU!Rc<<J5l4l%xRkVL0DGu3hhQV$n(tbx(e^<Q4N<l4>FL=m&<MnwOD-n0WDa+tJyViJw1ARc
+z&36YbvF|s#`8WNLW33GISt4_OaoJ<~DvwX_Q{}7KqBPLC`EcvrOA_cAbtPoy{BjNdMOm>5*){f=Z(`
+)PTI9>%~&oCMrFe3By<X&`|=ZERpoPTV$9MG*pN^g@eqp>yb&}0KTXAH=R17KSnWX4$cS{Lj})h;TVk
+75w%fXjuM$c7>C7ZvydGPy*mw>-SJfC6tLm!Gz#fmj)v~M2~3uOtn4Mzj;DUcEf69XG^cFiB`^b>b6h
+0s;{jf;GVNk*$uzk88Ej9Ke04X(xIJkL<6cz9y|!uMIJaKPPgsM)(uLY4Dy6p%!aG(koTma2s)gxg{D
+A!fIcl;)i?Mr!m3=Uz9qAC$&N73ZU7<A`*ijZfxLJ=&B*f#ooz;Fpc(D4WayQXRcLUV!eg`r-<XP_yi
+&Kh~{k5v!J}zHcX#Gs=k57VAOgA+{1Mvqt5Y*Aj>iF>e$@%Q!JWY}f<Yk)6GM+2D-Y5TW(f$+F*_(U+
++H9z(<-sTY64t0Di}GLDgLnKs#_=SxgA8lq$c&&=+&gE^Y;m)^VsM2!1lM>CxISpWZpBUEaLFSgY+IY
+PC2Wt>oYY6+_V=_n0sqMk2P0>IrEA`n4K1~AXZ;>G*2Pf~S!SKTW_HLrTDVu%J?nO%Q--sTW_LUt;~q
+^kGNF0C7xo6Y%Sfl#rvvrHQ)kPkF8npK#z_Rr($9ocHk+Ruv2A$Ju&}5&>UA766fJg!IaH|y_E8uWYj
+js?0sZOf9SxQ?-;xS%RpD**H$MpiiJiU(Tx6MD`j&s>dU**K7Z+2wzhM@QiPg|YY%fXS&DKJ2%CzB*k
+}CvU3s#G7;Y~z~|F-P|nn?ZL(ECZ=N_8I^RY$-nb?FvMtRCW{;C1P@CO-jCO9KQH000080P~d=N70jS
+2+0Nj00b!j04x9i0B~t=FJE?LZe(wAFKBdaY&C3YVlQTCY;<LEb1!djbZKvHVQh3^XLBxadF@$CkJ~m
+7zVEMKorBhZ;9@tALy#0Gg5=msatM$H1})NSA+qR_^ltp`J2R9lilS`Ek8KVWAX$&$a30?bhvTJIYc`
+)Rw|1-Le9rRqMrq6V!l+_v<=j1|OyrNb$yJ%MZJB3E$a#@lsd-^;ZkEVnRu7#oxREzEH(9}rVW08>$Z
+qN7%sous-Y__XTryMS0=^nqEUUM8Y_xpL)z;jx_xt;Mxc^I)a!<5WnpyQjmMkwBGME{))tPMA;E^vqZ
+n<Hlvc#rMg~u+Jg=nTrn{la|r!~SCYfl)ZKqcDa56wg>?#(lwqmAeD&_JkrJ*bi{Wu^RS8(TQTPTP9g
+-A2~pe4F6Y<a=l?NAsRdK3lHs7ydBm+6u^SD<*<lKKag+?hw@nnw(_H&0K9PV2BrUgUo9lC0~OesizX
+^GM#oOZp_zpi7b5SKbE3b_NYX%6rnsM#aJOsFSZ=@-nm_KpiJOS{(u5>%+au|+(-`RrJ$SIo#2UfoS;
+!0M+8T*vg;ygOZbBQSu%VN(#y3hZDJS+gag@{@7O{zwa%@Tg4nGjTjtup_f=j#1ibRn0Q88+=UE3IXJ
+*4Q$+8vKJOgy2z?@}jja0K1Gc6%0l}=q83-aGiJ~+xr3J66`A8JNsGTo1MSg6znI3z2{MOSTxuP?5(#
+}l>YW&Jb@27Ued^(}jn43em#sc3v7L^W&3P_8$27Y$o7&^~U#LLZS7?;|j-touc8<tcGg4R_dc5kmt|
++DX477E0_Qj=<NKc7)G!82r+Jnf#f_jg3rtXd0Lz)A_~*ykH(<cZJ-OV&vlZ5*AZfJJ(X=+{$8yai75
+h%u=%t#quX>q2p<#49tL>!FDTdz;>`gR@R&KgXU0F_4@u30QV4f4y+&z_=O_J6iWSI+(kfR0QrbsVGt
+fbS;CVhT_Ws4PqaV;Lgn~osft290gM*Ay_;f7*9-jWJ8L!w{&kK@9Ooou32{lL1J;OGICi2ZQ^kW;+f
+5{b>Q9%BqL8mc9lWhyq=e*(W%Vv(=Yp>Taw#fbz}FS{wlf#uIYj?>bPr95qfrTVvKW6gIwKtSc|GEwT
+9v02ma$}7&qfhuEdcizZN@%J$%4{m7=STof!$;{T-zz2gIC*=?(H@5z3paVs}G?RXaa7BQR-$$kp&ki
+W^}t$Fd`26Q*!aqthq-M$>7!yUj~!3K?P(WxRw!CzMk#G*fQimIC&c_lB$0AmY`w@VJmUPL_!3A_ih-
+Pp@<_GN;%a*2~sL)PB*A{(o>-KE>Ygc5c`SM$*It9y^PlQxWNc2{L9t#x2~{Ht4cSm3a#n!O239nJn(
+$t<4HYOU!S|sCp#25Y7bU-3uQ`P?b?AA`Cub7q}`0stA3T8y|JLbW2j{;^RTtjxJs0kCMw;c1F>7$co
+{<8tQ8%sOg*5#raY!nIAvHhxAi<xGcR=?RjDBz?;n4KxU_=?X7!$IRH1r!LJIe6v_Me}WZJ2t8#IE7R
+Uu-5BJ5yCG^FDl;f<w4Aw)kmgcvT8I71{6h-9w^`7^K*crdad^C~hCwn0t-&`~HJDGOu!m*E*~bBae9
+7eY&8pbpeF&k9(5IaZFLCl`gve*Cjl76`Dn`Ji)w-EUr~2mIC43WrOPLRBgYVPpplSeHm3Tq?az+3F6
+?dpKXh`3lb0aK1@t<Bn7-I6<3j$fe{kje==5)Cz?ChRiH=;4NRE{NSA}d~T<&fXO8wc}iEQ3((5`h|<
+afbc;Sn(hi%xBWe6Yc&|J}_vE87yuh1o;3NG|`BeLq?#Yj1IHJd?2)m=}JzZbY^%Y%T)Ah|5o(?B8j;
+@Fm`~`earBjSyMg<X|iFy+bPgEiyqVQ&0jA>S!w&Gw0a8+@v5$(Q=DQAHEtYXHSE}qvrJM%ENyu&}l{
+;2DscfZqPT&A$fI2@R99+zD?Dp&YL^VK0h6Ke`Gy@~Ep^zf=fpHy|=FpyPmu2=s#MTReI`m~kp`ih(j
+vBuNSkz2+!Ne{cE9?+j)#_oXwPCYx~U!*xt|4dO66#sGttaUZThG^7EElYK~NYwHh)|ULucj_ft>8_!
+)?<4iJN~#{);9aOs-R+eeYqy2n#K?{~Xqp}o$EQADoXeM&q>?|yag`2qTL-ArzXAum#%of?OY;|%>oW
+(fSTAIH6MZG^pny3qFMsoTs?<vT9!VERn)x34XWKnGVI$%CKbZa_Fg56Smcr4xw7rVv>=6n3l-3ZALP
+=LwEgL=l1J<2iQ;l<r#>UH6097l~{0mS^0|XQR000O8^OY7yIIrZPu>b%7U;zLCDF6TfaA|NaUv_0~W
+N&gWXmo9CHEd~OFJ@_MbY*gLFLPmTX>@6NWpXZXd38=p3c@fD-RBguY@zf3f?IK6SGph4W&(q0BKe4V
+dYh&d3;GrV^L{+--3W*}#pDSQc)U3uLH5C!6bZ}UfZ{s`cD8|Jd2ou*a3n8H5JKmUc8y{$11($|q~*v
+j@~e8hbf@!5cKl!~gcytr0S;`Y#%j*D<!OHS>z#lJiuANQZ$PX98NKis+IfiXN*36{cVGK2kx>$R$kJ
+yx&TKW`dSho@65EoDCzDY{6mL*V0|XQR000O8^OY7yW3^fp&;bAdb_4(bDgXcgaA|NaUv_0~WN&gWXm
+o9CHEd~OFJ@_MbY*gLFL!8ZbY*jJVPj=3aCwzfO>5jR5WVYH3<@DO>zejj$RPx`*U-(Ouw8`K9w$m<$
+w+e2^w)Rfk7VUd3z>spzTSH?mfSkqfe`Hw28Tj`>U!$}$b+|f2q-4MfJQwjudFFxFsil<3atV<sXb$J
+k3+`V2v;sB;vhZFv9h+t2Wf&|vaW7qkd?*`DO!N<WsTSO(#e{TJzM#337nS?R+(&-$r;<;TwPaN@uT`
+){iH&NMA!97W5##qe!t;xzQ(66R%_`!d{deN*{HUhoct?eKsNT=IP~bAaFI#`G}9k69z(X{y+d9A5BN
+Jshm!AM16uizr32>q1leFq3y;eaBJ-$Q;)2=wnTZ%azXn_DAlOGVpbQ*~EdK=)Exhy*W^CN?=<$70px
+U{H_PQnI;a1*p_?6TwCQSp`tGGqajvLt#Y}6GMl<#h9r7Ib8a|vHql16>3t1``~D)KK)|AG7TKAf@Pt
+tLXf&7EF}iD;fJemlNDWjljg^Z~3%3+c&Nf)942Qzkgu3^h(S$iwqJI?9gcb3CZ(1UdyIeZW67&e0^N
+y4T>f)mru*#iTGx@_EYpqEOF^p;r@c@JTn_{`pHgq<@Kl<0o1A;gDUxMFC?S`wLJ@0|XQR000O8^OY7
+y0x`qn5CH%H>Hz=%D*ylhaA|NaUv_0~WN&gWXmo9CHEd~OFLPybX<=+>dS!AiUtei%X>?y-E^v8eQ9*
+8lFc7@^6)Spy2=M?U)KjG%+Fla1+$;x{u!^zK+HTaoz6Lj>QGKz-J3BMG9@tJG<q&*eloDu~EqhQ$XL
+ax>6E2~rPjX~U3Bgcjdz6|yveHh7VbQ3nYp-X-i_&N0tv32eJ7iLtzLBrQ=fmsEetQ){^f<sq%gM|kI
+xlifhRc<eFj$_HZ?Z)(LNqZ8nN#y-4VD7#&Te<}ly>&6s8_}Vv~63%ezZaNa6#x)HzN3v+~Z0}>7n^M
+N_9eii11^D`BEuXG7Z0>plKlGs<$96irkNx|4+5DF)rxV$BG=nyV3!lm@Vg-|C$k|;5-}=R+rSr7=O-
+ys~Alm<x||WyyCW?Li_+wO9KQH000080P~d=NB8lKOLh$a05>uK05Jdn0B~t=FJE?LZe(wAFKBdaY&C
+3YVlQ)La%o{~X?kUHFKBdaY;$FDX<=+>dS!AhaCyxeYjfK+^1FToN>5^{k>xaZ{cyJBnOv;)GM6-&$e
+ByW$*>R!S*ZCc0m@P@_up?907Z}lDJSjBoia@%aNoPw2f(h1EFnR#DrH%)ARs)+vqBQO6j@wK7U<s-6
+7dZeJWD5}OnI0^EQq;e1&zgMg(PyGPlF9hqpX+;zOCp;wv~b0<!m&P#qMlG;G^JIWg5yXi$w)lu&4~#
+h;2ibOY%}J&Wj=|G*V857-?41X_%!#(p2t|HyMxSF-sV%O~?}HGMYY@vfxWl0+vKhL-t}#3mSsdqL)w
++MpV+86#UI4M+(~{o|_*Pc7;?*UYP-)rh;zqKebwcFcDHTLQ4)ZBc3<?HRGw@dNOVs3~e4-*1F{a-~x
+swQqqNhTd-BZ@)#)T2cp-Wl^0p6|54B=jsT;D(i^c@umhlLnZ(LKJHyl)DTE48Jqi+<1Mt89qaJY)l%
+e=5EC8nOeSH7)>32_mfR8sOvl;nL>zftiZbCMMrz8e7{mKhd2)RV~<FOVDKSxB|@th=OEcr=TnQ!m6J
+se*V-><mSG*bLGV{%Ep8<VWq!&M(Y%_MQcKeO562ZBiQv}87~4fzHP^w?`c!UIDFbbWT#VY&gksIZxe
+60CenC3{PGah<&Yd*%=&5^x(>wU^p-vLb2&E4h?jsl!KT^eGo{cFzib+$DNMatJT5?c-RUlWc?EUR|F
+n;|QMcm=^I)`R*}I^79FKd@JE^Cc|lmR)_n|<`c+#r{gDLE7FNA6L&rYv?Uy<1Zhca;*jC93Gv_Sc%3
+UAbG<*z+I2xw5rbLPa@&yw!?3>j)bg$|z2lh*y&3rdvLI_bKr8SG3ALrm1QMlG;!U&VuPl@|_!WdM<Q
+aa0wE4+laZnC9bRP`zt_5#}<(_OBu`Qr-@`UW=a52}R$i>1AMq3L-=`4Zlbz{_+80t^Rvrl7}P7RYD4
+)+c-i~NvTt_z3a?#<Gc2scjHC0@tDl;%08>hD*^9p8sJdcH>Ka%I4&vkRuTO!{HoWe(2hksM;Sk_Xa_
++a?wab3*`q%nJ-%DDJsj``)?N<GnW5a|hRcOE=^_+CCEJ9(hFkHQS=%H_y&KjmND`37ttw{Hjg@f$@Z
+xaH~PP91UV$WmJho=bL6R^_}%@(wmXSRnm%Eghr!L7(?4a{&D^K)q)kA#{754tva5;x(e@z(5g=%zm*
+wm*;|t35?U=?yl{;L2or2*T&fgRZ+iU{6@=wu?^qXX!Ga|dGH5(iTf(rE^p=QSD(SYtD@(Wx^a70o02
+20Af_)BY{&%3Q`>S9tcmoW%%YyP4Gzp-K<Wbp_S#>%fAZ!#Tq%ElGo7yJP%~TM}fNLR|^vz@4$~B%q>
+}2uA>^RQa%n~j_7RNMY83<x?q;bBcOD1^;c4??x6Xz+qHAtAG%H)Dc+bq~u<-z?LS{P-3rb%cD0{vbu
+YIkS}VA1zFVc<=OSNj)#Io$3|+MCep`WSHseCwd$axqdF^sIwU*O5r2;XhnxyUjtV$3ckg@QZ^$+dWD
+h;}vgQ_ay;{3I+^(!Q$0~eEaR)JuPm9l`B2Zdx>~zx3~8GbhHQiC)?daso6W(2kI2MSaQo@j3O&u2$(
+F=m_Y&&IScuUvuF%a7RF`7&T2vq{bm?-pWCg$zt&8yp=;yV01Adv9ziU~ood`6GD8)4$_T)bx|zmE&Q
+`c+QH!#$M69~G>xAgA7{E<oSozN+IJu@0=%>dx=loXkVxJi;GC7by<9-jLVIeR@6eSp|Ee>iiD<Ly}F
+1=&V!hbKAF}0SWRu>C+CnrzImGKV;0W*sbYz7dvT*k~$GEQr$rTQ@nh6k(L;C3iN&j1k^u1QFNC@NJ!
+0Tx&RSVibN$!Jt7+TBGnW%JLiCde|pMj;o-v2FeiVZ9L_N;9m7Tjj@;fz0rD$qYn9u)zSvX;vgOwuqT
+H6cvEU;QM7!f@vuHD|a!g#cZdCgJEY7NJ<lsraLrh!Ldt$!U=LT17Boi0qI*7j7`ZkY?KeLU*Tw^w@y
+@3QF9UwL3$t}kI|F(BhSRSMNoM*rK=TeW58eTfFT4K>^c>Ad&qW*I_QytB`~G|p@gO?Gs1k9Kyp`c1r
+b`4niBsScv@t_ECHQ_z{rbZ3ls^{DmL|sIb$$h1GH(~mkksax1Sd4tdI=0i9sM%xdqm^Wy!B4<VMU<t
+=P5q67FKQ1;c}w7d*{NNGV)U*mu#)tG;=c5SO4T&~Cw8)^>MQJGbp$>g0@DJd88JPE?--Js^aw78rU~
+lS+viyHqCz3wVI#+6g{Oa=Ej1#u~^fDu@VC0%|UK6tQ$XCG#nysf%@SY6(ABlHDG7i_I<SpuNiz*d-i
+hGl(8dN0$ds5VNjU5Az6$)X0nY7*-8aVwl!)5)hQU(-~ek&1~smYO&k!3)~tiP|H-%6~n`fGQCT)dz`
+``VHRZw)>8**St-HM_L;<jG4czx_ut2SiE<SC>PE3R{}SwhyWNx9hHn5)d-&8itI8^1z6!sVp{IRuEB
+lT?-@uC2#(?~aPOu0O1!lDz(<uoC)I5U`N9Jdzr#SXZZ?g<`_!KtBX?|K^o!;|1ehQ_txUw<r-p4+gI
+j(E)F-oU<rRJE1ebm%Fu{4cZm1QKPkWA;)CMOZdv}gErV%VzTS})vofX3F;sfh1SIjtif^+2DGHXC(p
+4E3~VNFq>rYc+*1213K-TW3`oWKy4yTN2>Gj}iiD0+e&G)dZ#%-#z@qeP;WBX;~9(Yqd~1|G(%~9Z`R
+UI-=^tjV@Tk?`eF;iV3M0%znyJW~tZUUY*+5y(n~D-BHsSNVXC76xI1DLOL4xrc&nGVZ~!?uUzoe^}r
+7bq=8(kigK#wzDxbusz=aER8?Oi5V6k6IKo_`T2WID-k5xLO>bL_kJPSB=$0pCLfC4>Lk>v;Dv2tBP`
+_f>eb{q1@v0zcw>*RPuzHlQHA6Q{tGYADxdJNwM8@4pGblm@sVdDMm*R*#w=0QNY7*?dAON83%L#Vln
+QAwU92S94g$j)#8ykb2s!+lnLKUcv<~_x5P<<Q6V)yliXp|k9-NBrQlfWLLDGla0prhb!JpAP~Zwf#+
+4KpJiluiZFJs^$r6TDly_~z=(i|e1?&dDQzC!~fnjx3&)NNoBVB8SnEhc6@VlluU+;_+lgj>+5ie}DD
+z1@VB#am}PA;W*f>If)$%qRE3ed1UeP;(Gqt8xZ^uITs}OKaG5t?#JYEMxOQ`Sob4)$KD}i{ODv*l@E
+1&<a-N6i|`0%KD-*+&-hE&71fZ0G2b{Ks7K_U1AF{5I-%y#Mg8eNXYY`x4u{*HhL;`*{G(3fYvf7SbQ
+6pAxEqZ&&C9OjJI><Bs;gChVbEFxb;uz(j@JP76*(T!Et<FHt~(2R>$SmgPN>RvZ`AGUJ37*sLLE(35
+p;2SFoc}p^FC$UTo3&hr>NbFtLN9xdt#zvG`}Irm)ya0N$HcB7R6ydEO5AiijQ8H<B*Eq9p=Z3Ri{Ri
+zYa=dBEq}S(THfyTQNm_e^gYxx1l_R7h0O4eT>!P<+_FIxjD4rpyxjJfQQ312NkQMR%m~yc6YVpa66V
+BHL(P!rs{0;w4#2G&3Apeh{dT9n&wprx3EX#`pwmwGx8j&^PV;lWv<F4)N}Wh@*UV`&)*5LsUVb84~s
+tI>tk~4NF_rUcY~Li3V!I$3atCFcf<O3)teHXw~5psi57swSmrVF$NgDcMdATco>vnZxO45@BeQ`jQu
+S2bTSU+vrP>Uay&A7?YgC`%Qq-ZPU-$B3nk*wqwiB{DBU=>8-=U>8<IWY$!B6|$swYr<K6=M9Ft3`+`
+mKafyP2A6UA#w!oTkofA7I(;>HDx1TD&e4uUH;nRom_%HZ~>S@;t5#B39}+PGW{R8f55Dl;2dqL$6VV
+{DJs#%H7wh*dLXVk<XqfO7I7m_O)1+jyAkPILxNy?PKNbqrd!Z$lY4KL%XR<9jt-W)h)vT@_o6)?&`h
+>zx}G&pULpBEgmF})h*bsi`M5at^?@arcjsrp~LmB$mHa-$8W<X=%wc(*R>V2=)J8T#`!#ru($il)~>
+fnJJ^Z2Uv2x}v|{}Y>ggkKS+}SIW_*z)inwF!*ZMa4H<umpi5WRgP6kH~rv;1v^^l50zChD&PEH<)la
+ov9IHH<82BQaGnflu3xnnzkQJ_%H&z<K72(y2yRo}gSfmO78F!?GP1q-v=6rM(dV~3*Dd7DV}dbKdt8
+{<6(B--y4{te)|PF(u7zCrgMo0sUEO?mIE4agn8xj4Cu=JGxsuNN$hSn&mC>()8A^Zb5Uz4fPF18<7r
+KD{14;8x&aM91T?<KjRYKx?R8i|{^t_(10G-o1H8Ue6bc=ReNLPxJZJ{K{**rH)YevH*`5)vu6{gx)d
+j!~`OT%B<K~_nr0KLG`|*c9~^5>prTbC*^{XMMLJ%XoQM17yY@qy60JHg!Md6J<ykI1~gK>tnT*I|B3
+NmP)h>@6aWAK2mtey7DoU80006200000001oj003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*<WMOc0Wp
+Z;bUtei%X>?y-E^v7R08mQ<1QY-O00;o{l@>?jTwf3x0ssJg1^@sk0001RX>c!Jc4cm4Z*nhabZu-kY
+-wUIbaG{7VPs)&bY*gLFLPmdE^v9ZR84Q&FbuuxR}ebcfaf1jpob!BfgKhU$Oa530z<LsRE;bdlH8^k
+_TNYOE4GthJ#;$QmPkH6KG7_#sUXYB#y1wT3}jUs>p|?CQH{r}+b2-SSLvkD2{c;froc=|k5(whSQ#*
+ml}epBru4Qb3bX^%o%dGm2+OTE->O>m{%M*U?Yz+5jY*4_#`y<hQSS9AETp_*Vyw1U%41fEx|VtmxA6
+N9D~*NjJOO2Lq1K|p1T;NeEph~@ujNW#g=)}6@eKo7vh0CCI?%?$+Gy-dVa=02b=xhG7*jFE3M|2!jI
+@c~ggT&2;92NGq3x4!&R0Sy*xdd4qT~UAgz>2T;08P4umGdMTf`0nx)+7E5WQzgdRqV0<V{8KQQX?5Y
+Rn{<vrp+dtFiDi(8t#Kd*>US;l*ss<;?+g!0#xY@ces?q)lso;$g?Pqmh*E?6<(FfYy2n4J)W3b%Wy}
+sv(GyMSK%z{t$bf0Kczl{~o^MIfkH%cEn-XlTlBq5MJ<YGK;you6AWSi=WAOuP|KAdJ_#PM+qq}OwRZ
+={t%B@$cG|Y5#}7H>W|vpoPiCcpe{bYkVPr+g?n!4>EfK+e<b!#Qf>3Co`n@T4MZ2(Zxgajj^fK~^U-
+78f7eQ1nJSdFsJ#}>O#&P)THZON+4?|}nr0yXsSnz`>HaTme>3(uV}i85G$dlSaGH##zfem91QY-O00
+;o{l@>>!Oo!#-1ONa{4FCWw0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJE72ZfSI1U
+oLQYomNe6<2Dez^H&VKSkzuP0{U?>;6s`X+69^v*md_(6b3DgZ8ntXawt1#(f{5VQj%pkaf9R%+ZsMT
+zV~LR(P(rHxw97YU|a=m4W%n;iv(-uVO1Mjm}&!sS+CJ!72v^C1xa1-h_LpqR6j57eu2W}b;$*kDYbB
+rd(;6z$Fc~6K0<1!@Brayhb_nyo?OkpVgbQ1jxy!WqF@2x9=)~@woHO%N_{(lYyLIJ+T3cQhUi{<<7$
+eyksT%kn!(40*i=5r^QpRVW%}hdRb{Z)A|aGD1vu^=wg{#Q=+`<IwraW3gjw%Il-;`mVXSS~x_Yx^DI
++pe{4>E;??r-6JVj<x=}>!&q#56MUQ$u;bsn6b^3f1Z@KL^H4dIDe;A5y+5MC5ypHe@8w&Y-iN8&7`C
+eW4R4Sb{$b(w;bZ+vV`iY52S!fu(43Gnz=Z9IyN--+0L|J>?N&Iycj&4DRk!H<4y9<82))1F}%WWq<&
+L79s8H-TbGPLGw8>;Z@cMie9Xq2<8G2*I0G9gw|m19l^}!&P)AMnF<iQzt2`TnMa}qhy1mL3i;~O)xH
+^#6UOMAk}z!aYa)@!cw%pSlrZ*`V^9Ra%xxM@!TbHo{E{;U})pZV4snrQ&9x<&K-{zcX8q)w_JPDD7a
+GJPvFx!cG$ttR%rOTiYw~Ma1m0V)PXVmkx$#aN}hgU54AOav&o4TQ-v-*Z8Q#BlW#%iIqukSov7)mP`
+)9pfm1G{M53!rB_HpbB9M^eTSnq8;T~u<ECkPa89<783ul(E)IpTwMaN^rQ>f8sq*Tn>a=EUh&6Z1vx
+AT0i6*<d6TBh$)ZpytO&gxVEawf90Cdja%1|!SrU@Wx&1+H~%!xEFM;TYW;oimL&{PV9;ipzjq>m_?E
+rHj~qr>!=>XV0hb;lsm0`wdNI3iyu?@e|KCebQYiLeCDm*uw`CwsBa8D^`W-olsU0uid=~W2MV3GI42
+~#9i>XwFw!6up$%89w!-mNpM(1yx!kl+nPyek25uVHY!{>;ytffjfxG;p??W{{Pk`i24}EQSb`DU5U>
+5y@L1Z6!LyPx#Q#AiNijgn&gewjUMBR+BqYGcog<;6tC#3NJ0`@fD{CHlH^wVZ1o1>^&gVTeFg&;R+E
+~4^sg+&<Fj&vi{#**n<w=RS<|+x;SHpIIQ=Uy#VWk6K+T5Dbw2E=CWbHR}=0{vUxN5Fod{cY57%ksoy
+PY){=cHt_HB6>M%2JnB;!Iw@KqtMt95;&6pF|zA&6y+@Wn?{`Hz<qqMizEGHt#-W)*ZO_C+XblV+mUl
+C)Z$p%sNi};`uxses;n*ji+uf@f%qG={w-J1s;pLG!=O6YQ9>UD8hK>ah)|cG7Ce2-Ca#i5SYQhDZZI
+ySk2Cai#Sb3wuG9-JgXgG&YvxO2luyMZ$CpGhbq56a0KPE7|AROxh9*zFI!T*N|v91q&r6nvUr1*cd=
+%C%AhxT`)?v06562^v2!kY5ks<+vXi2r*}u1_f3OP2HLqlmI^%z7yw%b(n@(@OH<}d}U$!fC7#nsgI=
+s;`8l1>$!*5+*<)*sz-uW>%?QfOG+-)i&bFcprGkQ-Wxq#t)r+Dg&hT+ghAv-=+{s&M?0|XQR000O8^
+OY7yy9}waLk<7{k2L@QDgXcgaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWny({Y-D9}b1z?FVRL0JaCzMu
+{cjt$^>_ajtb(8tCST$f`-m}ZXx(HpumnMEYz17;piY#;=5$n!yeMv$|9$U0ejIs6$#T;lh6$)6-I4E
+$@0+j7Ms3*L-Lf@pBkt~)+-y~27@uoZwMN|8-&3}bAEcJ5p0c)<rCNx)N*dAdN}ntd#8z&LyAPsXsHV
+{JqlaU}M{{THx8lSuD~eLp+VI+FzeJa6E9=$EN^Ar?o-)JdmAI_6Xv}Ls)V-+rM(8asg+4iX!mihX_6
+oM)%{$R(#v8#T?Gp>OTZ@{(KG6VhrshA1(lFs+!G75Fz7@hFWTmJo_`&Oif55i}ICjqvc&h=hQEb}?V
+AZgtYBt;$KmhQFzfmRlJNP{ULSP{Q0swvV1kn>voV+Fm*o?hVHAt$G+IX&C^DR98<%Gcp-=4EEAo^JV
+n`Yb--DJvM!GVa$l$MX?tud+|9ooYF!|>_NxRdS8Xl*vt=yn<aOl~KA&`9EE09Mt=K|21EhtyU%@<{F
+}eS0XOFNbK!Z?gyha}TkGBQDKRnAO&-8r5zOOJeU{X%A+~uA5d&PClKSoRk&U8nk2~##Z4b=LC5nmSE
+ztmgeqm3=}S{DY>^q_h^LMSR+8SHJl&X46hlO_y#nY8L&0e2vLL`2!NfE5gNX-KLNWcB`52XW=*?l02
+?-K!HB?Y>^Ap9EooiZHuMwMv$1MO+0nr0?Lw99#=o8fcprGxdMfPqEHu8|R#IrRT=YluRHF$7K&9G{)
+`kaQ)r|!8hjpingZBgLU8`z=azS3F4&Rnba6Cw6w8IgRuwJfZwRl5_5g^(RuA|3}pbYH=uqHLzwAutn
+vM#F@*y2?c90~)(umS!PpeUL*vbtvqpfy$m=z%-t?v@o_AsycYIyO?H|Kqh#E$~C{&6*o<f;F29v}LC
+1AzPq0;X7(pL5eUV8Kp1WaXTNEyt1Oqy@`YhPViIuG7{;G-Q1=x9gp-PG6HQHX9JP;cUZ0rdf^cp6+b
+Cik8eg#_3TIZ^W$j5o?2UUCqFsDd!bsy$|CC{Hud-}&;<1uEWJ~JWlHgR0<{qk$!j+H^Ha^9>c0@(;b
+f-i&JLykAGQ}x56C1ID0+;cM334GW(oPi1)ie!N@(4QSF&k<Rbmb{>kKAiDp{3mxYPpl%3N-?)>OpeW
+z(ppqrxT(*Wec%5eshk)II)Iz!{1eDrS~0T#Iy(TXB9eCThyIN=x+B;1lNr4A*q3YIsBTM-0X|8G1fN
+06|ExOZn2GD?X25izSl#6b^0q<}(gp>KSx!oIC=sjLR$7_eig1|3v#BI#Ig=08Kuv!x>=paX(T7jo7F
+U;;`m@PirjWl-dza+yYpCO9ccABG-7~V0((h?kzn}$$E`Mq3eH$9Mqlwqf-`&Cn#7U7NC<_SvQZt-Ff
+_ls=tJ++>oxKUUTgN6bU3g(nReQ;D#u<<M&xfJYhfl@B_pgy;dz8rlGhd%vzygKrRdvZxH25)L2&hHE
+cvLY83!5^&EfdvO0ES*Fvj1PnQiH;V{YyOqDXIo<}2%Ag9KG1vmh;1D3b<S<%Y^A~(0+Lnfr-kZAKIE
+GxPQhcTR1*D54OO>8c|R>Rg@UnqE9oBdpw1_M;*WpGoZUk#o0lr1sYwVA~Z@IICzm!f`C8u>xI3e}S@
+8ph}&qNLRxW8Or?`<7Q1s->Lbc^n*x{4ZwAR#mBrpfb6n$*a@N`T6PDZMw`s@+)U~VE!|2py<o(w^0v
+|?j=$?cJKzg#Gi<--_6)rmaK^N8T&d9hj3d*=kexqmXvxtfnq54PMIQhy7y=HpRXdG=pd{;oKfVzZb2
+K6U|QKd%BCGuRGs93#Yv%u);&80oO_CuQ_#t4@POa8pzoqdt0z$HZ@ISh&)hD+)`b-*$JlOV<4TkZf_
+7@10(}QZ-0H5DnJXx&Hyfp$vj7Le!)^lQj_V+#zhA%p2lIAAyIc)(a92|208Xq64sLL3w3mdH;S=_qF
+UV*|MgYE-VV7TLi2=p4g=QB&6V@LCBjGF&#bcl(>?ZO_p}cDNs>lA)JsK|GQ+`uXkH;9=H>vUDmZZu}
+CJxYfTbVb0Io2<0W47ndp6zzKV)u2Sn$@$j7cX8s`$+wR13CXbfWyKJy98!DiL)4L0;S=FmMZWip(`{
+j)cBrQ0896ViAiV5KCOT#g&`G`SX1h=7JTv2yIbtjxgRNVT=f<zJ=1mHlJ~f=_dZ=<lj&EGA6`?>aMp
+J<R;1oqX6bLzl~{^S{V(e)Y-~DZff`{Y3>J}%<hAkDNF;*GCEH;yLTjIOKy2G#R%OaHWpxFrRJR7oU~
+F1;5bR1`-zP~6WP?c|FFwL)QEEQJ`~aQq*i$zO4t*3IPu$AHqUr+{6EoK9aNao9Zo7?CJ3CIW+uCSh1
+5%<-y%-8Sj;U>8yAU%cI!d%KesuIhGQu(+!kF$0douAljwA7NQ6VLc+kZzi*%$~c#R8b@hul*pI6Q<%
+Pw~|LC44$vqljP^r}Ku?dW%{cn<w?%o3=E2t*U3d03Nh}vU`BXtCectx2=OzgHaG7mo{Rn0fR0f{E9`
+t=6k~QR+MrnOA3^}qI=&;BSKP41@sW;08~j0u}kB5)<!}dv``%&s3zLgx`<8V3ShCbbF62!w6J4@ZU`
+`w#gZDxT4MwN*RfDV;)sFZgPtmvaSTZKij)aCgM<w&(U$(9Isi`)?gt8XrI<uvot{OcpxwCga4#(raD
+`pY2v3@O?{xNUxskUqyKaxqcro?2k9_qN+uu>F{~<isK|LWHxR9V*E2C#9>W9$)krD}fJ~W#rw!Yn=V
+$6jNN_eEt;TKeF!(<wSb2`kihY7u8r~PEyargUkI2Gd<0G!yi7QCq>rir#Rrkpx;5XT-&Pju*M&T~}!
+u}3YH4!MPY`gL@~o2W*HO|-e_D&`3ydCg^A-J{IOfnrUb)X80<o+a-^&yR{YY}#P!$Qu7L!t88x&PG>
+gO4ocfdd!s)#VgoN^xNj>V|P<}lz^ZokAj08!~mib&ZkLMJZI07rw<()9+FFYve3@@b`=%8hhb+cW@|
+Q4F9q!2*l2T<CKg+@9Zz~TaX5?YoXeh$z+(^(^R$EP<*wN-)}nkzE&Mu2TYLlup}~2Wov<M(?)h4p;>
+5_|>TZAN&e{hNdmCP0QYf*ThxgAeosf{HVb<R-gsH0&syl}zT>c%_giQD33A0=x5a;8UvH{ZOqU2a}p
+$9i#Vj}8ZJ3_JXxaQd4?7_3`GH)G4Uu}uf15KKtKc@^0w}n&@|J|F1;mW?yqTH-&tADSQzeh0Yov33C
+Pn{XnoPM}rY!~k@oP0}W(8)>aiKOotmi9>e&X%=%sC4&t^!Ud5$j<w3+j%9+%SE@qKZzFUd&qJ&@go0
+Z6vZ=FTt^jAsI}cSn+H7&5Rc*70UB_9VOs(**HhPJ?O*SJX1MmN)OO|~QHyYqIXEQriYMu^qJPO%#J!
+2J6A%S~+et)psvY^GLT^ILVqbzyWZS{Fe4}YWS+~P9H*V4%Jm)&9{b@x3!F(LWylK8wFxjW<aa+e7Z@
+qU@dh45rsBeQwbfZ2C5PF%`<a_QW?JaSyevbg@JRpDln;gRxr17QA<Ckq+QmNx-h1@YjN91plI&q^{t
+tfMJSAH9*YoT$F!j2;0D|GU64sMmNAjcY9y!TlF(GjJwTGN9}%O^>!ve}lEPIpk*&e6kQ-Vmo&%0qg$
+4OmlS?#G!^oaRnsVC4y#U*q9ig)OzDDYa5KZ4Gm5lPMc{4xid;)(ynrJ$KSA`QNvqtOv^nk-b~?My7o
+xI@GJj_&2nI&a>GeJ2#vZo5W*+t-q)l2!b@l)ueld60^4r`}Dq#cxXm(OK0wH(Ai~eEo2i-1PNYiY$N
+P=ZEVw`2Z$Ok;8iw!CCeZWkVDh(dKGv7@MEJ@I`-g&2U<(`Uwfy7)!th-*Dy{mm1H_!kdL4p!o#|jmr
+=Ak8D$8UYw^>`&`bRW$~%}A$tM!s_yk>)<I*Bl($7mYVld;-r0RQBNWsV17xDPpgP;BOaXgH}hSYC@C
+MQhtM)Cb$+fJV4zCH(4JPdkx(f>DWg<*!GG+t*o?Fv>y?MVqKw{3ztA8SW?0vJD-IkC-CyU1SQ^;4Gq
+SdP7V%OXehrv@-&IZ)F>CIv|{4n%~3DTddj5vWF#un%V(K_>%BZ>`-rdpBg<%nKY~Rbe9&7Or@-5bI{
+2gd~?hmfW}5(Z$|z_x9nK!6w_x%vH?Pdg*FWKfgRMu9#5R@IavpP{W44tyszIg2g!5lvKFeks!K82>y
+werllo=+MK3EL?ikf1>A1-bI49ly!{Qsc5N#1u>OdM(L*&nm8>Hh1`Js*#K&>KO#SxT177Jtyw3+kO}
+r(w{@D0&O3DZvv5{i&vR+*CmG3%<k2E3N4@WNXJ;Ik;^)OI+o3(8&HT3^K&@`Uc13N*MMj^xiI^WH{k
+4K9I<hi4T!M~Lpb;IlN2t0hJKaKrsMw8*J3SHtMf-h#C!%3zHNlq*67j~?LDL6%)d)WwJXlJ!v^zN)Q
+zt=?<TYAv&l204C!ZZ~UQW{|^D+Q$77enIq9Ghn+aqfP^V&KaC#1^_!Swonj<TqR2hOv?uupeG{7_gs
+dh`qS0kX%jy?BIMt=lJ|!gwwNRDq?(~i~u_*29uC}_aHCxFFL-U%rRG@fv7Ei7R}zN&alKDbUR1cMFY
+4lLSmN_aJ_FqGa#F|#~RNNj&2B#cARztb5z#b@CEVHUhip#4*!p!qbB733s6e~1QY-O00;o{l@>=~Dl
+r_F2LJ#SApig<0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJy0RE^v9B8fkCaHuSrH1
+*;;6yedN1kHJzPP13E{k`%2wY)AuvvFMbo4o{?W4EynWq)3XSNZE;iCK35AzJu>jS6y49qF8PCrXxjx
+XuWPbj_?g@s|_bb|DK_e-cd%|CPSNsF58k673HME6`RdwD?#L;uJRIoD+)DjD7<Fq0)S^~@t)ux9a$-
+iT*DSOm=U#R7ME?qIBq!88)iX6G`J>gjhDoLY&fmVcHNenioC~5-gb}b7YJLD6}lz-w5{K5XjPIfg+D
+*k>x$H*;h4iA=Z7-{53HjjG}@iX&1HodL&uzVe^AEL_U1cTa)l@!!KNrc3A`v$MygeYNKeE95|7x+)f
+TW*f6aVCpln6R!|w=~u0fTM!lspDkd5Ae5m^q{HB0C7+z^`k3>e`8$_Oizo1r^gZODYcIiMYDKB<l4)
+2~P~Ek9|}ozCD<?w8|quTlYV_4+!KpjSU%&(W&w<O`yWgQB0E<hXX__(2}0#F!*0as7i8#cjYOVd@Uy
+Ti+xj!VpOu(}qn^rl49zWyCGDW%&l;z=M-gV9E#tIyZqr%CoL(I|q_fj0A84C-{b0@C1F#i3>%CuDm&
+NvGSbI8?f^*eBQv`g%rTX+ZM9sVuH<e*RDy&AJYogHzk(1hX_t6?C*F)h7yDL<<o~`9`sG~;HGr7W+N
+l;@Qb$bO^?@W(v&CPKzZ8ouIi-pLLY0~^@kH+@$k0^N<6J-FGw2(I#`eguGp5C<4i5TBM^_|NQ&!9!T
+~{vU^uq-4e3tX<wm|OJAye8goPDTL3TcJV~fk!{~Y2rXezk7G7hJ+DTMtxswP1;s2;_3uuBHPvAP%=C
+@!*X0)rivI$&i-rjFF@9ogb4pk14#q9>T5BiC+Q6l8O2c9dB-j4yM)De1r;T5xjzQLcN^-vbyK-8>Rm
+909K6vD8bk>-*%2cC2T*`za-%aflmPgT50$k_?tcW)SVl%S?7Ym`YKmma%2_Je7@TWoSrbWA7OD#;O!
+q*s2&H3i>P=$hBi+MStfMW#I@ws0RQS9EwXfio=WNpAD|)ijq=f0#I<ODZE0-VFEwpI}{dCD&Z?)iia
+W@#fcVS7oU6a-ax_Q5Y493N#s$*+|v&guM#Kaa7swMICzAeP#wR{T-Ws6c3FXuWrm2mzg<>1!Vk#rq1
+_>VohGgfsHXsOg80-WGm2JWt%dO85J9@?i48RdWf31%yoWaFD*49iD!I-E5>Kqq3@w|4Q};;0Ju{gDI
+d$BkBVHY6t$5{JsS^H)zznHHN>l2FP1L}rm*;NZmd;hVpsR;es4YXo&w0pns%OpNKo5^S>rnOvqWp2K
+(>HH_kjAg8`pA}BSF+Sg-1uH^ZYsL`P!2$P#RFuI3@%0+P_HD`-7n>9Yv_inNv(JY@)&@iP(7V3#2Nb
+_)xu>)o<L^OhI7)G9cqf<d{nl{?)ym35yoz*5Z!`MXJ)`~jih34@E_gb8jlVYpekE9aW)j*Ko@T+>D^
+l*v5^&#5l!jztd~i3I5vDZ5GSdqdtU{o*Y=4gaLvhz&bukw+SijTs&~6G2dPeaI(<d%@mO{Eb~2c^lh
+^isuSku$=bbtoLv+VB1`-SZOgai#^<O1ZthH1}@G!z;<upoNNn29L$gcs{=pc?ex?b}UUOwt#CmOfD%
+nn72*cX=v{PH77749#34<5VsSjb84yF|=O^xN^t?}rFiReRsJlZ~vBG3zUNAcR^l>^EXkjD#3bqARy&
+m!r-7LJ}ziMj<XEwjVE5Wh=Hg`)g5LT%MlE(vuEW%}1oRlvY6;(EKoH3PQwd)Hsd`kJLTEm{KsDyufu
+^bt@34la+#VR}HEX>CpcLVsjjo!$_yAC6JZ*y1PK%#56U1Pn*V?Hl=X?*W+wq&y5x`j~?}gKfOHpa(;
+HPC>DQzJd^qx+t<d2BSt;RblbXKfJVwx+A0;ty~nCDLWGhe?jb|j;j`O<+1+U$N|NNiJ@(5B(1*|j+G
+8l$V~PCn7B<n|4sA-hkM_||$_$yKv|w5Coc_fU*%SCJ2stG}fL)wbj7@;a+`sX5^0%<7S!`eGi!XT={
+r8`ae|;jO16=?-iQ~!T`EGoq9uE$LYaJX+8Vkt&DLc)l$YCzPZ9;ra#J^|p4|xm7l<E`|n;47QbYch*
+m=jw=kVxuMw=En7i9m(Zm=AH#6Ok2tfW|UJ`+C=j!}PE{t`SQ*pTsu*2uE=wdM$6Tzf1(@gW_?<6<6U
+Ij{F2A3V;qbw<Hx`zs4ViVuLTu#5%Y)G)rkb(JPe+jJUq^(~ss9w0w!vWrnKd1(TDwi6Y06blUYAcHP
+e)^Qv9qD(pb|**=JSl6EYuEAMi?fqEU7egcC})^?nfX>^ooKZ7gpgDdZ`Bau7z2X9V$cSf(!o4NZP*X
+ywr*b#c;tPAn_F*gWihG1r36`JIGg^~o;WzxVjUQoTZG^JXW<ZF}U-&@+G-JROipHhXH<=kxc5-p%|$
+4kD!6{-ma7v!zzi4azw|6k1dZ~f=;4GbIlUjeD}{{iN+{{c`-0|XQR000O8^OY7yV9$Fe1PTBEO)&rf
+DgXcgaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWny({Y-D9}b1!9da%E*MaCz+;+iu&)^<7^v%fJYgrP*v
+AgO!1eFNJ}wV`Lp5h2j!pX(Y3yNP!$VcDMQWp1F~8Iix6W(w8og*b;f}w>fi$Ue(oxWZ7!RcQwm0l5e
+(E%?Z70s$$1kroJa+nLp%BUX=;imHDDtvaHBCt7*|pR)WMf-=^6EE0<NBHu<BW!`UOx_|uk6Ci+edA0
+<X=V6r>fFnzDRncq{u-4y=)MO8MOmb@_!+8YL<q#M?3>4LQ&JDwNq<4v{P73_j8cvU~?Uy})^>#SyL_
+P8Z85D}<uwt2zg==XU3kFU3{reEXq)ijz;CX*#wku~GzH#KALc6qU6b$nj14J*00b-CH9FO*Ajt#(Br
+?$0j3R&4rqLg2FCfnP7S4|`f|<VUcli<asqaYUPCG)+OBi9}u$v}p`vEG?a0f%mi=;u88~SzhKm%i@L
+=t3;YG=?po$s!AI}Tmmr71PDkXe)T++1T={JjKDD^o~M!#TEQ5L-_Ug|#dGL9?`mb5P919bs(VE8h6w
+fF2K=O40`6u9KC@M9=FvUh6wz&B8N$J55DFtGdS>h;CvR_Qt&AwdMXbEIhgcQ9ZvbX~i$JUjy4DZf=&
++5CMu-<2;|$0+kjx-du?WH~WTQmCA2nZ(nur{c*fNp$=(@9JP}|B5*Hf-x{0eX^#hwxBI)qatS{Sk&j
+l}orG&FWAk0*)N#EOFUu@-MzS6f!|r`V85S_OSR`nHt8QXcc?v(ot++M<(jpFxyNj#Z4vfoXKWIEFw3
+_=WR2zk{`~*(-t3oJy3?-X5uh(DsH$_60cuVw@57m^VBx*A^&Q<#oe7@K2vUkz>N|S<T3XK9M^{DE#2
+CMw2h_omXU8!SlRH)6~ITRW<n+dm?#B3g8uEs-ytW#M5b*kxN#f%FhAg7UPqU2U_fyVArXXLNBoa1|f
+YYCD3=sB_2XR;&V|Q5m4iBuQmYA{0)?1@%yLSX;@RxN@EcNM7&hDDuZj)qNH@Qg2?ik7lB<YBwlPFF?
+ib6?!7VCmH9CKJTgXHms;S_`CDwU7+Ow*tIeVgewJK%mOF4Qm_!N|YZOa=hlyjpPL;${%`@LUZBDC_n
+@+-0Hj1JT%6hyOLAeqsL`t9|5EXw|RtYF1T3G<cEPq#3g_k!1T3{8dbe~K;`RH~FU9&QPuEK7AhM7E7
+Sg`~hD8Uw3Cx8PlAGNK6z;?w`{&~m1rOc8q0o7Mmp`dxWWRE~7tR&M^K8rP8k2tRQig_#%72juSQL|0
+;z(yI_@Kz|}O((l9#B?_h2%6`Ta+)|6#X4)zJXJhK0tc;*0PC;2z!5KKNQgqMjP_JF3HRenitqJkxf>
+w;z1|CQDdA*#NrI4F@-xi~QmM3qy$V+;XR602h3CgV-KK)h2&G%t5sG)PxboPt5$buTEF-=*TeiqoIa
+|um32@tDgQibt3Y}^OP3M>Gk0I=pCU8Pt<90($t#FRXpv6N=dT6-u_oHEc==VjR5(5bmh%k^OJ+MYbr
+xYv-$O=*Rur;XL{)$0~f{5F_VA5?1b%$!>ij~&tJj7}%!N;^#Zo?*E=YrHN2qKB0n<$-^UUgMf@Ki^l
+DY5L!wY-lMIP)m&*0fMS3`!=YwZ%|T5&B_7uIkw6xQqyp9$-8|fB_O9P$LCfyiN}B*stf9(CubG>0ZC
+tGbA%%(qoz70g}joU`O+@(PeN4ZJZ)1hj;Z(4B^BM1^PHbi^6nzIkbIMDR4YWUux0V)aWWPG*U7;Nb&
+yH6O=PxI(i2wT^sc8EeX7HN8PVdxg!N6{9gJ@_9cda<Mp#@u`?sbajsX@XuG>B^2Oy6s@GEk1?Y0=$*
+O6k9(MHe4`=UBZ~lILei99JhFZCvNtk9X^wb|iAS0LP7xl-`XV)5iwBG<HjttWX^ofwMi$fK6C6o?b(
+1)r;xC^y;`+HH_EVlcvTVL72A-mAuw4DyKhOus+H5{tn*cujZ-x`+x9cvhI5=S^RDaItk$gqc?|M=_q
+)lK%{{rj5;<#tii_4nd>@!^-hiVudfAPD^c|Bcl-E%JY}y4{?2l<Nni(lrLdjPEsGBHPW9&tBu(9oTJ
+)KBNPWI9*5Bo~JAAR+!Zl&<LjZ&Lnc?6c@A|z_WNtx^adV(mhisE7Jx@_VtuqVD>N_K&~Imo<E8@b;(
+gfA~6mVKThBYIqGeH)fL-?7A@z84ALyAY?)5ve9qzIk(E14--~Xpgd8nyFP1uaB6J6ISYKw*{m)VJIT
+HV+1~17=@`}VNz{!u~%`|aHBertXf*y81bDBf@B$naxx~}RtGG##xs}{5r`1kZdZK-j6!z5F^KZF!5m
+vUC+ZVGzh`v76O%|*1|wfIrzpohT|KmTxiEF%2)_{4ID^2VSe@PLJyD>m@f)Y+0U=}xr4$)8c{MCXsN
+(dV$u*GJ_ES%W!_nuO?)wA|-eRPA_yZ{bF$*-*Z?7dyoEoH~`a94Zsc=LWZTG$R?PNw-Dpfb1@t#1PQ
+T(sf<!woM#rBCc)CR=J#9n4|kt-4Ywn=5|Ox$T?#}g@T>;iOY)+Q)}GNkf^xT`ZEQ$fivfy<2oRFb?=
+m=cdOo+x;GGTJ6I}NfiPTZI%bFY)m+c<uepRFGT0J|6R!i@ZLa<x#Tf+q2Om_=P;@NC7Pjdqk@yi8Hk
+heuC<<}!nV)@i@%s|u_o$hPmHTDiVq>2@?Oo`;@8V|QX5>vUs8XZdF6^-u-s>JEis>B(`a5hCIZ~x#S
+0x~2snp-jzP22%H$TzmGD`nh<z-wy*o}0hA_i%pp%rUeY*>QZ-d3!}IoIb;a#3x>e1sB3$v%cMU3hEg
+ybx!;v-S<D^s-!4u$8%x58~;#em(5c!v!(H-q#VI5A53iY}@<W_Un)D{&so#f7HGIfwtT!OMkZ9vgHn
+ljVr|B!i%v-@*B1BqK7|fOb0(4RQS2u%{@32{z3Z=c3FyYc_(XXRWI|B)=&G+6kRv$PX&_!s^F&LUDH
+pSLcY4?wDt$(nqV#(AEFnMIpqAzTDp1@ACW?~O6}mQ7jbM?io7}#nk1|XV$UQ9{qe@cP4Dj=*uHLm!8
+G%`zXN&_r{S>1l6~uO*lqZ|h8h>zuO+Lq9_x-hGt~9tRASBAF{2G2_No4Jk;*NM7S6z&dl|M+*>4`}V
+$iiJdZOvGFq6&}Fn8XH9(-fkX>Pm`rkZ-<4URTy&(PB)D~?Re2pE~RLJ*jjzL5a1=H$i&?a+Eep)+TS
+AP3zQ-HVGOma`$w=?c2x{^}y%r<3nALk6Jy4{9j(OQK$&4(;9!!TXOOf}l?Wyw2bg3$dscv=E!y$;7;
+_`m6k70SGwkuLff8uK=c#{{m1;0|XQR000O8^OY7ylb<E~B@F-o&o%%6F8}}laA|NaUv_0~WN&gWXmo
+9CHEd~OFLZKcWny({Y-D9}b1!9da%E*-Y<O*KE^v9h8*6XdM)JFU#g>5~DwSC$Uk>O+0TSD}7)a6}aj
+#dnK4L|#EXEY6<C1oCz5Mr^*$271TvD=;I|6NJa^5>LJ2PAbLGZpzo1BqVSrOiBw`Eml#SN*~jO1T8d
+Bp09!ELszXtj&TB|OhkM#-90aK}kql7=%vIoUMHdQz?gYC-Glu0_MQ+miEanX@^$&+2vA)PRPJRu#R;
+fd85%w;T{|8A%#mmm9)zwqZp*Dd>g)pEa$?vSRd>ELnZeSV1a)pqz0jRa)K`97H2R1duFKNcTXI7DZV
+D!nR@@Y0^AzB_gOH|105JmSn3efp<Hh#H!5m@*ahxpzCb`3{c&KqQcv%1l$e(c`~_VUuy#9OUexj%u!
+vW449lKgH$E(*BYIb$!-fep(RLB64Y=q`E3n4(PF1ifCs1{NF+&9%~21w$!bE$8LjC%kp4GX6#y{_f?
+zUPRbaC?UNv=7u{b8#2G^3%B`@=)X0iNxPSWfy<5^kENmFD=nX)*~YF5#lPbLcIUee|I?~3U=zT=8hq
+#49ZSOfJeO3H%Q6m6j1x}qvL{knyBh)S;mJBpil*<^XjD(<3Zu)Ykuw3Xa&S&cjaftx(P#Agd~Sv71@
+)0?<rH|*;co>UB^-fY1K!{E>G>Yv}PpU%F8(bHKln@rS_#x<-LzzTxVseBj)l&8Tg0&2h`$tmXqR<^-
++mjEA@%P%acXFpE}Tq#?@>SRS$$8pG6zM6|=pOb9^o|0Uob2M(vHW#VFzzYE*gahWnORN_f6<+FF8$e
+S80DapNb&r^?FtKuCWzP>~;RqEcYgnug;8+~qUtLdHx^%l`MS6;_l?4>7F&@zk7#)HqoJBNE3kbDPy=
+}o}9g7&CPh0-30u_VUD0*T_bW3cA?k%h8U1$>oP7`EPGhTmYRYvpd->eF4HL5$QH{P$Zg61MW7^cdKR
+moZXCd(o2;Dz?$=)!yv8ftOP>s)fu9OpnQg?|?(hL+iiV6t0NPV2hrKm%~pz<^V*eK=)t7Z$H4<PF4<
+$e54^IqLybcadD?FnE$G4}(B*!GNz`MSwzsL|Fk!gSE~s&-#1~y+RjJx_+Fckb}L5`f?FvNW@K*do<N
+oTBk9uahZY_Kb-w?dindub8^IA2IQzm5UmzlVQRDI>>xgY#fbz*JP`kT32%UF_#h=)ndT2dYmNFaG{d
+A`Ji|TybHq_G&}T}f<S7ZIE0Z6|^VwXRW7a2wDL_X)req~Sn<jb)_iL;#Of&?BO5iRl8y?QEP(a7R`1
+G%j!*ZC^0lfEf>jk@st|MNU=`q5J3~FPt96}3f0<je*tjh$r09e@g-Ys{K1icwTq`M4C8ByDbg2x4d{
+fFW9J;D<_DY}C#A1aktxYmY#fyavlc|MkOz^ncU@IS+|q{X`3#?{^zmXPR_@Q3HQW38_z;bD1gyFsDl
+`EFhy)=@7tBzrWLS-{wgY@@yS;rJMn&GGTeVar9ZO}Xat?jSYI@?ZwVY--4p<$X;bwsL~V%v9K2x)H~
+cwOf3yYX%do;2T;eYhB^B^=8ynijjZ_4fFQwps9>Tc!A}Gf{bpeve|~u`tBlD&jItqJ7k_!_LVHUIy#
+b%7{4m`^&WG-fb~{8nPYEBWIXlTmsQh5Sf>ADlYfA1Cqzpf6mmV`JdzzM(Xcq?0?t&4)nfU48UM_dzG
+6tef!>OD1)q6|m=`tv9nBkNgnT4XBWS!fux$+NI;~Uoyb!7Sv;U!P*fEiPi0f^>&8)?d@WVD977wq7I
+<3CYW>^zoL%zU*h^YEOc`F%aJ6?L(gdd|;=Ly<{LxZSVf7jda^0)|xK9;3nM{>YDx^S6_Qfh%E;&iB<
+GMIS0-pub0PSQ4`v8_VExm!3IL3CfEn__Q66exNp{6B6=THj+^-)?If?$%C}=|R4(GxWyk9vPm`I|6z
+=Rd*ZwQ3TPKGAqLBuI*P#>4{FK3jIXe#e!Y5F2Y1v&DEk5q9?9PDCi^=95+cBM;hI|z3byU)(3Tt9(e
+e-Uk>T3>%#`ndRW8B<@@xGoh#_3$O0L%Z`TnS@&4!%5$nSvuB$^c$mceF*A~zZs9`MBW>6-l=t)cAZe
+^8%*HpW9F6yjX(Tz;ihB+FmZN*^dH$SNIK;pPaL<P<jgxkW1IWu|guRspo_OJWIY;}lFBOz_S2tPE1T
+lsnN-SV(Q%GBehfpsy%|B>0+qZX#&$!HpWOb&iP`n8g;9JS&c^R;X#LNP9nj3;{Vs+!1Cfw5@Ev-%}Q
+r~B?wrz54Ae&0?SRcQB~>qgbfEZb})Ia)*Wt$QM4#@e4^^DRx-+spUwl&PVp)HC<R8wxRJByrVS^?NP
+ISLcW!KS+h*twBfMbe`|dQN>*M;NfcL&uYZpQ$o$$d(S)1mCO68E!Dy_OKNAs@|djka*-ewe}40nc-A
+1i+l_i~$T4#04`eEg?_0JrYfWLXu_nZx3<>PI0sk18n5y<5`16Q=Qx!VkT|K{^JGFIEvMOP%^#s(jYt
+P6JukjMz+md+b`)OOY?rvnZzCzm9KwMaD;#phZh&F7^-`Ut>CAD57IKC3*A#3pc?LTb$$J_l;Hka|H^
+SR0#ksj&njXSw3w=wOp--tf#9M0PXI<mMC>2O|jG7K2Tnk^U!MtsroI3Y+Yden~HU`~0oj`ott5tV$V
+Q}-pk@qeA@IM0oHWr4mnH-<JIJ=9h^+^Kgg9O_DU>+{|RA(gh^fH>|F3<~8gpMR|!a&t1;?(cUAi-67
+2LbDX_Xv`g<JVt>01Bh7gD26Gm>0CX2&0ud~3#>1EDGwkA9PV^YE1JOHuyq2|{<u0o43F^rFVC`Q?aS
+)&8^UyRl0Fll@zvk^bCOW|VK!5&#to*R+(aaiygrFXHU39JIhjL_2@upDY^@~w!px-TkdmTW{(dH0MX
+PF9K@f9l=yRvu5-?6Z?by17eIlXVDqDl=YWt1*d92qP__mQ=Icl5}z|&2&Yq>sE1I*S>hLKXItEi2K7
+a$IFRW6!%7`F$YtZ}#iQ5{TgI^>+B7&;cBR(;^yMMn--S)Xfh%z*u#Y9X8^((owVg;_^XMy4BkagFcN
+rjUB+T_Ls0*cR-%0Ko`cy3mV%b#mSEGgTcP2WS@lfj|!mo+4D9@vecMc{%mE^FdCcM|Vuvek*RxB>nD
+8I*#&;y1ZqDVK23xi^o?1{uOj)Gmb|o0R6-OGLA?oApIn`wnh{<A3z4Sj{2xvgg0S;=g)el4GG(9>z!
+`mgSmSs&YkD=!)4e3W%f-!NIUd9;Jt+%XlBy*Vc}8oU`30vIK~$vs@hq)Rnyd3<jc^I%6mv|%xHR4>-
+dtL!>j^gODlNeRxsY|)=p!)O{B--J<W;(=!5Y?`348ERu;j@mwzAKlV`4upI!S#yPm!Dpf#oG7uay{9
+E8s9OSUVEw0EqPk9jt4U>^>RO-S|uGkbRlO=I<J`w8{)=Yb#yPAf)hCZuX@D2}nIuN8PbA+MP{zN?Pq
+-qRf(QU=AJll!u|g-ST90iW?=T7!gaqsG<UVd2uCM#Yc`1FqW0X_+{{tYi?*NLL7gPsE|QHAVZe0(Im
+pjSOO!c%&K&SUl-lFgYo2xFm<QLw%enyh@xymI{{*N|2eG@)IIwXOj2*I!o5-6f~@6J5N=otqYXB!v(
+oNt7B-Uo0<XUJ#t|JM7{4Ait3Nfmk=CIj3Z)%SP*SeH%7KY2mdU@Hqjf1z8bfcG@O!;zrKEVaVk&XP0
+7*V_zA9XF!z3JmRUNWL|K8I-Qy7+lef|j92VN~EOoMIi!wCV6G+i2!}<0!n30zYaw6vK#*1k%l`n=zZ
+%z`?iz~V7bdr<>?D|kB0c6eum$Ym!p8nO8P}S|*6urs{F(Faqke^V6Ihp=?a&q$gFVn%Z5k8$q&SbW}
+Fx4N|1%XElD2)J^9`R`~NNVJ3qCsLB429Rrz>S|?fZ--@RZNfE8FY#hKVzUI%~mTW_A<c)GL*YRjIFY
+nAuiuue2zcA{pHtpXYq&gzn_1KUw!;|{_0aK<MG|^KN(sI2Ks0|4uRCCZb{H6IG&JKV&fKKsZG7z)Ql
+3m`z1##tkez4okGBA1walwqTZecwSO#Nk-Q3UqYJKQmUzYvCX_Nu0g<@z&AD;>9!#7$9cqi?loPV1n)
+!j<L;XS(RZyT3p(W<u@RvRA(#i+f&APUKr!ZQex9zNkK&D~PJ`U8d5J~{r<YGMM3{9~vZ;3!+?;m!OA
+DPs7I#aTKpqZfN^t|QZ9x)PjlJG6A<!@=fO44zm4=u=f(ofja$)N?fZ<F9}c(}zUcA7wQToJm1Z7-(z
+H3^>&`9k+Y2wmx#R2&yFdr?2QVUJHZchCk<i>hin9dgj47YSBU4;@Tk#Xb2iP)h>@6aWAK2mtey7Dt~
+peKm##004#-001li003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?VUukY>bYEXCaCyyGTW{
+Mo6n@vQI8_l;>TF@yV=xye(rnnn+G6#(4yj=<5^b}gMUAAAdPV>H&LMS|>^j@a1SFQpbLV$1bQp#&Ay
+rcH)C!e@+Q43ArQ`-SN`tUmGb{2f*qv78b_bc*Y`EsRg*%aF1{ngYIiJCn=MAm8<GBHmA(=>-#0BJ6h
+C`uIc*--lJ>UxWl;Ur$1Ftm+f~A5~b7na(Aj$B+<Q><}90oam0DCAn8S3>OREeu75|v~s5c@*%J=xc1
+SGS+gMTpQGiNs6X`or&_DXGfu$ySxeXcn`r6oX=(8LkDBXy!B6b94lCu{(0KQo#%uCGk^lORqZ50?lo
+yb4Qg|aYPSBQA&a^41-{!)gIRCO^Hl=y#_QuX%uvef=kQS{`(BWe=iK$IfF76sml0TI@!_$!Fr9pA@2
+fi!Y#M0i{T8yqR=9@^p>eTeQ+|JUeF}769&IBw;@08WwjYV`-DX)CGoC;pt8;!Ol5hJ)D~kXjb#|PdZ
+V;LntC_m8&*oYcJc#<9B<+kOA!tW`0_Of0;GbT0}&A}@z`h3oD8GmTD(>{E<S&L_kh7S@ujmEBl<HZ0
+CY|{mCSfd-!m;#X&j79jU#vvb_chmkajL|Fl9mZ22Nb99Oe$QIoo^q5q^o);ZgIyN}&;1h%m!ZhQY83
+EADxmz&kIFXWf9(bI)XnMlj7UgW;Y(DO9>dMR+C&eogiw7x)RU-uyZU#hAT5uz`qb88|^6{JD>rJpz#
+RrN^vDyNT%h?&~+eKNre)z>nm$5BEGIVJ^iTipdec8<uj2?sD~zQzAboedmtkgrI9LXz8jz+WAK52ZZ
+9pIT0sg?`W5tdsA4k$HdOM&b6##MxQLi%HU`BUPsW;_CtbH(N(FbTFL{kqL2a)+Z99JM7V{J%H;vU^0
+KPHM_g}I_^RB+AWyalG{z_0kSKjbTsLruD@C;0Kz`A@CQ?0MCdS^+O-qt1i)RRfT)u4db!m|<stZsJ&
+-k)unoF+*gcl~cFd<w(v|iKNdOd68#66C6RUOyshzR?e7)z9fl`5(F+6~dHdNc#mZ)!Cw<V3EdEh7yZ
+mtD6Oiv}5eXM7E;t6#U$o^3#v={!puxKG*9RQR)vXgJQZz(f0#31jsrV~{<<0G0R45poA~i$PoIl0Jn
+GA3j{d>+AQ|zd`Dn%nrwwnR}-ULM0h0aR|muCjra<?L-dxj_yek>`&Jei(egG)HkWqk%j@QgbadB;-V
+Jaz6D|{O>qv&@$YKKG)u8gF5S=&`Pre$oPy^Wy3)`XngBe)U9Bcs=bvM0!)dDor{$ZFzJ;q^);1a2il
+)SUS75R3EDaKPWKM#W57(kfQN}C_S8)eEW^l%+dn9Cxf`i~PgD>9jOBfjGwBTx(d{QEhM)KmXBY`L4*
+w^VCsq5egOaHTl9tVDOi+qP_N1V3^C3^t3*=@_7&QL8UN>$pTw5=;RwNu|;!1rT>oIqSp`{U5_&N6M6
+Y)ejG?+bgN_W@|0;z+%9wSS0&N&#&d0$#Xs{e5Ej$)cS;lV-!*ggc7`aWzp#CZ+p0RALIvH{vs&fFC_
+ML1avPGnTp<@sDBOlR6eLqHQ}RRGl(6vo2#d%Z{t8#Ps-jL>@tP7Nfc!FU$+4FN6@DSBRQ9L2+MW1-9
+=N@WbG^8BT!W7Oyr2BI9Ob2So}dHalWcI>WIu-}WUEUnf4knDiSBiiw)I%|I%=^EumQe%_`N_9`PMEx
+{Yq-b8UciI7Ip^kH==9s?a0`6KXY1dh*~p5w@Agf<eU0X7MoHcpShsu$Jpj)+F!xiE{5`0+73f}rY<2
+0(fWhRe-7mF|WysuA_z(Ug~oqx4Xm6BGxdOT(RKW8O3PuGF4d|A29vHc#er3^>f^Q`yt+j`1;FuT<*W
+$%#67y4DrttrY1m*%Z};-J!Gm)tjs3pKq_p{E#q1|6YW2h9_=l_0#wC42HflkKt&Ydkhy|53mFfS0_%
+LMZ!O}+;}Q8rWpQZVE?~iZC#s;!LjNvkpiv16G?E6Mc?!*KBwuOux!Gv<LTHL>AsQuM~mK>i9ejW`|6
+l69IsVBG4vM4tmA_mn4KzdD&4Vr4qLpsYmw2fSxicBDynNj*tKIALCpjv#CxjK*IFwbg}>+ZZ#OhbTK
+7|(?=9BfXdIir`-C6!YUcGpii3XuP)h>@6aWAK2mtey7Dvs)G_L{%003qi001cf003}la4%nWWo~3|a
+xZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?VVqtS-E^vA6SW9mkHxRz>uOK#;RjUf^Rj7Nh6(DFM2aVD+FoFfW
+OWBN9T!BkDaRUGM&hWM5zT_x5<WOF;BxgQ;GaQbF+{$`K@_c(x2g&lB@ZG+aiqPw(t`3Ujravbozvm6
+F#he@jFY1!z6<18ss!6s8vFH0me$PZ%%SFRKIUL14sa!qm*_<>=a&e<OO_Jm}ttwdWj9jG`m*;O@uGX
+7$PVjs4?$ruEH>-d3(CV+1n?1jL=|17-#pWG;y;=XczI?k*=Lu<Bx{-{%rPUpiD6_7`->f*uhTpSb=6
+zz=f7(@x5`Hb~HQ1LVjuvplfrabm>&uH(Vu|)9anML0e>aWaX4G2aCT!QtS@YbS?OHu;uUFGOGh^+Cs
+r2_}w)5<d)S9+f6t!p+6{>OO-PgQaRcyy#4EAMD3wC}>B`sh>o21i6*mUus<|6CN%#tK2*_Nm}$8F94
+>$;-iF6Y}^)PkLD<blnWdRNjkeJkmH&m^I;K8TV)kSu(300se?_e?uq=sw%+)q{;1P}eYgOTdao%Pjq
+N!)UbdG7JPF8HBbV76BWpnqe7)O^gI!LcD9o6%0-DTxYGSRcn$ys-5y$nRgd&w7c?JYO<NJaMVvBi{J
+#!NG+WwoOc`@E#@YTTn8gQjmWI1Xw#7PXk_*E2UaLc3wh@`7hL6e*05>|{s7Uy6*#!SF^CuZNkTn{7O
+NLJkMf2AMfGC0H^Iotm6Ww?RT*~edb{SP@A)q&8jvnY`kF~jEB={j2t4v7RTO4G?=e8cpxV$I3o7W2>
+8LiJb3&Dpu-Uh?IltvqDVaEX4mmEg6#Rr?>QzS^9Y9CE2G3zQ_~}^}xPvjS-qVDPnq{Z*nh`3>=uo)R
+HOc1*TKBX%bk&Abwm>xN><HMa)d?47Zx#?tyC$2>5)bjfSycw1u(ErML4X)A=2Da~!0hO}%OUk^cQnj
+C_2q4ZX<m<EHVVQg&NGIpJ}V}+(|;frL+a4+k9wrAis`J4T5zOP$D;QC5dCpOw4bi*j;6h+YGc;_b(m
+F#1-~L*eZZEyP+6AsRQ$<^&}IWjadXnviw0!Sf2Vpf%;2INJqbVJ5wPmxp|H{hO$EYJlGo|c@Z~Y^t|
+{cSZ*jWs(7<jK7`9uhwkBg2%K}Tv%(}@PFUtz61{_{g2)FN7nohc6TV-XhmK75QjQm3I+d$3Uhp8A9h
+H`Ll6yvpm(gAfJyH=P*M(E2U^d-N!_51_M%ZRfXoMt^p*eFJSMS8_k9iWwi=qTzP{!e}P0O_5HbF)xD
+rOk#~Hy~Bkg(<LoMwpZR;kx3*i}DPQJWzOOAd&cPQA-K6ZFoP442bJ<o0-P{)4B?7ai`?_#}pk+r|A0
+=p1LsBKQVm_r2$7T5N$2!i#t+*c+0ylZhaj{+mSe@E3O`b9h?lSV<pE_s>OA}x-kBa4JaMNov1$w#|i
+G-o8h}(P|LkBI$By2f+fk%t+Kyn4ZlWY?PqZ9x?u`${1;47UNr%que(YC%ySOuN!O*{g0cpez2*=f;^
+tdmUQwuVwPh!xj9o0ju?`zjt-qN1`H$cN7D+*EAZpclRWQqFuREB|UIY+vwPPN<T4non_3L{-di7h7^
+RWeY2GG#|@<}8B$y`5`oShN-_O$FBx8Q!^(&A5c!C8!^UpO2dzE1VQutZb!9q{2c&h`2AfPIaYg4UZ@
+(LQ_iAqMX@{Q96+d@XOi+Jk^)P!(B+FI~fQ`$qaFf+;FTD&k%Xn2-3a>$@W;K1RAS{l~2sUEAm{loK{
+i1&n=}oD4u$EVRKtg=ii$Cjsrh#MAKHdu$&ub|TgQU6>&U20Ywf!zhud7XWNqpNNCR5@hGmyfREJg40
+i!T{eNlXsW{&XU<mZ2_(KEsk(Mza&_&LBpdkTXeM&)J7lC_S)G~CFHg!&u0^My95}ecF2d(>|1Jyr5%
+_d`y*LQ`llV}(mW<vF&Wg552=_Or9gglVIx9dq8cu!gf%*w1j3?6mO3uKbs)_9T4^T@31QY-O00;o{l
+@>>xuL<M%0RRAv1poji0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7cVTR6WpZ;bWN&RQaCwzf+iu%1
+41M=k=+OoQf53n|1dcldTkRrs+6}`XC^p>+l`oLwph2;JpQI>q9JlGF2Vcm;!y}SXExHB~QV(hnKnP+
+@-wB22PIi1yK#u<yRcy~B>spM4mX%!vgfj(#av21*;dpI$T<NzX$TY-x;**W+sMN-w<Wx#j2!Kb*UqF
+P`5ocYyfpQR%?O}9AUjn381vPpCWoTERDC07ez-x?JZE+a0SZyt_c@&2p&|+b=#H&2n-lkbWil28W$?
+{b?2AL&5SD-y=G++b6j*B+nKKo(ES-Gy<uRKTv&AJLa74Vq?)}qi#DAFH=&5VzdlGU<UlKV6%)_G<f(
+G^W58jsWuln-z<?`h}h_tb8DzGteGkZ6J4{!GkuC#=gT=S-^5@)`@<>6wu63mjw^U5GsqYCenC-CCYP
+jE>MaqgjZSZ5+19vZp0%H|sgESNkiRq4AM`cO~VSwnx9U1Z&#c8eQ@LmvA+M9z?*Ss%w0%LvDgyrO(D
+@)aZL-B;t?LJLEH!$ur}Xshp|xKXp3EZ$IetWRe{0zlO;Vu|G#=gY0cLo8?#Ue1Dijf5L1Cp6u+hF%#
+L_>|2&UW_YSBIKLiln2^fW?fu)q@mYe}V<!|DysCSqPkH<FIY&E!CnR@NKx;3n&pCRJ4gLa9O9KQH00
+0080P~d=M+5NmDCPtJ0PYb004V?f0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1!9da%E*Ma
+CzNWQE%He5PsLMVAU6?Q-!A8Q&tb?5~nDzBt_<78;s0gq_a(g76p=WV+{T8yCWr8wxz@=FkqWrER%QN
+efRN>q&?Sa#b&d4Wh)J{8565gY0LPXQL?fy^WPJei?uL96$z^fk*OSJQdrPjnjo-xa~d#w<^+~0DIv3
+HYZ|m`or8w_Tx2$YhYU)~KDn8()=GQd7NiAI-YZA1J|MCi0;8v6%SMb)f}n=c5CkqnDav%Vh9XxwHR7
+QOW8uNh?51qY(q>|I#|_kDX=R0MQ(URMl5oK@tMsP+KxO!1reOgOB~s9kW@=Rm2~qf0H2w4I?W_1}l)
+j3?I0%9q=4=6WJkbC@RYD^9Xe?m`g{2`((SPBjZ0ytQ%p?Aw^}g+yTDezDaMvxZXQGgsJd9JMA2{bsa
+$^{o{Kn-yGIK(tSE{&ytkOoTVLKUb*B}hTk2qH_y98EpZADf|u6KxQC^eXdYl5c2soF^(X#uTbe62*z
+DkF*o`nu@krU5G$OD-(1I8Dg7VYWgL!@dZ+bOh#Dnd%GrwQ!j#ECx9Z+L4o#P0$o!HBK<_EVlD+{k$l
+0cwlPoaBCx^Ad&7Gh^4TG65jEBm$DDcSf1UJCoeKESSF3T*&2RNhI7dmETmEl6a3^thS(AeHTMAuENO
+0$iYL)9rt}#q$Xhg$4N=_84-8NZ(9Xq;A|zoqx$Gs*qLaA0n(cdCu2+b84g_E#19G0YD4hzR2N~ktQD
+EknazqA+Uz_-pv6t)<c8^#rExQ9pK`UjO3faMN9WQY+x`$0tt7{T3)W`)sFvrktS`;kW)pCzx^Y}9Ij
+h;@%zfTfMlUu7J7n;!G9##hW@!WEZh<32ANug}C<i=rpQIOdpjC+{XDD;sc9a1QYdjN6THrbwEpZ$6{
+zM9N#uCFJQb~|u!^Wh`C^l4D%UpIK;lI#To*$p=uvGDy1w4MC%dHiE|1SlCMH2^M3(koq)IAt7#WpyV
+-_6fJ8q%&$Z#=?q5pJLd*{|$2WRL`z2UHV(9`%(JV9rsG0Qv~s&hC)mn+H)n@n>>JciMtGKgNDGW739
+?N*dM&0V^I`bZ`!?S-*?4rw!~`L9zLlXdL$fb<xB=@n6JQ;JR{n!)tqg*(5fm;^sH`ZkPw76vR)(Gwz
+E5c4%EedF=)^^-+MUBabKfo30ren(T2^WFxG285aKSy3(N}n59|cPG9v6@KzbgRu_Zr^Is)Pbn{)+@-
+p27QhIO=iafapXL?Cq&r@$j<yL~zSd0ZF!&Uz0wrwf%Z35An2rn97Nlukd^XT^ok#-1(lYV%D;MNgTz
+`akwK=$Gwjkc4?Hr?>v?VB0<ZfT{I9sQi9v2wk&tf<Ag)4W!{2^(2khv~0E8KnK(?DKAUBBDcmz@R+_
+qvEFou^fvo&A$Ns3xN=}hzX&`Gip9UEj(Qo_Ogz%4UX9*`{;w>?(;nTbrF$hWaxcJ(QQa8dR><VpU&v
+_5-a7?r^Xco8+nBv(Cyz$z7*7e}J9cvG!qVG6Q@WoWCf%9;Kb4EO*e3EZ3;TQ}2a-Ip9KAiN(ezKL%A
+V`OGZLwP)jc~8@0a*%NVif`_hYgiiuY3tIuKH2T+)pzX#Od<bbmY0m8|FghiLErggE#cP)h>@6aWAK2
+mtey7Dp`@u-Cx?008C)001xm003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?dWo~n5X>)XP
+WnpbDaCwzgO>f&U487-9aNS|fU<BK>Kn_`w!+<62Rs-u$6ozWksbE<$EZJQ#?7vU+N37KLn(AUpA|Kz
+QNGT~)24R?#rYtZFfoHiY46p~SL}@T==03!{;+iYzLn(QrVhja0STLbyvxG1XDD@*>M5@3J<oljwIJt
+WhtbPPOZ1)=<Zg=0p{c5+pxn8ZeK5VYm!LMuk6KrpT{{w{kn;N73HY@ARDp)m(?eFzhAJ$j9-F6?eY!
+@9!_`o!FixysL!=%yOcBOc{6_}x2^FdlYI%g4D(X&V}tsy87e=?C`;nh%!eqnw&13O|&Y=S9SVd!ZTN
+xM$tJ$xyX1Pd^a2v|`R&<dUqA|@fH09@9ai4sCRad`kG@#voySaDTqSi;_2PgK#skvqj)7)z^<OM@r9
+JJ%qUsWDECIxwSBlsy>DwAZ=&O|1FQmBuqi5jOxQ`wsWoedc6kqvQuefe&aJ`CKy5*u6X2ou%Lm_T)4
+|-X(rK$<~>GTG_TN@V6^I<ubMn!wmBwSG_)mHkrV3*%a()WWx?VFk=e-K&}LHvQIT=*R9z?sE7%g4-D
+!eZLN;;6DwQk6&42Lu#ffU?;vxj4;3Y@H5w+#im~@X-8B(<*1T@|iBcE@z4OwZpATs-X?|}BRRa_hdZ
+a=pu<2YaXbbhS_?<j0ozA|ycr=-~xCdsBs7|((?_LAvGzez7c@EgYbHvfFKcwl8s+ky6;#6&5BhvTo(
+>=Uf(pC2sl+O8c?QCo0jh#Dw2#s|u-&R9m&XYgy&jp3k8CKHeSlv@UoKg6-*?8r*M_=di)uVMpp^Qf)
+P4RD^n^QywEm9eU^oZyb2EBf-US{?n8;~<Dut9|Op`BzItH?a(=zW}|X?b{4ulL}Ao+#4kXZwXHdJtU
+_FU!zjUdmLeC+QK%`RpH1O9KQH000080P~d=M>8JS9VrF?0B9Eg04)Fj0B~t=FJE?LZe(wAFKBdaY&C
+3YVlQ-ZWo36^Y-?q5b1!UoZER(9a%E*MaCzle-EZ4A5P$byLAdBDVTw@iY3P={<O3+MrbXwWDFQ>FCE
+DgneSoCW1jGLK-I4krCEDqhr(u3bWZm8Gj`vOPWL=Uh+qJrtEXxQln_6l@x2hJcW|{q-kb>WH#p@~|Z
+N>AtV42{WNh;K0hY$^K((Im9MJ-dsA9@_k9(1N38@8});-M5hjcLi4eVwNGyluHCm{h5u3=njmNw#MX
+O^1>8lx#~?51bBf`GJDgQY;pWg6#;a6rN0J8L9Zqg20FGh$38(o4R854Spm87gXk@dB+6s!I>+iX_d1
+`CF5!0nV9Ni)ErTz1w)TT%U18seq1JG3D)pjFJrJV7gQ<ov95JgZ~tJq?y%AJEaMf|Sr#cKb_qDl(Tl
+HACRoWTZQOMZbf20Zz@L&;K)TxuBsdk*X;J6A+SMtqAc_?gm+Q+BdxntGrh(W-mvxTExAu7KCqGUM9M
+uxeoK>`B@w1_tb}M+E@#6VV>QQM{W~~%)9FL&{TaAa)JtQXvYot{WTcF|F#qjbgc#atoN;Hz)^SV`0j
+Cof9mY;hL%v>aZc^oAeHWZ0ZDXSo{L6M(6nC-wb6vRlNT=WDdR{@8k>-u#4!X5*4;@WoyESnLcHw1a+
+l2dhpRh_Ci?`kNlK4YJ%Et`zsj)Y)JCS*vdgmftZ*)=J@L8uGAdG=Wl3>^*se8PYa)Zy7@8T10n@cgV
+Go%XPQrZGyZ&EU>!N%z=sOuxcE$35DMPo3s=r5^5+#7vd0WK4Fow2y>W1KK8d{C|65%R4z+Z_HCEv+M
+XXbXh`x{ZxK<+&&vf0t5VYd-Q=Ss<m|qawOyx9`;onsQ1nb?x^de3~4NVe?FZ|q9Z1NkHaQd=Lbyy($
+gj!?I~!&;BzxBW?}5O977G?lUblR)Lpe#eFn{Qe{@IjfLxsbjLm{QSUA^X$n`UU&J^{WF*zeE|Ga&*b
+sTpn^gUT^PPphu3;gzu3r?9d+brAGwK&>AkvRC%_hduCG2hyy)eyXZWlqRqSEyex|FPhynP8$a3Ux)l
+hSlj46<?s<%{rOF0Y0+4m5Sf9k$Aid;O^xpbIJxOu<LaT!*WtJ-Ogqjsljl<?=PMfM{uRX5MEFAp-vq
+s4IRX9Ogk=MI|LQ|PMyAm6-hCDZ3vN{7dH^HONNE4`fA0!!}E=EEIVv<+X&zx<ONw{7xE@3ek>d0Znv
+&@M2H}Rl^>}vBcdfXGTg7xh-C~91EwO7wPt}QFjkNb4ZLovg1l5q@)99J6=xl|zPeo}Zg1-w&R|^ZEK
+k$;Ste&#PIPpMclrL}_jgw}>*PtOZl?*O4PfifAFgZ&QoGTZM`x`u-vfI^Sm|Us#)N|Ja0l(v-Liq}_
+tR7l_|oXciRWuIuK|IapWEwUdQq3C^#tI>`*+4|ZhS0`8GTPFxT>IsKAyk_?6>RBN8s;PZ_ZX5%wD|G
+HRQXWT!qs%4mp0{=IRNF+m@^#x+Yr&u7sp4ctzzS>8^C1TuF6)fczh?!l`)`&1|Ck*kAPF%!buB1V5M
+*s-Za+Av(nJrF&VojeF||AH_qM?hgIO;)aa$s~FM;XYikYd9QK*vzdEOMa!_lu?bobl;PVH#~&CQhmD
+&kH#@<U&tiLw=GC0@XcWT0$2~us`7UyN9<88Zx!-??;|-`Y*32-%5pj?)Al)7mM)AAP)vs5!ik!4?W<
+ZKRvfP&92B;ARFnrH8x)U>MP46eakk{C6r}fxPjB75HzQ9#0JTSWUj$P)+;P6}gf!^c&c(^Pq7gMOwG
+S@0V!Qq9i%olfJIWUKNc;EeS0G(QPJAP!pd+pzSZ$S1pyA=NS=O@@|be?xUndj;Ya(jFGhJ0ltrxk{{
+RnQ5^(rlhKMs~y)PoY*!=_Sc)@PKS{m^@6*3*!Ye)SMz#FIn>aK@)}*09y>U+-nMPG49k}%!AFhJI#<
+e;i{X&bxJ;7uG60#JqbJx`g$hms0;mmGtEj9&M}Meu7&tP`7Rqjm@Oa6;{TA0jz1?8+s0u>h1k;k3ue
+!<s%1&Vk~Ff;nF1&5XPzH^#j|&%c=0z-O9KQH000080P~d=M|C+zP`m;F0R9L704o3h0B~t=FJE?LZe
+(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1!gpY;0t2Z7y(mwN>A1+b|G*_g^9MWP>s6wa|wwHEW<vN1AO9T
+`_8W?nGoMNOIaR_P<Xj+p(Oe%Q~1I5_G5g?z^9^l&ncnRE=pA6a``RRw_g2la{<OP_*}ylx)W|lOiRJ
+U~5@I!I=R?xsIX=F<xt)m9oyZ4d?d12fANPm#ZSb$#3(;Dkb^iYPfulh&{8r+x9yDw76SNKi=fUx9j;
+TzyCa)<tdq6PnXl#Dqr4rj<O!Qc%mA5VYZe+8!C+M)jP?`oI?#(7DdsTQ?1EL0equ;11fgr*&s+R9Gw
+y>n-7ewC}It~>g0IXvye7RjWCRpOR|sx4!W&^9g~edmRC{{*jW;U34(Z;u`UNphg%KiOcNoE!yr_YU6
+PJZJfdQ4+<WV)$9eehh+_X)a&ZFXISv?{zCV`)KWHO2AcmmvrsLjQ+U6uc3D2GZFI#{xg{oCPOw4`@l
+SguSNhW!{HTxBPo`f+2)V<J0CSEBW%c1@i`G>rZ`TRv{5HpaOP*#8kIQ9KAhESAgAYUnOAXiGNcrxeQ
+vZ<jYS9iBvlWN<Yw7ziDW6|CCkQZit$j127J4vF$xO4$vFkM|!BH-1pF4oBk?cN9_6}4Ey(Xtq`7qmu
+GM9A=GVecmo^p>uNSH0lNaz1)VjZs+JpTABDgDR{GXWGOL1tsjsZbF@HV6WpOP#9;`d3^>m?M)U|5Th
+~)n&=0o91%^OCgXBVIfj_n-fl%ZEZp(da9Oc@{ats;@3iHho0a(BJAHF#0tc<$W#Gbnp9|Z=*h4s5_}
+|Ty6Wg@Db8!II@YX=NgxF7qGUJ4_QMS~(W^bXqhV=&GzqM;$W(@soslAXul!1WWilqArj}bkzwXD%)e
+2B;`k|3HT8K3)>Cuf(Ut@*LDKd(fO$#Jp0I2y4L@<qhRG>Lu#P)h>@6aWAK2mtey7DuU7-Kd5D007hg
+001Qb003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%p8RUtei%X>?y-E^v8WjWG_wFbqZaoWcjFH~<m@5)5n
+%>``bO$x_-xwu8v+OOnEXCqI2Z|G;ShYeUh(Xf4o^6Dzo<q*xT~;0ZjvNGPonl$GY>(MD2a7sZ&+ejI
+ZB$`2+4CvdNAGp6(DhsyP6;4{PXPFw!xUe5g(-!o=~TMw_yiHDk<h`}g0aBtY94Nyx11QY-O00;o{l@
+>?zVuh1^0RRA>0{{Rb0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BFJEF|b7d}Yd4-ZOPs1<}g?ImoQ
+&|!bDwTnuQU?ZD5Qw2$RatI&X)GK&@;T7{d+a!&Nt+69w!e4x-r0pU6>wfOuCd5D$f`Ehf!L8zjYA&y
+8SLe^Bx!U8jh4CDBUjR)6^a-uLXF%gg}L(x^+n!AT}ypnjO9uY!P-hpy2EtIz^~bC_Pir!MeblH2qBV
+%vEV#S{+_kq9t-%w6D5SI$e^}Z$m8n6XzVJ56#!TPH8t8Kowt0-{;p-w=|B<$=~)?;MLz=#gf0onf$$
+DKPp&i?9_LR>&?YcGaOJC&sKZ!@O;p(&+aSK3j@G<^<D8tZj((&oljtVhrss^MARbsA03Lx+=0V(0+_
+Ry4AiBKcP_iA{MAI=gaK5$;vdcKH{-N>gQW<?fa{u@n%sV=qoOeXnD3uHfD}0k=k%2<pyM}vMTqlf|o
+8^PtPPe*h*Rq3@Li)e=hNl;6jqRJ=%FF%FuWp4_UF21KlZ#39l`l*sohoZZn{Ma-#V=4x0|XQR000O8
+^OY7yBvqZMrU3u|ngjp<BLDyZaA|NaUv_0~WN&gWXmo9CHEd~OFLZKgWiMo5baH8BE^v9BQp=9pFbur
+=D~OzA!0-nwu!r<<=w(lPD+)oe=|rt886us{{{4zp5<BrCMavT<&Wtz|yBK=Vx*K_ns5RJr2$8|;D7c
+YP=W_+EJzBECS1@|pgch~4jL|qMicU0I!_ne}|0=m5__mcSBL+Lv`iQ;_u_k-EKp9W0`F+5%P_1`{Rz
+lk^UnixaXq+LM?$!CSd@3MCi(PVJy=AQxq3bGX%ABhb&A~*1XCP!%UT?b);Saup^%K5MIjIFp!gk;TL
+mGi6HoifXP}SV~9b6(Bn{2$M;`bOsRGY1H@(w{i62lIG(!~6pHsuXh+L1=3p9EhEPcCZHwo0<HxB|p;
+t;Iv~m?y6`oBDUKeu7fDGF`#ZxDl`3)1US$^Rw>HMERA(CsMJPdJ{aGG@I+>6L6stF|C)sOnKH@N;vd
+q_L`V#YG##7nrz`GylMC^fq(Ra!yY|jo4?_Pyhj$tkGsCJKl7(W$;2UIXHU;B!Q&%=yjvN(Xyjl>OGU
+p~KImM&5q_xoDSP#X3x1Ep|Ezw;`-`r8NB_U#nS6QS15ir?1QY-O00;o{l@>=!Pj1c~0ssK-1^@sb00
+01RX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BFK~G-aCyB|%Wm5+5WMRv7Cup^Ahg#2I;2H=?8&zvAZTe7F
+G`aFNhQwTcd3U($xiw}QJyT$?r>%~gj;7%AZ6Q!-l3F0r_Q<n>fo*J1IqEcfQFvQlQjkOhHBfO)D+Mu
+?Xxygbkvpd8BJqd>FGT41Uv^BUOUXjvIBXn!;Fq!Qezt?yp#v!amM$K5R`}e&tKxtEX!)GydU<(w3**
+!z^lQQZIGcLrSPa*maKxB(731tW7(P3f}I#2l&Mjii$!A&t#!cK&Cpx^z*jHwgd#4dSTyXp{Ht@;iQT
+={5G&!-d$xq&gCq0gcX@VcjDkuTNue6JH^|UmfoId$i1hGHVXf*T@&R1D+ug!COD04v@v#7nW+2NaDd
+Ui8Yl7kdTn5EQB_<tm6inQVgIJWO6ihsIUpK}>m#)>xF)?rUMewc0Ckzaq-dDWzJ}~~c^beyP8#{+Bo
+vZKG;5r&30OTQJz&V(9syONR9mBKS)+Q9Qf*_WX<o+)QlpH%3{0oJnSl{R-X4)2Tq?n2A-LA?8I<AV9
+$!khf%$|#ha!C|wk9U8`mUx@9%ii<U@I+Q6ff%N8b8-<`C|{1GF&Xv)J*I=3B&>;IU8$NztlIKb4Z(*
+BK5d_6@}@@S_uthZqyS1raAhK<?x_5rLPpJUWL~B4b^y~H-yGH6_4pr>^=}gOM@eEY^t*xi#{WEcbpy
+YOO!gB{O9KQH000080P~d=NA-WC^;il30Lmi(03ZMW0B~t=FJE?LZe(wAFKlmPYi4O|WiMY}X>MtBUt
+cb8dCgkukJ~m9|E|A+aIj}P=T*|82OJjNpy{On7HET9nxG%jK%gah%~%p0l3K6-`_2p>qWoItvIkTMc
+30AHUYy5|ql`wQtXAb}A#0JZx9pO=t+`z&-CWAH5^DiJTFZEKCymluhI9-Um^{ngi7%bh!mzhnyHqXv
+KK~c%+FBUHwyI-%y{@<L@Q1vTi$(S;tdi|5lZI8Q6$}<|wif!*F16qln{xxNWNR$msfB;L)~ZB~^csY
+IF#J|rW!Z}tFGvw~&5-kJP;4?bsu8H-cuMcB-d<&So-@3VIOip)JeksCabJqHWiM+~V2f--)h}3U38w
+cvXKV}#)<ng)Ta%AdMx+P_+S6Q0-v0UL*s{6s+ya-{h`oik$`aK$3xAPm?k(9QP2t#pRqu5tqHA(3YQ
+YU!dzQP7@O;fdFCy4TyQKF~%ZJoB$?`0vp0FE>F{4&|%dB$rb|Xu{7FspTE(J5Z5zK`V=ygQZ0|DR#y
+kDyBcF9`BO4YSiXq2~t4V^KgD3oExV80-?QFznUR<0ortZak{Y))JH3k8T&t%1-*Wq#3GX-GE+Yr)Ez
+Lxe-tF$LymA5KV3w0NO^j;!HzO>!~04y?p$5aGb#W$Og_)B`3V&3s(5rK&4K&ZUq`ne|Z)obZM%+#M=
+|yXUO{Mn<)9=?C)O2^*YyAGX3VBIy`0Y{L!goejA(Sn@{M-^lEg*e{=Vme$R;WM|oFjkV7C<e7I!Hi8
+P7QKS{cqO+YAMZsjVRvOCU+^D*<qHy<F=AWCbiGQLj8}3wm?xcXR@c{%=_`B0}E$88G!*vUUap|IKOR
+H3Evg`@_L+O<Z_P4*h{Ws%n1va&pbaN-PK__A{jq+Hdst$^MCKqJ=k{fQVo<xEf8+lh0?%8O1MLvxb`
+S3kt@7}BT^x&wB+`R`;x55@Jr0>2EN0JAEft3E;8+esvMF9Z>`*O`bjw!;h%*K=nH}Ap4s<U`INQm()
+jhS&D-$<E+E#5*vg++AZqHC9fY*(S6A>D7QO6v5%@BjT(TG2n*lm9cM2NJqa9e>I)mn)>lBvupeG>lR
+F{qyN!lDpCqJ0sq>;;6ZVmBc(g^*t@hO6k`iVj!6wcpanPC-n2Qj)Lj|*V15YQl3Gmh-PgKR(oHrV<C
+FTgeZH@41D-iCirNXM3%iF?@(67HcQdEepEs<p^vB%8p=nB@TN0>U@B;5Esz76k&2KF=K497colYSi5
+i`4&mi0nS@e5fwukkPdB}sigPTA=@d4Eb(!O2!J|7Qh?ivu5_Q56`Ye(m}-3j}4#go!;b}E?Tx!}><*
+}0I2|F&$1HE)uzJe&=@4Ju>cG`te1xd%@0vJ1WYoH&YdchyhS_!=Wzt^QUcT(Dta;VA|&dqf@&n&UKh
+40A%0hz9Hg(>fgE%vzLkAxmGVkN`kyU<tJeaE)xGEsBW|^<oB6>~iL-fT*q!%Fn1`K_$9=12%i7rJ4o
+16EKhZ48Z((bctkw&eMakP}3Y01t$1w_yV~Xv`g23xQH$XsD{5M_+!fc$woxA!*QvM^;pB<HbfZV;bg
+iaURI7j(7@lxYq^L<^sd6o9)e09MJ4O;!c&RE=JU^@w0JP?kJ&#E&J$eM5(-<TJjl8eOyY5UhY><A0|
+5n45ekZiBTfZca63fGv=a0KYaCVsa7NEYGsJJpiJcBLBqs!SjV>?lN9=-49Gicz=l2VL^{d?Z_BfAzH
+vlgAhhhKxlgV&{r&(O`1QJw!=>(&Nn76QR^%gKX#O(m!`_49_13=RmoA7{;!&6{uIrtkFQIK6;x*oED
+G=>1(04}LM^HQ|o(2;OJwG6UY)Uvc}f<=O25gZ$i9y8yopvvQeOL`Amx^E1=nUNx{i6OTK7^T+0PAfP
+CY_znYfrkU0xR++QkmR%|B4I(oLe-u@y{HAPnk#?4j>->I9yp{0aPH1nP<@=sLbJoY>DCa`uDM}a471
+EJEq0>evy$z^hr|FppS(sF=ECIogF<zX^5=8ex%v?PHWpAq{$~c1pRp!Rr#qF(7jG;+9#F>GAI2fB^q
+*1XUmgbJcs*q|i@<^=HjRWXT0shEMjU5wl$93Slej~mskaoAVWzVbiTw#a*yH_zJ)hy~Lk9$Eepub0Z
+?{{pA2PAR>UXn9xJPP_$;JJe2AP~nqfb~c0W!o7S1|agjVD|Zh<;4fAFol@1F)FMrV*9oRzPY{!-!H7
+2_ac`M;qymL~wD_tZUIgQ39YglHmp$hw0|j_(#)z=fi9ecmBv;zdH;#@wypkoQz*)yV&hfmF_2NC)9o
+~V9x-^xq_)lxm4NzgEM8po5CNHPJ7gLx@Cx|+@@oiP=h~w&U!=@=MYIN(($x{4Kxe@xwPMmeK2Ct)u<
+~^)FWWxPgxNeKdQDxft1>V(j&_L2l}+Qr%dO!u-B$r<ACh2B2)?c>2K7}?FhadNWV`I@l&`hA5Dj3DB
+sJ|Vtv3^uiWUz3v<OTG~$p8{i1JPML~`$3it~<P*KeGy5?C;rSBbCMhY$JVkv5<j+3$mq|_)#?5AAc!
+rxDyt~QnNT87zVj(<!>H-18iN7oDEM~&$>o$##*a8<bihoLRV{D6|Lz$K!j6a61f3K(Z$Sq<HzzW(0r
+A>w37Qwq-Jl7^$Jt%G*bjv>2RsIN3(-YhYtpgb&b3heL*_z7x*FxGfx99ju<3}bayV(+31rGYCB7B&)
+k3E&mRjXTWF2~0P5NT}#zfwX~9X*&md_(~Yc8AvN~Al0LdLPU<5;K*6QKjY94Z3Qv$!&96FfZc5=Cb<
+6N^H1mporK8~WQ3ZdCFCc5r)2148Xy-ejMT^kpaAju?&?zzRg0GRPucY~`_2!NEyKO;CE?x2Q8?U;J^
+>&4)RP1&#LHwDDK4aNCW^yOI)WbxB}kOl;5GWn7#t2z{SHnPZk98Ly@xQX5M4JAta2`EX}7*By>h3Z&
+S1Q;R&0Q(!u`H+(Kic2`*b)PKOqr|5S)1pBr#IMygW^2T9yZnDS7IOB#Tm#Jk<bBSqH_Aj^*tk-_m^f
+Z{gg*r2B|>8>IOP_YUZf_;)~)541=<JRtij7rTl5-@?bkj|Y$BWb*}75f6BINR$s1N&P$|{3=I#eCMZ
+7`v($G{}q6G&B4)t@?^PWqUCcubmLMt{i+-V`rYq@ck)Yp+ac}<HSLlP_*d?jk1L}I;e9~z6GdM^cs;
+#h9`w7uLXaXHqyt3u|4SiS`ja<n{OpeFXS!>jHCyb?<KD*S$#?}F;Rq&qo==BE6C4i?b`q({;t5)tQ2
+!J^73Lew*T%iiQYrWxw>H@d>H<TNeqI;Df4DpT_jGT`0lbA*oQ|fKk2chBPniL+pC{~dAc;M?+F|SsO
+(UCpmf=Ur@PSepjAAIm;dA8iLXQ}rsB3qsjLCRS&juAf>&IRv>Kd_Y4zPn~!+MrwVH=YFHj!n20Z>Z=
+1QY-O00;o{l@>=r5E(Zg0{{S73IG5k0001RX>c!Jc4cm4Z*nheZ)0m_X>4ULY-w(5Y;R+0W@&6?E^v9
+3R?BYNFc7@!D;6q(NT{nOIR>zc7HEI~c^n#`M}f9hHW5m6NUDzizDrV)t(TqfVu|JsXJ=<g<w_Qirg>
+#5g=q@B*h*!9EwvPt!PLJ8K|ODHfz7kjL6F<<q=||i<m&Yo(>MeBbjP^BYz9x#*w^~;ac^JziWh>Dm6
+-+59Y2LamhF9190V)Dw1x*>zB*ijZMTYVU2qtNb_UpLg|yU2V35-~s5CFvz>F;T3bsmaQJFo+90Z|7H
+*pfU_{CxY!UD{@+gxf{par^&Ke%oNW@Fi7Hos$n2sZruMj7Tr@~z?uBaI@TIZa7~`I9W0Z0s;<f7{W8
+sFyAzsY?D<VOp|+ln!^xFiuuNYK-DG0N+=*HE{p@TPdl!K#FmVM-_^~-2GbBRz%$(T<8!kpubrh9N2l
+%Bt!GWHI&kT%>r1NwewPWocjI1u*_0BS3|h}j}qYdS>y#=brWkLzqYgvjCUJrhZj|0H|Pt^?Hz8NJA(
+~Vne&HLgqx(Zeoz-z8=W{m4a?(ZtYcE^j=0O$7wP%WNRd1>>oL!Z%MyH<5`agxUn1=A@*hGbtEIqi!u
+86J0Y+(gX%d+zYQxNcVocMiTQ!KK8Z_Z^xEb=R<=Y??nXh@tgja1VDr!MHO%=PZb<G?Ik^hUA3Ma7EQ
+aT&1Wiq-#A)Qm@;4h>;o1*tjMAXhY-ZFB%+ZS{+@{9p%zmf?X*NqdfgQTy@QGdjfF-O}Ed_GX6ZRq+y
+jl%i?c8nW)0NMl5pE}`5sNo2`W;yk-I~6_|?lRsovzbnHx_Zs<33hIUD7?_NFw9Sa_y6blon`2Xtp(y
+XI&&a>zrM1k-BohkYxB|k`pvB?y1FoP{q0)hWHZ!_&|WDD1=4Nk40d#h*bXM9E=8_WFH#hb-3RS0-6(
+4VTUEzYLQ%t$zwJK>zH@E#(Bu=_`7eQHJ5hfkEJ<Jsy4*v76FYyTp?YhMj^I|bgCafIyt<=#jpC%Na7
+ff?6HrmV1lBlX*T_jBr-IHL?C#_};=3fzy*ef%emR|V?~`O&<Nr`g0|XQR000O8^OY7y-nujra037U8
+VmpcB>(^baA|NaUv_0~WN&gWY;R+0W@&6?FKugNX>x3DV{2w<Y-KKRd8Jn0Yuhjoe$QWVIE30Pc<pN;
+fo-%fShqp@Fc`xqvAk%prBssF=D**aWGiuOr-bPxl5_Xn?@ubUsvygX+SZz98Hj49v<0>@s;n)~{CN(
+!_$!Q1ax!Totf+XqSEk*Web{oXrJ76%GLW{o@fWq;eq@Hvfp+&ylsuoqS7qtgY#;V?@DfjGM%8vcf$s
+WHYR>Y#*GeXnb;*o@hixvj14i6nE4g*rQ54Y^DDXd6?O}}wR><GLFgnfGR_T3;LQXj61*SzvVY4hYye
+#IhV|FtKyCK6oUp(m=qp;q;#aAU;JB_WdUoMwWlHtXPe^*9ToV>Y8E)hQR;hvQk-k{Ys`1EWPA%FwO6
+b@MumXtu4PL2S+mu$@ughMUGbIrZ9WEHANtB$aQO~Z+>Qm>FXSqxH{lsroRC?TDPDMg<VUa#ikE1H>f
+W@aXW8F-Y9GL~#y0Mc0w<o^+NB2Tb8=}aBP*3UJju_?t!?=+t=IqeSfFO>z7119tEUFpV8e+~#-<{5o
+!Al(1)p~9kg6L|^Ox1G2H<h$V3WCW1Fy2>w1P&n7=&A~-%+(Ds=y~fueH}D>Au9JhMmkl)<jb2yT>iu
+o6PS{%&&Cp3-CEdVY*L-K;+vA<qO7}sK#?UL|wG<_BlnNCmKW~2Z<S+*)nmX2l#lq8HdUyBZ^W#$=mD
+aQ+F*oo$PY0zCx1ACGDvUMp=@=!$@Ixku_OKsD<u%7^K#uuwu4$Sc!^UYlW!$SHs9vA-JYvG|<C`Fkn
+vH81qHn9(xr#0v)yPwAfpg@z<3e>m;aHhuRI%UYr)vt2NbHnKl7$U3j#tUC@jB7+nd#hvG4h}z>rT-x
+5A4m*T*Xbv`7Wj^^o7Rbp28<pm$|&M{sQJ9TvrFW<v`&ljm^l68g9lzSNIo192M@DdZ9t}m%-V|AM{b
+}xa^JC9HN(1l%(rYnPVmm{`k2GJg<3)&U>Ce8c%F|8XSi%Oy<iIpUkh1Exd@@nv!Cu=TLok*}z{$7r~
+r=b+zWo<~g>)K_gH{93z&|U@c1_w|!g3GP)Y#;*hgLLQb;glRFsJAWp_AhlJcP0u`TDfM{&!`Z$}%Bb
+zJixvfJRDEWEEyVal(Xr57|SE9y1(gv@(rUd66RM7Z+qB&fce^5&U1QY-O00;o{l@>>&%^(s+2mk<07
+ytkx0001RX>c!Jc4cm4Z*nheZ)0m_X>4ULaA{<0Z)0m_X>4UKaCxOz?{4F`5&v%VAqWSH*gZ$drq|+{
+HbJv#(gw&Si^T0+(H1f-Q8BTS=p(5(F8X2Lp<mkv=xg**_DMQ3B=yIZlU=M3ATq@n&iv*-_t?SD4_Km
+7Sw5by%4P@O;mTmJ$Cfh9Rq`Q|xrmnQ!5%DKsO4JA$AxA8`!72_I{NO@$A7`UU$b|jWLL5*#hU$gF3N
+9~Yr9b8@8C6~)kvlhug-|cbRi9czf23kjLPhaYjMKXs$vCSgDlJSgcTrB2LE}P9xBaJX{?s>$_i$Ye5
+LdUU{#f=(69olLKlXqjDEbQ)7gXP&8`$|<W|DkjZ8#o#DqNwZE)e3DGi)*4jdRaY+227X%-@7^YtstB
+A~lf3|2KS?V5?_r545jDM&1fWiCY;u?HcD58}vft}iZcA1<T!4_61rEJMwN<uW(XU@#mG*^LLB&BcO0
+NmV&Uq7bD8zE6^~Kb7*Cy}$lFF$6jW50DN9nN|gh<E*lk7IBP(N?XR~M&$rp?0ydheqEXHX4dtq(7II
+Nhn0o!Y@@(!gc82vh~Rmy1y9$wAkqnmeg&%F`>nEJ8D77qJrk0&_2A+Zj!=~y4+cLQBsn(*H6u>Is6K
+0a60~dpYQq9%+rS0jM2ackjy~+bm$<D|y5PB6D0$J?ncLuDvn98SnM3xR==V*{8T_*Zi<QXBK+wQSE=
+xhKC!Xyje1}iO09h}!O29D~uowZKaq!cyZWkQvl>Agl4f0qowAbP6?9AzK+QDYus6wDX`{OU!MxKH%s
+mQ=tKu#OSBO~%`!cgrA9FJxd>FE{E(ahGF==4^V!4UXReqbR%7?!GXA6%(gI@j?`m|6-=!7_nRXFOl=
+wL!m6M2i!hgjS?T4y0T^M)juP3Q@1jT~Wt1RVp>w7%)*L!mW{CA?gUFk5T-FVh(3)kQfML>j?1o_^$_
+V|6cE><*JlFRU-Cu_09%p)F=kLh)WAZ^yohb>+%db7{;1yJ8jg78nj+`*8<|atXaoMi!VsklC}{-nmD
+JR?2}?DjAb%w4u@b#Dy<>F@-<tCJoWAvo@r|%eh$QSoM3XJTur;gGh}eH!J|i=0J_`XuSd)a(gZ^6b5
+$V;eV}`_LiDv{x+fV7o6V-W60=$0vO$4yTyUE#1bJ*tURK2%iioia!&sIbJfgu1T9cZMea2Y!5Q;}((
+QC%+)Vo#fNG&{k;_v~nBU}-fbGLCb!QgWNjIC2=hXp5C)HIg{cJUGm1s1ui=1`-Zs0y$ym4zBsC$+Bj
+4UrQTf+oDdX-JCF69{1)4kG~k@0x=s(-_x03;I54!Wj+t3g<I+bkgz4n&mgOSdGN11tNp)(Iv8a-Cdx
+~T0-5$;IyhQG&`q0Ct_*Y-NPjz3&{{xwpoHgupUvJMz5fk=<@Q%>xb#~ZhZEdP)*Rv*z{ynrz6I`=WD
+Q%$%R-#J)27#_40THMvpo`8L;aYWrNig@|zF2Tf0vf4gCu=UD87QGpmA+4PRun{S1AQ1+d?kA$#+M<U
+7pRegqVy&rz_`NR5VXs5|Cvk-3!c<?_8MAsLP=KYArZ4&NSO{2fJaw>j<Bu`!<O3P(fdb!akjMB#AXC
+1*cpmoVFvH5X8_`|7~G`_z~2@VOU-dfH5RAe-JG>n6Ugh&~DS7Ku~|2j@cNTTNX{&UUWC?VGiri-AM6
+?yrNlAYtt(eTa6Lw+rYl{;p{c{Bz&ULx1d2`<fME$5EPqjj4je2`}~}b8v(YBuk$K-{DuPHlEjK+Y!{
+2wvHj!On@O_k6xjIXq*lgP-+1Oyx5?;u2#F(>o#Qme@q*japoR2*OF_ID4jN~gBwpaMjEFwP+@@j_JK
+B6h0rqTGQyS`hLR4$noG@?U2n8*2`_#9Mr9}ff|J8=gHA3R^l&F<Pdt|->m&#@n_(WA&02B?IB{|~r8
+MJ4q;~D}Kw<yA6lDju3ES6xjPcd-`i`n)iyB5qx1p0g_MrDcNPNXWO|Jg<z>cF|fCDE~65+2Nsu8clC
+$0~5RkoS5UR?uBk`@*8n*fXf$Jm_gWD01>OhQ{I%MSVZ!^QRWfhZG&hDt>uVJw|MIm&aja#JY`t71jN
+WwPL!Csycamo)FSdepq|vm~0WrH)xpPllBqrok2N$Z~Mxc`iCkKyCU68o=oZLi`&hBN0v9lIJ5IVTcx
+B1FLz59-1R)B9}aYSqzdW!Tk@RA=V~%4FSrE01%MMJj}>}-m6Ov$J11zVPICrHM8AM*Q5l|Y49BCk#T
+7>j_}fG*=h&3x-{pBva}<lXf}HP`ZLcfvEgDJH*0xpl;p~Y(3txmfNukr7J7TZ9a-TA+*@n=S~ds+WX
+J&R+lEgc{|ywMKK`e^bJ!?TH5W`LBqJtHA0zHaa&o$2X<u~ogf#|ioaOw{0Hz}tU+&_I`^)nw`zNCxm
+p@+Ig!dor?{07YGH&P!hyx%yQos84+d&J~I7{6u-jfcR11AlWahuwaW@(2wgOs+v+V__F>fEq5_{x;<
+73qLoDP8jr&m1qsCYY=ft<a!&q3O@{)9LXqC$;>Cv@1d<twWe1+}~l*T}<mZbHeOOfmHHI(A8{^$UB3
+Jrq<j#XoctS4H{sa{;kBm0VFV^j;SK@zE&J=#g#9w{bhJ(fyM1T0pweRt$PU~Kwsdkt`L(VwhVa1)oC
+i&va*I%Av-k}Pr}e$p0Uv9($&{WguLnNs7r65u+ll9Wo)G(IHM=hI(ZE6cA#`IXxGVIQ{|3n^7iiXcK
+Slb+iuONZ5;d`P)h>@6aWAK2mtey7Dwk&dG-$l005*F001Tc003}la4%nWWo~3|axZLeV{2w<Y-KNVa
+cpUHWo&O_Yi4O|WiD`e?N~`~+%^=x>sJsm3`?!5+RZKK*yxs1;-XD}pePD~Bfaquro;(Jc|8BVkBfHA
+=J?P<2^i6kZ~gebM=~oZN|I%(TGtY?jIeSeq$YH!1g|w@?sq~8_QVtuRS<Xy%}Qv#g$e>KcW(oNqpny
+k3JBmihm9upw1nTJ6mqai7DQ<o1S{h{ZM?o0`Qy)2K|=0@Hb0=SpC5MSM`G7Mp<hvBL_cu}wAeYZAjm
+mYihTUzJ=0+3-Z2hQV=#8k!(X8k6*{IGjOceuTq_7iDONqCVPK^+C|xWu!h2DHn<`+1AhC++EQ=KIRY
+EpYuM?uzCI~1NpJa_;YZu(YXnLR4Zy2`Ux3{;}A=)1Wd!<APEBv~O&ywy%@s4s#cGhnLMS3ugHigcJ5
+lc-Bnem2!_$`cv3nK*-`cgwy;qH%BdP{N8YO&QQ!_@BsQ+6XdG_ABRLMS)J`Cu(unKYVBY(&!XQJL>3
+9taDS=3FR<P;0VMSDaOkk+U^iEr2(UW<r?HWZ|RgCGFBsUpR`t86udAs)Af2=L}h#L^B!9I_M&7KXRi
+xw3g<zR6_P}XsO$3rSJ?n(mx7wtP3QIrc2y=v)L*fh;}24_O9SxS3plx=4&dWH$TM5A&q_o^>p^C#9^
+0_?>!A_zi$93kGWIcYemi2VB_dCkwC-c34Ed~nI4r<(>66cGd);wQ>laphV>_-wycu*Dv7&}L-Q{+li
+-QEr?>cNQ(XQF2~!DeNkeE=wDUr`T>S4Y<*LZcoHt?Dd+8xDk6FGY*F(%&li#*s1$zumxxJ;#q;s9#^
+me7E^y6aeuF*KKi(aI$@jbb@?jmxGI7F3Jbh|b+Y`XoZA+0Pl)&Nr(x2IARznxU;g*niOTTcmE+7T^W
+kH<2Zh`R_nKC(&y78<L((q1~Y1GyBn>`VHz+RLTB?i1xToaW^&A$M_@91|TK9LeOsh~tTgy(FB$_m?#
+wn23)8F#EE_VwKp!)<8tXcMj{d;04Izc%x%<NxmWWrgAdk+clue;URc;Dl|Q{WCjZ<whGPpNLQe-HF6
+$J1u=IHRb=F2<mk<`7#(2uVgp!;AMrwAm~F^I9<WHYNdg!C#9Px7f>%omz!bDO;P8M1JUGsP?dza3<0
+SaZIH{+bF>NHxxFCPk8wBSwTw%Bc0*8ex5H~C>Y@)L|67ZH?4Ljwmga3=T9fKlXEcREi+Xw@YZLab~+
+tG}{<OTUacS|sPpw^=1g*#0sSu)eftB3I(962)m35%3$&dF&`Jrk4DwTtUskWygQCLZ&GnNO24By{iX
+6ovky(KB`CFB>PZl$i3c6}ZA<^HPQ6@<cUT8s^Dy9@Pe|X-!qT%@$M74ZMTlB@Zf{9>QVpY>dK7Wwwa
+ANq;P3I{iRLweT4@Pl$M=JI8*`$pgdoc(5Yuwkr;>i45kwOP)k-;<L<>rgvR9=|15yYTwFDHdTAU7-c
+eRJfmgiU%-3qy9an<pZk~j5-Z5qS?&n-h6w+;RPnJ|n05yjN48yy%x7vt={1ex_p_noOHQHl``y7lcl
+g3$|36`r@TdS@p0n@v{XEXezyH)fntgYR>*enH>Y%qHd87F6>wktXR0Bg-+^-G5Nypu5rhV*4^SBtwm
+_H-mmBWCC|0O66h!fj>&m_!g7yl7fQ&3yrme{%<Epo31+Y8#p*VoV9mhmr8O9KQH000080P~d=M^{rt
+Y&-)105u8#04D$d0B~t=FJE?LZe(wAFKlmPYi4O|WiNAiZER_7Yiw_0Yi4O|WiD`eomN|K(=Zf%&#yR
+S+LnkCHXbJx1QH0OjUjCk4}c;kJz1@~ac4Vp`S<wPxim?;YFL%(IOp=6?_8Wh$u*>DQJG3%ngU;Mq%y
+!VEk$K8_0K`joEyHzb}w}h6r`TCTG6%4SD%>18PM(v;{q2m_%01SHrtz;9=yc8H76@G3t+fD3WaP@d#
+yMKa>2BQyLv5nxpJnh<<47t<@Ccaq!VCyj#k=8U{F{&s5CDhKzr@zU7T3Sz^N>-u)*_^n>39yied&EW
+|lKBO9Ft4`Ms)aG{)ncf0t$Jjn)b1YJ(~o0gDrFGfI*q?u1*k$+HeSU-7P&jo=3CFJt-YRriAQld9y8
+6{aOyBg}1160(;#jc6<o|GNCs2hQ@xiYshj91QWR++a}&QA1UhNYpY_Lm^emXwnNCRqv|WWtGbbjv0j
+8ER2&}NR81>r1+HM#=y7R&q_%(GNl;TxKmhBSh#=6bZ*%*2&X!P(+MxVgC<BzRJ-FEN@;+i239V*a|(
+~lG|!y{EEg%AH74Bqod|ew)be|{xEj=ZXYzFiE9eBaOR8Y1!lBZMJ*%DFCUi#W$fV#oP*R8#Zjyn{AU
+o}{71MUcG)?YASjJOoq<9G1=j!%ul&c=$HQ{jZxGcdZD*?D+b%wCT^S=lSS!Dv>2-oNzCN?ac4gycvI
+wc|7$Wc7?)%)}1Ks@_EfC5S2hmtv3?|U^B+TmADUZK!<$knY`qS{hD+6@r0NuYZ*kJLv~PPRtcqSy~$
+WNK1UlL))kX(nzIHXGP7ZfHExxby=deAd)-g+6|o2(JHLsO}O>Ln}V0csLX7@&OL6jVL_TL^AV-;A`y
+V_>LlUiPHjc6Ez8WdwFpv(-aC^B$uOJ+8f}wgLIWs5xXY(iRTL8K}uKe4{};=O;vZ}UZFsH{kSgQX(5
+Y~!1*@Qsb^{2W1imb9&f27#g5i}G@%%nf2NB&5B~F@^&pz>hRo7PcCs4Qc)*#RWL4P@vYoNjanJ`iV!
+@NuU7uVZv9lY=|Cn+e|GQ_vN>i2Y6`ju~H(HM*vuY~`Wlv(C$8bdzhEH4@4QFTW))=JZFrWVbP)h>@6
+aWAK2mtey7DtF|o!UnN001!r001HY003}la4%nWWo~3|axZXUV{2h&X>MmPUteKjZ*_EEUoLQYeN@|S
+(?Ae?_g9QuRRYO4Z7DAxBtlw*MU|jBrK%9JHul(AvEE(w64!svtS^uV3BSa%bLPxBv*Vn>O6lJ4X7Aw
+Y;_@19lw_iU-1)k8(OGl`cf3Stu>vnEG~o6KOU+99nKB7{Lu0v;aFt%bivWnHRQ&QCG4#qq%X*N?fwx
+E$+(OL-0zQ|h9dHSyYPH}@mIxhp`=QN5k`l$!M4@($5t<QRQ&b=0fQ^Fp&gr*{Mb~vH8-%9HGz&3;?B
+Xt4-LCW7S0r|TKS+Vrg27*&8?w3UfoalGvK=WDtOI3$H3li?RM6TP?zn6cu&Q<)Ge|5Ix6bgLcPE3Im
+66|L0F8=Ch?hBJc?=(yd6p-{@tAEtZyvVrxV*n#uD9834x4*eZPqv0HruQz`UK1M6MV_mHwhq(iQ3?^
+HbFm$<Y9zZg?Z(OCjx3U3bGnYUh|TC$;Pt=p-~4klI(y+({dXw(vrN2SOjmmV{SP4$5a~CMzv5BwRhf
+NQH1`=IAA-g1o<q+I3eo?9%vKLhD%2UvJ9kAR1{1Is(cUMBcLZP3dfxwvN#EW^=->cKZoA%`Smca+#E
+9R_R554=E0V6F{h`?pTlzA`Jv!P&@r_oB{A*HPu9>x;^;_0$gGq>@Qw?*f3(9;zxLEv!dQbglng2ZJS
+?K6O>F3<{4S*xU+7-5#ZiOk$Vn)<e3QN&PG>{m@Z_5e9VTxfR}HxjcF3h%4aI-#2E>0p*1&Z%i_zlA-
+@&<!;T-<TiGBl6O9KQH000080P~d=M_AS8w)_DA0Ez<u03iSX0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rF
+UukY>bYEXCaCv2tO>dh(5QgvkiV?mf62)oMQ==YS+f`8`ECr`gR8?q!0gnW`bU#e|?>n&AbyP0wJ3IT
+#JF~lhwdzlrnuCS=yN{n>qoimn$gHbt8!UnaJW`3$U<FQAsKFi(R=p_sH}6F76SW~F;Xb~Dn=v7rx5C
+>8CY+RmPMkn03(g=T$Usdk0=|{lTObLg>iU+1ED;8>hpEgAi5c-aBUE7pXA92nS=HBhAiUv&wf*Piau
+|kKOh#j+n`P^h%<?f=Z_{l1fz3|o&$2~hK;s`rn%gW+AbR#vih>=sVgRK<G#Yu&s?qD9$&zd$FsinL(
+8!dEjMY>)`)*KYXWZ{~0*^{a2v->-SqNWOS&~J}cuDr(ch7rxSv@_i(tWbcVD|*;UAjs3$u4E}4OZzZ
+{7BMG1ju9JGWgc((LbA{afDcn^U4t48K{*vWO^*Arjq-TjS~$*qmHO0w*x)uj?8$GhV4~MX{jSC>=gO
+0sW_;$>LAZ+XPw48AL}b^K@>){+-L4}#H}A`#759-l9mt1a)bt+5a+oll(YW`Hv#a+VP_9Y=Xu2I^88
+M{#7<D_m-EefVO-Y<ee%MRZO8bGbDDho=-+Fe2e(1MWm+b|7x*>hg_m<-G>Qw+%L(F_7mSx@w_$YV%q
+IQz#io~6{qTX`!Cz2I0|XQR000O8^OY7yXGgu8umS)8<pcl#AOHXWaA|NaUv_0~WN&gWaA9L>VP|P>X
+D?r4Z*6d4bS`jtbyVMPn=lZ5=dU;=4^1aVx^xdyraok8r=eBJRMJ(|G$98LV$ImpHlb+R|Gu-KQL-O?
+0sB6G_ubtGV_2%X)4beSm|eVi3)f0AkwapeqOid@7{e{kP#VmkkvVFxJA_5eGI|V=5Wb=|TuGQk7cg;
+v(GVJ)zbA%HHBhk*q_WT$Bnobz-~s`kGpsFe37M*D!I{huT5fkf%|H?n#p6Jsww4i^5x%Bfaf}1j3*K
+2<&!^M2Z6oG|MoO1c(SywNHeTMWlAG5g*2C|mKx07TQ^Pftx$S_}q$OipQYcspN&_o3Qp_r+wbk5mS%
+zR#VOyq=SaNQx=G(@eH0nhe)q4z}Rxt^qMFMd$f)9%%PD0{%h&P|s_ZxUv+}$l!oA@Sy^&KqNtLu0Zu
+UEAD2#eJte2!PwAt1Gh(%^HgoqiI@T?;XH{Ynr|1Qe<lWNOTK!858$mJKTrO0`EVsT`<LSKPQk8j_b2
+i{KTv%=(kRii(0ls|wPzXl$b~O<jJaEwHUog6d5BeMqJ6xuXrC)?8Y;ATviA1j7x}nIH(hJkH?Cy+`}
+rPbrs$`gZXhuC8E4;miMrF9VWW2=)Wt&B<p^jJOM+EAs$oxz@`vwOx(IkysrDC=C8Hx@BY&e-x6c91H
+Krv_i`=!Hk)>w-8==+<8wi8jbvOsgYT7q9*|l@BPf+a6dY^F@}x%90vOtca>Y`Y#J;L$#;M5`%aWJIG
+!VOej+PYR4(E3rmDJu>yX4xTuCx8?ol|t{+LT+nY7&V5-qconi6G{np=aSuXdnwX<23)CjOM)vz_*2=
+!ILZYiTO6P18vxObC)y^rLWJe(L*1t~i3brIBnTJ>{fFAArG8+)XKZp+4jf;mo2l6VqMlPtHP^oQLr9
+*Lm<8P)h>@6aWAK2mtey7Duj<Iki;+000pO001Na003}la4%nWWo~3|axZXUV{2h&X>MmPUvqSFbz^j
+Oa%FQaaCzNRO>dh(5WVv&M)t*)2u`D(67@iBM@5aWWSmAhp#=um3h$EL#l-)<v)FF@p%kTu9?AulnfL
+b1+qXlQ%6hAKQ5v|p{`47UQm`t6$TWFwoS`#>2cDwPm_Z{lRA5R3zh)`D_7xX?pwe6lxbd!GWE%$i(q
+Mc?3|ra2nr%Ty1C2(a;2LsXA>eC@wE-?5mFv3VOr!`cH)XfYp2Q=H=RJi?45MyF{WY!f<33<J!KE?v?
+PSunt;af{o>ax8+BNCPLol6((fo?UcI}U%LajmJOT!h7nQVd8q$OnuDXdrvQUNOzQq4%KwN>13QMjOG
+Zd#_0STe4S;z?spj5;Wzd5;^&DkfmyM-W5<`07VN<PyhIu==)mT)~sST>9ZEm`AW!!gLYNf>p2xX>|{
+N_zd5JaOMJ%O|%WZ*2?N9k=$B{ne{6|JQ0w~ogiIf%5$F5T%u@Lfl$Z|Dna8wjcU!cJxERRGGeLtnj2
+=iKNmxJPOjt{;y7<iqcD!`{!$rWiIx@38SmCEjlSWQ)`ePeVF(~nOX@gIS~1cdh@1;<wj-JR#MF#$9S
+7*iFeiG2wTk1B#ww=*>>q`+DS3E+4vrxu)8)GmTO3)^U448Etjr$fg0${lqVHLy@dSb&hTuydgzOU9*
+xNAbIw)$??!|HER~(;+7QG7W#rqIt(d)xXH77F_y&}#L0*iOgD8zzdx52~dI?mw>zPzPn{5wGB{eKap
+tACxOb3ginbdLBRa&&Py`h#=<`BReK5_FysO43Da#?CKLO9KQH000080P~d=M~s4+wG|lv0IzKT03!e
+Z0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rZaAjj@W@%+|b1ras?LBLA+eVV#^($a%t4u01BKz{?bfz4|arR
+V~lT(&At}R*74#5$L2m}}a6vbH1Z@+%b06hbMq7-Lyn+>ayh~S{7r>9@j-P73(US^AR9#3aw@NEC-7s
+1zAs**`?S}rdy%bnew-Qe3e(rKY5!7`obJSb;6I9jL({$qX_2H)$vh_f_!7VZaq9MCgA^#=b15Z2i;n
+5%V=W@WG}G=PYU;4)5hpl>3*D1$f+qHMlMVwFZZSjFXxrZGT50P%MNA{&<qMk^S<fNz&_T%Z`?Sy?U)
+KL32RT7`-b4YPdudBTH=&%Zr>`Re5K)e}ILhW{;1bWsGk{%INKu*~r~Pz%5%Qe(g{QL7-!12xSx{8na
+&>ne}SIGqlIBD*YCD%SvI5*KA2kC&xGD2o}aR}KJDsWj*vod(CJz2K{()8o@2z<783_P<~M?QQVx=*^
+p>lefpOPJ`EPf|svPzCM0?{Q3mGeG?p={5|-e<CCw4fd(?cH2P+dBmRJ7j3m?(q}Qp|4g!}MBUCJO6k
+o;>tSg-^)l>)5>{{n3EGJm#d>$9Lkp-YP0Vqj4k4sh3lg6ULoy$C%2NxHY%W|3Piwm4T%gaEGi!6b4U
+hwxJEd4r0u)|=H$7u;Kq!9w$*{R-$?4R^omL-LK5SKc49ss#{=I{cUMwND6q;ERCDAx;J42g>_=DJi-
+qKd-2acvmYykYou7<{XzDq2@x7b-7cmK}OEI8Lus5>HN<bUN>Uuac#HmFHPLIM@l`)9dx<zefO>2U|h
+|$yN%OJp=L$k@@o?C}!CbL@d{rNYE_!?$vj}7hmj$6(~FLSE|tUtnPyPK(p}ZtC#-^4~8HXKkBF)06G
+NZOZ*YGq0CiO!Yi|ELeD4q67~xoUtIJHom?99<r47oM#F}}mJ`F2Ibez`rc-7GmO9|*3N_%9D{XKHgS
+YTo1<I^-tQ{cN+KO%sBQ6o6&_QU6#mrPO+io=~z_GzKZk~~c&PCi83;mP90k#6A@=jF7z|k6XKt&Z4z
+|~BXNCR%DDv>_rD)UgRfHA57EC)UM(^4h(#yZu%FV;AGE6f*p2Fj^iSGdFUxdv1~pKr7E9*Xex6Z?9g
+aAB(%ER1kcNs_JD3X~2b+vT+3D$ofFU4q8y#B|6E*n#k=E`NJ$H}FK1rD$I*s-S!4L%D>}NtSlu{<SX
+AfTVzB%)&hdK~D(?8bAXZutiM<W|ZXbp_qdE0MpfY=4h?A#`UebLSm-Xo2ZWvQL=mnI4Qt67<u?FSeY
+zM)@ICV7DuxR7;eNJi*q(B#%aCf8jXK(oT9OufK1ei-!TJL#b2OAs4@dAN5O}WJ3_4EG%hQ_MifT9-b
+TvIRT<3y3@PA?Tu=24u(8UcSsz~?R0CmOkt1n*{W^Yf6@!=2u#~-H-y9wUk3Z~xeB28`0|OkMV6anjF
+5!Sr0inJ#2TdDJ^K7~3_w4d}gAnchqCXgR4>o4MZLF9Yye0ssn+nY_6UL&ch&)$8Rc{I67g&iLC$<U|
+Y14Ya;Kt&;9sIEWDF0X!5oZ)X51dzF*P1B7ns9N*3C>`hz&;8|5%kGPn+Cn-uz~##PXU1s&pr+gD=Zv
+1JmUqi^~DAJm8v<I8+VgnH+b#tW5BbqK^t5bix;5v;b~2jy_%IA80{W|0fImFJ6_yi<blNC;ig522be
+znI&}Qefh#U$zIGqrhhD41(2|a!xjSl(O}@A_L7G*aWaTlO4fGsLww_d@bYcX$1$PGr5BqkgJ}IRnk9
+cwXRnl~W3-TvTH`4S`7fq+Stc-&t?V`!)Fie%D9ns^n6ddC~id?-?Tbi?MaXyeD5oJ1$^o3Ec*1zFz?
+oN6Xxwa>*zIXC6e+)e8*mK*6k7oA$fxjs8ymHGtx6vU>lO#2D&-Tx|V93R81o5;J#0Kxx0n;!Vzs}KY
+vr9Ba3Z(S_E3rh6rvt&tTcj~Hl>2&-yS49#ydp6~zX^`W<Jx;-s~k$^^6VNyciG2Xvu`b>2XKOnpTs|
+Fc3WX;RYkX8W{+FRH6X+aINRgsnYz{joI-aYFQa8iC(eKTqxUS_@1X}Me5AwCv!3v=a9*phJOLAQZlT
+*si{o~&TwsWU;yRK%zGo_cF=dAa)FdOJHE<jt;0~uaB!d6Y`hqCVqh(wQU`aNOBR&YgbXD91k1vvm9c
+>S*3*$m?C^_Ti3}jzvRh!?aq|lNuM^OnrG&t;&jE*9#-QogpfL)qMvJrxfLyCYUJ^I=Y^!d<{+=dPg?
+YBXn#ep7HYV21WYu2$&CLk9hLj?PrLQbBvpPkn<x?17Ep(h6P4qR4g2$ppi*-3nxFZD-<=Ae(}U_voS
+ikO#hs=CIrPm<6ckghQ-sOys#?lH`zQjF&caCG=0H-5;Aq$i<RU|#c8eUkGeAI-(&qP8Eca^>!WU`bc
+axxj|O-rm(}&~y-XgJUo?%e;&0hqPpnpOrR25K!f$YEYB3fD_w*I!B}MJqDr{bX<v9w<JLC90(Di+yG
+Eu*=Y+b+B%{t33B@hTu{JUv@4&D7wpH%ZC`Q_AlW#mw2+VY)??mKEfzYRG!1{fvR51rXK`j`b*T>X*D
+4YfHRws5wm{;uov%76f{dIy*3%dR=1qDbaoZPEODT#?_Yl{EVRTNi6-OXo*RQpetqBX4hJUSTvxlx)<
+H2TLX_7$BSQ3X;^nfZv4k8(|CVj`Dk^;nUlVG`UrIc*JDh7s}0uRr$nh=yqW;+!=&SwcjVHbbbU0Qkq
+f5+$+lA0h38}Zrzw*_6QFbhRPVB2yXq78`?U7vGv8B+S8`s;nMSN+|8@!fN)`Vae~(e0zr?eo#?A^iL
+Ha4_h6$KZVEEa~ir_r>`h5dEDFeq0tN5aY0bImIBURx{v~Vxc17`55zRPJ~c=c3DC75~52_My*obKud
+K#eUaTCGYakS)HqurD=}8}p0}^}FESKXAN+yh-@khE)$7w&!7Y9{{_Dx>H?LkEoxb8RgSJ3e4Qez3d)
+A}yvH;q9M2~^<E*d{Qr=Ktxc!3^HbHE`DnC2Q)ZO54B4h-jQ|7G*z$4`2oay-<|Q<O8Rk6{=)$>t-KL
+6T33w046doN<tCFtx)m$L5hi48edvt0DGdE_SMFzaBQ-qkB&0@xa3-eSXhLn|$@Kc@H1D*at52;j4V$
+`Zi9uL$jG=zWve8-oLaW@nw}~4p+Ha6!=SpEIwK2$rk7}1<@JZE{4vT#d~<!O_Uum%wSlVwBN&+{W7}
+s>=}F?2?f$=$NDeYoaVQ9D41YUv0MkWQYWXF3>sJ+T|{bu-U-@0_KCpnXaNStJ{*3dB3l<=jHSstTUC
+gqDXX}cab*YWVUjf6x?Rd?vx|e<4YrTHV$8dl?7elIz&7HL$`xyKFTt%U^jyJ8qhPKT6<?5d8JqNZwR
+}@J62WOgO@$j*0~;@~d?rek=tLp$z#Cdqn$V?Ib5;Ep&qugn2<0{AwwOS+QlzmJ4FHlg?8mq$gxWp5l
++MEh!eS3*#aU0v#KBwSq9YL8Vc2Mkvb$FItZ=&}C-zg2ThhW^><H{If$h%OcTKWLwK1Y$w8BKOtZy~J
+w4(L1DI#%+emfApp!9hQq#B1p1N(s%MJi;l>2!=nY3SNykkWw*%-e8UXi**8`0LJAJAVD5WzvJQ{U`r
+<{@J6!3puG?WFl>9KRguPc^~qhgFPDEb;g8|{-eO0k~`m_d7fOn56_#}5=?bFzKw44=vF0*8GO;Dx&?
+sv&vmR<&GY765NRn*Y7JVc)xMsrC^s$$UVLIGc8eruZ2I`#Euejy56mORrMU(`riIV2)Xr!zEY?#Vqb
+uOmYdlL6$5-xQ6Hb{;IgbjQPI*|4=umZ^0+$|v&DE9W$JqjFi&TsDZIe;++^i1}a#vb&-7aB!2NmCcc
+BY>EY}t@ORZj*8Zns0e@pIey^nLh#kCb3Vykeb}>IMx^cqd;xOlNU9TukK*)_@pK2xPkqkO(Ce=S<>h
+T-s1v3p5*lE%X#KQSEx=7gwnS9fqU@tKUtteCDh!=)bd(j^Hyg{}{V~4N^giwETPM9sl!B(nBpU?-uS
+2(tUpcIs)g8v&FTcB}aoTz6y%vm;$xHA1m}c+a;&VZ<u7E_0l6sOu*}Fn`G$<{5mSO=|ZF+VVvKvSej
+_tWqi{Djl_~F_N-1SS_S(NyY*7M$(bWbq1gech-0EG;~OHuJ`%!<AqQv$ZZIy4EJ3l%$sDY=1~<vl$J
+WDyz0={JPRpOl<I^dRe`dh)7m6;{)>6g}|7C)i23vxy@^M_|Dqp*S&M&*7GtFR8O^hvKF}uOb+S90V7
+ABE_N1FIrCE(1ULo$xjs+Rq5M6T#)glBIhbJ%ggF@|48&%$T;O#lIvb%>9LBl!28IkwAE013xd^qAxn
+cwIW57d95bR;P-(7s3;11A-bnE-}FfG`ZGANWLGRiCMktAT^g&z&dbJ8&1f}NUp7I;!tLt-WlCopW7+
+cfbS*fMsmhHmC=+Tji{Bn#D1D$u9DONQd@{#QKdtTDJYSxtSPgl&w(l=xbiWOZ<SU@;ijj`W)MWI7xu
+y*vp99@6O*{`Di-lmT{a0zut%$z6Crb6SM%{i1vdw1@XO#PEJ~G^1s-ntJzQn4UHgiUJ#4%00%3XsuO
+8BVDuF2$;9YRUvpLe8cyM;`<mtIkXo8AIlZo&c5E&MVl4~Y(O3pB5vN?q2)AlnY?q07^rPgmLf<S#U=
+yyaleX_K@KWe<sSlEKJd#IIdgX<&&Budun;i?v@^GPR3MLFwPRc~VH0K5_c3C+F&oxCvcF8G76jn%cR
+0CQ$z^a)Fong!^krW72@LrxdTRLU2<qPX<nO)|Pl2Y$dxYrG`q9plXTT}oB!0nb!lx3C*9)3bAm7{1Z
+-?3(YGfsT(8)DLj3?OdUZ;M?Hq77O;^xyygRf<1Vx%Xbo04Jwqm<if$Av1BZW(rM?S*@>r3S98#%I-O
+!bq^S77M=`U>f6BXhrAu05Yi*ecawMA;4^-<Ocx6mY0*G&C2Y@c?opXQP-=V=;l3k(@LlIAO!O@<dHG
+rrVLdTUc*d7iuR;2LYF=3jFIrR5up(aie$Qe-Umu77SIt(ei$v6|!n^ZxZ%EG@k+4#Ph=BmLS`#UqqG
+#8|pU<04)4D(ebJq@$T&@hFesyj7pd#I8kYoB8qb~iaJ(pU6S^K|S|+^$_b4HwHXh-CA!fS>fON|o=S
+t{;&6<)SLi5=x!lVt?+7&8Nx@w{Xxsu=s?@;$MfzeX_?b0_POi+9c|*O7~!un{^9US$;+O#PI|9Mx3o
+qtA5%nrhv#yM^^~7(0Y?-flj5{;Z*Y4(9l(SCq$VirxZD_tu&TQ5v9Xq`WJ?;<Z|i|<Rn8yq;J5u2Fn
+zk5WHIkBxqDndC7SAbGs7Rw^i|<3FqKq$;mL9dI%Gn#XDw(F6ug1Q~I%}3{d4=RObR~!EC&%x;)je%O
+YcD1ITtS=<t)Je1p!#sjtIn7(D-v@TsZq6Y#1VYXSTmf`7fw=M+;2Xn)$Lp9T%`l3r|9B}?V!{CeOk*
+EV%IhJjYL2B;V%qq@I#UO;JV=&>#On`R|>AQ^2Z))7;h*_>uO*c>gg0M_LCYVaPcf3N0tcgxM0)-mY2
+vTV<SYgA5CVdSsTgMU~JWET0aqJ$3rA1EO>fp4jW1dO(~?xThTC{{>^wL)^#(0+(hwd<j0{M!aa1V3-
+mMD@?VpDKElnozz=aFa=K(i(n6PDNg4b6r9Y`35!x=ro?ZIi(`yWXMScr}BkvG}J3+G{n7zKZxpXRTu
+Zs-U1e#QSYa{9sOLzfVX!~{S$VG_rCHz(V8v{<6HLO1FqQF^H~*5(KjZX;MBV7vT73IT)J?oo_7N{_M
+{WwJ??9y+>2=&uU)q)$c~GDJlD5bc7=I-yij8Ub9L01!>Vp{U`_Q>q#KLaxtUfaA*x#d^>F{y30eU`D
+5)|lOzVYP&_hvhT;Ge7^r_3-(%=jud_&4h3@Lpvq?E{z(+TJP(H&#n=0M370gat80>IQFCN<={Wqd$h
+`C$fY+jEX*jrAqPsTCmZ-|&zK!_<f`(axIRO~F3=)YLG4Q8cX?N68EAM%B#*#bwjOHZtxN_V3CXe$Gk
+AE+UO(xtD4;`LlvmYl#;Rg=YKBF01ZbaFDC5oHg(L&^PJCe*eXR&5#bvv%mDJ5@9#2=c?>n7t5sN3v>
+7#6@krn*xly$dZFBD+epJf(6-Xx%bSAgZEZAY;t)J8yzC@;SQP!ircjo7&s>tKXEM9OKiiedfLWq1!N
+sEfZ)P3%go$FF^A{q<u)0CG8~jysVAl`1X3pKW!h>m1r1VQ$wxv$2+yEa^ol<`=BnSgT0)f>-!SRA^@
+K!FS*m?!<<P5{1^-qI?^KK07aPb?BHy9kJg*}$Q{!DB#LChP|h&mnhv`+n;_4a$`!5(nbO@AMW<xF$7
+e?ADF1TL=laA5P&KF;@>$^~j>^5!2nDR7%7s-SxN7tK_h_V)y3MV_mDtR!nanO`1%s>}9vIvt&J*tFi
+&KgZ@dK(~|IIR*#pD$oi&)N~S0;%kg^i^EGwS7c2JlkWp#!@U|FwW%=>%sq5tpa278j?XItR~JybXHV
+l5fY=tc>kZdRqY@K2U!%u{y#aYFcI>%yJig;<_i9Cazd-uqitqkXL3W%Mbx98%IvqmEyJeSPoG7uZ_$
+aLWyLlp=%P62H0TNt2rWFaoij{5A9fW~~AaDKp21Ny7;3g-xZV)1%`d1AlY<UVsX<*T(RXVi*M3>7v`
+&p;tl~RIKU7Nbu>guo9{mR$E?dLa<Y7D%k;StR&EG@Ul<N7RgtI)TFedHU)-t~C_46E}SU6C3Iraydl
+BuCj0CLf;4Hc55UBgA?8_C`Zf#!z5>al&AvNY}gW^k}(PzoWu=6FBXYb?32-cpU0@ZFn~H&XKR(uX9e
+V9lXg0%^_`4Bc<t9w%jSi=65j>bxS~jHScY86cp&VV$uZSl5Qg(Z$RT7C^XNfcVIATL;Dj<*6@I-0bS
+jhwOO{)d&!p?xco~y%wQ7Lc%&gZaa>e{{CiDsc>O-dBzNfk*whJifKUk6C+sG+wQMz^c3c;{6FO=ip5
+ZqZ-$q}h!s{mHSd35DPVC=|sE@jd3hsQT{h)e~4)cLNdgqI=ZFA$kdH+bBy+nt-JWR*Y_LyEU&nCuOt
+;2aTl%<NoR;a*{duj{H1p|HJc;2yTw1g$>Rl|+i_pAEE$PsApBXAMPM;i)YUWl@0fmGMGCmGUJnysqN
+N1`8!8`pc%>B8BBM7?_*+?P5*?$^j)D^$~_eGMnRxbcr@7%O9%jTDV+h2y+D$1DQ_76B@w2WVDO>W#o
+PK<F3JJ#2TW{@@MRWWolI1GXPlclWa@SK8nUp{aQ0Et~wA&SxBqysOpO<D8m!M--ZtQtosydu$pknUM
+k<ibd4~9HuPeXql+oG~fy_q2z@9@eG95ey|+c%aKSuOeNS?g}K`7^pUN554H4E`>p!fy=$o&WU@>tw`
+;pjxdK042T7J)@c<c?Xh<ymUjdRe*Q8@>WV1HzolF_&6iHpnr~t?9WkmNiKeo+faZdIyu38kqx+H2&o
+}2jCRo~c0sniFYaTYnXr(F5*G+0$>+Oh2CjCvr$=F=|!@P~TFAC3$RHI9N=wjve*Bv!T;AAqo9#xQ1h
+xNoXWe93V-A7aqI(GhREtol1t`C$0h*gRmt%@Q(yzu4|m9?ZLc<y~&uHF{ZO92@PfO-jn`(B_bQ$>~K
+H*dOq$ViV9}&lly|Ua+f8K!fDeg+V1NwJuymZySF}>kr;}JVo}8Vz)ZODu2MnJ&U+H)zzAvF@!dY;l~
+Ib*4^(<y(j6sz1|a!q3=|A)wa}2#o9dXNa$UQ-nGQx7WCXO0YjmHpJ6*@%E;6L(JcX$1V<-d2Wp<BQz
+v6guhwVYrhnPuQQ0u3$79XdW3>iOjfRHf+IbECcMh^cpBL~Ho;tqU;<$=>;|?O*i%7<KWxtw{l95BI<
+{%;(Qk(u^j#-kWK)E*m2MKr~Rnm>GN0Xgsh3q>r!BnTPNYQ+t8DQYoDFl6X?rqNIPMdZEZ3eNuQzc{X
+70#gL3BV3hHI~Jfa~hQplaXjL!msni?5EK`Q+j9q;O_Q^4KN6xt@*?^?)DSOs#+ft9VZ!cmeIC)*^|l
+Z+qtPnn*E>|h*O#2u94)Zc@sa^eW{yr8n&ehpB8W`$+eoSnUC;<ha4?0XqdJ)t!*jWmcFTF=iM!J1t2
+mZhVR=4;it1%=Y6@vPUXJu3>BqPOs+IDj;H7;<SGqe%%+XI6;-#&r760Q@A`QHNsji0mG&0XW;2E#+w
+aSVu+yf3?W8;Z2T)4`1QY-O00;o{l@>?n1{EGg4gdhHE&u=^0001RX>c!Jc4cm4Z*nhiVPk7yXK8L{F
+Lq^eb7^mGE^v9}T5WUNHWL1>UxCs$6S0vQJI&p-zBs9yxINEh9FOg$({X$%L_rd2ieN#?vQziJ-?Mn3
+2vTyKHs4N@i6vt3VBfIVU2u!NQpK&7(^<uy?(9BeuT{>oF*~aklS$Rv>TR(%G8K6##%z&~g=W=Eu!Dl
+9_~S+f?48i1R5^Q^?65u&L~baG{{@6wwP168%W_q*MJWIwOE!_2VB#hfMa5*!QZ+9!$@5gOrL1OV83&
+R8;%5h;MimD&2YrEGlO`SGmf=}d6?;#fESJlK8%7hQr%y6VR6cog^y={V>~I^*8uB0WOq3<l;<ts=5O
+Z|PcmZBgJ_3gsUoxc`pK5`zN)fM1Ei0K%2dq?+YRR<#%2<|_mZL=#D9UpNd7A`K70+38aK?_#BKF<E+
+0oem7(X4I|NHHa=j^A0)6;|F^P|Hv_V$#$dVBo(==|vIF@Al|4vv3j|2aB-JzxTA!ZPBf(8NEOl%zs2
+CVia=5il@OmZ7o`shr3Za^=$np9(fr*FxtIhZRE4Wl4rC!QL1snVidtSLR7asH8X1YR-nk$)Z|lF&xt
+RN>_}JN|iy*q5VC8=+}~92dvOCuP`A`3AER9Gg6gh!aOSdBUuSuDV5a^pk=#YGK3-*DXm%hC6Drn%w=
+`k>-C01o@HQWpI!7AK2gCn9C5%R=cV-ZjhOQEw(;O7zvh`7H=evZJUxTbhbIT;=ZB}qQSXw#y{GRtc}
+{XZ7vpMCWTJ1^9M(YQ_eCTMmCguOi%f9K&sbd3ADGYlQDsGcGnMizx*YUktTz@DX87-4TflhFE)YfL?
++J|=qfF(s$eE*J<Z7@etO&h9Q@QsR&i2xbm*w6sj>BKDrGAAR22M2z0v&lWHWr1*#{`aL<Ydj-vNTwn
+8I7`&!xQ%G*-kxd*2oODf0hI?Rr=NnNgv0(<D%Ar8F#@WG+k$EAjS%y*E3?aX7}ImY#|P{R=TC>0|;h
+GYe|+IQA}Z93@EDUl9e;H$VlT8+9}G5z7o$bI{XeU;Fq+D#d3y>EIu60csUym`=!VxaYrH=G(*YoO5D
+cHd0FLV4VWT(cPHC<6m1c=`i5wLU@#wzIV&-1kCp5>Qz5GOEU5b}Sn=EEfZcDyPCpB`FFLTFMYS(lsQ
+G7M_KOy?p1J`%U~^H;)VQ$#b}yaC2+es}(aG!4o9@}5Z!mSNsv{gWVlHx=XT3JhcBz$*Iv%{gx?K?N%
++{g}9Z_VjwRGKK%dT==2WG^+Fixz9usjHcHu?<Hd4UK0V18g6AZ^@)!K~&aEsD*vqUWK|zwi7Jzlai?
+&U0S%X=<O?H<M$>%eRGAIGS;|`9+Gw2<geg8S5g9pleU_S8&W>wE^H*<zmx3Sq^dRxwCM?{O@^I-V1;
+O^FatbZ*V7!*&bjHnTQtf2Q3nMBK_&DqyGExV|K?b`1XfO`tm-xW7{!KXOJQ<`0nl5p*f%1z%XFOQ?o
+0Eb6x=Zcb9|UjNfBX>dyOXDVFu`ZTg1vg=gC)K}hQnN)Yh<TVxS`xMW~xvVnR*{Hn@+=|ra_kDgGDdY
+{Zmi9E5Yr_sku+waV336xAl)hE({nGpBdvBbc5E)bccBnsn4s&b^x3lYpD>WjVIOX54)*?s!ev%ml2p
+I?8|nz{wLv5_%f%!Nh{Yr~u@;h#;yTPbh~6KL`QRYunTdKCu*JYsJRb@XM#9-BFqvn_@c7a1n<8;*=N
+D`x!3NL2huikwQ0d4*~Nqay)+kX@7YdQCL*B=SZJ15OyikoF54>3W>1v7oZRq>5D9Z0|AzO_8vJP7(z
+!kbh;jn0Spzc8-HXr2_USRp%z#LS4f%N=J4Qc21So5;|dGGNJ7CS^%V2R5r11#-@RTTh1idL4s2n8py
+*_fYFID2G>|YPDxnc$kmZRRaA*xSeP8%V<BO38UuY>sM6+1R{VWP?=4}NE>XwLO(9kBD`cCd6ezByaM
+4mNbSlau*cy%1sp_EeM>#xMJwkyqRz+p<QJce}P`u((%4;(@(p=wKZp;RwTH><1!`5v@pV4M&DRe&$P
+g~=XP#g%U#_hxD;<k@#F-Jj0QK429TZKvpKMf{0RHSU)=R!dGz=D}@l2AopzFll%ztIl;(uJPxyjuRG
+P({D7*#k~1&1FV<`yjL`tz87FZWusII1Tjc_p1R5Q0`}K$9>lDww5pZ5XCzxl)ep6O&i(*&HQqcd~HN
+7H*oBs?0qfZ143gLWwvR7<Y=a}t^Z9GO*S+PV<qoFw&ofT%^WTYHv<%mCb1XEZ@$u2zFTH?I>@xZw(7
+MCO*)W#(Q*CK{OP}3DFEO5FHfEumS0@#Y=3k4IQ}Ajxz2XArB>0wBAh&T?&JV~`95)2U&iYYy!(}6O8
+Yy&f_J{SXZa2m=(inSVAhN6;rrzBW$?`553?72emA;H?{s=+g7F;%<2wx9x#KjB9}Jdzy{(2DUkw#fi
+-*eukwDMZ$pp=Ad%$77>lY4ry=zbDc>6kOn!G|k^ydz$xYO~cPtTurRYIS5lY)?$e3H#Uzg9n+%mjMK
+g#RCARU+SP)!1*M_m7F#<g70!xAV?#Ve2Q@1NDa5vMH`Vqk7FM{=Uiu$I?{XtVu5Eqoe=6FHOSH-usU
++hM~W?zL6oWQ%)qJ_;K{Z@!QkGR|k}WTIn8MDs8cM)WM+ag(8+eDqZBIJ|&%Oh)jhPI8p0)C{E0oY-<
+7|T2czG(^<FDa+#o+(yAS~#1gHBi|@R7X7<=46h)8fn#kif!u%FHtCSY**w)mVR>)v1&psM^gZWAAD$
+#f7`T>h3Qtm?qeZzk;)oM}nUH2}INw{?bCaq*UgHT-S@B8-HAxs&YgFsUD5Wx7RVC*t;q|I?0q9JhYy
+Nn|f74o~feCurM0NO;)s0Fr`DsNM@IY6ZNb1aw$^>H3-jD$9!qctN>Kf)h^z@K8z#F~$CXF|+JI5452
+=7E0HECg7##oM%S;N3rn+$0~!{HS4J6L6EXj|3&+wg@Vfrb~$R9S&D%1l$EXAh9{mJp|4t?<0ZN+&c`
+j-8T-E-ZyNt)KSrcnk=8vUb&~$(@6ds<~a<qDMW-dilvU+P8j`y<TL_jU(}4P0SM#W>4x+x#7v^(S0!
+^J;*AY8Hxz2t+BIC@WKB-ikqqXh`ir7F^>*$P??IdZuf+u%<^gO_vh_ed3^8=QLDe*Rv3nVE@mioLRs
+|`#le2V~p~U8)ynuEyD;Vr>@}`-3bd2BO#bzoG<E9hrqQ_0%9_UME#a1>Oce=se8=95Zr#3bHuJs4x<
+7mKk<BOfkKhxp-f7#ccr2n<8T;JSq_lf%}Lo^iyYY;yn@!ETXkfdeI-Hpb4NV|BXRMy>7luLA@&R^;(
+uUUvD#bJlGm4eux18Rkg>RTSD&OUEu=syW+l9tllwrkTV7@5wXx!+Hbqwg^D4n(7pc2}_`LN~xn^TPI
+D5E@Xls8n)l&zW~DWR(PIyWx!jNH=)6F?fG3<TWQ(&*TSU@Yab_x;}~2%=E!n%Izx%^Lnl&1fKok`(?
+ciiYV1wu%*)D(rY8bteldhvJe^dpG@X&u9+Mmy~40UgTktr0vp6j$(DfQ2=G8#N#I_PaBvt>3r$L%2(
+3jE%o7Fb^CIoD$h0V0U8`E+h)4B+$ij)#)z~<1w0<5WyQps#nimY0n}BqB50bWTCFsr5&6^6HDC0ex3
+LWR=-pR=e-E=B7?P$yA;hT%OapR?Jt=pFhxT7Ah5lTEaM0kGK;x@2g#N57Ngnx^Y!WP-J-aE}+4J&W+
+ir=ulNTvzfO?I|-<F1Q-g1~eo)Xe*br6?$uSGo*)0Xn*PpA5GzqjuN`ss?+$hA$$V^DFo-_pf+l@(M~
+BrO9qmEk#DR0Z=h9Sk~2iKLP>8SlHDPq0$UMrHlXTvj-<|;{_N0F&%BUcbj)Wi7dx*irDT-W75&8A?r
+WkebolNxkJLLqFD`CwYf^iwQzmWLc4ZgOg*5waKM_HgujuGEQPy3qC(3%gj;{m3=p0Q>NH`K?k;SX!N
+Ij!;I=V7PS}|O%p*|KOK-FWq1!N%Ny8crAOlB_a<z1LU}e05TjZ6@9Jl5$pkN3G2SJXskcbA(>#6NbP
++F9g!!k#NT^Ib1czR`hg2+;ZxR%m|-PmnS*SH3=#@OcBqO9elyD%~u#YI<tQi*DP$*XX`?I>>jai4?^
+{ilA*D<M^w+kkFr>RxuZ*+bfcAi2}6)aw;|64WZwS8fwGduH|kbMN&<*iY$IWDfx+8_%Xn!{l=!pj$G
+82+AsU{M`(GbN7A^fsw7p%0?f0jc~MzKU6%qG_Q_jL%PBn)_&$@I<&@t?v8PSFmF$p5az%Vc4R`Pxd_
+CrwVtW0o8`6P{6SrdrWcuTSAjEFIdgAinv>f3Mdl1kcTAQK3JbPd=xVU~!NxkMFjwr`^$U8IAy=T6W<
+yc)E=*?n?$s`C5Mdua!j6jl3s$9h%W&f3{+1KiY)&lh?h&()?>0-TR|Yq;)X;OVwzK}aWogldavH1B)
+21diuRCs3pzD@5qHkK=St4AV9=1hp#{7_aEmM~E6SZNsubJ5Pf$$=}wPI&EyGbeh_$RLMOE2X5Tjwwd
+Xn8u0n3Kbk`fWT*w#^PGQ>!m19e5eY)gFBC{7l@)R82K6W-_G<@?hri+BpuXLwz>GD2(p!$^#BFl~0>
+vOPd0jpfiy-qRFyv=8GFnJG^71HHUm(B5#Py`QCU}Oq$Hco$vkHxOmz<<W0xhtk7=fg*UMGAK$(I0Z>
+Z=1QY-O00;o{l@>>sRHesgZ~y?}p8)_R0001RX>c!Jc4cm4Z*nhiYiD0_Wpi(Ja${w4FJE72ZfSI1Uo
+LQY<-Kit8^@6-`klXG46ZK#2^_sSn=BL9t4OrNjV19&vYi}?2N(iFazbDRo&iX~iO+Ap_14|hJv|`hI
+D7YbE_P!Pn4aqH>gwux?_RIBcaqJnv&%fG^QPL=vpjiza=dpC`RwgIca4fB$&y8NS<JFUa#1YuWLA~y
+tSIw&a$BsgiV~i$81yKqYS((N*__XdI-jkpdYi1T@;a|B(j<9*m75p5u_XD&6*O+#k7c&Zo4s|FTxB;
+oLMUo@bA1K>si9qy!@t>D29ivtkEYX6@}R8N50cv}cyN=~_!bNZheL15J21|ofw8=-5@?=mnmnnRlv-
+4)92yi=*(A{OGAoPKW&yzLu=Z$J$$4IsmoSp1fdLdHw8gGq9_ij*=M({(^Smi85qt2)ZB<|M;LECB3W
+A$$v(A_1{bsb6=9icJWRzs(JlT}^3?R4%FqPwVGRt5w7LCBAm01+af-sbAsiVF0r((tL5G?9^wrSSYG
+C4UqNuE7^3h2t_c@5JC945_XwW{hhO>R29%-557w$28_>2z<Iudk|ky4M5Z+PkO$!enx>S#RomGD(Uh
+y_TIf)nc>GC;WYHPkx6<nm;T1S5cY2*2OY6f7kY}^>&rp#xM_@hWaX9$Pc$!UBU|4_RTu8&n`A4P-V5
+S4_DWhn{~0U9aYzP>4v&Iuk0fxkE*tB0&QHt;+gNWYOw$+M*4A0ie|ki&dv9HnH38lwz|nJ7+7&{I_v
+!7`nJwij8_?+K`O2S{|Ny0*7f#qFM*Fd#YcPj$63BwC&%>DQC(Lxx7kllwy<ht@+^G{V_gEt7w5&ISZ
+_55_$SF4hO~F`*JsHX7c7PS)WF0hMR`#rUnYZRqai$f3QutpK|5I93;dV+qM>i<1x%f1Yrw;A^nHYXA
+vMF_^6FFn)%(6CJvQqamV82Vf@5CSqrH65<eg8G{3crr`kS(tRr9<*1aN>;_}>y+3>Mk)e4Zu6VN#^@
+<6t;UxWfeDCP9N*8tv`v!)9FJR=|w|4PmpNZ;@Ff4Q#vf>LZMV*S2cx{D|u9at2SA*T76iup5y501C=
+QQC?HSAKo6nKbpMz?&Rdn+xIZ+_w^><K>#K22OOx`+M{GuSLfOJVhccF<0s!dKZ?Tr8hA^-7mgY>M2W
+;wH52^PJZ5HU=&7NxH?R}dh@0m(RRIjJ-ae9H&NqajRkhB`bpebJhqS;|AO<zh7ugoste_&FT>)QimQ
+9Mw-zLp5(q{5(NGuK?!;1h7U=wFg5rxTkob+ZZ+6}$KfcoJdq}pXRg99#y<11_2&lCHzBCg0&K-uf6%
+-cIuwtm+VA(>c4xhhua<R&lYRh{yY0F+~<ySq^X$7yj<K+`?@BaA89cn_Clp3fE;kZe-{dM+106bm^8
+Zi~eNPCRIFK_@>Rh;X)6@KOUPZ7#Na(84(g-KBeV{^JIYeuDu#k8tZX`Pw375Bh@b0Ef;OUp)SSnf2i
+9ro>GkUx$njJYVqN@9?C(y#jJ0VS+@H68_r&@erA0iQon05IcKfPRQyOTSNezf(Q<eJLo#KeV8Fl-sS
+7fiq41c8Q!Y`_UoWJ{~K_27!<t$sDShCDr>GLlR=X&E@ZU$sR2M;;s(p>K?gY44){`PVGK3g#af_(Oo
+g*mz=7h6rYPY&D`)wDo{kdeVUepwY83Q{N0|J9q1k4Ret+4}X_!1fZ}j=UX7uA<d-!>D`1z0jlHrek?
+cvin+rz*7*I@t4UwinMaqM6IYe@ggUwia%(l7rt$p7ruMt<3W{CtfvjGe=EzHRe0HLz1?aHavE6x3#j
+$bNzAm+J`~3U!|1FCgy@>i*!v{9*Es<TN|@=?wq)kp3e%81_-rNxwgO`^Pu$j)rOkAaKtR#^1fk_g8h
+^KTP_w{;2i)O_AS5pB><@aG+(2gYZ#3i@q_>Z}9j3RKNe60?zjASHc1v@Dn4GkhVH#f<*?Uf%I6^(=>
+Wgl9F1@@k7nW1_uNDI-(zkt)@V0c<j*|rX%1#{j}c(%L@btZwYc^1Q5P_didlFN7{e<<mt07e)I3Y{o
+Q|bK-!1)bDCYbS%O47V>P2L8jx3FFKml^F{e(_pFjm#41U+1PMg~OxBCzJ4-vwi*JeM-%Z6oekYTdL>
+MDD5p0Be<peWCaIV$xi?wsc^+B_jb*o(oYCw+g7tHs=3=GKa_kz$;jo^`H!oKVt5bqiZ~7@+`JjTCqY
+G(}Nn08@`N<muP>vbxEU(9I+>kgP#=TI3ljCD~>4Md<~k1vB?}2l%<YLYolJ6{T(Z>+}Gq1<CD!?5^(
+jEvC{{wHmZn-kZp}2F)3TxS#SGG!RlXauBzHtJMW4b=8F>I)WL6wtK{n<+EeRyN1ipvK44T^Fe!SdJL
+4>4jozTCn)z4fsjG!0_YZGTryS^YV#P&Mv8w7Jovr8RvWgKAs;5&SY@nWb>3_iYXsZ#-&tK>PK=eRNj
+;KGllM3THUj3C*=!4h1n>(OBj6D*503(HMG4HLDH>GeST4uj%ly_#=PYJ}u25I=&5X=vu!82i3gP`Oe
+Viu8WwNXqG;uX(27?|9Fi;yaMktPDQ<gc*qRHxQntTnT41hUagoqFr0I)GOE;%g|b51u2+b|aZ@3@7!
+twDEI(pgFihht1w143Fws5Cy{)x7V>jGBe8&Nr71T?!qb3ZveEhQnE1oHIz^XJwwx)1H4o`TCRp4CXV
+tUgS6VV*JECQS_%dQ-r;(Yd+NjZsXXuRiIk3N4GfAQfX6pKGeE!3+}f>i+4fa$#jxP-yT?h1jzG!gxp
+92tKH*AHh3EYH_Qk`=Wlm?T;61hVlK^h0Q9F0VE6VWXxoH=!SGNp`k6;N5p|xQ4&W(TzDCJ|2iLbUbk
+Xq#!*sL4<8S~^=z)T09?gr{Iz^5P5?=!iM~Ofj4y8X79{D{`W{t@W4_O)$^#_02KKBgGT>~gO4OORNN
+OO3gXQ$U^2;sCpY0w7Re|U2?xP%`WmJxulr5RG=hD86GiSHZ;wQLW8`faFRHB{*SYyfZKIN<%xq0opC
+iLeq2fhx`52atzX+kwF(^+Ig{*`P?Aj}krl>O7qRC#hxV@Kx4`8|e<&W)bhwUohRZyw%lt1g%Jf*k1`
+^EZ~;G$QLn^KFI3J=FnJwurqu?jF3678?mGtl=phQw{l$K8Dt*!n`D`NES8(4wO<1_HQPv35jcI&U=G
+9!|3v{^K^I@kVvB-wg;D@A++|S`Ok-K2sa_<nvRU%xUGf((gH;>gb?1h=vlHh5@caj`vEjU<!vq;NA8
+Y5^U^Gec$IY62Q^{=(=QtjaXwRBm<uf#p`4)LR&j9{)S>56g36?c*wT8yB?1LIkK!EfB9c^$jE<l!qL
+kS&G)rIvb(JR7_K--uVYr+QG>@qy7;V6rAHNtL|n`X^!CU0c3O<m)OuU<pEzIi8WaxCHu9NqI`O)X~d
+PD48{E4QrQ+GP$cdax}E5$J3aHt56Y?a4_=b7hu^7G(}Ak<DgRJ%`3{jC(dkgoV5SDCB%oEHo9;^Cdi
+7X0yuh@psf^y3E$Is{yv<W*{*yak)_ELISsjXFFE3HqW#AwlK!#*3qGc)2A*!9(>-w|M!#6lL07h>EE
+gX&N_ZXr_f+{dieBgI7*i3WnFDngJ(l0ZpumXeYV)-%D8Mk`!rUwuz}*zG@4i7pkRMbfS4u|RA}}Xz#
+p>)1a0fz1RR6lMKec=2T^5%%kc<^qTVJe82lO^%upcZjnNm916{ulbUQ)$X0^s|v}5b)rkLk7eCHoDR
+8heD=&PJi4-@Layv#o>vz7k?x(CdwWT{ZEHs*nu(S+%XMyGztv!(YFN^|H?XuXbZKM)?h#`#D1vGIN)
+=LozkSUoClin=PvS+O9!yslU+?VAIg9MG_r331A{j$gC+e3HRFxFJsmjUg`)i3|etvAKsx8VfRO*ei8
+L8)$;A8~hm$nR2so%7KAGtC3%1pifL@pnv6V_$PI-1mQdJJuWQr=S)_8HAU>^2cE$@p1@n_>{V6*tFi
+-s=`B7sBhoQ2j&L1Mm;x*&X$O!emOvon1p%P_fUn7}eo>Vdzy=AD?&x*3{<_+fb2EtVN@RKE5u%EEwK
+x-qrt2S!^O;`rd>jf@AUz<{H;jw-4ig<Z1K|uP49Zyatlgw(-8_Mm7BahF@~~g*L$`AFB*i7RN2vXoA
+d^%N)Klh6AZU{U5}OIhFfi&MH^^95YAe9@Vu2R+V0}%SxR1Wp4GInBCxZwT^$<*(?Kcw}MuK4Y_%43R
+g-#6=y>Ot^!W;QK2Mw-=n+TP;wIRQ6zL~r{e)k@M{qgwq^S6JsU*Em??(K^s`}M`2j$Zuv&3EtZ4=<0
+tKl=8~iF`Lb_fCXW^%8P`UBK3aQ_0@~61-98mpD%P>CvL9t~V=;43VK7zslDc23VXp=GQ+qTSEW%HQ>
+xNHtTT$_;A0$L@zJBugleXt6}8$gW|Ic<@IK<(5(Q9{`!*lz5eNc7ps#12u~9jEHa#2c%b}b+1KO>Ft
+P}qa`Xv#=^fMh2wcd$X2cl7+f6KlqJ<pSAMGEH&Y|&bR?h?3swx-e5Q39unH98up5J7}B7@!V(r1x-X
+GRANn8?5~87H+qKW~UFB<G~;gLpH~(H;cExTWKg-%oM`&=^UC^%W3FBj$6XPQb4qeA7TC6ExxjN+y$s
+_!7O6qKG}T`f|YC2Eo_^_v&*D9)i<tzC9r0$6^73lQr20jK-NLkQwF8+BFhE@0}IDK%_=zK5W&wd(FZ
+2HZ1N94=TY@TweKU@s9~c%q&>D?ERf4kH^~2D22H$0C>U=0WLi=lDmNUPoVx)UQ753t^q>Xg?~ix>Ep
+Am*8%>WZ$D8U6V^d?F1VR#irhI{4<0<AfHn+9ac~g76O<N!AB*qCOm%XueK=;h@pF8k8?gXe9uQj?nK
+5KqqDR22Z=gxg!g;~{Zn8x{a%r?%aZaeN<DM+&%>Isu!=N35#N=TzPyj~55O<wp8a(iKNq58Gd$?c1{
+UvUO88QSkn1WVPlVuEc>6(0fY}gakNJMORqVxMswd~w1JGMfIfA{QR(g%gq?FNInpZhyV*)+P7q@SKy
+e8vQ*Zr1Z$@ewVTf_BX6)CImDo$H{7ah83zzxz8N1lCrjNn|>TC$#WWHz@G`+#CKgyL~6G;9l;{F?PV
+1SOe{0xY7J6MfX}CsV!TfF@QvqemE999a#hQp$&h1RyM(@>^=mgE4PWYWZq3|-ib4>@}}&sjr~69ktC
+a3<Qc~8^^5`Ivbf1h3PnaDw9?W9gglT)i`<x8PyRZ2_58oyy!B%xPY<7+?HPd+9zEMr5+{6rx~D`=`2
+J+ilR4p+$9r1ngdYx`u=Ok^F%8t;6xXN6y48j?em9whZKfRce4X33*+Mf_kf*#>-cMw#LQe;LL60*p3
+*a!bamCZ=vYKxeIR@VFo0I93MLgVyk)e>^$=Y?0J&sJob(TC}Xb%#$R*sa(f&#;))0W^oozh&0&&pUl
+mL6H%Q#DC6K8J&JmQ3?xz>we6AiT6pnJ})_sANnW?>?7-Ar95yU`FW2kf@$UT2^ZB8f7I&Q6%>Usrn)
+}kA5~p{)K=%Ta}EFNu9qmr|PvcF)oq`>d*N$FhEU2WQR?z`}#!iyfSRP%hq<>z^EL70oy`*oM)9Mon!
+)P(Q1v6GVL`V3Fx6fG`4YVdSkoC`1DM0Ev%M*b~81y)7V-qF{l(l3@m2x14=NQ^Qfy3HN47ZCzW<!7Z
+(oHIO+F8vmxwev>QHpM{4w=Z;SIftGAC{RCWF?uWt%aNadMG2r0eO_rO3W61bbPooYah;&BL9Swl!S(
+e0$eW%CmP+YymKZCOe!*Mt*(H??m{kI}cG)BXwB*4HwLKAMF+pxs`3y%Oq$(-7pDbBOX{8gIVQayUMx
+pZ147RZ(L)?KQDv_3UaOA;G>2=I{LzjPp2ofe}Tk)eKKq`eg#Ej|nBsm3cxjMh40#G2k5U32oPRc~Hd
+2L>^!H^*{n<XPyw%FgE`3m%k)0*ucNoGBn}$4Te&Kpf#QDbh=<>4k}{>yB*MX-Ef@o70^5%G2a&D<|7
+>i#w_h~XO9EfM24s7Z;^j}IewNtwNEKB1egb^t;MCzo3QyB*sLo8+k)`t6os?Lgl*jjfdfI}(_?-Bnk
+q$U!9y>kN6i0v8?{^oj-Gv&fY(SU1scJ|-r)HHZ0C!!)P#`rlHMrkO?uHNm7eiJ%+t^32cI`RbVLAQn
+w<c&J{%?bhbQnuYXe4i!YBAC5Q9J{=v6^ko&8(rr2<tI35BtlumMqgK@_i!95sNozN)Lu<y9itT4KWv
+EOkC>jXFcxE}<Z*(clh)8;2iK`sZMfJ{SV3dB8j0<|f|T0g!##lYwz-PZMC&ttFn<nAvwA2jSdV4jdn
+Ok)aJ}o?BVPNNe+{^T)y3BqXQ7V*|oH{%TK5IxL~B>rIYpn-{*{uRvacXEw47>CBFie4GF|TcnOLy(V
+VjoA=108nP7$(J70MTVOAF<FglxdXqWfAjpm91S9V^({4OAPc>u8vt~;{K%o#ui8*4SwBB1{yXfi3jD
+G}R;?o_DN4q{|;QWdk1P<^H7-kM$02(oMlDhG2v9o>^>XQOJ+^FVQFhm)y3C9<S2O7z8Em4U992NWYI
+Q{oDc@BFycrUx4{1ZCW$gzkUJ^+KESSIp}ccbK9X3mD9@%57V$T*4Y&lb+~%R)H(ep>*UJ5G(f(lr&Q
+?UP|!+@bNh_j;c<sqkN3Y5VFZ@qTz3fd#{xE7|e4v=zJ#zJwm0JWikWkhVW`N>Ccg?>eD;(TmGqdJp&
+ug;}fxsp7IsFbWAa<fVumPLhn~pk$nDw{=p<GW%QQ!Wd+{bR42G=TWs#v_&h%+XtAA<HmpSS^LFj@!0
+;>@I{=m+y;09kDo((5WB<)4Tl0w<jug4mPZT8m-Kr`BKH|lgk>SG4VkHP8v2aP1YulO3Fd0;wL4-PhV
+}XnP}G_rj8VeBkaxqKK;KsA&+#pMQ~T1CoogG45=o{C!c@Q+HeWWPS7P+j1=?m|anp;5goAM#$`6F5r
++wpO>7U8lUg(wI0$+hYwPDvy3)Q_;%~uU^1aK&4H14a~Gaf3G5-b2G{A-j@e{HLSjn47V$A%LQz|rOd
+N}nUX7dJZ~kdc>8t4M0js;KtdkeixE)(I_cy%1oYdBuZ+$59KGHuGsyNLIj+@E*p?uDjk56&%Qo&)pj
+Y$sQUI2+FQ!A!Yo5*CC8Uk&)1r8znv^%c4w~I(2wVb&*Wz6}D3mc~40Qp`j~1#Vm{rBu)ltYgBqXj(}
+{ij2`uv>?UnRGWSLdFY!=$*j8e@*ubI94WY^XM(YoS0Ja8+0X+A>Ee^qjglIySfj(sH(|C=z`KhC%vG
+^DjYjn$vuX0AOvSzZB#{)HZnZU~`=aWDro_^!+S*gCE?S%sDE9)dNDBID^`_}6!kPGEMxBsIcIW!~!_
+^%WWz5_IwsxVOl)VK3RR$kLz!0?Fk5LkN(eVb=<Qufd}Y;r$tuZlGX0^p&E<6NzvGnWjqAR`IB!_Wkn
+6bad%PH`ASq(u#5aRZrMW$3quV-1CgYFVt;7-Fw+VCPR!Tao!6H<6_QeA*C}uaac2D6X-ZhB3A=tl@M
+zjQ1shWRc#!6=d3uxHgH<hpWD#dNjaV-F2Q#Cho%T`siam+pKdnTYk%Qj)A?4BA>hW6O3pE8qVsiPPc
+B^!w0SHquhn|)FLCk914vIKExUVLEV+6MZ>Mgy}^=W%|`a6NgLJnC?&<!E7SsXMXbepd+KDVslcCd?C
+g0SxF;mkjX6+!s2q3rX#+*0D#o(VU=N4_G$Z`ijux}|KWVgLk`(@C5@*CpOP-`(dBn(w4aC8Y5r!N&!
+h+4)J`46EJa={BF%8Eoe+knFqfd8rYq*DBaOu;yhiqaO!|BQ-m@TVn;*diSqf>oBzCp7QnMrXk4M-ks
+ie<6LYC13cDNZIBSY%U_@h~p(M$0SG^9$Gkz@fzVMWGjTN|+;vdXv|affxhFW*kEuvO->G)6~o9v`@A
+Z%#3l#F81L$J&8P+iOWYIYIV+ZB}OM-V{_v$WA&4`Snh&ll!pG>991wE!EiwTH31H^m|WM%p_?s;%M2
+aHgW00-k!=4ysj;>e9|+u7V?G`Ubc>UiEwtIXW$2d5Qm6pAx>6Gp3)@U4p(#0P&d1L_)gB{F3J#7-(E
+TU~fRqTL2~0!K5jinE`L@w&yG=08y%$M<06ccj>b5a46azh%wq4v0=!vTCfWTB%36bT62?x+a{oc%G*
+UGnHLWg8{V#PL24u8aVVIQ}4U#2ldR)Z82FDgmYSb~m|%YH(H&1t38W^D}kjfq^;d_=u<zD>xzV(P!(
+`9M)_iPz9l<LQ|WC;Jh9ZU<lwECM`r6&usyHr3(=50)PRS1RItpiFcW8;XQiurArOXax4DD7@O#HJ=l
+yE{lc&$PGU*UN3+89!D@w{f>m;Y+0Q~sz{@(!wo9JJY<0=pQ&1H$(VrxS!+P%httC|#+CHdMVm;9u1m
+-CgQ&*mh!y7Pn7W5uPp1YpIup?C&)UScefkutcEEJnPLtrNj>sSehacq{H=F>5jRU8tfi$aXAcR%L9g
+Ek=9y{BS347fV!+=SGz5)HyL+rx>$`-d794_pN=5GDT<rt-eh7}IYl<lyB-fiqGeg-C=`#hgzXd$0Y&
+2qq`W|^;Gk{K1F_JNsITt~qfW2<tO+X;B1F`-i)Y$qWY+aS-$F+6nZc<-dcC>ID$+!Q#A@By<(djIN^
++qm+DZ1oN?+whK+j~}ICk&uRD;Pm3z1gDLaS(KGdX7^s)VPf+Ub8tjfqS2y}Q}V(!W^{-gOC1zAfUl=
+m$ptO<Y_l?BqgWstF6<BKYozPU@0|P~NXwdsqq9$m<CN*=3UqbN!H*%A*|7gMm{5aMji!SNcQ1Wg=D8
+$p8vA~Ds9^0cn}SKZ8b`?~m?OmWeaC3>?<rW+o#-SvaIsMwjP-g>^+-lQ&%p`Kyn_sS^E8UHviek<84
+H$^AR9rQ?n6_G7*<0{0oX(+S`_7-5#%<~k8)bTOQD>Ft^z!zh-;vo%okh8ni)o0^B$^o9GGWjH6+{0+
+Y|a#mbi#6z;-Fm*h+eeoSMe$YUz6#=$NoTVz3pFe8iOxkk>9WT+*Mc6_*4a<(l-*9bvOdM3Smew}>E2
+Q9(aXV@GAdoL6Yel1?ailE;Rp2n`FUV<19>GxyhMN{Q2hW`T&#E`@_wNz_oQ+oNF~kB+GJV!F#$N<~T
+Ol=0H-^@od0<STzv3)@eJs-s@z$EVo*EN=7CITKCH2_-m-Iikd%!tXj?)Gj|1e(vLoqIRABD2C$sr1+
+;UzZ8CS1h<JLXzun;8KiX;`LesrCvqwURh0+x4M&$-jvAwXx8YD#<FzBYH7BBcDW&`jB}rhRhQ6=O-{
+vZ;faEG8ech>(HhEt9$~vF8AX3WahzrLgd_g1Oih-2UCd_>rN>)>LLY7eYAXl?&HMP~be8m_`R^;1AX
+Qw!jvuM~RzF<d~N5T~h3aGN%_{om#s1A>y3>=C?PS6p-Xv|y_#Gt%hk5S$knNntB7MWV&ya+>5DciD0
+&{V5gp(=l~yzDb;D?=xn(#iuPlpU<nC=>yLCT1ZZwX*EW-8$-c1MKBvA?hmyoxq~4$;+U+@u)0E5BY^
+AI7MIUe&fr_0~XwGGm3%;g!f^pG!rWw(&5lW*i4IksjXlo7+dv$)=APIOmCb9;j=ZP1|ZTm<pM42Q?q
+2afKyL^p&n^SBhs;)dCR0K=sZ9W^f%{J(I`i2Vxnvh_%Rh{XO#ZbnMcOd<Wn)7QT$+1Nxq8U+9-}KTi
+dqV(zZ3no*@$3LhOBa>LXem!4^+<zvOl1y=)WbxCo*V#8S-#ctf^=C@)JC#jXli9uW(wn0aeYk*V9=r
+js*4MJOJoimSAl9nDWPFRO~&1>$KN#?x*foLxk;H!1U5Z89-NaWuwwXFM5kkRWaxXQE`gn!g9wA(T%{
+9xQCM7}Fw)ItqmT5s2Z=yiwt}#iA!5+%G^KBgJ8=$D_R(-tW$}0h0v<0F-ZHRZ|tSfZU6PS~eowOh+2
+c#v?aU6;jAiqO4P>ok%bR-YMYnFDZ}A0tmQhd&Zebq_&qtlui@lT_qA{$ZYia470F{G97tyXfcYSys5
+4YgCRe#)(FX4ILk_$#yOd0(I}{_8ST_(J-FZFj0I~pFLpZB@gazOSDw{qWQuox=QB``l!%#*STREHdz
+e|8n=Es{fg5sQttRQ8lN}`rmqa3(y17AvEm8oHL`oRBvrr@MYt*o9?Z4!W(M1UNNGonR$oX!3yoD_h?
+g&oS1IVK~w-zPSeNZJxFBNh%sbHd@1;U!LIO2J?S@A)ICGNE4*5PJM7XE?$ZFqWkc<|&b098>mAbOL@
+7Z-5`xW}H9xKFax!IwUVJkLqdR_)f6DbSJ!ejkhqUagS$s|%lcD>F2Im-z|9#rpRO)TYTVHVcvfI9n3
+oQhEIB98KpoqdyH1M!Dc)eUn8ho1IFM!AR8k$<;J9p2DL?(B+CCpnQIgyKQ}SKp_|br~Q$l;T=FKwfV
+TEVPZq}<nU_5B<y28_Ga4|OqHX#=deGp@RlA<x4^A`6q^aIXxvyL12YDbxazx~kX;SQm1x7`fkgXr+D
+{_Yfri}zabM$2$CwZggTR0zwMkl>sY4&XHF8h%Dalkf-6*&>uJi{Xjy*?;7VP2~WAt8U>0tBO$D0M5O
+DK0;@D(|HAmIkYqYkgGxC0zA=8H{D0kV<884A630MfMaiOA7nW9&mKsJ!-F?G@3V3W1>%2py*Q+r%a$
+i~yvPp*mCe?ziGLzufTxWJ`w5byjEeb+Rx<D-qD!Dt$eCRk3F+Z^n0dmjx6*`^lSX)o|5^9^$H(G*xn
+uwP+$!^<mn3T^?a{&5u4Za6QgiFWhBZO}$DX9{Ay2$htR;tKRD!r<J+z%0F`P18pb4(MtlmF*FgMqFn
+pd39MG0IX@Th8-yTR1GV|=(ZP!NeNN(1%Y+gD$Mp>RYcH-|hM@Hsd4zD1qIXXuI8?uHPc2$Rb7_w{zD
+3G3p(>)W5eEuoBU4n`5w7pwnXDF@OW2sA{_wX6?BEfF=}zva0h#>dao`8Djv^5Mq<@n8TbAyr>sV<yM
+;|jxzT+B|hlf6vJQyP>n1nv#Ae-IJu(eK_B})JEqHgCH4+Db2Ls)=5TomU`)RB!Jw1Dg3z>6b)+#zUq
+2*>PJW)n~a25I+Ske)$NDOEP_n~4v7<EX~x{WLn!g2WkVfsrXGB3NeRyU{+dev|aLR~H@D(`t|T6)pN
+aNJC+P`nGex7MvL>5!cM*A0Ig7(i?TkhCkb*IIWzKz;-UVq%(>S1Jd2BD>%pS(n7$5SozRG<<3XIqUV
+@oYZU=uZLumxVv;6BXuVwtxMiMk=?Zkw*IN}<r_OZGQ*Jcc2KCi^ZhbRuqRL3%D`Xf(2{H?&kk|*-4u
+`H&idi_{+WJG>xv`HpJDgcPd3BmxEHYFd=48Qduxu@fH@CU0q|-{*SmcFdB>{`Y#9cjEDGDNF#C}++I
+NPkf;=8o|R$&)V^HlEvn6RvaE&Gu?J)m0ahsj{-;cSYwTHHnywm%)>YH1%q&{|5~3-{%4NZ+Y}YIexg
+3^1w4Tj#Waky1xM!j+3YGSzN7b{!vBN@KLBT+=0(T6ZB+kDC||+(lX=gIZ}xb5oizC3Y)&gQ!Rpb_NH
+-JiPkz4sp~s51k77))>O_U9%zLcgfgfx$-S0MkEU@ClQCaF-gU30I~$utt7g>TN%fK_i0yIi?|JVrOQ
+Fi^EGLSb9K*706T4@?vOWqzIM*j@_nRoi6HX~h~6-%F0=FfbscQ`aI8*sH?)@K!*Ffi5A|D)O<ra5>Q
+?S*^GgnTS^|oPZ0xKywF_7+6#(0o)&q}9_l!?UzEL2!gFuN*T;Ri7g2T2K%y_QdbWSypv)~o{ur$q<Z
+3+ap^4nl|)^6U4-*j+jV{$aEo16;&BNMZy8MOdOtY=WS3i<U-yjQ$-54ZvEy;gjM1#4-O=80nFO|&5r
+q;UFfm5hTgnOlEc`%GP|W{C05_dBl`qM`28()Xzf7h3r$Q_IAJzFg<|D!Ht7>C$Lxx{e^dv&&9uwTm4
+|v&N5qN=GkF&>*^&c0<W5BMbz>lYmS?cn2khe4;r^pQIWaQFy`ZL%u|F?T}af>4cn51?Pk^I?<eVzes
+H<(mx(}nw+0gq^@i72kPQ-e6I+FO^VhtzmM;PvrNb>I#^XrQ=Ef9SyDXJ(0W$1Q5<ir9!W&m)}*Bb&W
+>{K*2HLreA9J^Ee@t+Nlm$N+A!@EFTDQBux_VIv8S8G1<9L10Q&EVG2=x~H(41XGpn_kezw?<%hp7YQ
+aCCJFUB?OdMsr7-5Eq7BOjYs6Xu5TAZkYp!9ifyIs@AgdfBE*k)SLII$tvrp^=G$JRI^_B-5D_dy!IF
+-mL*+jbg8{P!^FC5YaI3PFu;_IA^U(JQm0APGGO8u|nA)M{(2++A{4!XQ*<MMuSli>U{^^*@wPbBwoj
+H&|Xdxq;+^UYWc_cew2}C^p@Y+fb3`ilzr+~nDmDS)IYhNoKJKzgAEzL{znP-IZAZWj=Z!Nn*|w3&3x
+J|Y_^b^o!`o)4SBQeAQJS~=t-NrEG<KZiCOP*EXhQgKvQNb(BmR5riya&s3o}*cV%L6Gb+n21|?G-V%
+&Ari@$h$8}rzd()2gyJ2S>EPjfg_#}uBoBL1zLbgBkDby4^(j>qE9F*^SpWyuI!^Gc_u7&SIf#7z=@Y
+gW{?o^Mb#m5Akvw6knsPPE{FBH39E7O&(PemHe@cilI8W~xheP<i#E0`3Z}GG!-h@k5*mEwaqR8KHvt
+zy$1Jn>Xa&RI;YjejDWJWeTY4KTDtWr_-VHf9@EIU#%QJcxm1eEpqNM9Mpak&IfSBKf)MK8T^nFFxbb
+BoPJwvEV$dEx#B9StK<P}$_Js0gf@-8A$kE;)55T)U7K=ewmcRLLMCtLq4tQ+9Vm|hN6}6_R7JvC<iQ
+QNld<ec{~_wo1GNbhYlSBhKm4zi>gstsC8CY1W^=Z3C6%4j-uucoeflX0h)I$UJ!<E>9X(X3BS?7%T(
+W@NE$(+sWu(PYNaZ59evKuxyCUt)ai_7W9I>Q;uD0f$4)e%ki|uCPu>v;?>Js6^qXpg=h<qLSgy2O>;
+bohhEBKc0;*^E6)>4JCPC|5nWT|u#$PGeRvBM-d4A6?>^Wm166nzRvP(^!D?>-v-hn)l%gxa#wqaP8L
+j#JCD&SE#gBYy)Rjtev?V*!72IRjtN<MPj<RE)NJH`Tb+N;l|AO?F|_wr!B@yE_*P-;cy3#I%9<h1Q-
+=E?$X%jnN0|>HuvV&DwXud2#Ji4NiHrzQZssEQE#eWOy$erStf$)xF3R(!8OC%vhOo_DAb^32ECHgIx
+<c^!aL3Q4e+M3Ee78caw7YlY*kh)iD_62bs=94Ap}-R}_a_&fD!a_e=xg<UG4TyN(JK0S@GMiw+rcx=
+;qDNy)-W4s@m(ef1n8_=%XiX_<s)21Y%*U|OsTVea@0NDZIch+jL+Fm%_>QokBR3=pJ9tw8*_JM6$UA
+!7`5G{)-yvUOFbSd1AEjyd{$n8MX;x$0ASK%5JQYb(5~*bN&LJJNpD&>0-@_<E->#WHzT8cu@|9*ifw
+f<D#fxj4}pak^1TB1R)vXOzt+)etx+j&de5#YKy@_gN>za$;R-5@Av5qksv=(PQohihTrG*FX=V7mYW
+;_2V0BtQ*yd8s1dWAsq)<eE3S#pm=?PD0OZF_m^^|MM7(pX6vLIu7z*2DZ_YT^l(YuBpq0Gh*3^~7@*
+^m^MX5~MMs<@zSehATX0ATM(|hTq>KrUBW0VqGp9Q;2}4ltl>*tSjPGS2M)Dt{B^bPz&(JPo1E*REL#
+9<UfG7r9jPO-5uWrea_ec;C<-~D>IW8d2TVe6LH~$+B=k3Z2*0$HFsAxAuW=E`ybv~p@h!FwhjUG=73
+R0v`)^y!g82dnGkdc?Q!@8I38MX24oV8JsDwmtw7oVO@%#8T`y^6oDO}T0zK|&~ShSc|4lOg!3s;<TN
+i`TW{Mmg8_CE^Fqa&fG)=a|X?RAvdeB6h%#v9vDvb@1uA4%0G;Pij6{eYqjoww+)jVFY(=fjIg;A#D(
+cv?&Xan*;x>u-T5Vk1&l@C&Sn?ntU(A*8Zd2+1{D>;T$)3=FI@>P#95?79untA4qD>++1>d5iSgs<A~
+}?eqBrg-&xEOKUV7HkvRUoh(b~uW7rnD?IBk3#NtW2hG9_?<7ACs&l{dE$h7@#t-_oyGCJu%{`<T}V%
+7M<BFH?UVO0Fo%XP<UQN@tbI}XF76p4n2uNMXyMqK2|>?u}IHf#DJt6Y}l#*peLhIQ=VAqCrpGleI6d
++{RhrfO{bE<-=Az}boYn@k69H|4rm=HX>sa%rKB^hnIDvBZB&9Wh=4G}K&F$kFKOn#YiHnu^=YWI!;a
+>Rg&O07ZQFQsT`s1KGM|@=)?2sI)==g@I6x?Zk`WLpXGBxVS7%nGn$KT3iNE)x~8|W{U|S!UGeDnUS<
+3in?-;$Z-erH)!3{mrJUGbr$dZJvJz|a!GM?Lz}@>zOdwc>AKq(%51N;glI*8qdC<^)Nw0bZ8^CeMl@
+>feNxpEPlUKnNxh)r9l`x7Vm<Q;0LMEVraKDybr$9O#Z%}g*;hJ;pex?@OD7bJH8^zD(Cm}gL~WJJUp
+&{lOJ6$0#(?>ybB-72Y#n=}Pe6dw&iv=6zq_F3zX%61`hOM!Sda6c#Xx(8VXfd_W41Wbe`>a-M&&P_E
+@knC)kh<{-Niuvvm4wD`O~(z;`4MGMI`HQ3clQsk8Kd9G0fh`dqB#|Mjzow*Mr7Y+r&?`m;=9`iA5Lw
+;=0$vQ<b;j&Q+V)9vnf?Ve8WPAdC_vlRygLi)Z?4Ge$E&k_j&_x!=7#U(yI96i!eCtywCtML^-wic<}
+s?#UHyFt*Pd2JRk8nGPo`pz+FHjKw)(_SKWTzRFe&ItOd?a8(-&#W58pDJ7T69;)iaB(><WujrecW#!
+)NDl0D~g*O<&Id!}?R+E#zzW>vk*Au*S^2PHPe>(E~9yeKC5DUXf$y5f3C0rgs!OE2YPWJ%+ByKYajM
+O?06+NmoOpPQKWC!9Sk4Pet7`E}HE4;RfyLP*aR0VkIR~~lYWY?p20Z3n$upSSFalU>|QI{C2%_SF5B
+xf9xiC=?K#g-b+hKae>a>0TP@4rwcF7n~x)NNf0$vxo8A4n8Vtx%?Hk|xk=?vKKGn$|{sHD{&1U6{~X
+&K4Dyi3IW;u&n~^o)TFfMhm$1KNTwk`k4ySaB)vqfv%SV$X-H8T9Pp?s2S<ol)PjQC2Tqe8DWM7vBFE
+xlHLX{jqmYzE-(9ymUzuFb)*7FUzF5h>xXdem#}Sjv8fHR)BZEbRwq%e)hQb`2{`4Bk$r+9<_bJ2JV5
+gLNJ`e>mur?9FY^+u_NF*G^W#ePAJ%dzH>(8i$*tq2OM(1LIIaP)&T2ZM7=kG(O&9!X=>AT6*gse=SN
+&1)fX2O}Xz%2Fi#MlDxIXU0?uvoxH>`CVF>bU@ICW&O-VKUxIoXs=c7bY%hXQXR;fq?$IaPx?qf2-#y
+OGp7_gu>5V(?fo3+v_X8Vn60UMlxk0zl<9Iz@KI*;?Je^tpBm(x1siCQTCyin#6W2({V3MhDV+1WQK2
+In^ph$Ki^@hcFatQ<{6Z@CaCADko~um72e)G-F1E`ACI{R3WcOwgDv6#ZOp}Fx#SSHcfuW<Pr|he4Ub
+vew2jnX}055Ot%&D^e$H^ym_+!?6<$6u~H25JcnJnXga;sK+#+^ZJhMWb>H2!h-9wrTCA}AES-<1{ae
+g$s{3bwb!;CCK>YyED$H<R<u%pal|WkA%b4r}3<k!^4j<q*vYY`ZE|{7xS2|aYlYU@yC=Sr^d~Py?5P
+?{Yt_tDs2II0FfNi{_EMk?lKP~a{KHBI=x=f8J`T*f~jW=rd|6m%~I(;`e{^8C0KP7~7|L1Ssp7=;wH
+kWiI`{z_$R?ma#13seJNT~E?fhZFxgvl;)J$sMBaU#EyHHGLesw}3-3s@VvSUF=_5mJkQvebuFEf^-c
+bHmMRZncR(*d=WxM%o&tC`b7`4?nVSO^SaE0EHur9H@WbpOvH!hr?P@hDE?Z5@O-T%-d%!AbUy_)HV|
+8r{GDsk*NU~x@L=pGv(+;z^k0DT&``oZhNrLt1|z6cqthE+IKrZZd%{2uWZC(w&2rK0L?44|KGL`Y-u
+**C<%Ayzj^)bU*%Fj$)3O@CslDsn<>#uTATVV+yw{gZ#kCi^_%zH1M<w$wo%R)lc#5-G;!}b%ZQHLw1
+_n&3{4>70H0KNPxH`|zz~c$P|ezUm0hjG=Bn<%AXzuXRDrE%3FNg=HzkVuD>&Smj-<MPhXTbEANwI=e
+1#Xqudh7QBwh>Sd5G~N>}N1A49H-`GNEDekK}Rno8SCq2z=&o^;`IO5a7kx6*L?;%BR;<@K}4}S{p!k
+_P&#fy(BubU`|AC7u#aioqOww#9B@*P%C^);h#Xe#KIkI8h4|3;&3Or;lVN@?&x?O`4K-jnlO0POCO<
+-vWRduMK}H62bAzNz2F>eNvP>G<XF>blxK~Y9pcw&qlzE3c!|jz!=N@ive^jFbH}iyYPPXg>)P|%!ZY
+Xiu)Xi)wpWFJ^o;6b)8;=R%6OWam5!L8_&DwvGssG_In?Da=8hpko{0n|0Sh7H0p<3+*{@E^<6EsMo&
+jlq_y<wIbSiSZJ)jzjT2jK*JYVE;fs!v=H5`eGER`9PtqXm*$?6Og4aTzLF&&-R%v(8YIp~rr7W+Laa
+vr>{*7+e%N7O`Rd<cb8t}w~Swf-Nw>%|&?rQ+4rqH}<PwCpIXj~%<^d%0#hs-a_s9TnnTgqIWYV=hN`
+>2$ktgjk+r>cO-}vQAD-mwEUSLa21K9Wzd)py27&9GQOslH)~E?cXn%mi(2qOj!m?F<W-`jD)H1p}5d
+dCh8#a-r8D)m+DBEDiv6(K%KfmW-9E41c{4H$q4i<U{om-2!syB8CE?7@|%eYQ@Of(7MK&b10Da?L={
+C8sLX9%xBu*{K>u89uqY$#_XK64MQ&=ev_m#@+^%0cf=BZEfEN^NomxQNMJZRqb?q}<2m-YJj|E1qSb
+TAMflVurD`$+EfC3D5O^(E6@;1A}r|Wu^zx?Gd>RwU*bg|yZmq2M4b)0)zD_hKLjnqB6?m9T_6HXu6!
+xCz(>JU~+6)S8M51^pOM%sg;%gd&x4~ng}i1nVYxC%8Q_GPxtddl8Om3}Fqoy1rc<)|MleDZ%ENxc0*
+j$QbdhkFJPIjps68BSZucT3{;pYv^ST@hV7Pv4vz_SqOU*;qHE)B=gYUPXOz(D%pM7e2({0&5fac+Sh
+U$n9DF88ypRs|j5u2x4D|uD)$?RW11{EJZ#P1j?B_+URF`$u8W{Fc|copPamW{{DG?G?a2)BmMNdcSm
+ne-oE+z_}e3DVMgV*`qP_NM=#$Ve-CZw?~~{6|J2{vnp*4z7#q=k@*lr>^dG-_^y)~r{^9ub%j35(a8
+g4+0+LXJCyJz669#*SdbdiA_kQ30sDhc=MRlc=ccIBF7Ef&8dKNzUd8A1cqo9Jy643;C&L{6~Lsfoos
+-Hd@e$O97K;-@leYNNN{GLA`KIU~Wi+|AfPJoLxxyXB+z7vl%Iesa((~`N5+|>SEv@bXkpKNROd9fzR
+QPSJ^oH4a}ypSXE_7M!sO{q7W=Z_WisYpo)(sW(S9;NQOQWliORyQ|A<>VCWAM|}zB^a&q6!1OhpZ=f
+c2md=e`04S%f23y*`y+h8-Dhj(XCmU|UpGto_x@8gq0{`Zn}_bhj6rl?6hHAX(@x=Hm-O0rSJ41wQsT
+Z320PTuw%z==1ih-5%Uf;?_P%?YYbQ7!^b_DWX}=YKwi~HtI2x~XD6^&dmrzch-t6C#XzoiglRh&sbG
+eg*X;XYV1)kbmlKCgn)&#4W(PV832_s`&xoD4$ZDd0fD_3`f_{q8=ca(YmAa7>biiMt~LkcWGIDhB4^
+)Ug%jLwV9i<SmpNui;;_4!p+U+47^>gaS}`U%&3!3joGS)%Dz4e^csVY0}U=kx5)9QE$p>4OwvQ}|Rv
+O0P1he6BU0`V%VgBRGPWZ2GM{mM87^+$*M^j#mf6)5B+H8i2Hm^&08Uv|eTyG@k3$@>6?o1r`dlri93
+}{00o*=N<PO7R{vKtq<eCwd0Eou`p(bn)N+4#=_0mhJ#;Dj!u#<p8mTh#WJ+t8?w>$w6u;Yo!g{RclS
+?RK5u#1@Z!gu=`A-?pwTPCC-J;@TR_T3TU96?z>Ux>)1BB65Aqh9;}lp%Y}6AL1@p1%s>pB1dXRael&
+d0nQb}$%)hIX@{4h)ja8c7cNF-lXYS)>%Io4fn?g>wTN1khkE4|9@ueQ<4i>Hw~u9hkLV{*glP`j`;i
+N_c4)8CXJ(C4JbAHDfn!dS3?3h-#4{k|cdhLG0x3n}zD8sRE$*n(eT7FdFg{d~Uj<)1a>q!vkIKPefX
+A<oJw_JmzQI4jBkl%GcCS&N!5zN34S0!}Au&?|Uvmeo98_?d$H$*b?)y(g<Zs2uhBU{=(#4eH#AXsAq
+<o-^Hbh=z-6x<39hw=YrW^Yj}0;NCzFf4zc156mb|i$G+i6jw^<L95Z=$tVE?+UB4oJaz5QLOhVdUsV
+gdJaoRnxP6=32RIn<$adY-`~P<|RrP(a1WlKnSi+1jJM1TCr;lCtSQ-gkA?xZoFVk;Np1*A=KK9M0_@
+-<8w$+&CL&}L_dV}7PTuLjOH*b1TZKwmyPZgSZ6ZXCZ1aBzhFVEH+@iN;&EPQcVcy2W+n6WiP+Q8k&b
+vezQC<Z)vtDStAd=c{{_g-9Ol&tPxN@x^Hj(3lJ$M`L|0%X15uxvW2v0BkJ2+NiJwcts5>-+lotD_b%
+=t<(zVDo3uJGrPMj8mxA{dNO4ls+d9_ghW6OWDbbY_xPQwj~Lk>w;MJq*@K&hn5u4HdEn;R|}-#nqEp
+CCcVUL$zOZH4E^H(mFzQ6TNb!S{L!<s;V#NG^G2$LX}CF7tM=*KY?tTNB1MLKNZsLIqgWGjd~q}LE#4
+FulQe_BqD|aZ;D)M%fPelAr5HIr`8Z0phsj5ie!dNw$C^f(jmOZ;vfKQ};PGeP<FJ5u{}1rgJzfMz=z
+r-wz6>A#!2`Ysfq&^ezRY7H4Q#cmtiepA0pk-CQaCekLHa?=4=nnr;9Odq$^O8<Lwdy4)#_{qb%j#8R
+Ww8$j-=VzzeS_WcPblb7=Iyg%1_fT9y(FerZ1esp_x<67)!zuFD&mH9lu>HMS%);Cq)eL`zUC#=nw{G
+`f3^aBiI?xr*eRGAN}|nayV0Ls^pp4w^S7er9S-^PZgziEdM~>L^#*5*1_DCxK8>REUm!DsaXX5JHBa
+w?FhAup13@4w784Dm98r)J3V-Uaq+v@j1a)0IG<P+b(hw8KLXfLD31mI|0w~)W<eU=s#v8H(Dvr|D*C
+L}3L{EZwZAz!Xp&d?IwQx#lk{maKvX>Y;y1(Cp^_?4??K3H7MXb3jTM(%`xfBS^f3bj+^QRLxn8mxpt
+%QWqPiUTDV<`J$NN!YImdWc1ea66+AG$Sl(KECRoVGe+M6p^@Fpbhil5d&F01H`+7YgB99<Dx&i2=t7
+|3Jxf!3j%r&)d3fD9C|fMWmFb!GRs96WNf?Z8t^zDA!Lm79b$!6<+OJlDvfJ&6!PX1t;S15pr#huADr
+wWK~H_7G)v<Sv_$#buX#%bL0;;*Q5p4{cLl!{}bgP$IK0X{_&*%pwZL3nxp%QZK4YbX=o%8Ux}Gt;Xo
+kYptvr!Y&5qbG2U&Ki61tMm<uRnYkW0@)o+u;KlK{OHhv%_O_^JqpjQ;ty}EGdk5~nD>?R=hWmMqA))
+MM@!qq@9(GOQ-TSx4ufGYR<vZptuTu~Y%x6MA2a^sMDWugNc2uqCQ8hAPUWM`GG<S*l(P=iPPrM6&{u
+{K`c;vpu%M3(=yXw_qo72N5XE>H#|3k0m$rJeDSio|wgU$PJ9`=X#YTE1f`@MG4{)Z2im(KJ<n4WLT2
+0VriiL_<#-fOeN)=NPX<L!98Su9NKob}bb=i)S$RjZ&cl+>2yyrrly>)E1{{P9z|ECUzPgrVk_my^)^
+h`-5g3-&jDZ+DveNDhfG-QB(mT$VHgBaHSZ6Mi;9pOs&`f!^eXVFr!(rUwylXiX(x{r*set;RE+Wy~B
+v*PX5E)m^)g%Z}B^!Z$m*^-`qo+i6jyf82sPbo_?9jV1DSz}hhmm#F+{lSfnYz0sih_o@a&QjNR%gZF
+-Me`X&@e>=Ld<j!e7@s3R+fi$$z7di>W`NmNGFir-~MyfBf0=q{f;D`(B4m)dB^?Ffm-?31w@zX~4Y+
+C;##|i4d49?s^Z?nEQ_+2j`YBSUcdP<s|OHH+gRJ+uhYDhuR5*GKOS@*)HQmvhg?(@^=Hr9EZa(86&-
+)oxsm0vtjzu%xc6JQ)CuzL(9O7iMmb1g8TVpC4=@`Bacl*XkmI=6WRTBJVNr~w*&`LE_t|Dk$GPs8pN
+pD$74VN9I}E~y#xURS0+PC;SGR&_TF#x)`5xumW7o0_|Idhv()Lx~3_{I?T|`=;OPdP})9yH-R-;i(s
+d;Zt{rpShk5M2%G!lgWj0{FY%EFAmZkQ-WrzZHf`*17CWcaW)NV$yCLF0F1%`jW9YWpM_Qh_-TLu6zO
+Oxa;jy^sX!JS<Wd~#EA|^M!r%=Le>n|CgBWWo6AL$EGJuVe$0Kh^o7JLN4`?Y{6ZD|@vy_%0q8#8hwm
+OleV0Gcl$(-hNZY3S@?s;9UkDXyN7Lmwl2P;Y6BU2okwN3iWrZnN>u-Qa>U2j)@T%sM1U*_n4!id81E
+dLY6bYY5MViHmJDN|hdkD_ef3sq%!54^VwO+V_l`98K+<1#Wx3s2gW;nWz|@f5g^f!ZPSR%a47PEFH+
+pP2fqErF<W6dZ#$&fBpKhJ9}t%t{%B3)e}4=SH5M>0<4UE-%fG1FwZuMa?fR{Vhvwih8}t7AjG5*L5Z
+>g;95u#vA)8A{Le1tR|~EzbHNqG{Jcfg0emPSnHY!g69e4e(~jso7(Bm628y(R$($d3<+}}V6NBsT4!
+`rAktv{5xip;a{+!&(E6BShPOOX9dWJ6N}W=CFO|{{7Md<Mq2962tHnHw2Qpzx>#NVk@VF(a(@v2{&@
+?aSVkg1VIHX{URb>`1dkxo^)aG`MwfCA>4!kYO0%8B?n{N(|Uw{3kzvK0~s@5iHD{SP)#XEGg8gqY?c
+<M&NEI|rYFp0lpUxOucMe;2wZ&V2(DK@1{FRk=spX$4htT4fZgqh&NkDVus4}e>S@9#bjECkamc>SL@
+W3be9UsVApr9STu@57@hD3G8FiQ$XrUg2TJ`7=E-GGClC(tkKTB_tBG0$zw<h7JvhrR6$Re)Zlc=*XM
+tJDUC1Ks?{dA=B>EaAgc%x-VbmJYmV-g0WFi0CV3OmPrHV=4^pXX6UX#ckkE)D>%d-s*720P2z1Ny~Y
+q-ywNEeq<sV5{Yq$Z-WJwr-rDkSSlG-%j6>M}E>^Fyvbeyh(nXO$Ud2zv3h9^{B<GuAVXmgNc!~O8s3
+kjKc9fm@BewD{3vP;KvB+wZbq?K&kZ+UZ?As*dHzVT-QRfDE!ZG7%jcM;N#be~Ue4gt><L~e)idb<9M
+0k_0G7cf4k^%W@C>H}sx;{ErEhM_KP8YxBy|>z~wklyT?v|#OFR;+xN0bd<xJc^EW^l9qglsc7w<Pwl
+t<J=~C4Sn#qdw(b;gm+v=F}rfXH;m9xlXHL`;;>EsF2+=2>eF(d+xgH-d35Ptp_@G64?S?K8Hl&QXKM
+Sg_V9hy47C-`ZeuMEip^Xjh|m(e#S}=q^~U&xn!!6QGtUi`nnk^_;zb7F~3W5WgLBH@dx`iGBgS@7cd
+#u2jUc637W8XGPqWf^i7PjBiJGE3Si8-s{&<P(XgXcdGLW(i-Sv~RrXE+93xdPr=nFUL;7sYAcFusGj
+I{zm^-v3B?rz5c?#bH^JT<+wC~*1!;!X@aM~M8C?b{b0pYyEyCT?yBn`NdKNo>mvEhMX+A0GV?ryZF>
+A(AIl6f}jGLZ`O9o54z58KN3cs!0ciDyKm-16Z1P%b!t5%!*-kN0c>5aPuTFlYk)d4T1*FPjdc#Og)s
+dNad}Co^}LSqp=dezJl?W>ps$vk-%&)WiVa<dNbTcBqQM3xja<E%agZ)u1&@3dbC#3K8D%w_CimC4kJ
+D;~K-j*qumu*w$HyG|*`MV*(8)mf@glGseA!_t*c_5bve_VgI1wncXC6w`f%l`3z9K<7o>Vf1><<7fr
+yu_Fbx_r_B=MA7I)Sa|*05j=KuuRWM=D@ZV;0?kObu=7Jt`^I+`ESJg043*~BfNvIv-CE`t3UQOd6PG
+<bh>B0!%wy_HQCyT`Mrh$1|yR&bM;Id;B^JC_SqR8hB6)@#KM&7FE3Z`xisQ#oNpBi?fKR~HqnySPe)
+Hz5sSWkkUAW$l>p~KQvO}?2|2N#X6A@WcS$;enAnxWhS*+x$l`nn&|%}c|FPY%yS8I||{MXO+Y442_Q
+k)uHuTe~Q-FLrS4)Zgl<J^h0{_iK6GLmy+r7I;4+8ey$Hj3JjcaS<=L^{^+tI~2(xEHvW?OWhj3sAkt
+e>5ma6`2s@~(+^c5Bty1^U}(*HNbLC572-K8t#9754A{C)C-jr9*rPEBf-oCkxIOPYyXS@!<wGC?Cxj
+AnW6$x!5KZ|{>)p5OD$i+jGO4&5?k+%fxI2T3#O|3-F#NxP6NsjXZMFP>8I9w_JfYk>YOI!ayPRJwn}
+@gaY&%M}^xwIPMyEbRq_l9{j}Mc<WAd*YJkj*><UfA*7<TCe)<7_#?OfaN@MQo+sWSL?y1d4J1{QJ)Z
+T5{3O-u3D7Ak`sr+BWW6-$%$VjocH@{eJ3t)>mH*{J~#xvB;v5i1(E#~0Pm>~K9pqUmE43G2g;|I5Bb
+mRJAFFSQio-C?8=j$aAm0q(v1=o6tiVUW2x1P%8>HuuBZ<M&6Cci)|yym|XR#^#RSFl+1gDoo15QBgc
+xg5NjgqA0I>w#2DpYVABG_vu#>hHg{egP7+4GuguMELod0C<`9v5r%mimJl$ZfXDhqfD0SfFp)X}13Q
+kW@DQw{ZS2FXHfvNjsKy~hCtst*b~Wgw|0Yz@!^<-3aYzRi-q0U6w4-XB1BM*+06DfBzRDU6zUn{P5D
+Va8j*@|*I{Rk;%q)zqWoq7cWA~8}jm~8XyYpQ!Oks(ec!;j@(-#e02jD=(v*~{Mnw!tQE39^&&yZD_Y
+M^{97job9RX)4MdwS2Sn>=nMVrW<*jgYAg5p9G81Q&;8>uSyO1vxKl{Wk!hEPwh#SB**0ig8e%D*P}f
+P)Ob};I0+^#Z%;4KAo_#i#U)qlLnMg`Q?L)y2#5p2xP6qeoNH53YEwAyZBBl9f`tMSieJrnzU*!Sv`2
+KE2Sn2S=sxU4PDvm)LS})S6bqqJ~a#^IW&TuWrr5mNBle0QEpLIG54<SgoK-BO@sPuOoK*~nEXm6s?W
+xDTKDL6pLZSmCA`~T=tfFUKDLj>ckomDmU9NFCAJ=+i$IRSCaRB%FbK$*XjHSY|DNxnYRTe(Hf1B)Sc
+;Ec@NFghQ$N8#i(P|$Nt*9uQJgpF`r{hyv_aqWbA(BnF?gY9k$@?GJSp8jF>eP>N<d{G*%N`OnT`caR
+P>ieYQvH|8Q?CGgyr3aUDI13?|E)+#EWE*aIVYWe{P}K6M|(IwvofPXp9r(S`M{&^RabBV%Jd%EMy~N
+@mUz3IkZ2;)QvbxCWAG-S5UfP_n`m}Pr2!tx%ZlZcf!U`+u3eD-Kp?FZ%D?}6D8FR&)NiHR}PvMK^}&
+^^K>3{q_^yLEv4Es_DS>D_Kq!h!bZ@zM%3Y%Z;dE4pR`7^3$pLC?}AOfCl5sGf~sR(f*?86!MMJP{s|
+GlEmNu==ZS`=9WlTyi^s3i^X>rTs6XBsV^jMVs-4Pi?u_YZY&Qw51JB{??CCb)^$O1Oh4@UkZqWj7EI
+#=&2f=Wx2xH_ss;HziE*ph3_WSHT!l^x4SC0%B6B18p;MN^p=i~_{KR?0oQiF`=->^yY>IgiRgl(GmT
+JBK7j8(aHk?ZE0y4SHg+*qh(qlsMJPPY`PdxZp9osNNk+G%lRbNlk$y>mT8d^Yp!$1@2!W6{IceFo@6
+4?EIPzy2;Nc?9d*5<JwHO*1zaG6tBGND2(Mpky&EePJm<lbri6Pn?hqasm^##2Uu1w6}<*S2CbpUh3M
+}9Xn$`d70nji)uxVOLR}EhbsO|?Gz5y!t870Ob6J&N#z(MHsLUpP}?`qhOuqw&C~!HCGB^x@MsuINAd
+#5RD)J<oqd0(vv22|v#W0zraq}lDcMMUjBRftJJ_-LW2mwCU5P<#`8-5>fKAzRQEVREfa12=;(zn?j4
+mp>6H^9eVAr!hs~ExIuIZ;3DDSNK4s2xBLUlWNjas>Y>-Zr`o^}_;Jeogs2(6vDK2Yx12?&#qhh4)qX
+O*wKz95E;5f^RHT<=dEbKY$vfj`x>37x54bDP>}T5FbRA#e<Ckde{Gtw#}I_oEQpt}KcO@DQFqk5&}%
+WS+wk);SsY^FbeDcR<aW=rY1Ue&w9?o4@}og^I0+!O!e8CLG)ld0l(rrDXjy(rn89=F+cZOo69Vr<CM
+Wj4k2?oq8SHdvWj!indIiRM*~Q2KSB)Q6vKoBEb`3W+<2DL<dNB0AMwKz6%3%{1FA~q4?l74uZ5*Z_O
+y}b>1UBZ<=H})!ThKRri-)R2#Y=P~-~}QG|fyew68Sb$zLnVI1K^ZiK+H|9l%;{xL((VQP#t(ZxOW5Q
+!>Hq?HudrEa|Cp%9=6j%-B!M(=_N==ZNU*<eN0J)t*6mP{>(sTrun8l_ZE%vaYL?+qRs8n|g-zztT|D
+mqQYPUv0Z#P+F}d8L}qP)!`qV&G6D&3C>KB7pw=x{yc{imNMD%=><C?rYq-@Op{H$F_cSn`Sq%!*#6t
+?bSkCL-z3^3t$@&kBJ0n;AI-TwC)|cMT@T6;%hamq-9vGNXFEHtAT=GjP)CEyn$RrU)3q8j|zV&3YRh
+In{Avhs-{NUXV+1w7Np~>L~}DNl|~oOnqkWt5EWI?X_3aWIj5aoI4QP$7c=JP`1Ub=>y9)96Za6wGLV
+@GoOVe}5+#bVO|elma3DPmN2bq+nQUwKu+lJP<8&yOVefhyNtpIMw~(ovF>G~hl6y2z-H*Ax>)u=S4q
+ipsKlb2g-P>(}BR_Jri~0H>&ABWIwlW&H0pAYWq};W~V4L*q<jtRbin7n|nF6z)Y?Nf|J-TyiMT~^)z
+2=F-9p*qir5Jth%B1NEL~ct-`$>bPXRXAEh_xCLZjyFHI(|r3E7!HT8AE4{@DfD=01)?7U<=-}W$D9K
+SkV(71xrNZ0acpv-GawRj`BQilEiquJD3Fxs?*5o)1d*k)Bk93sO~VM(mi-wQ%$&*Vjq6G^d~iJJCt8
+wr0udV?S|R40gPEEPR{rVu{}~}CJ5cLe2({2k6#{rfAsB}lgZ2DckiRH+T$YcD0#y$oQo=rM@XoPI*@
+$G`_q=(c2i3nLHdLt(A_JbPK{qSr33WzcFoJ#qwbgn8TRncNjnF_8xyr5E0Ed7^adHP*f`XU5~}@96{
+ReY9sUEJJGM%3Jyu}^Qusk3t0*`5PU73K{i&x~y6X0a>>!ohesikCEuzpJM1dbB46As>KJ1za4}4I0U
+_6Ty^tfYR(vYM6mt?Ma3V@%qhK%EDdfe)!OMABI{1ee9n=xw6cQ==WOKu)$+AW}=(%othQ@LA>Na2=&
+IS?lG^S?*Ujtt6IvKIwi4qG(ZL`t_qbRtFMJi~^`Nb)+9yI%cZeEirLx=g)?sKL-yN(#He6gM9&uPwU
+FI+LbF{7h~Vlz0lO%Cfjhg=Qr}k|1T5H3izDmS0c_HvuV^<}C7?tW*YokufdU`;M_wEh@XPX&Hi)uAL
+C^PA2Z|L=&lLv^cp#F#mB=+++(1C}Kz{sIZ<%LD7pK0E5DqL$z%ai+oZxc7><DVjI~rZIeW+4Q~<oHX
+#;e&9+L=cN^h|Z#*PCbB!63<6M-p#fH+Kb-cL~=Lhz_-k~)o-qS##l=gNsge+IP0-e?KiRtj_>G@gM^
+2VKzg#gB+z3>;lRwD+~6gm!p#(<nE0Ju!JTuu6l9==E4W2*xPDYf+I#e$%ra>DNQU|CH`FF8L=PLdSI
+>l<)dVqk9)j>Ni?=ng0bde|E!E%?T%*)l4%$uu-6Ijy~eCGr0CC{^eDuzA45Uis%0egt@cKJT#!e=y3
+teJH_2_)jNA;m5>>Ow56fU!oNkxP@dB24jl&1N{arOvBqRHVcdsjA)OWqtoKw8@S!yFXbQ;b184_zG~
+X%lcjagsqFCO3Y2bOe$X3cJ&3^PMdr}tEliwx<PeVq^6Yu*G;$#^=8PQqMK3V9A?@=D*#3OL76ddjLZ
+9;lN`aWPKa9lK#E^=>hV7N6K4h8ioGal`*x_Un1sV;<HDN6tYvF*N7M<hg9^p@f`g`nu9-%S?zOS*0^
+sS;JF{7HfH*~2m-4tBrx9(`>X4xE03Vr7%oz-#$&Sr&ykS+`;g3uHgpp*y~{Ebe}hHk?|Htn5I9K$))
+vHADd?E8rvoO~mFE(q&4;k4`gV!<~}Nu>}Hn??aJmPodkbGq=2E_Wa0d&cIEw<Nn!(zZo830>5(ZN&)
+qakGk`X5jH+oQ=LXArKWncfY~A>lfPt*)#KqtZZZmH(D#*+=r>~Iv8mrx}1Dk6E%Pi*JUkkBhf>ke#s
+p+?QPSEIeEjLVZ%>e5>FJo|M_Uw25XOR(ESU0uQ@PCGZ^5D<Xw##338)wot8`ZZX<gKJGI=@T6yPDo3
+D2WYbCk!w1h=zgQ2w>k6mZ}g1In8-y*i3P<?HcAY^m`x{6xM|9E4dJ9)xGc!wU^p^<xwJlqAK(|r(t_
+;kI#OF!JD;)k6Q&6ao#<}asNEsO8Mgz;ts-PuuCsvw_6Pz4fb`nA2$kT<|14Edp`99!6gOZKle^{Q$(
+@<I(zSDC;|S*=|SQx5z-Wiz)a)fJ88U(lFw*k*UqBeyxVwH=v;yK3Txt?9Zbxu&8EIbo0HUI|k@twYZ
+^6n~D1e~vK$-v1o*q#}Tg93R9&$0Rjqpi}+Va-~p90Almbs8j7S6yk-kt?MTAYvDb<R8!~?{sja<@nv
+lABm<7z4NNsbe%zqBTKk?G6P`f8N79-UbX|mkJfjI$^lunnVmuAbER^<)`7?CdTo<e4e36ycJiJFT*r
+s)Wku8MW+g^qI{g~C5&5likQ_`62pxfD?^vFYR?A8MVe-C}{z!g|RzyZqe_r5)TeKa|G{gOyGHT6X6-
+iPMFU_X5@{OaF&!<6$x_SvK?mS6Hc?7evN`u*|i?~Wp`fA|oEp&c2C$>82Y-^1HCu|W(z-2UVAAUzvC
+94aU`pf@HI_4WPH+jqxrUdLYj!}zOzd<p*>kKsR`jsNj~$3q42!Q``t0aQdv1|sqL7H?hRz@8UxUcGu
+Afj+YDz4_zY=P&+z^gf1zQ`_prj#g*3)vGrzzx%e0&D)2A4=Mb2_`snO?{dd$Km5kbKFuZ>e0A~%+^J
+ul{-1-hheK7y=>Q)A7TZtg>1vD5Da<h5!ot8j)-ThCfdllbz2Hyz2L?704xhrx{qfDaqvRj><@lS|Z{
+8lgc>eBav}a~$1NzYVc>)}2?cY2Nz}~O{6EEq@o4h19A6_m#$66Kz20`)BNOoI(c~^8_sm@9X=tLISj
+0Z!x&y4>>k3x8}nV5ih1qTX7wA%FdSa3%aK)I&fShFb2*c(MqennWkmq)FG@8r4q<~O4$p=FGUPtmua
+2c9Z4d7G1#K$m6Fr9^{4@8jv`%^4QHz^mdpkd&GaheLVM2PT1}9IN3F4{V@au?YLbY8zuyTf<Ix)S9B
+7HD@Ol^7Myr!X5ULhwhwnZQCGt0x=i~PP?zCtzKvsSkO~N^6ae6)N|x5DE_=2Ql)XJfg7*>RrAV>F;D
+Wo6_{i-or-94DBT!FD0!1uQZ4Xy>2<sgm{7ZnBB5(fE*t!pM-W8nXLX5p>7@8aq*BO9R>OkQ_4QQU1F
+M$-)H!bJ85$?jBzb|_erm==S@>;0sTq)z*~=H(wAaovekp+A4ElkG)@}ob_w0IsVejLo{!@=?q{B!kt
+US59z7#j6dFf;%^HV@J{(>?#sMG@ah*r3U9CTb@h9|QW#1;{Jd@-#VU75+k-8Lei7<Mwoa`E^RmW#(5
+juAzVcV68Vzu#D{k{PMZ+b%d2wW@Ju%q&{7fhNG56?YKc&h8<V7lk)bUfvKJA90}T0+gEwTh}bn3U`L
+H#l|2PVjbZt1F0u%Iv(1x(U}+4)nhbh>iE&7;bKEWPfeWfnA(lEReg;@Fi7u~s1OuQR?SnH;W3+SnXL
+x;)oeaDS56D|K#hS7T1LbT8f_m0nZEjJMUV91SHs}hIMSBa{mau2w<$;)VS_^y+@KW_iD@(ZDguVl^5
+uuq6vQfO@o(H@*VS&!cBzzIQ_teZJ63qbW~h_^k`|E-C(6ncU`(As^GG$5yT>moj7ws#HP!$*49b4U0
+p3(tlZNneNPpw(lT(`F6Ol)rp6N?T&7cSXWuG~bLM)<0L$J0$b=3cKdPqBOI`!`4nohZ&<_sRN+DXpj
+6;)JS#Mwx~m&*yIuoO1{Y_yW-gBrndJDNL$lH~^L!jk2&PboHn1{Ph+(E5bEY~73OK_y@Tcv@J!w{WX
+0)62Trtf)e$W;i1I(7kd*8eBc6!2(gIeg3<zoEJu(qR059f2Oo@DXwpxke-*cQ{JIBhL6)lzDi$s+W1
+x2BlpfEy7M|#Z{S4Q{PzUQ(YD1_^)Sl3y#fCil??kIQOeT21m|RmKZcH|aIqo=k{^v&dB+Z(&~ox*=}
+!(bYVO+5rMH#o&4{QYf;~OHw@6Q|jYQ9{x^of*FE1Hhhiz%yp8gy3nXl|_L*!!<LlwFi3_f3_ricLrt
++XVa_V8bVW`H;8dQ7fou__P)9~!_ul_zIom9u26y~W)th{q7EA4e(P!HA(Xnu%Jg*96O_3`GX+EW89r
+0y8OOp3XhZ5?W!M(wcTToR?hEci^J=@dKlatgj|a3QmJ}TE1*1n`o^HX<?%EI^x<o!}Ho}t<I;$@?De
+(*li{V_1BumH|Bq%7Be@Ak`VD}RJUu}7}TE9?`L`!2m@&E{T5R}0AiyA?QB`y0O`$tGr6d<OS=2m1lJ
+0Y7=+8OEgep(uhSsV@3N^EvMmi91BRQRyT-E)Lg(XtTQ>wa7#NR`<VAFwW5K(Ae>C*s6HHImYU1UshI
+W%5YS=nxD49{ERrK{$m`z^57aJhQ`|9?Qi5fP$>g3PgOeh5hwBkr+cVJU70~0~phUD{CFOQaf@)lYy`
+7@2T6Z6B#Unj4g|JR$hBU?x<^0o?cMr#=w8C&V?s(;+jcwH)I?C1x8pS}q7eeYD0;lyKOY$s;arUUsp
+kbeW?i<>3d@$HwOk<1#K$e(@#gLY_ONh6rKK@P&OU;IY8*1rv3N2{%O5x@?%?uV)qYd7Ecu8<&Ynidg
+NWqtR6T8fcDxuyn31Q@r4AkH3qVPAK>@WpDad~nmiTUorLOHwAt|E+a3tbH1WB)FsqjJ`uoI=gZxn->
+=sIl4}Q2m=z4F%eCSxskERc&KG0Nad4oLqJ&Sji(K;^f~Io%kW>a|Kh&@p<UF_A0jL*8W2J>ohm$@h3
+pMwUJr;P4V|f=I7D>1V>4BM^E+k{!^YVB!Il~OYG<|HtE{=2Ems{@yg_@{Gsm7p2N&$X$iwS=>;D=(r
+@XB`jP>D|2{N&MtcjtIpn+FF9B1EY!o-IIQ{f7c<h{CZXF-Kt{f-JV@@n!2Ze_lEkn&H1H0}m<`Dugt
+atEYO8`PIO29^KV2}&doCCO3?Q{Qt!ymvBb#gQM|xC&1oplC6UdXi*ZQ0J6duSV||lZ#Dhykkn`o!Bw
+fGz*4b-$!#sLuG7m9~K}jQ~vL|BKb7?Y0Fc{)>!gcB?wysa=8^imJ4q{z*#)nlncC3iXu)}{#k4#m^w
+|8L6PR^h-@!@+NA-OZE46T3?K$gL<4Ldv(b!!4~KyUAtNJovKH9upH_MeUQqUmJHrm`OTeM|zb)A(nj
+R>9Vs9NI_jz2T_MVQ4W-{na{Fi#67v2=CRc$z+p~=0DQY$`1sWSkfh!UL;z)LQ(*|uxs-+phSEMNsqP
+wBVx()WQtRx|<)<K$ILLSz&pfZVc%+4pzH=<X*!Tyf!27QeLcxz1--WicaC&Jr1od9Lkx`>v2ZTPQF^
+=8G>LJJEsn#R4`L)-}D(7npdW-nUz3hFgRUpYRt}*+Pq>xi6`}c_mA{Dak;&%GP=uYl6@S2rLCc-T}0
+9;rwvt(5-K*>)Xp9g4s}zBepHpCd!j;BuL&>;@%<-(O)c*OH?h%>LUIq1qac{nJEgZz(V-;u53-QrdS
+TLQVua)E?-{Ty@YF!h|uv0LeVW~VCfu>|MftdEAZ3*yWW~VCxgEW^q+?mF@B!ophVk5r-ZDyf4G-030
+d8!Qk5outv1BZsKQA#p95fEp#^2t%wbjQHB1>p^u5=QOEL%-vkl&;g@t{kDp|wD9AH4iU;*=G@;H5*!
+Xe~Kvkf4I15oB%&wp85*0ea4rAKq^)gq6Lz43<x2pk$Sgt{!5u2@d`ib42}NL-GTO~``>4{iXT%m;=e
+90rU+oP(W=yMue|X5OJbcMk@x4TG+JtyexRHClPR{wU>Jnq+)!c`$U_mDIXZf#rNrHgzBNM^A>QWY^e
+{)VUKAgpFtlwQQW;tD-^gOVa<|3_H2XDd7kx_7Py|^M*J@+cn%WHxbCs6AyRB2iLLOlX<=(6XhqZw|V
+PR7JR<61vSm<Xi6meLUMZ6uAgcwynW*(G$m@`ZA@XNuaT%~6M;2ie<RYDZ_|vB8wXs*$%QrNI*UJ*78
+s9#naR5wGOB27%dEcUQU>^4QC#vQPG+jJf=%%ghB4rO?#OP&&wlckzx;(5#+^+Ob>pCRaBC(eM=Zy?w
+y^T_rw-_@UI;g4htd}g%%K_%ULKGB%$GVXgYk{I9a4Ol#je^pdm8d452J$OeA#len=*$sc*?ire~Ym}
+)rIyZH6n|<;1n);gKIfuFjLZ6HgNjslT+=PaL2*-pu98wV&(`az*GjdF;byc(@M67CDy=<9ZehZA)or
+IF${8^Izx*sX4?S;(PhM-DNN%n@fx|=l@P;4o~<!$<n!7FQ_)iFDqv#PGGB$2w(l(0X|%$fMXp<CSyy
+T6u5eat;zowW>2ENB-IW!Lc#I@{Mk{nEhUnnHl8b&#lYwl8u`z@X19y_ihHP7VRmxbqW+6z%aGFf2GA
+Lc3Aet8gjCH2Ic-Eg5mRHx89Rfo?lFidyyWGHW!QOFZ6-nT`zEq|lb16wAqDhMW-*Sn1v*%@{`i+$Gt
+>>;vBOQ2>ZGN8X6|zq$l<)xlf3=lpLVx#G{{2{<J`5sY>$%Z4?=-U+=R{~q6MTABw{WRt>=<!)fc|u^
+akxoM^Sgr$O}A|qmI8-NWbk=2RLr7-#M)AtyXQF!TI>k*Y%xzKV_urf0~&%6QTv>Y+6oPGGF=xu@9=X
+!s}<{=ZkWZIAJ2Lrt`RvpC^c$|9{QF+nuaxi;TNCmU*@Q|tvQo%Q=(RMSyJxSYID9QX7mclh!hx^w#t
+hJoC%wI(tZ05Oup>S6Fn&exOD*NMR0x~nZ;cmAG>kL<KuF00`Fho=y7g=T_ujXb9{!bc9*T~tDae(f(
+I^U{ilPa(PB?G&&rbNREaOsDVrR~)q=|wax*MfF<URT-7yL=??J>EB`Q#8K3nB8=6vIEH@QZ-*jd_D0
+sT~{;p(;nhd5TqBA)62aMdD;N|4<nUj0BcaO*(9Z1{BQ1%!ZbIi**pbo+4_BdS+{LTao?!Xuyy8g<ut
+&GL>Ijdmoej<Nu0j1&Q?s7k2xIvBNY>&sVs^J7p?oJVN%QNd6|_)!Sbm_eNq#RNgmD3NQ(>9SRB2u-}
+pbV8PGHB)91pRVUd0iZuLh>53JwuFwDWTOa2WE!SR?`3ZlZLm$;gKyUW$3Th#uND?XinfU@lD+5iesr
+uNA*Do*TBwIi<+NVYDGqo#)vKh94?dZ2Bn{1hY1xj{$)-+>CQrSbp8iOWsdW%%AgTHwj9sA7w;)9h#K
+C55QVqTGlolR$g8!A9ztdK{f(yUdZC))IR{MZ}-`!%J#|zJwUZau}iR5&!B3o#rcqXaU@L!U&&Fob)F
+8w~T`PF&3S>|Zu9F&n#hLiz~Wje<+iMD**z8HoK3g#u66B%v8&<>6*z|faA(0!YEot1J8>t#VnA1WXu
+sj#RnE@!+lr3`<!3S&MrJi3m`91wV|)5XFM!8IQ)0ZGI*SP?n4+mu10GTO>VU;BW&*97iAcwP3UHJ-L
+#XJty5H7g+VTg-e%r>2zLu4Q-fRUW@j(DBHz0Lk1$%(;{+zV`^Tg6?6fD3RA14B|yLh&|Z-51&NM6kU
+>b8^dud;|K5HqaIJo)fqDy{3}A6SJ`K6ilFg^GKJ$~9jnu!43RGs6fNg`3QVN1K`zUx<^-Ff%x?-d7A
+S8O0y;1qHA!+f*tytcv!YzM*_uRL7(vv;+Nq%H>PfS9nt6x1hP((=cVi@ub-1op6H4WY83F)d1M|L=-
+BH$n#D@-3jP8BERm7eVu|7oOSh*mW0W|1)rQOixt=1F20h5ebHlnaEJpEIpw@Z&evi%9EH<L+AA(g>h
+kjph0ge3q<&+~$OAqj3>^aj$p0o|_ay%E(kn|vZZuSnV*7ezy#$*rpZXo`}|*)jWh!?_}S1v+~kshm(
+G`wn$f!#+u{6{LX%ScayAFs06JVYDCK*RQ6{GWRtL_(<t}SDtT*1zl=G+oYE~q;z8O1ja<cXhaNhT*Z
+g*)+v^y0v(s?#jLh%y^yM%H|t`(A=fvRKHLIB%gRew3t$wtRXwLHOD*itF2X=uFJ9GHHk4V)MtNC*%^
+mT5k5N#TQNedNNx6iuQo{AEb{cRJpKr9`-)MU{9Qv>I4fE;STZ--7UjcIcAg2MLw982+DKGIzwbOadN
+)P^PIic~e6<FB~&LSN`eGHPj7fP+a{F4FIBONu`thb9i7GnMC=>7AT&)+}iRXX}#NB=9V4=_0WFc_Ut
+af1)@^a17D4NgZNhG)ZGw=+`|b>yB_J&EVX%o7rNlQ0lGx+nb@90C3*I-x3)-SQ6vuI@H7usx!)aG4b
+g)*tJX=7P>|`i^Ew|Ne#D!|%f3f15+N<qPH?W?Xt|!d_n3%SEdubi8Xt26f(Y^7%RD4Mp{aJgmeL3rY
+X$syff=WbkD8`{X?PsgGyC!2CKMKfx0ie|Lj=ha3g7QH#1Nso)E{Ku374pghJG0wEy7ywE~aTohE&)`
+_}hj+YT+ROp>9b0(RXmCdWXDLS&9O%WY$cE+dtd-5s68F1R~`&WV8@TEh1iRa_9R@Mtn!aje}ey(7^<
+^^8Qw2b>1Fx~s`WIX=x^ux2>$hM5WYyNa$E(1hyU&Xx)`G=7A4a}?TPuHgEC>g~pp3*StqtP#?o<j!Z
+h*hz|fRA!s)v36?7sa^<8;S5Tj2)6crHw<uT@ev#x<3Za-u>j*hKbRkT9xxcY_V84q_OdxG+eY3=0Cd
+QAz+lmkg~;M&=*r?bAV?8-<5EB76fv5IJZ-XZaVloR3jyysKhjdh%4SwO6<LqF*qTS*2n6A2|&MtSW_
+3(!u!f}D-Qi;0=V%)3)-$-!5p@ZIukLS(++HawuKsw{PEv8ueN_k|NI=0aPh+qU;yEUhH+%%%>gR=DF
+;QF@Tf-`2qE(&XNVXdJp%|TP_p~{ZVHgg<(dO>&BsZgD=s4*C<Vi#r<mAAnfaab;uRPs2T5xRDjs96m
+6CzmhFbC^4TpYUxO>7ke8eBS8$b@DQWCfOSyJqLk(=vbD6yE-P8ih_`i80CmkT&P7EP=!<vvF%@{Ol;
+v*tP-u=*(Xa>eo{c>qf413=3hHAM9)RUFjXq9n+VbZvEP6b6X0RRsk(RGX=@0S$YYUQA@P*zu*m^gOe
+NW@<Y$6TGsGzW5P}stAd|N}hPtUfeC9%CADQ4m%!PUxMr%9$Ayjn0R$^sALJRV}t|6s=9^#Fg9UxwV5
+0QNpqY#!|@csNX-QP+h(Gg8gOLp&XR$-p6q~c#2TUrw3fT?@;a|B{7UZo$;{l%2+db3YDurGtD0-(=D
+NWBhXY;TjF-e*z=5v~S|#n&If^&?&^23w<M&6eM#=l5x37+0KYx#DikzUm68nF{hGA15%2}qQipQxkB
+s1-y*~P(=eD5$39r&xM$kx%KwVyn4#jqSXZ8+yV+6)ig$E_zq-0#-a>X`7`iZU=CDfjT<11~Z~qr<@i
+@?s?c+!_5`x_WzUV*`HJ&lV?q=tMC@>Q-{?aBVDhLiAB}ig>D>sZ#NA#WjV|QeYPm{_X)Kcg3A{j?oZ
+CFT?zL{QGoucovMDsKOl1I*0E86H|En+e|-f-+=|HYuAZ}4ircoM81A{xoqF06n3qj*$Bux$vB4X{-{
+rhr{(;hx;R+yeLufGx(#|FV^rse#R3revM8CCVEjetd?Rrzb|J{#EH7L_-72aB=7N-eGg>a6NF#06T4
+@hr7m{p<wHLF199t0PItX&7HDN0MLD!6JofXaN%!Eu$#WwOk4XyaGIJG@GKHZ`p#^fk5<r)>^9y}Qhf
+pSDp<?009`-+V0OH}g2>rx6<juKOP)9)gA8kk0VS_xKl7!2goK?QH#WD9mh$`_90h89i>V&Pe>`XGqj
+z~%-^QMp1(5dLM(I{^6sPj8afZ{8;_o`3so^8Qc9?~?CczkKuJyH`iA-ygk9ULC#v)0>ySCrPXwCNlg
+?E0rXiY)YH3KMKh)7*jN{Kg_&uN3(Fcoc~~F-W<JU?G;`YnX_V~@64a?@_-0?qaI6Nr#<t>$5FC9<JH
+AK-O{>VBmEQA;lKVPy9v1ZBN?x(VqzC9fQN~lFEG<Hmgn5cRQ6HeKXclQ8qILjH4#ocPR2os@rxkEI4
+aKCneHywXfhU>h}K4Lo4SudZA&99f8OB#^*chMOuZ%_C*I(9Wc@~y%iB4?YOf5v{J8c4>Qr6$LE&AE=
+ocsL^6PSSQ{yg8_wT3lqusbzdBm!&o8wiYxD`JeTbhf+%4_#bOMqelererB>NtN+*F>)BT<!-zU)XK7
+rF0ldfTJ8WI+{p>l=~N4x?FXkppk8igCbK*mgAVJHDBL=0R|>$Icb&|UBiUdORfoL(%%#~N}<eVvyGI
+q441;EtkfW1@Tw+7r9$tY)UWnmXLzxVjcs%5coEb8#LMQ2S+xFM<BfrAcywUKw9cWof7`|;%y`R7PE|
+n~ebPxS*yQxFAAb-jl4QhCx=jspUDbH7bfc2wS}(t@bZfavonMj?MC9VAD~y(P%Jet+8a+zzyWAn+U2
+s>-_+M2IJtz=Bp||*s!Yd!?Sa*Y-nzBI7{Gc#H_3v<4lga6mL&i}1k<7rmmx`|xK)ZZ(uCM*$cv&pZO
+zo13jKYaHxUD*$vZZ)xUbj(gWR;<y0fQ*xY^=`7A1{u<KF=3^9S(hdvu2wl!;QLT%)`_5nXF1qIU^Gr
+^)yg%TfvPi?h+aoBt?;@oKcd0#2YBhY$V_;5`yOPLd;#H-Zi+=OwA0QFSF|$9yIo;p{esNF6!itS-b<
+@b6$Ns;MOSj+o<>m28IE({qPo)>B+nAPEOvuRq<sK|N4gMzu&`QUIUa~aP51=RpG2}+T39vQyJ-04))
+J@7&3??_NmgtWh2B<)`=I3Khu(iJg{SVT>bVpzcJVilOz>F$-AX*E(i@&K#J%ilb1&nx<M!RDuZ=oK6
+!*@vguUXOyvejG8nG_acYeyY>b<kRM<i9RDL-VeF2e@Gf3&pfLurdo(0XuuKc6%m#8}rtY{&Z2OJPHq
+(^Cz9AEGR`NFAk%Xw)0qQrzMqb-k*pz)}w?)0`#HA-<qa`m}P+G|wczLKz4-_~c)pb|c0w$2^YRbsL6
+`Gs7BYvXTvr(u~2)5zWr+&c@KNHa4my7GjEv&*>=l0~bjn<beqo*W)ToQ>wwn!owPgY&uzS!xmkvS`@
+?+VG@n8wN?usl|SR#SfCriVb~DPPL5ocHn?H+VUFjPg=Dd2o)?N-n+~!75}221HS>jTh&=z0MRlN0Ii
+U?pSJW100K3!pa?k<pABH>66VNon8d=Xr}oYDsCdy0-KcsFI$^9wna3Vp*zpcI+Ke}iSB(G$pGXYN$k
+xCC6r<W)H&O}*4#?yRdjfTF4bW;BLbZgM|BdbHvkaANssMz4__w1bKcEK$$OiZnFCSn0x>@7^%Dj9lj
+q<EspsKrR_|_Ic4sbl<$3PfsIHOt-#Z(Uz3vcA<o^^Wo?+$t#ehJiuL9Um;rpF#CMlw7j+5$WhA^w)t
+*{n)!#qs1x`iyA^OQcM)rkLl0i>|Wy7x9wqCg%>i`A-Fh;i5UDpkTkj<=DEf%&`DkK9W2Co)|=fN>OH
+)$gt=6qBzf4z~?g(?_@q`vKzeMm0*~*V=DTNufO77WY=Gw_9xP&e@50Y`r9%+6E2BonmlV)>LH89&it
+{<Q*+ai5y1qQNsrl$92g?qHKj_858T;F|DKR*oavrho0{ercFR?*v+G=nSKG4c(<#0E{Ny;vZnA=IYE
+GHoyhj;>Xn{Bw)sDxwX^cX-xFFY*I38qy2>Z-gF2b#GOb*5Z`H~bbp)%Ds)int;c)bp@G7cG*iL<ARj
+_blW9c6UDVNFCHY*tdH)g69KvMHUwgp;3}vyJ7>0<_Ot5MPVt+CL2yiea^W;BP>P=>!8F=duz)J>g~G
+tQdeit)Pt6-@?hwZ3|)9dHR*bG+b?+lM}RUJ?E*-L7CvhZj9rmYP2O0zkV0vC1*8k93IQ2oZ#2hIvkF
+DAg2#<-&;&3HZ^7n5ZSgC$P%xe9w)8Wo=c`VC2M-6!EESkRjR8~N$7G)f<TG9JQqSD$Fm~!!m}L=^7J
+wV5)nAK$Ee!8-U36e#e>Y><i%4xp4)t}NOxg&u*jC@bH0(_kp7$6#Cp-SHA->8iriMC*camn*-Ge_t~
+&+H4d^ztUYM6*m?>XIN_BIIf$sgkP)h>@6aWAK2mtey7DxJ8ag34#007Sm001BW003}la4%nWWo~3|a
+xZXlZ)b94b8|0WUukY>bYEXCaCxm(TXUjF6n^(toSHm{S>l*HZf&N9C@NJ5S-?!q6Gfmky#bo-ZjJu?
+J>4LpiP@d1^&!H!eCIn?a|L5o?Kxe)NHEd?+>LJUU`82VGXYNo70D|V@W-}({Jt&{nh#S}eCu8HuHcd
+If(k)d36jA^5ED|wGofG^UEz09W{^=KIep$p0`djH1Y60sgcDdXjs|<gsW!Y~g1{S<FMv|@%{CHNgaC
+TH5Kf-=u;xiANoGRD*_LJ`L%c-d9|FlU+Y$sy+iXfEX-YH!9kn`AJ*cXRBphf7NuDcDO2km770qo3{b
+}^W3T$vfcntg}XJSv_ixuMg7qGmEHnNt{+z(*lgk#ro79m)!3+NSCUgX#zLj7=}IRy613edwxCxkmfd
+t-MwalD!Ka~6-TgF%k|;7<`^VF%+m3akgmb)ug-_|%C!I}C>i0iF-`lkG(i&J|3D*MkkNV?DSwO#J|n
+Ebk|TkM`KHToXwL_Bb*T;qZhWW8VwyKbMGxHZZXk)=bd|v}^OKvYJ~_=ws9Xa|@Slq;i-B{sLS-RJ36
+k+8Dx$Eai;#L&Bknem2qz6l=><gvOEMd&(XIM}ehSc=pV7X0|uBm8-AqBR@dHW!N~FU<FR7V*1OdE|R
+Z75yi7>OkH?Y3uK}>*Z~${VQJjyX(NWc-iosV;&`=@8&2XFXi+gPadx*<%}isEvOFiLM1g26$ln`MYq
+LZWNegmNsppL3GO5)?g2UaYQbO}uD)udukQw2;7N6tvdc8QtEmUNX-aYhhhj)FHrg<U+IOU3#R1$+d|
+AhnxzxMz?E1DBDZE%%D6iNI>%Z$CD{C3pS!i=mib>$z&h9LRMgzJQ_1%6$>zNx1MPa~jyhzMkd2%}T6
+kO`Nu6sS?0jlrq9)|^<L8DtU-bVZFK+2y3XudQ3wM?`fzUJ0I<ZIY`Uu89t@28A#>d~0x-NXeVr4qk(
+9_G|1My0!&CqYemNDM8IzsN-bx<NjF?PH=Qe@K{NDXR`JC_{yvy@Ey+?UMIP4=_K2=w(Ue{ki@1i4y7
+8vSHKc=-jQZoN0Q?`9bJk*@}v?ZyAXwz(x@8<H>dr&NVx%w<t5B^8Bm|HG81)c8^2C3HkI0=bCe3F{A
+i=;d~TxlLA-o!Q<9x*no{21p7y(hZ!kLTaH+TEY_c_$8e2oDZ`w&$-6_weux`yz{qU5TfgSrmuXhBYd
+LM6G=jFE!z7{u?>(mlQ`Y?4=cJfopvr}6`<!Oq0O7aYL?|JD`o$Yf|;z4_b9ywccB7|``M@`6IJ1`rZ
+EaL!W>QIAW>wfNdN(rw97OA-fxOPS#C4Cc5-RSb@>Y2Q>2kKJSD~4MM*L4_9@c*Eamg~W1sK34pGP#I
+{Zz(C?4gKLRDEF$V8M4HDF9IE!^f6LFOXJq;&e_+tv=_;>^|%C@?2NWQHyv;jMuUG@>kFg3W|AF7TOo
+C~w@f)sLCwA!?G#gww6gsV3nTgd{3oTIHg$t(XP3z?C6$C|UlE&g#!nrO7f5~n=SBPnP)h>@6aWAK2m
+tey7Dodn?<7V9005H;000{R003}la4%nWWo~3|axZXlZ)b94b8|0ZVR9~Td9_vBZrer>eb-kEVi=YLN
+_E_qB4~oHDM}(hkt#{q2m(W(#ig_@MY2oMwt#$q(<|Bn3HsW8K>sOU(%D^-y3}q{6cJz#cXrO1GsB(b
+5%jt}Sfn9Kmd~IpqTXl1(`p@oo@N`)ma78Viw>NfK0SjGOZhU*;ez5u@HNZf^BeW}#d006xW7o(FIq<
+ko>IPMdCt-ViWJJ6YM3P3Xt2gq5!xULA!K>M*>zb^C{{?8r%`bma0*e%F<^r{S;@0Br*O-P6`)txv@9
+T^6cDkZoL+BW$%CYzp#~XGZ&*k}<O>S?M<KXQZzvKinrf1?U<)b*$gJ5`YQ2?ZG~mDz2;x{E8O{5$EP
+rf(=M4Qz;u>IjFm;^^b6^bM3Gs0K1c*J5L1gBS9Tx_sr(49Fct9)*5aklvHw_P|E=_+7t}!AmA{+!E-
+Im(ct=Yh|M-pdFrk06Gws>%cNHH;7eT)urZd#`QQYIgozHNA3A1T0gz_>7MAH1=k+0k`wfMt?%%YdQd
+A``J+f;TmElUN#xb`9OvkfDA;gzng$@#PHJFa`!>LPkP{E1}g=R5d2P=U`SBYx8E7FKQUN&IByS6S86
+E8JL3jM1Zk>DA>~wXP`V+s3x`$r2D30i+D`zyF{q4jge)J3|lt@)RFPN<6_{<s{k4h*YreAXXdLWIg%
+7vY(sI%#*1E{5~;y(u?Z6*d58NiqTgyoJYB<l9+gGO>3j}sou#~hGGRD1bRM&U@*vJzRb;;onDTVVX`
+WZ^6^-$r<@N3*M_imt*d0p{x`=~3hx359cQ>?#r1Pu=_`78xj=^*I^h_?-xDzZXI!;ccBQK+f-HG*an
+<XK=>x7>ix7Pt$6>uH!dB$lK1h2Q=E%Y@gFI{KFW*#vd0THX5_3}t9S2%3*Magp<&FA3rlD4Ejq!E-^
+7!<Uf(>SW+*90`2$23{WQdm;RkiM#|3v!XwbvHPrrh)B1di$KM2C{9BjINi*a@j;-@pXhOu6sbYC!mh
+p=}mV}S`*&%Q^r@TgVZrg)XVFCm9nJ$s#2&a%=TSrLZqhh>})Dht)V;osr{>M4^`9D0c(roo#yE2tAH
+=@8z7D#{#7q*_dMb0UC^ZHgvZAZo~4J5(t{`I{rzwIXX*Xh`;$h<5S^&Q`=-PXK`!L%NULNW@V)*0kn
+fMb@#%~&;t%9|_vZoMyEj`tGg)xD#zqA3ey_4skkcIF+gP?HRQ#mOkIMX@%=gNCr_8s?yiw-<r1Nj%l
+Qe1Yys5!ao}Ed#%kbNFIAb-i3m-CVAAd?~gp`G$hpKzf-WSoWYw!Gz30DlSdQYz&opZd+IKJKkgfv&c
+qDt@!I0YKxLv{21^9_MFaQjr-?@mv?!75v)^D!@uDoK5VJbqyu+${g;2Ju!wxDg|<KOv7!$Ir_AqRg+
+`3HS?8O9KQH000080P~d=M=nUVY$XE#0Qm&~03QGV0B~t=FJE?LZe(wAFK}{iXL4n8b1!3Wb#8QJaxQ
+RrtyIm9+cprs>nR4=gY7_TBsm2^Qj{yxHUU|3MS2%OQ5af_vWdu|KvKKeW7{Mx&_hw6ZQd_W(xGIpvu
+M#n5FI=cXTF~qlRSg-$LFxog{rqNpmpW>bH5S<XAtS;&ZzCq!EiHzXHT9!gPGE1t1VngOyqBrg_k$|>
+$lr|uB!1y@4pGo5WJLTudG$N2B)F5GDMN)E`&Wwl^FB7fI?Yk)OG75xE+#NUAo)cNGP?zf;;4CTGMDN
+;a0gF;Hr<hbx=wPh}cOZukT=M^4iHFgvRI_RmcMQa)&<>@@st~k!W+@O|6~U$j(5g+MKK&Q%xgt1FD9
++sytFjJMP*Na|%42iYt;)U>ufNddVhq0*?sC??*tANmoQxVxDF&VLXZnTW}z842a4|A{gaJb;ZOSGCC
+s}B2ok)-HAGh;?;yDvku1=%b20a2@mNMDHb$~=D0vESj@!hPJGHlLOCBJ1tclZOPUDab5HY`uL}w>BN
+s7+X__GuNnQhA(uk2bMAI3KM2HN>H$+5f!s&M_WWySmkOi4}8(9ZEeEP2DMDP?<GwhA8V&Qw3X6XXrl
+zZE-;uIx_ARdhKL&H2o97TJXcTEy+C=x79d_4+_jCdCboyBZMlZbj~+SQ9R!@?Cm074)c<6bjeiGE0F
+M~W;7?K!)_`&poq&I8SG2n*8jP9Ode;~*%F-otubwyrgDy#}>!v~keZ3Qvt(SIS9~SN2diKIU0nsv2`
+4EREh8Y3-q4Co9}>_E>jiuxO#)syYaQO_f`V2d~B5Y=);)cuzNa5dc1W<jc1bUcq3{E$ndDuQ#o+ct&
+1<Fs%%_^+J}=Hbw5_(8{Vj!2RPt$G2W$h^Mg2j#XXAx1-TRCxyPPA9y)7bRhdW(njJ1QsXW3nLKy-=F
+R^}H@n<Qi>ku`3_=)u`uoQ|{M3h^`|z$0@B8qf55M%`*TLuuyQuHgD_QqV^edzL5{1dVs)xtYb9fr!w
+c-xdhi2gMk!v*i&l#PlzgIt;#<Ns%*p2Al)A;o9x30a9fA-;zKK#{(-?8U^08mQ<1QY-O00;o{l@>=F
+nWIMt1ONa|3jhEg0001RX>c!Jc4cm4Z*nhia&KpHWpi^cXk~10WpZ;aaCyB}(Qe{66n)QET(u8CH$t6
+C`!ZT)*98O7hNKgRR-+vWh2Vf^LV|3kw12<XCO|qvyHvZ{JV0XKbI!fjK29#7Uv8eISgtg*(+=ERf4G
+5>DCM$LaL;if`7RXv_PD$KW4TU5-c8H(C+E_+ggY+RLMc%epi8I}cOgYS7uMxMWcZ&H3s?xHrI=TmgI
+*z-Dl@%JB!{e&81Rfdn@Vm<#bGP-3UJi?mz9Q$b3nw3OFn;wrA!LV7cOjM`6w2AfqaR^9}da9eB?-!9
+;zuyEmCe1u&SG5s{5(Uh9?q40ZE=4q~NM+%ZkSoSU8LyNJPP7a2JL5-hd9^f-ro(01^yr5Shl~FoJ={
+`aba{42bUoq9PK+9%V@N;KgHz=!is!2oZ#IN9v&OPX}HwvN&&Y=X)q}#6vhliV2PSV;ms2p6|t9toYE
+21IkzzDIf@e-qRokHa0YkylyG@9=Y`?48sVSNbm*N9qoI>cQNUR_G1?r_IHTrhXJFXr^tpeFd!2$GHF
+B>T0feq#w2DTszz8FoBFY-VHkxI@Iz+OhAE>cL1JRS*gs5|xrn2fUSzT+fk~(zdtqSWQ8<o>$s(X5-y
+6}OPYpD*@o^Yo;FQ$>7f9qWqZv-)U6VpfiYx)$aoWb4USK9R2O41$CdBd%UoJ$~aWYx1VK&PuT}eKhf
+mm-!sd1-?LgQ$WYTP-mMOB4J%LSk1LUWnq%5j`A?w47A8ZngaLF?<&hxuQ>-~5`l95xOgPRy)Yh)VNB
+DR_{`GZ9asXqWSEJk_0#4&b%IJwHn;sn8)k7@`n&?8Jg+n3X7mp3T~d=NTH1E0rvHFDMH%<<-@9blg(
+aMCMWzI--4)a=`#Q-2=}7bA(_(ceWJ^G-(g6591=+%N^+t@%0P>>N}^2vmu$tc~_`J>*sdoCB;6ZQTo
+dObJ;~|H#+VDFBZL4%LPN-JBW0gpJB};&%+&a&hkpF+MVMfY)w1s6=*j5j7sNrGt66X)$H?&kQ75yH~
+&_tI_+J|sjBAi3#9KT=dh~l<f+N+Le7h}>YUwy40jKX*uC@*_1m-g?bY2C({Rk|CA~0DUi^>tsm0DP(
+0^)_v)gx;F%FGLb9^c0#aSzOO0D%(+9U1H))`{kUH|H)=c3^6leF{uj8FN%Q2uLIzhh6I>d0HF?>OJi
+bb)<EDX}b)eE)jdJ-NWVkr%{u#IxE@^CzJX_F-b_75apGE-o%2zQj*CE)^tug%xhXoXg#ZSc>0Ks_qu
+xch*i1fv?)^%J}0Ae>JlC*<L?jk5HRhd#CQ!=Nk^1H+h|JV{w0&*&p7>QLVARGFdnGWZi1JAC8KqgYD
+~%^EXgS0|XQR000O8^OY7yiZk0@{sI61Jp}*&9{>OVaA|NaUv_0~WN&gWaB^>Fa%FRKFLQ8dZf<3AE^
+v8`Q_YUsHW0q+DF#^-UKd)o&8Y~Qpj?@@3CNNo(z^(fz|dNhO;i>IQrgV|eTJY-4tbFz1)Ah#@+28jw
+s&)j4jgf2zMrAQ6FB?%4A#0(_2v<DZF%;)%LKs*M7p^(YO`(ObUlQJzy9_Rrb?TQws0Y_kpHDD{BhmC
+{<+!Zsv52J?n!Wh;Dt0hWv$XRv>G}qLlkLlL)f8Ii7~GWD3ooDy6Rd9?H0+bF58>jNGP?zhFj!mI@4$
+?;YPJvz^se9YoU}95V4g;UfsgR<aH~H5E`SeRUr%H%UfI|<X8GyBGLLdO|4tCmfnC@ZSJfdN=+kk1FD
+9+svJ^DJMv?R83mqB#3jinFb<0>y<lTHh6jY>{sE9=?3>6^%+d_Tj7KqHa}Feq0Z|!A1fv|OE}57?My
+Di0M2aA!yQ5B`csXXt)Z^HE5i=CI!$UejiaE`q879a%i<x-l#V1T8l=Bf%K#~HzposuJb2RsSol}SzI
+gcq!(hQkM@(lQbMvTNEn$Bn>LS#6+AtFi>PM<E34SQfr=49$@WFETTx~XPF@Dx=uoQ*GI;bxd*=^Wye
+JKM126eWlt4vgoAhIxoMiuN++nk3FpBv_ibeiRlNaV`=%joFkY5p~ei_lq>c#wFhaLLeFAPBUGK{*Y2
+niYy83IsL)Avp^@_1I_Rd=EU<(zW)%TASjLA!D>}@ZD-_a1!~u5(?VA(yft!FsaBf2viru-q0Z`3)mY
+o7x3a?j%pR(j21|4GO4UIStgGBw;Ek$lX-@A-;gMwcC;)tR(%O6@;V}#bKGAG*D=qF%2WNv21}|Q?^>
+4TQG8p=nEnbJ!x-%9p&tnj#lY!3{vV^WF@>ZT&S(SSQ_bBXhsgbG+`3io5O4hz-)XNQzoj!Tv{NArWS
+1*UhsTN8e2HVHI($V_!!Pj^HImch$eeA=BKD-~iJkrdyu77~%-yZp<5C8Px?LD4Y)j#0p_5U9E`cof1
+_u<QTyHCja4Nyx11QY-O00;o{l@>=vjs?i30{{R;2LJ#d0001RX>c!Jc4cm4Z*nhkWpQ<7b98erUtei
+%X>?y-E^v8uRNHRjHV}R1R}7Q~ORY>_iXw&31lR^{H`p{A#O||}mc}-_lBkkY9Jknh`wl5eb{4H6nB)
+v+=A60k3Z~ypp-`o4_8D|`H~q;;7`p<1Ja@}1&lB4F5BmQc@|jymJ%gBb@f6~j|EAz>lQvJhkMNr@7D
+W-$_*aL<8hF3iJV7P5TIdt|_rHFOJr9?q0nxlMN#Urawb+1c;OS%!s!3s|G#t8`u5OX&7!JP(BMbO#w
+PBqRd(0RkD7Uw_AnR6X3j>@%tl?&q@jcpja<H~F*=%+^9(n8IaHaQ?WVGPM3fq~SQix8toX-c{qzC5V
+L#x$Zi@GLYP>E*W5uS-duX&*wVq%@yMt8^<=NyR3)5rDZ47nAjN{NyXma?$%XFM%#CNHAua4|`Nj>~X
+dUZA_=(<&1P5g)f~r6)6U3UW~jD{exR(L<|cV`I%NzFIH*hxvLE#C{V|*6S!6<RoxVND%d4qV!U+7!`
+}Cmw!p$IQ+uc=76B=0d-&T8Yzh-rBEZ7u61?85~g4f;nPS?wDJ>&U%RS;QWafI`4m>FhPmWh_&Lz-OE
+?|Pi2f~SEGtxd(sF;W8N@|GMO^#{+Y@}irlIigjMDziSyssc8-p3VYehjYmwcXjAP@I<tH<@qw_vRZX
+^B?I%J9DJS)N;IE6j5$$Yh8<A@K`pLoj>Al3(+=lWsd%m0mV~oiFFt)KhsbI>N5|n{R+Qi-4@xp3yng
+$gNl24ac74H=bx_-_cyvtp%$98?mhrI$hD+{V*}Ge!L%wkb>L+wT_W=g;{;dd?9yQ)xZhBXKHZ6gQ@R
+vEw{tqwI|gQUQ>CS@x*2GCl)rRo>O@+jT45i1rnfpZ@bc3={{B~jA5*gIC$<Xr9H@Mn#sxxn(&;Yv@x
+xsWTo(=v8BE$gDRL&FMabsq(F~B3I`Nr>NwNB6}C9g($Suxpe1*zbuBkUH)vdG$N1r#!n)rK*Us<9c-
+0?*z9)a_DE8z7=GCB1VIyl)4$mLY4^AtISwCGHrG6L9=^0Cs71pGFw0tI0xKq>tg>&HrEOAY+MoYAIq
+N^-aYQJ~S)<HC7MPo^gAzqX$A$nA!aHqPajNmAA<KJEuzy@RAAOKh4v8LX<{{GxALxf=SNMroI_xr!K
+FDfej<;^%a;o+QQ!^7i<0M^pR2CH34MJ<#rFZAbL8hlW>A81Qn^Slu?%~irhSq`Bt!zKy)3Q$V}1QY-
+O00;o{l@>=)>}^bU5dZ+HK>z?C0001RX>c!Jc4cm4Z*nhkWpQ<7b98erVPs)&bY*gLE^v9}TWxdO#u5
+I`UvZT_5cE)plYHrLOwY)&(rBtkt|X^3bv)3KIEsis0L6=FCX@d5?(V(d4mi+Mnx@m1GjT*<Z*OmJ_t
+}>_9<!4dCv2I=N%k;fO}#q#BR(08j*gB-rTBXzs=5k!%!^u-)#xAH&uDbFPAay^<0cirC#!jqRjghMR
++l`hioC3u9uALr&2AJ|)>Hx><t!GfBoiZ^#cTtkkpptd#IjEEtO`k%QL-UkRla-_^=Pyz^9>7OUXfNz
+y}J=GUsehAvf#@{{vfKbDbqArd>s~fo^E&s&q_Vy9X$F@KAN(k%s=fS92~)rAugqwMYH(!?(W^^W^U>
+v)%17CM&u1>rP6=i@@BbrD9l-xyLOrIMUgM_O#wPpZ(vNp%Sueyf`fAF5_VM)A{(Qq;O+T0r|&QCqO;
+TJ&HJmjm*>&D)4Ok{>_OC#$fP+)MXUvnVe6(N=J^z*C`1-V)p9L1B1%?KCPXY^$%r@g+Gul%!og0L`~
+ja&lvp5Rv#cA?sg~^wB)<SvW>r!rPvUK|tcO%1J}pH-Hev=#vr&3IFU!0<d@yb#4Z*F6%(ufCe8)f0-
+aK>IJ2bm?;`ZyaSk(D4PoFvL1~&KC>FW0OvXxkWdCtVrU+iJaJbz4DI}o1$K$dl+9!GT^{m3<KpJQ-l
+5QCUbJGL;j7dtib`|X-O?LJIdyRF)A(TMfj5qMj&l=cn=JsN5EdUt(&`R4Na?9Xh@zTqk0gZ<|A;&12
+h;>(xj>CO4w&BggGJbXDCjg~2|D)t8a;Zy=>kS~6O6-{O&G@#-gz%0IJcLr+}DM2Pufy=QdO0uMmqM#
+D#N{z-()fB+gnqbO^A~XXgEfes_xXYmA2~2*K*B6^21q8@y5z8$#!<K1YiD!&ct48nBtF{Dmm%eC{v_
+64ofz*2CYz0=SEl<h;JNcBvU|Jk-DHA16PnM`Ph&7^I@ui?KCN2STR%BWbn<%0!B{ndKP{a?v+~OP+T
+M#PCH2`K(;OlKhJ5k6eT*@_WlQboMP1W!;-LVBk&{A+v2J%H#GoqZ`-%63#EA_|gd&Q&40G*MP7%lTB
+z-qPX8H579VHorxE1b?4sxE6b3mBNMw5qI#OGD)9IRJau<F?_Ss^ljjIl-4?(`+n`)?~<};!mh*S`Q9
+)_<?4SLxceiynKkIdd&m~Wbtn;d7U?D%y?akO;LwvS`Y+l)HN@P6o=*+Xc)v<@gxNWzrDI;DL_k8R8y
+T)Xm0ibFe|ZCQ%eAWF`0Nm&F4&KNo>0orC>3yIa~9}aN9rtWl4>ssDetAFfe3qcIY3xNeP-1i#P{Kv%
+F?9;7eJ3a)s<-#ddkaFyOKWu(&4WC7ElPr!0SxR)G)VM6QHP#qip$6Hw(=sF}E4#?;Cs&@RRmyT2#+y
+Vo{{Dk<O(!;8{7Nc(C)*uJ$aQqb;<{dFxsAS0D+YkK>Ll6=ug|A^!J3PQ?kHd2$me*Ibts`YQZsQ^Ju
+@~T`0u&gEEHi1r0LmPA~qnv+j53L~SY_d8Suj{&)eeuP3%J@X168%4lzz)kQieQotV~Y`xj$vi1<N^P
+Rx}MoSR1P-wL?=Z-sv3VB!1!s;>*w9T<6&)2-KV$RblwSAL(E9Zc`q7Tksw*weA#@?I;>|7Yg}CD43M
+EYcsYgr<nf%M?8J<L;y@h6A*4hV;K<(jmJtn;dj}(a{>6PE3kO|V(ECo1wM=U$$Qo`v!vK!mN4^ni>o
+m3c>Z6p7Hn%pZBLYcgl}kZ){;4y^RJ!iG<u|b{6N-VVhOpMpKtRt;nXwp>{2)NDp~?aOP<AYzUFDgWO
+b8k90bq?cZIdh1-sgx=xgx+G;5SQ>V<<ebaSh0w#ZbvY{N*uLL2T@7?wBH)GY^FAC5KwpVx^ua<FKku
+DtS3e`DPJwwrVoc=2W3^QlSJzvV2ShU!*GX?z6H%^E7X?&Zg{%rws*ST6&m3sH}oX*E{;kr*T~y?HP#
+Qa;F~q+>>(CN19UCTCLsm&OK$$v>n6jLC}=FctOk`x4e9)EHwa%U%&(jPhmW6mQIkYRzcWmL$*I4wB@
+l5V;H|Nm4loPVl$Nsu<sbuVh{+UXCl*)7N$NGkR``5bp^YDOaiv4Sx~%HX{907IiUq9?A6|2_WIruSc
+T?)JtMT3{T2zK#ZF}mZwRGG7a8Pjr4Vjr2FVtGf(*%Z6+88in>%CYSWB`;vD@Y)WQpZNgZVmjpkkCLF
+&j=o;Z8HN)s|scE>4R8V%RNmkcu{sd>5N8j<fgx{a`FV0LFa?ZlliJw%q{sg@#vipU2D(^wN&I(!YqR
+B|!Q7u53it!&?7!a4Z6~>6`_h(=a0NdFG9b7se%#P_2vfkGuDfJb}d!kN*KbrFBThqCaKDL0aI0dgyz
+DrP0s3gA621sl0)v?YYBDN&)Qb*}dKZNgnE2%8vo47tP!#Bwwk{BUrCRuO0^UZPJNdU9KPK+eb`voX&
+30vu$a5{>OUGx$~`0-1^l(EbNWYE_X<b^hd=$UB`P`ds0R-s3ogqBg#600y0_ctk6mgT*wCK`ILxU-~
+685fK}e!UefDil`JvSc!uv;rEbRu|5Gtix}GN_%3d6UY4T-4J)3P(uVr})i|@H0u#miz7%<@d&1H{F>
+bEVIp+eWqjb$y{M3ThJq}AM$9^T0rI~#&7E_wKaTS6&7nC4rE7LyRt#j>t&akA#E#uM5YCF#g9L9!jN
+Oz=5O+TEAO>@)?gWP>MFA(kKzr~$_?7{gJop)&$RQL9a4Uj!q|tY`Grtde}x<$_~1)!y;`laXigll{$
+e-J}kV&WzEo5>Oon!%b?;TWD|2PMz&pG+2U!N0W|f!S+I$6D$Z}aeQ`ugLU#>-e26Dza4vsd4%+oZYW
+vzRK^4Rudc7o_w#l)I*|Y}^Jy%puuH_Vr3jG5KBk&@cEt#^N>ZT~@!{o1KOuxOhwe+4uD{;FDOx1N26
+G(g13v|ZpdI|DDqQ$`gS1#LXzbBv92W5CN3ZWSd=T=PKyHDxBvjb>+=NAgrBem|TR^jinLsZ=M{|2u3
+V0)8)#GHZIuAe>I`5Fp`C6tOMg{#Ds=`HOb0RC1>PIz0G&iSDcIV|74`|W_hlCm=L6b(KMF<vSgdVDz
+n<|pJMtyGAAS(7*OTjA}xQyi15SUdrHt_Y7jpfU+YHrwqe8(RJ)7cLmRGcL3d8lWaHc|4Nod%t06*0{
+F^Dh%;+FMaGefH~_MZc0O@t+b0U703*J|*8$^R+5G??{vCwWd~29fYxf1Pm^u%w(~vMb&{3!v`p2%NP
+F&V}W}TjOo6^cwf8F&%jpOX$+1jwoD&il3QAheye!!9cZ>HQ_pRHvFG_yA`4Dqy{yVN^2!Bp_H3B)Tq
+$+`wUx@q47-;qYSI=DapT)@3J*#7m_uS?n2gy|ve}IkYi74*!+gjCcx_U6F!24JS)xhoKzvm_;qu^5Z
+y83XTtQBW0Z%9F)SUjDRP_#m5H>^cUAe>Fi5em?5)YtIJxWGD(CHC#^B3sC>J`c|%)AX@Qv-c*B(M@c
+$o%M9$HehGS&#P1HDc($4HBrrp0daBq(9c`Y4*z#Y7ltJJyf%)>=dnK)NQc`|LybeV|I;(BZW36QG$^
+c2!36GQB5&SY^%(bOhSbuMD3ywUfH{inql#~u8D-7Ib#Qtc$-w{4HeoE&vx<{8jMbLfLpF-O<9VpPIv
+0e^v4EpgSb|vA!cq#$zHX3i_#CAP<oBCzvNT^2wL4CW!toNlW@tCNoBF_BCi*8VXT(<06L)Y`wf58XY
+!DC!@7;%7zP&<<DodM@O%+rWFc@2#8B|yb(uF0Yk)2~{#Ry9?3y$se_wNKX&QE5H|VG#yT;66o8VQfw
+t^#sLg}dU$)Q@r157F=r5IEYx-F|;oBC-4G~9j%Bvb>Qo&U8eA4bg64lYW6rGB!+AJE-^&3z6KmFrtB
+t?dMTPodym_#6aE+8M?F4|AsK=a~mCoZE4Z0DG{@Bqc<Q78N<!Nom@pt$*=2HtAZ&zh1oX^;|K*LG;j
+RrUZ3;L#6fjkew3JJbe|ulrVy~84M?|;5BaZ!n*gcXs{+^saVxyuXt^tOkzcb2CnXmYP&Vu?8`B~(rE
+v0+zVU;Hr?!>Coj>BVPxEI3!97<)g#Y8vpARxI2-PgF*dhpYqj}@azxk3ane?7S_S?Oj8P}I2pX1AXu
+Ou)f4Rl5n#W2u=X3_T;Sb3YSwW>F+uR({UhMEt$qHr=6_+(82NVtD{T&qrpiY&yS$KvmZO-Zj9D&S7%
+a*L#0DFCPb$)i&#uthb|HZawa9P#idbcF%wQE!vzAtC(*xt3ui0Ry2{n@iczs<D;u!ZWVvy5**WoVMb
+X5PP+tTRie`Kd;li%vM$1zt7dhcTUfoSXv7yi9(g4(1pS96DD*10zkZ1@)|tmZqN(Dv3PzH7$S#Nd(`
+>OEOb^myK#R-Pl)0ci09~&xb3!TbQ9@T>w%&7^Z#trrVtP9kK?wPN&Q=*UxhGRyCO<d#a*6jFgHTPT%
+HRC=Sr8a<=73ZHib~@N0l{HkvL4E(B0gtxMifO!4wGXR9>lus3*5g9Z1ddek?S@miC^OTA&Q(5AlN@e
+{T|;&5GW(qnx+@%4Q)0YoaCgYkM-h;~IZEzU}0-G%F82)_nZPrh@mPz@YG*u`!p!}>`fujIzQvxx=%c
+BH`*l7k9peKd-RofSSXP!xr{^J#6hiy%(nqARjam&3}%+7hI5+HN12$-^>dlyhj-A{1qBG~O<HdgYEg
+m7TvSQcIfUy2&1?KE*I%WMAYl9idQP6ZU+3CJp}NQe+SHdW<1V)0)=ZK*P;gCK*nr;Z~PBf0rGUDQy`
+<QyhA9+Op@)4`Tu~eD1KB<3DayEq@fizQ^kr=En)1rvI@FNE^OW2Q{3@GYItL4X^1Uw?GDAZ!A0p@q(
+j^?1`sIY()O~<u4(7N4H~H;0QWlM?;u@q(_q`6$D_wPK9z$!jgIqheeKOj>_xJ6=MuubSvd3luOc^Hk
+tG|Nfoq83t)-+YL3rrl7n0Nn`KKq1JQ?R(r`UzX%IiueHnD7ybxLc{2o8@fO)RXSbp`#Ti`QC$Mn8LU
+zyvE<0sR%0y=KvCx*b4U@oabua^dl2T=q!FIwl-UfQA*U>|X$;?_P=+3P-pQeW=vUXk@v>+E63nLDSK
+c)kGXloyHd<3FDR<5=^G&Mzdas@s^@K^l9G4Dq9&oD8O=9`$I`d>h%ck;eU?v$RQntQY2NreV?4YMsF
+$7U}y8TBz?+JtjrOmxzGoY=)9KkM#J#q(z#nbu+YM%l<o}L(7$e0*C3q<YpL{gpOr<<V<YY&?65Jh|=
+G+_!Ojo7s+Ju;j7t4d4|=V`}D!k&t@_Hq5L5mA4;D5CVac3FOgQ!``s4|*$e_(F56R1FS*23``4@T4m
+|d#pi1_GER7ttO7t{})59cfW;NH+?%{Vh&f&+0bzb7WN@VijH>_i96K@4uL$Z&}2SAlY3ZB96bQrR_R
+PYS)`EEaKfEVPelpo5#TjjF*u*Hf$)7Q_Dlf3W4%ugNt{LGHaLx6$3#;N*C_Cl@{@-$5`U)+i`o$3;^
+^UEFG4{>hr+8>FkfD95<wWOB$s*Ejge!i-Ijulc0HlZZ0?A#EPy6gPp6Ehc+c+dN}v6{{Km98g6bhBN
+3DBb-%sr)hM(A6K8HW2&7+Bc{3JK4e6+U0zHrUA_zpf~G>$sVmqzAZ^0Jo{{ZDLl1)Up@K!9!%Tc7!N
+8z6L{BRlZ&*+z56@pJ(@Oz2h8JhFX@Wi?hU0}o!dsH4wP2`{s~Y^0|XQR000O8^OY7y%AV%vI|Tp$Cl
+vqy8vp<RaA|NaUv_0~WN&gWa%FLKWpi|MFJW+LE^vA6S5a@<Mht$}uOQk7+kqo@c^ZVqfRlJjwjc>=2
+YXnD>MYUr&5};LJH_>e?YECSogB+answ-awWuE)**<<B$&W`eFX8au5E7NjVi7@Q=ZD|&H$m{?#fw1W
+ZxtGALQ%@#ubo#AOqbF?uF@()ka?M5j)gT~mk4jX6^w2!!XSteRc^FgENukcqz6Z@zBz)~27bh%K)a;
+l(*7O>Q6>{C3`Q_2MY6=BJ{Ux}yf__SO~x=+n&>n_idM+XgtdpkT&o;Hc0p-N7RE?bm>>vJoP+njn-h
+;p`%vku4+jUUwa^RGivpk*p`4P1)m1_+iy|eEBBdvYl1vyA#XcT*?H`6Qk+W%33N3P|(IfxYdCF-haJ
+>%^gPQ${i4Awn$*Bl#-&}sQp?f!!R8?9ET4X&qlZkuO8z8XR#v)0gV1^u&1z9)(f}{f!GHJeL-Dhl3r
+NW9Qi25~6p9$wO!^2Fj(7VgroXv)Krc!cj{yg8WMinhS{=#|juOXKxQq)hUYYykr=?$FM$F?+UNhSI$
+c%sxw;zMvw%BlP92ch?q^3R|-ryue<jyr|Ou@l{A|F9-9aZF~KPW;gv>2~;#`_Omi>16NEIPTT=tIDh
+-Rts3v9RaE`QzC6SBr3aX`7^3G4myb(8llhV=|mWr49?_&0veoF#?m!)3Fgx+JS%flm2E+ut2Jbb_z7
+?=q}^7jL4AjMn?kM#hsCcM&bdJ><X!D~9|T|HIYnSyc?#&piK4E<BGjyX>JcJsK5e01S41=r>76JNOv
+9zkvzPvi8OHB=b+|qZyD9NL3P;Cm?no<fS*F(SG^SFsI<0_Dl*&*PAxzK}$W&L<SLbkZJ(&(}Zl{B@@
+#T0rhQ^u_!e|^Q^<jsM>8?Ky$Y#XQ>K<CXf2?GEW#ytQ8J&(<m8guJVga~O`Rm3Ao0gO$jht^<kdiBu
+qlx3bvzkmUAwjCKwahZ^OwLG>_b`If(Yuq|tFy~7H8(1%L=+T=aSe>v?qrJXTBII^B7aEu9a(J|HJXw
+-Jkl*KEVsdiS>`$AM_9}@kg9dTrM^eNp6<g>T;uH5E^;NB43bPTvI~sw6?DTA^IixSbC_}=^y%*LuXO
+J^E8KK?WG=Pm(r7D94e!1Yc#mH<Ps3&ccmvirg6({6j@5JN)`$g0QQ*sYI2<<fu`$^Bg}#F0d!%M$cD
+^4^+1I7DWi%M@pP4K|r5A$*+D^|SY#oH3k6yj{W!Q|=n%j?K!fyt1IH@1HHNaMy?TN5GvyXR1_i=lCJ
+AjvPvH^Tw(()iJ$(UryaM{+q!XfryP1oU0$rR{{x!zz@vO=2ns>A};OPMS^7#AF=yI3H13yMuuI>DcW
+F5FEk$TFuo5{3sJ?+O_;cW@I=(EO(bWy2&Z4@U-yJkciHO-IO1;x+>;{WcMPaiJ7<U9|O#KfY20KDN0
+`{Hj7?H4?UWlh0wfczf{H2NuD#X9N0JH7h0c(>V6w(M+>qh31proL1r1?t}&{sUc`Ab~}3wmF@<2wLK
+C2-QgSJw@oi#{b#p&YoY7xn(h;uX`}D;&Gh2>YVwIS{_+y<)7jZy^?kN^2YfEo=i|}Y*F3vCyD9p3qB
+)aKWw}(wcQ0PgeDC4~{@FIh+s-G>{TJ|`%m1)^_RE1KZ_2@m&Tn3HetV=ys)sf5fx@``-0R|V5V3kp!
+w13%n=C&@y3zFX{Hu8Xxp*u~Bj1PgOEE)>&rQMYznfq99NbT1ot=l1?Qc*^0|XQR000O8^OY7yDP{LP
+eh2^niW&d_8~^|SaA|NaUv_0~WN&gWa%FLKWpi|MFJX0bXfAMh%~)-38#faEu3teYFm^YQHnEW!fpJF
+R+U~_@;{>(~<d6mnN=r$sSG!x1Yspvie*4V~$$ga^_ru|;56PMw4(Iim8NOiCms2(`QkgGLSY;N|cep
+YdjmP7W62DhM8y)k?tVaK7|3;&`mDFrqq*W&1d~UeRH8U&0kXYnK&bg6A&Q?56GodtF6pGz=j>LZ?*J
+Ys$E42So!hadLuD@4YuQE9sjTWj{vl-Xo_$_mLv*WiS2l<8DjpxO><i>ATDl56tV#2ghHAQhRh4v(3Y
+ZRZG#4RVLNPgp<wlXrSDTz~(N+4bdo(eT#K%9e~4<Z5nU_*CI8*VC{pj`E7`eQUY|9E|Od3~4M{p-_Z
+^5e&w56Le#KVM#-f4sQ7V5cnLWtlnKz1~hYo6Qsqx1NF`R?jr(viR`J&v#eJr?Z<oJiV?mBSB8{nogz
+M@L)6=rDDMnFr1tx=pGa{th7)$UxR5%uJxu+X>>AT@B`P}2vaHWCucw4A$A6bnAI>Q&mkRhf;GI>QU&
+bQz#t)!;;;5p7zPLW<Uj}PfbEhZiXB~$0jK;hfE8p`h7mD~?DdLd^iqJ5^9(qUs349Ai`j2tZW^6%ky
+#qEf(YSRMu*WqnOH-}lF>B-$AbI}Jz48Y22AE~CP_jqvc-fc$C_l}5}02X=F0OD>7`PI3Ij6T3StYgr
+P2_vAOx`CvrNQ6REz!o?(P#=5$Qv(L?iu~F?_xfmayfOR;HPP@^1;k=RVVzwE{V<(L?6a)OzfV1N<RL
+>?y$0{<BNq4+4Sze4g22n@9Q!nW<3n-=s?L6*%?dwIj-;dvVmTv{9ithW(_9?IRhsW9EI<CGs8C<<GJ
+0bCjhErvFowFXR$Ld{!`7(!X~!bqGL7ljKD{M;<}Y-K2Fu-YPe+`=+}&U-1l9|M;_aW1(U7y2yKy<kd
+QP`{s~*iWG-URL?4zrBDmdbgmjW3#<jWHJxDT0o7Yx;@KK_)N*_YXeszwlX#U{B(&5b`MoIH1o2XsFo
+4CN2?w4nA*#%3JtVBRTI5B^!M8(NyB<W126EBj4aFbr)%X^FN&D{L!^L-vZ$ze_BCfyVsw}~jjeIXy#
+@DlyLow32g0QhRcdZAkD&Z=uNEyI72CV@rW#U0(ZQ0xRIq4X*B*W$D?d9!l&=nS4zD~bOphJ2WZlms|
+yFhAafeZ(Sg<I-3c97w33;J&B8b>`>%O6d=P*%I%Cx*-6UVxCT)j#Z>Kr#{zczZcpwjZj0`~GZ)ih9N
+We=5S|htJi~3))2IPuM-#P+@w)(vyZh98<qB#(o30qHa((e0K)5lakP(6_uYqe};rLP4@ud_b5zb^xS
+|JeOM_IY9bNad}|saP&ABB0Q9ga(ag2q(E3Sqj?Ceh1T6mXoE;q>9<$9#&Y>$?6;+m!D9~Dnb!i@(+m
+O>~eQ8X8L(Gp~kJ|>Q_u)SE*wvnnyn=xF9=p3PWn+%BLri$GvTy8qwTrNBZwWg)s?XD5kD+*b%USQ~p
+2n|dhf!<qsJnO6^|Axk(|!r*syiqluMqWXpAJB5N6Bf(^Z5hM2wQ`LH}AHnS)abCHyG5u<hKZ-y;-DD
+Hllo*1ShF@j{k<=U{>h3QZTL+>+oI6cynKu!cL~@PNsf2IXwOvwGI9}VC}oK8$_@>u{uti7`@!ZdblT
+Rb!qeOrwhxb1p<yEgiI8*oa^|ZwAg}Cd2nBlcd!*}n9ZOzPJ$lp5yP28_m8~-C(V5W<Z}YJ0P(s_+-)
+S;;7s%l{k#m(6X9xM%Z}tU{7hwT;%~#<u<fG%`*`TE2um=|pc0xb8y}jm$<D7(or~|AsSu|!Ft|sGQ?
+TFI!G-&#tK2tWq5BHhuYD5*XcU;>Eb$X4<gAV8GVzw|#I9|Ay0#-p5eX-5yS~N#xwB8&%c7>JxzsCdM
+z)IV<zlT{v#`{?{MdUK_D;tb1Zd;=>4Elf*qE;upi#S>r7#KH@Zj<lxkpWtYy&M(B9#i}20s){*vpsq
+8?KhRl}7aHpoL-Grkg-eC<YUWXQS)+!zrm^sq0I2;q(4B-bo0%-xBVQ-wWLuiF~Xv;J}Q+2PNx`DVlU
+bB}WK|_vXRGiCd2x09;tMhv4-B2Cj|FZ6+-w<SLL$8p~i6Cp18&VnN%pB7MYRZL<<e^tb>ZZJ=?6U65
+8dey*+PWqs_IkzcHZxDUOYO=k2B34;PR#t%M0XBz^nRh=~rwQ!`Kg6))n?^~@0Ljk9P-pmnLvl0edHg
+Q&La7G~0cV+qqJ8I2bdm^Ay)A5H7h9H`t$3rl8sNAKPCX0+O^(ky#{qy?c&E@&o?PWB)!A+KfC&CbT$
+GVz@0kE>-Pi#=19=2<lb=WyX6wWyKpb91-1(nDICN2670AeX~xCsby2zH2w9tV7;$iP0Vs1yX?#=F}F
+LFM>poWCLL`a$6C?S}^6#7>F?FYfi_r4l81)`!r%C?9(?yB}FYJR1AONyB1dZynwf<3BL+`7>P(J$us
+L6E95UxLt$%p!O1{x+%p!I!!zN5=Ay>91~;4k$f#k=^cqzQbR63#vG<zpjHC{@4i#IwyU4R;6(Kb^$_
+oRC)BfE=U%X@1=|R~$Ok|<h?xhG`qHWIWl50)_YAOYUvvHBbKhQPsM_$VYjx9~Ra$R3%Ho(2S=Nd~v7
+L+3urt6cIjfAIC)z&rUgf0N9CHpZw)>a{eT>YPHj`2d{T_phML!j>4N~Gk7F9jW5gv2G^l2vVg?mw(A
+i!f6jE8z`y7=!<zvfG7vP*ysd<QXsW>`Z_(PMHwNcMdXUl?*rh)SUne6<eirYdmLTT?QlzkJ9S=^c-k
+{{>J>0|XQR000O8^OY7y(@<J|OnCwT(zFBs9{>OVaA|NaUv_0~WN&gWa%FLKWpi|MFJob2WpZ>baAj@
+my;+l^O138Y&aVjTemQOGDlrc-I}gl45=bECmut*o(u_d-`s+>%J2EqJXYQ&xr>pJWZIQ7f9B>>D^nL
+4FYx(m3`C=c++sppcU6brZJf+!-a=NxJpzYShRhHae?x&agA$z%J*Y@^u%BNrS(^xi5^s?*Xrc99k`L
+ZdK?0C!m_siq&FZkclzrSetOZ1YQ&d-lacAJ+zy1g7v_m{N1-LGZb-Lv$iFYgC%LU{wd<Yfc82u|Hg5
+*=So@jWWR|Bl&9bbmSA@8`vQ-oD=X@26`q2VKs=&E|hcz_r@#)@9eli)j3(YRfO;GtRE|wY%M4)aDdN
+&5Lycciz4pJYCPHYjiJ9$Cs`1{rUS-)4@Zx|NWvJlfQ$$_pej&U(@*}inC_%@;`pw@&ABB$gX7sP9t#
+){H9CPi&7qo?0UYIM=-D?S`g_XB^ETh$gu@VEU09Wq6-3F<k2E#7kIp&xJAY;sA!SKu)`&fecK1eELd
+#8a0?1ur0fDq7971`&_$9g2okoS+#<>02_(57umu5*W{aF$Fyw20I7NTkV?@F8aB6|E3pQQg*aAZrQ3
+U%X;{}Q@5@r!Ii-caJ5gaNQHpVPacEOR0j9TRJg25Jf4u7EO1<fo{YLU@!lqfi#M*ar>6J%90UPRflH
+ANG7Tb^1GUDHIb5=B1IMZQoQokvx1e^K{ZOh~3I2)x7VOJ@*AekBx!EAWBMH?FN)MXwZrgt(@;mV0jL
+%1&Y8+%r+RXW(1s7$=9-$TzPS-RxzZI@Vs0GH=x+jrB}@1JxAcfy7G}jXbVPo_(?RsA^Nml`hIEk2x|
+w=#hn4iY6OW(7I1|xxx~{EG&F@1g&z@lojZJF#F{pF}8NmZzXxuCH}d!`Q8I<`g>cYV^dotV7hthMKh
+4V$W^&txrj5$fi48o9mRvbd%NQ!ISP-YRi@GD`=npJUbK-1>h*DnrVRa+gCs|HOhv(t@c~QK<`EzDn<
+h71@FWLq<PWcNpxf$Kb0DWTB*s^VSQPd#&Y`-d=HLGnc+=r|QZ;x<6iRa~6sG7(w1~5cn9$4X5-D{eR
+eG|!t!z^r(Jgnj`SNTpg<EMhY)OW~na<mub!IZLUK&NttbM1?a%bM$y_LO3t|WFZ)r_Y){MjVjQ%^3<
+a=E5`esc`v&2bjhH>^4tR}B*{r73K)DB1O@;`Uf@Kz$NHN$*nQb$jpyt&rHglBCpKV3o~yFI!WSc#{{
+tdbz-33A{>N5w$;_{d;5Kcv1$DdD+Paf}{oZe5c0K!ITa*!CZQ>9|GOeN!b=x6l2ItZt4dVV;T-2b{6
+jXcpPA6K<Cnh#hwEm+{NYf$Ttp6&+dMi@7z-m&$bNj`99Y57`;^t-K@_&x<i_9_jpova6HUXUvuvRed
++9hE$<2LUgOJg+_hJcIL}`{P_iNY_;LCcVeLcI-!lkGkL>y)@&NkYnnVC?U2_L0@RB4D|HSNiFY_{a1
+w?oSpo-gzR6fcZX!#CZ|D_$jBkT8_e#X<U(Uh8Sk6c9fB1;z(v0woDC5wcG00w>q2t<>MB!&=+rC*{c
+PCy6)KnA`jY{3CI!xtpBAXx}=F$u8@^pWC=h=5-?1tFG=7i{tqGzHgy>rnuTplcigVHPa_@RJM#NHAJ
+FU$FTiA{QvOU?Ygk1Q>9-0FOf^kOv|bLN|bSl3rjLd^7;*BsiKbGVWJs3cv=S>G4_cy(aJl_-FHmP}G
+-0U8-D<g3q$4z|Zj@$s3)oC)5!Ry6DyNfG9R^zoJE9OG?@#gw-T136NFkLtEbK;IlGaFzBnPZ7(=6k#
+@R;_G<qQ$AjfWszRjd1j$`OxHkYxUHBBLV3bd~_=a_Zo}0M)1jpLQGY$~Ckhm0V0BSgBXp*NOM}Bxta
+DZu*9O^>y4$G#i9u6rO9l-D=IfhMA3X*BdEuxF&=MVXcw*o(bcLDglft?<$(Og=mz)#U|PgOCFzQK0@
+pqd{LfZsj*H{o{$?p5i-D}Fl^psFt??(@7M|H#CBo;TzlnYhpMhWsNF_j%rs-<`N3WP8aBR_jx!9Uh6
+nLsT_+2`K`U2TZc!PXJzxZSAOxHpjv%b>q@|chaM!yF6X4B7QQP`MN?aY#AM5r$|?Oby;v2^2};2$Q?
+Ddg-)?*k#;wHaK9UE$dpkr$XlJ5JvyTneMy;LmFqeV9{Z$VFfox=mgpm!S!Tuwe=^TU-<Ee-KX(qXyU
++c^2*^aLakuL`<B4V*i*IjhEG)G>b^?!3fWH;5R){?Emd>+q;!?Sng?Vo(Iu8!!KRmMxpScKRUYMWt$
+Q_v`8B~Nhd6K=C`|5TLFDkcEyEt+(qe#@J99Zf>y&be&(@429b9a%tx%YsomS?QI<C<O58DbjUX_QuI
+=ds4nZAXiLgk?L6)?pW_V!@!9!LL{6XlHmB_2lD{z0?<yI6LXFw8bGrF6o839i5n~H&2GmpX#n%J=VK
+<S?XOPC;$0ZnPhj(+Oli^>lo!<dxP(o<%iCH$uKCr;PM5{L2DYLp>@ll&>+Sj{=+Z8JPENtnFXCMi0D
+g(q45QiL%SM~6An%Ud_pWZ4pIt}!b6}-0QU*E$g@QZ7=~C78KeO&`kB2RL*4^+J@^9p0T|yD3GH<p82
+&i?0%N99$U+<jhfT!rI}T33p_LD42Lr-mGSlf44j$O<5is$S1qO5j{!4~gL54Ytzhs!P`oE1~4!Xd;1
+7+|BPzuk`!_KLP77pkPfq)3?b3xx1{i^s31ht?Xnm%dWd*r+KjsJ$QwSc~jwhM6Xj_3mEOLmRxQqk-7
+E8FPG3AAb8cWs3K47<j*?x`;l@;mW!77*QXdij+#G`S!A5E$~{kvcG{L!vr^T=C}X1&J*>Q(NJx9049
+N)rFgiyh#n`;~9b6h>r#qFM)DulOn+NVJ-3Phk8OM{6B|nQ$ckF6azU;MhY52UoR}R!6V<8WS3_1`G@
+3C-U&l6-{%D~5Aun6ra#wrlpQ9^RTFy+7VohU{<XkAK7AYQriHrA7KP*Z{hKE%Jiiqxlkbb579k{~Re
+(pQhu=3{hMyE6II$#C*D~U!vB?a5+*sj~84j9ZpZA=2zX&Im+04!!Nzc?OBz=ye!Q?ukoy|95q#c~Jj
+8Qc=k*>h^!oeD`)o#-~PyA6Cw$f&#6?en3wbkJVWgQn~zIhVWEIQGL;}*9XBY0}H@(6Sb>6ZHZQe*}v
+uM7$m)1{P%d@u8Sq}#e6_Y(XQTd2*O%Q?_0Jr7yv(<37vpEmH!g~_*jvG@9>qG=&SrS;|FW9FQ_=c`N
+=GQTB>;JG4<BmGoZ6YX+GLk{`4;_Yf~$IMfyR}#hCBkS=HhJ&25&ZS?=@v3JnF^@<3f;Al$r&cK}tFz
+{W5xLTlGvDt4tk&0RKJ;kQw5L&dN@R1V_F+e9&&168L&Mx1F>9qLsAlnFb1o9cEdKL1*=PHo@}(>DXn
+>gi{^LGNx+~y>4+_X$`0jo<UCaCPGqe077r(Q{_oshD7aU5kD2ZWT(gw{y(ZK<e9)(bglhDp*0WQaj7
+>B+SVAZD>WRWacq~w>h0q6m`TDV1&E`Uf$i3LW&A8>5Jkc$kchgb4Q7l2^EO`uzdhSWm*j5bgfG6VyZ
+2nxkW8bkI16vCp=!9!rMh0fp)3=P#2&@6zmfDp>CMGUAZ1zkg3gHGNjKp2oYxCNd-R~Ezmk~TC6(8j5
+Krw!Ri8Fc*N1MC3m5Vl|WNK74Q^*7*JTE2osUU<kP`L2t%ceJlR+C>2Jo`EGTWC#e_9~1x@d=~&R_l$
+gtdc$H8VDVG0WZaNsOm`s`%ipvips}~VV>H**2U&t2K8SWFTR`0*^_n*Di(zB)*LV0_$?kx>9e}V|rT
+z+H?q^H|zbhC(xJS}9X!ZrTAzz_e@8W}}CR)(ZMO&h|sN$BQmVtO+W_V#eWYQ|NWM}0%og+dfk^q{w#
+&#^rvm=M=afmP@7vkL}B*nQ|the(!Du~&5;00?p-#vQCz@)CZT}f-kBgv|ET}t;@bLL0aT#Q6L8D%Wg
+*MqgJy2qM}y8v~WA#HNU*i;VB(&aXrD?6eXm|8?$(QPXNs_ctGR`lh>_UW>>&bpfPVI)*1RjvI|NWXs
+v;46UqGw%R=1&}Y_0Tb=TS2~sC-jbZUA9UgLYWedfz+zR9M##-Yk8CCFsoMdn+s@!s{09DwmnJI%wgZ
+n9^vhS!t^}dzl@ssmx8Z5Ff9!8(-?zA;LNr0x=~QVRIkR7y)5%*w2xLXB+Gtl_(d*j9&^*wd;MU_ds8
+@4G=leGuVk*eH2vI+5kB}2kAU=5T7{g6*E4|ZIU{n+_|AWiRba-U)c`4O)on2Xuqp`7AQwrlLXnd^5m
+9MXC83>DLA0KCU-EK2y<y&n#ujQ1g4jvsK%%z%px5{?oRhzhZ){D#QZhtvyXe|g@C}#7q%W2JXuI@49
+3?3hEt+&xm>4<mAGCd&J<8%qjtdR1{F~P=5vQr(+b7g;iWtnGlx<u<`mT?q=&TayckiSb6E`aBKSsY*
+8_LE9zqTB6-c#DAm4N$hx_4)Fxc=!o-{0n+}CllD!4RrPL*LwIyp^zL)5**Hv1jS-3&EYHwI0z+Zios
+}>!Wa}MY2qtF!q7#YKym=GAO})_h7to12+$fp5CCwerhq|`Lhn7zd`U=vbpX@@cHkzb765-hLV((`c=
+AngP=w&|L>4jhWq~`;uUv)ygpe`@#z80=#w{`+K@vDNE?Oknf+OG$bPR__0l5S20ZxeFX(SGhf<_931
+P1gJxG<(5<I*TpSYTLW1{BgS;|@DWNRcDTOOd7o)Wr8SdatT=*6;N0`h&<9fynRu@s$CP&lI4Hy!%c8
+QUmPTwy`5G?KYyYid*nuw3FU=ciulKDOQycNuK-&qy-kV!#Q|D>$tfG{)+mJ7NAA_6=C6sF3D4z;gfn
+GUlqk4>fwF3UyT;|E5rS2w8(D__fNtJ$RF_pY2_<z#vZygZ|zapU}e*IR4&b3EJxfO_<n=hyAZdQJEm
+9#%coDQF9#VBqU((xI5MYby9-bJr8GF~@>EW(p$vp}9CwyJ9Nht}jt?8%^l8ejYdzX(>MW4v<qgp=u3
+y_x{S4ycEj$%ZY}*{NgnJzEXkJ>1&JDPdp}W&jscoR&3h$tt`#-`7$T!bbyu}kXcU>+YyNwv|_F&pX6
+f<-FWF53|TnodoE;;so^J!u(oscH((+clz7iFm@Tv_+ZuD9<&+b;vbCDpxOE(bJT&MA&Rcsh1gW`?|6
+^`RxTE8ln~2+wm~Wv)XeTAP+L*4-f1yPSXWJu{4t>hyF~ood%Pb)NHioIa0wjc?m7w*iQUJXZgAVe8A
+^UQ~SkAIblE!8ne6`L4Ua!HD)t^m_7`r7ztrdpRAO=luP|m)D4^k=^^#wf@&4^}l><|0M2D<M{^sX$E
+I0pgC|7CoqmCS(3sqiX<79MOnZBEQ`@7{T28_E1!VcgG?YC1Dl=!dV*Zg$pRp7f<YEwQ;45b0s{<Rfj
+<lIoPn|Ch=%bJ0KGts5GjOee2SC+K>_4UnoSyEieTs9K0qVnKLLN1fEN?V0>hy!0Ym^a62-wzf#d*u#
+9~m>fD_Oh`ms2`F~lUCA~=*JSx^~NFQ5_4Ch}rn;Dlu%&9U(>)d&xR&g~6=KM~H0e0LFOLS8n<8J(>R
+J@_8{6$t)CB2T^;<l9Je(kteh2fwGh&|jS$ygA0@M?9I;rPI50I-2-!KoUYPbO58Nx4K21UO3CuIrfx
+iwihJO0Se65bAZ2we!tNLd;YG;$rI7-Q@`ckFnW|v!Lu%OyQa3H@Z$(dcv1LBO5u?D)+r`dXC!OgT3f
+nfxECIAJ5ajCzFMPGfI0lsiQv4&m$#vB5#Zu0q#)!~G&#do`g00;pS17i4Ed{*_Whh8e|6HnpEKmIPT
+Kc#hWw>T`+^9O9|P->?%!q0cZV~&e%PIN)?14UTS?q1C#SGlY`Nq1il*6zINp)Sd5h|-mF%|j_Bh-7e
+xJ<bV7_7lc2-*Rjp~itZ7B0{q^o@AnTh4+OI&5JW?!?;wm=b#DDunZd^ai8tx(tZkBl;7FSW3JT$(X;
+balq?4wyWaNL=b(N8XZb2Q(=iE(rk<B3}m9%{L-c6l093<Vh9bQShF;-H@9a-GzkEw@F}n=Y5ShDCWf
+b?jgAtU*fin&6vz>0rAnXytIUaao2CFaXhWfmXbXS9XIZH90u(Y4+lPDN<^UpokV$U+cQTRwZ`@NIz-
+{-kyy60jfUY&$*Xu~4Z?Lxdv4NZri9OUywpejgssm3a$4bO6fU{n4u`yE{Y=0=r4&tZicgKl$KP%dWT
+CgC-zO*i^~*ofkAArHo3taA0FsHo8Nd!4gEBPDp(ur;<X5}^40K?)Q}_a-AY@aomOTbjeSk~=7|Z307
++bIm#Ao*F;08DzPYFnD3@Ifi-yBl-6vYN6JV5dM^$r-kRYbx_H*|&lEPS3#9Ra~YqR^5DS^|s{U^_7T
+6TmA|P(PiBAb;&Hf_xFvla!c-0!b}{qz0%h%^}T!4gmwgOsQXo&r=)lLW941sWt*X{U<2SJ?bg*OI~o
+l7X@bR464oh@1j7K)7z9|qZsE97|8K4xF@*$$#-<K1?)AKs4!7^P)9y|ccIJmw--At_W-~mnKqv(JxT
+!prhpb0^os9+C=eph_=J&cy(*?g*rqGVw<uh%6x{;G=11&VSjH+>B7rwdVn9<VeB&Dj-Qs(xc!Rh;ol
+=CR;D>eVRrAefGOVVH;kC$gv9fx+JqmR5lwS(~O^|vX9$)h(FxjRQNnI2oRYF%JI$P(D$54`b6V1-ra
+xPzPo7R=hJ;$m$i)_<&9L97U?%H}r8)ZNPYpUhL=y67dC%S*qx5QrZ%ae_+bscppXHF97UUvX6GnfQy
+WQLe;n>~A0I8r5C!xp!GZH#QaJR_KQt!}59cB`6<`+zOgCSKQsa>n*#tR3}sUbD~6=gQpK?^Y|axyMP
+W(zc~8tqS~v+>Xxn&=p~Mcki*`lFHdD>7-b1w~yyaFm?&Hw`J0yRi@93jkghkCzX=jfDdd@N4FK?;Iu
+g&Z$!FjdW(`v$J4zmqK9iOx1wHzYfe!wb>)TP)h|YSz7elNEHu)-$P|h*ScEH;6EnL$oijJDDQWi})i
+x{eO1=8fQt36QEyGt`sQBIF$+9Nj5>Qq)FTuEjE}*X`7p5l&R*~UBWSO}CJT>NqK;_ZipXT$C+yCgTb
+fC9V1RoL}JMNCT%;Bhp&za4od{nlwmeyFl8*WFjzdp=1a~SUGUryG36F1^|J0i~tJJ_kAim^WHX{ibT
+O|aMN<7yudCR3BAS@0;!fMICCbRS1me4yl*n43gVorWr8gkX)lF&y`4S;A4b($q6uDd${$`&6qrYcS6
+z6`(PGd5rl(J#w4FN()!lqRGXUdA2)*k7f66_URdDr;S?InLv-{VHkprSwC0VO~LSW|2QlUEqNJ~U}#
+d|SO(5@<~>{AiBd#bUqouvScESUI2Le0W?e{k^tcDf5%p3RXsU9qobAlbE1l%r{gH`2>F-Ueii=p6k)
+`hBXH9r0duc@!Pfa`!UfB5MT&`B@eRwAM-wmUDOFQ4t4NQOik&*uPV&OmMt>5q9=gc*o@l*PuS)64ln
+qo1W`ij0d5^4}2F_;We1W*Hc3KOdc6ox8DKujpig9U5=6Kv71eOlBaL!n;@@COE{g__jJBy>;`Dj1+c
+(sYtF?8HV4^myUmR@qPJ3nqHu@EUUReZlKdnAi*WD&}D70FW#&V#2{RvN(sqd%&0x2FDBp6r{BT=AzL
+#ba9c8vLLI1OTbYs`z3vKP}W@5KhT#%1}#;fU(%N?$p9V5x2W)-`|s(kZ>H=A=4&EaIF&ZQ%$Rp-6F;
+#N0_f?}LH(P9;MkOBrAq-|B^jG^!bh5wJhdSojg_G5d%j=3GtZEIFwA!ZQ*8Fa9$X9~rXPXr^*4|3cJ
+M8*|J^Y#UwiFiUXagOOn{npJaiJ8ClUNC@auTD;_2C@Wlyc+io^Og)7*V5a>o3ylxX?7+aM1z&4=SmM
+H#L4jhMUU3PWstGFSzx`XFJbvdd<~eU^~r%{d*I{Uu)SqcFRyGu=C=ERvlz*T-o*-{_~eGBbaFt}nuK
+hAxlUyewuIf9&JtS}C1<x;>0w-(~kO&o-uT3Uj}1F38<(vQn~dk7c2Yt5S~Fm9b~`MQ#q1or~lqxgDN
+&_vR7A;Uu>5DL>kceB7+mwWV#w6cIT;9Cyddsy|&5YN;Nr?)KClCvq*J8%o^RdDk0<2d?@T1(4@6u(q
+1Nniq9{wi@yrA>(Cs#q#VTM(5&@j-k(S&x^;~e!&Y*=SuHr`y%xEfj`mWv)G9($-V`?x55!fA*8im4W
+p&WPIUAhT=_{vJbgWvl$FAC+L^XQTs%pZH{l}Osqnr!-3hJ_4<*?f#neGAh-?odbl~}m6&W<q(n1sI^
+w{9WGZv{M^7giklVh}{npEVT@N=;7wdbY<CrPhK4$HkRs}k=O;A27-sUHq~)~hEKA(|F#OCl$PR^3!z
+ggM{1u?-aaFbt=Lj4Wm=tUKA<TuAkXtkAjF$3<ci<tVMLEM4|&+{&@-+|FFcD$9K=blX<q)s=F;)LPB
+7XC1u-?QKII$io)FIczY5tKb@EbvKjj_2qtd#SOM?H-@Ip=yh=PiE!DW*t6VSl-SJuoboJblO5e{^w=
+Y(`-~_#i7$jYu@e4XZ7XsUS*Tkd6(M$aN=kLsvY-z1Vb;wJN?oZ8r_Rzo7j+{TQiRVRQXC&2mWokUrO
+d<!eJ*axIYZw&O}EUaJ4@~G=2!gK@JS0yjPvNsFO9RYx|KO+5L>9wxw{FgwjF3e-VvuNm<`y9qpw+$u
+tlb?3i*C(Ol`Q#mz21bg<5`OS&^ys=C@M}0JXiXTj~4$?~kY{lk9r`dva>$(6<PFhi<w*e2ecbZ%#ez
+Wp@IXJYQgY`X>>k|Kj_6_#D5#%>8wy8x52o3z-l|wJ3$zVL&$lHUcURriM{42@b|ta+oZZeZ4XzqoCd
+btVbk}P=Vr0Na%OuSm>oo6WBeQK;Ixe2}2<AfW!o}%KnT^0rLS72V+YV6neyzP7zJfAGjtZhbeOy14j
+tt9y#3%oDXgT!$T2Vegfzpu3@2JS{xBi>3b-oRWLY|{AGlQf|+g&4?m}0I|Sp~u#))lJ=aRb!b7Y!lQ
+#;{l<AlHJt|_iE}G)pGa6-I7o7Jn_SOYd6ge=8Vt%aJFl>HA-j(4-`J-1r!?(aUMh$SbqjI;%GpO%x^
+d5derufTVeho=aI_+>umsY<m*+)vG#O63%Siz2APF1{T2TkI$Z}N*oQ}$%9>B5%dQ#jNq71*3I+1_GN
+Kz;^K!`7a9rK8))4=7J<$fw3XovfeD74o~2^|QG`es{8dHdn~+PS(%n3i+6<-&$BB1zxbC%eeHqOYi_
+A3y{qvb(VbCaodZr-}FNx<UNbh?Cn}$^)c$vwb$@z!t;Yxs%Ayb2f1ola>>QqSuNukVs}R?r_h@zU}2
+=v?Mx~AqjlM#<9r~5S}metOG?-DsThiA#;rt1(ySI+satu1$p(m>1DSqTM%UjP>2X~wLp1FTbe{ZdVa
+<pCPdM$IBZ_Q<UTut-8!TsDHsmv#Q178$r75?0c&>li&C0E?KONX2-`}HjKR-W*d$)?yA?4}g#zQprb
+}H<R#4qcBQEI<Tj%swEhG&iw>Dp;5@2Kq8w~%za^~$@2Oy@E3!<}<<WH`yLM~?oUxY%@Y&S&`E)NQ=n
+=!~z5YwEb$UUO{YZ?yjww!r_J*y30B`3YO#zrz+02UF32dV(2k5d>%;YhH850M4c{^v%KOJ`InuU*{9
+RJhKQo3V}dka+o?m5R;0dpd3mF7{mwA4VVZJ1`uB9v}7`eA*i3Q1t2iE(1(Evj+?xG>6G*U_y7p97=z
+Bih=y)NKm&l@h-vW<5P&SRU<tTP2iytp2aQ72MS;udDb;|2aVh$jA*Fw7w$T3F*aG?`?HV(_VT(T+|B
+tf8uP5u@hAp<+<8n<!56uf#E=E5BB;^^3xgT-YNyE@dwu0*?G*#d`Ww8BJ@5iyaJsfq#>|IgZTUW86$
+K1g78)s}WBjgaTY0qJ5t>h<K+ty&lj4vEqlbIivHHr=5ERc%YI<392#>2Tjcl|!zF;*Tfb(CbxJygK;
+%sB1mV*;eAA$2p3iA(Ct^}k>X={^S&|3IA8KHPg6xfQt0Z0&2iOC}b1a2;lqGIj+mYnzSAi(5O!Pu_B
+N$V?IIX035Wm!UM;9ijztc#>AD_0E>7CU%`_$2o>OYrS5g^hTfS_uB=J#(k;kRP5+_MVhzUa66^XsFi
+yW@#ed27t&2qQ0x21veY3w%{I(Em7lD~x#bewrT;B#LHswd#jo!3&KB05e{<h`%AUr4O&Iv((IOy_XB
+mv`0Fodh7_<RAkw<XpdCE;;pBOGd`Enr*2c2X<K)@(d0)s%nl+ULH^Y|nKxG85Dz%`H;Ko4XITogmV1
+?oRrs+m9<%VBhDS}C7EA03{;C{r9msv#mMBY;Q&+JeTQ03tZZc0fRY!DrZXG=m;IKuw^BjDw8G5Kvxl
+(f`$iq5nG)Mx~89_B~;|HvS(aj9*XIzYSqjt)hC%ZROs~NQjzT0v&kvg`H*R$2cm@#@je}ZaDkXrmze
+FZP#1zh^1qGywmrT7{ftXwL3yz@7XLZ>T`ZWaJfnztSoAJP@lmp>dG09V<llZB0C<0;S^L$kGczSa_K
+vbbeRh}u32s=C<B(5mjGF<it`z5!gETHcho3@d}ot)!QK6eFqDYln^H!?zPJ&?JkHR1<Wl2!%=f5x5X
+WcY+{p9llK91uJ&KZf*JmT$INWX-)Q3n%O?$JaA1maDdfiUDoNr5c^pi?)x!L0)d1FqtOFAo>`g)ij{
+7Z*j`^><KOUSm?_`C?aj!?JhoJV$tHc^w_wu6;$55|m?umXIzca~3GPsdqR34Zt6gz=G8_Scifh%b}j
+zD`qn&v)Z~JKya)!ufX|=f5BUkN8Y$I7-tjNpm#La3q5h7>ZLQ&M+*4Vl2whG-xBSFC$ejt$IpPh#2U
+$0-7Vq7O$ZvEQN{9K;yg;3<p>Id`W(A8Bq#TwqeQ!0a+5zA;-X!?HGqEIe}mT+?3&v{}_7GFjGQb@-w
+xR#W2Q|<WO_PQxYH-K_&(l<C7c%jW`OG9FR>(yof0HE09NkBbiA~0lmdhFabNB%FuvzAlw+p-{5h6MS
+ijhBQL!s%BC3SMYuYRRN19|SIdWCopJtGA9`I|uHmu}<hykt-(k4>jNB>U^nu~aw-^D$aHR<WYQE(>Y
+sz~W6sk#==_6@x!?xnd_mlPgp<3_xVGC#oVEad|H1eJ-4P#<f%RM#qBjJn=J+lCvHk!(H@v<H)TaeyE
+V!O9aUh^bA)9unNApyYUrjdsol4D(bvB+Qe$lyztHH>{N_3<qa#Fl$ql3$bYUeiEEK#+uP2Ty%;D8p`
+|_X&jahx{`W_{SssgA)kn5BX;%aQZ9!GZP5s4|$!yZ)1Re8UoZi`lxK!OkksB%f^U7Y`K}<TpK~_?Kz
+KIbKk7ZE2gkz6C|={$^=uks!91l(Oa^b0~CrcF(y0ec2?qdWZf#^Sxs8okWLR}6(=Gudxk<kBkTOobU
+$39O393-r_)BY^RKMDT8R|yukhp<cWQ^!gOenizOeUfK%Cbatv(fZkR#e>s?|YpPPON`!EUW<#}i?c6
+|=waJA$J^H<>AWp+!V-=GM&YVm)Uk>zwy;x0&_D>FQ)5PF+*=KGTfz?Ro0aA#{UdODjgQ++I>v)jW~T
+SBT$_J#ky%<7G}nj}2``QK=91it^7DN>Qc3c}f_PTdL9PRJdnLYh`<Ea-&FNwH>?;Dch2Askg<iKddJ
+IBUUC`d#{hWy|_!}8@7GUJ1aWkc81_jWp_B&XbS8nBIVr;>)rFg$qjrp?(X){XGx#uZrH4R#MhQz`2&
+?#IL(M7aq$j%hDfULxF;f}J+)5gADfGBZ!G4P_G#8cZ2v&(4YyD6W0`PvcR%r7S<Z|5oa_#I>S&RO%m
+cEoo{f4FNX|%4WN-+A{e@`njdCL{TuJv!b6MK6?8NTX!|K%S?rm$Cj^#c(il)wpLdsXK%2$4QMTc&E@
+RfahZcKihOGBSzfi9dF;}FLuoVm;+{Mp~H557+9@+J+pQN1JfFdl5``H=c&$`OTu?@Y`+Nzolg{fa`f
+!m|(ao2zse?An=U+OE0HVM}dTFk`a$7h+}K(LVX_MEieYp8Q*l^C#xXUmD|Q^Mt@iioiGqXE2OIIT9l
+nl0X>>SSBdJ5e%@!DeCLh%0P)HEaZ8dfkGM(dlo?njZaQqxMD1u7HMVBL7V1ZnkS%xY$}MsGSG+twh6
+EpfRPf#lU2e^^)Iq1tT@H$FcO*$KqzzR&&(5I!A@>vVDq5)v|4#8kCEbV^#L$FK<^|2eYik(li5_;B8
+N8v777?`ghFcto0fusdjk^&rKa_0+?0&^%c69j%oFK$T4$U1=wA3_F6VmxXcXrEW9EtZ|C=ZOoO$xsC
+-5ILPu9fdY$@lVX|HTB!w}Wvyz%zLmQSfZmO-<h31b*EA#oQaYUk|kad&6^@ucNrP4i=y%TB5K(f%%M
+2~D}JkXfsCm)#mJ?i1JdI18(I-}W2y0z`Yh8qRl(t(SLEbsoX<C@aCTTyvx2mW_GnP&G~whu-L{ASLc
+{yAjrks*gsZpdxjw37cgox0sbf8BkHax_H`|XG+n3qRm$0i++>sHZ5Wd=|-1>C8Y`X^S$&oU2&TU>bT
+{x_I!R^o_iz4Nv33n?N)8-`=!|Q%zD2J@1wAr`vKDB_N}<Z`(5ayR3hvS`Y}AA=1FL;z7>sqgkK+aGb
+iOh%eJbHhK=Ox&c-;aBq_WQbEJOaKGW;JG*7Desfkc7rq<SWg?U%);D$T>f+6DFTI2L1J-M7cM1sFfH
+e2i2&9=9L_h2{SYT)Uc>9GAW8M^Um3;zAy9{Rr0q)kof7o?zXiW2~c5|m>!-)e!A<~+4?y`Cu8bbrt*
+Pq|gs_@=F|XmVRW<eBcV?uM6c`TDeOk-Ul&Vo9=LFJhE*-;?VKJiJd0Ezw^sbbE-ib9>YsujzK?zT&a
+0xZ9DHjnOq^5+*@#!(q(>>i5<vwd_jT?sn{9PfJ!^KC!2e*HyHmh<ev5bV%<c^SpUr4uf9CrtPmdHAm
+vjW4|oKUdss5^wwgRmd{~xSDl?c3;`=#miCqNizD9UL|E^}<4K=^r;!X#w8p=Z$|E1|ke>g3%<k{CJK
+ifk{e(=n-&_NT9=g!8Kbq#^ZvRpqlhgJ5_m}wja?SF;zx+o7tk(POr75HM@Bix$Bj9sa8+b~VerK2bT
+MqD}>dDUs_iX_boS`X{V7@jfa%fMmC}j9#st=OU6Z;a-tDaLZM-~_buz+K-$g;2N+;T8j%f;|gEP_S|
+T-z7JDu{Rtmyn<+ygFuKq&A;crUMHDkUPQs#H66{skq~`NF10dz)FCA{PaB&H-&zDGM`F$P0{RVs&Jc
+5h6R;R)pa>&ltdg17e@&+dGObSs1z6_zceXu0My{{9|cg*>)!mb0E#H|-!0rmrj^?gzyHX)McpoaEl1
+nMt&8gRt>3;cMH^Q{N+ezAQRwS3G^#^F-+Y*uU~6gv{l(8jB#QuWP|~{XrsPk6o9>ZsjhZ@~15fMLw%
+q8V{=Nh+LDs5%)+8Aad0kkadIlXA_Upw>D9N-g74NCGgZ14A{wRL(srulbuHAcI<YMy}qF2rUOa{t$H
+6I4p_}ZpVZNPkxzNi}F=z=zl2m$89=DVQ}+mWX}tbt=nq))1ekFQq4TjHnWD0&fjRD}Ry?l+}SW_wr@
+ECANQ;TFN#U`xFK6L<(p@~rQjUhCiUmis@JX6^+A`L<_4pDtG2`qSD(NgfsDyYoKkm>|wuMaJJ)cb|S
+04uHi!Ht-EQ^iw>*SZg}!3f+qBXAgsthdIp-f!?0zjb=t$Jf|ofXU%inp*bc9$sEzp)EM?jSnLDcU1~
+-t65*0M?T^fJOwui{+T1cccDk{z_MLgnSH>m58i6YObA*mLf%Nq?Sch608q2y>dz#m-n6bJyvn^4{yE
+)g%O+c)rtf-o65zC(Hsg%2kTN8e6=&gwGY0?+Pa!TqJJyG5BsydV55~67_3ok&6$*SKf%k06=(=sC+y
+dT*~TgC|Z7M!)E3mW!+_QL$^eL;Trh56b0g8c3a^RxE_`P~=hXYUL0!wd6O5geEuP4ms6G`@}OwQh%e
+MxPC8>}`>oCg?3Y`ue(@h?ikduHf0p#MUL}Y1lHZOErq<%H^%BPTd=}hUu^Zt8MnbQ&oo*(h`v|hr7+
+$HLpGxN89`C3@cIL^leq09q$JR!`;QZxmXC-879ije0LA-e1v>1tMgG+$NavkPTzySn(S6)2hIVyfd6
+5&fbw4dvo`vkf1K-7UmwMb%W46NDs;tIJu_eVPebmn47DmTh8)Sk;thnO)-yKQxmf64gw5?XmH{3n#V
+~Yvx0@M$8d$WPajHF3C`y&gMN6LDaf9!+8h7N96u}dtFv%g1%C)z_*VRUt(O1)dT*ei7pamxxTFmEt%
+BiUP#&I@Wf3ay`na^VFA2X?bW~2NEj*{&E`YluC|AD9e7a3IlCd2#6oO-W6I(7f2wiQXEG>4PuSGHA_
+LC*{^ExaXgX!CH<RArilY1%mh7xKm=v{vBSqOaFIQE){R%!)=i7=q%MY4I)vT`U9#`$%G#m%+W3C{1C
+E2Tx&_1okuADur<^a7PB8;#vtDUW`J+3Yc9K3*9)tbOL5nhC`bTSWjTs>9oiSScSk;#4=dB8$)3nDPi
+IIV_<n<lRt`Kera2+p}S`ISob7v)m=7|zP7FI(zghYd=K%a#LwOk-Z6-uA-s|66_0L30bpqhtM&7prm
+iS$xoX0aEY`q|ioFI+o&tb;rQDSDu(J8;qok(zhvxy&<ZrDxO@6&N^vppY`QhEWu&%#9?-kXNpP2!gS
+Uh#fB-bByl>F2yb1+5+V6O7IR@mkVWNL)9@9Y(@qf}J4%w8_uD>U4{<rhieb?L1T+hD>OA`r+4-%*0;
+VC;1F-?2H7uZ+&$v^hszdRzMijLysVC;rOIvX->okcu;Wkp%6Br8@GPzGr!OTrAd;*1tN~=3=eYTX(5
+6K5>=SydZ1zVLkf$+76g|v5cCKNl8-|M)P(!^~T*^b6D0bqreEa8}*E_@_5=Vv5r{Y=FLhlGvpSi=gP
+;)QN3^YxhzV?^LlppnqP8gvsP14Y{!y+U<V>g-Gr!ng7Kqve?3@xwQ)Ni(Hr5hOG-K{#%jL7$$g^WrB
+jFVL_9r$whEtf^~el^>WSngt8dRxz=5&mkQa``TGvK=d-RT*&2DY(s10qIx4Wf{h4XZSD|c%xUF!CMK
+=^Ua%_vcEi-XOdXZ~F~tF20o&Ky~u!ZbUc+(*Xp)xjxUA>nH=vL8IPidO1;Cg=7sbj9AJ3<77{k<|mu
+TeT)3d04_P5%Mjm{Df+~DgE_zV95JIFc(-|sHO6cd%5b&veNVRu;Od4^ZBpRt)$6;CUr4VVNtAaQ@iy
+CI*U@H;gS%%W{hx{uXSQj19IktrA?c{?XY66ZF1Z_?KwHLIRReQ;e<GB#21HcFuO)~yg@lOp*rs1SdF
+IXOf_?_gL;*?<Ut%*slt-m^42o#;Vd`VEwJezM@Ap>WpNe@d=niuZifpfO{P(XdIRMj>qOE03YDMs7I
+#=CWx2;CZW`+<-L0eaaH$yS=4{2_Mfe=n_6qBzjmLtQna0)2n0b%fx?GCm`9V=dVo|Ma_}yXMUot_AS
+G%=)XJ$wom>W^M&#LU|x8hP|X9=6_x$I~tx)KlvM%0LLb(TabPl}aj-0&=p(Lz()c=wpgS0q>HDD9fC
+)?Kz39V>L@-#0jfsJWNz36@>`W3QdJ-FyBx2FpBs9RAKG_{Vy9r|q9!{Y}h;peT~Ym@g9^rzKDv3DYL
+H1ZF1!R!kXK$%uL_1Ur>CLZ^)M>?b8)Nx1SDM`2bwmBDQKJb}p{aM4o)qaZ8`7X~vdjE>+e>^O<wda9
+r9^n-!hP3a~P2W#yyG^A*_=nJ0`Mktst3DZF$c;kF3;}H`J3|n9vOok`PiMJ`3bwb84-x8+!6Yxwrf~
+&%QSzKdEc$|H(G+*$1_mx8xiurGK*$S~fRR;&Oy)s(`7X7{h7;kh-@8x&zM}PiihZ}vb0ZsTCPynCKM
+hv_G1v=_6)dhXqL+7np&+862Q`#YnQaEr`GHFP`RNV-Xph%e-r}9Ry5r~y<9W{IBbNTI)Ejp{iR8`yK
+o=9l_u@V{3+xnZkOL7Uo?cH?}etx|7{fChE4Tz>1Aktrn3+Qt9Twjq^!hBi`pY7G9F!5NDS?LxVwI{J
+|yMDG&Xv_|NhJ%@R@A$Lm#&%r>4;lpma8Hlb712V1H}^NeId)pg=}E<AbQhFZR2+}Vtgy^=ZjJi6toB
+4x^<C>2>yxAFC;H)CS3$N$+H;Uxyo}uSqsz^kz5JYuD(CjePTcjvW-Z*hh)j<FXwL-psBT@ZoGf{z$=
+rtTFZ&MhTWNN`1j>A4rvg@sm{JF=y2H-iuBiEe^J7iT9$SU(9u7}a*+HW6`QYE9cm#Gt_(al#Bb=HTp
+CNziY@&JY4SP4Rbt@`~K-K3*641;x?GDxX+7MQyGuTO$mVKsS_+$Rekg7p1hf8VzwRcpj`fcel07hB=
+R(o!})U!p=!Yci`J87;Iso8kJ5J4vTHw*NAYY9!~14wb;Po}>XT(QvIE4={{EM)bwGY9!23le%gL`72
+YEv0f+Bx}maz#03QGzWgmak$NG(xfM8(^O`UXuWUo%;s=}JJhzu6sGuSg0o>Ak_{>c-f3SRa#y?(%0m
+)ZtgH1^fUBlFzm02Sw$%edcIJ1&D+Z*h$NjBWQ07WuevxYGk?ZF}m>hJ0I>d!C_h+-vQY(q&#ry8CH^
+y4VqI$><<@_XGQA9o;i4zLUka5Vk5YO&OndKs*oma3Pm$iv=tdG506fjs#9cIM%ke<gU&8STNbhN{W)
+VEDZ4w2DTns_0}fXHf7&ZwCYb>?*>`(}AH^$|aY<{X=AbQavWr6u-)AK%iuz1brcr;(exG%M2n865zS
+SED=MX2KGX=t|yPg0VgSzC`}ETKL<(aGB8E%42rB0fqNB>{B`Nx6{7Zx$d9p=qI(yzv$$bDf{fSi3Br
+o6h37wax6@drzYTMro??fXwYpQFOuZz`Wo3(LOg=94oE_FD%YAMQ;t0b!va7yk_?oqBnr7J<=~oK4DN
+RHvo&5hEI|$=WfnoXn-P!@VZH(dE2D$&fZ~AK%%_qb;1D&H&mf7ZOko6v0uH5c@$cK<VQE}&&g-J!U+
+PS;hM=0azu5cY2lj$q#RPe)DGk}n{;EjzTjjftV%Ge3#jKI{6F>H!Q(ZBIyDqfu2y~`o?$P0M(!6f-=
+;zHPknb9QU@sVDSW0~KQMl@lg65O!9(gTX{u*@uqr$m9RXG2#!1-JC4^e1Lz;KaAHB;q769v!so7*>Z
+UE<gLs=3*gyOWP1;-r3l=47JcmdC<31g90_V<h{lV|gE#<!N;`pZAm;EBo}o=j$kI&Va;>gwDHlGTak
+{Vz=``9gRzFduvuAW?_2?==EUr+78=V*7l&Wv38DJ&FXP>snk06w36=RC6b|2=o~pw@%3QqhowYjyH@
+DZ^Kd*QzSiUNI~BCLYC?l#6=)%_Jz~-<;C6Em$$`T8*{(w0J2@YhvH+6WjL<#t-0y-JUCnP;dPO3&|C
+VSr^y##;o{oB#?`&P+yY4h+3_rpTx34z6d#wO2NhR;9mtl6!@$1nHO}57>CXy^`6`xgmHJAg#+Qi23N
+k-4+r&=~NX1||VHIIZ0DqLBjNb(^Xaa$%=QGl)uo-EWg^Guek=LltO-i6l1oSUeEnK}6QaFy_9CtA-3
+!d|q%&@Jq|)Dt!!5{4$I8xf)Pbq)+SK6~6A+7`RWfzWu(x^e$eApYkO_@9H`&sTrDQ0mK~eDt)oDa&C
+d7CFT=6B<@E0=7LmRWhQcC@N@4;sx>bo_6W9#RE2NQwQ`0j93ydM44sK!3T~4;Q=%RKJ6ZnQ`6$wX)8
+MNXWl)SB~HUYCrQJadl3frnt<gSQV7`qby;#k;|zX>bzCv%%Oi1^<qi}E+$5BPp;a)fcuM2~mjj)Fy{
+Zu6m#AAn?_Sn@psw(PXKzAX`=3GGX#=!3)J?xc-OvHXH{Nt1%;=E~VPng0JIB?%QE7uNnV&a~`)EVnD
+jIz`yx$oe@@K>Qv(de8&xZV<4?b_trjrjf?)^KFXEojAUV-}dXhzbiLbZDnV!CxgtcQpiiKp;SNxh<8
+qq&M=W;f3Rgc8qIX3*Ny+*OlHX@}ejm)56bt)=>YD_OV76gtGaNTJ!0krw)tQxC3pb9P&qQ)ewfZYtk
+f`s0;+h<zGJ+Qr)59;fQDZRxB3Jdr`M?h*O;m{oKjy;rgeE1~{*0Px#tcCeTo@><RA6?wJZ40Ivl75P
+l+jb`Ck?HLV|_m+v9>Uo*V&%<|>@772rt@d(y50xs+2v&BCTDVxmpr5|70j3>!1k9t$m>-rc?S?us=I
+z<md82*o^w2xDD?iq~IQLv#PsgymsVR;gQoq>cL#eGZd5;|$Hrr4@B+a?hnjxM*Ef1JE^VTYM&x;E~E
+RPR;hu@q=CGS}g&0H@#pN{QvP=kYgeC9*P4=bvWigd574P-9lv-LT+-o15uu5d1=R^jNLF2z+NvcHl8
+%%|zAQ{=tIjL5%xS3VB$0`#wZ{A$~~uZnm7D>{3}-jBnZQ~x(NYP#%?ec`_M-h65FV%fC605w%=Bc@c
+=7zQMO*_ljb3IKYhG+c5+!UdvV=fX~fZqN+oppw%HQxt<2!ENNGN(8T^Rg)CDU9x!ELxGsKApKb`ESt
+cv3d&7|4I(%anDEL>5K6z*Pe`Y=wQyAtJJnj90GXS1_W_^{Zh%cB0&b7LE)bgbCCcDNKEKX|g+_1xXB
+)mvE}P~%e6uZEc974J7SprpcNd1~Xv5Ox<BzmWS*gSKY)!>-oVVJ^$W%P}+oh92s9NtXoA>PrhN(i`Y
+hAdvLURCuZ5_a`&5v72A3ksyG=8*IUl046_|QKx?9Zb{es|bEtCfoUSS(fan!9<}dr}$A4ua5+*|3!o
+`MEQC+h5MOa4W-|FCUKT;a!7>(mh^V0ZozF)8J#}!Fvr)JjLg;Hm{wk6*1u*@4MN?jRhrPs?`~FHwTF
+kPHZn2^bxtMTXfE-$+dbuQYQ3<*t?#(=De-YtxnX>9U+SKxjDoE+KU=D?C4N)ha<U4hfUZ#8zj|c!Sz
+;AJs*@5J&XkHbTe#|+$^`zb|)-sj~VeChxpp}__MF&`{QcZ?y1&q{B4ySkezQHG~7Yg%$@Y2ok#is)n
+2SsbsEqsPS@>a7q&ps`>U>0^#Rk!{fwTi44qK7>O&HdnVzJl3o13wksP1C%3X_EkCn;~SMy-bH&0jZm
+JIi?184zgqDa^eOB<O??Ugs&ccOE@5=;CpTjE}w^%1w4TLPo=x3|KozbdM#sk`^$aYc{-v9ss@tmBi{
+*5N2L$gf`2U%jcwuU^$(y{X9StGfT$n)TNpia6Xu;g*jkk#^>iZdx7H>%+2*Xdhv6?5SMB(Za4M%@Jo
+VjTtNH&!uCf4>X{5S=Bvc%sCv$G~Vj|3f&z_H@IFr?rGQch>*mJU6<SFAu;o{c)g$IY@#t|$?cn??c7
+VDB*;1bOap-lmhzpu+wucX1=i22EF$#)@ObKPX2mGrdA_`**Y(-OuP4qw%_Y>i?$1tN?MQiUXU%pqk8
+g=v#n1V5jZ=QaVEa{roX7bL^`g7T4A(Y@A3K5=RcW)%*tojjmfGY+8>Nj~5Vu=X<a2L7$R{>#=;)Z7y
+e*G-rIKk0XS+L*j>%(uSzp|dd{jx&XX;asMO~opwZsiu6C*9uTN)y}CK)eD>wkcv<>8)Pk5Tg*wEA)C
+Z;<sr-|Y{y{c+y!k#!2II{>CbNm#m-VWHL_rX7OdR|=C4@F@osjX?XCWDR&ef2|gQL#;#-us@oG;SkU
+tT(82xo`6cqNa*^4`&3X+ThZ*#G9g$<$I-N%IKjeoDWD@DopKbWHN+SsVm6&B;=K~_RBo2epivPglV6
+L3W$yAAW|_XqD?ssdI^~-Fa(7nQm@*;gUy=3WFO&7E;_Hv(1?Lm%$u60y?lW$c8}cKryar$1VlSU@Ma
+h<M>sJc;ha_|V8hb%L1z%uP9|q>f?fc%S-?sJfcS*7i>I}%YNwS}ZsfhYM=vVJMKn&QIqd~xM99Q8#7
+u5JRF!O0%EdMZVyc&4@RErkIWstuVnAzlnTJ2i4;N^{dEbQiSN0!WRlUN*^m0Ntg)3I!C{j=EUdAQ9V
+&V{XKsHAq=-8ggaj&k*yXX|(;vETdGcysnSq|+~RV=V5=inh!M>)l)4+vEJCpDFZtD@}KMRQ;YKkKvi
+$@$@Z-&Y7akvIDJL9Aos6wx%CKyls<kpCqwi2K;p?Qz$8rfq2=rr)VDp_4=;gWVX7D=iR}m^6@-BMBa
+?S(2b8os9g^<(Z!CEC(U^frEJVRul6F*G3E)s=?SquZX#4VJn><FvJxv@A93Pt#4>Xk4Md)NUXRr5`D
+WItROFolZBU8xxZSx&H{=rZjG+-BmiOj<H^g}Gp55}nFta9gI0pq7n|#!iB_vV6?QhG(USmASsy(`T<
+n$nh4%2TrQnWX&aGLTU5g35^Blz;Q$F2zLH$Ax*YFiYl6aUdM@?+1JLqc1FuCm|lh+-(;rQ1wzCi3=b
+3g##IF9P}hvp3LHEc5e1Cq%3G;$tV?pL}Hhus?q!b>tgX--t1(IZb~LDzcb)!g-yVVVU58MHOkCd4sv
+s#;~avDM*>_W_rUC2{gmH?SOfh<t$eQ_CWI9Ip(6dmhZA<yVAmIY4geYP<bqSr>wzPdv|V_*&Wr-t)4
+>Yz84+KyjNG$BlTr&9sT2KP24znjUGaym(`3ungO;yRQ8&<>W;HMWbMwPuH9kSKSad1lD3pZTtHA%Mk
+g!D5n)!wuDtDV?$k$S;aZsNV@EnIHLq|rS!dfUWgmMxxP-EgBxEsn54T|}lxSkkqA@A9#}nD9GgWW3-
+DWv12TLARVQH+=mfsEYl2b_Gy6KKUWl#~)9_yXPpE@k0s?~vV>$4pchxGrY?$4SP#oDe>_?%yHpBu61
+yE|eZ(2EKJA_!;niXb38zkV~xrm|U?Syk`z#QS{_l^LrLrnpk(xVkY$kgx(X5$yS%ng0USKNCg&NAm7
+}xz<nk{dt{#mU9EeJq*xG*GxGgL6ZpZLL=ZRMY15z2CjlE=tL3g-yg#`7*wD@j|X^1ZB3_in@k8|pwC
+2a%2I3UyC(B-3dH@^bQ=3)7V@h`6SB=}L;)zfuJ0Re_1E>@f&ir2(boG4V^HQ?SB%*0jTsP+Fa-E4K$
+L?9@3aOIYvLXy0Puql$PQ8<kN+c#%fN?Z!2Sf|&;QMw`>X!?3f=LyVENU9=?mt?;x=9g%AY8hSA;ato
+OJt>7s;1{Uyf;t`N#ba`sUrh{<qndf8PJD-+rVY_q<P<Nt?T&X3O8=|6lHfU!#rR4g5hrPW(|;HRi-w
+mDijN6X!6Wx*dmKXsmqMR&yirR@Ee*x9SU9Iuh?dRM)1LBJ5wY-m~KCRE5RBylC>~MWyEx(e8X|>uOJ
+^(e=6Tl4h`N8NJ?n&X4vo{JKGkFpjB7Qu|U(rsu%HH}ny+y0m|!n1OUsOGgA|KT}V4tmjJ;JC3gjSi9
+OiZQ<O@fa1kct|yU@($ns|f8qS{+FkwQO+Z`qwp<i0z|>;bm5W1LFgvkKWe5E$>2Q@(Co0)PXyK;Cnj
+Fo%{OtS4KiZ7d;t%sK@RxOa9}-;<+0Id)`ubhbcHGVtcS&+lnGWG7(vl$xbFaZwphn$A`cuvFyi!q%B
+p0=Hb_M19V)+dIGVn*NiL$yse_ME6bM6ao<f-?3b7@SnQFrW?rcW_l;fCD@M1dbC1EEi&fh#m;pPOr{
+qOAG1nEh_yMy_hb8+}naK|e;AzYQ=gWK_OVIjrws{Kuej^IZ5GRNlV*N`GYve@15LkNGxd;PUlN9P}!
+8?tvY52zES`y+Y?A<6bC8<?lpsdA^*{Wm4gd^p5OcRV3gU<I2mPXkO1=u+H?GRx`yq6;HT0?L4Ehbay
+r{E(|^P&Hzs$b17`|28$HE_)BWFm=$^VFf^TBqK%h47lu=Dc^2k|(#{EdOqo;2;xJc1ij<GjdohRQ!W
+He|*t*zxB6%5qu&=jtl`8xlIsWNjVWFo~%bxb9bJ=s3o^~2rweJvdQT6-CKjCU(y}T&uo)x-^E2L858
+^N?Aj<XI0OzDm&U$jxNcG(Nt3;GSS%j#;D(Cu+*pDhlXbuFnb8cm6|ekyh^_38PB;uLmlCT+(3csQKT
+xuzXyt$T9bzd&ZQ4~j?nNaFt)aQ^qp{e;ioEcKUs<KKj7;H>*e|8P^CiZ^M*V4LS>15k4QG=dRq>WJa
+T3h~6|WlwKn5uZFA*Y!ejqjIL8c+I2$_VEy`geD*ySTAA1|3<&Gk=(`>*Rv4@C_KcQ>I7g$kRT2q2oM
+th<=_}(*bxTqJlTMlOgAB`0AMsI`LB6>0*DFerWy<qZsfn{pJgKUn@mLTCzyWyZ^E=Ts$Y$`>ThA%id
+`TQ=+{87;3KGpKEpEd0aPQOpgQ_3R6}2Km45}*3wMXUrYFyDKckF#?gY4Mg)jUbuAyHI=#xAZz}<Lot
+K|7YlAYx2hP!BoUOth@DXq2BtrhvPfhInLiSsm3#2b}dy0b^jLhh<}x;~vXGkL0aV=uWjtlp17?~&1I
+%#Fxx7Jrm|#z7T`p?RWCy|ejx=jk+Ph#}xd18Jz5TH4c7Y6cve=*!d#O}0bxNeTEN5}ch;7RPjC9uRi
+kbpo#hNB?zio|atR4~bEu-E~-Am55d+m_J4P(LNtBM!wu8w58rv%T4HGml6hZh1_(Oo@}{KrRgf|`V_
+e!){f7pb4L_dn%d?Wd-C^_blxe}=y)i@+mUa3hp_3NJQJZ15~-rL_+fnUJ9CgO0iq8VT)FOYGo~%QpD
+P|N1-9+on@9Zx*Vh%UEdbZhUyV|eEWGaVK$iom3Y5$3ZpL&~O=kA$<=1cxeLu-B@K5tATpw1rR<hqMO
+v!JkR-U8(K|$#6!Zq{-uYUp8xh8zI5DLD7>zf0VJ4+~Vo#*ee>F*$$FW+(UCLWk`FRU{anDzkO+sdxA
+q_HtGG^QMD1f+F5M(!4|G0Q3*1;;uC2`rwBjvnuDC{BvfSDwkT*kY`AaIeQUFmAOBXHz@n68gNeDl%2
+$jiNcr8m74D@pmhk<(puOi6!GVb>FqR?i^mofx1C)=Gq2%+jEIuJN6xx6vdwtJ+0enmzBEO^s0IhsfK
+wUWv>a4nk`wKoT~1Ex@jojkU<F(&xW<lE{S!MM(xFfr|d-z=<!asN6yIn=u|k|Xbos2xg##)6#F^^F?
+-0Di1na{$qU}XlINJ0TJ_MI)~mu3k$=c<YyVR0{cMi$`I<C3!5o$t&M0b4*5Oe*?codgs-UU)2Rwt%^
+t>M>jenuO@Rt(Qhx!ZcXU<^tUQspvOdL$!$ty}Wx*&!i)PKHP>SG?`|HZrcfwuVDJNYhRKq-Qv5C;2@
+vKUi<1`%yTNMyXVz!QM^Yvzx~pCzNv6fhC-^jBnv5dTaD4C&1p6bGsFnrkE2){~Dm!XWXvRu9~*x3rz
+a{+s?v4uaksLg~#ynn;1aT10`Gd=P^mfZSU6^kydtgs~VPHo?STmT5hRQs7#^x}<=}6dHgc1-6kM=@>
+*3MD)kd;R!N){Oc&GWVxN`{DQLBiXX{A<lObk&L&>JvhTi%A^~3CSEBm<dm1D39aH`G7h8S(a|Z<TuT
+;j;nf>)O-<HzPi5UQP_p=-XD9U@^Q1ix*hgiS9{?$wl2p9iGz2H+90Ml$~)&WI4*AT7Px&U8~LAt#i9
+J7%7z+u`OW$sHo0sV`<_*oYVaLyt9OQyE)Kj~$BBbTv!r7x~`=K;o{@QKk6`7RK9a@H5G-%C(EHf|>f
+r?79Ro$Zg4v(V?!*)J!2^>+QeB*tIug`Wqze(Em-PWRM>UF&zpl2vii(#}pK)2&WQSGj`p9&h2g;&Vz
+Oj>dvZl3$*lNTR%@z-~ip4WQIkd%>F5E*#tA8Q$(x)k^5_;2w`D0THKyi|kaMk?35^bu|h{$GhJ`i+!
+PpFu>V!IJ|8-vGv}}WDXX*M&i!nDCCH@qyq~bi!xBj>+K}D$sQxc#6`5*A>Un&sjMP#bQ{o~oG64X?=
+s>AqDvi*dAQGyp13>Q6cu`h6q8farFizu)x)3CBKed=>Myo$TkP!&VsVY4no3?f6TcyGrk8w=KFjgG-
+;uTPfLKC&`e}>kBT;92UVkqS<y4cfL+uioqThS1tyE`+?e+XsW#(fS8v&-ZU)u{%yFQ}te`Z<tojd}R
+am@B*`TqO<Sso$xCe2oHEeEl=IlK9f&U01;J<rywKKgd1C5qqJ6N_}>j<~Sl4_%MouzpnPQeFvhe=mb
+4lg1|#?j>k&5iscRz4QUkjpDskojh6}m<l=NoXBMt^CRs$=ra|?Mnz#nM=d*cBI)fOmdIVR#$o*V)y;
+NynJ`vxcc>L8mr)apWFAzvdfnI#I-|$s*bOgKy=Wfn8T0J%Pls<=nYj|Rj*R>{(Zt#$Xe*nnWmKORJ-
+R0C%)bwm-v<z<a5jwdyW1q|7pq@n=RnVn9SkJqys<vO4s^{VmY651Wr+>^rZLfc>6Zu9(DnQ>Al5Y_;
+fY(rv1woP+k|lz#$I^SCdhb*6bZ}ii;F)*r=KIe)k@;N$9&`&Kbv0L?FJZ1dP=@Dv6&gd>n1hVQ^Clk
+JzdO1)H}s6xa*mEsYaAz9L=B&((U^m%X;&?zYUp%mJl+;w@3_@0bWyhTq^EPYr}UEcHbuR_kx=9{|hV
+{w&7zCEv&e{8Hs;;Y||fb@Na{Ue{|KiUJ&2EJbb4~iQ*Vb6D&rd6i$!?je@^$gk*7&!cY<;5R4_Lj{*
+k3I*Gw}FwmezLG}(@+kk8M7CA&9oLHj>Bn0r!e5@aZZJy&S0+xx=jrPHUWFJTq(4dbG=HQ4;9CGt%#S
+y@3VSzT)F9L=j1*M2Ju%Iw70=-58*w)`?(9Ilz1+fho0Re6{0c8<tBhD~eVFNS+641~`J`H4(TYCcmy
+vcCuOZ+)t5IwN*KfeYHrU3SLljFG82s~boo73w@PLm=>K|h#%U#_4W@QJ9gk>CMr|4_aB?wn1}@w4E8
+`&rWwOwr9+uC7~`Mrygfl@Rp&`(v6|ZC>cmS5Aq1u-A<K^WgG|XO|6<zpr@wbPhK|o05R+E&qh3-w~`
+6#YYambv2X(>8QxuY$%5pyKYK6i<b&~PO$jEJ4)ZZ@Oh_1IBhi@AMk`n6*u)n%EaT6(}%gpAeh}5xi}
+myevj4%`^+WPu5)!dokLcMaZU{}3Avz#)#_(l&+xO|R&S<{*Cg%zo|OU`FWRF&WubdN?;XOh7(skd5|
+f*6j;n9jfl7*dL|(k;5-=zCov{c$GNIR;MTHkHuRFwPvLzzZWye`T^Fn8Ye(aC$E|)m1X(`zdojS4J5
+i1T3LLbU>XCu$ddz84#U#Y_tgN(>KJjo`PDvopfr*F}?S>G11AKH|Kug2~;lU-`!C9ipS<%Hch3uhu8
+3cK($z3!nEaBk`v3kaXh87fR0*O=?4dFyS??E|NAi2n({%>9T;V|{$rRzRQ!;|(8#aY0T>oQvPX7tiP
+xq#Z8}b#;6FVpH&Jwi+u;FK_&Ht}QdW`^)cp@ygh_7eV`1=?yUMNOzgMW^}K4C4=AKHTd%!HF+F4$JE
+b5ckN?8M^8#qJh6xQ#IM(Bcan4xhWwDsq&c1*M{6MnD@Ov$AsoRb@9jB>`PU$Y>aZ}kHI6H%ny>LbK~
+5~?q%n6f7Gwvd2m;GWj!KT`jOoR!%`Dbj5%$zEcerMN8@ZoinL3ttVwosGcnHXz!i#%&Xo9_ShTfMeU
+a0PF2=-U%9ch<kjwxQ{7<t!<iPA=jzOXn;z81AGU#6T14|s^AXHjHMYejd6@`@8SCaMpc&ilsgP6#HS
+nIn$Dlotr+193AAe6Z#F8s;m^a^jgMS07)u@o_MGHpU@Z;AAf*{(=2?QhlmIe`X^J9B>i;a`1z6_-|e
+C)n5FklfQ!^f*|M*bJKJinhruBssqe~HZfr^Nxh9%rzxl^<C_gP1H9=zCfp<d3D%@BAT~8Xa`O>N0qr
+K*Hu@L^KNIZcRLX+BGZuhmGWsj1Syy2;A0up=*IqN@C<b)@7zd~VxHtqr55U`}LG5}4xb-6h8*hKjj%
+gM^C1`TTU~VnOK#@HTfv(|t2lyYIi9`pa5c&KCYD6x#+z%CxCH1G&3;H4V+WRj1rKt8P_p(}`vh}%Qe
+fakE&Ey;UJWcg^idzNZU_X#+emIu%!nXx>=rg&-)p+wufnB#SXZTXG`<$bO8vp!~qh7v!9r{YJlyv@|
+zzx=pp0|iB#PU(D6#mX-8736-XB5W7GrllYeAjlzyVzw$ZC|9lpyq)qu;uwuvi$9Fdf9o!hj9a?FXc(
+LR#v~oAv<3VM{buK;;_r$b63~<eW?|KB3`er0Q0O`?QP~g-4TPAYv*=y!~uF-FfJ*Qj_JuXU{x*;pVu
+94I(q!#tVXA!_F)Z7;T##<EEXy{l(vR5l&IYHN4pQN5PIutB|;s%&0C}(i0jUYY+~%-g3I+`D#^A#*K
+0Pxof3!3OG_B(-C3Zmz3B$*bKT^^k=gG9rrlR;c1})|UTF3?wvxkR2#p}S4)*D-a9VZEtceWT#YiNgA
+0zCp9HS(uTu3vlbQFdva);;cOVt_G#_O-(<~zoY%|S_huX682H>2|ud6``iv*@+AX8#01HuurF(C3qX
+sraiWS{?M);pSY<M@P86$Y&s*FGeENkK(AlAfKeoWm++L_u|{eHt?$ytVkSQcH}Wcol84k?)vzC`+Ki
+BJ(dNwJM?*isVL6(hMiuZ&U78`U6y$DWc61|#K`0JK6+U`Q{`+fLR%Lyr=@4fksfL<6x7>uE?n`X3et
+#;NQR6yIbO3Rl*2U}^oL@4%h%xe$^&1(%I0eHrzfoLvHtRQiPzbTyJjeh<Bgw)bBDp#7jz#9Db#N;9`
+G^k#g}ewSBN6Wx~vf@eoG`h={ir<Hwjs8q<T0J_wsg2ZqtNf67_N*GL>Axl29~Q!oyx3Plx;Zm4Dtke
+V<=1T@oj`gXoj7#!PE$8E{oJl-l?Y$VeXm1@_Oe<)ce}jV}Me{@5`^w!JvmthCpC8QH}2*K`2T&o|#|
+z`Be9Agm_@AN&3Un|eOYfc_>*fFdW!03b*)08Ai5`ZO9Gf`n=Pd+S<)tcU&;nJC&o1xzv_fQAWDEgFC
+ovyD)Ja_BazjFVfY69J~^nscpKh-2VyWXs4_Xo*u$t=$}pz#XiR$%ZRv{SjJx&@p}faXwl4fGoN02*2
+OZzqTIN4gLD<>tFVBXzS0ckn#yyR`^%|8UFegv;bL`Z;$2Q-^t&FmS5h<chCav<X4=ZpD91$U`Qwj8J
+3uy(KWPo(_Nu(j#0TP<307KmQ^bGlqsRjUQxJsQs|B0$;rjNzdl&xK~-SgJ~5Doy~6fVzv+kUF<D+GS
+~tw_=w4Sum<)!ESZXqwR7l;BXK&=?z`q};n^=gdnkarhLH67U=WyMpu0q!GqJ5U`%Ht?${ZcK*^62eG
+A(qer4wr6jda4wOkmTKc<Kv8`lfelh%cpBid*OgS#g_83@gZ~0&}0*iP}0t59VS`OhKe7ah;vEQvbFb
+KBMI`Pzc^C$Y3M?XT2vj6yD8N<Wu&BK9`9;=l3!KbKH+d5n<obI@FC7IhwhTYz99^?CwH|PDAgr_u>H
+ko?j3v^?45N{?_W4S-ooF1Nzea=aSzFRiO>gC&>Ve##3;(Y2DQXHp2}ShKe0?dI2ZbQ@&`H6{Wp-q!3
+lb#x)_p-iEiJA<b7z*X`vIYO$xk0q7{X;*_FO=RhLWxu3lf^{=~yfW*<G|Iw;RtTXb6*AXv|0OUJY9w
+u>omxcdkBAuLZdKTgQYdHG3ET#88{<ySXY@!7>}ndNfNc!^!V{=RjHLwuQ{L?lJ-(v;n~MJK}ObgkcE
+b2=|p5nJ7)T1YE%%~;eff%fm&PuseG!p+my5hw`58Qw_&!Yx)@-A<2(%N8inhLh_b`UE?s%!AgR!i0L
+<a$O@1Y@)BD84yi=Xzr>Esm$q2(yhd;38_V$o|Et#T*SFOoPBScyNh5jd)=Z^-L3bRBJdEWk@v)cs_)
+%<sf-089%}u5VWN3_P$BnkFz5Pu49UCwM+wazuK5XdzCHhsu)}UDE96H14=~W1!nX~Z#zEDKi~zqRAi
+?Mh7;R-fgq?)i>TWoI0Rjj4#ELd-u=&n0Td6DtfU&{`O@I;@N`f|CvgRDW>b3zg?B*BFrht1IVj!;p>
+MSH6;H<z%L>r83eyr(+NGS>`as;-G1a17DbYt^`TdNQa0kJjO=(*@0X)$sDG8*)c99H??z)qs^<Co3e
+`Sx|kC2JtQ3`N-IT;fw-YlRnW)WNLHU*as}564E!{;TH)eGzB*zULPgA^K&S;j<A+&P88FYUFQThg8|
+=gPUJOFZ~nY1{4z?zapuqpPEP+Xd<n@tE1;YP9Wq{&!zbRxnK2^A0A!U1(&3Xi`hLPoPx}-7d@~0t1C
+2&7iYS+;)oEP8@4+b4+e@%CE*8$xlJ@L7Pn(Y*1mQm{CVNkB0(NHfg_i?p8cHaib%}lOw1&nd21$pD`
+eY-%>10`Jd#+qFt(Y>(|5Bh-zr})7H1-tk!ha2bSy7qj(SG$bOUi5;)~>^^Bu1f`3}i+Qc`J_lySF&g
+L$kTa!Yqjv+jK58}_MyI0gMfRdo|K%t1nT<-1QGZLx@zMJm_;Mf3sN2e{e3?k}Y=$-OL{d^8YY`yPuC
+9W$r)UEJX$Sq>N2!syNn?(IWP>Ns$MJv>6CPe+!S?MPNVh_-vhJ^p+p(_3d2lB$@lJZge1YkHv5PV+k
+GPYe^*-8LGc9m1ef3_1I{7W~k~rahnE+^W+Ua@GiD5G;l)Pe&p|*_uXgzBxSM2e1)&nz)b!)*IpXv2c
+UBMDflHhx_Y4PjMzrM0%~>S?3P8a#H7l&#O?rpa&Y;EAKr~Id$HPZWnu;vsY|s??Wkj=Y9P6IO=ScQD
+rjk2j+_Fvzxkha*r{wrE$_(d;z^8Ra1wZTVKh^I6Axe7N%AL@efvhp!rpuf(yy}{ZY|<O=nHcpYn&*q
+}Y^PvloYt^Cc;tsq41&xlzhPbl+K_-tC~*z3--pUGU3fDDI6-Nu84f`of$M=Jg;dqkNVc<Iumtbm&*0
+F6#|_YMN0zJ=8-XBR2&R5k~ZK{Ox{=>Cgu1zdQL4m=1jh>Y2o8x4fL_TiPEiZociMM!dDw9T)DYwnlf
+sD8Tc%%}c|Q-M6n=5flV3v&=JQ*I!dTaU_D^$wGNNd*+g^c{H-vyz)TJcM;qMcG>xN13}4_nWk=P1}e
+Ql>X_e4nHJsbjx|9r#PbgKPN-y+@1cO%rHGU>buehyR&ZL5Ea~conjr-b@x|NA{DL_Bwug^hmkEclHk
+a&Xnl(2Jr|Ph`Qau#YLUdlvV`tj=yT4r-IWEKb+K6r>23HtapnmbZlDHajjQ6Kt!k+ZY=R6c>In%8DP
+?)$qPH#-AW}U}79}kJ!^=?fjaU`wByU`)z6+Zf?dNIW=r7_YM&Coke$rE`^?;d5Y%JX3;&*jx3QItO?
+`8bRHMTcLwS)P?+9zdpwT@H8I_HKGi(fDhUkXpy~@3;8u{F!jk;mT!%dtdhK`n=)K4(UpI)MZa8F;PC
+c(6h3Vx;dk@NW8IV@f0L!n5P}s@KdA=?m92<lCiWoI(FXevZIb*Cslrm8eiT|+Jl883r^o{ahy`^<(G
+l-Rvs42zRK$~8V_`;eX}ZOo<K$vwGuX|UtDn4JKTBr7Y0kr4iaVJ5OR-OdeqqTV88|?U2A5C-jX52rb
+UXoo!W<}%=_2yJxogQoQKQVIKMOalJ-Lhg@?(=+?w)_Gi`LY*Nl7p6s%u!XEsG<P!0H9YQ3T>*zQSIw
+70ty>Z2tW_%Z&U21|cCNB`qhzYUoFMy8H$N=qRF=6-PsR7%+dq}yvW5^bh2>k(#4*;(Sh5&dDvgm2m$
+=qB<(prCxa#ye}+0=ktH2w^C83#cL>?G&V7jGFkwPyS8Fgl(fq_~y_@VqgSmjoc{E^W6OQP%x~t#!l4
+cOSAp}Ufs0TqfOWy-9FU%PIL<7On}xpv&or+|3&_&)+&cv$aMKr$h7}AA=7u{hu?-wzs=V3pR)D+NB(
+ht%GN);8|d5H`Sxzskmy$#yYMMvS3$-O7(~M2$R~cb=^2aX>AF79$0V6X<XVw&YSGb6zEDaN@4MR@sY
++PS(OVPuO57o$#GsJ7s3$HlLJF^OJ!u-Z2h%U|tuO?$!Y#Lu%#oYO$bR(l;dK#`Y{Dvq1zMY3#a|Ujm
+UnBkg3>v9zw7$g2*$G?N2OGsW@K}TKo-Lpk!pt5H9wluv8nIysbS9)5v@6M+(5T5tX#yUaP55OUDnMj
+#ym96VVvok=|vAh+9`7>pj0F0Bd6S?eaUj-cnL49(+1NCY7@fiuat}H69zB+Zb2DwG)9^_k%|JtfvFu
+XEKKL;A#UR3W?~iozTNMQ7DY*)k0Er_{kNk9jmsvF6W{vFQ5D#_^9MVA-``G`z26?y7VdC-Vy=Pgl+@
+@%A*i2APS`8zKx!76#cn+}@*P5z-bYoVc7sK$j>HXxN-Ql}CP9v5PWV7n?O>sDcvap8hGfCbrg3P7{e
+s1osYo;7yd2ytFDRErhKJR|RcKc*zSDD^5gC_1yK4qPQ5q}>y;x@Qg85bl-8t^%H_I4F+`+7molR=GX
+BO5$e_T+rzk1<u-}X3M<~R86*hl(miEqAT<g!yQSrx57m8H*i{Vs4fm}#^1i+5?7X!eVG9NaU{5+OzN
+?3D8RL!{ZZNXtw|i0JbvoJu5_3Fy?J{<|yhI?Z17cv`gQPUHFYqqoJ1v-fD09dmXcY;&qG!Th<g3;n*
+-Zea|1yYCtk+rdJ5Qw|etFNn2d=5hH{X@|a<R{Gt^KaWs(-cOTmpF6wjj--VA^e`4I;rg(EtA(P*R&e
+wqoYC%~;|^jR%v9@M(%k~Zx93t_*e>ED<e8GMw1V4m=OkHEIQtonP+8hb1J%ko(dv<(!}@4aBbGNE&n
+)`hgx2V&pLi?4`^+b7T3L=}P0p0mWZszU{cAKoJlD=Mjzb>^a$r?|*Bf@Dp=phaR`!PEj7;@@&m7;^@
+u0!$WfSf4?_C+zg7j7_c0W}_n^*67Y33W%_jARH)-BQxE~e}+6%=IfzB5dFI$I%azAyD5){Z?gg#7Vf
+q|z%o)(wZM_ZK2mJ}S-(TBSJ4<ZL$)b{}|<jb5qi&KQ{Cl1}*Ixksi}>x3Xqn00#d7>M+i9aCF|QQvQ
+M=IyLlBc3<RRvN4e7f2DEc_E_r3Dfesj7ho3-YPX`N?)oDW$B~p?q;2+GkSeYwUE_}<swAlCV2WV>XI
+oTbbK=B`)G-c-|BGES!&cCwbYB%<ANyc(V<)PcuBIyF1y!6{L~9v!}y?`X!T_t4i0n{9{Sa#V_Avqa4
+$!P%F<(}!P1g<n=97s$TlfpO~>Nn&Dp1cVI6UTrw4%x+MVa_Au2dUZ=CR?%3z&)%%8ezoxL!h757*ay
+o?hky=v1XoA*JrAMfEs>X)m3Wv;LD*?EpP$ke#Q@q9T2&%2H77Ts}f(=_CD^_aA}lgb-T4eoOZ_Eb3U
+g=zQBR2L~i^MuB>wR>FtKBKH$gO(`x&-qn6Udivr!hta559fanQvJ`Y_?1`oPgeW0oC^UeJ_xgswqsB
+gq6jcXwMI6{rjY`g+}o@48uWZT^o6saZWW=RAO(zoV=%-W1)%B`g&^#Sh^>eegVW-TGKPeppH2n8niA
+i}&1oF;+9T?}c(7Rv#@kv5xR^*bP0mf`Ki&l5){qelqer00zeZi4tHN#~Y}*V08*U$MlOs<4D19l`AP
+B4GKLug=AEhr%7X`+&xp(1>u$=1fFX>Bcjjg_fKi#+IL0?Y%TXV@5?z8wX7<|6YXOY98zY1I$uG!Lbw
+t?4*wiUR%<xk?natE#NxW1c|*%;XZ0pTx&E<NXvFNLntRqlQ|?~|lj2K~wXMYDge)&hO6qn{1b(W=*X
+-jBOyQyPq4y@X&YggI?)X&@nC%I&ZMyR)YtI2F6%3EkT=rzt@hFur(6-Swdz)M<?Xhsn-e=T<18h0G^
+YK0BjZCr5`*xXZgJPgSm0E<dk(Chy|d0lwjT<(<|IToS}0&L?(1(=UDr2QPogc+^t|%gpg?Q4D)IXIW
+nQIUQW>)`iZECa07b-B~w+_s6>0qXc=1CdVR+p0)0+AZN72&Cv>#2a6`2QspI5nx;928wD3aYbt36WT
+RZiWDVU$B{3dSnG*N>I&)n%3vB6+iS@Q^$}pISRa&Vvj1E)1_d{#UX9b<{_rBOk37KBfQ4{*ZQB<q=X
+`rvR%*y9%1Yguz^jurNz`vkze?JYz9dW;ceUdFnq`5JR;Yl|XSqzOOe$rBges}WEdi#p<V}uX=rMJ&B
+k4{PzF%gK<W2|<lTSWJV=dW+n;)3_^?~BE`C^I9zZJUzUbj|T8Lw?U942Gs_ra!RNKvU>(=KaSboZqr
+@LW;rJ^|Kl_)??vY8t18NF)Dm;6Zo^Hp4WecQ(o`qiGHr%NXLaOxnfpK<K%Ng<D80-xk2#9$xJ)^d>A
+H1elUqXqnWM7N_5&)k2h4Ado_f)>zh6Li=)_SkMw6PVV9?G8n;uPT->-MvbQ9NaUEv!i@>vE%-mx&5U
+_bCK$;frbuU<UzMooMt63?&Ts3`|T3vl#gOa$!d*P0pOF9%{oXwe@XO%b25ITIDsJMk%qwM>9VdIl&Q
+F5kdr^Rkmjw%xrT_m6WU%^J~e;gZsz2d*Y#y=M*VhV7l@Mtr5*(QsT6ciCq1hi+6O{aea7K8xZ`0!(m
+6o%Xk;?iwoECh84dK*PUQ&3%?KCz`YTiq3_(9PQg-Kbg2uk2)27{#|P3>j>L2gEiLO#xtB?=1z^z5r*
+j&B_f&!E)=*D}Xa7!1MJ?h5*9|$TkO^;+s?(1q=ch<iBD3#{$KMjl}<-VB`FIY!sjKc||-;%>I7~8}r
+{ciJ<SgMB17hS$J(+5tFUv$&6mvt!?#=X7>?mjLsF0r)*i%F;5O!l<4$PfG~vO88&&P`!x@B&)F@}_M
+()xhsYFF!-mDYy})Fz6H(|WwAHEodBSk!?jLu`AV&}A*!la+iKVRY?ZNY|oH;NletEhLJ@t15Ezx_Bo
+lHF8o=Yko)~8~TYEM^LId;QJ!l1)+H|RrKjok%7@li9f@r^T;gfhXID(>gzcqtTqYAyefo3Yd(ZQSM7
+YkJ>GJhOK!#8>L;Wbqy!Is_j+2W*_)&jY`c<tNdT=2KcTqL*c-2yxJ@H4QmC>MZWgQv0e!rl1q(wlFc
+CoM(j+?#G8Fh7W?5y*R304my$Xeds6!*DV#Jk!C<@2kY<b)##CoPl)2g@Q{(bgZ{N-t`gmPC(j<LulU
+#;OH%A<9fzj-XY)KB0*RM$JwS%#Y#m)HRS)n{#0!dsZm;x39i84|RQ=}e&0IgU?n64yk4L^d-gObh>O
+%U%-dBrR3zadFG(H&1Nk5=SFEx<QaI8zY0mZPi(mY~Wx)mZN1b$2m#}j@INvq6|tj5lDkso(9wx;Bc6
+O{u7pY%oXp$Ze|{SiCp;^EOt+MR!?TIEcg)!wLM#Ef|LiXvxNfA!H0M@6*fqT<lIOqUW2_des$?nJIJ
+Lu3^PA`kiG*sIk`l&v+{E|*aiv`R?dd+U|+1c^sl^=9`dM(ppeFHIuo?@oTm5-N{wM)df(4gx77*ZWZ
+$DTib|Nq*KB|M=wX+#i}m&~GO{GKurCR_Ps9KF{?@qm5F*I^TYrv_4Fphp2o(lfhq$ta~!F?Aa)3!Kd
+Rbnd4&YZ%1-Ow8O1?5O9-rYJywWV8|%<c2VbING113b{KTpSE#3qR(43W#y?!h$4~|dR_{cwJM9zLyX
+KZR_N0rRI_1QR=Hpnlx1`6*m%Kz#1OZ3KJulMAi3lPc=bz7hcXXQASlk_XL#>^B%MQ0|exJw)KQ(cCo
+R=VOZyEE#@oZ)wL7%Nxlbh&t#Utv9_QtfEt`^6)#(O6mxto-pxn8qghz-MjH7|Sgc{S;`GyB-N*Aht0
+7opC>SV?v-m_IFtmiIN!(O!CP;FPT`LTSUK)vdo3TR5ci8X=$T7_VkGJNuo+L1M05TzYm$`)D>NLkZP
+$7w@Rk`h-kPFxW(@VB=}utPko7A;tQ5I9?O%P8V}^^k+<lxI8mSPQRmiRp@P`x&n5uQDxA$`oIQ$eIK
+LFl6)+GtU-Q0+?3;qd5M|1XdaEcuQ%vi!tDFuvh3~P`zIMW=J!;QmK2aP-8{Sw^Ui;rbH9Hp4w)TJY9
+NPbBpW$J)}dpB5a*xK8&7z+YX$Q?tBg{Uq!fvAlQViTO?{$?{pKkI^&Xa(YPfl4=1%Hs>}pwSTwOr^&
+@5PVq>dpKT*QY^rWg2(RFjgHaQ=`MS*s04hUsPKjpzomw|kk1NN=tGFxP<#M#%D*@);b@aPtoYYn?|M
+{3j8rat)oL<crjeqh2^bM%!LhUz<QIh@XPui_YACxVEjL$9D9GnC^dQ$uB|O2g`jMx?v<sVkm>qG=(u
+FgHs5>AQ-{0>t85|kR(OY?1#1=YpjH#TLWsF^knhPpZC*f=Qa^TCqOEm+#GQc>|?VfWQ*5AFxAOK;A%
+ie*dnt}u7hiE2PQUCFwco&fOnQ4paMeuiiH3=Rsm=wt*4SCphqA9kiKJLV67B}Aig6bP_>CkU@F2gVD
+%UQL<GR7K*0PFiEfU>F*t3F@7DKDHop<<k25=B1;UT2`AT?`2oJR_1m=emzjLoL>T{9zA>oaG|0tCWf
+<FDTKsSi_B<ES!rY#Mc@RihFI%TnVfR6UE3J8e7Zb1<k)|7$IE{N@}nR(Nf5Yy|=y!CIwGR>AyzmC>O
+%fIRa6mHR?UVJY~uL56?HL6snv#4QT=?u9FOqtfuO`6@De}lV+{!eayld}CkxqWc=(6_h$_Xkyl!C^^
+vUs87nD}|{L(v!@#dxH>Un|VZCCn6yN8)cl|G3KD&#HKyl#(s}Ar$dgs_0&;Yf6sGILV}7~rd!b%Tgy
+62?hZANOlSqpk;W<PZ5*>bn!(c3&kPm26MNin7Gl{C-JS_5rx;@g@|zW{F0_})<y?;#ZuR5cgBi4;uE
+H|E5!+ZFO5cj_5>_##U|=Ir7zcKRv)vU(-VKDY?0i<2rFi3$OfQ!{?_{?PUTo{#9-|V&d|4eU&F#fng
+`dQ(I<|_E6gER}HorqUXjqt|Az!oeWo$X6K#FU_sHbC{Bv<d@D;3U9I!$*wee7a5FN=qK)Hr2nmHosI
+X>fw#TS9pUOd?^T`x391`T7s`i*V(~at5?X*nZcsF`cuj8OA)qk*FohHQe#Um=Ss^c0ZYNT~|!|JbZe
+CWj?)4k#Ksx3EjO|aQ(U+ul6`<_j2yE$2BzED}I0*hwDm1$8S<qxRC`d!lgwJtBnGJSCv+*D#q*PCzU
+4GH673iLlXwsGw>W$m%|BtKFPFyz-g!)?_CrcICSDuGZvhhg~_*Zxa{m}$LA9Lh*&Xy3WvpH@P*#h%k
+p%O?a@EL$i%*GiaPDR_$jwBL=U*e?r7FKx>IuZC&UWPlvkNhDQ_4>rf0t%PvezQuAWq0I1Ewv-ZKMQe
+jc^Rh(>6)6GOkzI4;D?lipZ2idP;8+Pp-B#Z5<nUjbZk6+?1jd7xAHF5pT3P+!HJpQ!oNK{(4SFAdJ$
+0d+=Iy0q7zK(0^O&G7c(KjAxoIznH5njO8q9!t_y|GCuv&wrNxRR2%)V_D;mCw~FA&nJGWRD3sPO0CG
+ne5eT*fw2fM(MfIN1`J>xgeaIeSd)8pQ$hh~gaJVM1;YW@!(*UYP9R|J6Wv7ESKPo-K$>6^a1bZJN(c
+ppK>?VcTLs})3hXO}uE3TiK#gU+s}KWfG0awGU<pt-`Q$B6$6!ta-!x-b768)<Pr%le1#m`@0DM<a1N
+OQEz-2(cp#F;ye}pY@^9a2BMV>R)hxQ}#oJjmYp5ry>8*<!Hnhs7_7L<RphII=cPY<3_6{Zi@9RDwmS
+<M!%n+rS|UU(5Q?a$ooe3O#^Bsol5icj3^<@;m*TX*me^v^YF8ZWeM<BNWX2+w%J5|k@DE_fNLi|&b%
+Jw^S<M{OW7v1#;KDyZ~Bzfj2<)Du{HGGB5$Jl|(eIFN_<^gOy1lo<EL#QUcgik_=2_fYY7FJsMr3Maq
+oZbifA>6G(T%a{yyoci1^f~Q0vxEKUEp{UUH6nDi_JuqzIWFyL7Xub*HoDp&I@xoGxzWAvmc!nUsV=Y
+J3Qtb%(dc(q=C~iUBL+6lwK4;jsD1N5krgSOzcwOL!*aoh1mwR?sl+7jdQqr+$vymUC+BgP|kjoSXtJ
+e(Tj&&taQgAnwXp;Oqs~(v8-uG}*EAEXp^vmLOT^+MXw#f1Yqu63tT1-WoHpFU=hW5@EV_FS(4*yDr<
+d-}Hs8{hby^F4W3-_&bxr8T^G@7FnHW;Da(hIszg!|6ROmE*4LuPfDVaZFfQ*PdK&zS*apIf#m<KYzB
+I$U0-NV|r0rXKr}r$Z+f%I9n6E$S)QY1DX}-)CB^kg>3GUJ$3;&Y<ZsWXjvmWFc;~SE3E4!(3{xzKA<
+=7A!&5{TqDtN|SoVq$ik5reBXDjT@{%*a_M|Lf+UC+0wG0*&aE<h!?$*#L=#6qW<=rSjqbdy=i8x8}p
+!$Pu8j3=P}=l&j}G{a)Qoqs#@?GC{4Fnawu_MPt3KUS+1)tja<AGy=NjF3tRWqWg2xbrF$dhx8u?E9M
+v~lh*G$wB%GsZ=<mnFZuz^{M*n25=Q!?LvChA=wa@?Wnuk<z1$Oq+qkMEp{9{4;pI!PD2R^+9uKs$(`
+|l`(0(ov0)S!XoUkC^RC<_e3fx|1ZZN^(Fux2Rqx}hQ;Q>oF-5(@{p%ew!r2~E8DAI2=;3sA&1kN3$s
+c1w4tZEH>dx%yXVKm!V1fNbpU7$i~{4T{mg{uKp1S%TRp-y25{08k9nK#)&LBLSd=6WiDvwhiHd^aa@
+rXW0##{)h(BRzsbdKNYFzAM%yGnM)z`g`m7X$rlSh6hM6{dhojEl6jyw3lQ<wEbddpXPRp)AKRWgl_7
+q3YF0Hu%4Z(7$jf4gp@Xk5n-Zw46kPzMtVHSlxo9PE<LIq7!vl4$FXCJfc*noe2WR<<!5{(okMSD@1E
+JaK0{-as6FocCm@GfYCtHX*DGTR+f{jd*p7HFvGUDIV%y#A8!|p|duOBg&%+KX93F60bf5qh1FWGs$-
+9Y2BPrs84xb?AJh^^QJM)Rbp4&4IrU2{_HPh&?bzG#pF28J%*7wCN;E^<3yfxCXR<)3tbPbBwBt)56~
+>^@Dpb-kR2XQ*al5RaGlPT_ZFk~>ih)x}q}ay7FLAifjjHD=Z?7xd~eGZvP2ML>;aM#krOnr6FyPjWy
+b&Y`*ptxmPMo)KMha8m8z8b$))8a!k~@W>CA@xt=CJn+h0NcZs@&KL38K2T!sE3Pm~kFh!INzZ37RTi
+pe6qjAn5s;VZE~8d~w5z6SqOvkY2zKyZT5%tE*m?Ixdp%M+ylEHLL)j(QE4d6ZqS4x0BKj8eK9>o*&!
+{|N4D7n@8r=Lok<6Kk>ET5d2j+r<0;{f&57c6A#WCKKPd?v!^}NfNsDzS*Iv%2w!+pHe-ON-En1Si^W
+h5?<=+M=Pd43ht8byI0eo(H0DqcFO3*|Cq+7b;@)5NhzS#GRP<}VD>zt4`KFX>V82?<kvZ;nyU5tSHZ
+y6Vlr-a;a=8cvu#hXR>@dS2MvyMUjO0Rj)l_bn6WuP@O|=21D0dcz@=cvLjyj>LP8I?XIN4)o(X2z*s
+P3-7G~p#1$e()sHlJC*Ald>{q*H4hWykl_X)q>SBj*ZK7Hyl~4JI!&p^x!PQI@9}zZGb!%#k}mL;%h(
+L9P;-42n9|PnIr$a}`n_Xf+2RKnXQX$&x6wE9a5*`}1f0LV7k1bS19Kr`HNZ|c^u6e(1+qG1n0me$84
+cm)i7h%b7&Gc6)Gwa*`nM>+%T0V6p%^=Yx2AUp-@0d*>6)$xGaG}}55o|0<xDm0Gftsuf?ROe(oz?Bp
+$}_jm~i+z;2cGTsXjZM>v^svUTe|wRXsHA11d0)c*-P#9*@D5#^=UegQJobP;8#6ahD9ct|HNJeh;Vo
+xMY;zDQH04Zo>objH$t)wlp5BtF~EZHJl`yj9rQ^@>c70;Dy;gz~;{uzyA`R|M}HFAoS-2{uxNeo2OJ
+50~g*kaa-Y;qJa}67<Fd=scuci5)=%H2jH}iWxO}P^)*RK2>_Q#y2*Gm;J-EhBQ~3Lpj1MDG!4hW;8s
+Kf$*lNS2E*aj6ONLtpPYi#S#UNWFRr%-+SU;uEUuA6y1gIZ4r1VLQx+5(SNuk|!S*!}h&S)#HLY7wpF
+zNa3G+vgHUI?7$)7-a_kRtfpPHb40qO8BgY{294Skl;{MSGYr9b$<|5KobwhR9SPy_YDFG`r2g6f5bJ
+G1{Uz#95EyW}U0I3AI`_>PD~F!gIpYQsT%9?e}$Abp@vQfe0W)X<=)#OH*J-|d)A@qDpvvum&~lO<JA
+*omXgtYJSjr0TVk=WqwB4Wba?gSZ`QrxrE*0_|&yKKW+cyl%Ia%unyr^t>VhlT&l6w_WrOhnP5+H(Hk
+?<OC1(`k9`ZJHCxJG+CB{1R2A7<G?9%`+61P`Bu8ZL!A-?A+)5g)Mw~n&7%``ySGp+uL7Gr;_bCZ`d;
+MIG(I7G)CsH)oADY2!q_Q7j0mt!pYAAp(-gg`0y@$fg&GD@YOn`ZTag2YxKmR}KuYEUQQqSWpHYh<^~
+r7!o<uI^X2wsALGYJrzQP;65A??c&XKQRJuL!1eb(uMzM!?CKkSEg;<D_easE?z&SL#~aR(3ixjgwTS
+VKQKY<ZjNcDx@gO511Snc~PwOcdSy8DGH{4Dv8(`=8U7FVpp}XcjypR{-Ga&;Cv8J^avWVwvd_hu+ta
+C*E5lbbc@ccwq(MA#~YiIDb-u{qofPDFT3gsY9kle0b5~v}kW>B;U1mXOQsuSja_59EhrsX}g0>1R|S
+?Z0pFKyu+Y1cAcM-0Zw3nIu)m;F%YtAbpNua`$~Kt9uHX=2r}2nkmoLlW0X1MlO(^(ip#<|YNI`2)ij
+gkd4I#|tr}nbkkYBPlNk!OZ;F`gZn!kRr9^(u%iao&PKC1L5WU2NP%Y~u-XxY*wI%H2Ub}~FsYUJsEl
+d5_%Fc}0ELGVvtJ=gn>dy@NzVgul3PcJ$6lve}0)~grI@=d)P8nk@Jf3oNky#<V1~gpU&Q`&_{A+jCa
+Q|W3in~{@cd>vTOA_?Ki`<8BQ~HN{F);iR$f+1k)4)-!Frj<P!g+eXwD9F0bQw2-*!K<?{{*%F#pOT1
+_Lmj@8E&t5xWXNtZax;9t5vY|-w+y9I?)s)vq0wy2U?Wa#}u^{*H_42F%n6DH~>Rzy}<;)B)biJfk84
+13|U9yf5QO075ZtS>{ntk>-`|WL<@{iuK*qsV4y5rFW#J~*QHnJ#<#BE`X9ED=u!&wFu|KK0IPwa<|a
+=7(r^}hv^7azGxR82Q|v$Dwz=VU|0mpr|F7Y8p$Xa-+>So6&Nlt%4d0-T0q#@%Ru_Q{hx7@z#ZUGeXH
+lB91^l^^x?Kd2w*SFO@EHr=)NfUhKkTE@0sGXfO6WZ-^~%5L0LLdu<j$W(IU|inbx<W8qSBm{obx`<n
+Q7AX%O<$laA<*x)<7OyyIe>0i?y}LPoD(bl+6I)yY2S7kmR5Eci^}@=*8W32NiSVgq!xJs-6)qovWD4
+-C+N4+t=B3q4agQRJ<c#=qvX3Re+rCG;TkgL3Y&Xj8NRUnxybOM>jL@>D<tmxzd7NPOvdNELsQIO2yF
+XAU?Z;9nTi>;^ICsW>!EutS{ghW+VK9)LCMr<F2j8?p*upHhjJayLzP66O`WLS<dEi4S>S41|Ph41Xq
+W1aYy!Zhx1&_K6dhc$s3KQ^1jG}PBO|iys&CZ$(-dv=oupxg+_NfwRKM@Y-8=4#ki*{*ylsfAW!@45X
+9bW2TtO;IP4bc@>bf*V|YFJkfWjJwTRljv#H^cT140n<c%A-AVrEUk*ookMiV{82j_OSJ8Jyu-*M2?z
+4C*9hb<@QgQerD2;vi%AgS8r#3geavf{55uA!ex%L1Q$O|-``$vLJHo9wK5*hh&r^lhg7^+A#Tgx%|c
+M-IE^Rs{j@{uOrroduz7#Si*4pN76g^+`zdBfJc~YF=y=zc6|?_C=z|wrTbk7KZj~SoUy*`=dSJqRb!
+5Q8;1hfSAe2%!vB9=S`(O>J@)mAMImomm^(Y8r}=s==`p645}{^KP;mX>@0s#5R>u!Vb8+rK6ZS*kn3
+*Nb4q{u(pB6E1u3ORz4at-Y)Hiwok_hwIUm7=818PN$npEg?hVsHZH8D5CgsG!n)$jEC&StOUh;{+RO
+6nu7fC&`0clpcw1cKbo@jBGsp?dos}jjO+iR~#VeN?Fp*>K-bmZ|wT%(wzV2`BJE20+7^%B8cH|Nm{i
+h1>AYdu!%Uj93~cWz#AxasU;w+__soGTi8W@qx&k@xY|=2CLvAAcLW&AxJi_|g8!A>-?LpFwyNv;NH`
+KVba-;@f<Q1%7yo@7k>_Ln0VOvFtBwxZ(sfB3IO3Q8T1LTNA;z=H_Nxks<(JsR$I}v5&npzyc}gw}QE
+zP4;@t*4a<yln4OmH~{r<WK+6EP_Q<`0%^GDmsE8EXv4)@{vL+l9@q#71g#MVxe1Z3IeUbFA6C4NDe#
+s+nrPEirBYBz0QXFP_S<B)zyx@9ZK9|2A6Zf|vL=1}>G9P|<NWr`$4AYrpJM?5vTe!N_~^@Mjd{H}gH
+y^+V2kk)lnav<eLM6G{o1KP>NVl3G*I#F!hdstuR7anzWxLgLyNY2(%JrWEZUAi;NZ`(z(M~(_1lVF*
+7aPgj^^*798$l^eotQ=I(}==aUsUj7hmt$v7m2O{(B4j5DP%RlT{rs1udL2<-iNYl+o#Yf2WeiKa0?|
+1`VYa*sG!9K|CFad!CIbxW|*X?n2K4a=amjo%m=C;9VYaN5|T~I4M4#cvNiOD234cl*##kiO~C8mzb4
+>f%-<Bsqy;Z*2Ou>U@>(K^6n%vdnc#S;w&}}BBGA|q|?NSrsJLVT-P~3t{`}6)1_$**XR>(GyGCd*y7
+QT;&{JQifj~DvNHmI1q5%2E+{Lu<9eETz2tP%XOO&;CqE)$Ki}hVCi(Lo5#3YZTr8}V9$__Q6Goi%a^
+BT({Rn$9`u~`Fvn^MVWLxx|ugJ%Hhwk2@Z)%M5KqndrBoK|@jb2C~kU&E4^~arPRzze(X095$?m0DTW
+rPSiM~>SxvuV?w`NPjH{65(>UF#yxctuw>_EgVJTNdC!oDV`PR9QU5=0LB9en}<Ut-gQkj<U6gtou6_
+&_wqniU@xuhn*E@>mh))PX$k31~YA@nTfEY3ygtJyl^a6v$vc~(2N5YoZ#;XnBGV_&u{Y)*5L%d>+r>
+-|B!wL=-qB!7}NY7{4Ng&@RZH@yN6yhybE4g_&hHFsKS7Mu22*=p@3d>BJw9ucs{SfbEQ|YQbBlIA({
+eSBGqmHsJ8!RR#tPD8)bFR_QDW=Hxc|iA_-w)vF}ab4<`Uyhg5+9FSm&Cpa5@XHJ{uki{Ky(uuo^;>m
+9h*1osP-)rbIm4|{U=;Hu!3tRlcU@P}%Cy}`CeSPEEzyPc-|`5j4A{RohV{gMX|UrsPzb+y7`EIfv=A
+<dsaw(x*=l7Y8UD*_F9|N08qU)ArClDW$b{F+|AB><vo^fS3#>o;*mILJCaD@ry{+vqY>q<tM1%iPeE
+`<rTR5d}jLZTYtYzV45}1DZgQTTK!Eos|js=w<vnD--gzH2v<%R1_B7S0c<@{#nII_zPvmvbWqmXVAJ
+`UzBuz2A+u)oVcpVSK68I)5rF3ldl!YkySZ32QfG*YNIU7R^gfL0!^((W|pjI<&Su?<@Py?Fe2w%FDA
+oJ7#iOL|8l=qn{L1K%G6rpYx;5SlHJNnEE_#P8m+J~0<3Qoy{b=n?qW8QsK<lq7}?TVhZlY_#lqV?nf
+ta?5A?pqpM^&1-mOlQT3Q?u^^o<0otct<NE8B#&=ILEgD$(Ls{`v(96Cp<6xy!G<DFt?L3!%>Wxqo&y
+W1p*C3SB@t;xx1>28<V%{X5zA9=2{fG*W-m#g(rQ>**6R@OUR@+)+IiG~c1PL)_+FF7r$>ts2WF5xo9
+p`<Q1%PhBRWPh>nh4mX*nXk}lF4p@B)&3gs{dmkz@a;<pAI^S@-Jpo>tPhl?P>UyyW)UA8k`Q1X28Ub
+F<X;pC5f}b_3sr!ShzN2Sk{p(#le1tE8N=ee7>7X@9EF&PMPWb%Tn*zO*kY1@<;+hlR4K@gNawQpl$l
+xEvz+gX_C2^ySnW>E4bEnanaqD8=k6!@%+Fw$nUY69ln7vHM8SwqLe8P3zipwq>Y){&7H`;16s+@its
+Fk-A;KJmbw-~E>@4!DvB<|2@>h=GS0cl3<m$sdFTIstTB91L*Rab;ei|nWz$%S+4ZBP;W$tC$48IK7e
+P7Rz--247mG9?$kS}Y~Z%GXCmKY(5(QYap<u?&4t%KVjk*an(58=^9wF}*~wsx2{q|zHl+Oltp)3FR9
+^+7FEp`Z`?6TptKp;HY&OBFkMg1TX%M?v-LGJklt_*oKS(e=yhykLf#(mkb?Fmz?LJf_RYiIx-vfKNN
+s#v(XZk7RkTx|^yt*wBZU<+ZO}t?$<ge`C0`7}(5tXefTp&E>-EY?<9BDG>|E?Usm9v!buAFOkKezI(
+AyN!f0%u1?y0H~_@tmFGu(V#7$h`vZw_S@cwH(ym_7sM8_~-k#X|WpU6;gF9>=5o0f=)K0*nY-}@StU
+|Z9n*4ypTgzh7PF|`Rk;n2vJ1^100YlE+*2aRXhU!vBC`48!dX<FHMzVI?Wy8g*>$t}kw?%dsdzxMnt
+L_m`d-q1!OQvp@CE_Ln?G8_kDrjzuZCt1}wQO%!Ne!mamY2=4D->(fr0XQ_yUGtA0;Z?W%V1H*Kr1I?
+*DK9(y^GXXvb(+M5~z*G87AO{GHSQbQB4+8)A5*&&65%djLHu2xRsVvARR<}TOiA6dz6@_TiJ(e#q9R
+Y&0nz1=&Q%;PW8ug;AGVCVocyj|5BP<akY&iTOZN5y&Nc4MVc|OnPXsO%yYFrkE39&!Pj=+q?NjLl}0
+o2+Qy4<7p?%Do30y8;_fFu;kJFe$i2IUj2^>Gikg&@Nua1TnpRZ`y5@tyXF8fQ+fh9&wq5GK!%k%YsG
+|Qd`y;-f#@&0G=rf5uIx1^+FAtM$IWE&nIKs15X!nX6O~N}?2`8Z{zS;SGUwWmtaxyP4$k!F-cNZAs>
+k9L}z%)NDZ7*6z61iTT@f;DVdvm+2(#_yL!bRZN!A>^8WE;f5kYY~_R|MIs8{NLTk|L&Fc{p<E{py<<
+f6&b7f)vZrT=E%-SDd-2v+=kiLVNclTurU>jiXCeKf22!E)1z)Q-QHqh@#$_-f2gcDwwxOzFJ=N+m0|
+=F>p<bNW24nE>|Q+dDP-j^m>!l-bo<ZQ%V|`X8CJ!83fE2-PDJoQftKbck5H6`&g7b9;+aqri(y6y7g
+6XJAg~*fDO9}wOF)XO8aB7Ug*-^qqWeD+`dB92ocal>zY^V6xs^uFhjLN!DBjM+r^P5WBcwMn_^P9;F
+O_vJ6QU3Xe=p4?uhB;Yt57($4ytpoJ3MaK+)D-+ZkJ$#ymO>#8BUP9iKnQiV<CZ@!-H+JX5?dP2W1*H
+b<|qb~fU;T1f1UnD0fWMRe4&ynINvv)!m85#3P~-2`=TDx%{sk@OC|qm-}|DEX?U;o_kA5>m%JDN4)s
+@Hk__!qJ18I^=X}Re45_`lCKwHQ||t^@FI6>@*}~T#JVlUJQ#%u-~8|(i~-1@#*n+?3>-WAe5#g4q=#
+GjQpYFnnt#AcrZ6lEyd{-x)v)!5!^{>FUyb%Zrq5F(biFuajjR4zBQ#SHsJi~W|OURRUHjqx2dO8ORb
+YPHS38Rp7(^-q8PNhbNymFMF^X1pA5IKPndNT$Ah3BFS2{&S7GXl^7bYg33~tcVHxpnz_K5X`4N_V?n
+g&5-&QdYvsxoDBQTDHoPQ`#Jrhgv6k-~37Hp(*Nc~K{7gxY?n7x7$MhqmRG9>IIht0iiBUrHqqhE0b&
+quuVUtlQ6hGnTAU>U~FMArmrHv-^A&5$vg&zQw<BP`xy;75v?d)m)+4O9dpZ)6JV9T)=QHaec8f*3?*
+kX<|XoFM+TPlIxCL{ZTEU$86*{!hTN=?g5Ip_us@mN`6bO7?#omKnbR%OnHwP|f%BsjkhP<7CW&NwVC
+Us&yFenpIPv_<)7ygR?u|3fWqt;(k(Y4-K~?vnZrvBOv6lWt?N0lFgOaQRisN`s0<=tM<4cdIl#>%ZW
+=gcGD=?f{*lYBGg(MD}2eU{LFQ4h)Pkkw=GX=$7@=CwUcP;ZZ6pPvDoh9^>Msn#{-4!mC?9NKG$@3g>
+U22c-r+hrnnzcgslJwaLh+}inpj>ZkG(VmL%I~JLCB9c-hPD&pSmhwoRQ^Ddq9OAf~s&rg1niiiZdC@
+Q>1}iuCm<J)U#CzMUC<eA-ndrJHjRKLk_KF4`#;iMw!PWNzKr)%H^2Oh58nx^a*?9B8KQqlnp>JpgtW
+V+|}r_gLMOERs4UrYY{SJY|eAv@KPhbk}$=%T=!570t<y84~w-xKhZ_!0XGF-0Nr7c07A~bPq><u{r4
+t@MQ0mE*2E&GM2aZtIBVwoV_^gdYzsU#G&M^%uHDoIeAIdi<{m}<#vG;5eu-f?u@%wW!l8;43EuL8;v
+!ndy0#4dp}>(QtA<(mk)-%YnA3vB=|0j+O@lPY4qk^SI@Ok8oNPf{Gv?gNRw9X(KimqZOdMX<aRu7r4
+UKzzAHATM#T3{qmPD?FDA5@y7N9#%iFdj58`ga->S*S8aI1HyTMzQ6F{HX^R}!w9wEz()^F^y!$oxN7
+K^KLHZbO{BuO>hC6>ADy$$~0_+ObpO{icIPbmMK+#_GYMuniMp`och_~!`48OG*hKBfYm2k%Sb4n=4v
+$b0AzN-=1PqBM$R`4pw8-W2ZVh!`4u7uZq&^0~lH5eFLsuj<b+xz!J-Yy5Mn-HP#N8xZGWGk&AmvJ%F
+?H&K|1*H$J9?;`fSJCcgzu=yAnEo5Kwe+sRc7ud-x@Lz~Y8{8a_Wa2lk>C^Yr>8IEmR2~FS%$gX6DY=
+W$2H3&8lOwpSp}cVHQf;s=hT`*owR7XvXM6fnD}#TW==jtY?c+YkThhYX^AlL>R?ojst8i$%*vJz-?D
+PkRhu(EGP=vWBE#8@Ne-kA<l`H@_85S2WTMR4QF)Qn3plRjp@}Py03WsxaQl4kEx!|N_m;0<nQmi2{X
+3-rV(;dnd2wiNac(;r9)?-h!T{~p{c;Ctw8rRAvep@wI93H_E@Yrt(bCV*AjgAk?ft2aSAkKQb#+hli
+z4JQvz}mBAUa;2lt%oOhy7mOG3tKMuXV#3a<nF7H0jqMpd}`#;-s;OStU{a`IJ=^~y(DA08@l)|UX=S
+`u+$(5o%L->j*3HVZ+OjJn?sbENKcXC?PBnw*_`;riK34c8Wf^Cj#}|(_ZL6*#pp(d+2Pu<=jQA_Qgj
+)&Tk2Y*d`&+QDG=l#VjOKs^E>IWYf8J*DqWS}F_STSiAOBGUhe9$xTX8$xaIcoIlc&DeA;zJNHNHnRn
+Mb;j&_M~b4tBuu6^Jnv1KLljXtycBgWk9^w@D>A)>StctNAoc3n1VZW}t?A(MKz7fXK#PM2N0%1g&w+
+u}fBYvCR}mS|)x)pfe;m=ms$(Z2Al!p;)0Nwl=wkuiEg{1HPfeG>-Hvzc&M`%rZCu@%rYv$^FlB`C~+
+w>$r+b$#x1a&zv<;NH8}^`^7SX9nzoUKQ19I89h{sW5T_+&jm7l1${pw`)ipRaj=x0Po~tEwbT!#)}n
+UvlcTsd!x<|YlPf;M}yHrWuf1(DLwQB-pYOR55tVx|FxIJt?|O;`tM8D!_@D4FJveXalgE!e<2nXdF*
+q$<$ve!H<bGqJATO|f8X&EgUMY0ap?%+LjWUV3No&83bL=D)MhjnRHd^fSoFQlCOx-ijv?sHImqDRG8
+jB0lbQ6DK++eQL%~dHF5iOE<OvK={uqVK=6;(nQ=CGs7&r`_HO|33#3T%7qO%HTM8ec{LO>*p&CyMOc
+%kYi4k0y)!AdP|7NZ~|m|O<Pn}&()zb)GJ=eA_)Ure%Z$}f0Hu)Zv+YgdwCKZxLMQQf(yCpjouyh@DU
+<Jv=weKP3QTONb@*!ma}$9g37sSg$inlD?joVN~`#;|pa!H}#G{G(nZBR+3K{L}Zh_D}B&`RUevy0fo
+p5XfK1K}_9}TB11qWS$}G(XJ66qV)#7FT-7LFwJdX$BIR`e0g8rrIwD{I26}WU9;V`3|rUoL%p9oRcW
+8kw&m28NONUUPD*;&pBOTU`B10NIwGwj?}6S8Y1ewS^ayBiecTf=nMLE_T-Q^1V6MQSQEFlDA36PK-k
+!aVMVYr?_+b@uh|w+1c)*AnmzmeyF3`b&l**IbCZKyAWvVfh8d_oZUNt(RF&u|gz@6!ZsvQ0Bx-=2XB
+eV`hubvy-RIbU$Gtc2H#@)j;f06Q3(!=v59l%1p-w?Q(twMXhiUiK>s?AkYb>w+d`;pyxfhq6Ubw4c3
+<;FQM9rn21@EBWqyLCk$fN@JhTo;ein;OJ!;<+z8u6>c9br_lB%Eu3<dMq9uwcre9Q1{ZGfyi5W7Wt>
+;!I3Ps*UjVXU>Ma6KDJ`aLS8%+<XjO2nwr<;<G!eIeEA%_-UrrE-H8O=*a2Vf=)Dj>9NOJ*=rv$Zi%g
++0HNtmWQO<9Od*aB;nlo>YljpFDs1_T`a;p8)B*J=N9_K;1cO|oqWbJI#<IqjbV#+Sj-mtuJaiI`lSn
+OT7UK5UTIIx%2q#MTf{X->f^p0W|+gcBvZruk3^=zH>b;Cw_dNz6aX<l}b?jpJ^ZCK|v;`uG!dlAz<=
+!@kXBvLyb$Ka$AQ($TBF{E$8dVvLgg43OoCuQR<diBush`rA%!&qf(-#FvwiEikDY4q&0<cV5$%vny`
+&1v9(jSC(V9pA?TU#>O;_o#;r|2x&e`{t6}qCdnc>N@;I<Kbn~*IJ{$*zet}eAwp?{5x}kf1A~ch`I3
+>1s8lgo4XX}P&bHV;d)GFv%+!$Khj|P_gO81fq@emm>(JjHJAhz5<wC&1}knk0@|dQfqD%X3hhCT&&@
+cZcc$~7wJAABFb2D#1Pse07&J-Ptk_J=ArNpGf`W#Hfp-N1Ejc^?DVn)d6f9g~Fqi_1BX}$@BQMI&uo
+Rj3TYI9-%82ibBtiX_naopP7Hwtk$U6istgoxK;zIJkK|g*7WJvN;aAKUQl3Fe-{CP|WWBkxw?1{i;8
+Rm&6jMEu?E&6$W*#_*#mztmR`6<nB{2V@M9H9cvSM80#2e$#Z1lHRffh%^w!JLfpwVl|9vK-_uMIUtj
+us`|f_~6Z|OXbBeAeM5;Jq`<g<G2jzFB&AI>m~nunkR)FZO#*-wdDwgj7j=cwwBLC%@=yNC=QB&+lS>
+{Cl}YHQ(>$!#!h;;^a#~BCvV~R>%>8~<>}HM?y?EYz|y1eM`ZMMy0x6rXma~_Vm*t|TMYN9RO}v$J1a
+9CLO8YOG|=5nb;~%d_*i%SI!Jf<MA6Q$zBCG%?0BJESSL@NwA-V!)I#)=-gYNkJ|U0ojk#EB4~-)#ao
+Sdt*j>L}iBIx`?WnS8nI<9C)sw-Fhr$!+gVLjRL4#Rwl1|s-9bqrQ7?+teTy_&7oSI(WT~pV-2?}2%U
+Z!61?V{27(=KSvWZE2Y)_Bu=m>_o?`9bu-ek-gM3rpKy#jCG5&fzrtDc^$p%!d`CsmZC;Me{tgOY!h=
+Y06+=bn`-RL64sH4{j<eWV&|#J?b90blBWXe%H&xZK+9*h#c+16yh9rpOok2%55}z>93ylhHb!#o<0~
+;qV>fRAtYH#Vlh3X0cuZc>sh;JjuzHAZ%?PGdaTIj*vHsrRBvQP?xeL$Ct}uKc8q=zgolKz%o|Ce=kq
+z`)9BG2w;6Y%#uX+Mc_81cqoxXC5~$HmZ}7)8j3vMA>f^%BmKTaa*EHgs>1+b-T4he59X>Ng28fAf&_
+l9+%Kfz7(#8E^+mUea3C2uM<gLMa@L<o`DY}Fcj)XTixbfH869d%Xw0KDErE<D%MY<W|{=qt<pX1I(t
+*UmonVj*@m;XMB`aja__J^5)|5ni@&E6dWxW<3?_phem-#o{=+4!sDKZV~>_&@AhpM*wXH40ev0h&)T
+C_RiOu$m-^VHsyO(+bd8fIs5?m3%K90Ty>`ZW}<%gi^53Co>t9i=l%9t^iI6{RIj-5x}10<g7;rmiwR
+djnlcj75Ej9$dEvC1;xRScm$mu=!ax8Yh%_rPBW-^M9yN$<Xm6F&CQ>v99k=29#a(7tipzxH2hQa_uB
+t==#yOEeG-}1OTYYH@6P*TN4AI~{pw2z?1<+Q_AxkEML3s&P!;?c&x|$iZtR0We`9k#F(*}4M<f-6^I
+Y#)d*G+u1wM6#w72&l(7Vu(JQ_{{?1?s+(kwLeG#z<F3@3m_1~aB(WUSmT6fnv#Y;sWfx6r&dU-jcfk
+q=jW>L>pEmMu^{6Ak<osws29&NlR+-re&U*LD?`ze4uNcLgUuaZGD-jk)%UE~3D#)m8uKrVAoJAHF_N
+bcZ*Ax(Sbd?;XW>nVp558ZMZW<!&oUSZAi^E;eGOS;~VTOtcL0gZLa64B>hYcTs$I+6+N@%2t=`wB9H
+A-k}OdjI@Q;lu6FTqpbMAzhDE*&QA;Dz>zIpGMKSNj)$z27V=;(NS>NZ&Q_90#MGvw#mv=@jk(hDO^p
+g{Ev(p+db>KSW*zsm6*7u)Shz^Pk|~~8jKnn=YsOYj^#a{rnd-)<NhkEjhhB}ueXsXKQy<c1+I#)}!Z
+TQTC8WDVG>A~&L~P$|V)ZURykIqwVRgPg<uxO2@9A?8fqA>d&r>M@zXZQ6wB?nh<P#c>{Z+R4BeJ*Nv
+;DTADO-adUWFNcCHkG=HJU6JgMAWYp|Cx5&+~>f49K?l9CqD&Wi%>%8xtXz1IH@zp<_Ed5{2s`-)zH!
+@j#M!%3{N7;P?9q)KAoceUoQ*HlS@(CM+Id^tgaM6*n8MTYMK8c=d{eV6S=y3l9>7K~qOrMTOJri!-v
+kJ?^X6dI-hPve$CoDzdp;_ldabZF%g?p=^)iuF(D6(DOHq%EPKIV(xy>y_Kd+HIhZSjSEA&G_*p;RE<
+46Q&UOXqb(25LfT`?os)0k!SIqsZuE(Ej7MAfs7NWUO-(d%CtA_Cm-oCRw8wgIAKZC|dk=QVE4s>7T;
+9fP5=)|EoO0W7^r;}mi(+Z0^pMtxKO#)X%R?-;=foXiR%w&d&RABczZwTS%F@NM^Ci(-u!N}5n^I`Cs
+qzCQv&xaew|QqHrJm#Y2HV_4-VX=gBu@_^IW64E$<;$#XSlvSuE^z5QUY(-+pKXJB-Obeec2t)|JW<q
+{x3%oUsYXS0&AZ>zZ#~`(YSwj#GA?c?$4hKIB1eXG4|VRGlxOT1H2Moh#=PvgMJ51{0pD+&%laA3G_$
+kSt6hPzRe9jCx+u$Uk|u1bON)#z*E6unBt8ID4_>@vWSJ@JbKpS12zcwXY5aHZh&{jvCzIk?qCA#R-Q
+xErj&rOKwvGC2zo|PIgf-}psH&QeKugsfMElc4;Z$Tg+hR^D{~Ghg}^bySZL9+zjc@7H6#H(x4#NFy0
+LP;55Y-aOr4DEC)qOu<IU6=o@`4{3m;ZJ&Ot649h7$(vF+Nj0p<N6M;lg$eaLJ4s<(hW1%dMcaI}-Y)
+myx5oA`N~Asb(_&GyEy-9diKHV@CQS9$_3Fn+OOowHimvNiq^o2|edyqm7yv0Ts1kw+f7>IKi`p^n6j
+B=cFiNwtd`8T@Ti=&R+R*xQRqnzuueZ5M$xOvi><JRaK;M^Derydz<|(A#ubSe0;D9Jx+?IF^t`%dro
+)lyWR$Il8JV>_l3bBGoS6j&z+Q5ql4mW_v*+1btlWoBY^J7ym)154t$)T-Z#|3Qlgi*-?j6LGR>kgSO
+r_#ZgDn@m<C%gkSPGKP4DKmQ%bRyDr;QI?g)QlNu9BYYK5M`(7O{h3pxVOKEo<t@EKXmx<c!pTKI%YZ
+2En#Y8ft@@(aTjct&antRX>dC8URrB+W|1|WOWhmpmqONO@?=D2&H*fCmCn;WxcBKDL>h>a?E#h!cdE
+SGoYQMVrwqWbLN%7Y9%+&~L8K8Q&RII}PIgI9s4{t|vu2Rk!X!{A?qpjky{PZIWDr|eHN7Wt=B_ScO4
+B?}CEa~ZKP-2hYdHhXwBFGGQif@eo#E+@7IfXr|(sh=ix6{Jan=<TKJI6!d5Bol~#`X){JrI}8wSi*C
+Oui|LZ@RqVf&DDD6>0ej;fPBhnD$g?y0M60JyV1O6E?=(1jmvu1qxBsOP=9s$3poJ=236n(>DX!B$-N
+s=gKkbdc-njGFZ|LiVzHA=*r=rp;j*V48s#c;F>Z4Wvtz319)ehI`hD7$k=oVDC80>PE(VWEw<CIwwg
++TzTz+XEqHQ44m7`cho43bn8KwiUX;)ERJ~jWQ(BvsGnRB=9Hod^Dw<FHtJQ?}y0>OFP+qI10rE7Y5>
+s7%_*0>$4P4R;H!VbnYci#CvsdvVy)k`D_q!BYGa#iM!p=LRRyPBI_!JK1%inpk|uNl;|M{~IY9_x)2
+AKaadd;1{M<W3en4lTK}y<l~}b0#t|8WcIXCNGl{S6il=CBdC;q9b>@xnew_4<$Bs-pTUN^O`9SrInG
++BJ4=hlzHyJTq=QJk;pR3yMA5?4tC3gHP<Oz^^YA>*D?S2uRkQ-;l<|G{;^j?Xl0MtAMPF;XuEp{c#H
+ke+|JP49{axi*K#=w!++nq?AFg&v81WLc&h(nCw;Ty@PwaE_nj@r2!_Ntk|J@IC25vpDGH+*ieYG|?T
+$gQcl6u1C@{kjHOtB}unP`7r@+VvGQkoKnt0%(LIK)zu6m@uHS`n<MH9IkCd05f#l<pEeUO^VBU283B
+q<nJ1&$|Fs!gB+oC2HtlTXXeI_`Kr3oNDZr1;DlgP~dq_NpUjDDDnU2OM(Xa+CAvSg6JT^_{Xgz)Q1m
+hZnV8NIIJv=|R}>_ex6#=+mOnyQaInA&nB_c`deVbZi^pTKCbnu~Jb$&fj#!`Q=p>4qth$VJE}tWvNx
+=fr5$+z@k2n5$?i<H*;0yC&$>E=_8(Fd%EjZp5uwYqdeF|Oz1ZQEcojCGJO11WS#=YV{`E)NFSM{59T
+s<^b4uYbDfg!HQ5$Cc_o1p4v4lgRqKV+MnU4*OI?9W7^l_jIlpg%KPMpq4wLiIdLXZ2@bAw-6yS5Po~
+w!XUez2NsWOkTY8Ai%xj`|dxi0F}+eTj9Z8uWf!t>0PTiJW?(}h8SR81dA-F?tc>>Ne;>}?_GVJ7K};
+3qm4+B=^qBc5k(H4!ardF5KJ`A9B&;i9SZhG~$00&0i_707Gt7FP%PbYt&!ak-FuvD=hAjmVT0Kk^7K
+G^!FpgI90kgDe(X#X|S?V(f0wj^11(lBgNRJ8Ryhq!MP_Wm<e*rDYN#61wyUYD6Ec6$Z<7EnByni<Zg
+bp|#s*+$dMUXuAultg2>ErO_QE{!ocx>xB>VQLPbD-YboaZLxED-@CU>$Yu4D_o>s7L6^IwNlkHa;k~
+Xq8RaS!QPLO=!)C*CJ96BO=PObj9szqzw-RQvjfL~waF&LgO9+pScDM2Vkh67krJmz)@m$}IIWYMfMh
+*t_pl!x`<s)%^co0On2;%G5Mx^X)#^$D+y^0|oG_yAz(}Bc{oxSF%WZQCsA{j->?#tUNra^!^egm_sd
+4&(D^jhNfJiYY%{hOA*4r27mG!aW+f|TMAc-wQo&TJbXyNTysPQc2r6_p{a3uDdSkL9!ZDAIs@^8!8K
+<k`5tUKluWUgZ$4FNA(q)+YW4%RrIWI#6(X!TPmUR5XW1;)jnI%@Te>3kY}+7c)%wvgw*f5<t<kcDda
+USa@=+O<#<+#SQqkx_RNO#aT^mZ9t2qu_(QZ-VAw<C{xxi)o}q<j)*ulYmYB{J6VTakeesvR7BRUead
+vuo$ki!W^5aN3|#&0To1fHisK16ajZyfh3EF6pc=6*u7#QtRIymC#KPD|09I6yA%@uE&=;ykOq0jgv1
+MKAe(6cBry^^=>!?c{UiTGe^n`AnM(%bxJmru~?-ak3&*}Emrwa+!C)LyHtuO@p934{qCR2Ouf#8N{G
+U18_c5s(mxD@ZvxJ11h#bIqlpH|*^*h#x6&sv$_+Ds%(>>1@8zQYA`XDkV%&M%6$bac<D$a@J;dlYHw
+rKSB2TKF$$!&l|4>;FrQZs`!+vi}*x@IQCrkEr4Oq<_4h^lz~PgOVtY<1~X39FAicOHu@eam=?;$_Zq
+^0CbR1P?RZ-A=f5jAyR<p)>&%*rBg2O^oj3<^-&U*<iWOq1Z2Vh@BvIaI-6>M8t|EE6DN>$13Y#Vg<d
+^msB`~Hf6A%Sb8R0v=V5`<4=w=Qeb|{Mg^>r6g%NU^fRF*)WW>O*IiwK9P!x)r^TUw#&Ov#JJb^h_E`
+~7!7K8YR`1>G13Ui3q_$Tb3Zvb|fg6pz&*UO)<L-l_>c2Hm2n?mfcny~}sxv00d>uO90t4H?AY7Eu%n
+AGmf4B9&9!JK%?a0=+#jWaXo!?vxXjs8g#-iD6+yJLSFI`Z$1{cY&TzdQD~p(AkYztM^2$*~5xWA;tX
+vQ$y6E(mb(bM(Ol>>|t8i@_|teRy}uaP`Ow?NDjMtv{y1+E<<DHR)YeRuX!fp*NYuYMOF&#Gvi2V$|k
+&pM>;KCw4`fS5CMt<MY+C6U*BeA*<hTHeRLuHSHG*;km-^Wx4BkVo64baY&51SkaxcIjsj--71XKdgS
+RL`*DUz!sv)QeBs`*-i#NM$*Wh%tA&g$$y_Q^GesJW9<SFMeyVFt=nizbS0_=&+D&nu>hWya#R9nUXS
+7o)`;niO$uIlxJhHp%^Z2l?8X|S4e3wzIAdjh7QMYp%-=7P#IYqsBB&!Kk=rbyie))8c>DiKUeA`h5r
+e6$y&#k)=Y20J{#yu+H^H9XWm)x=XC;jD*&GeA>0Ks}S9>ySE>qn{bP}Su8Rn=k}v@=(RY`sc9TAU$u
+!#3iHM)DN{KcdJ-?Z-a>(mm&SGTG1Q_8hj!1#EYZ;JvYl;GkC!PaptLJl_Dt`ouRMbR{63_?2)N++WH
+UgQ2|bhrk_$zhFq&&-0GBI7%I6kL4O|4i|q*kJyDdQ_Px63}m^Cvk~)4@xC%V#diYbp3Cjn9~z^hvFm
+-<T0O@LCpA9t@oux*$Gv&RS*0zSvO8BLBFfBiGzzzC3jdHL0wt_H%5~9>?Au4OK#u{3xgo`EPX)b_i~
+aL)zigjZQwnrmSQIiM>f*6Xy3jS3Yn4}6)w06uDd5%gxSw@t{p87fRZ|!<?SSp#VzrG=q-qnXdS5p+*
+(1au_P35Gvg>=|KD6tT=JdmbQKPLd7Rm9dFVB}!ym|gg^wcHKsH=?0ZHo%Zdo+goJ+i(UrL<e<`UT6i
+u~)IDn_m5E=34004QXMKTFl|X@AD@=UId!C<j4BT|4y%*V^qi4t^1?N|M=Pmju-zBBd{eeZ*}y?p8rG
+CR)6p;&X5GoGGE|8@_M4Oxcql>E53Hw`OjVET|V;HIexs_-;#f06ngsEEbN$aFl~TybKf2k>N~O7+*y
+HxkO7b08~=Xo*ptDm`D-4XdrPY)kfz0wbCY5eX7Cw&7GlO=D?1v4no#ji^3UKDI0f?y7!DBzJ+pUTWS
+(;z1|I;JcnNJ}b5r36h8iF?f(U6AoJ<m!bO*l^F=TDM$Y9cQU*Y6$6YcT?#&&7{@5sMjrK5oHlP`!Nx
+^Ffyvs?oh%emuj9Ub&O*b5G9J+OC!!g7kyoUF+>6@;-j2JMv)jL6Tn`GgH>vk82x1SgpyKfQ0Dp7I1t
+p2)xX*rA0VkFl?VWzQ%OSOhZ*Ec@OFi(=;Bu=JYBfLLIf8_P|m^inuye%fY#CnE_E!Y|J9T7FW3H40K
+NceDcwYK<MW6;yIIb-NS<1ga&Nk}+<j<oV*Ay~Yl{WNL%=+cw(}C+`3&T#UUoAlf_oppqhG)d#gVzDn
+iHG-U8BPT~F~KQhM=q#tD@(U&*^{}M;&r_iG!FI6O|z?;fF=acrtHsl7ALWWsk3GS)6-N(rNa#$_bil
+*<H2a`0~{#3co)r#)ZwxCZ1Tg00U8$ZJH-dOJW)sRN#)%uF&NxsuP2~829Zu#BKFpe3OD8xk!x;<$&B
+zCZ5LmO<>jIMDY(?xh9HhOVQthI_B!g85J_lj4FE5ZaOEzz3Wr&igK;;`eNk?Ws(UnqA>SZ8d+lvNhv
+HhuCXelhr~Q#{}SacS?iCm95SwpJ3IPx_dzEBxvj+A=vw?J#;C^}J}-$_<--nT6Z+$&QGw<xM;>JaOf
+vT(bsW-T}EaQc_9bnv<BV38u5V*musUSuy7Qp0op5ay+#q&+FYK2MaTJPiXMKUDz6M1&q7)lK*(M_nN
+^6(B6Y@VE{vURf3dUI3u5pxF|M3aQmtRiM)UJcy)<O>XFpR<HMvG;rIU<4*1j^f}|X*bBPwgX}R^WY~
+vgPCB3U)QEYxl_sfdti^`r?l!Xkj;vD_p5n$3UFVPEG2D2<H?C0tIu85fc&wo3O#pG4Pps)Zkq6Jm?^
+%~Cv1X(zJGrYm)U#Sl(d2l8HhR3>>D!M>XH$?*@)C)rzKA;dySe=9qu@qtYxMm>liw49;s`1SOJfg8b
+{2)ajgO>?Fql(p{W5<`dzGTijBSNk1lX>na&12<@dDGhfy~KK?o?}Wl^Q-Z^NV42p;$;%1NQ?6NE_&*
+=dq_47Iz$^yzds(*h8<~<W0xt>E4kC`b&d8<)36fj<-QV&G`1#w(-b9wrKg>0?^ISk9k9W_Ol+!NP91
+9+CGT*SdtVNwyUXcr@e~jAC5@94nkF1G9JIQ#<ODg06PKN7luz81$6PMEb+f{QQ!AjMFx@$!h^T-dx4
+?(L9n$CW!b@Wslg!yD&Z%=KP$ZKqw$i(rak=O7SkaF?U+!18#hyT=evt-`E*PDQl|?4rM63I>yHjPJ)
+;!I(7_ifKq*we%3#H=Pi@`%Gv1A%I3v>AA&_$`5II7-_JwOEi0pRi<yT~`N<%etiEoez7XxW(<GVRcr
+Z$RcV#UX_%L(JTbf}KT?AW8fCXbAD5a2Z43L!q$a1^5Us3^S)bSO&rscE&^*3ezJA0p+w|Z>gU&FJaq
+@1TxGqW)5d$@b>5&)*gY=(Q_pfz$D2m`jlo+;Syk*JcE5PIBbrgU@*f1;0=?)ObV0;z#$%E0mw=J7PL
+HP(1QORXyN{kf)=n6l5e2pVB^n60Nc^ZW`lg(`E!Td*U}~@c$$(-&OwbS`Mm8Ua9<nz&g1%>VIkj*>v
+x8Qd^fJ|h6UEtKQ(}a@d{)PSXgtwLVFEZl*xKyUC{O2f95K#61$bQV;#0W_RsC3?6YiFSnG4s7vAHFB
+1XB;oM=^uyFDSgbg|!aI@a&<OPEF%3ao?MVd<weF)7ieUTKYJZSv=S0T$KkA!U{wV(Dm=T<_R!S_caz
+BbMq~rW{{gcn7+9vfFqh8^W&(+|{h-qr_}uV_C#9er)&UPH8<P<6GjIp00|sCN@hd*#RfB3s2p6#puX
+AoT`HJxO?2ESzFPP?w`>}zl#1kwBmcja1<hN(zB<R{YJ+3cUEi;bb84xXIxpw;ihkV@H;?Uz0nulX7R
+k~PYso>2P$DJ@g`sWBQl{!tId}CQD~gSO;~n;mZW&IJ8;9M3YHIxU~1bsSHS8^kjUY}D55(Ia`CC;sT
+hFG1;7P-BND%S%U^6I9yg?YVO;s)SW?Xwk?HyCMm|jrU~@lN`O7>9UgWAjXHcYz6y=>x4v3S)+XIbS*
+M%#!`L$jBptSuH_XNDoFDja{Wt|LB5mfy1O#CbSu`XWXF?&G-??E8vpV199Lk3Iv?mZCVmk0_kBQwio
+#Vg_nL5d|5L2?(tEgG^s?1s<$O&Gv=kKd@q>=hti@f56?Pkwlq05<kt`(q&=V=p_YAI|w%_QcF4GooM
+C$7SFVmF0Uo;~@t^XkS&6%oDR)WRoVE8`nVMwX)9F2lTXDy7(GBV0%<X9WE%gYrzl7#jrJ%^XM$j8?<
+gtd{661?M=R(>OsIA4<&LNt<WnpvAZ?9)%K>8bu1i#D^)V|{?U$S!ASL;&NS=8RmYa}V~L-z*3PU)1f
+J#ER}gQgE_VO$SDmh~*}ipm{tgW_55?HUvk>%cO$A-A+WLT2qqG5&Sv7M<oAv-`IVO=HSz-;B#eI8U`
+4~nXlc(RDck#v%EYp4#`C}67F_o};(viVhd#)Qbuf%=7vgNGn@pOssL9_DP?V?H4<zky`L)upNhcHb;
+KH~P#j{Tk3i<$MCCOQAHZl+UKj8jwo@&CM(m+}AaA5L_+cV+*GF5|h3(gq-oxBb<qYNn`<%c*ZJT~w5
+Rtn>cAXJY1;gU(;j%>O@Ee1kS`N1II_-Np}B`ig3B>RW~gOV5R6>70Y%=926LN)b^BjGut*X#xW?F#{
+n_{(Vo62x0}u#RM3LA|NXSry%A)XM!f|3o=*Y5ityfKv|+3Mn)LO_57)FZWfkI=94lRyf*+z&<yH6rg
+McHGdD;EcZDZ2gLQ8A0qg%*NE!uqPQ{SZ3QP95nJN-Nk}Bl9(Ep<UHp>J20#OwFU&2GYWqkEiWGFmTL
+|=mzM-TbXuS1iER7-v85*&2OuwOgi&e@HlnELiHfjvH?&!7QuY7cc3u<VS27WmIcR<F>YuIN<Z?g_rS
+M0;TqUaUK80P@=Z1AvB=8~79kmM9boAm1~;Zv)?cM3W4xhxG=4`VS}H#6)`y{gldTuGPe&6f3JK83h4
+Z?}hiXeLUGqao?FTByBXI5~X9ICI)N!WVj7xd**Ej*!b<k`KQkY`RU>O(`SSH^l<*^vq64(I6pp{-%=
+A-_lCsu1}7!i)$&Dr8y{m;x@H^ijygwKib@HYDq@fIzVJ-#4Tas^M2zDtg<TzGLnOI*#%h__W&PAlc{
+nYu#`*Lxxgk%&%l^R0Zix`Nosb)@XBnz87M8cYE#q1jyshE%ZRMRrMj(ZqbBa58d3rqUOjztM%}!kQ^
+>9sU7V!&dd5y{--97M)M0#5b$Ay*ONNrJAx9wAputi;Ssmnke%;`q%OuD1XX^F;oKyLzM+a8(zI<A%s
+Upa(*qMnA@TGySB=X|Jei+0TTs%P9y6BbV#_`w&GSuHYqhg$|NcO_zq_t<#ono2E>Vq?=}x9+P6-(rb
+Mc~{e-lL%e3Wy!RMdR=S{;)9y_WrAwrZTC&^g;!v#==)HS^$Fu3p9r>^CI<W`0Avban`=iRf5w!(n})
+XgTseEWun#S)1VWKdlVvX)XOfc);5yzFdptxpr69zreT0=zF<}%g(LQ1&J;H4%m~;r3(@N++(GcnYDP
+9Yp$X!#O74RrfP4T*@UX8)Q1SVX7CmFzJ@b|t-h!%@g@GfYwTto_#JbPB~PQrsxgf-V?KEO2x8&*7hc
+UQB3c<A>v0uPK8Zc1?PvhnpcyVwtSB5>8CriN9hobT`=p;BF<0~|^PWs<tUufUxd1w26=<LNO3;{ng(
+2yV6hH6*tP4ov|*Ki-A}h6xcE9UjaUenwu#XcV#UT~fuHhXNm5Rtn$*?J=oW)9W)iCb;~%AH0KS1BUo
+OG(|2xzTa?>_(;&#l;b0=pJ35xkKk)NYBS|bnBKfFWNw<O%nxXmt=Ql;fZ#?E;8hQ6(!key0blP8JhW
+!+Rj;h*k3#_v-Q?kc@q7n!Q<x$IFF1HeP=>vQ5>H@Ay5k8ux3UGNvX0tn7LjkE9z2{#FaZ5aj{U20mG
+V`&3T#tNEU4QmAglsO753%858t(g2@beb9NxvNiaeYTzm)aC*wguL?GA}?^Y+?2_6&XpSLDVQ{d%{L{
+Z$)_^T7a7|7oaq=zSue7*PB>Us}`Un7CKrW6>22j|y7~7!-g=Z!Ylj2yQtg4q6X_wMCwF%4l?9BpfQ-
+*?1kF63R;t>S+}eQdz8}Uc0s%L3g`=6qfWtENTM{PrI>iTq9cT5cG6mrKj9k>@K}hJJoY2H5ZDAH>WA
+%PGgSgowl<!2fW&ITZZh)s-PF9ZtP_fySWx(Wx62xQ>nDAzb6uryNYpLW-7gM+ay^7$Jry5yX=J$VM%
+>ir0wGVO!+(A2RrZnJR>=FjaMrGr3un;jr6v@hs|!oB@N$z6@{YdY|RggOUpg5P7WE1+838KW{uWyFQ
+!?zMp&`0Q-5nTk51k-#<rf~LD^chW^4^sEYzDb9Kxo=mT~mY2~cl9(w(ZTe}}BXe;6eH=dSh@APZoRO
+9n3T>sntSGKNy$YIAasR202H$!N~^VK9V8b5Lc1n;YE_bFL9!<@kFNHOoNMOTM(rqvqpS$XSN1VF{Qd
+Olg?#%;x@gL=HoAU<U^KSCs$M_HMp96GL>1$M6@J`<yA$;4o_Ldz;K9!E8ETfQ5NXJcE)J;M6eMn8N1
+rU|h*8qeRU~Snvx}tHA$OGJQY6b~pR?(BGd4)7DISs=r7)>{sp+k)0QQCnO4svW@|>Fu<O0UlM+h%VW
+SbN0X1hrf@V^)ynA5qF^DSfE`I&O$M)mNPg%S*EzBW4A8}!fFS~_8bw8T1?&!YGaPL&UUPhiJXXKXzV
+c|WN~6)9Z(gM=3adO99x%u$FhHpqf)iG^$X}h~SKc(Tb@<UZxiA(u7$^OWI1RS(@7oMb3BQC0HP4Z4N
+K1JkLOl)5x_IZ{AOLG^J@tvN-ZV?q^9O?rEE&L~01mv^-uPl3kq|$>SnAV<KDST9{`R)<?g{Q}c;h#%
+U8+Lf&h8!B4m^vGC|CR$<tq7G91;0Ud{RvKeV5i+C@k?w7fz?G`P0y_WMC{+`?m9xCBxjqIupzx$;sy
+$DZ<meHJx>N%qZ=$ki?5;)U43ribXXGeBUgw_@G+RAm{6CUE%fZR@y?&I_sUcyp;!}TrV`9Jm>9BlXq
+RAii?=)Mley1mTn)Nb-434#l|bI=JAGI6lW7}m!f9}j1}Lm>LWocMma{-XUqs8<6C6zYhX_J5*9<|xE
+TL)T<q^4Bk~<){2j=Me1{o-2QnhxVaDHqjL3JG@pm92@*QUU9mt4$hZ%ndG9uq$#@~UA$ak3WcOWD39
+cKI;$cTK08Gi>dBHv-g-+_$CcbM^aAS3b}X8bc`R0N)P;#)HuZp&hOmR8%zA@xB)X{6f~lOv}FN%)$q
+19LAG-Z=@{svC}A2ev&pJ6u~=+r+F$R3Gl++9C-eHV^T!*&HI|LJ0Tsvl+HFI^ZL7$~SAf>t$k{Un%U
+ECN{b}sm|$k;XI~_iso|k*5Et0)`NIxw(C7&mi_4l&|Nect*b>6Yps4CgvrnL25x?O11sWx7>4~PulE
+&*y<Y3XrT&b=$ZrYhIaD3bXBroS{wu+uau!WMVMl82@0-r#Eh3o(TE6Fv0kFm4Fo1~X5d0)LByPO`GT
+3u4gSlNMhmk~rfiRDz=2ABXK)0WGV=)X*X6Y=rLc+u<z)#@36a}-mFe^;L|C1=(2NK$5Ar>~CTNKX%@
+7PQ-BT~qL$B5U$buhF%2T1U5ahNlGYC9-Ie@el=;;`xe9vo&3M)8ir`VVLK$VAnF4KnEuSVa1(Xe9C;
+j&x+ykcy|_y@w-5Lwesf6k+#GH1bWK>Tg1_e-(8^{z*voOVknEmG%~OynT%Kt};~^!8b5v<p(S}{)9z
+E;WB++%qE;*cse+XMIiI^VjN;cS!<*J2t?_GhfDT6JmN!u4^0f#-EFhkE)RzylQijAU6m?Sdv|$Ft=&
+}9CK38XBRxD~+76TC9_sk!`30TwlDuZu_oFQ$xTn?fexstLWE9tZbj@k9I7Df5%lGERU35X9b2ZiHXP
+U_F<2?x+T#yV+cQ&_lJ@^Qi0cT4$(ws(L<-@iBhp|IZtmD55)xCxGkk7Hb?}=3c1a~$6>2H#?7x_x|k
+)qH|`&_BM3+9E30(9fivyuMv<j+O07e36`dD<5iz$FNn7{JbQleRMH7=K=1^#X)S3Bc)g?R9S$B=U6#
+^12WJ9boz1u?TpXx&of$H3S7<fk~&}BoMR!XaV~KbP(LUh>(YmSgnr0grZPV3Ema*^|of9;(NCcTpdC
+s@M2Zrh|RBVOu!QXXtfvc(6?K75dfnA6o4Sjf40d>G_iewN#F!E2ZjWPv$_X<SA-vi^oCo&5Z=ZF&|l
+4sS?xhA`T=iYRY=&?Jd{0bdN+f(YXHM`qtGGYjQ#*beAyA+K^G}2G#<Z2!LxG3t8hgF7NZV;7~vYg&W
+QW(V_1L2BQG!tVA5kqoC_qvy)gpgf>(Tjiob?pDW3t|2M}|Ld<=Z8WB~v@W;vMdS}x^kVCjNIt`T^pQ
+~yOT9?D6*Hiv{Q7~k~c;R8Q?)rp52!GZcU^i32T^msk+!wfVn7u+dWU|R63N{EaRI3Z?QqiVd<UcW%A
+2kdnX*{w+_lmLTP8y`DHK3FoK7zw2FeI4uOPf{S(ywDUDZ`}^dgCwnau`d<&PzOjK5$+Tl_)`zH!JEv
+wKd(0JV&QHdsD1>CV`|r{Wtk28oMSWMoT2w3-uSn=N{9B27f8EB?_5r>kDM~ip)elXtzV2AY;X6={dh
++`L*aA1+N!B&FumAO;f7AVcoD97=fspGvTCuT;Ai}3N<nKSqNx}1$uVT?ce2L^JF44v&e)Glm|u2#*T
+e=m=20eD_lI^#AS#07wC4@%(!K8SO_|_-_DXLU?DJLlZ;%&*p)7~8EK1N6Mxh+d;T(%%IEfPkLsBS4a
+@4owf)NJg?qCZ*0+tA3bHoWnVf7uC!n(V3mg5HK3*fF4`xc$Wvs^1n&rM?DxqmY~m-}I`&<_A30)>&S
+oPZ+V)ZBSGqagVEF_D~PFpHedTCT9oGzV`3;6#A;UZh=9a6cS85x{#%{I3j$(J*$FKf)6@iev_2=-li
+O#^!K%Pk91U%hBIP$z%f(eV*^=%yM<RV}#Qc3!f$y=8rU36t|zs1&h<4%LUaFpS-L09Oh+>1Pf~q+$Z
+3#d2{#B*Mj&nVu;!sgaNCRevA)%;o{yYJ?Oxkor@2x=b^I8$=IXA`c2<;v;)M2XBGwZwUI<&!2^`4j(
+qb7em;Pe64Ha+0ACfJ@RdAgJq-r{8z*@pro^K!>^XOsH%jhgPBH~c0{_YIkiQz=KN%kKSL6F9!$Urf?
+>DOc0$rfFRlKJWg3XL}pW$1+Z&@bXUd7?A*;rzm?D23QPw9@z@|(Tx>8Mv}rT}KQtg%N3%-`)0h&yB*
+;)=r6i4-3D^vE~<)~-60Ti0rxlZNrk8~)Y@697M*ee9)8(*$_1c02&DdfaC$M0$dmNTs=huOfEqQ0M4
+zQJp(W8%s1AQ8eG`T40Y`7GKGevMBb~+U;Z_qUbZ4T{IH8%Br!`Ej!O1THB1ov3snI=@IUnY_lMm#j%
+_&374!r`gyLOmm9~kvZ!4<{+Wc~&W;d$zcx*A9J#tk<(hEHBkAJZe%Ic$(=qjEDaNkk_5$9GjIoaW<y
+szkJ1~w*L)CNBE0NvfHa_=+vrO&kz3MLn_%Ixx+LiuZdjGUU0Piz_7bISm-zx%<7i_5fWl7+BMW7Mxn
+iHx7q@`aBs-*aRhn&0-qvIepq~NV25VoWq)->NXVy5W+2S!;v+%px<NMU+Yvq!R*Hq;MaP66no;7$PO
+e!O*8cwyPe-_qid7u{mn+ggU8;5oBDPLx;+ag(-Yg>NW*Wn3^)KRYddAJB8Wrf$h{eI&!_@Kkf&Mjmm
+hkxvPiQg(@UHGZx9^M2EaI0ww<(Tlef>gR5LFc)p^9D==h>vD`%1VIjpW_<R@H6(@n#I{o*I>eID&zG
+%U`umI;A7@pUJM?zh<_YyM!iz)Qn7!IcgJ|2-LEPS)bQ2+(&6)L~PRWG5T%KaAYK7Tp?b&h^QmRKwU+
+?3Cs_gE!exVAFIBNKv7EdisM})Q8k4Q<^z#m;iO3!mqc)4H2@<cl0#BMbEVsNarjZ+FWxu!4ZKB`Ujv
+<FMAWsz3QiGgJy+XW*#JEzl$r#M_}wrCoCj4erz|0ym0rF36u{*a<H9sf{n&At8qpaq^oAn)7XM9hCM
+=3mtR{%6kkX6OHW$lv$mfMPsz`~#={6~%Lb{A}(p2A0>s@_!2cKG)<y4%N4!fDEj&hfRfPn8m_odk$<
+WlpmsDfj;c=%fOEeI`huwzH(Xiuk0tSpkyxSgAL^Hxr%>QVh8wwCZT8$fB}#dMnl<NEc@Twy~&cJXqz
+>-=PGj9ZL%&g4{6p5MlnkQ#GoBB2oOkO5W1GOu3PBZ_wN7x`)Xd?J<=o6<BQC%s@mE*Ez?th1A0Xu&N
+H0n9MJC~R*_zOmBS6=l|N5`;tRq6b--n3dUc@#rgR~A*Ixnnsu@0%zhbywerWRmd7J_=IQj#if^}3~O
+h!etiu2AQme+HHxs%Q>Nlar5h9ge;$GG}j$V>Vm0t2{eb-`FGA?*B1<MB7~b$EG3&3i}0An@W%eBJoA
+@qODAe4$40hv-i0>?Es&mRw_9%KJ*>xM7f(pH?{4fgv7Ge>9$23#D^wCspSKdp~Ly5Kw0SlfjiJAxp+
+Ht|lL=rXBm^6YoQ`-Gts$+w(Wo_9Y=kHl4o{o5I_Sz7Ll>@=hh&jWeE~uO$~PUoN)G{cB25=Uyd!o`n
+u>5Cl^(qN8rW0<&f7W9f22@Mz`JB%c@?+sX-TY*OtJvec5R&CE@sWT})Xad?swG$n<kqMMtET(Dw}e3
+Zw~o0VcRrl8J}x*kw3sQSk(s?l=WUUHrxwnPBCcslC)-Y+5XrZ{?1?(Ya~A3|2)jwR}AZoOwMsj<jQu
+;bw8X}2V@N)$X!S%lEx@s9H+&s)CPMgWE56KBcV%y2>2-1=~<No;4dXK5todC#QrgbfLWhRn4IMqQN8
+q~Zz*ntR4sOs94O1!a*9l_;N(%R%&UJTZKHOSxU8Y{X=L^LHdDr|d6zyaX{3{5=|)x(Gc?%|gV<iX*d
+_Uud!I(KhUzi?QU-A11eXa9Z+5jRgn%xIgr9K<lN|+4=el^UuC7&|mq&{Il;1^wk$;xvAK%L;!z|zk;
+gMl&&2*nHC>m!Qt~hA=wvKSaHSS0W+1&;n<!xLCT6A(GhCk8wVP$JadNjd&-cPz(qbE+fI<t^kJQlZB
+!d>F0_x=KIn&&<?oRZ_ElB)-8|QN1*^MDRH9By9U#YOU&<zrOYN4Furx@IDkp*_eK}(oACgUD2e~92>
+l;eEiZqOrGpt35vcDZJyl`lbklf53g~#TTWr~u-hI}5}jY8UmtPtcZ-0}@7yw1tM>#w_0hG@J1JG?y`
++w%Ox8_|Li@xFDRH(9`3MSn0>Xr(z@s71Ym$wyr|I(tgIbDYrKyp-=fH|SdPg_r!(Q4E3~GRR5;^E#L
+DBdTs*o%ZIm8D)PPjc59=8(;O|JuA~X`OQh=;@Z}2UZ#&f5c7}U|1A*yuRQH1Y5E_J`85<{tL7cPO2@
+~mGB3%j8uIB%pAG@y0pYy0Wq^L&=?D|RoJQi`Q5uNoE_j({0B?Z^Jq8eQ0bXEAf&pT8ih=xhLC1*rxj
+rwTybxdj9$3L*V8(38*C`Tg2}#hD2^k=<L#LqkvE<t+zWQ&i?y+GAFnLL{V|4vQ6lj6~3E>D(yd!c+;
+(rOn0;t}O>93%;{o=_|FRAc1rPCP5(1Q{3v-hkN$*Kdi7$WpJp3%CF=Wo~s^h!Ult%JTh=-qWh5tP50
+5>JqMQHUicek~A;zNN&#3+2<c%hyZ)SUL2sF8$*e=<hE5$C<J3{k6WI8N-anWkdSAhEu#=;G_zp36r9
+8VW-5~HXNyg7G@A_><hGaf~f4B>hP#KffQ+#YW>ZtR$&Lxv|yVuRIZJJaz~8u`e-HgY@`=7i1)y_aI-
+wx;^|nQhaSr|$@y8`&Z=dd$T^Z}j6Nd{H(kihWR>^rJ$-akl4iHumRrUl%J2k3KGO?Tpr8w6(~qbm@)
+3>q7yLL2<H$8`lXj2uPQbRYE7Ar}Wa9<FVoFW8<|Qad7(yC;YN}$sviC!En9z}z8-5T1hU{LB8SUvXz
+9TOcYqy?lN;vz9YC`kRS5<CP_-fr*2=Q>ow9OPF`s|QyJqC+-@<LK63&+PMxpU?PNBV9ox2o8(yZhVB
+m@FBJ%>U@Gwe_jYdi8Ktj8^1^uNZj>yl`t2;>B}S{;rs-?6d`@<@|kr=*t60>Cx@lDHQ%36sit;`Nm3
+;On5<3b-8KW$C>9&OM2Tz4fBZ51PI+f06`*{@lR}{hob?rgg=fEpuZZ={tTX>pN);Ptd|_{GJ)eNbi>
+^v%nmOvxkYRchYpf<tzW5HKVpzhDe22d(&fo56y(mQ;0ifh<K59M$`L1mf$gq`&HTb_sgddWw7KfW;q
++utWj7`SDM0CcD4*LCEaB8yJPGiYpKgxjZaY*>^wM`&_L1)jq}^C4&giJVWb0?_1bgK(6=q$nq34W{U
+3jBU__@d2w3^2}KOXy*J~&jO6Q1vP1d~-sks5s+QAm6SV##pU<gB*be;wr7YQXY`8~#)U&Fy90M2(}@
+)!gk;D`Cg=>Zz!snt>hrE;3<1ny>QUT+PuTkXRD~$U7cX<!&FYrkiSgTKD}?i?R|sa?-9T7(!^Ytw3Z
+CoToF1IQ|Vwum$vbzHR<VA^Y1ddY}B;H|xptH=)eS{<{)*`kP(m*9D0FY#Q_5^Cq96`=7nRcZKt>U>r
+qP7xZi$43B|U?UI5n0Jxw!vAo6sNKz5t0xWwXUlT{M6i_m=H}QN7q|i%(onh;IHVH=06B=mj6A4iHMO
+P*SPJ!GO{uyyJrhsJLg4IB24+UEKFtzIZlR#1jLsqP2*1Vhse1r%E<#>h$k;C$?3x)$2&p=v_AYcT3i
+2^VZRMcVS_cZ(!jLS2at%`Pk!1$Nt??26qtBxRgOGbVCfN^uhwW}pWD4aX8RV}!8H~8uU_saKD@RCJ*
+G&Lunp!XT`PWcIpbT;*Heu>??d<JOB=bumh`55Tu7{5BrV1`q{=EZo2_#d`?F*Ck?uD|oKpszmH-}zY
+3SD))^{Quf&9{R#-o-56VI_{pkOfIw99}t5O=D5+l$qAOzSm|r1F}gCeHa}NTXUuzZ%yFVTOi_bA4<<
+6V_$h7^W5H+R4$qr$JKUzDTVMOfahDx$X`=XEdYV}iyJyfI*raC}yo1j|XKGTB=t7*CNN}-9UFo)RyB
+#RUIf`-Wy<CMEE(Z@gVJZLWHJjM-kXmlZ-U{OQ;$rjVnGlbAp|+3Eyq;q=>0ycYH&o>1CA&>hqwVD>^
+$k|+<Li~Q`oMv-TS;`cL9i{XriX1pu)F<^y&VF?@Cr}sgrXu}y4!RQ^`5X<rEMhiaJPE&a29djEFt+d
+isOxQ+|qrVj#un<8?_OOnCLvduF0@Hcp)uF;q6WoO6_zyMkc|@tnehPBeF6Kh#fa)a#K7UpNllli?Og
+vUK-6A>12?MW|I3`oNmqoGbfF}bqazKJpS6IibGKN5t*P3y1(6aQ|Rhetorxm<2Sl=3n^X3eLr_iZ-?
+9RQ69vCHJ=xCyE|-Y=5RNoPHxSr*+9Fh=t^4i9ILe-P32F=+YuQ%LaEB_aP7N|EXbQ{i=k$2!q@Y`-E
+NNkW^chgHckzXLFd;!+1_q3ZQ6Q<DdlSc?3#-3JATVW?Q-5cFT4Kk#;41y_C<AzCs7lo{Yz|wIW0FFg
+p%g4or9^~dO8-=et(0R2T9+bQG&7~UnAuZ7RB)vkI}`zp9z0@I2bh;31`~&qzKRl@AtHN@8Xm2ri9~s
+a;p2@iXg<lD~>>ti0jnzRvo3Gme58vj1~^X!|@nWv$6Y753~|PdQ7^;-deugseedPUBKi%tY6mv30{r
+b-zNB9=c{=MY-H^vtdslvkZU1(ElT%C3FE(Tu%dAO@m=I+dF_ItnhU<clJQyW2Qin=%z~1f^8nSA@2%
+!P&CGvZHT3D7Km3h`cxN?+aQa>`sI323I(Yc-#4FiJ^YTyyiivMhOW~WvL=Q+QfRU<Sx$1o3Ihg&t8f
+D63V^W{N$0P0dNq(#@BA#tOJxDu6e8OiQpH|SZZd?sNyT}9dW}r}B7f{gDkF~eRbMx7gJ_s71d-GPZ<
+U8Z+CCiC9BlHkJomscXbGs5app_&my<HHnl3#_kio}i4uD$?zHz037AMA~9<c&8?s^uidA%6QPpndcv
+On`kHOibt98~Ggil6@}ZKd_yS_vLQ^_?*p&2rfFeiyIfL+<_eaSEbsYC`~R=y41{Ph}i{(dGL>gOECQ
+K3PllL5*I4#%9CUh;yk4}MLSqxv3113ji$wY!`{<pGsbnpRy1@e#sYIfS%U{EG!5c>L38zy&HJ;MoWg
+-mG0J___X^KVKi)Myf2dPW3p5O$c1?BiAnB58ex0GgBO%$`Z$^$p=#GMKb|Y+b<%FYnTfmM`arbnuRr
+Fmn&9mt5F}v*igZhHf$-$*fd4FWa;P0#<eu<=bFX_iquCidW<U1{O39hhnHk<S{2~@HV<@5b@b_Jw0I
+Rv^c&_(ZW{E@ie;dxfm*b*+)dB)KrD&Dr3YT<Z!=CJHx-oyJ<m|;u5%jL1$WU|f?IwYPP#gvrm>)0h%
++~>;WlcxvG6(wWF0$w~{{d4c-?FJQxv9;U1!Jox8tmP)D?#Ez&bh4g`1@{DOR?nHYKO)EdjX~>NXimX
+`=kxizJc$gOu^1(#;5cgr?0Obp<uVML!*qk@@y4HS!gz;!x7v(cpRr|0POwu3C8zCVU3Rs8<1!+X;=*
+pZDSBPi@xZ?DhB{dT5A88iZPCsz2&YD_7pXZdIW3Ik$&3}}b3KR4)6tr)jW9``dU#~xL=ZPOABOm-vO
+eiTLj$wig`CN%J?@jsQQLZ3O7^z(q)WW@P%!C);jK)WxFrOopb^MfKd1LiWQbKY+XZCqAJUd+%X`-|h
+pkkfj<?3x-Cm56$2p79aeHrb1pmtw$6Y(7_iK}0Kg_oNN<HzfAN0-DD=eq*`rFR>zDS6pFp9wxOvBXj
+gF<nHU>F8LaF`%S0;OP*q^Ykfi4+iXz;RHp164(0WgEkwTDK$-3;{UKK&$}=fjs`T(^rOr6lIC^qipp
+CTkDVv4ti+InW$C%5?`B#<oYXmog%~G-=m+&R#B_iH_EKc=?vr<#M;_h)|P;R<|^GuuD!^0KrKvH*UQ
+xyd3o1lZ7AYz?wqS&)$$YAfUJ{w(J!?&r5J!ca{I>0k(c|&>*I0C&tey_N|>4{^QFlpyZteKKe;b)GW
+a)zDL$XB7RsQ&A_<TRKiO&h^FG9T<8B5sYNjK;4B+~;3M&{tZkwRPkBvJiei{Nhyw_+96;8&a0W&H{9
+bNx0YWjG#LqF}qmC8^0aLSu`at_RsfoFl**2Df~v{$c9x7pr98f{4>c4A(fDD^cHaD<izlO{tybHo_!
+xCbZ8bn4<abE16<Qqn`H3XGy{hfKfiPQ*OL6|>v!rk#8uuKR9#(iH!UZOI0EUiT*ljxbTIu+6PF+b(j
+0@?+xB?VaFy0gHKcc?2cg;Ar?G<DU~TZQ}#tV8QN&?o6EN3n#km9$|Z|;@aLldu@j1V%n#H?DfkNmS@
+Gmspvgo?Z#WUt(*Hcekn<>4vy-QI#rKX19uqbvU?g?*E>AhKOkw_UuQ$>A2G`IY8EF;ge0Z)4I!N(6|
+;TO`;?I#NF&1GTX~^kaZ2ns9d#Ay&$keTE}pgNZSHn;2Ci=zmfa<K8HD(2=WitU&~b+B`A<Ydrgl_`Z
+kL6knS{uxDE!!V#wS!i@Iylij~45au|M6ClU}C`S@$57(B?;_O?c0^t!bZ0>=0A*7LgieM;)<Zh_(_B
+xw~6HvuIy$YTF;KZg|zxaEzmXbKowu(b!9G^5$MTvBVz_#B~Wma2<}f@p&EyTh^c@#Oyh;6f+(hPPYF
+H2tM7Wl3(t|u`JF0k%r8H#H_<P(2D>w!#Sn;$%`m`ux`gzhQtM~osvJ&2ilYemD@8WyB|{1Pr8O+wTV
+Ntz-P(5LA>~`^C{P=2S*_Xd)hFQQ<#w7ZbaFO%%{hDy`+X*(Clmf7EEW6uI>FSj26)wH9ei$;)6F7f=
+c_l4i@xQNp+0ZersqRU+t-#-Ctg$(P)P|e{%OP)_Auf`NM`psCG--bmJyggbarTJfI~BX2EkoS}wZ47
+A<&K$XxDTwcKD?kTw(Jq!fp3ZRaS=%;_ZPl;PYo=;WP#B7?a+okX6s$F`YcA~j6pP%MuZ@8As&+<2v$
+MJ8N)hMb|c`{<hUSF5}=7cJE|qiOMnGE;6?DAmYVEsWofs5)moM;q9s*to9?eM!}WpU)X|dyV!M^Zc1
+~F6Zg8C1Td=PdS&RSArkui14~@#PZ^1Xjg@7b~o(okt}X7#Uas}P=n6m^5rc`xnk|?SZkO=j~fe?P&3
+_bSvf#0M~j|SDEX;UJvi!Ot6|{6>B;rm!!eRXNIe|5ZPDFF{K8CO6jmsg=BL|PQJ>1WXb@JePXd)cba
+<d|E)oUu#wbXCd9)d~vCk=}II-@wAknk4m29Tl^nQzIp)sUgkt??YoY~1<Pj7G{ygGqAzJw=hjhw+D!
+)Xsw8w3(>dSTfI_Qe)M5T)u3KU`FfYW!n%m1lPILNkRRrzx@%J=_txR!=-HIMGIqs`GNUKv!W?8V^-_
+24Y}XoOf%TQ9s_d1IJ+SeN%fHR}yBTSH5SK0Uu-yrk`LUy>G+0#yuYzlFsGsRYmpLZqE_R!$zU%R0{3
+~Ju6H%Yi!zd-Lcu5kKusnovrx#FjvmyiQ{f`eL;`MacXRH?`NvgT8e>j+L1qVr|M|X*2Z^d&(C|fXDB
+gSvb)FTex{De62OxbV%=jQg>*v@!jT#=cDQb;C=1LX?&|uO*k%_K`+1X1JPc<|9OFlJz^@m@+1|yIDI
+&9A3~UW`LGZ{9NBb{VikBd0iGh+Z`7KHRh>v0rA4x#3v%9xh`dI!5s<lA-lSD}jhe`T}V(@?FJRiZ=p
+LYDpK>Vv93)rKrrXows1LQanV0r=uzE&a?up<pY$CFtH){^Y&b}a*}SLszFfT2No_RY=&BLJN$S!u~Q
+`A$EM*0Yi;hl!4VR_8@l%5nsV(l7(|Ue1+bV1O>7K<_wQrOcO*kO+TAu~q$(2Fu3R;kD%>EQ<g|UJSI
+GnFIuqR00A&4Er4>e%Y>dLA&;~`9+<V=dM2p1X!-|gp>=Le+;JWPR6K+2of#$6D?tlA}FxQb9?~wxV`
+5XHRAJl;n<Ja_WemhmUMgxtURkY12b>?yS(=D7W%wNh{v)zp23jHvTZG-!u3uOMHDhF!OIj_bT5dF!5
+=>tK~>fY{N+UK63)$$Crxp~G+;0%LWv@Ja}$}Mx1zAPL}m(XT$E{v(55$c6yy7Cm;e7>gTKy~7OqQ>J
+4W*Y-q*{%wqJeC5d*|`IQQJN6Ku~3=45R{6olI!=$P3a%bsvAh)j6HJ?(u#q(Z0Pl7<=OOmz}=BWouC
+bRUUF2e+d%V@}MR99dz`opXH1?oqU2q#ZPYWl8@~oK(5Ci|Do4u+g4B)_BVuIP3)#Pj*tPf<d;g?s}G
+<TX+}VPCQ}~Q>Tb@b{h71W|_UK#~L=&Hz!XYcdpJbnJAM(I78f#CyP*EWTzNr<VF?u`E*_a`Fx%-g~s
+q%m7Xs()hQA`V0<O)XqF%`VF{D)J-Ul^s0<t0Or5d6*l-s`q8@r1*w~*`@e*M~<gmKi?ApHJj@#t$+U
+f_gOPlJB_u_r5oj8Ji<V$OBjbZCeY%|EdRh!<%q~4TjeQu7>GnoA!p{S7_Z=`$H{%p&}1D3%O53xXGl
+Yg^t6WL>B@EW@syT0H4(6@WN6%Rll!vHzc<p{(wmhD_<u=u<G+e*q_J>a;@AM)xD&{sF6AwT!>4Si5)
+&>5m8X^14#c&Nuel@Vvf<h5<#E94#o1d-EN#cJQBV%HsBGAflAd(>fLgE}XrOfxZjjt+f_w!4=Fm-<D
+GcgUvOw8(UW1{fZ#jgIe+k$MG_uKAK!=o9`dHQjj0)3QP_%W9MmM}OLf0wrCI14C^cbimFS*+4}r&dR
+h;=0lQAr!Mc#TK5R;JTOeWpp259?{|;9_FVYD1WGPp!bJn*3}2*T-o3IKI%~U)bDW)qoqeWF`p0W~op
+HXQf{W}UT*(gQIpLxsaX%8Yo$0eEcsL#$Z?<7q1J^-EZ+~5GK7BGgdw!BZi`>YkV;ovF<}iPor%bSOj
+ZJ}9Fiyh#v5y}OW8A}7>~3wU63}hfU5#zT*u@q_J@#)jgs1ZHh}-4^Qh!50clKlB{`9|p*^zx8Zu+tB
+`=315xAyLz&hs76qbP>c6hlxLOv5zB&@_dSB#y!`ic=`XFf>Ww=vU!DMgvJrDh5Rgng)$gFiAwNLOm(
+y|1O|PtZe3p{yqCzDxAnbGd7FCuoN&p#ld(w4YVsk-<ZNz?Vl7Nbcg~zbH)G~78U+Z{LJ43ML<;rkiJ
+mRt|j4>(w%}D3XuJQK?^xV0E;@M){gQjX}&Bqiop!m5)niM@M>A!m7>8Q)e<Z$@4dt!3A3E%mo*m@)E
+DS)ZQrBM4wBD~SMMZeE<J5C0&UGNYc5>%W6k9f3U4Ah?>$rHge5@kVzu%5gRSy#<o6aS^mUK)+w{{9o
+~b4V<~2cA<6S;GhmNqA8}HWarnBTe!0+q=d{Vj9n*Dv-{ISj09lsklhuFq!KdD@5;#4>ZTEAj+9cKDQ
+^WvXIY<<Y+{aCbFP~2HIpvy<7<Jb3=r=PCDAJ*LV@*73re{Ol2uey}{s!N%REUY)vjeVVm7hP<)(KU!
+YD>bJ)i4+EEP_DNgrD1rqKW%SeBt`wf@GO@;m^b3AR(yN5pX4QSvxK($Qj=ZDzui4=+v;jk7LYVn@sp
+>T+U~9|lkK9*ESW-4*pbLynShL4#*AVj8^UW<`poS#-nC|{$G5CWnp3=moU+b(Ei`?9!6Qo)*e!KMnc
+=d1E?4r*rmRIH51C%>er|kT2?|aLGQFiYK}yDMhe?q58m2C}_C_(oev|61`Fs@{-;uCqbCT5r;ksZ-t
+_M`9_=rr@UN|SP=A_&_qe&FEP?d!KSYD8Zqi)_DEq4d+$INZ0?C2gEGO@RO?+ib*=(N>elu#Y7JL801
+uhbw%a>xyk986*Ia$~8$@24jXmyGn-9@Hy`-I05WdChGo9;dN*2}!o~x26-4xJ*2Sjok(A$&{c`jm(&
+(=s7i;p(4#aF6)wKQN!73eaEy5UmP5qEV)9m=JcSq9{%KaM`P+0nsK5QLKt?R@<v|8Ri!nj&YN{iiAl
+k~IWMdlols$Ri8~9thj$@uO_zh0ATv8|;mFor7ar2FQ+)8LuuJJQP~7gg+y$H6VIx-tQG_8Hz~b?N8R
+=*q$oUF;)<(M`34bZ`R;e4+gQANQp+}7{WdFjM`t?F<lXzmayXijJX2H{lN}i<|7$;TGeTR5<WK-tbZ
+hPMdL}mJrs^3TJV&9Ob!tM9p6a%;dj1|7V5H1gbkFIUOalr&h0*GXiyzr0QX=1t0uYWE#5Px%_hdylR
++4(eL&1QMlb+K+v_ttBWGh-X@_6HI@Yt%YJj7v~L`ur<~G*aq%FFRe#1M?GaSWr?zL}Pq(`Oy@PTXQm
+p`|Y49_4LZ1YG%w#<UGT_-#eDcU;h0yK8))nH#VIK+Z2YU2^-LdmpsezU>WufrcLfqGfr1iN(H$E;Y}
+}(DcRG(d$QqqFU$EwAdDNDp7_@_W&NWONnxXOh|_U#SR8M-q{?|CG;)9Aphxyt0^$4f%4qIzp^iHyaO
+H5nDSAH=Xih!rkKx4CI$Sz9X5q5ivv{kzlOl%(!oVqnb<Fe0AIAJ?#_fD`9lK@^ySsQxO028wTF9LI>
+c{C(W}B^fKbwO^5c+95s(n4)9~M+Uw$Zc9iG6<ATS)JC5AVFK(GE))12Mw-e$<-_8{2Y23$VAz+pW=!
+mM)((ZOZVngBm{#A2DC=Z6eNg*HYJon~B~k+`Si!E7bXBQxEe^A@EnU^^mlFHf`3Nkr0z_BPGjFL<;c
+b<~&SEiY|O0#F~U5N6~gC7D4AjDzt6eWav}eG5YqE_koB%uKWJlhlom5j|{qNTS_={;kn@^KfIf%d90
+{M!L{o0aPOYC>o^mt;k~3l-HbQV)i%R(GVd4GV|Bl29#A2ji}5P@ThQ0sQo927yX{5GcRB2Is^!Mv$t
+$V5R!02%+|CD7vXKhalGlSdZ(eW!y~?|8S|0Cb%t@*H=V=_UIV19B;<)pHziGpa9;?H?^g?b{hHUR11
+vN1|GayA++LDmL-I1nw!9sl?_VsAVe`mxVL~9^Y`;A?;-P7bZMfetx?f>}grvdk5*%mL`zL=u_PoL}~
+IQ%Ck`YuYO5CW$ViotLMg$bIXNfaR|ioqEKhbaU{37Gz}W<*n~Li#G)h=zcAk29+SE&?cg6awh}*cym
+2*zXkjwYx3>MgyXCKmbla{V1V8-1as#5Jn)FTcXS*R0N^h8ni_#B@xGdCMuZ%P9oTkSk>FpwIW3Wh0A
+4aOC*?vpb~_9#EF2xAA$lEwpNj72JDj|;4Mi8*at7WVF{=oVN1*#|0+ln`c-}JTg~Vw?6aJ(vcMY21K
+|nuZp%*lula)Yk3k|J&$|ZqjrMP>QPpWBRJ=`tTh%0eGl=Cd2*_lt3dY_W>jDIThJBor{KFWo7eg=U)
+<BZyI3TbVH*TefwFm-52t<lYp!mTDe1Z`rkoJBn{k+8Qw_UjU^QxhLW!axs4gD+2{#Z3A)<A!8y1f?)
+e@}>e*4KA3O(prp7ws;jVo2?5<u*Ed#%2jkDstgavdaV2tsS*E=-yLPY_06)jL&C5!fuC3n>7Nf9bed
+q+87+5Mo`e?@k%jgZrg#mZDx5sHF9>H1z+B1Pc*}Wv#Hn~vL!q&M@BI-TPGqyY<qeL`39P-QMd?)Gs@
+G!w#*L?G8K@=f*l4|nCHOT)=gYu=N{SaN_&Fu+o_+?j>ut!Jeu(thuk2}+jv_?$Gc+t)3XuGhcYxWEU
+007Z=Jl5`@NMWjnmC-8j+oOb)}bt+*_AXZuk~rPvMR!3u4ygkdR{kDdVwl=i%yh_8t^E*BkWKZF%e&=
+G)qej$3l)kyDZF<m3Q~H4v!Q1TU`msxDGo-ZZ;DiZI`rQlFe%mhi-qNKzXaRoC;2*q?&o+ReL!MlnTr
+z0G)nk-eS3difXoLZ5d2sk>~OlQg!`dwOrq@dDVjzQy7u3XH)50{+@iY{nT$uBm(9XD<UkX?kVWC>M*
+>GayWZ$>Cb^)EEh^TWf7P?|E(RVqaYIz{#L&s_1$j;%YhNql3OQ<%;=Ry2QIwls9!v;aQH+($$X4grp
+Qz8y*eimz&I??=%%Rkm(^=dEBA4J7nz9i*7Htp5htJX#E-C(QeaNEs2YLz3=&cdo$!7Dd~tfo~h(9W`
+`#vqYuN(V=sFp^v&)2bQEo?;RR%l#jzUqHMJo3+_hq|VToyao$s_GSk^#ebQV0>6xy_Kq<YBXLV8$m+
+&$r&(X<x#yoOjgZLdP(?l5hNl~+kMCtbU4@=nr8eWLS!1wh|rUH<dn`LCSlPr&*8Nxm1EVekbs8G@l1
+0)=rHqi}{mXbfdYY=KUkz%iWs3N$GO43&ip7ztkhk4iw1gdqX!#cSUpMOPk643G%@n#}@m@QpA3R^V9
+wjq#P{P67G}nSdt9+k_pOfo={H1J()lGmRO1b%G5S>}CMdW_fpDZ4!ZrcVbn0rdM|rdi`y*GWM6HF3V
+m3J4(QA$vRw5Wndo+2cEXeIb#YyFp2yUH18{Dew&Ls^1_(A9miYuU6FWq)jP|w{dHxf`0oNuc9OrLPx
+2XkzG<y|lqjI%M^yz(lRNA?IIdNd*8-N`Z8LV__ac6^>p+He0umtaRh8%GcLRgM&z-~i-!uv3wZvV4e
+cl<U7YJ01fX(JN^LKcy8m*-lMNA$Ms)AC?2a|{UTTUPNWh_KrPj8yzv`BheQjY_VrN=qcYO2upb^YKV
+-WCk@yuUgZ9y@Il;km0=m%z%4ttZ8FW?4m4Uk^OWscrUh`lmJM-uV$r4=lJ!Eki-qf^@T##B9w_&^{c
+~d?L8U-P$#rja-J`C~DBm1kSJyZ`?)-cf|78QS}j~SdfT9IL2?2ClA{7Xpw-Y6c-q`8rhk?)!qpiFOw
+Uiwy%v?DS<MceCkLR`lh&5osl_UG#OzYNloVVE48luR1yugm$=(Le2kk{WSq`iCVO*}8u#r?jdzEK_0
+x??IHPY3WWU9sJn^WDn{Rt-Y;C(NW3zp7CWbwVXm;3c&yknjz94DuZ!VRwAJq@157d~JJ7EufmGleKX
+Bp>jl<x0*TUmxUnr>u4j@~!4b|`nxQCKo$26j*168>-b{`<~f?fdhoG-HTO5|EG;ZPO#-Rb=eP!O8Bq
+gQR|?EV*y66(4tK0sMYQe0e)MoQz!bVTZ=))wwqO0*sM=i8a1>oo1yu?~TeA%G*T8W4G_N=)on}B!^&
+fzrDH-l}@}pjvU}ib08077WKO*3!2D-J%YPr%7SGbW_O!qFXisV9II^LUQ<*FI$Y~hRffS5@+mhV8)@
+@Q1YDHf_xb+ikq?Ul6rgK}pH3U?UZE$NCQodH>BB{I;ramuwiqBsyHADbv2VG1w;4vsaynGGa)VPm@J
+@P^BU252T)QuU-Ocs|PhK4cCv+bjD+n0lQ9R1<6eEoYPu#M@y<tWB9+%3q%V8gfwSy6L$k}tF<gZdVK
+jWKR=EYlL7hdD^H_`nsz|8;TIsXKoKRw@PB*h2<!(kYvNF0HeM3g}o3dJc5rZ60%Xk_^n{iRr2^43F0
+!5kV919MOSf7q(HwzmFb0wC&wk4skiF%bUe76s5>TP0(FbpZ-EnhTPJ3DApKP>@+=V3O69011KRSG-!
+Nqr}f>q4-MSB!17<xpY8aNCD{qjLA^|GnaIEIZL{Vz%3X}ul@<32LgjcGl9VlA-Ss8P^&m_3f_J}aAX
+1GUrOl;tDEWVo15v8y**>KY4xKzERXE#e6NB!`6?b3xW|7NkUl2u_#s%Peug$>dEF|a^olWe1~T73sz
+q|kPcZeyDMeQhea|@GwP2n<Y?~rM)S(~eekF%>KIVP}@iQjYL+}#M1{Z&{EROFMIqObu!y#`XZQn-M*
+r!o`cMZVnZ`tUZGA3lX!5_VgRT$=HgjDg3ka}$2bYYChApAoEZ5W#(Y`8aWQAPyfc8=lNj?Pc*ad2zF
+y0%MV_mG&btN7<rPv;B62c1@Po^y9F)$yQ%*~5Fy(q1B5=VPLEu3_)@)N4sMpOsHs`rFX>^u8(%#UZG
+7n~mW=&{t{WQ&jV+Xh2WC;M{D~y|!|*tK6CqW5#*KjF%QaNj+v*gZZH2$~xb!_Z`eiQY-dU>yU`3H49
+`wN=4K`ka9<tZ&G(wv7**o<}?sB9G~XrrZM1<E5a(2569(#IKC!5VWgwoq)*R#5rc{s=W+X!)$_|^bC
+igEP?l^V(JH#69eRS(k5;=0<(+RFenihn9XMQTpV9(>!xQ_+A@T9#1{Bv%PKn#}<Gs3WUY6+V3oETZO
+R7O%YO3ktW<o1rqcbv_*nwKK3Og{DGd~LNx-W?`9Rq_4S?Hhc{B^N>SqvNR^3cc3G={FPSvlu{;?8oU
+oHAT%(%bmV=cHnfF1^!e?0yzKZ*OL|bSrwuwXY51G~iWQ2)aVPpwyru95IPFiE8D1<j~rao6@S7wrgG
+{8JjfPn!40A^Fg^@^HRe&0Y_Zf@WeBPTqLNlnZ0ocbHfTQ^FAxjR`8lBI^2;gvoX4~eC_mrEmi$E?R-
+hYo`*_nOwsiT+8(bH3I(r{u{3K-@WGvb*nW7#y31Z?MJdH+Ij}3&pY7c>82Og>CPqYf_K9w7GUm@owc
+}H06dyisZ{Yb!2%%BQDlOaoA-gG2-HP2rU-eD8Rk*l84<;q1H`PwAt&qJ~MP*zw?V!!R8bs2>NRH9|G
+Rl6)oE+|D0~{$=1O$-Q)x(di{9o<+&A<D7({F!1;QMOq@(+x{G>j9>0*p9};s{RC6ba+>l3r2-L%?h8
+^Uvj28Z_zYH&xNKv5m!GLL#F;H5JA{;WJEuq8N@YDDx#CQg8DRI2eYYP=KXN2Faj+WVa-)3$iVVAK=-
++ARz?m^($=<bl88YEuVo}?6ML%1alHVaU}#jc!C7}JqQlU#t8v{HbH=}JQ9KYa@k>7C(vUF!LJA!RCA
+NnW_-cv<y6?}QyxdZ1jGu=NnF3pNm#7nG4}Qlpo7^lo#QVL!rJ2hWQ_^p-U&MV$Z4yU=O23Wt;4^~L`
+;$Fz6|Rz?LKhssjm7>&?*oiadYS)MSQKkRaW;-HJL9KGn^Q-;@?Si>o(N5EXjm(BD@Rxr%CJG>GC~mU
+`0}ydn7*+0ID~Q6bOi<oMg3&JZT29Qhy7Q%;yoRd)D3vzRk-8+1Ki{^|r%{s;n77Fg$&AS#F!i<2lB#
+=?B{M;w$IDB6OVdwnc=c1-XUBGaL5Kn8Og*q$(=NJ<@M4_Kvyc!&S`rP|{M1oi2ti4!RYH=thS{3*QF
+5dYpBrirJ{~hqIfbBh_oPg=ClXR8yu>T~)<NAImB{k=i&t6OPrczq1!<$5JC)ABM=}6G+TO=aS59FsA
+P9WGnVn+6R)CqO);M-9Q!u=E5WMgx`scl+7mtDY(7th<LEku{nG|GUe%eDiJ(ym{jfDSIQ{HLzfstiO
+md=rkl-)wjP`LQ593{1?=rsGIftNTX$2}Cm51<NjDuN9*5J%me1pJefrk$y#U7{=UYMaGb2qi*vC7?{
+|iPLNc?cj=rQ`S*VtEzMf+;LHu&W}wuVyu%mPx}8AALa{|Ux^zMAP>OY*@VH`nksAPbwe!!wsv`{bp(
+jphCn%YYNoMzOf7jxp92Vok;~Pz_-ak*oHjaA0<dwXZ5a-CiXf&U>Pz*#psEnc;|;865PX9!uDjI9kf
+hD`;rWB%Vf}+o#{rvb$4;URko&B*99=a(cr#@2-_T92|mps7)B<^?BUy`eZv^Ui;JCuplWG4-yRv12N
+T7bSsngN^_$az^bVW*O~80A7<+9hC1=hHCRADy2f&Q^3bx1iqc@AIU3ZgA=#3{M@c^ls+P~uv=6O$nn
+oZH4=a4kjklv&lHAdpIMVjETSPIYdx2>l2UTisV*c`(DBbjp;#Oy6&9s>4`?)e+A3LPF9(K<8sLZ%Rb
+hzZ4;sCLt?vaG3XnGV>Q=9T~w@!aOW3-)bX`GL}XL5jA2E;<b9!QBe_u;YpzWui!6T`oE=(qguyTd+%
+8-t(>LoFE}MPL}iEI%+}$@fr_WJrRBF>1;8h%f!?fO<9tfNjaW7DQPfjer4EAggE>P~l7gu8<kvg2%*
+{*p`IAJo!yolUPfb*(&+L;GjQAtYuOP1GP+yUgbH%Rp&GQ9r;<S5neHDIbc~E5FMew+~4v#VE|h_LqO
+qlNgtU{!rNfDF9EwPZ<%FliFVZ~A=i#%6ocyRk|@H|lF<GV+iZYs7wucJXK9-$H^Ylc-Bd$pcKTSb;g
+`uCm;wBwy{3J%*MM`BBCU4yMIyWNl8&9?tNQ}EOf=(9>BYJq2m=X~<+z_pii(uK-t)C3C#zn-OxbXh=
+YDlFSzfQ(fNzNSG~Kru!0+_0KT*b<Wi><KTT5DX%3WDSV$k42>A|q#NjiQQuS|q^LXMxPVq9Rn{yM%b
+kEG2=o|7Z5n0{|O%X1bU^Wh~g@#ftyx`;BbLKY|NWaX!FBeVu~R<*&{DyPP|i})V8zX)YU{b6ewg@F3
+D)>rcdf7J|@PU3Yy>X=tsvLUJE37Y29&S>{#*0v|ZfUR~f-k`l+6Z}PHPCCk-lMQzA_QJf^lpyW&Adn
+xYW+2)@)puo?+1fr8%vQZza9=N|L@}8NdX_isHj_-OVG{pr>q@rfr{CXhFUK>K>z43xCq<6>Y=#3R-Q
+udinX|q(p8aM!pQH(*xbuwI(PJ;2h$AD=TdRnVhn-I{UfAGpGW2ad#8H6JqRU=xceJ6Nhmt~kwu}#EC
+_rI)JT}kj)|a)9Z%#}@Y$vw<AT*i0u>jXkYM3j@3Hq+!c?^D#v9_;|(4ur%Cee+gSF*o-a&BG()$var
+exIP{vMc9Enz;C8QG>P%u>E}_JYS4HO-)|gT!J(8*)o-l;E)VdRzsd}+r6l0al*Ap*5x4<KOCDfa%*o
+}Wc;GD(2v5w-w-2~J3WfA!VQVVW~2xI4zZ(bvF+nL6GzgoF5-?Of}KmJY<-`E!|n+xU|taF(a=~-5NV
+rx-56wd_Gqf?p63^2(U1E}Z6*`G9B|~UGckP-WMwYsXtMO)i5o;<?6cC;>a|D8`?g8Y*uO+VsCp7-5R
+fYDr1qBc-i4F#1UYil#PesCB~j}Vs1I*cE~+?0c6Q$B8wZ{*o(Gfu{B*JC_KboS+DEi|wq6QzO}#L`n
+aMOR*Z3h`@+{J#K%+X8H=Wb*Zy^uMi}s2G<UfHAz;}M%_X`C27f$gRiT*hJ$21E;aEw7Hl%hx!qbYRx
+!7NyWkuXEyBt?^7A`!agK%hN`f)R0Mebg^G$&zlR5y)qjXZw;H0R>4g75*g>;cN9TS@8;qfyy+#CS6O
+)lde7!OS%OtA0m)+Eol*ffn<kS5HI=pH2MNm%*tS))=_qJZ6Gdr6iI`{EC+{c@e*DqqsSO2W0Mi!P@@
+ERr*!qZSPmpt(aneiB~&B>`6Leia!`r~S;hDVGg@%TGv9|vk6j)p3kvyxi+)XdiM}d3<_nN~;DY}PT=
+;AO_He-R7F6Pnu<LvViw~?go=oX9zhQ-Wd28E2E2-rIM%P8cbc)po)%;<bAwB-oxBF-kLT@0N{BscfO
+!$5piY1vRMU;efQ0g<Xz60y}5#E6n{jp#7W4jLe+^-uP4AG6|gvp*(yU2x9EG%h4!aGZ`)uZ;I+Y&;)
+hY#M)9&MxPy~6-iy_O=Emm-|G?5ymbH#RpN^FqMWsXg6~oEm$s%sw}Vf)|r`%k9q6<+Ul>DkzB~qES$
+;OKurFo|~S>k~_g296s!LrIxTmD-XJJ=WmSn$_vEqbOdo3{MET*lJw!E`r+m*Lm7Kf^7}pQ@R(^m6gQ
+-Ze@q(Z&lrMt?X=@d{M;XO_Iw`6wv*f>%C8x7)t)imRd@)6g{Wuugd&KGcahKI<c!ftH8(`U@8wsDkI
+{X1MVFLSILyXnYOHO0Rasrl3M48;#eu?$OJ|PX=}l<G-N7}=NK6i{(^6*AjDb3T^y_e0F%(()Y1Q!0B
+}RGoQ6UtMYlV=LWbPi?$Ov2`ll4}%DrmW%->T-jHv+H1T9F}DlRE|4n#*}7kiv1S!>NWCe=kjR>EHY-
+zj=3FtvT^c_$-@mwQT!M^yr&8lW&aij(LQhN$Bu;C-zY25!KnxhfucqNccY6<b7r56onvceD7tLxAG-
+Xsj_KvD*qO6L4WETYKP#`hcim!67kZP54%JJ8y6*{;C@w`Bgy7dWHQVlrRtG7RnSqfR6G&e`|@qx*Tz
+&HWF$ceYaSQa3+c*#4*53I9))qwZW)(xJlRWkx~QK#cSsLgsKDVFQ=QtvyEH5gYOS6ac%T$h^^dt}_e
+Nf_uJFo7S9K6Xr5{bT&9M6+ygJ$@Ki;6L_zbz5-zgd*opYw66Ld?a#$0ai`4pVZ_%aQsYT|r($5B(c9
+;`<7k&Y@aqam1`1>GO9&dkGPPX1$*;tPJc*bRDlxrL@Uuu2btEcVG*&AbScr`$4AihL5P|2)$$VPq)8
+Ot|i-&o-6&rx9m&dEl<cY>%~CW)gn;GMhWul#jpOIduNV|6ys4by^@%tkbH$x7V@)cnIc;s_-|td^Kt
+Rn;rh+-|KMx$A85C_>adw{$qjM|0Vuke?wuI`0djfxU$a6-~RD`0ps)k`R`@(+n9oFZ?AX%<NqGhYhC
+{1-^=&2)(h(X#{S#?{6BxpF#kWZsBhvmr;oFJHF@>h;ogpAlHT9)TjZBh)k_MvWRXA_lLomb!1N3>1V
+Np1)xM4>K(rxNyU+0Jv~>vqz%VuTJ4}P@5l>b_&NZ=&QP49)2v7|LblVK%z)J*>U|?K3`I&SLjsq(yp
+qIV+ej+OoDPHTeGz^SIm(U=K!E2NTAwx`qJR1RtHRzQtIWo0s=`8s-L4(hNt#j590VuTLFM|bP15&hM
+|I4ZB{v~b=0e)+}O%v)G`xrV^{pqwo?*fogv~%yvlXHxk`o19Uhdj+ausJ34yB=5#pHNx6qR%3Y?Poy
+SPbgk~e2zE-Rz00A;sIn6Yn^wRcE=N;A6=C*++cFwtT44Db8UmU${{fDX)c&P5l`DjEZcPfj;md?OFG
+4{r`3i%_Oj4}^JEEAjzE^}{m&M?r=g^AhiG?QmQ{fkA{lwW*3O+no8$C{m+F^qGg`>|q%~$Z$y-$7ur
+Dx*`fa13?3Vx2)8ReN5rp#(D+9iXJsda85PFYyL#cIytS=O?=+XuO&Vx=6+gY@eh^H=Hmj7N){X3qL!
+Pb^-99LU`HQtd0_RKj|&z&mq>0Y?ghjfo)7Z=TDfqpllNRDuoRNa-9unt6D%e4TW@6c1{R=@V=ndu^6
+-)M7u=siFFEKk%EJ$uA*;p{B@_Sb1G$EckmiUagK<HN6lqR%nWTq_dN7y`;YGb~Xl$j=lb*hx|N>R}D
+b&Fthc%GGHBHT7ADx5JX0-Olzj5IcX~)pTKaaOFK8B47fCXayB%>1KC>T{1kLvo1)i!e~iSI>8NGM(m
+Xh+PMRUO4WlTJ#`xy50CLvy0lA%<1j*nf@r^^M6x-0q&TvN2je{AncMQz?J|&ss$DGSKu`DlBwPtz@N
+v;?dScdZHr<}Mp5US3uAS)_Ih7o{BL^`&T#&209rqh-=1ERU?8lCf_T35f!YET}hEuz8PHX$1k}<{T2
+&4{9Ls$h}qEr?cPuzo<`SIxE13pQYL^3WKYCks;o}uo;KAw-diqBJ@XwR#sc_#vaA~;E>59|5olILH(
+P@4I9v(tqb*Wa2%$a?m!+G?Wf@B)fR+mM|d`bDrI!;4xo;N-)*kJPrv;nRekt5ZUw0iUoI1yvWm#qtg
+nHXAp(V@tFbghDv0wlo^~E8>~cdC9z0uQ&aTLOFrUJ;fVsUkS_EA4I8uGV##7;^p=W9@Pdp)<_Yyb^g
+3^=;n12vy03hTg<)%-A>-1o7Og|iQ8S4(|g2$Afy@MU|W*wi{^?>!jLmD<5dy&!(rN0T|M6}_aeX4&n
+Ks~J*_<?y4shWW(ZETV2h?CEEMYgmeMjCe&xZ{G?~!FrswUHs@!m~w$F}3FIPgn!PIe66nM<P+RU~^s
+u&Mjo%S^wvUW#7P4B6PlG&W8`@4zB|NVi%e+BgZ27p`(mlHNCp8r?H2meiL`<KE5^p{}*7?L4YrVkE^
+(CBKo&8#BT3ltM8_EI4jkPgw`Vfbs`$yFO=Il&Sm05O*YR2ImUcob5A0+mL<){?<N{h3@LeA!L|1InL
+?ui&di`C2TeQqcB=QDFB?QLD2g16spN)B(uIAvhfwgL(nE5}l~E;k@jV5nu~KfkrfN@yI~+os5>~<(D
+;u1E|!__AkSP@$&yhm;hw*{uCy7#2vW)8ZJC}@9bScvR3?s{*}<+b7TO085)%A8id#=0BEUK1RzRrdY
+{%g0irPm$j0XDgh6jj<r9$aMn5(JrIxQQ*jLvJ`a2iwtLp{*oeTEW^@9G+1^eoHK|i}-e^u25Vvl@hU
+MZnEna1>N0^YdDp4=a#cCKGl#mShlzso158*i!33n{8D!ACE3aG6oJYnnkSqSW}KPf)WIPF%v2caJ|z
+_I?4R$qtjMf8v8b^YKY`5BJRuIa&_fsG9QJ`1M&#3dq8)%p}Rr>3qFn265Yv8-#U)*eYIQIm|A_ytel
+z8<PYUJhbjsSh=~N%x2u~BA9Jd=s2SJ@op!jE4f-sN3lMOu7+{qa}zK;fd)#S9Ee*#=kvBh&d~)+apX
+)8_F38Dhn)|pa`$MS@gYhXr%}Z_!EQF5U&*OH_;t1fXo(dR)2+kl8TGg(Q_J1+DK+M!l8{M5I_T9+EP
+qhY?Teb)BOTs3hv^#j@S*B*d~w|8*&mK`&L(B<xePnM2-=3E8;O?o_O3cNWeGeF-|?i6hQLh$`!iQeP
+?&(eq%_NFUf(knGxN?b_mWR(FWrx*(v1D>wePLyf)?JKDdTj5L|%W~?$yNH`T)69N8?;|td6SZN~RK%
+v?eRpuasNN1Wb;j*VdX*x*qP^+1A!F5r@<o8@sS2H@-v>eHX}y2~W999-2DvNlXuQ{G@mNW74Cz1%vy
+~d#RdjXl}UcJ&|5eKB4ulKnAf>H^ok2B?_fgtMKVQ?INU6M#0AP*2S;qD3%Q4QPx^iw@>?gL3dKy%g`
+W+>Sf<Cub`M8DEoS3tY+=$19Pmfl0WYC-6P-MbW+Sz);vGOaA#0=!5jl>dJJ3LhGy){WWgnn<6M$GlC
+HJ7xyZ@$xxHDNlCiEsMV#)@l@pb}sjC9&`M>R2U@^u1_P_nMLqGIT)?cu@uk=&?!ZF|Jr2J|B?{FEz8
+5DG%NrZ+;hCxvJOEFp$2k@3KE9@blIKL#(S-1+uu8gQG`#qyUroSY`4EA*+ng*FPMgtxbaBY6;WJm7-
+0JTz%aRP(^*xDjsR_-xMfkB$Dp&E?MCV)AdG610$(1k;gvtudX6))>aSB^AHfYkz%$q=xn(-5=*fRz=
+!x@zH|hX7t9z*Hk%h1(V&XMU-ZCJmqwO@3=ctE}F$vUouJYBPNm{u|=yTW$%h#q`QlewcxIQx-u$9Ua
+e)r}!zq&pfQ-E${j!z#{^RN=}Z@)fb9W_yrhV${*vaf`S-A{xSpudAGCpe%pnUer!Mpw(vdg`CXP|Z9
+e=z%)Qr=quSanc+XR;d!2KHH#?#)@J1Nn1$TG}0YVNCPv1<kGApZA)~Z_n?vCz=%1R}|%!$w(<8$LvM
+M*K7Z+W3>s#okmpNHz8%-SHxcG-t2X0Lbcl7Esef!?)CZX54eUhZe%s6DR%`;g8yb?&w7bvx!qP2<Ja
+m5zggw*}&$8Hjy)`PT=!r6G#c?UAE-O}u!`g|5X-bO~9$cIZs*al778Po_CNwn6sN#~>?(R%-+XW`av
+UqgP@BrRkMZ6oRjL7D0W}JnpX|il}#b#JhZa+=fXUvhGysk7`34D=a+quT9>c&e|?`9XA<7rUt#Ehna
+P(ZFt{u54*3197WpD@t!+AyrbKb))h`|aVL@8l_tA6Be6RBIk~CRs|Xb>ODB(Kx!cSIR*Bp6j5WTR>W
+|&_;fR9tYOV(QP;cd~eh$~P;jb5~&uPiO<-zpQa>!_}hh||qDBY!n7gI;C-%Y_z+qhoW9g1Z*OrY$7D
+RBpfJ$bMDEA<oRA_m2Q<L~(cGK9k3J>5bQ%tAUYXAPdnYj(BsAm`d;bL1FLzIR6(9Y=%5M!go5!?Wn}
+TCK4QjJ_b_K1ULBGaoe3cDctKh7Hrqc%K*frbo5t_VVH0SCoA^n!a%n*pXJl<0i<k<7pt77bMANt1o;
+(=uVb)I`TA|>CuU`<GGPH*RwFQHeR&ppsE&9i+x!4BD+<Ita^~@#n_*qL?)QJplK{Tz=vdVsF%y$u6@
+{`TjF-WlE~Xc`TT0mX_VD`n#75!rI~UHEQ@%ZCj+Wf1}jiLQ63wVK39cs>#aaWBixct@>RopV=G34es
+`vJ`QYN)d>215*>0fY`_=nGyTtypGNy20Gf;DN8vj7zR=H;jIW9=y*3lOJw=yQM^H=*qU}vZL)+j=t$
+Gyi=T-B`ND4{K`&8hX8y>nFsdPXdKAmdpl`si2y>6q4JGBYO!XL;t&&+P5{KZEc8TO8%<?wYIGEx(vH
+IN{KALb2GfY>B)Mn7|#YtxL@TE*0>m%)?~ZTgR{*rdJ_U0_r|a^2ie#tEuK}wje)RIHe#xalhZlJky7
+1#Kfw2!*NR_PCKRUu!^d7ag<gCqgS^Rw>(FNI-TcJXe0>%S%|S6U#=H4kN*6er7^wZthK*OWQ$`)PP3
+6!8s~!aPb#c<8p#JqSMol3^$;Ds^E{M}S4zB}6!(=m8wY<(gaJ5FskILmb;%v=c(S9y!8I+mLr%8xlY
+HvsL(Qjh^W3}!NZ$KbzhsG{Rs^Yxr{il7bD1Nwl-+5}wmdvkVupi-d<^CNH5OAA=dZ>mFWS_%mu&)3)
+Nb5s8ztq`TYO-H#h6GmgBc9hv=w~X*RQ>hi&Cvf;YBjx4K7LcycJVJimdZ(nZhwY39r}k?d4Hi%LD&%
+t7P(ux;^h!W0WB7(Y!97)00P%ATS22eLSv@UGhR<Q9Yh=3}VphVsVVRWl)DaU|;9EUhZc0<eJOU?^Vq
+q@%lr&l^f+?J0(0eu(dz$?z;((=Sb{(NYBZpFf$o#uZ24km~~=3wNe&BekvlAyO}<$FAMK+pYa~9(Df
+Y&AKCI<bcW8Ao|_Sh4Uy}f-R>e^BjLV{qP;q}=|VrBB5pb<{E~@1gG(W)U+dvy4YvUq*bU2DGly=Dj|
+XIDG<Ppr{1v9l0+r6z)q&~r`5e;i2umfmsR<H~OYMI^*W6Px6t{m%*nW4^-znRVM|>u21O>wwjll$lA
+QZtc1o>rRd;x>{E|C7ffO_k!1TTOtT@DJw2om)11QpmcsAfjK=4?#5YRwXWj4ekL1<(!2Qc&a!)Jh7#
+)))+gcM=MuB;qjOhDZ)l)W7Cz%Q0jI!a2#>O>LoVAb7)o%nQ)gU~NvikTHw`>H0XiR+GVi!hv-9a;+d
+Qfky^j14%^=2C>k`g;Os4E&n}d8$iki8NPEi)td%iM_fvW?_VavJD(K$A&?Aj!_|-FRnm7(2HJ2rpSm
+HllM#JpQ4mOwNR_zBPM?_4N3j?CrbRcau-UA|UO#UeA?K$C0{?b2&<~{UtI_<1w?UsZ=cc`^l#GcRWr
+n}XT4;TXp0)*TZgMM2d%Y%uSqY|RyTgTm#Bmcg21uIW@?{Vg(U9=l@ht2nfz*{&V4f!;(VKv-9I>A}V
+#~U^GRP;-Es5yxBp;jbBvR=Oiag3rO{Lvz`+{;%xVy4QNXqVO%Pp%ODe;xRAs%NPKW`{Z^Rew>$Q!kP
+f*qRlPK5+@DH}9`o<ugHcp=B%dQBl!Gx4RmKV6>0AyG0cZg$EXcTc!JS5$<Na7ErmrmLq+fk@WdQRrn
+lG9LRFT0c8Hx>!G<XTRqQ)PEZC#b9FL*%aw(j`Ta4NIPnuyxdJ79gFw@6`CRH{fke%q@M)otl){jziz
+N;_xLL~?Cq3ONRKbulsI15M0~nO@-Wx2OrG((YHp#7Rs;*Ku2n@JTdFp$!a+E4PvJh3n_dfg*1Kw~Qi
+*8R!L8HYf{sW}ctl9Cd=~jpgNzGT-HAQByG0RCAe-9nUZU7<H+dlIQ|n$9DyLmvR4vS{aGMm4nP$I~#
++awoP3kC+;&FG!y7VR<x`~|7)m(3i`TT?tVN|w{BGQw-A1<9;%BigAFD7ze_uh1q<UU>5E1}wyo_7vA
+ayYFjeVb)PW1zbO=1gjto08LA(s{h9waq?e6n?e@X2`W6S7au|H!&q4;<UwQOwODZB_=L3E#)jYemzV
+QcHk{ivE1Ea+Qa)$++$hNsQQ3ikt6-V+aN&P{u|x~Nu*$og~L7SKDFqF)TMAX_BDBIs=x8Jul9xBcm4
+<7Cj1j`gWkk{|L437VwJsI6>JBe;61MSBi{F1iLmzr!knU{m6P@y35ug34Jwjn$(3a$x&>UQ$~Sp%k3
+AG>N_KWc<mz4Wfx9-5Mnww7NawJN%i9Cb?xHNbZdQ#43;TFgv;D*^w+WAeW*u*L2AcR=j7-dn8VKj&Y
+TW%G^3inV@AB?;Xfjt}wmNnQm%E4%XLU?*NocWJ*|f@IJdmLcp*sv`Z}9-5LT)eQZSv5E)m=;Z*emD5
+d468>%xKR-)8eJO<)-b^?6NCA4aiF;_SYCv?}hkup5|gvY$^9p&1gt5p(s)eT-=+UQ=99tL*P3p4O*I
+<4M90wBO?y$QAz~)L_>+wEKeY_X~u9n73Z@bP(I>6JSsoeVI2u^vgcS5;f%d0%Xnzt&=5gV<t#N1Ja^
+E;Vgw@b)xGWuh7ykW*>3%2E{uv90ujmiTR<O(UVdFEi-Mb{)+hUec09|8LRE9<<+cOm?Blwv?!tCw1_
+9n}-23wxPMP%D?@V=;Jj+xcNxk8p5v(RdUf2icIZo1sBga0{#4ThQ{Xt|=#^AR~U&P$Sygs?8NG?04N
+r87jZONx;5u;}9jQ!Ol;{GPtk`<G;Bx<4!bYzb$e7Dgu$u{EtrQ)W`5;o6V2h-xNCYcP)DSH~4w|S;Q
+CDwGjp-OYkQcgZ-ss4f<vf;y3bdB+sz+}{wV@^qOr0xWJrFQ4}#)m91-}@8F6VE^9ZE|sWOO^eoJN#w
+K*^ga8)A*l=+<$SJcV_per}#l70HqijMM;7{79z!v7>42)g3&OEV<bZ(%bUyZugKrRkkFMpy=lS0H1M
+|pQnlu+<nT8}xNIrN|D<0xHo(?WN(2Fkw8+}YHl^2w?<k0k6>9+`0s|(NlYo*j>lGFj$oxugXL&m()?
+-NE3q;VhI5PzVl*Cs_Is_yE(-|nQTt>bSzGb(P20D8M1IDpXQJ`3m0&0jbU_g1XD)Yb#t^92YP6=Xb?
+MH0QmUk~C8Ib;Q^vMW)4d`$aFJI^DMqlOY{?Pr^L4)DFX5(8k_sQTtsMiJhyu;_(#NRaT`;gdyj5_;4
+W5=vP*eiM&vrm{_lNeI<_gq~k8l`W!JdijdN*prp#VtT%=LuBp^zmJ&;Q0A{dl|taI%vdu6D;UgEfN1
+TB?SUGDZ{M_itW}f6{NdG=DDDcy#K;V=3=X=tb_|WyLO`O+%^oljf2hHisKVo7|yt3AT~^Q)G;(uQXD
+%SJsxAyw+1sjvFtQ6^)u-s9xY&@-anAED6db+4>>1E7>g_JQO!cJ8f21>suNX^xN_)kw$F4~p}7`M?T
+kvmJ>H8Y!X2e%RO{eyQg2gZUj>93r)(5qh$<L4E4Ukbt&psJ9$GZ5UeiPzm@45GEOkb`?Ly&X_8EuKI
+C6e$HZh$)a^y_a1QtSPF7yXcg+)UoHmTU{J1RVN2kCy@gfOWW?Z%CU4m*;O{CKom8X2hs7u->BdW_f@
+D%4s`wCP{sV@`(gvAM&8ZF3zrneQnnusYhpFSO=Z{97OTUkLUCVxybO(-3FDY0K};R!ZIBaYN%yGYIt
+`;!09KmajQ~%+8*PV(0268<#p=>4RuuvZ7q93#@cKCX6-$k$kaFHGX?`qO(&IJMWI)q=&EjM~qg`;>4
+c)$)l-xQ(0XRBVO|8l4!o+m05aiAvX}7QhsodBh$NX&YSD==I>r-d0TCH$uvV_KKWhP!qjeBOcCA|F!
+WvyZMchbDhbqv+(DP!06&#e^s1}4&6-!&)uv0n207to@r2RiSthGkNu=g7J+>{JMXc*;%_~Ft*r%l$@
+|Q=fr#siOwQ=+0C%cXMYyWU4kMq@|Q=-W|9}Cx~_cgNHme*Hzl#$!X*(Q;>n=U@YdL&DWyQ;DCBF!+v
+B^{|w?k=hCi|`p%rB&_KBa)E8`Oo7@k7F`EiVu+aE@kuf#{XJj2^5a}2tOZ59KvZhfhIcJZ@H%AUq2l
+9zyFdhx4)7%u$qhfujjq^>(Vu^p8)g!!(qGw?$<;346h_ZpbWCWD1*Wn_#sFV1=4mHMN<@lGC0h@U&8
+BxP+788Wv(6IvjPO4(gKJF0HFZ{M6QrENhGHinETqdf~<9rt5*dpfCnR6^<Zfnpd3zvuo}axy%sazt0
+6Ps;s9Oee-#g-b1(q31p6&0pCM}pAQ+U>AUVKRj0A2MW>v~dQE*0PjhQiPSD^()$rKEJxoU~8aWWi4>
+gaT>!}+ZWkEjB89Zw(dYKl}4yo7)%CXL~@mdV_2=`Ye*pzBYf1AmjTQJhP3$l=%>-{JLQ@X>o+NeO+?
+6aBm5{)-cnL)?R&C}}O?hVXIVu#=4*<VQfu#%jUcV~#nE{tX@1z*@_^RQUswNzi#XKbVawGtY&@e?;h
+@HAL6F5Azdzev)eaBruEpC@>q1kU5K=bP#}X`tR-u^z%*mxAz44`KJ8adjkD@Q~u<he9lUNej{Idzw8
+C7k~+ULit|jM$M9fiqUalfJ3W)VbcaItcyWU>wq@_LoM~7|kG3BBuwtb*&u%-#+~Kb{L)lxQA-q#^cf
+oKp*RO5ulx`jgggLUg)B5bFv360ROfE-5QlBdP5%`XE&pCotp6&KYmWai_I%I!Y7-cDEivgCEm-Y;iJ
+0;3O-@G6aMcrmsZCUdfJade?(SC5(Z^Y?Ex>V0u$J3F)Z!a^qMt?dh4{U!#P9)@>x4VSRKzYn=dibFC
+0-YVyY|6NfoOR|EYguDnrBQoseGI<wrE7GtST<<yz8GAC%&UW;%2V-0FW9$upOkmPb)$k$Lbia}2f3-
+bW7ch=64?;l>!9Re{*jeZb1d}BtdhS~_=xh4DJ%{Z!iPO{qba=@;OTtquaCi$NhDW&;n#|^g`==8WjT
+*w9?7uuAt5BBp*dX6jBc^>_JN+lQ3*<2seAMy&Uar)FR`K;{jQsy>JTEA#fYEv#$Ha8GwlpT8g`SJgO
+i>e@tQ;&B?TwtP~ZoZP~F2TvyjZW?U32T@u84jb|RK>Bn$19%^3B>9-wKTg=FSM+0dyOHkZ<g4z3=>B
+&Pzs6p!TDS0z%IX3So5kv<-ugHI@GK|XbaQoAh*ZLbqs6yaTzX{MeYM1C1WlQNF7>NRF1+@cpBKZ|Z-
+j4owo%WWSTaoJW>8NtI$w1spf^&(=5v1rrcHd*Os1=IULnW>pEwtJLErA^t3x5_VhZ5E#W#N{T=WR^R
+exjjcgb&wdzztMCL7HC|<4{v%s*kAi+y4dZ%wR`{fUox?v`}^VF@%`h3Z^o}#JjToI{2kVRI^s|H#s8
+nj`A$Ion=$_21;Aj8B5@ocDT1V7f@UZfA%PArzHFdK1V(V;%e0*=1v#k2D$WP`4;8><2O15<+B}e2<r
+px)0Lq20WM5b9u2r!>gop<Dv`h*zY(aW#3CduQ0;mQOBp^Rn4&Gg^gX18vxA>J<Ad`UO7qg!#R<+!fB
+!T{lFyJ5P+B%w9H4YbMRII|g<Qo5>8Q{@ac3Zetx$5jEYyJ+<o4^6(TF6TNdzu1+LLBP(-3wseobD7e
+r2g2cL2b_Ro%&jJccCe-`U^<@sA63pxrv(v<JS1fJ79iFoY5h1ibGJ&@hw90Zfjfd!NF2YyBXePwAgR
+zg0r}$ajvyDCvH6sdOPqN2|9tOO*~{L?wB8(%W{Ig00Qw!FfV93r0ql)zCYUU2L3#S(5I`fa!uKbomG
+iLqIT+|*Wa!FpN)R#-RghKI`Thp8W#VV)BH?n!2SEXT;adCf#Ci@|IH2jmOS-8zk#1Vh5y+N1osd6d;
+|Zy*&;t^%OkrxTo9;hS>yW5bql7lQXgr@c~QHKu-Vx<5sargR{cQR9;RDXO{V}pk^J@ApLcV;JrO}#O
+wa><&f5#K;gIr9!TU6B)ga24N=Up28|rFARdpZO9tn55FmNU9>EG(<S(4lEc+S)ra`U4QEtJD$c6(Cc
+950u(<S;1NKAAC2gHh~}yGZIaH#*2kd1Q8KLtp9a+-4C`+A_ph8~a(<EMdar!|Ar{?FR2Hsyc1%*l9B
+_tK9rz29~)o2y*s}SaBxyh1qA>l|9t)Hqedl-3i|YoY<Y~&{R&s)K{Q`F$Les^HypS-0E`Av1zW$Hip
+kDRI?|T`6(E?8G*NusTSesNq8jh2OrHAQ+2Eo`XALh-or)Ed%P&MBXu`MAkR->?NN+8uDk?j70z62NZ
+A-Tkp|?@V$>gEv}p}~u?1#Ufw;=Q36;UimF?G?gC}2-dmRzCk>0S$!%<~^qGT5zH7BY++C8GvauWIpT
+E#C9(wc#{)=%qLJf||#r7xmO!k;unl&u#hC65&^iWXRnh7Dp<?FJwNF#m&`m8Xf9@qp@kl)Fayj9oG9
+{1^@;+8@p*%bG=~CplcaA}8S-jInX(8Z>&eY_mBmPs+?68@iIOJl8AO&=IAkCA@sDg#5foL!1^uDuRe
+Aacu5HY>b5hGY{_i<U`fy%$W1>Dr<e}x)XQy-Ai#Q6->BcAJ6k?YDD-fkzCm9pipG0S4Z+mb?2`q|6r
+wuwA>V8>|TS@4VAHm<fM!75RJLmCMSN^!WU(P^~pMP@+pcTytJKk;*qADJG=5<C{5;mfL|UxDZ$7v^a
+n(;m7Vn%iRIHuDPs=QEIs8KK8SHZo6xaKGDo?gO)#SePgM=l@gK)(n@ID^K3^qEa)kq<T=f0!F6POF3
+eD@EXP*?=^HzLQodRBzA2wWW{pGfjh@aC>{`|0y{F5IJ`Ke(J!%!5y&<_M9P>P~x8Y5APSjY&Dk|a%{
+F!p6v#XJKNPz!P=6mUGCEAz<KG>?UgFgfrvp!v6sh&l2l3!&Gdo$M{7vDW-xtGracx-%FQydW6BaHt&
+ck9?KmFU$WRzjBr=1R_J$ZaHg$OR}bvEJOsxfHosYl_7yJ70@+70kz37Ky6au+xS7s3JwyCkyY8a%s_
+ymq(B6O$p1rOzl}vafwSbwf7cG#^VWf_!ahOxax2`l=@`Rb8B7AF{#HB0M}s;)Xw7#GDdv?esPhelei
+Ghj%qGHuR~)?QCj6v83?Q5UQX$r=K&%pmx2*lEK8U~~K+?(;{?3WMtMXqw3HjRv1}N*eA#2|v9zQ9Tc
+yYM)x%m*``bxbK`lWp1?s=|?Fqq2&DybCj(KjFOPC=+m9B!kr&D#en7H24awi`UMhNyXIa5<hsRGx88
+bZlqL(}BmI+q&p+ne5NC+)XGmT*!VwL%gxzUVgY11Xo7RZkUG<W?8^w@-4fmOo>}JG-_Pd4S58upT|_
+-#jWEEm4Ddl>Y<Y3v_#ceO)k(F(v#gz?!K*cdO9yO|Eb7Q!fJQV#td%27_u6BF}%pO<H9bj3~9G%_eF
+f2&|Z;ih!PS%QF^S`l}<QqxZ=4(vP(K#+NVyl+Z0C5+h@0<9p24w%$gaJIq0z3c1Z(2E6#=J^I-ber0
+{bh32oBzS{9m8$;Cs#;u~ZPjKaft{2~Rd^1R(Yawj^E=h~X}Kx;~nl_0W_ib<@xeEzt|iUw<Yto{us|
+54H(`Z=k>u6L+3T*Pfj<Sup-wq<VE9Fo{1J(}!#f|BFmaW}L5WwDhZE&2?M<}2li`P%~ozdb-}OY6Z$
+9Km*pI%~&e63c@G(3+Kb$eeuc4aV7DcOw>rhX>z|q0qZo^F;cq*~q(u^1TNJkui3K2`0nEpeaUd7jCr
+C*LObWxKB4LOxw=fjBqvrZydMjXqPB!hn<eJEP1E&W(#45emLYt#_Tuhf@l2I4yKo0+_h)*e5liPiz(
+StSIRb>Xf?2JgmgMRk*vS<WPh2?EhG@<C$9^`HthDTbJ6O;>blpUnYBY{x`}(5f9Tn$Rc`Uwuj4Y3%e
+XUqDQAZ+e(ha|-@jBcA@y=2j*O6RwpsK-PiWyRR6ezE1$(IQ*3`H5KGqpImbu1l=~~@IuV_q&!GSRKp
+*){5frPq!xe9PR2KX*Cl63UKjS(9c;yRg>YZC5JS7Q7=-j0{Sf_bVI|3%TvicaqhYW_76{^5Z?V&V6L
+e$aHJK-(J<bd19gm_P^uM==yxOmccL#z}^v3Fb@v{sps298|Rcw823F;{uP1d5e(1x?bQBKurQFMnPT
+7*Nt$n1V9|M=3p++1=NUPP=pGwiUeqczqM~mfs%2C15>?Rt7?#6Wn}_sWOyy0!NGouNxtCQ0)uG^qWu
+e`qVRv1Vio0rK_&qxaRoZWYY*ZKB$)yrB|-3hnc4!aK#pU@;$)fK?<F0F6%4-z<`-M))LXTGx-=%k?N
+xeTU;D2E^Sl2Z48OOTSx`;-hE?wOhBrU@?Lo}_3>&qIuQqOd27!5?%YQ~d3*oTlcK>!n(9c8qlM($Ju
+lZoQRCJT4AhF|#aLmiHDC|*VJUZKYiiDE}o$P%-6n)_|hK5Nq5<Y9paW6bMOGo5&o;`z7O7?=4)rK_s
+`l!UkP}61h@=hVm+!JUkDUZZR?eyx<nkhf+`ziEg<B9JHWZR#*1AKlqg+UmoYbjT!Jeyj#*Y$XNmWd$
+AG^p-WNx2NUqlUcw;IxrLD%unex^}oR*8_LH?xpdXsS%4*9LI-6jEAO`^pD}vI9{hhN_!yA+we*Ude#
+E{q9{rP!JjwFg*i3~6hUSw@)9Mqws6^-wegG!DMKcyjYnmB6m*wnzuWCk2M(*RzJ?sS!Ou4|5$HFKKU
+S&OH=2$g^~K&i!H0cUW4-WUG{*1<Rq+MXa(zIHIobSj<$_~5ffdD(e*?h%zmwnJF(PMZTzNhTD2x;Ab
+G{#yTRY#|vmw~;lEx2%zmL!^VY>?-$}0MyX$%1GnSFcW-W0dq#I|(ARF((nP0kn$*S*UNtFpPO&VuDP
+aPG6eHVnC1u8wS(IE6nV7^+;Kz@mbDClaXXNWUy=NSc!;$-HyFc-cuLH&%2Z=}p3)0+=%?>!$7OK<HH
+@Jon<^OdK}d!GaXHw&KC2Z9Z{Zc}%I3BMV$H``1I-$FeierI&89ju(1Mc4)Fkj+sO=#2z2cJ!7<xO2D
+mv9Q&=9Z|)D?JQ3D*=QBcbaL^bY_8dO9>`)+h>Ie`2{*bgSf$@<!kg<}pX#|~2k=%K_I6qXPlGXHT<O
+esMu0bGml7HQpc6mk)VPs<GQ8Mp+7tT_WO-hicBSTLaRMNW8a?{N_CM=2dc<hHKmb(rMUk+lyQJ>n5p
+i{@*mD*mNwn@4hOUF`j6+1d1orQlc*>4$^VEscj+WQXdU;3#%J&OEi9smEy2|mRA|MB1-8^_@ovA`ro
+;xvjg3sf@rVouToOfUb@7><)TL48@qKx7~mzJO%8mIP3%DnCI24>7r_J!BcsV^}Og8vYOUwU`u10uT6
+tsSAdcYd;!h^=dCxUQU3%GjO)*D6b_A3&?>~w;bs9Q@_;r&w$iqP6D_t-#XTmK&^^l0HhbIWs4xdpAB
+rq6y&f`s{^}Sd-EVPkhNHjqB8*5FagX?WDT_gm-g>fA{1Z|jUQzUws#SiyRS7kdtWFOj<Dyn+5alkUj
+3}^538S4^p|(f`6~{AP@Y<OA!zV&Y%<JgvRbs?^M^!kG87+`?s3b!VnIv{u6oBe$A%fxgPr|ankr6x8
+a8ko+LJc3%}1nMIh%XWXGHJy$0=|xLwIY!3Vlm~ds*JHaTdR)z^O9#6Z$H4Ev5$dHeH@GTnEz{XhB*~
+_h5qlJBhql33Bi<9QG~1Y1@i8=3W#|ZHj4U!?}zy`>q)+2_B5>AxESE3|F4qJeHNES?)n+@cw(cU-9W
+4{IeJR-^e+^BCnq?y*@hw2Kkwo2CN&wWV;=UWOwkM%7%KXbA{FVOv}x{pLPKs-yV1B5)@-DNz+^){Mk
+pi?R5c6RKChN>{4Nv9DIp{5q=sSW1E;nz$e}~PKrGH1AFumC3@Bkt!QMeY1pfplIVJjX~-t-K}Z!(Q&
+TpGthTb4B>T<4ZA#e=HqU!ep*o7GW74JpE+%zmP8YPKMOSSw>~`p(4VpjfqNS}Aexaet)2V#fhd9<*>
+VQA2lXe;8rxXY$ORV?TgR2S6)gkXJvXkeld)PpNx9ODh<}LR^@6GGovY5c<Z7KE>&lKrLonSJ;i!pqm
+c3db;9A+v+bcw|qEP5GXLESD;Z~DDuJ~S?6Yk7$DAA{Pq9$KM3oyWi=9ro(+y_yn=+4Ldwvq$)!3kW#
+vEh&!{L73eT$O_u;Sn^mOFYgkT0FKwR{g64QsCoP*VYxnaj>!D&y8?Yq#9VfMrT-9S-}k4Y-9{;yXT$
+?LJ(YtRL2hm-glH{XZ>wF0@bV~_pyVYtF1tV|q&p*!#u>95z0yUm%BPg(_m(WGSB~WPJ3Bxs&u}L~$y
+_m{=`1atU3@d>iq#kN_bjaIvW?p8>3NjWi>5~H)2wjIq^}7ByR(sPA!o{s%UzY5steIAzTJy4havSTs
+A$@v#q2yEVoTcHse66bYtCwf5Y{+GFXS$vD;0-kEpX3UZsFQ(rpl#A4Xqxa3ViNjW{<mOkd_xBnFG4?
+<KP-68-mSl#QsJ&vmX<AzN|>(hR3Z|!Y#t!cGct=0hgZ54V_8(b04`Ej5fKVo=)Ors5!}SD5cPeDDqB
+sYq8p)*!~w`_HW_kzuZgn1gLrcK~d>5{{DSW{r~9yhyOW2{r`~@zT^5Ip6myJN65wKWst=ZMj6l*cKN
+qpK1C2DMw2vx;xx5v_|iSR#-JAvUQFN&29f9mHIaPvup+=Q%#fg7aIr=gK>bB)SPBOSyWnqz0oX36Rr
+G1iGsjaP=?rqyGmv3U;;Z;G3=HR0mGTz=j}QQ=7gDg;#mjjYGZsiutuN?R2pgt=5H<p@d48+<(?S`jw
+Vf?P0UIA)FP5TUWCXLSUgTgX0PlaFs!jlHi97iL@Bw|FQ(DWUXRJuavOWva1^?PTeEP~F{-^?6@Hz6@
+0L~YaGkI}dicZLy8rB(=#}kHrHl7gBP14`nz`pX2*s3ZmYmpMH>rVC)ZZGyJ_Z~upa^?KpWUkrihp_N
+sGOrf%M_8CU{XAmm-wyfb5kvoW$Y8|(!lV|?gF?E7jLE2q-*{>vP*1jN9#pX@FD^WfGn7$=s8iEaTVe
+S)Y1O`A^114^RW+t<BiZsP(Xc!?9-J-{EBe7-HR0ftwyCKV+3oj`#gDR9HDorFq%Y|B*zdh0QS13EPq
+XC(%A7i<`i9r?{v=6FPuZAPqQm9M?i&xiHBc*?y~~CheW2&Zvps}ihsRU<Jfm{xa0f5DvD-oJ8A?2E>
+&&`cJMreD4d>4;a@RWv;;Nf?7Y~RXAkUK@P<!v*)(iI}a@{FY)j{@;$T;ni%ILDKJ@+Mo3k{7^Eaxg3
+#^TU5BA)kGj<%9gafNZbPPgFbs`M@m6X_nZ*S$+6Gr!m2h`-!KLwyR~cau5;Cbj*eHO!0St6UfWOh)L
+Xxb7Z&r{5&jNOI$=p6d|ztETIc`ptB9bVEFk4u}wnmTZSVTFy`k{KzPC&oJ};4U0d%5B{<}Ur8M?|Cn
+nT{P11)UCSWqb{@J1Qr=4P@^~y9<rv;LV&~_CmC+DcI&pj`kYZt)wd<p}ay{sE%?2nhv}_`DRD%{RVd
+MH$)%TM$bEmP514np{`BSa8&=Fy?Y5`7M*s%l2>JD8CMzM+GdB=KFb}a0vKbS;e?r{F$rdQu&wzBNsU
+!_*#$ytH6-83oaeb(el!Ihb`u>GsJz}cZy^xLWS%KHwEd$B#t<5QEiddO~XJL@%waXMqlV}Qn8q0)_s
+-BGT%UGWQA`}cHqB>0scvFDU}uly!;FUDD@Q+2bn!pZE+L&(c9wisJ6p<bSvWG88PGYPN!M4N4X?_(E
+1j2E#`79=<+1#0YX8~S;U#Mc-ke6`%M*B?W2STxQ3o6GlGqSjl=_%CpD-TyBsxmB0HJ|AoOKfJ{y-T5
+z*{RyuBU%%Kpl>e&>{Z_GY0mTLX5@JnFM*%i3Ms1n_dzUJKJD8-_Ru1S|TSk5f)c~7u5L{*QwIyr@u$
+f%@&r-#^ZX*n`xj-L3e2wRVmJ<Ideg)Ob0m$kY0B!z~l?AK^EL)aQE&l))C_xK|<wd@>sl`#?UI!OmF
+n;YeTaasld;wG)mkSd#s58r!YyUQqEB+)Y@Jq$U;}_Z7(5E=5vDmy#x-9Lg7$37XA5`h>?{{~x;f$mF
+hH<*UnTI%>?$Cm1a&*wT^u_UuF?&-*f-}76Emj7Uk$Gg@F+)Dw$aWrT_arpd4nhzJu>KP$zQK65z_mE
+9jr(RFeuHrpY=a?y>+8n)>pA{ziqP-o_`4}WznkOlrU?Cc0+#ShDVHgV-Ja*=!eSjxD>gSMoF?ylogU
+o-<0qbw8rDEgi*X%n_GPV~y8N>6U$Pd_S5phUbTKd2Lw)I|ggq;SRr`=|-r~sVVf0)|P_Px%lvVf-=5
+hAgy`>}A2{vnzD!i@0844?VkK=whp;!*D!!Z}KJEUBlVlUUYOVQ@j!Jp>y)DycB7w9LSA4WuX^>Dn=I
+!$?Wss%@-DG!_~zA8i`h>KZ6Ggayles}O2Qjpukq~hSU)7`t+>b+a<+iKoDxkDHXksF||K5KFODd;C|
+2P>^5B_-s>k9$5`cZV|Lib$fz$Yi;an6~z~okgPJZK81|c3?AB+T&aN4l6@=);3|Wb(?W&pby}$L>PE
+Ij9=BT3FmjOH;~&N^(%Y7@C#+z$v=6$<wvh~Z%<s@N-vNz^_C`;&zYjt(YJ8$dsaL2W8;b+JOFLuoNc
+r?9UDT~Vq6zLY<mND#x7YN`}^AoYKT+itD(wW^-l*}W$r|}-06!o0s50K%s+i!pg;M-{L}XZ`u!KCUA
+3fs6)J~V{T$qV*I8(tx`Po6-=31<m6`KlN42)FE9EHmjWlR2^fruULikvt+>iS#E@Y%Ia%dY1+Fl8;0
+PC5JNx_JDz?ve4(<4mZx`j=Ss=&F^<i7k3w=s)$2$m1GRpt6r&Sbxg0Cx`wVc^C6GCJJ-><D|CFqLd4
+)bR3XA*S2ZSLJnjBrx7v=wq8DiL%`svMoj3=dgp$IM8AhS>if5(%tTiW==Ea+MJQX+P5&GRmT`9auLC
+zD%uox5iNx@l}4wVgXSnQ&?#i{>rT1V_AMDFcusfw>%zQ~_Q2@-$aHo`Y^URqap`bdcL!hUeVrpOPf6
+3X$Ez72REDRrJkS@1RgN@1g!#A$&41DP;+u0`<oW+TJn-*N@>7W5A5QR@bl?a<(I}14Bn8tX3EEQ8pz
+s|<)}(ie!DyKIGSeMMIIb#`6b^_5wswyq3Xo$4dP1z(>kBtP3J?WAe<60X9*q}(EG(uyo&)p0SUWFbY
+iB6}Ud!J}5Lu^-)itqt9DbQ&hO8O(3sa&J;EF(SP*KNFYm2HJR1Jb!y>iV|TMOsYwK5M}F9D4z7ABL@
+U>xYmlaRGz)iS1q4}rs}-!T*Z1ei&+`EVjAa4}q4mCYp1_gJ}TQ7hin?2fCdg{{=q9t8d7w^a%HZz2S
+5YDxct2m#3I`DnGMevc4HHf&U1RI`1-RI1Sc?S$W=1n-IH%SKPcKPRI9yz+iEMd){P{M{6x-_7xNQ-p
+q=V_vg1S7qNmE|ml6!<B8bYGt0-1-TsTKIB=;OVNSK3)g|9hkJ|q4%s)V&fN))RW=9QzMS_Q%yAaZz!
+z9+#<|K1kKACzZa4TsB;L`}6xGrd)}X9(b_{>CAA3?&WPF?Q^uPyA+6Q;tAiC2yqYH*#?i8ha+_PQl$
+39~x#BmOiCE0=@E#1Y~)%MGB!7Vi>I<NQx`5ov}^7W68>S$hAfos@WWU0|ii|Y$3*#7mv>vBs4vc#Tu
+o2$@5rCrO5<;uvr<2<<OE@EH#+_&Y7jC;e9H<F-S*_!nk&7j>Rn~R>49m=dr$4b3hK0vZiu>O|HRf|R
+1I(4Q|CWyzJ(_1>%g-aMPt!w4cpRdlU%x^&!(%#s*vS6;A{CL+@e_DV4VYl}ccHQOkB8uV!0;4pAQ`D
+E~xNrt+qC5xTPLPU}01>u~2IVyAn&`8bVaaNb5!rt*>g#|i3}T<lGPW$3>1qyw3Z1oxd0D+O1o%j))l
+-_UDsGE?2gH6stM*^%!Z9HB3bgsjwIZ4P4_bf)kX+^Dk`yePKq({#8-egh27Ir}@}92hcEnp!ZUL%;K
+q~+gSf-!=dhG{@p}#faSm1Acel$#%EJu<=&Hhpou8RC0bT%ON9Dg*4S@@MIj<KAzV9D^O_ue2Jn|WNo
+EhMg)KLr|h;+Wfz=Q5rHJC<SNqwrq4G6tfJn4as2mcfqifknw(zE(OcPU_4a-_1-2*qOX7hLYTL3t?k
+3G(V>&VoY74OR)ge+xT5*?ghHj8uP+~>hsG$)|}oEczi+czqgIe<!^KR-4vnU&GC0rgnl>2-%Sx(=lC
+yzsrTd7fn{AX=7HtZM1}6mjGy);gTec<xS)v}%&2$08H(N>$ycG#=d`!renK47(z9)QOCGgv<UWt$?k
+i?dXnIrw+RN9(2wvM`w!0tmO_BPqlOpUggAd&cbq+E(*9dnzcj}drW+HnDo1x5}r?9}GC!EjXP&#IKC
+h6)*+y+Lr&&g>ZWl0{|EwN{`V_DRh8od+=Eu($W-;%?2W<+>%KvGDe(+sKbOG4{8UHWsPA=XySY4fOu
+7hJg5BN9Zhca4QyA-i%1>srKRW?tUpi_^0c6x@k%*C#JSKVn4Jv4eZ2wy}eGI$X`Z*x)>+$kg!c7t0r
+J(1+MwcP(5}uN;wWOU|MF!nGgoLL^u==H9P_NbcX9wQr7Er+&*whZL6;!4qY$7n$d;Tv15`$o2lw9&>
+BXrK3m^_>+ChcQ0wb#R=;wEK@LRwp_318lo()Vf7C?n_txVLGQs*E+1QT-(!;RT(&&ZTAf!N3cKHqks
+{*G?n*O{3&%Lk{;0h$8<q4i!1AXC=?uERNC?e1`mG~yycbL3PNebBkIi*7H^#ULDjQ>@C$A25Ii?b38
+}a?ML*PRkTM-oL)3c`J%bufRWpDM`Stnu+;|W4X8IDM<$7Zo`wwKG0SC@Ta(rSmK&jlTAIe10)3gT~l
+vehp4y^aYFy2%UPm-a~Hx8s~_=3d?Qd4ITH?BkGIO=YWI@fwE)%PgGOk0qKcH_=Tz$=OJwsWx^6imHd
+BecT*;OziT3y4}eaoLYE=j`{s&5_EnX+XFR<qi!7@hy%e4jaG@67CbZEw^HnhQMB7VCAj6nu=>+jsr7
+%k;$lO5tIBm>_eas*|KhFx^S^XYSPOSo=O!>j_)FV9yLbCS3|2=c&mx~8_5YdE{g5F1Py2p`<*#-1S0
+^e+Ktezj%mSI97I(D;$TiCe$AAbHXe~qjC;!@PDGOjFWH>0Y0clbU*ca5;7Qi8jtwk*i4D5_D2hC_ei
+ZHRt-v6qtZnBOFBt)$RzRX(HLaqi6wxTVC0;C6LPu7y(wFn0&@V^}euZ1&fytZ7XK&`s^tKMvx0!Hy$
+nJ!?qw4WTRU&lGCpMr)vUl1F};6D`XGan8F4~9Qlsry(15`7*Z4(Pq``|^N`dM#cJzIvC;I6~h%D}Zf
+-RN+T@2%euT4k<XjHGj7keEJ5dm?1=?xpvJ3r7>^ftv_r8kQ)6|CgV2jq<L)pUM8bRrnDLjs`RZ+Mlu
+OtaioXEFo5Z&E>6C~<I`fYUEUl2s==EJXDufcLDtA(ji#S+jhrBQ^MQh@o-gWXzAB@E){+N(+EP-HYL
+>9+^qw!0Ry_?@7T^2I&fj0_>H?b=INMwc#5&a520LK-#=Rz^D?*NOZSnSGZG*s$s?9mGDT|w{hkXg`o
+A(M+d<ZTTwa3n_w$J0UUe}G%zK{UF78G1r(0u57>5_|&SU7V%`n=fgAym3S6|a1PkEdZXmcphLvjUrh
+r;E$n-U#YAhvQ4(hj(JR2ON45Y@Md|&RxTHQK6lcOp-b3SYr-8$EjuULE!GwklBvq?G{Rb%)k01KJKn
+(L4u6QX>wK|&N(@-a4p%AmkGFQC%hn?`!v&^KJO$q_JDYJTJ+kCAa|GC4((kF4@QJQ!CAW+(w4t&)6?
+GD%r8gY8~%Lg;PVe%;(rKl3c-%?(P7UNJY?-&w=DDzdGyMIc;9{%-~45GlZ9pI7tzMcans#5BG(<VJs
+00|)+I{%I9Yah_lAqnmnW*|9$O`Z9-)h!ZWb=n3v7^6dlD6k+|OrpBGvv0cRqLL7t>iTN?bf*{J~cC&
+S*#nIqv)3En?_OAgAL#c1CyB)aelI^x~#D7UDc*nH}i1HeB_5M8fT{>FDNgu6E|F+dVKmkS4(uQb^Qp
+>2Qb%4lCl_4owb^>*T@q-HgblxtC0}<)W$U!d7`*>XMZARTiF~5=Bhd-N51vWS@xLv22}FcoLWU*R;f
+SgU_WD>>i;tEl*@(8yiti+eE0cc0(NsG4v2)7w;R3(HRR0bOcY;`Z^G!ry1IqB-&P>EBa>F+Fd)cm)E
+U3sryI1*wTM&|5@t7dH)Jkx7Zc0_Y#veGAFZd^(pV~0gbFn|GJ&3<KwUQ2C&<=<E6{9%fEq6mtSu8zd
+7*!-@MK{4F2XCpFxl&DTJmeilz_*!w`aj85pKvf*=?Kp;3&$X$<)?OK3q9fQZQ2b(I3emY^V$1cF%vz
+IKX3fr*JCz}@zX4m1P<#JUm$Scsr>=B-%_u?kPppxx|(Zi`X8*sIIG6b3~$%PC=?I{vG=nN?jKU6Xo1
+_AtK6f{{Ro0?1;liNQ1iqKOL}0@ZSAwPVS(bY^V|2V9o)+8h^KK@?l<Rq8+af||coGZ$#Epg-CV2cPX
+BuW9St6kHO@QgL^ZSGK>BvN+X$*Af?nH-8MkBM!;61q}80aQ@^OyL;l<95SVOK}ncja-RLJu;7U&gJ<
+8NH1^=e3q&7?MImYHd?4uZddNDnfnwpiT-9RsmG1`x%kS8KG{vWqmv3PS#dbkG=(i5`CP@GFL_p(bQ*
+ho?G3SqMgy~5jCHUup9MJ0KdnWV36qJuEdY^KIC&OI`z*sC~AV!a{%75?hx-46x%D%OPU0}J{yMgIDE
++;RR=rfkzvzWna=9Qtr*>I~-d(TgrnD_k;(DI^j(O?1Z{GOuPG3DRr#qu||R^tA~qJ@5F_X=+ooBht1
+4~&5Q=9)~LrflYDN9poL*l|1G3OsE={+AdyIaR;7$$^64`8;nbZ2NlL$lSXQr(1rN#2bR9LKI9Dd#{n
+X(<p1%-)nufy`$&k*l}$7nEOYGLL-Bj!_L(oadUjK^6A1jCc^s6Il=G;dz|7OGt$m<D~D9b3HY30!&y
+-0BkHBcHo8ktYYz{{6Sdb}WzlW^x@gI@iAuY^7ie@a%)CtujC;ZnxhqS;@Ql5D)R-aOzNX-$$rXf)v~
+uT<;licT26nAej#Uy$b-U+gZ`qgWsOpi;Ihx+}9I-1mIO-W=&`j=YJod<gfp89U^oyL1%<;ldXeg7@c
+y}V7XCBql1+os`ELubyz4`B@t<az7h2Iy`?^Y-DUFjT5v)ji}-l(b8AA5U*K9{8aQAMiwJZ{IQlHT^j
+=vx!y?v2aH!Oxf7aGTy{m7ROO!p**(;e}Em!;rqt%%HZ0akgJaRNY{9tSmS?q}<&v&nQ$r2E-Fo()O9
+up|=<Qg`yMJ=D86lQ~kIK<mjXG3}?iv=W?3G6bIi76%t8jUYw3JH0&Sdo#!4l^{5P`?+j#?+FZePuW_
+^O)429ob5Qh{=g6B^&voU=B}=B%xX1gGbV#718Q3h9o{l@KHLn-Ps+6NF4xuBT=6%_Cx$4eNFUd{CW7
+RS|yfAG1@jMZ))1Hmv6C~9*i%+k{yok}|Jh}OubuK8?E_bO8Z#e4H{w9w$a^g0TPleeN-INEzk0=u!@
+VM5XtxlDE1^>At*EnUj%e~0{D*m@SpQ`TiN$c|CJ^hid#^uL8{LcwN|HWzEVfdG)_`x|$p$Li+3=S$*
+X&5DN3`A%VhCt{Ah;af#7u^54t2?{`6bXV>*{b546CmM-Ua<?bZ9_rU4N(BBE50to0Z6&_gkQj$LO|&
+bQ-HIADw+~lmRJVR9q4JU)iujmLEww|5Bw`z6<dRA%khiR3lyN%y3+&(F|{IHMWb*KdPCPva4GViWbI
+_P7_*D5nydl11&2Y<4FT3JpadC!Wn!IL{@bqZ%5^cBxc)=N!C{RTPEd@sb8gJ#F}1xj-i7JQX!!_bKO
+wLy`IGp~Uj}@syr>}yn2hma41N}J7DvQfOwt#WVvF_n;*4CjpN3<PV)gD=eKdMtl0ZZ$eF4Dyj?64T=
+CG-=mYJbAG^*G|yrONow530?W!@3i^jP>UhgZeF0qkVX-?u?vqoJrj`HGK$;fQZRoVAwb$mgMR%s`!u
+@S|!r^ga|Y5>Ig!OHlL;PJKsU{qyL6$-Q7<^&UNweWZsXOdCLXqbmCkAL|#WCWc?BOMaEx1AR*FdAwL
+5s1Ac^v79oSPjPgmURPXO;FZ|N%g6p~iaxoyZB#zpD<>0C(I@n@ww{>3IO0xI&1DYL=^*DAdDJ&tdA>
+y*f+dOK8#MXo?)nBNXteKK{K?NR9gl;<2GWiDQ2Ix5e>f1=E89IWkW;ceIvvq2CZ&0~=H@cOZFPM~(^
+K4^#UgIle#&~|`btt38tN(Z6wB4N((O?mn61D{k*3Qe5pUR7;geJ>l*7Yw(mJ!9t#-ncFyOrUggE$~q
+F^YhA1RaU??|k{cxR8)sHAxA-rD$nO|~bdKfYuY)=En3iuSVXt1LKS1ls-@Zt814K_ohpjB`6D145NS
+z*5NZNj$n>;N9C;Y_RXSJvIj7Eym1spE_EAj<Zf`Nau0Keiu?QSXoxqoE~llr7>9x%3=IR_U&WV+asc
+*Or2rN$Ee$%58TP>_e5SS$Fk@0AkE(%r04Q+4DU`6P%@JX>>m2kj9$o9Hhyc)vAj+|m?(^vhe|qe)%Z
+>NLcYlC+B6qDZITV#Nj!g-0n_G|bwN5#AJskiGM2whVfi-u$YofmoLYSnlB!hj`1;E7bc*ak33!&xS?
+<WnIJ>F1nYCc>H_BQfn^qhFfgMPjd$Eem#x{3nWf=S>S?kPZqtLT*_4fDMelz&}wqL}iBiBK!EV3~x9
+NFa-;x>0-WtuH2wgTH#cDXCc1?;#Skb(E_;6e+tg4w;Xz7PF!hh>P_$o1fg{<w@_Ibl>S7m`P|u;2a$
+Q(LEytvmQ^RxJlCbIn;Wg8R3jj^IiS_?>_gC@PqH&k7f?{Q`k2E@v9S27P1)R>|0W<S>@UmlLoLFtuV
+maRv??!1jdY5SFLG$P$Q^3^uIT!B7?Ty;`fv*s)6C<>y*eX|epczkI-;ub!|}O}M(9&e35#;0~7czdZ
+-AY3)BRX_}{wpVq&|sNZY@uzgwD?jGVZKfN#5?zD%%Sw5d+U06?(vG>0NiY^+=0yx{~$GmknKEwm`UR
+>tM&9Y=aPDw}{v+n%0{;Mj7Z}&-D@CP5O=*dB%+&tr!VSoJAh39<c7URhn)o*e?0@trl_6^BCPzxF<;
+}aTv0*!GrKjwV5W>q|TVf`bh08D}2LB&O5cmWmXTd_UJ*--5+SUw4^$uRrx$fe5nZ-FR7tP*>y5_x$f
+ipo9sVHA0(;Xg%q5HVX52JbOrmqT*S1@85ek*Pv~_v@=lHS2#!9)9|?RB?i19-kzEb)k2Qn@2xE8?Xt
+#S)XslWb@?>TwvdY-VDjbc07vgtk>ND<GnnfCrnNng&+3|+4#XinMh8{o%nb(?~{vrE<}4dd$t%0+h@
+PFL`=i$<hTx9YIjgrW2;(pkScIp<##APmDcFptE=;(oW>cc=4M=!@|uWtM{d;~I}|-q4mGmN#7PTrej
+|y?{Z{Omo$OX=AnMUIn(3$5M%u~bwVaSGUL2LHNEqzv(nt4#dI)C}M#u`)y1BCMN)X`sl&2|k3bqMEl
+NH|0zRVl=sYxjlDPNnAA&7*T$ZHt1!%b|$X8R}~*4=3lgAsPhgJ(rtp76U;I?&ON_5QfS^5VFA1@Rpr
+cDJq!G|SZX{>BW>oRYR$D!Zk+VKh3$_l1h_!Ri0z?oE~*RkN(YIZtut`ZGn}#Ec#!0is7(Oz1=pl8}V
+pS$?audVn&kv@)wv&LZ-?ijU*Rn{$ufCL*(btGagGP0pd!0ruK^uf105I;Vu9w~%(!G<~Mru`Jny61h
+8pL``9GHd$Mp-CPfMF3l6+IFy|58jraS6Dfaxg(~m<3BDho*X3QbBn(1Pwo`vEhmUN&&sX`%)%)_}AI
+1P^FtQJ$427dGgOMn*j@bVyIf&Ll_lN|u?trn60@XBN&_aOr6R3ZYp!Y<qxcmIM-wc@c%YbA(i2-?9n
+gn2-TwS1nSUvp37m#@KoW{W@!m=|4FzSAi9Ap^CSAeQq40fHanFUURoL~X|OWu&ISqrG*QJ`iArVepH
+)5BNEdmLUVBxlfqLsp~d<+{!Ukfn{lWa96Z=Rk76eoGE^?2$_FQ9Mp=wf)o}S};EvM2EI!sX(u41^xo
+tEKf&s&wK`s0`0qGLDTs6L35NSKwk>_(wG7>@`&bWqwcL-kB`en{X<{MYSfolu_pA+LFQenk9>rPG3%
+1d-`;8LxV*;SqQ~-n|25KveuGA@Mn`3b+@o1n+%_+$;eMd5fg!e(B`K#&F3((uLng1(ryUVZ^09awWW
+RpWr`WG6+`g9#gOz%@u{f$x=aCw+_%sl+bx7EJ0pXJ(zq$#^&E#n8*bsgxo8sn7u!m>LCDwC!HjG(z?
+$3P=?^{po(54+;w@J19vNA5jq<Zcv9-)~#4<53;^!fF~r&JXXuO}u}RbdXEe~rkFe5v@@PR!Pvum*>p
+#S0JIT|DP_S;43YAs9T#2kmf>uiL7kUGY@sLYKIA2X%S%jO{GS8(Nm7Wcs@2y~n=7Pp4PX9A`pQxfq3
+ZL*CqSZ+|z^>TonPsnIvVaj@jP;;eRmR&Uj7?2!w0>`9-E&);NB#|5s`f2=emKd_DIwC0_+!e_2b?%k
+7^rYA5?9HEf?Vr3`6qmQ${v9eqkdlUWsHvYzs`Uh^~Jg?d9eXbT{z1`WzpUKaQGVk-Uy(9}85!_sBXN
++CYt^NZ)>iaqP!ql?5Q&97|EqYYW$nB98w)+OruO4xaHj!{*&Mkg+)Y}s_=>m#9vT@s^AwH|;{(gCqG
+muI<DAPP_)b<&jY+{QjDuX#fq13MhP>)AsxgGPFqgts;pmP!t0^ex8W0WMCkm1R3n#bXu*4|iA?hSj^
+6;d~kBWEM83ymm^V2&r|nusQU8XgZw@eA%kUIJ1wt{kJv%-vlO#KU4ScZiieati(BtRIAsoe>OibnFq
+Iki1qK^w^9xIYM{XY)3w3Z-_f~)zj0<KID#8qCB_9!{NMzkEmjDiYM9zWovA4jm-XVhP};!Gxl3^g#1
+aN#c;tswerjeG)T{zph<Y&=eO4f90!k$6CVCBwPOiLEdPrY{q6sl-?-t)-~RVDx<$X(|H}R=U~2S9nW
+a(s3m>)FEoy#Avu_;or2O0e)%_xrd3j%c@-M&M!H3ZI-+QF@2)Z8SuP+|^>x&iMkM-r>$kz>i&~~R8m
+<H}M6o$|!36l(gQv^!m7=;0K91Q-HH^N|mw~1wdUjbMh7+_e!DX6KTFc4(}{B>%zkBOJ4{AsvN;?*2@
+^;e5wz&9sWCP~bI=!>ABMt5FiRF?pnF)M)`1`DyD*}Fw=u5KAP6EGwgtxQe~XyK$NkZ4a}@Hf!TiNLO
+x-BBxzWWfqB_7|D}#MBG{ow(&}G74PO@&U(ye;x6=GxG}s<#|s0Ix~OdFv6~}XD(%WK+CN*ZAR_Wu&W
+F&=*KibKWc6=TIQGcl|VQ#s$W9iD#9fnIr&JFw9=r;ORW6C8_m}*Bc(r75xl?}7&NX9UoGB_LhmC*(u
+%n|5!{lKRLk4$J(5O{>4J<$5pF^M&I7MTQUX%qg(_(e)5gD(p<Cj3#lm`}5^oH5c7$|+oxnui@`K5*I
+O=29%b6ub6W=6i_<Y>BC{soLl9TvA6i>PXS#{8VTHe+_jQ><oV$5;LiA!E_N#*6bdHB;agqBnM!r6R#
+nai~#sM4+&NrZg=qE56?0PDMx%{hl!e0=XX41H!f{JrDw6@Bb_<fz%arf%rt(G5@P;g+je3)3~1$<Q_
+H?uSByE$Uz<`n)^pygogMgG4^f<=VO>7d~9~2bu_i*wij%d1A~i7nxl%oC<C}KxZMt&e`moF)bYA>x8
+Q1{@{A>^_a~MO>ofxF3Z5u{lX=ZR{=XzaBHJ8r}QN}UJMgD5$E|rCialmB{v_#I)ROjqYYI%KOVE8SM
+<U5_vv}-yBOYAJgRQw)T4+)lquSFheP_bIXpR3*5~%s91o|bcun$G5pmCXMs%C3ICr-@3&g#L#dDF^8
+hQ_G@pVL}Q%2I02@&{{h?LhTklGa%dK{ax0B@dIE-Oy1msSq%B8Rni5hv0&iRp7u(N5yQ5WM~>u$vfK
+qHs&AaeRflpg|F}+}6S<?Yj1Lz{aQgXl0K_(<s81{9ZGsd%<`C+w~)<p4tsgS5Vp53UQ!m>hTm(w>T?
+R^UVF-l2l@%gqY!d&h$>mtm$|<AlapgabqyF!+d1OXtNP3=yVp;-7uijUYVEQblsEt0`rU+d-9Q1Y=k
+<fmq(^PyTsk;3(q++Xga)D%2v>wu$}J^yA7zFZy`dFgu;ntr8Xt9TA&YJt)QVmw$~+{QTaX5j7Z<6n&
+>}X8<neB9(k&*B0)ECXbG=3^UI^ou>ocXa#!1YP~B4gg~kGGv^DZ{^D1$KCVGkd1&Nb+Y>zgxe_qc!=
+<;H-4Z)bvS^FxROH@3E&p&tO`<~qcedf#uo1ICfXCn(NcjNTqu(j@oC2x??e9WV@z@MtD&9$Jx-#E*&
+oS#HcyaXNp(qV5OT!hbGm2<HaODCdwd+hb~35CFXB#_Px6|OqWT=c`n6=%rxOIs2H@J^^ewI~d<Je1u
+$M3=|gCt!&3rFnfxt_oXj{(Y?}l<7P{-8@meCX~I$vJM!|e5A^*NJ_3*UO2;Y2At@M+T$nM2@96vlIG
+y!Rw(IJE1uaYKf2i5UZnOi;1?2l#ln_X#-Yc=M78v;4_(_7d?Cjf7@hTm78{iBB^z@)x2@EfA|Bm@1x
++?Q0`bBn(4%`wEwiBv`>1Y-%HV6bWZhgj_cs=?Ild8{=M&4y(oa<xW(6*ica+AI_zX8`8*QQ8Eu+*M@
+!ZZ$kRC4NWXj$cdKS4KhRs2iRioZcvlqj~`LchKg7`{VsxQjWdwK>cT%c^8@G14SdagFtw&FF*$22*(
+onMHW#mdphc6`po4ijqCR-A&#>#<lFOH{n+aF3%<KDSoQ2`Zh`$hq3!Fs{g|Y)1D+;MK^&F`a!HsO_X
+j-mj0!b2B!PF7iGtoa`~uVW`^a8#5)aax=+jsveIMX3ZkMTyv-@B|mq(9wuZXCe&DGA?NGVQQik+C2Y
+lukue)6;PWXF`~kgHYU;;y3y*|_<8e&fb+$1}ZNn6g3w|jK#;})^a0xTl2_w#1%Wu1SCqO<LMrEJt%q
+t>}h}7)S@LDn=?p_Z|OjgM4u20e(xggzvcTh)W{K~N>Y<m%steiJn$T|>(b(O`VnRkvV<|$sQWSg=<J
+F2cn!+t;ck%k2f-tmgAP3)erCq~cxsV6OrxIvm=6sNM;+PAJaWQnVexFIj^-|Rek-ttP=^4{x&xwFD+
+w;M8tl$&BF7N>+4Wsmx+hPYElXUq3=;6vg1XT+i${*AB9FQF~FeQ^^)L2p=97xen!$8sAhph^7Gtv(>
+*|N6bZqv7A$^AB3eI09fW3UDw^U^GcmG>R;r7c|VEIR2?DWRd~i6o!GeF2R7$po5!%8KZ2SXaZh`F!+
+FGKO6X5kS76y=5>|=>h&34d&CK#N|UQOBOr#YGo-|-bb+H_2|_H#`B}k<1PB_4J(7T7O|Re@rU1rWpe
+?=1AtU5pD0r1<K7$`Em^fO6C$e?yb6I$%fD|Nwf%U1kmNxbm2Kh^j|Lzpi6||>_{W}6S4sFk}Cd(^KY
+OD5W1;X%YCp!J;%43P{OA*-Uy+kb{mhetXUXb4d$R(;`dOy|q*N!X%O4YLW*^2sb3iMeO)U}2Cjr6QJ
+iu7%L=qDTGw)<603e0g%nq$s}V9swc6z&%_DHVaf$Vs6ekni$UiHXMb?+pUS6SXg-a7zf6Km}$zEfgs
+sTlkT}_SuRi`YIAk%e8XtOf>I|bM~br(C=<wb36MtXxPvvb_Q%gJPcaX>4G`;J5{xYc}`9A@Yra&gg&
+aGm$ugacDUAPb%yR*<(r#MOuc5iHN<oyNTd3Cs9l*6*%l_@p}mln`K%AP;11GmKF18f7A=ot`@Y`i&_
+nmOkvEyzZ6659Wsixc4cquTce4ch1(Ut<)MxPdD4s3tF>@og^BbmWwQXQ<{DQxrpi(pST9=ml7{d}D=
+N;2*&+{RVx)&}yo;@C0Wp)#-pfQ7px~PF|JEwb?_Zt`AG<%57(%ry072_WcNU&!Vs<S&%(67gyoAg@8
+m>X>o=sZcz*>-O)HR?99NIh)b3?CbYfe2%tPRIj|pWABoZe6OX(xYb^LwnXyxTFG69~DRU9hWT+kqL^
+E<GUa;^yhsKo$88MTE16euQo8Kc5eq7-;p9Rs@Iw4DD#VTfvPmx5;T`T=U!Ek<xf~t-jDs^&YB?bK%B
+lj4~N!j-mDE6R4W<ygfP`=JcZCxfa*m6H@o*HV~4&q8KL*K<eTKPReiY|9}7TxnlR2RcPeNYzL{(+xm
+nx+MO1gvzC3&oXdG&93pnt3c?t(-MqG1RmVJ1{!Oy`1Wb?11=O7bkJV7hkQ?$VnHRi)vtjm@&`Lz4^R
+oU{b`^c2b=`_dr=c<;Kem<Wcc${<moNt3z%q};ZENw{EJB*t<<Z!^{G`RHc%T37ob`PFoH55h;kKLAe
+o*oe_y|M%=<1_m3{fDH)g`r+<OGaEZ51VjW?>ru4{Em&D=7fD0?)B(E!`}WxotbRB7eQRypTjZnvCgJ
+Wansl6Cg?00iXO0UaLm?MrAU4ZQ^}(fJvUgsXR>=MA^UWwoKv-T`RY)l2S?oR!uW1D9?Ow^9eGfZ`^n
+X6PlO{)0xOgu)37*i&KXJ%4{y4KAw1la_^O8;(opqrXUs}kihURn0m)`6l)4wc*Hy{5I&Bs1)x!ec@I
+;YM>79EeS<H-h<hlWW@}Eh_CqX4$F|;%LIXu_Te;TJ?{}7VE{vb~Ir}qAvI0gGPPC*EmBv6b%5FDW>1
+Snfl6pErSfx$2eQ_QDU(F6(n7oup@h+Zd?m*9RT0lf^!I*_13N3K#>%bN7(@|Me{fC;x+M=Tg;fe*km
+5ntV&S93%fUp*b+Rhc3Jz=4cF)9d^*4@Lk*;B~WgVvI@tlC2t+=qgvq#Gow(6RUa!4Jv=<XvIb}nCZ$
+8aCig*SFu1Bm<HXmwJr$g>&r<jSMg_oid`owP$42NMJk$gWBPw7P|^M@P|<z{RA9|<vH!(DW%{!~W%@
+^e%E8GWh)fwvyy7%Z%dD{~D$zW_y0@FRvXx}@F&lS+)n}@<^C9Jq$NHFzYRNolGk#r3%%Ar6qMsPyb>
+OGNc@~<sp_WX$o;8+O(sBNzb(-#15472s&;oFDMMPp>>~2CX9m8Wk9M$H8^7I40aXo3PRC?-si@P**@
+d$PW+{e+}KfOBLPYwzpy(xr!Ub9M()93QG&G%EZ9m>rdw9;L^FIl`z9D5{sxZ%UYV&jEJ8|Ju`EK^0<
+<_bM(Kb4dwKSvB(=IJ~Th7V&VTsb|qPl<#&29HCf@Txv-XqVZl$RYGMyBZCrD>s%1WW|^T3kr8d?DI7
+}2&a}6=y)LP%Wi)(uSA#l`L;^_OM!~pKd19RA5Sc!2T;IfHO$-HcvM+L`0oQM^M5l?nf|{3RP<J;lVc
+*Q;xSZT7oL8W)o^QcsJuDzc)565FpCD7MR%Rih5qW;MYiA$Y+q)|(J074Im?N5vkgCpO7rA4I>|cl(W
+4>f9$cHp?vV^KwSN*r{4A|jjOod`2ma=!%GIzPB>8k?&(T4uIkdlvq#?oEDD5%NCZQaThe?c(&3+S%!
+mv}+t&v<#jE02ca~ChaRgY13SD|~|6635$$yKx1Elin>dO<fi2QkDwtMo`OYCPQB#^gD;#pRS`8IPmr
+*t~j7g*WoNyimI-XrcyY(9`DbwA|Af?<H43_NKo)qbna46qQHUPA6eUHX?h(6;7ibs&Z92{AM$nKN_x
+sJU2w>=Z1<(jFJ$4#^m}#z<0ayncaprW2irlCBEP6x3S89%>jM_SAN|2_jrYb5g5g39EEWdp&1w>P=+
+88hM;i@!zq;f46fh=i0{!lPnQrt2nJhA8Te`dbzYs8iIqVEsQ;f^Mgte;Rn;m=!8F^ldYrBDT<I#dxe
+Ci9tGN`D0;fj&3{<x;<}YXLXLXerXm2MlXdKe3epZYCh7M4EK>*K)A;7o*i2)W5y7nrU{VotCS(}kW2
+6~MEUq#^m1?OZaI4yFu#m;_zA{_&PaQHfrZYYtu!WHU}=DAYp_x$uJT*-bVr1dA@%1&U*Z*b+a6(#Qx
+1!0qPI%wO07~g8V`5xob_Ye<#ALBirxB%bS1eJz2WvcHW%d9St#YJ@C-Q4O8vOoZ{e1R;)KLS~x^_44
+#wZ?yC_Q#*C`0?yC`!w#p%-IRz3%t`m1_I8D+XfAT-TcKFEB_3({Dxhmg{ed+-UtEKqQqqQNa>Cmj+#
+t{4Gc#L^8MA>7PQ-ixRSSdOI$s>z9FY><geOw*dV=_R$Ufs6=I;aGm8`+e7$1$XqND~bkR~(Srv1jGa
+sf)ZblH(3pb*?O-8Z|Z0}OIj3-=f&)2OHaW7%87vriA?-@q@TnM*$?yegz%pOo>sYgjTmaZZQ_9=0ml
+9C}rZu<|K%xA;qI{#32qa7*tG0HbcONE#Z7h#t<PydR|&=^uK0(-{PoM1H`HxhDxrsGRxUF|aH@V05G
+wuyRe<au=K;OLH+t3YFnJRQRaA)C-So7-kIvzi`DJI#XPc5?XYC8$`sq4l|)ZS!p4Myv~0K{wv}UV5c
+1cZxECUn8Iqarr3qtXp3#<gmB1XD^8)*3sqQdzWl5(?I*F_cHV=v9Qwzm*d^OPVK4gZX{<JcVesFq$U
+b(qT<cJF%!@B@Pn6;EXK1fA7K3MW)QCH<+P>k{m7ZGqw!$n2@Md4N}^!5#T&Xgx535Nj;)nT-er4OV*
+M+Y@~!J4^Om2UV;oY*bu*x9ao4k(9=x36_0&OhB;L`YuexMTDPg>KWdaB0me;$Mp4>}$$dKD|AkM@l^
+g7(`r&z@D!rjSz?`wKBKvNnjNmm?1@#IzwZQM4qav%p2q2gEknB-wN^g_6svHbxS=Ii!kYi1d~R8uBK
+_HcBdN0o{`Cy{J>RcRrCX|B86^yOjNhL=jVU88HIea!m%apI3-l)SdExhL@B;Sgo`%ilt%b4nhi8@Ux
+6QM{@t24D9THj+Yj%oC2HJop^bfyp!3=?{lxA$#11q{x18vL>th_Ui~73J9wof5^&f$ov=ZZ#zrANV>
+cQ#Sh{5zjVj%Vfg1e{gL1Esaoag4uMB&RsSu6UZW~tr&2(5`V0esHe^+?gz3*~0tBGPf=ywNz9XyoF|
+o$OWlu|BTz0dB#3cq|Fo?W#0w&`y>M!y6XA*2A*b5SYrbr3{VM~Tu6|`4xW+n!Ej8=0<G6IDHYMr2q8
+4yDkY>-fZAqzAs;dR1q9k!=dmt_j@hJH85cJuO9?z6A5FS}0%*SXhc0SNj&0$H+$q>5fevYUIdH-!HE
+8QUCZ&Eok>E(8LQ@9~Hm`mY8kzI+p1c@He#({o;0^pl<1?-YAT71TA58csdxGfS*8Yt9z|%J*ff0Ohi
+*`t@Z%9|P&1y^K}4=AXF?XvG79`nw}@-#JU(cv3@fb+||e(S?eLrbWXf;@L~>E0vP1tT%24$EaP&E!S
+T54XgG}i0p$V+i6C`%J+whB*s8FkA1;IroKJ))Rp)Bt1Xjb(%yS@Ke^q;uPbY-<mB`s+pX)ccW$FAGv
+8Gs>qk!n&1Xcb#v62#ga{dqSxz|nD?5c+j;pp5#?kW`;a+iK-R7LsA}yIGu4>Tx^;SA}4b4dM+(#v1L
+%KeY6tW<V^cK^zLQtK2S~5dda!qX$-aRV9&hnD%_jt6#F2dnXbm-_o<|}Osw|xSc*QbO;=VQcfe8P~5
+cFCKkokSyGHu)l%#YRr~M|5u(HGNeVW}@bZogJ@g29EK21-ic?G_Eh(cOvgcLCqy>y+*8#AI~L$eVy|
+%=<gCb?uY4-YV)^2Hjx)t83a%^7p0$Q$9$Tp0<6R~vK8K(rY5HDCT@vn#0STtb8&a%;FRWa96eLyscm
+y=<LBM$foK{-5hNSmh6{AxExD?5cgeX9EDVpT{7Eab6l$hD&Bb{>Xlbc8L8;!$ofq3Ddw;1n_fw+Z3o
+(-}5v0zSpxJt)CT<=bdeQ9ZVf5^=IzIRdqVFj9X7qTc(zlS>=2BO;un#lUW1O8jY=uJuk)>4BhWS!YI
+6Rz_r|w=QQNML%l@!mWsqo`oDh0_wB-1Sdj$n9PPNx((yf%3G>^9IVzMb@kEl@_<+$pB=pSpa*Wmjh`
+uF_8xQcy?6&tpqAO8>TGt6p0W0V}m5UU@|8LVSyohOhO-<uJ>tbJ{S<k@LdZ8~;SA%J;2Z=G`xE77pp
+#K=_x1AE!Hhd)2Q~9=}@chxu@FNel@B#%Ka$ixiF{2!+86jo>)L0Ov*;$LUWC-AjhJ1T}Q^0j5{YVjK
+gJ0?t+j=NhFl3VdEtx%Bh-@G}E~%(4YASByXx9mQ8U(KFB{U83Uhe-wkEjwMe8opuV;y@9-Q^pl{r#4
+^yiUne~9)%yvs0&ozLXc9!IB|QX^&p;#;jIaPXR06m}EPuzV#WB4aH7-X*)&cV6oR$z9(O_@s@9K&QR
+}j=L#&`4KNpFqq{E%N##bM;`=%UV}{(L_CD11{RVc*n9?k?JNamhJfEY9mSOZri3Q;1Q-S%>I8#F(8U
+d>QK&*jcO0B|d4GR=ry(F3X(L3ygg0P8eyz_=3LazG$oNOYG^F5XJL%`T6iI?ffc;4760fp5@0{z3q;
+FBD5@dmtUMAB{aQ}G*!>Ws$NP-YSX~`+k-eu=}s=B%$Zt65Gy}=WJg3Hg59B(%M6TtTnC)#3n*8#f`2
+eXE01UXxG7u(n_BL<^JIE+v8zEf&7&0$$}v*dO?f>jE-u&GCsGQJTR;y!^xVXAS6>y%>aOF35s0JAT>
+{#4X3DbL{oQ>%5BZi>!pOG|esuAvB4RicRzqnoVUG;bo`k;>+R0NbfmsqJ^zh<pZF$+WRYab4=GD3PG
+v%f1WyvI#0EW7Hig~CKKd#h3H#dmM4VMpuOe!RDzbViPuFY1-h!Zj$G$KWq{GkokdyI8vzH_z}{Zfbm
+EoD7W1m!f|A#&qJH<p=^(=IgC?<|(0WCSCQ!rO$0^PN$l_&)l2bXW?~gf`d7CAF>euNB~Mx%;9(T^$v
+Y^{ynUcp@$_zq&6xzupIp-axC(fNu%r2g`+Tb!u7u;`f!Rqj}P}h?O+yKm|O?%mfXw!oXH^vJq<ktTf
+p0p{UK-8)Upclg*iBDOTMo$DEM57qMhmzoHg@tH(;i_E8tQdBp~|s|?gExhul?3?Dt|<WW5>>}uX5g$
+qUM0+k0uuue)9mfBYALtV~HX)sRM*kch_RyzT2YnN=0BgOk0@~&w}KxBL<$FBixk9QtQ2bq7IsB8lV#
+OZY}jk%VmE+wzRxSP=tJ(~31a_<+ZxJYM56T)+2Cn39}<S$J)`Uj|<x4?>6Y7e*Vsk`V3-feC-)Ma<=
+R*ZrKS2{_`?+n|DDcJ`ETX;tTx$d08dg60>fQoa#9c3lonjSY?cH8lV=J1}dLz%nD+NtgI3JDDpmD~N
+f%<0V*MGV;s4Rt3C-E7W~QCWd2T%Ngy%de-qIg6yRr-_Cui4-m`PAXAOTxMDIr-IxRXSe&BW__<7W_<
+U9|M833B_9LcAK&b2@cVR=A1nxHjG$l)rD&XBNE)Rn3a4?5{M>b5ooc|>nq;=lV1VxD+DHW?v;-_X<3
+QTv3}mT47bQD`keCr5w#4U^J4S<K4KT$L5VewM%@m`RkcNVGagu=IYVxx&3}#i(PR}6bk{B4>03vCt{
+w5f`U_iro$u5_5mh2IzoJ1gTTcYz4VnKJA0$Cfr>Z6@wFk6B%ApBtrkXea-KacA{`K|vdN_JS@G~ho@
+!2RpN%|8uw#w;$6{OPM8*U`k95^#~%S|2~=wog==^~D)|tp$cc=I_8n!IymJO+E{H*U$PQ&543EYzb-
+j>Od&16OgYD%f23cwJrE(jt&mK4qg8l9qNs?yi6_242$EI)^!4+_~Uec-IS^iUSemoa@3nBF)e3gMd?
+gfXD>!E-dLVZ<m?|r%t^f^yiiEgpMAKWZD>cw<#vwFt?cI_Ov(G!j**bH=a(rn!m~p7aznBdeuW9Okx
+`>rB6wh+<srsAqeUl3Io$J63=izt+ub@sF7F&%MWV(I)MIO8$A^4xX^aus1Jmv-^M+YK#xnDd%aapP<
+q^6sFX)R?-LU!`BOXj_)v}|R<eDCp-oL6;hw$X3e@S|>Gs7@QPWa)8#SG?zuX6JB8ItboMS$CjedOos
+EgfD<q%vGCy$wzo*r7vfc%qI)ab325<W^}K<JnL+Ow7aAWoA!MV7uML{A%jkXsTTP839e`gWfdCD}$a
+5)1Zoz>{YvLChw2JMn-RDx9_pi5P<W9oom+xy%i<#L$_2biSdie&i$>+Xui1-y){g$6#P+E;hNPi5c*
+X-Je<^QM96tR=8hWrT0<SLC@CERF8GWf553hkIiDK)1v^<r6P|Y)wTrAoar=>N7>;(XR8|#UaTzQAj&
+lC~kR5mPNs2a#Rzd!CN%hW0My&<|He(T;LoFF?gNWIZ(1Q7J4MKHe;7;A=7Zm4rJGwl{DOJ9ZiBH@Y6
+c`QB;oJhQ(1ITm_voItELukbdlv~*YcAMkn&JX247g%CSh>R924*(%bF%=e0qvo_O>XYp$HFs9II@w~
+LF>3V<6<X+>KOUBWP|faKew9kqPatLvbc>CVB$`_rH|udNyk~3Oy!a*Hn@mU5=Ha1e?vA&;H7*{wtRe
+f##JQ^moX1>X$RjMc)HXtKRqQT7r>Pd`3J<;teqhyd4V4^tM-HVzK>KxBfLK<V$BziE-%JtEfywy`wQ
+Ry>;L$s=XKnxA8q-5@W}a5!up@z*?Tzr-F1IR4QYlTVVYioB83qM&5$5ZgqJiCCYA*#MZ!2meQF!HPT
+w!lXbEK~3Gzc=$D4xoAfQ*DS4uU#3VJTN-RE5GB~C42Yf0A52r#IiGw?SNgVDhy=0^C+-d^2)f%zW_c
+E5z<C1jrApM*(3ye7eBX#xUdgsxNnDcGLC0oiIv1<5r=qU#{z66`?%l>h-ATb0Ak1PJZKsz8>lLI=w_
+p!IkL`R(t7NofF0D5CtT-6<s{6S`?~qsoAlsG7U)>F!f2K`#54*ty>Pt<1;gV`QQQ61QoeqT-h_A22T
+Egm}USATa`kKk7(ps_&ULTz_hp_oihjA*sBC?6!e3hs~h3G*X;^@aG$?DSeaQ{IE=u@E;{s8Y=(-Fp&
+B_T*+O8Nbw#aT3{!eTT7YXwFuO#59RnS3ieH;dmM|<;|`Ght@`>lG5($E_4l6W-wzmmAB&;S1`KiAJy
+Jaumx7LjG<sJsm~>h+g(upjQ5~K}v52bkwUAf^vZpZSU42kP;yUTB!1&yyqUTY4m}g>}Ue!v*4qPzdN
+1To7<F-Hp`EK2~3;epP9a}(UrA!TG?|4gXl^wN5t$o=zb42>tmfv$V&V>9O4xY`{L76l|?84=J1wHxV
+J%4Ig01095Xvonzof{HKQ+y!$nHhO`E)cyD%IDR1;cme11CAG5TfsyJhKq)3Yz+<alZrqN*A3x0yuD1
+npz6dyZd$P;Oq0LYwLx7s7N^UaunCTO6(H#CVa!#vXY4?@)$tO}cF=iFuA3mmXT-VtaFq5qYt-(MLz?
+?pI$Y0}9<D8z3!j*Z4^=qm+f4|2u;H86TXa6N(2!eF)|l+=MmWqZ=<st+_p@SO&v@asj&y0yV`?UH!`
+YmdoH~M)j}k3ilQi4d+lLGJ3bGL$<7Gb6GUh6O9-IEyF#}VO$I}&UPRy{oyy()b#VjV;{qcS#jxa}NG
+2gJ^!G<~qR;9=_SsR_TF6tW-owLi0;$9ps`rD$SGg6q`DCKXBvMabYrHf)ydR*J@2lS#qX&a!%usNQJ
+B+SOXpRJpnzwqKUP21;_DWXH#$<!TpPM8sHf_glWn2N?_7jlTcD=&~<KK2XNA-ZhV%A11kPEF$9PS1*
+C?wM_a(KMkDf->xpN+>-YvD%5~YV0!YQ%?|i=u4yZs>%6}?NAQa7%J@OhivciMj9oTH~_Ea`dWT5TuH
+^zl%RM1m|>p8aOxG7HD4M}j|X$r-`@eBDIwQ*Id@r&O=Qkb7L2o?dd`vt?Vvz>6Vg<3$Aw)$$kLI!6!
+S^^Ot^o2=tP!}4Z%ff?s)NbkF(4{k7t9byn*ac4o2i_s7u7aO2@F<?%TmO6-(}~ud8ol+@ZIa<|MzgY
+tXfEH}=<%JT=g93@=`H6v}qNBvZ?Im?=+}`MNi9Je*E9I`xqw>N^`DOsgK%O`cce?DpKzEOQs?BeV<d
+y{lMW$G*a|*E42lHf~<!)--yW)p^e`^<KBZzK3TxzzylqDIDrgoEV(;{Egp2`Q?m}6-I6z<Na!vu7Si
+U+=!3cz%xAOYbm0_%{_{5x|UY0c+`VOe5;xZkM44lXHlpkx0L?C@4VQjkHaPoj@9*IJ@SN%PaB!awb4
+39`@SnSSU84!Ld94WRsQqDY;LmNk`VD|_GH8F1ypvFhkBPYb!-IR4oni|NIJ|_MyVy~aHz4g$PPB&sC
+$XH(eiD{!X7RM^{9ra?n`{_?pAAwMna5og_w`aRAiRWDqaa=&yy!MhN#)ol#Ik)(q-i!P-Se_x(#hBl
+RU@OWuLy#Ym$!}&U)qADbwo$R%<C)FjOxq^1(bcB7qSnpDgjpI$kd5ELa<KhU5!U-g<P&59|!{YI7v9
+6tA&LO9O>6s%bcqwo;t-@^cR9ZQk97cB91B5bn4wV>lw@GYNL9RRfF6D8?q$(_BA(NMyuQxN)QPi!kj
+Btn8fD5I9~`LmZp(x-<PP`kKv+02R8qQPlvmEBMwO;VYjvI@v3@7><8}WcIiJ$^HHRblCkbuDEB#U-p
+0dziWV{0L-eRUzBogv->Z0^!I;jqUrDdp8Wmahrj>(FaPq3{kQ+P{p~;6UogNETRzw2l>Ne$P1z1v^U
+D+To9nJ``l_sdVQ<lY{@eZU|9)$uU-pmNqkSwp_;%n04Z6((P{YI~tM9kITeeyDu)md;`?`xC_8@k{y
+}bYJKZd{k$FiICthf7U+2_}@9;O8xYT2#M=JF%u?f?Im`;~zA*H?o5^~EHAec|X|Up)TT7YBcL<!^!?
+9Qo<>zpc+m%eS!XX89N+e|?eI`{&{P1Ndsb?1;a;e^|bz9|jUWMcY{l+8qlvrB}zwga9QYCix46foX#
+*13LOy3_RN+Aj|bB+FlSlzE*rlWL55>05T_Wz*kss@`BaL6sT#zD5zbbKnn{DE+Xk)n4cA;fcggo%2p
+T*wu}hSV1XIXFG|-67)GrXskL!Lt~T7jTbuzLhBO94n#=h|FhKN725M!X2)1^0zy(IXi?$`uhuMC$!s
+NuyO}cs39zMcV8`7+Nl-~L)ioh~T=<mqTW*3Nh2OQugmg|=_bJDwD*0FJfr?R_6LDQ(>?gGVfb5BrjP
+?fMzC98>0;u*`#HS&iAs_u$c$iT0IG{wG{+n6^kkqQAK8=ht)=3tMICrSs+%}49q>Q`XASn*cMo3#Mu
+2(zXbB`(p#p`pzk7MMP!GBd>J<+4Ec2|6YIIo0e}(jo=WrB5*Hl4=H>&<4F8$@LDV>wc%+F=%pqFXF%
+j&7KK*<38Vu1Y1S?^(8^Lg~8>bx&3Ir`wEMaI}$Jcb>Ljn9o~1E+q8i~%c(i<Sw*y@CAQp<vF`zwo8q
+-&hEvdqgMSo<VEF^ST<N%ci3i~+!?`xUXB;z&e{Ea|>AN2BvP}HC4Aj>4^9LVtNqs@U4D5@vvL?W3bL
+uV)xON+5s*~>xk@KTIiU+=*_I}BHH#gg6fFd86!{r-K+m$?Vi`%zvc)2t#X%(r%oaOX)_a%)vq`?jWU
+*2I!Y2LpRX!%lJw!q2n-|!LfhBR2&n^L^&a4r9|&27C;#QT!IaBz&io>5z`_X+g<jbi!4y!=++k$TM|
+wF=F6mV4(c=et~R93vZ*?y3Fu<wI}ZzBFfRP6uRc?oHO#FT3K@chwftbA(Pjh=XKt_Vi_^%O#Cec!QQ
+-*L${gTdq@$g3CP&%;hFg#14HGSz^CV^m3yuf54aPE;m>*7<6p2Gx*Iwf3z%Gw#Z@o;#FffqUVd#lO<
+Otd%S@17$>uNj%f?A&qzno(@tw5-h7IMAx2N?PWgJ)YkkFsGAfdusis9o`&oRF6%U>EkU?23IlS1dVO
+-<G;lQTD^kfdVBH0vz7YP1b1j(U5UR`HNf^e6SB91mRLSMzg83F@3@0xRbXmRzTn-?0<68XA`a&IdV<
+4t+oHWrPG7k@L<6G88ZO_o`PMH<&^stTlP_aoGV+LcJRm*A2<REaqk%0)D+hE)z1Z@(SJp;w-TaZIRP
+jvJ;S@%zSQNB2T&?XleS2dHS>P)v^(-acm0P7@~Ql&Jm!kL38(I>HH|j)HP7xx=+&^8DUz`@;dx*eD-
+#HaArPTFauaI-0_d0wd2;sZ6IWdSUy+_3@Op7qRN1`@XEyA9{As*Wr>clPJF$MERLoD2LFSx;;X=(PF
+!c>bJQE6Y$fFon{!9m)LyEUr)_BKlvN36XFSD9W~t5H(5qm7U_1kXK{iB>JE*QagLlxHn=IHn7+$&S=
+2Fkzq~$(86za_e!8Q}uV_ga{9AHxCgxz8XRX!iYuxyw22qc|GN-g1PZgHkv^+Mh%hU2yR3l%m%Hp`ND
+~}uNw%IRfPm+nRV(ZzsXb;7yIB>>tCmwd0AofmI3a3-e-)iGh+vR2uOwrC<zdztf7->~mB*{2*0bvN6
+Y1|obv{401BRZ(pH#_8tYUx#TM#3ug=rfEpQQ=EfDf~&=iP7a*Z4Q}g^XJmkx--h@8?Ct-FzQ^~0h+?
+eL?6?|tfu=tSN}LX<(7@Woa1+@49Eh(-WRj>zc4h0kQjziACTUk+wvVWeYVvPSmsmH9~zY37ASCrfk4
+<2@_`>ux>67m(6pk~2}EkOv-_FI!>X?U3}jbY;yD4c3sejyB@q<hn~Yw0jwu+^Ofk@6TNb9^Xg`56@H
+(!rAgVM4;AjC#3l0JXv@qa}P-_Q}Tn8Zm2j&bKa5%np>=?j}M9-iyNv(uQ1O_Lx9Qdu*Mr41%=-=(Hx
+d6&kk8k#PpBD)aA8Le#Gm9ytal?CjbIr~p5-^>$gfDl3m)lc&>6JEXRq|3CHhzx>lDI>%fLlicp&!Kz
+*w?o{8w*@zN3C*4V^tazKQ0^X`2!=wsp;>I)c<fK)6?#TlLJo5tj#K>S@0;2Uq%50&f?Rc8Q0;<4{8y
+==W+-AK>t?<nuCKAbKkZv`bt*rQL=D!^al|~iM^=oX$N7Mr(U1(7f)$Pz`{^pIgUP2C(OBE2U@u8vm$
+|S{-t(oMQysxtUtRp+W2EsDW;A#(&_b>lqq28XR96$0n{M$tG8(kzEO|owY^`(DO3GWFvyao$F?$bFW
+hMgZ(-4^lqTMwhTA1~f{M+KbLw%Ba^@coz8)HBm+L!|LLV#+hwywX0=S+J602X4y?-){E<dW??#w=XJ
+DUQhF7T%mq8vo*P=ho!yScD5kAsuk+Ud!@+=#3joQtjXkW!CIP|6+lqtaEp<AnW$oZ8VlaL*vdAYb>U
+bHRw|ZD30a_B&5RC>Z0t2Xn*KkrJ{WUG6?I`k~KcNRK!g`KKqHVUMIIX?zg6d#x<D%aH$yF#^3spHFq
+60HfcNJKQO6UC)vFVK?t5bEiydD%C|yRMGu#gz`l7kl?Zr_;%1=cyd-kyGgIec`}N9kT#REPp>@TUr`
+R7iwLd)1|M}lbWi#=xVIZf*cT>cAC%j2ucpuEohk?=JjU1#Ly|qqNH6bI2G=O6Lm6{l$phOXc^8JY5h
+Go6*gqgHx$6W%pFKSG)ZN%`P}(t$0V;+E^c*&sGS2-KHtT(`V~<W~-|*L|R*oDci$fME15{A%7Ok;RH
+Q5on2$68tB70wN3UhEve4@>exH2O#jzWML(Jl?|9OZ|b-g@n$-DK3z;e>ZNq2_&>c;~J7kPQm975aJJ
+%7uLJ3!bp^Q^`&phoOkE**a>`|ARpW`-g+f?``>ckimWxWIpvnU(!!(&5j8Mc(r8|$a{&^w++Vsf+t@
+TFcZ+4S^~%C-6;fc2Z4MKvJx+{wHdTB6PeW;eTgc|D?VL^(9)HOxa<rCCOAJM<5M8XT{3rs0;x4n(_Y
+#43<F3U%Z|=(;ze((t!zX(1{r-u01c!i>?~pKiy9}odbHCZ!Z54e0Q$R1Lcb0&pOf+byMxU1Ly$o<VF
+@IT2m|TrH|uio<FfzuAfx>QL1u3o2<C))-^qg4M#*=|)v3BxPC}8#1&=2ghr<W^nS~wA?qB6O%%O6y;
+z2xEBQN*UMvf`evhLUOQ965YF6Uys=T7}2gfETmcy3W0iJgzxmQg)Ra=+cqkgi^{y`I1wIX;nm`?~NO
+#=Y%!g%1<@nG?00F7J*8N+3t-Da+4mq`4bf4J6ze@a0trZL~V^rg=ODr2FK3r!dNh-CZQk-39s4GUj{
+xFwU{+zE~}EE=q<9Jo3C+Qxd-718PCVJXuBE-so|Y$9#we#Wb{-(p2#js}`9cRk7Krtf!s!{@`}bsY>
+IL9rvfo*<9da1GTX@mIRA6*3>JcJ5*=OoAhVun>YRzlsTgge?G_<oYPK6{&HcDuv6h>k31*j!QV~A{L
+?{(3rRzpY?l#HJ(b$+&^!1p*oof0yoEV09U(5AsLQFniL-X?suHVYnoruJl3+>3n-OVD1$7b6yOTetJ
+TK5??(Y?9B45K{Go#c1iL?F+C*rcX)=MTFvtrZeJYw>7Z&T>&@XRr8%(TMU+_&=UxlKx*Rz|T+wI<Es
+?E!^ayEzkN&&~UBzH;OKK`0_ia?g9^5XIe?)5bnA;*}c^BXwM1AcDlPA|}2T+tUG_cqrTA*OydM&UVV
+SrryY9Z;YgyOBd^wMNui<Y)ZbGRn~0BTW{9HQM+yMzV4^&6sG+Mr9n)~q0*z9M|_<3TXUBvyW35p30t
+zezN_htY2Izze?=`p-&vMHh(8=;L{9#`;QPBZXJg$0l$ihX-^%t|eYshJ1Sl83ZMOc4{n!8e-##iy|G
+#j!_i*<6hx(({<nMzZL7)UhA`AmeS78((sL!p*6CfqM<Tp$TVjr@y5MdZ-+5u`j1sqhP43v>!1ekh%I
+-{1XmT?iNH-ls;27-6X|2Pfm&=?Fj_vEV6cV2~N0Q)cjdnM?f*}W0#kJsI$EB}A_C%LKsp3mS&V90x|
+Y=e|B26N$Oe4SQDzvNqV1`@L6kV~LMnJ?$GTw1hj{=0O3i2z}d{#r&>ojo&~#bl)HuR_7fYs^pV3y{L
+T%yDbl0rq%n`cUorgC#Wx)YLR58TEJS1W!n2ovg*lDDlsc5C2e?Tn;it_F55McM64pHH5+LqKe|<72=
+PNG!nP^G9OT}I5?0ds4q=u-pZ`YGDvgGWudk%)Kr<(oP5;3*qRDFv!2a|dh`!PU+6chzK)13(fl1cJS
+=yofbw}PcT$HocThQpzLm~jP14qDE%tq<U#^!s41RW~epLY;^IR>VpG+U{Z_@`Vm_AqnxA5pytAK^eH
+7wW(6-4|@W*u=x3kW|yf|M#=%_Dc3IuaZzo&s#=$Az9>OxePlr!;%XIYo7AJJAeE)!XZ}MhL7knUt-e
+d#PN>$#A!2yWQZeCCGNfx&3Ws=LhYeao)DIb1b8Hlay$|lL2v{Wj@(o_ZM+f;sbO)-sLvWg&f=8O2fJ
+)L)U~B^VsK`blBt+!M+~Z{Ss``^DR5wGnI{<G`|n*#jl!&bAZxDvGzN(ke@HHuQ4s_qF!*&tpw9$DGA
+K&77w+=?R{e7nbB=8P`mxM=R@bJ%Tn~jp#<Jt$2LR<vu~vob?V0)Up>ZEo4u7tutV0>5a;@UrpT~02A
+$n*+WfVT>8N!C+HE06kf#&T6v-pGhP%#w7!87V)ADuq(RI*$^u<R<Rk!cm!@rZ0l9DAsx5=RqjrlecG
+qkBybg%=7uf$eI!tQa^4(Bw+$^_~9I3blbJTh0#lXy=%2%3JWW$kI2uc+WA{sJ4&p^KQ{saxgg(H{|S
+eli33Dov@m?9^-h(D(VV@d(jm<i3}DLP>EeaRd7-XI-nbnhzP7W=WZTl53lp=;gt>XCil}M@Gm>bJ`J
+b!3`MoY{i#CcPDc%V$acF1iBv==#pK1=-eY}VG(t}+}-{<ds)6CB4)p*<#QAUsxB!)O`Zt6ns@M7I#0
++whV{OVH+(acREI(hBiD!Pb;G|p-pZOW0DRXS7WSw%o}Ay8nTEb2!Ttb|2%3_e?dXR@OxjmX8BM#J&|
+8Mq$vVgXKE@A$IsV^T`SU99^6uO{PHls3AbQHHbO;9q+qUM8V)gsv2^M;n5*I~vm!P(4rZ(i9->mcjk
+I{5dbqr2w<)+o>MOGp5q>os*K-Bm;W3Mne-l=>#KAo*UUN)R3_j2COJKetZDpwQ2KBX_Zoo!41USFS%
+U_j1c&)qA*@ZD|XTgWxLTGF7ZZGH+-=Iom6p?VBsw*b3@=SaK`j@<di_T#Kx*j~{@Xgoi5yJHk7(u5t
+^OQ+|qs>Q|=Wu<=q;P47_vWFzsl|1Ih=s*pOx!VR$KHp!Rm~T&z8kLmvG%jMp!1q&jqAzZX&CauI46B
+sjsM%}VGk?Glyd*FE<X;8E*5&<~<p|evcOCSq5xJrU*K*)}Q$&WimxDG998C>0>}E}OYT7S1!zDUmNn
+$RiD>T>518r_X{@hr&2?=@lk_%4TcHU<DA_Bub?2(P=^<L=t*3;@q+CoK2xY*BUeb}<p68U@_^OJINo
+SwNt`*b56bxp_j>PZ}4-T}_A3o-E9T0u+kbw{r?XW@FiLsc`spX?051!cA`Q>bii2y%ytGQ(8zg2-B*
+6BK(y@Y=YvNqO4ZO9nW|Hg!KCsS@0&+B`Ak&c4}K%nB>UCpV|Pf<shR`L#$#uffmvsnMxXh4|HSOTcZ
+ZhL?Tgsd>}wqi}d)a32b1e!pp+EyG5If@LlRna2j%6T{7FZMm{2xY`a%$_Q6GQj^M4-AjIT{wUu!S)i
+J&$~xdAeEn=0hVwxy=y%tAhk_q}^n=C#fi5@>rBHl<&G@GT+|}Jafk88U0Y6yuMfNX-0Z#x<0FDA>Y-
+F{({~XJmBLMmUY$HLvd;x|)(I*D2|9I8qIg?<11;zk}HlqQM!&e<3CjJ>EAOgAq0GQ!GUvR--WDH=~0
+v0nIaDovA@C+8n2L@qM;2cb+t2ysFLV8}ce6qEkvg`riNCp(<Nf-z<$MNr$I1G@XmtWnvWR*QhQpfV2
+J97;mERCAo@jH*(y*L)DcD@TYic^W9#wG6q`sOMfCh0p1#&fr3jvmhm&{v1u3b=HcFMU=QWc6lYuQ@+
+1`*?J4aOj^oy6Nyd^jqbNABFvr`4Ld+&b=D<<t4o59#@YLdI+`;PI$OE^`>%fYFL=^CHK(6Eku-#8k6
+VF+jC3w*INTC6?_x+%J#XLo@91bl-rp#A$PLP5{-9PwY=SsYsDJv=26EFAJeG=m+`qrl!v{@hq1xD?1
+4QG4$aPbAY@p%8Hg0J(i1*AJ~|AF*{Rb0`miU~8`BEegs+2qNAbKM30BQ}Vb=D#KwNnK*0}xh{)rK?H
+X(zhHWmDeTR6U&XMf}sM}XCQzIStw>f5ByB|+$1qPIPEZ(Q(c#D_!Grj>esNX7VscD-ui54~yFUK}L~
+3%n;T8VB>yT@D)t$5@t){Hdzo%WFPXd!^kgJJ;R@*max-nEuM#{Iq=Wu6pw;(VOI(=nb^eJPlr3Fn?p
+m$Jben{h{6~Jj303y{mttda;3vX$KvufZixUN_0$99<9mI>H(Q|2F<`>=4MP)-Q#*>Oxre{=w-^ugWk
+cUT;MP-PFZq-w9ce$89#RL(`8<iQ{!Rn?b0e~1kX#hq`K4i{_+%5LN}zwLwkiI;sh=fntmSQF@$hDe{
+G~X%9meaLx%8c!P`Fl3jQDF-XzOWZQB;C(~494e<)svJh@~10y#)R5{W=iBS#>Vkc(bK6QTi8idt0Ph
+<?T1n429>%#Zsy@BJ6whr64ZgkVc1?YZV!bInyY8*FX0qL_3+L~NZfizhP)^}}wZwGHZ}S^cDTw2o{N
+tk}GaxN~GpkU~^_h}>PXCJ>KygW!}B8TM@0t|pq_%-g+=rcx9fQoD;+LtO1g<g&g=g)}<P8*SHE;#BL
+$I6mZqk=0Rv5szzGq-SP7JNMiTI66%V;iB&-Hw;{fm#%w+>*>*}sEwsq{u$j$zu)}0-|OPrwtjf~;rl
+kn&kp!#X#BYIn_?=CvnT`D{im}UacnWU$6-;F<lx>3O=1?sSG4pO0y0f6_0q*x=5uDh?9^loZDFvbGa
+n!TheIGqLReU37iSnOTBbCT3%N((73iP2&!jjMd2s~nKZLy@ENt-)X{Z9ra99RLmu^TnmXTasW|(Ds6
+#);x;{}_d#le<anm8iZy8+;W<6wp${I$iK3lq8}{*DH+?HK&$l~w-xNv{$D_T0l4IW_EFR7{jQ*e7F>
+XWn{mpBF1=4ReL_kd6*!elcB(VG{+8z;9p@`^|Oj?Z%JW${(-z{RNTVx#C~--Xq^N-#hy(UdKe*(MmT
+f?9Pf-m^-M<lipfXThG#cwJvUq%-H%FtFL2$=mp<S{_cSHhcGI$qIk`55BOGpFI-}cKk<p3=v<qKxe`
+X}$k~csOv*XKyUXlqA&n$EmB^Ber;kA}od|sh0qfZ}nJ1f9qo|$}L9NOsNw$)}SOnNqGJeEaBT6nc6P
+|+u;;ySG*lr!SNru{EA~CmPIX&<i+m%;=-#$~N%1ezZ#jJZgDxSNEj(WTldk`xU=SD<S?S`lI37aXY8
+JvT2!wyY64|P48`EDYHr-ha3Q{?f{7*@6)7TtE=)Aj09KD7YN_sGE5IWC4P9pmOb!PeA6m+!>-sBcq(
+oty28y6xV3@8emRzMox)d^Nl<TXuchkfCfX7pyB=s@-<T2*TZZVjGL2$qbL~#K>PUJ>*-e*M6aTZ!B;
+99m@+ceQNB6;5f{_=8eX;@&RG!$URK*&-FWUi!tq7HO*Rzo_Kb3y4?|LQv!=0cMxDBhr^aqhgC||<cq
+NAo-RFt3l5gkJw!4#&b}hOP<O0*e@oCcQQdUs#uL@Kp>poJA2g)TrCYya1(#I_@If^WYZcY_%eZ#c{G
+Q+;c%FD&<HR(-C^9|=${G+N2X*h#i_!Aog1k2T`l<WI`dIJI_w&xdx}9Gr+H`VR!RCZEAkEO~gKICiY
+1SQzM@?S$PxYWao%<@=AyI$M=!@cBf{Dw{E1xsP4bvY0z`wjMc2&BH^1}6Sm91>7)%LS5&3rDiFZH&w
+PDP2}8^>hy)8T-UR*>0Zx%Ii`LSC-?O+O#*Z0@4oHb0z|>uH0N4?xYRx}N0Ul<yq=$0mG)x3o?FQ13(
+9-0Jk!K?1=)9@j5DeBbko-+aW!B*U+7|Ks2O_~hZ=bj^S22*adH_xO^p4c|1s<YHvb!9+63{EH!B!4x
+n6utg`IE0N*YlyI>W%a-yf21}k`YX{+ewpflsH4X!-o9vs%8373cpf?Qp6QBWvg<NcCm;_p#0{8$Y1(
+--JxCL!SDTrRwA}$7K5-uHofbXOkti;AK)G1RLEZ`;_Y#xNJHyMOCG=XsW%l3E}THinV{siN5`HuA6m
+cKno-`SkmwKDo1b%jH;ES!TDn0@5AeIk7|4Sh29=2;jc^{qD!iEv8#pdK@HC|_FP0nHG;h-tsQ&5(`n
+XSqI1!x~5R-EU^vZ~e1SM728ywxC5saE)uKgN~kt<&VmnHh3qGm=J3M6wyKh<?9etd*Sc2$v<!+Ph?s
+{O8TS{_2q(-VPNCtKF;{95cG#<uNk{;^E|K$-EESh^q;Hg@r;GWR!^x(NH>ejuB$DQQFeu)vA%YN(Cw
+{zBNp>r8y?wou}zunZkg9xaxw4oFb%|!*^9^MEJjs)u8){XQHb8u&KNiQ(T%!`+MMrFRR~Jl-UpAhP~
+~_o&C4}9Wm^wR?P0%<8%l65qP9L<O^b_1L^)7#sd%*K<Jww0rWMD_<&hYoh%nQ(z8>nw%V2}8WS-933
+)pYN%*O_C#YteVyA&N_dUBcEN~u@9ip8EiU^h=ZO?;vWRyW`6Z?F3{qF8C`i07?<vmMKwwkOImvg+1d
+@)}t`HziepC|f%C*7(sTpBlOvecSnNoxHZKkA!AiWgifo<1<7{6RFmem^hzs6Vo5PH*R7HiawsU*1?d
++Z}}^D4}C4F-%9+tzS8IVww<pvL)6#Z`r1FnZ`J7iLA>_|>AE^dPw+j`rUz*_9K;UX@Bsf>6uhDSh8h
+)F;7qzcEiE7pQq$h?M3vU{PI#6~_~57c#<X~+ZZfTDcKM5|o!zY!9dBH(vG$&C5D?;&?m;adjjoHVs6
+36*zG)h+1zf(C@>yP~DZzIcUIga$+z#<ZKGxA4-RroU814#5x7Yd%n2ml;M0q>ao03SYaNv|o7#MHL<
+YPH2gD+j~tD9WN#%qnP(^!qy-mbrcUnwH(<>oxaD{`jSqu8#*U|3zY!K&Nn=e2k^<q_6DSkD;y*0Pd;
+G~?P!z$<$uDmz@+B_d#Vg?S~L`K4x;YTU$X_}H{^BcI3_%^4P}XIm;aRd`%=!nw%!3BQw+*lrH|(cC6
+Gq`9~Ej@)fkKC3&dLus22GDeRsuKD|FR5iW7*=!!0OC2_^pY@{yBvN6g(8uxmEnd3b|6Bau|G-g{{o~
+tv{_PLt-uw~1J$z1+KY9RR_uKXL9mo3r<NEJV`?s$BZ9_hbLXOA4F=;GClLSU^EQzudj&dABu`ES>K9
+eM(V1^Dh<Iqr)ilLBGQ7o*>GAPWs;VC3z2?;&c(9f;fPy#mMq{(tf1Z7hp3WZcOw~Q|1Aftq{h;fLL1
+P+sq<lEri<Y$s65?bHD86)^&BbR(J!+9(3f=7ZFB8?$zV-%Fih9L|cz&GHz0G!5X*!LeN@O$Vyhrxs)
+9YaknAdShdB~NQOlcW#-Y{(C%{wmb3^wbI5B?a42e$;+2_itg_kbu<EcjZRcn3R$~y&HW?r{o%8?`|>
+I-hYwP!QqK7yPJa;Vo`2w!1N?Q$K)RlhTR=%MCiHy(b@ih?rRuCe*o$&l0w0)^x^*9S-3mA*=w8mN!v
+W&M9<Xzd1pVewDvDuP4WCia7BU>j!QTHhkK4e7;xNzqtAP%!l0@*5AtPqNB&Gty3SY7zXi~*1s?b2+b
+s&4s5bh=5q10$8qECNe^ogGuXGTF9}b3nBv#G6boIl8fDSw}2d~dnSGhjYDp;OIk$2|1`});ZgjWdXF
+5jy6p~dw_3~~{K<ZRs|V)IBeumbQZYsUQ7(yniEqx<=Ct~b8s?9T`Anjouw*cIgKhRIDL+{<U6lT*Dt
+k(EIEfUlI>wV)2N{knu2zvZV*EUNAecR;*t*nEAek)jQ+mrSBD($3>8cT^woTBscNm3A*#BPzo#8BNv
+dEuY9SrC-7@mM-cm@!7)0ANdWT#=?jt8hu$iVe(Sx>>Ou)(%P^S9I{jCeJ!5yU03+{6e$FgnHw{Gj)b
+V&#dtZcJ!Df)t<RfR=jyJqbhW<9W!g<d66>gP1{|hd??k3=EC8dOU*?IMM+%Q7mM+}bbmDi2RB!v_>a
+EX2x;j>saGG&{WA5>da!<N_zoBopYu6aek$D(fSy>Cu=~=d#$>pac0}&Tez-sU*{!3}sPjjOn{y@HIf
+j2Dbb*Bp_Hxg|>HTbMVSBYF38ryxJ4H3sL*$@y8b(3{Gx_5y|Zx`-HlWm=51KzDp=luG3tdc&|UmS?+
+w5Lg*<)+xOp5q)6_PXCY0y3Xyf#q3w676}viPXpQJ`#9Q#9T&|cW=XtRC=|G?e>x$Wu5FZJpv&v6G~c
+gl`3<OR0F}alaH?od9V57K_IWsts4>X;3P^g5qmE<KS7v|>x^I;=d(NoVGL+^XlioWs1m|^1y`Qb;wq
+HF#+_OANK5=ZVI`9|?(!nGD}I0MUxJ_d70x!fbV0SYq&z|=Fw;M2h`nhIk?l4|f@r%nu`9iFqdteOpX
+ibY8>I4jif2hlpn;4;hEcM=Dl5+7+w=1oinxljkLp6kax+mg$zrQ}UhEpxlDppy3%Vj4yYvGX-yOCA5
+JKnfw|oD$?=hJ9<C{BvHw5^feXV!6|BLH<4eksE0Gz;4n!#C|A~85&jHWRf#~{8_(596{Kb_zKNHv#0
+wJ~6%&^?ue`A{5(s7$0w+w7Z{4YvSg_IcCHGCeGc7sELM+ONioWqF9fUf?MD7r{aWRz||)FE|o*>7+0
+VjNv~4_mG6`x^NZ;3NyzT3v;_5>R_<N3}`W4WodX9_{%QBEjS7_f=~{w0sDV5C}xKGE*9ny;LukL#vd
+>k_+@%myueL6|48qe`)sVw`&xF`irbXfxu%)3TLL1P=K$Qx!_hP)eins7`c6%}0K3CGAHc3zcmJHAf&
+FF^w<~3Un*KZc9=59SSiHdvDb4|S6$xQocYg!e;24k(U{i_rs|g^Kzjpq4L+Q%m8Merh7yK$e==}u6(
+Ga_lFZ_N!NFX+F@|Q86sJ*j@xa99L4en`{T|+S56AcDjHS+bczfyhe<QKbuH^lD!O8~KnLL};`{XT9%
+eipcZxaDIk2v}S6{u+Ia+t8H=fwn~ETYT~|z?LZUz6SAmE>XDaD_c^Qc~O`AoR{yN7w5O$y=97>>e=d
+2glj}dqwGFzj*^+FyrP2baAWW6ZDp1>d3|3$U$MHvWMX|Io+;TVZ2iP<MSh(NPP^6po<<(wqhB%4IW~
+mbWvgS@_7Jui-i<vwt)3T(UAOx5(5%&~cE2=Next!bem3j<GYwAN<-Ea5w~$yV>Ve*MExkpgV{VZV_;
+uyJ{^hyLFggoOehhMb;`aneDv9RNSuM5)j`PLsbw2G?vSM95Ads!`gij~!QlD&(!Zp^XHGg%)VZHY64
+in5HIEi7yF;?-3+r_tY?rGC$*0H@!V)qF-2gJ5%P-lBG_ZzIgQ}`v8hr~a=s26`t%aWE)yZmpsfBt+!
+BtF@Aza>Xtw;V2U+WsM3hD!@>S$`0*|6~yOuUX5lLE>8j2%G_NB2@5FG|gf-$FV3yL2#gP25>)s8zjS
+gN&&&nibZf<1lV8M@LwDWnG*?JMPXBAwir8*5zLgrhM3Q-OEV1a)Ng~T!O37+mw?{f&{l2fbOGm}-a1
+$)$Oz*QrdR0*oc<>$0YxZ_Wh(VHHamfQ6f6duJ&R$P4-V4CmOjn|ibP;dF@~0F;Qu!_HgI|NO>#^@V1
+hu#z=IR;bmCu*rQAXaIDL$zG<{w@cW8K{8yl^=V7FVR=IUn@u=!qb`fH@P8{!92WXLD%1d3;I@bfi(3
+37N29x)DX{{&(n!hlUa|HlyXu2B9xh?(CY2A~OQEZFvzEk+gxIG93)E^vQB%=bd$Pay`b8u@@2J0Cai
+5HoQ<verd24S#lLUA7)ceVvz4d_Th`z$<@>O<zM3@=vhoD`=XLzmwE%8}_z44iT;lNvgHc?UvL?9=EG
+Od-W{s-|EsXLrIwq(*8mn_fv4Ir>EzY4}Wk=obQh4&Un`CYZQ;WovbC)e$66&FkfOuzg#UvQkAvb72G
+-E?}{hx^nEqb+4gMBWH?;5iOd&!G(H<!^IC~3p*@A@E<+-0MTg|}a&w29-JmId-bFUy+z$k|&Ma2$#4
+sXvR@lff=SJnZ6$5+A%af@_K5p9_!d)7l5tYq(GvH}|UAvCEx&jQXDC72`whaB8_;IrqSGsgOXI4lU6
+k0s}Rm6ABtHEt#E-E$ded8<@k7;zgL7flYIwHc&9B(XFzihR~GrRb=gEV7~eD^J-4XAK`2yVNvI#tNw
+-TU<u9R3{`M&5C_@m|lXD#lFetXCzuO~kI`*D3D(IHGp<r^&&CNAQh%ZTQ;0#6HCd()DRl^$EjBVz^g
+TG~6sj%cjCd#h!bD@1NT--SR{u(KEZ<DY}Q;#ZKyN&pZ<1!+5!!1#sZ-%8t+0c$meI9nhIKW-BuN%Q}
+2@*XC6Y?QoW!b#zFvs7E@s+y$(*xe0e41Z|RS9w(ot%=r{4ERj$g<GZ9(Micm)-1%{xyaE)rPc~j^p;
+|C8vOTGmUQUk((~4{p#GCQ3;rQrTudG+z9||$n9=vdKj-54z%wu{yAAU2~4DG#ehRh74W`^}44Lm<Fw
+hz{d9aUh2b6ls*+7~dr^@n8ixr!Q8HXd)cIJwlr+@9Kix6+FgfRA%x@oKH3`;x`@kZygN|DTgq#{VL%
+%lsFlmH)G@|4-7&-!M-Wg|_E3;GP7*Vkk{9ERKOF0%aKrV>pt=IGiLtizxs>q?XQCXyckdV^<=9rBuK
+g0r$(6#BPd0Ef51IgM2P5N?^RelCX6autE?opl_AhcnJkk*k=oa4h-5ta2(WsVvD0x^fRAVxTFYLv;q
+oCEQYZmx`>0AFYXWU!6{TiF&xwoWs3m>w#>Z(cX1I^%3_NfI~-j}FVn9u$alfLH2q8F*}#~h{FohSf_
+)mb`8!VzOtX<d94;|ZIRD5z>ra{I-$*ILj56{irA+*jlrqdHBma?<GR!C=={se6ODPkEBad&@<&Tsyh
+-PGfa6hJ$zXT=!Jf#eg9|5%VAE%T*!lHkgQikCYV!%<~0<Q|Mr)nD{2fIoIyB7qWzwo%u3xYS%M^sWy
+$7^%wwLNUN<hIiOy5TH0-XZoKV_d1Fqbuw3tzbS{_PQg8t-UL?lWN4<i>zmxJC5_i3biA}ljLzLp69c
+Rm;ElhBHd=!MdB-?IZV2QI%}gBO9N-zbZgHSX-@w|n)S!Qi{^KO7X>%lyNTsy?X`WPQkabb;M6PGJ-Y
+^fR}oQsd+y-D5|78cp_-zh+MX9icQK)N{qP7cdO2#8=2*}2Wmlu<ue<g<m(i|L9pq^;^(`4EZMV%IQy
+!<OrtNcmJY2(}zF93d^b#o@9Z^{oH)9js(8%lB-V*A=Md(3b2B7txTRZc`G-j3e`0Wus1%9>tZ9H+o7
+P$;h`g7>+U%6+49sT_gf0D+JbG+@&`?MSHP4y>N{O%FI3pM|XE$tnJ{^qj2!68`2q!>7>7-dO{#W;XM
+G{I0fO_C^qF*wbB?l%r0Ct3UjF-QQ(Wbt(%p*#qtD={eh!N_GK74<WLE3qg~0b?a7s5Js$!QilC6`Tz
+GA5t(i32p>{f@ZKV$wW|s3iF#k)g2KKuK<2vOA-~1SYF0uB`I83x_AK5ONunfpr8n#6fB1>&JBR!GK=
+dFKrir@Lt(2$v}kGpfI+LqUvt|Y3S4RR1BXnZkpoc$WKEW&W~k(%QM4blNME?^;C#YuXWsq5qAOJWT$
+ia+9~AD-ff+kQo&pbco_-l$^}ZJ$g0n?{t+Fwk`*?Vl3eED@v~77D^7g<l%=rQ#B^ct1P`vN8DV&9+E
+gGaDswe8}&Q$rudB|5+|7K$F-*P<vp78#J?{c5;-7{hcr+318aBKG!z75lQr=yeh#i#&+xAN5s`aaVU
+(PiqIXbD(SI~_`%{6Kp9tJ<;h<8R^}?2x}gyZ1Kb1?;e|um0zh>5u6uzu~$5DF;SABf%3c6X!-2_WSO
+FO<uIdim()o605~Sm852^?`i6q2M2vt5UzKtA_Al;8@eI7h4TsJJ0tI6`mBX#)z#*TZ7Wl|9rDAf!nT
+vBWm``@8kbFxx1NS*IBjGK>*<Ojqi5mhu9{Li9E~)ke4J;*7WJG_oNsjzH|fX|X(i9;L7vIMFxA^iLv
+}j`=dwg3dv9If+s%0tb_9D)D9x3PN~X1&(uZ-oOHRIy#*MwD&gQ)CDO2sLQc3p6S}?5Cce?Y{!S@?h=
+QG3HnEu)Bnn^o9>X0h(;OPVmr}z1wjm&s^T1MrDy)H%N(LtIG=ki8|i|X^#yXzRB-c+a(<-KC7JB}XP
+bY6JpujQZfPhQ|%IlQZ*kRRkxKTPA2RYOHpQEXMHM-)cc5WpMxOe@J<X-C9!gtMWXJkh&$zL#8gc=8B
+JzAyxK6h>d!j-<pFcUjTbS9E%+jgB8e_IS+I=41<bi=<Q48{)~+@d8aWp5DD+%X%{GZp%AwUf#Yn99q
+EPF+L3e&h`O*t>3k2$op89Wc_&9a+;l{ojdk-&A9m*8pN9V*;e*t?3X>1B+~N7dZ#7~wXq1FoqFeZ<)
+oa5U%LwH!TZ8r->X&ql1e#E4)}4dhbwM8JgYsYY_oZ;kTx|ZJr0Gd8-!A$iP%o9y>Om<wkI6{3o04xs
+)B#luxBqHd*LMIMr6#>F+O7c>M$eZOWSOu+hc9HwKbTLoCy1v-gDMaaZFfbe&<Zbb(tqh+^8E|=nV6Y
+KkW14EH#aQuMlC<Ml2Z;%AZHf*^c=tIFt9)dU&eOVMdcKF-f{h$FuY}hUMxwssSL-%>xfjkJB{^xmxB
+Si*a|qzu6@r%=x*Gvj+G5q)-a#VoqtV8itPzYiT`Vo}Qz_AOyM^XIrw~90-IlaM90XlB!ncT6~11`8P
+8EAHl)9U(>OVu9x_C7%TY?0vP5$99#V_xXvE}t$(n_ucI!Sz(@j4JR@0*A}ExmX^Oxwj-@CR15p@FqA
+2yLStAj`Q7Gx+pq0I`|8SX#mMvNT3`RfLLZ)&2U*zW%1#nNY3{n9h5Su~gDguK68=Jt+A})b3R=g;}@
+dSE4#@xS%42rynpUwZIxy34E8OR31rU)hw;$>JiM!@<9h_0ZPjD`gUaBXr?v(bw+3BkcM1{}>zE{bvr
+W*+cmh7DK*oQ(ZzwRZvQ&g1hV>e`XgN+-vIo^yII>G#S{+x8Ro|N4G-=HHFDV8Dg^lZXqj<&Q`W`CAd
+!=`@<-mxybgz7E?2DOxPR_!&@V%&(`1|N5%B@E5@Z0;7o~ewja7rr=m@>@5QOGG2SmN5hfHxAEF1$}8
+VqK=}G0|B)B)>!<LKUO@QzA^(vV@XK%EpTB_c^+VoYz@+3&A&<Yj({uq&^~g<=*Yo{_=k1$&d$6bOQP
+m^ao%coBY;<m&Yc-;t?wrE!cdMGe>HAu`csJTL!cbKxcUwEDFob)%>^oVSuE#(G!QU-6RCR8ysPG_$6
+v=j5SQRgLJ?!y2$Dn%MITjYwl{yV_$`c#vGOp_~kMuDQ*`GRjeX940OkdxQK5T8(cyp9}N679jo_n(p
+7)m_4mcN1K-9><(h9!y+<q<^N+MiQR%u~5KJL)}bOP^{+cEyLCrK{q^s5UunEWboT%|c`8KJ31PBMA)
+Q&r1mZHph(!=1Apk(Tlo;-gnuNo#7!>M)Z%GsXCk5lw?AJpsS@K6@%%KZ?hvgZ>T5HQ2jy@^4eGKWQq
+kMLT)p5f48j4=G&ygq+@I*g?KcZeZcO`=rDUVSMfv?$%Uwy^wWfjfX&F+68L?)d59?1CI}zQfVuTcbM
+>}9Yy0VS%hcDSND0j%h9ggzC~ZIQ-GjTR+Yaqz{%=4!fU?~^UPd+CUVr?zH!xD~*W|zdq5Qx9M;G4y{
+r_qI{r~EQfB!#kFbDB|_%`bnV06AYpt^*~zyIGAIL)Vp_W%AJO#fG3`yG1!&UL>{v{NLB!||dRjeRQR
+iYb^o2bh;-Fl7#bY_U6l%55BKr~pg?U?O6e?_k-_b=ovkX91L?7OcdUQ=;UO1n1!3G-$f8RPGZL<{7}
+%I0jB;;C^OJ3E&nwB!{s1He80HWhlgH3WeuNQK%|U@C8y+=z@%KP+kBv*cdDbz!~2x)RxmUToZ@GHZT
+CPBn^}N+%Nr)Wd$ZOh7T#1YM<nm;(7BkF(+U>x39T4U_Kk&b!6)c6_SN<)Xke)`Dfu86Xw?id%)=d-I
+k}p$J^Y-19Vr)1oB}}HIBWjD*ebj=8?wGq&*bT?b?K%QjN^M(Z%A^qBZhfwcak?m2V|*ejHn;c2P&=%
+C}(xN}t;N{oM!C!nrmOtK4cm3rT*sYV`#f^XY94!f8B6`t;UI3~z((%wKK;$cQ|?8;)Qrr|G?k6wXEv
+<qa07${3vYs9~K(d3^8EnIQNXNQ8suCwDdAz;OBk_THkiyIMum9_W}obcq&8caNe%{8|LYVGdvL>AdC
+2k<S$l73?~j-wi=`U%mQp_5paB`SyzoC!)*A5UJ)vmm{S*|9I73)!>nD@(-ew#tgNKEJwO$PI;w-9xV
+k$E9XAot`#Xf!Nz+ZU-x6F8(PUpje}ejHq4Z+8)o5Hh#7vHxXoaMf%uSK(%M~{q)GUgx=Ww#xYi#2?9
+&kw@6|}R#&gol2p{m}S|-QmnJG5-@y2*2d*78C13#{mhqP{YC5O=<p4eED$V9FehN;`}l|~3u!?hIR^
+4K*=D%T85p~0AzyA#uz#;Wy23^8nO)=xhZRlB?pf!kOlvsL-Nc%ARhC!0;Rr6T$+8MAp#?S1c^n3zgd
+W=OWNS7_ncd#xDN@|2ZYD_l_Do|%q5=o)|IM>ZmT&ebLy5jI`3Itd9cIoj@V#crvm^YmlC;*Yj@i+B|
+_mTfP)I|>4unYX{L!HW_PVl#m5d@jqsqlcT1k6X1C@qRB}M}e77YurFi+)BbI|L2AHZMSBM(R&6Xdos
+m*6l7fp(}1d3;i6at5)CS)Zn15X(Sj)+PP=u7Wg>gGalOiYeaCs)q^UINJ7tq+kIUWZLccqlg4ijj8W
+)k3$X?@}UTj+Bb+T5*&N#@Gu@6h8Ida?N`bc{p&03|c0!*>@&xmwOLzhM@AhWGCi%0|(>$$k;&HY_+q
+~kzf>v>R<oFO(=P3Di`TAZSLu_OA`!*a`eV!Op!P5bPyVP{-H`dt|fjYn;>Tet^%o*kG{Av?DLHKv*H
+cAG>)AGL#3L)Zvu%hBO!x372M%m>;2-s8x*RLSkorQA)+EHO-{gTJpLvrht#KY5Rxu?<K6sXA;h_;YK
+OiNggJ^Sj;30GaJB47)#)v;kBX{C^E^NqYNUV*Tks-$DF;=tS>${p*u_qp?HbAM8`>XlPbR!2&?WLZK
+U+S(Gf6Md;#ENy46Dz<@~{;w1Zd!5ctexcECW5OSe-EoPv;8BXhFppQkom?{C*3IQ^L(48zi82j0%C0
+MSA7c&hKt_R>S5y9#O89{)BEzcwj831BK7fgC-zDAcJODXJqj)+Cfw-|8%y38zQ(qLr~4GR`@_Dhe<7
+c6))A0j%L-(0-%he?32uporr+eaggB&UWjzI#BrC&DWHvzZ5~`-16j_!Us09(?T$yB)XYyZPkC0l-*5
+m2efD^&G${__}F*kE#NX{9dN@`m@8yc<mR|%%?qeKO+)sZ-O^EP(E!;a6q{CowU!X#z`yr_`_1#-J`y
+zpt@=SfWH8a=vjbVPDNB(*Qk^mL#*C`{_<B(2@36yZ^hiur0QqQNcvkdQVyGuz>96T0fmFO?O9U0+s-
+4K4Y{L^x8U{cl9D-5vc5Yvn3f+_GH<KR5$|QyoYdf1m{<9UUT9wEOxAuN{o#~dPTg(r6rEWUHmQm_6;
+-}3mQwQSyH-k{dyc9+`=PokYu9Hvk?l`7MM^?998X9<^&V|7=P7?UsM>9=!=V&Mm#~lVl{$;9A(;6l-
+LDh3x!T9#kdB5X9G{}^Cp{|-GV(AolRKNM2Vk+{aT{%t9p3O}eY~D%|8lvej6OE&oESTm#>$f84&26&
+(qMB@bZA6{P*!v!HiAox>-)jLgZ)c6p0m>>!W%^{f?UDQufPlI$1K7IWm5Bfx!B#q&ACW#;Tk}TDtR)
+Q*o<dnk>_$_aq<(RDYDvpl-kZ$iajTy*DY%U&Z=3><Pb@_edwZt_TX9X5w$)CTk;at(dcK7b)R_wdu;
+GiuWN@n$j8)BhJX`S=aq1066VszRqLHE_OoLfUp>AM%B3liZRiXW)#Fn~RS*k=Vs2>%r|8{rWij{HaV
+iaubTAh0&`H~%WO~@$<tJBq{(#U=uyI`^=b~>4R?sh9Gn-iGv<+tK8{1?bI6lRfYJI$>b%uE$9E()2P
+Qz3t%hKg7lJ2SPeD&5^at=By8dxn_i$skz48c6T+Oi(WJr_(|i}vgORr~wrDPP?;xnCYCW*^dsKqvha
+v!@4m_EqWld3I(s7k{1w2EU^y(;s8%-4G=Y;xP}*Z>T@=rv^cI3m<G=wr62u$gwIYr$p!Z@i_CGkmrq
+%!^ZAd<Sl=CC+o<LNQmS`jUWyk7RIi+k3gjQNaV%{pFk*n1)(SRf>o{ESs0*GLTY2Mi{kJ_2lyAFhmC
+SK?y;-+w@a6)?f#OZrP;Cmy=>m;2=ZNX890e}H&2h-_OTW6tI(54=C^aju%qsZu3#}QoZD!<UX%mX?O
+rw_1|h|@Vx!}{n);*VTs1rO`FTZQ`p{1T2#~U@N_g$y(xUNvUI?#K3`e9@pUrcCebglty{W7voC!`6y
+)}#cF*TOAR($i$pU#6hmf6~Nm|zS~5tCRabzRvus*&^K%yJB(2X%H0Sn5#uadf=xQ|e{T_T@3rSGT99
+%U;zn8EsF8t0(n-2q!G593Nj@WAxl<vqkb-3CK@%&M$s<w)rd#nH8q-p7QV{Tsl!}Y?CC`=uOt%ozcu
+lJlD26W^A`vB|iKevZ4u*ym)fk5!G;}f@m|0?7qCc1`v48#p)6<#%iw4zQeun`Dx+mNl-1kJ8iAAc$;
+>}u$iX#^-xY$t;{;ZS#*M4E$}7`xr4$ek4+2xfs$xC;WsKE6@_xP?RAyg)@8pxR5cRGdHSp`wveh~j6
+EZQPa;vpI1oMT_2kAXqWB!Eb9P+Ce0$ZZambbaT!p3N%(v@MA$zQA?2c_+^1*v$NE2rlM8Sy|gWn{fk
+i~s_*p3@94)+g}s!pawTyO_$ov+v+h?OEDJ|h4|cIr~{84u(68MvpJn=5m&^QV5kN0-*DRu1dFHg{J)
+x~~6-x+vJ2!@+*0TOvSS<KtS;K|RK{a3^Bg6UZHNKkRN6W1pUmp?RyZR`HV&oypj2;y#-=qn2u#UTyi
+mQ`rG=lyqq?If%yIzJ~vM`@NC+&yM-9Hv4k_FQ-Hi8MF%HXqc~0@h?*`vL$%QQm8gWmlO%UC=b)mZD#
+OrNlGjkepnz#p}+#qpehhVXBoca|7lp0pcd^2y@)WF@S~9NLlnm0@D%W94jQe2$SYn3fJE582m+QbVi
+Lw;(9>oyE@u{Bb((?aB}r(s36>3qZKlu^4ayNo7>I#2frrPxjKX9Ib}<j>uckz8zl*>edzKvp`cf^i&
+SY%O+Xiy=8H6af@5s*kePUs3EfrR3<J`BYc~}0KRYCa4&|wJk<$y2w34b(Z9cm@!gJZmTojKp#R=%wW
+`F=@%cSXqeOZt9Ae`SUSz0i<9&Co=fD>WhWORffy1+ERR_<Wv>IGPhLqEB|A7Mq0J@1}OWn-tH_YgKM
+!WIwG6yRtcwkY(;<p2>2}+Db8;Hxw;K4{QI@%b+rfR+_5&P&^fr1oT}i-0dw99=9`6m(%GoSJH7@D{~
+t)IiZVBft%PDK_+Gy`PF=1wG2h!_pGa1EN_}iIfZs+f={sQOUOBvx-epCz}|9NXM+B8x))>FcE;Tu?z
+^Nr*}GkctCCt=BH#7c=XmnxMn!|V-D`!1*z5{#oRx9uVwp<!q;vGt=inVzCDz8F(Y@-d3AL^_=*q}1f
+E=s$!W2%qBnDB=ZXbwJocWEuNw!l64nE=uFOCFylx0=$0(`H0V1LGrc{cvu0FN@-Y{Cup5=D<1#SA-b
+_MR;V)5`x*$?!J7<E>dHkxg?jL|l;ZeGw4?h|m=@A~t;R_MYftyjPX_Ly!O>hqy=nyt5sATpokBD`bM
+gj%SDO?qZ*BegF1R9(W5ls--mWqGlv~aitI5LUBF9X|dV+34H=KSikb^lfM_2;GSsKc(}0eBK_AFMZg
+UMJa`9|ea0==19aQf`&T6K;d@L3egEKFU9x*Yd#DbT-+Ax{CcQ@Fr(SLfiUQJPH1puEDX#M*KR%khAX
+$226(YY*V|K#{H1gWkcy=>KgEnZeyP>0YrOd~^TQl-Q(YKB6kAt$V)|<;3^Wtd8`H-lq>cJS)Dt4wPB
+Fh@jb%(IMO<@(xe4`Ay<1z&49@i$3vA3$bMf+^dIW;FW_DV1pU!?A@gF24)<8h6M0>C;<*rqdG9xvj9
+5IHaBo6dB_@s&9MdW_oQ`9nmJ>L6~t6ZrK`3ct1LzS_M~0g*4UbDqQ^X=!kh>^aWYf~P$H&?oIhJn3u
+gdYAl!^M?z1UJnWmMPs{hZDPYo%auKHR1YhCwK8V+VfJcqTA9zlm}H&|7}2_=BIj0WDJn)W-u6_O|2|
+l5`o8%$dkb)ox8H|+bZrVCufDWZ+E#u09d!QQBi@1Z%h6xsD93RaixDXM8J>pW;(wOF7V(T-+Pg#8kQ
+Xtq4v#NlNP004C8&Q<pS#15D6H0#5j+RRKxBg!E(}Z}aLXj=ID%*ll>p>2(J_G~hlu?b%l*tBG=}wtl
+!O0+v%~ZV0d@PZp<t1Da?}FY7_5%4OG<`a`v2G^{{hE3lJNL!L3(t-S&V|H3LUY3S;&_uNW@wCBdc*B
+sonaAtj2<=$ro#@kIx*!o0XvCS4cv5@R9BV@ARD^ma7=HKI-rC_US5|z8al@B-;K8%fXQc_66LXL&BX
+6i+|hhV7Yiqw#nIj?cDwDOJ-v-q<jXriW;RcBF6hjMk$Bsj^##h8`!~Q-w^z-VQ>TZ(<WBChpFwGUF}
+}K2V})^0CtM}JLm?cYb-;e7ajcBngh>;`gpKWbyx5Yi)YzHzT|iz5Nr0$#o`MFkp1_qRuEoSrhQ4y0v
+DBrjyl;bep&5sRHIF9oWgT4*uM32#Nc#3qNu=<*!}Vp1kzw%dYc>s#2$i+LUfs9-h0$o?erCO<SUw?M
+`k-%MM>*?T&8kdIz+z4x{R)!D_{WY`pg<)Ud;$EJe<c%D-QDD<x}4^xFfEti7wSZcRW8+`Q~zL+DyPb
+r--Vs-QDEXh+5om_Ey%QAv>`rR7G2n+JHah%MeN3akpBN=qXM&*K9ml=zJa4gXa*TlgRGg$ezb3&{Il
+|bJ;i|$0%2hojPA*!hhv!(ZsW;qmH?L*ZW~`?_2#qjZXJSUsC8irjCr7eMvmpmai@t+3pa0nqBV_L{c
+n0DNizQYZ4=7_KVfZLFB^`ghuN9XGUr;Eb`t#2YkO@3MsH6XP#8?0_B3HFc<6sJbC7idBT4sBh*FkVb
+Ub+uJJrLXJ86fUgY_B1Yb3sPhjJm>hqx~G`uSFzA2#a{%90>_%+F;3gOj}4|Ad5H3L2Xd^P#R6e@TCx
+VXcMC_KRoe?I+qVibW7T&@i605^2JTEP{7E1urIYVa$c!xcpE0xHVy;ECmyht{mfa$pXY1|AsX@XRe-
+>avm$t_0i<4s784Q4W?4z9wHz1Xs44ARL7mj_LmD#SxUk{Rjg<6hZ86Z=XPb{A2uL$L(8qlp%s>j(W-
+GcY)3|-4DMDKIZU6T?3nYE_(tFLIsiiCUJo%Jm7>?T2gvFs4crSx3Pc3*8_dVSBCKRyVpk(><uCR*|E
+sS!Jm|FsLzH4e$ig0tn6Qv!vzrmQ{L2Rcse+eV6)Bse6bwub(bwbz2$4r7$s47ZfrFz10r#;VX<QukN
+CEql6Akzkt(k73g_5daoTAtM#*QGydR&wRy_7)WKF>fK%mRNHr6#+Z_c{sGxOc$yK-Nu4M!o`FvVJ8B
+-PaKBn4N7pc;)L-&F4Ik!@9ajoWtJ&#Qe(8#|g|bv<fr@R3R-op{_e8@YvCC>PPTb_X`X(Zdw-Y0=ge
+s=WOC%9WD-2%p@=eYErL(uM0l?hmi~^VJ@z!g$b6<O$Za2iT%J{rzy&U&kzH_N1?Rh36AT-U<d1)pxP
+P$+DR_wVVdQ%V$E5@7<;k(ExFxLACZR{(3#*^6$7VX!l!Qfq#D-?``){{D3nyzq#kTz~lEH^&Wiu<Pq
+OjSU!zISQr^V9U-@LLcH0!C9p0*;_w>~Ku{rcw7~%*{=B*Z^%5a869?>{U1E#`hp<m3&>JUOLIx0ufR
+F+3><C}N99XLP`2@0T={aN)NW(!C1PDI5n4iZeEcnDURA!*~qEDn@r3O&=l!4`x48$ZD45a|&rx)iMs
+BL6nnj#`$6Jz{46UZb3LSpe@A1m^!{L{%}*7rqzxt)-2+Xssws*@(dshh3{15`ig5}&^J04V4W_vgr0
+Rh0LkoFK+H!NNUeIL5SJ8v6A2+t9tEzhFb(`)ZJ{m-A0ngM7W5Z&vf0UH1swgqkBucKx;Ht6O<*oT55
+j`I5L%WP?%be9JKx+t%zIj^9BblQHMxcsmh+it&%xL`t_Q+<0#mP4xhzti(fU3Qgk<E}-Za%zMn;>FW
+9y&tmVy#O6m2zw8^byzi90i{u+}oUe|UKAW&F(6-%N&pN%%j6GQxc&ZFDwyW5Dn_seIeN?p84o|h8+6
+q(3e0bC!Wq|}f8J+EYePAwE<ErV!^I1FtYMneB@ln4BGn3pG?~_z#8suOwj*6M3VM`lUb_%89klW2gy
+Dk^c<<m@CLEPVVZCDX8!<sxX8NQ3Lb!wEJc)JqAIvqM9qyX!Y6$~}$dR-t->SUd9l)YoqKF5YvJJs7<
+Z+FGJmF&wOH`0$4|1Sl93Hb~*roVs<sfyg=mbnz%HO>82L~2>qOZmm(-Oz8QUZn7c!U*BcA-$YsHsjz
+vy}o_)QA(*7oVjf;wAW76Ofz=&-01|uJTCVu<M&-1->_}4Sz9j<Z-=RQS;u<)3Z3;PRGfN>lejshMW<
+~_9=99x?r#_@z6!|wVhD*;xR-H7?dtTR^Cp2m#vMAv1*xyjsbdX=oQvQy8&Rbt-za6|bRjL|qqt2rEg
+~0A<@<OmZ^X1vM26i2gB7f{Ec<Ak_1!uYC)a9^dyU*jyOV4)u5dFggVV$Qba_4wNM@+hiq5>wj)yDV^
+`t;tP-|>8)VZv8v~>?=2D@CHm<+QMS4h&8@O*MJX}*BIhUkLu^$>V}2R7_9?wfQGn!~+Z<T32sT>OvE
+m2z(y_V4TX|NJx`Rs0`M@ykpxoLafa%~Bld&cL?IqA<If;gC<JB*YSE7qbj&#y`&#11=ep|Kc*J6bBq
+LBcL)be4CY#LcRE#79d(?Q_vKw=7UWh!~PW!Kl44!poAbzm-);oRLuhv!a|shhG4@j?dywy4g|c#x;$
+BW)KM0)YH|S*GJ{A7NIQc=RUK@5#GxfB_G?6mVDx_Vew8WqKQSb4koUqipn;b+Lvr%7mguikULWQ+F*
+GwrzEDC3@W(PaZ?0d|)=QU}iNd=47poZucHZUVUOM4>DS@4LFL)dB^^Jy-|6nxzl}cbMzcz1X<(EJE{
+gBTrW&RX|e>j~>GC;Jz+hcL<erb$@rZ^TH<s|<${J_0zeNLYb>jfCauyZ!;j9jq$$qJoYB}5j1WwvgK
+m9i$XX76W@l@n#htR<LS!XK8ucClBckL&ZO+!Dd_$WdpJg6sR&s2;{)Ur%>e;rN|;XxvNtT+_GRs-il
+ZH#X%gMWIvDw-YW!ulD%bzAmpqaSkqGB!H}}cfJ3pRexTsiNJW~bb<Glk##I}Fz?<`@8ez_wyqfIN_V
+mj2`%SCczZedgF{YR0uxM3anN8|+xhCWZe?5~&Vhw()pc_`F#J@PZV#xqijJbiK1@A0y{0|y^XdI|L0
+BV}ycj&Y{&F*#V_nOlI>qQsz7Iyq(uC|x7a#ER<E7)Hhs}!~j<+~RMDs~M9RVN|>}hrR*7W6@tg*K)_
+71bX7M)3>gHqN{aV2KKx#P$sje`8p0QWb%#NQGWTd_C@LA`q>p7v5>VYnwuR9z+~uW^|=GTZ$+O(<0l
+RV^V|P24<R55pUzdFzYBR3qCcj56D`?bD%gM6!u)kFlBj137Nm6}qd{4F+fm2p6Owp;j8Y%iDObOsyw
+3OsN+pa;8r4u)-U?ajz}u(YJg>HmOUi<ik7^I44CD<0|~FKWzs$<o!Ivu64RvQJdF#(!B;Tqs=Me#_O
+D7BzY%|(!6d6Z_jvj?eQ1&KDe8JKBL%+E$r8AC9Jz^gRWzXvCrA+kWt9v_;_7O%aOvfX%vN(T&q*WuN
+y{L@ulWUIeSp*BWK($=$_J2sX}S&ny0(1aEMBw-Xo@+Y|I0bQkT7aNM42Bg*3m`e;=ZFQI>S^V7!ry!
+~gj5)8X_#j$8dZ9QxI<ALB5-KJFWvP88=zj^G$LeviR0j{FpOz+TM0$*pkcBLdg}MJ>K1Df|ZP?2ynb
+HB21;S&1@5Az%S^fiFxYqoMYUW?@PdD$NLJngTEhW8ow{YEfac3s|r}>qv(d-iv$<oR<sXDX?s5j9Qd
+v90_?9w}{L`4(3?_D1ysFyOa<<3qWqTXz8C?n(dhsCVR<c>@qZu3x6pT>mcw@A3|>3_X({iTOrR+pHr
+Hz23sKfIPT5UUa{n><e=yq7m#EFHdB1jXG<r!+Zg8ZiTxsA4&INSkni%Z&R3S#(*cfOD$SR1UFghmn=
+yafwyXqsU&=pS3G%*_A6BC4;!m+8U$8`cqcS-F6Kd;%kXKC_vwbOr{u*%J6#@TvIqORGB3W~p5pN<NA
+Uj)J)b2cmhn-e=yBmX`T;AOn^tET$R`Kvvmzf1x94y>SJKj8dEQ1mAl@oKbp~C(k@!P!FT~CfrYFlfX
+800Jj=d|c0pEv1aeU@fZONpoBNxSEP=qi~h5>J2hM}Lj&mAofXA~jaFhoK%G$QSB}s7+bZxotNkYW1!
+n9{qGQd~ZnIXT0u{!}+l}n(J3{c;Z+264ZUF8yHm{Fy(T+p7<@&p>`q@W-~s|jb3=Zyua)w)6O)X%PG
+^3YIa4XA<uzmeQlOpvnoa@oyGlJ&-}3RBjn6*w_Y~`%sLM#+nf7RxxAuLe4;^CugNgnnCLMO4^0+Tnt
+5H@_nbU3)NSq99%tb!Vrb6paA4eMJDk=>lkY|$DjeNQ%Hspy9)g>B;-3Xr2&r%pfj^5H@07FSOxw-$9
+Ly7vrSYp5RxFk&hrwgde0e)d4_7?2F@Nl<+Ml$i$SK?PbV=GSVRmy9T!-PVJ+{4!opl7aGn|m~s1+Td
+NcY4ocvkE<TAE4do#6DHzd4H$n|m5N=$E4(8yyuH*B$$;p0DFeL$+KTo{ssA7CDKWpB%0v(>kW>({-S
+T6VXvwFkDicsi+Cp&*!nK#s~LW#k|d|PI8zc0Y9GFF^Q&lGe<{RJIkj^k-R=%D^JJ2(yQj^@4RT--!p
+8B8Sm+4;aQ5pW9E;@r}k{$=OMS@OxkT@N6|T^)#vqi5-4JSb+(iFDWNRBlaN1}W3hd8o32add-yzYrg
+n>Q*Ju=eL@a(ukLH2|g3XzygxrEjZr+gOaW{U|V3x~YcdZv7T&zH3J0Ot_wVc;7!m(Rib66}=;CplkR
+IdEV5Jos?{IKIY`{e00l9}TzC)WzsJ@ZiOj@6ZUJ~ZPcyL-8<oZT%QkLhVU%iBnk0%F$<_x0g6KL@gK
+)OjY5e7Gxn=UD8+;yfNQt~{~pYTg`E)t8bn@#)~KXC-5%rf9H5XBkw_UJn(W;*`B}wke|1ma+nzSZ2h
+Xy3>-mJ0%Zvvvwnf@t97-j$<{Oc7M%JF7})c#__lnS&t?!yZrG)F9-!<i$qY?&XFTE=NwYGj?z<Z`{!
+5{t)QffOLxD=8QZ43XkIa=5bx!TtG#xzKvZ}^E{}k*tMh|VZpu*i64h(>W_KF3fm8%iqDb4=pL7<)5N
+vy1<xKY^j(Kz{3ihsVFUAcJ626&N_{pPRue94~+pW44scvsyrmk?<O<Yblvb)6s(=;IX3wiJ8RZ4eMa
+G%S@HbCywzLXXHd7DhnJM7{|W*5r6Jyd=#ydGV!>WZc^ldJ1;wnnq`iUPzVbfcS!9(KEkM&{@1nn{~&
+fJ!j{j*>`XS;wPst$6%n-GKmG#{zsa9$adeR!`HEO<ij)FRUASFO2tys#PFBHU0DTxZNvSXPHf1wyC%
+Wq5DBSJPdI>%i!&F!#qvCR_2&<x6slIr=V8$<Jln%D&CWD@+!?H@0-^(^}vVOYV^=NKl<S=wZvSJ;CD
+Pfh<jK0`{+iU4|RGP+cvvt$rcGv>QPh&=Wy(C99>COV&z%YuC{Zoxj~UWe!C~|v-bDD*P{OI9JBv~vw
+ht%-~RoU`R`izP=<oRAI{P^$x;+X5hzXK97m!whY=`;lb^Qsr5Vg!XNwptT{6RQvUI0oFngS)&?^rkV
+Feuog)#ZLOFITr1N1UAlY`QHSZar(lUW#5qHn?2GD$gR;90?s(bAbf|7;|4NWyS3TAYf&>EdM`8oJz)
+C9skXqFCtcM=W*rW#Af!G{KeP7-k?s0(#1WM}Z%+CD_eE*h~P;mIydV=2u>MN0{6WPaxWUSw`Gu9N>D
+=<4K~jy_OsD)2Q(LeQNo)8oiwu1a0pwvDDICjr`O0>bJ<Tf?jzt%t?G@Z3jcgd^E~Gt?kb9&z+Qzzq8
+!WR*U@Ba&<NPT=}N%5$N`GF}Ff)N}ID%r)<%0dCNxSJu930p>%2PX`RGl!H+xCIR?0;9hrRNYp*l0eH
+JnzgiXs&9GOwH(+79{dD(N=^QGKtz*GtSHXkixy15ft-UbKVjY-08D<eGU>dKeamln}6ZrnOy_41=z8
+{G33cDNR4%ndg@Twg53;3iy2(c}H5RE~P;bFWYyU((L-X(2!2tSaz=@H#y!MG>l(C~M>7wcA-EkGgf+
+Q)(8U=_;by<{nc3#PChggV(#LVwY`YmyaqtDI;;Z-mGG0;twm?R@J5P>YI0tO3qoIyucUm`xc$4^E2K
+cxON&bZgce|?1oi^n|Rf_%3NJUlZRf{;<aHmFX^J%w|g5;S3NvYO{RKlX+4ee{bK$$*EKG2b8pl5kM!
+T{8Ej?uj|v-(q8!*RKMvS+$@fXvzy09%jq|S`_)Qj;L@_{%afYEWj$&AnWIlBc%~+_k&6aH<ilHV5Uu
+Hf)UrrYGDKpDhCwlSWh(7lY0W3OMRI<<{bQwAfM?;1%#S7D@1hf-npg$X|2y%-F9J&~jvguEDLn4OST
+(ISVGeXC=5DJv=co7n1OJOgBy52B@dw0r0ip(%@g<zw^QRoU6FFxc1Y}Q&@p9xrI1ba5j8vnY{S%i|K
+;iHji!yB8GIa&v6W`mzJJB#m0TI5TX_OF|r|17{6(g}O`#AX2TaH-1^iUcA#ZRY=vx;I;P6zQ5o?|F*
+4$GXKld}e*YED#_u35h#q#3aNFPk%r==gO?i`uab1?37t%?L-iS*i!iN6>GUO<>!9fWAV)ilG8hA(|8
+Czeya`sR&29{WLIedekdt2d>5|*h%MEdPV9?Yu;9<=YjzjW?E9?Q+O)+n@9ZwHjkIVh^X1^T@9$6E9`
+H}^?@!(y@K5jWPu?C7+5f^z>Re;_LC@RhB_XFIHOBD*6cJG=<HKuQeQLd7SaO|7T*M7t!r45NnSdjXQ
+J(zq=86%wJoyZ<`|N?{+t2Hv4}mbKeeyy+ljosOXJmOD-6NvL8E%i4%4sA>*1RO-G9g>8fzv{W7MHfI
+4Vhgs(GA<!smxfF$R7<i#-1Cex}Lnw&JJm9U`HaX2n|fYNpzknb7GpghU<R5I@9?UPmz4UYZB10XEBS
+l=CEv@zg~^ha)R^>@EbYIjzkoY(Q5Eb1h>(G<Z`uxhmtJISi#3RYWsi#=3*^b<LHssgqkcJKU`W_k<V
+9(yuKXcXhd|-2(Ke9JvS)I2E0=eC`#|E0afW?8ejlyoiB_t<yr8TncbhspzR;!uWp#O%2PpN6c_Ch4z
+R_YGpT-6r?!K9{zuE^ujc|kIXU>Cyn-O|jFp;f6I9-4Y@fXF(-%vvGWb_bMEmR%$iQQ@ziAyhfy-f{4
+-@n(Jxy5DgTLM?%)t4nQOS`Ss0^so)qzDwq6eHKdA56=Ud=K}<6|R{9IC%A!*ei>Skjhe7RTMd@ySg-
+oVA+7qAr#SAc}?*D-EX$p5e(i?sRq{o|)lbs{k(of9OF*jLL*PiuD4jhk~@OkDF{yNsT2PN#a1i2<Ey
+y>1)l^f+EM#YO%)`5=xyWT+R@8kM1!$>dE<;ET@AgW}rj2`tf+hEZQf?5I`;!B5lNpTOnj|)J5wf;vN
+SUvZ9yeR4Wy|2CiV=g7q;bXl_3V$d9(!PPRwoGu{J7O^3}F2D5&E+;}J>%V1jW@<Y8f&I{%F#8S$%=G
+;HV-}rw?!x-0pou0*CLKzo*`>Y?u=aSsQ|LgcztGNHm^3&2^9^u9}_AK+)GucO(KK^<SQa-kynE!442
+-E*>-QzQw3;%xn?@Ebbn8HYkCJ7v;DH?_#oQ6phf}o8Kp#+M(nb1%BcA}j{?1UlMw@!V3-z6pAMkISi
+8G0K-C;Ja?ACaHe_q;uvceN3ENAumNIez;^k-MrFh3(8`J4nVmlM3LS#cb5(T_pUgI%8tzGtgUuoWOU
+16!=|!1ch&FlW?~hfx|t?y_-`Jd)9onfqqk<y@u%BcD?`b7B7#8{eJX2Wgg*wpl-tVJLorG>WmFjsq~
+A+@Cu29QRCu7KXm=icW7bY2Xu6tZ!JbLEiv7>^igvO5WMjhmh;`Y_P1|gbo0t!;}IL+6{%t2wweo+#y
+>w7?5>^#f1LDS)d1<z9|rnbsO%#`YDpy#xK~IP^~^)&EOyI$?}qrI0L}}O&I{YHGIV~^B;m_3;M2h=`
+cZLKl;;!ARG(Sh5xRDs+e2YR55C)_MpbtE>;V(KBA$zP8wNMBAPmle%@>{_K35WHh|_<ZgQXTix3cj4
+K){zuX1C8|A#36aE+dY$?{3uHZ{^-Gy88cs>%iZDUDGUCA*Nf(mbrsV1IC7gHAy9XTf?rLhB#8aJI`7
+q4qo)g0W{B!Ve>>d^PMBgEGOadPJz%%dNs3kx`YA;CpWMUEqrk~K`u}1QG1llgSqloVk&JwJ>-0TAlA
+bLiJpU=BmvoTAe$0M-Dx0t&y&uw?i#b)hg;6~>nT62!WiRrvXMF$Jpy=tuk<NjkOXdTr%P#x^L-RfAO
+pYHA)8>H;ceQ|!O&)8CC@{}6pw0hCghnbM{?o-8q(wWKukK!dGQ3=>)`RKrh|MUhX&%M3nI)W7UwoW(
+AR3#Ms?kuN>kA10(x2t420m3y9~L!*ha~T>q`2;u~fkRA!sEG3i$m(bmwdFy+`CX_znDS8~6J@-os$3
+X9rGRroE8f)q4uSweR{_AIe|kZnyE-!;LI$7t!d*R%@^`A*t&n6!My*s9u0qap3&(ct}sBN$~I^!U~$
+J9h`FUNW`MiXSP69TD@Cz@Vl~B8<RUN-Gr~+p|yM50uIwU(aNczs@Z<wqh%6I5?$h;8ii5xXu{)}JG)
+LV9!X9I22}1ITsHPvj;tPV8(9YqAVRWj|6*H_u#uaTj!<071I)*`Jdh9il|CSl(5VGK=*7_sv&O@&Q&
+WN0)UOld2Ao_z7JT10vPDRyk}Ky1q&7#qr;Hl9jn#M<7*l+@cs!;s^TfupWn%QLy=hX1&ixBW6taw>6
+H;PHu*zX;tS^B(f$c>f(TjASvzCh-wC+Lf5*vfor=xJelh^IfJwD23;Fs?@p!0YCaK8Q5S9K2){n4c3
+zZu|*%f`>o_!b~B3dKnXMK<<}(<Dw~2(kM!zjeZYruY@TmA$dI_&9{WRVwj4A-FN(ec$yhyM@L(EFwQ
+w{EFT^HXlOcdyx{f<JCq?x3MvNhq4gfAr2w-Tj9ZfDE!T^H*ouvGxG+}d#UWl-Y>Z)E0MQ37vb-qyUl
+PTyxTzAaQn9KyO?!@;f-u>F#4fyvU@oD{~_PBoJ8IoKlFZZn-d!R787rKn@I7cmsMq%_kXN>&1~-Uw^
+evo|CwxlqiZbokrw1;?PenLc(26n+X5q5(AixZ(aE)W@1Xk(E?g0H0itAqsPCAFee<IB-HY36pL$!1N
+m~U>v%gWL%s-9+Uk>K%h6K!X{NigB9RrDe+}-(`f}$_lAC~s1?-lsc-1_ZX$iFteZV3IuYu}?2@Q<`p
+o64wRakfq$;&@|H(ifJ;BR&{cL<rsfaa1Y5Ocj16Pp8^26GiI=3l!6|2r}o@X{i%Ufesf?Ji_WpyQDd
+!oe1IvlXoY}SbSJJ8%U3_#+^k4s+xy7(=ph1PZTTa(tadtAA(ERRjvWAXv@olbw8)`O(Nkzg;FzJimC
+!m7q2-V0(IdYChj4p`!z({qv4=OJ3SBhqAo-T(i}M+9GW?A%RczvP6Tc@`O^)tH3AF+OV1q#HN>&$3e
+Zq<B_*zjIvAGaOL=+byfg|<Npt{B@eB^KP*bhLTrFMTG*|F|Tl=_?1dR5G<;N|ZOy*^*5S8TJHN8rO`
+!X(mFjRbjipX45Vw!#LU<~|}Uj7L>$%n~8!6%>=PSF;y0aj=KL6Z;E9H?Je7;osL8v4jMrxsk8b)<36
+0ZgYN%al<Jf7w2mzI0;3b*aWGQY(VtkfC8(n@)~1kG&2d5}i^i5@Iw@TLkZdNo#;S*KjjDpK-Ok1*eu
+0nxJ#2dyjKwH+W_a(%VN_irmZdjIFDaO;q_jbfiC(=hv!mCxA%U$b2qW^w!)@>~lOuq|WdWn}e_rM3H
+?`B9KBt;h5cSkH&`}8rKxU)Ff12qtUnlRVXKMO6IEZ=(%j_4(rlh4eV0fwuP0_Z&pBN>I0_k^2i%k!I
+p9KOs`5?GSah(!w%49ia0MfD=YZ(d0C`bD>HSr2(s_50SyXd>L_*qJ&~h&h7iT@OtLMSnAf6e?Zf5>x
+ICTwc{S3|Kify-M}I}}*BzyB-%%1DZRI!ZGl_S%NWc7xlf}On0@*kdL{L9gD*a!K^QDUEza8T{foKRL
+5fVda64^~AQHuJh2M3A1eTK1JBOwm=oh={Tx3YX<-ye$KCAcB=E+B$7MEm*v)?1HiqgiOOpSuyg;B7=
+4<NIdQzN19$s6zXD@)d&K(DZIGO@5^Y4PpC+*v=BEeJnWHe;}|Ov#4YrX!{|e_9~~1#G!Air`>Ub+R=
+1F;cXu1dp8S(_Mi6-Qf$|O-fc9Y|HQu)Wst;u-90Q{DxMs0BFS-BEb*shg_H9Q2?BN&WL~w|$XVud*7
+EG^h7R=NyJqI?%2(gJG36gd7G4upa0Y}&W%R1++b3~%zA&It+87d|<@Z(R8RX1=NyftOOee2xfOdVGT
+<lw08#fca9MnYN?}bZ_JQ>U<K`y@BeUNsmiQ?|=YFAGBox|lFc~=HY{!QucH+G|vUio&n+(2x@WyO53
+te(q=wi=V3r&F5B58xMy4cxBSu${0AHtaPOr?MK(#F&$mFT>c8){uecoj#(0KI3JkBvxZ-1si!SOEyO
+*rNIQVY20f(_7=<Ga(BUdI%v|fr5+p;m(-h93m9S;8W8#&?cA^nOVc@fU7Ha%$H}U=Nws|^;Mt2h^g7
+gp%P6XPiWM*Nz-PyVeu+HP#fb!{wA{OQnBWS+^{+Db%KbZeSqV|rG<`lOMIIesOz82A-eEij9jXMnRD
+lT{OlF1g2cR?b#O7EkiS3z2<v?*4J7O*SiJoWv^*Rci?%ftfl$@mZH!u~tCOYW%wrgZc)(#nPBdBzIu
+}FdjCCR>|yRCsZIzShKx#X`C?pGk5E#7Fji`<kv?N^#gdzvfF)ukJd0H~<SpbbGo=Yr-}43nv1%%|04
+Ml85S-Wif1s)fLUIuPTj*Tni($j`99X@M0k3bz2>!MKZ!MjV9X4&5HEW`e|Nnv<JX^>?iF6^=y^{XoA
+UqLN>)=r-KzM-4wu{-u7jxeo|IcT>G85*AaUsoMilz;i;ymq>#BZpaqeUk8QddUb=N!-Du`Egr{0O@`
+?h(8q%foMdWtucN&fr>Ym#Q_v)&K{>KgfhSrW>3Vs*_~<H9vql`FoQY+c_0}maC2NSMQ3MFRU>|(pmz
+Y-=kwa$FJb>vz8lBV`*HVWSob8{WdvwokL08u;yqbLlL)m;_9?mrb&er89K~^rtP6#UUG<<HI!H%OQA
+3Keezl{uJZR2`dWUyC||Ds;`lQqUiqS`FicxZJKV=gs$jWj0gkNT0%nnGPUw)r$by4@t!WGUGG+`m*j
+$<jsRsP8*dANu#5sg>6N`>OK8qVl1Hy|=5bZ1|lhSI!#80%IKJqjT3Y*b&%UbUo&y2l(A-ySMPB?8r#
+i-cUO*62(O6$8nuu&`+;td*|+bV#{|g@Vhx!5|~{IYzuE0jbUbn4}Vdoy)C?KL_aSTgod~6*GRysqPi
+LL8l&c(ow}61T1$V+`LWa@6r6c=#^Jsw^ZO#5mr|>f8wOd(#0sxeEoi{6aH^uu{Xx69PtYq)x7Eb#G)
+^DmK|$ORobT6}!{_Ym!iU4FFiE)3+7VIi)<c0*7?4$-6K2NTzCPEXI2os25isvw5vwQ*BlJSLLRwKHe
+mUbcTXH<ME$i~Y1>)jgY>a||%i^x*XwFX!+sfiWa5CjwF4L=3kQ-X$%M%HLsXy_h?^O+sl{{Fe8ZMSx
++49KMumIFQ@OFwNN{Q*Hde%wZWka2`wl9ZBA@iAbW@U@k%>yl~inCj~NWxs4#+0!y&=D^HE4CMuP`l1
+qnNzm0c~vZ*okdYz4!vsTcqc6k$$Jefn1?2IW-~G(-`zO|dbC+HhyWUm5%E%x^r+LcHwm-g>Nr`151f
+3#=*<k<V;nhlZPe)Fiu5$9RMh3JJ&7l0=iyZYyilCdAjJ-XHh1Hl*M)d#FWrOfx)?R1<58rmA=3P3|1
+>+_qGYNr#%FREJPQFuC<UzEOmGr@#>j0EP0~8Qik^tFRyi#<S1^z5wYXE3SZ>edtV`U}xE$BZ$>vYoS
+vx$!6)+$!71y;T@?i>Jem*#jddobSR!`I-FV^fKNqdx$k+&FK8kz^LG6S3R<k7Qf%V2?A)gFY<Q`gXs
+z?bF>h${vaei3TybWxD&ABUXs8rMzyzr$zs*ZKd*o}gcZzpVew{J5|152t+&7{8wKea4@}2^1o5jD}E
+(MlcwsaU3NGilAv2gSNQvr$z8v=odhHcAvuc5G?ZdZTh`X9ou6=l-}bhB-!IEg8UEp^N?_Rouu}ybOh
+g9xnOGFWykzIQ@?i!!|y!BTQ`di_iP7@?{!Qu{l(_(@3w6C9?gZ}u6aVgL%;1B2ze*$-$hVchzh?Iy5
+Qg~i~i2UAnzZbxAGYF?%}21Jy_^lY-9^*BkZG|{<lT&)SgMEt1tO}M&(d<``p~fL$(}T6`t!V`Kc`%g
+Z-HA7oAmbKH<i$o~l~cMzl*-ltpc$x!<KRG_EKKz;p4*v)*;8U)?owVcoaf&%BI%xNCemwo8YsGVJCI
+_G6jBzrOS58w2~D_t5SSBRd5`+Y>(%U4sQrvAVv1E|de)R^~FQK|vg~7L3c-kS>q#a-z!XOrtRv;r1H
+7jLy^_anW?n`E{%ng*kDhe+K3#t;+M!v(7?)>7S>tNAk+N`Kf<8(#L*K>R544s7p-CV%(ZFsL&v;GnO
+zOvQQI8K&|fP9hn_)U81n#D0Mt=Jq_v6pb_vPhJ2NH(D9iKI#CK>M8^bFx{9tM<=V`(;td%5^QqD<-F
+dae(h-}azHt|h7w<u1ngo*;$~h~9akcAhZMs@MWH^-(aIkGc>)07Quy9vuy79sJU?}R<sIAj9VxiStx
+?C-a**@E0PU;#9#*AVLu<mJm#$R7Yxm|jRxPXAtI#-HFsEn-}@+8Uq77u6gWqm{<#J*~cqXQ0o#G=Cm
+Cy~sR_!+q>`(%&R0ty6V0Ilj2wEZxjk*6Y~L_$4&>=TJf6VLNEAaKA+7sLLv5ano|oxlhWS@ud!kn~D
+MVbK9L*Zt@7Ru;UQ-pk3gM?Qa!PhM(`E)4X$x&Z=5cRmk}v20ROG~M}5tH&x?nZcn{AbaE>)YdPIew7
+mxHNwCL8%TH$5GF%C<lq50N?h8poT){iDL=ASI^(UwQ!NoiG7A{uOOFFkU7_*v$^y2Gt=^WOpokxx&Q
+;5Fa2fVw@W~T7AT{{l@#w<qnhw&5iQg0uIBFSsfCrx^<AHU8lJZlR6yies8rdORWVded)%1DuKlL|e<
+GU=6lmp;ZG_#xV1~ps0&Ch&`Eo_d>bMdGN&gU-|_`{cLPZsi)bKm&YF8i};=*H+K^{Q+<K*`n8Yd$EV
+(3^<3M$)y3AFQ*R=;#(JZ5%6g)^s*jmIXe(qJ?%YM8uPXU88s-WAI+r4sC-icNZ^%g%$S!?;Er8Tp`t
+LW19gpZ3E7L#<8ydLHjb*iLwwiOaJ!#rLXLdb}qo*ePyB0Gl(bkxRSkcyx^)d<1KuRq)Gf)?hE)Rt7o
+w$om{J-wFloa%S4o7jU<DUC6Y|(_ie#j&9ZGz<CShix~(@fC$)1dy8%%8MlCxLXVtTO9<O4eYFb{3Sg
+SZT+68nHq<$eRZIND%q$9IydboGV>ro718u&N?_*3<oANAl1Wv$6f5$A$?zV;wxP6O&rN}I^@bXU$pF
+ydoNy)yg27$=Qzli9P@=?N(M0Estu;O`GYtuM@YeH?K|T?m*QjydzF!VZN(BMUZ~Fji27XKP_RxY#7Q
+E``epQ2CqNu6L-{AQI$HPZHFzM>6Q9wfeQ1fd)Hg_)0vA$E;EvN>u8-IUFuaOcaUw;%wl$jB#dMaNkb
+Uy3Sr~V2j%Ap^(Q{oy8I!uWLMJIG$-l5;C|q<<3xW&l!nLF)pM6P_uKOGk6t4RqH8<VRkxR-xT9c_!~
+>`X-h)y=es@;{B2?*={24&!NRDo<brTWrv!kLyF<ml`XP61nFte0hhVdLEP|95jeX>}?fWR*1!K9g@Z
+sF}VuzV9NRE!2_V<K307k8erEt0C`Fv*Vr`MRYWY>vti(OWJNXNr=Ak!aq8+lIydMpUougW<#6WA*HC
+j$btuTl0<-Ii7>g|!J&DDuiS#KY3-rltrk<bAu!$E185b2kVzq!Cc+0()+I_7<t^0Asncx~r$xd%>UU
++kGfn?|L~5=##&zk$}Nhtmp8IzEo>);QZ}F@oywnGrtPrw)^Xu{KY-1DsIF0V-M<oeYo|W{}03c#rfL
+{$)4Q%SqS*wj`2mJ^*7glR|*QhjpY%H!YF8q7)ki&h4FjY2DArP)LT9uiuc$K3ii@==v|J!cTQ5f;tI
+Muu>Ukl+(Ja+oqY7)s;z$TDNnb>gxm2g+}ongEuh<O;!}HV$SxD>@1n10_^ZSrvEObBG?TZF{1)s*$$
+uhr4;S|e`FA373u`fCk2Nu3pKV0#b@{uTK)h!Yv3TDW+oIL&;C9PhfgsuwqT4@1=yy@#(Ox-@wd1d)p
+r^~WN)biplm5^4r#FZh{uZNMZIRc6FFLA(?TW>oo|Cz3^;{>tqt_*@u0zDJnJ1ZxciX}}NrV9T_*Rw5
+?=D=1)kik*lR4*?6Hm5a?H1?n!A(su>^)jERxM9aKr_2Z`rLy*E3C1w{I`RBjuKBcQ-9uOlLh^`o%7g
+zdI4FYtL?xTyf4zr&s$~cC)eAr&6{s0xJAng_^Y}6M$f)ExZy4KW9ss%Iof1=%<w3zjed8{4Ka=Mv<v
+T9Yz{;l`fWigv^|i*J1XUF%d@Nae<VZnEO4)jSCvcP3ZHTyU(RbZQ=S0fBQYupQ^{_jHLCO4a^AviX=
+S|6`;I~(^4r_|cCgf0a8}KQW`ox7^KM^iAlY`(HhQh40dn8|P51jncf|Rmio^8s+ow4F5?uqo53jkA(
+fUbW2F5L(L~wjKiLW1|?11QzO9Kei>E=-emG)#fZIhpu>>d#A8hBPk(Qv_wH-sLNMSh%^+aNyX_TFH3
+v7_2Unb^t;xSp6ry4K(k8*dkvlQPR3vF!;&E0&hIlli13BR6L7F0oLQ7ng52<MC|s41*+yL=1o*PTtc
+TB))(7T-&MlD<e6h6enoVEz`j`rs$=z8TzQ1q98sxxW7DM@b;Pw;TX}L3E-)I*kG@;-3%V$imD6D3ln
+Ft@VURUg$hzj6h+HH>5xl-rg(hQW5pm(w{wpQX`%w2ARfc!+&bE&<!)Y~>oVS>cat+<#0;&b&EZ!|UT
+Mz)oh_{EZpURzh7%&`wNXL14_Fb{c<HEJwA}$F4v+EWoR?>ijqsF5#!~9L0|pA&V%01acgI?k1I97U%
+dsnA$}Rz?G$X}GPU3^Z+Sp{t`Y9ipa!d)|WLmb<%j*kAQMep}QyTd<%CmSPMyAf~EZ-zL0ECfd_VT6|
+`+MAC^s_LCJ6t>5a*HJhq;W%v*GO()kGe@lGmg_@KvS6@@%f~OGY+@~<mvbcoi(X%4<@cCuZ1y&xrG;
+(!k7hwC%U>w=>=-z@dY|+UtX&$8tw|52WplBi%+mOHia>TBgcHc=*tODhVfF@Vb<gjmL|Q7<N|#H&k7
+xJ@OUFrM>DKPpygGzg_+x6LjY2kHfM<N#pPILgMEX=O(!A!Hr!0NaC=Xp$}Im$6ax6%5(-*Y$CD=*Jk
+|_Yak50&o^70BaW1Ab);{&0S}d}1EFv>s&IkT}L6xO}RiuINM+*$_;|lX{7Z~8j73SYAFu;#1%)eb=f
+R7dC!EpwcBZUGssx%<^&pjG1#^?6h`=s;CvL~B?cea^(PABI3{@SDTrN8#Wqya6q_bm1s7F6&3qE9J{
+Unm=I7^yH;yU6hL(r}6+V<NaU@o|>NoTg1riHYbnOUdK{7X^e|#E<<U;fE_C6U-I{F4_fsIK-`+FHI=
+Hsy`J_nI6%XdhzdOs)4D&o99NRIT66`IN3B99cKEX9o$zEdiin6p+hPj6pd<C(NNGqZUax1&JN`g`3%
+NI;FxYBz0N%hq@`cK2$u3$ZKWG%q9m$5TSk6rc)H*Um})MGe!5BYAs=&wepvM~K!>XX$_P@G{t*zLGN
+BQ@FQ+-0w(UzDHd@|Vu<2{!nXW^%xH4z34HGaW3HsP6eF`@`FP>lXt!{M$Xe^clP5i{VGTS^d_^!kj?
+_!{BM?CMz*A0>%>s9ieT;X3t@TBE>plPw9Lh+p^G#;48U~YA4q4bKC1}hvBpAj#-sj8JoM~}P<AEg{S
+p|E|)`4&x1em&}<9zdY^5->vo`dsE_@ifT28g>tnllq3CxLP5Zr*=E5#?Z|&w1%gZl~0el+CEJB!FNV
+T=UBR1?H7<2eL>qK-7R`88jvttn0_)!^P(!9m~f>`$9#0QOSP-FFKxuluTiR7-f-7PS;2e^B-!KMI}S
+<L6+~P@Kc0LZq&XF7$Kz-|T27*<RO*?_TzEs}p73przv&tRgEZqj%oI3Fp`gXfg~C(`ZObdQc5`uU!`
+?`*?d@K2DNL%*<iVfr(#YM7z>XtQhq3&)Fmu9tKm_G~-uU%VclOuk0Q^h+*`LS$|J?}R*S-Df>hD{?a
+16#_m?Tkn`<tRL`lpp|^gHfP-uYb!-pj3#c&}7LiMOa${En5Oy|NnGRkwbwI_>Z0#_`)o65p%LP<#*i
+DSGdE4d3ctKD;N0@nBEkCd6K#?QbmOS8+d#?p12rDA>Epc28B4d$rs)RhZZXT{aGYV7rbBiR`rmTa*v
+IjjG9DzZvp&oTid}T6--7wO_T(FNk;k5&l*p<!IS^+%J980#@q~SYH$Ca{fi!-~InE?*FC{%RYWCuj5
+!A@TDzAskZ5TTeQbPAB|X_%N6$ce=jG`wOQxcce%oL4As`Xocz<lZy(IRc`m@u9?ZXaF2K(o%)faqz|
+S7cKR=i6EyI9+_6*~bYkw)uO^=v?VIzkF+h>)kL&CkhDo0|DBj9v@oOO4Sv<H5?kGjID8;bJ&9EJ<+r
+sWDpm1tf-^Sa!UTkfAjT?|7<i;$=tZ@NkX5(yD~IP_0y7MckeWYakew<aj++r43B>|s`?9^)-6SH^3z
+nD8?-I8ui^MS<>@3j(ZjcDbu^4rw8|R-#(n4;-(vOxNTg?`P*HS)AI6OIzn^-N~jJ-8=;Y%zw!ec`QT
+>h@uO7NZD&AM-IWohHk=|Mj*F?5)PGY9aOE#M_RgXU&8)=$WN7=-9^0aP2+q^@<jrU&xbaSCo_}jQrA
+<)J|f$%G4fBh=Skv>>AA*m{3_VQ3S7`2&Tev~rS%M-?Cy4kBm|Hn)S3A0_k~l50rSd0&RyKZcd|pY<6
+#ILpHM>;s%!Mf_{2;XQt*Ufh~NYMIz7=Vuqa40xu^lDw27ZD*tDid+wbm${+J1RRN%7ar%8OCk-Oac-
+DKZ_C&82l^aMv48UsOGblzjOGMyER4h!+vS&z|lbYLrgJ!P`W!x7by)H~M|8X&_#vc#jk)Qb_s*@bQa
+LhAgB<~}+|B_C4(-loi&5mkVh>pW6sfnOe33Z|gY49{b4Aht!%^b>m0&vPTtN(M}xL|WUrl|zzdL2KZ
+f>cLU0P?mKs1Yzo#sPiF0Q7h7UcZ)~v)RVr%4ty=^hVPy!uyVaxEXC!EZ%8V>c4<8?|E1Qb_HA7Rz_J
+$3g|G0~k9NQ}Z9BjYbscOrcZOTevDinLnzQoJ)8wO@{%}6<-32d+?Oqn!ZseflzwcKuug*KA{>fC=J{
+NGdMe7aVam+JBuB9yLa9As4P#fvS1-6yvx`qpzZHvv@c;qMX?zo=^e4JR!!rRP!AKkGzN5Hf^gtS%0^
+i>En_KPf|WjK2{|8+3nZn3sn2!m#YlVW@pH^e{HB2VAM{_g_k9})@=4&V%4S))q>-=KW`7xDh*a3A<@
+<NY6(w6F2L%*hGL$HgP}SkIIWID2~~!idgK-9I!GpUsMLENVpXU%^s(z;1*i3~h8t+`xg&5p%i6i>Yw
+(+<S?3hpA|~6Ny;kK1i4`qo7?Q@RWSx>)14J&lR}ApsPv`b#mfMrF8UkUR64Bx#DTSP=Fz4(Oa}@e^m
+YFt&wz-n4WBVJ9Lq^g@NW_29o(S53yM!NaD&%3k`f3Rp&HoK#9IMvL7kU)FN5uh_JFA4kTZql`15+)N
+VXRy{>?dXephGBF7hJ`pAODk@YV$+ZwMDx>)JRR4^Pij*>fBK6m&0il6LTeq-~bIMK@z0rzZ0+S?j6x
+e)j5@t8cF3VfNbemkP+$FW%B#e2{+n<RA)4fm42F^K(aCJ$L0UW)+hRr*a=@Fa*rIFSvH*1Tosp}5fe
+@Or(DE^>Jmpwhl{3k&P4l|Tr`#f{ApuOsZxXMnkop5IWEW>8Ni73u2ysJoL=)OC$n7y0@M*J=^$cB5k
++Rrm8HU+3zSh!QIw@V0vb8l*O+(NIBhl49#1rddgI_mb*L6M3A-cuFoB=8}OH`F7h>DY<N8;t$1LpM!
+v<F)&h`Rq{rFEiw9-@zEF$HDSkr!$SRYEI6|3?=IiWqm<{bP8yF~nmqd{_iT_QUWB(xs7>pv>Gph6GI
+gmf4#hN1M-jnOV;4p8Gejf^W5yS|sR;6V-Ik+N7gVD;uzw!zKmX$WC|W)>nS2qZiNDIye0IN;p6@gFq
+K%(Q@BUv7^@RZZ;V|D)fuE|pZ;>?W@1hR~vWNRIu^0SOZ)w5Zw&h*ju@_~$?RBA_OFv-b9#Q+j-c*8o
+$TCp-&cPOM@6{r2yS6>^{w~)DkzE5KiS}DjzY@aPOGmJM+hM!)HhLWFG$N$l{<zrQQWL#RZP2&*OhUd
+ZhLQI^K)h2F+}~+K{MH)Y$iPMt{BUnkh3UP_B>HXK&(C{R$P4+Z$@^3PT#ikDq9y|f{Kt~KU1_tBxcI
+kb3@YK98jR59N<9xy6ABSC3nXWja_0UyNdyQ^G@9=;RPm9^{vt#3mO}dgj3qk;3oGo96kA`~Y2L~+z$
+fXMQWo~|<ZRy!hT`vycky@2%y(ir8mjHm@!KEZvszAi*S^oBl~?;4-y2z&B6b^`2oOBeOl=ZAW!L~=Y
+V2+58>;BV-JMI#d`{9AcOCH<K;|B*;~!!B7fs@MuY3ILVD~f;D*f~vzG%&VQ(X;t7Q5FtKLb>0qV1e7
+hP{^jx75_ndv8>FzNh&NmIXx5qavLyXeyL<!aDJQKm?*JXzUrYXZ(3p>Lq#6Pb08j9E70UeO_g@GaQh
+G$br5z7Zolhd0R20tJh*r<_H|NSS`9UxN{pz&~}kXAe#Ix+qil^yo@sszE0pHyS@?{Qz!E7iOlUpbmu
+3l7^GQG3&1BD<2|;p+bw(jIhm50*j+ZKJ)aZIb3H>eEU`3px(t1kXc+~POzRMqb+1ZOF|HXP6&9OwOJ
+m^o93>_q^`7Tb=!CDhjE=1z*Ew@KhvfW_dD4PWO&b>RUTCGxD6TR|CeZW!bjs<Wiyz^G5$T$?aRk=;y
+Mi3lb98@^Sz?asd1gEQ!qly0i=prMD<*vO-T+@(L;tY;;K%X<K;&P3W`u^fJP`8W)2t&7vL-o5dWe7B
+d&7AM4C8Q8-2m4O`FkRQ=jlF27Lv&_e(~-xL=%2E>UpEmeBch`C6ed0=Q9pTuO!GQ@NhxOE{X?$?vq%
+cdRsUWC$pDsU>gpnS!xTFsnbjStl_rNaj?a8y6Z6KR@<nj2sB>UH}^K#Es)CRwo2u`H+cwA_#=DN8S*
+BV&r_9<3eo!P$-7eiMaZ8@qr$?$xL<Wj3UL0ElXm_D5XoNEBjy|!^;J{ofko0AvYd=F2NQWDI+R=IxA
+k(0bC~3Y%PD~=9FMoR&|MLUznlT}!i@F_MY}jT+e9u@P~))O^^io4%!6`xsIlAjo?^N;+^*0RUO<rs8
+KCNJmP`GT0c=o&v0PCiTj76WJKrXc?W6VwSoif@I}Wq@Sw7Rpuf*&B=Z5{plK#(z{4JbDZyQt!-knHx
+t^0T0jG%W_qU3EsN)WsC2!!t{_w>(|hBsi_6%L5KUvqCf4c-o_AIkRl4zozKqh0crTH5XwVY|f%lx&d
+ut0K#{?|c}%WfR^WEW0GrTk4Lc_g0z>z7cpAzuV><qPxCv6uk{lgFUyl&4!@fN=op1qx~&+w+lH1@1C
+FE>dNux4yT>_TR6@Cr{Gkvnfwc!9z`9x?@Y$Mb{@w%o~u8DitYj=YP_4)$wE2Y7i`{>H=mO|r|Z{iHp
+=)tzW!Ty1%AZWukcF!5xfFF;_Khw)p90z`V6nL9ZBN8Sycv-kzS>14)_v2Zv3*|-p^NYeS71R%;#{T{
+O+P3;THHE-u@15fe*Yz`^mUa^z%UN9+WOl%ZLZ#*z1Hci0Dci?dkjEbg`Y`;at$CTLtLz=^soj1!~n)
+qE&9LBZ8Ci8p&g`pj?!dAc?q7y9=@ke^_gniQ_f8^zDsumBwVPhvQvoHG_fU**oB(HCTu28kQj^CFw1
+^*6s0;9-|GaEfw$ji&b<Nz0lFhn$ELys}EUl)wTQUpaaLL&xiFDfrlAqlq`!hs!9tsr$}Y-vqG=J*5T
+`$rXL3N5sKb6fy0f|6VK+chKzI|DxYywV$gJcxYwpSbqDs)Xb6q+@|-Bg3no$~C=D;`x|)?|o6kYd-K
+j+StM|a19s-VD;3&Nzvtn&j40gtQlA6FNDK5hIN({e%Ti|ziD;_6J%fK43*~5H<pHJlUe1=5J!d?BhB
+8IPc%b0HREbU1jOQj&I4CFGqcB0gEZiDbF5cqL3YVLW;9iTfp7-gHUI<}I>^Ikdey@SGgA2d#CH8M-8
+kCTUXGggH|t~k8%!1>yF&U8w^ygHx&mPZG<eKOApC!MixhjkPnv^4DwF=#0~N?}Tw^;2hVsOX-f9)CK
+Mh(C*&<x}>OqySYPoLAZ&j0{3a&n`W&u;gQiL8}a&Wf3mx3*pP)yiiy&$171E$xG56XLdohsONA38ue
+h5)Acl-pZ61UGRCWkah5nkv`8PL!tG9Fc8Qo1!>QDajw<JxBDeNTnnZNMt1wV$%{{N77um30h}gJZo3
+Y{_VJ~%tWG${Qt0f(IIA?AR{TFci*|g>VE!_Q|8~^VB{HJ687KveU-#6Ne(2{*Oh=AY9|G^tJ>D`b#e
+(StqZ+Su=`6&`_D7#TfDtQ~PhP&q(gzYke8;Ik{F0DZL`<B_R%s{;vC$%?5?3QWCFOV1~_OZ5rV2c3!
+cMC2>?!481OC@c1ykRg6?IA*#?0*OFu3ofvSU~&DU~~^GHY7&q9q>0qNBo^g@3nXvi~l_mle?5dAO9A
+KEB^mWB>vF<{-hUFevic8SlRr0R0jTu^?pHRFa0Gd1OLQ&{{)qRZ(;f8sN7BG&spJB`5u*#>jFgE``j
+MZzh=aLrbeMV+PO1cphG_B0P{*1JU_(qd|VIeic_h8Kn-y2(o15msE^*UJ6$06(<8W*6O*d)=-v)Ub|
+^{%3u@o&t*YU(FuvSX13m|bK(?X^0jRlg+sA-Jtlh4MuEORQarEwkpZE7tE|1rdDxZozIoQR|j)pTKa
+m-37fnqr0X7CBHJDM--vA*_qCe(V1tb4l6+j``WxS0gpU88K*klR$-?O#D0ZP+p})QhS}RWl?pkeQj1
+NZ$FmHvKEO4LU~<ym7`fnm!Vw*yh&Ft0;nnoU5cY-jvZ8#}_4^1zs1DeE|@a$hKPTTy^lMQoAZ!pw)2
+c_{xDAAJ1_XoiK9PRKy(aYFBD9IX|dT{FjVaROW=y!56?p;dELb^0Uo)t63MpllXs9qtIWz5FvGRo~y
+)LG#LmcJX;b(K*hsde;##&?OD~MQ#1^He}kmp8DtOca@n{nzk)K<O;&VV>g#yO;zhr)z!(!&uqqJMNS
+sO{oKe5}QyZp<gHUnlSW|X;jY;$>Exe1A!{w;j%WOz_7O~6a1+Tzs8iL4}dozg7<!RZCZc<35uI5DMg
+nWte13DUph^}1R+@H=$<)KWX$4fK?@xm4n@DTV*;@qRdOdhe2JjnDYflv5qzX}eALetDi=n%l<$@0mU
+D?aq)fv8rC2(ni!H?9Vt7lNK{EKcOBe_!0>;6708JWn9%o_YjMJyMPrwQi78HZsq?<{hZIqDNo8tgKa
+XI?!1U;?LD6lD(S4ZilY_T(G?X*k0`M^@SgWC;se`FB%g+J^ee)bONGK9H&W=CNOx{kN9aFIQ3!C=I^
+B%@mpb+@^@tO-%7gp+Z&C-K9z6;Kd%GdPT9C@@b=`y@EtPg@a-8M?mlX}wlDObDBMf7iFn7?kldN)?l
+nsO+A`-;LHS!rIQ*Cf^oDb6zjy<6=)>tJewTT_u^)e{QlP|+&;A?x>35EM*EWXUN=V5*8=w3weA@d`n
+Ok23BqB7Fw%EVvZqrXykWBsu0g1QV_$On=T^5&v#OhrKcr};krbC+fNqc6Oa9};w`>058!uq-b`)VH`
+@V+gV`>xo1pqem!=$;FZw(s@5Epv9k-W}Aw9DC0M_?z(gznls1c`ARK$-i;`q_h6wO=0^qZ!r5%)#D^
+B7^;hGVJfQHD~f5mYj;Lp%Y2!OjhTuGA-tl1A5ItGI1`fO$FJT(@Y{r(rWYvcw_;I)F1+W-(p~%0C2a
+NZdT5N6a0o4rK}(T6Eht~qy#)dv5~)jj<tWg{6V_<tjx-4;chQCbdF?QqL9Yb0bc*iJM+iC~S&C$=Gv
+a)7q6@P<#8_^X>J`{xJhU7G4#kyVlQUf*#kH;Bvn*W%{dOnVNmcX%dNe`uN!{*IINz3%uwY<0HaOy(H
+qJ|jhYqW3La$QW9Zj`yj@dM6l^fytV&6*k9Pd4h5~)e!h(mciDBP<Bu9NmSF|ZQdK=h<H@Q9H)%HX*6
+5B6)dcaa>dkCzv90O}dL;9hkdIU6Zu>R)AN_m4U7!{_|%zs87<j%JMW8Z!D}YH3(~y0@jGW8~qvLT&X
+dgxWW%-qvdgxH%UodthKl%2nck_A3a4Q}b$NFLb(y&>&(^#J&Gpw`cK1-Wzds5?DHuX3BP+o&#gBV)r
+}eZ}k0a)coUhgCyk!)z1VUNmYv#eZz3r@fp5@dw%p_7@7`u!G0uLp!FDgz@x15;+&V8)LkFOiMo&TnQ
+EQ&)hZ)2y&lf_f}Fv0Ff}#s@<*e=o=mw=pka`n^K=U6IBG8y4DSA*HL4(wc}T($wsc1nndUwo)sr7WM
+b<gx(R5;nM4pfkmU`9AUQgdSpe~!U7r0KFjmarsTq>ZLIt>XCv0zqp@(rU-r9Iq|Gxaa**S-M&J?^Jx
+TWkyHA4AMPT=FxB`Fi?y5JTf6N+A?NB72)UN#Z}%DEcs63;i7uwndwMO9^4`jXEmaMPhdmzUbY}y%D+
+O=MCxfzOEzhR3;Vd1%G>X75Pts?<8%Px7)ZGykpdMd?QvLdyu<j^sg8i<nQU*9isdl0XOzVz9sjz%eL
+$H79@1X4m{esn6}}z+iW1W%PHYIQw-l`Y$&`Fylp~Iy!Rq)mn3idIQaKsdp|-<B2dEFT%F2-e&x#cKM
+OHu?`EPKfoOdfum27)>GEMR{Vl}2kNwX>%=a_-HxSdywx-SD;NxcJBck;@5BZ8#j(v8WRH@agYYN_-u
+Dxxw#^4FHlKWU;mT-Ka3vgGU$?g^*i{);eiTIHFt%ioMsi$z6J!yT&2)@s+CLzB3`{*6ij%e%BhZEZk
+gTV(1PH(F=mLoaTjwOiK50rSpT8pYZa%uE!9errWmz*DU^VM1>qB4XbaBu4PFsrL&s(>L_vqUG^^t>@
+>#vCp&f^FgREW?*@MBH0I*SK+%^dvy8uttDS5vygIq??|p_>^e?Vx1+5htqMT4XB?g<0{1>zdh2EK3$
+14$UvwE>v71LHh;OkB85E*MzEk)#|u_R(uPqeLMta$AJIU+h)XhRn%E+c_zw`n&Ib#}SAVLA2Vkhy6q
+k(Q9BpAyq5mt0`C{?O$mOleJmRIPG~8D~wc1>*8jC0(b)z4;@fCxha0v=KIRiZ}502>gx`QJIDx@T?q
+jq+k)Mvs^CfuXi04uby1q`C)tc?KG7kOqo(YrQ}qFD#PxI;4nh7eH}8El9Ij98e**ZLA8Bk6R!b&Au<
+kQh{QbNz*vu9IETVwAl^ydC4K2dL@u5a_u{$BxoV^G3zmRCJJI<RYl6cs}Z)7IWgE3VMLk8W%$zq=_7
+h<@~%pdu#xn<v_P{ju#@YhhmK>$VR}ZIAzy{hO9XX*n;rdD}C_9F>NJ~Q!arhB=a**j;4oEz>LPDcY4
+_9!D~`XNvUGvEN9qfL`yufcce(?5PbRlNO%7o#9Z3AdJNSc<IAtk{~5S^I`=!jPl`s#9aA6@#ZVmCQ2
+nPeuGqdlLvQzz57lsl+KWZ@^6+=D%bo#z7lZFY<v%Ac5%uOe=uS*xXqUU&SV92rZKfOP*)5ooU1D|DU
+?p}OL*6uJyASd!@<P7*Ptjy=g+<B#5W&tqAad_o-R8N23%(c2Z1;e%UH5K7z1^ESeiyvQZ;vN>hn@}F
+5PV0aJ)8ONct!W>oZm{Fn0q2<`66vD2lvS_TdCrPzd1W;JF*x*s^9&7J^$*~%(MF6ki`hAYwzE#{ohc
+<X4VhlVthrFk7HkE0sJ|t{CpO`Csg@5i$C=F+-*K*R!xA{^#B?X8qpqj!VZ3i*)vwP>zz8%#FN)vs0^
+hpf%O3x+lcK=ewAPHQp&SyU$ZC102GOiPO{3DaB{b}!tMtNEF3h+jM<iyXRMr*OMc)UnK7y~S0133c_
+$y9zWA?ajJF;D=6P(2;T2CF+M<^-mO`#FL-8V?MJ0%dS9xVuMtfKbV&)s9J$$;F!TN9l3#!#y3+Sjz<
+|~T=9-qv$C!wo%?Le|AQ1_&w)4nG5kCpTNiX2||r>^ykM`&8wj36Z0UW@}r&6rR0H5o<C67j{mE@c%V
+LZ%tvpg2_|UDSbE`Z-r!;%r1pbXZ5VIT`F7>Qyu%0TMbN^bIAEOSv}7x<FzwoCK`8VW8nN#W-B@kR6J
+l%5k%yg~!ai>a&n_-Jw1t{vZKY@JztZD;ALrPZZgK?jN9xK=DWmNFZh*6%&PYHGJB*SkVw<xe8Got;-
+gu%HR>SfY;HJYC$P4FC(6ekavrFKPbuC)9!M$j^ohJ8lxnns5rGo9-amiNl^Om#EK!SnzR6XfG}d1*k
+eA+*P_^d0Taq7gv@vBin(+}+?1onV+L+?{NgnnBdub2#Isp$lLV7}fP-tnwH34@=hVO6@k3xp@v^!eA
+9$ma_Zdrrjdi-5+Ki*+6AFuGBNnh;8WA>t>>F^NM5rv(lsU_T54Iv%pSpoOfW+(d1oai^>gv>2-|OFg
+Usj`_+i&)+$$e4Lj@vd{;2~^Lkm=9)pV+f}Bk%rw#*t?}J8N?56@rX=ff4v=eGTj~YLAQDL8b3_GXk7
+s7=M+KB+@t78GY0|r?RP1_e_`7R6|8!gqW*w%`)g1_Ldc2H2c0>`l)`<Sozd8{?+3Yj}-RfbcOXMm76
+X|T8+$I-A6AyK~P`^8T#XqCRgmT9Ij2Wde7dBSq8hc3C;r4*<Hf15z<`e2oAo}u&>>7kT6;<<i#0OE2
+p}N`o{ScEO~9Qk%j03i=3aGH62{u0JPIaAsO@W3hiExaH0gE^I0S;71yW6ufl+cXkmuIInCtmiq`}z$
+VyySirZT!PYMAjFIP@X;>z$L7l#aXIC9MCfgOb>$7S1M=p0WVHo@`@;m(&+b5#US_s&k>K8ZO{4Pd}3
+sQ@3rVo*ebzOZM)V&>RV`f?~APkTiVn1J}=^T|;KmR47`cdyGJ6y?xUbfbyD0KzL_p=M;)H+-=JM8Fd
+R17nCa@DaXFmQucDBgx`*XYl)UL>P(8y8Q4IQ+aNV!2Ri#D_cOR1g<UH=JkOktrL1>qO$H^<x+sp-fP
+`uI2-guRmW|0e-TrBc&+aC$@eKhI{ENRB%l<TTi@aBnqKD#Dcd0ydoHo(SIHt=-OzCsXIIbTDqB!cmo
+an0nUcXj17<HpqTU`w&=AY#Dk!QGtg~_K3LbmnIGeST07-+pa4$x`AO~{tbM%^vnGJ#Yfrx=q%pn6)J
+|%Oy8~;WF<Y$46z;XXN82Z^Y{}2%E=YL1HF$hKwjD|>xfVcl4#7~2w-8SuQ7K!8g=HeD+?2ah(PF*(w
+4x@Wob&Ghmz$xBnc=YqqBx*M$-y)YcgWj{Sdkpn<Vc8h`E*%%{viCl=2ROU?TCner?iEG9s`TAE^WMH
+NdnVc6>2{3m(<9!zop=ivM{m8k-TM;SLoSlu#qSg9EjbtOw0c)K_V?uShv^JO?co!=8>s&-C}IzLP;_
+^{W^|RXe01R<E{%yx@A4iLtvR%<pW0;p;E4kKHWc})Hot`;(RTsS9*F>-LlIfH^2Yk^r<*mNnYFx_^}
+XFAH}`%zXb+V3e!9=cG{JdqQ3BuZQ5qkO_iuyq{`<RqlH>nmn0_-{@1q&zdQ{*LHB#rOl!05qfyIgjq
+)pMgyCvKLqx^g^WR^{xHZ}eq=H6?|QEc5Byyq$Eo<3W6v&Z-^@F1KJR=UF*AtYf0;*I(_`o)^1$f(N9
+iriIoPXFCKMvlk`%wmDi`sU~6bU3ZFYL0U(QzjDmjEE4T%cPa0)M3A;UX`D&p|N(tIptTp4Gxb69cGt
+$I(`J#wh|9d<I-Gvz7lhNbu7kap<zG<@>0~wbZ#aQnGC#<Jobr*)bZxY!7C>H%&RJ)jT+NhUq|9Tw)*
+SZ03PO_)WO`if3?)iOQ_|Zk0vLI@ss4xm<|sjclc7{nj^jq@SSco-*7r#mf!Auvvaf6aUy@=8ZHtFTd
+ie2?wRcY^{g80f&q$eNx29aLW-4#&cRMuy=JN5^;KRNi^L<6t`w%gDx#A%Q@lS{$ieqBm_p{uD;y{dD
+%zCoWLAiGzG@`VT;3047{b%}a6gf!Atx{!9#5*3%(|I7|AeL}@5gP$c&{IM_8G4OIvMu=^1FJ?$J-B;
+8Sh5px2*5SiGM>B_<!qFey2M6)fK*92r-(ai7&gGx0Nn`mt_(Nh|{6Wx0+0X0`48Nts^8@agYLZBhz2
+&dTgHB<_B&*-u4rFvz>3JZr&Wqf~9bC0yp0;#z0jF9)peXM@=U6Z}wMZnSg(j0drtJGHN7n^N9DNMic
+KkQVi5;ChveHiU3y@tch<$OCkoVB!h#?WN!vfAW%YsLe0(l-ENb~|1#7#fntz{{kisuyyS6Tb~UR%@o
+BWY37M`oYrj|Yw?T4ksdIFEZhmHa!m263mggC3L7B$S-EM}okRk-?7P1g2zb7T%{s*Mw-`6lKn~PR{7
+K14MyVe7&Qs4YJCAer3*=B0?Os@mb?)XNeS#Ard_5wAI)@tJ41VP?S%Aebyu;JmKnvxw)lxkDpKD+nM
+K}g>7Yk(xLKb5PL&^P9e&J@0p11{UQ6Woppmh``n0R~M9+GqLY(u$wY<6}iWw&(8xpg<_)s{knURRk0
+SK#ASR-f*3opW<HD3N)T&n+Qpcr|;t)MRzPZj04)MkRQPPUDU6qQ;sDitRM}yIi5`v2YIE4Z5CgT<Wk
+D#UYr-VsTid6T=&)7G?}4!$}k}>IwTB59u_nS!bcv~MZZ3<#@C4RdCxHWJ^dPzP8U^?f8nrDQ<+g^s(
+*gEk33%jAL99E<gC{*a2uJZ9yDu;m%gd?CbMAGnIVDPZ}-%;y(&@I&RFciA%cgnqool^dK0?%d)yPE{
+Y9*v*iJNIg2-F+0;3^slcj&7{g#xFiE^#s2}g!!r#-B)C6Ycv#M!O_ap_^eD`}Kbq?ly!7**FU&Ir|f
+HM+l#$UEv8fp&I|aYwp820#xVY&Jhzl^>|U9W5JhgWoY1c8DxGnVhGsOBQlQ*>3?*=&K0mmjO^czV)t
+Qa~^iR?bsTT>a0e|7c{7qM6h$(&mF5F4fS+;X$pL4JPJ+Y7P&y|8^oq_QOrmu80w)HVkdZAQ(N>AwJ;
+!!1*UKb4OqU54t{?0OjQQrnSX@09qHqU-or$HK(8(;S6bgXWfx_f_GF3fbj_~}v}ccMliaLhwczXGUR
+2|HF;h3yRY$ZA%&NF?OF1Yqu2i&i$!$-@&3VMD{<-Hv=5d08o3*C4t0<$kILYn#tPwBl&QEWu?9w52;
+cV77UWd<Zd31+CTny)-*H|dS92ppk1|j&V>oymUe<XWa?A!-C71wg;xW>N^z7UYR?$*hnrfmMxxO!+Y
+5ae~d5KN%r-fRA6Mz!v4*LwKvzkd5r^%(!J|MRzFoYq~wnY{1zZ#E7=5bQSs$61zPk)PGC{)a<-ZytX
++%nx)RUy6kkAUH}hAnk_WK&yF!?kNjmr68}x0*By3Arg@P#a}1lasarqx7rByR=q0TCv6rDXWogucZc
+BS5MUHY>1|_YIIv%z;v;{ujzDU<02A2YJh06aX!OJ0J&EMIhy$e8-mHYcX+VMfqk*vnHWC5cgn%&w96
+$n^LF64n1<uF@0yekhFKa#I4kY3T_m5J^uT>wLpDc*tZ}1iRl!gMbQQr*QW)abQx=NAB|86bpcO7EQA
+?9k5wpWs-XfFGPyL_@vnCgecyKi=9^At=_(f6tAfy@->ivubnWcvpL-{v{2fSbB0K{eyHQTu)4w{7$3
+r#_2gtNo<EWwp?ES6$?l3qE!Zp4fo$$;}TC&DHiUgza&w>7M5Or}7AozHJ$_P3yV26e}Rc0G97<&`tZ
+U$$OwaGM&92i86lX$$)+!%GhIaM<gCW?bP8qP9`kmBcdU}87IqxY%syX#Czti=hjr761fn~36fFH?5=
+7&OJsmgSv;wCJ0ntyOqw3PlbijarS(-I3wc0g^Hl4cM_y99-{aOptVs)EUc^ns#UgN7n@+POncJ5!;e
+mNC944r6Bor90rIYK;!;Y9N>$|ga@7PD`W?ChNIn*D>`{PVfQRBC8?{=;i;O!&DLRhu%mo;NDYTlh%W
+a87s=It9Tz_(jisP`IEfG7vFvtNnWd5Df&gq-Bj=I}b(jzprD$DTZs*kh$aT`aFPi&>2+V%(8@-aW~+
+RV<i0b%N-~ok%gR9XtvcM@Wdti&jY!UA&^m2%{&Rn6&-DBNeqsa~V66>24giTR`s@Z3+YkV_=>m4=2_
+(9YQrQP89~mlqqp@bk_BFKPI6^@?GLrr-t9|qVb!{K7`lYU~ASXD5!Nkq3w;^?R5|$=fKSDp)Jf7)z|
+`IlQk!;omCp}u~xB8q8)OoU(_>``}JVAx{XRu*P-qWgF~ZgVfA1o%M#lY#<LlARs&7rq>vxXLAlxdvr
+q1&hfV~+7D6FTJJ>v_JcQJ4V4xNi$HpuYXSr!>K}Y5qm8p`%k?S65ioG9OZe20eohGl#{XB)?j?Xa6X
+%QA8OI|BmqGT6Ez=!_S!6*FqI^`7J;HT8$&vGd^MHds6O2B!;`b~|or>^+q$$-L@KmSwN>+cYR1vSRG
+!Y(d>9FOcwlWcTp=5@F~29b+CWUo01)MuOAtO)gk&r3PnRxR!=SMqf}b^B5Y4t(Oyu$nW}VSGON<rN#
+8WUyw4V*X`eWt4AD7t?#P19W@hJEBZ9`@CDiN0(`B89cP!8}idjIB8;Hr8~hah$*>oNz#QoS$jqCad#
+ILf1=MOWUI{06r)}Dvb=GFL9)sL%1gkBh!ic=1r1;6#3=R$46B7^bK6hwNqSJuokPw9{(vZyl>>2zdh
+K~xuKs}@1v3gy<l5*R#tYG`5=gg)eP$OcD#Iixh57j@yQQYN-XpPtFv}-eg>CYD+6g70k&OQo5xvxxB
+e#30C7=2oadM6l-G^0$^4kjC)Y)M#z_mVgj|RHoCT^X0R_e<&LpWkS+;h`1UgN`F(1#whgp&9ZdiM?+
+D3K3IAP-^^9E%}oo_hm*LfxW@3+4&ptE#K;oGab*8ew>{(#zSTYGTh0Yml%vA_}yloX~4#X%mv~?Z<L
+wPoe^m($O(cPkLMqvgygE=Q)wLM>kx%aPf);Z+CDgKlRAV8;n_0((W+bRtV_@tKRV#KA?LhWnF+al;B
+;h%Bw31Nx~wKuDfTH;d8OQPW8>@;@jZI`oXC<n3%9s;i$elm*Y8svcWLyZB^k8LL*Mfm|R30qXYzz;z
+`c7xBG&iaHbhdRbai61bbtDK#s5Ttk7t&t=Z7#GLc@C?orx1@S9u7BqbM&mq@Nox=tiTGfQV>3hDBC*
+geY;lUo9};N+C;^ZM!s<ln|=pOT<T-sOGV)?+;W7Q}7drFGoJfMMYmJN*B3&^0CCegbR%PmK48*#7fa
+Kj?k11c6fo`DK|dh+d?i#E8s6M3*E0$l7oZ`oWJtfhwjf0gx!&@YB~_F7bx_aL`l&*cCEBwML<Uyp5q
+iH5T?(7zI^PB#2g!G)P3V={rbF{Q|my@~!+$(fk$<2P&vI=#pXI_>9g$`%3&)8sDH8&{;*^Qr;v0d1(
+43Y5NHFQXo0LLCfv*?H3?<ii0lKzkqH^4d|Mq-vb+xjYMtyr%snQe7pV3N(T9>RD4^00elOOSTJpPrb
+?6+0feBzRUXbE3F=;_lP4WO#N->CQ#OZd4;|4n{}Z>*A8Myk)+X2Bbqbez2B^&B^EPlWZFs0kt{<@r=
+q<**au~>XnT_);c=6u0n4bT!t@-oRKW7g8cc=b2bLhV__02#1T}1@?iRYOA;5n86&oMA@ivAcQ$xENw
+t=Bo+$zBsO-pYwF(LJ}n<S-OouU8w_Fd12II9<~Qo>Y!>M$V4z_+^dksM?%!QLTfMV|#~ZY#fV-y^k8
+D?4w$5Ah%5B{f>{OqPcs`9v2#xKOYYLIVZ7Q>pqGjQufb7l$n|y?R-s_HEorH3@LXToKsq8qLg($dsb
+j`ugp`9%2j{iT~x0KyvNRpBO+2V%6C1Z(XNCAk3^|Xv~wQW9o=5GUl7M+(*8M;WI7-xnRD{xnZ#_SK2
+Vm4&HL2(k|^0^e~rd`A2-4S_BaN8r36jeroUVuwWYY*GZc$*4PUn<XnMMQUM{LgnHMLzpVX802zf)>*
+36M64|&U#%OA{KCjAcbZ7<F1J0Y;`={r|baAvLig9FOs6h*%N&H?p2;nr+)d=JGg*=1L0dx^|na{6*0
+gVH!3RK1+LLHYquT7!Z;0n+AeO$S%ulwII}ezWc1%G?f6Z$HYDZQHY3zHJXTAj4%2j{x;CC@uLrr#L{
+4?DD-_nKN(ys~VSaVE>7W9lil7WT$!idwa1rn^JPJ#z7}X969yIY1}q|;)DN(54kd41%D^y$3WW!w(S
+=%V8y8p*dK!9pafhI2%>?Jw++c#5RKqOhtoM}-cH=EBsoA)tpR#y+d!}HX9B&aqJRUylsGOe#fnqRZ&
+NqH3G08lHR#KGQ{NBieZt2xN<S_DZixf`pTPbJTmTI4sp&R?dpv_3+dU@pcQaGI-HQ4iu>B?y_dX7KA
+MoAB@=re%=*Ov>e@wO94p9ByuatgMn|uHLohlu41|x1Ge>`P#8(CbzEo~nY4hA~CA94w<jDTC%7XK$F
+FkCio`_+_#jFO)@W!O&SCh)MbZy)3N{qz~|aOZ7iVAcMC8%+s6z8BE;#ds6LrOJ3Wt{HZ6$u@nlO}d@
+*V+-|wB8cd!YI#0SEFuX_dbq$ZDCa!J++e^5TsODx6Ec^l2luNJ!11^6b&0<%_0U&q{Xbdip|95Zf3n
+m=A8WlL@Zu<wVfpJgrEsjgXNbNeP0)KQ&0<_m(!PUQ5pLsssLg@LNy@dBYUXr!1<pfVbZR*5vEqm>7c
+uTpAv?r=IAO@L4B0AN8dy_}5lWc#R$@|kyl=0ZKeEmJ?h5V>OP2L=i_}9S++!KH#Up|otmhzketRS;Z
++}))8s)tbV)qnoX^g(_u_!tmN&9k}mGm`wlyIthXK7!3onk1Q8x2uw?O|~B<5_y5zD0-_9LZ2NrQW#<
+;BztX9cioe9w(1&#(1IdysD<BQBHX|sT8s;sgsHyd-nWPuQN*>RrXO_w>9w=pG{njsj7_*XVrS7LX(u
+KcOSYDR_dl3tT-*Ns&1)L${bfkW}WPKS~|M86{h(Q>J$F!oiF>Z&Y9x&zYrW08GLNoVCV3!-->_z=GJ
+MI_f=kx!|)o${F?t(Uw_Nn{4)0c`b~_-ynE*TC9nG5lxv~_DEsrMKNmRszje{yD<po}^#gXsG4#uXAi
+)AC8RsCHSP<_@2of~5WekAJ7y-a<!T_8LM2^3vJI;V~2pFcoK)w$&9?+l@8?Y3=8w6<b8->}1V*%Z9@
+xD&{I}ZPIh3Gr3nY?=xNcP_e`{qf*LF-+DfdFTLf&?G+rcP!VkkE|Zao6-++p*zvprb;Ah-plNv(We(
+?F?v%`0L(-6(j`v-@C&_E}Z{Fvh<DS*!l^4@=zeknkk(=RkC)D`+K2-y5WaOQ%;j6TlW)Iwdu8L<LJ1
+icneZ^<Jsl!P~!6Kw#xLGs!f`7thOCfwyi=j7TnUBSCeqBna9g4f3#E^BG2>2w@M2mB#%%Q%7X!-k$)
+T{{(R8y$9%*~QIIIKR_*m2PmA=!#<yb03HeD-K=w_)oR6Q?79#t;2n4PI7SHrWl;@f#eXMO_WxE9>xM
+!-h=G*>rh_;&2j+-UeR_GB)x8si6BD<~D7)Xlt=dgNhgN&z3xhUR8c!%c(Q<9|dTe-%Vv@&`M=Mlt<|
+2W1pp<f*I13Ldt;2h!{VLWU`gWK7T&G5WHxZIl0j~j{h6GjWp&BOhpSKah-^keJDj6}nTM`=fT{mS_b
+)cOL+<8`|)PWsai70SA@Gw0fsf2?R6?p{UD@6(rTDCUk2`PFrz^0eW2NqVO6Q9eU!C-0p7q(5q}5D=r
+r(3o*Yy%qNp?yPiHvgTFdj0064B-f`~1im7*KA8PgcTpu&AuQ}8RtpPKH0ymFwJ*`8W+vltd8fp~%!y
+az^)O=9D)~Aihg@Af<039ZtvWJ=cPMnh<@R;Q)p#k#L$KH+8xe<9W*edygj#!Qa0gE~z9$aeK;f%2nC
+5uu!+5iEOFT6LgyndUPA@o^RhtjO0w3kF=C@bq8#=cd4*69$P;x%Bfo-0<N!!=-(S})&ALRo`(0x&=@
+@KD>vp>3Eg6!xcL90cE?N#w(cY5tErAZ*S^;jRC$%($^nz?;PlKJWLyV1^^oo9>(PG$-P((PH<KGus^
+Qf-vL=1|#Tnyvm?5|(f+Uy0H3=JDDOPDXp@#)}N`$mp4HGa{*Z>|luTb_k8I3a2JL_k3=S#GY%1T(kS
+SkfLramGJgF?z~)6O5dOHQ9oz<NIag0_6EDe5t5Z=3HGJd3$ZJ*)V|wn^nhP_<YAGh{W5wj^I~al`hk
+q7)zBH!0aafu;^o+rg%uaNp48j3=~WpOv5`rbD=p|T8*3b?9QZD!D#ysKRv_l$PBHm#xMEKup!2<d8Y
+pUe#~{&~7GL0W<?^87>cP_dQ(I1My#A9CoDCOnKeL(qQ0B5(NRQJ(&iXG<FynuQnSQdX|AS#ZE#+Sh@
+t2uGz}3%yHn+|G6fEei1_WRj2t92E2hBlaHK6}|3jxsVmu539-tuBJpi|jQC@2w3-(emKB=TY$1g^H9
+bPU?KLAN*iF0KJ#q+it#7Qk+Av&vCWCWF442RFlrzcmfe1k?&9`M*gDFv1`?C<OxYvM4Bb+-&SNRlt0
+fgI;v<&GJGfV1A&GaT|*GOS72+vw5}uDpOeeWHS9=m0akPyni#9`#_TS0V3OmZ}H#pfEgl}{f^B|_pR
+6RVcZ1ShXP~+?BDdb{-T!Vvoz+dEo+MD3*_*i50P2!TWySw+MZ8_&DYcYYPQfnneJD!g?=^Ne`2)eqS
+J{xXZz%%$OW%Hx|PgTRK|p%<L9)0lq?*6vJp3-NnN=SVnx~kmJg1T%M!$;I-;89;#6m~^2|ExH9eVk?
+LOcS)hULbWI0Jnnm;Q^uFB9^<jyJ7(it>99br|4bUZSOJv@z>l4)z!+}TCA)tJlkqccYnaj2gApmYrb
+)ssGMLn>8w+yAF(6|l0%Bj{Mn=U^TB1m66FeJk%Oe1Z3QQ}YO}%C7^Vp4w|G5;xh~9rrw@vD#}H?4Iv
+;3{3A<sE;uBRPGZ!GtnDq#}0NZqr{-hCqwf(YdK0!F7V;0?a=-O`-gfhExPQPT?wDM?c;~=aB<JY)hH
+99CXUOAqaR0wxTv2->s@g9wGH|^WiZEwEf3cwqm1ymKjf_|D#xY~#PB+OGh)!sRty-igB9S1tS{>1p^
+9AP$2IR<dTMq&jND&cQK)w}#}p!6$OXuh`u%0cK1k}s6+|ms$J3Dv4z|IpFRYBD-L!WW@u3vy8W$C}w
+@D4vTl=+}hMv#b0gCd(p;_y3z7v721r()eT+dUT5VoN+S~ve#!@>TsJ4y=WCPX=1+m!Ard`-zgr7#g{
+tBMtS-?vZRqT848Mv}DJ-CyC&Uh{DG91p7_Jc5&lBn6D2WLW5u*6NeUtaKqhJ18FR>M6A-*s`~m2PQP
+ltE|=u7evf$;=bb6P7(aIpsjv~%9e0TyVt2g>T-QWOK~|vr#c_bt`Q;Ys*oq@<ul8E?dN&d4Yo;&G@W
+Vw<;bZ0-!65x|CcKXw)s(g`J4N8*@oW)`(OWu;fe1$PB%}dw+#ZEBUATjSb(h&IP7~%_W!_Gzmtaj#{
+>V8AdE`i1>_mXL~KTZ{BXH2P|}%Ez=D8AJPdSsXZgR0uU$a00KIoi3T)tJWI&B3{w`5xKv{bFraD32y
+wfQTk}ROTj|CnYP5i1R6n*n<r|+8f_lVg$QIeCO44!%GtZ!ajR)B;~`sV4R5Mcg3Y%xxKs~E*lkai(r
+aCh`u$8)oQ1@)J<ECaTzh5v#ejL&{D4!SQ&Eo2JjXFw|5kf-P&pXH2B>3?(e(ibm!FJkcIqx5s_B;;c
+O5Mq0CSG^_AqG0;YIJ~WjgGk@8Z2B+z?Zt5`D0YEFPy2b}9Q{<b@3!0zv}CGm$qvF=n*8a($rcGJFq6
+nE9fX+-Y4MN3_cA$yr?yrL1ZqGj=Qm|cG`Y47^jYiqrso0%@NW`iez0$G0b~dI;`cVS1Xp2^QMYgI;`
+ziI%T5oU)_}Ead_8NgTl5qgp;~dAOj(zru_&Lb(N}tCO<Fj+!!Ya-n5A>{T$~nZGrX4-jY=~j>vN}*@
+%TWED0a@AGiOQY+&ba8a|r1km{7n)xHTQ+5+D3?mZYMDAMuVbAL~<+6M_mJ=QzR7cmL5+Q`+?L{hs+F
+HwxDQmCK6*RTlqvH8+J5GnG`y$Xj89!g8Y4d->T~!M4OnMRJ|1<3-5@IyoJCswx8g604m&hBkC-x}r6
+zS$&nedbe=V)fNw$`B3g{zp~^xZk|lCB$2v9b;%y>vN}<{kedEZ8By1rp?J2O*+m$6L?uf|<7{)>e$@
+;oa}W6uWi5ZM7)MhHkxU23K1ez(uD^1+e$h<u2ULN6qTe#@hb8q!D-UVz6+>wV7o#}ax0G(%K`<Ws2<
+vtC9_IOtQJ3s4L+Ne5h)wUV3fzsl)J(h6rDKL@cY9se!EiC=W$jdz;a(k|$jG{xf9&i{`(&xG{1Bzk?
+NSzs2iq6;xxiI1tv=oG{60XZ=z<pm%tvdj2DEagUy+BE6q{cr;bLPwnx&^}#X#c@YwDX@_KFVc^HsGD
+{DVuTL=R3IYT)qoWeto^J;v^+NY~eqZoGirKaM2g!WU6~L8yNb*wScOe%BY)H4NfzM{7NK^^2Vq+=NH
+z8Ff3Use3&>V;9LwjJ-aCNSirTJEl9xZMq}xUyM6_!pk_w8vV9LH|bXB8M}uEuD>Dp-*&{*ylwN%O#H
+@e??Zmweu=y6w_`r!ai3PdY27f^;{<HT@Xd()h9M}rnXG?uCUC9(^~+9)|F7ooX*2(u*Z)gny4f~Bt+
+KgG39xgU14XmI&njZzY60fW0+4*A2?&9vUpt0H4&t5^37p97Y~(!*%K*tZ3I#T8vyE5|e6lPCr3eTMO
+dN*&-mLa#$1nv3lqmj<lE4l!$(tyf1&&_)R*R0`m803)DsLv0DnPa9HVKS=OIW<MEH?iW%K$bYiOm^&
+ccR6Bd7DQ6%9u(fsBm1a|G}90Z??_$Cku5%wFNqgyzptFY@})vWodzh(ImM!g-6h>)o6-50e0$hv{@s
+-#RW(tI}6#Y5(>SIW@mo5S;9}<2R=6+%1@TbOgo1QNEh8tuWdyH!log5hqgbe<O;~3gY_KBuM<`Rp@z
++jw@CIjbL9Hr%N<4MtpqJC-&*_MO%{;VNUJ4YQ1gS7?7h+Se&05iXaA?TYUY~0Gdthns*tpfUrz0B%}
+jb1u{zTagrJ_xnIiG2vx>V)Fo4Wf9B$8P0~+TIp@82{A$i$uZ5sd!s`P5z05$1`2{vT1J;acmKG{b%G
+y+uLmiI^fg_(!GwDWrFake?#pRo;J#glRNVOW?Sfka!==~%ULb!b-1Af>|!Wv8p<=bmV>x?W2tsrIRh
+`xfEe9xgd%8tT9oyKW^bDOL7bW<iP&ej%89QIUJ%u!oAg#d{cua}tT*#LjG)JKWnlc{vGBi=wW`s?wu
+-+IQX()2J%zEF4Dp@KQ@7PNi;ul5*9qO>VI}2|G!{)zI`DQJYnC*yb)vGf`an{i3Ou{a_VEKMnF`UlM
+v^Ui{H%A0kAPHWeSE<#fl2t9KP{Nro7=r@uHOZR|u%7;kLuJmSmpYISwLUVJ;gv$9p4T=EgQ2S}Dg6}
+PCt36I0bUlZ<XjeEsC91X3d1~SXIr7_!=Rl%Pun>Js20$=^oHRXnHX8m`Q{sWiI{Vof;qgA=JoeR2ih
+sdn-c;PTPu0J0?xNLHMJN5A37?TSH?nQQK!^vf7PUpy<`R$D{uE8bqIHfF|2}<yNw;jm;exUPtp!COq
+jx&86_vf4o^o?n|pz(J5j*ZiS*3GKP1ouLu)<Rk5elz%Kdyq<|nmIeF>R^mAv)1EkxRn?n1wxeVwWbz
+k6|c>4_n7I;w_GcD^3v78;OEN&uSY+ZxW?Z}y9uS+{o1o+ajvPVWFYUsdtIVYojtYPUJAzxCJ@{#yLv
+mY=}NpvbUz+A;a;FK2V=Sxhm%B{_w#D4&kgB7nKnd6_iV~{MciF8cJ<Nbyz5tP(ZhU$W<ywvO#{On!U
+$|)?v0&gRxI;)kpaV{uK}{U$L`iy6ie**@%*S{LqC`#W6>`oJ$IAz^!<o$5a!jlxTWoMYG8JzM>n-?b
+FLd{P)mo|_1;>8xDswDBxegNZ2x)OvpMni^Q!3>xApKguz5Fr?|6RnJboVF{_FlP?q54(!{`i&AuL7z
+oGkqxjr4m4^goXBz3rwj8f6HArZEEBej+5oeCZmqpiKhFfayfuF<CSL9&GUuM19j>C+|-0g8Fy-wTea
+Yc6c{;7!X{)>ku*K-JBi2on`#p!I;0hCllbsg0y8060Y&D#9%iMoyCB+%e*_jSqd~-Qg7Q0_(MN=<hD
+VP1hjlm@4ZC=rk%XmMdSD#Tz)r!Y@-2Ry#S3OfL!}8U1Ry(E=m0!=GB7vc5D$tBABnqKjZZ${v0=OHb
+l>v-?*|(-be4rX2DI@oV9Oqq#}RkPdMPtZdV{fnhGRu)XWT-tBKm1XKRWV%v%5Oo4I+;H_L^9T8+&pz
+2m1q$NXov8G1)!KdrNPd6__Nso4zjCu8W3+kTk-yW9PLGJP<6=zlVOFnj2qF!tP;M7q1zi(&6t#SCX(
+x1NyiO@+|*`}tB5!l0!$zS>=qP@Q~khab_KI@cn)`60C~5)B$3lJiBl+_?}97%kDd2qNMaqnY|bXx6d
+50->bM6@Klwqe?a6<Q~_cnef9?RWE^xyE!RvN^!5HjP<3_Kvovgb-b(}NIlBuwz-}ChO|UIBgkV{H>O
+}yYr(S;T5wUOi!oA}i#`>ld{68i-``)_4l0Fo4QC3k%O&I2RC{5on_trUC`R&X&QmiY4?4-%bBPwGK&
+pe|vpG(O=8$pd>E0($zj#_msb+nAZl7wx-1b9J#+j<lUVmG!E`Nv52)*;D3+r@z2e-F0KPVA)TgvUJo
+g9eV5XVEjV|xkawKQtP*&r_DQPqqmjB>OW8(p@j52>6iFHpN|!(uK|=84hA%wfW0D9M>tD)dOX>?;;|
+M3goy5?4^?lk(a;{{y?OJLT|HnBZZAxhC}L<RTK`duS`O<&s0X49}Fv$}0muuh@>REDI0s!L#4HQa4e
+|EIkhH{XiTAwKn~r@+HE-Y!fY{`8-;L3_U4R>6jW_bEiO;R1u>`kjcIk^ZkYq#`?TRM`9nAGwG<ojQC
+`a$S_?O<OmzYkaSS7K}??sIZNhl;em5~Rf<fbzvha~u!cTfU(;(hNn|5al<*RKyqm=7uxo-FhD-T{hM
+I%E%mJ3&xzW_w>q|L=XovhBq-ab<`Y}i$8>d?AwGOYY^r>5+e68lzya-&AIKN6ItMfNCRlc0%8|V9Dt
+=U6e<VrF^bz#xAD)=u)6ReT@x((%PKJg96at4&+x;iVu)%9OmXrHqBkosq-zG8Bzix<YTG6q#MZ{!<Q
+D?RdoW(ch`#&i*pBT~}{)C^W*)Yf%SayX$APFmaY@z9X{*+Tu1>be-QE24GNaC^r5B`bGztcM}Yig1h
+-;tGYgv`SCB^@2I#y23O(akH1g3WE8La%_(6tS>sg7h?TYK7BIAs(KfbHIE#y_}LNx<eJD)PC8BxUI?
+W%_@RlX5i?lzYUHU}I;vD9SpT}%#=WBhACB6hKsk%^H06R^*ao`KtsopdhSNKtRIP(C@EmrOY1h6}+#
+q_?tK|1>h0H0xVQ9>23xq6^;X&+Tfq1<lC_*;$eD<ha{c14v6i*WNvL!fyD^xqFmn-o+u#ZfLDOEVQQ
+6G{CqKo4&G6c4xVEY0U=~HbZ^@cqxW=Rbq+iQ|S#KT?KdyWwgyUT*DyU8~P4(;&ME}23nc?j42LnjP_
+K0-PXv3IP|rc&yMa6#0hTHCysMkZ|;jyE}D50B+!sD}gR?}>KGISp^l*WH@8NHY4+Sy=abIxsFB+3r|
+#B$C#Cs_?~e6~~G4tGz+ZZn6tRIY0UQ^~gGE7cX&1(UH2_?|sO44afb<$8%{p#=W?%a+S#k`Qh@>ZZ3
+0dI3b6oU7TZYvd>vLdG!7qNGWWo+qHt&gN*L*IM)@i9ufaZ2?ygv5yp*v$y#qxNx;C^D--poYRkW$@^
+~jGHc(~M*BVex*#7UE<b(U$7fJemaL(@;`9JUd9<JdOLgEyG;53P|EP@j_LTu2DWf+zwNeZV?gnSpL{
+RzS0Z(j5Kos*{$AQ6HRAk|KhAjN^cHOVjp@YGWDugHGz1k|_T%)5vUf8#%Z_JA%JLxGSA=;*-zoxMXR
+1qnhwK==&V(BUr-+y=XnH?<+lzS)LIFb9yge^=unZ$L=CDS;^lWdA>8O*ZHVR8dk;)`nnUI}r4w-taP
+itFye%qO-qDZkz#vyZoNqaFhie$66%!mWjo?LS;^`GW<GuFGD|NJXEKVP9}f)On4l=@iqVyW5ywR2h<
+ebaS<Ajv{)YnaR3`hXUG3Tz~mb+<A4qcAo_Wkp7V{$UHc&a*+AO{0Z-_gX!qxE5uz=MxcQdfqD3P5QS
+S!&g*~Mm<LKC)MJa_${`Q$`@xGg`;>&-?i8#LqWPmsZ^rt|E88|sf&{ifEi?&+L{W;zk4!agdfqJBHs
+hNlSBRf~j)j9eW3~}~D$=#dy^bq`dYOf<4>cK7cI;ApV&NVNjDI6NnwbomGI$hGrC)tJ#pZOYLMO~>-
+?Ww8QwHKA_+>q|!0uy$9{_GB7d5`N|dvV5K2QSai^2b+3n&(bAh*m0LWpP!jrh%UBMR*);DG^L$Nmaa
+S%yD|cyVK;4!}Kf)bC*2OUN;Nb)Sjj+?Q#K*EK)S+$X!+rLXJLjLF5aP$FR1fHK(<?icM{=P@PMx;|(
+{iJ!V-35xKG$#>Ry*sYX9-?r?Q+S{|}{uOfB^#h4fUc2ToOZi)+02KKW0%H4}P?0A;0$R`x=wmJMjp9
+_7*pIdwulY`H{<XsBxq&7c|k>mPRRgMcq?=RwQsLtyj0S*X6IHu=)*+*9+K%`bXos2Y}UT2Onv@rd3S
++`1_b~^-xHaM6n5}Xgh#5A|r-@RTXcFB?%hIu1qWU4!A;m(A;vQU#WTrUh$TfqxE;C>g)*AQA`5q@4c
+GvfCFytjo{-QA<@gARy+u%4N$T;|()Ci%%mLr-%rj#(3f?)P!TN%3$XAe7f$utSSOHoq##84_kxMEHJ
+n9QMEjb$c%nOT^UtekQXb466fQbPqYOX-ZV-^ejOI>Y+DSI56rGXgB6Q-js$2QQP1r?hL<1ZO@pes~T
+)3O{86%$6DmB-~3&#L{n8VGSr;;0*9%BHFlF)Oea!>9i2E55=RbhaCpkW*d8zUtfzb0?zC(zPHoLo(J
+CmZ?noAN&d2SgwxYHlwe=+Z{faW6mNg2uU-Bk>)7^R>VvN}zS@rxku`cT|ZhsKga_h3&R_QO~zW%pd<
+a^Dp|8#-x4Ix2(N%Q+=z6G6%1jyGH={wm?f#d;(gF-cue#=)P?;QNsVTu?BMb(>m!{1yJ1P02l(*pED
+W+>=l-0Ta^g6%8=XqBjsoIKDN`Gt|oQIP&;-x4GU3xX%bhw9in0l|JS+sB|o6NrV<AZs7LJ0F42H47p
+YGzsRvSyvhZ0gCk9;0dx7+g<!+(m}02^JDl&2H*ITL3?NL*`Ms$-V>#8vk=awL9_hLk59X!m|#fhsmF
+`aNWCdsJ}pT7Em_}c((!)Yz8<G=E}<VQ(0ot<-6hv{I<L~*6w=SYd-?s~-=72hx@_$<+n$7*-UZp*qm
+QP@^@nX(*zoMncKUr^|KzryukP!g+!plJeSKZtrhQr72132UM}3<H>f1JREAq89?4Qb_6lz_x(S(|$_
+)4!vID4mUm}9<9EzVp_{Z^hY(A|95*0UtY`Y<O~Y&lry>qhwLEi>6sG<)O)KX=n(c7nDW2S+KKN1ka>
+nENlz!utZUoxlx?4YDI=@owoGAUDLcu+UTEZYJWsQ*XP8_B=;Q^KcC`c4`hGz9D#F9d(!73DCaYQRDH
+HrcHpqnk6HLzS`f0*y_$HmyL)lL@=F%WzFrf+`NtIoJS@njcM0W$MNJr`lBZAk4bcoc*{(?jN}ws;U0
+$*YKB#)-^J=etwwOY5Bomjii-}**r7j;*x@V@kyS(L`jnmz-V5qr@SP5SJf8X!PuS%AT)S-A9m1Fq<@
+nK)mDYN91E;c+Z(WEiZWHQanh^Jd&lFq@Uc@Jx+nM6=JzI5#H}lz~Sly!RN1DUuiG5{Pk-uvwMhe8b%
+QcUr2?w1a7_%P_WsU>mnC_!~yZx&nKVsTYi6|-qD^v(qb<|0!IBRM4JLRxr*h6n6clBmnt}vurs(a1o
+p?AEw!}Su)&ci{UcT&65yQv%Ruqvu>cf&EOw2<z60Q}S=Y#)&kf6i9{ht6xC?H-Smv0S6TwR#=mqNNj
+}CmyAwH)nWhl(7#|zj^J)>hefuNf&2(m9F9|uVEg-C~MliPIJ2p4gAzb;{#y_is_dm!?>r=qV*J4mGe
+lSNn;XACisJg)03I;mg~-%2)((ow%1vDbGgj_SO^^YJpdvR2E9Bkjy$z8c2!ODhQ!jrabo&q@*nXJZF
+V+D9&_Ix3;pR}P2<ClSVzfkzxx32`pEWWllOc%fR<E=J1MBO&5YajL7Z34M)_k`p!P?%>_=1J$D*>P*
+gTyub~JGQnJ8_OyW|X=mD6l-KMtTPe=z{oPMZhuO2Xg*#UYUPBWEyBnKFtX3<~f|_*oN7O%=8kDAYc*
+6_IbnNNswx?qKE8x9t$v0qUuAmp3!TnauYSmg4e!{@qv6q-8r{e+M0exw~EG8dPV2N8)X{18LCaV+rF
+Po)UCm<ZrG;^)1i*&=mHnsmI&d<=?7>8JLO7f06-xC=`O+>5mGHypv$tmGMh?iq2IWF4gI}v>IqT{O%
+b+fAy5XvynF3deY7scS;Cn!>b=*EKW7WQh|g}P+U$?U`gA&9H*D-_WNln+x@oNL-f3#G!;K*4E>Qq{=
+%TXBhV7p+;$zlKa#~kkqeUDC)iVuipkuSo(#>QaV&gYf66xAa@N%p;f0(ZoiFggK4P)Fax7|gY{x#qm
+{#}vD;FP8c(w3@y7Qkd<i$wjn3eKu#+_fR;#m3%Zep@_A(wHtU!tCim;T|^c<s8kh{!ou+|}@C9E!``
+7)A)OQ_Jp4czvD;_Ccz9^U+vdVlParUafwB;p=Oy^Q1OS=~kSKFsz@iY;Z&b$IgnCLigkTdFgepHFHg
+#DR1n^x+eME1EGqYZK1NnJj{_z#9Tyk(zG6wdZ=Kf<)!F$;Pn>T-qaaMGLOMHf4T*m&n7b}9#ry+i*j
+tH9k)%1c!`k|Gq3Pis>g0%;c&*DEve{%3>}bG3|J%ETmEp{VaLYJQ8FKV{(d*}mqObQWCnR(%#?dWWz
+Ug}9fydk19;M7q|zbOVeYhf=4JubJf-Aiyg>GRdw3RoHPl??vPg^xE&J%3Jh0~~IPrt}^yimuI%Hy8A
+-!^znefMEUoTaAKh(ixoYDeU%~~%QA>;l2>Rj0Rh9UPm56chs*_LTN>J4f}!6ZTZPL-;FT(+m*D%g5D
+WoAssUb6O&yV?6S+gZF2V1$e!wn$zb^isshe4|cv>7NPX3^AF&h3BVXW_2PG3-0y7t$ywYwsveIT;?S
+@9P!F?((OG$@IhcFn?SjCib)c!#O?|eudrn<8${F{9&7%AywqQM1;4BQ{_Yt3Q@`TN5&o{z@RKqy$$n
+X^#DG}UhRWF-WC$7T8yj81fC?t+ot*@kLkysJiugJ>h62B#$bdtzA$1CWYft5%DtGfqHfMtU;L(mj5R
+1hBO{d@=DBk^*JP1%fj6r~odV4MGo7xqWKW!e&2QMuPqIvY2>2C8IHpk>+*LGz3ri<Nt1#n3qGX=bg^
+d0vr-nC2YUrGbZClDM%rr-Srd;QXH@cuh~!(a-xPmJ7r1^s{30#Y7V?F0S;(Oz#mT?Dcr-0}hczY)4N
+Ai22h-0=UG)B8KKgZ|U>{?qI}Q9SfTm&#8lUchSaobYs7T0JIA@P;EeJIEi5eM6%?v_H4{lJTK)${o*
+@%RBF?HiNT6Lm}tcdeFIaa2erPMZ8Q%dXCqZ2IVB{#`EN44I=a`?7P9L3c>A+?soHqzGm6>Tr+9#8XK
+*}5@UUaku18)gq}BdLK`^e=n4tKmHX`kNZk2(@hb4vknlrNsF$LsxqM|?t*cgr_N?)wE75gAS64U}Ea
+UVOhC9>M(plqlAa{_1T$Gwv#7RzGSj22AW%0Npb-+adO4U>AkkLavxtX9L*Q5=T2fMm<rY&kH*1fre(
+9!i!Rd##&mSk<C%doT@sC|HqWt3U9)jbbPKnypUd?bSn&sWJiYE2Py`f_WyY0@E8ykmMdhH|yM^xl3D
++gi@;8R-h|pK5hHg;X%qXvxg<-L~5mC(ft-Ffr<LGip(~SWq|P6|O&MA|zs3+g0ObvGQo2$*XUuS-~I
+KN4U2|Cw)|Lgq=ponxA@QPx~jKt9)ldh)5WnS?-Nzo3!0s?d+Cf=D^AJ!P+(3T*zFArubkxWxjb_o6X
+d`-7KUmO+9>bw>hAG6HN;Ige4L-$=a0_jmpBqA>Z|R*-ZCo?druq29BdOnVLNsLE2`l*NXt(vASg;HD
+(dJ*Sgu1@<SIk^5go<>wVTOX1r2)Nx`-vj0cvy#{v2}Xcmi0g?_{OeHlx=T0-7bFZM19lbtl9^Pd@Sz
+oJq9zDgSUs;0_iLc~#~Q8nyVRf6f*#QSRYO|kzI;jIDvPEn^?(c!fQt+$6Qb_y@kaE?0CBN26wlCw(F
+QBFt4bSV_u%|#};U%d4U>ku{|lrWZ5SYCF~>N&=IaL*MIKhH16VoI|qH%Ctj6n%=}e0)@o_;{D@`doO
+4f7FTy@(byHV$bM$OfGxklEJ3-au0ZN&4fdQ9+6dFHajiXm^14ZQ5<herZuA$sYMd)vayAz9d<(xQRq
+>}B+DBaxe70AZsGlDq^DMQyN13kQC=NvUQe;HT8SiecDtQc5KZJBb%@QZQN%Qf4{`y!Za(QNAl(<~E2
+pPvh{#N3lCp!#*RiKi6F=h*PR`mv<<W$i_Nlms)bh61w7L?9ZcPwO2sp01v-*J)TVH-rmfDxev`p;vL
+eP@vrJYuJG^H&eS{*K-p+WQg5wFJ}v-K`~K1**rGj3e$ozr8b<+VWQzCzC!=fH&Jz`l!S=Y^AL1Kve#
+mbBFgidK=~bn0m`lI>kz)SZf}Vl@)m7M8S1b)8g)_-cZbwx62|qLvX6?<h0wPNW`_Eu<}aAP8F8GGxL
+}gh}+<cNK9xb}Wgb<5zU<j*mshIO-@EH7*I>O*~^)={m{hi?%IkRTgBKp&rGW-~0-@9TfXYN8<*lgTU
+0i={kB>;#NGb=H*pZ(p~FkPH5!ZaCTfXwLLW4n28^o<0UN=>3KAF9v97w8r$--dF1`|mfT4iKb*$aii
+{$0h%D3oHlE_A*F7GF--H(Y#J;Qi`rO00|HU#mO5r&6bM@FiKKT<x{o=$Qxb(kd&7j_N^Vq*XO0_oxw
+ZSAZ2E|qYV<7L!Z3ZBa?HBCpDsSS=D@>Ds;y!!#0dId2^t-l<0SKy~K&ck><_g}>6q0~0EMgm*{RMDh
+V^E2<9f6S`(7VAbG66+lWcF{C01)UyusVHH+kwJz23#+p0OuU&_ew$j5;Xh1Tft%kG)ooln(^%h3Fa?
+>QwRbGi1J63_n#`1-oVNJ37Od5DrFmR8a^9peWwe^RFPLu2j#yI(iY9dYRuJa(q&tN^G}io8QG(G#8)
+Y9f`IGt{>7|+&L&C#FukmXBh0m&P4rV=?T2kM<TXEL6TJ(ws2-PmikisMA6Ih%bF{s3HNDent{M&mPN
+44;PJ}FK=Tl^X;_Ko4zo`9~S^R?U_j9d3{J{5X6>9V>c%D7tFk)8h!9(NRo6J&8Zp5h|<%=~<8Cs%74
+^h=p$$m|%PN6qCHRJm@qXw(I++smj?KCKpor`(1DBN&2iQ08u82Tye6K>Au;t)UvVPnN~=-4yWpAMbJ
+oG~}k`ir`chma8OW8o|nWgT~B2H!&1_VL1R896XX4ORpeg-{(++;j?h)KL<vR3rjTcCd6+f{xo84?c~
+q&Qa!X`~9()4D-miEq{tF{`E33G*uVKY&pm4c1U$U43%ZuPifjkoNV(ep32(87arat-3^AHmma?~l=z
+f-ByfVlTPv1!&X9A>>ZmKqTN@4I*xqH<r+UO2-670L_0|=3fTC1y?DL|C=ROe;%<~>~xkkgpv=<(V`_
+!7Ym#nxAgt9hQq0P^SoTw=MfQo#Q)$oio_xYi6Tp$zZtz`Fex0Kp?ly7LdEtLL%OUK(T9gD*$z|EXGJ
+0x%HJiKZ0Q9cau@l_Xtdm#C7?hCz!izhB$kJtFj?)})PA^BK!{N=tw!x!o$=1M4PA0_nt$}ul=L5+hz
+#$2Eug~BMP8-C(1eDkQ(%Wg37t~$EVVdvHBVP9R?&GE;ck^i7}u|9GR#Ya|P5sZ8wPQIp|%_K_5(X=_
+V=@tEH-KIL7Zx+P5K%wt$QJp5b*;9=cv_3Hfi=WlOglRNMj+3c0`O7+82Q|oZ+EKNAU1g0<c;Rw@e(a
+V0MOD`KWnJc#%V55~+zj-1Usq^)X6DRN#EnA8zeo7-NASr<nF?Z)$85J6Y3%hm;lw@75G6e)ul7#g@9
+J@vR|zxjLJVH~<Umc=S>0!nAun{l90&><APz&O)TN$_ZI3q^F{L<1cF5zEFB{UMvdBpcu75g1n4MKgY
+gR3g?xuM!s|Tw*7JP*4cA8s@ln6x9Zi70gS;BgB5*}I37nuI~>H|8%NnS-MaiL?=KF9NcjCm(y%ZqK?
+Ah;OnG;7&mMU^M*N<GP?BC#Ql$L>j2B-NLDy{^(eds0K7T`q>i?8aX2yxZ$`ITtjfr)N46F!F`P*hp2
+0)GG4mwD75`=G~)xoQzW@b{BJ(_K&^Bzh*MsF<ss}FHK+I34=t<6%38<ozqR5?Wv7f_$&>Rjt12BNY!
+#*mY8csl+8=%?iGqgv8SGF&o)o0eIfn;sf?@n6p4%DHO_|6Hh8R=#%M4fH1~L{I1+6y8SALTrNOluHQ
+)E_W4%$Y;VL4xm*GGfJo#aFzvB+PbL4!8(bmCY`%Hv1+&D71f0+S(*Ry<k)ZvsFuE@uVebSRWJ`k2pL
+*&RZNv#NTSdw1io<ikZuhPSbhRvUv-6cN;$TfYzy({MXE0<E}`FMXE9;9yW?mM=E@PizC;r!B&!_@1|
+L|<;M;4N92>Tq84LWPx?utU>0ofTw%XM_Fa@$xRQ60?SWIjqnLMdQ%cp2UJ*9$DfDqmVz@oEHjpzr0S
+?=52(*gZulzrypSp{yzarf8*q@!4m$r!P160@B~N=u{r4H{y<7O0~+8M=3S+pzjbJ7(8isoUsH_5po6
+ZU!B2|%R``xkAohb2pfx>t7tU`304gvNB#&eEeW_nXneq$_w?Qj#C<a=?u^7~N5gGVNC!l+sc&iO<*l
+W9FjsapbP_;#XNERRjX2G8z^z<f5Vez*n(i=(<e_4X{{}-_Ir_!tMz!DHikbeS8AKU)7gC+C736}II7
+VduN+jhXTRF*Z8Vs?*&Ir7-V3Go<c*y<kDVb7ePv+;`AXCLHQ?VK)iZLkD!$ddA5UsA&w2`RtYu`C7Y
+P^>0VTy8RAL^dDsuiMO~xCB{vdh?F;k?ePY|3nuf;$V_Vq<T8cI`>4fdtKE;77WeJ+ZidmUvpcV?3?%
+MpC@Brp>h4cnR}CEN3m^Nu+LXSGt*@6i9%GzEF08FqOMVo8bm1}%5O=_`wMyd*?Z^xSAw>04|fmu6A|
+~myq4zX?%J?`1skn7W;I5`vKcSd&fAe=4^@)mX&sL_FJ7wNs;{q@Zy%E~&t7AD$Kv9{BR*bf<Td87<^
+dpkc|aA^vzoAm_ofI|Zcufq;g!>AQm5Ok^-b&oEqtz&cAs?0ypq|SedTl(lUFnXjtFH@>M)a*f|=yaI
+Z&=*!u3Rka;sF6J@fo(xxGYKx(Lq^3XxNrz$4Tg<zcc354h&+y2{TMJ3`OLf(6Y2Ght@gb|zSo1q(+J
+Cy)clh{5fN<7GCF3E5r~=NP1QVOl=0o+mI=&v%I|O`#A_=f;l=%A<n5y!3M>Md}3uGu#ij0?%?8<@kF
+CGp#u1Qs^q938;{*8<Xwa;LAbSd|Km9T-ZuqyeFafP21onwuQ+`qN^5h3r}b03jztm#lA%3LL)4IoMv
+9;Lji=%=WWy|uiI8~!Wt)JeimzornquhSEMLzhfL-Mez-06MXa^d9Xi9!G~i0IGn}v6I(fgE?&Y;8^x
+c0!84DGUJ0=ix;M8qTgz@vr-6URC9f^{q=XB?Krk4Wb?}H^SIj!lj3J>_HAEptoG90~xjH(GE`**<-#
+ZY%Zw`{JIFO*-i<LU0m(0b@kcT*p({1}g|%Rh{>vaDSxON*vu*M=-;G<=0{VfUNyb%4m!7qZVJ;yFJK
+7q#!Rl=0AGxXQ)#@kAftfxmF_G(#r_ABel(W@zag9t9a)JK69|;4D1*BXg#&@<ABRRPu->qqEk9e=1^
+=u4cxF+(Ob~#0mM*$~T6YUql5V&AGKXQilQYB2B37Bgi$Z%gZ)<XnqP{An4#F)iO@BAtSzd2vRg^URO
+pSE)~l&DU<65s1W-AP*D>RU8Z^6*5>L~p4$ypQp%GsX`bKbRQGriH`Ta8C~r|gmK6dPd7!z0D~49oc?
+3WN2~7U@d|enq3d)GVrU(iy?XJuLjmvyr4!C*e6tnds>~My!W(dKrJ4ZB5z9QHF#I<rwO1dgf=}BxEI
+UigBY6<no%e}8=u5UaFtyJ*Usxn7-LPT5Elv}Xo^}J$o$pK|}HWZpWU@w{#41z6DN<Kmy=^PC@97L&=
+@aN)cN*Jx>fejwPWm`!c*o!z_d1WUEKvYil!XdM7mXzk<lQR$Zvh>cDDBec7zS&hQND!-ymeY|e=&V^
+#NfdD)TLG)2kp%{vtP<raBq@1O=LgGbY?7$!nJ6Jycwx*nz0>aN`N9Xk@rHaeN?>FMdHVpZJ9vIQfYU
+L<2Xa;**jnrmuOE+BfLY=|Kj(Fvm4fMx;&6-p2JPH`vyv(?ljt|0YQv!VfGuAzjJoaq<lf<$*Bc%5$&
+P2nB1-zNw5z|5`rzwfl6~-x%yj>sUg>M8{J*%$pYqm4!8?`%?vbifvggW3_|1<R?{Oz0*%ch4)4T0|!
+)WQxxqi@h(K)=speWq4gBuo$;=L{&M)qhH{;o45<1ej<5cH;Qh5ivkzf!o0_Zs&2oqL4d2=SEeS;Y-r
+ZOAc6_e%Ti0=q0Ow&(r8cllsU?m;s&*~{edWG~_0F1+Em6nWRtZ=ej`TPnjpFNdGr`j5lE;H~T5^aR-
+tpvU0s8Mv-g_lbJWmHzsM--izyqly}O2|Q&lsn|a6r4-Kk$!aRDR1AF5n)2Tt{K*;1?p=U4@B9r_k1U
+J4{&IHSR7a_HdIB*N$ijE$PcZihqrTCA+*|XhMYU}BBV{Om5!ijF40octpWokKxjo=t-QQoiJ>Xy6-@
+m%OPhz~E<s5&|T42cdrcHiv8825nOPZihL*#Z*+|%p0DgePr8HN;Rr^3Rz(ql_suH;M8BF!*gabeM_B
+IFHEo5eU?i`k*b+j%-rsH!51$4*DUkQPgGg{Ren$~aQl1<F)IBsif>DhTy$rDJt-Tk_(x4+2D;4>Hy<
+=DOIV$MT$c0`i9k7fX-A4|#Qz28;41a?b6jxbV*u4yy@%P(5SqnL57Ez*~yAqBtLBVd~ZkKcK)Zp?XK
+oGZ6MY?I6kytE{Dd#=3^Y<9l6*XaK8M3|cM<DBdvir8-$-k}jCAPp^8(0M}4*$m!W*7JZU#V6dA_<&a
+{p{yeMT)8;SrYo4Ew=NvH_&k5Ezy4j3<Wpf4o6@dA3bte-7sACMPu)qZQ^&#-ulUf9OJmTu|D*&^zyW
+Wv;^ZiM<Q5V3Ku%W!d{sm1BR47b##+<A~56_++rae1&b@{jiWCZ0%05;8qK%?$-4n!m~ZtRi20gzzwC
+R&ddT~8J}=qeIp`usID=n6j(ZbrPG@66R2qE00H=Ryw2Yp}E9NC(K|A|QZe^@wXKgidbK<=EARM7KPN
+a(vO?A%4^s-2oG)JLoLNn2T@70-kH2C>j$5L8)f}B%~iQYl9N*Bj^u=t|j3!B*pW&Or=#OX!$1H1XV7
+^tW1PE8uWwlWO)NiZABf<bp<43QfRk8ShaJlFwmpy;6Qj$)2V_~TB6fpp${>^k(DnQM%Ut+Se!yk)LG
+Xo68h8uR;RHYuiNh55rOBpA8Ju{%k@mY?cq0f<Ft=>^~q+X>Y^``z8v}=?C5{)OrLi0PtNk4sf1tzJ`
+pg1;TQ~pFbNSjM8en!fe3;)k=q}Xz<#PbbVA-*>%`8TgU34~-RW(O(swaFoa{y@3HNsVZ2<OjgBp>$I
+S$`#YMXH-!2Oha4hVS{%;0;j2}bwR1aA#_{H^^MZ5HrX!42ds{~iW!(V=(s4uQVagz$I5>CM+pe#kYG
+dnPD))7fr-VAo(q-?ja315Bd(Ww!Hd5Mgg}3-=HBpE3k+)NW9RuLf0j&MexB+Dn^gd1)K}gow5IGsb3
+?hJLJD=MC!{CE{$l>5iP)pY<r7vEE!~*=si=_*HFxAs|}0R%ZOqw))iAywRT&u)Hl#RNLQ#8a^7?zBM
+Kn%J46{(uFQ6PpNh)nK}FHdj3*_BY*wh%`;8DQ|sQO2FO;l$g7PiRu#zNkK8^E|E*>;OINhL(AH<EWh
+`zcefy!?B_o5)m<EWm%4RlQzD7()-Y|bwo2gWrMP6<uZ316|Iz~3}*|(vSlIF8&3gC*0;C{Hd_D|Vmf
+nNv~ozF7cpJYdRO!28o6%HSJiPcE(UJH2dN{=&HJX_aZfMgg{9z3`()!}7uxrcHhTMUYhSjiuSDyn>_
+IBZr8j}#+~84In~r_7D{D8fg(8&x1|UKMdK=IMG~aQM0eDxKQ|2UqE_ucQ`!c2m@?XASihaIQ)4==DB
+!ojqLlGjsb50WLq1YjCgIbe_gDW=7=Tb;y)I?+8uWg%~mk*(?s2IqpOv9;^jM8IU&(B~a?AA7em%sYH
+=qtXi%whnb2k4_DHhbXk+1^3>0=`3Mg=!QBrXQm`I(JalU7zZ4CYLqk?L0Ad_?c_s=pqt{z)DW2>`*+
+FEOa}nSib|<V;Bq>!d2E6oDu*lze_M>-!IPiBW#J@%dXlZfp_`}JUBe3d0Po!M6Y<?O<n>U$z(@n&ic
+{|AL^i2h|%zUFm#8O|>Ux0U=_9TBhko$HZ^KCu%RQ{o54*0!iZiG>pQXY9FzCg%pkJxL#`NwAM68K=;
+XCKRLKCr51cG|xG#qBv;ydXHhNV4L!^HB`}+kU30v-{+pIBmxBJ+EH5>m^#ORHwLuXuFl|2YP+T)w$C
+e%R9yoT>v;#*w%lLGFZ|9PiI1SIpN6P<a2=v`l&5-23fghhDY?Hte$1?JjJw7w?3=iw0T7bU@$9%wd)
+J#M~i#7Nwj<~J@p1gs1fsBW>qPU!#K~ORyRG__=!H{H7f3x9!fGYJG($Z`O@oglg`ZDM((imv?AU{tV
+7r@gg(c!H#YoAKRQ3bkvLn|Xf&2*L@w0b7Y*V#0)oR_e0l9I7h9Y9J|=1sF6C5?Bf*_-;NS3u|HuDnT
+rI;kt>3t}&Pdx7<)<+%gWtqrt&`VpX7JB{tAgd9{~rDG-}`_5`|iE|ejr^`S@GLH|L-b!760?!iu$)P
+*|&Xs7XHWoGbT+{{PW+6FDI)uEBhPu-~Z?T_1}N{-=hEhH|iNw|NQszx6RQ0n40+i-Q>Rd-1g_`ziZ|
+D^xd^*I~H;B{J<9bv484_Z;t8at-|2WBSc_(>1_b+Ig*I@5L(%5Jz(NJX7g)*u2;AjX#CC97h=2j8%M
+iM-#|lv?5EjwZ7%)hPH)4AHy`|tQpla-2l<uMG>PvVzWX5TO-}SdJs<7>BnWmi0D}A84LxkgW^?fIH|
+hQ6n(rke*jtzIJ$S=F>6?#e_vW!LGS!=#_h-04+k^d!`zHkQ3_Y&gQD(tU3lBT88Uc3svhB*T9A@)O-
+|{J!adYLKAC9Tf-L}uZNuL9+d)+*J!w@3Zz;m8N=Ij&Z3`E;MKXSd>_uth4>1h&l7k&EPC6}Gk<b7Ij
+-mxciHUSedOvh2|Gq-n-#NAmor)?%+5V>jKf?2$Q4ZW`V&z}uKCF0r{LZR7aQf7cDY~H8qfZ;BcV$!t
+KyNOg#t-q9+zKdGP<{T_L!61cv>6AzIPI<-RPTP_*10O=y+<T*Z|Jb-w|7IaEYb!;5`qxTju?>Hb9RA
+|ike(UtTm^`+Y`6jKf^X1B0CU;rthNW4&lhrYz8kK0HLmxzzdy9#0}g+l*>}bC7jmSM9|D+Pe2Y(He)
+@vqW@rDEee)Y*0(Wi#ljVS3n5$T5_y(?CXYp2jJj^dYb16G++roP8wwZKiF7J{W+Q!dSRBzA7#Cf18P
+T5tEx1HNn-F2I4zcUj~%KFy`h=C7g!U55;hrC=JpHG4-zC@(?wAxFoDKc@+5V{!(mQ?(ajX5OI{Gfci
+Vrv;J-UqH$KWgwJ3K0dqBq#8EW(Vw<PBZmXuBX6W5+&<x!|bcJdtC%CBMYR<_3>)g1u@wIaKMf2c)qX
+eU^DXaWf{8Ve&{J^!;}!@d&V41q=X$c;?h3X8#M(D_z2RL6Z&&8CITS7mf)J(0{PG;Qe{P6F^rTrfG`
+Y?2I_k?63_58o*Pq_$xc7!7H3-cbFkX$X|OO2IInTCUayZhjr?$Gtmh-PudIci5(@W4I2)V#lJ`X_rR
+CKm480vvGBH9i$$Eb!P#M4x#HHbbPXvc~@}6F1xu-{-fG7E15D+sBUtas_Ig2IWhll&|l;>sQtH*Ak|
+1JSB@FQ{H7v4a9Gteh>m|AfAvSv@tlz1M~O&hi@$6lKcT;n>ncg<XYDp@SdTO?P>rSbi%bA#OLb*?3a
+!kkV9>BW695~D2?adD$ZT+?giXa#C`<fDQQXCUC9D_!_VL7mlCAG%T3r~#4&{#_`Y{4jVY2!SgEF0>7
+g-!Ldksbx^rGk96o;K86kJsUU0x@qfCRlN%Io`&nd3cNsuGf*71ngm%_dulbbdAL@*;^3~2*GNhEq8m
+yd0G?5l`18cMXyZf<>D*kSfP!mC^R=)D7X(EX#&BDb$B%vE+4A$DjP6SwYc>Q4&AtJTnU9xic)wQpL(
+dv^yq)XY{j7L;?!?+bt`+LqwZ;RqD)KLb5ca=)7}M_p<U7OeZac}%p0l4qX>}<6fD!)gv7aXVUmy29H
+z0z6KP8jd>F3|ol^FRCBHoK9gSP}QLGFSoo0q*8D7-~fka%;he;z$2_S%Oa+;z&3H+u~7Zj2}1M{gcB
+6z;KWnB0TmX}VtrO?SJS{;JRZ^bQ;Zr*~)kX70f~c8|gDiqf~mMo(`deDuu`i0#!9$-4`mO!h;Pw=Qv
+v?G_#(_8UarjKwIv6aPnlf!udrSLd(JKFj#p`ZK;Pnfp;Z%-{jpuqST%Vn?Z&`;;jAj3EC`H80EJ&S~
+<+V14@RK=>|wpw_osBYS?Y^r}TA`~>bT@tbdMPbw=zij@c3w&lldz|W@jcV-3rY+65`)!$*pRn*1oNz
+6EqP`zlFS{z-Qb1#bZ6asQQz!Tj*REp({?Nzm@BDL(Utyp)pNx}B9D03di#P;s}^^UA2m+M;gOpVHwE
+-=D!9H^Aeg@w;)=GiA0c$wnWTPup@$uRAg`&<m2PUAPFwOmtEP4R9BXU3vCUQo^aIod4Qc}8_KB2A=x
+(f}4yYoiASj=X<KXGszzVZ7d`>*9DaORAtSK%(MwSNt}wG$ZPdCn$5cO)bT(pM_oqD*oYAN~hPOc<jt
+qEeF;`y|Srj98QdhuiYVU?8o4U2U^fxML7{&Fk1qJaYfLeMh6`8CWb|Y$fAdfwD_xE&-~5TL39X7XJ#
+M|362uJt^UzhW?Y6c+C8@6?jdX7Pm}H%67aH_%E~F<Y$&6q%hO+!%SjWs-8KP#G)@c+esp=K^jVbTvv
+`)z0@aFGmY#1|C>VLfwxa|aT~s@FAQs3aG0{@;zR2{|XcaRgYu%y!4SYk(i*=qpUNU=;za&ETZSrC-+
+pDb5?;nbtU-G;&S@zZneSLlReFh8_CNr~|ZSWfW6&c?*P*DNfG~f{a5+U0#R)px2j?r|>9t6$(zES(e
+K;TCX2OTyzJ^IL{2P9a3#UF%9X|zw1C-3j?0z|Cdc4BY6r0?&7iQc37k948mL<<fcys~EmGo#exu}ox
+Dp1yPiZ8|=u(@#n2WbP2!@}-7FOpzsLl9Q$Yf+$@hrO$Xj)Y+6dJSiT^W%SU1@bLvxc%x}oF}WmxQrw
+$cTIm97Dus9eCG*6S9SF3@(ME8_rylbdz<qx)jiq(H;o>|i^e#2T>+m$w=UNKo9iFTk3eEme@azi5<s
+);t0xM%b6x}-c7tV<-h(0cIDnxmYoh}xA4NHr;D0hFs*6Ow5f??YNVl=#m2aya<r-cBtvs(vKTeAsc)
+dDl5y<b+RF85GDy>;hHzJ^MH_AhO`84%Cfu33=TQIfCX%gSrS2QF<sL5GL2S{S<Yj2>1`!E1~Pg4RQO
+IREu%!LN-b=lrK^^)C<qb94RW&_7u0pURtVHZ8*TC^#JLL3KEOC#p}o{><jYq92_2(9WiI+TG}%T5J;
+ADdaY@yr-SfJt@8U(mM&x+d+=sqBA>34Y~W<n|Hla{qHn8{~&(lX7BB>=s$Mp)i<yI=5lZLmcVusy&b
+vD4J3C5J9*a_f^W^X?J^r+NRj=#yV}iZkK*qHbf?|g?;W_v4j4|m7V4i`Y<;)b%XbzV{Hev}slRWruX
+n2bw2f!`FIa5D@qb~p?MY#8KUi&h0``qRo7!KU8StyA{d#8KTW;WAYS&zvIcJS&C=Q5xWz^A|xD`i>@
+zRi4mzqh|H9&RGE@V!tm3dife={s5($ML2i<T>L?=os+C-Qma`vZAHFZS>v<hg{~>~IY;jfrItKr9XN
+Ut+!*O^4^XX7R;v%5j(Fd&hlc*dXivjmGaXeuWAzR$1wItAyUFV8U#V*aqx%OXia~3)t|*hyy}~2Mc%
+oIXkUFWSOFsr;q1pFz&|e>cOc{pc6vm$PF|>MS?2>=y0^|+7`P$5m9)GIj=DXHHXm^8s=ccz~1(=cI*
+=sVaF29$-Ah!MDLBc$Gom)NC0A{AH*x`v&75H;0uAbFRQvSor6gaU-$fjjc}J^H#`$$b9zN2z`n8Era
+tX{diIyf<rLK}%DLc{_9peq7_}X#ypjMAmAU!3TglLf{?&3jo8>k)%PqENf!qGlc|-O$fu8`iAjX?eE
+jL#IL*#-=^{4sMhp=`)2~<?=_ZO{_?;qv1yCL37aDfPst37M<-T-p@=jCms9sm7VW+_zS2-09aHH3Gw
+|9+i&eY5O<VUhQ56G7YODz3@<hx!uQ2iyBEb3tZYX(lh}HWrO(pLM+1@O`$xchiNHC1l920K`Mynii@
+5t`=5O8QM<l5Q_&pu(!VP#Q^Mf{}=SQySokI-Yd|qrd_B+z<IVP;o3pd+Qc@)XMXfM02$aBlKqlN8hV
+B7@WryXHGdxHN~rljf8qE=8W*a)*c(*2-w;L3O9Z;kTokWK528TRUtf2T%ZaLcK1qVGDtGK#qvFps8W
+a6P4sor85iJA@TP|-SiU@Lh4LHGyL?TmofH4Ol_SV#cE00oM1AM(L=kldPqU$@a10rZ7t3%9?lm<?Sl
+>vb=*^w#@9@&e9Hvuqri2$A@7UeFl<C86J+)2F*F{Lm8#GD(C5tO^i#E^K`9^vC2H)y5B984EJEg`pu
+2V9+L99y0q{Pl=6P)2vS0<DN8J!EIR-s{WYj1#$>s6~7<b2v-93H^1N{^8X=i#UjXi<932*wLFBKPc9
+1YxNt~zk;~^%}JVMZL6SZckYQ|FGLG|Q{H2P>W_zW)n@AVD18*XY43(E*)3es#7Pqs!G6F;ki2O7e|h
+u|z(F$^?b$}#=>Nzx_HcZX#O?FDk?}SK%~1De^$pJW{Yu{#1pEw4B-k#9zL{zW-V^#kvK!#|T{cIcJF
+1A1w?x2OjP4hW3^=%R?r%;#e%g@){MIf9gSST>@0|-cx)bqlrzg`roWDm2;vJ~`QYa6JcEZ2yU+A>c@
+ulzXh`ntSf0x}wJNk*<Y#7^YLGY~-fWLJEkhd`GTNYqv-HUdCq3zlmRN6xs@E*<&|1@TQ$?pt9!`RoD
+J@Zq#jLkk8r&+sG#20K}DfXL6h+G2K*!h*p+xI!``#Y!2U-H0fvzy@bXKtsA<;!rP-J6S6Nlo^E@1o-
+mF?-jrIQ`4A&e5|h>U;RvRq|-%ZZiGBQOD4-ZV1)7uYX-E5ZN|p+DV*3mV;&XD^14BhCfQye3MqUlkA
+IX9)2@h;JfK^yOg;CZm!Q;u1~h*N&lFc#I2nbdvq1mZ$hhKyQRU0s~k8B#y7=t)fg?mcosjN+_b0j<X
+?dAa{Ku$-&W-9p><S*III7zd$5NaOcqjetA20tI4|mUtIGEIi`@Hp`bZ_5SH&u!ch(^0=F6Qbzx{PGI
+_L7RGKP(@UC0=9k>0Zg`}DsUk^L$<v4f@kzC~`jv4OBlH4!t%ex$Fr7saAMXmfk{?KyBW@H=Sit+PWf
+A#$s3t}#Z%AD%@SB64Q)7flBp)A{n-0{-f+>sJr-&AalG7s-;hSN{tm%GKr-))p`0`=PSm_oe)R34d(
+jbrJHa+A_uq568((9d2_(NlFu%CfyGzr6mEJc9<!3<!9Y`g#UOQ&y0h$3fERI%!yE~!#rnrW@*c$l+D
+YBd)QFyGjkB-)*mk0`am;Ynw<$fk0DY*1`2T^>c(iNu&)o5eaz!z&C&{U)=(VBrek2a?7>4H2*p);A%
+p-FiM>G$i5f^*s-1(Lo=-0ciPo}gbK_BT%ypf~oMYgd?KUkqR)fzVp2zS^7eIYQ0+&dlkJqlr6R96xJ
+fUfi5pwaP4T?5tolkvoNoTEAIh7C|`t@==$a2`F8G<6=Ic7mX%knN%?l#JXrwk7kr9DLW+9G@dAKSX|
+ROq#U*TX}D=DNhD>$w(S@WI>`-n6`+fB`UsVx~fo>Uw@n=c_u(EqlG2Lv$4Fm%fl=orI1$=7$Fz;B0=
+-dQoosn3Fy4p&e`y03Snsxoxk_U}l~b`KrrNG6V?_#d0sXC5Yg9*G3sT!sR5DY)p2C=+f7&f+39>T#o
+=&H=$!*9ea7$ulVrd?*$#Z;eB4Futb-~{i<DIcOrNgju3ISP7KxPYlw0KM|cuc10#0n&TDb1UFODCH%
+sm21Aks7x<1Z%6X?$)JRUn%xTEB;K+pF^jhwhWIt8j*QQhFcb$h=H^;ma2Ubl1naJf{9Y{F(B{7hw8-
+S%VmgompTr4rAF&?$aEaJn3$^&T9OWCX&8j-0Zn`C5{a$=+Tk&8?LAZMubFDPd2yP@CH$v(X0-&o*Q(
+GZ@1n3KV?=m@27`N54SfTd;SCIayw9cm1GDmZnY9_Fu*V?ofAg_o!7V!0iOzD<;$IT%I0Xb=0$=gqFm
+2u4m6;Aj8?eWDfO(^w4A|JKFgRSs<g$*RY)9d0<T{xRY^3o8<xaCmS9>OxPTVpnTUoozJv*=iaq2U%u
+wht`E1%RCOE1FW;*(_f4Gay{PQ6rhJ#|tCKrrhPUgs7~`4WQ<DxB?$AE0iSIqs>c)Q~4!*66F}r7yx7
+BEsjdJ2YJl8Wf)7*~Qft_M+vvu1bdb3b`FI#W+#J79PEs(Vjy17t(=Foh%8)41&-_92JcDj3lcU!KPJ
+-_S2+^nxO{kh<()%Kg(GtPUVxr=VMgl_92mjSmo!RxlxZ713m%=K5#;>VMJRC0f#WBoQ7HGhxxhgQ<-
+=3KnJmYaz-A$%U<?Oose=Red41MebX?&DqHHiIp5Cc`$HefYuPw6rbIE+cr`V%t)a%v7|cFZ+wMy;U-
+M&4kJ4{qrrMvg_tGPP$!If@HTZdEE@iYOl&Fzm%1RKVENw3s?HJ`%S=49jE3eueM3{K5`!4R*9<w(^h
+DMBVK@A@)x&&ER*9_l{HwqoH@fe4{yJTQL}SErFjm$9m@Rz+e6*ZN`-x11o-YM7KMCMeYHI!edp|*F4
+ON-STZFD0{k^tJ8mADGW~SQ*2g;_niQw*&7G_-(DnWh<-HyC%?7rYeF0x6HaBpA{*~uYMuPlZWEc3N1
+oc5=#Jq0Ua=i^F^I?&gKEDJZGS$SKRKq|Re7amRTxD$(Mg;HxH5n3{0F+8qjO_)Biaow+oauT4hedGi
+%C%12kz#8l!w>anIR}ZKW<?--_^6cBt$0+-OaqAF3QOI0RvuDjx_TM7ArMI5<ZUGxPQEw8{a6&tYt{!
+XVo#4@e$98hE?W)pH-~xz+>ofQ=)Ln%dGu+VdyF|k`mbI(KnLmRXgY-qBOfCuS;G)}a4u_`L5Vv&;0b
+C7AqPaKiSn~JSbAERQjnPFW{^TL#}xD+O?DEf*l|GVO6~6h_T+qJo{JJQGdY>(@FLbgC)O7?HyfU(c<
+ZWD&Pw%D=;YOFI*6?~s<xz|G1d2`NgyE0;vRW7pGa=amoo3X73kIU{5tT~c#~>kTHGNSTeOp2-HE&K)
+JRm`Q@fKsXsy19@VKFfo?x`pSm~xbpF$3JFu~=)61Vy!;C5hK^G7dn`eA<7iWQpo2VyC%N-xXPXDtZ1
+l6Ae@{6j_!AP>`F90IjbTqis;JwDW&Y$h>P((rHwjpI0gbF7|A(mg`#Dh)o`wUQe}&*VkASU}xhb+nQ
+|+ew?}R>TMWQFeD!E0^ryyOVgy*ke_ZZT>7yYu`Di>o9BH!G~5^b?=Cu$P(u>M-lKxIxpi5iD*IM#4j
+qNY*QvqaoLSV5NgNqD40ymK^lo1^3fztbT{|#wf@V_Hj@`k@Y{KB+G>*5Z%Xo-+Gn|KNZV)qdm(!A+y
+mL)RFi$k%x>=PUp~PPKJ$O+g+JZr|J+5tbGc!NKyVVr@t?WgyC{A9)<yyULE?M2KNjpC(l<+F5PspY#
+FBjsg#Mgx>FrSOb!_j1`>wVJ?W_YZwmZJtt`8x;y>1TNGw11B)n&UZl>EZ=hN8F59=0db<8W{M-(2a<
+8HeF_9sm0po9m6hZ?(X5kNEEl{lS}8AKCZf;qLkGk^S_|9F^`#`ORnF+t~0w?OP-FIR5J^!#!tS2<lf
+QbHxw6U4Rar<~_Lm+1z!tm3Bh_`7ZGKCS2Z3>jo{VSd7Q0?sr+)dILqTPhIz&;r=~P-Y}P;f2mDp(aq
+W0`~q{=-!(R8cTZu`>diBd?cnEr@>@+Vq08@{^Vg#FYIF2>r})08{pNk40bt*WfxTbtwAt%PS$A<hCj
+hpM>yI1dvvB>vk7KPnx5LZN4uVdHH!q{!0FL=?t-I{`%goyDH-^b2ShlIxx9y&x?>Ytc7Byp~`On;jR
+w^y<cOKa1^V-}__j`=yZTJ6$h+)U0zf|_q-^zaUTiH+Ns2V|60Dppba_veT6d_wV*hGOXcl)6lm1Uj{
+>8x58h8}7bx{)THI1RPF?8#ql+j81X)bR+gam{hz^#n$*5TS82VEOWN8?jh@wS&nxB;jj!JL%mljQwC
+NaHUB31&)^c1XYkT1FNm_y&buvIa`?+YT!hdqs`nl$I*ugGlg;`)qPbu=&)E;J{Z!i&djOf=8NZ^bpp
+ugcJMA780J-)&T%)p-WV*R067#k!U9VB$QZ6V*4KpX<AkfCLw6lv<htfH-P=8|xdx3`y3JiTYJCi}>)
+~)d3$-S<lO2)mGw=BOxqB`hIjV&}JiREgC#Ro+{4l@J(*R^T$KcVy^=d7AsQ&5oN%aWJ(}vuC5SKBCs
+O-_1U_<}mXf@XQfO5GOORF-j_1AbIf24(cE77FlMN*@bg5hgrp~FL9O|$JEY*ziTtp01BoO!Y{C?mj~
+gkPO>mhsnQhd$AEm;AC!%k~7d*BQP+Y2;aFH%38E<6pXpxYtQ`n4{q2vL?r^0HCmqi!K4fL>3=Xn4<f
+~U2}c+ae-vML8!_OAUm-NkxU<S3El6vK*kTzy1n!mc3Np5iE<@wkc4~+NR}|!A>dC&9tBQ!pR7Cl6jh
+{G<4H8N_<qcSVR{%*Q1we8d!l@Dd*D`H#Hq^4IuR>va3JZ7vQPyQ35~Utf9`BWH2CLpoI(_XL(5-jR5
+k`4=`G<on$I(^w&c({(2+uH5WT_|!!eTaacaA;KPF=HjBxqNrto1&WA~{A2yPz2v)PEM6gV?wEx@!OB
++oqbIxNNY@^|Q<r=stpw*K0q_U3N>+^E|B$4{kTzSNKZNDTd-Jl9u>=s%q2J0%$qBMA_FtKX6!LY_#N
+AP@+JArMAj@C3pbO#U>dj)uGJEk^D!<X|&%$j&afn_Xfr{U)$ISDn0Bqp@Aq_UB=6e6OC{Eb>kh{T>d
+cdjxvhh@Ey*`O&@>68iuW-EAfj?1C}LuS9XvH|gSDH6QIQ;?TRoEqHH?-#PN_V%u3ZGasXSb6t4ar?=
+DiycrRR_aWfB@Gg2+<wM(jLHmH66zETL>O8#Xh>5R!KDyYQT8z6dXmVszu1(xLZ(cv=_$hyEdAkQJXM
+D+Q?orLNjl^AqDEANsziE^1E%A#o3-EJkmJiQ;Go!PxQn$~*J69=4;3?}a9Ppu9uW!cuL%#jP&RP^K-
+}Svj?j&1A@HVsWZT4nnKeow&{#|qfplYR+jV}USdxT=jtr)J>Xpa?d*OVFgQ&}c5gsOSR+P_g40Nxhg
+d{>^s+bvYl%7rprenw@?Z=S*L9eXCVd}`qWe?<(pvM*|WfeiE|Uoi0!oZ%)+L`LOD?g`u{kX<`XmNM#
+`VEua6tWH;b&$<rDz(uy)L<_PxWJsP%9ulMnw#1Jdc7er}za0)w0DP992lp5O>+srNr*&4^rN2uU?iH
+GO%8^6cLn@0#mz#WqJHM7952arw>YYT2hEf0?{KMvLA%UNvt<7f_mzOH5YW;kfnaRCOY{2jgcYMBv{2
+0!oC@{jSQf)IVpGNO!0L2W)HXJ4Gz>3a1dzf<T4l-BT9ej3Tgvl2aJd>1tG&|9K4t0~lHmV5M&N&Es8
+3M3i$D?W9iA;s$B);OP%#Kuo>bjmmXM}n_i~zg~7>|X%v94llFMfzDP+(>k>bSimz-uUHA<J6SWm%Ch
+^?ObiKBp@<^*A_2K1){*?4Oh4k3@9BPcfXMUcb(V)j!8&qDTO?%%&nS*Q0S!t0()6#6zN{ajidv=aZL
+$xTL*gd61ZJV*6~Ma=(aKIb~v;3F;~ZfkA66Pbe(4PRfd;!a7U@xqM}?jE^8SAFCTV=HNjrS~O+E<>4
+OZV0;Kh139Giq$D7SmZ`cHO@5RtvtlC;xlVjzCkw9oe)pKVwSLsQpMQqy@ZvkCL%19?f%WSJcakaxN=
+zonkoO9Zxc8T_s)Tk#PHS2@T<2do>6#dk;}pn4TW)yaC>DjSAh>9`nsw|E)B%UO@{86~(`nSuiTEsgp
+!1!i;b$#?{?``3oTmK=;MqM=8)vzv)Ao)vzWFTE=Wk0U&NAiiC)yxo^tEgP_@EgA>oaq`n55=()FTf`
+yx>}qC#-4sqteXxV}b7m|5`nFVV>7gAYSCyUc4f#9_eudkmkt%(lEPeO^rOR`J%u<C=77Vt-U(J($px
+2B*A#gq)<BP+pL?T@YURV{UWaV1JoyI9f2cE-%<kR+fz@P^i)yzxk*!d!AxR8J-ABlcb$O!$1TSc`J`
+S$WF8)T2i4E}wl_Zm2A8y!*2$*cU8nqvJUB|8h<gt5ky5Q!XI>v^MJnEb8WYr$HuNzbUvYlx8!fiXZU
+*{icOj!bf>H-vF7<kpc{x^9a5PJV*Y#F>p}f(s{S)s_Y}-E8j7AZeP6apW18^LHK*%Khp!Sn{zTj9G-
+&`Sko(#vl@TMsHRcSu;$voeErydPM;6<G)mGe>SkAxQ$lm$#J^EjLHtm?RahgKRS1@+uq1dE2<XGzX|
+!tvMJ?M6yN5?hGIT9M+v`uZ-Y-I*jCfWFVf<_LiLz}GL%=a4>dYl@Uiu`T7sH5zP10=0Y$j#I4_l!Hq
+=!EAbH@Oo?+)CFFcQrQWJKW6i2@f?fshbw&>hoZ(J94FgDe-D_0!Ync6@t19V9hk9PqTDe9=(g<ui1b
+kFl*`l3dZp(o+6+i*@%eFjc%u<_8Hk~ke}k+Hv6$z0iKaY!m3NHO8YL^F83ssZ)i&pd1o1opZ=A`g&C
+c9bok;Ihn}<W}^U_7Dc%!o}SCh9I=EgsA*+bP-Nz^k6OlEI3k022UuV!E64C{Y`7U6pownst!7lOn8(
+CL1T55Ju1JJnqnCUFENQ3S^!m_&%}!wCb44LY9iZ7aG#M40?3Edn0yunsxx5sf&0Gh2sywIlKtwMC%4
+M*=4H;7XAEs#IccYJm1qgpXc`9k#)Hlj8=`z&CIt;eGoC?RGuf_$|bn><kQ8_zR2}lY1}4b^!8LNKBI
+bblYW4JC(u?2XS-{-(c~Z{s7yd<SrtT?$_HH8lrc@<NLlsXphuv^AF)2O@iAU{W<63Y0p*>#V1BIC{4
+m{>IL*kRXG})H%OE4Qe}Urir$>P3AnA_2XK5io4GIU<}P;2@V~>lFQow=bqDjz1#a8{dk*Ql?2Tu9N7
+^GBr%C+CFR|S0{EpttkI)40C2+BqMt+eZ;U?w2>++W1X2VV&yh877kj!Qu+wOGpiEe@aD7^hdx4^I9t
+$3%RPk75ae}uPtA?J>80r~!OHp!nR?PUuQP13`vxF-=h_HlX!?5xTRCR<yiQ<?GUWA!50h95WqYF~Ow
+HuRXbW*SYmLzD7!R5@&=<iJ^~QJKsK3qVVh%KGWfKci}y7jW>Y#mgUYc@$esp!yC%z0D~%yyw(A*>Hl
+@gOojWVvx_}gVsc#WjI(sI`3*0jr1hv%Z*))v(njDt2`O6Lb&Od_NcL=dIZO04LKCi8aun#bxJ8qX#n
+Clnp}|Zf@q*9ci?&TWh#g3Y{Yk&wUy&D*UVt)j~yWu4*`N*P)*0D8TU&ds55y4&{S$~Mdn^&TB_EfL1
+p3sS|Q8ec%XuYe-H7w$*QL|%G(_d;ylbrqXptE0M~1zk3bRE=t7iilfL?j7d(z01ZHkS?dUUUGupGmm
+)8pdf!M-k{@sct<JLst)3=nsh(!rdV@e1rB@=@qe1K~rcun+*Bj}y1NtfU-?}P^|ICM1%KCVI^FwI0f
+ma~L<(JM*=22gxqIEVv{4$s3=(}Nl@BJ50*WCh1YulHbndY;_}dx2xed|3B|y?VEfUf@BqZw#6OCyal
+PHEW}*r!2kE-s5;)6uFcUbq=-aaWe0rNBATrp$>YFLfo`&>|AReEc-6c(17haBhitEAhQ~UG3KGUNiK
+c5jfRsMLAzmgif#<HnAc2<vaXt>0Kb?`z;~FmndltA6K8f#Dx`@*dIAr}G{@)$6ujSgB>XA!<)82_32
+)w|cfZP6*LP8%iOYP^KRY1hY+&-+&-Y(u{MjFq^!(<w(cv9FTRt>{Wk`pmoe+dcHGhnty~hH-82n4B%
+8Kn)x+6_<r6WkgX24@S6gr_J>BnVr0PXuT<L*T+-LVe8-)j(d?r?%}5c3e?VvO%-GLWh-*(aoZg(P7C
+n)E!RRp2v6#cl|Qv+5IZvQEx8`E`?6Qz?b^)SV_NKaMl<<ZL;8BxI*ANLw!x0|M@2fXewm6~4|@KCGl
+uZH=VaK2+l###M<*S?6ZO(N+bHg&f?QvO+kzWNn;mvd6a*&<xX0Eh$9XW^8!W@qLj)q*8@2aw$*5T{)
+1lhnJoc>xy5mr=!>lssp`>T13f30)z$wVY#u2JnmDo;>PyzAiWbN)!AI))dTLH8GI;*Zu|C#)b?U#ce
+K&?{L&TH3whN5e&G|u;f^pOYfs(qnHc1#Z8_0i3XEXBF18ej$#8j7xOGM)@l>MRX=*N3e~I(yWCG2T&
+=%d+k>+vlt##2b_i;h$m+XP*F`aHf<i&$U5^EHI)16Av8Z#k;%;V~zVW^`3Bh3$IZ@wvLqtnk7rE75E
+AKonp?m5CPlvvXSqQv&uz^{H2Yll~%^-GVjUcM}DP6KWkU#Exm6kgHwu}+H<WmoRDrm+%Eo6{37Qmwt
+LkbfStVvyUIAOl#?52^1u53I4yKzcs176esUe!Q`M6U3f`<40b+&FZ=++^t}uy6!48o$*p)NgN-=#PS
+EXA~Z)srVuzE;If}VH!2nBtn#<Cm)fTp9NY14P*rXKj4k>i`#@v{w718+e-8e8|D$}|pN;*Rq5EOncR
+9Bch{Es*#kQ3e2Qg^-|ECeZ@ZF07L3=)o4BkmMa_6N8-?%A)_J|pV?`^PA^ba`rDVihRTom}}A1Sd@M
+ZoVu)CAqbdg<vO7__&|><usB9s@+N9UyK{ZbN(#_N#~=xpQB@$bNi;>^P2$cJd57*lDcxzM6Pv8c*My
+xgfR+Vh7+GzL7g`<xUPB?<qk1-HNl}xV^Q8*h_p*;6I||PtjbkNBp3#5x)y1%WOTMdMTXRA3ff^|6Jz
+JUqfcVM@5h#$%eeEb$+P`>bBR62RHQ6?-HQj?7MFJ_Nt)nw$QoM$OG_A$<T(@_D~r8IpBjOal6`=V$V
+fpqgk~mRAzlhsosp<3-H6n$^IchXoLOCt(5VF$iZUD%d)4kB6!Z@nwx>&cW&Ub+;>#XS>T3fIn9Dad0
+*p=J7na(?EeDDwu|lk8$XwQ{)t}PV?yo-aw+Z|)Fts;EQoHD01Inmjrx~N@wP?JD;g%S)uFgWrzJ+nG
+|i66$pYCtC9e%QcvlY98a%oo-WovF_1FUH84v4&Lkz^CV2HDxOiMu@(MvFKA8NZNul8p@d-}-sWUNTO
+r#!g<M0w#}>x;c26p-qNDk-I#+pxD0OFhb1;Tjv%7x)m8E0s8RVZcx4YsQI)K-9xyJ=YL3KXDIwqUt3
+9&RNGVCw;bp^#GH+IE+<Rt@V71(&T_PB~x5y$wn%Zxpj}j^+6m*qAbb<3+01l5Dm~qusRqiezEXNI4T
+I7fhJiY;;{NU<XA3oU<(7h*GO&!zH=QfPYa6d;{_tm0s?DS;JApbtA`n*xE#MNX8QI#=771>!fT!DyL
+yW;GMzl6PjJ}5_^gUN8S7+h?CPPOu6N)WmF_FTW3Q`DY2KpgQ=;g=qOym3H&9H*ss$c}G(?L|0W&LVF
+_b6qMQZ4!fnGKXAk<xw^6=_$)QSlyD-B6)sI6fb6Ld4d#BjOoJ9Qqq8@PI|chb{xSWx{aVzMlo1_$6#
+oID){n@b^lYLs`2*pugyVN123rs5vf7HO^3qg7Z~g<s0}^>~sha-U#!gwm1$76wi;jBEluXk^E8R`50
+M-9$4~^xo`;Xneh6s~C!_y!l1!(m5Km95~XdvlmQDG4L3r+Pb|iXk@o-_?X;2$X@zi*NX#wl^ERff`$
+G7)r%aUk8!9`&Of9!q1`<lJpHl!nEzU4#a<<{gRV<>vQdBORk_8OSP$1WSdnD3hoU%e50sZE1ANko0n
+AS3-%6N)UtUxaRC5YZqJgdnSA{TiTRKv7qr*Ki@AG2ee0rd#!xJ(jgvHR@s{#rzQ;(1}H?(Gwmga>6P
+mOqbwM;yKmla;=fnHdV4zWsx$X3pLurK0pbjct#UYg~#fcUs*z7mPobXi)=jKkbDoxGBnC)=gU`DWP{
+0hbVxtl|*4<%f<#<S0{Fmq*5O?PwsW)kn%cS~6HlFop{$^dH9yLEne7-bdCI-QGgSv~SuGX_W?BPl-T
+3XU;7k#`Ec#0VtV%TAq3W4O!HN2Q1s$@1*Nqr<@1To7vxe)6p1Voa0Vh6aw{YNpc}df=)a|-<A--!v|
+c<CoV+&Oikc2HJi|sF$Pg|=5BzT>^w#DNcX-Yc`sgMGIv!@>Cm+Cb#t%|Gr+E~-)R>!a-$=A&QIBS`<
+h60TGr~JLmpSBn1aVOh+Yo7`XptYui~?GSN-n6BQlL8z~i;h+>5!QX8*3z&g1^NhquSYm-6!3NrG}T4
+mzkJ(P0lZ9z2#zmt<~8;gx7h`rv^8dBjay!(CC_%2Q5}E3VjqcEtt~H5LIn-lbwD6eGkjD%O~CTAzz`
+s8VDuuf&G`03Dm=PSP6eIQdW}bj0$|lnj5qB!PH=>m|SsIYBlco=DT_0F})h9INW0WxOi=N1+n@H=xo
+V#{L2-eHROZKoo}&6x^^jLLvx4z(4O1g0Vdol!ou7;5Tz8gzuQ?gzhvP2=QhJMfNdB_zw{IDO3v49S<
+ep9;pi6;&91nPgCw8G;%+|E{2Ql1RbZh<jLO5jPJZ$_^$}I6L{|;ItAMm_vjY>CLc@4H#S1|Sk?*N5g
+AVIns(t^UN=tv5r%v0D|}<I4LM=&excx<i6!H`gE@VZm2G$Nx1rMIdMcD*Dti9@^Ty-<VyLwI0V-Mc`
+q38iEmZp0_+Jf`&VLIk6)mAtOdRl@3B7nNWF|yS042K2j)&0)>?PPLOQ&3QLLV>GW!K<{Oi@D?9@S)r
+0OUB#7RWJ^dn5J7n_@n_KoJ4WHABoTg1MhU4SyW^wg;&K^Btoh?!w{Wk)6DjAbSopAH18E(}j{nO%#8
+hQ=v!Bzzi?-GnVHLUt2WcYv?866mHPBsMMd?iT8<s3hHt?#?nx#gmNJ_&)n0%lj#K^9VUQoWgym$1ej
+zTFK{p2!}=MC)dk}11B96Z-E(ez7naDvUbC9jQe<55)ZO3=(d4NCEQ@!@%Tp;;zMODyLK)ZkibeFzMF
+~HLxm-CuCSia&^WkKKVsZMsiu$k^9c$?KrwZr-<~-A*P+g&+I+<$f9+IM^6w`&)Q!|)iudP$6J*CVUV
+Pn)3#OD^t2v9m{77~I1k)&?%ko9z^ZHUqGtJj?8D959mewGO_81%e?uGpev$aA<C7^}KrGYrb@1>aUO
+xBz<Ub7k9hR~LG!`P#%D$V586PHI9fa}zf%vrP3Zdp$W0@0&V**1@f0T67j)nZ04H;&^M^<WAGMKyEg
+GqDr#T#XK?~_FNzQH1o#NxaMJgdlpFbbmn7;8Ny9M*^8a3Mh`TStZGJ5^mpBa8f<xRH4b`ht4mnH=f@
+Ofqm=VByrjBH$IeQsWdh~cYZ_9@pji;`d^n7QOfcuqxYz4!<IkJh|Gx+-y>89vi8D5t$QhJ9UTcZE9f
+J<~0R90geN&O{3tq!EH%mKbHGXU82osR5?lTci*jMKj$Hx)G(W|Wb*P54s7o(fShF!O%4rwIsiawX*a
+zbSxD!S2oQqbzFq3<l=<1Vx{0v{UXp5pV6=vmbGj}#Ev&gxzT4qJ}X8zTSE)z!HVXnv6kufb_DznI0L
+?Qi&cP|6h=<fGApwW*SF$9821RAZbE?8sg5+bNXvdw66?5Wgg`aky1M(UY%dK3hC~AbFuDufz>E3P)-
+$B&<#)b`5|-Qkhcw%DBf!kS^`wGaVI#9_X)ybzxmyaF>9}Bb~DIa5t3K0U4jN!bVnb<e@OA0E>UkOOn
+mwke%qV$b~_AfUoUK;bg(_M5$Th)i0tHLkb^{>wT_8k$WM8%Ewd{gkS+Q=J=x1n_yt;YtmR-JxD#?-N
+KSqRI&0g3}?scIk@Lpz=%>6V!?D4CWnw2gof7{0kSo>RMy8gY}p;$UIn$D!#EOwV4F_P+gJSmt?as%9
+YvPyJHMj1fp-&e9tw{z0wnAk-UtaKfiPcxLA!fT&)lAK&VAV+R93rym06i<HAEG@B{;-IV~JQ1qsn`C
+_YX-Y9#BSjLNZxesWc(4?rBV_8&cbQ^95&dXXo(4)y@nC>h@r}+vntRPe60W*`5y+=YYcsetJQxacU`
+_(VBi}r<Rw7_*A;HZg^o<F@!vdVpP3dyBZCZ%?$Q%6aq>C>FKs2^)fOuq!Y_aeB$p795R}p7q7~+qWU
+7b^SxK<t4ANA;iWJ`1mkPM4XE?5+H+O(y_x>^Pzmt7=KwX@T}1o&Cq-IQ_tU=trGH=YCrtV-^%n{j24
+hHM!$ky5FceEM2=Y64lp#Bk*!AD?J;Zv$76`kCk74M(*GFd`)emgn@nf(*kl)juviM$={%rf9<cHs-*
+aNq>k65w0kyvzh<&a+ls-JB;65riD=s(e=Z}^PLcPAE#{gI;|Y2OdTN%9?Aq3kXSw?VB9_w4R2WRLYm
+Kb$Wc&f5SG`Wf~<X7oeqlJ8z6g!l>^`vd)t@JK;cp68NpJ{C22o$&r@!u<*SF7R`*8}Yn796$I20@HM
+~T~qMdBX{<?`Ij6sT>zPVMq18BOy97PiT`FtaeiETP0+7g>+bp;0)~G0RxSo^tl3uxn6Sa4Rs6nYi0U
+jhz=nQCMDwmB*qg;}yKQXzl7r1plj4fQ*C-p&G{Codow#3vyua&?c)PO4e~ow&=n;=_=stqyatNZnM*
+>!e+Ba?yqjRHYrf^n^cQmWG4MZ_)7Q7V9z3E8rao0b;bs?k-wS_q`BZ?&!SX}cWxj5i$9HlIOnc8_QL
+@w>lQ;`j3Y2XZk!N-I7z=TdeJE7k}GFEq1-XM;=uwAO_fTj$qGC*ar+Sw2qwS0EeCBMt!GV?alcy~s;
+WY?hL>UJ@E0JVzsTuU;rz==Ech}L<T5XW<n1UEZB`RE|L!?(DYnkL^nDyP7eXp!?{q+>!*pVbpm0W)d
+R*((mhxR(9xo=HIWiTX(*@npLZa)SZW!$eLSy!{^Rhl32)iD@28!U?1fLx`q&tY;ikXN4W-B$9QMA5O
+fgfF~n8<1U?If{{-mcnGpTZrH<Qr|07&9P_6?leq$2sY0&1Zf!Auivkr`H=>uaxDr@d)j6$5sK<4_>Q
+6h?o7S~1AoPaj`J$P{z-E}BAwm;d4ym6@*dp*$Tcr+z_7<%4IP0;FWwl|<FGQFr1IAAJn3ARhqbD!C;
+VXq&#tFLNzC-H$S+CjyQUs=7-I=jWumh@fXYwlIMMDniV0Q>v*9ET}t%qR0WaTipgLdv?C=L2+JZvv%
+*)X>U+K~;B;;LR339eR~Zq9yxlEW;iGW+?+{Rpc?cBUq~Maw?Ud#i;;_Z8USFb~f=ieKTB&`+<^yjdT
+rquCf0sRrm%`w60-h-X{Q%bv#ZN~gjORp^^b-4@04!o3ag7n1fB{D#CuYC=35qriAaxAp}^zI09ecGM
+AR39+irZtSZ6O7ym;)i3NP$1wEGCTqk!7ZKSby8g`eTLD*9=66pCX09o}=b4(Ae)HD9Zv9=(6@6*&HN
+9Wa0E4{o!I785XgC6X|5HHx-{Z$jO&LyBVJ`Uv@`}i(I;AyVi=i|Jc29apznJt&Zaa_nU3@>>Gkt5Wb
+oK<sPhx~Ge0Z8<=ufhGmEXURIEn`j0JhDcK(RqGaSM>IAlhaLWE-e}y8_wWkHUV0(vm|y1xl_5vH?PS
+Lz>%XX5vP`1K^fh#S;SZ5|n!bk-Z<Mt=x7LMF82;){+!%ErM>p5KUYQWCDcK-k|tvkM4d9_l9K#B!GJ
+ZIRy}HZ#`^1<)0Rft%q&M+t6d1yX_;{DpdQ(Vp~}EhPQ^KeFV`y(yiP!g|9E-&(KAngRh3pHYb0kU;_
+jQAus@#fgl3Y1{e@spxNLv#eE7kkc_?M_D2uh-f{-G2?!m~C-4SJ3bKjA0Z;t+jSP^LTLYgTaow|vzL
+QnA|3G++6$kl{9&SrFTmD>7IyScv)~<>C#mx|4o8i6{9t`PAut1rd({?XR=Le^DVk|rIU+D-xOSvZ7Z
+E4@)Js;V31}OH4w;ddtSZ=WdH@=7)e#DSO@^)W*F=c#4VR-B{3V!6}{)4R%d@W^B+=(nod;V0k<?lQ2
+pVSYw*ZMJ?K@Z<y*Sw#%w;svg6xyA-Xy7d~>&-C9v=+foj1)@lOq}NX-Ddi%hXfiC9rkv>Xt&4ucl3P
+RA~Z7?<7lDVygC@frej?+srMLzo$Hz^@dICk{257TrgWj>!uOGQf%PFmAGg^Ix5apNO*dqp^{V$b(q+
+ZGeMfO8K5f&354N~jk*i9(wNnU(8w3nk)B<BO0w`I&P#Im_G@11$IwaUT{{%u-ve9TTLV|m(wAREUMd
+ir`Y{SM)-V4y%I~<kx&b>AmPFxYd56|NfecwfrOG(RJyVe`<6F6SZhxypP?wJjavwXxgyDIMO?F9{YY
+Ph5Jq?Q(^+@&l0z7l*NhPbV|Pu|kGYRY>BGlh(}+j%`;m5;x8E7Pdvg4MnP(Rt-LBBpMz;7}cV;%TKP
+?|MC<jnSf=W81f>6f5L`CKSWmd9GKu@?HAqjU+@zCo4#$*1kWV4Kd_h|Dn~Rdp}Rv>huR>7SBsA$H_=
+|K{cJ8mJnX95}ntD7UbU9mFd?CpbDJ<_F6m(4N5c3iAtxCAVsNik`}rvZo!O=Lm0@%0lrPqvscQaa$g
+?1z+bW+W(mshLwF>+B{)Bu>O@O7IefP@*zTf6II{V4DCnq2ar;g%x+GQ3eL3jUS`PSFTWE(VP;Ys@J;
+#x5ov+^0Mn+l0m-HBx+*r1h!uTQHi$&rH$l#e`_^64H{_kZad>;JJBm70C=>k+AnE$h%yZ`!e`R|es|
+DTHAat!~s(qH2isox4w7wqo;r1L%fGN<<VJv`lM6u-2e_f2AYcd!=4z6;I%0~utxyG03RHz^8gch7Er
+Gkmu;_J~5}lkE9P|D=#Tg`^-q+6cuT6!{Ov2g&X`(_g(8^sc=`eg!ywXrCB*H){60P;!req{-cQB0rg
+>1i2eb%!m4M-;YmruQBrR4pX06Nd5<!{72vHM__rm{WLy!K_`A2fav~>>EDym8QSsxw`0?DHUlK%E;Y
+JKDg6omXG8f#VktGa?q^cR_Nte%-5>iy;BWdxi|L0U((HLB=^j%^7_0fKAu^j6mxMkNOOp6Estsp*Ua
+6D#lPzOv-OVToY?l~pMEr$@=yicO^vln>f1wB<{`SS9PO!%eI@tUUcWMpkyWC%l6X-u!Cli;wRf<@}r
+0Xi0AkDiXY6^OsXdmP6$3lWoWIj3WUAILI6&vRzr$?k0q@uF5fZpk~TkCvRm(xI3B(}W0_^q9y*Jacu
+QMnH16+(bsxK`vnvGBAUI3?tnr_o$dx9PePu4w5MhNG<e&f+`k8${w28$vj^Sh!bc1z$l1H$A#zeUlP
+=gq<tKoHWNKU}_mc`-rp#&Egyrdee=0J|0ppz%nK$xYe78-*3--1zOb#Ri49(dmoZ`B?4NOA8D}4O04
+7O{OS#t)$e=-6^m3#W}FohdRrd_uO-`KzfMw!ptSDlcg568QZ23RO|RzOaib-eQ8AsTOk{@Jj5&_AbM
+Om?k%%6P)u%k#r1N#aphbJtSYTLv;l!Z3!WWXL%aN)x$#__vagQDJ)imlW0^k;6B6fM0^02b(Yr$Sg#
+Me+8>!+1{@Z^*ta+LHVFF&+^x}9|Dq`6%c-LJ)qIib8?<mg~4&f9okV_2J*pqEK#K^=hm<><k0)OWbx
+X<1GH5|%h_;7LGNJ6T1(W%N3USU<d8#(SmI-tpnYyj4CkeM4-VuofeCK2heOZ~XawZH#Qvi!*)H)pvP
+=b^a<P)A4y956pXcFh0ZC4I)ixm@k(k6`@CXZj8raq;IdFrnMQqt<_as)W<PX*H>jOupzJLHpi<rD;0
+_B1XYr~dg9)bqz>v0A}*3sT=Z1<ng6;NlY05R<dpCQ$p0cw1;M`~fBO-=7p!M48xOvc1?`Ys`H-;vDW
+=L)lJ6`sD`r~i5W19X9VpSGF*&NTlZ4-}mpt=wKozm~D(&G=Bg0cHj+x#hLbt8+vG=dy&3>Hw$LkL1R
+d1HbK|~dhKi%R0yN}E`2+No{63>eoc5C|-SIu{GIkf6HOf!L^lqS#zmzXJEf)wg)nWqWXEW%*CT)w<o
+kDXT+(>#{D_ir>~Y@Cozs_)OkC<b}tN!*JpR&$nX-|Qeq#V)MuOr3yb8VH0_z!Ty9Rl@ltq3pUEj|9u
+7nRQrJ;)+Kc-s&ac73aHsVlt9GK=-=MSw?a~4$iT1T=c-lZKS@Ca750Xla*3iw$@DCRED{t%3vRXf@4
+l|G=@`Z`xS(ec3cbktlsVV`S5QTckC}R-++;L(x%f~%OQW)EEZEI?D$+Pz?K@bndZ(DA;Z!4A!>CRaa
+&7mArJHcPD`m$DR(5OoByJU1q?}`I>qfZMQ}9ceO;@x$he~*KJZ&jTzu&M(x2sA7xD9z9A&Vk#`@ae_
+8OQiS>%V!RHlzAV_fOVKff-Rb6H~f?MLg8vmU)hHqaEKYy}KPRBBnq@6Bvk3wLzOXMN&l-JpQ1!DJI&
+2`{JSnX<FYD=gBhG)AP?++U$sR&1w^d(}Jkdj1;c!+@`kkQ!`bz2J#MqO%J;T+gk`QP5#a?1(@=n_YX
+#pYY?hY=(GZbH=}w*7{#iO9KQH000080P~d=N3Ah0j%EP>0A&II0384T0B~t=FJE?LZe(wAFLGsZb!B
+sOb1!3Ma&&VpaCvQzKX2SH48?bT3ifWeyWq~Z76CF8MTP*Kg6x9MqHMM{<x)}-FZt||{{<*eZld_e-+
+TLS*BteFW4V&N;xMK*2<-84cfmS1mgXLF@|W*Z2Vl3`K~0)sHH`4F7Qjo(5&PhBN2rvtO2pI>4oaR>$
+)O`}bC>Yv51jPuxweV$mc?~c0X%seLq@AbWa~e+DVOn?{$`3Po{_4iI-?JY7JsqicXS++@+~J0f^)yO
+VjFT$e6R-TNz!><T3f-owbIZGRZv)X(RqApN$GgP6jt)GDbirI;B_M`FvUOt-(aT@xF3Qh3?&GgT7ab
+EJzy!Xq2DaxIyK(_dar3n_%!z(Rf8^y!n08<jj76w=LP5A9RCyEgtN)^_)6l!G`ajDOJql1QGQLXW>*
+8%@4#~09*$LPPe)@scVx`zjECAzr>lYb!wG;r8Z*rpgAWgQFs8RG#vB$0gR-RKYU%`^P)h>@6aWAK2m
+tey7DrUx#oRCg006xO0015U003}la4%nWWo~3|axZdaadl;LbaO9bZ*6d4bS`jtom9b&+At8k`zuCpS
+hfmL1(ym5_0aaPhiWUKo~kM`juXrp+iQ<o^7oxM34vXxy3NHi<2Nspc@L&Ae>VqVOJ(X6M6Bi?+6rTn
+$%MHEch(4da2!*CRo)&TcU(O2S_V)DXFY=0abHSgtj|Le7@NYpdno#rh{9Qo!cAk4A0fn=`<xZOq=$9
+eno|^%HH9*jCE%)Whedw;$W$m?vmoTG?fCKjC#=`7xIf1&Pq^g^+>&tUn-v3khGFT{av!LX2WU?wk3u
+?x*+W8n@!tBZA2%iU9l`Xxx3shmV~H=i#CsZMk%2<MowvUO@N=G?Wh(`bL4vIy-sO3I2j7h%NhD~6(s
+Ld1^MTWd<0{jFH%<ywDG@H}OjXeBs~b3}7roYMJCHp`i!y^~`XC*J(=nWiL*}(K?Nk}m5}mh4C8^v7O
+|fkp58>h^j(hs1{xw6MXrdY8y;UX!!O_R2sFaoG^~-@516ArRc8IRfD&YBMgtbpf5_oL>lu85OtEFB&
+-fqy7?;jqA`yn3@C$Hz-7|XF>$@5;8MX3Z11dm<qGnH5(*+!*NnId3vH+GtD8zi@kd3PH-)jI?aGNft
+fVocJVi3jPGD6nyfWu^>mPFquFP-)ANTuxi)wO_u{KL2lN1_QjwHP`jby?l=JRX1e)7n8N%OX>IAw?E
+=5K4`IdR}Z(`pzLiQ2&=UeNPkP=japw<KAo?Xo>7!_m-m0sx>xxRP)h>@6aWAK2mtey7DvoU5zOBb00
+1XL0018V003}la4%nWWo~3|axZdaadl;LbaO9bZ*Oa9WpgfYdEHy<kK4$R|2}_3V~hbMpN5;{zT^}P?
+shkib+X=FIOAI^97E!et(o>vB-2e=)&=?BQ;&X<lxCdU0EZJ`kHqe-?y9cGud16b#P!Q-QMQ$8?$=`I
+ch|qhCl?o&mzNh`xBIT>)t#zUf5c8w^sTs)qPVN+>tLkVmTmi}WUcP<uj$(#3%wNw)jtR=|2jz1oBZO
+!e;s;N8=>}Ht9v7My4{NhspU!-qFfeQ?uMGj+g>>>=7N90GP<Mt-mAR~&+A&2y=ohCL1goimM?s}UFJ
+#*y6y^XBz+qi`~7tbf9i{iULV&N0zPh%2Q3Sf4?TP#?zLWt?zpRqM`_m3tbh;oNg8pfWhHm2QMd}x?!
+?f5gesAF64@mY2_)sGQg*#~MN|Dw>sBXoSHpgP-1zRIFwxkHi?S|^frXckZ&<#I_U;1+VaY^5Y(MIvG
+olb%zX7)A7WZ$j_Nv5IkON5OSH2N;3<=ymNZxkUoY-gise}0pOF=cg)Vrdj{;jU0#!1RnYv@xSiR8X$
+ihF5yLv(GeN+r!os9feNQAwb%SB(^h2ib#Sz~XGzvS?62jO<t9UVW4e%qfXv)$R+`7!)`>a#|;frlQ|
+GKH{j=>RvTP?R&D&1aN?&y>6TOm^1ISl8tRJ9cY$9#ox8sYn#;yoTrfoP~`%+HsYfKq29}W0}nU$;bO
+U^HIbA$Epado+>4^?blYiF^b%+OsU3(YJiG%-JBmXAiy(5$i>0_ld4althTX}!J>+iFnN?^Ln+<B?X0
+rgz-mM(NRz2{8&-0BIH?D2e60INl{O#{)bARk4Ja+c9u+(yRu*^*fyveW@Tt5z=UeOXj5`%66!F0q!3
+tBxtVt(32_H|pPM0m$Hz~VW2M8?m#Oi6%Ksy8sfc<c-DJ*bN#!TLw4A~Pf2zWG)t)=F>$T09h0ll7>>
+^z559lJ^U%JpKU;Y}(%KP;Pet7r4l9WEQPr%moW0qWy|iaTJxRXnNJ7HuPG_k6^8AEMTRu?p_ZiFOO{
+-w2KYQQ$z8<u8$V>ju-XbccZ<jPN_$<wl|}BQEK4m4sHdozL~AJetYpQ5du$Yb#FyWUAc!43?8B%y4V
+~46Leo6S97Fxy13ti7w~QGBh64|Gc+HiRy&0;@q}XC>LJqVhr(EsvR2s?dpRA1PtlK##0=hR)D)TM$a
+Kb&kiRH~y1)6ZZDyvNQ#BqpnJH?sG?$TBimFOyf0W0S_*m4@oZzQ?L_wK=vWr^YL+spxA0%jjj+O$;9
+%4aT{RZixvfdTtBbvvt9U$_8<^nf4`aRE2p<wuTZ4Ys^mbiPeqTsr?<k;mMwRvcVx*|+dRiLeeJ8=0!
+1HoKrG62WR@^ZxET&Gyv!0{j_-Zm2BJM8x!Y=nBI+ZTt9GY)II|Nn*2MC$(N$vs0yiPl)1m!}ARrb8W
+fKS}p<ymNwn*yWyvKM#H1A$0lX_V$O@(ip%Xa|kGhT~*Dlg<lQ><#~R1DEj{tUlm4`MO}j?gI{Vv!O&
+S*FNbu!QW5;bM)3I{O&<|FR5%pJ2v0>Qs)Zp$cmH%Sx3i@t^%_7auoDY~%qX4>W<i`|`NPkeyrHAG1_
+CqkLeB%>t#0qz!5~oAM~h(Gz<41eoevmd;;K;IXBB+Txy6Pt@u11DJnBvTKy#3Y{M75Bbdbty`gZf7&
+<ptIanj1~T+pcB@vsP4M*Ho$BgSh%iXJFiSz9^79BVNHr^NyTej`g1xK;ow3Skks@tu}2CA-kI0F4#x
+p%G)4Z?SDUh8MXOtn>d)o9wkE8sN6kB9R4n>7wuT!cka>Y~#T6#>1W^f{76_;ii1rd$Q*?$wFyPdxN)
+^;H{NmD+1&0!1iKY9kIa?9fSgk7pF-M2;12@0Cylli^?LEr*IHyIN_vjKuCfC7+dy0c5SCIej4kK&Ty
+XZ%{|W#>X47L5eJeQI&GbR|3p!Pdj6o>YJda`lBFoC5J}V%fVIeSTTZATxgb7WjtF@LnEH2dP}S$;hw
+H~^n6A$%s<mXDPqHCKvb?g)@u}s${U{x&yW<8;cELZ1V~aTmkrIqS2-iQvhz1t@76jazkUZWU?Lhcq@
+rr2CHG@C*`6KNu&@$FqId}vcE^7%6V!1;7`+m_Z^TbG~6{999!8?Y*oGH=@KwCT)LFd7;Y<fzV?&gM;
+nc{lr!z;vjkxkqk`veBq@%$hiX-6YZ@mTsDM&i%uxRj+FEv=~*i)6M{Okx#t;=tg}FO|1x<*{p^9p?4
+|vZ9`j3}K(Q<?FWD)vDym$>Pn>U`2?21YNkr`jRN%Uw>%5&+Z|n1Fp20Sg92x<&d-+2*iMXWaZ4;ZXY
+g+2)+g1f|^U(k{kO)HDx_i(rt^=B$)NyB*l<BbQrJ{J5Na#=6IU;`o&C;SjCmD{PobI5MRUofJq0YN2
+NO?{~OeSVlM80S%6DpP=j46WdndO==kahR(aU_hNyKw`AKv$k#*tDyI@4}0MZ7ROp>>C8%l;r5=TL%Y
+(S78oObVOFM$04E7?5IyH2->F3?!o{yE~Pq2me~@FEap7A!Dk#7P>~APoh#!QwgU9~ZLVTZ5<%#MeVg
+|6wr19NT;%Wpbk6$Z3G99bx$_7hl$OkOo!MY~pcf^&^qO=wl05ckQQ?761Wo<sVf=YH9icghb`8?KYI
++z-!C=o|SaF{r1Nnq9)pa?e_PomeEryi?>a!2sCPWKh&_FkzP>t#mWg^I#oV`VS>JSYz$CVH&{DfljK
+0eg+4~|W;;XUYg)Z0R=!{Sa!I=iwlH<0HrSoaTA%R!meg-yE!V^r(puV8)W+)th_9p7)~9UNlk>I<>e
+tpEB`ylXCz_#Rl_mf<7<`I8Huww*GM>^Nm{5?Ax`-1Y@I;bcd@j<gNJUX_Nt){`)$WtPX3UH?O}nWEK
+u0B*r&+|olVum`Ft!r?CwYv@&?CZjBLWgPqn)BOb|qfEd_15SPi>0%zq~22gkN|+5}7kEd|-&-Tn@NC
+uo89OV6_2lv*3735=jC+J#q1PEao}%lb8Z4KZ}<U6hF$A))?X@e2^`dGh2BPuLuNYy~@IFAfJVd3XCN
+Hn>{sPvc!wz0g)EfMovd@cyL4JRVB^djK{l+GBjB=jH<gBwjbqjQXxsy&3Cdo$7C>kP*c%Etb!fGb&*
+@Od+Le#{0>oo__Jh+!~oJH0D$nJmwUvuu>z$C5POYRik~B;H4%Oa3_i)+EMo$Upr7N(^Wuqlp2qx4c(
+Kw)(p<_7a$Tl7Vo5!<Cw|6ryTYBGzb#fCKl7%H>T}Ov@C0*yXC{1|*_xE6=WUFef3!6`{;8d@vpi`o*
+X+!C2r-|#F>ZZ7hq<gyY}2!tonbUFpM=NfZIb5=fnR$tHhNsUBXleydIH-mBB#Z)&|`5MB^yApQFUF2
+?$|#7Fko0q?!nfM9l5Sh+B~fumhnhCuzh2L;Ec@P_Kb457LQzzkT8KJ+-#F*n>L|T;x)KtpBg`Zw?mF
+>K>=bR_+v><Q&$(rZO)nup2QN{c_aoZ1AM*^XxJ$caO3|<00f0B`$TTaJQpxh)>EKo8K;hcZ3lzSXuu
+n59(bH(k2P&#J;qS62s{Nh{PzI)q-b&UK^gfkQxnCB9icF^qfzS_1&WXD{%OTxHo26k3Gd;(0)nDp2_
+T^K(1%<B1HR|vIk$KR1rJNH-ly*e0-x~xG>)coUgzezIzHEJ!Kwa`x|hiRkza|!RZ>BTxc}>*G@ZGnU
+MNZrlZyn;-rdEt3YKo^&5dQ3o$w;Ad(GCj&V2%PS1uzfn(;QCuXj;I!ZW=jCXKWsjEc{9Km*V(`y-yw
+xkeJX;@(Ru#Yr_HDMH*nXoy9lgpab!#Fnu<1$*1XSrZY%)#Tz;!#iIVMur1(S3aT9@)Ks9s<+9vb(WZ
+8q{mL5mGq7!cs9vh#!@viQk|a7j)dz6{Y;|8+FOmsZpToSrg{&E($z#gWU?|2h0%g8tIf*T%*i+rm`k
+YQ#48a{CF`fuPS&!QQ9+$=U=>y;xKh5Iy&|^Ff?T%S`3x7J&Iqxb&x4mq1;QnY+|EBBvBa!$wiKc04h
+EfM#U{EE86?5FH@Llgij|mf9$FXK<y;2{3ZZ$F1xNixt|8YF{4nI+*<b#0nZYWuNh~n~7{?SL^1X~@k
+}RL#J_u~+Dlii(EQ-P-h(qo6Mjuv7Sli<qrD$keR075_zcZeABh%snjdDAbXvs)gX-uGDQ^z~06SUni
+$N#JlmglMxPe{}XiNzC{3c8!Qdd%4%?TgKWadP$M>I=L8B@yUbt~r4KzhdLNCr5QK@)xLmr^0;h!B&x
+~ns~Jf)kE*{OM8mK1vJ{|74r(scdFNX7+pTJbU=UygP8GnN;6*AUC*Jbix5kKN9@s6rZFe@x#>dwiS+
+O}hibOpvmwR&0olyq+6-uzAB!JX&zdB!)JT_@Ts?M?Arm)O+Up#CC5^k%nYa>j#oe*$jw0pfD)Lt?o)
+T{fd2Bdh__GCg1m8=}gz!f6&=A5|^RY=I<C-hoibkV6aBOV+flN^Or&T@4tWK*u$7V0cpdKzm*tJ7M3
+@4j8Ts)ERq+qz{ONL1aHMevyp|PDN)%=z@fbftY*8guxkAifpMmgFVbEW4988eQqFow@)-O24)fX}8h
+cVwL1o}SKw-j#?<Y*bE<KhX{*@g5jEAch9p05fR%fpo!N8CIn3b<6kr8+S(Sy<t+TcXQ01&$z~al@CF
+~hgjZA3l&Gm!5t*>QQx!95tEx@NZDU3rc`*d;fNkz7iMtCm#UjUO1$~DEg$3SOP*%lwcR4~H@#};H-o
+XT=O}H6d0lajpPA)^RR_&zo_NzoYj-7}Wb_E%P*Xe#1nXZN$w^l66?c;Isge)5)b7aniki8nknn_;e9
+8|n-Sj%ca?~l0!02Q{xQo3Pw7~_qguvd@vr7f}AWWScs97@POI=5Y`{^oChBRZK{zCjHGw=kJENX-xd
+>Od0XYQ1*O?VnlN4Hmk%FdGnsMmngDkB8#K=Or?4)Sy(0;v7quKw9ZcO8kIr7<G8V&u9%jg%v~Q(cM#
+|D2;cf9u5FKD8_11K0*{d-+>6bOjM#@^OdXU->H|D_cTUbh@y`sVuml`L^sp`4u30HQbldvW$rFJrg{
+CZP3%|qkqL`(c7ylc((*E2^qpFu%bd-QZm4cz8qag(9+;-mK#cM7G9`88xNUpei14l<XTBSp%<?EhNT
+t2N~`TnhEdh?Cd;B;=#;ZBq9;a{gO)c`Rt!(&r%u5d#{`ur3tin{1v5M?+dY=$Mt$v5e79xP0(ASEzh
+&R_eTVP3WNWgezegQ*<*$DEi~q1|vfkvbs0g{;P-Vhuh`6R-EX}2}M%y*$s?@0SS*mkOagX<N8vi=Vq
+dXp3UHO8iS&J)^jbh&IvEz$i3nY5Qryq7g7t?q-&I!fm(A@Ga!_C@w?ai>Nch|P{B5u##r(<|G&yBJt
+_VeTD^}(XvRBcZq$AgkGM4pY-3u<$Ay?7zuy=OnlK0-Rac=^)p6T5)Png2e%9YB}O@TGyC?ww<cf1xh
+E1&wf~2M*kwafFVKUwkBk5A`am&oKs8ZUyhMeevnKxR=@c_1FL~x`9>a_#bG@zfFfQ`8uLzhsM-!_y!
+W2u4X8bT@?WSs_Uz}>#Lu{)i>*_*Xyf4i$A=+%}%Xg|43^{qA@j?UU$?qnSYheyF_#G-29J|gWImeI3
+~vJh}+*leAd!QD<*xHQx`JwH`Z0TkxnV64$1x<`9~g?<Lf}Ft8)ZdLC<>2satY`@gg`LvSja1I;pWcP
+2|AO5;3p~x62-Rau)>LaQhpq4uX@749<r9)z%G0MLy=SXa>KpLI<0I;~k{~<~HQSgRO-uH(&=Y%AsHU
+f)kE#x{$tj5Y4z2zYRyoU)akCV6K2e(3Ht^1HPLQp?4S-F11~XFT-;rgKuiZuSoQ5Lc?>H(0H}uQ?nI
+^dPKRlHafZc;cDrjYHrgtc|gx%(kQN?4VVJO&@_)(QxY4#AsQvFiHaNEV5SzI2%WkNo}P31>(S9pjq`
+jjJk&FSWG*2{7cg^J<!4Rc;0(lz=I3|RmUBTPz{I`O_tL+;;9DJ`2qS)3RgqRi!cUJTiC}f=4Vp*?qa
+rj%ou{?WK4ueeS61~bCHG?BM4w3Fp|m~W_*8kD(aC?Kz1f`nr*@#1+v#|i*dAG57F2LDqgj!$FXy#+&
+g&#P;fVzI6LAuJ5SisNgwVNloacLTS2d`j#a~WSqEJFNQon?RP2CN>oVk}8YzlA_8J)MCnRESl?T?3_
+Qu2QQP)h>@6aWAK2mtey7Dv(^96)3P007kq001HY003}la4%nWWo~3|axZdaadl;LbaO9dcw=R7bZKv
+Hb1rasl~zr2+At8k`&aCwhd_q-(o2V#9@;dWW+rJfg!T%;T4NPka`h2H5Bcp~$(F%Dq2+=RyYIdIl;6
+Ph)iq>_3%MMFv-$Nqf085@7Z-`fUk;5mDZXS_TcM;$UJpM>GA)FGN^w^rVM)t`G++w^gBDZ{lNRU_wn
+%Bik!w{!TC;4$mS|F^%Tg@f?l6L=YNaiFnNA;XwN@IKfsf2!caS7WRx)EE&297@+<glM!*K%iA-ZXS8
+evTcOaiMGV(FAKTU8{Qk>V_KS|f*bAt<7PNnTQZV56K4NNr+LA3!+Zn8R!)q_DHuz+jn=;EL&`p|7je
++CQ}#S{>c<9k~crir;vk9@fGZFmJi$q?r3(rlzFe9Ms|`W>lM_u+a?OLqng|svca%<I53zRT77ty_cN
+sIK{_(gCOMqG@Kq_iW~+VB=v_>Hdur}{Er?c9d@4aQ!vA<)g8(RI<nzTK9A_@#S_oB!MGarU_alZ;5A
+IUI__7n>$9%&4luTz_CW?Sz|gX>qneIsVQ!R^R6=q)3$|l>3h_~^mrVn<2fm9{d#h70Pfz#fK%_T1g)
+tQsDrZjwMY9+O2`pK2Ap5kLryFLo!jGKwo28*yi?W2w{{ahSi(ogV$sNyMk1VRR)!youcMW#JWrB=%_
+JOn=uOn59@$IE(6azmKXS-Ug-h?QXUz;AsKic3>DeL#Aen#CUC2rAXNl_KK%Lr{JMU0l4VL5sfjRbxu
+e<#MlQK=0ja1n=;rj^MJaAp|@8Pu_|RMrwVtCW*r#rw#54e)((?*$8LjFX-HOxurpYri9O>)`{a*ov6
+?yEZnLQV*nQf}D(L37+aQ<&j9gj>bKt2Vo3V;3+GxVn<fO4^}!nyhMUt6P#dx=`w}F+8U??{@V<FEL8
+*Dqq~zK-2b9`LiG0wb0ul7H;@xcle9*FImSTx{t+HxIMh(jg8N&Ku1BLPEYz9`9^lgc2oz4Pkl)JYz2
+V&U+U+VN1Yh5*fdFhVVBtcq`HLWuHb(m^(~{9AJjFcfzaDIbd}cIrp|`jP!0k1eHMqScXf?8m2#uqe{
+TtJCQy*)O7BAE)%xA|G-aSf!<{NJn!29z_V|y;6H_ljkZSo^7&_Jn{B7;0c=du0(P)h>@6aWAK2mtey
+7DpF${k)3-001-t0012T003}la4%nWWo~3|axZdaadl;LbaO9gZ*OaJE^v8mkx^^HFc8Pz^C=E}Xcg`
+A5PTT;Fc?hOd=sg$y|%%cbV=%vv3>Skl2+}uK%4e1|KI)ZGKTqd4w=e@EEmw)V*cPFjEzPkrt!T+V@*
+;ibu{dIuo&A^!oW%8ZH>@qbrv}quoXha8-5VAuz~`G(mwdw!N9}7T#Ut2!r0@Pi<%#Z;S&qS_Ir({QP
+SXkzhFRXL;5}jB}&xXVh)8?C*VCf38oo)TCbnqU<uo(*F-xC=U4zeysd5OOU08w4~CZT`Q?9|VLYR>A
+S8HJICed{_1T@Pu=5#>w$(Bu85_f9y;?7BhDDf)lJ$jN*jo{%;Wn$hm-vX6SpbAB&AH_>m`;x$T$iSE
+O8u=m8BLb^lLX4FP?Bp$r(h|_9y9_os!4SSl=#8i_ZoKU)T!f1NQwf;hikjLib31GDk;%Azn!QPR@`u
+Fb=>t3rD?|NI!&X=4Rep$`j6d>;l}R!`CHzt^g5N!NBrNI1`=|0r=nYs%EAlXz>wU#>ftvH`+d71`U6
+l)0|XQR000O8^OY7y^(yW7<{$t7_;COL9smFUaA|NaUv_0~WN&gWa%FLKWpi|MFKusRWo&aUaCyx={d
+3zkmcRS2z}eFymCB6cbh|mV%5IvEt#{iblQ`Fz%Vi{5glyInsU=7`-cIjtzxM%v07%Nt&duFvCb5N&2
+jJo1eFHuc#}AIhs><^6awghlef$JV4h{|v4-abj_g1P#C7UXfg*x~z|8sC~ew8Z$m2DvbM%ko!sYG)n
+#kS5jX?-iIi(loc!Lqaw+v-Nv;++9XLmcFrZB;kWyC|S#UX{ueWoaXud?ODI)^)WJc_nnk54Vj}XK$w
+BL#f&=7Rl_*R@NzY=f=rZRb4C7A(QL0Et&-t>ISN*+f~!n(zbe@DtT5aS*pCrck)HPYV^=a+BR3F*^l
+SvZ+}QtzIq0Qx_$+K^9~Y!{WYx@SiQ&q(o|f^X2BJUE171po(lNBy)}i<TtnNYtLyYqs-&%pBENW?ta
+Djp7P>~@D=ZxEQOIqj8q-IXu*Gt*0W9R(v~Cvbs@?#C(=h;TlNY8lbWm~uHDitC=at;j-kC1(fq>5oi
+KD)(>k1b84i<e5+gY`ZE_qv5O$8gTzy4IMXb0coa8x$Y1CGJRt<ar+&XocZe!sesn{+D9%3WII*@t&O
+O~v!8w!D_v%aW;_J5c}UK(exIXqvVLOG_q2UGToCWx9c4)ouWeJ2OO6FsVz}9JRm=DASGPeMkCXS^$(
+y1)i&_L6qh^5k(^`GQ!saXmSgKL4BEH!BjNW;#%G=U`>l6S3pd>B}_+9R9-6vlsE8Q75Pf4sh-KNswx
++7u%`kJ3$~Ue0W4Fb@&ybHOP^Rm-fYvxUl7wLZ1hyX?AwyYrr*`J$Q$~N>1&D$nTiWMpM*Am4Cj<*rW
+V`V)2UcprFABu7tRaXwmFbwx!{g)0{!`@CT&`^K;X{lf-eULM>BB_oJPq;RBIvy!L7Lgvz)wy!v}msq
+@{pOs5Y?ZRaz9c*nL}9fG3q+0Qah8me;WB$-%pqFV5b*e15)ofBx+JOw27704OlRoy?}f`y7eKCy$&q
+>#8k>+MGIVDqxcJK%2)-n?~Y#fr~E8<fhc-k4_ud&`nwb4F_$W983;gynOZS!%yc6yP%)Wem*;g1wB4
+Fc>d<~`ODYmi|0Rnc>TA<`?LRe3FVJY!~^*6^qYgT^Ox`Zx<5QRJvbmz%G%9VK?#0I=*z*u!Kz4=GMv
+erw4d|Oc^R_^n9OL_e+HbxoSR$v27DPP8IA@}P^@IJ)~)aZ!r=#?jf}+!G!k%_fo?^sWI~d|w1mzuos
+XZok}Z5U<VkRmQ%#(D1UDDk1b?xm`C61!LzUe$oaWKP$Qv|Cw_90eF;+Xk0R<rV5IX(lf~Nu>wgV^nQ
+#2W?qn^$nwFJ>siYQ8ctpLZ}^8SoxAYW}{b5&&)$qUW@;pnCt6(i&KQB7Nhc!qijJOmCK92l|9<x1l5
+!X8OC{4aS?06SUX@maTUoV0*(0o+{4622=XGm$C)hGI}uHT;G=44_(Yr^ijZEr6WG8{iE!@CsZYC=<{
+%=fSbecC;OUeKaDiFY*S@iHmAyB55s=^#L)sGfCX#AGlp}5F~)bawQ2-QxSJuZ8Gt<PwxXsQd0)3axJ
+b>l{QVSAw<C5v*@4fyI2rcU-bJc5a&$#ZaIi{aXl3~+QUF{gj+wn?49y>F>V^33^OmR9-Wo>?2t!%+^
+J8!TAbew=eASBU12gi+;1H7bydpYRvik=AsKCkpZGdq8jT~;a-&JG=xdT*6W9VJ0e^deiQOH*b}q2pW
+HPkzR&2(+jH$>fCI3D@uUk2BC_>3`y1B=hM@>Y<^#4@!PkzT_FRJX8HYXKUPke}a;xQdSZ4BIp3(#Qy
+{~3VC0i3Qe5+1D~JaK6b+xhQl(XxITAHJfMZd#bt1zJ7TF3)6^98UZb-D4@v>AGu(7r1yD0jC9{q=I|
+mD7J4YZx-52@EC$E8)5{w8NdBLaT?=l`fa&fH||<P0f7fzlNNZOc`$!L!MR9_{j3G+_wp7T3K>gU^tm
+2$m9!Y-Z}(s+N8&|QMxbG)NZ2qPm|w|^pQfv3IKbQ6$Dn5mY;##5o!1p?#uz8V5q-wMf@GYj?O=U4_|
+Cz#`{}m&PR18gyM*nEF<zzjkf+5Qi;Kot;1{z$k7$t9?j}qyKv+H|j)b=9sj2)7r*@U|aYTE{-*c3Hx
+Sf9K)lg|~f-!NBDY2ALtm_UiB<zBi&&BERcCEpJS);bO>s8aa?*KgB2Vlx883GbA|9;0QFWB!q+;Lq(
+L*G8d>7l;RZ_CTtgF_rXb`3uKk=Av3JA}?PF`PNTz0X8#;{-LQI5-LQ8tPm(NQ_IFU$cChQ|~(jxFx7
+LVv-GMHD6x^l6swN(rd?W60g{0uxM_#(6#ZK_fKZMcBYO3b}C#5u#8d9Ox!{1EB<{@p%L~Y`nm=zFjN
+g{c?m=h92Om7WFSq2+yNxO1eEv+!dtS~K)9P;Ab_w{66(zaf%#o2$m$}0+a2;Pa-4V?gxbng4l4(ybd
+!VWr2)fciNmkdVI;m1F_pQD#ivaidFnb9ljp#%(b5k{vzzBf0{O}893f&@FK6JRQmPM9er^aPv9v2h4
+_1d$9+|IRBkmi}=$Gu=3+ZgxwwAlRYL&B*fN<JK+vgn-mlCLf;_Of>q?yXk*a7zARf-&)#oCAk)r2N)
+A0M?uKT3dNhq`h64)x^xb4X@T8>WecXCUsK1&tt>@H2EI)W<8}FGzVa>Ns2K9^!lb%hRRyVQY2q18TE
+Ph3MR(UYXjSZ@DD15Q2iIOCaZ~e07C-h(d}6Fb#RI>Yc1L{2A%3f_3>ByS4a>@qM_>z=W6?zX*WLyh_
+c~W~A)`O_4u-ZHfoGusjYr{AO6?jOrO3ziVX?pjg%=K%iEs4-q-ptTQj5IUJ!Bkf0ZDtO{|q)_S=f7?
+5Pt>8T(qL~6GZFa=P(8sN)fTcj%=Bj}Zy(f@SV?i^Uej(vf=%QByd2bVo3A!-oyj-i2r5ce>HK)nYA{
+lGJUfdgPBWmdq+M12S;aZ0ArM37eEb<)8Ff9zU3r{ce+HBQ6?AcAF*9i`1SmokVi2Wvs4_aa?gb1mT<
+I2Ej`1dY2@>7}$s0Dhm%%!z;c^r`X8ns|{@14SU@Y#_-T%;+yK&m-WWS532>J$#5iF7h&|>dS|hvax<
+J4I$B|EjH!h*%BWC-lwMr=5TjL@FFksX0c#aX&DEfs5vT$T_GH%pv0&Jekl=tP)2%(U&Is>6e0lxMSA
+PPp>Y#}GNfuMn#?+O3pNW!w}jkmn_JpcMVm+FHqRpQCQ25fYgZ#o*3~^{lraanF@N(~Q&EU5=<wU`*u
+i}Fy6D0HOSX^x>UH5VA2Zz`9Ov_fpeUbh--#dS<V6Bw$MW^e5;A(JNdh8%0}Y2nyf2GMw4n-$-{!9EM
+vXNOp9{xJV=LK%jR7`)(iWj1)~MQSkG~64rFYreO{yTq$i92O!tO(WZK|a3=bVVwuP|(9Zm9UcIeBnI
+Ykl$@^cm0f_x9+1r9M3+oz6j&qx6`w-;WPqM2@1k>WjoM3x+x^)dquxfDmBCdR{<EP2#$P&<YA5IApW
+6oXH~(&0d`K_|^z=8nf|!jo~=%{`)PM`*Z~0*AgGHB;eN(`><5t*J)<ukb+-Ne9O{;UuPJYOA*1Uh?#
+W<v8$bgbg{KMT!{6d;L@~j?Q|L*MhDp~xoH~;@_hk~u$IFU)tL>bdZ193pHpD0Wb<;6k@S%ZIj+?GMV
+6SI0GcC3HD!*`KZj;Vl}?oNo3v~(V)=h0eYyXaNnq}(+@aq~I%=^CTP2EYWDE114t$G`rza<_I3Jeh4
+mUW`sxsjG!f3YPR+zRvKlU6&28^Rpd&*){1z?zShcJA_iXiH<9CYtEybppR+e@qAEm85O^{}}@bK+VJ
+xTC?QBD)T(I|p34Mk7$~;lgICC{p@lAdMCsR}U9v@L*KxJJC!H??TXhG*2IGBhq|OW=uV8uha5;?QT%
+3GoMbo^RzlgoAb4}LG8_aTrJL&k(-64%;{6WNb<TPJMw~H`DMPNn2NDDd>;&RZ@tO9=-7zU!8RCnr-T
+$<)krIir&#|SX{8Ym>z^a7kREghL`S3$9V3kqy)nrfK>_ZLo?lmV2r#G2X=~#!z*UJ6gVl;`AL|gKoL
+(dp<_I|&6MCG%U`myELzWqIys7GIbp5AVfap=Ofgl*3RX9jLe|Uc`F00_A;m8TudwTq;#`Ki7$V8fD+
+L1Pbb)sqdfAR%dRgW#-p<Q7HS_teeoyYP{2c%QYJkhX6r=sI_F7r!h3KT)`c%l=nVZGjCB3qYDg?V(b
+$H&~MYkp*TlRG=jR5}hbpqm)91*gpAGkcCTA796(<Eff^?F54zT|XMi@_{(tl6Je5^#GH!$v1S$EHAj
+%D$u{-E&*8kvlY4Pyu{C|bSHJtQXu_G)Ra>8bGm(hkr8mcmQO7l!;2y>VZSIGr*q*R0vI@yrA`%fI;V
+;q9wZ6#a#ge$3(78jXVSsY*t>|FY#Znfr+s@1h$_Y7<g+&`RF4#>NteKd+Y3yQdw4C&60ll>CijnrM#
+p)$1zz#+^z`u)ch?*bFQIoR`e*GwH&XW`+Lr{NyD(BH|Az;|Bs)GX7((>Zo=kW%12$FOvh#{ImigkEV
+zK2hr_wQ#1XK7LG?7i37frRCI-r)w#g{@hE6Xs`8Z+O7ldVbW?h<CPprk?L0d>Hz8%&9Jubzv?|Muit
+*auW~9Rx<R2yJp)FxqKI_K2whlZoRAW(d}eLJ1j@K0D}*?;F_o;bZOM=e-kp)VKv~!Rkv2KPZ|3=6gA
+G+xK@9>R3BKRe<s}0qT@`PB{w<<X@d$ZqQ<wcL}z!C+D>1yPp?}v>qmfP+X-;dyOB+B&{!%A0Z(JCrx
+jtx0gd3A8KGkC%>KiOa1!{_zibYZ9YXJ4n0UipToDZzDl=B+*U2{wG`H@Esx($e*e>9g5KS<*`K~+u^
+l9?@}bh_Wzt_QqW=y|c?<ojrd47$yT(HhpW#V*17s?0Qa1QfXm=~lUc7#0lws`eg{ym1LL<!ShYhCZS
+(!$YFkBzf!v`kJp;$<`v<R1Ug()v3oFQIikzU9miM-W32j<S2P7PyguL>;$*c_ehLKC6w1Qk%Gy+UZG
+Mv;ems`jU+yL>$t(VwFb(ASI$7X}`hQ3${lz9t@rt-QtjfN>c<wUqmVEYEJRN<s>`U%sbhuKTiE%*n^
+xhkj6-Yf&oGFJTJB9=N>hF}QXo_?It$%lv2<s>zXsA721b^R{7-CNq|PegRO!DaYVCoYef<gWesYa<#
+C|PSz9s<k-*_!pOGm)7$-llwNnK(O2qUzfz=sYt(eG{3Q=3LKrw*uQ2b~xl0gt>G|<TLyLxXc(2(!4J
+6Q~Nnn6=;<BeK8e@<ZMq?7*OIbAfewa64;*@={-5Cr6cXQ&nCkv3{PAD6#l@r!y=n4iEP}p5h!FcueC
+|LKHz<@4I_ATS4XXV~y^gE~`Glg=lJ!Ao+zLKkJ6OBPn3EnRu=OSKI!F&Y#%~4-5t_oakD(bd`Q9whx
+yn+G{xP`g@;2E=ESvPqZp*jrGx&t)i$O$r8yt+b`myJ!ta<`j85To`>05d|-stxyt*fa84>sfX85@L2
+IbmOT7->pGGRTS|@Uj~mQTKis`AcapSg?CwuoI^GhI-v}mTMlV`a;DSM0xX*B-5k!)4rP9P4*1a5k)F
+Yn^NY4I--m9&O-6?4?S@6rYzVPkj<M5aAZZUjtb73doNWLN$XE1TsW{>bR5C-bxz?gNdWE8T(>CPb#<
+do$f>YpWCwf%9guOYO?wS1<>Dyh$KT*%<6RgL%Z4@)W`!Hc2>^Vk`*0Me}kqCkut@xH%w1=e}iJvi5;
++S$JaQ!-ALM8V^aokV)nuJoUpYDB+6`EKuZj3pf<7u+46J_XT$zZ2DvY=6eyvLmh_p^`v+`Z%xC?3Q4
+e0+0rb4<x6Hd#N5e9{b@pbU&@i?%DE<RkGc%gmt1G$>-G0b4?$qG}Re$?2%G$wTV!ri@z_M=rp~&%x`
+7Xm94d(}IkKDB4dh^g3w*AO~7c1O_-@VMv&mxs1nbHj{Luq`ZzMw3?u%kwp=b0ahj0B|e#o(@B3r?#l
+!S5RMQBcQm{aPJ%>0TK^~~Dm-@1G!X3IK!*C4@vdM(u5;JyzK!zz`108cu#K5gJkRT;Y~paJi8=d&S&
+e(<7o9|XVjvejlYn*P&!gymS`b;5Ls}5&G@M2SlN~rRpk{w=Y7En2>Nq-~bT$@n)><{54VY>${f$Jwl
+}>%=UE+1F6}nw?bK4W)m$$L`K5;zR;T-lEmW(^nFL)o3vKZiSa{3YHv8BcF3V19s+I-a^wbxuv+q>Ex
+KcE46?5ilM8}5L)f0%`rHwv%ymUt!H_eW^=00$UCNFTO%!=DHXLDh1N$*;6i1bz@C3mv*rzgQ$Kfrx8
+c(i`NR#4F99ED4`v@6E$4!yC}{P0Wi4+=LtMMcdpsyjMw~B;(!Y#9mg7w33e+lUVA&@N#K_zlOps93I
+F7NLuvjb=SBP@uDU7x-E$mEKB8=crpCSgp@5Xa;WRON431PxeP3~(gNl5EvE>pDf#y}Q^HwFXELNEd3
+X^P$DZNqO4?jaU}kjR)LqOMXKf_nS$PXA_(EYt$@h7>SOKhjxeT@ta7oLgNCof~U>$&XA=hiVQr(uIP
+VpWdGV(jGr``ifwWRCeur^qZM9+55)^*Ob$&_(6#s>U{c<-X0Y-Q?4tk+%-??s9(QtKFW&hr$S>v?5<
+U~MY+Ts!OYA?%M*Il=@xX{E#IKj3|!ps4meP-yh7!^T;BUW`iT3?eDwojrR&ks^)Se{0gawMb1(nnUh
+}jdfs6oKreDT~c5R9O)k@q1Nsem2c2L+$ws8fYUV5Vst>cnT(Xme%ys7O<<CL!a;Gb_u(BW9$ByTeu;
+`2eL5k3;v~*<29AGcL?=^q=}J0H$0qD_2~#@7-984x`G=ev65$ciUD0c<S)rGqY!A$0fl8oSEHDApt|
+>C?IqFRG$ay5fE!Onq7J(f0TI856bL$qFBy+b67#w;YoDzKiypBb>1w!)s(ZY1mr@OP5NNbD@QuXmlZ
+0j(m)ZhXz#Md7NYw;&NTw<GbbT<=c(B@KNHhD&jmp2GhDBhd*gj!IilFLMVbMg(6-|5LokdNbRK+nej
+W1#QsIPaWi;)R#~W69~qFh>gdzM@Nj>4rQj%cbQR#^AC0^~tbs9jtdH2*6<%V6`UsY1LP2!b4|Enmop
+6rq&)$pqVom-^Ua<)meOg%)3J~3>MRc>uiT2A>(AHiHCp>il)6>*69t+^6zb4o6N)IlASknXB=uT^m{
+0!celdfGw0??0Kg!Zd4@Mwkx6q5ivpi`!$+rI6_n=HJ=x^p1ZFg}-LG=Y+d^gA1dZHX9#5ciVVI4<(K
+#H(Nl<jBJf$1wluQ7&+vg?#Y43>)z<p+Kkl%xuas1ltPTCPO4XvdZ+_27J9$~TjtJ4u=nYm!vEhXqBl
+;%eh!Ye_11<qP4RgrPZA${9mxDY_&p7r*XDOAtPe+mU($pI9hGQ(`_u0wwK8i?T)2J#Q_uphqp+hMrv
+3}+C#-ldf1=2D8y4&)SB%=HUk?$=bjYv=mipL7Frq@Bb!!h~kOZ~~eeDND*sGzq&JUoM1HCw8WFo#Ty
+iy_wyyq-O#Uawdx=^^YVSBJ+@qxiCbp3ZeF^TBCu`#PFU{8BQytDY8l_HKdyfE4l0{Epv5+tHIcgS(P
+#3#hJIVoW`z61;tbv(qIuWw}J&hhZVh6BSlyRA7q-?-tZb=GuQI=rm8_LsxMoLwgm@J@5S6c;g(oT#w
+Y!){9*;4GEPX(@HMg-KN3+7vl@d^mvC7cgPw~bOb+A~3;paDYFYJdgS+sZ!+;Pm27jsX%ac{?=NHz}Y
+l<QH3#11VbWgKhTzXiD52$y#N$-v@9UZTEj8mi+$;iQ`{W4TW9v|PAL6Qk7tt-*0WVV@?nW%g$PU$%=
+*Nrp~kfSCh92c(5ou$*y3-)jG)iqg>nqy~`eoq3_3=~iY!{DMGVxy_>A6^MM0IW!(>5DG8n2dHm57W_
+@Ja89PRrJAuQf#i~Hu`Gr4CgjmI&hXq{L!1gMV_^zXF`Kk*X~edRsKWP)&J6H99bI}iu_5O8pIb|odY
+a%=86exjtxb?^@hfePM?^!uzHV;={wWT42EwjQ!EQ_;xBl~aW5cepB~(E8#ySXTg@XA%awh=Z2(8zee
+Aq5(U{lW$fBSKg%Rdf1tA5IbB*ydSa`&Dbm$iiU-wl!5rdeRp<O#$>KhxtHdR&Zq@AByI?`}pN8-q+e
+Nh;MAZgaK7@(2==HBS`VFpg`!Ml6o3(b2i%NPn~Zvj_Vl&VN-5kveRT2$;j8Q9Icpa9<R+kKPKm4kMU
+nM}>XD7QnaMtw%N6Q<qN`ovKKAQ{9DFoNEBfo~=aELOKj;Ge;-5i*mHorUZewB7;K-cXE(E>!*xIe#?
+OI=p$%a!%*NpZ!^*9h~Gi@e~wtk_zz)>i&+s!}sWowc{*;#Qc<ZKiyS%rbxo}<u&^r!PcojaoeEs3@6
+C~DnPyIW&w&8DJ!%tf$P$$mMGiJg(QWS?}X(byRY!vIa5kAcbG(bdbNM7l=KW`ofo`AI@ApSCEnyl=@
+@JgJFGJOZYjC4-23ph_1QYWJPwJtvq5BgafsVx+F49_)2_~(?$Vszqp*}k(c7%EHA9`tB`l;7<2VlGe
+2)X;=SaNg_=TYO1(~J#vDseRK>+x{Uc6oA$bt}0n1bStJ}q@>l<)&=;}PRqe3OL%_u6Rb;Js`+kgaPz
+oHH@>Ky57hVRQ}Lc~$pr&;u{r?g1UX(R&~G?pwJbXbslSSQ*^y2Dm729m;%geJB0x2E`w!5cg<!V!Y$
+?Zo}yXp>DfJ(-ZJ0QnT@`zW`YMO~;Q*6n%YS$QReC1A6?CmMC7_98<Ry2G*-$@CmtrM~fXT<_MkAZGr
+JO!y45FuUXIvl#!Fhw6LHhUPN5zMsxSM;c#Lu!aJRs-#NDZJD%VKAxxz|*f@gBZHErBhgP2f2H9q2to
+;u#GxgYbM_*ocgIZ}=<YgBb>||l^vTjc@a>M{k<$Qx+266`P5R_BWb8vJNaO}g-!ZX;00U>&)@a%SJz
+Yy&S&kkuxf+tPRv>(O4&{TtRidXYNgr@62nAk5{?b``GP<?*y=_SrC3^0GQ0sdF_r43C_X7s(8Xod-y
+#$OMy1|m+q(R~m^K<E&|jtReIY@6RFscQfL6Xig19W|xI0{rdc@uN@ljhEq-Q{7xc#V-S?<92_jSF)|
+P@qH#dwy*dhnnI$c=Q+xu>^Nq@Sm~YrJNJwG>1JaJxU1auUZG(3neRfJ;!H^)>;-}S;Ejp+g}H(#i(M
+uhBti^oJ)=x=pA3K3mdGSnM2w2Q=-g4AIb5GR@w^)})paU>b*T(IU}<LPg^cFnWO#yMm}@Y{Z#=&*>`
+vPePx#I3K7;5#iaw#dMBU9_%so|`!4(_~*7lSG)9OfMbZ@Ve{0KlT3yE5>CJwEDeMc65q#cWVua1m9t
+vGwKA6fPdK4o#0w)nCe!Qx~T)68|Sm?@5(4vfNl;nC0x);CsckjNStIx%Z1{MWz0V4-&`jlp7pESK7P
+T`s-68pm!4ef`csM2olH(6hsA-KeUNV3k!Is<!aV%PepZ(0zqa@dD%Nw~xLx0h>ZL0E^x=L*JG)(J0o
+}q?git|JYu;ye_L7*tSb)ldDpU9pIgES})4m##zjifCuI#%Jb#2wn$tq(JugdR_CBNQ1T5$2V6I{6VV
+uvx<^2ix8xf@_!Opcvrc=OL2z1~sGJw2>)%gw+zo@yHB5kv{QGOK%nK5jR?s%pF}p5MIn=`>Jq8?-m_
+JrH-JS!+e}Rkmn6a@5H=KUbAHK)8JA5haZBDEb-EFf(_M+xdEy>33-wKR>s;cXDtM3HZ>jINdtUMD=L
+&{o0A&{Bp9=l_fvb?0fW?{2BTry*L-Cx)c_?*&K0<*zGR-=WHgBpm%Ncq0_I_gc!PT6GUbQ24=YA>Ys
+6>>H}GhZJ!uaf8C8_j$1-otiJ=XV(*vOr9m*XsU*2iG@r_jcH)4p_#<$2y<9i@&WM*Y{MXjyg<CKi>D
+B5B^ibpNThg&pIt^B+ljq&|l}E%LQ=y5&df9Si7gr;@PB_dagh5G<?MvuM2gX18gA?e|`VvHSxl4zCH
+aT6mcfwV-EBv+!<z8n6(R)0k{w6ua2Kg#WAo^po8PbrwN=bJjt2>b%qw$h2J4#C80(2lw?o`(5!!Ko#
+q8HYcy~Tn^@A?P;f2r0Y-A8Y0s!;y#ch89AeCdC&?JI@c-tHkHAptcFB6&lo)kyX-e#N+8fQ>A~u6e9
+J(BH|LB{=j)Q4<<RkG5zIl$@MZWBnF#PsA*c;ARy^yPvGD%P+!PU{;j)22g{8hC9nMf~VJvqL}`S1L!
+99JoD1thFpUKQktAAaHa$j0_{%0ZTAs61UTXq3(ppOe79Pmt<C!~@`V4@SVD6Jq#vcT61&`9ioGNMJ7
+JM-(Xf6HfaAar8%UYDG^wQEnym7|^VkYj{W8=5zLw{}GBWN--UWt|{rnCR0<;lZyivH-pO$IXhJPf1p
+agA2b=X>#nt37v?YG=UU#fDS6Fv2K6^0y}T>JyuLnF7ayVPC*8Nl!JW&ky>Q>aMqm?Vp4T!N3pNV4B+
+OHUX|-zWS_Y}V_};;Sq{?EWkWWPZ-STfvPQ>@~zG?Km_`^wH8}&hbrNr|hM-jqjR_GhYA+)KRzvEts#
+w#R$7@fhl_nN_bK5hTp43L;SphF%_i|p8MJGJCLcl>i?v=@~cdN}eO8&E2+psZ}5(^geQBHo~()Hr@B
+ley8UyPU<2T|iO7htiRwn{+*$uj2jrj+KeLUZ#2J$oB{Ms?|)qXZc8hA-bmv%+y$ZUDkYe=_PsKE^W|
+!V=p+Z87(<{h3~%sP)h>@6aWAK2mtey7Ds(S{wRtX006pP001BW003}la4%nWWo~3|axZdaadl;LbaO
+9rWpi_BZ*FrgaCz-K?Q+{VlK=G-*h!@%<z(8KncB^%;#_$g&%}HAI@{U1y=+RNNXX)tA~ghQM>CasmV
+1_avg;231VBo0c4q6UZdX5Si3A!ze|MvS2jbw-fk?_UE3T)ash0;&@X28C)mL8)D*00*Rjnc=mCDLu@
+L$1aFt}P}O03JY$)(s-<!zQqC1P>O9YneKQ6{yhOR<iN_*#m%NX150DpR#kvaVrRXrEN2Qg%2oO3Lyk
+lWHR3rd~}D@JIMLj(A$xdQ(=lDAizDm1~%Mp2Hw8oD%xgt9Y{k$oxKyYguP&X<nM&1AZOh80Ot<5vwe
+j;iH+VE8QX~*PFODZFq8dwwQ=FRVpi)KF^YRBAP1yvCInkvWY7tZEsqP={_0=`0&FY<7$SDW+_3CAL}
+Yk>Y45_tIHX*n1~CFm{WTGM|`xF)wMJY$9g27gUszfy4OU!tt8AxruM<}v+qydy}X)ToIO9kI6J+Xy*
+z()ex-q|0H%eJ%4OW-^^6`)M4Bl;M6#max_wh;?U*p*8@Zj`#`!GIa3~8v#(*jS0WFHS&Tb{**U~7G9
+}~IpXvlcFD$7@KvAvLKRsoR#z<B}Cv-C1q$#pytr>my8k?C2%JbYGFC7|cDENWTQ&!zL!U}+O4H^7Q2
+0y+T|zlml&9CRTQ=$~)jDVZX$I4h)K2bg%1)J@gW^fXrTyil@G87>X^!E#p`Z)#Y%Y5L;o>g@?X8}wn
+2myM!O)^gTV*+g8+dM1n8tSXC{76pD-$hu-?od~rmn>?K@wwqWf+l+u=Q{rf1HCXjvJg`fN>qY?r(sn
+WQV=#CimSqKOmT!@Wu>4f4fY~Q7H~Dc>D*YQ<i3OnaF0N9+l9Da5Jgc|;Txodj^6KR3d@!)TPft!?oX
+sxJ|MN^7i^oStM}xrt2G*Reb;U^Q2=g1(&6=itfTA=@a=^l|Bc9_ap#s$h{(mk3|7%zjD4z`=Yg1*Q6
+^urrHO1l@NLh%=NKGBz;Jd5<Z5Ag<*%ZL<D@pJGBYBo&R;LNjef0`-hV6}t4_ey>S)qT@^doV;6fuFa
+4N#BbV&HXLZ70}#EAFy97YoT)0f;3K7swzQg?;Aj;^%g~^d(z{n8E>GmxW}+Fw+53+Xb6;wtw(^Xa8U
+v?*Bl1N1Ta(in8{WCPp&4j>N63vgLMchQn4P3!5Io8i8SpM2<RC*BXC}c;3~i!#k!Ebr|0d5sY|?gky
+)_MuGMVXE`4CK!}<R8l+C&U~uTG@pvKE<*gLZSTT!%YDcC)Ba8His2y_f=w>2r$H-{`uqdnLT8*3)1H
+Ny&1jP@?ZZPBbHy`$(Vx&j)ClIVzk8g)XTI0}qksAMgCr=%aJ+e5`*2$pT8moIKg0~?1q~i##5{nX?0
+qcmM&}yiHphi4F@_KOIU?r477GB6CZj=;vt5#+3M8xs2_+<#jVgu$y4yWS%hffo$IA~b0sALQ+FWsR8
+*dMTY`8M(<><F^9)J!!2`1W2zpczK(xMQ5h`wu&roceTRy)%UO;+{iz)9O;VquW16PT-OOeK80#-odN
+vV^)mV=hSpYmVpVDIXE1ZaUwu4^CcQGwZXqX!B{m=V14|3oGY+!=pB|#eT<8qc+%j1SYsVuRSk4XWTk
+r;e^vi_^ho;vGqjP{$`LytDmofqXHeN+XhcTv4bg+ot|j;#aSfO+m$re5tX?O2f#@CCUn_L2kq)3MZl
+v4@^eJz!JH9EaZQJo~m4VxiXoxap(c{biMmvu)O&tIY-@vKeY)9jPuWFYtm1p8;+CkX^@s6~dnF0xxK
+RHXBsM8xeMWCu>k&bY(<4%i0-p%y7NqOS;ZYx@-nZdE`<S(MGw%z$t&xCzSTn%`Hs)2|?mB|fgUR4S(
+{YhM)Wd@Oz;12p$h}~N;y1%yyUOzY(z=-U-?wC^*-$lT==_tj%7!VL7X5?P-91CRrXdQoS<)H_7oPjb
+6o$t}`4Avo2a6BF=hhjtVum@@|JX$Wo4Ea4U(SluWavs^M!WWye%zYMM<mcdVh71R;EU_b!mE()|!@N
+vl1UvlTHC=<7RHmAF`A0at7r>Mx4m^LH+Fb?>8z3+S8&ZJYkW+E-{i%5T<md@#A8z&SIK(qd3|;H0Rt
+RA<JUkrsP>_N|5G-DXi5iK;F*fNkFgF}NRMUqFaXxY&L_9}|K0BDdLQj*|z;OaHYF^%f<Cp+NqR7WMg
+LUWBnGR2d8=@J2-ywA#i0|VB0|69#kP3?WL`xuHW5O6TU5?X%d2I>5fB5>F$58-{y~N=m1Q@Hsx;#X+
+-c*Sk3iR**7OIiqs;)QF!^5;(L&z4@dD?A14gjA*(2{2{h&ECS$N%P^pZtCz?j)uvFb)J;jN(NMintE
+xjsA<q1;k{q!Y;8bkugVLX*_WwJ;Jg-IS8EOE?8mD!Ct)`ai=i+?ot-;5d*83wE{yGxkSgkZY3Ul&8#
+Mo4^d&P31Ds=7l1pf5J>5x_~A@`cicO}?pkRO17eoo)ZS|#A4ETpkPS6U)Sy{Zl*xJ-mDTm(s$S=Z)i
+Oa=d!Sg94}Kqg8+{utiK*p;%IN1%lpKLVn3WWOg>i@ZXvG8T9lkg_dETc7tZo|qaCS8eXaBqeI~`M~3
+4UUEr!bk2mY7;mn+$_ih;C8{zEvp3Ab?p#kt_OI-yD6dSk1yHs-JGXYg|I4n-cNDwS4JPKw*|eI>F&$
+@%GK-6?&HJKBg4RvH->~*T;I+z;zsO<E+EwksH3qevT^$ffjNV-)11;2o&S-m~s{ne4H!QNb&d&N8f(
+Isxip&Rt;~4IOi{1b%#N1<9;iI%&LG}uK^Ry0#lENHxg`Ry{cqZ|9og-lfw*Gr4An-J?SuPXH}@501+
+cz8U$aPvVy3^isnQwx+}Cj><R-6wa0SsQWn?s3N>(dp<2Cn0pJJ2%3VF)hu}X9IxsqY7iEf09lG3pYY
+ntdSLj!t_y2IV&g3xZ8u%C5!TVRaC4a5Y*eey%`i%3vz-3!uvcw9_P07h26IluE8`-3B07vJNz@atqS
+tPzQ0jV5<)m=Aeg~39Cc!2Ak!B>Ng0G3TyHQG}v!CS!Kk8%xQ5<$vjF|5UwQVtF1<1`9T(U-*@b+A9P
+S$+z^j6XZxFFvN1$(xXUuzB#kUG)P4VfT!BJl92&0Ul_pfVMCo!rKDgF-1z=OyEFq04R=)`J(P7E87{
+k-Qn<{czto`o{k(31=D>4foYX#f2FLl>#T@#%Nl`m{Zo%*41zXlW0s;Y2<wSOe(J`_I;Q4--Pb0_*8j
+T=bac)Cy00nT#y{OxCjR!a;O>XK%{Efeaet>-7`b~50<7=gdY3&X9Xz{S?<;ZfPsif1_h_5RJf-#nC$
+r%&2PZZ9hd?dj=N;_#f5(i~LShEbAOy4n2>LqSiaYd*LHnSyxFPn(tQ0%&g3|jYk%b?VB0zh6TV|<%c
+#m@C2|&h<faZv;wzh&`+hv|-*R<CH{n6^SXl92IBL5tPDH#2Zb{{sSQrRMx?U?OY5aB4t(Qz031x7(f
+I(z+0G>IoLJ&?7Zb#MbZxz_+bz)U}27h)zC-{j(;0$-I#vpXB8G3GG{1uNl0`k(&}P@)I)CEUpWtQ*x
+C&WM~vjRVowj`xm`u;VHRqeZx!B8Is0cgG<x`>w~w@C>exS;rZ8AR^iV6YK1HRg+~B>k_zv^fYFm8-=
+zMt)9VciyX}VP{ZS3GmVzUiB=Hax@r{dzfmy84u-R03&lxe@6VuFJ49od{thlVIog$SV?K5Xx-laCa_
+)*<B@g6noHt-V6=z+@o`HA1%&wb?xoJTU6xb0A6g@%LyOC@3Rc_-di$MZaq&XY+BSOx8ckvQ{-C`0R;
+95AZ-^|`#y!nqmGs6?7@0d*_-e8z@mtlVPjyF8k-~!{uK#OSXMjInY`x+OF1CKx!6P<Vhm4<4A#@cYc
+Ax2bU<e(f1Xzw=~^x_ZnSP6|F#z-^*-{RN@9;a#sW<}+2o!&C|TPh~gMCW`0snw|>PY_y<MSs&7USx6
+e&f##Dg<n2-DliZi)ZW;>cW0U=;>$^}#jV9Mc-oukK(Zf){b_T8SK8H6)TgU<987?xQ|sb|kx8uuBfR
+J5AI82R=wXH3=D<^QeLWp@*9lF)MXykSPAIy#E2~r^&D-rCIjd@?Y_YyCaOoX@cQyP#3v-^jE&Ip1xe
+sptLIHG8elzNh-%*n;&BA@5QQ!Vhy8(D&3v28)JVSqL3Ypk(GX+bsNN}IWX08t~S4|tqxMW{lF`(~oI
+pUp)ujO=VX8!EiGgAy<f_dkS(wRM~yE5w-yA62PO((VR-y$n0qXo}xZu9G-BeP5%cQ%Xbx>B<l?hRQS
+4mC-J6EW0YDt<)*;D3!*{7SoU^enF8HGN~xeAxQULv+WvpIpj1AsD6r2ETJGfZwc|aJzfk2l~Z_)(*~
+QS&`MV85zLw)bRtR;)FE(LDm-GxCI8d`K1*rn4^WXQMQbel?!G{{4<?v#t={SmuGYCqFvrh^>s4aw@>
+w#XY*FalWuI4dr%hTrp^K2bABsMeD|(yHaTs=wpDdNQ&<6fyWb*$`DpLQ7iAp3#oU@pBv4`2Wm4uQ6@
+hjYu-aN{%Feq$ejyeePQ`FpmeC@vrr$jN`pNLqSd*YZfAWh+5X`={6RsTs2xqsLJ6hp(4o+Qf-20T^o
+q4~$Eu@0SKQM=C%Q<tbAdEfif5{?fGsfXf^uu5k7;WLEOor+R+!q%bn0Rp2krQC>HOx1oPAN}HkVIlZ
+Q@BbN)h>cANetWclG)kR*a)Do&;k^Sl6^pzFU6lw3m6vdhro&gAmC1U2LLJ!h9GzJ#}<8j`YQgIt(&z
+dn)O0f-Y&A)P$H+4b7@QoNjsRTBgA>O&ah0&k}ll*oC{l{NNH=_Ax(pcdI}6?yra<4!vKeQ;L?&xq`5
+lo^}qN7wBA(@GEamp5G9hvpe})7023l<s)}Q5h$LylfWmT{bJQtEA0@Un?YpfZ4CUjMu2k#i^|ne?Jh
+g_*B<(tFp3Ns>(bO(HEeV(u<*k+Z+?L!%w&Q$miw#jJl12>KMlbKUuiAF>%O_Xdt<H5erj6wawWg`)J
+a+4UN8^AMus)E@G^X>x<~fz0qw((6y^WoTzA1o;Si>NxO48qy!$<}tnacq)@JI^WB3jEe7Jhg``g>}y
+nBJ1tQBQs5*fq(}MOLj%HbdCbpM2@H{%8&OL_7+rVVG#{Mn85k(X!?xTf0NQ{8$&uGN?9JIdU9SKZ~c
+q12FBbx~;3YP!w4r2T%fvYjLugRre0KbFqq*Eorylbe*P2mF;sdOr4@G6$LbC3u}P|j>g=HPN$^n4O%
+)v=|E>L!Kw3Zn<w>527*3pSJNM=w&G<%PxAy^6wl&_@5&OiP8Dxj4b{Tb`{z!frp=f3$bAqr?w9dyKL
+3GuMHP3nbW_@=n*~IhwXnTvdq4f{zv}3>(Rku1qp?{ORk5|Rx|Fr6_5t}63WZ5ejVn`bjG90?YEL_Kh
+93Cy9s#=D)u3qIIEFr+`x9ke-muNXK*K;f9PPL<`L7U<m{~dYcS)mWvq1&ur{-a_V`pUl<uc2qvajiv
+-vFIA`yPMzuYXi!VPE49|Fw45k9#W@BU{npy8B)wYR^f5V0G-Y?HGVxPF(jc=#F#=I_m=hhOj{g&5p-
+D(+8_Y_xQM{^ft1!n#QSiptm&F*1+4jaJ4y8n`)QZ?ivw(5hhX3e`HAFKQ+tppE7s&&j@4wqy7TWaMy
+_HKE3Q?cR<FR|4eU;`49DOSFi|MQ)(X(e^RaV=S^SN$k?<o)$?$9bCHs9v(QZOX$wV%2~W*8FjmIl3J
+nlba%vK#oQLJD>5fSXoi+^u%YZ1p{A)Th(@e#vBu_og$6g#)iN%(3-fb2@Z=O>q<=a6H_RK888FYq5*
+1XTah;cJ2RV@Too<M$XFk}O7FnsWvK{u(&U8`A=e=1(IITadK`x9R{n3mMeV2HFfmS}SxzhWp7|2?ak
+&xhCqQ;x$awLZwR8+pKktNGkFH84Sn#sRo45k!V*HhCGRow3QLx<g}jr_Jdkpj~3UkqHxkZ{ApxNj!X
+>UBkK#Pr-tNKS?w|D4nsi{_=)<QQnEX#EqO%+{GaMhOw2bK=~NVc5EdG$`XT(>+F_S$Mu?G39E#FnNC
+WL<~nRLBcp>J`>^yI@f4G~hjDs~u3j2p8Quf^PCc8?#*7kYN%eLk+ZEBUI4U*kAlX;Z9s(};j0|dR-1
+h0D8hD@%ghg5A9WBGUeitY0Pn!8yF$uH|4cSU~L)@+SuoqTeuGcWdxR!W4ue>Xe<-D4qz{XR8Co)kki
+R^q%37vUYOxPBALuV))h`RZl4zJkEjXjY@owBeP=tP{J%)Wc~`uWQ<frYqKB~ROD3wvO=D=P>%9R8Bj
+@r0ouv#MMqd4?*S=7G{GM$tyD$Nm(~m*NU}Wg<p4L46~)!!dx$DnGVyLI7Q**D09gH(p3_3Y4n3+U6o
+Gvf%<7;|BM*ytWc>r?boZcfEcnyfR*9*9(T1JN#eFeBwU})VlsJyzY9wHkqiu^z_w10{NSJqqIKv5|=
+{GWRL%dPK&8P3iAQ*-;F)OeatWzwTI5lK?rhcozTm;%|V=p!*a#8J%Xz%o3yy_2_R?kUUXNaZWhwbC>
+5!$F2X#mH{?(&JupSRMN-2+S3$erN!$KY--vAu9k1AR*n^TUTs|IUa(Z{S;{4(y+J7bYp3wH_QQz^WO
+L9M{y0@)e+~tApb~}HS&-j-|f6;TBiZ@#22>96`JLp4}^SPege2xc!rP`r4G-932;|`T3WP&=ukxt4n
+Q&S;vh`1KDtQgh((fj7@)%ly(m;XSqgJO-7J!nSNdVd^aN&Mdrg7pcry%?rL0=v<>DV3)o+StC0azBT
+3Iz{k*h4eiU--P2m@{esC5e6~cFzvSw{5}uyw`84OHlR}zE{!Sc#xy2BqnA0FUA*xbhIse)#Sedb>b>
+hDULVEqW|+T@W+zvtFaANb-;`>IQbV`T5H6Lu)F&zKW$2!ty*#@*`+s5TdU40=+he%MBlh+o_o2+*Bo
+pk3MDAu9?*OGXJU1u)I5fhb8E*;7e%ARR)l|BiHO}wit)kjz2N<6gs_58dTONXav6iGvn?&l=16RIw1
+Ct&nz*cWLWg+8z9ZZ3kTLrSWgAUJa(Z}A#ZE>1g4&=pM=eUv99uYvKHgtZH3&!Il$?&WIcsE;?<=8~(
+9p-x=erWK9qxd?;eM$zVHZsA|6|s!797#f_scoO`&ZcFTfER*YO+J4ACoJ-zs?5>w&t*egP!?vY0R$B
+douwx$ncT<}%Rf2Nb$8&*wGZvN;LM7ETN4oMv^pT8I8FV{Ker`TWOCY~2Pjo9?(C`y>h+x5A-l!8bZL
+r@Ht(pj1{ChHHk-C&9oo=s21~lxB)BDnwnxm3XLP62lEu*i47_3(Q?u!&+<ZPLv2|6t6P0yp8Eada+=
+#c~=C0`n$(q8w$4Zx|pu9IuD$qREgrQ1dU1JjLsruOqyvcx45Mo55e9YFAc^n&iXO4}vON*&u%Urb=H
+FZ*yccZ;Ykya^V4vFW*$%n3zb>Uo<AizTA@dhgjX`24D!dbTiMW$EJRK)==!yU?Qg{|Wv+cdeZ^Y@sd
+qKlB6d-9wcawa;(C8`~3d8@BR3@Z}wP_MbV#3_8`PP)QPn+ncFnrlw5dH{>d_#hV*m6&8)U6vjoEd(3
+7FsdC)1fP2ybmL=fdk(qX6N&*4^hQePO9fbLQ9SKcIjsVQN39kLF9E9htam_d?BY~d;lXuXSJ(=uy-?
+;oVb4V)jQ_!Qaufxfo_Oz_WV&26%Ox2pz7n&dQ}-E>wvu4R^f;b%=_L{{1n^qLT49Md0JO)l##Sb@f5
+9y7b~oc>1Y~psSPCzQFe(T9HCw|DB9Z!=4k!o)7%IfzWSMglPv>GHZ8-H7NYGDjW^}WUt%J5Fsq1*Td
+lR|A_BvXkur^cQ4uIuEnZ7$jm1}#&p9&njA7a|PNvPb{7KDK(N2Ep$c3^vC_Um>+YIjSA%n=r^AvC$i
+(HfmDcRTtwxjA6E*T|Vq&lws55NfWMAn96Q^g#?4z5a1DMD{{DMV|pI!hZrmMdy+SU4EP?-ur{$+9%!
+Pdib#g%cD|7dP;1cFV6n$-Pz^U<;-qt4H0J@2<<0?a0*j&d-oKlVZ|=;hIR~kHaGkNpO%q5N7!_;j)v
+dMaDA^gZ!zHaF%r`2(Cad7zXzSrc0Kw~Y=1Ff!qm#2b?v9Ej2h2qDs~+x=jdTd)A;0VLs&1z3V(P98w
+aqV8-<(NK{2q6SUIFac5|hiWCc&ZWFIG{%h<ZtwxHMwPROcwxe(pUG#a}XNHN6TOTY++vAIyT<)qgoS
+rSmo;D-RuOb-rQ%u{&bU8Fnu>UG&y`^?8B`3e|Z-0-0qekC3{w@e|9xo9^i0Y<fn&#4x_1&DHtm9kB;
+a&JHlv3T3X8lRL1_04U!WSFa|?J-%87y;q0@mv?qs%P<hbd>=(IgAs#5<Ey+RyE%;lS-9H*5f6Xg=3A
+u4Fu>p-C^S2*ypY5J&*9A^?KW=S8mBQq8mTK!C+;1(?Jcg4vaIp8H=a%MKg@!VNgejcUE{%eO&tUf_5
+K_`(%lLmK<JqHQ*p(c-xHZmVwh_dow7WK%<+B@r$MpKcj*+zBCjc*ag~CYhxee`=K9I(&<4Oq$lL;?o
+uEKcLB;@z=T_P<?KPRJT>4rFPO<h;K>3P?Yu<dURoG+?X{h%2Q}1h9PrMsDc*Od)?NUFH6=x`&Rvu2y
+OE23exQm8eO0S6rx)#s%l2N&u#}x<L!RBo`hf3>CpXB7jV4`tBJzI$P)h>@6aWAK2mtey7Dp<Y$=>Az
+004jt001Na003}la4%nWWo~3|axZdaadl;LbaO9rbYXOLb6;a`WMy+MaCvoC&2A$%48Hp*1nj|fkz^D
+9WRn1UYI`WqQ`=(zL7t&)Av4mfq)FlyeTp7>>C^Q|N|72#PSRX5qCSxne?HNl#N~IFqOq+uhX*l+{pD
+Sh%w~IMdvWE18=EjX@Iv>4bwT`W?bA5?pqnt8Nj!%;U&SA@nZUnK;wQ7Wu2(@@qq-{+-dtZlh{e3ICT
+KIl{9<{L<Tp|7Uvy}WXk8A@2HV)qSCCbN;6?-9Bc|F}lhPwJPlMGal${lw8J(8FTG`onO3WR68^H&8C
+QnCJ?4f;%K>MUl@xSUi$rAo5(u1{a{C<42pX2|3|M{zEqB}JXRDd@7wBmcHrh(yHPPZ`DNHvC|bvj_^
+o<V91V^sa|P6}`_x?wFc#u%zttgg3l2eSvEYzh{X<F^!K<$_jS4NIZ|{1Sov*aa;;`ZM~7qV>?hTM@1
+GJA^Lk?kFf72CF?u2eG{B;H=0m-OJG@aVoN3pw$kV7y~Y`H779JX^vK7#bi}$PD^i}p_Kz*{0v^Pql}
+U;rSo#04^YDPb~J6Jg|g)Vqzy-KR4w$80Vxqd_SjH+4fSD41zsiA`Rl3LxGin)ZjT}!Ahw=zF-Uy#Xu
+3%A6|$&!L@~}+t`yKd|HXP3`&e6z?J<4<QjwrA`hp?<?T95KRw7uj1L1&73T?#t>Uv7c_nk%);z#GK<
+GgG*FFV!B)PGSiE+_vsoS4z1BLBUL*d{f<I`!=Nw-H@$xiCpqw5&N>Zl~K8Or;_xB&AZ!FH4p;oRc2V
+b}J(a+uOddje42!x05<<Wwq;|GWI2r)$56x7CqDrb?UkE8F0QT@M7<d+n1>;=pO9Ixlf-Nc%LYC-|43
+G|K^6`f3T)<^M*p+fjNX@<yhWMlnbb>)#UwN>2C79h_msVb?s%mMmRCYDtD+2Z)#j7;x`~}6XVB$`9S
+tqvPy>2Kvnr}hkCd`vYy+@7-F3=2cR5#jIrNq&YgG*A<>7}teMgbuLHJ`I*U(6+$G{dpFj;Zwd?eyrK
++~3L+RNv>9LC@Q!M$~mAssx$al7Rs?Dz`&g_(IVE}_Y(U6*ImC-5S*0q3zfc)Qe%&lxU$qSwWI}Qh@S
+~x50BrC|fca?PB2OreZiV8W_1rpY2Vhws!CSk^|sgRHOdVrHiwq5J%iW2j8vgzbzdN7gW25UT|x3)su
+bNK|MS7hcRS4t?%y4f<daq#6X!c1#lyu?=%^OjyxN8=1VrEi2;&%}Vwm@vvZ@|f)4fM9$XJ-QbaoC~J
+I_uThHyt3)6hl**-gZV4CXK*z$Z<(2KZ~}*un)HN_5E0Sb+x2Jr_{g))-;?nzb9ZbL9bb*e$W<9`nZ6
+_UA8;6YQKxDvJ*4g#pJ`d$olCAP^8rd&(o={qZ&M8)Ll`7_?PH};_fAX^)z-U3FBS8%a$1>4_ufha2a
+8irsK$paGrLCW+3$5j!S(?k+KRZgOu;*4+85m-vv#`I$cC>CK+7KFy=<Q8Il2n3AvPw5@6Xw68m!B-y
+)deWuX7%<c<`PtaZI;PEaK`yAXjwYm+@8Zv=0jgU1^*O{9?I$Ae#|SzzE|L4|x>8sZfRBNS{v%FXE2_
+SZ0jg3v`(G@aF-Jh;ei|E^vODFN>=GQ+I_q0k>FAYX1XJO9KQH000080P~d=M{JV`qiqBL0HF&203rY
+Y0B~t=FJE?LZe(wAFLGsZb!BsOb1!pra&=>Lb#i5ME^v93R@-jlHV}Q+R}cvnk^@^s--@6q5M;OL0=t
+W%NYRIEVA0ap=7ts(l8S3A+Hdd7P#4OMx2+%ISUht(b7rVZvA9}@(p0M6T#MGP7eCOGB)Pb_NGv?J;J
+nM7w{7WL3oiNl^phlSrI%s^3BQ$IG-eN0{MP#jE)%tDjP)2%Ybd=kwM&wu)Y3U|Cmp=69n?;F^#X5I>
+9Yuze{8*6Y4|9c2D6=ACjvj@l)i68iRKG6+KZy7&_z)!w0Z(zR$tH&ra|cUyGFwfYVU;9TI|4YO@;s0
+l`suh$pM8~Gq@<uXHgVFI<W_Ig5MM`b!^ZaiXs=c+8OrISo5MPpfgRpER~0yLrBQy7VV$lfEid2@MnX
+;pc3AQ6%el&$mQaG3nR(GV*a}oSYjL42@!xf({F)<dh*!&06*vIiUI@M3~BPRE@4&_^R7k;pV9At*~L
+HXcsM#4K<r?zu!PdoUaC5@Vr&Fi=i(Qv`vvS=pdjo4OJZN0iDM@ol|l!~g<_&^US9{MxKLGzn^)yBi!
+l%BZCOI&(?>+7*e|V0?(BD8oT<|(RNmda`}LlnuVIC_6!*4;!A2{G;#Ad)@KK;1$H1SB#b!#<8WFh#S
+z*5(QmPrXw}3hfUiyeKVgfB-Z|k9>FH*PMQi<m2U1#>LH$y9&wgy!RA~>JpgN?M61{WAb+pXENBmzRz
+atC6MVl`H6RE_dKpsI|8s-KOZAWSld%~(o4Z<|VbAPG=a&#lzT9}u-`q*cgK2saK7DS3l^se`b2C2S?
+Cfo;hn4+06u&4-FtfEa}lPy%gT!CKWrV*RM&Q?XpCTKVNNbD-CAfvL%xPo_qmudbf<(r#S0ALv7ve91
++k5~`_C)S9g+AUPPVI24`Sh_8Q5eFZKuI*kG*QFb_RG#^8P$iY0cGs^2y{EnUDaV089@)Zy58VS(GJh
+cr{vub^45F;f{ztoWn!b@#%L*Ra42#3jEtNsDOJ?%6US(H|s{pK|LDD6(9=lni={}}tKK!3v*o}P6Sd
+!dEXxj4-vW9E9|`X6(#!#Rd343U~7obyB6UX!vl)MsPhSr$2n?4o^sY>SHhKzC2HE<-=}%5VAR85$p!
+WH0f!xQQS$HsI>;4_1L}&n5_>-~=^=9C6WQHb5qv!1K^KgW|GLbvQZ7^*2ezr*kp6DknE}|6$;n{T)I
+KaM12(AUje%89!X%P({;l$NOk<bLhh{rMv-{fw*@w-sRPh#?fIQg(5TuS{C$jq-+<QxS~7%jCfuv^<U
+L!p*b&kT+P{7MxW!Xf0g>c%Y)+(YJ69sN7x%_58Z<4-;4O=Yz#ixu5@Mac0Jyye3#tt`FPOc9gvL)%o
+#e%2cQ4F$n?i9$Is~-_c{G36>mf~1pXfz>r(syG;VkkuJLQ(x7M_qEnbNhZwzO`3UuU4w%XH66P25su
++BYO5~a&!jxoamWopK3=y|mDJEzA4e%obdG)w*gP)h>@6aWAK2mtey7DwZ>xD{O+0034~0012T003}l
+a4%nWWo~3|axZdaadl;LbaO9tbZKmJE^v9(JpFguHj=;VufWQiC6!9FEvLCuiPCi)r*$`tvx(jAo$F;
+N5|U_Bq?RCUt9|X?e)9p4phP+Dws-HXUsFjSFc=Kx3o`@mh{5B52(u_oXCqM-)4?-r>2$i?ZYP)jDy1
+rvR~B)iI=?plI-Rq5ti&RV%0!A~p54TeR06xk#Y#-eG%VsQRic;&g$QyfN+lz48yE9974J3ZPW3cMRE
+AxqWg@0oF63Py^E5~Rxm1;lB?9vpuTH#JW_cmPSzLY1A{nasSCUAc-ZaIT`Ic2}%OogZs)hNTOY=>ws
+!l2kuVqpFD)KU{e%%In3WQUg&NR;!!V|jP_itpbV0zzQdlJeV7R_y8wphaA4KhKlq(3i;rJuwKXrhGA
+ZTQol%OH|@>qz%sWmd?(DD#8{^u=UVNYzJV`{Fc*aWZ-phXr&=b>CR|%hj;o5p92yGERy(^=bS}7O=T
+@F#^OdD}S<D21?O<Wy&*dffFd$;~Xf|s}Rqw0g}PoaKRBZe;dGb+~X?9eH`IOfVLjXq%ZS&_z}QANtH
+}hT*No>Roz$Kg>uR4r~96yH$f6d=kMQiI{v&=b6Ed039bR!Sh$;r^g5ll$7k=4{MRRMjz3@@$4h0Ahf
+ZHOK7WHguZ~|Ip1(Qsj}HB>&fmUzbL_u6Jo^T^(?)x@bD+Ho{W_7H3S5j|WWK`L(onilZ$y*>rhYGBX
+@G8!ia6DrZ~D<dSWJ+wM7fNDLV6?yI5eJ$d7y%#$lVAqiwn6>js}Z=5#SPe)QoswQ%+NfNMohSrOf?A
+rf#OkSuitPEk5*p8>$AugEkPsLGY+$K;}VvYyW8sPcnN*k*`J#zTz&hp3LK~upsk{HyotgI|psC#4VT
+6DJ(2N!ZqO__tpawC7X{8s>++HA}Cy*s|Wn&tN16`iaiJkuvaN<{C9-(W}f&8W%1+`IQs*M!pW&;zO<
+6lGqBvA;TvdSj`M@En7bf%n|xEuWhG9cD3_p<u`{XCK+HfqQWRK0@<G6A9%@j!?qEcRe)Pp#{CJ1rew
+^o7-l;|~YM_wjus3hBRMwAh_9-y5uw<!8?TuZ=za$HBo0W$^L71088i8rZUFX-`pZ9)oJk;8_HN9SAt
+mcBh$rYiv33#Uks0cykra_ztkk(ZE`J2=4jzK?yt_0f0DcCIN*a8U^<82v7G*)@ygOmc*_h@%_QqEwS
+RWS#XmF2VDSgBGD_x7JY5r1icgrgJ;n}KaCC#c<buVo5ko#!$xe%dv5nBl11{bx^}Zh&DP>NMJcOY8N
+56;Uowwzo-^#tuT4jSiDs2G<>nwGSuA88XO|70yzio&%z#4%pXJdR0$&W&sDPB7d~WTMayq0SRNvh}r
+FlQFfMs`LbM0z|2-0Ya>3-z=Vzgmd6h~wFI}sbw*C_V(+rEUCFSZ!BF~iYvg+#%9BisI1Gv`S1wM`TP
+HUUZ(}?bRPuj-#50hIUD8CiS-#0#)WJtWxO+8Nz?}4z*zaxGe&On#8o*`W3>{u>C`)%uqWj|Hl)kKS$
+c{KMJA4aXNkFa&C@>+hvmgZnnsx!)$rcy66c7r~sU*dODzv`_^Hw@ZBCbl<Cv^>5%5+B>Zz}5!4>?>e
+k5gC|$XO6x!>3ZrX=Xtuq_Fg24naA`ld_QOYhsYBSwWnXPZ~a0NH?2Y)tdDzM^B~5xKp(b>?krKh*qm
+G3Y<++r#O^4j8Q$Xd6-;*fxt%a9FY5a#(=og6O;>3EKb4Jpi6*lecN-Q4%mvj|EIBVUN{>Hi5?I3`T5
+H>yQ{ST!ljks|BQJdN9$Uyd@wuYY`SEBEJ}czgpLU>ln?@&Rv>K@C_Pw8T#y!>;I=QpLjy5Ey$Q@hFl
+ztB6X$7y<kTb5tY{gf%5dH{xCbf0X!x;cS^LZuqfsr*FJHcFS<Qv(z;E>1pvmc3f?&bCr&l{vhxf;+`
++bj8W5Wa73@ce=ATn^kNyWY(k|M>>We%@D0sLpeWyHAr@>>u#TnL-OQ*P*7P1f2_<NnnQNTd3~CFfrs
+Bqmvw^wBTv*{l`Bpo;26yAEt9?kBjca2nIxVx9l+*>FI<iE^60sJ$*`KFyopE&so9=84t2<~~8EF%)=
+0{9fK$$96kUtv|UJ%r<WLO+fDF*Wa*p-hU#Wwa#sY3B2!u<r39xJufe7%%80%D2pS3U3bK5bVL?$p~`
+uDo$cPr{Q4(Z&V*Y<J}f)g^N7JWj`-^=PWG9mQpmQER3Vr(fHzr*_pgt{aPR3e@I7f6V(2k~Cn*93SE
+WqwP)(D-au<tWpd|bXPzV?%9Z|`e$wV$dJQOfH9}DI@uUG`&<fUO!0<|fA5Dl1_!0Q!<%_f+oOxQYwS
+y}`!iV)3AebX!*aDQR<2ZMg2VJkc?z|;g{5l2xXVdUTW5pHiT7ugNUa9Lz<2mp610Vgz=!lBuCCWZ<H
+8dBW>{H$vQ>j8x^!r;{CBa>c9L-~*FjSwHvb~6gFc;F3ib;Y7giWpB;MZ7>{K-+|1P=$#=VB8DDfYeM
+5C~7p$=F)IXB`SeHR*Zr?5~DByhh+2-N&QhP6cpf1&Txth&INtZ)f6OXT`kcU{1ml;{n*jY?pY>6aLE
+c#mx95u-;A5wz(52u*t9-|;;L=X1dGbWvZ9p<@aaX4kgFCv=ZKa^yGu;3btsQy^)X_CjGA*dGz(A>=n
+VpR!HNyflDI9%ADfY}hS(U1hu#+>aEr!c{yx|r(GREVG+kVRJ2v4l4btahIE~yNfqT`nbQ|L4Az!2s_
+2}7(fgk@)oj}o|PwND*OrYocnzfceoIgZQR4yb4>5a^h>xr^pIKi2xlPlRqiytwPw27HB&9ZTqIC>hZ
+c^7@xeB1>LZe@}f<|Ko(T4KZtxu2qX#Fcp|s}72SHf|NI(tPbY01??*!*qIJHE>#Nt0qSC$3kD9MnT(
+XvTzgx=!6#jHim--I0PTjG;K;;V|8dkag(yL@O6ZTy8qnrWJI>^mxs$6&h&pHZwUE*d84`8<?ZKR{!M
+vv#ts=@7=Q!UkNF7x0v}wOIFbDvtwo%cbt9jIj1|Ul<`6%Iowts_#sx8=&Of%wx`Eg#>^!zr+Ak2HOX
+Cpyf#SX$5sK5BL+AK)0B3(q@f>#TJk<uX;xKkq1je&50TCpq9W*m-&hfG5Y5RmCnMp;Pr}+@i?}<hY8
+4C{5;3|M+oaEW9!mtY^ZNj2N>q>A+3JM_G=s~Tlim+{T`AkIEZSgG8&hozOHTHLNdv^eOtXT4kmR0m=
+8PoWV0~63sWo++_<S`V~mzdj<T3Kk(0KUe^9&aN@>7j?bTPASO%NwWi9n6(Xf(bYtD#L&Q>LiPHtjb`
+kPL6f9D_Eds5i1J>c(54<QIH4pyxpVGkD&a11f}Q|cSRRr85U{xU74NjAR0yNm{36-)S%N7&<s42oc2
+7N!^VXLBAjH|H91cflt7%nJrWpK=;OGQ-6t&*2#B%JN>Y1wu;g>4<3biJIGiOxDC?BRL?+p-$Wm(-jJ
+UI-I-TNakIrP2fNL-S7)Bj>in4M7G*zJS=Ek<++*O-iacYeRal7+lS;vHWBpMjlL=O_wRG!1+37i)x;
+=A+Y^JmR)Cn~^NYB4;fwbPK}Ge5$g)0%Q%@ztboxnmy(I6b?yi;c#z+C%L$6U7v&WSSdZwWONRAnx)e
+Ss5lWQ-P>bVNEFxuSWGFbM6Mp%DV!2MVJg9LaF6hyBhe2>?>QD%FOT+lrI2ZQO4?j!QVZjg7r_mYKso
+m7)^~JihPrJv+2>d`?e9MUZti;8@28u4p0S7oHDP8f;{Lo2Gl5w1V%r!JIC`V3k0#RwI@x=UJWavG*Y
+Loy++;D;}jy0VAx=x@XAUz(b*bLc!rTc+ocGOJ~zo#^=D#nU{v?kzR4P?_}Pe${_MP1Y}AC;Gz=!|?%
+@~Lt@y<`K~t<#U<ck_|DKgpPM|(bdg3+kp;N=6*j=1*q6zjI4$+`i=&;B3F^W}Kk{L%W#WC-l;qd8GP
+q=7NEm#I)q2_eh&C;YgQ)Zy-ydL<pRrgmIrwVD(A9$ROI-okn(`t^IJ(pAk09wY)oflxK0`Kvcy2tpU
+`=fLD*!4d9vWH(^UV_G$Cc#XNVb}@p`Z5n@i{L(BOeqcQc?JA_uy>231?E3r{P-n20jOp>T5xOzSKNB
+J-ZX}za*|Z+Cq8Sa&uS^3>d44#^y;-RK648@D$3QL59IrA&d%P6Z`j8{p>A@%hBdNvZVTtPYfwVi$D<
+UGSVO{$vFqq(1AJyhz0e%l!|sXC+1)5+6fCefBAmsZE>2<3-|kmW<mml!P>u88jk&n_qUw5yMIG_d?K
+(epyB4Sdh(U1f#oRgha5{MQ?DH=M`;Mt)D8S(Z)h97B2wSa|8K_UcOk-@ogL;n1yS~Wlt^7b^1dl<*Z
++bO~EOM{fn2kuMeNr-mn+ARrOOSi)v$hC^&vWOb!?BJDcQj7n{D|?mAkmNA8i$@sxi>X-v|pgT0WzlZ
+GQDm`CBMOH5QgUQr%T-%hpGIg4f*`0vkk<=K}8U!K{CbzeU;#{0aZYFqYyV=C2=T~%i6y$=qJ{i%!+x
+vJtwpZ@T1^F!(AxmVNSeN+HIL3v+l8ln8#R?1}!hBs6waG*0JH{3+Ok(zMnNPsH5rhY}|NgDGbe9tS=
+15xp{kqhBi-d6f|~d5`?r_8cb7F<RkH3L$q8inALQzz_7B=F_l<GI;yen8LPqi$yO57Vd#lzfR+%^Kz
+G<U;@DVCQk?@jzrzr>66!zE4i`w^+OOaUB|X^z;;%kI@k))1@3A+zXDm*ofK11G3Z%4N1hxhMdg6XHa
+3lYJ&KuHFDzmmi6X&cU>D9hAtTXCtAap-6ebKJKG-_u+tu?$%ti`~ha0`ak%hbf%#rgXa`_Ss*IsV{_
+XHR>b^SAGhKODdR{`i&u;rI+_59Izy0s|Jzw%x<8j$R$V{`#AfZ~yk@ySJzB{{H^M+4=WB{Num>Czym
+F>$7=$b)78IZ27NT73IzC-RdWng3m;EZ-033<nyQh_3Vp*=l|L@{)7qa)EK&D9@m=TJRL|C21`78uP`
+nYBB$ska}uU}NI*-gD`_i$m1D?D_BfaW5^{s4j6)aHu`fW-<Sa<Io*+vHhaZkkPT2j{skEr+XJZCQQ7
+ukvB5#i@I};;fd63SeyWb}@O9SlG-JC<lg}R6@FZSR7Y?~PzG4EjvhoaG9?!_ud(sJSU*155zL<~!G!
+g=n(xyYUO#s1UwQZOsWxdDO`i*;eB+tuH<ZN*49kCf9DkHp-nNz%&+ksM7n+nmx61Ca;Dsc17CT{f}b
+s7hQL4Lf0t?`$7;{}|&*Io?$kn+??5iyEA`H;<ca`(APi&nkfd?yQnsz<(xIO{F$cH9NwuJS%4yeK8W
+U%kaWgh{?4~>q%8|`2=R;1VN=lUrNSP#R|0mG;p@7$+{|-N{Y_=dMjc1-9PR8`O#k-xA*w7{_g1c_{+
+auT>iS&9fSGJ5$yvSEV(0eB$f?I(bP$n@vH}&VYtj1V`S4-UQtHQJNK%@*bWb-wQ!@+eB|`jwIvO9x9
+Fiht$VydtA-}7#lv3k50dk?7#7peo15In$Z7&EzCv1=fG}(%g<zZUI@o1QvQDG{72#S;9+-FwC!~@Za
+DNt9^Pzb|uE2gYgRZcMiYYj}D_26(G7K=f%~g~nE0p~9woomP*C}MNN3RYq<yb=&r&wr*AwoA^_OY6+
+7MK>n0M(q`DzVB+Tv9lf=mk#EgE@Ieu}^d?C}63f!ip#Gl|UVHuy~~LRg^}fgy#~tcK?f^xBv8+x9{z
+<u9rolb#Hh0q^VjA7)mfa6wcmW4XFy*FOeM3i)-~gu61&q@f)YFFH(3h!13W&k^-H2RE&uup%I4ycf@
+3K;J2|kyPB<`X%VRFheD6C0yx0eqsUGm>|U=iDUtSp5H*f8Ui`)2(}-DU0#pylyt!tcy4gf(n>f;=Cf
+6KsdR7TqI(d}4cXxu_VPufIyF1VN*W?e_N*V{8H+)hvpV{x>=bwA#-(F){y$gGHQ~f9YDcpVW;)OUEi
+h<yz4aB~^c$;2nkp-?MDwFi(iNl*)MO$JJn&r(CUrUfaq(NJ+#hcvNjJmh2t#mip>N*m8+Za>!xk85Y
+oFB$fZh-53tGDEP3A#+hl&N8v)rFuq%4|xERgFGfl@T{V9tV>|S~;yIZ185wl$jh^W&a-UUNb*3@*0d
+<P%id2%Y3)FKn<$cju}n#LhPeaq3<tY*B>%=1LbYI)`IxnCgSaz*}9=kzvlt_%_l&8mC4r^oSHW)=+h
+kD!B9{J?^c?_BM%jNDAEcnOw$X&^BwcSSR}z>5(VOVgtfRby@~TIrNA8s5*AUfHF_l+xpKM!f{@^-6~
+nPZGo3-EVl&L&@?8Z}#Tcu;?rhSV0<S(XtXm0I6R~5POvx>6Y%7qM>gUx|P2+k&u47tj<;<5h8io71_
+}&Ue@}M<W!dUj^>Tc03D8jiQ8*TT~rJbfj)0+UtF*0Jw(_>v>G1sLLX;dX7u?N<R*Y@99s`*+c-UO!I
+oStFYzGIyM$Y8eKG_d4qUEx)l&ivu2im%uKJWGvio&@4`jb(<lT<*~Qw>T=F7`QGyrfu_}fymRXi%%i
+0$pqV^a3}a41<6kf{nom*Bgt_9;BT&CV6MTmJ6<)N*$9za3Vah~l+X>*VhccYy_QSu%~vv>a@rhb@><
+6=^b7;yawNP^iUDHgF2$0E9&UuUNu&`{hFxhGsg;az`*$?n%$O$Pq!BdtGHWxB34RPBM=_QmOv>48g-
+ba7ApU_g;mbSvqN>u+Kyco1_1DmQO4K4xm2tyDN~8>4c=E(49$Z4AHLY+d!+08p8jy7Z6V%NO#p_b66
+TqY2$byH!^e%Cc#BH!rRE3?(8yO@T9-NQjB3>Y|u(BeN3tfoZFglt9;|4`sW~_#z;8qc)%F&wzv0S?;
+Qh~D!9dIx=Z`hl9+neUGPA!pwZ?`t@O}Tlbv%tDHW&jKWdbS(!2?J)Kq3gL?ZDf7Bx4xw+r$Lz%KHiP
+<gBhlMsr;zRWq|_|+3mI<uM@ddslB-Xq+trWZ$~hzwfN*tQKEymc3fS{);H+ZI=FR6bYtjPI7iIBR)N
+J@FLO1!Wg`7VUn`Z#mMPw?-mM-4-+81w`p*%MgbOMFCR(nlyWtqs_7=ev@ZeV6yX|iausHoAfrWY2ND
+Q_2%OwTm6Oh(~3S1}?v{vDA50P~2>0(Ql{sX4H{KSrZ`v>p@Dxsqchk*+E8n1-iKj#Su?;pX7Tj#Z<`
+5y9~r`KtAn>sC5eV4vAOV;$<V%@A>`kK;qFWuxeLq(@+rwj{zjYb>XH6*oe>WZ#r4G^SVEk#@vYQ12g
+w}s!WF>BQ6ZzI<OK$#`=Yy9zAPg8T6ERLzw<mI%yt-$@cn$S51)i^jr)#!}cbg+e-a~LAQ2gf>@f>(p
+UH)UeE&g`dniN65|?3Z+Un?rHL<K4%O_24K=Q?3rjhu2ar2jE`bNZscbvq-xXzz55Ht=6m4H%u7~!TT
+oZwW(RCOqy2a9(ed~;Q2_r;5*cw=j~2GgbMF!DQ7RA0~M1oH9V(E@^KfOftlRJr`^6DM58btWe>vN&o
+_qDnN0dZ3$ZeJ+UxfzmxrNXfh7uc#TqP`(jQX0tKGhM{GzMNmG66r0a$3OwgS2v0MEOPqUKK=-Z#MD*
+`z*?5UJ}LJJWR0g>l_WKBk%4ix)42ReoU>$O-7Gl%vM-A;}r*Lc*8<nDOK8Z%)&!-94|?0+Z+^G&ZYO
+m1ws<Z4#tPZQ_sh%U$r8l8kRl%yMF$>^a^jFsIO5>H>doa4<iQ9yR#F#!2R@#a9Z{OIb`ieu_}A{PS*
+a&*4H2I8+8IjAMtCe3cl6!JM1JebC<c0RzA?KZf*J47usRYC33KYc_McQBVYW+jhjaA5PzbQ%_ztRr>
+0>VmK1&Y3n2e9RMOQJk^0BFb4(Dd@C$Hruda{^AHWb7iI1`#5ulCfUP2DZc2UT+9AOC0<UxPv7MLiiN
+mCt6_m(Vr{DF_`OqFEJN=5QrN;u7b}@Q#S-Hi!j2i35xReIQ`{!q`4~G7m<G!dHq2a6JO=sD`cOV@M9
+l<z4M|1TpYR+*nI-mmd<}}aR0X`e3c;>7F?2GR{z#gmsVHe97vN8w#6!q388eX<6X#eTjf^_57MeRS`
+x+vT?+8GRm)1*2sNsLwDV|5#gy)7$sT6<gZLu+BUvax30;fQeCSA324AdmPmCp>D4{plqQCemqvyuSf
+@M+{SXlZSYMVW0;<PAtef;#C|`3Q6<jP;jOgXbyb^iSdAv;p+*%bxItxQsSV-_F%LXtApXz^*(g`y{i
+hWRUHi5S7qx+t{r5z0MslfQeTC0aIE}z>Z6{dOk;xSuv`5^GpFM?UsJ9ypoj;>KQIYMA?Fq0L{~!LNJ
++!`W&$hC$WK@yyfW8Us`@3PFmyMq<mRZTtJjtBMQ}hOpOxqhdVm(|7d@#7Ao3*#Hidlw=ieL*+!V`BI
+a6#$JsYbvVIb4NT)9Z&@ETK|fhzjMl@zX`Yo#F1^d>bExUF8S5@=XD)@l@@I8+L-H<@xr0a#SXSTHvT
+gA#Oasq`d31Taz7yHa+jonOu}t+8YrBI~UmWcVmPR|j(o-BdH2O)oP2NH#kNtQn_%b(rh^CR#UYb@62
+(TsH;qn&A|~T=>c(4*b?HZ$VpxrOChuX@M8|b>*#n1(M&Oq%T6F7f$C6^S&0(Dv?3Yw)K5A4bO<)#WW
+Ts74Pyu&p_@~uVyn(%v8N**sA$blYU(&)!UrQ;ed66@brF_>eUKqfF}{3qc&9?W>unXypv{1#E@3Udb
+lnT-<L28@`&E;fzfxWAvTG|ppFPJ`6)ghup^-1m;?l&WEvSv<ryKbQq3t{btK+|GYd>fH!%<xW!VD~2
+&LVbl3zGdWLWWs41F7`wX0^B4H$2IQ-Pt2^;$fO+B}x~w4BU2QomfSCD80q#u;oW)Z2)%H^%Am+qJSh
+^U9af+<*a=gbxs)&9{d&HWX!r%P{$6H%7ggdtqxLgvCM)IKM|}R*DjHJNkP57K`%ncPXGMQKhw6uG0;
+Fr8}h;)kV2~S5bk$^*a9xP)h>@6aWAK2mtey7DtaM>g6*4002G!001cf003}la4%nWWo~3|axZdaadl
+;LbaO9oVPk7yXJvCQUtei%X>?y-E^v8EE6UGRh>uSzEh#NZjgMEz%q_?-Dp5#GD$dU-ElG`s@VU6sK<
+e~h(xpW?IhjeuTmVo@0|XQR000O8^OY7yacoNbjRF7w4+Q`KF#rGnaA|NaUv_0~WN&gWa%FLKWpi|MF
+K}UFYhh<)b1!3PVRB?;bT40DX>MtBUtcb8d6iSaZks?5z4H~L_z)veYA5QYZYp61C#a5X8M~2B)&fJ>
+cD-xuE_V6*4j7VDNu+j8Ff;G*=FQOSy&W(P@1i-(qDeT9Lm1B%lW*`&Z_nw$y5I%`Pc+jN&||I<!KmD
+xn8p!Qs)UqD(3o*!HBU;5z%4MDeN>w8dzJCLs#B%RP=hTHEb6@hl{YVU^9{_<7^>Z&M9l;&%S7;$z%!
+nrGziSVq0X5CGf1iiH?4)ltq_<hqGy&XIRfNVufKgnZ72g*{;>O6%jkj9&}X*RuR$GZpi81F5RBAyNZ
+)zOg6Byy;Ig4D)Pcq-h>!+7aUmc<C=KSN@Q8tW;W1j@FE(ok=396SR;ysX-j1l(7K*BP#4{7$9|R|2n
+o2WiD-uow58-NZPl#X~&7$?TRzHo_^DvHKx>!K~%V4#RCYxEXg5_qlT*RRd5F>UL*Ji$D@{`3mQSKEv
+nW1G|nDgjcTCX92B7=e*k=C3dKaxBKsXA2uwL_ugk7iziJR3R!o`Y1@gOlcTJ665kF0nlC*Mmr=z6YO
+%)Wqa3K`V#}KIJ)4PlZz2gRwHUZhZ&{JGdGS2OoylgCW2srp$W*;r#X=|BRmF>~`&y>~?Si7gzqZKe%
+vmt@dClQ!i3!+g;L*%Un5*lVM&vjH&IjahbcH9iSi!`u&%k@CEwU9t`T6q$4w28p~vg4alQkiKi+<*Q
+G;f2)g*2<m=5#7BcxYQ&L$puG{LULo-g>`p!g&q1H<GFT>vl(%US(X$O`3q=eTnvQA8!y>y+1s`0_^T
+%ev>858H(Th%4`_NwA<y3B2x>AUA{U!y_$^p+!LXA|2*DwM&ttkITQo-w7c&L2=q0|XQR000O8^OY7y
+ah$&~caZ=9qCx=xF#rGnaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT49QXEkPWWpOT
+Wd6d0fuVuH9VEKN3#Q}`LxPY{L_9ht-yKAZe*=1J?<FXA|Mx*AXke1TrM9Z1#RH_X3ub)_pJEsO{H!$
+Oe^wqtG+}va^K2`*Ie)OZi{lCZ0e(_IV{QB{$FMj@uU;pkGkDvYOH$VSBAODNb{^;dLkFWmp=JA^kZ~
+yJ>yVu`7e*XTOH;=d9KmPgs|9JcDx3BxZ|NQ44-o5?$^~d)g9$$a^_RYJGkFWbPpS^r)`o+7qAK!M%e
+|+=rZ@zv1-J1`O_kVnR{2P7t=YRd?<M&^`{_f5HtJ}Bl-+kZz`Qf|wAN-H*`0?BKANKFR{_y7Y$2Z?R
+{_t0O{GT87`U8D^_t$>ZpFTc*{PVwi9M@Id#ozq+XOGXn{r2%CpZ)&v<(u!{eE9b_-|RQ}`TKVtKfL|
+J5B-`y{{HX&Q~&1i_~+Lj{@dfXZ@>Mw4{!hc_{pP3`0?@a{o}WP{rIQ%@9y{SsrvTqA3nVP@K<K-k00
+K=d3^u=k01Zi1N-~OU*G@m`1<v`$A>rHy#0Q6-xKli@%r62KYjn9+yD9fH*f#=SAO-wyKnk+81=_DAO
+8IPr#bwmU;qB`t2f{GNFV?7=G~hQ{Wib-;Sb-w{kj|f>h0HW-hKb(@%sD6cl_@AKmEr{{gQ$F?jFeFm
++!lKXZP<PZ{GIbdG^1*`S5-J=5c;@e0tj5<Euxn^^aeFWc<CF-_h{zdeDD;eA}z>;Zw`c{^k_?$D4lh
+c>B(7|I~BeKXixvqJL?zdi>$d<A?9x{PBlxUv&@t*~fqV;;aAio8N!+`25%Z^7yZxfBEI-zy9i9{=Pr
+kGu?mf_1itY{quJ%g5Ny)tv<Yd_wlbi@b2KBfAQtd|4X;{{AXYM>Wi=bh0*`=i?4qDi{Jh3@yp+Q`S|
+?t+t0uJ>WiQM{#T!W`S|Vczx?fQe)o&d9*^I>dGl$Cnasa6$^Xw3|FOIL^ZTC3Z{B=-{r21M_pAR)FY
+os~z;C~K{OR?-zv*TE`pw&xsK@KxBH#V>|JNFR`~Ka(`M%xcKPL3=A8-Hoc=!I}tH)nHylvTO>HD8nw
+|n`I^?lVs|Mh3D9_#22y?*!KzU?LWT{r*b+dp>Kzx?+7`wy=kKYRcEM?U(`pLg5mc^uE59OHRB9>4!x
+|K@-7zsE0r{g3}2mCBD^y5pa{{kO%2`0qcwdG~co<?BCu%UD}Lzk2=lFR$PI@2zBdBfS35L;UTB_kVu
+R_<JAz@b2rk-}VRpk3W6<_}zc{=}-Uim%n`W=9?cr`}mjtRL4*M+)Y0G^glcQ$61fZPk!>BAJ5O`dB)
+MdI#+!9)yi7@Y8~gNU*+7tI-cjee^uA0NyF!?`&U=48b08QZ{DCjK1np=`;uvX{KKcte){9%C-;wk_x
+OkNGj-3GJ;wKc{u}Myb;BOcPk+4T^Y}^s^mqU8{OowHy^;R<UF*ZY`MB4AU>BV0e?z49px(dhZTkI3H
+q)Pf`1((eKebX*fwa2$@*lr^dHLBF|Fr(~tAF|J7eD{}SO5BtU;OT?FTeQN@4x!uH^2VZFF)`9+pn;C
+p??0S*B=n97OMaLn-6Lsmuh$L`Okj!i+}yy7ys8U82ZTQj(c5c^VR$RXz>q^AHTH1JYF63?0?oXUiEO
+-t7DGiRjcHB)qlPEpE~&|UajY|e|Kc|=bq=Q5!KCl@m`&E(&Bi&dZ2=jco}ufZZyWLac2M23*G;j{e;
+JyuUdNf>(MQ*+5a4sua1Mi#=2gOEBWvluf|-*t25Tv{f_hHU|9Wue)?F?SCer(W<U5mkM-&?_|J9qI7
+UD4xPr$#it+N(s#o3PlNY)AujAbP&gyxH(ZBX@M?ZZWJ)&@w+5MhpfAr|F^#jMNbMn_&ez-@ndeX-8I
+{4gVGVXq3zVsY9d4tz|GV;+a_xoM1&h*8{>P8VgqS+&_{>Y=ZHZ#|Yah!wUoILBEhkCyBYxK-K7q9zd
+J@|=d^{0B!$2_|?X5=`T{iidXtjJ`%J+*sAn4sV%`n}?%oA+QA@^JM!_dl#SLUHkaF&F>zU*m~99IjD
+6dJH;G7GwW`|Ew(!{R%HVz5O5OC?32JwhN)?7rI6-#q&B@=N?D)*pAur(7&5>-XED0DU6=4tLLlN_t^
+cOy>3VLub)?oj<b3&{AW+-QPcN(vg}V<@*@5AJ*s{}b#IUC|LhO@4oupo@y_}3xUR)ZCU4)J_b%!__|
+NE$`u&PU>5-3qn@2K;gSlBf`{zhr?o!y8o0c2<jps{GE%MM3dS*}FF?vQG5sF6-|6**<t9x6#crRQF+
+vL3vqtya?(5#>D@VA`x#(6n<mm{3ByX=m7{gEGbbc;rh2!Va_A_qb=`U#Iw$jD+@T3UJv`g4=Ly3TvN
+*ZI;i=+vSWT7Fub*(Tk2cf=C2?}L7;rTaLqo~7~ZL60qG$%h&K>FAdIp3h#lWNgzBF@)%0^=$FZy^P1
+v^V=(vG~y%E4aUq>_ffsRy=hS#z1%I~vHLyeOTRe%o;|C1_B)^b`2(5mWoOXz7VfCt$NkrX0k-V*k3D
+D>YVewMxaqf?o%8w9@6w{nPVLVR=B*d%=r!sAD(Zt3=XX7|Jyf<Z;|;c7%Q(%Dy?)kFXjb}N@zTm-&~
+FbbFGM%`!ECkObG?>}eY||}lgG?~rs$uFp&o1<=dfDNC$eY1+52s>ArtL%v8G2Lm%TjwEQI*t{aR24l
+id3UIq%=~Clvc)@%lwp_uFfJ_gnSSAHYhukI+C@x&741-BGVQf^yIjCBbeSY^w+s6pa!{yu0Q+_dJYe
+uUqs0`wduF-ftexG@pu-CtS<}lRS9wr+py0Pc%h86%&mp@HTn(dp=vAoott$yc7E}Sgn4c9+ryPE^8t
+q{g@VZ*Rpz-^|G`8`d-Cyquh_Z-^PfS9+skuSwK6@e!pH=4TSriokwq&wtjvw(->6s<fET|U6_wkbGP
+@~SZ9xS@M{ck@6uKSr_-YHQFl6xsKbSFpLCgiq<>n|eW8VE;d$QsxZjugb;WxDDtpRSKcPjT2Ys~8Xw
+|zIIsGE02ljRKjI=_+X!Q#Ae-8ffS$DtfgE7nfIEGcH$_ItVgJ+=&0?UjMPt-*g&tk8%C^I7$W?XgE1
+Iya|&gZlBy84<C&7Res5&exo|M5{we6dixQVS5`e>!uH65&Au^_2Fzo?EX^{F3Nl^$Y3XplgxjOh4~G
+y>3?z|0q`QbZL>eep&vC7S&N;E%Vy@Vzyt>T`~_?@^;GEsfb6iWXX~RQpkc1RStH(4Mx96zhRI3KtJg
+)VNK8|^ow6IdR7NEr+4LnS<t#a(VYmJ4$HL5e*OrxBi08~jOd>3pT!EQDd@NTl9pwR7i$`Y7C|S5YJe
+5T_Tm>IUFdb>aiSkkIVUsM4@80X=lZ969@;Ny0cC86&SJF~R!_l&#$xv}gl&pT)u7=)E1(nSKx)e_7k
+}9Mc(z~C#um}RmSwTQP};-kX2}-L0lL2Z3`Cp_v(O$%=SQG*dl0RVH1=E07JkWz2e!~%2P>Xo=lyEYc
+BpkNny?qId&64yLJcM3>UC>35TTfuHjVe#{@H53WH5w@#%QZI*a*1FJ?P1f?#Ezk^&fB+v!`k@-tG;j
+s|EI84to&p{Cm6;J?U<kjuKA^hjTK?2*n5-i(myuKlMPc+o|k9&u|}OuXF&OuKwL``y~esXK!JAXiNn
+f{=o6>B}PRR<~-awSP5*eW}iX#l3|{w{tGYs+;(%}m*6@)F4YDWtRIhShC6}jelCngF`>A8Ji1>5|2v
+%#Uz4G-VOth=zwMWt_Qngf5b;7o)xg{LQ2Pmi8uG=vCAO^J5Odd4)owNN@bq`=n%l1q{E~hxTzf{jTw
+PoOKCk0n1SKaT-b=%aqaRLZ4)vCrah$Z%`P%Z^KETUlxA#I352*nRUNmx;mdSWe)aGa*>H*>yKNU7~9
+7Yajs=Z;}aP4`B_Dfn;m@Qm~Tq@T^BUJ-#E}P?ERj)(qcd?0>+TkFG7y4Pf578rQ@8e$BwnNLY`B9|{
+{fl36FhC-ngS|BrQ(~&t5rrktYd}U>+JpC+<DQ51OHQ@e<>yNU&6a1PE*vbB%EKkYi#_quPqhBja_x6
+GoH>m0Z8uvxwtheg5>G#IWaD)EsR?muhYl~!BIcY8(*wl%@%#(5$#5QcEsyh#WAll9yI26`*QjZw6Ll
+eMJ;3QsRg0lY2~YavjEYgEC-cD0Gd_;F{fBJ7<bjSkv3O^?XB9S{j`;WXTf_z!$}r?+8rc_>Dj!}Bs}
+=}lJnw$-OV|Vbc_n`mjqz5-PX`Vx4&>mKh?#pq(J|U4+@ICWdbtPhfTkK-9#X#~_%)GN8w@_L9b%W^2
+t5cWil;_<R6VII2Iixls8=ij3LhUGi?sJ!>)KwoUc`Z0JkX&F|99wUt*5$+xbS4L87lVTB<xxk)CZnr
+Yu1w`!ybQ{hxSX3y>WU7Jr8OpHsr<zJi$mgSglLJo~Um^rgAr6;+go?HT<;Ytnf>gMy7>$8XnBcJ;3`
+NsuC2P;pdYlXgp6HGJ=f67bHy0iHkAzK5m23@o`JRp|_<Um8m$fP_8{nP>Y+F;$iHA2Gq=8LkQ1WA@x
+Y&_WNqEZa4ynZvVtvB_S_L*l3#|xt%N{BcfMAv5U9KrAOD#hxxQ4gL_chf2j6Lx)J)JmHUYWZLNpN-0
+$o*Q7(z;2D=wwza}BpQ^6v{QY*27?HKKT+b_Y$p>T2NPZb`j0rxWY-trxiwTBEbEf>==CPK;H9@Fi|T
+gTqV)qcsi$9u6_-BG!ZdAeE;EYqLsS$*mnPDjq#tE@}<qxI7vz2~9*l6e~7vD*u;Y)!fxICc7@+x8Z#
+rQYG46Q4L(<cVO1Pd%BX9Uu4Rduc(M_+5&EHb<~@A*%uhEHhLGyL~+ELko(5BD6Si7{=h&pL@I)yaZo
+th$<d9-c0u?0Kx<8cVwsov_urvo$wA=OJSG6KlXJQ@5a^-7rZ34KEYm~V<yYE4due0D`vltLj0{%9Wy
+{S;t=1UI8IbZtETNgT=*pj?7-}vOkOv~C75=}^6;i76MrpKgU->Vg<+IKxq+~hR(;D^hp?<$f;*(0s8
+mCFAOMDM8cFE^PNK{fZ~ltQH(d;M01+3m6nETm!AoZF(SeN`>@qbbn*#!R;2d<9$kfF~n9PWDKu1~`B
+`m?3wTGcTV+XcB+2bAfZijju|F>1nMP!Xp7USTUMml_C6>B|*dR@w=P%<OebNAbRNe_lSd!d*vwvZt#
+jh@QLqnDkC;8YC_u?XUUws{cgv_5ss4Q<x$xBZfCge4u!Dj^x6#i3t<j_H36`W1ztZ2$v9bccu?PeX;
+|C!D$C9`7Q4>rr+viRp%Z;JD0Fj^~Wv4$C^wlie?`iw;M<4KW8X&k-7d%}ZLuPR0u&y?nY4F8;yw#qe
+y`7g)ff9}I8^oC`&cTlYZk^kkr<=C%86zodn(7Zx&p0At!EApU1>jp0la2k^Q<cry72zCyR~AO6z_Rc
+Nx@7ewqYJyhOws&^zZNV1cR0-O3rm$uCL0-9)rSxd0NncJO##m>EMtx#SL%;O0a?7fIVL)Ws~2M?SYo
+`vd#k1k;10q7irB?tQV#7`Y51Kf7LKh*Z^K`g8SD@kw&=LjbG=<_M(@(Iiht^?u8f%n#KocIR9GT8oW
+?~4xX+s>c(iJ0!hv&<b(u#dGq#9o*DfnXA)+G~Vgg2E-39@qArF$}ro>}bDaF6ZI!B^{R9)Tn|jaS#Q
+y=nlfa2O;Une=dT^9*(jH4mq_o_dK*;(vA2{Z?J{^ZtczPA%*gMiUV)}2vj<FVg}L9m@yIsC*<*Ang$
+<X-2M*XLEBBlWSqV;j=+9X;2iD*vBZN^%Yc$EjS<kg^~MuLgRm2g-qTXMU;L6oZlpbAjuMBHxa?tGB8
+(`}=4ps0ZXcQ;p1_7#EHa+^!@!w-2l)V^*9hT_;7~NkhzKnLhmcw7j@Z5I^5;{bWLCFZg^1%K*Uksv?
+ZF==k5fCbZj;r*Q#(<;&VIti3Hcvr=kw5+n^I?Vr%w1GmcQq>{gUnjntP~gc^87M!HycPc&aR6UpPr{
+%pL@@iYtj$8b-PBg!3Ht{I*|mOn+_2!h;hmf+8Qp`0FsdCbIW;%Z0+k@4#w*1P>c_aGZC)?U$Ua+XUg
+QV{bafp;^MEE-~451=!$dAUx(t>^{&BhtaE9yvV(7qy3UY!;aJ*_^`{M8Wq*LOPq$qM-7SK>q%ydzmi
+d!AjEYj`+GQB|Mt{$9E$;juTL1`f`~6r^||wq2hkF)dV(vFt^s#FPCdW_jSZ|NGjJX|FVfw!ZmL=3SL
+X604#1g#mz;)`q|y$v1&4bZoJPFc0b9_ezK6g$cfWyso4f%|+PbtQ3;_>?m08Sc50&&CDpDJ%jw2X3K
+^iBa{1V{aJe3p<{i3Q*-7vyGq!vASTn>7<5%AOmt%4tv_2D*A(cwq!__+4I82BZS*q#TxGJGcB%`rBU
+C3HK5wG4l`^KiXGuqMencq+?cI&SkFsBh~@JRsfe$E5~9BnhhV#m;n9%_$Q<0`w<zVC(@-|EfNdL~lD
+Z<s~p8*kyO*r-*Y9`gqbuIvGYLIL67GqwP!$;b)wB7y7f{6Z>ty<it;09jPFaL`_3~&dzbLP~@z5zph
+(Yvp{%H@N@+8i^17O;MNZlUUKk$!(@tqqng-^q$!3|Nn%ws_E^i<yAkSvh!36iOB}NIML+muF!u|O=5
+V{>pAAgrfn&Pd<>wA2>2j(f5yil+B-(tZlX=5+&jY+<xrNO0hEiY*Xeh)6TfQ!dWs)f*HrgR`?VVNQb
+AkpuTRa*1?cXpDYLUQnyHFE{0R#=<I6@nudh<n)ZiBrLc;L99Xv>V~+;X<a;~cKIfC5@ihT;H(on}xU
+hdCIOgmR9jWQLju=EQ~!H_=lwdi$vznzp|~Xo{vEPbU=n5_;~$Ln)>sw7^vKU(shN`&Sff>%ucZx87<
+0r2T5WI6^m6YVT^^9-154bo3;k>Hwb%i2q<;4!7`A+X(g5C}T+MxBZe~hPK!jPjjj+R66Gb)kdDF{Ng
+E@lVfTPkEW7=*%t|dsO`7dZwqTl>|<>Nsn{O9VI{+Nhu|>+E_<jtT|96)X!ex)sVpSf{l=4ZD<==gtv
+(}24XCRQH&DK{7(%>5Xi^-*$bl#UpHa10Zq94ZZ;;2CN<kS|0ZU8Yh7~XUW!}#;Ji5r7XYm9ai-XMJ!
+FbzdXzA{L+b@}vfXt_<O~C-_E!p>>grc3v$DW6P9E$|nAL?XW$$k-XH3W>$<L(z;A{i&CwFF^Az`gAF
+;rM5!juC)$NIOwwC;lw;Eyr~lf{0WEk$b<jB(?ubkeXY5(UZU!(oo9;+JKFiEnURe4|8YBO~ez1>P=I
+H`KY^J>q}4wq<4n9_tGmvmGSw9=oOHCn&1%!1aUu%YHSWaG)A~HvHgc=zvLjb+3^}CYbVQaL<-AMvPH
+rIf=OJ-HjH>`u!G(Nd;|_pFfvRE-}WNS;~-eh17XKxM3a6WX!8SvH@5LgX2})wG3?pXEDv-7Wj)T@9w
+&YYxw+#sh`UUQATXwU_5c?xlYdy+%RdkZy32)Pg;1DFjjhkpJdT8nr}L608uKK@FxUf=cb@l%@Xu0Ah
+R<(thk{~#3HmH{-l^aI`O=%j>ICdJ0<fetcyYsY=M<_sp=b&n3w%;aY!-%coqB4QyWs9O;gg3S(BHtD
+g_xd3>`!*cMO?^>gQX`GIs-mBtzAe~gl*a{hef*Xe%miuyz{bd<<gwXWKlGDBZHXvKqo(_GCGjGNtif
+Pn>ZHh+&(5OY1fJM#_l*^Ff)fbUt-D&39M$1XjxSfroTgqpxY?-Jb7`vUR<*aP5j(-ApNO{y+BrBaBI
+IQ@aR%^5q6>aO0;vS9r4A&q&vPVUhKpxOxJhUfwaORkFz6H3N(ZZr&*+8&`k1l?R_*colI6&9X}45LK
+y|mvc=wSg<nF!!%6+aG%d)AuTzBpg(4v|8Q{ZNo#H?EuS6ax=--ji*$aBlFZCsa@~7s8XD)IkC=N!%7
+M44b2C~S6{K?$VJpBkH7&jZzKkxiSE3Eb>&-B-WRlpcg?PR9B#K-t&81{jtSP0+*Gd~7J5LTT9D@gjg
+|Jmcc$m0xa=^%x9IbQp<5BkMj0)w6m`oRW2Vwc!pf-uSxR{;IHKIw<6{gM;=(wjpYb;09(0^Z(~@M2_
+e0}-)7;&Z@j7Te?`&wCo9nM;V<zFqJV;;ix1^Vmge2Tw=6puiN5X~m%gpB&y(go)E-%@#U#v1Idk`wt
+hq1am&roN9_gXB8$3x?0fe!(`b7HDei1@oMe?;v=MjjcfR69;h!tbb8Mi2qw$JLLo_0W{D3pOlp;u(a
+xO*2*6X;f$T4L<Cfox`jUiiTNiZb=`)zeUM<Iqv8Bia?eQQ$7^by{X+A9eX&ounjM}CLMZ@iv%%=;$<
+IoC2Jr#1&%VDwhgsV6rhdH@votVz@fziTLKq96jn10(YnJ9||B3-9mPHA?L8cL%`KpbR+mS}&O_cB+k
+K25ZZIm}(VaQAE7>uGf?N+R^Y7^N1JNVUpM%7&p!9QmOxx-4_sqRYfnZ8mpYNbz*X7vg3Rztz0rP5iz
+=E#E_E@sQx4+;hK(sdncE>x18kh>z!|d4QJyIl2-4JwcR`WD)_VW0Ea6m(C2v>(KTig=CbbrE-a*paU
+>A2dA{8`xS;5FtOFh-H&Nn@M$FvYCg@}xqmF{^nobLr`r!_8D4VEdmivhT<5SpiZID6Av%1LFqv%d9t
+T4`&|)aMUB8JtOgK5nE)0_g=RLpFy`IPRmt7w8j`auhtNZ@k;txMN^=Q}^Q$cZWqe@ZW!<@>lYok_?d
+e355xLnLb5g}6Zcc3h%mCKBG2&kbM=S~<0mcTMQM}kwiHm5zQdmSZVkgb`?AU~tO?9MY~Nho$4N*6+G
+=|#cq(M5~E{XDEdsJmb4UU6$4$*Wl%z)qc1<IK~%yDMlR5@~9oxo=;(2htbp3WW}B7O&lJ`z0gQHc$7
+bouk7wEjz$O6O-t|Nt&)NJokL+W|I}d16+p2h>`tLUqa=1f1V{HS8?ioT=IBxFl;||BTRTK<9U<>4$d
+;>usrA7W`D06b+7X@Lu(-$P6X8KWYC8@m6+q85^NEQ^~jYLz!s%A5{{vQvFQGmGwNO^{@KB{BAi1Ip;
+lx=Ql|&EoZRbyQX7DbWrBrhWl|`(PV33=nk3BhOWiAF95dvvLs3Tw;;x0|z`}ccQXuyPpRCge+(apGa
+N@pO{(tj_le$;3aA+1b6W*+(2bb$`wM5WtE0WFp6^4Raqd~<Et<#l$Y7@4cQTIAdqhg|kIMrB83qg@N
+4V;<BE)kYOvHirs*thvqZHMEvV&Pslc*zu_Uou?E9CT&~|0KaWp;i%KP!AOIz^$1W*@c63n)t-R%-H7
+x&{!`6$7Ku?UfUZ0_^JlNWj7B=MV{JjScrs*)^%|w@mK3W=DPbmv2R0-#k-(Nmijtvj^pOGhE5<4Q`Y
+fRJExyxes^2Mb8}iM?RvjqUrZQNBYhBnOo}cim6S{804Hg{wR`yNf#o}!4bhn*NpE~HwmqiqwLK*lz(
+#h-`=Rj8{X1fmr4aiVUR))NK`YI;+78)^^X|9(lBF^pWb;sEleEJF6*Z|4I^1AW5`daDj-9fZ!-#+!G
+5Kp%*}3yWr**HyPbXS$QVY7!Vgvu|6eL@~m1cZB)BwgeQEPNZLCXNC41)i4ZXQLdnejqh5!lC}WLPMz
+oka;Mx|A(A7)l>bY6*uY6;6+6ER?khv~u^`e#t?y=&%ytFxTc`5W?kIR#>=3-~f~`ym4CFxp#C)+O0>
+2SnT-Xr0%uDN~>V-wiNYCs-oY=8eN`JVKWiBiM{Hu)`GGH00N%kW4YhvJrC`dOz{ED5BV9ga$1OqA9G
+?xAAsS5`C6wj9pL-vmaux&LGTe=Nc(NSWKx^)m=@Uc;>k0Mtk9*7QBN{dk}jvwXj!Lb#=|-_643bT=k
+t~a>Runi+^b0hY!pQvgJmCR$1#*-7X@N~3OT`K=OGh5t^M(@I0d&~GR@;)hkz`Tmd8G=vTKF#iLqrM_
+(@A#84M3w=kgzYFjb_`anCPxujlXx3WCUD_o9GkjpXoE*bQs!vO`arm{=wt*Q)7p?0PQi3GRNWd-d!9
+d~%JgB8`e!g<@+C|6uBZ^G>B-A|b>d7;a(6BkPNsWWVwfroaj)osOr;X(9q^@N3VT3ssB~T*%lQG$ON
+xnM<PmYx6S@6LLmfrsa%ClIn#`x6I5wMU$6hPy{c#b7E|>8z-(IIjg`2T08%CQulg>b$Hfh;Hf#6l&e
+0d5u8>jn|QQZ9ATeik`JqlcBRQFZ^V7S<$=1_gMj$NMLdB*4}D{Zf4hi?!c(L{A(CgdhLxXo?t%gb-D
+>IR_B>GcdKlwUOnOqPG14IvUcCs0g?cI@X?Yc&8_t(o0`JX|Z8z53`+~aH)A&u4`P}6+EIa!RZ);J9J
+UtPzbU6X)hkf7;ne3H)5z~#4_jvJ3hHHBGrp66}|3t&8Tp1CyLMPU59F_ns9YW$aLrvl9<@4`;7krXs
+w@S@qiejq$hMSMHMo<GGb0>RaJq*#xLA-F{VXq<AxYv!k*R=zc%TXd?L{g-V#}JmPKoNi5fmAE`G|$P
+wHKqZ=U|@Q0TmMq`dX$B^hsp|70(&xqe40bV6f?FpJSQPn!NG!hEGl*fMU;bBbv|$Zfx6epMmPnic;5
+%xWDsOfokmNn-?Vekvo)?%3RqS6ASpqLY&?C}&CkfY_dce8%Su>^eufJAxu1?-O<ZP4JW}wWy5~Hs@F
+4ZaxzGt_?!W`8w7KVL-77TtL2l)dmT-EUk}xr;yp*6{=Jl2@!X9M9AaTRvOt=T9N9y91U+P|uVfOFxA
+SPbq>G_t$yfLe%Z^~0Wk2xK_|M)EM2{K)7|6T9NLbV-|ac=#G1F5?oR!G|10s1>^FSXIi)KmSiv)o4I
+gv&Q4pzd`d<qJpaw4BhExKJ}Zk@EBy0uqX`A7*)=sDo{aEgxGvhuUEKebl|4PxCX%uR47A6G10?g+ZS
+~9W(3=fIxnTxO<TnUFPY^3eDR;EBz9DfK9h#O3P}0jJ;_RfD&1rQ=&(Juq>QH;?AWGV6`mze|{P-c^u
+UDf-yeK-x|PA59px_hHNoVjNyV3)hzuITI^&8YfjiVZh4^Y^$}aSYIyLO2(MXyiQhHVu(eAvl@a!v9y
+Xhf30<<5G}znx?4<5>jV;#5gP7rDk|$4KS9*4r!yrpFOZDoB4S8r^JvDmC>WnWo2M4#boyT8C(JWNiV
+vVNmFf~?lc;t)pi>lOuGiqGdPIQU1m(z`i@4Wv>-Rl579CAF*VGq}N5$|EOFUu!%wan4t7<>GPnzWOW
+*T;0KJp8cFQBic>_L%)?E&ipLdh|m1A=)N%8`c?;)}Ed=3)$YOz*^uBLiEt~?N?j(N|pUFEx0v7Xo&#
++4kr*6xMz9@?&*#~d$6=?$k!1gi&w_BHG3aZ_qv8#lVAsN8hFBw<-y7=Vz`n-a%qg@W@eY~QuJ~&D?I
+Gbb@xksiDyx*B*(}cqQOHDM=h-920enp;fe#&bHZd#@|DM7JtPsIRUh~HmXo^ILoP>VX;9sGoT>{ek4
+XNQS0f=bjy-54g9!?!TZRtqVK~?G9`EJ4;R)($r1feN8E6qPD`^KnCM9?l6{r)fg;lLs$bpe{2~s(OC
+$>IIHUfy&Os71^LY0l|5l*(`5+B1B7XihAS2oH+aQnju#m*NF>s~Fv;F#usl^qzFgDULfR*bBhXZ68I
+R`5MSBY;y$6oh#8s(3E-x%<U0!Dm@u;U)He;4_7geb;_lKZ*Mhj@;0k$kUZ}PoXv~Tf4dEu<o@iJy0Y
+KbV+r~4m=)7pnrzD;gC}Ziu@eU(mfa^9Fh24&o!ufJ&A%Q`r#qO5-AV>9USi+sWvAoWMv}o^FgiTf&P
+6ME0)=q(m>Vj7r%skvGCZ|KBa+EC*d_|h-x@J3}`NM+fw55u`|>Re>;`*vN<ADyXB0SRWrh=fa!S#4E
+9mhP#9i&Qq@Cn?DEusoE}hNZOVVZyIY?P>R$Uz5o<Qx!}{@4@yBP{4|KI`$<_fgd-x%oFB8B9*w%PG@
+A1M*2y{<S>bZ;&Tx@C#s3#c9phVrNUj$Yh9ty%x*A8KcO$-q3b))X}+?fY`P4mZ8lQGF_=m8QD?2zAc
+tV6hQF~7JN<S?HW<d*Zb*Uh?DUg*R;4Jh&0RX*|(Wm*RSFaU{N<dl1Uk#lY7c9DU2=AHI^3WK`WMST4
+542Ko7J2Jr$n4&Xv21e2HcQDSW@}UNQK0S_Sjkx6)`@V`n-Roes9#qJjhaq|ibUYD=AvvY`?ClFHY~h
+lJ3<(R?_Kw-_mo#PT2kKr=Y3?@ch8CCM3HUD+Q<hz-FY}?JV^`!c$r44*e|WCLv=aStPS^@tPbNaV&c
+n*Ua%@gA(Z^{cVkD1yPwTT-)$&9ag&9zRvGcKmy4M51wyX)WJWmj36n*Y(%}i@U@bTbQCoaZR6mZB6&
+sxJV*WCHoLEY<#ayu-&H8c%R6@rhE@<lo2Vg;u)#6)+K+Lv=zojr~7^p)7Fb<Z#7aiAZADy}s>BIXf(
+N{tMN`TPTjHgTj+Ho^ejFJ^>75+Yv-658v`-7n{H%t42z;jJz5(CIYV#kaBmQV6i^CaE_=Sj^qisxvD
+i$>1K_ZsK^qoQG*H*3Dfuy+Fom1vEiBQtsWm&($Fw9AjBj^bgV=Gqz+hzn}VTzvLA5&%@FZb~JgEsfs
+-O$u2QQ9=T1QPe4AET6Y=wSX{X0=GDWxS5E*u_k}wS#+)uu69MD)3Xz{_slVsGsA=>-qcF(YU2NDK=i
+M*oajt!A7A=AcQ(KYpgwxJ_Q%uld-qiA1*e%dxA5I0cOhtU3Cxb)7qcBg0%VQxV5d`@p(mD(J-Ys1MJ
+yC>>D2&X+k<KBmA<X}{&q***y)TS$DQ<Y0+%pw06?$i|MVHuT;NcFIeG>buX=Y%0F3KB*2$c4DkwM+-
+Fo;>EKeSdl!NVo?{XB{0JWzSR93ZI(Tuun{nyPek{oLofhjp*R(=wL7E89?Y4)L6X7KiGa(%<0?N|Xo
+>ue>R#Qd!~Vbp`lJ_dHnlYSCqx3PnuzickL&{~j9xvpk4^&!?thA1To+vIIJ?66SrM0{02Lj8nJ9i=d
+{WPS|sKc@oCJLJFZO``DUm&-)X~A2{K|v%`2pYmR$<&EpWQSjc9HuKJgnleLqa(qvTA+T}6Ms(znR;I
+!BQW<T!vE%uuiB^hm#TjuP*INV?)b10P<<xx}jG1}3}PgKa_1n!!~Z>p;ucVi=4?^vAfsi@(Z8j5wpQ
+_{*5Rh~v4lqgtnZkQ<H_NEw4pPXrA|GfL9z6AH`ap@QmZ+cj4njt1U+EpTW!W<x*7U&68!}!8tVcbPU
+hJ>p94%f}Wz_*`vWK4xOfOVfb`ECT$e8iSZE_WJ*u?{%F9v!{*{V;>N*GbqsJl;y3!qR=FiuU2eUmJ!
+{ZU+ye6!CHCL#v;xZSp7?7AbFkox0aGO|*L~<1m@xkxblYlj)cDDd6Z?u{AyTjafpESk&bWRm-Sm$H$
+Z%b{y-`LUs>;Vx&9OdLQelQV)exp<2}IL_&+?J3t+tR=B~#?ta7jOE{nPRJt%Sxls_3eEKX-?x7@>7S
+|0%?$?AorKyA1dU`T)ZGMLBeL1W_U*0hw-ZZj4TsP-|ok#H%&|+@>fnW0Qq`<C{_6@Nx`#k!v?iB(#@
+E<56gu0?YtSa$Buw7YTRVwC2WetS3GKPp`5>QQ}girRuy4R2iP^uOREGLnto&=H9+|%gqoPB>{roA|Z
+Xvu4zrm__GJdnr1L0;3k*V9AGo>Sl?+G%MjONME)B+08i9#koNOrNOyAiJ>kWW<&;>Ry+4oFl@Ee-4T
+^j>Do>(vrd(uE03bECk{GcC)=;#S;Yp1YOy6$8XfVKD>6$TY@g(9EF%<!VU&FLW>a2pwfaq!{O$*)`<
+stEdS9Q-2GDbx;U8dFtjL(0)Y?&8NEijxvn!JmbUb#;4#?k!#LIwb1nYXlhnN~%Yw0yhbNy135S&sR-
+p{ngXoTA<TOp;wP7CYoE8SdgpC#R*1y!fo>Qe_N%62HX4lGs)7^_lMTU=D{KRM-USVKyrqgE%AN6(|H
+z(M2ub%%!b8g~Kd`uj0ramT4=+leF3}%OUd~0Eg$FST$M|kyo?z+rD-Rp^=THZBAG<d>J5AV_1R0qC-
+H!`W9K2+bb>1hPD-_^y?Te#<e`Vz|qrl;(;9df)Zd#SYbj_LPsmfVB>C;Lqw3_*SF1jv>L>RumKag`k
+rSx<vm5qN692?o6KX2)4*1^r}fnV0Q3H-~y}*BcG%UiWPSo@nys6L$}4s4R9-ekmwq$=396YAK573~i
+&5Nn{k`9&h_46DoNcoQaRUWQY|%D8)Xfj>xo@&+G;y7&$jfSo`K8d}4X^r+)EEKy4>!tD*X&HtzH=9v
+;m+43zde;ALU2kb*PzMJ()h&mNimy!|NZUWaE1A<~25y)`WZz*n$J7&rSImJ#A1xS7o^59|BzAMhEM#
+kcE@v#5K0l!d0J6D95@&`qm408oaXKohbzQ;d%kjKd2x%p8_oQYJLEUrpWXNlJK>JZN9yhc~jkt_9I0
+nL~`futU?kxXfDY5-~F0)DJD9+<DyNrSA2yjm(l)YdLflSuLVAYM2)x-lSG&4ok+D@8v~JR3e{#r1)p
+=3+i6a^n8mKJ^<$|ZkYS&K9E2gC&D7tWo;)GiJLR#kT8?ZSe7H-ekygZ)05ex7${rd1Kv7@b0X`N4<b
+L8SAm=i3!jN)JS_FMf(DDftVX3@>R$1o&(o9PQyE01pH8xh1|w%3lVBE4X;^jfv<k+N1phcy_qvhC8E
+Ve$krN7`F35aODlcbB`Mg1tuwZ(>(Q?p;QEH5kOnkQc+zbb3d%U<cyvs>#(I6FiT>E|&ug){is{ml`_
+KO?OlMbZX;IOj6GXtA@Qls7Rkh3cI(2SAaWaq8r!C93RI?>w`mGclThad5kP)LWlu-yGpU&4b2$CnT5
+ye)!Q`<670V4HmM1{j!6sm0&~+_JSe6+1lBM7h+w>+q&^uTS&XM!OOY)Q|^S@G|CMu;o=Y9DO53Iyra
+t5Yqv}*V1t`OZmL#mpsn$#5e4eg1(caz#-x~U7k}N&b0zhufgTfJICZuA%x}35x4ApF|B)5!+L9zM^+
+fjDZ27Lv1=a#@PMiHo}5lrh@rlkaSqg+I!t#R2+k#TQ_0h>W(qVn5;DN$PQ4Kk;CURLB4+jnQ#+}gTl
+yZuU~kvMOzK`oI={?=wGkE`do;>2ol@_R*1@VRHuJEcC9HPc)8}<;_$9aeTK7ssH-~4jw7sXN(@5`Q=
+e=}=619V>tT}Y%%!boZnDRp60n;-#KbzFOo|JR1rGlQy6#LT&^+s2A4b1X5_GA38lBx1xRlT;yJpQ&9
+le*W%<qFGtKFFpm3#d#)<K;}ryu}Ja3my4m|Kmd8gBjx!i000lPVakNo=KxXf}mvXW5(&T+AH%SpN9z
+{^r`v3<wc8lR7<CT&bQH6_xxI4;!zl*1$K}3&Z550wEh=Aio%*nM&+FQq9z<6d{S#+t>&Om@|-(A&yA
+QJXL(7$>Dau1mW=D=@Ua(%?}x&Nwvu06-oT*0ZY2gF=LoYP$KCJAJ-u4orGo>o^2)kY3J`2m^<#JG6a
+YwE2lL{d7^YIzwDa2asoWXxa^RPcDdMA~q=!}ZnteJ_F{-;Lb@Fb^eR|a;pq}^Kmv>;OciheS$=2(8Z
+1A(8lw)|<Z)BSu)}mfoW@?KpH-R86Fvo<(EyB-b$rnih`=!3b+AYhQtZA_<BTbGR{-@K37M)@}LI7e3
+t81=(2{DH3P^z|^QTKYz{VAeBcZlMj>TqVzc_8Tw4Lqkd(o3NTB3X>ZY1G9*)S0*5vF;Vk^1xaxPI*p
+g4a9a5Odgk0s|JE7;!!P6LND(N@=9ZGV8Di5`<x}%oj6N!p<qq=HDQ3jf&;Lnr{z_G!^<$jTa~EWUOQ
+f3B;F#i^DV#|?iE(+Vr(tPJuUbm|3+Z9m3DdA9ooZlzrtmLUQg+*E$le*$2||^aXfe6t>A~E!+})Krd
+SXQ@EF@@s4PtlIaE)Jd-IjYPg9+|?FDtOC)w+18UEOFPtbW;>}cl2CBvmAh|+!b0KCN5&>}pVkox-DZ
+c_KUq(GDlodlTYX|RLF@=hmH*i$DI4X|&Ikqa_F)CH%4c{G-5*JV!YUO^f&ExI(+G&7psyzRx1{*}jY
+Y{6T$$dwSI^=oQ}_Eo-I8FK3f>RuPqe(F|GWpo(e?fXEyQUdQ-9`F6Q;NwZYa(Sskdiy3`w;*!ud%q@
+iuji(c)_Fq*rV311#fiX>OcR=Ecn^dKGe4>jwZeW3xt(3NGO2q#DMNb1zLmufV27kH@K^CFM}bcG-P-
+34PN$`%mS&GIHAIp#c7AhG_j-^Vo)*^d`9*R1Nu~9&bUeKm5Sv3<a9Q7tLq^RkzvDC%wgzYF%;R1+&I
+up7a?-<TZI5+CUcb$@-j|9w7=Xv7od_1#0IRk<&G$5avd>>k>Rt!+b;AodxTod;SzQ)6TW?7gcHX~py
+lq!P*M;krEjsdrL9Q!qd7$ofkXxDX$<rIMvDJ&T!$_~9L%N=pmO$6iL;*1<#&Ag<z32v_xnkdE!!@O?
+*ZtA~vH*mO=jqL)Qc2Y2_NF<Na4#N#>rfOK1WqN8@<Eh4$L;S>_lncQ*vPx5XXs3(bHk3Mzn1WuVbi_
+y)-&R)5Fl{MbnNHruWvb9p2xWq1y7lJ89h?t8Bh-8fJj&9_$$*Sd&4W`*@$=Am#MW5`qMnXOOViIzh3
+)6(q)jm9NwiQnFofS7NwkKxixLx8H0~2bNA-VyN;hT#4n4OYKag5hYQL#O$s2Z%ffQs-cuqN%G2X;@Z
+6am<}$ItaE_fX<lNrN^!o;KrvQ;3FS0Bz7z7|-AoOs<vShc8$`Up2ow)>^r?=Oxo$uzXLywnu$mmfa@
+?NU7e{!*TKf^gJC^CIM-6pt(q7u*75X(?+wflvanBv7ToIur+q~RdfcY0;Lr^*THPobvjjSygZPmlhu
+7ryPI&vWZ_>Runh2u8H<)RrX`oUWOkskh8+X+gb)KtMvO5}#sl2g~sGq055UeV&YL`^&?^8r2qeKpi-
+|%UW&;8JX|XDjbg}A9^ii;hk4#;UVqs+CZ7y{-kxUmmA$<agY<iC~9`;;CP)L+GBd*&*f#H`yi^}nRb
+P;UBbTm{xi_)%kW}GPc#vp%=9if{E}&o-+MT5PFx;J#)seGQJxgI920pFZ;9LdfiQ`6b1>cygD>W>!0
+@XX93U=<AjKO$j9J+i$9^xD(ONLvnOmPx_j+(rX?f;@kY##hhhead{2GXT%$PStV;;v={XUSDRs$|iA
+Kv5TzE|&lK8)Y245#)mWR`wS!08|8Xdjuxx?9u$c_h8IC}|s>nVq-4L*45M$r{5NCr+7$ipI^cpMch1
+nrv6xn}f_A`I>lYc8<35RvP*(?!!|Z5ftq!>w$A5iRLu6W%C1B9@J5d%L~4-Lzl(3z*jc-@>>XCBJ@k
+$>+mR~H=MD(r}u+T&5hsGaCmVn2-EZB9BUF)d)NE6Z!QoCg}csXQun%Wx5wO9!}8$NU+$+{KyVpei!j
+q|f=(RX_ey@1*jA^@@`C&Rok`v6vF$AGiq}_q3GeWRKJt*bI)gHnd3d9sMfbv>aO8yH67FH=+g_Bs1n
+2Tn@2JQvs+AoOav$Dvx-VbE8xcU6Avaw={qhpX)c!h-ZQrbWjs5oQ0TV*bR$58!l~5jxbIF+V^snqoL
+ZJ|l6Okav<NUO4+^a?<!V0~NX%!$KL-Xtd%daHBHS>Et$l3etIiW(*cU6Hi@tOBL@clT{Fs#_0r|N58
+9*U|yRV8fj;VBhF(R1T|Uq^aSQu!rDb2r}hg1Xmp7fkAF63LOIAD8x!d_bBM@RmcbL>WF14McL<M7ZY
+@yka%>eRz|)*CE#Pu<(?pUb@e|vtMYUjNsrnUx0dR7rc^iZMNxsFWy6c_X{t1>b-bLt;hXb9uoi(TQf
+RZ&|Q;@|6-8~&XmD`;D==MIc~j9-Rof99-1ug5pbi6O)dWkJt_}_Oy_~%UwL#3`~4=QJ;-LRxb*{duY
+=^k;WrOh95g*rM{bf>Vwjb(0@lJiFI8YxSE?)tqcF15;4q)}c)3?iPoF?xc{APVeJlGrb5Qf)K4Pm2m
+yuP$C8}xdi?GVJ{-b_a*1hJI?TcR{0AxrwAUC~VZr}0^IZ=r4L6%p;l(!s0MZK2(u!7}Nzwi<YXix8Y
+*k29<(Gw|qGk<6gy~fkmr95zZZ`dI>3>0jl*H>=6&bL{-oJyC(2S;}KEk%A+$K{<ovT@(W3kHNM!vnc
+&$@2O56qh$J+~b9pq}Tahet_-jfpzz>C%eKC`7Cw_`F4p#N_IVT%48hdZr<Z1kAo^ZfMe4$C!YVcgve
+qH>KBjuD}vPjSXC2P)7~)DLTDA&xX0Um$r3)UJ#T<f4=9H-h~&H2)=+o_lJ|HK5_)mV)CXcWdVAv7->
+bnTxGx8G;NkUj7VBCpV6m7-NIhUGx%S&Jyb_Sua>3M=D9n5QF26qMy8Go`HID!q%#vJNxwFC055soPG
+IAgQ6m{Yc9B{T}K`Kt#vS4U=so1A}y}!g+HD>i*GtX*zV2J#i>OugO`^(RG^zcp|{NKy3Um(cw?vZ_u
+_uBWpx=FP|7HEJJmi!ff;CYSmq9#ctY5?@H(%DyxG;JQV*nSjO*7bN5PO5y;d-l=(>0*2C0QF*!?2YB
+#`tI?>%a`-ikmrd_oot@UO=Ru&VGN*F6S+JM|Ae~+R^E?jnVHlMnHroQU3kpX+p{FQyApZxKCWrv9H3
+wL<9<Vk#SvcPA=|tB2XQh05=*;`mLE-@iU!1FUt6DXCr-zk%cnDC(=S|OiHUa8#ddFV4*S?o=s}moq1
+HsG70mT{x_;*axCn)FYN|s%wNs1U_T@=gE6k8pA5A=_wKg-$$)IKMMASCIn@RGX2hQU-zqP+Q3&^8_*
+8hF_O&^Y&7bngluv)3=wGITEun%KH*K*&x$jt(6H<`RrcNpLi3e~RGwm0u#!+4gx@zk&n+*_8myr`7w
+p1b~m1M4rd{0-g!)OS+eHT{wVj}%k8H4oikn3i6S4vrVTm*pdxC1S<goR&kwy^ozn#Z+UIPM%*%r4)E
+!3ah%mFAqbqlsG;Nijpx{pPHM)aES5carqrs9)OW6cu%Ct-Akc8edvq(4KdR%)^b{aV#CSs=6av7$2&
+YHOa_tj>(k3PmBz~xc`CK?34YO}o%=RSJpMy@dknF-y5+h32aZd%ed9Me*L1$NDj~4)8Vww*r4NM5cY
+1ioFB=R6TMg)z6q4@qn;fEfIZv;^v~rEG(lRV7k03I0?pbohO$%zWonOc6P<bVF@@?Nn#^dp$Z(o1^(
+XS%<(J%h}&4<5!{L|Za|MvL9n{VI$g)ioP&o{|F-oE?z=EIx*9A9bs&6|&JzV>@Yzx(0CckjP{!<XS8
+$XIa`Yv~bgV{Qva$h5OdC-!FfWuFoR?<}RlXqIsSp2eNa<QM^8R_OFn#DLoM@Qrefj+#)_Wtp%yB_Yb
+oVnXC@xm>K&^7U&w{S<J&EFQOZbDTHg@kJoHi24@p)>2};3Z1LSmS4$%Jzrd;wYVZ`aoT>>E;ge=xck
+MdP*=IET~vvivm_y~jEcApaB+>zqT*t4gW2M0o5jrx%j;WERjY2}OY!XDr0eqDE)H2O?<Dao4rR29I@
+?8g<FZhef|x}_ybM}ZZGM*!G54z5n0aZeh4dbl*8-vu7C3A{u@+2WvF8^Rtjn*g;1JZ}2*Tn_((*(LA
+?>Q2te`lo`$YvH^^1cm%X^nN>ajR|u)M3s8zQLQUKV77yq0A{6xA$t(Sp{lJNZg&QuoUS!MrXs?o!<d
+J&5WT@%SS8UPKBDFLYsBmRB373fUctoawr5r_@Vh$$~A?Udv>cM~wZd4?K}2IfNc9FM9S~ZGg$LVjUE
+*4EKrZ*L`Spd1)+R^dj6{9wxM`!$8u=icn(_7c9MCU0l4|r9+8>xX^wJ&uv}%5Gd*H?iX1>_bru-_ZW
+MtqFacn+64()6o)J>o>`RMFYg}qYF!J$tyH=8m$H$B?S7FJZZcb5zh(uksW6=Ag;iW!=de7}#wp6hdB
+4Tsg+&?dqJ(={vUcqv!DaC*`K5)kyL8?#xl5uWHPJ8kdTFgaHfX7ZDePUijEf49C33s!UN@3KdWIS#z
+HDM~55V#}6uiybuL$r~d=C6CPGc`hd;`!FT<Z}O&jxiN@!W@c$YHz$mn{S*@q<guf{Vq1OL2mFw}8$C
+5Ft1)7o>E8nw0=V2Z%TDj)HJIo*Tje>$~5RmM1MwTAs8#X?fD}q~%G=la_;L4O$FZ3|b6Y3|j0WD>!f
+WJ7_s*IcPa)IcYg*IcYg*IcYh0)}+Ox#iYfe#iGUSJW@OxoQV(WzJsEy;LLb%5IZ=S6dXJV$}9sS8I(
+8#a53<Ng5vX_G(4zMjpv3>5U%bwXqmK3S|%-%mPyN`WzsTfnLI0LQM4#p6fKGtb+22oZbi$YWzn)|S+
+u-pdC~Hs<weVjXI-?oXmQcvq6IQ?JnwaD=O(Ck3%F$fgo2u~pdKtJnF@+^f)bXXrXoPhL8KD+*@6EY)
+E5NB0CDVO1<`N!i>#1i4Kk$xunmB9kkty3R{_HdAV%Q22VQ7^x&ss(AejK7#C_>J@l^MVtblF<f*BCO
+0N(}FEx>F6HVd#>z^4NI5=84kkRJs3LC_jEq$fz|evuV|yCC!mqMaZ<3B3Hkbq~DNz=I5YzQD-~JgmT
+D2^@gHTE}rayA;m`XX1mbZ&2JBoc#@sWCX=tL5euYgakookO&Nt)j^SMP^ub~ss;oq?k`csAMJjT6-r
+2gbbr8cf=D6oz5+BAV5oo;1>h!#;DZo62uFjkF>p5GxbAhkST|&a3vh#Ln1Xw2gIrQ@{xUdc9ONznG!
+>M{2X18Gz6Ax5L77uf>=5^ty5h6E1QB6yfnIP*H8?vNToM{wml4#z2PI>HuO8f66PAhsZ-Z+b0#q54`
+Nz4FO!zF_F9Ji^L6GhblIB4MDM;!D*G~j@Is}{{III~Q(+;q6a1=PG91RZG#JMj!!)NJ!5g4NCpae9?
+;0KA}Ai)^m&Y)&HD2@*5FM~S5Ad4Fmn+4ThL0MLu`%)ZymhKmUAq5}g`+|f{kQfOv76G^qU~51vf><w
+3rvO3WqK4prcW@a&oOh5y@oZ4gAC$HRMWaD6Xiy9qRNe)t?;vp+P_*Fq{w&u4X`yO403bnef1JDc5TB
+*{MQSK>gst-veIPniJOp?@z?cCK2t4`VKAzyHVZet1)Dy6p82iu(KFdpRZ$WVIDyS?EDsF?i*`Pvg`W
+2setU=~H$Y=)HoPbCMm?k*R9K>C-_GJ(FEZr}XM5;VEQ5c-Y3r^z&r`v+VFTpvMpd3Dc^g#(<5NZTyK
+7i8!H;u6mP2sb2zX%cO)gZ$%%O@a7qzD3J8j!dE6a-Of5S;~~LQwD&lqv=F)-mQi-u6p^ymnBb7F1dV
+bv;3{BB;L!=wpBsgLo>aTn&ozg3^?rbR(#k49W^)#67?5mjv~BK|x-Syb5Z{f&!@^fg6+*1?50N{ZCN
+96I9j&WhDXA4(dpPN{Sf!GDLiq?iWd-)FLQ^2+AIUl7XN+AV}>8>D?e{AEf1joO_Te50c<P!aGQF$Jm
+!p;j?tV$PX#vAPpO2V1s;WkjxBHjX|0)NcIKEzJR?3`Jynd!wIq=a}#sx9ekFTAT1N5TY}_Dz)}PJ9Y
+E)R9|t%$V6*{y4QOdVL<7(^iD3hL8S}iyi(djp3|LzL%L4Qjps#?M1VAc4P60g%P(TpK20?4=E*&q@G
+PmBrXX$>C8jg$w_2j`BvEV#daNH{>xC{y_g3`pGz%MMuDu<8Aa11Co1{C*~{Nl58zlaX`zJO&0)v7@S
+U{Hn<q_l$sQIJvz@+3hnAb`k092*>s3CMfQowUJc>3$I%&b@@kS}wmH&GDxH&<8@~g;(A*vtJxB;t)w
+t2_L|bIp@7@_$8bR2@ZG!3F?5b1y#{OU2ss>8I*qnl@~$cG(3G!t{@5t#ud<lSli#hXL$*Z2Lu`UprA
+h}&<_gqgF5`6u0CL%LFIf<>mJmz2Nml<ZFf+q9n_h{+WrncOZSURLG}ZR6)>iNqXYmU-~>U!E~xGe3L
+S&`wV(hks4feNv4YHbtnKgMvvj|R6Djy0OC2P|f*e(l<O#AgL7F8<mIMerAeRA$446^CRD$GLtnKgMv
+vj|R6SO~IZ9$=GfT4l{(V##yOjH4%!Y{}z-vKp5*=A6u8PsXU{Us0hEZr|MMU`eyR~eL41|^k26=keV
+k7$lyQT!Nwl}b4`NEandK|MsQyyq9cgdBE|J_&$$kTDK&0RaFGzmuSxTE|=d1m*V1=l!2_fGn;3WeoT
+%-7hjl<y}xF7nHgMg>6BNOOUP%Xn#;G78HdArC>n;SinJpBCnuQCSuz++?AK0_A02V3d*R0GOD1ADyX
+0eE2tnX0a^;Gq=FKt33v&rpMs=ZkWh;_?(yQ6P%0GE1_hNsLAgkPn!{5gxSkRAYez^I^*lipPf%VH<m
+-d#nTT!Qa92pS@%iBpXoQ?WvVwY+pl~H9TnVaDBCG|pc!BArRwSqp2`WUwgFf_&UlR6<LfB=K2!+S-%
+V8mVUMpV?5CNn{At*%%^6Eh*JV+%+ta~2tOUPaZIl!<Kp?o!DFPs7&gX}?210oub%g~?n<_mrDQ1<Tq
+X7MZ_A;_Eune!lD9&-P3%8){G<Kfqk=+eV&12P+=xre?p>7$4pzu}Gac#%Pp+d*<WNJ0l0+aMPj<duU
+IaggW@a<@T3Hb}mP=TG5m5q7?luXrObL1s0`tOohiAb}dBOM_f!kOmDhpg{&S$ae+_&LE>1q$z_8Wsr
+W1JBf!Q()}WXWEq3BVvqw2GJiojFGxoO$+jRz7Ual+d{~eV3ld*Jx+_R+#a&E|BhvjMek8BLOGvPNL8
+2<iQ3c7EAgdH)m4c*EkTeSNMUj3OIWvbd=RLppC1hU$)E#74f(%QLq6m^JL2@NXt^_%hARQ8<LxTKAk
+kklL89^!|avz4p5$S$0l%zC*97d3x2yzlZP9n%j1WAY>?+_#vf}}!_R0vWDK`J3gAw=%Wt?)s*UxbK^
+L69N{)4>LZqzGa&e&3l5rUC=p2jD(n_5rRBAbsR@kC(&7#+G3C0J#UqJwWaOBo82Y0LcT89iZm`S_f=
+8fY1Si4$yPdJ~V~<@e<JIfIbKGIiSx`y6P+*2s%K|0eYVLFUA)H4O34Bmp*jqZ+{)nqx(gs^j<XG8X1
+j&+FTW>PE;$YGt?CBcQ$UgFWnrqi{SA$&}V!enSw|ML^>eS0g(=fbU>s7A{`LvC<i^=>NG=^%3kI@Ui
+=cUbbzG;EFEC!D8KA-|3n`M7Hm3T(*c_f*mS_81123X>8Q;K@GZJu1PeADu<3wJ2W&cE(*c_f*mS_A1
+2!G7>3~hgzKO~OMXG9F=8137{UTie>hO9T{YfZwK&b;t9Z>3kQU{bepwt1S4k&d%sRKG4cXAuwqWeXr
+pwt1S4k&d%sRK$KQ0jnE2b4NY@d-RY)d8vwP<4Q+<9goX#V-L>2dFwg)d8vwP<4Q+15_QL>Ht*-s5(H
+^0jdsAb%3e^R2|nYa>bA6evu?l^;90<)d8;#cy++51702Q>VQ`VygII({H3kUC=aV$lz_v~{UTEk?0{
+TPU3KtvfUg659pLM@teBjBn2z@6_)i21TN>BqlQ<0BFX9Bt4p?@;vIB-4FzkR~2Mjx4*a5>17<Rz01B
+M+i?0{j%wfQ7&LidYIT`mPO1<MW?cEGR$h8-~MfMEv=J7CxW!wwjBz_0^`9rxuF_yyfBk_5{RSa!g&1
+BM+i?0{hh3_D=h0mB~Q^ARIJb^x;DGBLf2?lAw|Z_89dwG*nHQ0;;5nxO3jZ6`=OLD~t@PLOtjv=gMA
+AngQc=l!Ki82y(7Z6|0uLE8!1PSAFOwiC3SAngQcCrCR%+6mH5kamK!6QrHb^B!;eB?;P|=$M3WCwx2
+M+X>%J_;$j#6Rw?}q(c`aa66wi^FJre?zoWR*#vSYkUN3g3FJ;7cRrnrMaxCYMa#tl7cCYo7A+Pn7A<
+Z+isIP>awm{Gf!qn?P9S#zxf95pK<)%`CvZETo@*-pnlwwACC%=*kmA{dbSI=cA>9e-PDpopJfVEQqI
+vlf&5NhnZ_%u1Ry4c)b&6*b+@0X=1a~L6JHg!v?oM!bg1Zyko#5^ScPE%T!Q2VvPB3@gUy6wle@Sq6g
+1Zyko#5^ScPF?z!QBb&PH=aEyA#}<VD1ESPeSvAb?052jOp%v5h9p(!n_maoiOi&c_+*}VcrSzPMCMX
+yc6b~u<nF)C#*Z~!{=D-?icxic_+*}VcrSzPMCLkv<uUgfbRr+C*V5)-wF6mfOj4adLOF9R(HS158yi
+i-wF6mz;^<^6Y!mY?*x1&;5z}|3HVN;kHo`Ioa;Px(I{rR`$c|mP7*tifbRr+C*V5)-wF6mz;^<^6Y!
+mY?*x1&z&iondF)FIG0xpDQUmx-z;^<^^RQ4$C*LO&eIPd=@C1P;2s}aH2?9?Lcpf*8qIfoO;}e^jAn
+*i%CkQ-2;0Xdx5O{*X69k?h@C1P;2s}aH2?Ec%7z$JTk|6K|fhPz&LEs4jPY`&5z!L<XAn*i%C-gg^-
+wFLr=yyWB^St#A#j^?hPUv?+zZ3eM(C>tPC-gg^-wFLr=yyWD6Z)Od?}UCQ)H}~zw2uMqevumJcS64t
+`km14gnlRVJE7kR{Z8n2LcbIGozU-uekasB&)cu2cs8Nm3H?s!cS64t`km14gnlRVJE7kR{Z8OfLcbI
+GozU-udgpoj)fCSr^gE&73H?s!cS64t`km14gnlRBI|1Jb_)frg0=^UAodEAV_m}BmXS-iy1?HW8kw7
+_7C>p?b0=^UQoq+EId?(;L0pAJuPJnj;ymRdQJa+aa0pAJuPQZ79yA#}<;O+!>=g@~<{*~rP3z$2>+z
+IARFn8XU7Gh_+U!(@yo#5^ScPF?z!QBb&PH=aEyA#}<;O+!-Czw0I+zH{%y9ge`+WjImknV(ZC!{+e-
+3jSVJf(zmC!{+e-3j5&;qZ}`eTfCj2<Eom_Dj-hg3CUTADDN-yc6b~Fz<wUC(Jux-U;(gn0Lau6V{!u
+?i_KCxBZfYc_+*}Vct1>F!BTQPMCMXyc6b~Fz<wUC(Jux-3jYXSa*(HWQ95Hevu!TcY1F@`Qiu>;5z}
+|3HVOHcLKf>@ST9~1bipJI|1G~H=o3kz9ir~0pAJuPQZ71J_b~g(C>tPC-gg^-wFLrz;^<^6X2Zy@4O
+F(U_`rLv<=`p0pAJuPQZ5pz7z1BfbRr+C*V6_-U;(gSa-s@6V{z`$MYD`?icxic_+*}VcrSzPMCMXyc
+6b~Fz*EYCSh$7_a^~-5-TPVV$OY-8~fS)B0u=csbN+8VczMX_WLKwA)o%0K9C#0cLKf>;GF>Pyf3%He
+s;ge58yii-wF6mz;^<^6Y!mY?*x1&;5(-ikK6#hCvjmyy>o6piOKAKjlrA;Bnj}HfbRr+C%8Mo-3jha
+aCbtu6T+Pk?u2kBggfVTj~BlL(w&g*gmfpQJ0aZ(<W3-W0=W~&oxtq`ZV&e$ggYVJ3E|HB%cn4zFA3>
+RNOwZI6VjcK?u2wFq&p$q3F%HqcS5)m!krNAgm5Q>n?o%2OYv-iyA#}<;O+!>C%8Mo-3jhaaCd^c6Wp
+EP?gVoum^;DT3FgkVFZIVzcE3mxxI4k!3GPmCcY?bU+@0X=1b651?U5ia3-k_rqWA=O=i2@bhO+xbk^
+tWc_)frg0=^UGo#5^ScPF?z!QBb&PB3?Zxf9HtVD4OTj~BlL+@0cXeSUCvg1ZykosjN?bSI=cA>9e#P
+6&5GxD&#i5bj*NXcR-){USs_?gVltkUK%!3EEE3c7n7Mq@5t`1ZgKoJ3-nB(oT?e-d~c6q3nJUBG7h%
+wiC3SpzQ>0CuloC+6mH5kamK!6QrFW?F4BjG&^G-Zo*K$Bvd>7Haf^jLbcP2L(3;1Ng(Y6X(vcKLD~t
+@PLOs&vlE(~vFj1Bk=-vs1l3Ncc0#o$SSCT+3EEC*b^@{!kez_+1Y{>5I|11V$j;bt7Ur@0MUueS3C2
+$Fb%L)Ge4XJt_!#5{UY+pjgjXlLI^op`ug;rSV;;L-<Og1z@ahCrC#X6>)d{LjP<4W;6I7j`>N$M^a-
+;qSOy_;+F-EcbMScL(37byXbi$?+ES+HK1WPAGIw8^tiB3p#LZTBAosj5^o%hEmcE89EL^>hT36V~Sb
+V8)l<3Q?gh;%}v6B3<}=u8jEFfHKdyf2l;9=;@4I>FKjmQJvAf~6BIonYw%OXvPV-}EOsLX2S236oBk
+bmr!Wn8NNCA%aaOY&v1n37byXbi$?+Hl48PgiR-GI@6m%94cxEFrB&cBACMN7a@X8Cu}-l(+QhS*mT0
+C6E>Z&>4Z%uY&yZw364&1bb_NZ_a!Kp!k1xK2b)f>&;%wXfI8E`=nd`vq$6YsN}W*Z^lCgqCQx<e&c9
+&*d%Or1s5(K_393#|b%LrBRGpye1XX7`zsOYD8?pqdPEd8`zN8KV*!?0?py~uwCzLv&)Cr(Y0CfVW6F
+{8+>I6_HfI0!x36oBkbmn!>FMbJZI$_fZn@-qt!ln~8ooMb<Q^2MZHl48P335#UbpoanFrD|MPFTE`1
+W+e{I$_fZn@-qtf~6BIoe=4SNGC)(A<+qmPDpe@q7xFGwK*ad?`1-G6D*xz=>$tBSUSPd36@T<bb_T5
+ES+HK1Su!vF`<VE;>y}}uNb=?FOnn{F>E)!&>hyZEYs>f8gbH=sZUjT3XjsVr)Aya#V-LyConpJ(Fu&
+s@=H~H0`dcqPKb0uq!SXIkm!U&CnP!{(OG$q7rz7|oe=4SNGC)(A<_wvPKa~@qZ1gNK<ETQClET}&k2
+7{_;c32tQxD<{USeb=!8Qj96HO}9qb$-0!Ak=I)TvX7nJdubHC>0KXkD3WLUMA1WTV-yo60BY&v1n37
+bx^bb_T5ES=surBex;PMCDUq!T8c_a!J;weI)O3H5J$9>D@oCxAKu)Cr)@OHYX<fKn%vI-%4FrB3K{U
+c0^oTh{%aew5}27Jxbd)Cr(Y0CfVW6F{A?>4Z%uI6A@6364%kbY8c8qwX~!(g~4Hh;#y@6BwPq=mbV5
+Fgk(J34~6yqp}BtP9St%zQ}FgsC!LdbONIj7@ffA1V$$?I)Tv%j80&50-+NKoj~XWLMIS9fzWw>nKTx
+x`$e39(Fu%BU~~eb6Bs=eXNYt{q!S{YqDe?}LZTBAosj6fZu>^vYeJ+GBApQFgh(eaIxoe^w?UFFMW!
+XGfY1emE+BNlp9}t6&&?6BTQ3ENE;w|-p$iUOaOi?V7aY3a&;@@k_;bOZxp<79Xm-%-pxNe#n5^#ir1
+?p6uJmK$7DT!r(gl$&h;%`u3nE>R=mJ6)5W0ZS1%$5q%R#YL-S6;qHQ4kq=wr~wpbt)<qZ$e<U0~?~O
+BXo0z|jScE^u@`?>K<E*8)oySh~Q{1(q(bbb+M{EL~3@xM;a};G#v)!j}tL_~@M{!!mWhLCc_J&@yNl
+v`ktiEt8f>%j8)}3+Fd!k+eu!)IGl!>sGWZS{5yfmPN~=Wzn)|S+u-()<uhp78flpT3oc)^|TnJ?)Re
+QMN3cgg_*Ga9ay@+(gl_-uylc?3oKpW=mJL<IJ&^mb?nQ5FiI~4mM*Y#fu##9U0~?~OBYzWz|sYgE{O
+DmrxrN6z|jScE^u@mw|%4TwZPH^mM*Y#fu##9U0~?~OBYdY;lC7Ey1>!}jxKO?fujo?UB^D;g$e3@5g
+o8}L8Qy`)?QD8r775S!KMo~U9jnbO&4ssVA2JXE|_#3w|%4TwP4c)n=aUN!KMo~U9jnbO&4ssVAFMY?
+??I(njt}e=>kmGaoacQUJHC&;NyZ-72Z~1g36t`SI`Nyx8i{)r0GZyBPex2r|Y=65p}Nxr7kFSL8%Ki
+U9jnbO&4ssVABPgF4%OzrVBP*FzJFx7u2K9eQ5yp<)vWL1)DC|bit+zHeImkf=w4}x?s}<n=aUN!KMo
+)T`=i_Nf%7oe%mi8*mS|B3pQP_>4HrcY`S361)DC|bit+zHeImkf=L%lx?s|Ef2km*rTayGVABPgF4%
+OzrVBP*u<3$L7i_v<(*>I@*mS|93npDK={k3Q9@EnOA~mq-f=w4}x?s}<o37KFQ|yC<0#Fx#x&YJ#ps
+sW4LjDl<c-t>2D0M-p3rby3>Vi@il)9kQ1*NWC;%Jx14XC<6)di{^%h7FKjX~*t5h8eX!K({iUGVCHR
+~Njx;ME1ME_ijps|#K|r-Q!yhi3ac83v{MMTmgb1+*@pb-}9(US06&f>#&3y5Q9XuP%6X!K({iUGVBz
+agVqCk^){A(7J%u1+*@2H-oAa<hmf&1-UNBbpfplXk9?-0$LZ)y2g2rxBZd=UKh~1fY#-gJmPv5<hmf
+&1-UNBbwRERa$S(?f?OBmx**pz#y#HlOA3Nr5bT0n7v#Di*9EyQ$aO)k3vyjhvw~a~<hmf&HGD5*Y3w
+f{z;1NE$Q01J;ME1ME_ijps|#LT@alqBmtXOQ4?XAp9c_^%$aRfR^V@z&4c{4=@;Xa=?gC#I_`1N?1-
+>rub%C!7d|lw{0$&&Sy1>^pHaEgbbiarb7`wpO1->rub%C!7a$S(?f?OBmx**pDxh}|cL9PpOU1M`23
+`F;fBtftXa$S(?f?OBmx**pDv@W1^0j&#YT|nze3{jBlf?U`A<vkdPmx5py1iK*D1-UNBbwRERa$S(?
+^6PGZx&^*2@O6Q&3w&MR>ze01Ui=a;c7d@Aj9uXC0$-naj0M9k7<R$13x-`V?1Etz47*_11;ehpZX1i
+x{US+f7Fc${vI~Y?FzkY17Yw@~*9Fxn07(&Kl%AKCL%lP%UyU*7evu@lX1^r1402tN>w;Vt<hmf&1-U
+NBbxo%hk%3$n<hthet1$-MFQNmquIVMAWh0vV6Y>MJE}(S*tqW*f(^*1pfYt@HuKTiTj6wH{`~a;BXk
+9?-0$LZ)x`5UNv@W1^0j&#YT|n!CR~Njx;MH~WYK*~4!K({iUGVCHR~Njx;ME1ME_ijps|#LT@alqB7
+reUQ)djDvb?Y7KUJF!Rpy~ou7pS^G)di|9P<4T-3sha8>H<|4sJcMa1*$Gkb*)_&gbC<=5g1T)fvO8s
+U7+d$RTrqbK-C4RE>LxWs!Ic*Dg{gzV7k`MlcD&#Ujzm~T>$C=P#1u@0MrGbE&z1_sB3u-Li(RHLt3E
+J1)Z+7^JM7!?iZ;6P}lPFT+~QpUr_3TQWuoEpwtDWE+}<DsS8S7Q0jtC*V=VKX!`CKsew`#l)9kQ1*I
+-1bwQ~MN?lOuf>IZhx}ekrr7mLjf=*ZLJQ+&<rJ&RWr7kFSL8%K$J!=1F@K5qj@ej=r8BleBs%xL=PY
+>YV<HaulUKh~1fYt@CE_ii;stZ(Ipz1*^T=43GR~Njx;ME1ME_ij_yc(6>{US;5>Vj7nyt+Wu1*$Gkb
+%Cl2R9&Fz6Ysd-)djCEcy+<6>*m#H^X?aMf>#&3y5Q9XuP%6X!K({iUGVCHR~Njx;MKM7Een4~vwdC!
+ZQlKIg#|!o0(1&|UEu2iUsu=&H586A{0My@Sm5gdUl-)MVxN~rk$1ny6a>2<*A;e-OhK*-a$S(?3cYE
+cfM9{I3w&MR>jGbw-^oS4_$6TM0%I2#yTI24zAo@}fv*dEUEu2iUl-)MAlC)CF35F3uIs*p3ibU`!0Q
+597tp$Z)&;aKp!KQ82)QoEbwRERa$P{{0$LZ)x`5VoUy_gd?tT#`pmhPQ3us+H>jGLA(7J%u1+*@pbp
+fplXk9?-f>)Qf03G{$7Mi>JMVx@v1+*@pbpfplXk9?-0$LZ)x`5UNv@W1^0j&#YT|n!~?N_6@yI&*;U
+S06&0#z5Nx<J(hr7kFSL8%K$T~O*uCkT;AeIQo)z6=J<-TfjzQ0jtG7nHi7)CHw3D0M-p3rby3>Vi@i
+l)BR1kQ?Z9-Iu<hxw~I<5|p~2)CHw3D0M-p3rb!4>JyuL^@)8TJn-uB(j&lS<*oxkb9cW;641JU)&;a
+KpmhPQ3us+C-N^GH*QG*2#}wqcfYt@HuG)1VXzrH+UKh~1fYt@HE}(S*tqW*9Aa4b^F35F3t_yNqkn4
+h67v#F?yvK`Q0>Lf_b^)ymXk9?-0$LZ)x`5UNv@W1^0j&#YT|nysS{Kl|?z&9$b@z)T0j&#YT|nysS{
+Kl|fYt@Ho&ethS{Kl|fYt@HE}(S*t*dVPM%`-xuM3)6<?A9&P`(1mmDmefRYECfN~tHM2m~r+(15R9H
+?KxFcfUxIrckSf;xE)&sos=JML{WW+4l-Md?W~7J&dwz$MdM=?iX<aS{Kl|fYt@HE}(V6s|#LT@alqB
+7reTl)CHw3D0M-n>%IgKwfs^5>H<&~fVu$G1)wefbpfagHeImkf=w4}x?s}<lP+*{furl%@jQCC`$cN
+N(gl_-uyjGB3nE=!Uan^emM*Y#fu#!^UEt^fM;AD{?k@#G^}bB!2sT}?>4HrcY`S361)DC|bit+zHeI
+mkf=!?D2f%azrt8}0hfuveUepnQx&YJ#pe_J)0jLW=T>$C=P?tv8Z33V!0CfST3ou>R&Xb{ayI<r7Kw
+Xy<s{D!O2oaRJpwtDWE+}<DsS8S7Q0jtG7j(L=JD#WRwV>1mr7kFSL8%KsU6&K&V^Bke8vyD8P#1u@0
+MrGTu4^lwEoaod7L>Z6)CHw3D0Km-3qV}}>I*=90jMtk^#!250Mr+N`T|T}fa&Xb>mBM|F97ugpuPaq
+7l8T#P+tJ*3qXAVs4v*`1)IKL(-&;|f=N$c*9Dlqp10ni?)8FAU$E&5HhsaSFWB@2o4#Pv7i{{1O<%C
+-3pRbhrZ1TE1&O{O(bx0VJJh{i5a|mTeF38{VDtryzJSpeF!};UU%==K2z>#eFCg><guZ~#7ZCb-ZjO
+jb?S2<67cCbp7cGO9LCc_}7OePO&^&lx_@6Wjni1pI?UzvZdV!@cu=E9%zQEEKSo#7>UtsAAEPa8cFR
+=6lmcGEz*VCyjS`;mAd7$p~dOD7xWzn+!@0#u%*^wpM&9L92xDnTq#|30PpxMZf94VyG{2gultK{&U?
+%vGe13Ov7$7J@J={3`9rq@ibnO-wJR(g%}8tF9}nU>I*Z{F$WUehu<Eu+&iIxVBqGCD1z(=s|Oqth}v
+EuqsAIxV5o5;`rR(-J!K%{%?vYg$I9Wpr9br#*DqL#I7-+C!&3blN|s{d3wsr~Px<Kd1e3+CS&}vg%H
+xf8#ba?V-~iI_;s;9y;xz(;hl}gKj5;w1iGe=(L1ROX#$EPOIm9-v`o(^l#iorxkQsL7z_UX%C(D&}k
+2y_Rwknoc7Ndj?mn+e@^@7w13WQkJvf%Z`?+wJ#<<@rxkQsL8ldTT0y7Pb6P#8)pJ@sXE+mo`H^0&k(
+uYay{FGxqtkcs>FHqjt!xFIR?uk$omS6j^_=0}Fj^zi>N%~R)9N|jmrZu&{2RB?X$75D&}jvoR?uk$o
+mSB4i(*<mpT6*={d3wsr~Px<Kd1e3v_9WS^IzISr#*DqL#I7-`Z|#I&}k2y_Rwknoc7OY|D5*E(RM!l
+k%mY5ZtvfDNm@pyWpr9br)6|nMyF+TT1Ka3bXrEIC3IRsrzLb+LZ>BkT0%$b^PMmM#_g2TGCD1z(=s{
+&F~UFe>J3pF>9mnfo9MKOPMheoiB6m7Xy=_yk$>YhKkO9MbA_~#P8;d8kxt)V9KNEp=MQN&oz~K69i7
+(E5syP-q+RFJDe`aJ#;LV*T1%(3blOOt!;dyot)<giI&GxWCOU1R(<VA?qSGci+Rxi}iu@b5aS~q;+i
+<p)PHXA3mQHKwh!pU!aF?1Xar2v{HqmJl9p$^dPoKADs*QBoNT-c-+DNC3blOO#jda>brzLb+LZ>BkT
+0*BKblN}1`}wm@jQ`ReI_;s;9y;xz(;hnQq0=5ZYzp5~v$TXxOX#$OPD|*te@^@7ct3C7iScjT#;HAY
++C!&3blO9wJ#^Ybr#*DqKd1e3+CQiLbJ{<r{d3ws$NPEvPK<x!Hc9QFPaD0oj84nww2V&6=(LPZ%jmR
+(PD|*tgicH7w1iGe=y<;muM^|nxSft#MyF+TT0y53bXq~D6?Du2o10e8Y3rP}&S4YUp0>t(tMC0i4u9
+t*X%C(D&}k2y_RwJqu%6+jWpr9br)6|nLZ>BkT0*BKbXr2EC3O6L<MzCyWpr9br)6|nMyF+T%oD|p;R
+v0OT0*BKbXr2EC3IRsrzLdG?|XcEUb347Jp5?CxcK7d-(K_^#MxM=k8ca=w3|-5>9m_pJL$BOPCMzGe
+SPap_&08Y)owcNrgQkXM{ipT>a?3qyXmx>PP^%}n@+pwuvubHR?}iS-{-443I2`SbhV&PyXmx>PP^&T
+hBqy!(}FrJsMCTvEvVCiIxVQvVmd9RbN0Nt^WWdNovd0=rv-J`1DdcM9uXdbMyxG$+ES-2b=p#=Ep-m
+Jx0BTO`RY!6f8#b{?Ud5;CVejvrfTLLGTr=Sp91a!Yw&pnJEOF!POIvCpRev*_cw08nm#<mjaaMdw5m
+?4>a?m(tLn6>POIv)s!prwuoW`DMD45d_0^r`{>E*@+E=H2b=p^_eRbMbr+sw}Ph)Rj`|7l>PW$S#uT
+J~ww6D(BS9gZ{m-f|ZU!C^VX<wc8)oEXy_SI=$o%YpfU!C^VX<wc8)oEXS!m)Of>dtU~<2Ft$t<%yvE
+v?hi`h;85OtraAo9nc>PMhnrxlWtww7E{3>s-5@tdrZ{xJ^<U?6kQ~o9nc>PMhnrxlWtww7E{3>$JH}
+o9nc>PMhnrxz4xgT_?A{aT}sG*lBZ}HrHu$oi^8LbDcKVX>*-6*J*Q|HrHusotD;VX`SnPee365)ABk
+kt<%yvEv?hiI_;~|zB=uz)4n?GtJA(Z?W@ziI_;~|zB*rD-C69PU!KXAi(b9?X=$C7)@f;-mey%$otD
+;VX`PnVX=$C7)@f;-Yd?S13G825UZ<sXT3VmJv(LR3rx#Z*?p}~{{_gF?-%;CKr_FWRT<6<#sngcqxL
+t1_u0C9Jj3%k2by`}drFB|br+szWSEqe-+E=H2b=p_w>#I9Y{f*oFw69M4>hSsMa045nmey%$otD;VX
+`PnVX<wc8)oEXy_SO0N>dsF8PV0-bv`$Oww6so3>$J2^OY5|>PD|^wv`$Oww6so3>$J2^`|5msbtk63
++uJ0yrA}Mww53j4>a?X!Tk5o>PFw1<rA}Mww53j4>hM({oH6&rhH-md(pQAEs!prw+&c~R>JJwkqnV<
+w4$oGDbu&=sqW0BkU!AY7?sW7wZZp-sI_;}Zc#0dWmey%$?LBJK)#f^Ft`#1%My&O9T3_c`7wJs&FRi
+iD8au7A)A~BCuTMLow8Kt2?6kv9JM6T+&cnYpU9GRv`a0kDfpjMN8@It~jh)umX?>m6*J*v7*4Jr$oz
+~ZBeVx|VX?>m6*J*v7*4O#I52W+Y-?*KaT4Sfpb=q8a*N*;Quijv_zE11ww7x#!1~y&ou+t7Z?XdHF=
+TZIKYuaU}U3S`Kr(Jg1VTV`zcGzi$op#u1hn;rVX@?#5kqQq%Q}umGe&?URaht9d+G(MkcG+o%op#u1
+hn;rVX@{M5*lCBIcGzi$op#u1hn?qpee365(=I#hvePa*?XuGjJMFMf%e=J6PK)fc$WDvww8&12?6kv
+9>+5{q2hu6$Z`{U-Z|4usVB^&KI<2qM`Z}$z)A~BCuhaTEZLZViI&H4QtoVH&NavV;X@i|M*lBZ}HrH
+u$oi^8LbDcKVX>*;H)@f;-mey%$otD;VX`OdHwsXwixD8QD>$J2^OY5|>PD|^wv`$Oww6so3>$J2^OY
+5|>PD|^ww9fZE4V_v3#%+FDTBoITT3V;2by`}drFB|br=@k;SEqe-+E=H2b=p^_eRaORx--k)xQ$Nx>
+a?#;`|7l>PW$S#uTJ~ww69M4>a?#;`|7l>PW$Q;9-HsHx}SSZ%j>kXPD|^wv`$Oww6so3>$J2^OY5|>
+PD|^wv`$Oww2)0}&3rv#Cy>8!o0>LMq0*pI;B7naWBVTT91hr*Hr{md&WN-Bp3=K12xqq6=c_wy{0pa
+MoUU*p!s{C^W4t!;%E8pn+{rx2l*g>Z?>oQN;ni%NtkcHdxXlmI?PGM>T&F*f{lA7NTSN9(orYRur$u
+(!VW%B-+F>W(>svqfns(V~mz{RmX_uYW*J*v7*4Jr$oz~ZBeVx|VX?>m6*J*v7*4N2?4^iidzj2!+zU
+*UfTjSLFI<2qM`Z}$z)A~BCuhaTEY)eK<(+)fBu#@lgt)F`hZx7mKr(Jg1Wv3l>+F^$dyN^TKVW%B-!
+oDXDKho<qP%X0aeSdYQhQD$7fd;FEc3Nnsh4wl7cfI9JSKI8g%}(3ww9QU<%(>A`Rle7^e(p7GwbNER
+ZMD-@J8iYoB0DXz(;_=9veP0vEwa-hdwy-QdpTT+%Y9AmXJ7lL!{1$8vj2JadHA*2?j>We0XW;u{n~2
+x(p+-g;kv_hhwBd4-8Ds>DS8|+M)MVRzHZNb*y8N>`z{$xj3!1CqlwYj)2?NAFS#Y`l=^L}(0QXrF=M
+a^-Osz5J7btJ%ov9;4r3g~I2`|Q-NSVc*F9Xf`w@O$*xnhWhdB&%G0??87Xw`kbTJScOSTK|B?DdDh>
+PpqT=(X>?61>GyqDi`x_jWSy?8Gf<7SMTF>c0q7~^4#hcVdlxSe<}x$eVt*&w&QcrUr`0&nM)9_M9@m
+oZ+(co~C@(Atmpk|gh6Fwo0D>`ULKyq63R1_%RW=gWSswtr%vFwp)l0}<3As6$YPptd^iC0iX5&>^5~
+d;c2(It29lICrO+?m5%#q1g!bH>8!#X?`>NHw;8thqMl9ZJ*vf-?$OtMi5i>hW!oM{65{?38#l4qeDi
+AjPAaRLq>;;&hF-G#vq$)7WmuU_l9f^*&MRjlHENY;4ptDNvBQo3BMFWWasP#!3;@c=j^{SBz0FOown
+2cGlP@YA+PLN`J3;JWj~PHKk?ZIew6TjyP^`_A-r?#zcLWvWe>gVgFu3Z1kY|wNpjoE|Hpqa3H2x>cu
+4S&;32_7g4x9MHzfFb@}AwAl_QYgA;Cj}ThRBC>+<nTCL#75Vhj>IyOkda{vMINn}l}=?-1UW_SQx&d
+&vM{0Ft}kYQoIZ!w}vfylwE$d3IOq;Zj_(1$n=+>pc5&7ne7e50`r-{*`&QO+H6CodVc8>c_62KapUO
+V3A;vV3A;vV3A;vV3A;vV3A-EB=Mx}cYibG^f)9~B$(a4e<Hym!6L!!_zycT{zQyLjOG0s1|Y^_iWPa
+)`JS8ZclZ;zW#_t|$gRk&m}*5@*?0IS(kjv_W>+!0ihzoMihzoMikEmMnI4CrvcLOJ%(5b*BBQ%~s>r
+CAT}3uUHbpi?Hbpi?Hn;O<;$&v|*S!n-fb5SmB#{-76_M43VxN5?D<Uf<TfMtSJ=Z0wBC2}#x#{PE`z
+OXAvLdn~vSPj!X%%S|X%%TL4D^!gl2S9pnt+;snsWgrl7Icv;qNXk*&mdQnv9x^nv9xr0h3LWO_NQNO
+_NQNO_NP?F2Kyu<B(C4QIk=VQIk=VQIk;<HZ}nLna9~=(`3_R(`3_R)8CWxGy^81-i=PlsL80wsL80w
+XcggP$0VR8peCRupeCRup!==kOc^~4K{Y`&K{Y`&K`kkGUogF20?cSc*4O?uH;0AD@BS}C-TQmfj~Mq
+)3`uU;knCr2YjSIHYjSIHYjSIHYvxoF&>F+ZK5nGc&n8Wr4Vtu?w3@V<w3@V<w3@V<w0iFi6RY=5?p+
+|ICZ$D(6K~MO$)Jg>iEI(##QVRyCoQ9qTa(*EZV$OV<n}PJ9`bs~>mje__uU)L1`oMC<o1x;LvGoR_V
+BbG!polAze9M>?B73JG5{0nA++p2`8)ca8_otR5+}RyF$q4}|7Rc)%x<N>LxKwvC#3Qaxx>@J7IDAB1
+95gR@2jUc7d#~Rkl;gt*%It`NbupTph|JFKS+WP3I6UXrv~r-G@~%jo@;++%$eXJ#)lXmV$2?nzr%cc
+m~Ri^J%slV-ore5$n9aCJ>>O}*F#><cP?N~1rNDB<o1x;Lv9bbJ>>R~+e2>m#B<LY4-@OY+V0EhA*$b
+f`_2W-x!@tPhsYixdx-2IvWLhXB72DJA+m>w^^np-N)Hq3A)tqVp6u(5IT<_z^&+SjLA?m-MNlt-deI
+&71YV@|Vs^cL`!@{0o^d?-7g4?DyEk_;aFN!Fv|gl@UC9q~(=~g^XhilRvKNtM^R?f_JbRJSi<DlZ^!
+m;P%<14oS})RiG2dPU^&+SjLA?m-ZuiaB^S_JSU3>os=tV#;0(ucpwu1amexv`L^X^|UaqrJF8fm@uJ
+`vQ5pkB<g7XkeqA_965(2Iax1hgo^Q8*pE2<k;pFM@gz)Qg~A1oa}Q7eT$4Sj!~l+p0H07g4>4>P1wq
+XV23PXM<G}@9)(`ZZC3sk=u*hUgY+A_GcJ~++O7N+DBs#MqV%SdcFI5b2uHm$n8aLFLHa4+l$;@<n|)
+B7rDL2?L}@ca(gkcUgY&AuQz$Uf19+!>EKOnZ*t3K-M^d2-bD5$viEPdnP&{rdUHZ>lhT`%-lX(qPQ3
+{z8yo*8zq36KLA?p;O;B%wdK1)}pxy-aCaBewv%k(aDZO{Eds2Fn()E?Iv)bWo@Fuc1k-drRO=NE(dl
+T842;M~YCbBn?y*VMcnNDv4dK1u_fZk{CO+SF$KQRVDy$R|~P}!#bcN5f`pt4>ddl;ruHWL4pUi3XTM
+D-@B_jfLEaXNSt*_+6+SLu%pNB8~s&BS_>)|<4lY5VVHV!fGIZvuJ~(3^nX1oZyS1uo78Z-ROgRJJJ3
+J|3j?e)s<wXrB(!dNaG;r1U1G_ued0dXv&S-@V~vknOmCH)*{|>rGnuruLn(Zz6m1uslST{jYxyQ|v=
+jt1~BO|Cf){eD{S@!H4iZg!du558-_XFMCq|N;cg!od0E<+5f+|WB?NUFvUJ3_tEC;;#BY<!ECPldr0
+s@g4y`^_mJR+6M~0#4rl+Gfk^N}f*<DDhtNKR_ThU5_{HmC2=7C9AHw?(-iPo$r0U_xdiDX?JGDn6yb
+s}hn64f|`w-ga+Bxm!Z15qx58-_X??ZUm{r2~e+lSmf%&rfSedq=DFif!zd40(1LtdZnT;S$xu+nqd?
+w;%oy7!VH3GYLAAHvHXv%iP%K7^M|Wq%K|>qBTCLi>Dg)oxA(A9DMU+lSn;Y4`6TVtZQtklTmcKIHZx
+x9rdQdzf7x^7@chHlF@H`R)y8gAcPTdl~;8BKs0qHZ=XcMD``JFSF~*?D`T=w%Gl#A@1)bpf3S^3F!N
+s3*4L!zGU<zqc0hK$>>W)Uo!fV(U;lvC7UnVWaIE38;}29viXwDmu&84rTj|uIK=ZMo-gryz59VP2Jw
+8EQ(t2F63drZzQpn+mM^h<iRDWyck@zyhk6|1`I4KLc)q0JC7v(we3?^UV)+uwmsq~U@+Fopv3!Z;OD
+uOYQ+{oF9A?ya_cSM&FUfpK=F5x!%OuK1#=n;wzU1&Fhc7vN$>B>5UvjvcoAPVZ<B-Ri(%Dnamr%aE<
+iCXRC5$g&d<kQH>16NmUWr7$B=RMZZ@zoO8R1JZUy}KfO!gK3y(IHxCVffdOCnzq84?*184?*184?*1
+ncsJBI3*0p49N`149N`146pkkks*;Gks*;Gks*<#rn4vCA(qk3I4>39(!i=fi04+HTQhF;6khkYbqTT
+DckbbJ|JuJ`0J6Dn%<{dLdO0QBw@7;`zNelcqx*uquXFoavabg_QSY1<vbi%=pQ*ENBEAEE^XJPsVF+
+pnY6xlwY6xlwYOcL@w?JX8$-S4{5>gsc8d4fkns5GmIVTKh4QUN&4QUN&4QUN&4QUN&Wk>U0NNJcgLq
+J17LqPMrmwGuT3_%S+4YOv*X!hychk=Y{?+Muq*$mkX*$mkX*$mmt_g?DdoDjGg;A()Y0j}D+a(TGCx
+a__}_x|9+<;x`l41YfiFbpvJ?xUY}@1GcG7^p!V^ol*}d&wAL3^9fnL)-{)U2$D;U2$D;U2Q+qPruJ#
+R)bmf-ET9x|I0vTATy8|$P8o#GB@Hdz~Nel8*#YqH-GlS@ck2G9LCtU`g>&tI{g2|K>ZLt(VX9R$!Hh
+1<YJVIQ7&%D*Q@rU@h9JpGTJ`wH#h5Mpqqca8R%x9n}Kcyy16Ac*L}F|!!6;*pG{gnDBnLZ(6j%-Ko0
+{w4D>M2!$2<sz1)bG8}V}8m+QV<_vN}T*L}Zx^ALf3h{70Qj4(zRBa9Ko2xEjX!tqHdYBi|UpjLxg^%
+39W^t^D;t3j^@y&Cjt(5wE3OU59mfYkt2Q?T;8KTb+ft3j=1wpVTLdl=G+UJZIR=+&TCGo%%@8q{h~t
+3j;>wHnlFP^&?$X6RM<m_4!YVMr^4H4xT7SOa0rkXEc}u&TkT2CEvZYOt!QRrQkV642Q$s)_G$_&pgi
+Is^}#8gOdBsR5@3oEmUyz^PfB+8-gC;M9OqGxv9QdSW<p?@uuh83m^XoEmUyz^MVJ2AmpjYQU)hrv{u
+FaB7D4OIt%U+dT{!9Wshg4MH^t)hzSv-5{ti)ldizFAM`y%@9z8Y7nZKeD~%h0|a#lDnc~~)l{g=Te?
+B02B8{+YQ&rhPEB#@?6DB4L8xX|oDl4PjaYAyh%7=i2-P4|gHR1ZH3-!pRD)0rLNy50AXI};4MH{2-k
+W}czkgy3(uz<GLNy50AXI};4MH_yIt8Z&oEmUyz^MVJ2Amr4jatOG$04ZT)PPe1P7OFU;M9Oq15OP%H
+QJeU|AGNXDMB>})rgJLNpTNDTE)|>y&h>rs0N`LglZ6~L8u0y8iZ;PszIm*p&Ep0wDV^J+~W{bNNOOd
+fushK8f^*)DkL?K)Id@LNev`5kkmj@BVwtYKbzeihj>C#qm2ylgro+N8c1p&sez;hk{U>AAgO_*29g?
++NbmgF*#7IEF8=Q3^5OF3k}(J)BsGxKKvGkZGX2HiQiDqkE;Vd-yB}XCsyzzv1g8d^8gOdBsR5@3oEm
+Uyz^MVJ2AmpjYQU)hrv{vw-Ts!S_Bg~7ml|AZaH+wi2A3LKYD_F4sez;hk{U>ASlY5@Ke*I<F4d&=Fk
+}>$8eD2{sllbj#M8`>Xw;xlgGP;aRAOmj2|x`1HU8$$t|sW87=w5MQ3FH`5H&#508s-(4G=XZmKfBS?
+M|7o_lN}4&v$QLB0x~ls6nF!jT$s+(5OM928|jtYS5@bqXvx{6HowZ0I2ade~Pn03~FjnXTBe0Akqp%
+4G=Xz)BsTfL=6x%K-3&k3P242HD@+!=A^I?bvV7BGkeKE%%4Ei08s-(4G=Xz)BsTfM9m?fhk#;FbH4f
+0oD^bEgFy`jH5k+!f(kzk{50^>z)u4|%^{$$)4)yxI}PkK=bJyfGNFHB41$V04e~U|)8G?BIX<A%9I^
+>E4cIhb(|}C_HVxP`=X((~Cx)QYfKCHC4d^syA4xI_It}PFhirmP12#>u>12P9fL5H&?h`;Bdlb?NKM
+nje2R7Y-i7W;+7}Q`;gFy`jHOR&Q2SZss6q$3j%WnvK7=p^%$Jvv03~Df_Ik4&83(^Wi4G=Xz)BsU)2
+q*wG0MuN+y*G!mLLh2@r~#tpBB&VDU{HfW4F)wA(<%Hk7ukfJ26h_QX)dyP%|17W(?aBFkf%YO26-Cf
+X^^Kuo(6du<Y_Lli8#&0;w_#^*lA#=*-d;2UJpZ1;irM027Vg&Y2c@Up9X#!_-Wv$xd`Y*K*6Q~n+9x
+}-PD)h^*96-bQ;iUK&Jtn26P(GX+WpBm_)&*0h<PF8n9`uy;tP!y7$$fiy}_X<j$VR!A}D}%|&EErva
+S?befCl^dg`*)8I^lGY!r(IMZCucW*c?#G3|h8oX)nroo#AZyLO5E+$c&X>g{&nFeQ?I@4+WaQI-<T-
+)V0SUn0sz4qRG|ITOx6?qzTW00pop5`W?h|^S@yl=>0r-7a3Ue|A~{`F5Uf5$-m_B$Go!Jr0%8VqVMs
+KKBHgBlEKZla1k4fZtH)7<**4JU;d)L>A9K@A2qH<5*(27Vg&Y2c^1nNAU>L7WD08pLT3r@6h)&Ba+E
+@-)cPAWwrl4e~U|(;!cSJPq<RH`6KNG>Fq6PJ=iN;xzZQNxL{JM4sj*t)SC@PIHr1ylL>Jxk)R|G&s}
+XOmmaXdmnAG2{Fz6>}%V_Ss~OkP}4w7Q))WdD-cw?X>NkLZ%Xz&?IxfQ(?CoEF%85t_q(rcHz$Qq(?C
+rFH4W4>_ojzQ^yVd;B|z`Sg>2$XgEP%dHX){gm}VCNk(ht|)9`oV(p(-cFD`E`A1+@m8G}g_Sel1KVo
+HN44W=~D?5n}eSs}JGwI%Nq12D})Jb|SFmIhcFU}=D*0hR_>nul0oO7rkZlY392ZcYjTrU94+U>bmF9
+x@6n4X`x8(f~`tVj(^hG3e5uOM@;Ax-`3Dh`97H1Qlf(lxa|=L7C<usBG3iQ#sGxVdhkbX&|P7m<D1R
+h-r2K5kcu;NGr-TDAS-!gEGxSS|8GiDGjDH&mNPMVoHN44W=}h((HmFGScG^RBUOmrFjS{rZkw+U`m5
+24W=}h(qKx1DGjDH53AJn@ZWjI!%5+@k1&%cz%&5UJOs6{bh5XXw4zIcE)BXg=+dA|gDwraG`rl0aQy
+3^9{%p-k};S>c@Utcd5J8<G!WB3Oan0u#554oKuiNM&8zR;a83v{4b(JH(?CrFH4W4>FKLCC24WhBX&
+|P7n5M+!P7DUaX7?s03Oz~}aMNoK@-)cPAWwrl4e~U|(;!dt5?RD)5T`+$25}n1Y2L4kdN?ISo(6du<
+Y|znL7oPA8suq^r$L_PWllw$25}n1X%MGDoaVj1=Z14a<Y|znL7oPA8suq^r+N2@+WSmeU#3&UX<kwa
+HVxP`VAH(Y<@b5-aY$>?>Fn9%OJqT(0i6bP8qjH8=2Wn0z@~ZkTBH<g8n9{J{fzXmx@1770i6bP8qjG
+#rvaS?bQ;iU!gLBY4cIhb(|}C_HVxP`yK@9j@xT6wF{WqfJR-=`AWwrlO$aLJG+{agn+9weuxY@i0h<
+PFnqBC`Gu*=vRM2Tar{SZNgEtM{G<ehS`M$xK24@<aX>g{&nFePXoN0F96VGsuLo)HE!J7te8oUX(8N
+6wh61L3WZhq@@oN2cI#F=Kh#ocFu=eCC-p8KAAPrLVx(Z12y_a0#q-4m>R1&5esC+?lac2e5;V}BKQ-
+wB@E9%pYc@r0TNY8t3%pr#3vD8w`n(?CoEF-?i-Y_*9q4bC+A`Wu#)4Bj++-`966G735k=rq|Ovj56J
+&8EO6flUIN1U3n5((X#ZllrfJy7{|@%a=>W7{(aJ7{(aJ7{=)QDV#|-lW->COv0J;`{qy9mk4hX-Xy$
+9c$4rZ;Z4Gu#M&s~Ov0JO+9*Lxf|vv`31ZUE`#ahNCjAp*m@y7x9L6||aTwz;#$k-Z7>DB@u6wxd#dR
+;PdvV=se@9tlB7jK%lK>_GOk!D-FePD1`cVZv{PoR1H#g#DfSUns2Dll3FEijt>|t&OdKl>8#ypJiFv
+i0e4`V!x@i4|7?cusF*L}I}%XMF_%a<GQB=$HjW4w&<GRDgoFJpu;!WdzUFh)3jxNf*^xNf*EVVphx6
+w6G6EeTr^wj@?W2~!fLBuq(|k}xG<O2U+cDG5^&rX&_YY4-e6EHn|UBv?r-h7z14I7x7lhEF;_`;QDn
+A~7XlN~$Rx_(8yu#^1f^iJ<@{0Zamz#Ih)1O2U+cDG5^&rX);Bn36ChVM@Z3gehsxcW-)TC~QgClCUM
+Q`bn6QFePD1swo|2A^}SRmIN#bSQ4<L!qPeUGSKrvRhfFpXk@g&<aZx-f{HE)T@t#a;mhku0K}x(28y
+2}s>8P?@Wk~XL>6ySy~*!BpX3&F((F^RcY@sFOv0IjGYMxBE1?82X|~H3i%bMH32GAjBl=H4O`@UD?5
+*CLy3aAWg_tBJQFKY@lF%h-yL_?8L@1M30wsV+0Fzh(C16Rwl7J-vOMI_oFS#z0D6k}8Nx+h{pT!f4O
+eB_?@?fA$LYagziFHsymxL|}T@tz^bV=xv&?TWuLYJgH|Lk+|@9?rEs7X+hpe8|0f||rSC_zkum;^D2
+?>yrbPdJlsCgDuloj7<BdK7-11hXPac$4rZ;Z4Gu#EK~4Ov0IjGYMznJC=LNb;+x?i|TXG!!Se_KvOL
+Wc@pv@<VnbrkS8HeVr`TVCm~KkoP;<Dagw%+5{pcPJPCP{_6gcYaUY7kuX|HSYvt+W`(JJZQH7lZI|+
+8u?lR58-op@C_(|m_oxP2F&&VwXB@9X!lrSh^P+~chuqR<p!k)C7PV-p*>z^2d++t9|poBq*Wl@5k1V
+0IW63e1gaWdyzf}I3A33k%%LCpi)!;o9}N$`{4C&5pGp9DV%ev<j53BTtUluUTBCt***o@9Ubrl*9$p
+oBpQgAxWM3`!W3FeqVAVp){nCz*K`b`tC)*h#RHc8h8r)gFhm!cT&q1V5?#WX?Z@K?#Er1|{z!_5CXY
+5K!z%*pqh0Y97xX<}awu{wQe$q69<<h!PMbAW9~!@RL{sCD=)@lVB&oPV#pyz$z2LPlBK1eas0e{3Q5
+E@RQ&t!B2vpWC991iKS0MoP;>(eCGnJG7<75<VnbrkS8HeLY{;?sq%C<RTlgt)<22$PePo8I0<pm?r+
+Uw*uxN1<VnbrkS8HeLY{;?33(FoBvw5MaT4Mr#7T&g5GU>TvGMTrILxiclaMDNPePuAJgM?z+IGQDf}
+g~yC&5mFodi1xcGCII1rBF~@RQ&t!B2vp1V0IW68xmI$03{WlUVB{*h#RHU?;&&I{UeNvCu^Dlgdwrw
+<p4&gh2^|5(Xs}JPCdh3!d1Wb-oD-aS{uj1UBjHXW7L{6M;?wopkt|3Hd^vgggm(67nSENqhuVh?CC7
+f^Cn8+SviNixLY>MB6920U=M?Q<gnX*_Vg^f9>89<Vh@j65=GpNr;mWCm~L{e&4;}gb;ZW@+9O*$diy
+KAx}b{gggm(5^J4=I0<nQ;v~dLh?A~w{$!zvkS8HeLY{;?33(E0odh}wbP{Ww1U3n564)fLNnn$}CSB
+echovS0odh}wbQ0(!&`F?^KqrAtVuh2yCV@@5_*4nA1>z*?U|rw*$x0I;PePuAJPCOc@+9O*$diyKAx
+~nNlW->COv0IjGYMzXwOzhgYa+Z!c$4rZ;Z4Gugf|IqQoYIh{t|2w*d(w?V3UeX&K~!hKUr!b<Vnbrk
+S8HeLY{;?>DotxSmI2=nS?V5XA;iD)}*tmXmo$`Cu>cFHwkYN-XvBy32GA5Bvv>HViLq8RyYY=61pUG
+N$8T$CEee9DVCcEWfICHlu0O)P$sd$N$8T$C80|~mxL|}T@tz^bV=xv?zK>{-b5&qP$r>FLYagziFHj
+vmxL|}T@tz^bV=xv&?Qxu&i*!+n0gr2n+R$W)Fh}$P?Ml0K}}+DlOQHROoEsMF$rQ4#3YDG5R>kEe@9
+twBB)7Flb|L+O@f*PHHp<sf|vv`31SkfnS?G0T@tz^bV>JqmP9N#5y~W#Nhp(0CZSAXA(PN0p-V!Sgf
+0nP61pUGNi1Lzu%vrGOCpw>2w)Pxqyp2)cZCkbLMEY0LYIUt30)GpBy>sWlF%igOM3RRBx1#hP$r>FL
+Yagz31t#1mxL~fl}iGa1S|<y60js-Nx+hTB^8!94(EmdlK>_GOk&ZJFePD1!jyz52~!fLBuq(|k}xH)
+SV?e_p8bvpvEoFql3*pVXi0FA;3UCGf|CR%@x`;f<oJXUNfMGIBuPk;p8YJdSaBjmNi13tk|ZQaNRp5
+wAxT1#gd_<`5|Sh&Nl22ABq2$9o}JxTaUw)XtWpw^q)O6hJNdn>$CQLA2~!fLBuq(|k}xH{KH%(Y?X%
+wzA(op6U=qM27A=WIOTv_dDG5^&rX);BELIYnBsfWMlHer4NqYA?BE*6d!AgRa#G)m^NrICECkdy~wj
+f(pAW1@!gd_<`5|Sh&N$-A^Su8jaq9hhA2}u%?BqT{lk}6534TR~Zg((SB5~d_fNtlu_CB6Ga1Y*63S
+iU5dFNx(#0+9qF2}BZzBoIj;l0YPZNCJ@rA_+tih@|&>|IUIFv3yA&l0YPZNCJ@rA_+tih$Ik6Ad)~N
+6_L*NEJ%`&B)#AJcNUzm&s8rOXde>NiX;h15|Sh&Nl22ABq2#cl7u7)NfMHzch}O16(>TJ#PTH}Nn-h
+uKqP@kDk7adA4HObBne3pk|ZQaNRp5wz2E%Ff)gQ1V)>GgBq2#cl7u7)NfMGIBuPk;kR%~VLXw0e2}x
+4+J9fl^6NQzZ+yK@v2~HAgn1mz=NfMGIBuPk;kR%~VLXw0e2}x4E`I7}FLX^bvB_T;dl7u7)NfMGIBu
+Pk;kR%~VLXw0e2}u%?q<r%y3r>V6iIq!y$!RYcgLooIVvU(VB!Nf*kpv<ML=uQ35NRj3?*r{(!HEzh@
+$qLNNkWo@BxzfXeJ8(f!1u&&e^ZepAxT1#gd{0@oUi{3Cx?iVU_Bv8LU*)4ux<4Ax7+q@CAYoW7H8XQ
+ZG*Ix$i6eqyT79>I?=w3+7s`6p~jE&1%p`=KT`eZ?3<h-(%E-9NRp8xBS}V*{QKt5E;%{89rEwHytur
+%e7JnMWT0W7VW454X47zz;UvRJhLil;b@gJ!iD4zfN@n?z;UvRJhLa2@8BQ{sWH`y?B=bhun36FiV@l
+rd+vBG8FlHb#kQvAfWCk*09L6||aTw!pBM#R+T=#I@!*vhW-TBijIWe|mY{}S?S+rzK$(WKcC1Xm)l#
+D4EQ!?w63?~^*GMwb!ewNuRIWeqcSjntcGMr>M$#9b4B*RIDlME*rPBNTiILUC5;UxdqS&m!T<GhUVG
+RDgoFJrun@iNBC7%yXlF~ae~b;EVTb;EVTb=Tj`q7%bPhLy}xCBsRElME*rPBNTiILUC5;UvRJhLa2@
+8BX%dmxG=f8dfr_WL7B|PI5WP{Ia{IboebYuw-D#z>-;~WK7AJk})OEelJOHV}Ez{9~e#CEaHhN8B;Q
+*<XyBBPL&vjfTBxAmy9mCx^(hAVyrkZ)MTj1P?Mo1LrsR7%<?5eOoo^YF&Sbq#AJxc5R+N9<lSqXo6+
+NtR+PyoldDVzls0Raj4l~nGP-1zFBw=euw-D#vyU)Q-L2QT0X+<ng_X?mCBsSPywl~Rv-1|5WH`xilH
+nx7NrsaQCmBxi?!?Zadl+&HE18u`hLa2@8BQ{sWH`xil4sLEZs8=)-hFNnoaEh^oumF6HjSJ8mOis+$
+#9a(N!RbY!o%guB?A#&Ov#v%F(qS4#+0mm|8CZs7+W&7WNgVSS~8243?~^*GMr>M$#9a(NvF5{+7|&}
+$-t7e7HZa;7+^BMWPr&4lUcrGOv#v%F(qS4t|^&UP6JB@mJBSpu;hFRV6}-^$Yg-Y0FwbGvyjP{k})M
+?O2(9oDH&5TresXXn36Fi>;8_@b3$WF#+HmNnKevi4U>^1BT23#`RsxBelWYjNrsaQCmBw%_Om2rwTa
+70*S@e~OJ+5bF(qS4#*~aH8B;Q*WK7AJl3C4UILUC5mG9oXB!I}mN@g{a;UvRJW;K(MBqK>il8hu7Np
+dC0yzeobWH`zGefOrPgoc$2E14xthLa2@8BQ{sWba5s7EUspWH`xil3BoHB+1^-l9=TtMwHA#CL>8kl
+8hu7NpdCWTzgYUE1YCF$*g8Fl4K;wNRqwl>dkT!BT8m9laVANNv<SazkL))E1YCF$#9b4B*RH;o-*t*
+(zQ|yaDDftXM~283@e${Ooo#TCmBvMoMcur8A&pdWF*N*l941MNk)>q`^R&Qf7hO+!AfRHlUdSaB*{p
+Ukt8EYMv{yq8A&pdWF*N*l943ucJlme?+zJdA(N3LBS}V*j3gOJGLmE@$w-osBqK>il8hu7N%C$l&(H
+QaWRx{bMv{yq8A&pdWF*N*l942{hRGn3K_r7n29XRR`LInCKl`tLVhrL5A{j(7h-47SAd*2OgGdIE3?
+dmsGKgdl$sm%?H-EC=#4KNO5$RleBZ()H<Vw=f-b-duILRzzGLmE@$w-osBqK>a=eswY5+X|bZGrnmY
+x^x<-wE<gV@Q&bBqK>il8hu7NpdCW+Eacw$-6f_|J=inQCP{WW-^>)Rx=q%GLmE@$w-osBv+Eowa*aQ
+gp&*>`8?mf;e-%YGOT3QFd0rVoMhH88A&pdWF*N*l941MNk)>4B$>5K{?<~O^(JNwlR+edNCuG%A{j(
+7h-47S)Xt{ZGNplee`VfUdEYhd`QL60#NYkv<-^~-Trvh31(6IQ8ALLOWDv<9l0hVcNCuG%A{j*TZf(
+!s^*Ce{L^6nE5Xm5tK_r7n29XRR8ALLOWDv<9l0hVcNZzgP`MVy6WP(TrkqjakL^6nE5Xm5tK_r7n29
+XRRnH5WHzcss@z@HzPH7CZ8j2{_4GJa(I$oP@*BjZQLkBlD~KQew~{K)u`@gskJWY(M*KQew~{K)u`@
+gw6$#*fVR;u$(JbY$qr(2-fEWY#GeE%NuhcxKIsQ6r;9MvaUb88tF$WYoy0kx?U~Mn;W{8W}Y*tCNft
+`TJfxv*^UAkx?U~Mn;W{8W}Y*YGl;NsF6`4qee!Jj2f9WNk)tOeJ`F_bz;=WsF6`4qee!Jj2am=GHPV
+h$f%J~Bcnz}jm(-PqeZ@VEwWj4V${f}kx?U~Mn;W{8W}Zm)#%!?0y;8uWa!8&Ofn0Tj28L+-oLZ##Hf
++0M%OkS(2=1dLq~>=3>_IdGIV6<$k36YBSS}Kb&~J<yEmL1LPv&<3>_IdGIV6<$k36YBlcY!cB&h;2p
+YcearpAM*>6kO1>~$h;5eKeLPv&<3>_IdGIV6<$k36YBSS}qjtm_cIx=)*=*X-|^1a_BX4ah;Ix=gMj
+2am=GHPVh$f%J~Bcnz}jf@%@H8N^s)+8A%^0VJ1W)_|pH8N^s)X1ojQ6r;9MvaUb88tF$WYoy0kx?VF
+9LZ>rpZ)y2S$SgA$f%J~Bcnz}jf@%@H8N^s)X1ojQ6r;9MvcrOB%?)szSnG)o)|SUYGl;NsF6`4qee!
+Jj2am=GHPVh$f%J~BeVX<Xpz75M`rPfQ6r;9MvaUb88tF$WYoy0kx?U~Mn;WHv1-)FRip2n&-1<KVey
+HfBSS}K5t0ET14agn3>X<OGGJuD$SguKi;#>F86z@A<Zu0vS$txw$XJoFB4b6yii{N*D>7DOtjJiAu_
+Ckd$QTjZzYTlb%(taM|JxW~MY|wx*YxZsX7_`w-+Lyk$XJoFB4b6yip;7bV?@S?j1l?W&wQBGC&r456
+`55>Mu?0M86h%4WQ52Fky&+QWXQ;nks%{PMuz<EXFkm86C*@Mh>Q>!Au>W_gvbby5h5c*W|@(ZAtOUZ
+hKvju8S?veMl3%uLS%%<2$2yYBSdDIk-;H@LuQ$gK_P=e289d?85A-o<afWz)+|3UIAn0h;E=%~gF^;
+~3=SC_GRuq%3K<kKC}dE`ppZc!?|$C>FF7EOxR7xn<3h%Tj0+hTGAoRX2N@4C9%MYoc#!cR<3Y}L?q>
+aoaUtVE#)XUv85c4xWL(IsFfuEQ3<VhqG8AMe$WV}>AZN|eEI=_NWJt)6kRc&MLWYFQ;vz#qhJp+Q84
+5BKWGKi`kf9)FKXYtWpcoP|BxFd)kdPrELqcY8k)a?%L56}11sMu56l5sKP>{dxuQMx93<(($G9+Y3$
+dHg(SHx!E!;Y)7-^9ijdCV^3-_N%0=Q;OdnEUY`yq4o|lDGx&c3NnV(V)Le{TF`f@xOon`5*t|fBm=r
+`QP9F`XB%M|NP(o_V51zP)h>@6aWAK2mtey7DxVo83Y>x005K*001)p003}la4%nWWo~3|axZdaadl;
+LbaO9oVPk7yXJvCQV`yP=WMy<OVrgeJaB^>AWpXZXd6iU6Z`(K!z57=T>Wc%o+PI4X+YNe<6+1Q7vJJ
+^bk&{77Bbz8i8YGoK{q=qHSF()*?CN9==RMAQGoEhu`bWdj!(>L&Niv${BN|TU$v^Z?Znx8=<&J4?<y
+LB;C^03Ibd;K(QYnFRT{l`5!W&D4lB_)y$cZ{<=tN5|fxPD@R%XwZOhx_*C-P_PT_N`TidGu!aI|}44
++jv_!q{WHuq-^6v}q5FZ=Vu2aP3<>we!?VZfNl3%U__lnjTTC6lEdTQO1t#6PHINiP7H5O#_)dck?$Q
+M5(YpXd%_s%9=hBD)i(HEn2@b`lKILDY>!2w!yW^GLtive@0<<)S8A0p~-S7-H{$fcoMq&!&u;JQ_89
+hp+=XG6RP%XYj^hWVYZ?vJ5)*!tl2`%qS+`}06diiYsVxU?L*A%UYr_-Lh@4~G&Z0=xO+z|aUb4(V(S
+p0PtoA~+KI7G=ygx{P(Ql455OA~-I78#>=_wdwctf*Nm3tZckmuRKnS7F7%SQ^HIA!B^}&E#`aW5Ho3
+EA>&mQS}oMrKB`FMw1IE`EM@5m(UJw~u3sIo%)7KMW#9c9TkAmZU<I$1u3>c^AiY?S9To@W%(BF>hRW
+HpU5TCB3gJRe0wIrG`#VCFTGFBVsztPPwj*$b)MQTij=>rkL7X(yf-Z7x`1qDbH`vTy%a50%l|gI+*h
+480>+kv6_h&sJh~Fnw>j3znDu_A&HD)Thq_BnkaPp#?eMV_AWEtc<aJ8XD(AY8nH3`)M$^{W$pZc0jb
+s5qa;U(d_;|E|qSlQ(03}RBwu%u#WxSnho@$f8kG*Hp<Ik>p8(~&VF&YJz=n2q~1D@QL9C<lbX-4<Zz
+RRoAh{dhJfZWcVVVafT7%ezJC>uv5i`XO?0Wwx%zc-o~GDUs?+HdN;r3j9gFK-uNj*HivnX&(RwYhZ`
+SLcV^y8v;kyou{+)iw4yR~cpQNVr^@{4=t5V+}-{}1b-@m?VMejEPi>>GpyvW+JU!R(|)}rR$<yrQP)
+$}1e@S*byP)h>@6aWAK2mtey7DsuqS6MRz0077d001)p003}la4%nWWo~3|axZdaadl;LbaO9oVPk7y
+XJvCQV`yP=WMy<OV`yP=WMy<^V{|TXd6ib%j@vd6eb-k^9R##nTYCZWB0vvCw`l?dL6B}9g3UrpOCy_
+Di4;i6j<MK(?+hvV(!HSdgDr7}hcjnp4*lkrv)<XWrP61pH_+|;+UR65nIwOgR&^e>YgMkn%X@TSG=j
+0v8jDb?2BDLF?Lg`ZEOy3vb^*UeWuZckrN=5kU7AYil}nO8f6BINFw2qz=<^oJwY1R@oM|y&@`E7^%W
+uJNG-a?=x-wh0xVU_GNiTj4rO~yj=nRDO^xfWl50HU$rBY9vFVgcQ`B;NCV-l28XU&G})f|)`QdX3c(
+p~SNwx$K=DU~z{1SMgmDC*w#7K;K@%N&4QI@9zXi}*e#SfxF=>C!8s=g@0Klwi>)k5)EB%(&C8I%yq_
+?{4SfA<B&4rb9iPJdih9ieiJ-(E&wqVo&Kfw#X(jO$8iwd>ai{gCR+h3TrBz3$<q~Q%@VE(ak~27IP5
+@5VI^W%`EWmS0n~BNTP>QT@3(Bexy1utxLJ2(vGbFrY7f5Z90?@8s$8%MgW(_2FZvG*+K{)gKL5ckKh
+fgC~?L<f=uHczw2<^@{tMVlL>mc^XQIYIiU<b(>I%kXsAb;21B8EHQ*fK#yu3}N&Y?LPsyClf~_V@Mx
+ibmq6*7lTUsMlf#2xcr)6Ut4m&NnzVQUzr|~hYs(LWoh%t9@d%(1@6yo9hF@uMT$0v>daQP@w3aXWU%
+Gu1Hft&B){?}&&I-DkMV=CA<d?7{}z_{-us@c$rtyFqSW3kCUllTm6Cxi&UA4qr_YKPLaEo)GqnVkC;
+wU4qyZ9?s`=ANReNlSus+FOjB!Kb19_M7J<8%(w9uvE3eY7n;ZtOcKyKBb8ig#(?#-5m{Y%IG&2+3np
+OC<nbG9HpH{SsgcPjNxbmPO^Lhe^IYLpJQ+cl3}S&M)^p8AM9U(Wx5aZXt49i^?#N0Q$2?t&o{?j<|l
+{li3Oz@EXqbYmp=*c3TsKFcOS6n-jB|Q{oF{6Dt)mImA1K{Rx!;6#q22aR900nQZf}mt;NeNyr#2q@t
+$+tu4&p160)Tg(>F}=J+jDf&bH-Bzk<glJY>eW*R(Pfb}448ns()K@#`x9RU{$5;f@LVdizRxi^U-Gf
+_gOQR4(=_FP<@Y<l^m=)Oa;>K*P76xTC9M7LVqrsiTGn!Vkyb!Ys~`y|UFTYivjZ?S~ns*{M_IsGz&)
+EIWxST!+)ichiSU*Y;X7`$zu;$WFRV$!)ONrSOHS?qCAHJ(J$#kL$0W7FXc6io{LEOCd~pykwS*)c0{
+}sGv@*v}}UU3vft{PaAipxAERjd8X+Vwo?1hJaZ2DINTSY77O<Np(pb&S@}?2w05?4k6irg!mVjT8dQ
+*FkeDnAexrXFc@Cl|SQLvwL{@`B&XRusP)h>@6aWAK2mtey7DxDy!T=5l000Uk0021v003}la4%nWWo
+~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OV`yP=WNCABa%p09bZKvHE^vA6SZ!|_NfQ3fuc(W4I
+=ICc9OpK6){y{Xhs4GfFN%Dt2n{n0bTUkno+gC-_4`y0FEh;8m}I5(Nrx=M^i)?>S3ULC2M5_7sx_{f
+ExK;j8?8=*YS*{*5A*=-LGFOMV?noCj%48YR9BuL8PbjVL;Ajh=gDLiNY^n+lj}<n7;-^V%B{9G18F3
+7UWjkPS5u)$4asEi1bs`G&~>IF##X8z#G{@nZ6Cmx?v#$!T`e3VJQ^%4$FwL%Gq33aqQ-{u^#WB+PhW
+#YCV7dfzE5qY4XG_cp}z?)I!RpxM$5qr$IOR?GprEZIQk3SNq?l}gboQB8ZwIR7G|u1<@sSMUk<dR7i
+_hm7J@=GG;=8Sh8Ai@t`m?Jo(v=QFoYo|@Q#$m{)zJBaKTHnz{7D^Ya{d|T-ord)uU?>LQ}dDfzUY3-
+E81X7n`r8D}qpv6VjB|!tusZm#j!<sR&&vu(#HILqg&`%l;;Gh!r|6RaVlL9E${B=N-fP!<$pySb)+C
+^5G4=+F8mp!RDkM$-uH5!}j<AhB)XPu%dyWSty1xzX%SXrH{?-yW3uus;ztaSZ%kft?vCB(8AJq3;#u
+&<YWp6dW2JHCol^r4hA=kcKsc8RBO%aX7`@8zif6}jZTLyZ`)L*yK1}JtoN>~ZMy5V?`}Jd5>ZEpm5J
+GySw>!)cnFpg1xtFuIMNTJqu;~5Ar$mI8av+v-0TVoM3I9KnJ)J7p|66GJukLw4El!TkOF0jG}jW?0s
+2zj1(%I~ivYb_D$=V8h@9Yy4;OT>{ZbCW`qEcQ7pbN~!=yJ=>@6QxD&@n<%W{RN*TKp|A8NENo|se)a
+=D>a6Do<BYvu@wuk1MvpM0ZN?|!V09o<zp#n)R6*cZvJ_Tj+mM!(biZ=;x_2R=*6`@3endfmThcDn6m
+t=Dbdw)*WVz6u-3U-Y)*5A7_;AKFiq%#=GNj|RstE5`}ZS2bQr$&QwvBz#S~Q~W@VUL}Oru#gg6YhJy
+2ir|QDr_j$bOvrrS?4(3u?FkYs)lPY?noK#JMB?V!nf8_Ta=Aw9ws-Zee^KpL``vf#M(5q_^#z?%rF?
+vn>-5@<{(Yl^=W^-vG<t5`+Q)JwchhV&Z+bWB%`bDgoa;Lw$`sCYvC40VwkoRIB9!5oJ&Y%Y)bC3aoq
+j(b3V&EQTOSfh%9&0jV%#_b-v;tX7_JvzXCiQsE7(Y6Pv=6)@;M(7sY#5{l`z(#`9fj6%F;^x$(GHbd
+rJjko2ttibmSjm`JS2DN8~GKppeL{jxmuo$K#LlS`Zp!Ui#+vGePsQbjSFur3fSCeJ2>roRK}@>US~5
+3TfZg1=sQ28OjNp1Wbq0gBe7WmSf~&W2R?ENAvl-G>JNrlG3B2saBtbYr-QL82_jjNG?f^&*u+?xex{
+~xk8Q2#E)^~G(MbZE`ZBSUP>8fF-2G6$GzO<%$jLSjTXY>3%RxXqlGQszj*P&uA_o7TsRh7U%<y)QDI
+FZKoQfjIgDz+(04{`b>0eVcJ&OE*;}2>)>`%+nhCa`>(6XCA3I@;*Fd0hP8=@<f(zbyPM3}!rU9bN(v
+}<>7680rf$6yD078PZP;>B8cnO>c3TBf*)C0!I=VQfHbPMfV=TH~9H3(3#j<@w-Ag|Tf7<nj!XKh?0r
+#9pNOmr=Rq|KR(5xdeNs;*xGH4h**7@2Y0?5MLVHgtYY$7hNBbU<^2N8m4jTXX@45_BcUhl7Oy?l%&E
+Ywno57<iUWmtlgRT85`RC}O;C7xH|ZO;8KC<}7pDVI{72wE7k{N>Zo2`3o}P*16W6KtM|`@p8@jw^Zg
+hOJ#2m!-MqpfmHOn<@71NFVo#t&$YkVKARMg8C_<kgIF4&fx~GA))(L53(hfUkDO$fL1Q@8lV0#Ivuf
+RJNT&SBNe_&p`Gn9`B~#i`d>}?3usp&!I_#Kb(w_K@0}I(Gb#%PQE<{PwEoClCSnhFQ|2R*{+2RIF<x
+{F0Q9hq$c9`N-NIUi4yV7Z)unjea*{VXPVX{20&c*B=C_MpaQMh8B&$CYzyL8TF3V9Q=Mx|W-CxtUDH
+d5mTgv1!yl<A8=%|_!sV<uh5n})*$O*uU7B7O1P12Jm^VZ7muyyj_V9?Q+87?(>Pr=xPU$>}HzLZd7p
+YfL!3au&6`BxhXKX<Praf8J7N6O;_6DBWywXySk$*@E_f$jcs@l~^KQ2NsVZYBk_<HSjpf*vr8v9vnQ
+}!2+nyCaP*%v}`74Su~1-!YsHo@L34!7&n>iLhw$_Lz>KdBd5@9#m-L5PNMw?$DFIV&aDeijGOIoO56
+5CW?eOVcGAe|_VN$4frZ_D;6qzW$ql<3yR13tu|{uu!rG(n*)?+Q0WEI8Fqs{8Kdv2D<SfjNlD3@)O`
+@Y98cH1+A}+O2>OG_KclU%25Ai(|3EEHqYTg#DQ^}wWR8;Vylpdf}0OA;ZC5+4H$j~#nV+rV>or>Blz
+Q0&AuuAo9qkg^~KWzE_z5J;V&&DxIuF&7>Cz*--^aMFGA0s}rv<3QQi+$x&!M>|*3VFIu$OQ9;_Hzbv
+YBb*g=ETsxADADV;(q|lAKE_$=0W*?70k8r)4^OT|7Bo~?n0g|nlo>E9)ol09%v6d@4Oe<6VMUArviE
+pJpT=FE-r;Wi0xt;k8!x?(|9k~aXTO9Yu{8Wg?&RI&f^DyfUotQZ3txCIX)HuTep#Wz<<a6<euQ?yVI
+Y`<<1_B$;mQKeic-F_OY-X3e~MX#txpTpm=bQtGxO(BQUBb*@1z@|GZ4FN=L{KGCwKr6?L8W&Caupy7
+aC4V?md^kKZHa9>1dBGvvb0v6=9PLhki4Zcc|bjKm(@)FuLpwC1FqX#}RK(d){x8pU4lz0&v7%dKLxx
+;L!6ew`5*;1i31EJ=;>1Ckp1Notf4fiEkMU2$$gZkuKF{b&SlCn0w2<<2YArAYfGJ}|w%`+ubWg^|vq
+()+ugEU$m_sPvQ}DOUd;8;@e;?cu@5d0J2SCs0cR1QY-O00;o{l@>=iiKU-(1ONcf4gdf<0001RX>c!
+Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJow7a&u*LXL4_KaBy;OVr6nJaCyyE-*4JT5Ps
+*c7zsrImH42ilP*p302ESGAP9)6sCPn(Jr1jyU2Aup$oJRZtZgu0yMcQ;KjGP#Z@>BGhpE@gALtDpMp
+GD%`h)3W0KM^J{~J{J)@yZGZV(<7f8l}!(3c(p*D#U)@E~CHzTHNGyUa)hZop9(aLLD^z$|toxZxD~1
+z$0cAu5nQm=aL(pQ6z&3-JJ#OQGpig;FIi7I`LBYIhYfgC4Bo1hIG(Qx+?X>8g!^M!yN2+uJ`0qSW1i
+ZV<rS`f8Y?M)itbmP%g=qxd?aL>s#Ki7WsUrhdYV2QP|m;RcAJ0V81+n~fB;R6G(Z;A_QHY<2S$B53K
+GH)3`Nv5dfF0u*{&r|_hN0VcfPq@wt(^tc~eQzSgf$I>>awt6Sx!}JNpsEH~(pg=|G%%XL`T?!s^7ll
+S(8bWK+n_Q})C9=qfz)%u=BJUj_r*nJvD=JME_~>+Yw`GVeplUamvHYo=p$%*j=@<ekhuTH%o8VZ3JX
+H_4NN_i#KK&p>%Jhvo3)TpcMn4K#gh0OVeYE`g__T!X^clW)=kxA#`FuydBsHB<1ydxx4XFz~ptKYdC
+MM<tFd5AIUn!#78;wWHXG?!LT22Rx1q>hO(1lrdz8v+R#@#v0p60X1#lQhrAnr7_YL=<I*Vre>tt2Tu
+G>iv2%l(-OtchXZ!G^se71~8kO$Cg`B#bYrVj#s!k}!qjirxYDK}gerH^r%YsQ(J}C6v6vE$VyMX~7>
+Ia$@3VKs8uU_>lVqJq)B&E$B&Ytm~vpvF(qYPWz_wd%FYhv>;2>1B2<8GZ#y}R`ZqIf@3RY=f<UZHdH
+ZP%M3c!nTxw&N}~ytT$4cAai5F}G_%B5tJU0qX}z;PC>e$6QRB#w2AAfRisZv;#cBRmtA@tF&+PU&@^
+_BC_D=TAS#7(np=wT}E!;!<^e{!xboArz)3Y9G^AJ(RQz!D7N`bXKIYrCI{F>Q;%XzeHi|#ZOCnWPr>
+_Z)uIv$(t6CPgPtU6{10lIyuA}lx+Sbn&N&ZP>ypjl;&u@Y>Hr(&g3QGs@xSK^c4N#S!3g^MStT>xeu
+g(gxWC0wt_zL?)}MSisNrpfTiBb|a0&0HlP(MyzR0qaGF`drivHu<WKAkhpLCuKg0$=cz}P;E$yEHI&
+;5HBr<IiHP1z0a47gULc1l^|5sC*@RC?0pl_1?IGWFesxim1a_+%IORZS)k7tqMO4ZI?Vjk@iQ%j;;j
+dm3!4EHsg}mPmwwgx@gV1ecQVFFH&lY3UD%98+qu2n+s`h_KDeC=yr%Y`zl6Tsuuft1S1hlc*Z*Pm$S
+co!<>5Mdsc4E=JKE5nPvs>4FdAXy+9?Y!JRjgX;g=HoOI#P@O4D-vd@sDWtl!V9kk$2-h8&Wvy3)iAu
+HH|oT96izZWhSxTjw(!7nvP&mh1oL?R3ExD{t@9q1{o*KVnF$=+p5x?Sk)Fz2w?oebf#s{4Y>T0|XQR
+000O8^OY7y_!;PfTLS<9ZU+DWHUIzsaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4C
+QVRCb2bZ~NSVr6nJaCx0n?{6A85dF?yF;YGRbg&0GRVOv-2OyB5BY;>?m0nLP@9r#EH8!%nNw<IfJ@X
+?}2}rqH2?>v9-n@C^nX9YwAN2<JqX~^i{lO$2P;Wf#|3w#kuWDDcSTjwnTuChy^-azs9X*(zQYk@8Eu
+Yvr++=HE9ec{zbLNeu46ae_h%nO9OSs+f6DzZ2OQs-y1{(eo_AV1!hH0}YwakQ<Mmrqs*4W(vz%(;<Z
+_g|X&pD;l4)JX&VFT8-LaMc=dcQ$8?REzcXWb*}Dn;|)>uAo7?GxwwO8Q27E7KD7)m`HwEJP2&ey5pK
+D=RnDA~5vi4b3XQHhSj~uPgk7GU495)VovYAP^)~GPS~1LEXYKlQV^X2J>60Oi7v0WI2~^pC3wj5<36
+gSj2BkE{iIdN}Z$LP?l%AaYvEwCrcW$Ln(C6nk~}L%2de=!DE@Rc1*(2HrU+yB;_y=i4R0*XaK*H|CU
+(dKI9JVhlOsU&N15ov58u#E_~pRc5g#q1EecbXqP=kMrS2BRhpC3JJ>avgFgU7q0e}Jlroi$i&8a#0A
+KntT6~@^7u20R(wFXh-kmHSZ{dqp;1&(uBgxHHNdTiNE3~gb8~_i4dH*vay1mhOw0I2choi-05XUr}&
+Z$eY?tC%oFUQ?E&6e}oG#*4~F7r|1Am%xdUnDL7xiL@@Ln5Sd`_doLUI&6Ir?q%uv^ir;RZapgWLy2G
+9x9_(yLu6F67-g2LE88xJzI&lgXj6CyFfYhw~499q9%Rlz)9%u3N45cK9mKZhsqe+q@Hm;xIT0d+rH^
+^+O5tX?GDj0hUL;ngUQ`Hm(NwLR#*&86dD%BD#NsUG|bfw2GFk6G9?_A1NKkMer8R|_G1lW2bBv-5{d
+avlDcD6H1_uJZ4py@@v@k5|Jo%Sqhu3%yjJ>jN*?juZpTTzIw7psCx;c7XtCkfV3vJpHBF4>Q&7R2?;
+M*XdgWz$0o(-|7f^p?`ihd#h0IxJmqOc7`xib^VK9?5R$r1rHKSE^o?kdZ6U&h+(|USc|NGmIcDwcM$
+DhN08oymPDZQraL)7b(!T2WUVMY_ytFVF+#U5t&-t67hSJC?U|5Lv&FV@ubZh98<Ur<W}1QY-O00;o{
+l@>?9*$_0~0{{RH2><{)0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJo_HX>Mn
+8bYXO5ZDC_*X>MgMaCwzg+m0GJ5Pjz>O5$N)L)anFN}Czw0SHV`xQUA*6pfJSt_CY^8`)-9_Ure!ySZ
+eBNwR*T>vDYRRFx^0uODi49(qIS_u8G|v_q}_sQrari7l7PG}|zZtXxYi6tzvvBps#ZU#XO+8@r|mwt
+DFp+SAfYM1JBYR%XkVOhWz&2I7b8T_m;)Q=0CyjD(j)JAB%$vDpWJX>4q6k1Pw%F|7_6;>)Xq4OrU)u
+G)C2w5!zk@ZoPjTn(>NQz@E+SVt3fY@ayJD`^|;tz7NAu@zUngN0}y>~|VVwYD;)w*-csyrJ>IZ;USL
+hcrsAtgwfmQev6OnZ)lw?4Ay0N0HEEIhHPmhY+5Gj{h_k`KgIzatNlKj!{lv?b)X8sNutKPJMP@N)N2
+rqMGq;rDTNUzKmEqCgErsY;JQ_svC$*3nJ7tfX~#uCzkMsyPw!PSm<57alEZStdX%)5kBxoH@6|N3ep
+Es=!QLJ)~`x%t~4g8GuREZ#}5Fa&^^Y8R!lp`$xhXPfLQw4n|&V5XVe@n=xcK_X%1(Ld&EL%c+tNcNv
+2yR0gS4w(Eb4802p*8?a#<)wtD^EY!TRZd$VC@I;HMtLQNVsC$nCA-fvE5JfDn5(@vde%6ybKh<Q!ql
+f(%iQv)Sq_ChL`m%c!I9SEwJHsXoV=7=RGiUd|<d-z{HR7S6}dXaJ#^qyow+V~pnt;FnL`d)MwDChpx
+F!WKqMt?OBB=mQM7EF=el?kA`${1UtmT^9W4w}fleb;E*zHPj}Z4k|;u)KDtGyL?Mk4U*xN~}q#9$|?
++8s$Rg!us&k3+qqbbQ^{uckQj%E{%;#rBbAX3y(r9^%@DD2O`=?&37eiC3=w1aw)Og%Vou}N^0cNYVH
+dsnCi=PfLBM+NBEw5+B<7m`_{qp*f@mTz7o;i2;0Z#y@;@QJA9>DI`PR3!S|9#0hSd4s6v8Ms^)+E(g
+kNxOmqQJ&PI<>J~kWad!{vqJ;K`<M7jvXq+q?mBP6Z!S$HkP3*}6flmRH#YrnZj8!z5}xg*iqB_UVjQ
+CMLXDReayWoC_C#u#A~i;xpS_-L!3;-&82ZZdi^u~K~V`0d!+N>qJ3WrFZkg^m)PRrtN${j7h_xaIaE
+yP|~0OVz`G2K!ARba=BV>r$3gyX-{hVD391L?Dni6_;18mHEdty}Ba8AJEO?4>g_7o1@y*U(DE#&}Z5
+x_RQ?@7klyMBQ;JTmi^A^YuBA88G0<E+_MZ4W7eLp#8sH0P0xU`?a!Gm(|rGbj#gBwMT$C0{{c`-0|X
+QR000O8^OY7yAp6}pQ33z}g#-WqF8}}laA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT
+4CXZE#_9E^v93Q%!H$KoGtAS3HReNDhTWy_EJ~s7b66K(<lDDO%g(dR4P)?Jh+A_Z{;QDkZ7fz1TZ5@
+9mqfUhn;bX?Pzkv502jG6^wVtY$y(L2S?KVWyQcGTS?C`gZVzEkGm`+f5;Cu5p7#8waPcZe697*P+r%
+TGTQ#!nEMu(h$Mw(!B^n6SR7doJkB7(w^uE(m|-=lQxXs=|YxmXWFZR3|vKUWOf$1JYMdX8!X6L_T7^
+rBWkSoSuJx0FJw;25`;z5*=$7xvi69ZcS4f05X?2x3n#UjfF%37w-012TW~uZUtT*i`p^b_;X3^Wy6J
+!e7Tuy2tnPvwzFUH8rUj%r(pI|Rv??a#L|<fGBO~lBmHXOf29Cv3ls>FBDT3t|PeB|9%XB;8SgtgC=|
+CqYxoc|4#GI88%C#(<2_D0E_P~f>8ZDx9+o_*N=`u_b%vUi2tb;g>W}8J2W4(#jt0Wu(3DKp*T`}J)@
+@t7prrc?+WI;~I+MZJ1a(gWctP51)K-}h><bmZ0<hp79tB18#Kacq`<XX@PWC^96j~63(c6j>UbeCDK
+{q+&4eCXrzn1h7+RdWjxhR<cm)bm<v<729=>p~v`#*S{s<I&CdZZrlq30poo2$$dgai4mg$DQid#kSV
+tOWFMl&vWxERjKh6{W~8cUNzn{WWv%j4-USe&mW)nLFT%kfk(Baw=r4k$ok;hfcXn5xj>?M=4I`RGv(
+hN?KY5yRVwR*%Be5>SN1cyJBHleD4a--)~lF5{KESUP)h>@6aWAK2mtey7DxP5z|Xb<001`y001%o00
+3}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OV{dMAbYX6Eb1rasl~loQ;xG`s^A#g;X
+(h@6s~&dI9#R_GXd$2^sNz(aWB{wiMz(2{{rY_l&>l*q-AIw*@tZd@@7Za$FF$k_KO`wll2M%IG2KmO
+qu=xew%uw|v1OWBxsh5Z8kveoI+~h4QYk?TEnnF>?37z!9eb+SbLowx63}kz%#dj5C1Cgb%F687l3A0
+#Kn;A#-j!m{2<>(UElc60(GE+yH?~=TnC8YF<E3TcxuR9wFn%s2Y~b40D7E!;HM*ujuh$2~MR=DYr6>
+znM;SY|uUs7`85!-ZTpcjqmAn3l2+>s77n)18v2sT@gbqD<L-X2ijcyS6w#F-z3H#=K-bSI5K#)|)l@
++!Q>aHy_IkWa}=zK`EIZ!DyS+1lz)`wD_gs#3Bi~OCb<hl-_gRU@d7|XNWxwFU*>4GNgFbX}eW{dLk!
+%E2#$rD+!c1*(2KE&MiL&|YzBtJDmV*~m|{vol%eyAPnM}!95{<+$Tv4dIZO8C$}?B0jOYcySxf?f8U
+8LYgZ!N;f-N!{>n;SN5)h)LgY{%FN?aJ)XK4j6!?r=)nCEeeX#B|Sx17Nx~<2rRt7E*w5G$=zN_5MwM
+Ww6D=Q2&Qp1dPGKamrRmk8QLEwMH=Thjb|A}G>@_(87(GJM)O5BpXG5E?lPY>4r*Rf`Ca2RD0c=@;+6
+=h+-deD40h;HRkRha45LfNy{bsyjO^?Ga#0z*X$D5hhoVE0YtqJd=*>!;9vsk5{(|Mh;STOa+3nD6A0
+VM$6g<e0K9*}xkCidDLwCmc0G&q2?G5_<-cA2|uTQkd5&7z&IKBVJ-%q>MdaihN*w8QPwOV}N{9L3yu
+DNM9@mTa*oEkr6d=7xiy|Dg>iLHg`4I=UGjL08nSut7^&&6XFV==i8=<bi()?ZLd0|XQR000O8^OY7y
+s|F^<OalM__67g|GynhqaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4CYIW#$Na&KZ
+~axQRrl~mhq+c*$?_g4(+7YA_ExVsB%8uUR{?9_;FkZcrrGH7XJ6QxLlq!OrKzekoYY2pA|K1p*qJac
+9^?e^tE{lUX%O5;&9m?i`2k7v;%eGuDjwP~?snpwG#S}2N4#v~mT=9g4T1jcSCg)L|uLq}SAiO73?W@
+UD4$>iiOU?6_V-lby4Fcn3qWh%Th+TqvkjNShLnC8Yd_SCZQoYA`4Bfh^%*cxkB;i|2tPIN`R+uMHua
+WOogP$`P*SVu8Cw$Ge3l|)8+E7zqrw&Sk8!a_6=_9xAy+E`i8HG!cgZ)jfmt<i`2A&ru2D{NJ(lv`$U
+CigEOc1M*dDHWP5XVNwB5W<tt*=J*sUzkkhRc$JDhH`4wo^9cd8a_;yG-d~;^uU@es+pH-B~v7iWy;z
+y2}iry=C)_0hBcAoK!k<{@OyRdh$Z~>-Op?tEc7Ml9dA1zcF9=k2w(F@H+Oa76-Za4&<%Uc3@%D=t~4
+X5_pn=Nj}HK%&=-smt(i*4d8xWUKrH<jExyl|3ks)C^dpSpaJqQ9LoAer7yWCHWU*5cz^KX!?JE!mz+
+@0d-;ojaN8{1rsb)VMEvADcq2Vm1kmg~$7)8r*7}I<i&u7UXAWE2z64zp068R)?4#>hl$&9^_$~C1wp
+}h_SRYqI!%xH7U5)(xNPh?m9Q4f{Tn|-}VISYD6GAC_(mtL&I>|pxdbr&e-{&q3+Y0#yAdI%Ewr$P%7
+qz`2d=%F&kcByZiuR|vxa&Nx$dN<d-zixU&%LJB>4h^RF|8t47Tdmxhf`Ve5ZiRL1ch;;?Kl)ccqDiT
+|>{p&6coX*Xz4wrV?IQKYd5l^i(yi2dOpErOr1nf2&oKosr@5<V`j9YKMvw9RD@TZRR5`4o6FujO6a4
+)4@7rWD!M0MZRx4G)xyIP=>s+RdWr1CR$;fH7l2|#bRmZW)5BB<8P7HnzKSkGn!Kye^JE3nE3h%rodr
+rlMoh=4m8`kw}6y!=QK6Id-M(A<<7<aLu_G9#$WQRds^DD6CVEz!A=WJQ~;4RH}^8P}fzkMuy=S`s&Y
+uLkh5^=+Rb(or!76rc*)3PtErce7CKehe?P)h>@6aWAK2mtey7DuKn(m8Pi0046g001%o003}la4%nW
+Wo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OWpiV2a&KZ~axQRrl~!GE>NpU6=U0r>7m#QcdbQG
+RyM3S(TDA}np}Oj>R>&k1ux?^UwrMZ>>vx<l2!w8%Co(>BJagtFTwK&Ya6fn)xiB8}2d+1O`|-5@3}>
+(xjSE<85T=T+xnL3WWq`mn#PVMrMa+?E4L;eNMgbiOZa5(y@C_rGpaL0!so@~Ji$?n_K{CbhR&bvgE`
+_F7l}MGo5HZZ8%Iv;EW-x%&E@eEuiYd#L?C7eEfo8u2-RtY`L{STOpch3jw^$8x)TrJt$TI0mVH96&j
+Z{tDx*-d|gsGn}<I!627%qV#G+-pmc4i|*o<B(w@s(m~XH^OnB4`<!cM9wlc5(|o6QD5QI-@5c3@{P=
+CKdU|GT`CPnzkaKbZpcHRjdmQAKe!iqozpV5d|vBX0}~L+$ZNT_fcpBrXjI5z1b@@w1IeeATX3fpQ?K
+c$m!hP{f0`D1+JWKaa+#V2C1OQjE$eFnOMgb1-*las-X%`r&fY}rU7t~(r!q5`ay)0=sUF$tPr*u!)?
+?i2ExLR(c)?PvVflZ3O{=DdCy(E-V&CSrc<h4#>C?!;zUeYDJIO00w;pWVBUWskKX-gJX*Zk=!c_)JM
+cUhPUp~rS#Q1=^<T!lIm}+>v#B?506fG(Vk>5y$fd*~QO1&#4A3wh=`8hEDzByhqX0JS4XMmNa_T5xG
+$P6FOErw7Sf}}t%U;l1;2{WU+VHM8^$vC4Np*>3U*9%0z3;T)_b!2$_!&_NJaQlMkf?`|l&TH)QX31M
+^vJh!)$MjJyWcupfESM}=N=fi51(8j7mY@!WDJfin4KD*hS^ZTbZ$rO>$o^<KK<oQCbT0Wir$-Eb*`@
+Z?y=?1Sz&2F&6A^^=Q*mCU*vC?(r6MTS0sOaVV{g1nptWrfHa9>O+gxrnZMxzOLThT*^^0jQo6*6?dc
+5mu2`~^Drhtse#Eqff%YpGH7jqlVr<+2hOk_6T2#wrQ)3k7cKcNDP8C&x?3=TU^B^a12Y*)tI*ooWcd
+Pw>(BpD^+M_Jjr#%izSAhSiW~pdln`Wy~3YQr^RP6v#BJ42xg<dotM-tvt0?y{I#NV8BAGNmOVvCKcF
+@rnIH?$F`DhqWEQlxcM(O&AARy0D<JPPsz{(uI3+PVF#;mOOD)TyObYc$Jz7?y&?cr2SjvsK~@+YyZv
+ArDaaCnO|RJwa8r{H~63eO=ly(s%~d>Fn<h5rg#BHn98c;oWIfZ8@#I%0z!CUcWuxq$l<h#po+Z%hY~
+Dc5V7xTR*Z2@}^a3%bg)ABwy;9T#S!9C+mWm?y2$48|jNA{(jo1AxbWRzsxdSb<JXHHT_nrE`|!j-F}
+ZdI8aKebMYK^|Bz96Yq7`Oet8GoTHwC7O6(tZ(5a8$fO(&N8M-UYq&^z^98Q95_e`i$buju+pOMj%Gx
+a(jS^6O#nUB0q2fdU#8&3O@1-;C_WX+bH0YU%5lJB+D=TffS4u^pM08mQ<1QY-O00;o{l@>?VNd3&o1
+pok_9smF?0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJ*IMb8RkgdF_~OZ{kJ}
+$KUfQMwb_tNSI=q$BX)caFCEA1Od@0_oiCxb+FFZbZv5?Rlj>@`~a8?1lntRssf1|cIG!byX%>M%-I?
+Lf~(GTZvg#XyE7Pf;HrP${sen`&kAQSnMt@;?vv{a588n(!3|*^{N{R|C<W>XEF3EpGS{B(MlO1Ol+V
+%&7E(drK*YpA|ACA`ODrS~&F4$swM66weuy8{B2e)M3=G3SCF7QoB9b;tS8>FbQwc?}7Au@;7QuP@0`
+z9HfgxD;60|%IM&uj9NQP29OFLOfJMbgrPM1-j&clljI0A4d)K?g~-jj0YpaEtWK@`An70m)aT|aJ(=
+T4PSE7~PTNeN-#MBgy6OIQU<u!Ijv+HRPH$Bl?U`1VhMLVh0DuCt<{rElYMDC<b7d6@0t_22>eGQ>>b
+TKZDqYKF_H=UNE&T}%3*1QEi5#=_ZVr@EBLI86k)0fvuv_Y$OwcAEQHst`xODCya3X~ZG~3eH7D`QvF
+W2)w|gSK#4ksEjPJTd-NF4Xz)Do#Ec&9~cl9`VDU*m`YfN(ph>%jDWuIxi`7Ff0#gP@Cct<qfu)xdAv
+kl+-bDp`6ZFu`NDHCFs@1oKU!hp7~rlmYTqEyy6W|NlSj(F+nWqJ<1uvaN6>;{Yc%P#ANs8k3?D|r`*
+EiPFqSggV%kh@lh<49V90rZJ88>ExL%kn{Sl8h!~{JXX5v}mF<a8bI|>Ar$YOQU8G3>L6t5SN&4ylr>
+wq6bMfj#%ym#>KdvUrLWplnoyy>k{5vn>m3ID5yM=(ab>pB>^>ji--!c`DP<a*aawruEnS<}t34)8F>
+k$o@d3_c#&M9vBYM+I{zSy;po9#lvtN97~Okx-bA>?lR4$X*oQ$wJ}g%P*D}!q0GC(2M2V8vv6mWBrx
+ug)(Prkj-IjI&pf&>3JJ*I?oxz8T@<(aR$$s#F;#25@+&Ug}4gORfwzbT$Q*g&sB-5@?4F$8qd{;tMO
+c&xO&D_@V8ntG8=Jq2hT76CR>BJMxD12*WkG(aZR3U64&H8L7d<@L7d>ZDREPtn-VwWIg2=p=PcqZp0
+kOwdCn%z<~d26<T**4<T-~phvyvP9H(#*S7R8(#hBJ>R<86ii`iiQlDMQ7Dp?H+Hb1&^iEE#~P+4nO)
+H}vJpPN6<t+JV)np(XOk5+T}*K(h#xyv@+(6Bk=N7GOFOWwBTjk0Fc*L<2kyK&rw+KgtThtZx1)tCN6
+Og%}bg7udBiJiuGcld9XhxXs!Tb>BR*!}$nNLp(#NhNzFE?r^a|2ybY+cjg?j^tK^FnSSSE2-FLJIh^
+XU(Ctr-OU&6`bslQ-FWv$q0k%O8)d_257xck_@R2g`49E}%KxDE@7U7bZxr+I?cUGY(%#S6vfj_y(%#
+S6vfj_y(%#S6vfj_y(%#S6vfj_y(%#S6vfj_y(%#S6vfj_y(%#S6vfj_yHobpSlz9ordOz2D_q4E70y
+up*;<A>2qeWv^LJ|q!eYhEU2{@=-2lL;Ofa;F+9JT&L0{;5^W?3^|kzh7jc7A3<GxPVvYf7{&No#1YG
+UfXJj%4sU)!gHildJ7Jcwf}Kc(Lo-gJx2Oj7E^w9&BmZ?eJtR%WCj@X<41oGLwn<ceSjRDp`ZovL-$m
+ylL5+mi-7?hDG_Ewd_<$+oNSCJPsYwveyM}N6S)4Izj|@v}`|7+nqS~>9Cf)z8i<N>?kdawd`aOT%Ui
+XtYKf<zNe1X!kir54qubQL_&^|L%n>09>!I0k{;&Qc&r}Y4!891)962cwllYEn`gVqi<sZ%*{+(iy*=
+B#>EVy3hgg)S>*2ru_1-}}O7-x-{D(yDsAs#wU(dyQxV@)Gd`q|XeN6+Bwt18W?0$83NCM)6KS=`e%Q
+{v9K8;8M@YeIhSK0Uge(<X({0UG?0|XQR000O8^OY7ytbDvh3k3iGUJd{NGynhqaA|NaUv_0~WN&gWa
+%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4IfV`^}4a&KZ~axQRrrB-ck+DH)o&aW6XA8K^e2dyey6XhgA
+NJ9$*@uDi~MQE|duvdE*?XHu!zy4<Z!i#O%^tzv5UY?nGW_H$RXY~iJ`X2{l7!G><@vIM5!@J&RIAMF
+%ID`2H;ZE^27c7LH^bxp*NPg#G$Xuz`;E~L26s7~g4JXTMe8f<0QGpD=)L@YP7>)MW7D*IEvEUvvTnb
+Hp)mEzX4*|nOs!Z-FWCnd$B`IPx6jK&!n^35Y0jJl5&W8_w5k#%I3*9h;skPNGMUCna{j8Fn6h`q?Y@
+~8@^PD6ABc{H=gokUzBX|Rpp#dXdl9-JYdHqC9$XAM~#Ih79M9?xYPn6gNBr*n%2~g;Boxu|k2AJ^wl
+#2W#>GL45qFDG;j?LPjigdx@$MFJ&s3}wUhyoQ=Gl^Fr_sDt3Jro*&Y1mqs-t3vWv5Cy`MBqjee9G<x
+ASb<z{fJ7F1m3!xB3h2v0;#}Z#^z7m+*-#bC7nP>-B5+6TT8*dQXjZTVK>A*{UAUp^ohm@RtT}iAP!r
+EK(_E@Fu%Q9%%MBJhcDgfv^$>PUyv;^O<L+-hQy<7$O)LLQcReH5+{IBf7-hxkM7lAIGEqt>~9A1aep
+?0o4YA=VbYz>2ff9xJB7(&I=P$mU4R*4L9r#Xj^rK10YOHRnDo&w9_p<0dup$y1VbM->=CKW9&(x}V0
+1;c$uIRVlwzIMOD=mtFMtOiq-nvE;xs!neU<JK%D%rX8hX!d!TSywG4U;=7R<=~h6e<F6H2LCa3!^|r
+lT(Tw%>L-?Khow?GC_VMv_wp`s3>tE|IfFgF0oei%xVSP%;7+a*0Rx+EDKbd9?C2OldS*C09gIZZ?WT
+!><y9J<`t5Oj2#`LkozDHNhB+n782q3%6dXnOV&!(-bZQvl~r2_PIlU(fc&HK2UUNV<nmw>HWL)gn2%
+)wyOf4P05hz&^kIYoQ+1b`NBq{;e|}=Oqdno)YnIghxQIlSpdr=r}ed5IvT?um)leC=pLKztnEK{8Rf
+K$;If7x=P=odYlWNd)TGPPyg4<&_k0~VmuM{eDO%5lggZhq!OPe<u>DCk)i`T3%X}o4f<;&ksnEnqz$
++@^&8Nw$y<>prr^YQ{SMi|>8`A!|gb;<3YIX{{SGZl1bXV8tv|Fj^fu}w69^SwEe4c{Z4a;cB(=M<pF
+lo_ilPbRQhg3&{E$F_}&dBYKpuBzaDWB4CxPo|Drnkz;A)80uw2O`aF87z&#lBKf9Vfo?DU6mruHtpo
+C~Z%oU2W(QRBpQKQYPI>Gn%!|mGcL@hJ2xH=+Qx&9MA)lGWVL)G`J3bIoLnqb=5fMub}gQjIlH$tk|#
+UYv$XL6vH2KIxu>KRk}{cO&@}R`TfWwQ>Gf(wj!J5^6aFIij_i<HO$U>l`)FRZKU>z8OT6~OdETjzjh
+udQaXz5haWkV@AU2_9)Puq<m;RMgKhV#a2V>Eru{74?8eb&X6KyK#C0BiH{mMa%ci}2c3G4+>8?%fU7
+ONHM-d<P#?<oNP0n^pvovehXwWk>M(5>i<D4LDEzJ^zjMtkbefBWNew_N{>JAo&6an{9csBOm2cc)Tc
+hh-qF<;Jar+xY}ylyuC&vh%m4dsGMkIO!t($hj3^K5#-;r%LAUMgqjnt$6I`qqxdNq{`{h&?++8m&EQ
+v+*BLO9KQH000080P~d=M=Inlk*`w#0GhJ^05Sjo0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgi
+MXkl_>WppoPbz^ICW^!e5E^v92-EFUINp&FT@Ap@v#1~sC(`QGlm&mbtB#d!)sWG4e)beN~t0*q!s*A
+eJt-@~R*Ux@df<2Pm(hQQ*bxvl)-aBH&+Y@o}e(!t#@&6uw^P~Ur`A;4{{``kO`pM6K^!S?}|MZ9d?e
+V|(?DyV%@9~S@zJL7m(}#ch@XeQBKYsY}*Y6)6zJ2`t$N&4o*I$2m|NOh({o$JrUw!%A$4`&1zW(t3o
+9`Z9-JkjXyFVxW{F@KoeYjKp?fZXz|Mkb;zyI|3_?yRf|3RMj=YR74yKlex^7rrmU)g^6_|3QXkDq@3
+@zeZpXZ-lt$4{@{fA#78m*2hr_2XB6oE-oCrM~<muiyXizSM8Od;G-@|MGD?pXbivKm7RjA3yl|>&F*
+9`|ab4_usz%^v~~q{d$rge*EUUPal5uhx;-A;@iLc-|pW$9)JJkr~mQz*@v(H>C=bbJ^t!(-{E(U?>;
+_$_Q&sj`|+DUJ^x))Uw`=3r!PPKQK|jr)BE?2Z$JL#yMMWF?5`hx{P>5*S6_bf`1JnQAHIEMe;34ek1
+xOZ^?&;K=}!N<kH7x#n?L&1AHMnZ{W$LWyZ4`d_wAp{@W1@zmyaL6|MtGq$A5YM&HGRH)BNlYzxw*aS
+9jtcfB5SCH{ZU0eEIF;_kQ>7Z~v{N{?3j3{7*OX_`8pH_EWll{doW3e&4nK`TeJF@83Mu_pd)+?N2#=
+_PFc%FTVWF{ol3udkO#YzUe<czP@YY)1OnSssEr1{_RnJ{rK?B<onyZ@bCY)bG)DQU+%bi{ObMV58uB
+3%^$x0?9So-?Bl<G{)_+m(_jAL@q?fI!{fjI;EOMQ@RMKs!(ZQ@y^H#O_pZQKP9J{v`#T1I{kR|N)0f
+|T_s9Fr?+pI_M_>H#zuqZ+@He0T`14=<gWLbR&wufgAN~C2kH7or7mpu2e)fYee)0JafBEAdeDV0%FT
+eQNPk;WS?>`<tfB*i^Wvn#+qf-9YGXBk-<?lY;MfvOZ-+lSv>u>+`?Ei3A@VECJe*NpmZ@>KK_jg5q_
+5Q;hQ;#q2M)LbV{{O1QuRnhCPxHWcl7B1FzkYo9&EuPo-+lJ@mroz==(*$X-_`G(%fHp|XLs0t_5IHt
+dEFoS@|*wh^<4))zmxynhu_>;|J~OgKYse`@i!m8{mw`K{s(v3w{=}_e|7D*>+$&I&+p&-um1P=(NF&
+N|6{lEy?1xUKmSr~-B0zKPal8x`0`h3a=rci9pV4;yT|W@(%m}0y@TYdum5oO5MSTVclQB*xJ&tNAa?
+=&?)QHXZr{EC>bKu~{QBcR{ZXdZI*-R+{nh{ec>8`o@1IxJpMQR?`{%sYzy5qmg0~fa{>AedA<bKyfB
+V(*Ic@p#U%&tI@wa~tUM=Gb1^W2Uubgr}{?p?xuD|-z4}W?8zW4W+?7p~f_kO^CpZ1&kL+<BJxOTq%)
+&1XpIr*)A{<-!1@%MKx`1OBu;m5moKl<el|JxV;_=|t|+2=p}!H@s(Z$JO}FTVKvZ+`iU&wu)pfBfPH
+_y4;ytM9-4>*s#(H$VQ-KmPpl|KmsZ@9zsP<@Lkge)&n%+!6DC{q)mxGGG4c>-Ue(9<Nm1|Ha3Dd-p#
+-{^Id}zI!~b{n`D0Z}-c3`|R4!`s@+s`t0iKdB5Dhd+c>Sd(_+Wv&XuY-(8<Q`mOKJz1{Dw=X`E`_IR
+BB?A@J1`&q7Uafj!w&#vpbf4APYH{$-Ux)aw)|8-}bZ^s+yrMyfA$IYC{a4z8GzRmmPzWf#UOSzFP$y
+?qT?vl;EZ)Eb1b7%cnlVZElWDuXddpzU5lzYE_&igZQfBO06zIuM=9`^ma?)xEDOs>b%-X*XVS09D+x
+})cv;dR|VKkui1ocrxNYd3f94?fy6Du&B7JLDe<Hxy*}h_uHo)Rf9yknir&b<qeflTQ9suIxc4!@0b4
+ch%fEtf|2}!)o_GcO5>GZ+97Xx+S&Hxm?9(QhlV0q>JR8f4zP;pOZt%q1<)3>$=mP`*ZIedv30Ly5_&
++;0_<>si^MD#dR0jd2{P;=^15MvYv{%>*KD8Qa(A4>{2S=y1d<&vUkx|RP9PVbu_=bGt8;cmj6UZ%ep
+7+(<2|DJN0-!{2d|_4kFphn&&=3si*qyY*hdKVE28UrxY#MBe1VmhIi@i*DpgPc;q{X?+l+V5^DbbbW
+WiOk=k+<_Ulc7tGvG6+<H%j_l-P-;JM~*q<g;kv=1uH)C}6)^mBP*epD7uDV}cODeI>UV_MjAKJ(P{w
+&#AjuerJV)7M>l_kSrgH+SwQx!&%Qx{kKImDG8gmpcyc*X=*Ui`#s?Q(oH3QCm;{^_2BI-d!}&b2%Tm
+riTy+Pc7wW^yj*>(c3&+<xY3}PH1_?X+l#V@us%i|G6fao@BX&YqHikoVmPKe&!wh(0={yuC09Pfi$6
+W_d5^S+|NCI(R^gyP|Q8eRxQ6R_n-?}*W~Kvw6&x2Ion0Hk8JsCGt>7?fn3tQ)qL*0Tur4M75r4X`6?
+h6)yKp{f-WI0J!(!_=w;`wpK}ZM8&4swCLWr{UCY;8UJ>Y)9$WMYhtN0eDNVoIn$UBe@xI6-Qs+>vB<
+@l-s+)dy--94nf<=QkTINH{9lh+nE9DUe2(fv0Hp`Vfo(A`(eS{7pJXqXybU(7*?daV?L)}XAZOt7jf
+jep+uX?;M{Lni;CUSKc_wVM0#aK*V_ck4@XB0L@pu53&c>T$0-Tg$G&hF&grcPzb;<yi8UrtCU;kV<F
+k2Z92UPO1gP5rM(zK0Gyv5w|Ur&#kj7tx74{Vo*l){eY!Kk|gBs=vIvX;(*|dCEOZ4mH0^Au?}xM*ZQ
+@A1d|h=>N{0^}D;L((ybeIHmKT`8E%rfCTKi=8X_uvgvulEiGMIPo-%6Yr@88x#m*y&cu1okFGqur3Q
+V(z}-^{5xGtop1GX-=fRF1`&v_(9_1+UX+xsuD87dlu=O!IpGU>KA@G!3rRFHk#peBn&f)RUCA{*Q5z
+ajbpk;)6zD;lG4yTXw#x$X=Uzi?S|8dVcWO&!_T`5|{42loIa@F)Z6YL&E9G2*N>ep6JHuG}d|8-5jJ
+U<>6nOfGZmRzSnKLv|9#r@oxtU8)%jV0@+f;2$nT=Ui3h2`tH?^?d*%1_n$)B@g&%QF=*WL`$4YwFg+
+Uvs&~p0+$?sC}Qgx%-iyms);mB%x2!_1<-6NEYl*y~b1Dx_WvRxoXA}(+2vxkj#esc%7LpXnHemdaC-
+Tye=K!Tg@<Mj90C1Z!-az3ky8moWi|B)&0M&<TmdrbBFpS&E;tbsN-$1sqeo3OM{*pUh@*}%jaCv)Zi
+{xEyI}m+^;c3Yn?7mo4GU>-QGPhGAc<ov&k^-Kc}eH#F+l$noh(W8L+j#`;qlh(=S|iyM3J3jm&MT1%
+ts;E$HO$|5D5HdhVGW&AUoPn}3>cUdH&#czYRq?#^_AG4xy-#B<)A93<8to-uu$`xmOuP%3Ad$C<XQS
+sRd+p)XX*Q&l{5<ukvXsns)7wRD%BF|YZsV%8p>y3FUy51$79r~dJ&Q+(zGtg4>sF+YrXsJTea%wbX}
+dQtQ=?7ZpW-byX6HE*jfPxkgU1B*5@y{u*ydWh#t7oQVPVpFb{KJuv`b%J+VQ;{`Y(1eH%_NgsDYkt(
+ebu`-qZLJCCnYk-#Px4gO(=*mo)ZC%@^quQ;G<qqYiRV<S@oCSe-Pc4~Os6i2yc_yDPjbIl8JN!9Ek|
+c0hou>$uzZD9Pc6GI4?0O!Or@BA&h&j|ou2lZ*qjcwgu|OL^3qFPlB{X(lf>L0YkCcrt63J!4bRP~Tf
+@n-r$2Xxmr2B$72cj%x^!<dQB@Vs`LrUP$D4Mfm#v8>-Q$cY&nd&1V?WItpE`>*(d_}^^<Q#$O)l>%U
+7q{Q<Xa2QYZ`zm6I9o<t$E}#?QtGp`i_Y{#V$^+*B$EVpJs~Br_MB{&?M7>XQ?#vkLPom-g8z8Qz;W)
+h9*^U_sse^{keLZhgx%`S8Telap0jd*m}mLr96yq4_)Z}$ohQqk+r6m^}93usBUPY!<E+b@%PBFULB<
+&;LPNES&gP{W1GoYU|eqk=sGj>O-0S!OyHT)PgUR9tZ7l&_ru`1G?M$}9`)|m@Sihonk>!4ex~L3r|&
+mRDPI4j@hZcf4rxs`ipI<*M4Q#;9fx}Cy36K%TbRxVFZXaw%*jhJJZ3BnpS#DI`$gYvBwx!NUDG9-+g
+wJ**N`v+>Yf~|<JIF}#L8Kfd8ljNom^dbx$xb)EHm|G+Im0oT;T30*V_zzli`{d>E?!qI5fwZG35--I
+b(x6yu;@Hh+OaLxHH@yV9yZXMrv}fn4gH*GZ8kgpFZ+Aot7)@$;O4J%3_}Lm7#P?uHgZ^v-EhEhPs~f
+JFRD8M-bPHj?>6{28~HOouIH8p1G`8^^}jPE>U~OlVYD0s=gvL(QC|P))wOg!m`>k&D7r=K4vBLHnSH
+Idd*b9FnT>_wL87|^fr@DSx=!&^~d!7^IzW8Qtns9nJP(l{BZf_Xwx;nGb`NlUwTw8*K0j61u|)8QEu
+6CgiTBv5*RT-mh)l?R*LWSG7BaVKRvsp%+h^bbETRW>y1n25Fu=8UIv1uBlBeXrdiJ}A!5ROcOo-=a5
+*uMXD(+7JaKowdq484GF_#;{c1?w?x|bl)#_0QK0I=cQoTxj#*2xk$>24NPLkmtBhajBdfryptpuhX=
+WYz1=1$A1Y+30n3z22X@HnqZIWjyFPlKMm<JIMOW89;rXO!XG$Ydx6E{o>Z*tVu)DH-ZuXBiU{1p1Td
+qOaHTQzt)hKJh#2nWJu>ZsF+`o>OKf(P2!9`##q->t#LhrQ~!amy!K`Ly_h@sA`>ckchnW71vnHB%4`
+zuHil{MYRIS`^C(3uIIjHf*|_tH}pWy`LFgemy@-=?z&szx~45_8uyF5E}y=8u650vaq`zyjf$+9plN
+#a<i5x(h9(%L(2-dN-x=!3tW%cN$z#y9XH8;EnXxXWKb(d1Ttv*7DlCKNx~6ER7`8#`p?)m3*R=foCh
+Hkzj&##ctDamlE1Y-rbrY67qmZU2t)<vqhBfyv-Nt-+o@cH%EyWnT=W_GnZnk%4+Vpy4{o$;SP1+a0U
+8XDdiww2=ht6*IERH;*LHANU%dJ05Xrm_C^zl$>SV9@zpBZshB7>r0dS?7Fi<Oyz)x?vfe$Iv6W(<@W
+{P#=+T@E`hGv;d&v(G!RlACpxmU0cF>g=xR+<I0Xo<zUqK_4-}F;nN-lcAZN>EnKzcNCRA?3#kqeO>c
+-uN$7<$x#1v&DG0=r^~q?@y^hYql;U{fptx9V;Wnp-+9KlrItg>p{ESzzZB9K*$lU4U-vH!$?mWxmS*
+y*p-yjLQMj&|k?;9Y)@Pi{eN1GTg6)aTGk5r!t;*|+QW;{v)=UQWRDe01e)BF@xf+*|&3&7mRu6PN$H
+{eHGZvLz(2D#1(HQJDwbn;WWzG~#FQo+Tkx-X5i!|$?p81Fw>z;*=;r?z0Qz>eBrr<qSo&|v6Q3rlaF
++3-TRlo#Z&CT6Ux6H`Q^WNRBT8f$hzYlZNWd~rb*NjXbGo!YdyY{iz@M2hNmV-5mC?mAFxC~uD7qrGr
+2XBlsAsbk<px7c_3kfYyv#k8Dc)crYy^;mxouQ7$`fOQFE%V|vGc=?9nh{4Qe<vo+dw!HyNa)g*eqm{
+oOQX9p+!D|1XbNMJn2NlLW%f>l+ZK#Vzc8zx*~nqA4|Km^|NSzLYyy%@AkNHIU0#=9C|_$j0=*O*yoX
+UAR`WWa2L-#E<Mc|CZ1$vP)aYrM6Vqzpn%RYU_Y_|U)w$*@qQ+ZaZOz(f*5eih+Q>af_1uU^dzpiIYf
+SZ+<;;WXKekb6={0oa%WPt;xnV8VCdB=s=`Dl9!<5+g_jtv`Oh9HqX$Y9!a^<X_X5BbI=yCLi;|cb&?
+vKncI!%3A`NMoyx3rb(><_$6MNM}+b)<)T%(PD@V!U|hrys9Ss;Lz7rXIkZho3Oj>FyyLgWxiYIuji;
+fr`0E%g}>Pqfq|+TG0!PYqmdPdV}F!NqQ_t5`z?^yccO&&x;b~!Q?gV+pN<h@!i9WU>UF03kP+kYZg&
+*7(<Dv>VfMC+E+HNsdI4uYdVLFY0IWj;9;$~n=@~#sA+n`$<xBjrz{?q<-wXNd#qVt2w#gP^MiW^JB`
+NNR*l|W?Fim%4XEI^Sx#9p&g4~xu~C@r_gdrpCMu?Qk5>mf^QN~~gP1E%L8?*R*JE^G=9SZKCj?c$0q
+8NicSG27;WN^HrXPKJS|Or^t?3#0<Py&JGEebM+0P2^k=~d=P%qx|<1873&5U)fa=)~f<3;!7x$j$a5
+8lwjZIi4$YsrU}sF)wKM>~tOrTdy4*z%w{#k>7k^{e2AF=e*qQsLSKyc5f-$MXyo#?n~}Yb47S-n{_#
+c>B!d-KG$KprnC~8QzONjE>LNc#Lz13(KOlU%L}3;lBEPhdNkY$<j3L7a!EY8X51;C=k8uo<YhS<&m?
+GtLC}Z+r+>$^4am3?((rG!}+wbnPEC6e~+SqC!R_luwUEYI=plxlkY9!jqVU*J~MYb4ONeX1E!Ia#5F
+sodwv{Z9L<`|Lr7w_{g`gLjC%Jk=N=S>{(VAl>(KYy;bf>EwI#A>%F<~qZGO!(z_zQS6Cz%JI6ILz?t
+ambW==FPr)QawY9?{}wW}p)XUc5}XRdkQmu}m@wbNQ}W-ohYV>6#NF7GKD^RU@9o0%w?zQfBKHOt89m
+8K7yIf_oCEqwMoN=-AKwlEN6FqmAG+>DWXW@NLp9D$rJfr-bgLB`2nm101e(+BaoymzjV#QAYv+^apD
+c+P*#^43KRCpP@no|WdzV2!sA*m(!1C(|?TX`UuHyJv7S6`62;4s+hOgqN<jX_LCg+40`9xuB)2S4)}
+FR2Z`Z{tCfq5NC35OB3gF>&oysWg&KQdXh73nR{&g;hyd7d6cOcJ&;IPd&=Uq4jN~k6z)s^<P^tp+Th
+MrEl&qx7k=4MT(+LpEV;PE!-0rp^Lq__+W)(YW@<h><0SK=0!habmQCR`+e~Yg>(>k?rkORPsyD8A<n
+kg;W_Ef_^$*O<KA0saOuG=#H78z-3zyF5B1YHXydG)U4!&}pWNu02&5o;y={@ww|2bzx#t&!3Jx5{Yx
+nDEunIKqqSrO+e8y9}%)n1O5;b{f(T63CX+bCSN2iMCwXMae{P4{Ttea+l|Ho3-EOQ&wrY1sf;mR-vh
+#beF#)<(s$N3mx78_9zIOCPof=Nh@1*}UHJ;T+Io77w!*H<~>CvY>r;w<d*_GyPRPGsDcu+K)OP^tR0
+87R_2y3%0w~aLh=|a4596m$wRS`Y=m~yOib`eOkE1YX^DwQje<J)^3+;-mO#qLdl5m(7mhWS#Qq7Xew
+Yj>zUoVa!%Hh!~E#Ura-2w4UD7Eqp|%q5HwF=(6Y8%Gq<rgUDlS5!H7TYV9c_@GHj+`HCG=$GCQ62VE
+f}Svk+^nYiQ48V_Di<IptjQqbc!>n*%N}i6>~?hrzj~R(ooBw&LZr=g^qDng4RI=o<2$U3dfIm2=i?V
+$59G)O^jHI$q~_dNNc$x}cfc%zU9Hru8fa;>UAhdg9eoZHDBS8NjSdrv_gic4o;TnPnim(Bw7ZWi(r_
+nm5?w+|m@yOwe?zQ>1Uxf@k+&ipJ)`V|K7+T^F;Oo4j798)oxZ4o0sz7BmM<N-sN8NPXCY4m{=*QqQJ
+;&dm;znM3CETvI@rK~7?kY$5lUja4xIc)l=V%3?k|m0)Q$WSX-~(g8k)g)Ie~LR&NQDP!rJESNb;j`3
+czX)`B+<^a`1ygPh6=Z<Hi`}p)EnH<VXdzm+8L8fP%&F@)z*}a>!wk`FQv%fs#F`eD?Qqw_ce-8)5mW
+FdDk@Yn%ZbbJu$dI#M>dDoGXL&2M*2KhA=9Gwgc-UcDIq4?DNusN=!?*IaqW3s^g5Bq4FTgRXdje|0`
+I^?sdQV%EjdkxRx08;`nTf@d?iac0i*m|(V1Eul^_+`*o7L!SR?hlH(=)ppczDcYX(T*mnQElX$!pf7
+W3H|lCY*D^X{MApOF{FIdYyIh@G)R9pLSmcK?B#tWUkl4&kA*RSZBRFU|(-Tj`Oy}DKD`zo8N|j$DB&
+3*|{NovlW!)Sl3vv)~_SJdmG&<(q4{CEhnMwm(jV|w41D5N#{0OnUC2Mo^7kyo*k*2N1WBDjn9=+UD_
+ksTTa!jeChT4PKH{rBPVN4XE-i+O;r~iaPI4|Um1$fJ=+I!ife{iQ<0pzHb&ZqStN0d&e;LD<`|U){u
+<WiN6UzCXE-yVnHtVk$IK56^m_tt?(j_A>OV$)V$PjRHlktnM&D*{I_G$d;bo37K4xj-MvRfT`T?mQX
+Ru(7iL9ASPP?7YscJ*abSwtryHaK~H&e1v$=Pmk<;k!{2PRmavp~{q?>Xp-QFH2+`OMLpl%Xx-W%0FU
+QsNZXnj?5I2Uv5Kt$<ZDtU0SSWig63Cx$1PUYz~kW%!)8=NWaTbIQi7$_c<V{h`^)Wjk%nMyIoR%bMg
+aqnR<y2G*QamZ8&Yj~PbG{Lms{*~nWqGuA}BmDEEJubI|3_q-g1xtp!$R#7Ie%hueTp-u}(t~mw)#MU
+&uKb;SpGfRSM99x`dE^ChB=4{u+{OC$^%Jglnrw?<ydDZlpv!Hp-km-Yp>N$6~CdMYk{5Wf2ccfe$bi
+MCT3$|mqYHnoBa>UfCrjfhhDYV%Ym>%1r;LMo`ZEFq(l+8&yl=tjw4(UE8!+F~S==N$r$FNYd+xgl$8
+=aFlCu`57Q>@p2iNNpuX%{`~-S>X<&+k9|@w?ypV}f73|N7&<_(PMA{xIV4;hXQ?e|rDzck?G2zkdJS
+`>*B*{qO(q>GvPMeSbfvPQ=V`Ij^v4%2kKtVb@T-o&Hr*33@b#otKkntEMpZO#0!aZ@4p@Fwr47HMtx
+>T8;uPLc3~;Mz`bK_NsYoJ&xj84iB!HnlOBG<U2z>iR0PJ(aGg3%9>r+k#BpP%ldLTI=*J|;*9*7{RY
+RR*6hL81mDw4-3hbhIMlL@zZ^_kWMs|y)$!9cHgDE(%>d+>-!jc#_LmljTNV_{nDlO$Yc6rVobO+bp)
+Uusms88j0n9a6I_pVCeV5~`i+wCKxU8m@t?6YG>&{RNJ0QCpHC^`F*PQe>jxE#tHC=!MT5C2P$ofLV%
+aC;$;FcqzYtHVk5l)NbrE^<OE-X97%Sv|5tY%PThLoPfqQS9{W!Gexg|Ar%&dK=YaQJfeb~!Y(`W2;;
+<h)2HCXCX{F}XFn&Gv-WYye=iH4_KJ@|~eM^|b0a>^?^=ET7g)XdK~PCVOkzsdMeip2QkdH9W1+aH6&
+5Ap7d6Gtpt*wVV=Jm}^;jF9*%98Q2{!U(WWfIY~ZSmyYZ%XEc|iN6UbAXDCjb!d;Hau9+57;bnhjalq
+xI_i{RWIe5HgW78()a{7BY?7T?fa$NS^a@24+kF?Bruh9U<xR+zx%c<+-B;}f3+Yz7Tu)%U{dpS3_9Q
+|9)*{wMfv+N2i6XxY8>vD2v&9M_3ertqzI(nV#atv=dw6&b7S#$Wq?%*<iTF#f=843)?f|lbqYxWnZ^
+O`ZrN#o@_@p2Ap*(qQ4pcdj?m|!^$xESJ^zkj&$^+D+bhGTzgj{oK)79Df)Hzyk*qC;KNp*kYEY?-f_
+Lp%9<-=V;m-5isurQcjms$AE&z&y7Qag1#_kg&M;ng#H*Y6tR`BV_B${KHAz<)rR%q;@$axttzcb6&_
+yVL3Fr9GYFu%Pz-dmxG(j`MhQ6y=JC9XZM#QugiheHRo~$gq>Ag&Z;hFRhP4>%URXstm<;uaycTi9D-
+R6f2>)ATW{YPiV3Tj<t)&0wqn7?MKhLZ*fJ|vhOFg8=W?82*#%!CdowvT8@<TqouR-OXqt(y!E8Buxt
+zUR&R#C(ESGbZ%aOiiUuW4FSaW=GCb|%8IYW78C^{UnT63()NrE*3WkI~2^J&pxbYC+fI~%zii(C#rE
+@yL=qZ`Zm`_530Ve4<9=xg?a2;FiTaXC@A91mP`;K(_aWuJc830$+QG*c|6^44<{(uus~@Y=Esx@;ya
+ZoQ!10w>F&Z_VPz%xyVSx16b4&eSbu>Xri=%Q=HPLy>^m7TQ@HVp)$bXW5oxY}+xm?fltx>TEj|w4F-
+WcKWydnT-!OzIeAyXtv{G+i|h&u+e2Lu^ndFPIhd&!P|b^ew%qw&hIQ@xBM>|EE(XW?cCRP?rS^twH^
+D~j(u$>zP1xz+Zm;8mwMaz-EWi4mSRh>rPxy3A#rE8CE1dMBq2#i5|V@@AxTIQE)r6N6gh3)yD6CyC)
+@Jr%3+W6>3N>lXXNnuog7jQ<(egNxn{X$Nl{YVadBrjBgrJGd6BNAYw23Lmae61>1Lrb`M1lpWbMa0L
+hcNYbVs^b^-X;oiI2p`Z64{4bVs_U%RMD~O7@iEDFv_J4vcJD``gy_w!gS-EpB^D+w6U#$W5R&Y}!^f
++e%|!W3D+Lk&Fv$>!9tt&7PBJ7KR&hZD($_GdFwws_;*rJ6<FfCr;IDhibM%HQS+%J412e$jo+RX4`k
+!j?8RFX0~%O+roJ}9ka>Bwl}#QaM^a2w{3-OS9II9zcUmkHf6VsgUygP<K6geTMKW?;B5`Ht(Ue{(tb
+Ut0(VqoBcN@|;LcEB*tOnHgzRe`Szy@e-6(I{<=tR++XvYWgKRVI?HI_mTeR)qZbu*P48?@gAKN-;&*
+_gnk0&N<T5tQ++y3IVL9)sBwzS_CYTG(`bFw|#jhk@o7aPodi3uk*u07=?Ff8G=V;URhZkn+zz_!B^8
+-Q%b0`{Di*p{H%L5a;)HcQ*~zPIxb+j)p>IlFE3Z98JyS%(c(H+SDQsJG4M?U=)zp-8}E+cAd?ptoI_
+?U=)M%wgL++KxGF#~il94cpe@wn@4jZrC<%_8e~5YgR-xKT9VZEaSJ$=#BWd?YV8|Y1_cqsCHwz4Usm
+$*;r*;Dc>0i2aDfrIk*i0d)lAV0NXCZw$HZh{%`oc?Nx4@_1o^?UUM^|!3N)*p>VL_zU{Sd+w9vm`?i
+n1ZJ}>|v~7}a+v6KKY`al=w!(Ar5)HP(?+k^5ZSZXad~=~~M|#@;-}G{0f^Elp+wtCZyf;za_O-VS>T
+PR!ujw3YMsK^%+wSwWv%KvrZ#&D|w#BxAyloqA8^+s)@wV-@?YeGzZ`&T!rf=`It(tA4W!pd5c1^b3i
+EWc$1MSVIH-Fy1d9&utL^nj(_;3@mO=Ip11%Y|%Hq+Xs6MF`qX)Y7pc22iF(`{>X+w9!-b+*mUZO3KX
+?A$gx?+n$yjm~YObKB_LHafSB&TXS}+v40dF}F?3jb%4&-ymj#*KJ#J+mXC8RR4Tx^QvusaWnSobze0
+v(uo0Egxl`mCfM7?;I=WiZ3%80fp><2z~0Zcsj}_$ZF_y&2H(a#+XmmZt+#FKZ996~j^4JTcfHC&49s
+TUwwbqi_qLh0?c;6xc-ubSwvV^%<8AW2?cQy>ciZmWCgj_e-L_@7ZP~rs$Y$HA+jwl-ircp0HUZxD#<
+sn&ZEx(FO0oI1ZFFsWT-zSko_%4pb!R9XfYE+YbWPKiL}1w2+BUT|aoo0fwymsfD{I@t+Vh7t(@yPV-
+5H7wy8zcjg8i&*KWoR_jS#UVb>YcvUuxsfZC`5Jm)iEFHb&cs?9Nb}*pu2eqPFd)ZTo54YT9<0cDy7>
+lGlr@1q&M7Z0iD{mwx%qaMmPt?{Mh0Z?x?j?b)%QquajGjwyz@O#u{>qUXbs&F6YD6h?FUDqsI4u~4#
+ev+dbzdp6si&9-N=?bK}hGu!^mwm-A&%Iw*dxu*HpmD%=Uw!N5bFJ{|t*t1#xHdQ8`?7M7REE~RW+ai
+0mJ4o)f+abgI)Al^J{f%vFW82l($bH+a*fuM+&5CWaV%x0Pwj*}V-h%2uQrm9CyG^0Dors;Y-(*#$m2
+cY-+a|=Goh?$lXJ-o+Z+j5i9>lf>vF$-@+Yfh!Yu=Vvd?EM^r8iC9%y`4!4Qw}?-Qace!p#CV^4pkh&
+q1Y~bH{T_0*YU31hMJErUTo8bz7WnOVVwnvu7GUv+bF|nz3#();mM7G}FhKc+F@(gW!x68qS`EGl4DK
+wiw$JV-r*pI};&l=FU)@yb5T_Mx3DZjnemv(&u-AWlmwf-W4$<eFO6i%y-UIgnVzTy@~c_)Ehl-^t`F
+_=E@r+Z;re<@@B@H6mLkpdGKbS?=}m*=5B!S2ErQ%Zw$OK@W#NK`)=&JvG2ydoA_?9yTR_pxp#&}Ad<
+UD?k2e#-EL~Tf$avio7HYsyHV|>EH<y*y!M&`sp)M9?WVCCyl(Ki!RrREo4IbZy3y)JtDA{#Cc2sEW}
+=&k?wY9x6x~pCL(vUIHx%7abVJcyvx+(MhIHZrjBYTx!RQ8~8;sr=3Jfy3$>=7dn~ZKUy2<D!qnnIwG
+P=p=2BRB{ZZNvR=&qTH)D*@P!Q6vzAfua%ZZf*b=q96^jBYZz!RQ8~8;ou+x@)E)WOS3!O-45v-8}S~
+$tx7yP;^7l-Sed&S_K&0V044gJ>SnU+dS7y(-`Syq??g$M!Fg4W~7^u?l~Vekqk;VDBYlRgVN1OHz$3
+!LFopi8<cKFx*6$aq??g$M!Fg4W~3XBZali_=%%Baj_#gO507p<y7B17qZ^NIJi6x#M)vE!q!S&CbTi
+V;NH-$gh;$>;J44ZdNB3)M|IB|$CpsAEW~7^uZbrHp>1L#xk?t3fsysw`XDCGQ=*FWPk8V7=@#w~*8;
+@>0y7B0q@A;VN6(ik<bR*J@Nbd~ANl#(V&CR962_D^ebd%9dMt4umixV)q=SwyAY^F~N8&R8)Ueo1((
+hW*CDBYlRgVGI3Hz?hpbkEmh%(Rb|Zd$r&={~c(o$tVy?hBc2TDocJrlp&fZd$r&>87QdmhSm0>*?^2
+>7GAL*7X_nd{EfH(+y8IJl*hg!_z(AU@?6pLEZB;7Smf|)QwR$M%@_ozC#hjQ8!239Cc&VjZrs7-Di$
+X#k?z>06O!?b2IaX0Ai_kh9U^2ZkW1Z>V~Nsqi&3PO}EWbH%r|Zbz{^`P&Yx{1a+TL5rn!C>Ygu8nC>
+2<Zj8Dy>YndOm?40rZi2cA>L#e0pl*V?&!`AO-3WCf)J;$~LEZDE1T%<W)QwU1naZ205JhLobxLy{LT
+_`;kaOnplXzw-He+T4!m8IS1<>j>>jA#H=L-R5WdK<>WZlo15>wVqSvO_flyy_qO<6Z(-IR4x)=gPAW
+ZjT;L)HyhH)P$<!8FRcDeHdDpT20psCA>(jaoNq-Kce=)(u&&S>=<~YlczWy611*H*ej%b@SHETQ_gr
+ymj-|&0F_)>kPlVb@SGZTQ_dq^Y`hmaT&Vq=akYE&=iX(g0367Zs@w9>weDt3n1vaq3dR@o4M}ite1J
+~Ra2%0Dd@VP>xQlyx^C#Yq3edO8@g`jx}ocyzk;4M8+g4l6hY*5lh;jNH+kLUb(7akUN?E&<aLwRO<w
+o>soX66@av|pcZSdTSvs+UUpIc;_%-lr;Mc&ffnNi^27V3v8u&HvYtYwsfnNi^27V3l8ss&|YmnC<uK
+`_yw+3$w^X|Z{G2aejaT?6^&T!3tO1Gq2(k<z>bX&SD-FBtzN?VF8#g<}Av88}%SIxbLbRk_x7t+PNA
+zerp(uH*CN-0H3ky4}-{-7dIYoOLZt$|u&zM7||fJ(ZOuB5XPtSPi|xtfp6kJ8qZVY^;S*V46g?RqT<
+Tf<(#TZ6Y=H5Je<e55_n9%;=Sg0}{54c;2_O$(-(0bK*S26PSR8qhVMYrM@xo{~K!d%B$Sz5!hWx(0L
+&=o-*9pld+afUW^uW6+f>1#}JQ8Z0%KYcSV$!CQm325$}C8mKi;YoOLZt$|ttwZ{DY9QX^|8n`v)&->
+@gL;|`7bPebl&^4fIK-Yk-0bK*S#x;j!a{fy)!9rdGyasr^GZZItdbj4@1q*%+{2KT*@N3}Lz^{Q{1H
+T4-4g4C{T*SgB@M~OCEG3CJ!LNZ|1HT4-4g4DTHSlZT*TAoVUjx4eeZ4ajBKS4%YmnC<uR&geyass<@
+*3nd$ZL?-Ag@7Q1H1-!y)zUO7WZMw8hYZGz<U+DnBc9!TZ6X-Zw=lWxHWKV;MSOLUJKqDyft`h@Ydk1
+!CQm325$}C8oV`lYw*^<tufzIQu8^R{sCPBx(0L&=o-*9pli(EmeH*suUAdhLIS@AehvH@_%-P3ouQZ
+@uR&geyass<@*3nd$ZM<_Q|2~fUL+F@_%-lr(APUd;ecNQzXpB{tP@-%%=lx?9g2o&B=|M(Yv9+wuYq
+5KzTO!M2mBiNHSlY!*Et!}z{y5nfY<=B0b&Eh28az18w@taV1vO1gAE273^o{SFxX(QF-Ns(>O*vZ*Z
+{EsVgtkmhz$^XXDB*AY=GDRu>oQO#0H2B5E~#iKx}~60I>mL1H=Z1jrn7MAh7{r1H=Z14G<e3Hb88E*
+Z{EsVgtkmhz$@MAT~g3fY>`jBMFEN5E~#iKx}~60I>mLubTXY1c40j7kn=mT;QZYKzD{>0@Q@{O;|33
+iG7%shY53-yoM=Qn0<vgR~XsDkQ_7MO%JrEbC`O>v9V_)nP(IeNH&mcAlaBd-KcqZae-t5$p(@QBpXQ
+f&QLfo*<iAPWCO_tk_{vqNH&mcAlX2&fn)>829gaV8%XxfP$b~kps_(?gT@Ap4H_FXHfU_n*r2gNV}r
+&9jSU(bH1=KK*ub$tV}r&9jSU(bG&X2#(Ac1{L1P2N#{9(r1qm7(H1^JLeijZOHb89P*TAoVU#}T~7;
+G@uV6eepW4`gICat&_gAE273^wp<;Mc&ffnNi^26+wg8sznwzk8gv%wU7T27`?;*kG{1V1vO1gAE273
+^o{SFxbGa*F*z@4F($wHe!CK!@#X~hN6SF25OC%=$MBW9lSMoYoOLZt$|vDv<7Jn$QqC}=1-4;vIb=h
+${Lh4C~HvGpsYbzgR%x?4Za$DHTddfiWHDF=2Ul3)&Q&lSOc&IU=6?;fHl}^P}QKSK~;mQ22~BJ8dNo
+?>bt<IfmH*m238HC8bmdKY5>&$s)12s{y>0{1xpQ#8U*!1W_O13jyU0{!BGRF21X5x8W=S&YGBmBsDV
+)fqsE-m)@*~N-WduJm>MuOU~0hBV5u?3tj+iXs0L6Cpc+6mfNB8M0IC60?+ir;Q4OLRL^X(N5Y+&x0a
+PRB9z-8d4WJr8HGpaW)tIjb4Wb%EHHc~u)vM;=1qN0Psv0@BY5rMLDuM%^2Fwhc7!dK!P;^kMn6t?>#
+V15iOwf)%9DyYQG6Y%(Vi24lEYD-Uu8djU843~0;;_&SE8DQL4U59CxC?8zu-J;3;m_oICfYMOp6RZs
+Va#dTm`TLU2xb&FxLss4rq7@LcDmK+RHh@Cb~IrlPGZVuilJsqai8(ZL3Ci%z^Xx11E>a24WJr8HGpa
+W)c~piR0F6+&4iPv22l;78bmdSY7o@`s@IGNR5hq-P}QKSK~;mQ233ukF@>lGQ4OLRMD?2Cq+T!5i4L
+k7R5hq-P}QKSK~;mQ235T?6c|`Fuxen{z^Z{&1FHsA4XPSc^?DVJxIn9cRs*d@%@;bq3Qb@D)&Q(g^9
+?WaP@;pf#(Xd0HI=9TAgw`KgR}-|jrjw!psYbzgR%x?4Zs?JH2`Y>)}X3ERb#&Tb7r+@HPC9H)j+Fvh
+N1(j238HM8dx>3YGBo%szFtQss>dJsv1-^sA^EvpsII<q64c2Rt>CrP1wwzXV&XoiNy(D4Za$DHTY`q
+)tE1E7GrmYf(5V!U=6?;^YzR#o20BkS%b0$WsNzAI#UYL8l*KKYe3dJLlHw+gR(|XHuGu0LRo{d24Ic
+(>%EC{$QqC}AZtL@=vk*w)}X9GS%b31DC;y0${Lh4C~HvGfUE&o1F{BW4agdh_0G^hGffJ$25Jq|8mK
+i;YoOKutkF}q;smV*S`D-sXf@7EMc8Vv)fiiyhRRlht;T!>@w8jO8hka-YM|9XtASPntp-}XGn^qu?E
+%&RtN~bqtp-~Spc+6mfNB8M0IC601E>Z|jWbh`GhdOY<X~#R)PSjR=3eLK=5m6Cr3On4mKrQIFlu1bz
+^HeI;)J6HM-7e|7&S0zVAQ~<fl&ja21X5n8U!^6Y7o>QsCR~<1EB^&4T2g3H3(|J(}1S|PXnF?Hw|tY
++%(QSou1^(It82tHw|tY^F^`KXTsB%uX~=}lb{AX4R{*xG~j8#(}1Tj-`N`UH0Wt?)8MATO@o#OC=Es
+$j5P3Q;L*UNK}Um*1|5y(*cTp+`JOgCcremnr16|%0}2F`1}F_s8lW^lX@JrIrNK$#Ia3i(8jLg;X)w
+}Yq`^pok;WM53>Azt@Mz%Cprb)YgN_Cry)zUOcr@^6Jm)qiv6vvEK}Lg&1{n<|8cZ~pXfV-WqQOM(48
+;T#4JaB=G@xie(SV`>MFWb)b4ox+;LyOKfkOj_1`fS56cao&cxdp@nAs5z4IUaOG)QR7mqN}Ei9-X21
+`dt+K1jpnouM9M{*Y)!P%;{1G{|U>(IBG%MFWZk6AdOBOf;BiFwtP5?-Gh86iq0aP&A=v&bLn)p>w`C
+#4w$BH1TNS(WIkEM{^FRvb#IOOAePD?&wHHnv66VX)@Aeq{&E=ktQQeJeqVg>1fi?q@(XHi^622$w-r
+tCL>Konv66VX)@Aeq{&Eg{_xbiB0*^)(wx7;Nk*ECG~Z?qucV84k$4_-(xr4MUAkOKmXcY()a%`nVM>
+?MrF7+;lCGpH=}NkJlDS+-QBsr?Ek#SwbB8TSOVX0GB=EfEZd$sQ?nrn1?zqyC>_~PbJCd1-BpFRInq
+)M|Xp+$+qe(`Sj3yaPGMZ#G!Dxcf1fvN?6O6t~GMZ#G$!L<%B%=vMbB^Adza<$>>(VsANidpVG{I<s(
+Y&r#Ezf^SEIROL;?cyTiAR%+CK*jKnoKn3Uvn@?OfZ^YG{NY7hkJf0o#-H=Nk$WjCKOF5nou;MXfn}c
+qKQKjhb9h99C~N?nxjgb^LRd(GwYPsJnh`b>kT=i90Ug)O*)!%H0kJ_q3FP)iANKUCLYae%3`iGS&I%
+lns_wvXwuQ7qe(}Tj@}v0=I-m$(uod6nv66VX)@Aeq{&E=ktQQeMw*B;5osdQJ44aINRyE!lS{M7oG<
+jMc}5`uN)wdke2bS^deYLQrAbQ@mfp48^BIuoQMhSx)8wW(-_u2P5}xLK0|4Jif|>+132GA5B&hEap(
+Z^|c$)Au;c3Ftgr^Bl6P_kFO=OzLG?8f{(?q5@WSYn{k!d2+M5c*M6PYG5O=OzLG-+wl(xjzHOE0UFM
+5Z}pn#eSf>1D-o0nmh}2~QKACOpmgju?=YYjO}V1U0EkQkR_Xlgd{PA_k!*K}~|11T_h264YE%)>F(=
+q*IWSzlh<cxu(Ykr^!u|n<h8S`J3jN%ZnIzn(#E?X>!x#rpZl{n<h6+ZhB`ZVz_B?)8wYfO_Q4@H@&R
+c5}qbJO?aB{G~sE&(}br9Pv0dyO?sN}G~sE&(}br9PZORdJWY6-@HF9R&etZe!6c|Tf8L)6H4$ne)Fh
+}$P?Ml0K}~|11T|mZwe0>eY7*2Ws7X+BPDmy~O@x{VH3@3&QT3W1r4uI@H8E;p)WoQXQ4^ykMoo-*XD
+Bc@YI4-%sEJV%oaLV4o2+#rmOE)imNQt+h&CPSbXWSPJ3}!s?QfdiG_z@36LYTu5)<$=;c3Ftgr^Bl6
+P_kKO?Y}|C>-c%!qbGO2~QKACOl1en(#E?X~NTlrwLE5iD8181od4a)I_L>P?Ml0K}~|11T_h264WH9
+Nl<hCSr3c;#Hfi;6QkZ4j-%H6QeuIDQ4^ykMoo;G7&S3!V$>w4Nl=rZCP7VtdS@sm=xM^!gr^Bl6P_k
+K&H2h{@Q?&G32GA5B&f+vlba?ty)zUJ+%&mqa?|9d$xV}+CO1uPn%p#zX(H1^rin}wndXpb#+1W6^4x
+=PpruJmla?keO<J0?G-+wl(xjzHOOuvn%$O4M4SjQYF@a1InI<w#WSYn{V^%D4IdQ>Flba?tO=Oxwri
+n}wnI<w#WSYn{k!d2+M5c*M6PYG5O=OzLG?8f{(;PBQWSYn{k!d2+M5c*M6PYG5O=OzLG?8i2(xjzHO
+OumkPUiqh6O<+>O;DPkG(l;C(gdXmN)wbOC{0kBj5HBxBGN>pcZPz1ktQQeMw)mu@o3`F#G{EvuW5vg
+G#P0!(nO?*NE4CX8Hxl(nv66VX)@Aeq{&E=ktQQeMw*N?8EG=oM5KvGGv|xaW?lwLlaVGPO-7oGG#P2
+;+?UP)k0u^XJeqVg>1fi?%;_A+Xp+$+qe(`Sj3yaPGI~u%gGUpOCLT>NnqV}+XoAtK=^V&tlF=liNk)
+^5CK*jKnq)M|Xp+$+qX|Y6jAqTMwdY-TNJf*4CK*jKnq)M|Xp+$+qe(`Sj3yaPFq&XA>0dIwBwTlf!h
+u?4&B69F??@*ms7qp&1Se^!pBDIOah;aDY3Y}id1-BTXDB9S-Sy(p`9fBeK1+{T3d|gProS^Cof%oZJ
+|eBSm_gid`|hHkIRnykm(w#&S2gYSPwz@M?DhID(`&&(OOuu+ElpT@-=PqprAbSZmS(*=jQO1_PceuS
+WSX=zX=&2Zq@_tqlat;ViW5+ppfnk2GSXzEiANKUCLYb2NoY-q`-PDvB27e^J?j)knmtR|p0~?OdpTa
+Do5hMJqooN-6O?98#GLsty{HsgnzS@&Y0}c9rAbSZmL@ID`D6VlDrB1TZ}Ta%Ysw6sCOo|}6hZVf;c3
+Ftgr^Bl^W~;J|K-90h?^!iO>UapG`VSV)4P^c1vgD@n%p$GX>!v<raAwx+l=SjG`VSV(?q6;OcR;rkZ
+B^*M5c*M6PYG5O=OzLG?8f{(|nO>&woiar9Oo_Wg%=XA(B1iFi-aS>Z<vjbgy?M7CYQDdum~BSnP1qY
+rp<$Dn$g*(}btVO_Q4@H%)Gu+%&mqa?|9d*>g>?b7oH3^IzIBJ<a(?+$>hFS*&8z#Hfi;lb|L+O@f;6
+G~sE&(}br9PxDM~b0!C|15cBiCO1uPn%p$GX>!x#rpZl{n<h6+ZkpUQ&-6CnG`VRa(?q6;OcR+VGEHQ
+f$TX2@BGW{siA)ojCNj+-(?q6;OcR+VGEHQf$TX2@o^g*e?@A{`xM_0J<fh3@^UOL0oaX%7UDN#GX~N
+T-FTJ1MfS@KpO@f*PH3@1G)Fh}$P?MnE8Hx^sn)5GjO;>Wp%tXSOi%hPsH|D|;zg}Eem|)bLe>W>RYI
+4-%sL4?iqb5d8jG7oVF=}Ge#Hfi;6Qd?ZO^ljz$}~A@&c8b~9UDwdn3^m#S!!a`#Hfi;lb|L+O@f*PH
+3@1G)H_4bflw2nCP7VtnglfoY7*2Ws7X+hpe8&`c$)Au=burUJ*h;fiBJ=vCP7VtnglfoY7*2Ws7X+h
+pe8|0f|>+132GA5J40W<tMk65@1B1JX-t5qCQwbFnm{#yY68^+sySb+H@!VoO{#ils8hkJiB%J;CRR<
+XnpicdYEspts!3IoswP!Us+v?a=ik~%teQmivTn)w(#~1Cu+?O%`Fy=Gzmr&qJm+!d=0pfk%{gV7L^X
++O64fNCIo%ggEudOJwSZ~?)dH#oREwn+MlFn5bIP<hYH`%!s6|kVpcX+bf?5Q%2x<}3BB(`Bi=Y-kEr
+MDE_0DifGXEB1$ze%1QudT&OT6d5B;FEliMJ1LX}8O5$@bh~ND`8SBq2#i5|V@@AxTIQ?l7fDDN>4*B
+BjW=!;~Z?Nl8+YlB6UlNlKEEWFDfVC@D&clA@(>BB;P=ans_a#Z8Nw7B?+qTFA7JX(7`>riDyv{?S2m
+jpC-oP45hkbVs@)-BY?LzWLx&x~D5WC3(8iQ;Me)0)d+rH!W`ZuE1$=)8eMZO^cfrH!W^j+_aEsA=5&
+pWolkVp)v{;GOZ!gqNPPki<TBGEm~T%v}kG3(xRnBON*8kD6RSD=*+B&mKH3%GZX~0w62+UJm*J=MFK
+J{WLoou?IvTzO^cfrH!W^j+_bpqouP2xriDz4mKG>2P+C@%Wu{o5v_NTr(gLLgN(+=0CoN8TXDA#%X@
+SxLr3Fe0lolv0P+Fk0Kxu)}0;L5?3zQZoEkt@}C>$7RG16kB#YpR#^{i?Ir3Fe0lolv0P+Fk0Kxti5k
+d6!1HN`UNgabAz{b4cEVx+}L3y&5aEj(Iyw7_UF(PE;-MBfz@Ego7tv`}cF&_bbwLJNh~>oBQn;?TmO
+g+mL677nd7odXXo9$GxKcxds^;-SSui-#5uEgV`nv~XzQ(88gGLkoxA843a(T0FFPXz|eEp@l*Vg%$}
+d5?cJT=3j9#hc5_P5VRoZouNnopann+fEEBP09yR>dX=VVm>v#-76dH_S`f4#Xw4VlKfdQ*QN8mO6)?
+15Xu;5ep#?(=h87Dg7FsN{SZJ}(!k~pg3xgI0y)zUL9JDxSanRzR#X$>$76vU0T5IZNo>x3z&?2C<$I
+R`<6$dR2diCq=^ip2h%kd(yj)H|23oRB}EVNi?vCx`-c(Y(=!O()C1w)I276&a1S{Sr2XkpO8poKvTg
+BAuY3|f2o2n1RPv=C?^&_bX!U)9|6JVFD576vU0TJz6d!@|O#g+XgiAHhM3gBAxZ4q6<vIA~$e!k~pg
+3xgI0Eeu*1v@mF4(Av{SaM0qQ#X*aM76&a3S{Sr2XkpO8poKvTgBAuY0#kIN_Vf`}@@4g17K3HoR2Cm
+)K2fF;^?EYVfI$m`76vU0S{Sr2Xw7%D7Y8j4S{$?xXd%!dphZB7fEEEQ0$K#L2xt+|BA`V;i-5i>1X}
+d->PajT5NHw5f}aIH3w{>-EcjXT$NW84TJs{=^brWO5NILLLZF2}i+~mZEdp8uv<PSs&?2BU0@`!yIt
+l`MXDA#HXd%!-poKt-fEEEQ0$K#L2xt+|BA`V;i+~mZt(ZOnfffQS1X>8R5NHw5BA`V;i+~mZEdp8uv
+<PSs&?2BkK<^Ai0s<`rS_rfdXc5qYp9Mb)eir;J_*w9?;Ae5q;-1Al?+ir(_$=^Q+_Si6anItO#XXCA
+7WXXfS=_U@XK~M(uNBt4y({`z^t0$^(a(aP1wRXZ7W^#uS@5&qXTi^cp9Mb)e%5^BZ_&@9pG7~5eir;
+J_*w9?;Ag?lf}aIH3w{>-EcjXQv*72Qp&+22ML&yv7W^#uS@5&qXTi^cp9Mb)eir;J?pfTk=4|LSa?|
+s)A_0LG0xbes1hfcf5zr!_ML>&y76B~+S_HHR=zWJG0f80*Ed*K!wBTpK&w`%?KMQ^q{4Drc@U!4&!O
+xnrp+!H7eir>K`dRR^;Ag?lf}aIH3w{>-Ebdv{v$$t*&*GkUh9Uuc7WgdgS=_U@XK~Nsp2a<jdlvUB?
+pfTkxMy+C;-2pcd=~R8<XOnGkY^#!LY{>@3wajuEaX|pvyf*Y&qAJsJnswz0rM>6S;(`HXVK20omKNE
+NFE+}7V<3QS;(`HXCcq35v!i35Dwt8xMy+C;+{o2i*^?6EZSMLvuJ10&Z3<~JBxPScPI!zXMxU|e}}x
+MltcpUEZSL&v+!nN#6pBc0t?&~uB-V5(re~z7_R7A@vWj*#f%Dx6yzwXQDCB&MDd400cG`FH3f3!4u!
++4#%A3yGx3>k&$N2xoimr4`H{In&4gga*XiS@+n!!_zURKCkxW6(gObf6n*}usY8KQis98L-cxK_u!k
+L9L3uo5+yKu!bi)R+ktnthl+<0d3%%Ye@F$-cA#4Lze5VIg=LCk`f?=FMl^|~)T1j;OwStzqmW}(dMb
+=RVUXBNpUl6lR5iZcsm-ghWEcxLg;LYaj!3uP9{ER<O&^O}K}XBN&ZoLM-taAx()9C&8&%;K5FGmB>y
+$}E&wD6>#zq0EAq1u+X^7QZZhS@R8~)${KUO?8P5idhu1C}vU20+_`wi(ghx2@8&%r=7bN96hTGfLQ>
+u0A>Nq0+<Cbi(MAFEOc4GvVdg)%L0}KENhN@_RJixWns(0mW3?~TNbt~YFX5>sAW;hqLxK1i&_@7ENW
+S2W)9f0uw`M(!j^?C3tJYoENWTQvZ!TI%WI}c=(5mdq05?YAT4%T?6TNpvCCqY#V(6o7P>5SS?IFRWu
+ePLmxV41UDkX9X<^I4mW3?~TNbt~Y+2N@sAU1m0+t0Vi&Yk@ELK^pvd+vLU}eF|f|Ug;i&Yk@ELK^Vv
+Iu1n$|96SC<{&&oGdt5^RJW_oh&$6aI)ZJohcwa1e`25S#Yx8WWmXTlLaSh{tkZHJfVD7h_Vo65y~Qz
+1t$wm*8J1%(>V~zdQQ66kCF)lLRo~eo<k3avJhnv$|96SD2q@Qp)5jKgt7=_5y~QzMJS6<7NM;9>-R#
+G1t$wm7Mv_NS#Yx8WWmXTlLaRWP8OUjI9YJA;AG7|%wBY|;AA1mqLD=-i$)fWEE-ufvS?({$O4fCB8x
+*7hpgw!zCdJw$O4fCA`3(oh%69U^Dlo-2TLQ1Miz}M8d)^5U}QaK<^Up#K^B8723ZWU7-Y>q-aTsu5L
+qCyKxBc)0+IEccSQq-yfYLIAhJMYgUCxy-!!smWYfr|kxe6;MmCLX8rd|mVPwO|cTFRkMmCM?x5?|0<
+dWo)<dS4bvRq_Iv7}g1EGd>0{_dscpQ-N2VM({8+tO|6wsc#%E#3CH?MmB~wiF>nNZ}}FgUAMv4I&#v
+Hi&Ex*&wn(WP`{CkqsgnL^g<Q4%r;CIpm#TN>Y-PBqd2nQj(M;B}qwAE>cpo6fH$d(NdT>G>B{v*&wn
+(WP`{CkqsgnL^g<Q5ZMf}8Dt~KMv#pl8$sR~J|%fd@|5H$$y1W2Bu`16l86BY*$lE7WFyE%kc}W4LB4
+AS*$lE7WHZQSkj)^QK{kVI2H6a<8DulaMv#pl8$mXLyfak)46+$yGstF;%^;gWHiK*i+4!;XW7Ef`k4
++yNJT`c|GgS5DvB_gY$A*p#9UD3}bZqF@%(0ncGsk9*%^aIKHgmi)RQ1rYp<_eGhK>y#8#*?0Z0Oj`v
+6*8t$7YVr9Gf}z{6ljM9UD3}bZqF@(6OOoL&t`W4IP^~Hgjy|*vzq+V>8FbjdzBso;NmcY~I+sv3X<j
+#^#NU8yhz^ZfxAxxUm6Qo3^(3$Ghf@%^RCHHgD|LLCoj;c)Uob`k7-h$DT8oCW#Fm8$336yfb|LQaV8
+Z9UD3}bZqF@(6OOoGsk9*%^aIKHgjy|*v#?HP!K@JhK>y#8#*?0Z0Oj~v7uu#$7YVr9Gf{db8P0=^M#
+=e9UD3}bZqF@(6M=A^Ty_ljT;*`Hg0U(*toHAW8=o2FAQze*w*5W8XGk>YHZZlsId`aBgRIIjTjp-He
+zhV*od(a<DH=(V2sTen=v+HY{uA(u@Pe<#zu^d7#lG*Vr<0NYijU0l_CfjV>8BPjLjIEF*aRny4YZ`!
+D55O28#_A8!R?h?D<zHd%x=6+v*=LHePJJ*!%T{bgG{&HeGDG*mSY!V$;Q*FAQzG*m$w=V&lcei;WkX
+E;e0ky4ZBF>0;Bx28#_A8!YyGVQ7=ZhKdap8!9$aY^K;uv6*5s#b%1l6q_kFQ*5T#OtI$+LmMhKRBWi
+&P_da}GsR|#%@ms{HdAb-*i5mRVl%~Nip>=74AnnWY^c~!v6*5s#b%1l6q_kFQ*5T#OtGz@+p?=IpV|
+`W&QSfEF}DdqPYY&>%@ms{HdAb-*i5mRVl%~Nip><8DK=Al*HE#cVnfA-ip><8DK<`QoY*+Aabn}d#)
+*v+8z(kSY?63qsOnK-lf)*8O%j_VHc4!f*d(z@Vw1!siA@rlBsNKGh<InH>LFr7#D<6s5gQ^lL~MxI5
+V0X*L&S!N4G|k6HbiWQcxR~UA!0+shKLOj8zMGDY>3zpu_0nZ#D<6s5gQ^lMC|!~&!&hC5gQ^lL~MxI
+5V0X*L&S!N4G|k6HbiWQ*buQHVnf6`LsbtE8zMGDY>3zpu_0nZ#D<6s5gQ^lL~MxI5V0X*L&SH@51Su
+0KWu*3{IL09^TXzc%@3O&Ha~2B*!-~hVe`WtKWu*3{IL09^TXzc%@3O&Ha~2B*!-~hVe`Z0hs_V0ANK
+fR^TXzc%@3O&Ha~2B*!-~hVe`Z0hs_V0A2vU1e%So5#}AttHZ^Q&*wnD8VN=7VhD{Bd8a6d-YS`4Usb
+N#YhK4<VTi(>LsbN#YriM)on;JGXY--rlu&H5F!={E!4VxM^H0=2r#HNN#4VxM^HEe3w)Uc^xQ^Tf)O
+%0nGHZ^Q&*wnD0VMD`rO%0nGHZ^Q&*wnD8VN=7VhD{Bd8a6d-YS`4UsbNFIo`aH24VxM^HEe3w)Uc^x
+Q^Tf)O%0nGHZ^Q&*wnD8VMD{7gOW`Rn;JGXY--rlu&H4K!v=;83>z3WFl=Dhz_3waqrygocZRB-6*en
+uR@khtSz)unW`)fPn-w-IY*yH)uu);7!k(|=Y)sggurXm{!p4M+2^$kOCTvXDn6NQn1HuM`4G0?$_Iw
+>@6T&8hO$eJ1HX&?6*o3eNVH3h8gbfHA5H=ueK-hq==j%9|5H=xfLfC|`31Jh$CWK80n-Df3Y(Ut6um
+NF%xJGR~5Y)7wAwXNe_N*Icr8bklnE?>OCWK808xS@iY(Ut6u-Rbu4D@iY;b6nThJy_U8xA%cY&h6(u
+!&$3!6t%D1e*vp5$v9Uo(47zY#P`gut8vRz~+F>0h<Fh2W$@59I!cHbHGM`-80ZLz-EBW0Gk0e0&E1>
+2(S@gBfv(0jQ|?~HUew}*a)zD26_hA46qqsGr&fGP5+wyHT`S)*YvOHU(>&)e@*|I{&mklkN+C~HU4Y
+**YvOHU(>&)e@*|I{x$t;`q%WY>0i^oJ~Pnczs7$J{u=x>_-pXj;IF}7gTDrU4gMPZHTY}r*Wj<u4D{
+r$$zOxN27e9y8vHf*Yw*|Lufbn~zXpE|{+jtU^XoGMJ@jkl*UYb(Uo*dEe$D)v`8D%v=GV-xnO`%%W`
+525`piHN{hIkT^K0hU%&(bWGrwkj&HS4AHS=rc*UYb(Uo*cxGtfi7W`525n)x;JYv$L?ubE#nzh-{T{
+F?bS^K0hU%&*T3^w6)FUo*dEe$D)v`8D%v=GV-xnO`%%W`525n)x;J>*2n}eU1AX_ciWo+}F6TabJ_Z
+CVfr%n)Eg4Ytq-GuSs7&XP_s2P5PSjHR)^8*QBpWUz5HjeNFnB^fl>g($}P~Nnbx_peKDz`kM4L>1)#
+0q_0U|lfEW>P5PSjHR)^8*QBpWU(YebCVfr%n)Eg4Ytq-GuSs8%z9xN5`kM4L>1)#0q_0U|KWCsPeNF
+nB^fl>g($}P~Nnb<0hI|eA8uB&dYslA-uNhxIXP}3C4fz`KHRNl^*L<(}Uh}=?d(HQn?=|0RzSnrK@m
+|kgZXdpP(B46N2kjlSchKHJdk5_uw0F?nL3;=79kO@G-XVL3?A;k&l3bEpl3bE3NtPr_k|oKKWVy(aV
+o9;2SW;{$4D^TU9jbSz-l2Mj>K&?gsNSJ^hw2@wcc|WhdI#zqsCS^=fqHj_AxTJ*lB6UlNlKEEq$DXx
+x=2cqQj`=WMM+_xKUD8fy+id5)jL%0P`yL-4%ItU?@+x%^$yfKQ13v!1N9EnyE8nJ97&EON0KASk>p5
+nBsr2C7kNtYl;SDHQ;Me)&#9EBBu`16lH65)sNSJ^hw2@wcc|W>dWY&AsCS^=fqDn(9jN#2P`yL-4%I
+tU?@+x%^$yiLRPRu|L-h{TJ5cXHy#w_Q)H_h`&QR4;y+id5)jL%0P`yL-4%ItU?@+x%^$yfKQ13v!1N
+9EnyE9bvRPRu|L-h{TJ5=vby+id5)jL%0P`v~74%9nP??Al+_3jK+J=Hr@?@+x%^$yiLRPRu|L-h{TJ
+5=vLy#w_Q)H_h`K)pLdRZsN})jL%0P`yL-4%ItU?@+x%^$yiLQ13v!1N9EnJM*ti9IAJy-l2Mj>K&?g
+sNSJ^hw2@wcc|W>dI#zqsCS^=fqDn(y*ohf0KEhB4$wP5?*P5S@(#;8EbpMagYpi_J1Fm<yo2%%%DXd
+E^(^nOyu<Pi%R4OZu)M?a4$C_z@1VSc@(#*7DDR-WgYxbSRXxi)Ebp+q!}1QxJ1p<8yu<Pi$~!3UpuB
+_f4$3<y@1VRpLsif64$C_%@36eX@(#;8Ebp+qgYpi_J1Fm<yo2%%$~!3U&QR5}yu<Pi%R4OZu)M?a4$
+C_%@1VSc@(#*7DDR-WgYwQC7d$NQu)M?a4$C_%@36eX@(#;8EbpMagYpi_J1Fm<yo2%%%6oTM-eGx%<
+sFuHSl(fIhvglXcUay*d57X1igzg9p?HVl9g25nsOmA^!FUJb9gKG{-obbW;~k23DBhuXhvFTIcPQSW
+c!%QM8LE1WcQD?;cn9MhjCU~J!FY$_9g24--l2Gh;vI^2DBhuXcZRAS;~k85Fy6s<2jd-#cQD?ec!%O
+0igzg9p?HVl9g24--kqVU$9M<h9gKG{-obbW;~k85DBhuXhvFTIcPQSWc!%O0ig#zI>M`EIcn9MhjCU
+~J!FUJb9g24--l2Gh;vI^2DBhuXXU{;7@eam281G=bgYgc=I~ea^yhHI0#XA)5P`pF&4#hhZ@66vtAB
+=Y}-obbW;~k85Fy6s<2jd-zcPQSWc!%O0igzg9p?GKhLF|L^4#qne?_j)x@eam281G=bL-7v9I~4Cwy
+hHI0#XA)5#0>Np?_j)x@eam281G=bgYgc=I~4CwyhHI0#XA)5P`pF&PRu}$@eam281G=bgYgc=I~ea^
+yhHI0#XA)5P`pF&4#hhZ@5BuB81G=bgYgc=I~ea^yo2!$#yb@6P`pF&4#hhZ?@+u$@lMP@kMR!1I~ea
+^yo2!$#yc4AV7x=|4#hhZ?@+u$@eaj16z}8=^ce48yo2!$#yc4AV7!Cz4#qna?@+u$@eaj16z@>HL-9
+_|K#%bb#yc4AV7!Cz4#qne?_j(`@eaj16z@>HL-7v9I~4Ea4D=Z9V7!Cz4#qne?_j)x@eam26z@>HL-
+7v9I~4CwyhHI$&Ondx4#qne?_j)x@eam281G=bL-7v9I~4CwyhHI0#XA)5<P7u}?_j)x@eam281G=bg
+Ygc=I~4CwyhHI0#XA)5P`pF&&irc=2jd-#cQD?;cn9MhjCU~J!FY$_9g24--l2Gh;vI^2DBh_V=rP{G
+cn9MhjCU~J!FUJb9gKG<-l2Gh;vI^2DBhuXhvJ?2!q9{94#qne?_j)x@eam281G=bL-7v9I~4CwyhHI
+0#XA)5)C}|(?_j)x@eam281G=bgYgc=I~4CwyhHI0#XA)5P`pF&PR&4%@eam281G=bgYgc=I~ea^yhH
+I0#XA)5P`pF&4#hhZ@6-(R81G=bgYgc=I~ea^yo2!$#yb@6P`pF&4#hhZ?@+u$@y`5}-NASV<NZG!+p
+!75KmY{6{HqWsa_C5dx=~;!78341vG<c}VPW9Jcw#&;o)}MzC&rWFN%5q3QamZ16i<q0-k8OBVmvXP7
+*C8R#uMX-@uYZCJSm<OPl_kSlj7NC=wm!Fo)}MzC&m-wiSfjEQamZ16i<pL#gpPm@oY2nF`gJtj3>qu
+<NfFP7-L=D=ka;jm+Sr>;|EYn0|XQR000O8^OY7y1+&KF83O<Siv|DyGynhqaA|NaUv_0~WN&gWa%FL
+KWpi|MFK}UFYhh<)b1!3PVRB?;bT4IfV{3A7a&KZ~axQRrl~he{+d2@u`&TULivu`n+=mCWFX%y5?9_
+<AAlWE#GH7XK6QxLlq!K8<zC(YdZ5-fLCuzAmoS9v1yM6hg{@{KzrST{kOtS&?$Ft;t-pOsZ+O*g*&8
+*x=EfgiDV3Lk1^CFcJk+B=f5sO-<)RC56BJ+-)SeYGLG9~#77|5Tpce&UxLRD33nF}wCcKEbAWA`5br
+n#|)cy3vEE@<8C5#L@VY{1$zxN7UElUz~n%a_l9xR@SMtQ4go*HOxj?GqOVC5h4A%609H?YOJ|5Fwff
+`-A3EZLF;5n!wPLH#Bej*65>tSfk|H3flyiO3O^nl>QmSZmBUf<wBF?Lb?MyjPN9M@zGe+SEi6<6GF8
+vASbZ)Y%6#2@P4|aF*`7&d)92BW?rwA%uzg+Icvux9PL8PZC{-l1|r#!2n`M3_wL>jOPq(hpV&G?=u^
+}?-F8ImlCjhgKJZ63ccJhKq#IJ`hCNkA7cF>IT9DLx*e$%r3jiVX8Dm6irrL2?t1b|bOW#L}Z?ols;^
+`xOkJB`sE*@`@3#W04{vD8HwNnznP-TVo4Tu9^GDwqesEGTc@o4cF*bhgG=^)E!I7=y}d7Lgr$#NX0G
++(CkSvH7>GUk)T!OTl0pDiu{Ss6H4uoqIfgY-wV*MXoaXe*u=ZO&O@qDbHu*)@OFLuK@4uNNh+hTf7a
+NgLm#XDcx~n7%jN1<GrGyBPXB>e9bGBnkaPp#>Srhq46pP#I&p)HlwD)Jcrmn@_#o&2{gen;y|JL*%`
+O2GhI$xkTEnR%uN|QMJyu!aDXlYu3<@{)L}tQY$a}jpqcX8T<ME^jL%KBK5|3j9Mk~t<-!fOZF#OIGG
+$yP9=aj&s~`5W5HmVJfwHO9K={hjl(KB({rx+Ejyb`u&q?9)ykD{?hrii>s+RdWr1CR$tY>Hl2|#bRm
+ZU^kMZzaE{uLpKWB%pXjL86PU!0e;hkT}zA1ep`32s8ecz7G9|#t!(Ia>kwqd_Ic5<af#eWL4>}#v(1
+8%r}NI$fG0#Hi>1QY-O00;o{l@>=OPoosyKmY(ZhyVaG0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>
+VP|D?FJow7a%5$6FJ*OOba!TQWpOTWd6k`SuO-Ei=HK5>QIfS<TWaIVKUEoJuSdcd&!Dw20<4kujn%-
+#w5I81ZZ}5kr{DPfqI$g2c%)rk9QvMfDl029GUACRp3L);pZw$hw%>gC&mVof{r;m5KK%Hz54Yd^{*N
+F0&+WhX?oS?mvVH!|<Mzj=7k_>6>iNs<gV$d@ZZF<!e|Y`BU%Y(zJpcao+wWh!`11MN*H7D*FJC;qdb
+@p@-+Axh-K8JBdhzx}UisU{zdgQu{oUi!_WJAX?LTNUzyI;$+c#f6|L*bsYWBtJS8wvqr|(`r`A0Uqe
+e(LL-v9FH@%h`wueL9Kw8j5?tLIO&_1%wot8d<JKmFimTf45vCjQ~q-`n1Q`EvV|@4neSeSGuy^tZ>a
+>PbF${p#)0i!Z*<$NcG=pZzl5*|tACfBLuWlNT@l`t;)4?dMze@a^{Y_4dh+Z@+o{>VEzls+TXmczXW
+yBct~9)8pgz=JnTaf5?vgYWwl^_uH4xUu{p1U%hx!&F4V8-JZYt>KCt{^7?OIfA!+)A9?HhS6}7h(Cf
+F4Pv5?IH-`WG@t?NeKfcMHZvXuF>hUR`=9BNgc=_VXy!iJozI=T3=5c%eX8Vr!-hA`-k@_7S`Rwk<_P
+f{FyrcW8?eRr^&b|Nk`1B^<+4_6!-QDgMpKUqUKYjj|{^x9d$Av%3PXD;Q%-MK)cjbHka0veXq+e|>U
+fJw7IrjOFY%m}6hcv70i^uKzH;-R`|MFS3kl)?@^`p=K<&S^*e0%@nKX3o~{->Y5|MBO4{#AZAM>;>v
+`Ky**eEVIR;8$Bd)zkA=Z-2~=X9Iuu@Y4_eC9in@Hy{1}qtE|L?|=8v=O2Ig*=O7D{`l$k{`SfHpML(
+)2Y>qg`=4%~{OQwA{`lF4?`_*>kB{$$n8Ex<gZv*u{B<_@?du%LuO8n%fAR87J^G(>df#LRUw*ZH^Za
+j*In7@_zDSGOp63$z?#KVHIehv0)n9$yyyWi#`m625*W0VtZ=Y>HJiSQUN$dMhvzx8_eSV*%(SP~gv+
+Zd4rRT5y?PX5EXL<SWUVNQR|L*1M*H6#3-@JbFmaqQd{k(SXt?fT=V{hB`r_b`8|LVW(!;gRa|L9bH@
+{kRG_@@s(|5sMTzkmPu>dQ3C=U=>htd*7qgn4}Z^!nTFix+=AwjW+U{Tr6><M#RU7e73I_21J0<sy0h
+l->U%zxA4D%PYTs_2r9~`NjY7&D*!%{qiS2{P4qjk6(TN-rFC38SNL}<`qxB_$Th&dTiUzfBsL~{@(2
+UZ249{&UbGeD-PZ|T7UP}n)O!O`@G+ZYsBQjea8LPwT>80{YPItKHq-(&eZALr;Pvh>vwJa;-~HB_dk
+EO{kp%WQ~ES}n2-FQuX~jjW^aD+(_`-K=lQRn{d#||?MJP!AHPdS`SKs#?)euikAD0&Y>hs>++M%R_5
+0>6tL)qFzx-zVCcT}WCH>H+zy0*#!Fl=1=YRg>qYvKy{a=3j(Py83`q6Lx^!Z1B{P-`QzMucAXE+$f5
+59T+gw3VB{rexEbWlv<_Uv6F@BilaAO7XDkN($(c~Rup-K}%o=db^M)vvdoKBPZw&syyJvv%y~v+VTo
+tj*D$rN@qE`RSD}(fQZ;Y>l?(2W#c`_Wo>~Ex(*I_^ijCUv7MwGqYxY7;7AP(HPH0pV?Z@cK*5Y4%?j
+1(zr+dtL2qf-mABm&syWt^SGXkIJtE{pN+Zam(Mw>-6I|v-O4ZIw??)x_cQM~^Ts{fe7243)^=R^UUu
+$TyH0Mi1^u1*uOpt#e)%AL%45>*nmG>n)qK12k{6A(p0%@a(aHzuJ=<;Bx4dEYcjounjGZsfXXD7>8#
+|rueKHtVwR`Ok*$tj~^KtWAv2vv6Vlevkj9ce}=TP-HIz7rOW964~<uh{mUh8(pjvcle`}vTMv7Yt)$
+cB!c`N+$+GyGQGuyN%@_gnTTpWcyX8i)OD&+-B3)0v;1j1BGf{;+uPKCkT7mGhA=iwDnnz8KYW9=zn>
+ox8&r%u&8K#$u@Q4zoVcZpt2F#6u2m{?SKp<B73d7)5r0XKwq|ndj`sN^@#+?z}f?JfCJxTFW`lxx1L
+(nzuM|-rCBy_ba>LsJ0xVzUM=xN%?#`)1KyWElf8*$X?|gG_jR`=9dp{!(+bd@7f=>E1S%R^y#w;M?P
+Oq-@_nBK3~Vg@*#7c^45z^G`fG}nD?<5^GjpP+l-^^A>w?<p=Dj<SoW2Tw~-^Voti23d(pT3%GM4>Iw
+vkCV=$qt!6Rp|t;YEFM)$kzUb({C$l1!l$}ec!Gq22%wv8MS>?a);4QbtZhiwEl!q8%8IRugSI`e^#U
+j4mVJfsZ{h8C|d7fXI|cUD7V#_7jNyXj}r7hshA;)dBq+8OqU{kCrJY8_uA=WRK$_UOQ{Sj?SnZ8?pt
+rD5gFco9aNgOLkNo61KDe9MdzKaw+@y{>kT{UINmc004%b9~cC^1)d+__$nx_!w8zKKQiJK^#(kxsN0
+7HF(U$nD<_GcAe)#K1;eXJY0UC)xeTyhaQMQj=_xQ<mEcau4-%-OOoZ6HqK>?QjT1U93|V0{?Hk>Y?t
+X|+<e?C4LsKz%Y=1&&``P)eCNPZ<h=r$YAhYcFw)NF*vo!_+WF1={>o`jm&3X^vCC{I??tz;LOf;y4E
+)H&Jmh=%<&N10lc%RRnDe0B{OWuddZ+F>%FA=Y8<#Jx%sb@dV<?R)PaG(wfEAu66N*PUv3OTa?{~W+=
+k3f6<`ZOBwZWNJ=8)mp(x&qc!8mwp9(=bAJPMI(&oD!v#uRv(wW{5Dra$X0SJvlZ2D1zKK>6p%gV<zD
+Y$R`!mcVS~0&gpABn{B#a=N)!>(%brAF^ATE(?JrH1qj#Vgq|Pc@U;d-x(|zy!p;BZu-gXVB}aF8G`Z
+-XSKT``#XFahcIvhIXx3wP3JS|zRyQPX@Gfa{<R!AoX>P!3?fV#&tB_zq$6c~o$-b1%Q-r@F}@&8+7N
+rtb5}I$E&Dc%E7xw&TKuU|T-?hz#A?X6iBHmZU=IyPvoYM+4_8z^sgtspvK8D5@0g~{h#1Bv?kYRBqU
+<4^Mf!FAXheybD=J4MzkjAvOaJFr36x9}kCdaW`QsSz=7-BPm+&BD<|OUaF1Y0EjyR38TnQC9PZ-;9q
+Vj_aBMy8<cWSY?^p<=|s1hh-fXW3stKGyU*(8=vB*zWp<6$0oo3tsHHFwB$(ecyyug+xTgRdIUg@0mL
+@N5JI+Xa{4owC!e%0^f)x&wL&jnJ)WkFvFK>K$?EgyJjj-}{lHe=I$G*+WZQlC~Utf6zDvgHJqhOieH
+&LMiLQuY;6`Tmy&OoBoVd({l*&UuD1fln-f~t_Gr2Fuv1eg^R*%0T#E*sSRC2P9Es3odn8B+}^_`{h(
+F5iA$KhG#&g2vz0@ZkH)g<ETQcH9CYIMIWTdM<-vMe`Sh(37Mb$~8XNOo#~oaf(LODNWd(Fdgc>xSb9
+v&~5@KLZ47|Y}<5|OXaRCDz2N(SXk{Q)*;*$K9GXpqDCy@&w2Llv2#n3A!Dlccsh@~s=8I53+jwXQ{w
+nV4TCG4p6mTM^2v8y3*-!!IAf)~Di(EUNU>EPc13~a*7E3IYF)e$*B)7az_j<f8cCoX9K&RoK*UP475
+Ov*uwB%np$(v2qJ%fM%ViwK5$5T3jE1!rAxC}$^e3D9A?bnYOJ*-1CBRXlt`5&%PRC6JF>=YwRs!Wrg
+#x`YQ`Nn5OT6PI)s^+i7hi<#A)u3^W$9NYm+%4{*n=_@Z_HsLOZn*@cNCwNc5RP#2#CH(~I1id_P2zY
+p2TGt6T6B?FDI?H@;jJ-Q@`jHc_07&cF&E0O|lF5q7iP9lp8A1m`0H9+|@dl_YjVL<^Fy1w`i(8YP9Y
+g{Q`<(YUBrZvi!E^#0*3`MCfv@R2=j4f$GDXYQuzVa+XAR6Djyv#5hgWEOce_CzaM(+{6CfY7m(~U7p
+@YZ72_G&}JS+pxFgAS500bw3+QGy`q)~o-T+G|WvPu`3^QQM_7y;u~-Y};J8zEO@HRSR;c%;SCV9A&`
+fIt5vD6eWar#Srs2yB7$7Y-MkI>8&HqabGiFCGgwFed{~hS)$^m}h><_qcVhJT~1b@eSl5q&+49P+W3
+~!%YOW7Mv^N8^kx@UBm0<z_V8Iu~=N@P33oTvLABcFwfb+d|AvX!(CXnvHXaoq5#Im)EHQ`i50Hhhg+
+m>OvZJ$3ocoeqHFo!sKNzAu$o5P=EX#TJI@XvJBdiAaZ}5<cm~|y9XF@A<RpazGYw-7SfOFHe2}AL;@
+W5ue~SGs!bgzg7zuFri7q-3>7KWXdBX~@_tDI!PkI@qwM;RC0sd@aVp5hq%DS9T%#M-n`ue@bA+7Ntn
+_w20ve`5&7v#d?T!YNaGA}f~Vt}UHX+eXPl%L{q4!?J|3obc$3_|e~hRw@0fO~1W5<HeZZf8ZY2D|Y!
++vsGz%j9rPAjYV;nIJ#&<8;~yARUYY&#jxqx}19;$)2N>)(_V)XbnGkm7U{;rVfb-y>Z`hFmL$Z<6w$
+K9s<a&a|grYiTBIQ$nW7)Ph9CCqQgbwCs#->-2F{l(nz`25Mc1x!lq91y`={T>~b&|8S{1~ityOU5GN
+^NKBiEB1oyhzO<a<;%31^~9Nn*y4-xE7?iEHq;ye+d1LV_27<ktYZX1-<q(hFg?DtGuGRoJwQuWu42;
+pPrUK)0gQPR~kY<tcVibk08hPUG@+q?cIDoej^WWojmRCeS`MhaICi>YHC9f}KkoVY`nh+G5Nb5=+?A
+MjydP*HZ48PY?pR2)Bm2)vVh!5!jboZY2i!Rj<EgT=WI5sGdI?>tOn!1Yma=!7rnOi%05B4n9Uz(5GX
+Ob-TGD6pH~EXRS=W;yT<nkyPGcNYFV;Y&{a$pD9wYzdkLWO&%T$myXC?0o5t=^(~42vxZN%xxK^8~2^
+y5*&Zp3Z&G;KU^T6;lG$r%&s%=mncg(nlJq_WDf1-!gTf1H4f<~m^XkES)2UIWjqFx?gvX4P$>c&@-@
+aB%){Ka$Tkw1<HUCtZ<G!r=WUj~vnokXPPnS-CE;dWHpwtvpjeZ=`jdEkF-jNHND_D6ekWmM;>oFF1o
+AQL2N?l2l9{z(%={E`Lv%budp;bQoZmCkfblw`#y6woL-)orM=M_%FLoae1Jtl>elQp+rp%-W#+C>QB
+Y;1(UC{iyaY$UU3_J<Wv$adhz$Z4=1+0$Amc-$9iBnH}rwgI;flRbt0nOieOFE&<-*A0oO-=SAxckni
+f<If6%Q+3Ln16-Vi-rgSxL)FMIzN!aJ9|i6vH&jWoN~z%85cIVW&vK(<&(vapIptn)Oe)>mGB@eVi|-
+?_$}K_Oqlo=2Lk$TkTF}6#vyo;Oip=ErhPljANfjjZ|!BKbHoctlcVM>5|?z?*=_(1vE#tNK^}noZC1
+$aEFEZOL4i?t7nPBa=Vb#F-J}g@H*rZbW50@#CPM^eu-fzKaZxPNBbRH37+!#$2FIC$nomeX?0}8@6T
+0b+^8qf|$c2~D^{^fwIVX3^%Z@L~g*#z=H@x{qh;N{`%r|)v*AHUCjVCjs%Q#=?8+1AZ!6p>=bu5`d&
+Zhxo(Igb@kUJ%fnPl<#TxJ9pe!A=-5|>Pq^~)t;h=mSbP<Tx75>r42=Q08F=Thsy(KSU+P1_r|WAeA+
+f75mC4>ZmfFiFN9(qcjfWPh=d-2r0F5;VZynUG+1VcaIF?ed7AmK#4@;u6e+z&fD=5-a$mw2NQI9cGF
+}^4l>i)`}~H(nUM%xzr?a2r9vL!6i-J$an||OM2bu+h$$j3>P><V2Xz+ZV)fwcwN!}Oaai0@MXIP^?-
+t&k~d5l_H(kL513U5j~oZh?xb)2i?KWzG&u>vk~NK}dO&)z`Opb=c{>?i%?UHL2EsgF%hf<K+oeH?%s
+bf<FrO594zs%^EnoikN?ej{fcu)hRyG3z7jKY~h2^acp9n%AOUO<*(sq9lG=Lb;puPM<I^7fhU~8l-t
+cyhmf@sWPaZ-Z0gd!%#CTUn;!HnQAGJu{*rcN5ZX4SLOh4+WvYCimeV@YSq9b=)C2M&y=l}*vNvT4GM
+P=Lh4wDv51GQ&Www<$h@++F%rc)4ZKut}#$d=tdG=pR3Vu~BlGqpq;449i&6ZsL-3Ry>-(>0p5kz6zL
+X2H>Vk*R!zHf)VL(|G*z|+ht^?deqyyaahb-uC3`5<32mlb<}9VrFR@N{7$o4&|KS~|5=4DQsYDHp+V
+jK4dUDMo?sz)vG7lZ+lM@id8P%pfx{&st3xKqx~#dhfY<a7lS*yAW0Vkv!69R{JuJuBZZ3qvHc~aPYo
+zzRxlAz16&6FeqT+Fevcx5$ZpUK2ME^!yY6McLmOoCOUOM1#9>mZt;l^F@g@-w1jc-Ej^pgj}C{u|0h
+RtXUa`waTgF%SLBw%w?*j<2jyW_1{d1)y5{CB(5ak`}}@Z6-f(RLvh>l5TY1yd~b3@-fHu`_)POg{}3
+l*MOUx1BX{3E`i@)P@bh)zLVt&Y`V3zIX~MA1wM&OBhm&2;PAyi)qZ4S{IEv4p6S&3_8xz{NL(AcR=l
+yE(dOL_()LTq-TYR0g04<$TO>5amgt^X;JCqg|#JIgQ;ZPGAIHxSt|iI4N{w<X3!$h;(K_d%ea}&f07
+TGdl<Y-|H!ilwim?Zgd~J2*|6G8-!-kRFWm+C0{tB>(^d1<6PJw3x|$#q0_dR0!xc?NOD_j?i9ycV<<
+}1wH-YJDx=2_L_^7!?wM!jmTID!>n*hP8EP|v-IO87<M^##cDFY#0AzsHd89_tJ^=>zDNqSu-Thz(Wi
+c@98@7Bm5>QmBBptl%)Nk?P*^n5cRpB3NI+cR%G-(@WZRF7DolX7>87@Hmw=470eP(a{zlDKZ!nRzp}
+ZRU?<za72=dvAb_NpWP&TADavOMq0dE|&=-(utNAevb)V5LHb!5WqBVy#|c*!=wZ`EI%KDRWnLBS?V~
+m8Ig)0VH~<Qq=eLouYuWxWqtoVs@=pT{gjt;qG%2H-mu0TODvzdlO^c@Zyg3|lStOv71r@xrsA`*-~3
+08FKD&|1R<<(Fz{zko}{ZAYrA3o8)@Zc5rv{*gK27{r0uBjs_`AvapW4*VO->PfYp9FTiPn_xQSH&*O
+rd2P#u9E!RrF#2jziw-TrKVONiguK~)EYF(BVkKXZwpot}#}^yd05+Aw&YvV24ia&|N%wo4snmHlGxq
+$d?!O**H=J(5%lQkKoE8xVrJss|?Y)DvI8;oF~O`t`6;*%?qUU1px_d}D=dmqQEtvRmh6QfQ-F>NzE$
+!LCq7-K2NF<MHhJNuH0@Zr*^_U`i$qGXk=e<6yl3t`8FZX<Rua3kG9OnrL=8YaAxHWS!0jXj=cr7(uW
+#N;89iy5T|=Rjf|kw1WaN51|tSw{k&{YZU$NFSw+ezkz5@Vj1<f*~A-m-}HJof=(U;W0w=a6hiBUTVx
+#0+n?oH%yprL0y1w0Bz1xR$t*192(}7ro(@-rjQnPr;_ZCkSE#`duljk91ANJKFa&bmE=T~}u+ll@Qz
+w^u8~!}SHoiA_VWbH&S5uQwxa0trs04s)8ofAjXMx0uD100y#11^J>|zNMK31CU#FY*I1v$9PU}!gS$
+--k~r!me`V>62;qnxCxJP44eVS5vXVJIzuqgsb$=dK71Ph9+2VkFm_N*cIyX92H^l`oY?pvas>2m~1e
+vS-t-3+@RrK7khvu>9m8X_q>Vm8ipdGq<}gnLnski@2^M%yd~rtHL9+cz$JBCLwq?3gOd=1MOPJaUHX
+ScERl>CMCpoml}myDvd*qia}edxQ0>@vJY6h?r`^e9H`@1yfj{rnGDjzC3eJ!7QTL($sMY#6yp((0qy
+x^orEUN;<)a1(U(LwjbJr&o7CRvIUc@u$Nb;}*h>xDKmr=Fgai+?s^U;}|HO2OCk-eP^;dH~uuJl32D
+!`F;?IU0;34+}fW_j6f;R&CY&CLazbADZ?Y&jmKI1VnxLD{*Fb=T5pE80LOh`@(J;56l<3&`xyrR}|T
+IfUXrCws5wHm9tbdVBnTUIr}N0=$h7%x1bk%RSv&S&Y1&U<`M_!3rS-WXY2SF~mac{Cp!<J8S}8GM!>
+Dwv5qOfv__jfDdrSLp^5Nv5CN<uwSD9RSyqT7t+ytP5}6ES6nVKJd;NDgcQJy$o_g>qhK&`-c;K3HWF
+`tFVg`9L7z0S!F@%Syplhk2p}BJFd4Q2<SqWrgE=We;4|aWqQjKG>dD+pJ6p5j4I+zfF=pWhR|RPpWZ
+T`r6F~sLz4e)H*v`TS#{iD>tb$Tg36ThvTumiPVWGSnAZ9b35b}Ru_?G0POWqxQ{C&;ur8u7Yo_6bGW
+Pl>P6C#X3jE^ohV6}3kxtPDF2@=sP;wQ2HcR)~RmVbZOjneByI4o~9Y&Sf!$3PQC@18oGL6D<pe%&*p
+jtfU%|D=f&5Nv=FpRJn(wmy83!&S68JsCt#VDpK9PTe>yPR&!`KmaC?scJD%Zs3PtlCO@QE5Enk|+QZ
+LA%M01wRW5<U`{sb)&*y8?Sjo_qr%pj4pO;YA^>6r0mAJ2%vqa^>zJFP&F*kOUUIkT@0`j3Fh5y;*wT
+^2lY_^Ju}rq?qIm?oow}!E-(fIsRm@lXyqqK&-ik?^G$TG9d+11EH-%Xg=gt_|Lg*(rfz_ByyWV3jK^
+rQT-!jR-Ao5@=ACDudu<r_9L1fIMW3uZOpMfj5@aYG<m1AXAqVbN9!TdiP2i^p^e}ke<B+%{zfT^Z#L
+)y(#RAYwSrPneB1xK59me8NdI6uhsE{xC-wx=R5cIZ-?lqKTPKhOq2$Qtfpie$5?1tWhk}c(d!n%|cj
+}l?vq>6^EcG0~KShE3&>vG;liC4TFpJ*sn)5uDtlF^hf?ScYq9~ZI?MHn}JK=<0=cP2%Q#qg{{SB8TQ
+(Erqu$w85$4UDpp$#ueFe&E^$*PHYncf0T<t(F@6nN>HHN;D4Z&?rOYGh--ZJaEVzN^q1=92Q~dJ5RI
+yvNLqAlVVDv2-m=U1MO^=j9#TL5hub&AxCJ&UXtlbfVzJYJ^=DSS=&YTiUyL5fNz29;D6yu8i-~birc
+s=!qM4;k}!|A@B<kFe8M4%e3XAc_quHWrwzK9B1V3Jh)X>*4#h68_2Amjrg+lO^;R<CZJ<)o#KpMVy{
+MaBPOO~<G&7(l;SLFBpDNUq2%8pMu4B0LL%-v_m_bdvaLIx06&8XlkKB^HxAFjLa+GH~+adaCnb<@{6
+(XHzdQ3&Kmgbj>^KKWu1ow@yi$DcmatI~82xsM<2z3hB-Vj@<q(r|%A!fm_3|3yBr3*UHy$(c-V>tFI
+0QA?ymrkJz9(q9D3>NsL_|?Fj3nkowI$MWO9opjPce|(SO;?65Q2B#{Ryo0LiX~IX+8z+NgZf*OVpHT
+NuBSNOfLh}!{rZ9KwV^;7m)L?z(Irl0K>|O90(<Dy94c?a(}eSeTAO^n>+LY8yMBD2dtK@^hJF>1ofS
+4DnNwU6#f`vZ?aCyzTFZQ<saAX)2arVN-G{o@CXH?^G1v#$;2|?a@dv_eNm8=>maNXH^bNnqrV+^PK~
+A}-kl1b~zFoRQ76;!_$Jxm!W$zS`%Ox2C0X=;gs_-%WQjO1QbbzWZa-e%{h+|qe*zzxoGjSAhh^cPRI
+U<a?=foupYj>D3S-<Y;(SsYF)h=}$5HU*{@8~^=2qErc+|;zIl_!6bAq(jqrc9UuQMI}JTgBr8-Rl%>
+b(6SV4ng)qRf(9C!QfteWgTKRcf=r0a;P>t!B^I}=Z*b16a#Vbn643t6)j7wTnRsESrF2=%l$*Q46Lh
+(dM{8*S2bVy$phUh)hCdvI{*xxkUaQn3L9g%2MQ|TP~t<lJ1oWOPo?_s`sePg%RhAMIEU{*E*y>nXm(
+oC1KTv|RX@c5SjQ~ag+gha64}J#5RMnFKG3~(^T^fo04`>FL;>KG42LRVUr?j*Z7HgCy}>9dkc-lD$&
+Z#jsCzZ+!U-(ddbUV3*qX)?p2X+{JBwP43I+LjvklF|vEj>8O;P!s(!G)zOGRB(YKF`sBVbrIs)R%C0
+P1<1Qj^vTmk!CNVG+42?6}7_amiE-M7gFBXBVoD1^>Dfv|J)Uctlv5h94P}1t(jga&5AhJ1z%q@hE=s
+K=;~Dx0|TV0I4-Yyo`ZQ+9l_Reo~VJ%1gpdCKwZ|^6*sOQtKGqYhH6={?KHnc@B_d2@!dZwPM_~jH(j
+hlG!CPQ@$j<q)+y)>=)f@V=GyQ3GHlf8f;1G(|Q0)2;oV_THyX^LZLaGG${!JdQ)aRbrQAS(7kpN$7Y
+z`WwPktCHhBe;kF;kvu2*a@?IPq5QDuYO#)0|@F@M{f$nvXE3LD{z2apouE|_LnTtzc{Dt1lvcoVl!W
+~<7b^#a@?sb9gwZUoSq!1M0<4a8jGgUiB6ew4wL6kBTV;wII0E;6cEj6|`Z-1bBU8hX`w9}Xv-jPy3K
+8R!P?g3XG$Op!B!;iQNhumvpN6G{Nj&|Dvy4Mzxuv#PDv6)`G3#N<IT(gWcG}2`bINdfuCrCes&~VpA
+${x_Y&Q{eMq$Pa+w9Xp}i91(PdV?84JvyvVGnSjG!hB@DqVe#+_xPfFZCKZ={aK3tO?GGqeAbwXbsD8
+ENfTV83Xe+QU2010S?Z<tJk-6CgKekv88N>_MjQy}zr*S`0`;I8GM1fyFoUQcNW0no$TT8qxa}9+Ya8
+yIU<j1%V4*1d88=pxzSgIaXRG&^S|QP4+GR16?v!5RK=(R8`h)UE7x(q*7Wkgx9u8p&pV50ZiY`Ot-U
+V1?Giz@a!i^4<za8ja2mJ3+B<1$Cx)M(ekGJ7+TDQ|08&Meiuue0%i&3R+tCr@f`p|*ywYzx`#mho=v
+e1DnS=376*cHqATok>RWj^GIL**%%(Ng{FCX8|0!yzsK%-r+g1`*Y71bd1NC~+G)t=>&sL4_0>#^tUS
+l0$cmO1JOD(;nzvCwo4Y+f^+*9%|L62@q>Gv^bV9@lBzO5flLoOfN}>JN(p^-s8Z2oU@#(fd}6;+{Iw
+`aU6gd@{z`lpFxlt7lk=fb5i~#uf1?@SM94i(7i52{g?Bm;=9{@+XZw?=_$q>KEV>?>FzjS$E8%dOE?
+o`EAKwgy>@E+qZ^OGbEL}?c(;ltt<8&|J@uY?b7Y#Q3S1W_E{FQXwyK_WpnDzWxVv7=5;~-&Nz_zBtl
+ph|ObfO6f3*&vq0SoI+;SWKtN616-Rl$<tHHT6Q}@kiZ0azerB#QK`%KwO<(HT44Iu~1q=h?hs}6and
+vzVTjxQ^c>IACOctRkQ3X;o&SdAdSU%Ug)Mfdm<3|9LK4|K2VwC=>DSPr`d9r{`_BjN=L-=Wk=X|-g<
+VL&@vCKxt2W7a<R1KsO#Pw=62>93b$B6zYX>O!UiRG+3(OLvUJ#b7P3?fPkf{2k}fy*7|@C&3#gq^!5
+)-oT~bQRQzj$BKg#(DDP4S-0k5zqKi{>Np3w*Eucx8AeW3CCs=rE^<~=9NjFb`PPfqAg5Cpb}2DOx^p
+;QwJy}X0;CuH7)znvWoLK~s!vkBqUtgtFqcL5K$=jryo`QshN*q-hq_nw_LLt+Z4Lsazz;UdOI?P}z5
+2D@h-wh}Ds!T|qq!G=yLit7-Roc%?>dFg?!IQWFtKeb?KgCf66{IF#b8kE=)#|!l+;b9T?D%B@kRGaO
+`(Ubm$(TwUg-7))v-m<xu5DwRQ>=U-RMBEV+e%UlGF)R>((nD%l$a)32Ul4s5f_q*6mLUy6aD#f!MH{
+G94<;si%Qj(0Gj@3wN(Kb+7IpI`uoDR1UpS$WNkmv#@}}wV-(ly}@G5JWHO-{iD+cYQ3R*oss~{(m4E
+%`;Q5>jcxsd%w#%cDy2vcJHm*rrZJqBuw#Cu>;c^?B6jEibxgVvfD<-lS)0w{bLv20F5HjK2Ht+UelA
+x&b;#Y_u(giSy$%XDov7U@rnH*uoyc)`5%vJfNbzUZT;d}6v(^3&DNhrGb+^07-%c!Fpar$Q3p%ttQz
+4N1Y1-aY2Xj}+Ha<YIJ*b8Z=+*fj<ACmUDn+#Te*)+ge7VYu<eD7?H1{Dcp|9G<(+&cWS$=4-#&!Eib
+g#?cajL;_ApqB)nUVkH4KXK!B4W~pVz#??f%^}XwJp?$tGBQHkO#Wgp-PQN+SNjp-(lgnW7yrDsy0zB
+PvmHuuD7X$1pwe0h8s?<n}0y}+CgBQ?THhW+LAu8zeRMCywKk<mFP0&ZU!V0%t{rkNSgFkeg3%IC(PV
+Kh79~eZ}N_;K_qQ+np=`Es%TPqzl;6_EP@lay&J|?`E_)!4Y-{>@&&H7?!esFvK9!*PO$%307>H7T>h
++g_gaG#*Sh^Wy4MZ|*LJB2nLkM^cmj4QFLre=_b?-nTg&CA#E~0PBk0q;wN7L4>nFO`!JrI+!2&rq3v
+N}5!ca)SHb}H-<{3gb5FHY{#&B<nfN@Ck*72X{UgxybV(?rdJMGtO7i`79<iX*0npI=B8xnKI?vWuXB
+c0Il>*sDANB2T%jKpzK|7!5&y>dmh3#^OffiN`~jHO$4<i4y?EWfg<-NYpwD7WMcPfH>=T)_!E%z|N9
+q|n2yuJRf~It?GEtQ3*d&<S7Ve@}I<CZu~ExI>d9Hs(H_`hfC&bOl3)*_C+9dr^P|zZh<j1kF}Dg>zS
+5Qrl)N*s>>rVVTYhNfF9r^euR2qN@7XCY!<XTiPCTj&NYs{+1KnYexjoJ&8uq8ew#x&Rqy|7fOi1j@=
+>0S!U-EBUafpdonJ0#CxB_iSD(LZ4YWkIXy%xl2pi$si0b!BsJPmf_a%VPHfQOp6>7i#M~P=Y8=qLj;
+^+ybpiVCC1eYynBjI`@pR9so0IGypb~zPb7$5v8+A|7ez!{<#{|B%o3N}<(kxCR=^QE1hWqN6Y@{ulw
+RrcCK$T1dlkN}OhINssc29J8p3&j@1RF=8bQZ1^R~)lvnk;p!E-OgVS?-wm9WG%2ckK&3(Y+4JjT?@s
+d5VncH<X_ZhGPIVnh6_fgAUc$mGg+Lq#t#3t%l|9FLj*m>LHn2W(4{j(94pMaYf<J4!6(gN35(3mp_Y
+{T-A-#QTX>n_u4IEny6sqBNR3g?k+bydEAAP?WQ?7KsL^lP5Na1rn@R0@$BLfu$VimlsO!g5hyES385
+A|>%bjyq*Iv#j+sGLSi`F6OOH}|#1q|XFAOU%6dP*~=4GynsoTO0ussLc+b-dnX^D~w_rRL;mOk}F_c
+}ZZ<gyxl`Dmyc<yR;JEsF@lf59)&H;OLJZOiT<QFv&~yKeu0?sY0i1@|=-B<|rj-6>_DcGvUe5a1Xkl
+su0FEy-m$n-atDq>nmh<V5$nmi7G0O&AtaJdlfI%RGxUAd+zN?e3{Yh>+czUC8pX&fqN7_@a9q*5;;B
+5GxQJ!iaRJsBX!}J?#=^9!`ai=~0oxVjWa9=TAiO1n2Pl5>Z)!MHd2q7gW?0(fA;WZH%~ZhVXZ7xf%%
+GySbE|E<NIj?zPFZDD87gkf#z*o}Gwrhh*)k`0DbRJxYgt1S-oe3!hFN5vJ;IC%V^(e0R7vZkh6W`o(
+gha5+QTpJ#wk4Asx5xL{U0;>?Na7YQOySo1c-CD@PjEd|ZBgbr3v$pN*Nih0=TsdbZRmwc`c7m~F0gS
+*{}^`@7Wd}IQy+NVGfu2O%e%!h<Q@(?z?nAESQ7eGxe{m$(l(7i785=`tu3DFMs1(^%8z+mQ^vgGAiC
+6ZDeL!&ZLlqE2C)8HN7(7kS+Nuv_sa=+6w#lmAv#|A6Vhr3&nsv9N$Wp(*<&w^yQk)!g$6W!~AMV&Iz
+>W$oQ@A%?RMyS0Z#FG%OCHycz2|3MMmgx@peAP|QS-6DZQ9bXmR`Q<MWfvxd2Zi`AW(x+XPN^XJPzPP
+BHq&DJSK%{sug#U>mI@Polwec#xvS8DK`%CL_XIF|X--A8izlo-yKd@zH@==<V*c-@IoVwPY}F5vUl9
+u7S=ZoRa)8B#3cX#*1&kN&NUU=^PIRvW%DhXey7L|&(%sQtiXxn=vbb3<Mo`%SgiB)4<+a9|nm2T>RH
+yM6@?wxTqztI?*K+PD8bUsfVi_+-i4vW}u%kp#4c4yh#nYbXUbmwT0>rvpAv*VZ217iQr;2m8aU0z&m
+U;9U0c~2kR_auGxf9*%Mtp0Y`6k;~$Ddr@L4=@jVh@+nRG*ooOp0Q;iHVFv*T3%Z^;|XVS1$&RnO!J%
+2_o2#b?GGhfLz!ThcZn!z9_GCn{(HKDjuVI<?uSD$8*0x87iXHe#5MWbym9XISr=4-A!#)!0Plj7rl7
+0{DZnz;~~2RH`0z_6^op%FYN)_Q+DG46}ZT4mB%Ei$%0L9IC-h@MfbYGZJnx3t=V{z9^+<2AVnWxnHc
+E3OZ(%CILK$XrLPW)3>&ZbhVHegVUUWqm@<mf(wsCRoE3Qs;Ccx;A<^0{4X}HjnBC#i+s;n)CAb_Tje
+-)@7mD;1!pp+|rWPn`63b>K9v%SVQE>(r7Dv>1Oeeb671C;_l2<bVU_#jg`=y;IFq<2h(Qa)Bt;W#Xb
+#G&)!?fIZUEM37m7Pz_u0%3(@98b15zB3`>cNLBHK4Zp%r0R({OQwpS=kx7*Hx1;JqJTQf!>roUZgmZ
+UCRRz4y8lbbhry0d`5)v90TTN#U1C-y-qA?_BzDL6B7umpr^~CGlT5j-OCER`U`nXXgApxZG`GokK$o
+YbgzxPr6CULhr4jV1<Rr=WK)x%G3+{2aMRsq=GGz~1p6IIdEDo%oakP=@B^-4)#f0&uf!ZR?+VqcIx9
+#Mi|OoU9wg~gY(?Fctp<d}@(<`<yIarHI7J<T=0S@f>^dq9Am1G-x21SAB3`UIZM#4Lb{`)5-7fkP3#
+$~p8v(7k%O4=!VcI)n*HpIP*$s!r9v0o%szQ2X90VfwdE;MnubYBJ<rY$}ZZCi=ZK6}OP>Z_hJgVn7B
+*j-%Xac2oWmODXohNppd)-)?3yOciJ}=LX0X1~<-rAWXI(4&zN<SERw|$b{_o060RP9jq5Y}-3F5Tlf
+cfqyp0ucfppeZ#9Zw&`Gd^r7S@C>Y8CGILW?mQOV>tJS@OCH{_?QT@@M9xcRNt1d)iiXN=%0|j7>8>a
+vxUsX&c{<g-Qrw!|Z5b{|73ZEmcG%ECx2!2UM-0Z{aVGM_zDMw0uAjTzCC|mEP9F%PXS6)`YkHz96Xj
+kksPd&K5uNY|i3!M8b&E#7dGinGUOCg6&$tM()_vobXN2sIIljoF@L>67IZF<d^BgUb7wW-7smM|1V4
+R-!diX%9ektUs!5ju7UEd+`W5oB|4xY=iRovkx!`+1#eo*6ZsV~taDP^G(8_H9L`-+FhCV0pvXpiq%!
+}DNBIYYA7;n6Uq@aT8D@Fj=BiyA0;<VHFf22Wn$!GXv_T2X>AJZTxg!rXL}IZe(q>U_Bq-RpK4Y<<$=
+`7M_1w8mJP`=AYRpRjvN-HL-=dUT69?gNMVIF7qr_TyN(-8JyqpK5W~h|e>eeqckGg6C1g^pm434|(>
+q(C{3u(q*3LUZ+d#a3irt^;o(<lj}J|@O^`eraN=kRWoYQk(yh$4-IhnY2gQSugltoWjXxLw6e7-f@1
+2blvSWk&t1eQjlHn5l#U5!tbDGhJZqtQ?QX0LH^2Jez9`<q^1GjgeS`ZFM7Z%!7FCN6U+}*bH<!9s8Q
+w$3PvQvXUXXLMcJyli1S5)^>@4O_zbs2*X^4qG9IsLBdafEFvozY&r7MgHNorJ0!#yC5;Aufq9ucq{B
+7CRT6>_#}_v9Qy&q;9iHBiG-u{=OwXk7+z_ec)=YXF8KfKBnt4@ckX%I$X+y4Q|9G)hU$nu6zMa6VUu
+h?~2TOvcv@OP-qt_q04@PB}fy*x0SS7r(yHz4p`NWz4QnzI&XAgs-PbUM}I`u`28oAYO6E$EY)+6=S2
+L+SOJSu3qR~m$h2AJBP7>Z$YEaI#8p|2SCVHNBl8p1u<A5?%bx7R@+aDw_oaBLqsDzM2gOgZ_1CP9)L
+@Wa}TB)r>GWml}A<_IKz0k+g3OewZCPddtGkkw+P2Bu7*8KPvY$Y&0UZrIFXUTk}0yH#vE#TE*%{Pc<
+pz);1b0-aI!<zfz)`p@w#el!|`PyLW@)WZl`IC!P8ynVUxPeyIuAP_d3?kQ(63ihlCS1YZ_6r?H;)>`
+4penH4pDEx5+cG`Rms2EOf68q2BCuoL=KI_#NB87kLso66gV&z2F-+N*5haId*r#u>-+F5^f#HLiaj7
+!?#4sQ*95kb$R?YVa)S|mbZcOr0CvEln}+RyK;G`<E|0cc`FOu>+tAm0@d(T04UFOxr2o*HY7Wfn)$k
+%`{ut`<rIvqWLz~Y7yr831(&#Q<`A~w(uvi}Q0R_+KxIc;IvL-->R$LKvPM=-^Pn4d3}2;VTIgP<XWV
+*piB%zFeBBL&P+|+tXR@7V0)%>ZD=#*pW(fvq9@QNX%B=N<?se|&8}w*evjva5l}%6)?Qw}7E^(-)@B
+keb2M~@y_NYU%gju}j>h8y>^Yc8b)Whg`=1Lh@h{It7@FHvKj*{E|@uC#&yVRM#-mq63TIybxtH)jbh
+exj?R`6`h`hYYM`$bB-LHsmA@dW0#T4gmXe)(>feZp5=&SVd7%w!|XrDn7)Pej(l+#0qdNl7S%v!%Cj
+tBUKV)*HIlDQo88)atalX<7*K<qoV(dQ=hAs0Ql+on_q~ajla8R+X-TGkmkZ;95^Pag(9-1GxXrKEQd
+u@F3*kY~)*|+FV}Y2QDlS;JbED=go;?Fb@0c31`e36B<fDRM*s@ZI;z_IiO8Kwx=?7S9#&orS8?E#61
+@U#&n@0TJA=Ik(q8SRNGI^F^}>it{P^9NVhy+baz)m+3&E9qwLW0rpRtcMViOxlcG=0b=W=LKq3RI;_
+)EMBQ+0?snXa<aBDvfXQSo1AS`(90clTHJ1Zv)&$ZyWx)mVs?<990;ZeG^8x>{vuD}Y+R{QlhNh$LHH
+@qn^uevm?o#Fx;5*T}|0H(m7Vm4*YVPyckb9rceyW0hqH2DY0``zPkPTd>%Ub$X!3Al8nPW96ahXc<%
+oHi+(@I0uyUCu8d>1v3a*CkGcia-3BfI7j5u<P+{?$xCJ=E0E7ut>hE#OJC8?snlz;CB{P@#e`OFopd
+x3QOP;^9;6Xo}sgvXJsSFA4_Lx_+s)#)ulPeA>$@{;N409fjMfY6zr068aH2|a~W>)fFYa4Z7tbDJL|
+yK-Nvh3bgvy0*=wMdvw+KS`AIPPJ13x)!@Ub?U);23SyUM=g?P_!yGmEVChPPM?!)%SEvOtSmtxo3L{
+hOlV##gi9x~@C#U6fuq`9Fu@9eH$6iC;te_(5D`UlUl5EHuOg#^sFf^`#77@6)-{2U83{gnfwrL3KTS
+dXt5rPpBFVaEA75r`$M_{oztJ)uty?$pkRLv>yp<&35&_@2W;7}w3$ekXRAJ&fI-C&6vG1M8B_^{_~P
+rGw(IiY;T#;XZ7Zt0%g+o5O{LDz@$I9tU)<RFs#sSMupTT~_|o%%Pb=_YeZZ(CQq)6CdIMT$!6rA?!v
+keKq?_9$Eo`OZ(Be8+P%v#zTR$KW^bsj&)a?q}t_vbP<)PwDLma@(%&28St3_7da^O3=amw?+k;+eFm
+(*=YzzYDB+3t<b@sZIow*a=G|ZPCCl<}9TM)zq=P%Ka1Zb0vZAYiG>;dzQshQDq!%HlCfB{6ce{y8f)
+sYBeIZeqZb-Gte3;|A8<eE4&J0g$39_p(>L*D29_ifW&k2-&K=;~B&OCC!Q-ZoG!2DL!dBLimEn=KXj
+c`)LQb;tAIipJP>GI)fy`g*U?h&+B3<jC<%Gh*wk2t$1V)qyg1+-8PQ=v*@S{kj%4xfxj@rY=*avV&h
+31}XE;BT5tPij%N>n5!<q5#aq?zxubA8c122M{vTsr|gDc4*fwcqN0Un15OQgV3q&d!-|y29!~vU;)_
+`(x4O)WAAr=2lFOl2{(e+JPHPaB9R$SM2X?ildZl2=*ig%AW1WO4N1y6CG>9BI?nF7d?2vHHXLs6Kz#
+EItf@#9eqCAyE>!(I)KYi~6rOnY;^nBqW`8{+gHSO%4${VlFO2zVKFw`B&Qx**zcZaEVYUZ>dz#RlZ=
+!ph>dZY>#S$wlQ1=J{H%Onx&4a=7gc5uwg}K<>u_o20lX9cv?I+Q_4o^3|ToQrRNDYRjLs_t{^e2MMc
+csdmX*`NrA(I@5>8cM=Z(tmldoy;Aszhm5OrehP<X+S41DjS(E_1GD^)yX`i#38ehZB;S`lfqS)b+YW
+!Shyd64NGSeZ8xX%M1qgIQ<H?RA*(m-SKXBGH+A6M3J1aBzCvuHR}w}dXh2W2X0i9G+`S3)r-kUdt{S
+D%DZ+wzeJyO_{``nJ<OGR!B<bD!m;bw$R%*@Apu6|Z7S*tH9GD)DE;K}yjPgx@Qh}~y|v>J#+2g3R4X
+5zMJ`i$@$_1)i{o&ReQ^`#-7e><LF)y{Z<y_drfZUFSaK}Oh@k`Yx>YHXowXL`50Cg`I^`eGz0R%@K+
+t}<nTcs{x*VUB5j@@O$nv}VVKxSX-S}>C_gs0!SMU58y4NO$QRnPC7$yq!yCt?u+=2$(9a?$fdInM<R
+|sGgwRn%(e@x1fxY|jnZn44WWZX`!v|Ec$i6LZ0^Qad$?U_k3XiIYzMc?g$O9YFOZYHx+rIf{Y&-HbB
+G^{$83fFMiI5Vgh>;7^RHN?`{fCTxSE0q0;ZN$}!$u17XbA9bEv+Dc?&)|CZ+;I;r>i&kEAKc^1K4Ea
+zgt?eyp@2H;EpWHt>EWK8zVzmrbMOovSknA5KEwD{c?+R9{n=t@fe8fmc}PbPFVrpo!RPLw9E}Gz`q+
+kgaLR|5)2LzBdLx45I)*re+Y3}aL1}&hRvVllN32o>U>q(pBcBULQw?R46jV7|gnzYam*2-B+vWskR2
+}dNZXC_<7?uZQu3-_vGc-NX#beXavW!{hgPWHwzH;}MeZpt?81lWt!(=_E6S#yMB99CYt?YHBFE-A!g
+g?kLc@Ml(`~w#Hu!Z^=gFWG<)!7vOq?<p1L|2<9%NPRfr3ZrhfiHs>ApN|@0bGJ?YwLwK>@@JFd5k^&
+?DB>bCsd+&^0xSe8R1O7s6hk2wBo`2WzL^`__xQWAK!lS;?-ZbFCJgM{(&Fsea)}SZZBTFeSCU+^VXl
+a{p#`U<Cp%#=y%^gefRp!V`i%A;4Bk_{r2ZnoD_fkh}(5N00T;XO67a*5=A)6I@X(|exDdd&-jHxcwp
+-XgAweEho__g?{Hw@QI&woU{hqUDJG!NgTtwU-K)X2pJ>I*2Z~u<c!$deeH&akxDwQfkSwS>1&n`yS%
+L~?u-77x>j!G$fTxI7J5>Y4EHC*EZmT(o2Umh<AfSQ@S5U=@<~SVO>*NYTaXkZAyJ;;|`3MxVyzmZJY
+LYlXeKW2v@DD*y4v5i!ybpB$fp{;Fy#!!9sGvrx6ZZqfEHAvnmBE!RIeZ7j_dr7&h=7AUGANNn4LmEA
+5F1UKVwJbrAs;AadEuSB5+zo^tp%E)KwT5iH~~rz#313a!kmK$8ic~2S{szDqF1B|6tleW4p(AR$N_?
+ieNb`_%HV<T3W}{kbu}mu23cEB-3V#~0bv%sPVNa5v%KUx*a{p`z_|ouNWcjMkSjn%K`aSkN#OVc4m;
+p~gA^}7>CtQFMxdDGg?G3TTY>)!n9YF247kgHZ4B7MfbR?Fw17zqc(H(#3MixKRRIbVv%K&QS7IyV|3
+R`IWa2?49;DYn+8iW1L9!KOFhSlBqyPaTk6xKnpqS-_ceoN;5srdH12`BU!4NKicLMYhpqBtx1ehW~4
+FLd%e%<qSF>lz4{s#qLpsx!gQh`b%C@Tjg%K#t;ST_Kx0l5%R3PD;Mq*yU3LkkqMVXB2!54=rq0$1?6
+H^IrsL8&>|bQ*>mgaJWOAQ0vT(!M~G5XceY-U%EiW_jTquEbW@hZi{4fQ$$Vu)%R_!N#6IWF4G56%<E
+;gKmNo5Q5FRF>cR|VwM-);Yw@;kwsA54eGK%!8oWT2IPIf=7yOOu_0h&f(;j8c6I+kR-l;Wg?G3TTcH
+jY)Tsg$Z6J9K08XF+4Ag7^ITz@d0=ZJaQU@in7^R2{6tleW4p(9;RF;COMZnAlWN^Us1|(s??FFP;z_
+JB|QozFm>`RQe=j~$Nuod$9Fi$5t4^v*U(jaRKQzsIeAR`D-5kWkRn*_>0h#9k#Yk^{x7v5RC1TS2Pj
+gSon>f>PhL_p66XUqi0zyu1N;5?8(FBcql5wjFffnt^y-r-7Y#e@RkXppuC;)0+!9aIg1a$Hc+2>@Zh
+p#&*buq`|8ljZ}(EHAvnmDmcqeuMqD!T#G|A8WATEl|k?J2ZnmhrzDGKuZ$Ti~`j~sFkE$m=;p9puii
+bhXR#NP|pc!XaRv4WM}~u6!JT87-0rXJPuUHfxI<lDUt)lEHC*Ewt|i_pcDc*W1u|@6m)_7Cy?ZXX$(
+c@0AU9u;h@eM)EwhJnK@9*^1?e@iLIC(kl2Kz3=k%O0wK9U-W#MWVe$ab3xH4{LW|=@j3{P#;hn+D<m
+KSy<Yn;^yl^Ep0>cnOY5}jH4qy}Fm!lZ;Krzb;?{FoyLPa|$ItNwbfM^VgtwD`6sD}mx!k`2g6#N3RK
+Pa=sapMvcv%K&QS7IwL{sCDTu;l@n7jR_(5f)@z0q_q<;DG21xX6HpjN`^7C}w%dcd!)*qX0gKTn=na
+z^VkSO2BXgY(~HsgcJc;e2}pR*>fB>E<rKN3-53xwnC~Gq<TR%7Gz^#)<p6Wq%1)y5+)v`8$r4e<OXr
+vxCF&)h$6v+fmSCF-2|eAppX`Dq9LU~u{Ws92Gyyco)naZf^tSsD2U_6B`9Wj;hnq^);{2&gXAorUIQ
+jHAov2BETqIBc>-!DNEm~pFHADd8<(J%<%M^+5?hfPgw_jo9tC>OpfnzwauOU^66}!-G^>F)GsxJ3^e
+^sTObZmVyyQFB3I&5e!yDAZg35D1Py|3aP?H5Ri=h4%@Q6X`AMAGuWc_j8xCF&4FTBH**b1eYV9#Gru
+@4gvPF)DjQwVk_1&1F5l8S%~5B3@dJ7wd%aS4i9UU-Kqu@!_2fi@zj0SEP@pvDuVZb5PuBvC<?FxaFT
+h^hkGAkG_?pqS-_ceoN;K~o-R7(&_!ekIty6YQ=D_S6KMX99*dq)pi|5o~3Mdj~+EnB|3cxDs2T0uxY
+vflMb*WCRL>pd=lXF@qXcz}5#8ULX+*O180XT!LbjmwX3XK|2u01_GuzARK}kbWp7f3Y9^9F{s=HmAj
+C+fL{-&^;kD9K{3k<?{Foy0worZSOEbPq?JLc7iLQ+27(%RK-mPfwx9+R@ay@~erCIgOCEl*ef{$Jzd
+gn;Guiw1=MVn}P)h>@6aWAK2mtey7Dp|jI8+$}004{z001-q003}la4%nWWo~3|axZdaadl;LbaO9oV
+Pk7yXJvCQV`yP=WMy<OWp!h8cW`oVVr6nJaCwzfU2ob-5Pj!YjLHj<h#Oj6scqB;<3NfoU&5d$PiV2n
+u<Cl(+FcU4zrJU|khURG>nD45=Il9V#?x+JeyBgVA5CdIN(R$(K>hJ7`Jdj%ZMWLA+%e6p+)6DJB_?N
+*j!N@GDkUOgx0E3kwN9xcExknM9Y3)$6<aa|`3o4xpR#wEs2HKL+-sQ$FO7Eiw3V@k4*=8L*ke4iEIj
+A5sSk*+uM#$3RW+{KdFmur)cgGT6Cf_8M-(eXi;(MR!H(?{=SL-p(ca3<-W%I-S6>hznh5)y=2C5~Ea
+{rS(33Ybul>&GMn9}ka$|+9gG+^FCT9x&3}Uxbn>}ShljU5xBRq`oBy|4XSk#v$mqi^yd!0j0VC~tK?
+&RV9bVXx!U`qF_*+R{Hzfm$n@mOZ89g}cWg_zsDI5iAJ(uN2P4d4&%-V#fkhr6HHIz;G0)H~hQAa==E
+>IfhBqnlMIyaMT(6uMzgm7!A?Jb00slhg;?9n8lI5TW!Lb3_}az2jo9x<EiKJ&cy$W~(K|(?@!U7mIk
+he7r?2ti~xCctnzAr6hnM%L?slFbBY7ut>h4BJPjIqvd0uKO8NmgEXb#Y(X*2<Hd56tj6(z=BvegmJT
+AKl=<XwaPyMOXO9a&mIhYl?1faW(f)`AJ1|r^?ZgwK(HTq36bbw!RsBz0R7P(Pf>H9~=q<^DwDDbfwi
+45W`Fqu0puBXri?Pq5F8$X-lF;83dXS=gC<{Ojl`*zUedBycoy4fU`Oxd#T=zcS^oUj|B5yr3nBM*68
+fmv$g*7Eb<tEz+>)2P;Y@i<v48PH2ue|Kno)es=?B|EmrUv^(>aFt_wo+s}srgiv98S`3GC7`{N&s^n
+yfD>G!C;jvAMSoRh_R4rhh=o8=Un+)b~>40U#V6rQ^L7p>`1S3nKiZrmIWrGp!Hf}=d9Nq$Es-J;kjH
+G{hof#4qwr_Y{X9J>jmANU%9?2eWmyZtpED98=XH8EOw(u@GNZ0e%&;3twqUy3bgF^R?|D&aJ_tZ*ZK
+)iO9KQH000080P~d=M>&o34eLSx0IQ4u05bpp0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXk
+l_>WppoRVlp!^GG=mRaV~Iqm7QIW9mkQT@6WF&Kv=*A+Hz%PL{`-pvw$Sa9%5ukpkxfZHwHzvtj17mO
+_Q>r`Slymlhk^F@c_HJpvXSw)X9(dc;k&Xvp@LYAOE*~{ORwXeZGD6?2}JF|LW82<IleM<X^Y{=Di=>
+KG?qg?tc5?>ESOAZ(h9KK6!j~zdgL&{_yyp53gUpsDFR|{f}=RUcPwu__V!z{c!*0-S)EH^Wp91k3M_
+z@a~~L`P=)y-oJkQ;r?lR{C0cy51v`?|NQ>l+m|nXxc|Q%`|$YYZT<7<hsP)XQ4ij}e0*9zfBAI(;@$
+nL?VF!`;(z_B7f(Fvho9=JzI(U*;*(!)?Yho-#6NudhucT5UvJNO@7wM9{oDJezuv!EGx_B4&AX?EZ+
+@(4{^IQ~|EYemZGU+2^zYl353m37^zi-mtF1=(ZhQB*efiV7?;hX0pMNdY>xXZiUOfH8s(t%(f4{wb{
+Px}7YGA+FetP_Id->wc_H_U1;q7{SEyTO+#hX{ZetfFWfB*RE;oG11(T{Il)pQv3yZfi_-~POYzyJJ?
++h_N0Yoy!n@88@%)oi}}@tfBVFYCjfJ-obs^Y(sw@pk)xpS}I=?<@5?2J+SWfo#8ftjD{$zuE2|>UF;
+RulG-H>nB_Pu>JgP@1J<L)n5PN#XH7dyZHkj{ACULr|os^#?#NA{O}(x!QW^4YI}I&W527lufNm-Yod
+QEv)aD7-+p|1|Lu>j@9GKl-tFI>ef=+A{PFAUqtE}e{o6;+pMUiE*MIs=y|-4nUakFGPkQ+NhcdxeTg
+~d}#hZ6O)xhfkfB5wIC;w8P_~_$jpFR8fPmKO|&%XZr)33hTe)q-m?W66>kDh=1?2|u!_R;h0%RfH<@
+{6xN{czj9y1)N<iCN5lw8;NwiNCE!e*ai2`Re}Ni-*^5*VO-1+xxZ#`1;lM-HX58*EYYre<+LEUX+Xc
+@YDa-9=?8j^B3n^ANl)&{$_jlc6;;q?r!_r(?i)#S>J!z-FnL3_xG-h{^f^vTef=Bi#PxNy0+k}`uOi
+2zO6_9?)Bs2)7|#*<J)(<^$#D_XZPOP{;M|jwrzj>s($j{{kMJk`EUOpgUScDdhqW*?xXcuwzu!_i~s
+nsLdWZ$*!Nf4%kN%1y?BW&y?gP^>-*(_^)n_|llzY^o?iX@+ICl;e*Av>?my=-2M^AA>h`N&{m*Ux@V
+NM!f2kMR-sAlXSAYLP8}&lp<NW!B^Zg6^tQYE$>xK5=zW@9}cR0^p-4i-~wq`u#IZM3#vtQRn{`wc~S
+MUG&%lhe9gLq!fUBmsaAAD2qVd%f!eld>B3MlojU;4O~$MU70eyEW655M)rH#kDi|AAGlu|2*iPks9i
+fBOE%m)~vQRa`TMl%qZW?ep7h?ZBVE{?nJwKKbagKmYdGS6@GW_VFLTe)h%Ze}4W^{m(DI{{F^~KK|^
+}KY#V?e|^eyj=BH*3S0B_<KH*tpSNG!+C19bcEokpBF0@GGw#OK>yL9k>YrohpY5(4d;KhCy=7kY&us
+pYWur&G8?AoU`<{2VdO|(0*Asf?dAG$;k3Q<%+f^SPZT9+E?02!x{jOd0k$%ltf5dS&^4#nB{Ozizj5
+B&Yu(thfJLh#5y@s@12k$w~yPVz6)i+%^Yy9<1Q6_@h)L8RkaOZi~YS?%E>@}0vue;mUgE#WZQLnV~s
+7dYhrR_L^kJQZ0Htx0>&Rwhb@T?l^UQcaV<BF5->`~93HJDn%F>7Y6W_F%6+kVa(X4Vt3#??o!`8eBN
+j~Oj$Ehf)zXV#F8`r<KbVrSN?4D(j&Rm&3nWHEX@VXqM$qt<z^5BHilFPt^5TDfsCiBWUfM$KvLSAA?
+}k4AlG&7n4BYuWGGJnO4+)EkdcGs~>MZOMS^(f#W6owcy_IhKY+s=3!=X02=O(Ov^iHod;{R+G(oV?8
+xG286{#y@@d-yFBkAc>ZY2yOydmy=|-Y8uizjPQPk!gT*~Gn;Q1${1Nrx#`o1|YKeQR<-;b*1_Fzz(d
+=h^UF@|MM{Qp13{Nc!N#<D#Iqq&_ufdklH@=?1G4>Oiz{FczHN7l5EXSxPv@*Og(-pvgu+p^&&CP4Ih
+FOo<23uX>rao66X$LE?*92<6YkD2~t*^R^+DJB-eXC_C6X<+)ut?=rwf30J$zwF#dUV@sXM!Qs_Dl?~
+zL{<9?;W$=+%&w7VY4)~O7+?LrZVi>l-dP$Z`9W})@xoY#(@Xmt+SS8T=lmxds)M%7s{h*5+}=W)$)~
+X*4MR;Q?z<uXMbvyFyf1;Ob1sJh^y9O!-#`#a)S^4R{PKUlE>7u5`WmsfVSxfG06%EHHL%5J}>^Mf8)
+24O*zUs$|5l(JiW7nd#|D6tQ7%j1$G>tpOx8QtGI0CzxtN5oUq|x0}rV2Pofkr?0gQNWT{%Ray#FCoc
+Mfwd(@)UrkwS18394XU8n`xMr}aUWAAdu#96*UaHzOh<C>EpVCMv++zB*UepD8R9kz@8C|fF{V&zV}s
+Rp-g#K@hcnq?DhFYhd0!Y6Aq6;rS_0_=uGUii#j)>giZgV%=O^amrXKLW2#OluHK+u_dFG7Ljz!-{-9
+f$SvOu);kJU(r}ycJ!*rmxY%HkJFf6e_Ul>We&`d0psb7NMA9GC!B6d#gCnB)R!y24K{$O3@oi4SRRH
+$FwvtHvJA$@G-71%b$CC|KQM$NeKf-?w<<d%EF}|7{4vY4+Eu$ymexp8^|NS34dWHdTuW7pR8MVe`eY
+$5a~5`3?!g!?ERDf2?@kgNeDlza8+q(xtr>9BjU%y0+!2p0Cmd{JJ#Y~7%WMn|7k<lBb|Tk-C-Tj)*9
+zp&oa?D<PhfcEiG(?3axwXH5FBbuWi}cC6VONJX#gzy+S!yCxY<qsXK5zcYp^sY`*G9`#*UZG6JKuZ&
+)jQ8Mr~$AsX^YvCa~Ye3&$WPmkSMo`q1Wj<(>*fja4t(2|l}L7`|#Mt}YR&Y28<S{YH+j_w0B8(T22h
+)kn65?VO1*vJ6b8Qd=hu1Oc|bFU~TpK``p1{C?=S1WMny*Orwz)ml%&+~Aw)huFy^@AEz~W&?_hgAh8
+C4Pej6F6G7KeIgPoTz<ur*-YHOLK=~Tb=`qF7vP0(RZ#<926k-3$L%Q3Y3@G&s*_Ge$01Jlo3PsW68x
+_-r?|>W8`-6q0;W^$<`0}C7#Cr+0yw$*a=q$}&a4x$NrIInl9*3~BXeA3*iFy_jHwYO=&O-xE88^oyL
+N#mFeJ9HCQ9nS7`JW5^@J?s*G2%Z&{Rf5kSdeAl{K0T43Z=VJa`a2C);zKqBeGcNfE`%^G;%ZeG@xh{
+z1qgu91vymHG?`<Y2t@BCk!o8WTSaiYMrZg~TCh<p_9$%raAa1_Uq(#P#Z}A~9J5L+9mAoDN`9!8FYB
+BI^+$%4uq%WerWssvN_r)X&Hb6()?!-K<CIAR2&WJ0@Qy0aOS#6T@En*f2|s@f6z-bnA=Tg=KY8QN<>
+FnP5kxtoLAA-FQ_~xlF$`kVXtErxV&@UPg%KmI3?9<jTa@w;KO#W3~+stC%8EF_96-7)`reL+r%3W<1
+=*Z-6mB7DkOpVyp+28STU-b{SkY2r37ecapF>k!2hBWXHN$m8k>Z4*<u}fupC!h+!8hWaBEn2PR%A%~
+-LM#mS5Ws9w*ifJ%f&-`sh^AhqJQjkP|(JN0VWA-0rd3AG&u1qTygqNKi|(-ZspFgjzURexaTe5`&}3
+(4PVPPL1e4O_UeRFx4Jp&7!e;}Ehk;0vHVnS7aBeU^z%Z46jR`p`u|owuQv6FWBM-I{2X%>>#u7No{c
+2o*))veypm!#F5`S`b+3J!Oa1DM?^U;&dktt?j`bcf3giFl(1L9@xziQ#1^u2-meXJ-TqA7W~%uv+_Q
+6f11)jFIG*Abz)x?1PBCV4b0{+K^TxQUt)I+i>Kntoe<h72sYkWSc$aVbWcL9lVTUN?n$CIot%@<L<+
+m8)OOMbW>R?>!vn&UBOMraZSXlTXHvP5{t}xV7LSaTB$dg$LAvJe-C}4r+EASi7FM*~*nq3TSLHtv<x
+YgCtthZZdDy8a(oz7gt4~y322rM=gNSXooMDYfJ_js|+AcO~cLVqXbFQ7C0<KTQWR4ARf3l>c{6UPY@
+YT$9)SE<+296n)eiwxazD5C4PFVjmsOS~Kj2$~X4j=8;H3}b=op9f&0y-br4x#RjIRi&mI>AbJYjjq)
+5&`uSCc!YbDVVGg21u@=MhC}GpB)5n61|wK{t9N<K^zQjcdG~K5YQklo?<`payRQ;l&um28u3C2xEV3
+IpdvViluTu-om=yCY7kP`ARiTCW#<7)gGDP3#smpfF-@>ygT}gwObkO*tsN6dcV0BX`jh%D)&g`_@sN
+spkh#VRdr4t}{hpYXU<21a@ikC-2QZhJLtU^c*iKcS9pm7E)Rx;taG15XWf#;%R9CPAHBl^?5PguLfk
+Tb;=~R%7gqnPDX9sJFjZLYw8OG6JDGNgDIPV7R02fy_#He__IP$=VPu@HZB0B-^<jn!ivUQyvJ|;y+i
+xmMWo+caC5UU+7^CdDF&_u*|92$sJ^Q4vxYCSe7Nbe&g9zr@p4u$AU<f;v4Tlr!NzU?yQJ24g)%c7VI
+vUXwY8q!7$TE(?^lmcHYXpuuo+d-HI$Oq6<#)v0&TtRk&dVEseQ14f8fI%Sk)rRsOmL)Bk>vID%*)Z`
+<>gBA#1-YmZL7UJsuow!@azFC&WPh@|kkoL77xGgB;sg1{ZaN{|1|@vWhtg>WtxW21+%d53lkhbg*_f
+0-vTm$E**!x7EXC;t!$S~!v_2Ac=VMJ=)IgmFG3>zgDm_wm15)5)DiU|<BBo5XKSfoIFrmc{t$~z0S=
+`G)gz@7AfU4VenbNvzNH%W5X3P{+rIqex1Smjwz~M4epG6}lLUV1Wl_suN!AORXVe%2?T@RJtBajX2a
+Y{?9VTd}3Ye~MiE-PoMVrtyuB#nYr2Q1Pi4(o)M!^q{zQR@Lu<0O<{12=}2V$mvJ5Pb9u;e&}DL@;pU
+(M9gN?hm<1I38Lg`6Fd;h*xd5nkGXBOrl|E!Hn{hxFpWBKJZ~M+et1G$X}3MJ2rg~r)S0KGN1|2hF6(
+#Odu0NS|lmpYPzQ&2oI3hBHDDTnpGq4PoKcj;N+VYa}H5Cae)v3fd}B+De16wt=2_b`%OHA6t(2VUAm
+O-3OOwO!MgUv@nS&alzs@en92Y{9EL)exIrEOP1ep!Vi0ZkrZYvSwXPTLE+oR=HY$%YY|6Yrd5CLl*n
+MBb9PHnIN=CT?2SJNufLG+ex0t-f*aY7MhYm@-kw3;EhLoxTVh}5;^bTO48HWWK764WrqrMJcH?7nlY
+>$8yIdFA|rwv}BK1a=d34$nr!}1(BCkqx#ba89edILkiUPITTqQ{nk61!7@ZLskTsM{&Pb94)9m|gw$
+5)hokL%s>x12jM6yD34-mkHKQD1zPZ1nbiQQ_Us0=zv%zNd=sB13y=IKZ!dsS7vDQ0KtK0bzKqayMf=
+@V$VdwP^R!)1uiU8`R7Uo;1N&`WJEbW-rqzHau8FTZ>3I|gs(}pQchL1D4r@q0J5~cz=gnz{0&9A%G(
+3pMy9Mm-WWUNtl?YB<_=tnP$g+hc)X<GQ=*I{i*HfK)k2D*re;ItCH|&$3Z~2RTNhn)sW+ZVfMrt#P_
+4EaOE!ivgri<f0cVGU-v}ig6xt<I7tI1xpgmEE(rS^0!{xVQMT7v-VQmUuLfoNvxYb^}ALX*}`5jBHU
+0|uG6f7Bm_ycIwL_VaZ5k?I%8UcJGkpzZ(t6(J9$H<7-bJ0d1W+RK80``$zZe+q3?l(jLacyIz+E|=M
+MeI(*8}QooEkH1WofPn83b@$sL%xTls--E5AW#z7FGzM^emw=>4XFE#bq36KIKa+|S9ULNT_(5#rGZ=
+fu-er?fm^cBw5?9LE>m4#*tU3Nk%&7rdDAqJp5!xmBc|G6KnkGQV#Dyv(5H+Ce?Xi}>lARzfX`1nC4h
+zlj;2isD&lA}QL^8ignE$tj|)GN#IWwQ_f~@eaLRL0Osp7#e~_f0dip6#4Duba7M&uHCEY|J01o)1W<
+P7Ob23fTVl_Qn56&GXal>r3X^5sgNRrKA8N3{hn9{7hi8c~!P*ofJVdVi_${<X!GPE=z3mJ54vN}_On
+17NWE|@Wh*bOYZ*@@t1Ro)WwFHtUOlvYgzJ2Q|Z0O66K`#$arN*OsIa?fr-2ByQIX(*O2+Z&~zF`J!+
+90C*@)u)DGtdO1|UtBsg*yaMu(zGeq1fUrrSh-b3h?%z2^vhT;{QD$M2VjguIly!QU>@)sL-5)$>`|l
+6W^kYy?PQ29DKok_oWP0aCl5RzPAKNbu20H98Rl%&&`!evIuYX5F`f=8X%t_T*JzeZNor<`DJ9%F)pb
+Qn>Np@IwQ9vSNfH3<uq35HL#}8vAS!i0y|Mqt)JGwL{2A_I?}tp%+G=2uZo9OEjgVS6(n8hC65|IB2R
+vo5TbG}l*0T_?i|qxx;t-TAsVR+B;1d@x`;Zk$$iYq*O`?WQa3?vJF-p@jd4dnIXy_zFn@-PQ+X#w);
+|j!tOrlNOEhccYX=bF2#VZY7vmBaKj#n73%s7zv5n3rk@?pc05VO{>+395KgJs#Mv(sL$bYI8m?OLzy
++CluViDp`mKLlennmcVx*%3<R8=YZ)0cTUTcg1P^gVZ-c98Hw4Taek0mCu98Cod22;VM!2h%_DqYsER
+3@Oo<C3svw@L0)z~3<UVHq;!{YwD+Sg!+?;;dKY2JWOUo$*fi6lJ}a9BqXK_%(aH+Q7vLgSBjmKx!2@
+>;Rt$VCN(h`U4`s2^HD_|9_*04>q-#$Yl4syn116?ye^mj=KuFG)IbJih6t_o6CM03gtq2hZJj$?%-A
+y($4$=nGpiW`sDlyhq;R|Kv1aL(d^s?w4HKO2PH7hg$E+-8W_9I9TfSv~LJHVf}E*RLIPZQ<RhaqUuJ
+nr2PDt%ne8+i4=XdAB4D4hlj^M;P19FsK!nshrKyFv|KCI-~*2{VyB;3PoLfWrz<!XN;40Br$GaswlB
+$^jizX@Z%tRKa3U$9LP?W5`$0BtJ|sN&@hLgZM#EJSnz_E}KM9^JRJ<O#nh|S)rW>1UsOPLZlUB9H_}
+2ixao}wD`3~ATI06W4HZbi9!fH7@@=;3D3@ESpSn9p`Bx~u`p2RI~qWuM!?_pl>#vAL-41x0Ixgc81Y
++`F}(Y*ldVQ3><8fmrar1&*Nn;><Ur#P1iKpP(~?LqLo#q5300)l4d~X-Q*#!`vwYGlv|%WQ3M|Rc@A
+jG?>{l$uZ`pZDtTt>1Gc!_0$ezG!hU{g)6|ElaOnGe84Wvv1r%p+wLtT{gK`ZzGMJVQX30zs|scYjiJ
+kZvAC0I7aHnLR!h~HhN?i%c9`L{f9E$$(MBb!OL5VC7vhYbQU3^BBNsZ(~^0MJIRoa!975ZOlS*$FaP
+=FIjJh?^}vaYbJ+A0C3vV`H$bDL#TZ30-#eQ|!Xy&?<rsxhb5mTP<KVR>|1iu}k)MWeFr7ko!``FthW
+Ns-kwK=D`!2gayo4l{=<GqCYLLUE-29$-bgZC}%)BpngH-N|AwWcZgUjsSUQYLtJ;T(`G?Q`?2ac3SF
+CYC-Lxry0UfJ2#VXO70HU=DP1uItR9#Y?Z5_Q(=Qnu5T(PUd<xRjErt!*jeKqh1*0})BG42;^^Vy$yJ
+iqgU64z46~KoA6FWte8K=u10`#bHr<6P!N46}8r{V!t2V=K+f&F$}50Y=lZQGDR5z?mkFx4vH(A3N2n
+S4$<;}i_AM|P~5#;9V0x|Ay8;Ab$e6;RbafWD9$G?qAc%pe#Ic%hn}$QS(9t&ObcNijo}n`%325R(#x
+x-u<PR*f2W91F<<5_hRFqXrZ?yc0rzhWaiMRCT~{-|c6alrDv1Y4}ntg>P3bO}neKNH*G=quDA2wWWm
+#{D^^)O0eLGvn#WK-Wz25!iSp3vRXFNBt?aXb$5V#qZQk1_z@gBE6`}2b?C%I?-us2>0nfny2Ku+HNY
+b>?dLAXPjHZ8wh4@3sPCX27~PJH#hk&KoC4<uOU&n>_D<@jw3&oZZTcm6-D#*DmU(u0ZS}Kn5^)3r5P
+mzC7|)8>N(#z9rCQY{h{L;-hk>8HCFgdfNQQ08yu-@Iv_l&%q*XCp6%Ukjeah#m-vf*-rjHE>(Dq5dP
+!vJ8Vq=%v<#N$n=qewFg#&Y>>hE-4QcqND)a+VA*#NQ{u{DN4s@r+Qk9HeaT@e&XOGo7+#uGA0KbHtm
+;6iMI3bU_g&nRS$i8d4~8#GQwq<IUQi6r8BKCGB}5=aw}Z`c9_fNT;-P?H;+U&f-x<TcfYS(mP|f`Pa
+T7M*quEYi_LDULjC&0W^nm?eNvcTuW$*#kKQJ+wb)F*GXH07H(@9L!XgA|I8s_P6Xw5ne7;jan8Fd+i
+$`nbK_7>K8V{Mr>1ck)jwZK|)WgJe7ulJrMphyLUD;<ee@xQ75i<+WWJkf2b1>aZnV{Z40Y+n1I0WQW
+=WvQ&Ay|nq(kawq_b_fY&l~+qIHPuqh<sVX1lA<yWtXE~MTZ_9fN-8lR9jG*^Wjf;<NFqJYj^YKl>lG
+c`ss6`@=WoTTvvZ%PxQ9joKRTI+xEHW*JJ{)#T`rKow(&!#Oyn&^m!JJr=jk79#VXl}|by~HFGab0$A
+C2DkoB_>_6ARq=PM<EEQH4Zg!DkL{WP?R#eEVpc&><Naw5T+dsvdwCXE;h*2!3z|f5CfalJH3;xi313
+mK*FX?!ohY+8+>Sxxx!{8e;e>GoU;K`Lq<@K51DXD7|3$T4oIYNNjqO^MQCVUHoAA|4be8)n0cRS5<|
+82g~NgUsEFdSw|msjRM2!QbDRF6Ld!RBS*qnASEtcHPhqR=Eg5Zu88&DD2_5pR4Ga7!wwX57M4<pGN^
+?q$Dlja$%y4W9xH6<eU15YQ7ER`1YTAPDq|r;Ok{u@w<w9*@H5xC6Eh;7MK<OptGI`pTX5~WJt*P6lz
+Zz%OaA=X}PRJjfc)LmUgvBq-1?usR{$)&CnXF3MgS1<3gXUTxlvbl2M!#V5>oHgo_=e7CSlg$4Ljqn0
+Yp{c3DCWgh;2Ce53MvZ+d*R5&8_k%$+iURAvWCoL$qOg~sbV&W(oJ8^X)_wo9!si%nF`aIG8Fp}qzR0
+;918;_AyMUrMGZ-Cld&@@0W0yDs#COy?i&F!t~~)m(6bI70FkKrHrZtQy<1mi#-=w(B}g9eVUtlEih(
+w|)-r@$*#mh;sH0lnUIzX_7OG`HG}NUf2qM$8IaKO8N1zGjyzf$6^5i&#+920L(}n8pHXR{TY}Dh4od
+(SY{=9X|D+LStDgX?ySnVmdgUdEsb8?nA01Iw)c+}6biSXG~ig(ThDBzOq@c4o)b){&^Kj>mQ{#{zaM
+uLcpvO-;1w~NK*p{nX2z<znK0k1Y~V>=y!b-NVyN%f@4R(c4AFV#g$LCRaG7)h;_q)Z)3Fv-}L@=z%u
+TXih9G7GAVjXq3g%i5`;kcleFwb<H~x>>OTr8n?Zr`sK{PhFMUYF36!2|6Rgi#6e4Wlq<6w+6AdO98H
+lsF{#ZfP#ucvFGZ*k<t#i7CWvM<%TBF9r+3Ghf&!;&A09b+s(V|VnhGnvYjb+xXu7vY}cw?h--Z3#W3
+lv#nJ>fRsBsRhKC&p_r%t2PFb2)(;N|7o}5ew)fOn(n_>;hwE-$@rzsNdt5GVa#4IswgQ;#dZ4Nuz*r
+AX@K357bkW(_-k%}*k&}@Q7avJJ@0&G+Ty9^uVfQcUp$WO^~Mx!($ZS|{%(a>0Hi3b_S6SE1KjkLE2d
+D<-;RP(AAQ}!nr2n~YXW;!H>j)ZVq?G<Ln*`|7+9qRP_g=7I}d-dL_h!2+CmIZkLey5*$0U=?7Hs@GN
+s70apy$$90D7A;G3!-i#vo})HMmx3Hv4k9oX}eCH9I;jEmh>^~tFBEkqC&+COnyTHurb0xvvRESJP^c
+|VQQT))pm%xNf0EnZi{MR>@XCr(=u47T9L!HD}i%2Is=hBWTVCKoqg%ItP{zO;M6AiLZq~*O^F2`SVN
+Dr5aKYAcCLmQHb=fN|0$Lee0$zv*?{;^h3}<nplZeB+t4d$?BG2^Wwta3AMT*S*p<tB=t4aK#WgNe<?
+T`$cs>b4V~djDV8KLVs!~I6yvGzqa#kd!64>)XA>&W$@(?VMUI5h_?T?<T{`csGaww9U;P239bO_auw
+j>Gs{E;5<Y7~#LWFs;<k^p?^Iw2wek@%C&2b5SS(542Xbh-|OM{o*!SA00cNkHNa7`0vU%)`TRl0J$~
+J<Y<G91_U{5tSZ8eU-$Iu$6e&7rwIc#FAI@m?5!-YTyvcY;e}ygM(^4Q}D8y9so>5%u9bLFoGDeDUX>
+pM7o8G3GyDOQWk5iM0bZwJCx)s?m!4k`-7q!QWqQmZyF|8LWu_%bjL0aJR}sbG!hpC)rRhJ<1pmd)sV
+^6su-tb)h<<<@=!!d(nMG3<Ka6#*Oj0%ovllI$YU=lE2<Pw6kIbpSPV)*-6|neyIbUtQ-~Ex{OHXLsP
+3+RyM`78=$KMmOqEe)vzoMUu!ITvxPu(oy7B7N>;-?MQN>J3{76j@Y|R1&oo%P9acfQcHM3KWY>HLEJ
+7DFGP`fML^VCaUQUcb{0_n1mN}FL9R-#!Ni>rVx>u!oIT#Ga)a=%10>E?NAU|Q<|>Fr`sa1)g_Xvdne
+A|fKT6dHE8Vb0Cg+>*FsMG9pcA!xXrN=A3j*jb2rzy(<X*4LqkHi+3xT?=ro?+R<Roy&=O0!%x>ikj*
+!V9as_(TXRz>`>#Q((O|wpG~n}jw&hAV-FI`a*CeZ+dYT~XQ`T6l^z|RMhmIQE?W#wQKt=u6|BuEGEy
+pzKxBt{Y4~(NG=x+?JVuCit}R8W0-M#3XE88-NdTt|t{+Isf}(trUTO<T8^Dc!a4V8&v3o?36NSt|MU
+cqX_NHazx!PUUdf3y_2`d95oDd97&lZNN`=zQ<#2|ZZqIXgfDazVNzjCm4#qC6~uAV}|d<U#Z1Bcx@w
+fLqM(9<86ygYa}JqZP%^%ykSv4ecJTuOG26_WSWa8hBj!ZkueoV2r=K5K^6?!ppe+C8|kl$*eeLwU9y
+MO?FC388cByGaUoda9P9j_}Z_C0Cb}u?GqGv-O_R#{^INItpq!n%eFpE{{TZ97A=0Mga9j1cId+EKtw
+YLkD=-lJszktv|SrZdcLsd8a}$k4YZNQiu?OGa)NPN{Tavex%G=yaPRo<DUjHLq1RtoLA^;fe1w_yfR
+d70$I%@(?eXgWsIZbYS<GQE^569+c%e)5H$-Q-lPuMq)vBu44-8m*JE<Zl&M5uj)z4KEy?ztgkiw;Q>
+5+>S?TVXc^PiPws=9U{UP??l<6U4k%0C%nq)cXi-z2}kd>Mxg&>DgV9H_)_EL}5tWXKecFrl9ksHUZQ
+q0p=stvGQ1tVxxIkL@)9E!O;<0WrHK8Ls2*O5C5UD6@@Ha+m7E;2kX0t>d<X-z01$4Ce@v3T-(wZTnI
+5(wBihi7(`By0dgP(MAiw?Mlm1V~bxmfDC5y{WFXbuF7>$*7nHP{LCOCee>|bXmhDG<o(<g#guPv(XL
+4>umJ~li!;vg_IffWDMV_!!;Q-g^7&N<Hn$nwE}vIR}P$>uwK_3kcRA>Pl+0!Z7L_C9F!W73BNoQ<Pj
+6Huif)`D4b{znQ#yUC_4ba<lvtjBC8p9=-h3m$Lb~60At(q#EC2dR;pqg(MsdM`lhszX6Ue%^msO=yL
+l?}5<qQu0SV8G7}OouE<Y6e$$bHOhh*wh!7<hS8GmWPpk#m>ZgZXgWxqXYzZ+$t=oYW#SqH+G!Xi#ZG
+>D+Knyub&u=pM_p=WPx?)jJ|Vc}f?lFKNi(^E&DLgGt77kzQVfUyNY4z;UHuX&J+P<R>uk#-F0AbKF+
+NXae4=jF?W=*!byw&JA`)b!D#w=z6DTqf_RP*fp{%b-piR4zp>!+BZDuwEefO|<RlW80RV4n7^+^Z<v
+$gwwZHm0KeL;eQ>OPNe{wL_(9LG!UwL%?0_-_8m<$JEF0~c-Bv<+%Q!-Y3k|Vl**UISDSS_-*VaN0P(
+MW2RP#p(3-GS?ty4n^;0@@w-;*rhT?Io+SvH+=_T5{9#=84tm`V|ra)01jIqev&0{JO=<v{LnjQ^H&(
+u<UwbhhRrD;1y6^vb22!uoFuP47jwYwe*>Qu6=rgAEeQO=3f7<zxBG1+WM+kFYodJdtKfEEm7jP!)~I
+_zb`0myAHAQa65Qq)CyOxZ#Xsm<6Gl_(|XMj)zW!YZXxYo0+h9}y^FfH~Ggyds{&%3<nTJ<4OV1T<{%
+XaHYS8(i)5M2Ktcbr>FB@Yaq?=ORBHDoU+qacFBj%R>*UVUZM%gT%F&eJMO6*U?YL&#|V>n7o=};fZn
+Lm6Mw4`>lgXx}o~q0~Vffm0W4`MF?L>Nv-(AN}5*M);v5sm1(g8=r%mHJZwUV8HLw;F_Aac!7=TX(h|
+5>aE_!-?_?tmZA7=t2Il2y1G$ziKsWZ0jXg=w?Ke`00(`9&@M;dc@R(*rjDV;6Zp#JxHTV_lrSNK#cM
+MBv`~hqU$1s{IJ#U;KJ8xZ)!OHB4h*(3~ymtN%^-q$gP14GfLmn7saMEL^g6Q2NidKs#$c3Jg6DpL~2
+~|$&5;auH12a1*xm`>9Ks(#JW#I1MG^he>XrY;~C#7n{J01vWRF&g>3?qO$3F6^WJPd&lvs<}=vSMBG
+V2_OnNP9KaYHknGMzl`jSZ7r!6xBcB<hwu{7hRIh%g*D9Cw7p%Lqr9mO$jfrVLoPF@F9G6=eTsY8QNV
+#&v$d$N=<uHIwT3cy0^?0VNQ5z%Tt-BH+h`m5l?_wDq-TD{jwcsw_=zAR6uy`1X~Xsu&HL*D7F_*PBU
+iRmE%cDV*IY4L+W-Z%sd*j4x-8x(vG}jsaR(@j;26bf}0c8Dh09gzV2-D5)XFSTUx^N4|L5=m9z>W3A
+G_5zmE3P=TQr9mH=Tc%~HK@qK=y6x=9;m5|^h<j_rf(^^3RfXu;n;`1G&$Pd~l;?%~Z}wr}oVKmLt7<
+{r5eZF_k0?*8eX+r=KAxR~w!rT1n1@Z-}Dk8kg9NhqJ7^05FEnwZO`nMI8!&xVrMgDX}}6d?%H%HO87
+DXTo~VWV#4frjLIH|~;9S{X};hHiV27P2!O=PCD2r0`P)4N-1@sS^~F?B^|6i%Dpjz?ks#4e2AP?Gh$
+5C~BtEDh4x1_KB+nd{IKTCS1Yn9!=k^y>{jU2pQ`=$ykFb?Bw}Q>fmR-;9Ka^SJIy7OOxU-NPo9r=Yv
+Et``Rc!%)XWPOp5$Knx4D&BrtD;f-@BAPOeg@!2~HKXwux0*25%c1&J{@JdyO?l4PG$vY}p<8Yj{4C5
+6XetU=Q+XbKEAC1~>AQtqD=oE+1b`1~XZrrJVikASI4@NB{oW-Og3^1y>BmxZLK-oc=^pnW=}P{_jxu
+azFDg-Z)MJYi1)$;5yh2*{>@Kn=9RGn8S`+J$br1#mhz-xjbB39mIVhap3N^W-`Q#}fb?p`#ON^Rz>Z
+6%lEtx03`dq)B&hf+G|BnW#}GM;e0Q7O(~x>eV^20&oaW(_{@N5jmOs(1C*jV3K<#U4aR}3?TL-YQ%I
+twJHH=kxBqKt2n8dCz)cvJq7?d)05<|2m!N@mu7>~ik@AP0Vd3PQ1(u+-=uUIHouS!CvYTH1*z+UlDS
+PnZ6dEr1Yx0i42+T#^aBnnU|Iw2JZzYdcoW4;K-MJsrl}Ii0xvwP0~<H#d!|Rby_1XfQ*!n>y8ex0+5
+v?>n(1m@EZ?cXQ{m9%2!q*oEQW(h+&=<c9K^M7=+Kjqj=?qb348)3FVj;YU1J0XQ)i_W7yMMfA5B=oN
+na{G220eQK(0L36M|PUFU|uY(wo$PL4&94a4K~5J*<7R%;x+6w)0+a<d<Ms-mftHy5yfYA$9>sk0rup
+!~ts?t|1lnpa2^X`)LoD5_jS_$qo*Crig!%0z2Rek}gbwnr;c^&0TcIl+ld_lZw>Mm>y?_l1@k9tx0h
+exy&9<NnHU>N#Q1OXhJU~h&9u%w~gCf_FyagI$WE$mhvIZDeL`&95@9J0|X~#lG1gW+bDh~X<?cXSlp
+!WOmv)6jWJ|qT<Z)PgJHKo0iz53jmcDb;e}y3J$y^Ta7|S&o+DfFoSnxpgW7rmTPJhMupbUw2#Sut-z
+RSzDDQ&~`y_v-e-Nb-#2p;vPScJT0^0haJ3Bdtm?#u0IB-4Q;gLAz?HK5_CIwioxX_$!#|}wmp7OkmY
+c2?j<dSmt$JA<tz#NlH%EqM4lb1lzz7+q2#0?N$(rKJf))SmPL8FOOBv{hhB#kE4J)0@hs{*GPfh<mg
+?_6tt@?jDlHn%%5^a-w%LCU6b0zH#%YQj%tm^ahj5}5>vFm5)?S^mr`9?YA3NlwoaHa7uJnd^aI4|bH
+p89|Ad3RY>R&y{B`6Bj9S#X9z_YvN)_-Rf}41k>S~dioz%*Kz1AaZq=n)*Y93d6}BZ#-{Y->175aROv
+iAI#qQeP>7W`u}EC%vNz3sqkW!6REq071yzEVa;r&_ouK5T>yt<(3fr8h`gymB5?UqM`Dotmv1ZGVw4
+LUc*vF%8lh46B=~oXstZBv;RYA4QT#*tV#lxn7|H4jiauJzeiDr7ln$zS}!!Nt6)i#A6gj(vL2_KfEi
+NKN*tUo{)iO?!pi@ChBx#<{kHz<;%5cp1nW5JFl9qBwA&2Enqi`RP?19G%U;XG)NB?vQzDMsH#$Ue*!
+R11TyVtBZZY@A$glPI2p5_%4uOK=J13L`epqTfCt(G#k+M(7s|p8#x1rvk}J!p43RI65xp$lBxNL1QS
+<@J(of>E)aB2r4*W6VzUbFlHk4?qW^!jFVzFff%Vfs$4%^Brd~*K}!&8yl)#~HtI9!XNK_l5-NmK!N2
+)th_VD>Os~K2YKWC=2niexPH5~2mvWmz;>*iL_&yhsj$YE+3tF$!E*uqUG*4}?0Ym4dG7y@dYu=}82u
+wRYvCn)?pD_4?%t$#7&zq_}5T^#YE}=puO_bSu6$b+i)fy(7)LAcD#NJzRt#>nvZf!vb4IFN;d_gGee
+kZm+yf23V2cB}8^+Kz)3xG{`e>PdUMxyuK2ah4#J5jJ-dL36XTQ9q9rT{lQeIp^rTTNs%*-^w3gGUEE
+R>Fzp;elQU7yR=g&w#_IJInPh<Ajis%x+-TlDP=;5OC6+K=U0+=3!pKriPWPgdHc=CYrR0HY6nIl0+b
+soP<d3xS1k3&}y(i3H_O9ToYO)k>({t$wbPZbft6Ml9OhePGBA^Rbqkw950@_42nE|x7Z@++N4qIXKN
+HTE(ku;yFX$@N+ty?58Nzw!}ikZ=z6}J<i`5Yy-N^ZOry<ZXkz=x%W2Ki4aX~3yWhGx9}%8VJq-0{aE
+~E~#?Fm(&{av}Hzcp)FinV{VD2Hxf}?X`QLuXQnEhd_5a$_h6>|~=X{#qqlbAlxb_bCqfs`SAi2gIvY
+LqZUzD=Yk6FwmTrb&N4rpV!PFIi2_Kcu}x<S@CL=)qi^e;A0p5^6Pww+ToLXtbm>OQQdjgW0_`QL*Uo
+{*=S$21Yfv4r4i)$`i@nO^lsD76hpxHF31{VQo)CDbZ>Kd{M2T=xcaAnoW2&-8tf6JPTBt1dN=5N&FV
+TF;!rB?!(iXXt=5xB^q${c@FCv?Mu;Ku!UYTNYFaW!9;&3niw7pvO7O){jgDNN)ke+#&5IRE6q5rkkY
+h-y<l(w9S|s=!b8OpBBn!hK8b`OJuI5gv;pash@WTEz793njFlCzK;ET`NnBQ~cKHyPB@EqMg@|B@gf
+WC*^kc*H&KwiSorizxUU3WtX6+HFPY-n~I)o{nYSGE$w_(4RPWd27C2UZF&C{k67o<)~nbfP%V@{7Ml
+Pq&sz*txj#nDpKE=yP(c<ZDBllA0h)4)uKCUPYpcXEZ@^o}%m>s+@LYCCIJDIS4*>9OS1?hhyGj<O3Y
+Q(;Qe!I6L(uO1$sq)Y|hB8Ql;+P6f38Hf0at><)1QoT<kifQdl`z+|GOqvM;>SZ?T0Z;X^^i&H0@la;
+NKvt3p3mR{X;vfcjhW<t(;z|<|_1|3N6G0ueI+TcZ-?FYQ@~rj6hfU?w5q$k5<s=1Jn$p<i+}#h$)O4
+bVeJ(Qx&;vW1<h9Ht=q8?cmjZ5Ztr!M=vz{fat}2Gy&{p`O5Mk)9rU%RtY38J5G+CvvLqg#m<h5j%W7
+;IOn4qB~YL+l0r-WkBbj{FAPkcL151vBayw{JI3`j@^_|pId>88|TNWZP*sZoi@yg!PHp2z03yvb7oK
+rruku<ruFMkX0_$ugw_r07U<A@0_8?COdf-cJQ{SkJ<M?6IW(1U6A9Oua{dj^oENMhujqoYSDmnTrYz
+(hooY=~ro;5KH;D!+PIk_c4AQ!IUkd13ISH^Kdw7Nmvd~N~0^fR1*P3><8iBi3DqU8qwp27LVfYzOL`
+2PYAk$>mLe4(r|Aa>MMDAy3)%f<zpbph>YeUfMY-g22{laS0pGfy-C`ed8Zjv)HCGRErx*|G`0sV=t2
+AlaK?mu&$JZxssmsE`lE><j}(e{(Waa-XK5yEY<N!s1;7a7iE31!r&o=Nfx8~dTfEr@2pkZqWrxNgDH
+~g!mi64E%3+ouq$RcF^awSozobhtcR3T$_MzU|+jJn)F3UG>p%-RqX-j(|OamtS7EEL}46SHG?53iFG
+)|&k&<ugZ@OTnY*Na?<5MJ^u;_rgL(8+;KfY)Ok@->Bd1{7zKRi%=eo{{gCNlM8*#&oEw2K2Zo#KTL@
+lA2Ain#4H8Z*rI+Op$a&Iljf2G%x?R(83&KuYomzwq?TdObZj}pP)~e^u~gN7t>8|Yq_ZCVR>R2j;EK
+l82CxQWFR=2xm(tIL6wlzXX&h#mMnt|Drfd8N#Q9e-lYQDB~m<>-$Z~|Eq-MMM)f|SP|{E+Z7wsWARV
+}V;}?SPrNj=#B{~Ap3{<$`rH!X-(BYkO#uzZlx~2_iKpMr}6B?%;D6}G|OgS0?e_Di4z>xQ6szm_Aak
+ausd0#xyT$iE_kK!qqLw^=j0YRf84~btQ1VwuC)IhV6h~p-O!gRJCa~6+Gui4o}qSG}bkx7jKk)W%Ck
+e1d##L-wL!ltV~;<BSbAPWQn$x)Z|l9tnh-vq$4n~H{ZLhIz^ohP>O!`cEv;KMek#f<W+Q#!-i9)<`#
+X+><$#}fUA`HwDvmp*w`u}PdwzX`BJNPrFv`aIJsL{4FbWj4C=keo=b$mrTMeG-fX=4yH(ZZ5>3F=B!
+Ux?$qdE1M4KF=NV<W+B{@FeYms3O>=Z29?g79<E*Z6R6+~*SiG<jWr2+;Liey=4C+qbkF?t3MSM;l)$
+?!chYnJ9vQR*@_MW;Xw!TNNF+guxdKaRbfy%L^=49VYkAt9uv(PmwthNHX81CU7bmx-!I^Su;w}}jsq
+5H}Nhf~NDNmS^Ny(VpixJ)1Nd3(tL`FJ;lhl;Z^3!v{GJBl0tX-BaL)5YO$|)@OR3U*zH)aj7^NF5na
+*jG<y#NCR2u0!?Dc#p(mYU(rJF|T^OFZo4;1HJ>*xz`=^11U<*YIj}a|jXCHk|R@-ILgYqlw`YgzDsq
+!y9Y7w5lt`^z5@y5byE0AvN^-UlKslvJ1F-5eh4?FG^F&)P;0$CFcSA!}vjNSgS&R&Tk+7q6PllL_OX
+}b=cq_GM>hK95uvdzR%qvyflAH3Jy)3Nd69hZ>^!U)Je=Ylu(17CK1Y!DKiHB8ZtPiZ4#AL(0$%>>}|
+#;=}@!js`QqtvxV}Lr}zz6fG^7g&^VgAkyksEc9vAKVv!RCeNEdf$WW3BFTAOMS0+h)3OJ1rsIJqik#
+{o=bL9A3s+Xj(Hm7Hd&-HtPR499S0_cSh+P&bA;tthA?$&iABnvleU}|F|h_nXrDz`+75X9EW*`@)y4
+|uvc<;gw5=Pj&oYwI397&RKtNfRn@y#zEQ^}ub&D)@U`=$t34LV67{;=6II`;qJx5(vDFlpPHDb%2)`
+rAe8AzLFz@!(U^8&rFVOPx@kcd77)a6&fDc##)bVi`R}@=(7ED8sRwgmto!ve<+ivoiL<J^g>V}xlOr
+9iMhj+N%0_oP8ke7PpD#CPx6lPGxbNaIQTPcq-#ZZ9XmtcGk05tn1sCs0kgpe$E-9eVQkn2pG=8<;mV
+<%>l*xV+6e}0QV(=XAlVZJ=gDZ|4pjgf+<l-f1<=E4hdKwr_wF|lzPuA}F~UB9TF?4eY}Sc3-Q;7{FI
+BRFsrgUtz+abSsB)MdX(wl4Hm4SeiPH(5^eJwGwHYWK13ol{MrO5TYVBSH<GpNzJ&7i37cr^5GW;9jH
+5n0xjj>3f8LzyS)1C`a#5M-C>)4IF_#a*U7!nlSdi=yQF5XPmyVly^?|_X<nA!XiR*tYu4`B`|y3=A?
+cciPuGJEy)YO$daOcOs{p^({~#tuQX4LPsGAj*2Z4HV;~I*ITWJyVQ*1UM`hwTd>wZ-GeZVmd5xx@Wk
+*AP&cYBTE|HaoVgh&|4*gvF%A*3Mbf<X<mT58`hhr@DXZZH<0nbDbUuBh(I=uWUj{Y9tLCk!yAK7SIJ
+*iVpj_e)0LAx?&cJl1QZH$%G+?`(R=ZuizN49OI<-kXl~we$R6PzFv4k9%T@|aj{#7bkXv)8<)EyW<{
+XPZ3OhdRUMQGjdZ9u2+ij>8!xd(5r?0X-_J8r2!)3S}E87TY4}TXKXF{jk6ueNvWQ=B!jvbobSK*@#P
+&xs1CRFj1Sy;ak$cp~(`c|xcu?m!o-mFOFJgLf3ba&Yo34cA3v-O|{j}c7r?U#fkmvDvhT*U4Pk_+U_
+2@V*${S-wKnAgIQY`<7}+Bmqp#LrK_5)YG`$`#5Ayn0Zy%@t3?g1af%@)J+*zRKo-BTtE5Rs{%Lo3gG
+#!%oZ)6jrpPvy1L5%Bd*)I&a&c0t<JG(<+ImXAVi}3n;Q^LP8a(X->~Tfe2{i#T2Vaaj=HuwIscdrgB
+;HTE8dDyF@8(!(~YeA>3zpPl=_nhK=Zk!e^!OG~`e++GAmHX+}>u9}-FVa{3FYoRo;1nq+OT1GWXD+}
+r7S(XA&yc<L$R;b(yXA!w)eHX7*Va9?RX0Q)rUCNTa8k*>d@N$6=0Wa&UiEKCQ+qeeLORz_p1KTIOX?
+aW04sBW!?y`})lMrZ0NPFc&PO+qC#?V*RfJPZg<IC0Og)U+Jo_Ur_kr)x1?9|<D}9ZosSFfwu|W#V;V
+rS7;m${$+HLX%=Ax7voBjpyBH#++7p!%>7w0n%`nC(m+$7=w2TtkEb%W5D1yNTu2R5@|@P41Z6WO<&^
+^-N@VAZSkr3;n9e7@{+W&Evh-o+Fu51`-YBH1M*{97I=pP71>tX0qV-e4t#63TmZ-~ii?mI_Jmd6c@Y
+g^RE2<yW?!&LJ3Mp<QlZJy+i9A05qy}OgDD0$XPreJn7d72&sPXSL9CJ(bFK|=smL~qI#hlGtDwA6Dl
+8ll!NKS+X0&Coz>y^hl1tm?r-?@RD>qDNr+=1c;3f^yfFhXw_R+Lk=y@%#l*hbQPs-mtX8&|5{vxx7S
+BNkMr0~hzG80B{bgxl!-!LV+;+WF#NEZ^wfR&<!gx5hVA7%~3wtAgW99HC1G}e59P`kbu0idEG@(d?t
+rpO^QHwxdpgt0^^D33&-kY1$g9iCde{U9?f$Gj$u&dKH0lPhk6f*9V$aS^$gs6(<y<1pL|c_BMd!el5
+dt3Lb0bFIH?2Ge6jPkNtfGf&g|B|2i$d4a@|cuG<Q%v~PBga@4{An1zY)T~I1Pj$mWe9HW3`OH459$j
+LZ?&Y+2Fy;lP5vuKUOeA=r#?HHgbNGvBtg*<4UA-gzH~sZq62$)6TV8O}_0!O``K-i-nk6i-(kO`5R)
+^~h*O^^t2y~-Dnh-S;eO2Pisi=VJWDuhBGOHdINTgBJkm-C>gWCvGe2Lg>0x3h@ADA4cu4mz9yVic}=
+~$J;$}DMQPN2IB#7w?~M#c0ufrxyxH1VhT387kon8F&JzEM)(GeX@TB(G*$5Tq$7OtlexI8g&JphOjq
+<09lp#xHryX>edU0UKf4eesUomKLrdxei~r6x(+9gGVbLGTW<R*XNN7=DvD07#Za05{_cx{N9ZT%9&o
+?q%4RL&ai%!B4C4eugsG5Tx*zKIzx;hp?cHM@>ZjPH`*(Z^>?U9OX2Qa36qOtNG3UZ$|VfFhcr6YVPh
+4YPELC+cC54$QXg?A5eZK3ae?=p{uZ3e+XF$cGdvPEB`w_reTZ_$=9q0ocx-RwBwEhy<uP(oz!(L~u!
+NJ#5-K?0YmyioZHwV;DBwO8ZAy6{TA5G~Qyb$<L?}^YRi-Af6pOiZuzr{&R)O37Qj9?6cz04#zJ@&zv
+hf^iBgO<Q+pw0DoePV5TbV3DYEafLOSCc_mt;H}9IAemtWmuIBsh?eMC0k53n{Wzt$~~<iMk<Ku(UgD
+>uEy5E0m6eC;|VoZkNK8G51$AO>@QW&1GsQ!`Wi6B%5<B9|I=JUbX>-?FfHi8+O6=pZ;b;yA`2BgfzD
+oJ7Oo2_&ik|`eE5}xdHB5?bcz_NfBjAo4bP;_7aoYW@6ZfEl>~78*=1eu_xpt6OqT<rG<BwRCI@pvFa
+W$n|3*twp7qa3^QlZe4B@j3Sed`=ob|ltO+lt5s1McjUK$E>Scxc7+(0Y7U{v8-7j^OR115d40xnDJ?
+7*Q07)y+iqt)rD2Rfyo95D{3U&f2TuNhto0P047la~x$>l8>NN}tFzyrf>y8gbR6u55<gp_IdoGM*$U
+S=!ZXY<}RU$c=z9khEs1>iD|CE~+46(|_pyb%ZG4Vh%pMw|}rROH`O>ESLSIXkBcc$%KV*#TuQ>ZLD>
+0`<R|NEbBN+r>$w_L#(FTwZT+{h>?`Yu9Oejm`A*M>rbx!vID7?=eUZN_Yx$*cC7jC?y~`A2Lhp%uo9
+J2>h0VR}>cc0-?k3MZICubYH({0XKFp?ZDXs<_KH@8qy)5i%l~2R29LY;@M5I{(8$x$Khm{`$j<-CI~
+rqX*-n|9zj3?G){lRm03e_LhYQT4a3|$@sP#^e={y^pYCV<cHQIhfqsi!es<a9VD$F_USc1sITRWYTe
+Hih@<8u8lX*Lg8g{_#Z+hYD6sw8Q6hYb6-(6BhLxdpWg~uMI`NHE>_K3tqG#BDhL$5i29EqOuF#S$V%
+K=jYnPU8fC719=qv9>*3mH!2(|M>aQ+I3JA#2bZolxJ?hQM$~oufyc+<79XbqeV`3CZ(NiE-#=E856I
+(H^uU#p@Ik+9|RjFg<C*f~Yt3z@;#Tp84!vU6cw;4}bZ$w*`50i-GXQb;J2OcuL-cO|j|k{nTl!00kQ
+1xPGnT;qZqnmA<SFcHI`srU=FvoI<!MmX(h4My<<V??j)<V=bmp8}P`A2Z1x2YW>qyBAra9%mRf6knH
+2)_@Wh4FvWucMu?xvDbxG)NDk{|auHzsuo~t0&<%4jP^qzd(8uc!6#4+UjggYNr+9$^Tz);}>b)BWN8
+MFj$hVqtNFq~IIIRiUJa-U>_LiI^Pfvsai4rQ#Rqf(%k`d^6M1?^5DUsFJ*+QVmnk}UaKAGyi0N$P58
+*p+dMbvHepZ-ow3q|v*PbIv_ge$U|uBJa@xs(`n<%xWF$x1C8F8{#r)O~>~sEDRlNMK4qb1%Ir4z4BD
+puRU7t%@{fVCLxOR4#9;#u^Svb{>n7BHNor{jHLEETH7D^{^B{G;-H3OILW{Q8fKqBy_l%gfvpVIvse
+MDGpN;<!8yD;jb~6yQP7rsysMhayT4_mh9j><wU?RQ;8w)VEe?E!dm&46fCX#^{78s*xldsJ9J}}l^P
+zCKzxY@&rmI@a%?>s<YwjJ8J|n|XnTG|H#lX)WuYF%t8!0e>3+l`WP~A7z-}qSo4t__EL%`y8G)~`d-
+6hum<z);#rUfRSw62(TNIr)BzipB8<iE|E8y{dkkb}Be>CM?IEg2R4kahnE-Gnd5~oK92}mrdo@kq|H
+sP1I+XvgXuV4K8{rOMZo5%lnar@s;O9KQH000080P~d=M<2D@_8J2K0Fed&05t#r0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoRVlp!^GH`NlVr6nJaCwzfO>f&c5WV|X4C;#mxY{_y0^1F
+GkQF;M)?bip6ge5RG_r|Oq(M>%)L*}kw(M*gcd@IJG@N;S@6Bks-RlnxM~{;^O()4{o{eZYT_k_eFLJ
+w`F0J-V3oCb03q^@3n53i1{E$kC$k-j_h((=q>O@N~k@>*StjvKenUee!4CK$*yIdR?p{lC2%!QXmJA
+B%MvBwVp)6&>eJhv=77qo4Ti0`iwHeekZT($SqOKxcJ`STxuxSAeOtQ4go*HOxj?K2lAC5h4A%5Ck9?
+YWyj5h0oh`<<3j?X0ZmBY~kOZ)n;0z0s|HSfk|D3flyiO3O^nl>P<8?x`^~<wBF?Lb?+?jPN9M@td)z
+uS_A!CWLBTKu%!o*;elC;p2QwQ+8lVkF423&9dGqnWK0rbJmVYI68!w+rK(B4n(q+2#pQkkM7<ROPq(
+hpV>M@=q?(ZZ)*|zWGwZB5B$;1Lnyof>4p@#Vb7JJQ&&8Am0FP0N8CNk#|sdl^aXQ7Tc+A^S*tz}kW1
+ettFMdoisJbbeT&mHp0A$nkqfJFiUyvLWOYyyz>sBy_6?W=U^YsVuc(NJlj&sj6zGp9tNAF)XuL=%re
+&P2Cdqmlr?gzB%SASdh%)B0$HC2OE?+z@0a+PXS+EyUxmNoV8tlMO6|@)6j7H}yF;gV)lN_4=)kS6W?
+kE@~ua4f6EJ+*Rrxz<RJ($0D{RPTvhx-`&JnGZ$10)IkU7-gV%Ez(<^jH~V`!qDphtye&+S|Lq;P&I-
+)9rw0ogwn8heq><|F}lFola>@MNzfQ_rf~%2Wz&_j|PU{XjUsPhmGe1ry2X@@wBbMevx|TJcg|j`Ce*
+1mnFxOES$_vC+8BtTm~;pbzAT_y!$k`d-%smjE&SdY@-W5=c>2q*=&Z5r8=El3Fq42iDB<LaV!k%3k*
+j|n~lWU*=%}_RoTYFbG<Nnn|{d-U(u#&_0H(a73sY<Y~R(s6a54BfB6?jdOtx}EJ%;wMcR)2rtRiNi;
+91iXxZ0R(}&X)A3A>nP)h>@6aWAK2mtey7DwZfttM*{001vE001=r003}la4%nWWo~3|axZdaadl;Lb
+aO9oVPk7yXJvCQV`yP=WMy<OXk}t@Wp{9LZ(?O~E^v9hTWxdO#u5ImUvZT`h@6tF<kW5I)Xh+`WOE|Q
+8cD9l<9akl;7Fpv3xfkhv%kL2?%n|hl5*^HrkY720e5@*zR&I<&z|l7QK#o`CRgfma&~@od#+9|-<|!
+Uo>=?r;F-Ex8uhMB7D=vCbymbiC00#~|0HRu)%!eoG^NFxXsJtUDixc`L{(9$2unu?p28$gsszhkn@5
+utD^sdsuBttxvA+4aU2gD?PKyjruZyxuirlKGG`cdenr)cCE@fRBP+M(4d0DC6SzkRrK7IibdzX*YI8
+D_Jt+ndLSW`ZlSQtMm@~TW`HN@%J{!3<Us>=s;ourF0$<$D-z?G^Bb-k&UMef9fg3@GG>T*N6nwQ2XT
+g<C982d?WidscFSEY#)E94<}rF0(usVK2OE8=9np+|KdLmc9|GG%7H%HCYPSC__uQ}xE=ri3)t^(;*y
+Y`#n)lUt*-RV#XCm#wBQh>=^zh`K01UXx0HQYOKBI-87%C+hj=#GmFU4is$K(-rX#<E?08A53pl3Tu>
+p=V-4AT1jJ-<V<!6-QyP!grI9UlbRV-TQjfI0VrUt`Y^eB`|kan8ee@>AI3K~<Ey)mKVdC2jkmC$P)f
+2@nt(8*Ds^6Mz&Hp@&u`A&V#oM&ayhyCNZemc?yk;nZ`H-S8#Pwf<D0w5+55}!jk<n+bN%l2d<2g(#;
+cf=xlhTBisvAi70@JHUMHy)(Lchx77V7bTIxpwGe-t75-W{}SZ%(`hG~&6SYB*t75bA(<|;3$fm)Xdd
+<WjQV_i^b?K^<eN27syae_rUe@J11TWr5b<{*8M7DYKwr-iL(>2!>}$Inkrj)x~d9G@ul{uWQ3`cdas
+ufH*sJUcj;mqn&VE>o<^Vg~79Jqn>SJiIm4H9zzV5$zQ$YW*T}Ra8@WqIu0pCdn7g%4|iRB+J&NUfma
+EeBeFN9TgYG=0}LMsH%0EZpb#pB2WGURl|$(MXeWx97X$V29v5uMINlx(xjAJ94#_FX&oNxP5Rc%O0%
+Z$HK|)>4@h;5g_GE5yS3csO~(t_OL5{A6T-zb%;i>DWxgPkD28M^<)p*O?YrTRKfd_e@W(@Bq<<zB(z
+*BadY$BPv9`mL=PzE+g3Dr&M4c5af#GETiZ8=VN5dumO)Gx>d211#Fw3U}T#5)Dwmg%L$omneup9IaJ
+>(T{Cdr`>suf7332Y6oMk<1yc3DS;+~$N5S+y>ZVCHk|$}3VlnaS4pH=oYg015{a!7(Eh(nbi+lia|}
+am*%g1r448Dn4Z)b`}&_$6tOt9v*-B+XZD=TVzIMiH(qNb8;-UV1_DT>Gj$mv-xGBs-az(D4BDp$BB*
+*#7SgXy5RVuj|u*;%OiySLRU2eh4yyX0W%RX6aR&LHB?R`lTETD)n!MvRuP<OImhT$i&Ta5YJ~ukq&7
+wsBMQW&LRKp&P@Lf_^TiR!i%=c}4FWhaWp3gDa#@O)sj0>^enG}-U4coIhLpn<wPUQQ(K)!0g5hG1nQ
+S5fC#;rwW~%lql7Tg4HI)CT(;C_UWqSYouYRA?Dya36JWA`B&Y3eLaj*I<jERd3Z{TKLRgpp6s@6to5
+&4*x4)Wtkg0z#Al&nbcNwpy}9ZHNIZoN_&q_iTV6#XxpdyvX-R7~nmBzNFodQ=KtLM>35YdS(ntJBHr
+iCaOU=yXxQB+G2X5;R+_F}6Jh-be0O>b0eU%xN_>bG8KIDA_G6Ro0|)iqpK|EhiV{qzZ-+zC>-NqX)1
+R!+vA987Yi7>?A0)klcuks6!7_<Q@nuabo+FY9zZg&Y*qsq{Lq+UU<JkSP`)CCwx|)*2^L_^4c@iyaq
+7}uYKEu>NlMMDxaV|T1P9exx=4D2FF`@H5!eSsiGrwNN30FR67Y`pz=DSGQr%P=L^DXqGPE2QS{`zfb
+87pSf{p7hmX#SX!9O}vC|&9H2tKn?Yh}va}G#H5gv2o6%Ii2GV&2;=#i_C;+J-XqMghVKBL(3)`}8@U
+8Q>CXz|g^x?puIAQ{MC*ozzAr2|k#Vp@Uyh&tytAoM+Ib*YnncR`KcIHPt_C&iLZv%(6du#hNapoP2u
+Tj#Ems3^IC+l!Jrlr;q|1h25xJW^%@pA#f90)w=3Kx#wWW!58K>*d%o52!(6XFJDffufeFdd2z6saDU
+5M*yM#PZ01Rdu0ZhC{Jvr9&(g9T8TuDS{Y<u3bJlA4!6-n<Ncz)q54qJ9~@1{+$16=a%tPz)$3)_iAE
+B~`4#j<?cpBWv#Lud+qfQ(gz6Hgb3u5NfP0f1H7P>6mvWFWN=!`#t{m#64nassi0&anseK#C40j<@UX
++<m2SEh-U}-&R3q>(zwDrs(oDSL`?RhYo8}aIyQ=tb3l1sTd&J%7(x3LeMKzX2D#DF6>h~mh*eYn@_7
+cPjDRZ$V;W1)jx5L1*}Ro#U!bzBHd$2GwlY%Owt1M2bu^n`9`&4tLSUr~Wu(xVog!)^dkNCD1|2&<J8
+5Fk}s-i;~;VAz4zZ2C~Jk$PP>4H0=qAZaJSD-L@Kb0`o^m62xZ$%7KIRpU$CkRitBb80upngE+~@)3&
+oWg;Fy$e(Ja^fc<3NA>iqAq#C%rifb<0W^Di+G&RzHgx_e&!|Usc>tjnEm;<v4`7Bg<3=Qi4NyiCsF8
+W{U+aWX1Ua7}B<=?TAcYfe-;_nY66&tl?_<Sz+h!N0Bou6<bn%9VL`tP_J!ql~CX<{ahX~S&;e9-KkT
+k_2fm)ICv`LChrU3}uC?iw_JwtPWtk)p7p&&P9;YFNIToY~F6Q4f!N=PN9EaEz%u0_~Gsa!)Vjrq3JC
+8f}nzEmayH1J>q?TV(Ga<Q^VD|gdNDuQ}}a^*`@8#GM`8vquqhvVC`$)ppT;ZnOoLG&0WCrC@UpX)LV
+UUP=dH^|orKXOpQDs>bd$Ea9QoTWy{jhv(ow#_9;8I5h;ccluFD%65>5jgdV+7*|1Kxm1`78yL*y^8O
+3TIW6z2e)?~z&9N_FQeM^A;~GtP3dn>SrFV7ZNd(MP~-^7=LDogHH900ZEvF_JzzxDObWvMIzTmsEhG
+TB1PS>ycdxTLNgZr<&~TePp{62b&1U*RL|y5m92Rru!5~Ah7|M0(?kXXK*qlqTYDG3pU}p0Lzd%Oxe>
+KhM*h4(3><lX;!xH&UnM%8LF>`8mitdCURd;kDK2>kF_>>td!k83Fi%BR76UrE2z&5bS){6zz3dfhvF
+*2y!<_zH`Vu9RSUkDQWZ)zTm!^W-goqr8HVy)9^YbfKUP|tV*XC$>s1DYBORd+<!=~{0n{s9eusE<d-
+$K+Np_*`TG9jGPZA#V;$QnnS_wxcG>V$xV4aC2J$Sqq2U0t<MST)nOMm9`EU@5sph?I%}<R}@9E?~ua
+f!_rMq--O}o29~rDU<@_!(KHN4l6+X9WaU*;i)estffoE+ET^-aQoV!$no$#Z<<3N6Dha<3q-iAVh%(
+p_j*fE3l+K`HxL-wKbakr@X?n-(14pANNxt-iy--VR=%+HSvd~C^5S6R6G!K)Reqa?@DyczAi&PNupm
+st|fWoUVP-lzZ1BDKJWNblKSR(}VqXVvmL5%-NwXZv4Asohjhafz-m|Tr7?|&Izs8{Ovi_r&H?{22yl
+bKsGJ)g>h*e_|G7`No={gphJ`z8L#{MaqIK9|3L@GJa-Kf48Yw_`Mi{OE;W6FmD1NjU`^$j~pj{^qgi
+raH47F-0)V;fb&4JQ(r7Ix~Yp%t?NLEJnz^q*10VQu>c;33o~JjE|<+M0z^8x)%w~-i~kXUr%oD##d(
+~+>5WXEbjBa@(R4hkTDNpX+M+GcdyScL;jG}P7V(KIk|lg=HH&5-kg89zZy^JRp?&_m+#(8&UTk{obu
+q{AWD(5I#Ji_RL1_x1A^MInXCJIXr;Qp?@?-W&*`VLe$-vnEaG%}pE)(UeDF$LA**k%5v6}+2_nH%z5
+YSNnLOBH%Tc=^M<5p6(YI_m=<&&ga{lhR7BBIzDeSALU2wT#RibhhwRL!H5Nyw`@s4v*c50AQjQmZrS
+GfuWs^RLvxQ_@*qfAd6atNqt>80U1NtTe8yCz^}fNvRqyNpF$$F{J1@+)<uj<(+foOR!`_|~)qji#LI
+yuH*6q35VI4oq{;9)9n3vY`eZIrvon7+8JRiraqE+58(06jE;4O6)!72**tnoGIzQxxDH{eX_-;Cif0
+hlVkm_$ejMy>+{y;Py7VA^K*xzyvU#AO59KIvbwW6PCVArYEcw11<LRuOnb3(;A@@}(KI)pb(ETDSyZ
+MvQau{+qlN7I5riN|r2#ssvRF}RoLBz(n+_I;8~S3qR4yCb>_M6t@68uL;imi`x5lp1x^y%DmR6)&5Z
+sEy=Q6$3%4l|Wu;^yME);mm)Dog&7Oop`ni70sjStpNGdS8(wTBbehGX^@J?HrZ(zlzt;9UGEUqG0pM
+%eRIoer8Ax)Ke8b+3s=ZivUD_M_`!bu>^%=%sYFI^si*ru7VI<{=D20(R&qXv79G3+|ps5$@`!J>#0M
+P{B8Fa<L}7D~PHkH8Z)HCzZ<-wv`!Zb(>cJk-AzNG?3P%l?+`)Kun;u&=H@b6!B|KH{+zZ(QgG<i)99
+?1uf0*9Aga*2?A$sOzZ+ipVsqE%ya@m<M)kC1RJ{)GIVWNgG7VPm##Nzo1tdZ(tJ$d+VZD5qJUb{8~M
+&?%ircjn#z2RTvW6dElu=b;!vWKz8Z_a(?<y$mWYHCC*y!zA@Lt8=nf9@ooN_t_|a6mV&75h43kow#A
+N!;ZA4@1E~yUcDvdPu9I)1xC$KkCe>@3IAkZgN$Mf%!aW|?ad6zGjHT?igyfY+%AQ`Bs$>3mhK5&u4N
+p{es<US~&7;j>(o2lAnf$kt2L+4*2!yoEw>7M%+>Nx7ucegn$qn?E>6eund3*t<>*KbS+t92=TlL><%
+g)@Qc*1Iw?5^W#-@Eg_uUM;I9eU3Tr9?&#utuouRzJp;TyZ`?r4;*nTLS6U@`ls~VabUTtilSPe8miy
+2qzc5hJ)KDL_q@D=wS_owp|11-B^daUCwwvhhZDRpmBZOIJAfku<gN<Y%tKd5*10nGr7lb2IOWb&yq^
+Z%G*aP6?o>^>i{KjR&_yRba5j`gw=+iGuA%|e5710q6`8Kweaf7%0Goz7p}!7iNe5E*N+Y+6I4YCtjh
+32>S^IQjz@sv9%^81WRc_p8kHjmUKUnt}O1c@TPt)<WD;w-La{-JC093ee1+A)AuiU3iZN`-&`D>c3*
+@31x(NTjevJqK=KA0g-EEr8Cc^&u@{kYmEr?kva;R04=hD0~_G^@RbTJDQH-SoJ!wd=_aQO&MFZ@TP4
+GJ5xl0etsWV-9LE`2y7gw4sMl|E?`3Hxuvd^eXGL34eIv7&$73PYkPK*z`;^lXb9F4u%!OuEe|D6ZCx
+AVP5yH@l19QzN6mX&!&)OukWG0ei}Z1NjxxrpFa&xUWOs{`4g1;Qr<mD?W^7AA781Hy*ts%waj;$fo~
+V!`91-*YvkuoU!kLZhk#+%yd$4Ut=Z>?SHvSZBB%Ps7x(#HueYZ`WzXyLCo@m+*?Za-c%$l3sE7~upU
+|$?><^;Zuen95^}Uq7OKbZ&@__$+YuzevauDRp3+$(9ckDmbL?1xk`8qsC8N5cWN|8i&vYh0rfS6=1)
+dL2IQ;$>HL66UG?T4WaPNzic2B!BkM%|v+JX2FY9rkGarMq<8sWm?byWHe$?=(?nX;SUn2V*9usyvWM
+98Hyjn#DA1J!>_+KZ1u}vt(Fd8iN0LrJfi%zT8(I_KffLlKuNDHT)jByMx2fQ6|2e?I^24W#!wqcElQ
+tEeU1tMM<kkek+>n?Hl57*=?_8di}leGTb-Q+1c6;Rnr{&54Af${XM#F2>xAE{S6equH{<}zP#X>z<{
+Bt`=TkB-8j!3s7!BWbYpddJ(j*>?_$YVZ$|q)gGR-_gS%eqvg)|~1i8=u*MZEi={qL*#)`fpEBX}Ox=
+!iOPWSA-nfeBGcXEV)v7O{_WB4Oex>J4R|E+>Q%J=ui^L=-249IDDM@3Kj;|>gz67jE4i@m2ja(DAy?
+K|YR{j|-O{{>J>0|XQR000O8^OY7ym#>-*zE%JL)3^WtFaQ7maA|NaUv_0~WN&gWa%FLKWpi|MFK}UF
+Yhh<)b1!3PVRB?;bT4XYb7pd7aV~IqmAzfBCAXEPd4GOI0Kxz^Dy`o9T8r#LHz2FzYSs7)l8lD?rjVA
+7%85#4sxniq*8KY6^Ds0Xpxi*WE<$<s-efSymy2t?>m}o(kN)<5*DpW&$IpLz{rdAyKl|;MpIyKF_3u
+9YzpwwrZ$Ena==$o<&)4tXz516|Z(hE>K7ISm^Y!Ze^_#c<&#Tw3U+%xZ`|gJ~ufBfy;qANY>({TI-+
+Z{f-oN?y>8GNfzj^iH)h_uD&wqJ-{r3Ckch}o*uMdBt&i(t}K7V-s^~>*{|9{oKdi&=6{`1}UZ{PXHu
+K4=>+jr;luirht{P6tE^`{@L@ju?`<vZ&7{>Oc*KYzG>_UX^Bvew*H{LP<#e0}oz_4Nh6eSdxN{Qmjf
+U!K1?Px9&8Hy_@;`qK~lF@N^{=l^+sa$Uc9`R-q@-@khOFYjJ`cm3kpE&OnOczgZ+#}9vg`{sWB-Bqt
+&{psDycR$i=-@bc(zTUt6_QSvLhW-8Z<J%vuuV22o-aUWw>iwyHcf^P5<(qH*)7y8u{C97^dG+m&eDu
+ScZ}#KR>JQKFzI*>uAO7QS|8V{K`TcI`^^ebQp5N`K`TY-ndj0C_UHI3pzJ7l5{`q?O{`#KJ-v9ZpJ@
+qRZ^5xx->sN1g_4e-HU(c`h_uTt0&+p#vPp<lL`RQ(VjW4b})<1jsf%fmw{GNh8-;Mt9dc8;E-A^Sy{
++nI!*C+kvdiBO?|GfKt|6^CUAN1chtgb&jUq8Hm{`QC0FLn+4x7UCF{Hy==yFYw&ee&BsUjO}*FTVKX
+w_pA7@Aq$aPw#K{_??<wefRx_;5XNPs&_BneE4xUd{^+B&%XHdzwHvA{POc(fBw}UY5lK0|LV7&efj0
+}tKWTbeRBQ&lP|vd{L?@D`jaoN-~Zu@-~aB*&py7cFQ1=(>S8+cZ*}tj*TvuND!+TXd-9v-4=-Q6et#
+bQk9&CE?*_j9=KAx?zdY|@{`&dVM%49kPm%9`{68AQ*Kgnai_g1@{Ix^>{(AN8_2%t|7uUbPd$qB%(f
+1!lch~aQ@qMvD|MkZ&u2J@nUcUL)*Lw)Q+{J(O>f2rQuU@}>`|ic{%eU`8@YCOXvdcbdDUV;2_9)l&h
+cEXh|EvG5&wl$4|C>hTqo-Z*moFKt{Z!w+d;8t>@=pw8ef(zQ`+t78z5_^mZoc0b`TF$_+djPB&$lhW
+54)51^w=Hv-S<BLwhzx=|M|_^*KhyjNBjJrUVi`b%`-#t?)l$-cz*Ns#{0`Zz22C8`TfDh?xR1w-4FZ
+aFaGKCFRkmdKYaR&fBO9~S3hbp-tVb=@cH)Lo_8P9-csjv{o)t@^?H1q`B;5_R7d>u(HvPG`=e3nPam
+xsZBX!0`%fR`?nnwg`uypm)myRYpMUd=G5TqKF!#P-(60aTQ)SnGx_(xEasTo2{duV$53Jc2`!RMW{>
+QR!_K#@NE?8HLzl-{Jv;EzSU+fQmZpCGtyMOiL_uJh5t^2>&&-s4=*3bXpi>IgEY5)AyAAkS(r=R@#p
+a0?WFTeWY^I!hqtIvP;+kgJzll{M4W&8L+cYXTjm+$E7jm3Za-8+pJ<8Zw=HGJ~RUw`(`Uw-~SKHDXF
+i)qD_`ReUoOZ=DXXHVDFA1}7CEnMQoRc76n!Da3*>KZR<sk#3($BV1Y@}lusZ;$?>Jz~D-@rW0##CXw
+0*(I>tC7$*l<;AY7zNooN?qB7LQdWCWYmFD<kq<tb`!-o#)V1P8seRvO-<Ho7KiO@XV}B7*PQ9%@l^T
+`rb_1iV{nOpHwuhLz*WI@E*mqs)Mac)vZ~J$(&RzNHTqY0Fx$Kra`dx3IPy3JhqCEDU_GK5^pK)1SI+
+y+Qy)`boqQ~yEVsTo2<xHMoKVcpA%{cW&f2!qSAMF;!zHy0N?{0qM(zy&S_DFJ>FZO8pueFQq4jqp@4
+sG$6Yd?tf&d1aK*r5oXo`+nI-L<=?OQ%DNb?mXMV;8Tp?l0P7KXakw*>-dKexw>}*V6KC?~145Cv`u|
+evIIfv|~SM-=Nq(-Qxj9>Yi#eb%KJ$?|81sb4{LW@?0~lx0FhIZIQoXbQh1d@3k)s=%VDJ<f7!FM;0X
+(CH8=Ba6PybF7?z~=hOb9@k!@0xCEEvGPzKaZY+h88zbF%OQBeySfN;<Sjkgw@4MbY@j~%R@k;Rx=Y6
+4MrDUZfa0Z3@LWxR=Mu|p=MhS33y|KrwG)gu~HcB>1HcB>1c1m_ic1m{c)hW>_(J9d>0gMXpq~45O@1
+W$M<Q}L!WE3A19~2)H9~2)H9~2+lFenxjV`7z{MD$bd+T+H|-WN&+C4-Vd$)sdbGARkB3z#k`3ilWd=
+36kM=kCMZj|XS<F`sr9fx+F%GIwJNINQ})5%*#?l;mF60&LN~aIcNMLf;hdxW{78DeiB*wLfj(4`#Md
+#|&&>?e5)xUAuwT+Cx_RgDD(LSEumqf;|jO;YQ0BpY4I)Lw@kE3+#G>dND73Lu~(Q?K!aT&_!qg`32+
+`kiUP;g=g4^-|JZ6cWc|pUFCk5)4%(3?9O5yIfWKhtYPr1jo$~xb@yPycI{!?#i^}fi$>X#sDaTvb^{
+A^>_g4^w12Mw*|jBg1ok0!v-W4Sl9^ZR?qx-N_ZMdN+Hh`k#x@5PlY<4U$FA2opBnaIo5O7tn2^^sxD
++k-p6%D}!CeKm(?w&C!~U^L%HXrkzp$s&OMg-CUQ3>^!uw+@Fa8T7yzzUrWxWlfO=B)DjT-{fplw&B*
+q%Rog>WG|IBWIsgq5I5J|6yy7G2cl>eFcI<q~L>`(LB74uTA(Q^CAlU~fOsU2ofVa@f>8>=$!<<9)Cg
+?UJprZrN{zXSR6QRraNw!M%2m6wLh9%E50N_36T5?SI;XG__Jkv6K12*|4m)upToT=+)g6iZM;Fokbg
+W<AL>m|FKI}Y-q)X)@?)grE%$81{ZqTPId`Q>fIA;&k}lsR$`<s?qI!hk6UG#))HL|C+qx$>ANy$Ww%
+Otnz0P}Yk#vlh|00%`vGaWJu(l@`h#&}Rcj0}rgvL6>ezj`;p0HDk^?pj<HouRH9fL>x;?<&m2_Hg!*
+<sB{TTQ=j6FkvC$Yz$A5eS%F($$;M5Fh-*iW?or5Pv(kMx7RM59L>JnRK{_k->dmZ%k<N)Y+<d`9yxy
+NX%EQnBl$znB*+P`f#Pi#_XiKWx8b--#9t0d8$R{q_cMp)RaV#lmje727_Y*)T!v{<AUZAH#u9E(Z^N
+`z8AiK4xXCtc;cQqOvH`tbG9wMRVc5D9(ylSrIEMVr50FtcbOq^{wNVR942y%BZ7PSsC%b_Jxulw6Ze
+dn(YfWWJP2w_a(4^{z<WRw|Dy``wxn<JYux>h2kuabuW(<hh?L=&|bWj{anq}q;7aLT*>_}D#uvsXns
+Ve)~EgZ#BJ1pqzQXr?clOAjGeA+6Bmw2+KaOWtB>2!8LeF<4P*7K<KS%Im(;;eS%51GFpl8>6#K%Xu>
+e;`h6R|hA9}VG(_e!UEWDM4x5nLG{1S}ozEF|{xcX=;z?B8KvH)YJ_Jxuxqm^Z}VmQF)zEEOPVxDmu_
+$9c-0|#<nD9Pegao4cA1NU-YC~2><Xy8&0f`Ne>Iv9dc?ca6$l7U~+FgXpA)95^$&_VDp@RA2U=3wm~
+I*=@ZgTWj0{-DqIg%W_Gl{4Q4eo4b)Yv8hh%LXnRxMT^~7fRx(HSpNLV*`&3JT~yyz+(fC?Y0j)9@+J
+R%LXnRxNP9E;i)ws+3?gFP;5Z40mTLs8&GU`Y7I}V;i<J7XR-Z~{RbsGCBaI=Q)^(PfsqDA8W?F{q=A
+tJMj9BwsO<|Sz(|WTZn6E6fr;D~N`jS!lh(jU10xNLG%(V@NCP7cjNtPRy!pXmzb}-?Gj1^$H?TsycX
+_+$Msj<W&HdB;FE85%VjLQixiT1y@MteEn6llbZBB6_+OcPWU$U5LtNAgFnL4X9b)&I|BM1~5P;5Z40
+mTLs8&Jfk?F)~z&bqmyklkJ&*??pNl6VdKLP;Rmn4}#jcA(gSVh4)&g8MSK1efG;>`rdKWdA`)AlZQ=
+9_zkP5=eGz2Fv5VP!cG1o~H949Vm97*nwiN$KT2Am+U_%2_!p^>_D;u$qpomXa|Aa(9xq;_k|Kbu>-{
+p6gyDt_j-}rFWG-k5=eF+*?}aX-o8+h;GqM>4ir03>_8E3dS5636gyDt<E-<!{gV9$C4poIk{w8PAlZ
+Rr$82?=*nwgPiXA9+pxA+82Z{uzqu>5|;+J$F*?}Z3)V@#>NOmCEF<TudcFb1CY;}C?4n{f{>0qRTkv
+{Knn~WP+>0kwieqSgFMmiYjV5EbQ4n{f{!KEJqj0`X`z{mh2<M!9*_Dl93gHM7>a+zEx2}WQ&228}jk
+sq#Qt}6qK3@|dl$N(eb_N(XiOZFd>1S<oq49wO5Be<jcLP;<(z{mh21B?tXg6py`lmH{6opGDnFWG-k
+608icGB8^Mj0`X`z{mh21B?tXf`hd$lmH_Gj0`X`#u>M{{gV9$CBe!7D+9ANz{mh21B?tXLbS3k+zX5
+hFoNT>FO&cyBhI)@{E`7y23Q$jWni`j7#Uz>fRO=41{fJ&WPlN9j(wp77#Uz>-0{W4FBxEEfE9wMeW4
+^68DIo&eqSgFMg|xeU}S)i0Y-EkT<qg<j~jkTfRz9%0agNg5?};gY~T(Lc&-5jG$eFjQ-<+5;n^@A)6
+2qjq22{wf?qOlKZe#(!x&&Bz(|0R03$?x`@+4zNPv+5BLPMNj6^%*w(v^=tOQsIuoBpl03!iL0*nM02
+{3}y-xo@NkpLqBMgok$WmzwNNr06AD*;vldlFzIzzA;czEBd31Q-c05?};^Z(k?@M&jll7Jf;9l>jRN
+RswqxU?jjufRO+r0Y(Ch1Q-c05@002NPv;J<BNq~5@0333PH($oZA;lf)P15*sVcmK8T(Nk?+u{3@{R
+4B)~}A@zBCA39u4iCBO<a<h~Rxl}qE&xePABCAmy4lmH_MM)JgkJ7U=52H&<Xlmsh@JxMT<U?jmvf{_
+Fx2}Tl(Bp69Bl3*mkNFE*$;(XT&RuZfvSV`<jf{_Fx2}Tl(Bp69B0(-b8BPGB{f{_Fx+54#%za+s*f|
+Ud-i9LZY*)y1uU?jmvf{_Fx2}Tl(Bp69Bl3*m`aq7h{NwAV&CBaH!PY5*!G{?SB5{x7mNidRNB*936k
+pv?NMl$2nyZw^=2PMHuf|bOcBp69Bl3*mk2yFI%NEvuT14n4!2yH7w2{4lD9yk0F2&jFbBv?u8NrI6C
+BMC+(7@1&Xf{_VE;E48Ra0xES<&0awFPUIvf|UtYCiY~4kqJg77@1&Xf{_VECK!Q;+ZRfJkqJiT@k@4
+qzsC)#cwZ<9RwniYHe*{<N`jFIMkW}UU}S=k2}ULunP6msk$L=*Qt(SASeamDf|ZFqnP7w%dtWFCMkW
+}UU}S=k2}ULunP6msk$L=*Qt(SASb;*^7fNDJCK#DuWP*_iM&R%Eg?oXK2}ULunP6msk$L=*Qt(SASb
+?P&FpB#^Nic%{yDyXkBNL2FFfzf&1S1oSOfWLR$ej1M;g?LXGQr9OD-(M%!N>$76O2qSGQr3MBcwC-B
+K6=>xKu8U%Ne(dU$Vf;0xJuwEbPewBMXcyFtWhN0wW8IEHJXb$O0n^jKF*DW$=t!#V=W4Wr39iRu=XI
+7Ij}J2}a0E44A@!r$6xU2Oj>=!(U(oW_7INmz0WMvcSp$D+{bF?8yQngbe#aNied&$O0n^j4Uv+z{mn
+43yiGemz0WMvcSp$D+{bF?8yQn3ydr<0^z$almsISj4Uv+z{mn43yiGemz0WMvcSp$D+{bF><N_ezEB
+d3EHJXb$O0n^j4Uv+z{mn43yiFJj~jl;0xJuwEU>b$Cku=$FtWhN0wW8IEHJXb2zjSL%4xv*4hXRAmf
+YjE{gVAh;giaxap_zJm*A3ICKpO>j092JzEGl2qEMnx;`k+HhvU26LdinOLdi<WO36yeO36yeO3BK-D
+kUl<DkT~v8YPZjQg%4L>ur>5lx&o2lx&o2lx&pjl<bu3+^bWfQ=(I%Q=(Ji_$6hB<GbEY$wA3M$wA3M
+$wA3M$wA3M$-%t_C4v$`iJ(MK;`k-ooIQ5ELCK(GP%<bPluSw{C6kg#$>d&1iKIkQA}NuSIDSdl;rOn
+1QgTvqQgTvqQgTvqQgTvqQgU&xMTtd;MTtd;MTvEf+hW`nB^M>Z3N&QEa|N7MKxYMXR**#tIL;u87G%
+)^PC6hdgDhH<+b`L1@>7sV3leFSp{Vp*$4|XZJ`{?U5U&(-53t{umj>Ywq~3zm+XeMd%DukrIQgkV`&
+698iVxDTB0=_~vG85Etqu0@A~q=Ctl^b4g5=7SY!FWsKB||KcPcwhe!5sjD#oW0vtG~!mAIi2z+c2j#
+Jew;ocq=rH<j@7GH+^z#Aq9Ltq`hM@AgYB{M1S?S#Wv^Tv;KKYtBkiYJ~vdVx=oGPDEyfUb+ZjFCxjp
+GgMdv>)n1y!Q@m})Qg3uK)qfNY=wDrVU;d??TSYQ)3jT;>uoS@)eQMXY98*bGEJ;^`y~ZGs<31>@|y+
+A-Ee6ek?MsXUT~%^7T}8|zTxX$Fpn2sRD6UA6;|$icNyeySiB0!$P4GI5Yq$+tc#er4BJ715JOW5gD-
+rsN-SKlq7`DTvM_W=KkMCoNyC6t=0JgAE-VlgrZUKXHKtmJAFNQs1unE;Pn#({;?j$><V7TRVREgvsZ
+#(7yN*f(Uf^;XcIqNvyl6*ZK`U6yg7qjYZG})F%0V)@JE`)+gYVnb-486z!<--W!YEc~{|dwkxz|GUT
+rq+TKkg#tsF=a(!tSaT=28#;yYV)k8U#>KWrYVX%<+OdTES%@o-eG?7Yk3pA*`_P7p`n2cqzCp7a>4d
+H_o=tMPAP*pwXT0hVyuktpse79PJ=Ch>8H7qOd$)4Cu8?dyrp+^{6<G)Ene+2)V1-RXDrCVpLct3!!O
+{QLpeu<ze=STjQ2@BHC&auK6?pZf?7?G4)i*5Lc{efiAp=LMjR40=renMi+avns9rVgK_qnr(>=%lL$
+I%KY6m<BJw!oKFO1nuGRq-^I^7Wx1$boM&~wxCW21JJ`l`ac>mXs(X6JEkA2ur1@8&`3-~4V1!+=E2@
+^0DmeB&eS%wK%29F@85X2RESMnzp^kXgeyx4K_whx#&n9w$yWlk%NO2xcXXH)@&pmva57A?nIVLkXUJ
+ov?|vEJ>MTr{Dg3ApgF;F16lCWm39FWbY6Iu((MXijG0^NQW8nCMcqZ0*dqKwkpGdf8NB_Y2poLI+fO
+^)l4~K~l+JHhl#I7Z-V%YGMJ`tPnTVcE<rbPR6b?WTb0gM+^RXA>UH^5x@W}ycV*h0RRf|CVhw?z{xD
+^CJgr(w>?Ros+nA~{U{NxIN-!S&~lOpwYvK4rT8Mx(8y<zW80lp=%t5?Aq06p_}y{xQ-#mO6J~mo17=
+#0)VknK3r<8W67Niy6zq$HRC8GZVtf8n2!V@ahxKm1qyof(H(LmUFK$y2ArzLo$~ad-rUr>s@fsSwSc
+R(YacF!DEw+31yC1e+QlY;(<WX@gA*odu>;kz_A-NlmTZu!OMvZZ+cx4@Tq7beZR+tX^WWC!jDWR>yY
+(Z5N*yoFQ<RU_=<RJ@b-%5aR!9W%=zJ-{%sO%zyE%^FZyZzLOz67VLsz__DbPtxS{q!BTrb0V4vKxg=
+O@W>-D0BMpcQ9a(q=j@`apqAk`Vy2kb=Vj+Vb}*^;ffh(-Sy&34uD1OmoU8Haa^e!Lq*8R0-tf4=u3t
+If`z%?^Q}Tbm*Z_=zFDX*5;uj^Y9;Ym;lVpemy480VdZG#1KQ*06!z-QxY4UDEf=}OOFaxrMIBBitQd
+sH1@&-2X<t)=MCJ#=xuQcUWc@BkDeJ{AA(So<O9fp}3B97D*O>G#wthHqaqqgzxq=I=-S#tFuMP_HtQ
+R{@-gceNhJ@%%d!l5IL@>)U_udF)?9otE#|@^kK)Dtd5;)d^w_C^2_wDiM=mxP5M4)g=m3(Z0Dyhd;c
+{p(y&<C!-9`@E;!4ccIC=)zY=<5q6&3d<AQV1|B*}oRY*R)p`v%x8`zreRCaXdBVPwH&!drc>&BB|z_
+y6Z(>qJ{xJs(_>mO0HlmFLDaSX^o}0bRyJhLNGQR7O3Y!pjH@+C^vq$NAXm!>&?Dg$4jS0h+^QpFS0^
+~)JcJVE=9;N%gIy`_+SgMop_2+-`2bRlEQl2;K2*Ruudwrk`KLDMlZ5m4WM*b=T=UYe5;=t7DI}Npyk
+#v?T+cpW3uhcW3oaGTX2ys3Pg4VhU0>xuP{?(YCw4q9O?2<zoY!~p<=@R-6OfzH~Od&fLw5*751*2K*
+i6x<Aw*u$_NoxNVRmb%awTdYT9W=WFDQu_D_fdP<a{;p;GJE=K64L()sf8z_RhU0<i1PCeEj^L5e-xj
+`cY8qI)H&)9@&oQAJaPh7*CX2T2kwVo}st1<9m8D)G$)WpuIn)iCTk`EFFwPt`f=+MwD=(VPpgUMF00
+AvNDvH;c+Hz+Om!wt9RT8r?Oj?bM6zl~}Z4v@66w;hu$@Q6=)MFg>NK-R&$I2>M9ZSMqwHq6!E0f|9E
+(?*83=NxgrMp%G#{Ga+OlX1?J03$#<?4i$&2!G&JBvdAeb6vf4QRqNyK-|d&QX`n(x2^n4tc5pB%dX1
+txYD@*ySV@=^e7wR8y)^F7e-#3*f<5X*_u8Of3S@Lq4oeQYz$O%Z1*8uZzPJ~C1uS7d76kxkouM>Xo6
+0ymEn&p=cfc93ej{;Sm=e_}6Z+yJ0xsk)D$2Er+@!#bT-HmhS}>&-8iQ+|^`fGCZOq=wh|V>VdscLYr
+75*^_JY-+`R<r929E^gwY;!yr|PMB*BjW$RO-n*o~|?xx{;|_@Yhd}frSNKaUvsJDCX>i<N!rVp=_yV
+y{PD3FSy=fm;_%^@<cZsCkgYy1sOmQQ)JyL3XcoQ6+r?%7;7V|iK;N_-F``J%By%>;|tUuwHoo*jRa@
+yFaZnHviblgR`-Icu#yVCbQJRN%2nMfDW}R}R8j5JA!&kNFb&-vdUk;kBp4t(L50)9`{NWKTPTXvbfu
+|x`z45NfD;{R=jmJ{Cr3zemGd<C#1Df3WMdmGtvJE3n25hfJ<{g~zvy1u@js0tu_9r}g6@fbU9NBaSY
+6f-jgK7<3SgoOZp!6ait@;Mw_nmBkqRqmK`B*1@&%hvS@tW&y~ZJ9urb&t<4y8N*Kp4O9zJQ@%Hm?Z+
+b^kFEK&yri9&VV!jFLk!^~>7+|NMY$R$k&8KobD878`tUaR%^lMDd<8AH}xFK9UiPi5dbu7e<k3WIQ%
+$IB{s;*cgo50p?0tDm>sLHAl&M2m7`2tmXa%voj>Q-YLqA-bwKSe*$|(RT-9*_63Wc`Hl<*+lEbFVP9
+aZEGZd3f)-4Fj%qf!lcs~I8JRC=qsdC@FpGd(0A1bIygi3dI6`pq`#!(nyi^S8{B_k7_7y@5h08~v2a
+1lcP#v6Fd{t1y>1@eD^E`{vccd~4MNrFz_LAza+##?>sK1G;6z~h>%#yHdKZ0asb{|k>jpYqED_NaK6
+E9Q*-*w@C>E~JLC1^4Sqxb|mlRZQg$}K_I@dhD{+Y+<UMusWYDn8r;zBVoT`{R8o%b|G7Ay_#(RSkRw
+8PvGUAcw;XX-`wT3r+&0Seis=E?{MR}2}@1r;(n1!c+=k`>D}gyDuBLoiex;_>)V$akMA3ckYnR&bX(
+v~6dC)vl<PDp`9QOKU*9RHwC^EQ#n204pl83)kOzw_nl>{|Kd9brENNk`^o^ZlH*H8rdIzA}ympnmBK
+%-ggC;yt>=F{gQ$f>B1{(51(}DRCX={Zx2O3f_Zlqko9Y1H8C#qK5mM!UzZ*`^9|i=MTT_o(}L?;Nuk
+$t-WzA7y$=bX^<bsLudeVwm5#5FwS|oDWfQ4azQm|vxfr=*;YzX|Ba$j^Q&JdApnVziLT{Fb^M~3*GC
+HP<RJ-*q);C-Y{rjS>SR+V=1oyI5V_Pmwsz$m+2S+kU(hvFnJS^C0?>Kx3F5*L1u2To$lv5KD3*x;Tv
+Q*Xscl2<aLbnPJoGDi3$B6?fy4O<GuhXIEY--U<c83xH7(8uN3XbtXI>L~_<#+j#MQz?jm+1IA=w2&Y
+^$VSDQD#6^9wi6QN0&`mGu<!)VyQklsKCxvV|+V}S-b9fgE+t`(Q$4uc-@U*MXFfOokate57Amtt=-S
+QGwaoX#RqdW`>DO#FDZBwjqd5FO$&@yA^mbqZH7TARv#y+42vJ$h$+bvLNr_`7OsBsrWM_5nXYe|K6T
+!1IICDagz%X8=FTp*S(N2wwZ={mO&;E{!Ss^pIeFfS?)7R0>-c>r5lF7Hc=A)01;U;(Rf8_$<ALJ_P&
+C^>%;g!mY%Mn)(7hJaf(7=ubmLQ7i8EtFK;rRY;klM%Of_)TI!j-(c)L$c1A2Hwmcl1QF)$){<TOiqw
+YwGOgxtijxDMEzYCZs~RGE3DIq7xM9?q51zvx~I9o5?j`rv<w^(BCih&A|<;>5MRI9<gV$}GExf(kb4
+VwGx!)zrKFk_(|qohRwyP6!<rq7@#kpA8q-f+fUYhaZx2fPHDrof3lh;0)bsT|%tMQn;ZHA9|W)!M&t
+eC`v1rlID@L)5{@Cyb<?O;-uYvHM-ZUO2?jF<ymx>iXI~^!ahv5wh&nld0Gs<8z@MGROIXzad)Y=oka
+IqApsiXc_Wp785O%LY8kUOJCq1wxIYUqf79<|onP+Wa#WeMw_lC!wYlp|t1#S@!zfhg7I#KEpyas9;n
+#6VcGos$O-X%RVi~MmK@noT+b^lZfXP4*qdkqWB{iY-iU7c)>Ly|WdQ&(X6g^}(Knr)c`M#R^CGaqk5
+H6Gi$rnn|izT*T;V($d+RxAuoOZntmIKxyY1lkF?Cx>feo3%B@WS=HPW}Lg6JDVqMQlhBi|HlAInCAQ
+V$oP8@(2~^)k5@B`^gtpbgxB*0gK$yG^V_q=KBF1A!%Y{f&n$f(e?Y-`bP4rObyb2<hgkbbgxyq14Ud
+nz=R|$%FfYY6m%;U4y8PtjnceQY-a%mD7v?hQtigWbf2*6#V}RJqrvDEC)fcSPa_LOV~Dfh-B>8`p3d
+q6#0BNq*HO{EUfrF`^h|T6vP;jD)0Dvpryq+9L%`H2Mn+ZBO9?kdJF&=IT5qub?Zl$^C3yd`hYttJXg
+f5K(x%urR_l98xLV2>$Z!)q-|-u|*A|92$N3@zfJ>Qf##V3NC4BLeFi2;hj577LX)5Bn0{2!PH*bXQw
+W<5Zkd|tcxuje4fv`L7p`*`9k#&~^7|`Num`V;H(huiz$Fb;MNk(F)92tIkXeYL7fjC`0G4e3xaFZC3
+PMT75@x~p;5xeTyk6zKe7PCm^GTFaVFs{{*1y>o)iiHO{mMXQdWTUTXC`J%x_U1Sn<JezxuSG8dx1v;
+YQV*MeCqdWB8?mYuwo6qa0v;18isvG3;nr2D@~v~M=w3_8S``z)q&jrv3<Pl?O>H89F3=(s-F8=wTWy
+Z{%3;)~?|jA$-RosmMib@Ovqly3-DjFO>@0Ffc`qYiOh~yC{rJhSmoQk@IQgN9?)5@fR$PzyS4CkKl8
+GmurJ0A6V)J0Z6Ru%EeDc@WUsfddL-rHLHgvBw+|FJMh4f?i0aVa55rX^(2EU?aW5*m5!2pvmhD?g=P
+o8awk5eyv33=1v2Ib~nRPcj3tP-u1IQ8N{CJxq#wpp!#7`tbiDFc!XRjWk3@Fm2I#UVahdF_Xx905~p
+HpFBg69LD+;f6Jwv@)eb?CD`Fb+?y14nn<aoE(G?MAs@~d6<xriC~q0{RtED(|j7vr*Re!?6YRM>%}i
+2h0@?{plw+w85z_2?CuHGHFme#7@G<fvzlO9;vHwhtwUqp^}?4dv#|7KJrWz>jAMw7|8fq(i}iBSjc6
+iF$YWWT$uDO#rpeg$BLX_(0M<7%@VG{1)a57fK`vA+RXr7g7L<E%b)Maz4umPp%rjGuK*W04CtUR~3D
+_`O<!m~|G&B<h*skW&xKL|KX)1il<u-EjPAp{2O~}+s9tWdcki!-hG-4#yEx3l^QV}4DDIA(NQXG0-%
+9I#Ygi{6np*tn17rrFVi4-jH!)!lvi^GE<*Vb!tT%b-yy+{sn3B3#a7dh;sM<grr)FnDy6wSYxL3N?(
+W{u$otBg$3MJ1yn+4fNB!ZV~9YQoQTw->&IgzZIIu(JKKt69Q_Z<;s67zKOMJnn)Ozu_wsD{qowM$c2
+(!s%ahuSFl4lMRAPg+r)CMjX`KSs~J$)xgS0WW5+a)<JZ)t(tl1cfICuLM{+)LI$$QbgSBd_nf->EO$
+Hu+0tsxMt*5ZnxB4PtvPWayW}<=w3Ei^#}C)w>Is{xmTobb<3@27K9BJTX9#P*BRJ*sPXD5Nt>LtSdX
+SV?vzd+zvABgK0;_nGSBANMxP0Jj^dlgvI1zVy@k`hvc<Bov?W^Q;3(s8GXi$p2f?KM)o<1ISdhUWFh
+>IEuR2=Dc>fL@xn7?HeY-s5&My3j9jW%p+V^3OT8Lduhe6docV7h(IB1=!#<5zPU3URmGgq+f%kRz_G
+)f2@CA7)c=8-l{4k#X^+<IiUVJ|_+!xZiR2G{>(~>FfdO)#aQG#4=8j_rt`aA~d`wmmqLPXB0y*6*tt
+2zJ#3{wVB=@L|0~V@l!dYqvH5?*IslTy-5ON8esbC5`gNRobN|Z+t9r>vXxcdQ9qH8tYMCWdYecZdI-
+dp<}ct8iy=SBhHI%W!Gx3(2Q+lA7Ylo}<ZvsxiD-bRY2Xeu6hL-6gOCslPrB;rCkI3diPLMwITwXPWo
+WNVvumFmjh-5&ntp)i@8!@#u(+Yx%T<^XF1PB*hSuQj2lx^a;+N?&!oRCqsF;4y1-B##fU2|VV#xU7_
+h}2)$ys@b$?;3jy<W_>f?l|w8R{%v^>L_pGb&76Ge86x&7jv@lx`K#E2+%_U)}2QqZ+!`Ldk*$){J;m
+v3jV1Iym})eV+SCnO4M)mwXt`bsNX{kn)!j9P`wR?$tDL>I8QQApV7YF!CEFxZyRLygim;x}Dwwn240
+62qgXT_Dj&cws|}X0B+>DB&czE7$ZWbG7P7x#3A$r>+8euH6MU!K-yjNp2z53FY|BgyDdwn$A}%Qg7@
+YTL`WGfRA;5TCEvUZ>y<5GS3)VK9d5gh?zNgcM3_(&%bKNQ_uZvR*nZ<Igrc;NodV5V)Whf%t02)}p0
+}SktEqcEENWsTR8@zQ)YCoW`*idaDKV|NIA=WyWGVD&($}{4^aHxr-efFO8TA6XSY3Hf^2adq6~eiag
+$P*c&ck)w{D6F|*!AX$IL~=Zz~reGPoqjl9LI%vLnld09sB}LOggF6wv>E9Y<cz*TwV1Ps<_uTbg$(s
+>`811g99DW(Cn8y&KI7rCta{!DAb1L2t`C@-Q*;Yrnbk43mdxELayLas*G;>Qc@f`9TV=r#9VjLaFtL
+KVa9j&(v+SG=PCip?Wdx9EpTO5YS%GfN)(u+Eu9Tm*slC%3St6sl=o`VWO1t!U14|QLEWpwx30Z3q`V
+Dkw&sx?vcBwms>6eM!v5lsj{M2sUuKo0>^<Mmy%x5G)M<ce_Qot>XmGLhM_{k=5+7c9f#>85lY2_8m;
+lw3_+2k~oaIDM`v~Yr05y~Uj2qB_JZe~lW^f9p)s{3j+c7-IjU9ny-18XSYgsDzG%q^51~x8t7+hBC4
+xQF;FE2RR!a7*bILQ8JyH$Tb?*83=Nt?&@b+nkXE^V$ale3Acqy?5RRn)^K1NQX9sji*&NGdtugEMun
+RSHiZj}ukEdsRM+bf&9`Q0^z|;qDB=+TNttoa*uf3Iy-<7~ShNoX0p$I_QW6>NtvM7j_8`O%rpaSsQ#
+`mBPgIn;W(J?)XC8>ypO7Y{4E@gDAvAWD9_Td3OnFB$;f&5YyzKJ2)@Oc1`H6lYeXKUb|-i4s8L*kWi
+mS70d>FAjO>Jr10I4K#*+0TDgF?F?~7D`iAbcz{Iwt9z_tT>;mfe)eS1M)6^@~zEm~%#6BU|2oU6OsB
+#>=QA787O~qwM8a&U7Ai2WPSNNR@4|6ReBW1Aqf<rUJpHkW~Q|y~5M-S7`y%y5d?HDElc<4%nDmds}9
+Z1)d+JlljCspVPKTZ=DPx7EAuh-DMUehs^%0#*0iDl$c+hI%r_SF5*%~i(7)*&d&0v_9Nt{}8+M{m^B
+y~>TKAkm9+kBo=lAfdlJ?#MWjU1aT{K+pa<vw?+V*!!+mr@d2siQ)A_rcmm^4KS=6^av|hHQLsnC3LH
+(aN0Z~9;Tn>7?1AWr`D$XakOHr19{F_vibNsDndeINw$E{obzewEz*J+t2**ZFF?yZ-_X5Y%D!(&5VY
+y>*PXcfm~IV#$e5n01efHrZZ;Zog)HPDztiu2NcEGlF92Y=zSw+R2YZ^Qf!-|2<R1q&xB}BelVOaq1;
+^pc<0)S<mD1|y(bQ#HTd)y(VIS~VtE?RXd_CHMrB!fB5~%5kA6-R5_gWvSX!*{((@BMz)13-pF+RgVa
+gBD5fu1k`FyFF_TRZ{s%;TkBqHC{)O%1%Ky%75l4Ah6?7J8e8HnNvEZY&>=8%+hS=Ut7HcVdeKJPeVz
+p+AYgu4GU-O*Pj?)aE7#^XSc^;!Kl3#-K&;peBVGl5NzhUvicwC^>$mY&H)GthfyJNAn#H8IA>j9i}I
+ky(oD~$~N5g4BacyERUvsM<-aBg~D8;ZMR6sd#XajARQW%AQ_b@)31f8;B~hbzXZOoz|1$-A)`mRJwq
+0^BI*S+*P$YRr=nF4ltc^)EM`|oD1C(VkbP7bLG?yzFvFnU6cwzb={uAK7o=3dIVtXS(^+07Vyd}Rna
+XzR#V?_bYQ|G-1dc#EEGm5ys^rXku<$5{z*00K)E@RZo&@pcHP&74_Diap$eY$&rH5;}QhV1tQJ|*}b
+HWCvS|yi-or*joGb*@sHeKB-SZ$sycv#<uD+fn}5t5OQEk|mkH*Z49Y0^z2xLr#<I`BCDPDl4z?0Y1n
+4eeid%a_6n68F_DZDvrF*H$yL)(c5mO!g8Vqeg1&@B|&*YeAAzh^az#nAvMc<IBA_RqjhJ{~_}6TBfe
+AG>2L&A*P$XPQ43WSvH}Xl~JFniq<WQ`cc^WrRvNFv4M=Lz&37DEzbP8^JGZ-w*6I@Aa&*j5lPsiYG!
+#fk>?uXmcB5J%9@^%z-j?-U0{*N(Z$o3>~C?W6pONcDr+ySS(WZqo4k%|rswWagW}9QI+F<Y5K^Jt`^
+kDSk8_egSb*xn?(vm^6OyodLzV@#=djsb-k#*`P!(Xk1z+&Q-TmlZ8E2>3JaZPqAErk!60V?f?r!-qH
+VD;I#22B;6QFwV=sEwa7xg8|rd;QV2HUZJ?oPlKuzN~f`n4P_jT-*9QLa)38dhW~``upn5>NO&f`fEn
+l_JGERT~nzjdC*(fvQJgXK_QzP$+X416Y@%f9U95n`tY~>nX0nrX}4u2N&86Mc|>UO%)GnwB25zU+*l
+_MXqtzyZw?XX;D=#84Jr-<Fu2FZbMU++AbMhLZPBc4&%Pui0X;cjQPpGb#$-QV4VbLSKp_<gW|9mV!8
+i8rSs!(z3!{?#B1Fj$DIDs%UXB6_$BP~sf3#KY>Y@H4FUlCFh(iu#p-ubCoGgCBU^Ewk;zE+Q5{`;_q
+<mu{Y3!ZmG`TiMbT8)(^FY}Y8sL%%Y(cp(ce_y>is*D%Axa)-SNCvBqPnOQ8LogFh(l=#OWj+P0wn(L
+PZltgUetU6v;U?=jy2!-D^>MXpRz-Q;s{q)9Z4nRrgd+rKxy!MFk^7T$O4hS}6dk-RIe`=VZq%EP$q~
+>xrM#LP-bU*(va>4kHN`vpm&KBlti1d5M1q{@~2>UeRO~$kM6;mcX^?4d|kpo@M_~!HbO|T^QaFXXE5
+14dM*Q?|wjE!kXIPayZ_RWx?gf=mzDH>!usJxitc^qHo2P)TZnwl+Vegx4Yh$*Y(lsm+wD##N|hy{pI
+=Hk01Wb%K-lL{QB*`^PY&eyx8D+_2$F#yXXBo-mvh^^M~iJJ(vFbAKrcc_WkoyRmcvn*3ylGWE}_`im
+FS`UMSiZjv#<22wb(mi-=+<wTM~N1Fw6K6sd3o0iHnMvIRH|1RH?`3-aZiH7E#3f{b5~atL}OATfe9K
+A1&OZzF?vHb|%kQCBedfj&FP&;^80m@LNW4>ATpARFxJ4Md}X3^Ak;*j5<#roWDOHdv>F)M&u52aZnw
+_+ZEbF-NdcH4q*M?1Dg`u{%FlM1l-qK#7M(B2({nZUTb4<Cz8_S}1oScN=6=;+#e5y*R|8s-y_+iaV_
+t9m=}Z^5$J{CyB?N|78y%7AHOkobK>^dRV`}V+ddqRTYg(sBCH22!BV0@El&#5zqF+V~4fDxHg+l;Eh
+{$b~SJs2C~$K&a5>zO#xO2kU$g`)e6pG8XfU$a|6UW6synd-FaqC*{IT(F6!W{8tG3v!=HF-oDflUk~
+SUjY#UOc<WR7#K}M!COQLG;;kc%VLoHxhbGU7ZBmo;%I{NUan(QOO?B~$f>F?=5pMF{g?MS<Ya6t-`Q
+v!%BqIz6E$BR^}v;vErO)?$vY=aN&?$0*Kj?oVfIe@}xLyS!8@HXfnWV?hI3h5%wFka=5$Q|))pvegq
+lh%Y2^Aj;rkiqXw04|LTJqX$0@!<%n8uUcqNeBC%qCM_<gCLN8L-G>74}!&^;mt&I3X`;fTL}_tK^Cm
+lBa`+sOgCAo;kMj+ULc+gi1UshC7@UX9x$4Us68cuC?oKsJMw^*CvHzZ8(eT8rr6^)M;zA?&qi~1L-%
+7UH~0WkP-?Bq@X=MefNbLmSxV24IpT9h#GPB~1g*TpV%Ll9fG!Ew7W}~=EbgADfo~Rga2*|1Cm$EhUc
+#6-BFzPs`L^z04e@L=Cjuk01ePs#BjB_)I>QHV{)<)u1vtdcZ0OQ_hA?+h0U7_U7m+K`Z#1h!U62l=E
+hj+_9Tx7}h3x3*TRwb{ZoS-fSoz)F!MI81rr%Y*!}E4qHPDsRBE8Vb(zN3ELIpgmhz)QscF|4*boVdL
+4H@)a6ob0pD<>P;7=&#nOCvX_GM8si2<?Lpp5`2j025d5dU0;B1@(mZr&npTFSfu8>C8iQSIj_z0_`w
+?frH;5sXDrs=1CUqkR$Zl?gvf}!f$N&#ewG(coU)d#yWQsBV+)&9s^c4G_Q)Iv7Le0?0F99=!$1)R$%
+oS7B)0s&BsmubfL!hOj~Xs%fC!RINY$|kixCq_6+fCSBp1o0!DDUrAdC%Q*a(0>_e1{!=Y0!Rjy*dbp
+LVu0{r$o<a9?o+ws|37g&NKog6#^#{I5b57}^Ah@(1!>NfdmuEn>`w08FcA{7;19Kg_;1eT~_jC>;k>
+q|>#1LQb@f=;m56?7S-3^_q;R#|s@@k^Qy#QSPU9EPDuI`1*sP=C=hYL2RH)9SLc694toG+_1b!-+fM
+**1M&!ti5CYX6ph(&x)n)jzG=Z+GPwq!7doo*miGp>G}WY*+W<=@n?^0wHCn2Q&RgZYa81VSGdd<oNq
+RtQ&+&fwvspp!Hsd5YKk9l0mo`XyZGve<%ZYS1-YEc+`ed4b^=Z48!#WJY5jGML&Ih<^|%}K&}y8f14
+RX2;9ip1-qUC?LnaDiEfgB?uK~m>9=l?$Iy|Fi{9>j&@Yi8o_<H8napTI1J^mafbABda&O2&iFUJvwG
+q}VAG6=}B2qyC-4HN_`V?kw2L+9dtbsJ8tD7}3(N4XrFvH3*7e10dzR3~Av-QkM8Z$&KP0;!BMIfdGF
+}NCXT<_?LLu|hK2@pi2-5z(nh-Yh&a#!?>7DY%FeKD9>l^@8kJ3LZb=U)5^5T`w@yS6;I<M>gCXFDnT
+-V{m!Q^moEh#M?cL(vi4&g1Ab2~Ftl)-_;_wT7jb*rt`A>Sa^U5(O9<w>0TlVzI*6K*wXBxM0JZwRs0
+dmCDOGz6DU;BoX4-U>8kv1z7rcEOID6`T-jrHp%<&AOS+~AQ%tA#?a9;c!4JG+3)_{eo1%Eo&#lU!n8
+^xQ-mjMPOhlEd$IW#3HP%juZfd9!vRR4cn8CXXQK;Um3np*F%?RRfP9ZGJREWFzcjs8<Y%-5!vSRw{T
+#kO5YL9Yte0mmbfK^x5;`u}+}TO71v_8@_OrXu9DXQRqeI>R0y;REEKF5VFMbKCz(6_?NXG)=F(6`sN
+H@@A1~U6#*HetM4q@EsG%`&AZyV%^dmi#>AfD~+N}<O)HnIDD9i?MUSu=)1_K_`aJ+<rl8F0Ct?B{lM
+GiW95dhtufDM<r4<tODP(e{xl@8t4>$h@Hk3nZFdUyvktkaz7Qx5Hg7<A~sS1sR9p@VFQnv`pcSSk*)
+jF@YQ>Ncsi2x`0>?Na1j=sj3+EYlV@~ICh6uWbO7cl6sNDLFu;0LeyawU}MOpclT9woitn>R|(SRF?`
+@7PUaO^dmif{;9CrD?Bik;Apcv~UZ(KyEh@Nct`{DHGSt|Z{=|gb>kx0p+U>=`@z4}DRrY@T2gvkch_
+(34if9p&V>q`5s698t^RXZ9aYH=Y(GHR}-oNstM~;W1<-eHGu6c*b!i$Al4?Ma~7Pq34hc9JD$EjDp#
+P$+wK@|p^T(Fm`GRrFDPdoj4zU5FU0gyfjV@U?$p`7E8^t>2r_k*4aYsv5p2TOD8?+IR(PDfA&&!D2v
+U0v+qQUk(j-N+jDeh%B=rBc)j-ME~`RK)%@W6+_mlGaB55)HZu$<-aTSfHHhuJcMw))4{mDxh62yEaG
++qut>LFs|;}NdL4Dy+pW?j+=;I`q~aMh;!!O{eXD3b@v)+v^6;dD(5Ub{-u&a?}AiZJBJYB?}SKZ@Xi
+T?u&?*#yrFm&Bi{)4(f;Da;cdB-4HpV7spF;uo3q<VG*tg&@dRD&Igl}k$6c?7vteW0M9hAg%8rLX6%
+tBb+;!iz+@~blj`6=Cn;X<a6?>_TJBJ+b4wt9qG*o<j0u@jTc^ObPfo`gi#EY;q7<SgHe(vw<G!#fJN
+AS!G#Is$OOHBbZYBPu<h1idz9%q%@TSc4^Fz*3t9Z<0yPpy-*4=B*~_^DnFjScN2_tbWT*3nHhip{#U
+;mS_4r*x3UlEcG10_<Ua3G%2Mf0B2&&<{Kx{*LFDO}mk(2`8=NCx&hqt6x{s+m)Gm7-IFuR*4UG*3IF
+ev7vLo>=kU%ZyqWlf9s(`aEncDk**4lQDC!Bgef)*E!%)14*xI|&+@B4tI%)`ngS($PYRVUoQQOgEH}
+*T^z}fbrOr7B55HweiM!tIm$ZIvEZF=314}1+S}&=sAgm2Vbi*Ix6%r|TM8Odbi@kaL9p2$WlGL9H7$
+?G@N}dV}>jA)VTvU%a;R`&k5X8D)kXLAI;y!$TcxWtX^dLJMWWgdFx3Do>lvof9gmUe}(9y}M0d*};?
+oXr7=Dgvdu_64@S!q*6mlbhCAKb^aS;W{{T}Z!>$&;7E0=9b3W5lyfs#rEI^pB<}@H2p7cuz=}=O=z)
+y-ZwOoj4cQn&a;vo(=QL2mY>aLK24zLMJ42U}-`|<`6Hqe6TN}s0aGG8ph<~?gzxPP#Pie;>Hor9+NV
+cb1@Vm4hW}Oy3xm@70yri7Do)`q~_sEc)7|`Kz9U0MjQ@fieLYvUYH94tANdDdI69SaME1--Q>a@&L(
+kQuCnWeCc`TWBKz)EeGaD#bFm73!qu7}nFuWih$D5nNXTNJ;`GBw+VFA}>ZMBDD|4Z#&`=2p)V47lkx
+86b!f3@YnwH=;=O=W_NrDW;vj&2YSRpOK>e@Z}+AxA(pnC{5E@z|;8IBNZZ#So*OQM^dM+DEyRd#zFw
+==!{7o-%k_w<ebVjkn22Ht7te45%fa=`EF9=)C8Zm)jH^7;I!Mg?!y{>4ec8wwnuZdj`mgVzwO_`&kr
+Fiqi_dncL3IW%_qK>psQ(l-1k!y|nrHww@5Ex&W1n5L<lbQ&gj?s1#0Z#XV+$VS#0eoueV!eX&HspLo
+<92@}LOE?ieql6<k9m_K>5YGlPwQ`6~FkOS`+6j3(+Uo9U<!6F1t&QhFVfO@cuor+W07~THcX+wVuGc
+Q$S=@qpdYUQhGkEs-X}*odl+n~C<%zJP8PnN_`z4;dTxI*!)IpWqqtp3m@PqDTo|<DOqQ|LAoxupT>h
+T4@^90jarTZlhyj<le@SH;*R=(mmM5^ftk033YnGEg_ZW->r;ns5#(z3MV-1^`F@oY?0(cupWCczIIV
+mfUL6m@|bqd#;p06Pi4;MkN-Q~Sej&u90z!Iw-o6NVpCCGNyS=b-y1?gU-yMwF@R0ANX)$2pvH+0*>w
+j<XQY2HL?Ohun}7wuhp-;ja)568@=LgOLu@dwuGp5g^cg%{Q=iALQ_!yj+EP?M5EF_)~AAh=G;ak!k*
+m27<Gx7vO29%n*3mu92EY1kcM=cD<MlScgtecQsFXr)E0c%;!n@uG^A>a72wTs<DS44k(1(?f)X4?Tk
+^tJ$2}$KHWyZ%!6@i1YiOC6H{>$vmCl7+;Sr$uUbJ`zW>x-@;LZMy`PdnD=p-`u*59X91<6}>Y)Y%c!
+W-{4(#;S1|DaV7cW=AkD?Bo2<&7(APPU@`2=A4G~CeHa6Y3-;s6jA-v8co&C6AG|6*uBUauk_Ys1k!F
+@1qH;c_+38h%SR+TDaPQ;y6M*qz56A0wXa`VVYqZH6b)+3u{I4x{zbK<o~Q=tfQ>P$f1q83eek+P}B|
+i+Hw%UF$pZa2;T^o5j-xvY_E%cNo3UcV+4X!C`0yQ40rRl3wq)5b<nG%?lO_iB4yN1$!1_i8$cAG*-;
+F<$)3D3?2Mw|4#nZx!moAFL5Wan^D8p6Em$4iBurjh~2}T_=fGiN&p>qAeV{dU9mfE16t*-cl#yX_dz
+UA4j7IgdhX#s5&sXr<*vhu=x(zyxWfP?5H5CS{{51Y;i0iCN`aUolvAXy4Pf1J=g0EEOm5?rCXER>kd
+8&^DqZ8Grs$<}m>e%x+4HR#h0t5JylS)?qs4=Fe@{*$ygwc94Y>_8X!xQ-klFh=l#G|FJXOy*)0bx-R
+`_y*<!zdBC;G-5S92IK-x^C?bqrWn<FJ$*-255h+3p)#VNtu54O7+?K`c8+aWd9JStwG~F58McEjY0x
+IQ^?@=+@0gA)f7?3P*mtOUxo|isf-64!Z`l`^@wbwoCn*s9l0bbLAb*<~&}mLcIdOw@T6kCeyN8m={Z
+AU@kl*JVlpdj~Q}rR#g@y#P9eG@oc~VHaBw07A^aYB?xjOmUasI*r;4C33YEnSKqP8ExnC4>-g27coy
+?QCWDI$pp~mlb-BUN*uUaeP#{c52N^@B1ZDZGCm!PED!aYu4`?7}mXv$fkdtK&Qo=!YEJ*u?IZ{%UO|
+Q@lqnS83;noko!^>6h`zVh055wHxQevIYs1hd$?muAb0}e3Y?_xP__%ucukSl#2yUxp1c6(XGr6o9_J
+&B6*VVtwaD2Y6iptB>kNy5K0#FMb&6p<uyXs~v9Z}JKcjfD~EzM%u=E%D9M4WAAVac>LfF<Xi7Hk+im
+b{$PmmmtEf_xt5Cyj*3EoB7-GG7%!x<rr}X6VZDt^Pz7Qj-Yf8ou(yLv9LQi@elJ+?ta*QNqX?0>0B2
+@_{qe_OFS_4rSLCr!$kC;^7AJ*tbD6;l=R5)qj<T>whsWH+dY*VS9-8guVg{<GtixOGM(lt=9~Yr7(I
+86ms+P@^d&54ot^vnFa|tqE<Yvga{H)6D{EfTgPl!1d0t9^3Wp_#^PVZ&PrdLZNMX8!pjkq)7QOlghl
+lsFHjms_V&iT63BZ!~WGrPsXWaF|mw0Fv<XBA)f5rtran2CVOhn~(r%D=V@1I7~(}aD)oc2B65YIMMJ
+CHHy;b;%tKoQp&6n(r#w{-fl)7_nC=v3Y*29Ywn!?C}p-!>jZ(x8sPm=1~$ic6U%RSxQq8wppsML*a|
+zo5z?TF$s3o=wj5&4+Mec;fsEo=Hf;*y^D?&Vi18OM8PVgZ2jX2KSG<-tCv<L%<3@!Im`Dbu(<B{Yy#
+PW`EgBnn4wa!-WA&=4~Gk&nD-2Bqup^H?86>C0j+)U6lBE`EW*wvq|OWCsgkF`1bzfcSk&nUTxRQgk+
+3baf_zY6}!riaV`-pqC=bSkm2Eo=c~<Gf|!wpyuWpWD8Zh3cN=Taz(tD|En2i_(V|6TwzW%Kon*O5mW
+paZlMp&VW}=So^~5cqcoykFItSRGl*Q1=q%)J0vo({0PCKh#3*k>V2jZGz&PiX%$rpLQbP1u=w(CC2q
+`0vM<Hp%s$@y^U$z*BllYW+5z!WnY-#LA0jv0?z2Z9!A`#TIVTL^pgZ30GlhA~VSS3lzP?yCn}?uc1C
+QphZFLq?-hap*YpayVP{urrojZ8PRLk(DMog(Pd8Cik#U329wn43cdk9Y?yeswI0V`aFk~ATZkX%AkW
+9HYy}4@>cBv9X+Q+_fPJHx999Fwzv6(e^SBWH4xoAB{5Z!sZR2yNzx_BAM|P=d&Cx5xmD@|*^*AnF$B
+dx?eNt|OSb=OsOPwejA3s<(IUWEXD#zvyk2H6(vto-wfQIa!bp^}$pDSWZm(DgLGOcuspZ}M0(p7Ut~
+)vGU%%kFgx#V!1TT*xf=BqV>vbEIW4in)Ywxlj@Pr*pCJc$)EUM$NHY8MA_X{V7%l^3Q-F``uhfH&c#
+RGG2<;+XCGC$lc2Cy`s?yuE~7NILfQzDxIJ#itz3+kowbi<g!?h~Sun;WS-41hJA6JGe1XBLVc-5$;m
+AkOl2$A!odcD>{~@RU-Cdbjfg0JIacF^l77cl$d#blG$KPR_}*UT*u_&Ev>vVP1;4K^uK8_B0dh{ORi
+F(V5n}Te-AFSS9F%tmn{8)Bzi3z{Jc4BD5sDPl9?>v6L)aDxWd8uyKUTN%&h4QTBC8c$?F|=w6e^wtF
+4}9&QrCCN5voU1Kq7WJ~+9w&L)x$YFH}C-cNIzV7E9H+8STuPT2eT4@BS={$Cz_$&!Q($iegIwX#CCn
+Rkij|1JG?&s_$&r3f2sS~8u@MSd2qh)(NpMxpyVGG2pi3^qZFNxQJmWB)L+7#^G^^6<3*Tiw*_(FbfV
+;BXN8?1j~W)lON7{*Q*+)-k5q!+0PVGbsfsK)KDqkB!^w|sD8H5~8ZKy`~#<=Id`ak!^(=8vubE$RM*
+r@8Gfy4SwW@4Z+hd{FpP3(<VMmb0rNZ7)A&`Z>#VTTzf_z2^nG*H+KG08Yi0%WGi8K)x^fW8rH+tP`J
+S6`8}@C{euws5&oCBPsdR6j~9EceE$nr4W@TTXU$5MhoolHZ;o<;yv!1srE29#KXRI<|O`h|6)=RCM1
+dLBoUnCZ~IWGih1#{_37hkw(zUr1p1J#!T3nI)}sp|5BJoO!n7&|g|6yCr{<~BLahC?cZja)XGo2pS+
+t_MDfJvqLB4E{TcsVCStK?I)DuXyu;qA9Rc1;kfU^Stw|mjQ!^vFj>4RT%ul?X>YQyo|1<dsK-jH@+#
+ZZzr5`Kw-JW-H$+^X&}a}E){@qq5NFF}unf^=>lLQ>X>g%U44tC%iU>9_PglNm`+hY86hd384Ll7HIm
+1t!SP6Zv^Iw|3E6O_vKyL!YFwb2yYlah@p76P<asGrs(J4!;X^ujq&pb$OyLPlV;&^JUO?q!M!!Ma}$
+$(**0-ka?sBVA6-#JOS$HUP<{pWpg$(zoQU9L}H%J2l1{%ZNBCZS|*zi!vdkzOtj>Qmb^bs{+Ptgt`|
+e2k{t3T5s{}mEMRc5+2<CA7FYb0ZYw09f#0dbkA*~6-iIlZTiNvz`T63yb5=WRnI;m|ZK|<F6_uVp<v
+tXMqHNyP+Hj7bjItfxh{VaRmti-1ZnUege4Wp%ji<rxG_~BUbnUFxM7eICVr_zB&7al_bgwCUh_Om&y
+*x*D0Vm@TeH-F{mU{X=4M7o$VITIPh6DBWIESE+VtA?#yWMa~T#9ltHPDI(c2d){Y(B;W-FBkePSx7J
+`DwVm-u@1{*JdJG4h&sT`i23fT89nmnAz@rWPLnT-F>-UoyUZl1W?E!vYk!LknOu4(sxx@2YsLZz<&`
+WpNh!+65=)8{b>VnaBRLWW^|Cxz5_UN>ZOmI19IfpS|rdNA}*W#fbl^zoT{Xk*2dZElu#AB{N1rT>Rz
+#bsbm@oFVUwa3e;>`7IpkO9o`V<XgIAgA<C3gnhYoSz0RY1?a+FuWYE3#Qafs?mk)_0_P`aDV2G(YY(
+^rf%OjtIJR+WN>l{u2ulv-HSEce&GLnhrt-{MC3b#bcmEBbenjt;*mn?w7XG2Xif9XpQ?n4&gRjC)Mf
+`!#h;^+a>6BL&%Vx`ePBN3gX^cv~TMyyypbYB0Alvmz)9OzIE1IK-a2f;QcNsNT_OKF*N5=r{FozECD
+SB`E={EVfT#iiG~AMi`;c2@qRqmt*#Q%ChPr)%;ryggrF1u2G%F%e5NPpm#CyxhDIy4OVEkczzxY|tb
+mB7~-^(~UtjjNNYyPY}(e-=gk0J<VJ_JT08zw(IU^007lQyARa-7w~7{^PQ=_DazC_E=jlh-26onImx
+p3o2P|?1HU|Tv)^nv7c6mi1hIr&OX!&>Vx-GGQOwTpK{{P^c%$^tSZKghFpgldE!k1l<7G)qq{n!r5@
+CFbeiTBq(kLkXi9a5vUigx3HqYHE_#pi}q{Xe=Z?ThvN5iv_^jLkD8x9L9$d2cg_u(H9MY~=<VUl*0<
+jjGpr(}zmjoq$d`YGdfa|iTwc7bwU?#OZ*cmHm`B;j9E7S(h5!dQaE7A~}&?w0bzMB}tKeU+n<BR|#k
+cbu!|5T8`{T5SxEyUrw~S$G3ba#hj$Fvm0;Ex)B+d1aqLA52g9%wt%R-CplxjG#T4PB4U4@Mob=oE)u
+h-%a<VLHa4l9PWHNmBUjUyAC7qRHqheS|yR6?>c13@;%3>>AFX(sREbZha>84gAbFDEJj1@=iOfT62u
+T)uh)El;SpvlyNCS_kGwNlvN&kr^cf|gMVY1pZ!mrMsPxd-B~}3^G<J7Cfja;YT-0fIW9z#D)3h&X1P
+g>QFrIrF>fZ4iA@}ZImVdSF>--d60c)d-gfvCs<+N~k3=psd9*JSF=TZ!N+1EKlm|%J1f%VY93PaOnH
+r2qB??}TVqQP$FLw6tEvrf}h7(c@!74mrwK_ODzb|<Wt&I>CS3QjNzsYF18%TJP^Aq@tqOo+#3>pNqy
+N4e{TFTu4>eEA&byX1n6t3Ig@3C_CpZtxtJ8fu4JP2yaqUi7GUz3?SNCv@3m+(BE5-*w(nCO6K+C1IR
+;miULMFNhyC%>3X&<*kb+-g+7jwGMLnjuDHI-pe>PJSYj0nI^?fW1SzG=xJ@f>(OGzPNI9wp(ly~Vck
+sEO;)gE1xr^j)|^4|DLoXO%xtnY<gjlr+t}E=4=W)k+4ZufGrf~pnM|-`_6&1ZOoSA$z;2MrN)_@*hf
+-+?X~sNbe*AR;ie0ZS{g4>Nr9Ej913`@1P~j%_PLKF6?sZ<CMk^0^SiJA~hVC^<S>_PL#-dnVazQy9#
+<UBl$jbK(hpDu2e&=wLRKs1n5ADWp-}UZh4H9m7f&p)%d2>*vzvr28`ON02yoV}Dwdd}o9CpyzH;&`I
+@!(v<R0ID+cAkjM8$zpo8tn|(Xc-UZg;Aru<dnn^_zL1W?|#^R$uJ{_usqR}4{~!2J~I)P53+NqrhJ`
+$`2_+P7CCx}-Nsy!FOGdc_nL^q69IT408bU)O!+}_uiK5GmJs`Y^xYm-;>l8O2_;A;ZP&-qk>Uhyzt1
+X}@&=f`s>atJp*0&M4Kr*iQ$5a~mhgr=CaP{X0@RZi!42E(#XmEng3f!Gb42W&NW2qOccSG^#oKODkY
+qx<-H7osoXp;)-SxtkKpH3fSsKrquE9e{-{Pvbhd?gTVW$%8VUO%NJCuk4o?+}4?d}J3ukuldtlM;;^
+y<wd_zX;IWz_nPZc@OmXPm=FcsM9{PiPxwpCul`ZZFCzoon4^lxrC$eq#<)KLc^oI+zGP5(WXyNuo6A
+zErT@>pZ&GB)K&-{P_2&|6n2-Ob=gYJiQ^7g52a#Fn?BpW#u%V#WRi$gyn|Afh<ldu^k?A=nG`A8NA<
+z&)9rheOHm~<<=Sw&*8GGaQNfISs4DOW-bWUwL#o<PyS))9*@%xn3wd1Hr2LqVZI;nwj>%3$gSJPHt+
+S#{M)3C=HSsI>4h8OIO%AHGs<bAwNbc_8^~yN&scaIeJZAx!*=+oQ^J)TGTh7ZwA^YPR5?L+6Td92N-
+E^7mYg)Vp2z;Gd!@@V9O8*hY!;`zKAJl@H3;V?Hi8lk8U72=cB1~y`=zcJ!R?=EQk@9P3;R$^o8j3#t
+O&D(C_E8`50mcUELXp?7>s5u$4;Vq9prBl)p(*BPqgA}jN<~qNH3jt6@@>+Si0MQ4suftY-=}fnzua*
+{Sq<{2;YY1OiDSz9VF`V?s*XpTf_>QW+|8Vr{!p5gtI!<vF`Swd#xt=O$d3=0Z>3)*tN-sgN6w=q&o>
+uG01?8hdUr1enAP)n&ocq_DhBdFU0AIE<I7BCu;P5%skT-U3#KN&(!1%|7A-wO;mvK%X-%fUxE-lJ<(
+{LlG;Cu<Kr>TX-Pa9Q!T^>iI0~o8i~$)JhTRfj{>#3UhE&r^F(K!>ESOP?89_LXP$6yDf5<Eu~U)XGA
+tOAaLoJNUi^~ugkT377e@Ck?Hj{H3{v{w_%Fy{6NN>(ztNX-$P=Ke%Ufs8DC~N1W>62MJM3i85q=EPH
+64mJ5ojcejB(@$c#-ZbBafblFf#R8?zk{Lzoh!H#r-{Zz^IUo&J=NC1al$Xo-#7ae|c+W2#);Yyrhrr
++3igSipGQ$QLiV!I9G5J+}y!pp9C~bufb_8bK<%C(sX(9Ej;+PlR@7H0C0!112Fv+)azo5&QEBdFKcn
+r3X3N(bEeZo+}!p7-D~Rg>K*!48>U~^{Ybl(-DYv3HsneVrUdaLUO-MEPE%PMbDsSo+~lc;@5$0C5a2
+a~7oJp0j45kO#AK%C)L{u{jc<l~@EM!<t0m4nM)#Vir4ylaqJvI!(21%!Q8lNU8na#IMTlsVP$-E$lB
+Ftff07j`=Xpsr|KIh3hw1Hz8&I1jqR&K^nW!-nHD)5jOvILn>@krzCJM$x!I-EObDn+U{D0TWWI|4u$
+o>*(U!vtp#CwTkFHz|wD!oLJm*y*_?x5Y0KtGL3?|tL^f7i?Ujs7c9eI=r<L|K)Js!%E=`lLj3lxSp9
+c??pRRMmnQB~f$atvBNTyIw#q3naOq&(9i?NGcK`MIxF=)DfvVLM4PUEL04s+CX{1y!~o)uSq^XrvnQ
+RP12M9O7lq)>ZYHi(Iv=b77Q(<8g-L25_g`L|39VFl}TK)wooexS&#<Rrc8-PQpaECT7Ocv&Qu}mcCt
+q2t<&ZIyI!zj&QIIrK_q6vG#vN8C=O7_SfwH!vP~(YX@bePWu5#*qI*qKKIU7<sH7PcGbH9k$b6&;3o
+{fZAmIKJYCq-j;q4R3KGz)=qI>1SCzJ#*@bL+up7P*u-YJm{bDfgb&?hM!4HKR6&vMP{_IJ>|rVK02U
+qU%1j7aK`K^&%}pEMl|UBbqt#2O@8N}@@M$#bpS-$D1vg-<96U?6W&nGoDeDqw(?N$D0T6*&|Fij?|*
+@JJbhb@JU4-7A+Dlc0VQwok(ANoYKYg(rdQB;K4vnUnZ&67fwUxk-#RiOeSP*nFIIexiHj!Y7n0lq{6
+2l&qAjl&qAjl&svVQle6#Qle6#QR46q6WuEpKA~izWTRxGWTRxGWTRxKWanO;5}gvA5}gvA5{Flw=w7
++-2_*+52PFq32PFq32PFq32lpD37?cP~1SNtJhbNfmUb*lIC4-Vd$)IFVGAWsqOiCvAN=hUpk`hUYq(
+t81HW{}`$w|pc$w|pc$w|pc$w|pc$;rJIB^D(XB^D(XCDuJ|i*Z|&T$Egt+*p}t<0jg;i8gMch?}T;C
+c2f0V><CWCkdWOCS#HmnC0wenCM=)@ChZs3hV4-jhtxMCK|Sha&00}n+VP(Dzb@+Y@#BYsK_P)vRTeK
+PZQlM7e1jRSV0{&(S%JjVG}vnL=HBQgH7aM6FJyK1~!p_O=Msb8Q8qfDVykCx$p@k!3rX<iT-P%|C;E
+(CPJ@?25BNcn&?m_X}(FuW5VoC*w%TUlQYr1a@n7N6*#a7r7`hUCW5Sq3~M5}nn<oDYO9IRY9h3nXsa
+gLs)=xF-iO&wbgx|agpyzdrPM?uHPJ#%v``Z*)I{$zkvdIOP7{^WMBp?LI86jj^FB9eqI>1SCzJ#$NS
+G!PripNAB3GIyl_pB1iBf4IPMYYFCc31F5NV=8n)T?xC%RWId_qaEg4AfDGMc0gCSszAOlTq#n#hDED
+wv4`WupF=WaTHx+gT6qInljx;S)-N71)7EbTx^jCQ6=(l4qjinJ9TCN}h>yXCmF1NOvY`ommgBG10wp
+*`I(F6d4mS#zb5((LYQi4HL1#L}M_~6HJonlT7v`3p`2oPI9^P*4a#SuUz<ql3<0L%_J)`NxDo@D3g@
+PB+oHPSWNN}lYGF0?w`;n6Iy&<%FnHL`z22wUEjWb`LECOKVNU&{@cr^{})h80|XQR000O8^OY7y8-?
+V^OA`P9luG~rF8}}laA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4XfV{UYKE^v9(Ty
+1mPIFkOZUxAx^ID3!r5`4Lxm(5l=j-7bcv0b)PbE#CyMN5#yjYMimDjwgjKY+l4Zjf{~+1ahSiDFp-@
+!I|L(~TzKXV0epiI>N(PtV2K>Cy4|^|5$)c5(Dq@kHHcvuEOFrNl*3-j`Le7Dx3$iLw=M>;Em+>q39u
+Y<5*SFS@!B^L44JPRw<kgBhBhR%KV}mao)jwXV0S5%p4Z6MgFX=c;Sx#a6Ly%erdy*JfKc{TJP_xU3s
+nK5tafsfD<E?0Xy!RWzor?V}!Q)rq~MeIbAN;d|Z1Wc34axL%7ZQ@0gYs#VQrwXiEWs;jOk?{<2bd+q
+*@x<rV#Me|8qmh1bb+=%Bw&#)6+EiNCsRb9FDYf-JsyQXL!jntNnQlhPw-GiRkPvWuOiFr|pMlH(LcC
+Q7|iK1Hkv2JwxO}!|WkEUo>E%b8CtUJ|gTFCJA`8#o@T0K+oT2-pitGV3Wt;@M?d{)j?)hbc6Vr$CU6
+-nyEOyt^4M4Z&R`(C;~303OrX6(<ZX?2N64kSi%Jw`(7davlr{Ixc>rs2Mx^rKj7Z8T_kFew6B=|YrM
+-|b4vUVqRX>V-aNScy9&cCA|O){*W(*A>5<-n_YZcOwqZ--}-kudWWyZ{Gi;>uO2st6G1yr*gB^5L^h
+os-~#AM?G=f!Q11jqc^(6;mgyr)0_8Z_9v$|=f~IA;^g8=9E!`stDDoKcV~xJ;_}_q<;C^!fe_b9LBv
+LwQ$qG4Ug|D4wU*>Ubw#;u?b6?C^|pF~>xEbqpOsegT$LJ8qR=L?ef%aJuIuW)U$1UL0{uypOHtKbBp
+#YlV@IQJq+Q($={M4#pC3dbm%36>eOhZ3T<hj1<x+QjvaahU5-;nvGgaRn>b7wrW&B*Gu@vInwJv$;p
+Ey2$^_8UZY&Kgq^+p`b>&@1vebd{{LsM*T>t->Voxgi~dvS7mba-?8`r_)ncp?6K_UhvN=Jv1W7r*Fl
+&*kj+{Nml~H@8>EXSX+Ru8yzYT%5hqS7aQ|-X8wXRJp!zdVcy=cm3+{<}kRL&45(>e^oZceNh!6c|Kn
+ijcC>1cdD9e=@xfu4SplxYPY%5`qI)<#e5~^N`tmh+eULnrMaW*dRy<?J!%v0)P3DN&i=Mdj&zTPOVq
+EO9(QlH7f0rmMDf4mC4XbP>GpCzn(ZY~razjW)c?-<m${Pm-%|bAezL#Q{+e{xWt8`y@RRpFrG07Af0
+F%s>fdEwlChG^3CpyyFFPySGBXqXEpqs+a{Ss?Vkc_mp1U8rj=Yzsl}+AD4Dy21AUo8{y)5u8bH8OTf
+2*h1$=JNxUX#8)N!0e3_dmAOynBiMzkk_@5;pNUcTyWmj%1p-SI#7zJ6YsCeshLG>9LsklH84uN8Cn8
+XBMN9WAwMa{@oaj38N&<*h?tsk(Bgnl3v1gz0%17D>v`Z1IgVYP4?)s?}_^y_x0@v7+-pcLKg|x;a`u
+@f0Jwpqhxu~bEtH`!KZ0N$d!y!Z424IGRLy9Yh~@->Pz}%Ko3dZN66!ZdRt-^-(ytziqwD`4HQboglk
+v;2tV<}LL_7ROWdMj51Yg<%EH4cI%Wom+4PXI66G8^v^=brTRTZKbJuexe@rm-*Wkn8&r&y?bTk5IoX
+la=dL{{srVP-a<}6Te7W}}aO4q3zkXP<qs_i9l?}?KxoN68o0Y#LTdC(+eOi4smCmn`tkOB)$yi71GY
+&3C9mwGutr5GkOGame8u5Sws8UvzWQ3BCZNKqMEB}tUjNvb8Krw&`G!xvnsboCS0f96ZRr23qx-V=Vp
+CTUllI0hptr61|nNm5RulwC`m5x~wvxuy~6E%jr^ekAUhQa7K}hIuH0qB*Hcr&<{p7^abXESn+oh_eC
+^7khXR?E^TpL{9h!X8@FpOO#|3dwv8)Ak8EqYLTvs#N!Op1#3v(pJW*2E&<%jrAiPZ59To{BMSAb1Tt
+^$_MO{j=Fmn6!xSY8@MM$*whN_aZUSU<p+r+AQ<Um&Phm9m-NsIBKo4v_jZ#)GiQt(tPehm?Svtv*s0
+xgc_<G<LB214_E=`gs$E0Kyk5B{B0D%L=VAYUEDP*Q?$<XSt6|p)YKzNCg?C^ugiJMu%KF7W$Py}gZ{
+u|nJiw;8#_<G5%$%y<CTqyJP(j$U%8L*joS&<ac3bGuA*WBvKQG^>k;t1#<4?cm|vLTO>UGkkKx(8k$
+MN&E9Q9Gb?rP56hI^gmJ?SS@aN5P{2#G@&9q1@Le-g^1aKoy0}NrbHi{mvZ1tzO`hh=!!ccj%cYdP1}
+i`_hzx5}K&=19Oi<ERaUzZ^*7QhjEJW$!&nArN_UeeJnPlC+R^WUA?|$$fGn_3d+JsL0KSUqIiorNGc
+1Y1*M0?A4e2=lQGeTk%!y^9*x;VL+d8Yk@Z3?xK6%1b3B>&!KXJEiHAdIA2JG=SHhUEqC7%S$8jiom0
+DQmtP_fffYoqgoH(45XQItr>OZHxHup@4xB8e%G>nai%DH(-Z@*Z3!c>X)D2!O40lwi&g3p|`s3+nP0
+51(wI-Eyy&!fbr3F88IDdGqCEV`aMZ=d>iEQN>(7EW44iVRjGiARTlGl(~Flau~8%EApq;=*TyFBpXQ
+6cJX;<VPF5Aw6N4KzBOCC}0c;0X?+agy)9=Axt6u<hv>g_jpQ2MsOTxoibX($Pk?!eGrdQ6h8FRi7yQ
+?M;Ii@#U5KSdJ3IXEAgV?Er;`w#G^xoi=9Ub-9fmsk`4}-vfXwM4URJ@?G2=p?euGOg%S`v2{24R@a!
+7j@^R&oA;GV34=z0w@jMnn9&Sv0OO6?3L(fF(A&F7|E2A;dU{A%vGy({@O)e*b6tOAS0l8I1gUFP~0Y
+zB(AcGlCNSAM&iF6<hPF--sBu@%y@(7VZn5D^mzgA>vn9><WFg(8Dsd7v!4kij}$(Rk(M3Ky)1P1{po
+G5J|$=44EpXc!NKzjLLqM4U6Cn8E7<PPb?uN<MYbqN_W33`R`_D&!q{5*=iVt7;^JJOFMQXY?}!oZnQ
+hIy1lgx@g2CwG}fwBL*)awnO`ujA2hb2oOO^ph|tLl)R36ZPkAfl0)3_+&an`{;{L6xzhn9Sadg`A#s
+ry+zYXnChjM9i+!G3d%x0fWjOQJdg0vBR-RB@Sct6X&hxk2|TPtq#sAfP=UhXyD%VPDcXyHwm1L~frj
+ZM>R}K)(MDK$WKcMP@QwmFa-11k4o{X75;H#>f@H}ifQ@;`qiD~<!BF+Nx4hhY4ciFO)G#d$G!fe9K2
+z#JK%of_^JFx|qdBXAFfSkSC{YB*Cq*?JgWy>nFTs)0BKac7l}f)Xv<?X$dtVq+G8K<XXBs>i$y4NX-
+y=*Ka#V5?qP-J;r~FD1j1;pab7Et6?!pZiJajTB8wP|xELkewnEZ3k0Hj{C45ct8vOJK-Fwy}{(cL@{
+J<qG~h>0kP?`nCp3B_`*#kK`zm~dkXH&??%F7Y1Td0LY8-vkPtP2pC~f9yoLkCSX<vU}J2Q@0o?G98x
+>ztb=@iKj#2FR46c5*}RRJj#=dq3SsVC~+$T8Nx7#@L_^6>p6shZ5t*xDN-Y23J9N@PM9C1j57i@$|i
+BEmyz_q;DOyxzM-#5f@ScDa=u_zAI9(FT@=2{QJL*(oc{i{7auzv^)fIf%H@;UBn%j&X2K`3OyLb4eg
+hIFNr9qdZ8+rM>#Y|=)|yFFvWixzHd2zF(eQ9V<{Q(k1&YLxsvuvVM5O4k?6Qb64NoU!UL1C$G*mrT<
+#1>Hw59&zTHp2>XAk0Gm`ACVjL8Jjq?n2niHL}-*9IgpD<ym93q(|fJ9=c$WQt@t17jkrVpWuIAS9(h
+ZIoj&NP}Y$G{v#J;pL!1uV~LEXwN3fDFTomB6n=3Lz0Hnr)cTx;eo!`)Wba!#f9NL3VtIDdz!>6n%~C
+Z4;1iSKlkZzM8+59@UfG}?R0D!C$f1&Pl(;a`<^`1pk0q-OBrsW$&pC**;)X$h6Jd@x8zIAh7%>4=WI
+xuj3h!r7D(ov3G$HaGn{D{ijZ8%EJ0PFF1)K9-(n1h7>0Y4HI1FBQ-3XqXj_s;l%JkTjpMP*@r_JMbk
+Ghf<xlB^dnO)ikk8>yJ=$TYoSx;u;~TgQ4!00KB|;mLsjUdafqnhpxhL+%crq*WHJ}tIZhV)6(hjuqB
+rZsqr097lFb+?<6R{2UOxTXV9KxJENfv8Q{6Zi*+R_0kaKOa1Bv;x*67;ZPM3gWJoQFBmrz53lJ`I`k
+DewL*uK?L$0)lRs5Jo6!luYHYVgzK^Z-z?)9VJYPaEs)-(kPr->UF}LO6ZyL(GaLk2nttA55W+^;t2W
+qFYQ8kI5U)ZGOEEMe0V0{1cXC@o0YH(<d0k2c?4e%R8v%I`=i_7qzRZ9F=p)9B8!M`<uH*;MinM%xC@
+1g<K&OWGBHHXe&?PurV~4SO1QIU+geT8@_d{r0pSyu@)RU^6xkDgS0st(h9i#vh=vd_LfZ6<Gwu_TXu
+uSXZ^sg~9V7b=I^s_?aw(I<kh;xTDIWXhdw<-Ug1h98x2F-$&Bjse>p8+F#Nj88c>V~SNkIqLmghzA$
+QxyhbD!5EUr%@RW49ds<O*OHW}I^#l!SMfGrwA%yC5Uui67ed5rtdh$s&FP1O6RxJl)U<y&})QCQh!u
+#{&a=yDXVXrE`wqJ>=Z2odOiT#U_)Z)Hxg}ZBXI}PeDR<2-@uklz@=``0@78&(NJ;iY@VM1b3vg1^ja
+2k;lNqqnYoQjXgcl>8#1rwKN!rHoA}=4CpB_qytgJtQY=D#?ccu33`rme2bBu&j;nl0W+i#X9*b*{|=
+vJ9^MNLdDJeH@o2bb;`9{WZ=z|C3&7nCP!qTiY)s4%CeVg~Gj1{RnAZZ#sD6Joo3D$u6+ai-qEfAL=i
+CmfV*S{b?T<5||1H!~+}@T|+1=jmwQ9ZGx8>%a`Fdc^&~<ydGt{;PQ&n#_H+5I6uhd$+5U~)?L}$L!i
+9MXS^v$06!+2s|@AUMVHnK<fO7rDjHMYE^KFX>;&gT#D4Q8MRzb-c0wbE1g5PuYZ5QD>uZ0vF=w17Hs
+_t^FQ&UZ~?4ou(emP>P*ur+5n`}2iVtzMerqN}1Q=6Zq=>+L~_N~wi8^!cf(AH+)kY|JTK)2=ne?Wb~
+U#;MF{Lp#WD`sccboBCQ;G$#|!cN-(gVWl%Y8*}-hF^7=N@k=wlIow_MC3=qAMtv^pUE5EUP4aYU53D
+ZN#l2aeR;8KQO|w&xIW4Q^=2&WfFu1OiIr*#=P|er7{_OFpXjxx(N>zG=3pJeIE7SCaI4RbxBHS$X(t
+6lLqIkJ8?`NuF5YpOU?Wx@SYgD&usu>&Htk&sC3;tf~Nzd78s4=JB)gtO=&y=O-`Y2j$3q}Y1@!)Qyz
+v#jEqWV7D197VHP_&BFZx&TIqI;-4U@R;+S~#+61H(?s^`S?;)=h|s9xfIzsB4d1`x&#|%vyT<==tbb
+Sxl`|qYi}lMfJzl=c~GI`$NZWv~E@Vs)6Pn%=y?}Iz1QIMUtHA-p+c7(jv^U@3u5o3cg6+s6Qxf>}6A
+YQev~yHoPc}WQ^5D=Ae6Pj)On;tJs#+J?o{1<e)28+Z<_M*Z1YTpRX=!>d#7SRkN%cEy=k$AOLC1J!M
+9|(!;9M+)#=?_wWl*)>}{1PeflsZ}+OZ?XT<^*nTK~EX99`<l}zORF}O#!|=jVU;E3%V9Z{ojJ_1l`$
+kW-8FN5TQ)$y1kEbWIXDQ-8OpE<qrhAh;+6v<?*c3e3>!r)die%Eh?q9^{IQaA+Y#_%chNPaXde^Ap)
+0bxJ9m9w4*!PF2<$wIpi|OOX>9swfgT4(Bo+hPRRn@!u)$O9_ilNK2oHik#siX9)R}Du^$;)zK3~Sga
+9h?8#&T-a*W<5hO#d^Loe-J`YYt3?<c&M9Ct)+zi?<R1In$+Lqsr@Ow_R#XJpO3W>f7};;91i0<8{^n
+@?f?8hC>d+0<YmV4el8=eQ5PCr|9pBq2#fZf2$%N1GF~zypuOsCRcs7szq@{OdUA8C$N6rcLssug(BS
+pHdad@)Q1qpf(z8M!&@4+$1m;h7AOtd_{-<-OYgOHMD_?tAHm#k$ULO8NgyqJVv7+Icz4+JfrTFs;-b
+R0mfBoTPzyG5p@vq16)b*32{Q>t7DJMgI-QzPdt-t>hdOtaQqW34bc&NOi%OBF@yDy-TrX_iF;>2tMR
+m-==4RrjkS#UXN0r&FT*VZM&{#)(ItHrdm<<JL>M3Kaa<9H)-vv}U0mBkdvd;C%5=e^dvbDT|?mA__<
+>-gQ#&zIkBUB3@#k1%%(%pFc5{^a;80Ik-#>ru}qPc=+`x4_g=$~-cyz{bc-zaE)~@+*<~%<OzVsqcT
+(Ao_bT`#(@i0|XQR000O8^OY7yNKy%m3=04N@G$@YI{*LxaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh
+<)b1!3PVRB?;bT4dSZf9b3Y-eF|X<=?{Z)9a`E^vA6T3b`wNEUwAujrc8)+AL5qi)&8Cc6)Wfef_-C?
+GYJr!8SOpo)+@lAMIu+W)@ak=*Ljx)>AK+Lys4Y`4zkJKwo<L+RD4{vYxF@Wb(`I5|EzJiR;=?@!JS{
+wW4zUwN;@)twe+d3+nEQ6dhqg%)ukK4yQ$NfP1v{rxJ9=TVvEVxGi0EyWx%JDyQJPUA9$=CAszPO_!W
+MRp^~9-ELq)nze{mYSK3v$Vk1e3|9d7Z?`jSzfQta~+j>A+8@Pi(OYyPPUd0aCKLT*9UKeoXw^%(JQ_
+q_LD?hkZd6?bfNRFdQnGmkfmiFU#}46>*CEGR)qK%<zK{koZRN|y?85tuoPt`&L7IVEM3PBs*?CRkMa
+j%?Izb+6xmJr4Zz-shioP0Q7Ur1h>O}hh$uyrE`G^!=-+3H_~wC@R_Oxa5b9Fr_l4o`!|5k+q6;7uA9
+Sj7M038nPU1O~PvW^w3oW8TENQK{vrHWkkjpg?ag@P)#qK+yW85d-Uv*w!MW`KVysa(z0=a%2m4qMJT
+$17&fPN4OvXL9jon8uTqze(JmDxLRj}I`2K)<1^#I+WyLf@<s9~K}hem}nY?d;Q)*gyR&e&4^i*gw7c
+{0_2U8h4Su+ERSKL<uegqROMRd;oA5_;`46@EbJt-yfeGUwtO@N5@yEhnJV)=<Gu5i}U@9tK)-DC;J!
+T{L{tx+2!Gm5SLmTiiylVk{gO|VDdf#lM7u&aZ=Qwe@1!>08AF*F8ZpG=DChhQ6fT%EFYef!z4>@t9Y
+Se34JHx8<A$EFTUk5Y6sQV$u6we{Q4;R`HnBf5<*e>B|!=<p?(zK!1Pg)Ww|fjXGKY(ANQdfC@F)tG7
+O{;pDwX7^dk;Wf8BDZyz*dJjmLX+TV;uu-$i*eNAVPOA*_m3lq3&gk?Dd06#6^rh6;wttkmDj%EAa1k
+m@+P)xTWma+Rmma=0gohx=>TeZ5?z^Kuo{C0dzoOkV4XRB-k!%NLcR+7kgfc}DmS3?%gVav4MND!NYe
+UNt<Bf|9;bMORHqRd*dBQ|Op)aVphVzF#G`5wN9X=_)(R(ZMj~V*tQLmLj}#b$<;9f1ixVP_aS<eT|Z
+M;dv*3oR0rBU_<;vy!Jq#zuh(J{VW8s8$7{G|3zjbnF;;pTx3R(8Rc(x4VwvTG-7wx7qbA@%$EV)cNO
+00^DZul@Ay1L08@$Y3g0J?9pSsG%!~pLr4Nw_ZclNGR7Sp(Gl)QJhII+mDa^_7Ae!+S%><q%P~8QR2|
+P_`Z3<OFh0im*!@CT~G@*hFOp9GvnVAq8c&u*Wnvn{54zWh-_&kF-1gjA25Sm?>Aulk~s|H>axKKc(f
+J6ldESTWh#Wlq>tISLh6u7}`zy)!jP!i(A15hdnaS3s7r4-~8<P_u-<P_voT{pA9BleYoih_!Qih@cA
++R1Z>dm+?9sD)4qX$`!L=pFLmv@#RH3`7M)1w_OMvEsHEfl8{-5q$>jV}OS#F@gzFtxS<#P?@<VGZ0a
+9i&SxIgwG?a5q)DaLF+I#A!Jye00o(z(7j1zW)3p~s*=EwIoy(zM3_MQLQ?ENil7C!L-XK*SwZS}mvy
+EVWCorDnSlzGouUh$6{*7{I1`Fgs7|m>jX?p8$ZU;9!_0)1=>m-vC!l#mHAL^AendggXHY+;+L2YYn?
+55mH-Y!Y^EOYmzoyb(Q|X7NQhUm@$BU#fvSGxSsYGqD3tlEf8L7#t&ZXR~M@nl7wMS1gm8R7c;uA<x*
+G(x?6K5JVlc<_Xvw9w^r_wIsn<8715R9tk<egY`A-c2f$ZjV#Q|bA`ZT`gJM)zsBp{n_CTfEG0(?2-e
+2q_zG^;E(MAAdn6?0lA$I7AovkCjg6y4VAX;BQ#JiE|tx$5Qcov&qgZoGE)BT{mI^;Z87-Q@WjN+~un
+D6YQv2t)1#y9NKWs=MuqA5GXirM&?PB-qvtwS_Xdj$V+Wp@g7q*ub@#@>)*2a4Fht(Pg}LSio*60LiZ
+)BOZMF9N^TK?>cvM0$b}d5c#>-jf!}wxgFvl`8G6Y<1VN3UpM%GIo0n&-4`cY>BN%30t!u;-kgDiaO`
+-=8s4dre>cDueW0xI%)Ea>r;y^wZf$X4hzt<AAcl9(5`g79L3vO>ppEIKx^U8lRf-O1V{jV4fGe#uK!
+n%<9VpA+?+4$PJ4#IW*+g)0JGYI4+4#FOvTX4{>0sGGJY}E^W@orrQ28~>EFMTr+6br3`Ksug&uoCT-
+QAcV}gliOP9ldmh?o0N9jWldLOLkW^sBA9`>`c$<#&$Los|mLkUak&;HRta1GxbM`CM&Axl1H+z$!Gi
+J*=+Z}f;9r(8R{_(`a5p`fnvUR{E{&fG~H3x3J>pP>R{6eK{oy9&V`Lrc{~2sA+)CupTCm%w}&qG{h5
+{6K~V3yX74pSQH`MM{RKE+-Dsm$?6;W$b`scW^k^?Up$-g!(6{xc8ecpsJtS_Q@qsMx^AGQH<IPNjp)
+j6_py9xtha`)b_vFw<{}R1mvfZ~2D+h_$nfM0y{4BKVAZYev<=bPYJB4_lFMB>PyN*321-{vtZ6z99H
+B+AQ%|YH$We@dM$$htEI`hExo{d^E<2INdm<JnAY4-1fa9#O|`D<r^<5Fg8v%|z5Bluya+s@{j7qZH-
+Rj7PRZs50Pl47nr>z9+|3fW=C|L9tiX!|hm1Lo0^XXi7gSgqZ7ih1KrY;bT!+{#df1A7WJCo|RY#aE<
+nbHlQrn9sgEv`1=_1($=66{+DsF-&e@tvTTSc<Ol3_WeJhsRKKc&FRP8rA%sFlMRAyCA#9~?<dI=I~<
+s4(AO@?5x4Li4%(wrH3HpC$QB~!lx7(Sm>92v&_TxPKy5(4eY?hc5V%CS^I)J3-1;{N0^hB__DF5jf$
+0Tz+Ht612Z7l^SRL@GY`1!z1&*M1BH4LlOoq%`d#u#&9zRAYCg#ovp48S@w+m)JRvl&jU9mNWKsl;GO
+UT90gMcTH4^!^d{%^}Q@)D;s!#wXX>q9nSxm{^$*5mQHRo}NeyOpp;z^lGZXa8{RsRNfTH+}qDPZqjW
+zTL~1@i<*P=dS3MS?E)+l=dNLP31Z<cD%QV>vp58Cz5Ru+m%*$I4}o6J7to2*|;-t$bs{_&A0pN?9O)
+Amo1;|N^E68b`W&yefW?k?SHbmFE{E(5QHt>+Z?!O7S06gMmJjNyTJ@1a3@fE_PKM|E%`ME-5TK8=uX
+5Q4(gj>>vkXHzP0E7)yB`nF5?__e<cQW9oVz4OI5e-H`D=dU%st!RQ;@Yfj?Xa-PH{osFp(i=RpMBZ1
+)Ye0R$GVTRd)V+}5@RTb0<y!xnWQ9bfGzALXD=Vf$+Q;hm{5@H}<ajl=&2?GL8Og1i6NTkTRBGUxH(*
+*{u&k1EGOl?7gbZJ&Kf-ALk7Qs|ryc=>W6zU2;ei8svtmT7#RI;(@>7vE&M8{hN)oA<^%Y1%xWV4h9*
+w<pBUbLaW4pZCP($5MULS<++93wr+HV^rqxcW8AVeyAkxuO621JW9^9BBlo?F6c2`hzC2f2|bcg34PI
+ed6?eP<Lf87EOicnqbMn~ZvYm$JdN%()_*#_JbOExj%RPjKY9Q0yx&`ot8I?$k;iub-?0JSBKuanC5%
+;c^uS6IebLiBk!F8H-hTm5O9KQH000080P~d=N1UFwWBm&N06j$j06G8w0B~t=FJE?LZe(wAFLGsZb!
+BsOb1!gVV{2h&WpgiMXkl_>WppoWVQyz*d2(rNY-wX{Z)9a`E^vA6TWwF=Iu`z(UtwLeYAdCz?T~qCw
+I3)KdQo}PKvnhAD$E3kIwW(G&|Y@6|9zj6#Ic=t2Byqas|q4R;v65Jm*+fohRN2J`-i>Te}8zwjt=+s
+PtNz*yQ9;+4{V9-mbb+&Zen(tC)Y_D&DmZyi&;{z<Lp1ld>-NY_VzAKrcs&YY&uWkv}9ArY<p_xVVaZ
+)M*bdujpx}S&RKTFN{3F!pTuP`jTW(?Hp$Wgm-!;gs|zS*XIWnNPxCk`;~BfWuQc|jigMDmxW`mCC40
+H|iiz=f1Qky4ZMHj~vkA!-Y!Vl7{xzP}p6q35nJ1TbaPwvH>J54rJC5=%>@1mI=gBR5&0t~4%8Z@emp
+573_>WM{lgm8H?<s0mc^tDMyDGoIu(#|!yJOQRWqCYHidsDaQL-qV{WHrk{x+K>SNGI)m(Ji0SzX5YZ
+J}cL{^TP&iVIlE-p6U2!<)0a%Xu=z;G<+3r$x-7f-R`8xY3b1AVba@L)bwE^;LA=vN*wgn)_><7wBPf
+Td3I@jXp!JUq&U_k8Cbz;42t?&*sQRu10P<L!dpKu_Ue3-XQikfkHU?4X|RDF}o|`tGl@m4UlD@4ln*
+V{dmE4Pd>9xyOYW8$;IclkVT|%7x}9-CASMeaK_+O9;M|y42Oc_{mI@R7_s~A@aXX3Gg*IdcyY3Se$E
+a~Cv2CU?M^NZ_dXu&PT1MU$=T`o{x)OhajZg2!E^<=4e=FJ-e!p8EH0yDUevCCMtTbvIG?eb=xdBLPv
+ZoLVi8JYalcj$=UIAP`HLYspl?}n#nP<w*|$6ac7VQKcA-V**9YjQ+dc~ggrfAz94R=*@Pp(Est@K_m
+iz2oR+J=qyo<413c+8CfD6Vxo}*{k58FTa{gI@y<w5a>)5Fp02_TbR-$mCkVLhHxrQJk%G$kAzM~g)Q
+w=bg0dHlws0h52z?ThR*M>*g=`xkrZ$uRUE*LIxejKNL5h8lMxHG<Rx{!<#Mk)%fYk88`-1hq9{N21Z
++9kdW_fHp)Mp^beJax{S^eKv#)4Ie@dF(NfP+!G;PVpxb_A%=w*Ho&j}h7F*80QCb59fV7*32STE5JQ
+HTX#jOYTw{p9tZYcv2qUSf&WGru2KB=c`p1=;0r%JdRt#}T-B2M$uw*1~4y#5GA5kCFjFww7gi~W!GK
+TmV;$w)9VaXUXkD-1HC&y!7a*ig@5NeL$9Jt|7%LkR35uD;fT#nEnE+8%-E+8%-E+8%-E(U0j6Oa>-l
+aP~;ld@7Xh8hWR32{j=fSAM(i6N5CF(kl{07C+(4j>yqHh}5?a>1}t6G2S?@c`mG5Z{3~k}r{biR4Qp
+Un2Pu$(KmJMDit)FOhtOB~o*V5KGdCOBl&u0TXl!#zHmLQ1p@i$QrC>aF{WYA!n!%M5Sg5HSnDY5~bv
+kN}LNChK5)%#DpOxKs*{`1Nuwqp{jx33~B&3Mn1v`2KX^J#^3-WgW(XP^4lRyVL0N!c1Rxg5ib_hYGS
+AfNQ{QiOb|zm7=a$hW&uGF;sd(I-;kz51F`{KV2f93uDJKg^S&jiK-8Yu=$Vav7B+e^4_OSfF8dJecn
+|STMQba#$7^dmXzIGH^Fdvdt7;t9c>T%_YH}dbpehcVil%C&N~emaDkp;3Aqy1eRpd0*yhnsZB(;k31
+X<8!x}YITB2i*BVmV?sf-^B6F<`}d!-^@1U9gQ#((svFp2_8zTv~E@=7ySJYZCpQ&0ScL%WX8>yU+2I
+olKt@=9ytO#V|j<JbwX>``!mP;)IGIekesLhB<t9JPMz;FVE!iw{2et@9UJeNA<l<c~>iL*uWfwQx_I
+d92FPQ9MwbEd@e#%M@2?-NA*I6Q}I#7Hay{(M4@=1Dy#~RI~6~6d84Y-0k8POTSg#NDOV(-iNwVtURC
+eJyj1hVzEt<bz^q|m^1nki)h8wtlO5WD(MKo555&~O5yaNS6U5lW6=6d(MEOKOv=mS<2clvuk0`4m4W
+e&iBU%@TP{M{pXhtFzqWS6*p#JuS$Yn5iCYNV&`E_<KPhPcufvwL^@ocokj_JQp>OW3ftjx-2&WiZYy
+EvW3#T#UZ{{#J3NnVryhcxevhuebz7y7>7a>o>q+d(iI)BQZgZSz+3!?y+ELHE=6Ix3T|u`R;4Ip-KZ
+xhsk!N{^!Sy0(|-kNd#`2QXrs*MDWxfWn7{n+LCkse|K?N`Z6^kj*fcm9-66WVvr1(&uX)tpzR{mRlg
+3h-}6J9noc>Tb3!*uiFB#ehik)u$BelDOoVJ))^7Li9D4j-rle0Uo#7?*m!-P%^9WPZ4F;xfO%~sy6!
+%*n4}d0ZPTMI5RbD!Azb!iptGRPM0ISg8oSZ&=S4-`yxJrm^lLymdTJbKR5)U=x(?UCfo!;#H_;YNkE
+#XHShJ=D55_=YOwFRBVb+3ZuGT{0nptpFqsqHF3*1Qjt}JNecaJ&>B<4cqrdFmt3)NLGd~Rf+c2ASO#
+>FOZz?(VyeyZH_HclEB%C1dVAX^J$gMNib-p*rJqiC)+#YmzP1G6H7HU`p)5O1U7tvjQ`f<6xP`d0^5
+bAoyO_wYeC65Q7;q4FXcE1VXrJ|4L;>FQS*4_Y45d{E=Tu;4M?J1wyN7M6CkKT=cZIy4k)APcfx1(g5
+7U_FgQ(bk2zPHbwYYDPe@eqcmjuL~NDn?j<^iK^0T)T!T!2Sq#kQVYIwWwH^dqM<TwV_#+AyJE0pr>Y
+~}#eu3l-p0k;r#hclp2P>D^;ToER;qV}Y7%|*bWT*53)&Rw*sD5NsbB*Z==>@q*fqg@-o`*0=k=Lq#$
+8zEb~ED6kWmYLLA6r$Rdp;-RHZsKb&K8^H+~Dh<wT7w**S!Da6@68FHdwtx<k4Hxo_HKJ-usN6dE-m_
+0H6+%#H<p&+UNO5inw)I?Y|OwC3b3Hk55@xHK)`h9#y&vL|6?!V?Dm>R}oYYHw`gK=>veJe#o0_?jVe
+;joQ?{X}5SXshTWvCV?CW_01rR$?u#LEB{_!Tz{OWkE$rwu*XLa6xzZRh0$(T+*#|UHyS03RVo<#nR2
+Yj*h9u>#xlw&blDC!@Sui7L!h-F+&}LZt`@<8yuKb-bLmz?;9iv1BtvvgPjFaLE!6X7+J8fW)6`Z1zo
+1_6x-NrsH>k_b}9U67d<xzYTxbcL?0cVUHvXtao-i<)`D)myC7pmr~B6vEh}qpwXX^peKKjYU@ug57P
+{8B)EJCpCs9R3?@rCR^^^t@b;+2Sv2oDl25z&mih)A<@{ec@3%WkLuiR>QZGpPoU!BdcDj8w)8aS{S&
+z*CZ6V2LauaD-_fzF3nS_<UV+PJ(Hx~ny}uG(7Yu1BW9vZp|Ek-G2cS-P8hlasm;F>s*QM4N5Y49gSP
+xM2EbCafFnz8ax$pjSkt%7(U$e}@Hq%()TSAKb)1KZ$lZ(R9GvJ(-E_?Gw%UZT{=__HdxkuB-o{EVOH
+5*UPo4QO~q|7FYBw1y4Dw7;xWS8&@0OGLnmx-R3^Z0+a9TKGPn(b~v&itU0^ZEww<+XmgG$2R6O57Ia
+CYE54GIrEjl)57TM~2dgbuo(th~^TD&bz;%htML50x_PJ288cJUOiDaF_cy@N9z#S2@$6J4M+q1yjJ}
+Dbqb<q!KWx@CRZ&<*4R&b-Q8v{GRUVrs-hi}=@!+|^QZu2VzuRQNVmW*=sjfDStjJ=pzU(<T=hPhtir
+7wTHSkT+iGy1B-aa88XUy$v7&7qRJy|`Z_(`bH{6$yPqVnXjVLzr)mhxF}>O6W@*FZR=GdguKpF3UKF
+z(F)GVqY0m#N|nJ8>9cX`bFg5y#IOLr~0)%_5JwOuDIU;{K3w^s9E-{cukg7E*(|x5<Si%=X*y#*rCJ
+o)7PU>IDQ>g?i}m47gqbS{56Lk?2zFBJbLSShnKgb!C<vF&Tl>b=*(0$Raqey!th}idfy8F30w$kU~R
+FhdGsY7y<utgXXO1KP)h>@6aWAK2mtey7Do{wLmg!a002}m001`t003}la4%nWWo~3|axZdaadl;Lba
+O9oVPk7yXJvCQV`yP=WMy<OY+-I^XL4m_Yi)02Wo#~RdF@(lZ`(K${;pp!*kG{@pk_tNj_nlpA<ZV;V
+ACXUQWX7kP;5F8sz_c*Zt6SSf4_&4DUy0|okoj$K_f7d$>B3I&kTpOp}oEKKYDxm?);K2&QDG+Z%*m$
+#ns6l)FHd)?9uImpsO^z591)BlVmO^%;<gck1&b?e13df#o;W-layvrDB_%EkU4PF)blvZL(Ke5d=XK
+y6e%SO%3ClYe<|{87A%DU8zymvkLfZ=iw^*%>m)7vXQ>EsF{itAf!I_Pq!MenhN_30UY+a{n@q-lXca
+%8<0zu3l+9==GLe1}^U{-(B+k?DZUr}AW&20yA$lLApXoY`?$hv*UK1?LDNpEnoj)Y;#(z*1g?DL?t|
+e=WR0ztFMg9cC-q1Q((JY8574tAF;Xy=BK|KF8NiqL1nTLzD>{`WhxFfC3Mf#X24&PmVpbL?~QhFz1k
+;0qn)m;?MF!>^!i8vD!WVDoh*@I^4Od4{tF@(+%z!&VkArWF+>i#0q3_ZjTn9{aEbP2J3739)>L~|)8
+?!)LcMTkbKW*)Rspgo;a7#FY);2sYEgriSLE4mZ3%EV$7xj=v{eLTPY{p!Ol9bbN;kH^#L@#XEOH;@I
+>7)AU_r0{Wx6r2;hN`p9G!*Bq+Kb@ZZjv2>q&o9nzKS}G)&TlVIZ*J)9YD&j+eLTHAKlyNRJf-Uo)9b
+67(*vR#Ar!@u%r=rMiWh)<Ou*z^<Utr^rR$#%-V6ptb9xBA2!weiLSz&LSR%{yvtk$}@qOVhrf5RnP`
+IEt$z6I%L*x#!uMu6K==i!w`q_a?Lk6KB{v06$H<*4FE&zQNB}wYi+a$}S==)>L^*CeRYvy~5=)(<qy
+8h_&^0ys}%ANyYJ{%sE%PNUz_7J4O49Sy~iLlC6K@_cNo`_63Na8E<Mkb6*vs`@53xoj>5b7|v7r#zL
+zDnbw+drag{dfo5SIbpA%U3~}q6KsXc_#`|!P!%i&Wnlsh&-(1iL~!TnS^z|T!xUm4elaw<XiwP9{pX
+8-zHZn)+Ge!Z}iH6OYUxM(E3aSzP)F_v^NREB#iIAq)8YqVYs_n>xS`5YiP_}=6PrXmnO1}@yuMt4sp
+jA!}EBA`v_wk@)&1wUg^}pb7(X{mz;*KF@_GM_yBE!Dd?ME+(X0EE^CGxYevv8@&F?n0~GF)Lo}%oaE
+DOCWgn~=p^Y(ii2e=5hte8pAU+<-hT-8DqS6`JBn+(cq=oJLNSIMS0U!4RGuh;g5921@?zu7=SWX2k=
+{FdmfuBQA?SrcU+6d31&9q?&!8}QO33KN;s6G&9vSeWjx(9N)%pLg|V@N#$G!4)vVSK0<$S9%tlLBUj
+Ncr$@Aag>(L0mdGK;KYCO(uYxJ}LsvIG|+;BF~5eo|W9pVGV~EVVDD1=7tm{sa1;M&ZQ_ClMsNG))Vf
+&yblT(0c%Ja26QI7po!#kG%htt%j7f04$(#uPO1Y8A5|1DJZIl=p2C=+=SMaAQH}o3s}agZ-_=No{HR
+7fRU@3W@m(%bCnbE%N^%@$Gx6t2#50i{!2<7R^e@5`$GIWn=GbI>z{f+T$FT|rh5$Pld0sJ`3XGal<)
+3%p`NN4Ej>SFBXgG135*TKK@z2gX9F9L<1o3@os;rcr`?9Ma03;qRzjm?B-K$2uP2<J?vd8f^NZdf>J
+L#@0&^o>9Kd(KNYP1c0rNnuTwl>=wuh7xPfoDANT93A?vfu?Aa0|0}NeNcdt^ON%&_fiwn%-ugIi^@C
+IAA+lz_wXXqN)L<iFH_10klQoIaebS>!0~1M?5P|yt1N?)#zKuW=@(MG&6<UdC-dme8Yl@f~nJURrEb
+8TK!ny)%0jBddzcmJ>c~y)bs1RD-w4-%kyTn;;y+8`{TewwyTh;R;Fgf0@tgtem#=|JN9Ot`_>q@dib
+XGQITFu;`6n(L}6P^)clxP$y5~l7StDIqPbVwW6U+{G2bkJzRm~rn%9;GTEF_-c|K_4<vXI#o{8FmEo
+<SK7L>KOenK#7lF5N`mKU@7S&ep0G;2>=Zkkoucl)l&CV{5U^{Du+Zvwp7f_g==t>MPl){12A4d1;Q%
+}AS>XJ;S(%Q&d-#mX91Y3IiN%{Pwrn+Im)_bdH>eIDqO_qNr@H^)(KaL{>1`p&(PS4^nN?^|HLYaWVO
+arJ#lW3IU_?DL9_Lg_uz06kdHRu9yA^DCXa;)EN_^vQu8JX4?EHC1CKz>6)=mv8v2>w;#ayjWF@Hkqh
+Dsua<QTEBaSOl5)kE^c$cEY9_0-?i=OD-RUYyylQ?fgPdp$x)=J4*e~Txn5&x{Tt@S1tqNbHzThzlvf
+}MhL;x9qtImE<iEUIQPBIb9d`|&oMaVP2L~n_CJbv>(2<GX8O9d_aDxF&qVL)jQ8`r45yxcTELK{m3D
+dB^Jpb8yU)w^m!5l5zp0sUR8zkyex^|>F)efjpm2GO6Wwz#}%MB}x&7WB66Iy?SsjmfZV}}`yZWbKHJ
+g?OnGmNJ1t@}b-J?Lh?B@eijiAra4A8MA9zSe?yGHho5w&|?S0jua@u9+KV2pb&e-vjI@sC)aWR8NhH
+#A}+GF4)P`LZbbZ>stpdr|xfEugXc@n&`De@~cH-uR(n`&oBM`md2|LYnJsU8?4sXQWI?`oeA9d+!BR
+4MfG!lX@|*B%kIoJaQdWTgE#VEoA*t#w{XzrVRt=HBaSA|^t~ITUNsu4vAJy6E*y04KJ9yi#es>rjq%
+R4&|L3zZB46c7PLpfu0vgy!9hL3>VNl^TeG0PWz<V-JC)Y&2fP8YJqs#;mt38@^S8)r0PHrQnQX>d`1
+Uf=%2@T})HSpPb7u~=N1)wNr#-&r>foJ|cO-GU8_&!Gy*{jp^ervhMr-w;UVXlO^_R-qG_%^1>2$+_u
+2kyUIsPlY^8C*O>^qy6N{e?B)O!j4bclYLS?_iIazw3fpSn_XDc>)i%hw&=2YDKPg|PjuLm~BeyIzK~
+Ai7SnP`)EEmG8zv)H|4r<=Yp9&}Si*r}4dfXZ%9sxkw>!7DSnFl}(w*FM~&c{-4fou3nGF!^!K>PtLy
+_=VSRs+77O?zq|Gy^#vx)lc(&pw5#xDj}}qzS&WY;PW}v>e*;iU0|XQR000O8^OY7yAEJ8-o(TW|HY@
+-DH~;_uaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4dSZf9s^Vsd47ZEs{{Y%Xwl)m
+mFm<4O{K_pc}&bTlA&IPP{55=N^P!+{yJ+!R2gd0LsI0c(t%V>`oSSNq@hD?3iNZ@H0uSXv$1-PKiJU
+Av9Ex7YkfpU=KrT+`*n>Dl$&8GXLIIsFH<(C%4#bpN90Cib8G&<p4^8Y}V>x{Chg2Z2Wqq5o6G31&tw
+UYw|u#wt~#G>T~i(nG7@a1r{c53)biPZdNH6;t#?X#;D}FaGZQ*C>RnF!i3Nn7EDN)Q`f1MzQizHKxZ
+|#-q;Mixn77W?=r3(*EfINyq7eMI-qkodf|5nKq%JN>uz)jdSd$5FY!FQ)slG92{rpuDtjM-TJ|E?7z
+}SngA805#7$xmnh6A?p|i-UJ&tow9qiK$($fcUc4~$Ug$iEA5~JPqy4`h%*_Xb2mWL1#WS}3Q>+vv(N
+p>cEkDsLn$pM%DOO`Y$+5zYDS6@ek0{3a*J$iN&3I@Uj$s=%L8{`{q;SEP>j%123Dl-9DpWBb+)f_@e
+}u)C{z!$1A}^r{k0meK?B@*RZVm)`fW625`9#XcJj>?bzzE4hQAnF}w4pEUdnw~bFid#i0YJ|vfNSGo
+<zb@<w5DV7!;I|<9Elq+graW<PI^={P1Msgu)zVe^zGvQ>&?SGom_vXZzsdy$@TsBPoRa{FpFTwIr*=
+Xzz1V!6?<Vi18^|7IvbvT#fp>97nc|J-x>Y+#r^f!-5s6Z4C#b!Plopwrw^AWL%Mw!-rn4u9m3B_6((
+k5HW_(g;wP|tjbKTnkmm<UuKIVlHvzz4OfTL~1vifra&Al>qGU4L$cI4`K4<!3i8kmb@}DS-Qk&jlAF
++c_u6Y++w0~`6(CE;nt^ko2{s`cLJ1jr<pTPP&h@#l0&ry;x>D39=I#LMdqi`KT^l*ofwm&+%{&UUJv
+}b{_>~@dy9F6i4<c$zKNgfE(Wa<UMjK&f23ks6@g}C8>;V?_puQcP(0SCC+_ny@sLzPbBFdKG{DVe=K
+g75xh8jjMbmxpM^dcpEhWk!X~-lBM%EtJRPAe{${@0%ZvqqpRv08j*W_&kLa&PWw-aVY?!q)aA0_}qJ
+sfjYL{{E%1tlL_vln;2;e3i=!ETNt+2>z&4XBDllZfK78}HiFr>_Io<Bk<3Qg>-BavZjOd}1DgiIrXy
+VY4(=tcJa&YR=U(3yLZI_F`VrO)pin<os^eI61QH1r{T`-q?H$>|k?3I2?_epq<Ko(FMbn+5=|bXB2P
+{}JSanz;v*l-ugGM-zJYZUk_iztyQD=~`=>biz2T2a68-_vFVRpFoSxHu^k82lf`mJb=97{;V0F;mbE
+*-vyqz<#iGN*@rgwFQ^TS6Q%79I2g^ibH6f{ny5p+X08JFEuoUGxDM3dYIAY%OL2W&&3UX0WD3E}IUL
+But4(;qU|&NFK)`5DOqf<YSCG1oT-!W^!a(0x$(H&K5yyjzx({*TF)-b-D9A7`tpK(6}&U7pg%x5U{=
+&=&*Bn5=1T}xR8X!1ZL!dDLVC_7KBKbg#tC`Ea~zh^bQk2G7KoeOv0@az7c>a;UCHKu#fEUGnl}g5}{
+xbv0uRe5DB&*Vh6D#I2BeVs7J&|fPf87JdS4qE!4J%!xn}jSen>Cf|)@Dj#n6w2%V#aI5tI=HG*w5=w
+;Dh9dKE?;sR6@8yt$F6#GS@{+g<Aq>^JWaDK=5lOLmAc7-(O2K~d1<8<^qQlnE^eApfI1<%I{)8<t6=
+NvlyBc2Y`GioaKv=RlT9nt;W`l=pd^>&GxnWN?6=h$vTlZ~VKIJ8Gce4xYruFWj+2a!$lH7Ai-6Z0k)
+S7$!QlvcB()lJTuCFYPcXJ^sG0;oAse%cy=TSnk!+L;K%8VH>25aev-T(DJGSC*_>w?Q3=*v1C3<N??
+8LLph$%QW7?3v%%*7r$DhtlB`>vPEVc&DL?CgFHjv7z1sRb{>gr3&%uYdO;de>X9hdMbK6c@;PDn;~j
+C(8i{fZ6v8fyU$cQ33GLaa(XQzQS&2qn;4EiR^F-zxEm5wBi3I|&v^k|;r}Uc@l&Wp3HE~tjDi_Q)&j
+nqPHfby4Kp54)^hUAmh~{iu76)bPtUw^<Uav+$p>9hyRuR-?u0+tB6LNtkN9WrAJ1>+yZdza0V_g-O*
+2Fb)VVVBIut9-I?+GO@h!qH`Nz{lyR^*k(nleF{URbpgu7RMM15FhvpVQ$tw}D2m$?h!M5SUoph1CmO
+=0+gPHF24|@(D_}#yD_Fao`##^iiNzk?JRdw%A({2h|)X_CeRcpbx!<7es>>*1@xKUnuVobw#Xd-CKT
+Et~aY{0a>zn@hleF%Q>1-9GDfUDIQjMK`#>(7Ry=XBCX{zRyV8I((C)M`P_c!-wlGY71|E9Eetk8(D=
+quue93Z=4)U1^{;r}%w9ZoSeQ~)TO*4D9dXN=iO75b6?GeI+N@;~>#h7!W_V$F2CP8fw#sWqm*=A}<6
+vt9%@wIo%glym5gU1-iG}4D6{qZh>JFfz!Ng=W@BACtp!!davW&LMT(UuZPcltr8ntc_t4Y_Uc+~$YD
+SN~+xou?wVYfxTDN|>lK9J=Wd4D82l`}{aigGn7D$=eH=p$y`mCKhd_QrRg)|bg^I~A0S_`YUU>wa~o
+uF7uAg|cZJyP7!FP1iA=76(o9)l1su;m?eLdP?oW2CIpdMMUkCW}cXI9Vo)u{D0$oYuh}0*6>1E+Oos
+ut`JmhAnkH630vn%4MDXIJI1|9T=zt7*rji6YpGzqY}NO`dU0Rrh5FJJ2<p4f7CTd2dUa>2rn!EybcJ
+6h`!}$`+FsaxQ)@v`4Jos%=nOS%&{X@{-wrlFAWKU%0$mSE2zG3PayFWq?2<Ux_PE;;iRGQLzSU@J?c
+N4;1jW{(ZBvZa;$UZ9D91rrdV?1<#HO^>Y@3m`WaIiO?S5x!-WPP;uI0kc5&VBJkYxs9F<3qYJ9ag7$
+|LiG=DSs>*Tl`TW6fNsD`lJyHj&q=LLGr-YaWIhDw26pHM6p9Z(eJ6ewPXxGiVyDo|u}?iTNFWJn=36
+G%No)q4+Z4-xkq_QR!o?569H}6&HU5@?pZC@s0VLhbu3Q{a>K2eD9Evz246z{>Te%qr~SgC5HSB8fcx
+vL7zXu$cTO@H8~5P`P<A(m8L2N!MPVC$}UhPD!ukz6~=#iGd@WB&H4|24Du9sKUDvi!ssv0`Y%vR0|X
+QR000O8^OY7yP`8?X#|i)d_%HwfI{*LxaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT
+4dSZf9t9Zf9X~X<=?{Z)9a`E^vA6TJLY$I1>G?zhbb#Vq3t?l9Xf1Degm>P21qoBuG*$`stw9Od?c~y
+pr71cewxk9xYKMCE9VeJ}eHn8mjeUIDGTw&5#bXx99&y?@m6Pp3~Xs@yYr13B5bJIQ~1WvE2*y=;lGv
+MUmVmS)9^wK9iJ`^eO*mlBO}PA0HQ4GL5UeplO=OtfDDw_5(fiG)t-kBY%_MWSY-qLHQk39-pv3msL5
+9=hAYU<XMT!VxAYP3pl3Byr}O_3mI2(Mz_nA$Np4tp?uAknChXTSI4i3PbLvuc+L0eC{5`~*_L!AOId
+uAvpSOFJgbW2c7ZTom9GzRhv-vWe5K1Iy)TkSdP6{1QI*r>vU<p~CVo(rCbvagEEQ{ag_Kn0chwVsy`
+^QoplO^@A!kWhy9W^!#o6rFyukR!e3slT)vZM~LpTa`C5y*WbNJ!>Go8s2Na=&jWPxZd7q@9L#o)7KD
+zj2jT+&?KD<2F~?-h{i1_-^+;eN&LTapQ$tGU0)qQo5%`&`e~cpMON{VJ{$eq?j52EGR9C8fwlp-1j}
+DKL@FD9KiCAHY3M;1Gd6K~{7tX;I3%MLK{7*wXK(Hy<xP-_X(d7yA9^>gwqH=F3~yf@wTO{%TLj;~Wy
+45uz&MtXcv%9DF*tI{t_eNAFJ0PH(;_^zTn^&QGqd>HWnO9ns~{)y?Vg=d+_Ly8L`~d2xNRPjoG%rdW
+~bBe|vc4lW;aFgcS|oTOzP`WK|P1i*Ag5AipNG*4v$MNy0rnJ-_I!!*zCSMg$qA@nUJca-JTfS!s3+J
+X9d*@YLA-vLBF-5<~ahf$n;O_73Y41b^8!S(wz&x--Q%gaicemcTfCOBtrc*r=>=WE<q_m57_f7`OD>
+;-Tv4h|0MvdUAMKEy>lg?P$Z2#azNr|FVrxhz$HB)>y9N-#=hmHb|<JPhFhsZQd1`RkRe7DcwY9UfA-
+e7uF<SMx<Strl@D(Ut4g<*i(i3eKMLVzwG64vC?XCko%OhJ-qw&l6bQ#J8zD49?&!6aV<Q$nIlIohie
+c{GvdC!jk?*uL8t2*zOp5&xt@hdx0DOq1<rgCLH|8q1*`NMhvz)*1HL7G$f({4H<Me#5aJ30_V6MPH>
+KAA-<JeIKn-SZ`g&f8;%D74RLdfn?q%wjxmgJ3^8y7!x6q!^lPxx2naKB7z#M!7))>|@Xc{w00{wsz&
+WXB%0`WWbBcgK;9{hVm7(HUg#-s2gGRU>Dd%bs_aop3lK~$w92|$h5#ku)IKVN&F~$+$fZZ5&2$zpxH
+-_DK2nS;rj$t^4;TVQv7)CIRU>LzLf?))Q5$qz^MX-xt7r}1A;9vs72@EGNoWO7b!wC#02unGiz<4ql
+2*z+A3<fe8;4`R{9e{(edN{%{#u4GbHTV!JW{@aAnBXwx&?2}HAX$KL0geO+4um4?00$ro#WcbYAXW$
+r5*Va1GgRjoDAY4WE^I?g5^^|zXaw>vlsFDxpacT)2;>R~M?e=si3vp0NDWrDcn0AKh$^5+r3r(<s2C
+~)FpShgq!Vll%uaw$k-7#iky0USKs<riKtQ?zY7~$qx(6P>P&rZ|K#c-&7E0Ek3KfHdy5?<_1h0eODa
+kl&{@+dh(rzODr@P6Y>>|2}1l=y8N>OzZRW&>4CZjF7iE0A7=_X;_O;jEK*Sd*n6#Z^8Y`V!o(@hi_(
+R7p0>L#jDV0I8$cN5iF#=dScd~r9yW*0xEQoU8d@0`5CezKH*EMzv7<sn4Ie#ZT~q`-dafD8NCWVk;L
+N0E8XnHX9I2m4|qLiM<icxn$-|JZ&MO@``mChxIH!j{*zVEc@*$<IOC>dxZqzJ{y192<liKm3{Jf$yg
+9+!2jCzB;cz>O3$HeS^7mZCt2twmO3Z*x=891dbH~z6Jqz5QG*2u^R-uM&Re(%*~zqno`!F^&$wLiQE
+1-2!!+9&rO{#(Qr^-dl7VJ>Bha51D_svGo!Fnp*aZGaj-3d9tU0F_K}%0)*uVCU)%GhENq1!Y*bdy$u
+0i%Id3>vqr~nk&meFkw$;y_I?<VUQ7vr3vZfZg-GO(W>1)>1%ZsaVqZ0j^@YTmN)woHCViN*;HoF@LC
+j((4yU<qR_6VL)q7Oq~C3Q85AMXbi0uyC7-*w!4V0w|Rg$)Qc>Ce*>`GEJL?AL;+M`qOm`>}Ol3j{jf
+rU%tw@HWt0Am~@3XtPpRot=TOS=M)%1z-HaSp$qN?OJfU^5$7Kp$p91CT_bcYbA0EmqxJLTBudWt@+K
+lu%3(Nn>A;<SQpGZW>#jcOuGMu*1WZ=wOM%a%G9p~(K5qZ74oJNaSOvPcWFikhUvN%O#a2dRie7uRTf
+;uhl8#r?ERVD6L+D5`i9AZzS7-?wROmvKHzSMcGDAmI~1dNb7e=rlihlo1-%k=XV>4R8|a*9Mz%u0J!
+cjIVP)H_5T+WrHE+Ey;cZu5%fSnkXyP%|7PdKOR_tCKn4zL|!`g+zG#c&3U}zRs?OE3WYn88!ZEU=c;
+D>c!YQc0n^SemT7dkVGogy%u-_%^#u0OgzYmI0GX6e}#0+UFs1H*x6EnG~N4$R*JT$#E9`@>gKw%NB8
+&)T8P8f4sPw}EgVy0dZXS=f1^9R@~!tW|`b)nKc+u(4PO`ZF@?f9?dc1p+(AymRHMv02(gXZCgMn+$a
+j=-t1my|9he&5vCNreAtFaFf+%#O5RrZJb8u_H3qo2n&HX3$_m2Zsg~qLBOq5Q2!P%Ixq;_a9BHCV1u
++7w!?Bo)XReLCmiqHYIJ*|erk1P!$W_reqrhJfIHn!?|_Xe4F{n!)X74>TwNU)epu&uQwv=LHt(CV;H
+d?dgMK@6F*I4w!qGcHdq-||V=i?D%a3<u;Y8i#z+}P9Zfc^*Lcb@PIQ<;h(h7`UQw8=)%$%Vnsci^0`
+=$znGoRa1dK`of2O4mHkh?qC(3O45hr!d+fxY|a)<Wpbg1fv0z8YBHq3yuhS};(%jg0l?>~pil>g;~p
+1halm>?+aDPq)c+s*$&;vejm^;AX+%Kn&b|z^!X;Vd$=n7Kz(`JZmHGArKA%SEJosLQ_nAmff=PtB@H
+7`k`9`E{?U;Xcj3uPGJpn`*t_2fv^m_D(iCK>cC24SM%#S(#C3Jo>_&1K=(pnU3qlCI{$aunrCg)(d)
+mF_^Zw>AtUU-Wx2l}bP>3yoGiFpchkjtPuu;0uNJo3A9RYz-G|ux_rcp|yZF`CC(Xss&xDO8@a+M+ih
+HIO{8DkX>2CWR1im$)yP*0Jv>|j;<Nn>)ov*Kzz!xiX&ib-mi@=`2_h(Vxy`T%<Mqs;Mi=ZD>UyB=s<
+gPS+4mKiK_fvv7XLdH+`@8xJhOd1v@72aEbah}j>B`%3@H%MT>}=mp(C;SvD<Jx1>b#fr%OP!k4RoN!
+%+(veGxh$%r?@JT?-<p8`C(=Gc(a@*(>T4%%S636ai!i^g*n@w4Am<cE2FPc&QG#?^(Ob3tSVW+;HFr
+}fyP(L>O6jwxc$@V^~IYgI+(l>KL!5|g5NuDnQabj^E+vOb!b30%b&_O3gRlbJ-SQduQEEMEdL`8{sT
+};0|XQR000O8^OY7yOFe&G<q7}*3oHNtHUIzsaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVR
+B?;bT4dSZfA68VQFn|WMynFaCyaAZEvGU68_GwDDCL9iNt#8#<Am!?n5S<%tn_tB_6HjBeB~F8UkLxG
+vhtof4`@JrmHbdoXlx&5C;0C>ZzxytBVZp-u3^Z52qi`uju0Z<n-$9ls;TspZtrqjD6?5qv@lf>pXsl
+(<q^nY_2FS=ra2&PLc@EPfx2fo<(Jr(=3TqTG9+-2A-LEp2lU2nZK#8D#?~Ar))uGk4%ieQe`oVmWqj
+uv$VitzRdFK0fOl!%j@=8uA)-S>3&^F+*B3iTGnz6RgWd@o$QmCOb#KUcldyglZ0;d*n)0Vq4KY4UR!
+dKrDY!9uVCh0v44aXqRS}%LN{^pkjGE-3jtwCWkxsa@-a&r`$1I_-{(=j)~qdZrKrdj<u?F(PwQ+&vn
+Zup&EujL4<bs6()q7hj`>g7JYKAI(<+_A9F4kE`BPyy{CM?=E>r=e^iicMhc!2=`y`%W@<lvTX`v`8X
+sO$ZN6XZi26EQ`p|cF)D|X+LiqWrie^q&b7Lo&Dv^5fag0J_YQsaj=mwMtpK(8r*H*zy`&`W`}bWU+v
+iG2k3xPd?z`VC=4_lj19TC5Tu5-^tjIG_H0{b@?aSD)#R<J;TgtLf+W7z?J+3;)%U;-@7-a89r)kJ54
+t;1F<mdVBIaW*mPwzc`<M*67d9r&p(UcXW1rOUHC`d^<fq`E+r7OE;fxZ?5l72Sj&D8HzQTeI&OOFCg
++M1Cw)AMsZTqrhkTe3jj>!^ca0raPv&Xh$xD%M3(DU`7p`Shss_|v4p;-ctL4a`t&W25j%*!4evsV^{
+<bhpACFE5EvAtUlO?B4%5%#1w@}ES(f|sAuCEf^zs;U11UuCiwFZj^yv;Q+y2q%)o(i{m3JNl%Y%cXd
+Rb)&%^ssXnjv_KIuKUHDoT<y%`;VK14(^H+~|POVOFZ|WhG$<32=2BJ*Z!ARk_O3syRHOV*PXvxqHi1
+IxAOE9io-!R^+{^NCju#vV2}mlt&a`B~QF2cP+9c$-c!Hm8)Wvl$E0)B}RlH`=D3NbRaMC><L~?;-^?
+Y77^l5(Vn>6A81i0#yX^o<x-2AM)!$2@}}o^BN^!KX?C4sg`=PTNqZj1@pn6iz4t`$T<{7p{ihZqw3y
+KUkyDG2T8#8}JGYAoYc%3e`XU(N9ODe|tlNe-5AZB}n&^A<AL{-?oD&~$j8jx%hJhyz(R_&JN#GN%G{
+IB>u>ztd0w?Y#`hK_-%|VTZMnj**5+{ZWeGlzIi_-|vekgniR6>lQ1}Fl|86TouR$@khN0SkT;1QZ>3
+^dx9K%el!L{GzTAd<m$ZpJ_ZJ0<MIqQ!~fFos~L1S$zc5{M*FNTJ&>R%%XgFEL(9-;)yZButoS4A6b3
+pD|HlqQpe02V-IgB_Sq-P!eKH2nFE*1cW1i!Nh6+fJ@y0@M9fLxIz%Y$%F|=L(mRILyS8>-@(}TXov=
+Fk&eG1u7F6@AWYEY4l$6>uE~^I5&A}2Aeh4vfB>xtjY1<q#|RzTc0CZB&`_V~6B>=CTaSa_5w4>U0zd
+@99*yYK=18p#=MZh+fI_WJa|2G$CWqhwLM713p+=yi3`EgTU!heKq){C}faZj>MqEcY$6BCvt{$TWY7
+R!43{8}_4HHNDipgUgtNIGT2yF>2B>0jLEJ3DOJ>I@fJC~@d5|e6B@?<5pmO}lxQt3<;M=&w?2ig~Lj
+?{V}B=4KZ!6-Z&*?z?FkPR3QLNOleZe+%i_*DH*3<5-m?oQPMvM+W&J|Y+d!w|x!NTnB1`cNa)i4WoE
+hs_b-sIT^yA3@t@`@Ep-szue$vS}5~UHv4RGpI)BKCA#f<s}4RGqn~bt2-}fr`8yZR#D-Rl}1su2hDH
+5MhlI=Lf$;2bC{8m^?6~VF=!i&-J;PRwz{_>kPLxv(73w!RW^5?og~^Jq9RE&q>A=lbo2+7gV66Hkv%
+$PW1?uqRstl`C|b@r^MdK@9v;`{S{SzOwEDSUY7CwQ6}Bjh_ico7-6n2Y6Rf^&BuYlEXvD}yylL}{^|
+F!L^|@%LZXUvLu!+FLyKpQHc16IM=lpN=f{iRAIb^>!B7*kbBJ&LA0YhML7$o5q7B)C=z0H@F@kcivL
+KBH0(`j605%lY?aeHRT@cd(1_}bt>6*3`SAG<b3g{>z0-0^~By7^=`y>4b@bMLuwO*B}bf{ESCNP*1<
+-K;1Ya$Uch)oG%%U5$LzZSuY?D>sp7q`BLN(P9x8V{8JFwHLUyey{hhMYa}9G6{XNp#-hHVMD3GWu8U
+T>KU#LZIXFK^m^XK<%LX>;h(QXo{hP8%y#6-cDhT}wk*_*StlRx%wUr&7jCcZzF-2x@L*S=-7VxZ7@S
+D7DW2yj8wo)NOBIPB1LbDr%~;%I#n~IBZ}(<vmT|1x7IG7WZ<9ISm7lYGg9FRFCDSq;wv<|$&Ec8BcI
+&o_=4Jg2iL%w`RtE0gXgp%ob-Z8~N!JTI#evO4hENlQHKn}ta7?hdrjPfRdGL7E8Thu`wK2%ib$4gkU
+#W5qo?nx~APAWUc&*GUCbMtYc+_*8eSz0T!Z9q^f~Ij}*xKyw!=h;yt=%?9I3!vl44&QAK_kFTXm%}F
+kv7D5wI)m)7;SDH!u{b|AGS}Oy^&{Mqc8CJy5w=`tj2Cd@Us6I2gZTCK6QtL&kF3VP+oI*T_@U#)U8l
+#iEUF&vpaJH-CD&x`r14@u|If(Z&!*@7Ip<}-+_1Fz<ucoTMfJtF%eNC;3a5VXqv8|rRr4(`0I{+C*!
+dxTJiNL8;hP<8n)T>%k~(l=Xlm$o{{YPk=-vgx4dd!aJ8A`WB04RH<yh#aHE~q%A6PJ^cy2~TnnO|uq
+6(7ooG_4eGRq4yNf=sb>jB7q8H=9Xt9~e?t*T@H`;lU^ZK=lcH&M5%t|o6wOZ^8piTNbMw&O*KJAv1=
+Xvm~T>EZpb@MXZ8pRW@_q>Lky})09ZLw#IUbhJ6ucw_i7OVB;sA#q}tJN*Gjn5^UXrt1OU`h3D2MZ)#
+3GGg2Pb9t)f!SJRkA0>h;iZoB`SvdQ^TDPcw!JU3f5DKxt2yYfZ{x80a@<6sY<2Rt^lnD%<-qdZg)G@
+q{mVVnL0}xoWp`tvKUUf|*IH+{Dzj1Va$wp-{pQN0zcwa4(Gz={xwmV%eP)a4ZMBkT|Bh#+@@&9GKU3
+la&H&MghOQaB#j~}KXtnWL!P;$^4_NODd*ky(W`)4iAQoWP7k&P51U%QhgBK*@pr5qow>QTNyX{3C0Y
+6e?ZXkG*0OMP`Bk~yBR`f|Oz;y6R^;ITprc}NX_}@!5%b<|g%6|)qn=#=Ccw;2;!bY-R(eX;)^H6K6v
+~4@iZU#7i+1};Y^w^I_;@`d&y0m)RS9>3|JsIhTIdK1bWD~55Xv-_l{|Lbw1-y2GEjIpN2)g@tPeKu$
+!S3Cw5bR=Jv$@*+mlO3S4qi*@3trgaweNNMdIaoy%|}`K(**Nb!oNMDpJ&cjT0b9A-^W|N9=X&X`pxy
+X4wq4x$KUl>{-HxP=xMrM#<M87$%<HiA91U{QNze!Fp>Jpi)zpprIx4ZgZ_5&LY1Y;G2kpp3gsI_g(|
+P2Cx!N(faezg`pNr`{`g~&L|@e55vAFmk@sIvO9KQH000080P~d=N9CHTnR^BR0B{rl05t#r0B~t=FJ
+E?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoWVRUJ3F>rEkVr6nJaCwzjTXWhv5PtWs*g3qAm
+QoB!NSo6KaB#~U0vrtOWHKGbu|?3tSX36taDM&Sm9Ja_p24wK`|Y<kDUwU2{TH-5-@AS2b*)a{=|H<T
+u>OGqx+S9o<2i$Y<j*{G1F(e8fJbmI{^LR5!X)IcOh!23&0QHW1wK>EQ$m7=s#PPk&<(lbsCLI*Ss<2
+7f|!BYV;c4QOhul%WGGrJRw4IX#YGt5t6U1HU(hfNg^cB%WUgX9Jgv3GT`E_St>qf2<_gNz1=MRb6HV
++@uR<#bU_`nRj9A3vEA!(>ED<WnpH>)OIlB0Y5`cSGzQB+N&yp|T6QG9*lz`z{%|)m+ocS7mZXjq}Wb
+x_@B?-cm2K-67a!uKtNyZ=&Gxdg^-@;m~z;i>8%;!<ekC{^7hW=+E5x)>VpRGx>3Vn>1!ct5wqBP0h`
+xEG~2t$GIEMyYthpVT6dkF4vkA)EfH-aU}qIsT5n;dZxN5B?nUZ?*S7{_tqrt+f%>QyskODt9}Rw%oQ
+{Ks-H33!1{uOYy4$rM@Ln}R%2A9$$E&apW7fCe$pH*7z6Vz7$XY!y_{0_wuA?)b-GGKN<F0e-bcqgH?
+XaErQF3LM1($CmhF8E`a=QAsycYjhk9+;>LS4@9)u-ClS6K;GNkalhj@U=K#nf?;bk?pl*xYXrl|XgF
+{>RV*%JX^JVC`;wfcc!nky0yBw|#N|O0hyH-&jnKishq?R8u*@FAS>=O^9kN`XRl`7p&pKX&<O#h6K7
+&xG3cN{<y@Tz!Q(d%@*SCVR!mC!`%0wkMd<n1w4#I6dL(_I3gsebYM2b}JTZpaIO|$mNY}8DEiGz|u7
+j*h}CoVoE!<b2&G}UBnSxWJQ(PQ;!j#(&lm>Ozfs));h!iq+z66*_f&$7|QlDa1zdXw&QDx_~1_Nepk
+)M|~V{mDIigG<Ah+}Ze011RYkHRB=kjluW_3QZCU-TW=bLP4DbZsc)}lXS@y)+Jy{>2U|g8~J#&aD#l
+*`blJZ)Wh<r4E`$KSb*aL9KYH#p0X4Ex-cG_PtXpbn>(X1*6N{eNw_zpqXZfD0vG!$GK?N}L1<1X#ba
+TS6cx(`u4RB(sGM>mtF;;~c(vyFxEsRRXX8xbGtcKUiO)QruTFgR^ZDw;S3jSxL41w#`5MI6DDYwDzI
+P+sCGlOxd?!G19PwS+XXCDj?`kjK(fF>2?`k7H=e--@u8HqD<$D*B<B0FtJ{#90zUE%OqwzI~uUUu>*
+QZJv>EJN#hWKu_`vn)4;*++OKWQBC-Nbx3)%$(+h4{Yg@MSLQr<jkPG^xfB-<R{}LyP!Y=g)^0@wLw9
+YZG7le7-jEwUd6aepZg%ig~e!&uX58OK%!Sd{)Xg$jlyqWPaWe-`%@>gKa<Wi0>|)52SPWHb`!S>kwb
+3c@i$YX&mu&QoccE_5dV{&n7<mUB1D#pEmK?wsB$5P6xgMZa&$w8{nIXZ@7_N<31taFDzK2td6o!$c0
+vDqzcJ!{I6C2Prb+ee~|rSEz!&%UunUkMS;>yIVQKr7p+yR9RF!6nP%h2uS8rYYNAJbHO=H#)N~GN)(
+d}26y2%p)?h=^++sHi>_3lc7X2JhISsw#fw(B~R<gXYvBX0j8N@vxOs5==iqmO1V!<qt)3u>r-PFpI=
+&dU8FsaH&o0^OTTlTc-bkTDoMW)m3S6Z!t3U1*4Sh#7Y)6_S3to?=$P~6NOaIwIeyYkG`G}&iQLwCWp
+y^>6=WC(w~@z57<(I>NhRsSo8&lvMHqylbxRRq#2dZ%5U@c@0D(g9`4!e=^cPUG!xRMxNzMMou%PJSf
+LjnU;5lCrx|eN6ZDW$)tm1`bcve=<lu0UyhI6aE3RP4dNtnM2pd+_6nwJbT5|c%t~aV=5tc6rPFEyX3
+&Kt@R|C`(o_D6<XktSboDtJfrN>-0r8$TVuP%8bJLJkyGXECK2I``z-W!S_A{#hEmXRNSl!-Otosw%<
+1Dp2)4><QQi&jqR11g{;dscbw}nNo#3jqY>)*JJEB_NL;2YFjSK%Cxk)pvj@FO0#^>0}4ZjPN+W8ZGF
+F}B(y7<FRK}pQ$Qwnx7L?Zq&qK|j+HwHZRtt1wqU?l%G=Z2u%7Y0koh2&~oHTL>Yc=>=1H5=Qlk}DYh
+15ir?1QY-O00;o{l@>>(oN;rV1ONcU3;+N)0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow
+7a%5$6FKuFDXkl`5Wpr?IZ(?O~E^v9JR$Fi4MihSMR~#u13tE=KR((m7N`#OE6$s*@Rg^|(u#aIBGlO
+QvNxi?m-<Uf#Y1&$f6rb~*+jlPb;-dJWoA!ODPu)(d-5<5-ru*3XgU(<tN*6R<Fg+SMmrBIc(jk+y)Y
+Cs@91EH#`NqaV6D)+W>?mZ%fzyTpKx?I~hmMj?0(Q%99P1Su(h<1=*TDDL*+8rqqUADCG7wH`W$|iO+
+GH<qOapD!^1!fg9MUY!Jmy0Q<9)4CgjzT%w<`3#UT?rfA-qP-IHsY;S{kxt^Ty%2l9pD^$XSB=%C>R|
+2~kg&R~pE8Zsd}_5i)e-G!0U>&?-ZW7AgKjti4|E>dhF+00X0Sw9-<!D$)tItbylP&Wtdrr#v#uWOd}
+;QO*^mI-x))G8{^~P73rn5-R*f8`v*(D5KPi5*303U#nxYv|Hxy`xEN2MSXP7iVboaB(qosFz(8Lm1P
+o^R$gWodw%bHBBM<rbf@7y>&X?dgx<&YGXV*GuQj&OHXf@WfXc%8{4oP7Z&*RnDaGia*;*D;u&*>EsW
+P_<bPWH%AqsuROwx=gu^c6F6&`@4AD!{T<77<D{uBLZ4u{SD_~{B*^aomWeeFpuSFwa+RAq#6DH4Z+U
+VGSjfJO7B)9s9(eD-&palbtp(cR;anlxw*$DP)s+Z@thG8{aP+BI~Xc}uY;vxwv!#SvUCHJHTa2pQXT
+=}%~{MS^ii3-QKibHG@&kOanNmHw+9##+s@dSS9B^onFeN@L{Sjl}F=W^>(zmwkWNvstCz8UP9P8lwd
+xnBU0=uJ2;4O_gr6bspMl!nXds(WrlG{911iO-7KMd#K&N{p3ZuD3#DDzhpMGrO05ZYP69AxCRcr+sZ
+dLX=U^b1Z_Yq2uJJ&nXptU1+lO=Xi4nkP3rhaT|Rol!ed#{r--IgiL)`CmMzE8M(&>@eC-&u1Npbwba
+}g-W^miYMM!@l$!qFs#ivl*Myckb-WhdQSmz@{{amk7{iU=^)Zl)(kFz|0><}1@lhAwXg1k09itLN3E
+c8_b%=!K2g6Hg}o4!w#Sn_G@D|CC0j+ny?Q6+Jc9O6V5cj201R^?2yQ}}(A{`_DE=3GX253L;66s(DH
+xM^^5%9i54+|+ty=KEQ`RF15{-jRdr{bbj;ZHzu)aA&gxO+%g~^E@b{Fts)7@QYw-+2UvA*K)K~3XOB
+mSNcMm{aWAxvrmq2jb~UTsJ;@KT9%)8_NV%i2XFY>(s@8eU%MXX@H2c6ydFt8{*cp-vpvq!bv|x(6+7
+<#M~2L}d}QB>^#GpEPS&Wz7*^|s`2o!{hM3Yvs-KyG<28D2ydY9Giddx&3<`I2w?hXWtJ@0lsc~!zsQ
+y%t^|Ih2s&OeXERC1y=jQ74+2wZbu7HLuA=w>3#FtX;xcOGBx43fB)!XbN7!A8T-Rts4Mb5RGveL<XF
+~v`=DEkkiu&~5~rPV5uA*;Y=@%up%qld@gxHTD1#}C6cUb?rH%6}R0orz(W7?msi<n->-IXS$C=I81^
+si*WeP)h>@6aWAK2mtey7DvvEk2_fd006HC001}u003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvC
+QV`yP=WMy<OZDM0{XL4_KaBy;OVr6nJaCwzgVQ<<<5dF@t7>O@Lq8!kx)LwKSj0=>OfMpv+(dmR1dkx
+zxUTb%q$oK2-t$`2&mF_G>if7)uoq2D(wA;5I8jYv31<hyvcoB_hG++8Z=oi>_t4;Ba(^8AA$eAQxrJ
+RJJ2lbDTlF=#`FI*dBk{#2ATS~d*iB*~sKzpqd!z>q80QQx?aH$HeNo8bjPy@f<)+DUp2<`V}E)r&i$
+_-w1p>+KMVhWT##1qY#<tc5dn(_K5rX5#N;i#RZj(<nPet!UpoA4faQc~!!hC*(*e&Ok15?|$3i%p67
+I_B;VM2H@kex^XktrmOwK<LntRTNZqr}7#R?JE2+sa#&Y&08x}0|Kp7Rw$9%F4{>>szGNdHk#?mNuFu
+W$*9b}Vw`)bR7nZTN%K^gLsO8?63f%ylt%qtr6Q|bsLWG{;AXX4@6Ad1>0(85ZZIEBdCoO@3d)TX35w
+?;;kn_&3>7YBc4vJju94{22u&2|YftWp3*_C|ZYL0-VQ+9c?a0`L0MudD^^YAWT;Uy>u1LZ|eX6`I!F
+ke@L|)VG;28b^AqIWLPSS=`X?Rx3E*OBN$65Ssxr)hKJkg^UhTbB6x(62iAPd(IOk!V10b)$0S#B#d4
+uXeq=zl|nH=507@ssO*GK&}ED5A+SB##0wjA#C8?u8Vr!eAMVdvKidlf+KUTOz+noPlz$pd`K=CZsux
+{sems8k8yR*b9fv3CE|F62saQ)&KHPs(f3|i;}aT_arjP6;|$53+xVd_R3wboc%rQW|uw<0Al&Ggar}
+GCn5v&L@K4bG*ZSo=!1va{%|npe;9o14~SL~BENcQy!iSbZ=`msm1%rQz2k+m)v7ECtu~MV7P_y`M<(
+KS>M}u{IhK?*``m=C;>qU*#QYSd35mYXq6UPQ$<K=u$1DGP&;V^W!;gbu6Ez*V*d^DqzT)~3Hb6=7<@
+YZaBL)aHKpV0B)WnPsxjf9iUL6)c9zm^EBALP6KJue?+I6nw#Lb6W3k#dkdM$8&uGbyIWp<3a=enrZg
+uWr&TW`HTfDHXff4!yLb9Sh6*KK6Z#$0CJ*>RbA@4=0-lk%6TcY0r@&NAQNJBiyl&--_Ca*ZDti}b%x
+O9KQH000080P~d=NAcxnnoJA;0ESEe051Rl0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_
+>WppoXVq<f2Z7y(m?Of4v+DI0C=PSC3ha^>ql>mX5R32;(&N#u23vi|KG$ll1Ye$y6lCZ;W?Z5ABwFI
+}N+d?*(&F&<?q>WCW=5|XuhuhsuyG=jX>E!*z6`Nk1O|Iq>b~^oZ_K|7owp(qsxZ~_o?BDvK8?duz#h
+IV5`{<v35V+kazGWU9>$Zxj7onf}Q1e&*$b;yC$1Ga2lxPF$U-2~Y+y@Sa?(a9D@42ZTg$cZkAEG#W0
+}Zq5D3-^)n7b)ovE?>vvHDSNEZTb5!bjbu?Dg3jW{t-VG(lGHvXdZSGf_8TGoJAHk*{P=&Z01l{pBW&
+;@8QWU*HI1mu~zAyY_?I*uQ56g9uZWM(lc<-bG>Fe;As;U&d~{6{EC{IcG_<PM;vKw`?12nCFHp<|{v
+u&BH*X%net6i(=s4M=O856^AzA3c4erPI-Kv6l3`Q>V{2u0+F)!JmfL-=6bUXd=JP|-{WDznVYZ&aV)
+vpkJPycWS$4Y&Le0(8{M~z`+$qje&lfiM_8|G6`z*3*kMu3Uc0G?AI|23Aija1w=96O5f{uZIRyKju9
+zQY&ECP-!ynKfbo2=>BevvhlkoK>=s*ilmwmoi{PyW)!A`Eeu+Jy6*~!)7%Uh@mBMm5=KiQIh{}A}lF
+!U;R!*mOQLj#wS+1YQvI61wTUM#+d=+7?}SCjdioqw9K6Lx(vTU?yoOiyO)`et_hX+G&PHs`z;VlkN1
+An%5F4NcxhFp?{tx_*$zu782kn?QiUiru-798R;xeYm2S3%AI__F3mJh{D^fzd+d!=v(HmSs0}q_7wY
+Y?ZDMnIlIux{`_{}qW8KTW?N9n4gUz>6wHBs?ysTg^B{`i4m*vKR8+k@0j}AzEYq<1rp4IJ9FFK7Y;y
+H$LlJ4WTI)EvXI&3&;uKC)BF>z+efT_eV~B@O;#h>jFVf_aw;+zwi}!Y`CI0>03ljE@y>7uR$gGa~WD
+G(!;chfX0&crO5Ir&TD;$ISAmFz!x{q$K;T5(C&y4K|_FCJzC0e&e2sc$+@GU*RC-}XQ9vA$cp5GVzz
+MkI~{J!M(I_gv41Hm7R^tj*;^n6?JZ9U%>d|S^S3jR>f9}50Z&mRf?h~mrsj|6`t`Gb!76u2Y!&Pb07
+zN6=l1%IsPj|G2R@cYVB;I81ilHaFr!FShMd@1-#J%1_qOFiEcd{58!1mDy1SAxIN^H+ku((}3Cb3LC
+6KG*Ztg1@HtW(EuTdfj@Hi$#*|v?3RkTw@BUk?j;1RkTghFh@!gd6B78eNi;w7`dj9Q5-JXLWM_JxBO
+fC?9Pp+JY2ZTfQ$ZtbXnO8?h!MuGrzoqFIEA-|9a&GZjxlRU;V-g5$$C0pogq>?t1Vo2kP0?3ZT|0SK
+3KZq8SvOs8<TON(kM1QSp_i`Rd<7?$55rgK?{s6`A1c6`vGwvqr>8)i!HXTQB3!AV}RV2qoR1ghUGUU
+Wjb;WU^2{K`L|c2{uJQ2X;~{2^F0B1gX7}V0MPelMTxUc48|D6`UeSbu9=Z<;nJcI@xnCr;;$z6Y>K)
+{j3tk!e4qIBW1&pf~hei%Auq}K7$}7u@S*U8kmWkyhC!8Yr~!LE;~8$#<FD5lSN+^16kOz7|LQK3r7}
+X@gWZ}fAphG!m^v5y(831Hrvq)>p&KEQAK`aNAx+XMTzPQ3(;owmu$0vG#@TRoAMhd5vMBBWMq__3n@
+6<T;oCr#WB88ZO(4oT%*nocULKGTEelAd&#%Gf}=IDoikM2q-<lBNmNmcthk4$rc^7d^n;)jW|q~JZZ
+^3&Gd^6mT2OwtR`M50`1Aawf)}!lGZc!CGn9g<;3z|(_&7r$ag?D@IL=T=9Azka<J0qThC<;uLm_dLp
+-?!^P)HnQC=`w}6cR@n3T4YUL&2Dpe*?xD3dP463W=i(#U|_sXDF0i;S7bsafU+TC_|xeoS~4oQ)9@b
+428r|hGKTqXE{S5ag?D@IL=U1)GlQxQV&pyA|HLlJo`Hjt+5)EqtyFpjoYClrR<;TC)dRx(-bO>8dl!
+VT^o*(%M(+P#+KRcnMQB48{)F_^a`u#XSQP&^|WrY6Yhg<GdyHA#W_E0HkHt8%tkuQ-fce4vMzm`9Y~
+%^cJo)C5TkHxHzbbihQhJkkT|j%3deRs;>d0&9NP_vBfFvG3fm2dBfFt+Y&Rs1?8a<tH}r(qpKmur{`
+U>-MxXuZ`PgnK9NUeGiY&0~H=qdGDY06y)lP{Wp8cZqn&oa&M5Rv-iW+s|EOqXnI1d_#b2dPY#90mcf
+H;4g?ZtWVIqTBmvyuu>=N4<Ta%Kr_rubN!3#Ni2ZKn8Gn@JpLGlgSqCh;G3v7&dBHV^+4ZSGf66|Bwb
+bf8tyX3Bf8Hj_BgW^t^a{r7_`R#dbZ1cA_IN}`EkaCo_*abAFzD;lqHs9*3=BG6>=h|VR{Jix9`1E{1
+R&jWB2&zvS>?Ve-w_GgIjDHF|IiJYRST4z{N`wtq{sDWW+S4|_sQlqaitgi;&ct-jpE$jVhuW$8QEm<
+~Qe*eG7C#ZWCdsaVFBz->dV|f-eeWhFTk$iGZ2zi!%2}sY!o<-r<vq&6y7KLNaB5~wd6plTM#F1xFIQ
+A?ON1jFD*t19+c@~9Z&r;EfXXT1;XpNm-8j$zb_ckphUOamx9s`YCG&3z?j!S1@>Sh6CS}$88LZ;Pbi
+%9IJb3JbpgK>1S9by3wZ__)m11<9#Hq7=RLN&SlkM+bRxq;YgdAiBA<@t%&XU0PA@gxyj`O-*i<?~|y
+-Vep{;F70LJl}?wK1D3?Lr?5IliW?hSJ^JPb<F><;i0!>Zs-Qv1h6O!wjHtOkv;Kj!{(zK_$xoW1vYo
+RyKFxxi{0=x+mIJ$I~t#O5Omo*%G*!eb+-F0;aRk)=rYiIOw{k@*Qs{X#O~GGUDI0c-kV_6KbzQh*}!
++V5Z#>W;njYStir}+b!p=BJAa-2{^4TYYR%!_zm$tp_|I)}Iv20b3huxE2)ti$aq3$Vc5w>FE>7ae#o
+OjrFm1Sv{Xp)}^h1{X9o~abL_!HV*(Ts5y65b{z2ym8MLdyIFA7sv)Zc(oEVn7|vd<iJ3iKvZ0l6=i3
+;h%Vbn8Fzu&W)lA8F~UdA_@!4|rL=o6R@8!QUNn5&Ba%=e=aztMT+@*>88d^F+UDcjw6&O;JtjF@-=M
++MrimYf`->ALD}?^vW^oH|XtLM4HOY*}P|*PFgMT$LT~d6>7Q(n~Iqkf|*T7Ql>)jv8l+TeHBNhLh-S
+wm>u;3Et?9($EHH!$W$mCn+k~|Q=xEdDkP3fg~G9^kT^0G3dg2G;{Q`iO;sGrJBcHCr*JIqB#z{r!m+
+%QIFfe?$MR0%-)U#VK<sQ#Q^Q;lK;cW98*HiSqoTm+(Qe1Xe{*v~c4$Z!`ppe>tb$CnU)kJH*M?@SP=
+ct8sY6Fkwx4H+2ji$+!t0wxf3!c_+HDr5{9{*9>wJ43ArhOzJ1t87x4Q<(##9fi_JAT4K6tY@H>~051
+liy;x54R88~ka5KW*@*4eo4kI;n2)Q)PCfy!(DGXqO6lda~?-8W!<R?}F+GI7*>uU+03A=&5Pgyr{Tf
+qrTL+;8gE|J;ep5^)5L5rY<;rh6_%g>4MY31>v0oeOsJ6&3~d>`hl+d!~H~;!hfV4iN)-cGPj8Qk=j8
+2Eq|g*ZICbTda2;ZA1NIBBZ-&%@q4Zy7Qdx)&~NInKkkw79!LIY!L4Nxud+{Cy^hr{UW1Nhi@R9>s$B
+~4(9a6ixE{+tM23XDQar{vZYjp1nvRX#w@>|K_qhe$1M}i0_REPc{1F>oPJD3_JDaR?Q?)2{M7rcrW6
+^hve$5$a62-tY21iCQaEyU=EJk!uYQi=R`^YG4$FL7yowZR6O=Eau6hp@tYR6(s7o{dfrZGA)ijiZC8
+sDjO6pm>)M@Hc|hNBw`DNNO()Wp~{#z#gmc8u|}#-bX9YZ~s6QMiubHXaMQiKS^QkBnmJ7)x?28vm%Z
+?U{ymR1~c3Ifl10H+pHvL@)WPB>Bqz&j2j@F}`rAVcv*7amc@WA6huF4y_#JK74THsN5y}%2AdbK%(#
+;P)h>@6aWAK2mtey7DxIT6}Wl^006lX001}u003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`y
+P=WMy<Ob7Es?VRCb2bZ~NSVr6nJaCyB~{cqbi68&9&#Q;HJd+WM(cC$dYn*z3@B*yvcB>NV*o*^(Y9T
+SyBm86sU{`!5C<S)t2wZQEf2oRAok27y(o~Vn9(?9CCcfBF?dtG<vyVU8Ay8oavwinI?O_rQSN<4{}M
+bwpZPD0Z_{#Qg1qiHOjxzgAPmrQAHXwD4}jZ_rEwBc+Kda*D9W;gtqM>64x<buozXxI<A(IHDXmR75E
+EJ9|4j5QurB31eTU>Zx6t%r&;!{_w4NfFCOF=erm4USqGs&;F1*=)7|anig&?I<G8+G_H+=IWWxvrM`
+&HcC9MkzZBUe!~jUfT=Gu7SWRuD|$zW(2$Wd-k7C~Qwx8&!5@pH-Iu$3Q-*SaKxrf%6;m6_?m}@+S}x
+2BBL7Gmxu%fCr1)Iu?0hg~h{f~wQo(;E=VGz3OY3-!d~I5WtCil8d^em@pKGK-cRc0_$B);KkqF`37a
+@-|C#ET}YkIk-^4138=Yi0z1bj;WN8$qe_H0{ztk7knwLL9ItRq*dGGpUMxfAPHL(m(FP%gFgY@DQEp
+Xr=LoWd?q9Q*@7B>IB(qeo6_%@^ya4g}cJAHB)_XgZ<x@PYnldtQ4udH4uhlmfdbaE2sSNhAP_tQ3pQ
+27v=$;CkJAc(gmcesA(%qrdG<hOY0^?Z~4xjoaR&*PZs;9*w8oc;vec6qoZY#g@!dB=0C*0CFY4B!+~
+ENN1@(pu8FZMsr%SXO1$598)zXh7L(KKUBj=#!qR!aM=_3k;H;xY3lT%1bPSUS*k8j_Vuk}D#Avcu39
+i+@s|iC@Zo+d7J$Buq*Qh4NNud?pbg*VWvkVE*ZR5HBAWVGIrk%Xc=Mg&bKy9sl$|HN(au6)ej1dogc
+y8-+HKc!`AL*~1VOn`7mGeO<8*OrC|8MXq0Hh%u_Tac?l^vXFz&m7-}|>q*YxWzj^7E~;b?kyA9!v*n
+B04=e?RKqV6)Y{{KY}}dz0S3;Bi0lCf(`e$fVi$aODi$yY_1)%`3-$7<5Mcpxd4VBk#ubuzq>fbcWMG
+$Mu5IZQ#5Aw7Xb8I5Jx(8@(fl+V0fMs_8gR7%{EM7x7cXI~&7~c&Q#eLDZmFXwU@(fq*myK~?i;QMV$
+)qdL0N&Mlu`-?GTgc+6HjbG;tQnCJfXcZAzGLaXhJ>{BCH4J<gKO%K^^LKeKKYn%6lQ_L|E=-fO2JI#
+=agbB43h}4<ToL|;S@c=Im8)s}YI5=jMiW4S^t-py#SPjZM$&Rc|5sZYw%2g3~xXT~VdoqsA6y74HT6
+1>XW(z45=LkD6If&HuIh1Lhy+Bb{lP<B)6weG+ee8}>Q`=Qn#ciK|?PB8LsA2HAIisJ-Jymp?NW&s{>
+a?{^ZQ3l3HZS%O>P){E4C0NV+4q8GR*a!q(3vM*M1(|r-2v{{d*UX@2R^Wr7l}l}l&Z7ewLitpTE%6n
+MhSXPK1aX5hLZO1=SuEvF)Oou%nP({K1r;zE<_7$E}(qjJaI9vcpdk-0d<wx@!1vxtfXEG5y2wJ-*W-
+a{DLTT)YbI7oipp>EqF~4j|&@l9CrVFnwB33uQ?>f&PZtMY4MI-W!`>eiY78`%>hd)v(H-@u7<VQkw0
+R}AbzK!R30C6vJ2lrtIxvi_5Li<uKP+rIaq1X{s(R`%5;!Uf*%y5pkSv_Q+p6yErs;cVMsN4n=x<}WR
+)xNam5mQDVg&)<O7C#**DZo%uZsYQ(}jHtJ7AQcY<Xf^0wTnc0sRCCM8d2Qq1))ORB7xpHs+Lxp*hEH
+zT~HP6wkz*Vp?SO@rU0-%t?6--_K^YMxJY=AKe+lW?aOsmRHAA=2azCi&nbSbw5dE;*;$&^qq&2<H58
+{dB%xajR=+18!6!y_Z%iZ#3=WLbPvm-M;$MYcZ>w1uFe2FCxYj`9E=m-h97T^%WtngOFO8s5FhdIczE
+EsE+A{x@SsWJjw|EonE;j{%r(5;<NV!*Q+O7{h#@du#B%3WkP&enI&#3%caC8%p3iGX1)IXP_NPX!^I
+m)GvNEFJ@*nkzLe~Ts&FMy*^R$dQi0=PZWi9WR{W~1Mibh1LW2%J2xc{UUqURW{Jx-ptH~x2A&bUR3t
+N8Ae*aZwwNe;rAHwdn_`dYYE$Wq`PY}pPv)rQ@As;>z30BUZP)h>@6aWAK2mtey7DuGq<-a5Z007$z0
+01}u003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<Ob7Et2XL4_KaBy;OVr6nJaCxOx
+ZExZ@5dO}u7^z=CVsnHQz13Cs0a&1Dfg%E`s?`da#35FVjcljg+^@fJ5=v6S6?&GC$jm&?ygRwMsXov
+fjK(vVjQfL`H-O&cq5lKU*xuA`U>Q+(P;AQt;n0@>1*Tyt|6!aHSPAw*l}3#p5v8etfEwx>slZ25r*>
+c%3uYLaeWx#!%RN;fLogN8Xg{My`(#g1wA;mk`NS|OG(M`mROtf}!(6IN?kh?R4PcX`jHj-MvRwNFT}
+1}!{RXsKEf*;&%^m1+4hw6mVL>%jFEq$9=}Tc0+r*e(T{r$h3BZ)7XP7g-Rcr@e03$SDB+L^NNs(H3Q
+G!3hrPa%~eA5c$1;UbYwoybSwz{FB6toP@D@J|?iHyM~0u&9H&irGg3=k3gAr<0xGGJk1MX?AlUz?Vp
+YNroH9?e!Tp&C<x5fxOS`+2<K%t!Eq`BZ2MM8n?7baX7`&<5h=fxu8AeOmrIpbYiaw{1U4pzXK^Z#iQ
+VbA>uFHhygP-U1sKbOIdPr4C4^vINIW17IShjj%cR2MICJR~$dsP>3}RW8Optw1vm<^8R79gzoGK9=n
+T0ceZ@GLtAVGYO%o?6Wi@MLt@NI5n&Px90{g_MgJZV-QIXIUOw6AhvVgJ;CV27SU?x%-Nka;Uro9Tn6
+DP|2XEkDb16NP*cP*@$ZsVMk#Z-olDH&<ah;|9gzeQBAP*oSFBIGCQ{1Wm2u{d8xvGa;ifx)NLXHK!0
+~UgirU|c#;q2geo^%&kj{R-oR`^a6IxZR!@yxLW9>Rw#MCu`zQZ=C`wXvqtE@E44*KK`qzqVX}m4}jZ
+2L`k67hXO$wOXifX`K9S*(w?D_eyTC1lU5mXnoXp)QqfVo@#Q4-sGUNy~o6s(_Ug|jx2#V5nCT`3653
+8oud2zt3NW~wmWV=S5B?62|pzJrn3Jv_~1;iu%h#hU`qV5<U^YAX7}y(Hiz4XPA7e66jIn?U!P0yr_T
+QzS$Pi468d@+74VywZ;4_=oN{z}2VIHBEsG0WQ7q1JweJtQ5?8A%E^y)Qvl7j6?A^9oOBA2Dr5i<GOU
+4;{=RVaG?K9i*oMcw{aMWr(Ct8Eo>wASstDg=Zz7lMc0vf`4&G5ytUe`6{VJ^4l6y%gm-z@Euv)*M!8
+vcf#rRg{`%9W$qXkG+8&hv$UilM#``F`>*gcZjB3dCVYu1Kn|EEmE~HtMPxmKW$Mm+q&qRV#Rv+t#PB
+?^d>e`t|lNDl8w4eQ_7-$MxWHi(iKPzHI#8F&DT0<GZ%{3<<a2HpL*1LA@{?|DTv(sbc!c$f_eMN*#P
+3uG33#on3djUQxwnMHc67C5QetR0Q>(QHh|6^`oRto&IXn%5D{Yj2j$*e*sWS0|XQR000O8^OY7ymtp
+W${R992v<?6OGXMYpaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4yiX>)LLZ(?O~E^
+v9JR&8(END%(cuNWyGYE<fjRwrE><s?E#!W|HViK?g<p~W7<UhG}8yH4W%`kS>4IJQaC-hP64d1mIB*
+;${QG#@x0UX7+O9u0<5ZwTk(o5A03#P+0h0*ej8jpA!ASOfzZAaD(_{LZ6@xl*mcCz;#YnT`ZEoGdT!
+5hJ-p1u_KFfI;?CG}>odBvBkEg8R&HDK!06TdA@?1Pn8&a=EXN84O^RW{CArOj)dLN})CeoIx9UpFjU
+W5RK+8^rHyo)>gwDHL6Dp@=69$7{ynKk;>8SFC+n&F!ca49<3FR;R8^H28@JRYBo|7^%FG_Un!<i%Tl
+NiLCerQQDUc%$^?8SKw-dj4o^fFU?TWUD)NtIz{Avv5)n{2Hfw__);kWbrnfLgO_{<K3RG0hELla|C+
+9KuQD_9FVQXc2b71PyCgK%|z@;SkjNMZ}PI?>r5tSwhd~|!eXa!;iqymQ-n?H4PYaQE^bP5r5L+w1>M
+hXs<2EauIyCLrB2LV!{Pc%lbLP#`*Nz@?(vW2gs#r4hY0{YWC_}ZV(`_skUDcKU!q^17lNIc#~oPen+
+#e_*IaRQhO=YwnV=%0_qqs5)g{&KXK4m}SpZ|2a4S%1D54Q|K%Im~Y7vm0;d0(gi!iY=K<B=0E>2{M+
+%WPpb8Nav;BQF}Ed7zMClk4SCyk<&y0qbssaf2xO(6zi;Baybxs3OocMO$VM7r`e(Dt96%94*l)W(ED
+x&e(#YH6Aux!z$5od9uo9rB&F)Wxzxs*PWt5A{n+buKlDCzdjPi{Nsb*DPA}fLL{3^Q>Xf}MI@PUE$r
+#*t<!_kMXtqkOh>F5sl0=4|rv?Y4_0Y^RZQ(<UhKsc}G<7laH(cP(ZIEd^tC{4QoeRP2N6?OU;o$u>@
+-9m9J}stH^IdlTZhc{%%f@yg;IpaNQ59OpWjk*&q4gFO`klt7?PX{}b*t6#Bc^p`n-}La*Gx-?b`DKg
+2+Jj>)wNtY8l$j~+tcjmzBJ!k+kfsdE~uKoSp#cMsf3zmjj!+2x3i;EIt`%@93Fft%vRkL?PWv4JsY{
+;ZB&$Oe~?T)&Kk|Kn9-$RF;+t=G>H<&;A-9_<Pb5)&^o}*lQJe7(`Gw^2!)er_6oakxLu%hi`VFMJDF
++8(;olOPXej*+ZB}FGP^t(_BVdte&J24<psdmVQO6*Dkas+XnUWX(K5hQvaTCtq^H`|hTb&gW~(bJzl
+o$K=h}G>@1WQl8+t>~CI|GMtY`p8^&V&%9sYK(f5f}Galzk?&Jr0@X(m{+U(v_O!wV@!uW~vvdV+Pjj
+>paR`pEqM$Rtyy9@(}cAHeGDWQ|IcLXkJj4rrY*ipg!H?vWYDLB~uRdrQCd?kQ3>irxFy9I97qe|^ee
+AL)y%$p6cI=WU&@m)<-wuk63ZWID&;FwzYz|535|eWgk5m8!J=KmBx}+1Z2Kzmn>kDlano0KF`q-Y-y
+}oqZP7HN9_ecRwxJlB9_D2V<JN{X)rCT)Q&z7iiEoG|gYCOV2sB!KGQEkjZ+pq>m})*iYV|T3u<6NfG
+h@g>Pg3Sqkl4-^>?-+r@HmJs;Aa@kP7+E4RM!;VBhddASbhRNgJpm}fHx%h$Z&?JAYCcP+l8%D#=GaT
+K6PJ!a3&E{)CswcYv;P)h>@6aWAK2mtey7Dug|Hab@a006ie0024w003}la4%nWWo~3|axZdaadl;Lb
+aO9oVPk7yXJvCQV`yP=WMy<Ob#7^PWpZ<2Y-DA0Wn*-2axQRrwOD;`;y4rkpHDHj)rz18atZWStFF2d
+P|~8K6am#etF}TRF~!;tN4B$F?(MVRI3Yk1L%Z#r)k<K`{5<b7=Hx`bpwqkT4`I;n_J)%lbOtxwU*Q1
+lN#z8lO9D5XKGM()pv!y$RKPX+od$smw;}yTxWE%{>2g6N_(T#<G7cV+nw1QpA5uw??1FqF0gDI+wg9
+PtM*1O<!gC{nTdP$ZQqPr?g#y3wi1FkL0>g;$bl>B|mBfenIzcR7<#L4;t?{a*1fyGnre)a(p(Qt=9R
+x5|v;xLN5dMw$sU}?(N>1l7>Su`B8SVgFyZkGRXz<AC3L1bCN{|djYq?}$LNQsc@!t)YI%ivb6ANt&g
+eDE>oV$Fjth?ZZfM5&xgpz-PHH*P>L*T@xBE26iCBY5-pBYE~74zw0t+wLOM}3uAlJJ$tjC?t~g#i($
+3S5$qaJ+sL&jacq^MHCJ6a-uWk=hf>ohcn9$Ydi3I1J$v^M3+D@m$@l+>blZH0|uR4PqU21;dp}esp)
+F7;7l>8Ul0|&p1u333i(LK*I!fiO#_f2#AV4VfewEKrF~24(f=2wD4PhdUbO<h4%0Z{MH_i+r#OXPe_
+Zdz*BT^ibPjYKoJ<V;%+F{C^!OK_r~2T<Y;&LgZ}i3lHTc0hrP)JoSQMUVbmT^``z0?dkmx7@#tpKGt
+s$(WF}U|)ERke;srvk7+MmO#HE2qRsVwS6)0ff!_xgm(9Isftn$Id2#MA|<imi4kBPp>vNPx>pbH3@t
+iuzh7&{oACGR52&cAic3eT*=FE$dn;nx6NFhO>QE)d!Y7~^&5Fd-G`bsKrDrfpjdyJgt`w-eksw4gV<
+cwzB5sZ<smlg3mIQ)%U4wo7!FsBAXGdizl&lP5L|7+^HCBhKb%tqs8-*&C&MaDr9}efA_8cJr6!hHW+
+X#3N^Ml;^zebSIZ;d&j(r14$e6H5yYDKG#zLjpZ*BK<JSsWh6nv$ABDxBErjq^n}+%!U>T(l>X$V(X^
+Y*IuPlZnN85EmFxbne|>vBn_i83ldGG-1)PIrHmwR7#hkQ-N2%JW5QNRX*%fky<u8%x9GaC%#S2^^cG
+do3Q{>MoSl%$27ciSqtfRA;AxN;OrTc2BujXutXdNmuHU#Cw^w=t*0Ws#)yZi4St;YR#$9bsM3aqI9A
+vWXQYPJ6S`;S_QX;re(ISg4yiexz&fkbx?c@mE0UmCT_R?l=przD<WP$0%1RT5$}qF<|L$*py$V&pEU
+kNX7oEYtcc&+8)<ayo7xN-|EQ%N6XD$ECANCNEs+9%Ai>Q7n-^apTV4MY4e_q_PAO&8yYzDb|bw*4$}
++#N=DsWpt|gWsw}}J&*^E@;F-`EC}%v7u3P+#QToOL{|39V{sy59#*z{?f`qiIRqp$5{!Katdz<5afA
+(F-V=Ei%*(WP`^rf|IZ~w6xpQ-k#hAd?kUgn(hPC>3>g1RaUrBa%)_Q>R^G$s-k#UBUAM+Qbm-QUzbf
+Dva!fJfn-c%wz60h$P*2biN`K}nY_I}u^MS`u2ry3bs&Do%*lhbj)>49abzeLm))~$d_iFJ^KKE+YzH
+Bz^lgWkU(bVn<6DlW?HI6I-|^zm&HcU~j$yo#l=r^@C1cgs@$Rjrv5Su-<tCymoO&WEvxadKeTWh72G
+YFb3e!DyY@&DwJcl+M-4Y4!hx>O{8n;q*fzxw4^v-_`Y;MZHT@FEB)EfYcaHPiu&c>yoH@)(}nmwDq4
+DLIMB#hG-UJ_*lv%ach6bk`3Mvy7q@3OtzAs`ZU{1FeNA^*xRH$lwgNDX|B&^4JEzfZg;_hdVPVBJKV
+eF0Y0lXU(<BTO|Q>gqn4(>XxjLGA3rPh3+=FF3gU8aX%t#zLI3=ykWGJOXBx&cLa7&?uZ{B3JeNsU97
+Fz>w)K|^n+>idpYF{yNenjZvxgVhH^VELcdy+apNRHk&<vu*Eu*IQV|k)V2A{+>X)bMELncXHZhd(`3
+Vnhcy5y0_+2+TbS?I2aQPUT#y_L_eqrh82qxP3xT3vo5p{Ms&7OKa*b!^Do^HupZP<Z*FDT+_H7g%Sv
+_Mf0e?IFkb!_)Cb&FHnEu~l3j9?`3#L9Kj5L@y11txSiCWVYs<xL<cK0xw{K6klb}J_hy(pPS^7WM|W
+vPv-HXCXCj?6cQ&1ZYIC87}b!aqks03&QJGYwRTLDlJc)^2S;36brpH_+3uanwvY4zarF6^MnHFaUGY
+7x*2?`pDwA1-X=qEOgLrKcDfdSQ3b|TnC(K+;ENb*R5bTF@caQtC&Io#u^wugO9%tP$6k~lykxV(dyF
+Nqdm!(4uTOWm6(6@J-0@TqG^Gr}5&vI>sN1J}J#CQCPhIAFL@X?Rh%Owl^QpUd^M(?Z#bM>FZYgvK!0
+=)eQdM{97c>jEnH{R4C4rEEKR`~}|O9KQH000080P~d=M_>COYaRpu09*<H05bpp0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppofbY?hka&KZ~axQRrrB>T+<2Dd|*H;WSFLvvwb{1PCn`Qy
+qu@j@dL6Wh^lORhYn+Qc3B$d?t`VJ-eCOeC6t0!x8&dixJLnc>Ommla2o<>s`kNShDKY-qN*8dYez+O
+355UdeqimjN4IP_(Lz%*p?Z^n7#O0|NRVy?4;js!D|kdOF=Ty9W-Ou<~pLHH>e9Y-6aP?qI_#gSoBXn
+Iu}sfrg$40EZ<c&soon80#dWIQhwDVuAvrBZ7HjeZk4cXzjx;v(FIF6ZD`tOgG?sy9r^PWn<9#g@5|s
+-c@-DFQHw)GN#xUn!QsHIPCBM#6k+)>2gclQo<zRiw7orBoq;mZ^CsVfV0=ImD3wg$dJTdNRU5B$D5x
+B7P<lmTqk*7YVgvRU1^9-g)>mUBDPMDTOB#sHmHHzT_+>@|eXaG(x0dV`F-KaB66U_>~YCN=je2dk@I
+yyRH3(N>c=G+|I6Am9Yg<K_fC&KTUIEiA@r{1y0jYJEnWlf<vbXFj1skllSz45>lh@v_`N*$Tg-pZ&3
+!q!uL_|JX-|NoxZ^LuIF{9!OJ~i$!YpZ^D8s4Y{MBPrmj>Z%$9^xg2}+^KNF+d8;wW7i&Z}y1=E4=!*
+J$77v^0r81)z9t_SmlH=p?f7r;l{S!~U`Wb)qPlu~AroJ`O}jO()X7aFf7fjohA^oBI%7#VF8M07+p+
+n<^tmts})OO%75_rOvR(zM`RG1?v4zO(64mcx8owDhstf-fBcMdFpy2z;UsSxTvgTuRk~p47%dCtc#U
+Z#td!b?0-t1F-NZ^3el>>Epk=M6MhsRWbwD4$PjKm=3d{f$1t9uOp?=Y?NG*D;20uK_AV$2=0J%K*Wl
+;2D2z$Gl6?_@x`}aCgqokk}YGcvmI;^3~&9(gl-`2IJ4;>q)qX{xi{;<1GL>Qj^o5U(z*bar5h(g=q{
+kEfCisJ7&5w1!my!{r&ZiOCwTW1wFlWZH_R&86RLjRLE}hmjj8CiGL5FQ7gGj)sN@Lpk&J!b+V^Rm&C
+*HF=CeK>%C69a)$~FUWms3B(Bw+Mr`)91x1SEY6fr5VEr@#gF~gp%*j^PjehAwqJ~%`w>e2=NWx`@L8
+gcW4M~CKw`fyl47Y9m7bz0}B@m;Ya!c>|Gp5d4Dr|I=1iu{LlM@CQZXX+NGdSw28Wr`_tUYR&`ON;5|
+N}(vH%uzI&@a+L^&gn<ukGJL52k1CXy@pL(j;dJ`_lO2#L*>8LN4O^vI%Nrp*s{MJk^SeH7xWiF7(9C
+edKo|d7k}%!TNhkg{)3p>>gR&9S1hiTySp<7PF?x~em|9JPXV*1fNt#-HrlNVtmb)l`<m5{U*P19JHc
+1&b=)@nGgIRC-;wLuZdT|D_YY7@0|XQR000O8^OY7yc()cWf&%~m2M7QFF#rGnaA|NaUv_0~WN&gWa%
+FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR9XX>MtBUtcb8d4*JMkJ~m3{_bBv<Zwug<%D$`FyMd#)^*+Pf
+VK#FDfUrNY&sE6mRw0r>izmrk`>!SyZS>AisZxBhpW}9=&e%n@H5=O{$x?qcTO}2mC+h2FO7!I7$x-)
+gs#EjUGGR>#fn@D&PDG}#&Ql1uzsy;iv#S?TXTkuh$8Ev_?tV4f6Rqx^yQlTl&nxYOpfoL_TlY})=|L
+p_KLmwk^QnRilVWm1vVuV@&Va)#(LNkK;Pqjzk74l??}JBzCV9WF@f=b&Z!4QnImOv{8C{TY}}}5q{7
+ZPe>KpRsgbw6^0E`w^Tyb=7G8|@A<<T7aYUP4LJIHG(-wBtd>O7%7o;hj1Yyy2lmKS`P|?TnZ8Yh<RA
+l$L$0@T`ydXc(FsY++GXl;gNA&ze#mv=SwrKh*%07w9INlCf|1k(Opc{Bb4>dM|IswWYk6~R-G=D{vd
+jsNZWDTni=K9!}Ue_zQ2&>7~v0{WAoeKiK2e^UVK<lO9C-&<Pad0t}9AWhN&@1(raEmlEc#4|0)*5@C
+2Cjs2bIZ*L!bZ7FcHyH!09)xd4pp<Im9LDgXZrAU1XmOpgL?Un{t_zZTC}v)B@=a>L(RjMv}Pdysk!q
+sAInv8Rd`1AkRktg`}?QA#-M>}ah*|hv3C|e_p*9XLx73YglA9OZ6Bwe`x8P!X7I^j)2rZEqyi6pic&
+<G=Ga^9qDvl-Xngoh@Y1tIA~$p@q=Ob|I5lX*+J%q@T2oCZq~cs^={aZJ5H!@Fh@RU+1hmvwKcZ3P8e
+{|c6cGTu)W%{B7fFDcj*lS#OO3Byh%YIhR3BZ7{~1Iy%vDX6i+*huR?MOQlb9^LpY6%?vcBC2R|T$=4
+sO%jLa->c=p5x&x^-F3a-6Tj7-pfSknaLcLR+Zfv^MM4bxqw6ke23aEKBzHaO>7^3(E%BIO1#_D9adw
+HwuM@zGJkKYQdu-6EZV2lDz7z^uyojY3)F$ix?an4Vb2Z!;o(PE3U+LgrkryXY5>ZSZ`;ymHh{H3!>|
+2_z!#AV@YA3=+uz~?v(K1z)s<7qt_nJf-cel{RL@>#yjY?;0#<4UJyH4-|r?i>X(^)Bl31}38rt6cO-
+c&oj54uIq0Sm9*@Qj53?xYq>sqo=X*kolvnD?m+6eo-M>+)JZd5qRhpA2i+=%7O9KQH000080P~d=M;
+Ssahs_KC0M;k~05t#r0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvaV{dG1Wn*+{Z
+*FrgaCx0tZExH*68`RAK{yDcP1i!5eh6S-gUhAOHE5d)()2^&8f&h^wOMN=Rn&TSd$|98&kRY4dhy!6
+{SsLk4(E+C&kVD!%Z*6OJXdL>%fbk~smi($zsRc6#m!v&vTfulSMKkVraoMp2>jNxu(_?3%<$eX`|@*
+{-kqp@s;WkO<aa;Rby;5s@j`sgWwS2p&6%lGs@FQb5T8oN!w7jVbxvC``!tuvSax<l`ekY@mP=JMs$M
+Q*qw@8v`$)eEi%y2*d)+wdxqxKL=1?j7bT9KQ9-CUGY9-UVNp7q%i+^Zt%R;<P-h9(pV8V3&t}|7u?0
+cOyQ<mt~CKdLSH_9ZXU9!xuW{S;#+~~ch(y+yyI+!^>u|jzM{QGwhe7Tf)zFdlhxQxE26n4uGzZsRiF
+N;R%Le<e+L_dE0`uRsVK-IKUbVchvZSx$f%GIAxEuP_(bH@~uxE~?ThNq{e{O{+wyw{n6d!3`tj9e>0
+ik{{APQieVkJ#Pn^j2)}2fZ+2TZyJ*CfC;+xi|W6b$u;JSt899F)Vi!d~zob_)CdgNz<IwPRz3{<g^P
+`>QENhkXd45<)V_c+z9u&i-BL{UT?M=QEWFW$XBj~<t$XLHu%>-KD;7n`<yaus?w<CdRwF&yC3dV4Ke
+jb30aW4HbNVr?qLdu>CRJNka@fE`a0wovzKSE1(v69vC0x5-s6E{*qL#s;ldl$ID*zE{LuRDeQx0AFw
+#!CgQJV!;5g$f&cB`a)LVQi3l*FjqE42M8ob?8|9i*)G~u5o15RCdEbN<5c7$e+I!Cqu9cK*++fEl)f
+(+iOgP;h6eu2n7L&zx{C~^UjLS;lQWwwUgOs`duQ4sJWc@2kN<Park8SQj)+lkW8ChYLVP_3jYtC*ze
+(GG|%L2!k&#G*%s;M>+VmO^dKSlhX<QPauf0?#HvFy~YMBu$VCOJnpZKUgN#B71EbvX|utJ4M(w>Ma~
+@IkW}56tk=>%acsPtWE~veO!wj<(RC98Yd!7TwlUDk!<wMZ6j{wy&@mC$_&_XRuMj{2+k_(&GyW$)Qv
+6*;_c$2)2SZ=YcMO~fi5%gwtsH==BD40&GMN25KAOijAY#`KFHjtS!WvTJwALAA>HSSoKlpH^}xm01H
+DAlTiH8rnaYoxw`0L|L^pd*jw<w+#A8X}k1>`wCZIcWQ-17a&q<I{OKlWTPXk0?i-^h?7R1p<ERdX2N
+)!r25oLESyLi|ZR4fsE2-B7Fm<g3MiGp2IL~8vUU0$FRR0mO&nlKgDGn5PAVFV(%!1_vYOm7?n9C?ds
+NRG1=cMxP`nxls~v4TI#w*ZnZR&?RS7$2L*1Ax%M|KokHZo9s(EI<@N9G{7~$fZpZq-IBOGbFef5V(`
+n5wu6k5x8wlN8#VJB-j+X%5C0wqV7!#?)pG5Cb9vRbLi%li2`fC;|f$02&DYC)+*1=tZyo*Ym=afKmw
+(-;zSc0?5uGDEU_r$Mw!n36n1I3!FI^BsWi4Y7e+Vw9*3Y0w(fSaz6qttGEK)w=3_}Hz$-pd1w9h3f-
+$yLCYufpzH{#Zum%W~V$L*WO^Ke&C9J)STlk^VML={Wg#wggk7z@((C1vsB2^rG%<apE_u|d#@4ih$m
+l-OLrs+Jcp-^KPw(KaL5xTG+$*N>(c@1}EYdEQYGo;S-br74-+#I0vo>S4T##>{JNjuBNt0UB3TbWPp
+VD;j@hAiz-eQVkXCaQEIK0wTXv*i_XM`Kf%26h%Ha$W=MEDE&)pMJbTWU@1AGd?9U%dW4P7q*llFU!h
+l3uw@fJ*QE--;^fwk-`T4vry293Bl@Z3NJ%Z+SUis+<^iB3ym+Fzj53ULpB;rxltCOuPm#)G>sGR?c2
+BhgyFCb4&`h}LYl_Wuc|qFUzT@TQAf8f%f(98(F4Tr@>$vvujJp0zqb4RnEEoJz(<8TL#exIZX<$Q(V
+J*Y_v;T|J;y8l>P47~mi>6ud&m7}$K9@U{M%M#q7`u*xbv8cSFdaW2=cc}gTbP%`k=6Q>pG-EYz9buj
+c*inbRc6#J|FmE_CU2k_N@*}LWX{2zw{}fvImx`Jq(hKmqDIkk{hmZHh4-C5TX%V%zCA&qek`Hp}n8Y
+Mcl4^MpFfH>MFu2H|9MX%0e-j{0TflmHP7h;_4Z&vZszCo!T;B*p<r1IeOYhSgOBTW+l=<9k7EVDdK|
+QVv4z+w#_&@5Xr4HvT17nomwdq%_gA;<NU|BpsANV3j;R%9iHk4?2?gd$Ov(KcRuIHySzKUnsJr6JEt
+tmE5%~r$zD74FYjJ^y4PG1EbqWzg?i9nZfDZS9xiL$kVlI^`(voYzGo5|gN(l8C4aOzd;HzLXOfe^u%
+Cbb?jo24c6N7IaLmYm>5)Dpb@$C`w?s>b)pc(g=S@c~QE;{$pj-GgfgPhL`rdX>m-7tnID0GZ=dkVu^
+<B7WtR0nc1Uj?ZMz*dM@N<#1opadIeq;pgQ|Y=yor56sx8*^d@><-Wj26zjJ8FP_QU~sOn2=hcED2=|
+1~V3&kR*Le2}1LXi877Ooci*$)VUGaR{42NRqK25tu;q7;tF)43t1m*!GL+huG|8_sXg@zb`nH=5kW~
+?>9?>&$miD*eP9!jwz{~d(I71XepPu#){e~mFKlsnj?v9z?6}yQXH=Fviig?Ud9b>T3kq9xDqVQk-)=
+doN64HLq6I91nG1y>`m0pd*<=D8=|0W;sNUUBpLQb*^I$;(YykYHVRK9{+z-7N?jkNrWoRPQf?D>LYa
+-V)=#x41)rUYX^m!iM)9)xcBh(Xh$ZJ4v(btEvbg*lh>XNWi)LAC-Fe(a_Y@cC+B^Mz`6BMNOJD@U6X
+N>*K_6WlFC#5QP$~PMvC4#;}-T`5kcED93-YKdj;6Kg4@NZ>De_d=j_QOuIbCMNs-pbMgHm!gzh0ybG
+M$yvjq$RSWT7^_2xfKFDr%#gP1>)Ws0ZexU(h;*c_wW-aj>m^C5D=<}6&!!Eh7mWty?4q^4kdFM@u5u
+;BrAev6s1o<fiJ8v%`K;)6|ru%72kAF{?v&|pczsf)UysUB^*;vF#4oyvjWg^@5kDXSIP2nNnUSjuKt
+W74YKKY@HmYPy-zPm;LC5XII_&;xqS#hn**qJ<tsv|lb9Vr{V#<cTc`eJt4;7Es4NZ)P-wnjPl~-gvP
+H4WweK=n(QOKJHatpOPuKNcbLuAl?Bj_c5YBDegO+Qbc)LMPph6hgG$3Rp#anScKyEw*{0e>S?C;bWX
+(k$ZN5g|gY|s&kb2@E)m~lGtP53*-R$F^1760eIw&KoC6S{7dP29>70B(=_=si3*5P5;kDM&?-OhWC(
+0P|>j#Q4u8pcVx(+F`554|P+NGuu2L)T~Jf`Zth+t=nB6+sUC;l&8Ul15AEOpzidM%@UpuKC0dHp2HL
+o8qv-@pQKPY@|<R4Ph&Faiidg8=?04CQRr0d$9ed|fWf#FNvDU#%Wc&)EDQZf)jG2SnMAa!x^$#ARdc
+XRw7p>=EBk`ZWjYDZ-xTID&uK@GA`*v#F0YO_YpUXK0-gv4&(G!1KIob*6&(~a)FoSwyb%ACDLE4mh=
+*v6UTL(!2e$KV(^R=$`Ie}IG0utp16+ihk>27cP19)GQ2AFuD_5&(>e9o?div7DFU{;!ycBWQY$BDC^
+OwuH?HoDYC^2kQeWQ>Qd);+6T$yedk017_k%T+Vqbt~v|Be8L?@NlpVpwv?+h#;=h2T>&f&~v$TbKS?
+GmsUAMox1A1-&+Y*8i9ctT$@#nX22R%g%$XaM0{XTG^{AI@2~%_&S{aFG`W-TkaS$7(u5@iZ8u08u*G
+fc+Pq4Tg_bsxr-n^4`VMKcsL)zmA<Af+@7o|w4^0t9T5V2y6R^s`&3<9HCT&d)3Qm|h1XZ@NppGMt^E
+gI2W_B2nYGL)XqGO4#A(jT8zXO!&8DUyocaNmeE#5za@o@PROFd>7#AAsg{YYNb@GPuJOh8%nG?`2E4
+bs`=a2THV^5Hge8Izgw{5^nt=Br$e1D&auXMHA!pmBn+5do6%bkti?q1)^7jUwo+Kcl&{m-$+-o}I*J
+MrkI$}j9PX;3vpFFa^(iVwGGGP-A@AE7k*FDJ-Lbz=ym#&N`P@JAC3M|<=OD*d8uGSAMhMufpPPzL2*
+d;{k7A4P-*-n%wUt*dx&r%OK?k?LX_jLjy+uo@r&nmD?E?B{(>ZNqGvr$Cg%?;OCHHhGzDa{fP1O9KQ
+H000080P~d=M^e;8_>~I)05c;105bpp0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVly
+veZ*Fd7V{~b6ZZ2?n#aerF+cpyaU!MY(Oh;6z(2kupt)`wE*H(I-y0*_&n(5>^G(<uYYl>h2(6X-6?|
+!>@kN_#i$vx&YnVJNF-No*2A6PVBS6nEPQN>igW`o?k%dKo4d3wjxU?KS0J`?)|7w}pUD#?xhFf#*g<
+%WtfFPGA+jVqeo(It~^xy&C110}W-LY|YWty=Mt91aNl^!$}lRh$+%D;tJ?o6W9Hcv-Sk<uDoeCgIRn
+PnkZXtism;(@*3~FP{p*#Ux}<<nw=<_+XG0R7!GMuh-jlTEyqS!PCfadiGATj3`brreKs%vY>eZL+yf
+-!l@*Frq@(Jb<nEjgs&^TWRt5Exw$$2tDc3z+;{>Xbp_G0oUUb|U%#i40R^oJSOLSadv(f+JZ+81u~H
+(R*NW-)k+D@l)q;!lOIfirU*ze8TyPXD);xeHkiOt08|ZhVC7*MIFO4FtrgrSyfx=994m44LC4b4QG1
+;)8V1#BFK#ZtWE>X*B#YjolOaj;#z?I~Rq_iY+W;MYwBV?;4r;E;rQ^y5RGsy8II+%Dn>nw~uvN7Wb%
+uQ%qA@XIiUStfQKiq7kVrv|KZ(Pa_v$Phu+BSouj;F<7ieybyx=L1DDpZW)7>+v0YLypyQ7D*Tk{9<Z
+NtsZIV86?8FpLZCnULrs4gtwV&?+(U!VH22aQOlxWug*!sZd~_vtydJ5FX$_GT$aLT``dUi2R|64bgd
+r6Y|aexP6x}@cQ-Pc<|?7a6LVlUd)oy<JlAzx$qX_Lv*nJ>h+ksf`6mNi9=V9dafTj^|NP<H)RCyU_(
+jVi6P)Oq!Ro-&mign3fY4a6t^dps8a<XLn?Ey7KF-7zD&W>smkYh0ahgi-7*2lRU5`i!ix;vXql0cZQ
+#iuQO3r{j=>F(B`wGu+itkX0CEB>tH3UVf}_+n4gvQ?zO2!8oNG(M7us`L#TzF%P7$aqpp}<tQD<y2u
+-JY{Zf{{bd8%%2Cjd#NDP=sZ*ATg>)&u;TXf;o6Yf*rr4&#<p#d*<D+!YMn`t*4%QkH<P1LUOPqck7$
+h1f3yqLSH2Ed=Hbe?yHg#xaBt#U>Ln!>ED(mL(UkO<e9gJpMW+X9~aave*(@fr0ImgZPa$#9{mpXhDN
+L;l{O{lrBaRJC4dg5$h7{S!^+QnT5b^fr7aeEF0s*vXMJu3tAV-n6uzCLvhe~Zf`^N#dc=Wm56LsdAf
+2YlqAa~ofjZN!{dT33pNB6#gap4S*=MV8L*t4TqPHmr_=M}Ul2sEMj(HI8V_NE>YB4v4$7g3{D-Zl_q
+U)r_CVKF!Nw$CkS(tvrO6c%WF-i~kPZ2fv5FiQ`8~v{Qpa~q*#>u~gmFU_$*l9`O6edwnY0XDmV8n(<
+V*4)ScSjHn&sy5Jh{0%`EfcMlV&LSaXP&^K0o_;icw@7EdI5X;8Gaz=oX40z$4ZHTIVd-44H4W@CXJB
+6YC1yKbH`HS-gzLVBrtH-rkPlCi)CKKqNY79+5CqelQ`@3iEtIPA%iUiGjN%6}!p6(Cz>1>gxJ(cA0=
+}8U=W8!2N@4fj}MY9+A%?)=2z*#Xw8KBzF&FkZaIC4EQjG!EP`$p_@T`H=D?1V&`Qbz*|~K)@Y3+NnY
+kENdVl%VoY98v6S%V#ofls*#J=}QRAy$4*M0V``vBgidWIlM8M%#mp!8v0I*E3mlESZ5W^7KDP#|*V#
+taD)TxW`mQ9i0F&%^&QYdQNp>l)*ESUlUf)7Ja1l-0}#x_C2#uuD?N1}r<c{2)Xn)vHrYtOs&FGj9*F
+1T({K<+B;Adwgoxy%FF9Aob5ki*z!a+j4w#^gQI<(X7S3{n)<Ya0l9Boz#D2l_*YiiZObrCzQON2o@y
+1Fn8S7m%l3G>Vn*pA0`x*`c&Uq493xhPv$=88ZoUha-o2oA@(yFCFlq{IM~<nFEpBr{b1iajUiA1qrv
+N2Z)ZVwL<$JKxjREw}%FRy{RiEOi=_*!-<anyTF%waCq}&D^~|L@kgA^^GHBOg0|JOJTHW3N2XF?o`O
+_Cot8ZqU@ArrD|yDCX2NGgj?T2X@)FQQFjb2ZPAhEFrxW`un`d3JC3hRFJpT-Qs;`krfF8H<Ll-~4;=
+!xGda#4KZ=}bm2Zr{j`{ONjJX{5&PzA8a(}Z?Dmb4?$j_im$F!)+zVfGo>XWJM+BRe@gh}nZx&@u=m5
+;O<!?>G+i<uI6f=0z#Uv0q5tn}E{1hO#QM`*8h=qc7y>8TsPrE!p`aPj-VKtt$_^m0(aIvlhQ&J;OtB
+V-c%p&)?9;6IFwVg+7n9bve`w5(^-VUnQy9!t{_t_c`@wV5*<&?X8xvnH6)f+)K^#o?!QT`-`vM%n$e
+JtFI0gBMg}4n?!Qa{>y_ARO}u-4AnY%f^h5D2PPyNl+W&R=nL)w<D?U3XP2j!6Y@5HAZD^XmS>#FatM
+D}#Y%mOYLx{3?(%q`LCY>0by)mF#a*vV0OjF61gmx7)2Bvj#lb%qqJO_NrzO!1MAl7=98I(ug(eQ6pg
+|BEF};RO6oyI5=@V)?>594?m5f0zt~x?nj4>y}k-K2_H8n?Y_ulgXVhR~M+c}Ia)RG_0FTX!NPfn+Ak
+KdiolG)i$)5~|Wrf_PgeLMlQGL9WP^Jj-Rou3oP3ot=w(Kc{f#$;$8!wHQN&>7vcp8~Jh?{$s`LP*Gv
+451juW8V*)L*-14t7=*<^OBKQ@wf55JyUV4R5uv5QVN+{depSt0fxWVOe)=_T5@9hruYpa6ds$;q^uc
+ve)!ebeN1u1Q;uCV3xNL}Sc3^RU}2ZgYv<^h<51V*j%Hib|7?r!UFg5*!T+P36~Ehwtre6>+BSer=GY
+)Low1v9??lifcSpVHW4S)19;CXz6^wbEEq}P*$_g5uXcs6#G&zg;&vnB_Lbt#Q_t<<Zk?ZH#Red>+Y=
+r)=cuaqa)oJtGnbK<5J%$rHyMTF}k)6{+GxP_abe8AXKJ!nI_JnDm^Q2FC#ewdJW{IzhUhgyt)ivrb4
+^g+H%W6n?1D+6`@|t?)KTP03brRGhUbF15vj}6_<TDGCuyylj&14vy(4w#hlLWmDrNg8Wjr57I**wX2
+9{hq$%~`=W|Brg3C3y*^6ma-!ni}T12j9RZ_O`<&5j+=XR8igj3&rl5-`}Qp!jK83(1b4*-R^j-ct*F
+;h<r!99=eYpZ*hneci$J|L)5R;hvptbXL0NlZs4$n5=3(b`IV7B_WvBkrYWnN3y}Bbpvf&6_DL0t>UK
+6pdf!_K!*hZhj$*u_qAKz>G3p1--pxudBRChohiu~#O|4sX0pG@2f&&OloXdkcU(~=WOYr-y;MXSG<T
+I1)({&U6+LY3dQ=dq|C7~dif7)6I%Al?`LAi%dT-}Dcqg`Qg)IU7;^FRpJ{`aUc1GdyE#)MAb^Sq=26
+c2DZnu(h2Kv8e6JNdUkgcTs=j%@K4Q)`_}uV=}{<;9fzqsPiLJ9gyC+(aMU=<J;DnLbMF-dz~ka&rTL
+Uu;{k*7sh=g~EGKND$GelOFK*l;h)Xc;e;Pm5!C<HY9&LCJPAa=vsIurf;iO3?8uhrsOu|>oqU+Og&U
+W%-#_jG&dbZ7K|#q>C(3nNLH{lzc($pHT?}p%mv>_eMJU(g_EdYD4(8i{q}?$eDms?M_K5qUDxA~;g;
+b3^*ja<_vDKHlW+FyP1jzIb-mns{p}kqbO!l1FJwF#J_4l9t!{5lXtD4fH$uyecLM}?yXWqSDeRR;yX
+qJZnb*_5zdO60o_^E~fZJm=nnkK@q7KkuW#O*)f1-Dc;2mjY4qeKmJCo$zmG`?(6CibGM9=TfdgL@RV
+2#&2{l8WG3s6e~1QY-O00;o{l@>=wyhb?lAOHYwb^ri30001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>
+VP|D?FLiQkY-wUMFJo_RZe?S1X>V?DZ*OcaaCzlD`H$PynZM6pLCnCA<jNc$-J}?-Qn<F07`0>9M}tK
+YNF4D=!!<=}`KTGIi`~C{zw6!9Sd+9xfgJ>nM85NT-+eq)n{C_mnd-WxJ*oIh-LBVFvo>G#UYn0MMc2
+R^eR8tu+D)e0<tNo=l^vt)vO;I?>G2EvI?eiOquO2Xo`2v6c654pa?*GEi!3|K-tPOWw#lBHWbj9`{M
+SzHl$qpJwQB!@52o3R&9>i9vu}5GeWE@u)wa)GQX`uA!VU13BL=|6(t?}AdgrvBy69JJw|StqYFVwS<
+wf>m)s*cG0D+;9^W;_AXfwo^#(P$`T9t3xwq{t+!?#`Ax69xWts`poeXILtS4Fc{rT6qzyDV^`zZUgQ
+dCy-LpWi8fU^vfb|NA>tlmdG1>HGJuyl?6ImyK4-U8mkDI0fDR$mc&jnaI|+#qv|JRyx<Zo^OhNc{RU
++l{dx40{ofkdW2ii^NORMrkSoj%bR7}G-`?448e&X=a>7rUS6q<;`Ke>Z8rO7wu8X#!-u!tGy4ACfAC
+k;!HQHFzp%Yum=m35e=4-HP|93m-iv`l$M7)=7I3{*fXk(}ZS)p!!~zB<dMD2eD((8J9soe4=kDRW?A
+q;Cm425FPv6ZBU;MX*2Iak>K(EEFtIgZB>gfyty+3z|fY}TWC*QQ!aL)Yb<OFyGJQq%pL!VdhQFY__9
+M66}pPZb`pQ}}|t9xkIUj8SneGCsz&VZx<)#Z^sS=NQt9$fL|m|sqqhEGpV>Hl~V88=@cqp9o6gVYxK
+>1n2S%PXK=>Tp4gFBW--5IDmkE1EKF@IGph+sWwzV;$_}hqh7lg!sg>TEjETRJd)aR@r=BHB~>KkF}~
+-(+nXxB@~|F0J1gWI;*T{durew;$K%(u;G;C$EQEE)I1ZGq{{qsa$@_QWmkQ_t*gtdY?W?Cz|gBtfOW
+vWuT+!Wz{h4;?@E<jb_FtDph3X2?e$k#QI>$ET4_5Nu0E&9W;AE6`>xuKM?d{CnuLuAd1kal9}3W3fZ
+**J&~?`}+321g<Dj#9oxtW^HyLI3vT?uPD(;jUjG6|(x%JO3dg_)o@fW3<UL9T*J`Gxb_6`!5{Tx@vp
+RS8q9$k9l(4<S`{^N;vEbn;uZ;KkvwQpg%2Ykjwvxn{Xu&S!4Gbc{sg;7tpGMT~6YbXW(Nca$uAZ4i1
+qZLiaUsk%ftYINnikjqu!)$>-kZQ9JR~6ui&idjLAQ7<+M6-+Y^Q(2fC83?S-THjVpU+oSt<JwLip%2
+b$B)$I^5OZb>hheWJ+wcoAOG9qllf&04}gQrNdZAp(yAZ-MBnlkPrrNm@`q_A-+p}a@zwJ`y?XQP&%Y
+oeVNQ^e_Bi}HjhJ!7R6bmFAQ&I$Vx?kkLR<!DmGPdzez7gOViOT7%Ia(fqMmy62q;{Z$JDa1%GY^z3c
+D?8?0h<z5@?}M)%2|$%5Ws1;D~cA!c`zzyH&hJ`uoBvkoDjp0z@i6T|hX*!#_N<t8rWjR&}@4AY)j1>
+v>kdVl;5=@_k?QJN>F^2<tBubG8yWA0nTo*TAIU{jxxQ1x;W~e>tFy6&b9!Drr;akMf5Z?DA3m$nXAJ
+V2Xcvj;vT+1bp~SQ}45zw);eEQq<GOITHkJT>&#es8;Ky?PzwZ#fBgfIqZgKjo9kQ74jay!jdJNDnOQ
+ju324I<*x9hoy-o<vO@vPifmQ4MUQ|104Na0Q(*ZO45E?ABdpC#j3SCLM3fiDU{Sh)L}&oC%~ECU%EA
+!z1OVX^RvJW)SjZ&n3KWEF-|n&vfWZ#7f`fUmh9hs73H2RH|7!r5QJQ2?I?tZ25R|wuXb1?kTlTP)t3
+rF*1n$GgF=Zr&|BCvi*dvA<SRbns;aO~o&${}Xa%b_wZgUBf!)AR>2?(o;YvhsuSb#U7uR=1*v)8aqe
+9>f&a24=ywW~b}$~K_=R6TJR*t@R!9f@+-=*v~MSP=7DEU>RuM2Rb8@Y4`}{%US2;ODS{B@Efake6_j
+0Ifa0Ks5qh;(AM9Tub8Z`_Z)up+}1kU_lFUkbuR>dOmb$Cp9gth_|{}2;^YlLrWtLRP>H=>3H+(N7Ox
+Fq*bR>D2rg^;<~76ltxoNd=y(eAg;`gwF3cJ!-yWE2HfCXb*fec9gC4vg`5Zg9&{bGT0s{?d{4H4WwP
+AWMR)L|P-TMkA=^Y{ia>l*Fx!OD!;>QT2y}uiZUL`Ihk{<)PF*A41yXHG)SW}Yf(z)hCtLUd_B2}C4J
+?!dyU-74y}PCcwJmD_c%2!80E;x~c!c^43~*WX^*)>xt%pi^vyugYTUb@=T}PlWJHFhb?m>DzEyOGW@
+tx?AbqRm?`4(+aPf`*2X7n`s`0f>GX;<y8E)4<`3YAp{;H$wtM?M5wLS0TJOx+N?12@f2*6-`00SmKR
+oDQsJo<RC3Oi2S2-JZ9`3zUYpFVdrWx7gpX$e-`u$HwzdH{JrvvZD3XPVDe0(^}wsm@OcQ>8o2Nk0s#
+r2&l#;(!z=deaKK6p}-xnC&cz+dnsJNu}0HWbc`A4!2|Yy2$GpF{`~oiZ=ZgA^<n<u<?9!3K7N=Q;+#
+F2GG{qBk{r5BEA+wN(#DKY(l$h|LtX6gdfI!ax8?_7G4aUM*KCs>yadlOB)f!Rh?+KiaBsyBt+YAWvA
+}a`JO-1{KsCz5%)HOhESQrLV){;k@dc2`oe?mRy(=$g%zn)2May@S%BS$P8$>=u-AS>Z@n>Vej|D9vg
+O)G^+OiCah9TB4lhYn}XAr=)u@69@`S1Vnt@UqF(4p94N9S&v>6Z14vIPNw4w>;JLuXL$2}R^iAXjQP
+IqGzCWW(&EvAYVPY@v{EcRd*df$QMIl|r+zL(!&zv$3WKNQNsRvr%2{$mx@9cik3jOr3|@Z+4rxFZ^c
+N!?b~w8p&Is3wR-)0t9jZ4tI5yy=cgq{h?UbYJ@7`y6u38Z)gM918`xAOtQFc!HU?D=L1+}vEG0<CJ~
+oML;gti2-3u-roA!d3NTwzNzqw?B0z+p_`+MZ+^2YkH@1JLwspb!STsjnp>3HQknBANJxnGTk9dZ;%r
+Kf}KmFo6H5zq8aF3lDzBMl%h$0dZiA6Njckpn3H_IN`7InMMt7g@XPr>5r2xDMTY-4yT+xT8NxybJ6)
+9hZ#7{^1?Or1w(;$bVcFti*<7<MXoULX=7))jP&C&`EspXV5HI5&Lph;c+>d7O6&%-=s_$92TZgU`Kl
+MSrY4R~t`>y@>uveFu101lHn@6{(AI_=!UZQCmC%RulKk;+H5G4(sgfN=0^bv0$c2Ce30&!gaAAogU_
+OjEa!YpI*!2tYLzGhm2<YdoN}((DO}Ep+dH5S=~jf0~U&FLN^1(vHXs)hU1)Vc^HOS7<wm(fj$#_jpK
+&n4eT4Oro{m&5}dV8WWu#B;$}W#6RSoMzzU0#Ob!)NQ7hrFr#)aC*of>mrtq2*f+B+lT+UJ4N)Ye#t@
+I+dW9V|0`3QpHRt(RMOw4e?5b`|Vao%2DREM4tz-W+o)n>QJ$N)nOFqZ4-k)tIzUf1mn@6~UIUM2+Qn
+=9b)bv=cDM;MJ@^}8Uh=b<;Ezb|87uAJurZ9@$*08K*IWtzc$K*j^~0J+y7LCQ+YYu`!7z6#pvV2Egp
+k0J%Yap`v;>U(0-(%f5tFldkQCK#G{xMfLztOXz*%{|RV#jbBbXsHGy)}u)_1BsvF!I@{Zv#>5h2!b|
+bP5sTTXYa>;U>xJ+oA*2DUW4X@&=EOooi`w7Ds+&bN`+S4z(BAEBzbaK?&-vd8=&qnNt*j)J;cvZ>my
+J)G40E4P{n*6ADx=dKi=*_-kx0+h$rZ+b&mX)g<|s%=AKiSonzJ`xZHIeA|v@L5OfsRDIAB6!F~aT=o
+N54yR!vu);=}jYY_?*pR*P<`dvWP+#xNA_s^3J%y3N5Vp(*hM+28?iH>GewvY>{lKB9#0CKa%ByC4PF
+$79z6PWKLsf-Ip4E{?Bn04%3I=Bg5q{zBx57uoru#sUgk%)v)ZD-Q*OxO@31@TZTzE<5Hm<`Z4tg(cd
+_fs${8kK)RRzzxQA~y1bA;tq@T17kz5xMcBsB2QBf$=n<m_QzZGBjaZl72Ey$nVs{XR}JiCV?{oAYpF
+FFdnn>*tIt{uNnC<oWcO<P5Fp{t>iJ2P9?nMpN<bugadfnk<8#3$S1(3u3BGN6{xUob%4PJOwMUmSg*
+QC*wMy=d|S}Zu4)VB3y==mvBRr61EgRy189^jag#{z_w2-Id}I-#!-Pt1xb5tFRc~n{APmIi&zO!<<M
+s%JzdO|HOu$8FCQBjlMVLsi)BbC~`*G;dINn(zBgTW7*9wR@rk0OrX-Y(Er#U3kpObsVdASkNh=@QNp
+bszG%@%Cg%c`#W{WKcShA%Lcqs(eRfNp4wU-ahCsgddyotx^qX$B`?PY^MB7uhK<S&~Y+*s|XN7Vjla
+0A2nl)(S_){k$cVD+mtjYD^D-77*l_#m7Ly#>r5!d|LS_zNE#J8A2f&cjf^q+xPSY^`dZpKD&6YIHq1
+)0+jJU(7%A+U;`Y{4g!SPOBYl`A$C=G@tQM=KS|;lfoX|3R;ae15b3YF_9lqy5m0^DfYL<D7y)KPk@O
+L(MDRJM@3@>e5dY5x+B4eLw$-{qi_-6`d7BMo5WauKTjUTLs&u|f1yNkHzt%#g1@2fiMV+HNr%~)EFI
+(2Yjq=d0d{!oT4VSyM_A?tQt#|6t!^dAg_5nPyn;ud8mSH_XTpg7ahYOG@YXznOL1hv&^phSR=U@HP;
+7&BWDLyIVi}M)!G~19%Ii%&8VU>q$v-|f&`8Gp_{_Q}GRUIZ(deW*z&jf|5GG^hv0wp@CmwVFgyg1&G
+N^hVy^C%5Pw_&g(mF!4A#H|5*#6Q`ei`Y}7J{aUs)SlEvio~Mm#*|7k4;^F>%fvrV!pm-iOW4O9g{G4
+5{;eL8K#)!cWBoylKh|?xynl~yw!!|}os8B&Oddw37Yil>LKp{0pgveRHP9i~=#?~9vL|~<8B5{{)Ft
+xx1)g$XEO1zqyrn+(o#dx*u=K{~HEyfJYJH^kk!lrd)8jy>g=wi1WQ~dT@OJ;08Hi?HM=3O6HtXB{6V
+s$38&3!5Jm8m(q&_gPnIbMROQrtIhwCI|Y(6llsbNGDN&wK-l)rD@Oby2u{scJejbRYhQrt^a6Ll-2A
++N)vG-Q#>YYM11%E&`xFe?ez;2?YRH$ncC!89A3aU4zQxe1DLK8$|;D}Sb^+imuuSyz&lfH_mBH^2eo
+*yy?llz_-1gKb9BEb<`p$W9x1lG$WE=0vP|hzdjgRaO@%sf<!ffxx5TFpYql7Ns+l=oq#wvb)aZvh3-
+=K{KvEVG>3Y4JXlcgS17IU1puRs8w-GOy|LM$9~{5)<I1NS|qT&-G<^^u-gM8vl9Wg6AveR&A9F;MLG
+MM>RO8pVxa*dlA+`YhuDP{qQs!b?0{}CD4OrAF!=K){=Hyvz*r7M+tVczX(3YO<aZRqfst%&16}6DB%
+GKGBsMfrXm^|s7&!?J&Ju1YpxLFWu9cjIB<PcFK$NPwYtKeX*mqR4@;Lu`%1JW2)k<}E%7A~8W5;9bW
+WA25$m|^XM2MJ2V>{jq{UQ-ySU`N(Z#_4^Qz!|u+q#TQy<=y-lOUS<r9VSb>FzS*FeWB41eoG5PrODK
+JJLAtKu2o0`?+~Fi+SXnU+V@F*nwG&DDCu*UeY@{q$n6ZKK=Rhp3dmMpHKP!NJOFZ@Ta>&DtmHBIp@3
+nrXgz8toy4;5_yap#;NAFM3(<f>E>tIb7@yBmzdJ#IoE6>z~(u7+me(HejK{t6RHKt0uvx54f7~0Sdl
+5b$lHLQ75@g^d;Vyln-BjEN6SoL={b2=E%YlrNA;h|a-8*d=p;<!2;s&C)dH1yPBbk*buxLb)~sM(Kk
+JEsBIFrF!{-Ilz|gE&YfPFOkIqNKo-gv~`ug*m=Wi}BsZDYw3b1u{TZ%TI>~wMgS9iImk}?vllr(9^k
+zz0}J&I|WVTDlg=lrOZO@g$U8+J-^o^_Q*{hhN_IVK(^QQ9mNii<RC;fJaCbd;_f#ucp*I`K!}^(UEE
+;jlcwZ6gd$I2eK&r(mK;9A{gYmtQEG0bpVnPY^I*ZkKIo^55=~IoByppqz1@F?u>-uNKwi@t55Xh@VD
+C*oi3}m@F!JbpO8LYlr<3aq5Iq1&GVgxfs_)6J*ajkF4*J!tgQBR;{j8&1ubuXrcjH=LcmAf%1$&WMe
+k6*Kdz3)p0UZQi>ZmjlQUAJy^Nm1BVjS(F55wlt|lSV$>Eh7P<-})soUn8wL~yy?bd*D`<6Hm7zHgD!
+MJ13v&+_w(`zI{$y*z<rV&*3;(DVY--?oL0x9~t&wCY@C~#V^xQ(%7W4!3GJws%EPCOG1Wm3s8h^)hM
+F<<dK=W7Vv(PQ5%f%l4Pr*Fcv6ps;Z~-laPV|5yK6OqpkV69YvB{jm*g*BW7azc0fAb#y#Y|S!FY`$<
+EYe{(=<}j42%S_@a*DRKI;eFvmLz#%fM|-vmJ&|trJ7@2b6Ru-yaMcDtRjt_QX9rWi(iuz7)dHNE!-t
+zqYbyyeQR5q4LPL4P|1U*Ycw2#QC_YdAn`wVjW!@d4IRhK5QwDxY<Vv0fcFOZ!@+vWjWivMQx`DRr1x
+<hUt8N+anm^6bCPn8f6pZ0p0%XL7NnS@R?J?+j1*%@N`h15PBbL?zXLf=L817<Gnh}pGe<pSu|P0^+C
+Zg>XvuT^1$zBDF`Ei&*yP;l3rlgu$~moL;in%ARSE^ekr%P7&=#W7H86R!2cJCM1INtJscOJ!H{&sR4
+k>V8af645vLsTmvDBt%rNqL{Y=RLq85{epM2UHqClNX(X>sOkB{u<eU(OI<zoPL^V2T`7&4t$Z1{f2v
+@_L_VPix)UY6$|1L67i$0CYnc$*U6#x38QzY|<Boa`X@q7MYJY?<p6tIwy2LL>s(<ix_sIChUL}0H5N
+3*cHI4cB|IFGPxjd*_Hs-Cl4P@;h)FhKA${%f=|E3fBrDZvlm!tXE(lV0nx=_%4-*^1c7PN%KhO8K*q
+o^!(g&;n{F;va*|D&{J|*?i;@f2Tqh-HctWJEw_f^*tHBA^g++9dTZZlv9*`YJ@%<7~pe2WcB<>d%;v
+Y&>J0p3_SYmN1JUjVVt{`u!)(x$5fb|UqQn2RCRI^Ws(7UQsIuOXAR~0BCNni9lznfz3*F=t8XlC+T0
+wC#jeC0FUlX5W_lb9m4b!=6Nipxnun6bffA5SV|Ka?1QJ6|mP8G${Uh?7Zqjq@qFup>qcFTcuV@}h1d
+N&=3Lx^C4D<mCD)Te4@tA)5##c7s>d$0_kNM3gEO?m5Y?;i^U_bU1UEL=osp$f6m=W$aL06v0*SX<)H
+EYbHn^+R&37*=QDM2?nDisw0u>#OeQ{Pyy6YrH47lK;%xba^+7s(q=UwU;htSHiJz}gJlsR$70dt{*j
+C)fEH~NZU`D#Sx%WKcLBVoHB}skWzyt8WJEwPlQXo)U>!v;8%>FqPZk68^mAaRh1hkt1WwXq`Vh~>7I
+Bdw(N+AbS2)c@TwjYrUJj%64OT9>S|2u{`Ri?!RU$>ET<VlMOoBovS|A*U>tzjRCv1dC&ipE<xcJIp$
+e{`>uf;4o_7sCe4)Q+H{W0FuvCy-A6IyNH-U=OCw5maY<!3>p+a1iuJ<DFJZmnD%uT{^v$lqjzR4myj
+V}wjlq`7vxk8(}SsbkrwEBDFpDhW)~p|x_&78#5gI<k~J$J7&_7o5Q!R<#(pC|mdFdqSa2Tw%Kp@(De
+!4SI37(DD#UnmytuQL?sqwcBF!d7M_S1xNcDY_RN;QrjqWb_wbd@udP5hGf|zU+5~5D0qzlsvg0$UOX
+NdP@wE<Nsjv1&`5aU4GS(WN44vc(@fmDXQm$VfCq=l>p~mh&gldmZn@UPa*g}R7tWKDq~l%l4Cur)<y
+P~_1hpZA#1)sNQ(Vl@)h!X<aOhP!dg{vNt%hl!oTU&Qz-v@ieHViWA#)*7lF}0%$3Q@erw;Oz(hQ|L#
+6qHzpLvJyYlb1mC;sCcpDF1uY(RkCC{zd5t>noLt3XKqi^U3a*6RI*u@j@gd>ET(mr)O>SdmF892wXg
+TCX=4Hw%X|m8Fy}fyS}7I>|NU5*LZ6mF*6Q`++ox1_RSMGR3VR5#;sszqu`7ab<5F%=k(&!Hbdh6hG8
+X&^L#ffj1pyk#~YqgHub<`Jm7r$9VTEFagPac89_x|IeM!EP&Ln7%KE}$KV`FGRD6T!{rLYIqs=O%!4
+?sFV#gU^Nn)V$JNH%K@k^{3=C70*!`%`*%{>_P*MZ>sRx+-;aal{Z&ahb9V~JjDt8-4cLPQj9S)~uai
+BCNOkfU0WR*#w5(T3$<qk<VCbb*#QFs%xLyC)N8uhuiOoB6bpdkoc^jH=t6DS&XZY9Gd=_$if;$Tgfc
+^TI|x<fHQ9IF({=3R2!=&ZJHt?Gd#>nQR4vyppOD4Qc@q+PL8h~)q9O{VA*T(=qDI~iP}>YVKdG3(%p
+DWr}yFtMDG<TUMPH<9Z^=zm}&asraCyUfa6XVRDm)0`^A`Fr`Vhh3_oxCH+=#{?wDp-TY>)-a~K3Uxe
+s#)W_;Kb8;!-sYek#d$S#GV?e%l;FT7lY?`+>t11c2Y!&y9aiA*%m)lW<JqPHG%O?tlS+s@XLRex-Q~
+|5Hn#jk1TfQkjbVa06^2fJdi+aLP)5tpHK_mNM6Udqi~_t&(zsE$xWlDCqD~>zTOnSsX73%jQG8+?IP
+7xbHM~vK9hO~k<D9awous~SU@-N0Tj5P^<ioPpZYc49c-h%&)jNvdi<t(7OA6kFb}Y%Hv?40EsSAy?Z
+E+~1ut2uB&x&<Xg(BLoo;xM40?t3ahZIEBVcy~n7fYXBb}eS$gM1y`>qe$V*!d+){m*2u9+CEBvIvs=
+fvLM`$&B|!RCex<*u(JUzL5nm4YLURFUPFI%Qo=8#90TvJ^g(cx%lIW8;zgLYq|TC7=Z*Q*wHQ;65>z
+N+GDOnH___>$_g^e(4+9=;ggic#5Fz3M&G^oFba7<VvO@Xj$~tI1y=p_8QNgBV-V0vXLeT&<(zETHuK
+@Al3i<}5XlY@al?E#^XC`R^Uz3B_jLB)8$%efyXXY&Kpjcnm{x{2BUv}M_zMU7B?&mkQPppyq+dlnHq
+GtE7HYGY@vgNLybj4hFj3DzIsw<7Q!fi}rhz#I4`&f!9Q4xu{jfWhv%~22EiIh4Yj`kax*dyE13dTtD
+IO&R?p-S!T?jr-vtFXRgx_O|B4$9{a_{(*5(#K{34Ku9RotT$r<GK-(Iya?FE(v?vCz6+WbkPxmt?Yh
+w^JRs(taTUsnTdI_m@R;A2Xscw`}PwJ;4Akd*IOJ==R#*FHTMO|4ND7Vj(yH`rTS}ThM>{h1@)7-<h8
+dSn&1iiv>PF_kjdQ+pgpc4zvEaxcud6a4_g9LCHq?Rw7vOQUsT4i++&B;*;9Xu|fr1Z;Qo}7ae>Bqgu
+<?VvbKVuUK}2zl9)~-uRsE4+XPyCwCru=NSZFDd_`?>HG_fqX-toqH%n5dCfUTmV$b_?YaX>j<s8_)Q
+FroMME{Te$mr`iyQUfn;X4DKKkm$lwgCq9Pe^<V>*<7m*YSD>PCYe(#hh7{~ZzJP=A9m=1NP4EQfkT2
+rV5`dOGH^N&9vnhwtDKnu|B>G1=od&f1jwsuKNPARa^hxIx{nY;zKJ=6nEI&Bw5TJu$3zF`oyT;31kO
+_&I7K$A~TDW7xnuSd3%1Z(+0OaG`1RWU#q+>aK_PaNzci<E|8W%dLrz0DE)Xd_mUyG3>~WMYjxq?sQp
+KnnfAk$%IVMhOYQFYJVxdy5{*`SdbqOHj!?|HDG9516;<IVgvlR4a*!+hZyt4K1BE-3f<br@PYuPZtD
+^Af*~9_qQB2I4&c?mg<la$M_4|1@zvd~k?}F{g~J@i8b4vP=rEz(Uf*ECS|(Ct2cgTo@Rx1mvQ$dG!c
+>9_;YQX2IMUh6w-?Cm4pk<(BER3+rvnAuz1%!zs_ER5*a19nJ>S5uO&z!!BAsaN&E(S%`u60A$iJ8U`
+z#wjo@QT7v)@m`OpNeq>~Pm#<-b2rOX%hu50{YGeJAm!FLa#_EGx?LFro9{9;M%K7png&5uw&QG{|qd
+6!rb!t*9yS-n-MSsD3d~sJ)px=)$MKgQH*38cM&h5t`Fy^b;MP1D&_>@!cy+kI3p#&Aj#6#ZxwBO!~r
+bq>Zl!n#&Im+5N`vrS{3<O@RJ`nG(^>An7z576>LgG$FNgzk7s#ewIc6ZoZn<n@^NvZ`^;i>uF=Q9%h
+5Y?&*J}d(89>nErUcmS@tL4Bx!rv3&BhSX?Km<BBkLRFGjth(o$DJ>GT)Ls-4MAu!Nyf?>8rwxDYEMR
+1x!oX?w#URQd4E!t94|0=)YND+Z?dS$@qV`DlBaeGHWd24hac{WPBVumK(wdci<V}l#;tZmkK<yU!KQ
+B>x=QWrymksW$*e(v%&ixR+Gwo6SnGO6YqFEP{oIsHl@R$5*4n|d@!QGfV{4DolQsJz*p+nf?ZV8XH7
+`C&^14Pt#qCAEdkJR~8~ICn_o@CklKrviJy`!y+d=c&E|!e2^myBxh_3DJ+;WG0&@EaWtGqBNR>-mnN
+uXvoxa9kssjH#=Yy@0TYobz6g;LQEdn1jT)^SO!x9&F_-9<>mZiROqph(%x|JpAoG7?}2E#&0{dRS29
+nQ-tPg)M!iZ#JJg|F=2#(S%G{*1l6QfGKtGV@bh_Bab%ra}kMT}kd@iv3%dchp=9_N}-yg!Kkz6abZr
+e3Txn;XK_ihuw&YjWZOr{$JT7X%Yh6V+XD0&uV5G9FfFH2NVcgWM}$tBjphzzJ_sKR+;95=-O0Z>Z=1
+QY-O00;o{l@>=HHY}u+1pojR5dZ))0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUM
+FJ*XRWpH$9Z*FrgaCyB~U2h^c6n*DcSVYkhiKP$whBo_9O1hEKGy=ALBxdFUR%Yy}Ka!xT{q4Qi_Lvz
+M67o`{Wy9FM$3FMm<2${J3-&WNz^1PmR8}ZydcCaR#<1^;#luu<r3Y#3YTRS^>H8H>KV{B9&Q?2Sx5B
+Ve<*tBH-_NWtP&3}2TuUK0gZO_d-b;A)ZnQeygHlD<U)t%3{jHT|K;wpG+=qV=7+o^XDhv{%=dfnWrI
+5ldmjeUEdgw=otOR5D24Zmf(CCN;*|ndO2GUMO2a)=hX3Q>4pIx#+JL*-hfvzEU8N^i$zzcSRXQ)K>R
+1ig#)0Hw{Mrncu>$D6TTP{68%jJ*_?v;cgqwlLk+zkG5U~J$nvG@jeX^z39aLcy*85l-Vncx=L{4u*T
+?~&;AX(n+DiNx>pI3XVL*yiqkpswEFA&K+(-3jr{xbPC&f}~k218v1xWJncTb5R&Z%9CCzbCxM7AtS2
+WB99u^>UDPpj@?2d60?e=O=|;USLk0LQ#qUwn>(vYZbgPf;}Rlq#c33QCQ4e6_VZ#_!D%Qwg;m+fYHf
+lURRWbJ+JJ=D3eSThTV7Ql(F{cXSqJg{q`_2ZRtMH_F-CPuUg9F;poPm;&N5yIzjadeif2#gIXRkwDl
+0tTN)*EGM!iWQGAz7q2O^jYb_aajVVb}3*BMwHbJnc1`n3I{#FhM2l&)mbl`GIptx1wv01CzUN`xHpQ
+PBFh;+mJf490BH&U$j9v88l`Ib*-C@#DX!;X<swjP?t>_fa<X2d27vYyuhF2qi;DxJW-0eJs~FF*$}A
+C01}YH%f+;PUfJC9YupcW2B>ZhH->I*S-V~%p(k8lENRoG#X6;NBWqR#A!{azXQU-E)5$@czy3&>ML*
+4DG+@cySUmzH1pf-R4v;}1@T=OJK1u%f&8>kS~Vzf-@;h$V07<nz#6Ram=3RJPTwFll5H{k=uBQcDc4
+^btTuRC;GnN0RnaC|04@HAu%>^1l$e8zhD=@8+e^GHL<!2-xkWEMVJ7`PYZ6DkK%^moLOx^p=O}4%Vs
+DW)#PcdVa;*q<9u7z6%AUZckY3|VWtr0^*6xJc%v+o9Y{G;WQ54ve-~d}G<a3Pj96!#0=S_Iy*MB{H{
+Ou9XmB%%)h+E6p*N&&(9!IJ4jt+eYF=+l6vUhcQNJAh12Sq{Vk%4$726ApuO<I+-0`xQJwvD4>AVqC1
+bA_6as%}51>tNhko^27Gc#v4vh7v2}7PPPd038<N(T%n5&f;M^$Z4JT<0q`mm6EKw&laGkWo38!4)&-
+EYeV{y*AA~hkGO8Qz6nQH*ePpdh5i+Vdcg~$19ehh>yw`iTe}P1@Ssh-55C4<{^IgKx7@H5;QY)7?}E
+KksBqltAG~m}Zufr<*?XN0o(aXU?+jkFqHnSv&Nkv1XOR|!tekxd{()-~;g5IJg^eb=^j3%JGolm)*R
+g}c<W>~T)6fJz<+()!?Dp9k3>u8qN39*TUiAL!MY|zRPFe>*j1+5M>7ju-;cq{P1x`svtywN4=G@;hG
+L~b!ca5v6f^G@_I@M}_VlIB2-cM(@ljU?a`+bHx`D7KUmCIcU6gFZ$`RVhA>E!mNbN=6C-OenjasB6b
+b!yE4D-t`o`en26;#r@Lex@q|^jei9EE5+p|1ROLjZkA9qPmrSHbh6Pu@11i@Jjv^ZzLzt9PrfuN5ac
+MEVwQa&v)2$mE=wzfHHoOw<=#k&cu!ym9~OVNN=3&q2LyCUVZ?PUUpC{;l)SEwb(cvBq)M;^JmmLU!h
+kj?`#pG>&Uf2?-e#nPCh|CXf@(P)=|E3&HggLb7o;RNmS{a%uA7c@qs0Y`{CA$%YRTy0|XQR000O8^O
+Y7y^G0R)1qT2C$rb<rF8}}laA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcRUoWo%?~E
+^v9RSZ#0HHW2>qUqN^XByWzoCjC&j5U`+2w*YHb^yR~l1U4;E4jV~SMJkOqY`=YXBqfoOoOH$XB@xAY
+?(N~-(Y&Zctq3b9&!a~1T+X;zDtN)vzG8AV%W6@OsG`X&-7!h*2FdSdvsubAvTK->>qL}_m1>PbVa9V
+-(t<4sY@ki9;(%6F&J(J*D4&Q#F?B3e&1exURx<+6d_LEIe?c^&ZWx)rG?2$10CG-5MkqnxXr?!=Dq7
+Qmw5kjWJ|94~T_s8w`wk#Bl9xNODnz>4bS`fqSAq4Q$G^x(DTY`HEy<P{T`cYVM*SQ6zlCib<=KSoHB
+(Jp_R)__aJ`@|E`PixqHa~p9JzMMBqkGeLDl+((UjF|?M-O1a!2!q4Fdlw%PUrstSJ*r5?XEm2rX0cx
+e<y%0x(`VBL<UbOr}_EbMltfiYHA@>wFKfWm%IW$Y{GHUv7AE1Agvq6hZ1xUTTC=l*ec9UtU}o8|9MZ
+{MO`?pzSh({BjAXc`m+4^6u43a&mI|bV;bx0J7NuOBGAWc5l9(K7UT$Gvm~==$TKSF?BR;fEq}c&fq1
+wu4*I;yRQHY@F75uabv6~^S#F!ZM_eymnM*_THNsz0I38mP016HGOKMwnNnar_8|n1mlhHuY~F+U)zw
+vfRknx9P(vaSV-g(6czz`30XZTb54x?Y-uH67Y{W`5DGaDgI1l>$>^@<YA|H*(=X%$9U5naBsi>5_Of
+K0_iRP}+<@yOFCVKnF>57o=$lJZTf&88HqQ>{w{OwF^RCfH_n%M4e%A4J{uCoML6Y4EXGARb}`aV9A)
+8Vp4>lHX|Ne(`eckDAzR=wgZPeZZ&1UwlWvNMuWMO((>af95|MvQDlx_8dV*Ao@aLyM82ZohtkLSGP9
+o)|AtrMG)rlFmvZDm*+i@5`!X@nNmz#LH=zlqc#~pKB8&gtG#8s(8s2r@1L1Izmd9^iD?`O)PTjHEv(
+8L&@@NNo>~v50{;_@f%UXqSc_^ob!Ql(OP>IYw^LC&gZxT^mpit##*QUp>-)HfBoHk;n_Gg6A~Dr(7r
+AV5q{Dn18&@iv~wSu^IA1k&SaS6a%sXeuJUf!3=vlvZngQRsq>s~Pa`cUBbUS*lF>wodav~mk*CnL_f
+U$p#xYm%dEZnxAV?%R6tM;hFSgjP(*{~Ppbj1Bam)6WvZgRfa`xtDy^2ZE=0xCB_d?)I^yzR15>1WQ7
+q8C=cGZP-<;xALndVvd2_ag_Mjg~Epd-7d1qiKHGcScfCPc6z!Isv+a`1)TGPD6chqz78G3s6gP<!m;
+B+*U#DOe2TVN%LFdBAt%Ma9an->`__-!xnd5zwR5f%sEHY<by1*tHG;OydQNd`~`AY&Wt98a2ez0(w6
+5Ukn)`7Jc+^!8CF`R<wDMq-d}LvbO(4ldOfq!(jrEik1m$i?>eT!aq&05rEB(1&L#F>VL;)$O(6XZTp
+HRG;UpEm`Oo=2IvnA%}#$EDRhbvma*9b8b;qe^PKTOA3!r6!e=NVp;JQ!m2_;QCm$8S^K}7zoWr{cU6
+W$>W#JlN+rNV|>&tu1N;|zOuf#u8+<w1fb<I<{)syPMvG$Q+7N5!Hi_iZsDLVLrX|YxC?GxiLA~wj6-
+3X@RE^Xj@F(nT~{u?e4PP9MvHwJGD-rf)bDs&>?`Uy8IjEdnI)_TPXxEP#AyQr^HL3=^_nM)AiXiq?F
++Zkop_03Pj8MJ1gCKMdh8Qwyy7T6!4<KiuhcC8It_Qu`Q`tHF&$~L!8<N4jw`3rJ;0{@)AKT#BIrZcB
+4f^&I!-B&>%f#!DXnSG#2x?7fdt{B$JX}Cy7HFQoY=EY$PUN~CWQT3f*bmvV(D`+@rXyz;TfV*5Amx$
+m638GJem!Y9x&sK|J*`k3D5cHkiX?_l9$z>+Y=^=V3pTpE{)6oxmFz&2j6w&LGv|ILU#Geb?_uqv1Q6
+#wb;@o0lkURWQl-;2*+A*k*cI%S7qR>1R;|ypA+6=%IXGQE4rx$fmFWcqo#efNxD<)<3rQ?NXm?hH4d
+Iv$@_YS-23mo9qgWesq)^*F5HW%p;3H7p}q2BM%aM1ls)oj~d+VjC)NWAW-mnMB8eQVDxS%!BQm!Yr0
+2sy!L2rpD5N;><7ba(EEAZzX!M3`0zi1P_z&zHmB!Jy3dRt-5mcW-&_{qcd;>jr7uA^l@Dyq5O2+X=u
+nmJfh-XLA~OGW_1*nJfm^IAx7Vfz2eqhH;EDSMZ+ze~6vpc<B%7fj!WEM;|dQ3<EqI-EKYQE%CNuN51
+Y$Hv=v_kpAs@@rdy{S#B6GxsS;7b<&Y*b2$4KP)h>@6aWAK2mtey7DsFm5g<AP008L-001!n003}la4
+%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kW@&6?aBp*TE^v93RZ(x#Fc5yvuQ&}&O;Bruh
+7c;+%fyiSFeU+SfEp*cu4axMY-d|v`0bn>CvDnpY>}p=zVGh7yZd|*b)$`gQdd~oBv~0<L)(f9L}&2t
+@4925`@@TkL%X<+aMtkhmfxW5%~m|@Sr{Gl)*{+g0%bM$e8oR4TKCx>Cds`K4h@_~UQB9z+cxQ>b*tH
+jN%rIHSCS+ZuAp3NZE;!XR#n_=(rlgpJtrq8;s14O0PBJqZmm!^ax=4(Pa+s>BrmZ><zS@^)S^bGwX|
+$6A{)gs=vt$?rlc8L<3r@(=`KS>OI`fN(%H1DZ;#M|aNHs2J1Vb3uq0T%wT*9t1u3ipy@K?13U?W}mi
+SuWs)9;T+-Z3AAWNv=Ixi*FH_!jJMoLkf#n!Ujp5`%4Pec%BYKe7Rl^D5BLx#vWfM^0VIp(XQzj`PtA
+W&KyV?Azw1dH!&H~^tQ?4S?RPe&TqVd0-mh!JqZ`8fX;wHt<ctu4DArmus4b}+I>X2lp(*cdEnVAXsM
+Z`u3gKSTV!M&MN?oY2b8@L7|An%~ld-C9^!wW{>aA(}3%mgtbdf;xqqTGU#Exd+YjL&@b+xL=EM9c5~
+tFAj(iUZ_R(a!JJ-l*Wi&AO6kD<EYZ2j-GHE^cJRy%CC%N-AX4KZrqZfb;TVY2=zDn{EJq&H}cEi4Lq
+=>F%Zm`pXZ%(p3?wmkI<|Th7@mnikZ&=SBtAlPp;-JwsKAgUPqGKHhdbzkE@W1+BO_F)91}D8)xdv+v
+ZxVKxx{_4VHoekcx=Dq9P3qQg1!rUPuWA!VRh*@T#kJ2o#0)=&sDidMI?Yp`{Q%7HQUtWXMN)_cPcs9
+W3XEi~ZO>`OnhrNwDxaNsDwco1NO9=BJv#DWrYutQVPox-U=a-LmKxVlsoOqI7GzhG=Y>WrItY{CP)<
+`5(z6YtGxIMBMEyw-~@W6CfGDE4ZL9=RG&1ic;4NccKtdxXsg{{S9*X;FfOF;9im=g@t5Ktka#|b<m?
+-orH!)<6*mJv&S1=YUSt)9O%XdOpaWukJ~$YYJH?3&}cb?+&Ad%xNI@arlau5KTt~p1QY-O00;o{l@>
+=^s&pGR3jhGVBme+30001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK};fY;9p~VP
+|D>E^v9R8f$OcIP$xH1(BkVTqx=z#qGj?3-og7wzxJelI$H0*+Qme+U7=<bRw0+SL}b^89qc(lGAOk{
+t!zfXE^Vd!*_MnNK#ar4KFv7iEQ>WLLQM%d%3Mjaxx+CSvS>=v`ta)Wm?llFv2w$to>f`a>DL&R!j0u
+t)4Ya)yx9GQJ{(1O|!fz3YJS=mBJpmU^y#gvHw-D)oE2q%1hRm^LbU4`gmPcg@t%~b@l1A+xY}LiCVi
+17azodf3oF16Zw|ym^rFh^ND+3<rEkGMT?dxW)t#(-d`|~oq9Ep4f}7)gmgTq*AEN?yAtM1D|wNEKsp
+joP#G-v`we1tXy9csnPeF)iYy~@avdROA81K8tcem5eQK)vz4u<@dxi)RbMzUYk?kxms1QszmI2uprM
+GA+WgX4Nr0VvCIdIuO@yBEW_5{d`Rizsy-@_wo;w&raj%C?&GP%5bpZ)XfKmY!C@sCS58%H+`h~x!Bl
+5+;C2wp&%LcP!o-{DzN;(sC`itLs(0w{^#hKXX3c+!bOB}}!I9vvO2@7G09-3hX;8bX^TmkkB68!ZZQ
+w*_(tES3Vaq|B4tFjfPDxSHRPYOT<KESL}o>&%Rxz%}Z9)ut15a0Ynjj+E^#Q`@tSqw{vRWDOu3qL3B
+IDQJr<8DL{%#YIk=m7)Sx3r2<P@HEDcNDGdn5QJB68C`+A+{JJ80^Y*Qyl7X9+;O=jU~8Qu64I6ha7Z
+e+u(3EmWQ=V1Eh{@@?7l8|&f)&>*595yacn8pvzt3d!|T<G;|?u|I?tp<ued+~G>l3H(S?a^2R2jMo-
+K5oI_dd%8XD;W*b|bLpeaM!**)FW1)I$#9mZF$UXh(P957Jdj5?LgUrdAJc+h}Y|Bs_z&aTjM(Pw5)p
+KhvZQ!pTJ_cZ!{fOnJ^-;Q8Uw^cQEdBMuq!YbC>_KQj9Ch9@O6S(TUDw$qcu{FsuUUHdbv0%kIaaKKl
+{w%RZGDnbvjOl;s3?OvUw1?QBa^&H1Iz$Kr4i`fp?NRgbbmNpfj5H5?z#r=hV)(i(^Lasc%M~T{jMQm
+fz;ZmD`Vxa%Sz|c!1ONfQZAwLBaQfa81LUPl?rWx>ZfRkbmL48083+kY?8Nm5QwXAeLkmdC;7$!YZYY
+OmTJ9l8NT7;?^SR8|Crm+ll$GpGNy?C>lL=_55;9R))uYJ<Tl}1AKc)~e?>hhOJL;06*F*es=z!3bxg
+~Y(dbQ<(?3B?Ec(TEQ=CXzGwYO0NqvRl}ZUH0S(z@8W+>;q%n=L#uvPpEINB}pgX^B{N##YHYiFb3*D
+<bTW0Ub+5Do9QZU#(cFGC|%7S?&1$R5g)~O337CG}7^ueXYMfcMvyswAtYK18&hB<ZEKH{@f<~z|g;j
+36|=;XIS__w!rmxN&NIa8#s{04GvhxH+PAOB-2UX%q;V<(>%rw4eR-lw(y=0`BFUF)p8HYZ3qY)@(AQ
+rt#(x6ibIx^Xcjw_<}kmYtM)DHj>BeIf$^=^2*AtZb-_1V=~)xx#$~H;?cMn$^B6l;W=YrJ_sV<<c3A
+l?Ms-f?c<$LGipU1YPFg11YBkTbjt_|F7Y2-d-3QTmd~J+6;=46jEO37TX;zex)OA5ZxYSYsSAY->Yb
+)I$AlAh~;{ljfU@Ky=Ee>V$uv}j(LLvHgj#@yfLp#(lZ&=O8N9UC~PDIUezUFL|9(mT;)yn1pOkRuD^
+IC{ebNvOoA)#0e$2LU%{A?QfIuyZJ43C6RM}VrxJmV;;{3c}W5$XV?8sagukX6N$T6R?CTZr>qNUR^L
++AH2Pn!vPi-vsN>K+*t8wN!JTK;+nR7sd;&J#iQyybv(y(e#1dV9rq0Uyl&<Y-D@I1b8)se@KuAY4g(
+7h_rnu$I7*3<Fkp#qe1Jin}R7DFNLQAu6W6Jyr2yRq8U~Z2TR(~ytMREnALNQZ4f4RI$B!8MIf~AEu>
+*v$fnI9vU)M#a1k#-eWmkzdt2(&*NYBKfP73?E$YEG%`pmftcn_lN`=cd^+IKv>WF57Wt-{;FXRGp^Z
+=tjo$-|(6Szuek@;2AGM%FiDHlXRsIaBQ0T~bb?$q4A2%~^5A)vIWgg90)%aVf4H_ugou1ctW1QaAfN
+!)Y>$8E!dh=p~G@gEg)F`{}Y&~$fp&#9H{fC!A~bSbK$m6&l_h%0hSu~H+nhK8YTI22#sZDW82F35pH
+Q#*0_&GvwIj8rq<We@ndxx0?A8iU|2aHxU65yk~w9{b0vQbx8R0!F5#iBzJ3+R5*REed<)vt_|71c^=
+uD%GAr#e&$d0#HU^8Nh1q2-DS3FB>xGTS~!9+hpx64(wbRw(IYKk8ea`+fvXDx9B{F)l$j|lam2WXU@
+ojMUM$38=Uq^QIqorNBY#N0AaB@LLQOJwpN+4VFdu*GV<%idp*9fIB~A^AmwJv83zHPJ23TcyLfX-et
+L28JRz5+EBRacGJWZ@)3Gijm90Q6o;*7V`7yl@RP)&L2brni3@dpk+A$YLW+fU(j0-y#iOrcrPaLNM(
+HWF#H&spqU5|t6LZW@}IL0CK0d(dm<t4OhJLp8?UZB-tnA?Yhs1!a&bMq`y&!dB>Y<Qy*slH0pff+;s
+OX)~8f=B+E?Pzz`;{EIxIvft{i$ejC8~9&W@zI4dHu6}I<5%$ah&(2TjA}gj^}TQa*Lk9yn0f=`*vre
+kdFkKTmfnKae#iJ<@Ee!R{amhFBAtHxF(TxLrfN5OfTxSLH7{{YiMJ=Z3s4)UAJ5OvPOq$eqZg{VvvS
+ICw5Uhjz_8OHW-d5rQ=5*Cx*x}0Yz%%d7JUs}qH;_#)ddeh;_T>CWjfp00H@NFR2p4M-j$HSJOct!kO
+AE%VokJUh)zMOw9Y8xPa~+rOrCieRB1{APpxPWy+fxEsjOdtC#O&(x5dgbtmLN%>u}K)KugJUGl>WWq
+QR8Vj{a=k4lKpa7}2Z>)o-0VfARBR%K02qv=Yp>Pe@;U4HmzayiBWRGdAJWu)X=_^YD(72k#IMU(&q%
+DzpR@L)mm_2s=vRNpA!@GH~ro|H0YoaFiLC{KErUKU;X2!n=7`xb_l9cNWvsV-yGcdCVgWYTaIH8#1?
+|Dzm>lhjnmaGiS}$RfyfmK`OL*XnOg;YwmkZy@nWT?Ss43DM&VTK~Z>EZ1x&_T~!hvAG&|6>$XJKZ{l
+d0kl%tUqH~~I=+eaoQah!1EKtTiN^hd!%EoywG`KBmBKAo44&mg>9}d~ja;~FHLAcsAyyGxLHmG`oWs
+ifl##H4+qePvbx?c|hNx9j+b&mr^|6?a^!OYcYakM5q%+Dwp1IO@Zbc4p_2i)OwVR|NS!QAQ=2Ay;!p
+neZD>4qX<ygJlHGpHY|{gsU2?0&gdb_uoC%nt5yockv#6Ti+?MTlL*+sgZe)F4U8N8Kd*cy$oC<pw*V
+fdqPB5MiM32l+3NzAc)O=*iP3k>?yGlaL(;J!-1ggAk-vLgTP^Tt{zhuW0~%$U*hugQji0f<`<Z{EB9
+b!k+sPV-Gv)z@!jgPR9otNjFgFj}g)byLzAFul2T%$Qx7ha8RYL9o?_l2mr;5Rz51({eW-dOo+_P!EN
+mo)_QMDbqZx0i=ZM@RV|mS*!QrY*&-}lOSC=-RX-h&TLxFFAM(>N9eRD74Ub7M5(x9o=<r~~1AX87mS
+8bDC>V5quEju`3jZS|cC!VoH*;rDTy}|i?oA^81W-!@1QY-O00;o{l@>?l-NJu>2LJ$f761S+0001RX
+>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFLGsZb!BsOE^v9hSYL13HV}XJr#NH}vQt%k
+*qZ@k2-0i`&@@5gVh>GV(GnfAkwlH6;<!cj*>^`$lw{d$iWU=sNT$g9f4{qvR@><a@LDOEL#?t*ETfX
+Cii5~YsWh$eB9SQ{@u!5B8ZONGnNm^>jt&ov%-d)sGG59`HyiBRMP6rGAUnNSNtLHev(ad@Sg<TxEMN
+wof)D(knpgUg>$Oa);0|9W8LKMjzZlEqXP)SZ73k>b$lOn5@yH8-yg20INfdxwz?|n5Y}S&b$#~8S4G
+g9^(wfF7x-5ASLvBciP}RxWkYg20&EaYMuT}T=D3L{hjD;*pDYNK&KEFQoOV_v*11nN<d~lXd<g8%#T
+t&3?(mu!0$n3vX@=>HH=#sD5qd>_?`kXz8;vN#2=du93EpdZf0Wk(;ZQ>-aJm-`rhR|j$lC?QQN1EL$
+!L!slgeYqXb;X?wHzgiji3A<84egRMQ{;{>oF`GK7(``ctaxEaxh=-j7*K{0N~O3VWDll*ZqaKFA3wZ
+<G1;2z{MXq$g!8kLw;{Yedv`WJn?&E_zLe?q=hz<;iDR-0{c3DeS47B1DIoRDX7ieY^|g<2Yh*7wiEw
+>&g9==Kq<<&#)AIx3dy$=2S+NCtNIUKcL1@^BEQ3tS2dGPu&xT`JDp@MQG;h5}k}*x5Coh65c%u?q%0
+~`*BYk+Fr;)EE)=}DtdQk{1SSG%31*kj2Q(RS3r-xaAHD{?=1b3a=1tKkqB&$=7vTHO8%HOyngu2K$r
+m#e&s-Z!CfqQ}Z>WT5EG82i=**5ZNZ5}!?w`i1^q1*NNQ((g-2qC~2q8mjK-LJ0a7gz6Zg1gDcCB+IB
+3xQfL7UPO%tFRGe_8!ePX_cavQ6uKe3yg02`rOiTxZy}O;DM_lP-q#VeHNkT;5FVs#o1idc&J6mrL2p
+TsqKOD$Hxzw$rRu>I9XO&u|)eViV2K|_flUpkjK;U(W%2i{-j;f^l=qgTg-MSc{E_zyN=MKLV?^A5PK
+kPkTg2#Sl$K0K5N-L-DfY<&k<dao?ey)@9Db4h(<8)C-5$`o0v37u`h?@%2SJDD-9~KWEG-?v}h5>R%
+BeqG)rB|&^2FUO5-0xi0)J$vm1|MZjxw1q0xy@#u-Y<a`daFOvQRov#d>l6(+Qf;6g*f3akW%9br~Z#
+k3N-W>je~sSw;V4wWXTpn5@2?vR2mS<*gA%oH_-lzc=0ATjMKhw>g7Hfvr07BS3H3^rQ=5!yU4XU{z)
+AUyES0CXF$m^&PeV@)78<f#l7vRzL_dwuMzIcox|E=#V)lhz4h)$Bn_ne^QQ8Pi@YE3RwhHV^e0?~71
+-8)o4*t|pw_{fEx>g1?QBtl}@$guL*pRQV}gc9hwLg|6klhY-IU6FNEH_6VL9?Z7jUPjpPkRh^kCzT1
+Cg0{ISFnGm>6_6Ih`t>xYKDDs0>GYI|&;8ojNkL}hr6uORYs!`8wFEhFck{`n5Z+iY`Yudr6`G0&()_
+A$Icl$o)YftfS0@gU*YZH`VjP78#I0=b&v5U9O;Lr?H(ZMMycEYBF@op09YE`mrCRw__5MtrdRP1R`h
+~SF4v<)^7`=yC|JhnU=s{TZP<gt}ArRvSo>pd-W(C6A(?9%t%zCN3tq$!1_RFN@E_lt(XQb^se&_RVh
+VwEh|GTXMz99wZW<4|Xf^})8GTI5ucrHtSl3(_N3rt{9(7M4inq49T1NZc35(-%jp_0>tO*B$Y1ur^@
+@r&-LEV3u{G&Ue;S{2%!C%_GZdD&q=iS8($FttUlqNkLs>Hx|JO_#d|47}M_psf9ox5W?lf<(V<(1nf
+4nmFmZV-$EoB`6Bp}>hlw$wr@He^-|JJQ8V~D$kbpZWiW+c$y6}R<6woENh0EwF>R&iNIz+6&YknkDC
+=GYlRY%h6Pc%hd^$7?+_f7fSo>Hyp6sJS3a1@1UksV0C_EXr=Ba$IxCJ7|(f!TmlHc!@yO87<$Fiqw`
+^K7f$8yg**nxh;yfw3;@qY-V`ApER5IGo=b5}xPqVyuHo0KLdCSAwFvZnTwzAfN1V8LT)Fizwmd1yaG
+Af=^}n+hvHC)RPe;I_rZIu^&SL|~B9Xjjz)0X#Lx2EnN{!RtA?B5>8jjJci~CO8hPYNLe4o%l0hE@xA
+YKEfDuy?DAsm9cqMnD#P<rkuDO^7QP-j}9!Nnk9V~Xi$D<_9xKiyF}aUH^iH!yq#2O3hkDcVVUbD{lP
+AvnK4mZs*3$~pdCfKlWFS*9Oe|m3Q0*CxVIQ~U!R|Q9rC_T1D6A`ML@vV*4pVHqU|_I-ZLKpDe-JMyq
+6jbhS?7rOGH<=4J@zQz(Dh@VR4JeM&^?RS<Or!Tl!uR1(VUPOuKb%C=XE#n;Nn(h&DU?k<qnIq4(=-=
+%-1K$FR*l$aT=5g)gapeVrQ@ezM-UL$dU6U{B-YJLB<QYE666Jr9GVa~kI;G!7rTg~?!VvtiIhnuz#*
+_u0^yjQ#^qO9KQH000080P~d=M?(S``VSHS082ms05Jdn0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h
+&Wpgiea%^mAVlyvtWpi+EZgXWWaCyx;{d42CmB0J1z-gx=DUlhkeV6vs^Q9ZFxA9Dq-LZX_cG8Rlk)V
+W{B6T2WMg8mVy$65<NJw^ixlZ_lEs@~E`@;K{R)_f+gG+9AmC|gZ^)5*ZDN4<FO<xPSIuk#oVyD?1Ex
+J{zQiHc}nf>~A@3XAVa#d|vU8PS#yBD<FimIV49_YcNxp8I|CyTTy3Xy7Am9=~4o5#mb-w2)w^<JjhE
+;NYw%O-@_CspYRfDo=T_Fkk_ChV7c0AlxR@6<a0A-=2h&88`zM0Pf{8|--c#Mh!uYWb7fqSi{5>xDk-
+M2#e@crDDm8C%Jcs{=iou}^=wvS8AxEDb$AIkyh`8T%SwJUsk~5VG$yS{8}2sJYi<y~uZqw5V!8^6V@
+vcwGZ|dBL?XRH{j}`Wx&uou3gaGLf^zLY7i57L!^O`P9e`FZ`M;a;a*)(CWah*`sPi?{UU8hbJColWA
+`W><BAR?nwqnY<GZm4L4z-hr?QEuC=mg%vi~{!b3(0-Kdh;Rg)I7UD!eDwy=N@Ll2P5Sy^ei+uihbzz
+1jj)jd{3Uu;qGsGQK|XEC0Aer>jLkNyF$Zlyhd#9`5$zvFWJz>8W$)*O}8Io<pA-JciVe|`7(Fb&=vV
+BW{RClbSe5JnFAQxp+6ftGz&m7>+_KmJ|r{!yhqO6c1!+2^cJ?5|ZzU#}@=XM{fBwa6^Xw(!?(2%F(I
+c74spYvAg5K#<RCOND*Xvo6zCJZQq4>72+ax%-G#%<ecEjp#ovY7Hy{JE*xVg-Xm8UxRtwbCuQSNDkL
+>RS2dl=TXMtCTs;hLvI9Yt>SXIs<Oj!33id-6%{y$jPWvKh6@?1%JWQY@y>oDN_u^{M4rK0J5}w3(g%
+2*@iYZ)iY#I5abv+5I`LsqSIk3kFZG5%eLCn381-7i20#Y`q!pM+Kzt4ipD$&#BuMb3DZnKl9w{#wpp
+pVTK&%qL+zkB)2fBF15x&uOCJnyGHSv>XRSsXdCV;sDb=z0!$#U}cC#^W!alEaCs##tsfWP6-vT78m8
+mbFAc7SU6oA}(dM{7<na4|A-L3@|F)0exPoWv{;1kj6u0KOCjQf1!;y0QixBa#3vvLP3k1REJSo!AG?
+nb_`hje21gvw9~|nah;fx6Xl^RR;TvjW<TA1B)?Os9S)OLOuzr{v?NSW;A;|ZSk(3<ZfpbNbx-#!uDZ
+<KDRx-Ko4vX(o!k8Ml-iiEl=J0?~U@wo<{E#hYc0#*jm83AYCB;&*M^R0U8yVF{y!Q;KQ>hDQp8S$uc
+b(U=vEnp+x$SiB+>^yGHFGey9_62k&+OVY%8%K&mN`)TBMf^x@{?caOK<Jucqg-h6z2d;b8|^#{YuS;
+xsAY3cp#`@8$wo5#h&<GaWIdbp*vR~K(*?CRnY|6Jjp-{PO&{dmNn&q$%J#{k_5@)~`^43C;_>g$Ucd
+xo2!PWTZHP7R-E#lXQ(z?N!U*VF22qhX;6nwvu?1L0bPc2!l_k6oBYH=MV!7FgD(G#n28#F8}YMYYq2
+TenMrg?)t<g|A?d7ee4}(_Zjq_zA1chw!0gF+8=O!pDZ~@YH<h14gbBVEhn13|S6~!W)Bl$~YevAcPO
+e@<gBHyZHTqC}G*)-T>BOb<m<-ppl047lCXE2ecZVkn!G(O^&1o)0nUwE%>j`1JpBkg#UU^yzJuI)4>
+424X-I+F-VP(h;bAH;#s)=8}?3tsX_~gwwzp&$&=1&eL!~wj!E&d&Ot=Z6n_;WX<rzGrrpCuA#&Z97X
+)Ge5S7SN?F6En$=L1K8GoQ_JWYyfPhm=j{rjpMquWct5hC(C0dfYf4``?8evv2+uDjMGLXD<6lXVIX9
+UsX~cimQaUMBE`lzcatl66Hy(PI<JNe!l3Pe!xRbn44tWEnBSr7gIKAUcLoPUeyGJ+L357Ko#Y1IviE
+JvWx$+BfGHyO5{LBWowSB$@)bZIKZ@Yf1OITnTmLgbF(YkxhE$HT>#D%ow{Wkx2#WyC|xZqZ0R{dly&
+Y^{}a$B16S=uQ40vDU2e&>X<e+v1~m@X6ntMd%^8vvwGwOz*_^Ayj#>Wjd)QUg1bHQLS`+P1cFOcU<^
+s~J^;(W0FM76oecA48AE_JO`@!TSA1(OZLToz#K#lR;A}ap#$IM?7n|eT3Jp{QY(I%}Agds?Ci7J(?@
+ni_Vpzq@AN*}}x0Mf~Uy=xHqRA@p;U8Sc-{ol#;`I@K!#-g2jLAvWB_}587hDw*>|CP?h|R9pMe?sCS
+9?Dhh$M1Qf^;slwe4H^?)W=&tWutC!l1=FOIj4gsiX68h4zC?L)mOsKo1y0@^26x76nGo;PL7C9#oJ)
+`jc~&ux~k-9GRn`$0)V4)3A_ltEQwZ#Tg8PGXPs1ARiKXGhZ(KV3r6m3Qv2t;5cka#?gfGEgBP%tY^%
+nz9O5(PeZ`pz;S!gCIy==S3YHBgg$R}ghLyLpevP-jSA9nfte#$aa`Vj_GvL6q$+4Q8epukVUS*=aE#
+6&co1bRwS30N5_Y=(aKnCkd3E8zd$4+am0Y%WE%ZeTJ?1e7*;3qLwkqhT1)qT@$Rw}*jhw0J=La#@(1
+me9p`s7dug91*UJ0ciuFqIrH1);-J0e6=y&xe{PPgNzK8QRyx(XpltWOzeW}$%?7we!TCi5O*1UaC4S
+o^Y7uVB@SRlv#JM_ah=>VZ=%AoQ|<Us5OVQXG>*ua-U;&@;M+vjDoAbpZz%&HgTP_8Ulku$(g*!pONq
+1JtGaQYRz!n*lvDCZ>Vm3>3jD0=-rut0q4sjGzJ@vc}bW5yIy^2!z#S=t#RVwDF^0(v6n$t+x4OFLJU
+;W?=Wp9Urq?aw`k2kdxN>#wrTN2pegby4Jp8Jj)~+8Q$d@U2SQO6|?{~u;Iwt<uX8SxlFte%#=ugxRB
+}JRC~5<(v7KZiPm=j(ui1sg&<q+5t*T$%uel!HK7Pa${P%GHAKdu#@KkvpTNut)J1?v2qG<b+}q9I@Z
+jD#xW1m#wLBWoaja~GS6jNG%8{*KdHRf7QIy=)s^CrlVR$TU_CG`<;P@##YW^&2E?~VfX>E<X$Fd?lt
+3kv_byH|pQw`M?UjwE{Eu6qG<ZQn*B_LTGP(rzE3z<n*Qgs)|599<4$Af#Q8k@pUbvEgjXHjlIpe(^*
+V$Bo42f%m^YRRzH3H!qPbeN_9frRSgG5H8#jgnagc@b>9T!KD!<zsi%LMPMckupa+2ps~)*)urSbB1C
+W)IK5=k!U~|2iFgmLo0ag2kdwa99Oit1G@re;v2f_4ND&=X}bH^9yY46Is+DcD{y)F&dMsfv`<OUoOI
+W)3g)ihWJG&nCvgHQ;%Hgk{L3}FXj5Wp;~+~u6<p_)+Mc@_^_+d(tk(xuK&$bjn65z*g>E=|gB@OH4_
+zWd*?d_UbU`%kL>`B8Vh22t21o{n*47X9Xa}s5_`)9sSeQ6<aRMMOkZo9zNXV)Spm-LLy)(@cn=)e13
+!I8e#DIiYLr`+^_TOgg^5X3!n;=s2FTPkc>pC&;l1i<=kagXNw_jfV`|qZJzzLA15rTFFV!brRWsC{I
+Tw*w0qv670LeD<*L4xT~TYRx)CISa2)cU|pj?21LI$C$+B;XZ24Z&&~9|j{8!=nMo?cJZhy`8hWOi;w
+T7vszv++ObhQe4-9>JLp|v=D);Ew|V~Xyi)@k_NryWWpid1ZRk@nHn6_46&`PA$v@)+23~3)dFVBghO
+LoLlY(7NgJK`B@2%!{(wa!Rt_0cTS7Rfs7BXfDKMEYr3xCmcX}`2L^`nbS1~h6rB;RtXX;EXreo4YNt
+LGwgm6aTb4>r9k6K(hwdO3Mx!qv*sHD*CZ%s|mP;XJBfc_Rv1=Ti7CbD(>puM3NW~>m{N8-<wgb;ZTd
+Qt81RiKSgf>Fmcn8bAU|1P~?TIQ(uj7c*JT(i3pBX^)n+*6}2j$<$>G#TG?c3`EdCs9JCqQ;PXh%np0
+E7li)9H0<oe<%d6NUAq^O+Fh!0fjmd@O4<(B5XM7LR80QV4|}bZQwENmUMa4;8`MTGo3V<9>FcB7<#F
+GjjiylmjhjX_SxGnqe;+_eCM6(p~4Y&ja4kno1hoN42Ee?1xdv_GS9ZPTY$lY$W_ZO_;-P!DT!?`bY9
+_ECm~z&6?hHUh9Cr?yzf<N;|Q`}A|(Q+<dcd-Sh-@MDs+DY$kaTrl%uU3GWp(?AKaS-q_{~N6;OhdHj
+q6Qhp=7^egb_4h9agN)K%kXo22voh&!e}2UlE2Oao{oF2v%O3uS&qiB?R*+?lXlk`NnIHSqYNEs3G97
+IrCylLKAuwL*pm<{(9p5zwv(G>CWi8_aOXnRwHI6K9?xV0~NUV%_UkAUHJiGLK9!h1hqd<_|PaV~${Z
+?C~%yY{)rrHHn*|o-xp%&pvZ@W%0B>a_avG?h?y*Y);_gMUEwNZCN<lYPp;_wddYpRc14)l{sLx%-;@
+T_MqClvSZA>*m6h@0fIM+gXL^naRlW=tqg`h+Tj?i>G7iSvRf+1ESJOC1BJzv7(2T5ze=|C+E-npB0q
+Bo0p9!cm#aCZHYR+(8p!&kiGu{J&bIE?%t(<tu$Qj}n*=J;PBnNH@_@TWMAQ>=6xgmQnRU+Gq(WQN@3
+MV2!r;dJKt5x0b39`g9X%)(SmvoOHQKLLWR7G&;I$LPskL_OMcEGD$+d1UM!EToldJ9QjIz@lFJM8FH
+0IyQl@X0SxjsDU{aPtLGXj1SnHiLY1RrDUiWz)6Iyq(g%$DQ*wojxVdNzE`8w^E*ne(O?geAv1tHDcC
+N0Kc{L8L*UMLSE`?rn0z6YW)F^W*azZDwhV@Sp?g`HK+xYwAv6QAQNrsEK<W&WzcjKXB4((^(>31m+^
+lvB{aD_M;EFi%L&8QlFyEKY?>CtDnyu$}ViDW-<ya63~RnF%>3VUTpUv1sF|V;=#zQ9=*U&i~C8MLp&
+dC!StN!0d@>^IJOAiO@N(68aUlG;;L#OSMUi2El-u)fv+2R!M|yeoMtjAd)tSM=mo)eNymb2jBC8$%<
+XUnfMQ`|RZOAme;TQ9JQEBI4))riUKW_4q;CR3ZNlATsVg_*_)Bhx(QgpdVJ5l?$B*z9uAAa0_(hae7
+bmo3^~1b{@FVPnA7U`tMP4Vj*+C>I7H-z7T<d|r->?t(8-^LyFCd`L<+_2$34bYM)?-4%&HPe3spY+F
+-ohV1*i5OdHJP6u4QDgF!81?6qV;w6l_GqvR&2|~c-uQ@b`}1!lVGdy?;&P*JN@C}$4+AX()(~44wPk
+rP~g=s2c6*m#u0+s;qO7-u)EUCCAo2~oh@^;9W4knSC#4&-a1zjZT&j08dg>aJzvlp5KUnP-KFyb=O}
+D$)F1gf)p~F*wA^3RJQs_$dFt_Q8WEzmCH;@POAqGdl+I;UoBrU<{hvNC+bI*Ee=a{qSTRl>9cv`Bj#
+U0fe`QFonGuQ8JLpdg_w3&}$30K;g?1d`c|8F3e?|14#OYTNiHQhyFo4ki>;pUv>1QkAKa=)0c=nMFe
+NkJUc*W?!tEUW3P8UF;ermWNHe2v3FAAOP3Aas!d$Cv8HP@b#M;}Jx2@)ef^J^o+B-eN_b%6$>fTCK<
+^yJWPEV3K^wskV{!#ITD27+V5I4138!)fcedu@zhY^xy|O(UJ_pI1O$*cxqv_1g&rD&uc%-@PCAeViH
+VUeWJXY+$wSq)VX!zDIGCsC%ximCFq_s30t@jAZI+D>l`h#6Uk$rk{mTJx9rBZ2MR*$Sj^;MUMB7sHj
+uzHSSFv4|O+|$Hl^@5MCsx83uWHt$<mf?+eXY;J^pg+f?;}J_fCPp?G*Q`^PJ2d-(ILMqH%5Z7Ce=qC
+F~qRevq#Zd@Nn#a`s+>moSIQ9K{`B-U?w09|3V#0~)#(s3!;YeH*`c#Vf$ry=h#DkqGt*~+|iOky54z
+Be5WhV^sEe?dQg?{%{&Jsd?wkx%?U4X@W~Z>*2=zfem91QY-O00;o{l@><;00002000000000v0001R
+X>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJo_RbaH88FJE72ZfSI1UoLQY0{~D<0|XQ
+R000O8^OY7y2&0?NmjwU-n-Ks2IRF3vaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR
+LrZgg^KVlQrVY;ACFZ)`4bdCgZ*Z`(Eye)q57QUsDZRk7Fg#egsbnU@Aj)1q<F2PY6@>1?x+MUAAQX2
+bsb?nud!ET;j|VVGVlk;jjB-`#zrkB*K;SM$qvEH3p1a?O)i^L)(;Au^vOBG-~HOARSoJ}{8{E`b!J(
+lX9RqYI@<U@s<PpDBPT+h|=(Pfik%La-KMok5U@?MZP`$}Ho{*C(77sFM_Q%(Gv38cxQeBlKXT<->Hu
+@L5T*H8??%arB4rZ6Ty)Z|Cz5*JmQnA<<mqBlwy?q1lC*ok=O=-g?~cv9-n~6YDhvTHCBaU*ZTzJDcl
+l+f32>$PU+WJOcayIU9{eh|Izk(2b%z-a-_4IG1FxQj7@iFLMt-c{U>Ahkh~C47Ny#VsXv}ITJa%$Hd
+UDSdm5JYqA41N!?+Z7nN)f6Ww))Gv(OP6jm&Xc+PbcxeBtC&q@W-XGP_kO%dB?FJrk@`1A6vqPN=JuF
+Le)=8U7;467zZ8b5u!Wath$k98x_ZG4S2nG{lJbk>#|#Te{>$OAH=Ri8QHRI2exie<da9vHWnzrKHWd
+U17gL$gFZTM4<1bqiHg4N!sUsDw<(#0=1y;9wxCEHIhF)KTqcD=*j)w$Dq8fObK{N7yjapO$fD0u7BP
+z2KuFmY94y6DX{3?K(F%j?X50dmzU#auIfxEylM&Sx^Lfww>K-%gJru0@(_YvQBB_P(@T$rZzfxf5AS
+0B(g2JrbaiIa<z%?z~)#%QcAG%ZDj^*PPHJCJJ21pZK*U{0=uJD7X{?3+VBjRgg;P5s1YQl&~6E7(04
+m8w^6%;Vn2p#va`S*4N_Q^YuCBf7&R$@ao6-=w`Xz@u;bKfn?i9rR>#U=$2+9<aSv^4t0UM5rTtz3c;
+0A-v4NOG6>0wK@BP7r)0UfaOP%^VK=JA{<M-g~jFFMZE+C6j+(JnHr;&xOkt#Y?RaL#}C<r*MLlgj-?
+1_>~QG}g%NCfRSy>$Y5!LA`sSq^KVxfQ1^C>5_^5MJv!1?Tp7bxcYO-$sRlL;^Y*(QtG%JAXgFIGxX;
+ceCs3)AO0zZ6MD(l<SYSaGK4hHFo{!_2LieDs!Fl+1%+T&u3R62Y<MbN;|a6@c<ygu?E2H8EjIbKjn#
+b@r&$g822%!EecaQ)}?W}_S>wtqdp;{%dryq6EJ(hTaUiIB#m`!_FV))a9gjU-BNcl9(%pae}t{L2h-
+pZRv;ly@Jm>=Hyw^#idykCOJeFV)8<Jglu`Z;nuV#|5X&!EiOj8lRWaTT5cLk5p#yDHqQ`cyDv)hTSH
+C>Xv7<Jk%+h*;lvP}B@RUk6ah5^8hC$I%`s-?q?&GX9*|r2OI^A{KsI$);@A1WrUyT8sL5dEs;WpD=U
++@)Ei|HF|n6T~<XF3q1L))pvn`x`=2s1RogOQx-<H?vK{WEjtA|Y@-<?ywz2Q^lO?3Y$uSqCJtecTRR
+{viszp`7epclMKGb9&Y!4|}whANVbc*OaN7o=Zki@W@lOpq#xuy}X=VowtHAU}rCxTSYg$fij(bn!lZ
+0%`eWn;<WGGH`)2-vpcrG7sFl?z83}WgwIFlr^tKabm*Q3O#=R(GXLMn_u+Ho*>dhJrl{z*URNYqH`x
+y%`)NELs8PoPCF$q!WGF`i38^Jm!WXL}bsnayk^*mI&XYyu?C$rdo6SYOqqhfl{cONhv$rLr&B>FRbv
+R#-95@GZ(B7sP28;h%xY|J1YYDoPxv8!8#*MW|2V4DLY>Sx|sn1M>E=Bqf_R6&WLLkXOg|CP0DIw(&y
+$I*BL`$R&q>dE*OEp(K8tz={F~Q9Z_2B97Z(LtZ&`<rvYG@b`@N5ODz1*?&Y@mkHX9Mgf!5jSpP)h>@
+6aWAK2mtey7Dqx+F@<#u003?z0024w003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=
+kV{dMBa%o~OaCvWVWo~nGY%XwlwOMU*+qe<_&R>C!=SGxIX5ysnOx5ax&ygqU*pg~F$z0P6O_7j<Gez
+(K(6)MW{q5ZaKoWeBlipl4_DCcbyNlgt-@w6Ouv%S{C#H5}HJ^oNvdD8T6xnb=mIt-t8F>?sE1stOiK
+rcwl(0-HQN)VzOcJ&s2VRgTUZjbavIj~OCy!LH&4D22m<q)<ERGZf;>_VW=ZcPyB}wQeDpEyKwic0KK
+$vjvG~?<Nez+V+yrc00%eH^jLNiJe6bD36k}y;#N<kIzpp}SHmcSyfp#rQElBS!$^X_&`Y93N+vC(@3
+<|L2ehiI$yl`2Y8nv6Z~l;np+PBYw;51bUp)@FMhoWC8y<1E>p?Uc%8oHClJHqqPQ%}_ImWOm-+5d3-
+Qc|XFQ;wUS{qE>rD(bO;I`+bz5QUpFZ%c~XnfrE-trX6jJT-Y?&3S9T7t=VJQ5onO6-gr}F@%a8fg-F
+3GZ-ba;O0acM2?$vB6OC2KGQ~qnbx$^e@3o0bKgT?w-kJiMsCL<!ZUF@Q2t|?icXvx7=+6b%LIy<p1N
+XlMNbn4SMj~>5FPWloAr;@Vf7APWZ_kq=1?T`mBSoU@KseY(5XJVC)Ux1do~O7wz=aVkNg+}2{e7wN{
+m6nAWRI;ul{k#`5B#vb4u_+5s0jZjOZh{+jy}gUS7fGTOoia07Mw>?dfptO?qFe2K<tfS^W#c;5=j#X
+T<pPX2NQ-Put`Fg0_XrF%7BLG_MUb$(^x{IGXeNBwv;3iv_?C`#)ZKM8VgNh=}44HPIi$An4E)W*dry
+9C6v(326jpKh9pcz>lBwlth~CrUU>l6Tu>>|5l#*uzND1gU*67crr=5iVRs)6LHf)ll?Z?fkqjo?ux%
+k|D_ck{&TbGk6SEv~APWGD9F=zSBvpE0<@FvPy_;TLe7L>~LF4dpwtPRm4X<abyZd`cBZ9$pD5!N~jP
+TLkk=|MB<9TBsCr6l#0pYYqZEK~b`XDX{NGLNlCOVGCuU^qCcw!GMrwNM!F1D}m>eY%0$hYA<+S72BB
+^K~{bu2I>jAg&ZMtdOykHm#w>JZZBJmpM5S{e)no>v8h4spmXfs<VVFkzTe&_ZYaGUjaTtUmnk_wWMj
+F~0x_s}IZN;`Z(ZUd8&SFiKUJ!Hy~(a1-Elkt*CL;{Jz7(&k;yhSTbhspwgU6B1IRw}=?Tj}vM>g}qt
+!Hn9ir?`fq>x(qOV#X$mIv4n8R@e=sLS7<LAYt$jH)GjjiI70kF#f%^U7(&V82f(DI2{$aITyJEga~L
+mFsTU@g@+VD!!ZRGex%M{j&Zw@%?I(p|spo}Y$}|kggnS<KB%;BH4Cpf`2lsJ(b^nX!onX!&JcGP)Dk
+$ccQ(atj(SM>kC&B$h2bXL*2mtPu-N1W)u?msh<Om!<+D()jL|7d#^p{o5#T){#g1Cd!C_sjy;tXnhf
+!sqI$A~|v`;rOyMY|n?dwILKTU;)#F(4n`ydAsl4$QsqW_tI2@ov;z1CjZ7-dv(@RV#jVf}^8HP~_Ny
+_`h`Y=uxB8;@4pb<qL&*s!-G^4yG`;lCalu_j@_L1?w*6)24`z)7#nAkNUx=NxN9y%@*^E>t6chbb8y
+Ozn%W$!|ZnY?kIg##af^5`xabX%&x=P6)2|HhA2Xt^++Cty&|@C(y0=+dA{;Ij|;X9ksz!wr|IpbA*k
+afL_SX<%0nBM6DO#G)<fVA^@sG9bmN}E4bL9v;S9hU?<y$kF0KOe*%}hE$^2!*9SltKaa=U$)mBFL9n
+RK(q}-&@voEwOML%TFIPV>8v1G3g#P)uD4c<3t`4#*2Y2EP|=znr@qGyD$X$;{nA~I#$oq~k4F1BYI_
+6#<(k)}&3W}dR9xZQ8y#VJHhtBUyD5>lFGFt}LFgL6!!Dcu7HQ_yfIY9=~a)^S}VaqS6KdF-ZOw%}0h
+_e}RNJ80zg*sHEC=FOofYV_gz=l~7YBOO_01Xt2WY;T=$g=-opBztN_KwtJm2iWgX?1`bFTQh~z6c}p
+<MyuFq^1#G1^wX=H#uy+_sDc%?;#o`=o1l91J^5D^%yk_LK4Ris0nuf3&p#iL^Ve_wIQ+eRhd|y^6*d
+|JU$+}*IqNg1Rx}bZ&Psw8+Z|#bG9)Hp{XEH}=_sHQe$jWJiouN8UytfOTu@brtXgiIyrmfxQL67?t!
+V+vboYoBhW)OHvaO?VfSakbgGedBc*9{W5}pCiBlJESx`gGP;HRdOI{>4xP))}+HR&HFWYEO$VBAR8*
+c`L0usb!Tt;?`6hpeqjpuR=8OA|4GALxh@-2A?wCy+U6;vz>u(?=zi$my&C^X*#>qhp1o`@voe5j<)5
+YFO_I-D6tJ%$46XhQ$bzdN_jE@NvnA^rnqoE4nvPBtTjmYE+~&(=3O*JfM@yaC)C;P0@0f<-H#cOFe%
+9A~efkXF2FzaloYpni>GfeAVOm7j?%^83X}L)-+3eh<v{xqS6lVY)##XADfFm31pJ2bWQM0|LdgR<?O
+8N_*e?_2zk=<!abO>m>fuQSyh`e%{?%(O$rM4(F4UMI}~&Sb77bWo`WH9N`_l7R^tVvY)q~Oy1J#^LJ
+p0#)E2xH+i!RRxhg;8hoIEdNWvTyLak&f#2G5xjGP)dvK{|O7YiJOLlJGafH%rFa&jjM7eV@Ts$Se^N
+18-+Ur-m;`eBbKB<;D~T-Y=hEO}jT5pQQ)Z*bU2k&aG*B^=gEORm;Bdx*CYl!6Ubn#tLD+W;*szMqiu
+wmfHW>SGEXNV%lOytN*J#*dc#G!k3D!|Cb66aI2kXdD6Qm6+rvZQlf%S(}ZM_H+~9X!8&95+<qkaO#B
+YapxgkA@o25nM{a(2K5$tIGEEx8G!9{aW&{kBGy=IK&pG2rjqs$<B<H4{{N(c-)fFu+k#^yQ<n;r@TU
+wy4U~6T-cB#4vyanv;p+W|yLXFE^Z)lAK1G?jg}JqL;HK9mBk~0QV9tr1xC8U$cjSC_@cv~4O@;Y)qX
+0H;2YSedeRV}7NAxcb0{p?9KCXi)8wNgM+E9E3*M%Akzr`L-bbbY|o%Ln%2K3hvB<o$7II>qSX9Q<eN
+$;di3FRb&J_sfNJo>eaQLe-FX$JvMFrJ%QcA~Ydf;+BY-O_xUrt62vC&3!CxBCeJDtMR&1*HQP4TcUr
+zP))N<Qs8>AB!ceOn*UdLaNK5+4315X9?t~m;8X8OY4ErTlXs_iMQR_HM~u%`1Ki=j=$7O==|L-Hf4T
+k6^Or)^Iqt7{9omv7hvBf;0!BJr5bny`*Up1t&P?!i^L(szISpJ0ZT{c=RdvvOHcK{yjXSlb^!F94%I
+pqSNHA>A2g^P9eNj(@Q%eHS3F3Hecq7W0Y+=V)$DqD_v3OJLa%?`g){2osBeY-(x5A6kHMI9#rggK-)
+-?sH>V@bFepbqjH_XAIUkOOP0_8(&U8aQcLfdJ%~v2mpC5MP0Xsnb2>bNz=xO%pF8ESi{put&(_6Oid
+$hZ$*XjQBH^SavOb5<yL%1Hd?Wtmj?*j3x%8#>W;|{2eLWCJN-w7nm-N<@6#2Lz}Y<+KNvEskBoLh%M
+K7NhJ13jP~lQ|@bkv<P;U4n)`OLN^nvP2rlmISt^--+zFROqo67CMKun#$@Qt7p<|Q((kUFopV^Bi_%
+>#g}1uvQX}!HoM&;uq}T9%1SaEvTyW>;hOHEM~1I2VbZ`c#Jt>tp+nC+_-@y{CqLBZGU_=1q`9H2dt<
+-kUTI||k2_0qp<fBZbF<PCHnyXEfa7EHjJd%Js=URH-#VfGT(N*+*v3~a$NKSwRvg8oJW5I)DL1d1q+
+c!`5$(yBdcb4dtErT@L7+0Pi3V$EzAC-QcVa-kBef_irx{|UDeSw-QdJmdce2dzC*+-eQ3@RmzWj4uk
+t$C1Y|9j$2f8muiO-~jq;&GO%s-1Io|<1SZkD&x)oQkw<GA+x<~4jgR}Y>W3w7u#T~_q?8`biSq|4Df
+32}*k<ec8}hSbpwC0hc0U}vV|VM(jGE3oFX((!9jZY!Xh)V8FCQkepZ&-!}BE|h>l!owX*R&ocq(0>s
+#Uzr=i-bN7j=1vE2`MZn-hX2t~pmT)tJH$X`ra^^z{H$R*d1##VG>>+4=`b$-FHlPZ1QY-O00;o{l@>
+?8)D0j%0000=0000w0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK}UFYhh<)b1
+z?CX>MtBUtcb8c}pwG&sB(zPb)1cElQ1#SIEpQ$S*2UNK7iu&nYcQjfe2LxY9uC^kCA(#X0f0i6zMy@
+frEWC3%Utsa#wDP)h>@6aWAK2mtey7Dq<Grf2X9008GA002G!003}la4%nWWo~3|axZdaadl;LbaO9o
+VPk7yXJvCQb#iQMX<{=kaA9L>VP|D?FK=>WWpZU?Uu0=xbS`jtwOVa&+c*;bo?k&UC~PkjC+Xd`*x<a
+l-EE2scDE_oUU3H_FldRkd6q;iDcSWc_P^iEkd&ymv{~TtA(6=8aNhXL43%^CBaiom(j`+HcCQnmMe<
+t4<#fj4lpDj!9WR-#a>G>4-kr)F{CP3|YXrZ(#a}P+*DJ>JgcYaaG(S6M?_ixUju<8<z(tsnnM#&|C}
+kci4ZCs}XFN?|7r|0aR8^7S12Xza6(=pXyOO=Qy84d2=ci1;>VJr`l=)WZh-r}uZbZVW97HEJzutXhz
+sOkRMj($}Sr#{!m$6F3oX4d+z#hPV9;@t9i&z%ITz>oN<;(B>a(Pzj)6E%!pN&=-Fw=sAtQnbSE6SBj
+L|!s(*s6Xz6Q5&Il<bbEZnaiAV3AZ=c3OQMqt5OtzK#`G#;y8UoXjUFjuHFB(@MDKyHaHII4VW~zBoH
+Ov%U=do4~J`rJ3M>R5{lt=WdaSY%R0_M;W1UjwxI_A^LOnLk=@Lp~0UCLryH?g<*RTl9h@bNW{4#{s>
+O#P#0PqKx@z+4FKcWY8AmtDsuY}tGwi}Ilf`Q09D8i1cX_L0hS2}vIK%i3oGIUKf<#o{!wl(?lF9cxR
+e>}xlx*8E?kO%V|XUeCpfRgwo17sX~{AY^=T<o3Z3(m8If-0s}<<q5TDxSNM*i9Q!>sHRjtz#jespuX
+<BN<Qpa}N9vOqE)ukb0h#aIo%5nz`4TO%&MatkHWbhoa>&~%U9N>_EEe%Ux4Bas;chWEeQI^ULC`p#!
+=~jWacG=9cyXEnY#9>HXDu^O96}Pehbb#l!AP|PSxM6Jh?)~kjh`qi2*T;zceEaVQXS_sgz`NkOa<!V
+G=_2+G*INUBzS$oW<06uxax%FCI>}S{UqlV-UKc^u%yY&*$ZgKc3S1~n*NZi)A#X-(U6tsfBk+Y7*j8
+vF8x6U_GXMwyLy1UgK5G&4%22dO-8~y90M~0-YLx6O)T4fuWT+`>JY(Op>qc7=lS^R!@l*&pz|)ENT!
+<LG1ZOf+VCd^c(1>k8%IC?1J#To<YJN4C_#YRiTB))gccAe@S!xN^5CLusEMp7i{wyEZ(9d}jaXG+kT
+Q^v_ym`RI_!u$!`>o2w!_bC!o)+x)Kh8SiHakq)K@-N7@p$6G3p^ZIK#Iu!o?wA5^MF1KJ(YwrQyH<-
+jQwq~_@1d`DIXT6#--=%gH1<p7;<rBxP-+3_>|gch|bM%C*cTf6^@n^sSsOuawzrDK@WK1v9icl_R!N
+o41}9CMq;r532`@2_>%Mm?3a2T#vV*jYy%Xbac+7Z0+SEL3I0F=@e#1Rdgx6puOFJ(h7GuJ3A>Wtk<9
+xY$7&v-TY6q=ITui??_65$X1rHx!k$+f7T%>Wf^I!Mgid{p_()#O;h2Co@R@vDVQmvcDFFONI!6VbmL
+}i^b2%n0bpUUzPu6jik=!Cm5BO35!iiWIgGmawnksg0c%;m{Z8WD$V~qv?kn0M#9U}Xfi_g~XE$7S&R
+ZNH1S&!Ch+XOgb9bMD603MQ~b5hR%Zq6AW-=->=HMV~w4&zE|A7$4~{D8qzVJG2G5O@PcJGxI&qzTb1
+U$mpwF{LSB8fc+0T|#j=rb#gGwDBW<N~5d)helU{MlsaNdPo&JF=H3s6CguceNVAUWfBIMu{9}>Hd^&
+E%*F+&hZh%N=wVwBPG{`}RxX<?AU8Y(js8SB@mfA5`%(2=i;B<^*sw7y9HQVDBF-&7YJjd*F$2c)bY$
+u29uRJZEew<|IURQGtj?wZ{&isd9lY5p#V!=sO|f6@-rV~#WMi=!>md*VM%kOz?1rb<8jTew7;OXDfo
+c=zLR@LmPDCOr%F|@_H4*Q`YjY>u97!%7UmVtq&NmsTA1NKAsv`glF)qmUz($CBUXbg4#ExNRYrU1GC
+8&jE;u@S~JR6-nTF==Gzpz1xAU==+Tx2z|<Qtfa3(7Sc=r7eV?4lAy?oUKP#Yyg}58vuQJjGONUtBc7
+k6q3XsL(h?y6+LFAA(?2$)qbIl5|g%1C0UJ9!^5$c5H<G5}DXS1W^jj?LaDkM*_N`?d(Krx84CA-Puy
+s`_CR1K#y6oo+B}!D8T^^jTlo0ck%Ivc`3_w%p2hH$bQbGWBUzToYWhxFm1-Nrs$~snC<Q4s2D9*Skk
+A8gi!3n3Jf&ZYIGsp5#1uen=pg^lO{k8cM}BX^pb>MLuS7vxPKFX@ZAOmv4gZ1B+y%yJdd$wfsWxOkh
+oUn_6DogJz~i%+Ox%JN6ooEgZ6lLc{Q9I-3Y^b${JQW=K$0}xf9c{C5GUNj&u#)-a#!5tqM3U<_2`5y
+9a3;5K(D-(}?Ccz47NS<oGq+^@}fb*Uoh_R`R{=%BaVF0qNXGN^U8ev{-b1Ce5|M^m)Rz@*wgVHssS<
+jwtBKp;))+6=$9-9A0#WHvnvU<#%khz$gX-;Ra!%JZHZG8@zVgYUn0lReEAa0b|f*7*j#|27O_hwW><
+?OrejS)vM_hxCXUT%hZcS*VcXo$r0p9SSF2+?I}^&{%zJTB^^c*QNM0%0DWOe+Ub!@4Tpe3ap(7$V3b
+e3u#-OuPv5XxN;u9An!ZvMo2JKEAO_Xvp;zd(xzMz~F@aI{fVT?*FXPvADGb4dP^u*q?v4Vl6EF_e8X
+^SC1Ak-ZS!P`C$-ip-AA#QrTBUHSHy0aTrRBn}87hgV`(>0o5Oy9SUjv&$#CRc@kauixOsNbZKUNyTx
+KMe5(vi0F5dX-bwr|>~Do5nQ_oiUp*pm}kmmI^=mI(1QCgUY?kiI*|SQyaORle;2u?9*<W}I?%eYlk~
+AYHrqQV<RKXmvhR1v*BJr;I4w*pS08i*-_Qu%Pi6jBf=DL<zJq&{!zsYk_(}hTi)dr!XTwZbOo{Z>KQ
+Ay*6J`5}Wiw*Y`#C&3z77{Qk#4OJC@)oi93<6ESqeD_gD0#MUcO@DZntkTo&r+1hzf;mLd+bk{BVrRR
+jIUypa-qupD$&2xYb%26(!2nALvc*=PuR;y?>C><<&t{-1$Bh;Z&!(}5OlcpU}@hFVw*!Esh#bV$B=D
+mv&{FVpy1*FV4-Nhqj7Bb#LGqnu@j_Se%TP~YY@1b2723ZOc)QZY5gi`J5(s`_kQ{dowF_E-BopzwP=
+4a8)UQ@IprKT8+jE=!0HnoKpgkHU{cizX=1lK{R&(LiU=}GlAGdqr@8PQ_JH_%(OHwwFqztfm4KI>hv
+8m1f@r%*cjYSN{+9_&me_pj&mk`GIuv$*qC90xuK*_&``gsAHNP;<fz;u;q5TLqOJex_todspjVgFe=
+g1S8O&;H_F}x<O!1xL2hU=fj!Z<5d{z;<p;7C@uAJr6XxRMJyKh3G6486<kY$?YaU*ZSi&g8gqvIBx*
+#C744zf-(4`aSJvOjGF}vP-|ZRTr!L20JS+AqyWmjF1|u75ND7$60w1l~_TD>y5-c!napnekX0(_7Jp
++c;&Bds79TyLs^>$ypE}@UkIh4|svDa|*|3|!PU6CB5I1V-T&+sn8&-oK4%8OJ&v2JTC_o>`|UqV{Em
+4Q=ezb-%P>frxPpm&!7W5dn9mm$1U!H=z9*xi!%_HX_<VMv0(F5e3f+_F5u3bd*xM*V*gPKy5lP)h>@
+6aWAK2mtey7Dv-q9<f{w003+)001=r003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=
+kaA9L>VP|D?FLP;lE^v9R8f$OkxbeGx1*@Q_e5Z9c$>9ndHbtGp*|?Y3sqMYp?R6p0GHr7si>^q=@fH
+2=of%S3%TBtj5kw^CJsggPq&(02U}P+f6q?Od%C;%AlLd>^Ofa+LhNYWCGnKH1oms-;jPYc~&OOg-wN
+9?odY8(@(l9@2v$MCSZ{NVbQ+6qmuY4teNEnf7l>qL8NLNy82|T1`OOcA{jxAE27%>ajJQaeeIg6G&U
+5J1g1#)(5P1H<H4VMY(#X+<64lr9MW(j<>nwu?81!={#R*~eukj+%IS&77Og97I=2K#)o6wDht3a?F4
+W`f5|Cdf{(492z+jNO3oLYq`Z$Rq%)DBjFaiwqpgm2@JIoLr%SYwKjA!3b0}U@J9~b9@T2Y`vMrQZEB
+GlgN9zF)*lcFcJw;fhB*0nb0DRkps?*HMh`@kWl+II@LHgp=7pQsuio)0$!SL(gb7*qM0f1TF^QHnO{
+X@a0v0|Dvs6G&V4eIXq5g5=4}EnK2=YG%(1hbC<EGBC2$tj#oRiOUUE=>DxA|mGDHImwN5gVQZ!l{n1
+2Z~uvRHa#26@R-$4y-du%+snSAVydaOTY52N8P{p;Q}^SWc`djb2{pWF^VOc-E{x`WAQHoRfo!Dse!e
+{dbJ-lvCAZ#-thQR}3C|8Upu!C-%Ib@$=AKX}hBfp9RKu)F?!e*#=5LxvI^uYPZg-0pj$t6S)HFZ*}>
+$>)IG^d|#g+B&%zj#!sHbVrl^)rY(8h&_B5Jq*V^P~aN)4*G+e5s2yC_Xd*=h=noM`vp2|eA~UdBU#-
+Kp#R8fcr|?ZJnFx{ov_>C-E|K}FMFU{_wugCNEX<2b=U3R2kg3g-+hnzjo1*_j0nx?_wlxeLtya$NLc
+r3(jN}clB?lhGJ<XZhK?pV?Z^JO7qITAKSrP2jE2A;T?!O{(jTB=gF(--L4UK#v;YqFKa6{YHrKuG9W
+WcC(Pd<qQ@O$kX9@y`8<l2#z0+CK?AAiJT4BgHu-4KrWEafa%PZ{N)D8V*P5G&#(#4-WAbSEh1eB#a?
+VNSqdW@a0k$936r$3xVfBIoA=4TL_EeJ9;NjDI!bGC)JhE)S;j}lIi`OZ77htKE04z%oK>}4`nzrOv#
+E-u(Pm^EjB@>7e!4;Oc`2+0m4GHF2L5)$4*q&Wck77)ddzw%*x+HoVE4Gmi^G&Yq9Pj}hqbZ5|42)OT
+l8u$O+b6T$WvzEV!mWZ`z)N0DLutUwt`KTR>RkCO|=;)J@cB(p82%12%y{Gjik@!Ao&>Zctg6E|0I_3
+smL#qxXhSX@xv%dG0KXI>Z^~c`{`>UN19&h<hGsv}?ou2_~qdPfP;R&$8_fOfockKMM&EBw6t6||3%d
+~I)0GUid8k@!fv*MCN(nJ5Lxlb-&zq`o#54%ufGc@f&BzcE@@)af4lE(O(i8%|ySU^bnklW`qF#b|uA
+W<`?Y7RE(u8Ipx{7-(noezrVNURMTLR8KniTBb}rB#v>aL^lYP-XB%7wx;r^7`?ISMmDsyvED=A!eUo
+#gRYwY!<?++MjQdD8QGt^9qy~-C6xDjx_;8Ozd^A0zp(9=!D3H);c^h<~Ear@c!Mz!cX{06x{nZ%UD?
+8Jdf)gUp{t}@i`<J8i08<z}e(N!;E=o`L;^aM+=WQ=wA`|1@y6z5#I6NcGp5jX%Ql=A~vAHF)la)R#F
+##IM~54FcBYRx`imnfG;e_b%_#6m`!YOts#pLx+;DWe!IjP{A)<1ppX+d!ggteOz>R~e;k(@+bm+JYM
+_2$(BV%?&UBtNsVd+LDB6nE0TWMh%(^6|UDA`hXQk#YyNAMKc5i2}aNi4l12@D_Qzl}2F;GdxjT?C5_
+$8)Rm3AlRQdO#)&`V>$8U29F_g66j-6WNV$z+HNgo*8u<yAriGfIYLQ&-C#&T+MH&$zU!uIw6DbN-j!
+M7HEn+rSZ=O!BWC&%4d;&INz7$5HZ8uZPaN$32f&P25rc!|VYmv-vPmrUzj)DAGLN4V0OveKRuF4)@h
+z)MWL`2IUxsyjs`y#b9-*_2O{2W|%Il7cM)kO3RhRVqj-^l_IQf#7g_N7_p0+s*yb9W4uEt!)XTx+l&
+<wF`I}r?Ppsdjgcu7t61dxYU`@%;_;#}RZaaN>^`a;sqlY3(heHwVMhut7s7|9Q7YEhnQmZQCe)S%j%
+yv@C6o2WAX*qY`Y^0BOZhg;@oeJ&w-sL>SIZ&b;{c0Ppqt&)HYf&DPdX9WVJ5Rp)I?1c0BLrBvOF8Ln
+K?l5obqJRB&%$Afn*y8c>N|{@Sj7#2e2r3xVKydxvWlEUWN->^|@TEd8%!oTv7kQ_L~V{4=)xJwa^T)
+-GsL^W*tUKJxcMFrxqPMv7BND!JPv90tanX!)A?GA0S=DA(VY|EM+NHG4*$lTk?@kj&hJm;hEdX{oO;
+}Bl7iOH(BLl2@*1mE0$YG6XhlBsBR~dhp|YXtgQVJ;-PJ~iO1FZewAQv;3W=YSYmNaYSY+De4+;#J=!
+%~YOZaoiQu0bu_2tlQKJbZ73<VF6Pt}VT1Ah-=t6huj}I$q9&zhRxD(erf)>ssj}@(g>%8S)9S1u`j^
+~L5iyP?evnk;mV+Hk>iUAY%%VeqRPh3A^SHU~VkfyoN&QANjeigi942^syx7tTWj-$t@AT!b7p0-m`>
+1I5N!J~7P%q$Pr$mc1Uy`Vv?(rBraTl41e3P-N8k-ciCq*saZxQNvX4@(Q*UeQSA?PDTJlt;b)SPeGw
+_&<f)TqQ27Zt|96pbliTjC3Wm<_j@ZE)MQ(|58=rqJ(wata_Z9$7-Y=_T7t-yGdh6Fkz~u$`o&aTGt|
+V%A-xZwlZ<=%g{9e>q`soN^I>6JFj}x=#JW4<yxvv1EO*JYg@$PafmIQk&Dg_?%er77$R;MvhSJKAq4
+LW7SE-E*R#WIcj|#BDmdcfzv^iUPhh7mR(Ou0<+Ej%pJ+5{izrT8V&`O}0((|W^;l&@5G!V$s#Sxa%4
+t@1CQy-DYzW=CR$g7z4z~6V#?YBoS_mmVg59M%fyodY!=J^j;fyThQj3}}-%G$-n`lXV1NQsWW4~u)b
+<jqCl=rK!M9eb8`V<eHLux_cR)XQ~#lo<KFbgD1x^p8SwsJy(f!p~eHrm3oUcms%%}22#I6l2%_=(3G
+3M|jIN+6skf>6UNofB70kt=F8SppzzHX4$54p~weXfh>hH=0xQ4w#fWvs$e@QuyqNCy^jjym_F#^s{N
+Xz!<>1mLkJ0s49go&HmwwG#zB)w_ErCrgf@Xq#$EFw%a6AwMk}ScB<*Sfqe-MV13jtw72`$V&fh0IOb
+ExTmabT!jqkk@dZc_JD9v4aR;3*7rFzcA%x*xn)m>Gm8vjomm=hwP3X92vO3!xd}dJXTr`tIzHK-(cZ
+^BT>{JPHvm<sV!np*zl{H)W$#atlrFRcxt@py%3sMJVc=M|uFtW>nU1nL10#ukAkT)(CRj|exbC+Ipi
+UBBKx?e1}cA3g8vcnYCN~FjHWQ(tnCSQ;>n$ns)OqU^LZ-H!W`m=KWcVWiz1qNx;9-`BlCcn+>jXF<v
+`CB1hM<6ac@|guiGHBy<M=sW``qv6}8RJ_-Thgj<N-D3F?St}x)u7GRT&CZq2g$T*?B$WEulLuQhRrc
+fwC}+SRwi#wy>?EpX(XTlcMVs$-8}um-d(WMpDHU7DzP)XcJyWQlB6q1-<LtV`LZp)N|@qmI(uewuI+
+6Lx8G7wjaS_L*e?+17WVA>;;`6hQI1=?ba0`AA3dW&QUwaQQ#^=~3+D)T%`6r5vST8=zEYj6N1#h2&m
+gW-OM}fbaNOVSPSn>}irRK4bi~&pQ?RftTBc>4E9#XrFEq^t3rq5RHw`dk5tYRht}!L69KInq?mjE<Y
+S=kNvXd%TtBD9N;Ih!ofWQwy#3S=aDyY_?s~}n%0W;J3<pBsJ802)5@<>d1^ayV#2%1DeMxHhmZ<YYB
+jSCvzOiQQA1)9@m4IVNF6en1|a^k|^S^$vkKF(pl7O|RYcP|pFNY~8*clH1s3QYTv6@C{(C9htC!g)4
+u9w2Vb2lVsQUI*fW#XCUi`?cFvOMhXJJM$3xJk}K)5QB;yz6Ky@!2`N=7U&!T7nOE=l&xb)>XoW<5&7
+T%eCEy}6zfVCiFvdk?yJJd&UyvhDltPIHVpjlc)9>--+lME#a4CR@KdNnY~N_JgKB~(_V1%<YSv(z<<
+MEHH68cksTfX`HZw?hl?yLGZ*-DP6_vb;-{%Q`#tpCKUA)DDWg)esGnL391*I=M8a_Bc6#Es1Wm%1{b
+*CgP2W@$R6_a#z#tno*G3#2?Er$^>oaUlzG9tQ_yl*$aOLhQikfqB88*2LCXFL@g3dEXgh$UFv^bH1u
+3f>&K{U2(&+Gv9xv!SZ^<r;V2(RiiLB$8RpazV#cdzK^OD=i1q|F`AD@|BjOrmwIZgjlni5^{;1OknZ
+J8P<VYmChKD(8nG0*&;0_zg=aEx|fZt<v&y>T~GqulfLgvz&Ym3*Elr$O;f=i%ZeUW64FI1lhQ{}zW0
+yBt1dK3{ACR09sB&IV2P|6IRS@;#kL=AcDWK&Lx}F<+aPxe6xj|YLI&Afhy-R8N-^@d-p^99A&@D}fH
+GCT*kRyON<K6MYC!7FOBR>@16zyRxPvOvZs}cW&NeMn8bFE2Y2WE%%jU?7%12v3aRrkM?`Td?w@pQA7
+|H4DwL1cQZy>gK=L3u1N7|qruz&r#-TDtuO9KQH000080P~d=M-Z@R<=p@P0L%dZ08Ib@0B~t=FJE?L
+Ze(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvrVPk7yXJvCQb8~E8ZDDj{XkTb=b98QDZDlWCUukY
+>bYEXCaCwc6!Ait15Qgu4iXZk+#nOY`1TV@Syr^#wvdPp2lT1h_yY}tvrYckrHpj`#_kEJ*`v%L~;cb
+?YBkua!n>raX2TYTP3yHJfiL$uC87;KWnRADr#f)m?Numza(5Im!c&_VpWkt)6WA!|0^HG63Kt{q)q7
+k>4t574&$sw>Uo_??D*Wvqc>X$0Dm0uirru`SX_Pv&jBSp)ZEDWc4ThXgU<tb1UU9&GWH#@wrAQD-!9
+S%ic*_1QL#dn-WEjSTU=A*OJH%e*mmBItA+Q0C&L%YSd*UcADO9KQH000080P~d=N3GPH8w3Ub0KyIc
+08{_~0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvrVPk7yXJvCQb8~E8ZDDj{XkTb
+=b98QDZDlWCX>D+9Wo>0{bYXO9Z*DGdd97FdZxcrl{oQ}X$Wi1w<6H=7DYcYJQGgbSCJ_dT3e;NP?Zs
+QpyQkfCjQiL3&F-CjjssL}gG8MDn2$Ga-j4hI{_DA<h4A@&IycU1v5-kh<x=Nf8BL`%3%XwWIX<6e_%
+|ZyoYY>4idJ)}Y3Y={p~>WB*6;Uwy#u;5UXJPYTsbOLB?-Tmx{#JUJY7$oQ>AjLoh0GHQf><VZy{au#
+%p!(2xHC{m6~NlH21z9A0HPccUj!djJ-KNJ#NfpbHAv1J+-Kf^<-tQH=W)}>yX8CN@p}U&A!VXW{(EF
+US0|3=t5erN|g&QpIK{c@>W#K_%R*#;E`$^>-BntENNP3$Jk5*R7s&n)WS6ae4nQ)RTa6gg*!WGZUc^
+A`7pyH^4!>hKY2s1o?p=8lSki=$jONEV}y3v3%8myy)l)`l=NlDl5;z@WzN?Qnp;QlR^zG(*gK^9{$u
+z{m86ZQLdSUc$4bAnnx2cw$>{HZu7Q^<BK8s3(<8UmUVI)mCn{O`1+pE{(8`5Sp_*qfoVmJEKCy#rkT
+wC>5WziLtr6^oQ|}+fD7zNp#>x0&jBSG{w%Y4iZkF07gJEM9Np6q*v|Ea8-Usx;xuv9olOKM3@^DPz)
+~JFCsX=2$rbH#|EqFw;H3BLlrxyk++FI~)p~+~u@kcd+F0Hs(pdUcdb8<_2E7xpKIxl9Ga&uX9Ow<Ns
+U6WXt*fIpRR+6s^uBan7H!IXadFIIsEz-GcrEo^WD!OHE5Nx(Bt5E2tJwk8x14lq9z@yh4yp*PVdx@T
+2PKX&jranD@zuJQ6Y1$IS)e^BSYi?F#K)Ha^)v?(c?9%R@Dm3f^IVz-Sp)+S^n>K3KT5n(I68yQGDK8
+gvk~Jq;!SYH0MVkC__2%VMy1aT#!sAdKfDAoLf@&*Sx)|VsC9G5^ATU_XRX*>vRqhlxp9?E;k5fpwwg
+B0|OeJQrB7>7Ql7jCrKaT>x!*<p}v86pEXgKIn@;8=Gtg%9h4Uw3F$~#$=Bj!PfJ-F3qdCq7}h4B?sx
+MOO=!p#~>K_=>2>LMAu|0DbOplKLcb)w2ZQ?%Sl`FS$<tdEWcDH*%1$^LdXfp3=RH0zxHPi5-Z!K-8C
+m+Ay4<zgm_0=GGAR2Kz0KWb{$wezN3Ecu9nL#sm@ic0;3>xE%q3Jq~TtX5{y<)W5J3uoFw>|__GE2hc
+{>=}h}Z%V2+rNldDAvt(lvzv@otJUzIdd$$KmB=NO_QP-h2V852<ED>9Q3SA}&a$qxWN{$ajfTk4jw*
+zErHRtU?Xgkl@i&6v3$0_EfNEopI|pL`$9FzpSs8rD2HBrR>4dGMap`x2j>9j0zPx(%?Baa#EbVm^0|
+&GC@V3lMn|Jxn<icvUeVMlp@mpzCx#n*5G~`1=N|0G0dH5(eTNvVjBs#>>Y#r`Gq$ei-Bz<-xJ@n7=<
+s?lvMPz7)t-~Q+0d9&%Pfnfy)MC>iF_~q>RR`XssaWlb5zUsKUR-mLEYDSOo8=P|gRFgwt?MA7dsph2
+_u&yJh=(?zswL2*D~(o!A%{|PpzT=K!Pc=io>8)$Num*P&bFi9;hEWvNl8EI5&Ub@eM$PUdd<w#8A%*
+E?JS&RJd**0;!jA&dF3y05FALF7UHZw$@PeC*&DMESGSQMJnRNd#iP|x)n@a~xE74;@uK?^!Pr@Dqi=
+a0jHF$3+7shDD%WJiMKQWsL!KlkRK$7N2l-NrI~A7`&YRGTcHJ?;>Fyx2*<)O=)xO+2C`R~pbGrw}<!
+nFw1A6!F-TsuyJdVr>PZ$P(oGVNhaRAtfY-?(<#90h4OVBaf+wYVv&Jm&V`yu_?N`9rH?a$XMTS=X?C
+sK@*CwCiQZ_ets;n1O_{_fOO80qw&dB;hrFg4)GIdr=R4=LGxp<o@461fm{!h_go6LgxUI}dp`Sb+L}
+Mtf+#5@YKXbNs!}-j}?C_JG|Qj8i;wZaKg*awnYKCcxK8Sz&5j$snQlQVE^;8&FFF1QY-O00;o{l@>?
+5sq};c0RRBz0RR9t0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFLiWjY%gD5X>M
+tBUtcb8c~y}?PQ)+_MfY<GOKccvG<QHq2qeUU!a7P*cN(>ggY9;h+mq0=(CqB@{r#=n!4omm*7aA*je
+w&uTJ^ObyxvX_nP%T0_%RZzfOb}45^O*TYJ?6M&xe+2gtMa%(5a_T<lx2pHBG}5BQ{0N_%)&EDjjW)r
+bF8x%chV>LGYhj8;u)CHWQJ-toZ%_=~dP1BL&58J^j|t50$CjUY5d&AggJ8xFARNp@`BZ8fG*u(-chh
+?Yw19xzZ5}$c-!8XoOsk$>dn_s2`Cg-A8g0GX^yoF7qFYEYEGXD&N+z_goY(@vwKhpqh@PB#)K_O%j!
+0F@0OQk5~Sg$?@<5P)h>@6aWAK2mtey7DxJZI#dh=007+#001}u003}la4%nWWo~3|axZdaadl;LbaO
+9oVPk7yXJvCQb#iQMX<{=kb#!TLFJo_RZe?S1X>V>WaCwbZQE%He5PtWsI3y2|3|9!chXDfe;C0RxEK
+P#O1qK9#MN5>;g(d}(O6z~$JCc$t$1Y|dku4s-yZi3$BgL-K#(~x4D|ZuT_W1<xQyaYl%Ox)zh+xuaD
+QEEXcz=KQFyTK+-Z;4POABMP$pJ2*Afo~*&8@m}z<&tqKr48B{uh|4K)WqB&`9QLZFbhXqAhR(w@g~@
+U-Uto2_*dT7U#kUCMN=!H_bwAgyLE6A%b@iOePhtLD*HPmBIy~)v7YOX?T^;tJHgViYa`s4KGD4N)LB
+&ap9j|xND8Furw_PQ3GZT0~~@wMD(QCt!`xnYqXKt@+zBnki21r?fkxhj=Yr2+Pv60*GRF>{$4JhKJ~
+~2==Fd2H6lD{$8)&V2aJOhuY6!(hM{3J-vyR{$?t4$!T8vrC_EiS0Zw}`$(_%mgMux_E=PG8UhYdN)C
+Nn)l*RbDa&ix74z0~b6j8$==Ns;rbH+0}gDZM_H4BwUy&baWO)*OU_B8wXkWx=oOl0V*1E>eB0@A=8=
+5IKw_OS?%*tXmPv#{PnWIN4{!KuKL4qWLlBty00%g|x}Kk|3c_ba2(I4}zyk{8PF+YDaf-*tAKzFdFn
+cf@fC!=7<JFQ?#!v@T&u4f4i~jh(yZ&fyH>kD;mDmLY1}`0*4j(9~dgiH;-`vdIkJ;1qI`kpE6e!XkA
+@QY$?J{+5D3?96Ya1V(hC4cy%L46<5Cu5^-)Q@DhM@G4U=$9%wTU2{VfU9&2k0lGRnkJ))PCXtG|RsF
+6~Hv)l!Mg>xvJ%vvWp7w5wW*@WP!8)83W-7$MHZsiY!EsBJ3CA;-R~4c-jnrANVxt)P<>Wvs9G@aUuX
+ICxZIO9}DzRG~KTxTJRh8k^;!KMj*R8vWA+7$pfBa*9zxs1=JO6sWTrKZDFCM=xXQNz)kJYUy`6{B_(
+5TE{9D;utdB=f{Lqr)&<F=_+wn!kc5dg;eg!|sO5?qzMr!@mO370VV_`$UeDo#-(TW#Geo*u@fMUnp6
+AHWmJrdK9h3t2Gf0j80yDR{l*)ZAVVZ4|t?P91*$EEE;B3`4u-*uWf6$@mVj$&KzB41M2`3>-MZ!qTu
+v>>U!|fU%K!%_LxHSu0)d6{GNgAvJ6hl%t%;$_v!aXdR7<phOnM7F*f-osO#+d{g)ib}e=w9y$yHt-6
+N5Yv8n>%Y&pbtF=I$Q59M26Ntzi2RruK(LA(f7^}Gg!Y4;pS2TuRF0OGDq_lp=<1@Ly=>4yl+;DFAFG
+Gl^FVT6p3mAiMs5AS}agC-i&H<e@XfIT)4-!R{?Akkfe*E;$>UsHPG5>s^Dy$9$Zr|V}G)6nrsgfy75
+Bmkz82Uw5!;K=GZG9*wl{*?m&*71raFtGu<PEEe3vZ&L?x9-7;z<6+W$f*~99<36COd-hVkQFbUS^OR
+;f1X=5>@IqDkbuV1#ySEH^y0E0IRV$M0p>-Jj#OZM6tmU+MkiX)T;cQR{0aa4~K_I;xj#VGw%O0IMme
+1*fM=rtSoYwSeBkTCdg*U^2AX~W3w7Z5PEj?KIm{wA!7Vh<#gEorjn#57rXxJE+^#aB%)yg=M9&`5YZ
+LuXzehE<l<n8V&*6Y3P||&c)D44k;Xgp`QGT7BJZN>y0*KT9o@4g{Y0Z7O?0%+M<Yk=Ku=suruZ+N`~
+*--0|XQR000O8^OY7yt*Rrv#{vKVDF^@nH2?qraA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY
+;0*_GcR>?X>2cYWpQ<7b963nd8JidPunmQeb2AB>Pv{01g0T0MFMGbXhNFUBzW7V)JblMM`CBT0~Ex6
+$97uhgBGT(FBIQ<&OP_DOTlZ%h{U5qpmoEAg6!xJS<Vaeyrr?maYOPOa*Z;U^xk=S-lzHe?E6)Eb}|3
+<`TXJw9D{%TK%20@WknSFp6BVWP?H->OC$vfxp#xuq@<#^cznSbhCuQh8<pCKlwGf1TZtmoA(`qPJeY
+p8W}2s@RplDe2!6jxUpyC7lB~isCwbY$3=+#Wo6YRs%TlM4w@Sj23(z4^5T%uXz&|ZY1#9WCXWf&A2v
+S3zN8+@dFJZB`5-lzkI<KOT;OCq%%oXJ<v5?3@{59v8a#D$&cTN~&2m~_DD<mw0ib@rNmMfLeuCa67#
+0Mn^lcoz26rc2^cmJw+;Eq<6&RfB6P8k-Etsv)hO?tOW8>Ms9LUeNqAktL)mKIoy)9725ObG-w0;RT?
+e^hCiL}Yzo#ap-|Os6H=ywvm#JZn~}<Sc4MRnhFj{y}^+8%6AUKByV5G(@8DP+G2u#7K=ABIO+y#kzF
+1>At~oK_5uhDNCs5KK31-@z~n6>Dc|N1j`fshmg^rzdn@4BhsU(1g{ccJhCs7sXV(UbyH!Ic#iAj<Ye
+bIn-HVDot+X@6Sn_8K9~iL;&HGNegghrDMIl3c`&w~z<=7UVR}G}kYT*e9&PM~c(e^qqie8c)wk@iGt
+lMk#nd(`DU*t@9H&A;)7~H%T`tpk_kh(Kc(dgbf{BkgxuGAM#ht=~K^%#1u}n_6^In(Z$BQcd(x3^Ax
+2e~*3*d+bEjApzuY+$ptqwkz9s?iFk0aQ&jC~@*4eK~g?UwoQ4t|cx<bDYEvb~-&2;u_mtKX_+^wAGo
+;N7>th2OH~e=lOn|B93y*PyTC@CI}W+#Tph1gciB4Z?c{P)h>@6aWAK2mtey7Ds3Q;WQBe001@u001@
+s003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kb#!TLFLGsbaBpsNWiD`em65?}!!Q
+s;@BWI#y~Ky;19B^+&{HYF?KQ0Bm90jRgtShYzprEkyO2_-E*8i$^Jcdv1v(y-KS<*u^&8iHHoFSasq
+1?FK8+-g5iTR~NC6NAeI^S^8`>IuBuEmHoNS<Yh-%jDJO(`F*W#B?JJP+79mt#^me~2i;kpVR3I?1tL
+c##2l$ILJ>bAYPUGORvSs<Q*%|W8D%`fyN$um4M2i;9f&mp+5W_gV94+O0W!O|K*I?JeSnvQa5Le|Zb
+x-U8+|F9c~V<I$!2@IS~Tq91Ab-Hdy3<6%-_H9qmI~%F_wUzRj;MH0BU$cNZ5`3VU%0HX>-C}1bFQX@
+>a7uNSotKo`cI&4422e`_1QY-O00;o{l@>?P)Y~Lb3;+PcCIA340001RX>c!Jc4cm4Z*nhkWpQ<7b98
+eraA9L>VP|D?FLiQkY-wUMFLiWjY%g+UbaHtvaCx;_dvDvw5&z$xVv!&a>B@{8_s~{2HFXl@Vl;^ZM~
+5qLxKb-}CGkRWRX!rChWpt&GyC8!N#`zyJ0Vaga(8y-H}9dd-CmY8tFxUr$<%c&H=C^3oSdvnxnofzZ
+WFPuGg(wjuLw>Ud|t|;5J|m&t+J`-Whu)sJ$T1&Z$w=lom(GDS<6J`&aE54)BfHYQSIR*p>KtTi9Js~
+@r|%7RN3vx$qBH^W`^NtBkH$sDas&@3%(O^9KxC;=T*gsp+K&F29Coi9rftZBXyf4vRG%ErsODPbV5(
+(JlV369`m{uyM4_LS)Q|`6ucIUvqBv7c10B)*IU-?Q+S-R&pdBLHD;A0YV}sYF}z?a!79EM`4MP0l}P
+nzNGJp8^paGpvjV8_3J!uFyvb{}mL>e!OPNonY8QS=y>q5TOq5n;-;CiAPiN2Y|1>M<UTC*%>v|7+J^
+;0Myx<6$rZm(4ht3g7C$Jv3MWy%;$W{lV@8*lqn2qq}^yyP^%Xj-+M2Xxz9fgX+>yqt7d8T);;IPdAR
+{2?!WtIZhfQu&|{zeHyU&OCJe{6u6DOk=YVK@wRR`FF1Sg($350(Ok#Fop~Jg>xZ`TKD}1T_a_+m$jW
+;NVd5{T>*|3N|HUoi6{^lzE=5E*#&BJg-`^rpQHAowz2z%T}B*l2d3W9GFI66W)+`mZ)W+<XLNr(>*W
+w4)#M>kTuVz7J>!-WJR-EiIT~+J-C*P=eayYY{5Ul?>$(XNJWtdQZ<usdI?_E6m=D~h<+4!0-*g~763
+M0mtqGyfZMD>udHg|4EW$h%GMltg)qQ?aiVAmY0B)X9-+f*qy#im!I5Hxhafq;mOF4iQB>I~Kb~!~%~
+ts-JhQZTLe$6_n?l^~!Er!2BHai=vLa^qXPKeKz)drV1}aYEQtk)kn<(MdJj;=J)Wte0!5?Vf&<<^X_
+(N-yj)mX{kz=_eeZ{hft!wKIeHH3>&?@nbJg^-vj^+v20r+Q5QA;SxBt=kUD;(w)TLFfIMy8i3s{#7~
+tdxZrKL@x0W|BqS-pLw~FTondmY_1v1JJCh1~jvUa92p!Rc08)fi!MbyO7krlnL0pd(zjVjdrW-;kXx
+lxG-i8V1@b7!-W=VAbJ>Sd=M&)5ntD$^dLHLA0rJ8*eL#VqK;a=<43>;T%!WRJ`?LTXzn-I+Am74%xM
+4t^N3J@m<~GMMeJ?1%Ub{Hd4<|yiFF<1(k?-Z;q1*~@u7{glutRUYF;-O4N}1_Cg)@LdWK&Y`1Kbi+c
+0o3`Rnh4$RFW~pnuxC6ZKZ6G1z`B@~m>8#uI^=29iOEktS7ms0|E^V}+*AYxJ0EE1NtW^3oSalTgRFP
+!?2bXrooMD(!h_pBQApqR81KxU!{_6)6-w;I?2E!4Ak}kkyPiR!TW!w@_#?l2L$&6%&Asd1#5zB2=UZ
+Bal<DDb!TubV&&<dNqH2`Tg5P{BFK@bNwp*;my@z{`TtQ;voo?MZ{o%Ne9A93ikjaA?H|54>{Tl*7XY
+rl4Za@TP2WaCS&>ht#b)SV1|<Gb-d<DElZa*{e-C}<S%=)p_Ql)LKLRVggmCRze>+lF2o-Bb&C=F-1!
+9W*(nR2v46ARuFcStz)haMXJ_o(ltH8x0qRdNajr?1KJO^^BsxbBfV=6sDU#_jB6gMzYnB>j%pYL`RK
+1hv8GZj5zN0Am$;zlX!BnkC6as@glpwh*Hcm<hbrrsqyu18Y{NnQEKd)cEUOu2L1Bk1zpi+)J34Lv?p
+vq)D=bf@2wgPJlsGk%jZna|~U{=}V#qAcVsu~L}%nnEotjh0zn9?@L-joH|oEPEx6(+^HOogh8+gl(D
+*hHpjQDQvgG*F&G%k*C;azq{cXlak$%r9SInX0SR58u;&ujX&(i#c9he^^{yzyFAri<`@r^U+V*P##Z
+FoNU+ZEWVfx6+2oXF(?NPK7Te*dq5*Bjxh_wF(@EkkCi3P&L?A2cg)a-bdzE(bp)lKo~_fT8#-qXVKD
+B6h=)k}7C!^-`tf{GHo#mgSCpdDSY`LwB%HdQl&>S@Y%?XQ|47RW5A~J*n2L!RVbRje)?Rv~w4zK2k1
+=<qo=|qfj7|IpU9n&V?LX;qVjuU?H!bKQ3k!R<fJwBz(BIirAMEVj@;7wC(X6Ab4&ws?_U`#xx+;Yoy
+<3mj;`-I~ltHh~B1i^s?h#xQJf5e|TVpT8LEHK1>8C^3Dusd#Qckm^4m_18k7zB&H&5dwuK8$hu_oiV
+jHf)GQqOG$wc+pS`Ol#4{XHFD8c^Z*%O*J+R;0hH=ZAx;Z1;p6RYYn+6M&Z7845;btN=-iVQ_k?R_hq
+}59-SmWLD=mnSvNs2T5MpM&D71iW@pPQ2=u60*6m80GtCa)9NgdyFIV7RhDP<(GEhSuJEwM#&Vn25r%
+(5B5NCP2nI;M8w<>2$V^yZhrLv-7VcsK0}BXY3NbGPWffXGhh9FgB2sP(d#53WdW}0avF?Z|H!#0Ls-
+p3HyA+%#ZkYy#f<CBryVF!`qExJ!P4MWYETEdeM%Iv__I}Eql<e&9@coEAVJ%VTys&2s&coKtH=>S>N
+n;XJ*5YgRHE62IIcX8~+SE96q4`TwmRL?}3N1#{5>0QaDoB@@tBB!{^(|!GK}Aej@`htI>zEA&SE?OP
+PgyX0j4B*EN9?jfEqAJbv42|FVn@Z|5MUpvz;!D`J94d<npi<CC~l!P0`xMTn<E~q*+sY3#!8m#jp}*
+bQBnP=L}_u}?ZChwKIF$}P!Wc(MzLoQp{p{p7?d;_Qc_#(VnL+<(t8}>;;f3>S&h<&PBw7e0n^MDv1C
+7p5p;Oa_m<FQR1M8&c<l~VQfSvP4B)&jqq$pe@<0;_=<#*mr03Um6f%sGHV`Lv0H@=jHwGO!F{}ML2}
+u;0fKLq!S8$TFLTiH4ODfJm2;2O%vpzL<>Y}m|8!$LMZ9?<Q>aP+r=)KzT-+zRT(3wuBDFvIOmReb;E
+n~4C=%8>_xwib&9g+|sY<`}z;04VQZ0G+}^};skJ461VXw$3M(1p~^h{hB_Nah2r%HaCJz|^Ta_1)px
+63=rmum^P)dU)EFY$(`Vf*0Ueq^X?&_Vcb*%VVr&%HZdLQ=u`x8gmLUul@=jqJ_K$>nsO*4NT9|E(pE
+gq5QIa`W0YeVD<>JZ<*7XWkpho9mG=4+H@1vl(}ukR6DAFl2_{M7~j#DYfwh=sV;dUhKigU)AbI&FSE
+P|GtDBFg*R2zExTc8N`ph)D=T8uV=I1i-|k1aA?(^ly!GxWCSvEH-U}JK1t(1AW#zKV>D<0yA=H3jeZ
+@qf<pX)srZlC24Yp2OgCDBTv_$RL0HT|Y*x(*bf?;ai<hlDMVXA~gs1W$28lo(h8n5AaX8hjoFv$y*9
+3F3v*(>K|6ZALuUX=2}nVN$j)3h)0;irdHb_-zAbwGtTtqirDd1mEItW1mDvgtB!`8K7k&#-!Y1{G6F
+aG(bqtbpu>wv%^bf#ZSH$=k><R0n&pq9x25dbv1iicf_+6eF(;SJp%Ekk}il2^4}|CF!KAyz9C55a=3
+lhQ|rNQC;qKh|k_B!>VzSB?Y+;dOG@(r$Rbm03WB87)A!u^aHSv8vj^K%n=U*LMwg~8a7lQAU0%Sv1-
+gN&SgS>?hp*XqAm3yXxf?7mw4B!di+~Euchp~S{k`xba8*ou1v1LsJRk~5M*&~g1s_=Jo|(**l#8i2u
+mFjgB1p+NVS1*YU(7EP0wHZv#E!bFeIk;2QATe^Y`<ctC#Wo=H~jQkL-}v6w3qdGI+qp5A&B-ugwXO8
+XE+?&G>N&t6AqXC*qnu_-Fzbj=BcsSYH@n`O~Y;;ObXSdAc`3FFIN#Bd`AJjwr}$z1e&~l?+uq+HJ9r
+@E{EcX!o^=GjX_b$`@7KVA~&H5jA>Jjl!uJI?w@@O_y3{-f%o|N9d*4mwJTm2&1<IqdT11m_K&950ob
+E+T?iOPTTL?bOrZEJ)0PjdhTrMx_7j6=jE*Xa1?&gw1BNyJ55v7F%9bQ53(NPh=h7$8h$Z<_!U4Ulzr
+y8el@;_YU1;G^f!>+ds%4z-svJd`9Dxg0|XQR000O8^OY7y-RBv$f(-xwH75W7G5`PoaA|NaUv_0~WN
+&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cZb8KHOaCyyIZExeo5&o`Uv9W<j%E|QEYLZrsQ
+8=<LXW-6uEgud+QAjItWo=E7DwniQO|QSb&+H4iq$uA7{Zb)_$>r?q?Ci`lFMG=@U9*KuxC*6Ya=l5j
+oUO#CST1)1RwVYZvn09|SF2bq9N9Yhe!$d<pXvXffB!?rkOhJG6pBqQ(?mJi>Ga(s%hGJXt`jA~A`^E
+p&zEVo{>ZaLCXby?2Q=d}$;GFfU9g)p5uLY})7kXq8n177tVAcw1kc4R5=&mh`7EqcckEgs%aYW+Yz=
+$W>EzjN#Mm)=5GrR_nPg&1FoZCw!|=0Hl#2DM-V9#c_%`F4nM%Vakq_8-a{n;9xxJYT*xmi@!|nL?-3
+$z$&xfsZ-A=InTdo)$|I#s9V%M5NYiFeAZn%QDTHaP(w|7|BEH?07hVEs2`~L2JGM!%E-W*)x5D)0`^
+{2Df`ZvJixq0;Fc-%Fyrs#Bz*(Eav%k1$47@MX=77C@+y-x8>CUoYUY=hyyhj&vJ%FRk-YScN#gcG*P
+^Nkvvo^Iun4A$wtWE}G#%^pvu#9ih)JY>%zn@wdTW?)dCwcQ3#Rm>ME2-EfHCQJVz!d#s$aOx+UjEA{
+|K&LW^#HV1DujAVE)h-cF!CIX140JIFS3HXZS<uN2dpX&P#fj4Es}rnqVue4|!auA8dlZStIL->DndD
+5;zOcYuCYB<zdu6JSIkb|mc+S!#eb|U>2{}nou-J%HnTc@6dRK2Jr;{=M-ydj38^cHsu@rJJdLaESM4
+nqtq~atLwHZ9wvCGNy<d^Y#GtUi-e;yy050c0nD%5V66-uz-)nv+G#7iE>3m!hP?Mj9#Y}FaSqJR^{>
+DEk%q~eQMu$vF>-Z5U}E9gar+5`sd{nhvE{pDEcrLLw^Es0x_!QB-=B<NgC-duio_b?k@-@TpOPhogH
+ZIWHm!z=6pyBbwbZWNd_s-E2F+4&#ss2O^D{ma`?*YI<%`U7or<o@1IFCBaPsQ+qedeB7QaN4C~_Knb
+Q-(Zv7PX7gu_K*(%@ji`2meAl~jBrr315(g?7y~psKD~57cHh^C=b-jPZA2)SG90nHUA}_<owIM*FgV
+jJ=%<Pe@l`V=0I$ZjN!dN1#E*UU(*>;<koDvAX5`H5C)OJd`Yg>#CWn5YW9O|*`kitzz=O>&DD&B@r$
+oFQz<|>{4bymLr}Rg)rHCkSG!V8)UL!tcA5@7@?3u>}p%WbkCu@s6749z*u0;4WTcs*b_*#@vX^##IB
+WBLlI7blSoP#o2v@qqfP(bA_itQ1kHj)BtTV`YGQpV!o>?g5%@q2a1CWiXw5T`uS!p*`Jmx(<Nx8#6X
+-fidM$>knjlFdG~EN^ugrv@>U6~eu@u3c%=k<}S{#_jf`-)0(@8!Y9LC24Lrx4Fk?&+aoWVPg-wjnEF
+;Yjuq7uLvj1nka&R^T;TLLFxs~S|n_a@WZ<8Y&Z)GmBW7h&=SDq0iA*so5zg93iH(QKfvZZ_;COvfSU
+t<0T<wejU(i-M2HBH3h2Hpl8}_iV_<Wz&#3>Bb@>{upN5xK+0$@2mI<__!(EVtHcrQ-cL6=@Yr1>M@~
+jX(z#3qrXhmA)&s@e7o&rQ~j3HP)BR;7XJ#~vLM)o^2aDm)&DIXE0a5R)i@(pljlS!y9-sxzz<~tU03
+Yaat$UxyzdMa2YA~a5pgIYqOfW2wSm`Ee7++6x%B@BRM5#nr=hDxVSA~~fqRr#q4tvdB-;dCr*dcLtS
+!zwuF4)%x-W%7q@;tAXy$r^s!U)e%HcT~fM{Z+dTPaI{5M=hil2})A?et_#N6aOq}JrHjp2dKOF;wHG
+bQ>Xsi1e3!+@`X?x{P=1gY6Pm6r0zo2c`Vlp-wy}uyYI{y)XzmAjD2zWAAZm3)jFLHIXSWuQt%S?Au_
+m0lJ`jC1Llob*FHKr(tpPkZ565PQNkyEO88ceU`!db&_EeWfD|z#nPKUHftb;TXMC+cRIo9w$S8|l>?
+jpCs1W$Gy!EQdZ#*F|S>+l40N*H-Bl##{TX6aW$+WHjGUzc=kZl1hNMoJLx;^R#WP5yL_7RKZF<%icj
+-2_Cs#Kr>XPZ<hNeMKS-z5lBniCbF^S5II(Po}mgUE?`w&t6f%boo&(zSazaQ=E^>iBYGcs{Z<y&3y#
+r1g*B!hZ`iV1%OA9d!q++wBKP|3%g_W@e<CnGGVsL=k33@)^F>!(h+10ZI|gVmNrB1P<2bYuE;I`rE6
+{$HZROZRmCV^MZb$T$v#2laW<-(Cxc}_$W-Q6G;f4@AYw{fM2RdHoM{ZpV>CeV2-5u-MI$%&2Shr)iN
+{YbWycV{M`kPw_Vij0rdWM{#M)Y-))9Z{$uT;BG+Ej56pP4(i|zNHsfMn%CFKI*;J(2X`c(&<P)GFhn
+|jpd;R%nz#j4G=(n@aM=cqF{RJk1^;$GyB<b%XY~$yqdVA{k9k^sdrSY?vl^EWG3qovOCUXu`?hU1JI
+N(#UrXpRtS%T+Gh7^re&J#eBDQm4aQ&WZ#p;Yss=^pHrUU|9}K+DLa2pXJF<iX4fBp7<)m8m>*pdUd%
+KAP9+gSjEQcA&={#7J$w5CUS77$g%dpeqrWH9b4g$vyCHzznPFHRe_hawxzQ<BJ7y9i3O0HG9)kCw#P
+F1OGcVzn}c|!}a~-Y7U+%M_NH^&9%7Yr&45@cac>InNuP<r)+Hw%gL%uY=zx{+fGcG%Fb-W=-=47+em
+c;k<4IrNUi*{@XsKq9W-2^v;kU)gz@a%1g@$ob91DUdSxJ4IekFV_x;J_-;W~ad7ky4*#Yxtb&gunN^
+P9`0vzDe)e?2ttBPa4DKs@JJKlRq*>DCgLpkUUv3wHy-t+qv+3bM+zo@g6>wi<JZ9CiS+O?f~o`!X4V
+oGDJLi7F-sM)ICO|;3HSK;6QF5|caDFw)XHZHHo<=28!vQ0_IoZNfnxx##I^Piu0uGKN9mvE-c<fxBx
+2OgR$#W>xT)YCQNpuOonpBo<FWi!NK@P}6cyGF94C!eq@)A-YEDQYv@CN+i5FkPe4Qe}JQgiQru5*-D
+gLdO@R#w4350-c6B4r>Uhwh{>uP(UQQ!D#tA42#mysH!wIBD?yi$Ytzx`)4Ipb)!JT!GVMCH|fhB&9@
+iqKYFIuQa3XET1r)EyAs{g2kWU~p>mlQy6m6Ny@vaIZt@La>U^%5&*v8GQM4n>$1(xLF4AYwucyGCty
+XSB{pv_4AZt{1&B~SpghTCW%i^tsDY~wije+!QIKIVeO`Qm222>7f=~nV_MR`cMjP89e(tK6&(rPh>O
+(sI^b{%wh9NYVXCA-~-gi2;xKh^T}()p?leq0G1i?A1bD6mlzC%p<lmwC1wZ8sZLF}mb5uflX0_vW?b
+tqD|%Pi+ZR3T-K972!1@A)Mjv-O0@7gT~UHLE&R<ux222l{8yaA5A>1-$Ee!iIJ}R2Y@luja5ZtfYD<
+#rU;!C+EMqLD_f)hHiw|X%x>JW%6A(Sn~grJ3pbSmdcADVcqx0Koi_}xOyZ}uTb8dX1ydeBAfQD0K}{
+JAq)F=KW`tq}4ysg}EXnWQx?|T7u_UmeNLSF+a8<y#ThM02YHR8q8=xd&!Ih+3Crh6w34}Xfkg_5f3G
+&tz&(_N#)?Xq{*>qZ{J?8(u@bCE=>kO%ez$riL>nivqaA>XES_$moD;W<x#uoBxcm`XtT{SkozkWYq2
++*7=dxA0N8)w1mS0109M4Bkr@$mJZ`iEEdFFJke2|P+?d+tPBf?!FJB((KnP{clDh4|w2iPscyYx~V*
+*Z#yt8#h?}y1?}tgL)>`Ds=rSqH7Z+*VWXv_Oj^1z&8lgz6eZ&xal^jVd4N79L7aNPPO#QLNXK{=ORl
+$_bfbSPAVWBN(y)VCygWS>Fb3{6_<`#oG{5)BiGR#r}8So%flM4UrNhdYtT+<YdyD5Y{>$>WNx6p+Z{
+C0JN>tw<Js#za|v3Lz}&@Fyz1$oX62=X7~j!}ZdZVhLec1maz9gT`l(_gO{gzgi=+wts|9__YaB*I1H
+Ha*4S`ZrSB_R;wwa39O;r3Z2_sI{bXPIbcbTJk6#(Y0Jx7UOLU5})_31Ov5JJ!Zr5q(Ql!zvCi|TuCP
+v^c6GB3R*pY!Lfu(>DgGM2W7;F1!PMHp8s*_2B2GG@SsJuE*K?cD&$Mr~V9QbR9_QfRUt_aaU;>&xYg
+=Q3aH%y=$j?cV;ano-WTDq+f-@P>3j`<<U-P{jgwFMg{#R;0zRXkZVRyXLA_j`i-BS094kZa6dqioF4
+%+@$%Nv`8YetVwr~uWOS9$?a5s@EDj}k7q(3DJ7xLJSKxF5|l>Tlr#bwSE7HpMDa0`iy{}z-CFKnv)f
+@X)QGVVyEKVv+r@#1z^rs}VU`RgH@B0UheKDHZK^51mWOOHchBO2<~8#E1+wE||CQ9Cy}4vTr<bO3iU
+^dN^}JhnKTI@Xz46{}2|IgS`kns*P)h>@6aWAK2mtey7Dwy~a9}zM002lO001=r003}la4%nWWo~3|a
+xZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kb#!TLFLY^bWp8zKE^v9(T5Xftwh{iWUxB$B&q*^1?;6)l
+%jP<9ZO6@w6MO1&`cZ;NNJ32!3ILw0e&}!S?&5`jNS)7VJJa45OCqpX>^}SK3w_D%wu04S&AY}jqpFX
+>GAnmNb#~4=BWkufvaPlIMy{AuOt!VGxE0K9xn->ii`jvj$t5#;QOUJ*+boC%5qh%LY8Qmd-Ck+S7O&
+sD{qyrTi<hsyfBxak-Qw<tA78!wa5wRfQR>9)D+=+c5_>CEYr^h3cj$Wyn!M6l=?R?uoEgzt*@}kU^Q
+IG@Q+Hfh*aEUT-N1#PP8Kh{X>OTX{asXcI-5+uT&(J}hSL@(Z#pX{4E`=IF6iIC3ah2K7bMd!q=O5vw
+lr+rwH026!RH-XrDmv-Y+2VzSSbwLVpC)?wXmITofLQGWHPB5ZcH%7)G65O5+1NhwQITQG{-}W2|f4E
+46As{Rs;dCT+P5NJmO)kG<@4D)s&^%hJV3?xA9~+poODvzb<a(pee6!zbS5JaRb5!R=)!|Kk}Augr0g
+CxBlOLtrlhvjux;Uw-s9a(t$muSFc~)UCr4Q{#agLi%)#FZ$we4-SySX>GoE$z0hCz9c+5o%4*Bhz0g
+|L!jL!8o8fo7+bq4@uQ`7nOmZ6x@|&C4?2MhI;j6|$&IpT+X1r;fGF<bWutFPz?sEB)YQ=JyYS*g8dF
+$hHZ+`svZ`riJQhy{O4#kzEC#Yz7Q^rFG*|2p5Fmkr`dNJPCq*V|JQQX4)K*c}Oy7XBTcaUHdu$MtTQ
+nU`2utuycx|9(&WXD)5c6(c}A6oo9N`gSd@Wc3DDZ2$ghaJI`s33RuVO)!z7v_ut02Lgd|A9@YmDvJM
+ftrzT{C)Tym{in7?^Dk(<a6|!e^lDzUU(y;!32cOoUI$hEvbb|Gk^G7r5+J%$3Mwkw_|*#I<SITJ1fF
+b2QEW^`<=*0-iWw>gY7j~y1%(%rmHFe+OxMi>9CvO*4;A1Sn8Z$2CI5;Xr-fp^TLif+iSIg@Hnz1KHW
+6%Es^JC08hGHQtXdW+P5CW+YS6`x4?5sZ>}r%`FbrIas9iSr>o!A&;IcAn`ghjej``cQOafW?CG;VEf
++-;ht_qsK>$MNN}y06XA!kP(+v%0GVcYKJj#EFvM+MpO4x%CEuLaT)meEjl8}d(Qb32?<;}GRvL}KVJ
+kG_vKQr$<;MX)}Lwq`CMhX9iPR*Q0{&?7PMFEU#Mas~S;Ers~E335Qe7;^&0Bl7di`<*&8DDLnAzhx2
+;y}P(?(W`2&n3L^J#6%pJ!kL8Bv*#L{OLU|Rt<7wt$>O_L#SQEF>)xHJF-<2r&{MkXFm28zUX)nV$bw
+8bd!*2=^nDJny&5<xRskNd-3iA1G@ny(pPifdq<Cei4B3iYg<Zz;15`NfCo}(X^%kk_oA8i>S?A(rIp
+z7dst%<Qvl=hx|Rs_c@IxyXs2jQJgNm4?gTfT7JW|ffpj)_GSkGM_vBXrowqI>^a0iDjn;qA_fB1J$l
+!k>W%R5~L)t@tJik-0-tRN>%(sw-HJ4Pni;6={n<&=1hdX2#mmEPCsX^lEXwj8!M=Xz%+Eu!3ygBfr$
+=IUe1`@hl$Hc*m1xxPGe!>X`muiR(xwgn#Kw7G;L3uvN{$QZ5Z^8l>vSdgtHx!JR)8SCn0jN>~(n`xe
+_Es}^CRlza+s+C~vca+`fY$7&I?Xl;mP)s(LqEZ7AfAJGzD6?Sjy|F*4_Lod4WJ_{66QqAP<d*Gh((b
+58?lQ6w-tc-0+e@5nw-ZB#6jY9I=XKL=)+~1b~~={hoyVK2>rOpiroMV>WXOpN!JC1;kkZn;;Ec17D$
+zg#ngyqJ$HEaC1)r=L$l<I(C~FuCf;b)#Ud01xBffmC2MHXLZKNdS}0BZr>ohC2(sGQ{rD6%<3Fc6T^
+yd>NsnPG{X00-SgviNexC`Pg#hs0K*^nbL|;8Iu~L7cy@~LHv*Z&!yJAn+G;SjpK^}7Aip8RZT)bG!a
+~sYRd(4v?`tIX9>JTA^Z&lsvIka~JQAp^RGdw2I9{b1_TTy*<Ie_=|uZ3t_bx0S6k;EjPd`UwhOR8lu
+3?klx5~q9ce76NZ5mhGJ`%@DUk(x5&(eMImR-&;ZvYNnA0r0;u(1e=4Fesgm+_6EbOCZ2%_z_6NV43I
+D*+?WHx=ShrS=X?i@n@P!lt?V3J{A;#I@?fGxbC7aEPy+u&F&zv8*jUv(6aKTYe0MSMa%vnv?@|Z#m^
+wfEr$Ie3d>Be2PHVZ7>)Q-DO5Uvit&e$ouel>&gqF!AM<5&`zfvJqs<L`*z^v}MuwaxMxC9UgGNstPc
+QB=s68<>>v;kQUtw~zY}a9;6X=MGOw{1-LL$R!r(|(~@rj`Hu@YMR(R)SXeamjj!6`$fpS%9SLl*K2=
+$+KpL45?%XOt$z!&a!-5OaMa%~KdXN)kftX6*7|iT74?RtfpZ7o_az|Ddmi$Q-3f?2#4@p2Uw~3Ub1Y
+z`Vdl5T)V}2X53}K|L!rygKI8myqO;7Ek7_(8QqI+%&}?8;0d7A946(FrCf)u%)`6&O*H!ibT(n6)B9
+oQqrx<rPq4dS6{B4Ai|I5<0SQ7)?Hdo`^Im%^uH{ZNsWPjdpN*B+NOigVW#i`90QNrE$(oGk+9*kL5o
+#UsD9jS`pzxNiKHKlN(+<ccu;xXOey1fOx+~~NSJN*^-(`Z3mAi{u@$EULk6o#0SHc#pjWg9lK&l=Dr
+j1)MDOB63R*WXKF&mO%t;|7c(sk?Y%4^KKv+ZBxE)9fC{Rqa)GyIq%mdgvNbIH#P{d<NbPY%C<Cr;{Z
+bd6Uc$Hse^Go(O>^j;ji-AMpi&}_1ozu5I+_we1G0LOghwI7Vm(-7)DTL?Djg#Ye0napUTpl5pOt&t<
+zcw`f^j^W^u{ejBfRdB?j-ZqjdFNQrm63xA5K)$utz&3=j|(!tP&?ya0M(s#-AkmIFzrg+>}nrB$w*$
+=6oYp)B_>B`x1yB%8ctnbX=>*{MBIvcMtp{)S%bzno-KdI>UCd#Aqk9?5Cc8??Bp~-CH%RWGR4K``h@
+&t(J25%_Vm8ni1_28_OEcpT=+WhGz}I+!}sVqBiqWqhN71Yij|n<%4K%iax7k7c@fn64+50P_P^qNxG
+)PB^<&E%2pI2~`M(eYU*P{2L@t|t=#y*a^m;@Q#ata_`>D$iIiVuYpu)a7kB;v=-5>jG;&Bq%x^b8g9
+AX=W9C_H%G{(P-N~hZSTonJPhKMgS*j^clgJ%Z_YGOh@?vKgR?zF{;Osu9TFQ>x_|4LwZazy5f{GGxO
+oS;H12TJT1V<I_iS(byUU<^ZUW-tz&G=wwvWFS0&AD%jRKtDXDrRsGW)U-S4$l-K8kj(@JxIs#LJZSo
+%W&aA`6d;hip=pIXDPOb_@&haR`Qi{~SiL4i?QZWzcBk(f_#UsIB?M}7%%^y{zW~$o;dXdXi@j)T9DV
+xPZk*<2BRm=<w&8yVoV?2_LUVs|j~L{ubfygT7GE{eZ1Dvl)ZRdm<?^48A<Sh6x8B(_-5m>!pVP1&ch
+r9}SiX$+!Ca%+C&PF!-cfj#$HayIL85gngsa)ka{e?2K*;wQu<&oVY%Ki7x4GftXQgM$#-yJNGA{8o1
+`g3jUz`r?k^rj6<a|lyBQ&KHmc8mup6>Xk>CL?77iT)3oknYv+1W~UV5D@G&<sd7welnVHcFfR3XfGk
+)yX`>`ed?y0Z>Z=1QY-O00;o{l@>?a;^|rc2LJ%f761S-0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L
+>VP|D?FLiQkY-wUMFLiWjY%g_kY%XwlrC4o`+cpyZ?q5Ny5lBWHdD8$z&gf=0O_N@MpxDbL=!eC^OG}
+hZgc4Pf+Kunm-^`GbD8IxlDqz<VHSe5xW~kEWisf1<kvpkt%VgDP<5<lrQMk5IVlpX_Cd<UPTr?x1k2
+>eLaLbJq?~T!BGMThSt+;c>vMY9%*nBH0k<3}bTerpEt+oz-Q3CyPzy4<{%nol#!#9<vU2+f7a>cD>U
+yMozi_!$1fwI>$n=K~{u8WHc`u&AFZk=g!*BZe}ZI}^_5f+5Xdc%0l{{8v$E&KB6W5&K%Q7~>92O1!+
+*&|miF_EZc+1B~;p+OH<pzt9ZX|hT?bTpk!=(!LjTdivGv|6RPvUBGBzxrL*V$LuG;TEG1ZZQzuE$;|
+OT6|G>0$CR!dJMkfB}<Y34i>D|jvg}WxN-JLx^0>)l872FBEMqrf<32){%wecZ5v@yi1(a<KeOzJbKW
+oSlJ~|ta_GZ}i(DJL3ZcAdj0W9q*9R6AU>2lSs4{W{Hsb^A@tWsfpSUUPi(FS=fLu!@-HxH_m@Bl08T
+&&dkQBAd1yk~?U~bD@*6nBPIVj0c<tcNF0K=_=tdK`6db_q-wNCgD%Q{zWfyw8#!aK|$Lt$a08brZvk
+Y-{7uV8B?@dndNFF;z_WlVzcuA-Rdu&dX&s{_Mi-$kH1<y<YC()kg;ei!aZo3BiT+K*hrU{UU24#aMC
+X5;$U(qKsP%r2i5346xUKJ4>>XZE-E2DbZ%?fv~bT|WXn$pHtPhVloNAwZwE5L_&Ma?x&es|r?WNF3M
+_B><9)ZtLO&Y+>UQEC59oAq7YgdzJ&%z$;eq9c|rOF!7D&PVFc!EF?E#5#A%2tee(3;BmT_bEs#|)~#
+dGb!MW_KU)bn2F1kIUi7CA?^r6bO-8V3dvRntXKTglue5Klq7>WmN5Ph0bfq6fG0TR?y#K~4z~CiYE+
+?a{eDmfF`vTe98<r+p=bGhWvC(>?L<Uv4sJK)BLWhhQhCg0C$$ZG$slw*bT44|)mJRd`NYtWhIVb_{U
+tf-4Da$e_vC}<hcG9o|O*fsI<?Bn5cunG_y{x0Dcm_lF!7;!?gcu6(JHm)5ARGhZ*hPd77wk!}Cl35>
+3#AGvreTkpM$|q7MAf(skt|Cg$uHTs^00=<)bGKLLf8pp&shRdN@4-%<5_kh0_K|$D`CqyRzie`iSqd
+zQ?ewKbS6D;*hBXj{!!NirnA_r25k1Q95MIXe3pNT{2lobWLNP(0^UK--UGxKONkz7Z38`XRZ3%B3LU
+->N>+Any!ur>Lf-I@E`<0nQb{KaUn@Y!(zD?L4hblc0g56Fkzg3E06T+ufiPpAygATa0!xE)3rR=Lia
+W@wBI584{#H_;NaP$TV?`(eLrE*G3W(ZuUN4{nv<S`+RpLM-rPhlzH`i<aKSYP?#Z`o1^ct5ViWuY0N
+qhOjt9@zlYxtuv&ztw(9AMqT<WdW6R2XjRwpt6*2i|7D0e%;PE1G^4Hyd$CHQLll)+<?j1292t{SuMJ
+izu&vI0hln@JY-zD`j1z#i)+()lckloXEMxX}lGq>87&+_6#**xAn~7#aP5(GyFez)0tj)4acQ-OC$5
+T${3>WMRe<T%fNOI>3tXOpTAtfW3PDP>uKnQ@OPq)TAZ$fZ0<P<wxp8}G<s-n57BWfV?V>t`9hSX2RD
+)g!w)=GG%0uiIF@P5Ye%C5q{Ig)RN+wt#-&Hdj>7kPm>1B+P!4dG!*S$rn1;&sgHEN3<wG}5KYYK6{?
+;&njXa!}x_(Z_q-5Yp4v$MokTb_#K0Bp-5CPr!xX$Qcc0?l#4$<-KySf%o!BD;CteWHJKjKe9=f6T!i
+G9Q5p&RJ)TdDB;0_mLVLO?^e48}F1c$i78E0kO(wm<#MaZCjAs?E1Sh=sGflBI-fz|z*Of!SXa9(ZZe
+<#nxhgqNP<ZBa-Zi@8#}xmS-e1U4e>zYi1|Fv>!5?V@=D@=P~QR$K)^&=cO>?%Y<_Szn@<o?z`zi&OR
+<XOsQf#DE4!4D&{WQ8YqC&on$l&omdr&wNZD4O{)1Z|heP8cYF<QFCWHjXQ{pBm2Ag&JSwvKcild67`
+(DoOQxJ$k6E!*d^3Bdkb!8gGboU!T_*v7d}c%SsJRzX$`e4g)1{{HjAyR)WVectAD-zaccc1ofogNAA
+IT%&Y-WW(AW{LqsBz<?~(<zIQ%zF?q~hZ5J$W(p9BDusMeH&Vmt?hh<Yvl%fhO26y54$c5@i9Df`I5d
+wf+0$OlLz0in@he#D;M)B34-h^-01y%~k}x7)`bJB>(-sSUq(m-p-nMyb1GQ0@otgU!14@?t8Q!tRq!
+$E^-#Q6?OEwHikIy*!9ajfw!t$K5Vu@oG5t2J*vW^%TdV4Yn7r5i8!(aT5-^VgqwoHlhzYku;OGkQ*>
+;?@JM0hD+!yhhP1uw~eTSo1krPsus$MeOrI-51#-waKz3bsR3BD&RBXRnX^|%bq$3HNjJo>+wAcc`z;
+uu4(8~=2r%IGUp}D?1>FtjT~<sfE5>@fhzM}}H#?si(@_@xr#~#9u<Mh;I;4svT}P7sk!v{9Y=omEJT
+lV!g5OyoJw!oYR*!tG0IRJNyckcP&FI5HpH9KlCJPvk-^0RW&}7}`K)gqzC;tafO9KQH000080P~d=N
+8pYFHH-oP0M`cq02lxO0B~t=FJo_QZDDR?b1z?CX>MtBUtcb8d3}{li`y^|hVT9rBE7_rn(cKVr=?2^
+Jrw#y3BhQsaUxchf+UlqrT@L7FWZWp&0*J?Jo9Geor$tqt%|R-d8G#+jgppf$>o7iA;^iJFw}KaGE=O
+OYqjb~UAMjMHPkiX-AQRn=!;RJw@`<35jf1QQnq_I39wwrl=L)2o&vOx)oOr0kTRbA={|h~aZ2nbe7;
+W#ZfqjI;PZXb0M*SgvETgZ4f?3g1B=i1NiL0b7DxDVINs-bd6V%Y1@7mZj{pPz%N-J16bY&sZ!Fo8zX
+ie9Y$lbY9X!tC8;@rLiD(uJ*kl$FnnVJU85^N27O~OO1dWk^<W5E^4+ikoz=+<413X!h$W0;vnHn6KX
+=qdYo7%241C74#7K&c3Z^80eZ8Mh^+?`iDo6@G(W33EYasXSK9)p^LS@VNb_`N@J#z1br>mD}5Hv-I`
+%15t+H^pxbf*E3P^MH|cY@0Y+Ht9+sLHJq{+v9eb%H1$tfVW9D2)b9(F>awV=vnP#>tyA+nXdAn9xX1
+t00;w%Qdw^0qw6vh(JGzSTQ`ClL%19SMoFwVGl#hTvb-w1F{b!R@V@1;hhm6copEHn@WEBGYdRX<aY6
+Oyh07bK@HDZV(B7)|jnW#hQ)jhr(p?&ix2@nW>85bPjZg;aj@sr3CRFD|w*Frw)ERU#DzK>pL}mhcJJ
+IY{xT4qPF!5Yw^hpE$*vOl=cMto8!AA6%Vl=vRyDJ?FTN0Kdq&g0(Y2_OKmZO~2*L+-2-{;~D&fwxQb
+|3cTfWf7KlO;c?=;5Q*O6LmiNH61RldyyMlhF&mD?o6}cIg8jE^Qlpih=OGO5LQr-MB8czLT=}4^T@3
+1QY-O00;o{l@>=65Bi<>0ssKV2LJ#X0001RX>c!NZ*6U1Ze(*WV{dJ6Y-Mz5Z*DGdd977#Z<{a>{_bC
+K%2EMcK<n2?e8{p)s`ip7$uw1MWl=B*D_~@sRY{fqzOzk2NZLuA#s{$Po_l-lY*N7spfs&jEf}Sc6%`
+i>q6f+IS}|((gfw!wktEe7RaONLkz_G1il|KXc4NCG<l|^Dq2uYd+1JVPdiotg==FN!owkDuSFTx}dl
+JyP%9+Y|2~tHup(Yktr9Al;Bt!q&8EM_q#dv-_TP`rnGXZ=Y%x&!qO;C!SXlw6!#A3Ahd@}ob6S~lM&
+6fV0&Oa@}UjMn>-Co}BdT@7VY)&478f#eCZ`ZGYXV)`2`mvnuX_<sq(7yr?JhCHAehhpL2}`3oSKxn!
+liW2ay7mD$+e3Y6UB*Uv3f5)jJdSdB;vzu~^#^5wMG5$$*oqH=xI98s`Q`;$i~$mI&c`tmu4^6nSe$o
+;e31t0`0D<MjacR2e|e389U&ynBPsRBHM(Z_+~(^IO<)9%0H>&80`}9YI6N?@B}*XXt+)*nXkxW>85I
+nt%4t=Ok;@uQ#73*$?%ldrXGy|J^sp2)BSwUs$HSPFnWEH{EKdh;8Hts|&*kG&Q;-dh0o}9G2cV6-;N
+aufnjEa-BB8ue5@Tz8u-~5}f+#TyeQr8!bbQ|&F`PvI!5MUPkr~I(k&5G;67y236NHh4_K<cp3DXL5>
+Q6M9<j|QEnL54<VxBO=vhn4;seo;h|Mp+=-~PY!w<i0xzq&0|BqS3-R^nSVMbMStb>)5-94hqM(Ghjo
+OpOI&=XL*&<_5=lCbAM+uh#4=5uO_HD*KIdX^Pk%1Cx^$=$SdccY{Z^k*?Q;HW_P+TV&gT_f~G|wW|!
+n;BSK^_MVsC{lP_+?u!UotN{%>yxJee%UP|Mayw42+eb5^pGvdgp8l;a;MuW`@s70%G_`FVcJtb`{#Y
+*ANpc%Rse}m;Jc&%P?g*MOoXqFb`4Db+4Mi;#RQgW4kawknSvY`HIl(WUm2QXAZV?{&15ir?1QY-O00
+;o{l@>=m_|e-o0{{RT2><{V0001RX>c!NZ*6U1Ze(*WW^!d^dSxzfd8JlOi`y^|z57>)K(UiL-kukHT
+39HQE)<p?wu{i(V@E`mTuDwg{q-G5c5ElkVOt*}X}+ZQW;9BrRYNGPI@@WWl<=li+7k9=l;|wbu#Tc$
+ZcgSrZ+RP4&K)P&8#7R-reU%iVmyT9Xro%t%yK17R%w7gFj$7wXn5QKh(Y<&Ego30gD8p$!Hgk~-oB5
+Y^yB(IBKWOVD?dJUw(Tt8(pV-0l%!?Fp4DJbL^S;FxP}I#HKbCyVK$4r+u4d~Ac7l<p6I|xfdID{4W!
+|gAAlG<94eJAL1(kAC2N4>Bo44*FY63OMd4sW$fs7Tn!KpiGx9npOowRn$@za6LfA^Eni{KtH3p+3D|
+ol}cXyb%%ve0DUZvz(whr)1sIVHi<d)LJfT&WkVY)UrZ1%^V;&4n%W-?1eN}R(wJNr*PqtO$MvZ5&|A
+~zW;{RAmU&KB;Aw>nPocRRrur!$ihjLqZQdRDlwd2FoGaOGREPT2grk}%gaVA+A`Jg&I7^xA=F^VPEf
+Vs{WS?^59wHI~F>QZNAV_W>8HSs?$d+<ozuSnrb5ApP}_HBmnC87AeUq*Z~mipz2<xZImX&YYSqsRzn
+f+kz}_aQ5<ggR$oy-<Ak-a#x|mZR_9-Wt<{#UhK$`3qjtHtFpu*x5^V5UlSJhyu=^pMFd`iDwtTLWJx
+nCAZm4YtBM|(FtCLDYZaQufot0_0rG&@m?b-ANG;SG6SzqM2B|0&k;?X|Uql}q#A$oo5dMXdZD&9)a<
+QQJe=44&I1T6~eQZF2G`VfJ*j&M(nZs$BMrG#OueLoNT5Hv{$(MAFc+$vpC;J}OH`$c9jx_0Fp>NVcN
+voB>Zs}ehtWtQ-9=+-zXfTXT)@~;pMymZgMGN;fq3`%mCH;PxSJ9H|^kS<qMtCIX{^*W|QA%Fed8#H&
+B_9G$WOK5|t9`Z|MRQYM@>F47&Zb^8w{lsT8#&3`#_V}ZHPrBaHtI!VI}G}{AICo#3?I?6*C3#cDle*
+N!gW5GPb$T=7=#I~mIABEW;3IAl6imZ@cdy>W=D;6mw4~bB>p9_0{;R~O9KQH000080P~d=M;;c?ovH
+x<0M-Kl02BZK0B~t=FJo_QZDDR?b1!IRY;Z1cd2Lcli`y^|zUx;EE<p~YX3wSRB`j>up{KA2#U9)0S`
+Q?dtpEMWvgJf!busdMFU?GAwFJi6(T^4x10jvJ9{APiVf4tBJ0b0iGs09??y%NUa@8d8X}KQY{O@#nJ
+)iGj{H)P<p_O~WzFMm-A*3F-bMS_PSqtU*-TxvGPEi!+Uf-Y(2xzs1)!KzZ1Sl>MjzDJsjekbRyZKi_
+0PKv_9sFLYe^SAX;clmy{k=y;Ndz|7f*n=qaA*&pmj@ky+s>^q=d(RJ(bJRDoi(EN2k6i<C!`p-WvN6
+q6>q1z<a~Yr4Wcm|g`4m_C>^aqIOqd;zy_{UbY@H_;Tfw8w+=(la-V6{vSDk(W`)bk`>gMKOc~*ZpfC
+ugUTpMblJHkG_!)<nr?eRMM{NtR)kJ$YRoa74c{_5IZp5$+$8TFyCY?RWIMQ{2aiOF7Q2MsXL$PxOh2
+3WVp0Pr%XqNl0dKEVY!zxnArz^swN07*s-!vojDTn34VYc2Sg`YsH|5~UrsO}#KDdaKl#$^lpFt`=5%
++yxK3gy{6pYlQQssCS4O9KQH000080P~d=M*}W(qBjlz07E$d02crN0B~t=FJo_QZDDR?b1!Lbb97;B
+Y%Xwl%^O{B<2Ld=zk+bFu)Vdl_jm~E0=wDmU4TOoG`qJCxj>*L+EycrDoG{r%l-D7;fF*@lt0owoKSQ
+vab`4}?-?o^!S^IhH;rlpOH)$pYc3R_Yst$-vDDmOUD@X)-)@U)>u!0?1XWzL_tO1RY+rARlDT`iYgA
+FXyB8`dAS17oI9y*5xZJ{yvGR(&W~^4^RzLe91Q!NKuaXt{l~?TQYJ;rTMV$!thr|Dk{m~SH?OCPnnd
+(5+RI-fk_q56*pf_fv121_-RlzIifS(JY8d|dIIjwlQE%}<33z8R7;kINdvmH?C2ozP$ULEos&7SC%{
+ellg2V-{Wzh$O(d0R<E%ThC-H@_g3L3?*6*mJ=f`Kc+&{Bt49<Ewc~+R@AQIX7?U-m3X^0IZUz_Ph{1
+fMkud9hiF&%RtwMK4{KrSkdfuvfcA%mfu!)Mg8~hM%jHdv<`WYU??rSJC;4AHC4Nmzf``cjN&cWcf7f
+w0KG83HG2Slb%o~11Xzf*(`3ukzx1<NnpSiV7Cr~CtfW#pIa}GzhW0QwRyUbUe7%s`s8bST@A^uwEy>
+;C?v~8ZzPQ=}5848loRdZZBSdu6iL6;xYzii1Vv+xSb5Cl)e+L1RMwIRIe}NsA?MiMnP_P~V7eIf{6(
+f>2B4dP#Y*#!Zn1(}>przzQHZ_V6^l$cpoM|CXJpN!~sG7+F{H@4#`1<D}XPOeN2&3`<4tyYr+npQ;1
+yEixuqSX7Ug=%J@|w99u{;T6iMjwaP3`j3@YzU^D?D~kguH{7Z^?tT;z#mRRE|HB2dk8&7W=Y=Pm*MW
+(ozJ(%%<N$=wZo#ujfqq8vOYrLK&zQjs6(UPgbKy`$G$ia?%L00fQ9>lxW{d6D!-CZ6MAURiV;!CRw>
+zkoQz<CH(XL>BXA5p#?#G7COg*AjUjt3HYd2Eh@yD6j+wP2f`=reh87!T>pUG&AKR+Wbi*FXaV||&FA
+N0vAj@pU9t;Me9d2grlTT;OT;;f)Gp#RU8?B<{=ZZpHU__g)tpJSnwlk~-CN!>MW3lyO@~XMQ`(ejh5
+0aC*|D--4Qv6t5xnvzAoO!WrU8QC^V7l6Bx+Yk@=^eH#TA5?x}*?T*ej$w9W^jSeAsXdyht}n&c5lJW
+MsK)>aCzT!?-Y@<}qm4i;{aO)Q(i_g*?MMwLUti%hZt@b>xs{N=|!sOD*XB6O;R*s&im}sq$u%&z5XV
+eLAi(SmPT%mP6**FUVvlI}Gq!DsoVoM4Xo$RZ|H`K>(417o{ij#kn%|{GydWhH3c%hms<@)Rn#&-B@#
+qDkQ!}GbFc~N>C(Am1z0^#%aD;J4MHF8OGBAXaJ;)W(cn$pxmOw*b_BaYo`hMip~TfJwx%sPq3y2mLQ
+(@)xvb~5F<umb2WpkRAmRG;dCdV@{-8oo4sMV;ZQUz1#{UrCQ}WQj=`KMTPW8mVzdM1x*#n%5`-!C3}
+D3u1r4=@hzr;4zp0msFLSC`GQDiyx5ko+Z3Rp(TOWvS-vFg!Kxgj%R#VXi)KcoUJ!{2*zHB{wesWiyJ
+3(&(Mj}tTl!s#9Xjd*thNcVh5ic^jMzUc@)ZQyuii@c%)Z2V`;u4>A*1Ln+L34sco!r}E`ar<4$pPx-
+SlpaIqEY~`2OmDv1(a;%f&BM_ZV<~43#`1~eZ0H-{PF9@cmJ`kf&XHZzC%UHUt~gN_l)^$<veS!5HTG
+Ta)TRBzTJQME&cTI{!5e^f^8by2V@v*r{<Fj<<GX;sQ9T&hf5dAy)|hH&b-A+5sQfF280PdR~Ao9_ax
+v9do9t}4s#C0tBIUu2N?8_3|;d^`C0EhwlMGYi;^g*+4qmX{d)WBKd#9qXjH)rOm$<)LZ>POG&mA@no
+7%+dqIpIwwFb=Aj77K?4dD2Cf967A-v=MUm=Mo2+tR5NN^sn6>&lL$$we3(P|DtuK=?&Ei29-o|*9?o
+pBTs6$Dy>48)b@yehe9vy<gA<NF#UIEmWbq_ew8L?DwmvfJ}lK#T49#$b{9V3h|Otq<Ac><XyAhl-!I
+jSckTm+ZrE`<ZLQ%Kj|VUIfQTt4AvrM14pxZ$w)c74)bV;P9$Ecvu(tPAx7@9nM^SLG{FaBr6-J41qQ
+jOC_TaNO0hjgVU6})-#E=H6dLu6|Kl87yxmHpvEefMCul{!$0Lep=>kYrV50l=NGOU_kj2GiCG%yR`8
+|{MHf^CNI}Dd($KwqO$DUWsMUGvSY9xp!WaH+;c+*=1kDLz?#(nzPi`|#y3s>cJK(T8aj(P48cs4KO4
+yrtbmJyMZlI`y07qR4BZeTMHI(nQC~(omLsv@J!E(|P(Mbz}@s8(2|Dx?Y%qAPC;>j9n_hzkUk<4VsP
+T{69G|{v?BvDocQ;n#?>2jz{<UNHH`6O3PsokScr&!}qm1ewBf|nh5@Hj;2gu3$N8~R*X@|Tpo>b%iW
+4Ijr)nhNBkJFgy6w-7_aLU!bB{!)kvp#UoFJl%1rIza_b$KC~zzy^X25uSzMw(m>3_N}LrF@V0;X}ew
+7e+Qn2&LEnqZ~8c>^K3nsfk|FI+fbg`xNgI(g_+0j%$VNw4aWUUFVLZNVQAx6N&GR%b+ENI;`Ev<h+L
+Svqtx0_T%1(93^X{17>cEYt|k6E8^Cz{dV`4**cM{$Mckm1LezMVg<ZqLV{B7F>l1Dapkjwmvv~-Hm!
+Jh?rol1NYygPY<c3zdu&9|pZ->z5nlrZ}J;5_w$+E5NV?!pOhTS?+doE;w`K+i+tn7GChM4nSTnVI=K
+<EXxm^rS_1qMId6t644d&Wb_v>UX#lf;N3I-tue)rTFv%^iv+`==a#&*o9$0nV8DbwKmJoy;7Lp0COn
+imqGyj1%x-(F=)_?g;j%&H3k)Bj)y1lhFqrX8tS58rwPB+jV{UqMJUC*cxV~qH5Tmu&5p1v(=RR*jJW
+kG~t-T^XlG0&#Bvt2pA7tAh1s#;g#+2#j6rD?GR%22>z}CLcyEkPN2HsGc#K<gCl+Ncun3(9B?hB$?v
+?VX59<sed+kDJ|2(VdcYfU;STc?{4vz}#G9jQY_fx{c+H>!awo^&KWL9y-)pANGN0jD5gVjTaB^ZkS4
+ha69wNLD+9(cnik;6un2?$v7|5pB0wmOGDi*RPnxUlEZ{Za?!49E36VV3~x_q_i(B|kiAL1OqKD)jOs
+Mzvy6`=Qu*`sA_K+R?}C`lBaqt4KIW}Xe{HUTYlm;ysWrY11zT6rh#Eit`>h;6CSs}%=PX@Kcs(BRyE
+_>xG#v9@oGZ!FjV``w^#3J!srM_YhC1KYK3(FP!7ARh}jP_d)+WsT6tdY-854ERLP(^8Y2eUFgI3yHm
+6o|v6$nvi|>h1RGsT`dSa5>Puj8UVX#3%T7uG-X+1j?=&`j7>H<U*K?4&!t|l4H+SYkW)#P|0J7|Zet
+#S*HHT>B5xF_{es7b()@-WnhXoII9L8<Y=7lDg7&_VauF|YiU*FXfV=Q3qPT6f1Xm}xvvuOCS2xCi{T
+g{S2)?6e_Q0#fu=jCbGvMvoBYhhO?Uy;LvHIsIdfV3&UYitIixcS`Jo&Au|9nI+enH)d$A20}>`H-Eu
+QV9Z@>qA~U-iLG?^;gm{fPLWKf~nM$;>`T_k2vbUf^DNzhizncw`1Jyl(I%Y0oX<UeE(F?WWg#nhe%v
+EAt&mf?xlfWd9tFA+4CN69O(02*5u3a_IAhcbuOLiMHP)M&34y`6T8PWW#)=W^RrKs&n$2yMDm)q-73
+~p_)Rz;(7pFvR9&q^wJ!r=n#b8*o{*$xoKermYNpqQxB!|mwq;Nz2E_@{n}0*p@!S-ecBbf`ig;a4-;
+>yC)YD$9K(cx7cj$)2_@iq%{F+f>e|&&w3oFjX6z?}aibpL{&@1SmVOk*WD2YO)x+a)=&-*y3LcjsUO
+y1;%oz&drc~F^Wo>AIU8~Wwt-FuV==xm!wII@x`0?up{Ok?CCBb8(V{$hse1>(pvsHa86Y*)htY4p=9
+Qjw@*euh>b=+$WFVV<xSUe(Y19y91_ZAhxzVsJ$TtMi*$YK-OWH?#GgW|)fWbLjec38RZx)Z7t<~;?j
+GR3GposaVgxpZ$^$T5(O{!;Z1?Cfw?<GOV8fE)nogmDyhG-d|`H&~kEmS#Y7(SCgj3Dq9EOpgTO9wlU
+JfPWr#ySX1%WV*!ZLzLK2TJ{t9FYX)i({HY+9FORZrFJ%gH%C<M&Aq0?4Hyvr2+pQMJI-w|*0}df*n6
+*@ZM3&<$ssPNgqwIc<zJ_5F*NdVf9x0-CHHt#oE?Gl<$2OfLV<fi(h@&|XS{}NTYHWE=|Y_Q!Pp`)Vs
+)3EJ`PqOCF49OPW}gSqp3FaIT^??aensE;7x5yo%o5!*p#{ZvyAAtH=F?$Yui)z{~BdB1%>GWXMec)F
+s*?;U``)<(<nV1!N16;a0*-kMP$OhBN7}fQN-!ZtF<=`xnGcYWI?HfQ|lh-k;vTBlWly*)pX9-$X!!A
+tL}zG@4C+}4VMYIw|@c=`9#qt6@34!TUYlLG+InDJ)V<$vUo!g;b!GyLhNS#c=ULt@bUVK1*-d#LY@*
+QCCH{30Jp~k;S|_?WPXB@IcX)e`JBUyo=tvlwQ~kPIg_xb{QpUh=(y*r(foa0BKAr!Ho$vBdgXY)jKU
+le8x9`K%QbX77x+QNMk5<Zj*f3e1s(3FxB322kOQ+HT=XLS)&BrcO9KQH000080P~d=M{$pn5HJS-08
+<<Q02BZK0B~t=FJo_QZDDR?b1!UZb963ndCghfYveW%f8W1Cqy*bnujzAO`_Pm?;JB8y(1#-k#nx_o(
+ON!9PO|@fXY^rP{@7f&9Mlh4TbdcojDGVWWv}FsP`d9-r#PjgKDJUB!tS*co#E7dZ#I5h$ity-4x7y$
+`P#ZI=o4=$sRCpBc%X`F*{PE25!pS{yp+cyYpOtc?LG$@{!;SR)Uwfm<o8+|Ro{0w_(_@@*^TMzrs6M
+w^hT;9GuN^)N{XE9kA{{;(7hejIr)>7kL<v2kV)m_Z_ZTtFvwE;Ymju?Xv2i~i+}BE#gDu(0~u%{dhH
+I}(Ap96idG|CVLkk$wWhqP4Z9b7kn#M$xd<};#gA)NK8y&;V<lrh9N8nMZj2t3?An}I<K4$*v%!7git
+NG$6`<QkJC;(~up?}324tmRT9c2Uns<Kcf3vF%f#>4l!u}!!=}jZ8^PR?3OVL%Z010;KVq;mne>Gu>6
+9N3dS2u)_5_WzIvUsCwQ$KN{!6KNH3O1sMmPJLuh6LFYJJM;82}6N`kjqxd1NrP7bQdOc2X?3va<3>~
+Bu>$a?*Z_2QyWTC&BZ<^ADB9TaUVWBK6^(vkuA7y<gA<^c~<n?W4=w#2iu2xr9l^@FT(enqF`0!w>9l
+2@kzEa$>Hx-cBW$Btt5};S&2NmL>mcFYgET41YjiVgxTFNp?N&@z(u=rP72a{ACVMdaJs=+lD$Rq&S~
+DYMxyW^qj`}2u9|bFd0_uBng{6waQuarpwQct6i!JYOD5i1!!)_wyP$olmFbw^%@Z*X%#a7B$wA0_CS
+VoF?3myrX{52CPLfELSJlyr({?Eqvtjwc{3u+`_+Z;<u4cArvr85xzJK5TXv7I)rR~gsx4|fI7%sbo^
+u}>B-?E$AR=qpU(u-T0r4eF$tGM8hfe9Agia&87TP$hhi7Sm6v>?Bog2<d)ENtWj)FdL&2YV<wnrMgL
+Ucg1Pm;I|NP(BgP3RpU-UoNi}EmPpek%ZA`fdxdFSi~>+Xp!tKY++}zQAxP(YEe<YzjSZ=1YaL~u?fp
+{N@&zsqy3|TF%&66t`*1h!~$T56G&JKao?&MCyj*Qs<jIt-y9W-(iDgGtciP=7)mIfin@8!JFjgq48<
+TBjM$Cri7_aIe5U-xaMdu;ON@t?Sh@yaQSWPBO_+v>B{ulGw%T}VjTLb2VLn(7V=*~I5;i6DL25IQMW
+ZnTY!qfXTJaWIT;7zYiG$Y&Vlf^kJO4Y26$GE^nkm$<wHRTm-iN6oRPC$DGJtxJL&J3H>4(7JVk9M0&
+=o0R`V}dmnypCbEAf&Ty~c&{0Cjxe8T)ClFLKz(RnYR=?mG`DDktXDVw$!q*sus`oC**Hje1CC>0F%m
+zNa9+TG=kYRDthmmNs2O4*6#<)7d0G$tCnG*PT+3jIRiMe6*U_*Uy*wE}LZi@VM1o?_fK7cho3)TgOJ
+vMU0$z-Vtlw9a09Q_0WbF{pcO}c>u?YQH%}@OJHuKBZxgX_Fi&(Xr#iv&JuEsF2W`#Pl_+HZqi|aEL%
+x*sOsHNSf&B%rl<Hp#GE<erM3m5SQz_^V276X?c9UyLo5hK<`}da4XqeGa>F19Sei^tDa?yWvN;}Sbe
+L1&{I`xVG(D_?JP^bytF598pXb^<xWMxj*5|=K(g?Jcek@koSIk|+J*m%UEAxUh+lo!CNT&lM^{wZ70
+QBA8?kqAEvcb1PWf=-{I(HD~Lxx>y2PYn|S0S`?S0rFUbY%hoZ_*5%2R|{Ry|ADqUQz(paAOg?U)U+|
+fyyU~%oOMYYwpuIX7NsDw{RU(;7y5z<Qe!r@>5Pe$p-RcttDo+p(P0e^e8L79Ihjqccb?Oq)$h%*6c{
+qHII#aZssQ=qfT>E@j>VTrw6Ld&T23^h4-BN1IK|0I>Yl5fy0}X0WGC7{K#1|KB<Hn1_VdwcUHu7?bg
+UG*4RaYI$mYj7JLu`b<@qIxO=|E%e`f3>yl<oM9;z++0+<y{!%Hrm@G8gb321>E$}6Sh<t{OGT%^q{5
+2wOPkgbM#(>W!3olF4_VN*gxm(C(6yp<=TQ4c`a_e+n-DQ0MVbbtW`v(l#Y}lvVzBO1~1j%!6T^2f$b
+0C69q&B+NQRKtPQY~FGoCCp#UswH|;`lk$TvWbWjJ;s{r?~SBed&5@^gVUr+iJvX^<i=hcDEM&76uFE
+nogSRp>h8|8*6tzXzB<tLB6XuR(PCS7C2w=kh}bjoiQhjO@vam6YO|jF^klz`57SOt|iOgQgwZgIsU&
+ziUSJ`C6-?dEIh_sLeq7rvxHodGZi^Hw5*eb_*<tyq?kpLIK3?=hFEc@^ecy|Y=Yh4aDu`nq0^t7*CM
+CCyIwqXyzi-X;jBt_JF<Fg4Z0Pq#g^D@{x=0t;LJAv0Z>Z=1QY-O00;o{l@>?Y>*thL1^@v35dZ)d00
+01RX>c!NZ*6U1Ze(*Wb7f(2V`wgLd97GmZ`(E$e)q2+S_G0>M<}rEC71_vX@(WZ(qMM50iGc+5^b}QN
+R^~wdqw~I&Y?)^=5BqM1K1LI&c}28NM@^KMY3$$m`1TIBfP4mGK4;ASvH1c?fY!z#!I=|ak2BS`lS7%
+VMlXNw4Tkj2wC$weWaS@vZ|;kJnnV-Iw#AA>+754vJ)=kL6njP$$MV^S(U2JSBeU4jR>+_6zmuTPxtS
+;smAb9`>7o>nPIBpf|i-)f3uNX>fG`2h$~D6qn#i-=Bzec3O(}SMk%Q}Ic=zEbe7A41<AV)*>7)G*T3
+E@vtK?u+`a3hvu7p>sk~tQw96FJvQatH6RA}5V_M1Q4)|yzljS7`duOv5`oR=g`23{c`K=v`vrNzmGM
+j){UQ(^el2MiKofonDO|E7HJ}+LpXs5{*ti#Zfn&wY*2WubpQZs^R1m<)Ddg?@SDGcQhogY6nOr3s80
+m4FA!3Bx5#f$7%qX7v9y$m52wURrsmTibP9>ojzRRG7?Wo<&^yj^KDv!VstLLqa(O_s%)mD@RaMb%Ej
+&#R|{^RLCMAufX`%n2$aY0&R$c8VaA=2ek_wK}DReE_rLAc3Fg^3v94G#?B@S(rt1Zl+>}K2x=b9)7=
+_C~av|ngu$Y)~A|hjVgyCdsfzq=x1m(0{TR5Awp&^1-WnIL*CnFi5(QVa1Tk8%!Z!TRB5K5x!~J4*Rq
+|GO&!0P51miCkgG<xXuBNF3C6A$Z@wi$8asM5FkMj&R`=T}j(+1>V@E3Xso{zhsMb_a<s@zkiP!>3(x
+}Ue9f1<T6~ds6)}9*;6V~=tJ+{KZ*bc;r8e|(NugdDi0i_qLz$gb^m_6v+Nx}Nw0xJR?6tXcX*VLF(o
+D3bGAWVoVPS$TW-IBH9z)QdokT@NqJ@XOBV)&Z$?Fmkd!q0&7MPTC5@PxZU#$6!Wq>sYc%Q}`FmMggD
+W?SA45(f^x+tg%_4KIsKGsr!(lLsSI6E`-BpWhMyd%91=Vd=S7PX_8356~QtXu4$TnW=jvkEcFBSZWC
+2ss)jJVSSE}@uDjqlE`tFANc}9QbozR93UL<Ge&n*fVTDmH{0LUo{^GkL)v`;o#7ggXjz^JZKr?+z#D
+C&PX1uzK%s-eoQ3Il9rIv3Xit-9Be4a!D7j!MEDY-a-@`n34;mI{Y>F_FKtqrNBRLg7`79lnAJ$!v&t
+L2avN`w(s$^KDZVQ3IbsOAGkHr1PU}t@-I|1i}7jUnAgdCjn)0yoNsR{<2w(Ch@J6l0;9s7J(h#uq3Y
+`}JCHc}oq6n3d6cG6=W@RMuQ*kC{g)A;d4g+KG@Y*%CM>5Pm|CkU<G^X42B`hr0CJDmR#-dL&GzxlB4
+R@<bsuHpO=yOAD);^!qs%3wutTT76K-5&NxpSU?xbY0S%g;e7)(i%6w;oC0N3lEaHq4?|8jZ$&Wl~7c
+;g|yHAtz)BTHLR(#)>lSh=TdMQRt4Dutx?hl4z%EF<p5MAv1JatEPapJuoOHuZBtQ)WgAWdB|&RGo_Z
+m1wq#ad;Y7N_Nhrl<S~d)du}d-(=c1(5V?oLBiX4L*orGc-I<{D;#uZ0PWRAf4OFJSvw`bX-Xbv}vO1
+Nzcdup<m;h@WpJ3I_|p<Oi0k3pxsIu%)viXP**WFqd-OETu7Bq6WK_w7moj`(HCimb<A>pF!4>_?Uxd
+(3VRrnc|<C;Idt)f4av>~LoDzf&Hb#-pfDK%?^ogaczZrLUI+Z)~=)+N)1k1Ilg7O#m8_Ua-U8`BMCf
+C%y4iN*dXBc_u%X<cFbN@)aB6=r0jLk|OwfMdB&aYw}%^q#q@Rb84&ALE*lJcZ&-R3ts4mT(k&j`LWS
+v3YN^t1jDG)Fskik3=TV68RG)Un6GS(A@kFUnsQRn_VTct@VJN*ia$2?0)mLiMAvTY-JN#4`viNbVRn
+%*dG<F>WX!YL;18OT_DHw|JipjDK+*fV<?8M2?ajL@NC1gj-6Wb1P|q(t-Hg6%m2U!{K-|7v-7HsEIB
+p@FrQOzO|4~351jqjY+z9)Pmzwc@sr^xJ9B&_LmnTQ90-7x5&@x?vIcE3rbIaeu60w)LthbkT^xc}AB
+(r}2P)h>@6aWAK2mtey7DrNv&_*E!003?i000yK003}la4%zTZEaz0WOFZbXm58eaCyC0-EZ4A5P$by
+LAWR+6}Hf4W8N0zqbt@nL6YsoFjz*SZPl`<l2p=Q*#Eveilju#O52zDA+o9a9q;3J$742990;YGMmLg
+EN{T}*q$cb^iL%k0w!bHn4a)HYE6TKHQgP`duQ<<d%*w>Blw!Lrs;&DelttG?ojx$dGjTYuDt8Lb+n=
+3`(o8psW+Lax3Elm5etvm(HzSv~x7W9wEPdovF1#`IeoG}+qLCSgZ6|07n7VW}rCY8)n6;SFiXFh?1j
+MqEDMjvf;>j8l+h?+v5co`|Q+!E^Y9r)<=|WTxA<=+y6(_L$K}Zg7!Sb@?IjLE;XIrjP02&3j6<84|&
+=Q%IMu7k<ISE2u*HUcBO4O)ct;sJ|%daF&y&~0sUGf=qjaYQTifCf6Ip090i>lC+#)_Al8F|CxR>9-V
+{>i%9Zc;;ldf}K66i-r*?&CTb$fVgJr{IE0S#G|7VX>FMPc+{|Gx$5-6eU;DEO-eS*URYC8<wG}Wu!E
+Onrhkb0d&KfQZKJqsd)Hm$IE)z#!K%w*)(NI%0fZ*Hob6}T?vGfaF*>{^wKDqgdVo^PXWlKwl31G6ip
+q!orOV2y1>h(vN5*_o)L^;FY3jHR6?88MPPnQ;`E8h3Md>$rUVw`rsPme1X%M}<eV^PaZ5IE1D^`*?<
+<oW3ca#r<QBg++;9r_?!1LUf`BSmSDeXgM;)OkvMx5D<k<XL7L}$J8r!FLIyFH~7T-6?F50?>8pR;KV
+23YM^L)o3(Gmi$p<zK%v0ZeT&LQ`*cxYOa$PJntESk`@9D3wZ!O9G>keM=rykYMOX_J!oM?8}JYg2&x
+gc&u^tH_pDTrv$Jk%zZckm_$b0-c7QfpsKG`F1-;N87%UgVGFY;5iK(?(A8DNT$1*Tg7z>9Qgt$)A7<
+$JVA9QGg2pp?KN&3G|u!%$URVvDz4XUhE0$S9{I59D>6z9=Rl8<TN?;2qc6C;^7gG@r**sUe(ModGjP
+bRdiiz7702%-`6uis2#ywJv%uA`3q&@RPSRyI6mn^HQa<gWm`g8+t#Telxb>WyxEdW!I8N4`nt%N_qy
+RPjXh}Z7f#P2rfWA4??06l*R45anD&sM3F*{1&1+2XX&Rq1kbX0Y($nE9%_3ecsa40opP9C6_Flm}%i
+aHJHybUS<g9XO39aGdv#8Im-I@$<7Wl<U87!aJn9z@NX4mv>AP|1dJWvc43&~Y@2lGWRmLM4XSXjqQ}
+g-Y>z`b88~9Og7E0R#;7^t0o2NjOp98P~IrB|~*T5Vnt(_h%Pp_h+X9*Q3B7c73D=ayb2t6Mc=%@Tq{
+qqQL{Bs3V1kim|n8FF%suQ+?}%)h_m<={~9FXHp1Oo9_RAr11M;;gyCSR)ckVG$wtSmqIhtbs{dN5v~
+H+XboEX3qL}=n?K*p-+#Kg{(0(k$Znrk<aaG{vA{_hJIb&p`2%*x9=SalTjXa{j{wZ-XWV=z^54213A
+YStFCNR@og+?8iKO^FcE)xhPCvyX67=*TSt%6ns{<D7`7`n$j-0BwJ40ZCwWIja80tRJP8|5WhBTopY
+rq}ZeVdz`6#g!YjKd^(oB)?%umJNK7>=2U;YAR6tZW=ipqWcO&kC6}g(g~%Y{#>`@g_J!Jcrt08#n37
+#3%wi5JbP&MdrESrN3R+$t)cEf)QqBW^D@PPW#iJn_SxI3kRRxUFPMH4ZLZpJRE@R*3=kc#vpSM7)&C
+8qAun`_&r=P{A)Xcn|X6OpU<bs=~o{ePyTLsvLLV3l)Ux@bb?9Ah^kL8N%Y~XcFb9ih@aSS<tLso&|O
+R6p_Pu?W5)gA$UTI1ydMLfnGzTxZIQeTYb$3AZ0l-F#fP@ohG<`fV?cX2gyHqG;#hod?cnENSn%B;w(
+6$HbrM{p!h$(@8*DeYGViC!saD^WcA?DG4C{Z`=FJ=hM~^EG4;zXOwkOsv#HUzSb)1B@49Agyl5<v}b
+tag-f1-4g!8C|Xc<4E+Ux?8q%FkQ*<4_Hq50BLEm_F{my}ll`mC1ilO9KQH000080P~d=N2`e$KhOgJ
+07MG_02u%P0B~t=FJo_QZDDR?b1!vnX>N0LVQg$JaCwzhZHwbJ5dQ98F$5OJhu8G;a=tIy7D}O%UP~#
+*p(yq^7As3WN#0!keMj<poa}OfMAm3tAB|>I8QlP5Rp&Z`jDcucZ5;5U)v|NQ`gIfy^j5S{<wfWtJJ*
+PEl=#;#o_SH@%wX`_xc!^5j!XF$e(Qw629--;vG|O%Zn?3@%uKbjsw1~p=%(SS992H|%b5qi7uY(XmA
+$gcJ@FS~w27jqklb4MM~QxK(6S%q<S7CgyWK9#6B{{Kyzmp+ED9TB4}>M0B!bh>O?-{E4u%Dmvl#rY^
+jX2eM{e+`SM~^h68caI>pCZ-oun3u&cfAjIJ6g6YXufv*JkZyWH=mBIM<@6gFXO>{1xGdwIIBdh6Wuk
+x#Ql~5*?aGC?pB)pc0Y{osk@vtYw2+p9>>e2WP|E7R}O~<K>1kCBCB6?ctEEKvcXz$RVCUiUYZ`6e7_
+_Hh_oL=o5U=JwRXK<AB2_$g=DX&!d5l%ajT61c6bn?dW99#d~n$IOF~ZO04J_2qhe2dyBG4;R81(OVf
+v!^N<z2HbP>5ojxgenIxN3EQM!T;>n7pWaPcgco{w@jQzynNBn5wl>Q$VVa4f+r9|uUcp3H;124SZJo
+e|N%8hizJL&Q-U)OY>B(L1$v4`+VrbgN33;g`g)i`IWq~xW%5Gs&Q;uRIF&Lv*y>m&7sGJwBpx=KA1P
+;f<gOUXgX8uC)K{)UOhAJy#Ak7Ii2F}>h5R6Ukk`Us(Qnku6+-Azb#KWi0Jy|gb-X#<5;l`u^xIH0RV
+T_{mvNZD+cL~HLu-7^_+qH%-!>|}J;?te<x0VZ>{nobRgI^2a6e9kRWHI$4X&W@dsC9`O)hh`8+$ZZ&
+CPpi-<6pDdSG|p&AH^-M(Dkb<m`!#Gm_dDc(+b7;fnv#`@&>UFd8v@DC-C;DIg)LSJlU|uy>sjOw`R_
+>Ax^3(?Y1PL{it|6T!gRB5P4UKVNmW{E{+0qky^&M0=VK@%fi=3kWw3`hALcE?0;_!Sy=F|VPpGe}bm
+9E5KW&s=0T-^ug0{uYk8PXIe0yb*+Qn@NKfEL955w|K^f&pF4Rs5PFDfH6qbtiww1vt`g|~{j@dmjc_
+GsvSXoL9?k^7AArpP$%fR${6^5eZ6pzk$)qN3M=+sM`2vbP~LTv*(`w{C+M|ED`D?(|UdeFEqaLhlt?
+(QR9r_K%f^y+=5)K&1w6^$@Ra=U-#K98c`KB!&liP!%o^@=yiC);kGw9r;wi_q=nO4fTCWacUb$^gmE
+b0|XQR000O8^OY7ycz{pFGY9|x5*+{l6#xJLaA|NaV{dJ3VQyq|FL!8VWo#~Rd8Jw1kJ~m9fA?QOxF~
+GrENvfyMYO=R*~Kjo9D-gBc?bf5mMEKDSyV~NUiYuR8IqDneK_l$9&DMM8P3O#Gei0(61|H^CURcw?@
+6PJ=sk`s3Mq~xO^Zf1lBFr($682D=uU~U(JZyU7YjFDiv1oWv_FMv-_BH4FrgM!V$JJ#M-|J&@kpz@7
+5h8;v*D5*S*3roGd(%4a`xI1ex}(A-Loeglsyf5%~-9us8mbn#5_tV<d%Gv?8HT*er<S}f8^2(_w17W
+IibmanC?j$&C5=>{Yr&QCFP23NhRcwmi#Z4)>Iz`T=5Q9ZFyT2Sf+)%3`uiV!wf}JdX0|a#Pa9rGq2x
+&weIZM&IiWImh>zB_>E=<P|3WT#XLu~gxN6{)?bUo$H%9C{`b?D^y|MKA3voZpMNI_#4koe;y(nh)}e
+80n9CZObGg})m0A7(o@J}eVzEFNFbM)29^yUIpUl`gO)Gi?Bq3Q=Ql*@>Hcsm?0yg&x0?%@}G~dP)%R
+{Kl4*bLvDTE{!(MTeDc0~|a;7A4-sWQ$~yf6|hFNb-85z$d?!GuzdRBsKRC<-PO(FaCoexj8|0#QJOw
+Gko_x-~DsIJ>eEg$9r_k~b0u*@?*u;g!;~EMxKn8Xg5sGfg4FTcQ|4@kW}BUM@0qPYznw_jh+@2oOng
+Ai|kg$o(Cw?n<he(#{soUBIfCZfY$=sUDJF;`i1PE69?sF!p9(MIctzzFp3hPOS>P1j1f5s}uszpav-
+ojS<wkObg;rUFH15^M;m|s&zJo&AedL0T31`D>Kw66p?hHCFJK?iaq(}F!`N)@Zw2S%`#qaCLc%~$1@
+y}4442KykyrHzC{eyIc18##2>I5Nh8Z2Gn66&Q(G3?5Z8D<IHaQNyN<Z?jcNetfEI9%g|~H~b^Hj|@y
+c@LtbokSE3VUYtyo!X$vZ0d0IhfLUe0dYSn)M%Jb#-Z`pRMxckDjnqGN>gkb(^g6hr#B{XM3LX>MM?+
+;ul;5Eq?SkRPJRm=r;}SZxD>9Kf7dRHWnCBkYzFmC0&~v7k+<lTqFZ$quZnll3z2E3W{zA3)^87KfzS
+h^;`(49h@-eU_^a**Sl>^HN>B-1LiWt~#a9v@vkn-L~GmnWqxz-(=hI3ZQiZkEqJa8@UFNj!Y_u?Bry
+a4T0lUbD^G|rWuYVD_UbhUrhvg`S_|Og`%);?AFKR#1Q>;$yts+S0=y3^~Fzm7{<3cCxZ`I1OTLNa4E
+~Vrn{2ez~XQC8`yGju1Z1kG{fqD1EZ4JZMcHMdW&K<^04KQ=O$3?OR=NnHB?!O8CYG#%?~i=!^$o~9N
+`4EFoksR3KPf@YDEe3g>b26<-?D-CKzFOlV_ksoH15GPsvg(n;}7)G@ob*{pwVre6F&x$r+S37D-kzX
+wqFHbz-HcO(llbMwzIRUoWl5?R3(_h66JS)Zr>uM45fi3kVbF4e-lAvns%nIUls;$|F9~7v^N7dnuYa
+^rJ^<B^a8}HKzF0R4OJX)%E5<2z=^r!C$tXcAHz+T1Ra9k`Mbjdr4h>$WzyeDboRXMSpNM8Gy$&g+2;
+Hi-TiRh-JTrbXwRx(zSMNZy1v=0;2v3a~xU=*;Mz#ZDCF#wnMfOm!5aqxB6ud)+*?NGKQhOZdgG5e;V
+aA!w(68hT5Dn&}R{^3So9Xs1gsXny}k9G>V#fFKNy^`BQdtbJ!pUs;Jg-9T)~3)kjd{8MMG}$#;#(j$
+ZunF%AmMUXS+Z-!px@*O#p~-faGqj0uleuXOMSfv(wg+jff;U-|8>X;o)-Nl>u*Vyi@Xa_v>>dM(!iI
+hsj6yNI(awI$BhiJoncpWpz1ApRES92osh{?S?#Gpsmi&){Ri%YlX<jJQ2`#~7pa>Uj@Gc07w3LIwxK
+pA|Oa+m$`g_X~J+e5~sC#nk#?rx9w=UiQTJG6A_LYPloBjUb2e$STs_dq7DwXIF0OCg9TCeSl(9sKMl
+8FQ{Vj8K72)Gog(ZaG2*<c`x>?ivpp4EBL-ah;u#F=P!*y@p3zwnYw@3PN8(*sIKz~eKeZ(u3P9%*(;
+VIjkm&?>w|^510I-&HMK(?p}39B*!?0{x-hq9uwHPBHjWe0r6sRkRMKkF4~-pUuTN5Nv6iCjS%Tp)TM
+xFRe;t!S9K|`sO9@%gUV1n@$JSKPap2uSK49!8=2&rO&#dZCU1JAGyH2WiB#ez&pkY%0{q!J|?n#5uF
+;guCR0NfFH(i%O%;pHjq8wZj_dSbld>kghE{%6MYphl4VS`4XXpBJ|CL5b{Z5~LxtIJN9nL@@c<qt45
+kcS&1yj>k{q_OS8$6A*7W|}QEvz_tr&TOz7HCPkgB9C{N=SVJ-A7}?hG(+Yc+2)D6^o$(;7S#mW@Zne
+@SnA)BcJe3Yt_pE=5cb#?IC#vELZQX%-Y?<?(xb>{c&?(deLNH;-upG;z(uV--9^-hae#lT88+~uZqS
+5u5dP_C4~T5@;`K5%H*Z^`?L}RTm#06p`z;!~rgES~4E5JnUyZSCb%z>TjfHA?nP&LOT@Kr8w`u)%?W
+TACQB1qdE+4vXmeBcdc0Ra;O2XXGp6?-XefvHyBD`|wWQ4=lglwj_hYinpXSG>xru}NX@rK^sf0j>_m
+-eQ@1_g2gY5$b40h&6%&uMG_X`B8-ZCdG3ZAb{_iD{&vaZ~*Wc5~C>o$H3HTyfxF4;b>MPVo-|JSY4Q
+P)h>@6aWAK2mtey7Ds#jtz(o0002r2000&M003}la4%zTZE#_9FJE72ZfSI1UoLQYl~!$U+cpsXu3vF
+bP{{5aIZOJs3>nZZ?T{g`i=;(16ahnvlubk?1(LEWU_X9GQnI8t?$CU2MDp%<_wc!wIF4^^mFHkr)B+
+QE&b0v{n@=t+e5&nAN!Z|lP|^UE49bGqT<LYhgt1yID(jBYIF6%St2GdkSGLle5D@E9X$y2=R8d(@!g
+_)iY=koyMU8|q?U$)d6zr6uOv#2zrgUn=x7L{D-z>3p$)m{X`aA;s^YVpSE`?mCnUc9!w&F}=_K&bbO
+)EpX-H3n7cxmCvi+|8s>7GsJL#EaxwW(A2LDQXsjfN;GW11Ut@YbEiz=Z~eeKSt#kg2_UotHyZFVgO$
+q9n~HT=cfDKE3~*isW{y^y3qiS=f!Dh!9#7$b}2|>J7yEK*bY?cgk*28E!~fXEa-Jw?^ENL7Z4ckaF=
+P7?RYA4G%jIFS#Z7PAgIg=GMltLM%x6xb!CPu4b3>yIa(~N2f&Yh?X|ICPL;4e!GCt*#usV1AKiS(D~
+20;vFvnu4q3+_7^S8UY0lkVpOFw9`VBPAp_RsbJkbXP;2!Fk0+4q*-6I1Mn2H%o_SBfy~Y};yXTN6%+
+9Gpx-%vDcTs9EVmyc8+l<V(Gd&=)`_mi?g9MW@o+-vh&N@LgrYcKF$|de1HO4!ohTC7HaPpU4>j}l5v
+zt^lC+`rootGU~MrMNYSi~}Z@sT1FJ%=%1tV_~ZDl_f`7q#X6hz?zL{4T+FYs@CjPV?=-hQt-h%qCr6
+t&yllk^e!nQ~9x=j^L*8MPU9frP7?i@i9~ZRIm!ZeExX+4w)bm3po|VVL*1(_Yf%l4R@eC7jWcUx60C
+j7(`GqGqS8PhxfIqU>RoZKrG$|!?JW}Y5WYsH?%-bqv%>>S~ZgJ6W|qTx55RZwSkv#`U-!}-kh9_@&C
+^!@bV?Rd3v$4Io%zGnEDlH<USgIU1xOvZ-S~jNerM+8lLh}aRX8z^r|S-7ISW02>EEb2v*jX=6pI`3c
+IQnt|Lv$x};O=4b!qJis>(BuaB@DXO;1ps$ZX-ygfNN3-&*`$7k5(@%{5kX0BhmHXj-ru*!3wZTcBHg
+XizB{&wBo8~Dpl0h2H%n3bvu1{_<Gt+2G$uv9>|v~Hp~*HmDUg86MoRJdF55VYeTZKDHhXMu1Fj-XNm
+qmNaO2nuPJ%*ky2aejS0ze{>7$QeGYGN~%L^oUQ~YEEu0=d<g7;NP%nH9Ioq0#ghhAZX`EZ#&A_I5pP
+o?!@pcE08emtmBo-s0N(|nO>^hy}y}V&Hg+GE7lwoCiIGDkFco<$u(UFTVqkFT+qhCmOBW5DcDXGv~K
+p3&#?l%aG|BbINbWg>aJA<<GLHu@ZR7V+IA4H=!U~X8|uSAd5DFF!e8+ejA2fNYyDHVFOBro_|6(*ge
+?+v2tE#hYQu*$qpv+09C<3dFayT?3U{I?JS0sLI<MWOZNkLx!V_&^D!g2y(FThddxWCdBVBSo@>*=eV
+PXg@U(-6E=8Io}Bd<%EHMf*eOT!yBncy5+aJ1)Yx4?J6TuT}?&XN>D=HU%kd9BEN&SYK{sn59OdMzX>
+EvUg?q|*_&(K;<u7OowY_37L52ke$%08QIUgZ1uf65LMiA0{rQ`t;5DP%wOw4b^6pTwncu`Qi3+G9HH
+*a!&PbqQ|PHMH4Bl(b}3m^58u>h~*)3{bmA2AvT&Fhi)D9CM`GRC8ejHYlvI36q@JB{lQV<bOLW1Huu
+b=*ky3y9equxULvIoPwvP5&9(vu;jQto`9d~9D4kWv!nHmvg;p{wqh{_y$NaR>hU$o}t#LdZ#b`7hV?
+pZkW*`k07dVv_BJtLd*OFp(yG@BreH{G<P)h>@6aWAK2mtey7Ds}jrI$?=001~z000;O003}la4%zTZ
+E#_9FJx(BbYpLBW@%?GaCz-KYjfMkk>B+zCQ@7kZ5pwCANHb(F2$87n<<&nk+i*AGQ$FaA&C_TZ~;&f
+?<)WOx_cfN00WY;y?eV!P%euEW_o)1-8~JSu<<g?82s~?pRBhmnX`P!+08azCNYbW#UhLsuAjv7aN!1
+_pNo%dl?0oJcaBb2%GW7;@p-@=!hFfrJY9uZ7ACRF`Z<Dz=l5X%&zC&qY@VcS=J|IIUK(UD-%nO+FAr
+y76y{rcDi^=!DMG>dA>7Rq_Qw6rJvxCo=YVP&E|xia^Xk=~U%q+u`W2gP*~c*Uwru466s}#?jUqP0F&
+P{3jHmZJ5R-2-f(Uby@eXahgL?|+@Yjn27KB-zhO<r1fgZ4jEScwkF{g)Nj)(_|zgh7(2jl@1BJRaow
+%(-cBopJ19?2$WbIy?txP8EKk$Q2?1Eig%OuqDTBD^<C?m6Kj_7^94=)>*xntLe=W9GrO;Bm+^S^_5m
+pjiTxCm{8yGz!^J?7IpTAY_~MI!SZjc6nnRy$s<Odn=AYVvrgp13Wh4NXvk541jlem?1DA%X%}5LLa#
+cDCAxk<9#5#rZFr7K&0qtN-OGRYwm~h(C@H^G|Y1zOMx&14Q7Ar54u0HQTK2C8+OqjonLkP??<eAb;U
+k)hr{k*-0zLpr~ddI8}{CIhZk&o&BpKgBR0Ce9K(=ahxG^NS066=gSP+<C-&dpT=jbwCV=Zp_P#efe+
+OT?-}kTj;~!afaKSG7<3VpU0)Zc}!8PlB><z|j^bUYjF#Ep8uKL~YuX^nAdI(eD{!Q<^-@U@sFZ#pY`
+Iy0j^XtK=_un4?3Or&L-S^$MxcCqegE{KPr+3})=o(fV!lp(auEu~nyBuD>XIIxFLiWQ5_I1%6cM<B%
+@EQ;ubzoj^{H}-40N*bBJ0JJ22RI%U9uMJbhYfmfuljF$gYzCvy~goCvLOuoFp?8GtUK(FaLwxvV*m#
+8VJLtED1)AWNfd|4hza4)8^X@tcM0xgnFH?8JWW<?I-PIwP0FWJ7OqexK`F8%0#%%f??*@SzA{fs5YY
+&%g}R?|^(ouxoB4Llvm>#@8Vtnrp2q=*ks2939>E^wk$1=c^AsfdXYc5!e}OEx?8DyA0IG|&DvY?(`u
+nZ-@_(ii{QuR<KTjvm|Jdr_fPVYv=qTWGcJD=DFa_O?b0=$`9x=F90HlEEuDsk|!b5r%&Pj<@VkBOv7
+lIl2=tUdeOVcEES_2s13CPMu(gL6yJUwO4(iVHhB)pUZ31c=+H~fgK9@+W%Cicl%!DqlSjD@~RI<h2=
+w)j2eK7cI@FleS&4<O~~R^Uj4R5K+Jr~Z=rcUSzLM@Rh8=j)vH=?U#X%tPuJ<D3ZpZx{OpN}%P)axeC
+|c-jG^a$^W>@E)j1PxWmc!CWvY5+pm51IiO&4BaJq7n^sdbaw#uEh|s3Gvh;i7bg#~TtO0FENsEEzQ!
+$moEXSwys|@Da|~iDm%P$wdofakFL;Xf)nlz1!sgWyB7?>@UNoR8<t4p8B3a?!Vq-Gm@#2Rceqes&Wf
+{60w96^_jTFBmn;7AO`hhc7G6@j^8hIFjE>3t4zGdWs(d_#IGI`5v6XxD5;waS{fDimYAD}_FhzkV?v
+6MzNJ%b<u$b%tC)?pF266p{(>j(hBIshn4CAI;;;6HCdSOz2;COn`s_A*J748Di{l95VMD5RGIzYce4
+<8oyJ20+sU{E(OPXe;81o(FR!aaabfD}so}gFO>G2%0vjmm|A!6<I8kC?H>mj^EEWUL>|!j1>V21s4T
+QF4!1ih{2=_po4?NkZN*#eB4zClTQyx@x64bP;_C)QK6C31<%8ruclKc<I%js?)cVFIpPmUGQx4~gAG
+~AHc<|P@tJGx$CVg41g@9?j%cnJFGwsGL^~DE=fJBt@30K1iL{9MBN+5>#Z9ZBXP|}%9LfrX=-dLP6E
+f!bylNQGM>yh98)eZc6S!j#{0bzlvs3NxD#njdF54E%LaVuJ54R|3)-(ukhY|39O>M<)G&g}g1!FpR%
+Y)}<gCyonJhe~xH&cH*OkF4Ir!v((<!hLF2uuD6XkN3gh^2#Groq1AqmpG+TszdTB7lDz*59f;C~g)C
+6$X!IFJCt?vNY~rP~Y~c69IK{19VlP=-F3k6hZj*D)l?%NM+EW7Vvurvd;~wILYiMZbq5(cPuPS5w}u
+oE+B}=T4bA8M_W;yXugS*JIY)Qi$0uR4?gyW<LOOzJnjt#<;**6{oVQN&0p3jz<c~J%)ciu+NXc|6W+
+`=bG+$3Z~w8?7O*~D4=)-4ea@acpC4Yd;crXu_u{&F+0Sl$e1CA!`=NQptuf>C#8{^Pw!dSc;DyGYBy
+FmUH{@Jr?0+@KT41bO?3DhhF|9(7TK;@NkKu3S@jnFg6ZVO-6%`8~JP;X~ZFq%iU=Un%7<U)0O!1>|C
+h8UR4HBI*&`0rQIyKXYFb;FAazZu?ku|gpAXL&AIodY3q@ha0^{ODdNp#QUqM5Ot5>&i~Vw6t(VAn#Z
+GuS<v9-Yvt@)M+$I%zAg+Cq-{=vtScDk)&+X6q=-otE1wc0)zdGa5o};#KiT*H+XurvzIXCq;4xA+#&
+wQZ`S$1sWOzT(=Fh>Wec5sIV6_wl<`;dKC*Wq(}ybzIO;nyEZ|VeY%QXM&+k^?c3Fp*Ow-Yo)?9chB}
+mf-eLI+X}rx`7z=_sPn`Dc>(fbT7?yF&`p>x+OY2X<^b8Ad85UNY3>2naM(iHkknh0`r_&C7$y3kgcn
+UI|0$f7DQv10tMB7ktDc-^@l|=kP7*rkXr6d>_{=sxGl6c;bCw>ySrm#w?5jT_uNuQ^eH%!QvEDzInE
+T5sXB6#}dN(sGbz-7f0CzRar3SBX#LALst!L#EJG1Z!k8q~A&7+6;E@Rm}Xz!Cr4u3R1mPP-x=K{<qg
+QHwO$tTT|LSCjHkJoX8}v<4jlSb7G|5)j1Ob(PUHF}#$OBqnL>nupj1yKuCVp@vDx|Lw$OH$L<<cm%K
+|oSMgqYPq0>=}aXK7<4zUR8RoMH80Y<e|0rlO!JCiC15w<gv^IY%!ANzvznoXgei!Yvr6FqD7@p*)=s
+-R?r}5bguO6(x_rDJ(H$gd=G=bGAY>XzX)CN(m2`J?TVJhbSX-6lZDGNLhPBOnm=KoL*%=td)jF@mz=
+2`-_8C3>Sq3qxk`4HKu~{ajjKFOySVPcmps|iTJW!u)Wfj{<jJyaeYwYFWJ<nV=l0gjBr}0n)50eukb
+mEkZU;)0UMOSOv7~h!nZ9OGO?D37%KjWN6lDizqwZ&<9ZIn@`R+mcJDxqM-GV77nI8cqCbD(|rVbaxv
+pHb&&#*ZpOzZ`xd$wLG`*Tug+F}LJ|b(}@o{pCp4*UiF5R|L{iI$&*|qLRTK^8M@Wf83SU(VnA{<Vm}
+2!OU)Yg2GjxFgbOjDKbPdX)W=&Rkuq;q$1I(=37)27t=cwVpzSb9^Vk>QeG-h5uYunRMAnvu1=uD6T5
+d%oYun}u}<xLPefU!bt8tumN!|RteVm_(0x?|z=jf;FNuC14|9w3CH9X*A=r+&=Y#lcOEI1=aKi)+Q=
+XR7Zndk%Wa(wJVZXy#7%fN9blMUcsvePq!Q*o*ETCtT&#Bhpx7i=g#3{e42b3A&D>kbz*DtU@JDpX~b
+X!%XpRjW@vLxK%RJAyP&-5tlZCYfq2=6#|ilVo7lQvDg!$eJ{zhSvq;I3^cPWjIB=K^SnY=m<)V@xRn
+?5HADEJnFl0)wckE}@weG_hF6oLxU2u9QQ)Y)Xcw**$*9WnY)-JP_p|eTXakAfDEe8+0pP=1I0Ww^_H
+c;LEBO`j%2vt6Ju&)VB)ZR}BxMQs5cqsxL8oQ<{O@O+51<1;+j{;n@r8N@utVeq9-fnw8WlQB`~cgq~
+eMFJ~bpC0o+zw^ZMQ7_u*AGR}>IglBO}^<!<Z6-9tNE(xU+b-msM)$;ZJ)J!&@;R%J9i3v4kXElJva<
+QLsYE_FWs%ty}CBF0^04=8V-rtOWoL=<L$5>DMhrX}dG(7+3O_=hl#J_WPlxI*ti*@R%rTUTz+|Zr0)
+NMelz@%>2PfFX9lN6sttIk~CMy<6#$V1swkrbQLSUIv`<Bj{B)vaMgL=v5<BDbe+CfHQtyy>v-8uBY;
+e{fN?(2ksME(TXyarV!|%hn;K2_lL{<e?7CAJo+%lrW6|;lt0(8QC{V5f#p7bqd@f0=qLk1Hx2WfhW!
+v9Z_c$I6_?MS<MqRU8=om|E>+ZU2sC88?4?LwtGpL=8j~^mgE4_>pg72YftJa4Zh;d6NLz`mQfTGUbR
+{m#0yzow-NI09ur||o19_a_f9b^9ewes03<P%P=y$EsGrczkS}1wVGIx$Jca&;(LGI)yh3!m)hOs7*!
+cYG>5EVt{O|vToV4wy5L3`!5wwD?-2et*h6_*mBLr(0CzXA3_3^Y2c0mxUj+D%z>qp#6Eool>W~@3IG
+xgikNgeD7gSbi*LFjNz6ysj1+6M+Zkoxgd6m!5?;$CpeHbqoG$|6-fc!DYylfFROipfH0t7qGMsz*3a
+w`n^T`jrLwDKe~4We1st6E@(Fpq>FpM;H-T4-g7~1<?ji>#UV9Dv}}YK1D3Vj4zQ1F|$!1=&@58^;_m
+9;b~{(E?(-uS29zx(a>HjCrCP2Ky|YMi0{P(gajF{pLJM9ZyZ3zcE`6iKqxy=lssSt$#Z;>B}QB5vm_
+0$se4{d^}catVG317IhUhmHiY#%rK<>ED{>_tIOSlpIiOR~pgcae<}_6bXqlEbeqFKsg6QzD=)@3P6(
+ClnR?C1Xvlc)PT$(M_;}PNeY2s8^`e(UBy$R`rWsrn&Dc|`r<zA4nl_yUV)(O6bkg9h3m1_Z@7s0pac
+rNLO?fvvF+aWm60Y!!zS>QTpvRN#}+i3iutSB(XG9Qv?e5(#$I1uG0dD<bE7`Ut}l$FPUZTW|#m$x!V
+Yvz=tnj4}Q)?ibhuo8dkd|dMZUw)MDH>^{BA10fug8XcgH%>>CWtwc=d7bn!z*W%@eyxo$A~Y<lQCRA
+(yx4=SxMD|ikYhm$d%mdaHRe1Z37BzdP2i7sV-Pq7X41yd`y>oxoQTW&%P<PGq$k+#u%>qgqpf8&s*N
+7)vQMH6E!QTTLVEF(ds%3)2xNZep?EQ+ymXz4ib5`ew*;KnL)8K~zT<pN@MwM7L0PbOa+Z)td5jZFyQ
+7yzUAK-G&6pO+<83|3$M-THNIW-dEi{nZWHxxeN*b!_J%$$TC=Ov~jG{hpi$6NMt1;r7Mt4`S7wp(QZ
+XP|4cuen*#g3ZU3_ISe%yb4PsjtnGa0elv@fvv%CCMH0l%b(6fO+0jxl@<u2XR97ml6g|=Ypk6BX}E5
+Zck~uRKC-j-CZIn37Cs*xN%^D++CN^TW_wNDb@<>yo0yjrg-r?EnC=<R*^rGKT8bTg(f-WEo@5`o*A=
+Yjx@HJKycT%(Yj|x?;OS(+7G^=CL#_FVSvqsp>LjT4O5PQJ8YY5Se7Siu}+ZCX$jz~ypaXcyX1l2^Ry
+$Q6<H-{HjN!bKPTU8fwoSQ8O-03(|jOj<Yn~MWE|+u3Oc&1<AU}K7TIs6B$LlrgWtS`kM`ngI9Dr_^s
+HIx!~j<nY84#^*W*!dyl<9~E?E3TWiXx>dJF7WITP;>+s0BmK(&yw7kfD&P>^jY76r30goay+evwNQ$
+p{d?!7|?Owty$eTt-M2&r+}*UZ&2#XiT|fpeQ39)HP6K+2Yl4^X#;G?28QoO=py{zd-Z+76(sIbWm-v
+f1T78sgE(WEiJUN+5RSo?u@85rQ=oYu_J?IVKD6ISk|Cg$0~})7u9pyYaE0Oih?aEls~_*_EPwSwDe(
+v3v9bUn$A~wQO~g1*dfQjgHFZJvQrbfHMSQ&3Zvq9KM5$3*FW;1An@9+wbY?G^!fl8+>4(8)ugU2ijR
+POYf`BdOR6<bBIchdsrXv1*6Z0wQd>#aYG8u;=l~2UZp=1ZBd3(^QmYh9=@7WRJs7nz-htXEifCV@ie
+F7!S?Dq8g|~oMt=2wLiOrUy#-=uvAdtk#Bz43$3^wHbGNHPGuBcF{PLEggQb`^zUv65VS1}r^wN}fcf
+V+U_Wx+#N>xHZoPj2TEs{uO1$?S42p5CrmRGk|EXj#xyUGFeS(<kyfMXm4-HUj|nYZ?T?sUIFfWn+Od
+d5a?&ps@Nf`6URAxp;xc0Z!b^3G~Er#ZX0rCeiFi_O){eL129jQ6ujz-QU^LC+Gi4ZG8%zrq({%dl5D
+4ut#^)9ae>8?2_8YT9tb32^(Qclox@Elt0#?WL{KwD1nkK;iZ#;D#5}m+stSUhfwL%e&Wb|57~0MeJ8
+#))B#$#mx3{E<Q-m(p{`O331d=K7k$PsxvdVhH2yy=3f`wXVxgy2-AyH`qFk@O%SPwDj2OKi-|S(-$_
+t}qlI>=M!blI+6xZFA^&ww~@;19l7JHBeX8@9)9fY$Q+l;av(WH)qtA6&aI%jLrt>Xu|vZbN+kZ7JFd
+5GM(w>lt_uSfBsY!2(v5A2Efrxd{htQAQ+tx=h~H+uhek^AnbU6Ae;DX}8viyOj5O7b+sZ_V^?_UH1Q
+b+7<NfUG{100?I&-^{nX5~u;~?>0kpY$2SGb0eZujngWUJR}-bDk(!Kk)n5)<rG#%qq>4a;i#-T9Tbc
+%n_DrhK0uag0Lynqm}fKp%U>=URTkc`+{;=xx>_Z%ES$-P#R@sa&5qeednQ_e?XPg%@D#<WdZb7`Jj~
+>Zh$7WsFhq%y_@z;u%IvR8RYJ5KU6uPqShD9(5f&z@p+^}liar_DryKg$6wRSpk}4I&+tW#-2#LbJlA
+>i<SoU`?)br({-`P?q)q1IQtEN@u9qsALu`b`(f>W~!YH@89EP0eXtPnygO8y6mteIa`WN=TOrAyW-I
+)H6o6^ACZUtO>$*$(w8zfYs8$R>gHeVgnTllIBK30{A;(U{7#W}^Be+8$2V%4vH+)k7#Mx<Re<JPP!?
+D(5mgslL4;e{!@Ks46e|C6+_pUnzcLr@Wv7R@8T3OaBK@O9KQH000080P~d=N2(fUyg>i}07n1-02Tl
+M0B~t=FKusRWo&aVUtei%X>?y-E^v8EE6UGRD99|(%gs+o%_-K)%u7kFP{_<J$S*2U@C0#m6ap#(Jh`
+~I;^Px@a^m9^Y!#xFz%oiY3Q8b3rC2TiP)h>@6aWAK2mtey7Ds4KGHu}i008F!000vJ003}la4&6dWM
+yn~FKKRMWq2-dd3{hpj>0euy!REkHAt)Ej>H4F@q?(e>DE;mN3lWhd*V$&REYH{@yvMa88a>_K{D>@<
+AB)^+{}VkPv(+CfU|-nVP=)OOkQ=QYTNz&o2KbQN2HE%@HdU;+4sELH>%X&N97zuVCOXXF#d>i1*N1X
+Ch8!-wjDhO>?`te6EdX$tYy&3+3Kil8@#Jip9YSx4B$z(6>fK1QMxDiDiQEQ0LjgS@n4ZGEGw*{eg*Q
+*s+q|1EtKr}lH<qOGeXo)pH)=CKiyBqvOrD0otP8ttrhoj6i*lp=3iSDcmPmK0|XQR000O8^OY7y000
+000ssI2000008UO$QaA|NaZ*XODVRUJ4ZgVeRUukY>bYEXCaCrj&P)h>@6aWAK2mtey7Dv)CZ&hRj00
+58=000;O003}la4&CgWpZJ3X>V?GFJ^LOWqM^UaCx;?TaTkQ6n^JdSTkAyYT%)dk@C9IN~=}1ZC^TyE
+C&vW6$dl6nIu#7zxUYOOae?=?ec=LzjMF%P$+pKloqwA6;Mj}sglYNw$oD7259pg1ns_%`#mrB-KT>7
+q~cXjSZ26LGkL0*>6mGg3%;Y(ajyr6g4gc+OQ{VL;yb+7T)_!SGr(%Yh3<fRFqAi7$Z5{CQGAC8S$d4
+=8<e?J9SusQ3bd?M2091=YXK;-=}bwH=~uTGQChMSIw1~%XHv23$o8O#kpwQ8sB<7rMxg-SfymlawN=
+!9(H5bRuV*d!g}j6sOkEi%g$`FFL=Ek$*B!zwDwRXg@{I!0K5B5s=9?^G)t?l+%&FkzQ8x!@O2!10rM
+R{mwFc#X>m#qw3wyXsu`+D0H=mi%-T`XFYGLSGrf*0%W(G5m-%^|p0D@*LJAk8z*TGm>^o{p{5qjA=g
+j4RsOm;wor3~CQFFdu^y_gYNflD<mAR4f7L!U#1i2!AG?Zu<OMkf?*BS3gL&X4pg)e)0|V$cSWsEI|V
+2d3!>49l5eQFzNgH5U`c1J#rcDO?P0L?e&`TK+b@JN_0AdQQrSKJIO@2y4^e=8+NMGwk=9&?4;N2dc8
+lZbbIuF$q@=A}3)|kWV@!pB%P{WK*)t6)J||?|<&1uu$2Nm)*m1b?3sR$0o<-rd0nMhoPeSl^1K<%r>
+t3d{*)uO0SirwrMzv#!<IBtHlV6PlrnFR|8;3h>@>fP_NYXw5_X^*S@g4-ZB_*=H@^o>JWLyF=5)mX@
+NyH&X_hIz{YPH(MpR-X&f@OVXdMVi+|SnJ`UeZ8EqAd!sl?2F7g@!D|4?;q#BsY4mOh^enfUJ^S(~~C
+rp%W<w@eXD_qX1F!`$?VdBiLI;qQddmns2Q#3R-?upBc4=H#<fdp_#Y9&4-NU$*}<`Kkkd>>2uG*#|A
+rP?q};xbtpUe;|h_aDrNTStcpxadwCB#?8%b^>mD>qqRS(e`mkyK%3VU)S4o$qc|d?lDdR3umlu&p8#
+%BHV=2QY-`7G?DvZsOcab-}#)hOy=Y4U+CU?o}j<yyTv@zzxuuPG{L<G=6(EtUo7PR`sC-CIF<BW7o=
+TRp0U{tqbV;7Id9j~@7UfdoK%_=+(E|hULxFt+R4cT_B>SD4&jD8&GR|S8q2x8HP(1@Kh68VZ_SU!_H
+m1_g|j>-Zbtd78NR=vlkEI$4nY3={?#too8<X|J&5I+oS7=oIq@`bDg)at(#OVm5ihpUeYzO1R_20*U
+hWcmS-Kv{Py)JrMR43f<uPFGp9f~=lAVoy?ZothgEn7~P4`E3(Rk1p`EDC8Uky13vP3>qj}tfcX!!h0
+{=j@Fq!99KE5N&cG4h~*U>-qEc2^RPNpV1Hf3w!Z$uQjdI@#E3{C|WS@gu#{HzsPF(&H650#2vww96T
+}tjWb0lA?n6Tj}4V)Bf}P^)FCM0|XQR000O8^OY7yTxrr7fB*mh6afGL6951JaA|Naa%FKZUtei%X>?
+y-E^v8ejzJ2<APhzKo+7xG-e3+e+s<+*!GQ>Z);5XvABzZ<X7`i#^MBxy0WnmqOo$NkQCPwMq@=2ZT^
+kcFZecnx?xH@WLB6|O>8z0{)2<UMq=TD)52>5AOpXu}4a?zK2zdq#Vhd|MU+^{1#@_#0ciYINpAD_+-
+e*9&Y~~M8O9KQH000080P~d=N8H>qutp020LCQ%022TJ0B~t=FLGsZFLGsZUuJ1+WiD`ets4Dr+s5^G
+{S^mi1;th2(w_!s5C$}HHV;VbCLS`tahVf&vTmkG_KviyHvQlC-hCfGTz9C@#^T+*ckla)Iy*aCd|*H
+IT(ASHJ4trDVx*-)^7?LZ2D^)$XbvRLcYW6jmgj^Y+D3GQZe>&T9n1CeVqw-BX}<|(zsY^Y9^HYqL;T
+EOZh4#K53DX5kxBlT9hwIwvtCpc-`cy>c|s-8%ejW12e)v*Ovl8IR(PAm#aj<qHpjYZXlViNSeK&&@}
+j9bkcU4h*iZO>&THAxs<HqV`Y{lp2Vsu|YdhZ5j-F*G>bG#u6*6)zL?coU*LS>fAax@SwBkRryru`wv
+)wKZWuxy{Eb<&E0R?TyO`;@}i{QHCcCq;Q`k&|T&hroFa7M7KXbvq9u@uRV%YE0iU-2LL6$Oy4lNBDh
+NEfd!-@QA3e?0-U{PF8&nST2s`}3cY6tMsM>gw{t_4%9p^6L8H^8H7oWI^ELsRU?{n4laeS`GJD$ve8
+O*k~i<W3*W}pp=q5jyBY44kk0&*}+y`@%ny(hDV?-p9cjIH^&@KF;UaGZPdf;5uo>3NcvjR-B=b5d}h
+A~y&w0CO<p{|Nvk70NkIBDZ>J!)yr$wb+G*<Q<o3YAUJ$cGx`u2ql1Rx)d<b<oHWje0AEwO3xLa<XNB
+7@V&6c`tO+mM3Bq^Ef5(1_Mag03Bs%JB|=5NkFUgOoxGR+W*>>5ED!fP^l9<ErFY)8f(wnWE4FI^EdF
+eWzdn;IU#VVKO(bIpRGRzE3@CO(o$38x$m>o%LW7|vEisWC5Tv1jT`x>lGVWMAoM8A)8nby}C?#938N
+#4VykT^A617YfrFIHU(+nOfu8747x}!~>o|n%($@EQUlOtTj6q20MabkW(I(UD4v6-m$mpWCZMm1_&+
+%zXN@Mb_&6j@P@ZXutvW6=6@&)v1`#eBjWWWfi^W{SG@hG4-N>l8Ugr=9qB|5S}~aur0@5&>;;I92{2
+AtJC#2zzd*z55qWAe`-K0##uHZg>M!3}-Uycjtz+%)q#Uf{`qb*YvyANTY{IHja^O-i^_?ZSB=_ueY(
+z<@xa;xQnw-Um5<@`Z*Hdj*xw2-=uHbdUgzL%i&J_5M|GIJxV*W0ID<HH(5wNlMWU{SzrxurKbb#MMe
+#OfVnpd?RANg;?>s-T5?8<xIg1GM3quzDSZ79K5f*X0=AGS=a)C0yA1`7;qf?2rl(sgJTIfvu`7qp@Q
+mz0VEb#xU-Cc>=UZ8n~`PbfnhR;@6ju<{&KE-Lx2saGK|53C@&mZNoqQ^z9MvoZB|GCLDs$8<*#cMw{
+Kfph|V7=43St0@M5M-M?a4_z8b{u{a8gA*1Mg&_M111%{8qa6VJMM(flZKHNy=BO~+mg<;FX*&}n)zP
+=ct|SJOP8mAYVh%7M6eeh&lkx1HK42ywY`0@Yr?620=&`b#%y=u6Hvrj59h61W>XhD@uoK*xkxLck?2
+Xe{$$_3!bpR3ppcUEn9pcu(OZj9u&1l&`Mf`v9tFURFpWqkhf0A-lrvUW|+XM%WdeA!T7}AklOY#9sf
+VW^Nwq`+0OImgNroV&a+6!Q^Q1w4@qS(QZA&Kl;tod1)YK>qUg2z?NE^$dKQ}Z}7nTfmwY3q76ZG{=h
+CKh!ShNW%Kt5Wx2!o*Z?U<$lpRMhJk26wZX>k@2m)R*W0guZ99mnj{bC%zzG3@<zEhdPxmL{ohsM-EZ
+|MJQJRDU`jc_-qGj5x5&;c(`^FukngInS(G~n8XL<9h~<*SG#6I#VXAjtbirvUI;p);_0PogLN`m1}H
+(DmOPU+UTC`#V7M4VQI%ju$X?NuSX6g=mxXd4{ovB%8%f|lMG$;R6Y>r5IW>7%Bq-jXHenFXU|l3Lq1
+I8PV>Q(YEbu$4GAk{wG%lM;>15-oFT0c<kPm>eo~p=DvDJgS-8gB4&E%dH_qHX^wN2`dVCga&DkKdI3
+5-$)iZDjd7M*R_rC1<LUiSEg{PJs*;8e>n*DR>!A|@Z$vLHxWmggleI%wO`rK6P_Ja-#I56IC+w19_z
+4sNdE&(LOcu8r>Sc0aJM)pWv-6Q))1#(werPC$5^c7yN3YS9@UBrG!?7+D{yw5|9Fg8aj*(8aGp1{lK
+CA-s!Yzb$)9ZY=+plbct!QH3+qN$eYw`U#oX1m9N^%8JlETSsO<XQR}puNkN*+HZtj3^LDp5Cmd~*dx
+%WFd$kd2v36SqO+R3JP3cAfx1zHPvZ~Bd=7fduR-)_R3{7lsOkwJsW%UhPjT)Rvh3z`nPgNHdpB7~W7
+=L|Jw#PEj%^B&7{EY}{6QxPNuWu)pJOK+rsBXAQ3Nv#dorCRA0w90xPBJq+CZGZ4Hj%8jvV$$j(!g}C
+C8-P$PCY9@Py=hiOnk004T0!p{K=gefj2cO+M{Uinkt2|4ZPZkWC>;m>m4euW9myKuN6T*GE=We|Lyn
+H8s(7Pm=ALyr-MExsWij&*wy8`z>hZG~KhUoR*PfouM|Wbd^O6%RB4kt{jYUn-b0KN;R&F0B%RP+^7@
+1OgZ@^Qh#h13Y3Q2q-uA7;;^goaEQ8`tR2%4DRx4Qlwdq~26tnA#*^hqG~;77aE*6HcMOK#@;3;@^Q<
+nHldxkk#8dAlsCSfq-Jlu8zJQ;b_^rtDA<X~|I}v5353ab1@!ihR)YlM&`Hr)47>zzKe4zwA6;5ORn!
+TDQ+T-}6$o3c~2=}irh9tke_|LoZHQ^nE%N~3ZnxmQ*46ECF###buD(-QNh*hwJ2C`rS3}!F5!uFmJn
+sN1|S|wFefRbD{;t{y8-8EBl=qx&}Izwxu{?1`?oe%Z)nQkTi^}%}>`!!K{*I=lXYVJ5p1m>2NrYc5&
+qBm#RBbN}QCsC&3irum}d49^IycjZQP0+^-J$g<y*MYjR)c1G*dSazVGjc>-)T+3*t%j;u^(&H$kWXf
+Ka->kWz+wH;77;GZ{HJ#BZqu-ZTB$6E3y3_^4TKtaxtRwQ_e|&naNP4^uS$iY5u$Igdf1-yZSk<7Ywn
+rqxR3$UHYX%WU32aOrSHx((9QbRjbGIYx|ZIGvW{BHpg0$uJaVv|<Ykf)3lwj_POMp_g(oUUB@LrKg)
+cTI+e@@fyL^?JL0Z>~ngEm98W97OAnw`EmbDo~Lzs0Wybl@`wM7O>!cKVthtvbA=^2kzcBV&SSNa}Wm
+ieAlt+6~a5M2an$al2ppi)!=^6~8N+v!%>=_^8VGBGhi>&2{hTw`W|mR{0k3WHSZGe(H6b@~2e+at#?
+ktfV&9GjEcNsZy_5-@M@6YC;JL0f}HjS-O69ymBcvua=dXLjOcNXZfLQnG5dA_sYw4&Tho|3qMk#{(5
+965XIl;a!Agq}PqD1g|l04o^W$1B>|=oRD_F12&6114GH@grEl~2X6&?;7zY5+p)LWHzGIQ^X$70dtu
+~y7&8<WxXQTL=g&hIgL4Y+IHam&>s@~Q7JhU|ni$^+UfzuG#1$8Q#@_5PBFGbNKcXzd5Zh|@oZ0CXVh
+@oOk`Uyr!WJ1I?`-M}>H(~=qSDCCD0cV?Yeu^tkp><4^{-+iVTZOmVX!l>tTePofKMw@?QDqujYuZ@?
+J`O9>?j?c3&atXG`K$1Pbg7qKIh-G#f<7;1r^i@eX#0F7QR1#IvuhP1an09GOSEqD%?qg%ikHye$1kW
+G$&@~Ks+Qq7M)ZBX!j$fW$^PF50aXmS<6&LsX~YE{{c`-0|XQR000O8^OY7y8ZA@;-zfk9Sf~I1761S
+MaA|Naa%FKZa%FK}X>N0LVQg$JaCzlD{g>Oualh-YK<|11m?NQ&CT`z3jN@8Tsux?)ldQPu-5CbS<q<
+s)fC2DM=QRI&XJ)^@NXk{(z8Abi<^j92yR);i^R=_<zTGB8vEGflo)tw>ZM(J~lX5Y%^=@PZ|NGG+@x
+E?vZmQ-+{@D)lSI^|HVY3^nTK)B)ii~XAt*crUyf6C(V5#pvRs8d#HTG#$4I=^?a{O2JYq=?#8&>pfJ
+37S@-Qcoq*3}LF&bDP$=Uv$ktd~VzGPZg{Z$w*N)#b$<YgVl))7{<_Jsa9xzhuKS8LAIn(Hz^Zt3|#o
+`&-tl9#qZ9`mSey#UT;j3iZ+l1F-XLd&d+2-v3VYP<9Av+;14GjXp2iZC8%6T2qeI9V>>hpC;eEdj0&
+5-@VgCSM7aMx8+K8awv9vJx$=BVr&ZlvzaFNFaAYephVd(HwcoZMS)@;maH4Awox6<9wqR+YKF0_>k>
+b`==&DB@g62P@N(F7SdXo~!vRenO*HVjU6ujZi>fKQhiTI98l;(G*Q{7y+*SR!D{I!=O_MjT-W1PYeE
+s6v7sWSUzWu|Cx5dj>U%z<$<u@;;RC@==v|6&LhPMI}+STQQz(&6vd&Z_Pi?{TqU>{(@ffyF$@^;r1t
+Ewj~$ssOkM(-r3u@KD;)d*nZ8~h94EpN*kq%3xGQ`A*+D;nLFw~V*evzjK9!&^cN#jsm+eG3E$^?FvW
+ijjR7X?CW`OQ`sgeiZ~`v8(D8?;`_*_eS4t*eE;ARzQW&!M5>Gldl-iE1wOCrnd|Ub@i%wQ+5CH9bdn
+0HNhhEYFM#mEa0s!*s~w;|1gW98?vyXmp6cccjjWbTdKK&pZYy53Hb#(SI`?m3Sfx{%wKXG{m{!?j9x
+<clR(C!M>q^%8FR_<a=_Zx^e!ukrrZLvoItS^TPL{Wst(9w?8_m;$(vjdqPI}JXTTrE<tFRX@3Z`Tl3
+iV1et&g+eSUR4`Nx#6xe1l#^`Xk^wqf+9S|?i$2_K@0rY%&tZGO|YyDob=QDwYu&jjpxV0$3pi%0YsI
+Ml9hoDx$37sk7+%VCg9{Vn@(SM_WQ6P&e+|76P%2F}SJ(E>zPjYW|StX?C*{x}8Jwq-+s^8q}Sm@AUy
+97ha|tf~=L(fnlz-JPoLst4DJC|eE%tQ=7?*>$UOWb<$PodKq3IhW!!d}!SR@=u@>QBSaH(Le<oAi!M
+7CJq4ORJSab8zuk%7YDj1f$?(?6ODgFNUo5d>FI+BuLfEI&5~t=G29)HzJTeC4;>o}Fnqp##Q8IKtK6
+6tzVjiZiB-++0j@_m!&V6n9sadn>sf@a`VY5|96uG3f7pdI4L~9Nhl43Gp-@Erd}N}`op8V*tsxszUa
+5KVm%ly=w`7!YU)dE3W8R^#@t0i=DHnMO{Kx*l=@>roANEHnSj;7DI*sGrQ8jD6eVdE}pVQ>q4IpCE)
+~g{Ip?fxwJg<<d7CU0^eDNYKN37Fp0y4nChqA_P=OHQYK&nElI5a)LZZWJY*i{w}d;0T9x`R?uT`k8U
+xvyaKWzzywkl*CSMjNgKdEwC}uUk-Ezo!hhgR*}}Xd6wWRN~+tC0f1F`e5D<(2cQ8hOzA#4f>{T7iA4
+ygPj##J|M-2PcF1!)9&h(0c}&>v1GwmlPqBtfHmdGSHNYLZI8f2P1)0A%f?N+%AFopD^}J8??t%J$O}
+2<nWBBs7ie5?+LeP({uY4o9|kzs#1R7owOoS?oD91qN-*nPotVO*4N!b3s4!ar8hzwCvO?0X6D3eM)1
+PE(5be8m7%Ci2-_|t>aT8}0Ms24#{(WF1&yffRC5WxtMaHs6=_-I(`mEWCVpVJP6ZdFjNmdQDNg<b)z
+mLT)$Zx>^IX$#{787rY3e(W}z?>No`yqUmKY5AYkx0PFP3`@-9Tbv$vSmFO+p;4Fi0u+;mAiT@K)Q?V
+vGnZSm;G%x0%gzUtb|3BTt7&`@N4AUstn41q&rE;r~t{c?_qWM;s=RWunB8)gtsL^i-848DuAXy3Ml(
+=K!%m2DBfPAjx1npriNmd!EZxEv_wI#9uckj3=A4~?|R7ikR$S&{ux5JlIuv$3KSr@4lztU4V5tFOaI
+*0mJ<Hl7okdursR)MAqg<PaznLDf;4_lYyrlmA>3(UHw(xxFdbTIb84BBMlw7UKD0ss@eVoCQg!G0qe
+bG6l7>%CPQIcI4dja^pjz-1S&ClWZ>r@c*;Y535%;6g7`3V~H8xmic!XwABTWZDOb09#)Gk~=93X5A@
+85o%o89>+UO+NCnoMYkI7^btZN<Pkn6UKoB;B(NowBLfg7K|t2orW)J`^o4;@|nu)zz5ks?+4@B+#NE
+Uy`QLWp*iO9;s0jx$uZwL$Nlz*Z{qP*jLqMr+uz~SjB~`+YUMY%A2;|5|fiJ%Edr`(`;x+LIStDXldN
+X&8UDw+=@Q$8E`k%O$3YtE|%30ssZRX>n5-sRvx<XQ6&w6KtdKc)cZu9P9{<(cUr?a^{rRXsz1Xt6J-
+TV&a?4l!NT~QJXdS*td}g!Xf03BTxBfE<RlIY>#o~fv&v7BGa6>F?8JL(PM2ZcgkjocS<PqNG<gPbAu
+y+>Dq517CyP-oOx<y0r6#`vbk%KpJ3!}efx{WR3Zz*Q%+oj^ES#)b#_>%i%QPkhC7%kDrj|ISz{g2rs
+uOOSBqJnHCzLkV7y`7hcAESfP&^9g!uCl3kUbCXTEWWs0Q(E_jyWH8it7apZUe(<%=+{yzses2x2Mu5
+TllkN4l9TWbF2rM4mCuJd*Tq-Dc=-nM8UxT@lgrCi@f5T8#8$mk|ZXi3B$H2m*-tsSKO@rQWS|%FhR|
+gDL&h~?p>Sxo@IYF$i-eJs*)H3=pm$pbTGZ3k!^7`M<MYJu4BW%$EQ-hVL&J5^RoeYMaIEM;Q@aOo3G
+0NT<(p}DJVaD$su4+v`8(!xdF+bydeV{^|Rq*kAp@d1hj<13_!cD>Keor6s=I$Np}r)Y?TJpGJ8xk?`
+ZLnO`EH(Gyx<-$ClN)V%m=8kB$Q+%&PYvrJiclz_aX<JdgiqqpJAtaniRdYk!1bJq@c0A$fa`Xnn|@n
+id6+@RFf13uLPzP0piaeDL9|PCN^&E5>u1It)md)QtrTW0Lk-N3}z<>ey%Ut0!|P>!eTnLvjv`mCL|q
+Lkeq270yjo9#AN;RkN(s5~n0f5R;!R`(acI5*-7%hj+_#+hrac<x-q@&1sswaPeZ8;hmxfP4f6y7IFu
+`5o~8)^_(;}8ml*(u>l!a%fx(65OaY}397Sbu4i3<=tRl%H^A|^#r6W=YHk0l|1Gv*Zx!4OS&&9r6uE
+SzO%;_+rnV`5qC3*H-3}Q9`AdaYX^om$j~<|#zFawK<qL9r0xvGfm|XJB9=A)-97&u^&@PC)dMZkxOe
+`c#y%Jxyqky7`cw0U|pE)}x8ck-{#(UlmghF~Qi6o=p4lwz&VNq9>$FL%5t)HNK!*nxnq~!RY!e~#RO
+rlB{2fDuPSU)}}G%M`-bIoV3R{HPnxERL9QS3EVeZCPGP)?JtL9X3(FcZYAg;+4f;Ys6c?v2^<vBbw-
+gnTRk-ei{b;OD$w{8h%cFe+rLdRF%c)<4Vx@+>|=16Z-e?j}p$4`_%m2hXb^Cz~4ZqLZVx_I)#i{yZ%
+M*d`yvB*WWB7{LO#ZP1QxEq}-Cbp|Z&{5ZC1`)kC4QSP6>?6?l1Jyt?vvD%f6pYl^;jV8Cct4dh7WPS
+lbxYRbb3zCYt4PtFEI}S($yC}6lIuDZvkgbO{xo5(o1wgU88nOfEk3cUN0;C+DT%IP!7`z7go~4{2la
+JW041Hc#ACSZ2Kq79l0ITqvd@_zL-`=k9cb3lRGc4e=tGZblaJ37^n+e9BlaHM{xkdV>KuOkQ531XHU
+>E!BRBjc6VZVI!@87(*NL~$Gv4I&{5VucA{zasUsY4P_UHu}-HaE2OEq>|^XbnS$C~UVDvoP%`JjN;u
+tpj{)Zc1ijWT@KQOMFz<wkkV;QCXmvK)>wGf3{VV8Ie?c7toSXn222V3LE)1Es$uCp3prUqL9amliND
+lOIVcys*lFf9FmOXH+gt<PNvc28C{sW24gPoX;me1v7yJum}YOu&{`A-<O`X*(DmdXL1<sNJ7PoNqDT
+SwU)$6V6#52a0~!IRdMoHbtZIZe+D>}~OtrsXv@*P42RN&G1u`t_M~jildciWW1#Ojau%eZ7)4M<45A
+c}qgofRF@B(z~b;w$}&ayROx`9f^+K<yO6<tc<m_z3Z-{-)2SUDj5W4Iskb7Vp-P;9efzG2i+53L>=-
+T{-ZSTIspy2xT=pFqPA8Wgq*;8bu3@g9%$Pwn>2S)5Gw^i4xDq7*2!nu3KbKvQcnCx^(uEbNr^w<$k}
+!EzVxffbBSiiowyoj!?FD&FZ>XeFQK&?qXYg~7xF@-=!RitfSfjJZS&Z+CL?x)qLa<TE5Qal0dyRJBJ
+Cl5Am81gm3fW82mP?T+n_+MUUxtdmz%kd}%opS{zST?YR1R^Exxo$YQQ|Ei9N0cw}gy22qV#_hXpPR!
+UF`}4#MD?wDRd2EYz13mHxz>BEBeIy-r3&q*V7!)k)Dt~--4gVo+@$R%9Wy}k<cWL+Q`dCmB6$XAny6
+bV>55qf6-aL$(wt0r<J>XMO$ha<~9gOX2s%5*ft#Y7?z4`Mq!nmcg@s4n1jiS&|RdeG}ba&;?oHf`)H
+arX%P)$+V55tI347^7dndwbZ!Z5F_1us(vIBunSMCJR?<9h%;>05sEAd!TckQeOYY?vqsiI%_KC<1;r
+-Ybng*^{ZWNa8f`iF+xnyT^5v3?i}hOrexI+~0L54a>gjMkTr66sn00m!luRx=GbQoBO*L-@boSy!mr
+cq!>@$3Fa=rI3Bk>fZqJ!_XQpeaR50X1NbaJ)F_r)412poqGcc0az`s3w7uC9jWz`RI%pe`9u41Ipx~
+YZ;FH;`X=fN(I9sl7PHb3e8|A&w6_d+UeoO5tr^$B=&7A3&bi!kdV)Km8KCZc>6ke^i9jEA!Dxr0J(*
+*Q)!RK+|N(WZ51r86|R71p6HpV^y4NVh-hi5}<K(N~a;<lbMcaJtJF+juSJQk<IK~d1kqY%d>mHix(G
+<S)k)b}guGc=hAH&D>;Y&s*z_%?N0vInxfU&#d$U|k_>S$pDDaI^VYJEiW8g*SLLJ#OQ(G2<Lx@)?&!
+<>xLIeeMJunPX>swzX;RLGD|%yD@f-WZ7=FfUcrucdVYv^F7aB{OkMQ#|y96u$-qI?Vy^wss(5}7;!@
+lJLGKJXK=o3=Od3D6)tN|w^)F7zRBf@IIwhb9SoaH<bkGu4YkEEvy=J!<cOst%Q}UPGa`pProN1L!YS
+%9*lll|uYon6M5L*<3%`#e%P)WJZXUErMeI9bvXymAnk&krMk~t(_EtP~Hv$Uk2jY-#N)U41@ePSbFm
+B3004b@l87z&v3M1X2V*ikIEuNeqrxP*ffk#f1_qX?DziMC+QFUt{#Ys9jd8EX<d1CIF{1}{;@pD1iQ
+W=r7{3vt;)<5w(w!r?YpBNp16MHL8IQGbt$@oc@TkV{c?Qn$4jiZ;0&(t0AH5->3fqr>B2Jy+m-fW_)
+M%EXEyn>K7WNLrR?|b|@q?R*ESkO3|!1==+k)JR|OLEWfG$ybHU5A6g*jqfQTCOq+gk>4wMeU`;-w9J
+cfDi$w-o~UXZla8!P5MSoJuze;L^VS715I{wcS0Va+>(jo1AH-p_+5^mAIo`!oe6Z|CdKFr)=V-<MUx
+zjFxf=QPghs{)fE;@@jrs`$m0C36x&N5LC29GmE5kk&T=f32UmnL`I`T6;Q%JTwH*Jj#ELD9k{Sy%Bc
+G5R!FcZj#v>j+;A1rqSwRxKWhV)M3tPw|;|YxgyGbNro)``^B<f=Ez?7*4ym!hRt548wb6DcTt^a@^6
+yB&r#h)+h$)OD#N)5OsS3x;M+`sWbn4lbil|G|;jl^$gV3-QIydvkreOce~Hp4-ASeFdd4W5Pumf2H7
+;liS~qC7ShFNY77$hmbj3yQe-oF=b;m7(JY&mW*ezlHs$T_->Mfc1a)0ZjwwuG@mPG`gyQhJrB}1MP`
+5+Hw&js;>mJ8Yxyo=q7!;v%&*xIN3V}8}B%F!^PvrH=v2`7P!AX?jE}GG0gYlZdccjfA!h3JUku)qu3
+!Fc*yar8+_iPdc`A6u~ylDXoy{>E!&FU)oBX$z=N+<-!{PjK>1*d_(6M;!zYfiKUwQV5hbjYMMZp`i@
+gE0$bBYB+yCu*cvKC1%%O1U3*Cb$w}YWB9}lE9pCiV-hgR@8YE9vDntWLgZL%_{Yuo_s-@tzvpetIFB
+G^gQohK<4O%p-W^2LyTf=()~hVhE5S!I`D;yH{n4o<H-Z2sD>X0g#VRaj$j{T{{*e7IT3GY^`zUq5nr
+HXS68H3Oo+$DDFVUkhu>W2yI6On9m6Fgs%9#<uA}v)>5$L3MK)Q0z~RY5OG9wquh)QI+u!iY@od0?Wm
+4Hp*IoAr(niEpNGpwe3h}#_u`z(mK$PU$b(kK<!y@FYgj}WPYBwXIx1_2_NPWLuZ9DBo1l0&=42~#V#
+>|d@TC~IUg`$52%@(a=X53K{v|XX@<cn0cV&bzfGR**`Gl;=*RFB-?E1}=yZ!!nS8iNKERf;!(12o^v
+xs^`KwO`)FU3!K|RZ@&Oaf`Or9K~iLgt}j#IEEdboUYy+5uY0Merq3cI`w8`{w(pi?Os0VYD;w1r!em
+D4wW`2FnF>z98_J@FR52QA&vvopB-Bs_jlgokui0Z%M-%L@~>Wm}_jlbAie1C5leG8!RFTbg<(?($^~
+Bq?nKX8h#<iuw|H3cTc*VWdLVr&pI*{>v{WSJ!FOHR+}7iG%<K)B6|(ZauS{(Fkp_pasu{81w^LjZCC
+7_slM-_+;2RVa{cFekv?tEjvg8XlMdn0PNvx;K!rH$dNn9CZg_*h||ME-Y4_UyyKR1&Za><HM%HIwn|
+0QI;npwLF;)5m;-}@38TWG5uVXOA71AgV7owmO$WMdB~WSNel+ZU+?;$>HdtAtlIR&Q_KHUhjTH7_yi
+pMsu$=_F&%5TfY44kpm`^L>=d~H`o=_i^geLTHkION5X3g{Jqq?-DK(0ZEfv})u-i;%=n4W7JzdV02O
+->f&3J>62Bxn6e^eBfpfxN}N@lSV4a{E#y1oy~?IDHJgv5p;M+6@rZpfoD3$g0GfOaQYs2Jy}A1XL_E
+7+KwF+*8##z4|hM0W0TayoGdx)xm_qKA?3p4#kHc=IzNepxmYJ8vw{i0Ft}{g{8i&6U0`M5iglg0J&E
+iq(AMpm9sT57q`rKB9?Wz8{2}?5BA&8Ieqn8C0`*K&q}qj-1R-MERX;>D|_Hdv7LIvi1m8|!Yb%xATO
+>_{v`c_OaMIyq^pY8=f;s;Q%k!yK0o|m_V<S$kR2dLGJwBjYi>ISm5SADWE!l1+3v;xb1snS0zJH4&(
+_rkfHqT5#?5ZfxZ<n_bq!SerFL&B@FWIf?4p7onk3smpu#i(vAU6IceK4AF$8R!L>`7Ck_cfInwd-_X
+pGqjZd3~z7|-&ysMaEnq3fTQ2R*%vjeB^tq5^xbnwn?5+3G|QA{o5x6=jHxs20ovmS2o`<w5#p)+Rk*
+;+1$rSA+!4z1%<<!Zun$C8q5PnR^ip`equ&1I2atIWY0RJo&3uICDJIS~|Lf?WKTjN*SA)vBS~~J|Sg
+O5&SqO{3wx?6bi?XVinMkU{ArbAV4vsi*b!xWldFndqGLobWj;{xYB7rO%=jrMJ83~2XJ~c_F5rFno(
+fmu-tmo`c^Hfm*`3<NoiGzBCNt^vQL8>u6zm{1-4Gn(J3b8ponr>-aCsUX(qISMQ^fCb7^EQk`bvE7Q
+<$RU|t4NV#0N!1mv2HPPw7>BY~ij?rP3GNtx?%IZFI`M2`r!3DsUldAVA}Xi6ZEzFKaUR1wsS_-j6X7
+~MQY_#hQ(Ih(XMG64ZR%exY_B83~`QU|8B@%WoL%2580XiJvTY~f?-3?Xdu3cn2@+s_K3T+DQGq9ntI
+I<v@4ehZL>Z=Fi*5*C0+j5nKMBu>p{Vgbz1Zr3-^>xbF4Y+xm=9?xbyTf)+myP*qijcd&jgu>H50)>%
+{C^qroYGkp1g{?E=#_HpvN-MT#(M%MY2P`bkc}+yTH)^Z}uy`cja4Z8p9S^#Teh<>Vi%g5S<Dg)&h>>
++tx7K61K59v5Le~N2)>!{16n&_4a-O6Z2K=*XEmJFgR?wQgi#aZNk%XLV3;{_;?O!N`mf?+)Z{SG=qQ
+Hc-dP>q53X2vQm2oM<1j$%BJ{B@ul?l(pE_bV3#g>G??U+nSL5!ZH%U!I)vYXUu02-aO>B@erkTp2_s
+i>TvNC`)qlYA{ZlqGtfsvX;R?0E~#tRhfnpLQ9CaRyOM8wi%H93G^p%qh<UD0$wiak;_FC5rWv^IpbG
+3Mb`IV@5K-+g<4$TdZ=H8*w+YC;4Z~r%z|y!?XM|1en|n&gb6xe%vPQAO9d!8ABAAW|j4t6m+}p<<sk
+U_0H!K@(<yGcAy;>{4Spq8=Hy7a%e0)NG8~&%<UscqxoYzhU<>qqaa5ja%o0<PuwY>Z54xLJedrd?om
+7(lJzE>YKVs>vEGqDP!`M98w$0dq<j;Z3N7dw%4|^c#KC*WlJEk=SCaeA2Av;@6SrOz%CZJqK9uuK3T
+Z~>IPTUmdtnB%CwmC;h^6l!bq_%wLy<1D|0|+&aAK3<M=2usL1-d2k@3KOio(~)@pId7K?*L{6$8>$d
+=LNmM(@{XfWSR%)81Q}8l?-FC10Uk;v#C|9^<GxrA!yb9>-(x2O09OjVEELQ<UNg9WNk17CZ-^F5+Wa
+oG*}~o2n^ehGKP%{+KsJ#HIuvyud=78W%M_LX@>DJEtx12~E1~S>zDpS%--)o56U*l_PNp^P`#|fo@T
+bsXyJA)v2vnaoil60ihwCf?u}6Q`D<lSMqn|nteqtJnyEiMBIAa`6Msz;>bLizTnb(pjZ32B&D997IA
+FCiI}AhB4TPbXUTVEy(1l4Kmv-}4oAT-aC<Dh;&TMe;uEJ^A4q_9@j0Hj0tbAI5$>hnSVMbvlRdp40^
+b{f6j$;77X8S#WmBy&Z0P?%hi2l)79IzU_a<flmN87GcVKU-cc;E_&QK(ZFKkCiAnw%w7|1tde9a}yy
+%{;gmdfJx_jFXflNNUhKQx;<5ki@1Vvag`@iag+e#((@i2w6(ayXx6lp??Do^(40oVh&RiOdrV#Dg9u
+U0I}$74hBa*)TgB@}H_MwS6G|*_1nVkjDcS4EE7UNz69)C1)_fc~Rp-2O9ySdq}L?t^)Ghw;Pd{6_dS
+Dp*+c|$qnc+FtF@?QlU#YTTJMhtRxMj__QJytbReow89w8d;DkN?u*sh=qup23WPLzGi2Y>fDg&s7NG
+k8ASe1HeU2VPq&`YA6CZ=mCtDR)X%#%sr$wMoE8x)b%)GvbY!BGLK!Pka{a1iSORE#PErJ3$^&vichs
+&!01Pc2-H#1(CElo8dERwh9-bNp+LO`0hWibDbs(V?vmoeaDH$!x&>d+l?FTeis+ZWIM-UByk?*Z&*z
+nWV-!5^7@AXVxAZZcsvhQK+gz$0e`HpB^lsTmflj}rKalmx!)qYxqrch6Y@`Bjqhp<}b6lf({>1@2p-
+?Lmgc5c5P$v8-9y6fkXtuuApPE)j`KhN65@4Dxs-V;+fRqgX;#CH==fP`UK76zDGnz)wCreepc`&2N&
+YpP?r6r2Xe#|2kHmA6LWVB^=n)3N$ovtIiUc0nt$rUY|Ug@3}8AF8|6bJHZ0Rv8M3!#lxTR**AYW0s6
+iP<XAHbhv#nMG6fZ|X}%XMlXmCyU3o_lh6lL55;ENWb76ybiiel@@`jtbpO=F^o59AeVef=Sch-|Zk4
+_9A0B)cZ0C>1pW;L#0=wHSq43`)@ElNWBPcN=RrPOgiqkmI-Rj6D%TSKHKi(?_uLC0iFb))Km6o=mJs
++n1RpWrC~5TjOovxA|e^-d<C7I8P~W|_<DDIGAO8%_d)2%iiyBTRKv<$<>Lh9>G{6QKn1IxbgR1bM<!
+D>671@TM#A@F9NXY_+KBYJA9KNU514)gP^UEvJ7XkI;z!1cKf}SAlu^t3ZiiDtbYuQ^<KmC(PQljGLk
+(&%(DQvnO=83Rje$zBPbgZFSZka~SZYg|A`GMJ6zj9bPBnqYcD>@<-8#Ovy@~KCeciuKyV~?O;W{sCu
+mkl@SNG(U>I9+0j5t94OB_aRZF0F^AIXk%*(tc*=ukJsy(XV5T0nsSk6M&{T-O(V~^5b{Sxemxk)%&~
+q4qQ%Haa&~+$@iqpk8Kep#A8%&boC09{kC^4Fm0xyu*lZQnFGJEn5MQqcvKu=Y$ih$=tBly|D1Y*kmX
+7CfwTu001W!GUWEGDLz94zIl+g?_(sXFEVxjf%JKkGjac#o~~<sW;rJOD3#+FYMB7pKf86BW=LOehq`
+1d?ThZ2S{fmvR!ZD>F?Q4kd(-oKf%i6`uh)-02b~j5!#ylll!I1C4K)v6iq_%2}`vlA9^yy@N*pgw-Y
+ryJ?JqcHU5ZSgjg_H^#^X;Gt-*?g95WOah3e7;%Ob=2l%1Po{bPUhRef-$npway@T=elf4a5l%(>?3D
+^MCg!lLcPq*bgogEFfJMe>Zp1eFEqT^U5~dRSQToC(n)CcXiZ6T)X8Q3_>PcF`I+*e2s62f~WB{537p
+Y?}a~5{!?jS20@mn53CrxAO$||2ngs!czL^>MA(ZY{hr!oF=$`4=C1vSZRR@V3B!!X-5Xb=(7!aaq%k
+QihndJI64q6bggy=y34Fv=EqfE0y_Kv-sMw6;wIPZ|Q#XLx4;@S-eWrWAvfe2#8iKnIs~A6K~;Vs&-f
+{Kj+lTih2VYVqVSynp<pv*R4cM~8>F(@udQVJ97ma-_sZO(P<QvKBOCgacNM`m}WN<6tCnT-SH9r&&{
+@b4y37FJNcC{RjjfeNoz1Dq|%%gLPgzjiO4WqZ12n&vj{v<wc8x4+`QF%ZwpHc4g{45n78$yXm&){k2
+3&PKxhc&?Qmi9Qk4oSz4bcAul?a{kYY?1z{QjFqRBnASoZiCBdUB_OMnD{`uUrgoI5<a_CVBS3Z;op1
+d6Lrn&v@N{jkcucy``IVSiU$&%~9L8p!hZW@VjkIbk8nGDy_X%&{T6y#~%u)-X9U$4weAF#?$?DR7De
+Dr3ieIR>x!+tyVjM)m5VOkS;7%2`y$pOM(gV$F#qvOtVt^q2|+m0)WyL9u;Q-G{>A%%GrJk?vfDfNT*
+w^d}4qqC@Z)rHZ{1zyX&H9Cwr<Y1X$_H8=WqQ8h}e`fUC&UWy7*07#Jc87kcYDmix4<*wHy(w^MMNT{
+28_q^9qCU^V>GC6Y@>r?D>5Y>~&zLgTZ0L4IkpQQnLQRYygutx}JP_Hyo;dR-%r^Fs#HpCp2Rb}MCPa
+P6hx{1i{%e~9*a#fV3ay1b`KjWjdH1}KD}aillY?>0Tnh6s1D{xL(K7x#25)Y2v&;vu5Gv>Tp^eVCCG
+8$XE;n#Zx$e33LpI&a#PEKdNk#{Ubf*738`c4h%oQu{@ow0}(_a%x&N8;GEoS`vrlXByCAAYkhlhtrp
+@U!cn%5H3zLr?lwD7gaCqFlTV+7nMFtp)w9mt0hrs{x!O)M#HU;~0*v2IAC5S_I=nfljUAD<=Ix|d*|
+;;lH0dz{+eeqN%2V&%rR4$AWj1y#BaU?b{)v%S3ior*MY7RY&Nre6!3nNC13?%S@ffEU#V?!!5B?S>s
+y<JqwNuDMBq(fDQ}Byf|QxCFzl_uQ!Sx*cDljpc|Q24RVez+VY0*<fg5(by<XrXT3OPh?^|R%F^f)3l
+7R8yLmQ7HADTE_U4NYDAq1(Zk#xWI$oZW72V>YxIVCd}J@Nhw3Dz)74LQ+x&MZwHs*%<jFWh5v58nPi
+#fbz$sugMpcrHw&b4lC9r^o-*<K<(yZ;fy4oJ39hqojJaS4#KF4Gq`Ive2+}?pSQ^JkB?}AA>N5q+s%
+(^108J_--+gIGdt-ZdE^W3ZCGZiDagKnNy0Ii}p?{j}S*@ZUkmhnPk+4BYMfKi@l`?Ye3%xw>eT}s_-
+ZOMS<%}xqglA8pz7;&XqoDGtKHzp#%x0~6_<nBf;hfoT%6vPB>!m%PbC>6gQwI;2Per^DH*1_YLn2h5
+ac`!qQ)J7CL6G-8>Rq3=M`=GPuAppAvlglU9F1f~WW>Z#lSkAl~D|xQEX_t}XpUWRG&6Fg)Aj}qBJB;
+h<1Lh7F6Kzh=J1j%Dvx{;`mVh91H%*@8&j4BDQZNNsNs0K#+XR>8vFu7<;8Hz5>qmL#cw*74Prs+EbJ
+-Uc*u;O$u2%UkC;vze3$Y7VB4S5V<f-UlyIzZq&1BDV1N(R>0M*QI`gYf4A_tVZ)ci+@6t2U;3%W41*
+!6XW8JsTo+Q0+JWXcpL=#lpt*`-b?yA;g3d;wHTC*XyO#QUK%R{VkhFK5rL5mJ_p<qf5TUG!yx+3)Z-
+ywk5ziv`kE2OPpbaayAtQoMS4&2z27etDZlsy(~jiw9Sa5me%l30bOuz&7m)ct=C2n0anZIvj-dDq)%
+WamRYRs=7CmFq8d>VM=_NdsCi2r??SiK1$xd{W`ZA@7WFe&^hS~PcE;%IQ_+S_Qjju;ClG&<@djsU!P
+Cr_<{}|$u}nxO^PH_b8wW^9wV=a?kvJHb!I>sC45+#hYFA^gcqx7{5tq##u2Cq@|4cI>$T!{(DBHe(c
+fgbK=OiQNMs%-c<DKBRo+qvvxsO!9VuYeWaA};2Vmwi?1#&!;6El|aBBup2EPGB88Q@w(B=mP%3VDcc
+gw-tN@2F>;MzbA1UZ@3-Q&$@q(Yd%%$r{=Np*%mfDnuql$LccU&uBD-O{HomxwNyXEWGIH#*!k=a{nl
+;_>4}U2gFIzw6oEDe&I;%NEqd{&G?Nbo~oclb;JeeZgNUSotOTR|@LawIW#h!IRUOXyXf)hF}?vPLo%
+7OFVgSW%_y@d_{s4uSM6@3m%ATjKAdzWy{wF5hCZX=acN}^78wu>+AEYYhte|MIBPPMhLtvnzm3q+uF
+R->b3m#Y$Di@DH^(fFvp*4&vwCBCH+*6jtS2)zF1YK#tnY1P=EXdgwORozUmfMd3YI<zb6>7Z?vSH?0
+HBWw|W>+yiP$F7Pkn4Vo(nJnk&-HF_nj0+?3LTRDMQTa-ygvuM%v(VjnY*t7gD@Ew5)bV}B_#<V9*v2
+QMB}bbU-BP5V|4d&a!aAAN@pK50>qS!qN0oQr}IFZc*2r021g|MJ)4Q>+>ca_Wpoy!^{wIjV7IknBPx
+P2@KwScpY&A~XqS!v&t&av(Ug&6x|7JwF@hjF(f~SnKlPu5n2Wn66Xg>B01zX2%TP9%|J1j9$BQ#Jri
+zGl06I@8E^qO<0Usg~21_iZ*VSMy<70DP`~qxa3b?y`zo=az-rJO~tL}#6ywqPeITa;or>-`~&qDKlS
+IKlVTp*mXBCYBB>?kadys8lnUkD=Mvk9=~&`XjVtoF7h5(Am@)>Y7cbqO&17HZXX0jRoN%*)Ufdvxk>
+Z*wnIBZ1M>vNKl}8`QT&ChO5Ao-4?S~<z$P<nHn!64S%FKvwh|RDyB7qLeWT$-)F+tQEsMvG3MNJ7s$
+UWiR#lha#gR&7zJw;r&62*mBV7B}N?YYMp<klrdglW1lC6HWYo_~Rh=OI1BAF=`8OICgS>as^H_Brw*
+V-lf+r&#(N_anN7my+`bmK6y~Z@9+il45|J+WdfzT6s$lL)TK};;EA6A%)eHQB@*F$}r4%uN43O8&FF
+F1QY-O00;o{l@>=`7|$3%8vp<_YXATe0001RX>c!cWpOWZWpQ6~WpplZdCfg*ljAmW-}hG_Gc}QPrQy
+b@e41o@-do#sTe)*w=S|jCds93}ge2ZjB#$6R)?M9yPj>?#@uW0%QVGj0k4T`=Xf*l_c-J*Ymgl>nA3
+BletUR_&*E7D6O*Ql)SHIu9*<rhFQ&nQymrX5A!$R!%Q1wN*?ceBEyP@9pO;btpqU=TIyr`P}zO47=X
+CpQIwrsQfMbt&p8Tjq<KJSEVhHfilvQ;PkG6>n*aEj(sR}C*rljYD=dEewM?+<G>)GgmW=kRVdbrC;p
+MeEFN`Jt9QuPTmjzv;TBTeIK5K>sB=32)w2o#4e;HxkAA4V!tsE@j`9n*m{c*Yw{uLtU)d_nmktn?e3
+&D68V@vZGNfq+n|JZ(gcF3}$-FKfuJxP8>zuPhDVyYMKJ!RFtx<_*u^$e)Rw_A0*DB7i%VJiR)jKFu}
+U%j=U=WDya3!gPZBaGw{p0fWdQkJ2iaSj>E&BZ{EDYKB8lfX63WJ=zmA=mU&+DBXHgdnr$mCC9~!vm(
+AwSz;UaGH)<MU2h3X5WuNCuDXJZOFyzIbS^g}}62|%b<h@4*h4+8^`veG^`an7X`=GX9P0e3;1w5AM@
+Oc{LGjw}mJ77-u1IS|md$MP-ZR(!Gs_L1nahn`N$oSTUVgr$)#|9$|_`X({T98VbE39kp0(x-Rb$Xxi
+wuSArguho2GO7tas$pzo_8~GU(vEQK{+(#Ma9tJMaZML@@`2sS1-oNQY`c73tr`7+T|PfQtyYTe<1}f
+BS5H0;3x~!2b|?S7LZFK^TV#K3%6h5BoLCaK?z;_aUry3*;z|U1b8}<9N!AQK>kopR4nkDyMFQ39d-b
+CQR5@(2U0K0=U?-H@OilkIbm%OUXcoLa_XpZ&S_W+@!Jt3m_`Rx{lUcxfb`tD3$ewNBD>myJASp=QV_
+6GA$AZpqzZr@wVIb(d<@KTm@!oYXt?JAUye=x$6Y(F~ea8!ICdDe78FVjd(eWN$unI`VnjPzg`o1rZ0
+uVx`67{wOL9T~CmHh!Jrl*Lrg=sTh6eZG`1JOWC^F7c@V|r?asxaLT{Dp=U00zX%2G^Fx+MHR@AT(e{
+5JS9!jR*=2Fe}VhBMwOF3TRb>kbe;{c@W3g5+#{ri=si&K$p%zbEshvM;IuWodu+Idf_Fl+5(6G^tor
+XMij_$I+WW3HZvd!A<GV1PXtCT80U|q0;cYV)){F%E(5P*W7fg|_4%OeTr^|!A8BuZOiO?WAx}u)AOO
+T=QzJJ<mv}Y9CR^xBOk%4<rZyE5KedgdwL6US;F|_k`Y3u{@SbO8+a;6&3WdL*uGiD@m4y2hB#ZlEf{
+J=lL)@XB>nunx{2!Dw;c8jP!l4=InPiCJD0z4(1p~GJ;n-G0eNm9AC-@nTw3-G+QIP|+^AQ7g<Znn_1
+BH06w=ZnHUK5n5Wh$k;);|N8H;BoRKZ~4e@utv;5*NUK3oRPhX|TZ93wAOy9f^{LENYry_kadeyar<Z
+8?`f_kAQc?C@_xVdLS^_f66?8$w}C%a#xBXhmGEJ1dnTx3rze>jW~&}d1xA!2c;Jt*gKGcHE<^|rzi3
+!p2PowMs=x>nQ;Pr_d)*fb%bivOjIEt#2?5|#NKD6%xRX9Q9j}O#3=H5w2a0x06j9;hS=Qh(4(LdSUM
+(rhE0i&%)Xb)&<yeBJ}ua4_4%KkuG}ZL?As4N{{EYXL*KXZ;qBW!tp2dcw$1Tvdv5t#kne9xDM8V{{o
+;!+{(0=z!u%~dC_4R2IuPGKMkfUuR-wHLFlO?AY7P|-NX>oKY@h?FMNv3r9rn)-T;@hJE*GN=KIh0XG
+V6cp7c1A)Q)p%YFZY|fi(#ff$eJ;*AsE7mk(~Z3pCJ&~mCF~S8EBeZ?Dz0{*DP1FO=0oC>&!Ll9C^Pz
+2nlW@Ic`gf(T(%=3t2tNx0al`a@G#>S!U`FqQB$|t-$~SA5uw4*=^My#t&A%v?cfWEm#X8;;xo^@cx9
+EfGBiB(DF!k^9zh2#l79*cTGJ5$2JH|XP7xGNGJ3dNmRf&B?mP}57l&*V06LSs9{-qhRxi9IsN#t8=`
+1vqtA3F;hEbcNliet+;ZR=YU1kO_r`#6S4gW9d>+8m=f}u^4-5n==xzzlbajum71*TN=}<A{7pScd``
+*Fn)RlcNz`AWR_F=~mBeajsWN1zw$vU$~V}u5pfSCjD2XsYn3TPqP?csNe_9AHO8br>s5G~qbO1JWEd
+jK1m{1XQph*H%g^9higI@41hVU82GC+mz7OdLI02|hrvCXI=&3Q-BN@{dPQ$uL8dn>(1l14)H{Fc1-6
+Fk1Hq^2^4lF?zYyR=HFrYU3gp1Z6UtYi!A_85uZ~=H$v}WuJXKw?bKE0ejwfl3~S;rNZb7WAqj1exI5
+S$+91?;RCw~0WCBliyKdHyTWypETGKBzuhKU;-I!ue_3kn#~*)uaQO_^E8H9m%>1t6EkHc-x-Yj%??K
+xmKU?sCNnq!1Ko1p2%#&E)HYk+`11wCA;pBObJO-jyq6=Ks7O>G>2L=SPgFn=QO9YnXVqE)0=FZi@`O
+%q~+t7H<k4EykpM7x)?fh462TyoHd+$x46DMcy(T%LxX2<V&OWPbAQ(l!E>~RoqRc=QL0)RQSq{z(Q{
+_f4X#-2s)4etl#B1&Q+KrCFK8Ex2tAt`qQ@W#kJL_2=~r=?9~zabzhQE_i8!E5la!xl;b!Uy$-H(y&-
+;ac0j-KLZ3gm%^sSOoAQ8HG^q@6`v^P&C?`lNKD0MufPZN0kT-#=kwXn@Gsxs2^>~6j52ODhhY)qZcz
+?hHX4!Eea%j=|vwc;XF2{UoI<4MMk~g$c60wOD8yyH@n>W><2krhO|-~%gXiB^Bl~@Fl34Bggk^Jqq@
+NVmUXx#GaXZQjb^<LsN6944{Hbr<BXo10l+KjdW_LNR{XdrIKF(K9=Q3ksyT^nxe6@sg^6)~2Go~2yR
+fp+MBq=W%YoTf&+?bz4|^1Fh9Rdw?WIY0lV2|W?M~`A#J{q;!j6zE<qD(AUoL{hxMM0VlILWW=Xqw57
+Hd|C8poFuiB<@%EZU)q<&2>s{S)i0(LzIMimn3!jaZxSio#)LE*W<sx}fol%Mn<h9(ZNc;9n^0{m?K4
+lxiVRoMHsYAqBag+0-9Q7Z7r2v28gjPS}q%JHk#WTM$252RQLY*T@kdd{q2)j45IlifC@h7HlvkbV6G
+Q_9JLe6~LMNP6r<OplSv!uttxHT$Y83V%wSz1#M>0g~F5WOjTZwLY8XM-!;7sw~+-}9@`3q1HGY8TZw
+CeGT#km3@tWvsIbPY00+yj-iZ$FGyrIsp<^g&ih(<76v!pFyvB8av0+#pQ~vw}Lc<x{U))lXOyP*q7G
+qTuCi`r07J{(Du4doaAg1nQmmPo^|E$8hu=<KxQJa@o=F#Tk%rP&t#IwJX(+;kC_Y7Y9(u-lJlwx+`T
+@9AY^5z4Y4QRt)l1TSqcO6sldjiGI&Co@RVC%UN^-I|`HO}^C9bh|~;B`?a1Xdm;Rj%-9+N5@$(ZsL}
+j%Y*Q+z4%uXgA07@QjgY2iul#jSGL(NU}I{y=6mJF>;k)%i272dfQN#7pA)F_?}YNcF&{*Y0{DwigEh
+d96dI0qoDZo-T5I{Pw^9Icwi$E40_r~BU^|gYVOFYa)AB2IFrM65iAN3rw!0?=}Bti8qzj%`r`|rdXy
+LKhG06>7|sG=xC&8HPG(Tx5IURqEWU0A9Yr4(at|(5jVhBZjlcxOdneb#4v%-;DpaNTfI4*)ppCNXyI
+T3gK_;`?h10o3iD}~(v<M87&p6hTE0iK|bxihz@|JHfT}FCow0Vhk^4E-glkKxL%R#<p?EcqLo6fRq*
+grf*_Du>|<86JmK_Kpq5H%0zR?x#}oJIEnKH+{yklNy+II6e6reri2-Gmx^0h--#1*qy~sC&_At0D*=
+O^~pA?Zjo`(|QVbSQXJAmr?&<q6c+za?+q|>boX=Li*GYCIL;SFqIt&n!B`iSA0qdO2Zr}WSJP}9Z7%
+_T1qzIiMp3aXO9??@s_>kbyH*H1;v9l&h$#&lYX~ni61w7``n3L1Qd8?+h?#0fL%kZY>W2Mp@YBqeG@
+hSW!Z8Gk?ny99j?&x`#kk|x>oflDo^G=re;F(zwH_%pu%LD4boEy{@Q8cb})PYArO(eYZ&;k;HJb%L_
+Me(VPM_&q7-M++phq%R&@P2HRV*nRHc)T*LjXwE+shIQFN3aPMHuXq)R!59ejK#R_lfQB85|4D5H@X8
+a|o$sReC~cG)s$7AU!?6A(v6I$XC(_{62VwwbqGNukx{;yt-pC^a#yHO4U+3??X<zX*5AtL%mwZv@QC
+Q%o*07F*oOMcg{Sa|#$ElnFe2i$`&ReR=SIvJsQZm#|E(1$=Q6hwx|3izh2kLyK+f+T{&sdQAi6i}xl
+NaM#_yDmet5s6W(NaB8~q0b{LAy(_z;O3iuHzbFs_qF>VXSZ!I>L*UwG6K)Px_77jwQ%vqy_W9J;4ws
+#H^_;tzU_EbDR_9d_y~jAd{-O&1FgXU=2F(x(lm#&z-THz3R@LcohPfZtJkQt<Dxua>El$!hwqi%9`g
+P2i@-CWtI2EB%Z5kyAf&wvm!rT|dDz=!>iAMx(DU?KtV8tjgSW*}M(v$^jn;u}4U_>2<p7-MZbO6Wmo
+-jc}fo+DpW$S=^prMq&_p%N8%~*$?Z4baDuEeTLGCry}R%@$4lp|MeY{0{3<+Hb4fTqKhF4G<{38E3#
+>m9A`F%Rs4u}jGQ9y*|6dbDT89$xL)Sf)Bt{Wkvnz3PCQA0<1mk;;x^?d2%l!JwLI5zvEu3v^IvSe5u
+r@d%ZYxhc9pH(yFwOx5knM0-Eq8QsJV%<+^1=<E*pRL6*I@{NSE#RD0|xqL3$DJ2yxTa1(aJ=xSj-I$
+d=N6;;X;l^$s(dokUsJ6(<G4hLBZp313va;mff^VT=`l(Ce;E{QItOt2kh03^lx8&@R6jOs#;)TNtG=
+j8ru|?WDOwO~dcDqd3qCGQ0oT?Eht?Ly3Vb07-!mHryI6G&Pk!%6-=-Cu+$2ovYBl>LyUey#)RxUnmZ
+iN8Gf>k*e7A|&$u25y$q3cxW+^W?kN0(26*5EAtpsYoRDa4E-0*_D(T_tHrdXkf{55`h$tZo7$ik4c9
+>&EN5X6Uizm&$J?hO5u4#Df~;?mU)_8rQKM^jfJo;J%oIS&vbDOcKK4p&~#+Vh5qjMZymjzH4L)8(jg
+^*l9|?wdAlVz9xcOdTlDe>_Ie{+F~Tc>2StTPh1+trm*#QPSo5L8ET6dJ_dXYA`{mdJ|o|#vC1@DuF2
+G9EqA%L$yc`Q>C)9+o`-;&UC-%}EyjjyiFuRzvA%vn)eD^&L8rfl@_i%dM9-Arjz<q}*|#uZDly$~>1
+Ah-B}_%VVTlNYz7xV;dRljD)sc~OnH{@bGucF7lrWl7Y9Md;nZQLN1S+7jj8;AbYLqHuBQ6p?(NM2>b
+TuRurrJJ&FOBUL2;CxlbQpN%gjgC2k4J{~zHjae*mv<DDn7xY2X3H;SnLE^8Y7D9*_FDmAL+=NY)Gp7
+W#IA<Ws(VkK(_&;5bj_tMaiWov`C?x;b{&+C+=bObaSd!BMuw$o#~#R4riVH0c<HprMww5HN)tvc~k+
+W|5mf2OYu}pQ4Kme2k3k3Dm;G%P)G}!53mCgAf_%VX=itfc5(KKn;@D?7nsLC5xAU@ZL7p4!^an&dKx
+k(Z}E}+aTbf2K~tfhRQp(x#dyQhP8&am@e<vbfK0}lZQVJx@ywYMP)^1~jyY=5*uODtXiiZXrz_=V(c
+zz9Pk7#z0G1<0I%xxd3MT2CRP4xzT}j_;@T?+Rnqv;&4;kBIZ47^ev#c60<<N407UsCH6)wNUlY2+Bb
+jI``9WG*SR5nzuR%kqw{CZW2=nC5D)<i!eI%mcY&v)RX18P)`zW5KKkxD@?0)CibwOiMwg;gMHSD^Jt
+g<9~^7|2ogVeNx!P~e(VS7a2U&~t^QDv+PuLplQ)#G$b4HdekE$^}=eDGw>?09~=aM}O#zkI>bIm^E)
+3Gc9CQQF$T(8bw?482mv*1~tjz>Uwd3%*u~pcU>}NIs;(63e6FlGvgRpB4o_e=>xWFn>z8Dhq+KhGPU
+SW!kl%wR-MopC<zOJr6gb^q2Tn-wwQ<N4j!3x>OVGmMmGS&`a4_u`$@tPd7Nl8ostiM#HkQ~em@GyxY
+!sWkOrMj+xs!lTXuWlVYhw<<z=>LWwI$8qpPJ^q{Hh*xK@frR$51FFqt$R{#x2~=8?pN3qs8+H$yHbs
+Wl~gLLkmjN8q2!=O5=mWbSF7Xr-r*Rh(<Q>FHIvlvauzF}xrjJqk<*q+Y-0Z`oI0;S9;bIZM3CU_4E1
+4scMZR44*&r-Ptnt>V{fb*6JX(u^77t#UCU-J9)p=%n&6sK9XBmEZ&M9^;bAYSIiJ_jBo(!<3!+1spp
+bm<U&&3J|yo%VA}S3Nzqc5DVCu^-1U8@fZ%<qotNH<6Otw>Ke@?fS_xak;Eiyw7az?4Fw()<>Ppna`E
+TO7RHJgG8j+F+0hXUL+ws1sK_x{FL66sergfuU_Sek&#5CN{}kIeUcxk~Moc>@8BPwdW(zvVowesh%E
+wV2Wc1FK^zb)$jzq=n$W+FlN9k6@NlJdQVxZ;b=^DiwjL+X2vieNQmUnWMsdz8^GXn<0iC|-z0TC)q$
+ys|Hjs!*##SE;B4oSgy8=-PmvPCvDC8AVxD&=sbKxIFN71pq*6I`m4g?(Rv4Ps7pVP^`5!bJHVZ0#%!
+DzKP*&1xSijzYPpVA`e~a9h#o*QId-=c7co?2`%M9!gz04Akm%EiGtW2deFb#x~*=#=FX_Eyp5lEOv@
+O+7G-dM6n`7%x=cw-nzkRXHFM0P!Nf>_^fMwYa7%tv_B#UKpLyF{jl3q31n3o-+!45XnsI{gw^6?<f0
+OLI#d;lcp-Kw2o;*Pw^q$LH$_v>9?8S&Uf9*m=7~E}xO_p=WE|2f9|@FtB24rNOS4Y=EX@uXg{egCE!
+ELS+Ma-ZJXnUAZq+*<d(VUt<2M$JHxpcl@E~_xm9lf^Nj#?_71N=-KVb-COexYU6$_aG1S}3Lg+4Xi{
+*Y=ITaKC2Nw1(!dX{*c|0;5*nS4?tQ5`4@1Jt0nfwpQUg6ze7@bA{USta$nl2h<WJ3-z|oYG4|ohF1L
+3yn83-UU$J8RY9HkuJ8JZi-7Q_{f6KsGIa50{5(b1k-d^b1>dMBN*{Xl?d<jb$QuuKYsk4j2App1RrP
+p)v;_WiG)>8W<uPyWWST+S)p`NW6|t%)&Sk%D@mSi2a}3_j4S<D6}SCQP8nBXoHvDI|ML&OQ<t43`vn
+#bPBxkqyOIL`f1O`E*GXke*<V5Y>yvBKmZD&`j$x_Gr6PJ-4>%K30uSV6D>JO{Rc|7dH0Jqv?zozZ*H
+_nJknpBU!?fBq<uQsGVlLG49wYWGOqh9@YUSSQ5$>lu+#*nxw_p5A@&xLYaoz|;(y8<GV$4S-bRXwny
+{Y!S4n;NLU!0#)U|yZ0v&i~$^K|$Gxw?xz@cEeuy-3$rCiN~1e(eUkl-x7Vyv1=bEN-4?c-$-8NK15r
+5UuNYm{)7z#M5oPJV+`$8wvZiyp!&2eOCv2y=GcbxKf{ly~)+st?imdJ1s|$rqj|(Ic)HRMI@axZ0vL
+UQ*_c=t@yg)IdKj4nuJEY&3K)VbB{v2t$l$|aC<@6C6taMu}rb1Iu`03D34FpB~xG%=reZSf`6jZ&VJ
+q8Mu7eiRIy_-3{`>XLNX5y25~yEQ{@_^4j5np8*9rf5Uz%{^^JdQCWd}2*u2oCy9=+b2`f+NendLHga
+MI|vtq>c?JGK(A7meWHEGZBsx%m^Z^vaq2y2N|h|RE{2{C?{{3inEP~ZW}^V5Q4v0g+&qZ+Sk?Pq-jQ
+LeoU@~iNUhg5b~ieP2+i0qy|k}%P)uGlbWCz)%Iz>*8lnWe8eN1i6q9iCYcO4YYr^J1<RP~`q2eE;KO
+MzZ5%)&{8rMMG=aTp{}^qS|z0|2RYED~YP)CeoB3YtE9(2zCi3_5`~xdCavt{jtT%qnxj?`+3Z9ykxE
+vpXmyfSO#*8Ouxzk)i+t)-`CB(5s-jVAyp(7<Q-V!c<uS(2kYvg2$<>Gq6LwdY=8g0QV_D@h4LxQus@
+)W(>F~RRJ>*C!a_y?dvOdspSf$3F49tmphtByRYU33uuckirRw+{ukD})sD+MrjW6Xaq;nyyBjgs;k1
+knmvn;cYpj}9TM@|meToX+?X9}r&yeL{{@0=4o-a($|U4T|mqRO2#9afdvMfkA>sxG~4=sIv-f#h>k7
+lyJ&pJoK5b@Eh8u1n`JJqTJH_85O=+w&Ih6U0O7pnkhe+0#APbT`T*rqtbP&zOqUHSc4nJ9#AIqgD1^
+nj3^^PHTOyHQxEm!4*I7dM^|TdSnaCSPM=8K6IY2l(;1OnkFMvS-a4bqWcEcb{fJ0<`}b7`Gof-F~Pg
+4RF(+XU8SJv=84*RjSLtyj&pQ~UQLIJP?lPI1Gxhu1;tQN$z=Rons;5X;ZYlO>gZk!+}ts@NH2RBv~U
+Q|VBY}!rvEvBa|{}qKSeBYGizwO3dgggihK0Qw%%WH!fzbT>K~l>q>-m>Yj4@<Di?hMWv#E(HNPL1t2
+O%-@AmNJSHF5b;a`dKPTJH$+1!m+^y^d}7@12IygBd}bC^QkPe++GKaP#;ZAv0m(czq%%GLT<@@VB0E
+lwZOb&A1KE-c$omuv#I2C0O@YF)wA>C16!`8y%<<k^qcS2SsG)8#{Ta(;UHR%<Uxak&sHZTP9A;h1u_
+W%vbeK8??7V$v7Llo~K`uocra;W){}qz%!)O9!{SJ(g_f?!9z7wIS1=^+vstm`Zb`0?g$oF~H1Kwjr5
+Jc;KjmZGqQZOl1-ybyHEwWqR?iiu6SYpKE-QX13qYpzRz~j&{PdM@X+^DDCq6{@3RgIf6Y)ig_~+pZP
+08t^Ys7e*;iU0|XQR000O8^OY7yxkxk!UkCsI-x~k`7ytkOaA|Naa%FKZa%FK}b#7^Hb97;BY%Xwlty
+x=d+_)8fpI<>ZFeIU4x!t}LE(S%h-2w{~*+sJ3JUD^Okth$>8d4=GkEccdd(YuTq9jh71l2=463KJ@&
+W&l6Jd&zv2Qw&ERfHdVsSKfeExW<6%Drzk{(C1MAGvr8uTqC!`aJ$L?AW)wW1CHj#CzVCwLJFJ1mc5c
+y6Vq0tq*KVKK%IhZ$Eu_za{p)!ZVeYgR0qdp1stpH(UxGX#6U;(1vy$#m_$|CDpWjFuWUg=IUq^14dL
+oZ3)*^Cu`bmNyD|NrdP!TJu<!Bkn|&@I?|5+n^mC6p;$00y<q}VE*d5b(0<%(Hn_$V*#+yBkIekTe#@
+&0xmHyH+v<*LJt_7xuzAl+F89B&+7x$Iu&b-9zY@*B@-0Cd6BI@hBgvkD3P<_O8emS$fstWmB0NDY4a
+NR7aM%k1mO#&bYgh{?;DVc~$~Eg+XCKcMAN*K4Zab79FXl^?Vl6l}z1W)rEd1xc^RLJOKI5PHf~L=`Y
+B-wgtrTp*$SPR{QSLu&CJlO~j@%g5cpa>(CLMyd_`G9)ctL(q10%e(27BbsOxQC369SCMi4m&6$bl>v
+dL-bieS4G*Z_iuuciMsW66mNC*R0$mxhlssz%F-KniQz1s7Ak8B_i=bOd-u2xcO+wXd^xjbR`$9%vYG
+cdJA+$K8V|EO&~z8^_IxNXx=cw#4}e?AWY!<)jBXn&SkG8V$@=Jy>A{llfWe^fY2-ww9gu-vX;Nh*NW
+Mjx284y(!}W4$*{D!014~u-BL#U2U7whK|rEDAHhaXdS@9GOMggh;{uWtD5diSgGr?LoI~;i*TkVI;7
+XL<Atfv_5G1kP2Vrzz-17$F+vi+J^O!p{WL!b?L7vFa6WSWcQJIGXYi{h8(!7`qqoG!ef}q=oUIUQ>i
+N|F;9cvdXqWg{;_F|kY_kZ|Ab~}=l{W&94G_yL8PzIG))dWYXn0dD9J*>dH=1`&FTY#@o=h)GHCE;wp
+Qhe4yVbfet*f|nLEI{fF3GXf)FtBnTj(|O)d~XgvfXtlfghc7%)<jOxuR_L1ezc-3(6xzTM9=f)_ih8
+BE@H3*f$wW64E5#a2-O43O2ly71CpTJSTZ9K!D^;8Rp<C1=<Q3Grvija@?%SG=`DW0MWBo%!bc<F7ix
+g;v>AFYF=>w^wxnZFLm-EDXZ$u*kON>9q_gmh6SG~Y()F#C%2x&OE}nP61-bHxop&7*YfvB-s5s~M$#
+x=l(P1dW&50v;k-J6qOyAjq%lE)++FU9Pc<J*o+*n@}Hpb#FNouIlNXP0m>Hn!nKzCYBWFwz)qfJps1
+2y#_oqYYdAnk9Gi+l~;&<$+od8JWMuE)$$b_rWdQn+I6rJ#e66(F$X>gaR&4D0Xg<_VV%4^u9Fcp&I8
+;*Y!5Djx*o$_eTZ4>1ck0tpoBQ89y@hlvZ%dEtMxevAjLP+wE|zo#OLYCp-Ybyh-~<x9eeK{T~;5ZLQ
+X+sbdO|EYRCe!3`V_UpVgxw+PN*>;VLHS_5EGgBIzGoFfSq4nU@qA$%oW&s!LmSmhDozaVkp!Z7b^>q
+wEJJG`ggB2({XS3yX(#9vwI|)Ch%s7wB1ZNn6^6Y0NYX;qHe!dmOUr4qkIY_i6naGM_sZ?A()&j)LGw
+dpDq@mkDc-I@_OCGvPEDN@;EAFzjptDT7GP@Tdk)B>R18*ZMSO;9F&(Z7=2HEoX)Zp)24=qT(%WlTF$
+S%%{mmjD9E`#Cm<`o-!#_NKeD&M?1<G4JV20&z=hV6&PJbM@1WLp<t2`WPrNhr^d5PYUi(pe2*CGC4A
+mNg-M{A}&p>LB?A)g$l2@A8>;1022Jbll-{`<mQm6Cpm?`=Q3Bq#Ziw6Go>(WZ0Z@!MR_bdcqSIB-wR
+)T0bpkJS;zc$u=|ffr>}S%K(~ZE(Z;A9x24p{v2xBs;JCi45bHW=wyjtcx`gAMxcnq3UaOgOionWsuN
+l1|5y_iGu$kzpgj<&R+Oo5K_;<CO|!hvy)CgjH;Xy;Ulvo#Sn4}kyHb@5oq+#9f4S$&ac}V+pfm5^VN
+gkd$@MarzNEtD8-anrem6o8$|0CjNYF~)J=GpsAE`CpL*{!8T8M}>yxfgjKHHs`WiDOwj2jYHJ*!)?X
+=<8XlqD{-5+mGVg6A67FU@+2awr~&i^p^XiBJrgokc-HV($zEsERiQc&}oU@3p=n*Q0b7LdDv{&#$Yk
+b}=wX-_D_}n+oE{?y%-gT#hV<X8TU2c>*WrnrgNjI^U3=1o;3Rl|1R~YBx5^kP?ly8O_ne<}wQSH#WL
+8`1ZhbNysk;02f|6NQGs|Mo3W59H;@gb*(r+HQZE3q#RzNjze?9ZlI5NqL50rWIq@?c%n4+!cL<s$m2
+v+6A=pX#CY-7)%F4-#!fVoIOE*^{?i;k!I<_hR4q&zEYCiE#LnwBE0XSvNBq<@6FKWbfycx$1E6iDCq
+PYnZCfmR95KTpNT&7~Or>ykaij4D_FISBdmNH|R3Am=X!PelJ~=b{rD@^2@@1Y$JtAAI<Fv7_c_LRV0
+BrxpZoy%D?`42Ait83M_J{wVi={&>KkUct!Oz^G#9O0{0hZ@6b$d%M>WVsF?3|bGvgGbuC0Rt-%x0nO
+ao`}o3nCUELM<DXUk#?c`P0>+p?Ck2LjH->#U`({U_FgS*)%3j7cc={JShk^=SFAq*L$9E$cTOX4Sh=
+%vhLWpgzgEl4PI>vcc%{%F7{Ay5PV~U5}YT;OLW(`$+2G}P%8}knt1y%+pY|@x{a|H;Qs?qO9KQH000
+080P~d=M?@=AP_Q2W0KRAd02KfL0B~t=FLiWjY;!MPUukY>bYEXCaCz-LYjfL1lHc_!Ci3|J+7u)?$!
+1F%*>P+or#i=SS@L-|wgQ8|kVFIm2mtEg+8=-Ym?r>AN;Y?OA5Mua5}4`f>FMW8_juk^D-p-@b+>M09
+E*HaS4}69Sz8tBPR9IwYfC*(tFn{VU6If96WKInrN0zaniSH0TP*T&p}#ifMb+w0b=BtAb<!>MgLb*@
+^1^)UlFocy&+4X1WotUOH|ArPH1ph!{5h}b>((5nSLbykuTxofc~!Q0ipM2DFNy>o9yLwX*v4tK0-S`
+MGY~_@T@|-oGX<nFn15VfELz(IXYPRyw*>sOInc09(u-sv+o@Q$vWfB8R5a_d1meW&GJ|KAdDE?dbmi
+q#*w1lU#fzevCB>AWT{Q?UlZAv|tE9Pro;c6x*2H2GU&=D88jS~_88@=6)(wzu&?*A*N2}^m8ocweUU
+#GIbc;9tcrtw7$ga78`3SSv@EzD@j?E7Ud+L){y2bZ5ootU!MAC}Wu7R${CtI>;<!EEmVk?ee(ST78#
+fP9-b&Zt4R0LVx)<tp?BP-$e8IXKk$64M4Ogi-wB<%&hKQ8C^tE`$;QskdyTqY|51YmOH@Lsn$Htb;O
+K2xi_jdR65GvJKjFwmgAECHPbwZ}z@Np{Mv?KPzl<3c7GvIBt<!7^$6M_!jzav^D|u(HU&m(rgMQ^D)
+fNStAd?H00&JK3!AGAYcA@F9rTqStL*f+!J>VWV{ka^`$P#x`G5To)^(JwRzvqlU>qgtBVgVTVQ{<Hc
+Q*q!QPQkdtMLifB0J=Wk#9$MJt1#YcZXJ9>S3eDeAfo1eaW^XBC3+0jeyxz`bArWOl^zm!c2Vj7%pZE
+c}`k_~W@QdAK%eMQg0m>43CC(!KspPx9;p?MfYK!<4d`3a&Bb+gaGWb5bSH-lC{J8E&YETF~N^S7gL7
+M~f6{+%??csO0?&>Q2i=%Myl_+xB=46-;WF#?o@cp%E^w}c*#k?)U;!`f9G(<r1&d03`!l*!q85e7K=
+s>;@d6nP6sfSI{WauOk!@$bQ>H~iLCCg*C7an;04lD9HM%?Dq2L{#0d#8Yvk4Z+z>4QlvN(kwtRKYDa
+=g`Xyjx1k&UfGS%zB^XIHKs*YWC$NbS1P8J5#}E6%tEpHi2vL?5R@>X#RU6@h2sHq_ZN*jIEkT~d(b3
+<JPtPJ~GMoT(((M_Xi|WZKp^0i(+Ag5CWkjZ3d~wJ&KH?z`T;bw5DWtUu3CD;C05$Hjq$On(g8hzQ)|
+Vgl&L?1ZKptYSYIzyQQGJ85u#*Op-gvwg+Yei@Lk4JDJVZYL?7K|Y9kP-;x$t=hi!%d9h9)@`NObk_7
+!2<<eRjsE6L2q_fdUvJfcZTTZ_%I$3HswkER$j`S_uk06JQy{x)xm}zEqFet?$VCg>6^WFrjO-*vZY|
+mwS7AQ*o7mwx8$MGCTZwZ*M}BV>=eY0!>A}06i{aiHNm_KO|rg6@Kg*MVlLY-#jI#8KI<ihYW%nE|W4
+VWTQE6<W)Fdm+4gC+D(zLpiSqMWyzCLywAZ0xB@=zmQtWSXmL3suz(_IB+xR+cB-<t0pF<-S4&yy8Bc
+&oa%kOEis)z&5Xx#Ql2RC_4fRF0Y^tj=65_oS;A^Ezcmht^Qa0#KnVt%e8c`&g0T5s&mQ^k1>jGY&HO
+|YmG!g`?3*576jSM|Nrp4Ka4!B07>jsQy2VD`mK<HbRv(Zy!;ewB-8Kn4ka$lp<`1tM7^Ou&#9^lx(M
+;$T^!rCRj&K1{m2@pc2xCZk6?eW>s#DN0NBoVx7@&(#*f$K!nBDl2#>d^aIHsqU{zSz*w1eV`fc8n9&
+mvGn1>M5olbMzr`IMqm8CIwIz#Ul|5cv~tC0#!tQBKnBzydb{mRi>#4F+rQdQkaFneqaT({y;w`1sxL
+V2N0}Sj3yckY6WHOiDN;_h{Hqi#1(deOE5AJ-HcTd=rQ8qo3@iHS;BL`H=K~i0y9gsvFd}MrUnGiD_I
+xaE&Xw-*bf%C=~`q%oDp;dT7YCcHWO}TJsDsfL;*CxFCQROaC=S;&RrEo(G{<s^@>@2fS-F9fIhwm>_
+YyKFTnH&ln7i+RMeuH!x};lnRXSZKUgk=L~0M3@Z!R2EyiJz;gkhJp3>h>V5}~k=mjkS|9z#WfkL=M@
+B}_{l(R&c)03CidE2#!FHF}ocj!@y#~RuQMCb(eIV>kGNqWE(bs#igr&<jO?s5zM`xFF-B0DuEaWu;r
+C^<kA0=2xUN~C`nydhTw?Br?u{KXk~Cuqk4PzvbfhZD~rI#76oQCr8YT=W6aA#!#Sn-B-;KW{H+$y5s
+%8Ug_v;8LP?MjmvQR0|@E+tvU_wt4}1iT3;EeL#uVC-L#?H}B2{X54p$!hx*{URNB6g1rRC<hlmdlbH
+zr&4mi~wwwG-{I-@YLOBo*+dw=Vz*3X-p1X>V=LYOT18VAxw$L9V^!e&=0%89Z7|4lB2h$uK+)Zm+gU
+i^BO#2@Yr&|c&!)8JRKu}=3Q4n!?3_Z-k9Ee~DjL0EE-3S;F<BMOb9Mpy4jL8P7_%?v4K)k1c0lTFII
+7=diP&%w;zXC#*&yGd160@AbE3ncf`%T>vw89t2L(~r0HIC(P;83@+m;<}Q+!|j*@C6!wdNqpBS+kaR
+qfLI4J8UU84y(E#&9OnGaO7q#jH0^>#$edu&S3{#7I%(9ByfA&IY_?;7^X(`88)WP5?F8e1tLJ}Jwg8
+imJ=3+R$j+*)<**8B*T~@0hg~Kzno+NxEqZmsH%b>0od5?TQn?VwEqSQCTxNaza%@K&jWPuqGPS&{9T
+(6|58}%P|XDywd+|J>;MdGs3lgzayE9t8SY3aNWgpaRXi2@;J1rM^lM0)htdB0r|(J5xcx3QjP}$A6>
+Ak9yv@+^iy^F05Swrr=^q&go?mB%q_)_LKImY2u*21J_}*GhLSlWwx1W#?Tu%X=YzXMk8AwoDuWN|x$
+fq{WT|Q^+rp$fb_J$gW20kEAF@UmOA*nU?Fatf3b+jya96Z$hH%P`%95qe!re3)0R)r-7{bq`Q6Rb#r
+ni|`Rqy}HSX~}P5mp#YWlV755RSFE1KnSZgN4j}n6(S*WGJr{!AQs<tc~{7%AAb4v{Lx!^nUlZsF?)O
+fTuzu_u+!6nw)Yzp0T9pGjR>C|e4Bju<>~pOXHO^3PS-QNKgMvB+!<kUw=qNf=Exp74-jjV<^_7ZvtW
+X1U|tU>J+C7Se!z$--#b`$^PRs2da%L1sLK_~2}ak>#xbrlUa*A@cER`3pFe)I3;{1tAyF3$3`2VDJt
+8=BSSo@dzI7iUs0l<~BSAL*5H#fhjB_))_T&}<{HfS?<lM#O){fB5={X|bN+JS9)ZV@_5wGnFwPj%tf
+5pbo3_5d&Em{l;6d0x#E;sxBc_gE$WA9NVpymhf1r)JW4e%_%?SXU&zNU!`K=ZF4UaK29&#z%EUqe6R
+b<;?SDvm$JjgeK`4tZjnZ(Oee*M)oC;w<Qgv>-lSFeOCH@7zb7KlotO%P$W7)^}jx8Esm|;Q@eBeegB
+zs-9x{30i~x)naMl?kVoYG}7$a&`XAc{GX#)R|wcZok!R*7(adbbPSzfcilF-?J{ZPZd<J+@xpGhL%P
+5Ajwhlh8kHaiLQ;|=vqRzYZrh~0FEt}M!FUiwyZry?i2oZXFQMNqp7PxT$vZ~-J?OefdW3Ta?!$JsS7
+NjNhr@l^friyBK)7yifPNTl&D^q8!whwwPKvqhNI;RMYqiQ}E$?ocC@twea)va$`RniK&*86w0g8(UR
+ND{!s<xio7Vya5(&5KZUDe^98++O=?mOJWH28l2o(j&vsl<&Sx5q^75UG}r#RqS#&$Tghp&_{7eCQ|A
+c9TO1S2H<}*$=Y-hd#Smf6~}haVg{WW>w_nMSF-UuBW_SsJ+sJc?&ot2Q*XA2xXoo;Gy7F;En;GO-=5
+CP`*sGHDGs#+aQ*P#X^yF*qFn354XVvoCW|+OksI?#1i+~dm=DKMYF%jEgWx1UUxk&{8fHox5{*zshH
+vJ9cuk*J{NeR2z0A70Z^`K?+*n>=U+tr044^4wmM{rWSQ|cr8=avQ)FP+_`}Xu2j{%GX5Qj+{rX_%D{
+ptwAfwM8_?$Yd3RuZfUJa+JTTFPGd`Qu9M+!93rfORVY%lXvy1V(utGd7q-HtZH#jI)?m{`WC1}@-;I
+xNA1m;x2^!dX042#A-a5H=%mr<VA^4QJF=A~tf!<sQh<E9Ovjz=J0lcK%y;Ap<SPk+I`>Cvge^J~44n
+uxsg_3--3H@OaDwLzL~)V@M|wGO;21ZG2V<?i+59Yo86?UdaH@h&UaF+(IV<M#=86Zl*kHx5<>eRQ6E
+uv<cI7@{9tUZNf0x_7#9~05>Jzgm(vpBg$;zZsYgf+X<UUDg(BN4GHnv?Ih$4*_V0Cr&-9qcIU-pH*o
+%yI+r3(T+>`_%s6D5DAGPJu9BP9478EVJuRvz;Y}gdNl-i?f39VWIu>)E{@8TAc>-<-jnb<k4-k?*Rv
+Ndr45KLaRyFRQ$n;>C1g^pCyiBh@Ws1B%2Mtgwv=ML-u=YHsG?lo9=nCS8BA;n})mxb!;)bP1s6g*-W
+ng!110Dt?LK}R&Ngub=!pshLfmKUA&1Y6QpPjrsIS}&%-DNs_$|UC~p;FNON(qV`UsiMRLsNa0<y-mN
+8k}s@>g*S(yfmHgGTy3@zX5v;-bL6uMx<#fQ}N;4V-V;TX;hq?2b)~v&bJLoFk(C>CckFvK;HjpFY@}
+($u7LRM_#7II+JDz+Ix;<+B13-YTn(8nBR67=kG)mKm}YO%gZBrtF@Z_(<e)*Pqc+dG7_p2RIu}OlCL
+$6leWpxDfLFj5aZaPq9Y?SuFO7tLXyLnNwZiZ2hjc{jDe?O$fqz1a(;HuAHxg*Qn}<N=)+D<y7-$=*w
+QYan;~jvX^75~WTK0#Dua&p{xF$VH;Kmj^(kZsv$?NXvW?w5dLo^Wir9@^5+H@$&y5C#)UKR0n*6Ne-
+5Af%jX1|BM7Bei^$!aB=M}V;J*M}e>Bn~i9LMHf{n0=U_20zc=&#xM0Q~OOVaA5I9qW4l^@wR0#5Scz
+-tt-7y<v3I<?b=ngAbfJMW)<KQI4*v1`|Z$R(2kwyvHbmE$%G3kT=?OLBSrh=r8TY20j%B&D`mFFV1!
+T-eM7B)My=#ab>CZR*4q)C4!zi*pm~FklR7BDTq*qc~?1V4cvZI(s+h@ctMNlXDLK5O<pO1*q@B~aT?
+!_6FG1@o9Km})VtH8xAE!m*%4)Om45a*b$l{mcxAQ2iRt%)>;w5y0lAUwAA{ncXK~gYL)NH8Rb605HG
+uYbl!*QS^nS2Uq0)md2^>WZ>mtJ=nkvg^TcLG!NQuVi!{`M9*iVf8A?;}v1T-yf=v5iG-w>Ppx^6qo_
+q^BIw0xdFuORyH-8Y;RZR(ianfnAP&}^)1AnZva0gv!Js?2&`osItnXXy9orI0ZGu@7MY#rn}B8f7z_
++hE*<Df@>N2MwB~9NPg4OY7P@Qsin46yZqOe>!!>t+l{NYvAJEJ8M*M$MvFm>3Y<@Z|<v&A0=mHAyPY
+fYK8$hJtX)2?g%=S;Lhl<kQ%5qrfOxXqkd)o$=v~HnY@%mRqq&AjzCy3Wa;ogajGVuNa~zU`-L3_1@4
+Ni$W>BO2INIqT{#C4Jq6hId=9NyF}rK1WR3Y%uLhC4e&L!T4}Hh4_i7}<;UQTfx^)dWhSyVZGeLg}jF
+`l%(gn@rD$f9~>j`hjz=NgC7t0QwFq#mRFB8nZ0@JE9d-Ryl8B=P_?iKC0RB2blKfXVHefo1acMa`ee
+@8_>^R(=Wu@^K<)sDRa&RM6v<xLwtYK7+!7k!LmE-iZ%C-g%IM1(kCJk{eyXU8Wmcp_5~>_7SPtH9SJ
+{oSZh*)eB>YS9tRLlN#x#ooBEc^%ZVl>J<qqDSC8Jl^#m?GGP4F)T;vFU>7$GS67G&=EQ2_!2QP+{c7
+(C-K{tC$C@qV=|<Cf8D(n2&kx9>Ae^un#pV*!Ig#y`w4unj$a>Pk<F8@8Ab2qi<4LHexe$jzwY_lD45
+umHXruSp_lr+cdqvgRCpGzSD2{iqz@~_ro%USkLN5Yp7>tkPCT7IlWbL(|ACHCCy{5t<tc8Aq)<m9G>
+@SUOw4!u%flBRDC}&tlas?>mDe1iq)wJ&rxfq7r#VO!AtghQXZJ}%`ZUooYKHlWC2Px18vLgS_u^PuI
+fWiKGchw9hNNwB%sCgFm29I;)GbUb-tViCsoIu;IRHT5)NwbM%S)L}jIO8j`9?0*Mba?ZE4i9URa63y
+Zv<CWFccRu+1{yGG}XGEqELX-UVx)a83Nmsy)GVKZ)4_5kz*p*9GD$EE35`ZSZZ1kh-4=j5wy%#x{km
+Z8<k{<R&L$m;_KviXL37L-6D4EP1C*@MAd2Q`&CX!jr9fb1=fnz$T1_SD_a9e#$`U#^`+6zdHn-&3&E
+g`=;{id{pBm>5gQV#T#3U$0tWPqa6sHN2`6kT6rY*iNE*ywN0Wi=@iVvgMO>ZV;_FCH#E+1YE@A9B^@
+bc7UrAxw^q?PxUX1h5t)m{poC^XCN?-`x^R67>Dt$~aM|*hE+;4@4m!)$sg^4;qE$<oT>J0IIAfDTej
+lo-oTlzx|{o`H-_%j)j$+8$sHn^Gv!!`qxVLL}uA`;>Pj=)Bbq1fD)4aBe?{!pcg;u%bcZ_wevq8ah@
+so4L^Xdfz!p^kj+?Pw5u;0mv+H0B%9rKpmn*(GJNON!*#XghaMT@-Wr_UOkDpR(krx|_Xfe}8|R96TV
+NzL6SctVwNYl&PSrhtMscLaps4tcp-cD?LU^$x%ofp_qyv<c)Bz`sSN&#7^H<ihuIFl(ii!I_V&@*4m
+)yg542=HZWmyPW&&KFaiWt>`$^_1TInMQU6CR*#89+rs~w({ATyCe(_mp#ar57^|7NRm0Adz19+}N^j
+k;qD}+ZOVZHQyKn=1B3<%xbRlusm_$p~|lf(~9f)`0iJGJU83B%D9@)70BQdQqv(FJQqrUoe+$xKEj@
+`&+3aXHjCPeeWUfppT)eZbo5P9CT={`T-m^ffHBY;nh`l^_;6AWmjflbBr7nLe;IA=fdBtnT4heDEm2
+azUO3O_{$_xQ`X+vQvT+4$Q0D^9z_hprkgJV#dtweVp6Qw~x^ajL<7^h8Y@ojO#`Q9%^`9)cLP^FJSZ
+gzDqBQ=ML}15{?tvM;wrTjd#z<a3Qoet1$>0{RO0V4AvYs7h;AI@-Hsq9l8Qap_+;;DhIz#$H6}kr_~
+B&scF_V-Mympz2@M$sj_t{X|HJmgs`n;n$L5}R{-MxCS4_3jr%FLiMjg|9P<vAMhfIwRlE^1K5Y4v{W
+2-?j0(m!AA35&Y?+g6&v>74#79|j+=sly|6@fHsWl~o&PZ)s%J4tsjjD9_i{K~!zqbFQYl}aQY>(#34
+#A;AWn%=Dwg3B~1eHFPR`m@|ubTZDVo=JEe`nOdoi;`ex4e3wmxDa1TVjEGc>Puf-tE}`NlyKPKjy{v
+YWDxA505h5_u$PXJEMD2sv0Nm=r$AQpkXAT+hnSm${p=JhAtSgw&eHnAiH>tO~2M<v-{>>)-ouP(OV~
+Z8>^2N9mmEwTXCb~`eJJ6;kQ-$b?5>bd>uEFujSFLo${O8$#K227^8@G1Yu(8qn+6Osn@$CsDJg2Pom
+p`yG^sfaLaw*rhNsN0KAKZU%~Lu+pT`vPzdH?_W<wd(8(=i2vUsX*st}Bhq+7a5(1vql%ScbzswWU!#
+)ZF!09ojrX%Y|>!z6KBZ@vHT#Nfa{BZpDpN<Yx_VBaei&e77Q_<!N2y#J{x3-D0Rw)F4XyL{q4#vS)s
+Sp%NSybS)s&wOrtr6WFTm>{&lfU(fxP#P&FKMT1rBumk&$!*G8%81;uEHPOgLhX9<>P@TS6parj?nB#
+I;XpL(N_pOP_OmjW{pzXpd%hOLHO+8<NL?BsVcVNbM$DkO=?XF{su42hmXI&Uz2Bp%J(KXy#d*{hK4k
+uf4*nWVsfB>D5+;Gxa1g%gcTxKE;m&#jfOP0*wFwgVV|08G2f_~WuT-kUncWE;ZlD*Z=KI2oo%QK26U
+!aWk5ir^oH%sms`#-3cF_e<0Je}@O@1@2*!SfOfK5$xW&JdElgM06#RthN?pUo>4JFq${`+(jr%^tCU
+I59j1QjZ-jZgBg>4{I5Kdjn;!xkL_VVcacRx<eOR&=EEhCbf8>{msYG<pmT@b@UJeZ&@i?IP5R}WIQ8
+#d`3md-^WRJkQyPT7dyW9fh2_P%Zh+xqL!7okRAF}5GJ+b9+IJyI9HgaBxY#hu$dzs{hoa~JT7ZQ{-O
+5V7U>({!_!-q^LZsPT8Vn$p*X=Dp>V=*{sP&Ztr94t|B+(oV(pTa~k~o`V|L_Lo?x-tv;25Pg7%9%^X
+M>fOb5(lN@*XkBMXCqu8r$fgS7+{2!{=st<*>Q31-hfd4WvzI6D&gf0ftTt8jIOJbGhoYpwpDChuh*|
+VJ!@HvATW!WR3pAJst}A1obwF5*O+4yuRO$2$CEqAGn@ptETHV*+3l9&EqV2Oaso%riJ*Xj4Fk*0Ynu
+M*w$zU4<hrw4vNtr>fi?M<5SJbF0R=fswQd&@`%VUyQzo~ciAhz;O>MfwYRyS8vPQ3@fHdNCKbH+7TW
+ARz%#y>v2d$34}1iT0Y18;$|B0!I<cJ(r$MzrrheT~hgM+ZFM8IP=k7ZnypbyC|ya!@Sm;hh5GY^Q&Q
+LEznkDh*~apNPlfEkBk!_xu@Nf#YL#FMmgqgl3OQT+EXkli2A<K`Psuky{5w09>Cz_lF|%2-aU;GPsV
+U40nU>+7a;)TqfBlV#AfX8!f-j<UVS{xG8-vZ)B0JOPFLaMjL+oGPquZaza#`fu80g-SUx6w&XNXzCy
+yDUYS0$YILy#-%f~iSU@Y8>2oN3K^s8}WweNZ4)y+%ub<Fi6>f&9>kT0$0LQnK?S%8#kcG<qDd)_gBi
+w+N;1ipdl7WhZE>#hHMJOY~-8eI}#CsAHUt(@4E@C9qHrG`JI*`DnR3wSdv|h0g0gAe1g^^t(%?$4qv
+AJcv47z*Y)1k5j%sm}j%9``kxbT>-?3k*Hzj2e_5ZK&&$HgKr2+v%pL3GKW2~y91yd`@|A%nm4eId|o
+P__6D^fJ67`pc05#=(vq8zHusZA+Ij;=QbN(2nOFe31?~=dXC#z`?>KVcJ~6Om(T!cXciy^iwPzQ_cQ
+tofjEJ0_Kxqa<>T7zT+j@aL>2fPT#-v1Hhmhl>QxD@0+A{s&UW+w(8amRXRwVU`;p55n@CDUQnsoo1|
+%JE3d9^yj;XM&dc0hOhv$*;Ggf!T1X1&3R>#|$L4nS<|#9H@8+q&@{ewM>RJq}S~es0&QX{xUIUkpaD
+xNH^p6PKfU!=ysy^=2MVh18g?Pt`Lmp_m@kT8(m|4Zs4oI=WC97o<>f6qtCw2Xf8gWXw8H0+|TIv_31
+(pXi`9eK#hJ;s<^9DU}`>l@{G}dx{OGH~rnYGF!L|S<HY#`^HUMH;m4y+8!sk<?V8^v%7aU2}Y2tcgw
+?7u${sgGdKK;1Pe+*Oy1v$Y8sKs;($68&!(2nB(!ESuek-c{wh6dLTZ56Hm>YI`U5P^j=uB|hIEZHU&
+790VSzoCI@~y*6MVaV7g)ukzV$yRLE9YgY>x-j}74vR|mp0W93|69W{rR1^nF7!!D~?070%5^S+(j=!
+DyJQ%s`<-nhe#9ueL1eJLEu!F7c&3L$@nSBgzNu0#d7yO1Vx@G^h;=5mU6s$)i@(%!^(d);{Lf_9AEM
+-wtHn1SpWHWK}+Zy6HB?B;Zy6u)Od8IxSj{IrstEIEY-hsMH-x`AyJQR4ZMQ&gdo(uhonHVqdE9o-i6
+#%6CoRkh~xGWIAsO0t1Fu5j1-qGcZD$@qNA6VkXxp6rHHwY}4PKP(o@oEj(wdS&BA~$0dnbkU_@__@f
+?7F%=*xg;gFl!Y0)$Xk7x<b}B>cj4=sAju+(f)q)wR!|Zeei1ko39NKH`y(`2zg+(;;c&PADGY_&G%m
+i>V*?$QhR)hZW8?RFJW}<ZJhl&McwKb?7-9Uo1}2-9INmt+ejoSZdB-vV{(XTt(pE)a>(hGG3;a33EP
+_Id^PhSHpl7aDWLknOeWI*_`x}Ds_3W4Oa1Gj8PI4cwBDbmk``Y9>s)VJAFOx$D^&tR=T^yOmGcE-Zh
+0WHxUCz10g9u;+pQW~$IZNbC4-{iGB8e{#akgrIpF1gT~!sWcHOWV+%H0OtHNt^f}DwH`}kg(8WzIU<
+fmhW4ND?lcrM9e(k<6j$#oQ1LMN{jAXV{5S?w~xi_P<uy4Juch8*Ddp5L9y&f4rDZM_<NzUpM%xI;3}
+=2Y(K496GU0c{NI7fYF5aQ3~rDncx9E2Z2t8c-dWQ(f`_uQEMfOE_IK&;_l%uE$|8s?9M6Ld+f%FY{j
+Je*;iU0|XQR000O8^OY7y^O&Vq2nqlI)gu4^6aWAKaA|Nab#!TLb1z|VaAaw6b1ras?OI!N+Da0B=U2
+=sTLrkVaGaAH6?==qcI-O7t#RUP1(z~N1I)3|=x9a(c5BaX-|iV*L0p{8R-K2X`~Ya?>&tY1-96Z{tl
+IsAOX3QTkr|al(I*y=Q6#2hqEsk5?e>_fNj$V&F>QBU#lN$@qKdU?7<xRCga#g&3XhLC^J+>E8FR&mh
+$9llQ79yncFh7g8q;w`v5F(+ID}6_5h+54QuwiAj`>`xCEp8~zLN`CGrL0`q8cGp>F$S%>yw*@eyvvX
+*oefEMUG3|33EWT251=2DQk9W1YQv;fp6{@XaF|GzE1#ZLO^LXVS4wz44Dh2xJ1JcfFUCh!E<mD;Q8F
+8ii^OmX()VIi5QE(62!p|M2sqy-!DS=!q<{=0nAk*;tcR3*2!S6s7r48<Vy$M|IxnW!-z(Ub`Lkn_J?
+j5UW1t9Z26~v&pXNcKiaki&zF(Lmy?eKTts7EHM30*Jn+O!Ci{Aw!6#5m4nClMXvOgW08Xf)gG&K=4>
+G)g*25Te6QnrWdJ*^wh=K8#Nwl2sK!1JJKe;BJaHa7QoS=`8L^7p#FqUL2G+MI!Td#j{a_L;&^tzW$_
+xARrd*A(*Ics4P9kux9u|R33np4yufe7{^L8kzWr$9U~K_6uUu@g6tNXG&5$Z$cv>CnM-7&+gXHUU$m
+22<wHz^X7->8T;06jL^vu!xbpIPh4sr!^l#f&^L^KC($4R%skIr_<19GCyM5wg*?@pWOH9;0#O~iTU6
+*5+e?)1|O1lMSmAJcHJo(^y834ps9I2cxfLbXL5USeQ|T$F>V^gfve8~e8B=w5=$?)(!j5F_IK*X%+_
+#e=I~LPaJdmL$Va9$%#cc^6DoVAv+D>_*~WKvAR^4&Ny8a4<;-~CjD!!4YgiQuSj}dwAf)Sgnp5yNb%
+G)7^hznTKzq1aC;d0Ug9g59ktAbVsUu8AFdys#vav&-DoFY%IXWViM<dYG`XPhaJOni{v>MiO4qKbed
+!{L=1(H2iQ8)?~d~aAl5Kgb#J3n>KZ>~<Q)*2%Wvurz@XKQa5+7Po=v_R--RyML@OLF_@jDo=ly+4oL
+Ge-C+<W35&ixIgN0Vw%fERFXeq!gPL!W5uV?#cIBiBT4542AF+4O$sf89RY{0y1PsndCl-wU@yvEhAy
+zk|qTPOwG;N69{+;G7>IEFQ9`YL6)dQ3z(XT=n19Aq27?<hY4l1j3ae%(*kF}Y@l!nMlJ%k?x7f8jCl
+b3$jS>oVA}y*hhpvoWZF~iMnZ~_(hUd;_G8)hlT32mw{PD3^6q`(p>MxDXxdbU^Wzd4>WP+1H<l795R
+Q&Z8DmqjT9Qj{+E|f`uROE6c~B6bX6#V5l<G2$t!rXiPC#y<coWPqFoXxSifyIWUonl5K6Mtikg*|%I
+7C{p=#wwEq!9>}9$^#Jf%1PM+;|H0)HHSdq%;@%0W3XRm4G!xPv{0>)eWg0MIFeldgy*+S7kDQ9kqH(
+D!sFFceDalVd8qQJF~a|r}54p05Eb|WX2$@eP7IU=3`@rw5DApP0%fcfpJov^DXRnQf{%<g`Ks%_PZh
+gjyuYk22@CDd0NFWZY#_C#_F)OW6+YD&6WDMQw6V*Sc;>H(pTS{<@IK(gpii1?8~YGy^9`6d9IfZw7#
+}F7fupt^j4iVm0jTHiYe-OWfn00998xwjbE2y{QTjojb1PeuIB`koq4YXsa45h)x>S&JcA^&Cu`|)BV
+P~D=4{(%E)~t^#oY%+jq(%cMGZ9;JI{-aYoS<&T-2wqMZshS(c+~QH66_`<Yp)fM>}X=jFu3Dx~oZ=2
+{=uO$aZtLSqm-SJZfneJ#J|csdw;LGZc_&6?1IqegY$KJ6~53PGyS?#A@{ZuX%Mdm7<LLDvNI;$u;*p
+E#s!mv<8)9#K+b11a{Rq&+T|mr1Ab7ESE{zFiU6~i=%`dF3>h?d6Ideq(vH!KRi!VqWyoKST|2deZ3*
+e!|yKBg9*io`h=BZuVPcRq#tZIdpFl-7aun@oOF~noXeW-&}$|<u;oS|?X-V@wyKG6BO?`?vx52S+cR
+S~-kz+2J?C!ZMZz_#7>W@a)5yaNl3cL@(Mer9kzOS1l#H3ASi0K}7a?tjJZzhIZoho{>eaF4&1JaNmN
+218D2~t{U`~WkZ3D%VMA+92saN1i`9Hf=&qQoby4~jXc4g|dnR2nCD~A5b#a;g^e=Ka(Rx&75NqJv0!
+7qb6v*qgoEt*EGXvl_cZ>htp?Cu(8l>2{y*{y?j(&LCuahE<0N+#U%7;uUyM|*iGsFVoGXk@$e%L93v
+NIKVGz!9+`vj}314t?wklSP$~Wv!NNc}5Sk?M}LPb9HrdUA&Yo0_ku+B40P8@8fzZsv9b*ceZ2JH^pj
+wRa*HTSV5!U3-N^906twCZgX#3qK$PVhnqlX9h0C9o9ek&z|19-@&OhtTsc{gCRu1K{#rqy^So7jY_9
+CaT$`hPOA6Q$Jf_l46a<vFK4zcFZd=y&v3TQ6{TW?I?NpSg*}x<LOfyhXXUz9kPSrNud_()KzDn%4Mw
+9#ge%2<XB{rQaBs?_T>{7rw_zS56H2^@OMZ=Zc^PP&pJ^kT~k`eX&A$6aKZjJcwz)w)HhcFr0dQQTAP
+{t9%GmRz2PTCo!QgRX>$26iaS21iRG+(<SLU#aD$r&@fF~}n(tM)VuYA2T}zxi1g?E)djLvjMnOs&*q
+;E4PyFBIkbrVg=^5!7leuUX1vSlScQDdf@G9px*l4h}_FY0B&W{WW)X5+qM-0ZJxT*f((Mzp_Psx060
+N|Mm1APVdv*-Rbqc^QnJ&mn#riedtkVP`d_KX~#N9f@08%!MB0>43rNcF*u!vzKB>f=%c5gbwf`{<4_
+ap89%p(qZT16DEIf6MT^f^olfTs(7Uzj?`APjbE>Zn!x?lGj*CL%Y7m+)(rd#Fhj{pA`{})Uz4g2f->
+p5OVLSpAjH0$1F$z6S9B}+c2nU{xfNowM9R6Z#PnO}0fbZMdL9Y(&{`p6yjJIzgeIFWTSSuLWO6Io%t
+Zt(u<6n>yF$<t}d3x-U6O4FWmUM7KwoT}F1~qm(8FEEj=$&Lf*SyFknqfSaHr4-;A@M&lbQcF`Gthr*
+yJP-0?j5~*eenMMu}QKCl@6jh2nojP$*-1Dr;9`eTILK0nuy42xJ`0Q4qv}6Q{yri&XuJFL-6TST82x
+jb083Rxcez2SmncbVleySX~0?Z(Uo`<*b6)~0KnFLm-hZ)i)g5_872(yafyzzt+m>669K$eaFO9Fqf(
+yV<($B5Tc0J#4V245MJ_blX%vZqleVLksK53fP)h>@6aWAK2mtey7DvqtO)dZe001Bb000sI003}la4
+&UqX>4;ZVs&Y3WG--drBuOg+b|5h`zyHgFb8n`0mHJxu*)tz73firZK`1_OM)b)$%_8_DA`WzpzUEZ0
+z?wY$Hym9%G(}<P(vI%3IWtR>m$e}SUp4(<Gv`Sa~o<W<9^;o?42TwMWGUoBUcZRbeLg$A3d_QXtfLh
+eh#F&U*xfk)}Kn-Jn?G#ttf!M4iyt2Ls5t_pjH(a+2iic8m!o=F^(G!fa$w|-wIxke;&cu2#<6?1JVE
+<ou-x|baT3jkGprB7jT!boDZyvWK9Ke&z>*%RqMo44w`GXqZ66O`iwv-lLiWqsOFRt)O2(qKNg*BJGl
+1)79YW^V{YI9!K2jaCSaecX|R<;KnRwD!YS~*1lY?1CgS8ToO{Q5T%m<iugN@Pmr9Z>3`1UIs{!u!%p
+;=dLh@*>Q7KoGF7q0y_2goxm`GB(Wi&&r)buAafy}r$LG;Yc2bK<;W#mXwzt1Y<0_&AAOS7AMv7V(U=
+WeX~=Ps>F=TXsdx8c6L&2(=!+wE1J|8AfwyjAGGHPO@opY_cry^}_#o+L_tYr{EcTr5Y=qw~|~Ek?{E
+zYGR8-(TP0^^1S|v8k2yy^Pth79x+f5WGs+gs3hIwo7=vW&9Zb9J2~JE*=9swiO(t9{4qg%>p5Zwi=}
+wT)9X&A6+G6Wfw}UWgXwEa#6rvP)h>@6aWAK2mtey7Dv9ThG^dc003wR000;O003}la4&UqX>4;ZWMy
+!2Wn*D<X>V>WaCyB|O>5gg5WVYH3@QN`TpxOBLtO|>4uOyaH_$^VVr7k_EvsFzv$E@d-`SPqSh7rdC=
+~{4cV^za`BWUo(GAolZxw>8gacVOsz&VyoH0u3lK%m<$XEdd9bhl4=4%3xnbtv$jm52S(r8*mG2<21)
+R3k{>spIx>Jg1~tWHL?4%6-&Mg6@pWyyHMae$AaXsZMfJj>?x)yMZvob>;T>j?O1Sn+7E!P@LFzt?Q}
+anPSdS;8~woL|mGZvlV$H^8@d&zrliH!t^3kKbSoKjyeEXC41!>v!;dD8}daU*Mt3B;0F<r43G5DIGG
+JQf2o2fqgw1jV#WwfKy3QBatX^2}~PHV-HIxq~Z>!`HmX`wdrow9-+GmuYF{)2vU#)+PL7R6ZFD)KC{
+~XnN?3lD*pLv*4=420$79-DjGADL~RyIpS+^Z)_XDL%ymI%2|)lgxRpYw19&QV=ytTQujICZy;KS+p>
+s|b>wqbE25|y)GGIfL1ckBC$YwRt?XlOxNUchA58*C}z4Ga(#*1j;=cVEr@ULlYV+fgXOJV#gge&i`7
+S1tzg_et}$)U(SV};K7`5mKqtDeTNUiv`2L~@6+7Ztdyj;=Fi=hL>I=3p;0Zzktgk!Tte76kbUk*Se8
+2Hc_H+&T}#t4lJ$K2yLe>MTj=AY4OyGJw}`MKOokOK0-q2Q4f~!5+@LfVzTa2(}wojx&V2LwlH@Y5eW
+{J^8Y77JYqu*MvyfhO@xIGE{BYm&tb0w8ZhA=f)?@JGt9zo;JsvE8L;-ZPsV};y$7YoDYp$*O)tQOJ2
+~t_8oi}*!m9xlm=Q(wDQ=8VAF>@L1V%}V=FZLY8fWO<AxvC<<h*pIc%?;zd~z`O-?Gj7-^0Nd}2FnmR
+aj_%m1^My7labjn~8AGVK^AeI80>GNI-PMZ-qExOB%0_tLlGSh7>>!Dvp%HX|N?08mQ<1QY-O00;o{l
+@>?xL+!w%0RRAl0{{RQ0001RX>c!fbZKmJFJ@_MWpjCRbY*QWaCvP~!EW0y488j+-r7S`ATxFz;M1^S
+!+`bB0Xqh&Ovl=6(V(Qd`TNnb(ll^&Fl6$*NAihooLC|_RPKDMClmXs>V!v3ZmKPh8bQe)DIONUA-T4
+ys+Ky~p6tIh$g?NeE!X?c74+QVM~9ivvcHSy%`?GZHYY@0d5ArMx{hpgnp9PAr$HAm30n$bzd(FZo9S
+*C@Hd4B6G9qEliHkC(9&6}b)s4<r#e$y2hU68U}BUVtK5ywKw9Xg^tc?Mn8f3^B&u?75_)!=0vh~cPd
+PUCZXhhv;Y0I^xQJ-2VqZPbiwiNlwc>nk@g)zg=q#m^&f=nZQthV`Y!V?KkpfA5MO@`e^SgUA4L)5<u
+kw!*S!V{F*|&r9M|3O@=2a8UgQeArK0$`i;`Br^oZb<$CXhDRlTfCI<%^J7#_VFxDdOXIbb~q88+Me_
+w!ds{Ss*p{?LMt;Z5Z@oX^MDw{3^oozG8X5Z)OOz0hHvWsv36%B4urL3r#&Yc3H&S-hTQ2b9nf9>2u3
+Iuf~3T;RdhFyd1is-i%bn{SQz}0|XQR000O8^OY7yVN$wcvjhMD{tN&B6aWAKaA|Nab#!TLb1!UfXJ=
+_{XD)DgjaFN4+cp$_*RLR)hf1wS6y4h}5AzatGvGRa9UvHrK%gbc=0=eMNhNWA{SGgZC|g;p7t7?ieC
+K@UqDsjc<$2YbR?$31tZt+-h#a)!t)aR9pCsYDkiyVc!`Y!b;qrK7;&|~FpNJ?qRk}NuI!TN=|B@hh!
+kIZKN=mCtUkln8w07q!rKB1OmTg_1^IjYg9qA^O4WVHTE@a&h(<wc!w|DoC`{BAZjO*@}3T*=}nTl}r
+qWLowrBtYonoC7$lH8z;G#beYm8`0$q5>>gxEd8ifLu+z5p!aCj+iKT>k#kH+R)lXh9EOwD2f$#@vqz
+ulfuL4+@b~AN<ot($sxVDHpEak>r;TaEC^4t=o&Lo0ytugf#^rj{e9pLhu4}2a-=FLX@$IkwUCOoUVO
+?thS_Z9ewQ8fC@7(q6Dv;Wg>eqCx0(VTYUxL^HBAfbN_3#XEG<#xB0oE0R;s32gB^`%&FnoBnw21L@W
+}5QA$?dNK<OlU_Uv<Jl$V}MCJ5)z)f<+6@|F^pgd;V1beV=EC<@&n>@6`yrGuq8dKZbmo1>2zSSmQt8
+m-+0(DPZU8uX}2yXNRmz`k=}eOK-<)s$Cr@K{JyW&`>zANIM0z_d#@fDk27Wl#o-b98{*J61e01j4pp
+J9fIf_tp+)U`(N+Rj!=l+;I9#IiAGOIV_>NuEN4ou}rGYX80e;MEbSAwNE-jGxYwNjSPK2oIxkV!nUy
+NAs`=2W8H9O?6FQCR7(f%Ps3}VwO0y7kh{LiUleK5_wHQ>BJ8a_ZfKb@(M0qFQ(ZWmX_E$3!2A`2=Y2
+cly*YD=cdPyCAzyAb0Z}j!?Du7KYqi)nTJ8<Bd;OQ6t9>brPP=ltL3dD%21<eP615E?$Re_3;6Wh<fl
+yRBsS|mo!-#WZx9a~7A>Q6^?st3OY|PaSdVpDn1f`|!dmaJdFe2P2Jh9`6>V7l$qOy`)zQ8#iGvCFv+
+U@Rl^AQ(){-Dlm`tGu9KCS$?+-=v}KmSkt@6~2={}@|%a&MGtIu-NN(5N{SB{O**%3C`I7Is2M1u@AF
+o>a|}UJDW*)|TB86C~Kj>SC`7$ojCBFhC#VBNIP=N+XeM0IpB6<)zPw6N&J4B~yfQvl~ntheAO@PE|?
+(Z7-+6hN}KdAg@W(E`;ZO2<e;Q1=CE!c7Rb=&^WUBWbE0<n58qeh`aXc1ft}_POidK_0Bc)jWK7N0G~
+SV5O!1?`-y0`6{48tXvXZ}d6qdIqbuw$(rhwKc2r#e0_QI6@OiP^Z`TOMYyBNWQe!HfnUVs-2u)`XtK
+AnsvVd{B-0s)2A9W4u+Eszi;^4DqJNA1`^!Jxqt&h?A7`Mvg3W=liti~mMkyE!A?68@4XoXkuT|E{KM
+!h5K)PzL8Exd(m*#W-y4e!~c&;f$_H@Qp`i1!oz4{CSjPTDO8Uki-W%OT%=Oydcm_!A<R4HcK2J$&h=
+P#Exme8TW9NFAE#Ks2@X>`8XD9Df7?j{NpfeIc(Kn>vsiefqe+BhcfS=<*%GcNqQ72xLUn<!!%FT_J)
+CN`H+kCjS9YO9KQH000080P~d=N45Jl@sS1q0J9YU02KfL0B~t=FLiWjY;!Mfb#!E5bY)~NaCy~OZEx
+E)5dQ98!KpAJ54QG#VFQBZLy)9v&@?TYtVNdv0wbMcCK9QUlpUk!f8QPTVo`EZ18qM{9}<b=9gp|&+(
+XZ`%2^!GOIvD)V<z%KY0LPHQCVpruD(a3dOYP8tjMAHzB5WT@0q&274o+ER3<98T{NF&ZAPOx?N*2)i
+kBc$rCBSQ+|NY@g9WG}x#y&|S-J#mgp!OK*3QNCU}u9w<+ytzWR>MvY#>oGHB<KC^!)hEhl|Z>Ayas0
+7QbD;J>5*9Y22c2(@g($aq=!cIy`!L5+9$wn=)NWNj8+IL&r;@ZOJo`%Z-ZCimVj}X~@&5&{K8`HWt#
+LZ5gEPA$+-r{DRK2kT)ul1tj+|IX6}-33@adU7Vb~K#PuE#m9%2C-IBdm+XKA<Bzj(J{zZ(<Cpv6*Zb
+p(zXM!LaRQC=b-*Gt<IIhPaV&X`X)wWMNyd#~@7wcSKsG<3oi8jVL8xx-AhDDE5yK~iIVO>i!p3oEaA
+7@84}C_4WauQ$Y}~ufj_6Ja8RGGxLgW0ylm&S58OgX)MwDOyn$u`|#?BN^nOy*b2VpE_hEv|IuzJ1Oo
+Sv1y9aRA`++xav5~&^X1%2z$Xfg&9f~R3CF=-{>A%ViO@bt}z)(WHho@XVv_ld99)A{N1sCA*?OK?U`
+S+OoelMz#vlv;zdF_m6N*)w)%F)s>AaJJF{6{->|uheasI4?3+TRZs!1F#C7a*FdoTINMK@YScm2t5J
+jk&T@l!M=AA+*;4+B^;*jWV&5h*Dt8fbJIZL^Khs=)s`u&t@s|+=HOD7+vcI*np>sKK^RN}^wfSZ@m6
+ih=Fd#s@a#L5Odo}U|53?YZR-7cm-3miSFrxhh2+9SWt8)KU<A?VFl-<zEUA@r&O^w?@F~4+#l)ORHN
+@kvi4;l@o^Eww?<g9u0n2XC&ef_hVQ&d_-$;E+m@s4>4&SVC2ER%J=r0t>GIpyv>i7aM3AjxAC&cFC5
+Tj8lz%Y)Ji3T({jW!W#JJ)Up9DNI%YM4QEhA%JhW$RwPFEK2QfiB_A4l-Yz^)4TG=@ZVCnb}r2y-fDT
+yG2852GZXQXLrZgo}FLUSm@a_7q@8If$J~AbH`uXfSRzGpn<{?w_q8!8-#U2VDdc1=)z9URQ38eJ*wj
+E$GMi-+8Y}4ko`2iu40M4H1t*P9RxuIRdZy|T(YttO76ahGFAaO6S=SsDdrmfEkP!0rsh?R8LB}g6JQ
+0Bz!kuK#GDC)B?g^_U;-FOoD|Y&BRxE^0zqiw3r6I<!bO6dJ=5dG3?qe_)O_yNREg`<sd_{d%p_VMD7
+Z>(PFDq?Pl_?)x02<M=wjW4F>*sWJk+_8gCJCX>Np!W7+fb1LbQQ!ptRbqTNWryyHr}eZrarJ<7v04p
+>EBhNORYU+LzKMQmw*C2Uw56g3g5mIzBcl3DhGL9uq@v7S7dvv&VH^sGumi1$85(4&fs}Z-rmA8%<j+
+M&v0R@@xmYO5;`6p6g*{4^gYfy1HL@Jnyy_dS0D%44$y(><{)M`}H^c^ZU>4&k&M&VfM`S+;rXTckep
+xZ&T3=5Z2FKY4BbQ{m`wrmfdK177X&a%4t)OZ52|`eQxM)y46OM@Px5(sk5P<i6P)N2cLq&gvc0Tf53
+|(6V(m)Cr2!MoO&`aB+^_Mh!K%omX{{<*Yq5EH85l8&}`|_4ly6n;bG{>u(OA5L$WKJTm>33;OK<l`j
+OiMV$#7QRX!A$3`T0Y_ZUtrCy_8QB3*5VCx3l4`{mkim(Ltc?Q%RsWITF~X}~M69@2owQQId-?Wluvx
+w5bWz;(5Gtjz-#Vr%_WR2e2rH&??hq(1MPfY>suuZ=)-gI;SF|8UR~_ojRF4H;&-#Iz&mvI~@+xt~x1
+>~!pdkf~a^zQ>nJq-eM)!Sv)S@`BolpVAeH+9^Ch)JK>4$D=JsfhS4wlzw>ejo5+ub!Gb!)$cVC!2;S
+fEQa8tDjj1lO>K7zIb$<r3$+6L?u=37f}t9|<RarY$hH5Gq|z)@nWYR-)CdeSQjJ^9Q`%xFxI?xrO7=
+*7)iBv?R*AS~zlVzQ95Wya*x1XHr>NW)o~N(>yQ`_ZKe)`(zxrN!;{M?_o7mNHN#UmKMJ2dI`UD8%hA
+0})&9$m$avnJ@!%8-r-!hfseSv=h+!*=JT=nSgWb_|UO9KQH000080P~d=N1-nT+Zzc00CpMx01p5F0
+B~t=FLiWjY;!MlX)bVi#aT^{+c*-v`&SSS8f|5?$|Q3O(7+~{>~3RcvxDtk<PZb~Em1bpGAWQ$yFI^t
+U-3g<wtJA7!}^f6rDCz_RlTYrQyaZ!>vi3^&WQDz$$hJhV|-(E(>bvYzZdm+u}VHlE45lI;sfb~aauP
+vJ+Ma`7F)Shyh#hgubkc8{{8mTea<RT^R97gsoZ+URn-Wy2<uwe7Ny>|+@-ajZ$JI~@6UI`>(0r>rnj
+{=d+yd&{w8J;MWeS{skTXSD`>w;D90W7&`Yl$g{rg}Y<)U;SF}cNjj*=Z@LupeH#xgir4f5l+Tiyy3w
+NSvh3W17Y=B*({w1H}sVT!M+e)QGzvW%{xmY-Je7j)yT=JsSjW&GG7cbZ^+F7P+M6a%efvHRwqhU(P7
+3B6JC~-S!b0$^UbQQsNceGCIKawazh@j}!tM*Tdg?K7O>)1#C2Y3c^(zfh|ebP!S77K)$t*vN8>DI}u
+b=h!hSFI5+$cQRuHE)^?FCVg?MyOJIBW@^c+&MD{7qx6e&X!4LnftA>g<lCPYjzN<((2l=2c-|}utPH
+S8p%yYDj5DsmE(a>SaIDn>|1BS1#7##h{_zTmB@T8EGe{I>*4K`gg!yQu+9on!rt9|xV>dakisvPHB)
+-nI5<WhBfu)vyPWL)pc?{-;W0&vG`8AWxq+$WjvHP&${+Z4;E18qe&d65yC9qJiRyYgMr&`_m)?pKTn
+W;B>yB%H(*RWPpm5^Jt=+K|t1Qc(UHa^zuX96wxM7X7ZgoymyW>_^==$<C>w|i4j^uGAJnH1{1d?kbf
+ChyZ^}xR-o`xs%D+M8Hi|}qGIKx6Z$vR}lL7N9V&WVr?ArXK*aV7>lDyz9FB`8WeQaZ%Aht(8hMo}i~
+eV7`Kq6dVA0QFp^&kVrgo0e)d_?i{o6|qVlWq~h&jQ<T;`O%Abo?gy6zpSz1L}Ey!#bW&)u|Qoz=3L<
+LwEpE(6cMzAe~ID<n@xxMfF$;%ZlyQhpe53Lxm^0c|M0buAQH-=;{>#^VGXh?X?LrPl8!1CI8rgK*n>
+E3G&g`d?NNkWxu+;XOdl**dr*z$l_k|1F~~v(5l459HG)6-G!azYIP%OQCqr;ojeHPnCo6OfR(8gsYG
+zQhLmwG$7rez6EBCVDrVn<+3dz19)z=o0Lf-rGDz|_q)PGVG`a{k(9hD$T(MQA^g_XT7-sAz1Aj3cqu
+fEpgCMm;4KzMK82BTgV?ACd(EOq1pn>yr@fA_|}tE4S~UeGhx<l8fg9hrJV+X%F&V_5o=UrK-pcuP&?
+pn74cD7Q;!BU14iNHgM!8iO99*;kUoPL?}F0vy>vDTKvSi}ZkGB^rLjUxA)gVk;Ra3+e$~Bez>3K37R
+orwkU*!qdcHmU^6%_$Qjx2j(xp0ahPVX}~Atj7OO_M)2y`BSD=C1x7q^`d#!%?7#R<hY>@%^?F6go3o
+ePY|&I-zI-^)w`?F=N`3m=k?-W4hGUY(rdc3pPx3AnjQM&rC1F;lWXnB;vZ6O?Zq8y*L0n7Qr;!Y-4}
+;q(&23&Ryv4``VK>s@TW?&D#cE=GZ_ZPPdX&1ey;}z$Or=sPP|g#Z29c4zUytA@+@P`=YA=Z+a89F0N
+LK{<QTFpq1LlI!P33j&c-sn<kCYLykVUbX=cI5?{XVIqEI|tZ4J@jVZ@Xj|;tvCS+FMtQ^xeZH{}7cN
+CCLv#$pX}xU^^HeXWdy$^N{=_eSY{bT%!50f5s%f9g15LgbU|`B?Hz&iai(Cvjcca;#hNi@|@4u|3u8
+5OWMG-pbMxLL!$VgMWjNU5QHt<ZKzhF@*Q@FgfP<@8mSsJ3frqwbOdOcV-8b7n;4axKgO8Ubw+LZlm~
+NIvjM>gMPe-=72O^M1oN@Dfa?bu7p8r0BWgk#^$-yGDjl241>EO2r*K5PwE|CkD~#nDY1oMKNXr3ZOd
+lAQE`T|<_xXylnu5>|3O~o9`)f2|HY<F8c?|(RtM>P+;>3kL*72<XXI4wLWG~rjc92Ajeqe7h`ggn_8
+2wj(3QOFvcOmr`QOyaKCbZ#1>m>@>>nZy^eQVo_Q=Ko^yGJc6_M0%eKfcHFUs0S^75<1b*A4rz_;FfC
+qHRm8Sw3g*Yxb&FM2IY3+2tk6(GMJs8#n^Ytxne9)p;}Uf?e4qyXxiEuA195?#pyh5tBx`oT;1FqwT1
+@J`p3Nn{IcM0+e|=t*Dq&+)8B^D3G%HGa`pE^iL;q=rvIr23527yr(widtEw|5{WUaidW$JA<nDp-KV
+=-M&|)L(&uM1e{@U-_JZB%cxD;T;G}LHHKCaNjpM|C8ZuH9&6;*b8Af%cM3}v|I@K|<F_1I!B4DFF*=
+nSVI)$JS!4GhSM`D9R$xyB6+!gRX(49&cX_ureLa-dE3&+P$Kzks4<f&^=YQP*hx<R1L{lU=ciQ7WyZ
+oBhS1nL1sO26YWfT}b$!V${0TKP+dB}HU8BV4@L^3zQitOf+mbn$U0ZbBp$vwAm9xbGPLUa${4fg@`0
+n<Gl5Q;O_Htw?DXXRuwGRr{Nsy{<Z(pvN(b_{C$i;Vsjh370M&AL%Xvk<^|6z^}g<GWH#&w0jNvetNU
+&1+vMyz??o+eDuQ+BOM7j&3X7*jyf+Uducgp(`v4bX2`Mg6B{xXINo0tMXY$pDo?JNK3-K5Febi6Bj9
+qw_nV5p9lat@TTC|>?EHgX*G)I+;r|b(y$#R$oUFVwJiO;~CY%}cCc95R7p8`E7m`YNOh-CjTUY7M`K
+QCUu)JD<lO@K4th%}iMaXt_EuX^6VGzhH8-9j9?jH>eGNUef!QFT#{HI)g#_jP%b)GJupU2=kyd1S?>
+Zxmg`FCa<2uC{WFAGDvTn1UQ%+8dG_zouY|3guf$FhkNRk&gt@;nX<zN?Sdk0$!At#AOJTQ)>{GAo9$
+q?hu_lFZ1>(fp7Q^CI!rOiLpbDY?E5cY<FgUdNrMF{XJH*d?pzwhu$mRK{C!c3HII?5gl?c=mOEm2e1
+Ivm&Fj#s2_MO9KQH000080P~d=M?a&4R8b880PrmU022TJ0B~t=FLq;dFJE72ZfSI1UoLQYr5gQj+cx
+%h{}n`v!1C%TYkR;Qilx_U+U*8x-LSN~AG$)JCEDg9iykT2@rwTM_ueBZQD2VJ89^*l<m3DPBG1mw{>
+zIj7l!dXXTM!tvuG!@k*Z*+DoU;L88g*(t8{sGb~agRwP8uJtjbD@Bw=!cZx~+~l~<)m?C;6MjSH;{<
+$vUAwUWik|5V05nRQjld}4ub<u*=sqR5nvjl7FDY9~xw={%PUzulH=BN#W#4kz?GSTF8Uu`PiH<5#@m
+*_GOCfYfFdZdy->GHJFsKLD(9J!cD^-d5WrlX}il;7nqw#a63S7&D)+;g8;wT8KH|bxTkZaVJe_W|PS
+zNf1WDF4>3aE;Z9Ro32Ee7@a0s{8>pYHliq}A7LtTNobhq*cHRXA89N~l7er5>j*K;xiRdw)LdKPL~8
+MeNXyxC!VaLWU~hc@IG;Y#kV)6Di6In>SrZDf#IN-V|1bX1_+^&mmcDldqJt|lu>_IJLY7Gq8IdpDCi
+r>8ex;oN<Bigy-eoV#HA=@yYN<5aaV=G4SVy}8Nag@=Hl`j4g&S8!n4}P8u2K+h3Hm{(#BRe`0OyxOx
+rPm1T(YO}KjVMQ*|Yc=%akz1v}9|(6O4c@HD4ju>y>iJfY-b%brfz5%xKLA?z~ln2$(R8te<}zU9c^X
+Y$L}xBM!$k!)Gla0Hgat&;qQnpoMWQ`M93#AiM<qg}ur{4G)zZFf$|Ya)1+cfbMu+2@}mmVS>e=l8(X
+r;P#B&JW>re#-yXc<MnZD6EaB4$?iUZnt!YZk8RQxqg(D}=CSRG&9mH&HzLx`8VD_L9k~f`0|hG-s-P
+hygO{LN$c1S~nXC_p_goj?0ix*@FL0p;PHy@fy$FDa8?O+8un2}*<6L4nf|6Zz>9C>o?t@?Y5!edPL%
+3#QQ30mj8;FdMbo_hXV2fY^m9C0D$1c&l<_u?Eb}S7!h$P&DekSrR350$<N#vFxeL%H_-wUy3N&wY}l
+LXmN%@!i%;4VNY%(`??RU&aEAqY+bR$;(!{3MAxhJi!m80rFqF;M&GU#+5+Yhl5vB|Ue%aAO@yFqR4)
+cxf@1G^E&l=1jz^m_Zbadl*eGFz#KH>H=P0fNi9=sw$_mLCh#9q)B<$4#;yv3~gn*!VQ40xcuA@K=_K
+z%`A@N>1dn|X>ka6chn46Jq;HjX`uhg#P}5dp=1$_0%|V>uKHy*Cy4O5LtCG|*8((TBMSi{19<>#7+B
+D-tU-+vI80<8G5k1J7EbEAIkdt-MRYnyfE#K^(JtvL&ea}ld-hNZ9t>&DhzBhKv%18SJZ>6Z;Vv!co(
+9)TLWJF-aSTI_xXHaxVR<+K!+_0$ye&xPtW927lWZq+j-}J7CEf+kdf*ZJ5jcd#?Q(VSzdjP8S(Iy8!
+)I4P=GBDDPNLCddvnZfoKOCW>lFm8ufDq7<5L)Skq<BtAi@7`defq;m*RAx28c*QLd`)3w#vv->4QTG
+2S$7zd~otzU6zpT8fHoknLeea-W-kCw#;ZRi)T+6&}!_zU`eLIAF?Qyw}Pe5fB$`{@=WMq-$rxitmHB
+eTm}>vvZPlLVc%5|9E@p4gF8k6k~548v~=PGel5U=jHv{B^yHg=JtHyi*9R-=jerz5V|$Jf4tSnbsmP
+wYmFu$Hn&%f6kp8c$MVzY5#rCk}7f=&j5JYo<5NjcnninSLP~C~u3JM_|L?8F7RS|p#n20;R+2+84*M
+IP0r7-7V3xEUaW_%0+j5q8I=w0OE;t8bccV}@g!LsmUU`@_bj4)W7Bw<_>cVtH=MZ-5Vmv(Oy3S%9Pp
+!SxE9y4J|SrDWeWaANH5rcN*2Pi7`MXq>e$&pY+&q-Gx=wW~a-Xo2)+6>~OxrEsNR+T>>x3F3e*&D5u
+ZaX=^3=Ck`&2{^6-Vi3K(i&VxNaFine`HYn!q85V6IdLmipJ=POWLu%y>M_pfmkNsIt*W-BZ^wi@$4E
+<IDn)eOo5g%&Ne(pUbwsv2#VQRd&xVe5+Ds&@h$N0#p7=w^)FSb@)w}5-@>XF^X@XrMxa`KjA{+DHV4
+H10!A)=I{at5eFRE-Nl(qf#`3`LLS@0GkEeA+f?ehv^3LlwVR7n%2ogi49RZj*doug*q{#`bgM|=DrR
+$v8_QIc)K=xpXXdPxyJL=p5gNM*V>|i3rkXj9wZ2FSWMwsp3WOuQP$c1Q!;7dZVLq&K0s<5-8qsULEv
+z`Yd-odtQ^+hvyNVW<&oCie8Is>a(LruXJ3j8p~HbbdFbBArpi4^l}zZ;Mb3%2MzW-%6h<X%a5K3|)&
+-&-7<z5=s`mtaR=29$4s8uWZYHwyr~n;c@^jC)do1fynAfl8mtzXYwWL+%KR-ul^KRA|-c_rZmO@T9S
+JT@`;;s-(QQbVk2XJN(UyLk$PKy3BD9T)SDv!|th;YM>tkU66W)d~N8l*bp_QA?FGGe>y_dm0iQiy&~
+^}q}kL(X5bZN2?{4yz$u&Vr!>lQkkE1ono*=svmYbD3cJl|L~6z^ofY@qJ)H$KsRhJxpI2gZ7^8#dIr
+unG*8<Rl*p?HIfsSgM@zM%`iA++|1A|I$!NFjra4X1pH;zM9I<=XL517878lm+5Sb}<=t^+FOwZ$gNH
+cUt8x~7$N;KY!5bAu3WZZKwUp%^Oh;pPSecXRW_7!sF63&Z=7drymyu{_s;XNRw;%0gxnvM#7oi~|e<
+fPVv15=Igv3P;&$f4Cm=2pPWsvS<Umb~A`Mcp!m^C$GhSk>(Hw;z_s0f2ScClvY)02oOq%C1gvH^#;^
+%HZ#U+2yM`F_8rfGe(<(J?|~D;O!A8$g5cKmIK=GD7Dow67_T}-aL0~1Fx#t#A+(Bd%lVd9A1JZCY9v
+7f_nw;&>aBonX;#axg<!hwI{dUM`q6?N@+8;9u!POYZ)4Kzb2)VN*x5Kz^%Ih)@Knmi;52g)&Bo`4Cm
+xpc@wrIl;LAR{0;(zSj}!ggyF{S-+#Ky_JOtM^AI9ee7Woj`Y|dx>RNq7O^{v+w^q)?1@ThWh^ut!9s
+1rxIW&_(%CY;KwhX|l$8xP=Y?Kq-oWM&j`<}8vkjQik}qWw&6@q00&(>Zou2MplQQJchndphI_b5=ri
+P&%hbX8PJEhbR^ocv|KQt{0O~Zq<9NAxSDUu4v;@xleL^9*6ur##MMf!ZL7$7Rb>ycW>h<)^?yi6pkR
+Kxy*<n)wv1F%|UfO!ib)Rbx%Re7OWBr)Xu4FH2oj;Q~{Yx{hPV;-qQBrn~(P?R)?if@XaQzC2|?;eaS
+!%N40F+NltCnReMOp_K2T6#gtDq<59~!>NNW*XAeDX+BuSNcI>gsIXS?&M<6t`Eo4n!%b@_g2j+fU8u
+d}lYmo~VoWKnFd>d$_)A|2M5;2x64I{W1KoH^Lg#>`m+)fzT?IZ{Girdo@uz9argOtwetxu9TUFM)`8
+&2YW_32D;MVo&e9v5Q8bXqUrDi`2zfVi}R6sl;t0py%ptvgHw8z)plpmf>y@gQG@8b|e80O6kf40QlW
+qR{F}|G0q+##NgGy@G*TBfjpC$m{0NXDN#;Df!A=QX+l)c#yk}3UUDy5i4%j7z{RIg%BAOx1|aM)o+1
+%e&+|~U}5UgAZ8SHr$fudmU_sZPmL@vh4}pPlW(7Vrf%LR@Va<5LVoV^*Pq;BW@1|fODXi{8rl!bh7%
+ZlP|u*;hEM~t#5X`Yyki^gJgp_#Zuh0Np|2+%dP0kOSQ$a5uRT3`k1v3ezN3asg<cW*cQCGdR1Zbl8(
+7_3_6>eghf`?zQnYndB&ogE<?jz{GElL#+9#&Wz;|3)fv5Y-9$^g!HQXry4YH0WRhh8s24}kslU(dXe
+(A4iy?*oR{rB_wCCDT_vZJ^wHBUsblUfzsTMjKYS7@EsGdI=30k2=a!?PavGZHv-fO&h(w_BkZ*K~;A
+xw&SKk$INcV*&u$)bbWGvxngLM5^VMxY^Xn-*K5^?`pUuu*vY|O`WiPWRG#Iem{23+d(wyN|6M3Qc^Y
+`%6VI2CxHpYIB!d<@zwU4A^Z87v=Z+lc4~=7_5x)5=IzgK-oC>laRp*b*JK%VqUURf&axj0+k9brr86
+ieya2?VNGsbqlyse~FAn@b7!omUjoCzqLqDysR@U5)tS_mMbM$&~uBZMKq<@QGxQMcVffy~mw$JsoOh
+k~^z1z~BwlI}~BN5qTG8v0>qcP4Ij}HX?R?B7Yno9ecKs)^6Nq-x_^LrJyvmlzqqj;ZMQuIGH!Cwxd;
+Z#?98_7Sgrc14wWc4K#f2Rn%f8F4AU2NOCnD&80=ce&JsC4AF+?m!;vum6e9Z?Jvj6*l%C~u0nY01k@
+9OJ+jP#(En!1Zr$11?N`f;(s5%aXa?>rX<19Y~(r0dy0u_+OlBcYq#prOgeC_EEb24^T@31QY-O00;o
+{l@>?qnThqw1poj`5dZ)Y0001RX>c!gV{<QJVR~U<axQRrl~-GD+cp$__pczD1(Fh3ZQ20?0(cnKbwh
+z-9g1x)Za^>+WpR;2m84>)zkcUXqC{Pst$r~thv)j8i*h9^LTTQbR&h#+s2ZsZVfR{=t>LuWZ#MmTDG
+vuxAKX@Iw=%qHa#3=(SA4U%Ab)=R<>M{+jl+E*G!eCtM2k~W$w#h}R+Xi=Pa38)-x&3DyCLw)QKRn4X
+=5n})BQ2+Uhq@O8$;e(ns-V`)iLNF0qy#`jg`=dCTWc*wWC*+QJlvl6S^tc6A+tXBz;T=LRiM+Z?3eE
+^;=mRCCia6lgGN0EE`of1vQd_2Aj<W-NzN#IX@-`Za&zvkkXn}5P=BD(voRSer8`8Q{hxn)Y*u%z$Uo
+=ssekE3FXd`;ZX5L(t%X5IxV^joff>}8jeG|$DidD+rR6=n8sggx6!&7mib6ymlICcr8@zGtb#lToQY
+Z(N<+=dJjST(ewQ`){EDdqIQ8o4;fSl=NULevfO9%m#01|($-o8}EZ+`KFUTKHrjRxHaYtS!KP2DB<W
+2I1WRmMTFr;9QoV1#2nk#mwcnxq?q?R=;1XoO@g-BWHar?v%LK_HQ*^v9z03)C_gr6GOp^Wu@C5ZC2v
+GMO*kqm4^P9WyY7!~@U1V2tx0mq_eJV)Tqf00b+6Mizh&cb~Fe!q*(zna@Oe>lVn%~Gb1R4G$t0EC`C
+FJ8PD*6&VT4JddIT8lg_j8spAbr+xo`URI6*GANq;lO#7FBTOF$OfFc`jS)+8Lq=%+i8dpgv8qhqtU2
+y($)=2A410>@*^O1f_VwHLqK)7Vx5aP7AmW=qd^-g_P`;)YOpqCb?+}C3`wO@JT>Lhgj-i$Mp>hguJB
+FNS8YvGSyjx&W-!$~zl%wF%y!PYc*0=u(}g@zZ8F)K-LI_Fe3YI;Aj)#4L9grRy8#vYC`?-PsK7YdER
+G@Gk5o3O*(7<FmUIjP`>Zzt*?1z_CS!(AMEq~=O<R@-i0zl)j$HfiRz=Bs!$j?g^0+&*0DD#tWGxnP3
+P<4tOmHys-Y69w?}IItRXDs`Q1z__t1}4!Sa2q=){{#e1>_R<!y67^dsfb~>;qq8YY$Ya^nrS*7_Nsi
+_F2s`q-$r1sB;-E5E#5g3DD#Szz!_B)R;S$I#_}gWiAdqvCB)pNn6FEDNXcW8P#{gRjj~nZ{2{5{hH`
+wz_y~%9T%jKCKo4LEdKiR!L<UGW5-ZfNYEA74!Q}a@20meU&1ZRU|=w-*jwp;XQ2p(i8f3b%(5a3I{v
+}3i}a(zbpWQ~WHS03#=v@zzp(s#JAg~=Qf2CCL`MLrz9>RnN-hgx4sEy;HHUH0vP~e=!r+Mx=Kw^qo&
+5vDm1x3g;E~k{`GkSRJ0el$qznEnS%G5r3!quzKrj%_<o5pV<qi%%t&nwYZRDUP8UvL3o4Zw7m!DK<i
+oU@xyIvaN@{9~GeBR92y*Q(kB}RgI{BiV{#@<z?^8grX*9r(G^7jFd*53w+LDx>k1QGi!cygkwVFy%L
+pH~(rR?#|B6v@6xPbYa=k9|NR%Vz+M*R*2LB7ubhv~V2MLKTGj<nwWRcO8A!S7CA$eTTyUaG1sxz%z1
+K;sow+lo}|KgOY6%-Yfyq6`uv=>m{W#IbltMgCrpC(dA-{w{8<H!kLNGgzo*U%xaaw@t0OW>4Z#lJz8
+Lka(<6-ddV={dICh7Y<|;TnYzGJIJ_hAysM5WutJ=a@(2Yvn$%e+rML4E(W^0(*-4)9!+|zR{sUv8`#
+wX{5<JH}^0##&*g~9dqW!B8O=+vJHqqH^GGyqps2s?L92v=dbA2HlYAyAAKz?rm{Op12Y1xG_j5=G;3
+72{cjUOhOaBg`#@ZNhey)T&M+_T0#gYK<*5i?K5zA+07!sDMV^#zdc`sWy^$(wj85kJr$Q=O>(bFgpD
+(dJ)JO9KQH000080P~d=N7IP3k3R_j0Jt3h01f~E0B~t=FLq;dFK20VE^v938C{R#w(;G+g1E&(@~tf
+26>T3}ERgh?^a=z`kxSbL#}H_VvY3@bg{16V6#4I+At{Oauy=1Cyc9K@8P4~Naw#g3Bzdb_$&!Tdsu5
+BXdQ_rpHB0RCYUTDzaXRt()IDleH90R?_bgQ0P+hE6IihWN6DMa@XF|q`KgX3gGZnY8Ecvk`)KIAyp^
+Dh;#5}+KxMaVzOlc2++woXvH7`|nAmgej=}UraMnqOxGLPIj?EzsKk^eBMxTrsgT1!!`Rx2O^=eZ`2B
+ZlK{9|79>8i;!Xcu<Z^0*Di6d}8_svlk{wO)F5v8o<($Dn<UzbvWi^y;~9ZLqNb0oVX6mM#g2g7x$Zz
+H6rP@(^AyT?yIz56;rSqnrHkos3P<4q0qYV*Q$F^s_2#;INCuF8BD;s;fCd4TD;~uNkYZSJVJ>){u4N
+e$a^YJAmR7#pDwt98}&@wk^c($qzILwtusPvLK~1Ft2K&(9NUuz3Wym&_IBGXm71p|i%%7;SuEsfYge
+hLK(|(co79RB<+J_x$3Oq+!-sW`j_1hXu74iH6p@<gQlt^FmIFp6Um}uAda95yqimzy5EknZ2Eb~O#9
+++o!gUb_Mq2AJB?Al6;<QW@XudGYL3;b$ret-9%eYip@+J(n!Fqj|ov+yip+-u*h5um@YV1pBx_>~dX
+hepI_2Q`*EUly|3xi*NL8|}`eQsxG<~u;hO44~s7*3_{?l)7wdX9|dn#rUoTa=<xK+fwd3?70tc_1IJ
+khh@w-EjT@&>mBZmv@}*hf}-58`zE(q|$Gxwt;{EHXjjmpLN`O6L2$kp~36eGsZCIbHD)AVELZ4y?gi
+WyJs803uc@OgM{K*$QMHGE(${nv&_H<cx{mM*Z@V34%0vau?K|LWrVnT%5WV98_NtD1cca)X9vh$+|~
+_EpTZ8s@K(#0-PDb0p?d~AXWA}#QU_;(tXn>yQI5SEd5~?LK#YJ`k<FF64?wZ$lW6q@jE6nrMK;v@=H
+_?PNS1Tl@8}%nT2+O(BuZz1=J9)4DlqBvl0D9^4Hinva&mQ>r#vC`nl`+Fqn;DdAZg{44m*+2n^|@*$
+0qOz&l&{-2*=<^92_~aTu4T`o1-CM5M{NZn!ypY5qeTRHjo@SN?NBFQ!4rb2G24gB!}d-#q3Opl~UXv
+`H4Zv0i=-rm(SmRBqBF!iw4dj1yp*DSs?Zq@7EpQ#=}vQkVZimxeD>>o+nvOP)?Wx9bfk<AoqyFkN+M
+duLlBSecZu|O6ZQ+v98R$$i6?iA5b2KfXzxE_8JvxOsX`J0D(BsU>JUd<dl|TW7M|(h#_v&P<_me{@o
+|hmYMN444J^ln1$dTs{trZ+6vC2ZB$&x@2uU8u&tLk^-OXwWpw7WaQ16W*Mzxc`<*#1_^GIKenOwL+K
+H{;F?|IF*TFiSwT)YdoY@=q{w=`p?=j&cnZ&Xt1NTT&d#ps(mTY8(ctr~^y5jQf?#TCCLS36P5DB;;t
+xw=srcSj1Ij3!@ofY1?ib2rbW9j;L<mZB+Miet{exbDn*X<)hSkdDPVNi#6?p#p?DQ;9Wd6$?3uHi-J
+qVtF~vp(<T{=;EGQrApuK>+4rHcl5*X0NNX7tGG7iwyewN_C5)ro8sl?P<6(Z9j6s9ttL$%J!B_jjPY
+zyEX@d9SK3J_i8<jb@QMwK^%<q9o!@m_#As`_9};P*`*^N)p{{X@j4fCBMCl1D}%x3UfnBV?ihIJ9mZ
+(X@mU4k3Q$YYz_d)(ZX5wbbMU<XnyThfz2X8_()A449r>{?Uq~h(BKOI8?0|6LJcnM#MvKqiefzh0lV
+K|<FJoF~x8jMFhDOSkR%1_GsInGiwg`oPuZGsECp-UKh8PP9JQvGZQ-@%;2R^vu!#(S-`myuP>-Ds29
+B#_tc7B?aj91FVp0rm1nS=@`V%up=*zQ$(mG`uj*jguEEV^+AfZCXO23`Y=3$x1Ca_f=2#&xc4b}zPg
+1PwP>Hex}9DJxeb?5j{)K&rK?U7<c9h3R=?zk&pon^rGfd>`PoHh^`ioaMi_3jyW)zu$+7A2=;}&9D-
+*>~unjO}~lnR5<ZHdn1e%IW{-|Vu8befMz~Ut1bkB-(K32x7~OMM>F%FmirMQKrwHv0jtHv%fkoDJq|
+8rhsMdo)xryO3P!W9q|zUGD-E=guOrc&I4sD@p5V$vSd`}i-t)c#JOFN2JWo~AmCEO_YbcqTxeNzsf)
+91Y1;^CU*;-_Z_QaU~&O-<>aa?VLbL(`u0fFOET$l_;2?0layQ#1oW-|s1Ohj+QP-mP@Nh8ItEY+Pfk
+=q#e%|L10S?<PIF65adW~LO4a7OmJ5XeRma48(+^UiS&c4q-0*eDZf)GVa@g<P0zd9ga@HO?BDcOLFh
+LA}nKk~s_*xin^R1tkpZcK!@_8t%IC+_V7x6`><6n}Qyh<|$4I`WFUia(qb~TeAQIH;6i(F&zpCRep%
+~in(SL#FzDK+Ty@b18`(QVs6$GfW7^9u->!sb?R&CHOzP9A39Le1~gtxDFc}q0|S|{r{-;0CY}8)j!L
+1w-PrL!IJI9pi;Fezp-QY95J0tN@E+pNWE43(%`5IHBS<!5XQVqNyP5_Dfc@rP-2vRA;}qQ_@`W3X3C
+t@R(`}g1;wZcJ>wlS<4VbIa?UDTZrymrgnkPnrRx$ZaA(TeEJAB%jh=Jb&2nGrgC{^sX+hEYt&jm$Iy
+5N<OBTH$EAAd?+i1t*V&A$-b`SRtneNdtTQ%8Tv1XFdOdt?_OpDZD8Aw$W{Z9!r2DaM&*(H_?^`731i
+&N*oZ10K<1!fFP;z>v?ZP8q^to;rgYYeG+0JDQR11)4`?=3GwxT-&)8ie`B_1(qJ52s#h4#pd+;0#zV
+AP0Sa8-3)f2S{-Ultor(yy9vI-7aAlyHblSUoDP<kyH9t@j^qbQ4m;2aZW;I6=`B>V=Hzs6;+N=Gt3#
+JF+^5KxJwvilVfXl};6o;e>}N8EGMI)y%gy!S!lX3rnM27~hV9$|XZCg_8F;7SXVy<-#zmc1FAi@&KI
+c?{_)i=gaTMcv^?y)H0|XQR000O8^OY7y7@M0oj|Bh#ZW90i6aWAKaA|Nac4KodZDn#}b#iH8Y%Xwlw
+ODP7+%^#YzQ01f^st?HP1BDD3x$x<LYo#2`eko1iY?npypkiyyBA9SduL?JmS6Vv(xwXsmNc4qX6Dh1
+bcK>flBPv#TE)_o@M9yDA@seLb!%AKjW?TqzLsUlMd^l8yD!6zO~GsC#)@q=1?p~ileC7{+NsXDZfg2
+DZK$bYqK-x}Hj19IK@dJ;@)uK@OYueuqhvkG<?<|QNpmNR+EOFaR(V3{iHTgQMDxewC{Juqok>yfvY|
+>dwb^XYE2hZKxtEm8{ATCEG!^s+fr&sYtEtxHcc!vdaaxB{Y|-6@zz@YiQ&5$G9p_xRsl?4G*2vV&X0
+;Too7Y*zj(~DULfjvtmRFTAjrXbhZ>=l$WvJoV*2)1)Ym^m)J)*0jW_1yRV;KlZ(SyX_&CSi*$1ZmjB
+fpdumF7mO&xCf95KLmE{mhv*Tv#20GSDz+!~Cb<D`0n2NRA(K{0@U{hXwIMTDE}FZ9{ZPD;k=844sN`
+)u_)S-a%ZoBF*ISNJSn_R`ItFsLCpSV!`P2Att(#=Tw_qwr2M;tu>2DcFuQ8lLa_g*E6#nNj-o=koB3
+Htm=b|F^Vy0Kx=kNWrN1G7jm2Tj(ZR%sS{*v@<DPDc6+UVJ!5QgRpte2*y+<Zxu*qtV<-N!6X6r_>oz
+8e(LD6PL~BBtnJ_?rmT}-LZEH*+_&1e@5L84qO!z8c%Q=V$)xEkD8F9BrS8waAxM86uIa*+k8EXs)|G
+0mvlvMH5`FHZ3Wf*3=Xzjs<sh#e0Cugbzob4rLf;Y0Qb0Vba(>We-|4hI^xYc*?7xXy>L-9nJ2O1c5g
+}M8nZE|Xmr@{79a3HUI(scAhQg~$RHcZI2&a)L`O<UK_l&}0-;*;+U1uBpDJV@?T0DG+|sIXY8Q<(Ay
+%L|m59lp^jY~I80uvqVV3YJq6LZV=fV`+wLMFF|R@e{e3S$I-?;A6F(K+VoRre1vv*Xuy+tBO`B%0(g
+9_z4{Xm`kb>{JGT~6N7jmh!z)<p3&u^--|8m=n|vxx5AOGc_Br%LQV+N2LP2GuB!QJs`d1_+}*Hs?XL
+8TeSmGDt0%TMVX;eGr8`VRaiN0@rW0)xZ>C(f=2NayltQk%ViIBzol>9!>|JBRNugB?E@%R!21Aoz7+
+T_4(w8Fw5_&Bp`v6yh%T037<*TyBrl+Nliqu>%!i6=K0Jpfo86D1?#pU!!vDWUuHRv_iwL=wz`-k&G{
+*Y`Buc8P25{5Z^-6b!-$9@i~rc{C$kE)j41=jFBGDsw)0+NQWqqS4ACi&x15=`D0-rSJyto+ibAE1Ob
+&=>O%I^)UZEyh(hh!z>nd1gYlf0pF;ii)hdlIKo{t>$`0e<%7?$@f59&cD<A1P7eRH{!um-7+oRI+&R
+XUZcdS^aWb<mBUh_+qcdRFM{g?=>)ED#M3gPiR*rsIRdvE#l$H246eY13wG$P*5ggMhKjxW8#H|R&!w
+_=YmRE&tF)rJdX{Te^545AWX}E%-z==DZ(3!r@~Pzt-haY9+Mqj|(Z?9yCOH=R`IBOHrwW&67TAf`TN
+F-edB#Tq0Hw;{g}1`T=3NlYFGW@{J}?8@kQ+H(p~^CCl>7i!Z}*HvtO?HJ7`5ZMhnF7&^>wuW;jji}Z
+qNqm9b72ZlIc4+5Pa$BrSvZu^kiEb_6&XXS|J>E_(gg(6TVYjtF*$?XuKf5nm`kZ%aeryF7l}hG$!a)
+Z{aowm)vlkmUz&;_iP^gXUY<e2Y(e2{xbmjACU!s>x7?+Z&3^vfG<?-r4P@4c&*{d*$Dy7&Cky)i2ih
+|Ux3)I_W)TNFE|!YjX*U%Wf~sYC7cO3YQt+X+WZ4hO9KQH000080P~d=M?<@N5j_k50OTkD02KfL0B~
+t=FLq;dFLQNbc4cyNX>V>WaCy~QZI9cy5&rI9!R}%q>DJa}uPE9W+1#~ht_YA`i?qEK@VbI*(YDUA<R
+dBXE{gv5&J0P36lEv(p#>@g8=D*shx77qX5@`%4lK(yt!f3&GFBdrjZiFK$);)*&-C}n#Ee(Xc3ak4`
+?Znwli-ud26-LJW15|KT{I$<<#T#yPF$v~sH$>hxgT>OIm;!}!x?>d)|Yj`pDppnvflSottu;Nr^G=C
+&Zn#><*~|NfREY@NK+%RU4pD@mBDCFcv^QH;p3G3z=bTE`dw2i(NvR3CQo(te5f)IHxryaz-iNUb2ye
+2k0Tg-xQZA5yjtESS5cy=1wVz9@ZnG@#fuqC!<xs!Suw_2M1TAAan9oOHgOo2DBK9`Co|<QDC<o_!jF
+k0qDT3aWjU0nTRAS;8yJ2Yr(Y#+?(h?=noLmHTmYrfdb;K6BaOvbR_6!sFeF=7xs>cL?dqg89s3$j<`
+V`F(gUjqWQk~?D1{wMyFtN^O=gGJJHFmGt<qBxz8<)QiI|v7iPx=^JNt2`)Y1JcI~pJdNoiNUVQ>smM
+=W`@nZoJTxdM5Op!i%}U47yZL$zcvsAI~kR+A}v+Hp|ds!=<Z!w0DZs7AtyZZ)WbgZI^q>UgID>5g%9
+7bPi#lYA=Gj`mND9)R;>#{r+{cX@$I;%n82mpQwU5xeOacBG)6W2-<b#7wkxhK`xnMeK1~M0nz;Kkah
+r&C6Z$lu1<pyNB=cN^*aiKP!=EygrozT<c%a$KQVchiJ~CchSFl!le6j$kloWGC8r3N}h}LF4pq)q~(
+SbMJwjsp4$D=r(D#aVgER!pm%wVOAD^JIFvPK5MQj>FxZq?-$AuagQYIS#GU<px@1h4g0I?b9Q{cvUF
+~wk)>?`c*Fadz*<+2Hvf)Yb5#&|u`=$n<!@u1uXbBWXYDl-DX^-)Jl4_?pqgMw!{uj!%+x>rNn4;EGu
+sh+axkm>6jQ7SxTzF3;AQ&etAYjQ7Bb4QO6b-2w`dDzO%9@yT++3ir&|;$(fk{Wp$uT-YeRIUM2!2T8
+jCAnS(;Gz+!bMtKyfO#U-bj>^)JQD4({_9iqMRExPJ9mn-)dxv_D%V7DV2;z6lR}m=YC*C!(|--IF@^
+kLXan+F&ShRg5@<MhM1Xzd^$vAv)QH*YtA<1Gh7R5SHfk#0wzz8iZpcv1}O?&zzOK+_vx<!ZaU+L!=v
+MK8Bg(9MA~1XCA;;kbjuc=PM2YP1bFLsAzlvao$*5Sp=swSb_++vft-s-GdL?2vRpd*wZ|e?+}rPqx6
+*6Jwi@oztW;js&Mpq)|4tx)hr;*y03J@6!6r;GSPcHxA=`~i@Q#J=-5L(2xyT`Mw<F=noiiu)<8d2KT
+~^5`JW~vhU^DK3F|FaZ59NRq_YB>>-}W=WO=h|T+9Mv(Wo?jr&3^h~Cq#4Fc(G1WqS^|6<Oo9Rm#l)~
+!SzRoe}Y>G@gbG3PQKk{8<B4hfMsAbEJDy<AQf{ahUkWOjyoTs+8Q$4vTpfsWi*RWrpt@3*>@lQ@yGX
+bz}c!I_+n3u*kgWKvmCCv^*-NnwgWVqAve5GFj4#_;I!xDiL)sEv4IG4w@tyArHWxLS<Gkm%LFQ@C&=
+G@D~3+qv%RDtqIe)XrSLgq(cigNkZW*@`(*KIIlPzYuJ*jahULZUxcjX0b<)xye=ZO0p^t0ylN^-1fm
+#R3RPYn{&bB2KwqZi;GQ9?|6rVGd?}3y`ObPoORntH*YJ8rY2qV=6D!uUodo}UAb(Kr1a0NuamIf>f*
+XUDT?a$qx7FTS+gno%04@^Rh<LF|(gq;`5v;L}&RyV-6hSD83M>J{cwJzBzxHe_GLSB<0^Hgwb0{FFy
+?@hp?<TBgf`E80oo~cjot^|xQ<WVvfe}fF8XFZx@n1F)2wXF*FgeMbq7%yGe4iDE|+Ldz`W$2529dn{
+nB$J`EIEukwjTp*@6q!!*^<IF5P!E#iGCy(hUD3d)VQvtgMjd7yI8y%&iiBJ!T~OLBMzr&@9D|xV3Nw
+AmbJ6QChtmBSxi62b^ufUKc!<8(e(klH4CCFB_%UyFjGA{2am{v(f;^AJ!75KT16aO+>V%O;niTieM5
+*o!%<t}C7V<z>UN_ljj{t^!YO0+xIg@{Y5eE8>^d8Yb-`dF3dWgWtt~uQ*1oMpH>Cc1RHTwWX7Z_NY7
+6h55X)y&Z2}g1hv^qbYk{$D^skgE$xWk7aEIqzpgzU)i8rZPeLn3G|8oNJWf+{N{+(0D&JAgK;5mtRW
+LX_L?9%m0Byguy!_Qqn{LkhbL8#rc*C>iERxl|XoBBFlYZeXD2Oxh1d?$<o{n~Y)3-+6=@6gj#O;3nw
+MRDTr8CWHv<U!>v(Dytwp8exShCN~}`wLi?|q8*>BcLaW@U@}=}Y`lK@;t6ub7_qdVi}7ll6@UOHOm-
+C@Z*mo8jQDtF4@r_>B)IjR!ExB_%!SEA7d8Re+4$)i#Qb^9O)_fw#2;#i>8hd??}cbYFA^2O4dj;R{P
+WyxmS9JG<9iI0N;x<pWXym`KCr|%=RwPFq6E|$;`os6^%v6JzX~GA$WuZ5#fSVDOC@xk1}QX$3`O(Mi
+Q8lS`21@9SOXK^ngy?j5X=Jq(2v0+Oq7N7eK^SoI@%?_^lu|DAr^OwvQ}}ic)d)t-%`SZ(qedmF^Yt}
+Wv>}zUks_R$Z&H?x-bI`y!qz&Pz|x#YpM19aobTtCZn0jNKcLA&r8Sye#onqlPAU4#;qv&V1<HsLymK
+HBe2p8xsSC0aolLN@87^Jcvf+TZmCw^0oCWc$w&!l;Of-J3$-^a2b35|?yvE>u|1u)59~hubwC3O{cL
+O6K3$7Cn$6Hq7yBppe5PlOPY66(!DFvPtjS>{>3f62^;XZL5?xmQ;N?wk1>7;<=f{Nxtq`d~v)I%ErP
+O`q#d6|84bBBEjXrUc$puHyl&=EF9?xe~zQwaj1Inla9P$%XZNM=vSwt1R!kL{SZ}D<E);{Ycl5M4=p
+Xj0?5Y(s}NXs0c8+JA^_mzZ;H8=1#>@lMj(AY!@vE$E!oG`EFpy*EfcRvF*Bx#x-v3C*2uuONbMv`Ts
+?6r%oL+T);F#3h1Q)YGUoN2`&hatjDOYZf>X;w?@jibs)BEykVL&H2MP-9E<mXrh58;y^6APVHIxF;4
+ggPFH>ui>4APgm)Ekj}b(4P~hSx&W54Tva12lWW`(vu1@^bGZetXbxQM=-o0T*!g<@60QmqY4#f@)H;
+Q~Lu=YuKUkz#1FxT2NVc%o;`PA#bi*268n>T2%#Y2We>#%lQv3s<(flyF`=rc{*<ip`HWzRmxo>0bUU
+cOsnjaxu9Q3NNM`C8|`(0`q;W%aEN#CjpJ49Y%xa$jzp3X$w*Gu%*I6_q+hQR;Bn;!})6-lr^c{vn<8
+xZan9?7Pys?3UrCEbmj>G!bl@Icc(-dz9<C0dERhU%51(x1-?rl;9F{sN(9!U#4{t0UK-J?;v3zr<oE
+QUJgEXeiBJag@zxcb^rbz>Dpi9Ny-}TAe$GL2kevIp3MirWTg^nAAHV<Bl#g_h08T-A!cgpJ0{E18K<
+sUVUH<mclvCWP|uzY~|ZWYu<gQ3WD$hHTGAWAuOEo3X3r;E&*6ZyZzo-#-~H+@iEu|CcHD&GkP+wO2=
+3Itmqi<2~&$}_WK6k3hwLX3BV**2+pP7fc)%RYwS{Ce+XYOni?2?hyl5Gg}>Bj*Z9LII-R_;fp|x{Pv
+$IirD#;msI5ycMLri9p>oC=WYj*ok>B3PYgq7L*3#bkruVkNAL2NT^YE>hR`&3fnEy&`5zRzI<QjZ^B
+MoRxQi;D0a)0=RKMvDK-h~-#y2~Y0KOz<YdadMCEk|B+3krxozo=bP&%Sov)LL3ZF)5!KQQ%dS_QN8z
+6C)DWT)`cID;D?tb%HNwzNHs5dfs=#yj2JTddl8O-$verWVy_V3$7SR!^b2?Qp<Br_ne>PkG_XF%W3c
+fhj8`iO6RvH);gAM+}5|`7_-i7(~f>=8Cdog8CasI7e_PbJHmDeIP+<ZuNt;+iFh5_Wbz+SO9KQH000
+080Qi*_NBv~N`E>yR06hZ$03!eZ0B~t=EjKPPE;24;X>)WfX>Mk3FGNLCLsCglR7p=xE^>2pm6E|u!!
+Qhn?|F(y+>n~wD+WSCpxr7o&Ma+B8j&VZ;uh`gaoUbDG=a!1vH#EZ9|sfIX0u^z-vK{V$LgL(Xnfy;Y
+oTQ~9b!nOl;Mb-cSTUmyVAz#Vn`4RJ(w0m1l3{(t#6X>QA=>rDujfx8Da#7qQl<a9$p!GZ~2>#+_;3w
+S_1KEBJ-IHNicTJ54$<8&`@Y?!UPeHK?&n?5lX2o$d&a~hX@b88E6Rn1+ipqJLNQ3um39K;3Iu6@no<
+YYF<&>t5i>qf-IDokWoweE~NqZKGL9h?%x#B<lM9fU&w0SD>K<;O~P55rO^)|3GKbMFcGGg({GI?;-D
+<J@ni~&{{;nAw6N%>Mt0BJl3(QjCu1KYfb+Bj6wdTDnq}u1j0wR_Pj;0hUl%KzZJZpZ!E4oO5W#82ms
+6sO8t_~p<_V{D%lE9kh)+N}2m+%TO)465QmFZloxZE1a!t3PZfOSs`&p3~m&G*BCs0cR1QY-O00;p1l
+@>=C)Al7Q0000)0000a0001RX>ct!E-@}LE@WwQbS-IaW^XTLZgg^aUvO_}Zgg`lba-@&PR`FO&d*7W
+FHSDXEGQ|C<toT5P_R`1l2*BinRy_tF*;w*1X+X&08mQ<1QY-O00;p1l@>>(4j9~H0ssKh1pojc0001
+RX>ct!E-@}LE@WwQbS-IaW^XTTWprU=VRT_GYIARHtyEoa6EP6{E0&*%M4dJ%REh+mP$VK18j-xA>g3
+wni|L(h{E;+@^546AzK9g@a1uP_GCSTG?|OH~;VFTQ#@WcHY{&gJt(+Aak~B>Z1@{wFs&eRoFRlgr;H
+#8;oQ_7`c;UBR3gbq6|C({-Y?3Mw`^S!3S?J3ZFr+dKYiOK@5IJ(8MIj0o<<qqZDa!`z`a>OD_EFT%)
+eQ*cTH*)SblGG^df@vz#I#en1)D08DjqdN6^u`KF=AhTUVQpKn=fa#-$aOs@5yF=S4`YWtIN4fkiJ6y
+vB==<jh}S(jGv-aOCAUl7k@!tK*t*J<XuQbqR(CxgYlnLNM?=I?5}z$&c&FRhq}`V{M#6XjqZ@Ivn3&
+_kujPFL!H1*@$1qUsw7(^nQ~;)DSN|C**rL%6%})YfqN_s9-mo!IT|#(%G!WcK<w)m&Alm_6u4c?*p<
+h(w8j(oA3d->Ow6V8MiIVn4{0L<uwyrff6S)QZ0TInWkyGJkZo&h&2FTvv#dLrc~@`x8V{t2w?m1S!-
+$iCG%?WOL=4V&F^o8CeFIcjNi8k0`Igft;W`MiJck4^VU7-t8D}JH4`Yn<D;9tzr#yj#ePItSFk@qqO
++(tfo|ozo9YjIiYOoMw!okH%_u9l%ZB_scJ2?~M3vu>}NRP1HxS)#*ka1h}St&?oax&!=L`d1E!<&G*
+rk~#%q_f`QD$_E_>S#vB;;h>x*;WC5WfP#^Ro(T8akxCOju<r07<nnfPTs#e7w5%%nQ9^xc+#P*Q;+<
+vTUHeML-l_u?4JQpO9KQH000080Qi*_N2NX~o-Y6Z04@Lk02}}S0B~t=EjKPPE;24;X>)WfX>Mk3FK}
+XVE^2dcZmU+xERHWq%}GrxPF1o}C@CsU)lpDN&n$^AOD!tS%+CV~Dw$iPBqk>(E7bx3P)h>@6aWAK2m
+tt%7DvY)m%;`B000C4001EX003}la4k13F)lJLWNCABEop9MZ!dIja9?a?c4cfXba-?t$SmLj08mQ<1
+QY-O00;p1l@>>bGk<Si0001P0000P0001RX>ct!E-@}LE@WwQbS-IaW^XT7NJT|V3(rVR&Cv}@Eh^5;
+&$Ci6)HC36Pt8j$N-W7QvQkJ&$t*63F9)en&@j+5GSM^8<O<5qFVXcZ)(t2vO3lekvQj81Doy1INldp
+=D5x~j&CAbA)lJN+gozj<M7RJ@O9KQH000080Qi*_M{ckU>FELh0KEqQ02}}S0B~t=EjKPPE;24;X>)
+WfX>Mk3FHJ>MK}11RL6ue8Zqq;zec!JbiH8D`*S!FhOj8IAm#A$TH7Pu{8+&T6u)WJ(Qu6oA+HTT>2%
+0=OGv~~GW-rl+OgIsJM`NX`6FB7iY$Pg7KugV*;$wcm581?5l`xA0rUk-yG46xGR8<w$8D`*=D&bxkk
+hOK9EM=V&$Q7yvEz+MNN6XnaRbiy*JAuMEZId`AM_ezo;8Mk`?}=bq52Sz$=U6IDadfH(D<Mk?udy7>
+rEy-ASkFJ^MtN;{u2fzkr?}XKOtH4)>T)<`=eT%KCbJ2ku#4L+NXVph-pSH}Mdx)$!UE<wtSqx}DI6t
+MC2%u3A6@+zv9nTGEAJ&zsDm~{s)B=w6V6*m5_k&{jE%~C>ZpP-DP|?E@rJc)3)lCV#5%=58?5quitR
+E9h^xsE`dS+`Cu-8<_NZQ?OqG^t08G@qdk_ZK0wwt;FUyQzl<*pnqRx$|s?fg+QRkkz74Tgz>FaP;Jb
+$+{<9J8L(XM#DYZga*cOP$dWDF}^VrY;EQ`73k3r57b%+N-ke>)=*aA+MFVb}P}OGEMjO>wgmq<9Y)P
+|)kaUI*OUM6QM#UY_<2_`&|G=g@3wq;^5u(vF_Rs#N-OD~^R5)9dvZ3+sNjc~RKO@<g=D-kuSnM&6={
+a!)nk21Vuy%v5T*GI@-3OzTQ6;?x^MrR0UH%G0}B_N)WNUc=q6)wex3*><PMiETDIUfEjuAqxYQ`c)g
+Zn&^;c1q}kNW&S@$H9pv<F1|`v_!*4D3@xd7BMiqc4i<u`4nDNjBTNk?8uI=ZBbEVcJK_zM8p6#Dw2~
+Uyaszeq;I@+ZuhaZk6rAsBNQ;PGfR^6`GQ2)Z;6+dX750lktQ8Fbse|>EHqh1)jByUK3Qy0Iw`UQGE$
+evRBt8>aj$G4bLHA3XFgdz)25&QL#x490+^yy<(hMS}!JkVTrSg#>+8)>WhnW#|TJ*YSj%|7*8{5g0&
+L2=q0|XQR000O8_>~q%jg!*R`zZhbR96518UO$QaA|NYH!d+QGA?9kb960fZf0*UQbj{gQbe_U$&#bW
+66L<l`cK4b=3&tb#2|z~3=$x;!I<Y6#PIcv>fWlWRA$z#*VesAQ!3N!93tGq{kTU2P1^h~$4RSnaGd`
+%%dSsc7{mW}NvLfqcx}hA3B=QTyVKO$0#%1~ANLb<p84_?MJUec#~lQs5c)s=NAm-zbkphsl8K)Z;K;
+EY-wcrNCOU`9Zf5WfgtnsYX#Jx?<;=R@R&Ixpf8%&=ANqA!x>fK3Rmay6HGpgg=upXq$(0+r-~!9x_7
+}q#qyuHKBwL#9@O&s7BK`qu+^!G17p!yP6GlbF?f?_>u!qr0b&?#Ux!;vK!QuFP)`JT^u=?G84iLxw!
+t%?YZick3`WLKIU-6cbCpF9~x;Wbtb!AP;?xB|0^UD}Ex_GHULX>542cZNA{e>0OQ&rUN>j=+|EuOpu
+>L2_2!U+QsAV>R<XckxtxJh;guF|ls3)(W@ArOV&e<6jlAAWi&GKm%m&#J*{2sg~QoaN;ZO#>b}9(g-
+81a?1~y1rK);0}VZzb7lLf^dF8iSYFRBehwgJ{B_xJTnp08S4Xvb8)tAY?J{)K+0Z1qdSx!(Z6Ae+IQ
+a{2v;uwm$(!X=dg9k4l6*<`(Qp-J`J&^CgE3!4aNFujM@&z(Z5}x2^$o~1~-0%%7Wum%G2l_@#%^+6R
+}pJDi1+IS^3$}QbnKeYNbfxCht%b{`;Z&!5zll@#`Rb1>^CNJ>Z9o(9b7LG}tO23GLJ#A>8PJ0oa3+$
+~&kgH<rJHu)n8#O2V*sohOvC8z2yc_Hx!Eiz|cmHmN>)>iI)`_15J=(GnjoxQ6Z!5F-DEaUNk6)ZJH0
+kY5Z)n#V4d<24pj`q8^lagk*uE&|ayseoE0R2E$nk>0@=NPYxN8#f5LuzwvWv@MJT_RVe@vW{}#F1}3
+ns}@MQ><5kAPSc%(Ce7FC<Bp(kjQR-Kg}`6Gy+Epda7V!atP{Bp#F9n_kPd0fQ*S9W@co0HXzavc{2{
+b=1cAH*4&9Pg@e5v#L{Sl8j$DE_|0vqYp15WR>a`4`CI*|^-VB`|7R;;Mj-m+oBVeDty6$?KE#{Pk*r
+Dk_{CKp^u_loKc@U{?qQ`;**~;3V)-vICFtvHyn@LKy%fqfny|1hTC-I<<Hmh`LdFLsBJ2QEwl5r(sl
+yK*UA_vy`$~`K`-eJFi{_D3_E7>F8fI!`k5r3@PYCRwlc!wjFnNhQ-5Yu48n+UVC108n=MZ6iwcl~4w
+oz>9Q#aGYG;-yten!y<drUx5~&56TsF84y`^b0?#QJOWDM56Ke>8J2-z}>)Yh7U0{OjpYiC#5=I*K0j
+Xz%AvLN#!Cqhd}@c0gS;%e4@Fb-=PqN{|5ZWmlx=HC;$^^I_h*8DD%OyI<u#iPEff=`|xrAlc5KFtnM
+C`Z>|ge4!JHHcX)NVtTjFDaGThYocldLRMH?~psCdgZLG2Mcwk;OcWXS#Tg-)i3;pA>_?T!G2B+KuT+
+3|{&crG>Lz-k@CzaNEsp1TIOvGW8w@71bi3Wnv-w;G;5l)@kG#?nUoB<C?-FeorStw>z5p|j`!i<S9b
+KBGN**ge0DU>QC?_dZge?yRjW0&?r>c2W9tm2_oV||4_aBf7*l}B>&5*24-bg!4xMq4L?TO?C8e}{q?
+@*Dgu2d8e4uCJ!WxP2u!gwUDmULe7RZT$z*!#3{wkJ;@N)`D{v>Gv(n?Qn?voxgvcn^U`t4sx{|485^
+qkhmxy@G$gPIE5C`d!^s_rN5?9d82nI^}DJ0@umN2NsU%z42%2_Y)3LAS8`D2s6)IntMLh2g-BfVYTu
+%4wZ$w1|K0Qd&G9s|^S%Q3o*iSvn+#e*>O9M$!#WqNyN}>#GA|XB?oaYn!Y|9g*dZA5yOsMlO&11f|B
+2_eOzoUsC^=`gd~;Q7XUaMbgsborEYqH{r$X6#Dq&Clj(U&ieqGd^Te@FOlk6VOfq=N~p}l!ZR{<sl*
+4f>k)!C8U9I<kiLJ=oEAoUK0v2TFgs!v})wYIy|+^sd0rtMddNxVxtYe<vpXRj-RW=whgdLul4%Revz
+zAvs<)S;fz3V~n1_mSujfbHUSP-YLMXpRVcGOjj!f;H<H@?{cZ^?51HUfxkK{yybD!Jjkn^!omFni+k
+Hvr}+6B<UlG_Qz|pryh&eo3W2y7v}KbTXQMf`9y8%45Nr|u-_lKDajyF2bW<x(t=IG13gnTgmDEsQzj
+{V#g&K#Qr(2n9R|Vg(d-3w3+?5rcst3?gJ|s<a1JtY=NL~l$-7Oa(xd`d$+x1w91cnYLGcbF-ra9=wB
+egXnrhJ_T$2emXyMTam6-|GCQg&w3>1Ct-KMx6N$e1)v9{F*(AT)1g1WCYfl?qog=Wf3oV446UD+&P-
+F9AkFb}GO9b$#tH}o@R;W+p;Zr~2?vwV0hLS_^aVSl<=un2nhN9>-WZO%)CjmDJG(Hctq0&6?U-vsgI
+739~*?>f6R5K?6HBeoSrcR(wbAgz6t!^i+VJ%sg?U7RZz?0dC{cQ8m1_}8#Mba(whPlyBz$`{49kOIN
+n2#K&`(;IfWLR?%L^L3E#-~#PS<ecv?5J8Erk+)hbjl%wQl0_clHcibaYSmltU<5ZN$c}sk-17(}*>(
+c2{(Q>DD&2e-!QR8`KgjPADAhaDnwrI&viu}oZseKMDA<v8d1`dUkIsh^!vahKY4Q$-$#0yk3<vjVz!
+zZQ(a*85H-b(Kk*3l$W?{_X)ZMVdaRMKi%B_#8WH4_5cL<3<=-0@buCB+<|4>EL8b&6fjE8Z0c!Z#e8
+*6a69v&vPAIQ$N9JX*_iRl!p|52P`UqknPmo~#I@Pa)kjT0v44n>jyaM#SBRtKoGdvV&2DAj>wXcQUL
+Ht1GeLL~Av@UX>+a1MW>c8bX2A#3L(c@?oj9F_qixdN!{bK0QBqq2{z2yf-ex!;i#NPc6x0Y_g^TXwI
+F)Tj!K8SHr(r_V8PYpBY{tkp^R>QeU1^$~Dd_)H!I^^N7mA$_f_paFHR3Ml!vHMR^*c%aI?(@Pds0kN
+BJ;B_2qkdzz<nzo>ZLEk*<V{@{tBz*zis*?M>(qN9S%b@NR2Gem}OASk}k2>sjM%PS6nCXVKr6h!S7k
+@v2r;k%Bb=B?Go&ei}hV=p#^HM%EHI<88yIb@g3<Vj>DhVv<d`Du~ds6y^`f+Lx-l;|FeYpzvTi`dYG
+&UzGtV)d~9QM}RH&SI)t9A@zOGYUCUJw34eXCpNhkG8d!4{zQ2mo14lm~*-VnHSk9>UCl#Ufsl_Y1f!
+HG~4uZ@kO#Tt9E>Zulz1d+o&uu8M$<7L^|2036q-QL`F<+|jvF??k>tcM*P^-Dw9C-%Q6p3}xE;uM*D
++{o^_C__H!!6cvgNJWhg)Sh);-L8HAMbAxMNCoqjSpZmsy|DgBDoA=R1N)iy6>a|FnV7$~*pj)P4AP+
+OK<d5#*8Q#5Lv&i1cNDRlmNhL1~OTQ_xD2@N)NAXXVe!8FLmeOr3q&h^sF<r4ajod$!AhquMs~~aPG?
+J1i89Vq_YwJ%){uv2j1-1%K-bALW0EIMnIMaY|GZwK+)+2p5IZ~AxJ!+hf8wZ7b<3j(C3~u+-s;{Qbm
+g`wL-#x(QZk@nO+^k$iHy$oZt-8utg$<*gr<`uNACl>99LZ1?*f&C<cO;^}8|39aEH>oZo0uGV(DG>*
+y0K}UJyPz1%lk}X$Q|(>xqks~ld`3(>O+LxW6~LBWfi~+uA!zzIisO}LryaYRgUk;*?0(WdI-~*+mSf
+=E_#1q|M=xor?IAZrAq?oU@L=>^kYc(<82DYC2(W<@|b23ER5!0cXqskAmUR3{so^V?hwx9t8dCz2=>
+hzeR3^;mW<p8&k%tLjiD0Ad?F|M*r=;U`f+sJLHH;6^bd5^j6*+kq5G=&`W~6Gp-?=<tj&YP_xp9QgQ
+hZC-3`}81pDhP3OrO=(5*7YaqJuP-hC?I^~0<jbU^bV<>Y$FNg(bp3`Wyd=Y<ag%A{u9n@E}>E-X{vr
+X7e+0__*}Q0HOwhK(B$*|SFeI9E6hdH(*gw+-eR-PTJ_aSL-zRX@X2I<t>E0;gc=8|-Hwc=Z$YVm_cW
+rfT}`0;~by>=pGrFOI7NaY9<{n5!i|il>>6cL<DsXUA>HY~uRMQW7OtPHQq!*k4@0xgJXkz(gh-wNTG
+X>?}8;bw41<W#o<BcQf(}{!O^9z|o-Yw4F7**|()R5cy~^IFjiQl(2G#GiLV~-I}z(9sJ&({DkfnP2z
+gtkos=n4nvoE<M2TbMU5_bD}H4{Y`Jmyp<LubBtbsRt^TS5y^texRf7g4X`6=oZn^%XORFsO2j?m296
+xz3u~#8F7sUfQ5@R(d{&K%Yh;;2A2skD1mSeBPR9^2hu^y9Vw4Vv)-^NyK)z51qJrry3a$|iP1(l>f2
+Otv-2%Z}^=8g>j0+wg~VnnD^al6ln_Y?YG`v-3E*3w4nRhss(th;kar0pC=lGd<EgQVSwu!WkE;>)3b
++--T<^dkoToB6+5bn@e1^v(kwTuY4)NjCSBelb!Zn!8rsV$)e&f$$O<wxR714E`5}DM>ezH?IHcWs;{
+DJ_D{AwGtq5I^R3GQ#vp=_X?rNw`?m1eAYr>$=K8?gnq~It5Ni2YkcN!yb7e1dp#Fk<&Kz-v4>7@6mn
+5ZfWZaJ6XzzqwESj~_io@<9M8Y~^Yt}M0j?>Og-(pJ4nk0-fZ=1yLXX@O8mU8+PP*9gohVXsQ}yts=D
+rJ4T^Rn|F#Z3w@IP?$?rU|4)Dil4xQ2bCQ=e$6IP@mbgwdo?F2p^i;#hPYOAF&Il5c_hyEO4+@W!+Vz
+8S@BMQ`{XV`)eSu*0IeD4K!2I7I~>80W<U&-;-Ai2j5aI|K!PH(CGCyoIq_1oC&&H>aa^!3VAn#UR6`
+lV^{<cV;S&?yLTQ7Rou@Eb>{ie@zn9uX!(fl*6Oq^r4w=jqupVX$$eqN*gv!uXSB0*V$p~+jQWLI}&~
+G=zYx)KEJ*8$_G!u7p+)K6z{Aeqsq%-wwhvfaV^AKZiTk01}&_6o^OtYzjtbWL*f)4HNuMQtEhzKe&9
+IIDLj`BpB;(<a6MeUXfCEf_j?zf2-V-}M-cfP)8rQUH?}Wf0Ch1H4dip7c`z=Yk4c>2RD+j2lma3P3o
+6?Y={4Glv@N0k3Co-5sp>Y|hIsS>>%<qQIWbqu?Kn_KAIKFo8N#s!YAnok5AF!?TM76|@zL;-ha=~4C
+50h=<m33blm;d#i;;7iI1;elRaH91<}=zoH*$Y#lb;0db`KN>2}o!N_#wMqb<}6}a)RZ1XMa9F+TcnY
+=8oBm#F3YGDE4jadZmcNs!!5S>h0+5`L2jl0Hc7-xX@aH0deP#d9^>IKE`2$Du`@8BUHbGVdA^A^~&`
+0toH%AB|bQ!kT`r(LLjpV(Uo-%hn}6T>IqTZp1A7!aOHb+vpe-(@qbCvj79O$FqDO-<YEoR>MkbEO*f
+#<P1hd=!EGDuJ_UQmhcC!2)X-;}_j{5w{L*(rAKUaSHXTp8Op*{4AN^x*Yg6UN6x4C0IvoTkI3Pm3gQ
+4G}zxk|nu*u7z8TfKW70^9NB8INrQB&$=d?XjJifIIY(Eic)w}R+9$@l|0aGxJ9*jvVEFHwHi$E(Ky?
+3h@@EC(h#2lw-R6Deh7M*{62>nuMji|;MGfj7$w@N_FRsk`eDDlgV~iNO*{NT{TaRU+xcK?Fg#JRa`}
+K-QZ$C*I<CQTzF`Ec|Mw2fZ&+<}e=*dZal@PjfX*fQCEhUT{p!1XS*wEjjbC0(TVj-ctXCyeYK&=a<*
++w`6FKp~CmlIa;{|GXv)$ka#&`iGti3lO3U%4lbeoY&75~^$mKN2Wj^OJd2e*{V3r4Hardq79>K1P@$
+Uu#3{Y&J+0E69$9<ZF3BANzPCGn!8d8}1$fj=3RaJE5XJ^k#{2~+l!tLTdM$amN<IoDg2ko>YN7K@FT
+cm0U*LUPY^v<5hrbPN<~3-`uUiUG@s6b1lo1GPKNZM%jigD2`i_AVPu^h|3Vw&bl;2Fg0u``5-g`*bM
+(ZHxGa4)!Eao11zN{yDPMZtzIAcsQJSQJ|E21|UnY(dX#hcf-UOj$v8+#2p3xEo%T$nw(bpAQNdt!|a
+DQn%DI&IY~S{PeikK87X-wX6t?9UtdCRqU?*IeTLcq>dYDdaLSh<jc%eb<e>CEw2t95blU?<f%e7WvZ
+_#8gLhOs012tYa`Z5SZd(dOHbhj~x<byfGlPeO$!!fj-aQeE-Z_WFP%u>^_uN6k<Q(j^kh<<{Od@ZVT
+8<jBZx<a2#|&ae!tNi7V)<Te(1j)OYB86Z&bCe(b*+CUpfNCqET6O{$krJ|e=^-d{uqJxM(XF4Chb{P
+d12##SnV?`IJHLI2!x4Y)&R-NifJhY>W(^Ce=LFs|X`f@mb}Ens3Cc{2y-!=^?c>V1tidF=e^zJDn}=
+OsCr&LvPAzM!S@q=aaUW_cG^o6eeH=qhL`3RHLP#~Cs7U5GSY9Y2rFe1&aQn{T0#!QSgffUZ$(+I3c`
+`&jXDe-J$FMs$tSkcCb^&rZBs^e3|CzE&8XK)`I6?BiHDJnsDiB@xPk8-OAqhgL2O84~w2nKdNd=9&P
+BZ$SNetUjK`Fz$Yp<D`)96-Lii7_&&Z6l@@T!23f5^tRROKzfO^U=zyNdlTdra1s{HNAGaex+YQflGr
+ysgz0uFDJuiw0g*ClFWMB~O$^GwK72fG-a(<?0QXH=RUe*M(#?5pWC-Bym?(z8Vs~jOSnAcjSFUZ$y*
+DqsrJ0Jh`38c%g}^Rs$FLv1v>b931<9xMyMt(qW~VCHQAMAul6(xL5{fHuGN*Iv-phDfE${WuFXW%q)
+oV%3g@Fq6#jTy$$_`RgoJWR37WjNk*Guw{oJ*u4n~n&dl@$qr-$6I8+uAqsUL_>l<FJG9dzH^yH#%z-
+(W4T8wIpzm{6adNgdREWF!X&d=^xN>NM6$;3lG6oxd;VdTbg#f=Ga}moU&fWoPIy+c^}&H3{?b(?r`G
+WaKBLft>W*tNEepBJR&DrrzPuE2sA2+V8Q@6=)s&jDeZY(yT}W*ih0mzg}f~c5=Fj+-hf|cSHEEb<X_
+S~1`A%6IPC)oWs6K&f#aPH)zXK9(o?jiqp|yxQvN}G56wwSFejm2@^eov>q(`>RLKQRRO@p?(Ih30(K
+Sd4rb_Ki=f4+tFSvD{eu&CO2^BZGF$@I+RNR2B${5^6$hXbgo<9>r1mOxlr$>6&3J3zCzH_uM4|J~gL
+`cJ1tV2WCdXKSoD5aZNS*W^)K8%G$5oWZGrM4v$7<=C$_=TNpPyHd2JkIHu>Vh6%+Jf(I4%k2fa_)_l
+ts&VFTwaH1UP7U3yd&Z7{I{%w@I$lIdCYs{j%aLdI)d%r`Ihh07J;tiRCjV{b23Pw8f8WxxPvL^eM9>
+faKFWhw~5HDXly*|>=?-i@F$h?i3K_@k3~CkSk^5gjTvqL+Y7jbOcZ|Kk@*Mu>+_(jNh8RB8bfkS?}>
+`0_Jv72D;twev>ql6qHPgU)s(^74uRhraWCj!r0o=0WY=ZA0(IiEcnbUzlCx!ak?I=kFzUt~t=f0m;k
+;~>21R^l!}regl3;F0u~c<%YT90G10I|Fb{rB|Q&LD0lT^6OSa31DO?yGex9jh(%>8v{b#J+MW-{5al
+5WxWwa=lnOFIn#FxOey>f~{-iQ9TA(SG+X+&>dMo47&v<#yq1yq*|a(Ft+tYhiBhS9=rjlDWvN#~zU$
+>OsqC>jSsn${LvZF8F<-^3oWKwtv5xnyjzE3LIoYjLB>7S|VHG*h6*{Nnnt9^zhaYkxq$wZS#HX+mQG
+r*OuwL5BcqOqi_ZQaO-4S2wqCSN;Kky9()L=?&SSg?iWTy8}3Ze&s_1xWPg8#{%eejP?(~O`vD)V07;
+R~IfF{j@fqCHl*k`3RHaK+qWRMLaeVl{BYSomo*n;>+Ris<UE@2bzDl=HWLT|Z_1sVif}|f8c}*@%bs
+$ZNF)?zhb>Z(;{Fjt{IK*8&HlHRHEKWILau|e1u{>>(aX5z&-%84SijEv*MM2&iuQ;Wgj2%LJ8*#rQ{
+ls@i$r?VTN(LkRx!pXIZ`VY?N~jG=q&t$Ve=T9V9r9w664ZMu;x~MCuqozeND52WPtxi`rB7m=od~8p
+Q8UYlW}IqC11gnt=<pQoNQclAOFmr%klew@`vHjGk-lX{tSs@R6Q)g^i`)TI=3EtvON$kGCYcR)NRU*
+G`F-Bx&z1uAU3~h2=<~KKU{p3~5S&V^RCHYekXl*<+jIGa=V*gYA5@(L5NkpGCY=!EkJq{1Y-PhIyYc
+cPkd{3Wg_(NO`>W@>gmi*bL^mFfzArQ5ytrJnlIxc9iT5)_-?N3i|B0)`uD*5(%`M$ob7v8k2f1X;J<
+qt>6%_J9!JK%fk~&!W-Da&np2dx;e{G8qX_0ZKGg;iO2`Tb7)woPW>d94&>v-O`X!ww3FJGgs-Vs|}@
+y9%)pM73w*s+N&Tt<6Df+YSy4)7p1NKm0bkb%$moR2Ws1vY;g{T+h8x0`;;mUnM~`HJ=K2`i7~ayej2
+tf@oa;59s+#-bjX$u428P!>KHTjSX#{V&Nr4b7^Fpt@sRH#yZF6@U_R3prJ0WfYC_AQTgGaStswv-f9
+Yes6gFmTj2chIVj{;NGHAXabdU7#h>jZcV~5K*}4BLeM6_bQVmvEr{a|LCN<!4*x>-X=r0}fmQr4Ua>
+J#=9@hMx+wWqf=1QrSi{8WV4MupxHm`bIkZ0x%CWZv8u)E&G92?k?X;S+?~BX8`AMf*7}KfTopXH#92
+Zua&^iZldvg)+eRJmD*gg%;w5qJuT@tZz6R-VMcIU<DHX4*nnTmm{HK*2yJL-b}qx$+&x+L_v@bg@fO
+;8(AZ{8wDLid#zgw8&kF1P?M$I8TTk_<<%;uA@*e?+W5UT|63zs=;`mrxxGb3yP_c#}g1H#g;dpPt(L
+{Z7;SHJMUi)N`fjcPN2;TLONjYW!+=-5Xq@p=?0S<#An9u#4D}M?x|%nn$}yg^|z;8EqS_3@O{7sLdn
+)l&Ns5*c*%3?NYb;Q6IdHZ&7=ipm6zOCUy{6iOj?&%g@J#KGD^wwR{d?3M9<%dJ}oXpNpX<tG{$<QKb
+GmX_Wf`rabD{Xn;d^W-a=z#<|^JbcTiNvL0ye(Pl*{ow#RT`~495?@8k>4D)wJO^_D^Vg#oiaKd1Qa_
+kuH)ShP8nripdO|Z?__WlG*(~oVw|7vpo^3r!ol)e<$V?Bv#6{Es7b*69{bejQ@Yoj$`RS4HDi`)gM+
+Xe~!7h>TD<yd`f&&zEZjWt0j$KQwYE$I_o2)d-mSMW+Y&D}DcCTRDkGoSzHcK&gkd9n_Po4ykQIT2(D
+5*$udOdqNN*^0tiJQJ?Z`j%e`v|4A0YXn65JZJ#_dCV@{A+4ZKYad2PQBY=qboDV1ZTP%ZZ4ay`Qcw$
+#!<ZLG0}%>)@gDIGgWnIpe@|C>-ziP1Q<#i3`|=_;cu2HxzqD=+!x>R@o^emaL=HnZ_Dl(U9yj`9J8q
+2q*IN1_=<8XP8uk{+qC}HT!U(1TT5;>eEOVY&O|FZ>%^{Y}h}8Q5r{6Pv5H82lB;J;JM9>O9*I7B6Gl
+PS&#1G=B1d?MkyF6EU$hhC~E%j*|^n0fM+u6LeV4<+BSh4XO7yDE@qWkvBr>a-s2mrd+?M!1pVvfY7C
+<yAiUeZ^L-^?bNNQ*$~^Z{_|WM31tbMjNmjP)9Y@0_!8!+1VGqO99nqKE&yGU>N$@rlf^l`@stbr|ep
+eqn2650VDw#_2<BjN6J1qm^3gHB~xtD}&xIBz{jeR_U{e?-qX*m>({GCIAP|`0c{1#W`Di+d6=Jh%ym
+$IRx%;HO?a}Fiq0Pn`+w1**`YoI7H;90TM%Sz8^Z#CbGM+XHgP49OOJz!Rt)&4d5RhmQd4uS_ttD#@`
+PweI^^bEkk(7mwo=AVWb($#(gc*-Cp+0>lU*1EO~jHfMY<3Jw~Y4YE#4g4hG-P{QQQ>e_jss-}`!7$#
+)aVGe%cPJOOTT!O6Bq7R7l?p-6`O)hH+V_9#w`_PmOY{j2SM@@+*|^%vc%9&D%HL@<cHw<ZXZkw+-K&
+wI;#zym$?GfnFZ8a#neI_;=GP-vgYn!0`s)cK)k=E;W(-JL;^h~R-Yt?U*?%rQ7xizKuH>*8*CCZ4+{
+2=uR_=_ljYO;Q)|xoO|d`;*ePr>lE3X*m#(460bYO#yib>MUps)be*I@_yL-d$K4Ei{O<A#BcFFyFR$
+Zv<-^}G6!FiD!R!{037FguwF^5X=pyOw|qb%{~AyJAu7VA{&3Ic$~Bs!$_iN2s>bP3hjN7J$DSM|=Sg
+Aaf%L~9>9(32+`;g_tK6SdPeYY%wf*gNp=lFjTn7E7Cb%wOd*w>Z=-DO??ehf(Rc3Q++UAtA6(;14Lf
+>~pzi0dVKQBz@tdh*uKi$oGC{=S4%q2<Ct<3K)0%$sA0T0SyK3z`wafcDuACIdG`)2cs*9OXfss%2Z8
+kN~MuS-YJ*CH&?NBz>w7nQP$A(lr36M1!$n>hOK%KgRdpRKU}$7lNfov#{-@@=8<FtyK`qkIhDk=jq?
+0(xakhOB*hX!rCc_4n`u`|Q~tw5?`--%0#;%4VrI`PlcboOOMc<*1>2F6|DBr29t>h=4W?G&NuwINuk
+*38N)NT#Oxs{Cmsqmuvo}5zU>LE3IVrLDe0B!N8EXk;w(iP%r26GfCX;$`#mRcr^CAKVRU#{!ni@s0#
+xpNI&#bP0dx6bCj5J?~!{Pxy8g?Spu;=a!$5}a$=qlHT3a1o^~Yl@7=0TydPg)U;v#0So@B@km@XiZh
+0y2IzKU4tMU+ePSU#vw(1>!zTpCb{-AL9%-0uA=?;G4BwM(t-15r<JHBGc#|o;uSgdITYw+f>Dh|N<c
+&gkeHYsox?j_1|;ChF?pVI%oe2TPeim(i;!F|hJ(FrDVgh(U;U<v9$r7|1|@%cn)kUD`f(VDu1c6r2S
++9q=Ty(#qbX`b6DUk!fk_RyZco)yEIW6pfsx0>=qj+_D0W^;wEd}-BitQ{hj-l6YD=KqE6%jB$HXFpe
+?e&~3UR_VyDAUJpLHW$@e1u!gA!)ABft3y*iCF36_l^=|s1Mq6YGdApR>m!z|m`84U?l4>q+(?bwY}z
+*5rL`&uIXYWn0pG9Q|0~_6D6Gkyvfz}Z=5>g|oB`TcHtFYf9|04@^W;Ia=o!j1wtT)c<G-8FA1@PmJy
++rD9-=Xj#L9v+lkuKq4}5z^f>V!sw%<h7T}1dsk%#A-MBXnT{0rIBYhP;%v}^#L21M5%h&$4A>l{`ZP
+Kni2tT1pO;!M0r5F-cAv-=?O$D#KJ*^riDJ-*rvmRr$eorePs(j1qt+&Q7z+GSMju{FEjnxr^}vsL>i
+c}HRBe>b-6%d1>e{lzJ^Coi=msG}+@ttGQFp;txs4w`9CpI!LjG9B_m=KO85zu(>TW%wql3T?tZj`L-
+IbQWF|^K;Vby_ek3BpNUh9^nI_T^0AZIo;1R^$YR2Wl@B0>xK|>DUA@82em_!$*z$J51z~`5jl>f&#`
+c9=?__N_yyeR$)BaxFU+V5!w>JIa3-M|1^dRJCb;e;mXIPgiI0wFN%$%7nRs0_%DYW$ZtP$L0pIYRXJ
+%dtK~%7EE~pNwLvwqfs>oPW;cE!qt~dR}*SGdoH=c68p6ea;bLZg~So+*KDPC=&9tEE41pR9BVR(t`(
+8^9Ug@n_whE*-VLInq`T=3wye;!}>DQ12l7U_p5kj`;E1})*L5p*&&1>U<0&Ga6?MZ81qXS!l}5{U(;
+M0W&?y-!{rx}E=e%w$!%ecdFh<@8Q0$UNPqp0xs5A&o$xCLnn}dOAQxtYSP<2KtVd)YI#(!=A_&^0Gf
+m3s&;-a*N<_NJ4zoHceb~a6%qf>+FVHPVP{Oct_jn*H7~|tfaq{#G6Dgf@(nYM-yn1fIFA7yx-(Ryg%
+M87Ye4x_jfQ!zC*rWp|uz{k{$IR%qc=^UK(+Gk$@+&rxBRTq!D!JtNH-bgZR`V=s)&t+%NaP&t>9QqE
+Zq7dbu&;z8cne)}%{{4q=0~Do_k1NlRpQC7-t_KH@!9%cqN<f!>9M%37TtvX_;=Vvv62OdCFoGGVZbj
+~(O087h>2<j>>NKi|yr*O`v59ud@g!qI7zokJzJt{BszjJC4fg-af`e@3nX?7M46z$&-<dFA$RwD%Vu
+G)Ckpj`KiDmdXIQa+qRLFJxmbGg`pYaatbMKptd9f9BY~qroObyT9Hq_WwwDzW{y;24P8>6U4yDopR0
+Sa#)Tgx8Y-_)2i>62^}WhdU)I!<N6&!{dAWt%6|214}QAaq+|M!Wk4%WZ%HzS<i+VWCqXL0KZ6z(-aw
+9TH&guFjd@~iKhE2UTf@%p!aAICHlxp8R+0zoSk>}9PAv!15r0;jF%9_@)3qIi6W~X%&+9@0<J!04x>
+=7R(v~-ur5uh?K|@n-tjwRHia$1xvk5G4tDS$j$xpzKck=Gnu#DG!5IS;-%LKmLInZg9Oi>R#1pPC#o
+&fX|oRG8sypxBbAHlv_w;kS40?r-9B)oVjl~zZSOc^Werqbm1QbQYPCnMnGl3SpHeoF8kXJx)J#nfXl
+)sw5+!iVLS9PaHMMHHFp@4*43TIxO?JgYC}CbM@iPX4@4@)w@lG(q~|Bp}-dTqZ<0e>jW8xny<cVrih
+dDz|&EwCpOFXPiJvsin3k5B)r&{1@7f8$7Qb+?SHxMHi;Ll^Iu=mCPsbu;-eyf06VM7r`*Qi@;=w+zv
+v1-bnZhD+rq|^xvhkI!sy^pELGShF7P=u_o{z)rrf8YvW<G_y`xgKvcZcwyX<b9}u5z*Dw7LzP51hoV
+g!u8Hw5sc8XZxE(vNAk;?b$fkg>)HA_cEWDmRMTRlX*;uUoqe|+tmhfG5rGMP`qwldOYr`jx^TqS}KJ
+X{LTGE-kzM=PL+-XXur$iL7wX%x6a_&R<4tvkETmBr7uK&Cbk06}SBFKkJKx3%`X!|FMPu6A>?=}Zj!
+;3(tkIm~z@gd^(RK`t!nIiUeYf~)vw-rO_c09R;py|bc|GSJIAocNi19{!j9RypwPq%B#nN{b>6aAOT
+c-`=iE)xg~mCDSw7JH8>x`SE#O<7YATMDyI${cvhH^i4>+oG%j>F8quN&aCcz_#88bE-{wUtfhW+O{}
+Yo`sD?PPpCNkuu6;gq08y!E02tq8EDvVE~Z{BWtgET(vME)7}x7qjD)<~o{0PhrwluPeBPLRJtEc|q^
+6(M%c|wGKKrNRd2twUN=A_C@}=|SW7McoSdH~du70BRqxZ$@IUZIxkp!P#GflgjRofbdz6G`07>R0h-
+<Q&SE001;UVglU^zR)IfcOveANW7dGfF^OMfLw;xeKEo)$36?|EKGRpXeEJs2$M6vSd^5`uz&m_~Gy%
+Y{H8H_R8Ta!6i7VU;Sn)=>I+*{Rw+3QoF@z>hyWEWte|~LqLdHdxsCn(h@p3g>0>MdMvNJ1T02p)XRX
+hJX{tdd-vUQ%iNV_=_~z9XPg?8L7SV-%8`eaYVzRy6^`d^{-_wutdQr;K%v$$MK_WAcN_2%>aVxrWPM
+%zgf0OcVr*kAvkV?ifimP=%%nI?pmj<cZr=@N+4x{gQpTnzsc)bguls;{rS5Yha;(VRFDxl($Jnt2G2
+gyOV40r`vgZJQzdmx=-~D|l`xB<lF^bOac3=N@s2isUADbL|nfrAd#}Lh{dJzF*Qq@~bM&WuPOs2buT
+1TtI8lK1L!E5mj{TkO2I8OSBM?ZAX3a<_8&Q~18yaMC3**M{6wZ9fUJPeJ05Mv~RZqFAuY^(PF08mQ-
+0u%!j000080P~d=M}*7(0hSE_0Inzi01p5F00000000000HlEc0001RX>c!JUukY>bYEXCaCuNm0Rj{
+Q6aWAK2mtey7Dr1-q-8S!002k=000jF0000000000005+c!wmocaA|NaUteuuX>MO%E^v8JO928D0~7
+!N00;o{l@>>yz1OOy3jhFlDF6Tx00000000000001_fg=w90B~t=FJfVHWn*t`ZDDR?E^v8JO928D0~
+7!N00;o{l@>?ZFvLBk3;+P(C;$Kv00000000000001_f%zH$0B~t=FJfVHWpH6~b7gWaaCuNm0Rj{Q6
+aWAK2mtey7DrVyt(^l9003<{000pH0000000000005+c&L;o>aA|NaV{K$_aCB*JZgVbhc~DCM0u%!j
+000080P~d=M*_3X#M2@G0Kj|z01p5F00000000000HlEcH~;`}X>c!OZ+C8NZ((FEaCuNm0Rj{Q6aWA
+K2mtey7Dw4?P5ssZ008L(000pH0000000000005+c09yb6aA|NaWq4y{aCB*JZgVbhc~DCM0u%!j000
+080P~d=M}lBlphYnN0Oz^@01N;C00000000000HlEhT>t=ZX>c!TZe(S6E^v8JO928D0~7!N00;o{l@
+>=!`MGmE3IG7~82|tf00000000000001_fpm@l0B~t=FKlmPVRUJ4ZgVbhc~DCM0u%!j000080P~d=N
+AFROtn~!|080@701*HH00000000000HlH5m;eB9X>c!aWpFeyHFRNTb1rasP)h*<6ay3h000O8^OY7y
++pKr?eE<LeoB#j-6951J0000000000q=5pT003}la4&OoVRUtKUt@1%WpgfYc~DCM0u%!j000080P~d
+=N22h{*OVjx0P1xB01N;C00000000000HlGip8x=GX>c!hXk}$=E^v8JO928D0~7!N00;o{l@>>tv|7
+Pq1ONcc2><{W00000000000001_fo{YA0B~t=FJE?LZe(wAFJE72ZfSI1UoLQYP)h*<6ay3h000O8^O
+Y7yhqJe*GdlnPD>nfE7ytkO0000000000q=5#>003}la4%nWWo~3|axZCcVPs@-Wpi^baCuNm0Rj{Q6
+aWAK2mtey7Dr$n2aRR{000F8000;O0000000000005+cZv+7VaA|NaUv_0~WN&gWa%C-cWo~3|axQRr
+P)h*<6ay3h000O8^OY7yA$z<E&j|nk^d<lR7XSbN0000000000q=5?t0RV7ma4%nWWo~3|axZdabaHu
+VZf7oVc~DCM0u%!j000080P~d=M`CG)PT?E?0N`@~01^NI00000000000HlEr5CH&iX>c!Jc4cm4Z*n
+hlX?QMhc~DCM0u%!j000080P~d=M|TE-#sUEV05k#s03rYY00000000000HlE+Edc;<X>c!Jc4cm4Z*
+nhRZDDe2WpZq3VlQ7`X>MtBUtcb8c~DCM0u%!j000080P~d=M=lfGq{amR0E-U*03iSX00000000000
+HlFpE&%{=X>c!Jc4cm4Z*nhRZDDe2WpZq3VlQoBa%*LBb1rasP)h*<6ay3h000O8^OY7yocgHmt^fc4
+E&%`lBme*a0000000000q=9EN0RV7ma4%nWWo~3|axY_HV`yb#Z*FvQZ)`7LUukY>bYEXCaCuNm0Rj{
+Q6aWAK2mtey7Dw@rbX@ra003kV001KZ0000000000005+cS2Y0uaA|NaUv_0~WN&gWV_{=xWn*t{baH
+QOFJWY1aCBvIE^v8JO928D0~7!N00;o{l@>=5s>bjv0RR9N0{{Ra00000000000001_fsr}^0B~t=FJ
+E?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`ybAaCuNm0Rj{Q6aWAK2mtey7DvLpK6;V>001`u001HY000000
+0000005+c{5t^vaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJo_QaA9;VaCuNm0Rj{Q6aWAK2mtey7DvlK
+7a~Fo004|9001Tc0000000000005+c&O8ACaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJo_RbaHQOY-Ms
+TaCuNm0Rj{Q6aWAK2mtey7Dqt>BlV#J004Lh001Wd0000000000005+cR7n8<aA|NaUv_0~WN&gWV_{
+=xWn*t{baHQOFJ@_MWp{F6aByXEE^v8JO928D0~7!N00;o{l@>>v9aWsL1polm4*&or000000000000
+01_fjUhA0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%geKb#iHQbZKLAE^v8JO928D0~7!N00;o{l@><~C
+W4j62LJ#q7ytkz00000000000001_fi6=40B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%g<Va%o{~X?kTY
+aCuNm0Rj{Q6aWAK2mtey7DtPF#X`IQ008m<001KZ0000000000005+cI9mY#aA|NaUv_0~WN&gWV_{=
+xWn*t{baHQOFL!cbaByXEE^v8JO928D0~7!N00;o{l@>>y9aj9y0001S0RR9i00000000000001_fi+
+wK0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7y!K
+ViQTLl0B%nbklEdT%j0000000000q=89Y0RV7ma4%nWWo~3|axY_HV`yb#Z*FvQZ)`7PVPj}zb1!CTY
+-L|#VPj}zE^v8JO928D0~7!N00;o{l@>?Sh{jZ=0RRBa0{{Rn00000000000001_f$w4g0B~t=FJE?L
+Ze(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FLGsMX>(s=VPj}zE^v8JO928D0~7!N00;o{l@>=YN2j8v0002
+t0000W00000000000001_f#GBU0B~t=FJE?LZe(wAFJo_PZ*pO6VJ}}_X>MtBUtcb8c~DCM0u%!j000
+080P~d=N0+05?KA@b000R902}}S00000000000HlG&WdQ(iX>c!Jc4cm4Z*nhVZ)|UJVQpbAVQzD2E^
+v8JO928D0~7!N00;o{l@>?-akwnw2><{tBme*-00000000000001_fi!6W0B~t=FJE?LZe(wAFJo_PZ
+*pO6VJ~5Bb7^#McWG`jGA?j=P)h*<6ay3h000O8^OY7y?qbWv3IYHCJq7>(A^-pY0000000000q=8s-
+0RV7ma4%nWWo~3|axY_VY;SU5ZDB8IZfSIBVQgu0WiD`eP)h*<6ay3h000O8^OY7yv#FkMF9iSq0Tci
+L9RL6T0000000000q=BM!0RV7ma4%nWWo~3|axY_VY;SU5ZDB8WX>KzzE^v8JO928D0~7!N00;o{l@>
+>ovL_)c1pokK761Sr00000000000001_fed>A0B~t=FJE?LZe(wAFJo_PZ*pO6VJ~-SZggdGZ7y(mP)
+h*<6ay3h000O8^OY7ygBg4GT>$_9MFIc-9{>OV0000000000q=9sR0RV7ma4%nWWo~3|axY|Qb98KJV
+lQ7`X>MtBUtcb8c~DCM0u%!j000080P~d=N4dg6YELQv0MwoU0384T00000000000HlEqf&l<<X>c!J
+c4cm4Z*nhWX>)XJX<{#9Z*6d4bS`jtP)h*<6ay3h000O8^OY7y&X<4#el7q2bie=r9{>OV000000000
+0q=A^M0RV7ma4%nWWo~3|axY|Qb98KJVlQN2bYWs)b7d}Yc~DCM0u%!j000080P~d=N8$#~TnQBb0Ps
+-&02}}S00000000000HlFa+W`P@X>c!Jc4cm4Z*nhWX>)XJX<{#FZe(S6E^v8JO928D0~7!N00;o{l@
+>>zUvLR8G5`Rp!~g&v00000000000001_ftK(A0B~t=FJE?LZe(wAFJx(RbZlv2FKlmPVRUbDb1rasP
+)h*<6ay3h000O8^OY7yp$jB<Ko0-_9yI^}9{>OV0000000000q=5n>0swGna4%nWWo~3|axY|Qb98KJ
+VlQoBZfRy^b963nc~DCM0u%!j000080P~d=M?Dd)0*wa%0E!p@03HAU00000000000HlF`G6Dc_X>c!
+Jc4cm4Z*nhWX>)XJX<{#JVRCC_a&s<lc~DCM0u%!j000080P~d=NBEuZi)$kQ0IHP$03QGV00000000
+000HlFPIsyQ2X>c!Jc4cm4Z*nhWX>)XJX<{#JWprU=VRT_GaCuNm0Rj{Q6aWAK2mtey7DsQxo_Tr;00
+4I>001BW0000000000005+c@Ld7`aA|NaUv_0~WN&gWWNCABY-wUIa%FRGb#h~6b1rasP)h*<6ay3h0
+00O8^OY7ymRfBkJP`l@XEy)<9smFU0000000000q=Beu0swGna4%nWWo~3|axY|Qb98KJVlQ)Ja%pgM
+b1rasP)h*<6ay3h000O8^OY7y;{G(?r>Xz|09*k88vp<R0000000000q=6uN0swGna4%nWWo~3|axY|
+Qb98KJVlQ+vGA?C!Wl&220u%!j000080P~d=M{H`n3{|)Q004dg02=@R00000000000HlEcCIbL)X>c
+!Jc4cm4Z*nhWX>)XJX<{#QHZ(3}cx6ya0Rj{Q6aWAK2mtey7Ds=j6_hMF002J8000{R000000000000
+5+cj@|<RaA|NaUv_0~WN&gWWNCABY-wUIb#!TLE^v8JO928D0~7!N00;o{l@>=C7H%S}82|vETmS$b0
+0000000000001_f%6vx0B~t=FJE?LZe(wAFJx(RbZlv2FLq^eb7^mGE^v8JO928D0~7!N00;o{l@>>S
+MSulFrT_o{P5}TL00000000000001_f!i?z0B~t=FJE?LZe(wAFJx(RbZlv2FLyICE@gOSP)h*<6ay3
+h000O8^OY7y)W-_X6}12W0B`{S8vp<R0000000000q=8u21ORYpa4%nWWo~3|axY|Qb98KJVlQ_#G%j
+U$Wl&220u%!j000080P~d=M^Il|Ed3+^0E3tS02}}S00000000000HlGYjRgR3X>c!Jc4cm4Z*nhWX>
+)XJX<{#TXk}$=E^v8JO928D0~7!N00;o{l@>=M?`QnO0000I0RR9g00000000000001_f!(qN0B~t=F
+JE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|WUukY>bYEXCaCuNm0Rj{Q6aWAK2mtey7Dum&FJ;aF007Ga
+001Qb0000000000005+c=d%R>aA|NaUv_0~WN&gWWNCABY-wUIUt(cnYjAIJbT4gbb7L-Wc~DCM0u%!
+j000080P~d=M>MI?%19gl05W6%04D$d00000000000HlHVwFLlhX>c!Jc4cm4Z*nhWX>)XJX<{#5Vqs
+%zaBp&SFLP*hbZKlZaCuNm0Rj{Q6aWAK2mtey7DsOu0Il``0021(001ih0000000000005+ch0+B8aA
+|NaUv_0~WN&gWWNCABY-wUIUt(cnYjAIJbT4yxb7OCAW@%?GV`gViO928D0~7!N00;o{l@>=4KS&_x9
+smGNX#fB!00000000000001_fyC4W0B~t=FJE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|qd2?fLZf0p`
+E^v8JO928D0~7!N00;o{l@>>#LpWPwSpWdjX#oHz00000000000001_f%NkQ0B~t=FJE?LZe(wAFJx(
+RbZlv2FJEF|V{344a&#|rVRB|^Y-KKRc~DCM0u%!j000080P~d=N2Z$lEQ$dD0LlUY03ZMW00000000
+000HlGPO$GpPX>c!Jc4cm4Z*nhabZu-kY-wUIUtei%X>?y-E^v8JO928D0~7!N00;o{l@>>jmU58HNd
+N#@Qvm=W00000000000001_fn`qy0B~t=FJE?LZe(wAFKBdaY&C3YVlQKFZgX^DZgg`laCuNm0Rj{Q6
+aWAK2mtey7Dokc9IXFD003jk0RSTa0000000000005+caG3@GaA|NaUv_0~WN&gWXmo9CHEd~OFKBda
+Y&CFUa&u*JE^v8JO928D0~7!N00;o{l@>=j&nXNA6aWClKmY(B00000000000001_fw0~N0B~t=FJE?
+LZe(wAFKBdaY&C3YVlQcEVRU79ZEP-Zc~DCM0u%!j000080P~d=N1bYj3DX_`0I_=j03!eZ00000000
+000HlHQ@&*8KX>c!Jc4cm4Z*nhabZu-kY-wUIX>M?JbaQlaWnpbDaCuNm0Rj{Q6aWAK2mtey7Dve{3;
+RYC008_%001EX0000000000005+c2NDMWaA|NaUv_0~WN&gWXmo9CHEd~OFLPmTX>@6NWpXZXc~DCM0
+u%!j000080P~d=M^Yzq><b+L0D&w403iSX00000000000HlG8CI<j;X>c!Jc4cm4Z*nhabZu-kY-wUI
+bZ={AZfSaDaxQRrP)h*<6ay3h000O8^OY7y9v<BwV+8;J!wdib9RL6T0000000000q=D8$2LNzsa4%n
+WWo~3|axZ9fZEQ7cX<{#RbZKmJE^v8JO928D0~7!N00;o{l@><;00002000000000e0000000000000
+1_fpkd+0B~t=FJE?LZe(wAFKBdaY&C3YVlQTCY;<LEb1z?CX>MtBUtcb8c~DCM0u%!j000080P~d=M<
+Rgx#FYR503HDV03`qb00000000000HlGuNe2LMX>c!Jc4cm4Z*nhabZu-kY-wUIW@&76WpZ;bUt(c%W
+iD`eP)h*<6ay3h000O8^OY7ye-4mNAOQdXZ~_1THUIzs0000000000q=A%52LNzsa4%nWWo~3|axZ9f
+ZEQ7cX<{#CX>4?5a&s?XY;b5{Vr6t`V_|GzbaZlQVs&(7b1rasP)h*<6ay3h000O8^OY7y6?b_$xB>t
+Gx(WaQGXMYp0000000000q=5)b2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?fZfa#?bYE>{bYW
+j(Xkl`5WpplZc~DCM0u%!j000080P~d=M-Z9Hz@P&F0B{fh03-ka00000000000HlEtPX_>SX>c!Jc4
+cm4Z*nhabZu-kY-wUIW@&76WpZ;bY-w(EE^v8JO928D0~7!N00;o{l@>?QlWz#g1^@sADF6U0000000
+00000001_f%8%a0B~t=FJE?LZe(wAFKBdaY&C3YVlQTCY;<LEb1!djbZKvHVQh3^XLBxac~DCM0u%!j
+000080P~d=M>wzKp|Jn}0AK+C04V?f00000000000HlEiS_c4dX>c!Jc4cm4Z*nhabZu-kY-wUIW@&7
+6WpZ;bb75|2bZL5JaxQRrP)h*<6ay3h000O8^OY7yW3^fp&;bAdb_4(bDgXcg0000000000q=Eig2LN
+zsa4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?tXlZn1b8ul}WiD`eP)h*<6ay3h000O8^OY7y0x`qn5C
+H%H>Hz=%D*ylh0000000000q=6P)2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#PWpZg@Y-xIBaxY(BX>MtBU
+tcb8c~DCM0u%!j000080P~d=NB8lKOLh$a05>uK05Jdn00000000000HlF$UIzehX>c!Jc4cm4Z*nha
+bZu-kY-wUIb7gXAVQgu7WpXcQbZu;NWpZg@Y-xIBaxQRrP)h*<6ay3h000O8^OY7y000000ssI20000
+0EdT%j0000000000q=7VS2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*<WMOc0WpZ;bUtei%X>?y-E^v
+8JO928D0~7!N00;o{l@>?jTwf3x0ssJg1^@sk00000000000001_fq`rX0B~t=FJE?LZe(wAFKBdaY&
+C3YVlQ-ZWo2PxVQ_S1a&s?pVR$ZZc~DCM0u%!j000080P~d=N1#lH<>CYY08I@504)Fj00000000000
+HlH8ZU+ExX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJE72ZfSI1UoLQYP)h*<6ay3h000O8
+^OY7yy9}waLk<7{k2L@QDgXcg0000000000q=61}2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*=b!lv
+5WpZ;bUt(c%WiD`eP)h*<6ay3h000O8^OY7yVJa~kmj?g<6(IlsC;$Ke0000000000q=A`&2LNzsa4%
+nWWo~3|axZ9fZEQ7cX<{#Qa%E*=b!lv5WpZ;bWN&RQaCuNm0Rj{Q6aWAK2mtey7Dr&udnW`6002!f00
+1fg0000000000005+cc8Ui8aA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWny({Y-D9}b1!9da%E*MaCuNm0
+Rj{Q6aWAK2mtey7DtnxCHo}}007T6001ul0000000000005+c!juO9aA|NaUv_0~WN&gWXmo9CHEd~O
+FLZKcWny({Y-D9}b1!9da%E*-Y<O*KE^v8JO928D0~7!N00;o{l@>>zHhndQ1^@tt761S&000000000
+00001_fi$8A0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1z?CX>MtBUtcb8c~DCM0u%!j000
+080P~d=N6o`DuL1@D0A?Ei04V?f00000000000HlEgsRsaXX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7cV
+TR6WpZ;bUt(c%WiD`eP)h*<6ay3h000O8^OY7yov#Vw`2hd`jRgPzCjbBd0000000000q=8MZ2LNzsa
+4%nWWo~3|axZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?aZ*4Acc~DCM0u%!j000080P~d=M+5NmDCPtJ0PYb0
+04V?f00000000000HlG7u?GNfX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7cVTR6WpZ;bWpr|7WiD`eP)h*
+<6ay3h000O8^OY7yEf}!Z!2$pP<_G`)FaQ7m0000000000q=C4$2LNzsa4%nWWo~3|axZ9fZEQ7cX<{
+#Qa%E+AVQgz<a&s?dWo~n5X>)XPWnpbDaCuNm0Rj{Q6aWAK2mtey7DqE4*c~Yb003wg001oj0000000
+000005+c$GHapaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWp`n0Yh`kCFKl>iY-MzEWo0gKc~DCM0u%!j0
+00080P~d=M|C+zP`m;F0R9L704o3h00000000000HlFCzy|<uX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7
+cVTR6WpZ;baCK~KWN&RQaCuNm0Rj{Q6aWAK2mtey7DuU7-Kd5D007hg001Qb0000000000005+cK*I+
+BaA|NaUv_0~WN&gWXmo9CHEd~OFLZKgWiMY}X>MtBUtcb8c~DCM0u%!j000080P~d=NAzNalYIdI0G|
+T@03rYY00000000000HlEl#0LOyX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BFJEF|b7d}Yc~DCM0u%!j00
+0080P~d=M<i99sipw{0Gb2<03!eZ00000000000HlG&#RmXzX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BF
+JxhKa%p8QaCuNm0Rj{Q6aWAK2mtey7Dr1@Zq6M7008d>0018V0000000000005+ct;YudaA|NaUv_0~
+WN&gWXmo9CHEd~OFLZKgWiN1fE^v8JO928D0~7!N00;o{l@>?!f28$T3IG7gBLDy(00000000000001
+_fd|S50B~t=FJE?LZe(wAFKlmPYi4O|WiMY}X>MtBUtcb8c~DCM0u%!j000080P~d=M?w%8Hy;B409g
+tE03-ka00000000000HlGP)CT}?X>c!Jc4cm4Z*nheZ)0m_X>4ULY-w(5Y;R+0W@&6?E^v8JO928D0~
+7!N00;o{l@>?dx-=4S0{{RT3;+Nn00000000000001_f&SMA0B~t=FJE?LZe(wAFKlmPYi4O|WiM@OW
+NC72Z)0m_X>4UKaCuNm0Rj{Q6aWAK2mtey7DuGbAQDCh002!G001KZ0000000000005+cvD*g#aA|Na
+Uv_0~WN&gWY;R+0W@&6?FK}sOY;R+0W@&6?E^v8JO928D0~7!N00;o{l@>?mQhD|d1pok~6#xJx0000
+0000000001_fjHy`0B~t=FJE?LZe(wAFKlmPYi4O|WiNAaY-x05Y;R+0W@&6?E^v8JO928D0~7!N00;
+o{l@>==Q$=h%0{{Ru3IG5n00000000000001_fs5$}0B~t=FJE?LZe(wAFKlmPYi4O|WiNAiZER_7Yi
+w_0Yi4O|WiD`eP)h*<6ay3h000O8^OY7yh-{tOM*;u<F$4erA^-pY0000000000q=5|W2LNzsa4%nWW
+o~3|axZXUV{2h&X>MmPUteKjZ*_EEUoLQYP)h*<6ay3h000O8^OY7ySk>pY`~d&}iUR-uApigX00000
+00000q=A#~2LNzsa4%nWWo~3|axZXUV{2h&X>MmPUtei%X>?y-E^v8JO928D0~7!N00;o{l@>>5N4=Y
+{0ssKz1ONaa00000000000001_fzI&<0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rFV{dJ6VRSBVc~DCM0u%
+!j000080P~d=N3N1NwN(ND01*cO03-ka00000000000HlGu^alWNX>c!Jc4cm4Z*nhiVPk7yXK8L{FJ
+E(Xa&=>Lb#i5ME^v8JO928D0~7!N00;o{l@>>gf||7z82|vUZ2$lx00000000000001_fm8Pf0B~t=F
+JE?LZe(wAFK}UFYhh<;Zf7rZaAjj@W@%+|b1rasP)h*<6ay3h000O8^OY7y=mr%YMh*Y~tu6onAOHXW
+0000000000q=Bgr2mo+ta4%nWWo~3|axZXUV{2h&X>MmPc4cyNX>V>WaCuNm0Rj{Q6aWAK2mtey7Dt#
+=rN?M+0082j0RSZc0000000000005+cEguK~aA|NaUv_0~WN&gWaBF8@a%FRGb#h~6b1z?CX>MtBUtc
+b8c~DCM0u%!j000080P~d=NBUZEjFJQZ0M82m03ZMW00000000000HlG~j|c#8X>c!Jc4cm4Z*nhia&
+KpHWpi^cUtei%X>?y-E^v8JO928D0~7!N00;o{l@><>ChsIh1ONb&3IG5b00000000000001_fvA)S0
+B~t=FJE?LZe(wAFK}{iXL4n8b1!0HaxQRrP)h*<6ay3h000O8^OY7yE=aa)B?ABe`2_#~9{>OV00000
+00000q=6@y2mo+ta4%nWWo~3|axZXlZ)b94b8|0aZ*^{TWpXZXc~DCM0u%!j000080P~d=M;w`>M+gJ
+}08R@403QGV00000000000HlG5oCpALX>c!Jc4cm4Z*nhia&KpHWpi^cXk~10WpZ;aaCuNm0Rj{Q6aW
+AK2mtey7DtLR+g|<x002D&0018V0000000000005+c&Y%bYaA|NaUv_0~WN&gWaB^>Fa%FRKFLQ8dZf
+<3AE^v8JO928D0~7!N00;o{l@>=vjs?i30{{R;2LJ#d00000000000001_feEAt0B~t=FJE?LZe(wAF
+LGsZb!BsOb1z?CX>MtBUtcb8c~DCM0u%!j000080P~d=M^Wr;Om`6g0IER%03ZMW00000000000HlHK
+rw9OWX>c!Jc4cm4Z*nhkWpQ<7b98erVPs)&bY*gLE^v8JO928D0~7!N00;o{l@>?Jp62K~1poji6#xJ
+m00000000000001_fugwx0B~t=FJE?LZe(wAFLGsZb!BsOb1z|VX)bViP)h*<6ay3h000O8^OY7yDP{
+LPeh2^niW&d_8~^|S0000000000q=6Q{2mo+ta4%nWWo~3|axZdaadl;LbaO9Zb#!PhaCuNm0Rj{Q6a
+WAK2mtey7Dv-iT7FD<0szvq1OOiZ0000000000005+c&c_G<aA|NaUv_0~WN&gWa%FLKWpi|MFJob2W
+pZ>baAj>!O928D0~7!N00;o{l@>>>F)xm00RRAH0ssIV00000000000001_fmcBb0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!3Ma&&VpaCuNm0Rj{Q6aWAK2mtey7DrUx#oRCg006xO0015U0000000000005+c_(BT
++aA|NaUv_0~WN&gWa%FLKWpi|MFJo_QaA9;VaCuNm0Rj{Q6aWAK2mtey7DvoU5zOBb001XL0018V000
+0000000005+cWJL=AaA|NaUv_0~WN&gWa%FLKWpi|MFJo_SYiVV3E^v8JO928D0~7!N00;o{l@>?R9v
+nbq0{{Tk2><{h00000000000001_fq+;G0B~t=FJE?LZe(wAFLGsZb!BsOb1!9hV`Xr3X>V?GE^v8JO
+928D0~7!N00;o{l@>=AcKy7I0RR9r0{{RW00000000000001_fh1fD0B~t=FJE?LZe(wAFLGsZb!BsO
+b1!IbZ)<ZdaCuNm0Rj{Q6aWAK2mtey7Dx3e?fB*(008)L0015U0000000000005+c>Rk%}aA|NaUv_0
+~WN&gWa%FLKWpi|MFKusRWo&aUaCuNm0Rj{Q6aWAK2mtey7Ds(S{wRtX006pP001BW0000000000005
++c41Ws%aA|NaUv_0~WN&gWa%FLKWpi|MFLPycb7^mGb1rasP)h*<6ay3h000O8^OY7yDw@gO<pcl#fD
+8ZtBme*a0000000000q=D3#3jlCwa4%nWWo~3|axZdaadl;LbaO9rbYXOLb6;a`WMy+MaCuNm0Rj{Q6
+aWAK2mtey7DsH838QTU005y2001HY0000000000005+c`<@E`aA|NaUv_0~WN&gWa%FLKWpi|MFLQKq
+bz^jOa%FQaaCuNm0Rj{Q6aWAK2mtey7DwZ>xD{O+0034~0012T0000000000005+csG|!2aA|NaUv_0
+~WN&gWa%FLKWpi|MFLiWjY;!Jfc~DCM0u%!j000080P~d=M~^A$<ud>P06qW!04V?f00000000000Hl
+FHzY73xX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7yac
+oNbjRF7w4+Q`KF#rGn0000000000q=CA>3jlCwa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=W
+My<OUtei%X>?y-E^v8JO928D0~7!N00;o{l@>>FoWC)5kpKXqLID6V00000000000001_ftJDx0B~t=
+FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoMX=gQNa%FKYaCuNm0Rj{Q6aWAK2mtey7DxV
+o83Y>x005K*001)p0000000000005+cTvQAIaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB
+?;bT49QXEktgZ(?O~E^v8JO928D0~7!N00;o{l@>>NvR7F%1ONcY2><{y00000000000001_fyh@30B
+~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoNXkl_>Wprg@bS`jtP)h*<6ay3h000O8^
+OY7y_>jT?4haAN3M2pkIRF3v0000000000q=8Ia3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQ
+V`yP=WMy<OV`yP=WNCABa%p09bZKvHE^v8JO928D0~7!N00;o{l@>=iiKU-(1ONcf4gdf<000000000
+00001_fw5%_0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoNXkl`5Wprn9Z*_2Ra&K
+Z~axQRrP)h*<6ay3h000O8^OY7y_!;PfTLS<9ZU+DWHUIzs0000000000q=9~F3;=Lxa4%nWWo~3|ax
+Zdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OV`yP=b7gdJa&KZ~axQRrP)h*<6ay3h000O8^OY7yz}XNq-
+~#{v4haANI{*Lx0000000000q=7AN3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<O
+V{c?>ZfA3JVRU6}VPj}%Ze=cTc~DCM0u%!j000080P~d=M<Dy%IZ*-t0EGkq051Rl00000000000HlF
+tatr`)X>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJo_QaA9;VaCuNm0Rj{Q6aWAK2mt
+ey7DxP5z|Xb<001`y001%o0000000000005+c1a%AmaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1
+!3PVRB?;bT4CXZgX^DZgg`laCuNm0Rj{Q6aWAK2mtey7DuZFCdNzy008y|001-q0000000000005+c3
+U~|vaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4CYIW#$Na&KZ~axQRrP)h*<6ay3h
+000O8^OY7yrY+JraRdMWa|{3gG5`Po0000000000q=Be=3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7
+yXJvCQV`yP=WMy<OWpiV2a&KZ~axQRrP)h*<6ay3h000O8^OY7y)=2%#$pruapB?}JE&u=k00000000
+00q=9LG3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OWpiV5Z7y(mP)h*<6ay3h00
+0O8^OY7ytbDvh3k3iGUJd{NGynhq0000000000q=9~h3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yX
+JvCQV`yP=WMy<OWp!g}aBy;OVr6nJaCuNm0Rj{Q6aWAK2mtey7Dp=NE|ITO005e^001%o0000000000
+005+c+lveUaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4IfV{39|a%FKYaCuNm0Rj{
+Q6aWAK2mtey7DolM#^f0T004^y001-q0000000000005+c+TRQSaA|NaUv_0~WN&gWa%FLKWpi|MFK}
+UFYhh<)b1!3PVRB?;bT4IfV{3A7a&KZ~axQRrP)h*<6ay3h000O8^OY7yB~POi-#`EWIEVlMG5`Po00
+00000000q=7}_3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OWp!h8cV=>BaV~IqP
+)h*<6ay3h000O8^OY7yEuuJ783O<Sj0OMzGynhq0000000000q=9rK4FGUya4%nWWo~3|axZdaadl;L
+baO9oVPk7yXJvCQV`yP=WMy<OWp!h8cW`oVVr6nJaCuNm0Rj{Q6aWAK2mtey7DqXa^bPAm0066u001)
+p0000000000005+c-zN<KaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4OOGBYtUW^!
+e5E^v8JO928D0~7!N00;o{l@>=IwcGX@0{{S#1^@sx00000000000001_fg5WL0B~t=FJE?LZe(wAFL
+GsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoRVlp!^GH`NlVr6nJaCuNm0Rj{Q6aWAK2mtey7DwZfttM*{0
+01vE001=r0000000000005+ch;9u4aA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4RS
+Vsd47aB^>AWpXZXc~DCM0u%!j000080P~d=N0+af4!%|Z0Mobt05AXm00000000000HlFNfeip~X>c!
+Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FKTIXW^!e5E^v8JO928D0~7!N00;o{l@>=Eh2+
+Rf6952|O8@{b00000000000001_fl}BF0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>Wp
+poUaAR(CcrI{xP)h*<6ay3h000O8^OY7yNKy%m3=04N@G$@YI{*Lx0000000000q=D+|4FGUya4%nWW
+o~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OY+-I^Vs&h1VRC6<Zf$R5Wo#~Rc~DCM0u%!j00008
+0P~d=N1UFwWBm&N06j$j06G8w00000000000HlFW_YDAWX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D
+?FJow7a%5$6FKl6MXJdJCX>4q1V{LC_Wo#~Rc~DCM0u%!j000080P~d=M-d@I9c2js08}vm05<>t000
+00000000HlGW0}cRiX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FKl6MXJ>L{WovD3WM
+ynFaCuNm0Rj{Q6aWAK2mtey7DpeVdkUTj001^D001}u0000000000005+cUJVWaaA|NaUv_0~WN&gWa
+%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4dSZf9s^Vsd47ZEs{{Y%XwlP)h*<6ay3h000O8^OY7yP`8?X
+#|i)d_%HwfI{*Lx0000000000q=8Zw4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<
+OY+-I^XmxI9VRC6<Zf$R5Wo#~Rc~DCM0u%!j000080P~d=M@v0_UF8Y@01GSt05$*s00000000000Hl
+F(A`Sp>X>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FKl6MXLM*`X>D(0Wo#~Rc~DCM0u
+%!j000080P~d=N9CHTnR^BR0B{rl05t#r00000000000HlGdEe-&1X>c!Jc4cm4Z*nhkWpQ<7b98era
+A9L>VP|D?FJow7a%5$6FKl6SX>Kuaa&KZ~axQRrP)h*<6ay3h000O8^OY7yrJQkdodf^?#0&rcH~;_u
+0000000000q=9)f4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OZDM0+VRCb2bZ~N
+SVr6nJaCuNm0Rj{Q6aWAK2mtey7DvvEk2_fd006HC001}u0000000000005+cY&i}9aA|NaUv_0~WN&
+gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4gUV{>P6Z*_2Ra&KZ~axQRrP)h*<6ay3h000O8^OY7y@#
+SZlObh@3hD-neF8}}l0000000000q=6ef4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=W
+My<OZDM0{b8Rkgc~DCM0u%!j000080P~d=NBSBSxOxTv0J#zX05|{u00000000000HlGmNe%#TX>c!J
+c4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FLPpJXkl`5Wpr?IZ(?O~E^v8JO928D0~7!N00;o
+{l@>>&+~vO{1ONcr3;+N)00000000000001_frL;F0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpg
+iMXkl_>WppodVq<e>a&L8TaB^>AWpXZXc~DCM0u%!j000080P~d=N0(vnR{aD30JIJO05bpp0000000
+0000HlHbR1N@eX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FLP>Xb8vERVr6nJaCuNm0
+Rj{Q6aWAK2mtey7Dug|Hab@a006ie0024w0000000000005+cOj-^AaA|NaUv_0~WN&gWa%FLKWpi|M
+FK}UFYhh<)b1!3PVRB?;bT4&oX?A6Db75>`Wprg@bZ>GlaCuNm0Rj{Q6aWAK2mtey7Dr$EAZs23003M
+H001)p0000000000005+c`(X|MaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4&uW;k
+$iZ(?O~E^v8JO928D0~7!N00;o{l@>>Mw-zsg0{{RA2mk;v00000000000001_fof(B0B~t=FJE?LZe
+(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvaUukY>bYEXCaCuNm0Rj{Q6aWAK2mtey7DpLEEQieu0
+07n~001=r0000000000005+cJ8BL9aA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR9b
+Z)|L3V{~b6ZgVbhc~DCM0u%!j000080P~d=M^e;8_>~I)05c;105bpp00000000000HlFicMbq>X>c!
+Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJo_RZe?S1X>V>WaCuNm0Rj{Q6aWAK2mtey7Dq
+?CMmX~z003`x001`t0000000000005+cL4yteaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;
+0*_GcRLrZf<2`bZKvHaBpvHE^v8JO928D0~7!N00;o{l@>=HHY}u+1pojR5dZ))00000000000001_f
+r+FJ0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvgcw=R7bZKvHb1rasP)h*<6ay3h
+000O8^OY7y^G0R)1qT2C$rb<rF8}}l0000000000q=9a#4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7
+yXJvCQb#iQMX<{=kW@%+?WOFWXc~DCM0u%!j000080P~d=M{E%hAUXp80O<$-05Jdn00000000000Hl
+Gyu?_%mX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJ@_MWpHnEbS`jtP)h*<6ay3h00
+0O8^OY7yTdH&$HVXg%z9awuGynhq0000000000q=8Ab4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yX
+JvCQb#iQMX<{=kaBpvHZDDR<XJv9OaCuNm0Rj{Q6aWAK2mtey7DwjY!he7V004Iu001xm0000000000
+005+c(ZCJ>aA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR&wadl;LbS`jtP)h*<6ay3
+h000O8^OY7yLjoE44-x<XOF#esF#rGn0000000000q=BEv4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk
+7yXJvCQb#iQMX<{=ka%FRHZ*FsCE^v8JO928D0~7!N00;o{l@><;00002000000000v000000000000
+01_f&JPJ0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyveZ*FvQX<{#5UukY>bYEXCa
+CuNm0Rj{Q6aWAK2mtey7Dou9o6na8005g20021v0000000000005+cR@)8$aA|NaUv_0~WN&gWa%FLK
+Wpi|MFK}UFYhh<)b1!vrY;0*_GcRLrZgg^KVlQrVY;ACFZ)`4bc~DCM0u%!j000080P~d=M?z6Cg>?)
+70B$4z06G8w00000000000HlFL;SK<BX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJo
+_RbaH88FK~HpaAj_Db8Iefc~DCM0u%!j000080P~d=N59k!AU^;A07?J=06G8w00000000000HlEt?h
+XKOX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK}UFYhh<)b1z?CX>MtBUtcb8c~DCM0
+u%!j000080P~d=M@GS>XYdLD0OukA06qW!00000000000HlGa?hXKOX>c!Jc4cm4Z*nhkWpQ<7b98er
+aA9L>VP|D?FLiQkY-wUMFK}UFYhh<)b1!dlWMy(?WM5=yV{|TXc~DCM0u%!j000080P~d=N7Gmyv0M%
+S0BkJ)05t#r00000000000HlHR`3?YZX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK}
+UFYhh<)b1!pgcrI{xP)h*<6ay3h000O8^OY7y5U^<F-2eap%mDxZO#lD@0000000000q=BOd4*+m!a4
+%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kaA9L>VP|D?FLQHjUu|J@V`yJ!Z*z2RVQpnEU
+tei%X>?y-E^v8JO928D0~7!N00;o{l@>>>)SMdx1^@uU4gdgD00000000000001_f$Iqm0B~t=FJE?L
+Ze(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvrVPk7yXJvCQb8~E8ZDDj{XkTb=b98QDZDlWCX>D+
+9Wo>0{bYXO9Z*DGdc~DCM0u%!j000080P~d=N4%-@gaZKp0OkPz05$*s00000000000HlFo5Dx%wX>c
+!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFLiWjY%gD5X>MtBUtcb8c~DCM0u%!j000080P
+~d=NBVU-R15_G0No4#05|{u00000000000HlGq5f1=xX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?F
+LiQkY-wUMFLiWjY%gPPZf<2`bZKvHE^v8JO928D0~7!N00;o{l@>>>sw2I}0ssIh2mk;z0000000000
+0001_ff^SN0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvwbZKlaa%FLKWpi{caCuN
+m0Rj{Q6aWAK2mtey7Ds3Q;WQBe001@u001@s0000000000005+cG#U>8aA|NaUv_0~WN&gWa%FLKWpi
+|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cYWpi+EZgXWWaCuNm0Rj{Q6aWAK2mtey7Dv$3+ayp7006`$00
+1)p0000000000005+cnHvuPaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cYW
+pr|RE^v8JO928D0~7!N00;o{l@>?c=NYzw4FCW&CjbC400000000000001_fjTG;0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvwbZKlab8~E8E^v8JO928D0~7!N00;o{l@>?r3UFXL3jhE
+}B>(_500000000000001_febbe0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvwbZK
+labZKp6Z*_DoaCuNm0Rj{Q6aWAK2mtey7DwCS=~@2=007Ju001!n0000000000005+cnL!T#aA|NaUv
+_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cba%?Vec~DCM0u%!j000080P~d=N8pYFH
+H-oP0M`cq02lxO00000000000HlHENe=*UX>c!NZ*6U1Ze(*WUtei%X>?y-E^v8JO928D0~7!N00;o{
+l@>=65Bi<>0ssKV2LJ#X00000000000001_fvHRn0B~t=FJo_QZDDR?b1!3WZE$R5bZKvHE^v8JO928
+D0~7!N00;o{l@>=m_|e-o0{{RT2><{V00000000000001_f!R+F0B~t=FJo_QZDDR?b1!CcWo3G0E^v
+8JO928D0~7!N00;o{l@>=H7SNrl0RRBj0{{RN00000000000001_fkRUd0B~t=FJo_QZDDR?b1!IRY;
+Z1cc~DCM0u%!j000080P~d=M*}W(qBjlz07E$d02crN00000000000HlE*RSy7gX>c!NZ*6U1Ze(*WX
+>N0LVQg$JaCuNm0Rj{Q6aWAK2mtey7DsW9lMpZm002`Q000yK0000000000005+ci(?M}aA|NaV{dJ3
+VQyq|FKlUZbS`jtP)h*<6ay3h000O8^OY7y+3V+&R|Wt8{1E^E761SM0000000000q=D^f4*+m!a4%z
+TZEaz0WOFZbWnpq-XfAMhP)h*<6ay3h000O8^OY7yQi;$;AqD^dZW90i6aWAK0000000000q=9&G4*+
+m!a4%zTZEaz0WOFZbXm58eaCuNm0Rj{Q6aWAK2mtey7Duaz89&ei002Y_000>P0000000000005+c%X
+bd|aA|NaV{dJ3VQyq|FLiEdZgX^DY-}!Yc~DCM0u%!j000080P~d=M|glw#xn>201_Pl02KfL000000
+00000HlG^dk+9`X>c!NZ*6U1Ze(*WcW7m0Y%XwlP)h*<6ay3h000O8^OY7yd;YCslm!3)N(}%2761SM
+0000000000q=7hu4*+m!a4%zTZE#_9FJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7yf}*9DO%(tDI9dP
+z7ytkO0000000000q=5j64*+m!a4%zTZE#_9FJx(BbYpLBW@%?GaCuNm0Rj{Q6aWAK2mtey7DuWYXS_
+iG002h-000&M0000000000005+cgP#uoaA|NaZEs{{Y;!MPUukY>bYEXCaCuNm0Rj{Q6aWAK2mtey7D
+s4KGHu}i008F!000vJ0000000000005+c_@55|aA|NaZEs{{Y;!MZZe(S6E^v8JO928D0~7!N00;o{l
+@><;00002000000000Q00000000000001_feN7y0B~t=FK=*Va$$67Z*FrhUtei%X>?y-E^v8JO928D
+0~7!N00;o{l@>?RF>h651ONb#4*&od00000000000001_fkdGX0B~t=FK=*Va$$67Z*FrhW^!d^dSxz
+fc~DCM0u%!j000080P~d=M_g&r8Grx)02BcL022TJ00000000000HlH5r4ImbX>c!cWpOWGUukY>bYE
+XCaCuNm0Rj{Q6aWAK2mtey7DwFNGq6Sr0071%000vJ0000000000005+ckERa*aA|Naa%FKZa%FK}W@
+&6?E^v8JO928D0~7!N00;o{l@>=DEmQ*EDF6Uir~m*K00000000000001_fd;Y<0B~t=FLGsZFLGsZU
+ukZ0bYX04E^v8JO928D0~7!N00;o{l@>=`7|$3%8vp<_YXATe00000000000001_ff?Kn0B~t=FLGsZ
+FLGsZUvp)2E^v8JO928D0~7!N00;o{l@>?2NHhpv2mk=z8vp<p00000000000001_fr|GJ0B~t=FLGs
+ZFLGsZUv+M2ZgX^DY-}!Yc~DCM0u%!j000080P~d=M?@=AP_Q2W0KRAd02KfL00000000000HlE*01y
+ChX>c!fbZKmJFJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7y^O&Vq2nqlI)gu4^6aWAK0000000000q=
+5n;5CCv#a4&UqX>4;ZVQ_F{X>xNeaCuNm0Rj{Q6aWAK2mtey7DvqtO)dZe001Bb000sI00000000000
+05+cJSq?XaA|Nab#!TLb1!0bX>4RJaCuNm0Rj{Q6aWAK2mtey7Dv9ThG^dc003wR000;O0000000000
+005+cY%CA}aA|Nab#!TLb1!6NaB^j1VRUJ4ZZ2?nP)h*<6ay3h000O8^OY7y@<Z*wqyYc`g988n7XSb
+N0000000000q=A7i5CCv#a4&UqX>4;ZW@&6?b9r-gWo<5Sc~DCM0u%!j000080P~d=M`2RBVzUGQ0R9
+XB02BZK00000000000HlFhF%ST7X>c!fbZKmJFKlmTXK8L{E^v8JO928D0~7!N00;o{l@>>}`!?~B1^
+@uF6#xJg00000000000001_fj>470B~t=FLiWjY;!Mfb#!E5bY)~NaCuNm0Rj{Q6aWAK2mtey7Du5k1
+=|}5004Fx000jF0000000000005+c13eG`aA|Nab#!TLb1!viE^v8JO928D0~7!N00;o{l@>=oql8pZ
+4FCY}EdT%$00000000000001_flEdZ0B~t=FLq;dFJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7y>zRr
+5%LM=cOA!D75dZ)H0000000000q=C&-5CCv#a4&Xab1!0HdSPL5E^v8JO928D0~7!N00;o{l@>?Sh_s
+JC2><}N9RL6h00000000000001_fyY@80B~t=FLq;dFK20VE^v8JO928D0~7!N00;o{l@>=Bo0~X~1p
+okU6951d00000000000001_fihzd0B~t=FLq;dFKuOVV|8+AVQemNc~DCM0u%!j000080P~d=M?<@N5
+j_k50OTkD02KfL00000000000HlHQXb=E!X>c!gV{<Qabz*j9a&u{KZZ2?nP)h*<6ay3h000O8_>~q%
+{ba)VbpZeXJp%v$BLDyZ0000000000q=92~5CCv#a4k13F)lJLWNCABEop9MZ!bheQ$tcoP*h1zPA+n
+DbWlqH0u%!j000080Qi*_M;X)hB`N>_07L))03-ka00000000000HlE#cMt$@X>ct!E-@}LE@WwQbS-
+IaW^XTLZgg^aUvO_}Zgg`lba-@7O928D0~7!N00;p1l@>>(4j9~H0ssKh1pojc00000000000001_fr
+WPv0B~t=EjKPPE;24;X>)WfX>Mk3FKuOXVPs)+VJ>QOZ*EXa0Rj{Q6aWAK2mtt%7DuH%DV{F?001rk0
+00~S0000000000005+cCwdS7aA|NYH!d+QGA?9kb960fZf0*UaAI;UYIARHP)h*<6ay3h000O8_>~q%
+#~+u%1^@s61ONa4ApigX0000000000q=Arn5CCv#a4k13F)lJLWNCABEop9MZ!dIja9?a?c4cfXba-@
+7O928D0~7!N00;p1l@>>bGk<Si0001P0000P00000000000001_fz^5t0B~t=EjKPPE;24;X>)WfX>M
+k3FIPxKMNCjj0Rj{Q6aWAK2mtt%7DsNd4e99u006xQ000~S0000000000005+cYkLp?aA|NYH!d+QGA
+?9kb960fZf0*UO+{2eL_t(RP)h*<6ay3h000O8_>~q%jg!*R`zZhbR96518UO$Q0000000000q=Alp5
+CCv#a4k13F)lJLWNCABEop9MZ!c0sLr+pfP)h{{00000>;UWlXG{P9!Ke@b000
+"""
+
+
+if __name__ == "__main__":
+ main()
diff --git a/asdctool/src/main/resources/es-resources/index_ops.py b/asdctool/src/main/resources/es-resources/index_ops.py
new file mode 100644
index 0000000000..d1f3bb0021
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/index_ops.py
@@ -0,0 +1,151 @@
+import itertools
+import string
+import json
+from datetime import datetime
+from elasticsearch import Elasticsearch
+import elasticsearch
+import elasticsearch.helpers
+from elasticsearch.client import IndicesClient, CatClient
+import sys, os, getopt
+from file_utils import readFileToJson
+from config_properties import getGlobalVar
+
+
+
+def createIndex(client, indexName, createBody):
+ try:
+ print "start createIndex"
+ if (client == None):
+ client = Elasticsearch(['localhost'])
+ esIndexClient = IndicesClient(client)
+ res = deleteIndex(client, indexName)
+ if (res != 0):
+ print "operation failed"
+ return 2
+ create_res=elasticsearch.client.IndicesClient.create(esIndexClient, index=indexName, body=createBody)
+ print "create index response: ", create_res
+ if (create_res['acknowledged'] != True):
+ print "failed to create index"
+ return 1
+ else:
+ print "index ",indexName, " created successfully"
+ return 0
+ except Exception, error:
+ print "An exception was thrown!"
+ print str(error)
+ return 2
+
+
+def deleteIndex(client, indexName):
+ try:
+ print "start deleteIndex"
+ if (client == None):
+ client = Elasticsearch(['localhost'])
+ esIndexClient = IndicesClient(client)
+ isExists=elasticsearch.client.IndicesClient.exists(esIndexClient, indexName)
+ if ( isExists == True ):
+ delete_res=elasticsearch.client.IndicesClient.delete(esIndexClient, index=indexName)
+ if (delete_res['acknowledged'] != True):
+ print "failed to delete index"
+ return 1
+ else:
+ print "index ",indexName, " deleted"
+ return 0
+ else:
+ print "index not found - assume already deleted"
+ return 0
+ except Exception, error:
+ print "An exception was thrown!"
+ print str(error)
+ return 2
+
+def copyIndex(client, fromIndex, toIndex):
+ try:
+ print "start copyIndex"
+ if (client == None):
+ client = Elasticsearch(['localhost'])
+ client.indices.refresh(index=fromIndex)
+ count=client.search(fromIndex, search_type='count')
+ print "original index count: ",count
+ docNum, docErrors = elasticsearch.helpers.reindex(client, fromIndex, toIndex)
+ print "copy result: ", docNum, docErrors
+ if (docNum != count['hits']['total']):
+ print "Failed to copy all documents. expected: ", count['hits']['total'], " actual: ", docNum
+ return 1
+ # if (len(docErrors) != 0):
+ # print "copy returned with errors"
+ # print docErrors
+ # return 1
+ return 0
+ except Exception, error:
+ print "An exception was thrown!"
+ print str(error)
+ return 2
+
+
+def usage():
+ print 'USAGE: ', sys.argv[0], '-o <operation : create | delete | move> -n <indexName> -a <address> -f <mappingFile (for create)> -t <toIndex (for move operation)>'
+
+
+
+def main(argv):
+ print "start script with ", len(sys.argv), 'arguments.'
+ print "=============================================="
+
+ try:
+ opts, args = getopt.getopt(argv, "h:o:a:n:f:t:", ["operation","address","indexName","file","toIndex"])
+ except getopt.GetoptError:
+ usage()
+ sys.exit(2)
+
+ host = None
+ for opt, arg in opts:
+ print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(2)
+ elif opt in ('-f', '--file'):
+ mapping=readFileToJson(arg)
+ elif opt in ('-a', '--address'):
+ host=arg
+ elif opt in ('-o', '--operation'):
+ operation=arg
+ elif opt in ('-n', '--indexName'):
+ indexName=arg
+ elif opt in ('-t', '--toIndex'):
+ destIndexName=arg
+
+ if (operation == None):
+ usage()
+ sys.exit(2)
+ elif (host == None):
+ print "address is mandatory argument"
+ usage()
+ sys.exit(2)
+ elif operation == 'create':
+ print "create new index ", indexName
+ client = Elasticsearch([{'host': host, 'timeout':5}] )
+ res = createIndex(client, indexName, mapping)
+
+ elif operation == 'delete':
+ print "delete index ", indexName
+ client = Elasticsearch([{'host': host, 'timeout':5}] )
+ res = deleteIndex(client, indexName)
+
+ elif operation == 'move':
+ print "move index ", indexName, " to ", destIndexName
+ client = Elasticsearch([{'host': host, 'timeout':5}] )
+ res = copyIndex(client, indexName, destIndexName)
+ else:
+ usage()
+ exit(2)
+ if res != 0:
+ print "ERROR: operation Failed"
+ exit(1)
+
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
+
diff --git a/asdctool/src/main/resources/es-resources/types/auditinggetuebclusterevent.txt b/asdctool/src/main/resources/es-resources/types/auditinggetuebclusterevent.txt
new file mode 100644
index 0000000000..b7e9435f97
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/auditinggetuebclusterevent.txt
@@ -0,0 +1,8 @@
+{ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "consumerId": "CONSUMER_ID"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/distributiondeployevent.txt b/asdctool/src/main/resources/es-resources/types/distributiondeployevent.txt
new file mode 100644
index 0000000000..a74f0370e6
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/distributiondeployevent.txt
@@ -0,0 +1,14 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "currVersion": "CURR_VERSION",
+ "distributionId": "DID",
+ "modifierName": "MODIFIER_NAME",
+ "modifierUid": "MODIFIER_UID",
+ "resourceName": "RESOURCE_NAME",
+ "resourceType": "RESOURCE_TYPE"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/distributiondownloadevent.txt b/asdctool/src/main/resources/es-resources/types/distributiondownloadevent.txt
new file mode 100644
index 0000000000..879c4c4231
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/distributiondownloadevent.txt
@@ -0,0 +1,9 @@
+{ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "resourceUrl": "RESOURCE_URL",
+ "consumerId": "CONSUMER_ID"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/distributionengineevent.txt b/asdctool/src/main/resources/es-resources/types/distributionengineevent.txt
new file mode 100644
index 0000000000..a261042720
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/distributionengineevent.txt
@@ -0,0 +1,13 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "consumerId": "CONSUMER_ID",
+ "role": "ROLE",
+ "topicName": "TOPIC_NAME",
+ "apiKey": "API_KEY",
+ "environmentName": "D_ENV"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/distributionnotificationevent.txt b/asdctool/src/main/resources/es-resources/types/distributionnotificationevent.txt
new file mode 100644
index 0000000000..6375ead9bb
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/distributionnotificationevent.txt
@@ -0,0 +1,16 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "currVersion": "CURR_VERSION",
+ "currState": "CURR_STATE",
+ "distributionId": "DID",
+ "modifierName": "MODIFIER_NAME",
+ "modifierUid": "MODIFIER_UID",
+ "resourceName": "RESOURCE_NAME",
+ "resourceType": "RESOURCE_TYPE",
+ "topicName": "TOPIC_NAME"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/distributionstatusevent.txt b/asdctool/src/main/resources/es-resources/types/distributionstatusevent.txt
new file mode 100644
index 0000000000..8fed9dd0c0
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/distributionstatusevent.txt
@@ -0,0 +1,12 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "resourceUrl": "RESOURCE_URL",
+ "consumerId": "CONSUMER_ID",
+ "distributionId": "DID",
+ "topicName": "TOPIC_NAME"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/resourceadminevent.txt b/asdctool/src/main/resources/es-resources/types/resourceadminevent.txt
new file mode 100644
index 0000000000..4631aa3367
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/resourceadminevent.txt
@@ -0,0 +1,21 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "currVersion": "CURR_VERSION",
+ "currState": "CURR_STATE",
+ "distributionId": "DID",
+ "modifierName": "MODIFIER_NAME",
+ "modifierUid": "MODIFIER_UID",
+ "prevVersion": "PREV_VERSION",
+ "prevState": "PREV_STATE",
+ "resourceName": "RESOURCE_NAME",
+ "resourceType": "RESOURCE_TYPE",
+ "dPrevStatus": "DPREV_STATUS",
+ "dCurrStatus": "DCURR_STATUS",
+ "comment": "COMMENT",
+ "artifactName": "ARTIFACT_NAME"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/useraccessevent.txt b/asdctool/src/main/resources/es-resources/types/useraccessevent.txt
new file mode 100644
index 0000000000..ebd27b55e3
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/useraccessevent.txt
@@ -0,0 +1,10 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "userUid": "USER_UID",
+ "userName": "USER_NAME"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/es-resources/types/useradminevent.txt b/asdctool/src/main/resources/es-resources/types/useradminevent.txt
new file mode 100644
index 0000000000..15e0d9bdca
--- /dev/null
+++ b/asdctool/src/main/resources/es-resources/types/useradminevent.txt
@@ -0,0 +1,20 @@
+{
+ "action": "ACTION",
+ "timestamp": "TIMESTAMP",
+ "requestId": "REQUEST_ID",
+ "serviceInstanceId": "SERVICE_INSTANCE_ID",
+ "desc": "DESC",
+ "status": "STATUS",
+ "modifierName": "MODIFIER_NAME",
+ "modifierUid": "MODIFIER_UID",
+ "userUid": "USER_UID",
+ "userName": "USER_NAME",
+ "userEmail": "USER_EMAIL",
+ "userRole": "USER_ROLE",
+ "userBeforeName": "USER_BEFORE_NAME",
+ "userBeforeEmail": "USER_BEFORE_EMAIL",
+ "userBeforeRole": "USER_BEFORE_ROLE",
+ "userAfterName": "USER_AFTER_NAME",
+ "userAfterEmail": "USER_AFTER_EMAIL",
+ "userAfterRole": "USER_AFTER_ROLE"
+} \ No newline at end of file
diff --git a/asdctool/src/main/resources/scripts/baseOperation.sh b/asdctool/src/main/resources/scripts/baseOperation.sh
new file mode 100644
index 0000000000..b27b734276
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/baseOperation.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+#echo $FULL_PATH
+
+LIB_DIR=$FULL_PATH/..
+BIN_DIR=$FULL_PATH/../bin
+CONFIG_DIR=$FULL_PATH/../config
+
+JVM_VERBOSE=-Dverbose
+
+JVM_LOG_FILE="-Dlogback.configurationFile=${CONFIG_DIR}/logback.xml"
+
+######################################################
+
+#BINS=`find $BIN_DIR -name "*.jar" | tr "\\n" ":"`
+
+LIBS=`find $LIB_DIR -name "*.jar" | tr "\\n" ":"`
+
+JARS=$JARS:$BINS:$LIBS
+
+
+export JARS
+export JVM_LOG_FILE
diff --git a/asdctool/src/main/resources/scripts/cleanCsar.sh b/asdctool/src/main/resources/scripts/cleanCsar.sh
new file mode 100644
index 0000000000..c11cdef8f8
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/cleanCsar.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass clean-csar $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/dataMigration.sh b/asdctool/src/main/resources/scripts/dataMigration.sh
new file mode 100644
index 0000000000..b05c6dc355
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/dataMigration.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass migrate-1602-1604 $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/dataMigration1607.sh b/asdctool/src/main/resources/scripts/dataMigration1607.sh
new file mode 100644
index 0000000000..027852d882
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/dataMigration1607.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration 1607
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass migrate-1604-1607 $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/dataMigration1610.sh b/asdctool/src/main/resources/scripts/dataMigration1610.sh
new file mode 100644
index 0000000000..45d61adeee
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/dataMigration1610.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+##############################
+# Data Migration 1610
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass fix-properties $@"
+echo $command
+
+$command
+result=$?
+
+if [ $result -eq 0 ]
+then
+ command="java $JVM_LOG_FILE -cp $JARS $mainClass align-tosca-artifacts $@"
+ echo $command
+ $command
+ result=$?
+fi
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/deleteAllProducts.sh b/asdctool/src/main/resources/scripts/deleteAllProducts.sh
new file mode 100644
index 0000000000..38e468d5f7
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/deleteAllProducts.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# delete all products from titan
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.RemoveUtils"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass remove-products $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/derivedFromAlignment.sh b/asdctool/src/main/resources/scripts/derivedFromAlignment.sh
new file mode 100644
index 0000000000..05025a01c0
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/derivedFromAlignment.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass align-derived-from-1604 $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/esToCassandraMigration.sh b/asdctool/src/main/resources/scripts/esToCassandraMigration.sh
new file mode 100644
index 0000000000..383904c661
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/esToCassandraMigration.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.EsToCassandraDataMigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass es-to-cassndra-migration $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/esToCassandraMigrationExportOnly.sh b/asdctool/src/main/resources/scripts/esToCassandraMigrationExportOnly.sh
new file mode 100644
index 0000000000..2c8e346f30
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/esToCassandraMigrationExportOnly.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.EsToCassandraDataMigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass es-to-cassndra-migration-export-only $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/esToCassandraMigrationImportOnly.sh b/asdctool/src/main/resources/scripts/esToCassandraMigrationImportOnly.sh
new file mode 100644
index 0000000000..9ce3ca8aae
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/esToCassandraMigrationImportOnly.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.EsToCassandraDataMigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass es-to-cassndra-migration-import-only $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/exportGraph.sh b/asdctool/src/main/resources/scripts/exportGraph.sh
new file mode 100644
index 0000000000..648cb50669
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/exportGraph.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.ExportImportMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass export $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/exportGraphAsGraphMl.sh b/asdctool/src/main/resources/scripts/exportGraphAsGraphMl.sh
new file mode 100644
index 0000000000..17d660ce4e
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/exportGraphAsGraphMl.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.ExportImportMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass export-as-graph-ml $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/exportUsers.sh b/asdctool/src/main/resources/scripts/exportUsers.sh
new file mode 100644
index 0000000000..d701c57f92
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/exportUsers.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# export all users from titan
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.ExportImportMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass exportusers $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/fix_icons.sh b/asdctool/src/main/resources/scripts/fix_icons.sh
new file mode 100644
index 0000000000..230dea7218
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/fix_icons.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass fix-icons $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result \ No newline at end of file
diff --git a/asdctool/src/main/resources/scripts/fix_issue.sh b/asdctool/src/main/resources/scripts/fix_issue.sh
new file mode 100644
index 0000000000..af63ec525d
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/fix_issue.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass fix-properties $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/groupsAlignment.sh b/asdctool/src/main/resources/scripts/groupsAlignment.sh
new file mode 100644
index 0000000000..742b246a48
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/groupsAlignment.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Groups Alignment
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass align-groups $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/importGraph.sh b/asdctool/src/main/resources/scripts/importGraph.sh
new file mode 100644
index 0000000000..ffdd2fddb0
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/importGraph.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.ExportImportMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass import $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/populateComponentCache.sh b/asdctool/src/main/resources/scripts/populateComponentCache.sh
new file mode 100644
index 0000000000..79ddbf822e
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/populateComponentCache.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Groups Alignment
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass populate-component-cache $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/python/duplicates.py b/asdctool/src/main/resources/scripts/python/duplicates.py
new file mode 100644
index 0000000000..be60b05909
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/python/duplicates.py
@@ -0,0 +1,47 @@
+import json
+import sys
+
+dict = {}
+dupliacteUid = {}
+#debugFlag = True
+debugFlag = False
+
+def debug(str1, str2=""):
+ 'print only if debug enabled'
+ if (debugFlag == True): print str1, str2
+
+print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+
+with open(sys.argv[1]) as json_file:
+ json_data = json.load(json_file)
+ for x in json_data['vertices']:
+ uid = None
+ nodeLabel=x.get('nodeLabel')
+ debug(nodeLabel)
+ if ( nodeLabel == 'user' ):
+ uid = x['userId']
+ elif ( nodeLabel == 'tag' ):
+ uid = x['name']
+ elif ( nodeLabel == None ):
+ pass
+ elif ( nodeLabel == 'lockNode' ):
+ uid = x.get('uid')
+ else: uid = x['uid']
+
+ debug(nodeLabel, uid)
+
+ existId = dict.get(uid)
+ if (existId == None):
+ dict[uid] = x.get('_id')
+ else:
+ dupliacteUid[uid] = existId
+
+ print dupliacteUid
+
+# with open('data.txt', 'w') as outfile:
+# json.dump(json_data, outfile)
+
+
+
+# print x['uid']
diff --git a/asdctool/src/main/resources/scripts/python/duplicatesAndRemove.py b/asdctool/src/main/resources/scripts/python/duplicatesAndRemove.py
new file mode 100644
index 0000000000..a4bd35dd2b
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/python/duplicatesAndRemove.py
@@ -0,0 +1,136 @@
+import json
+import sys, getopt
+from collections import OrderedDict
+
+dict = {}
+dupliacteUid = {}
+#debugFlag = True
+debugFlag = False
+
+def join_strings(lst):
+ concat = ""
+ for string in lst:
+ if (string != None):
+ if (type(string) == int):
+ string = str(string)
+ concat += (string + " ")
+ return concat
+
+def debug(desc, *args):
+ 'print only if debug enabled'
+ if (debugFlag == True):
+ print desc, join_strings(args)
+
+def log(desc, arg):
+ 'print log info'
+ print desc, arg
+
+def getUid(vertex):
+ uid = None
+ nodeLabel=vertex.get('nodeLabel')
+ debug(nodeLabel)
+ if ( nodeLabel == 'user' ):
+ uid = vertex['userId']
+ elif ( nodeLabel == 'tag' ):
+ uid = vertex['name']
+ elif ( nodeLabel == None ):
+ pass
+ elif ( nodeLabel == 'lockNode' ):
+ uid = vertex.get('uid')
+ else: uid = vertex['uid']
+
+ debug(nodeLabel, uid)
+
+ return uid
+
+def generateFile(inputFile, outputFile):
+
+ with open(inputFile) as json_file:
+ dupliacteUid = {}
+ json_data = json.load(json_file)
+ for x in json_data['vertices']:
+ uid = getUid(x)
+
+ existId = dict.get(uid)
+ if (existId == None):
+ dict[uid] = x.get('_id')
+ else:
+ dupliacteUid[uid] = existId
+
+ log("duplicate ids", dupliacteUid)
+
+ json_data_vertices = json_data['vertices']
+ log("number of vertices is", len(json_data_vertices))
+
+ ids = {}
+ deleteIndexes = []
+
+ for i in xrange(len(json_data_vertices)):
+ #print "****** ", i, " *************"
+ #print json_data_vertices[i]
+ id = json_data_vertices[i]["_id"]
+ uid = getUid(json_data_vertices[i])
+ isDuplicateId = dupliacteUid.get(uid)
+ if (isDuplicateId != None):
+ debug("uid to id pair", uid if uid != None else 'None', id)
+ value = ids.get(uid)
+ if (value == None):
+ list = [id,]
+ ids[uid] = list
+ else:
+ value.append(id)
+ deleteIndexes.append(id)
+
+ log("ids", ids)
+ log("deleteIndexes", deleteIndexes)
+ log("deleteIndexes size", len(deleteIndexes))
+
+ filter_vertex = [ x for x in json_data_vertices if x.get('_id') not in deleteIndexes ]
+ json_data['vertices'] = filter_vertex
+
+ log("number of vertexes after filter", len(filter_vertex))
+
+ json_data_edges = json_data['edges']
+
+ log("number of edges", len(json_data_edges))
+
+ filter_edge = [ x for x in json_data_edges if x['_outV'] not in (deleteIndexes) and x['_inV'] not in (deleteIndexes) ]
+ json_data['edges'] = filter_edge
+
+ log("number of edges after filter", len(json_data['edges']))
+
+ json_data = OrderedDict(sorted(json_data.items(), key=lambda t: t[0], reverse=True))
+
+ with open(outputFile, 'w') as outfile:
+ #json.dump(json_data, outfile)
+ json.dump(json_data, outfile)
+ log("output file is", outputFile);
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+ inputfile = None
+ outputfile = ''
+ try:
+ opts, args = getopt.getopt(argv,"h:i:o:",["ifile=","ofile="])
+ except getopt.GetoptError:
+ print sys.argv[0], '-i <inputfile>'
+ sys.exit(2)
+ for opt, arg in opts:
+ if opt == '-h':
+ print sys.argv[0], '-i <inputfile>'
+ sys.exit(3)
+ elif opt in ("-i", "--ifile"):
+ inputfile = arg
+
+ if ( inputfile == None ):
+ print sys.argv[0] ,'-i <inputfile>'
+ sys.exit(3)
+
+ print 'Input file is "', inputfile
+ generateFile(inputfile, inputfile + '.noduplicates')
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
+# print x['uid']
diff --git a/asdctool/src/main/resources/scripts/python/graphSize.py b/asdctool/src/main/resources/scripts/python/graphSize.py
new file mode 100644
index 0000000000..6c36ec6844
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/python/graphSize.py
@@ -0,0 +1,56 @@
+import json
+import sys, getopt
+
+dict = {}
+dupliacteUid = {}
+#debugFlag = True
+debugFlag = False
+
+def debug(desc, *args):
+ 'print only if debug enabled'
+ if (debugFlag == True):
+ print desc, join_strings(args)
+
+def log(desc, arg):
+ 'print log info'
+ print desc, arg
+
+def graphSize(inputFile):
+
+ with open(inputFile) as json_file:
+ json_data = json.load(json_file)
+
+ json_data_vertices = json_data['vertices']
+ log("number of vertices is", len(json_data_vertices))
+
+ json_data_edges = json_data['edges']
+ log("number of edges is", len(json_data_edges))
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+ inputfile = None
+ outputfile = ''
+ try:
+ opts, args = getopt.getopt(argv,"h:i:o:",["ifile=","ofile="])
+ except getopt.GetoptError:
+ print sys.argv[0], '-i <inputfile>'
+ sys.exit(2)
+ for opt, arg in opts:
+ if opt == '-h':
+ print sys.argv[0], '-i <inputfile>'
+ sys.exit(3)
+ elif opt in ("-i", "--ifile"):
+ inputfile = arg
+
+ if ( inputfile == None ):
+ print sys.argv[0], '-i <inputfile>'
+ sys.exit(3)
+
+ print 'Input file is ', inputfile
+ graphSize(inputfile)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
+# print x['uid']
diff --git a/asdctool/src/main/resources/scripts/python/user/exportUsers.py b/asdctool/src/main/resources/scripts/python/user/exportUsers.py
new file mode 100644
index 0000000000..e32a3b0a21
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/python/user/exportUsers.py
@@ -0,0 +1,122 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+
+
+################################################################################################################################################
+# #
+# Export all active users to file - for 1602+ #
+# #
+# activation : #
+# python exportUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <output file> | --ofile=<output file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python exportUsers.py [-f <output file> | --ofile=<output file> ] #
+# #
+################################################################################################################################################
+
+ALL_USERS_SUFFIX = '/sdc2/rest/v1/user/users'
+
+def errorAndExit(errorCode, errorDesc):
+ if ( errorCode > 0 ):
+ print("status=" + str(errorCode) + ". " + errorDesc)
+ else:
+ print("status=" + str(errorCode))
+ sys.exit(errorCode)
+
+def getUsers(beHost, bePort, adminUser):
+
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ url = 'http://' + beHost + ':' + bePort + ALL_USERS_SUFFIX
+ print(url)
+ c.setopt(c.URL, url)
+ c.setopt(c.WRITEFUNCTION, buffer.write)
+ #c.setopt(c.WRITEFUNCTION, lambda x: None)
+ adminHeader = 'USER_ID: ' + adminUser
+ c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', adminHeader])
+ res = c.perform()
+ #print(res)
+
+ #print('Status: %d' % c.getinfo(c.RESPONSE_CODE))
+
+ c.close()
+
+ body = buffer.getvalue()
+
+ #print(body)
+
+ return (body, None)
+
+ except Exception as inst:
+ print inst
+ #print type(inst) # the exception instance
+ #print inst.args # arguments stored in .args
+ #print inst # __str__ allows args to be printed directly
+ #x, y = inst.args
+ #print 'x =', x
+
+ return (None, inst)
+
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <output file> | --ofile=<output file> ]'
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ adminHeader = 'jh0003'
+ beHost = 'localhost'
+ bePort = '8080'
+ outputfile = None
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:f:h:",["ip=","port=","ofile="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-f", "--ofile"):
+ outputfile = arg
+
+ print 'be host =',beHost,', be port =', bePort,', output file =',outputfile
+
+ if ( outputfile == None ):
+ usage()
+ sys.exit(3)
+
+ users = getUsers(beHost, bePort, adminHeader)
+ error = users[1]
+ body = users[0]
+
+ if ( error != None ):
+ errorAndExit(5, str(error))
+
+ #print body
+
+ io = StringIO(body)
+ usersAsJson = json.load(io)
+
+ writeFile = open(outputfile, 'w')
+
+ json.dump(usersAsJson, writeFile)
+
+ writeFile.close()
+
+ print("-------------------------------------------")
+ errorAndExit(0, None)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/asdctool/src/main/resources/scripts/python/user/importUsers.py b/asdctool/src/main/resources/scripts/python/user/importUsers.py
new file mode 100644
index 0000000000..669cbbe6f2
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/python/user/importUsers.py
@@ -0,0 +1,215 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+
+def importUsers(beHost, bePort, users, adminUser):
+
+ result = []
+
+ for user in users:
+
+ #print("Going to add user " + user['userId'])
+
+ getRes = getUser(beHost, bePort, user)
+ userId = getRes[0]
+ error = getRes[1]
+ #print error
+ if ( error != None and error == 404 ):
+ res = createUser(beHost, bePort, user ,adminUser)
+ result.append(res)
+ else:
+ if ( error == 200 ):
+ curResult = (userId, 409)
+ result.append(curResult)
+ else:
+ result.append(getRes)
+
+ return result
+
+
+def convertUsersToCreationObject(users):
+
+ cloneUsers = copy.deepcopy(users)
+ for user in cloneUsers:
+ #print user
+ if (user.get('fullName') != None):
+ del user['fullName']
+ #user['userId'] = user['userId'] + '1'
+ #print user
+
+ return cloneUsers
+
+def getUser(beHost, bePort, user):
+
+ userId = user['userId']
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ #print type(userId)
+ url = 'http://' + beHost + ':' + bePort + '/sdc2/rest/v1/user/' + str(userId)
+ c.setopt(c.URL, url)
+
+ #adminHeader = 'USER_ID: ' + adminUser
+ c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json'])
+ c.setopt(c.WRITEFUNCTION, lambda x: None)
+ res = c.perform()
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ #print("After get response code")
+ responseCode = c.getinfo(c.RESPONSE_CODE)
+
+ #print('Status: ' + str(responseCode))
+
+ c.close()
+
+ return (userId, httpRes)
+
+ except Exception as inst:
+ print(inst)
+ return (userId, None)
+
+
+
+def createUser(beHost, bePort, user, adminUser):
+
+ userId = user['userId']
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ url = 'http://' + beHost + ':' + bePort + '/sdc2/rest/v1/user'
+ c.setopt(c.URL, url)
+ c.setopt(c.POST, 1)
+
+ adminHeader = 'USER_ID: ' + adminUser
+ c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', adminHeader])
+
+ data = json.dumps(user)
+ c.setopt(c.POSTFIELDS, data)
+
+ c.setopt(c.WRITEFUNCTION, lambda x: None)
+ #print("before perform")
+ res = c.perform()
+ #print(res)
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ #print("After get response code")
+ responseCode = c.getinfo(c.RESPONSE_CODE)
+
+ #print('Status: ' + str(responseCode))
+
+ c.close()
+
+ return (userId, httpRes)
+
+ except Exception as inst:
+ print(inst)
+ return (userId, None)
+
+
+def errorAndExit(errorCode, errorDesc):
+ if ( errorCode > 0 ):
+ print("status=" + str(errorCode) + ". " + errorDesc)
+ else:
+ print("status=" + str(errorCode))
+ sys.exit(errorCode)
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ]'
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ inputfile = None
+
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:f:h:",["ip=","port=","ifile="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-f", "--ifile"):
+ inputfile = arg
+
+ print 'be host =',beHost,', be port =', bePort,', users file =',inputfile
+
+ if ( inputfile == None ):
+ usage()
+ sys.exit(3)
+
+ print 'Input file is ', inputfile
+
+ usersFile = open(inputfile)
+
+ json_data = json.load(usersFile)
+
+ #print json_data
+
+ cloneUsers = convertUsersToCreationObject(json_data)
+
+ activeUsers = filter(lambda x: x['status'] == 'ACTIVE', cloneUsers)
+
+ #print activeUsers
+
+ resultTable = importUsers(beHost, bePort, activeUsers, adminUser)
+
+ g = lambda x: x[1] != 201 and x[1] != 409
+
+ result = filter(g, resultTable)
+
+ if ( len(result) > 0 ):
+ #print("ERROR: Failed to load the users " + ', '.join(map(lambda x: x[0],result)))
+ errorAndExit(3, "Failed to load the users " + ', '.join(map(lambda x: x[0],result)))
+
+ g = lambda x: x[1] == 409
+ result = filter(g, resultTable)
+
+ print("-------------------------------------------")
+ print("Existing users: " + ', '.join(map(lambda x: x[0],result)))
+
+ result = filter(lambda x: x[1] == 201, resultTable)
+ if ( len(result) == 0 ):
+ print("-------------------------------------------")
+ print("No NEW user was loaded. All users are already exist")
+ print("-------------------------------------------")
+ else:
+ print("-------------------------------------------")
+ print("Loaded users: " + ', '.join(map(lambda x: x[0],result)))
+ print("-------------------------------------------")
+
+ errorAndExit(0, None)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/asdctool/src/main/resources/scripts/schemaCreation.sh b/asdctool/src/main/resources/scripts/schemaCreation.sh
new file mode 100644
index 0000000000..657db5e6f1
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/schemaCreation.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.DataSchemaMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass create-cassandra-structures $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/updateIsVnf.sh b/asdctool/src/main/resources/scripts/updateIsVnf.sh
new file mode 100644
index 0000000000..62bfb9cd66
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/updateIsVnf.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+########################################################################
+#
+# Example:
+# ./updateIsVnf.sh ../config/titan.properties Bservice,Myservice
+#
+########################################################################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.UpdateIsVnfMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass updateIsVnfTrue $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/src/main/resources/scripts/vfcNameAlignment.sh b/asdctool/src/main/resources/scripts/vfcNameAlignment.sh
new file mode 100644
index 0000000000..10fbb257ef
--- /dev/null
+++ b/asdctool/src/main/resources/scripts/vfcNameAlignment.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+##############################
+# Data Migration
+##############################
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+source ${FULL_PATH}/baseOperation.sh
+
+mainClass="org.openecomp.sdc.asdctool.main.MigrationMenu"
+
+command="java $JVM_LOG_FILE -cp $JARS $mainClass align-vfc-names-1604 $@"
+echo $command
+
+$command
+result=$?
+
+echo "***********************************"
+echo "***** $result *********************"
+echo "***********************************"
+
+exit $result
+
+
diff --git a/asdctool/tarball.xml b/asdctool/tarball.xml
new file mode 100644
index 0000000000..62deb75e10
--- /dev/null
+++ b/asdctool/tarball.xml
@@ -0,0 +1,26 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar</format>
+ </formats>
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>${project.artifactId}-${project.version}-jar-with-dependencies.jar</destName>
+ </file>
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <directory>src/main/resources/scripts</directory>
+ <outputDirectory>scripts</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/config</directory>
+ <outputDirectory>config</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/catalog-be/.gitignore b/catalog-be/.gitignore
new file mode 100644
index 0000000000..fbfe5d3901
--- /dev/null
+++ b/catalog-be/.gitignore
@@ -0,0 +1,8 @@
+/target
+/target
+/target
+
+/target
+/target
+/target
+/build
diff --git a/catalog-be/.pydevproject b/catalog-be/.pydevproject
new file mode 100644
index 0000000000..7ff1370cd2
--- /dev/null
+++ b/catalog-be/.pydevproject
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?><pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+</pydev_project>
diff --git a/catalog-be/README.txt b/catalog-be/README.txt
new file mode 100644
index 0000000000..575484081f
--- /dev/null
+++ b/catalog-be/README.txt
@@ -0,0 +1,41 @@
+#open rpm
+#install jetty
+#run installJettyBase.sh
+#copy jvm.properties to base
+#export variables
+#run startJetty.sh
+
+#Properties:
+
+ STOP.PORT=[number]
+ The port to use to stop the running Jetty server.
+ Required along with STOP.KEY if you want to use the --stop option above.
+
+ STOP.KEY=[alphanumeric]
+ The passphrase defined to stop the server.
+ Requried along with STOP.PORT if you want to use the --stop option above.
+
+ STOP.WAIT=[number]
+ The time (in seconds) to wait for confirmation that the running
+ Jetty server has stopped. If not specified, the stopper will wait
+ indefinitely. Use in conjunction with the --stop option.
+
+
+#Upload Normative types:
+# 1. create zip file containing the yaml
+# 2. create json string (payloadName should be the yml file name): {
+# "payloadName":"normative-types-new-root.yml",
+# "userId":"adminid",
+# "resourceName":"tosca.nodes.Root",
+# "description":"Represents a generic software component that can be managed and run by a Compute Node Type.",
+# "resourceIconPath":"defaulticon",
+# "category":"Abstract",
+# "tags":["Root"]
+# }
+#
+#
+# 3. run curl command: curl -v -F resourceMetadata=<json string> -F resourceZip=@<zip file location> <BE host:port>/sdc2/rest/v1/catalog/upload/multipart
+# e.g.:
+# curl -v -F resourceMetadata='{"payloadName":"normative-types-new-root.yml","userId":"adminid","resourceName":"tosca.nodes.Root","description":"Represents a generic software component that can be managed and run by a Compute Node Type.","resourceIconPath":"defaulticon","category":"Abstract","tags":["Root"]}' -F resourceZip=@/var/tmp/normative-types-new-root.zip localhost:8080/sdc2/rest/v1/catalog/upload/multipart
+
+#
diff --git a/catalog-be/normatives.xml b/catalog-be/normatives.xml
new file mode 100644
index 0000000000..059867224e
--- /dev/null
+++ b/catalog-be/normatives.xml
@@ -0,0 +1,18 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar.gz</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>src/main/resources/import</directory>
+ <outputDirectory>import</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/scripts</directory>
+ <outputDirectory>scripts</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/catalog-be/pom.xml b/catalog-be/pom.xml
new file mode 100644
index 0000000000..ddf421a046
--- /dev/null
+++ b/catalog-be/pom.xml
@@ -0,0 +1,790 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>catalog-be</artifactId>
+ <packaging>war</packaging>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <properties>
+ <swagger-ui-version>2.1.0-M2</swagger-ui-version>
+ </properties>
+
+
+
+
+ <dependencyManagement>
+ <dependencies>
+
+ <!-- JSON and YAML Parsing -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>${jackson.annotations.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.sun.jersey.contribs</groupId>
+ <artifactId>jersey-multipart</artifactId>
+ <version>2.14</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${security-utils.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.tinkerpop.blueprints</groupId>
+ <artifactId>blueprints-sail-graph</artifactId>
+ <version>2.6.0</version>
+ <optional>true</optional>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.tinkerpop.blueprints</groupId>
+ <artifactId>blueprints-graph-sail</artifactId>
+ <version>2.5.0</version>
+ <optional>true</optional>
+ </dependency>
+
+ <!-- Swagger Dependencies Start -->
+ <dependency>
+ <groupId>com.wordnik</groupId>
+ <artifactId>swagger-jersey2-jaxrs</artifactId>
+ <scope>compile</scope>
+ <version>1.5.1-M2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet-core</artifactId>
+ </dependency>
+
+ <!-- Swagger Dependencies End -->
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>common-be</artifactId>
+ <version>${common-be.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-dao</artifactId>
+ <version>${catalog-dao.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-model</artifactId>
+ <version>${catalog-model.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- slf4j + logback -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Snake Yaml -->
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- File changes listener -->
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Gson -->
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Json -->
+
+
+
+
+ <!-- jersey -->
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-jackson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- http client END -->
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit</artifactId>
+ <version>3.4.1.201406201815-r</version>
+ </dependency>
+
+
+ <!-- spring - used by A4C -->
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.janino</groupId>
+ <artifactId>janino</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.janino</groupId>
+ <artifactId>commons-compiler</artifactId>
+ <version>3.0.6</version>
+ <scope>compile</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </exclusion>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <!--<artifactId>asdc-titan-cassandra</artifactId -->
+ <version>${titan.version}</version>
+ <!--<version>1.0.0-snapshot</version -->
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- TITAN END -->
+
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <scope>compile</scope>
+
+ </dependency>
+
+ <dependency>
+ <groupId>org.elasticsearch</groupId>
+ <artifactId>elasticsearch</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.elasticsearch.plugin</groupId>
+ <artifactId>shield</artifactId>
+ <version>${elastic-search.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- functional java -->
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Aspects -->
+ <dependency>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-aspects</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- CAMBRIA CLIENT for U-EB -->
+ <dependency>
+ <groupId>com.att.nsa</groupId>
+ <artifactId>cambriaClient</artifactId>
+ <version>0.0.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.att.nsa</groupId>
+ <artifactId>saClientLibrary</artifactId>
+ <version>0.0.1</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20131018</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>tinkergraph-gremlin</artifactId>
+ <version>3.0.1-incubating</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.tinkerpop</groupId>
+ <artifactId>gremlin-groovy</artifactId>
+ <version>3.0.1-incubating</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- CASSANDRA -->
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-core</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-mapping</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <!-- CASSANDRA END -->
+
+ <!-- Inserted for ECOMP Portal Integration -->
+ <dependency>
+ <groupId>org.openecomp.ecompsdkos</groupId>
+ <artifactId>ecompFW</artifactId>
+ <version>${ecomp.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>com.att.nsa</groupId>
+ <artifactId>cambriaClient</artifactId>
+ </exclusion>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <!-- Artifact Generator -->
+ <dependency>
+ <groupId>org.openecomp.sdc.sdc_common</groupId>
+ <artifactId>openecomp-sdc-artifact-generator-api</artifactId>
+ <version>${artefact-gen-api.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.sdc_common</groupId>
+ <artifactId>openecomp-sdc-artifact-generator-core</artifactId>
+ <version>${artefact-gen-core.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc.sdc_common</groupId>
+ <artifactId>openecomp-common-lib</artifactId>
+ <version>${dox-common-lib.version}</version>
+ <type>pom</type>
+ </dependency>
+
+
+ <!-- Jetty Proxy -->
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-proxy</artifactId>
+ <version>${jetty.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlets</artifactId>
+ <version>${jetty.servlets.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- TEST -->
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-bundle</artifactId>
+ <type>pom</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ <version>${spring.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+
+ <build>
+
+ <finalName>${project.artifactId}-${project.version}</finalName>
+
+
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <logback.configurationFile>src/test/resources/logback-test.xml</logback.configurationFile>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <packagingExcludes>WEB-INF\lib\slf4j-log4j*.jar,
+ WEB-INF/classes/elasticsearch.yml,
+ WEB-INF/classes/portal.properties</packagingExcludes>
+ <archive>
+ <manifestEntries>
+ <SDC-Version>${parent.version}</SDC-Version>
+ </manifestEntries>
+ <manifest>
+ <!-- <addClasspath>true</addClasspath -->
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+ </manifest>
+ </archive>
+
+ <webResources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <!-- <directory>target/swagger-ui-2.1.0-M2/dist</directory -->
+ <directory>src/main/resources/swagger</directory>
+ </resource>
+ </webResources>
+ <attachClasses>true</attachClasses>
+ </configuration>
+ </plugin>
+
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+ <includeScope>compile</includeScope>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-installed</id>
+ <phase>install</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${project.version}</version>
+ <type>${project.packaging}</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.parent.basedir}/sdc-os-chef/sdc-backend/</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+ <plugin>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-maven-plugin</artifactId>
+ <version>${jcabi.plugin.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>ajc</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+
+
+ <!-- Swagger Plugins Start -->
+ <plugin>
+ <groupId>com.googlecode.maven-download-plugin</groupId>
+ <artifactId>download-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <id>swagger-ui</id>
+ <goals>
+ <goal>wget</goal>
+ </goals>
+ <configuration>
+ <url>https://github.com/swagger-api/swagger-ui/archive/v${swagger-ui-version}.tar.gz</url>
+ <unpack>true</unpack>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+
+ <execution>
+ <id>copy-resources</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>target/${project.artifactId}-${project.version}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${project.build.directory}/swagger-ui-${swagger-ui-version}/dist</directory>
+ <filtering>true</filtering>
+ <excludes>
+ <exclude>index.html</exclude>
+ </excludes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+
+ <execution>
+ <id>copy-normatives-backend</id>
+ <phase>install</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.parent.basedir}/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default</outputDirectory>
+ <resources>
+ <resource>
+ <directory>./target</directory>
+ <includes>
+ <include>normatives.tar.gz</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+
+ </executions>
+ </plugin>
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <id>normatives</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <finalName>normatives</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptor>${project.basedir}/normatives.xml</descriptor>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+
+
+
+
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings
+ only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>com.googlecode.maven-download-plugin</groupId>
+ <artifactId>download-maven-plugin</artifactId>
+ <versionRange>[1.2.1,)</versionRange>
+ <goals>
+ <goal>wget</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-maven-plugin</artifactId>
+ <versionRange>[0.0,)</versionRange>
+ <goals>
+ <goal>ajc</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <execute />
+ </action>
+ </pluginExecution>
+
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ </plugin>
+
+ </plugins>
+ </pluginManagement>
+ <!-- Swagger Plugins End -->
+ </build>
+
+
+
+</project>
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/api/IAuditingManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/api/IAuditingManager.java
new file mode 100644
index 0000000000..36b79c2d9c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/api/IAuditingManager.java
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.auditing.api;
+
+import java.util.EnumMap;
+
+import javax.servlet.ServletContext;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+public interface IAuditingManager {
+ void auditEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields);
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatConstants.java b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatConstants.java
new file mode 100644
index 0000000000..eb14b5afe7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatConstants.java
@@ -0,0 +1,258 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.auditing.impl;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+public interface AuditingLogFormatConstants {
+
+ static AuditingFieldsKeysEnum[] DISTRIBUTION_REGISTRATION_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME};
+
+ static AuditingFieldsKeysEnum[] DISTRIBUTION_DOWNLOAD_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] GET_UEB_CLUSTER_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] DISTRIBUTION_DEPLOY_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] DISTRIBUTION_STATUS_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] DISTRIBUTION_NOTIFY_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] ADD_REMOVE_TOPIC_KEY_ACL_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ROLE,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY,
+ AuditingFieldsKeysEnum.AUDIT_STATUS
+ };
+
+ static AuditingFieldsKeysEnum[] CREATE_TOPIC_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME,
+ AuditingFieldsKeysEnum.AUDIT_STATUS
+ };
+
+ static AuditingFieldsKeysEnum[] ACTIVATE_DISTRIBUTION_ARRAY ={
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] CHANGE_DISTRIBUTION_STATUS_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT
+ };
+
+ static AuditingFieldsKeysEnum[] CREATE_RESOURCE_TEMPLATE_SUFFIX_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] CREATE_RESOURCE_TEMPLATE_PREFIX_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID,
+ AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE
+ };
+
+ static AuditingFieldsKeysEnum[] USER_ACCESS_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_USER_UID,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] USER_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_USER_UID,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] AUTH_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_AUTH_URL,
+ AuditingFieldsKeysEnum.AUDIT_AUTH_USER,
+ AuditingFieldsKeysEnum.AUDIT_AUTH_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_AUTH_REALM
+ };
+
+ static AuditingFieldsKeysEnum[] ECOMP_USER_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_ECOMP_USER,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] CATEGORY_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_CATEGORY_NAME,
+ AuditingFieldsKeysEnum.AUDIT_SUB_CATEGORY_NAME,
+ AuditingFieldsKeysEnum.AUDIT_GROUPING_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] GET_USERS_LIST_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_USER_DETAILS,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+
+ static AuditingFieldsKeysEnum[] GET_CATEGORY_HIERARCHY_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_DETAILS,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+ static AuditingFieldsKeysEnum[] USER_ADMIN_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_USER_BEFORE,
+ AuditingFieldsKeysEnum.AUDIT_USER_AFTER,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+ static AuditingFieldsKeysEnum[] EXTERNAL_GET_ASSET_LIST_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+ static AuditingFieldsKeysEnum[] EXTERNAL_GET_ASSET_TEMPLATE_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+ static AuditingFieldsKeysEnum[] EXTERNAL_DOWNLOAD_ARTIFACT_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+ static AuditingFieldsKeysEnum[] EXTERNAL_CRUD_API_ARTIFACT_ARRAY = {
+ AuditingFieldsKeysEnum.AUDIT_ACTION,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME,
+ AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID,
+ AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL,
+ AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID,
+ AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID,
+ AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID,
+ AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA,
+ AuditingFieldsKeysEnum.AUDIT_STATUS,
+ AuditingFieldsKeysEnum.AUDIT_DESC
+ };
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatUtil.java b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatUtil.java
new file mode 100644
index 0000000000..5fec39bbdb
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingLogFormatUtil.java
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.auditing.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.Formatter;
+import java.util.Locale;
+
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+
+public class AuditingLogFormatUtil {
+
+ // When adding any new fields here, please keep the convention <fieldName>=
+ // <value>, with the space between them.
+ private static Logger log = LoggerFactory.getLogger(AuditingLogFormatUtil.class.getName());
+
+ // This is the key by which audit marker is recognized in logback.xml
+ private static String AUDIT_MARKER_STR = "AUDIT_MARKER";
+
+ public static Marker auditMarker = MarkerFactory.getMarker(AUDIT_MARKER_STR);
+
+ protected static void logAuditEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ log.trace("logAuditEvent - start");
+
+ try {
+
+ // Common fields
+ String modifier = getModifier((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME), (String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID));
+ Object statusObj = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ String status = null;
+ if (statusObj != null) {
+ status = String.valueOf(statusObj);
+ }
+ String desc = (String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ String action = (String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+
+ AuditingActionEnum auditEventType = AuditingActionEnum.getActionByName(action);
+ StringBuilder formattedEvent = getFormattedEvent(auditingFields, modifier, status, desc, action, auditEventType);
+ String formattedString = formattedEvent.toString();
+
+ // This is the only way to fix DE166225 without major refactoring,
+ // after it was previously agreed with Ella that activity type will
+ // be the method name.
+
+ if (auditEventType.equals(AuditingActionEnum.AUTH_REQUEST)) {
+ HttpRequestAuthentication(formattedString);
+ } else {
+ log.info(auditMarker, formattedString);
+ }
+ } catch (Exception e) {
+ log.debug("unexpected error occurred: {} {}", e.getMessage(), e);
+
+ } finally {
+ formatter.close();
+ log.trace("logAuditEvent - end");
+ }
+
+ }
+
+ private static void HttpRequestAuthentication(String formattedString) {
+ log.info(auditMarker, formattedString);
+ }
+
+ private static StringBuilder getFormattedEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields, String modifier, String status, String desc, String action, AuditingActionEnum auditEventType) {
+
+ StringBuilder formattedString = new StringBuilder();
+
+ switch (auditEventType) {
+ case ADD_USER:
+ case DELETE_USER:
+ case UPDATE_USER:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.USER_ADMIN_TEMPLATE_ARRAY, auditingFields);
+
+ break;
+ case USER_ACCESS:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.USER_ACCESS_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case DISTRIBUTION_REGISTER:
+ case DISTRIBUTION_UN_REGISTER:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.DISTRIBUTION_REGISTRATION_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case UPDATE_RESOURCE_METADATA:
+ case CREATE_RESOURCE:
+ case IMPORT_RESOURCE:
+ ArrayList<AuditingFieldsKeysEnum> createResourceList = new ArrayList(Arrays.asList(AuditingLogFormatConstants.CREATE_RESOURCE_TEMPLATE_PREFIX_ARRAY));
+ createResourceList.addAll(Arrays.asList(AuditingLogFormatConstants.CREATE_RESOURCE_TEMPLATE_SUFFIX_ARRAY));
+ if (auditEventType == AuditingActionEnum.IMPORT_RESOURCE) {
+ createResourceList.add(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE);
+ }
+ AuditingFieldsKeysEnum[] createResourceArray = new AuditingFieldsKeysEnum[100];
+ createResourceArray = createResourceList.toArray(createResourceArray);
+ formattedString = buildStringAccrodingToArray(createResourceArray, auditingFields);
+ break;
+ case CHECKIN_RESOURCE:
+ case CHECKOUT_RESOURCE:
+ case UNDO_CHECKOUT_RESOURCE:
+ case CERTIFICATION_REQUEST_RESOURCE:
+ case START_CERTIFICATION_RESOURCE:
+ case CERTIFICATION_SUCCESS_RESOURCE:
+ case FAIL_CERTIFICATION_RESOURCE:
+ case CANCEL_CERTIFICATION_RESOURCE:
+ ArrayList<AuditingFieldsKeysEnum> checkinFieldsList = new ArrayList(Arrays.asList(AuditingLogFormatConstants.CREATE_RESOURCE_TEMPLATE_PREFIX_ARRAY));
+ checkinFieldsList.add(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT);
+ checkinFieldsList.addAll(Arrays.asList(AuditingLogFormatConstants.CREATE_RESOURCE_TEMPLATE_SUFFIX_ARRAY));
+ AuditingFieldsKeysEnum[] checkinFieldsArray = new AuditingFieldsKeysEnum[100];
+ checkinFieldsArray = checkinFieldsList.toArray(checkinFieldsArray);
+ String comment = (String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT);
+ if (comment == null || comment.equals(Constants.NULL_STRING)) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, Constants.EMPTY_STRING);
+ }
+ formattedString = buildStringAccrodingToArray(checkinFieldsArray, auditingFields);
+ break;
+ case ARTIFACT_UPLOAD:
+ case ARTIFACT_DELETE:
+ case ARTIFACT_METADATA_UPDATE:
+ case ARTIFACT_PAYLOAD_UPDATE:
+ case ARTIFACT_DOWNLOAD:
+ ArrayList<AuditingFieldsKeysEnum> artifactFieldsList = new ArrayList(Arrays.asList(AuditingLogFormatConstants.CREATE_RESOURCE_TEMPLATE_PREFIX_ARRAY));
+ artifactFieldsList.add(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID);
+ artifactFieldsList.add(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID);
+ artifactFieldsList.add(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA);
+ artifactFieldsList.addAll(Arrays.asList(AuditingLogFormatConstants.CREATE_RESOURCE_TEMPLATE_SUFFIX_ARRAY));
+ artifactFieldsList.addAll(Arrays.asList(AuditingLogFormatConstants.EXTERNAL_DOWNLOAD_ARTIFACT_ARRAY));
+ AuditingFieldsKeysEnum[] artifactFieldsArray = new AuditingFieldsKeysEnum[100];
+ artifactFieldsArray = artifactFieldsList.toArray(artifactFieldsArray);
+ formattedString = buildStringAccrodingToArray(artifactFieldsArray, auditingFields);
+ break;
+ case DOWNLOAD_ARTIFACT:
+ ArrayList<AuditingFieldsKeysEnum> downloadArtifactFieldsList = new ArrayList(Arrays.asList(AuditingLogFormatConstants.EXTERNAL_DOWNLOAD_ARTIFACT_ARRAY));
+ AuditingFieldsKeysEnum[] downloadArtifactFieldsArray = new AuditingFieldsKeysEnum[100];
+ artifactFieldsArray = downloadArtifactFieldsList.toArray(downloadArtifactFieldsArray);
+ formattedString = buildStringAccrodingToArray(artifactFieldsArray, auditingFields);
+ break;
+ case DISTRIBUTION_STATE_CHANGE_REQUEST:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.ACTIVATE_DISTRIBUTION_ARRAY, auditingFields);
+ break;
+ case DISTRIBUTION_STATE_CHANGE_APPROV:
+ case DISTRIBUTION_STATE_CHANGE_REJECT:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.CHANGE_DISTRIBUTION_STATUS_ARRAY, auditingFields);
+ break;
+ case CREATE_DISTRIBUTION_TOPIC:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.CREATE_TOPIC_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case ADD_KEY_TO_TOPIC_ACL:
+ case REMOVE_KEY_FROM_TOPIC_ACL:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.ADD_REMOVE_TOPIC_KEY_ACL_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case DISTRIBUTION_STATUS:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.DISTRIBUTION_STATUS_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case DISTRIBUTION_NOTIFY:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.DISTRIBUTION_NOTIFY_ARRAY, auditingFields);
+ break;
+ case DISTRIBUTION_DEPLOY:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.DISTRIBUTION_DEPLOY_ARRAY, auditingFields);
+ break;
+ case GET_UEB_CLUSTER:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.GET_UEB_CLUSTER_ARRAY, auditingFields);
+ break;
+ case DISTRIBUTION_ARTIFACT_DOWNLOAD:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.DISTRIBUTION_DOWNLOAD_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case AUTH_REQUEST:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.AUTH_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case ADD_ECOMP_USER_CREDENTIALS:
+ case GET_ECOMP_USER_CREDENTIALS:
+ case DELETE_ECOMP_USER_CREDENTIALS:
+ case UPDATE_ECOMP_USER_CREDENTIALS:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.ECOMP_USER_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case ADD_CATEGORY:
+ case ADD_SUB_CATEGORY:
+ case ADD_GROUPING:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.CATEGORY_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case GET_USERS_LIST:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.GET_USERS_LIST_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case GET_CATEGORY_HIERARCHY:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.GET_CATEGORY_HIERARCHY_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case GET_ASSET_LIST:
+ case GET_FILTERED_ASSET_LIST:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.EXTERNAL_GET_ASSET_LIST_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case GET_ASSET_METADATA:
+ case GET_TOSCA_MODEL:
+ formattedString = buildStringAccrodingToArray(AuditingLogFormatConstants.EXTERNAL_GET_ASSET_TEMPLATE_ARRAY, auditingFields);
+ break;
+ case ARTIFACT_UPLOAD_BY_API:
+ case ARTIFACT_DELETE_BY_API:
+ case ARTIFACT_UPDATE_BY_API:
+ ArrayList<AuditingFieldsKeysEnum> uploadArtifactFieldsList = new ArrayList(Arrays.asList(AuditingLogFormatConstants.EXTERNAL_CRUD_API_ARTIFACT_ARRAY));
+ AuditingFieldsKeysEnum[] uploadArtifactFieldsArray = new AuditingFieldsKeysEnum[100];
+ artifactFieldsArray = uploadArtifactFieldsList.toArray(uploadArtifactFieldsArray);
+ formattedString = buildStringAccrodingToArray(artifactFieldsArray, auditingFields);
+ break;
+ default:
+ break;
+ }
+
+ return formattedString;
+ }
+
+ private static StringBuilder buildStringAccrodingToArray(AuditingFieldsKeysEnum[] sortedFieldsArray, EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ StringBuilder formattedString = new StringBuilder();
+ for (int i = 0; i < sortedFieldsArray.length; i++) {
+ AuditingFieldsKeysEnum key = sortedFieldsArray[i];
+
+ Object fieldVal = auditingFields.get(key);
+ if (fieldVal != null) {
+ formattedString.append(key.getDisplayName()).append(" = \"").append(fieldVal).append("\"");
+ if (i < sortedFieldsArray.length - 1) {
+ formattedString.append(" ");
+ }
+ }
+ }
+ return formattedString;
+ }
+
+ protected static String getModifier(String modifierName, String modifierUid) {
+ if (modifierUid == null || modifierUid.equals(Constants.EMPTY_STRING)) {
+ return Constants.EMPTY_STRING;
+ }
+ StringBuilder sb = new StringBuilder();
+ if (modifierName != null) {
+ sb.append(modifierName);
+ }
+ sb.append("(").append(modifierUid).append(")");
+ return sb.toString();
+ }
+
+ protected static String getUser(String userData) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(userData);
+ return sb.toString();
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingManager.java
new file mode 100644
index 0000000000..aa5afa4a8e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/AuditingManager.java
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.auditing.impl;
+
+import java.util.EnumMap;
+import java.util.Map.Entry;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.dao.impl.AuditingDao;
+import org.openecomp.sdc.be.resources.data.auditing.AuditRecordFactory;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingGenericEvent;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component("auditingManager")
+public class AuditingManager implements IAuditingManager {
+
+ private static Logger log = LoggerFactory.getLogger(AuditingManager.class.getName());
+
+ @Resource
+ private AuditingDao auditingDao;
+ @Autowired
+ private AuditCassandraDao cassandraDao;
+
+ @Override
+ public void auditEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ try {
+ boolean disableAudit = ConfigurationManager.getConfigurationManager().getConfiguration().isDisableAudit();
+ if (disableAudit) {
+ return;
+ }
+ // Adding UUID from thread local
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, ThreadLocalsHolder.getUuid());
+
+ Object status = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, String.valueOf(status));
+
+ // normalizing empty string values - US471661
+ normalizeEmptyAuditStringValues(auditingFields);
+
+ // Format modifier
+ formatModifier(auditingFields);
+
+ // Format user
+ formatUser(auditingFields);
+
+ // Logging the event
+ AuditingLogFormatUtil.logAuditEvent(auditingFields);
+
+ // Determining the type of the auditing data object
+ AuditingActionEnum actionEnum = AuditingActionEnum.getActionByName((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION));
+ log.info("audit event {} of type {}", actionEnum.getName(), actionEnum.getAuditingEsType());
+ ActionStatus addRecordStatus = auditingDao.addRecord(auditingFields, actionEnum.getAuditingEsType());
+ if (!addRecordStatus.equals(ActionStatus.OK)) {
+ log.warn("Failed to persist auditing event: {}", addRecordStatus.name());
+ }
+
+ AuditingGenericEvent recordForCassandra = AuditRecordFactory.createAuditRecord(auditingFields);
+ if (recordForCassandra != null) {
+ CassandraOperationStatus result = cassandraDao.saveRecord(recordForCassandra);
+ if (!result.equals(CassandraOperationStatus.OK)) {
+ log.warn("Failed to persist to cassandra auditing event: {}", addRecordStatus.name());
+ }
+ }
+
+ } catch (Exception e) {
+ // Error during auditing shouldn't terminate flow
+ log.warn("Error during auditEvent: {}", e);
+ }
+ }
+
+ private void formatUser(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ if (auditingFields.get(AuditingFieldsKeysEnum.AUDIT_USER_UID) != null) {
+ String userDetails = (String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_USER_UID);
+
+ String user = AuditingLogFormatUtil.getUser(userDetails);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_UID, user);
+ }
+ }
+
+ private void formatModifier(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ String modifier = AuditingLogFormatUtil.getModifier((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME), (String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID));
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier);
+ auditingFields.remove(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME);
+ }
+
+ private void normalizeEmptyAuditStringValues(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ for (Entry<AuditingFieldsKeysEnum, Object> auditingEntry : auditingFields.entrySet()) {
+ if (auditingEntry.getKey().getValueClass().equals(String.class)) {
+ String auditingValue = (String) auditingEntry.getValue();
+ boolean isEmpty = false;
+ if (auditingValue != null) {
+ String trimmedValue = auditingValue.trim();
+ if ((trimmedValue.equals(Constants.EMPTY_STRING)) || trimmedValue.equals(Constants.NULL_STRING) || trimmedValue.equals(Constants.DOUBLE_NULL_STRING)) {
+ isEmpty = true;
+ }
+ } else {// is null
+ isEmpty = true;
+ }
+ // Normalizing to ""
+ if (isEmpty) {
+ auditingEntry.setValue(Constants.EMPTY_STRING);
+ }
+ }
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/AsdcComponentsCleanerTask.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/AsdcComponentsCleanerTask.java
new file mode 100644
index 0000000000..b3b842b9e4
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/AsdcComponentsCleanerTask.java
@@ -0,0 +1,175 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.clean;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.CleanComponentsConfiguration;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component("asdcComponentsCleaner")
+public class AsdcComponentsCleanerTask implements Runnable {
+
+ private static Logger log = LoggerFactory.getLogger(AsdcComponentsCleanerTask.class.getName());
+
+ @javax.annotation.Resource
+ private ComponentsCleanBusinessLogic componentsCleanBusinessLogic = null;
+
+ private List<NodeTypeEnum> componentsToClean;
+ private long cleaningIntervalInMinutes;
+
+ private ScheduledExecutorService scheduledService = Executors.newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("ComponentsCleanThread-%d").build());
+ ScheduledFuture<?> scheduledFuture = null;
+
+ @PostConstruct
+ public void init() {
+ log.trace("Enter init method of AsdcComponentsCleaner");
+ Configuration configuration = ConfigurationManager.getConfigurationManager().getConfiguration();
+ CleanComponentsConfiguration cleanComponentsConfiguration = configuration.getCleanComponentsConfiguration();
+
+ if (cleanComponentsConfiguration == null) {
+ log.info("ERROR - configuration is not valid!!! missing cleanComponentsConfiguration");
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeComponentCleanerSystemError, "AsdcComponentsCleanerTask.init()", "AsdcComponentsCleanerTask.init()");
+ BeEcompErrorManager.getInstance().logBeComponentCleanerSystemError("AsdcComponentsCleanerTask-init", "fecth configuration");
+ return;
+
+ }
+ componentsToClean = new ArrayList<NodeTypeEnum>();
+ List<String> components = cleanComponentsConfiguration.getComponentsToClean();
+ if (components == null) {
+ log.info("no component were configured for cleaning");
+ }
+ for (String component : components) {
+ NodeTypeEnum typeEnum = NodeTypeEnum.getByNameIgnoreCase(component);
+ if (typeEnum != null)
+ componentsToClean.add(typeEnum);
+ }
+
+ long intervalInMinutes = cleanComponentsConfiguration.getCleanIntervalInMinutes();
+
+ if (intervalInMinutes < 1) {
+ log.warn("cleaningIntervalInMinutes value should be greater than or equal to 1 minute. use default");
+ intervalInMinutes = 60;
+ }
+ cleaningIntervalInMinutes = intervalInMinutes;
+
+ startTask();
+
+ log.trace("End init method of AsdcComponentsCleaner");
+ }
+
+ @PreDestroy
+ public void destroy() {
+ this.stopTask();
+ shutdownExecutor();
+ }
+
+ public void startTask() {
+
+ log.debug("start task for cleaning components");
+
+ try {
+
+ if (scheduledService != null) {
+ log.debug("Start Cleaning components task. interval {} minutes", cleaningIntervalInMinutes);
+ scheduledFuture = scheduledService.scheduleAtFixedRate(this, 5, cleaningIntervalInMinutes, TimeUnit.MINUTES);
+
+ }
+ } catch (Exception e) {
+ log.debug("unexpected error occured", e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeComponentCleanerSystemError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeComponentCleanerSystemError("AsdcComponentsCleanerTask-startTask", e.getMessage());
+
+ }
+ }
+
+ public void stopTask() {
+ if (scheduledFuture != null) {
+ boolean result = scheduledFuture.cancel(true);
+ log.debug("Stop cleaning task. result = {}", result);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ if (false == result) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeComponentCleanerSystemError, methodName, "try to stop the polling task");
+ BeEcompErrorManager.getInstance().logBeComponentCleanerSystemError("AsdcComponentsCleanerTask-stopTask", "try to stop the polling task");
+ }
+ scheduledFuture = null;
+ }
+
+ }
+
+ private void shutdownExecutor() {
+ if (scheduledService == null)
+ return;
+
+ scheduledService.shutdown(); // Disable new tasks from being submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!scheduledService.awaitTermination(60, TimeUnit.SECONDS)) {
+ scheduledService.shutdownNow(); // Cancel currently executing
+ // tasks
+ // Wait a while for tasks to respond to being cancelled
+ if (!scheduledService.awaitTermination(60, TimeUnit.SECONDS))
+ log.debug("Pool did not terminate");
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ scheduledService.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ componentsCleanBusinessLogic.cleanComponents(componentsToClean);
+ } catch (Throwable e) {
+ log.error("unexpected error occured", e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeComponentCleanerSystemError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeComponentCleanerSystemError("AsdcComponentsCleanerTask-run", e.getMessage());
+ }
+
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/ComponentsCleanBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/ComponentsCleanBusinessLogic.java
new file mode 100644
index 0000000000..b12b55ba8b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/clean/ComponentsCleanBusinessLogic.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.clean;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.components.impl.BaseBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("componentsCleanBusinessLogic")
+public class ComponentsCleanBusinessLogic extends BaseBusinessLogic {
+
+ @Autowired
+ private ResourceBusinessLogic resourceBusinessLogic;
+
+ @Autowired
+ private ServiceBusinessLogic serviceBusinessLogic;
+
+ private static Logger log = LoggerFactory.getLogger(ComponentsCleanBusinessLogic.class.getName());
+
+ public Map<NodeTypeEnum, Either<List<String>, ResponseFormat>> cleanComponents(List<NodeTypeEnum> componentsToClean) {
+
+ Map<NodeTypeEnum, Either<List<String>, ResponseFormat>> cleanedComponents = new HashMap<NodeTypeEnum, Either<List<String>, ResponseFormat>>();
+
+ log.trace("start cleanComponents");
+ for (NodeTypeEnum type : componentsToClean) {
+ switch (type) {
+ case Resource:
+ processDeletionForType(cleanedComponents, NodeTypeEnum.Resource, resourceBusinessLogic);
+ break;
+ case Service:
+ processDeletionForType(cleanedComponents, NodeTypeEnum.Service, serviceBusinessLogic);
+ break;
+ default:
+ log.debug("{} component type does not have cleaning method defined", type);
+ break;
+ }
+ }
+
+ log.trace("end cleanComponents");
+ return cleanedComponents;
+ }
+
+ private void processDeletionForType(Map<NodeTypeEnum, Either<List<String>, ResponseFormat>> cleanedComponents, NodeTypeEnum type, ComponentBusinessLogic componentBusinessLogic) {
+ Either<List<String>, ResponseFormat> deleteMarkedResources = componentBusinessLogic.deleteMarkedComponents();
+ if (deleteMarkedResources.isRight()) {
+ log.debug("failed to clean deleted components of type {}. error: {}", type, deleteMarkedResources.right().value().getFormattedMessage());
+ } else {
+ if (log.isDebugEnabled()) {
+ StringBuilder sb = new StringBuilder("list of deleted components - type " + type + ": ");
+ for (String id : deleteMarkedResources.left().value()) {
+ sb.append(id).append(", ");
+ }
+ log.debug(sb.toString());
+ }
+ }
+ cleanedComponents.put(type, deleteMarkedResources);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ArtifactInfoImpl.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ArtifactInfoImpl.java
new file mode 100644
index 0000000000..e1a1270fe3
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ArtifactInfoImpl.java
@@ -0,0 +1,196 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+
+public class ArtifactInfoImpl implements IArtifactInfo {
+
+ private String artifactName;
+ private ArtifactTypeEnum artifactType;
+ private String artifactURL;
+ private String artifactChecksum;
+ private String artifactDescription;
+ private Integer artifactTimeout;
+ private String artifactUUID;
+ private String artifactVersion;
+ private String generatedFromUUID;
+ private List<String> relatedArtifacts;
+
+ public ArtifactInfoImpl() {
+ }
+
+ private ArtifactInfoImpl(ArtifactDefinition artifactDef, String generatedFromUUID, List<String> relatedArtifacts) {
+ artifactName = artifactDef.getArtifactName();
+ artifactType = ArtifactTypeEnum.findType(artifactDef.getArtifactType());
+ artifactChecksum = artifactDef.getArtifactChecksum();
+ artifactDescription = artifactDef.getDescription();
+ artifactTimeout = artifactDef.getTimeout();
+ artifactUUID = artifactDef.getArtifactUUID();
+ artifactVersion = artifactDef.getArtifactVersion();
+ this.relatedArtifacts = relatedArtifacts;
+ this.generatedFromUUID = generatedFromUUID;
+ }
+
+ public static List<ArtifactInfoImpl> convertToArtifactInfoImpl(Service service, ComponentInstance resourceInstance, Collection<ArtifactDefinition> list) {
+ List<ArtifactInfoImpl> ret = new ArrayList<ArtifactInfoImpl>();
+ Map<String, List<ArtifactDefinition>> artifactIdToDef = list.stream().collect(Collectors.groupingBy(ArtifactDefinition::getUniqueId));
+ if (list != null) {
+ for (ArtifactDefinition artifactDef : list) {
+ String generatedFromUUID = null;
+ if (artifactDef.getGeneratedFromId() != null && !artifactDef.getGeneratedFromId().isEmpty()) {
+ ArtifactDefinition artifactFrom = artifactIdToDef.get(artifactDef.getGeneratedFromId()).get(0);
+ generatedFromUUID = artifactFrom.getArtifactUUID();
+ }
+ ArtifactInfoImpl artifactInfoImpl = new ArtifactInfoImpl(artifactDef, generatedFromUUID, getUpdatedRequiredArtifactsFromNamesToUuids(artifactDef, resourceInstance.getDeploymentArtifacts()));
+ String artifactURL = ServiceDistributionArtifactsBuilder.buildResourceInstanceArtifactUrl(service, resourceInstance, artifactDef.getArtifactName());
+ artifactInfoImpl.setArtifactURL(artifactURL);
+ ret.add(artifactInfoImpl);
+ }
+ }
+ return ret;
+
+ }
+
+ public static List<ArtifactInfoImpl> convertServiceArtifactToArtifactInfoImpl(Service service, Collection<ArtifactDefinition> list) {
+ List<ArtifactInfoImpl> ret = new ArrayList<ArtifactInfoImpl>();
+ Map<String, List<ArtifactDefinition>> artifactIdToDef = list.stream().collect(Collectors.groupingBy(ArtifactDefinition::getUniqueId));
+ if (list != null) {
+ for (ArtifactDefinition artifactDef : list) {
+ String generatedFromUUID = null;
+ if (artifactDef.getGeneratedFromId() != null && !artifactDef.getGeneratedFromId().isEmpty()) {
+ ArtifactDefinition artifactFrom = artifactIdToDef.get(artifactDef.getGeneratedFromId()).get(0);
+ generatedFromUUID = artifactFrom.getArtifactUUID();
+ }
+ ArtifactInfoImpl artifactInfoImpl = new ArtifactInfoImpl(artifactDef, generatedFromUUID, getUpdatedRequiredArtifactsFromNamesToUuids(artifactDef, service.getDeploymentArtifacts()));
+ String artifactURL = ServiceDistributionArtifactsBuilder.buildServiceArtifactUrl(service, artifactDef.getArtifactName());
+ artifactInfoImpl.setArtifactURL(artifactURL);
+ ret.add(artifactInfoImpl);
+ }
+ }
+ return ret;
+
+ }
+
+ private static List<String> getUpdatedRequiredArtifactsFromNamesToUuids(ArtifactDefinition artifactDefinition, Map<String, ArtifactDefinition> artifacts) {
+ List<String> requiredArtifacts = null;
+ if (artifactDefinition != null && artifactDefinition.getRequiredArtifacts() != null && !artifactDefinition.getRequiredArtifacts().isEmpty() && artifacts != null && !artifacts.isEmpty()) {
+ requiredArtifacts = artifacts.values().stream().filter(art -> artifactDefinition.getRequiredArtifacts().contains(art.getArtifactName())).map(art -> art.getArtifactUUID()).collect(Collectors.toList());
+ }
+ return requiredArtifacts;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public ArtifactTypeEnum getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(ArtifactTypeEnum artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getArtifactURL() {
+ return artifactURL;
+ }
+
+ public void setArtifactURL(String artifactURL) {
+ this.artifactURL = artifactURL;
+ }
+
+ public String getArtifactChecksum() {
+ return artifactChecksum;
+ }
+
+ public void setArtifactChecksum(String artifactChecksum) {
+ this.artifactChecksum = artifactChecksum;
+ }
+
+ public String getArtifactDescription() {
+ return artifactDescription;
+ }
+
+ public void setArtifactDescription(String artifactDescription) {
+ this.artifactDescription = artifactDescription;
+ }
+
+ public Integer getArtifactTimeout() {
+ return artifactTimeout;
+ }
+
+ public void setArtifactTimeout(Integer artifactTimeout) {
+ this.artifactTimeout = artifactTimeout;
+ }
+
+ public List<String> getRelatedArtifacts() {
+ return relatedArtifacts;
+ }
+
+ public void setRelatedArtifacts(List<String> relatedArtifacts) {
+ this.relatedArtifacts = relatedArtifacts;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtifactInfoImpl [artifactName=" + artifactName + ", artifactType=" + artifactType + ", artifactURL=" + artifactURL + ", artifactChecksum=" + artifactChecksum + ", artifactDescription=" + artifactDescription + ", artifactTimeout="
+ + artifactTimeout + ", artifactUUID=" + artifactUUID + ", artifactVersion=" + artifactVersion + ", generatedFromUUID=" + generatedFromUUID + ", relatedArtifacts=" + relatedArtifacts + "]";
+ }
+
+ public String getArtifactUUID() {
+ return artifactUUID;
+ }
+
+ public void setArtifactUUID(String artifactUUID) {
+ this.artifactUUID = artifactUUID;
+ }
+
+ public String getArtifactVersion() {
+ return artifactVersion;
+ }
+
+ public void setArtifactVersion(String artifactVersion) {
+ this.artifactVersion = artifactVersion;
+ }
+
+ public String getGeneratedFromUUID() {
+ return generatedFromUUID;
+ }
+
+ public void setGeneratedFromUUID(String generatedFromUUID) {
+ this.generatedFromUUID = generatedFromUUID;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaErrorResponse.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaErrorResponse.java
new file mode 100644
index 0000000000..149ea2286a
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaErrorResponse.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+
+public class CambriaErrorResponse {
+
+ public static final int HTTP_OK = 200;
+
+ public static final int HTTP_INTERNAL_SERVER_ERROR = 500;
+
+ CambriaOperationStatus operationStatus;
+ Integer httpCode;
+ List<String> variables = new ArrayList<String>();
+
+ public CambriaErrorResponse() {
+ super();
+ }
+
+ public CambriaErrorResponse(CambriaOperationStatus operationStatus) {
+ super();
+ this.operationStatus = operationStatus;
+ }
+
+ public CambriaErrorResponse(CambriaOperationStatus operationStatus, Integer httpCode) {
+ super();
+ this.operationStatus = operationStatus;
+ this.httpCode = httpCode;
+ }
+
+ public CambriaOperationStatus getOperationStatus() {
+ return operationStatus;
+ }
+
+ public void setOperationStatus(CambriaOperationStatus operationStatus) {
+ this.operationStatus = operationStatus;
+ }
+
+ public Integer getHttpCode() {
+ return httpCode;
+ }
+
+ public void setHttpCode(Integer httpCode) {
+ this.httpCode = httpCode;
+ }
+
+ public void addVariable(String variable) {
+ variables.add(variable);
+ }
+
+ public List<String> getVariables() {
+ return variables;
+ }
+
+ public void setVariables(List<String> variables) {
+ this.variables = variables;
+ }
+
+ @Override
+ public String toString() {
+ return "CambriaErrorResponse [operationStatus=" + operationStatus + ", httpCode=" + httpCode + ", variables=" + variables + "]";
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaHandler.java
new file mode 100644
index 0000000000..3528ed9be3
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/CambriaHandler.java
@@ -0,0 +1,610 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.att.nsa.apiClient.http.HttpException;
+import com.att.nsa.apiClient.http.HttpObjectNotFoundException;
+import com.att.nsa.cambria.client.CambriaBatchingPublisher;
+import com.att.nsa.cambria.client.CambriaClient.CambriaApiException;
+import com.att.nsa.cambria.client.CambriaClientBuilders.TopicManagerBuilder;
+import com.att.nsa.cambria.client.CambriaClientBuilders.PublisherBuilder;
+import com.att.nsa.cambria.client.CambriaClientBuilders.ConsumerBuilder;
+import com.att.nsa.cambria.client.CambriaClientBuilders.IdentityManagerBuilder;
+import com.att.nsa.cambria.client.CambriaConsumer;
+import com.att.nsa.cambria.client.CambriaIdentityManager;
+import com.att.nsa.cambria.client.CambriaPublisher.message;
+import com.att.nsa.cambria.client.CambriaTopicManager;
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+public class CambriaHandler {
+
+ private static Logger logger = LoggerFactory.getLogger(CambriaHandler.class.getName());
+
+ public static String PARTITION_KEY = "asdc" + "aa";
+
+ private Gson gson = new Gson();
+
+ /**
+ * process the response error from Cambria client
+ *
+ * @param message
+ * @return
+ */
+ private Integer processMessageException(String message) {
+
+ String[] patterns = { "(HTTP Status )(\\d\\d\\d)", "(HTTP/\\d.\\d )(\\d\\d\\d)" };
+
+ Integer result = checkPattern(patterns[0], message, 2);
+ if (result != null) {
+ return result;
+ }
+ result = checkPattern(patterns[1], message, 2);
+
+ return result;
+
+ }
+
+ /**
+ * check whether the message has a match with a given pattern inside it
+ *
+ * @param patternStr
+ * @param message
+ * @param groupIndex
+ * @return
+ */
+ private Integer checkPattern(String patternStr, String message, int groupIndex) {
+ Integer result = null;
+
+ Pattern pattern = Pattern.compile(patternStr);
+ Matcher matcher = pattern.matcher(message);
+ boolean find = matcher.find();
+ if (find) {
+ String httpCode = matcher.group(groupIndex);
+ if (httpCode != null) {
+ try {
+ result = Integer.valueOf(httpCode);
+ } catch (NumberFormatException e) {
+ logger.debug("Failed to parse http code {}", httpCode);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * retrieve all topics from U-EB server
+ *
+ * @param hostSet
+ * @return
+ */
+ public Either<Set<String>, CambriaErrorResponse> getTopics(List<String> hostSet) {
+
+ CambriaTopicManager createTopicManager = null;
+ try {
+
+ createTopicManager = new TopicManagerBuilder().usingHosts(hostSet).build();
+ Set<String> topics = createTopicManager.getTopics();
+
+ if (topics == null || true == topics.isEmpty()) {
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.NOT_FOUND, null);
+ return Either.right(cambriaErrorResponse);
+ }
+
+ return Either.left(topics);
+
+ } catch (IOException | GeneralSecurityException e) {
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ CambriaErrorResponse cambriaErrorResponse = processError(e);
+
+ logger.debug("Failed to fetch topics from U-EB server", e);
+ writeErrorToLog(cambriaErrorResponse, e.getMessage(), methodName, "get topics");
+
+ return Either.right(cambriaErrorResponse);
+ } finally {
+ if (createTopicManager != null) {
+ createTopicManager.close();
+ }
+ }
+
+ }
+
+ /**
+ * process the error message from Cambria client.
+ *
+ * set Cambria status and http code in case we succeed to fetch it
+ *
+ * @param errorMessage
+ * @return
+ */
+ private CambriaErrorResponse processError(Exception e) {
+
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse();
+
+ Integer httpCode = processMessageException(e.getMessage());
+
+ if (httpCode != null) {
+ cambriaErrorResponse.setHttpCode(httpCode);
+ switch (httpCode.intValue()) {
+
+ case 401:
+ cambriaErrorResponse.setOperationStatus(CambriaOperationStatus.AUTHENTICATION_ERROR);
+ break;
+ case 409:
+ cambriaErrorResponse.setOperationStatus(CambriaOperationStatus.TOPIC_ALREADY_EXIST);
+ break;
+ case 500:
+ cambriaErrorResponse.setOperationStatus(CambriaOperationStatus.INTERNAL_SERVER_ERROR);
+ break;
+ default:
+ cambriaErrorResponse.setOperationStatus(CambriaOperationStatus.CONNNECTION_ERROR);
+ }
+ } else {
+
+ boolean found = false;
+ Throwable throwable = e.getCause();
+ if (throwable != null) {
+ String message = throwable.getMessage();
+
+ Throwable cause = throwable.getCause();
+
+ if (cause != null) {
+ Class<?> clazz = cause.getClass();
+ String className = clazz.getName();
+ if (className.endsWith("UnknownHostException")) {
+ cambriaErrorResponse.setOperationStatus(CambriaOperationStatus.UNKNOWN_HOST_ERROR);
+ cambriaErrorResponse.addVariable(message);
+ found = true;
+ }
+ }
+ }
+
+ if (false == found) {
+ cambriaErrorResponse.setOperationStatus(CambriaOperationStatus.CONNNECTION_ERROR);
+ cambriaErrorResponse.setHttpCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ return cambriaErrorResponse;
+ }
+
+ /**
+ * write the error to the log
+ *
+ * @param cambriaErrorResponse
+ * @param errorMessage
+ * @param methodName
+ * @param operationDesc
+ */
+ private void writeErrorToLog(CambriaErrorResponse cambriaErrorResponse, String errorMessage, String methodName, String operationDesc) {
+
+ String httpCode = (cambriaErrorResponse.getHttpCode() == null ? "" : String.valueOf(cambriaErrorResponse.getHttpCode()));
+
+ switch (cambriaErrorResponse.getOperationStatus()) {
+ case UNKNOWN_HOST_ERROR:
+ String hostname = cambriaErrorResponse.getVariables().get(0);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebUnkownHostError, methodName, hostname);
+ BeEcompErrorManager.getInstance().logBeUebUnkownHostError(methodName, httpCode);
+ break;
+ case AUTHENTICATION_ERROR:
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebAuthenticationError, methodName, httpCode);
+ BeEcompErrorManager.getInstance().logBeUebAuthenticationError(methodName, httpCode);
+ break;
+ case CONNNECTION_ERROR:
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebConnectionError, methodName, httpCode);
+ BeEcompErrorManager.getInstance().logBeUebConnectionError(methodName, httpCode);
+ break;
+
+ case INTERNAL_SERVER_ERROR:
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, methodName, operationDesc);
+ BeEcompErrorManager.getInstance().logBeUebSystemError(methodName, operationDesc);
+ break;
+
+ }
+
+ }
+
+ /**
+ * create a topic if it does not exists in the topicsList
+ *
+ * @param hostSet
+ * - list of U-EB servers
+ * @param apiKey
+ * @param secretKey
+ * @param topicsList
+ * - list of exists topics
+ * @param topicName
+ * - topic to create
+ * @param partitionCount
+ * @param replicationCount
+ * @return
+ */
+ public CambriaErrorResponse createTopic(Collection<String> hostSet, String apiKey, String secretKey, String topicName, int partitionCount, int replicationCount) {
+
+ CambriaTopicManager createTopicManager = null;
+ try {
+ createTopicManager = new TopicManagerBuilder().usingHosts(hostSet).authenticatedBy(apiKey, secretKey).build();
+ createTopicManager.createTopic(topicName, "ASDC distribution notification topic", partitionCount, replicationCount);
+
+ } catch (GeneralSecurityException | HttpException | IOException e) {
+ logger.debug("Failed to create topic {}", topicName, e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ CambriaErrorResponse cambriaErrorResponse = processError(e);
+
+ if (cambriaErrorResponse.getOperationStatus() != CambriaOperationStatus.TOPIC_ALREADY_EXIST) {
+ writeErrorToLog(cambriaErrorResponse, e.getMessage(), methodName, "create topic");
+ }
+
+ return cambriaErrorResponse;
+
+ } finally {
+ if (createTopicManager != null) {
+ createTopicManager.close();
+ }
+ }
+ return new CambriaErrorResponse(CambriaOperationStatus.OK);
+ }
+
+ public CambriaErrorResponse unRegisterFromTopic(Collection<String> hostSet, String topicName, String managerApiKey, String managerSecretKey, String subscriberApiKey, SubscriberTypeEnum subscriberTypeEnum) {
+ CambriaTopicManager createTopicManager = null;
+ try {
+ createTopicManager = new TopicManagerBuilder().usingHosts(hostSet).authenticatedBy(managerApiKey, managerSecretKey).build();
+
+ if (subscriberTypeEnum == SubscriberTypeEnum.PRODUCER) {
+ createTopicManager.revokeProducer(topicName, subscriberApiKey);
+ } else {
+ createTopicManager.revokeConsumer(topicName, subscriberApiKey);
+ }
+
+ } catch (HttpObjectNotFoundException | GeneralSecurityException e) {
+ logger.debug("Failed to unregister {} from topic {} as {}", managerApiKey, topicName, subscriberTypeEnum.toString().toLowerCase(), e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebObjectNotFoundError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeUebObjectNotFoundError(methodName, e.getMessage());
+
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OBJECT_NOT_FOUND, HttpStatus.SC_NOT_FOUND);
+ return cambriaErrorResponse;
+
+ } catch (HttpException | IOException e) {
+ logger.debug("Failed to unregister {} from topic {} as producer", managerApiKey, topicName, e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ CambriaErrorResponse cambriaErrorResponse = processError(e);
+
+ writeErrorToLog(cambriaErrorResponse, e.getMessage(), methodName, "unregister from topic as " + subscriberTypeEnum.toString().toLowerCase());
+
+ return cambriaErrorResponse;
+ } finally {
+ if (createTopicManager != null) {
+ createTopicManager.close();
+ }
+ }
+
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, HttpStatus.SC_OK);
+ return cambriaErrorResponse;
+ }
+
+ /**
+ *
+ * register a public key (subscriberId) to a given topic as a CONSUMER or PRODUCER
+ *
+ * @param hostSet
+ * @param topicName
+ * @param managerApiKey
+ * @param managerSecretKey
+ * @param subscriberApiKey
+ * @param subscriberTypeEnum
+ * @return
+ */
+ public CambriaErrorResponse registerToTopic(Collection<String> hostSet, String topicName, String managerApiKey, String managerSecretKey, String subscriberApiKey, SubscriberTypeEnum subscriberTypeEnum) {
+
+ CambriaTopicManager createTopicManager = null;
+ try {
+ createTopicManager = new TopicManagerBuilder().usingHosts(hostSet).authenticatedBy(managerApiKey, managerSecretKey).build();
+
+ if (subscriberTypeEnum == SubscriberTypeEnum.PRODUCER) {
+ createTopicManager.allowProducer(topicName, subscriberApiKey);
+ } else {
+ createTopicManager.allowConsumer(topicName, subscriberApiKey);
+ }
+
+ } catch (HttpObjectNotFoundException | GeneralSecurityException e) {
+ logger.debug("Failed to register {} to topic {} as {}", managerApiKey, topicName, subscriberTypeEnum.toString().toLowerCase(), e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebObjectNotFoundError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeUebObjectNotFoundError(methodName, e.getMessage());
+
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OBJECT_NOT_FOUND, HttpStatus.SC_NOT_FOUND);
+ return cambriaErrorResponse;
+
+ } catch (HttpException | IOException e) {
+ logger.debug("Failed to register {} to topic {} as {}", managerApiKey, topicName, subscriberTypeEnum.toString().toLowerCase(), e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ CambriaErrorResponse cambriaErrorResponse = processError(e);
+
+ writeErrorToLog(cambriaErrorResponse, e.getMessage(), methodName, "register to topic as " + subscriberTypeEnum.toString().toLowerCase());
+
+ return cambriaErrorResponse;
+ } finally {
+ if (createTopicManager != null) {
+ createTopicManager.close();
+ }
+ }
+
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, HttpStatus.SC_OK);
+ return cambriaErrorResponse;
+ }
+
+ /**
+ * create and retrieve a Cambria Consumer for a specific topic
+ *
+ * @param hostSet
+ * @param topicName
+ * @param apiKey
+ * @param secretKey
+ * @param consumerId
+ * @param consumerGroup
+ * @param timeoutMS
+ * @return
+ * @throws Exception
+ */
+ public CambriaConsumer createConsumer(Collection<String> hostSet, String topicName, String apiKey, String secretKey, String consumerId, String consumerGroup, int timeoutMS) throws Exception {
+
+ CambriaConsumer consumer = new ConsumerBuilder().authenticatedBy(apiKey, secretKey).knownAs(consumerGroup, consumerId).onTopic(topicName).usingHosts(hostSet).withSocketTimeout(timeoutMS).build();
+ consumer.setApiCredentials(apiKey, secretKey);
+ return consumer;
+ }
+
+ public void closeConsumer(CambriaConsumer consumer) {
+
+ if (consumer != null) {
+ consumer.close();
+ }
+
+ }
+
+ /**
+ * use the topicConsumer to fetch messages from topic. in case no messages were fetched, empty ArrayList will be returned (not error)
+ *
+ * @param topicConsumer
+ * @return
+ */
+ public Either<Iterable<String>, CambriaErrorResponse> fetchFromTopic(CambriaConsumer topicConsumer) {
+
+ try {
+ Iterable<String> messages = topicConsumer.fetch();
+ if (messages == null) {
+ messages = new ArrayList<String>();
+ }
+ return Either.left(messages);
+
+ } catch (IOException e) {
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ CambriaErrorResponse cambriaErrorResponse = processError(e);
+
+ logger.debug("Failed to fetch from U-EB topic. error={}", e.getMessage());
+ writeErrorToLog(cambriaErrorResponse, e.getMessage(), methodName, "get messages from topic");
+
+ return Either.right(cambriaErrorResponse);
+
+ } catch (Exception e) {
+ logger.debug("Failed to fetch from U-EB topic", e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(methodName, e.getMessage());
+
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.INTERNAL_SERVER_ERROR, HttpStatus.SC_INTERNAL_SERVER_ERROR);
+ return Either.right(cambriaErrorResponse);
+ }
+ }
+
+ /**
+ * Publish notification message to a given queue
+ *
+ * @param topicName
+ * @param uebPublicKey
+ * @param uebSecretKey
+ * @param uebServers
+ * @param data
+ * @return
+ */
+ public CambriaErrorResponse sendNotification(String topicName, String uebPublicKey, String uebSecretKey, List<String> uebServers, INotificationData data) {
+
+ CambriaBatchingPublisher createSimplePublisher = null;
+
+ try {
+
+ String json = gson.toJson(data);
+ logger.trace("Before sending notification data {} to topic {}", json, topicName);
+
+ createSimplePublisher = new PublisherBuilder().onTopic(topicName).usingHosts(uebServers).build();
+ createSimplePublisher.setApiCredentials(uebPublicKey, uebSecretKey);
+
+ int result = createSimplePublisher.send(PARTITION_KEY, json);
+
+ try {
+ Thread.sleep(1 * 1000);
+ } catch (InterruptedException e) {
+ logger.debug("Failed during sleep after sending the message.", e);
+ }
+
+ logger.debug("After sending notification data to topic {}. result is {}", topicName, result);
+
+ CambriaErrorResponse response = new CambriaErrorResponse(CambriaOperationStatus.OK, 200);
+
+ return response;
+
+ } catch (IOException | GeneralSecurityException e) {
+ logger.debug("Failed to send notification {} to topic {} ", data, topicName, e);
+
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ CambriaErrorResponse cambriaErrorResponse = processError(e);
+
+ writeErrorToLog(cambriaErrorResponse, e.getMessage(), methodName, "send notification");
+
+ return cambriaErrorResponse;
+ } finally {
+ if (createSimplePublisher != null) {
+ logger.debug("Before closing publisher");
+ createSimplePublisher.close();
+ logger.debug("After closing publisher");
+ }
+ }
+ }
+
+ private String convertListToString(List<String> list) {
+ StringBuilder builder = new StringBuilder();
+
+ if (list != null) {
+ for (int i = 0; i < list.size(); i++) {
+ builder.append(list.get(i));
+ if (i < list.size() - 1) {
+ builder.append(",");
+ }
+ }
+ }
+
+ return builder.toString();
+ }
+
+ public CambriaErrorResponse sendNotificationAndClose(String topicName, String uebPublicKey, String uebSecretKey, List<String> uebServers, INotificationData data, long waitBeforeCloseTimeout) {
+
+ CambriaBatchingPublisher createSimplePublisher = null;
+
+ CambriaErrorResponse response = null;
+ try {
+
+ String json = gson.toJson(data);
+ logger.debug("Before sending notification data {} to topic {}", json, topicName);
+
+ createSimplePublisher = new PublisherBuilder().onTopic(topicName).usingHosts(uebServers).build();
+ createSimplePublisher.setApiCredentials(uebPublicKey, uebSecretKey);
+
+ int result = createSimplePublisher.send(PARTITION_KEY, json);
+
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ logger.debug("Failed during sleep after sending the message.", e);
+ }
+
+ logger.debug("After sending notification data to topic {}. result is {}", topicName, result);
+
+ } catch (IOException | GeneralSecurityException e) {
+ logger.debug("Failed to send notification {} to topic {} ", data, topicName, e);
+
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ response = processError(e);
+
+ writeErrorToLog(response, e.getMessage(), methodName, "send notification");
+
+ return response;
+
+ }
+
+ logger.debug("Before closing publisher. Maximum timeout is {} seconds.", waitBeforeCloseTimeout);
+ try {
+ List<message> messagesInQ = createSimplePublisher.close(waitBeforeCloseTimeout, TimeUnit.SECONDS);
+ if (messagesInQ != null && false == messagesInQ.isEmpty()) {
+ logger.debug("Cambria client returned {} non sent messages.", messagesInQ.size());
+ response = new CambriaErrorResponse(CambriaOperationStatus.INTERNAL_SERVER_ERROR, 500);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ writeErrorToLog(response, "closing publisher returned non sent messages", methodName, "send notification");
+ } else {
+ logger.debug("No message left in the queue after closing cambria publisher");
+ response = new CambriaErrorResponse(CambriaOperationStatus.OK, 200);
+ }
+ } catch (IOException | InterruptedException e) {
+ logger.debug("Failed to close cambria publisher", e);
+ response = new CambriaErrorResponse(CambriaOperationStatus.INTERNAL_SERVER_ERROR, 500);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+ writeErrorToLog(response, "closing publisher returned non sent messages", methodName, "send notification");
+ }
+ logger.debug("After closing publisher");
+
+ return response;
+
+ }
+
+ public CambriaErrorResponse getApiKey(String server, String apiKey) {
+
+ CambriaErrorResponse response = null;
+
+ List<String> hostSet = new ArrayList<>();
+ hostSet.add(server);
+ CambriaIdentityManager createIdentityManager = null;
+ try {
+ createIdentityManager = new IdentityManagerBuilder().usingHosts(hostSet).build();
+ createIdentityManager.getApiKey(apiKey);
+ response = new CambriaErrorResponse(CambriaOperationStatus.OK, 200);
+
+ } catch (HttpException | IOException | CambriaApiException | GeneralSecurityException e) {
+ logger.debug("Failed to fetch api key {} from server ", apiKey, server, e);
+
+ response = processError(e);
+
+ }
+
+ return response;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DeConfigurationStatus.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DeConfigurationStatus.java
new file mode 100644
index 0000000000..5e4c08275f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DeConfigurationStatus.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public enum DeConfigurationStatus {
+
+ OK(""), MISSING_CONFIGURATION("");
+
+ private String description;
+
+ DeConfigurationStatus(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngine.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngine.java
new file mode 100644
index 0000000000..6cb31c8cf2
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngine.java
@@ -0,0 +1,367 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("distributionEngine")
+public class DistributionEngine implements IDistributionEngine {
+
+ public static final Pattern FQDN_PATTERN = Pattern.compile("^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*(:[0-9]{2,4})*$", Pattern.CASE_INSENSITIVE);
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentUtils;
+
+ @javax.annotation.Resource
+ private DistributionNotificationSender distributionNotificationSender;
+
+ @javax.annotation.Resource
+ private ServiceDistributionArtifactsBuilder serviceDistributionArtifactsBuilder;
+
+ @javax.annotation.Resource
+ private DistributionEngineClusterHealth distributionEngineClusterHealth;
+
+ private static Logger logger = LoggerFactory.getLogger(DistributionEngine.class.getName());
+
+ private Map<String, DistributionEngineInitTask> envNamePerInitTask = new HashMap<String, DistributionEngineInitTask>();
+ private Map<String, DistributionEnginePollingTask> envNamePerPollingTask = new HashMap<String, DistributionEnginePollingTask>();
+
+ private Map<String, AtomicBoolean> envNamePerStatus = new HashMap<String, AtomicBoolean>();
+
+ @Override
+ public boolean isActive() {
+
+ if (true == envNamePerInitTask.isEmpty()) {
+ return false;
+ }
+
+ for (DistributionEngineInitTask task : envNamePerInitTask.values()) {
+ boolean active = task.isActive();
+ if (active == false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @PostConstruct
+ private void init() {
+
+ logger.trace("Enter init method of DistributionEngine");
+
+ DistributionEngineConfiguration distributionEngineConfiguration = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+
+ boolean startDistributionEngine = distributionEngineConfiguration.isStartDistributionEngine();
+ logger.debug("Distribution engine activation parameter is {}", startDistributionEngine);
+ if (false == startDistributionEngine) {
+ logger.info("The disribution engine is disabled");
+
+ this.distributionEngineClusterHealth.setHealthCheckUebIsDisabled();
+
+ return;
+ }
+
+ boolean isValidConfig = validateConfiguration(distributionEngineConfiguration);
+
+ if (false == isValidConfig) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, DistributionEngineInitTask.INIT_DISTRIBUTION_ENGINE_FLOW, "validate distribution configuration in init phase");
+ BeEcompErrorManager.getInstance().logBeUebSystemError(DistributionEngineInitTask.INIT_DISTRIBUTION_ENGINE_FLOW, "validate distribution configuration in init phase");
+
+ this.distributionEngineClusterHealth.setHealthCheckUebConfigurationError();
+ return;
+ }
+
+ List<String> environments = distributionEngineConfiguration.getEnvironments();
+
+ for (String envName : environments) {
+
+ DistributionEnginePollingTask distributionEnginePollingTask = new DistributionEnginePollingTask(distributionEngineConfiguration, envName, componentUtils, distributionEngineClusterHealth);
+
+ logger.debug("Init task for environment {}", envName);
+
+ AtomicBoolean status = new AtomicBoolean(false);
+ envNamePerStatus.put(envName, status);
+ DistributionEngineInitTask distributionEngineInitTask = new DistributionEngineInitTask(0l, distributionEngineConfiguration, envName, status, componentUtils, distributionEnginePollingTask);
+ distributionEngineInitTask.startTask();
+ envNamePerInitTask.put(envName, distributionEngineInitTask);
+ envNamePerPollingTask.put(envName, distributionEnginePollingTask);
+ }
+
+ logger.debug("Init UEB health check");
+ distributionEngineClusterHealth.startHealthCheckTask(envNamePerStatus);
+
+ logger.trace("Exit init method of DistributionEngine");
+
+ }
+
+ @PreDestroy
+ public void shutdown() {
+ logger.info("distribution engine shutdown - start");
+ if (envNamePerInitTask != null) {
+ for (DistributionEngineInitTask task : envNamePerInitTask.values()) {
+ task.destroy();
+ }
+ }
+ if (envNamePerPollingTask != null) {
+ for (DistributionEnginePollingTask task : envNamePerPollingTask.values()) {
+ task.destroy();
+ }
+ }
+
+ }
+
+ /**
+ * validate mandatory configuration parameters received
+ *
+ * @param deConfiguration
+ * @return
+ */
+ protected boolean validateConfiguration(DistributionEngineConfiguration deConfiguration) {
+
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ boolean result = true;
+ result = isValidServers(deConfiguration.getUebServers(), methodName, "uebServers") && result;
+ result = isValidParam(deConfiguration.getEnvironments(), methodName, "environments") && result;
+ result = isValidParam(deConfiguration.getUebPublicKey(), methodName, "uebPublicKey") && result;
+ result = isValidParam(deConfiguration.getUebSecretKey(), methodName, "uebSecretKey") && result;
+ result = isValidParam(deConfiguration.getDistributionNotifTopicName(), methodName, "distributionNotifTopicName") && result;
+ result = isValidParam(deConfiguration.getDistributionStatusTopicName(), methodName, "distributionStatusTopicName") && result;
+ result = isValidObject(deConfiguration.getCreateTopic(), methodName, "createTopic") && result;
+ result = isValidObject(deConfiguration.getDistributionStatusTopic(), methodName, "distributionStatusTopic") && result;
+ result = isValidObject(deConfiguration.getInitMaxIntervalSec(), methodName, "initMaxIntervalSec") && result;
+ result = isValidObject(deConfiguration.getInitRetryIntervalSec(), methodName, "initRetryIntervalSec") && result;
+ result = isValidParam(deConfiguration.getDistributionStatusTopic().getConsumerId(), methodName, "consumerId") && result;
+ result = isValidParam(deConfiguration.getDistributionStatusTopic().getConsumerGroup(), methodName, "consumerGroup") && result;
+ result = isValidObject(deConfiguration.getDistributionStatusTopic().getFetchTimeSec(), methodName, "fetchTimeSec") && result;
+ result = isValidObject(deConfiguration.getDistributionStatusTopic().getPollingIntervalSec(), methodName, "pollingIntervalSec") && result;
+
+ return result;
+ }
+
+ private boolean isValidServers(List<String> uebServers, String methodName, String paramName) {
+
+ if (uebServers == null || uebServers.size() == 0) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingConfigurationError, methodName, paramName);
+ BeEcompErrorManager.getInstance().logBeMissingConfigurationError(methodName, paramName);
+ return false;
+ }
+
+ if (uebServers.size() < 2) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeConfigurationInvalidListSizeError, methodName, paramName, "2");
+ BeEcompErrorManager.getInstance().logBeConfigurationInvalidListSizeError(methodName, paramName, 2);
+ return false;
+ }
+
+ for (String serverFqdn : uebServers) {
+ if (false == isValidFqdn(serverFqdn)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidConfigurationError, methodName, paramName, serverFqdn);
+ BeEcompErrorManager.getInstance().logBeInvalidConfigurationError(methodName, paramName, serverFqdn);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean isValidFqdn(String serverFqdn) {
+
+ try {
+ Matcher matcher = FQDN_PATTERN.matcher(serverFqdn);
+ return matcher.matches();
+
+ } catch (Exception e) {
+ logger.debug("Failed to match value of address {}", serverFqdn, e);
+ return false;
+ }
+
+ }
+
+ private boolean isEmptyParam(String param) {
+
+ if (param == null || true == param.isEmpty()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isValidParam(String paramValue, String methodName, String paramName) {
+
+ if (isEmptyParam(paramValue)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingConfigurationError, methodName, paramName);
+ BeEcompErrorManager.getInstance().logBeMissingConfigurationError(methodName, paramName);
+ return false;
+ }
+ return true;
+
+ }
+
+ private boolean isValidParam(List<String> paramValue, String methodName, String paramName) {
+
+ if (isEmptyList(paramValue)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingConfigurationError, methodName, paramName);
+ BeEcompErrorManager.getInstance().logBeMissingConfigurationError(methodName, paramName);
+ return false;
+ }
+ return true;
+
+ }
+
+ private boolean isValidObject(Object paramValue, String methodName, String paramName) {
+
+ if (paramValue == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingConfigurationError, methodName, paramName);
+ BeEcompErrorManager.getInstance().logBeMissingConfigurationError(methodName, paramName);
+ return false;
+ }
+ return true;
+
+ }
+
+ private boolean isEmptyList(List<String> list) {
+ if (list == null || true == list.isEmpty()) {
+ return true;
+ }
+ return false;
+ }
+
+ private String getEnvironmentErrorDescription(StorageOperationStatus status) {
+
+ switch (status) {
+ case DISTR_ENVIRONMENT_NOT_AVAILABLE:
+ return "environment is unavailable";
+ case DISTR_ENVIRONMENT_NOT_FOUND:
+ return "environment is not configured in our system";
+ case DISTR_ENVIRONMENT_SENT_IS_INVALID:
+ return "environment name is invalid";
+
+ default:
+ return "unkhown";
+
+ }
+ }
+
+ public StorageOperationStatus isEnvironmentAvailable(String envName) {
+
+ if (envName == null || true == envName.isEmpty()) {
+
+ return StorageOperationStatus.DISTR_ENVIRONMENT_SENT_IS_INVALID;
+ }
+
+ AtomicBoolean status = envNamePerStatus.get(envName);
+ if (status == null) {
+ return StorageOperationStatus.DISTR_ENVIRONMENT_NOT_FOUND;
+ }
+
+ if (false == status.get()) {
+ return StorageOperationStatus.DISTR_ENVIRONMENT_NOT_AVAILABLE;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ public StorageOperationStatus isEnvironmentAvailable() {
+
+ String envName = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration().getEnvironments().get(0);
+
+ return isEnvironmentAvailable(envName);
+ }
+
+ @Override
+ public void disableEnvironment(String envName) {
+ // TODO disable tasks
+ AtomicBoolean status = envNamePerStatus.get(envName);
+ status.set(false);
+ }
+
+ @Override
+ public StorageOperationStatus notifyService(String distributionId, Service service, INotificationData notificationData, String envName, String userId, String modifierName) {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received notify service request. distributionId = {}, serviceUuid = {}, serviceUid = {}, envName = {}, userId = {}, modifierName {}", distributionId, service.getUUID(), service.getUniqueId(), envName, userId, modifierName);
+ }
+
+ DistributionEngineConfiguration deConfiguration = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+
+ String distributionNotifTopicName = deConfiguration.getDistributionNotifTopicName();
+ String topicName = DistributionEngineInitTask.buildTopicName(distributionNotifTopicName, envName);
+
+ StorageOperationStatus sendNotification = distributionNotificationSender.sendNotification(topicName, distributionId, deConfiguration, envName, notificationData, service, userId, modifierName);
+
+ logger.debug("Finish notifyService. status is {}", sendNotification);
+
+ return sendNotification;
+ }
+
+ @Override
+ public Either<INotificationData, StorageOperationStatus> isReadyForDistribution(Service service, String distributionId, String envName) {
+ StorageOperationStatus status = isEnvironmentAvailable(envName);
+ if (status != StorageOperationStatus.OK) {
+ String envErrorDec = getEnvironmentErrorDescription(status);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, DistributionNotificationSender.DISTRIBUTION_NOTIFICATION_SENDING,
+ "Environment name " + envName + " is not available. Reason : " + envErrorDec);
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(DistributionNotificationSender.DISTRIBUTION_NOTIFICATION_SENDING, "Environment name " + envName + " is not available. Reason : " + envErrorDec);
+ return Either.right(status);
+ }
+
+ Either<Boolean, StorageOperationStatus> isServiceContainsDeploymentArtifactsStatus = serviceDistributionArtifactsBuilder.isServiceContainsDeploymentArtifacts(service);
+ if (isServiceContainsDeploymentArtifactsStatus.isRight()) {
+ StorageOperationStatus operationStatus = isServiceContainsDeploymentArtifactsStatus.right().value();
+ return Either.right(operationStatus);
+ } else {
+ Boolean isDeploymentArtifactExists = isServiceContainsDeploymentArtifactsStatus.left().value();
+ if (isDeploymentArtifactExists == null || isDeploymentArtifactExists.booleanValue() == false) {
+ return Either.right(StorageOperationStatus.DISTR_ARTIFACT_NOT_FOUND);
+ }
+ }
+
+ INotificationData value = serviceDistributionArtifactsBuilder.buildResourceInstanceForDistribution(service, distributionId);
+ value = serviceDistributionArtifactsBuilder.buildServiceForDistribution(value, service);
+
+ return Either.left(value);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineClusterHealth.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineClusterHealth.java
new file mode 100644
index 0000000000..a3d0362c61
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineClusterHealth.java
@@ -0,0 +1,356 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.common.api.HealthCheckInfo;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckComponent;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component("distribution-engine-cluster-health")
+public class DistributionEngineClusterHealth {
+
+ protected static String UEB_HEALTH_LOG_CONTEXT = "ueb.healthcheck";
+
+ private static Logger healthLogger = LoggerFactory.getLogger(UEB_HEALTH_LOG_CONTEXT);
+
+ private static final String UEB_HEALTH_CHECK_STR = "uebHealthCheck";
+
+ boolean lastHealthState = false;
+
+ Object lockOject = new Object();
+
+ private long reconnectInterval = 5;
+
+ private long healthCheckReadTimeout = 20;
+
+ private static Logger logger = LoggerFactory.getLogger(DistributionEngineClusterHealth.class.getName());
+
+ private List<String> uebServers = null;
+
+ private String publicApiKey = null;
+
+ public enum HealthCheckInfoResult {
+
+ OK(new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.UP, null, ClusterStatusDescription.OK.getDescription())), UNAVAILABLE(
+ new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.DOWN, null, ClusterStatusDescription.UNAVAILABLE.getDescription())), NOT_CONFIGURED(
+ new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.DOWN, null, ClusterStatusDescription.NOT_CONFIGURED.getDescription())), DISABLED(
+ new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.DOWN, null, ClusterStatusDescription.DISABLED.getDescription()));
+
+ private HealthCheckInfo healthCheckInfo;
+
+ HealthCheckInfoResult(HealthCheckInfo healthCheckInfo) {
+ this.healthCheckInfo = healthCheckInfo;
+ }
+
+ public HealthCheckInfo getHealthCheckInfo() {
+ return healthCheckInfo;
+ }
+
+ }
+
+ private HealthCheckInfo healthCheckInfo = HealthCheckInfoResult.UNAVAILABLE.getHealthCheckInfo();
+
+ private Map<String, AtomicBoolean> envNamePerStatus = null;
+
+ private ScheduledFuture<?> scheduledFuture = null;
+
+ ScheduledExecutorService healthCheckScheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "UEB-Health-Check-Task");
+ }
+ });
+
+ HealthCheckScheduledTask healthCheckScheduledTask = null;
+
+ public enum ClusterStatusDescription {
+
+ OK("OK"), UNAVAILABLE("U-EB cluster is not available"), NOT_CONFIGURED("U-EB cluster is not configured"), DISABLED("DE is disabled in configuration");
+
+ private String desc;
+
+ ClusterStatusDescription(String desc) {
+ this.desc = desc;
+ }
+
+ public String getDescription() {
+ return desc;
+ }
+
+ }
+
+ /**
+ * Health Check Task Scheduler.
+ *
+ * It schedules a task which send a apiKey get query towards the UEB servers. In case a query to the first UEB server is failed, then a second query is sent to the next UEB server.
+ *
+ *
+ * @author esofer
+ *
+ */
+ public class HealthCheckScheduledTask implements Runnable {
+
+ List<UebHealthCheckCall> healthCheckCalls = new ArrayList<>();
+
+ public HealthCheckScheduledTask(List<String> uebServers) {
+
+ logger.debug("Create health check calls for servers {}", uebServers);
+ if (uebServers != null) {
+ for (String server : uebServers) {
+ healthCheckCalls.add(new UebHealthCheckCall(server, publicApiKey));
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+
+ healthLogger.trace("Executing UEB Health Check Task - Start");
+
+ boolean healthStatus = verifyAtLeastOneEnvIsUp();
+
+ if (true == healthStatus) {
+ boolean queryUebStatus = queryUeb();
+ if (queryUebStatus == lastHealthState) {
+ return;
+ }
+
+ synchronized (lockOject) {
+ if (queryUebStatus != lastHealthState) {
+ logger.trace("UEB Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
+ lastHealthState = queryUebStatus;
+ logAlarm(lastHealthState);
+ if (true == queryUebStatus) {
+ healthCheckInfo = HealthCheckInfoResult.OK.getHealthCheckInfo();
+ } else {
+ healthCheckInfo = HealthCheckInfoResult.UNAVAILABLE.getHealthCheckInfo();
+ }
+ }
+ }
+ } else {
+ healthLogger.trace("Not all UEB Environments are up");
+ }
+
+ }
+
+ /**
+ * verify that at least one environment is up.
+ *
+ */
+ private boolean verifyAtLeastOneEnvIsUp() {
+
+ boolean healthStatus = false;
+
+ if (envNamePerStatus != null) {
+ Collection<AtomicBoolean> values = envNamePerStatus.values();
+ if (values != null) {
+ for (AtomicBoolean status : values) {
+ if (true == status.get()) {
+ healthStatus = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return healthStatus;
+ }
+
+ /**
+ * executor for the query itself
+ */
+ ExecutorService healthCheckExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "UEB-Health-Check-Thread");
+ }
+ });
+
+ /**
+ * go all UEB servers and send a get apiKeys query. In case a query is succeed, no query is sent to the rest of UEB servers.
+ *
+ *
+ * @return
+ */
+ private boolean queryUeb() {
+
+ Boolean result = false;
+ int retryNumber = 1;
+ for (UebHealthCheckCall healthCheckCall : healthCheckCalls) {
+ try {
+
+ healthLogger.debug("Before running Health Check retry query number {} towards UEB server {}", retryNumber, healthCheckCall.getServer());
+
+ Future<Boolean> future = healthCheckExecutor.submit(healthCheckCall);
+ result = future.get(healthCheckReadTimeout, TimeUnit.SECONDS);
+
+ healthLogger.debug("After running Health Check retry query number {} towards UEB server {}. Result is {}", retryNumber, healthCheckCall.getServer(), result);
+
+ if (result != null && true == result.booleanValue()) {
+ break;
+ }
+
+ } catch (Exception e) {
+ String message = e.getMessage();
+ if (message == null) {
+ message = e.getClass().getName();
+ }
+ healthLogger.debug("Error occured during running Health Check retry query towards UEB server {}. Result is {}", healthCheckCall.getServer(), message);
+ }
+ retryNumber++;
+
+ }
+
+ return result;
+
+ }
+
+ public List<UebHealthCheckCall> getHealthCheckCalls() {
+ return healthCheckCalls;
+ }
+
+ }
+
+ @PostConstruct
+ private void init() {
+
+ logger.trace("Enter init method of DistributionEngineClusterHealth");
+
+ Long reconnectIntervalConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getUebHealthCheckReconnectIntervalInSeconds();
+ if (reconnectIntervalConfig != null) {
+ reconnectInterval = reconnectIntervalConfig.longValue();
+ }
+ Long healthCheckReadTimeoutConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getUebHealthCheckReadTimeout();
+ if (healthCheckReadTimeoutConfig != null) {
+ healthCheckReadTimeout = healthCheckReadTimeoutConfig.longValue();
+ }
+
+ DistributionEngineConfiguration distributionEngineConfiguration = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+
+ this.uebServers = distributionEngineConfiguration.getUebServers();
+ this.publicApiKey = distributionEngineConfiguration.getUebPublicKey();
+
+ this.healthCheckScheduledTask = new HealthCheckScheduledTask(this.uebServers);
+
+ logger.trace("Exit init method of DistributionEngineClusterHealth");
+
+ }
+
+ @PreDestroy
+ private void destroy() {
+
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(true);
+ scheduledFuture = null;
+ }
+
+ if (healthCheckScheduler != null) {
+ healthCheckScheduler.shutdown();
+ }
+
+ }
+
+ /**
+ * Start health check task.
+ *
+ * @param envNamePerStatus
+ * @param startTask
+ */
+ public void startHealthCheckTask(Map<String, AtomicBoolean> envNamePerStatus, boolean startTask) {
+ this.envNamePerStatus = envNamePerStatus;
+
+ if (startTask == true && this.scheduledFuture == null) {
+ this.scheduledFuture = this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, reconnectInterval, TimeUnit.SECONDS);
+ }
+ }
+
+ public void startHealthCheckTask(Map<String, AtomicBoolean> envNamePerStatus) {
+ startHealthCheckTask(envNamePerStatus, true);
+ }
+
+ private void logAlarm(boolean lastHealthState) {
+ if (lastHealthState == true) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckRecovery, UEB_HEALTH_CHECK_STR);
+ BeEcompErrorManager.getInstance().logBeHealthCheckUebClusterRecovery(UEB_HEALTH_CHECK_STR);
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckError, UEB_HEALTH_CHECK_STR);
+ BeEcompErrorManager.getInstance().logBeHealthCheckUebClusterError(UEB_HEALTH_CHECK_STR);
+ }
+ }
+
+ public HealthCheckInfo getHealthCheckInfo() {
+ return healthCheckInfo;
+ }
+
+ /**
+ * change the health check to DISABLE
+ */
+ public void setHealthCheckUebIsDisabled() {
+ healthCheckInfo = HealthCheckInfoResult.DISABLED.getHealthCheckInfo();
+ }
+
+ /**
+ * change the health check to NOT CONFGIURED
+ */
+ public void setHealthCheckUebConfigurationError() {
+ healthCheckInfo = HealthCheckInfoResult.NOT_CONFIGURED.getHealthCheckInfo();
+ }
+
+ public void setHealthCheckOkAndReportInCaseLastStateIsDown() {
+
+ if (lastHealthState == true) {
+ return;
+ }
+ synchronized (lockOject) {
+ if (lastHealthState == false) {
+ logger.debug("Going to update health check state to available");
+ lastHealthState = true;
+ healthCheckInfo = HealthCheckInfoResult.OK.getHealthCheckInfo();
+ logAlarm(lastHealthState);
+ }
+ }
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTask.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTask.java
new file mode 100644
index 0000000000..1eeaa1229e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTask.java
@@ -0,0 +1,293 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class DistributionEngineInitTask implements Runnable {
+
+ public static final String INIT_DISTRIBUTION_ENGINE_FLOW = "initDistributionEngine";
+ public static final String ALREADY_EXISTS = "ALREADY_EXISTS";
+ public static final String CONSUMER = "CONSUMER";
+ public static final String PRODUCER = "PRODUCER";
+ public static final String CREATED = "CREATED";
+ public static final String FAILED = "FAILED";
+ public static final Integer HTTP_OK = 200;
+
+ private Long delayBeforeStartFlow = 0l;
+ private DistributionEngineConfiguration deConfiguration;
+ private String envName;
+ private long retryInterval;
+ private long currentRetryInterval;
+ private long maxInterval;
+ // private boolean active = false;
+ boolean maximumRetryInterval = false;
+ private AtomicBoolean status = null;
+ ComponentsUtils componentsUtils = null;
+ DistributionEnginePollingTask distributionEnginePollingTask = null;
+
+ private CambriaHandler cambriaHandler = new CambriaHandler();
+
+ public DistributionEngineInitTask(Long delayBeforeStartFlow, DistributionEngineConfiguration deConfiguration, String envName, AtomicBoolean status, ComponentsUtils componentsUtils, DistributionEnginePollingTask distributionEnginePollingTask) {
+ super();
+ this.delayBeforeStartFlow = delayBeforeStartFlow;
+ this.deConfiguration = deConfiguration;
+ this.envName = envName;
+ this.retryInterval = deConfiguration.getInitRetryIntervalSec();
+ this.currentRetryInterval = retryInterval;
+ this.maxInterval = deConfiguration.getInitMaxIntervalSec();
+ this.status = status;
+ this.componentsUtils = componentsUtils;
+ this.distributionEnginePollingTask = distributionEnginePollingTask;
+ }
+
+ private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
+
+ private static Logger logger = LoggerFactory.getLogger(DistributionEngineInitTask.class.getName());
+
+ ScheduledFuture<?> scheduledFuture = null;
+
+ public void startTask() {
+ if (scheduledExecutorService != null) {
+ Integer retryInterval = deConfiguration.getInitRetryIntervalSec();
+ logger.debug("Start Distribution Engine init task. retry interval {} seconds, delay before first run {} seconds", retryInterval, delayBeforeStartFlow);
+ this.scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(this, delayBeforeStartFlow, retryInterval, TimeUnit.SECONDS);
+
+ }
+ }
+
+ public void restartTask() {
+
+ this.stopTask();
+
+ logger.debug("Start Distribution Engine init task. next run in {} seconds", this.currentRetryInterval);
+
+ long lastCurrentInterval = currentRetryInterval;
+ incrementRetryInterval();
+
+ this.scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(this, lastCurrentInterval, this.currentRetryInterval, TimeUnit.SECONDS);
+
+ }
+
+ protected void incrementRetryInterval() {
+ if (currentRetryInterval < maxInterval) {
+ currentRetryInterval *= 2;
+ if (currentRetryInterval > maxInterval) {
+ setMaxRetryInterval();
+ }
+ } else {
+ setMaxRetryInterval();
+ }
+ }
+
+ private void setMaxRetryInterval() {
+ currentRetryInterval = maxInterval;
+ maximumRetryInterval = true;
+ logger.debug("Set next retry init interval to {}", maxInterval);
+ }
+
+ public void stopTask() {
+ if (scheduledFuture != null) {
+ boolean result = scheduledFuture.cancel(true);
+ logger.debug("Stop reinit task. result = {}", result);
+ if (false == result) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, INIT_DISTRIBUTION_ENGINE_FLOW, "try to stop the reinit task");
+ BeEcompErrorManager.getInstance().logBeUebSystemError(INIT_DISTRIBUTION_ENGINE_FLOW, "try to stop the reinit task");
+ }
+ scheduledFuture = null;
+ }
+ }
+
+ public void destroy() {
+ this.stopTask();
+ if (scheduledExecutorService != null) {
+ scheduledExecutorService.shutdown();
+ }
+ }
+
+ @Override
+ public void run() {
+
+ boolean result = false;
+ result = initFlow();
+
+ if (true == result) {
+ this.stopTask();
+ this.status.set(true);
+ if (this.distributionEnginePollingTask != null) {
+ String topicName = buildTopicName(deConfiguration.getDistributionStatusTopicName(), envName);
+ logger.debug("start polling distribution status topic {}", topicName);
+ this.distributionEnginePollingTask.startTask(topicName);
+ }
+ } else {
+ if (false == maximumRetryInterval) {
+ this.restartTask();
+ }
+ }
+ }
+
+ /**
+ * run initialization flow
+ *
+ * @return
+ */
+ public boolean initFlow() {
+
+ logger.trace("Start init flow for environment {}", this.envName);
+
+ Set<String> topicsList = null;
+ Either<Set<String>, CambriaErrorResponse> getTopicsRes = null;
+
+ getTopicsRes = cambriaHandler.getTopics(deConfiguration.getUebServers());
+ if (getTopicsRes.isRight()) {
+ CambriaErrorResponse status = getTopicsRes.right().value();
+ if (status.getOperationStatus() == CambriaOperationStatus.NOT_FOUND) {
+ topicsList = new HashSet<>();
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, INIT_DISTRIBUTION_ENGINE_FLOW, "try retrieve list of topics from U-EB server");
+
+ BeEcompErrorManager.getInstance().logBeUebSystemError(INIT_DISTRIBUTION_ENGINE_FLOW, "try retrieve list of topics from U-EB server");
+
+ return false;
+ }
+ } else {
+ topicsList = getTopicsRes.left().value();
+ }
+
+ String notificationTopic = buildTopicName(deConfiguration.getDistributionNotifTopicName(), this.envName);
+ logger.debug("Going to handle topic {}", notificationTopic);
+
+ boolean status = createTopicIfNotExists(topicsList, notificationTopic);
+ if (false == status) {
+ return false;
+ }
+
+ CambriaErrorResponse registerProducerStatus = registerToTopic(notificationTopic, SubscriberTypeEnum.PRODUCER);
+
+ CambriaOperationStatus createStatus = registerProducerStatus.getOperationStatus();
+
+ if (createStatus != CambriaOperationStatus.OK) {
+ return false;
+ }
+
+ String statusTopic = buildTopicName(deConfiguration.getDistributionStatusTopicName(), this.envName);
+ logger.debug("Going to handle topic {}", statusTopic);
+ status = createTopicIfNotExists(topicsList, statusTopic);
+ if (false == status) {
+ return false;
+ }
+
+ CambriaErrorResponse registerConcumerStatus = registerToTopic(statusTopic, SubscriberTypeEnum.CONSUMER);
+
+ if (registerConcumerStatus.getOperationStatus() != CambriaOperationStatus.OK) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private CambriaErrorResponse registerToTopic(String topicName, SubscriberTypeEnum subscriberType) {
+ CambriaErrorResponse registerStatus = cambriaHandler.registerToTopic(deConfiguration.getUebServers(), topicName, deConfiguration.getUebPublicKey(), deConfiguration.getUebSecretKey(), deConfiguration.getUebPublicKey(), subscriberType);
+
+ String role = CONSUMER;
+ if (subscriberType == SubscriberTypeEnum.PRODUCER) {
+ role = PRODUCER;
+ }
+ auditRegistration(topicName, registerStatus, role);
+ return registerStatus;
+ }
+
+ private void auditRegistration(String notificationTopic, CambriaErrorResponse registerProducerStatus, String role) {
+ if (componentsUtils != null) {
+ Integer httpCode = registerProducerStatus.getHttpCode();
+ String httpCodeStr = String.valueOf(httpCode);
+ this.componentsUtils.auditDistributionEngine(AuditingActionEnum.ADD_KEY_TO_TOPIC_ACL, this.envName, notificationTopic, role, deConfiguration.getUebPublicKey(), httpCodeStr);
+ }
+ }
+
+ private boolean createTopicIfNotExists(Set<String> topicsList, String topicName) {
+
+ if (topicsList.contains(topicName)) {
+ if (componentsUtils != null) {
+ this.componentsUtils.auditDistributionEngine(AuditingActionEnum.CREATE_DISTRIBUTION_TOPIC, this.envName, topicName, null, null, ALREADY_EXISTS);
+ }
+ return true;
+ }
+
+ CambriaErrorResponse createDistribTopicStatus = cambriaHandler.createTopic(deConfiguration.getUebServers(), deConfiguration.getUebPublicKey(), deConfiguration.getUebSecretKey(), topicName, deConfiguration.getCreateTopic().getPartitionCount(),
+ deConfiguration.getCreateTopic().getReplicationCount());
+
+ CambriaOperationStatus status = createDistribTopicStatus.getOperationStatus();
+ if (status == CambriaOperationStatus.TOPIC_ALREADY_EXIST) {
+ if (componentsUtils != null) {
+ this.componentsUtils.auditDistributionEngine(AuditingActionEnum.CREATE_DISTRIBUTION_TOPIC, this.envName, topicName, null, null, ALREADY_EXISTS);
+ }
+ } else if (status == CambriaOperationStatus.OK) {
+ if (componentsUtils != null) {
+ this.componentsUtils.auditDistributionEngine(AuditingActionEnum.CREATE_DISTRIBUTION_TOPIC, this.envName, topicName, null, null, CREATED);
+ }
+ } else {
+ if (componentsUtils != null) {
+ this.componentsUtils.auditDistributionEngine(AuditingActionEnum.CREATE_DISTRIBUTION_TOPIC, this.envName, topicName, null, null, FAILED);
+ }
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, INIT_DISTRIBUTION_ENGINE_FLOW, "try to create topic " + topicName);
+
+ BeEcompErrorManager.getInstance().logBeUebSystemError(INIT_DISTRIBUTION_ENGINE_FLOW, "try to create topic " + topicName);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public static String buildTopicName(String topicName, String environment) {
+ return topicName + "-" + environment.toUpperCase();
+ }
+
+ public boolean isActive() {
+ return this.status.get();
+ }
+
+ public long getCurrentRetryInterval() {
+ return currentRetryInterval;
+ }
+
+ protected void setCambriaHandler(CambriaHandler cambriaHandler) {
+ this.cambriaHandler = cambriaHandler;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEnginePollingTask.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEnginePollingTask.java
new file mode 100644
index 0000000000..fc7c473d6b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEnginePollingTask.java
@@ -0,0 +1,207 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.DistributionStatusTopicConfig;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.att.nsa.cambria.client.CambriaConsumer;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class DistributionEnginePollingTask implements Runnable {
+
+ public static final String DISTRIBUTION_STATUS_POLLING = "distributionEngineStatusPolling";
+
+ private String topicName;
+ private ComponentsUtils componentUtils;
+ private int fetchTimeoutInSec = 15;
+ private int pollingIntervalInSec;
+ private String consumerId;
+ private String consumerGroup;
+ private DistributionEngineConfiguration distributionEngineConfiguration;
+
+ private CambriaHandler cambriaHandler = new CambriaHandler();
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ private ScheduledExecutorService scheduledPollingService = Executors.newScheduledThreadPool(1, new BasicThreadFactory.Builder().namingPattern("TopicPollingThread-%d").build());
+
+ private static Logger logger = LoggerFactory.getLogger(DistributionEnginePollingTask.class.getName());
+
+ ScheduledFuture<?> scheduledFuture = null;
+ private CambriaConsumer cambriaConsumer = null;
+
+ private DistributionEngineClusterHealth distributionEngineClusterHealth = null;
+
+ public DistributionEnginePollingTask(DistributionEngineConfiguration distributionEngineConfiguration, String envName, ComponentsUtils componentUtils, DistributionEngineClusterHealth distributionEngineClusterHealth) {
+
+ this.componentUtils = componentUtils;
+ this.distributionEngineConfiguration = distributionEngineConfiguration;
+ DistributionStatusTopicConfig statusConfig = distributionEngineConfiguration.getDistributionStatusTopic();
+ this.pollingIntervalInSec = statusConfig.getPollingIntervalSec();
+ this.fetchTimeoutInSec = statusConfig.getFetchTimeSec();
+ this.consumerGroup = statusConfig.getConsumerGroup();
+ this.consumerId = statusConfig.getConsumerId();
+ this.distributionEngineClusterHealth = distributionEngineClusterHealth;
+ }
+
+ public void startTask(String topicName) {
+
+ this.topicName = topicName;
+ logger.debug("start task for polling topic {}", topicName);
+ if (fetchTimeoutInSec < 15) {
+ logger.warn("fetchTimeout value should be greater or equal to 15 sec. use default");
+ fetchTimeoutInSec = 15;
+ }
+ try {
+ cambriaConsumer = cambriaHandler.createConsumer(distributionEngineConfiguration.getUebServers(), topicName, distributionEngineConfiguration.getUebPublicKey(), distributionEngineConfiguration.getUebSecretKey(), consumerId, consumerGroup,
+ fetchTimeoutInSec * 1000);
+
+ if (scheduledPollingService != null) {
+ logger.debug("Start Distribution Engine polling task. polling interval {} seconds", pollingIntervalInSec);
+ scheduledFuture = scheduledPollingService.scheduleAtFixedRate(this, 0, pollingIntervalInSec, TimeUnit.SECONDS);
+
+ }
+ } catch (Exception e) {
+ logger.debug("unexpected error occured", e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(methodName, e.getMessage());
+ }
+ }
+
+ public void stopTask() {
+ if (scheduledFuture != null) {
+ boolean result = scheduledFuture.cancel(true);
+ logger.debug("Stop polling task. result = {}", result);
+ if (false == result) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, DISTRIBUTION_STATUS_POLLING, "try to stop the polling task");
+ BeEcompErrorManager.getInstance().logBeUebSystemError(DISTRIBUTION_STATUS_POLLING, "try to stop the polling task");
+ }
+ scheduledFuture = null;
+ }
+ if (cambriaConsumer != null) {
+ logger.debug("close consumer");
+ cambriaHandler.closeConsumer(cambriaConsumer);
+ }
+
+ }
+
+ public void destroy() {
+ this.stopTask();
+ shutdownExecutor();
+ }
+
+ @Override
+ public void run() {
+ logger.trace("run() method. polling queue {}", topicName);
+
+ try {
+ // init error
+ if (cambriaConsumer == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, DISTRIBUTION_STATUS_POLLING, "polling task was not initialized properly");
+ BeEcompErrorManager.getInstance().logBeUebSystemError(DISTRIBUTION_STATUS_POLLING, "polling task was not initialized properly");
+ stopTask();
+ return;
+ }
+
+ Either<Iterable<String>, CambriaErrorResponse> fetchResult = cambriaHandler.fetchFromTopic(cambriaConsumer);
+ // fetch error
+ if (fetchResult.isRight()) {
+ CambriaErrorResponse errorResponse = fetchResult.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, DISTRIBUTION_STATUS_POLLING, "failed to fetch messages from topic " + topicName + " error: " + fetchResult.right().value());
+ BeEcompErrorManager.getInstance().logBeUebSystemError(DISTRIBUTION_STATUS_POLLING, "failed to fetch messages from topic " + topicName + " error: " + fetchResult.right().value());
+
+ // TODO: if status== internal error (connection problem) change
+ // state to inactive
+ // in next try, if succeed - change to active
+ return;
+ }
+
+ // success
+ Iterable<String> messages = fetchResult.left().value();
+ for (String message : messages) {
+ logger.trace("received message {}", message);
+ try {
+ DistributionStatusNotification notification = gson.fromJson(message, DistributionStatusNotification.class);
+ componentUtils.auditDistributionStatusNotification(AuditingActionEnum.DISTRIBUTION_STATUS, notification.getDistributionID(), notification.getConsumerID(), topicName, notification.getArtifactURL(),
+ String.valueOf(notification.getTimestamp()), notification.getStatus().name(), notification.getErrorReason());
+
+ distributionEngineClusterHealth.setHealthCheckOkAndReportInCaseLastStateIsDown();
+
+ } catch (Exception e) {
+ logger.debug("failed to convert message to object", e);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUebSystemError, DISTRIBUTION_STATUS_POLLING, "failed to parse message " + message + " from topic " + topicName + " error: " + fetchResult.right().value());
+ BeEcompErrorManager.getInstance().logBeUebSystemError(DISTRIBUTION_STATUS_POLLING, "failed to parse message " + message + " from topic " + topicName + " error: " + fetchResult.right().value());
+ }
+
+ }
+ } catch (Exception e) {
+ logger.debug("unexpected error occured", e);
+ String methodName = new Object() {
+ }.getClass().getEnclosingMethod().getName();
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, methodName, e.getMessage());
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(methodName, e.getMessage());
+ }
+
+ }
+
+ private void shutdownExecutor() {
+ if (scheduledPollingService == null)
+ return;
+
+ scheduledPollingService.shutdown(); // Disable new tasks from being
+ // submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS)) {
+ scheduledPollingService.shutdownNow(); // Cancel currently
+ // executing tasks
+ // Wait a while for tasks to respond to being cancelled
+ if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS))
+ logger.debug("Pool did not terminate");
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ scheduledPollingService.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionNotificationSender.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionNotificationSender.java
new file mode 100644
index 0000000000..667276db2c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionNotificationSender.java
@@ -0,0 +1,127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+
+import javax.annotation.PreDestroy;
+
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.DistributionNotificationTopicConfig;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component("distributionNotificationSender")
+public class DistributionNotificationSender {
+
+ protected static final String DISTRIBUTION_NOTIFICATION_SENDING = "distributionNotificationSending";
+
+ private static Logger logger = LoggerFactory.getLogger(DistributionNotificationSender.class.getName());
+
+ // final String BASE_ARTIFACT_URL = "/asdc/v1/catalog/services/%s/%s/";
+ // final String RESOURCE_ARTIFACT_URL = BASE_ARTIFACT_URL
+ // + "resources/%s/%s/artifacts/%s";
+ // final String SERVICE_ARTIFACT_URL = BASE_ARTIFACT_URL + "artifacts/%s";
+
+ @javax.annotation.Resource
+ InterfaceLifecycleOperation interfaceLifecycleOperation;
+
+ @javax.annotation.Resource
+ protected IServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ protected ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ protected ComponentsUtils componentUtils;
+
+ ExecutorService executorService = null;
+
+ CambriaHandler cambriaHandler = new CambriaHandler();
+
+ NotificationExecutorService notificationExecutorService = new NotificationExecutorService();
+
+ public DistributionNotificationSender() {
+ super();
+
+ DistributionNotificationTopicConfig distributionNotificationTopic = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration().getDistributionNotificationTopic();
+
+ executorService = notificationExecutorService.createExcecutorService(distributionNotificationTopic);
+ }
+
+ @PreDestroy
+ public void shutdown() {
+ logger.debug("Going to close notificationExecutorService");
+ if (executorService != null) {
+
+ long maxWaitingTime = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration().getDistributionNotificationTopic().getMaxWaitingAfterSendingSeconds();
+
+ notificationExecutorService.shutdownAndAwaitTermination(executorService, maxWaitingTime + 1);
+ }
+ }
+
+ public StorageOperationStatus sendNotification(String topicName, String distributionId, DistributionEngineConfiguration deConfiguration, String envName, INotificationData notificationData, Service service, String userId, String modifierName) {
+
+ Runnable task = new PublishNotificationRunnable(envName, distributionId, service, notificationData, deConfiguration, topicName, userId, modifierName, cambriaHandler, componentUtils, ThreadLocalsHolder.getUuid());
+ try {
+ executorService.submit(task);
+ } catch (RejectedExecutionException e) {
+ logger.warn("Failed to submit task. Number of threads exceeeds", e);
+ return StorageOperationStatus.OVERLOAD;
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ /**
+ * Audit the publishing notification in case of internal server error.
+ *
+ * @param topicName
+ * @param status
+ * @param distributionId
+ * @param envName
+ */
+ private void auditDistributionNotificationInternalServerError(String topicName, StorageOperationStatus status, String distributionId, String envName) {
+
+ if (this.componentUtils != null) {
+ this.componentUtils.auditDistributionNotification(AuditingActionEnum.DISTRIBUTION_NOTIFY, "", " ", "Service", " ", " ", " ", envName, " ", topicName, distributionId, "Error: Internal Server Error. " + status, " ");
+ }
+ }
+
+ protected CambriaErrorResponse publishNotification(INotificationData data, DistributionEngineConfiguration deConfiguration, String topicName) {
+
+ CambriaErrorResponse status = cambriaHandler.sendNotification(topicName, deConfiguration.getUebPublicKey(), deConfiguration.getUebSecretKey(), deConfiguration.getUebServers(), data);
+
+ return status;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotification.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotification.java
new file mode 100644
index 0000000000..73a0336361
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotification.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public class DistributionStatusNotification {
+
+ String distributionID;
+ String consumerID;
+ long timestamp;
+ String artifactURL;
+ DistributionStatusNotificationEnum status;
+ String errorReason;
+
+ public String getDistributionID() {
+ return distributionID;
+ }
+
+ public void setDistributionID(String distributionId) {
+ this.distributionID = distributionId;
+ }
+
+ public String getConsumerID() {
+ return consumerID;
+ }
+
+ public void setConsumerID(String consumerId) {
+ this.consumerID = consumerId;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getArtifactURL() {
+ return artifactURL;
+ }
+
+ public void setArtifactURL(String artifactURL) {
+ this.artifactURL = artifactURL;
+ }
+
+ public DistributionStatusNotificationEnum getStatus() {
+ return status;
+ }
+
+ public void setStatus(DistributionStatusNotificationEnum status) {
+ this.status = status;
+ }
+
+ public String getErrorReason() {
+ return errorReason;
+ }
+
+ public void setErrorReason(String errorReason) {
+ this.errorReason = errorReason;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionStatusNotification [distributionId=" + distributionID + ", consumerId=" + consumerID + ", timestamp=" + timestamp + ", artifactURL=" + artifactURL + ", status=" + status + ", errorReason=" + errorReason + "]";
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotificationEnum.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotificationEnum.java
new file mode 100644
index 0000000000..bd77f3915a
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/DistributionStatusNotificationEnum.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public enum DistributionStatusNotificationEnum {
+
+ DOWNLOAD_OK, DOWNLOAD_ERROR, ALREADY_DOWNLOADED, DEPLOY_OK, DEPLOY_ERROR, ALREADY_DEPLOYED, NOTIFIED, NOT_NOTIFIED
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IArtifactInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IArtifactInfo.java
new file mode 100644
index 0000000000..169f4f3efa
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IArtifactInfo.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+
+public interface IArtifactInfo {
+
+ /** Artifact File name */
+ String getArtifactName();
+
+ /**
+ * Artifact Type.<br>
+ * Following are valid values : HEAT , DG_XML. <br>
+ * List of values will be extended in post-1510 releases.
+ */
+ ArtifactTypeEnum getArtifactType();
+
+ /**
+ * Relative artifact's URL. Should be used in REST GET API to download the artifact's payload.<br>
+ * The full artifact URL will be in the following format :<br>
+ * https://{serverBaseURL}/{resourcePath}<br>
+ * serverBaseURL - Hostname ( ASDC LB FQDN) + optional port <br>
+ * resourcePath - "artifactURL" <br>
+ * Ex : https://asdc.sdc.com/v1/catalog/services/srv1/2.0/resources/aaa/1.0/artifacts/aaa.yml
+ */
+ String getArtifactURL();
+
+ /**
+ * Base-64 encoded MD5 checksum of the artifact's payload.<br>
+ * Should be used for data integrity validation when an artifact's payload is downloaded.<br>
+ */
+ String getArtifactChecksum();
+
+ /**
+ * Installation timeout. Used by the orchestrator.
+ */
+ Integer getArtifactTimeout();
+
+ /**
+ * Artifact description
+ */
+ String getArtifactDescription();
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IDistributionEngine.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IDistributionEngine.java
new file mode 100644
index 0000000000..a27156616b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IDistributionEngine.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+
+import fj.data.Either;
+
+public interface IDistributionEngine {
+
+ public boolean isActive();
+
+ public StorageOperationStatus notifyService(String distributionId, Service service, INotificationData notificationData, String envName, String userId, String modifierName);
+
+ public StorageOperationStatus isEnvironmentAvailable(String envName);
+
+ /**
+ * Currently, it used for tests. For real implementation we need cancel the initialization task and the polling task.
+ *
+ * @param envName
+ */
+ public void disableEnvironment(String envName);
+
+ public Either<INotificationData, StorageOperationStatus> isReadyForDistribution(Service service, String distributionId, String envName);
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/INotificationData.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/INotificationData.java
new file mode 100644
index 0000000000..48f6c42823
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/INotificationData.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.List;
+
+public interface INotificationData {
+ /**
+ * Global Distribution Identifier: UUID generated by ASDC per each distribution activation.<br>
+ * Generated UUID is compliant with RFC 4122.<br>
+ * It is a 128-bit value formatted into blocks of hexadecimal digits separated by a hyphen ("-").<br>
+ * Ex.: AA97B177-9383-4934-8543-0F91A7A02836
+ */
+ String getDistributionID();
+
+ /** Logical Service Name. */
+ String getServiceName();
+
+ /**
+ * Service Version.<br>
+ * Two dot (".") separated digit blocks.<br>
+ * Ex. : "2.0"
+ */
+ String getServiceVersion();
+
+ /**
+ * Global UUID generated by ASDC per each service version. Generated UUID is compliant with RFC 4122.<br>
+ * It is a 128-bit value formatted into blocks of hexadecimal digits separated by a hyphen ("-").<br>
+ * Ex. : AA97B177-9383-4934-8543-0F91A7A02836
+ */
+ String getServiceUUID();
+
+ /**
+ * Service description
+ */
+ String getServiceDescription();
+
+ /**
+ * ServiceInvariant UUID
+ */
+ String getServiceInvariantUUID();
+
+ /** List of the resource instances */
+ List<JsonContainerResourceInstance> getResources();
+
+ /** List of the artifacts */
+ List<ArtifactInfoImpl> getServiceArtifacts();
+
+ void setDistributionID(String distributionId);
+
+ /** Logical Service Name. */
+ void setServiceName(String serviceName);
+
+ /**
+ * Service Version.<br>
+ * Two dot (".") separated digit blocks.<br>
+ * Ex. : "2.0"
+ */
+ void setServiceVersion(String serviceVersion);
+
+ /**
+ * Global UUID generated by ASDC per each service version. Generated UUID is compliant with RFC 4122.<br>
+ * It is a 128-bit value formatted into blocks of hexadecimal digits separated by a hyphen ("-").<br>
+ * Ex. : AA97B177-9383-4934-8543-0F91A7A02836
+ */
+ void setServiceUUID(String serviceUUID);
+
+ /**
+ * Service Description
+ */
+ void setServiceDescription(String serviceDescription);
+
+ /**
+ * ServiceInvariant UUID
+ */
+ void setServiceInvariantUUID(String serviceInvariantUuid);
+
+ /** List of the Resource Instances */
+ void setResources(List<JsonContainerResourceInstance> resource);
+
+ /** List of the Resource Instances */
+ void setServiceArtifacts(List<ArtifactInfoImpl> artifacts);
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IResourceArtifactInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IResourceArtifactInfo.java
new file mode 100644
index 0000000000..9a77b9f94f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IResourceArtifactInfo.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public interface IResourceArtifactInfo extends IArtifactInfo {
+
+ /** resource name */
+ String getResourceName();
+
+ /** resource version */
+ String getResourceVersion();
+
+ /**
+ * Global UUID of the resource that specific artifact belongs to.<br>
+ * It is generated by ASDC per each resource version.<br>
+ * Generated UUID is compliant with RFC 4122. It is a 128-bit value formatted into blocks of hexadecimal digits separated by a hyphen ("-"). <br>
+ * Ex.: AA97B177-9383-4934-8543-0F91A7A02836
+ */
+ String getResourceUUID();
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IServiceArtifactInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IServiceArtifactInfo.java
new file mode 100644
index 0000000000..e102f742ac
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/IServiceArtifactInfo.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public interface IServiceArtifactInfo extends IArtifactInfo {
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/JsonContainerResourceInstance.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/JsonContainerResourceInstance.java
new file mode 100644
index 0000000000..25a6f46c90
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/JsonContainerResourceInstance.java
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.model.ComponentInstance;
+
+public class JsonContainerResourceInstance {
+ private String resourceInstanceName, resourceName, resourceVersion, resoucreType, resourceUUID, resourceInvariantUUID;
+ private List<ArtifactInfoImpl> artifacts;
+
+ public JsonContainerResourceInstance(ComponentInstance resourceInstance, String resourceType, List<ArtifactInfoImpl> artifacts) {
+ super();
+ this.resourceInstanceName = resourceInstance.getName();
+ this.resourceName = resourceInstance.getComponentName();
+ this.resourceVersion = resourceInstance.getComponentVersion();
+ this.resoucreType = resourceType;
+ this.resourceUUID = resourceInstance.getComponentUid();
+ this.artifacts = artifacts;
+ }
+
+ public JsonContainerResourceInstance(ComponentInstance resourceInstance, String resourceInvariantUUID, String resourceType, List<ArtifactInfoImpl> artifacts) {
+ super();
+ this.resourceInstanceName = resourceInstance.getName();
+ this.resourceName = resourceInstance.getComponentName();
+ this.resourceVersion = resourceInstance.getComponentVersion();
+ this.resoucreType = resourceType;
+ this.resourceUUID = resourceInstance.getComponentUid();
+ this.resourceInvariantUUID = resourceInvariantUUID;
+ this.artifacts = artifacts;
+ }
+
+ public String getResourceInstanceName() {
+ return resourceInstanceName;
+ }
+
+ public void setResourceInstanceName(String resourceInstanceName) {
+ this.resourceInstanceName = resourceInstanceName;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceVersion() {
+ return resourceVersion;
+ }
+
+ public void setResourceVersion(String resourceVersion) {
+ this.resourceVersion = resourceVersion;
+ }
+
+ public String getResoucreType() {
+ return resoucreType;
+ }
+
+ public void setResoucreType(String resoucreType) {
+ this.resoucreType = resoucreType;
+ }
+
+ public String getResourceUUID() {
+ return resourceUUID;
+ }
+
+ public void setResourceUUID(String resourceUUID) {
+ this.resourceUUID = resourceUUID;
+ }
+
+ public List<ArtifactInfoImpl> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactInfoImpl> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ public String getResourceInvariantUUID() {
+ return resourceInvariantUUID;
+ }
+
+ public void setResourceInvariantUUID(String resourceInvariantUUID) {
+ this.resourceInvariantUUID = resourceInvariantUUID;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationDataImpl.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationDataImpl.java
new file mode 100644
index 0000000000..8a2ef8e63b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationDataImpl.java
@@ -0,0 +1,118 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.List;
+
+public class NotificationDataImpl implements INotificationData {
+
+ private String distributionID;
+ private String serviceName;
+ private String serviceVersion;
+ private String serviceUUID;
+ private String serviceDescription;
+ private String serviceInvariantUUID;
+ private List<JsonContainerResourceInstance> resources;
+ private List<ArtifactInfoImpl> serviceArtifacts;
+
+ @Override
+ public String getDistributionID() {
+ return distributionID;
+ }
+
+ @Override
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ @Override
+ public String getServiceVersion() {
+ return serviceVersion;
+ }
+
+ @Override
+ public String getServiceUUID() {
+ return serviceUUID;
+ }
+
+ public void setDistributionID(String distributionID) {
+ this.distributionID = distributionID;
+ }
+
+ public void setServiceName(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public void setServiceVersion(String serviceVersion) {
+ this.serviceVersion = serviceVersion;
+ }
+
+ public void setServiceUUID(String serviceUUID) {
+ this.serviceUUID = serviceUUID;
+ }
+
+ public String getServiceDescription() {
+ return serviceDescription;
+ }
+
+ public void setServiceDescription(String serviceDescription) {
+ this.serviceDescription = serviceDescription;
+ }
+
+ @Override
+ public String toString() {
+ return "NotificationDataImpl [distributionID=" + distributionID + ", serviceName=" + serviceName + ", serviceVersion=" + serviceVersion + ", serviceUUID=" + serviceUUID + ", serviceInvariantUUID=" + serviceInvariantUUID + "]";
+ }
+
+ @Override
+ public List<JsonContainerResourceInstance> getResources() {
+ return resources;
+ }
+
+ @Override
+ public void setResources(List<JsonContainerResourceInstance> resources) {
+ this.resources = resources;
+
+ }
+
+ @Override
+ public List<ArtifactInfoImpl> getServiceArtifacts() {
+ // TODO Auto-generated method stub
+ return serviceArtifacts;
+ }
+
+ @Override
+ public void setServiceArtifacts(List<ArtifactInfoImpl> serviceArtifacts) {
+ this.serviceArtifacts = serviceArtifacts;
+
+ }
+
+ @Override
+ public String getServiceInvariantUUID() {
+ return serviceInvariantUUID;
+ }
+
+ @Override
+ public void setServiceInvariantUUID(String serviceInvariantUUID) {
+ this.serviceInvariantUUID = serviceInvariantUUID;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationExecutorService.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationExecutorService.java
new file mode 100644
index 0000000000..74fbb2c660
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/NotificationExecutorService.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.DistributionNotificationTopicConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+public class NotificationExecutorService {
+
+ private static Logger logger = LoggerFactory.getLogger(NotificationExecutorService.class.getName());
+
+ public ExecutorService createExcecutorService(DistributionNotificationTopicConfig distributionNotificationTopic) {
+
+ Integer minThreadPoolSize = distributionNotificationTopic.getMinThreadPoolSize();
+ if (minThreadPoolSize == null) {
+ minThreadPoolSize = 0;
+ }
+
+ Integer maxThreadPoolSize = distributionNotificationTopic.getMaxThreadPoolSize();
+ if (maxThreadPoolSize == null) {
+ maxThreadPoolSize = 10;
+ }
+
+ ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder();
+ threadFactoryBuilder.setNameFormat("distribution-notification-thread-%d");
+ ThreadFactory threadFactory = threadFactoryBuilder.build();
+
+ ExecutorService executorService = new ThreadPoolExecutor(minThreadPoolSize, maxThreadPoolSize, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
+
+ return executorService;
+ }
+
+ public void shutdownAndAwaitTermination(ExecutorService pool, long maxTimeToWait) {
+
+ logger.debug("shutdown NotificationExecutorService");
+ pool.shutdown(); // Disable new tasks from being submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!pool.awaitTermination(maxTimeToWait, TimeUnit.SECONDS)) {
+ pool.shutdownNow(); // Cancel currently executing tasks
+ // Wait a while for tasks to respond to being cancelled
+ if (!pool.awaitTermination(maxTimeToWait, TimeUnit.SECONDS)) {
+ logger.debug("Failed to close executor service");
+ }
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ pool.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/PublishNotificationRunnable.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/PublishNotificationRunnable.java
new file mode 100644
index 0000000000..362f3948ed
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/PublishNotificationRunnable.java
@@ -0,0 +1,156 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PublishNotificationRunnable implements Runnable {
+
+ private String envName;
+ private String distributionId;
+ private Service service;
+ private INotificationData data;
+ private DistributionEngineConfiguration deConfiguration;
+ private String topicName;
+ private CambriaHandler cambriaHandler;
+ private ComponentsUtils componentUtils;
+ private String userId;
+ private String modifierName;
+ private String requestId;
+
+ private static Logger logger = LoggerFactory.getLogger(PublishNotificationRunnable.class.getName());
+
+ public PublishNotificationRunnable(String envName, String distributionId, Service service, INotificationData data, DistributionEngineConfiguration deConfiguration, String topicName, String userId, String modifierName,
+ CambriaHandler cambriaHandler, ComponentsUtils componentUtils, String requestId) {
+ super();
+ this.envName = envName;
+ this.distributionId = distributionId;
+ this.service = service;
+ this.data = data;
+ this.deConfiguration = deConfiguration;
+ this.topicName = topicName;
+ this.cambriaHandler = cambriaHandler;
+ this.componentUtils = componentUtils;
+ this.userId = userId;
+ this.modifierName = modifierName;
+ this.requestId = requestId;
+ }
+
+ public INotificationData getData() {
+ return data;
+ }
+
+ public void setData(INotificationData data) {
+ this.data = data;
+ }
+
+ public DistributionEngineConfiguration getDeConfiguration() {
+ return deConfiguration;
+ }
+
+ public void setDeConfiguration(DistributionEngineConfiguration deConfiguration) {
+ this.deConfiguration = deConfiguration;
+ }
+
+ public String getTopicName() {
+ return topicName;
+ }
+
+ public void setTopicName(String topicName) {
+ this.topicName = topicName;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getModifierName() {
+ return modifierName;
+ }
+
+ public void setModifierName(String modifierName) {
+ this.modifierName = modifierName;
+ }
+
+ @Override
+ public void run() {
+
+ long startTime = System.currentTimeMillis();
+ ThreadLocalsHolder.setUuid(this.requestId);
+
+ CambriaErrorResponse status = cambriaHandler.sendNotificationAndClose(topicName, deConfiguration.getUebPublicKey(), deConfiguration.getUebSecretKey(), deConfiguration.getUebServers(), data,
+ deConfiguration.getDistributionNotificationTopic().getMaxWaitingAfterSendingSeconds().longValue());
+
+ logger.info("After publishing service {} of version {}. Status is {}", service.getName(), service.getVersion(), status.getHttpCode());
+ auditDistributionNotification(topicName, status, service, distributionId, envName, userId, modifierName);
+
+ long endTime = System.currentTimeMillis();
+ logger.debug("After building and publishing artifacts object. Total took {} milliseconds.", (endTime - startTime));
+
+ }
+
+ private void auditDistributionNotification(String topicName, CambriaErrorResponse status, Service service, String distributionId, String envName, String userId, String modifierName) {
+ if (this.componentUtils != null) {
+ Integer httpCode = status.getHttpCode();
+ String httpCodeStr = String.valueOf(httpCode);
+
+ String desc = getDescriptionFromErrorResponse(status);
+
+ this.componentUtils.auditDistributionNotification(AuditingActionEnum.DISTRIBUTION_NOTIFY, service.getUUID(), service.getName(), "Service", service.getVersion(), userId, modifierName, envName, service.getLifecycleState().name(), topicName,
+ distributionId, desc, httpCodeStr);
+ }
+ }
+
+ private String getDescriptionFromErrorResponse(CambriaErrorResponse status) {
+
+ CambriaOperationStatus operationStatus = status.getOperationStatus();
+
+ switch (operationStatus) {
+ case OK:
+ return "OK";
+ case AUTHENTICATION_ERROR:
+ return "Error: Authentication problem towards U-EB server";
+ case INTERNAL_SERVER_ERROR:
+ return "Error: Internal U-EB server error";
+ case UNKNOWN_HOST_ERROR:
+ return "Error: Cannot reach U-EB server host";
+ case CONNNECTION_ERROR:
+ return "Error: Cannot connect to U-EB server";
+ case OBJECT_NOT_FOUND:
+ return "Error: object not found in U-EB server";
+ default:
+ return "Error: Internal Cambria server problem";
+
+ }
+
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ResourceArtifactInfoImpl.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ResourceArtifactInfoImpl.java
new file mode 100644
index 0000000000..31f3cb6fda
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ResourceArtifactInfoImpl.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public class ResourceArtifactInfoImpl extends ArtifactInfoImpl implements IResourceArtifactInfo {
+
+ private String resourceName;
+ private String resourceVersion;
+ private String resourceUUID;
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceVersion() {
+ return resourceVersion;
+ }
+
+ public void setResourceVersion(String resourceVersion) {
+ this.resourceVersion = resourceVersion;
+ }
+
+ public String getResourceUUID() {
+ return resourceUUID;
+ }
+
+ public void setResourceUUID(String resourceUUID) {
+ this.resourceUUID = resourceUUID;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceArtifactInfoImpl [resourceName=" + resourceName + ", resourceVersion=" + resourceVersion + ", resourceUUID=" + resourceUUID + super.toString() + "]";
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceArtifactInfoImpl.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceArtifactInfoImpl.java
new file mode 100644
index 0000000000..50d1700f37
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceArtifactInfoImpl.java
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public class ServiceArtifactInfoImpl extends ArtifactInfoImpl implements IServiceArtifactInfo {
+
+ @Override
+ public String toString() {
+ return "ServiceArtifactInfoImpl [" + super.toString() + "]";
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceDistributionArtifactsBuilder.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceDistributionArtifactsBuilder.java
new file mode 100644
index 0000000000..da148ee80a
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/ServiceDistributionArtifactsBuilder.java
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("serviceDistributionArtifactsBuilder")
+public class ServiceDistributionArtifactsBuilder {
+
+ private int defaultArtifactInstallTimeout = 60;
+
+ private static Logger logger = LoggerFactory.getLogger(ServiceDistributionArtifactsBuilder.class.getName());
+
+ final static String BASE_ARTIFACT_URL = "/asdc/v1/catalog/services/%s/%s/";
+ final static String RESOURCE_ARTIFACT_URL = BASE_ARTIFACT_URL + "resources/%s/%s/artifacts/%s";
+ final static String SERVICE_ARTIFACT_URL = BASE_ARTIFACT_URL + "artifacts/%s";
+
+ final static String RESOURCE_INSTANCE_ARTIFACT_URL = BASE_ARTIFACT_URL + "resourceInstances/%s/artifacts/%s";
+
+ @javax.annotation.Resource
+ ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ InterfaceLifecycleOperation interfaceLifecycleOperation;
+
+ @javax.annotation.Resource
+ IArtifactOperation artifactOperation;
+
+ /*
+ * @javax.annotation.Resource private InformationDeployedArtifactsBusinessLogic informationDeployedArtifactsBusinessLogic;
+ */
+
+ @PostConstruct
+ private void init() {
+ defaultArtifactInstallTimeout = ConfigurationManager.getConfigurationManager().getConfiguration().getDefaultHeatArtifactTimeoutMinutes();
+ }
+
+ public ServiceOperation getServiceOperation() {
+ return serviceOperation;
+ }
+
+ public void setServiceOperation(ServiceOperation serviceOperation) {
+ this.serviceOperation = serviceOperation;
+ }
+
+ public ResourceOperation getResourceOperation() {
+ return resourceOperation;
+ }
+
+ public void setResourceOperation(ResourceOperation resourceOperation) {
+ this.resourceOperation = resourceOperation;
+ }
+
+ public InterfaceLifecycleOperation getInterfaceLifecycleOperation() {
+ return interfaceLifecycleOperation;
+ }
+
+ public void setInterfaceLifecycleOperation(InterfaceLifecycleOperation interfaceLifecycleOperation) {
+ this.interfaceLifecycleOperation = interfaceLifecycleOperation;
+ }
+
+ public INotificationData buildResourceInstanceForDistribution(Service service, String distributionId) {
+ INotificationData notificationData = new NotificationDataImpl();
+
+ notificationData.setResources(convertRIToJsonContanier(service));
+ notificationData.setServiceName(service.getName());
+ notificationData.setServiceVersion(service.getVersion());
+ notificationData.setDistributionID(distributionId);
+ notificationData.setServiceUUID(service.getUUID());
+ notificationData.setServiceDescription(service.getDescription());
+ notificationData.setServiceInvariantUUID(service.getInvariantUUID());
+
+ logger.debug("Before returning notification data object {}", notificationData);
+
+ return notificationData;
+
+ }
+
+ public INotificationData buildServiceForDistribution(INotificationData notificationData, Service service) {
+
+ notificationData.setServiceArtifacts(convertServiceArtifactsToArtifactInfo(service));
+
+ logger.debug("Before returning notification data object {}", notificationData);
+
+ return notificationData;
+
+ }
+
+ private List<ArtifactInfoImpl> convertServiceArtifactsToArtifactInfo(Service service) {
+
+ Map<String, ArtifactDefinition> serviceArtifactsMap = service.getDeploymentArtifacts();
+ List<ArtifactDefinition> ret = new ArrayList<ArtifactDefinition>();
+
+ for (ArtifactDefinition artifactDef : serviceArtifactsMap.values()) {
+ if (artifactDef.checkEsIdExist()) {
+ ret.add(artifactDef);
+ }
+ }
+ List<ArtifactInfoImpl> artifacts = ArtifactInfoImpl.convertServiceArtifactToArtifactInfoImpl(service, ret);
+ return artifacts;
+ }
+
+ private List<JsonContainerResourceInstance> convertRIToJsonContanier(Service service) {
+ List<JsonContainerResourceInstance> ret = new ArrayList<JsonContainerResourceInstance>();
+ if (service.getComponentInstances() != null) {
+ for (ComponentInstance resourceInstance : service.getComponentInstances()) {
+ String resoucreType = "VF";
+ List<ArtifactDefinition> artifactsDefList = getArtifactsWithPayload(resourceInstance);
+ List<ArtifactInfoImpl> artifacts = ArtifactInfoImpl.convertToArtifactInfoImpl(service, resourceInstance, artifactsDefList);
+ Either<String, StorageOperationStatus> responseResult = resourceOperation.getInvariantUUID(NodeTypeEnum.Resource, resourceInstance.getComponentUid(), false);
+ String resourceInvariantUUID = null;
+ if (responseResult.isRight()) {
+ logger.debug("Resource {} Invariant UUID retrieving failed", resourceInstance.getComponentUid());
+ } else {
+ resourceInvariantUUID = responseResult.left().value();
+ }
+ JsonContainerResourceInstance jsonContainer = new JsonContainerResourceInstance(resourceInstance, resourceInvariantUUID, resoucreType, artifacts);
+ ret.add(jsonContainer);
+ }
+
+ }
+ return ret;
+ }
+
+ private List<ArtifactDefinition> getArtifactsWithPayload(ComponentInstance resourceInstance) {
+ List<ArtifactDefinition> ret = new ArrayList<ArtifactDefinition>();
+
+ // List<ArtifactDefinition> informationDeployedArtifacts =
+ // informationDeployedArtifactsBusinessLogic.getInformationalDeployedArtifactsForResourceInstance(resourceInstance);
+ List<ArtifactDefinition> deployableArtifacts = new ArrayList<ArtifactDefinition>();
+ // deployableArtifacts.addAll(informationDeployedArtifacts);
+ if (resourceInstance.getDeploymentArtifacts() != null) {
+ deployableArtifacts.addAll(resourceInstance.getDeploymentArtifacts().values());
+ }
+
+ for (ArtifactDefinition artifactDef : deployableArtifacts) {
+ if (artifactDef.checkEsIdExist()) {
+ ret.add(artifactDef);
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * build the url for resource intance artifact
+ *
+ * @param service
+ * @param resourceData
+ * @param artifactName
+ * @return
+ */
+ public static String buildResourceInstanceArtifactUrl(Service service, ComponentInstance resourceInstance, String artifactName) {
+
+ String url = String.format(RESOURCE_INSTANCE_ARTIFACT_URL, service.getSystemName(), service.getVersion(), resourceInstance.getNormalizedName(), artifactName);
+
+ logger.debug("After building artifact url {}", url);
+
+ return url;
+ }
+
+ /**
+ * build the url for resource intance artifact
+ *
+ * @param service
+ * @param resourceData
+ * @param artifactName
+ * @return
+ */
+ public static String buildServiceArtifactUrl(Service service, String artifactName) {
+
+ String url = String.format(SERVICE_ARTIFACT_URL, service.getSystemName(), service.getVersion(), artifactName);
+
+ logger.debug("After building artifact url {}", url);
+
+ return url;
+
+ }
+
+ /**
+ * Retrieve all deployment artifacts of all resources under a given service
+ *
+ * @param resourceArtifactsResult
+ * @param service
+ * @param deConfiguration
+ * @return
+ */
+ public Either<Boolean, StorageOperationStatus> isServiceContainsDeploymentArtifacts(Service service) {
+
+ Either<Boolean, StorageOperationStatus> result = Either.left(false);
+ Map<String, ArtifactDefinition> serviseArtifactsMap = service.getDeploymentArtifacts();
+ if (serviseArtifactsMap != null && !serviseArtifactsMap.isEmpty()) {
+ result = Either.left(true);
+ return result;
+ }
+
+ List<ComponentInstance> resourceInstances = service.getComponentInstances();
+
+ if (resourceInstances != null) {
+ for (ComponentInstance resourceInstance : resourceInstances) {
+
+ Map<String, ArtifactDefinition> deploymentArtifactsMapper = resourceInstance.getDeploymentArtifacts();
+ // List<ArtifactDefinition> informationDeployedArtifacts =
+ // informationDeployedArtifactsBusinessLogic.getInformationalDeployedArtifactsForResourceInstance(resourceInstance);
+
+ boolean isDeployableArtifactFound = isContainsPayload(deploymentArtifactsMapper.values());// ||
+ // isContainsPayload(informationDeployedArtifacts);
+ if (isDeployableArtifactFound) {
+ result = Either.left(true);
+ break;
+ }
+
+ }
+
+ }
+
+ return result;
+ }
+
+ private boolean isContainsPayload(Collection<ArtifactDefinition> collection) {
+ boolean payLoadFound = collection != null && collection.stream().anyMatch(p -> p.checkEsIdExist());
+ return payLoadFound;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/SubscriberTypeEnum.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/SubscriberTypeEnum.java
new file mode 100644
index 0000000000..f8c0e3f593
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/SubscriberTypeEnum.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+public enum SubscriberTypeEnum {
+
+ CONSUMER, PRODUCER;
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/TestQueue.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/TestQueue.java
new file mode 100644
index 0000000000..c9e3c4e34d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/TestQueue.java
@@ -0,0 +1,188 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+public class TestQueue {
+
+ public static void main(String[] args) {
+ ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder();
+ threadFactoryBuilder.setNameFormat("distribution-notification-thread");
+ ThreadFactory threadFactory = threadFactoryBuilder.build();
+ // TODO: add the package of google to the pom
+
+ ExecutorService executorService = new ThreadPoolExecutor(0, 10, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);
+ // ExecutorService executorService = new ThreadPoolExecutor(0, 2, 60L,
+ // TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(20));
+
+ // 2 threads are always up and they handle the tasks. in case core size
+ // is 0, only one is handles the tasks.
+ // ExecutorService executorService = new ThreadPoolExecutor(0, 2, 60L,
+ // TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(20));
+
+ // TODO : check what happen when the number of threads are full. Throw
+ // RejectedExecutionException
+ // TODO : check what happen whether the pool is full and the size of
+ // pool
+
+ ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(threadFactory);
+ Runnable task = new Runnable() {
+
+ @Override
+ public void run() {
+ // TODO Auto-generated method stub
+ try {
+ System.out.println("iN SLEEP" + Thread.currentThread());
+ Thread.sleep(10 * 1000);
+ System.out.println("OUT SLEEP");
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ };
+
+ for (int i = 0; i < 4; i++) {
+ try {
+ executorService.submit(task);
+ } catch (RejectedExecutionException e) {
+ e.printStackTrace();
+ }
+ }
+
+ newCachedThreadPool.submit(task);
+ System.out.println("After submitting the task");
+
+ MyWorker[] watchThreads = new MyWorker[1];
+ BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
+ for (int i = 0; i < watchThreads.length; i++) {
+ MyWorker myWorker = new MyWorker(queue);
+ myWorker.start();
+ }
+
+ for (int i = 0; i < 1; i++) {
+ try {
+ queue.put("message " + i);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ public static class MyTimerTask extends TimerTask {
+
+ AtomicBoolean state;
+ Thread thread;
+
+ public MyTimerTask(AtomicBoolean state, Thread thread) {
+ super();
+ this.state = state;
+ this.thread = thread;
+
+ System.out.println("After create timer");
+ }
+
+ @Override
+ public void run() {
+ System.out.println("In running of Timer task");
+ if (state.get() == false) {
+ System.out.println("In running of Timer task. Going to interrupt thread");
+ // thread.interrupt();
+ } else {
+ System.out.println("In running of Timer task. Finished.");
+ }
+ }
+
+ }
+
+ public static class MyWorker extends Thread {
+
+ boolean active = true;
+ private final BlockingQueue<String> queue;
+
+ public MyWorker(BlockingQueue<String> queue) {
+ this.queue = queue;
+ }
+
+ Timer timer = new Timer();
+
+ public void run() {
+ try {
+ while (active) {
+ String s = queue.take();
+ System.out.println("Thread " + Thread.currentThread() + " fecthed a message " + s);
+
+ AtomicBoolean atomicBoolean = new AtomicBoolean(false);
+ MyTimerTask myTimerTask = new MyTimerTask(atomicBoolean, this);
+ timer.schedule(myTimerTask, 10 * 1000);
+ doWork(s);
+ atomicBoolean.set(true);
+
+ }
+ } catch (InterruptedException ie) {
+
+ System.out.println("Interrupted our thread");
+ ie.printStackTrace();
+ }
+ }
+
+ private void doWork(String s) {
+ // TODO Auto-generated method stub
+
+ CambriaHandler cambriaHandler = new CambriaHandler();
+ INotificationData data = new NotificationDataImpl();
+ List<String> servers = new ArrayList<>();
+ servers.add("aaaaaaa");
+ cambriaHandler.sendNotification("topicName", "uebPublicKey", "uebSecretKey", servers, data);
+
+ System.out.println("IN WORK " + s);
+ try {
+ Thread.sleep(1 * 1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+
+ for (int i = 0; i < 10; i++) {
+ System.out.println("*************************************************");
+ }
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/UebHealthCheckCall.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/UebHealthCheckCall.java
new file mode 100644
index 0000000000..c522ca91b5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/UebHealthCheckCall.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.concurrent.Callable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class UebHealthCheckCall implements Callable<Boolean> {
+
+ CambriaHandler cambriaHandler = new CambriaHandler();
+
+ String server;
+ String publicApiKey;
+
+ private static Logger healthLogger = LoggerFactory.getLogger(DistributionEngineClusterHealth.UEB_HEALTH_LOG_CONTEXT);
+
+ private static Logger logger = LoggerFactory.getLogger(UebHealthCheckCall.class.getName());
+
+ public UebHealthCheckCall(String server, String publicApiKey) {
+ super();
+ this.server = server;
+ this.publicApiKey = publicApiKey;
+ }
+
+ @Override
+ public Boolean call() {
+
+ healthLogger.trace("Going to run health check towards ueb server {}", server);
+
+ boolean result = false;
+ CambriaErrorResponse cambriaErrorResponse = cambriaHandler.getApiKey(server, publicApiKey);
+
+ logger.debug("After running Health check towards ueb server {}. Result is {}", server, cambriaErrorResponse);
+
+ if (cambriaErrorResponse.httpCode < CambriaErrorResponse.HTTP_INTERNAL_SERVER_ERROR) {
+ logger.debug("After running Health check towards ueb server {}. Error code is {}. Set result to true", server, cambriaErrorResponse.httpCode);
+ result = true;
+ }
+
+ healthLogger.trace("Result after running health check towards ueb server {} is {}. Returned result is {} ", server, cambriaErrorResponse, result);
+
+ return result;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public CambriaHandler getCambriaHandler() {
+ return cambriaHandler;
+ }
+
+ public void setCambriaHandler(CambriaHandler cambriaHandler) {
+ this.cambriaHandler = cambriaHandler;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/VfModuleArtifactPayload.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/VfModuleArtifactPayload.java
new file mode 100644
index 0000000000..89cb6d91ee
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/distribution/engine/VfModuleArtifactPayload.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.common.api.Constants;
+
+public class VfModuleArtifactPayload {
+ public VfModuleArtifactPayload(GroupDefinition group) {
+ vfModuleModelName = group.getName();
+ vfModuleModelInvariantUUID = group.getInvariantUUID();
+ vfModuleModelVersion = group.getVersion();
+ vfModuleModelUUID = group.getGroupUUID();
+ vfModuleModelDescription = group.getDescription();
+
+ artifacts = group.getArtifactsUuid();
+ // Base Value is set from properties
+ setBaseValue(group);
+
+ }
+
+ private void setBaseValue(GroupDefinition group) {
+ if (group.getProperties() != null) {
+ Optional<GroupProperty> findBaseProperty = group.getProperties().stream().filter(p -> p.getName().equals(Constants.IS_BASE)).findAny();
+ if (findBaseProperty.isPresent()) {
+ isBase = Boolean.valueOf(findBaseProperty.get().getValue());
+ }
+
+ }
+ }
+
+ private String vfModuleModelName, vfModuleModelInvariantUUID, vfModuleModelVersion, vfModuleModelUUID, vfModuleModelDescription;
+ private Boolean isBase;
+ private List<String> artifacts;
+
+ public List<String> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<String> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ public static int compareByGroupName(VfModuleArtifactPayload art1, VfModuleArtifactPayload art2) {
+ Integer thisCounter = Integer.parseInt(art1.vfModuleModelName.split(Constants.MODULE_NAME_DELIMITER)[1]);
+ Integer otherCounter = Integer.parseInt(art2.vfModuleModelName.split(Constants.MODULE_NAME_DELIMITER)[1]);
+ return thisCounter.compareTo(otherCounter);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AdditionalInformationBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AdditionalInformationBusinessLogic.java
new file mode 100644
index 0000000000..e01d4c238c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AdditionalInformationBusinessLogic.java
@@ -0,0 +1,573 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.AdditionalInformationEnum;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.AdditionalInfoParameterInfo;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.model.tosca.converters.StringConvertor;
+import org.openecomp.sdc.be.model.tosca.validators.StringValidator;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.WebApplicationContext;
+
+import fj.data.Either;
+
+@Component("additionalInformationBusinessLogic")
+public class AdditionalInformationBusinessLogic extends BaseBusinessLogic {
+
+ private static final String CREATE_ADDITIONAL_INFORMATION = "CreateAdditionalInformation";
+
+ private static final String UPDATE_ADDITIONAL_INFORMATION = "UpdateAdditionalInformation";
+
+ private static final String DELETE_ADDITIONAL_INFORMATION = "DeleteAdditionalInformation";
+
+ private static final String GET_ADDITIONAL_INFORMATION = "GetAdditionalInformation";
+
+ private static Logger log = LoggerFactory.getLogger(AdditionalInformationBusinessLogic.class.getName());
+
+ @javax.annotation.Resource
+ private IAdditionalInformationOperation additionalInformationOperation = null;
+
+ @javax.annotation.Resource
+ private IResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ private IServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ private IGraphLockOperation graphLockOperation;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+
+ return webApplicationContext.getBean(class1);
+ }
+
+ /**
+ * Create new additional information on resource/service on graph
+ *
+ * @param resourceId
+ * @param propertyName
+ * @param newPropertyDefinition
+ * @param userId
+ * @return Either<PropertyDefinition, ActionStatus>
+ */
+ public Either<AdditionalInfoParameterInfo, ResponseFormat> createAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String additionalInformationUid, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Additional Information", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
+
+ ResponseFormat responseFormat = verifyCanWorkOnComponent(nodeType, resourceId, userId);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ // lock component
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, nodeType);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedLockObjectError, CREATE_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_ADDITIONAL_INFORMATION, nodeType.getName(), resourceId);
+ log.info("Failed to lock component {} error - {}", resourceId, lockResult);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+ try {
+ responseFormat = validateMaxSizeNotReached(nodeType, resourceId, additionalInfoParameterInfo);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ // validate label
+ responseFormat = validateAndConvertKey(additionalInfoParameterInfo, CREATE_ADDITIONAL_INFORMATION);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ // validate value
+ responseFormat = validateAndConvertValue(additionalInfoParameterInfo, CREATE_ADDITIONAL_INFORMATION);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> addResult = additionalInformationOperation.createAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getKey(),
+ additionalInfoParameterInfo.getValue(), true);
+
+ if (addResult.isRight()) {
+ StorageOperationStatus status = addResult.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, CREATE_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeSystemError(CREATE_ADDITIONAL_INFORMATION);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.Label));
+ return result;
+
+ } else {
+ AdditionalInformationDefinition informationDefinition = addResult.left().value();
+
+ AdditionalInfoParameterInfo createdAI = findAdditionInformationKey(informationDefinition.getParameters(), additionalInfoParameterInfo.getKey());
+ result = Either.left(createdAI);
+ return result;
+ }
+
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, nodeType);
+ }
+
+ }
+
+ /**
+ * Validate the value format. Format the value.
+ *
+ * @param additionalInfoParameterInfo
+ * @return null in case of success. Otherwise response format.
+ */
+ private ResponseFormat validateAndConvertValue(AdditionalInfoParameterInfo additionalInfoParameterInfo, String context) {
+ ResponseFormat result = null;
+
+ String value = additionalInfoParameterInfo.getValue();
+ log.debug("Going to validate additional information value {}", value);
+
+ Either<String, ResponseFormat> valueValidRes = validateValue(value);
+ if (valueValidRes.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidValueError, context, additionalInfoParameterInfo.getValue(), "additional information value", "string");
+ BeEcompErrorManager.getInstance().logBeInvalidValueError(context, additionalInfoParameterInfo.getValue(), "additional information value", "string");
+ result = valueValidRes.right().value();
+ } else {
+ String newValue = valueValidRes.left().value();
+ if (log.isTraceEnabled()) {
+ if (value != null && false == value.equals(newValue)) {
+ log.trace("The additional information value was normalized from {} to {}", value, newValue);
+ }
+ }
+ additionalInfoParameterInfo.setValue(newValue);
+ }
+ return result;
+ }
+
+ /**
+ * @param additionalInfoParameterInfo
+ * @return
+ */
+ private ResponseFormat validateAndConvertKey(AdditionalInfoParameterInfo additionalInfoParameterInfo, String context) {
+
+ String key = additionalInfoParameterInfo.getKey();
+ log.debug("Going to validate additional information key {}", key);
+
+ ResponseFormat result = null;
+ ResponseFormat responseFormat;
+ Either<String, ResponseFormat> validateKeyRes = validateAndNormalizeKey(key);
+ if (validateKeyRes.isRight()) {
+ responseFormat = validateKeyRes.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidValueError, context, additionalInfoParameterInfo.getKey(), "additional information label", "string");
+ BeEcompErrorManager.getInstance().logBeInvalidValueError(context, additionalInfoParameterInfo.getKey(), "additional information label", "string");
+ result = responseFormat;
+
+ } else {
+ String convertedKey = validateKeyRes.left().value();
+
+ if (log.isTraceEnabled()) {
+ if (key != null && false == key.equals(convertedKey)) {
+ log.trace("The additional information key {} was normalized to {}", key, convertedKey);
+ }
+ }
+ additionalInfoParameterInfo.setKey(convertedKey);
+ }
+ return result;
+ }
+
+ /**
+ * verify that the maximal number of additional information properties has not been reached.
+ *
+ * @param nodeType
+ * @param componentId
+ * @param additionalInfoParameterInfo
+ * @return response format in case the maximal number has been reached.
+ */
+ private ResponseFormat validateMaxSizeNotReached(NodeTypeEnum nodeType, String componentId, AdditionalInfoParameterInfo additionalInfoParameterInfo) {
+
+ ResponseFormat result;
+ Integer additionalInformationMaxNumberOfKeys = ConfigurationManager.getConfigurationManager().getConfiguration().getAdditionalInformationMaxNumberOfKeys();
+
+ Either<Integer, StorageOperationStatus> checkRes = additionalInformationOperation.getNumberOfAdditionalInformationParameters(nodeType, componentId, true);
+ if (checkRes.isRight()) {
+ StorageOperationStatus status = checkRes.right().value();
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None);
+ return result;
+ }
+ Integer currentNumberOfProperties = checkRes.left().value();
+ if (currentNumberOfProperties >= additionalInformationMaxNumberOfKeys) {
+ log.info("The current number of additional information properties is {}. The maximum allowed additional information properties is {}", currentNumberOfProperties, currentNumberOfProperties);
+ result = componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None);
+ return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * validate additional information value
+ *
+ * @param value
+ * @return
+ */
+ private Either<String, ResponseFormat> validateValue(String value) {
+
+ boolean isNonEmptyString = ValidationUtils.validateStringNotEmpty(value);
+ if (false == isNonEmptyString) {
+ return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED));
+ }
+
+ boolean valid = StringValidator.getInstance().isValid(value, null);
+ if (false == valid) {
+ return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS, new AdditionalInfoParameterInfo(null, value), null, AdditionalInformationEnum.Value));
+ }
+
+ String converted = StringConvertor.getInstance().convert(value, null, null);
+
+ return Either.left(converted);
+ }
+
+ private AdditionalInfoParameterInfo findAdditionInformationKey(List<AdditionalInfoParameterInfo> parameters, String key) {
+
+ for (AdditionalInfoParameterInfo infoParameterInfo : parameters) {
+ if (infoParameterInfo.getKey().equals(key)) {
+ return infoParameterInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * validate and normalize the key
+ *
+ * @param additionalInfoParameterInfo
+ * @return
+ */
+ private Either<String, ResponseFormat> validateAndNormalizeKey(String key) {
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo();
+ additionalInfoParameterInfo.setKey(key);
+
+ key = ValidationUtils.normalizeAdditionalInformation(key);
+ boolean isNonEmptyString = ValidationUtils.validateStringNotEmpty(key);
+ if (false == isNonEmptyString) {
+ return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED, null, null, AdditionalInformationEnum.Label));
+ }
+ boolean isValidString = ValidationUtils.validateAdditionalInformationKeyName(key);
+ if (false == isValidString) {
+ if (false == ValidationUtils.validateLength(key, ValidationUtils.ADDITIONAL_INFORMATION_KEY_MAX_LENGTH)) {
+ return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_EXCEEDS_LIMIT, additionalInfoParameterInfo, null, AdditionalInformationEnum.Label));
+ }
+ return Either.right(componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS, additionalInfoParameterInfo, null, AdditionalInformationEnum.Label));
+ }
+
+ return Either.left(key);
+ }
+
+ /**
+ * update key and value of a given additional information.
+ *
+ * @param nodeType
+ * @param resourceId
+ * @param additionalInfoParameterInfo
+ * @param additionalInformationUid
+ * - Future use
+ * @param userId
+ * @return
+ */
+ public Either<AdditionalInfoParameterInfo, ResponseFormat> updateAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String additionalInformationUid, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Additional Information", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
+
+ ResponseFormat responseFormat = verifyCanWorkOnComponent(nodeType, resourceId, userId);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+ // lock component
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, nodeType);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedLockObjectError, UPDATE_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(UPDATE_ADDITIONAL_INFORMATION, nodeType.getName(), resourceId);
+ log.info("Failed to lock component {} error - {}", resourceId, lockResult);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+ try {
+
+ // validate input
+ responseFormat = validateAndConvertKey(additionalInfoParameterInfo, UPDATE_ADDITIONAL_INFORMATION);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ responseFormat = validateAndConvertValue(additionalInfoParameterInfo, UPDATE_ADDITIONAL_INFORMATION);
+ if (responseFormat != null) {
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> addResult = additionalInformationOperation.updateAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(),
+ additionalInfoParameterInfo.getKey(), additionalInfoParameterInfo.getValue(), true);
+
+ if (addResult.isRight()) {
+ StorageOperationStatus status = addResult.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, UPDATE_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeSystemError(UPDATE_ADDITIONAL_INFORMATION);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
+ return result;
+ } else {
+ AdditionalInformationDefinition informationDefinition = addResult.left().value();
+ AdditionalInfoParameterInfo parameterInfo = findAdditionInformationKey(informationDefinition.getParameters(), additionalInfoParameterInfo.getKey());
+ result = Either.left(parameterInfo);
+ return result;
+ }
+
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, nodeType);
+ }
+
+ }
+
+ /**
+ * Delete an additional information label
+ *
+ * @param nodeType
+ * @param resourceId
+ * @param additionalInfoParameterInfo
+ * @param additionalInformationUid
+ * - Null. Future use.
+ * @param userId
+ * @return
+ */
+ public Either<AdditionalInfoParameterInfo, ResponseFormat> deleteAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String additionalInformationUid, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Additional Information", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
+
+ ResponseFormat responseFormat = verifyCanWorkOnComponent(nodeType, resourceId, userId);
+ if (responseFormat != null) {
+ return Either.right(responseFormat);
+ }
+ // lock component
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, nodeType);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedLockObjectError, DELETE_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(DELETE_ADDITIONAL_INFORMATION, nodeType.getName(), resourceId);
+ log.info("Failed to lock component {} error - {}", resourceId, lockResult);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+
+ try {
+
+ Either<AdditionalInfoParameterInfo, StorageOperationStatus> findIdRes = additionalInformationOperation.getAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(), true);
+ if (findIdRes.isRight()) {
+ StorageOperationStatus status = findIdRes.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, GET_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeSystemError(GET_ADDITIONAL_INFORMATION);
+ }
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
+ return result;
+ }
+
+ AdditionalInfoParameterInfo foundAdditionalInfo = findIdRes.left().value();
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> addResult = additionalInformationOperation.deleteAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(), true);
+
+ if (addResult.isRight()) {
+ StorageOperationStatus status = addResult.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, DELETE_ADDITIONAL_INFORMATION);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError(DELETE_ADDITIONAL_INFORMATION);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
+ return result;
+ } else {
+ result = Either.left(foundAdditionalInfo);
+ return result;
+ }
+
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, nodeType);
+ }
+
+ }
+
+ /**
+ * @param nodeType
+ * @param resourceId
+ * @param additionalInfoParameterInfo
+ * @param additionalInformationUid
+ * @param userId
+ * @return
+ */
+ public Either<AdditionalInfoParameterInfo, ResponseFormat> getAdditionalInformation(NodeTypeEnum nodeType, String resourceId, AdditionalInfoParameterInfo additionalInfoParameterInfo, String additionalInformationUid, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Additional Information", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<AdditionalInfoParameterInfo, ResponseFormat> result = null;
+
+ try {
+
+ Either<AdditionalInfoParameterInfo, StorageOperationStatus> findIdRes = additionalInformationOperation.getAdditionalInformationParameter(nodeType, resourceId, additionalInfoParameterInfo.getUniqueId(), true);
+
+ if (findIdRes.isRight()) {
+ StorageOperationStatus status = findIdRes.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus, additionalInfoParameterInfo, nodeType, AdditionalInformationEnum.None));
+ }
+
+ AdditionalInfoParameterInfo foundAdditionalInfo = findIdRes.left().value();
+
+ result = Either.left(foundAdditionalInfo);
+
+ return result;
+
+ } finally {
+ commitOrRollback(result);
+ }
+
+ }
+
+ /**
+ * Get all additional information properties of a given resource/service
+ *
+ * @param nodeType
+ * @param resourceId
+ * @param additionalInformationUid
+ * - Future use
+ * @param userId
+ * @return
+ */
+ public Either<AdditionalInformationDefinition, ResponseFormat> getAllAdditionalInformation(NodeTypeEnum nodeType, String resourceId, String additionalInformationUid, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get All Additional Information", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<AdditionalInformationDefinition, ResponseFormat> result = null;
+
+ try {
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> findIdRes = additionalInformationOperation.getAllAdditionalInformationParameters(nodeType, resourceId, false);
+ if (findIdRes.isRight()) {
+ StorageOperationStatus status = DaoStatusConverter.convertTitanStatusToStorageStatus(findIdRes.right().value());
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForAdditionalInformation(status);
+ result = Either.right(componentsUtils.getResponseFormatAdditionalProperty(actionStatus));
+ } else {
+ AdditionalInformationDefinition informationDefinition = findIdRes.left().value();
+ result = Either.left(informationDefinition);
+ }
+
+ return result;
+
+ } finally {
+ commitOrRollback(result);
+ }
+
+ }
+
+ private ResponseFormat verifyCanWorkOnComponent(NodeTypeEnum nodeType, String resourceId, String userId) {
+
+ switch (nodeType) {
+ case Resource:
+
+ // verify that resource is checked-out and the user is the last
+ // updater
+ if (!ComponentValidationUtils.canWorkOnResource(resourceId, resourceOperation, userId)) {
+ return componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ }
+ break;
+ case Service:
+
+ // verify that resource is checked-out and the user is the last
+ // updater
+ if (!ComponentValidationUtils.canWorkOnService(resourceId, serviceOperation, userId)) {
+ return componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ }
+ break;
+ default:
+ return componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT, nodeType.getName());
+ }
+
+ return null;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java
new file mode 100644
index 0000000000..16ed4a1868
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ArtifactsBusinessLogic.java
@@ -0,0 +1,4127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.Configuration.DeploymentArtifactTypeConfig;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.validation.DeploymentArtifactHeatConfiguration;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.heat.HeatParameterType;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.IComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IHeatParametersOperation;
+import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.IUserAdminOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.openecomp.sdc.be.servlets.RepresentationUtils;
+import org.openecomp.sdc.be.tosca.CsarUtils;
+import org.openecomp.sdc.be.tosca.ToscaError;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.be.tosca.ToscaRepresentation;
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.yaml.snakeyaml.Yaml;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.sun.org.apache.xerces.internal.parsers.SAXParser;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("artifactBusinessLogic")
+public class ArtifactsBusinessLogic extends BaseBusinessLogic {
+ private static final String ARTIFACT_TYPE_OTHER = "OTHER";
+ private static final String ARTIFACT_DESCRIPTION = "artifact description";
+ private static final String ARTIFACT_LABEL = "artifact label";
+ private static final String ARTIFACT_URL = "artifact url";
+ private static final String ARTIFACT_NAME = "artifact name";
+ private static final String ARTIFACT_PAYLOAD = "artifact payload";
+
+ private static final String ARTIFACT_PLACEHOLDER_TYPE = "type";
+ private static final String ARTIFACT_PLACEHOLDER_DISPLAY_NAME = "displayName";
+ private static final Object ARTIFACT_PLACEHOLDER_DESCRIPTION = "description";
+
+ private static Integer defaultHeatTimeout;
+ private static final Integer NON_HEAT_TIMEOUT = 0;
+ private static Logger log = LoggerFactory.getLogger(ArtifactsBusinessLogic.class.getName());
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ @javax.annotation.Resource
+ private IArtifactOperation artifactOperation;
+
+ // @javax.annotation.Resource
+ // private IResourceUploader daoUploader;
+
+ @javax.annotation.Resource
+ private IInterfaceLifecycleOperation interfaceLifecycleOperation;
+ @javax.annotation.Resource
+ private IUserAdminOperation userOperaton;
+
+ // @javax.annotation.Resource
+ // private ESCatalogDAO esCatalogDao;
+
+ @javax.annotation.Resource
+ private IElementOperation elementOperation;
+
+ @javax.annotation.Resource
+ private ResourceBusinessLogic resourceBusinessLogic;
+
+ @javax.annotation.Resource
+ private ServiceBusinessLogic serviceBusinessLogic;
+
+ @javax.annotation.Resource
+ private UserBusinessLogic userAdminManager;
+
+ @javax.annotation.Resource
+ private IHeatParametersOperation heatParametersOperation;
+
+ @javax.annotation.Resource
+ private IComponentInstanceOperation resourceInstanceOperation;
+
+ @Autowired
+ private ArtifactCassandraDao artifactCassandraDao;
+
+ @Autowired
+ private ToscaExportHandler toscaExportUtils;
+
+ @Autowired
+ private CsarUtils csarUtils;
+
+ @Autowired
+ private LifecycleBusinessLogic lifecycleBusinessLogic;
+
+ @Autowired
+ private IUserBusinessLogic userBusinessLogic;
+
+ public ArtifactsBusinessLogic() {
+ defaultHeatTimeout = ConfigurationManager.getConfigurationManager().getConfiguration().getDefaultHeatArtifactTimeoutMinutes();
+ if ((defaultHeatTimeout == null) || (defaultHeatTimeout < 1)) {
+ defaultHeatTimeout = 60;
+ }
+ }
+
+ public static enum ArtifactOperation {
+
+ Create(false), Update(false), Delete(false), Download(false);
+
+ private boolean isExternalApi;
+
+ ArtifactOperation(boolean isExternalApi) {
+ this.isExternalApi = isExternalApi;
+ }
+
+ public boolean isExternalApi() {
+ return isExternalApi;
+ }
+
+ public void setExternalApi(boolean isExternalApi) {
+ this.isExternalApi = isExternalApi;
+ }
+ }
+
+ // new flow US556184
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleArtifactRequest(String componentId, String userId, ComponentTypeEnum componentType, ArtifactOperation operation, String artifactId, ArtifactDefinition artifactInfo,
+ String origMd5, String originData, String interfaceName, String operationName, String parentId, String containerComponentType) {
+ return handleArtifactRequest(componentId, userId, componentType, operation, artifactId, artifactInfo, origMd5, originData, interfaceName, operationName, parentId, containerComponentType, true, false);
+ }
+
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleArtifactRequest(String componentId, String userId, ComponentTypeEnum componentType, ArtifactOperation operation, String artifactId, ArtifactDefinition artifactInfo,
+ String origMd5, String originData, String interfaceName, String operationName, String parentId, String containerComponentType, boolean shouldLock, boolean inTransaction) {
+ // step 1
+ // detect auditing type
+ AuditingActionEnum auditingAction = detectAuditingType(operation, origMd5);
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ // step 2
+ // check header
+ if (userId == null) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ log.debug("handleArtifactRequest - no HTTP_CSP_HEADER , component id {}", componentId);
+ handleAuditing(auditingAction, null, componentId, null, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ // step 3
+ // check user existence
+ // step 4
+ // check user's role
+ Either<User, ResponseFormat> userResult = validateUserExists(userId, auditingAction, componentId, artifactId, componentType, inTransaction);
+ if (userResult.isRight()) {
+ return Either.right(userResult.right().value());
+ }
+
+ User user = userResult.left().value();
+ Either<Boolean, ResponseFormat> validateUserRole = validateUserRole(user, auditingAction, componentId, artifactId, componentType, operation);
+ if (validateUserRole.isRight()) {
+ return Either.right(validateUserRole.right().value());
+ }
+
+ // steps 5 - 6 - 7
+ // 5. check service/resource existence
+ // 6. check service/resource check out
+ // 7. user is owner of checkout state
+ org.openecomp.sdc.be.model.Component component = null;
+ // ComponentInstance resourceInstance = null;
+ String realComponentId = componentType == ComponentTypeEnum.RESOURCE_INSTANCE ? parentId : componentId;
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(realComponentId, userId, auditingAction, user, artifactId, componentType, containerComponentType, inTransaction);
+ if (validateComponent.isRight()) {
+ return Either.right(validateComponent.right().value());
+ }
+ component = validateComponent.left().value();
+ Either<Boolean, ResponseFormat> validateWorkOnResource = validateWorkOnComponent(component, userId, auditingAction, user, artifactId, operation, componentType);
+ if (validateWorkOnResource.isRight()) {
+ return Either.right(validateWorkOnResource.right().value());
+ }
+ // step 8
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> result = validateAndHandleArtifact(componentId, componentType, operation, artifactId, artifactInfo, origMd5, originData, interfaceName, operationName, parentId, user, component,
+ shouldLock, inTransaction);
+
+ return result;
+ }
+
+ /**
+ * This Method validates only the Artifact and does not validate user / role / component ect...<br>
+ * For regular usage use <br>
+ * {@link #handleArtifactRequest(String, String, ComponentTypeEnum, ArtifactOperation, String, ArtifactDefinition, String, String, String, String, String, String)}
+ *
+ * @return
+ */
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> validateAndHandleArtifact(String componentUniqueId, ComponentTypeEnum componentType, ArtifactOperation operation, String artifactUniqueId, ArtifactDefinition artifactDefinition,
+ String origMd5, String originData, String interfaceName, String operationName, String parentId, User user, Component component, boolean shouldLock, boolean inTransaction) {
+ Component parent = component;
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+
+ AuditingActionEnum auditingAction = detectAuditingType(operation, origMd5);
+ artifactDefinition = validateArtifact(componentUniqueId, componentType, operation, artifactUniqueId, artifactDefinition, parentId, auditingAction, user, component, parent, shouldLock, errorWrapper);
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> result;
+ if (errorWrapper.isEmpty()) {
+ // step 10
+ result = doAction(componentUniqueId, componentType, operation, artifactUniqueId, artifactDefinition, origMd5, originData, interfaceName, operationName, auditingAction, user, parent, shouldLock, inTransaction);
+ } else {
+ result = Either.right(errorWrapper.getInnerElement());
+ }
+ return result;
+ }
+
+ private ArtifactDefinition validateArtifact(String componentId, ComponentTypeEnum componentType, ArtifactOperation operation, String artifactId, ArtifactDefinition artifactInfo, String parentId, AuditingActionEnum auditingAction, User user,
+ org.openecomp.sdc.be.model.Component component, org.openecomp.sdc.be.model.Component parent, boolean shouldLock, Wrapper<ResponseFormat> errorWrapper) {
+ if (operation == ArtifactOperation.Update || operation == ArtifactOperation.Delete || operation == ArtifactOperation.Download) {
+ Either<ArtifactDefinition, ResponseFormat> validateArtifact = validateArtifact(componentId, componentType, artifactId, component, auditingAction, parentId);
+ if (validateArtifact.isRight()) {
+ ResponseFormat responseFormat = validateArtifact.right().value();
+ handleAuditing(auditingAction, parent, componentId, user, null, null, artifactId, responseFormat, componentType, null);
+ errorWrapper.setInnerElement(validateArtifact.right().value());
+ } else if (operation == ArtifactOperation.Download) {
+ artifactInfo = validateArtifact.left().value();
+ handleHeatEnvDownload(componentId, user, component, validateArtifact, shouldLock, errorWrapper);
+ }
+ }
+ return artifactInfo;
+ }
+
+ private void handleHeatEnvDownload(String componentId, User user, org.openecomp.sdc.be.model.Component component, Either<ArtifactDefinition, ResponseFormat> validateArtifact, boolean shouldLock, Wrapper<ResponseFormat> errorWrapper) {
+ ArtifactDefinition validatedArtifact = validateArtifact.left().value();
+
+ if (validatedArtifact.getArtifactType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ ComponentInstance componentInstance = component.getComponentInstances().stream().filter(p -> p.getUniqueId().equals(componentId)).findAny().get();
+ ArtifactDefinition heatEnvWithHeatParams = componentInstance.getDeploymentArtifacts().values().stream().filter(p -> p.getUniqueId().equals(validatedArtifact.getUniqueId())).findAny().get();
+ Either<ArtifactDefinition, ResponseFormat> eitherGenerated = generateHeatEnvArtifact(heatEnvWithHeatParams, component, componentInstance.getName(), user, shouldLock);
+ if (eitherGenerated.isRight()) {
+ errorWrapper.setInnerElement(eitherGenerated.right().value());
+ }
+ }
+ }
+
+ private boolean artifactGenerationRequired(org.openecomp.sdc.be.model.Component component, ArtifactDefinition artifactInfo) {
+ return artifactInfo.getArtifactGroupType() == ArtifactGroupTypeEnum.TOSCA && (component.getLifecycleState() == LifecycleStateEnum.NOT_CERTIFIED_CHECKIN || component.getLifecycleState() == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> generateAndSaveToscaArtifact(ArtifactDefinition artifactDefinition, org.openecomp.sdc.be.model.Component component, User user, boolean isInCertificationRequest,
+ boolean shouldLock, boolean inTransaction, boolean fetchTemplatesFromDB) {
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> generated = generateToscaArtifact(component, artifactDefinition, isInCertificationRequest, fetchTemplatesFromDB, shouldLock, inTransaction);
+ if (generated.isRight()) {
+ return generated;
+ }
+ byte[] decodedPayload = artifactDefinition.getPayloadData();
+ artifactDefinition.setEsId(artifactDefinition.getUniqueId());
+ artifactDefinition.setArtifactChecksum(GeneralUtility.calculateMD5ByByteArray(decodedPayload));
+ return lockComponentAndUpdateArtifact(component.getUniqueId(), artifactDefinition, AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE, artifactDefinition.getUniqueId(), user, component.getComponentType(), component, decodedPayload, null, null,
+ shouldLock, inTransaction);
+
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> generateToscaArtifact(Component parent, ArtifactDefinition artifactInfo, boolean isInCertificationRequest, boolean fetchTemplatesFromDB, boolean shouldLock,
+ boolean inTransaction) {
+ log.debug("tosca artifact generation");
+ if (artifactInfo.getArtifactType().equals(ArtifactTypeEnum.TOSCA_CSAR.getType())) {
+ Either<byte[], ResponseFormat> generated = csarUtils.createCsar(parent, fetchTemplatesFromDB, isInCertificationRequest, shouldLock, inTransaction);
+
+ if (generated.isRight()) {
+ log.debug("Failed to export tosca csar for component {} error {}", parent.getUniqueId(), generated.right().value());
+
+ return Either.right(generated.right().value());
+ }
+ byte[] value = generated.left().value();
+ artifactInfo.setPayload(value);
+
+ } else {
+ Either<ToscaRepresentation, ToscaError> exportComponent = toscaExportUtils.exportComponent(parent);
+ if (exportComponent.isRight()) {
+ log.debug("Failed export tosca yaml for component {} error {}", parent.getUniqueId(), exportComponent.right().value());
+ ActionStatus status = componentsUtils.convertFromToscaError(exportComponent.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(status);
+ return Either.right(responseFormat);
+ }
+ log.debug("Tosca yaml exported for component {} ", parent.getUniqueId());
+ String payload = exportComponent.left().value().getMainYaml();
+ artifactInfo.setPayloadData(payload);
+ }
+ return Either.left(Either.left(artifactInfo));
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> doAction(String componentId, ComponentTypeEnum componentType, ArtifactOperation operation, String artifactId, ArtifactDefinition artifactInfo, String origMd5,
+ String originData, String interfaceName, String operationName, AuditingActionEnum auditingAction, User user, org.openecomp.sdc.be.model.Component parent, boolean shouldLock, boolean inTransaction) {
+ if (interfaceName != null && operationName != null) {
+ interfaceName = interfaceName.toLowerCase();
+ operationName = operationName.toLowerCase();
+ }
+ switch (operation) {
+ case Download:
+ if (artifactGenerationRequired(parent, artifactInfo)) {
+ return generateToscaArtifact(parent, artifactInfo, false, false, shouldLock, inTransaction);
+ }
+ return handleDownload(componentId, artifactId, user, auditingAction, componentType, parent, shouldLock, inTransaction);
+ case Delete:
+ return handleDelete(componentId, artifactId, user, auditingAction, componentType, parent, interfaceName, operationName, shouldLock, inTransaction);
+ case Update:
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.findType(artifactInfo.getArtifactType());
+ if (componentType.equals(ComponentTypeEnum.RESOURCE_INSTANCE)
+ && (artifactType == ArtifactTypeEnum.HEAT || artifactType == ArtifactTypeEnum.HEAT_VOL || artifactType == ArtifactTypeEnum.HEAT_NET || artifactType == ArtifactTypeEnum.HEAT_ENV)) {
+ return handleUpdateHeatEnv(componentId, artifactInfo, auditingAction, artifactId, user, componentType, parent, originData, origMd5, operation, shouldLock, inTransaction);
+ }
+ return handleUpdate(componentId, artifactInfo, operation, auditingAction, artifactId, user, componentType, parent, origMd5, originData, interfaceName, operationName, shouldLock, inTransaction);
+ case Create:
+ return handleCreate(componentId, artifactInfo, operation, auditingAction, user, componentType, parent, origMd5, originData, interfaceName, operationName, shouldLock, inTransaction);
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param componentId
+ * @param artifactId
+ * @param userId
+ * @param componentType
+ * @param parentId
+ * TODO
+ * @return
+ */
+
+ public Either<ImmutablePair<String, byte[]>, ResponseFormat> handleDownloadToscaModelRequest(Component component, ArtifactDefinition csarArtifact, boolean shouldLock, boolean inTransaction) {
+ if (artifactGenerationRequired(component, csarArtifact)) {
+ Either<byte[], ResponseFormat> generated = csarUtils.createCsar(component, false, false, shouldLock, inTransaction);
+
+ if (generated.isRight()) {
+ log.debug("Failed to export tosca csar for component {} error {}", component.getUniqueId(), generated.right().value());
+
+ return Either.right(generated.right().value());
+ }
+ return Either.left(new ImmutablePair<String, byte[]>(csarArtifact.getArtifactName(), generated.left().value()));
+ }
+ return downloadArtifact(csarArtifact);
+ }
+
+ public Either<ImmutablePair<String, byte[]>, ResponseFormat> handleDownloadRequestById(String componentId, String artifactId, String userId, ComponentTypeEnum componentType, String parentId, String containerComponentType) {
+ // perform all validation in common flow
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> result = handleArtifactRequest(componentId, userId, componentType, ArtifactOperation.Download, artifactId, null, null, null, null, null, parentId, containerComponentType);
+ if (result.isRight()) {
+ return Either.right(result.right().value());
+ }
+ ArtifactDefinition artifactDefinition;
+ Either<ArtifactDefinition, Operation> insideValue = result.left().value();
+ if (insideValue.isLeft()) {
+ artifactDefinition = insideValue.left().value();
+ } else {
+ artifactDefinition = insideValue.right().value().getImplementation();
+ }
+ // for tosca artifacts generated on download without saving
+ if (artifactDefinition.getArtifactGroupType() == ArtifactGroupTypeEnum.TOSCA && artifactDefinition.getPayloadData() != null) {
+ return Either.left(new ImmutablePair<String, byte[]>(artifactDefinition.getArtifactName(), artifactDefinition.getPayloadData()));
+ }
+ return downloadArtifact(artifactDefinition);
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> validateArtifact(String componentId, ComponentTypeEnum componentType, String artifactId, org.openecomp.sdc.be.model.Component component, AuditingActionEnum auditingAction, String parentId) {
+ // step 9
+ // check artifact existence
+ Either<ArtifactDefinition, StorageOperationStatus> artifactResult = artifactOperation.getArtifactById(artifactId, false);
+ if (artifactResult.isRight()) {
+ if (artifactResult.right().value().equals(StorageOperationStatus.ARTIFACT_NOT_FOUND)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, "");
+ log.debug("addArtifact - artifact {} not found", artifactId);
+ return Either.right(responseFormat);
+
+ } else {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(artifactResult.right().value()));
+ log.debug("addArtifact - failed to fetch artifact {}, error {}", artifactId, artifactResult.right().value());
+ return Either.right(responseFormat);
+ }
+ }
+ // step 9.1
+ // check artifact belong to component
+ boolean found = false;
+ switch (componentType) {
+ case RESOURCE:
+ case SERVICE:
+ found = checkArtifactInComponent(component, artifactId);
+ break;
+ case RESOURCE_INSTANCE:
+ found = checkArtifactInResourceInstance(component, componentId, artifactId);
+ break;
+ default:
+
+ }
+ if (!found) {
+ // String component =
+ // componentType.equals(ComponentTypeEnum.RESOURCE) ? "resource" :
+ // "service";
+ String componentName = componentType.name().toLowerCase();
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ARTIFACT_NOT_FOUND, componentName);
+ log.debug("addArtifact - Component artifact not found component Id {}, artifact id {}", componentId, artifactId);
+ return Either.right(responseFormat);
+ }
+ return Either.left(artifactResult.left().value());
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleCreate(String parentId, ArtifactDefinition artifactInfo, ArtifactOperation operation, AuditingActionEnum auditingAction, User user, ComponentTypeEnum componentType,
+ org.openecomp.sdc.be.model.Component parent, String origMd5, String originData, String interfaceType, String operationName, boolean shouldLock, boolean inTransaction) {
+
+ String artifactId = null;
+
+ // step 11
+ Either<byte[], ResponseFormat> payloadEither = validateInput(parentId, artifactInfo, operation, auditingAction, artifactId, user, componentType, parent, origMd5, originData, interfaceType, operationName, inTransaction);
+ if (payloadEither.isRight()) {
+ return Either.right(payloadEither.right().value());
+ }
+ byte[] decodedPayload = payloadEither.left().value();
+ NodeTypeEnum parentType = convertParentType(componentType);
+ // lock resource
+
+ if (shouldLock) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(parent, "Upload Artifact - lock ");
+ if (lockComponent.isRight()) {
+ handleAuditing(auditingAction, parent, parentId, user, null, null, null, lockComponent.right().value(), componentType, null);
+ return Either.right(lockComponent.right().value());
+ }
+ }
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+
+ try {
+ resultOp = createArtifact(parent, parentId, artifactInfo, decodedPayload, user, componentType, auditingAction, interfaceType, operationName);
+ return resultOp;
+ } finally {
+ if (shouldLock) {
+ unlockComponent(resultOp, parent, inTransaction);
+ }
+
+ }
+
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> lockComponentAndUpdateArtifact(String parentId, ArtifactDefinition artifactInfo, AuditingActionEnum auditingAction, String artifactId, User user,
+ ComponentTypeEnum componentType, org.openecomp.sdc.be.model.Component parent, byte[] decodedPayload, String interfaceType, String operationName, boolean shouldLock, boolean inTransaction) {
+
+ NodeTypeEnum parentType = convertParentType(componentType);
+
+ // lock resource
+ if (shouldLock) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(parent, "Update Artifact - lock ");
+ if (lockComponent.isRight()) {
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, lockComponent.right().value(), componentType, null);
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+ try {
+ resultOp = updateArtifactFlow(parent, parentId, artifactId, artifactInfo, user, decodedPayload, componentType, auditingAction, interfaceType, operationName);
+ return resultOp;
+
+ } finally {
+ if (shouldLock) {
+ unlockComponent(resultOp, parent, inTransaction);
+ }
+ }
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleUpdate(String parentId, ArtifactDefinition artifactInfo, ArtifactOperation operation, AuditingActionEnum auditingAction, String artifactId, User user,
+ ComponentTypeEnum componentType, org.openecomp.sdc.be.model.Component parent, String origMd5, String originData, String interfaceType, String operationName, boolean shouldLock, boolean inTransaction) {
+
+ Either<byte[], ResponseFormat> payloadEither = validateInput(parentId, artifactInfo, operation, auditingAction, artifactId, user, componentType, parent, origMd5, originData, interfaceType, operationName, inTransaction);
+
+ if (payloadEither.isRight()) {
+ return Either.right(payloadEither.right().value());
+ }
+ byte[] decodedPayload = payloadEither.left().value();
+
+ return lockComponentAndUpdateArtifact(parentId, artifactInfo, auditingAction, artifactId, user, componentType, parent, decodedPayload, interfaceType, operationName, shouldLock, inTransaction);
+ }
+
+ private Either<byte[], ResponseFormat> validateInput(String parentId, ArtifactDefinition artifactInfo, ArtifactOperation operation, AuditingActionEnum auditingAction, String artifactId, User user, ComponentTypeEnum componentType,
+ org.openecomp.sdc.be.model.Component parent, String origMd5, String originData, String interfaceType, String operationName, boolean inTransaction) {
+ // Md5 validations
+ Either<Boolean, ResponseFormat> validateMd5 = validateMd5(origMd5, originData, artifactInfo.getPayloadData(), operation);
+ if (validateMd5.isRight()) {
+ ResponseFormat responseFormat = validateMd5.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+
+ // step 11
+ Either<ArtifactDefinition, ResponseFormat> validateResult = validateInput(parentId, artifactInfo, operation, artifactId, user, interfaceType, operationName, componentType, parent, inTransaction);
+ if (validateResult.isRight()) {
+ ResponseFormat responseFormat = validateResult.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(validateResult.right().value());
+ }
+
+ Either<byte[], ResponseFormat> payloadEither = handlePayload(artifactInfo, isArtifactMetadataUpdate(auditingAction));
+ if (payloadEither.isRight()) {
+ ResponseFormat responseFormat = payloadEither.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ log.debug("Error during handle payload");
+ return Either.right(responseFormat);
+ }
+
+ // validate heat parameters. this part must be after the parameters are
+ // extracted in "handlePayload"
+ Either<ArtifactDefinition, ResponseFormat> validateAndConvertHeatParamers = validateAndConvertHeatParamers(artifactInfo, artifactInfo.getArtifactType());
+ if (validateAndConvertHeatParamers.isRight()) {
+ ResponseFormat responseFormat = validateAndConvertHeatParamers.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, null, artifactId, responseFormat, componentType, null);
+ log.debug("Error during handle payload");
+ return Either.right(responseFormat);
+ }
+ return payloadEither;
+ }
+
+ public void handleAuditing(AuditingActionEnum auditingActionEnum, Component component, String componentId, User user, ArtifactDefinition artifactDefinition, String prevArtifactUuid, String currentArtifactUuid, ResponseFormat responseFormat,
+ ComponentTypeEnum componentTypeEnum, String resourceInstanceName) {
+
+ if (auditingActionEnum.getAuditingEsType().equals(AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE)) {
+ return;
+ }
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = createArtifactAuditingFields(artifactDefinition, prevArtifactUuid, currentArtifactUuid);
+
+ if (user == null) {
+ user = new User();
+ user.setUserId("UNKNOWN");
+ }
+ switch (componentTypeEnum) {
+
+ case RESOURCE:
+ Resource resource = (Resource) component;
+ if (resource == null) {
+ // In that case, component ID should be instead of name
+ resource = new Resource();
+ resource.setName(componentId);
+ }
+ componentsUtils.auditResource(responseFormat, user, resource, null, null, auditingActionEnum, auditingFields);
+ break;
+
+ case SERVICE:
+ Service service = (Service) component;
+ if (service == null) {
+ // In that case, component ID should be instead of name
+ service = new Service();
+ service.setName(componentId);
+ }
+ componentsUtils.auditComponent(responseFormat, user, service, null, null, auditingActionEnum, ComponentTypeEnum.SERVICE, auditingFields);
+ break;
+
+ case RESOURCE_INSTANCE:
+ if (resourceInstanceName == null) {
+ resourceInstanceName = getResourceInstanceNameFromComponent(component, componentId);
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInstanceName);
+ componentsUtils.auditComponent(responseFormat, user, component, null, null, auditingActionEnum, ComponentTypeEnum.RESOURCE_INSTANCE, auditingFields);
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ private String getResourceInstanceNameFromComponent(Component component, String componentId) {
+ ComponentInstance resourceInstance = component.getComponentInstances().stream().filter(p -> p.getUniqueId().equals(componentId)).findFirst().orElse(null);
+ String resourceInstanceName = null;
+ if (resourceInstance != null) {
+ resourceInstanceName = resourceInstance.getName();
+ }
+ return resourceInstanceName;
+ }
+
+ public EnumMap<AuditingFieldsKeysEnum, Object> createArtifactAuditingFields(ArtifactDefinition artifactDefinition, String prevArtifactUuid, String currentArtifactUuid) {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ // Putting together artifact info
+ String artifactData = buildAuditingArtifactData(artifactDefinition);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA, artifactData);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID, prevArtifactUuid);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID, currentArtifactUuid);
+ return auditingFields;
+ }
+
+ // -----
+
+ private String buildAuditingArtifactData(ArtifactDefinition artifactDefinition) {
+ StringBuilder sb = new StringBuilder();
+ if (artifactDefinition != null) {
+ sb.append(artifactDefinition.getArtifactGroupType().getType()).append(",").append("'").append(artifactDefinition.getArtifactLabel()).append("'").append(",").append(artifactDefinition.getArtifactType()).append(",")
+ .append(artifactDefinition.getArtifactName()).append(",").append(artifactDefinition.getTimeout()).append(",").append(artifactDefinition.getEsId());
+
+ sb.append(",");
+ if (artifactDefinition.getArtifactVersion() != null) {
+
+ sb.append(artifactDefinition.getArtifactVersion());
+ } else {
+ sb.append(" ");
+ }
+ sb.append(",");
+ if (artifactDefinition.getArtifactUUID() != null) {
+ sb.append(artifactDefinition.getArtifactUUID());
+ } else {
+ sb.append(" ");
+ }
+ }
+ return sb.toString();
+ }
+
+ private Either<Boolean, ResponseFormat> validateMd5(String origMd5, String originData, byte[] payload, ArtifactOperation operation) {
+
+ if (origMd5 != null) {
+ String encodeBase64Str = GeneralUtility.calculateMD5ByString(originData);
+
+ if (false == encodeBase64Str.equals(origMd5)) {
+ log.debug("The calculated md5 is different then the received one");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_INVALID_MD5));
+ }
+ } else {
+ if (operation == ArtifactOperation.Create) {
+ log.debug("Missing md5 header during artifact create");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_INVALID_MD5));
+ }
+ // Update metadata
+ if (payload != null && payload.length != 0) {
+ log.debug("Cannot have payload while md5 header is missing");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> validateInput(String parentId, ArtifactDefinition artifactInfo, ArtifactOperation operation, String artifactId, User user, String interfaceName, String operationName,
+ ComponentTypeEnum componentType, Component parentComponent, boolean inTransaction) {
+
+ Either<Boolean, ResponseFormat> validateAndSetArtifactname = validateAndSetArtifactname(artifactInfo);
+ if (validateAndSetArtifactname.isRight()) {
+ return Either.right(validateAndSetArtifactname.right().value());
+ }
+ Either<ArtifactDefinition, ResponseFormat> artifactById = fetchCurrentArtifact(operation, artifactId);
+ if (artifactById.isRight()) {
+ return Either.right(artifactById.right().value());
+ }
+ ArtifactDefinition currentArtifactInfo = artifactById.left().value();
+ if (operationName != null && interfaceName != null) {
+ operationName = operationName.toLowerCase();
+ interfaceName = interfaceName.toLowerCase();
+ }
+ Either<ActionStatus, ResponseFormat> logicalNameStatus = handleArtifactLabel(parentId, operation, artifactId, artifactInfo, interfaceName, operationName, currentArtifactInfo, componentType, inTransaction);
+ if (logicalNameStatus.isRight()) {
+ return Either.right(logicalNameStatus.right().value());
+ }
+ // This is a patch to block possibility of updating service api fields
+ // through other artifacts flow
+
+ if (!operation.equals(ArtifactOperation.Create)) {
+ checkAndSetUnUpdatableFields(user, artifactInfo, currentArtifactInfo, (operationName != null ? ArtifactGroupTypeEnum.LIFE_CYCLE : ArtifactGroupTypeEnum.INFORMATIONAL));
+ } else {
+ checkCreateFields(user, artifactInfo, (operationName != null ? ArtifactGroupTypeEnum.LIFE_CYCLE : ArtifactGroupTypeEnum.INFORMATIONAL));
+ }
+
+ composeArtifactId(parentId, artifactId, artifactInfo, interfaceName, operationName);
+ if (currentArtifactInfo != null) {
+ artifactInfo.setMandatory(currentArtifactInfo.getMandatory());
+ }
+
+ // artifactGroupType is not allowed to be updated
+ if (!operation.equals(ArtifactOperation.Create)) {
+ Either<ArtifactDefinition, ResponseFormat> validateGroupType = validateOrSetArtifactGroupType(artifactInfo, currentArtifactInfo);
+ if (validateGroupType.isRight()) {
+ return Either.right(validateGroupType.right().value());
+ }
+ }
+ // TODO TEMP !!!
+ NodeTypeEnum parentType = convertParentType(componentType);
+
+ // TODO TEMP !!!
+ boolean isCreate = operation.equals(ArtifactOperation.Create);
+
+ if (isDeploymentArtifact(artifactInfo)) {
+ Either<Boolean, ResponseFormat> deploymentValidationResult = validateDeploymentArtifact(parentComponent, parentId, user.getUserId(), isCreate, artifactInfo, currentArtifactInfo, parentType);
+ if (deploymentValidationResult.isRight()) {
+ return Either.right(deploymentValidationResult.right().value());
+ }
+ } else {
+ artifactInfo.setTimeout(NON_HEAT_TIMEOUT);
+
+ /*
+ * if (informationDeployedArtifactsBusinessLogic. isInformationDeployedArtifact(artifactInfo)) { Either<Boolean, ResponseFormat> validationResult = informationDeployedArtifactsBusinessLogic.validateArtifact( isCreate, artifactInfo,
+ * parentComponent, parentType); if (validationResult.isRight()) { return Either.right(validationResult.right().value()); } }
+ */
+ }
+
+ Either<Boolean, ResponseFormat> descriptionResult = validateAndCleanDescription(artifactInfo);
+ if (descriptionResult.isRight()) {
+ return Either.right(descriptionResult.right().value());
+ }
+
+ if (currentArtifactInfo != null && currentArtifactInfo.getArtifactGroupType().equals(ArtifactGroupTypeEnum.SERVICE_API)) {
+ Either<ActionStatus, ResponseFormat> validateServiceApiType = validateArtifactType(user.getUserId(), artifactInfo, parentType);
+ if (validateServiceApiType.isRight()) {
+ return Either.right(validateServiceApiType.right().value());
+ }
+ // Change of type is not allowed and should be ignored
+
+ artifactInfo.setArtifactType(ARTIFACT_TYPE_OTHER);
+
+ Either<Boolean, ResponseFormat> validateUrl = validateAndServiceApiUrl(artifactInfo);
+ if (validateUrl.isRight()) {
+ return Either.right(validateUrl.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> validateUpdate = validateFirstUpdateHasPayload(artifactInfo, currentArtifactInfo);
+ if (validateUpdate.isRight()) {
+ log.debug("serviceApi first update cnnot be without payload.");
+ return Either.right(validateUpdate.right().value());
+ }
+ } else {
+ Either<ActionStatus, ResponseFormat> validateArtifactType = validateArtifactType(user.getUserId(), artifactInfo, parentType);
+ if (validateArtifactType.isRight()) {
+ return Either.right(validateArtifactType.right().value());
+ }
+ if (artifactInfo.getApiUrl() != null) {
+ artifactInfo.setApiUrl(null);
+ log.error("Artifact URL cannot be set through this API - ignoring");
+ }
+
+ if (artifactInfo.getServiceApi() != null) {
+ if (artifactInfo.getServiceApi()) {
+ artifactInfo.setServiceApi(false);
+ log.error("Artifact service API flag cannot be changed - ignoring");
+ }
+ }
+ }
+
+ return Either.left(artifactInfo);
+ }
+
+ private NodeTypeEnum convertParentType(ComponentTypeEnum componentType) {
+ if (componentType.equals(ComponentTypeEnum.RESOURCE)) {
+ return NodeTypeEnum.Resource;
+ } else if (componentType.equals(ComponentTypeEnum.RESOURCE_INSTANCE)) {
+ return NodeTypeEnum.ResourceInstance;
+ } else {
+ return NodeTypeEnum.Service;
+ }
+ }
+
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDelete(String parentId, String artifactId, User user, AuditingActionEnum auditingAction, ComponentTypeEnum componentType, org.openecomp.sdc.be.model.Component parent,
+ String interfaceType, String operationName, boolean shouldLock, boolean inTransaction) {
+ NodeTypeEnum parentType = convertParentType(componentType);
+ // lock resource
+ if (shouldLock) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(parent, "Delete Artifact - lock resource: ");
+ if (lockComponent.isRight()) {
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, lockComponent.right().value(), componentType, null);
+ return Either.right(lockComponent.right().value());
+ }
+ }
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+ Either<ArtifactDefinition, Operation> insideEither = null;
+ StorageOperationStatus error = null;
+ boolean isLeft = false;
+ ArtifactDefinition artifactDefinition = null;
+ Integer artifactParentsCount = 1;
+ try {
+ if (interfaceType != null && operationName != null) {
+ log.debug("Try to delete inteface lifecycle artifact {}", artifactId);
+
+ Either<Operation, StorageOperationStatus> result = interfaceLifecycleOperation.deleteInterfaceOperation(parentId, interfaceType, UniqueIdBuilder.buildOperationByInterfaceUniqueId(parentId, interfaceType, operationName),
+ inTransaction);
+ isLeft = result.isLeft();
+ if (isLeft) {
+ artifactDefinition = result.left().value().getImplementation();
+ insideEither = Either.right(result.left().value());
+ }
+ } else {
+ log.debug("Try to delete artifact, get parents {}", artifactId);
+
+ Either<Integer, StorageOperationStatus> parentsOfArtifact = artifactOperation.getParentsOfArtifact(artifactId, parentType);
+ if (parentsOfArtifact.isRight()) {
+ log.debug("Failed to delete entry on graph for artifact {}", artifactId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(parentsOfArtifact.right().value()), "");
+ resultOp = Either.right(responseFormat);
+ } else {
+
+ artifactParentsCount = parentsOfArtifact.left().value();
+ log.debug("Number of parents nodes on graph for artifact {} is {}", artifactId, artifactParentsCount);
+
+ Either<ArtifactDefinition, StorageOperationStatus> result = artifactOperation.removeArifactFromResource(parentId, artifactId, parentType, false, true);
+ isLeft = result.isLeft();
+ if (isLeft) {
+ log.debug("Artifact removed from graph {}", artifactId);
+
+ artifactDefinition = result.left().value();
+ insideEither = Either.left(result.left().value());
+ } else {
+ error = result.right().value();
+ }
+ }
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ if (isLeft) {
+ StorageOperationStatus deleteIfNotOnGraph = StorageOperationStatus.OK;
+ if (artifactParentsCount < 2) {
+ log.debug("Number of parent nodes is 1. Need to delete from ES {}", artifactId);
+ deleteIfNotOnGraph = deleteIfNotOnGraph(artifactId, artifactDefinition.getEsId(), true);
+ }
+ if (deleteIfNotOnGraph.equals(StorageOperationStatus.OK)) {
+ if (artifactDefinition.getMandatory() || artifactDefinition.getServiceApi()) {
+ log.debug("Artifact is mandatory or service API. Clean all fields for {}", artifactId);
+ artifactDefinition.setEsId("");
+ artifactDefinition.setArtifactName("");
+ artifactDefinition.setDescription("");
+ artifactDefinition.setApiUrl("");
+ artifactDefinition.setArtifactChecksum("");
+ setDefaultArtifactTimeout(artifactDefinition.getArtifactGroupType(), artifactDefinition);
+ artifactDefinition.setArtifactUUID("");
+ long time = System.currentTimeMillis();
+ artifactDefinition.setPayloadUpdateDate(time);
+ artifactDefinition.setHeatParameters(null);
+ artifactDefinition.setHeatParamsUpdateDate(null);
+ Either<ArtifactDefinition, StorageOperationStatus> resStatus = null;
+ if (artifactParentsCount < 2) {
+ log.debug("Only one parent , clean existing placeholder for {}", artifactId);
+ resStatus = artifactOperation.updateArifactOnResource(artifactDefinition, parentId, artifactId, parentType, true);
+ } else {
+ log.debug("more than one parent , create new placeholder for {}", artifactId);
+ artifactDefinition.setUniqueId(null);
+ resStatus = artifactOperation.addArifactToComponent(artifactDefinition, parentId, parentType, true, true);
+ }
+ if (resStatus.isRight()) {
+ log.debug("Failed to clean placeholder for {}", artifactId);
+ responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(resStatus.right().value()), artifactDefinition.getArtifactDisplayName());
+ resultOp = Either.right(responseFormat);
+ } else {
+ log.debug("Placeholder was cleaned for {}", artifactId);
+
+ ArtifactDefinition artifactUfterChange = resStatus.left().value();
+
+ insideEither = Either.left(artifactUfterChange);
+ resultOp = Either.left(insideEither);
+ }
+ } else {
+ log.debug("Artifact isn't mandatory/service API. Removed. {}", artifactId);
+ resultOp = Either.left(insideEither);
+ }
+
+ } else {
+ log.debug("failed to delete artifact from ES {} status {}", artifactId, deleteIfNotOnGraph);
+ responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(deleteIfNotOnGraph), artifactDefinition.getArtifactDisplayName());
+ resultOp = Either.right(responseFormat);
+ }
+ } else {
+ log.debug("Failed to delete entry on graph for artifact {}", artifactId);
+ responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(error), "");
+ resultOp = Either.right(responseFormat);
+ }
+ handleAuditing(auditingAction, parent, parentId, user, artifactDefinition, null, artifactId, responseFormat, componentType, null);
+ return resultOp;
+ } finally {
+ if (shouldLock) {
+ unlockComponent(resultOp, parent, inTransaction);
+ }
+ }
+
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDownload(String componentId, String artifactId, User user, AuditingActionEnum auditingAction, ComponentTypeEnum componentType,
+ org.openecomp.sdc.be.model.Component parent, boolean shouldLock, boolean inTransaction) {
+ Either<ArtifactDefinition, StorageOperationStatus> artifactById = artifactOperation.getArtifactById(artifactId, false);
+ if (artifactById.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(artifactById.right().value());
+ log.debug("Error when getting artifact info by id{}, error: {}", artifactId, actionStatus.name());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(actionStatus, "");
+ handleAuditing(auditingAction, parent, componentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ ArtifactDefinition artifactDefinition = artifactById.left().value();
+ if (artifactDefinition == null) {
+ log.debug("Empty artifact definition returned from DB by artifact id {}", artifactId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, "");
+ handleAuditing(auditingAction, parent, componentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ Either<ArtifactDefinition, Operation> insideEither = Either.left(artifactDefinition);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleAuditing(auditingAction, parent, componentId, user, artifactDefinition, null, artifactId, responseFormat, componentType, null);
+ return Either.left(insideEither);
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> fetchCurrentArtifact(ArtifactOperation operation, String artifactId) {
+ Either<ArtifactDefinition, StorageOperationStatus> artifactById = artifactOperation.getArtifactById(artifactId, true);
+ if (!operation.equals(ArtifactOperation.Create) && artifactById.isRight()) {
+ // in case of update artifact must be
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeArtifactMissingError, "Artifact Update / Upload", artifactId);
+ BeEcompErrorManager.getInstance().logBeArtifactMissingError("Artifact Update / Upload", artifactId);
+ log.debug("Failed to fetch artifact {}. error: {}", artifactId, artifactById.right().value());
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(artifactById.right().value()), artifactId));
+ }
+ if (operation.equals(ArtifactOperation.Create) && artifactById.isLeft()) {
+ log.debug("Artifact {} already exist", artifactId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_EXIST, artifactById.left().value().getArtifactLabel()));
+ }
+ ArtifactDefinition currentArtifactInfo = null;
+ if (artifactById.isLeft()) {
+ // get previous value
+ currentArtifactInfo = artifactById.left().value();
+ }
+ return Either.left(currentArtifactInfo);
+ }
+
+ private Either<ActionStatus, ResponseFormat> handleArtifactLabel(String componentId, ArtifactOperation operation, String artifactId, ArtifactDefinition artifactInfo, String interfaceName, String operationName,
+ ArtifactDefinition currentArtifactInfo, ComponentTypeEnum componentType, boolean inTransaction) {
+ String artifactLabel = artifactInfo.getArtifactLabel();
+
+ if (operationName == null && (artifactInfo.getArtifactLabel() == null || artifactInfo.getArtifactLabel().isEmpty())) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingArtifactInformationError, "Artifact Update / Upload", "artifactLabel");
+ BeEcompErrorManager.getInstance().logBeMissingArtifactInformationError("Artifact Update / Upload", "artifactLabel");
+ log.debug("missing artifact logical name for component {}", componentId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_LABEL));
+ }
+ if (operation.equals(ArtifactOperation.Create) && !artifactInfo.getMandatory()) {
+
+ if (operationName != null) {
+ if (artifactInfo.getArtifactLabel() != null && !operationName.equals(artifactInfo.getArtifactLabel())) {
+ log.debug("artifact label cannot be set {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED));
+ } else {
+ artifactLabel = operationName;
+ }
+ }
+ String displayName = artifactInfo.getArtifactDisplayName();
+ if (displayName == null || displayName.isEmpty())
+ displayName = artifactLabel;
+ displayName = ValidationUtils.cleanArtifactDisplayName(displayName);
+ artifactInfo.setArtifactDisplayName(displayName);
+
+ if (!ValidationUtils.validateArtifactLabel(artifactLabel)) {
+ log.debug("Invalid format form Artifact label : {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ artifactLabel = ValidationUtils.normalizeArtifactLabel(artifactLabel);
+
+ if (artifactLabel.isEmpty()) {
+ log.debug("missing normalized artifact logical name for component {}", componentId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_LABEL));
+ }
+
+ if (!ValidationUtils.validateArtifactLabelLength(artifactLabel)) {
+ log.debug("Invalid lenght form Artifact label : {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, ARTIFACT_LABEL, String.valueOf(ValidationUtils.ARTIFACT_LABEL_LENGTH)));
+ }
+ if (!validateLabelUniqueness(componentId, artifactLabel, componentType, inTransaction)) {
+ log.debug("Non unique Artifact label : {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_EXIST, artifactLabel));
+ }
+ }
+ artifactInfo.setArtifactLabel(artifactLabel);
+
+ if (currentArtifactInfo != null && !currentArtifactInfo.getArtifactLabel().equals(artifactInfo.getArtifactLabel())) {
+ log.info("Logical artifact's name cannot be changed {}", artifactId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED));
+ }
+ return Either.left(ActionStatus.OK);
+ }
+
+ private boolean validateLabelUniqueness(String parentId, String artifactLabel, ComponentTypeEnum componentType, boolean inTransaction) {
+ boolean isUnique = true;
+ NodeTypeEnum parentType;
+ if (componentType.equals(ComponentTypeEnum.RESOURCE)) {
+ parentType = NodeTypeEnum.Resource;
+ } else {
+ parentType = NodeTypeEnum.Service;
+ }
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifacts = artifactOperation.getArtifacts(parentId, parentType, inTransaction);
+ if (artifacts.isLeft()) {
+ for (String label : artifacts.left().value().keySet()) {
+ if (label.equals(artifactLabel)) {
+ isUnique = false;
+ break;
+ }
+ }
+ }
+ if (componentType.equals(ComponentTypeEnum.RESOURCE)) {
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> allInterfacesOfResource = interfaceLifecycleOperation.getAllInterfacesOfResource(parentId, true, inTransaction);
+ if (allInterfacesOfResource.isLeft()) {
+ for (InterfaceDefinition interace : allInterfacesOfResource.left().value().values()) {
+ for (Operation operation : interace.getOperations().values()) {
+ if (operation.getImplementation() != null && operation.getImplementation().getArtifactLabel().equals(artifactLabel)) {
+ isUnique = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return isUnique;
+ }
+
+ // ***************************************************************
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> createArtifact(org.openecomp.sdc.be.model.Component parent, String parentId, ArtifactDefinition artifactInfo, byte[] decodedPayload, User user,
+ ComponentTypeEnum componentTypeEnum, AuditingActionEnum auditingActionEnum, String interfaceType, String operationName) {
+
+ ESArtifactData artifactData = createEsArtifactData(artifactInfo, decodedPayload);
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+ Either<ArtifactDefinition, Operation> insideEither = null;
+
+ if (artifactData == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Upload Artifact");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Upload Artifact");
+ log.debug("Failed to create artifact object for ES.");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ handleAuditing(auditingActionEnum, parent, parentId, user, artifactInfo, null, null, responseFormat, componentTypeEnum, null);
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+
+ }
+ // set on graph object id of artifact in ES!
+ artifactInfo.setEsId(artifactData.getId());
+
+ boolean isLeft = false;
+ String artifactUniqueId = null;
+ ArtifactDefinition artifactDefinition = null;
+ StorageOperationStatus error = null;
+ if (interfaceType != null && operationName != null) {
+ // lifecycle artifact
+ Operation operation = convertToOperation(artifactInfo, operationName);
+
+ Either<Operation, StorageOperationStatus> result = interfaceLifecycleOperation.updateInterfaceOperation(parentId, interfaceType, operationName, operation);
+
+ isLeft = result.isLeft();
+ if (isLeft) {
+ artifactUniqueId = result.left().value().getImplementation().getUniqueId();
+ artifactDefinition = result.left().value().getImplementation();
+
+ insideEither = Either.right(result.left().value());
+ resultOp = Either.left(insideEither);
+ } else {
+ error = result.right().value();
+ }
+ } else {
+ // information/deployment/api aritfacts
+ log.debug("Try to create entry on graph");
+ NodeTypeEnum nodeType = convertParentType(componentTypeEnum);
+ Either<ArtifactDefinition, StorageOperationStatus> result = artifactOperation.addArifactToComponent(artifactInfo, parentId, nodeType, true, true);
+
+ isLeft = result.isLeft();
+ if (isLeft) {
+ artifactUniqueId = result.left().value().getUniqueId();
+ artifactDefinition = result.left().value();
+
+ insideEither = Either.left(result.left().value());
+ resultOp = Either.left(insideEither);
+ } else {
+ error = result.right().value();
+ }
+ }
+ if (isLeft) {
+ boolean res = saveArtifacts(artifactData, parentId, false);
+ // String uniqueId = artifactDefinition.getUniqueId();
+
+ if (res) {
+ log.debug("Artifact saved into ES - {}", artifactUniqueId);
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleAuditing(auditingActionEnum, parent, parentId, user, artifactInfo, artifactUniqueId, artifactUniqueId, responseFormat, componentTypeEnum, null);
+ return resultOp;
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Upload Artifact");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Upload Artifact");
+ log.debug("Failed to save the artifact.");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ handleAuditing(auditingActionEnum, parent, parentId, user, artifactInfo, null, artifactUniqueId, responseFormat, componentTypeEnum, null);
+
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+ }
+ } else {
+ log.debug("Failed to create entry on graph for artifact {}", artifactInfo.getArtifactName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(error), artifactInfo.getArtifactDisplayName());
+ handleAuditing(auditingActionEnum, parent, parentId, user, artifactInfo, null, null, responseFormat, componentTypeEnum, null);
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+ }
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateDeploymentArtifact(Component parentComponent, String parentId, String userId, boolean isCreate, ArtifactDefinition artifactInfo, ArtifactDefinition currentArtifact, NodeTypeEnum parentType) {
+
+ Either<Boolean, ResponseFormat> result = Either.left(true);
+ Wrapper<ResponseFormat> responseWrapper = new Wrapper<ResponseFormat>();
+
+ validateArtifactTypeExists(responseWrapper, artifactInfo);
+
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.findType(artifactInfo.getArtifactType());
+
+ Map<String, DeploymentArtifactTypeConfig> resourceDeploymentArtifacts = fillDeploymentArtifactTypeConf(parentType);
+
+ if (responseWrapper.isEmpty()) {
+ validateDeploymentArtifactConf(artifactInfo, responseWrapper, artifactType, resourceDeploymentArtifacts);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ // Common code for all types
+ // not allowed to change artifactType
+ if (!isCreate) {
+ Either<Boolean, ResponseFormat> validateServiceApiType = validateArtifactTypeNotChanged(artifactInfo, currentArtifact);
+ if (validateServiceApiType.isRight()) {
+ responseWrapper.setInnerElement(validateServiceApiType.right().value());
+ }
+ }
+ }
+ if (responseWrapper.isEmpty()) {
+ if (parentType.equals(NodeTypeEnum.Resource)) {
+ // if (parentComponent instanceof Resource) {
+ Resource resource = (Resource) parentComponent;
+ ResourceTypeEnum resourceType = resource.getResourceType();
+ DeploymentArtifactTypeConfig config = resourceDeploymentArtifacts.get(artifactType.getType());
+ if (config == null) {
+ responseWrapper.setInnerElement(ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED, artifactInfo.getArtifactType()));
+ } else {
+ List<String> myList = config.getValidForResourceTypes();
+ Either<Boolean, ResponseFormat> either = validateResourceType(resourceType, artifactInfo, myList);
+ if (either.isRight()) {
+ responseWrapper.setInnerElement(either.right().value());
+ }
+ }
+ }
+ }
+ if (responseWrapper.isEmpty()) {
+ validateFileExtension(responseWrapper, () -> getDeploymentArtifactTypeConfig(parentType, artifactType), artifactInfo, parentType, artifactType);
+ }
+
+ if (responseWrapper.isEmpty() && !NodeTypeEnum.ResourceInstance.equals(parentType)) {
+ String artifactName = artifactInfo.getArtifactName();
+ if (isCreate || !artifactName.equalsIgnoreCase(currentArtifact.getArtifactName())) {
+ validateSingleDeploymentArtifactName(responseWrapper, artifactName, parentComponent, parentType);
+ }
+ }
+
+ if (responseWrapper.isEmpty()) {
+ switch (artifactType) {
+ case HEAT:
+ case HEAT_VOL:
+ case HEAT_NET: {
+ result = validateHeatDeploymentArtifact(parentComponent, userId, isCreate, artifactInfo, currentArtifact, parentType);
+ break;
+ }
+ case HEAT_ENV: {
+ result = validateHeatEnvDeploymentArtifact(parentComponent, parentId, userId, isCreate, artifactInfo, parentType);
+ artifactInfo.setTimeout(NON_HEAT_TIMEOUT);
+ break;
+ }
+ case DCAE_INVENTORY_TOSCA:
+ case DCAE_INVENTORY_JSON:
+ case DCAE_INVENTORY_POLICY:
+ // Validation is done in handle payload.
+ case DCAE_INVENTORY_DOC:
+ case DCAE_INVENTORY_BLUEPRINT:
+ case DCAE_INVENTORY_EVENT:
+ // No specific validation
+ default: {
+ artifactInfo.setTimeout(NON_HEAT_TIMEOUT);
+ }
+ }
+
+ }
+
+ if (!responseWrapper.isEmpty()) {
+ result = Either.right(responseWrapper.getInnerElement());
+ }
+ return result;
+ }
+
+ private void validateDeploymentArtifactConf(ArtifactDefinition artifactInfo, Wrapper<ResponseFormat> responseWrapper, ArtifactTypeEnum artifactType, Map<String, DeploymentArtifactTypeConfig> resourceDeploymentArtifacts) {
+ if ((resourceDeploymentArtifacts == null) || !resourceDeploymentArtifacts.containsKey(artifactType.name())) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED, artifactInfo.getArtifactType());
+ responseWrapper.setInnerElement(responseFormat);
+ log.debug("Artifact Type: {} Not found !", artifactInfo.getArtifactType());
+ }
+ }
+
+ private Map<String, DeploymentArtifactTypeConfig> fillDeploymentArtifactTypeConf(NodeTypeEnum parentType) {
+ Map<String, DeploymentArtifactTypeConfig> resourceDeploymentArtifacts = null;
+ if (parentType.equals(NodeTypeEnum.Resource)) {
+ resourceDeploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceDeploymentArtifacts();
+ } else if (parentType.equals(NodeTypeEnum.ResourceInstance)) {
+ resourceDeploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceInstanceDeploymentArtifacts();
+ } else {
+ resourceDeploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getServiceDeploymentArtifacts();
+ }
+ return resourceDeploymentArtifacts;
+ }
+
+ public void validateArtifactTypeExists(Wrapper<ResponseFormat> responseWrapper, ArtifactDefinition artifactInfo) {
+ ArtifactTypeEnum artifactType = ArtifactTypeEnum.findType(artifactInfo.getArtifactType());
+ if (artifactType == null) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ responseWrapper.setInnerElement(responseFormat);
+ log.debug("Artifact Type: {} Not found !", artifactInfo.getArtifactType());
+ }
+ }
+
+ private DeploymentArtifactTypeConfig getDeploymentArtifactTypeConfig(NodeTypeEnum parentType, ArtifactTypeEnum artifactType) {
+ DeploymentArtifactTypeConfig retConfig = null;
+ String fileType = artifactType.getType();
+ if (parentType.equals(NodeTypeEnum.Resource)) {
+ retConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceDeploymentArtifacts().get(fileType);
+ } else if (parentType.equals(NodeTypeEnum.Service)) {
+ retConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getServiceDeploymentArtifacts().get(fileType);
+ } else if (parentType.equals(NodeTypeEnum.ResourceInstance)) {
+ retConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceInstanceDeploymentArtifacts().get(fileType);
+ }
+ return retConfig;
+ }
+
+ private Either<Boolean, ResponseFormat> extractHeatParameters(ArtifactDefinition artifactInfo) {
+ // extract heat parameters
+ if (artifactInfo.getPayloadData() != null) {
+ String heatDecodedPayload = GeneralUtility.isBase64Encoded(artifactInfo.getPayloadData()) ? new String(Base64.decodeBase64(artifactInfo.getPayloadData())) : new String(artifactInfo.getPayloadData());
+ Either<List<HeatParameterDefinition>, ResultStatusEnum> heatParameters = ImportUtils.getHeatParamsWithoutImplicitTypes(heatDecodedPayload, artifactInfo.getArtifactType());
+ if (heatParameters.isRight() && (!heatParameters.right().value().equals(ResultStatusEnum.ELEMENT_NOT_FOUND))) {
+ log.info("failed to parse heat parameters ");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT, artifactInfo.getArtifactType());
+ return Either.right(responseFormat);
+ } else if (heatParameters.isLeft() && heatParameters.left().value() != null) {
+ artifactInfo.setHeatParameters(heatParameters.left().value());
+ }
+ }
+ return Either.left(true);
+
+ }
+
+ // Valid extension
+ public void validateFileExtension(Wrapper<ResponseFormat> responseWrapper, IDeploymentArtifactTypeConfigGetter deploymentConfigGetter, ArtifactDefinition artifactInfo, NodeTypeEnum parentType, ArtifactTypeEnum artifactType) {
+ String fileType = artifactType.getType();
+ List<String> acceptedTypes = null;
+ DeploymentArtifactTypeConfig deploymentAcceptedTypes = deploymentConfigGetter.getDeploymentArtifactConfig();
+ if (!parentType.equals(NodeTypeEnum.Resource) && !parentType.equals(NodeTypeEnum.Service) && !parentType.equals(NodeTypeEnum.ResourceInstance)) {
+ log.debug("parent type of artifact can be either resource or service");
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return;
+ }
+
+ if (deploymentAcceptedTypes == null) {
+ log.debug("parent type of artifact can be either resource or service");
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED, artifactInfo.getArtifactType()));
+ return;
+ } else {
+ acceptedTypes = deploymentAcceptedTypes.getAcceptedTypes();
+ }
+ /*
+ * No need to check specific types. In case there are no acceptedTypes in configuration, then any type is accepted.
+ *
+ * if ((!artifactType.equals(ArtifactTypeEnum.OTHER) && !artifactType.equals(ArtifactTypeEnum.HEAT_ARTIFACT )) && (acceptedTypes == null || acceptedTypes.isEmpty()) ) { log.debug( "No accepted types found for type {}, parent type {}",
+ * fileType, parentType.getName()); String methodName = new Object() { }.getClass().getEnclosingMethod().getName(); String configEntryMissing = (parentType.equals(NodeTypeEnum.Resource)) ? "resourceDeploymentArtifacts:" + fileType :
+ * "serviceDeploymentArtifacts:" + fileType; BeEcompErrorManager.getInstance().processEcompError(EcompErrorName. BeMissingConfigurationError, methodName, configEntryMissing); BeEcompErrorManager.getInstance().logBeMissingConfigurationError(
+ * methodName, configEntryMissing); responseWrapper.setInnerElement(componentsUtils.getResponseFormat( ActionStatus.GENERAL_ERROR)); return; }
+ */
+
+ String artifactName = artifactInfo.getArtifactName();
+ String fileExtension = GeneralUtility.getFilenameExtension(artifactName);
+ // Pavel - File extension validation is case-insensitive - Ella,
+ // 21/02/2016
+ if (acceptedTypes != null && !acceptedTypes.isEmpty() && !acceptedTypes.contains(fileExtension.toLowerCase())) {
+ log.debug("File extension \"{}\" is not allowed for {} which is of type:{}", fileExtension, artifactName, fileType);
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.WRONG_ARTIFACT_FILE_EXTENSION, fileType));
+ return;
+ }
+ }
+
+ private Either<Boolean, ResponseFormat> validateHeatEnvDeploymentArtifact(Component parentComponent, String parentId, String userId, boolean isCreate, ArtifactDefinition artifactInfo, NodeTypeEnum parentType) {
+
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<ResponseFormat>();
+ Wrapper<ArtifactDefinition> heatMDWrapper = new Wrapper<ArtifactDefinition>();
+ Wrapper<byte[]> payloadWrapper = new Wrapper<>();
+
+ if (errorWrapper.isEmpty()) {
+ validateValidYaml(errorWrapper, artifactInfo);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Validate Heat Exist
+ validateHeatExist(artifactInfo.getUniqueId(), errorWrapper, heatMDWrapper, getDeploymentArtifacts(parentComponent, parentType, parentId));
+ }
+
+ if (errorWrapper.isEmpty() && isCreate) {
+ // Validate Only Single HeatEnv Artifact
+ validateSingleArtifactType(errorWrapper, ArtifactTypeEnum.HEAT_ENV, parentComponent, parentType, parentId);
+ }
+
+ if (errorWrapper.isEmpty() && !heatMDWrapper.isEmpty()) {
+ fillArtifactPayloadValidation(errorWrapper, payloadWrapper, heatMDWrapper.getInnerElement());
+ }
+
+ if (errorWrapper.isEmpty() && !heatMDWrapper.isEmpty()) {
+ validateEnvVsHeat(errorWrapper, artifactInfo, heatMDWrapper.getInnerElement(), payloadWrapper.getInnerElement());
+ }
+
+ // Init Response
+ Either<Boolean, ResponseFormat> eitherResponse;
+ if (errorWrapper.isEmpty()) {
+ eitherResponse = Either.left(true);
+ } else {
+ eitherResponse = Either.right(errorWrapper.getInnerElement());
+ }
+ return eitherResponse;
+ }
+
+ public void fillArtifactPayloadValidation(Wrapper<ResponseFormat> errorWrapper, Wrapper<byte[]> payloadWrapper, ArtifactDefinition artifactDefinition) {
+ if (artifactDefinition.getPayloadData() == null || artifactDefinition.getPayloadData().length == 0) {
+ Either<Boolean, ResponseFormat> fillArtifactPayload = fillArtifactPayload(payloadWrapper, artifactDefinition);
+ if (fillArtifactPayload.isRight()) {
+ errorWrapper.setInnerElement(fillArtifactPayload.right().value());
+ log.debug("Error getting payload for artifact:{}", artifactDefinition.getArtifactName());
+ }
+ } else {
+ payloadWrapper.setInnerElement(artifactDefinition.getPayloadData());
+ }
+ }
+
+ public Either<Boolean, ResponseFormat> fillArtifactPayload(Wrapper<byte[]> payloadWrapper, ArtifactDefinition artifactMD) {
+ Either<Boolean, ResponseFormat> result = Either.left(true);
+ Either<ESArtifactData, CassandraOperationStatus> eitherArtifactData = artifactCassandraDao.getArtifact(artifactMD.getEsId());
+ // Either<ESArtifactData, ResourceUploadStatus> eitherArtifactData =
+ // esCatalogDao.getArtifact(artifactMD.getEsId());
+ if (eitherArtifactData.isLeft()) {
+ byte[] data = eitherArtifactData.left().value().getDataAsArray();
+ if (!GeneralUtility.isBase64Encoded(data)) {
+ data = Base64.encodeBase64(data);
+ }
+ payloadWrapper.setInnerElement(data);
+ } else {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertCassandraStatusToStorageStatus(eitherArtifactData.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus));
+ result = Either.right(responseFormat);
+ }
+ return result;
+
+ }
+
+ @SuppressWarnings("unchecked")
+ private void validateEnvVsHeat(Wrapper<ResponseFormat> errorWrapper, ArtifactDefinition envArtifact, ArtifactDefinition heatArtifact, byte[] heatPayloadData) {
+
+ String envPayload = (GeneralUtility.isBase64Encoded(envArtifact.getPayloadData())) ? new String(Base64.decodeBase64(envArtifact.getPayloadData())) : new String(envArtifact.getPayloadData());
+ Map<String, Object> heatEnvToscaJson = (Map<String, Object>) new Yaml().load(envPayload);
+
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(heatPayloadData)) ? new String(Base64.decodeBase64(heatPayloadData)) : new String(heatPayloadData);
+ Map<String, Object> heatToscaJson = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+
+ Either<Map<String, Object>, ResultStatusEnum> eitherHeatEnvProperties = ImportUtils.findFirstToscaMapElement(heatEnvToscaJson, ToscaTagNamesEnum.PARAMETERS);
+ Either<Map<String, Object>, ResultStatusEnum> eitherHeatProperties = ImportUtils.findFirstToscaMapElement(heatToscaJson, ToscaTagNamesEnum.PARAMETERS);
+ if (eitherHeatEnvProperties.isRight()) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.CORRUPTED_FORMAT, "Heat Env");
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Invalid heat env format for file:{}", envArtifact.getArtifactName());
+ } else if (eitherHeatProperties.isRight()) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.MISMATCH_HEAT_VS_HEAT_ENV, envArtifact.getArtifactName(), heatArtifact.getArtifactName());
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Validation of heat_env for artifact:{} vs heat artifact for artifact :{} failed", envArtifact.getArtifactName(), heatArtifact.getArtifactName());
+ } else {
+ Set<String> heatPropertiesKeys = eitherHeatProperties.left().value().keySet();
+ Set<String> heatEnvPropertiesKeys = eitherHeatEnvProperties.left().value().keySet();
+ heatEnvPropertiesKeys.removeAll(heatPropertiesKeys);
+ if (heatEnvPropertiesKeys.size() > 0) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.MISMATCH_HEAT_VS_HEAT_ENV, envArtifact.getArtifactName(), heatArtifact.getArtifactName());
+ errorWrapper.setInnerElement(responseFormat);
+ }
+ }
+ }
+
+ private void validateValidYaml(Wrapper<ResponseFormat> errorWrapper, ArtifactDefinition artifactInfo) {
+ YamlToObjectConverter yamlConvertor = new YamlToObjectConverter();
+ boolean isYamlValid = yamlConvertor.isValidYaml(artifactInfo.getPayloadData());
+ if (!isYamlValid) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.INVALID_YAML, artifactInfo.getArtifactType());
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Yaml is not valid for artifact : {}", artifactInfo.getArtifactName());
+ }
+ }
+
+ public boolean isValidXml(byte[] xmlToParse) {
+ XMLReader parser = new SAXParser();
+ boolean isXmlValid = true;
+ try {
+ parser.parse(new InputSource(new ByteArrayInputStream(xmlToParse)));
+ } catch (IOException | SAXException e) {
+ isXmlValid = false;
+ }
+ return isXmlValid;
+ }
+
+ public boolean isValidJson(byte[] jsonToParse) {
+ String parsed = new String(jsonToParse);
+ try {
+ gson.fromJson(parsed, Object.class);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ public void validateSingleArtifactType(Wrapper<ResponseFormat> errorWrapper, ArtifactTypeEnum allowedArtifactType, Component parentComponent, NodeTypeEnum parentType, String parentRiId) {
+ boolean typeArtifactFound = false;
+ // Iterator<ArtifactDefinition> parentDeploymentArtifactsItr =
+ // (parentType == NodeTypeEnum.Resource) ?
+ // informationDeployedArtifactsBusinessLogic.getAllDeployableArtifacts((Resource)
+ // parentComponent).iterator()
+ // : getDeploymentArtifacts(parentComponent, parentType).iterator();
+
+ Iterator<ArtifactDefinition> parentDeploymentArtifactsItr = getDeploymentArtifacts(parentComponent, parentType, parentRiId).iterator();
+
+ while (!typeArtifactFound && parentDeploymentArtifactsItr.hasNext()) {
+ ArtifactTypeEnum foundArtifactType = ArtifactTypeEnum.findType(parentDeploymentArtifactsItr.next().getArtifactType());
+ typeArtifactFound = (foundArtifactType == allowedArtifactType);
+ }
+ if (typeArtifactFound) {
+ String parentName = parentComponent.getName();
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS, parentType.name(), parentName, allowedArtifactType.getType(), allowedArtifactType.getType());
+
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Can't upload artifact of type: {}, because another artifact of this type already exist.", allowedArtifactType.getType());
+
+ }
+ }
+
+ public void validateSingleDeploymentArtifactName(Wrapper<ResponseFormat> errorWrapper, String artifactName, Component parentComponent, NodeTypeEnum parentType) {
+ boolean artifactNameFound = false;
+ // Iterator<ArtifactDefinition> parentDeploymentArtifactsItr =
+ // (parentType == NodeTypeEnum.Resource) ?
+ // informationDeployedArtifactsBusinessLogic.getAllDeployableArtifacts((Resource)
+ // parentComponent).iterator()
+ // : getDeploymentArtifacts(parentComponent, parentType).iterator();
+
+ Iterator<ArtifactDefinition> parentDeploymentArtifactsItr = getDeploymentArtifacts(parentComponent, parentType, null).iterator();
+
+ while (!artifactNameFound && parentDeploymentArtifactsItr.hasNext()) {
+ artifactNameFound = (artifactName.equalsIgnoreCase(parentDeploymentArtifactsItr.next().getArtifactName()));
+ }
+ if (artifactNameFound) {
+ String parentName = parentComponent.getName();
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS, parentType.name(), parentName, artifactName);
+
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Can't upload artifact: {}, because another artifact with this name already exist.", artifactName);
+
+ }
+ }
+
+ private void validateHeatExist(String heatEnvId, Wrapper<ResponseFormat> errorWrapper, Wrapper<ArtifactDefinition> heatArtifactMDWrapper, Collection<ArtifactDefinition> parentDeploymentArtifacts) {
+ boolean heatFound = false;
+ Either<ArtifactDefinition, StorageOperationStatus> res = artifactOperation.getHeatArtifactByHeatEnvId(heatEnvId, true);
+ if (res.isRight()) {
+ return;
+ }
+ ArtifactDefinition heatArtifact = res.left().value();
+ Iterator<ArtifactDefinition> parentArtifactsItr = parentDeploymentArtifacts.iterator();
+ while (!heatFound && parentArtifactsItr.hasNext()) {
+ ArtifactDefinition currArtifact = parentArtifactsItr.next();
+ if (heatArtifact.getUniqueId().equals(currArtifact.getUniqueId())) {
+ heatFound = true;
+ heatArtifactMDWrapper.setInnerElement(currArtifact);
+ log.trace("In validateHeatExist found artifact {}", currArtifact);
+ /*
+ * ArtifactTypeEnum artifactType = ArtifactTypeEnum.findType(currArtifact.getArtifactType()); if(artifactType == ArtifactTypeEnum.HEAT || artifactType == ArtifactTypeEnum.HEAT_VOL || artifactType == ArtifactTypeEnum.HEAT_NET){
+ * heatFound = true; } if (heatFound) { heatArtifactMDWrapper.setInnerElement(currArtifact); }
+ */
+ }
+ }
+ if (!heatFound) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.MISSING_HEAT);
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Can't create heat env artifact because No heat Artifact exist.");
+ }
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateHeatDeploymentArtifact(Component parentComponent, String userId, boolean isCreate, ArtifactDefinition artifactInfo, ArtifactDefinition currentArtifact, NodeTypeEnum parentType) {
+ log.trace("Started HEAT pre-payload validation for artifact {}", artifactInfo.getArtifactLabel());
+ // timeout > 0 for HEAT artifacts
+ Integer timeout = artifactInfo.getTimeout();
+ Integer defaultTimeout = (isCreate) ? defaultHeatTimeout : currentArtifact.getTimeout();
+ if (timeout == null) {
+ artifactInfo.setTimeout(defaultTimeout);
+ // HEAT artifact but timeout is invalid
+ } else if (timeout < 1) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_INVALID_TIMEOUT));
+ }
+
+ // US649856 - Allow several HEAT files on Resource
+ /*
+ * if (isCreate) { Wrapper<ResponseFormat> errorWrapper = new Wrapper<>(); validateSingleArtifactType(errorWrapper, ArtifactTypeEnum.findType(artifactInfo.getArtifactType()), parentComponent, parentType); if (!errorWrapper.isEmpty()) { return
+ * Either.right(errorWrapper.getInnerElement()); } }
+ */
+
+ log.trace("Ended HEAT validation for artifact {}", artifactInfo.getArtifactLabel());
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateResourceType(ResourceTypeEnum resourceType, ArtifactDefinition artifactInfo, List<String> typeList) {
+ String listToString = (typeList != null) ? typeList.toString() : "";
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE, artifactInfo.getArtifactName(), listToString, resourceType.getValue());
+ Either<Boolean, ResponseFormat> either = Either.right(responseFormat);
+ String resourceTypeName = resourceType.name();
+ if (typeList != null && typeList.contains(resourceTypeName)) {
+ either = Either.left(true);
+ }
+ return either;
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> validateAndConvertHeatParamers(ArtifactDefinition artifactInfo, String artifactType) {
+ if (artifactInfo.getHeatParameters() != null) {
+ for (HeatParameterDefinition heatParam : artifactInfo.getHeatParameters()) {
+ String parameterType = heatParam.getType();
+ HeatParameterType heatParameterType = HeatParameterType.isValidType(parameterType);
+ String artifactTypeStr = artifactType != null ? artifactType : ArtifactTypeEnum.HEAT.getType();
+ if (heatParameterType == null) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_HEAT_PARAMETER_TYPE, artifactTypeStr, heatParam.getType());
+ return Either.right(responseFormat);
+ }
+
+ StorageOperationStatus validateAndUpdateProperty = heatParametersOperation.validateAndUpdateProperty(heatParam);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ log.debug("Heat parameter {} is invalid. Status is: {}", heatParam.getName(), validateAndUpdateProperty);
+ ActionStatus status = ActionStatus.INVALID_HEAT_PARAMETER_VALUE;
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(status, artifactTypeStr, heatParam.getType(), heatParam.getName());
+ return Either.right(responseFormat);
+ }
+ }
+ }
+ return Either.left(artifactInfo);
+ }
+
+ public List<ArtifactDefinition> getDeploymentArtifacts(Component parentComponent, NodeTypeEnum parentType, String ciId) {
+ List<ArtifactDefinition> deploymentArtifacts = new ArrayList<>();
+ if (parentComponent.getDeploymentArtifacts() != null && ciId != null) {
+ if (NodeTypeEnum.ResourceInstance == parentType) {
+ Either<ComponentInstance, ResponseFormat> getRI = getRIFromComponent(parentComponent, ciId, null, null, null);
+ if (getRI.isRight()) {
+ return deploymentArtifacts;
+ }
+ ComponentInstance ri = getRI.left().value();
+ deploymentArtifacts.addAll(ri.getDeploymentArtifacts().values());
+ } else {
+ deploymentArtifacts.addAll(parentComponent.getDeploymentArtifacts().values());
+ }
+ }
+ return deploymentArtifacts;
+ }
+
+ private void checkCreateFields(User user, ArtifactDefinition artifactInfo, ArtifactGroupTypeEnum type) {
+ // on create if null add informational to current
+ if (artifactInfo.getArtifactGroupType() == null) {
+ artifactInfo.setArtifactGroupType(type);
+ }
+ if (artifactInfo.getUniqueId() != null) {
+ log.error("artifact uniqid cannot be set ignoring");
+ }
+ artifactInfo.setUniqueId(null);
+
+ if (artifactInfo.getArtifactRef() != null) {
+ log.error("artifact ref cannot be set ignoring");
+ }
+ artifactInfo.setArtifactRef(null);
+
+ if (artifactInfo.getArtifactRepository() != null) {
+ log.error("artifact repository cannot be set ignoring");
+ }
+ artifactInfo.setArtifactRepository(null);
+
+ if (artifactInfo.getUserIdCreator() != null) {
+ log.error("creator uuid cannot be set ignoring");
+ }
+ artifactInfo.setArtifactCreator(user.getUserId());
+
+ if (artifactInfo.getUserIdLastUpdater() != null) {
+ log.error("userId of last updater cannot be set ignoring");
+ }
+ artifactInfo.setUserIdLastUpdater(user.getUserId());
+
+ if (artifactInfo.getCreatorFullName() != null) {
+ log.error("creator Full name cannot be set ignoring");
+ }
+ String fullName = user.getFirstName() + " " + user.getLastName();
+ artifactInfo.setUpdaterFullName(fullName);
+
+ if (artifactInfo.getUpdaterFullName() != null) {
+ log.error("updater Full name cannot be set ignoring");
+ }
+ artifactInfo.setUpdaterFullName(fullName);
+
+ if (artifactInfo.getCreationDate() != null) {
+ log.error("Creation Date cannot be set ignoring");
+ }
+ long time = System.currentTimeMillis();
+ artifactInfo.setCreationDate(time);
+
+ if (artifactInfo.getLastUpdateDate() != null) {
+ log.error("Last Update Date cannot be set ignoring");
+ }
+ artifactInfo.setLastUpdateDate(time);
+
+ if (artifactInfo.getEsId() != null) {
+ log.error("es id cannot be set ignoring");
+ }
+ artifactInfo.setEsId(null);
+
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> fetchCurrentArtifact(boolean isCreate, String artifactId) {
+ Either<ArtifactDefinition, StorageOperationStatus> artifactById = artifactOperation.getArtifactById(artifactId, true);
+ if (isCreate == false && artifactById.isRight()) {
+ // in case of update artifact must be
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeArtifactMissingError, "Artifact Update / Upload", artifactId);
+ BeEcompErrorManager.getInstance().logBeArtifactMissingError("Artifact Update / Upload", artifactId);
+ log.debug("Failed to fetch artifact {}. error: {}", artifactId, artifactById.right().value());
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(artifactById.right().value()), artifactId));
+ }
+ if (isCreate && artifactById.isLeft()) {
+ log.debug("Artifact {} already exist", artifactId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_EXIST, artifactById.left().value().getArtifactLabel()));
+ }
+ ArtifactDefinition currentArtifactInfo = null;
+ if (artifactById.isLeft()) {
+ // get previous value
+ currentArtifactInfo = artifactById.left().value();
+ }
+ return Either.left(currentArtifactInfo);
+ }
+
+ private Either<ActionStatus, ResponseFormat> handleArtifactLabel(String resourceId, boolean isCreate, String artifactId, ArtifactDefinition artifactInfo, String interfaceName, String operationName, ArtifactDefinition currentArtifactInfo,
+ NodeTypeEnum parentType) {
+ String artifactLabel = artifactInfo.getArtifactLabel();
+
+ if (operationName == null && (artifactInfo.getArtifactLabel() == null || artifactInfo.getArtifactLabel().isEmpty())) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingArtifactInformationError, "Artifact Update / Upload", "artifactLabel");
+ BeEcompErrorManager.getInstance().logBeMissingArtifactInformationError("Artifact Update / Upload", "artifactLabel");
+ log.debug("missing artifact logical name for component {}", resourceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_LABEL));
+ }
+ if (isCreate && !artifactInfo.getMandatory()) {
+
+ if (operationName != null) {
+ if (artifactInfo.getArtifactLabel() != null && !operationName.equals(artifactInfo.getArtifactLabel())) {
+ log.debug("artifact label cannot be set {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED));
+ } else {
+ artifactLabel = operationName;
+ }
+ }
+ String displayName = ValidationUtils.cleanArtifactDisplayName(artifactLabel);
+ artifactInfo.setArtifactDisplayName(displayName);
+
+ if (!ValidationUtils.validateArtifactLabel(artifactLabel)) {
+ log.debug("Invalid format form Artifact label : {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ artifactLabel = ValidationUtils.normalizeArtifactLabel(artifactLabel);
+
+ if (artifactLabel.isEmpty()) {
+ log.debug("missing normalized artifact logical name for component {}", resourceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_LABEL));
+ }
+
+ if (!ValidationUtils.validateArtifactLabelLength(artifactLabel)) {
+ log.debug("Invalid lenght form Artifact label : {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, ARTIFACT_LABEL, String.valueOf(ValidationUtils.ARTIFACT_LABEL_LENGTH)));
+ }
+ if (!validateLabelUniqueness(resourceId, artifactLabel, parentType)) {
+ log.debug("Non unique Artifact label : {}", artifactLabel);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_EXIST, artifactLabel));
+ }
+ }
+ artifactInfo.setArtifactLabel(artifactLabel);
+
+ if (currentArtifactInfo != null && !currentArtifactInfo.getArtifactLabel().equals(artifactInfo.getArtifactLabel())) {
+ log.info("Logical artifact's name cannot be changed {}", artifactId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED));
+ }
+ return Either.left(ActionStatus.OK);
+ }
+
+ private boolean validateLabelUniqueness(String parentId, String artifactLabel, NodeTypeEnum parentType) {
+ boolean isUnique = true;
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifacts = artifactOperation.getArtifacts(parentId, parentType, true);
+ if (artifacts.isLeft()) {
+ for (String label : artifacts.left().value().keySet()) {
+ if (label.equals(artifactLabel)) {
+ isUnique = false;
+ break;
+ }
+ }
+ }
+ if (parentType.equals(NodeTypeEnum.Resource)) {
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> allInterfacesOfResource = interfaceLifecycleOperation.getAllInterfacesOfResource(parentId, true);
+ if (allInterfacesOfResource.isLeft()) {
+ for (InterfaceDefinition interace : allInterfacesOfResource.left().value().values()) {
+ for (Operation operation : interace.getOperations().values()) {
+ if (operation.getImplementation() != null && operation.getImplementation().getArtifactLabel().equals(artifactLabel)) {
+ isUnique = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return isUnique;
+ }
+
+ private String composeArtifactId(String resourceId, String artifactId, ArtifactDefinition artifactInfo, String interfaceName, String operationName) {
+ String id = artifactId;
+ if (artifactId == null || artifactId.isEmpty()) {
+ String uniqueId = null;
+ if (interfaceName != null && operationName != null) {
+ uniqueId = UniqueIdBuilder.buildArtifactByInterfaceUniqueId(resourceId, interfaceName, operationName, artifactInfo.getArtifactLabel());
+ } else {
+ uniqueId = UniqueIdBuilder.buildPropertyUniqueId(resourceId, artifactInfo.getArtifactLabel());
+ }
+ artifactInfo.setUniqueId(uniqueId);
+ artifactInfo.setEsId(uniqueId);
+ id = uniqueId;
+ } else {
+ artifactInfo.setUniqueId(artifactId);
+ artifactInfo.setEsId(artifactId);
+ }
+ return id;
+ }
+
+ private Either<ActionStatus, ResponseFormat> validateArtifactType(String userId, ArtifactDefinition artifactInfo, NodeTypeEnum parentType) {
+ if (artifactInfo.getArtifactType() == null || artifactInfo.getArtifactType().isEmpty()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeMissingArtifactInformationError, "Artifact Upload / Update");
+ BeEcompErrorManager.getInstance().logBeMissingArtifactInformationError("Artifact Update / Upload", "artifactLabel");
+ log.debug("Missing artifact type for artifact {}", artifactInfo.getArtifactName());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_ARTIFACT_TYPE));
+ }
+
+ boolean artifactTypeExist = false;
+ Either<List<ArtifactType>, ActionStatus> allArtifactTypes = null;
+ ArtifactGroupTypeEnum artifactGroupType = artifactInfo.getArtifactGroupType();
+
+ if ((artifactGroupType != null) && artifactGroupType.equals(ArtifactGroupTypeEnum.DEPLOYMENT)) {
+ allArtifactTypes = getDeploymentArtifactTypes(userId, artifactInfo, parentType);
+ } else {
+
+ allArtifactTypes = elementOperation.getAllArtifactTypes();
+ }
+ if (allArtifactTypes.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidConfigurationError, "Artifact Upload / Update", "artifactTypes", allArtifactTypes.right().value().name());
+ BeEcompErrorManager.getInstance().logBeInvalidConfigurationError("Artifact Upload / Update", "artifactTypes", allArtifactTypes.right().value().name());
+ log.debug("Failed to retrieve list of suported artifact types. error: {}", allArtifactTypes.right().value());
+ return Either.right(componentsUtils.getResponseFormatByUserId(allArtifactTypes.right().value(), userId));
+ }
+
+ for (ArtifactType type : allArtifactTypes.left().value()) {
+ if (type.getName().equalsIgnoreCase(artifactInfo.getArtifactType())) {
+ artifactInfo.setArtifactType(artifactInfo.getArtifactType().toUpperCase());
+ artifactTypeExist = true;
+ break;
+ }
+ }
+
+ if (!artifactTypeExist) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidTypeError, "Artifact Upload / Delete / Update - Not supported artifact type", artifactInfo.getArtifactType(), "Artifact " + artifactInfo.getArtifactName());
+ BeEcompErrorManager.getInstance().logBeInvalidTypeError("Artifact Upload / Delete / Update - Not supported artifact type", artifactInfo.getArtifactType(), "Artifact " + artifactInfo.getArtifactName());
+ log.debug("Not supported artifact type = {}", artifactInfo.getArtifactType());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED, artifactInfo.getArtifactType()));
+ }
+
+ return Either.left(ActionStatus.OK);
+ }
+
+ private Either<List<ArtifactType>, ActionStatus> getDeploymentArtifactTypes(String userId, ArtifactDefinition artifactInfo, NodeTypeEnum parentType) {
+
+ Map<String, DeploymentArtifactTypeConfig> deploymentArtifacts = null;
+ List<ArtifactType> artifactTypes = new ArrayList<ArtifactType>();
+
+ if (parentType.equals(NodeTypeEnum.Service)) {
+ deploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getServiceDeploymentArtifacts();
+ } else if (parentType.equals(NodeTypeEnum.ResourceInstance)) {
+ deploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceInstanceDeploymentArtifacts();
+ } else {
+ deploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceDeploymentArtifacts();
+ }
+ if (deploymentArtifacts != null) {
+ for (String artifactType : deploymentArtifacts.keySet()) {
+ ArtifactType artifactT = new ArtifactType();
+ artifactT.setName(artifactType);
+ artifactTypes.add(artifactT);
+ }
+ return Either.left(artifactTypes);
+ } else {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateFirstUpdateHasPayload(ArtifactDefinition artifactInfo, ArtifactDefinition currentArtifact) {
+ if (currentArtifact.getEsId() == null && (artifactInfo.getPayloadData() == null || artifactInfo.getPayloadData().length == 0)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_PAYLOAD));
+ }
+ return Either.left(true);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndSetArtifactname(ArtifactDefinition artifactInfo) {
+ if (artifactInfo.getArtifactName() == null || artifactInfo.getArtifactName().isEmpty()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_ARTIFACT_NAME));
+ }
+
+ String normalizeFileName = ValidationUtils.normalizeFileName(artifactInfo.getArtifactName());
+ if (normalizeFileName == null || normalizeFileName.isEmpty()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_ARTIFACT_NAME));
+ }
+ artifactInfo.setArtifactName(normalizeFileName);
+
+ if (!ValidationUtils.validateArtifactNameLength(artifactInfo.getArtifactName())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, ARTIFACT_NAME, String.valueOf(ValidationUtils.ARTIFACT_NAME_LENGTH)));
+ }
+
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateArtifactTypeNotChanged(ArtifactDefinition artifactInfo, ArtifactDefinition currentArtifact) {
+ if (artifactInfo.getArtifactType() == null || artifactInfo.getArtifactType().isEmpty()) {
+ log.info("artifact type is missing operation ignored");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_ARTIFACT_TYPE));
+ }
+
+ if (!currentArtifact.getArtifactType().equalsIgnoreCase(artifactInfo.getArtifactType())) {
+ log.info("artifact type cannot be changed operation ignored");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ return Either.left(true);
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> validateOrSetArtifactGroupType(ArtifactDefinition artifactInfo, ArtifactDefinition currentArtifact) {
+ if (artifactInfo.getArtifactGroupType() == null) {
+ artifactInfo.setArtifactGroupType(currentArtifact.getArtifactGroupType());
+ }
+
+ else if (!currentArtifact.getArtifactGroupType().getType().equalsIgnoreCase(artifactInfo.getArtifactGroupType().getType())) {
+ log.info("artifact group type cannot be changed. operation failed");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ return Either.left(artifactInfo);
+ }
+
+ private void checkAndSetUnUpdatableFields(User user, ArtifactDefinition artifactInfo, ArtifactDefinition currentArtifact, ArtifactGroupTypeEnum type) {
+
+ // on update if null add informational to current
+ if (currentArtifact.getArtifactGroupType() == null && type != null) {
+ currentArtifact.setArtifactGroupType(type);
+ }
+
+ if (artifactInfo.getUniqueId() != null && !currentArtifact.getUniqueId().equals(artifactInfo.getUniqueId())) {
+ log.error("artifact uniqid cannot be set ignoring");
+ }
+ artifactInfo.setUniqueId(currentArtifact.getUniqueId());
+
+ if (artifactInfo.getArtifactRef() != null && !currentArtifact.getArtifactRef().equals(artifactInfo.getArtifactRef())) {
+ log.error("artifact ref cannot be set ignoring");
+ }
+ artifactInfo.setArtifactRef(currentArtifact.getArtifactRef());
+
+ if (artifactInfo.getArtifactRepository() != null && !currentArtifact.getArtifactRepository().equals(artifactInfo.getArtifactRepository())) {
+ log.error("artifact repository cannot be set ignoring");
+ }
+ artifactInfo.setArtifactRepository(currentArtifact.getArtifactRepository());
+
+ if (artifactInfo.getUserIdCreator() != null && !currentArtifact.getUserIdCreator().equals(artifactInfo.getUserIdCreator())) {
+ log.error("creator uuid cannot be set ignoring");
+ }
+ artifactInfo.setUserIdCreator(currentArtifact.getUserIdCreator());
+
+ if (artifactInfo.getArtifactCreator() != null && !currentArtifact.getArtifactCreator().equals(artifactInfo.getArtifactCreator())) {
+ log.error("artifact creator cannot be set ignoring");
+ }
+ artifactInfo.setArtifactCreator(currentArtifact.getArtifactCreator());
+
+ if (artifactInfo.getUserIdLastUpdater() != null && !currentArtifact.getUserIdLastUpdater().equals(artifactInfo.getUserIdLastUpdater())) {
+ log.error("userId of last updater cannot be set ignoring");
+ }
+ artifactInfo.setUserIdLastUpdater(user.getUserId());
+
+ if (artifactInfo.getCreatorFullName() != null && !currentArtifact.getCreatorFullName().equals(artifactInfo.getCreatorFullName())) {
+ log.error("creator Full name cannot be set ignoring");
+ }
+ artifactInfo.setCreatorFullName(currentArtifact.getCreatorFullName());
+
+ if (artifactInfo.getUpdaterFullName() != null && !currentArtifact.getUpdaterFullName().equals(artifactInfo.getUpdaterFullName())) {
+ log.error("updater Full name cannot be set ignoring");
+ }
+ String fullName = user.getFirstName() + " " + user.getLastName();
+ artifactInfo.setUpdaterFullName(fullName);
+
+ if (artifactInfo.getCreationDate() != null && !currentArtifact.getCreationDate().equals(artifactInfo.getCreationDate())) {
+ log.error("Creation Date cannot be set ignoring");
+ }
+ artifactInfo.setCreationDate(currentArtifact.getCreationDate());
+
+ if (artifactInfo.getLastUpdateDate() != null && !currentArtifact.getLastUpdateDate().equals(artifactInfo.getLastUpdateDate())) {
+ log.error("Last Update Date cannot be set ignoring");
+ }
+ long time = System.currentTimeMillis();
+ artifactInfo.setLastUpdateDate(time);
+
+ if (artifactInfo.getEsId() != null && !currentArtifact.getEsId().equals(artifactInfo.getEsId())) {
+ log.error("es id cannot be set ignoring");
+ }
+ artifactInfo.setEsId(currentArtifact.getUniqueId());
+
+ if (artifactInfo.getArtifactDisplayName() != null && !currentArtifact.getArtifactDisplayName().equals(artifactInfo.getArtifactDisplayName())) {
+ log.error(" Artifact Display Name cannot be set ignoring");
+ }
+ artifactInfo.setArtifactDisplayName(currentArtifact.getArtifactDisplayName());
+
+ if (artifactInfo.getServiceApi() != null && !currentArtifact.getServiceApi().equals(artifactInfo.getServiceApi())) {
+ log.debug("serviceApi cannot be set. ignoring.");
+ }
+ artifactInfo.setServiceApi(currentArtifact.getServiceApi());
+
+ if (artifactInfo.getArtifactGroupType() != null && !currentArtifact.getArtifactGroupType().equals(artifactInfo.getArtifactGroupType())) {
+ log.debug("artifact group cannot be set. ignoring.");
+ }
+ artifactInfo.setArtifactGroupType(currentArtifact.getArtifactGroupType());
+
+ artifactInfo.setArtifactVersion(currentArtifact.getArtifactVersion());
+
+ if (artifactInfo.getArtifactUUID() != null && !artifactInfo.getArtifactUUID().isEmpty() && !currentArtifact.getArtifactUUID().equals(artifactInfo.getArtifactUUID())) {
+ log.debug("artifact UUID cannot be set. ignoring.");
+ }
+ artifactInfo.setArtifactUUID(currentArtifact.getArtifactUUID());
+
+ if ((artifactInfo.getHeatParameters() != null) && (currentArtifact.getHeatParameters() != null) && !artifactInfo.getHeatParameters().isEmpty() && !currentArtifact.getHeatParameters().isEmpty()) {
+ checkAndSetUnupdatableHeatParams(artifactInfo.getHeatParameters(), currentArtifact.getHeatParameters());
+ }
+ }
+
+ private void checkAndSetUnupdatableHeatParams(List<HeatParameterDefinition> heatParameters, List<HeatParameterDefinition> currentParameters) {
+
+ Map<String, HeatParameterDefinition> currentParametersMap = getMapOfParameters(currentParameters);
+ for (HeatParameterDefinition parameter : heatParameters) {
+ HeatParameterDefinition currentParam = currentParametersMap.get(parameter.getUniqueId());
+
+ if (currentParam != null) {
+
+ if (parameter.getName() != null && !parameter.getName().equalsIgnoreCase(currentParam.getName())) {
+ log.debug("heat parameter name cannot be updated ({}). ignoring.", parameter.getName());
+ parameter.setName(currentParam.getName());
+ }
+ if (parameter.getDefaultValue() != null && !parameter.getDefaultValue().equalsIgnoreCase(currentParam.getDefaultValue())) {
+ log.debug("heat parameter defaultValue cannot be updated ({}). ignoring.", parameter.getDefaultValue());
+ parameter.setDefaultValue(currentParam.getDefaultValue());
+ }
+ if (parameter.getType() != null && !parameter.getType().equalsIgnoreCase(currentParam.getType())) {
+ log.debug("heat parameter type cannot be updated ({}). ignoring.", parameter.getType());
+ parameter.setType(currentParam.getType());
+ }
+ if (parameter.getDescription() != null && !parameter.getDescription().equalsIgnoreCase(currentParam.getDescription())) {
+ log.debug("heat parameter description cannot be updated ({}). ignoring.", parameter.getDescription());
+ parameter.setDescription(currentParam.getDescription());
+ }
+
+ // check and set current value
+ if ((parameter.getCurrentValue() == null) && (currentParam.getDefaultValue() != null)) {
+ log.debug("heat parameter current value is null. set it to default value {}). ignoring.", parameter.getDefaultValue());
+ parameter.setCurrentValue(currentParam.getDefaultValue());
+ }
+ }
+ }
+ }
+
+ private Map<String, HeatParameterDefinition> getMapOfParameters(List<HeatParameterDefinition> currentParameters) {
+
+ Map<String, HeatParameterDefinition> currentParamsMap = new HashMap<String, HeatParameterDefinition>();
+ for (HeatParameterDefinition param : currentParameters) {
+ currentParamsMap.put(param.getUniqueId(), param);
+ }
+ return currentParamsMap;
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndServiceApiUrl(ArtifactDefinition artifactInfo) {
+ if (!ValidationUtils.validateStringNotEmpty(artifactInfo.getApiUrl())) {
+ log.debug("Artifact url cannot be empty.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_URL));
+ }
+ artifactInfo.setApiUrl(artifactInfo.getApiUrl().toLowerCase());
+
+ if (!ValidationUtils.validateUrl(artifactInfo.getApiUrl())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_SERVICE_API_URL));
+ }
+ if (!ValidationUtils.validateUrlLength(artifactInfo.getApiUrl())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, ARTIFACT_URL, String.valueOf(ValidationUtils.API_URL_LENGTH)));
+ }
+
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndCleanDescription(ArtifactDefinition artifactInfo) {
+ if (artifactInfo.getDescription() == null || artifactInfo.getDescription().isEmpty()) {
+ log.debug("Artifact description cannot be empty.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_DESCRIPTION));
+ }
+ String description = artifactInfo.getDescription();
+ description = ValidationUtils.removeNoneUtf8Chars(description);
+ description = ValidationUtils.normaliseWhitespace(description);
+ description = ValidationUtils.stripOctets(description);
+ description = ValidationUtils.removeHtmlTagsOnly(description);
+ if (!ValidationUtils.validateIsEnglish(description)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ if (!ValidationUtils.validateLength(description, ValidationUtils.ARTIFACT_DESCRIPTION_MAX_LENGTH)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, ARTIFACT_DESCRIPTION, String.valueOf(ValidationUtils.ARTIFACT_DESCRIPTION_MAX_LENGTH)));
+ }
+ artifactInfo.setDescription(description);
+ return Either.left(true);
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> updateArtifactFlow(org.openecomp.sdc.be.model.Component parent, String parentId, String artifactId, ArtifactDefinition artifactInfo, User user, byte[] decodedPayload,
+ ComponentTypeEnum componentType, AuditingActionEnum auditingAction, String interfaceType, String operationName) {
+ ESArtifactData artifactData = createEsArtifactData(artifactInfo, decodedPayload);
+ String prevArtifactId = null;
+ String currArtifactId = artifactId;
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+ Either<ArtifactDefinition, Operation> insideEither = null;
+
+ if (artifactData == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Update Artifact");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Update Artifact");
+ log.debug("Failed to create artifact object for ES.");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+ }
+ log.debug("Try to update entry on graph");
+ String artifactUniqueId = null;
+ ArtifactDefinition artifactDefinition = null;
+ StorageOperationStatus error = null;
+
+ boolean isLeft = false;
+ if (interfaceType != null && operationName != null) {
+ // lifecycle artifact
+ Operation operation = convertToOperation(artifactInfo, operationName);
+
+ Either<Operation, StorageOperationStatus> result = interfaceLifecycleOperation.updateInterfaceOperation(parentId, interfaceType, operationName, operation);
+
+ isLeft = result.isLeft();
+ if (isLeft) {
+ artifactUniqueId = result.left().value().getUniqueId();
+ artifactDefinition = result.left().value().getImplementation();
+
+ insideEither = Either.right(result.left().value());
+ resultOp = Either.left(insideEither);
+ } else {
+ error = result.right().value();
+ }
+ } else {
+
+ NodeTypeEnum convertParentType = convertParentType(componentType);
+ Either<ArtifactDefinition, StorageOperationStatus> result = artifactOperation.updateArifactOnResource(artifactInfo, parentId, artifactId, convertParentType, true);
+ isLeft = result.isLeft();
+ if (isLeft) {
+ artifactUniqueId = result.left().value().getUniqueId();
+ artifactDefinition = result.left().value();
+
+ insideEither = Either.left(result.left().value());
+ resultOp = Either.left(insideEither);
+ } else {
+ error = result.right().value();
+ }
+ }
+ if (isLeft) {
+ log.debug("Enty on graph is updated. Update artifact in ES");
+ boolean res;
+ // Changing previous and current artifactId for auditing
+ prevArtifactId = currArtifactId;
+ currArtifactId = artifactDefinition.getUniqueId();
+
+ if (!artifactDefinition.getUniqueId().equals(artifactId)) {
+ // different ids ==> artifact node was cloned .
+ // if no data in request get from ES
+ if (decodedPayload == null) {
+ if (!artifactDefinition.getMandatory() || artifactDefinition.getEsId() != null) {
+ Either<ESArtifactData, CassandraOperationStatus> artifactFromCassandra = artifactCassandraDao.getArtifact(artifactId);
+ // Either<ESArtifactData, ResourceUploadStatus>
+ // artifactfromES = daoUploader.getArtifact(artifactId);
+ if (artifactFromCassandra.isRight()) {
+ log.debug("Failed to get artifact data from ES for artifact id {}", artifactId);
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertCassandraStatusToStorageStatus(artifactFromCassandra.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus));
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+ }
+ // clone data to new artifact
+ artifactData.setData(artifactFromCassandra.left().value().getData());
+ }
+ }
+ artifactData.setId(artifactDefinition.getUniqueId());
+ // create new entry in ES
+ res = true;
+ if (artifactData.getData() != null) {
+ res = saveArtifacts(artifactData, parentId, false);
+ // set on graph object id of artifact in ES!
+ if (res) {
+ artifactInfo.setEsId(artifactData.getId());
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifactRes = artifactOperation.updateArifactDefinition(artifactInfo, true);
+ if (updateArifactRes.isRight()) {
+ log.debug("Failed to update artifact on graph - {}", artifactId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateArifactRes.right().value()));
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+
+ } else {
+ insideEither = Either.left(updateArifactRes.left().value());
+ resultOp = Either.left(insideEither);
+ }
+ }
+ }
+ } else {
+ res = true;
+ if (artifactData.getData() != null) {
+ // override artifact in ES
+ res = saveArtifacts(artifactData, parentId, true);
+ if (res && artifactDefinition.getMandatory()) {
+ artifactInfo.setEsId(artifactData.getId());
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifactRes = artifactOperation.updateArifactDefinition(artifactInfo, true);
+ if (updateArifactRes.isRight()) {
+ log.debug("Failed to update artifact on graph - {}", artifactId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(updateArifactRes.right().value()), artifactId);
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ resultOp = Either.right(responseFormat);
+ return resultOp;
+
+ }
+ }
+ }
+ }
+ if (res) {
+ log.debug("Artifact saved into ES - {}", artifactUniqueId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ // resultOp = Either.left(result.left().value());
+ // return resultOp;
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Update Artifact");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Update Artifact");
+ log.debug("Failed to save the artifact.");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ resultOp = Either.right(responseFormat);
+ // return resultOp;
+ }
+ } else {
+ log.debug("Failed to create entry on graph for artifact {}", artifactInfo.getArtifactDisplayName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(error), artifactInfo.getArtifactDisplayName());
+ handleAuditing(auditingAction, parent, parentId, user, artifactInfo, prevArtifactId, currArtifactId, responseFormat, componentType, null);
+ resultOp = Either.right(responseFormat);
+ // return resultOp;
+ }
+
+ return resultOp;
+ }
+
+ private Either<byte[], ResponseFormat> handlePayload(ArtifactDefinition artifactInfo, boolean isArtifactMetadataUpdate) {
+ log.trace("Starting payload handling");
+ byte[] payload = artifactInfo.getPayloadData();
+ byte[] decodedPayload = null;
+
+ if (payload != null && payload.length != 0) {
+
+ decodedPayload = Base64.decodeBase64(payload);
+ if (decodedPayload.length == 0) {
+ log.debug("Failed to decode the payload.");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return Either.right(responseFormat);
+ }
+
+ String checkSum = GeneralUtility.calculateMD5ByByteArray(decodedPayload);
+ artifactInfo.setArtifactChecksum(checkSum);
+ log.trace("Calculated checksum, base64 payload: {}, checksum: {}", payload, checkSum);
+
+ // Specific payload validations of different types
+ Either<Boolean, ResponseFormat> isValidPayload = Either.left(true);
+ if (isDeploymentArtifact(artifactInfo)) {
+ log.trace("Starting deployment artifacts payload validation");
+ String artifactType = artifactInfo.getArtifactType();
+ if (ArtifactTypeEnum.HEAT.getType().equalsIgnoreCase(artifactType) || ArtifactTypeEnum.HEAT_VOL.getType().equalsIgnoreCase(artifactType) || ArtifactTypeEnum.HEAT_NET.getType().equalsIgnoreCase(artifactType)
+ || ArtifactTypeEnum.HEAT_ENV.getType().equalsIgnoreCase(artifactType)) {
+ isValidPayload = validateDeploymentHeatPayload(decodedPayload, artifactType);
+ if (isValidPayload.isLeft()) {
+ isValidPayload = extractHeatParameters(artifactInfo);
+ }
+ } else if (ArtifactTypeEnum.YANG_XML.getType().equalsIgnoreCase(artifactType) || ArtifactTypeEnum.VNF_CATALOG.getType().equalsIgnoreCase(artifactType) || ArtifactTypeEnum.VF_LICENSE.getType().equalsIgnoreCase(artifactType)
+ || ArtifactTypeEnum.VENDOR_LICENSE.getType().equalsIgnoreCase(artifactType) || ArtifactTypeEnum.MODEL_INVENTORY_PROFILE.getType().equalsIgnoreCase(artifactType)
+ || ArtifactTypeEnum.MODEL_QUERY_SPEC.getType().equalsIgnoreCase(artifactType)) {
+ isValidPayload = validateYangPayload(decodedPayload, artifactType);
+ // else
+ // if(ArtifactTypeEnum.APPC_CONFIG.getType().equalsIgnoreCase(artifactType)
+ // || ){
+ } else if (ArtifactTypeEnum.DCAE_INVENTORY_JSON.getType().equalsIgnoreCase(artifactType) || ArtifactTypeEnum.DCAE_INVENTORY_TOSCA.getType().equalsIgnoreCase(artifactType)) {
+ String artifactFileName = artifactInfo.getArtifactName();
+ String fileExtension = GeneralUtility.getFilenameExtension(artifactFileName).toLowerCase();
+ switch (fileExtension) {
+ case "xml":
+ isValidPayload = validateYangPayload(decodedPayload, artifactType);
+ break;
+ case "json":
+ isValidPayload = validateJsonPayload(decodedPayload, artifactType);
+ break;
+ case "yml":
+ case "yaml":
+ isValidPayload = validateYmlPayload(decodedPayload, artifactType);
+ break;
+ }
+ }
+ }
+ if (isValidPayload.isRight()) {
+ ResponseFormat responseFormat = isValidPayload.right().value();
+ return Either.right(responseFormat);
+ }
+
+ } // null/empty payload is normal if called from metadata update ONLY.
+ // The validation of whether this is metadata/payload update case is
+ // currently done separately
+ else {
+ if (!isArtifactMetadataUpdate) {
+ log.debug("Payload is missing.");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_PAYLOAD);
+ return Either.right(responseFormat);
+ }
+ }
+ log.trace("Ended payload handling");
+ return Either.left(decodedPayload);
+ }
+
+ private Either<Boolean, ResponseFormat> validateDeploymentHeatPayload(byte[] payload, String artifactType) {
+ // Basic YAML validation
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+ if (!yamlToObjectConverter.isValidYaml(payload)) {
+ log.debug("Invalid YAML format");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML, artifactType);
+ return Either.right(responseFormat);
+ }
+ if (!ArtifactTypeEnum.HEAT_ENV.getType().equalsIgnoreCase(artifactType)) {
+ // HEAT specific YAML validation
+ DeploymentArtifactHeatConfiguration heatConfiguration = yamlToObjectConverter.convert(payload, DeploymentArtifactHeatConfiguration.class);
+ if (heatConfiguration == null || heatConfiguration.getHeat_template_version() == null || heatConfiguration.getResources() == null) {
+ log.debug("HEAT doesn't contain required \"heat_template_version\" and \"resources\" sections ");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT, artifactType);
+ return Either.right(responseFormat);
+ }
+ }
+
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateYmlPayload(byte[] decodedPayload, String artifactType) {
+ Either<Boolean, ResponseFormat> res = Either.left(true);
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+ if (!yamlToObjectConverter.isValidYaml(decodedPayload)) {
+ log.debug("Invalid YAML format");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML, artifactType);
+ res = Either.right(responseFormat);
+ }
+
+ return res;
+ }
+
+ private Either<Boolean, ResponseFormat> validateYangPayload(byte[] payload, String artifactType) {
+ boolean isXmlValid = isValidXml(payload);
+ if (!isXmlValid) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.INVALID_XML, artifactType);
+ log.debug("Invalid XML content");
+ return Either.right(responseFormat);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateJsonPayload(byte[] payload, String type) {
+ boolean isJsonValid = isValidJson(payload);
+ if (!isJsonValid) {
+ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus.INVALID_JSON, type);
+ log.debug("Invalid JSON content");
+ return Either.right(responseFormat);
+ }
+ return Either.left(true);
+ }
+
+ public void handleTransaction(Either<Operation, ResponseFormat> opState) {
+ if (opState == null || opState.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+
+ public Either<Operation, ResponseFormat> deleteArtifactByInterface(String resourceId, String interfaceType, String operationName, String userId, String artifactId, ImmutablePair<User, Resource> userResourceAuditPair, boolean shouldLock,
+ boolean inTransaction) {
+ User user = new User();
+ user.setUserId(userId);
+ Either<Resource, StorageOperationStatus> parent = resourceOperation.getResource(resourceId);
+ if (parent.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(parent.right().value()));
+ return Either.right(responseFormat);
+ }
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDelete = handleDelete(resourceId, artifactId, user, AuditingActionEnum.ARTIFACT_DELETE, ComponentTypeEnum.RESOURCE, parent.left().value(), interfaceType, operationName,
+ false, inTransaction);
+ if (handleDelete.isRight()) {
+ return Either.right(handleDelete.right().value());
+ }
+ Either<ArtifactDefinition, Operation> result = handleDelete.left().value();
+ return Either.left(result.right().value());
+
+ }
+
+ public StorageOperationStatus deleteAllComponentArtifactsIfNotOnGraph(List<ArtifactDefinition> artifacts) {
+
+ if (artifacts != null && !artifacts.isEmpty()) {
+ for (ArtifactDefinition artifactDefinition : artifacts) {
+ String esId = artifactDefinition.getEsId();
+ if (esId != null && !esId.isEmpty()) {
+ StorageOperationStatus deleteIfNotOnGraph = deleteIfNotOnGraph(artifactDefinition.getUniqueId(), esId, false);
+ if (!deleteIfNotOnGraph.equals(StorageOperationStatus.OK)) {
+ return deleteIfNotOnGraph;
+ }
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private Operation convertToOperation(ArtifactDefinition artifactInfo, String operationName) {
+ Operation op = new Operation();
+ long time = System.currentTimeMillis();
+ op.setCreationDate(time);
+
+ String artifactName = artifactInfo.getArtifactName();
+ artifactInfo.setArtifactName(createInterfaceArtifactNameFromOperation(operationName, artifactName));
+
+ op.setImplementation(artifactInfo);
+ op.setLastUpdateDate(time);
+ return op;
+ }
+
+ private String createInterfaceArtifactNameFromOperation(String operationName, String artifactName) {
+ String newArtifactName = operationName + "_" + artifactName;
+ log.trace("converting artifact name {} to {}", artifactName, newArtifactName);
+ return newArtifactName;
+ }
+
+ public StorageOperationStatus deleteIfNotOnGraph(String artifactId, String artifactEsId, boolean deleteOnlyPayload) {
+ log.debug("deleteIfNotOnGraph: delete only payload = {}", deleteOnlyPayload);
+ Either<ArtifactData, TitanOperationStatus> checkArtifactNode = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId, ArtifactData.class);
+ if ((artifactEsId != null && !artifactEsId.isEmpty())) {
+ boolean isNotExistOnGraph = checkArtifactNode.isRight() && checkArtifactNode.right().value().equals(TitanOperationStatus.NOT_FOUND);
+
+ if ((isNotExistOnGraph) || (checkArtifactNode.left().value().getArtifactDataDefinition().getMandatory() && deleteOnlyPayload)
+ || (ArtifactGroupTypeEnum.SERVICE_API.equals(checkArtifactNode.left().value().getArtifactDataDefinition().getArtifactGroupType()) && deleteOnlyPayload)) {
+ // last one. need to delete in ES
+ log.debug("Entry on graph is deleted. Delete artifact in ES for id = {}", artifactEsId);
+ artifactCassandraDao.deleteArtifact(artifactEsId);
+ return StorageOperationStatus.OK;
+ // return
+ // componentsUtils.getResponseFormatByResourceId(ActionStatus.OK,
+ // resourceId);
+
+ } else {
+ log.debug("Entry on graph is deleted. Exist more connections on this artifact. Don't delete artifact in ES for id = {}", artifactEsId);
+ return StorageOperationStatus.OK;
+ }
+
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ // download by MSO
+ public Either<byte[], ResponseFormat> downloadRsrcArtifactByNames(String serviceName, String serviceVersion, String resourceName, String resourceVersion, String artifactName) {
+
+ // General validation
+ if (serviceName == null || serviceVersion == null || resourceName == null || resourceVersion == null || artifactName == null) {
+ log.debug("One of the function parameteres is null");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ // Normalizing artifact name
+ artifactName = ValidationUtils.normalizeFileName(artifactName);
+
+ // Resource validation
+ Either<Resource, ResponseFormat> validateResourceNameAndVersion = validateResourceNameAndVersion(resourceName, resourceVersion);
+ if (validateResourceNameAndVersion.isRight()) {
+ return Either.right(validateResourceNameAndVersion.right().value());
+ }
+
+ Resource resource = validateResourceNameAndVersion.left().value();
+ String resourceId = resource.getUniqueId();
+
+ // Service validation
+ Either<Service, ResponseFormat> validateServiceNameAndVersion = validateServiceNameAndVersion(serviceName, serviceVersion);
+ if (validateServiceNameAndVersion.isRight()) {
+ return Either.right(validateServiceNameAndVersion.right().value());
+ }
+
+ Map<String, ArtifactDefinition> artifacts = resource.getDeploymentArtifacts();
+ if (artifacts == null || artifacts.isEmpty()) {
+ log.debug("Deployment artifacts of resource {} are not found", resourceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactName));
+ }
+
+ ArtifactDefinition deploymentArtifact = null;
+
+ for (ArtifactDefinition artifactDefinition : artifacts.values()) {
+ if (artifactDefinition.getArtifactName() != null && artifactDefinition.getArtifactName().equals(artifactName)) {
+ log.debug("Found deployment artifact {}", artifactName);
+ deploymentArtifact = artifactDefinition;
+ break;
+ }
+ }
+
+ if (deploymentArtifact == null) {
+ log.debug("No deployment artifact {} was found for resource {}", artifactName, resourceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactName));
+ }
+
+ // Downloading the artifact
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> downloadArtifactEither = downloadArtifact(deploymentArtifact);
+ if (downloadArtifactEither.isRight()) {
+ log.debug("Download artifact {} failed", artifactName);
+ return Either.right(downloadArtifactEither.right().value());
+ }
+ log.trace("Download of resource artifact succeeded, uniqueId {}", deploymentArtifact.getUniqueId());
+ return Either.left(downloadArtifactEither.left().value().getRight());
+ }
+
+ // download by MSO
+ public Either<byte[], ResponseFormat> downloadRsrcInstArtifactByNames(String serviceName, String serviceVersion, String resourceInstanceName, String artifactName) {
+
+ // General validation
+ if (serviceName == null || serviceVersion == null || resourceInstanceName == null || artifactName == null) {
+ log.debug("One of the function parameteres is null");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ // Normalizing artifact name
+ artifactName = ValidationUtils.normalizeFileName(artifactName);
+
+ // Resource validation
+ /*
+ * Either<Resource, ResponseFormat> validateResourceNameAndVersion = validateResourceNameAndVersion(resourceName, resourceVersion); if (validateResourceNameAndVersion.isRight()) { return
+ * Either.right(validateResourceNameAndVersion.right().value()); }
+ *
+ * Resource resource = validateResourceNameAndVersion.left().value(); String resourceId = resource.getUniqueId();
+ */
+
+ // Service validation
+ Either<Service, ResponseFormat> validateServiceNameAndVersion = validateServiceNameAndVersion(serviceName, serviceVersion);
+ if (validateServiceNameAndVersion.isRight()) {
+ return Either.right(validateServiceNameAndVersion.right().value());
+ }
+
+ Service service = validateServiceNameAndVersion.left().value();
+
+ // ResourceInstance validation
+ Either<ComponentInstance, ResponseFormat> validateResourceInstance = validateResourceInstance(service, resourceInstanceName);
+ if (validateResourceInstance.isRight()) {
+ return Either.right(validateResourceInstance.right().value());
+ }
+
+ ComponentInstance resourceInstance = validateResourceInstance.left().value();
+
+ Map<String, ArtifactDefinition> artifacts = resourceInstance.getDeploymentArtifacts();
+
+ final String finalArtifactName = artifactName;
+ Predicate<ArtifactDefinition> filterArtifactByName = p -> p.getArtifactName().equals(finalArtifactName);
+
+ boolean hasDeploymentArtifacts = artifacts != null && artifacts.values().stream().anyMatch(filterArtifactByName);
+ ArtifactDefinition deployableArtifact;
+
+ if (!hasDeploymentArtifacts) {
+ log.debug("Deployment artifact with name {} not found", artifactName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactName));
+ }
+
+ log.debug("Found deployment artifact {}", artifactName);
+ deployableArtifact = artifacts.values().stream().filter(filterArtifactByName).findFirst().get();
+ // Downloading the artifact
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> downloadArtifactEither = downloadArtifact(deployableArtifact);
+
+ if (downloadArtifactEither.isRight()) {
+ log.debug("Download artifact {} failed", artifactName);
+ return Either.right(downloadArtifactEither.right().value());
+ }
+ log.trace("Download of resource artifact succeeded, uniqueId {}", deployableArtifact.getUniqueId());
+ return Either.left(downloadArtifactEither.left().value().getRight());
+ }
+
+ private Either<ComponentInstance, ResponseFormat> validateResourceInstance(Service service, String resourceInstanceName) {
+
+ List<ComponentInstance> riList = service.getComponentInstances();
+ for (ComponentInstance ri : riList) {
+ if (ri.getNormalizedName().equals(resourceInstanceName))
+ return Either.left(ri);
+ }
+
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND, resourceInstanceName));
+ }
+
+ private Either<Service, ResponseFormat> validateServiceNameAndVersion(String serviceName, String serviceVersion) {
+
+ Either<List<Service>, StorageOperationStatus> serviceListBySystemName = serviceOperation.getServiceListBySystemName(serviceName, false);
+ if (serviceListBySystemName.isRight()) {
+ log.debug("Couldn't fetch any service with name {}", serviceName);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(serviceListBySystemName.right().value(), ComponentTypeEnum.SERVICE), serviceName));
+ }
+ List<Service> serviceList = serviceListBySystemName.left().value();
+ if (serviceList == null || serviceList.isEmpty()) {
+ log.debug("Couldn't fetch any service with name {}", serviceName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.SERVICE_NOT_FOUND, serviceName));
+ }
+
+ Service foundService = null;
+ for (Service service : serviceList) {
+ if (service.getVersion().equals(serviceVersion)) {
+ log.trace("Found service with version {}", serviceVersion);
+ foundService = service;
+ break;
+ }
+ }
+
+ if (foundService == null) {
+ log.debug("Couldn't find version {} for service {}", serviceVersion, serviceName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_VERSION_NOT_FOUND, ComponentTypeEnum.SERVICE.getValue(), serviceVersion));
+ }
+ return Either.left(foundService);
+ }
+
+ private Either<Resource, ResponseFormat> validateResourceNameAndVersion(String resourceName, String resourceVersion) {
+ Either<List<Resource>, StorageOperationStatus> resourceListBySystemName = resourceOperation.getResourceListBySystemName(resourceName, false);
+ if (resourceListBySystemName.isRight()) {
+ log.debug("Couldn't fetch any resource with name {}", resourceName);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resourceListBySystemName.right().value()), resourceName));
+ }
+ List<Resource> resourceList = resourceListBySystemName.left().value();
+ if (resourceList == null || resourceList.isEmpty()) {
+ log.debug("Couldn't fetch any resource with name {}", resourceName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, resourceName));
+ }
+
+ Resource foundResource = null;
+ for (Resource resource : resourceList) {
+ if (resource.getVersion().equals(resourceVersion)) {
+ log.trace("Found resource with version {}", resourceVersion);
+ foundResource = resource;
+ break;
+ }
+ }
+
+ if (foundResource == null) {
+ log.debug("Couldn't find version {} for resource {}", resourceVersion, resourceName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_VERSION_NOT_FOUND, ComponentTypeEnum.RESOURCE.getValue(), resourceVersion));
+ }
+ return Either.left(foundResource);
+ }
+
+ public Either<byte[], ResponseFormat> downloadServiceArtifactByNames(String serviceName, String serviceVersion, String artifactName) {
+ // Validation
+ log.trace("Starting download of service interface artifact, serviceName {}, serviceVersion {}, artifact name {}", serviceName, serviceVersion, artifactName);
+ if (serviceName == null || serviceVersion == null || artifactName == null) {
+ log.debug("One of the function parameteres is null");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ // Normalizing artifact name
+ artifactName = ValidationUtils.normalizeFileName(artifactName);
+
+ // Service validation
+ Either<Service, ResponseFormat> validateServiceNameAndVersion = validateServiceNameAndVersion(serviceName, serviceVersion);
+ if (validateServiceNameAndVersion.isRight()) {
+ return Either.right(validateServiceNameAndVersion.right().value());
+ }
+
+ String serviceId = validateServiceNameAndVersion.left().value().getUniqueId();
+
+ // Looking for deployment artifacts
+ Service service = validateServiceNameAndVersion.left().value();
+ Map<String, ArtifactDefinition> artifacts = service.getDeploymentArtifacts();
+ if (artifacts == null || artifacts.isEmpty()) {
+ log.debug("Deployment artifacts of service {} are not found", serviceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactName));
+ }
+
+ ArtifactDefinition deploymentArtifact = null;
+
+ for (ArtifactDefinition artifactDefinition : artifacts.values()) {
+ if (artifactDefinition.getArtifactName().equals(artifactName)) {
+ log.debug("Found deployment artifact {}", artifactName);
+ deploymentArtifact = artifactDefinition;
+ }
+ }
+
+ if (deploymentArtifact == null) {
+ log.debug("No deployment artifact {} was found for service {}", artifactName, serviceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactName));
+ }
+
+ // Downloading the artifact
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> downloadArtifactEither = downloadArtifact(deploymentArtifact);
+ if (downloadArtifactEither.isRight()) {
+ log.debug("Download artifact {} failed", artifactName);
+ return Either.right(downloadArtifactEither.right().value());
+ }
+ log.trace("Download of service artifact succeeded, uniqueId {}", deploymentArtifact.getUniqueId());
+ return Either.left(downloadArtifactEither.left().value().getRight());
+ }
+
+ public Either<ImmutablePair<String, byte[]>, ResponseFormat> downloadArtifact(String artifactUniqueId) {
+ log.trace("Starting download of artifact, uniqueId {}", artifactUniqueId);
+ Either<ArtifactDefinition, StorageOperationStatus> artifactById = artifactOperation.getArtifactById(artifactUniqueId, false);
+ if (artifactById.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(artifactById.right().value());
+ log.debug("Error when getting artifact info by id{}, error: {}", artifactUniqueId, actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormatByArtifactId(actionStatus, ""));
+ }
+ ArtifactDefinition artifactDefinition = artifactById.left().value();
+ if (artifactDefinition == null) {
+ log.debug("Empty artifact definition returned from DB by artifact id {}", artifactUniqueId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, ""));
+ }
+
+ return downloadArtifact(artifactDefinition);
+ }
+
+ private boolean checkArtifactInComponent(org.openecomp.sdc.be.model.Component component, String artifactId) {
+ boolean found = false;
+ Map<String, ArtifactDefinition> artifactsS = component.getArtifacts();
+ if (artifactsS != null) {
+ for (Map.Entry<String, ArtifactDefinition> entry : artifactsS.entrySet()) {
+ if (entry.getValue().getUniqueId().equals(artifactId)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ Map<String, ArtifactDefinition> deploymentArtifactsS = component.getDeploymentArtifacts();
+ if (!found && deploymentArtifactsS != null) {
+ for (Map.Entry<String, ArtifactDefinition> entry : deploymentArtifactsS.entrySet()) {
+ if (entry.getValue().getUniqueId().equals(artifactId)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ Map<String, ArtifactDefinition> toscaArtifactsS = component.getToscaArtifacts();
+ if (!found && toscaArtifactsS != null) {
+ for (Map.Entry<String, ArtifactDefinition> entry : toscaArtifactsS.entrySet()) {
+ if (entry.getValue().getUniqueId().equals(artifactId)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ switch (component.getComponentType()) {
+ case RESOURCE:
+ Map<String, InterfaceDefinition> interfaces = ((Resource) component).getInterfaces();
+ if (!found && interfaces != null) {
+ for (Map.Entry<String, InterfaceDefinition> entry : interfaces.entrySet()) {
+ Map<String, Operation> operations = entry.getValue().getOperations();
+ for (Map.Entry<String, Operation> entryOp : operations.entrySet()) {
+ if (entryOp.getValue().getImplementation() != null && entryOp.getValue().getImplementation().getUniqueId().equals(artifactId)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case SERVICE:
+ Map<String, ArtifactDefinition> apiArtifacts = ((Service) component).getServiceApiArtifacts();
+ if (!found && apiArtifacts != null) {
+ for (Map.Entry<String, ArtifactDefinition> entry : apiArtifacts.entrySet()) {
+ if (entry.getValue().getUniqueId().equals(artifactId)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+
+ }
+
+ return found;
+ }
+
+ private boolean checkArtifactInResourceInstance(Component component, String resourceInstanceId, String artifactId) {
+
+ boolean found = false;
+ List<ComponentInstance> resourceInstances = component.getComponentInstances();
+ ComponentInstance resourceInstance = null;
+ for (ComponentInstance ri : resourceInstances) {
+ if (ri.getUniqueId().equals(resourceInstanceId)) {
+ resourceInstance = ri;
+ break;
+ }
+ }
+ if (resourceInstance != null) {
+ Map<String, ArtifactDefinition> artifacts = resourceInstance.getDeploymentArtifacts();
+ if (artifacts != null) {
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ if (entry.getValue().getUniqueId().equals(artifactId)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ return found;
+ }
+
+ private Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists(String componentId, String userId, AuditingActionEnum auditingAction, User user, String artifactId, ComponentTypeEnum componentType,
+ String containerComponentType, boolean inTransaction) {
+
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ if (containerComponentType != null && NodeTypeEnum.ResourceInstance == nodeType) {
+ nodeType = (ComponentTypeEnum.findByParamName(containerComponentType)).getNodeType();
+ }
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+
+ if (componentOperation == null) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ log.debug("addArtifact - not supported component type {}", componentType);
+ handleAuditing(auditingAction, null, componentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ Either<? extends org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentResult = componentOperation.getComponent(componentId, inTransaction);
+
+ if (componentResult.isRight()) {
+ ActionStatus status = (componentType.equals(ComponentTypeEnum.RESOURCE)) ? ActionStatus.RESOURCE_NOT_FOUND : ActionStatus.SERVICE_NOT_FOUND;
+ ComponentTypeEnum componentForAudit = (componentType.equals(ComponentTypeEnum.RESOURCE)) ? ComponentTypeEnum.RESOURCE : ComponentTypeEnum.SERVICE;
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(status, componentId);
+ log.debug("Service not found, serviceId {}", componentId);
+ handleAuditing(auditingAction, null, componentId, user, null, null, artifactId, responseFormat, componentForAudit, null);
+ return Either.right(responseFormat);
+ }
+ return Either.left(componentResult.left().value());
+ }
+
+ private Either<Boolean, ResponseFormat> validateWorkOnComponent(org.openecomp.sdc.be.model.Component component, String userId, AuditingActionEnum auditingAction, User user, String artifactId, ArtifactOperation operation,
+ ComponentTypeEnum componentType) {
+ if (operation != ArtifactOperation.Download) {
+ Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, userId);
+ if (canWork.isRight()) {
+ String uniqueId = component.getUniqueId();
+ log.debug("Service status isn't CHECKOUT or user isn't owner, serviceId {}", uniqueId);
+ handleAuditing(auditingAction, component, uniqueId, user, null, null, artifactId, canWork.right().value(), component.getComponentType(), null);
+ return Either.right(canWork.right().value());
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateWorkOnResource(Resource resource, String userId, AuditingActionEnum auditingAction, User user, String artifactId, ArtifactOperation operation) {
+ if (operation != ArtifactOperation.Download) {
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ String uniqueId = resource.getUniqueId();
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ log.debug("Resource status isn't CHECKOUT or user isn't owner, resourceId {}", uniqueId);
+ handleAuditing(auditingAction, resource, uniqueId, user, null, null, artifactId, responseFormat, ComponentTypeEnum.RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateUserRole(User user, AuditingActionEnum auditingAction, String componentId, String artifactId, ComponentTypeEnum componentType, ArtifactOperation operation) {
+
+ if (operation != ArtifactOperation.Download) {
+ String role = user.getRole();
+ if (!role.equals(Role.ADMIN.name()) && !role.equals(Role.DESIGNER.name())) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ log.debug("addArtifact - user isn't permitted to perform operation, userId {}, role {}", user.getUserId(), role);
+ handleAuditing(auditingAction, null, componentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<User, ResponseFormat> validateUserExists(String userId, AuditingActionEnum auditingAction, String componentId, String artifactId, ComponentTypeEnum componentType) {
+
+ return validateUserExists(userId, auditingAction, componentId, artifactId, componentType, false);
+
+ }
+
+ private Either<User, ResponseFormat> validateUserExists(String userId, AuditingActionEnum auditingAction, String componentId, String artifactId, ComponentTypeEnum componentType, boolean inTransaction) {
+ Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, auditingAction.getName(), inTransaction);
+
+ if (validateUserExists.isRight()) {
+ User user = new User();
+ user.setUserId(userId);
+ handleAuditing(auditingAction, null, componentId, user, null, null, artifactId, validateUserExists.right().value(), componentType, null);
+ return Either.right(validateUserExists.right().value());
+ }
+ return Either.left(validateUserExists.left().value());
+ }
+
+ private AuditingActionEnum detectAuditingType(ArtifactOperation operation, String origMd5) {
+ AuditingActionEnum auditingAction = null;
+ switch (operation) {
+ case Create:
+ auditingAction = operation.isExternalApi() ? AuditingActionEnum.ARTIFACT_UPLOAD_BY_API : AuditingActionEnum.ARTIFACT_UPLOAD;
+ break;
+ case Update:
+ auditingAction = operation.isExternalApi() ? AuditingActionEnum.ARTIFACT_UPLOAD_BY_API : origMd5 == null ? AuditingActionEnum.ARTIFACT_METADATA_UPDATE : AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE;
+ break;
+ case Delete:
+ auditingAction = operation.isExternalApi() ? AuditingActionEnum.ARTIFACT_DELETE_BY_API : AuditingActionEnum.ARTIFACT_DELETE;
+ break;
+ case Download:
+ auditingAction = operation.isExternalApi() ? AuditingActionEnum.DOWNLOAD_ARTIFACT : AuditingActionEnum.ARTIFACT_DOWNLOAD;
+ break;
+ default:
+ break;
+ }
+ return auditingAction;
+ }
+
+ private Either<ImmutablePair<String, byte[]>, ResponseFormat> downloadArtifact(ArtifactDefinition artifactDefinition) {
+ String esArtifactId = artifactDefinition.getEsId();
+ Either<ESArtifactData, CassandraOperationStatus> artifactfromES = artifactCassandraDao.getArtifact(esArtifactId);
+ if (artifactfromES.isRight()) {
+ CassandraOperationStatus resourceUploadStatus = artifactfromES.right().value();
+ StorageOperationStatus storageResponse = DaoStatusConverter.convertCassandraStatusToStorageStatus(resourceUploadStatus);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageResponse);
+ log.debug("Error when getting artifact from ES, error: {}", actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormatByArtifactId(actionStatus, artifactDefinition.getArtifactDisplayName()));
+ }
+
+ ESArtifactData esArtifactData = artifactfromES.left().value();
+ byte[] data = esArtifactData.getDataAsArray();
+ if (data == null) {
+ log.debug("Artifact data from ES is null");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactDefinition.getArtifactDisplayName()));
+ }
+ String artifactName = artifactDefinition.getArtifactName();
+ log.trace("Download of artifact succeeded, uniqueId {}, artifact file name {}", artifactDefinition.getUniqueId(), artifactName);
+ return Either.left(new ImmutablePair<String, byte[]>(artifactName, data));
+ }
+
+ public ESArtifactData createEsArtifactData(ArtifactDataDefinition artifactInfo, byte[] artifactPayload) {
+ ESArtifactData artifactData = new ESArtifactData(artifactInfo.getEsId(), artifactPayload);
+ return artifactData;
+ }
+
+ private boolean saveArtifacts(ESArtifactData artifactData, String resourceId, boolean reload) {
+
+ CassandraOperationStatus resourceUploadStatus = artifactCassandraDao.saveArtifact(artifactData);
+
+ if (resourceUploadStatus.equals(CassandraOperationStatus.OK)) {
+ log.debug("Artifact {} was saved in component {}.", artifactData.getId(), resourceId);
+ } else {
+ log.info("Failed to save artifact {}.", artifactData.getId());
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isArtifactMetadataUpdate(AuditingActionEnum auditingActionEnum) {
+ return (auditingActionEnum.equals(AuditingActionEnum.ARTIFACT_METADATA_UPDATE));
+ }
+
+ private boolean isDeploymentArtifact(ArtifactDefinition artifactInfo) {
+ return (ArtifactGroupTypeEnum.DEPLOYMENT.equals(artifactInfo.getArtifactGroupType()));
+ }
+
+ public IResourceOperation getResourceOperation() {
+ return this.resourceOperation;
+ }
+
+ public Either<ArtifactDefinition, ResponseFormat> createArtifactPlaceHolderInfo(String resourceId, String logicalName, Map<String, Object> artifactInfoMap, String userId, ArtifactGroupTypeEnum groupType, boolean inTransaction) {
+ Either<User, ActionStatus> user = userAdminManager.getUser(userId, inTransaction);
+ if (user.isRight()) {
+ ResponseFormat responseFormat;
+ if (user.right().value().equals(ActionStatus.USER_NOT_FOUND)) {
+ log.debug("create artifact placeholder - not authorized user, userId {}", userId);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ } else {
+ log.debug("create artifact placeholder - failed to authorize user, userId {}", userId);
+ responseFormat = componentsUtils.getResponseFormat(user.right().value());
+ }
+ return Either.right(responseFormat);
+ }
+
+ ArtifactDefinition artifactDefinition = createArtifactPlaceHolderInfo(resourceId, logicalName, artifactInfoMap, user.left().value(), groupType);
+ return Either.left(artifactDefinition);
+ }
+
+ public ArtifactDefinition createArtifactPlaceHolderInfo(String resourceId, String logicalName, Map<String, Object> artifactInfoMap, User user, ArtifactGroupTypeEnum groupType) {
+ ArtifactDefinition artifactInfo = new ArtifactDefinition();
+
+ String artifactName = (String) artifactInfoMap.get(ARTIFACT_PLACEHOLDER_DISPLAY_NAME);
+ String artifactType = (String) artifactInfoMap.get(ARTIFACT_PLACEHOLDER_TYPE);
+ String artifactDescription = (String) artifactInfoMap.get(ARTIFACT_PLACEHOLDER_DESCRIPTION);
+
+ artifactInfo.setArtifactDisplayName(artifactName);
+ artifactInfo.setArtifactLabel(logicalName.toLowerCase());
+ artifactInfo.setArtifactType(artifactType);
+ artifactInfo.setDescription(artifactDescription);
+ artifactInfo.setArtifactGroupType(groupType);
+ setDefaultArtifactTimeout(groupType, artifactInfo);
+
+ setArtifactPlaceholderCommonFields(resourceId, user, artifactInfo);
+
+ return artifactInfo;
+ }
+
+ private void setDefaultArtifactTimeout(ArtifactGroupTypeEnum groupType, ArtifactDefinition artifactInfo) {
+ if (groupType.equals(ArtifactGroupTypeEnum.DEPLOYMENT)) {
+ artifactInfo.setTimeout(defaultHeatTimeout);
+ } else {
+ artifactInfo.setTimeout(NON_HEAT_TIMEOUT);
+ }
+ }
+
+ private void setArtifactPlaceholderCommonFields(String resourceId, User user, ArtifactDefinition artifactInfo) {
+ String uniqueId = null;
+
+ if (resourceId != null) {
+ uniqueId = UniqueIdBuilder.buildPropertyUniqueId(resourceId.toLowerCase(), artifactInfo.getArtifactLabel().toLowerCase());
+ artifactInfo.setUniqueId(uniqueId);
+ }
+ artifactInfo.setUserIdCreator(user.getUserId());
+ String fullName = user.getFullName();
+ artifactInfo.setUpdaterFullName(fullName);
+
+ long time = System.currentTimeMillis();
+
+ artifactInfo.setCreatorFullName(fullName);
+ artifactInfo.setCreationDate(time);
+
+ artifactInfo.setLastUpdateDate(time);
+ artifactInfo.setUserIdLastUpdater(user.getUserId());
+
+ artifactInfo.setMandatory(true);
+ }
+
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifacts(String parentId, NodeTypeEnum parentType, boolean inTransaction, ArtifactGroupTypeEnum groupType) {
+ return artifactOperation.getArtifacts(parentId, parentType, inTransaction, groupType.getType());
+ }
+
+ public Either<ArtifactDefinition, StorageOperationStatus> addHeatEnvArtifact(ArtifactDefinition artifactHeatEnv, ArtifactDefinition artifact, String parentId, NodeTypeEnum parentType, boolean inTransaction) {
+ return artifactOperation.addHeatEnvArtifact(artifactHeatEnv, artifact, parentId, parentType, inTransaction);
+
+ }
+
+ private Either<ESArtifactData, ResponseFormat> createEsHeatEnvArtifactDataFromString(ArtifactDefinition artifactDefinition, String parameters) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(ConfigurationManager.getConfigurationManager().getConfiguration().getHeatEnvArtifactHeader());
+ sb.append(parameters);
+ sb.append(ConfigurationManager.getConfigurationManager().getConfiguration().getHeatEnvArtifactFooter());
+ byte[] payload = sb.toString().getBytes();
+
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+
+ /*
+ * if (!yamlToObjectConverter.isValidYaml(payload)) { log.debug("Invalid YAML format"); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML, ArtifactTypeEnum.HEAT_ENV.getType()); return
+ * Either.right(responseFormat); }
+ */
+
+ ESArtifactData artifactData = createEsArtifactData(artifactDefinition, payload);
+ return Either.left(artifactData);
+ }
+
+ /**
+ *
+ * @param artifactDefinition
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> generateHeatEnvArtifact(ArtifactDefinition artifactDefinition, org.openecomp.sdc.be.model.Component component, String resourceInstanceName, User modifier, boolean shouldLock) {
+ List<HeatParameterDefinition> heatParameters = artifactDefinition.getHeatParameters();
+ heatParameters.sort(Comparator.comparing(e -> e.getName()));
+ StringBuilder sb = new StringBuilder("parameters:\n");
+ if (heatParameters != null) {
+
+ for (HeatParameterDefinition heatParameterDefinition : heatParameters) {
+ if (heatParameterDefinition.getCurrentValue() != null) {
+ HeatParameterType type = HeatParameterType.isValidType(heatParameterDefinition.getType());
+ if (type != null) {
+ switch (type) {
+ case BOOLEAN:
+ sb.append(" ").append(heatParameterDefinition.getName()).append(":").append(" ").append(Boolean.parseBoolean(heatParameterDefinition.getCurrentValue())).append("\n");
+ break;
+ case NUMBER:
+ // if
+ // (ValidationUtils.isFloatNumber(heatParameterDefinition.getCurrentValue()))
+ // {
+ // sb.append("
+ // ").append(heatParameterDefinition.getName()).append(":").append("
+ // ").append(Float.parseFloat(heatParameterDefinition.getCurrentValue())).append("\n");
+ // } else {
+ // sb.append("
+ // ").append(heatParameterDefinition.getName()).append(":").append("
+ // ").append(Integer.parseInt(heatParameterDefinition.getCurrentValue())).append("\n");
+ // }
+ sb.append(" ").append(heatParameterDefinition.getName()).append(":").append(" ").append(new BigDecimal(heatParameterDefinition.getCurrentValue()).toPlainString()).append("\n");
+ break;
+ case COMMA_DELIMITED_LIST:
+ case JSON:
+ sb.append(" ").append(heatParameterDefinition.getName()).append(":").append(" ").append(heatParameterDefinition.getCurrentValue()).append("\n");
+ break;
+ default:
+ String value = heatParameterDefinition.getCurrentValue();
+ boolean starts = value.startsWith("\"");
+ boolean ends = value.endsWith("\"");
+ if (!(starts && ends)) {
+ starts = value.startsWith("'");
+ ends = value.endsWith("'");
+ if (!(starts && ends)) {
+ value = "\"" + value + "\"";
+ }
+ }
+ sb.append(" ").append(heatParameterDefinition.getName()).append(":").append(" ").append(value);
+ sb.append("\n");
+ break;
+
+ }
+ }
+ }
+ }
+ }
+ return generateAndSaveHeatEnvArtifact(artifactDefinition, sb.toString(), component, resourceInstanceName, modifier, shouldLock);
+
+ }
+
+ /**
+ *
+ * @param artifactDefinition
+ * @param payload
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> generateAndSaveHeatEnvArtifact(ArtifactDefinition artifactDefinition, String payload, org.openecomp.sdc.be.model.Component component, String resourceInstanceName, User modifier,
+ boolean shouldLock) {
+ return generateArtifactPayload(artifactDefinition, component, resourceInstanceName, modifier, shouldLock, () -> artifactDefinition.getHeatParamsUpdateDate(), () -> createEsHeatEnvArtifactDataFromString(artifactDefinition, payload));
+
+ }
+
+ protected Either<ArtifactDefinition, ResponseFormat> generateArtifactPayload(ArtifactDefinition artifactDefinition, org.openecomp.sdc.be.model.Component component, String resourceInstanceName, User modifier, boolean shouldLock,
+ Supplier<Long> payloadUpdateDateGen, Supplier<Either<ESArtifactData, ResponseFormat>> esDataCreator) {
+
+ if (artifactDefinition.getPayloadUpdateDate() == null || artifactDefinition.getPayloadUpdateDate() == 0 || artifactDefinition.getPayloadUpdateDate() < payloadUpdateDateGen.get()) {
+
+ log.trace("Generaing payload for {} artifact {}", artifactDefinition.getArtifactType(), artifactDefinition.getEsId());
+ Either<ESArtifactData, ResponseFormat> artifactDataRes = esDataCreator.get();
+ ESArtifactData artifactData = null;
+
+ if (artifactDataRes.isLeft()) {
+ artifactData = artifactDataRes.left().value();
+ } else {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ handleAuditing(AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE, component, component.getUniqueId(), modifier, artifactDefinition, artifactDefinition.getUniqueId(), artifactDefinition.getUniqueId(), responseFormat,
+ ComponentTypeEnum.RESOURCE_INSTANCE, resourceInstanceName);
+
+ return Either.right(artifactDataRes.right().value());
+ }
+ String newCheckSum = GeneralUtility.calculateMD5ByByteArray(artifactData.getDataAsArray());
+ String oldCheckSum;
+ String esArtifactId = artifactDefinition.getEsId();
+ Either<ESArtifactData, CassandraOperationStatus> artifactfromES;
+ ESArtifactData esArtifactData;
+ if (esArtifactId != null && !esArtifactId.isEmpty()) {
+ artifactfromES = artifactCassandraDao.getArtifact(esArtifactId);
+ if (artifactfromES.isRight()) {
+ CassandraOperationStatus resourceUploadStatus = artifactfromES.right().value();
+ StorageOperationStatus storageResponse = DaoStatusConverter.convertCassandraStatusToStorageStatus(resourceUploadStatus);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageResponse);
+ log.debug("Error when getting artifact from ES, error: {}", actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormatByArtifactId(actionStatus, artifactDefinition.getArtifactDisplayName()));
+ }
+ esArtifactData = artifactfromES.left().value();
+ oldCheckSum = GeneralUtility.calculateMD5ByByteArray(esArtifactData.getDataAsArray());
+ } else {
+ oldCheckSum = artifactDefinition.getArtifactChecksum();
+ }
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifactDefinitionStatus;
+
+ if (shouldLock) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, "Update Artifact - lock resource: ");
+ if (lockComponent.isRight()) {
+ handleAuditing(AuditingActionEnum.ARTIFACT_METADATA_UPDATE, component, component.getUniqueId(), modifier, null, null, artifactDefinition.getUniqueId(), lockComponent.right().value(), component.getComponentType(), null);
+ return Either.right(lockComponent.right().value());
+ }
+ }
+ try {
+ if (oldCheckSum != null && oldCheckSum.equals(newCheckSum)) {
+
+ artifactDefinition.setPayloadUpdateDate(payloadUpdateDateGen.get());
+ updateArifactDefinitionStatus = artifactOperation.updateArifactDefinition(artifactDefinition, false);
+ log.trace("No real update done in payload for {} artifact, updating payloadUpdateDate {}", artifactDefinition.getArtifactType(), artifactDefinition.getEsId());
+ if (updateArifactDefinitionStatus.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(updateArifactDefinitionStatus.right().value()), artifactDefinition.getArtifactDisplayName());
+ log.trace("Failed to update payloadUpdateDate ", artifactDefinition.getEsId());
+ handleAuditing(AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE, component, component.getUniqueId(), modifier, artifactDefinition, artifactDefinition.getUniqueId(), artifactDefinition.getUniqueId(), responseFormat,
+ ComponentTypeEnum.RESOURCE_INSTANCE, resourceInstanceName);
+
+ return Either.right(responseFormat);
+ }
+ } else {
+
+ oldCheckSum = artifactDefinition.getArtifactChecksum();
+ artifactDefinition.setArtifactChecksum(newCheckSum);
+ artifactOperation.updateUUID(artifactDefinition, oldCheckSum, artifactDefinition.getArtifactVersion());
+ artifactDefinition.setEsId(artifactDefinition.getUniqueId());
+ updateArifactDefinitionStatus = artifactOperation.updateArifactDefinition(artifactDefinition, true);
+
+ log.trace("Update Payload ", artifactDefinition.getEsId());
+
+ if (updateArifactDefinitionStatus.isLeft()) {
+
+ artifactData.setId(artifactDefinition.getUniqueId());
+ CassandraOperationStatus saveArtifactStatus = artifactCassandraDao.saveArtifact(artifactData);
+
+ if (saveArtifactStatus.equals(CassandraOperationStatus.OK)) {
+ titanGenericDao.commit();
+ log.debug("Artifact Saved In ES {}", artifactData.getId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleAuditing(AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE, component, component.getUniqueId(), modifier, artifactDefinition, artifactDefinition.getUniqueId(), artifactDefinition.getUniqueId(), responseFormat,
+ ComponentTypeEnum.RESOURCE_INSTANCE, resourceInstanceName);
+
+ } else {
+ titanGenericDao.rollback();
+ log.info("Failed to save artifact {}.", artifactData.getId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ handleAuditing(AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE, component, component.getUniqueId(), modifier, artifactDefinition, artifactDefinition.getUniqueId(), artifactDefinition.getUniqueId(), responseFormat,
+ ComponentTypeEnum.RESOURCE_INSTANCE, resourceInstanceName);
+
+ return Either.right(responseFormat);
+ }
+ } else {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByArtifactId(componentsUtils.convertFromStorageResponse(updateArifactDefinitionStatus.right().value()), artifactDefinition.getArtifactDisplayName());
+ log.debug("Failed To update artifact {}", artifactData.getId());
+ handleAuditing(AuditingActionEnum.ARTIFACT_PAYLOAD_UPDATE, component, component.getUniqueId(), modifier, artifactDefinition, artifactDefinition.getUniqueId(), artifactDefinition.getUniqueId(), responseFormat,
+ ComponentTypeEnum.RESOURCE_INSTANCE, resourceInstanceName);
+
+ return Either.right(responseFormat);
+
+ }
+ }
+ } finally {
+ if (shouldLock) {
+ graphLockOperation.unlockComponent(component.getUniqueId(), component.getComponentType().getNodeType());
+ }
+ }
+ }
+
+ return Either.left(artifactDefinition);
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleUpdateHeatEnv(String componentId, ArtifactDefinition artifactInfo, AuditingActionEnum auditingAction, String artifactId, User user, ComponentTypeEnum componentType,
+ org.openecomp.sdc.be.model.Component parent, String originData, String origMd5, ArtifactOperation operation, boolean shouldLock, boolean inTransaction) {
+ NodeTypeEnum parentType = convertParentType(componentType);
+ String parentId = parent.getUniqueId();
+ Either<ArtifactDefinition, StorageOperationStatus> artifactRes = artifactOperation.getArtifactById(artifactId, false);
+ ArtifactDefinition currArtifact = artifactRes.left().value();
+
+ if (origMd5 != null) {
+ Either<Boolean, ResponseFormat> validateMd5 = validateMd5(origMd5, originData, artifactInfo.getPayloadData(), operation);
+ if (validateMd5.isRight()) {
+ ResponseFormat responseFormat = validateMd5.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+
+ if (artifactInfo.getPayloadData() != null && artifactInfo.getPayloadData().length != 0) {
+ Either<Boolean, ResponseFormat> deploymentValidationResult = validateDeploymentArtifact(parent, componentId, user.getUserId(), false, artifactInfo, currArtifact, NodeTypeEnum.ResourceInstance);
+ if (deploymentValidationResult.isRight()) {
+ ResponseFormat responseFormat = deploymentValidationResult.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+
+ Either<byte[], ResponseFormat> payloadEither = handlePayload(artifactInfo, isArtifactMetadataUpdate(auditingAction));
+ if (payloadEither.isRight()) {
+ ResponseFormat responseFormat = payloadEither.right().value();
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ } else { // duplicate
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, ARTIFACT_PAYLOAD);
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ }
+
+ // lock resource
+ if (shouldLock) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(parent, "Update Artifact - lock ");
+ if (lockComponent.isRight()) {
+ handleAuditing(auditingAction, parent, parentId, user, null, null, artifactId, lockComponent.right().value(), componentType, null);
+ return Either.right(lockComponent.right().value());
+ }
+ }
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+ try {
+ resultOp = updateHeatEnvParams(componentId, artifactId, artifactInfo, user, auditingAction, parent, componentType, currArtifact, origMd5, inTransaction);
+ return resultOp;
+
+ } finally {
+ // unlock resource
+ if (resultOp == null || resultOp.isRight()) {
+ log.debug("all changes rollback");
+ if (false == inTransaction)
+ titanGenericDao.rollback();
+ } else {
+ log.debug("all changes committed");
+ if (false == inTransaction)
+ titanGenericDao.commit();
+ }
+ if (shouldLock)
+ componentType = parent.getComponentType();
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ graphLockOperation.unlockComponent(parent.getUniqueId(), nodeType);
+ // graphLockOperation.unlockComponent(parentId, parentType);
+ }
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> updateHeatEnvParams(String componentId, String artifactId, ArtifactDefinition artifactInfo, User user, AuditingActionEnum auditingAction, Component parent,
+ ComponentTypeEnum componentType, ArtifactDefinition currArtifact1, String origMd5, boolean inTransaction) {
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> resultOp = null;
+ Either<ArtifactDefinition, Operation> insideEither = null;
+ /*
+ * currently getArtifactById does not retrieve heatParameters Either<ArtifactDefinition, StorageOperationStatus> artifactRes = artifactOperation.getArtifactById(artifactId, false); ArtifactDefinition currArtifact = artifactRes.left().value();
+ */
+ Either<ComponentInstance, ResponseFormat> getRI = getRIFromComponent(parent, componentId, artifactId, auditingAction, user);
+ if (getRI.isRight()) {
+ return Either.right(getRI.right().value());
+ }
+ ComponentInstance ri = getRI.left().value();
+ Either<ArtifactDefinition, ResponseFormat> getArtifactRes = getArtifactFromRI(parent, ri, componentId, artifactId, auditingAction, user);
+ if (getArtifactRes.isRight()) {
+ return Either.right(getArtifactRes.right().value());
+ }
+ ArtifactDefinition currArtifact = getArtifactRes.left().value();
+
+ if (currArtifact.getArtifactType().equals(ArtifactTypeEnum.HEAT.getType()) || currArtifact.getArtifactType().equals(ArtifactTypeEnum.HEAT_VOL.getType()) || currArtifact.getArtifactType().equals(ArtifactTypeEnum.HEAT_NET.getType())) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, ri.getName());
+ return Either.right(responseFormat);
+ }
+ List<HeatParameterDefinition> currentHeatEnvParams = currArtifact.getHeatParameters();
+ List<HeatParameterDefinition> updatedHeatEnvParams = artifactInfo.getHeatParameters();
+ List<HeatParameterDefinition> reducedHeatEnvParams = new ArrayList<HeatParameterDefinition>();
+
+ // upload
+ if (origMd5 != null) {
+ Either<List<HeatParameterDefinition>, ResponseFormat> uploadParamsValidationResult = validateUploadParamsFromEnvFile(auditingAction, parent, user, artifactInfo, artifactId, componentType, ri.getName(), currentHeatEnvParams,
+ updatedHeatEnvParams, currArtifact.getArtifactName());
+ if (uploadParamsValidationResult.isRight()) {
+ ResponseFormat responseFormat = uploadParamsValidationResult.right().value();
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, ri.getName());
+ return Either.right(responseFormat);
+ }
+ artifactInfo.setHeatParameters(updatedHeatEnvParams);
+ }
+
+ Either<ArtifactDefinition, ResponseFormat> validateAndConvertHeatParamers = validateAndConvertHeatParamers(artifactInfo, ArtifactTypeEnum.HEAT_ENV.getType());
+ if (validateAndConvertHeatParamers.isRight()) {
+ ResponseFormat responseFormat = validateAndConvertHeatParamers.right().value();
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, ri.getName());
+ return Either.right(responseFormat);
+ }
+
+ if (updatedHeatEnvParams != null && !updatedHeatEnvParams.isEmpty()) {
+ String paramName;
+ // fill reduced heat env parameters List for updating
+ for (HeatParameterDefinition heatEnvParam : updatedHeatEnvParams) {
+ paramName = heatEnvParam.getName();
+ for (HeatParameterDefinition currHeatParam : currentHeatEnvParams) {
+ if (paramName.equalsIgnoreCase(currHeatParam.getName())) {
+ String updatedParamValue = heatEnvParam.getCurrentValue();
+ if (updatedParamValue != null && updatedParamValue.equals("")) { // reset
+ heatEnvParam.setCurrentValue(heatEnvParam.getDefaultValue());
+ reducedHeatEnvParams.add(heatEnvParam);
+ } else if (updatedParamValue != null) {
+ reducedHeatEnvParams.add(heatEnvParam);
+ }
+ }
+ }
+ }
+ if (reducedHeatEnvParams.size() > 0) {
+ ArtifactDefinition reducedArtifactInfo = new ArtifactDefinition(artifactInfo);
+ reducedArtifactInfo.setHeatParameters(reducedHeatEnvParams);
+ Either<ArtifactDefinition, StorageOperationStatus> updateArtifactResult = artifactOperation.updateArifactOnResource(reducedArtifactInfo, componentId, artifactInfo.getUniqueId(), componentType.getNodeType(), inTransaction);
+
+ if (updateArtifactResult.isRight()) {
+ log.debug("Failed to update artifact on graph - {}", artifactId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateArtifactResult.right().value()));
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, ri.getName());
+ return Either.right(responseFormat);
+ }
+ ArtifactDefinition updatedArtifact = updateArtifactResult.left().value();
+ String updatedArtifactId = updatedArtifact.getUniqueId();
+ if (!currArtifact.getUniqueId().equals(updatedArtifactId)) {
+ currArtifact.setUniqueId(updatedArtifactId);
+ currArtifact.setPayloadUpdateDate(updatedArtifact.getPayloadUpdateDate());
+ currArtifact.setCreationDate(updatedArtifact.getCreationDate());
+ currArtifact.setLastUpdateDate(updatedArtifact.getLastUpdateDate());
+ currArtifact.setEsId(updatedArtifact.getEsId());
+ }
+ currArtifact.setHeatParamsUpdateDate(System.currentTimeMillis());
+
+ Either<List<HeatParameterDefinition>, StorageOperationStatus> heatParamsForEnv = ((org.openecomp.sdc.be.model.operations.impl.ArtifactOperation) artifactOperation).getHeatParamsForEnv(currArtifact);
+ if (heatParamsForEnv.isRight()) {
+ log.debug("failed to get heat parameters values for heat artifact {}", updatedArtifact.getUniqueId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(heatParamsForEnv.right().value()));
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, ri.getName());
+ return Either.right(responseFormat);
+ }
+ List<HeatParameterDefinition> updatedHeatParaetersList = heatParamsForEnv.left().value();
+ currArtifact.setHeatParameters(updatedHeatParaetersList);
+
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifactRes = artifactOperation.updateArifactDefinition(currArtifact, true);
+ if (updateArifactRes.isRight()) {
+ log.debug("Failed to update artifact on graph - {}", artifactId);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateArifactRes.right().value()));
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, ri.getName());
+ return Either.right(responseFormat);
+ }
+ }
+ }
+
+ insideEither = Either.left(currArtifact);
+ resultOp = Either.left(insideEither);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, currArtifact, null, artifactId, responseFormat, componentType, ri.getName());
+ return resultOp;
+ }
+
+ public Either<List<HeatParameterDefinition>, ResponseFormat> validateUploadParamsFromEnvFile(AuditingActionEnum auditingAction, Component parent, User user, ArtifactDefinition artifactInfo, String artifactId, ComponentTypeEnum componentType,
+ String riName, List<HeatParameterDefinition> currentHeatEnvParams, List<HeatParameterDefinition> updatedHeatEnvParams, String currArtifactName) {
+
+ if (updatedHeatEnvParams == null || updatedHeatEnvParams.isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT, artifactInfo.getArtifactName(), currArtifactName);
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, riName);
+ return Either.right(responseFormat);
+ }
+
+ for (HeatParameterDefinition uploadedHeatParam : updatedHeatEnvParams) {
+ String paramName = uploadedHeatParam.getName();
+ boolean isExistsInHeat = false;
+ for (HeatParameterDefinition currHeatParam : currentHeatEnvParams) {
+ if (paramName.equalsIgnoreCase(currHeatParam.getName())) {
+
+ isExistsInHeat = true;
+ uploadedHeatParam.setType(currHeatParam.getType());
+ uploadedHeatParam.setCurrentValue(uploadedHeatParam.getDefaultValue());
+ uploadedHeatParam.setDefaultValue(currHeatParam.getDefaultValue());
+ uploadedHeatParam.setUniqueId(currHeatParam.getUniqueId());
+ break;
+ }
+ }
+ if (!isExistsInHeat) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISMATCH_HEAT_VS_HEAT_ENV, currArtifactName);
+ handleAuditing(auditingAction, parent, parent.getUniqueId(), user, artifactInfo, null, artifactId, responseFormat, componentType, riName);
+ return Either.right(responseFormat);
+ }
+ }
+ return Either.left(updatedHeatEnvParams);
+ }
+
+ private Either<ComponentInstance, ResponseFormat> getRIFromComponent(Component component, String riID, String artifactId, AuditingActionEnum auditingAction, User user) {
+ ResponseFormat responseFormat = null;
+ List<ComponentInstance> ris = component.getComponentInstances();
+ for (ComponentInstance ri : ris) {
+ if (riID.equals(ri.getUniqueId())) {
+ return Either.left(ri);
+ }
+ }
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE, riID);
+ log.debug("Resource Instance not found, resourceInstanceId {}", riID);
+ handleAuditing(auditingAction, null, riID, user, null, null, artifactId, responseFormat, ComponentTypeEnum.RESOURCE_INSTANCE, null);
+ return Either.right(responseFormat);
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> getArtifactFromRI(Component component, ComponentInstance ri, String riID, String artifactId, AuditingActionEnum auditingAction, User user) {
+ ResponseFormat responseFormat = null;
+ Map<String, ArtifactDefinition> rtifactsMap = ri.getDeploymentArtifacts();
+ for (ArtifactDefinition artifact : rtifactsMap.values()) {
+ if (artifactId.equals(artifact.getUniqueId())) {
+ return Either.left(artifact);
+ }
+ }
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, riID, component.getUniqueId());
+ handleAuditing(auditingAction, component, riID, user, null, null, artifactId, responseFormat, ComponentTypeEnum.RESOURCE_INSTANCE, ri.getName());
+ return Either.right(responseFormat);
+ }
+
+ public ComponentOperation getComponentOperation(NodeTypeEnum componentType) {
+
+ switch (componentType) {
+ case Service:
+ case ResourceInstance:
+ return serviceOperation;
+ case Resource:
+ return resourceOperation;
+ default:
+ return null;
+ }
+ }
+
+ public ArtifactDefinition extractArtifactDefinition(Either<ArtifactDefinition, Operation> eitherArtifact) {
+ ArtifactDefinition ret;
+ if (eitherArtifact.isLeft()) {
+ ret = eitherArtifact.left().value();
+ } else {
+ ret = eitherArtifact.right().value().getImplementation();
+ }
+ return ret;
+ }
+
+ /**
+ * downloads artifact of component by UUIDs
+ *
+ * @param componentType
+ * @param componentUuid
+ * @param artifactUUID
+ * @param auditAdditionalParam
+ * @return
+ */
+ public Either<byte[], ResponseFormat> downloadComponentArtifactByUUIDs(ComponentTypeEnum componentType, String componentUuid, String artifactUUID, Map<AuditingFieldsKeysEnum, Object> auditAdditionalParam) {
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<byte[], ResponseFormat> result;
+ byte[] downloadedArtifact = null;
+ Component component = getLatestComponentByUuid(componentType, componentUuid, errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ auditAdditionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, component.getName());
+ downloadedArtifact = downloadArtifact(component.getDeploymentArtifacts(), artifactUUID, errorWrapper, component.getName());
+ }
+ if (errorWrapper.isEmpty()) {
+ result = Either.left(downloadedArtifact);
+ } else {
+ result = Either.right(errorWrapper.getInnerElement());
+ }
+ return result;
+ }
+
+ /**
+ * downloads an artifact of resource instance of component by UUIDs
+ *
+ * @param componentType
+ * @param componentUuid
+ * @param resourceName
+ * @param artifactUUID
+ * @param auditAdditionalParam
+ * @return
+ */
+ public Either<byte[], ResponseFormat> downloadResourceInstanceArtifactByUUIDs(ComponentTypeEnum componentType, String componentUuid, String resourceInstanceName, String artifactUUID, Map<AuditingFieldsKeysEnum, Object> auditAdditionalParam) {
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<byte[], ResponseFormat> result;
+ byte[] downloadedArtifact = null;
+ ComponentInstance resourceInstance = getRelatedComponentInstance(componentType, componentUuid, resourceInstanceName, errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ auditAdditionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInstance.getName());
+ downloadedArtifact = downloadArtifact(resourceInstance.getDeploymentArtifacts(), artifactUUID, errorWrapper, resourceInstance.getName());
+ }
+ if (errorWrapper.isEmpty()) {
+ result = Either.left(downloadedArtifact);
+ } else {
+ result = Either.right(errorWrapper.getInnerElement());
+ }
+ return result;
+ }
+
+ /**
+ * uploads an artifact to a component by UUID
+ *
+ * @param data
+ * @param request
+ * @param componentType
+ * @param componentUuid
+ * @param additionalParams
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> uploadArtifactToComponentByUUID(String data, HttpServletRequest request, ComponentTypeEnum componentType, String componentUuid, Map<AuditingFieldsKeysEnum, Object> additionalParams) {
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = null;
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactResult;
+ ArtifactDefinition uploadArtifact;
+ Component component = null;
+ String componentId = null;
+ ArtifactOperation operation = ArtifactOperation.Create;
+ operation.setExternalApi(true);
+ ArtifactDefinition artifactInfo = RepresentationUtils.convertJsonToArtifactDefinition(data, ArtifactDefinition.class);
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentMetadataByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ if (errorWrapper.isEmpty() && !getComponentRes.left().value().getMetadataDataDefinition().getState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ component = checkoutParentComponent(componentType, getComponentRes.left().value().getMetadataDataDefinition().getUniqueId(), userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ if (component != null) {
+ componentId = component.getUniqueId();
+ } else {
+ componentId = getComponentRes.left().value().getMetadataDataDefinition().getUniqueId();
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, getComponentRes.left().value().getMetadataDataDefinition().getName());
+ actionResult = handleArtifactRequest(componentId, userId, componentType, operation, null, artifactInfo, origMd5, data, null, null, null, null);
+ if (actionResult.isRight()) {
+ log.debug("Failed to upload artifact to component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, actionResult.right().value());
+ errorWrapper.setInnerElement(actionResult.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ uploadArtifact = actionResult.left().value().left().value();
+ updateAuditParametersWithArtifactDefinition(additionalParams, uploadArtifact);
+ uploadArtifactResult = Either.left(uploadArtifact);
+ } else {
+ uploadArtifactResult = Either.right(errorWrapper.getInnerElement());
+ }
+ return uploadArtifactResult;
+ }
+
+ /**
+ * upload an artifact to a resource instance by UUID
+ *
+ * @param data
+ * @param request
+ * @param componentType
+ * @param componentUuid
+ * @param resourceInstanceName
+ * @param additionalParams
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> uploadArtifactToRiByUUID(String data, HttpServletRequest request, ComponentTypeEnum componentType, String componentUuid, String resourceInstanceName,
+ Map<AuditingFieldsKeysEnum, Object> additionalParams) {
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactResult;
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = null;
+ ArtifactDefinition uploadArtifact;
+ Component component = null;
+ String componentInstanceId;
+ String componentId;
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ ImmutablePair<Component, ComponentInstance> componentRiPair = null;
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentMetadataByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ if (errorWrapper.isEmpty() && !getComponentRes.left().value().getMetadataDataDefinition().getState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ component = checkoutParentComponent(componentType, getComponentRes.left().value().getMetadataDataDefinition().getUniqueId(), userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ if (component == null) {
+ componentRiPair = getRelatedComponentComponentInstance(componentType, componentUuid, resourceInstanceName, errorWrapper);
+ } else {
+ componentRiPair = getRelatedComponentComponentInstance(component, resourceInstanceName, errorWrapper);
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ componentInstanceId = componentRiPair.getRight().getUniqueId();
+ componentId = componentRiPair.getLeft().getUniqueId();
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInstanceName);
+ ArtifactOperation operation = ArtifactOperation.Create;
+ operation.setExternalApi(true);
+ ArtifactDefinition artifactInfo = RepresentationUtils.convertJsonToArtifactDefinition(data, ArtifactDefinition.class);
+
+ actionResult = handleArtifactRequest(componentInstanceId, userId, ComponentTypeEnum.RESOURCE_INSTANCE, operation, null, artifactInfo, origMd5, data, null, null, componentId, ComponentTypeEnum.findParamByType(componentType));
+ if (actionResult.isRight()) {
+ log.debug("Failed to upload artifact to component instance {} of component with type {} and uuid {}. Status is {}. ", resourceInstanceName, componentType, componentUuid, actionResult.right().value());
+ errorWrapper.setInnerElement(actionResult.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ uploadArtifact = actionResult.left().value().left().value();
+ updateAuditParametersWithArtifactDefinition(additionalParams, uploadArtifact);
+ uploadArtifactResult = Either.left(uploadArtifact);
+ } else {
+ uploadArtifactResult = Either.right(errorWrapper.getInnerElement());
+ }
+ return uploadArtifactResult;
+ }
+
+ /**
+ * updates an artifact on a component by UUID
+ *
+ * @param data
+ * @param request
+ * @param componentType
+ * @param componentUuid
+ * @param artifactUUID
+ * @param additionalParams
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> updateArtifactOnComponentByUUID(String data, HttpServletRequest request, ComponentTypeEnum componentType, String componentUuid, String artifactUUID,
+ Map<AuditingFieldsKeysEnum, Object> additionalParams) {
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<ArtifactDefinition, ResponseFormat> updateArtifactResult;
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = null;
+ ArtifactDefinition updateArtifact;
+ Component component = null;
+ String componentId = null;
+ String artifactId = null;
+ ArtifactOperation operation = ArtifactOperation.Update;
+ operation.setExternalApi(true);
+ ArtifactDefinition artifactInfo = RepresentationUtils.convertJsonToArtifactDefinition(data, ArtifactDefinition.class);
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentMetadataByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ if (errorWrapper.isEmpty() && !getComponentRes.left().value().getMetadataDataDefinition().getState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ component = checkoutParentComponent(componentType, getComponentRes.left().value().getMetadataDataDefinition().getUniqueId(), userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ if (component != null) {
+ componentId = component.getUniqueId();
+ } else {
+ componentId = getComponentRes.left().value().getMetadataDataDefinition().getUniqueId();
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ artifactId = getLatestParentArtifactDataIdByArtifactUUID(artifactUUID, errorWrapper, componentId, componentType);
+ }
+ if (errorWrapper.isEmpty()) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, getComponentRes.left().value().getMetadataDataDefinition().getName());
+
+ actionResult = handleArtifactRequest(componentId, userId, componentType, operation, artifactId, artifactInfo, origMd5, data, null, null, null, null);
+ if (actionResult.isRight()) {
+ log.debug("Failed to upload artifact to component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, actionResult.right().value());
+ errorWrapper.setInnerElement(actionResult.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ updateArtifact = actionResult.left().value().left().value();
+ updateAuditParametersWithArtifactDefinition(additionalParams, updateArtifact);
+ updateArtifactResult = Either.left(updateArtifact);
+
+ } else {
+ updateArtifactResult = Either.right(errorWrapper.getInnerElement());
+ }
+ return updateArtifactResult;
+ }
+
+ /**
+ * updates an artifact on a resource instance by UUID
+ *
+ * @param data
+ * @param request
+ * @param componentType
+ * @param componentUuid
+ * @param resourceInstanceName
+ * @param artifactUUID
+ * @param additionalParams
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> updateArtifactOnRiByUUID(String data, HttpServletRequest request, ComponentTypeEnum componentType, String componentUuid, String resourceInstanceName, String artifactUUID,
+ Map<AuditingFieldsKeysEnum, Object> additionalParams) {
+
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<ArtifactDefinition, ResponseFormat> updateArtifactResult;
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = null;
+ ArtifactDefinition updateArtifact;
+ Component component = null;
+ String componentInstanceId = null;
+ String componentId = null;
+ String artifactId = null;
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ ImmutablePair<Component, ComponentInstance> componentRiPair = null;
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentMetadataByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ if (errorWrapper.isEmpty() && !getComponentRes.left().value().getMetadataDataDefinition().getState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ component = checkoutParentComponent(componentType, getComponentRes.left().value().getMetadataDataDefinition().getUniqueId(), userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ if (component == null) {
+ componentRiPair = getRelatedComponentComponentInstance(componentType, componentUuid, resourceInstanceName, errorWrapper);
+ } else {
+ componentRiPair = getRelatedComponentComponentInstance(component, resourceInstanceName, errorWrapper);
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ componentInstanceId = componentRiPair.getRight().getUniqueId();
+ componentId = componentRiPair.getLeft().getUniqueId();
+ artifactId = getLatestParentArtifactDataIdByArtifactUUID(artifactUUID, errorWrapper, componentInstanceId, ComponentTypeEnum.RESOURCE_INSTANCE);
+ }
+ if (errorWrapper.isEmpty()) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInstanceName);
+ ArtifactOperation operation = ArtifactOperation.Update;
+ operation.setExternalApi(true);
+ ArtifactDefinition artifactInfo = RepresentationUtils.convertJsonToArtifactDefinition(data, ArtifactDefinition.class);
+
+ actionResult = handleArtifactRequest(componentInstanceId, userId, ComponentTypeEnum.RESOURCE_INSTANCE, operation, artifactId, artifactInfo, origMd5, data, null, null, componentId, ComponentTypeEnum.findParamByType(componentType));
+ if (actionResult.isRight()) {
+ log.debug("Failed to upload artifact to component instance {} of component with type {} and uuid {}. Status is {}. ", resourceInstanceName, componentType, componentUuid, actionResult.right().value());
+ errorWrapper.setInnerElement(actionResult.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ updateArtifact = actionResult.left().value().left().value();
+ updateAuditParametersWithArtifactDefinition(additionalParams, updateArtifact);
+ updateArtifactResult = Either.left(updateArtifact);
+ } else {
+ updateArtifactResult = Either.right(errorWrapper.getInnerElement());
+ }
+ return updateArtifactResult;
+
+ }
+
+ /**
+ * deletes an artifact on a component by UUID
+ *
+ * @param request
+ * @param componentType
+ * @param componentUuid
+ * @param artifactUUID
+ * @param additionalParams
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> deleteArtifactOnComponentByUUID(HttpServletRequest request, ComponentTypeEnum componentType, String componentUuid, String artifactUUID, Map<AuditingFieldsKeysEnum, Object> additionalParams) {
+
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<ArtifactDefinition, ResponseFormat> deleteArtifactResult;
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = null;
+ ArtifactDefinition deleteArtifact;
+ Component component = null;
+ String componentId = null;
+ String artifactId = null;
+ ArtifactOperation operation = ArtifactOperation.Delete;
+ operation.setExternalApi(true);
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentMetadataByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ if (errorWrapper.isEmpty() && !getComponentRes.left().value().getMetadataDataDefinition().getState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ component = checkoutParentComponent(componentType, getComponentRes.left().value().getMetadataDataDefinition().getUniqueId(), userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ if (component != null) {
+ componentId = component.getUniqueId();
+ } else {
+ componentId = getComponentRes.left().value().getMetadataDataDefinition().getUniqueId();
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ artifactId = getLatestParentArtifactDataIdByArtifactUUID(artifactUUID, errorWrapper, componentId, componentType);
+ }
+ if (errorWrapper.isEmpty()) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, getComponentRes.left().value().getMetadataDataDefinition().getName());
+
+ actionResult = handleArtifactRequest(componentId, userId, componentType, operation, artifactId, null, origMd5, null, null, null, null, null);
+ if (actionResult.isRight()) {
+ log.debug("Failed to upload artifact to component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, actionResult.right().value());
+ errorWrapper.setInnerElement(actionResult.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ deleteArtifact = actionResult.left().value().left().value();
+ updateAuditParametersWithArtifactDefinition(additionalParams, deleteArtifact);
+ deleteArtifactResult = Either.left(deleteArtifact);
+ } else {
+ deleteArtifactResult = Either.right(errorWrapper.getInnerElement());
+ }
+ return deleteArtifactResult;
+ }
+
+ /**
+ * deletes an artifact an a resource instance by UUID
+ *
+ * @param request
+ * @param componentType
+ * @param componentUuid
+ * @param resourceInstanceName
+ * @param artifactUUID
+ * @param additionalParams
+ * @return
+ */
+ public Either<ArtifactDefinition, ResponseFormat> deleteArtifactOnRiByUUID(HttpServletRequest request, ComponentTypeEnum componentType, String componentUuid, String resourceInstanceName, String artifactUUID,
+ Map<AuditingFieldsKeysEnum, Object> additionalParams) {
+
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Either<ArtifactDefinition, ResponseFormat> deleteArtifactResult;
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = null;
+ ArtifactDefinition deleteArtifact;
+ Component component = null;
+ String componentInstanceId = null;
+ String componentId = null;
+ String artifactId = null;
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ ImmutablePair<Component, ComponentInstance> componentRiPair = null;
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentMetadataByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ if (errorWrapper.isEmpty() && !getComponentRes.left().value().getMetadataDataDefinition().getState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ component = checkoutParentComponent(componentType, getComponentRes.left().value().getMetadataDataDefinition().getUniqueId(), userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ if (component == null) {
+ componentRiPair = getRelatedComponentComponentInstance(componentType, componentUuid, resourceInstanceName, errorWrapper);
+ } else {
+ componentRiPair = getRelatedComponentComponentInstance(component, resourceInstanceName, errorWrapper);
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ componentInstanceId = componentRiPair.getRight().getUniqueId();
+ componentId = componentRiPair.getLeft().getUniqueId();
+ artifactId = getLatestParentArtifactDataIdByArtifactUUID(artifactUUID, errorWrapper, componentInstanceId, ComponentTypeEnum.RESOURCE_INSTANCE);
+ }
+ if (errorWrapper.isEmpty()) {
+
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInstanceName);
+ ArtifactOperation operation = ArtifactOperation.Delete;
+ operation.setExternalApi(true);
+
+ actionResult = handleArtifactRequest(componentInstanceId, userId, ComponentTypeEnum.RESOURCE_INSTANCE, operation, artifactId, null, origMd5, null, null, null, componentId, ComponentTypeEnum.findParamByType(componentType));
+
+ if (actionResult.isRight()) {
+ log.debug("Failed to upload artifact to component instance {} of component with type {} and uuid {}. Status is {}. ", resourceInstanceName, componentType, componentUuid, actionResult.right().value());
+ errorWrapper.setInnerElement(actionResult.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ deleteArtifact = actionResult.left().value().left().value();
+ updateAuditParametersWithArtifactDefinition(additionalParams, deleteArtifact);
+ deleteArtifactResult = Either.left(deleteArtifact);
+ } else {
+ deleteArtifactResult = Either.right(errorWrapper.getInnerElement());
+ }
+ return deleteArtifactResult;
+ }
+
+ private ComponentInstance getRelatedComponentInstance(ComponentTypeEnum componentType, String componentUuid, String resourceInstanceName, Wrapper<ResponseFormat> errorWrapper) {
+ ComponentInstance componentInstance = null;
+ StorageOperationStatus status;
+ Either<ComponentInstance, StorageOperationStatus> getResourceInstanceRes = null;
+ Component component = getLatestComponentByUuid(componentType, componentUuid, errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ componentInstance = component.getComponentInstances().stream().filter(ci -> ci.getNormalizedName().equals(resourceInstanceName)).findAny().get();
+ if (componentInstance == null) {
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER, resourceInstanceName, "resource instance", component.getComponentType().getValue(), component.getName()));
+ log.debug("Component instance {} was not found for component {}", resourceInstanceName, component.getName());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ getResourceInstanceRes = resourceInstanceOperation.getResourceInstanceById(componentInstance.getUniqueId());
+ if (getResourceInstanceRes.isRight()) {
+ status = getResourceInstanceRes.right().value();
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ componentInstance = getResourceInstanceRes.left().value();
+ getResourceInstanceRes = resourceInstanceOperation.getFullComponentInstance(componentInstance, componentType.getNodeType());
+ if (getResourceInstanceRes.isRight()) {
+ status = getResourceInstanceRes.right().value();
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ } else {
+ componentInstance = getResourceInstanceRes.left().value();
+ }
+ }
+ return componentInstance;
+ }
+
+ private ImmutablePair<Component, ComponentInstance> getRelatedComponentComponentInstance(Component component, String resourceInstanceName, Wrapper<ResponseFormat> errorWrapper) {
+
+ ImmutablePair<Component, ComponentInstance> relatedComponentComponentInstancePair = null;
+ ComponentInstance componentInstance = component.getComponentInstances().stream().filter(ci -> ci.getNormalizedName().equals(resourceInstanceName)).findAny().get();
+ if (componentInstance == null) {
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER, resourceInstanceName, "resource instance", component.getComponentType().getValue(), component.getName()));
+ log.debug("Component instance {} was not found for component {}", resourceInstanceName, component.getName());
+ } else {
+ relatedComponentComponentInstancePair = new ImmutablePair<>(component, componentInstance);
+ }
+ return relatedComponentComponentInstancePair;
+ }
+
+ private ImmutablePair<Component, ComponentInstance> getRelatedComponentComponentInstance(ComponentTypeEnum componentType, String componentUuid, String resourceInstanceName, Wrapper<ResponseFormat> errorWrapper) {
+ ComponentInstance componentInstance;
+ ImmutablePair<Component, ComponentInstance> relatedComponentComponentInstancePair = null;
+ Component component = getLatestComponentByUuid(componentType, componentUuid, errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ componentInstance = component.getComponentInstances().stream().filter(ci -> ci.getNormalizedName().equals(resourceInstanceName)).findAny().get();
+ if (componentInstance == null) {
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER, resourceInstanceName, "resource instance", component.getComponentType().getValue(), component.getName()));
+ log.debug("Component instance {} was not found for component {}", resourceInstanceName, component.getName());
+ } else {
+ relatedComponentComponentInstancePair = new ImmutablePair<>(component, componentInstance);
+ }
+ }
+ return relatedComponentComponentInstancePair;
+ }
+
+ private byte[] downloadArtifact(Map<String, ArtifactDefinition> artifacts, String artifactUUID, Wrapper<ResponseFormat> errorWrapper, String componentName) {
+
+ byte[] downloadedArtifact = null;
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> downloadArtifactEither = null;
+ List<ArtifactDefinition> deploymentArtifacts = null;
+ ArtifactDefinition deploymentArtifact = null;
+ if (artifacts != null && !artifacts.isEmpty()) {
+ deploymentArtifacts = artifacts.values().stream().filter(art -> art.getArtifactUUID() != null && art.getArtifactUUID().equals(artifactUUID)).collect(Collectors.toList());
+ }
+ if (deploymentArtifacts == null || deploymentArtifacts.isEmpty()) {
+ log.debug("Deployment artifact with uuid {} was not found for component {}", artifactUUID, componentName);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactUUID));
+ }
+ if (errorWrapper.isEmpty()) {
+ deploymentArtifact = deploymentArtifacts.get(0);
+ downloadArtifactEither = downloadArtifact(deploymentArtifact);
+ if (downloadArtifactEither.isRight()) {
+ log.debug("Failed to download artifact {}. ", deploymentArtifact.getArtifactName());
+ errorWrapper.setInnerElement(downloadArtifactEither.right().value());
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ log.trace("Succeeded to download artifact with uniqueId {}", deploymentArtifact.getUniqueId());
+ downloadedArtifact = downloadArtifactEither.left().value().getRight();
+ }
+ return downloadedArtifact;
+ }
+
+ private Component getLatestComponentByUuid(ComponentTypeEnum componentType, String componentUuid, Wrapper<ResponseFormat> errorWrapper) {
+ Component component = null;
+ Either<Component, StorageOperationStatus> getComponentRes = getComponentOperation(componentType).getLatestComponentByUuid(componentType.getNodeType(), componentUuid);
+ if (getComponentRes.isRight()) {
+ StorageOperationStatus status = getComponentRes.right().value();
+ log.debug("Could not fetch component with type {} and uuid {}. Status is {}. ", componentType, componentUuid, status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
+ } else {
+ component = getComponentRes.left().value();
+ }
+ return component;
+ }
+
+ private String getLatestParentArtifactDataIdByArtifactUUID(String artifactUUID, Wrapper<ResponseFormat> errorWrapper, String parentId, ComponentTypeEnum componentType) {
+ String artifactId = null;
+ ActionStatus actionStatus = ActionStatus.ARTIFACT_NOT_FOUND;
+ StorageOperationStatus storageStatus;
+ ArtifactDefinition latestArtifact = null;
+ List<ArtifactDefinition> artifacts = null;
+ NodeTypeEnum parentType;
+ if (componentType.equals(ComponentTypeEnum.RESOURCE)) {
+ parentType = NodeTypeEnum.Resource;
+ } else {
+ parentType = NodeTypeEnum.Service;
+ }
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifactsRes = artifactOperation.getArtifacts(parentId, parentType, false);
+ if (getArtifactsRes.isRight()) {
+ storageStatus = getArtifactsRes.right().value();
+ log.debug("Couldn't fetch artifacts data for parent component {} with uid {}, error: {}", componentType.name(), parentId, storageStatus);
+ if (!storageStatus.equals(StorageOperationStatus.NOT_FOUND)) {
+ actionStatus = componentsUtils.convertFromStorageResponse(storageStatus);
+ }
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(actionStatus, artifactUUID));
+ }
+ if (errorWrapper.isEmpty()) {
+ artifacts = getArtifactsRes.left().value().values().stream().filter(a -> a.getArtifactUUID() != null && a.getArtifactUUID().equals(artifactUUID)).collect(Collectors.toList());
+ if (artifacts == null || artifacts.isEmpty()) {
+ log.debug("Couldn't fetch artifact with UUID {} data for parent component {} with uid {}, error: {}", artifactUUID, componentType.name(), parentId, actionStatus);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(actionStatus, artifactUUID));
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ latestArtifact = artifacts.stream().max((a1, a2) -> {
+ int compareRes = Double.compare(Double.parseDouble(a1.getArtifactVersion()), Double.parseDouble(a2.getArtifactVersion()));
+ if (compareRes == 0) {
+ compareRes = Long.compare(a1.getLastUpdateDate() == null ? 0 : a1.getLastUpdateDate(), a2.getLastUpdateDate() == null ? 0 : a2.getLastUpdateDate());
+ }
+ return compareRes;
+ }).get();
+ if (latestArtifact == null) {
+ log.debug("Couldn't fetch latest artifact with UUID {} data for parent component {} with uid {}, error: {}", artifactUUID, componentType.name(), parentId, actionStatus);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(actionStatus, artifactUUID));
+ }
+ }
+ if (errorWrapper.isEmpty()) {
+ artifactId = latestArtifact.getUniqueId();
+ }
+ return artifactId;
+ }
+
+ private Component checkoutParentComponent(ComponentTypeEnum componentType, String parentId, String userId, Wrapper<ResponseFormat> errorWrapper) {
+
+ Component component = null;
+ Either<User, ActionStatus> getUserRes = userBusinessLogic.getUser(userId, false);
+ if (getUserRes.isRight()) {
+ log.debug("Could not fetch User of component {} with uid {} to checked out. Status is {}. ", componentType.getNodeType(), parentId, getUserRes.right().value());
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(getUserRes.right().value()));
+ }
+ if (errorWrapper.isEmpty()) {
+ User modifier = getUserRes.left().value();
+ LifecycleChangeInfoWithAction changeInfo = new LifecycleChangeInfoWithAction("External API checkout", LifecycleChanceActionEnum.UPDATE_FROM_EXTERNAL_API);
+ Either<? extends Component, ResponseFormat> checkoutRes = lifecycleBusinessLogic.changeComponentState(componentType, parentId, modifier, LifeCycleTransitionEnum.CHECKOUT, changeInfo, false, true);
+ if (checkoutRes.isRight()) {
+ log.debug("Could not change state of component {} with uid {} to checked out. Status is {}. ", componentType.getNodeType(), parentId, checkoutRes.right().value().getStatus());
+ errorWrapper.setInnerElement(checkoutRes.right().value());
+ } else {
+ component = checkoutRes.left().value();
+ }
+ }
+ return component;
+ }
+
+ private void updateAuditParametersWithArtifactDefinition(Map<AuditingFieldsKeysEnum, Object> additionalParams, ArtifactDefinition artifact) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID, artifact.getArtifactUUID());
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA, buildAuditingArtifactData(artifact));
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, artifact.getUpdaterFullName());
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AttributeBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AttributeBusinessLogic.java
new file mode 100644
index 0000000000..15fe86da33
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/AttributeBusinessLogic.java
@@ -0,0 +1,295 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+/**
+ * This class holds the business logic relevant for attributes manipulation.
+ *
+ * @author mshitrit
+ *
+ */
+@Component("attributeBusinessLogic")
+public class AttributeBusinessLogic extends BaseBusinessLogic {
+
+ private static final String CREATE_ATTRIBUTE = "CreateAttribute";
+ private static final String UPDATE_ATTRIBUTE = "UpdateAttribute";
+ private static final String DELETE_ATTRIBUTE = "DeleteAttribute";
+
+ private static Logger log = LoggerFactory.getLogger(AttributeBusinessLogic.class.getName());
+
+ /**
+ * Created attribute on the resource with resourceId
+ *
+ * @param resourceId
+ * @param newAttributeDef
+ * @param userId
+ * @return AttributeDefinition if created successfully Or ResponseFormat
+ */
+ public Either<AttributeDefinition, ResponseFormat> createAttribute(String resourceId, AttributeDefinition newAttributeDef, String userId) {
+ Either<AttributeDefinition, ResponseFormat> result = null;
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Attribute", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
+ if (lockResult != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_ATTRIBUTE, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
+ log.info("Failed to lock component {}. Error - {}", resourceId, lockResult);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ try {
+
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ }
+ Resource resource = status.left().value();
+
+ // verify that resource is checked-out and the user is the last updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ // verify attribute does not exist in resource
+ if (isAttributeExist(resource.getAttributes(), resourceId, newAttributeDef.getName())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ATTRIBUTE_ALREADY_EXIST, newAttributeDef.getName()));
+ }
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
+ if (eitherAllDataTypes.isRight()) {
+ return Either.right(eitherAllDataTypes.right().value());
+ }
+ // validate property default values
+ Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newAttributeDef, eitherAllDataTypes.left().value());
+ if (defaultValuesValidation.isRight()) {
+ return Either.right(defaultValuesValidation.right().value());
+ }
+
+ handleDefaultValue(newAttributeDef, eitherAllDataTypes.left().value());
+
+ // add the new attribute to resource on graph
+ // need to get StorageOpaerationStatus and convert to ActionStatus from
+ // componentsUtils
+ Either<AttributeData, StorageOperationStatus> either = attributeOperation.addAttribute(newAttributeDef, resourceId);
+ if (either.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
+ return result;
+ }
+
+ result = Either.left(attributeOperation.convertAttributeDataToAttributeDefinition(either.left().value(), newAttributeDef.getName(), resourceId));
+ return result;
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+
+ }
+
+ private boolean isAttributeExist(List<AttributeDefinition> attributes, String resourceUid, String propertyName) {
+ boolean isExist = false;
+ if (attributes != null) {
+ isExist = attributes.stream().filter(p -> Objects.equals(p.getName(), propertyName) && Objects.equals(p.getParentUniqueId(), resourceUid)).findAny().isPresent();
+ }
+ return isExist;
+
+ }
+
+ /**
+ * @param resourceId
+ * @param attributeId
+ * @param userId
+ * @return
+ */
+ public Either<AttributeDefinition, ResponseFormat> getAttribute(String resourceId, String attributeId, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Attribute", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ }
+ Resource resource = status.left().value();
+
+ List<AttributeDefinition> attributes = resource.getAttributes();
+ if (attributes == null) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ATTRIBUTE_NOT_FOUND, ""));
+ } else {
+ Either<AttributeDefinition, ResponseFormat> result;
+ // verify attribute exist in resource
+ Optional<AttributeDefinition> optionalAtt = attributes.stream().filter(att -> att.getUniqueId().equals(attributeId) && att.getParentUniqueId().equals(resourceId)).findAny();
+
+ if (optionalAtt.isPresent()) {
+ result = Either.left(optionalAtt.get());
+ } else {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.ATTRIBUTE_NOT_FOUND, ""));
+ }
+ return result;
+ }
+
+ }
+
+ /**
+ * Updates Attribute on resource
+ *
+ * @param resourceId
+ * @param attributeId
+ * @param newAttDef
+ * @param userId
+ * @return
+ */
+ public Either<AttributeDefinition, ResponseFormat> updateAttribute(String resourceId, String attributeId, AttributeDefinition newAttDef, String userId) {
+ Either<AttributeDefinition, ResponseFormat> result = null;
+
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
+ if (lockResult != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(UPDATE_ATTRIBUTE, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ try {
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> eitherResource = getResource(resourceId);
+ if (eitherResource.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ }
+ Resource resource = eitherResource.left().value();
+
+ // verify that resource is checked-out and the user is the last updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ // verify attribute exist in resource
+ Either<AttributeDefinition, ResponseFormat> eitherAttribute = getAttribute(resourceId, attributeId, userId);
+ if (eitherAttribute.isRight()) {
+ return Either.right(eitherAttribute.right().value());
+ }
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
+ if (eitherAllDataTypes.isRight()) {
+ return Either.right(eitherAllDataTypes.right().value());
+ }
+
+ // validate attribute default values
+ Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newAttDef, eitherAllDataTypes.left().value());
+ if (defaultValuesValidation.isRight()) {
+ return Either.right(defaultValuesValidation.right().value());
+ }
+ // add the new property to resource on graph
+ Either<AttributeData, StorageOperationStatus> eitherAttUpdate = attributeOperation.updateAttribute(attributeId, newAttDef, eitherAllDataTypes.left().value());
+
+ if (eitherAttUpdate.isRight()) {
+ log.debug("Problem while updating attribute with id {}. Reason - {}", attributeId, eitherAttUpdate.right().value());
+ result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherAttUpdate.right().value()), resource.getName()));
+ return result;
+ }
+
+ result = Either.left(attributeOperation.convertAttributeDataToAttributeDefinition(eitherAttUpdate.left().value(), newAttDef.getName(), resourceId));
+ return result;
+ } finally {
+ commitOrRollback(result);
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+ }
+
+ /**
+ * Deletes Attribute on resource
+ *
+ * @param resourceId
+ * @param attributeId
+ * @param userId
+ * @return
+ */
+ public Either<AttributeDefinition, ResponseFormat> deleteAttribute(String resourceId, String attributeId, String userId) {
+ Either<AttributeDefinition, ResponseFormat> result = null;
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Attribute", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
+ if (lockResult != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(DELETE_ATTRIBUTE, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+
+ try {
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> eitherResource = getResource(resourceId);
+ if (eitherResource.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ }
+ Resource resource = eitherResource.left().value();
+
+ // verify that resource is checked-out and the user is the last updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ // verify attribute exist in resource
+ Either<AttributeDefinition, ResponseFormat> eitherAttributeExist = getAttribute(resourceId, attributeId, userId);
+ if (eitherAttributeExist.isRight()) {
+ return Either.right(eitherAttributeExist.right().value());
+ }
+ String attributeName = eitherAttributeExist.left().value().getName();
+
+ // delete attribute of resource from graph
+ Either<AttributeData, StorageOperationStatus> eitherAttributeDelete = attributeOperation.deleteAttribute(attributeId);
+ if (eitherAttributeDelete.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(eitherAttributeDelete.right().value()), resource.getName()));
+ return result;
+ }
+ result = Either.left(attributeOperation.convertAttributeDataToAttributeDefinition(eitherAttributeDelete.left().value(), attributeName, resourceId));
+ return result;
+ } finally {
+ commitOrRollback(result);
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/BaseBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/BaseBusinessLogic.java
new file mode 100644
index 0000000000..93ddff38d5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/BaseBusinessLogic.java
@@ -0,0 +1,571 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.IComplexDefaultValue;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.IAttributeOperation;
+import org.openecomp.sdc.be.model.operations.api.IComponentOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
+import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.ProductOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+public abstract class BaseBusinessLogic {
+
+ @Autowired
+ protected ComponentsUtils componentsUtils;
+
+ @Autowired
+ protected IUserBusinessLogic userAdmin;
+
+ @Autowired
+ protected ResourceOperation resourceOperation;
+
+ @Autowired
+ protected IGraphLockOperation graphLockOperation;
+
+ @Autowired
+ protected ServiceOperation serviceOperation;
+
+ @Autowired
+ protected ProductOperation productOperation;
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+
+ @Autowired
+ protected IElementOperation elementDao;
+
+ @Autowired
+ protected IGroupOperation groupOperation;
+
+ @Autowired
+ protected IGroupTypeOperation groupTypeOperation;
+
+ @Autowired
+ protected IArtifactOperation artifactOperation;
+
+ @Autowired
+ protected IAttributeOperation attributeOperation;
+
+ @Autowired
+ protected IPropertyOperation propertyOperation;
+
+ @Autowired
+ protected ApplicationDataTypeCache applicationDataTypeCache;
+
+ public void setUserAdmin(UserBusinessLogic userAdmin) {
+ this.userAdmin = userAdmin;
+ }
+
+ public void setComponentsUtils(ComponentsUtils componentsUtils) {
+ this.componentsUtils = componentsUtils;
+ }
+
+ public void setGraphLockOperation(IGraphLockOperation graphLockOperation) {
+ this.graphLockOperation = graphLockOperation;
+ }
+
+ private static Logger log = LoggerFactory.getLogger(BaseBusinessLogic.class.getName());
+
+ protected Either<User, ResponseFormat> validateUserNotEmpty(User user, String ecompErrorContext) {
+ String userId = user.getUserId();
+
+ if (StringUtils.isEmpty(userId)) {
+ // user.setUserId("UNKNOWN");
+ log.debug("User header is missing ");
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUserMissingError, ecompErrorContext, user.getUserId());
+ BeEcompErrorManager.getInstance().logBeUserMissingError(ecompErrorContext, user.getUserId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ return Either.right(responseFormat);
+ }
+ return Either.left(user);
+ }
+
+ protected Either<User, ResponseFormat> validateUserExists(User user, String ecompErrorContext, boolean inTransaction) {
+ return validateUserExists(user.getUserId(), ecompErrorContext, inTransaction);
+ }
+
+ protected void validateUserExist(String userId, String ecompErrorContext, Wrapper<ResponseFormat> errorWrapper) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, ecompErrorContext, false);
+ if (resp.isRight()) {
+ errorWrapper.setInnerElement(resp.right().value());
+ }
+ }
+
+ public Either<User, ActionStatus> validateUserExistsActionStatus(String userId, String ecompErrorContext) {
+ Either<User, ActionStatus> eitherCreator = userAdmin.getUser(userId, false);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ if (eitherCreator.right().value().equals(ActionStatus.USER_NOT_FOUND)) {
+ log.debug("validateUserExists - not authorized user, userId {}", userId);
+ Either.right(ActionStatus.RESTRICTED_OPERATION);
+ } else {
+ log.debug("validateUserExists - failed to authorize user, userId {}", userId);
+ }
+ log.debug("User is not listed. userId {}", userId);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUserMissingError, ecompErrorContext, userId);
+ return Either.right(eitherCreator.right().value());
+ }
+ return Either.left(eitherCreator.left().value());
+ }
+
+ public Either<User, ResponseFormat> validateUserExists(String userId, String ecompErrorContext, boolean inTransaction) {
+ Either<User, ActionStatus> eitherCreator = userAdmin.getUser(userId, inTransaction);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ ResponseFormat responseFormat;
+ if (eitherCreator.right().value().equals(ActionStatus.USER_NOT_FOUND)) {
+ if (log.isDebugEnabled())
+ log.debug("validateUserExists - not authorized user, userId {}", userId);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ } else {
+ if (log.isDebugEnabled())
+ log.debug("validateUserExists - failed to authorize user, userId {}", userId);
+ responseFormat = componentsUtils.getResponseFormat(eitherCreator.right().value());
+ }
+ if (log.isDebugEnabled())
+ log.debug("User is not listed. userId {}", userId);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUserMissingError, ecompErrorContext, userId);
+ BeEcompErrorManager.getInstance().logBeUserMissingError(ecompErrorContext, userId);
+ return Either.right(responseFormat);
+ }
+ return Either.left(eitherCreator.left().value());
+ }
+
+ protected Either<Boolean, ResponseFormat> validateUserRole(User user, List<Role> roles) {
+ Role userRole = Role.valueOf(user.getRole());
+ if (roles != null) {
+ if (!roles.contains(userRole)) {
+ if (log.isDebugEnabled())
+ log.debug("user is not in appropriate role to perform action");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ return Either.right(responseFormat);
+ }
+ return Either.left(Boolean.TRUE);
+ }
+ return Either.left(Boolean.FALSE);
+ }
+
+ protected Either<Boolean, ResponseFormat> lockComponent(Component component, String ecompErrorContext) {
+ return lockComponent(component.getUniqueId(), component, ecompErrorContext);
+ }
+
+ protected Either<Boolean, ResponseFormat> lockComponent(String componentId, Component component, String ecompErrorContext) {
+ ComponentTypeEnum componentType = component.getComponentType();
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponent(componentId, nodeType);
+
+ if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
+ return Either.left(true);
+ } else {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(ecompErrorContext, nodeType.getName(), componentId);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(lockResourceStatus, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, component.getName());
+ log.debug("Failed to lock component {} error - {}", componentId, actionStatus);
+ return Either.right(responseFormat);
+ }
+ }
+
+ protected void unlockComponent(Either<?, ?> either, Component component, boolean inTransaction) {
+ ComponentTypeEnum componentType = component.getComponentType();
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ if (false == inTransaction) {
+ if (either == null || either.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ // unlock resource
+ graphLockOperation.unlockComponent(component.getUniqueId(), nodeType);
+ }
+
+ protected void unlockComponent(Either<?, ?> either, Component component) {
+ unlockComponent(either, component, false);
+ }
+
+ protected <T> Either<Boolean, ResponseFormat> validateJsonBody(T bodyObject, Class<T> clazz) {
+ if (bodyObject == null) {
+ log.debug("Invalid JSON received for object of type {}", clazz.getSimpleName());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ } else {
+ return Either.left(true);
+ }
+ }
+
+ protected Either<ComponentTypeEnum, ResponseFormat> validateComponentType(String componentType) {
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ if (componentTypeEnum == null) {
+ log.debug("Invalid component type {}", componentType);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, componentType));
+ } else {
+ return Either.left(componentTypeEnum);
+ }
+ }
+
+ protected Either<Component, ResponseFormat> validateComponentExists(String componentId, ComponentTypeEnum componentType, boolean inTransaction, boolean createNewTransaction) {
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+ Either<Component, StorageOperationStatus> componentFound = null;
+ // if(createNewTransaction){
+ // componentFound = componentOperation.getComponent_tx(componentId,
+ // inTransaction);
+ // }
+ // else{
+ componentFound = componentOperation.getComponent(componentId, inTransaction);
+ // }
+
+ if (componentFound.isRight()) {
+ StorageOperationStatus storageOperationStatus = componentFound.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageOperationStatus, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
+ log.debug("Component with id {} was not found", componentId);
+ return Either.right(responseFormat);
+ }
+ return Either.left(componentFound.left().value());
+ }
+
+ protected Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists(String componentId, ComponentTypeEnum componentType, ComponentParametersView componentParametersView, String userId,
+ AuditingActionEnum auditingAction, User user) {
+
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+
+ if (componentOperation == null) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ log.debug("addGroup - not supported component type {}", componentType);
+ // handleAuditing(auditingAction, null, componentId, user, null,
+ // null, artifactId, responseFormat, componentType, null);
+ return Either.right(responseFormat);
+ }
+ Either<? extends org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentResult = componentOperation.getComponent(componentId, componentParametersView, true);
+
+ if (componentResult.isRight()) {
+ ActionStatus status = (componentType.equals(ComponentTypeEnum.RESOURCE)) ? ActionStatus.RESOURCE_NOT_FOUND : ActionStatus.SERVICE_NOT_FOUND;
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(status, componentId);
+
+ log.debug("Service not found, serviceId {}", componentId);
+ // ComponentTypeEnum componentForAudit =
+ // (componentType.equals(ComponentTypeEnum.RESOURCE)) ?
+ // ComponentTypeEnum.RESOURCE : ComponentTypeEnum.SERVICE;
+ // handleAuditing(auditingAction, null, componentId, user, null,
+ // null, artifactId, responseFormat, componentForAudit, null);
+ return Either.right(responseFormat);
+ }
+ return Either.left(componentResult.left().value());
+ }
+
+ public Either<Boolean, ResponseFormat> validateCanWorkOnComponent(Component component, String userId) {
+ Either<Boolean, ResponseFormat> canWork = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ if (component.getLifecycleState() != LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT) {
+ log.debug("Component {} is not checked-out", component.getName());
+ return canWork;
+ }
+
+ // verify user id is not null
+ if (userId == null) {
+ log.debug("Current user userId is null");
+ return canWork;
+ }
+
+ // verify component last update user is the current user
+ String lastUpdaterUserId = component.getLastUpdaterUserId();
+ if (!userId.equals(lastUpdaterUserId)) {
+ log.debug("Current user is not last updater, last updater userId: {}, current user userId: {}", lastUpdaterUserId, userId);
+ return canWork;
+ }
+
+ // verify resource is not deleted
+ if ((component.getIsDeleted() != null) && (component.getIsDeleted() == true)) {
+ log.debug("Component {} is marked as deleted", component.getUniqueId());
+ return canWork;
+ }
+
+ return Either.left(true);
+ }
+
+ public ComponentOperation getComponentOperation(ComponentTypeEnum componentTypeEnum) {
+ if (ComponentTypeEnum.SERVICE == componentTypeEnum) {
+ return serviceOperation;
+ } else if (ComponentTypeEnum.RESOURCE == componentTypeEnum) {
+ return resourceOperation;
+ } else if (ComponentTypeEnum.PRODUCT == componentTypeEnum) {
+ return productOperation;
+ }
+ return null;
+ }
+
+ public IComponentOperation getIComponentOperation(ComponentTypeEnum componentTypeEnum) {
+
+ switch (componentTypeEnum) {
+ case SERVICE:
+ return serviceOperation;
+ case RESOURCE:
+ return resourceOperation;
+ case PRODUCT:
+ return productOperation;
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+ public ComponentOperation getComponentOperationByParentComponentType(ComponentTypeEnum parentComponentType) {
+ switch (parentComponentType) {
+ case SERVICE:
+ return resourceOperation;
+ case RESOURCE:
+ return resourceOperation;
+ case PRODUCT:
+ return serviceOperation;
+ default:
+ break;
+ }
+ return null;
+ }
+
+ public ComponentTypeEnum getComponentTypeByParentComponentType(ComponentTypeEnum parentComponentType) {
+ switch (parentComponentType) {
+ case SERVICE:
+ return ComponentTypeEnum.RESOURCE;
+ case RESOURCE:
+ return ComponentTypeEnum.RESOURCE;
+ case PRODUCT:
+ return ComponentTypeEnum.SERVICE;
+ default:
+ break;
+ }
+ return null;
+ }
+
+ // For UT
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ protected Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes(ApplicationDataTypeCache applicationDataTypeCache) {
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus operationStatus = allDataTypes.right().value();
+ if (operationStatus == TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logInternalDataError("FetchDataTypes", "Data types are not loaded", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.DATA_TYPE_CANNOT_BE_EMPTY));
+ } else {
+ BeEcompErrorManager.getInstance().logInternalFlowError("FetchDataTypes", "Failed to fetch data types", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+ return Either.left(allDataTypes.left().value());
+ }
+
+ protected Either<Boolean, ResponseFormat> validatePropertyDefaultValue(IComplexDefaultValue property, Map<String, DataTypeDefinition> dataTypes) {
+ log.debug("validate property");
+ String type = null;
+ String innerType = null;
+ if (!propertyOperation.isPropertyTypeValid(property)) {
+ log.info("Invalid type for property");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_TYPE, property.getType(), property.getName());
+ return Either.right(responseFormat);
+ }
+ type = property.getType();
+ if (type.equals(ToscaPropertyType.LIST.getType()) || type.equals(ToscaPropertyType.MAP.getType())) {
+ ImmutablePair<String, Boolean> propertyInnerTypeValid = propertyOperation.isPropertyInnerTypeValid(property, dataTypes);
+ innerType = propertyInnerTypeValid.getLeft();
+ if (!propertyInnerTypeValid.getRight().booleanValue()) {
+ log.info("Invalid inner type for property");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_INNER_TYPE, innerType, property.getName());
+ return Either.right(responseFormat);
+ }
+ }
+ if (!propertyOperation.isPropertyDefaultValueValid(property, dataTypes)) {
+ log.info("Invalid default value for property");
+ ResponseFormat responseFormat;
+ if (type.equals(ToscaPropertyType.LIST.getType()) || type.equals(ToscaPropertyType.MAP.getType())) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE, property.getName(), type, innerType, property.getDefaultValue());
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_DEFAULT_VALUE, property.getName(), type, property.getDefaultValue());
+ }
+ return Either.right(responseFormat);
+
+ }
+ return Either.left(true);
+ }
+
+ protected Either<Resource, StorageOperationStatus> getResource(final String resourceId) {
+
+ log.debug("Get resource with id {}", resourceId);
+ Either<Resource, StorageOperationStatus> status = resourceOperation.getResource(resourceId);
+ if (status.isRight()) {
+ log.debug("Resource with id {} was not found", resourceId);
+ return Either.right(status.right().value());
+ }
+
+ Resource resource = status.left().value();
+ if (resource == null) {
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("Property Business Logic", ComponentTypeEnum.RESOURCE.getValue(), resourceId);
+ log.debug("General Error while get resource with id {}", resourceId);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ return Either.left(resource);
+ }
+
+ protected void handleDefaultValue(IComplexDefaultValue newAttributeDef, Map<String, DataTypeDefinition> dataTypes) {
+ // convert property
+ ToscaPropertyType type = ToscaPropertyType.isValidType(newAttributeDef.getType());
+ PropertyValueConverter converter = type.getConverter();
+ // get inner type
+ String innerType = null;
+
+ if (newAttributeDef != null) {
+ SchemaDefinition schema = newAttributeDef.getSchema();
+ if (schema != null) {
+ PropertyDataDefinition prop = schema.getProperty();
+ if (schema.getProperty() != null) {
+ innerType = prop.getType();
+ }
+ }
+ String convertedValue = null;
+ if (newAttributeDef.getDefaultValue() != null) {
+ convertedValue = converter.convert(newAttributeDef.getDefaultValue(), innerType, dataTypes);
+ newAttributeDef.setDefaultValue(convertedValue);
+ }
+ }
+ }
+
+ protected void validateComponentTypeEnum(ComponentTypeEnum componentTypeEnum, String errorContext, Wrapper<ResponseFormat> errorWrapper) {
+ if (componentTypeEnum == null) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(errorContext, "invalid component type", ErrorSeverity.INFO);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.NOT_ALLOWED));
+ }
+
+ }
+
+ protected void validateCanWorkOnComponent(String componentId, ComponentTypeEnum componentTypeEnum, String userId, Wrapper<ResponseFormat> errorWrapper) {
+ IComponentOperation componentOperation = getIComponentOperation(componentTypeEnum);
+ if (!ComponentValidationUtils.canWorkOnComponent(componentId, componentOperation, userId)) {
+ log.info("Restricted operation for user {} on {} {}", userId, componentTypeEnum.getValue(), componentId);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ }
+
+ protected void validateComponentLock(String componentId, ComponentTypeEnum componentTypeEnum, Wrapper<ResponseFormat> errorWrapper) {
+ StorageOperationStatus lockStatus = graphLockOperation.lockComponent(componentId, componentTypeEnum.getNodeType());
+ if (lockStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to lock {} {}", componentTypeEnum.getValue(), componentId);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockStatus)));
+ }
+
+ }
+
+ protected ToscaPropertyType getType(String propertyType) {
+
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+
+ return type;
+
+ }
+
+ protected void commitOrRollback(Either<? extends Object, ResponseFormat> result) {
+ if (result == null || result.isRight()) {
+ log.warn("operation failed. do rollback");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ }
+
+ protected Either<Boolean, ResponseFormat> lockComponentByName(String name, Component component, String ecompErrorContext) {
+ ComponentTypeEnum componentType = component.getComponentType();
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponentByName(name, nodeType);
+
+ if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
+ return Either.left(true);
+ } else {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(ecompErrorContext, nodeType.getName(), name);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(lockResourceStatus, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, component.getName());
+ log.debug("Failed to lock component {} error - {}", name, actionStatus);
+ return Either.right(responseFormat);
+ }
+ }
+
+ protected Either<Component, ResponseFormat> validateComponentExistsByFilter(String componentId, ComponentTypeEnum componentType, ComponentParametersView componentParametersView, boolean inTransaction) {
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+ Either<Component, StorageOperationStatus> componentFound = null;
+ componentFound = componentOperation.getComponent(componentId, componentParametersView, inTransaction);
+
+ if (componentFound.isRight()) {
+ StorageOperationStatus storageOperationStatus = componentFound.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageOperationStatus, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
+ log.debug("Component with id {} was not found", componentId);
+ return Either.right(responseFormat);
+ }
+ return Either.left(componentFound.left().value());
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java
new file mode 100644
index 0000000000..5cf42cedd6
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManager.java
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("capabilityTypeImportManager")
+public class CapabilityTypeImportManager {
+
+ private static Logger log = LoggerFactory.getLogger(CapabilityTypeImportManager.class.getName());
+ @Resource
+ private CapabilityTypeOperation capabilityTypeOperation;
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Resource
+ private CommonImportManager commonImportManager;
+
+ public Either<List<CapabilityTypeDefinition>, ResponseFormat> createCapabilityTypes(String capabilityYml) {
+ Either<List<CapabilityTypeDefinition>, ActionStatus> capabilityTypes = createCapabilityTypesFromYml(capabilityYml);
+ if (capabilityTypes.isRight()) {
+ ActionStatus status = capabilityTypes.right().value();
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByCapabilityType(status, null);
+ return Either.right(responseFormat);
+ }
+ return createCapabilityTypesByDao(capabilityTypes.left().value());
+
+ }
+
+ private Either<List<CapabilityTypeDefinition>, ActionStatus> createCapabilityTypesFromYml(String capabilityYml) {
+ return commonImportManager.createElementTypesFromYml(capabilityYml, (capTypeName, capTypeJsonData) -> createCapabilityType(capTypeName, capTypeJsonData));
+
+ }
+
+ private Either<List<CapabilityTypeDefinition>, ResponseFormat> createCapabilityTypesByDao(List<CapabilityTypeDefinition> capabilityTypesToCreate) {
+ List<CapabilityTypeDefinition> createdCapabilities = new ArrayList<>();
+ Either<List<CapabilityTypeDefinition>, ResponseFormat> eitherResult = Either.left(createdCapabilities);
+ Iterator<CapabilityTypeDefinition> capTypeItr = capabilityTypesToCreate.iterator();
+ boolean stopDao = false;
+ while (capTypeItr.hasNext() && !stopDao) {
+ CapabilityTypeDefinition capabilityType = capTypeItr.next();
+
+ log.info("send capabilityType {} to dao for create", capabilityType.getType());
+ Either<CapabilityTypeDefinition, StorageOperationStatus> dataModelResponse = capabilityTypeOperation.addCapabilityType(capabilityType);
+ if (dataModelResponse.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedAddingCapabilityTypeError, "Create CapabilityTypes");
+ BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("Create CapabilityTypes", "capability type");
+ log.debug("failed to create capabilityType: {}", capabilityType.getType());
+ if (dataModelResponse.right().value() != StorageOperationStatus.SCHEMA_VIOLATION) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByCapabilityType(componentsUtils.convertFromStorageResponseForCapabilityType(dataModelResponse.right().value()), capabilityType);
+ eitherResult = Either.right(responseFormat);
+ stopDao = true;
+ }
+
+ } else {
+ createdCapabilities.add(capabilityType);
+ }
+ if (!capTypeItr.hasNext()) {
+ log.info("capabilityTypes were created successfully!!!");
+ }
+
+ }
+
+ return eitherResult;
+
+ }
+
+ private CapabilityTypeDefinition createCapabilityType(String capabilityTypeName, Map<String, Object> toscaJson) {
+ CapabilityTypeDefinition capabilityType = new CapabilityTypeDefinition();
+
+ capabilityType.setType(capabilityTypeName);
+
+ // Description
+ final Consumer<String> descriptionSetter = description -> capabilityType.setDescription(description);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DESCRIPTION.getElementName(), descriptionSetter);
+ // Derived From
+ final Consumer<String> derivedFromSetter = derivedFrom -> capabilityType.setDerivedFrom(derivedFrom);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DERIVED_FROM.getElementName(), derivedFromSetter);
+ // Properties
+ commonImportManager.setPropertiesMap(toscaJson, (values) -> capabilityType.setProperties(values));
+
+ return capabilityType;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CategoriesImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CategoriesImportManager.java
new file mode 100644
index 0000000000..26ea80ac4b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CategoriesImportManager.java
@@ -0,0 +1,274 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
+import org.openecomp.sdc.be.datamodel.utils.NodeTypeConvertUtils;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.Yaml;
+
+import fj.data.Either;
+
+@Component("categoriesImportManager")
+public class CategoriesImportManager {
+
+ @javax.annotation.Resource
+ private IElementOperation elementOperation;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ private static Logger log = LoggerFactory.getLogger(CategoriesImportManager.class.getName());
+
+ public Either<Map<String, List<CategoryDefinition>>, ResponseFormat> createCategories(String categoriesTypesYml) {
+
+ Map<String, List<CategoryDefinition>> allCategories = createCategoriesFromYml(categoriesTypesYml);
+ return createCategoriesByDao(allCategories);
+ }
+
+ private Either<Map<String, List<CategoryDefinition>>, ResponseFormat> createCategoriesByDao(Map<String, List<CategoryDefinition>> allCategories) {
+ Map<String, List<CategoryDefinition>> result = new HashMap<>();
+ log.debug("createCategoriesByDao: starting to create Categories.");
+ for (Map.Entry<String, List<CategoryDefinition>> entry : allCategories.entrySet()) {
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(entry.getKey());
+ NodeTypeEnum nodeTypeCategory = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentType, CategoryTypeEnum.CATEGORY);
+ NodeTypeEnum nodeTypeSubCategory = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentType, CategoryTypeEnum.SUBCATEGORY);
+ NodeTypeEnum nodeTypeGroup = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentType, CategoryTypeEnum.GROUPING);
+ if (log.isDebugEnabled()) {
+ log.debug("createCategoriesByDao: creating componentType:{} nodeTypeCategory:{} nodeTypeSubCategory:{} nodeTypeGroup:{}", componentType, nodeTypeCategory, nodeTypeSubCategory, nodeTypeGroup);
+ }
+ List<CategoryDefinition> newCategoriesvalue = new ArrayList<>();
+ for (CategoryDefinition category : entry.getValue()) {
+
+ Either<CategoryDefinition, ResponseFormat> createdCategoryRes = createCategorieDeo(entry, category, nodeTypeCategory);
+ if (createdCategoryRes.isRight()) {
+ return Either.right(createdCategoryRes.right().value());
+ }
+
+ CategoryDefinition newcategory = createdCategoryRes.left().value();
+ String categoryId = newcategory.getUniqueId();
+ log.debug("createCategoriesByDao: create category was successful {}", newcategory);
+ List<SubCategoryDefinition> newsubcategories = new ArrayList<>();
+ List<SubCategoryDefinition> subcategories = category.getSubcategories();
+ if (subcategories != null) {
+ for (SubCategoryDefinition subcategory : subcategories) {
+ Either<SubCategoryDefinition, ResponseFormat> createdSubCategory = createSubCategorieDeo(entry, newcategory, subcategory, nodeTypeSubCategory);
+ if (createdSubCategory.isRight()) {
+ return Either.right(createdCategoryRes.right().value());
+ }
+ SubCategoryDefinition newsubcategory = createdSubCategory.left().value();
+ List<GroupingDefinition> groupings = subcategory.getGroupings();
+ if (groupings != null) {
+ List<GroupingDefinition> newgroupings = new ArrayList<>();
+ for (GroupingDefinition grouping : groupings) {
+ Either<GroupingDefinition, ResponseFormat> createdGrouping = createGroupingDeo(entry, grouping, subcategory, category, nodeTypeGroup);
+ if (createdGrouping.isRight()) {
+ return Either.right(createdCategoryRes.right().value());
+ }
+ newgroupings.add(createdGrouping.left().value());
+ }
+ newsubcategory.setGroupings(newgroupings);
+ }
+ newsubcategories.add(newsubcategory);
+ }
+ newcategory.setSubcategories(newsubcategories);
+ }
+ newCategoriesvalue.add(newcategory);
+ }
+ result.put(entry.getKey(), newCategoriesvalue);
+ }
+ return Either.left(result);
+ }
+
+ private Either<GroupingDefinition, ResponseFormat> createGroupingDeo(Map.Entry<String, List<CategoryDefinition>> entry, GroupingDefinition grouping, SubCategoryDefinition subcategory, CategoryDefinition category, NodeTypeEnum nodeTypeGroup) {
+
+ log.debug("createGroupingDeo: creating grouping {}", grouping);
+ Either<GroupingDefinition, ActionStatus> createdGrouping = elementOperation.createGrouping(subcategory.getUniqueId(), grouping, nodeTypeGroup);
+ if (createdGrouping.isRight()) {
+ if (ActionStatus.COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY.equals(createdGrouping.right().value())) {
+ log.debug(" create grouping for {}. group {} already exists", entry.getKey(), grouping.getName());
+ String groupingId = UniqueIdBuilder.buildGroupingUid(grouping.getUniqueId(), grouping.getNormalizedName());
+ createdGrouping = elementOperation.getGroupingUniqueForType(nodeTypeGroup, groupingId);
+ if (createdGrouping.isRight()) {
+ log.debug("failed to get grouping that exists groupingId: {}, type: {}", groupingId, nodeTypeGroup);
+ return Either.right(componentsUtils.getResponseFormat(createdGrouping.right().value()));
+ }
+ }
+ log.debug("Failed to create groupingcategory for {}, category {}, subcategory {}, grouping {}, error {}", entry.getKey(), category.getName(), subcategory.getName(), (grouping != null ? grouping.getName() : null),
+ (createdGrouping != null && createdGrouping.right() != null ? createdGrouping.right().value() : null));
+ return Either.right(componentsUtils.getResponseFormat(createdGrouping.right().value()));
+ } else {
+ log.debug("createGroupingDeo: create Grouping was successful {}", createdGrouping.left().value());
+ }
+ return Either.left(createdGrouping.left().value());
+
+ }
+
+ private Either<SubCategoryDefinition, ResponseFormat> createSubCategorieDeo(Map.Entry<String, List<CategoryDefinition>> entry, CategoryDefinition newcategory, SubCategoryDefinition subcategory, NodeTypeEnum nodeTypeSubCategory) {
+ log.debug("createSubCategorieDeo: creating subcategory {}", subcategory);
+ Either<SubCategoryDefinition, ActionStatus> createdSubCategory = elementOperation.createSubCategory(newcategory.getUniqueId(), subcategory, nodeTypeSubCategory);
+ if (createdSubCategory.isRight()) {
+ if (ActionStatus.COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY.equals(createdSubCategory.right().value())) {
+ log.debug(" create subcategory for {} category {}, alreay exists retrieving", entry.getKey(), newcategory.getName(), subcategory.getName());
+ String subCategoryId = UniqueIdBuilder.buildSubCategoryUid(newcategory.getUniqueId(), subcategory.getNormalizedName());
+ createdSubCategory = elementOperation.getSubCategory(nodeTypeSubCategory, subCategoryId);
+ if (createdSubCategory.isRight()) {
+ log.debug("failed to get sub category that exists subCategoryId: {}, type: {}", subCategoryId, nodeTypeSubCategory);
+ return Either.right(componentsUtils.getResponseFormat(createdSubCategory.right().value()));
+ }
+ } else {
+ log.debug("Failed to create subcategory for {} category {}, error {}", entry.getKey(), newcategory.getName(), subcategory.getName(), createdSubCategory.right().value());
+ return Either.right(componentsUtils.getResponseFormat(createdSubCategory.right().value()));
+ }
+ } else {
+ log.debug("createSubCategorieDeo: create subcategory was successful {}", createdSubCategory.left().value());
+ }
+ return Either.left(createdSubCategory.left().value());
+ }
+
+ private Either<CategoryDefinition, ResponseFormat> createCategorieDeo(Map.Entry<String, List<CategoryDefinition>> entry, CategoryDefinition category, NodeTypeEnum nodeTypeCategory) {
+ log.debug("createCategorieDeo: creating category {}", category);
+ Either<CategoryDefinition, ActionStatus> createdCategory = elementOperation.createCategory(category, nodeTypeCategory);
+ if (createdCategory.isRight()) {
+ log.debug("Failed to create category for {}, error {}", entry.getKey(), category.getName(), createdCategory.right().value());
+ if (!ActionStatus.COMPONENT_CATEGORY_ALREADY_EXISTS.equals(createdCategory.right().value())) {
+ return Either.right(componentsUtils.getResponseFormat(createdCategory.right().value()));
+ } else {
+ log.debug("createCategorieDeo: category exists {} retriving.", category);
+ String categoryId = UniqueIdBuilder.buildCategoryUid(category.getNormalizedName(), nodeTypeCategory);
+ createdCategory = elementOperation.getCategory(nodeTypeCategory, categoryId);
+ if (createdCategory.isRight()) {
+ log.debug("failed to get category that exists categoryId: {}, type: {}", categoryId, nodeTypeCategory);
+ return Either.right(componentsUtils.getResponseFormat(createdCategory.right().value()));
+ }
+ }
+ } else {
+ log.debug("createCategorieDeo: create category was successful {}", createdCategory.left().value());
+ }
+ return Either.left(createdCategory.left().value());
+ }
+
+ private Map<String, List<CategoryDefinition>> createCategoriesFromYml(String categoriesTypesYml) {
+ Map<String, Object> toscaJson = (Map<String, Object>) new Yaml().load(categoriesTypesYml);
+ Map<String, List<CategoryDefinition>> allCategories = new HashMap<>();
+
+ Iterator<Entry<String, Object>> categoryEntryItr = toscaJson.entrySet().iterator();
+ while (categoryEntryItr.hasNext()) {
+ Entry<String, Object> categoryTypeEntry = categoryEntryItr.next();
+ String categoryType = categoryTypeEntry.getKey();
+ List<CategoryDefinition> categoriesPerType = null;
+ Map<String, Object> categoryPerType = null;
+ switch (categoryType) {
+ case ComponentTypeEnum.SERVICE_PARAM_NAME:
+ categoryPerType = (Map<String, Object>) categoryTypeEntry.getValue();
+ categoriesPerType = createServiceCategories(categoryPerType);
+ break;
+ case ComponentTypeEnum.RESOURCE_PARAM_NAME:
+ categoryPerType = (Map<String, Object>) categoryTypeEntry.getValue();
+ categoriesPerType = createResourceCategories(categoryPerType);
+ break;
+ case ComponentTypeEnum.PRODUCT_PARAM_NAME:
+ // TODO
+ break;
+ default:
+ log.debug("Not supported category type - {}", categoryType);
+ break;
+ }
+ if (categoriesPerType != null) {
+ allCategories.put(categoryType, categoriesPerType);
+ }
+ }
+ return allCategories;
+ }
+
+ private List<CategoryDefinition> createServiceCategories(Map<String, Object> categories) {
+ List<CategoryDefinition> categroiesDef = new ArrayList<>();
+ String catName = null;
+ List<String> icons = null;
+ for (Entry<String, Object> entry : categories.entrySet()) {
+ CategoryDefinition catDef = new CategoryDefinition();
+ Map<String, Object> category = (Map<String, Object>) entry.getValue();
+ catName = (String) category.get("name");
+ catDef.setName(catName);
+ icons = (List<String>) category.get("icons");
+ catDef.setIcons(icons);
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(catName);
+ catDef.setNormalizedName(normalizedName);
+ categroiesDef.add(catDef);
+ }
+
+ return categroiesDef;
+ }
+
+ private List<CategoryDefinition> createResourceCategories(Map<String, Object> categoryPerType) {
+ List<CategoryDefinition> categroiesDef = new ArrayList<>();
+ for (Map.Entry<String, Object> entry : categoryPerType.entrySet()) {
+ Map<String, Object> category = (Map<String, Object>) entry.getValue();
+ CategoryDefinition catDef = new CategoryDefinition();
+ String catName = (String) category.get("name");
+ catDef.setName(catName);
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(catName);
+ catDef.setNormalizedName(normalizedName);
+ Map<String, Object> subcategories = (Map<String, Object>) category.get("subcategories");
+ List<SubCategoryDefinition> subcateDef = new ArrayList<>();
+ for (Entry<String, Object> subcategory : subcategories.entrySet()) {
+ Map<String, Object> subcategoryInfo = (Map<String, Object>) subcategory.getValue();
+ SubCategoryDefinition subDef = new SubCategoryDefinition();
+ String subcategoryName = (String) subcategoryInfo.get("name");
+ subDef.setName(subcategoryName);
+ List<String> subcategoryIcons = (List<String>) subcategoryInfo.get("icons");
+ subDef.setIcons(subcategoryIcons);
+ normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(subcategoryName);
+ subDef.setNormalizedName(normalizedName);
+ subcateDef.add(subDef);
+ }
+
+ catDef.setSubcategories(subcateDef);
+ categroiesDef.add(catDef);
+ }
+ return categroiesDef;
+ }
+
+ public static void setLog(Logger log) {
+ CategoriesImportManager.log = log;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java
new file mode 100644
index 0000000000..9b99b665f7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java
@@ -0,0 +1,309 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import jersey.repackaged.com.google.common.base.Function;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.Yaml;
+
+import fj.data.Either;
+
+@Component("commonImportManager")
+public class CommonImportManager {
+
+ private static Logger log = LoggerFactory.getLogger(CommonImportManager.class.getName());
+
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Resource
+ private PropertyOperation propertyOperation;
+
+ protected void setProperties(Map<String, Object> toscaJson, Consumer<List<PropertyDefinition>> consumer) {
+ consumer.accept(getProperties(toscaJson));
+ }
+
+ private List<PropertyDefinition> getProperties(Map<String, Object> toscaJson) {
+ List<PropertyDefinition> values = null;
+ Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = ImportUtils.getProperties(toscaJson);
+
+ if (properties.isLeft()) {
+ values = new ArrayList<>();
+ Map<String, PropertyDefinition> propertiesMap = properties.left().value();
+ if (propertiesMap != null && propertiesMap.isEmpty() == false) {
+
+ for (Entry<String, PropertyDefinition> entry : propertiesMap.entrySet()) {
+ String propName = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
+ newPropertyDefinition.setName(propName);
+ values.add(newPropertyDefinition);
+ }
+ }
+ }
+
+ return values;
+ }
+
+ protected void setPropertiesMap(Map<String, Object> toscaJson, Consumer<Map<String, PropertyDefinition>> consumer) {
+ final List<PropertyDefinition> properties = getProperties(toscaJson);
+ if (properties != null) {
+ Map<String, PropertyDefinition> collect = properties.stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
+ consumer.accept(collect);
+ }
+
+ }
+
+ interface ICreateElementType<T1, T2, ElementType> {
+ ElementType createElement(T1 firstArg, T2 secondArg);
+ }
+
+ protected <ElementDefinition> Either<List<ElementDefinition>, ActionStatus> createElementTypesFromYml(String elementTypesYml, ICreateElementType<String, Map<String, Object>, ElementDefinition> createApi) {
+
+ List<ElementDefinition> elementTypes = new ArrayList<>();
+ try {
+ Map<String, Object> toscaJson = (Map<String, Object>) new Yaml().load(elementTypesYml);
+
+ Iterator<Entry<String, Object>> elementTypesEntryItr = toscaJson.entrySet().iterator();
+ while (elementTypesEntryItr.hasNext()) {
+ Entry<String, Object> elementTypeNameDataEntry = elementTypesEntryItr.next();
+ String elementTypeName = elementTypeNameDataEntry.getKey();
+ Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
+ ElementDefinition elementDefinition = createApi.createElement(elementTypeName, elementTypeJsonData);
+ elementTypes.add(elementDefinition);
+
+ }
+
+ } catch (Exception e) {
+ log.debug("Failed to yaml file {} {}", elementTypesYml, e);
+ return Either.right(ActionStatus.INVALID_YAML_FILE);
+ }
+ return Either.left(elementTypes);
+ }
+
+ protected <FieldType> void setField(Map<String, Object> toscaJson, String fieldName, Consumer<FieldType> setter) {
+ if (toscaJson.containsKey(fieldName)) {
+ FieldType fieldValue = (FieldType) toscaJson.get(fieldName);
+ setter.accept(fieldValue);
+ }
+
+ }
+
+ enum ElementTypeEnum {
+ PolicyType, GroupType, DataType, CapabilityType, InterfaceLifecycleType
+ };
+
+ private ActionStatus convertFromStorageResponseForElementType(StorageOperationStatus status, ElementTypeEnum elementTypeEnum) {
+ ActionStatus ret;
+ switch (elementTypeEnum) {
+ case GroupType:
+ ret = componentsUtils.convertFromStorageResponseForGroupType(status);
+ break;
+ case DataType:
+ ret = componentsUtils.convertFromStorageResponseForDataType(status);
+ break;
+ case CapabilityType:
+ ret = componentsUtils.convertFromStorageResponseForCapabilityType(status);
+ break;
+ case InterfaceLifecycleType:
+ ret = componentsUtils.convertFromStorageResponseForLifecycleType(status);
+ break;
+ default:
+ ret = componentsUtils.convertFromStorageResponse(status);
+ break;
+ }
+ return ret;
+ }
+
+ private <ElementTypeDefinition> ResponseFormat getResponseFormatForElementType(ActionStatus actionStatus, ElementTypeEnum elementTypeEnum, ElementTypeDefinition elementTypeDefinition) {
+ ResponseFormat ret;
+ switch (elementTypeEnum) {
+ case GroupType:
+ ret = componentsUtils.getResponseFormatByGroupType(actionStatus, (GroupTypeDefinition) elementTypeDefinition);
+ break;
+ case PolicyType:
+ ret = componentsUtils.getResponseFormatByPolicyType(actionStatus, (PolicyTypeDefinition) elementTypeDefinition);
+ break;
+ case DataType:
+ ret = componentsUtils.getResponseFormatByDataType(actionStatus, (DataTypeDefinition) elementTypeDefinition, null);
+ break;
+ case CapabilityType:
+ ret = componentsUtils.getResponseFormatByCapabilityType(actionStatus, (CapabilityTypeDefinition) elementTypeDefinition);
+ break;
+
+ default:
+ ret = componentsUtils.getResponseFormat(actionStatus);
+ break;
+ }
+ return ret;
+ }
+
+ protected <ElementTypeDefinition> Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> createElementTypesByDao(List<ElementTypeDefinition> elementTypesToCreate,
+ Function<ElementTypeDefinition, Either<ActionStatus, ResponseFormat>> validator, Function<ElementTypeDefinition, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
+ Function<String, Either<ElementTypeDefinition, StorageOperationStatus>> elementFetcher, Function<ElementTypeDefinition, Either<ElementTypeDefinition, StorageOperationStatus>> elementAdder,
+ BiFunction<ElementTypeDefinition, ElementTypeDefinition, Either<ElementTypeDefinition, StorageOperationStatus>> elementUpgrader) {
+
+ List<ImmutablePair<ElementTypeDefinition, Boolean>> createdElementTypes = new ArrayList<>();
+
+ Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> eitherResult = Either.left(createdElementTypes);
+
+ Iterator<ElementTypeDefinition> elementTypeItr = elementTypesToCreate.iterator();
+
+ try {
+
+ while (elementTypeItr.hasNext()) {
+ ElementTypeDefinition elementType = elementTypeItr.next();
+ final ImmutablePair<ElementTypeEnum, String> elementInfo = elementInfoGetter.apply(elementType);
+ ElementTypeEnum elementTypeEnum = elementInfo.left;
+ String elementName = elementInfo.right;
+
+ Either<ActionStatus, ResponseFormat> validateElementType = validator.apply(elementType);
+ if (validateElementType.isRight()) {
+ ResponseFormat responseFormat = validateElementType.right().value();
+ log.debug("Failed in validation of element type {}. Response is {}", elementType, responseFormat.getFormattedMessage());
+ eitherResult = Either.right(responseFormat);
+ break;
+ }
+
+ log.info("send {} : {} to dao for create", elementTypeEnum.name(), elementName);
+
+ Either<ElementTypeDefinition, StorageOperationStatus> findElementType = elementFetcher.apply(elementName);
+ if (findElementType.isRight()) {
+ StorageOperationStatus status = findElementType.right().value();
+ log.debug("searched {} finished with result:{}", elementTypeEnum.name(), status.name());
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(status, elementTypeEnum), elementTypeEnum, elementType);
+ eitherResult = Either.right(responseFormat);
+ break;
+ } else {
+ Either<ElementTypeDefinition, StorageOperationStatus> dataModelResponse = elementAdder.apply(elementType);
+
+ if (dataModelResponse.isRight()) {
+ try {
+ BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("Create {}", elementTypeEnum.name());
+ log.debug("failed to create {}: {}", elementTypeEnum.name(), elementName);
+ if (dataModelResponse.right().value() != StorageOperationStatus.SCHEMA_VIOLATION) {
+ ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(dataModelResponse.right().value(), elementTypeEnum), elementTypeEnum, elementType);
+
+ eitherResult = Either.right(responseFormat);
+ break;
+ } else {
+ createdElementTypes.add(new ImmutablePair<ElementTypeDefinition, Boolean>(elementType, false));
+ }
+ } finally {
+ propertyOperation.getTitanGenericDao().rollback();
+ }
+ } else {
+ propertyOperation.getTitanGenericDao().commit();
+ createdElementTypes.add(new ImmutablePair<ElementTypeDefinition, Boolean>(elementType, true));
+ log.debug("{} : {} was created successfully.", elementTypeEnum.name(), elementName);
+ }
+ if (!elementTypeItr.hasNext()) {
+ log.info("all {} were created successfully!!!", elementTypeEnum.name());
+ }
+
+ }
+ } else {
+
+ if (elementUpgrader != null) {
+ Either<ElementTypeDefinition, StorageOperationStatus> upgradeResponse = null;
+ try {
+ upgradeResponse = elementUpgrader.apply(elementType, findElementType.left().value());
+ if (upgradeResponse.isRight()) {
+ StorageOperationStatus status = upgradeResponse.right().value();
+ if (status == StorageOperationStatus.OK) {
+ createdElementTypes.add(new ImmutablePair<ElementTypeDefinition, Boolean>(elementType, false));
+ } else {
+ ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(upgradeResponse.right().value(), elementTypeEnum), elementTypeEnum, elementType);
+ eitherResult = Either.right(responseFormat);
+ break;
+ }
+ } else {
+ log.debug("{} : {} was upgraded successfully.", elementTypeEnum.name(), elementName);
+ createdElementTypes.add(new ImmutablePair<ElementTypeDefinition, Boolean>(elementType, true));
+ }
+ } finally {
+ if (upgradeResponse == null || upgradeResponse.isRight()) {
+ propertyOperation.getTitanGenericDao().rollback();
+ } else {
+ propertyOperation.getTitanGenericDao().commit();
+ }
+ }
+
+ } else {
+ // mshitrit Once GroupType Versions are supported add
+ // code here
+ createdElementTypes.add(new ImmutablePair<ElementTypeDefinition, Boolean>(elementType, false));
+ log.debug("{} : {} already exists.", elementTypeEnum.name(), elementName);
+ }
+
+ }
+
+ }
+ } finally {
+ if (eitherResult.isRight()) {
+ propertyOperation.getTitanGenericDao().rollback();
+ }
+ }
+
+ return eitherResult;
+
+ }
+
+ public <ElementTypeDefinition> Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> createElementTypes(String elementTypesYml, Function<String, Either<List<ElementTypeDefinition>, ActionStatus>> elementTypeFromYmlCreater,
+ Function<List<ElementTypeDefinition>, Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat>> elementTypeDaoCreater, ElementTypeEnum elementTypeEnum) {
+
+ Either<List<ElementTypeDefinition>, ActionStatus> elementTypes = elementTypeFromYmlCreater.apply(elementTypesYml);
+ if (elementTypes.isRight()) {
+ ActionStatus status = elementTypes.right().value();
+ ResponseFormat responseFormat = getResponseFormatForElementType(status, elementTypeEnum, null);
+ return Either.right(responseFormat);
+ }
+ return elementTypeDaoCreater.apply(elementTypes.left().value());
+
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java
new file mode 100644
index 0000000000..6c7b8b9bc7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentBusinessLogic.java
@@ -0,0 +1,860 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datamodel.api.HighestFilterEnum;
+import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.cache.ComponentCache;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+public abstract class ComponentBusinessLogic extends BaseBusinessLogic {
+
+ @Autowired
+ protected ArtifactsBusinessLogic artifactsBusinessLogic;
+
+ @Autowired
+ protected ComponentCache componentCache;
+
+ private static Logger log = LoggerFactory.getLogger(ComponentBusinessLogic.class.getName());
+
+ private static final String TAG_FIELD_LABEL = "tag";
+
+ public abstract Either<List<String>, ResponseFormat> deleteMarkedComponents();
+
+ public abstract ComponentInstanceBusinessLogic getComponentInstanceBL();
+
+ public abstract Either<List<ComponentInstance>, ResponseFormat> getComponentInstancesFilteredByPropertiesAndInputs(String componentId, ComponentTypeEnum componentTypeEnum, String userId, String searchText);
+
+ protected Either<User, ResponseFormat> validateUser(User user, String ecompErrorContext, Component component, AuditingActionEnum auditAction, boolean inTransaction) {
+ Either<User, ResponseFormat> userValidationResult = validateUserNotEmpty(user, ecompErrorContext);
+ ResponseFormat responseFormat;
+ if (userValidationResult.isRight()) {
+ user.setUserId("UNKNOWN");
+ responseFormat = userValidationResult.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", auditAction, component.getComponentType());
+ return Either.right(responseFormat);
+ }
+ Either<User, ResponseFormat> userResult = validateUserExists(user, ecompErrorContext, inTransaction);
+ if (userResult.isRight()) {
+ responseFormat = userResult.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", auditAction, component.getComponentType());
+ return Either.right(responseFormat);
+ }
+ user = userResult.left().value();
+ return userResult;
+ }
+
+ protected Either<Boolean, ResponseFormat> validateUserRole(User user, Component component, List<Role> roles, AuditingActionEnum auditAction, String comment) {
+ if (roles != null && roles.isEmpty()) {
+ roles.add(Role.ADMIN);
+ roles.add(Role.DESIGNER);
+ }
+ Either<Boolean, ResponseFormat> validationResult = validateUserRole(user, roles);
+ if (validationResult.isRight()) {
+ ComponentTypeEnum componentType = component.getComponentType();
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+ if (componentType.equals(ComponentTypeEnum.SERVICE)) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, comment);
+ String distributionStatus = ((ServiceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getDistributionStatus();
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS, distributionStatus);
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS, distributionStatus);
+ }
+ componentsUtils.auditComponent(validationResult.right().value(), user, component, "", "", auditAction, componentType, additionalParams);
+ }
+ return validationResult;
+ }
+
+ protected Either<Boolean, ResponseFormat> validateComponentName(User user, Component component, AuditingActionEnum actionEnum) {
+ ComponentTypeEnum type = component.getComponentType();
+ String componentName = component.getName();
+ if (!ValidationUtils.validateStringNotEmpty(componentName)) {
+ log.debug("component name is empty");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_COMPONENT_NAME, type.getValue());
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+
+ if (!ValidationUtils.validateComponentNameLength(componentName)) {
+ log.debug("Component name exceeds max length {} ", ValidationUtils.COMPONENT_NAME_MAX_LENGTH);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT, type.getValue(), "" + ValidationUtils.COMPONENT_NAME_MAX_LENGTH);
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+
+ if (!validateTagPattern(componentName)) {
+ log.debug("Component name {} has invalid format", componentName);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_COMPONENT_NAME, type.getValue());
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+ component.setNormalizedName(ValidationUtils.normaliseComponentName(componentName));
+ component.setSystemName(ValidationUtils.convertToSystemName(componentName));
+
+ return Either.left(true);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateDescriptionAndCleanup(User user, Component component, AuditingActionEnum actionEnum) {
+ ComponentTypeEnum type = component.getComponentType();
+ String description = component.getDescription();
+ if (!ValidationUtils.validateStringNotEmpty(description)) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_DESCRIPTION, type.getValue());
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+
+ description = ValidationUtils.removeNoneUtf8Chars(description);
+ description = ValidationUtils.normaliseWhitespace(description);
+ description = ValidationUtils.stripOctets(description);
+ description = ValidationUtils.removeHtmlTagsOnly(description);
+
+ Either<Boolean, ResponseFormat> validatDescription = validateComponentDescription(description, type);
+ if (validatDescription.isRight()) {
+ ResponseFormat responseFormat = validatDescription.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", actionEnum, type);
+ return Either.right(responseFormat);
+ }
+ component.setDescription(description);
+ return Either.left(true);
+ }
+
+ public Either<Boolean, ResponseFormat> validateComponentDescription(String description, ComponentTypeEnum type) {
+ if (description != null) {
+ if (!ValidationUtils.validateDescriptionLength(description)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT, type.getValue(), "" + ValidationUtils.COMPONENT_DESCRIPTION_MAX_LENGTH));
+ }
+
+ if (!ValidationUtils.validateIsEnglish(description)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_DESCRIPTION, type.getValue()));
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateComponentNameUnique(User user, Component component, AuditingActionEnum actionEnum) {
+ ComponentTypeEnum type = component.getComponentType();
+ ComponentOperation componentOperation = getComponentOperation(type);
+ Either<Boolean, StorageOperationStatus> dataModelResponse;
+ dataModelResponse = componentOperation.validateComponentNameExists(component.getName());
+
+ if (dataModelResponse.isLeft()) {
+ if (dataModelResponse.left().value()) {
+ return Either.left(true);
+ } else {
+ log.info("Component with name {} already exists", component.getName());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, type.getValue(), component.getName());
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+ }
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "validateComponentNameUnique");
+ BeEcompErrorManager.getInstance().logBeSystemError("validateComponentNameUnique");
+ log.debug("Error while validateComponentNameUnique for component: {}", component.getName());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateContactId(User user, Component component, AuditingActionEnum actionEnum) {
+ log.debug("validate component contact info");
+ ComponentTypeEnum type = component.getComponentType();
+ String contactId = component.getContactId();
+
+ if (!ValidationUtils.validateStringNotEmpty(contactId)) {
+ log.info("contact info is missing.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT, type.getValue());
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+
+ Either<Boolean, ResponseFormat> validateContactIdResponse = validateContactId(contactId, type);
+ if (validateContactIdResponse.isRight()) {
+ ResponseFormat responseFormat = validateContactIdResponse.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", actionEnum, type);
+ }
+ return validateContactIdResponse;
+ }
+
+ private Either<Boolean, ResponseFormat> validateContactId(String contactId, ComponentTypeEnum type) {
+ if (contactId != null) {
+ if (!ValidationUtils.validateContactId(contactId)) {
+ log.info("contact info is invalid.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_CONTACT, type.getValue());
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateIcon(User user, Component component, AuditingActionEnum actionEnum) {
+ log.debug("validate Icon");
+ ComponentTypeEnum type = component.getComponentType();
+ String icon = component.getIcon();
+ if (!ValidationUtils.validateStringNotEmpty(icon)) {
+ log.info("icon is missing.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_ICON, type.getValue());
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, type);
+ return Either.right(errorResponse);
+ }
+
+ Either<Boolean, ResponseFormat> validateIcon = validateIcon(icon, type);
+ if (validateIcon.isRight()) {
+ ResponseFormat responseFormat = validateIcon.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", actionEnum, type);
+ }
+ return validateIcon;
+ }
+
+ private Either<Boolean, ResponseFormat> validateIcon(String icon, ComponentTypeEnum type) {
+ if (icon != null) {
+ if (!ValidationUtils.validateIconLength(icon)) {
+ log.debug("icon exceeds max length");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT, type.getValue(), "" + ValidationUtils.ICON_MAX_LENGTH));
+ }
+
+ if (!ValidationUtils.validateIcon(icon)) {
+ log.info("icon is invalid.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_ICON, type.getValue());
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateTagsListAndRemoveDuplicates(User user, Component component, AuditingActionEnum actionEnum) {
+ List<String> tagsList = component.getTags();
+
+ Either<Boolean, ResponseFormat> validateTags = validateComponentTags(tagsList, component.getName(), component.getComponentType());
+ if (validateTags.isRight()) {
+ ResponseFormat responseFormat = validateTags.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", actionEnum, component.getComponentType());
+ return Either.right(responseFormat);
+ }
+ ValidationUtils.removeDuplicateFromList(tagsList);
+ return Either.left(true);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateComponentTags(List<String> tags, String name, ComponentTypeEnum componentType) {
+ log.debug("validate component tags");
+ boolean includesComponentName = false;
+ int tagListSize = 0;
+ if (tags != null && !tags.isEmpty()) {
+ for (String tag : tags) {
+ if (!ValidationUtils.validateTagLength(tag)) {
+ log.debug("tag length exceeds limit {}", ValidationUtils.TAG_MAX_LENGTH);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT, "" + ValidationUtils.TAG_MAX_LENGTH));
+ }
+ if (validateTagPattern(tag)) {
+ if (!includesComponentName) {
+ includesComponentName = name.equals(tag);
+ }
+ } else {
+ log.debug("invalid tag {}", tag);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_FIELD_FORMAT, componentType.getValue(), TAG_FIELD_LABEL));
+ }
+ tagListSize += tag.length() + 1;
+ }
+ if (tagListSize > 0) {
+ tagListSize--;
+ }
+
+ if (!includesComponentName) {
+ log.debug("tags must include component name");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME));
+ }
+ if (!ValidationUtils.validateTagListLength(tagListSize)) {
+ log.debug("overall tags length exceeds limit {}", ValidationUtils.TAG_LIST_MAX_LENGTH);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT, "" + ValidationUtils.TAG_LIST_MAX_LENGTH));
+ }
+ return Either.left(true);
+ }
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_TAGS));
+ }
+
+ protected boolean validateTagPattern(String tag) {
+ return ValidationUtils.validateComponentNamePattern(tag);
+ }
+
+ protected Either<Boolean, ResponseFormat> validateProjectCode(User user, Component component, AuditingActionEnum actionEnum) {
+ if (ComponentTypeEnum.RESOURCE.equals(component.getComponentType())) {
+ return Either.left(true);
+ }
+ log.debug("validate PROJECT_CODE name ");
+ String projectCode = component.getProjectCode();
+
+ if (!ValidationUtils.validateStringNotEmpty(projectCode)) {
+ log.info("projectCode is missing.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_PROJECT_CODE);
+ componentsUtils.auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, component.getComponentType());
+ return Either.right(errorResponse);
+ }
+
+ Either<Boolean, ResponseFormat> validateProjectCodeResponse = validateProjectCode(projectCode);
+ if (validateProjectCodeResponse.isRight()) {
+ ResponseFormat responseFormat = validateProjectCodeResponse.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, component, "", "", actionEnum, component.getComponentType());
+ }
+ return validateProjectCodeResponse;
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateProjectCode(String projectCode) {
+ if (projectCode != null) {
+ if (!ValidationUtils.validateProjectCode(projectCode)) {
+ log.info("projectCode is not valid.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_PROJECT_CODE);
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+ }
+
+ protected void checkComponentFieldsForOverrideAttempt(Component component) {
+ if (component.getLifecycleState() != null) {
+ log.info("LifecycleState cannot be defined by user. This field will be overridden by the application");
+ }
+ if (component.getVersion() != null) {
+ log.info("Version cannot be defined by user. This field will be overridden by the application");
+ }
+ if ((component.getCreatorUserId() != null) || (component.getCreatorFullName() != null)) {
+ log.info("Creator cannot be defined by user. This field will be overridden by the application");
+ }
+ if ((component.getLastUpdaterUserId() != null) || (component.getLastUpdaterFullName() != null)) {
+ log.info("Last Updater cannot be defined by user. This field will be overridden by the application");
+ }
+ if ((component.getCreationDate() != null)) {
+ log.info("Creation Date cannot be defined by user. This field will be overridden by the application");
+ }
+ if ((component.isHighestVersion() != null)) {
+ log.info("Is Highest Version cannot be defined by user. This field will be overridden by the application");
+ }
+ if ((component.getUUID() != null)) {
+ log.info("UUID cannot be defined by user. This field will be overridden by the application");
+ }
+ if ((component.getLastUpdateDate() != null)) {
+ log.info("Last Update Date cannot be defined by user. This field will be overridden by the application");
+ }
+ if (component.getUniqueId() != null) {
+ log.info("uid cannot be defined by user. This field will be overridden by the application.");
+ component.setUniqueId(null);
+ }
+ if (component.getInvariantUUID() != null) {
+ log.info("Invariant UUID cannot be defined by user. This field will be overridden by the application.");
+ }
+ }
+
+ protected Either<Boolean, ResponseFormat> validateComponentFieldsBeforeCreate(User user, Component component, AuditingActionEnum actionEnum) {
+ // validate component name uniqueness
+ log.debug("validate component name ");
+ Either<Boolean, ResponseFormat> componentNameValidation = validateComponentName(user, component, actionEnum);
+ if (componentNameValidation.isRight()) {
+ return componentNameValidation;
+ }
+
+ // validate description
+ log.debug("validate description");
+ Either<Boolean, ResponseFormat> descValidation = validateDescriptionAndCleanup(user, component, actionEnum);
+ if (descValidation.isRight()) {
+ return descValidation;
+ }
+
+ // validate tags
+ log.debug("validate tags");
+ Either<Boolean, ResponseFormat> tagsValidation = validateTagsListAndRemoveDuplicates(user, component, actionEnum);
+ if (tagsValidation.isRight()) {
+ return tagsValidation;
+ }
+
+ // validate contact info
+ log.debug("validate contact info");
+ Either<Boolean, ResponseFormat> contactIdValidation = validateContactId(user, component, actionEnum);
+ if (contactIdValidation.isRight()) {
+ return contactIdValidation;
+ }
+
+ // validate icon
+ log.debug("validate icon");
+ Either<Boolean, ResponseFormat> iconValidation = validateIcon(user, component, actionEnum);
+ if (iconValidation.isRight()) {
+ return iconValidation;
+ }
+ return Either.left(true);
+ }
+
+ /***
+ * Fetches Component From the DB
+ *
+ * @param componentId
+ * @param componentTypeEnum
+ * @return
+ */
+ public <R extends Component> Either<R, StorageOperationStatus> getComponent(String componentId, ComponentTypeEnum componentTypeEnum) {
+ ComponentOperation componentOperation = getComponentOperation(componentTypeEnum);
+ Either<R, StorageOperationStatus> eitherComponent = componentOperation.getComponent(componentId, false);
+ return eitherComponent;
+ }
+
+ public Either<CapReqDef, ResponseFormat> getRequirementsAndCapabilities(String componentId, ComponentTypeEnum componentTypeEnum, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Component Instance", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Map<String, List<CapabilityDefinition>> capabilities = new HashMap<>();
+ Map<String, List<RequirementDefinition>> requirements = new HashMap<>();
+ Either<CapReqDef, ResponseFormat> eitherRet;
+ ComponentOperation componentOperation = getComponentOperation(componentTypeEnum);
+ Either<Component, ResponseFormat> eitherComponent = validateComponentExists(componentId, componentTypeEnum, false, true);
+ if (eitherComponent.isLeft()) {
+ Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> eitherCapabilities = componentOperation.getCapabilities(eitherComponent.left().value(), componentTypeEnum.getNodeType(), false);
+ if (eitherCapabilities.isRight()) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ eitherRet = Either.right(errorResponse);
+ } else {
+ Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> eitherRequirements = componentOperation.getRequirements(eitherComponent.left().value(), componentTypeEnum.getNodeType(), false);
+ if (eitherRequirements.isRight()) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ eitherRet = Either.right(errorResponse);
+ } else {
+ requirements = eitherRequirements.left().value();
+ capabilities = eitherCapabilities.left().value();
+ eitherRet = Either.left(new CapReqDef(requirements, capabilities));
+ }
+ }
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeResourceMissingError, "getRequirementsAndCapabilities", componentId);
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("getRequirementsAndCapabilities", componentTypeEnum.getValue(), componentId);
+ eitherRet = Either.right(eitherComponent.right().value());
+ }
+
+ return eitherRet;
+ }
+
+ public Either<List<Component>, ResponseFormat> getLatestVersionNotAbstractComponents(boolean isAbstractAbstract, HighestFilterEnum highestFilter, ComponentTypeEnum componentTypeEnum, String internalComponentType, List<String> componentUids,
+ String userId) {
+
+ long startUser = System.currentTimeMillis();
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Latest Version Not Abstract Components", false);
+ long endUser = System.currentTimeMillis();
+ log.debug("Activation time of get user {} ms", (endUser - startUser));
+ ResponseFormat responseFormat;
+ if (resp.isLeft()) {
+
+ List<Component> result = new ArrayList<Component>();
+ Set<String> nonProcessesComponents = new HashSet<>();
+ nonProcessesComponents.addAll(componentUids);
+
+ long startGetComp = System.currentTimeMillis();
+ // Read components from cache
+ Set<String> filteredComponents = new HashSet<>();
+ filteredComponents.addAll(componentUids);
+
+ Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> allPartialComponents = componentCache.getComponentsForLeftPanel(componentTypeEnum, internalComponentType, filteredComponents);
+
+ if (allPartialComponents.isRight()) {
+ log.debug("Components was not fetched from cache. Status is {}", allPartialComponents.right().value());
+ } else {
+ ImmutableTriple<List<Component>, List<Component>, Set<String>> immutableTriple = allPartialComponents.left().value();
+ List<Component> processedComponents = immutableTriple.left;
+ if (processedComponents != null) {
+ result.addAll(processedComponents);
+ }
+ List<Component> dirtyComponents = immutableTriple.middle;
+ if (dirtyComponents != null) {
+ result.addAll(dirtyComponents);
+ }
+
+ Set<String> nonProcessesComponentsFromCache = immutableTriple.right;
+ nonProcessesComponents = nonProcessesComponentsFromCache;
+ }
+ long endGetComp = System.currentTimeMillis();
+ log.debug("Activation time of get Comp from cache {} ms", (endGetComp - startGetComp));
+
+ // Fecth non cached components
+ List<String> componentsUidToFetch = new ArrayList<String>();
+ componentsUidToFetch.addAll(nonProcessesComponents);
+
+ long startGetCompFromGraph = System.currentTimeMillis();
+ if (componentsUidToFetch.size() > 0) {
+ log.debug("Number of Components to fetch from graph is {}", componentsUidToFetch.size());
+ ComponentOperation componentOperation = getComponentOperation(componentTypeEnum);
+ Boolean isHighest = isHighest(highestFilter);
+ Either<List<Component>, StorageOperationStatus> nonCheckoutCompResponse = componentOperation.getLatestVersionNotAbstractComponents(isAbstractAbstract, isHighest, componentTypeEnum, internalComponentType, componentsUidToFetch);
+
+ if (nonCheckoutCompResponse.isLeft()) {
+ log.debug("Retrived Resource successfully.");
+ result.addAll(nonCheckoutCompResponse.left().value());
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(nonCheckoutCompResponse.right().value()));
+ }
+ }
+ long endGetCompFromGraph = System.currentTimeMillis();
+ log.debug("Activation time of get Comp from graph {} ms", (endGetCompFromGraph - startGetCompFromGraph));
+
+ return Either.left(result);
+ } else {
+ responseFormat = resp.right().value();
+ }
+
+ return Either.right(responseFormat);
+ }
+
+ private Boolean isHighest(HighestFilterEnum highestFilter) {
+ Boolean isHighest = null;
+ switch (highestFilter) {
+ case ALL:
+ break;
+ case HIGHEST_ONLY:
+ isHighest = true;
+ break;
+ case NON_HIGHEST_ONLY:
+ isHighest = false;
+ break;
+ default:
+ break;
+ }
+ return isHighest;
+ }
+
+ public Either<List<Map<String, String>>, ResponseFormat> getLatestVersionNotAbstractComponentsUidOnly(boolean isAbstractAbstract, HighestFilterEnum highestFilter, ComponentTypeEnum componentTypeEnum, String internalComponentType, String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Latest Version Not Abstract Components", false);
+ ResponseFormat responseFormat;
+ if (resp.isLeft()) {
+
+ ComponentOperation componentOperation = getComponentOperation(componentTypeEnum);
+ Boolean isHighest = isHighest(highestFilter);
+ Either<Collection<ComponentMetadataData>, StorageOperationStatus> nonCheckoutCompResponse = componentOperation.getLatestVersionNotAbstractComponentsMetadataOnly(isAbstractAbstract, isHighest, componentTypeEnum, internalComponentType);
+
+ if (nonCheckoutCompResponse.isLeft()) {
+ log.debug("Retrived Resource successfully.");
+ List<Map<String, String>> res = new ArrayList<>();
+
+ // Map<String,String>resMap =
+ // nonCheckoutCompResponse.left().value().stream().collect()
+ // .collect(Collectors.toMap(
+ // p -> p.getMetadataDataDefinition().getUniqueId(),
+ // p-> p.getMetadataDataDefinition().getVersion()));
+
+ res = nonCheckoutCompResponse.left().value().stream().map(p -> {
+ HashMap<String, String> map = new HashMap<>();
+ map.put("uid", p.getMetadataDataDefinition().getUniqueId());
+ map.put("version", p.getMetadataDataDefinition().getVersion());
+ Long lastUpdateDate = p.getMetadataDataDefinition().getLastUpdateDate();
+ String lastUpdateDateStr = lastUpdateDate != null ? String.valueOf(lastUpdateDate.longValue()) : "0";
+ map.put("timestamp", lastUpdateDateStr);
+ return map;
+ }).collect(Collectors.toList());
+
+ return Either.left(res);
+ }
+ responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(nonCheckoutCompResponse.right().value()));
+ } else {
+ responseFormat = resp.right().value();
+ }
+
+ return Either.right(responseFormat);
+ }
+
+ public void setDeploymentArtifactsPlaceHolder(Component component, User user) {
+
+ }
+
+ public void setToscaArtifactsPlaceHolders(Component component, User user) {
+ Map<String, ArtifactDefinition> artifactMap = component.getToscaArtifacts();
+ if (artifactMap == null) {
+ artifactMap = new HashMap<String, ArtifactDefinition>();
+ }
+ String componentUniqueId = component.getUniqueId();
+ String componentSystemName = component.getSystemName();
+ String componentType = component.getComponentType().getValue().toLowerCase();
+ Map<String, Object> toscaArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getToscaArtifacts();
+
+ if (toscaArtifacts != null) {
+ for (Entry<String, Object> artifactInfoMap : toscaArtifacts.entrySet()) {
+ Map<String, Object> artifactInfo = (Map<String, Object>) artifactInfoMap.getValue();
+ ArtifactDefinition artifactDefinition = artifactsBusinessLogic.createArtifactPlaceHolderInfo(componentUniqueId, artifactInfoMap.getKey(), artifactInfo, user, ArtifactGroupTypeEnum.TOSCA);
+ artifactDefinition.setArtifactName(componentType + "-" + componentSystemName + artifactInfo.get("artifactName"));
+ artifactMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+ }
+ }
+ component.setToscaArtifacts(artifactMap);
+ }
+
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> populateToscaArtifacts(Component component, User user, boolean isInCertificationRequest, boolean inTransaction, boolean shouldLock) {
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> generateToscaRes = null;
+ if (component.getToscaArtifacts() != null && !component.getToscaArtifacts().isEmpty()) {
+ ArtifactDefinition toscaArtifact = component.getToscaArtifacts().values().stream().filter(p -> p.getArtifactType().equals(ArtifactTypeEnum.TOSCA_TEMPLATE.getType())).findAny().get();
+ generateToscaRes = saveToscaArtifactPayload(toscaArtifact, component, user, isInCertificationRequest, shouldLock, inTransaction, true);
+ if (generateToscaRes.isRight()) {
+ return generateToscaRes;
+ }
+ toscaArtifact = component.getToscaArtifacts().values().stream().filter(p -> p.getArtifactType().equals(ArtifactTypeEnum.TOSCA_CSAR.getType())).findAny().get();
+ generateToscaRes = saveToscaArtifactPayload(toscaArtifact, component, user, isInCertificationRequest, shouldLock, inTransaction, true);
+ }
+ // TODO if csar artifact fails delete template artifact
+ return generateToscaRes;
+ }
+
+ public Either<Either<ArtifactDefinition, Operation>, ResponseFormat> saveToscaArtifactPayload(ArtifactDefinition artifactDefinition, org.openecomp.sdc.be.model.Component component, User user, boolean isInCertificationRequest, boolean shouldLock,
+ boolean inTransaction, boolean fetchTemplatesFromDB) {
+ return artifactsBusinessLogic.generateAndSaveToscaArtifact(artifactDefinition, component, user, isInCertificationRequest, shouldLock, inTransaction, fetchTemplatesFromDB);
+ }
+
+ public Either<ImmutablePair<String, byte[]>, ResponseFormat> getToscaModelByComponentUuid(ComponentTypeEnum componentType, String uuid, EnumMap<AuditingFieldsKeysEnum, Object> additionalParam) {
+ // get info
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+ Either<Component, StorageOperationStatus> latestVersion = componentOperation.getLatestComponentByUuid(componentType.getNodeType(), uuid);
+ if (latestVersion.isRight()) {
+ ResponseFormat response = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(latestVersion.right().value(), componentType));
+ return Either.right(response);
+
+ }
+ Component component = latestVersion.left().value();
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, component.getName());
+ // TODO remove after migration - handle artifact not found(no
+ // placeholder)
+ if (null == component.getToscaArtifacts() || component.getToscaArtifacts().isEmpty()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, ArtifactTypeEnum.TOSCA_CSAR.name()));
+ }
+ ArtifactDefinition csarArtifact = component.getToscaArtifacts().values().stream().filter(p -> p.getArtifactType().equals(ArtifactTypeEnum.TOSCA_CSAR.getType())).findAny().get();
+ return artifactsBusinessLogic.handleDownloadToscaModelRequest(component, csarArtifact, true, false);
+ }
+
+ protected StorageOperationStatus markComponentToDelete(Component component) {
+
+ ComponentTypeEnum componentType = component.getComponentType();
+ String uniqueId = component.getUniqueId();
+ if ((component.getIsDeleted() != null) && (component.getIsDeleted() == true)) {
+ log.info("component {} already marked as deleted. id= {}, type={}", component.getName(), uniqueId, componentType);
+ return StorageOperationStatus.NOT_FOUND;
+ }
+
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+
+ Either<Component, StorageOperationStatus> markResourceToDelete = componentOperation.markComponentToDelete(component, true);
+ if (markResourceToDelete.isRight()) {
+ StorageOperationStatus result = markResourceToDelete.right().value();
+ log.debug("failed to mark component {} of type {} for delete. error = {}", uniqueId, componentType, result);
+ return result;
+ } else {
+ log.debug("Component {} of type {} was marked as deleted", uniqueId, componentType);
+ return StorageOperationStatus.OK;
+ }
+ }
+
+ public Either<Boolean, ResponseFormat> validateAndUpdateDescription(User user, Component currentComponent, Component updatedComponent, AuditingActionEnum audatingAction) {
+ String descriptionUpdated = updatedComponent.getDescription();
+ String descriptionCurrent = currentComponent.getDescription();
+ if (descriptionUpdated != null && !descriptionCurrent.equals(descriptionUpdated)) {
+ Either<Boolean, ResponseFormat> validateDescriptionResponse = validateDescriptionAndCleanup(user, updatedComponent, audatingAction);
+ if (validateDescriptionResponse.isRight()) {
+ ResponseFormat errorRespons = validateDescriptionResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentComponent.setDescription(updatedComponent.getDescription());
+ }
+ return Either.left(true);
+ }
+
+ public Either<Boolean, ResponseFormat> validateAndUpdateProjectCode(User user, Component currentComponent, Component updatedComponent) {
+ String projectCodeUpdated = updatedComponent.getProjectCode();
+ String projectCodeCurrent = currentComponent.getProjectCode();
+ if (projectCodeUpdated != null && !projectCodeCurrent.equals(projectCodeUpdated)) {
+ Either<Boolean, ResponseFormat> validatProjectCodeResponse = validateProjectCode(user, updatedComponent, null);
+ if (validatProjectCodeResponse.isRight()) {
+ ResponseFormat errorRespons = validatProjectCodeResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentComponent.setProjectCode(updatedComponent.getProjectCode());
+ }
+ return Either.left(true);
+ }
+
+ public Either<Boolean, ResponseFormat> validateAndUpdateIcon(User user, Component currentComponent, Component updatedComponent, boolean hasBeenCertified) {
+ String iconUpdated = updatedComponent.getIcon();
+ String iconCurrent = currentComponent.getIcon();
+ if (iconUpdated != null && !iconCurrent.equals(iconUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validatIconResponse = validateIcon(user, updatedComponent, null);
+ if (validatIconResponse.isRight()) {
+ ResponseFormat errorRespons = validatIconResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentComponent.setIcon(updatedComponent.getIcon());
+ } else {
+ log.info("icon {} cannot be updated once the component has been certified once.", iconUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_PARAMETER_CANNOT_BE_CHANGED, "Icon", currentComponent.getComponentType().name().toLowerCase());
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ protected Either<List<String>, ResponseFormat> deleteMarkedComponents(ComponentTypeEnum componentType) {
+
+ List<String> deletedComponents = new ArrayList<String>();
+ log.trace("start deleteMarkedComponents");
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+ Either<List<String>, StorageOperationStatus> resourcesToDelete = componentOperation.getAllComponentsMarkedForDeletion();
+ if (resourcesToDelete.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resourcesToDelete.right().value(), componentType));
+ return Either.right(responseFormat);
+ }
+
+ for (String resourceToDelete : resourcesToDelete.left().value()) {
+
+ Either<String, ResponseFormat> deleteMarkedResource = deleteMarkedComponent(resourceToDelete, componentType);
+ if (deleteMarkedResource.isLeft()) {
+ deletedComponents.add(deleteMarkedResource.left().value());
+ }
+ }
+
+ log.trace("end deleteMarkedComponents");
+ return Either.left(deletedComponents);
+ }
+
+ private Either<String, ResponseFormat> deleteMarkedComponent(String componentToDelete, ComponentTypeEnum componentType) {
+
+ Either<String, ResponseFormat> result = null;
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+ NodeTypeEnum compNodeType = componentType.getNodeType();
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentToDelete, compNodeType);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedLockObjectError, "Delete marked component");
+ log.debug("Failed to lock component {}. error - {}", componentToDelete, lockResult);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+ try {
+
+ // check if resource has relations
+ Either<Boolean, StorageOperationStatus> isResourceInUse = componentOperation.isComponentInUse(componentToDelete);
+ if (isResourceInUse.isRight()) {
+ log.info("deleteMarkedResource - failed to find relations to resource. id = {}, type = {}, error = {}", componentToDelete, componentType, isResourceInUse.right().value().name());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ if (isResourceInUse.isLeft() && isResourceInUse.left().value() == false) {
+
+ // delete resource and its artifacts in one transaction
+ Either<List<ArtifactDefinition>, StorageOperationStatus> artifactsRes = componentOperation.getComponentArtifactsForDelete(componentToDelete, compNodeType, true);
+ if (artifactsRes.isRight() && !artifactsRes.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ log.info("failed to check artifacts for component node. id = {}, type = {}, error = {}", componentToDelete, componentType, artifactsRes.right().value().name());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ List<ArtifactDefinition> artifactsToDelete = new ArrayList<>();
+ if (artifactsRes.isLeft()) {
+ artifactsToDelete = artifactsRes.left().value();
+ }
+
+ Either<Component, StorageOperationStatus> deleteComponentRes = componentOperation.deleteComponent(componentToDelete, true);
+ if (deleteComponentRes.isRight()) {
+ log.info("failed to delete component. id = {}, type = {}, error = {}", componentToDelete, componentType, deleteComponentRes.right().value().name());
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(deleteComponentRes.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, componentToDelete);
+ result = Either.right(responseFormat);
+ } else {
+ log.trace("component was deleted, id = {}, type = {}", componentToDelete, componentType);
+ // delete related artifacts
+ StorageOperationStatus deleteFromEsRes = artifactsBusinessLogic.deleteAllComponentArtifactsIfNotOnGraph(artifactsToDelete);
+ if (!deleteFromEsRes.equals(StorageOperationStatus.OK)) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(deleteFromEsRes);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, componentToDelete);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ log.debug("component and all its artifacts were deleted, id = {}, type = {}", componentToDelete, componentType);
+ result = Either.left(componentToDelete);
+ }
+ } else {
+ // resource in use
+ log.debug("componentis marked for delete but still in use, id = {}, type = {}", componentToDelete, componentType);
+ ActionStatus actionStatus = ActionStatus.RESTRICTED_OPERATION;
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, componentToDelete);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ } finally {
+ if (result == null || result.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "delete marked component");
+ log.debug("operation failed. do rollback");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ graphLockOperation.unlockComponent(componentToDelete, compNodeType);
+ }
+
+ return result;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
new file mode 100644
index 0000000000..89ef6f7e19
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java
@@ -0,0 +1,1661 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.info.CreateAndAssotiateInfo;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.api.IComponentOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.GroupOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+public abstract class ComponentInstanceBusinessLogic extends BaseBusinessLogic {
+
+ private static final String ARTIFACT_PLACEHOLDER_FILE_EXTENSION = "fileExtension";
+
+ static final String HEAT_ENV_NAME = "heatEnv";
+ private static final String HEAT_ENV_SUFFIX = "env";
+
+ private static Logger log = LoggerFactory.getLogger(ComponentInstanceBusinessLogic.class.getName());
+
+ @Autowired
+ private IComponentInstanceOperation componentInstanceOperation;
+
+ @Autowired
+ private PropertyOperation propertyOperation;
+
+ @Autowired
+ private ArtifactsBusinessLogic artifactBusinessLogic;
+
+ @Autowired
+ private GroupOperation groupOperation;
+
+ public ComponentInstanceBusinessLogic() {
+ }
+
+ public Either<ComponentInstance, ResponseFormat> createComponentInstance(String containerComponentParam, String containerComponentId, String userId, ComponentInstance resourceInstance) {
+ return createComponentInstance(containerComponentParam, containerComponentId, userId, resourceInstance, true, true, true);
+ }
+
+ public Either<ComponentInstance, ResponseFormat> createComponentInstance(String containerComponentParam, String containerComponentId, String userId, ComponentInstance resourceInstance, boolean inTransaction, boolean needLock,
+ boolean createNewTransaction) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Component Instance", inTransaction);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> validateValidJson = validateJsonBody(resourceInstance, ComponentInstance.class);
+ if (validateValidJson.isRight()) {
+ return Either.right(validateValidJson.right().value());
+ }
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+ final ComponentOperation containerOperation = getComponentOperation(containerComponentType);
+
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(containerComponentId, containerComponentType, inTransaction, createNewTransaction);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateAllowedToContainCompInstances = validateAllowedToContainCompInstances(containerComponent);
+ if (validateAllowedToContainCompInstances.isRight()) {
+ return Either.right(validateAllowedToContainCompInstances.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+ if (resourceInstance != null && containerComponentType != null) {
+ Either<Boolean, ResponseFormat> validateComponentInstanceParentState = validateComponentInstanceParentState(containerComponentType, resourceInstance);
+ if (validateComponentInstanceParentState.isRight()) {
+ return Either.right(validateComponentInstanceParentState.right().value());
+ }
+ }
+ if (needLock) {
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "createComponentInstance");
+
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ Either<ComponentInstance, ResponseFormat> resultOp = null;
+ try {
+ log.debug("Try to create entry on graph");
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(resourceInstance, inTransaction);
+
+ if (eitherResourceName.isRight()) {
+ resultOp = Either.right(eitherResourceName.right().value());
+ return resultOp;
+ }
+ Component origComponent = eitherResourceName.left().value();
+
+ resultOp = createComponentInstanceOnGraph(containerComponent, origComponent, resourceInstance, userId, containerOperation, inTransaction);
+ return resultOp;
+
+ } finally {
+ if (needLock)
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ public Either<CreateAndAssotiateInfo, ResponseFormat> createAndAssociateRIToRI(String containerComponentParam, String containerComponentId, String userId, CreateAndAssotiateInfo createAndAssotiateInfo) {
+
+ Either<CreateAndAssotiateInfo, ResponseFormat> resultOp = null;
+ ComponentInstance resourceInstance = createAndAssotiateInfo.getNode();
+ RequirementCapabilityRelDef associationInfo = createAndAssotiateInfo.getAssociate();
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create And Associate RI To RI", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+ final ComponentOperation containerOperation = getComponentOperation(containerComponentType);
+
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(containerComponentId, containerComponentType, false, true);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateAllowedToContainCompInstances = validateAllowedToContainCompInstances(containerComponent);
+ if (validateAllowedToContainCompInstances.isRight()) {
+ return Either.right(validateAllowedToContainCompInstances.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "createAndAssociateRIToRI");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+
+ try {
+ log.debug("Try to create entry on graph");
+ NodeTypeEnum containerNodeType = containerComponentType.getNodeType();
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(resourceInstance, true);
+
+ if (eitherResourceName.isRight()) {
+ resultOp = Either.right(eitherResourceName.right().value());
+ return resultOp;
+ }
+ Component origComponent = eitherResourceName.left().value();
+
+ Either<ComponentInstance, ResponseFormat> result = createComponentInstanceOnGraph(containerComponent, origComponent, resourceInstance, userId, containerOperation, true);
+ if (result.isRight()) {
+ log.debug("Failed to create resource instance {}", containerComponentId);
+ resultOp = Either.right(result.right().value());
+ return resultOp;
+
+ }
+
+ log.debug("Entity on graph is created.");
+ ComponentInstance resResourceInfo = result.left().value();
+ if (associationInfo.getFromNode() == null || associationInfo.getFromNode().isEmpty()) {
+ associationInfo.setFromNode(resResourceInfo.getUniqueId());
+ } else {
+ associationInfo.setToNode(resResourceInfo.getUniqueId());
+ }
+
+ RequirementCapabilityRelDef requirementCapabilityRelDef = associationInfo;// createRequirementCapabilityrelDef(associationInfo);
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> resultReqCapDef = componentInstanceOperation.associateResourceInstances(containerComponentId, containerNodeType, requirementCapabilityRelDef, true);
+ if (resultReqCapDef.isLeft()) {
+ log.debug("Enty on graph is created.");
+ RequirementCapabilityRelDef resReqCapabilityRelDef = resultReqCapDef.left().value();
+ CreateAndAssotiateInfo resInfo = new CreateAndAssotiateInfo(resResourceInfo, resReqCapabilityRelDef);
+ resultOp = Either.left(resInfo);
+ return resultOp;
+
+ } else {
+ log.info("Failed to associate node {} with node {}", associationInfo.getFromNode(), associationInfo.getToNode());
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstance(componentsUtils.convertFromStorageResponseForResourceInstance(resultReqCapDef.right().value(), true), "", null));
+ return resultOp;
+ }
+
+ } finally {
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ private Either<Component, ResponseFormat> getOriginComponentNameFromComponentInstance(ComponentInstance componentInstance, boolean inTransaction) {
+ Either<Component, ResponseFormat> eitherResponse;
+ Either<Component, StorageOperationStatus> eitherComponent = getCompInstOriginComponentOperation().getComponent(componentInstance.getComponentUid(), inTransaction);
+ if (eitherComponent.isRight()) {
+ log.debug("Failed to get origin component with id {} for component instance {} ", componentInstance.getComponentUid(), componentInstance.getName());
+ eitherResponse = Either.right(componentsUtils.getResponseFormatForResourceInstance(componentsUtils.convertFromStorageResponse(eitherComponent.right().value(), ComponentTypeEnum.RESOURCE), "", null));
+ } else {
+ eitherResponse = Either.left(eitherComponent.left().value());
+ }
+ return eitherResponse;
+ }
+
+ private Either<String, ResponseFormat> handleNameLogic(Component origComponent, ComponentInstance componentInstance, ComponentTypeEnum containerComponentType, String containerComponentId, boolean isCreate, boolean inTransaction) {
+ Either<String, ResponseFormat> eitherResult;
+ final ComponentOperation containerOperation = getComponentOperation(containerComponentType);
+
+ Either<Integer, StorageOperationStatus> componentInNumberStatus = containerOperation.increaseAndGetComponentInstanceCounter(containerComponentId, true);
+
+ if (componentInNumberStatus.isRight()) {
+ log.debug("Failed to get component instance number for container component {} ", containerComponentId);
+ eitherResult = Either.right(componentsUtils.getResponseFormatForResourceInstance(componentsUtils.convertFromStorageResponseForResourceInstance(componentInNumberStatus.right().value(), true), "", null));
+ } else {
+ String resourceInNumber = componentInNumberStatus.left().value().toString();
+ eitherResult = Either.left(resourceInNumber);
+ }
+
+ if (eitherResult.isLeft()) {
+ Either<Boolean, ResponseFormat> eitherSpecificLogic;
+ if (isCreate) {
+ eitherSpecificLogic = handleNameLogicForNewComponentInstance(origComponent, componentInstance, eitherResult.left().value(), containerComponentType, inTransaction);
+ } else {
+ eitherSpecificLogic = handleNameLogicForUpdatingComponentInstance(origComponent, componentInstance, componentInNumberStatus, containerComponentType, inTransaction);
+ }
+ if (eitherSpecificLogic.isRight()) {
+ eitherResult = Either.right(eitherSpecificLogic.right().value());
+ }
+ }
+ return eitherResult;
+ }
+
+ private Either<Boolean, ResponseFormat> handleNameLogicForUpdatingComponentInstance(Component origComponent, ComponentInstance componentInstance, Either<Integer, StorageOperationStatus> componentInNumberStatus,
+ ComponentTypeEnum containerComponentType, boolean inTransaction) {
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ if (componentInstance.getName() == null || componentInstance.getName().isEmpty()) {
+ if (origComponent == null) {
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(componentInstance, inTransaction);
+
+ if (eitherResourceName.isRight()) {
+ eitherResult = Either.right(eitherResourceName.right().value());
+ return eitherResult;
+ }
+ origComponent = eitherResourceName.left().value();
+
+ String resourceName = origComponent.getName();
+ String logicalName = componentInstanceOperation.createComponentInstLogicalName(componentInNumberStatus.left().value().toString(), resourceName);
+ componentInstance.setName(logicalName);
+ if (containerComponentType == ComponentTypeEnum.RESOURCE) {
+ Resource resource = (Resource) origComponent;
+ componentInstance.setToscaComponentName(resource.getToscaResourceName());
+ }
+
+ }
+ }
+
+ Either<Boolean, ResponseFormat> eitherValidation = validateComponentInstanceName(componentInstance.getName(), componentInstance, false);
+ if (eitherValidation.isRight()) {
+ eitherResult = Either.right(eitherValidation.right().value());
+ }
+ return eitherResult;
+ }
+
+ private Either<Boolean, ResponseFormat> handleNameLogicForNewComponentInstance(Component origComponent, ComponentInstance componentInstance, String resourceInNumber, ComponentTypeEnum containerComponentType, boolean inTransaction) {
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+
+ if (origComponent == null) {
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(componentInstance, inTransaction);
+
+ if (eitherResourceName.isRight()) {
+ eitherResult = Either.right(eitherResourceName.right().value());
+ return eitherResult;
+ }
+
+ origComponent = eitherResourceName.left().value();
+ }
+
+ String resourceName = origComponent.getName();
+ componentInstance.setComponentName(resourceName);
+ if (componentInstance.getName() == null || componentInstance.getName().isEmpty())
+ componentInstance.setName(resourceName);
+ String logicalName = componentInstanceOperation.createComponentInstLogicalName(resourceInNumber, componentInstance.getName());
+
+ Either<Boolean, ResponseFormat> eitherValidation = validateComponentInstanceName(logicalName, componentInstance, true);
+ if (eitherValidation.isRight()) {
+ eitherResult = Either.right(eitherValidation.right().value());
+ }
+ if (containerComponentType == ComponentTypeEnum.RESOURCE) {
+ Resource resource = (Resource) origComponent;
+ componentInstance.setToscaComponentName(resource.getToscaResourceName());
+ }
+
+ return eitherResult;
+ }
+
+ public Either<ComponentInstance, ResponseFormat> createComponentInstanceOnGraph(String containerComponentParam, org.openecomp.sdc.be.model.Component containerComponent, Component origComponent, ComponentInstance componentInstance, String userId,
+ boolean needLock, boolean inTransaction) {
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+ final ComponentOperation containerOperation = getComponentOperation(containerComponentType);
+ Either<ComponentInstance, ResponseFormat> resultOp = null;
+ if (needLock) {
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "createComponentInstance");
+
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+ try {
+ resultOp = createComponentInstanceOnGraph(containerComponent, origComponent, componentInstance, userId, containerOperation, inTransaction);
+ return resultOp;
+
+ } finally {
+ if (needLock)
+ unlockComponent(resultOp, containerComponent);
+ }
+
+ }
+
+ private Map<String, String> getExistingEnvVersions(ComponentInstance componentInstance) {
+ if (null == componentInstance.getDeploymentArtifacts())
+ return null;
+ return componentInstance.getDeploymentArtifacts().values()
+ //filter env artifacts
+ .stream().filter(p -> p.getArtifactType().equals(ArtifactTypeEnum.HEAT_ENV.getType()))
+ //map name to version
+ .collect(Collectors.toMap(a -> a.getArtifactName(), a -> a.getArtifactVersion()));
+ }
+
+ private Either<ComponentInstance, ResponseFormat> createComponentInstanceOnGraph(org.openecomp.sdc.be.model.Component containerComponent, Component origComponent, ComponentInstance componentInstance, String userId,
+ ComponentOperation containerOperation, boolean inTransaction) {
+ Either<ComponentInstance, ResponseFormat> resultOp;
+ boolean nameAlreadyExist = true;
+ String resourceInNumber = "";
+ String containerComponentId = containerComponent.getUniqueId();
+ ComponentTypeEnum containerComponentType = containerComponent.getComponentType();
+ NodeTypeEnum containerNodeType = containerComponentType.getNodeType();
+ NodeTypeEnum compInstNodeType = getNodeTypeOfComponentInstanceOrigin();
+ while (nameAlreadyExist) {
+
+ Either<String, ResponseFormat> eitherNameLogic = handleNameLogic(origComponent, componentInstance, containerComponent.getComponentType(), containerComponent.getUniqueId(), true, inTransaction);
+ if (eitherNameLogic.isRight()) {
+ return Either.right(eitherNameLogic.right().value());
+ } else {
+ resourceInNumber = eitherNameLogic.left().value();
+ }
+
+ Either<Boolean, StorageOperationStatus> isNameExistStatus = componentInstanceOperation.isComponentInstanceNameExist(containerComponentId, containerNodeType, null, componentInstance.getNormalizedName());
+ if (isNameExistStatus.isRight()) {
+ log.debug("Failed to check if component instance name exists for container component {}", containerComponentId);
+
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND, componentInstance.getName(), containerComponentId));
+ return resultOp;
+ }
+ nameAlreadyExist = isNameExistStatus.left().value();
+
+ }
+
+ Either<ComponentInstance, StorageOperationStatus> result = componentInstanceOperation.createComponentInstance(containerComponentId, containerNodeType, resourceInNumber, componentInstance, compInstNodeType, inTransaction);
+
+ if (result.isRight()) {
+ log.debug("Failed to create entry on graph for component instance {}", componentInstance.getName());
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstance(componentsUtils.convertFromStorageResponseForResourceInstance(result.right().value(), true), "", null));
+ return resultOp;
+ }
+
+ log.debug("Entity on graph is created.");
+ ComponentInstance compInst = result.left().value();
+
+ Map<String, String> existingEnvVersions = getExistingEnvVersions(componentInstance);
+ Either<ActionStatus, ResponseFormat> addComponentInstanceArtifacts = addComponentInstanceArtifacts(containerComponent, compInst, userId, inTransaction, existingEnvVersions);
+ if (addComponentInstanceArtifacts.isRight()) {
+ log.debug("Failed to create component instance {}", componentInstance.getName());
+ resultOp = Either.right(addComponentInstanceArtifacts.right().value());
+ return resultOp;
+ }
+
+ resultOp = Either.left(compInst);
+ return resultOp;
+ }
+
+ /**
+ * addResourceInstanceArtifacts - add artifacts (HEAT_ENV) to resource instance The instance artifacts are generated from the resource's artifacts
+ *
+ * @param componentInstance
+ * @param userId
+ * @param existingEnvVersions
+ * @param containerComponentId
+ *
+ * @return
+ */
+ protected Either<ActionStatus, ResponseFormat> addComponentInstanceArtifacts(org.openecomp.sdc.be.model.Component containerComponent, ComponentInstance componentInstance, String userId, boolean inTransaction,
+ Map<String, String> existingEnvVersions) {
+ log.debug("add artifacts to resource instance");
+
+ ActionStatus status = setResourceArtifactsOnResourceInstance(componentInstance);
+ if (!ActionStatus.OK.equals(status)) {
+ ResponseFormat resultOp = componentsUtils.getResponseFormatForResourceInstance(status, "", null);
+ return Either.right(resultOp);
+ }
+
+ // generate heat_env if necessary
+ Map<String, ArtifactDefinition> componentDeploymentArtifacts = componentInstance.getDeploymentArtifacts();
+ if (componentDeploymentArtifacts == null) {
+ return Either.left(ActionStatus.OK);
+ }
+ Map<String, ArtifactDefinition> finalDeploymentArtifacts = new HashMap<String, ArtifactDefinition>(componentDeploymentArtifacts);
+ for (ArtifactDefinition artifact : componentDeploymentArtifacts.values()) {
+ // if (artifact.getArtifactType().equalsIgnoreCase(
+ // ArtifactTypeEnum.HEAT.getType())) {
+ String type = artifact.getArtifactType();
+
+ if (!(type.equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType()) || type.equalsIgnoreCase(ArtifactTypeEnum.HEAT_NET.getType()) || type.equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType()))) {
+ continue;
+ }
+
+ if (artifact.checkEsIdExist()) {
+ Map<String, Object> deploymentResourceArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getDeploymentResourceInstanceArtifacts();
+ if (deploymentResourceArtifacts == null) {
+ log.debug("no deployment artifacts are configured for resource instance");
+ break;
+ }
+ Map<String, Object> placeHolderData = (Map<String, Object>) deploymentResourceArtifacts.get(HEAT_ENV_NAME);
+
+ String envLabel = (artifact.getArtifactLabel() + HEAT_ENV_SUFFIX).toLowerCase();
+ Either<ArtifactDefinition, ResponseFormat> createArtifactPlaceHolder = artifactBusinessLogic.createArtifactPlaceHolderInfo(componentInstance.getUniqueId(), envLabel, placeHolderData, userId, ArtifactGroupTypeEnum.DEPLOYMENT,
+ inTransaction);
+ if (createArtifactPlaceHolder.isRight()) {
+ return Either.right(createArtifactPlaceHolder.right().value());
+ }
+ ArtifactDefinition artifactHeatEnv = createArtifactPlaceHolder.left().value();
+
+ artifactHeatEnv.setHeatParamsUpdateDate(System.currentTimeMillis());
+ artifactHeatEnv.setTimeout(0);
+ buildHeatEnvFileName(artifact, artifactHeatEnv, placeHolderData);
+
+ // rbetzer - keep env artifactVersion - changeComponentInstanceVersion flow
+ handleEnvArtifactVersion(artifactHeatEnv, existingEnvVersions);
+ Either<ArtifactDefinition, StorageOperationStatus> addHeatEnvArtifact = artifactBusinessLogic.addHeatEnvArtifact(artifactHeatEnv, artifact, componentInstance.getUniqueId(), NodeTypeEnum.ResourceInstance, true);
+ if (addHeatEnvArtifact.isRight()) {
+ log.debug("failed to create heat env artifact on resource instance");
+ return Either.right(componentsUtils.getResponseFormatForResourceInstance(componentsUtils.convertFromStorageResponseForResourceInstance(addHeatEnvArtifact.right().value(), false), "", null));
+ }
+
+ ArtifactDefinition artifactDefinition = addHeatEnvArtifact.left().value();
+ if (artifact.getHeatParameters() != null) {
+ List<HeatParameterDefinition> heatEnvParameters = new ArrayList<HeatParameterDefinition>();
+ for (HeatParameterDefinition parameter : artifact.getHeatParameters()) {
+ HeatParameterDefinition heatEnvParameter = new HeatParameterDefinition(parameter);
+ heatEnvParameter.setDefaultValue(parameter.getCurrentValue());
+ heatEnvParameters.add(heatEnvParameter);
+ }
+ artifactDefinition.setHeatParameters(heatEnvParameters);
+ }
+ finalDeploymentArtifacts.put(envLabel, artifactDefinition);
+
+ // audit
+ EnumMap<AuditingFieldsKeysEnum, Object> artifactAuditingFields = artifactBusinessLogic.createArtifactAuditingFields(artifactDefinition, "", artifactDefinition.getUniqueId());
+ artifactAuditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, componentInstance.getName());
+ handleAuditing(AuditingActionEnum.ARTIFACT_UPLOAD, containerComponent, userId, artifactAuditingFields, inTransaction);
+ }
+ // }
+ }
+ componentInstance.setDeploymentArtifacts(finalDeploymentArtifacts);
+ return Either.left(ActionStatus.OK);
+ }
+
+ private void handleAuditing(AuditingActionEnum artifactUpload, org.openecomp.sdc.be.model.Component containerComponent, String userId, EnumMap<AuditingFieldsKeysEnum, Object> artifactAuditingFields, boolean inTransaction) {
+
+ Either<User, ActionStatus> user = userAdmin.getUser(userId, inTransaction);
+ if (user.isRight()) {
+ log.debug("failed to get user properties from graph for audit");
+ return;
+ }
+
+ componentsUtils.auditComponent(componentsUtils.getResponseFormat(ActionStatus.OK), user.left().value(), containerComponent, "", "", AuditingActionEnum.ARTIFACT_UPLOAD, ComponentTypeEnum.RESOURCE_INSTANCE, artifactAuditingFields);
+
+ }
+
+ private void handleEnvArtifactVersion(ArtifactDefinition heatEnvArtifact, Map<String, String> existingEnvVersions) {
+ if (null != existingEnvVersions) {
+ String prevVersion = existingEnvVersions.get(heatEnvArtifact.getArtifactName());
+ if (null != prevVersion) {
+ heatEnvArtifact.setArtifactVersion(prevVersion);
+ }
+ }
+ }
+
+ private void buildHeatEnvFileName(ArtifactDefinition heatArtifact, ArtifactDefinition heatEnvArtifact, Map<String, Object> placeHolderData) {
+ String heatExtension = GeneralUtility.getFilenameExtension(heatArtifact.getArtifactName());
+ String envExtension = (String) placeHolderData.get(ARTIFACT_PLACEHOLDER_FILE_EXTENSION);
+ String fileName = heatArtifact.getArtifactName().replaceAll("." + heatExtension, "." + envExtension);
+ heatEnvArtifact.setArtifactName(fileName);
+ }
+
+ private ActionStatus setResourceArtifactsOnResourceInstance(ComponentInstance resourceInstance) {
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getResourceDeploymentArtifacts = artifactBusinessLogic.getArtifacts(resourceInstance.getComponentUid(), NodeTypeEnum.Resource, true, ArtifactGroupTypeEnum.DEPLOYMENT);
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = new HashMap<String, ArtifactDefinition>();
+ if (getResourceDeploymentArtifacts.isRight()) {
+ StorageOperationStatus status = getResourceDeploymentArtifacts.right().value();
+ if (!status.equals(StorageOperationStatus.NOT_FOUND)) {
+ log.debug("Failed to fetch resource artifacts. status is {}", status);
+ return componentsUtils.convertFromStorageResponseForResourceInstance(status, true);
+ }
+ } else {
+ deploymentArtifacts = getResourceDeploymentArtifacts.left().value();
+ }
+
+ if (!deploymentArtifacts.isEmpty()) {
+ Map<String, ArtifactDefinition> tempDeploymentArtifacts = new HashMap<String, ArtifactDefinition>(deploymentArtifacts);
+ for (Entry<String, ArtifactDefinition> artifact : deploymentArtifacts.entrySet()) {
+ if (!artifact.getValue().checkEsIdExist()) {
+ tempDeploymentArtifacts.remove(artifact.getKey());
+ }
+ }
+
+ resourceInstance.setDeploymentArtifacts(tempDeploymentArtifacts);
+ }
+
+ return ActionStatus.OK;
+ }
+
+ public Either<ComponentInstance, ResponseFormat> updateComponentInstance(String containerComponentParam, String containerComponentId, String componentInstanceId, String userId, ComponentInstance componentInstance) {
+ return updateComponentInstance(containerComponentParam, containerComponentId, componentInstanceId, userId, componentInstance, false, true, true);
+ }
+
+ public Either<ComponentInstance, ResponseFormat> updateComponentInstance(String containerComponentParam, String containerComponentId, String componentInstanceId, String userId, ComponentInstance componentInstance, boolean inTransaction,
+ boolean needLock, boolean createNewTransaction) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "update Component Instance", inTransaction);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<ComponentInstance, ResponseFormat> resultOp = null;
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(containerComponentId, containerComponentType, inTransaction, createNewTransaction);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+ ComponentTypeEnum instanceType = getComponentType(containerComponentType);
+ Either<Boolean, StorageOperationStatus> validateParentStatus = componentInstanceOperation.validateParent(containerComponentId, componentInstanceId, inTransaction);
+ if (validateParentStatus.isRight()) {
+ log.debug("Failed to get component instance {} on service {}", componentInstanceId, containerComponentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND, componentInstance.getName(), instanceType.getValue().toLowerCase()));
+ return resultOp;
+ }
+ Boolean isPrentValid = validateParentStatus.left().value();
+ if (!isPrentValid) {
+ resultOp = Either.right(
+ componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER, componentInstance.getName(), instanceType.getValue().toLowerCase(), containerComponentType.getValue().toLowerCase(), containerComponentId));
+ return resultOp;
+
+ }
+
+ if (needLock) {
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "updateComponentInstance");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ try {
+
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(componentInstance, inTransaction);
+
+ if (eitherResourceName.isRight()) {
+ resultOp = Either.right(eitherResourceName.right().value());
+ return resultOp;
+ }
+ Component origComponent = eitherResourceName.left().value();
+
+ resultOp = updateComponentInstance(containerComponentId, containerComponentType, origComponent, componentInstanceId, componentInstance, inTransaction);
+ return resultOp;
+
+ } finally {
+ if (needLock)
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ // New Multiple Instance Update API
+ public Either<List<ComponentInstance>, ResponseFormat> updateComponentInstance(String containerComponentParam, String containerComponentId, String userId, List<ComponentInstance> componentInstanceList, boolean needLock,
+ boolean createNewTransaction) {
+
+ Either<List<ComponentInstance>, ResponseFormat> resultOp = null;
+ org.openecomp.sdc.be.model.Component containerComponent = null;
+ try {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "update Component Instance", true);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+
+ ComponentParametersView componentFilter = new ComponentParametersView();
+ componentFilter.disableAll();
+ componentFilter.setIgnoreUsers(false);
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExistsByFilter(containerComponentId, containerComponentType, componentFilter, true);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+
+ containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+
+ ComponentTypeEnum instanceType = getComponentType(containerComponentType);
+
+ for (ComponentInstance componentInstance : componentInstanceList) {
+ Either<Boolean, StorageOperationStatus> validateParentStatus = componentInstanceOperation.validateParent(containerComponentId, componentInstance.getUniqueId(), true);
+ if (validateParentStatus.isRight()) {
+ log.debug("Failed to get component instance {} on service {}", componentInstance.getUniqueId(), containerComponentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND, componentInstance.getName(), instanceType.getValue().toLowerCase()));
+ return resultOp;
+ }
+ Boolean isPrentValid = validateParentStatus.left().value();
+ if (!isPrentValid) {
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER, componentInstance.getName(), instanceType.getValue().toLowerCase(), containerComponentType.getValue().toLowerCase(),
+ containerComponentId));
+ return resultOp;
+ }
+ }
+
+ if (needLock) {
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "updateComponentInstance");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ List<ComponentInstance> updatedList = new ArrayList<>();
+ for (ComponentInstance componentInstance : componentInstanceList) {
+
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(componentInstance, true);
+
+ if (eitherResourceName.isRight()) {
+ resultOp = Either.right(eitherResourceName.right().value());
+ return resultOp;
+ }
+ Component origComponent = eitherResourceName.left().value();
+
+ Either<ComponentInstance, ResponseFormat> resultSingleUpdate = updateComponentInstance(containerComponentId, containerComponentType, origComponent, componentInstance.getUniqueId(), componentInstance, true);
+
+ if (resultSingleUpdate.isRight()) {
+ resultOp = Either.right(resultSingleUpdate.right().value());
+ return resultOp;
+ }
+ updatedList.add(resultSingleUpdate.left().value());
+ }
+
+ resultOp = Either.left(updatedList);
+ return resultOp;
+
+ } finally {
+ if (needLock) {
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+ }
+
+ private ComponentTypeEnum getComponentType(ComponentTypeEnum containerComponentType) {
+ if (ComponentTypeEnum.PRODUCT.equals(containerComponentType)) {
+ return ComponentTypeEnum.SERVICE_INSTANCE;
+ } else {
+ return ComponentTypeEnum.RESOURCE_INSTANCE;
+ }
+ }
+
+ public Either<ComponentInstance, ResponseFormat> updateComponentInstance(String containerComponentParam, org.openecomp.sdc.be.model.Component containerComponent, org.openecomp.sdc.be.model.Component origComponent, String componentInstanceId,
+ ComponentInstance componentInstance, boolean needLock, boolean inTransaction) {
+ Either<ComponentInstance, ResponseFormat> resultOp = null;
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+ if (needLock) {
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "updateComponentInstance");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ try {
+
+ resultOp = updateComponentInstance(containerComponent.getUniqueId(), containerComponentType, origComponent, componentInstanceId, componentInstance, inTransaction);
+ return resultOp;
+
+ } finally {
+ if (needLock)
+ unlockComponent(resultOp, containerComponent);
+ }
+
+ }
+
+ private Either<ComponentInstance, ResponseFormat> updateComponentInstance(String containerComponentId, ComponentTypeEnum containerComponentType, org.openecomp.sdc.be.model.Component origComponent, String componentInstanceId,
+ ComponentInstance componentInstance, boolean inTransaction) {
+ Either<ComponentInstance, ResponseFormat> resultOp;
+
+ Either<String, ResponseFormat> eitherNameLogic = handleNameLogic(origComponent, componentInstance, containerComponentType, containerComponentId, false, inTransaction);
+ if (eitherNameLogic.isRight()) {
+ return Either.right(eitherNameLogic.right().value());
+ }
+ NodeTypeEnum containerNodeType = containerComponentType.getNodeType();
+
+ Either<Boolean, StorageOperationStatus> isNameExistStatus = componentInstanceOperation.isComponentInstanceNameExist(containerComponentId, containerNodeType, componentInstanceId, componentInstance.getNormalizedName());
+ if (isNameExistStatus.isRight()) {
+ log.debug("Failed to get resource instance names for service {}", containerComponentId);
+
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND, componentInstance.getName(), containerComponentId));
+ return resultOp;
+ }
+ Boolean isNameExist = isNameExistStatus.left().value();
+ if (isNameExist) {
+ containerComponentType = getComponentTypeOfComponentInstance();
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, containerComponentType.getValue(), componentInstance.getName()));
+ return resultOp;
+
+ }
+
+ log.debug("Try to update entry on graph");
+ Either<ComponentInstance, StorageOperationStatus> result = componentInstanceOperation.updateResourceInstance(containerComponentId, containerNodeType, componentInstanceId, componentInstance, inTransaction);
+
+ if (result.isLeft()) {
+ log.debug("Enty on graph is updated.");
+ ComponentInstance resResourceInfo = result.left().value();
+ resultOp = Either.left(resResourceInfo);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to update entry on graph for resource instance {}", componentInstance.getName());
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstance(componentsUtils.convertFromStorageResponseForResourceInstance(result.right().value(), false), "", componentInstance.getName()));
+ return resultOp;
+ }
+
+ }
+
+ public Either<ComponentInstance, ResponseFormat> deleteComponentInstance(String containerComponentParam, String containerComponentId, String resourceInstanceId, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Component Instance", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(containerComponentId, containerComponentType, false, true);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "deleteComponentInstance");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ // validate resource
+ /*
+ * if (!ComponentValidationUtils.canWorkOnComponent(containerComponentId, serviceOperation, userId)) { log.info( "Restricted operation for user {} on service {}", userId, containerComponentId); return Either.right(componentsUtils
+ * .getResponseFormat(ActionStatus.RESTRICTED_OPERATION)); } // lock resource StorageOperationStatus lockStatus = graphLockOperation.lockComponent( containerComponentId, NodeTypeEnum.Service); if (lockStatus != StorageOperationStatus.OK) {
+ * log.debug("Failed to lock service {}", containerComponentId); resultOp = Either.right(componentsUtils .getResponseFormat(componentsUtils .convertFromStorageResponse(lockStatus))); return resultOp; }
+ */
+ Either<ComponentInstance, ResponseFormat> resultOp = null;
+ try {
+ resultOp = deleteComponentInstance(containerComponentId, resourceInstanceId, containerComponentType);
+ return resultOp;
+
+ } finally {
+ /*
+ * if (resultOp == null || resultOp.isRight()) { titanGenericDao.rollback(); } else { titanGenericDao.commit(); } graphLockOperation.unlockComponent(containerComponentId, NodeTypeEnum.Service);
+ */
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ private Either<ComponentInstance, ResponseFormat> deleteComponentInstance(String containerComponentId, String resourceInstanceId, ComponentTypeEnum containerComponentType) {
+ Either<ComponentInstance, ResponseFormat> resultOp;
+ NodeTypeEnum containerNodeType = containerComponentType.getNodeType();
+ Either<ComponentInstance, StorageOperationStatus> result = componentInstanceOperation.deleteComponentInstance(containerNodeType, containerComponentId, resourceInstanceId, true);
+
+ if (result.isRight()) {
+ log.debug("Failed to delete entry on graph for resourceInstance {}", resourceInstanceId);
+ ActionStatus status = componentsUtils.convertFromStorageResponse(result.right().value(), containerComponentType);
+ // TODO check
+ /*
+ * if (ActionStatus.SERVICE_NOT_FOUND.equals(status)) { resultOp = Either .right(componentsUtils .getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND)); } else {
+ */
+ resultOp = Either.right(componentsUtils.getResponseFormat(status, resourceInstanceId));
+ // }
+ return resultOp;
+ }
+ ComponentInstance resResourceInfo = result.left().value();
+ resultOp = Either.left(resResourceInfo);
+
+ log.debug("Entry on graph is deleted. Exist more connections on this artifact.");
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = resResourceInfo.getDeploymentArtifacts();
+ if (deploymentArtifacts != null && !deploymentArtifacts.isEmpty()) {
+ StorageOperationStatus deleteArtifactsIfNotOnGraph = artifactBusinessLogic.deleteAllComponentArtifactsIfNotOnGraph(new ArrayList<ArtifactDefinition>(deploymentArtifacts.values()));
+ if (!deleteArtifactsIfNotOnGraph.equals(StorageOperationStatus.OK)) {
+ log.debug("failed to delete artifact payload. status={}", deleteArtifactsIfNotOnGraph.name());
+ resultOp = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(result.right().value()), resourceInstanceId));
+ }
+
+ }
+ return resultOp;
+ }
+
+ public Either<RequirementCapabilityRelDef, ResponseFormat> associateRIToRI(String componentId, String userId, RequirementCapabilityRelDef requirementDef, ComponentTypeEnum componentTypeEnum) {
+ return associateRIToRI(componentId, userId, requirementDef, componentTypeEnum, false, true, true);
+ }
+
+ public Either<RequirementCapabilityRelDef, ResponseFormat> associateRIToRI(String componentId, String userId, RequirementCapabilityRelDef requirementDef, ComponentTypeEnum componentTypeEnum, boolean inTransaction, boolean needLock,
+ boolean createNewTransaction) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "associate Ri To RI", inTransaction);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<RequirementCapabilityRelDef, ResponseFormat> resultOp = null;
+
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(componentId, componentTypeEnum, inTransaction, createNewTransaction);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+ if (needLock) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "associateRIToRI");
+
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ try {
+
+ resultOp = associateRIToRIOnGraph(componentId, requirementDef, componentTypeEnum, inTransaction);
+
+ return resultOp;
+
+ } finally {
+ if (needLock)
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ public Either<RequirementCapabilityRelDef, ResponseFormat> associateRIToRIOnGraph(String componentId, RequirementCapabilityRelDef requirementDef, ComponentTypeEnum componentTypeEnum, boolean inTransaction) {
+
+ log.debug("Try to create entry on graph");
+ Either<RequirementCapabilityRelDef, ResponseFormat> resultOp = null;
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> result = componentInstanceOperation.associateResourceInstances(componentId, componentTypeEnum.getNodeType(), requirementDef, inTransaction);
+
+ if (result.isLeft()) {
+ log.debug("Enty on graph is created.");
+ RequirementCapabilityRelDef requirementCapabilityRelDef = result.left().value();
+ resultOp = Either.left(requirementCapabilityRelDef);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to associate node {} with node {}", requirementDef.getFromNode(), requirementDef.getToNode());
+ String fromNameOrId = "";
+ String toNameOrId = "";
+ Either<ComponentInstance, StorageOperationStatus> fromResult = componentInstanceOperation.getResourceInstanceById(requirementDef.getFromNode());
+ Either<ComponentInstance, StorageOperationStatus> toResult = componentInstanceOperation.getResourceInstanceById(requirementDef.getToNode());
+
+ toNameOrId = requirementDef.getFromNode();
+ fromNameOrId = requirementDef.getFromNode();
+ if (fromResult.isLeft()) {
+ fromNameOrId = fromResult.left().value().getName();
+ }
+ if (toResult.isLeft()) {
+ toNameOrId = toResult.left().value().getName();
+ }
+
+ resultOp = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponseForResourceInstance(result.right().value(), true), fromNameOrId, toNameOrId, requirementDef.getRelationships().get(0).getRequirement()));
+
+ return resultOp;
+ }
+
+ }
+
+ public Either<RequirementCapabilityRelDef, ResponseFormat> dissociateRIFromRI(String componentId, String userId, RequirementCapabilityRelDef requirementDef, ComponentTypeEnum componentTypeEnum) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "dissociate RI From RI", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<RequirementCapabilityRelDef, ResponseFormat> resultOp = null;
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(componentId, componentTypeEnum, false, true);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "associateRIToRI");
+
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ try {
+ log.debug("Try to create entry on graph");
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> result = componentInstanceOperation.dissociateResourceInstances(componentId, componentTypeEnum.getNodeType(), requirementDef, true);
+ if (result.isLeft()) {
+ log.debug("Enty on graph is created.");
+ RequirementCapabilityRelDef requirementCapabilityRelDef = result.left().value();
+ resultOp = Either.left(requirementCapabilityRelDef);
+ return resultOp;
+
+ } else {
+
+ log.debug("Failed to dissocaite node {} from node {}", requirementDef.getFromNode(), requirementDef.getToNode());
+ String fromNameOrId = "";
+ String toNameOrId = "";
+ Either<ComponentInstance, StorageOperationStatus> fromResult = componentInstanceOperation.getResourceInstanceById(requirementDef.getFromNode());
+ Either<ComponentInstance, StorageOperationStatus> toResult = componentInstanceOperation.getResourceInstanceById(requirementDef.getToNode());
+
+ toNameOrId = requirementDef.getFromNode();
+ fromNameOrId = requirementDef.getFromNode();
+ if (fromResult.isLeft()) {
+ fromNameOrId = fromResult.left().value().getName();
+ }
+ if (toResult.isLeft()) {
+ toNameOrId = toResult.left().value().getName();
+ }
+
+ resultOp = Either
+ .right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponseForResourceInstance(result.right().value(), true), fromNameOrId, toNameOrId, requirementDef.getRelationships().get(0).getRequirement()));
+ return resultOp;
+ }
+ } finally {
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ private Either<ComponentInstanceAttribute, ResponseFormat> updateAttributeValue(ComponentInstanceAttribute attribute, String resourceInstanceId) {
+ Either<ComponentInstanceAttribute, StorageOperationStatus> eitherAttribute = componentInstanceOperation.updateAttributeValueInResourceInstance(attribute, resourceInstanceId, true);
+ Either<ComponentInstanceAttribute, ResponseFormat> result;
+ if (eitherAttribute.isLeft()) {
+ log.debug("Attribute value {} was updated on graph.", attribute.getValueUniqueUid());
+ ComponentInstanceAttribute instanceAttribute = eitherAttribute.left().value();
+
+ result = Either.left(instanceAttribute);
+
+ } else {
+ log.debug("Failed to update attribute value {} in resource instance {}", attribute, resourceInstanceId);
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(eitherAttribute.right().value());
+
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus, ""));
+
+ }
+ return result;
+ }
+
+ private Either<ComponentInstanceInput, ResponseFormat> updateInputValue(ComponentInstanceInput input, String resourceInstanceId) {
+ Either<ComponentInstanceInput, StorageOperationStatus> eitherInput = componentInstanceOperation.updateInputValueInResourceInstance(input, resourceInstanceId, true);
+ Either<ComponentInstanceInput, ResponseFormat> result;
+ if (eitherInput.isLeft()) {
+ log.debug("Input value {} was updated on graph.", input.getValueUniqueUid());
+ ComponentInstanceInput instanceInput = eitherInput.left().value();
+
+ result = Either.left(instanceInput);
+
+ } else {
+ log.debug("Failed to update input value {} in resource instance {}", input, resourceInstanceId);
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(eitherInput.right().value());
+
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus, ""));
+
+ }
+ return result;
+ }
+
+ private Either<ComponentInstanceAttribute, ResponseFormat> createAttributeValue(ComponentInstanceAttribute attribute, String resourceInstanceId) {
+
+ Either<ComponentInstanceAttribute, ResponseFormat> result;
+
+ Wrapper<Integer> indexCounterWrapper = new Wrapper<>();
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ validateIncrementCounter(resourceInstanceId, GraphPropertiesDictionary.ATTRIBUTE_COUNTER, indexCounterWrapper, errorWrapper);
+
+ if (!errorWrapper.isEmpty()) {
+ result = Either.right(errorWrapper.getInnerElement());
+ } else {
+ Either<ComponentInstanceAttribute, StorageOperationStatus> eitherAttribute = componentInstanceOperation.addAttributeValueToResourceInstance(attribute, resourceInstanceId, indexCounterWrapper.getInnerElement(), true);
+ if (eitherAttribute.isLeft()) {
+ log.debug("Attribute value was added to resource instance {}", resourceInstanceId);
+ ComponentInstanceAttribute instanceAttribute = eitherAttribute.left().value();
+ result = Either.left(instanceAttribute);
+
+ } else {
+ log.debug("Failed to add attribute value {} to resource instance {}", attribute, resourceInstanceId);
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(eitherAttribute.right().value());
+ result = Either.right(componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, ""));
+
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Create Or Updates Attribute Instance
+ *
+ * @param componentTypeEnum
+ * @param componentId
+ * @param resourceInstanceId
+ * @param attribute
+ * @param userId
+ * @return
+ */
+ public Either<ComponentInstanceAttribute, ResponseFormat> createOrUpdateAttributeValue(ComponentTypeEnum componentTypeEnum, String componentId, String resourceInstanceId, ComponentInstanceAttribute attribute, String userId) {
+ Either<ComponentInstanceAttribute, ResponseFormat> result = null;
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+
+ validateUserExist(userId, "create Or Update Attribute Value", errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ validateComponentTypeEnum(componentTypeEnum, "CreateOrUpdateAttributeValue", errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ validateCanWorkOnComponent(componentId, componentTypeEnum, userId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ validateComponentLock(componentId, componentTypeEnum, errorWrapper);
+ }
+
+ try {
+ if (errorWrapper.isEmpty()) {
+ final boolean isCreate = Objects.isNull(attribute.getValueUniqueUid());
+ if (isCreate) {
+ result = createAttributeValue(attribute, resourceInstanceId);
+ } else {
+ result = updateAttributeValue(attribute, resourceInstanceId);
+ }
+ } else {
+ result = Either.right(errorWrapper.getInnerElement());
+ }
+ return result;
+ }
+
+ finally {
+ if (result == null || result.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ // unlock resource
+ graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
+ }
+ }
+
+ public Either<ComponentInstanceProperty, ResponseFormat> createOrUpdatePropertyValue(ComponentTypeEnum componentTypeEnum, String componentId, String resourceInstanceId, ComponentInstanceProperty property, String userId) {
+
+ Either<ComponentInstanceProperty, ResponseFormat> resultOp = null;
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Or Update Property Value", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ if (componentTypeEnum == null) {
+ BeEcompErrorManager.getInstance().logInvalidInputError("CreateOrUpdatePropertyValue", "invalid component type", ErrorSeverity.INFO);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_ALLOWED));
+ return resultOp;
+ }
+
+ IComponentOperation componentOperation = getIComponentOperation(componentTypeEnum);
+
+ if (!ComponentValidationUtils.canWorkOnComponent(componentId, componentOperation, userId)) {
+ log.info("Restricted operation for user {} on service {}", userId, componentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ return resultOp;
+ }
+ // lock resource
+ StorageOperationStatus lockStatus = graphLockOperation.lockComponent(componentId, componentTypeEnum.getNodeType());
+ if (lockStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to lock service {}", componentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockStatus)));
+ return resultOp;
+ }
+ try {
+ String propertyValueUid = property.getValueUniqueUid();
+ if (propertyValueUid == null) {
+
+ Either<Integer, StorageOperationStatus> counterRes = componentInstanceOperation.increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, GraphPropertiesDictionary.PROPERTY_COUNTER, true);
+
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourcePropertyCounter failed resource instance {} property {}", resourceInstanceId, property);
+ StorageOperationStatus status = counterRes.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(status);
+ resultOp = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ }
+ Integer index = counterRes.left().value();
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = componentInstanceOperation.addPropertyValueToResourceInstance(property, resourceInstanceId, index, true);
+
+ if (result.isLeft()) {
+ log.debug("Property value was added to resource instance {}", resourceInstanceId);
+ ComponentInstanceProperty instanceProperty = result.left().value();
+
+ resultOp = Either.left(instanceProperty);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to add property value {} to resource instance {}", property, resourceInstanceId);
+ // TODO: esofer add error
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(result.right().value());
+
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, ""));
+
+ return resultOp;
+ }
+
+ } else {
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = componentInstanceOperation.updatePropertyValueInResourceInstance(property, resourceInstanceId, true);
+
+ if (result.isLeft()) {
+ log.debug("Property value {} was updated on graph.", property.getValueUniqueUid());
+ ComponentInstanceProperty instanceProperty = result.left().value();
+
+ resultOp = Either.left(instanceProperty);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to update property value {} in resource instance {}", property, resourceInstanceId);
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(result.right().value());
+
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, ""));
+
+ return resultOp;
+ }
+ }
+
+ } finally {
+ if (resultOp == null || resultOp.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ // unlock resource
+ graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
+ }
+
+ }
+
+ public Either<ComponentInstanceInput, ResponseFormat> createOrUpdateInputValue(ComponentTypeEnum componentTypeEnum, String componentId, String resourceInstanceId, ComponentInstanceInput inputProperty, String userId) {
+
+ Either<ComponentInstanceInput, ResponseFormat> resultOp = null;
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Or Update Input Value", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ if (componentTypeEnum == null) {
+ BeEcompErrorManager.getInstance().logInvalidInputError("createOrUpdateInputValue", "invalid component type", ErrorSeverity.INFO);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_ALLOWED));
+ return resultOp;
+ }
+
+ IComponentOperation componentOperation = getIComponentOperation(componentTypeEnum);
+
+ if (!ComponentValidationUtils.canWorkOnComponent(componentId, componentOperation, userId)) {
+ log.info("Restricted operation for user {} on service {}", userId, componentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ return resultOp;
+ }
+ // lock resource
+ StorageOperationStatus lockStatus = graphLockOperation.lockComponent(componentId, componentTypeEnum.getNodeType());
+ if (lockStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to lock service {}", componentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockStatus)));
+ return resultOp;
+ }
+ try {
+ String propertyValueUid = inputProperty.getValueUniqueUid();
+ if (propertyValueUid == null) {
+
+ Either<Integer, StorageOperationStatus> counterRes = componentInstanceOperation.increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, GraphPropertiesDictionary.INPUT_COUNTER, true);
+
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourceInputCounter failed resource instance {} inputProperty {}", resourceInstanceId, inputProperty);
+ StorageOperationStatus status = counterRes.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(status);
+ resultOp = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ }
+ Integer index = counterRes.left().value();
+ Either<ComponentInstanceInput, StorageOperationStatus> result = componentInstanceOperation.addInputValueToResourceInstance(inputProperty, resourceInstanceId, index, true);
+
+ if (result.isLeft()) {
+ log.debug("Property value was added to resource instance {}", resourceInstanceId);
+ ComponentInstanceInput instanceProperty = result.left().value();
+
+ resultOp = Either.left(instanceProperty);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to add input value {} to resource instance {}", inputProperty, resourceInstanceId);
+ // TODO: esofer add error
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(result.right().value());
+
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, ""));
+
+ return resultOp;
+ }
+
+ } else {
+ Either<ComponentInstanceInput, StorageOperationStatus> result = componentInstanceOperation.updateInputValueInResourceInstance(inputProperty, resourceInstanceId, true);
+
+ if (result.isLeft()) {
+ log.debug("Input value {} was updated on graph.", inputProperty.getValueUniqueUid());
+ ComponentInstanceInput instanceProperty = result.left().value();
+
+ resultOp = Either.left(instanceProperty);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to update property value {} in reosurce instance {}", inputProperty, resourceInstanceId);
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(result.right().value());
+
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, ""));
+
+ return resultOp;
+ }
+ }
+
+ } finally {
+ if (resultOp == null || resultOp.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ // unlock resource
+ graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
+ }
+
+ }
+
+ public Either<ComponentInstanceProperty, ResponseFormat> deletePropertyValue(ComponentTypeEnum componentTypeEnum, String serviceId, String resourceInstanceId, String propertyValueId, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Property Value", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<ComponentInstanceProperty, ResponseFormat> resultOp = null;
+
+ if (componentTypeEnum == null) {
+ BeEcompErrorManager.getInstance().logInvalidInputError("CreateOrUpdatePropertyValue", "invalid component type", ErrorSeverity.INFO);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_ALLOWED));
+ return resultOp;
+ }
+
+ IComponentOperation componentOperation = getIComponentOperation(componentTypeEnum);
+
+ if (!ComponentValidationUtils.canWorkOnComponent(serviceId, componentOperation, userId)) {
+ log.info("Restricted operation for user {} on service {}", userId, serviceId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ return resultOp;
+ }
+ // lock resource
+ StorageOperationStatus lockStatus = graphLockOperation.lockComponent(serviceId, componentTypeEnum.getNodeType());
+ if (lockStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to lock service {}", serviceId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockStatus)));
+ return resultOp;
+ }
+ try {
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = propertyOperation.removePropertyValueFromResourceInstance(propertyValueId, resourceInstanceId, true);
+
+ if (result.isLeft()) {
+ log.debug("Property value {} was removed from graph.", propertyValueId);
+ ComponentInstanceProperty instanceProperty = result.left().value();
+
+ resultOp = Either.left(instanceProperty);
+ return resultOp;
+
+ } else {
+ log.debug("Failed to remove property value {} in resource instance {}", propertyValueId, resourceInstanceId);
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(result.right().value());
+
+ resultOp = Either.right(componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, ""));
+
+ return resultOp;
+ }
+
+ } finally {
+ if (resultOp == null || resultOp.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ // unlock resource
+ graphLockOperation.unlockComponent(serviceId, componentTypeEnum.getNodeType());
+ }
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateComponentInstanceName(String resourceInstanceName, ComponentInstance resourceInstance, boolean isCreate) {
+ ComponentTypeEnum containerComponentType = getComponentTypeOfComponentInstance();
+ if (!isCreate) {
+ if (resourceInstanceName == null)
+ return Either.left(true);
+ }
+
+ if (!ValidationUtils.validateStringNotEmpty(resourceInstanceName)) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_COMPONENT_NAME, containerComponentType.getValue());
+
+ return Either.right(errorResponse);
+ }
+ resourceInstance.setNormalizedName(ValidationUtils.normaliseComponentInstanceName(resourceInstanceName));
+ if (!isCreate) {
+ if (!ValidationUtils.validateResourceInstanceNameLength(resourceInstanceName)) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT, containerComponentType.getValue(), "" + ValidationUtils.COMPONENT_NAME_MAX_LENGTH);
+
+ return Either.right(errorResponse);
+ }
+ if (!ValidationUtils.validateResourceInstanceName(resourceInstanceName)) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_COMPONENT_NAME, containerComponentType.getValue());
+
+ return Either.right(errorResponse);
+ }
+ }
+
+ return Either.left(true);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateComponentInstanceParentState(ComponentTypeEnum containerComponentType, ComponentInstance resourceInstance) {
+ String componentId = resourceInstance.getComponentUid();
+ Either<? extends Component, StorageOperationStatus> eitherResourceResponse = Either.right(StorageOperationStatus.GENERAL_ERROR);
+
+ ComponentTypeEnum componentType = getComponentTypeByParentComponentType(containerComponentType);
+ ComponentOperation componentOperation = getComponentOperation(componentType);
+ if (componentOperation != null)
+ eitherResourceResponse = componentOperation.getComponent(componentId, true);
+
+ Component component = null;
+ ResponseFormat errorResponse = null;
+ if (eitherResourceResponse.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(eitherResourceResponse.right().value(), componentType);
+ errorResponse = componentsUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
+ return Either.right(errorResponse);
+ }
+ component = eitherResourceResponse.left().value();
+ LifecycleStateEnum resourceCurrState = component.getLifecycleState();
+ if (resourceCurrState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT) {
+ ActionStatus actionStatus = ActionStatus.ILLEGAL_COMPONENT_STATE;
+ errorResponse = componentsUtils.getResponseFormat(actionStatus, component.getComponentType().toString(), component.getName(), resourceCurrState.toString());
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+
+ public Either<ComponentInstance, ResponseFormat> changeComponentInstanceVersion(String containerComponentParam, String containerComponentId, String componentInstanceId, String userId, ComponentInstance newComponentInstance) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "change Component Instance Version", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<ComponentInstance, ResponseFormat> resultOp = null;
+
+ Either<ComponentTypeEnum, ResponseFormat> validateComponentType = validateComponentType(containerComponentParam);
+ if (validateComponentType.isRight()) {
+ return Either.right(validateComponentType.right().value());
+ }
+
+ final ComponentTypeEnum containerComponentType = validateComponentType.left().value();
+ final ComponentOperation containerOperation = getComponentOperation(containerComponentType);
+
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponentExists = validateComponentExists(containerComponentId, containerComponentType, false, true);
+ if (validateComponentExists.isRight()) {
+ return Either.right(validateComponentExists.right().value());
+ }
+ org.openecomp.sdc.be.model.Component containerComponent = validateComponentExists.left().value();
+
+ Either<Boolean, ResponseFormat> validateCanWorkOnComponent = validateCanWorkOnComponent(containerComponent, userId);
+ if (validateCanWorkOnComponent.isRight()) {
+ return Either.right(validateCanWorkOnComponent.right().value());
+ }
+
+ Either<Boolean, StorageOperationStatus> validateParentStatus = componentInstanceOperation.validateParent(containerComponentId, componentInstanceId, false);
+ if (validateParentStatus.isRight()) {
+ log.debug("Failed to get resource instance {} on service {}", componentInstanceId, containerComponentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND, componentInstanceId));
+ return resultOp;
+ }
+ Boolean isPrentValid = validateParentStatus.left().value();
+ if (!isPrentValid) {
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE, componentInstanceId, containerComponentId));
+ return resultOp;
+
+ }
+
+ Either<ComponentInstance, StorageOperationStatus> resourceInstanceStatus = componentInstanceOperation.getResourceInstanceById(componentInstanceId);
+ if (resourceInstanceStatus.isRight()) {
+ log.debug("Failed to get resource instance {} on service {}", componentInstanceId, containerComponentId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND, componentInstanceId));
+ return resultOp;
+ }
+ ComponentInstance currentResourceInstance = resourceInstanceStatus.left().value();
+
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(containerComponent, "changeComponentInstanceVersion");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+
+ try {
+ if (currentResourceInstance.getComponentUid().equals(newComponentInstance.getComponentUid())) {
+ resultOp = Either.left(currentResourceInstance);
+ return resultOp;
+
+ }
+ String resourceId = newComponentInstance.getComponentUid();
+ if (!getCompInstOriginComponentOperation().isComponentExist(resourceId)) {
+ log.debug("resource {} not found.", resourceId);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ return resultOp;
+ }
+
+ // esofer - before deleting component instance, we should keep the
+ // groups which holds this instance
+ List<String> groupsToRevert = new ArrayList<>();
+ Either<List<String>, StorageOperationStatus> associatedGroups = groupOperation.getAssociatedGroupsToComponentInstance(componentInstanceId, true);
+ if (associatedGroups.isRight()) {
+ StorageOperationStatus status = associatedGroups.right().value();
+ if (status != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("ChangeComponentInstanceVersion", "Failed to getch groups of current component instance", ErrorSeverity.ERROR);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return resultOp;
+ }
+ } else {
+ List<String> groups = associatedGroups.left().value();
+ groupsToRevert.addAll(groups);
+ }
+ // rbetzer - before deleting component instance, retrieve env artifacts to keep track of artifactVersion
+
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> retrieveEnvArtifacts = componentInstanceOperation.fetchCIEnvArtifacts(componentInstanceId);
+ if (retrieveEnvArtifacts.isLeft())
+ newComponentInstance.setDeploymentArtifacts(retrieveEnvArtifacts.left().value());
+ else if (retrieveEnvArtifacts.right().value() != StorageOperationStatus.OK) {
+ log.debug("falied to fetch instance deployment artifacts {}", componentInstanceId );
+ }
+
+ resultOp = deleteComponentInstance(containerComponentId, componentInstanceId, containerComponentType);
+ if (resultOp.isRight()) {
+
+ log.debug("failed to delete resource instance {}", resourceId);
+
+ return resultOp;
+
+ }
+
+ Either<Component, ResponseFormat> eitherResourceName = getOriginComponentNameFromComponentInstance(newComponentInstance, true);
+
+ if (eitherResourceName.isRight()) {
+ resultOp = Either.right(eitherResourceName.right().value());
+ return resultOp;
+ }
+
+ Component origComponent = eitherResourceName.left().value();
+
+ ComponentInstance resResourceInfo = resultOp.left().value();
+ newComponentInstance.setName(resResourceInfo.getName());
+ newComponentInstance.setPosX(resResourceInfo.getPosX());
+ newComponentInstance.setPosY(resResourceInfo.getPosY());
+ newComponentInstance.setDescription(resResourceInfo.getDescription());
+
+ resultOp = createComponentInstanceOnGraph(containerComponent, origComponent, newComponentInstance, userId, containerOperation, true);
+
+ if (resultOp.isRight()) {
+
+ log.debug("failed to create resource instance {}", resourceId);
+
+ return resultOp;
+
+ }
+
+ newComponentInstance = resultOp.left().value();
+ newComponentInstance.setName(resResourceInfo.getName());
+ resultOp = updateComponentInstance(containerComponentId, containerComponentType, origComponent, newComponentInstance.getUniqueId(), newComponentInstance, true);
+
+ ComponentInstance updatedComponentInstance = resultOp.left().value();
+ if (resultOp.isRight()) {
+ log.debug("failed to create resource instance {}", resourceId);
+ return resultOp;
+ }
+
+ if (false == groupsToRevert.isEmpty()) {
+ StorageOperationStatus associatedGroupsToComponentInstance = groupOperation.associateGroupsToComponentInstance(groupsToRevert, updatedComponentInstance.getUniqueId(), updatedComponentInstance.getName(), true);
+ if (associatedGroupsToComponentInstance != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("ChangeComponentInstanceVersion", "Failed to associate groups to new component instance", ErrorSeverity.ERROR);
+ resultOp = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return resultOp;
+ }
+ }
+
+ Either<ComponentInstance, StorageOperationStatus> fullResourceInstance = componentInstanceOperation.getFullComponentInstance(resultOp.left().value(), getNodeTypeOfComponentInstanceOrigin());
+ if (fullResourceInstance.isRight()) {
+ resultOp = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(fullResourceInstance.right().value()), resourceId));
+ return resultOp;
+ }
+ resultOp = Either.left(fullResourceInstance.left().value());
+ return resultOp;
+
+ } finally {
+ unlockComponent(resultOp, containerComponent);
+ }
+ }
+
+ protected abstract Either<Boolean, ResponseFormat> validateAllowedToContainCompInstances(org.openecomp.sdc.be.model.Component containerComponent);
+
+ protected abstract NodeTypeEnum getNodeTypeOfComponentInstanceOrigin();
+
+ protected abstract ComponentTypeEnum getComponentTypeOfComponentInstance();
+
+ protected abstract ComponentOperation getContainerComponentOperation();
+
+ protected abstract ComponentOperation getCompInstOriginComponentOperation();
+
+ protected void validateIncrementCounter(String resourceInstanceId, GraphPropertiesDictionary counterType, Wrapper<Integer> instaceCounterWrapper, Wrapper<ResponseFormat> errorWrapper) {
+ Either<Integer, StorageOperationStatus> counterRes = componentInstanceOperation.increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, counterType, true);
+
+ if (counterRes.isRight()) {
+ log.debug("increase And Get {} failed resource instance {}", counterType.name(), resourceInstanceId);
+ StorageOperationStatus status = counterRes.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(status);
+ errorWrapper.setInnerElement(componentsUtils.getResponseFormat(actionStatus));
+ } else {
+ instaceCounterWrapper.setInnerElement(counterRes.left().value());
+ }
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogic.java
new file mode 100644
index 0000000000..6cebc7dd53
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogic.java
@@ -0,0 +1,285 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+/**
+ * This class holds the logic of arranging resource instance on the canvas for imported VF
+ *
+ * @author mshitrit
+ *
+ */
+@Component("compositionBusinessLogic")
+public class CompositionBusinessLogic {
+ @Autowired
+ private VFComponentInstanceBusinessLogic vfComponentInstanceBusinessLogic;
+
+ private static final int VFC_CANVAS_ELEMENT_SIZE = 50;
+ private static final int CP_CANVAS_ELEMENT_SIZE = 21;
+ private static final int CANVAS_WIDTH = 1000;
+ private static final int CANVAS_HEIGHT = 700;
+ private static final int SPACE_BETWEEN_ELEMENTS = VFC_CANVAS_ELEMENT_SIZE * 4;
+ private static final double CP_RADIUS_FACTOR = 0.4;
+
+ enum RelativePosition {
+ LEFT, RIGHT, UP, DOWN
+ };
+
+ protected Either<List<ComponentInstance>, ResponseFormat> setPositionsForComponentInstances(Resource resource, String userId) {
+ Either<List<ComponentInstance>, ResponseFormat> result = Either.left(resource.getComponentInstances());
+
+ boolean isNotAllPositionsCalculated = resource.getComponentInstances() == null
+ || resource.getComponentInstances().stream().filter(p -> (p.getPosX() == null || p.getPosX().isEmpty()) || (p.getPosY() == null || p.getPosY().isEmpty())).findAny().isPresent();
+
+ if (isNotAllPositionsCalculated) {
+ // Arrange Icons In Spiral Pattern
+ Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations = buildSpiralPatternPositioningForComponentInstances(resource);
+
+ // Set Relative Locations According to Canvas Size
+ componentInstanceLocations.entrySet().stream().forEach(e -> setRelativePosition(e));
+
+ // Update in DB
+ result = vfComponentInstanceBusinessLogic.updateComponentInstance(ComponentTypeEnum.RESOURCE_PARAM_NAME, resource.getUniqueId(), userId, resource.getComponentInstances(), false, false);
+
+ }
+ return result;
+
+ }
+
+ private void setRelativePosition(Entry<ImmutablePair<Double, Double>, ComponentInstance> entry) {
+ int xCenter = CANVAS_WIDTH / 2;
+ int yCenter = CANVAS_HEIGHT / 2;
+
+ ImmutablePair<Double, Double> matrixPosition = entry.getKey();
+ ComponentInstance componentInstance = entry.getValue();
+ componentInstance.setPosX(calculateCompositionPosition(xCenter, matrixPosition.getLeft(), componentInstance));
+ componentInstance.setPosY(calculateCompositionPosition(yCenter, matrixPosition.getRight(), componentInstance));
+ }
+
+ private String calculateCompositionPosition(int center, double relativePosition, ComponentInstance componentInstance) {
+ final double topLeftCanvasPosition = center + relativePosition * CompositionBusinessLogic.SPACE_BETWEEN_ELEMENTS;
+ double offsetedCanvasPosition;
+ switch (componentInstance.getOriginType()) {
+ case CP:
+ offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.CP_CANVAS_ELEMENT_SIZE / 2;
+ break;
+ case VL:
+ offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.CP_CANVAS_ELEMENT_SIZE / 2;
+ break;
+ case VF:
+ offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2;
+ break;
+ case VFC:
+ offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2;
+ break;
+ default:
+ offsetedCanvasPosition = topLeftCanvasPosition - CompositionBusinessLogic.VFC_CANVAS_ELEMENT_SIZE / 2;
+ break;
+ }
+ return String.valueOf(offsetedCanvasPosition);
+ }
+
+ protected Map<ImmutablePair<Double, Double>, ComponentInstance> buildSpiralPatternPositioningForComponentInstances(Resource resource) {
+
+ Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations = new HashMap<>();
+
+ List<ComponentInstance> componentInstances = new ArrayList<>();
+ componentInstances.addAll(resource.getComponentInstances());
+ Map<ComponentInstance, List<ComponentInstance>> connectededCps = getCpsConnectedToVFC(componentInstances, resource);
+ // Remove all cp that are connected from the list
+ componentInstances.removeAll(connectededCps.values().stream().flatMap(e -> e.stream()).collect(Collectors.toList()));
+
+ buildSpiralPatternForMajorComponents(componentInstanceLocations, componentInstances);
+ buildCirclePatternForCps(componentInstanceLocations, connectededCps);
+
+ return componentInstanceLocations;
+ }
+
+ protected void buildCirclePatternForCps(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstLocations, Map<ComponentInstance, List<ComponentInstance>> connectedCps) {
+
+ for (Entry<ComponentInstance, List<ComponentInstance>> vfcCpList : connectedCps.entrySet()) {
+ Entry<ImmutablePair<Double, Double>, ComponentInstance> vfcOfTheCps = componentInstLocations.entrySet().stream().filter(p -> p.getValue().getUniqueId().equals(vfcCpList.getKey().getUniqueId())).findAny().get();
+ buildCirclePatternForOneGroupOfCps(vfcOfTheCps.getKey(), vfcCpList.getValue(), componentInstLocations);
+ }
+
+ }
+
+ private void buildCirclePatternForOneGroupOfCps(ImmutablePair<Double, Double> vfcLocation, List<ComponentInstance> cpsGroup, Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstLocations) {
+ final int numberOfCps = cpsGroup.size();
+ double angleBetweenCps = (!cpsGroup.isEmpty()) ? Math.toRadians(360) / numberOfCps : 0;
+ double currentAngle = 0;
+ Double xCenter = vfcLocation.getLeft();
+ Double yCenter = vfcLocation.getRight();
+ for (ComponentInstance currCp : cpsGroup) {
+ double cpXposition = xCenter + CompositionBusinessLogic.CP_RADIUS_FACTOR * Math.cos(currentAngle);
+ double cpYposition = yCenter + CompositionBusinessLogic.CP_RADIUS_FACTOR * Math.sin(currentAngle);
+ componentInstLocations.put(new ImmutablePair<Double, Double>(cpXposition, cpYposition), currCp);
+ currentAngle += angleBetweenCps;
+ }
+
+ }
+
+ private void buildSpiralPatternForMajorComponents(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations, List<ComponentInstance> componentInstances) {
+ int elementsCounter = 0;
+ ImmutablePair<Double, Double> currPlacement;
+ ImmutablePair<Double, Double> prevPlacement = null;
+ RelativePosition relationToPrevElement = null;
+ for (ComponentInstance curr : componentInstances) {
+ elementsCounter++;
+ if (elementsCounter == 1) {
+ currPlacement = new ImmutablePair<Double, Double>(0D, 0D);
+ } else if (elementsCounter == 2) {
+ currPlacement = new ImmutablePair<Double, Double>(-1D, 0D);
+ relationToPrevElement = RelativePosition.LEFT;
+ } else {
+ relationToPrevElement = getRelativePositionForCurrentElement(componentInstanceLocations, relationToPrevElement, prevPlacement);
+ currPlacement = getRelativeElementLocation(prevPlacement, relationToPrevElement);
+
+ }
+
+ componentInstanceLocations.put(currPlacement, curr);
+ prevPlacement = currPlacement;
+ }
+ }
+
+ protected Map<ComponentInstance, List<ComponentInstance>> getCpsConnectedToVFC(List<ComponentInstance> allComponentInstances, Resource vf) {
+ Map<ComponentInstance, List<ComponentInstance>> vfcWithItsCps = new HashMap<>();
+ List<RequirementCapabilityRelDef> allRelations = vf.getComponentInstancesRelations();
+ for (ComponentInstance curr : allComponentInstances) {
+ // Filters Only CPs
+ if (curr.getOriginType() == OriginTypeEnum.CP) {
+ // List Of elements the CP is connected to
+ List<RequirementCapabilityRelDef> connectedToList = allRelations.stream().filter(p -> p.getFromNode().equals(curr.getUniqueId()) || p.getToNode().equals(curr.getUniqueId())).collect(Collectors.toList());
+ // Adds Only CPs Which are connected to VFC
+ filterCpConnectedToVFC(allComponentInstances, vfcWithItsCps, curr, connectedToList);
+ }
+ }
+ return vfcWithItsCps;
+ }
+
+ private void filterCpConnectedToVFC(List<ComponentInstance> allComponentInstances, Map<ComponentInstance, List<ComponentInstance>> vfcWithItsCps, ComponentInstance currCP, List<RequirementCapabilityRelDef> connectedToTheCPList) {
+ if (!connectedToTheCPList.isEmpty()) {
+ // Set Of Ids Of components Instances which are connected certain CP
+ Set<String> mateIds = connectedToTheCPList.stream().map(cpRelation -> cpRelation.getFromNode().equals(currCP.getUniqueId()) ? cpRelation.getToNode() : cpRelation.getFromNode()).collect(Collectors.toSet());
+
+ // Vfc Component instance Connected to the CP
+ Optional<ComponentInstance> optionalVfcConnectedToCP = allComponentInstances.stream().
+ // All instances connected to CP
+ filter(p -> mateIds.contains(p.getUniqueId())).
+ // Filter in only VFC connected to the CP
+ filter(p -> p.getOriginType() == OriginTypeEnum.VFC).findAny();
+
+ if (optionalVfcConnectedToCP.isPresent()) {
+ final ComponentInstance vfcWithCps = optionalVfcConnectedToCP.get();
+ if (vfcWithItsCps.containsKey(vfcWithCps)) {
+ vfcWithItsCps.get(vfcWithCps).add(currCP);
+ } else {
+ List<ComponentInstance> cpsList = new ArrayList<>();
+ cpsList.add(currCP);
+ vfcWithItsCps.put(vfcWithCps, cpsList);
+ }
+ }
+ }
+ }
+
+ private RelativePosition getRelativePositionForCurrentElement(Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstanceLocations, RelativePosition relationToPrevElement, ImmutablePair<Double, Double> prevPlacement) {
+ switch (relationToPrevElement) {
+ case LEFT: {
+ boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.UP, componentInstanceLocations);
+ relationToPrevElement = isOccupied ? RelativePosition.LEFT : RelativePosition.UP;
+ break;
+ }
+ case RIGHT: {
+ boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.DOWN, componentInstanceLocations);
+ relationToPrevElement = isOccupied ? RelativePosition.RIGHT : RelativePosition.DOWN;
+ break;
+ }
+ case UP: {
+ boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.RIGHT, componentInstanceLocations);
+ relationToPrevElement = isOccupied ? RelativePosition.UP : RelativePosition.RIGHT;
+ break;
+ }
+ case DOWN: {
+ boolean isOccupied = isAdjacentElementOccupied(prevPlacement, RelativePosition.LEFT, componentInstanceLocations);
+ relationToPrevElement = isOccupied ? RelativePosition.DOWN : RelativePosition.LEFT;
+ break;
+ }
+ default: {
+ throw new UnsupportedOperationException();
+ }
+ }
+ return relationToPrevElement;
+ }
+
+ private boolean isAdjacentElementOccupied(ImmutablePair<Double, Double> currElement, RelativePosition adjacentElementRelationToCurrElement, Map<ImmutablePair<Double, Double>, ComponentInstance> allElements) {
+
+ ImmutablePair<Double, Double> adjacentElementPosition = getRelativeElementLocation(currElement, adjacentElementRelationToCurrElement);
+ return allElements.containsKey(adjacentElementPosition);
+ }
+
+ private ImmutablePair<Double, Double> getRelativeElementLocation(ImmutablePair<Double, Double> currElement, RelativePosition relativeLocation) {
+ ImmutablePair<Double, Double> relativeElementPosition;
+ switch (relativeLocation) {
+
+ case LEFT: {
+ relativeElementPosition = new ImmutablePair<Double, Double>(currElement.getLeft() - 1, currElement.getRight());
+ break;
+ }
+ case RIGHT: {
+ relativeElementPosition = new ImmutablePair<Double, Double>(currElement.getLeft() + 1, currElement.getRight());
+ break;
+ }
+ case UP: {
+ relativeElementPosition = new ImmutablePair<Double, Double>(currElement.getLeft(), currElement.getRight() + 1);
+ break;
+ }
+ case DOWN: {
+ relativeElementPosition = new ImmutablePair<Double, Double>(currElement.getLeft(), currElement.getRight() - 1);
+ break;
+ }
+ default: {
+ throw new UnsupportedOperationException();
+ }
+ }
+ return relativeElementPosition;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ConsumerBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ConsumerBusinessLogic.java
new file mode 100644
index 0000000000..226225a07c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ConsumerBusinessLogic.java
@@ -0,0 +1,313 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.Date;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ConsumerDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ConsumerOperation;
+import org.openecomp.sdc.be.resources.data.ConsumerData;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("ConsumerBusinessLogic")
+public class ConsumerBusinessLogic extends BaseBusinessLogic {
+
+ private static final String CONSUMER_NAME = "Consumer name";
+ private static final String CONSUMER_SALT = "Consumer salt";
+ private static final String CONSUMER_PW = "Consumer password";
+
+ @javax.annotation.Resource
+ private IUserBusinessLogic userAdmin;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ @javax.annotation.Resource
+ private ConsumerOperation consumerOperation;
+
+ @javax.annotation.Resource
+ private IGraphLockOperation graphLockOperation;
+
+ private static Logger log = LoggerFactory.getLogger(ConsumerBusinessLogic.class.getName());
+
+ public Either<ConsumerDefinition, ResponseFormat> createConsumer(User user, ConsumerDefinition consumer) {
+
+ Either<User, ResponseFormat> userValidation = validateUser(user, consumer, AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS);
+
+ if (userValidation.isRight()) {
+ return Either.right(userValidation.right().value());
+ }
+ checkFieldsForOverrideAttempt(consumer);
+ user = userValidation.left().value();
+ consumer.setLastModfierAtuid(user.getUserId());
+
+ Either<ConsumerDefinition, ResponseFormat> consumerValidationResponse = validateConsumer(consumer, user, AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS);
+ if (consumerValidationResponse.isRight()) {
+ ResponseFormat responseFormat = consumerValidationResponse.right().value();
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS, consumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ String consumerName = consumer.getConsumerName();
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(consumerName, NodeTypeEnum.ConsumerCredentials);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedLockObjectError, "createConsumer");
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError("createConsumer", NodeTypeEnum.ConsumerCredentials.getName(), consumerName);
+ log.debug("Failed to lock consumer {} error - {}", consumerName, lockResult);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS, consumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ try {
+ Either<ConsumerData, StorageOperationStatus> getResponse = consumerOperation.getCredentials(consumerName);
+ if (getResponse.isLeft() && getResponse.left().value() != null) {
+ return updateConsumer(consumer, user, true);
+ }
+
+ Date date = new Date();
+ consumer.setConsumerDetailsLastupdatedtime(date.getTime());
+ consumer.setConsumerLastAuthenticationTime(Long.valueOf(0));
+
+ Either<ConsumerData, StorageOperationStatus> createResponse = consumerOperation.createCredentials(new ConsumerData(consumer));
+
+ if (createResponse.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponseForConsumer(createResponse.right().value()));
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS, consumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ log.debug("Consumer created successfully!!!");
+ consumer = new ConsumerDefinition(createResponse.left().value().getConsumerDataDefinition());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS, consumer, responseFormat, user);
+ return Either.left(consumer);
+ } finally {
+ graphLockOperation.unlockComponent(consumerName, NodeTypeEnum.ConsumerCredentials);
+ }
+ }
+
+ private Either<User, ResponseFormat> validateUser(User user, ConsumerDefinition consumer, AuditingActionEnum auditAction) {
+
+ if (user.getUserId() == null || user.getUserId().trim().isEmpty()) {
+ log.debug("createEcompUser method - user is missing. userId={}", user.getUserId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ log.debug("audit before sending response");
+ componentsUtils.auditConsumerCredentialsEvent(auditAction, consumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ log.debug("get user from DB");
+ Either<User, ActionStatus> eitherCreator = userAdmin.getUser(user.getUserId(), false);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ log.debug("createEcompUser method - user is not listed. userId={}", user.getUserId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_ACCESS);
+ log.debug("audit before sending response");
+ componentsUtils.auditConsumerCredentialsEvent(auditAction, consumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+
+ user = eitherCreator.left().value();
+ // validate user role
+ log.debug("validate user role");
+ if (!user.getRole().equals(Role.ADMIN.name())) {
+ log.info("role {} is not allowed to perform this action", user.getRole());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ log.debug("audit before sending response");
+ componentsUtils.auditConsumerCredentialsEvent(auditAction, consumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ return Either.left(user);
+ }
+
+ private Either<ConsumerDefinition, ResponseFormat> validateConsumer(ConsumerDefinition consumer, User user, AuditingActionEnum audatingAction) {
+ Either<ConsumerDefinition, ResponseFormat> validateConsumerName = validateConsumerName(consumer);
+ if (validateConsumerName.isRight()) {
+ return Either.right(validateConsumerName.right().value());
+ }
+ Either<ConsumerDefinition, ResponseFormat> validateConsumerPassword = validateConsumerPassword(consumer);
+ if (validateConsumerPassword.isRight()) {
+ return Either.right(validateConsumerPassword.right().value());
+ }
+ consumer = validateConsumerPassword.left().value();
+ Either<ConsumerDefinition, ResponseFormat> validateEcompUserSalt = validateConsumerSalt(consumer);
+ if (validateEcompUserSalt.isRight()) {
+ return Either.right(validateEcompUserSalt.right().value());
+ }
+ return Either.left(consumer);
+
+ }
+
+ private Either<ConsumerDefinition, ResponseFormat> validateConsumerName(ConsumerDefinition consumer) {
+ String name = consumer.getConsumerName();
+ if (!ValidationUtils.validateStringNotEmpty(name)) {
+ log.debug("Consumer name cannot be empty.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, CONSUMER_NAME));
+ }
+ if (!ValidationUtils.validateConsumerName(name)) {
+ log.debug("Consumer name is invalid.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT_PARAM, CONSUMER_NAME));
+ }
+ if (!ValidationUtils.validateLength(name, ValidationUtils.CONSUMER_NAME_MAX_LENGTH)) {
+ log.debug("Consumer name exceeds limit.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, CONSUMER_NAME, String.valueOf(ValidationUtils.CONSUMER_NAME_MAX_LENGTH)));
+ }
+ if (!ValidationUtils.isUTF8Str(name)) {
+ log.debug("Consumer name includes non UTF 8 characters.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT_PARAM, CONSUMER_NAME));
+ }
+
+ return Either.left(consumer);
+ }
+
+ private Either<ConsumerDefinition, ResponseFormat> validateConsumerPassword(ConsumerDefinition consumer) {
+ String password = consumer.getConsumerPassword();
+ if (!ValidationUtils.validateStringNotEmpty(password)) {
+ log.debug("Consumer password cannot be empty.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, CONSUMER_PW));
+ }
+ if (password.length() != ValidationUtils.CONSUMER_PASSWORD_LENGTH) {
+ log.debug("Consumer password length is not valid.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_LENGTH, CONSUMER_PW));
+ }
+ consumer.setConsumerPassword(password.toLowerCase());
+ if (!ValidationUtils.validateConsumerPassSalt(consumer.getConsumerPassword())) {
+ log.debug("Consumer password is invalid.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT_PARAM, CONSUMER_PW));
+ }
+
+ return Either.left(consumer);
+ }
+
+ private Either<ConsumerDefinition, ResponseFormat> validateConsumerSalt(ConsumerDefinition consumer) {
+ String salt = consumer.getConsumerSalt();
+ if (!ValidationUtils.validateStringNotEmpty(salt)) {
+ log.debug("Consumer salt cannot be empty.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_DATA, CONSUMER_SALT));
+ }
+ if (salt.length() != ValidationUtils.CONSUMER_SALT_LENGTH) {
+ log.debug("Consumer salt length is not valid.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_LENGTH, CONSUMER_SALT));
+ }
+ if (!ValidationUtils.validateConsumerPassSalt(salt)) {
+ log.debug("Consumer salt is invalid.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT_PARAM, CONSUMER_SALT));
+ }
+
+ return Either.left(consumer);
+ }
+
+ public Either<ConsumerDefinition, ResponseFormat> getConsumer(String consumerId, User user) {
+ ConsumerDefinition tmpConsumer = new ConsumerDefinition();
+ tmpConsumer.setConsumerName(consumerId);
+ // In case of filter (southbound) call
+ if (user != null) {
+ Either<User, ResponseFormat> userValidation = validateUser(user, tmpConsumer, AuditingActionEnum.GET_ECOMP_USER_CREDENTIALS);
+ if (userValidation.isRight()) {
+ return Either.right(userValidation.right().value());
+ }
+ user = userValidation.left().value();
+ }
+ Either<ConsumerData, StorageOperationStatus> getResult = consumerOperation.getCredentials(consumerId);
+ if (getResult.isRight()) {
+ ActionStatus action = componentsUtils.convertFromStorageResponseForConsumer(getResult.right().value());
+ ResponseFormat responseFormat;
+ if (action == ActionStatus.ECOMP_USER_NOT_FOUND) {
+ responseFormat = componentsUtils.getResponseFormat(action, consumerId);
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(action);
+ }
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.GET_ECOMP_USER_CREDENTIALS, tmpConsumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ ConsumerDefinition consumer = new ConsumerDefinition(getResult.left().value().getConsumerDataDefinition());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.GET_ECOMP_USER_CREDENTIALS, consumer, responseFormat, user);
+ return Either.left(consumer);
+ }
+
+ public Either<ConsumerDefinition, ResponseFormat> getConsumer(String consumerId) {
+ return getConsumer(consumerId, null);
+ }
+
+ public Either<ConsumerDefinition, ResponseFormat> deleteConsumer(String consumerId, User user) {
+ ConsumerDefinition tmpConsumer = new ConsumerDefinition();
+ tmpConsumer.setConsumerName(consumerId);
+ Either<User, ResponseFormat> userValidation = validateUser(user, tmpConsumer, AuditingActionEnum.DELETE_ECOMP_USER_CREDENTIALS);
+ if (userValidation.isRight()) {
+ return Either.right(userValidation.right().value());
+ }
+ user = userValidation.left().value();
+ Either<ConsumerData, StorageOperationStatus> deleteResult = consumerOperation.deleteCredentials(consumerId);
+ if (deleteResult.isRight()) {
+ ActionStatus action = componentsUtils.convertFromStorageResponseForConsumer(deleteResult.right().value());
+ ResponseFormat responseFormat;
+ if (action == ActionStatus.ECOMP_USER_NOT_FOUND) {
+ responseFormat = componentsUtils.getResponseFormat(action, consumerId);
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(action);
+ }
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.DELETE_ECOMP_USER_CREDENTIALS, tmpConsumer, responseFormat, user);
+ return Either.right(responseFormat);
+ }
+ ConsumerDefinition consumer = new ConsumerDefinition(deleteResult.left().value().getConsumerDataDefinition());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ componentsUtils.auditConsumerCredentialsEvent(AuditingActionEnum.DELETE_ECOMP_USER_CREDENTIALS, consumer, responseFormat, user);
+ return Either.left(consumer);
+ }
+
+ public Either<ConsumerDefinition, ResponseFormat> updateConsumer(ConsumerDefinition consumer, User modifier, boolean isCreateRequest) {
+ Either<ConsumerData, StorageOperationStatus> updateResult = consumerOperation.updateCredentials(new ConsumerData(consumer));
+ if (updateResult.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponseForConsumer(updateResult.right().value()));
+ return Either.right(responseFormat);
+ }
+ consumer = new ConsumerDefinition(updateResult.left().value().getConsumerDataDefinition());
+ return Either.left(consumer);
+ }
+
+ private void checkFieldsForOverrideAttempt(ConsumerDefinition consumer) {
+ if (consumer.getConsumerDetailsLastupdatedtime() != null) {
+ log.info("Consumer Details Last updated time cannot be defined by user. This field will be overridden by the application");
+ }
+ if (consumer.getConsumerLastAuthenticationTime() != null) {
+ log.info("Consumer Last Authentication time cannot be defined by user. This field will be overridden by the application");
+ }
+ if (consumer.getLastModfierAtuid() != null) {
+ log.info("Consumer Last Modifier USER_ID cannot be defined by user. This field will be overridden by the application");
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CsarValidationUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CsarValidationUtils.java
new file mode 100644
index 0000000000..badb257a80
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CsarValidationUtils.java
@@ -0,0 +1,265 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class CsarValidationUtils {
+
+ private static Logger log = LoggerFactory.getLogger(CsarValidationUtils.class.getName());
+
+ private static final String TOSCA_META_FILE_VERSION = "TOSCA-Meta-File-Version";
+
+ private static final String CSAR_VERSION = "CSAR-Version";
+
+ private static final String CREATED_BY = "Created-By";
+
+ private static final String NEW_LINE_DELM = "\n";
+
+ public final static String TOSCA_METADATA_FILE = "TOSCA-Metadata/TOSCA.meta";
+
+ public static final String TOSCA_META_ENTRY_DEFINITIONS = "Entry-Definitions";
+
+ public final static String[] TOSCA_METADATA_FIELDS = { TOSCA_META_FILE_VERSION, CSAR_VERSION, CREATED_BY, TOSCA_META_ENTRY_DEFINITIONS };
+
+ public final static String ARTIFACTS_METADATA_FILE = "HEAT.meta";
+
+ public final static String ARTIFACTS = "Artifacts/";
+
+ public static final String TOSCA_CSAR_EXTENSION = ".csar";
+
+ public static Either<Boolean, ResponseFormat> validateCsar(Map<String, byte[]> csar, String csarUUID, ComponentsUtils componentsUtils) {
+ Either<Boolean, ResponseFormat> validateStatus = validateIsTOSCAMetadataExist(csar, csarUUID, componentsUtils);
+ if (validateStatus.isRight()) {
+ return Either.right(validateStatus.right().value());
+ }
+ log.trace("TOSCA-Metadata/TOSCA.meta file found, CSAR id {}", csarUUID);
+ return validateTOSCAMetadataFile(csar, csarUUID, componentsUtils);
+
+ }
+
+ public static Either<ImmutablePair<String, String>, ResponseFormat> getToscaYaml(Map<String, byte[]> csar, String csarUUID, ComponentsUtils componentsUtils) {
+ Either<Boolean, ResponseFormat> validateStatus = validateIsTOSCAMetadataExist(csar, csarUUID, componentsUtils);
+ if (validateStatus.isRight()) {
+ return Either.right(validateStatus.right().value());
+ }
+ byte[] toscaMetaBytes = csar.get(TOSCA_METADATA_FILE);
+ Properties props = new Properties();
+ try {
+ props.load(new ByteArrayInputStream(toscaMetaBytes));
+ } catch (IOException e) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file is not in expected key-value form in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, csarUUID));
+ }
+
+ String yamlFileName = props.getProperty(TOSCA_META_ENTRY_DEFINITIONS);
+
+ if (!csar.containsKey(yamlFileName)) {
+ log.debug("Entry-Definitions entry not found in TOSCA-Metadata/TOSCA.meta file, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("Entry-Definitions entry not found in TOSCA-Metadata/TOSCA.meta file in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.YAML_NOT_FOUND_IN_CSAR, csarUUID, yamlFileName));
+ }
+
+ log.trace("Found Entry-Definitions property in TOSCA-Metadata/TOSCA.meta, Entry-Definitions: {}, CSAR id: {}", yamlFileName, csarUUID);
+ byte[] yamlFileBytes = csar.get(yamlFileName);
+ if (yamlFileBytes == null) {
+ log.debug("Entry-Definitions {} file not found in csar, csar ID {}", yamlFileName, csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("Entry-Definitions " + yamlFileName + " file not found in CSAR with id " + csarUUID, "CSAR structure is invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.YAML_NOT_FOUND_IN_CSAR, csarUUID, yamlFileName));
+ }
+
+ String yamlFileContents = new String(yamlFileBytes);
+
+ return Either.left(new ImmutablePair<String, String>(yamlFileName, yamlFileContents));
+ }
+
+ public static Either<ImmutablePair<String, String>, ResponseFormat> getArtifactsMeta(Map<String, byte[]> csar, String csarUUID, ComponentsUtils componentsUtils) {
+
+ if (!csar.containsKey(ARTIFACTS + ARTIFACTS_METADATA_FILE)) {
+ log.debug("Entry-Definitions entry not found in TOSCA-Metadata/TOSCA.meta file, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("Entry-Definitions entry not found in TOSCA-Metadata/TOSCA.meta file in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.YAML_NOT_FOUND_IN_CSAR, csarUUID, ARTIFACTS_METADATA_FILE));
+ }
+
+ log.trace("Found Entry-Definitions property in TOSCA-Metadata/TOSCA.meta, Entry-Definitions: {}, CSAR id: {}", ARTIFACTS_METADATA_FILE, csarUUID);
+ byte[] artifactsMetaBytes = csar.get(ARTIFACTS + ARTIFACTS_METADATA_FILE);
+ if (artifactsMetaBytes == null) {
+ log.debug("Entry-Definitions {} file not found in csar, csar ID {}", ARTIFACTS + ARTIFACTS_METADATA_FILE, csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("Entry-Definitions " + ARTIFACTS + ARTIFACTS_METADATA_FILE + " file not found in CSAR with id " + csarUUID, "CSAR structure is invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.YAML_NOT_FOUND_IN_CSAR, csarUUID, ARTIFACTS + ARTIFACTS_METADATA_FILE));
+ }
+
+ String artifactsFileContents = new String(artifactsMetaBytes);
+
+ return Either.left(new ImmutablePair<String, String>(ARTIFACTS + ARTIFACTS_METADATA_FILE, artifactsFileContents));
+ }
+
+ public static Either<ImmutablePair<String, byte[]>, ResponseFormat> getArtifactsContent(String csarUUID, Map<String, byte[]> csar, String artifactName, ComponentsUtils componentsUtils) {
+ if (!csar.containsKey(ARTIFACTS + artifactName)) {
+ log.debug("Entry-Definitions entry not found in Artifacts/HEAT.meta file, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("Entry-Definitions entry not found in TOSCA-Metadata/TOSCA.meta file in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND_IN_CSAR, ARTIFACTS + artifactName, csarUUID));
+ }
+
+ log.trace("Found Entry-Definitions property in Artifacts/HEAT.meta, Entry-Definitions: {}, CSAR id: {}", ARTIFACTS + artifactName, csarUUID);
+ byte[] artifactFileBytes = csar.get(ARTIFACTS + artifactName);
+ if (artifactFileBytes == null) {
+ log.debug("Entry-Definitions {} file not found in csar, csar ID {}", ARTIFACTS + artifactName, csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("Entry-Definitions " + ARTIFACTS + artifactName + " file not found in CSAR with id " + csarUUID, "CSAR structure is invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND_IN_CSAR, ARTIFACTS + artifactName, csarUUID));
+ }
+
+ return Either.left(new ImmutablePair<String, byte[]>(artifactName, artifactFileBytes));
+ }
+
+ private static Either<Boolean, ResponseFormat> validateTOSCAMetadataFile(Map<String, byte[]> csar, String csarUUID, ComponentsUtils componentsUtils) {
+
+ byte[] toscaMetaBytes = csar.get(TOSCA_METADATA_FILE);
+ String toscaMetadata = new String(toscaMetaBytes);
+ String[] splited = toscaMetadata.split(NEW_LINE_DELM);
+ if (splited == null || splited.length < TOSCA_METADATA_FIELDS.length) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file is not in expected key-value form in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, csarUUID));
+ }
+ /*
+ * if(splited.length == TOSCA_METADATA_FIELDS.length){ if(!toscaMetadata.endsWith(NEW_LINE_DELM)){ log. debug("TOSCA-Metadata/TOSCA.meta file is not in expected key-value form in csar, csar ID {}" , csarUUID);
+ * BeEcompErrorManager.getInstance(). logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " +csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR); return
+ * Either.right(componentsUtils.getResponseFormat(ActionStatus. CSAR_INVALID_FORMAT, csarUUID)); } }
+ */
+
+ Either<Boolean, ResponseFormat> block_0Status = validateBlock_0(csarUUID, splited, componentsUtils);
+ if (block_0Status.isRight()) {
+ return Either.right(block_0Status.right().value());
+ }
+
+ return Either.left(true);
+
+ }
+
+ private static Either<Boolean, ResponseFormat> validateBlock_0(String csarUUID, String[] splited, ComponentsUtils componentsUtils) {
+ int index = 0;
+ for (String toscaField : TOSCA_METADATA_FIELDS) {
+
+ Properties props = new Properties();
+
+ try {
+ props.load(new ByteArrayInputStream(splited[index].getBytes()));
+ } catch (IOException e) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file is not in expected key-value form in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, csarUUID));
+ }
+ if (!props.containsKey(toscaField)) {
+ log.debug("TOSCA.meta file format is invalid: No new line after block_0 as expected in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, csarUUID));
+ }
+ String value = props.getProperty(toscaField);
+ if (value == null || value.isEmpty()) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file is not in expected key-value form in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, csarUUID));
+ }
+
+ // TOSCA-Meta-File-Version & CSAR-Version : digit.digit - format
+ // validation
+ if (toscaField.equals(TOSCA_META_FILE_VERSION) || toscaField.equals(CSAR_VERSION)) {
+ if (!validateTOSCAMetaProperty(value)) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file contains {} in wrong format (digit.digit), csar ID {}", toscaField, csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not in expected key-value form in CSAR with id " + csarUUID, "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, csarUUID));
+ }
+ }
+ index++;
+ }
+ return Either.left(true);
+ }
+
+ private static boolean validateTOSCAMetaProperty(String toscaProperty) {
+ final String FLOAT_STRING = "^\\d{1}[.]\\d{1}$";
+ final Pattern FLOAT_PATTERN = Pattern.compile(FLOAT_STRING);
+
+ Matcher floatMatcher = FLOAT_PATTERN.matcher(toscaProperty);
+ return floatMatcher.matches();
+ }
+
+ private static Either<Boolean, ResponseFormat> validateIsTOSCAMetadataExist(Map<String, byte[]> csar, String csarUUID, ComponentsUtils componentsUtils) {
+ if (csar == null || csar.isEmpty()) {
+ log.debug("Error when fetching csar with ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Creating resource from CSAR: fetching CSAR with id " + csarUUID + " failed");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID, csarUUID);
+ return Either.right(responseFormat);
+ }
+ if (!csar.containsKey(TOSCA_METADATA_FILE)) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file not found in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not found in CSAR with id " + csarUUID, "CSAR structure is invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID, csarUUID));
+ }
+ byte[] toscaMetaBytes = csar.get(TOSCA_METADATA_FILE);
+ // Tal && exchanged for ||
+ if (toscaMetaBytes == null || toscaMetaBytes.length == 0) {
+ log.debug("TOSCA-Metadata/TOSCA.meta file not found in csar, csar ID {}", csarUUID);
+ BeEcompErrorManager.getInstance().logInternalDataError("TOSCA-Metadata/TOSCA.meta file not found in CSAR with id " + csarUUID, "CSAR structure is invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID, csarUUID));
+ }
+
+ return Either.left(Boolean.TRUE);
+ }
+
+ public static Either<String, ResponseFormat> getToscaYamlChecksum(Map<String, byte[]> csar, String csarUUID, ComponentsUtils componentsUtils) {
+
+ Either<ImmutablePair<String, String>, ResponseFormat> toscaYamlRes = getToscaYaml(csar, csarUUID, componentsUtils);
+ if (toscaYamlRes.isRight() || toscaYamlRes.left().value() == null || toscaYamlRes.left().value().getRight() == null) {
+ log.debug("Faild to create toscaYamlChecksum for csar, csar ID {}", csarUUID);
+ return Either.right(toscaYamlRes.right().value());
+ }
+
+ String newCheckSum = GeneralUtility.calculateMD5ByByteArray(toscaYamlRes.left().value().getRight().getBytes());
+ return Either.left(newCheckSum);
+
+ }
+
+ public static boolean isCsarPayloadName(String payloadName) {
+ if (payloadName == null)
+ return false;
+ return payloadName.toLowerCase().endsWith(TOSCA_CSAR_EXTENSION);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java
new file mode 100644
index 0000000000..e5fba0dff5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("dataTypeImportManager")
+public class DataTypeImportManager {
+
+ public static void main(String[] args) {
+
+ List<PropertyDefinition> properties = new ArrayList<>();
+ PropertyDefinition propertyDefintion = new PropertyDefinition();
+ propertyDefintion.setName("aaa");
+ properties.add(propertyDefintion);
+
+ List<String> allParentsProps = new ArrayList<>();
+ allParentsProps.add("aaa");
+ allParentsProps.add("bbb");
+
+ Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
+ System.out.println(alreadyExistPropsCollection);
+
+ }
+
+ private static Logger log = LoggerFactory.getLogger(DataTypeImportManager.class.getName());
+ @Resource
+ private PropertyOperation propertyOperation;
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Resource
+ private CommonImportManager commonImportManager;
+
+ public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(String dataTypeYml) {
+ return commonImportManager.createElementTypes(dataTypeYml, elementTypeYml -> createDataTypesFromYml(elementTypeYml), elementTypesList -> createDataTypesByDao(elementTypesList), ElementTypeEnum.DataType);
+ }
+
+ private Either<List<DataTypeDefinition>, ActionStatus> createDataTypesFromYml(String dataTypesYml) {
+
+ return commonImportManager.createElementTypesFromYml(dataTypesYml, (dataTypeName, dataTypeJsonData) -> createDataType(dataTypeName, dataTypeJsonData));
+
+ }
+
+ private Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypesByDao(List<DataTypeDefinition> dataTypesToCreate) {
+
+ return commonImportManager.createElementTypesByDao(dataTypesToCreate, dataType -> validateDataType(dataType), dataType -> new ImmutablePair<>(ElementTypeEnum.DataType, dataType.getName()),
+ dataTypeName -> propertyOperation.getDataTypeByNameWithoutDerived(dataTypeName), dataType -> propertyOperation.addDataType(dataType), (newDataType, oldDataType) -> propertyOperation.updateDataType(newDataType, oldDataType));
+ }
+
+ private Either<ActionStatus, ResponseFormat> validateDataType(DataTypeDefinition dataType) {
+
+ String dataTypeName = dataType.getName();
+ List<PropertyDefinition> properties = dataType.getProperties();
+ if (properties == null) {
+ // At least one parameter should be defined either in the properties
+ // section or at one of the parents
+ String derivedDataType = dataType.getDerivedFromName();
+ // If there are no properties, then we can create a data type if it
+ // is an abstract one or it derives from non abstract data type
+ if ((derivedDataType == null || derivedDataType.isEmpty())) {
+ if (false == isAbstract(dataType.getName())) {
+ if (false == ToscaPropertyType.isScalarType(dataTypeName)) {
+ log.debug("Data type {} must have properties unless it derives from non abstract data type", dataType.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
+
+ return Either.right(responseFormat);
+ }
+ }
+ } else {
+ // if it is not a scalar data type and it derives from abstract
+ // data type, we should reject the request.
+ if (false == ToscaPropertyType.isScalarType(dataTypeName) && true == isAbstract(derivedDataType)) {
+ log.debug("Data type {} which derived from abstract data type must have at least one property", dataType.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
+
+ return Either.right(responseFormat);
+ }
+ }
+ } else {
+ // properties tag cannot be empty
+ if (properties.isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, dataType, null);
+
+ return Either.right(responseFormat);
+ }
+
+ // check no duplicates
+ Set<String> collect = properties.stream().map(p -> p.getName()).collect(Collectors.toSet());
+ if (collect != null) {
+ if (properties.size() != collect.size()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DUPLICATE_PROPERTY, dataType, null);
+
+ return Either.right(responseFormat);
+ }
+ }
+
+ List<String> propertiesWithSameTypeAsDataType = properties.stream().filter(p -> p.getType().equals(dataType.getName())).map(p -> p.getName()).collect(Collectors.toList());
+ if (propertiesWithSameTypeAsDataType != null && propertiesWithSameTypeAsDataType.isEmpty() == false) {
+ log.debug("The data type {} contains properties with the type {}", dataType.getName(), dataType.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, dataType, propertiesWithSameTypeAsDataType);
+
+ return Either.right(responseFormat);
+ }
+ }
+
+ String derivedDataType = dataType.getDerivedFromName();
+ if (derivedDataType != null) {
+ Either<DataTypeDefinition, StorageOperationStatus> derivedDataTypeByName = propertyOperation.getDataTypeByName(derivedDataType, true);
+ if (derivedDataTypeByName.isRight()) {
+ StorageOperationStatus status = derivedDataTypeByName.right().value();
+ if (status == StorageOperationStatus.NOT_FOUND) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DERIVED_IS_MISSING, dataType, null);
+
+ return Either.right(responseFormat);
+ } else {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.GENERAL_ERROR, dataType, null);
+
+ return Either.right(responseFormat);
+
+ }
+ } else {
+
+ DataTypeDefinition derivedDataTypeDef = derivedDataTypeByName.left().value();
+ if (properties != null && properties.isEmpty() == false) {
+
+ if (true == isScalarType(derivedDataTypeDef)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_CANNOT_HAVE_PROPERTIES, dataType, null);
+
+ return Either.right(responseFormat);
+ }
+
+ Set<String> allParentsProps = new HashSet<>();
+ do {
+ List<PropertyDefinition> currentParentsProps = derivedDataTypeDef.getProperties();
+ if (currentParentsProps != null) {
+ for (PropertyDefinition propertyDefinition : currentParentsProps) {
+ allParentsProps.add(propertyDefinition.getName());
+ }
+ }
+ derivedDataTypeDef = derivedDataTypeDef.getDerivedFrom();
+ } while (derivedDataTypeDef != null);
+
+ // Check that no property is already defined in one of the
+ // ancestors
+ Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
+ if (alreadyExistPropsCollection != null && alreadyExistPropsCollection.isEmpty() == false) {
+ List<String> duplicateProps = new ArrayList<>();
+ duplicateProps.addAll(alreadyExistPropsCollection);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, dataType, duplicateProps);
+
+ return Either.right(responseFormat);
+ }
+
+ }
+ }
+ }
+ return Either.left(ActionStatus.OK);
+ }
+
+ private boolean isAbstract(String dataTypeName) {
+
+ ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
+
+ return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract() == true;
+
+ }
+
+ private boolean isScalarType(DataTypeDefinition dataTypeDef) {
+
+ boolean isScalar = false;
+ DataTypeDefinition dataType = dataTypeDef;
+
+ while (dataType != null) {
+
+ String name = dataType.getName();
+ if (ToscaPropertyType.isScalarType(name)) {
+ isScalar = true;
+ break;
+ }
+
+ dataType = dataType.getDerivedFrom();
+ }
+
+ return isScalar;
+ }
+
+ private DataTypeDefinition createDataType(String dataTypeName, Map<String, Object> toscaJson) {
+ DataTypeDefinition dataType = new DataTypeDefinition();
+
+ dataType.setName(dataTypeName);
+
+ if (toscaJson != null) {
+ // Description
+ final Consumer<String> descriptionSetter = description -> dataType.setDescription(description);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DESCRIPTION.getElementName(), descriptionSetter);
+ // Derived From
+ final Consumer<String> derivedFromSetter = derivedFrom -> dataType.setDerivedFromName(derivedFrom);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DERIVED_FROM.getElementName(), derivedFromSetter);
+ // Properties
+ commonImportManager.setProperties(toscaJson, (values) -> dataType.setProperties(values));
+
+ setConstraints(toscaJson, dataType);
+ }
+ return dataType;
+ }
+
+ private void setConstraints(Map<String, Object> toscaJson, DataTypeDefinition dataType) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DistributionMonitoringBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DistributionMonitoringBusinessLogic.java
new file mode 100644
index 0000000000..9d9425ea85
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DistributionMonitoringBusinessLogic.java
@@ -0,0 +1,214 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.info.DistributionStatusInfo;
+import org.openecomp.sdc.be.info.DistributionStatusListResponse;
+import org.openecomp.sdc.be.info.DistributionStatusOfServiceInfo;
+import org.openecomp.sdc.be.info.DistributionStatusOfServiceListResponce;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingGenericEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionStatusEvent;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+@Component("distributionMonitoringBusinessLogic")
+public class DistributionMonitoringBusinessLogic extends BaseBusinessLogic {
+ private static final String DEPLOYED = "Deployed";
+
+ private static final String ERROR = "Error";
+
+ private static final String DISTRIBUTED = "Distributed";
+
+ private static final String IN_PROGRESS = "In Progress";
+
+ private static Logger log = LoggerFactory.getLogger(ArtifactsBusinessLogic.class.getName());
+
+ // @javax.annotation.Resource
+ // private AuditingDao auditingDao;
+
+ @Autowired
+ private AuditCassandraDao cassandraDao;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ public DistributionMonitoringBusinessLogic() {
+ }
+
+ public Either<DistributionStatusListResponse, ResponseFormat> getListOfDistributionStatus(String did, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get List Of Distribution Status", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ log.trace("getListOfDistributionStatus for did {}", did);
+ Either<List<DistributionStatusEvent>, ActionStatus> distributionStatus = cassandraDao.getListOfDistributionStatuses(did);
+ if (distributionStatus.isRight()) {
+ log.debug("not found distribution statuses for did {} status is {} ", did, distributionStatus.right().value());
+ return Either.right(componentsUtils.getResponseFormat(distributionStatus.right().value(), did));
+ }
+ List<DistributionStatusInfo> distribStatusInfoList = new ArrayList<DistributionStatusInfo>();
+ List<DistributionStatusEvent> distributionStatusEventList = distributionStatus.left().value();
+ if (distributionStatusEventList != null) {
+ for (ESTimeBasedEvent distributionStatusEvent : distributionStatusEventList) {
+ distribStatusInfoList.add(new DistributionStatusInfo(distributionStatusEvent));
+ }
+ }
+
+ DistributionStatusListResponse distributionStatusListResponse = new DistributionStatusListResponse();
+ distributionStatusListResponse.setDistributionStatusList(distribStatusInfoList);
+ log.trace("list statuses for did {} is {} ", did, distribStatusInfoList);
+ return Either.left(distributionStatusListResponse);
+ }
+
+ public Either<DistributionStatusOfServiceListResponce, ResponseFormat> getListOfDistributionServiceStatus(String serviceUuid, String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get List Of Distribution Service Status", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ log.trace("getListOfDistributionServiceStatus for serviceUUID {}", serviceUuid);
+ Either<List<? extends AuditingGenericEvent>, ActionStatus> status = cassandraDao.getServiceDistributionStatusesList(serviceUuid);
+ if (status.isRight()) {
+ log.debug("failed to find service distribution statuses. error: {}", status);
+ return Either.right(componentsUtils.getResponseFormat(status.right().value(), serviceUuid));
+ }
+ List<DistributionStatusOfServiceInfo> distribStatusInfoList = new ArrayList<DistributionStatusOfServiceInfo>();
+ List<? extends AuditingGenericEvent> distributionStatusEventList = status.left().value();
+ distribStatusInfoList = handleAuditingDaoResponse(distributionStatusEventList);
+ DistributionStatusOfServiceListResponce distributionStatusListResponse = new DistributionStatusOfServiceListResponce();
+ distributionStatusListResponse.setDistributionStatusOfServiceList(distribStatusInfoList);
+ return Either.left(distributionStatusListResponse);
+ }
+
+ private List<DistributionStatusOfServiceInfo> handleAuditingDaoResponse(List<? extends AuditingGenericEvent> distribStatusInfoList) {
+ List<DistributionStatusOfServiceInfo> reslist = new ArrayList<DistributionStatusOfServiceInfo>();
+ Map<String, List<AuditingGenericEvent>> serviceDidMap = createServiceDidMap(distribStatusInfoList);
+ Set<String> didSet = serviceDidMap.keySet();
+ for (String did : didSet) {
+ DistributionStatusOfServiceInfo distributionStatusOfServiceInfo = new DistributionStatusOfServiceInfo();
+ distributionStatusOfServiceInfo.setDistributionID(did);
+ String dReguestStatus = "";
+ String dNotifyStatus = "";
+ boolean isResult = false;
+ List<? extends AuditingGenericEvent> auditingGenericEventList = serviceDidMap.get(did);
+ ESTimeBasedEvent resAuditingGenericEvent = null;
+ for (AuditingGenericEvent auditingGenericEvent : auditingGenericEventList) {
+ auditingGenericEvent.fillFields();
+
+ String action = (String) auditingGenericEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName());
+ Object modifierUserId = auditingGenericEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName());
+ if (modifierUserId != null) {
+ distributionStatusOfServiceInfo.setUserId((String) modifierUserId);
+ }
+
+ if (action.equals(AuditingActionEnum.DISTRIBUTION_DEPLOY.getName())) {
+
+ isResult = true;
+ resAuditingGenericEvent = auditingGenericEvent;
+ break;
+ } else if (action.equals(AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST.getName())) {
+ dReguestStatus = getStatusFromAuditEvent(auditingGenericEvent);
+ } else if (action.equals(AuditingActionEnum.DISTRIBUTION_NOTIFY.getName())) {
+ dNotifyStatus = getStatusFromAuditEvent(auditingGenericEvent);
+ }
+
+ resAuditingGenericEvent = auditingGenericEvent;
+
+ }
+ distributionStatusOfServiceInfo.setTimestamp((String) resAuditingGenericEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName()));
+
+ if (!isResult) {
+ if (dReguestStatus.equals(String.valueOf(HttpStatus.SC_OK))) {
+ if (dNotifyStatus.isEmpty()) {
+ distributionStatusOfServiceInfo.setDeployementStatus(IN_PROGRESS);
+
+ } else {
+ if (dNotifyStatus.equals(String.valueOf(HttpStatus.SC_OK)))
+ distributionStatusOfServiceInfo.setDeployementStatus(DISTRIBUTED);
+ else
+ distributionStatusOfServiceInfo.setDeployementStatus(ERROR);
+ }
+ } else
+ distributionStatusOfServiceInfo.setDeployementStatus(ERROR);
+ } else
+ distributionStatusOfServiceInfo.setDeployementStatus(DEPLOYED);
+ reslist.add(distributionStatusOfServiceInfo);
+ }
+
+ return reslist;
+ }
+
+ private String getStatusFromAuditEvent(ESTimeBasedEvent auditingGenericEvent) {
+ String status = "";
+ Object requestStatus = auditingGenericEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName());
+ if (requestStatus instanceof String) {
+ status = (String) requestStatus;
+ }
+ return status;
+ }
+
+ private Map<String, List<AuditingGenericEvent>> createServiceDidMap(List<? extends AuditingGenericEvent> distribStatusInfoList) {
+
+ Map<String, List<AuditingGenericEvent>> serviceDidMap = new HashMap<String, List<AuditingGenericEvent>>();
+ for (AuditingGenericEvent auditingGenericEvent : distribStatusInfoList) {
+ List<AuditingGenericEvent> auditingGenericEventList = null;
+ String did = "";
+ auditingGenericEvent.fillFields();
+
+ Object didValue = auditingGenericEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID.getDisplayName());
+ if (didValue != null) {
+ did = (String) didValue;
+ }
+
+ if (!did.isEmpty()) {
+ if (serviceDidMap.containsKey(did)) {
+ auditingGenericEventList = serviceDidMap.get(did);
+ }
+ if (auditingGenericEventList == null) {
+ auditingGenericEventList = new ArrayList();
+
+ }
+ auditingGenericEventList.add(auditingGenericEvent);
+ serviceDidMap.put(did, auditingGenericEventList);
+ }
+ }
+ return serviceDidMap;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java
new file mode 100644
index 0000000000..5ebb86f1ba
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ElementBusinessLogic.java
@@ -0,0 +1,1125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
+import org.openecomp.sdc.be.datamodel.utils.NodeTypeConvertUtils;
+import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.PropertyScope;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.Tag;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("elementsBusinessLogic")
+public class ElementBusinessLogic extends BaseBusinessLogic {
+
+ private static Logger log = LoggerFactory.getLogger(ElementBusinessLogic.class.getName());
+
+ @javax.annotation.Resource
+ private IElementOperation elementOperation;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ @javax.annotation.Resource
+ private UserBusinessLogic userAdminManager;
+
+ /**
+ *
+ * @param user
+ * @return
+ */
+ public Either<Map<String, List<? extends Component>>, ResponseFormat> getFollowed(User user) {
+ Either<Map<String, List<? extends Component>>, ResponseFormat> response = null;
+ // Getting the role
+ String role = user.getRole();
+ String userId = null;
+ Role currentRole = Role.valueOf(role);
+
+ switch (currentRole) {
+ case DESIGNER:
+ userId = user.getUserId();
+ response = handleDesigner(userId);
+ break;
+
+ case TESTER:
+ userId = user.getUserId();
+ response = handleTester(userId);
+ break;
+
+ case GOVERNOR:
+ userId = user.getUserId();
+ response = handleGovernor(userId);
+ break;
+
+ case OPS:
+ userId = user.getUserId();
+ response = handleOps(userId);
+ break;
+
+ case PRODUCT_STRATEGIST:
+ userId = user.getUserId();
+ response = handleProductStrategist(userId);
+ break;
+
+ case PRODUCT_MANAGER:
+ userId = user.getUserId();
+ response = handleProductManager(userId);
+ break;
+
+ case ADMIN:
+ response = handleAdmin();
+ break;
+
+ default:
+ response = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ break;
+ }
+ return response;
+
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleAdmin() {
+ Either<Map<String, List<? extends Component>>, ResponseFormat> response;
+ // userId should stay null
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ Set<LifecycleStateEnum> lastStateStates = new HashSet<LifecycleStateEnum>();
+ lifecycleStates.add(LifecycleStateEnum.CERTIFIED);
+ response = getFollowedResourcesAndServices(null, lifecycleStates, lastStateStates);
+ return response;
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleDesigner(String userId) {
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ Set<LifecycleStateEnum> lastStateStates = new HashSet<LifecycleStateEnum>();
+ Either<Map<String, List<? extends Component>>, ResponseFormat> response;
+ lifecycleStates.add(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ lifecycleStates.add(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ lifecycleStates.add(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ lifecycleStates.add(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ lifecycleStates.add(LifecycleStateEnum.CERTIFIED);
+ // more states
+ lastStateStates.add(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ response = getFollowedResourcesAndServices(userId, lifecycleStates, lastStateStates);
+ return response;
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleGovernor(String userId) {
+ Either<Map<String, List<? extends Component>>, ResponseFormat> result = handleFollowedCertifiedServices(null);
+ return result;
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleProductStrategist(String userId) {
+ // Should be empty list according to Ella, 13/03/16
+ Map<String, List<? extends Component>> result = new HashMap<String, List<? extends Component>>();
+ result.put("products", new ArrayList<>());
+ return Either.left(result);
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleProductManager(String userId) {
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ Set<LifecycleStateEnum> lastStateStates = new HashSet<LifecycleStateEnum>();
+ Either<Map<String, List<? extends Component>>, ResponseFormat> response;
+ lifecycleStates.add(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ lifecycleStates.add(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ lifecycleStates.add(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ lifecycleStates.add(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ lifecycleStates.add(LifecycleStateEnum.CERTIFIED);
+ // more states
+ lastStateStates.add(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ response = getFollowedProducts(userId, lifecycleStates, lastStateStates);
+ return response;
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleOps(String userId) {
+ Set<DistributionStatusEnum> distStatus = new HashSet<DistributionStatusEnum>();
+ distStatus.add(DistributionStatusEnum.DISTRIBUTION_APPROVED);
+ distStatus.add(DistributionStatusEnum.DISTRIBUTED);
+
+ Either<Map<String, List<? extends Component>>, ResponseFormat> result = handleFollowedCertifiedServices(distStatus);
+ return result;
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleFollowedCertifiedServices(Set<DistributionStatusEnum> distStatus) {
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ Either<Set<Service>, StorageOperationStatus> services = serviceOperation.getCertifiedServicesWithDistStatus(propertiesToMatch, distStatus, false);
+ if (services.isLeft()) {
+ Map<String, List<? extends Component>> result = new HashMap<String, List<? extends Component>>();
+ List<Service> list = new ArrayList<>();
+ list.addAll(services.left().value());
+ result.put("services", list);
+ return Either.left(result);
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(services.right().value())));
+ }
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> handleTester(String userId) {
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ lifecycleStates.add(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<List<Resource>, StorageOperationStatus> resources = resourceOperation.getTesterFollowed(userId, lifecycleStates, false);
+
+ if (resources.isLeft()) {
+ Either<List<Service>, StorageOperationStatus> services = serviceOperation.getTesterFollowed(userId, lifecycleStates, false);
+ if (services.isLeft()) {
+ Map<String, List<? extends Component>> result = new HashMap<String, List<? extends Component>>();
+ result.put("services", services.left().value());
+ result.put("resources", resources.left().value());
+ return Either.left(result);
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(services.right().value())));
+ }
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resources.right().value())));
+ }
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> getFollowedResourcesAndServices(String userId, Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates) {
+ Either<List<Resource>, StorageOperationStatus> resources = resourceOperation.getFollowed(userId, lifecycleStates, lastStateStates, false);
+
+ if (resources.isLeft()) {
+ Either<List<Service>, StorageOperationStatus> services = serviceOperation.getFollowed(userId, lifecycleStates, lastStateStates, false);
+ if (services.isLeft()) {
+ Map<String, List<? extends Component>> result = new HashMap<String, List<? extends Component>>();
+ result.put("services", services.left().value());
+ result.put("resources", resources.left().value());
+ return Either.left(result);
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(services.right().value())));
+ }
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resources.right().value())));
+ }
+ }
+
+ private Either<Map<String, List<? extends Component>>, ResponseFormat> getFollowedProducts(String userId, Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates) {
+ Either<List<Product>, StorageOperationStatus> products = productOperation.getFollowed(userId, lifecycleStates, lastStateStates, false);
+ if (products.isLeft()) {
+ Map<String, List<? extends Component>> result = new HashMap<String, List<? extends Component>>();
+ result.put("products", products.left().value());
+ return Either.left(result);
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(products.right().value())));
+ }
+ }
+
+ /*
+ * New categories flow - start
+ */
+ public Either<List<CategoryDefinition>, ActionStatus> getAllResourceCategories() {
+ return elementOperation.getAllResourceCategories();
+ }
+
+ public Either<List<CategoryDefinition>, ActionStatus> getAllServiceCategories() {
+ return elementOperation.getAllServiceCategories();
+ }
+
+ public Either<CategoryDefinition, ResponseFormat> createCategory(CategoryDefinition category, String componentTypeParamName, String userId) {
+
+ AuditingActionEnum auditingAction = AuditingActionEnum.ADD_CATEGORY;
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentTypeParamName);
+ String componentType = (componentTypeEnum == null ? componentTypeParamName : componentTypeEnum.getValue());
+ CategoryTypeEnum categoryType = CategoryTypeEnum.CATEGORY;
+
+ User user = new User();
+ Either<User, ResponseFormat> validateUser = validateUser(userId);
+ if (validateUser.isRight()) {
+ log.debug("Validation of user failed, userId {}", userId);
+ ResponseFormat responseFormat = validateUser.right().value();
+ user = new User();
+ user.setUserId(userId);
+ String currCategoryName = (category == null ? null : category.getName());
+ handleCategoryAuditing(responseFormat, user, currCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ user = validateUser.left().value();
+
+ if (category == null) {
+ log.debug("Category json is invalid");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ handleCategoryAuditing(responseFormat, user, null, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ String categoryName = category.getName();
+ // For auditing of failures we need the original non-normalized name
+ String origCategoryName = categoryName;
+ if (componentTypeEnum == null) {
+ log.debug("Component type {} is invalid", componentTypeParamName);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateUserRole = validateUserRole(user, componentTypeEnum);
+ if (validateUserRole.isRight()) {
+ log.debug("Validation of user role failed, userId {}", userId);
+ ResponseFormat responseFormat = validateUserRole.right().value();
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ if (!ValidationUtils.validateCategoryDisplayNameFormat(categoryName)) {
+ log.debug("Category display name format is invalid, name {}, componentType {}", categoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, componentType, categoryType.getValue());
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ categoryName = ValidationUtils.normalizeCategoryName4Display(categoryName);
+
+ if (!ValidationUtils.validateCategoryDisplayNameLength(categoryName)) {
+ log.debug("Category display name length is invalid, should be from 4 to 25 chars, name {}, componentType {}", categoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, componentType, categoryType.getValue());
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ category.setName(categoryName);
+
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(categoryName);
+ category.setNormalizedName(normalizedName);
+
+ NodeTypeEnum nodeType = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, categoryType);
+
+ Either<Boolean, ActionStatus> categoryUniqueEither = elementOperation.isCategoryUniqueForType(nodeType, normalizedName);
+ if (categoryUniqueEither.isRight()) {
+ log.debug("Failed to check category uniqueness, name {}, componentType {}", categoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(categoryUniqueEither.right().value());
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Boolean isCategoryUnique = categoryUniqueEither.left().value();
+ if (!isCategoryUnique) {
+ log.debug("Category is not unique, name {}, componentType {}", categoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_CATEGORY_ALREADY_EXISTS, componentType, categoryName);
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Either<CategoryDefinition, ActionStatus> createCategoryByType = elementOperation.createCategory(category, nodeType);
+ if (createCategoryByType.isRight()) {
+ log.debug("Failed to create category, name {}, componentType {}", categoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_CATEGORY_ALREADY_EXISTS, componentType, categoryName);
+ handleCategoryAuditing(responseFormat, user, origCategoryName, auditingAction, componentType);
+ return Either.right(componentsUtils.getResponseFormat(createCategoryByType.right().value()));
+ }
+ category = createCategoryByType.left().value();
+ log.debug("Created category for component {}, name {}, uniqueId {}", componentType, categoryName, category.getUniqueId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ handleCategoryAuditing(responseFormat, user, category.getName(), auditingAction, componentType);
+ return Either.left(category);
+ }
+
+ public Either<SubCategoryDefinition, ResponseFormat> createSubCategory(SubCategoryDefinition subCategory, String componentTypeParamName, String parentCategoryId, String userId) {
+
+ AuditingActionEnum auditingAction = AuditingActionEnum.ADD_SUB_CATEGORY;
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentTypeParamName);
+ String componentType = (componentTypeEnum == null ? componentTypeParamName : componentTypeEnum.getValue());
+ CategoryTypeEnum categoryType = CategoryTypeEnum.SUBCATEGORY;
+ // For auditing
+ String parentCategoryName = parentCategoryId;
+
+ if (subCategory == null) {
+ log.debug("Sub-category json is invalid");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ handleCategoryAuditing(responseFormat, null, parentCategoryName, null, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ String subCategoryName = subCategory.getName();
+ // For auditing of failures we need the original non-normalized name
+ String origSubCategoryName = subCategoryName;
+
+ User user = new User();
+ /*
+ * if (userId == null) { user.setUserId("UNKNOWN"); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION); handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName,
+ * auditingAction, componentType); return Either.right(responseFormat); }
+ */
+ Either<User, ResponseFormat> validateUser = validateUserExists(userId, "createSubCategory", false);
+ if (validateUser.isRight()) {
+ log.debug("Validation of user failed, userId {}", userId);
+ ResponseFormat responseFormat = validateUser.right().value();
+ user = new User();
+ user.setUserId(userId);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ user = validateUser.left().value();
+
+ if (componentTypeEnum == null) {
+ log.debug("Component type {} is invalid", componentTypeParamName);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateComponentType = validateComponentTypeForCategory(componentTypeEnum, categoryType);
+ if (validateComponentType.isRight()) {
+ log.debug("Validation of component type for sub-category failed");
+ ResponseFormat responseFormat = validateComponentType.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateUserRole = validateUserRole(user, componentTypeEnum);
+ if (validateUserRole.isRight()) {
+ log.debug("Validation of user role failed, userId {}", userId);
+ ResponseFormat responseFormat = validateUserRole.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ NodeTypeEnum parentNodeType = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.CATEGORY);
+ NodeTypeEnum childNodeType = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.SUBCATEGORY);
+
+ CategoryDefinition categoryDefinition;
+ Either<CategoryDefinition, ResponseFormat> validateCategoryExists = validateCategoryExists(parentNodeType, parentCategoryId, componentTypeEnum);
+ if (validateCategoryExists.isRight()) {
+ log.debug("Validation of parent category exists failed, parent categoryId {}", parentCategoryId);
+ ResponseFormat responseFormat = validateCategoryExists.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ categoryDefinition = validateCategoryExists.left().value();
+ parentCategoryName = categoryDefinition.getName();
+
+ if (!ValidationUtils.validateCategoryDisplayNameFormat(subCategoryName)) {
+ log.debug("Sub-category display name format is invalid, name {}, componentType {}", subCategoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, componentType, categoryType.getValue());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ subCategoryName = ValidationUtils.normalizeCategoryName4Display(subCategoryName);
+
+ if (!ValidationUtils.validateCategoryDisplayNameLength(subCategoryName)) {
+ log.debug("Sub-category display name length is invalid, should be from 4 to 25 chars, name {}, componentType {}", subCategoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, componentType, categoryType.getValue());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(subCategoryName);
+ subCategory.setNormalizedName(normalizedName);
+
+ // Uniqueness under this category
+ Either<Boolean, ActionStatus> subCategoryUniqueForCategory = elementOperation.isSubCategoryUniqueForCategory(childNodeType, normalizedName, parentCategoryId);
+ if (subCategoryUniqueForCategory.isRight()) {
+ log.debug("Failed to check sub-category uniqueness, parent name {}, subcategory norm name {}, componentType {}", parentCategoryName, normalizedName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(subCategoryUniqueForCategory.right().value());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Boolean isSubUnique = subCategoryUniqueForCategory.left().value();
+ if (!isSubUnique) {
+ log.debug("Sub-category is not unique for category, parent name {}, subcategory norm name {}, componentType {}", parentCategoryName, normalizedName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY, componentType, subCategoryName, parentCategoryName);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ // Setting name of subcategory to fit the similar subcategory name
+ // ignoring cases.
+ // For example if Network-->kUKU exists for service category Network,
+ // and user is trying to create Router-->Kuku for service category
+ // Router,
+ // his subcategory name will be Router-->kUKU.
+ Either<SubCategoryDefinition, ActionStatus> subCategoryUniqueForType = elementOperation.getSubCategoryUniqueForType(childNodeType, normalizedName);
+ if (subCategoryUniqueForType.isRight()) {
+ log.debug("Failed validation of whether similar sub-category exists, normalizedName {} componentType {}", normalizedName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(subCategoryUniqueForType.right().value());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+ SubCategoryDefinition subCategoryDefinition = subCategoryUniqueForType.left().value();
+ if (subCategoryDefinition != null) {
+ subCategoryName = subCategoryDefinition.getName();
+ }
+
+ subCategory.setName(subCategoryName);
+ ///////////////////////////////////////////// Validations end
+
+ Either<SubCategoryDefinition, ActionStatus> createSubCategory = elementOperation.createSubCategory(parentCategoryId, subCategory, childNodeType);
+ if (createSubCategory.isRight()) {
+ log.debug("Failed to create sub-category, name {}, componentType {}", subCategoryName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(subCategoryUniqueForType.right().value());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, origSubCategoryName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ SubCategoryDefinition subCategoryCreated = createSubCategory.left().value();
+ log.debug("Created sub-category for component {}, name {}, uniqueId {}", componentType, subCategoryName, subCategoryCreated.getUniqueId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, subCategoryCreated.getName(), auditingAction, componentType);
+ return Either.left(subCategoryCreated);
+ }
+
+ public Either<GroupingDefinition, ResponseFormat> createGrouping(GroupingDefinition grouping, String componentTypeParamName, String grandParentCategoryId, String parentSubCategoryId, String userId) {
+
+ AuditingActionEnum auditingAction = AuditingActionEnum.ADD_GROUPING;
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentTypeParamName);
+ String componentType = (componentTypeEnum == null ? componentTypeParamName : componentTypeEnum.getValue());
+ CategoryTypeEnum categoryType = CategoryTypeEnum.GROUPING;
+ // For auditing
+ String parentCategoryName = grandParentCategoryId;
+ String parentSubCategoryName = parentSubCategoryId;
+
+ User user;
+ Either<User, ResponseFormat> validateUser = validateUserExists(userId, "create Grouping", false);
+ if (validateUser.isRight()) {
+ log.debug("Validation of user failed, userId {}", userId);
+ ResponseFormat responseFormat = validateUser.right().value();
+ user = new User();
+ user.setUserId(userId);
+ String groupingNameForAudit = (grouping == null ? null : grouping.getName());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, groupingNameForAudit, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ user = validateUser.left().value();
+
+ if (grouping == null) {
+ log.debug("Grouping json is invalid");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, null, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ String groupingName = grouping.getName();
+ // For auditing of failures we need the original non-normalized name
+ String origGroupingName = groupingName;
+
+ if (componentTypeEnum == null) {
+ log.debug("Component type {} is invalid", componentTypeParamName);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateComponentType = validateComponentTypeForCategory(componentTypeEnum, categoryType);
+ if (validateComponentType.isRight()) {
+ log.debug("Validation of component type for grouping failed");
+ ResponseFormat responseFormat = validateComponentType.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateUserRole = validateUserRole(user, componentTypeEnum);
+ if (validateUserRole.isRight()) {
+ log.debug("Validation of user role failed, userId {}", userId);
+ ResponseFormat responseFormat = validateUserRole.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ NodeTypeEnum grandParentNodeType = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.CATEGORY);
+ NodeTypeEnum parentNodeType = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.SUBCATEGORY);
+ NodeTypeEnum childNodeType = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.GROUPING);
+
+ // Validate category
+ CategoryDefinition categoryDefinition;
+ Either<CategoryDefinition, ResponseFormat> validateCategoryExists = validateCategoryExists(grandParentNodeType, grandParentCategoryId, componentTypeEnum);
+ if (validateCategoryExists.isRight()) {
+ log.debug("Validation of parent category exists failed, parent categoryId {}", grandParentCategoryId);
+ ResponseFormat responseFormat = validateCategoryExists.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ categoryDefinition = validateCategoryExists.left().value();
+ parentCategoryName = categoryDefinition.getName();
+
+ // Validate subcategory
+ SubCategoryDefinition subCategoryDefinition;
+ Either<SubCategoryDefinition, ResponseFormat> validateSubCategoryExists = validateSubCategoryExists(parentNodeType, parentSubCategoryId, componentTypeEnum);
+ if (validateSubCategoryExists.isRight()) {
+ log.debug("Validation of parent sub-category exists failed, parent sub-category id {}", parentSubCategoryId);
+ ResponseFormat responseFormat = validateSubCategoryExists.right().value();
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ subCategoryDefinition = validateSubCategoryExists.left().value();
+ parentSubCategoryName = subCategoryDefinition.getName();
+
+ if (!ValidationUtils.validateCategoryDisplayNameFormat(groupingName)) {
+ log.debug("Sub-category display name format is invalid, name {}, componentType {}", groupingName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, componentType, categoryType.getValue());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ groupingName = ValidationUtils.normalizeCategoryName4Display(groupingName);
+
+ if (!ValidationUtils.validateCategoryDisplayNameLength(groupingName)) {
+ log.debug("Grouping display name length is invalid, should be from 4 to 25 chars, name {}, componentType {}", groupingName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, componentType, categoryType.getValue());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ String normalizedName = ValidationUtils.normalizeCategoryName4Uniqueness(groupingName);
+ grouping.setNormalizedName(normalizedName);
+
+ // Uniqueness under this category
+ Either<Boolean, ActionStatus> groupingUniqueForSubCategory = elementOperation.isGroupingUniqueForSubCategory(childNodeType, normalizedName, parentSubCategoryId);
+ if (groupingUniqueForSubCategory.isRight()) {
+ log.debug("Failed to check grouping uniqueness, parent name {}, grouping norm name {}, componentType {}", parentSubCategoryName, normalizedName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(groupingUniqueForSubCategory.right().value());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ Boolean isGroupingUnique = groupingUniqueForSubCategory.left().value();
+ if (!isGroupingUnique) {
+ log.debug("Grouping is not unique for sub-category, parent name {}, grouping norm name {}, componentType {}", parentSubCategoryName, normalizedName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY, componentType, groupingName, parentSubCategoryName);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ // Setting name of grouping to fit the similar grouping name ignoring
+ // cases.
+ // For example if Network-->kUKU exists for service sub-category
+ // Network, and user is trying to create grouping Router-->Kuku for
+ // service sub-category Router,
+ // his grouping name will be Router-->kUKU.
+ Either<GroupingDefinition, ActionStatus> groupingUniqueForType = elementOperation.getGroupingUniqueForType(childNodeType, normalizedName);
+ if (groupingUniqueForType.isRight()) {
+ log.debug("Failed validation of whether similar grouping exists, normalizedName {} componentType {}", normalizedName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(groupingUniqueForType.right().value());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+ GroupingDefinition groupingDefinition = groupingUniqueForType.left().value();
+ if (groupingDefinition != null) {
+ groupingName = groupingDefinition.getName();
+ }
+
+ grouping.setName(groupingName);
+ ///////////////////////////////////////////// Validations end
+
+ Either<GroupingDefinition, ActionStatus> createGrouping = elementOperation.createGrouping(parentSubCategoryId, grouping, childNodeType);
+ if (createGrouping.isRight()) {
+ log.debug("Failed to create grouping, name {}, componentType {}", groupingName, componentType);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(createGrouping.right().value());
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, origGroupingName, auditingAction, componentType);
+ return Either.right(responseFormat);
+ }
+
+ GroupingDefinition groupingCreated = createGrouping.left().value();
+ log.debug("Created grouping for component {}, name {}, uniqueId {}", componentType, groupingName, groupingCreated.getUniqueId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ handleCategoryAuditing(responseFormat, user, parentCategoryName, parentSubCategoryName, groupingCreated.getName(), auditingAction, componentType);
+ return Either.left(groupingCreated);
+ }
+
+ public Either<List<CategoryDefinition>, ResponseFormat> getAllCategories(String componentType, String userId) {
+ AuditingActionEnum auditingAction = AuditingActionEnum.GET_CATEGORY_HIERARCHY;
+ ResponseFormat responseFormat;
+ User user = new User();
+ if (userId == null) {
+ user.setUserId("UNKNOWN");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ componentsUtils.auditGetCategoryHierarchy(auditingAction, user, componentType, responseFormat);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ResponseFormat> validateUser = validateUserExists(userId, "get All Categories", false);
+ if (validateUser.isRight()) {
+ user.setUserId(userId);
+ log.debug("Validation of user failed, userId {}", userId);
+ responseFormat = validateUser.right().value();
+ componentsUtils.auditGetCategoryHierarchy(auditingAction, user, componentType, responseFormat);
+ return Either.right(responseFormat);
+ }
+ user = validateUser.left().value();
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ if (componentTypeEnum == null) {
+ log.debug("Cannot create category for component type {}", componentType);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, "component type");
+ componentsUtils.auditGetCategoryHierarchy(auditingAction, user, componentType, responseFormat);
+ return Either.right(responseFormat);
+ }
+
+ NodeTypeEnum nodeTypeEnum = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.CATEGORY);
+ Either<List<CategoryDefinition>, ActionStatus> getAllCategoriesByType = elementOperation.getAllCategories(nodeTypeEnum, false);
+ if (getAllCategoriesByType.isRight()) {
+ responseFormat = componentsUtils.getResponseFormat(getAllCategoriesByType.right().value());
+ componentsUtils.auditGetCategoryHierarchy(auditingAction, user, componentType, responseFormat);
+ return Either.right(responseFormat);
+ }
+ List<CategoryDefinition> categories = getAllCategoriesByType.left().value();
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ componentsUtils.auditGetCategoryHierarchy(auditingAction, user, componentType, responseFormat);
+ return Either.left(categories);
+ }
+
+ public Either<CategoryDefinition, ResponseFormat> deleteCategory(String categoryId, String componentTypeParamName, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Category", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentTypeParamName);
+ if (componentTypeEnum == null) {
+ log.debug("Cannot create category for component type {}", componentTypeParamName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ NodeTypeEnum nodeTypeEnum = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.CATEGORY);
+
+ Either<CategoryDefinition, ActionStatus> deleteCategoryByType = elementOperation.deleteCategory(nodeTypeEnum, categoryId);
+ if (deleteCategoryByType.isRight()) {
+ // auditing, logging here...
+ return Either.right(componentsUtils.getResponseFormat(deleteCategoryByType.right().value()));
+ }
+ CategoryDefinition category = deleteCategoryByType.left().value();
+ log.debug("Delete category for component {}, name {}, uniqueId {}", nodeTypeEnum, category.getName(), category.getUniqueId());
+ return Either.left(category);
+ }
+
+ public Either<SubCategoryDefinition, ResponseFormat> deleteSubCategory(String grandParentCategoryId, String parentSubCategoryId, String componentTypeParamName, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Sub Category", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentTypeParamName);
+ if (componentTypeEnum == null) {
+ log.debug("Cannot delete sub-category for component type {}", componentTypeParamName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ NodeTypeEnum nodeTypeEnum = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.SUBCATEGORY);
+
+ Either<SubCategoryDefinition, ActionStatus> deleteSubCategoryByType = elementOperation.deleteSubCategory(nodeTypeEnum, parentSubCategoryId);
+ if (deleteSubCategoryByType.isRight()) {
+ // auditing, logging here...
+ return Either.right(componentsUtils.getResponseFormat(deleteSubCategoryByType.right().value()));
+ }
+ SubCategoryDefinition subCategory = deleteSubCategoryByType.left().value();
+ log.debug("Deleted sub-category for component {}, name {}, uniqueId {}", nodeTypeEnum, subCategory.getName(), subCategory.getUniqueId());
+ return Either.left(subCategory);
+ }
+
+ public Either<GroupingDefinition, ResponseFormat> deleteGrouping(String grandParentCategoryId, String parentSubCategoryId, String groupingId, String componentTypeParamName, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Grouping", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentTypeParamName);
+ if (componentTypeEnum == null) {
+ log.debug("Cannot delete grouping for component type {}", componentTypeParamName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ NodeTypeEnum nodeTypeEnum = NodeTypeConvertUtils.getCategoryNodeTypeByComponentParam(componentTypeEnum, CategoryTypeEnum.GROUPING);
+
+ Either<GroupingDefinition, ActionStatus> deleteGroupingByType = elementOperation.deleteGrouping(nodeTypeEnum, groupingId);
+ if (deleteGroupingByType.isRight()) {
+ // auditing, logging here...
+ return Either.right(componentsUtils.getResponseFormat(deleteGroupingByType.right().value()));
+ }
+ GroupingDefinition deletedGrouping = deleteGroupingByType.left().value();
+ log.debug("Deleted grouping for component {}, name {}, uniqueId {}", nodeTypeEnum, deletedGrouping.getName(), deletedGrouping.getUniqueId());
+ return Either.left(deletedGrouping);
+ }
+
+ private Either<User, ResponseFormat> validateUser(String userId) {
+
+ // validate user exists
+ if (userId == null) {
+ log.debug("User id is null");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION));
+ }
+
+ Either<User, ActionStatus> userResult = userAdminManager.getUser(userId, false);
+ if (userResult.isRight()) {
+ ResponseFormat responseFormat;
+ if (userResult.right().value().equals(ActionStatus.USER_NOT_FOUND)) {
+ log.debug("Not authorized user, userId = {}", userId);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ } else {
+ log.debug("Failed to authorize user, userId = {}", userId);
+ responseFormat = componentsUtils.getResponseFormat(userResult.right().value());
+ }
+
+ return Either.right(responseFormat);
+ }
+ return Either.left(userResult.left().value());
+ // ========================================-
+ }
+
+ private Either<Boolean, ResponseFormat> validateUserRole(User user, ComponentTypeEnum componentTypeEnum) {
+ String role = user.getRole();
+ boolean validAdminAction = (role.equals(Role.ADMIN.name()) && (componentTypeEnum == ComponentTypeEnum.SERVICE || componentTypeEnum == ComponentTypeEnum.RESOURCE));
+ boolean validProductAction = (role.equals(Role.PRODUCT_STRATEGIST.name()) && (componentTypeEnum == ComponentTypeEnum.PRODUCT));
+
+ if (!(validAdminAction || validProductAction)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ log.debug("User not permitted to perform operation on category, userId = {}, role = {}, componentType = {}", user.getUserId(), role, componentTypeEnum);
+ return Either.right(responseFormat);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateComponentTypeForCategory(ComponentTypeEnum componentType, CategoryTypeEnum categoryType) {
+ boolean validResourceAction = (componentType == ComponentTypeEnum.RESOURCE && (categoryType == CategoryTypeEnum.CATEGORY || categoryType == CategoryTypeEnum.SUBCATEGORY));
+ boolean validServiceAction = (componentType == ComponentTypeEnum.SERVICE && categoryType == CategoryTypeEnum.CATEGORY);
+ boolean validProductAction = (componentType == ComponentTypeEnum.PRODUCT); // can
+ // be
+ // any
+ // category
+ // type
+
+ if (!(validResourceAction || validServiceAction || validProductAction)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ log.debug("It's not allowed to create category type {} for component type {}", categoryType, componentType);
+ return Either.right(responseFormat);
+ }
+ return Either.left(true);
+ }
+
+ private Either<CategoryDefinition, ResponseFormat> validateCategoryExists(NodeTypeEnum nodeType, String categoryId, ComponentTypeEnum componentType) {
+ Either<CategoryDefinition, ActionStatus> categoryByTypeAndId = elementOperation.getCategory(nodeType, categoryId);
+ if (categoryByTypeAndId.isRight()) {
+ log.debug("Failed to fetch parent category, parent categoryId {}", categoryId);
+ ActionStatus actionStatus = categoryByTypeAndId.right().value();
+ ResponseFormat responseFormat;
+ if (actionStatus == ActionStatus.COMPONENT_CATEGORY_NOT_FOUND) {
+ responseFormat = componentsUtils.getResponseFormat(actionStatus, componentType.getValue().toLowerCase(), CategoryTypeEnum.CATEGORY.getValue(), "");
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(actionStatus);
+ }
+ return Either.right(responseFormat);
+ }
+ return Either.left(categoryByTypeAndId.left().value());
+ }
+
+ private Either<SubCategoryDefinition, ResponseFormat> validateSubCategoryExists(NodeTypeEnum nodeType, String subCategoryId, ComponentTypeEnum componentType) {
+ Either<SubCategoryDefinition, ActionStatus> subCategoryByTypeAndId = elementOperation.getSubCategory(nodeType, subCategoryId);
+ if (subCategoryByTypeAndId.isRight()) {
+ log.debug("Failed to fetch parent category, parent categoryId {}", subCategoryId);
+ ActionStatus actionStatus = subCategoryByTypeAndId.right().value();
+ ResponseFormat responseFormat;
+ if (actionStatus == ActionStatus.COMPONENT_CATEGORY_NOT_FOUND) {
+ responseFormat = componentsUtils.getResponseFormat(actionStatus, componentType.getValue().toLowerCase(), CategoryTypeEnum.SUBCATEGORY.getValue(), "");
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(actionStatus);
+ }
+ return Either.right(responseFormat);
+ }
+ return Either.left(subCategoryByTypeAndId.left().value());
+ }
+
+ private void handleCategoryAuditing(ResponseFormat responseFormat, User user, String category, AuditingActionEnum auditingAction, String componentType) {
+ componentsUtils.auditCategory(responseFormat, user, category, null, null, auditingAction, componentType);
+ }
+
+ private void handleCategoryAuditing(ResponseFormat responseFormat, User user, String category, String subCategory, AuditingActionEnum auditingAction, String componentType) {
+ componentsUtils.auditCategory(responseFormat, user, category, subCategory, null, auditingAction, componentType);
+ }
+
+ private void handleCategoryAuditing(ResponseFormat responseFormat, User user, String category, String subCategory, String grouping, AuditingActionEnum auditingAction, String componentType) {
+ componentsUtils.auditCategory(responseFormat, user, category, subCategory, grouping, auditingAction, componentType);
+ }
+
+ /*
+ * New categories flow - end
+ */
+
+ public Either<List<Tag>, ActionStatus> getAllTags(String userId) {
+ Either<User, ActionStatus> resp = validateUserExistsActionStatus(userId, "get All Tags");
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ return elementOperation.getAllTags();
+ }
+
+ public Either<List<PropertyScope>, ActionStatus> getAllPropertyScopes(String userId) {
+ Either<User, ActionStatus> resp = validateUserExistsActionStatus(userId, "get All Property Scopes");
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ return elementOperation.getAllPropertyScopes();
+ }
+
+ public Either<List<ArtifactType>, ActionStatus> getAllArtifactTypes(String userId) {
+ Either<User, ActionStatus> resp = validateUserExistsActionStatus(userId, "get All Artifact Types");
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ return elementOperation.getAllArtifactTypes();
+ }
+
+ public Either<Map<String, Object>, ActionStatus> getAllDeploymentArtifactTypes() {
+ return elementOperation.getAllDeploymentArtifactTypes();
+ }
+
+ public Either<Integer, ActionStatus> getDefaultHeatTimeout() {
+ return elementOperation.getDefaultHeatTimeout();
+ }
+
+ public Either<Map<String, List<? extends Component>>, ResponseFormat> getCatalogComponents(String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Catalog Components", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Map<String, List<? extends Component>> resMap = new HashMap<>();
+
+ Either<List<Resource>, StorageOperationStatus> resResources = resourceOperation.getResourceCatalogData(false);
+ if (resResources.isLeft()) {
+ Either<List<Service>, StorageOperationStatus> resServices = serviceOperation.getServiceCatalogData(false);
+ if (resServices.isLeft()) {
+ Either<List<Product>, StorageOperationStatus> resProducts = productOperation.getProductCatalogData(false);
+ if (resProducts.isLeft()) {
+ resMap.put("resources", resResources.left().value());
+ resMap.put("services", resServices.left().value());
+ resMap.put("products", resProducts.left().value());
+ return Either.left(resMap);
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resProducts.right().value())));
+ }
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resServices.right().value())));
+ }
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resResources.right().value())));
+ }
+ }
+
+ public Either<List<? extends Component>, ResponseFormat> getFilteredCatalogComponents(String assetType, Map<FilterKeyEnum, String> filters, String query) {
+ ComponentTypeEnum assetTypeEnum = AssetTypeEnum.convertToComponentTypeEnum(assetType);
+
+ if (query != null) {
+ Optional<NameValuePair> invalidFilter = findInvalidFilter(query, assetTypeEnum);
+ if (invalidFilter.isPresent()) {
+ log.debug("getFilteredAssetList: invalid filter key");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_FILTER_KEY, invalidFilter.get().getName(), FilterKeyEnum.getValidFiltersByAssetType(assetTypeEnum).toString()));
+ }
+ }
+
+ if (filters == null || filters.isEmpty()) {
+ return getCatalogComponentsByAssetType(assetTypeEnum);
+ }
+
+ ComponentOperation componentOperation = getComponentOperation(assetTypeEnum);
+ Either<List<Component>, StorageOperationStatus> result = componentOperation.getFilteredComponents(filters, false);
+
+ if (result.isRight()) {// category hierarchy mismatch or
+ // category/subCategory/distributionStatus not
+ // found
+ List<String> params = getErrorResponseParams(filters, assetTypeEnum);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(result.right().value()), params.get(0), params.get(1), params.get(2)));
+ }
+ if (result.left().value().isEmpty()) {// no assets found for requested
+ // criteria
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.NO_ASSETS_FOUND, assetType, query));
+ }
+ return Either.left(result.left().value());
+ }
+
+ public Either<List<? extends Component>, ResponseFormat> getCatalogComponentsByAssetType(ComponentTypeEnum assetTypeEnum) {
+
+ if (assetTypeEnum == null) {
+ log.debug("getCatalogComponentsByAssetType: Corresponding ComponentTypeEnum not found");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ switch (assetTypeEnum) {
+ case RESOURCE:
+
+ Either<List<Resource>, StorageOperationStatus> resourceCatalogData = resourceOperation.getResourceCatalogDataVFLatestCertifiedAndNonCertified(false);
+ if (resourceCatalogData.isLeft()) {
+ log.debug("getCatalogComponentsByAssetType: Resource fetching successful");
+ return Either.left(resourceCatalogData.left().value());
+ } else {
+ log.debug("getCatalogComponentsByAssetType: Resource fetching failed");
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resourceCatalogData.right().value())));
+ }
+
+ case SERVICE:
+ Either<List<Service>, StorageOperationStatus> serviceCatalogData = serviceOperation.getServiceCatalogDataLatestCertifiedAndNotCertified(false);
+ if (serviceCatalogData.isLeft()) {
+ log.debug("getCatalogComponentsByAssetType: Service fetching successful");
+ return Either.left(serviceCatalogData.left().value());
+ } else {
+ log.debug("getCatalogComponentsByAssetType: Service fetching failed");
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(serviceCatalogData.right().value())));
+ }
+ /*
+ * case PRODUCT: Either<List<Product>, StorageOperationStatus> resCatalogData = productOperation.getProductCatalogData(false); if(resCatalogData.isLeft()){ log. debug("getCatalogComponentsByAssetType: Product fetching successful" );
+ * return Either.left(resCatalogData.left().value()); }else { log. debug("getCatalogComponentsByAssetType: Product fetching failed" ); return Either.right(componentsUtils .getResponseFormat(componentsUtils.convertFromStorageResponse(
+ * resCatalogData.right().value()))); }
+ */
+ default:
+ log.debug("Invalid Asset Type");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ }
+
+ // TODO new story Tal
+ public Either<List<? extends Component>, ResponseFormat> getCatalogComponentsByUuidAndAssetType(String assetType, String uuid) {
+
+ if (assetType == null || assetType == null) {
+ log.debug("getCatalogComponentsByUuidAndAssetType: One of the function parameteres is null");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ ComponentTypeEnum assetTypeEnum = AssetTypeEnum.convertToComponentTypeEnum(assetType);
+
+ if (assetTypeEnum == null) {
+ log.debug("getCatalogComponentsByUuidAndAssetType: Corresponding ComponentTypeEnum not found");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ switch (assetTypeEnum) {
+
+ case RESOURCE:
+ Either<List<Resource>, StorageOperationStatus> resourceListByUuid = resourceOperation.getLatestResourceByUuid(uuid, false);
+
+ if (resourceListByUuid.isLeft()) {
+ log.debug("getCatalogComponentsByUuidAndAssetType: Resource fetching successful");
+ return Either.left(resourceListByUuid.left().value());
+ }
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(resourceListByUuid.right().value());
+ log.debug("getCatalogComponentsByUuidAndAssetType: Resource fetching failed");
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+
+ case SERVICE:
+ Either<List<Service>, StorageOperationStatus> serviceCatalogData = serviceOperation.getLatestServiceByUuid(uuid, false);
+
+ if (serviceCatalogData.isLeft()) {
+ log.debug("getCatalogComponentsByUuidAndAssetType: Service fetching successful");
+ return Either.left(serviceCatalogData.left().value());
+ }
+
+ log.debug("getCatalogComponentsByUuidAndAssetType: Service fetching failed");
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(serviceCatalogData.right().value())));
+ // case Product is for future US
+ /*
+ * case PRODUCT: Either<List<Product>, StorageOperationStatus> resCatalogData = productOperation.getProductCatalogData(false); if(resCatalogData.isLeft()){ log. debug("getCatalogComponentsByAssetType: Product fetching successful" ); return
+ * Either.left(resCatalogData.left().value()); }else { log. debug("getCatalogComponentsByAssetType: Product fetching failed" ); return Either.right(componentsUtils .getResponseFormat(componentsUtils.convertFromStorageResponse(
+ * resCatalogData.right().value()))); }
+ */
+ default:
+ log.debug("Invalid Asset Type");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ }
+
+ public List<String> getAllComponentTypesParamNames() {
+ List<String> paramNames = new ArrayList<>();
+ paramNames.add(ComponentTypeEnum.SERVICE_PARAM_NAME);
+ paramNames.add(ComponentTypeEnum.RESOURCE_PARAM_NAME);
+ paramNames.add(ComponentTypeEnum.PRODUCT_PARAM_NAME);
+ return paramNames;
+ }
+
+ public List<String> getAllSupportedRoles() {
+ Role[] values = Role.values();
+ List<String> roleNames = new ArrayList<>();
+ for (Role role : values) {
+ roleNames.add(role.name());
+ }
+ return roleNames;
+ }
+
+ public Either<Map<String, String>, ActionStatus> getResourceTypesMap() {
+ return elementOperation.getResourceTypesMap();
+ }
+
+ private Optional<NameValuePair> findInvalidFilter(String query, ComponentTypeEnum assetType) {
+ List<NameValuePair> params = URLEncodedUtils.parse(query, StandardCharsets.UTF_8);
+ List<String> validKeys = FilterKeyEnum.getValidFiltersByAssetType(assetType);
+ Predicate<NameValuePair> noMatch = p -> !validKeys.contains(p.getName());
+ return params.stream().filter(noMatch).findAny();
+ }
+
+ private List<String> getErrorResponseParams(Map<FilterKeyEnum, String> filters, ComponentTypeEnum assetType) {
+ List<String> params = new ArrayList<String>();
+ if (1 == filters.size()) {
+ params.add(assetType.getValue().toLowerCase());
+ params.add(filters.keySet().iterator().next().getName());
+ params.add(filters.values().iterator().next());
+ } else {
+ params.add(assetType.getValue());
+ params.add(filters.get(FilterKeyEnum.SUB_CATEGORY));
+ params.add(filters.get(FilterKeyEnum.CATEGORY));
+ }
+ return params;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupBusinessLogic.java
new file mode 100644
index 0000000000..3b4528d3a3
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupBusinessLogic.java
@@ -0,0 +1,1512 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.io.FilenameUtils;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.info.ArtifactDefinitionInfo;
+import org.openecomp.sdc.be.info.ArtifactTemplateInfo;
+import org.openecomp.sdc.be.info.GroupDefinitionInfo;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.GroupOperation;
+import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("groupBusinessLogic")
+public class GroupBusinessLogic extends BaseBusinessLogic {
+
+ public static String INITIAL_VERSION = "1";
+
+ private static final String CREATE_GROUP = "CreateGroup";
+
+ private static final String UPDATE_GROUP = "UpdateGroup";
+
+ private static final String GET_GROUP = "GetGroup";
+
+ private static Logger log = LoggerFactory.getLogger(GroupBusinessLogic.class.getName());
+
+ public GroupBusinessLogic() {
+
+ }
+
+ @javax.annotation.Resource
+ private GroupTypeOperation groupTypeOperation;
+
+ @javax.annotation.Resource
+ private GroupOperation groupOperation;
+
+ /**
+ *
+ * 1. validate user exist
+ *
+ * 2. validate component can be edited
+ *
+ * 3. verify group not already exist
+ *
+ * 4. verify type of group exist
+ *
+ * 5. verify Component instances exist under the component
+ *
+ * 6. verify the component instances type are allowed according to the member types in the group type
+ *
+ * 7. verify the artifacts belongs to the component
+ *
+ * @param componentId
+ * @param userId
+ * @param componentType
+ * @param groupDefinition
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinition, ResponseFormat> createGroup(String componentId, String userId, ComponentTypeEnum componentType, GroupDefinition groupDefinition, boolean inTransaction) {
+
+ Either<GroupDefinition, ResponseFormat> result = null;
+
+ try {
+ Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, CREATE_GROUP, inTransaction);
+
+ if (validateUserExists.isRight()) {
+ result = Either.right(validateUserExists.right().value());
+ return result;
+ }
+
+ User user = validateUserExists.left().value();
+ // 5. check service/resource existence
+ // 6. check service/resource check out
+ // 7. user is owner of checkout state
+ org.openecomp.sdc.be.model.Component component = null;
+
+ // String realComponentId = componentType ==
+ // ComponentTypeEnum.RESOURCE_INSTANCE ? parentId : componentId;
+ String realComponentId = componentId;
+
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreGroups(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ componentParametersView.setIgnoreUsers(false);
+ componentParametersView.setIgnoreComponentInstances(false);
+
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(realComponentId, componentType, componentParametersView, userId, null, user);
+
+ if (validateComponent.isRight()) {
+ result = Either.right(validateComponent.right().value());
+ return result;
+ }
+ component = validateComponent.left().value();
+ Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, userId);
+ if (canWork.isRight()) {
+ result = Either.right(canWork.right().value());
+ return result;
+ }
+
+ result = this.createGroup(component, user, componentType, groupDefinition, inTransaction);
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ }
+ }
+
+ private String getComponentTypeForResponse(org.openecomp.sdc.be.model.Component component) {
+ String componentTypeForResponse = "SERVICE";
+ if (component instanceof Resource) {
+ componentTypeForResponse = ((Resource) component).getResourceType().name();
+ }
+ return componentTypeForResponse;
+ }
+
+ /**
+ * Verify that the artifact members belongs to the component
+ *
+ * @param component
+ * @param artifacts
+ * @return
+ */
+ private Either<Boolean, ResponseFormat> verifyArtifactsBelongsToComponent(Component component, List<String> artifacts, String context) {
+
+ if (artifacts == null || true == artifacts.isEmpty()) {
+ return Either.left(true);
+ }
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = component.getDeploymentArtifacts();
+ if (deploymentArtifacts == null || true == deploymentArtifacts.isEmpty()) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(context, "No deployment artifact found under component " + component.getNormalizedName(), ErrorSeverity.INFO);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ List<String> currentArtifacts = deploymentArtifacts.values().stream().map(p -> p.getUniqueId()).collect(Collectors.toList());
+ log.debug("The deployment artifacts of component {} are {}", component.getNormalizedName(), deploymentArtifacts);
+ if (false == currentArtifacts.containsAll(artifacts)) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(context, "Not all artifacts belongs to component " + component.getNormalizedName(), ErrorSeverity.INFO);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ return Either.left(true);
+
+ }
+
+ /**
+ * Verify that the artifact members belongs to the component
+ *
+ * @param component
+ * @param artifacts
+ * @return
+ */
+ private Either<List<ArtifactDefinition>, ResponseFormat> getArtifactsBelongsToComponent(Component component, List<String> artifacts, String context) {
+
+ /*
+ * if (artifacts == null || true == artifacts.isEmpty()) { return Either.left(true); }
+ */
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = component.getDeploymentArtifacts();
+ if (deploymentArtifacts == null || true == deploymentArtifacts.isEmpty()) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(context, "No deployment artifact found under component " + component.getNormalizedName(), ErrorSeverity.INFO);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ List<ArtifactDefinition> resultList = new ArrayList();
+
+ for (String artifactId : artifacts) {
+ Optional<ArtifactDefinition> groupArtifactOp = deploymentArtifacts.values().stream().filter(p -> p.getUniqueId().equals(artifactId)).findAny();
+
+ if (groupArtifactOp.isPresent()) {
+ ArtifactDefinition groupArtifact = groupArtifactOp.get();
+ resultList.add(groupArtifact);
+ } else {
+ BeEcompErrorManager.getInstance().logInvalidInputError(context, "Not all artifacts belongs to component " + component.getNormalizedName(), ErrorSeverity.INFO);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+
+ }
+ }
+
+ return Either.left(resultList);
+
+ }
+
+ /**
+ * verify that the members are component instances of the component
+ *
+ * @param component
+ * @param componentType
+ * @param groupMembers
+ * @param memberToscaTypes
+ * @return
+ */
+ private Either<Boolean, ResponseFormat> verifyComponentInstancesAreValidMembers(Component component, ComponentTypeEnum componentType, String groupName, String groupType, Map<String, String> groupMembers, List<String> memberToscaTypes) {
+
+ if (groupMembers == null || true == groupMembers.isEmpty()) {
+ return Either.left(true);
+ }
+
+ if (memberToscaTypes == null || true == memberToscaTypes.isEmpty()) {
+ return Either.left(true);
+ }
+
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null && false == componentInstances.isEmpty()) {
+ Map<String, ComponentInstance> compInstUidToCompInstMap = componentInstances.stream().collect(Collectors.toMap(p -> p.getUniqueId(), p -> p));
+
+ Set<String> allCompInstances = compInstUidToCompInstMap.keySet();
+
+ for (Entry<String, String> groupMember : groupMembers.entrySet()) {
+ String compName = groupMember.getKey();
+ String compUid = groupMember.getValue();
+
+ if (false == allCompInstances.contains(compUid)) {
+ /*
+ * %1 - member name %2 - group name %3 - VF name %4 - component type [VF ]
+ */
+ String componentTypeForResponse = getComponentTypeForResponse(component);
+
+ BeEcompErrorManager.getInstance().logInvalidInputError(CREATE_GROUP, "Not all group members exists under the component", ErrorSeverity.INFO);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_INVALID_COMPONENT_INSTANCE, compName, groupName, component.getNormalizedName(), componentTypeForResponse));
+ }
+ }
+
+ ComponentOperation componentOperation = getComponentOperationByParentComponentType(componentType);
+ if (componentOperation instanceof ResourceOperation) {
+ ResourceOperation resourceOperation = (ResourceOperation) componentOperation;
+
+ for (Entry<String, String> groupMember : groupMembers.entrySet()) {
+
+ String componentInstName = groupMember.getKey();
+ String componentInstUid = groupMember.getValue();
+
+ ComponentInstance componentInstance = compInstUidToCompInstMap.get(componentInstUid);
+ String componentUid = componentInstance.getComponentUid();
+ List<String> componentToscaNames = new ArrayList<>();
+ TitanOperationStatus status = resourceOperation.fillResourceDerivedListFromGraph(componentUid, componentToscaNames);
+ if (status != TitanOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError(CREATE_GROUP, "Cannot find tosca list of component id " + componentUid, ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ log.debug("The tosca names of component id {} are {}", componentUid, memberToscaTypes);
+
+ boolean found = false;
+ for (String memberToscaType : memberToscaTypes) {
+ if (componentToscaNames.contains(memberToscaType)) {
+ found = true;
+ break;
+ }
+ }
+ if (found == false) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(CREATE_GROUP,
+ "No tosca types from " + memberToscaTypes + " can be found in the tosca list " + componentToscaNames + " of component " + componentInstance.getNormalizedName(), ErrorSeverity.INFO);
+ /*
+ * # %1 - member name # %2 - group name # %3 - group type
+ */
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE, componentInstName, groupName, groupType));
+ } else {
+ log.debug("Component instance {} fits to one of the required tosca types", componentInstance.getNormalizedName());
+ }
+ }
+ } else {
+ BeEcompErrorManager.getInstance().logInvalidInputError(CREATE_GROUP, "Cannot find tosca list since it is not supported for product", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ return Either.left(true);
+ }
+
+ public ComponentOperation getComponentOperation(NodeTypeEnum componentType) {
+
+ switch (componentType) {
+ case Service:
+ case ResourceInstance:
+ return serviceOperation;
+ case Resource:
+ return resourceOperation;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Update specific group version
+ *
+ * @param groupDefinition
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinition, StorageOperationStatus> updateGroupVersion(GroupDefinition groupDefinition, boolean inTransaction) {
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+ List<String> groupIdsToUpdateVersion = new ArrayList<>();
+ groupIdsToUpdateVersion.add(groupDefinition.getUniqueId());
+ Either<List<GroupDefinition>, StorageOperationStatus> updateGroupVersion = updateGroupVersion(groupIdsToUpdateVersion, inTransaction);
+ if (updateGroupVersion.isLeft()) {
+ result = Either.left(updateGroupVersion.left().value().get(0));
+ } else {
+ log.debug("Failed to update group version. Status is {} ", updateGroupVersion.right().value());
+ result = Either.right(updateGroupVersion.right().value());
+ }
+ return result;
+ }
+
+ /**
+ * Update list of groups versions
+ *
+ * @param groupsUniqueId
+ * @param inTransaction
+ * @return
+ */
+ public Either<List<GroupDefinition>, StorageOperationStatus> updateGroupVersion(List<String> groupsUniqueId, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, StorageOperationStatus> result = null;
+
+ try {
+
+ result = groupOperation.updateGroupVersion(groupsUniqueId, true);
+
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Update GroupDefinition metadata
+ *
+ * @param componentId
+ * @param user
+ * @param groupId
+ * @param componentType
+ * @param groupUpdate
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinition, ResponseFormat> updateGroupMetadata(String componentId, User user, String groupUniqueId, ComponentTypeEnum componentType, GroupDefinition groupUpdate, boolean inTransaction) {
+
+ Either<GroupDefinition, ResponseFormat> result = null;
+
+ // Validate user and validate group belongs to component
+ List<GroupDefinition> groups = new ArrayList<>();
+ groups.add(groupUpdate);
+ Either<Component, ResponseFormat> validateGroupsBeforeUpdate = validateGroupsBeforeUpdate(componentId, user.getUserId(), componentType, groups, inTransaction);
+ if (validateGroupsBeforeUpdate.isRight()) {
+ result = Either.right(validateGroupsBeforeUpdate.right().value());
+ return result;
+ }
+ Component component = validateGroupsBeforeUpdate.left().value();
+
+ // Get the GroupDefinition object
+ Either<GroupDefinition, StorageOperationStatus> groupStatus = groupOperation.getGroup(groupUniqueId);
+ if (groupStatus.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(groupStatus.right().value(), ComponentTypeEnum.SERVICE), ""));
+ }
+ GroupDefinition currentGroup = groupStatus.left().value();
+
+ // Validate group type is vfModule
+ if (!currentGroup.getType().equals(Constants.DEFAULT_GROUP_VF_MODULE)) {
+ log.error("Group update metadata: Group type is different then: {}", Constants.DEFAULT_GROUP_VF_MODULE);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_TYPE, groupUpdate.getType());
+ return Either.right(responseFormat);
+ }
+
+ Either<GroupDefinition, ResponseFormat> validationRsponse = validateAndUpdateGroupMetadata(currentGroup, groupUpdate);
+ if (validationRsponse.isRight()) {
+ log.info("Group update metadata: validations field.");
+ return validationRsponse;
+ }
+ GroupDefinition groupToUpdate = validationRsponse.left().value();
+
+ // lock resource
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(componentId, component, "Update GroupDefinition Metadata");
+ if (lockResult.isRight()) {
+ return Either.right(lockResult.right().value());
+ }
+ try {
+ Either<GroupDefinition, StorageOperationStatus> updateResponse = groupOperation.updateGroupName(groupUniqueId, groupUpdate.getName(), inTransaction);
+ if (updateResponse.isRight()) {
+ titanGenericDao.rollback();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Update GroupDefinition Metadata");
+ BeEcompErrorManager.getInstance().logBeSystemError("Update GroupDefinition Metadata");
+ log.debug("failed to update sevice {}", groupToUpdate.getUniqueId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ titanGenericDao.commit();
+ return Either.left(updateResponse.left().value());
+ } finally {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+ }
+
+ /**
+ * Validate and update GroupDefinition metadata
+ *
+ * @param user
+ * @param currentGroup
+ * @param groupUpdate
+ * @return
+ */
+ private Either<GroupDefinition, ResponseFormat> validateAndUpdateGroupMetadata(GroupDefinition currentGroup, GroupDefinition groupUpdate) {
+ // Check if to update, and update GroupDefinition name.
+ Either<Boolean, ResponseFormat> response = validateAndUpdateGroupName(currentGroup, groupUpdate);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ // Do not allow to update GroupDefinition version directly.
+ String versionUpdated = groupUpdate.getVersion();
+ String versionCurrent = currentGroup.getVersion();
+ if (versionUpdated != null && !versionCurrent.equals(versionUpdated)) {
+ log.info("update Group: recived request to update version to {} the field is not updatable ignoring.", versionUpdated);
+ }
+
+ return Either.left(currentGroup);
+ }
+
+ /**
+ * Validate and update GroupDefinition name
+ *
+ * @param user
+ * @param currentGroup
+ * @param groupUpdate
+ * @return
+ */
+ private Either<Boolean, ResponseFormat> validateAndUpdateGroupName(GroupDefinition currentGroup, GroupDefinition groupUpdate) {
+ String nameUpdated = groupUpdate.getName();
+ String nameCurrent = currentGroup.getName();
+ if (!nameCurrent.equals(nameUpdated)) {
+ Either<Boolean, ResponseFormat> validatNameResponse = validateGroupName(currentGroup.getName(), groupUpdate.getName());
+ if (validatNameResponse.isRight()) {
+ ResponseFormat errorRespons = validatNameResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentGroup.setName(groupUpdate.getName());
+ }
+ return Either.left(true);
+ }
+
+ /**
+ * Validate that group name to update is valid (same as current group name except for middle part). For example: Current group name: MyResource..MyDesc..Module-1 Group to update: MyResource..MyDesc2..Module-1 Verify that only the second part
+ * MyDesc was changed.
+ *
+ * @param currentGroupName
+ * @param groupUpdateName
+ * @return
+ */
+ private Either<Boolean, ResponseFormat> validateGroupName(String currentGroupName, String groupUpdateName) {
+ try {
+ // Check if the group name is in old format.
+ if (Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(groupUpdateName).matches()) {
+ log.error("Group name {} is in old format", groupUpdateName);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME, groupUpdateName));
+ }
+
+ // Check that name pats 1 and 3 did not changed (only the second
+ // part can be changed)
+ // But verify before that the current group format is the new one
+ if (!Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(currentGroupName).matches()) {
+ String[] split1 = currentGroupName.split("\\.\\.");
+ String currentResourceName = split1[0];
+ String currentCounter = split1[2];
+
+ String[] split2 = groupUpdateName.split("\\.\\.");
+ String groupUpdateResourceName = split2[0];
+ String groupUpdateCounter = split2[2];
+
+ if (!currentResourceName.equals(groupUpdateResourceName)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME_MODIFICATION, currentResourceName));
+ }
+
+ if (!currentCounter.equals(groupUpdateCounter)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME_MODIFICATION, currentCounter));
+ }
+ }
+
+ return Either.left(true);
+ } catch (Exception e) {
+ log.error("Error valiadting group name", e);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ }
+
+ /**
+ * associate artifacts to a given group
+ *
+ * @param componentId
+ * @param userId
+ * @param componentType
+ * @param groups
+ * @param shouldLockComp
+ * @param inTransaction
+ * @return
+ */
+ public Either<List<GroupDefinition>, ResponseFormat> associateArtifactsToGroup(String componentId, String userId, ComponentTypeEnum componentType, List<GroupDefinition> groups, boolean shouldLockComp, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, ResponseFormat> result = null;
+
+ if (shouldLockComp == true && inTransaction == true) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("dissociateArtifactsFromGroup", "Cannot lock component since we are inside a transaction", ErrorSeverity.ERROR);
+ // Cannot lock component since we are in a middle of another
+ // transaction.
+ ActionStatus actionStatus = ActionStatus.INVALID_CONTENT;
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ return result;
+ }
+
+ Component component = null;
+ try {
+
+ if (groups == null || groups.isEmpty()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.OK));
+ }
+
+ Either<Component, ResponseFormat> validateGroupsBeforeUpdate = validateGroupsBeforeUpdate(componentId, userId, componentType, groups, inTransaction);
+ if (validateGroupsBeforeUpdate.isRight()) {
+ result = Either.right(validateGroupsBeforeUpdate.right().value());
+ return result;
+ }
+
+ component = validateGroupsBeforeUpdate.left().value();
+
+ if (shouldLockComp) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, "Group - Associate Artifacts");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ List<GroupDefinition> updatedGroups = new ArrayList<>();
+
+ List<GroupDefinition> componentGroups = component.getGroups();
+
+ // per group, associate to it the artifacts
+ for (GroupDefinition groupDefinition : groups) {
+
+ GroupDefinition componentGroup = componentGroups.stream().filter(p -> p.getUniqueId().equals(groupDefinition.getUniqueId())).findFirst().orElse(null);
+ if (componentGroup != null) {
+ List<String> componentArtifacts = componentGroup.getArtifacts();
+ int artifactsSizeInGroup = componentArtifacts == null ? 0 : componentArtifacts.size();
+ if (artifactsSizeInGroup > 0) {
+ List<String> artifactsToAssociate = groupDefinition.getArtifacts();
+
+ // if no artifcats sent
+ if (artifactsToAssociate == null || true == artifactsToAssociate.isEmpty()) {
+ continue;
+ }
+
+ boolean isChanged = componentArtifacts.removeAll(artifactsToAssociate);
+ if (isChanged) {// I.e. At least one artifact is already
+ // associated to the group
+ log.debug("Some of the artifacts already associated to group {}", groupDefinition.getUniqueId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_ARTIFACT_ALREADY_ASSOCIATED, componentGroup.getName()));
+ }
+ }
+ }
+
+ Either<GroupDefinition, StorageOperationStatus> associateArtifactsToGroup = groupOperation.associateArtifactsToGroup(groupDefinition.getUniqueId(), groupDefinition.getArtifacts(), true);
+
+ if (associateArtifactsToGroup.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(associateArtifactsToGroup.right().value());
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ log.debug("Failed to update group {} under component {}, error: {}", groupDefinition.getName(), component.getNormalizedName(), actionStatus.name());
+ return result;
+ }
+ updatedGroups.add(associateArtifactsToGroup.left().value());
+
+ }
+
+ result = Either.left(updatedGroups);
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ // unlock resource
+ if (shouldLockComp && component != null) {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+
+ }
+ }
+
+ public Either<List<GroupDefinition>, ResponseFormat> associateMembersToGroup(String componentId, String userId, ComponentTypeEnum componentType, List<GroupDefinition> groups, boolean shouldLockComp, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, ResponseFormat> result = null;
+
+ if (shouldLockComp == true && inTransaction == true) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("dissociateArtifactsFromGroup", "Cannot lock component since we are inside a transaction", ErrorSeverity.ERROR);
+ // Cannot lock component since we are in a middle of another
+ // transaction.
+ ActionStatus actionStatus = ActionStatus.INVALID_CONTENT;
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ return result;
+ }
+
+ Component component = null;
+ try {
+
+ if (groups == null || groups.isEmpty()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.OK));
+ }
+
+ Either<Component, ResponseFormat> validateGroupsBeforeUpdate = validateGroupsBeforeUpdate(componentId, userId, componentType, groups, inTransaction);
+ if (validateGroupsBeforeUpdate.isRight()) {
+ result = Either.right(validateGroupsBeforeUpdate.right().value());
+ return result;
+ }
+
+ component = validateGroupsBeforeUpdate.left().value();
+
+ if (shouldLockComp) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, "Group - Associate Members");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ List<GroupDefinition> updatedGroups = new ArrayList<>();
+
+ // per group, associate to it the members
+ for (GroupDefinition groupDefinition : groups) {
+
+ Either<GroupDefinition, StorageOperationStatus> associateMembersToGroup = groupOperation.associateMembersToGroup(groupDefinition.getUniqueId(), groupDefinition.getMembers(), true);
+
+ if (associateMembersToGroup.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(associateMembersToGroup.right().value());
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ log.debug("Failed to update group {} under component {}, error: {}", groupDefinition.getName(), component.getNormalizedName(), actionStatus.name());
+ return result;
+ } else {
+ updatedGroups.add(associateMembersToGroup.left().value());
+ }
+
+ }
+
+ result = Either.left(updatedGroups);
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ // unlock resource
+ if (shouldLockComp && component != null) {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+
+ }
+ }
+
+ /**
+ * associate artifacts to a given group
+ *
+ * @param componentId
+ * @param userId
+ * @param componentType
+ * @param groups
+ * @param shouldLockComp
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinitionInfo, ResponseFormat> getGroupWithArtifactsById(ComponentTypeEnum componentType, String componentId, String groupId, String userId, boolean inTransaction) {
+
+ Either<GroupDefinitionInfo, ResponseFormat> result = null;
+
+ // Validate user exist
+ Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, UPDATE_GROUP, true);
+
+ if (validateUserExists.isRight()) {
+ result = Either.right(validateUserExists.right().value());
+ return result;
+ }
+
+ User user = validateUserExists.left().value();
+
+ // Validate component exist
+ org.openecomp.sdc.be.model.Component component = null;
+ String realComponentId = componentId;
+
+ try {
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreGroups(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ componentParametersView.setIgnoreUsers(false);
+ componentParametersView.setIgnoreComponentInstances(false);
+
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(realComponentId, componentType, componentParametersView, userId, null, user);
+ if (validateComponent.isRight()) {
+ result = Either.right(validateComponent.right().value());
+ return result;
+ }
+ component = validateComponent.left().value();
+
+ // validate we can work on component
+ /*
+ * Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent( component, userId); if (canWork.isRight()) { result = Either.right(canWork.right().value()); return result; }
+ */
+ List<GroupDefinition> groups = component.getGroups();
+ Optional<GroupDefinition> findAny = groups.stream().filter(p -> p.getUniqueId().equals(groupId)).findAny();
+ if (findAny.isPresent()) {
+ GroupDefinition group = findAny.get();
+ Boolean isBase = null;// Constants.IS_BASE;
+ List<GroupProperty> props = group.getProperties();
+ if (props != null && !props.isEmpty()) {
+ Optional<GroupProperty> isBasePropOp = props.stream().filter(p -> p.getName().equals(Constants.IS_BASE)).findAny();
+ if (isBasePropOp.isPresent()) {
+ GroupProperty propIsBase = isBasePropOp.get();
+ isBase = Boolean.parseBoolean(propIsBase.getValue());
+
+ } else {
+ BeEcompErrorManager.getInstance().logInvalidInputError(GET_GROUP, "failed to find prop isBase " + component.getNormalizedName(), ErrorSeverity.INFO);
+ // return
+ // Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+
+ }
+ }
+
+ List<ArtifactDefinitionInfo> artifacts = new ArrayList();
+ List<String> artifactsIds = group.getArtifacts();
+ if (artifactsIds != null && !artifactsIds.isEmpty()) {
+ Either<List<ArtifactDefinition>, ResponseFormat> getArtifacts = getArtifactsBelongsToComponent(component, artifactsIds, GET_GROUP);
+ if (getArtifacts.isRight()) {
+ log.debug("Faild to find artifacts in group {} under component {}", groupId, component.getUniqueId());
+ // result = Either.right(getArtifacts.right().value());
+ // return result;
+ } else {
+
+ List<ArtifactDefinition> artifactsFromComponent = getArtifacts.left().value();
+ if (artifactsFromComponent != null && !artifactsFromComponent.isEmpty()) {
+ for (ArtifactDefinition artifactDefinition : artifactsFromComponent) {
+ ArtifactDefinitionInfo artifactDefinitionInfo = new ArtifactDefinitionInfo(artifactDefinition);
+ artifacts.add(artifactDefinitionInfo);
+ }
+ }
+ }
+ }
+ GroupDefinitionInfo resultInfo = new GroupDefinitionInfo(group);
+ resultInfo.setIsBase(isBase);
+ if (!artifacts.isEmpty())
+ resultInfo.setArtifacts(artifacts);
+
+ result = Either.left(resultInfo);
+
+ return result;
+
+ } else {
+ log.debug("Faild to find group {} under component {}", groupId, component.getUniqueId());
+ BeEcompErrorManager.getInstance().logInvalidInputError(GET_GROUP, "group " + groupId + " not found under component " + component.getUniqueId(), ErrorSeverity.INFO);
+ String componentTypeForResponse = getComponentTypeForResponse(component);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_IS_MISSING, groupId, component.getSystemName(), componentTypeForResponse));
+ return result;
+
+ }
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ }
+
+ }
+
+ /**
+ * @param componentId
+ * @param userId
+ * @param componentType
+ * @param groups
+ * @param inTransaction
+ * @return
+ */
+ private Either<org.openecomp.sdc.be.model.Component, ResponseFormat> validateGroupsBeforeUpdate(String componentId, String userId, ComponentTypeEnum componentType, List<GroupDefinition> groups, boolean inTransaction) {
+
+ Either<org.openecomp.sdc.be.model.Component, ResponseFormat> result;
+
+ // Validate user exist
+ Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, UPDATE_GROUP, inTransaction);
+ if (validateUserExists.isRight()) {
+ result = Either.right(validateUserExists.right().value());
+ return result;
+ }
+ User user = validateUserExists.left().value();
+
+ // Validate component exist
+ String realComponentId = componentId;
+
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreGroups(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ componentParametersView.setIgnoreUsers(false);
+ componentParametersView.setIgnoreComponentInstances(false);
+
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(realComponentId, componentType, componentParametersView, userId, null, user);
+
+ if (validateComponent.isRight()) {
+ result = Either.right(validateComponent.right().value());
+ return result;
+ }
+ org.openecomp.sdc.be.model.Component component = validateComponent.left().value();
+
+ // validate we can work on component
+ Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, userId);
+ if (canWork.isRight()) {
+ result = Either.right(canWork.right().value());
+ return result;
+ }
+
+ // Validate groups exists in the component
+ ResponseFormat validateGroupsInComponent = validateGroupsInComponentByFunc(groups, component, p -> p.getUniqueId());
+ if (validateGroupsInComponent != null) {
+ result = Either.right(validateGroupsInComponent);
+ return result;
+ }
+
+ Set<String> artifacts = new HashSet<>();
+ groups.forEach(p -> {
+ if (p.getArtifacts() != null) {
+ artifacts.addAll(p.getArtifacts());
+ }
+ });
+ // validate all artifacts belongs to the component
+ Either<Boolean, ResponseFormat> verifyArtifactsBelongsToComponent = verifyArtifactsBelongsToComponent(component, new ArrayList<>(artifacts), UPDATE_GROUP);
+ if (verifyArtifactsBelongsToComponent.isRight()) {
+ result = Either.right(verifyArtifactsBelongsToComponent.right().value());
+ return result;
+ }
+
+ return Either.left(component);
+ }
+
+ private ResponseFormat validateGroupsInComponent(List<GroupDefinition> groups, org.openecomp.sdc.be.model.Component component) {
+
+ Function<GroupDefinition, String> getByName = s -> s.getName();
+
+ return validateGroupsInComponentByFunc(groups, component, getByName);
+
+ }
+
+ /**
+ * @param groups
+ * @param component
+ * @param getByParam
+ * - the method to fetch the key of the GroupDefinition(from groups) in order to compare to groups in the component
+ * @return
+ */
+ private ResponseFormat validateGroupsInComponentByFunc(List<GroupDefinition> groups, org.openecomp.sdc.be.model.Component component, Function<GroupDefinition, String> getByParam) {
+ ResponseFormat result = null;
+
+ List<GroupDefinition> currentGroups = component.getGroups();
+
+ boolean found = false;
+ List<String> updatedGroupsName = groups.stream().map(getByParam).collect(Collectors.toList());
+
+ List<String> missingGroupNames = updatedGroupsName;
+
+ if (currentGroups != null && false == currentGroups.isEmpty()) {
+ List<String> currentGroupsName = currentGroups.stream().map(getByParam).collect(Collectors.toList());
+
+ if (currentGroupsName.containsAll(updatedGroupsName)) {
+ found = true;
+ } else {
+ currentGroupsName.removeAll(currentGroupsName);
+ missingGroupNames = currentGroupsName;
+ }
+ }
+ if (false == found) {
+ String componentTypeForResponse = getComponentTypeForResponse(component);
+ String listOfGroups = getAsString(missingGroupNames);
+ result = componentsUtils.getResponseFormat(ActionStatus.GROUP_IS_MISSING, listOfGroups, component.getSystemName(), componentTypeForResponse);
+ return result;
+ }
+
+ return null;
+ }
+
+ public String getAsString(List<String> list) {
+
+ if (list == null || list.isEmpty()) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ list.forEach(p -> builder.append(p + ","));
+
+ String result = builder.toString();
+ return result.substring(0, result.length());
+
+ }
+
+ /**
+ * dissociate artifacts from a given group
+ *
+ * @param componentId
+ * @param userId
+ * @param componentType
+ * @param groups
+ * @param shouldLockComp
+ * @param inTransaction
+ * @return
+ */
+ public Either<List<GroupDefinition>, ResponseFormat> dissociateArtifactsFromGroup(String componentId, String userId, ComponentTypeEnum componentType, List<GroupDefinition> groups, boolean shouldLockComp, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, ResponseFormat> result = null;
+
+ if (shouldLockComp == true && inTransaction == true) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("dissociateArtifactsFromGroup", "Cannot lock component since we are inside a transaction", ErrorSeverity.ERROR);
+ // Cannot lock component since we are in a middle of another
+ // transaction.
+ ActionStatus actionStatus = ActionStatus.INVALID_CONTENT;
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ return result;
+ }
+
+ Component component = null;
+
+ try {
+
+ if (groups == null || groups.isEmpty()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.OK));
+ }
+
+ Either<Component, ResponseFormat> validateGroupsBeforeUpdate = validateGroupsBeforeUpdate(componentId, userId, componentType, groups, inTransaction);
+ if (validateGroupsBeforeUpdate.isRight()) {
+ result = Either.right(validateGroupsBeforeUpdate.right().value());
+ return result;
+ }
+
+ component = validateGroupsBeforeUpdate.left().value();
+
+ if (shouldLockComp) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, "Group - Dissociate Artifacts");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ List<GroupDefinition> updatedGroups = new ArrayList<>();
+
+ List<GroupDefinition> componentGroups = component.getGroups();
+ // per group, associate to it the artifacts
+ for (GroupDefinition groupDefinition : groups) {
+
+ GroupDefinition componentGroup = componentGroups.stream().filter(p -> p.getUniqueId().equals(groupDefinition.getUniqueId())).findFirst().orElse(null);
+ if (componentGroup != null) {
+ List<String> componentArtifacts = componentGroup.getArtifacts();
+ int artifactsSizeInGroup = componentArtifacts == null ? 0 : componentArtifacts.size();
+ List<String> artifactsToDissociate = groupDefinition.getArtifacts();
+
+ // if no artifcats sent
+ if (artifactsToDissociate == null || true == artifactsToDissociate.isEmpty()) {
+ continue;
+ }
+
+ if (artifactsSizeInGroup > 0) {
+
+ boolean containsAll = componentArtifacts.containsAll(artifactsToDissociate);
+ if (false == containsAll) { // At least one artifact is
+ // not associated to the
+ // group
+ log.debug("Some of the artifacts already dissociated to group {}", groupDefinition.getUniqueId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_ARTIFACT_ALREADY_DISSOCIATED, componentGroup.getName()));
+ }
+ } else {
+ if (artifactsSizeInGroup == 0) {
+ if (artifactsToDissociate != null && false == artifactsToDissociate.isEmpty()) {
+ log.debug("No artifact is found under the group {}", groupDefinition.getUniqueId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_ARTIFACT_ALREADY_DISSOCIATED, componentGroup.getName()));
+ }
+ }
+ }
+ }
+
+ Either<GroupDefinition, StorageOperationStatus> associateArtifactsToGroup = groupOperation.dissociateArtifactsFromGroup(groupDefinition.getUniqueId(), groupDefinition.getArtifacts(), true);
+
+ if (associateArtifactsToGroup.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(associateArtifactsToGroup.right().value());
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ log.debug("Failed to update group {} under component {}, error: {}", groupDefinition.getName(), component.getNormalizedName(), actionStatus.name());
+ return result;
+ }
+ updatedGroups.add(associateArtifactsToGroup.left().value());
+
+ }
+
+ result = Either.left(updatedGroups);
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+ // unlock resource
+ if (shouldLockComp && component != null) {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+
+ }
+
+ }
+
+ public Either<List<GroupDefinition>, ResponseFormat> createGroups(String componentId, String userId, ComponentTypeEnum componentType, List<GroupDefinition> groupDefinitions, boolean shouldLockComp, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, ResponseFormat> result = null;
+
+ List<GroupDefinition> groups = new ArrayList<>();
+ org.openecomp.sdc.be.model.Component component = null;
+ try {
+
+ if (groupDefinitions != null && false == groupDefinitions.isEmpty()) {
+
+ if (shouldLockComp == true && inTransaction == true) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("createGroups", "Cannot lock component since we are inside a transaction", ErrorSeverity.ERROR);
+ // Cannot lock component since we are in a middle of another
+ // transaction.
+ ActionStatus actionStatus = ActionStatus.INVALID_CONTENT;
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ return result;
+ }
+
+ Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, CREATE_GROUP, true);
+ if (validateUserExists.isRight()) {
+ result = Either.right(validateUserExists.right().value());
+ return result;
+ }
+
+ User user = validateUserExists.left().value();
+
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreGroups(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ componentParametersView.setIgnoreUsers(false);
+ componentParametersView.setIgnoreComponentInstances(false);
+
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(componentId, componentType, componentParametersView, userId, null, user);
+
+ if (validateComponent.isRight()) {
+ result = Either.right(validateComponent.right().value());
+ return result;
+ }
+ component = validateComponent.left().value();
+
+ if (shouldLockComp) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, "CreateGroups");
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, userId);
+ if (canWork.isRight()) {
+ result = Either.right(canWork.right().value());
+ return result;
+ }
+
+ for (GroupDefinition groupDefinition : groupDefinitions) {
+ Either<GroupDefinition, ResponseFormat> createGroup = this.createGroup(component, user, componentType, groupDefinition, true);
+ if (createGroup.isRight()) {
+ log.debug("Failed to create group {}.", groupDefinition);
+ result = Either.right(createGroup.right().value());
+ return result;
+ }
+ GroupDefinition createdGroup = createGroup.left().value();
+ groups.add(createdGroup);
+ }
+ }
+
+ result = Either.left(groups);
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+ // unlock resource
+ if (shouldLockComp && component != null) {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+
+ }
+
+ }
+
+ public Either<GroupDefinition, ResponseFormat> createGroup(Component component, User user, ComponentTypeEnum componentType, GroupDefinition groupDefinition, boolean inTransaction) {
+
+ Either<GroupDefinition, ResponseFormat> result = null;
+
+ log.debug("Going to create group {}", groupDefinition);
+
+ try {
+
+ // 3. verify group not already exist
+ List<GroupDefinition> groups = component.getGroups();
+ boolean found = false;
+ if (groups != null && false == groups.isEmpty()) {
+
+ GroupDefinition existGroupDef = groups.stream().filter(p -> p.getName().equalsIgnoreCase(groupDefinition.getName())).findFirst().orElse(null);
+
+ found = existGroupDef != null;
+ }
+
+ if (true == found) {
+ String componentTypeForResponse = getComponentTypeForResponse(component);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_ALREADY_EXIST, groupDefinition.getName(), component.getNormalizedName(), componentTypeForResponse));
+ return result;
+ }
+
+ // 4. verify type of group exist
+ String groupType = groupDefinition.getType();
+ if (groupType == null || groupType.isEmpty()) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_MISSING_GROUP_TYPE, groupDefinition.getName()));
+ return result;
+ }
+ Either<GroupTypeDefinition, StorageOperationStatus> getGroupType = groupTypeOperation.getLatestGroupTypeByType(groupType, true);
+ if (getGroupType.isRight()) {
+ StorageOperationStatus status = getGroupType.right().value();
+ if (status == StorageOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(CREATE_GROUP, "group type " + groupType + " cannot be found", ErrorSeverity.INFO);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_TYPE_IS_INVALID, groupType));
+ return result;
+ } else {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+ }
+
+ // 6. verify the component instances type are allowed according to
+ // the member types in the group type
+ GroupTypeDefinition groupTypeDefinition = getGroupType.left().value();
+
+ Either<Boolean, ResponseFormat> areValidMembers = verifyComponentInstancesAreValidMembers(component, componentType, groupDefinition.getName(), groupType, groupDefinition.getMembers(), groupTypeDefinition.getMembers());
+
+ if (areValidMembers.isRight()) {
+ ResponseFormat responseFormat = areValidMembers.right().value();
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ // 7. verify the artifacts belongs to the component
+ Either<Boolean, ResponseFormat> areValidArtifacts = verifyArtifactsBelongsToComponent(component, groupDefinition.getArtifacts(), CREATE_GROUP);
+ if (areValidArtifacts.isRight()) {
+ ResponseFormat responseFormat = areValidArtifacts.right().value();
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ NodeTypeEnum nodeTypeEnum = componentType.getNodeType();
+
+ // add invariantUUID
+ String invariantUUID = UniqueIdBuilder.buildInvariantUUID();
+ groupDefinition.setInvariantUUID(invariantUUID);
+
+ // add groupUUID
+ String groupUUID = UniqueIdBuilder.generateUUID();
+ groupDefinition.setGroupUUID(groupUUID);
+
+ // add version
+ groupDefinition.setVersion(INITIAL_VERSION);
+
+ // set groupType uid
+ groupDefinition.setTypeUid(groupTypeDefinition.getUniqueId());
+
+ Either<GroupDefinition, StorageOperationStatus> addGroupToGraph = groupOperation.addGroup(nodeTypeEnum, component.getUniqueId(), groupDefinition, true);
+
+ if (addGroupToGraph.isRight()) {
+ StorageOperationStatus storageOperationStatus = addGroupToGraph.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageOperationStatus);
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ log.debug("Failed to create group {} under component {}, error: {}", groupDefinition.getName(), component.getNormalizedName(), actionStatus.name());
+ } else {
+ GroupDefinition groupDefinitionCreated = addGroupToGraph.left().value();
+ result = Either.left(groupDefinitionCreated);
+ }
+
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ }
+
+ }
+
+ public Either<List<GroupDefinition>, ResponseFormat> updateVfModuleGroupNames(String resourceSystemName, List<GroupDefinition> groups, boolean inTransaction) {
+ List<GroupDefinition> updatedGroups = new ArrayList<>();
+ Either<List<GroupDefinition>, ResponseFormat> updateGroupNamesRes = Either.left(updatedGroups);
+ Either<GroupDefinition, StorageOperationStatus> updateGroupNameRes;
+ Either<String, ResponseFormat> validateGenerateGroupNameRes;
+ int counter;
+ for (GroupDefinition group : groups) {
+ if (!group.getType().equals(Constants.DEFAULT_GROUP_VF_MODULE) && !Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(group.getName()).matches()) {
+ continue;
+ }
+ counter = Integer.parseInt(group.getName().split(Constants.MODULE_NAME_DELIMITER)[1]);
+ validateGenerateGroupNameRes = validateGenerateVfModuleGroupName(resourceSystemName, group.getDescription(), counter);
+ if (validateGenerateGroupNameRes.isRight()) {
+ updateGroupNamesRes = Either.right(validateGenerateGroupNameRes.right().value());
+ break;
+ }
+ updateGroupNameRes = groupOperation.updateGroupName(group.getUniqueId(), validateGenerateGroupNameRes.left().value(), inTransaction);
+ if (updateGroupNameRes.isRight()) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(updateGroupNameRes.right().value());
+ updateGroupNamesRes = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ break;
+ }
+ updatedGroups.add(updateGroupNameRes.left().value());
+ }
+ return updateGroupNamesRes;
+ }
+
+ public Either<Boolean, ResponseFormat> validateGenerateVfModuleGroupNames(List<ArtifactTemplateInfo> allGroups, String resourceSystemName, int startGroupCounter) {
+ Either<Boolean, ResponseFormat> validateGenerateGroupNamesRes = Either.left(true);
+ Collections.sort(allGroups, (art1, art2) -> ArtifactTemplateInfo.compareByGroupName(art1, art2));
+ for (ArtifactTemplateInfo group : allGroups) {
+ Either<String, ResponseFormat> validateGenerateGroupNameRes = validateGenerateVfModuleGroupName(resourceSystemName, group.getDescription(), startGroupCounter++);
+ if (validateGenerateGroupNameRes.isRight()) {
+ validateGenerateGroupNamesRes = Either.right(validateGenerateGroupNameRes.right().value());
+ break;
+ }
+ group.setGroupName(validateGenerateGroupNameRes.left().value());
+ }
+ return validateGenerateGroupNamesRes;
+ }
+
+ /**
+ * Generate module name from resourceName, description and counter
+ *
+ * @param resourceSystemName
+ * @param description
+ * @param groupCounter
+ * @return
+ */
+ private Either<String, ResponseFormat> validateGenerateVfModuleGroupName(String resourceSystemName, String description, int groupCounter) {
+ Either<String, ResponseFormat> validateGenerateGroupNameRes;
+ if (resourceSystemName != null && description != null && Pattern.compile(Constants.MODULE_DESC_PATTERN).matcher(description).matches()) {
+ final String fileName = description.replaceAll("\\.\\.", "\\.");
+ validateGenerateGroupNameRes = Either.left(String.format(Constants.MODULE_NAME_FORMAT, resourceSystemName, FilenameUtils.removeExtension(fileName), groupCounter));
+ } else {
+ validateGenerateGroupNameRes = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME));
+ }
+ return validateGenerateGroupNameRes;
+ }
+
+ public Either<Map<String, GroupDefinition>, ResponseFormat> validateUpdateVfGroupNames(Map<String, GroupDefinition> groups, String resourceSystemName) {
+
+ Map<String, GroupDefinition> updatedNamesGroups = new HashMap<>();
+ Either<Map<String, GroupDefinition>, ResponseFormat> result = Either.left(updatedNamesGroups);
+ for (Entry<String, GroupDefinition> groupEntry : groups.entrySet()) {
+ GroupDefinition curGroup = groupEntry.getValue();
+ String groupType = curGroup.getType();
+ String groupName = groupEntry.getKey();
+ int counter;
+ String description;
+ Either<String, ResponseFormat> newGroupNameRes;
+ if (groupType.equals(Constants.DEFAULT_GROUP_VF_MODULE) && !Pattern.compile(Constants.MODULE_NEW_NAME_PATTERN).matcher(groupName).matches()) {
+
+ if (Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(groupEntry.getKey()).matches()) {
+ counter = Integer.parseInt(groupEntry.getKey().split(Constants.MODULE_NAME_DELIMITER)[1]);
+ description = curGroup.getDescription();
+ } else {
+ counter = getNextVfModuleNameCounter(updatedNamesGroups);
+ description = groupName;
+ }
+ newGroupNameRes = validateGenerateVfModuleGroupName(resourceSystemName, description, counter);
+ if (newGroupNameRes.isRight()) {
+ log.debug("Failed to generate new vf module group name. Status is {} ", newGroupNameRes.right().value());
+ result = Either.right(newGroupNameRes.right().value());
+ break;
+ }
+ groupName = newGroupNameRes.left().value();
+ curGroup.setName(groupName);
+ }
+ updatedNamesGroups.put(groupName, curGroup);
+ }
+ return result;
+ }
+
+ public int getNextVfModuleNameCounter(Map<String, GroupDefinition> groups) {
+ int counter = 0;
+ if (groups != null && !groups.isEmpty()) {
+ counter = getNextVfModuleNameCounter(groups.values());
+ }
+ return counter;
+ }
+
+ public int getNextVfModuleNameCounter(Collection<GroupDefinition> groups) {
+ int counter = 0;
+ if (groups != null && !groups.isEmpty()) {
+ List<Integer> counters = groups.stream().filter(group -> Pattern.compile(Constants.MODULE_NEW_NAME_PATTERN).matcher(group.getName()).matches() || Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(group.getName()).matches())
+ .map(group -> Integer.parseInt(group.getName().split(Constants.MODULE_NAME_DELIMITER)[1])).collect(Collectors.toList());
+ counter = (counters == null || counters.isEmpty()) ? 0 : counters.stream().max((a, b) -> Integer.compare(a, b)).get() + 1;
+ }
+ return counter;
+ }
+
+ public Either<List<GroupDefinition>, ResponseFormat> validateUpdateVfGroupNamesOnGraph(List<GroupDefinition> groups, String resourceSystemName, boolean inTransaction) {
+ List<GroupDefinition> updatedGroups = new ArrayList<>();
+ Either<List<GroupDefinition>, ResponseFormat> result = Either.left(updatedGroups);
+
+ for (GroupDefinition group : groups) {
+ String groupType = group.getType();
+ String oldGroupName = group.getName();
+ String newGroupName;
+ Either<String, ResponseFormat> newGroupNameRes;
+ Either<GroupDefinition, StorageOperationStatus> updateGroupNameRes;
+ int counter;
+ if (groupType.equals(Constants.DEFAULT_GROUP_VF_MODULE) && Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(oldGroupName).matches()) {
+ counter = Integer.parseInt(group.getName().split(Constants.MODULE_NAME_DELIMITER)[1]);
+ newGroupNameRes = validateGenerateVfModuleGroupName(resourceSystemName, group.getDescription(), counter);
+ if (newGroupNameRes.isRight()) {
+ log.debug("Failed to generate new vf module group name. Status is {} ", newGroupNameRes.right().value());
+ result = Either.right(newGroupNameRes.right().value());
+ break;
+ }
+ newGroupName = newGroupNameRes.left().value();
+ updateGroupNameRes = groupOperation.updateGroupName(group.getUniqueId(), newGroupName, inTransaction);
+ if (updateGroupNameRes.isRight()) {
+ log.debug("Failed to update vf module group name for group {} . Status is {} ", oldGroupName, updateGroupNameRes.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateGroupNameRes.right().value()));
+ result = Either.right(responseFormat);
+ break;
+ }
+ }
+ updatedGroups.add(group);
+ }
+ return result;
+ }
+
+ public Either<List<GroupDefinition>, ResponseFormat> createGroups(Component component, User user, ComponentTypeEnum componentType, List<GroupDefinition> groupDefinitions, boolean inTransaction) {
+
+ List<GroupDefinition> generatedGroups = new ArrayList<>();
+ Either<List<GroupDefinition>, ResponseFormat> result = Either.left(generatedGroups);
+
+ try {
+
+ if (groupDefinitions != null && false == groupDefinitions.isEmpty()) {
+ for (GroupDefinition groupDefinition : groupDefinitions) {
+ Either<GroupDefinition, ResponseFormat> createGroup = this.createGroup(component, user, componentType, groupDefinition, true);
+ if (createGroup.isRight()) {
+ result = Either.right(createGroup.right().value());
+ return result;
+ }
+ GroupDefinition generatedGroup = createGroup.left().value();
+ generatedGroups.add(generatedGroup);
+ }
+ }
+
+ return result;
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ }
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java
new file mode 100644
index 0000000000..3dee3839b1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/GroupTypeImportManager.java
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("groupTypeImportManager")
+public class GroupTypeImportManager {
+
+ public static void main(String[] args) {
+
+ List<PropertyDefinition> properties = new ArrayList<>();
+ PropertyDefinition propertyDefintion = new PropertyDefinition();
+ propertyDefintion.setName("aaa");
+ properties.add(propertyDefintion);
+
+ List<String> allParentsProps = new ArrayList<>();
+ allParentsProps.add("aaa");
+ allParentsProps.add("bbb");
+
+ Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
+ System.out.println(alreadyExistPropsCollection);
+
+ }
+
+ private static Logger log = LoggerFactory.getLogger(GroupTypeImportManager.class.getName());
+ @Resource
+ private PropertyOperation propertyOperation;
+ @Resource
+ private IGroupTypeOperation groupTypeOperation;
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Resource
+ private IResourceOperation resourceOperation;
+
+ @Resource
+ private CommonImportManager commonImportManager;
+
+ public Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat> createGroupTypes(String groupTypesYml) {
+ return commonImportManager.createElementTypes(groupTypesYml, elementTypeYml -> createGroupTypesFromYml(elementTypeYml), groupTypesList -> createGroupTypesByDao(groupTypesList), ElementTypeEnum.GroupType);
+ }
+
+ private Either<List<GroupTypeDefinition>, ActionStatus> createGroupTypesFromYml(String groupTypesYml) {
+
+ return commonImportManager.createElementTypesFromYml(groupTypesYml, (groupTypeName, groupTypeJsonData) -> createGroupType(groupTypeName, groupTypeJsonData));
+ }
+
+ private Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat> createGroupTypesByDao(List<GroupTypeDefinition> groupTypesToCreate) {
+ return commonImportManager.createElementTypesByDao(groupTypesToCreate, groupType -> validateGroupType(groupType), groupType -> new ImmutablePair<>(ElementTypeEnum.GroupType, groupType.getType()),
+ groupTypeName -> groupTypeOperation.getLatestGroupTypeByType(groupTypeName), groupType -> groupTypeOperation.addGroupType(groupType), null);
+ }
+
+ private Either<ActionStatus, ResponseFormat> validateGroupType(GroupTypeDefinition groupType) {
+ Either<ActionStatus, ResponseFormat> result = Either.left(ActionStatus.OK);
+ if (groupType.getMembers() != null) {
+ if (groupType.getMembers().isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GROUP_MEMBER_EMPTY, groupType.getType());
+ result = Either.right(responseFormat);
+ } else {
+ for (String member : groupType.getMembers()) {
+ // Verify that such Resource exist
+ Either<org.openecomp.sdc.be.model.Resource, StorageOperationStatus> eitherMemberExist = resourceOperation.getLatestByToscaResourceName(member, false);
+ if (eitherMemberExist.isRight()) {
+ StorageOperationStatus operationStatus = eitherMemberExist.right().value();
+ log.debug("Error when fetching parent resource {}, error: {}", member, operationStatus);
+ ActionStatus convertFromStorageResponse = componentsUtils.convertFromStorageResponse(operationStatus);
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("Import GroupType", "resource", member);
+ result = Either.right(componentsUtils.getResponseFormat(convertFromStorageResponse, member));
+ break;
+ }
+ }
+
+ }
+ }
+ return result;
+ }
+
+ private GroupTypeDefinition createGroupType(String groupTypeName, Map<String, Object> toscaJson) {
+
+ GroupTypeDefinition groupType = new GroupTypeDefinition();
+
+ if (toscaJson != null) {
+ // Description
+ final Consumer<String> descriptionSetter = description -> groupType.setDescription(description);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DESCRIPTION.getElementName(), descriptionSetter);
+ // Derived From
+ final Consumer<String> derivedFromSetter = derivedFrom -> groupType.setDerivedFrom(derivedFrom);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DERIVED_FROM.getElementName(), derivedFromSetter);
+ // Properties
+ commonImportManager.setProperties(toscaJson, (values) -> groupType.setProperties(values));
+ // Metadata
+ final Consumer<Map<String, String>> metadataSetter = metadata -> groupType.setMetadata(metadata);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.METADATA.getElementName(), metadataSetter);
+ // Members
+ final Consumer<List<String>> membersSetter = members -> groupType.setMembers(members);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.MEMBERS.getElementName(), membersSetter);
+
+ groupType.setType(groupTypeName);
+
+ groupType.setHighestVersion(true);
+
+ groupType.setVersion(ImportUtils.Constants.FIRST_CERTIFIED_VERSION_VERSION);
+ }
+ return groupType;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/HealthCheckBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/HealthCheckBusinessLogic.java
new file mode 100644
index 0000000000..81dfc1a256
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/HealthCheckBusinessLogic.java
@@ -0,0 +1,441 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import javax.servlet.ServletContext;
+
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth;
+import org.openecomp.sdc.be.components.distribution.engine.UebHealthCheckCall;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.IEsHealthCheckDao;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.switchover.detector.SwitchoverDetector;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.HealthCheckInfo;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckComponent;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.WebApplicationContext;
+
+@Component("healthCheckBusinessLogic")
+public class HealthCheckBusinessLogic {
+
+ protected static String BE_HEALTH_LOG_CONTEXT = "be.healthcheck";
+
+ private static Logger healthLogger = LoggerFactory.getLogger(BE_HEALTH_LOG_CONTEXT);
+
+ private static final String BE_HEALTH_CHECK_STR = "beHealthCheck";
+
+ @Resource
+ private TitanGenericDao titanGenericDao;
+
+ @Resource
+ private IEsHealthCheckDao esHealthCheckDao;
+
+ @Resource
+ private DistributionEngineClusterHealth distributionEngineClusterHealth;
+
+ @Autowired
+ private SwitchoverDetector switchoverDetector;
+
+ private static Logger log = LoggerFactory.getLogger(HealthCheckBusinessLogic.class.getName());
+
+ private volatile List<HealthCheckInfo> lastBeHealthCheckInfos = null;
+
+ // private static volatile HealthCheckBusinessLogic instance;
+ //
+ public HealthCheckBusinessLogic() {
+
+ }
+
+ private ScheduledFuture<?> scheduledFuture = null;
+
+ ScheduledExecutorService healthCheckScheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "BE-Health-Check-Task");
+ }
+ });
+
+ HealthCheckScheduledTask healthCheckScheduledTask = null;
+
+ @PostConstruct
+ public void init() {
+
+ lastBeHealthCheckInfos = getBeHealthCheckInfos();
+
+ log.debug("After initializing lastBeHealthCheckInfos :{}", lastBeHealthCheckInfos);
+
+ healthCheckScheduledTask = new HealthCheckScheduledTask();
+
+ if (this.scheduledFuture == null) {
+ this.scheduledFuture = this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, 3, TimeUnit.SECONDS);
+ }
+
+ }
+
+ //
+ // public static HealthCheckBusinessLogic getInstance(){
+ //// if (instance == null){
+ //// instance = init();
+ //// }
+ // return instance;
+ // }
+
+ // private synchronized static HealthCheckBusinessLogic init() {
+ // if (instance == null){
+ // instance = new HealthCheckBusinessLogic();
+ // }
+ // return instance;
+ // }
+
+ private List<HealthCheckInfo> getBeHealthCheckInfos(ServletContext servletContext) {
+
+ List<HealthCheckInfo> healthCheckInfos = new ArrayList<HealthCheckInfo>();
+
+ // BE
+ getBeHealthCheck(servletContext, healthCheckInfos);
+
+ // ES
+ getEsHealthCheck(servletContext, healthCheckInfos);
+
+ // Titan
+ getTitanHealthCheck(servletContext, healthCheckInfos);
+
+ // Distribution Engine
+ getDistributionEngineCheck(servletContext, healthCheckInfos);
+
+ return healthCheckInfos;
+ }
+
+ private List<HealthCheckInfo> getBeHealthCheck(ServletContext servletContext, List<HealthCheckInfo> healthCheckInfos) {
+ String appVersion = ExternalConfiguration.getAppVersion();
+ String description = "OK";
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.BE, HealthCheckStatus.UP, appVersion, description));
+ return healthCheckInfos;
+ }
+
+ public List<HealthCheckInfo> getTitanHealthCheck(ServletContext servletContext, List<HealthCheckInfo> healthCheckInfos) {
+ // Titan health check and version
+ TitanGenericDao titanStatusDao = (TitanGenericDao) getDao(servletContext, TitanGenericDao.class);
+ String description;
+ boolean isTitanUp;
+
+ try {
+ isTitanUp = titanStatusDao.isGraphOpen();
+ } catch (Exception e) {
+ description = "Titan error: " + e.getMessage();
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.DOWN, null, description));
+ return healthCheckInfos;
+ }
+ if (isTitanUp) {
+ description = "OK";
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.UP, null, description));
+ } else {
+ description = "Titan graph is down";
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.DOWN, null, description));
+ }
+ return healthCheckInfos;
+ }
+
+ public List<HealthCheckInfo> getEsHealthCheck(ServletContext servletContext, List<HealthCheckInfo> healthCheckInfos) {
+
+ // ES health check and version
+ IEsHealthCheckDao esStatusDao = (IEsHealthCheckDao) getDao(servletContext, IEsHealthCheckDao.class);
+ HealthCheckStatus healthCheckStatus;
+ String description;
+
+ try {
+ healthCheckStatus = esStatusDao.getClusterHealthStatus();
+ } catch (Exception e) {
+ healthCheckStatus = HealthCheckStatus.DOWN;
+ description = "ES cluster error: " + e.getMessage();
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, healthCheckStatus, null, description));
+ return healthCheckInfos;
+ }
+ if (healthCheckStatus.equals(HealthCheckStatus.DOWN)) {
+ description = "ES cluster is down";
+ } else {
+ description = "OK";
+ }
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, healthCheckStatus, null, description));
+ return healthCheckInfos;
+ }
+
+ public Object getDao(ServletContext servletContext, Class<?> clazz) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(servletContext);
+
+ return webApplicationContext.getBean(clazz);
+ }
+
+ private void getDistributionEngineCheck(ServletContext servletContext, List<HealthCheckInfo> healthCheckInfos) {
+
+ DistributionEngineClusterHealth deDao = (DistributionEngineClusterHealth) getDao(servletContext, DistributionEngineClusterHealth.class);
+ HealthCheckInfo healthCheckInfo = deDao.getHealthCheckInfo();
+
+ healthCheckInfos.add(healthCheckInfo);
+
+ }
+
+ public boolean isDistributionEngineUp(ServletContext servletContext) {
+
+ DistributionEngineClusterHealth deDao = (DistributionEngineClusterHealth) getDao(servletContext, DistributionEngineClusterHealth.class);
+ HealthCheckInfo healthCheckInfo = deDao.getHealthCheckInfo();
+ if (healthCheckInfo.getHealthCheckStatus().equals(HealthCheckStatus.DOWN)) {
+ return false;
+ }
+ return true;
+ }
+
+ public List<HealthCheckInfo> getBeHealthCheckInfosStatus() {
+
+ return lastBeHealthCheckInfos;
+
+ }
+
+ private List<HealthCheckInfo> getBeHealthCheckInfos() {
+
+ log.trace("In getBeHealthCheckInfos");
+
+ List<HealthCheckInfo> healthCheckInfos = new ArrayList<HealthCheckInfo>();
+
+ // BE
+ getBeHealthCheck(healthCheckInfos);
+
+ // ES
+ getEsHealthCheck(healthCheckInfos);
+
+ // Titan
+ getTitanHealthCheck(healthCheckInfos);
+
+ // Distribution Engine
+ getDistributionEngineCheck(healthCheckInfos);
+
+ return healthCheckInfos;
+ }
+
+ private List<HealthCheckInfo> getBeHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
+ String appVersion = ExternalConfiguration.getAppVersion();
+ String description = "OK";
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.BE, HealthCheckStatus.UP, appVersion, description));
+ return healthCheckInfos;
+ }
+
+ public List<HealthCheckInfo> getEsHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
+
+ // ES health check and version
+ HealthCheckStatus healthCheckStatus;
+ String description;
+
+ try {
+ healthCheckStatus = esHealthCheckDao.getClusterHealthStatus();
+ } catch (Exception e) {
+ healthCheckStatus = HealthCheckStatus.DOWN;
+ description = "ES cluster error: " + e.getMessage();
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, healthCheckStatus, null, description));
+ return healthCheckInfos;
+ }
+ if (healthCheckStatus.equals(HealthCheckStatus.DOWN)) {
+ description = "ES cluster is down";
+ } else {
+ description = "OK";
+ }
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, healthCheckStatus, null, description));
+ return healthCheckInfos;
+ }
+
+ public List<HealthCheckInfo> getTitanHealthCheck(List<HealthCheckInfo> healthCheckInfos) {
+ // Titan health check and version
+ String description;
+ boolean isTitanUp;
+
+ try {
+ isTitanUp = titanGenericDao.isGraphOpen();
+ } catch (Exception e) {
+ description = "Titan error: " + e.getMessage();
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.DOWN, null, description));
+ return healthCheckInfos;
+ }
+ if (isTitanUp) {
+ description = "OK";
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.UP, null, description));
+ } else {
+ description = "Titan graph is down";
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.DOWN, null, description));
+ }
+ return healthCheckInfos;
+ }
+
+ private void getDistributionEngineCheck(List<HealthCheckInfo> healthCheckInfos) {
+
+ HealthCheckInfo healthCheckInfo = distributionEngineClusterHealth.getHealthCheckInfo();
+
+ healthCheckInfos.add(healthCheckInfo);
+
+ }
+
+ @PreDestroy
+ private void destroy() {
+
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(true);
+ scheduledFuture = null;
+ }
+
+ if (healthCheckScheduler != null) {
+ healthCheckScheduler.shutdown();
+ }
+
+ }
+
+ public class HealthCheckScheduledTask implements Runnable {
+
+ List<UebHealthCheckCall> healthCheckCalls = new ArrayList<>();
+
+ public HealthCheckScheduledTask() {
+
+ }
+
+ @Override
+ public void run() {
+
+ healthLogger.trace("Executing BE Health Check Task");
+
+ List<HealthCheckInfo> beHealthCheckInfos = getBeHealthCheckInfos();
+ boolean healthStatus = getAggregateBeStatus(beHealthCheckInfos);
+
+ boolean lastHealthStatus = getAggregateBeStatus(lastBeHealthCheckInfos);
+
+ if (lastHealthStatus != healthStatus) {
+ log.trace("BE Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
+
+ lastBeHealthCheckInfos = beHealthCheckInfos;
+ logAlarm(healthStatus);
+
+ } else {
+ // check if we need to update the status's list in case one of
+ // the statuses was changed
+ if (true == anyStatusChanged(beHealthCheckInfos, lastBeHealthCheckInfos)) {
+ lastBeHealthCheckInfos = beHealthCheckInfos;
+ }
+
+ }
+
+ }
+
+ }
+
+ private void logAlarm(boolean lastHealthState) {
+ if (lastHealthState == true) {
+ BeEcompErrorManager.getInstance().logBeHealthCheckRecovery(BE_HEALTH_CHECK_STR);
+ } else {
+ BeEcompErrorManager.getInstance().logBeHealthCheckError(BE_HEALTH_CHECK_STR);
+ }
+ }
+
+ private boolean getAggregateBeStatus(List<HealthCheckInfo> beHealthCheckInfos) {
+
+ boolean status = true;
+
+ for (HealthCheckInfo healthCheckInfo : beHealthCheckInfos) {
+ if (healthCheckInfo.getHealthCheckStatus().equals(HealthCheckStatus.DOWN) && healthCheckInfo.getHealthCheckComponent() != HealthCheckComponent.DE) {
+ status = false;
+ break;
+ }
+ }
+ return status;
+ }
+
+ public String getSiteMode() {
+ return switchoverDetector.getSiteMode();
+ }
+
+ public boolean anyStatusChanged(List<HealthCheckInfo> beHealthCheckInfos, List<HealthCheckInfo> lastBeHealthCheckInfos) {
+
+ boolean result = false;
+
+ if (beHealthCheckInfos != null && lastBeHealthCheckInfos != null) {
+
+ Map<HealthCheckComponent, HealthCheckStatus> currentValues = beHealthCheckInfos.stream().collect(Collectors.toMap(p -> p.getHealthCheckComponent(), p -> p.getHealthCheckStatus()));
+ Map<HealthCheckComponent, HealthCheckStatus> lastValues = lastBeHealthCheckInfos.stream().collect(Collectors.toMap(p -> p.getHealthCheckComponent(), p -> p.getHealthCheckStatus()));
+
+ if (currentValues != null && lastValues != null) {
+ int currentSize = currentValues.size();
+ int lastSize = lastValues.size();
+
+ if (currentSize != lastSize) {
+ result = true;
+ } else {
+
+ for (Entry<HealthCheckComponent, HealthCheckStatus> entry : currentValues.entrySet()) {
+ HealthCheckComponent key = entry.getKey();
+ HealthCheckStatus value = entry.getValue();
+
+ if (false == lastValues.containsKey(key)) {
+ result = true;
+ break;
+ }
+
+ HealthCheckStatus lastHealthCheckStatus = lastValues.get(key);
+
+ if (value != lastHealthCheckStatus) {
+ result = true;
+ break;
+ }
+ }
+ }
+ } else if (currentValues == null && lastValues == null) {
+ result = false;
+ } else {
+ result = true;
+ }
+
+ } else if (beHealthCheckInfos == null && lastBeHealthCheckInfos == null) {
+ result = false;
+ } else {
+ result = true;
+ }
+
+ return result;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/IDeploymentArtifactTypeConfigGetter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/IDeploymentArtifactTypeConfigGetter.java
new file mode 100644
index 0000000000..68569ebc3b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/IDeploymentArtifactTypeConfigGetter.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.config.Configuration.DeploymentArtifactTypeConfig;
+
+public interface IDeploymentArtifactTypeConfigGetter {
+ DeploymentArtifactTypeConfig getDeploymentArtifactConfig();
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ImportUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ImportUtils.java
new file mode 100644
index 0000000000..94a7340ff7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ImportUtils.java
@@ -0,0 +1,722 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.heat.HeatParameterType;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.util.GsonFactory;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.representer.Representer;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+import fj.data.Either;
+
+public final class ImportUtils {
+ private ImportUtils() {
+
+ }
+
+ private static CustomResolver customResolver = new CustomResolver();
+
+ private static class CustomResolver extends Resolver {
+ protected void addImplicitResolvers() {
+ // avoid implicit resolvers
+ }
+ }
+
+ public static Either<List<HeatParameterDefinition>, ResultStatusEnum> getHeatParamsWithoutImplicitTypes(String heatDecodedPayload, String artifactType) {
+ Map<String, Object> heatData = (Map<String, Object>) new Yaml(new Constructor(), new Representer(), new DumperOptions(), customResolver).load(heatDecodedPayload);
+ return getHeatParameters(heatData, artifactType);
+ }
+
+ public static class Constants {
+
+ public static final String FIRST_CERTIFIED_VERSION_VERSION = "1.0";
+ public static final String FIRST_NON_CERTIFIED_VERSION = "0.1";
+ public static final String VENDOR_NAME = "ATT (Tosca)";
+ public static final String VENDOR_RELEASE = "1.0.0.wd03";
+ public static final LifecycleStateEnum NORMATIVE_TYPE_LIFE_CYCLE = LifecycleStateEnum.CERTIFIED;
+ public static final boolean NORMATIVE_TYPE_HIGHEST_VERSION = true;
+ // public static final String ABSTRACT_CATEGORY = "Generic/Abstract";
+ public static final String ABSTRACT_CATEGORY_NAME = "Generic";
+ public static final String ABSTRACT_SUBCATEGORY = "Abstract";
+ public static final String DEFAULT_ICON = "defaulticon";
+ public static final String INNER_VFC_DESCRIPTION = "Not reusable inner VFC";
+ public static final String USER_DEFINED_RESOURCE_NAMESPACE_PREFIX = "org.openecomp.resource.";
+ public static final List<String> TOSCA_DEFINITION_VERSIONS = Arrays.asList(new String[] { "tosca_simple_yaml_1_0_0", "tosca_simple_profile_for_nfv_1_0_0", "tosca_simple_yaml_1_0" });
+ public static final List<String> TOSCA_YML_CSAR_VALID_SUFFIX = Arrays.asList(new String[] { ".yml", ".yaml", ".csar" });
+ public static final String UI_JSON_PAYLOAD_NAME = "payloadName";
+ public static final String ABSTRACT_NODE = "abstact";
+ }
+
+ public enum ResultStatusEnum {
+ ELEMENT_NOT_FOUND, GENERAL_ERROR, OK, INVALID_PROPERTY_DEFAULT_VALUE, INVALID_PROPERTY_TYPE, INVALID_PROPERTY_VALUE, MISSING_ENTRY_SCHEMA_TYPE
+ }
+
+ public enum ToscaElementTypeEnum {
+ BOOLEAN, STRING, MAP, LIST, ALL
+ }
+
+ public enum ToscaTagNamesEnum {
+ DERIVED_FROM("derived_from"), IS_PASSWORD("is_password"),
+ // Properties
+ PROPERTIES("properties"), TYPE("type"), STATUS("status"), ENTRY_SCHEMA("entry_schema"), REQUIRED("required"), DESCRIPTION("description"), DEFAULT_VALUE("default"), VALUE("value"), CONSTRAINTS("constraints"),
+ // Group Types
+ MEMBERS("members"), METADATA("metadata"),
+ // Policy Types
+ TARGETS("targets"),
+ // Capabilities
+ CAPABILITIES("capabilities"), VALID_SOURCE_TYPES("valid_source_types"),
+ // Requirements
+ REQUIREMENTS("requirements"), NODE("node"), RELATIONSHIP("relationship"), CAPABILITY("capability"), INTERFACES("interfaces"),
+ // Heat env Validation
+ PARAMETERS("parameters"),
+ // Import Validations
+ TOSCA_VERSION("tosca_definitions_version"), TOPOLOGY_TEMPLATE("topology_template"), NODE_TYPES("node_types"), OCCURRENCES("occurrences"), NODE_TEMPLATES("node_templates"), GROUPS("groups"), INPUTS("inputs"),
+ // Attributes
+ ATTRIBUTES("attributes"), LABEL("label"), HIDDEN("hidden"), IMMUTABLE("immutable"), GET_INPUT("get_input");
+
+ private String elementName;
+
+ private ToscaTagNamesEnum(String elementName) {
+ this.elementName = elementName;
+ }
+
+ public String getElementName() {
+ return elementName;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void handleElementNameNotFound(String elementName, Object elementValue, ToscaElementTypeEnum elementType, List<Object> returnedList) {
+ if (elementValue instanceof Map) {
+ ImportUtils.findToscaElements((Map<String, Object>) elementValue, elementName, elementType, returnedList);
+ } else if (elementValue instanceof List) {
+ ImportUtils.findAllToscaElementsInList((List<Object>) elementValue, elementName, elementType, returnedList);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void handleElementNameFound(String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList, Object elementValue) {
+
+ if (elementValue instanceof Boolean) {
+ if (elementType == ToscaElementTypeEnum.BOOLEAN || elementType == ToscaElementTypeEnum.ALL) {
+ returnedList.add(elementValue);
+ }
+ }
+
+ else if (elementValue instanceof String) {
+ if (elementType == ToscaElementTypeEnum.STRING || elementType == ToscaElementTypeEnum.ALL) {
+ returnedList.add(elementValue);
+ }
+ } else if (elementValue instanceof Map) {
+ if (elementType == ToscaElementTypeEnum.MAP || elementType == ToscaElementTypeEnum.ALL) {
+ returnedList.add(elementValue);
+ }
+ ImportUtils.findToscaElements((Map<String, Object>) elementValue, elementName, elementType, returnedList);
+
+ } else if (elementValue instanceof List) {
+ if (elementType == ToscaElementTypeEnum.LIST || elementType == ToscaElementTypeEnum.ALL) {
+ returnedList.add(elementValue);
+ }
+ ImportUtils.findAllToscaElementsInList((List<Object>) elementValue, elementName, elementType, returnedList);
+
+ }
+ // For Integer, Double etc...
+ else if (elementType == ToscaElementTypeEnum.ALL) {
+ if (elementValue != null) {
+ returnedList.add(String.valueOf(elementValue));
+ } else {
+ returnedList.add(elementValue);
+ }
+
+ }
+ }
+
+ private static void findAllToscaElementsInList(List<Object> list, String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList) {
+ Iterator<Object> listItr = list.iterator();
+ while (listItr.hasNext()) {
+ Object elementValue = listItr.next();
+ handleElementNameNotFound(elementName, elementValue, elementType, returnedList);
+ }
+
+ }
+
+ public static Either<Object, ResultStatusEnum> findToscaElement(Map<String, Object> toscaJson, ToscaTagNamesEnum elementName, ToscaElementTypeEnum elementType) {
+ List<Object> foundElements = new ArrayList<>();
+ Either<Object, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ ImportUtils.findToscaElements(toscaJson, elementName.getElementName(), elementType, foundElements);
+ if (foundElements.size() > 0) {
+ returnedElement = Either.left(foundElements.get(0));
+ }
+ return returnedElement;
+
+ }
+
+ /**
+ * Recursively searches for all tosca elements with key equals to elementName and value equals to elementType. <br>
+ * Returns Either element with:<br>
+ * List with all value if values found<br>
+ * Or ELEMENT_NOT_FOUND ActionStatus
+ *
+ * @param toscaJson
+ * @param toscaTagName
+ * @return
+ */
+ public static Either<List<Object>, ResultStatusEnum> findToscaElements(Map<String, Object> toscaJson, String elementName, ToscaElementTypeEnum elementType, List<Object> returnedList) {
+ Either<List<Object>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ String skipKey = null;
+ if (toscaJson.containsKey(elementName)) {
+ Object elementValue = toscaJson.get(elementName);
+ handleElementNameFound(elementName, elementType, returnedList, elementValue);
+ skipKey = elementName;
+ }
+
+ Iterator<Entry<String, Object>> keyValItr = toscaJson.entrySet().iterator();
+ while (keyValItr.hasNext()) {
+ Entry<String, Object> keyValEntry = keyValItr.next();
+ if (!String.valueOf(keyValEntry.getKey()).equals(skipKey)) {
+ handleElementNameNotFound(elementName, keyValEntry.getValue(), elementType, returnedList);
+ }
+ }
+
+ if (returnedList.size() > 0) {
+ returnedElement = Either.left(returnedList);
+ }
+
+ return returnedElement;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Either<List<T>, ResultStatusEnum> findFirstToscaListElement(Map<String, Object> toscaJson, ToscaTagNamesEnum toscaTagName) {
+ Either<List<T>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Object, ResultStatusEnum> findFirstToscaElement = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.LIST);
+ if (findFirstToscaElement.isLeft()) {
+ returnedElement = Either.left((List<T>) findFirstToscaElement.left().value());
+ }
+ return returnedElement;
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Either<Map<String, T>, ResultStatusEnum> findFirstToscaMapElement(Map<String, Object> toscaJson, ToscaTagNamesEnum toscaTagName) {
+ Either<Map<String, T>, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Object, ResultStatusEnum> findFirstToscaElement = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.MAP);
+ if (findFirstToscaElement.isLeft()) {
+ returnedElement = Either.left((Map<String, T>) findFirstToscaElement.left().value());
+ }
+ return returnedElement;
+
+ }
+
+ public static Either<String, ResultStatusEnum> findFirstToscaStringElement(Map<String, Object> toscaJson, ToscaTagNamesEnum toscaTagName) {
+ Either<String, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Object, ResultStatusEnum> findFirstToscaElements = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.STRING);
+ if (findFirstToscaElements.isLeft()) {
+ returnedElement = Either.left((String) findFirstToscaElements.left().value());
+ }
+ return returnedElement;
+ }
+
+ /**
+ * searches for first Tosca in Json map (toscaJson) boolean element by name (toscaTagName) returns found element or ELEMENT_NOT_FOUND status
+ *
+ * @param toscaJson
+ * @param toscaTagName
+ * @return
+ */
+ public static Either<String, ResultStatusEnum> findFirstToscaBooleanElement(Map<String, Object> toscaJson, ToscaTagNamesEnum toscaTagName) {
+ Either<String, ResultStatusEnum> returnedElement = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Object, ResultStatusEnum> findFirstToscaElements = findToscaElement(toscaJson, toscaTagName, ToscaElementTypeEnum.BOOLEAN);
+ if (findFirstToscaElements.isLeft()) {
+ returnedElement = Either.left(String.valueOf(findFirstToscaElements.left().value()));
+ }
+ return returnedElement;
+ }
+
+ private static void setPropertyConstraints(Map<String, Object> propertyValue, PropertyDefinition property) {
+ Either<List<Object>, ResultStatusEnum> propertyFieldconstraints = findFirstToscaListElement(propertyValue, ToscaTagNamesEnum.CONSTRAINTS);
+ if (propertyFieldconstraints.isLeft()) {
+ List<Object> jsonConstraintList = propertyFieldconstraints.left().value();
+
+ List<PropertyConstraint> constraintList = new ArrayList<>();
+ Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+ Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
+
+ for (Object constraintJson : jsonConstraintList) {
+ PropertyConstraint propertyConstraint = gson.fromJson(gson.toJson(constraintJson), constraintType);
+ constraintList.add(propertyConstraint);
+ }
+ property.setConstraints(constraintList);
+ }
+ }
+
+ public static PropertyDefinition createModuleProperty(Map<String, Object> propertyValue) {
+
+ PropertyDefinition propertyDef = new PropertyDefinition();
+ ImportUtils.setField(propertyValue, ToscaTagNamesEnum.TYPE, type -> propertyDef.setType(type));
+ ImportUtils.setPropertyFieldRequired(propertyValue, propertyDef);
+ ImportUtils.setField(propertyValue, ToscaTagNamesEnum.DESCRIPTION, desc -> propertyDef.setDescription(desc));
+
+ Either<Object, ResultStatusEnum> findToscaElement = ImportUtils.findToscaElement(propertyValue, ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
+ if (findToscaElement.isLeft()) {
+ String propertyJsonStringValue = getPropertyJsonStringValue(findToscaElement.left().value(), propertyDef.getType());
+ propertyDef.setDefaultValue(propertyJsonStringValue);
+ }
+ ImportUtils.setField(propertyValue, ToscaTagNamesEnum.IS_PASSWORD, pass -> propertyDef.setPassword(Boolean.parseBoolean(pass)));
+ ImportUtils.setField(propertyValue, ToscaTagNamesEnum.STATUS, status -> propertyDef.setStatus(status));
+ ImportUtils.setPropertyScheme(propertyValue, propertyDef);
+ ImportUtils.setPropertyConstraints(propertyValue, propertyDef);
+
+ return propertyDef;
+ }
+
+ public static InputDefinition createModuleInput(Map<String, Object> inputValue) {
+
+ InputDefinition inputDef = new InputDefinition();
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.TYPE, type -> inputDef.setType(type));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.REQUIRED, req -> inputDef.setRequired(Boolean.parseBoolean(req)));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.DESCRIPTION, desc -> inputDef.setDescription(desc));
+
+ Either<Object, ResultStatusEnum> findToscaElement = ImportUtils.findToscaElement(inputValue, ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
+ if (findToscaElement.isLeft()) {
+ String propertyJsonStringValue = getPropertyJsonStringValue(findToscaElement.left().value(), inputDef.getType());
+ inputDef.setDefaultValue(propertyJsonStringValue);
+ }
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.IS_PASSWORD, pass -> inputDef.setPassword(Boolean.parseBoolean(pass)));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.STATUS, status -> inputDef.setStatus(status));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.LABEL, label -> inputDef.setLabel(label));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.HIDDEN, hidden -> inputDef.setHidden(Boolean.parseBoolean(hidden)));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.HIDDEN, immutable -> inputDef.setImmutable(Boolean.parseBoolean(immutable)));
+ ImportUtils.setField(inputValue, ToscaTagNamesEnum.LABEL, label -> inputDef.setLabel(label));
+ ImportUtils.setPropertyScheme(inputValue, inputDef);
+ ImportUtils.setPropertyConstraints(inputValue, inputDef);
+
+ return inputDef;
+ }
+
+ public static AttributeDefinition createModuleAttribute(Map<String, Object> attributeMap) {
+
+ AttributeDefinition attributeDef = new AttributeDefinition();
+ ImportUtils.setField(attributeMap, ToscaTagNamesEnum.TYPE, type -> attributeDef.setType(type));
+ ImportUtils.setField(attributeMap, ToscaTagNamesEnum.DESCRIPTION, desc -> attributeDef.setDescription(desc));
+ ImportUtils.setField(attributeMap, ToscaTagNamesEnum.STATUS, status -> attributeDef.setStatus(status));
+ Either<Object, ResultStatusEnum> eitherDefaultValue = ImportUtils.findToscaElement(attributeMap, ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
+ if (eitherDefaultValue.isLeft()) {
+ String attributeDefaultValue = getPropertyJsonStringValue(eitherDefaultValue.left().value(), attributeDef.getType());
+ attributeDef.setDefaultValue(attributeDefaultValue);
+ }
+ Either<Object, ResultStatusEnum> eitherValue = ImportUtils.findToscaElement(attributeMap, ToscaTagNamesEnum.VALUE, ToscaElementTypeEnum.ALL);
+ if (eitherValue.isLeft()) {
+ String attributeValue = getPropertyJsonStringValue(eitherValue.left().value(), attributeDef.getType());
+ attributeDef.setValue(attributeValue);
+ }
+ ImportUtils.setAttributeScheme(attributeMap, attributeDef);
+ return attributeDef;
+ }
+
+ private static void setPropertyFieldStatus(Map<String, Object> propertyValue, PropertyDefinition propertyDef) {
+ Either<String, ResultStatusEnum> propertyFieldIsStatus = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.STATUS);
+ if (propertyFieldIsStatus.isLeft()) {
+ propertyDef.setStatus(propertyFieldIsStatus.left().value());
+ }
+
+ }
+
+ private static void setAttributeFieldStatus(Map<String, Object> propertyValue, AttributeDefinition propertyDef) {
+ Either<String, ResultStatusEnum> propertyFieldIsStatus = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.STATUS);
+ if (propertyFieldIsStatus.isLeft()) {
+ propertyDef.setStatus(propertyFieldIsStatus.left().value());
+ }
+
+ }
+
+ private static void setPropertyScheme(Map<String, Object> propertyValue, PropertyDefinition propertyDefinition) {
+ Either<SchemaDefinition, ResultStatusEnum> eitherSchema = getSchema(propertyValue);
+ if (eitherSchema.isLeft()) {
+ SchemaDefinition schemaDef = new SchemaDefinition();
+ schemaDef.setProperty(eitherSchema.left().value().getProperty());
+ propertyDefinition.setSchema(schemaDef);
+ }
+
+ }
+
+ private static void setAttributeScheme(Map<String, Object> propertyValue, AttributeDefinition propertyDefinition) {
+ Either<SchemaDefinition, ResultStatusEnum> eitherSchema = getSchema(propertyValue);
+ if (eitherSchema.isLeft()) {
+ SchemaDefinition schemaDef = new SchemaDefinition();
+ schemaDef.setProperty(eitherSchema.left().value().getProperty());
+ propertyDefinition.setSchema(schemaDef);
+ }
+
+ }
+
+ private static Either<SchemaDefinition, ResultStatusEnum> getSchema(Map<String, Object> propertyValue) {
+ Either<SchemaDefinition, ResultStatusEnum> result = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Object, ResultStatusEnum> propertyFieldEntryScheme = findToscaElement(propertyValue, ToscaTagNamesEnum.ENTRY_SCHEMA, ToscaElementTypeEnum.ALL);
+ if (propertyFieldEntryScheme.isLeft()) {
+ if (propertyFieldEntryScheme.left().value() instanceof String) {
+ String schemaType = (String) propertyFieldEntryScheme.left().value();
+ SchemaDefinition schema = new SchemaDefinition();
+ PropertyDefinition schemeProperty = new PropertyDefinition();
+ schemeProperty.setType(schemaType);
+ schema.setProperty(schemeProperty);
+ result = Either.left(schema);
+
+ } else if (propertyFieldEntryScheme.left().value() instanceof Map) {
+ PropertyDefinition schemeProperty = createModuleProperty((Map<String, Object>) propertyFieldEntryScheme.left().value());
+ SchemaDefinition schema = new SchemaDefinition();
+ schema.setProperty(schemeProperty);
+ result = Either.left(schema);
+
+ }
+
+ }
+ return result;
+ }
+
+ private static void setPropertyFieldIsPassword(Map<String, Object> propertyValue, PropertyDefinition dataDefinition) {
+ Either<String, ResultStatusEnum> propertyFieldIsPassword = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.IS_PASSWORD);
+ if (propertyFieldIsPassword.isLeft()) {
+ dataDefinition.setPassword(Boolean.parseBoolean(propertyFieldIsPassword.left().value()));
+ }
+ }
+
+ private static ResultStatusEnum setPropertyFieldDefaultValue(Map<String, Object> propertyValue, PropertyDefinition dataDefinition) {
+ Either<Object, ResultStatusEnum> propertyFieldDefaultValue = findToscaElement(propertyValue, ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
+ Gson gson = GsonFactory.getGson();
+ if (propertyFieldDefaultValue.isLeft()) {
+ Object defaultValue = propertyFieldDefaultValue.left().value();
+ String type = dataDefinition.getType();
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(type);
+ // esofer - supporting customized data types. The validation of the
+ // type will be in the creation of the property.
+ // if(innerToscaType == null){
+ // return ResultStatusEnum.MISSING_ENTRY_SCHEMA_TYPE;
+ // }
+ // customized data types value is represented as json.
+ // Also customized data types which are scalar ones, for example,
+ // data type which derived from integer, their value will be
+ // represented as json.
+ if (innerToscaType == null || innerToscaType.equals(ToscaPropertyType.LIST) || innerToscaType.equals(ToscaPropertyType.MAP)) {
+ String jsonObj = null;
+ if (defaultValue != null) {
+ jsonObj = gson.toJson(defaultValue);
+ }
+
+ dataDefinition.setDefaultValue(jsonObj);
+ } else {
+ dataDefinition.setDefaultValue(String.valueOf(defaultValue));
+ }
+
+ }
+
+ return ResultStatusEnum.OK;
+ }
+
+ private static ResultStatusEnum setAttributeFieldDefaultValue(Map<String, Object> propertyValue, AttributeDefinition dataDefinition) {
+ Either<Object, ResultStatusEnum> propertyFieldDefaultValue = findToscaElement(propertyValue, ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
+ Gson gson = GsonFactory.getGson();
+ if (propertyFieldDefaultValue.isLeft()) {
+ Object defaultValue = propertyFieldDefaultValue.left().value();
+ String type = dataDefinition.getType();
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(type);
+ // esofer - supporting customized data types. The validation of the
+ // type will be in the creation of the property.
+ // if(innerToscaType == null){
+ // return ResultStatusEnum.MISSING_ENTRY_SCHEMA_TYPE;
+ // }
+ // customized data types value is represented as json.
+ // Also customized data types which are scalar ones, for example,
+ // data type which derived from integer, their value will be
+ // represented as json.
+ if (innerToscaType == null || innerToscaType.equals(ToscaPropertyType.LIST) || innerToscaType.equals(ToscaPropertyType.MAP)) {
+ String jsonObj = null;
+ if (defaultValue != null) {
+ jsonObj = gson.toJson(defaultValue);
+ }
+
+ dataDefinition.setDefaultValue(jsonObj);
+ } else {
+ dataDefinition.setDefaultValue(String.valueOf(defaultValue));
+ }
+
+ }
+
+ return ResultStatusEnum.OK;
+ }
+
+ public static void setField(Map<String, Object> toscaJson, ToscaTagNamesEnum tagName, Consumer<String> setter) {
+ Either<String, ResultStatusEnum> fieldStringValue = findFirstToscaStringElement(toscaJson, tagName);
+ if (fieldStringValue.isLeft()) {
+ setter.accept(fieldStringValue.left().value());
+ }
+
+ }
+
+ private static void setPropertyFieldDescription(Map<String, Object> propertyValue, PropertyDefinition dataDefinition) {
+ Either<String, ResultStatusEnum> propertyFieldDescription = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.DESCRIPTION);
+ if (propertyFieldDescription.isLeft()) {
+ dataDefinition.setDescription(propertyFieldDescription.left().value());
+ }
+ }
+
+ private static void setPropertyFieldRequired(Map<String, Object> propertyValue, PropertyDefinition dataDefinition) {
+ Either<String, ResultStatusEnum> propertyFieldRequired = findFirstToscaBooleanElement(propertyValue, ToscaTagNamesEnum.REQUIRED);
+ if (propertyFieldRequired.isLeft()) {
+ dataDefinition.setRequired(Boolean.parseBoolean(propertyFieldRequired.left().value()));
+ }
+ }
+
+ private static void setAttributeFieldType(Map<String, Object> propertyValue, AttributeDefinition dataDefinition) {
+ Either<String, ResultStatusEnum> propertyFieldType = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.TYPE);
+ if (propertyFieldType.isLeft()) {
+ dataDefinition.setType(propertyFieldType.left().value());
+ }
+ }
+
+ private static void setPropertyFieldType(Map<String, Object> propertyValue, PropertyDefinition dataDefinition) {
+ Either<String, ResultStatusEnum> propertyFieldType = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.TYPE);
+ if (propertyFieldType.isLeft()) {
+ dataDefinition.setType(propertyFieldType.left().value());
+ }
+ }
+
+ private static void setAttributeFieldDescription(Map<String, Object> propertyValue, AttributeDefinition dataDefinition) {
+ Either<String, ResultStatusEnum> propertyFieldDescription = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.DESCRIPTION);
+ if (propertyFieldDescription.isLeft()) {
+ dataDefinition.setDescription(propertyFieldDescription.left().value());
+ }
+ }
+
+ public static Either<Map<String, PropertyDefinition>, ResultStatusEnum> getProperties(Map<String, Object> toscaJson) {
+ Function<String, PropertyDefinition> elementGenByName = elementName -> createProperties(elementName);
+ Function<Map<String, Object>, PropertyDefinition> func = map -> createModuleProperty(map);
+
+ return getElements(toscaJson, ToscaTagNamesEnum.PROPERTIES, elementGenByName, func);
+
+ }
+
+ public static Either<Map<String, InputDefinition>, ResultStatusEnum> getInputs(Map<String, Object> toscaJson) {
+ Function<String, InputDefinition> elementGenByName = elementName -> createInputs(elementName);
+ Function<Map<String, Object>, InputDefinition> func = map -> createModuleInput(map);
+
+ return getElements(toscaJson, ToscaTagNamesEnum.INPUTS, elementGenByName, func);
+
+ }
+
+ public static Either<Map<String, AttributeDefinition>, ResultStatusEnum> getAttributes(Map<String, Object> toscaJson) {
+ Function<String, AttributeDefinition> elementGenByName = elementName -> createAttribute(elementName);
+ Function<Map<String, Object>, AttributeDefinition> func = map -> createModuleAttribute(map);
+
+ return getElements(toscaJson, ToscaTagNamesEnum.ATTRIBUTES, elementGenByName, func);
+ }
+
+ public static <ElementDefinition> Either<Map<String, ElementDefinition>, ResultStatusEnum> getElements(Map<String, Object> toscaJson, ToscaTagNamesEnum elementTagName, Function<String, ElementDefinition> elementGenByName,
+ Function<Map<String, Object>, ElementDefinition> func) {
+ Either<Map<String, ElementDefinition>, ResultStatusEnum> eitherResult = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Map<String, Object>, ResultStatusEnum> toscaAttributes = findFirstToscaMapElement(toscaJson, elementTagName);
+ if (toscaAttributes.isLeft()) {
+ Map<String, Object> jsonAttributes = toscaAttributes.left().value();
+ Map<String, ElementDefinition> moduleAttributes = new HashMap<>();
+ Iterator<Entry<String, Object>> propertiesNameValue = jsonAttributes.entrySet().iterator();
+ while (propertiesNameValue.hasNext()) {
+ Entry<String, Object> attributeNameValue = propertiesNameValue.next();
+ if (attributeNameValue.getValue() instanceof Map) {
+ @SuppressWarnings("unchecked")
+ ElementDefinition attribute = func.apply((Map<String, Object>) attributeNameValue.getValue());
+ moduleAttributes.put(String.valueOf(attributeNameValue.getKey()), attribute);
+ } else {
+
+ ElementDefinition element = elementGenByName.apply(String.valueOf(attributeNameValue.getValue()));
+
+ moduleAttributes.put(String.valueOf(attributeNameValue.getKey()), element);
+ }
+
+ }
+
+ if (moduleAttributes.size() > 0) {
+ eitherResult = Either.left(moduleAttributes);
+ }
+
+ }
+ return eitherResult;
+
+ }
+
+ private static AttributeDefinition createAttribute(String name) {
+ AttributeDefinition attribute = new AttributeDefinition();
+
+ attribute.setName(name);
+ return attribute;
+ }
+
+ private static PropertyDefinition createProperties(String name) {
+ PropertyDefinition property = new PropertyDefinition();
+ property.setDefaultValue(name);
+ property.setName(name);
+ return property;
+ }
+
+ private static InputDefinition createInputs(String name) {
+ InputDefinition input = new InputDefinition();
+
+ input.setName(name);
+ return input;
+ }
+
+ public static Either<List<HeatParameterDefinition>, ResultStatusEnum> getHeatParameters(Map<String, Object> heatData, String artifactType) {
+
+ Either<List<HeatParameterDefinition>, ResultStatusEnum> eitherResult = Either.right(ResultStatusEnum.ELEMENT_NOT_FOUND);
+ Either<Map<String, Object>, ResultStatusEnum> toscaProperties = findFirstToscaMapElement(heatData, ToscaTagNamesEnum.PARAMETERS);
+ if (toscaProperties.isLeft()) {
+ Map<String, Object> jsonProperties = toscaProperties.left().value();
+ List<HeatParameterDefinition> moduleProperties = new ArrayList<>();
+ Iterator<Entry<String, Object>> propertiesNameValue = jsonProperties.entrySet().iterator();
+ while (propertiesNameValue.hasNext()) {
+ Entry<String, Object> propertyNameValue = propertiesNameValue.next();
+ if (propertyNameValue.getValue() instanceof Map) {
+ if (!artifactType.equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ @SuppressWarnings("unchecked")
+ Either<HeatParameterDefinition, ResultStatusEnum> propertyStatus = createModuleHeatParameter((Map<String, Object>) propertyNameValue.getValue());
+ if (propertyStatus.isRight()) {
+ return Either.right(propertyStatus.right().value());
+ }
+ HeatParameterDefinition property = propertyStatus.left().value();
+ property.setName(String.valueOf(propertyNameValue.getKey()));
+ moduleProperties.add(property);
+ } else {
+ addHeatParamDefinition(moduleProperties, propertyNameValue, true);
+ }
+ } else {
+ addHeatParamDefinition(moduleProperties, propertyNameValue, false);
+ }
+
+ }
+
+ if (moduleProperties.size() > 0) {
+ eitherResult = Either.left(moduleProperties);
+ }
+
+ }
+ return eitherResult;
+
+ }
+
+ private static void addHeatParamDefinition(List<HeatParameterDefinition> moduleProperties, Entry<String, Object> propertyNameValue, boolean isJson) {
+ HeatParameterDefinition property = new HeatParameterDefinition();
+ Object value = propertyNameValue.getValue();
+ if (value != null) {
+ property.setDefaultValue(isJson ? new Gson().toJson(value).toString() : StringEscapeUtils.escapeJava(String.valueOf(value)));
+ }
+ property.setName(String.valueOf(propertyNameValue.getKey()));
+ moduleProperties.add(property);
+ }
+
+ private static Either<HeatParameterDefinition, ResultStatusEnum> createModuleHeatParameter(Map<String, Object> propertyValue) {
+ HeatParameterDefinition propertyDef = new HeatParameterDefinition();
+ String type;
+ Either<String, ResultStatusEnum> propertyFieldType = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.TYPE);
+ if (propertyFieldType.isLeft()) {
+ type = propertyFieldType.left().value();
+ propertyDef.setType(type);
+ } else {
+ return Either.right(ResultStatusEnum.INVALID_PROPERTY_TYPE);
+ }
+ Either<String, ResultStatusEnum> propertyFieldDescription = findFirstToscaStringElement(propertyValue, ToscaTagNamesEnum.DESCRIPTION);
+ if (propertyFieldDescription.isLeft()) {
+ propertyDef.setDescription(propertyFieldDescription.left().value());
+ }
+
+ Either<Object, ResultStatusEnum> propertyFieldDefaultVal = findToscaElement(propertyValue, ToscaTagNamesEnum.DEFAULT_VALUE, ToscaElementTypeEnum.ALL);
+ if (propertyFieldDefaultVal.isLeft()) {
+ if (propertyFieldDefaultVal.left().value() == null) {
+ return Either.right(ResultStatusEnum.INVALID_PROPERTY_VALUE);
+ }
+ Object value = propertyFieldDefaultVal.left().value();
+ String defaultValue = type.equals(HeatParameterType.JSON.getType()) ? new Gson().toJson(value).toString() : StringEscapeUtils.escapeJava(String.valueOf(value));
+ propertyDef.setDefaultValue(defaultValue);
+ propertyDef.setCurrentValue(defaultValue);
+ }
+
+ return Either.left(propertyDef);
+ }
+
+ public static String getPropertyJsonStringValue(Object value, String type) {
+ Gson gson = new Gson();
+ if (type == null) {
+ return null;
+ }
+ ToscaPropertyType validType = ToscaPropertyType.isValidType(type);
+ if (validType == null || validType.equals(ToscaPropertyType.MAP) || validType.equals(ToscaPropertyType.LIST)) {
+ return gson.toJson(value);
+ }
+ return value.toString();
+ }
+
+ /**
+ * removes from Json map (toscaJson) first element found by name (elementName) note that this method could update the received argument toscaJson
+ *
+ * @param toscaJson
+ * @param elementName
+ */
+ public static void removeElementFromJsonMap(Map<String, Object> toscaJson, String elementName) {
+ for (Entry<String, Object> entry : toscaJson.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (key.equals(elementName)) {
+ toscaJson.remove(elementName);
+ return;
+ } else if (value instanceof Map) {
+ removeElementFromJsonMap((Map<String, Object>) value, elementName);
+ }
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InformationDeployedArtifactsBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InformationDeployedArtifactsBusinessLogic.java
new file mode 100644
index 0000000000..129110e13f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InformationDeployedArtifactsBusinessLogic.java
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.apache.commons.codec.binary.Base64;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.DeploymentArtifactTypeConfig;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+//Pavel
+//currently NOT IN USE - there are no informational deployed artifacts after US601880
+
+@org.springframework.stereotype.Component("informationDeployedArtifactsBusinessLogic")
+public class InformationDeployedArtifactsBusinessLogic {
+
+ /*
+ * private static Logger log = LoggerFactory.getLogger(InformationDeployedArtifactsBusinessLogic.class. getName());
+ *
+ * @javax.annotation.Resource private ArtifactsBusinessLogic artifactBusinessLogic;
+ *
+ * public boolean isInformationDeployedArtifact(ArtifactDefinition artifactInfo) { log.debug("checking if artifact {} is informationalDeployable", artifactInfo.getArtifactName()); boolean informationDeployedArtifact = false; Map<String,
+ * DeploymentArtifactTypeConfig> resourceInformationalDeployedArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration() .getResourceInformationalDeployedArtifacts(); if( resourceInformationalDeployedArtifacts != null &&
+ * resourceInformationalDeployedArtifacts.containsKey(artifactInfo. getArtifactType())){ if( artifactInfo.getArtifactGroupType() == ArtifactGroupTypeEnum.INFORMATIONAL && artifactInfo.checkEsIdExist()){ informationDeployedArtifact = true;
+ *
+ * } } String message = (informationDeployedArtifact) ? "artifact {} is informationalDeployable" : "artifact {} is not informationalDeployable"; log.debug(message, artifactInfo.getArtifactName()); return informationDeployedArtifact; }
+ *
+ * public Either<Boolean, ResponseFormat> validateArtifact(boolean isCreate, ArtifactDefinition artifactInfo, Component parent, NodeTypeEnum parentType) {
+ *
+ * log.debug("checking if informationalDeployable artifact {} is valid ", artifactInfo.getArtifactName()); Wrapper<ResponseFormat> responseFormatWrapper = new Wrapper<ResponseFormat>(); Map<String, DeploymentArtifactTypeConfig>
+ * resourceInformationalDeployedArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration(). getResourceInformationalDeployedArtifacts();
+ *
+ *
+ * artifactBusinessLogic.validateArtifactTypeExists(responseFormatWrapper, artifactInfo); if( responseFormatWrapper.isEmpty() && isCreate ){ artifactBusinessLogic.validateSingleArtifactType(responseFormatWrapper,
+ * ArtifactTypeEnum.findType(artifactInfo.getArtifactType()), parent, parentType); } if( responseFormatWrapper.isEmpty() ){ ArtifactTypeEnum artifactType = ArtifactTypeEnum.findType(artifactInfo.getArtifactType());
+ * artifactBusinessLogic.validateFileExtension(responseFormatWrapper, () -> resourceInformationalDeployedArtifacts.get(artifactInfo.getArtifactType() ), artifactInfo, parentType, artifactType); }
+ *
+ * if( responseFormatWrapper.isEmpty() ){ validatePayloadContent(responseFormatWrapper, artifactInfo); }
+ *
+ * Either<Boolean, ResponseFormat> response; if( responseFormatWrapper.isEmpty() ){ response = Either.left(true); } else{ response = Either.right(responseFormatWrapper.getInnerElement()); }
+ *
+ * String message = (response.isLeft()) ? "informationalDeployable artifact {} is valid" : "informationalDeployable artifact {} is not valid"; log.debug(message, artifactInfo.getArtifactName()); return response; }
+ *
+ * private void validatePayloadContent(Wrapper<ResponseFormat> responseFormatWrapper, ArtifactDefinition artifactToVerify) { ArtifactTypeEnum artifactType = ArtifactTypeEnum.findType(artifactToVerify.getArtifactType()); if( artifactType ==
+ * ArtifactTypeEnum.YANG_XML ){ String rawPayloadData = artifactToVerify.getPayloadData(); String xmlToParse = Base64.isBase64(rawPayloadData) ? new String(org.apache.commons.codec.binary.Base64.decodeBase64(rawPayloadData )) : rawPayloadData;
+ *
+ * boolean isXmlValid = artifactBusinessLogic.isValidXml(xmlToParse.getBytes()); if( !isXmlValid ){ ResponseFormat responseFormat = ResponseFormatManager.getInstance().getResponseFormat(ActionStatus. INVALID_XML,
+ * artifactToVerify.getArtifactType()); responseFormatWrapper.setInnerElement(responseFormat); log.debug("Xml is not valid for artifact : {}", artifactToVerify.getArtifactName()); } } }
+ *
+ *
+ *
+ *
+ * public List<ArtifactDefinition> getInformationalDeployedArtifactsForResourceInstance( ComponentInstance resourceInstance ) { String resourceUid = resourceInstance.getComponentUid(); String resourceName = resourceInstance.getComponentName();
+ * return getInformationalDeployedArtifactsForResource(resourceUid, resourceName); }
+ *
+ * private List<ArtifactDefinition> getInformationalDeployedArtifactsForResource( String resourceUid, String resourceName) { List<ArtifactDefinition> informationalDeployedArtifacts = new ArrayList<ArtifactDefinition>(); log.
+ * debug("checking if informationalDeployable artifacts exist for resource {} " , resourceName); Map<String, DeploymentArtifactTypeConfig> resourceInformationalDeployedArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration()
+ * .getResourceInformationalDeployedArtifacts();
+ *
+ *
+ * Either<Map<String, ArtifactDefinition>, StorageOperationStatus> eitherArtifacts = artifactBusinessLogic.getArtifacts(resourceUid, NodeTypeEnum.Resource, false, ArtifactGroupTypeEnum.INFORMATIONAL);
+ *
+ * if( eitherArtifacts.isLeft() && resourceInformationalDeployedArtifacts != null ){ Predicate<ArtifactDefinition> predicate = p -> resourceInformationalDeployedArtifacts.containsKey(p.getArtifactType()) && p.checkEsIdExist() &&
+ * p.getArtifactGroupType() == ArtifactGroupTypeEnum.INFORMATIONAL; informationalDeployedArtifacts = eitherArtifacts.left().value().values().stream().filter( predicate ).collect(Collectors.toList()); }
+ *
+ * if( informationalDeployedArtifacts.isEmpty() ){ log.debug("no informationalDeployable artifacts exist for resource {} ", resourceName); } else{ log. debug("informationalDeployable artifacts found for resource {} are :{}", resourceName,
+ * informationalDeployedArtifacts.toString()); }
+ *
+ *
+ * return informationalDeployedArtifacts; }
+ *
+ * public List<ArtifactDefinition> getAllDeployableArtifacts( Resource resource ){ List<ArtifactDefinition> merged = new ArrayList<ArtifactDefinition>();
+ *
+ * List<ArtifactDefinition> deploymentArtifacts = artifactBusinessLogic.getDeploymentArtifacts(resource, NodeTypeEnum.Resource); merged.addAll(deploymentArtifacts);
+ *
+ * List<ArtifactDefinition> informationalDeployedArtifactsForResource = getInformationalDeployedArtifactsForResource( resource.getUniqueId(), resource.getName()); merged.addAll(informationalDeployedArtifactsForResource);
+ *
+ * return merged; }
+ */
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogic.java
new file mode 100644
index 0000000000..7d0624440e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InputsBusinessLogic.java
@@ -0,0 +1,526 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ComponentInstInputsMap;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.InputsOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.resources.data.InputsData;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("inputsBusinessLogic")
+public class InputsBusinessLogic extends BaseBusinessLogic {
+
+ private static final String CREATE_INPUT = "CreateInput";
+
+ private static Logger log = LoggerFactory.getLogger(InputsBusinessLogic.class.getName());
+
+ @Autowired
+ private IResourceOperation resourceOperation = null;
+
+ @javax.annotation.Resource
+ private InputsOperation inputsOperation = null;
+
+ @javax.annotation.Resource
+ private PropertyOperation propertyOperation = null;
+
+ @Autowired
+ private ComponentsUtils componentsUtils;
+
+ /**
+ * associate inputs to a given component with paging
+ *
+ * @param componentId
+ * @param userId
+ * @param fromId
+ * @param amount
+ * @return
+ */
+ public Either<List<InputDefinition>, ResponseFormat> getInputs(String userId, String componentId, String fromName,
+ int amount) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Inputs", false);
+
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<List<InputDefinition>, StorageOperationStatus> inputsEitherRes = inputsOperation
+ .getInputsOfComponent(componentId, fromName, amount);
+ if (inputsEitherRes.isRight()) {
+ if (inputsEitherRes.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ return Either.left(new ArrayList<InputDefinition>());
+ }
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(inputsEitherRes.right().value());
+ log.debug("Failed to get inputs under component {}, error: {}", componentId, actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+
+ }
+
+ return Either.left(inputsEitherRes.left().value());
+
+ }
+
+ /**
+ * associate properties to a given component instance input
+ *
+ * @param instanceId
+ * @param userId
+ * @param inputId
+ * @return
+ */
+
+ public Either<List<ComponentInstanceProperty>, ResponseFormat> getComponentInstancePropertiesByInputId(
+ String userId, String instanceId, String inputId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Properties by input", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<List<ComponentInstanceProperty>, StorageOperationStatus> propertiesEitherRes = inputsOperation
+ .getComponentInstancePropertiesByInputId(instanceId, inputId);
+ if (propertiesEitherRes.isRight()) {
+
+ if (propertiesEitherRes.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ return Either.left(new ArrayList<ComponentInstanceProperty>());
+ }
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(propertiesEitherRes.right().value());
+ log.debug("Failed to get inputs under component {}, error: {}", instanceId, actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+
+ }
+
+ return Either.left(propertiesEitherRes.left().value());
+
+ }
+
+ public Either<List<ComponentInstanceInput>, ResponseFormat> getInputsForComponentInput(String userId,
+ String componentId, String inputId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Inputs by input", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<List<ComponentInstanceInput>, StorageOperationStatus> inputsEitherRes = inputsOperation
+ .getComponentInstanceInputsByInputId(componentId, inputId);
+ if (inputsEitherRes.isRight()) {
+
+ if (inputsEitherRes.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ return Either.left(new ArrayList<ComponentInstanceInput>());
+ }
+
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(inputsEitherRes.right().value());
+ log.debug("Failed to get inputs for input under component {}, error: {}", componentId, actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+
+ }
+
+ return Either.left(inputsEitherRes.left().value());
+
+ }
+
+ public Either<List<InputDefinition>, ResponseFormat> createMultipleInputs(String userId, String componentId,
+ ComponentTypeEnum componentType, ComponentInstInputsMap componentInstInputsMapUi, boolean shouldLockComp,
+ boolean inTransaction) {
+
+ Either<List<InputDefinition>, ResponseFormat> result = null;
+ org.openecomp.sdc.be.model.Component component = null;
+ try {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Properties by input", false);
+
+ if (resp.isRight()) {
+ result = Either.right(resp.right().value());
+ return result;
+ }
+
+ User user = resp.left().value();
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreGroups(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ componentParametersView.setIgnoreUsers(false);
+
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(
+ componentId, componentType, componentParametersView, userId, null, user);
+
+ if (validateComponent.isRight()) {
+ result = Either.right(validateComponent.right().value());
+ return result;
+ }
+ component = validateComponent.left().value();
+
+ if (shouldLockComp) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, CREATE_INPUT);
+ if (lockComponent.isRight()) {
+ result = Either.right(lockComponent.right().value());
+ return result;
+ }
+ }
+
+ Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, userId);
+ if (canWork.isRight()) {
+ result = Either.right(canWork.right().value());
+ return result;
+ }
+
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(
+ applicationDataTypeCache);
+ if (allDataTypes.isRight()) {
+ result = Either.right(allDataTypes.right().value());
+ return result;
+ }
+
+ Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
+
+ Either<List<InputDefinition>, StorageOperationStatus> createInputsResult = this.inputsOperation
+ .addInputsToComponent(componentId, componentType.getNodeType(), componentInstInputsMapUi,
+ dataTypes);
+
+ if (createInputsResult.isRight()) {
+ ActionStatus actionStatus = componentsUtils
+ .convertFromStorageResponse(createInputsResult.right().value());
+ log.debug("Failed to create inputs under component {}, error: {}", componentId, actionStatus.name());
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ return result;
+
+ }
+ result = Either.left(createInputsResult.left().value());
+
+ return result;
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+ // unlock resource
+ if (shouldLockComp && component != null) {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+
+ }
+
+ }
+
+ public Either<List<InputDefinition>, ResponseFormat> createInputs(String componentId, String userId,
+ ComponentTypeEnum componentType, List<InputDefinition> inputsDefinitions, boolean shouldLockComp,
+ boolean inTransaction) {
+
+ Either<List<InputDefinition>, ResponseFormat> result = null;
+
+ org.openecomp.sdc.be.model.Component component = null;
+ try {
+
+ if (inputsDefinitions != null && false == inputsDefinitions.isEmpty()) {
+
+ if (shouldLockComp == true && inTransaction == true) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("createGroups",
+ "Cannot lock component since we are inside a transaction", ErrorSeverity.ERROR);
+ // Cannot lock component since we are in a middle of another
+ // transaction.
+ ActionStatus actionStatus = ActionStatus.INVALID_CONTENT;
+ result = Either.right(componentsUtils.getResponseFormat(actionStatus));
+ return result;
+ }
+
+ Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, CREATE_INPUT, true);
+ if (validateUserExists.isRight()) {
+ result = Either.right(validateUserExists.right().value());
+ return result;
+ }
+
+ User user = validateUserExists.left().value();
+
+ Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(
+ componentId, componentType, inTransaction, false);
+ if (validateComponent.isRight()) {
+ result = Either.right(validateComponent.right().value());
+ return result;
+ }
+ component = validateComponent.left().value();
+
+ if (shouldLockComp) {
+ Either<Boolean, ResponseFormat> lockComponent = lockComponent(component, CREATE_INPUT);
+ if (lockComponent.isRight()) {
+ return Either.right(lockComponent.right().value());
+ }
+ }
+
+ Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, userId);
+ if (canWork.isRight()) {
+ result = Either.right(canWork.right().value());
+ return result;
+ }
+
+ result = createInputsInGraph(inputsDefinitions, component, user, inTransaction);
+ }
+
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+ // unlock resource
+ if (shouldLockComp && component != null) {
+ graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
+ }
+
+ }
+
+ }
+
+ public Either<List<InputDefinition>, ResponseFormat> createInputsInGraph(List<InputDefinition> inputsDefinitions,
+ org.openecomp.sdc.be.model.Component component, User user, boolean inTransaction) {
+ Either<List<InputDefinition>, ResponseFormat> result;
+ List<InputDefinition> inputs = new ArrayList<InputDefinition>();
+ for (InputDefinition inputDefinition : inputsDefinitions) {
+ // String resourceId, String inputName, InputDefinition
+ // newInputDefinition, String userId, boolean inTransaction
+ Either<InputDefinition, ResponseFormat> createInput = createInput(component, user,
+ component.getComponentType(), inputDefinition.getName(), inputDefinition, inTransaction);
+ if (createInput.isRight()) {
+ log.debug("Failed to create group {}.", createInput);
+ result = Either.right(createInput.right().value());
+ return result;
+ }
+ InputDefinition createdGroup = createInput.left().value();
+ inputs.add(createdGroup);
+ }
+ result = Either.left(inputs);
+ return result;
+ }
+
+ /**
+ * Delete input from service
+ *
+ * @param component
+ * @param user
+ * @param componentType
+ * @param inputId
+ * @param inTransaction
+ * @return
+ */
+ public Either<InputDefinition, ResponseFormat> deleteInput(String componentType, String componentId, String userId, String inputId, boolean inTransaction) {
+
+ if (log.isDebugEnabled())
+ log.debug("Going to delete input id: {}", inputId);
+
+ // Validate user (exists)
+ Either<User, ResponseFormat> userEither = validateUserExists(userId, "Delete input", true);
+ if (userEither.isRight()) {
+ return Either.right(userEither.right().value());
+ }
+
+ // Get component using componentType, componentId
+ Either<org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentEither = serviceOperation.getComponent(componentId, true);
+ if (componentEither.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(componentEither.right().value())));
+ }
+ org.openecomp.sdc.be.model.Component component = componentEither.left().value();
+
+ // Validate inputId is child of the component
+ // And get the inputDefinition for the response
+ InputDefinition inputDefinition = null;
+ Optional<InputDefinition> optionalInput = component.getInputs().stream().
+ // filter by ID
+ filter(input -> input.getUniqueId().equals(inputId)).
+ // Get the input
+ findAny();
+ if (optionalInput.isPresent()) {
+ inputDefinition = optionalInput.get();
+ } else {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INPUT_IS_NOT_CHILD_OF_COMPONENT, inputId, componentId));
+ }
+
+ // Lock component
+ Either<Boolean, ResponseFormat> lockResultEither = lockComponent(componentId, component, "deleteInput");
+ if (lockResultEither.isRight()) {
+ ResponseFormat responseFormat = lockResultEither.right().value();
+ return Either.right(responseFormat);
+ }
+
+ // Delete input operations
+ Either<String, StorageOperationStatus> deleteEither = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ try {
+ deleteEither = inputsOperation.deleteInput(inputId);
+ if (deleteEither.isRight()){
+ log.debug("Component id: {} delete input id: {} failed", componentId, inputId);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(deleteEither.right().value()), component.getName()));
+ }
+ return Either.left(inputDefinition);
+ } finally {
+ if (deleteEither.isRight()) {
+ log.debug("Component id: {} delete input id: {} failed", componentId, inputId);
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Component id: {} delete input id: {} success", componentId, inputId);
+ titanGenericDao.commit();
+ }
+ unlockComponent(deleteEither, component);
+ }
+ }
+
+ /**
+ * Create new property on resource in graph
+ *
+ * @param resourceId
+ * @param propertyName
+ * @param newPropertyDefinition
+ * @param userId
+ * @return Either<PropertyDefinition, ActionStatus>
+ */
+
+ private Either<InputDefinition, ResponseFormat> createInput(org.openecomp.sdc.be.model.Component component,
+ User user, ComponentTypeEnum componentType, String inputName, InputDefinition newInputDefinition,
+ boolean inTransaction) {
+
+ Either<InputDefinition, ResponseFormat> result = null;
+ if (log.isDebugEnabled())
+ log.debug("Going to create input {}", newInputDefinition);
+
+ try {
+
+ // verify property not exist in resource
+ List<InputDefinition> resourceProperties = component.getInputs();
+
+ if (resourceProperties != null) {
+ if (inputsOperation.isInputExist(resourceProperties, component.getUniqueId(), inputName)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, ""));
+ }
+ }
+
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(
+ applicationDataTypeCache);
+ if (allDataTypes.isRight()) {
+ return Either.right(allDataTypes.right().value());
+ }
+
+ Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
+
+ // validate input default values
+ Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newInputDefinition,
+ dataTypes);
+ if (defaultValuesValidation.isRight()) {
+ return Either.right(defaultValuesValidation.right().value());
+ }
+ // convert property
+ ToscaPropertyType type = getType(newInputDefinition.getType());
+ PropertyValueConverter converter = type.getConverter();
+ // get inner type
+ String innerType = null;
+ if (newInputDefinition != null) {
+ SchemaDefinition schema = newInputDefinition.getSchema();
+ if (schema != null) {
+ PropertyDataDefinition prop = schema.getProperty();
+ if (prop != null) {
+ innerType = prop.getType();
+ }
+ }
+ String convertedValue = null;
+ if (newInputDefinition.getDefaultValue() != null) {
+ convertedValue = converter.convert(newInputDefinition.getDefaultValue(), innerType,
+ allDataTypes.left().value());
+ newInputDefinition.setDefaultValue(convertedValue);
+ }
+ }
+
+ // add the new property to resource on graph
+ // need to get StorageOpaerationStatus and convert to ActionStatus
+ // from componentsUtils
+ Either<InputsData, StorageOperationStatus> either = inputsOperation.addInput(inputName, newInputDefinition,
+ component.getUniqueId(), componentType.getNodeType());
+ if (either.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(
+ componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
+ }
+ // @TODO commit
+ // inputsOperation.getTitanGenericDao().commit();
+ InputDefinition createdInputyDefinition = inputsOperation
+ .convertInputDataToInputDefinition(either.left().value());
+ createdInputyDefinition.setName(inputName);
+ createdInputyDefinition.setParentUniqueId(component.getUniqueId());
+
+ return Either.left(createdInputyDefinition);
+
+ } finally {
+
+ if (false == inTransaction) {
+
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on create group.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on create group.");
+ titanGenericDao.commit();
+ }
+
+ }
+
+ }
+
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java
new file mode 100644
index 0000000000..780980ba08
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManager.java
@@ -0,0 +1,124 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("interfaceLifecycleTypeImportManager")
+public class InterfaceLifecycleTypeImportManager {
+
+ @Resource
+ private IInterfaceLifecycleOperation interfaceLifecycleOperation;
+
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Resource
+ private CommonImportManager commonImportManager;
+
+ private static Logger log = LoggerFactory.getLogger(InterfaceLifecycleTypeImportManager.class.getName());
+
+ public Either<List<InterfaceDefinition>, ResponseFormat> createLifecycleTypes(String interfaceLifecycleTypesYml) {
+
+ Either<List<InterfaceDefinition>, ActionStatus> interfaces = createLifecyclyTypeFromYml(interfaceLifecycleTypesYml);
+ if (interfaces.isRight()) {
+ ActionStatus status = interfaces.right().value();
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByGroupType(status, null);
+ return Either.right(responseFormat);
+ }
+ return createInterfacesByDao(interfaces.left().value());
+
+ }
+
+ private Either<List<InterfaceDefinition>, ActionStatus> createLifecyclyTypeFromYml(String interfaceLifecycleTypesYml) {
+ return commonImportManager.createElementTypesFromYml(interfaceLifecycleTypesYml, (lifecycleTypeName, lifecycleTypeJsonData) -> createLifecycleType(lifecycleTypeName, lifecycleTypeJsonData));
+
+ }
+
+ private Either<List<InterfaceDefinition>, ResponseFormat> createInterfacesByDao(List<InterfaceDefinition> interfacesToCreate) {
+ List<InterfaceDefinition> createdInterfaces = new ArrayList<>();
+ Either<List<InterfaceDefinition>, ResponseFormat> eitherResult = Either.left(createdInterfaces);
+ Iterator<InterfaceDefinition> interfaceItr = interfacesToCreate.iterator();
+ boolean stopDao = false;
+ while (interfaceItr.hasNext() && !stopDao) {
+ InterfaceDefinition interfaceDef = interfaceItr.next();
+
+ log.info("send interfaceDefinition {} to dao for create", interfaceDef.getType());
+
+ Either<InterfaceDefinition, StorageOperationStatus> dataModelResponse = interfaceLifecycleOperation.createInterfaceType(interfaceDef);
+ if (dataModelResponse.isRight()) {
+ log.info("failed to create interface : {} error: {}", interfaceDef.getType(), dataModelResponse.right().value().name());
+ if (dataModelResponse.right().value() != StorageOperationStatus.SCHEMA_VIOLATION) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponseForLifecycleType(dataModelResponse.right().value()), interfaceDef.getType());
+ eitherResult = Either.right(responseFormat);
+ stopDao = true;
+ }
+
+ } else {
+ createdInterfaces.add(dataModelResponse.left().value());
+ }
+ if (!interfaceItr.hasNext()) {
+ log.info("lifecycle types were created successfully!!!");
+ }
+ }
+ return eitherResult;
+ }
+
+ private InterfaceDefinition createLifecycleType(String interfaceDefinition, Map<String, Object> toscaJson) {
+ InterfaceDefinition interfaceDef = new InterfaceDefinition();
+ interfaceDef.setType(interfaceDefinition);
+
+ Map<String, Operation> operations = new HashMap<String, Operation>();
+
+ for (Map.Entry<String, Object> entry : toscaJson.entrySet()) {
+ Operation operation = new Operation();
+ Map<String, Object> opProp = (Map<String, Object>) entry.getValue();
+
+ operation.setDescription((String) opProp.get("description"));
+ operations.put(entry.getKey(), operation);
+ }
+ interfaceDef.setOperations(operations);
+ return interfaceDef;
+ }
+
+ public static void setLog(Logger log) {
+ InterfaceLifecycleTypeImportManager.log = log;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/MonitoringBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/MonitoringBusinessLogic.java
new file mode 100644
index 0000000000..e9708f50d5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/MonitoringBusinessLogic.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.impl.MonitoringDao;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.common.monitoring.MonitoringEvent;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("monitoringBusinessLogic")
+public class MonitoringBusinessLogic {
+
+ private static Logger log = LoggerFactory.getLogger(MonitoringBusinessLogic.class.getName());
+
+ @javax.annotation.Resource
+ private MonitoringDao monitoringDao;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ public Either<Boolean, ResponseFormat> logMonitoringEvent(MonitoringEvent monitoringEvent) {
+ if (monitoringDao == null) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ ActionStatus status = monitoringDao.addRecord(monitoringEvent);
+ if (!status.equals(ActionStatus.OK)) {
+ log.warn("Failed to persist monitoring event: {}", status.name());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ return Either.left(true);
+ }
+
+ public String getEsHost() {
+
+ String res = monitoringDao.getEsHost();
+ res = res.replaceAll("[\\[\\]]", "");
+ res = res.split(",")[0];
+ res = res.replaceAll("[']", "");
+ res = res.split(":")[0];
+ return res;
+ }
+
+ public String getEsPort() {
+ return monitoringDao.getEsPort();
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java
new file mode 100644
index 0000000000..1e0670daa2
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PolicyTypeImportManager.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
+import org.openecomp.sdc.be.model.operations.api.IPolicyTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("policyTypeImportManager")
+public class PolicyTypeImportManager {
+
+ @Resource
+ private PropertyOperation propertyOperation;
+ @Resource
+ private IPolicyTypeOperation policyTypeOperation;
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Resource
+ private IResourceOperation resourceOperation;
+ @Autowired
+ protected IGroupOperation groupOperation;
+
+ @Resource
+ private CommonImportManager commonImportManager;
+
+ public Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat> createPolicyTypes(String groupTypesYml) {
+ return commonImportManager.createElementTypes(groupTypesYml, elementTypeYml -> createPolicyTypesFromYml(elementTypeYml), groupTypesList -> createPolicyTypesByDao(groupTypesList), ElementTypeEnum.PolicyType);
+ }
+
+ private Either<List<PolicyTypeDefinition>, ActionStatus> createPolicyTypesFromYml(String policyTypesYml) {
+
+ return commonImportManager.createElementTypesFromYml(policyTypesYml, (policyTypeName, groupTypeJsonData) -> createPolicyType(policyTypeName, groupTypeJsonData));
+ }
+
+ private Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat> createPolicyTypesByDao(List<PolicyTypeDefinition> policyTypesToCreate) {
+ return commonImportManager.createElementTypesByDao(policyTypesToCreate, policyType -> validatePolicyType(policyType), policyType -> new ImmutablePair<>(ElementTypeEnum.PolicyType, policyType.getType()),
+ policyTypeName -> policyTypeOperation.getLatestPolicyTypeByType(policyTypeName), policyType -> policyTypeOperation.addPolicyType(policyType), null);
+ }
+
+ private Either<ActionStatus, ResponseFormat> validatePolicyType(PolicyTypeDefinition policyType) {
+ Either<ActionStatus, ResponseFormat> result = Either.left(ActionStatus.OK);
+ if (policyType.getTargets() != null) {
+ if (policyType.getTargets().isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.TARGETS_EMPTY, policyType.getType());
+ result = Either.right(responseFormat);
+ }
+ if (result.isLeft()) {
+ for (String targetName : policyType.getTargets()) {
+ boolean isValid = resourceOperation.getLatestByToscaResourceName(targetName, false).isLeft();
+ if (!isValid) {
+ isValid = groupOperation.isGroupExist(targetName, false);
+ }
+ if (!isValid) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.TARGETS_NON_VALID, policyType.getType(), targetName);
+ result = Either.right(responseFormat);
+ break;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private PolicyTypeDefinition createPolicyType(String groupTypeName, Map<String, Object> toscaJson) {
+
+ PolicyTypeDefinition policyType = new PolicyTypeDefinition();
+
+ if (toscaJson != null) {
+ // Description
+ final Consumer<String> descriptionSetter = description -> policyType.setDescription(description);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DESCRIPTION.getElementName(), descriptionSetter);
+ // Derived From
+ final Consumer<String> derivedFromSetter = derivedFrom -> policyType.setDerivedFrom(derivedFrom);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DERIVED_FROM.getElementName(), derivedFromSetter);
+ // Properties
+ commonImportManager.setProperties(toscaJson, (values) -> policyType.setProperties(values));
+ // Metadata
+ final Consumer<Map<String, String>> metadataSetter = metadata -> policyType.setMetadata(metadata);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.METADATA.getElementName(), metadataSetter);
+ // Targets
+ final Consumer<List<String>> targetsSetter = targets -> policyType.setTargets(targets);
+ commonImportManager.setField(toscaJson, ToscaTagNamesEnum.TARGETS.getElementName(), targetsSetter);
+
+ policyType.setType(groupTypeName);
+
+ policyType.setHighestVersion(true);
+
+ policyType.setVersion(ImportUtils.Constants.FIRST_CERTIFIED_VERSION_VERSION);
+ }
+ return policyType;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductBusinessLogic.java
new file mode 100644
index 0000000000..3a54513b0b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductBusinessLogic.java
@@ -0,0 +1,887 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ProductOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("productBusinessLogic")
+public class ProductBusinessLogic extends ComponentBusinessLogic {
+
+ private static final String PRODUCT_FULL_NAME = "full";
+ private static final String PRODUCT_ABBREVIATED_NAME = "abbreviated";
+ private static Logger log = LoggerFactory.getLogger(ProductBusinessLogic.class.getName());
+ private static final String INITIAL_VERSION = "0.1";
+ private static List<Role> creationRoles;
+ private static List<Role> updateRoles;
+ private static List<Role> contactsRoles;
+
+ public ProductBusinessLogic() {
+ creationRoles = new ArrayList<>();
+ updateRoles = new ArrayList<>();
+ contactsRoles = new ArrayList<>();
+
+ // only PM is allowed to create/update products
+ creationRoles.add(Role.PRODUCT_MANAGER);
+ updateRoles.add(Role.PRODUCT_MANAGER);
+ // Only PM is allowed to be product contacts
+ contactsRoles.add(Role.PRODUCT_MANAGER);
+ }
+
+ @Autowired
+ private IElementOperation elementDao;
+
+ @Autowired
+ private ProductComponentInstanceBusinessLogic productComponentInstanceBusinessLogic;
+
+ @Autowired
+ private ICacheMangerOperation cacheManagerOperation;
+
+ public Either<Product, ResponseFormat> createProduct(Product product, User user) {
+ AuditingActionEnum actionEnum = AuditingActionEnum.CREATE_RESOURCE;
+ ComponentTypeEnum typeEnum = ComponentTypeEnum.PRODUCT;
+
+ // validate user - should be first to get the maximum auditing info in
+ // case of subsequent failures
+ log.debug("get user from DB");
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "Create Product", product, actionEnum, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ // validate user role
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, product, creationRoles, actionEnum, null);
+ if (validateRes.isRight()) {
+ return Either.right(validateRes.right().value());
+ }
+
+ if (product == null) {
+ log.debug("Invalid product json. Check product servlet log for createProduct entry params");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, Constants.EMPTY_STRING, Constants.EMPTY_STRING, actionEnum, typeEnum);
+ return Either.right(responseFormat);
+ }
+
+ // warn about non-updatable fields
+ checkUnupdatableProductFields(product);
+
+ Either<Product, ResponseFormat> validateProductResponse = validateProductBeforeCreate(product, user, actionEnum);
+ if (validateProductResponse.isRight()) {
+ return Either.right(validateProductResponse.right().value());
+ }
+
+ log.debug("send product {} to dao for create", product.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+
+ Either<Boolean, ResponseFormat> lockResult = lockComponentByName(product.getSystemName(), product, "Create Product");
+ if (lockResult.isRight()) {
+ ResponseFormat responseFormat = lockResult.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ log.debug("Product name locked is {}, status = {}", product.getSystemName(), lockResult);
+
+ try {
+ Either<Product, StorageOperationStatus> createProductEither = productOperation.createProduct(product);
+
+ if (createProductEither.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByComponent(componentsUtils.convertFromStorageResponse(createProductEither.right().value()), product, typeEnum);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, Constants.EMPTY_STRING, Constants.EMPTY_STRING, actionEnum, typeEnum);
+ return Either.right(responseFormat);
+ }
+
+ log.debug("Product created successfully");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, Constants.EMPTY_STRING, Constants.EMPTY_STRING, actionEnum, typeEnum);
+
+ // //Add product to cache
+ Product createdProduct = createProductEither.left().value();
+ // cacheManagerOperation.updateComponentInCache(createdProduct.getUniqueId(),
+ // createdProduct.getLastUpdateDate(), NodeTypeEnum.Product);
+
+ return Either.left(createdProduct);
+
+ } finally {
+ graphLockOperation.unlockComponentByName(product.getSystemName(), product.getUniqueId(), NodeTypeEnum.Product);
+ }
+
+ }
+
+ private void checkUnupdatableProductFields(Product product) {
+ checkComponentFieldsForOverrideAttempt(product);
+ if (product.getNormalizedName() != null) {
+ log.info("NormalizedName cannot be defined by user. This field will be overridden by the application");
+ }
+ }
+
+ private Either<Product, ResponseFormat> validateProductBeforeCreate(Product product, User user, AuditingActionEnum actionEnum) {
+
+ Either<Boolean, ResponseFormat> validateProductFields = validateProductFieldsBeforeCreate(user, product, actionEnum);
+ if (validateProductFields.isRight()) {
+ return Either.right(validateProductFields.right().value());
+ }
+
+ if (product.getIsActive() == null) {
+ log.debug("no isActive value was provided, setting to default: false");
+ product.setIsActive(false);
+ }
+
+ product.setCreatorUserId(user.getUserId());
+
+ // enrich object
+ log.debug("enrich product with version and state");
+ product.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ product.setVersion(INITIAL_VERSION);
+
+ // Generate invariant UUID - must be here and not in operation since it
+ // should stay constant during clone
+ String invariantUUID = UniqueIdBuilder.buildInvariantUUID();
+ product.setInvariantUUID(invariantUUID);
+
+ return Either.left(product);
+ }
+
+ private Either<Boolean, ResponseFormat> validateProductFieldsBeforeCreate(User user, Product product, AuditingActionEnum actionEnum) {
+
+ // To be removed in 1607
+ // See below
+ String oldName = product.getName();
+
+ Either<Boolean, ResponseFormat> componentNameValidation = validateProductNameAndCleanup(user, product, actionEnum);
+ if (componentNameValidation.isRight()) {
+ return componentNameValidation;
+ }
+
+ Either<Boolean, ResponseFormat> componentNameUniquenessValidation = validateComponentNameUnique(user, product, actionEnum);
+ if (componentNameUniquenessValidation.isRight()) {
+ return componentNameUniquenessValidation;
+ }
+
+ // To be removed in 1607 and replaced with generic
+ // validateTagsListAndRemoveDuplicates()
+ // See comments on the validateTagsListAndRemoveDuplicates(user,
+ // product, oldName, actionEnum) function
+ Either<Boolean, ResponseFormat> tagsValidation = validateTagsListAndRemoveDuplicates(user, product, oldName, actionEnum);
+ if (tagsValidation.isRight()) {
+ return tagsValidation;
+ }
+
+ Either<Boolean, ResponseFormat> validateIconResponse = validateIcon(user, product, actionEnum);
+ if (validateIconResponse.isRight()) {
+ return validateIconResponse;
+ }
+
+ Either<Boolean, ResponseFormat> projectCodeValidation = validateProjectCode(user, product, actionEnum);
+ if (projectCodeValidation.isRight()) {
+ return projectCodeValidation;
+ }
+ Either<Boolean, ResponseFormat> categoryValidation = validateGrouping(user, product, actionEnum);
+ if (categoryValidation.isRight()) {
+ return categoryValidation;
+ }
+
+ Either<Boolean, ResponseFormat> contactsListValidation = validateAndUpdateProductContactsList(user, product, actionEnum);
+ if (contactsListValidation.isRight()) {
+ return contactsListValidation;
+ }
+
+ Either<Boolean, ResponseFormat> productFullNameValidation = validateProductFullNameAndCleanup(user, product, actionEnum);
+ if (productFullNameValidation.isRight()) {
+ return productFullNameValidation;
+ }
+
+ Either<Boolean, ResponseFormat> descValidation = validateDescriptionAndCleanup(user, product, actionEnum);
+ if (descValidation.isRight()) {
+ return descValidation;
+ }
+
+ return Either.left(true);
+ }
+
+ public Either<Map<String, Boolean>, ResponseFormat> validateProductNameExists(String productName, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "validate Product Name Exists", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<Boolean, StorageOperationStatus> dataModelResponse = productOperation.validateComponentNameExists(productName);
+
+ if (dataModelResponse.isLeft()) {
+ Map<String, Boolean> result = new HashMap<>();
+ result.put("isValid", dataModelResponse.left().value());
+ log.debug("validation was successfully performed.");
+ return Either.left(result);
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()));
+
+ return Either.right(responseFormat);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateProductContactsList(User user, Product product, AuditingActionEnum actionEnum) {
+ List<String> contacts = product.getContacts();
+ if (!ValidationUtils.validateListNotEmpty(contacts)) {
+ log.debug("Contacts list cannot be empty for product {}", product.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.EMPTY_PRODUCT_CONTACTS_LIST);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ boolean isProductCreatorInContacts = false;
+ String modifierUserId = user.getUserId();
+ for (String contact : contacts) {
+ if (contact.equals(modifierUserId)) {
+ log.trace("modifier userId found in product contacts");
+ isProductCreatorInContacts = true;
+ // No need to validate for this userId - it's modifier's
+ continue;
+ }
+ if (!ValidationUtils.validateContactId(contact)) {
+ log.debug("Product contacts has invalid userId {} for product {}", contact, product.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.PRODUCT.getValue());
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ User contactUser = new User();
+ contactUser.setUserId(contact);
+ Either<User, ResponseFormat> validateUser = validateUserExists(contact, "Create Product", false);
+ if (validateUser.isRight()) {
+ log.debug("Cannot set contact with userId {} as product contact, error: {}", contact, validateUser.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PRODUCT_CONTACT, contact);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ contactUser = validateUser.left().value();
+
+ Either<Boolean, ResponseFormat> validateUserRole = validateUserRole(contactUser, contactsRoles);
+ if (validateUserRole.isRight()) {
+ log.debug("Cannot set contact with userId {} as product contact, error: {}", contact, validateUserRole.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PRODUCT_CONTACT, contact);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ }
+
+ if (!isProductCreatorInContacts) {
+ log.debug("modifier userId {} not found in product contacts - adding it", modifierUserId);
+ contacts.add(modifierUserId);
+ }
+
+ // passed - setting all contacts user ids to lowercase
+ List<String> tempContacts = contacts.stream().map(e -> e.toLowerCase()).collect(Collectors.toList());
+ ValidationUtils.removeDuplicateFromList(tempContacts);
+ product.setContacts(tempContacts);
+
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateGrouping(User user, Product product, AuditingActionEnum actionEnum) {
+ List<CategoryDefinition> categories = product.getCategories();
+ if (categories == null || categories.isEmpty()) {
+ log.debug("Grouping list is empty for product {}", product.getName());
+ return Either.left(true);
+ }
+ Map<String, Map<String, Set<String>>> nonDuplicatedCategories = new HashMap<String, Map<String, Set<String>>>();
+ // remove duplicated entries
+ for (CategoryDefinition cat : categories) {
+ String catName = cat.getName();
+ if (ValidationUtils.validateStringNotEmpty(catName) == false) {
+ // error missing cat name
+ log.debug("Missing category name for product {}", product.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.PRODUCT.getValue());
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ Map<String, Set<String>> catEntry = nonDuplicatedCategories.get(catName);
+ if (catEntry == null) {
+ catEntry = new HashMap<String, Set<String>>();
+ nonDuplicatedCategories.put(catName, catEntry);
+ }
+ List<SubCategoryDefinition> subcategories = cat.getSubcategories();
+ if (subcategories == null || subcategories.isEmpty()) {
+ // error missing subcat for cat
+ log.debug("Missing sub-categories for category {} in product {}", catName, product.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ for (SubCategoryDefinition subcat : subcategories) {
+ String subCatName = subcat.getName();
+ if (ValidationUtils.validateStringNotEmpty(subCatName) == false) {
+ // error missing sub cat name for cat
+ log.debug("Missing or empty sub-category for category {} in product {}", catName, product.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ Set<String> subcatEntry = catEntry.get(subCatName);
+ if (subcatEntry == null) {
+ subcatEntry = new HashSet<String>();
+ catEntry.put(subCatName, subcatEntry);
+ }
+ List<GroupingDefinition> groupings = subcat.getGroupings();
+ for (GroupingDefinition group : groupings) {
+ String groupName = group.getName();
+ if (ValidationUtils.validateStringNotEmpty(groupName) == false) {
+ // error missing grouping for sub cat name and cat
+ log.debug("Missing or empty groupng name for sub-category {} for category {} in product {}", subCatName, catName, product.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ if (!subcatEntry.contains(groupName)) {
+ subcatEntry.add(groupName);
+ } else {
+ log.debug("Grouping [ {} ] already exist for category [ {} ] and subcategory [ {} ]", groupName, catName, subCatName);
+ }
+ }
+ }
+ } // for end of checking duplicated
+ // validate existence
+ Either<List<CategoryDefinition>, ActionStatus> allProductCategories = elementDao.getAllProductCategories();
+
+ if (allProductCategories.isRight()) {
+ log.debug("No product categories {}", allProductCategories.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(allProductCategories.right().value());
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ boolean catExist;
+ // convert non-duplicated to data modeling format and update in the
+ // input object
+ List<CategoryDefinition> newCatList = new ArrayList<CategoryDefinition>();
+
+ // over all categories from request
+ for (Map.Entry<String, Map<String, Set<String>>> entry : nonDuplicatedCategories.entrySet()) {
+ catExist = false;
+ CategoryDefinition categoryDefinition = null;
+ // over all categories from Titan
+ List<CategoryDefinition> categoriesList = allProductCategories.left().value();
+ if (categoriesList != null) {
+ for (CategoryDefinition catInDb : categoriesList) {
+ if (entry.getKey().equals(catInDb.getName())) {
+ catExist = true;
+ boolean subcatExist;
+ // copy data
+ categoryDefinition = new CategoryDefinition(catInDb);
+ SubCategoryDefinition subCategory = null;
+
+ Map<String, Set<String>> subcats = entry.getValue();
+ for (Map.Entry<String, Set<String>> subcat : subcats.entrySet()) {
+ subcatExist = false;
+ List<SubCategoryDefinition> subcategoriesList = catInDb.getSubcategories();
+ if (subcategoriesList != null) {
+ for (SubCategoryDefinition subcatInDb : subcategoriesList) {
+ if (subcatInDb.getName().equals(subcat.getKey())) {
+ // copy data
+ subCategory = new SubCategoryDefinition(subcatInDb);
+ subcatExist = true;
+ Set<String> grouping = subcat.getValue();
+ boolean groupExist;
+ GroupingDefinition groupingDefinition = null;
+ for (String group : grouping) {
+ groupExist = false;
+ List<GroupingDefinition> groupings = subcatInDb.getGroupings();
+ if (groupings != null) {
+ for (GroupingDefinition groupInDb : groupings) {
+ if (groupInDb.getName().equals(group)) {
+ groupExist = true;
+ groupingDefinition = new GroupingDefinition(groupInDb);
+ }
+ }
+ }
+ if (!groupExist) {
+ // error grouping isn't defined
+ // in Titan
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_ASSOCIATION, CategoryTypeEnum.GROUPING.getValue(), group);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ subCategory.addGrouping(groupingDefinition);
+ }
+ }
+ }
+ }
+ if (!subcatExist) {
+ // error sub category isn't defined in Titan
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_ASSOCIATION, CategoryTypeEnum.SUBCATEGORY.getValue(), subcat.getKey());
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ categoryDefinition.addSubCategory(subCategory);
+ }
+ }
+ }
+ }
+ if (!catExist) {
+ // error category isn't defined in Titan
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_ASSOCIATION, CategoryTypeEnum.CATEGORY.getValue(), entry.getKey());
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ newCatList.add(categoryDefinition);
+ }
+ product.setCategories(newCatList);
+ return Either.left(true);
+ }
+
+ public Either<Product, ResponseFormat> getProduct(String productId, User user) {
+ String ecompErrorContext = "Get product";
+ Either<User, ResponseFormat> validateEmptyResult = validateUserNotEmpty(user, ecompErrorContext);
+ if (validateEmptyResult.isRight()) {
+ return Either.right(validateEmptyResult.right().value());
+ }
+
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, ecompErrorContext, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+
+ Either<Product, StorageOperationStatus> storageStatus = productOperation.getComponent(productId, false);
+
+ if (storageStatus.isRight()) {
+ log.debug("failed to get resource by id {}", productId);
+ if (storageStatus.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ // TODO check error
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.PRODUCT_NOT_FOUND, ComponentTypeEnum.PRODUCT.getValue()));
+ } else {
+ return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus.right().value()), ""));
+ }
+ }
+ return Either.left(storageStatus.left().value());
+ }
+
+ public Either<Product, ResponseFormat> deleteProduct(String productId, User user) {
+ String ecompErrorContext = "Delete product";
+ Either<User, ResponseFormat> validateEmptyResult = validateUserNotEmpty(user, ecompErrorContext);
+ if (validateEmptyResult.isRight()) {
+ return Either.right(validateEmptyResult.right().value());
+ }
+
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, ecompErrorContext, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+
+ Either<Product, StorageOperationStatus> storageStatus = productOperation.deleteProduct(productId, false);
+
+ if (storageStatus.isRight()) {
+ log.debug("failed to delete resource by id {}", productId);
+ return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus.right().value()), ""));
+ }
+ return Either.left(storageStatus.left().value());
+ }
+
+ private Either<Boolean, ResponseFormat> validateProductFullNameAndCleanup(User user, Product product, AuditingActionEnum actionEnum) {
+ String fullName = product.getFullName();
+ if (!ValidationUtils.validateStringNotEmpty(fullName)) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_FULL_NAME);
+ componentsUtils.auditComponentAdmin(errorResponse, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(errorResponse);
+ }
+
+ fullName = ValidationUtils.removeNoneUtf8Chars(fullName);
+ fullName = ValidationUtils.removeHtmlTags(fullName);
+ fullName = ValidationUtils.normaliseWhitespace(fullName);
+ fullName = ValidationUtils.stripOctets(fullName);
+
+ if (!ValidationUtils.validateProductFullNameLength(fullName)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_FULL_NAME);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ if (!ValidationUtils.validateIsEnglish(fullName)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_FULL_NAME);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ product.setFullName(fullName);
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateProductNameAndCleanup(User user, Product product, AuditingActionEnum actionEnum) {
+ String name = product.getName();
+ if (!ValidationUtils.validateStringNotEmpty(name)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_ONE_OF_COMPONENT_NAMES, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_ABBREVIATED_NAME);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ // Product name is required to have same validation and normalization as
+ // category
+ if (!ValidationUtils.validateCategoryDisplayNameFormat(name)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_FORMAT, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_ABBREVIATED_NAME);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ String normalizedName4Display = ValidationUtils.normalizeCategoryName4Display(name);
+
+ if (!ValidationUtils.validateCategoryDisplayNameLength(normalizedName4Display)) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_ELEMENT_INVALID_NAME_LENGTH, ComponentTypeEnum.PRODUCT.getValue(), PRODUCT_ABBREVIATED_NAME);
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+
+ product.setName(normalizedName4Display);
+ String normalizedName4Uniqueness = ValidationUtils.normaliseComponentName(normalizedName4Display);
+ product.setNormalizedName(normalizedName4Uniqueness);
+
+ return Either.left(true);
+ }
+
+ // This is a workaround for a current tag--->product name behaviour, which
+ // will be changed in 1607.
+ // It was agreed with Ella on 23/2/16 that the tag validation of product
+ // will be made against the old product name (before normalization),
+ // and in 1607 US will be defined where UI will no longer pass tag of
+ // component name, and BE will add it by itself after all needed
+ // normalizations.
+ private Either<Boolean, ResponseFormat> validateTagsListAndRemoveDuplicates(User user, Product product, String oldProductName, AuditingActionEnum actionEnum) {
+ List<String> tagsList = product.getTags();
+
+ Either<Boolean, ResponseFormat> validateTags = validateComponentTags(tagsList, oldProductName, ComponentTypeEnum.PRODUCT);
+ if (validateTags.isRight()) {
+ ResponseFormat responseFormat = validateTags.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, product, "", "", actionEnum, ComponentTypeEnum.PRODUCT);
+ return Either.right(responseFormat);
+ }
+ ValidationUtils.removeDuplicateFromList(tagsList);
+ return Either.left(true);
+ }
+
+ @Override
+ public void setDeploymentArtifactsPlaceHolder(org.openecomp.sdc.be.model.Component component, User user) {
+
+ }
+
+ public Either<Product, ResponseFormat> updateProductMetadata(String productId, Product updatedProduct, User user) {
+ ComponentTypeEnum typeEnum = ComponentTypeEnum.PRODUCT;
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "Update Product", updatedProduct, null, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ // validate user role
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, updatedProduct, updateRoles, null, null);
+ if (validateRes.isRight()) {
+ return Either.right(validateRes.right().value());
+ }
+
+ if (updatedProduct == null) {
+ log.debug("Invalid product json. Check product servlet log for updateProduct entry params");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return Either.right(responseFormat);
+ }
+
+ Either<Product, StorageOperationStatus> storageStatus = productOperation.getComponent(productId, false);
+ if (storageStatus.isRight()) {
+ if (storageStatus.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.PRODUCT_NOT_FOUND, ComponentTypeEnum.PRODUCT.name().toLowerCase()));
+ }
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), typeEnum), ""));
+ }
+
+ Product currentProduct = storageStatus.left().value();
+
+ if (!ComponentValidationUtils.canWorkOnComponent(productId, productOperation, user.getUserId())) {
+ log.info("Restricted operation for user {} on product {}", user.getUserId(), currentProduct.getCreatorUserId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ Either<Product, ResponseFormat> validationRsponse = validateAndUpdateProductMetadata(user, currentProduct, updatedProduct);
+ if (validationRsponse.isRight()) {
+ log.info("product update metadata: validations field.");
+ return validationRsponse;
+ }
+
+ Product productToUpdate = validationRsponse.left().value();
+ // lock resource
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(currentProduct.getUniqueId(), currentProduct, "Update Product Metadata");
+ if (lockResult.isRight()) {
+ return Either.right(lockResult.right().value());
+ }
+ try {
+ Either<Product, StorageOperationStatus> updateResponse = productOperation.updateComponent(productToUpdate, true);
+ if (updateResponse.isRight()) {
+ productOperation.rollback();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Update Product Metadata");
+ log.debug("failed to update product {}", productToUpdate.getUniqueId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ productOperation.commit();
+ return Either.left(updateResponse.left().value());
+ } finally {
+ graphLockOperation.unlockComponent(productId, NodeTypeEnum.Product);
+ }
+ }
+
+ private Either<Product, ResponseFormat> validateAndUpdateProductMetadata(User user, Product currentProduct, Product updatedProduct) {
+
+ boolean hasBeenCertified = ValidationUtils.hasBeenCertified(currentProduct.getVersion());
+ Either<Boolean, ResponseFormat> response = validateAndUpdateProductName(user, currentProduct, updatedProduct);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateFullName(user, currentProduct, updatedProduct);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateDescription(user, currentProduct, updatedProduct, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateCategory(user, currentProduct, updatedProduct);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateContactList(user, currentProduct, updatedProduct);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateTags(user, currentProduct, updatedProduct);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateProjectCode(user, currentProduct, updatedProduct);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ if (updatedProduct.getIsActive() != null) {
+ currentProduct.setIsActive(updatedProduct.getIsActive());
+ }
+
+ response = validateAndUpdateIcon(user, currentProduct, updatedProduct, hasBeenCertified);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ String currentInvariantUuid = currentProduct.getInvariantUUID();
+ String updatedInvariantUuid = updatedProduct.getInvariantUUID();
+
+ if ((updatedInvariantUuid != null) && (!updatedInvariantUuid.equals(currentInvariantUuid))) {
+ log.warn("Product invariant UUID is automatically set and cannot be updated");
+ updatedProduct.setInvariantUUID(currentInvariantUuid);
+ }
+ return Either.left(currentProduct);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateProductName(User user, Product currentProduct, Product updatedProduct) {
+ String updatedProductName = updatedProduct.getName();
+ String tags = "";
+ String currentProductName = currentProduct.getName();
+ if (updatedProductName != null) {
+ Either<Boolean, ResponseFormat> validatProductNameResponse = validateProductNameAndCleanup(user, updatedProduct, null);
+ if (validatProductNameResponse.isRight()) {
+ ResponseFormat errorRespons = validatProductNameResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ updatedProductName = updatedProduct.getName();
+ if (!currentProductName.equals(updatedProductName)) {
+ Either<Boolean, ResponseFormat> productNameUniquenessValidation = validateComponentNameUnique(user, updatedProduct, null);
+ if (productNameUniquenessValidation.isRight()) {
+ return productNameUniquenessValidation;
+ }
+ currentProduct.setName(updatedProductName);
+ tags = updatedProductName;
+ updatedProductName = ValidationUtils.normalizeCategoryName4Display(updatedProductName);
+ currentProduct.getComponentMetadataDefinition().getMetadataDataDefinition().setNormalizedName(ValidationUtils.normaliseComponentName(updatedProductName));
+ List<String> updatedTags = updatedProduct.getTags();
+ // As discussed with Ella currently (1604) we are not removing
+ // the old name from tags.
+ if (updatedTags == null) {
+ updatedTags = currentProduct.getTags();
+ }
+ updatedTags.add(tags);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateFullName(User user, Product currentProduct, Product updatedProduct) {
+ String updatedProductName = updatedProduct.getFullName();
+ String currentProductName = currentProduct.getFullName();
+ if (updatedProductName != null && !currentProductName.equals(updatedProductName)) {
+ Either<Boolean, ResponseFormat> validatProductNameResponse = validateProductFullNameAndCleanup(user, updatedProduct, null);
+ if (validatProductNameResponse.isRight()) {
+ ResponseFormat errorRespons = validatProductNameResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentProduct.setFullName(updatedProduct.getFullName());
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateCategory(User user, Product currentProduct, Product updatedProduct) {
+ List<CategoryDefinition> categoryUpdated = updatedProduct.getCategories();
+ List<CategoryDefinition> categoryCurrent = currentProduct.getCategories();
+
+ Either<Boolean, ResponseFormat> validatCategoryResponse = validateGrouping(user, updatedProduct, null);
+ if (validatCategoryResponse.isRight()) {
+ ResponseFormat errorResponse = validatCategoryResponse.right().value();
+ return Either.right(errorResponse);
+ }
+
+ categoryUpdated = updatedProduct.getCategories();
+ if (categoryUpdated != null) {
+ currentProduct.setCategories(categoryUpdated);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateContactList(User user, Product currentProduct, Product updatedProduct) {
+ List<String> updatedContacts = updatedProduct.getContacts();
+ List<String> currentContacts = currentProduct.getContacts();
+ if (updatedContacts != null) {
+ if (!(currentContacts.containsAll(updatedContacts) && updatedContacts.containsAll(currentContacts))) {
+ Either<Boolean, ResponseFormat> validatResponse = validateAndUpdateProductContactsList(user, updatedProduct, null);
+ if (validatResponse.isRight()) {
+ ResponseFormat errorRespons = validatResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentProduct.setContacts(updatedProduct.getContacts());
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateTags(User user, Product currentProduct, Product updatedProduct) {
+ List<String> tagsUpdated = updatedProduct.getTags();
+ List<String> tagsCurrent = currentProduct.getTags();
+ if (tagsUpdated != null) {
+ if (!(tagsCurrent.containsAll(tagsUpdated) && tagsUpdated.containsAll(tagsCurrent))) {
+ Either<Boolean, ResponseFormat> validatResponse = validateTagsListAndRemoveDuplicates(user, updatedProduct, currentProduct.getName(), null);
+ if (validatResponse.isRight()) {
+ ResponseFormat errorRespons = validatResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentProduct.setTags(updatedProduct.getTags());
+ }
+ }
+ return Either.left(true);
+ }
+
+ @Override
+ public Either<List<String>, ResponseFormat> deleteMarkedComponents() {
+ // markAsDeleted isnt implemented yet
+ return Either.left(new ArrayList<>());
+ }
+
+ @Override
+ protected boolean validateTagPattern(String tag) {
+ return ValidationUtils.validateCategoryDisplayNameFormat(tag);
+ }
+
+ public Either<Product, ResponseFormat> getProductByNameAndVersion(String productName, String productVersion, String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Service By Name And Version", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<Product, StorageOperationStatus> storageStatus = productOperation.getProductByNameAndVersion(productName, productVersion, false);
+ if (storageStatus.isRight()) {
+ log.debug("failed to get service by name {} and version {}", productName, productVersion);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), ComponentTypeEnum.PRODUCT), productName));
+ }
+ Product product = storageStatus.left().value();
+ return Either.left(product);
+ }
+
+ @Override
+ public ComponentInstanceBusinessLogic getComponentInstanceBL() {
+ return productComponentInstanceBusinessLogic;
+ }
+
+ @Override
+ public Either<List<ComponentInstance>, ResponseFormat> getComponentInstancesFilteredByPropertiesAndInputs(String componentId, ComponentTypeEnum componentTypeEnum, String userId, String searchText) {
+ return null;
+ }
+
+ public ICacheMangerOperation getCacheManagerOperation() {
+ return cacheManagerOperation;
+ }
+
+ public void setCacheManagerOperation(ICacheMangerOperation cacheManagerOperation) {
+ this.cacheManagerOperation = cacheManagerOperation;
+ }
+
+ public void setProductOperation(ProductOperation productOperation) {
+ this.productOperation = productOperation;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductComponentInstanceBusinessLogic.java
new file mode 100644
index 0000000000..46188012d8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ProductComponentInstanceBusinessLogic.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("productComponentInstanceBusinessLogic")
+public class ProductComponentInstanceBusinessLogic extends ComponentInstanceBusinessLogic {
+
+ @Override
+ protected Either<Boolean, ResponseFormat> validateAllowedToContainCompInstances(org.openecomp.sdc.be.model.Component containerComponent) {
+ return Either.left(true);
+ }
+
+ @Override
+ protected NodeTypeEnum getNodeTypeOfComponentInstanceOrigin() {
+ // TODO Pavel this might be also a resource?
+ return NodeTypeEnum.Service;
+ }
+
+ @Override
+ protected ComponentOperation getContainerComponentOperation() {
+ return productOperation;
+ }
+
+ @Override
+ protected ComponentOperation getCompInstOriginComponentOperation() {
+ return serviceOperation;
+ }
+
+ @Override
+ protected ComponentTypeEnum getComponentTypeOfComponentInstance() {
+ return ComponentTypeEnum.SERVICE_INSTANCE;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PropertyBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PropertyBusinessLogic.java
new file mode 100644
index 0000000000..1e6b1c1d82
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/PropertyBusinessLogic.java
@@ -0,0 +1,391 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.ServletContext;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.resources.data.EntryData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.WebApplicationContext;
+
+import fj.data.Either;
+
+@Component("propertyBusinessLogic")
+public class PropertyBusinessLogic extends BaseBusinessLogic {
+
+ private static final String CREATE_PROPERTY = "CreateProperty";
+
+ private static Logger log = LoggerFactory.getLogger(PropertyBusinessLogic.class.getName());
+
+ @javax.annotation.Resource
+ private IResourceOperation resourceOperation = null;
+
+ @javax.annotation.Resource
+ private PropertyOperation propertyOperation = null;
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+
+ return webApplicationContext.getBean(class1);
+ }
+
+ public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
+ return eitherAllDataTypes;
+ }
+
+ /**
+ * Create new property on resource in graph
+ *
+ * @param resourceId
+ * @param propertyName
+ * @param newPropertyDefinition
+ * @param userId
+ * @return Either<PropertyDefinition, ActionStatus>
+ */
+ public Either<EntryData<String, PropertyDefinition>, ResponseFormat> createProperty(String resourceId, String propertyName, PropertyDefinition newPropertyDefinition, String userId) {
+
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Property", false);
+ if (resp.isRight()) {
+ result = Either.right(resp.right().value());
+ return result;
+ }
+
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
+ log.info("Failed to lock component {}. Error - {}", resourceId, lockResult);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+
+ try {
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ return result;
+ }
+ Resource resource = status.left().value();
+
+ // verify that resource is checked-out and the user is the last
+ // updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ return result;
+ }
+
+ // verify property not exist in resource
+ List<PropertyDefinition> resourceProperties = resource.getProperties();
+
+ if (resourceProperties != null) {
+ if (propertyOperation.isPropertyExist(resourceProperties, resourceId, propertyName)) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, ""));
+ return result;
+ }
+ }
+
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
+ if (allDataTypes.isRight()) {
+ result = Either.right(allDataTypes.right().value());
+ return result;
+ }
+
+ Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
+
+ // validate property default values
+ Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
+ if (defaultValuesValidation.isRight()) {
+ result = Either.right(defaultValuesValidation.right().value());
+ return result;
+ }
+ // convert property
+ ToscaPropertyType type = getType(newPropertyDefinition.getType());
+ if (type != null) {
+ PropertyValueConverter converter = type.getConverter();
+ // get inner type
+ String innerType = null;
+ if (newPropertyDefinition != null) {
+ SchemaDefinition schema = newPropertyDefinition.getSchema();
+ if (schema != null) {
+ PropertyDataDefinition prop = schema.getProperty();
+ if (prop != null) {
+ innerType = prop.getType();
+ }
+ }
+ String convertedValue = null;
+ if (newPropertyDefinition.getDefaultValue() != null) {
+ convertedValue = converter.convert(newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
+ newPropertyDefinition.setDefaultValue(convertedValue);
+ }
+ }
+ }
+
+ // add the new property to resource on graph
+ // need to get StorageOpaerationStatus and convert to ActionStatus
+ // from componentsUtils
+ Either<PropertyData, StorageOperationStatus> either = propertyOperation.addProperty(propertyName, newPropertyDefinition, resourceId);
+ if (either.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
+ return result;
+ }
+
+ PropertyDefinition createdPropertyDefinition = propertyOperation.convertPropertyDataToPropertyDefinition(either.left().value(), propertyName, resourceId);
+ EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, createdPropertyDefinition);
+ result = Either.left(property);
+ return result;
+
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+
+ }
+
+ /**
+ * Get property of resource
+ *
+ * @param resourceId
+ * @param propertyId
+ * @param userId
+ * TODO
+ * @return
+ */
+ public Either<Entry<String, PropertyDefinition>, ResponseFormat> getProperty(String resourceId, String propertyId, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "create Component Instance", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ }
+ Resource resource = status.left().value();
+
+ // verify property exist in resource
+ List<PropertyDefinition> properties = resource.getProperties();
+ if (properties == null) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
+ }
+ for (PropertyDefinition property : properties) {
+ // esofer - check also that the property belongs to the current
+ // resource
+ if (property.getUniqueId().equals(propertyId) && property.getParentUniqueId().equals(resourceId)) {
+ Map<String, PropertyDefinition> propMap = new HashMap<>();
+ propMap.put(property.getName(), property);
+ return Either.left(propMap.entrySet().iterator().next());
+ }
+ }
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
+ }
+
+ /**
+ * delete property of resource from graph
+ *
+ * @param resourceId
+ * @param propertyId
+ * @param userId
+ * @return
+ */
+ public Either<EntryData<String, PropertyDefinition>, ResponseFormat> deleteProperty(String resourceId, String propertyId, String userId) {
+
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Property", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+
+ try {
+
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ return result;
+ }
+ Resource resource = status.left().value();
+
+ // verify that resource is checked-out and the user is the last
+ // updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ return result;
+ }
+
+ // verify property exist in resource
+ Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
+ if (statusGetProperty.isRight()) {
+ result = Either.right(statusGetProperty.right().value());
+ return result;
+ }
+ String propertyName = statusGetProperty.left().value().getKey();
+
+ // delete property of resource from graph
+ // TODO: need to get StorageOperationStatus
+ Either<PropertyData, StorageOperationStatus> either = propertyOperation.deleteProperty(propertyId);
+ // Either<PropertyData, StorageOperationStatus> either =
+ // propertyOperation.deletePropertyFromGraph(propertyId);
+ if (either.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
+ return result;
+ }
+ // propertyOperation.getTitanGenericDao().commit();
+ PropertyDefinition createdPropertyDefinition = propertyOperation.convertPropertyDataToPropertyDefinition(either.left().value(), propertyName, resourceId);
+ EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, createdPropertyDefinition);
+ result = Either.left(property);
+ return result;
+
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+ }
+
+ /**
+ * update property
+ *
+ * @param resourceId
+ * @param propertyId
+ * @param newPropertyDefinition
+ * @param userId
+ * @return
+ */
+ public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateProperty(String resourceId, String propertyId, PropertyDefinition newPropertyDefinition, String userId) {
+
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
+
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
+ }
+ Resource resource = status.left().value();
+
+ // verify that resource is checked-out and the user is the last updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return result;
+ }
+
+ try {
+
+ // verify property exist in resource
+ Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
+ if (statusGetProperty.isRight()) {
+ result = Either.right(statusGetProperty.right().value());
+ return result;
+ }
+ String propertyName = statusGetProperty.left().value().getKey();
+
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
+ if (allDataTypes.isRight()) {
+ result = Either.right(allDataTypes.right().value());
+ return result;
+ }
+ Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
+ // validate property default values
+ Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
+ if (defaultValuesValidation.isRight()) {
+ result = Either.right(defaultValuesValidation.right().value());
+ return result;
+ }
+
+ // add the new property to resource on graph
+ // TODO: convert TitanOperationStatus to Storgae...
+ Either<PropertyData, StorageOperationStatus> either = propertyOperation.updateProperty(propertyId, newPropertyDefinition, dataTypes);
+ // Either<PropertyData, StorageOperationStatus> either =
+ // propertyOperation.updatePropertyFromGraph(propertyId,
+ // newPropertyDefinition);
+ if (either.isRight()) {
+ log.debug("Problem while updating property with id {}. Reason - {}", propertyId, either.right().value());
+ result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
+ return result;
+ // return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ PropertyDefinition createdPropertyDefinition = propertyOperation.convertPropertyDataToPropertyDefinition(either.left().value(), propertyName, resourceId);
+ EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, createdPropertyDefinition);
+ result = Either.left(property);
+ return result;
+
+ } finally {
+ commitOrRollback(result);
+ // unlock component
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RequirementsBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RequirementsBusinessLogic.java
new file mode 100644
index 0000000000..f8dde98010
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/RequirementsBusinessLogic.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("requirementsBusinessLogic")
+public class RequirementsBusinessLogic {
+ private static Logger log = LoggerFactory.getLogger(RequirementsBusinessLogic.class.getName());
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentsUtils;
+
+ @javax.annotation.Resource
+ private ResourceBusinessLogic resourceBusinessLogic;
+
+ @javax.annotation.Resource
+ private IResourceOperation resourceOperation;
+
+ public Either<RequirementDefinition, ResponseFormat> updateRequirement(String resourceId, String requirementId, RequirementDefinition requirementDefinition, String userId) {
+
+ // Get the resource from DB
+ Either<Resource, StorageOperationStatus> status = getResource(resourceId);
+ if (status.isRight()) {
+ log.debug("Couldn't get resource {} from DB", resourceId);
+ return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(status.right().value()), ""));
+ }
+ Resource resource = status.left().value();
+ if (resource == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeResourceMissingError, "Requirement Business Logic", resourceId);
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("Requirement Business Logic", ComponentTypeEnum.RESOURCE.getValue(), resourceId);
+ log.debug("Couldn't get resource {} from DB", resourceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ // verify that resource is checked-out and the user is the last updater
+ if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ // TODO
+ return null;
+ }
+
+ private Either<Resource, StorageOperationStatus> getResource(final String resourceId) {
+
+ log.debug("Get resource with id {}", resourceId);
+ Either<Resource, StorageOperationStatus> status = resourceOperation.getResource(resourceId);
+ if (status.isRight()) {
+ log.debug("Resource with id {} was not found", resourceId);
+ return Either.right(status.right().value());
+ }
+
+ Resource resource = status.left().value();
+ if (resource == null) {
+ log.debug("General Error while get resource with id {}", resourceId);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ return Either.left(resource);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java
new file mode 100644
index 0000000000..ddc03e14e8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java
@@ -0,0 +1,5475 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic.ArtifactOperation;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaElementTypeEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datamodel.api.HighestFilterEnum;
+import org.openecomp.sdc.be.datamodel.utils.ArtifactUtils;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.info.ArtifactTemplateInfo;
+import org.openecomp.sdc.be.info.MergedArtifactInfo;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GetInputValueInfo;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.ParsedToscaYamlInfo;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.UploadCapInfo;
+import org.openecomp.sdc.be.model.UploadComponentInstanceInfo;
+import org.openecomp.sdc.be.model.UploadPropInfo;
+import org.openecomp.sdc.be.model.UploadReqInfo;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.heat.HeatParameterType;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.IComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IHeatParametersOperation;
+import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.CsarOperation;
+import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
+import org.openecomp.sdc.be.model.operations.impl.GroupOperation;
+import org.openecomp.sdc.be.model.operations.impl.InputsOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.servlets.RepresentationUtils;
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.be.utils.CommonBeUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.kpi.api.ASDCKpiApi;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.context.WebApplicationContext;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.representer.Representer;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("resourceBusinessLogic")
+public class ResourceBusinessLogic extends ComponentBusinessLogic {
+
+ private static final String PLACE_HOLDER_RESOURCE_TYPES = "validForResourceTypes";
+ public static final String INITIAL_VERSION = "0.1";
+
+ Pattern STR_REPLACE_PATTERN = Pattern.compile("^[ ]*\\{[ ]*" + "str_replace" + "=");
+ Pattern TOKEN_PATTERN = Pattern.compile("[ ]*\\{[ ]*" + "token" + "=");
+ Pattern GET_PROPERTY_PATTERN = Pattern.compile("[ ]*\\{[ ]*" + "get_property" + "=");
+ Pattern CONCAT_PATTERN = Pattern.compile("[ ]*\\{[ ]*" + "concat" + "=");
+
+ private static Logger log = LoggerFactory.getLogger(ResourceBusinessLogic.class.getName());
+
+ public ResourceBusinessLogic() {
+ log.debug("ResourceBusinessLogic started");
+ }
+
+ @Autowired
+ private ICapabilityTypeOperation capabilityTypeOperation = null;
+
+ @Autowired
+ private IInterfaceLifecycleOperation interfaceTypeOperation = null;
+
+ @Autowired
+ private IComponentInstanceOperation componentInstanceOperation;
+
+ @Autowired
+ private LifecycleBusinessLogic lifecycleBusinessLogic;
+
+ @Autowired
+ private IPropertyOperation propertyOperation;
+
+ @Autowired
+ private CsarOperation csarOperation;
+
+ @Autowired
+ private VFComponentInstanceBusinessLogic vfComponentInstanceBusinessLogic;
+
+ @Autowired
+ private ResourceImportManager resourceImportManager;
+
+ @Autowired
+ private GroupBusinessLogic groupBusinessLogic;
+ @Autowired
+ private InputsBusinessLogic inputsBusinessLogic;
+
+ @javax.annotation.Resource
+ private InputsOperation inputOperation;
+
+ @Autowired
+ private GroupOperation groupOperation;
+
+ @Autowired
+ private IHeatParametersOperation heatParametersOperation;
+
+ @Autowired
+ private IArtifactOperation artifactOperation;
+
+ @Autowired
+ private CapabilityInstanceOperation capabilityInstanceOperation;
+
+ @Autowired
+ private CompositionBusinessLogic compositionBusinessLogic;
+
+ @Autowired
+ private ICacheMangerOperation cacheManagerOperation;
+
+ private Gson gson = new Gson();
+
+ public CsarOperation getCsarOperation() {
+ return csarOperation;
+ }
+
+ public void setCsarOperation(CsarOperation csarOperation) {
+ this.csarOperation = csarOperation;
+ }
+
+ public IResourceOperation getResourceOperation() {
+ return this.resourceOperation;
+ }
+
+ public void setResourceOperation(ResourceOperation resourceOperation) {
+ this.resourceOperation = resourceOperation;
+ }
+
+ public LifecycleBusinessLogic getLifecycleBusinessLogic() {
+ return lifecycleBusinessLogic;
+ }
+
+ public void setLifecycleManager(LifecycleBusinessLogic lifecycleBusinessLogic) {
+ this.lifecycleBusinessLogic = lifecycleBusinessLogic;
+ }
+
+ public IElementOperation getElementDao() {
+ return elementDao;
+ }
+
+ public void setElementDao(IElementOperation elementDao) {
+ this.elementDao = elementDao;
+ }
+
+ public IUserBusinessLogic getUserAdmin() {
+ return this.userAdmin;
+ }
+
+ public void setUserAdmin(UserBusinessLogic userAdmin) {
+ this.userAdmin = userAdmin;
+ }
+
+ public ComponentsUtils getComponentsUtils() {
+ return this.componentsUtils;
+ }
+
+ public void setComponentsUtils(ComponentsUtils componentsUtils) {
+ this.componentsUtils = componentsUtils;
+ }
+
+ public ArtifactsBusinessLogic getArtifactsManager() {
+ return artifactsBusinessLogic;
+ }
+
+ public void setArtifactsManager(ArtifactsBusinessLogic artifactsManager) {
+ this.artifactsBusinessLogic = artifactsManager;
+ }
+
+ public void setPropertyOperation(IPropertyOperation propertyOperation) {
+ this.propertyOperation = propertyOperation;
+ }
+
+ public ApplicationDataTypeCache getApplicationDataTypeCache() {
+ return applicationDataTypeCache;
+ }
+
+ public void setApplicationDataTypeCache(ApplicationDataTypeCache applicationDataTypeCache) {
+ this.applicationDataTypeCache = applicationDataTypeCache;
+ }
+
+ /**
+ * the method returns a list of all the resources that are certified, the returned resources are only abstract or only none abstract according to the given param
+ *
+ * @param getAbstract
+ * @param userId
+ * TODO
+ * @return
+ */
+ public Either<List<Resource>, ResponseFormat> getAllCertifiedResources(boolean getAbstract, HighestFilterEnum highestFilter, String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get All Certified Resources", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ IResourceOperation dataModel = getResourceOperation();
+ Boolean isHighest = null;
+ switch (highestFilter) {
+ case ALL:
+ break;
+ case HIGHEST_ONLY:
+ isHighest = true;
+ break;
+ case NON_HIGHEST_ONLY:
+ isHighest = false;
+ break;
+ default:
+ break;
+ }
+
+ Either<List<Resource>, StorageOperationStatus> dataModelResponse = dataModel.getAllCertifiedResources(getAbstract, isHighest);
+
+ if (dataModelResponse.isLeft()) {
+ log.debug("Retrived Resource successfully.");
+ return Either.left(dataModelResponse.left().value());
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()));
+
+ return Either.right(responseFormat);
+ }
+
+ public Either<Map<String, Boolean>, ResponseFormat> validateResourceNameExists(String resourceName, ResourceTypeEnum resourceTypeEnum, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "validate Resource Name Exists", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ IResourceOperation dataModel = getResourceOperation();
+
+ Either<Boolean, StorageOperationStatus> dataModelResponse = dataModel.validateResourceNameExists(resourceName, resourceTypeEnum);
+
+ if (dataModelResponse.isLeft()) {
+ Map<String, Boolean> result = new HashMap<>();
+ result.put("isValid", dataModelResponse.left().value());
+ log.debug("validation was successfully performed.");
+ return Either.left(result);
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()));
+
+ return Either.right(responseFormat);
+ }
+
+ public Either<Resource, ResponseFormat> createResource(Resource resource, User user, Map<String, byte[]> csarUIPayload, String payloadName) {
+ Either<Resource, ResponseFormat> createResourceResponse = validateResourceBeforeCreate(resource, user, false);
+ if (createResourceResponse.isRight()) {
+ return createResourceResponse;
+ }
+
+ // Creating resource either by DAO or from CSAR
+ String csarUUID = null;
+ if (payloadName == null) {
+ csarUUID = resource.getCsarUUID();
+ } else {
+ csarUUID = payloadName;
+ }
+ if (csarUUID != null && !csarUUID.isEmpty()) {
+ // check if VF with the same Csar UUID or with he same name already
+ // exists
+ Either<List<ResourceMetadataData>, StorageOperationStatus> validateCsarUuidUniquenessRes = resourceOperation.validateCsarUuidUniqueness(csarUUID);
+ if (validateCsarUuidUniquenessRes.isRight()) {
+ log.debug("Failed to validate uniqueness of CsarUUID {} for resource", csarUUID, resource.getSystemName());
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(validateCsarUuidUniquenessRes.right().value())));
+ }
+
+ List<ResourceMetadataData> existingResourceRes = validateCsarUuidUniquenessRes.left().value();
+ if (existingResourceRes != null && existingResourceRes.size() > 0) {
+ log.debug("Failed to create resource {}, csarUUID {} already exist for a different VF {}", resource.getSystemName(), csarUUID, existingResourceRes.get(0).getMetadataDataDefinition().getName());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.VSP_ALREADY_EXISTS, csarUUID, existingResourceRes.get(0).getMetadataDataDefinition().getName());
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", AuditingActionEnum.CREATE_RESOURCE, null);
+ return Either.right(errorResponse);
+ }
+
+ log.debug("CsarUUID is {} - going to create resource from CSAR", csarUUID);
+ createResourceResponse = createResourceFromCsar(resource, user, AuditingActionEnum.CREATE_RESOURCE, false, Either.left(csarUIPayload), csarUUID);
+ return createResourceResponse;
+ }
+
+ return createResourceByDao(resource, user, AuditingActionEnum.CREATE_RESOURCE, false);
+ }
+
+ public Either<Resource, ResponseFormat> validateAndUpdateResourceFromCsar(Resource resource, User user, Map<String, byte[]> csarUIPayload, String payloadName, String resourceUniqueId) {
+ Either<Resource, ResponseFormat> updateResourceResponse = null;
+ Either<Resource, ResponseFormat> validateResourceResponse = null;
+ Wrapper<ResponseFormat> responseWrapper = new Wrapper<ResponseFormat>();
+ String csarUUID = null;
+ String csarVersion = null;
+ if (payloadName == null) {
+ csarUUID = resource.getCsarUUID();
+ csarVersion = resource.getCsarVersion();
+ } else {
+ csarUUID = payloadName;
+ }
+ if (csarUUID != null && !csarUUID.isEmpty()) {
+ Resource oldResource = getResourceByUniqueId(responseWrapper, resourceUniqueId);
+ if (responseWrapper.isEmpty()) {
+ validateCsarUuidMatching(responseWrapper, oldResource, resource, csarUUID, resourceUniqueId, user);
+ }
+ if (responseWrapper.isEmpty()) {
+ validateCsarIsNotAlreadyUsed(responseWrapper, oldResource, resource, csarUUID, user);
+ }
+ if (responseWrapper.isEmpty()) {
+ if (oldResource != null && ValidationUtils.hasBeenCertified(oldResource.getVersion())) {
+ overrideImmutableMetadata(oldResource, resource);
+ }
+ validateResourceResponse = validateResourceBeforeCreate(resource, user, false);
+ if (validateResourceResponse.isRight()) {
+ responseWrapper.setInnerElement(validateResourceResponse.right().value());
+ }
+ }
+ if (responseWrapper.isEmpty()) {
+ String oldCsarVersion = oldResource.getCsarVersion();
+ log.debug("CsarUUID is {} - going to update resource with UniqueId {} from CSAR", csarUUID, resourceUniqueId);
+ // (on boarding flow): If the update includes same csarUUID and
+ // same csarVersion as already in the VF - no need to import the
+ // csar (do only metadata changes if there are).
+ if (csarVersion != null && oldCsarVersion != null && oldCsarVersion.equals(csarVersion)) {
+ updateResourceResponse = updateResourceMetadata(resourceUniqueId, resource, oldResource, user, false);
+ } else {
+ updateResourceResponse = updateResourceFromCsar(oldResource, resource, user, AuditingActionEnum.UPDATE_RESOURCE_METADATA, false, Either.left(csarUIPayload), csarUUID);
+ }
+ }
+ } else {
+ log.debug("Failed to update resource {}, csarUUID or payload name is missing", resource.getSystemName());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_CSAR_UUID, resource.getName());
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", AuditingActionEnum.CREATE_RESOURCE, null);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ if (responseWrapper.isEmpty()) {
+ return updateResourceResponse;
+ }
+ return Either.right(responseWrapper.getInnerElement());
+ }
+
+ private void validateCsarIsNotAlreadyUsed(Wrapper<ResponseFormat> responseWrapper, Resource oldResource, Resource resource, String csarUUID, User user) {
+ // (on boarding flow): If the update includes a csarUUID: verify this
+ // csarUUID is not in use by another VF, If it is - use same error as
+ // above:
+ // "Error: The VSP with UUID %1 was already imported for VF %2. Please
+ // select another or update the existing VF." %1 - csarUUID, %2 - VF
+ // name
+ Either<Resource, StorageOperationStatus> resourceLinkedToCsarRes = resourceOperation.getLatestResourceByCsarOrName(csarUUID, resource.getSystemName());
+ if (resourceLinkedToCsarRes.isRight()) {
+ if (!StorageOperationStatus.NOT_FOUND.equals(resourceLinkedToCsarRes.right().value())) {
+ log.debug("Failed to find previous resource by CSAR {} and system name {}", csarUUID, resource.getSystemName());
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resourceLinkedToCsarRes.right().value())));
+ }
+ } else if (!resourceLinkedToCsarRes.left().value().getUniqueId().equals(oldResource.getUniqueId()) && !resourceLinkedToCsarRes.left().value().getName().equals(oldResource.getName())) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.VSP_ALREADY_EXISTS, csarUUID, resourceLinkedToCsarRes.left().value().getName());
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", AuditingActionEnum.UPDATE_RESOURCE_METADATA, null);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ }
+
+ private void validateCsarUuidMatching(Wrapper<ResponseFormat> responseWrapper, Resource resource, Resource oldResource, String csarUUID, String resourceUniqueId, User user) {
+ // (on boarding flow): If the update includes csarUUID which is
+ // different from the csarUUID of the VF - fail with
+ // error: "Error: Resource %1 cannot be updated using since it is linked
+ // to a different VSP" %1 - VF name
+ String oldCsarUUID = oldResource.getCsarUUID();
+ if (oldCsarUUID != null && !oldCsarUUID.isEmpty() && !csarUUID.equals(oldCsarUUID)) {
+ log.debug("Failed to update resource with UniqueId {} using Csar {}, since the resource is linked to a different VSP {}", resourceUniqueId, csarUUID, oldCsarUUID);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_LINKED_TO_DIFFERENT_VSP, resource.getName(), csarUUID, oldCsarUUID);
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", AuditingActionEnum.UPDATE_RESOURCE_METADATA, null);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ }
+
+ private Resource getResourceByUniqueId(Wrapper<ResponseFormat> responseWrapper, String resourceUniqueId) {
+ Either<Resource, StorageOperationStatus> oldResourceRes = resourceOperation.getResource(resourceUniqueId);
+ if (oldResourceRes.isRight()) {
+ log.debug("Failed to find previous resource by UniqueId {}, status: {}", resourceUniqueId, oldResourceRes.right().value());
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(oldResourceRes.right().value())));
+ return null;
+ }
+ return oldResourceRes.left().value();
+ }
+
+ private void overrideImmutableMetadata(Resource oldRresource, Resource resource) {
+ resource.setName(oldRresource.getName());
+ resource.setIcon(oldRresource.getIcon());
+ resource.setTags(oldRresource.getTags());
+ resource.setVendorName(oldRresource.getVendorName());
+ resource.setCategories(oldRresource.getCategories());
+ resource.setDerivedFrom(oldRresource.getDerivedFrom());
+ }
+
+ private Either<Resource, ResponseFormat> updateResourceFromCsar(Resource oldRresource, Resource newRresource, User user, AuditingActionEnum updateResource, boolean inTransaction, Either<Map<String, byte[]>, StorageOperationStatus> csarUIPayload,
+ String csarUUID) {
+
+ // check state
+ if (LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.equals(oldRresource.getLifecycleState())) {
+ if (!oldRresource.getLastUpdaterUserId().equals(user.getUserId())) {
+ log.debug("Current user is not last updater, last updater userId: {}, current user userId: {}", oldRresource.getLastUpdaterUserId(), user.getUserId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+ }
+ String lockedResourceId = oldRresource.getUniqueId();
+ List<ArtifactDefinition> createdArtifacts = new ArrayList<ArtifactDefinition>();
+
+ Either<Map<String, byte[]>, StorageOperationStatus> csar = null;
+ if (csarUIPayload != null && csarUIPayload.left() != null && csarUIPayload.left().value() != null) {
+ csar = csarUIPayload;
+ } else {
+ csar = csarOperation.getCsar(csarUUID, user);
+ }
+ if (csar.isRight()) {
+ log.debug("Failed to get csar for casrUUID{} ", csarUUID);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(csar.right().value())));
+ }
+
+ Either<ImmutablePair<String, String>, ResponseFormat> toscaYamlCsarStatus = validateAndParseCsar(newRresource, user, csarUUID, csar);
+ if (toscaYamlCsarStatus.isRight()) {
+ return Either.right(toscaYamlCsarStatus.right().value());
+ }
+ Either<String, ResponseFormat> checksum = CsarValidationUtils.getToscaYamlChecksum(csar.left().value(), csarUUID, componentsUtils);
+ if (checksum.isRight()) {
+ log.debug("Failed to calculate checksum for casrUUID{} error {} ", csarUUID, checksum.right().value());
+ return Either.right(checksum.right().value());
+ }
+ boolean isUpdateYaml = true;
+ if (checksum.left().value().equals(oldRresource.getComponentMetadataDefinition().getMetadataDataDefinition().getImportedToscaChecksum())) {
+ log.debug("The checksums are equals for csarUUID {}, existing checsum is {}, new one is {} ", csarUUID, oldRresource.getComponentMetadataDefinition().getMetadataDataDefinition().getImportedToscaChecksum(), checksum.left().value());
+ if (oldRresource.getLifecycleState().equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT))
+ isUpdateYaml = false;
+ } else {
+ oldRresource.getComponentMetadataDefinition().getMetadataDataDefinition().setImportedToscaChecksum(checksum.left().value());
+ }
+
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(lockedResourceId, oldRresource, "update Resource From Csar");
+ if (lockResult.isRight()) {
+ return Either.right(lockResult.right().value());
+ }
+
+ Either<Resource, ResponseFormat> result = null;
+ try {
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> prepareForUpdate = null;
+ Resource preparedResource = null;
+ if (isUpdateYaml) {
+
+ prepareForUpdate = updateExistingResourceByImport(newRresource, oldRresource, user, true, false);
+
+ if (prepareForUpdate.isRight()) {
+ log.debug("Failed to prepare resource for update : {}", prepareForUpdate.right().value());
+ result = Either.right(prepareForUpdate.right().value());
+ return result;
+ }
+ preparedResource = prepareForUpdate.left().value().left;
+ String yamlFileName = toscaYamlCsarStatus.left().value().getKey();
+ String yamlFileContents = toscaYamlCsarStatus.left().value().getValue();
+ log.trace("YAML topology file found in CSAR, file name: {}, contents: {}", yamlFileName, yamlFileContents);
+
+ // Either<Map<String, Resource>, ResponseFormat>
+ // parseNodeTypeInfoYamlEither =
+ // createResourcesFromYamlNodeTypesList(yamlFileName,preparedResource,
+ // yamlFileContents, user,false);
+
+ Either<Map<String, Resource>, ResponseFormat> parseNodeTypeInfoYamlEither = this.handleNodeTypes(yamlFileName, preparedResource, user, yamlFileContents, csar.left().value(), false);
+ if (parseNodeTypeInfoYamlEither.isRight()) {
+ ResponseFormat responseFormat = parseNodeTypeInfoYamlEither.right().value();
+ componentsUtils.auditResource(responseFormat, user, preparedResource, "", "", updateResource, null);
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ Either<ParsedToscaYamlInfo, ResponseFormat> uploadComponentInstanceInfoMap = parseResourceInfoFromYaml(yamlFileName, preparedResource, yamlFileContents, user);
+ if (uploadComponentInstanceInfoMap.isRight()) {
+ ResponseFormat responseFormat = uploadComponentInstanceInfoMap.right().value();
+ componentsUtils.auditResource(responseFormat, user, preparedResource, "", "", updateResource, null);
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ Map<String, InputDefinition> inputs = uploadComponentInstanceInfoMap.left().value().getInputs();
+ Either<Resource, ResponseFormat> createInputsOnResource = createInputsOnResource(preparedResource, user, inputs, true);
+ if (createInputsOnResource.isRight()) {
+ log.debug("failed to create resource inputs status is {}", createInputsOnResource.right().value());
+ ResponseFormat responseFormat = createInputsOnResource.right().value();
+ componentsUtils.auditResource(createInputsOnResource.right().value(), user, preparedResource, "", "", updateResource, null);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ preparedResource = createInputsOnResource.left().value();
+
+ Map<String, UploadComponentInstanceInfo> instances = uploadComponentInstanceInfoMap.left().value().getInstances();
+ Either<Resource, ResponseFormat> createResourcesInstancesEither = createResourceInstances(user, yamlFileName, preparedResource, instances, true, false, parseNodeTypeInfoYamlEither.left().value());
+ if (createResourcesInstancesEither.isRight()) {
+ log.debug("failed to create resource instances status is {}", createResourcesInstancesEither.right().value());
+ ResponseFormat responseFormat = createResourcesInstancesEither.right().value();
+ componentsUtils.auditResource(createResourcesInstancesEither.right().value(), user, preparedResource, "", "", updateResource, null);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ preparedResource = createResourcesInstancesEither.left().value();
+
+ createResourcesInstancesEither = createResourceInstancesRelations(user, yamlFileName, preparedResource, instances, true, false);
+ if (createResourcesInstancesEither.isRight()) {
+ log.debug("failed to create relation between resource instances status is {}", createResourcesInstancesEither.right().value());
+ result = Either.right(createResourcesInstancesEither.right().value());
+ return result;
+ }
+
+ preparedResource = createResourcesInstancesEither.left().value();
+
+ Either<Map<String, GroupDefinition>, ResponseFormat> validateUpdateVfGroupNamesRes = groupBusinessLogic.validateUpdateVfGroupNames(uploadComponentInstanceInfoMap.left().value().getGroups(), preparedResource.getSystemName());
+ if (validateUpdateVfGroupNamesRes.isRight()) {
+
+ return Either.right(validateUpdateVfGroupNamesRes.right().value());
+ }
+ // add groups to resource
+ Map<String, GroupDefinition> groups;
+
+ if (!validateUpdateVfGroupNamesRes.left().value().isEmpty()) {
+ groups = validateUpdateVfGroupNamesRes.left().value();
+ } else {
+ groups = uploadComponentInstanceInfoMap.left().value().getGroups();
+ }
+ Either<Resource, ResponseFormat> updatedGroupsOnResource = updateGroupsOnResource(preparedResource, user, groups);
+ if (updatedGroupsOnResource.isRight()) {
+
+ return updatedGroupsOnResource;
+ }
+ preparedResource = updatedGroupsOnResource.left().value();
+
+ } else {
+ Either<Resource, ResponseFormat> dataModelResponse = updateResourceMetadata(oldRresource.getUniqueId(), newRresource, user, oldRresource, false, true);
+ if (dataModelResponse.isRight()) {
+ log.debug("failed to update resource metadata {}", dataModelResponse.right().value());
+ result = Either.right(dataModelResponse.right().value());
+ return result;
+ }
+
+ preparedResource = dataModelResponse.left().value();
+
+ }
+
+ Either<Resource, ResponseFormat> createdCsarArtifactsEither = handleCsarArtifacts(preparedResource, user, csarUUID, csar.left().value(), createdArtifacts, ArtifactOperation.Update, false, true);
+ if (createdCsarArtifactsEither.isRight()) {
+
+ return createdCsarArtifactsEither;
+ }
+ preparedResource = createdCsarArtifactsEither.left().value();
+
+ Either<List<ComponentInstance>, ResponseFormat> eitherSetPosition = compositionBusinessLogic.setPositionsForComponentInstances(preparedResource, user.getUserId());
+ result = eitherSetPosition.isRight() ? Either.right(eitherSetPosition.right().value()) : Either.left(preparedResource);
+
+ return result;
+
+ } finally {
+ if (result == null || result.isRight()) {
+ log.warn("operation failed. do rollback");
+ titanGenericDao.rollback();
+ if (!createdArtifacts.isEmpty()) {
+ StorageOperationStatus deleteFromEsRes = artifactsBusinessLogic.deleteAllComponentArtifactsIfNotOnGraph(createdArtifacts);
+ if (!deleteFromEsRes.equals(StorageOperationStatus.OK)) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(deleteFromEsRes);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, oldRresource.getName());
+ }
+ log.debug("component and all its artifacts were deleted, id = {}", oldRresource.getName());
+ }
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ log.debug("unlock resource {}", lockedResourceId);
+ graphLockOperation.unlockComponent(lockedResourceId, NodeTypeEnum.Resource);
+ }
+
+ }
+
+ public Either<Resource, ResponseFormat> createResourceFromCsar(Resource resource, User user, AuditingActionEnum createResource, boolean inTransaction, Either<Map<String, byte[]>, StorageOperationStatus> csarUIPayload, String csarUUID) {
+ log.trace("************* created successfully from YAML, resource TOSCA ");
+
+ Either<Map<String, byte[]>, StorageOperationStatus> csar = null;
+ if (csarUIPayload != null && csarUIPayload.left() != null && csarUIPayload.left().value() != null) {
+ csar = csarUIPayload;
+ } else {
+ csar = csarOperation.getCsar(csarUUID, user);
+ }
+
+ Either<ImmutablePair<String, String>, ResponseFormat> toscaYamlCsarStatus = validateAndParseCsar(resource, user, csarUUID, csar);
+ if (toscaYamlCsarStatus.isRight()) {
+ return Either.right(toscaYamlCsarStatus.right().value());
+ }
+ Either<String, ResponseFormat> toscaYamlChecksum = CsarValidationUtils.getToscaYamlChecksum(csar.left().value(), csarUUID, componentsUtils);
+ if (toscaYamlChecksum.isRight()) {
+ log.debug("Failed to calculate checksum for CSAR {}, error {}", csarUUID, toscaYamlChecksum.right().value());
+ return Either.right(toscaYamlChecksum.right().value());
+ }
+ resource.getComponentMetadataDefinition().getMetadataDataDefinition().setImportedToscaChecksum(toscaYamlChecksum.left().value());
+
+ String yamlFileName = toscaYamlCsarStatus.left().value().getKey();
+ String yamlFileContents = toscaYamlCsarStatus.left().value().getValue();
+ log.trace("YAML topology file found in CSAR, file name: {}, contents: {}", yamlFileName, yamlFileContents);
+ Either<Resource, ResponseFormat> createResourceFromYaml = createResourceFromYaml(resource, user, yamlFileContents, yamlFileName, csar.left().value(), csarUUID);
+ if (createResourceFromYaml.isRight()) {
+ log.debug("Couldn't create resource from YAML");
+ return Either.right(createResourceFromYaml.right().value());
+ }
+
+ Resource vfResource = createResourceFromYaml.left().value();
+ log.trace("*************VF Resource created successfully from YAML, resource TOSCA name: {}", vfResource.getToscaResourceName());
+ return Either.left(vfResource);
+ }
+
+ private Either<ImmutablePair<String, String>, ResponseFormat> validateAndParseCsar(Resource resource, User user, String csarUUID, Either<Map<String, byte[]>, StorageOperationStatus> csar) {
+ if (csar.isRight()) {
+ StorageOperationStatus value = csar.right().value();
+ log.debug("Error when fetching csar with ID {}, error: {}", csarUUID, value);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Creating resource from CSAR: fetching CSAR with id " + csarUUID + " failed");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(value), csarUUID);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.CREATE_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateCsarStatus = CsarValidationUtils.validateCsar(csar.left().value(), csarUUID, componentsUtils);
+ if (validateCsarStatus.isRight()) {
+ ResponseFormat responseFormat = validateCsarStatus.right().value();
+ log.debug("Error when validate csar with ID {}, error: {}", csarUUID, responseFormat);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Creating resource from CSAR: fetching CSAR with id " + csarUUID + " failed");
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.CREATE_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+
+ Either<ImmutablePair<String, String>, ResponseFormat> toscaYamlCsarStatus = CsarValidationUtils.getToscaYaml(csar.left().value(), csarUUID, componentsUtils);
+
+ if (toscaYamlCsarStatus.isRight()) {
+ ResponseFormat responseFormat = toscaYamlCsarStatus.right().value();
+ log.debug("Error when try to get csar toscayamlFile with csar ID {}, error: {}", csarUUID, responseFormat);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Creating resource from CSAR: fetching CSAR with id " + csarUUID + " failed");
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.CREATE_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+ return toscaYamlCsarStatus;
+ }
+
+ private Either<Resource, ResponseFormat> validateResourceBeforeCreate(Resource resource, User user, boolean inTransaction) {
+ log.trace("validating resource before create");
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "Create Resource", resource, AuditingActionEnum.CREATE_RESOURCE, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user.copyData(eitherCreator.left().value());
+
+ // validate user role
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, resource, new ArrayList<Role>(), AuditingActionEnum.CREATE_RESOURCE, null);
+ if (validateRes.isRight()) {
+ return Either.right(validateRes.right().value());
+ }
+ // VF "derivedFrom" should be null (or ignored)
+ if (!resource.getResourceType().equals(ResourceTypeEnum.VF)) {
+ Either<Boolean, ResponseFormat> validateDerivedFromNotEmpty = validateDerivedFromNotEmpty(user, resource, AuditingActionEnum.CREATE_RESOURCE);
+ if (validateDerivedFromNotEmpty.isRight()) {
+ return Either.right(validateDerivedFromNotEmpty.right().value());
+ }
+ }
+ return validateResourceBeforeCreate(resource, user, AuditingActionEnum.CREATE_RESOURCE, inTransaction);
+
+ }
+
+ public Either<Resource, ResponseFormat> createResourceFromYaml(Resource resource, User user, String topologyTemplateYaml, String yamlName, Map<String, byte[]> csar, String csarUUID) {
+
+ List<ArtifactDefinition> createdArtifacts = new ArrayList<ArtifactDefinition>();
+ log.trace("************* createResourceFromYaml before parse yaml ");
+ Either<ParsedToscaYamlInfo, ResponseFormat> parseResourceInfoFromYamlEither = parseResourceInfoFromYaml(yamlName, resource, topologyTemplateYaml, user);
+ if (parseResourceInfoFromYamlEither.isRight()) {
+ ResponseFormat responseFormat = parseResourceInfoFromYamlEither.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+ log.trace("************* createResourceFromYaml after parse yaml ");
+ ParsedToscaYamlInfo parsedToscaYamlInfo = parseResourceInfoFromYamlEither.left().value();
+ log.debug("The parsed tosca yaml info is {}", parsedToscaYamlInfo);
+ log.trace("************* createResourceFromYaml before create ");
+ Either<Resource, ResponseFormat> createdResourceResponse = createResourceAndRIsFromYaml(yamlName, resource, user, parsedToscaYamlInfo, AuditingActionEnum.IMPORT_RESOURCE, false, csarUUID, csar, createdArtifacts, topologyTemplateYaml);
+ log.trace("************* createResourceFromYaml after create ");
+ if (createdResourceResponse.isRight()) {
+ ResponseFormat responseFormat = createdResourceResponse.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+
+ return createdResourceResponse;
+
+ }
+
+ public Either<Map<String, Resource>, ResponseFormat> createResourcesFromYamlNodeTypesList(String yamlName, Resource resource, String resourceYml, User user, boolean needLock) {
+
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(resourceYml);
+
+ Either<String, ResultStatusEnum> tosca_version = ImportUtils.findFirstToscaStringElement(mappedToscaTemplate, ToscaTagNamesEnum.TOSCA_VERSION);
+ if (tosca_version.isRight()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_TOSCA_TEMPLATE);
+ return Either.right(responseFormat);
+ }
+
+ Either<Map<String, Object>, ResultStatusEnum> eitherNodeTypes = ImportUtils.findFirstToscaMapElement(mappedToscaTemplate, ToscaTagNamesEnum.NODE_TYPES);
+
+ Map<String, Resource> nodeTypesResources = new HashMap<>();
+ Either<Map<String, Resource>, ResponseFormat> result = Either.left(nodeTypesResources);
+
+ Map<String, Object> mapToConvert = new HashMap<String, Object>();
+ mapToConvert.put(ToscaTagNamesEnum.TOSCA_VERSION.getElementName(), tosca_version.left().value());
+
+ if (eitherNodeTypes.isLeft()) {
+
+ Iterator<Entry<String, Object>> nodesNameValue = eitherNodeTypes.left().value().entrySet().iterator();
+
+ while (nodesNameValue.hasNext()) {
+
+ Entry<String, Object> nodeType = nodesNameValue.next();
+ log.trace("************* Going to create node {}", nodeType.getKey());
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> resourceCreated = this.createNodeTypeResourceFromYaml(yamlName, nodeType, user, mapToConvert, resource, needLock);
+ log.trace("************* finished to create node {}", nodeType.getKey());
+ if (resourceCreated.isRight()) {
+ return Either.right(resourceCreated.right().value());
+ }
+ Resource vfcCreated = resourceCreated.left().value().getLeft();
+
+ nodeTypesResources.put(nodeType.getKey(), vfcCreated);
+ mapToConvert.remove(ToscaTagNamesEnum.NODE_TYPES.getElementName());
+
+ }
+ }
+
+ return result;
+ }
+
+ private String getNodeTypeActualName(String fullName) {
+ String nameWithouNamespacePrefix = fullName.substring(Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX.length());
+ String[] findTypes = nameWithouNamespacePrefix.split("\\.");
+ String resourceType = findTypes[0];
+ return nameWithouNamespacePrefix.substring(resourceType.length());
+ }
+
+ private Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createNodeTypeResourceFromYaml(String yamlName, Entry<String, Object> nodeNameValue, User user, Map<String, Object> mapToConvert, Resource resourceVf, boolean needLock) {
+
+ Either<UploadResourceInfo, ResponseFormat> resourceMetaData = fillResourceMetadata(yamlName, resourceVf, nodeNameValue.getKey(), user);
+ if (resourceMetaData.isRight()) {
+ return Either.right(resourceMetaData.right().value());
+ }
+
+ // We need to create a Yaml from each node_types in order to create
+ // resource from each node type using import normative flow.
+ DumperOptions options = new DumperOptions();
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ Yaml yaml = new Yaml(options);
+
+ Map<String, Object> singleVfc = new HashMap<>();
+
+ String actualName = this.getNodeTypeActualName(nodeNameValue.getKey());
+ if (!actualName.startsWith(ImportUtils.Constants.ABSTRACT_NODE)) {
+ actualName = "." + ImportUtils.Constants.ABSTRACT_NODE + actualName;
+ }
+
+ // Setting tosca name
+ String toscaResourceName = ImportUtils.Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX + resourceMetaData.left().value().getResourceType().toLowerCase() + '.' + resourceVf.getSystemName() + actualName;
+ singleVfc.put(toscaResourceName, nodeNameValue.getValue());
+ mapToConvert.put(ToscaTagNamesEnum.NODE_TYPES.getElementName(), singleVfc);
+
+ String singleVfcYaml = yaml.dumpAsMap(mapToConvert);
+
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "CheckIn Resource", resourceVf, AuditingActionEnum.CHECKIN_RESOURCE, true);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ return this.createResourceFromNodeType(singleVfcYaml, resourceMetaData.left().value(), user, true, needLock);
+ }
+
+ public Either<Boolean, ResponseFormat> validateResourceCreationFromNodeType(Resource resource, User creator) {
+
+ Either<Boolean, ResponseFormat> validateDerivedFromNotEmpty = this.validateDerivedFromNotEmpty(creator, resource, AuditingActionEnum.CREATE_RESOURCE);
+ if (validateDerivedFromNotEmpty.isRight()) {
+ return Either.right(validateDerivedFromNotEmpty.right().value());
+ }
+ return Either.left(true);
+ }
+
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createResourceFromNodeType(String nodeTypeYaml, UploadResourceInfo resourceMetaData, User creator, boolean isInTransaction, boolean needLock) {
+
+ LifecycleChangeInfoWithAction lifecycleChangeInfo = new LifecycleChangeInfoWithAction("certification on import", LifecycleChanceActionEnum.CREATE_FROM_CSAR);
+ Function<Resource, Either<Boolean, ResponseFormat>> validator = (resource) -> this.validateResourceCreationFromNodeType(resource, creator);
+ return this.resourceImportManager.importCertifiedResource(nodeTypeYaml, resourceMetaData, creator, validator, lifecycleChangeInfo, isInTransaction, true, needLock);
+ }
+
+ private Either<UploadResourceInfo, ResponseFormat> fillResourceMetadata(String yamlName, Resource resourceVf, String nodeTypeName, User user) {
+ UploadResourceInfo resourceMetaData = new UploadResourceInfo();
+
+ // validate nodetype name prefix
+ if (!nodeTypeName.startsWith(Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX)) {
+ log.debug("invalid nodeTypeName:{} does not start with {}.", nodeTypeName, Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, resourceMetaData.getName(), nodeTypeName);
+ return Either.right(responseFormat);
+ }
+
+ String actualName = this.getNodeTypeActualName(nodeTypeName);
+ String namePrefix = nodeTypeName.replace(actualName, "");
+ String resourceType = namePrefix.substring(Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX.length());
+
+ // if we import from csar, the node_type name can be
+ // org.openecomp.resource.abstract.node_name - in this case we always
+ // create a vfc
+ if (resourceType.equals(Constants.ABSTRACT)) {
+ resourceType = ResourceTypeEnum.VFC.name().toLowerCase();
+ }
+ // validating type
+ if (!ResourceTypeEnum.contains(resourceType.toUpperCase())) {
+ log.debug("invalid resourceType:{} the type is not one of the valide types:{}.", resourceType.toUpperCase(), ResourceTypeEnum.values());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, resourceMetaData.getName(), nodeTypeName);
+ return Either.right(responseFormat);
+ }
+
+ // Setting name
+ resourceMetaData.setName(resourceVf.getSystemName() + actualName);
+
+ // Setting type from name
+ String type = resourceType.toUpperCase();
+ resourceMetaData.setResourceType(type);
+
+ resourceMetaData.setDescription(ImportUtils.Constants.INNER_VFC_DESCRIPTION);
+ resourceMetaData.setIcon(ImportUtils.Constants.DEFAULT_ICON);
+ resourceMetaData.setContactId(user.getUserId());
+ resourceMetaData.setVendorName(resourceVf.getVendorName());
+ resourceMetaData.setVendorRelease(resourceVf.getVendorRelease());
+
+ // Setting tag
+ List<String> tags = new ArrayList<>();
+ tags.add(resourceMetaData.getName());
+ resourceMetaData.setTags(tags);
+
+ // Setting category
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName(ImportUtils.Constants.ABSTRACT_CATEGORY_NAME);
+ SubCategoryDefinition subCategory = new SubCategoryDefinition();
+ subCategory.setName(ImportUtils.Constants.ABSTRACT_SUBCATEGORY);
+ category.addSubCategory(subCategory);
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ resourceMetaData.setCategories(categories);
+
+ return Either.left(resourceMetaData);
+ }
+
+ private Either<Resource, ResponseFormat> createResourceAndRIsFromYaml(String yamlName, Resource resource, User user, ParsedToscaYamlInfo parsedToscaYamlInfo, AuditingActionEnum actionEnum, boolean isNormative, String csarUUID,
+ Map<String, byte[]> csar, List<ArtifactDefinition> createdArtifacts, String topologyTemplateYaml) {
+
+ boolean result = true;
+
+ Either<Boolean, ResponseFormat> lockResult = lockComponentByName(resource.getSystemName(), resource, "Create Resource");
+ if (lockResult.isRight()) {
+ ResponseFormat responseFormat = lockResult.right().value();
+ return Either.right(responseFormat);
+ }
+ log.debug("name is locked {} status = {}", resource.getSystemName(), lockResult);
+
+ try {
+ log.trace("************* createResourceFromYaml before full create resource {}", yamlName);
+ Either<Resource, ResponseFormat> createResourcesEither = createResourceTransaction(resource, user, actionEnum, isNormative, true);
+ log.trace("************* createResourceFromYaml after full create resource {}", yamlName);
+ if (createResourcesEither.isRight()) {
+ result = false;
+ return createResourcesEither;
+ }
+ resource = createResourcesEither.left().value();
+ // add groups to resource
+ log.trace("************* Going to add inputs from yaml {}", yamlName);
+
+ Map<String, InputDefinition> inputs = parsedToscaYamlInfo.getInputs();
+ Either<Resource, ResponseFormat> createInputsOnResource = createInputsOnResource(resource, user, inputs, true);
+ if (createInputsOnResource.isRight()) {
+ result = false;
+ return createInputsOnResource;
+ }
+ resource = createInputsOnResource.left().value();
+ log.trace("************* Finish to add inputs from yaml {}", yamlName);
+
+ Map<String, UploadComponentInstanceInfo> uploadComponentInstanceInfoMap = parsedToscaYamlInfo.getInstances();
+ log.trace("************* Going to create nodes, RI's and Relations from yaml {}", yamlName);
+ createResourcesEither = createRIAndRelationsFromYaml(yamlName, resource, user, uploadComponentInstanceInfoMap, actionEnum, topologyTemplateYaml, csar, csarUUID);
+ log.trace("************* Finished to create nodes, RI and Relation from yaml {}", yamlName);
+ if (createResourcesEither.isRight()) {
+ result = false;
+ return createResourcesEither;
+ }
+
+ resource = createResourcesEither.left().value();
+ // validate update vf module group names
+ Either<Map<String, GroupDefinition>, ResponseFormat> validateUpdateVfGroupNamesRes = groupBusinessLogic.validateUpdateVfGroupNames(parsedToscaYamlInfo.getGroups(), resource.getSystemName());
+ if (validateUpdateVfGroupNamesRes.isRight()) {
+ result = false;
+ return Either.right(validateUpdateVfGroupNamesRes.right().value());
+ }
+ // add groups to resource
+ Map<String, GroupDefinition> groups;
+ log.trace("************* Going to add groups from yaml {}", yamlName);
+
+ if (!validateUpdateVfGroupNamesRes.left().value().isEmpty()) {
+ groups = validateUpdateVfGroupNamesRes.left().value();
+ } else {
+ groups = parsedToscaYamlInfo.getGroups();
+ }
+ Either<Resource, ResponseFormat> createGroupsOnResource = createGroupsOnResource(resource, user, groups);
+ if (createGroupsOnResource.isRight()) {
+ result = false;
+ return createGroupsOnResource;
+ }
+ resource = createGroupsOnResource.left().value();
+ log.trace("************* Finished to add groups from yaml {}", yamlName);
+
+ log.trace("************* Going to add artifacts from yaml {}", yamlName);
+ Either<Resource, ResponseFormat> createdCsarArtifactsEither = this.handleCsarArtifacts(resource, user, csarUUID, csar, createdArtifacts, ArtifactOperation.Create, false, true);
+ log.trace("************* Finished to add artifacts from yaml {}", yamlName);
+ if (createdCsarArtifactsEither.isRight()) {
+ result = false;
+ return createdCsarArtifactsEither;
+ }
+ resource = createdCsarArtifactsEither.left().value();
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ ASDCKpiApi.countCreatedResourcesKPI();
+ return Either.left(resource);
+
+ } finally {
+ if (!result) {
+ log.warn("operation failed. do rollback");
+ titanGenericDao.rollback();
+ if (!createdArtifacts.isEmpty()) {
+ StorageOperationStatus deleteFromEsRes = artifactsBusinessLogic.deleteAllComponentArtifactsIfNotOnGraph(createdArtifacts);
+ if (!deleteFromEsRes.equals(StorageOperationStatus.OK)) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(deleteFromEsRes);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(actionStatus, resource.getName());
+ }
+ log.debug("component and all its artifacts were deleted, id = {}", resource.getName());
+ }
+
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+
+ graphLockOperation.unlockComponentByName(resource.getSystemName(), resource.getUniqueId(), NodeTypeEnum.Resource);
+
+ }
+
+ }
+
+ private Either<Resource, ResponseFormat> createGroupsOnResource(Resource resource, User user, Map<String, GroupDefinition> groups) {
+ if (groups != null && false == groups.isEmpty()) {
+ Either<List<GroupDefinition>, ResponseFormat> mergeGroupsUsingResource = updateGroupMembersUsingResource(groups, resource);
+
+ if (mergeGroupsUsingResource.isRight()) {
+ log.debug("Failed to prepare groups for creation");
+ return Either.right(mergeGroupsUsingResource.right().value());
+ }
+ List<GroupDefinition> groupsAsList = mergeGroupsUsingResource.left().value();
+
+ // Either<List<GroupDefinition>, ResponseFormat> createGroups =
+ // groupBusinessLogic.createGroups(
+ // resource.getUniqueId(), user.getUserId(),
+ // ComponentTypeEnum.RESOURCE, groupsAsList, false,
+ // true);
+ // In this method we assume all instances exists in resource
+ Either<List<GroupDefinition>, ResponseFormat> createGroups = groupBusinessLogic.createGroups(resource, user, ComponentTypeEnum.RESOURCE, groupsAsList, true);
+ if (createGroups.isRight()) {
+ return Either.right(createGroups.right().value());
+ }
+ } else {
+ return Either.left(resource);
+ }
+
+ Either<Resource, StorageOperationStatus> updatedResource = resourceOperation.getResource(resource.getUniqueId(), true);
+ if (updatedResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(updatedResource.right().value()), resource);
+ return Either.right(responseFormat);
+ }
+ return Either.left(updatedResource.left().value());
+ }
+
+ private Either<Resource, ResponseFormat> updateGroupsOnResource(Resource resource, User user, Map<String, GroupDefinition> groups) {
+ if (groups != null && false == groups.isEmpty()) {
+ List<GroupDefinition> groupsFromResource = resource.getGroups();
+ Either<List<GroupDefinition>, ResponseFormat> mergeGroupsUsingResource = updateGroupMembersUsingResource(groups, resource);
+
+ if (mergeGroupsUsingResource.isRight()) {
+ log.debug("Failed to prepare groups for creation");
+ return Either.right(mergeGroupsUsingResource.right().value());
+ }
+ List<GroupDefinition> groupsAsList = mergeGroupsUsingResource.left().value();
+ List<GroupDefinition> groupsToUpdate = new ArrayList<GroupDefinition>();
+ List<GroupDefinition> groupsToDelete = new ArrayList<GroupDefinition>();
+ List<GroupDefinition> groupsToCreate = new ArrayList<GroupDefinition>();
+ if (groupsFromResource != null && !groupsFromResource.isEmpty()) {
+ for (GroupDefinition group : groupsAsList) {
+ Optional<GroupDefinition> op = groupsFromResource.stream().filter(p -> p.getName().equals(group.getName())).findAny();
+ if (op.isPresent()) {
+ GroupDefinition groupToUpdate = op.get();
+ groupToUpdate.setMembers(group.getMembers());
+ groupsToUpdate.add(groupToUpdate);
+ } else {
+ groupsToCreate.add(group);
+ }
+ }
+ for (GroupDefinition group : groupsFromResource) {
+ Optional<GroupDefinition> op = groupsAsList.stream().filter(p -> p.getName().equals(group.getName())).findAny();
+ if (!op.isPresent() && (group.getArtifacts() == null || group.getArtifacts().isEmpty())) {
+
+ groupsToDelete.add(group);
+ }
+
+ }
+ } else
+ groupsToCreate.addAll(groupsAsList);
+
+ if (!groupsToCreate.isEmpty()) {
+ Either<List<GroupDefinition>, ResponseFormat> createGroups = groupBusinessLogic.createGroups(resource, user, ComponentTypeEnum.RESOURCE, groupsToCreate, true);
+
+ if (createGroups.isRight()) {
+ return Either.right(createGroups.right().value());
+ }
+ }
+
+ if (!groupsToDelete.isEmpty()) {
+ for (GroupDefinition group : groupsToDelete) {
+ Either<GroupDefinition, StorageOperationStatus> deleteGroupEither = groupOperation.deleteGroup(group.getUniqueId(), true);
+ if (deleteGroupEither.isRight()) {
+ StorageOperationStatus storageOperationStatus = deleteGroupEither.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageOperationStatus);
+ log.debug("Failed to delete group {} under component {}, error: {}", group.getUniqueId(), resource.getNormalizedName(), actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+ }
+ }
+ }
+ if (groupsToUpdate != null && !groupsToUpdate.isEmpty()) {
+ Either<List<GroupDefinition>, ResponseFormat> assotiateGroupEither = groupBusinessLogic.associateMembersToGroup(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, groupsToUpdate, false, true);
+ if (assotiateGroupEither.isRight()) {
+ log.debug("Failed to associate artifacts to groups. Status is {} ", assotiateGroupEither.right().value());
+ return Either.right(assotiateGroupEither.right().value());
+
+ }
+ List<GroupDefinition> updatedGroups = assotiateGroupEither.left().value();
+ List<String> groupsId = updatedGroups.stream().map(e -> e.getUniqueId()).collect(Collectors.toList());
+
+ Either<List<GroupDefinition>, StorageOperationStatus> updateVersionEither = groupBusinessLogic.updateGroupVersion(groupsId, true);
+ if (updateVersionEither.isRight()) {
+ log.debug("Failed to update groups version. Status is {} ", updateVersionEither.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(updateVersionEither.right().value()), resource);
+ return Either.right(responseFormat);
+
+ }
+ }
+
+ } else {
+ return Either.left(resource);
+ }
+
+ Either<Resource, StorageOperationStatus> updatedResource = resourceOperation.getResource(resource.getUniqueId(), true);
+ if (updatedResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(updatedResource.right().value()), resource);
+ return Either.right(responseFormat);
+ }
+ return Either.left(updatedResource.left().value());
+ }
+
+ private Either<Resource, ResponseFormat> createInputsOnResource(Resource resource, User user, Map<String, InputDefinition> inputs, boolean inTransaction) {
+ if (inputs != null && false == inputs.isEmpty()) {
+ List<InputDefinition> inputsAsList = new ArrayList<InputDefinition>();
+ for (Entry<String, InputDefinition> entry : inputs.entrySet()) {
+ InputDefinition input = entry.getValue();
+ input.setName(entry.getKey());
+ inputsAsList.add(input);
+ }
+ Either<List<InputDefinition>, ResponseFormat> createGroups = inputsBusinessLogic.createInputsInGraph(inputsAsList, resource, user, inTransaction);
+ if (createGroups.isRight()) {
+ return Either.right(createGroups.right().value());
+ }
+ } else {
+ return Either.left(resource);
+ }
+
+ Either<Resource, StorageOperationStatus> updatedResource = resourceOperation.getResource(resource.getUniqueId(), true);
+ if (updatedResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(updatedResource.right().value()), resource);
+ return Either.right(responseFormat);
+ }
+ return Either.left(updatedResource.left().value());
+ }
+
+ private Either<List<GroupDefinition>, ResponseFormat> updateGroupMembersUsingResource(Map<String, GroupDefinition> groups, Resource component) {
+
+ List<GroupDefinition> result = new ArrayList<>();
+
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+
+ if (groups != null) {
+ for (Entry<String, GroupDefinition> entry : groups.entrySet()) {
+ String groupName = entry.getKey();
+
+ GroupDefinition groupDefinition = entry.getValue();
+
+ GroupDefinition updatedGroupDefinition = new GroupDefinition(groupDefinition);
+ updatedGroupDefinition.setMembers(null);
+
+ // get the members of the group
+ Map<String, String> members = groupDefinition.getMembers();
+ if (members != null) {
+ Set<String> compInstancesNames = members.keySet();
+
+ if (componentInstances == null || true == componentInstances.isEmpty()) {
+ String membersAstString = compInstancesNames.stream().collect(Collectors.joining(","));
+ log.debug("The members {} in group {} cannot be found in component {}. There are no component instances", membersAstString, groupName, component.getNormalizedName());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_INVALID_COMPONENT_INSTANCE, membersAstString, groupName, component.getNormalizedName(), getComponentTypeForResponse(component)));
+ }
+ // Find all component instances with the member names
+ Map<String, String> memberNames = componentInstances.stream().collect(Collectors.toMap(ComponentInstance::getName, ComponentInstance::getUniqueId));
+ memberNames.putAll(groups.keySet().stream().collect(Collectors.toMap(g -> g, g -> "")));
+ Map<String, String> relevantInstances = memberNames.entrySet().stream().filter(n -> compInstancesNames.contains(n.getKey())).collect(Collectors.toMap(n -> n.getKey(), n -> n.getValue()));
+
+ if (relevantInstances == null || relevantInstances.size() != compInstancesNames.size()) {
+
+ List<String> foundMembers = new ArrayList<>();
+ if (relevantInstances != null) {
+ foundMembers = relevantInstances.keySet().stream().collect(Collectors.toList());
+ }
+ compInstancesNames.removeAll(foundMembers);
+ String membersAstString = compInstancesNames.stream().collect(Collectors.joining(","));
+ log.debug("The members {} in group {} cannot be found in component {}", membersAstString, groupName, component.getNormalizedName());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_INVALID_COMPONENT_INSTANCE, membersAstString, groupName, component.getNormalizedName(), getComponentTypeForResponse(component)));
+ }
+
+ updatedGroupDefinition.setMembers(relevantInstances);
+ }
+
+ result.add(updatedGroupDefinition);
+ }
+ }
+ return Either.left(result);
+ }
+
+ private Either<Resource, ResponseFormat> createRIAndRelationsFromYaml(String yamlName, Resource resource, User user, Map<String, UploadComponentInstanceInfo> uploadComponentInstanceInfoMap, AuditingActionEnum actionEnum,
+ String topologyTemplateYaml, Map<String, byte[]> csar, String csarUUID) {
+
+ Either<Resource, ResponseFormat> result;
+ Either<Resource, ResponseFormat> createResourcesInstancesEither;
+ log.debug("************* Going to create all nodes {}", yamlName);
+ Either<Map<String, Resource>, ResponseFormat> createdResourcesFromdNodeTypeMap = this.handleNodeTypes(yamlName, resource, user, topologyTemplateYaml, csar, false);
+ log.debug("************* Finished to create all nodes {}", yamlName);
+ if (createdResourcesFromdNodeTypeMap.isRight()) {
+ log.debug("failed to resources from node types status is {}", createdResourcesFromdNodeTypeMap.right().value());
+ return Either.right(createdResourcesFromdNodeTypeMap.right().value());
+ }
+
+ log.debug("************* Going to create all resource instances {}", yamlName);
+ createResourcesInstancesEither = createResourceInstances(user, yamlName, resource, uploadComponentInstanceInfoMap, true, false, createdResourcesFromdNodeTypeMap.left().value());
+
+ log.debug("************* Finished to create all resource instances {}", yamlName);
+ if (createResourcesInstancesEither.isRight()) {
+ log.debug("failed to create resource instances status is {}", createResourcesInstancesEither.right().value());
+ result = createResourcesInstancesEither;
+ return createResourcesInstancesEither;
+ }
+ resource = createResourcesInstancesEither.left().value();
+ log.debug("************* Going to create all relations {}", yamlName);
+ createResourcesInstancesEither = createResourceInstancesRelations(user, yamlName, resource, uploadComponentInstanceInfoMap, true, false);
+
+ log.debug("************* Finished to create all relations {}", yamlName);
+
+ if (createResourcesInstancesEither.isRight()) {
+ log.debug("failed to create relation between resource instances status is {}", createResourcesInstancesEither.right().value());
+ result = createResourcesInstancesEither;
+ return result;
+ } else {
+ resource = createResourcesInstancesEither.left().value();
+ }
+
+ log.debug("************* Going to create positions {}", yamlName);
+ Either<List<ComponentInstance>, ResponseFormat> eitherSetPosition = compositionBusinessLogic.setPositionsForComponentInstances(resource, user.getUserId());
+ log.debug("************* Finished to set positions {}", yamlName);
+ result = eitherSetPosition.isRight() ? Either.right(eitherSetPosition.right().value()) : Either.left(resource);
+
+ return result;
+ }
+
+ private Either<Map<String, Resource>, ResponseFormat> handleNodeTypes(String yamlName, Resource resource, User user, String topologyTemplateYaml, Map<String, byte[]> csar, boolean needLock) {
+
+ Map<String, Resource> createdResourcesFromdNodeTypeMap = new HashMap<>();
+ Either<Map<String, Resource>, ResponseFormat> result = Either.left(createdResourcesFromdNodeTypeMap);
+
+ String yamlFileName = Constants.GLOBAL_SUBSTITUTION_TYPE_SERVICE_TEMPLATE;
+
+ if (csar != null && csar.containsKey(yamlFileName)) {
+ byte[] yamlFileBytes = csar.get(yamlFileName);
+ String globalTypesYaml = new String(yamlFileBytes, StandardCharsets.UTF_8);
+ Either<Map<String, Resource>, ResponseFormat> createdNodeTypesFromGlobalTypesTemplateEither = this.createResourcesFromYamlNodeTypesList(yamlFileName, resource, globalTypesYaml, user, needLock);
+ if (createdNodeTypesFromGlobalTypesTemplateEither.isRight()) {
+ ResponseFormat responseFormat = createdNodeTypesFromGlobalTypesTemplateEither.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+ createdResourcesFromdNodeTypeMap.putAll(createdNodeTypesFromGlobalTypesTemplateEither.left().value());
+ }
+
+ Either<Map<String, Resource>, ResponseFormat> createdNodeTypeFromMainTemplateEither = createResourcesFromYamlNodeTypesList(yamlName, resource, topologyTemplateYaml, user, needLock);
+ if (createdNodeTypeFromMainTemplateEither.isRight()) {
+ ResponseFormat responseFormat = createdNodeTypeFromMainTemplateEither.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+
+ createdResourcesFromdNodeTypeMap.putAll(createdNodeTypeFromMainTemplateEither.left().value());
+
+ // add the created node types to the cache although they are not in the
+ // graph.
+ createdResourcesFromdNodeTypeMap.values().stream().forEach(p -> cacheManagerOperation.storeComponentInCache(p, NodeTypeEnum.Resource));
+
+ return result;
+ }
+
+ private Either<Resource, ResponseFormat> handleCsarArtifacts(Resource resource, User user, String csarUUID, Map<String, byte[]> csar, List<ArtifactDefinition> createdArtifacts, ArtifactOperation artifactOperation, boolean shouldLock,
+ boolean inTransaction) {
+
+ if (csar != null) {
+ String vendorLicenseModelId = null;
+ String vfLicenseModelId = null;
+
+ if (artifactOperation.equals(ArtifactOperation.Update)) {
+ Map<String, ArtifactDefinition> deploymentArtifactsMap = resource.getDeploymentArtifacts();
+ if (deploymentArtifactsMap != null && !deploymentArtifactsMap.isEmpty()) {
+ for (Entry<String, ArtifactDefinition> artifactEntry : deploymentArtifactsMap.entrySet()) {
+ if (artifactEntry.getValue().getArtifactName().equalsIgnoreCase(Constants.VENDOR_LICENSE_MODEL))
+ vendorLicenseModelId = artifactEntry.getValue().getUniqueId();
+ if (artifactEntry.getValue().getArtifactName().equalsIgnoreCase(Constants.VF_LICENSE_MODEL))
+ vfLicenseModelId = artifactEntry.getValue().getUniqueId();
+ }
+ }
+
+ }
+ createOrUpdateLicenseArtifact(resource, user, csarUUID, csar, Constants.VENDOR_LICENSE_MODEL, ArtifactTypeEnum.VENDOR_LICENSE.getType(), Constants.VENDOR_LICENSE_LABEL, Constants.VENDOR_LICENSE_DISPLAY_NAME,
+ Constants.VENDOR_LICENSE_DESCRIPTION, vendorLicenseModelId, artifactOperation, shouldLock, inTransaction);
+ createOrUpdateLicenseArtifact(resource, user, csarUUID, csar, Constants.VF_LICENSE_MODEL, ArtifactTypeEnum.VF_LICENSE.getType(), Constants.VF_LICENSE_LABEL, Constants.VF_LICENSE_DISPLAY_NAME, Constants.VF_LICENSE_DESCRIPTION,
+ vfLicenseModelId, artifactOperation, shouldLock, inTransaction);
+ Either<ImmutablePair<String, String>, ResponseFormat> artifacsMetaCsarStatus = CsarValidationUtils.getArtifactsMeta(csar, csarUUID, componentsUtils);
+ if (artifacsMetaCsarStatus.isLeft()) {
+
+ String artifactsFileName = artifacsMetaCsarStatus.left().value().getKey();
+ String artifactsContents = artifacsMetaCsarStatus.left().value().getValue();
+ Either<Resource, ResponseFormat> createArtifactsFromCsar = Either.left(resource);
+ if (artifactOperation.equals(ArtifactOperation.Create))
+ createArtifactsFromCsar = createResourceArtifactsFromCsar(csarUUID, csar, resource, user, artifactsContents, artifactsFileName, createdArtifacts, shouldLock, inTransaction);
+ else
+ createArtifactsFromCsar = updateResourceArtifactsFromCsar(csarUUID, csar, resource, user, artifactsContents, artifactsFileName, createdArtifacts, shouldLock, inTransaction);
+ if (createArtifactsFromCsar.isRight()) {
+ log.debug("Couldn't create artifacts from artifacts.meta");
+ return Either.right(createArtifactsFromCsar.right().value());
+ }
+
+ resource = createArtifactsFromCsar.left().value();
+ } else {
+ List<GroupDefinition> groupsToDelete = resource.getGroups();
+
+ if (groupsToDelete != null && !groupsToDelete.isEmpty()) {
+ Set<String> artifactsToDelete = new HashSet<String>();
+ for (GroupDefinition group : groupsToDelete) {
+ List<String> artifacts = group.getArtifacts();
+ if (artifacts != null) {
+ artifactsToDelete.addAll(artifacts);
+ Either<GroupDefinition, StorageOperationStatus> deleteGroupEither = groupOperation.deleteGroup(group.getUniqueId(), inTransaction);
+ if (deleteGroupEither.isRight()) {
+ StorageOperationStatus storageOperationStatus = deleteGroupEither.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageOperationStatus);
+ log.debug("Failed to delete group {} under component {}, error: {}", group.getUniqueId(), resource.getNormalizedName(), actionStatus.name());
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+ }
+ }
+ }
+ for (String artifactId : artifactsToDelete) {
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDelete = artifactsBusinessLogic.handleDelete(resource.getUniqueId(), artifactId, user, AuditingActionEnum.ARTIFACT_DELETE, ComponentTypeEnum.RESOURCE,
+ resource, null, null, shouldLock, inTransaction);
+ if (handleDelete.isRight()) {
+ log.debug("Couldn't delete artifact {}", artifactId);
+ return Either.right(handleDelete.right().value());
+ }
+
+ }
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getResource(resource.getUniqueId(), inTransaction);
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ return Either.right(responseFormat);
+
+ }
+ resource = eitherGerResource.left().value();
+ }
+ }
+ }
+ return Either.left(resource);
+ }
+
+ private Either<Boolean, ResponseFormat> createOrUpdateLicenseArtifact(Resource resource, User user, String csarUUID, Map<String, byte[]> csar, String artifactFileName, String artifactType, String artifactLabel, String artifactDisplayName,
+ String artifactDescription, String artifactId, ArtifactOperation operation, boolean shouldLock, boolean inTransaction) {
+ byte[] artifactFileBytes = null;
+
+ if (csar.containsKey(Constants.ARTIFACTS + artifactFileName)) {
+ artifactFileBytes = csar.get(Constants.ARTIFACTS + artifactFileName);
+ }
+ Either<Boolean, ResponseFormat> result = Either.left(true);
+ if (operation.equals(ArtifactOperation.Update)) {
+ if (artifactId != null && !artifactId.isEmpty() && artifactFileBytes == null) {
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDelete = artifactsBusinessLogic.handleDelete(resource.getUniqueId(), artifactId, user, AuditingActionEnum.ARTIFACT_DELETE, ComponentTypeEnum.RESOURCE, resource, null,
+ null, shouldLock, inTransaction);
+ if (handleDelete.isRight()) {
+ result = Either.right(handleDelete.right().value());
+ }
+ return result;
+ }
+
+ if ((artifactId == null || artifactId.isEmpty()) && artifactFileBytes != null) {
+ operation = ArtifactOperation.Create;
+ }
+
+ }
+ if (artifactFileBytes != null) {
+ Map<String, Object> vendorLicenseModelJson = buildJsonForUpdateArtifact(artifactId, artifactFileName, artifactType, artifactLabel, artifactDisplayName, artifactDescription, artifactFileBytes, null);
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> vfLicenceArtifactCreateEither = createOrUpdateCsarArtifactFromJson(resource, user, vendorLicenseModelJson, operation, shouldLock, inTransaction);
+
+ if (vfLicenceArtifactCreateEither.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("UploadLicenseArtifact", "Failed to upload license artifact: " + artifactFileName + "With csar uuid: " + csarUUID, ErrorSeverity.WARNING);
+ return Either.right(vfLicenceArtifactCreateEither.right().value());
+ }
+ }
+ return result;
+ }
+
+ private Either<Either<ArtifactDefinition, Operation>, ResponseFormat> createOrUpdateCsarArtifactFromJson(Resource resource, User user, Map<String, Object> json, ArtifactOperation operation, boolean shoudLock, boolean inTransaction) {
+
+ String jsonStr = gson.toJson(json);
+
+ String origMd5 = GeneralUtility.calculateMD5ByString(jsonStr);
+ ArtifactDefinition artifactDefinitionFromJson = RepresentationUtils.convertJsonToArtifactDefinition(jsonStr, ArtifactDefinition.class);
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> uploadArtifactToService = artifactsBusinessLogic.handleArtifactRequest(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, operation,
+ artifactDefinitionFromJson.getUniqueId(), artifactDefinitionFromJson, origMd5, jsonStr, null, null, null, null, shoudLock, inTransaction);
+ if (uploadArtifactToService.isRight())
+ return Either.right(uploadArtifactToService.right().value());
+
+ return Either.left(uploadArtifactToService.left().value());
+ }
+
+ public Either<Resource, ResponseFormat> updateResourceArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, String artifactsMetaFile, String artifactsMetaFileName, List<ArtifactDefinition> createdNewArtifacts,
+ boolean shouldLock, boolean inTransaction) {
+
+ Either<Map<String, List<ArtifactTemplateInfo>>, ResponseFormat> parseResourceInfoFromYamlEither = parseResourceArtifactsInfoFromFile(resource, artifactsMetaFile, artifactsMetaFileName, user);
+ if (parseResourceInfoFromYamlEither.isRight()) {
+ ResponseFormat responseFormat = parseResourceInfoFromYamlEither.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+
+ List<GroupDefinition> groups = resource.getGroups();
+ Map<String, ArtifactDefinition> deplymentArtifact = resource.getDeploymentArtifacts();
+ List<ArtifactDefinition> createdDeplymentArtifactsAfterDelete = new ArrayList<ArtifactDefinition>();
+ if (deplymentArtifact != null && !deplymentArtifact.isEmpty()) {
+ for (Entry<String, ArtifactDefinition> entry : deplymentArtifact.entrySet()) {
+ createdDeplymentArtifactsAfterDelete.add(entry.getValue());
+ }
+ }
+ int labelCounter = createdDeplymentArtifactsAfterDelete.size();
+
+ if (deplymentArtifact == null || deplymentArtifact.isEmpty()) {
+ if (groups != null && !groups.isEmpty()) {
+ for (GroupDefinition group : groups) {
+ if (group.getArtifacts() != null && !group.getArtifacts().isEmpty()) {
+ log.debug("failed to update artifacts from csar. List of emty but group not empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return Either.right(responseFormat);
+ }
+ }
+ }
+ return createResourceArtifacts(csarUUID, csar, resource, user, parseResourceInfoFromYamlEither.left().value(), AuditingActionEnum.CREATE_RESOURCE, createdNewArtifacts, shouldLock, inTransaction);
+ }
+ // find artifacts to delete
+ Set<String> artifactNotInGroupSet = findArtifactsNotInGroupToDelete(groups, createdDeplymentArtifactsAfterDelete);
+
+ // delete all artifacts which not in groups
+ if (!artifactNotInGroupSet.isEmpty()) {
+ for (String artifactId : artifactNotInGroupSet) {
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDelete = artifactsBusinessLogic.handleDelete(resource.getUniqueId(), artifactId, user, AuditingActionEnum.ARTIFACT_DELETE, ComponentTypeEnum.RESOURCE, resource, null,
+ null, shouldLock, inTransaction);
+ if (handleDelete.isRight()) {
+ return Either.right(handleDelete.right().value());
+ }
+
+ }
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getResource(resource.getUniqueId(), inTransaction);
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ return Either.right(responseFormat);
+
+ }
+
+ resource = eitherGerResource.left().value();
+ deplymentArtifact = resource.getDeploymentArtifacts();
+
+ createdDeplymentArtifactsAfterDelete.clear();
+ if (deplymentArtifact != null && !deplymentArtifact.isEmpty()) {
+ for (Entry<String, ArtifactDefinition> entry : deplymentArtifact.entrySet()) {
+ createdDeplymentArtifactsAfterDelete.add(entry.getValue());
+ }
+ }
+ }
+
+ // find master in group
+ Map<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>> groupArtifact = findMasterArtifactInGroup(groups, deplymentArtifact);
+
+ ////////////////////////////////////// create set parsed
+ ////////////////////////////////////// artifacts///////////////////////////////////////////
+ Map<String, List<ArtifactTemplateInfo>> parsedArtifactsMap = parseResourceInfoFromYamlEither.left().value();
+ Collection<List<ArtifactTemplateInfo>> parsedArifactsCollection = parsedArtifactsMap.values();
+ Map<ArtifactTemplateInfo, Set<ArtifactTemplateInfo>> parsedGroup = new HashMap<ArtifactTemplateInfo, Set<ArtifactTemplateInfo>>();
+
+ for (List<ArtifactTemplateInfo> parsedGroupTemplateList : parsedArifactsCollection) {
+ for (ArtifactTemplateInfo parsedGroupTemplate : parsedGroupTemplateList) {
+ parsedGroupTemplate.setGroupName("");
+ Set<ArtifactTemplateInfo> parsedArtifactsNames = new HashSet<ArtifactTemplateInfo>();
+ parsedArtifactsNames.add(parsedGroupTemplate);
+ List<ArtifactTemplateInfo> relatedGroupTemplateList = parsedGroupTemplate.getRelatedArtifactsInfo();
+ if (relatedGroupTemplateList != null && !relatedGroupTemplateList.isEmpty()) {
+ createArtifactsGroupSet(parsedGroupTemplateList, parsedArtifactsNames);
+ }
+ parsedGroup.put(parsedGroupTemplate, parsedArtifactsNames);
+ }
+ }
+
+ ///////////////////////////////// find artifacts to
+ ///////////////////////////////// delete////////////////////////////////////////////////////
+
+ Set<ArtifactDefinition> artifactsToDelete = new HashSet<ArtifactDefinition>();
+ Map<String, List<ArtifactDefinition>> groupToDelete = new HashMap<String, List<ArtifactDefinition>>();
+ Map<String, List<String>> dissocArtifactFromGroup = new HashMap<String, List<String>>();
+
+ Set<ArtifactTemplateInfo> jsonMasterArtifacts = parsedGroup.keySet();
+
+ Map<GroupDefinition, MergedArtifactInfo> mergedgroup = mergeGroupInUpdateFlow(groupArtifact, parsedGroup, artifactsToDelete, groupToDelete, jsonMasterArtifacts);
+
+ // find artifacts to delete
+ for (Entry<String, List<ArtifactDefinition>> deleteGroup : groupToDelete.entrySet()) {
+
+ List<ArtifactDefinition> artifacts = deleteGroup.getValue();
+ for (ArtifactDefinition artifact : artifacts) {
+ findArtifactToDelete(parsedGroup, artifactsToDelete, deleteGroup.getKey(), artifact);
+ }
+
+ }
+
+ // Set<String> deletedArtifactsName = new HashSet<String>();
+ Either<List<ArtifactDefinition>, ResponseFormat> deletedArtifactsEither = deleteArtifactsInUpdateCsarFlow(resource, user, shouldLock, inTransaction, artifactsToDelete, groupToDelete);
+ if (deletedArtifactsEither.isRight()) {
+ log.debug("Failed to delete artifacts. Status is {} ", deletedArtifactsEither.right().value());
+
+ return Either.right(deletedArtifactsEither.right().value());
+
+ }
+ List<ArtifactDefinition> deletedArtifacts = deletedArtifactsEither.left().value();
+
+ // need to update resource if we updated artifacts
+ if (deletedArtifacts != null && !deletedArtifacts.isEmpty()) {
+ for (ArtifactDefinition deletedArtifact : deletedArtifacts) {
+ ArtifactDefinition artToRemove = null;
+ for (ArtifactDefinition artFromResource : createdDeplymentArtifactsAfterDelete) {
+ if (deletedArtifact.getUniqueId().equalsIgnoreCase(artFromResource.getUniqueId())) {
+ artToRemove = artFromResource;
+ break;
+ }
+ }
+ if (artToRemove != null)
+ createdDeplymentArtifactsAfterDelete.remove(artToRemove);
+
+ }
+ }
+
+ ////////////// dissociate, associate or create
+ ////////////// artifacts////////////////////////////
+ Either<Resource, ResponseFormat> assDissotiateEither = associateAndDissociateArtifactsToGroup(csarUUID, csar, resource, user, createdNewArtifacts, labelCounter, shouldLock, inTransaction, createdDeplymentArtifactsAfterDelete,
+ dissocArtifactFromGroup, mergedgroup, deletedArtifacts);
+
+ if (assDissotiateEither.isRight()) {
+ log.debug("Failed to delete artifacts. Status is {} ", assDissotiateEither.right().value());
+
+ return Either.right(assDissotiateEither.right().value());
+
+ }
+ resource = assDissotiateEither.left().value();
+ groups = resource.getGroups();
+ List<GroupDefinition> groupToUpdate = new ArrayList<>();
+ // update vfModule names
+ Set<GroupDefinition> groupForAssociateWithMembers = mergedgroup.keySet();
+ if (groups != null && !groups.isEmpty()) {
+ Either<List<GroupDefinition>, ResponseFormat> validateUpdateVfGroupNamesRes = groupBusinessLogic.validateUpdateVfGroupNamesOnGraph(groups, resource.getSystemName(), inTransaction);
+ if (validateUpdateVfGroupNamesRes.isRight()) {
+ return Either.right(validateUpdateVfGroupNamesRes.right().value());
+ }
+ List<GroupDefinition> heatGroups = null;
+
+ // List<IArtifactInfo> collect = resources.stream().flatMap( e ->
+ // e.getArtifacts().stream()).filter(p ->
+ // relevantArtifactTypes.contains(p.getArtifactType()
+ // )).collect(Collectors.toList());
+ // List<GroupDefinition> heatGroups = createdGroups.stream().filter(
+ // e -> e.getProperties().stream().filter(p ->
+ // p.getName().contains(Constants.HEAT_FILE_PROPS))).collect(Collectors.toList());
+ heatGroups = groups.stream().filter(e -> e.getMembers() != null).collect(Collectors.toList());
+ ;
+
+ for (GroupDefinition updatedGroupDef : groupForAssociateWithMembers) {
+ GroupDefinition group = null;
+ Optional<GroupDefinition> opGr = groups.stream().filter(p -> p.getUniqueId().equals(updatedGroupDef.getUniqueId())).findAny();
+ if (opGr.isPresent()) {
+ group = opGr.get();
+ groupToUpdate.add(group);
+ }
+ if (group != null) {
+ Map<String, String> members = new HashMap<String, String>();
+ Set<String> artifactsGroup = new HashSet<String>();
+ artifactsGroup.addAll(group.getArtifacts());
+ associateMembersTToArtifacts(createdNewArtifacts, createdDeplymentArtifactsAfterDelete, heatGroups, artifactsGroup, members);
+ if (!members.isEmpty()) {
+ group.setMembers(members);
+
+ }
+ }
+
+ }
+ if (!groupToUpdate.isEmpty()) {
+ Either<List<GroupDefinition>, ResponseFormat> assotiateGroupEither = groupBusinessLogic.associateMembersToGroup(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, groupToUpdate, false, true);
+ if (assotiateGroupEither.isRight()) {
+ log.debug("Failed to associate artifacts to groups. Status is {} ", assotiateGroupEither.right().value());
+ return Either.right(assotiateGroupEither.right().value());
+
+ }
+ }
+
+ }
+
+ //////////////// create new artifacts in update
+ //////////////// flow////////////////////////////
+ List<ArtifactTemplateInfo> newArtifactsGroup = new ArrayList<ArtifactTemplateInfo>();
+
+ for (Entry<ArtifactTemplateInfo, Set<ArtifactTemplateInfo>> parsedGroupSetEntry : parsedGroup.entrySet()) {
+ ArtifactTemplateInfo parsedArtifactMaster = parsedGroupSetEntry.getKey();
+ boolean isNewGroup = true;
+ for (Entry<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>> groupListEntry : groupArtifact.entrySet()) {
+ Map<ArtifactDefinition, List<ArtifactDefinition>> groupArtifacts = groupListEntry.getValue();
+ Set<ArtifactDefinition> group = groupArtifacts.keySet();
+ for (ArtifactDefinition artifactInfo : group) {
+ if (parsedArtifactMaster.getFileName().equalsIgnoreCase(artifactInfo.getArtifactName())) {
+ parsedArtifactMaster.setGroupName(groupListEntry.getKey().getName());
+ isNewGroup = false;
+ }
+ }
+ }
+ if (isNewGroup)
+ newArtifactsGroup.add(parsedArtifactMaster);
+
+ }
+ if (!newArtifactsGroup.isEmpty()) {
+ Collections.sort(newArtifactsGroup, (art1, art2) -> ArtifactTemplateInfo.compareByGroupName(art1, art2));
+ int startGroupCounter = groupBusinessLogic.getNextVfModuleNameCounter(groups);
+ Either<Boolean, ResponseFormat> validateGroupNamesRes = groupBusinessLogic.validateGenerateVfModuleGroupNames(newArtifactsGroup, resource.getSystemName(), startGroupCounter);
+ if (validateGroupNamesRes.isRight()) {
+ return Either.right(validateGroupNamesRes.right().value());
+ }
+ Either<Resource, ResponseFormat> resStatus = createGroupDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, newArtifactsGroup, createdNewArtifacts, createdDeplymentArtifactsAfterDelete, labelCounter, shouldLock, inTransaction);
+ if (resStatus.isRight())
+ return resStatus;
+ }
+
+ // updatedGroup
+ if (!groupForAssociateWithMembers.isEmpty()) {
+
+ List<String> groupsId = groupForAssociateWithMembers.stream().map(e -> e.getUniqueId()).collect(Collectors.toList());
+
+ Either<List<GroupDefinition>, StorageOperationStatus> updateVersionEither = groupBusinessLogic.updateGroupVersion(groupsId, true);
+ if (updateVersionEither.isRight()) {
+ log.debug("Failed to update groups version. Status is {} ", updateVersionEither.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(updateVersionEither.right().value()), resource);
+ return Either.right(responseFormat);
+
+ }
+ }
+
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getResource(resource.getUniqueId(), inTransaction);
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ return Either.right(responseFormat);
+
+ }
+ return Either.left(eitherGerResource.left().value());
+
+ }
+
+ private Either<List<ArtifactDefinition>, ResponseFormat> deleteArtifactsInUpdateCsarFlow(Resource resource, User user, boolean shouldLock, boolean inTransaction, Set<ArtifactDefinition> artifactsToDelete,
+ Map<String, List<ArtifactDefinition>> groupToDelete) {
+ List<ArtifactDefinition> deletedArtifacts = new ArrayList<ArtifactDefinition>();
+
+ if (!artifactsToDelete.isEmpty()) {
+ for (ArtifactDefinition artifact : artifactsToDelete) {
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> handleDelete = artifactsBusinessLogic.handleDelete(resource.getUniqueId(), artifact.getUniqueId(), user, AuditingActionEnum.ARTIFACT_DELETE, ComponentTypeEnum.RESOURCE,
+ resource, null, null, shouldLock, inTransaction);
+ if (handleDelete.isRight()) {
+ return Either.right(handleDelete.right().value());
+ }
+ deletedArtifacts.add(handleDelete.left().value().left().value());
+
+ }
+ }
+ if (!groupToDelete.isEmpty()) {
+ log.debug("try to delete group");
+ for (Entry<String, List<ArtifactDefinition>> deleteGroup : groupToDelete.entrySet()) {
+ Either<GroupDefinition, StorageOperationStatus> deleteGroupEither = groupOperation.deleteGroup(deleteGroup.getKey(), inTransaction);
+ if (deleteGroupEither.isRight()) {
+ StorageOperationStatus storageOperationStatus = deleteGroupEither.right().value();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageOperationStatus);
+ log.debug("Failed to delete group {} under component {}, error: {}", deleteGroup.getKey(), resource.getNormalizedName(), actionStatus.name());
+
+ return Either.right(componentsUtils.getResponseFormat(actionStatus));
+
+ }
+ }
+ }
+ return Either.left(deletedArtifacts);
+ }
+
+ private Either<Resource, ResponseFormat> associateAndDissociateArtifactsToGroup(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, List<ArtifactDefinition> createdNewArtifacts, int labelCounter, boolean shouldLock,
+ boolean inTransaction, List<ArtifactDefinition> createdDeplymentArtifactsAfterDelete, Map<String, List<String>> dissocArtifactFromGroup, Map<GroupDefinition, MergedArtifactInfo> mergedgroup, List<ArtifactDefinition> deletedArtifacts) {
+ Map<GroupDefinition, List<ArtifactTemplateInfo>> artifactsToAssotiate = new HashMap<GroupDefinition, List<ArtifactTemplateInfo>>();
+ Map<GroupDefinition, List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>>> artifactsToUpdateMap = new HashMap<GroupDefinition, List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>>>();
+ Either<Resource, ResponseFormat> resEither = Either.left(resource);
+ for (Entry<GroupDefinition, MergedArtifactInfo> entry : mergedgroup.entrySet()) {
+ List<ArtifactDefinition> dissArtifactsInGroup = entry.getValue().getListToDissotiateArtifactFromGroup(deletedArtifacts);
+ if (dissArtifactsInGroup != null && !dissArtifactsInGroup.isEmpty()) {
+ List<String> dissList = new ArrayList<String>();
+ for (ArtifactDefinition art : dissArtifactsInGroup) {
+ dissList.add(art.getUniqueId());
+ }
+ dissocArtifactFromGroup.put(entry.getKey().getUniqueId(), dissList);
+ }
+
+ List<ArtifactTemplateInfo> newArtifactsInGroup = entry.getValue().getListToAssociateArtifactToGroup();
+ if (newArtifactsInGroup != null && !newArtifactsInGroup.isEmpty())
+ artifactsToAssotiate.put(entry.getKey(), newArtifactsInGroup);
+
+ List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>> artifactsToUpdate = entry.getValue().getListToUpdateArtifactInGroup();
+ if (artifactsToUpdate != null && !artifactsToUpdate.isEmpty())
+ artifactsToUpdateMap.put(entry.getKey(), artifactsToUpdate);
+ }
+
+ // Map<String, Set<String>> dissocArtifactFromGroup = new
+ // HashMap<String, Set<String>>();
+ List<GroupDefinition> dissotiateArtifactsgroups = new ArrayList<GroupDefinition>();
+ for (Entry<String, List<String>> dissotiateEntry : dissocArtifactFromGroup.entrySet()) {
+
+ GroupDefinition dissotiateGroup = new GroupDefinition();
+ dissotiateGroup.setUniqueId(dissotiateEntry.getKey());
+ dissotiateGroup.setArtifacts(dissotiateEntry.getValue());
+ dissotiateArtifactsgroups.add(dissotiateGroup);
+ }
+ if (!dissotiateArtifactsgroups.isEmpty()) {
+ log.debug("try to dissociate artifacts from groups ");
+ Either<List<GroupDefinition>, ResponseFormat> dissotiateGroupEither = groupBusinessLogic.dissociateArtifactsFromGroup(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, dissotiateArtifactsgroups, shouldLock,
+ inTransaction);
+ if (dissotiateGroupEither.isRight()) {
+ log.debug("Failed to dissociate artifacts from groups. Status is {} ", dissotiateGroupEither.right().value());
+ resEither = Either.right(dissotiateGroupEither.right().value());
+ return resEither;
+
+ }
+ }
+
+ if (!artifactsToUpdateMap.isEmpty()) {
+ List<ArtifactDefinition> updatedArtifacts = new ArrayList<ArtifactDefinition>();
+ for (Entry<GroupDefinition, List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>>> artifactsToUpdateEntry : artifactsToUpdateMap.entrySet()) {
+ List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>> artifactsToUpdateList = artifactsToUpdateEntry.getValue();
+ for (ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo> artifact : artifactsToUpdateList) {
+ Either<ArtifactDefinition, ResponseFormat> updateArtifactEither = updateDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, artifact.getKey(), artifact.getValue(), updatedArtifacts,
+ artifact.getRight().getRelatedArtifactsInfo(), shouldLock, inTransaction);
+ if (updateArtifactEither.isRight()) {
+ log.debug("failed to update artifacts. status is {}", updateArtifactEither.right().value());
+ resEither = Either.right(updateArtifactEither.right().value());
+ return resEither;
+
+ }
+
+ }
+ }
+
+ }
+
+ List<GroupDefinition> associateArtifactGroup = new ArrayList<GroupDefinition>();
+
+ for (Entry<GroupDefinition, List<ArtifactTemplateInfo>> associateEntry : artifactsToAssotiate.entrySet()) {
+ List<ArtifactTemplateInfo> associatedArtifact = associateEntry.getValue();
+ Set<String> arifactsUids = new HashSet<String>();
+ for (ArtifactTemplateInfo artifactTemplate : associatedArtifact) { // try
+ // to
+ // find
+ // artifact
+ // in
+ // resource
+ boolean isCreate = true;
+ for (ArtifactDefinition createdArtifact : createdDeplymentArtifactsAfterDelete) {
+ if (artifactTemplate.getFileName().equalsIgnoreCase(createdArtifact.getArtifactName())) {
+ arifactsUids.add(createdArtifact.getUniqueId());
+ isCreate = false;
+ break;
+ }
+
+ }
+ if (isCreate) { // check if already created
+ for (ArtifactDefinition createdNewArtifact : createdNewArtifacts) {
+ if (artifactTemplate.getFileName().equalsIgnoreCase(createdNewArtifact.getArtifactName())) {
+ arifactsUids.add(createdNewArtifact.getUniqueId());
+ isCreate = false;
+ break;
+ }
+ }
+ }
+
+ if (isCreate) {
+ Either<ArtifactDefinition, ResponseFormat> createArtifactEither = createDeploymentArtifact(csarUUID, csar, resource, user, artifactTemplate, createdNewArtifacts, labelCounter, shouldLock, inTransaction);
+ if (createArtifactEither.isRight()) {
+ resEither = Either.right(createArtifactEither.right().value());
+ return resEither;
+ }
+ arifactsUids.add(createArtifactEither.left().value().getUniqueId());
+ }
+
+ }
+ if (arifactsUids.size() > 0) {
+ List<String> artifactsToAssociate = new ArrayList<String>();
+ artifactsToAssociate.addAll(arifactsUids);
+ GroupDefinition assotiateGroup = new GroupDefinition();
+ assotiateGroup.setUniqueId(associateEntry.getKey().getUniqueId());
+ assotiateGroup.setArtifacts(artifactsToAssociate);
+ associateArtifactGroup.add(assotiateGroup);
+
+ }
+ }
+
+ if (!associateArtifactGroup.isEmpty()) {
+
+ log.debug("Try to associate artifacts to groups.");
+
+ Either<List<GroupDefinition>, ResponseFormat> assotiateGroupEither = groupBusinessLogic.associateArtifactsToGroup(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, associateArtifactGroup, shouldLock, inTransaction);
+ if (assotiateGroupEither.isRight()) {
+ log.debug("Failed to associate artifacts to groups. Status is {} ", assotiateGroupEither.right().value());
+ resEither = Either.right(assotiateGroupEither.right().value());
+ return resEither;
+
+ }
+ }
+
+ ComponentParametersView parametersView = new ComponentParametersView();
+ parametersView.disableAll();
+ parametersView.setIgnoreComponentInstances(false);
+ parametersView.setIgnoreUsers(false);
+ parametersView.setIgnoreArtifacts(false);
+ parametersView.setIgnoreGroups(false);
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getComponent(resource.getUniqueId(), parametersView, inTransaction);
+
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ resEither = Either.right(responseFormat);
+ return resEither;
+
+ }
+ resEither = Either.left(eitherGerResource.left().value());
+ return resEither;
+ }
+
+ private Map<GroupDefinition, MergedArtifactInfo> mergeGroupInUpdateFlow(Map<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>> groupArtifact, Map<ArtifactTemplateInfo, Set<ArtifactTemplateInfo>> parsedGroup,
+ Set<ArtifactDefinition> artifactsToDelete, Map<String, List<ArtifactDefinition>> groupToDelete, Set<ArtifactTemplateInfo> jsonMasterArtifacts) {
+ Map<GroupDefinition, MergedArtifactInfo> mergedgroup = new HashMap<GroupDefinition, MergedArtifactInfo>();
+ for (Entry<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>> groupListEntry : groupArtifact.entrySet()) {
+ Map<ArtifactDefinition, List<ArtifactDefinition>> createdArtifactMap = groupListEntry.getValue();
+ boolean isNeedToDeleteGroup = true;
+ List<ArtifactDefinition> listToDelete = null;
+ for (ArtifactDefinition maserArtifact : createdArtifactMap.keySet()) {
+ listToDelete = createdArtifactMap.get(maserArtifact);
+ for (ArtifactDefinition artToDelete : listToDelete) {
+ findArtifactToDelete(parsedGroup, artifactsToDelete, groupListEntry.getKey().getUniqueId(), artToDelete);
+ }
+ for (ArtifactTemplateInfo jsonMasterArtifact : jsonMasterArtifacts) {
+ if (maserArtifact.getArtifactName().equalsIgnoreCase(jsonMasterArtifact.getFileName())) {
+ MergedArtifactInfo mergedGroup = new MergedArtifactInfo();
+ mergedGroup.setJsonArtifactTemplate(jsonMasterArtifact);
+ mergedGroup.setCreatedArtifact(createdArtifactMap.get(maserArtifact));
+ mergedgroup.put(groupListEntry.getKey(), mergedGroup);
+ isNeedToDeleteGroup = false;
+
+ }
+ }
+
+ }
+ if (isNeedToDeleteGroup) {
+ groupToDelete.put(groupListEntry.getKey().getUniqueId(), listToDelete);
+ }
+
+ }
+ return mergedgroup;
+ }
+
+ private Set<String> findArtifactsNotInGroupToDelete(List<GroupDefinition> groups, List<ArtifactDefinition> createdDeplymentArtifactsAfterDelete) {
+ Set<String> artifactNotInGroupSet = new HashSet<String>();
+ for (ArtifactDefinition artifact : createdDeplymentArtifactsAfterDelete) {
+ boolean needToDelete = true;
+ if (artifact.getArtifactName().equalsIgnoreCase(Constants.VENDOR_LICENSE_MODEL) || artifact.getArtifactName().equalsIgnoreCase(Constants.VF_LICENSE_MODEL))
+ continue;
+ if (groups != null) {
+ for (GroupDefinition group : groups) {
+ List<String> groupArtifactIds = group.getArtifacts();
+ if (groupArtifactIds == null || groupArtifactIds.isEmpty()) {
+ continue;
+ }
+ for (String groupArtifactid : groupArtifactIds) {
+ if (groupArtifactid.equalsIgnoreCase(artifact.getUniqueId()))
+ needToDelete = false;
+
+ }
+
+ }
+ }
+ if (needToDelete)
+ artifactNotInGroupSet.add(artifact.getUniqueId());
+ }
+ return artifactNotInGroupSet;
+ }
+
+ private void findArtifactToDelete(Map<ArtifactTemplateInfo, Set<ArtifactTemplateInfo>> parsedGroup, Set<ArtifactDefinition> artifactsToDelete, String deleteGroupId, ArtifactDefinition artifact) {
+ boolean isNeedToDeleteArtifact = true;
+ for (Entry<ArtifactTemplateInfo, Set<ArtifactTemplateInfo>> parsedGroupSetEntry : parsedGroup.entrySet()) {
+ Set<ArtifactTemplateInfo> artifactsNames = parsedGroupSetEntry.getValue();
+ for (ArtifactTemplateInfo template : artifactsNames) {
+ if (artifact.getArtifactName().equalsIgnoreCase(template.getFileName()) && artifact.getArtifactType().equalsIgnoreCase(template.getType())) {
+ isNeedToDeleteArtifact = false;
+
+ }
+ }
+ }
+ if (isNeedToDeleteArtifact) {
+
+ artifactsToDelete.add(artifact);
+
+ }
+ }
+
+ private Map<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>> findMasterArtifactInGroup(List<GroupDefinition> groups, Map<String, ArtifactDefinition> deplymentArtifact) {
+ Map<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>> groupArtifact = new HashMap<GroupDefinition, Map<ArtifactDefinition, List<ArtifactDefinition>>>();
+
+ for (GroupDefinition group : groups) {
+ Map<ArtifactDefinition, List<ArtifactDefinition>> gupsMap = new HashMap<ArtifactDefinition, List<ArtifactDefinition>>();
+ List<ArtifactDefinition> artifacts = new ArrayList<ArtifactDefinition>();
+ List<String> artifactsList = group.getArtifacts();
+ if (artifactsList != null && !artifactsList.isEmpty()) {
+
+ ArtifactDefinition masterArtifact = ArtifactUtils.findMasterArtifact(deplymentArtifact, artifacts, artifactsList);
+ if (masterArtifact != null)
+ gupsMap.put(masterArtifact, artifacts);
+ groupArtifact.put(group, gupsMap);
+
+ }
+ }
+ return groupArtifact;
+ }
+
+ private void createArtifactsGroupSet(List<ArtifactTemplateInfo> parsedGroupTemplateList, Set<ArtifactTemplateInfo> parsedArtifactsName) {
+
+ for (ArtifactTemplateInfo parsedGroupTemplate : parsedGroupTemplateList) {
+ parsedArtifactsName.add(parsedGroupTemplate);
+ List<ArtifactTemplateInfo> relatedArtifacts = parsedGroupTemplate.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null && !relatedArtifacts.isEmpty()) {
+ createArtifactsGroupSet(relatedArtifacts, parsedArtifactsName);
+ }
+ }
+ }
+
+ public Either<Resource, ResponseFormat> createResourceArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, String artifactsMetaFile, String artifactsMetaFileName, List<ArtifactDefinition> createdArtifacts,
+ boolean shouldLock, boolean inTransaction) {
+
+ log.debug("parseResourceArtifactsInfoFromFile start");
+ Either<Map<String, List<ArtifactTemplateInfo>>, ResponseFormat> parseResourceInfoFromYamlEither = parseResourceArtifactsInfoFromFile(resource, artifactsMetaFile, artifactsMetaFileName, user);
+ if (parseResourceInfoFromYamlEither.isRight()) {
+ ResponseFormat responseFormat = parseResourceInfoFromYamlEither.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+ log.debug("parseResourceArtifactsInfoFromFile end");
+
+ log.debug("createResourceArtifacts start");
+ Either<Resource, ResponseFormat> respStatus = createResourceArtifacts(csarUUID, csar, resource, user, parseResourceInfoFromYamlEither.left().value(), AuditingActionEnum.CREATE_RESOURCE, createdArtifacts, shouldLock, inTransaction);
+ if (respStatus.isRight()) {
+ return respStatus;
+ }
+ log.debug("createResourceArtifacts end");
+ log.debug("getResource start");
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getResource(resource.getUniqueId(), inTransaction);
+ log.debug("getResource end");
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ return Either.right(responseFormat);
+
+ }
+ return Either.left(eitherGerResource.left().value());
+
+ }
+
+ private Either<Resource, ResponseFormat> createGroupDeploymentArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, List<ArtifactTemplateInfo> artifactsTemplateList,
+ List<ArtifactDefinition> createdNewArtifacts, List<ArtifactDefinition> artifactsFromResource, int labelCounter, boolean shouldLock, boolean inTransaction) {
+ Either<Resource, ResponseFormat> resStatus = Either.left(resource);
+ List<GroupDefinition> createdGroups = resource.getGroups();
+ List<GroupDefinition> heatGroups = null;
+ if (createdGroups != null && !createdGroups.isEmpty()) {
+
+ // List<IArtifactInfo> collect = resources.stream().flatMap( e ->
+ // e.getArtifacts().stream()).filter(p ->
+ // relevantArtifactTypes.contains(p.getArtifactType()
+ // )).collect(Collectors.toList());
+ // List<GroupDefinition> heatGroups = createdGroups.stream().filter(
+ // e -> e.getProperties().stream().filter(p ->
+ // p.getName().contains(Constants.HEAT_FILE_PROPS))).collect(Collectors.toList());
+ heatGroups = createdGroups.stream().filter(e -> e.getMembers() != null).collect(Collectors.toList());
+ ;
+ }
+
+ for (ArtifactTemplateInfo groupTemplateInfo : artifactsTemplateList) {
+ String groupName = groupTemplateInfo.getGroupName();
+ Set<String> artifactsGroup = new HashSet<String>();
+
+ resStatus = createDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, artifactsGroup, groupTemplateInfo, createdNewArtifacts, artifactsFromResource, labelCounter, shouldLock, inTransaction);
+ if (resStatus.isRight())
+ return resStatus;
+
+ Map<String, String> members = new HashMap<String, String>();
+ associateMembersTToArtifacts(createdNewArtifacts, artifactsFromResource, heatGroups, artifactsGroup, members);
+
+ List<String> artifactsList = new ArrayList<String>(artifactsGroup);
+
+ GroupDefinition groupDefinition = new GroupDefinition();
+ groupDefinition.setName(groupName);
+ groupDefinition.setType(Constants.DEFAULT_GROUP_VF_MODULE);
+ groupDefinition.setArtifacts(artifactsList);
+ if (!members.isEmpty())
+ groupDefinition.setMembers(members);
+
+ List<GroupProperty> properties = new ArrayList<GroupProperty>();
+ GroupProperty prop = new GroupProperty();
+ prop.setName(Constants.IS_BASE);
+ prop.setValue(Boolean.toString(groupTemplateInfo.isBase()));
+
+ properties.add(prop);
+ groupDefinition.setProperties(properties);
+ Either<GroupDefinition, ResponseFormat> createGroup = groupBusinessLogic.createGroup(resource.getUniqueId(), user.getUserId(), ComponentTypeEnum.RESOURCE, groupDefinition, inTransaction);
+ if (createGroup.isRight())
+ return Either.right(createGroup.right().value());
+
+ }
+ return resStatus;
+ }
+
+ private Either<Resource, ResponseFormat> createDeploymentArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, Set<String> artifactsGroup, ArtifactTemplateInfo artifactTemplateInfo,
+ List<ArtifactDefinition> createdArtifacts, List<ArtifactDefinition> artifactsFromResource, int labelCounter, boolean shoudLock, boolean inTransaction) {
+ Either<Resource, ResponseFormat> resStatus = Either.left(resource);
+ String artifactFileName = artifactTemplateInfo.getFileName();
+ String artifactUid = "";
+ boolean alreadyExist = false;
+
+ // check if artifacts already exist
+ if (artifactsFromResource != null && !artifactsFromResource.isEmpty()) {
+ for (ArtifactDefinition artifactFromResource : artifactsFromResource) {
+ if (artifactFromResource.getArtifactName().equals(artifactFileName)) {
+ artifactUid = artifactFromResource.getUniqueId();
+ if (!artifactFromResource.getArtifactType().equalsIgnoreCase(artifactTemplateInfo.getType())) {
+ log.debug("Artifact with name {} and type {} already exist with type {}", artifactFileName, artifactTemplateInfo.getType(), artifactFromResource.getArtifactType());
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected formatr, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR, artifactFileName, artifactTemplateInfo.getType(), artifactFromResource.getArtifactType()));
+ }
+ alreadyExist = true;
+ break;
+ }
+
+ }
+
+ }
+ if (!alreadyExist) {
+ for (ArtifactDefinition createdArtifact : createdArtifacts) {
+ if (createdArtifact.getArtifactName().equals(artifactFileName)) {
+ artifactUid = createdArtifact.getUniqueId();
+ if (!createdArtifact.getArtifactType().equalsIgnoreCase(artifactTemplateInfo.getType())) {
+ log.debug("Artifact with name {} and type {} already exist with type {}", artifactFileName, artifactTemplateInfo.getType(), createdArtifact.getArtifactType());
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected formatr, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR, artifactFileName, artifactTemplateInfo.getType(), createdArtifact.getArtifactType()));
+ }
+ alreadyExist = true;
+ break;
+ }
+
+ }
+ }
+ // if not exist need to create
+ if (!alreadyExist) {
+
+ Either<ArtifactDefinition, ResponseFormat> newArtifactEither = createDeploymentArtifact(csarUUID, csar, resource, user, artifactTemplateInfo, createdArtifacts, labelCounter, shoudLock, inTransaction);
+ if (newArtifactEither.isRight()) {
+ resStatus = Either.right(newArtifactEither.right().value());
+ return resStatus;
+ }
+ artifactUid = newArtifactEither.left().value().getUniqueId();
+
+ }
+
+ artifactsGroup.add(artifactUid);
+
+ List<ArtifactTemplateInfo> relatedArtifacts = artifactTemplateInfo.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null) {
+ for (ArtifactTemplateInfo relatedArtifactTemplateInfo : relatedArtifacts) {
+ resStatus = createDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, artifactsGroup, relatedArtifactTemplateInfo, createdArtifacts, artifactsFromResource, labelCounter, shoudLock, inTransaction);
+ if (resStatus.isRight())
+ return resStatus;
+ }
+ }
+ return resStatus;
+ }
+
+ private Either<Resource, ResponseFormat> createResourceArtifacts(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, Map<String, List<ArtifactTemplateInfo>> artifactsMap, AuditingActionEnum createResource,
+ List<ArtifactDefinition> createdArtifacts, boolean shouldLock, boolean inTransaction) {
+
+ Either<Resource, ResponseFormat> resStatus = Either.left(resource);
+
+ Collection<List<ArtifactTemplateInfo>> arifactsCollection = artifactsMap.values();
+
+ for (List<ArtifactTemplateInfo> groupTemplateList : arifactsCollection) {
+ if (groupTemplateList != null) {
+ resStatus = createGroupDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, groupTemplateList, createdArtifacts, 0, shouldLock, inTransaction);
+ if (resStatus.isRight())
+ return resStatus;
+ }
+ }
+
+ return resStatus;
+
+ }
+
+ private Either<Resource, ResponseFormat> createGroupDeploymentArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, List<ArtifactTemplateInfo> artifactsTemplateList, List<ArtifactDefinition> createdArtifacts,
+ int labelCounter, boolean shouldLock, boolean inTransaction) {
+ Either<Resource, ResponseFormat> resStatus = Either.left(resource);
+ List<GroupDefinition> createdGroups = resource.getGroups();
+ List<GroupDefinition> heatGroups = null;
+ if (createdGroups != null && !createdGroups.isEmpty()) {
+
+ // List<IArtifactInfo> collect = resources.stream().flatMap( e ->
+ // e.getArtifacts().stream()).filter(p ->
+ // relevantArtifactTypes.contains(p.getArtifactType()
+ // )).collect(Collectors.toList());
+ // List<GroupDefinition> heatGroups = createdGroups.stream().filter(
+ // e -> e.getProperties().stream().filter(p ->
+ // p.getName().contains(Constants.HEAT_FILE_PROPS))).collect(Collectors.toList());
+ heatGroups = createdGroups.stream().filter(e -> e.getMembers() != null).collect(Collectors.toList());
+ ;
+ }
+ for (ArtifactTemplateInfo groupTemplateInfo : artifactsTemplateList) {
+ String groupName = groupTemplateInfo.getGroupName();
+ Set<String> artifactsGroup = new HashSet<String>();
+
+ log.debug("createDeploymentArtifactsFromCsar start");
+ resStatus = createDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, artifactsGroup, groupTemplateInfo, createdArtifacts, labelCounter, shouldLock, inTransaction);
+ log.debug("createDeploymentArtifactsFromCsar end");
+ if (resStatus.isRight())
+ return resStatus;
+
+ Map<String, String> members = new HashMap<String, String>();
+ associateMembersTToArtifacts(createdArtifacts, null, heatGroups, artifactsGroup, members);
+
+ List<String> artifactsList = new ArrayList<String>(artifactsGroup);
+
+ GroupDefinition groupDefinition = new GroupDefinition();
+ groupDefinition.setName(groupName);
+ groupDefinition.setType(Constants.DEFAULT_GROUP_VF_MODULE);
+ groupDefinition.setArtifacts(artifactsList);
+ if (!members.isEmpty())
+ groupDefinition.setMembers(members);
+ List<GroupProperty> properties = new ArrayList<GroupProperty>();
+ GroupProperty prop = new GroupProperty();
+ prop.setName(Constants.IS_BASE);
+ prop.setValue(Boolean.toString(groupTemplateInfo.isBase()));
+
+ properties.add(prop);
+ groupDefinition.setProperties(properties);
+ log.debug("createGroup start");
+ // Either<GroupDefinition, ResponseFormat> createGroup =
+ // groupBusinessLogic.createGroup(resource.getUniqueId(),
+ // user.getUserId(), ComponentTypeEnum.RESOURCE, groupDefinition,
+ // inTransaction);
+ // Ignore validations and get component
+
+ // Since in these groups we handle only artifacts, then no need to
+ // fetch component instances
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreUsers(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ componentParametersView.setIgnoreGroups(false);
+ componentParametersView.setIgnoreComponentInstances(false);
+ Either<Resource, StorageOperationStatus> component = resourceOperation.getComponent(resource.getUniqueId(), componentParametersView, inTransaction);
+ if (component.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ Either<GroupDefinition, ResponseFormat> createGroup = groupBusinessLogic.createGroup(component.left().value(), user, ComponentTypeEnum.RESOURCE, groupDefinition, inTransaction);
+ log.debug("createGroup end");
+ if (createGroup.isRight())
+ return Either.right(createGroup.right().value());
+
+ }
+ return resStatus;
+ }
+
+ private void associateMembersTToArtifacts(List<ArtifactDefinition> createdArtifacts, List<ArtifactDefinition> artifactsFromResource, List<GroupDefinition> heatGroups, Set<String> artifactsGroup, Map<String, String> members) {
+ if (heatGroups != null && !heatGroups.isEmpty()) {
+ for (GroupDefinition heatGroup : heatGroups) {
+ List<GroupProperty> grpoupProps = heatGroup.getProperties();
+ if (grpoupProps != null) {
+ Optional<GroupProperty> op = grpoupProps.stream().filter(p -> p.getName().equals(Constants.HEAT_FILE_PROPS)).findAny();
+ if (op.isPresent()) {
+ GroupProperty prop = op.get();
+ String heatFileNAme = prop.getValue();
+ if (null == heatFileNAme || heatFileNAme.isEmpty())
+ continue;
+ List<ArtifactDefinition> artifacts = new ArrayList();
+ for (String artifactId : artifactsGroup) {
+ Optional<ArtifactDefinition> opArt = createdArtifacts.stream().filter(p -> p.getUniqueId().equals(artifactId)).findAny();
+ if (opArt.isPresent()) {
+ artifacts.add(opArt.get());
+ }
+ if (artifactsFromResource != null) {
+ opArt = artifactsFromResource.stream().filter(p -> p.getUniqueId().equals(artifactId)).findAny();
+ if (opArt.isPresent()) {
+ artifacts.add(opArt.get());
+ }
+ }
+ }
+ Optional<ArtifactDefinition> resOp = artifacts.stream().filter(p -> heatFileNAme.contains(p.getArtifactName())).findAny();
+ if (resOp.isPresent()) {
+ members.putAll(heatGroup.getMembers());
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ private Either<Resource, ResponseFormat> createDeploymentArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, Set<String> artifactsGroup, ArtifactTemplateInfo artifactTemplateInfo,
+ List<ArtifactDefinition> createdArtifacts, int labelCounter, boolean shoudLock, boolean inTransaction) {
+ Either<Resource, ResponseFormat> resStatus = Either.left(resource);
+ String artifactFileName = artifactTemplateInfo.getFileName();
+ String artifactUid = "";
+ boolean alreadyExist = false;
+
+ // check if artifacts already exist
+ for (ArtifactDefinition createdArtifact : createdArtifacts) {
+ if (createdArtifact.getArtifactName().equals(artifactFileName)) {
+ artifactUid = createdArtifact.getUniqueId();
+ if (!createdArtifact.getArtifactType().equalsIgnoreCase(artifactTemplateInfo.getType())) {
+ log.debug("Artifact with name {} and type {} already exist with type {}", artifactFileName, artifactTemplateInfo.getType(), createdArtifact.getArtifactType());
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected formatr, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR, artifactFileName, artifactTemplateInfo.getType(), createdArtifact.getArtifactType()));
+ }
+ alreadyExist = true;
+ break;
+ }
+
+ }
+ // if not exist need to create
+ if (!alreadyExist) {
+
+ Either<ArtifactDefinition, ResponseFormat> newArtifactEither = createDeploymentArtifact(csarUUID, csar, resource, user, artifactTemplateInfo, createdArtifacts, labelCounter, shoudLock, inTransaction);
+ if (newArtifactEither.isRight()) {
+ resStatus = Either.right(newArtifactEither.right().value());
+ return resStatus;
+ }
+ artifactUid = newArtifactEither.left().value().getUniqueId();
+
+ }
+
+ artifactsGroup.add(artifactUid);
+
+ List<ArtifactTemplateInfo> relatedArtifacts = artifactTemplateInfo.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null) {
+ for (ArtifactTemplateInfo relatedArtifactTemplateInfo : relatedArtifacts) {
+ resStatus = createDeploymentArtifactsFromCsar(csarUUID, csar, resource, user, artifactsGroup, relatedArtifactTemplateInfo, createdArtifacts, labelCounter, shoudLock, inTransaction);
+ if (resStatus.isRight())
+ return resStatus;
+ }
+ }
+ return resStatus;
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> createDeploymentArtifact(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, ArtifactTemplateInfo artifactTemplateInfo, List<ArtifactDefinition> createdArtifacts,
+ int labelCounter, boolean shoudLock, boolean inTransaction) {
+ String artifactUid;
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> artifactContententStatus = CsarValidationUtils.getArtifactsContent(csarUUID, csar, artifactTemplateInfo.getFileName(), componentsUtils);
+ if (artifactContententStatus.isRight())
+ return Either.right(artifactContententStatus.right().value());
+ labelCounter += createdArtifacts.size();
+
+ Map<String, Object> json = buildJsonForArtifact(artifactTemplateInfo, artifactContententStatus.left().value().getValue(), labelCounter);
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> uploadArtifactToService = createOrUpdateCsarArtifactFromJson(resource, user, json, ArtifactOperation.Create, shoudLock, inTransaction);
+
+ if (uploadArtifactToService.isRight())
+ return Either.right(uploadArtifactToService.right().value());
+
+ ArtifactDefinition currentInfo = uploadArtifactToService.left().value().left().value();
+ if (currentInfo.getHeatParameters() != null) {
+
+ Either<ArtifactDefinition, ResponseFormat> updateEnvEither = updateHeatParamsFromCsar(csarUUID, csar, artifactTemplateInfo, currentInfo);
+ if (updateEnvEither.isRight()) {
+ log.debug("failed to update parameters to artifact {}", artifactTemplateInfo.getFileName());
+ return Either.right(updateEnvEither.right().value());
+
+ }
+ artifactUid = updateEnvEither.left().value().getUniqueId();
+ createdArtifacts.add(updateEnvEither.left().value());
+ currentInfo = updateEnvEither.left().value();
+ } else {
+
+ artifactUid = currentInfo.getUniqueId();
+ createdArtifacts.add(currentInfo);
+
+ }
+ return Either.left(currentInfo);
+
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> updateDeploymentArtifactsFromCsar(String csarUUID, Map<String, byte[]> csar, Resource resource, User user, ArtifactDefinition oldArtifact, ArtifactTemplateInfo artifactTemplateInfo,
+ List<ArtifactDefinition> updatedArtifacts, List<ArtifactTemplateInfo> updatedRequiredArtifacts, boolean shouldLock, boolean inTransaction) {
+
+ Either<ArtifactDefinition, ResponseFormat> resStatus = null;
+ String artifactFileName = artifactTemplateInfo.getFileName();
+ String artifactUid = "";
+
+ // check if artifacts already exist
+ for (ArtifactDefinition updatedArtifact : updatedArtifacts) {
+ if (updatedArtifact.getArtifactName().equals(artifactFileName)) {
+ artifactUid = updatedArtifact.getUniqueId();
+ if (!updatedArtifact.getArtifactType().equalsIgnoreCase(artifactTemplateInfo.getType())) {
+ log.debug("Artifact with name {} and type {} already updated with type {}", artifactFileName, artifactTemplateInfo.getType(), updatedArtifact.getArtifactType());
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected formatr, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ resStatus = Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR, artifactFileName, artifactTemplateInfo.getType(), updatedArtifact.getArtifactType()));
+ return resStatus;
+ }
+ resStatus = Either.left(updatedArtifact);
+ return resStatus;
+ }
+
+ }
+
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> artifactContententStatus = CsarValidationUtils.getArtifactsContent(csarUUID, csar, artifactTemplateInfo.getFileName(), componentsUtils);
+ if (artifactContententStatus.isRight()) {
+ resStatus = Either.right(artifactContententStatus.right().value());
+ return resStatus;
+ }
+
+ Map<String, Object> json = buildJsonForUpdateArtifact(oldArtifact.getUniqueId(), artifactFileName, oldArtifact.getArtifactType(), oldArtifact.getArtifactLabel(), oldArtifact.getArtifactDisplayName(), oldArtifact.getDescription(),
+ artifactContententStatus.left().value().getRight(), updatedRequiredArtifacts);
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> uploadArtifactToService = createOrUpdateCsarArtifactFromJson(resource, user, json, ArtifactOperation.Update, shouldLock, inTransaction);
+
+ if (uploadArtifactToService.isRight()) {
+ resStatus = Either.right(uploadArtifactToService.right().value());
+ return resStatus;
+ }
+ ArtifactDefinition currentInfo = uploadArtifactToService.left().value().left().value();
+
+ Either<ArtifactDefinition, ResponseFormat> updateEnvEither = updateHeatParamsFromCsar(csarUUID, csar, artifactTemplateInfo, currentInfo);
+ if (updateEnvEither.isRight()) {
+ log.debug("failed to update parameters to artifact {}", artifactFileName);
+ resStatus = Either.right(updateEnvEither.right().value());
+ return resStatus;
+ }
+
+ artifactUid = updateEnvEither.left().value().getUniqueId();
+ updatedArtifacts.add(updateEnvEither.left().value());
+ resStatus = Either.left(updateEnvEither.left().value());
+
+ return resStatus;
+
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> updateHeatParamsFromCsar(String csarUUID, Map<String, byte[]> csar, ArtifactTemplateInfo artifactTemplateInfo, ArtifactDefinition currentInfo) {
+ Either<ArtifactDefinition, ResponseFormat> resStatus = Either.left(currentInfo);
+ if (artifactTemplateInfo.getEnv() != null && !artifactTemplateInfo.getEnv().isEmpty()) {
+
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> artifactparamsStatus = CsarValidationUtils.getArtifactsContent(csarUUID, csar, artifactTemplateInfo.getEnv(), componentsUtils);
+ if (artifactparamsStatus.isRight()) {
+ resStatus = Either.right(artifactparamsStatus.right().value());
+ return resStatus;
+ }
+ Either<List<HeatParameterDefinition>, ResponseFormat> propsStatus = extractHeatParameters(ArtifactTypeEnum.HEAT_ENV.getType(), artifactTemplateInfo.getEnv(), artifactparamsStatus.left().value().getValue());
+ if (propsStatus.isRight()) {
+
+ resStatus = Either.right(propsStatus.right().value());
+ return resStatus;
+ }
+ List<HeatParameterDefinition> updatedHeatEnvParams = propsStatus.left().value();
+ List<HeatParameterDefinition> currentHeatEnvParams = currentInfo.getHeatParameters();
+ List<HeatParameterDefinition> newHeatEnvParams = new ArrayList<HeatParameterDefinition>();
+
+ if (updatedHeatEnvParams != null && !updatedHeatEnvParams.isEmpty() && currentHeatEnvParams != null && !currentHeatEnvParams.isEmpty()) {
+
+ String paramName;
+ for (HeatParameterDefinition heatEnvParam : updatedHeatEnvParams) {
+
+ paramName = heatEnvParam.getName();
+ for (HeatParameterDefinition currHeatParam : currentHeatEnvParams) {
+ if (paramName.equalsIgnoreCase(currHeatParam.getName())) {
+
+ String updatedParamValue = heatEnvParam.getCurrentValue();
+ if (updatedParamValue == null)
+ updatedParamValue = heatEnvParam.getDefaultValue();
+ HeatParameterType paramType = HeatParameterType.isValidType(currHeatParam.getType());
+ if (!paramType.getValidator().isValid(updatedParamValue, null)) {
+ ActionStatus status = ActionStatus.INVALID_HEAT_PARAMETER_VALUE;
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(status, ArtifactTypeEnum.HEAT_ENV.getType(), paramType.getType(), paramName);
+ resStatus = Either.right(responseFormat);
+ return resStatus;
+ }
+ currHeatParam.setCurrentValue(paramType.getConverter().convert(updatedParamValue, null, null));
+ newHeatEnvParams.add(currHeatParam);
+ break;
+ }
+ }
+ }
+ if (!newHeatEnvParams.isEmpty()) {
+ StorageOperationStatus operationStatus = heatParametersOperation.updateHeatParameters(currentHeatEnvParams);
+
+ if (operationStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to update artifact on graph - {}", currentInfo.getUniqueId());
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(operationStatus));
+ resStatus = Either.right(responseFormat);
+ return resStatus;
+ }
+ }
+ }
+
+ }
+ return resStatus;
+ }
+
+ private Either<List<HeatParameterDefinition>, ResponseFormat> extractHeatParameters(String artifactType, String fileName, byte[] content) {
+ // extract heat parameters
+ String heatDecodedPayload = GeneralUtility.isBase64Encoded(content) ? new String(Base64.decodeBase64(content)) : new String(content);
+ Either<List<HeatParameterDefinition>, ResultStatusEnum> heatParameters = ImportUtils.getHeatParamsWithoutImplicitTypes(heatDecodedPayload, artifactType);
+ if (heatParameters.isRight()) {
+ log.debug("File {} is not in expected key-value form in csar ", fileName);
+ BeEcompErrorManager.getInstance().logInternalDataError("File " + fileName + " is not in expected key-value form in csar ", "CSAR internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_DEPLOYMENT_ARTIFACT_HEAT, fileName));
+
+ }
+ return Either.left(heatParameters.left().value());
+
+ }
+
+ private Map<String, Object> buildJsonForArtifact(ArtifactTemplateInfo artifactTemplateInfo, byte[] artifactContentent, int atrifactLabelCounter) {
+
+ Map<String, Object> json = new HashMap<String, Object>();
+ String artifactName = artifactTemplateInfo.getFileName();
+
+ json.put(Constants.ARTIFACT_NAME, artifactTemplateInfo.getFileName());
+ json.put(Constants.ARTIFACT_TYPE, artifactTemplateInfo.getType());
+ json.put(Constants.ARTIFACT_DESCRIPTION, "created from csar");
+
+ /*
+ * DE250204
+ * There is no need to check if base64 encoding. All files that are inside the CSAR are not encoded and need to encoded. The check for isBase64 is not in cases we get files with extension .cert or .key these files are base64 but if do not
+ * encode them again, when the user download them, the decoded file will return not the encoded file that was upload.
+ */
+ // String encodedPayload = new String(artifactContentent);
+ // boolean isEncoded = GeneralUtility.isBase64Encoded(artifactContentent);
+ // if (!isEncoded) {
+ // log.debug("payload is encoded. perform decode");
+ String encodedPayload = Base64.encodeBase64String(artifactContentent);
+ // }
+ json.put(Constants.ARTIFACT_PAYLOAD_DATA, encodedPayload);
+ String displayName = artifactName;
+ if (artifactName.lastIndexOf(".") > 0)
+ displayName = artifactName.substring(0, artifactName.lastIndexOf("."));
+ json.put(Constants.ARTIFACT_DISPLAY_NAME, displayName);
+ String label = ValidationUtils.normalizeArtifactLabel(artifactTemplateInfo.getType() + atrifactLabelCounter);
+ json.put(Constants.ARTIFACT_LABEL, label);
+ json.put(Constants.ARTIFACT_GROUP_TYPE, ArtifactGroupTypeEnum.DEPLOYMENT.getType());
+ List<ArtifactTemplateInfo> requiredArtifacts = artifactTemplateInfo.getRelatedArtifactsInfo();
+ json.put(Constants.REQUIRED_ARTIFACTS, (requiredArtifacts == null || requiredArtifacts.isEmpty()) ? new ArrayList<>()
+ : requiredArtifacts.stream().filter(e -> e.getType().equals(ArtifactTypeEnum.HEAT_ARTIFACT.getType()) || e.getType().equals(ArtifactTypeEnum.HEAT_NESTED.getType())).map(e -> e.getFileName()).collect(Collectors.toList()));
+ return json;
+ }
+
+ private Map<String, Object> buildJsonForUpdateArtifact(String artifactId, String artifactName, String artifactType, String label, String displayName, String description, byte[] artifactContentent,
+ List<ArtifactTemplateInfo> updatedRequiredArtifacts) {
+
+ Map<String, Object> json = new HashMap<String, Object>();
+ if (artifactId != null && !artifactId.isEmpty())
+ json.put(Constants.ARTIFACT_ID, artifactId);
+
+ json.put(Constants.ARTIFACT_NAME, artifactName);
+ json.put(Constants.ARTIFACT_TYPE, artifactType);
+ json.put(Constants.ARTIFACT_DESCRIPTION, description);
+
+ String encodedPayload = new String(artifactContentent);
+ // DE250204 - need to encode all.
+ //boolean isEncoded = GeneralUtility.isBase64Encoded(artifactContentent);
+ //if (!isEncoded) {
+ log.debug("payload is encoded. perform decode");
+ encodedPayload = Base64.encodeBase64String(artifactContentent);
+ //}
+
+ json.put(Constants.ARTIFACT_PAYLOAD_DATA, encodedPayload);
+ json.put(Constants.ARTIFACT_DISPLAY_NAME, displayName);
+ json.put(Constants.ARTIFACT_LABEL, label);
+ json.put(Constants.ARTIFACT_GROUP_TYPE, ArtifactGroupTypeEnum.DEPLOYMENT.getType());
+ json.put(Constants.REQUIRED_ARTIFACTS, (updatedRequiredArtifacts == null || updatedRequiredArtifacts.isEmpty()) ? new ArrayList<>()
+ : updatedRequiredArtifacts.stream().filter(e -> e.getType().equals(ArtifactTypeEnum.HEAT_ARTIFACT.getType()) || e.getType().equals(ArtifactTypeEnum.HEAT_NESTED.getType())).map(e -> e.getFileName()).collect(Collectors.toList()));
+ return json;
+ }
+
+ private Either<Map<String, List<ArtifactTemplateInfo>>, ResponseFormat> parseResourceArtifactsInfoFromFile(Resource resource, String artifactsMetaFile, String artifactFileName, User user) {
+
+ try {
+ JsonObject jsonElement = new JsonObject();
+ jsonElement = gson.fromJson(artifactsMetaFile, jsonElement.getClass());
+
+ JsonElement importStructureElement = jsonElement.get(Constants.IMPORT_STRUCTURE);
+ if (importStructureElement == null || importStructureElement.isJsonNull()) {
+ log.debug("Artifact file is not in expected formatr, fileName {}", artifactFileName);
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected formatr, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, artifactFileName));
+ }
+
+ Map<String, List<Map<String, Object>>> artifactTemplateMap = new HashMap<String, List<Map<String, Object>>>();
+ artifactTemplateMap = componentsUtils.parseJsonToObject(importStructureElement.toString(), HashMap.class);
+ if (artifactTemplateMap.isEmpty()) {
+ log.debug("Artifact file is not in expected formatr, fileName {}", artifactFileName);
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected formatr, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, artifactFileName));
+ }
+
+ Set<String> artifactsTypeKeys = artifactTemplateMap.keySet();
+ Map<String, List<ArtifactTemplateInfo>> artifactsMap = new HashMap<String, List<ArtifactTemplateInfo>>();
+ List<ArtifactTemplateInfo> allGroups = new ArrayList<>();
+ for (String artifactsTypeKey : artifactsTypeKeys) {
+
+ List<Map<String, Object>> o = artifactTemplateMap.get(artifactsTypeKey);
+ Either<List<ArtifactTemplateInfo>, ResponseFormat> artifactTemplateInfoListPairStatus = createArtifactTemplateInfoModule(artifactsTypeKey, o);
+ if (artifactTemplateInfoListPairStatus.isRight()) {
+ log.debug("Artifact file is not in expected formatr, fileName {}", artifactFileName);
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected format, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(artifactTemplateInfoListPairStatus.right().value());
+ }
+ List<ArtifactTemplateInfo> artifactTemplateInfoList = artifactTemplateInfoListPairStatus.left().value();
+ if (artifactTemplateInfoList == null) {
+ log.debug("Artifact file is not in expected formatr, fileName {}", artifactFileName);
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected format, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, artifactFileName));
+
+ }
+ allGroups.addAll(artifactTemplateInfoList);
+ artifactsMap.put(artifactsTypeKey, artifactTemplateInfoList);
+ }
+ int counter = groupBusinessLogic.getNextVfModuleNameCounter(resource.getGroups());
+ Either<Boolean, ResponseFormat> validateGroupNamesRes = groupBusinessLogic.validateGenerateVfModuleGroupNames(allGroups, resource.getSystemName(), counter);
+ if (validateGroupNamesRes.isRight()) {
+ return Either.right(validateGroupNamesRes.right().value());
+ }
+ return Either.left(artifactsMap);
+ } catch (Exception e) {
+ log.debug("Artifact file is not in expected format, fileName {}", artifactFileName);
+ log.debug("failed with exception.", e);
+ BeEcompErrorManager.getInstance().logInternalDataError("Artifact file is not in expected format, fileName " + artifactFileName, "Artifact internals are invalid", ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID_FORMAT, artifactFileName));
+ }
+
+ }
+
+ private Either<List<ArtifactTemplateInfo>, ResponseFormat> createArtifactTemplateInfoModule(String artifactsTypeKey, List<Map<String, Object>> jsonObject) {
+ List<ArtifactTemplateInfo> artifactTemplateInfoList = new ArrayList<ArtifactTemplateInfo>();
+ for (Map<String, Object> o : jsonObject) {
+ Either<ArtifactTemplateInfo, ResponseFormat> artifacttemplateInfoStatus = ArtifactTemplateInfo.createArtifactTemplateInfoFromJson(componentsUtils, artifactsTypeKey, o, artifactTemplateInfoList, null);
+ if (artifacttemplateInfoStatus.isRight()) {
+ return Either.right(artifacttemplateInfoStatus.right().value());
+ }
+
+ ArtifactTemplateInfo artifacttemplateInfo = artifacttemplateInfoStatus.left().value();
+ if (artifacttemplateInfo != null) {
+ artifactTemplateInfoList.add(artifacttemplateInfo);
+ }
+
+ }
+ return Either.left(artifactTemplateInfoList);
+ }
+
+ private Either<Resource, ResponseFormat> createResourceInstancesRelations(User user, String yamlName, Resource resource, Map<String, UploadComponentInstanceInfo> uploadResInstancesMap, boolean inTransaction, boolean needLock) {
+ log.debug("createResourceInstancesRelations try to create relations ");
+ List<ComponentInstance> componentInstancesList = resource.getComponentInstances();
+ if (uploadResInstancesMap == null) {
+ log.debug("UploadComponentInstanceInfo is empty, fileName {}", yamlName);
+ BeEcompErrorManager.getInstance().logInternalDataError("UploadComponentInstanceInfo is emty, fileName {}", yamlName, ErrorSeverity.ERROR);
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE, yamlName);
+ return Either.right(responseFormat);
+ }
+
+ if (componentInstancesList == null || componentInstancesList.isEmpty()) {
+ log.debug("componentInstancesList is empty in resource {} ", resource.getUniqueId());
+ BeEcompErrorManager.getInstance().logInternalDataError("componentInstancesList is empty in resource {} ", resource.getUniqueId(), ErrorSeverity.ERROR);
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE, yamlName);
+ return Either.right(responseFormat);
+ }
+
+ Map<String, List<PropertyDefinition>> propertiesListPerResource = new HashMap<>();
+
+ long totalCreateRel = 0;
+ long totalCreatePropVal = 0;
+ Iterator<Entry<String, UploadComponentInstanceInfo>> nodesInfoValue = uploadResInstancesMap.entrySet().iterator();
+ while (nodesInfoValue.hasNext()) {
+ Entry<String, UploadComponentInstanceInfo> uploadComponentInstanceInfoEntry = nodesInfoValue.next();
+ UploadComponentInstanceInfo uploadComponentInstanceInfo = uploadComponentInstanceInfoEntry.getValue();
+
+ ComponentInstance currentCompInstance = null;
+ for (ComponentInstance compInstance : componentInstancesList) {
+ if (compInstance.getName().equals(uploadComponentInstanceInfo.getName())) {
+ currentCompInstance = compInstance;
+ break;
+ }
+ }
+
+ if (currentCompInstance == null) {
+ log.debug("component instance with name {} in resource {} ", uploadComponentInstanceInfo.getName(), resource.getUniqueId());
+ BeEcompErrorManager.getInstance().logInternalDataError("component instance with name " + uploadComponentInstanceInfo.getName() + " in resource {} ", resource.getUniqueId(), ErrorSeverity.ERROR);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE, yamlName);
+ return Either.right(responseFormat);
+ }
+ String resourceInstanceId = currentCompInstance.getUniqueId();
+
+ log.debug("************* addPropertyValuesToRi start");
+ long startAddProperty = System.currentTimeMillis();
+
+ ResponseFormat addPropertiesValueToRiRes = addPropertyValuesToRi(uploadComponentInstanceInfo, resource, resourceInstanceId, currentCompInstance, yamlName, propertiesListPerResource);
+ log.debug("************* addPropertyValuesToRi end");
+ totalCreatePropVal += (System.currentTimeMillis() - startAddProperty);
+
+ if (addPropertiesValueToRiRes.getStatus() != 200) {
+ return Either.right(addPropertiesValueToRiRes);
+ }
+ Map<String, List<UploadReqInfo>> regMap = uploadComponentInstanceInfo.getRequirements();
+ if (regMap == null)
+ continue;
+ Iterator<Entry<String, List<UploadReqInfo>>> nodesRegValue = regMap.entrySet().iterator();
+
+ long startAddRelation = System.currentTimeMillis();
+
+ while (nodesRegValue.hasNext()) {
+ Entry<String, List<UploadReqInfo>> nodesRegInfoEntry = nodesRegValue.next();
+
+ List<UploadReqInfo> uploadRegInfoList = nodesRegInfoEntry.getValue();
+ for (UploadReqInfo uploadRegInfo : uploadRegInfoList) {
+ log.debug("Going to create relation {}", uploadRegInfo.getName());
+ String regName = uploadRegInfo.getName();
+ String nodeCapName = uploadRegInfo.getNode();
+ RequirementCapabilityRelDef regCapRelDef = new RequirementCapabilityRelDef();
+ regCapRelDef.setFromNode(resourceInstanceId);
+ log.debug("try to find available requirement {} ", regName);
+ Either<RequirementDefinition, ResponseFormat> eitherReqStatus = findAviableRequiremen(regName, yamlName, uploadComponentInstanceInfo, currentCompInstance);
+ if (eitherReqStatus.isRight()) {
+ log.debug("failed to find available requirement {} status is {}", regName, eitherReqStatus.right().value());
+ return Either.right(eitherReqStatus.right().value());
+ }
+
+ RequirementDefinition validReq = eitherReqStatus.left().value();
+ List<RequirementAndRelationshipPair> reqAndRelationshipPairList = regCapRelDef.getRelationships();
+ if (reqAndRelationshipPairList == null)
+ reqAndRelationshipPairList = new ArrayList<RequirementAndRelationshipPair>();
+ RequirementAndRelationshipPair reqAndRelationshipPair = new RequirementAndRelationshipPair();
+ reqAndRelationshipPair.setRequirement(regName);
+ reqAndRelationshipPair.setRequirementOwnerId(validReq.getOwnerId());
+ reqAndRelationshipPair.setRequirementUid(validReq.getUniqueId());
+
+ ComponentInstance currentCapCompInstance = null;
+ for (ComponentInstance compInstance : componentInstancesList) {
+ if (compInstance.getName().equals(uploadRegInfo.getNode())) {
+ currentCapCompInstance = compInstance;
+ break;
+ }
+ }
+
+ if (currentCapCompInstance == null) {
+ log.debug("component instance with name {} in resource {} ", uploadRegInfo.getNode(), resource.getUniqueId());
+ BeEcompErrorManager.getInstance().logInternalDataError("component instance with name " + uploadRegInfo.getNode() + " in resource {} ", resource.getUniqueId(), ErrorSeverity.ERROR);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE, yamlName);
+ return Either.right(responseFormat);
+ }
+ regCapRelDef.setToNode(currentCapCompInstance.getUniqueId());
+ log.debug("try to find aviable Capability req name is {} ", validReq.getName());
+ CapabilityDefinition aviableCapForRel = findAvailableCapabilityByTypeOrName(validReq, currentCapCompInstance, uploadRegInfo);
+ if (aviableCapForRel == null) {
+ log.debug("aviable capability was not found. req name is {} component instance is {}", validReq.getName(), currentCapCompInstance.getUniqueId());
+ BeEcompErrorManager.getInstance().logInternalDataError("aviable capability was not found. req name is " + validReq.getName() + " component instance is " + currentCapCompInstance.getUniqueId(), resource.getUniqueId(),
+ ErrorSeverity.ERROR);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE, yamlName);
+ return Either.right(responseFormat);
+ }
+ reqAndRelationshipPair.setCapability(aviableCapForRel.getName());
+ reqAndRelationshipPair.setCapabilityUid(aviableCapForRel.getUniqueId());
+ reqAndRelationshipPair.setCapabilityOwnerId(aviableCapForRel.getOwnerId());
+ reqAndRelationshipPairList.add(reqAndRelationshipPair);
+ regCapRelDef.setRelationships(reqAndRelationshipPairList);
+ ComponentInstanceBusinessLogic componentInstanceBL = getComponentInstanceBL();
+ Either<RequirementCapabilityRelDef, ResponseFormat> eitherRelationRes = componentInstanceBL.associateRIToRIOnGraph(resource.getUniqueId(), regCapRelDef, ComponentTypeEnum.RESOURCE, inTransaction);
+ log.debug("************* finished to create relation {}", uploadRegInfo.getName());
+ if (eitherRelationRes.isRight()) {
+ log.debug("failed to associate ri {} to ri {}", regCapRelDef.getFromNode(), regCapRelDef.getToNode());
+ return Either.right(eitherRelationRes.right().value());
+ }
+
+ }
+
+ }
+ totalCreateRel += (System.currentTimeMillis() - startAddRelation);
+ }
+ /*
+ * List<InputDefinition> inputs = resource.getInputs(); for(InputDefinition input: inputs){ if(input.getProperties() != null){ this.inputOperation.associatePropertiesToInputOnGraph(input. getUniqueId(), input.getProperties()); } }
+ */
+
+ // Either<Resource, StorageOperationStatus> eitherGerResource =
+ // resourceOperation.getResource(resource.getUniqueId(), inTransaction);
+ log.debug("************* create relations took : create {}, add property value {}", totalCreateRel, totalCreatePropVal);
+ log.debug("************* in create relations, getResource start");
+ ComponentParametersView parametersView = new ComponentParametersView();
+ parametersView.disableAll();
+ parametersView.setIgnoreComponentInstances(false);
+ parametersView.setIgnoreUsers(false);
+ parametersView.setIgnoreArtifacts(false);
+ parametersView.setIgnoreGroups(false);
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getComponent(resource.getUniqueId(), parametersView, inTransaction);
+ log.debug("************* in create relations, getResource end");
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ return Either.right(responseFormat);
+
+ }
+ return Either.left(eitherGerResource.left().value());
+ }
+
+ private ResponseFormat addPropertyValuesToRi(UploadComponentInstanceInfo uploadComponentInstanceInfo, Resource resource, String resourceInstanceId, ComponentInstance currentCompInstance, String yamlName,
+ Map<String, List<PropertyDefinition>> propertiesListPerResource) {
+
+ Map<String, List<UploadPropInfo>> propMap = uploadComponentInstanceInfo.getProperties();
+ if (propMap != null && propMap.size() > 0) {
+ Map<String, PropertyDefinition> currPropertiesMap = new HashMap<String, PropertyDefinition>();
+ List<PropertyDefinition> propertiesList = new ArrayList<PropertyDefinition>();
+ Integer index = currentCompInstance.getPropertyValueCounter();
+ Integer indexInput = currentCompInstance.getInputValueCounter();
+ List<PropertyDefinition> listFromMap = propertiesListPerResource.get(currentCompInstance.getComponentUid());
+ if (listFromMap != null) {
+ propertiesList = listFromMap;
+ } else {
+ TitanOperationStatus getPropertyRes = ((PropertyOperation) propertyOperation).findAllResourcePropertiesRecursively(currentCompInstance.getComponentUid(), propertiesList);
+ if (!getPropertyRes.equals(TitanOperationStatus.OK)) {
+ log.debug("failed to find properties of resource {} status is {}", currentCompInstance.getComponentUid(), getPropertyRes);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(getPropertyRes)), yamlName);
+ return responseFormat;
+ }
+ propertiesListPerResource.put(currentCompInstance.getComponentUid(), propertiesList);
+ }
+ if (propertiesList.size() > 0) {
+ for (PropertyDefinition prop : propertiesList) {
+ String propName = prop.getName();
+ if (!currPropertiesMap.containsKey(propName)) {
+ currPropertiesMap.put(propName, prop);
+ }
+ }
+ for (List<UploadPropInfo> propertyList : propMap.values()) {
+ UploadPropInfo propertyInfo = propertyList.get(0);
+ String propName = propertyInfo.getName();
+ if (!currPropertiesMap.containsKey(propName)) {
+ log.debug("failed to find property {} ", propName);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, propName);
+ return responseFormat;
+ }
+ PropertyDefinition curPropertyDef = currPropertiesMap.get(propName);
+ ComponentInstanceProperty property = null;
+ ComponentInstanceInput inputValue = null;
+ // TODO
+ String value = null;
+ List<GetInputValueInfo> getInputs = null;
+ boolean isValidate = true;
+ if (propertyInfo.getValue() != null) {
+ getInputs = propertyInfo.getGet_input();
+ isValidate = getInputs == null || getInputs.isEmpty();
+ if (isValidate) {
+ value = ImportUtils.getPropertyJsonStringValue(propertyInfo.getValue(), curPropertyDef.getType());
+ } else
+ value = ImportUtils.getPropertyJsonStringValue(propertyInfo.getValue(), ToscaTagNamesEnum.GET_INPUT.getElementName());
+ }
+
+ property = new ComponentInstanceProperty(curPropertyDef, value, null);
+
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = componentInstanceOperation.addPropertyValueToResourceInstance(property, resourceInstanceId, isValidate, index, true);
+ if (result.isRight()) {
+ log.debug("Failed to add property value {} to resource instance {}", property, resourceInstanceId);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(result.right().value());
+ return componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, currentCompInstance.getName());
+ }
+ Either<Integer, StorageOperationStatus> increaseCounterRes = componentInstanceOperation.increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, GraphPropertiesDictionary.PROPERTY_COUNTER, true);
+ if (increaseCounterRes.isRight()) {
+ log.debug("Failed to increase resource property counter {} to resource instance {}", property, resourceInstanceId);
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(increaseCounterRes.right().value());
+ return componentsUtils.getResponseFormatForResourceInstanceProperty(actionStatus, currentCompInstance.getName());
+ }
+ index = increaseCounterRes.left().value();
+ if (getInputs != null && !getInputs.isEmpty()) {
+ for (GetInputValueInfo getInput : getInputs) {
+ List<InputDefinition> inputs = resource.getInputs();
+ if (inputs == null || inputs.isEmpty()) {
+ log.debug("Failed to add property {} to resource instance {}. Inputs list is empty ", property, resourceInstanceId);
+ return componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ }
+
+ Optional<InputDefinition> optional = inputs.stream().filter(p -> p.getName().equals(getInput.getInputName())).findAny();
+ if (!optional.isPresent()) {
+ log.debug("Failed to find input {} ", getInput.getInputName());
+ // @@TODO error message
+ return componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ }
+ InputDefinition input = optional.get();
+ TitanOperationStatus status = inputOperation.associatePropertyToInput(resourceInstanceId, input.getUniqueId(), result.left().value(), getInput);
+ if (status != TitanOperationStatus.OK) {
+ log.debug("Failed to associate input {} tp property value{} ", getInput.getInputName(), result.left().value().getValueUniqueUid());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(status)), yamlName);
+ return responseFormat;
+ }
+ GetInputValueInfo getInputIndex = getInput.getGetInputIndex();
+ if (getInputIndex != null) {
+ optional = inputs.stream().filter(p -> p.getName().equals(getInputIndex.getInputName())).findAny();
+ if (!optional.isPresent()) {
+ log.debug("Failed to find input {} ", getInputIndex.getInputName());
+ // @@TODO error message
+ return componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ }
+ InputDefinition inputIndex = optional.get();
+ status = inputOperation.associatePropertyToInput(resourceInstanceId, inputIndex.getUniqueId(), result.left().value(), getInputIndex);
+ if (status != TitanOperationStatus.OK) {
+ log.debug("Failed to associate input {} tp property value{} ", getInput.getInputName(), result.left().value().getValueUniqueUid());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(status)), yamlName);
+ return responseFormat;
+ }
+
+ }
+
+ }
+ }
+
+ }
+ }
+ }
+ return componentsUtils.getResponseFormat(ActionStatus.OK);
+ }
+
+ // US740820 Relate RIs according to capability name
+ private CapabilityDefinition findAvailableCapabilityByTypeOrName(RequirementDefinition validReq, ComponentInstance currentCapCompInstance, UploadReqInfo uploadReqInfo) {
+ if (null == uploadReqInfo.getCapabilityName() || validReq.getCapability().equals(uploadReqInfo.getCapabilityName())) {// get
+ // by
+ // capability
+ // type
+ return findAviableCapability(validReq, currentCapCompInstance);
+ }
+ return findAvailableCapability(validReq, currentCapCompInstance, uploadReqInfo);
+ }
+
+ private CapabilityDefinition findAvailableCapability(RequirementDefinition validReq, ComponentInstance currentCapCompInstance, UploadReqInfo uploadReqInfo) {
+ Map<String, List<CapabilityDefinition>> capMap = currentCapCompInstance.getCapabilities();
+ if (!capMap.containsKey(validReq.getCapability())) {
+ return null;
+ }
+ Optional<CapabilityDefinition> capByName = capMap.get(validReq.getCapability()).stream().filter(p -> p.getName().equals(uploadReqInfo.getCapabilityName())).findAny();
+ if (!capByName.isPresent()) {
+ return null;
+ }
+ CapabilityDefinition cap = capByName.get();
+ // TODO temporary fix - remove specific capability node validation -
+ // String reqNode = validReq.getNode();
+ // if (reqNode != null && !reqNode.isEmpty() &&
+ // !cap.getCapabilitySources().contains(reqNode)) {
+ // return null;
+ // }
+ RequirementAndRelationshipPair relationPair = getReqRelPair(cap);
+ Either<Boolean, StorageOperationStatus> eitherStatus = componentInstanceOperation.isAvailableCapabilty(currentCapCompInstance, relationPair);
+ if (eitherStatus.isRight() || eitherStatus.left().value() == false) {
+ return null;
+ }
+ return cap;
+ }
+
+ private RequirementAndRelationshipPair getReqRelPair(CapabilityDefinition cap) {
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setCapabilityUid(cap.getUniqueId());
+ relationPair.setCapability(cap.getName());
+ relationPair.setCapabilityOwnerId(cap.getOwnerId());
+ return relationPair;
+ }
+
+ private CapabilityDefinition findAviableCapability(RequirementDefinition validReq, ComponentInstance currentCapCompInstance) {
+ CapabilityDefinition aviableCapForRel = null;
+ Map<String, List<CapabilityDefinition>> capMap = currentCapCompInstance.getCapabilities();
+ if (capMap.containsKey(validReq.getCapability())) {
+ List<CapabilityDefinition> capList = capMap.get(validReq.getCapability());
+
+ for (CapabilityDefinition cap : capList) {
+ // TODO temporary fix - remove specific capability node
+ // String reqNode = validReq.getNode();
+ // if (reqNode != null && !reqNode.isEmpty()) {
+ // if (!cap.getCapabilitySources().contains(reqNode)) {
+ // continue;
+ // }
+ // }
+
+ RequirementAndRelationshipPair relationPair = getReqRelPair(cap);
+ Either<Boolean, StorageOperationStatus> eitherStatus = componentInstanceOperation.isAvailableCapabilty(currentCapCompInstance, relationPair);
+ if (eitherStatus.isRight() || eitherStatus.left().value() == false)
+ continue;
+ else {
+ aviableCapForRel = cap;
+ break;
+ }
+ }
+ }
+ return aviableCapForRel;
+ }
+
+ private Either<RequirementDefinition, ResponseFormat> findAviableRequiremen(String regName, String yamlName, UploadComponentInstanceInfo uploadComponentInstanceInfo, ComponentInstance currentCompInstance) {
+ Map<String, List<RequirementDefinition>> comInstRegDefMap = currentCompInstance.getRequirements();
+
+ Iterator<Entry<String, List<RequirementDefinition>>> regListValue = comInstRegDefMap.entrySet().iterator();
+ RequirementDefinition validRegDef = null;
+ while (regListValue.hasNext()) {
+ Entry<String, List<RequirementDefinition>> regInfoEntry = regListValue.next();
+
+ List<RequirementDefinition> comInstRegDefList = regInfoEntry.getValue();
+
+ for (RequirementDefinition comInstRegDef : comInstRegDefList) {
+ if (!regName.equals(comInstRegDef.getName()))
+ continue;
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirementUid(comInstRegDef.getUniqueId());
+ relationPair.setCapability(comInstRegDef.getCapability());
+ relationPair.setRequirementOwnerId(comInstRegDef.getOwnerId());
+ Either<Boolean, StorageOperationStatus> eitherStatus = componentInstanceOperation.isAvailableRequirement(currentCompInstance, relationPair);
+ if (eitherStatus.isLeft() && eitherStatus.left().value() == true) {
+ validRegDef = comInstRegDef;
+ break;
+ }
+ }
+ if (validRegDef != null)
+ break;
+ }
+
+ if (validRegDef == null) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ return Either.right(responseFormat);
+ }
+ return Either.left(validRegDef);
+ }
+
+ public Either<ParsedToscaYamlInfo, ResponseFormat> parseResourceInfoFromYaml(String yamlFileName, Resource resource, String resourceYml, User user) {
+
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(resourceYml);
+ Either<Object, ResultStatusEnum> toscaElementEither = ImportUtils.findToscaElement(mappedToscaTemplate, ToscaTagNamesEnum.TOPOLOGY_TEMPLATE, ToscaElementTypeEnum.ALL);
+ if (toscaElementEither.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE);
+ return Either.right(responseFormat);
+ }
+
+ Either<Map<String, InputDefinition>, ResponseFormat> createInputsEither = createInputsFromYaml(yamlFileName, mappedToscaTemplate, resource);
+ if (createInputsEither.isRight()) {
+ ResponseFormat responseFormat = createInputsEither.right().value();
+ return Either.right(responseFormat);
+ }
+
+ Either<Map<String, UploadComponentInstanceInfo>, ResponseFormat> uploadResInstancesEither = createResourcesInstanceInfoFromYaml(yamlFileName, mappedToscaTemplate, resource);
+ if (uploadResInstancesEither.isRight()) {
+ ResponseFormat responseFormat = uploadResInstancesEither.right().value();
+ return Either.right(responseFormat);
+ }
+
+ Either<Map<String, GroupDefinition>, ResponseFormat> createGroupsFromYaml = createGroupsFromYaml(yamlFileName, mappedToscaTemplate, resource);
+ if (createGroupsFromYaml.isRight()) {
+ ResponseFormat responseFormat = createGroupsFromYaml.right().value();
+ return Either.right(responseFormat);
+ }
+
+ ParsedToscaYamlInfo parsedToscaYamlInfo = new ParsedToscaYamlInfo();
+ parsedToscaYamlInfo.setInputs(createInputsEither.left().value());
+ parsedToscaYamlInfo.setInstances(uploadResInstancesEither.left().value());
+ parsedToscaYamlInfo.setGroups(createGroupsFromYaml.left().value());
+
+ return Either.left(parsedToscaYamlInfo);
+ }
+
+ private Either<Resource, ResponseFormat> createResourceInstances(User user, String yamlName, Resource resource, Map<String, UploadComponentInstanceInfo> uploadResInstancesMap, boolean inTransaction, boolean needLock,
+ Map<String, Resource> nodeTypeNamespaceMap) {
+ log.debug("createResourceInstances is {} - going to create resource instanse from CSAR", yamlName);
+ if (uploadResInstancesMap == null || uploadResInstancesMap.isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE);
+
+ return Either.right(responseFormat);
+
+ }
+ Map<String, Resource> existingnodeTypeMap = new HashMap<String, Resource>();
+ if (nodeTypeNamespaceMap != null && !nodeTypeNamespaceMap.isEmpty()) {
+ nodeTypeNamespaceMap.entrySet().stream().forEach(x -> existingnodeTypeMap.put(x.getValue().getToscaResourceName(), x.getValue()));
+ }
+
+ Iterator<Entry<String, UploadComponentInstanceInfo>> nodesInfoValue = uploadResInstancesMap.entrySet().iterator();
+ while (nodesInfoValue.hasNext()) {
+ log.debug("*************Going to create resource instances {}", yamlName);
+ Entry<String, UploadComponentInstanceInfo> uploadComponentInstanceInfoEntry = nodesInfoValue.next();
+ UploadComponentInstanceInfo uploadComponentInstanceInfo = uploadComponentInstanceInfoEntry.getValue();
+
+ // updating type if the type is node type name - we need to take the
+ // updated name
+ log.debug("*************Going to create resource instances {}", uploadComponentInstanceInfo.getName());
+ if (nodeTypeNamespaceMap.containsKey(uploadComponentInstanceInfo.getType())) {
+ uploadComponentInstanceInfo.setType(nodeTypeNamespaceMap.get(uploadComponentInstanceInfo.getType()).getToscaResourceName());
+ }
+
+ Either<Resource, ResponseFormat> eitherResource = validateResourceInstanceBeforeCreate(yamlName, inTransaction, uploadComponentInstanceInfo, existingnodeTypeMap);
+ if (eitherResource.isRight()) {
+ return eitherResource;
+ }
+ Resource refResource = eitherResource.left().value();
+
+ ComponentInstance componentInstance = new ComponentInstance();
+
+ componentInstance.setName(uploadComponentInstanceInfo.getName());
+ componentInstance.setComponentUid(refResource.getUniqueId());
+
+ ComponentTypeEnum containerComponentType = resource.getComponentType();
+ NodeTypeEnum containerNodeType = containerComponentType.getNodeType();
+
+ if (containerNodeType.equals(NodeTypeEnum.Resource) && uploadComponentInstanceInfo.getCapabilities() != null) {
+ Either<Map<String, List<CapabilityDefinition>>, ResponseFormat> getValidComponentInstanceCapabilitiesRes = getValidComponentInstanceCapabilities(refResource.getCapabilities(), uploadComponentInstanceInfo.getCapabilities());
+ if (getValidComponentInstanceCapabilitiesRes.isRight()) {
+ return Either.right(getValidComponentInstanceCapabilitiesRes.right().value());
+ } else {
+ componentInstance.setCapabilities(getValidComponentInstanceCapabilitiesRes.left().value());
+ }
+ }
+ if (!existingnodeTypeMap.containsKey(uploadComponentInstanceInfo.getType())) {
+ log.debug("createResourceInstances - not found lates version for resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ return Either.right(responseFormat);
+ }
+ Resource origResource = existingnodeTypeMap.get(uploadComponentInstanceInfo.getType());
+ ComponentInstanceBusinessLogic componentInstanceBL = getComponentInstanceBL();
+ Either<ComponentInstance, ResponseFormat> eitherCreateCI = componentInstanceBL.createComponentInstanceOnGraph(ComponentTypeEnum.RESOURCE_PARAM_NAME, resource, origResource, componentInstance, user.getUserId(), needLock, inTransaction);
+ if (eitherCreateCI.isRight()) {
+ log.debug("createResourceInstances - failed to create resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ return Either.right(eitherCreateCI.right().value());
+ }
+
+ ComponentInstance createdCI = eitherCreateCI.left().value();
+ createdCI.setName(uploadComponentInstanceInfo.getName());
+ /*
+ * updateComponentInstance(String containerComponentParam, org.openecomp.sdc.be.model.Component containerComponent, String componentInstanceId, ComponentInstance componentInstance, boolean inTransaction, boolean needLock)
+ */
+ eitherCreateCI = componentInstanceBL.updateComponentInstance(ComponentTypeEnum.RESOURCE_PARAM_NAME, resource, origResource, createdCI.getUniqueId(), createdCI, needLock, inTransaction);
+ if (eitherCreateCI.isRight()) {
+ log.debug("createResourceInstances - failed to update resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ return Either.right(eitherCreateCI.right().value());
+ }
+ log.debug("*************finished to create and update resource instances {}", uploadComponentInstanceInfo.getName());
+
+ }
+ log.debug("*************Going to get resource {}", resource.getUniqueId());
+ ComponentParametersView parametersView = new ComponentParametersView();
+ parametersView.disableAll();
+ parametersView.setIgnoreComponentInstances(false);
+ parametersView.setIgnoreUsers(false);
+ parametersView.setIgnoreInputs(false); // inputs are read when creating
+ // property values on instances
+ Either<Resource, StorageOperationStatus> eitherGerResource = resourceOperation.getComponent(resource.getUniqueId(), parametersView, inTransaction);
+ log.debug("*************finished to get resource {}", resource.getUniqueId());
+ if (eitherGerResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(eitherGerResource.right().value()), resource);
+
+ return Either.right(responseFormat);
+
+ }
+
+ if (eitherGerResource.left().value().getComponentInstances() == null || eitherGerResource.left().value().getComponentInstances().isEmpty()) {
+
+ log.debug("Error when create resource inctanse from csar. ComponentInstances list empty");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Error when create resource inctanse from csar. ComponentInstances list empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE);
+ return Either.right(responseFormat);
+
+ }
+
+ return Either.left(eitherGerResource.left().value());
+ }
+
+ private Either<Resource, ResponseFormat> validateResourceInstanceBeforeCreate(String yamlName, boolean inTransaction, UploadComponentInstanceInfo uploadComponentInstanceInfo, Map<String, Resource> nodeTypeNamespaceMap) {
+ log.debug("validateResourceInstanceBeforeCreate - going to validate resource instance with name {} and type before create", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ Resource refResource = null;
+ if (nodeTypeNamespaceMap.containsKey(uploadComponentInstanceInfo.getType())) {
+ refResource = nodeTypeNamespaceMap.get(uploadComponentInstanceInfo.getType());
+ } else {
+
+ Either<Boolean, StorageOperationStatus> eithervalidateResource = resourceOperation.validateToscaResourceNameExists(uploadComponentInstanceInfo.getType());
+ if ((eithervalidateResource.isRight() && eithervalidateResource.right().value() == StorageOperationStatus.NOT_FOUND) || eithervalidateResource.left().value() == true) {
+ log.debug("validateResourceInstanceBeforeCreate - resource instance with name {} and type not valid", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ return Either.right(responseFormat);
+ }
+ Either<Resource, StorageOperationStatus> findResourceEither = resourceOperation.getLatestCertifiedByToscaResourceName(uploadComponentInstanceInfo.getType(), inTransaction);
+ if (findResourceEither.isRight()) {
+ log.debug("validateResourceInstanceBeforeCreate - not found lates version for resource instance with name {} and type ", uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(findResourceEither.right().value()));
+ return Either.right(responseFormat);
+ }
+ refResource = findResourceEither.left().value();
+ nodeTypeNamespaceMap.put(refResource.getToscaResourceName(), refResource);
+ }
+ String componentState = refResource.getComponentMetadataDefinition().getMetadataDataDefinition().getState();
+ if (componentState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name())) {
+ log.debug("validateResourceInstanceBeforeCreate - component instance of component {} can not be created because the component is in an illegal state {}.", refResource.getName(), componentState);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.ILLEGAL_COMPONENT_STATE, refResource.getComponentType().getValue(), refResource.getName(), componentState);
+ return Either.right(responseFormat);
+ }
+ ResourceTypeEnum resourceTypeEnum = refResource.getResourceType();
+ if (resourceTypeEnum == ResourceTypeEnum.VF) {
+ log.debug("validateResourceInstanceBeforeCreate - ref resource type is ", resourceTypeEnum);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_NODE_TEMPLATE, yamlName, uploadComponentInstanceInfo.getName(), uploadComponentInstanceInfo.getType());
+ return Either.right(responseFormat);
+ }
+ return Either.left(refResource);
+ }
+
+ private Either<Map<String, UploadComponentInstanceInfo>, ResponseFormat> createResourcesInstanceInfoFromYaml(String yamlFileName, Map<String, Object> toscaJson, Resource resource) {
+ Map<String, UploadComponentInstanceInfo> moduleComponentInstances = new HashMap<String, UploadComponentInstanceInfo>();
+ Either<Map<String, UploadComponentInstanceInfo>, ResponseFormat> result = Either.left(moduleComponentInstances);
+ Either<Map<String, Object>, ResultStatusEnum> eitherNodesTemlates = ImportUtils.findFirstToscaMapElement(toscaJson, ToscaTagNamesEnum.NODE_TEMPLATES);
+ if (eitherNodesTemlates.isLeft()) {
+ Map<String, Object> jsonNodeTemplates = eitherNodesTemlates.left().value();
+
+ Iterator<Entry<String, Object>> nodesNameValue = jsonNodeTemplates.entrySet().iterator();
+ while (nodesNameValue.hasNext()) {
+ Entry<String, Object> nodeNameValue = nodesNameValue.next();
+ Either<UploadComponentInstanceInfo, ResponseFormat> eitherNode = createModuleComponentInstanceInfo(nodeNameValue.getValue());
+ if (eitherNode.isRight()) {
+ log.info("error when creating node template:{}, for resource:{}", nodeNameValue.getKey(), resource.getName());
+ return Either.right(eitherNode.right().value());
+ } else {
+ UploadComponentInstanceInfo uploadComponentInstanceInfo = eitherNode.left().value();
+ uploadComponentInstanceInfo.setName(nodeNameValue.getKey());
+ moduleComponentInstances.put(nodeNameValue.getKey(), uploadComponentInstanceInfo);
+ }
+
+ }
+
+ }
+ if (moduleComponentInstances.isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE, yamlFileName);
+ return Either.right(responseFormat);
+ }
+
+ return result;
+ }
+
+ private Either<UploadComponentInstanceInfo, ResponseFormat> createModuleComponentInstanceInfo(Object nodeTemplateJson) {
+
+ UploadComponentInstanceInfo nodeTemplateInfo = new UploadComponentInstanceInfo();
+ Either<UploadComponentInstanceInfo, ResponseFormat> result = Either.left(nodeTemplateInfo);
+
+ try {
+ if (nodeTemplateJson instanceof String) {
+ String nodeTemplateJsonString = (String) nodeTemplateJson;
+ nodeTemplateInfo.setType(nodeTemplateJsonString);
+ } else if (nodeTemplateJson instanceof Map) {
+ Map<String, Object> nodeTemplateJsonMap = (Map<String, Object>) nodeTemplateJson;
+ // Type
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.TYPE.getElementName())) {
+ nodeTemplateInfo.setType((String) nodeTemplateJsonMap.get(ToscaTagNamesEnum.TYPE.getElementName()));
+ }
+
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.REQUIREMENTS.getElementName())) {
+ Either<Map<String, List<UploadReqInfo>>, ResponseFormat> regResponse = createReqModuleFromYaml(nodeTemplateInfo, nodeTemplateJsonMap);
+ if (regResponse.isRight())
+ return Either.right(regResponse.right().value());
+ if (regResponse.left().value().size() > 0) {
+ nodeTemplateInfo.setRequirements(regResponse.left().value());
+ }
+ }
+
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.CAPABILITIES.getElementName())) {
+ Either<Map<String, List<UploadCapInfo>>, ResponseFormat> eitherCapRes = createCapModuleFromYaml(nodeTemplateInfo, nodeTemplateJsonMap);
+ if (eitherCapRes.isRight())
+ return Either.right(eitherCapRes.right().value());
+ if (eitherCapRes.left().value().size() > 0) {
+ nodeTemplateInfo.setCapabilities(eitherCapRes.left().value());
+ }
+ }
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.PROPERTIES.getElementName())) {
+ Either<Map<String, List<UploadPropInfo>>, ResponseFormat> regResponse = createPropModuleFromYaml(nodeTemplateJsonMap);
+ if (regResponse.isRight())
+ return Either.right(regResponse.right().value());
+ if (regResponse.left().value().size() > 0) {
+ nodeTemplateInfo.setProperties(regResponse.left().value());
+ }
+ }
+ } else {
+
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE));
+
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Import Resource - create capability");
+ BeEcompErrorManager.getInstance().logBeSystemError("Import Resource - create capability");
+ log.debug("error when creating capability, message:{}", e.getMessage(), e);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML));
+ }
+
+ return result;
+ }
+
+ private Either<Map<String, List<UploadPropInfo>>, ResponseFormat> createPropModuleFromYaml(Map<String, Object> nodeTemplateJsonMap) {
+ Map<String, List<UploadPropInfo>> moduleProp = new HashMap<String, List<UploadPropInfo>>();
+ Either<Map<String, List<UploadPropInfo>>, ResponseFormat> response = Either.left(moduleProp);
+ Either<Map<String, Object>, ResultStatusEnum> toscaProperties = ImportUtils.findFirstToscaMapElement(nodeTemplateJsonMap, ToscaTagNamesEnum.PROPERTIES);
+ if (toscaProperties.isLeft()) {
+ Map<String, Object> jsonProperties = toscaProperties.left().value();
+ for (Entry<String, Object> jsonPropObj : jsonProperties.entrySet()) {
+ // Property
+ String propName = jsonPropObj.getKey();
+ Object propValue = jsonPropObj.getValue();
+
+ if (valueContainsPattern(STR_REPLACE_PATTERN, propValue)) {
+ log.debug("Ignore property value {}.", propName);
+ continue;
+ }
+
+ if (valueContainsPattern(TOKEN_PATTERN, propValue)) {
+ log.debug("Ignore property value {}.", propName);
+ continue;
+ }
+ if (valueContainsPattern(GET_PROPERTY_PATTERN, propValue)) {
+ log.debug("Ignore property value {}.", propName);
+ continue;
+ }
+
+ if (valueContainsPattern(CONCAT_PATTERN, propValue)) {
+ log.debug("Ignore property value {}.", propName);
+ continue;
+ }
+
+ UploadPropInfo propertyDef = new UploadPropInfo();
+ propertyDef.setValue(propValue);
+ if (propValue instanceof Map) {
+ if (((Map<String, Object>) propValue).containsKey(ToscaTagNamesEnum.TYPE.getElementName())) {
+ propertyDef.setType(((Map<String, Object>) propValue).get(ToscaTagNamesEnum.TYPE.getElementName()).toString());
+ }
+
+ if (((Map<String, Object>) propValue).containsKey(ToscaTagNamesEnum.GET_INPUT.getElementName())
+ || ImportUtils.getPropertyJsonStringValue(propValue, ToscaPropertyType.MAP.getType()).contains(ToscaTagNamesEnum.GET_INPUT.getElementName())) {
+ createGetInputModuleFromMap(propName, (Map<String, Object>) propValue, propertyDef);
+ }
+
+ if (((Map<String, Object>) propValue).containsKey(ToscaTagNamesEnum.DESCRIPTION.getElementName())) {
+ propertyDef.setDescription(((Map<String, Object>) propValue).get(ToscaTagNamesEnum.DESCRIPTION.getElementName()).toString());
+ }
+ if (((Map<String, Object>) propValue).containsKey(ToscaTagNamesEnum.DEFAULT_VALUE.getElementName())) {
+ propertyDef.setValue(((Map<String, Object>) propValue).get(ToscaTagNamesEnum.DEFAULT_VALUE.getElementName()));
+ }
+ if (((Map<String, Object>) propValue).containsKey(ToscaTagNamesEnum.IS_PASSWORD.getElementName())) {
+ propertyDef.setPassword(Boolean.getBoolean(((Map<String, Object>) propValue).get(ToscaTagNamesEnum.IS_PASSWORD.getElementName()).toString()));
+ } else {
+ propertyDef.setValue(propValue);
+ }
+ } else if (propValue instanceof List) {
+ List<Object> propValueList = (List<Object>) propValue;
+
+ createInputPropList(propertyDef, propValueList);
+ propertyDef.setValue(propValue);
+ }
+
+ propertyDef.setName(propName);
+ if (moduleProp.containsKey(propName)) {
+ moduleProp.get(propName).add(propertyDef);
+ } else {
+ List<UploadPropInfo> list = new ArrayList<UploadPropInfo>();
+ list.add(propertyDef);
+ moduleProp.put(propName, list);
+ }
+ }
+ }
+ return response;
+ }
+
+ private void createInputPropList(UploadPropInfo propertyDef, List<Object> propValueList) {
+ for (Object objValue : propValueList) {
+
+ if (objValue instanceof Map) {
+ Map<String, Object> objMap = (Map<String, Object>) objValue;
+ Set<String> keys = objMap.keySet();
+ for (String key : keys) {
+ Object value = objMap.get(key);
+ if (value instanceof Map) {
+ createGetInputModuleFromMap(key, (Map<String, Object>) value, propertyDef);
+
+ } else if (value instanceof List) {
+ List<Object> propSubValueList = (List<Object>) value;
+
+ createInputPropList(propertyDef, propSubValueList);
+ }
+
+ }
+
+ } else if (objValue instanceof List) {
+ List<Object> propSubValueList = (List<Object>) objValue;
+
+ createInputPropList(propertyDef, propSubValueList);
+
+ }
+
+ }
+ }
+
+ private void createGetInputModuleFromMap(String propName, Map<String, Object> propValue, UploadPropInfo propertyDef) {
+
+ if (propValue.containsKey(ToscaTagNamesEnum.GET_INPUT.getElementName())) {
+ Object getInput = propValue.get(ToscaTagNamesEnum.GET_INPUT.getElementName());
+ GetInputValueInfo getInputInfo = new GetInputValueInfo();
+ List<GetInputValueInfo> getInputs = propertyDef.getGet_input();
+ if (getInputs == null) {
+ getInputs = new ArrayList<GetInputValueInfo>();
+ }
+ if (getInput instanceof String) {
+
+ getInputInfo.setInputName((String) getInput);
+ getInputInfo.setPropName(propName);
+
+ } else if (getInput instanceof List) {
+ List<Object> getInputList = (List<Object>) getInput;
+ getInputInfo.setPropName(propName);
+ getInputInfo.setInputName((String) getInputList.get(0));
+ if (getInputList.size() >= 1) {
+ Object indexObj = getInputList.get(1);
+ if (indexObj instanceof Integer) {
+ getInputInfo.setIndexValue((Integer) indexObj);
+ } else if (indexObj instanceof Float) {
+ int index = ((Float) indexObj).intValue();
+ getInputInfo.setIndexValue(index);
+ } else if (indexObj instanceof Map && ((Map<String, Object>) indexObj).containsKey(ToscaTagNamesEnum.GET_INPUT.getElementName())) {
+ Object index = ((Map<String, Object>) indexObj).get(ToscaTagNamesEnum.GET_INPUT.getElementName());
+ GetInputValueInfo getInputInfoIndex = new GetInputValueInfo();
+ getInputInfoIndex.setInputName((String) index);
+ getInputInfoIndex.setPropName(propName);
+ getInputInfo.setGetInputIndex(getInputInfoIndex);
+ }
+ getInputInfo.setList(true);
+ }
+
+ }
+ getInputs.add(getInputInfo);
+ propertyDef.setGet_input(getInputs);
+ propertyDef.setValue(propValue);
+ } else {
+ Set<String> keys = propValue.keySet();
+ for (String key : keys) {
+ Object value = propValue.get(key);
+ if (value instanceof Map) {
+ createGetInputModuleFromMap(key, (Map<String, Object>) value, propertyDef);
+
+ } else if (value instanceof List) {
+ List<Object> valueList = (List<Object>) value;
+ for (Object o : valueList) {
+ if (o instanceof Map) {
+ createGetInputModuleFromMap(key, (Map<String, Object>) o, propertyDef);
+
+ }
+ }
+
+ }
+
+ }
+
+ }
+ }
+
+ /*
+ * private boolean valueContainsStrReplace(Object propValue) {
+ *
+ * log.debug("valueContainsStrReplace value is {}", propValue); boolean result = false; if (propValue != null) { log.debug("valueContainsStrReplace value is {}", propValue.getClass()); Matcher matcher =
+ * STR_REPLACE_PATTERN.matcher(propValue.toString()); result = matcher.find(); }
+ *
+ * return result; }
+ *
+ * private boolean valueContainsToken(Object propValue) {
+ *
+ * log.debug("valueContainsToken value is {}", propValue); boolean result = false; if (propValue != null) { log.debug("valueContainsToken value is {}", propValue.getClass()); Matcher matcher = TOKEN_PATTERN.matcher(propValue.toString()); result =
+ * matcher.find(); }
+ *
+ * return result; }
+ */
+
+ private boolean valueContainsPattern(Pattern pattern, Object propValue) {
+
+ log.debug("valueContainsToken value is {}", propValue);
+ boolean result = false;
+ if (propValue != null) {
+ log.debug("valueContainspattern value is {}", propValue.getClass());
+ Matcher matcher = pattern.matcher(propValue.toString());
+ result = matcher.find();
+ }
+
+ return result;
+
+ }
+
+ private Either<Map<String, List<UploadCapInfo>>, ResponseFormat> createCapModuleFromYaml(UploadComponentInstanceInfo nodeTemplateInfo, Map<String, Object> nodeTemplateJsonMap) {
+ Map<String, List<UploadCapInfo>> moduleCap = new HashMap<String, List<UploadCapInfo>>();
+ Either<Map<String, List<UploadCapInfo>>, ResponseFormat> response = Either.left(moduleCap);
+ Either<List<Object>, ResultStatusEnum> toscaRequirements = ImportUtils.findFirstToscaListElement(nodeTemplateJsonMap, ToscaTagNamesEnum.CAPABILITIES);
+ if (toscaRequirements.isLeft()) {
+ List<Object> jsonCapabilities = toscaRequirements.left().value();
+
+ for (Object jsonCapObj : jsonCapabilities) {
+ // Requirement
+ Map<String, Object> capJsonWrapper = (Map<String, Object>) jsonCapObj;
+ String capName = capJsonWrapper.keySet().iterator().next();
+ Either<UploadCapInfo, ResponseFormat> eitherCap = createModuleNodeTemplateCap(capJsonWrapper.get(capName));
+ if (eitherCap.isRight()) {
+ log.info("error when creating Requirement:{}, for node:{}", capName, nodeTemplateInfo);
+ return Either.right(eitherCap.right().value());
+ } else {
+ UploadCapInfo requirementDef = eitherCap.left().value();
+ requirementDef.setName(capName);
+ if (moduleCap.containsKey(capName)) {
+ moduleCap.get(capName).add(requirementDef);
+ } else {
+ List<UploadCapInfo> list = new ArrayList<UploadCapInfo>();
+ list.add(requirementDef);
+ moduleCap.put(capName, list);
+ }
+
+ }
+ }
+
+ }
+
+ return response;
+ }
+
+ private Either<Map<String, List<UploadReqInfo>>, ResponseFormat> createReqModuleFromYaml(UploadComponentInstanceInfo nodeTemplateInfo, Map<String, Object> nodeTemplateJsonMap) {
+ Map<String, List<UploadReqInfo>> moduleRequirements = new HashMap<String, List<UploadReqInfo>>();
+ Either<Map<String, List<UploadReqInfo>>, ResponseFormat> response = Either.left(moduleRequirements);
+ Either<List<Object>, ResultStatusEnum> toscaRequirements = ImportUtils.findFirstToscaListElement(nodeTemplateJsonMap, ToscaTagNamesEnum.REQUIREMENTS);
+ if (toscaRequirements.isLeft()) {
+ List<Object> jsonRequirements = toscaRequirements.left().value();
+
+ for (Object jsonRequirementObj : jsonRequirements) {
+ // Requirement
+ Map<String, Object> requirementJsonWrapper = (Map<String, Object>) jsonRequirementObj;
+ String requirementName = requirementJsonWrapper.keySet().iterator().next();
+ Either<UploadReqInfo, ResponseFormat> eitherRequirement = createModuleNodeTemplateReg(requirementJsonWrapper.get(requirementName));
+ if (eitherRequirement.isRight()) {
+ log.info("error when creating Requirement:{}, for node:{}", requirementName, nodeTemplateInfo);
+ return Either.right(eitherRequirement.right().value());
+ } else {
+ UploadReqInfo requirementDef = eitherRequirement.left().value();
+ requirementDef.setName(requirementName);
+ if (moduleRequirements.containsKey(requirementName)) {
+ moduleRequirements.get(requirementName).add(requirementDef);
+ } else {
+ List<UploadReqInfo> list = new ArrayList<UploadReqInfo>();
+ list.add(requirementDef);
+ moduleRequirements.put(requirementName, list);
+ }
+
+ }
+ }
+
+ }
+ return response;
+ }
+
+ private Either<UploadCapInfo, ResponseFormat> createModuleNodeTemplateCap(Object capObject) {
+ UploadCapInfo capTemplateInfo = new UploadCapInfo();
+ Either<UploadCapInfo, ResponseFormat> result = Either.left(capTemplateInfo);
+
+ if (capObject instanceof String) {
+ String nodeTemplateJsonString = (String) capObject;
+ capTemplateInfo.setNode(nodeTemplateJsonString);
+ } else if (capObject instanceof Map) {
+ Map<String, Object> nodeTemplateJsonMap = (Map<String, Object>) capObject;
+ // Type
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.NODE.getElementName())) {
+ capTemplateInfo.setNode((String) nodeTemplateJsonMap.get(ToscaTagNamesEnum.NODE.getElementName()));
+ }
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.TYPE.getElementName())) {
+ capTemplateInfo.setType((String) nodeTemplateJsonMap.get(ToscaTagNamesEnum.TYPE.getElementName()));
+ }
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.VALID_SOURCE_TYPES.getElementName())) {
+ Either<List<Object>, ResultStatusEnum> validSourceTypesRes = ImportUtils.findFirstToscaListElement(nodeTemplateJsonMap, ToscaTagNamesEnum.VALID_SOURCE_TYPES);
+ if (validSourceTypesRes.isLeft()) {
+ capTemplateInfo.setValidSourceTypes(validSourceTypesRes.left().value().stream().map(o -> o.toString()).collect(Collectors.toList()));
+ }
+ }
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.PROPERTIES.getElementName())) {
+ Either<Map<String, List<UploadPropInfo>>, ResponseFormat> regResponse = createPropModuleFromYaml(nodeTemplateJsonMap);
+ if (regResponse.isRight())
+ return Either.right(regResponse.right().value());
+ if (!regResponse.left().value().isEmpty()) {
+ List<UploadPropInfo> properties = new ArrayList<UploadPropInfo>();
+ regResponse.left().value().values().forEach(list -> properties.addAll(list));
+ if (!properties.isEmpty())
+ capTemplateInfo.setProperties(properties);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Either<UploadReqInfo, ResponseFormat> createModuleNodeTemplateReg(Object regObject) {
+
+ UploadReqInfo regTemplateInfo = new UploadReqInfo();
+ Either<UploadReqInfo, ResponseFormat> result = Either.left(regTemplateInfo);
+
+ if (regObject instanceof String) {
+ String nodeTemplateJsonString = (String) regObject;
+ regTemplateInfo.setNode(nodeTemplateJsonString);
+ } else if (regObject instanceof Map) {
+ Map<String, Object> nodeTemplateJsonMap = (Map<String, Object>) regObject;
+ // Type
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.NODE.getElementName())) {
+ regTemplateInfo.setNode((String) nodeTemplateJsonMap.get(ToscaTagNamesEnum.NODE.getElementName()));
+ }
+ // US740820 Relate RIs according to capability name
+ if (nodeTemplateJsonMap.containsKey(ToscaTagNamesEnum.CAPABILITY.getElementName())) {
+ regTemplateInfo.setCapabilityName((String) nodeTemplateJsonMap.get(ToscaTagNamesEnum.CAPABILITY.getElementName()));
+ }
+ }
+
+ return result;
+ }
+
+ public Either<Resource, ResponseFormat> propagateStateToCertified(User user, Resource resource, LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean inTransaction, boolean needLock) {
+ Either<Resource, ResponseFormat> result = null;
+
+ // resource updated with checkout. certify the resource
+ if (resource.getLifecycleState().equals(LifecycleStateEnum.CERTIFIED)) {
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> eitherPopulated = populateToscaArtifacts(resource, user, false, inTransaction, needLock);
+ result = eitherPopulated.isLeft() ? Either.left(resource) : Either.right(eitherPopulated.right().value());
+ return result;
+ }
+ try {
+ result = lifecycleBusinessLogic.changeState(resource.getUniqueId(), user, LifeCycleTransitionEnum.CERTIFICATION_REQUEST, lifecycleChangeInfo, inTransaction, needLock);
+ if (result.isLeft()) {
+ resource = result.left().value();
+ result = lifecycleBusinessLogic.changeState(resource.getUniqueId(), user, LifeCycleTransitionEnum.START_CERTIFICATION, lifecycleChangeInfo, inTransaction, needLock);
+ }
+ if (result.isLeft()) {
+ resource = result.left().value();
+ result = lifecycleBusinessLogic.changeState(resource.getUniqueId(), user, LifeCycleTransitionEnum.CERTIFY, lifecycleChangeInfo, inTransaction, needLock);
+ }
+ return result;
+ } finally {
+ if (result == null || result.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Change LifecycleState - Certify");
+ BeEcompErrorManager.getInstance().logBeSystemError("Change LifecycleState - Certify");
+ if (inTransaction == false) {
+ log.debug("operation failed. do rollback");
+ titanGenericDao.rollback();
+ }
+ } else if (inTransaction == false) {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ /**
+ * @deprecated Use {@link #createOrUpdateResourceByImport(Resource,User,boolean, boolean,boolean)} instead
+ */
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createOrUpdateResourceByImport(Resource resource, User user, AuditingActionEnum auditingEnum, boolean isNormative, boolean needLock) {
+ return createOrUpdateResourceByImport(resource, user, isNormative, false, needLock);
+ }
+
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createOrUpdateResourceByImport(Resource resource, User user, boolean isNormative, boolean isInTransaction, boolean needLock) {
+
+ // check if resource already exist
+ Either<Resource, StorageOperationStatus> latestByName = resourceOperation.getLatestByName(resource.getName(), true);
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> result = null;
+
+ // create
+ if (latestByName.isRight() && latestByName.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+
+ Either<Resource, StorageOperationStatus> latestByToscaName = resourceOperation.getLatestByToscaResourceName(resource.getToscaResourceName(), true);
+ if (latestByToscaName.isRight() && latestByToscaName.right().value().equals(StorageOperationStatus.NOT_FOUND))
+ result = createResourceByImport(resource, user, isNormative, isInTransaction);
+
+ else {
+ StorageOperationStatus status = latestByName.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeResourceMissingError, "Create / Update resource by import", resource.getName());
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("Create / Update resource by import", ComponentTypeEnum.RESOURCE.getValue(), resource.getName());
+ log.debug("resource already exist {}. status={}", resource.getName(), status);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_ALREADY_EXISTS);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ result = Either.right(responseFormat);
+ }
+
+ }
+
+ // update
+ else if (latestByName.isLeft()) {
+ result = updateExistingResourceByImport(resource, latestByName.left().value(), user, isNormative, needLock);
+ }
+
+ // error
+ else {
+ StorageOperationStatus status = latestByName.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeResourceMissingError, "Create / Update resource by import", resource.getName());
+ log.debug("failed to get latest version of resource {}. status={}", resource.getName(), status);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(latestByName.right().value()), resource);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ result = Either.right(responseFormat);
+ }
+ return result;
+
+ }
+
+ private Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createResourceByImport(Resource resource, User user, boolean isNormative, boolean isInTransaction) {
+ log.debug("resource with name {} does not exist. create new resource", resource.getName());
+ Either<Resource, ResponseFormat> response = validateResourceBeforeCreate(resource, user, AuditingActionEnum.IMPORT_RESOURCE, isInTransaction);
+ if (response.isRight()) {
+ return Either.right(response.right().value());
+ }
+ Either<Resource, ResponseFormat> createResponse = createResourceByDao(resource, user, AuditingActionEnum.IMPORT_RESOURCE, isNormative, isInTransaction);
+ if (createResponse.isRight()) {
+ return Either.right(createResponse.right().value());
+ } else {
+ ImmutablePair<Resource, ActionStatus> resourcePair = new ImmutablePair<>(createResponse.left().value(), ActionStatus.CREATED);
+ ASDCKpiApi.countImportResourcesKPI();
+ return Either.left(resourcePair);
+
+ }
+ }
+
+ public boolean isResourceExist(String resourceName) {
+ Either<Resource, StorageOperationStatus> latestByName = resourceOperation.getLatestByName(resourceName, false);
+ return latestByName.isLeft();
+ }
+
+ private Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> updateExistingResourceByImport(Resource newResource, Resource oldResource, User user, boolean inTransaction, boolean needLock) {
+ String lockedResourceId = oldResource.getUniqueId();
+ log.debug("found resource: name={}, id={}, version={}, state={}", oldResource.getName(), lockedResourceId, oldResource.getVersion(), oldResource.getLifecycleState());
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> result = null;
+ try {
+ if (needLock) {
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(lockedResourceId, oldResource, "Update Resource by Import");
+ if (lockResult.isRight()) {
+ return Either.right(lockResult.right().value());
+ }
+ }
+
+ Either<Resource, ResponseFormat> prepareResourceForUpdate = prepareResourceForUpdate(oldResource, user, inTransaction, false);
+ if (prepareResourceForUpdate.isRight()) {
+ ResponseFormat responseFormat = prepareResourceForUpdate.right().value();
+ log.info("resource {} cannot be updated. reason={}", lockedResourceId, responseFormat.getFormattedMessage());
+ componentsUtils.auditResource(responseFormat, user, newResource, oldResource.getLifecycleState().name(), oldResource.getVersion(), AuditingActionEnum.IMPORT_RESOURCE, null);
+ result = Either.right(prepareResourceForUpdate.right().value());
+ return result;
+ }
+ oldResource = prepareResourceForUpdate.left().value();
+
+ mergeOldResourceMetadataWithNew(oldResource, newResource);
+
+ Either<Boolean, ResponseFormat> validateFieldsResponse = validateResourceFieldsBeforeUpdate(oldResource, newResource, inTransaction);
+ if (validateFieldsResponse.isRight()) {
+ result = Either.right(validateFieldsResponse.right().value());
+ return result;
+ }
+
+ // contact info normalization
+ newResource.setContactId(newResource.getContactId().toLowerCase());
+ // non-updatable fields
+ newResource.setCreatorUserId(user.getUserId());
+ newResource.setCreatorFullName(user.getFullName());
+ newResource.setLastUpdaterUserId(user.getUserId());
+ newResource.setLastUpdaterFullName(user.getFullName());
+ newResource.setUniqueId(oldResource.getUniqueId());
+ newResource.setVersion(oldResource.getVersion());
+ newResource.setInvariantUUID(oldResource.getInvariantUUID());
+ newResource.setLifecycleState(oldResource.getLifecycleState());
+ newResource.setUUID(oldResource.getUUID());
+ newResource.setNormalizedName(oldResource.getNormalizedName());
+ newResource.setSystemName(oldResource.getSystemName());
+ if (oldResource.getCsarUUID() != null) {
+ newResource.setCsarUUID(oldResource.getCsarUUID());
+ }
+ if (oldResource.getImportedToscaChecksum() != null) {
+ newResource.setImportedToscaChecksum(oldResource.getImportedToscaChecksum());
+ }
+ newResource.setAbstract(oldResource.isAbstract());
+
+ if (newResource.getDerivedFrom() == null || newResource.getDerivedFrom().isEmpty()) {
+ newResource.setDerivedFrom(oldResource.getDerivedFrom());
+ }
+ // TODO rhalili: handle artifacts here (delete from old resource and
+ // add for new)
+ // TODO rbetzer: remove after migration - in case of resources
+ // created without tosca artifacts - add the placeholders
+ if (newResource.getToscaArtifacts() == null || newResource.getToscaArtifacts().isEmpty()) {
+ setToscaArtifactsPlaceHolders(newResource, user);
+ }
+ Either<Resource, StorageOperationStatus> overrideResource = resourceOperation.overrideResource(newResource, oldResource, true);
+
+ if (overrideResource.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(overrideResource.right().value()), newResource);
+ componentsUtils.auditResource(responseFormat, user, newResource, newResource.getLifecycleState().name(), newResource.getVersion(), AuditingActionEnum.IMPORT_RESOURCE, null);
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ log.debug("Resource updated successfully!!!");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ componentsUtils.auditResource(responseFormat, user, newResource, oldResource.getLifecycleState().name(), oldResource.getVersion(), AuditingActionEnum.IMPORT_RESOURCE, null);
+
+ ImmutablePair<Resource, ActionStatus> resourcePair = new ImmutablePair<>(overrideResource.left().value(), ActionStatus.OK);
+ result = Either.left(resourcePair);
+ return result;
+ } finally {
+ if (result == null || result.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Change LifecycleState - Certify");
+ BeEcompErrorManager.getInstance().logBeSystemError("Change LifecycleState - Certify");
+ log.debug("operation failed. do rollback");
+ titanGenericDao.rollback();
+ } else if (inTransaction == false) {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ if (needLock == true) {
+ log.debug("unlock resource {}", lockedResourceId);
+ graphLockOperation.unlockComponent(lockedResourceId, NodeTypeEnum.Resource);
+ }
+ }
+
+ }
+
+ /**
+ * Merge old resource with new. Keep old category and vendor name without change
+ *
+ * @param oldResource
+ * @param newResource
+ */
+ private void mergeOldResourceMetadataWithNew(Resource oldResource, Resource newResource) {
+
+ // keep old category and vendor name without change
+ // merge the rest of the resource metadata
+ if (newResource.getTags() == null || newResource.getTags().isEmpty()) {
+ newResource.setTags(oldResource.getTags());
+ }
+
+ if (newResource.getDescription() == null) {
+ newResource.setDescription(oldResource.getDescription());
+ }
+
+ if (newResource.getVendorRelease() == null) {
+ newResource.setVendorRelease(oldResource.getVendorRelease());
+ }
+
+ if (newResource.getContactId() == null) {
+ newResource.setContactId(oldResource.getContactId());
+ }
+
+ newResource.setCategories(oldResource.getCategories());
+ newResource.setVendorName(oldResource.getVendorName());
+ }
+
+ private Either<Resource, ResponseFormat> prepareResourceForUpdate(Resource latestResource, User user, boolean inTransaction, boolean needLock) {
+
+ Either<Resource, ResponseFormat> result = Either.left(latestResource);
+ // check if user can edit resource
+ if (!ComponentValidationUtils.canWorkOnResource(latestResource, user.getUserId())) {
+ // checkout
+ Either<Resource, ResponseFormat> changeState = lifecycleBusinessLogic.changeState(latestResource.getUniqueId(), user, LifeCycleTransitionEnum.CHECKOUT, new LifecycleChangeInfoWithAction("update by import"), inTransaction, needLock);
+ result = changeState;
+ }
+
+ return result;
+ }
+
+ public Either<Resource, ResponseFormat> validateResourceBeforeCreate(Resource resource, User user, AuditingActionEnum actionEnum, boolean inTransaction) {
+
+ Either<Boolean, ResponseFormat> eitherValidation = validateResourceFieldsBeforeCreate(user, getResourceOperation(), resource, actionEnum, inTransaction);
+ if (eitherValidation.isRight()) {
+ return Either.right(eitherValidation.right().value());
+ }
+
+ eitherValidation = validateCapabilityTypesCreate(user, getCapabilityTypeOperation(), resource, actionEnum, inTransaction);
+ if (eitherValidation.isRight()) {
+ return Either.right(eitherValidation.right().value());
+ }
+ eitherValidation = validateLifecycleTypesCreate(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return Either.right(eitherValidation.right().value());
+ }
+ eitherValidation = validateResourceType(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return Either.right(eitherValidation.right().value());
+ }
+
+ resource.setCreatorUserId(user.getUserId());
+ resource.setCreatorFullName(user.getFirstName() + " " + user.getLastName());
+ resource.setContactId(resource.getContactId().toLowerCase());
+ if (resource.getResourceType().equals(ResourceTypeEnum.VF)) {
+ resource.setToscaResourceName(CommonBeUtils.generateToscaResourceName(ResourceTypeEnum.VF.name(), resource.getSystemName()));
+ }
+
+ // Generate invariant UUID - must be here and not in operation since it
+ // should stay constant during clone
+ String invariantUUID = UniqueIdBuilder.buildInvariantUUID();
+ resource.setInvariantUUID(invariantUUID);
+
+ return Either.left(resource);
+ }
+
+ private Either<Boolean, ResponseFormat> validateResourceType(User user, Resource resource, AuditingActionEnum actionEnum) {
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ if (resource.getResourceType() == null) {
+ log.debug("Invalid resource type for resource");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ eitherResult = Either.right(errorResponse);
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ }
+ return eitherResult;
+ }
+
+ private Either<Boolean, ResponseFormat> validateLifecycleTypesCreate(User user, Resource resource, AuditingActionEnum actionEnum) {
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ if (resource.getInterfaces() != null && resource.getInterfaces().size() > 0) {
+ log.debug("validate interface lifecycle Types Exist");
+ Iterator<InterfaceDefinition> intItr = resource.getInterfaces().values().iterator();
+ while (intItr.hasNext() && eitherResult.isLeft()) {
+ InterfaceDefinition interfaceDefinition = intItr.next();
+ String intType = interfaceDefinition.getUniqueId();
+ Either<InterfaceDefinition, StorageOperationStatus> eitherCapTypeFound = interfaceTypeOperation.getInterface(intType);
+ if (eitherCapTypeFound.isRight()) {
+ if (eitherCapTypeFound.right().value() == StorageOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInterfaceMissingError, "Create Resource - validateLifecycleTypesCreate", intType);
+ BeEcompErrorManager.getInstance().logBeGraphObjectMissingError("Create Resource - validateLifecycleTypesCreate", "Interface", intType);
+ log.debug("Lifecycle Type: {} is required by resource: {} but does not exist in the DB", intType, resource.getName());
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Create Resource - validateLifecycleTypesCreate");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Create Resource - validateLifecycleTypesCreate");
+ log.debug("request to data model failed with error: {}", eitherCapTypeFound.right().value().name());
+ }
+
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_LIFECYCLE_TYPE, intType);
+ eitherResult = Either.right(errorResponse);
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ }
+
+ }
+ }
+ return eitherResult;
+ }
+
+ private Either<Boolean, ResponseFormat> validateCapabilityTypesCreate(User user, ICapabilityTypeOperation capabilityTypeOperation, Resource resource, AuditingActionEnum actionEnum, boolean inTransaction) {
+
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ if (resource.getCapabilities() != null && resource.getCapabilities().size() > 0) {
+ log.debug("validate capability Types Exist - capabilities section");
+
+ for (String type : resource.getCapabilities().keySet()) {
+
+ eitherResult = validateCapabilityTypeExists(user, capabilityTypeOperation, resource, actionEnum, eitherResult, type, inTransaction);
+ if (eitherResult.isRight()) {
+ return Either.right(eitherResult.right().value());
+ }
+ }
+ }
+
+ if (resource.getRequirements() != null && resource.getRequirements().size() > 0) {
+ log.debug("validate capability Types Exist - requirements section");
+
+ for (String type : resource.getRequirements().keySet()) {
+
+ eitherResult = validateCapabilityTypeExists(user, capabilityTypeOperation, resource, actionEnum, eitherResult, type, inTransaction);
+ if (eitherResult.isRight()) {
+ return Either.right(eitherResult.right().value());
+ }
+ }
+ }
+
+ return eitherResult;
+ }
+
+ private Either<Boolean, ResponseFormat> validateCapabilityTypeExists(User user, ICapabilityTypeOperation capabilityTypeOperation, Resource resource, AuditingActionEnum actionEnum, Either<Boolean, ResponseFormat> eitherResult, String type,
+ boolean inTransaction) {
+ Either<CapabilityTypeDefinition, StorageOperationStatus> eitherCapTypeFound = capabilityTypeOperation.getCapabilityType(type, inTransaction);
+ if (eitherCapTypeFound.isRight()) {
+ if (eitherCapTypeFound.right().value() == StorageOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeCapabilityTypeMissingError, "Create Resource - validateCapabilityTypesCreate", type);
+ BeEcompErrorManager.getInstance().logBeGraphObjectMissingError("Create Resource - validateCapabilityTypesCreate", "Capability Type", type);
+ log.debug("Capability Type: {} is required by resource: {} but does not exist in the DB", type, resource.getName());
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Create Resource - validateCapabilityTypesCreate");
+ }
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Create Resource - validateCapabilityTypesCreate");
+ log.debug("Trying to get capability type {} failed with error: {}", type, eitherCapTypeFound.right().value().name());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_CAPABILITY_TYPE, type);
+ eitherResult = Either.right(errorResponse);
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ }
+ return eitherResult;
+ }
+
+ public Either<Resource, ResponseFormat> createResourceByDao(Resource resource, User user, AuditingActionEnum actionEnum, boolean isNormative) {
+ return createResourceByDao(resource, user, actionEnum, isNormative, false);
+ }
+
+ public Either<Resource, ResponseFormat> createResourceByDao(Resource resource, User user, AuditingActionEnum actionEnum, boolean isNormative, boolean inTransaction) {
+ // create resource
+
+ // lock new resource name in order to avoid creation resource with same
+ // name
+ if (inTransaction == false) {
+ Either<Boolean, ResponseFormat> lockResult = lockComponentByName(resource.getSystemName(), resource, "Create Resource");
+ if (lockResult.isRight()) {
+ ResponseFormat responseFormat = lockResult.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ return Either.right(responseFormat);
+ }
+
+ log.debug("name is locked {}. status = {}", resource.getSystemName(), lockResult);
+ }
+ try {
+ Either<Resource, ResponseFormat> respStatus = createResourceTransaction(resource, user, actionEnum, isNormative, inTransaction);
+ if (respStatus.isLeft()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ ASDCKpiApi.countCreatedResourcesKPI();
+ } else
+ componentsUtils.auditResource(respStatus.right().value(), user, resource, "", "", actionEnum, null);
+ return respStatus;
+
+ } finally {
+ if (inTransaction == false) {
+ // graphLockOperation.unlockComponent(resource.getSystemName(),
+ // NodeTypeEnum.Resource);
+ graphLockOperation.unlockComponentByName(resource.getSystemName(), resource.getUniqueId(), NodeTypeEnum.Resource);
+ }
+ }
+ }
+
+ private Either<Resource, ResponseFormat> createResourceTransaction(Resource resource, User user, AuditingActionEnum actionEnum, boolean isNormative, boolean inTransaction) {
+ // validate resource name uniqueness
+ log.debug("validate resource name");
+ Either<Boolean, ResponseFormat> eitherValidation = validateResourceNameExists(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return Either.right(eitherValidation.right().value());
+ }
+
+ log.debug("send resource {} to dao for create", resource.getName());
+
+ createArtifactsPlaceHolderData(resource, user);
+
+ //
+ if (!isNormative) {
+ // enrich object
+ log.debug("enrich resource with creator, version and state");
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ resource.setVersion(INITIAL_VERSION);
+ resource.setHighestVersion(true);
+ resource.setAbstract(false);
+ }
+
+ Either<Resource, StorageOperationStatus> dataModelResponse = resourceOperation.createResource(resource, inTransaction);
+
+ // Resource created successfully!!!
+ if (dataModelResponse.isLeft()) {
+ log.debug("Resource created successfully!!!");
+
+ return Either.left(dataModelResponse.left().value());
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()), resource);
+
+ return Either.right(responseFormat);
+ }
+
+ private void createArtifactsPlaceHolderData(Resource resource, User user) {
+ // create mandatory artifacts
+
+ // TODO it must be removed after that artifact uniqueId creation will be
+ // moved to ArtifactOperation
+ // String resourceUniqueId =
+ // UniqueIdBuilder.buildResourceUniqueId(resource.getResourceName(),
+ // resource.getResourceVersion());
+
+ setInformationalArtifactsPlaceHolder(resource, user);
+ setDeploymentArtifactsPlaceHolder(resource, user);
+ setToscaArtifactsPlaceHolders(resource, user);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void setDeploymentArtifactsPlaceHolder(Component component, User user) {
+ Resource resource = (Resource) component;
+ Map<String, ArtifactDefinition> artifactMap = resource.getDeploymentArtifacts();
+ if (artifactMap == null) {
+ artifactMap = new HashMap<String, ArtifactDefinition>();
+ }
+ Map<String, Object> deploymentResourceArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getDeploymentResourceArtifacts();
+ if (deploymentResourceArtifacts != null) {
+ Iterator<Entry<String, Object>> iterator = deploymentResourceArtifacts.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<String, Object> currEntry = iterator.next();
+ boolean shouldCreateArtifact = true;
+ Map<String, Object> artifactDetails = (Map<String, Object>) currEntry.getValue();
+ Object object = artifactDetails.get(PLACE_HOLDER_RESOURCE_TYPES);
+ if (object != null) {
+ List<String> artifactTypes = (List<String>) object;
+ if (!artifactTypes.contains(resource.getResourceType().name())) {
+ shouldCreateArtifact = false;
+ continue;
+ }
+ } else {
+ log.info("resource types for artifact placeholder {} were not defined. default is all resources", currEntry.getKey());
+ }
+ if (shouldCreateArtifact) {
+ if (artifactsBusinessLogic != null) {
+ ArtifactDefinition artifactDefinition = artifactsBusinessLogic.createArtifactPlaceHolderInfo(resource.getUniqueId(), currEntry.getKey(), (Map<String, Object>) currEntry.getValue(), user, ArtifactGroupTypeEnum.DEPLOYMENT);
+ if (artifactDefinition != null && !artifactMap.containsKey(artifactDefinition.getArtifactLabel()))
+ artifactMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+ }
+ }
+ }
+ }
+ resource.setDeploymentArtifacts(artifactMap);
+ }
+
+ private void setInformationalArtifactsPlaceHolder(Resource resource, User user) {
+ Map<String, ArtifactDefinition> artifactMap = resource.getArtifacts();
+ if (artifactMap == null) {
+ artifactMap = new HashMap<String, ArtifactDefinition>();
+ }
+ String resourceUniqueId = resource.getUniqueId();
+ List<String> exludeResourceCategory = ConfigurationManager.getConfigurationManager().getConfiguration().getExcludeResourceCategory();
+ Map<String, Object> informationalResourceArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getInformationalResourceArtifacts();
+ List<CategoryDefinition> categories = resource.getCategories();
+ boolean isCreateArtifact = true;
+ if (exludeResourceCategory != null) {
+ String category = categories.get(0).getName();
+ for (String exlude : exludeResourceCategory) {
+ if (exlude.equalsIgnoreCase(category)) {
+ isCreateArtifact = false;
+ break;
+ }
+ }
+
+ }
+
+ if (informationalResourceArtifacts != null && isCreateArtifact) {
+ Set<String> keys = informationalResourceArtifacts.keySet();
+ for (String informationalResourceArtifactName : keys) {
+ Map<String, Object> artifactInfoMap = (Map<String, Object>) informationalResourceArtifacts.get(informationalResourceArtifactName);
+ ArtifactDefinition artifactDefinition = artifactsBusinessLogic.createArtifactPlaceHolderInfo(resourceUniqueId, informationalResourceArtifactName, artifactInfoMap, user, ArtifactGroupTypeEnum.INFORMATIONAL);
+ artifactMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+
+ }
+ }
+ resource.setArtifacts(artifactMap);
+ }
+
+ /**
+ * deleteResource
+ *
+ * @param resourceId
+ * @param user
+ * @return
+ */
+ public ResponseFormat deleteResource(String resourceId, User user) {
+ ResponseFormat responseFormat;
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, "Delete Resource", false);
+ if (eitherCreator.isRight()) {
+ return eitherCreator.right().value();
+ }
+
+ Either<Resource, StorageOperationStatus> resourceStatus = resourceOperation.getResource(resourceId);
+ if (resourceStatus.isRight()) {
+ log.debug("failed to get resource {}", resourceId);
+ return componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resourceStatus.right().value()), "");
+ }
+
+ Resource resource = resourceStatus.left().value();
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(resourceId, resource, "Mark resource to delete");
+ if (lockResult.isRight()) {
+ result = StorageOperationStatus.GENERAL_ERROR;
+ return componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ }
+
+ try {
+
+ result = markComponentToDelete(resource);
+ if (result.equals(StorageOperationStatus.OK)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.NO_CONTENT);
+ } else {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(result);
+ responseFormat = componentsUtils.getResponseFormatByResource(actionStatus, resource.getName());
+ }
+ return responseFormat;
+
+ } finally {
+ if (result == null || !result.equals(StorageOperationStatus.OK)) {
+ log.warn("operation failed. do rollback");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
+ }
+
+ }
+
+ public ResponseFormat deleteResourceByNameAndVersion(String resourceName, String version, User user) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.NO_CONTENT);
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, "Delete Resource", false);
+ if (eitherCreator.isRight()) {
+ return eitherCreator.right().value();
+ }
+
+ // Resource resource = null;
+ List<Resource> resourcesList = null;
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ try {
+
+ Either<List<Resource>, StorageOperationStatus> resourceStatus = resourceOperation.getResourceByNameAndVersion(resourceName, version, true);
+ if (resourceStatus.isRight()) {
+ log.debug("failed to get resource {} version {}", resourceName, version);
+ return componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(resourceStatus.right().value()), resourceName);
+ }
+
+ resourcesList = resourceStatus.left().value();
+
+ } finally {
+ if (result == null || !result.equals(StorageOperationStatus.OK)) {
+ log.warn("operation failed. do rollback");
+ titanGenericDao.rollback();
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(result);
+ responseFormat = componentsUtils.getResponseFormatByResource(actionStatus, resourceName);
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ }
+ for (Resource resource : resourcesList) {
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(resource.getUniqueId(), resource, "Delete Resource");
+ if (lockResult.isRight()) {
+ result = StorageOperationStatus.GENERAL_ERROR;
+ return componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ }
+ try {
+ result = markComponentToDelete(resource);
+ if (!result.equals(StorageOperationStatus.OK)) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(result);
+ responseFormat = componentsUtils.getResponseFormatByResource(actionStatus, resource.getName());
+ return responseFormat;
+ }
+
+ } finally {
+ if (result == null || !result.equals(StorageOperationStatus.OK)) {
+ log.warn("operation failed. do rollback");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ graphLockOperation.unlockComponent(resource.getUniqueId(), NodeTypeEnum.Resource);
+ }
+ }
+ return responseFormat;
+ }
+
+ public Either<Resource, ResponseFormat> getResource(String resourceId, User user) {
+
+ if (user != null) {
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, "Create Resource", false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ }
+
+ IResourceOperation dataModel = getResourceOperation();
+ Either<Resource, StorageOperationStatus> storageStatus = dataModel.getResource(resourceId);
+
+ if (storageStatus.isRight()) {
+ log.debug("failed to get resource by id {}", resourceId);
+ return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus.right().value()), ""));
+ }
+ return Either.left(storageStatus.left().value());
+
+ }
+
+ public Either<List<Resource>, ResponseFormat> getResourceByNameAndVersion(String resourceName, String resourceVersion, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Resource By Name And Version", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<List<Resource>, StorageOperationStatus> getResource = resourceOperation.getResourceByNameAndVersion(resourceName, resourceVersion, false);
+ if (getResource.isRight()) {
+ log.debug("failed to get resource by name {} and version {}", resourceName, resourceVersion);
+ return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(getResource.right().value()), resourceName));
+ }
+ return Either.left(getResource.left().value());
+ }
+
+ /**
+ * updateResourceMetadata
+ *
+ * @param user
+ * - modifier data (userId)
+ * @param inTransaction
+ * TODO
+ * @param resourceIdToUpdate
+ * - the resource identifier
+ * @param newResource
+ *
+ * @return Either<Resource, responseFormat>
+ */
+ public Either<Resource, ResponseFormat> updateResourceMetadata(String resourceIdToUpdate, Resource newResource, Resource currentResource, User user, boolean inTransaction) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(user.getUserId(), "update Resource Metadata", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ IResourceOperation dataModel = getResourceOperation();
+ log.debug("Get resource with id {}", resourceIdToUpdate);
+ boolean needToUnlock = false;
+ boolean rollbackNeeded = true;
+
+ try {
+ // Either<Resource, StorageOperationStatus> storageStatus =
+ // dataModel.getResource_tx(resourceIdToUpdate, false);
+ if (currentResource == null) {
+ Either<Resource, StorageOperationStatus> storageStatus = dataModel.getResource(resourceIdToUpdate, false);
+ if (storageStatus.isRight()) {
+ return Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus.right().value()), ""));
+ }
+
+ currentResource = storageStatus.left().value();
+ }
+ // verify that resource is checked-out and the user is the last
+ // updater
+ if (!ComponentValidationUtils.canWorkOnResource(currentResource, user.getUserId())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ // lock resource
+ StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceIdToUpdate, NodeTypeEnum.Resource);
+ if (!lockResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedLockObjectError, "Upload Artifact - lock " + resourceIdToUpdate + ": " + NodeTypeEnum.Resource);
+ BeEcompErrorManager.getInstance().logBeFailedLockObjectError("Upload Artifact - lock ", NodeTypeEnum.Resource.getName(), resourceIdToUpdate);
+ log.debug("Failed to lock resource {} error - {}", resourceIdToUpdate, lockResult);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockResult));
+ return Either.right(responseFormat);
+ }
+
+ needToUnlock = true;
+
+ // critical section starts here
+ // convert json to object
+
+ // Update and updated resource must have a non-empty "derivedFrom"
+ // list
+ // This code is not called from import resources, because of root
+ // VF "derivedFrom" should be null (or ignored)
+ if (!currentResource.getResourceType().equals(ResourceTypeEnum.VF)) {
+ Either<Boolean, ResponseFormat> derivedFromNotEmptyEither = validateDerivedFromNotEmpty(null, newResource, null);
+ if (derivedFromNotEmptyEither.isRight()) {
+ log.debug("for updated resource {}, derived from field is empty", newResource.getName());
+ return Either.right(derivedFromNotEmptyEither.right().value());
+ }
+
+ derivedFromNotEmptyEither = validateDerivedFromNotEmpty(null, currentResource, null);
+ if (derivedFromNotEmptyEither.isRight()) {
+ log.debug("for current resource {}, derived from field is empty", currentResource.getName());
+ return Either.right(derivedFromNotEmptyEither.right().value());
+ }
+ } else {
+ newResource.setDerivedFrom(null);
+ }
+
+ Either<Resource, ResponseFormat> dataModelResponse = updateResourceMetadata(resourceIdToUpdate, newResource, user, currentResource, false, true);
+ if (dataModelResponse.isRight()) {
+ log.debug("failed to update resource metadata!!!");
+ rollbackNeeded = true;
+ return Either.right(dataModelResponse.right().value());
+ }
+
+ log.debug("Resource metadata updated successfully!!!");
+ rollbackNeeded = false;
+ return Either.left(dataModelResponse.left().value());
+
+ } finally {
+ if (!inTransaction) {
+ if (rollbackNeeded) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+
+ if (needToUnlock) {
+ graphLockOperation.unlockComponent(resourceIdToUpdate, NodeTypeEnum.Resource);
+ }
+ }
+ }
+
+ private Either<Resource, ResponseFormat> updateResourceMetadata(String resourceIdToUpdate, Resource newResource, User user, Resource currentResource, boolean shouldLock, boolean inTransaction) {
+
+ IResourceOperation dataModel = getResourceOperation();
+ Either<Boolean, ResponseFormat> validateResourceFields = validateResourceFieldsBeforeUpdate(currentResource, newResource, inTransaction);
+ if (validateResourceFields.isRight()) {
+ return Either.right(validateResourceFields.right().value());
+ }
+ // Setting last updater and uniqueId
+ newResource.setContactId(newResource.getContactId().toLowerCase());
+ newResource.setLastUpdaterUserId(user.getUserId());
+ newResource.setUniqueId(resourceIdToUpdate);
+ // Cannot set highest version through UI
+ newResource.setHighestVersion(null);
+ newResource.setCreationDate(currentResource.getCreationDate());
+
+ Either<Boolean, ResponseFormat> processUpdateOfDerivedFrom = processUpdateOfDerivedFrom(currentResource, newResource, user.getUserId(), shouldLock, inTransaction);
+
+ if (processUpdateOfDerivedFrom.isRight()) {
+ log.debug("Couldn't update derived from for resource {}", resourceIdToUpdate);
+ return Either.right(processUpdateOfDerivedFrom.right().value());
+ }
+
+ log.debug("send resource {} to dao for update", newResource.getUniqueId());
+ Either<Resource, StorageOperationStatus> dataModelResponse = dataModel.updateResource(newResource, inTransaction);
+
+ if (dataModelResponse.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()), newResource);
+ return Either.right(responseFormat);
+ } else if (dataModelResponse.left().value() == null) {
+ log.debug("No response from updateResource");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ return Either.left(dataModelResponse.left().value());
+ }
+
+ /**
+ * validateResourceFieldsBeforeCreate
+ *
+ * @param user
+ * - modifier data (userId)
+ * @param dataModel
+ * - IResourceOperation for resource crud
+ * @param resource
+ * - Resource object to validate
+ * @return Either<Boolean, ErrorResponse>
+ */
+ private Either<Boolean, ResponseFormat> validateResourceFieldsBeforeCreate(User user, IResourceOperation dataModel, Resource resource, AuditingActionEnum actionEnum, boolean inTransaction) {
+ Either<Boolean, ResponseFormat> componentsFieldsValidation = validateComponentFieldsBeforeCreate(user, resource, actionEnum);
+ if (componentsFieldsValidation.isRight()) {
+ return componentsFieldsValidation;
+ }
+
+ // validate name
+
+ /*
+ * log.debug("validate resource name"); Either<Boolean, ResponseFormat> eitherValidation = validateComponentName(user, resource, actionEnum); if (eitherValidation.isRight()) { return eitherValidation; }
+ *
+ * // validate description log.debug("validate description"); eitherValidation = validateDescriptionAndCleanup(user, resource, actionEnum); if (eitherValidation.isRight()) { return eitherValidation; }
+ */
+
+ // validate icon
+ /*
+ * log.debug("validate icon"); eitherValidation = validateIcon(user, resource, actionEnum); if (eitherValidation.isRight()) { return eitherValidation; }
+ */
+
+ // validate tags
+ /*
+ * log.debug("validate tags"); eitherValidation = validateTagsListAndRemoveDuplicates(user, resource, actionEnum); if (eitherValidation.isRight()) { return eitherValidation; }
+ */
+
+ // validate category
+ log.debug("validate category");
+ Either<Boolean, ResponseFormat> eitherValidation = validateCategory(user, resource, actionEnum, inTransaction);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ // validate vendor name & release
+ log.debug("validate vendor name");
+ eitherValidation = validateVendorName(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate vendor release");
+ eitherValidation = validateVendorReleaseName(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ // validate cost
+ log.debug("validate cost");
+ eitherValidation = validateCost(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ // validate licenseType
+ log.debug("validate licenseType");
+ eitherValidation = validateLicenseType(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ // validate template (derived from)
+ log.debug("validate derived from");
+ if (resource.getResourceType().equals(ResourceTypeEnum.VF)) {
+ resource.setDerivedFrom(null);
+ }
+ eitherValidation = validateDerivedFromExist(user, resource, actionEnum);
+ if (eitherValidation.isRight()) {
+ return Either.right(eitherValidation.right().value());
+ }
+
+ // warn about non-updatable fields
+ checkComponentFieldsForOverrideAttempt(resource);
+ String currentCreatorFullName = resource.getCreatorFullName();
+ if (currentCreatorFullName != null) {
+ log.warn("Resource Creator fullname is automatically set and cannot be updated");
+ }
+
+ String currentLastUpdaterFullName = resource.getLastUpdaterFullName();
+ if (currentLastUpdaterFullName != null) {
+ log.warn("Resource LastUpdater fullname is automatically set and cannot be updated");
+ }
+
+ Long currentLastUpdateDate = resource.getLastUpdateDate();
+ if (currentLastUpdateDate != null) {
+ log.warn("Resource last update date is automatically set and cannot be updated");
+ }
+
+ Boolean currentAbstract = resource.isAbstract();
+ if (currentAbstract != null) {
+ log.warn("Resource abstract is automatically set and cannot be updated");
+ }
+
+ return Either.left(true);
+ }
+
+ /**
+ * validateResourceFieldsBeforeUpdate
+ *
+ * @param currentResource
+ * - Resource object to validate
+ * @return Either<Boolean, ErrorResponse>
+ */
+ private Either<Boolean, ResponseFormat> validateResourceFieldsBeforeUpdate(Resource currentResource, Resource updateInfoResource, boolean inTransaction) {
+
+ boolean hasBeenCertified = ValidationUtils.hasBeenCertified(currentResource.getVersion());
+
+ // validate resource name
+ log.debug("validate resource name before update");
+ Either<Boolean, ResponseFormat> eitherValidation = validateResourceName(currentResource, updateInfoResource, hasBeenCertified);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ // validate description
+ log.debug("validate description before update");
+ eitherValidation = validateDescriptionAndCleanup(null, updateInfoResource, null);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate icon before update");
+ eitherValidation = validateIcon(currentResource, updateInfoResource, hasBeenCertified);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate tags before update");
+ eitherValidation = validateTagsListAndRemoveDuplicates(null, updateInfoResource, null);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate vendor name before update");
+ eitherValidation = validateVendorName(currentResource, updateInfoResource, hasBeenCertified);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate vendor release before update");
+ eitherValidation = validateVendorReleaseName(null, updateInfoResource, null);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate contact info before update");
+ eitherValidation = validateContactId(null, updateInfoResource, null);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate derived before update");
+ eitherValidation = validateDerivedFromDuringUpdate(currentResource, updateInfoResource, hasBeenCertified);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ log.debug("validate category before update");
+ eitherValidation = validateCategory(currentResource, updateInfoResource, hasBeenCertified, inTransaction);
+ if (eitherValidation.isRight()) {
+ return eitherValidation;
+ }
+
+ // warn about non-updatable fields
+ String currentResourceVersion = currentResource.getVersion();
+ String updatedResourceVersion = updateInfoResource.getVersion();
+
+ if ((updatedResourceVersion != null) && (!updatedResourceVersion.equals(currentResourceVersion))) {
+ log.warn("Resource version is automatically set and cannot be updated");
+ }
+
+ String currentCreatorUserId = currentResource.getCreatorUserId();
+ String updatedCreatorUserId = updateInfoResource.getCreatorUserId();
+
+ if ((updatedCreatorUserId != null) && (!updatedCreatorUserId.equals(currentCreatorUserId))) {
+ log.warn("Resource Creator User id is automatically set and cannot be updated");
+ }
+
+ String currentCreatorFullName = currentResource.getCreatorFullName();
+ String updatedCreatorFullName = updateInfoResource.getCreatorFullName();
+
+ if ((updatedCreatorFullName != null) && (!updatedCreatorFullName.equals(currentCreatorFullName))) {
+ log.warn("Resource Creator fullname is automatically set and cannot be updated");
+ }
+
+ String currentLastUpdaterUserId = currentResource.getLastUpdaterUserId();
+ String updatedLastUpdaterUserId = updateInfoResource.getLastUpdaterUserId();
+
+ if ((updatedLastUpdaterUserId != null) && (!updatedLastUpdaterUserId.equals(currentLastUpdaterUserId))) {
+ log.warn("Resource LastUpdater userId is automatically set and cannot be updated");
+ }
+
+ String currentLastUpdaterFullName = currentResource.getLastUpdaterFullName();
+ String updatedLastUpdaterFullName = updateInfoResource.getLastUpdaterFullName();
+
+ if ((updatedLastUpdaterFullName != null) && (!updatedLastUpdaterFullName.equals(currentLastUpdaterFullName))) {
+ log.warn("Resource LastUpdater fullname is automatically set and cannot be updated");
+ }
+
+ Long currentCreationDate = currentResource.getCreationDate();
+ Long updatedCreationDate = updateInfoResource.getCreationDate();
+
+ if ((updatedCreationDate != null) && (!updatedCreationDate.equals(currentCreationDate))) {
+ log.warn("Resource Creation date is automatically set and cannot be updated");
+ }
+
+ Long currentLastUpdateDate = currentResource.getLastUpdateDate();
+ Long updatedLastUpdateDate = updateInfoResource.getLastUpdateDate();
+
+ if ((updatedLastUpdateDate != null) && (!updatedLastUpdateDate.equals(currentLastUpdateDate))) {
+ log.warn("Resource last update date is automatically set and cannot be updated");
+ }
+
+ LifecycleStateEnum currentLifecycleState = currentResource.getLifecycleState();
+ LifecycleStateEnum updatedLifecycleState = updateInfoResource.getLifecycleState();
+
+ if ((updatedLifecycleState != null) && (!updatedLifecycleState.equals(currentLifecycleState))) {
+ log.warn("Resource lifecycle state date is automatically set and cannot be updated");
+ }
+
+ Boolean currentAbstract = currentResource.isAbstract();
+ Boolean updatedAbstract = updateInfoResource.isAbstract();
+
+ if ((updatedAbstract != null) && (!updatedAbstract.equals(currentAbstract))) {
+ log.warn("Resource abstract is automatically set and cannot be updated");
+ }
+
+ Boolean currentHighestVersion = currentResource.isHighestVersion();
+ Boolean updatedHighestVersion = updateInfoResource.isHighestVersion();
+
+ if ((updatedHighestVersion != null) && (!updatedHighestVersion.equals(currentHighestVersion))) {
+ log.warn("Resource highest version is automatically set and cannot be updated");
+ }
+
+ String currentUuid = currentResource.getUUID();
+ String updatedUuid = updateInfoResource.getUUID();
+
+ if ((updatedUuid != null) && (!updatedUuid.equals(currentUuid))) {
+ log.warn("Resource UUID is automatically set and cannot be updated");
+ }
+
+ ResourceTypeEnum currentResourceType = currentResource.getResourceType();
+ ResourceTypeEnum updatedResourceType = updateInfoResource.getResourceType();
+
+ if ((updatedResourceType != null) && (!updatedResourceType.equals(currentResourceType))) {
+ log.warn("Resource Type cannot be updated");
+
+ }
+ updateInfoResource.setResourceType(currentResource.getResourceType());
+
+ String currentInvariantUuid = currentResource.getInvariantUUID();
+ String updatedInvariantUuid = updateInfoResource.getInvariantUUID();
+
+ if ((updatedInvariantUuid != null) && (!updatedInvariantUuid.equals(currentInvariantUuid))) {
+ log.warn("Resource invariant UUID is automatically set and cannot be updated");
+ updateInfoResource.setInvariantUUID(currentInvariantUuid);
+ }
+ return Either.left(true);
+ }
+
+ /*
+ * private Either<Boolean, ResponseFormat> validateResourceName(User user, Resource resource, AuditingActionEnum actionEnum) { log.debug("validate resource name is not empty"); String resourceName = resource.getResourceName();
+ *
+ * if (!ValidationUtils.validateStringNotEmpty(resourceName)) { log.debug("Resource name is empty"); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue());
+ * componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null); return Either.right(responseFormat); }
+ *
+ * if (!ValidationUtils.validateResourceNameLength(resourceName)) { log.debug("Resource name is exceeds max length {} ", ValidationUtils.RESOURCE_NAME_MAX_LENGTH); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.
+ * COMPONENT_NAME_EXCEEDS_LIMIT, ComponentTypeEnum.RESOURCE.getValue(), "" + ValidationUtils.RESOURCE_NAME_MAX_LENGTH); componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null); return Either.right(responseFormat);
+ * }
+ *
+ * if (!ValidationUtils.validateResourceName(resourceName)) { log.debug("Resource name {} has invalid format", resourceName); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_COMPONENT_NAME,
+ * ComponentTypeEnum.RESOURCE.getValue()); componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null); return Either.right(responseFormat); } resource.setNormalizedName(ValidationUtils.normaliseComponentName(
+ * resourceName)); resource.setSystemName(ValidationUtils.convertToSystemName(resourceName)) ;
+ *
+ * return Either.left(true); }
+ */
+
+ private Either<Boolean, ResponseFormat> validateResourceName(Resource currentResource, Resource updateInfoResource, boolean hasBeenCertified) {
+ String resourceNameUpdated = updateInfoResource.getName();
+ String resourceNameCurrent = currentResource.getName();
+ if (!resourceNameCurrent.equals(resourceNameUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validateResourceNameResponse = validateComponentName(null, updateInfoResource, null);
+ if (validateResourceNameResponse.isRight()) {
+ ResponseFormat errorResponse = validateResourceNameResponse.right().value();
+ return Either.right(errorResponse);
+ }
+ validateResourceNameResponse = validateResourceNameExists(null, updateInfoResource, null);
+ if (validateResourceNameResponse.isRight()) {
+ ResponseFormat errorResponse = validateResourceNameResponse.right().value();
+ return Either.right(errorResponse);
+ }
+ currentResource.setName(resourceNameUpdated);
+ currentResource.setNormalizedName(ValidationUtils.normaliseComponentName(resourceNameUpdated));
+ currentResource.setSystemName(ValidationUtils.convertToSystemName(resourceNameUpdated));
+
+ } else {
+ log.info("Resource name {} cannot be updated once the resource has been certified once.", resourceNameUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NAME_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateIcon(Resource currentResource, Resource updateInfoResource, boolean hasBeenCertified) {
+ String iconUpdated = updateInfoResource.getIcon();
+ String iconCurrent = currentResource.getIcon();
+ if (!iconCurrent.equals(iconUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validateIcon = validateIcon(null, updateInfoResource, null);
+ if (validateIcon.isRight()) {
+ ResponseFormat errorResponse = validateIcon.right().value();
+ return Either.right(errorResponse);
+ }
+ } else {
+ log.info("Icon {} cannot be updated once the resource has been certified once.", iconUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_ICON_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateVendorName(Resource currentResource, Resource updateInfoResource, boolean hasBeenCertified) {
+ String vendorNameUpdated = updateInfoResource.getVendorName();
+ String vendorNameCurrent = currentResource.getVendorName();
+ if (!vendorNameCurrent.equals(vendorNameUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validateVendorName = validateVendorName(null, updateInfoResource, null);
+ if (validateVendorName.isRight()) {
+ ResponseFormat errorResponse = validateVendorName.right().value();
+ return Either.right(errorResponse);
+ }
+ } else {
+ log.info("Vendor name {} cannot be updated once the resource has been certified once.", vendorNameUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateCategory(Resource currentResource, Resource updateInfoResource, boolean hasBeenCertified, boolean inTransaction) {
+ Either<Boolean, ResponseFormat> validateCategoryName = validateCategory(null, updateInfoResource, null, inTransaction);
+ if (validateCategoryName.isRight()) {
+ ResponseFormat errorResponse = validateCategoryName.right().value();
+ return Either.right(errorResponse);
+ }
+ if (hasBeenCertified) {
+ CategoryDefinition currentCategory = currentResource.getCategories().get(0);
+ SubCategoryDefinition currentSubCategory = currentCategory.getSubcategories().get(0);
+ CategoryDefinition updateCategory = updateInfoResource.getCategories().get(0);
+ SubCategoryDefinition updtaeSubCategory = updateCategory.getSubcategories().get(0);
+ if (!currentCategory.getName().equals(updateCategory.getName()) || !currentSubCategory.getName().equals(updtaeSubCategory.getName())) {
+ log.info("Category {} cannot be updated once the resource has been certified once.", currentResource.getCategories());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_CATEGORY_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateDerivedFromDuringUpdate(Resource currentResource, Resource updateInfoResource, boolean hasBeenCertified) {
+
+ List<String> currentDerivedFrom = currentResource.getDerivedFrom();
+ List<String> updatedDerivedFrom = updateInfoResource.getDerivedFrom();
+ if (currentDerivedFrom == null || currentDerivedFrom.isEmpty() || updatedDerivedFrom == null || updatedDerivedFrom.isEmpty()) {
+ log.trace("Update normative types");
+ return Either.left(true);
+ }
+
+ String derivedFromCurrent = currentDerivedFrom.get(0);
+ String derivedFromUpdated = updatedDerivedFrom.get(0);
+
+ if (!derivedFromCurrent.equals(derivedFromUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validateDerivedFromExistsEither = validateDerivedFromExist(null, updateInfoResource, null);
+ if (validateDerivedFromExistsEither.isRight()) {
+ return validateDerivedFromExistsEither;
+ }
+ } else {
+ log.debug("Derived from cannot be updated once the resource has been certified once.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ } else {
+ // For derived from, we must know whether it was actually changed,
+ // otherwise we must do no action.
+ // Due to changes it inflicts on data model (remove artifacts,
+ // properties...), it's not like a flat field which can be
+ // overwritten if not changed.
+ // So we must indicate that derived from is not changed
+ updateInfoResource.setDerivedFrom(null);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateDerivedFromExist(User user, Resource resource, AuditingActionEnum actionEnum) {
+
+ if (resource.getDerivedFrom() == null || resource.getDerivedFrom().isEmpty()) {
+ return Either.left(true);
+ }
+
+ IResourceOperation resourceOperation = getResourceOperation();
+
+ String templateName = resource.getDerivedFrom().get(0);
+
+ Either<Boolean, StorageOperationStatus> dataModelResponse = resourceOperation.validateToscaResourceNameExists(templateName);
+ if (dataModelResponse.isRight()) {
+ StorageOperationStatus storageStatus = dataModelResponse.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Create Resource - validateDerivedFromExist");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Create Resource - validateDerivedFromExist");
+ log.debug("request to data model failed with error: {}", storageStatus.name());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(storageStatus), resource);
+ log.trace("audit before sending response");
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ return Either.right(responseFormat);
+ }
+
+ if (dataModelResponse.left().value()) {
+ log.info("resource template with name {} does not exists", templateName);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.PARENT_RESOURCE_NOT_FOUND);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+
+ return Either.right(responseFormat);
+
+ }
+ return Either.left(true);
+ }
+
+ public Either<Boolean, ResponseFormat> validateDerivedFromNotEmpty(User user, Resource resource, AuditingActionEnum actionEnum) {
+ log.debug("validate resource derivedFrom field");
+ if ((resource.getDerivedFrom() == null) || (resource.getDerivedFrom().isEmpty()) || (resource.getDerivedFrom().get(0)) == null || (resource.getDerivedFrom().get(0).trim().isEmpty())) {
+ log.info("derived from (template) field is missing for the resource");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_DERIVED_FROM_TEMPLATE);
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+
+ return Either.right(responseFormat);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateResourceNameExists(User user, Resource resource, AuditingActionEnum actionEnum) {
+
+ IResourceOperation resourceOperation = getResourceOperation();
+ Either<Boolean, StorageOperationStatus> resourceOperationResponse = resourceOperation.validateResourceNameExists(resource.getName(), resource.getResourceType());
+ if (resourceOperationResponse.isLeft()) {
+ if (resourceOperationResponse.left().value()) {
+ return Either.left(true);
+ } else {
+ log.debug("resource with name {} already exists", resource.getName());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, ComponentTypeEnum.RESOURCE.getValue(), resource.getName());
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ return Either.right(errorResponse);
+ }
+ }
+ log.debug("error while validateResourceNameExists for resource: {}", resource.getName());
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(resourceOperationResponse.right().value()));
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ return Either.right(errorResponse);
+ }
+
+ /*
+ * private Either<Boolean, ResponseFormat> validateTagsListAndRemoveDuplicates(User user, Resource resource, AuditingActionEnum actionEnum) { List<String> tagsList = resource.getTags();
+ *
+ * Either<Boolean, ResponseFormat> validateTags = validateResourceTags(tagsList, resource.getResourceName()); if (validateTags.isRight()) { ResponseFormat responseFormat = validateTags.right().value();
+ * componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null); return Either.right(responseFormat); } ValidationUtils.removeDuplicateFromList(tagsList); return Either.left(true);
+ *
+ * }
+ *
+ * private Either<Boolean, ResponseFormat> validateResourceTags(List<String> tags, String resourceName) { log.debug("validate resource tags"); boolean includesResourceName = false; int tagListSize = 0; if (tags != null && !tags.isEmpty()) { for
+ * (String tag : tags) { if (!ValidationUtils.validateTagLength(tag)) { log.debug("tag length exceeds limit {}", ValidationUtils.TAG_MAX_LENGTH); return Either.right(componentsUtils.getResponseFormat(ActionStatus.
+ * COMPONENT_SINGLE_TAG_EXCEED_LIMIT, "" + ValidationUtils.TAG_MAX_LENGTH)); } if (ValidationUtils.validateComponentNamePattern(tag)) { if (!includesResourceName) { includesResourceName = resourceName.equals(tag); } } else {
+ * log.debug("invalid tag {}", tag); return Either.right(componentsUtils.getResponseFormat(ActionStatus. COMPONENT_INVALID_TAG)); } tagListSize += tag.length() + 1; } if (!includesResourceName) { log.debug( "tags must include resource name");
+ * return Either.right(componentsUtils.getResponseFormat(ActionStatus. COMPONENT_INVALID_TAGS_NO_COMP_NAME)); } if (!ValidationUtils.validateTagListLength(tagListSize)) { log.debug( "overall tags length {}, exceeds limit {}", tagListSize,
+ * ValidationUtils.TAG_LIST_MAX_LENGTH); return Either.right(componentsUtils.getResponseFormat(ActionStatus. COMPONENT_TAGS_EXCEED_LIMIT, "" + ValidationUtils.TAG_LIST_MAX_LENGTH)); } return Either.left(true); }
+ *
+ * return Either.right(componentsUtils.getResponseFormat(ActionStatus. COMPONENT_MISSING_TAGS)); }
+ */
+
+ private Either<Boolean, ResponseFormat> validateCategory(User user, Resource resource, AuditingActionEnum actionEnum, boolean inTransaction) {
+
+ List<CategoryDefinition> categories = resource.getCategories();
+ if (categories == null || categories.size() == 0) {
+ log.debug("Resource category is empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.RESOURCE.getValue());
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ return Either.right(responseFormat);
+ }
+ if (categories.size() > 1) {
+ log.debug("Must be only one category for resource");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_TOO_MUCH_CATEGORIES, ComponentTypeEnum.RESOURCE.getValue());
+ return Either.right(responseFormat);
+ }
+ CategoryDefinition category = categories.get(0);
+ List<SubCategoryDefinition> subcategories = category.getSubcategories();
+ if (subcategories == null || subcategories.size() == 0) {
+ log.debug("Missinig subcategory for resource");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY);
+ return Either.right(responseFormat);
+ }
+ if (subcategories.size() > 1) {
+ log.debug("Must be only one sub ategory for resource");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_TOO_MUCH_SUBCATEGORIES);
+ return Either.right(responseFormat);
+ }
+
+ SubCategoryDefinition subcategory = subcategories.get(0);
+
+ if (!ValidationUtils.validateStringNotEmpty(category.getName())) {
+ log.debug("Resource category is empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.RESOURCE.getValue());
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ return Either.right(responseFormat);
+ }
+ if (!ValidationUtils.validateStringNotEmpty(subcategory.getName())) {
+ log.debug("Resource category is empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY, ComponentTypeEnum.RESOURCE.getValue());
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ return Either.right(responseFormat);
+ }
+
+ Either<Boolean, ResponseFormat> validateCategory = validateCategoryListed(category, subcategory, inTransaction);
+ if (validateCategory.isRight()) {
+ ResponseFormat responseFormat = validateCategory.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateCategoryListed(CategoryDefinition category, SubCategoryDefinition subcategory, boolean inTransaction) {
+ if (category != null && subcategory != null) {
+ log.debug("validating resource category {} against valid categories list", category);
+ Either<List<CategoryDefinition>, ActionStatus> categories = elementDao.getAllCategories(NodeTypeEnum.ResourceNewCategory, inTransaction);
+ if (categories.isRight()) {
+ log.debug("failed to retrive resource categories from Titan");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(categories.right().value());
+ return Either.right(responseFormat);
+ }
+ List<CategoryDefinition> categoryList = categories.left().value();
+ for (CategoryDefinition cat : categoryList) {
+ if (cat.getName().equals(category.getName())) {
+ for (SubCategoryDefinition subcat : cat.getSubcategories()) {
+ if (subcat.getName().equals(subcategory.getName())) {
+ return Either.left(true);
+ }
+ }
+ log.debug("SubCategory {} is not part of resource category group. Resource subcategory valid values are {}", subcategory, cat.getSubcategories());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
+ }
+ }
+ log.debug("Category {} is not part of resource category group. Resource category valid values are {}", category, categoryList);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
+ }
+ return Either.left(false);
+ }
+
+ public Either<Boolean, ResponseFormat> validateVendorReleaseName(User user, Resource resource, AuditingActionEnum actionEnum) {
+ String vendorRelease = resource.getVendorRelease();
+
+ log.debug("validate vendor relese name");
+ if (!ValidationUtils.validateStringNotEmpty(vendorRelease)) {
+ log.info("vendor relese name is missing.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_VENDOR_RELEASE);
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ return Either.right(errorResponse);
+ }
+
+ Either<Boolean, ResponseFormat> validateVendorReleaseResponse = validateVendorReleaseName(vendorRelease);
+ if (validateVendorReleaseResponse.isRight()) {
+ ResponseFormat responseFormat = validateVendorReleaseResponse.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ }
+ return validateVendorReleaseResponse;
+ }
+
+ public Either<Boolean, ResponseFormat> validateVendorReleaseName(String vendorRelease) {
+ if (vendorRelease != null) {
+ if (!ValidationUtils.validateVendorReleaseLength(vendorRelease)) {
+ log.info("vendor release exceds limit.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.VENDOR_RELEASE_EXCEEDS_LIMIT, "" + ValidationUtils.VENDOR_RELEASE_MAX_LENGTH);
+ return Either.right(errorResponse);
+ }
+
+ if (!ValidationUtils.validateVendorRelease(vendorRelease)) {
+ log.info("vendor release is not valid.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_VENDOR_RELEASE);
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+ return Either.left(false);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateVendorName(User user, Resource resource, AuditingActionEnum actionEnum) {
+ String vendorName = resource.getVendorName();
+ if (!ValidationUtils.validateStringNotEmpty(vendorName)) {
+ log.info("vendor name is missing.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.MISSING_VENDOR_NAME);
+ componentsUtils.auditResource(errorResponse, user, resource, "", "", actionEnum, null);
+ return Either.right(errorResponse);
+ }
+
+ Either<Boolean, ResponseFormat> validateVendorNameResponse = validateVendorName(vendorName);
+ if (validateVendorNameResponse.isRight()) {
+ ResponseFormat responseFormat = validateVendorNameResponse.right().value();
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ }
+ return validateVendorNameResponse;
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateVendorName(String vendorName) {
+ if (vendorName != null) {
+ if (!ValidationUtils.validateVendorNameLength(vendorName)) {
+ log.info("vendor name exceds limit.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.VENDOR_NAME_EXCEEDS_LIMIT, "" + ValidationUtils.VENDOR_NAME_MAX_LENGTH);
+ return Either.right(errorResponse);
+ }
+
+ if (!ValidationUtils.validateVendorName(vendorName)) {
+ log.info("vendor name is not valid.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_VENDOR_NAME);
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+
+ }
+ return Either.left(false);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateCost(User user, Resource resource, AuditingActionEnum actionEnum) {
+ String cost = resource.getCost();
+ if (cost != null) {
+
+ if (!ValidationUtils.validateCost(cost)) {
+ log.debug("resource cost is invalid.");
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateLicenseType(User user, Resource resource, AuditingActionEnum actionEnum) {
+ log.debug("validate licenseType");
+ String licenseType = resource.getLicenseType();
+ if (licenseType != null) {
+ List<String> licenseTypes = ConfigurationManager.getConfigurationManager().getConfiguration().getLicenseTypes();
+ if (!licenseTypes.contains(licenseType)) {
+ log.debug("License type {} isn't configured");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ if (actionEnum != null) {
+ // In update case, no audit is required
+ componentsUtils.auditResource(responseFormat, user, resource, "", "", actionEnum, null);
+ }
+ return Either.right(responseFormat);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> processUpdateOfDerivedFrom(Resource currentResource, Resource updatedResource, String userId, boolean shouldLock, boolean inTransaction) {
+ Either<Operation, ResponseFormat> deleteArtifactByInterface = null;
+ if (updatedResource.getDerivedFrom() != null) {
+ log.debug("Starting derived from update for resource {}", updatedResource.getUniqueId());
+ log.debug("1. Removing interface artifacts from graph");
+ // Remove all interface artifacts of resource
+ String resourceId = updatedResource.getUniqueId();
+ Map<String, InterfaceDefinition> interfaces = currentResource.getInterfaces();
+
+ if (interfaces != null) {
+ Collection<InterfaceDefinition> values = interfaces.values();
+ for (InterfaceDefinition interfaceDefinition : values) {
+ String interfaceType = interfaceTypeOperation.getShortInterfaceName(interfaceDefinition);
+
+ log.trace("Starting interface artifacts removal for interface type {}", interfaceType);
+ Map<String, Operation> operations = interfaceDefinition.getOperations();
+ if (operations != null) {
+ for (Entry<String, Operation> operationEntry : operations.entrySet()) {
+ Operation operation = operationEntry.getValue();
+ ArtifactDefinition implementation = operation.getImplementation();
+ if (implementation != null) {
+ String uniqueId = implementation.getUniqueId();
+ log.debug("Removing interface artifact definition {}, operation {}, interfaceType {}", uniqueId, operationEntry.getKey(), interfaceType);
+ // only thing that transacts and locks here
+ deleteArtifactByInterface = artifactsBusinessLogic.deleteArtifactByInterface(resourceId, interfaceType, operationEntry.getKey(), userId, uniqueId, null, shouldLock, true);
+ if (deleteArtifactByInterface.isRight()) {
+ log.debug("Couldn't remove artifact definition with id {}", uniqueId);
+ if (!inTransaction) {
+ titanGenericDao.rollback();
+ }
+ return Either.right(deleteArtifactByInterface.right().value());
+ }
+ } else {
+ log.trace("No implementation found for operation {} - nothing to delete", operationEntry.getKey());
+ }
+ }
+ } else {
+ log.trace("No operations found for interface type {}", interfaceType);
+ }
+ }
+ }
+ log.debug("2. Removing properties");
+ Either<Map<String, PropertyDefinition>, StorageOperationStatus> findPropertiesOfNode = propertyOperation.deleteAllPropertiesAssociatedToNode(NodeTypeEnum.Resource, resourceId);
+
+ if (findPropertiesOfNode.isRight() && !findPropertiesOfNode.right().value().equals(StorageOperationStatus.OK)) {
+ log.debug("Failed to remove all properties of resource");
+ if (!inTransaction)
+ titanGenericDao.rollback();
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(findPropertiesOfNode.right().value())));
+ }
+
+ } else {
+ log.debug("Derived from wasn't changed during update");
+ }
+
+ if (!inTransaction)
+ titanGenericDao.commit();
+ return Either.left(true);
+
+ }
+
+ /**** Auditing *******************/
+
+ protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+
+ return webApplicationContext.getBean(class1);
+ }
+
+ public ICapabilityTypeOperation getCapabilityTypeOperation() {
+ return capabilityTypeOperation;
+ }
+
+ public void setCapabilityTypeOperation(ICapabilityTypeOperation capabilityTypeOperation) {
+ this.capabilityTypeOperation = capabilityTypeOperation;
+ }
+
+ public Either<Boolean, ResponseFormat> validatePropertiesDefaultValues(Resource resource) {
+ log.debug("validate resource properties default values");
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ List<PropertyDefinition> properties = resource.getProperties();
+ String type = null;
+ String innerType = null;
+ if (properties != null) {
+ for (PropertyDefinition property : properties) {
+ if (!propertyOperation.isPropertyTypeValid(property)) {
+ log.info("Invalid type for property");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_TYPE, property.getType(), property.getName());
+ eitherResult = Either.right(responseFormat);
+ break;
+ }
+
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
+ if (allDataTypes.isRight()) {
+ return Either.right(allDataTypes.right().value());
+ }
+
+ type = property.getType();
+ if (type.equals(ToscaPropertyType.LIST.getType()) || type.equals(ToscaPropertyType.MAP.getType())) {
+ ImmutablePair<String, Boolean> propertyInnerTypeValid = propertyOperation.isPropertyInnerTypeValid(property, allDataTypes.left().value());
+ innerType = propertyInnerTypeValid.getLeft();
+ if (!propertyInnerTypeValid.getRight().booleanValue()) {
+ log.info("Invalid inner type for property");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_INNER_TYPE, innerType, property.getName());
+ eitherResult = Either.right(responseFormat);
+ break;
+ }
+ }
+
+ if (!propertyOperation.isPropertyDefaultValueValid(property, allDataTypes.left().value())) {
+ log.info("Invalid default value for property");
+ ResponseFormat responseFormat;
+ if (type.equals(ToscaPropertyType.LIST.getType()) || type.equals(ToscaPropertyType.MAP.getType())) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_COMPLEX_DEFAULT_VALUE, property.getName(), type, innerType, property.getDefaultValue());
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_DEFAULT_VALUE, property.getName(), type, property.getDefaultValue());
+ }
+ eitherResult = Either.right(responseFormat);
+ break;
+
+ }
+ }
+ }
+ return eitherResult;
+ }
+
+ @Override
+ public Either<List<String>, ResponseFormat> deleteMarkedComponents() {
+ return deleteMarkedComponents(ComponentTypeEnum.RESOURCE);
+ }
+
+ @Override
+ public ComponentInstanceBusinessLogic getComponentInstanceBL() {
+ return vfComponentInstanceBusinessLogic;
+ }
+
+ private String getComponentTypeForResponse(Component component) {
+ String componentTypeForResponse = "SERVICE";
+ if (component instanceof Resource) {
+ componentTypeForResponse = ((Resource) component).getResourceType().name();
+ }
+ return componentTypeForResponse;
+ }
+
+ private Either<Map<String, GroupDefinition>, ResponseFormat> createGroupsFromYaml(String yamlFileName, Map<String, Object> toscaJson, Resource resource) {
+
+ Map<String, GroupDefinition> groups = new HashMap<String, GroupDefinition>();
+ Either<Map<String, GroupDefinition>, ResponseFormat> result = Either.left(groups);
+
+ Either<Map<String, Object>, ResultStatusEnum> eitherNodesTemlates = ImportUtils.findFirstToscaMapElement(toscaJson, ToscaTagNamesEnum.GROUPS);
+ if (eitherNodesTemlates.isLeft()) {
+ Map<String, Object> jsonNodeTemplates = eitherNodesTemlates.left().value();
+
+ if (jsonNodeTemplates != null && false == jsonNodeTemplates.isEmpty()) {
+ Iterator<Entry<String, Object>> nodesNameValue = jsonNodeTemplates.entrySet().iterator();
+ while (nodesNameValue.hasNext()) {
+ Entry<String, Object> groupNameValue = nodesNameValue.next();
+
+ String groupName = groupNameValue.getKey();
+ Either<GroupDefinition, ResponseFormat> eitherNode = createGroupInfo(groupName, groupNameValue.getValue());
+ if (eitherNode.isRight()) {
+ String message = "Failed when creating group: " + groupNameValue.getKey() + " for resource:" + resource.getName();
+ BeEcompErrorManager.getInstance().logInternalFlowError("ImportResource", message, ErrorSeverity.INFO);
+ return Either.right(eitherNode.right().value());
+ } else {
+ GroupDefinition groupDefinition = eitherNode.left().value();
+ groups.put(groupName, groupDefinition);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Either<Map<String, InputDefinition>, ResponseFormat> createInputsFromYaml(String yamlFileName, Map<String, Object> toscaJson, Resource resource) {
+
+ Either<Map<String, InputDefinition>, ResultStatusEnum> inputs = ImportUtils.getInputs(toscaJson);
+ if (inputs.isRight()) {
+ String message = "Failed when creating inputs: for resource:" + resource.getName();
+ BeEcompErrorManager.getInstance().logInternalFlowError("ImportResource", message, ErrorSeverity.INFO);
+ Map<String, InputDefinition> resultMap = new HashMap();
+ return Either.left(resultMap);
+
+ }
+
+ Either<Map<String, InputDefinition>, ResponseFormat> result = Either.left(inputs.left().value());
+
+ return result;
+ }
+
+ private Either<GroupDefinition, ResponseFormat> createGroupInfo(String groupName, Object groupTemplateJson) {
+
+ GroupDefinition groupInfo = new GroupDefinition();
+ groupInfo.setName(groupName);
+ Either<GroupDefinition, ResponseFormat> result = Either.left(groupInfo);
+
+ try {
+ if (groupTemplateJson != null && groupTemplateJson instanceof Map) {
+ Map<String, Object> groupTemplateJsonMap = (Map<String, Object>) groupTemplateJson;
+ // Type
+ String groupType = null;
+ if (groupTemplateJsonMap.containsKey(ToscaTagNamesEnum.TYPE.getElementName())) {
+ groupType = (String) groupTemplateJsonMap.get(ToscaTagNamesEnum.TYPE.getElementName());
+ groupInfo.setType(groupType);
+ } else {
+ log.debug("The 'type' member is not found under group {}", groupName);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE));
+ }
+
+ if (groupTemplateJsonMap.containsKey(ToscaTagNamesEnum.DESCRIPTION.getElementName())) {
+ groupInfo.setDescription((String) groupTemplateJsonMap.get(ToscaTagNamesEnum.DESCRIPTION.getElementName()));
+ }
+
+ if (groupTemplateJsonMap.containsKey(ToscaTagNamesEnum.MEMBERS.getElementName())) {
+ Object members = groupTemplateJsonMap.get(ToscaTagNamesEnum.MEMBERS.getElementName());
+ if (members != null) {
+ if (members instanceof List) {
+ Map<String, String> membersLoaded = new HashMap<>();
+ List<?> membersAsList = (List<?>) members;
+ for (Object member : membersAsList) {
+ membersLoaded.put(member.toString(), "");
+ }
+ groupInfo.setMembers(membersLoaded);
+ } else {
+ log.debug("The 'type' member is not found under group {}", groupName);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE));
+ }
+ }
+ }
+
+ if (groupTemplateJsonMap.containsKey(ToscaTagNamesEnum.PROPERTIES.getElementName())) {
+ Object properties = groupTemplateJsonMap.get(ToscaTagNamesEnum.PROPERTIES.getElementName());
+
+ Either<List<GroupProperty>, ResponseFormat> regResponse = createPropertiesValueModuleFromYaml(properties, groupName, groupType);
+ if (regResponse.isRight())
+ return Either.right(regResponse.right().value());
+ if (regResponse.left().value().size() > 0) {
+ groupInfo.setProperties(regResponse.left().value());
+ }
+ }
+
+ } else {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE));
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeSystemError("Import Resource - create group");
+ log.debug("error when creating group, message:{}", e.getMessage(), e);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML));
+ }
+
+ return result;
+ }
+
+ private Either<List<GroupProperty>, ResponseFormat> createPropertiesValueModuleFromYaml(Object properties, String groupName, String groupType) {
+
+ List<GroupProperty> result = new ArrayList<>();
+
+ if (properties == null) {
+ return Either.left(result);
+ }
+
+ Either<GroupTypeDefinition, StorageOperationStatus> groupTypeRes = groupTypeOperation.getLatestGroupTypeByType(groupType, true);
+
+ if (groupTypeRes.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_MISSING_GROUP_TYPE, groupType));
+ }
+
+ Map<String, PropertyDefinition> gtProperties = new HashMap<>();
+ GroupTypeDefinition groupTypeDefinition = groupTypeRes.left().value();
+
+ List<PropertyDefinition> propertiesDef = groupTypeDefinition.getProperties();
+
+ if (propertiesDef != null) {
+ gtProperties = propertiesDef.stream().collect(Collectors.toMap(p -> p.getName(), p -> p));
+ }
+
+ if (properties != null) {
+
+ if (properties instanceof Map) {
+
+ Map<String, Object> props = (Map<String, Object>) properties;
+ for (Entry<String, Object> entry : props.entrySet()) {
+
+ String propName = entry.getKey();
+ Object value = entry.getValue();
+
+ PropertyDefinition gtDefinition = gtProperties.get(propName);
+ if (gtDefinition == null) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_PROPERTY_NOT_FOUND, propName, groupName, groupType));
+ }
+
+ ToscaPropertyType type = ToscaPropertyType.isValidType(gtDefinition.getType());
+
+ String convertedValue = null;
+ if (value != null) {
+ if (type == null || value instanceof Map || value instanceof List) {
+ convertedValue = gson.toJson(value);
+ } else {
+ convertedValue = value.toString();
+ }
+ }
+
+ GroupProperty groupProperty = new GroupProperty();
+ groupProperty.setValue(convertedValue);
+ groupProperty.setName(propName);
+
+ log.debug("After building group property {}", groupProperty);
+
+ result.add(groupProperty);
+ }
+
+ }
+
+ }
+
+ return Either.left(result);
+ }
+
+ public Either<Resource, ResponseFormat> getLatestResourceFromCsarUuid(String csarUuid, User user) {
+
+ // validate user
+ if (user != null) {
+ Either<User, ResponseFormat> userValidation = validateUserExists(user, "Get resource from csar UUID", false);
+ if (userValidation.isRight()) {
+ return Either.right(userValidation.right().value());
+ }
+ }
+
+ // get resource from csar uuid
+ Either<Resource, StorageOperationStatus> either = resourceOperation.getLatestResourceByCsarOrName(csarUuid, "");
+ if (either.isRight()) {
+ ResponseFormat resp = componentsUtils.getResponseFormat(ActionStatus.RESOURCE_FROM_CSAR_NOT_FOUND, csarUuid);
+ return Either.right(resp);
+ }
+
+ return Either.left(either.left().value());
+ }
+
+ @Override
+ public Either<List<ComponentInstance>, ResponseFormat> getComponentInstancesFilteredByPropertiesAndInputs(String componentId, ComponentTypeEnum componentTypeEnum, String userId, String searchText) {
+ return null;
+ }
+
+ private Either<Map<String, List<CapabilityDefinition>>, ResponseFormat> getValidComponentInstanceCapabilities(Map<String, List<CapabilityDefinition>> defaultCapabilities, Map<String, List<UploadCapInfo>> uploadedCapabilities) {
+ ResponseFormat responseFormat;
+ Map<String, List<CapabilityDefinition>> validCapabilitiesMap = new HashMap<>();
+
+ for (Entry<String, List<UploadCapInfo>> uploadedCapabilitiesEntry : uploadedCapabilities.entrySet()) {
+ String capabilityType = uploadedCapabilitiesEntry.getValue().get(0).getType();
+ if (!defaultCapabilities.containsKey(capabilityType)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_CAPABILITY_TYPE, capabilityType);
+ return Either.right(responseFormat);
+ } else {
+ CapabilityDefinition delaultCapability = defaultCapabilities.get(capabilityType).get(0);
+ Either<Boolean, String> validationRes = validateUniquenessUpdateUploadedComponentInstanceCapability(delaultCapability, uploadedCapabilitiesEntry.getValue().get(0));
+ if (validationRes.isRight()) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NAME_ALREADY_EXISTS, validationRes.right().value());
+ return Either.right(responseFormat);
+ }
+ List<CapabilityDefinition> validCapabilityList = new ArrayList<>();
+ validCapabilityList.add(delaultCapability);
+ validCapabilitiesMap.put(uploadedCapabilitiesEntry.getKey(), validCapabilityList);
+ }
+ }
+ return Either.left(validCapabilitiesMap);
+ }
+
+ private Either<Boolean, String> validateUniquenessUpdateUploadedComponentInstanceCapability(CapabilityDefinition defaultCapability, UploadCapInfo uploadedCapability) {
+ List<ComponentInstanceProperty> validProperties = new ArrayList<>();
+ Map<String, PropertyDefinition> defaultProperties = defaultCapability.getProperties().stream().collect(Collectors.toMap(PropertyDefinition::getName, Function.identity()));
+ List<UploadPropInfo> uploadedProperties = uploadedCapability.getProperties();
+ for (UploadPropInfo property : uploadedProperties) {
+ String propertyName = property.getName().toLowerCase();
+ String propertyType = property.getType();
+ ComponentInstanceProperty validProperty;
+ if (defaultProperties.containsKey(propertyName)) {
+ if (propertyType != null && !defaultProperties.get(propertyName).getType().equals(propertyType)) {
+ return Either.right(propertyName);
+ }
+ }
+ validProperty = new ComponentInstanceProperty();
+ validProperty.setName(propertyName);
+ if (property.getValue() != null)
+ validProperty.setValue(property.getValue().toString());
+ validProperty.setDescription(property.getDescription());
+ validProperty.setPassword(property.isPassword());
+ validProperties.add(validProperty);
+ }
+ defaultCapability.setProperties(validProperties);
+ return Either.left(true);
+ }
+
+ public ICacheMangerOperation getCacheManagerOperation() {
+ return cacheManagerOperation;
+ }
+
+ public void setCacheManagerOperation(ICacheMangerOperation cacheManagerOperation) {
+ this.cacheManagerOperation = cacheManagerOperation;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java
new file mode 100644
index 0000000000..329481a546
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java
@@ -0,0 +1,921 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.components.impl.ImportUtils.Constants;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.WebApplicationContext;
+import org.yaml.snakeyaml.Yaml;
+
+import fj.data.Either;
+
+@Component("resourceImportManager")
+public class ResourceImportManager {
+
+ private ServletContext servletContext;
+
+ @Autowired
+ private IAuditingManager auditingManager;
+
+ @Autowired
+ private ResourceBusinessLogic resourceBusinessLogic;
+
+ @Autowired
+ private IGraphLockOperation graphLockOperation;
+
+ @Autowired
+ protected ComponentsUtils componentsUtils;
+
+ @Autowired
+ protected ResourceOperation resourceOperation;
+
+ @Autowired
+ protected CapabilityTypeOperation capabilityTypeOperation;
+
+ private ResponseFormatManager responseFormatManager;
+
+ private static Logger log = LoggerFactory.getLogger(ResourceImportManager.class.getName());
+
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> importNormativeResource(String resourceYml, UploadResourceInfo resourceMetaData, User creator, boolean createNewVersion, boolean needLock) {
+
+ LifecycleChangeInfoWithAction lifecycleChangeInfo = new LifecycleChangeInfoWithAction();
+ lifecycleChangeInfo.setUserRemarks("certification on import");
+ Function<Resource, Either<Boolean, ResponseFormat>> validator = (resource) -> resourceBusinessLogic.validatePropertiesDefaultValues(resource);
+
+ return importCertifiedResource(resourceYml, resourceMetaData, creator, validator, lifecycleChangeInfo, false, createNewVersion, needLock);
+ }
+
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> importCertifiedResource(String resourceYml, UploadResourceInfo resourceMetaData, User creator, Function<Resource, Either<Boolean, ResponseFormat>> validationFunction,
+ LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean isInTransaction, boolean createNewVersion, boolean needLock) {
+ Resource resource = new Resource();
+ ImmutablePair<Resource, ActionStatus> responsePair = new ImmutablePair<Resource, ActionStatus>(resource, ActionStatus.CREATED);
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> response = Either.left(responsePair);
+
+ String latestCertifiedResourceId = null;
+ try {
+ setConstantMetaData(resource);
+ setMetaDataFromJson(resourceMetaData, resource);
+
+ Either<Boolean, ResponseFormat> validateResourceFromYaml = populateResourceFromYaml(resourceYml, resource, isInTransaction);
+ if (validateResourceFromYaml.isRight()) {
+ ResponseFormat validationErrorResponse = validateResourceFromYaml.right().value();
+ auditErrorImport(resourceMetaData, creator, validationErrorResponse, true);
+ return Either.right(validationErrorResponse);
+
+ }
+
+ Either<Boolean, ResponseFormat> isValidResource = validationFunction.apply(resource);
+ if (isValidResource.isLeft()) {
+ // The flag createNewVersion if false doesn't create new version
+ if (!createNewVersion) {
+ Either<Resource, StorageOperationStatus> latestByName = resourceOperation.getLatestByName(resource.getName(), isInTransaction);
+ if (latestByName.isLeft()) {
+ return Either.right(componentsUtils.getResponseFormatByResource(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, resource));
+ }
+ }
+
+ response = resourceBusinessLogic.createOrUpdateResourceByImport(resource, creator, true, isInTransaction, needLock);
+ if (response.isLeft()) {
+ resource = response.left().value().left;
+ latestCertifiedResourceId = getLatestCertifiedResourceId(resource);
+ Either<Resource, ResponseFormat> certificationResponse = resourceBusinessLogic.propagateStateToCertified(creator, resource, lifecycleChangeInfo, isInTransaction, needLock);
+ if (certificationResponse.isRight()) {
+ response = Either.right(certificationResponse.right().value());
+ } else {
+ responsePair = new ImmutablePair<Resource, ActionStatus>(certificationResponse.left().value(), response.left().value().right);
+ response = Either.left(responsePair);
+ }
+ }
+ } else {
+ ResponseFormat validationErrorResponse = isValidResource.right().value();
+ auditErrorImport(resourceMetaData, creator, validationErrorResponse, true);
+ response = Either.right(validationErrorResponse);
+ }
+
+ } catch (RuntimeException e) {
+ ResponseFormat exceptionResponse = handleImportResourceExecption(resourceMetaData, creator, true, e);
+ response = Either.right(exceptionResponse);
+ } finally {
+ if (latestCertifiedResourceId != null && needLock) {
+ log.debug("unlock resource {}", latestCertifiedResourceId);
+ graphLockOperation.unlockComponent(latestCertifiedResourceId, NodeTypeEnum.Resource);
+ }
+ }
+
+ return response;
+ }
+
+ private String getLatestCertifiedResourceId(Resource resource) {
+ Map<String, String> allVersions = resource.getAllVersions();
+ Double latestCertifiedVersion = 0.0;
+ if (allVersions != null) {
+ for (String version : allVersions.keySet()) {
+ Double dVersion = Double.valueOf(version);
+ if ((dVersion > latestCertifiedVersion) && (version.endsWith(".0"))) {
+ latestCertifiedVersion = dVersion;
+ }
+ }
+ return allVersions.get(String.valueOf(latestCertifiedVersion));
+ } else {
+ return null;
+ }
+ }
+
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> importUserDefinedResource(String resourceYml, UploadResourceInfo resourceMetaData, User creator, boolean isReusable, boolean isInTransaction) {
+
+ Resource resource = new Resource();
+ ImmutablePair<Resource, ActionStatus> responsePair = new ImmutablePair<Resource, ActionStatus>(resource, ActionStatus.CREATED);
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> response = Either.left(responsePair);
+
+ try {
+ setMetaDataFromJson(resourceMetaData, resource);
+
+ Either<Boolean, ResponseFormat> validateResourceFromYaml = populateResourceFromYaml(resourceYml, resource, isInTransaction);
+ if (validateResourceFromYaml.isRight()) {
+ ResponseFormat validationErrorResponse = validateResourceFromYaml.right().value();
+ auditErrorImport(resourceMetaData, creator, validationErrorResponse, false);
+ return Either.right(validationErrorResponse);
+
+ }
+
+ // currently import VF isn't supported. In future will be supported
+ // import VF only with CSER file!!
+ if (ResourceTypeEnum.VF.equals(resource.getResourceType())) {
+ log.debug("Now import VF isn't supported. It will be supported in future with CSER file only");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ Either<Boolean, ResponseFormat> validateDerivedFromNotEmpty = resourceBusinessLogic.validateDerivedFromNotEmpty(creator, resource, AuditingActionEnum.CREATE_RESOURCE);
+ if (validateDerivedFromNotEmpty.isRight()) {
+ return Either.right(validateDerivedFromNotEmpty.right().value());
+ }
+
+ Either<Boolean, ResponseFormat> validatePropertiesTypes = resourceBusinessLogic.validatePropertiesDefaultValues(resource);
+
+ if (validatePropertiesTypes.isLeft()) {
+ response = resourceBusinessLogic.createOrUpdateResourceByImport(resource, creator, false, isInTransaction, true);
+ } else {
+ ResponseFormat validationErrorResponse = validatePropertiesTypes.right().value();
+ auditErrorImport(resourceMetaData, creator, validationErrorResponse, false);
+ response = Either.right(validationErrorResponse);
+ }
+
+ } catch (RuntimeException e) {
+ ResponseFormat exceptionResponse = handleImportResourceExecption(resourceMetaData, creator, false, e);
+ response = Either.right(exceptionResponse);
+ }
+
+ return response;
+
+ }
+
+ private Either<Boolean, ResponseFormat> populateResourceFromYaml(String resourceYml, Resource resource, boolean inTransaction) {
+ @SuppressWarnings("unchecked")
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ Map<String, Object> toscaJsonAll = (Map<String, Object>) new Yaml().load(resourceYml);
+ Map<String, Object> toscaJson = toscaJsonAll;
+
+ // Checks if exist and builds the node_types map
+ if (toscaJsonAll.containsKey(ToscaTagNamesEnum.NODE_TYPES.getElementName())) {
+ toscaJson = new HashMap<String, Object>();
+ toscaJson.put(ToscaTagNamesEnum.NODE_TYPES.getElementName(), toscaJsonAll.get(ToscaTagNamesEnum.NODE_TYPES.getElementName()));
+ }
+ // Derived From
+ Either<Resource, ResponseFormat> setDerivedFrom = setDerivedFrom(toscaJson, resource, inTransaction);
+ if (setDerivedFrom.isRight()) {
+ return Either.right(setDerivedFrom.right().value());
+ }
+ Resource parentResource = setDerivedFrom.left().value();
+ setToscaResourceName(toscaJson, resource);
+ setAttributes(toscaJson, resource);
+ eitherResult = setCapabilities(toscaJson, resource, parentResource);
+ if (eitherResult.isRight())
+ return eitherResult;
+ setProperties(toscaJson, resource);
+ eitherResult = setRequirements(toscaJson, resource, parentResource);
+ if (eitherResult.isRight())
+ return eitherResult;
+ setInterfaceLifecycle(toscaJson, resource);
+
+ return eitherResult;
+ }
+
+ private void setToscaResourceName(Map<String, Object> toscaJson, Resource resource) {
+ Either<Map<String, Object>, ResultStatusEnum> toscaElement = ImportUtils.findFirstToscaMapElement(toscaJson, ToscaTagNamesEnum.NODE_TYPES);
+ if (toscaElement.isLeft() || toscaElement.left().value().size() == 1) {
+ String toscaResourceName = toscaElement.left().value().keySet().iterator().next();
+ resource.setToscaResourceName(toscaResourceName);
+ }
+ }
+
+ private void setInterfaceLifecycle(Map<String, Object> toscaJson, Resource resource) {
+ Either<Map<String, Object>, ResultStatusEnum> toscaInterfaces = ImportUtils.findFirstToscaMapElement(toscaJson, ToscaTagNamesEnum.INTERFACES);
+ if (toscaInterfaces.isLeft()) {
+ Map<String, Object> jsonInterfaces = toscaInterfaces.left().value();
+ Map<String, InterfaceDefinition> moduleInterfaces = new HashMap<String, InterfaceDefinition>();
+ Iterator<Entry<String, Object>> interfacesNameValue = jsonInterfaces.entrySet().iterator();
+ while (interfacesNameValue.hasNext()) {
+ Entry<String, Object> interfaceNameValue = interfacesNameValue.next();
+ Either<InterfaceDefinition, ResultStatusEnum> eitherInterface = createModuleInterface(interfaceNameValue.getValue());
+ if (eitherInterface.isRight()) {
+ log.info("error when creating interface:{}, for resource:{}", interfaceNameValue.getKey(), resource.getName());
+ } else {
+ moduleInterfaces.put(interfaceNameValue.getKey(), eitherInterface.left().value());
+ }
+
+ }
+ if (moduleInterfaces.size() > 0) {
+ resource.setInterfaces(moduleInterfaces);
+ }
+ }
+ }
+
+ private Either<InterfaceDefinition, ResultStatusEnum> createModuleInterface(Object interfaceJson) {
+ InterfaceDefinition interf = new InterfaceDefinition();
+ Either<InterfaceDefinition, ResultStatusEnum> result = Either.left(interf);
+
+ try {
+ if (interfaceJson instanceof String) {
+ String requirementJsonString = (String) interfaceJson;
+ interf.setType(requirementJsonString);
+ } else if (interfaceJson instanceof Map) {
+ Map<String, Object> requirementJsonMap = (Map<String, Object>) interfaceJson;
+ if (requirementJsonMap.containsKey(ToscaTagNamesEnum.TYPE.getElementName())) {
+ String type = (String) requirementJsonMap.get(ToscaTagNamesEnum.TYPE.getElementName());
+ interf.setType(type);
+ interf.setUniqueId(type.toLowerCase());
+ }
+ } else {
+ result = Either.right(ResultStatusEnum.GENERAL_ERROR);
+ }
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Import Resource- create interface");
+ BeEcompErrorManager.getInstance().logBeSystemError("Import Resource- create interface");
+ log.debug("error when creating interface, message:{}", e.getMessage(), e);
+ result = Either.right(ResultStatusEnum.GENERAL_ERROR);
+ }
+
+ return result;
+ }
+
+ private Either<Boolean, ResponseFormat> setRequirements(Map<String, Object> toscaJson, Resource resource, Resource parentResource) {// Note that parentResource can be null
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ Either<List<Object>, ResultStatusEnum> toscaRequirements = ImportUtils.findFirstToscaListElement(toscaJson, ToscaTagNamesEnum.REQUIREMENTS);
+ if (toscaRequirements.isLeft()) {
+ List<Object> jsonRequirements = toscaRequirements.left().value();
+ Map<String, List<RequirementDefinition>> moduleRequirements = new HashMap<String, List<RequirementDefinition>>();
+ // Checking for name duplication
+ Set<String> reqNames = new HashSet<>();
+ // Getting flattened list of capabilities of parent node - cap name
+ // to cap type
+ Either<Map<String, String>, ResponseFormat> reqName2Type = getReqName2Type(parentResource);
+ if (reqName2Type.isRight()) {
+ ResponseFormat responseFormat = reqName2Type.right().value();
+ log.debug("Error during setting requirements of imported resource: {}", responseFormat);
+ return Either.right(responseFormat);
+ }
+ Map<String, String> reqName2TypeMap = reqName2Type.left().value();
+ for (Object jsonRequirementObj : jsonRequirements) {
+ // Requirement
+ Map<String, Object> requirementJsonWrapper = (Map<String, Object>) jsonRequirementObj;
+ String requirementName = requirementJsonWrapper.keySet().iterator().next();
+ String reqNameLowerCase = requirementName.toLowerCase();
+ if (reqNames.contains(reqNameLowerCase)) {
+ log.debug("More than one requirement with same name {} (case-insensitive) in imported TOSCA file is invalid", reqNameLowerCase);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME, "requirement", reqNameLowerCase));
+ }
+ reqNames.add(reqNameLowerCase);
+ Either<RequirementDefinition, ResponseFormat> eitherRequirement = createRequirementFromImportFile(requirementJsonWrapper.get(requirementName));
+ if (eitherRequirement.isRight()) {
+ log.info("error when creating Requirement:{}, for resource:{}", requirementName, resource.getName());
+ return Either.right(eitherRequirement.right().value());
+ }
+ RequirementDefinition requirementDef = eitherRequirement.left().value();
+ requirementDef.setName(requirementName);
+ if (moduleRequirements.containsKey(requirementDef.getCapability())) {
+ moduleRequirements.get(requirementDef.getCapability()).add(requirementDef);
+ } else {
+ List<RequirementDefinition> list = new ArrayList<RequirementDefinition>();
+ list.add(requirementDef);
+ moduleRequirements.put(requirementDef.getCapability(), list);
+ }
+
+ // Validating against req/cap of "derived from" node
+ Either<Boolean, ResponseFormat> validateVsParentCap = validateCapNameVsDerived(reqName2TypeMap, requirementDef.getCapability(), requirementDef.getName());
+ if (validateVsParentCap.isRight()) {
+ return Either.right(validateVsParentCap.right().value());
+ }
+ if (!validateVsParentCap.left().value()) {
+ log.debug("Requirement with name {} already exists in parent {}", requirementDef.getName(), parentResource.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED, "requirement", requirementDef.getName().toLowerCase(), parentResource.getName());
+ return Either.right(responseFormat);
+ }
+ }
+ if (moduleRequirements.size() > 0) {
+ resource.setRequirements(moduleRequirements);
+ }
+
+ }
+ return eitherResult;
+
+ }
+
+ private Either<RequirementDefinition, ResponseFormat> createRequirementFromImportFile(Object requirementJson) {
+ RequirementDefinition requirement = new RequirementDefinition();
+ Either<RequirementDefinition, ResponseFormat> result = Either.left(requirement);
+
+ try {
+ if (requirementJson instanceof String) {
+ String requirementJsonString = (String) requirementJson;
+ requirement.setCapability(requirementJsonString);
+ } else if (requirementJson instanceof Map) {
+ Map<String, Object> requirementJsonMap = (Map<String, Object>) requirementJson;
+ if (requirementJsonMap.containsKey(ToscaTagNamesEnum.CAPABILITY.getElementName())) {
+ requirement.setCapability((String) requirementJsonMap.get(ToscaTagNamesEnum.CAPABILITY.getElementName()));
+ }
+
+ if (requirementJsonMap.containsKey(ToscaTagNamesEnum.NODE.getElementName())) {
+ requirement.setNode((String) requirementJsonMap.get(ToscaTagNamesEnum.NODE.getElementName()));
+ }
+
+ if (requirementJsonMap.containsKey(ToscaTagNamesEnum.RELATIONSHIP.getElementName())) {
+ requirement.setRelationship((String) requirementJsonMap.get(ToscaTagNamesEnum.RELATIONSHIP.getElementName()));
+ }
+ if (requirementJsonMap.containsKey(ToscaTagNamesEnum.OCCURRENCES.getElementName())) {
+ List<Object> occurrencesList = (List) requirementJsonMap.get(ToscaTagNamesEnum.OCCURRENCES.getElementName());
+ Either<Boolean, ResponseFormat> validateAndSetOccurrencesStatus = validateOccurrences(occurrencesList);
+ if (validateAndSetOccurrencesStatus.isRight()) {
+ result = Either.right(validateAndSetOccurrencesStatus.right().value());
+ return result;
+ }
+ if (validateAndSetOccurrencesStatus.left().value() == true) {
+ requirement.setMinOccurrences(occurrencesList.get(0).toString());
+ requirement.setMaxOccurrences(occurrencesList.get(1).toString());
+ }
+
+ }
+ } else {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML));
+ }
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Import Resource - create Requirement");
+ BeEcompErrorManager.getInstance().logBeSystemError("Import Resource - create Requirement");
+ log.debug("error when creating requirement, message:{}", e.getMessage(), e);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML));
+ }
+
+ return result;
+ }
+
+ private ResultStatusEnum setProperties(Map<String, Object> toscaJson, Resource resource) {
+ Map<String, Object> reducedToscaJson = new HashMap<>(toscaJson);
+ ImportUtils.removeElementFromJsonMap(reducedToscaJson, "capabilities");
+ ResultStatusEnum result = ResultStatusEnum.OK;
+ Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = ImportUtils.getProperties(reducedToscaJson);
+ if (properties.isLeft()) {
+ List<PropertyDefinition> propertiesList = new ArrayList<>();
+ Map<String, PropertyDefinition> value = properties.left().value();
+ if (value != null) {
+ for (Entry<String, PropertyDefinition> entry : value.entrySet()) {
+ String name = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ propertyDefinition.setName(name);
+ propertiesList.add(propertyDefinition);
+ }
+ }
+ resource.setProperties(propertiesList);
+ } else {
+ result = properties.right().value();
+ }
+ return result;
+ }
+
+ private ResultStatusEnum setAttributes(Map<String, Object> toscaJson, Resource resource) {
+ ResultStatusEnum result = ResultStatusEnum.OK;
+ Either<Map<String, AttributeDefinition>, ResultStatusEnum> attributes = ImportUtils.getAttributes(toscaJson);
+ if (attributes.isLeft()) {
+ List<AttributeDefinition> attributeList = new ArrayList<>();
+ Map<String, AttributeDefinition> value = attributes.left().value();
+ if (value != null) {
+ for (Entry<String, AttributeDefinition> entry : value.entrySet()) {
+ String name = entry.getKey();
+ AttributeDefinition attributeDef = entry.getValue();
+ attributeDef.setName(name);
+ attributeList.add(attributeDef);
+ }
+ }
+ resource.setAttributes(attributeList);
+ } else {
+ result = attributes.right().value();
+ }
+ return result;
+ }
+
+ private Either<Resource, ResponseFormat> setDerivedFrom(Map<String, Object> toscaJson, Resource resource, boolean inTransaction) {
+ Either<String, ResultStatusEnum> toscaDerivedFromElement = ImportUtils.findFirstToscaStringElement(toscaJson, ToscaTagNamesEnum.DERIVED_FROM);
+ Resource derivedFromResource = null;
+ if (toscaDerivedFromElement.isLeft()) {
+ String derivedFrom = toscaDerivedFromElement.left().value();
+ log.debug("Derived from TOSCA name is {}", derivedFrom);
+ resource.setDerivedFrom(Arrays.asList(new String[] { derivedFrom }));
+ Either<Resource, StorageOperationStatus> latestByToscaResourceName = resourceOperation.getLatestByToscaResourceName(derivedFrom, inTransaction);
+ if (latestByToscaResourceName.isRight()) {
+ StorageOperationStatus operationStatus = latestByToscaResourceName.right().value();
+ if (operationStatus.equals(StorageOperationStatus.NOT_FOUND)) {
+ operationStatus = StorageOperationStatus.PARENT_RESOURCE_NOT_FOUND;
+ }
+ log.debug("Error when fetching parent resource {}, error: {}", derivedFrom, operationStatus);
+ ActionStatus convertFromStorageResponse = componentsUtils.convertFromStorageResponse(operationStatus);
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("Import TOSCA YAML", "resource", derivedFrom);
+ return Either.right(componentsUtils.getResponseFormat(convertFromStorageResponse, derivedFrom));
+ }
+ derivedFromResource = latestByToscaResourceName.left().value();
+ }
+ return Either.left(derivedFromResource);
+ }
+
+ private Either<Boolean, ResponseFormat> setCapabilities(Map<String, Object> toscaJson, Resource resource, Resource parentResource) {// Note that parentResource can be null
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+ Either<Map<String, Object>, ResultStatusEnum> toscaCapabilities = ImportUtils.findFirstToscaMapElement(toscaJson, ToscaTagNamesEnum.CAPABILITIES);
+ if (toscaCapabilities.isLeft()) {
+ Map<String, Object> jsonCapabilities = toscaCapabilities.left().value();
+ Map<String, List<CapabilityDefinition>> moduleCapabilities = new HashMap<String, List<CapabilityDefinition>>();
+ Iterator<Entry<String, Object>> capabilitiesNameValue = jsonCapabilities.entrySet().iterator();
+ Set<String> capNames = new HashSet<>();
+ // Getting flattened list of capabilities of parent node - cap name
+ // to cap type
+ Either<Map<String, String>, ResponseFormat> capName2Type = getCapName2Type(parentResource);
+ if (capName2Type.isRight()) {
+ ResponseFormat responseFormat = capName2Type.right().value();
+ log.debug("Error during setting capabilities of imported resource: {}", responseFormat);
+ return Either.right(responseFormat);
+ }
+ Map<String, String> capName2TypeMap = capName2Type.left().value();
+ while (capabilitiesNameValue.hasNext()) {
+ Entry<String, Object> capabilityNameValue = capabilitiesNameValue.next();
+
+ // Validating that no req/cap duplicates exist in imported YAML
+ String capNameLowerCase = capabilityNameValue.getKey().toLowerCase();
+ if (capNames.contains(capNameLowerCase)) {
+ log.debug("More than one capability with same name {} (case-insensitive) in imported TOSCA file is invalid", capNameLowerCase);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.IMPORT_DUPLICATE_REQ_CAP_NAME, "capability", capNameLowerCase));
+ }
+ capNames.add(capNameLowerCase);
+
+ Either<CapabilityDefinition, ResponseFormat> eitherCapability = createCapabilityFromImportFile(capabilityNameValue.getValue());
+ if (eitherCapability.isRight()) {
+ log.debug("error when creating capability:{}, for resource:{}", capabilityNameValue.getKey(), resource.getName());
+ return Either.right(eitherCapability.right().value());
+ }
+
+ CapabilityDefinition capabilityDef = eitherCapability.left().value();
+ capabilityDef.setName(capabilityNameValue.getKey());
+ if (moduleCapabilities.containsKey(capabilityDef.getType())) {
+ moduleCapabilities.get(capabilityDef.getType()).add(capabilityDef);
+ } else {
+ List<CapabilityDefinition> list = new ArrayList<CapabilityDefinition>();
+ list.add(capabilityDef);
+ moduleCapabilities.put(capabilityDef.getType(), list);
+ }
+
+ // Validating against req/cap of "derived from" node
+ Either<Boolean, ResponseFormat> validateVsParentCap = validateCapNameVsDerived(capName2TypeMap, capabilityDef.getType(), capabilityDef.getName());
+ if (validateVsParentCap.isRight()) {
+ return Either.right(validateVsParentCap.right().value());
+ }
+ if (!validateVsParentCap.left().value()) {
+ // Here parentResource is for sure not null, so it's
+ // null-safe
+ log.debug("Capability with name {} already exists in parent {}", capabilityDef.getName(), parentResource.getName());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED, "capability", capabilityDef.getName().toLowerCase(), parentResource.getName());
+ return Either.right(responseFormat);
+ }
+ }
+ if (moduleCapabilities.size() > 0) {
+ resource.setCapabilities(moduleCapabilities);
+ }
+ }
+
+ return eitherResult;
+
+ }
+
+ private Either<Map<String, String>, ResponseFormat> getCapName2Type(Resource parentResource) {
+ Map<String, String> capName2type = new HashMap<>();
+ if (parentResource != null) {
+ Map<String, List<CapabilityDefinition>> capabilities = parentResource.getCapabilities();
+ if (capabilities != null) {
+ for (List<CapabilityDefinition> capDefinitions : capabilities.values()) {
+ for (CapabilityDefinition capDefinition : capDefinitions) {
+ String nameLowerCase = capDefinition.getName().toLowerCase();
+ if (capName2type.get(nameLowerCase) != null) {
+ String parentResourceName = parentResource.getName();
+ log.debug("Resource with name {} has more than one capability with name {}, ignoring case", parentResourceName, nameLowerCase);
+ BeEcompErrorManager.getInstance().logInternalDataError("Import resource", "Parent resource " + parentResourceName + " of imported resource has one or more capabilities with name " + nameLowerCase, ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ capName2type.put(nameLowerCase, capDefinition.getType());
+ }
+ }
+ }
+ }
+ return Either.left(capName2type);
+ }
+
+ private Either<Map<String, String>, ResponseFormat> getReqName2Type(Resource parentResource) {
+ Map<String, String> reqName2type = new HashMap<>();
+ if (parentResource != null) {
+ Map<String, List<RequirementDefinition>> requirements = parentResource.getRequirements();
+ if (requirements != null) {
+ for (List<RequirementDefinition> reqDefinitions : requirements.values()) {
+ for (RequirementDefinition reqDefinition : reqDefinitions) {
+ String nameLowerCase = reqDefinition.getName().toLowerCase();
+ if (reqName2type.get(nameLowerCase) != null) {
+ String parentResourceName = parentResource.getName();
+ log.debug("Resource with name {} has more than one requirement with name {}, ignoring case", parentResourceName, nameLowerCase);
+ BeEcompErrorManager.getInstance().logInternalDataError("Import resource", "Parent resource " + parentResourceName + " of imported resource has one or more requirements with name " + nameLowerCase, ErrorSeverity.ERROR);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ reqName2type.put(nameLowerCase, reqDefinition.getCapability());
+ }
+ }
+ }
+ }
+ return Either.left(reqName2type);
+ }
+
+ private Either<Boolean, ResponseFormat> validateCapNameVsDerived(Map<String, String> parentCapName2Type, String childCapabilityType, String reqCapName) {
+ String capNameLowerCase = reqCapName.toLowerCase();
+ log.trace("Validating capability {} vs parent resource", capNameLowerCase);
+ String parentCapType = parentCapName2Type.get(capNameLowerCase);
+ if (parentCapType != null) {
+ if (childCapabilityType.equals(parentCapType)) {
+ log.debug("Capability with name {} is of same type {} for imported resource and its parent - this is OK", capNameLowerCase, childCapabilityType);
+ return Either.left(true);
+ }
+ Either<Boolean, StorageOperationStatus> capabilityTypeDerivedFrom = capabilityTypeOperation.isCapabilityTypeDerivedFrom(childCapabilityType, parentCapType);
+ if (capabilityTypeDerivedFrom.isRight()) {
+ log.debug("Couldn't check whether imported resource capability derives from its parent's capability");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(capabilityTypeDerivedFrom.right().value()));
+ return Either.right(responseFormat);
+ }
+ return Either.left(capabilityTypeDerivedFrom.left().value());
+ }
+ return Either.left(true);
+ }
+
+ private Either<CapabilityDefinition, ResponseFormat> createCapabilityFromImportFile(Object capabilityJson) {
+
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ Either<CapabilityDefinition, ResponseFormat> result = Either.left(capabilityDefinition);
+
+ try {
+ if (capabilityJson instanceof String) {
+ String capabilityJsonString = (String) capabilityJson;
+ capabilityDefinition.setType(capabilityJsonString);
+ } else if (capabilityJson instanceof Map) {
+ Map<String, Object> capabilityJsonMap = (Map<String, Object>) capabilityJson;
+ // Type
+ if (capabilityJsonMap.containsKey(ToscaTagNamesEnum.TYPE.getElementName())) {
+ capabilityDefinition.setType((String) capabilityJsonMap.get(ToscaTagNamesEnum.TYPE.getElementName()));
+ }
+ // ValidSourceTypes
+ if (capabilityJsonMap.containsKey(ToscaTagNamesEnum.VALID_SOURCE_TYPES.getElementName())) {
+ capabilityDefinition.setValidSourceTypes((List<String>) capabilityJsonMap.get(ToscaTagNamesEnum.VALID_SOURCE_TYPES.getElementName()));
+ }
+ // ValidSourceTypes
+ if (capabilityJsonMap.containsKey(ToscaTagNamesEnum.DESCRIPTION.getElementName())) {
+ capabilityDefinition.setDescription((String) capabilityJsonMap.get(ToscaTagNamesEnum.DESCRIPTION.getElementName()));
+ }
+ if (capabilityJsonMap.containsKey(ToscaTagNamesEnum.OCCURRENCES.getElementName())) {
+ List<Object> occurrencesList = (List) capabilityJsonMap.get(ToscaTagNamesEnum.OCCURRENCES.getElementName());
+ Either<Boolean, ResponseFormat> validateAndSetOccurrencesStatus = validateOccurrences(occurrencesList);
+ if (validateAndSetOccurrencesStatus.isRight()) {
+ result = Either.right(validateAndSetOccurrencesStatus.right().value());
+ return result;
+ }
+ if (validateAndSetOccurrencesStatus.left().value() == true) {
+ capabilityDefinition.setMinOccurrences(occurrencesList.get(0).toString());
+ capabilityDefinition.setMaxOccurrences(occurrencesList.get(1).toString());
+ }
+ }
+ if (capabilityJsonMap.containsKey(ToscaTagNamesEnum.PROPERTIES.getElementName())) {
+
+ Either<Map<String, PropertyDefinition>, ResultStatusEnum> propertiesRes = ImportUtils.getProperties(capabilityJsonMap);
+ if (propertiesRes.isRight()) {
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND));
+ return result;
+ } else {
+ propertiesRes.left().value().entrySet().stream().forEach(e -> e.getValue().setName(e.getKey().toLowerCase()));
+ List<ComponentInstanceProperty> capabilityProperties = propertiesRes.left().value().values().stream().map(p -> new ComponentInstanceProperty(p, p.getDefaultValue(), null)).collect(Collectors.toList());
+ capabilityDefinition.setProperties(capabilityProperties);
+ }
+ }
+ } else {
+
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML));
+
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Import Resource - create capability");
+ BeEcompErrorManager.getInstance().logBeSystemError("Import Resource - create capability");
+ log.debug("error when creating capability, message:{}", e.getMessage(), e);
+ result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_YAML));
+ }
+
+ return result;
+ }
+
+ private ResponseFormat handleImportResourceExecption(UploadResourceInfo resourceMetaData, User user, boolean isNormative, RuntimeException e) {
+ String payloadName = (resourceMetaData != null) ? resourceMetaData.getPayloadName() : "";
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Import Resource " + payloadName);
+ BeEcompErrorManager.getInstance().logBeSystemError("Import Resource " + payloadName);
+
+ log.debug("Error when importing resource from payload:{} Exception text: {}", payloadName, e.getMessage(), e);
+ ResponseFormat errorResponseWrapper = getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ auditErrorImport(resourceMetaData, user, errorResponseWrapper, isNormative);
+ return errorResponseWrapper;
+ }
+
+ private void auditErrorImport(UploadResourceInfo resourceMetaData, User user, ResponseFormat errorResponseWrapper, boolean isNormative) {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, AuditingActionEnum.IMPORT_RESOURCE.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceMetaData.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, ComponentTypeEnum.RESOURCE.getValue());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, user.getUserId());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, errorResponseWrapper.getStatus());
+ String message = "";
+ if (errorResponseWrapper.getMessageId() != null) {
+ message = errorResponseWrapper.getMessageId() + ": ";
+ }
+ message += errorResponseWrapper.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, user.getFirstName() + " " + user.getLastName());
+
+ String version, lifeCycleState;
+ if (isNormative) {
+ version = Constants.FIRST_CERTIFIED_VERSION_VERSION;
+ lifeCycleState = LifecycleStateEnum.CERTIFIED.name();
+ } else {
+ version = "";
+ lifeCycleState = LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name();
+
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, version);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, lifeCycleState);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE, "");
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ private void setMetaDataFromJson(UploadResourceInfo resourceMetaData, Resource resource) {
+ resource.setTags(resourceMetaData.getTags());
+ List<CategoryDefinition> categories = resourceMetaData.getCategories();
+ resource.setCategories(categories);
+ resource.setDescription(resourceMetaData.getDescription());
+ resource.setIcon(resourceMetaData.getResourceIconPath());
+ resource.setName(resourceMetaData.getName());
+ if (categories != null && !categories.isEmpty()) {
+ CategoryDefinition categoryDef = categories.get(0);
+ resource.setAbstract(false);
+ if (categoryDef != null && categoryDef.getName() != null && categoryDef.getName().equals(Constants.ABSTRACT_CATEGORY_NAME)) {
+ SubCategoryDefinition subCategoryDef = categoryDef.getSubcategories().get(0);
+ if (subCategoryDef != null && subCategoryDef.getName().equals(Constants.ABSTRACT_SUBCATEGORY)) {
+ resource.setAbstract(true);
+ }
+ }
+ }
+ resource.setContactId(resourceMetaData.getContactId());
+ resource.setCreatorUserId(resourceMetaData.getContactId());
+
+ if (resourceMetaData.getVendorName() != null) {
+ resource.setVendorName(resourceMetaData.getVendorName());
+ }
+
+ if (resourceMetaData.getVendorRelease() != null) {
+ resource.setVendorRelease(resourceMetaData.getVendorRelease());
+ }
+
+ resource.setResourceType(ResourceTypeEnum.valueOf(resourceMetaData.getResourceType()));
+
+ }
+
+ private void setConstantMetaData(Resource resource) {
+ resource.setVersion(ImportUtils.Constants.FIRST_CERTIFIED_VERSION_VERSION);
+ ;
+ resource.setLifecycleState(ImportUtils.Constants.NORMATIVE_TYPE_LIFE_CYCLE);
+ resource.setHighestVersion(ImportUtils.Constants.NORMATIVE_TYPE_HIGHEST_VERSION);
+ resource.setVendorName(ImportUtils.Constants.VENDOR_NAME);
+ resource.setVendorRelease(ImportUtils.Constants.VENDOR_RELEASE);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateOccurrences(List<Object> occurrensesList) {
+
+ if (!ValidationUtils.validateListNotEmpty(occurrensesList)) {
+ log.debug("Occurrenses list empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+
+ if (occurrensesList.size() < 2) {
+ log.debug("Occurrenses list size not 2");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+ Object minObj = occurrensesList.get(0);
+ Object maxObj = occurrensesList.get(1);
+ Integer minOccurrences = null;
+ Integer maxOccurrences = null;
+ if (minObj instanceof Integer)
+ minOccurrences = (Integer) minObj;
+ else {
+ log.debug("Invalid occurrenses format. low_bound occurrense must be Integer {}", minObj);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+ if (minOccurrences < 0) {
+ log.debug("Invalid occurrenses format.low_bound occurrense negative {}", minOccurrences);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+
+ if (maxObj instanceof String) {
+ if (maxObj.equals("UNBOUNDED")) {
+ return Either.left(true);
+ } else {
+ log.debug("Invalid occurrenses format. Max occurrence is {}", maxObj);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+ } else {
+ if (maxObj instanceof Integer)
+ maxOccurrences = (Integer) maxObj;
+ else {
+ log.debug("Invalid occurrenses format. Max occurrence is {}", maxObj);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+
+ if (maxOccurrences <= 0 || maxOccurrences < minOccurrences) {
+ log.debug("Invalid occurrenses format. min occurrence is {}. Max occurrence is {}", minOccurrences, maxOccurrences);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_OCCURRENCES);
+ return Either.right(responseFormat);
+ }
+ }
+
+ return Either.left(true);
+
+ }
+
+ public void init(ServletContext servletContext) {
+ if (this.servletContext == null) {
+ synchronized (this) {
+ if (this.servletContext == null) {
+ this.servletContext = servletContext;
+ responseFormatManager = ResponseFormatManager.getInstance();
+ resourceBusinessLogic = getResourceBL(servletContext);
+ }
+ }
+ }
+ }
+
+ public boolean isResourceExist(String resourceName) {
+ return resourceBusinessLogic.isResourceExist(resourceName);
+ }
+
+ private ResourceBusinessLogic getResourceBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(org.openecomp.sdc.common.api.Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ ResourceBusinessLogic resourceBl = webApplicationContext.getBean(ResourceBusinessLogic.class);
+ return resourceBl;
+ }
+
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
+
+ public IAuditingManager getAuditingManager() {
+ return auditingManager;
+ }
+
+ public ResponseFormatManager getResponseFormatManager() {
+ return responseFormatManager;
+ }
+
+ public void setResponseFormatManager(ResponseFormatManager responseFormatManager) {
+ this.responseFormatManager = responseFormatManager;
+ }
+
+ public ResourceBusinessLogic getResourceBusinessLogic() {
+ return resourceBusinessLogic;
+ }
+
+ public void setResourceBusinessLogic(ResourceBusinessLogic resourceBusinessLogic) {
+ this.resourceBusinessLogic = resourceBusinessLogic;
+ }
+
+ public Logger getLog() {
+ return log;
+ }
+
+ public static void setLog(Logger log) {
+ ResourceImportManager.log = log;
+ }
+
+ public IGraphLockOperation getGraphLockOperation() {
+ return graphLockOperation;
+ }
+
+ public void setGraphLockOperation(IGraphLockOperation graphLockOperation) {
+ this.graphLockOperation = graphLockOperation;
+ }
+
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ public void setAuditingManager(IAuditingManager auditingManager) {
+ this.auditingManager = auditingManager;
+ }
+
+ public void setResourceOperation(ResourceOperation resourceOperation) {
+ this.resourceOperation = resourceOperation;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResponseFormatManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResponseFormatManager.java
new file mode 100644
index 0000000000..e8c0bf3d8a
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResponseFormatManager.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.ErrorConfiguration;
+import org.openecomp.sdc.be.config.ErrorInfo;
+import org.openecomp.sdc.be.config.ErrorInfo.ErrorInfoType;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.OkResponseInfo;
+import org.openecomp.sdc.exception.PolicyException;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.openecomp.sdc.exception.ServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ResponseFormatManager {
+
+ private volatile static ResponseFormatManager instance;
+ private static ConfigurationManager configurationManager;
+ private static Logger log = LoggerFactory.getLogger(ResponseFormatManager.class.getName());
+
+ public static ResponseFormatManager getInstance() {
+ if (instance == null) {
+
+ instance = init();
+ }
+ return instance;
+ }
+
+ private static synchronized ResponseFormatManager init() {
+ if (instance == null) {
+ instance = new ResponseFormatManager();
+ configurationManager = ConfigurationManager.getConfigurationManager();
+ }
+ return instance;
+ }
+
+ public ResponseFormat getResponseFormat(ActionStatus responseEnum, String... variables) {
+ ErrorConfiguration errorConfiguration = configurationManager.getErrorConfiguration();
+ ErrorInfo errorInfo = errorConfiguration.getErrorInfo(responseEnum.name());
+ if (errorInfo == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.EcompErrorNotFound, "ResponseFormatManager", responseEnum.name());
+ log.debug("failed to locate {} in error configuration", responseEnum.name());
+ errorInfo = errorConfiguration.getErrorInfo(ActionStatus.GENERAL_ERROR.name());
+ }
+ ResponseFormat errorResponseWrapper = new ResponseFormat(errorInfo.getCode());
+ String errorMessage = errorInfo.getMessage();
+ String errorMessageId = errorInfo.getMessageId();
+ ErrorInfoType errorInfoType = errorInfo.getErrorInfoType();
+ if (errorInfoType.equals(ErrorInfoType.SERVICE_EXCEPTION)) {
+ errorResponseWrapper.setServiceException(new ServiceException(errorMessageId, errorMessage, variables));
+ } else if (errorInfoType.equals(ErrorInfoType.POLICY_EXCEPTION)) {
+ errorResponseWrapper.setPolicyException(new PolicyException(errorMessageId, errorMessage, variables));
+ } else if (errorInfoType.equals(ErrorInfoType.OK)) {
+ errorResponseWrapper.setOkResponseInfo(new OkResponseInfo(errorMessageId, errorMessage, variables));
+ }
+ return errorResponseWrapper;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java
new file mode 100644
index 0000000000..6bbe88f30c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java
@@ -0,0 +1,1768 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.openecomp.sdc.be.components.distribution.engine.IDistributionEngine;
+import org.openecomp.sdc.be.components.distribution.engine.INotificationData;
+import org.openecomp.sdc.be.components.distribution.engine.VfModuleArtifactPayload;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.GroupTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.DistributionTransitionEnum;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingGenericEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionDeployEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionNotificationEvent;
+import org.openecomp.sdc.be.resources.data.auditing.ResourceAdminEvent;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.kpi.api.ASDCKpiApi;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("serviceBusinessLogic")
+public class ServiceBusinessLogic extends ComponentBusinessLogic {
+
+ private static final String STATUS_SUCCESS_200 = "200";
+
+ private static final String STATUS_DEPLOYED = "DEPLOYED";
+
+ @Autowired
+ private IElementOperation elementDao;
+
+ @Autowired
+ private IDistributionEngine distributionEngine;
+
+ // @Autowired
+ // private AuditingDao auditingDao;
+
+ @Autowired
+ private AuditCassandraDao auditCassandraDao;
+
+ @Autowired
+ private ServiceComponentInstanceBusinessLogic serviceComponentInstanceBusinessLogic;
+
+ @Autowired
+ private ICacheMangerOperation cacheManagerOperation;
+
+ private static Logger log = LoggerFactory.getLogger(ServiceBusinessLogic.class.getName());
+ private static final String INITIAL_VERSION = "0.1";
+
+ public ServiceBusinessLogic() {
+ log.debug("ServiceBusinessLogic started");
+ }
+
+ public Either<Service, ResponseFormat> changeServiceDistributionState(String serviceId, String state, LifecycleChangeInfoWithAction commentObj, User user) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(user.getUserId(), "change Service Distribution State", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ log.debug("check request state");
+ Either<DistributionTransitionEnum, ResponseFormat> validateEnum = validateTransitionEnum(state, user);
+ if (validateEnum.isRight()) {
+ return Either.right(validateEnum.right().value());
+ }
+ DistributionTransitionEnum distributionTransition = validateEnum.left().value();
+ AuditingActionEnum auditAction = (distributionTransition == DistributionTransitionEnum.APPROVE ? AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_APPROV : AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REJECT);
+ Either<String, ResponseFormat> commentResponse = validateComment(commentObj, user, auditAction);
+ if (commentResponse.isRight()) {
+ return Either.right(commentResponse.right().value());
+ }
+ String comment = commentResponse.left().value();
+
+ Either<Service, ResponseFormat> validateService = validateServiceDistributionChange(user, serviceId, auditAction, comment);
+ if (validateService.isRight()) {
+ return Either.right(validateService.right().value());
+ }
+ Service service = validateService.left().value();
+ DistributionStatusEnum initState = service.getDistributionStatus();
+
+ Either<User, ResponseFormat> validateUser = validateUserDistributionChange(user, service, auditAction, comment);
+ if (validateUser.isRight()) {
+ return Either.right(validateUser.right().value());
+ }
+ user = validateUser.left().value();
+
+ // lock resource
+ /*
+ * StorageOperationStatus lockResult = graphLockOperation.lockComponent(serviceId, NodeTypeEnum.Service); if (!lockResult.equals(StorageOperationStatus.OK)) { BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.
+ * BeFailedLockObjectError, "ChangeServiceDistributionState"); log.debug("Failed to lock service {} error - {}", serviceId, lockResult); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR,
+ * service.getVersion(), service.getServiceName());
+ *
+ * createAudit(user, auditAction, comment, service, responseFormat); return Either.right(componentsUtils.getResponseFormat(ActionStatus. GENERAL_ERROR)); }
+ */
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(serviceId, service, "ChangeServiceDistributionState");
+ if (lockResult.isRight()) {
+ ResponseFormat responseFormat = lockResult.right().value();
+ createAudit(user, auditAction, comment, service, responseFormat);
+ return Either.right(responseFormat);
+ }
+
+ try {
+
+ DistributionStatusEnum newState;
+ if (distributionTransition == DistributionTransitionEnum.APPROVE) {
+ newState = DistributionStatusEnum.DISTRIBUTION_APPROVED;
+ } else {
+ newState = DistributionStatusEnum.DISTRIBUTION_REJECTED;
+ }
+ Either<Service, StorageOperationStatus> result = serviceOperation.updateDestributionStatus(service, user, newState);
+ if (result.isRight()) {
+ titanGenericDao.rollback();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "ChangeServiceDistributionState");
+ BeEcompErrorManager.getInstance().logBeSystemError("ChangeServiceDistributionState");
+ log.debug("service {} is change destribuation status failed", service.getUniqueId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR, service.getVersion(), service.getName());
+ createAudit(user, auditAction, comment, service, responseFormat);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ titanGenericDao.commit();
+ Service updatedService = result.left().value();
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS, updatedService.getDistributionStatus().name());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS, initState.name());
+ createAudit(user, auditAction, comment, updatedService, responseFormat, auditingFields);
+ return Either.left(result.left().value());
+
+ } finally {
+ graphLockOperation.unlockComponent(serviceId, NodeTypeEnum.Service);
+ }
+
+ }
+
+ public Either<List<Map<String, Object>>, ResponseFormat> getComponentAuditRecords(String componentVersion, String componentUUID, String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Component Audit Records", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<List<Map<String, Object>>, ActionStatus> result;
+ try {
+
+ // Certified Version
+ if (componentVersion.endsWith(".0")) {
+ Either<List<ResourceAdminEvent>, ActionStatus> eitherAuditingForCertified = auditCassandraDao.getByServiceInstanceId(componentUUID);
+ if (eitherAuditingForCertified.isLeft()) {
+ result = Either.left(getAuditingFieldsList(eitherAuditingForCertified.left().value()));
+ } else {
+ result = Either.right(eitherAuditingForCertified.right().value());
+ }
+ }
+ // Uncertified Version
+ else {
+ result = getAuditRecordsForUncertifiedComponent(componentUUID, componentVersion);
+ }
+ } catch (Exception e) {
+ log.debug("get Audit Records failed with exception {}", e);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ if (result.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(result.right().value()));
+ } else {
+ return Either.left(result.left().value());
+ }
+
+ }
+
+ private Either<List<Map<String, Object>>, ActionStatus> getAuditRecordsForUncertifiedComponent(String componentUUID, String componentVersion) {
+ // First Query
+ Either<List<ResourceAdminEvent>, ActionStatus> eitherprevVerAudit = auditCassandraDao.getAuditByServiceIdAndPrevVersion(componentUUID, componentVersion);
+
+ if (eitherprevVerAudit.isRight()) {
+ return Either.right(eitherprevVerAudit.right().value());
+ }
+
+ // Second Query
+ Either<List<ResourceAdminEvent>, ActionStatus> eitherCurrVerAudit = auditCassandraDao.getAuditByServiceIdAndCurrVersion(componentUUID, componentVersion);
+ if (eitherCurrVerAudit.isRight()) {
+ return Either.right(eitherCurrVerAudit.right().value());
+ }
+
+ List<Map<String, Object>> prevVerAuditList = getAuditingFieldsList(eitherprevVerAudit.left().value());
+ List<Map<String, Object>> currVerAuditList = getAuditingFieldsList(eitherCurrVerAudit.left().value());
+
+ List<Map<String, Object>> duplicateElements = new ArrayList<Map<String, Object>>();
+ duplicateElements.addAll(prevVerAuditList);
+ duplicateElements.retainAll(currVerAuditList);
+
+ List<Map<String, Object>> joinedNonDuplicatedList = new ArrayList<Map<String, Object>>();
+ joinedNonDuplicatedList.addAll(prevVerAuditList);
+ joinedNonDuplicatedList.removeAll(duplicateElements);
+ joinedNonDuplicatedList.addAll(currVerAuditList);
+
+ return Either.left(joinedNonDuplicatedList);
+ }
+
+ private List<Map<String, Object>> getAuditingFieldsList(List<? extends AuditingGenericEvent> prevVerAuditList) {
+
+ List<Map<String, Object>> prevVerAudit = new ArrayList<Map<String, Object>>();
+ for (AuditingGenericEvent auditEvent : prevVerAuditList) {
+ auditEvent.fillFields();
+ prevVerAudit.add(auditEvent.getFields());
+ }
+ return prevVerAudit;
+ }
+
+ /**
+ * createService
+ *
+ * @param service
+ * - Service
+ * @param user
+ * - modifier data (userId)
+ * @return Either<Service, responseFormat>
+ */
+ public Either<Service, ResponseFormat> createService(Service service, User user) {
+
+ // get user details
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "Create Service", service, AuditingActionEnum.CREATE_RESOURCE, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ // validate user role
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, service, new ArrayList<Role>(), AuditingActionEnum.CREATE_RESOURCE, null);
+ if (validateRes.isRight()) {
+ return Either.right(validateRes.right().value());
+ }
+ service.setCreatorUserId(user.getUserId());
+
+ // warn on overridden fields
+ checkFieldsForOverideAttampt(service);
+ // enrich object
+ log.debug("enrich service with version and state");
+ service.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ service.setVersion(INITIAL_VERSION);
+ service.setDistributionStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+
+ Either<Service, ResponseFormat> createServiceResponse = validateServiceBeforeCreate(service, user, AuditingActionEnum.CREATE_RESOURCE);
+ if (createServiceResponse.isRight()) {
+ return createServiceResponse;
+ }
+ return createServiceByDao(service, AuditingActionEnum.CREATE_RESOURCE, serviceOperation, user);
+ }
+
+ private void checkFieldsForOverideAttampt(Service service) {
+ checkComponentFieldsForOverrideAttempt(service);
+ if ((service.getDistributionStatus() != null)) {
+ log.info("Distribution Status cannot be defined by user. This field will be overridden by the application");
+ }
+ }
+
+ private Either<Service, ResponseFormat> createServiceByDao(Service service, AuditingActionEnum actionEnum, IServiceOperation dataModel, User user) {
+ log.debug("send service {} to dao for create", service.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+
+ Either<Boolean, ResponseFormat> lockResult = lockComponentByName(service.getSystemName(), service, "Create Service");
+ if (lockResult.isRight()) {
+ ResponseFormat responseFormat = lockResult.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, service, "", "", actionEnum, ComponentTypeEnum.SERVICE);
+ return Either.right(responseFormat);
+ }
+
+ log.debug("System name locked is {}, status = {}", service.getSystemName(), lockResult);
+
+ try {
+
+ createMandatoryArtifactsData(service, user);
+ createServiceApiArtifactsData(service, user);
+ setToscaArtifactsPlaceHolders(service, user);
+
+ Either<Service, StorageOperationStatus> dataModelResponse = dataModel.createService(service);
+
+ // service created successfully!!!
+ if (dataModelResponse.isLeft()) {
+ log.debug("Service created successfully!!!");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ componentsUtils.auditComponentAdmin(responseFormat, user, service, "", "", actionEnum, ComponentTypeEnum.SERVICE);
+ ASDCKpiApi.countCreatedServicesKPI();
+
+ Service createdService = dataModelResponse.left().value();
+ // //add service to cache
+ // cacheManagerOperation.updateComponentInCache(createdService.getUniqueId(),
+ // createdService.getLastUpdateDate(), NodeTypeEnum.Service);
+
+ return Either.left(dataModelResponse.left().value());
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatByComponent(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()), service, ComponentTypeEnum.SERVICE);
+ log.debug("audit before sending response");
+ componentsUtils.auditComponentAdmin(responseFormat, user, service, "", "", actionEnum, ComponentTypeEnum.SERVICE);
+ return Either.right(responseFormat);
+
+ } finally {
+ graphLockOperation.unlockComponentByName(service.getSystemName(), service.getUniqueId(), NodeTypeEnum.Service);
+ }
+ }
+
+ private void createServiceApiArtifactsData(Service service, User user) {
+ // create mandatory artifacts
+
+ // TODO it must be removed after that artifact uniqueId creation will be
+ // moved to ArtifactOperation
+ // String serviceUniqueId =
+ // UniqueIdBuilder.buildServiceUniqueId(service.getComponentMetadataDefinition().getMetadataDataDefinition().getName(),
+ // service.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion());
+ String serviceUniqueId = service.getUniqueId();
+ Map<String, ArtifactDefinition> artifactMap = service.getArtifacts();
+ if (artifactMap == null)
+ artifactMap = new HashMap<String, ArtifactDefinition>();
+
+ Map<String, Object> serviceApiArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getServiceApiArtifacts();
+ List<String> exludeServiceCategory = ConfigurationManager.getConfigurationManager().getConfiguration().getExcludeServiceCategory();
+
+ List<CategoryDefinition> categories = service.getCategories();
+ boolean isCreateArtifact = true;
+ if (categories != null && exludeServiceCategory != null && !exludeServiceCategory.isEmpty()) {
+ for (String exlude : exludeServiceCategory) {
+ if (exlude.equalsIgnoreCase(categories.get(0).getName())) {
+ isCreateArtifact = false;
+ break;
+ }
+ }
+
+ }
+
+ if (serviceApiArtifacts != null && isCreateArtifact) {
+ Set<String> keys = serviceApiArtifacts.keySet();
+ for (String serviceApiArtifactName : keys) {
+ Map<String, Object> artifactInfoMap = (Map<String, Object>) serviceApiArtifacts.get(serviceApiArtifactName);
+ ArtifactDefinition artifactDefinition = createArtifactDefinition(serviceUniqueId, serviceApiArtifactName, artifactInfoMap, user, true);
+ artifactDefinition.setArtifactGroupType(ArtifactGroupTypeEnum.SERVICE_API);
+ artifactMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+ }
+
+ service.setArtifacts(artifactMap);
+ }
+ }
+
+ private Either<Service, ResponseFormat> validateServiceBeforeCreate(Service service, User user, AuditingActionEnum actionEnum) {
+
+ Either<Boolean, ResponseFormat> validationResponse = validateServiceFieldsBeforeCreate(user, service, actionEnum);
+ if (validationResponse.isRight()) {
+ return Either.right(validationResponse.right().value());
+ }
+ service.setCreatorFullName(user.getFirstName() + " " + user.getLastName());
+ service.setContactId(service.getContactId().toLowerCase());
+
+ // Generate invariant UUID - must be here and not in operation since it
+ // should stay constant during clone
+ String invariantUUID = UniqueIdBuilder.buildInvariantUUID();
+ service.setInvariantUUID(invariantUUID);
+
+ return Either.left(service);
+ }
+
+ private Either<Boolean, ResponseFormat> validateServiceFieldsBeforeCreate(User user, Service service, AuditingActionEnum actionEnum) {
+ Either<Boolean, ResponseFormat> componentsFieldsValidation = validateComponentFieldsBeforeCreate(user, service, actionEnum);
+ if (componentsFieldsValidation.isRight()) {
+ return componentsFieldsValidation;
+ }
+
+ // validate service name uniqueness
+ log.debug("validate service name uniqueness");
+ Either<Boolean, ResponseFormat> serviceNameUniquenessValidation = validateComponentNameUnique(user, service, actionEnum);
+ if (serviceNameUniquenessValidation.isRight()) {
+ return serviceNameUniquenessValidation;
+ }
+
+ // validate category
+ log.debug("validate category");
+ Either<Boolean, ResponseFormat> categoryValidation = validateServiceCategory(user, service, actionEnum);
+ if (categoryValidation.isRight()) {
+ return categoryValidation;
+ }
+
+ log.debug("validate projectName");
+ Either<Boolean, ResponseFormat> projectCodeValidation = validateProjectCode(user, service, actionEnum);
+ if (projectCodeValidation.isRight()) {
+ return projectCodeValidation;
+ }
+
+ return Either.left(true);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateServiceCategory(User user, Service service, AuditingActionEnum actionEnum) {
+ log.debug("validate Service category");
+
+ if (service.getCategories() == null || service.getCategories().size() == 0) {
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.SERVICE.getValue());
+ componentsUtils.auditComponentAdmin(errorResponse, user, service, "", "", actionEnum, ComponentTypeEnum.SERVICE);
+ return Either.right(errorResponse);
+ }
+
+ Either<Boolean, ResponseFormat> validatCategory = validateServiceCategory(service.getCategories());
+ if (validatCategory.isRight()) {
+ ResponseFormat responseFormat = validatCategory.right().value();
+ componentsUtils.auditComponentAdmin(responseFormat, user, service, "", "", actionEnum, ComponentTypeEnum.SERVICE);
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(true);
+ }
+
+ public Either<Map<String, Boolean>, ResponseFormat> validateServiceNameExists(String serviceName, String userId) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "validate Service Name Exists", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ Either<Boolean, StorageOperationStatus> dataModelResponse = serviceOperation.validateServiceNameExists(serviceName);
+
+ if (dataModelResponse.isLeft()) {
+ Map<String, Boolean> result = new HashMap<>();
+ result.put("isValid", dataModelResponse.left().value());
+ log.debug("validation was successfully performed.");
+ return Either.left(result);
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(dataModelResponse.right().value()));
+
+ return Either.right(responseFormat);
+ }
+
+ public void setElementDao(IElementOperation elementDao) {
+ this.elementDao = elementDao;
+ }
+
+ public void setServiceOperation(ServiceOperation serviceOperation) {
+ this.serviceOperation = serviceOperation;
+ }
+
+ public void setCassandraAuditingDao(AuditCassandraDao auditingDao) {
+ this.auditCassandraDao = auditingDao;
+ }
+
+ /*
+ * public void setUserAdmin(UserAdminBuisinessLogic userAdmin) { this.userAdmin = userAdmin; }
+ *
+ * public void setComponentsUtils(ComponentsUtils componentsUtils) { this.componentsUtils = componentsUtils; }
+ *
+ * public void setGraphLockOperation(IGraphLockOperation graphLockOperation) { this.graphLockOperation = graphLockOperation; }
+ */
+
+ public ArtifactsBusinessLogic getArtifactBl() {
+ return artifactsBusinessLogic;
+ }
+
+ public void setArtifactBl(ArtifactsBusinessLogic artifactBl) {
+ this.artifactsBusinessLogic = artifactBl;
+ }
+
+ public Either<Service, ResponseFormat> updateServiceMetadata(String serviceId, Service serviceUpdate, User user) {
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "updateServiceMetadata", serviceUpdate, null, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ // validate user role
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, serviceUpdate, new ArrayList<Role>(), null, null);
+ if (validateRes.isRight()) {
+ return Either.right(validateRes.right().value());
+ }
+
+ Either<Service, StorageOperationStatus> storageStatus = serviceOperation.getService(serviceId);
+ if (storageStatus.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), ComponentTypeEnum.SERVICE), ""));
+ }
+
+ Service currentService = storageStatus.left().value();
+
+ if (!ComponentValidationUtils.canWorkOnService(currentService.getUniqueId(), serviceOperation, user.getUserId())) {
+ log.info("Restricted operation for user {} on service {}", user.getUserId(), currentService.getCreatorUserId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
+ }
+
+ Either<Service, ResponseFormat> validationRsponse = validateAndUpdateServiceMetadata(user, currentService, serviceUpdate);
+ if (validationRsponse.isRight()) {
+ log.info("service update metadata: validations field.");
+ return validationRsponse;
+ }
+ Service serviceToUpdate = validationRsponse.left().value();
+ // lock resource
+
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(serviceId, currentService, "Update Service Metadata");
+ if (lockResult.isRight()) {
+ return Either.right(lockResult.right().value());
+ }
+ try {
+ Either<Service, StorageOperationStatus> updateResponse = serviceOperation.updateService(serviceToUpdate, true);
+ if (updateResponse.isRight()) {
+ titanGenericDao.rollback();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Update Service Metadata");
+ BeEcompErrorManager.getInstance().logBeSystemError("Update Service Metadata");
+ log.debug("failed to update sevice {}", serviceToUpdate.getUniqueId());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ titanGenericDao.commit();
+ return Either.left(updateResponse.left().value());
+ } finally {
+ graphLockOperation.unlockComponent(serviceId, NodeTypeEnum.Service);
+ }
+ }
+
+ private Either<Service, ResponseFormat> validateAndUpdateServiceMetadata(User user, Service currentService, Service serviceUpdate) {
+
+ boolean hasBeenCertified = ValidationUtils.hasBeenCertified(currentService.getVersion());
+ Either<Boolean, ResponseFormat> response = validateAndUpdateCategory(user, currentService, serviceUpdate, hasBeenCertified, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ String creatorUserIdUpdated = serviceUpdate.getCreatorUserId();
+ String creatorUserIdCurrent = currentService.getCreatorUserId();
+ if (creatorUserIdUpdated != null && !creatorUserIdCurrent.equals(creatorUserIdUpdated)) {
+ log.info("update srvice: recived request to update creatorUserId to {} the field is not updatable ignoring.", creatorUserIdUpdated);
+ }
+
+ String creatorFullNameUpdated = serviceUpdate.getCreatorFullName();
+ String creatorFullNameCurrent = currentService.getCreatorFullName();
+ if (creatorFullNameUpdated != null && !creatorFullNameCurrent.equals(creatorFullNameUpdated)) {
+ log.info("update srvice: recived request to update creatorFullName to {} the field is not updatable ignoring.", creatorFullNameUpdated);
+ }
+
+ String lastUpdaterUserIdUpdated = serviceUpdate.getLastUpdaterUserId();
+ String lastUpdaterUserIdCurrent = currentService.getLastUpdaterUserId();
+ if (lastUpdaterUserIdUpdated != null && !lastUpdaterUserIdCurrent.equals(lastUpdaterUserIdUpdated)) {
+ log.info("update srvice: recived request to update lastUpdaterUserId to {} the field is not updatable ignoring.", lastUpdaterUserIdUpdated);
+ }
+
+ String lastUpdaterFullNameUpdated = serviceUpdate.getLastUpdaterFullName();
+ String lastUpdaterFullNameCurrent = currentService.getLastUpdaterFullName();
+ if (lastUpdaterFullNameUpdated != null && !lastUpdaterFullNameCurrent.equals(lastUpdaterFullNameUpdated)) {
+ log.info("update srvice: recived request to update lastUpdaterFullName to {} the field is not updatable ignoring.", lastUpdaterFullNameUpdated );
+ }
+
+ response = validateAndUpdateServiceName(user, currentService, serviceUpdate, hasBeenCertified, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ DistributionStatusEnum distributionStatusUpdated = serviceUpdate.getDistributionStatus();
+ DistributionStatusEnum distributionStatusCurrent = currentService.getDistributionStatus();
+ if (distributionStatusUpdated != null && !distributionStatusUpdated.name().equals((distributionStatusCurrent != null ? distributionStatusCurrent.name() : null))) {
+ log.info("update srvice: recived request to update distributionStatus to {} the field is not updatable ignoring.", distributionStatusUpdated);
+ }
+
+ if (serviceUpdate.getProjectCode() != null) {
+ response = validateAndUpdateProjectCode(user, currentService, serviceUpdate, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+ }
+
+ response = validateAndUpdateIcon(user, currentService, serviceUpdate, hasBeenCertified, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ Long creationDateUpdated = serviceUpdate.getCreationDate();
+ Long creationDateCurrent = currentService.getCreationDate();
+ if (creationDateUpdated != null && !creationDateCurrent.equals(creationDateUpdated)) {
+ log.info("update srvice: recived request to update creationDate to {} the field is not updatable ignoring.", creationDateUpdated);
+ }
+
+ String versionUpdated = serviceUpdate.getVersion();
+ String versionCurrent = currentService.getVersion();
+ if (versionUpdated != null && !versionCurrent.equals(versionUpdated)) {
+ log.info("update srvice: recived request to update version to {} the field is not updatable ignoring.", versionUpdated);
+ }
+
+ response = validateAndUpdateDescription(user, currentService, serviceUpdate, hasBeenCertified, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateTags(user, currentService, serviceUpdate, hasBeenCertified, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ response = validateAndUpdateContactId(user, currentService, serviceUpdate, null);
+ if (response.isRight()) {
+ ResponseFormat errorResponse = response.right().value();
+ return Either.right(errorResponse);
+ }
+
+ Long lastUpdateDateUpdated = serviceUpdate.getLastUpdateDate();
+ Long lastUpdateDateCurrent = currentService.getLastUpdateDate();
+ if (lastUpdateDateUpdated != null && !lastUpdateDateCurrent.equals(lastUpdateDateUpdated)) {
+ log.info("update srvice: recived request to update lastUpdateDate to {} the field is not updatable ignoring.", lastUpdateDateUpdated);
+ }
+
+ LifecycleStateEnum lifecycleStateUpdated = serviceUpdate.getLifecycleState();
+ LifecycleStateEnum lifecycleStateCurrent = currentService.getLifecycleState();
+ if (lifecycleStateUpdated != null && !lifecycleStateCurrent.name().equals(lifecycleStateUpdated.name())) {
+ log.info("update srvice: recived request to update lifecycleState to {} the field is not updatable ignoring.", lifecycleStateUpdated);
+ }
+
+ Boolean isHighestVersionUpdated = serviceUpdate.isHighestVersion();
+ Boolean isHighestVersionCurrent = currentService.isHighestVersion();
+ if (isHighestVersionUpdated != null && !isHighestVersionCurrent.equals(isHighestVersionUpdated)) {
+ log.info("update srvice: recived request to update isHighestVersion to {} the field is not updatable ignoring.", isHighestVersionUpdated);
+ }
+
+ String uuidUpdated = serviceUpdate.getUUID();
+ String uuidCurrent = currentService.getUUID();
+ if (!uuidCurrent.equals(uuidUpdated)) {
+ log.info("update srvice: recived request to update uuid to {} the field is not updatable ignoring.", uuidUpdated);
+ }
+
+ String currentInvariantUuid = currentService.getInvariantUUID();
+ String updatedInvariantUuid = serviceUpdate.getInvariantUUID();
+
+ if ((updatedInvariantUuid != null) && (!updatedInvariantUuid.equals(currentInvariantUuid))) {
+ log.warn("Product invariant UUID is automatically set and cannot be updated");
+ serviceUpdate.setInvariantUUID(currentInvariantUuid);
+ }
+ return Either.left(currentService);
+
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateContactId(User user, Service currentService, Service serviceUpdate, AuditingActionEnum audatingAction) {
+ String contactIdUpdated = serviceUpdate.getContactId();
+ String contactIdCurrent = currentService.getContactId();
+ if (!contactIdCurrent.equals(contactIdUpdated)) {
+ Either<Boolean, ResponseFormat> validatContactId = validateContactId(user, serviceUpdate, audatingAction);
+ if (validatContactId.isRight()) {
+ ResponseFormat errorRespons = validatContactId.right().value();
+ return Either.right(errorRespons);
+ }
+ currentService.setContactId(contactIdUpdated.toLowerCase());
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateTags(User user, Service currentService, Service serviceUpdate, boolean hasBeenCertified, AuditingActionEnum audatingAction) {
+ List<String> tagsUpdated = serviceUpdate.getTags();
+ List<String> tagsCurrent = currentService.getTags();
+ if (tagsUpdated == null || tagsUpdated.isEmpty()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_TAGS);
+ componentsUtils.auditComponentAdmin(responseFormat, user, serviceUpdate, "", "", audatingAction, ComponentTypeEnum.SERVICE);
+ return Either.right(responseFormat);
+ }
+
+ if (!(tagsCurrent.containsAll(tagsUpdated) && tagsUpdated.containsAll(tagsCurrent))) {
+ Either<Boolean, ResponseFormat> validatResponse = validateTagsListAndRemoveDuplicates(user, serviceUpdate, audatingAction);
+ if (validatResponse.isRight()) {
+ ResponseFormat errorRespons = validatResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentService.setTags(tagsUpdated);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateDescription(User user, Service currentService, Service serviceUpdate, boolean hasBeenCertified, AuditingActionEnum audatingAction) {
+ String descriptionUpdated = serviceUpdate.getDescription();
+ String descriptionCurrent = currentService.getDescription();
+ if (!descriptionCurrent.equals(descriptionUpdated)) {
+ Either<Boolean, ResponseFormat> validateDescriptionResponse = validateDescriptionAndCleanup(user, serviceUpdate, audatingAction);
+ if (validateDescriptionResponse.isRight()) {
+ ResponseFormat errorRespons = validateDescriptionResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentService.setDescription(serviceUpdate.getDescription());
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateProjectCode(User user, Service currentService, Service serviceUpdate, AuditingActionEnum audatingAction) {
+ String projectCodeUpdated = serviceUpdate.getProjectCode();
+ String projectCodeCurrent = currentService.getProjectCode();
+ if (!projectCodeCurrent.equals(projectCodeUpdated)) {
+ Either<Boolean, ResponseFormat> validatProjectCodeResponse = validateProjectCode(user, serviceUpdate, audatingAction);
+ if (validatProjectCodeResponse.isRight()) {
+ ResponseFormat errorRespons = validatProjectCodeResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentService.setProjectCode(projectCodeUpdated);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateIcon(User user, Service currentService, Service serviceUpdate, boolean hasBeenCertified, AuditingActionEnum audatingAction) {
+ String iconUpdated = serviceUpdate.getIcon();
+ String iconCurrent = currentService.getIcon();
+ if (!iconCurrent.equals(iconUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validatIconResponse = validateIcon(user, serviceUpdate, audatingAction);
+ if (validatIconResponse.isRight()) {
+ ResponseFormat errorRespons = validatIconResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ currentService.setIcon(iconUpdated);
+ } else {
+ log.info("icon {} cannot be updated once the service has been certified once.", iconUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.SERVICE_ICON_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateServiceName(User user, Service currentService, Service serviceUpdate, boolean hasBeenCertified, AuditingActionEnum audatingAction) {
+ String serviceNameUpdated = serviceUpdate.getName();
+ String serviceNameCurrent = currentService.getName();
+ if (!serviceNameCurrent.equals(serviceNameUpdated)) {
+ if (!hasBeenCertified) {
+ Either<Boolean, ResponseFormat> validatServiceNameResponse = validateComponentName(user, serviceUpdate, audatingAction);
+ if (validatServiceNameResponse.isRight()) {
+ ResponseFormat errorRespons = validatServiceNameResponse.right().value();
+ return Either.right(errorRespons);
+ }
+
+ Either<Boolean, ResponseFormat> serviceNameUniquenessValidation = validateComponentNameUnique(user, serviceUpdate, audatingAction);
+ if (serviceNameUniquenessValidation.isRight()) {
+ return serviceNameUniquenessValidation;
+ }
+ currentService.setName(serviceNameUpdated);
+ currentService.getComponentMetadataDefinition().getMetadataDataDefinition().setNormalizedName(ValidationUtils.normaliseComponentName(serviceNameUpdated));
+ currentService.getComponentMetadataDefinition().getMetadataDataDefinition().setSystemName(ValidationUtils.convertToSystemName(serviceNameUpdated));
+
+ } else {
+ log.info("service name {} cannot be updated once the service has been certified once.", serviceNameUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.SERVICE_NAME_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateAndUpdateCategory(User user, Service currentService, Service serviceUpdate, boolean hasBeenCertified, AuditingActionEnum audatingAction) {
+ List<CategoryDefinition> categoryUpdated = serviceUpdate.getCategories();
+ List<CategoryDefinition> categoryCurrent = currentService.getCategories();
+ Either<Boolean, ResponseFormat> validatCategoryResponse = validateServiceCategory(user, serviceUpdate, audatingAction);
+ if (validatCategoryResponse.isRight()) {
+ ResponseFormat errorRespons = validatCategoryResponse.right().value();
+ return Either.right(errorRespons);
+ }
+ if (!categoryCurrent.get(0).getName().equals(categoryUpdated.get(0).getName())) {
+ if (!hasBeenCertified) {
+ currentService.setCategories(categoryUpdated);
+ } else {
+ log.info("category {} cannot be updated once the service has been certified once.", categoryUpdated);
+ ResponseFormat errorResponse = componentsUtils.getResponseFormat(ActionStatus.SERVICE_CATEGORY_CANNOT_BE_CHANGED);
+ return Either.right(errorResponse);
+ }
+ }
+ return Either.left(true);
+
+ }
+
+ public Either<Boolean, ResponseFormat> validateServiceCategory(List<CategoryDefinition> list) {
+ if (list != null) {
+ if (list.size() > 1) {
+ log.debug("Must be only one category for service");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_TOO_MUCH_CATEGORIES, ComponentTypeEnum.SERVICE.getValue());
+ return Either.right(responseFormat);
+ }
+ CategoryDefinition category = list.get(0);
+ if (category.getSubcategories() != null) {
+ log.debug("Subcategories cannot be defined for service");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.SERVICE_CANNOT_CONTAIN_SUBCATEGORY);
+ return Either.right(responseFormat);
+ }
+ if (!ValidationUtils.validateStringNotEmpty(category.getName())) {
+ log.debug("Resource category is empty");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.SERVICE.getValue());
+ return Either.right(responseFormat);
+ }
+
+ log.debug("validating service category {} against valid categories list", list);
+ Either<List<CategoryDefinition>, ActionStatus> categorys = elementDao.getAllServiceCategories();
+ if (categorys.isRight()) {
+ log.debug("failed to retrive service categories from Titan");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(categorys.right().value());
+ return Either.right(responseFormat);
+ }
+ List<CategoryDefinition> categoryList = categorys.left().value();
+ for (CategoryDefinition value : categoryList) {
+ if (value.getName().equals(category.getName())) {
+ return Either.left(true);
+ }
+ }
+ log.debug("Category {} is not part of service category group. Service category valid values are {}", list, categoryList);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.SERVICE.getValue()));
+ }
+ return Either.left(false);
+ }
+
+ public ResponseFormat deleteService(String serviceId, User user) {
+ ResponseFormat responseFormat;
+ String ecompErrorContext = "delete service";
+
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, ecompErrorContext, false);
+ if (eitherCreator.isRight()) {
+ return eitherCreator.right().value();
+ }
+ user = eitherCreator.left().value();
+
+ Either<Service, StorageOperationStatus> serviceStatus = serviceOperation.getService(serviceId);
+ if (serviceStatus.isRight()) {
+ log.debug("failed to get service {}", serviceId);
+ return componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(serviceStatus.right().value()), "");
+ }
+
+ Service service = serviceStatus.left().value();
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(service, "Mark service to delete");
+ if (lockResult.isRight()) {
+ result = StorageOperationStatus.GENERAL_ERROR;
+ return componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ }
+
+ try {
+
+ result = markComponentToDelete(service);
+ if (result.equals(StorageOperationStatus.OK)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.NO_CONTENT);
+ } else {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(result);
+ responseFormat = componentsUtils.getResponseFormatByResource(actionStatus, service.getName());
+ }
+ return responseFormat;
+
+ } finally {
+ if (result == null || !result.equals(StorageOperationStatus.OK)) {
+ log.warn("operation failed. do rollback");
+ BeEcompErrorManager.getInstance().logBeSystemError("Delete Service");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ graphLockOperation.unlockComponent(serviceId, NodeTypeEnum.Service);
+ }
+ }
+
+ public ResponseFormat deleteServiceByNameAndVersion(String serviceName, String version, User user) {
+ ResponseFormat responseFormat;
+ String ecompErrorContext = "delete service";
+ Either<User, ResponseFormat> validateEmptyResult = validateUserNotEmpty(user, ecompErrorContext);
+ if (validateEmptyResult.isRight()) {
+ return validateEmptyResult.right().value();
+ }
+
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, ecompErrorContext, false);
+ if (eitherCreator.isRight()) {
+ return eitherCreator.right().value();
+ }
+ user = eitherCreator.left().value();
+
+ Either<Service, ResponseFormat> getResult = getServiceByNameAndVersion(serviceName, version, user.getUserId());
+ if (getResult.isRight()) {
+ return getResult.right().value();
+ }
+ Service service = getResult.left().value();
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(service, "Mark service to delete");
+ if (lockResult.isRight()) {
+ result = StorageOperationStatus.GENERAL_ERROR;
+ return componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ }
+
+ try {
+ result = markComponentToDelete(service);
+ if (result.equals(StorageOperationStatus.OK)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.NO_CONTENT);
+ } else {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(result);
+ responseFormat = componentsUtils.getResponseFormatByResource(actionStatus, service.getName());
+ }
+ return responseFormat;
+
+ } finally {
+ if (result == null || !result.equals(StorageOperationStatus.OK)) {
+ log.warn("operation failed. do rollback");
+ BeEcompErrorManager.getInstance().logBeSystemError("Delete Service");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("operation success. do commit");
+ titanGenericDao.commit();
+ }
+ graphLockOperation.unlockComponent(service.getUniqueId(), NodeTypeEnum.Service);
+ }
+ }
+
+ public Either<Service, ResponseFormat> getService(String serviceId, User user) {
+ String ecompErrorContext = "Get service";
+ Either<User, ResponseFormat> validateEmptyResult = validateUserNotEmpty(user, ecompErrorContext);
+ if (validateEmptyResult.isRight()) {
+ return Either.right(validateEmptyResult.right().value());
+ }
+
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(user, ecompErrorContext, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ Either<Service, StorageOperationStatus> storageStatus = serviceOperation.getService(serviceId);
+ if (storageStatus.isRight()) {
+ log.debug("failed to get service by id {}", serviceId);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), ComponentTypeEnum.SERVICE), serviceId));
+ }
+ // Service service =
+ // createServiceApiArtifactLIst(storageStatus.left().value());
+ Service service = storageStatus.left().value();
+ return Either.left(service);
+ }
+
+ public Either<Service, ResponseFormat> getServiceByNameAndVersion(String serviceName, String serviceVersion, String userId) {
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "get Service By Name And Version", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+ Either<Service, StorageOperationStatus> storageStatus = serviceOperation.getServiceByNameAndVersion(serviceName, serviceVersion, null, false);
+ if (storageStatus.isRight()) {
+ log.debug("failed to get service by name {} and version {}", serviceName, serviceVersion);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(storageStatus.right().value(), ComponentTypeEnum.SERVICE), serviceName));
+ }
+ Service service = storageStatus.left().value();
+ return Either.left(service);
+ }
+
+ private void createMandatoryArtifactsData(Service service, User user) {
+ // create mandatory artifacts
+
+ // TODO it must be removed after that artifact uniqueId creation will be
+ // moved to ArtifactOperation
+ // String serviceUniqueId =
+ // UniqueIdBuilder.buildServiceUniqueId(service.getComponentMetadataDefinition().getMetadataDataDefinition().getName(),
+ // service.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion());
+ String serviceUniqueId = service.getUniqueId();
+ Map<String, ArtifactDefinition> artifactMap = service.getArtifacts();
+ if (artifactMap == null)
+ artifactMap = new HashMap<String, ArtifactDefinition>();
+
+ Map<String, Object> informationalServiceArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getInformationalServiceArtifacts();
+ List<String> exludeServiceCategory = ConfigurationManager.getConfigurationManager().getConfiguration().getExcludeServiceCategory();
+
+ String category = service.getCategories().get(0).getName();
+ boolean isCreateArtifact = true;
+ if (category != null && exludeServiceCategory != null && !exludeServiceCategory.isEmpty()) {
+ for (String exlude : exludeServiceCategory) {
+ if (exlude.equalsIgnoreCase(category)) {
+ isCreateArtifact = false;
+ break;
+ }
+ }
+
+ }
+
+ if (informationalServiceArtifacts != null && isCreateArtifact) {
+ Set<String> keys = informationalServiceArtifacts.keySet();
+ for (String informationalServiceArtifactName : keys) {
+ Map<String, Object> artifactInfoMap = (Map<String, Object>) informationalServiceArtifacts.get(informationalServiceArtifactName);
+ ArtifactDefinition artifactDefinition = createArtifactDefinition(serviceUniqueId, informationalServiceArtifactName, artifactInfoMap, user, false);
+ artifactMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+
+ }
+
+ service.setArtifacts(artifactMap);
+ }
+ }
+
+ private ArtifactDefinition createArtifactDefinition(String serviceId, String logicalName, Map<String, Object> artifactInfoMap, User user, Boolean isServiceApi) {
+
+ ArtifactDefinition artifactInfo = artifactsBusinessLogic.createArtifactPlaceHolderInfo(serviceId, logicalName, artifactInfoMap, user, ArtifactGroupTypeEnum.INFORMATIONAL);
+
+ if (isServiceApi) {
+ artifactInfo.setMandatory(false);
+ artifactInfo.setServiceApi(true);
+ }
+ return artifactInfo;
+ }
+
+ private Either<DistributionTransitionEnum, ResponseFormat> validateTransitionEnum(String distributionTransition, User user) {
+ DistributionTransitionEnum transitionEnum = null;
+
+ transitionEnum = DistributionTransitionEnum.getFromDisplayName(distributionTransition);
+ if (transitionEnum == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Change Service Distribution");
+ BeEcompErrorManager.getInstance().logBeSystemError("Change Service Distribution");
+ log.info("state operation is not valid. operations allowed are: {}", DistributionTransitionEnum.valuesAsString());
+ ResponseFormat error = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return Either.right(error);
+ }
+
+ return Either.left(transitionEnum);
+ }
+
+ private Either<String, ResponseFormat> validateComment(LifecycleChangeInfoWithAction comment, User user, AuditingActionEnum auditAction) {
+ String data = comment.getUserRemarks();
+
+ if (data == null || data.trim().isEmpty()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "Change Service Distribution");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("Change Service Distribution");
+ log.debug("user comment cannot be empty or null.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ data = ValidationUtils.removeNoneUtf8Chars(data);
+ data = ValidationUtils.removeHtmlTags(data);
+ data = ValidationUtils.normaliseWhitespace(data);
+ data = ValidationUtils.stripOctets(data);
+
+ if (!ValidationUtils.validateLength(data, ValidationUtils.COMMENT_MAX_LENGTH)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "Change Service Distribution");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("Change Service Distribution");
+ log.debug("user comment exceeds limit.");
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, "comment", String.valueOf(ValidationUtils.COMMENT_MAX_LENGTH)));
+ }
+ if (!ValidationUtils.validateIsEnglish(data)) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ return Either.left(data);
+ }
+
+ private Either<Service, ResponseFormat> validateServiceDistributionChange(User user, String serviceId, AuditingActionEnum auditAction, String comment) {
+ Either<Service, StorageOperationStatus> storageStatus = serviceOperation.getService(serviceId);
+ if (storageStatus.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.SERVICE_NOT_FOUND, serviceId);
+ createAudit(user, auditAction, comment, responseFormat);
+ return Either.right(responseFormat);
+ }
+ Service service = storageStatus.left().value();
+
+ if (service.getLifecycleState() != LifecycleStateEnum.CERTIFIED) {
+ log.info("service {} is not available for distribution. Should be in certified state", service.getUniqueId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, service.getVersion(), service.getName());
+ createAudit(user, auditAction, comment, service, responseFormat);
+ return Either.right(responseFormat);
+ }
+ return Either.left(service);
+ }
+
+ private Either<User, ResponseFormat> validateUserDistributionChange(User user, Service service, AuditingActionEnum auditAction, String comment) {
+ log.debug("get user from DB");
+ /*
+ * Either<User, ActionStatus> eitherCreator = userAdmin.getUser(user.getUserId());s if (eitherCreator.isRight() || eitherCreator.left().value() == null) { BeEcompErrorManager.getInstance().processEcompError(EcompErrorName. BeUserMissingError,
+ * "Activate Distribution", user.getUserId()); log. debug("changeServiceDistributionState method - user is not listed. userId=" + user.getUserId()); ResponseFormat responseFormat =
+ * componentsUtils.getResponseFormat(ActionStatus.USER_NOT_FOUND); createAudit(user, auditAction, comment, responseFormat); return Either.right(responseFormat); } user = eitherCreator.left().value(); log.debug("validate user role"); if
+ * (!validateUserTemp(user, Role.ADMIN, Role.GOVERNOR)) { log.info("role {} is not allowed to perform this action", user.getRole()); ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ * createAudit(user, auditAction, comment, service, responseFormat); return Either.right(responseFormat); }
+ */
+ // get user details
+ Either<User, ResponseFormat> eitherCreator = validateUser(user, "Activate Distribution", service, auditAction, false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+ user = eitherCreator.left().value();
+
+ // validate user role
+ List<Role> roles = new ArrayList<>();
+ roles.add(Role.ADMIN);
+ roles.add(Role.GOVERNOR);
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, service, roles, auditAction, comment);
+ if (validateRes.isRight()) {
+ return Either.right(validateRes.right().value());
+ }
+ return Either.left(user);
+ }
+
+ private void createAudit(User user, AuditingActionEnum auditAction, String comment, ResponseFormat responseFormat) {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+
+ createAudit(user, auditAction, comment, null, responseFormat, auditingFields);
+ }
+
+ private void createAudit(User user, AuditingActionEnum auditAction, String comment, Service component, ResponseFormat responseFormat) {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS, component.getDistributionStatus().name());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS, component.getDistributionStatus().name());
+ createAudit(user, auditAction, comment, component, component.getLifecycleState().name(), component.getVersion(), responseFormat, auditingFields);
+ }
+
+ private void createAudit(User user, AuditingActionEnum auditAction, String comment, Service component, ResponseFormat responseFormat, EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ log.debug("audit before sending response");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, comment);
+ componentsUtils.auditComponent(responseFormat, user, component, null, null, auditAction, ComponentTypeEnum.SERVICE, auditingFields);
+ }
+
+ private void createAudit(User user, AuditingActionEnum auditAction, String comment, Service component, String prevState, String prevVersion, ResponseFormat responseFormat, EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ log.debug("audit before sending response");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, comment);
+ componentsUtils.auditComponent(responseFormat, user, component, prevState, prevVersion, auditAction, ComponentTypeEnum.SERVICE, auditingFields);
+ }
+
+ public Either<Service, ResponseFormat> activateDistribution(String serviceId, String envName, User modifier, HttpServletRequest request) {
+
+ Either<User, ResponseFormat> eitherCreator = validateUserExists(modifier.getUserId(), "activate Distribution", false);
+ if (eitherCreator.isRight()) {
+ return Either.right(eitherCreator.right().value());
+ }
+
+ User user = eitherCreator.left().value();
+
+ Either<Service, ResponseFormat> result = null;
+ ResponseFormat response = null;
+ Service updatedService = null;
+ String did = ThreadLocalsHolder.getUuid();
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, did);
+ // DE194021
+ String configuredEnvName = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration().getEnvironments().get(0);
+ if (configuredEnvName != null && false == envName.equals(configuredEnvName)) {
+ log.trace("Update environment name to be {} instead of {}", configuredEnvName, envName);
+ envName = configuredEnvName;
+ }
+ // DE194021
+
+ ServletContext servletContext = request.getSession().getServletContext();
+ boolean isDistributionEngineUp = getHealthCheckBL(servletContext).isDistributionEngineUp(request.getSession().getServletContext()); // DE
+ if (!isDistributionEngineUp) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Distribution Engine is DOWN");
+ BeEcompErrorManager.getInstance().logBeSystemError("Distribution Engine is DOWN");
+ log.debug("Distribution Engine is DOWN");
+ response = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return Either.right(response);
+ }
+
+ Either<Service, StorageOperationStatus> serviceRes = serviceOperation.getService(serviceId);
+ if (serviceRes.isRight()) {
+ log.debug("failed retrieving service");
+ response = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(serviceRes.right().value(), ComponentTypeEnum.SERVICE), serviceId);
+ componentsUtils.auditComponent(response, user, null, null, null, AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST, ComponentTypeEnum.SERVICE, auditingFields);
+ return Either.right(response);
+ }
+ Service service = serviceRes.left().value();
+ String dcurrStatus = service.getDistributionStatus().name();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS, dcurrStatus);
+
+ Either<INotificationData, StorageOperationStatus> readyForDistribution = distributionEngine.isReadyForDistribution(service, did, envName);
+ if (readyForDistribution.isLeft()) {
+ INotificationData notificationData = readyForDistribution.left().value();
+ StorageOperationStatus notifyServiceResponse = distributionEngine.notifyService(did, service, notificationData, envName, user.getUserId(), user.getFullName());
+ if (notifyServiceResponse == StorageOperationStatus.OK) {
+ Either<Service, ResponseFormat> updateStateRes = updateDistributionStatusForActivation(service, user, DistributionStatusEnum.DISTRIBUTED);
+ if (updateStateRes.isLeft() && updateStateRes.left().value() != null) {
+ updatedService = updateStateRes.left().value();
+ dcurrStatus = updatedService.getDistributionStatus().name();
+ } else {
+ // The response is not relevant
+ updatedService = service;
+ }
+ ASDCKpiApi.countActivatedDistribution();
+ response = componentsUtils.getResponseFormat(ActionStatus.OK);
+ result = Either.left(updatedService);
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Activate Distribution - send notification");
+ BeEcompErrorManager.getInstance().logBeSystemError("Activate Distribution - send notification");
+ log.debug("distributionEngine.notifyService response is: {}", notifyServiceResponse);
+ response = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ result = Either.right(response);
+ }
+ } else {
+ StorageOperationStatus distEngineValidationResponse = readyForDistribution.right().value();
+ response = componentsUtils.getResponseFormatByDE(componentsUtils.convertFromStorageResponse(distEngineValidationResponse), service.getName(), envName);
+ result = Either.right(response);
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS, dcurrStatus);
+ componentsUtils.auditComponent(response, user, service, null, null, AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST, ComponentTypeEnum.SERVICE, auditingFields);
+ return result;
+ }
+
+ // convert to private after deletion of temp url
+ public Either<Service, ResponseFormat> updateDistributionStatusForActivation(Service service, User user, DistributionStatusEnum state) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(user.getUserId(), "update Distribution Status For Activation", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ String serviceId = service.getUniqueId();
+ Either<Boolean, ResponseFormat> lockResult = lockComponent(serviceId, service, "updateDistributionStatusForActivation");
+ if (lockResult.isRight()) {
+ return Either.right(lockResult.right().value());
+ }
+ try {
+ Either<Service, StorageOperationStatus> result = serviceOperation.updateDestributionStatus(service, user, state);
+ if (result.isRight()) {
+ titanGenericDao.rollback();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "updateDistributionStatusForActivation");
+ BeEcompErrorManager.getInstance().logBeSystemError("updateDistributionStatusForActivation");
+ log.debug("service {} change distribution status failed", serviceId);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ titanGenericDao.commit();
+ return Either.left(result.left().value());
+ } finally {
+ graphLockOperation.unlockComponent(serviceId, NodeTypeEnum.Service);
+ }
+ }
+
+ public Either<Service, ResponseFormat> markDistributionAsDeployed(String serviceId, String did, User user) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(user.getUserId(), "mark Distribution As Deployed", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ log.debug("mark distribution deployed");
+
+ AuditingActionEnum auditAction = AuditingActionEnum.DISTRIBUTION_DEPLOY;
+ Either<Service, StorageOperationStatus> getServiceResponse = serviceOperation.getService(serviceId);
+ if (getServiceResponse.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeServiceMissingError, "markDistributionAsDeployed", serviceId);
+ BeEcompErrorManager.getInstance().logBeComponentMissingError("markDistributionAsDeployed", ComponentTypeEnum.SERVICE.getValue(), serviceId);
+ log.debug("service {} not found", serviceId);
+ ResponseFormat responseFormat = auditDeployError(did, user, auditAction, null, componentsUtils.convertFromStorageResponse(getServiceResponse.right().value(), ComponentTypeEnum.SERVICE), "");
+
+ return Either.right(responseFormat);
+ }
+
+ Service service = getServiceResponse.left().value();
+
+ Either<User, ResponseFormat> validateRoleForDeploy = validateRoleForDeploy(did, user, auditAction, service);
+ if (validateRoleForDeploy.isRight()) {
+ return Either.right(validateRoleForDeploy.right().value());
+ }
+ user = validateRoleForDeploy.left().value();
+
+ return checkDistributionAndDeploy(did, user, auditAction, service);
+
+ }
+
+ public Either<Service, ResponseFormat> generateVfModuleArtifacts(Service service, User modifier, boolean shouldLock) {
+ Function<ComponentInstance, List<ArtifactGenerator<ArtifactDefinition>>> artifactTaskGeneratorCreator = ri ->
+ // Only one VF Module Artifact per instance - add it to a list of one
+ Arrays.asList(new VfModuleArtifacGenerator(modifier, ri, service, shouldLock));
+
+ return generateDeploymentArtifacts(service, modifier, artifactTaskGeneratorCreator);
+
+ }
+
+ private List<GroupDefinition> collectGroupsForCompInstance(ComponentInstance currVF, Wrapper<ResponseFormat> responseWrapper) {
+ List<GroupDefinition> relevantGroups = new ArrayList<>();
+ Either<List<GroupDefinition>, StorageOperationStatus> eitherGroups = groupOperation.getAllGroups(currVF.getComponentUid(), NodeTypeEnum.Resource);
+
+ if (eitherGroups.isRight()) {
+ final StorageOperationStatus storageStatus = eitherGroups.right().value();
+ if (storageStatus != StorageOperationStatus.NOT_FOUND && storageStatus != StorageOperationStatus.OK) {
+ ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(storageStatus);
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(actionStatus));
+ }
+
+ } else {
+ relevantGroups = eitherGroups.left().value().stream().filter(p -> GroupTypeEnum.VF_MODULE.getGroupTypeName().equals(p.getType())).collect(Collectors.toList());
+ }
+ return relevantGroups;
+ }
+
+ private ArtifactDefinition getVfModuleArtifactForCompInstance(ComponentInstance currVF, Service service, User modifier, List<GroupDefinition> groupsForCurrVF, Wrapper<String> payloadWrapper, Wrapper<ResponseFormat> responseWrapper) {
+ ArtifactDefinition vfModuleAertifact = null;
+
+ Optional<ArtifactDefinition> optionalVfModuleArtifact = currVF.getDeploymentArtifacts().values().stream().filter(p -> p.getArtifactType().equals(ArtifactTypeEnum.VF_MODULES_METADATA.name())).findAny();
+ if (optionalVfModuleArtifact.isPresent()) {
+ vfModuleAertifact = optionalVfModuleArtifact.get();
+ } else {
+ Either<ArtifactDefinition, ResponseFormat> createVfModuleArtifact = createVfModuleArtifact(modifier, currVF, service, payloadWrapper.getInnerElement());
+ if (createVfModuleArtifact.isLeft()) {
+ vfModuleAertifact = createVfModuleArtifact.left().value();
+ } else {
+ responseWrapper.setInnerElement(createVfModuleArtifact.right().value());
+ }
+ }
+ return vfModuleAertifact;
+ }
+
+ private void fillVfModuleHeatEnvPayload(List<GroupDefinition> groupsForCurrVF, ComponentInstance currVFInstance, Wrapper<String> payloadWrapper) {
+ // Converts GroupDefinition to VfModuleArtifactPayload which is the
+ // format used in the payload
+
+ List<VfModuleArtifactPayload> vfModulePayloadForCurrVF = groupsForCurrVF.stream().map(group -> new VfModuleArtifactPayload(group)).collect(Collectors.toList());
+ Collections.sort(vfModulePayloadForCurrVF, (art1, art2) -> VfModuleArtifactPayload.compareByGroupName(art1, art2));
+ // Update Payload With Heat Env
+ vfModulePayloadForCurrVF.stream().forEach(e -> addHeatEnvArtifactsToVFModulePayload(e, currVFInstance));
+
+ final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ String vfModulePayloadString = gson.toJson(vfModulePayloadForCurrVF);
+ payloadWrapper.setInnerElement(vfModulePayloadString);
+
+ }
+
+ private void addHeatEnvArtifactsToVFModulePayload(VfModuleArtifactPayload vfModulePayload, ComponentInstance currVFInstance) {
+ List<String> originalModuleArtifacts = vfModulePayload.getArtifacts();
+ if (!MapUtils.isEmpty(currVFInstance.getDeploymentArtifacts()) && !CollectionUtils.isEmpty(originalModuleArtifacts)) {
+
+ final Collection<ArtifactDefinition> depInsArtifacts = currVFInstance.getDeploymentArtifacts().values();
+ // All Heat_ENV
+ List<ArtifactDefinition> heatEnvArtifacts = depInsArtifacts.stream().filter(art -> art.getArtifactType().equals(ArtifactTypeEnum.HEAT_ENV.getType())).collect(Collectors.toList());
+ // Unique Id Of Artifacts In the vf module
+ List<String> moduleArtUniqueId = depInsArtifacts.stream().filter(art -> originalModuleArtifacts.contains(art.getArtifactUUID())).map(art -> art.getUniqueId()).collect(Collectors.toList());
+ // Collect Only Heat Artifatcs that are Generated from artifacts in
+ // the module
+ List<String> relevantHeatEnvUUID = heatEnvArtifacts.stream().filter(heatEnv -> moduleArtUniqueId.contains(heatEnv.getGeneratedFromId())).map(heatEnv -> heatEnv.getArtifactUUID()).collect(Collectors.toList());
+
+ List<String> fullArtifactList = new ArrayList<>();
+ fullArtifactList.addAll(originalModuleArtifacts);
+ fullArtifactList.addAll(relevantHeatEnvUUID);
+
+ vfModulePayload.setArtifacts(fullArtifactList);
+ }
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> generateVfModuleArtifact(User modifier, ComponentInstance currVFInstance, Service service, boolean shouldLock) {
+ ArtifactDefinition vfModuleAertifact = null;
+ Wrapper<ResponseFormat> responseWrapper = new Wrapper<>();
+ Wrapper<String> payloadWrapper = new Wrapper<>();
+ List<GroupDefinition> groupsForCurrVF = collectGroupsForCompInstance(currVFInstance, responseWrapper);
+ if (responseWrapper.isEmpty()) {
+ fillVfModuleHeatEnvPayload(groupsForCurrVF, currVFInstance, payloadWrapper);
+ }
+ if (responseWrapper.isEmpty()) {
+ vfModuleAertifact = getVfModuleArtifactForCompInstance(currVFInstance, service, modifier, groupsForCurrVF, payloadWrapper, responseWrapper);
+ }
+ if (responseWrapper.isEmpty() && vfModuleAertifact != null) {
+ vfModuleAertifact = fillVfModulePayload(modifier, currVFInstance, vfModuleAertifact, shouldLock, payloadWrapper, responseWrapper);
+ }
+
+ Either<ArtifactDefinition, ResponseFormat> result;
+ if (responseWrapper.isEmpty()) {
+ result = Either.left(vfModuleAertifact);
+ } else {
+ result = Either.right(responseWrapper.getInnerElement());
+ }
+
+ return result;
+ }
+
+ private ArtifactDefinition fillVfModulePayload(User modifier, ComponentInstance currVF, ArtifactDefinition vfModuleAertifact, boolean shouldLock, Wrapper<String> payloadWrapper, Wrapper<ResponseFormat> responseWrapper) {
+ ArtifactDefinition result = null;
+ final Either<Resource, StorageOperationStatus> eitherResource = resourceOperation.getResource(currVF.getComponentUid());
+ if (eitherResource.isRight()) {
+ responseWrapper.setInnerElement(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(eitherResource.right().value())));
+ } else {
+ Resource resource = eitherResource.left().value();
+ Either<ArtifactDefinition, ResponseFormat> eitherPayload = artifactsBusinessLogic.generateArtifactPayload(vfModuleAertifact, resource, currVF.getName(), modifier, shouldLock, () -> System.currentTimeMillis(),
+ () -> Either.left(artifactsBusinessLogic.createEsArtifactData(vfModuleAertifact, payloadWrapper.getInnerElement().getBytes(StandardCharsets.UTF_8))));
+ if (eitherPayload.isLeft()) {
+ result = eitherPayload.left().value();
+ } else {
+ responseWrapper.setInnerElement(eitherPayload.right().value());
+ }
+ }
+
+ return result;
+ }
+
+ private Either<ArtifactDefinition, ResponseFormat> createVfModuleArtifact(User modifier, ComponentInstance currVF, Service service, String vfModulePayloadString) {
+
+ ArtifactDefinition vfModuleArtifactDefinition = new ArtifactDefinition();
+
+ vfModuleArtifactDefinition.setDescription("Auto-generated VF Modules information artifact");
+ vfModuleArtifactDefinition.setArtifactDisplayName("Vf Modules Metadata");
+ vfModuleArtifactDefinition.setArtifactType(ArtifactTypeEnum.VF_MODULES_METADATA.getType());
+ vfModuleArtifactDefinition.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+ vfModuleArtifactDefinition.setArtifactLabel("vfModulesMetadata");
+ vfModuleArtifactDefinition.setTimeout(0);
+ vfModuleArtifactDefinition.setArtifactName(currVF.getNormalizedName() + "_modules.json");
+ vfModuleArtifactDefinition.setPayloadData(vfModulePayloadString);
+
+ Either<ArtifactDefinition, StorageOperationStatus> addArifactToComponent = artifactOperation.addArifactToComponent(vfModuleArtifactDefinition, currVF.getUniqueId(), NodeTypeEnum.ResourceInstance, true, true);
+
+ Either<ArtifactDefinition, ResponseFormat> result;
+ if (addArifactToComponent.isLeft()) {
+ result = Either.left(addArifactToComponent.left().value());
+ } else {
+ result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(addArifactToComponent.right().value())));
+ }
+
+ return result;
+ }
+
+ public Either<Service, ResponseFormat> generateHeatEnvArtifacts(Service service, User modifier, boolean shouldLock) {
+
+ Function<ComponentInstance, List<ArtifactGenerator<ArtifactDefinition>>> artifactTaskGeneratorCreator = resourceInstance ->
+ // Get All Deployment Artifacts
+ service.getComponentInstances().stream().filter(ri -> ri != null && ri == resourceInstance).filter(ri -> ri.getDeploymentArtifacts() != null).flatMap(ri -> ri.getDeploymentArtifacts().values().stream()).
+ // Filter in Only Heat Env
+ filter(depArtifact -> ArtifactTypeEnum.HEAT_ENV.getType().equals(depArtifact.getArtifactType())).
+ // Create ArtifactGenerator from those Artifacts
+ map(depArtifact -> new HeatEnvArtifactGenerator(depArtifact, service, resourceInstance.getName(), modifier, shouldLock)).collect(Collectors.toList());
+
+ return generateDeploymentArtifacts(service, modifier, artifactTaskGeneratorCreator);
+
+ }
+
+ private <CallVal> Either<Service, ResponseFormat> generateDeploymentArtifacts(Service service, User modifier, Function<ComponentInstance, List<ArtifactGenerator<CallVal>>> artifactTaskGeneratorCreator) {
+
+ List<Future<Either<CallVal, ResponseFormat>>> allFutures = new ArrayList<>();
+
+ // Get Flat List of (Callable) ArtifactGenerator for all the RI in the
+ // service
+ if (service.getComponentInstances() != null) {
+ List<ArtifactGenerator<CallVal>> artifactGenList = service.getComponentInstances().stream().flatMap(ri -> artifactTaskGeneratorCreator.apply(ri).stream()).collect(Collectors.toList());
+ if (artifactGenList != null && !artifactGenList.isEmpty()) {
+ ExecutorService executor = Executors.newFixedThreadPool(artifactGenList.size());
+
+ artifactGenList.stream().forEach(e -> allFutures.add(executor.submit(e)));
+
+ boolean isSuccess = true;
+ ResponseFormat firstError = null;
+ for (Future<Either<CallVal, ResponseFormat>> entry : allFutures) {
+ try {
+ Either<CallVal, ResponseFormat> actionStatus = entry.get(20, TimeUnit.SECONDS);
+ if (actionStatus.isRight()) {
+ isSuccess = false;
+ if (firstError == null) {
+ firstError = actionStatus.right().value();
+ }
+ log.debug("Failed to generate artifact error : {}", actionStatus.right().value());
+ }
+ } catch (Exception e) {
+ log.debug("Failed to collect result from artifact generator ", e);
+ isSuccess = false;
+ firstError = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ }
+ }
+ executor.shutdown();
+ if (!isSuccess) {
+ return Either.right(firstError);
+ }
+ }
+
+ }
+ return Either.left(service);
+ }
+
+ abstract class ArtifactGenerator<CallVal> implements Callable<Either<CallVal, ResponseFormat>> {
+
+ }
+
+ class HeatEnvArtifactGenerator extends ArtifactGenerator<ArtifactDefinition> {
+ ArtifactDefinition artifactDefinition;
+ Service service;
+ String resourceInstanceName;
+ User modifier;
+ boolean shouldLock;
+
+ HeatEnvArtifactGenerator(ArtifactDefinition artifactDefinition, Service service, String resourceInstanceName, User modifier, boolean shouldLock) {
+ this.artifactDefinition = artifactDefinition;
+ this.service = service;
+ this.resourceInstanceName = resourceInstanceName;
+ this.modifier = modifier;
+ this.shouldLock = shouldLock;
+ }
+
+ @Override
+ public Either<ArtifactDefinition, ResponseFormat> call() throws Exception {
+ return artifactsBusinessLogic.generateHeatEnvArtifact(artifactDefinition, service, resourceInstanceName, modifier, shouldLock);
+ }
+
+ public ArtifactDefinition getArtifactDefinition() {
+ return artifactDefinition;
+ }
+
+ }
+
+ class VfModuleArtifacGenerator extends ArtifactGenerator<ArtifactDefinition> {
+ private User user;
+ private ComponentInstance componentInstance;
+ private Service service;
+ boolean shouldLock;
+
+ @Override
+ public Either<ArtifactDefinition, ResponseFormat> call() throws Exception {
+ return generateVfModuleArtifact(user, componentInstance, service, shouldLock);
+ }
+
+ private VfModuleArtifacGenerator(User user, ComponentInstance componentInstance, Service service, boolean shouldLock) {
+ super();
+ this.user = user;
+ this.componentInstance = componentInstance;
+ this.service = service;
+ this.shouldLock = shouldLock;
+ }
+
+ }
+
+ private synchronized Either<Service, ResponseFormat> checkDistributionAndDeploy(String did, User user, AuditingActionEnum auditAction, Service service) {
+ boolean isDeployed = isDistributionDeployed(did, service);
+ if (isDeployed) {
+ return Either.left(service);
+ }
+ Either<Boolean, ResponseFormat> distributionSuccess = checkDistributionSuccess(did, user, auditAction, service);
+ if (distributionSuccess.isRight()) {
+ return Either.right(distributionSuccess.right().value());
+ }
+
+ log.debug("mark distribution {} as deployed - success", did);
+ componentsUtils.auditServiceDistributionDeployed(auditAction, service.getName(), service.getVersion(), service.getUUID(), did, STATUS_DEPLOYED, "OK", user);
+ return Either.left(service);
+ }
+
+ private boolean isDistributionDeployed(String did, Service service) {
+ Either<List<DistributionDeployEvent>, ActionStatus> alreadyDeployed = auditCassandraDao.getDistributionDeployByStatus(did, AuditingActionEnum.DISTRIBUTION_DEPLOY.getName(), STATUS_DEPLOYED);
+
+ boolean isDeployed = false;
+ if (alreadyDeployed.isLeft() && !alreadyDeployed.left().value().isEmpty()) {
+ // already deployed
+ log.debug("distribution {} is already deployed", did);
+ isDeployed = true;
+ }
+ return isDeployed;
+ }
+
+ protected Either<Boolean, ResponseFormat> checkDistributionSuccess(String did, User user, AuditingActionEnum auditAction, Service service) {
+
+ log.trace("checkDistributionSuccess");
+ // get all "DRequest" records for this distribution
+ // Either<List<ESTimeBasedEvent>, ActionStatus> distRequestsResponse =
+ // auditingDao.getListOfDistributionByAction(did,
+ // AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST.getName(), "",
+ // ResourceAdminEvent.class);
+ Either<List<ResourceAdminEvent>, ActionStatus> distRequestsResponse = auditCassandraDao.getDistributionRequest(did, AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST.getName());
+ if (distRequestsResponse.isRight()) {
+ ResponseFormat error = auditDeployError(did, user, auditAction, service, distRequestsResponse.right().value());
+ return Either.right(error);
+ }
+
+ List<ResourceAdminEvent> distributionRequests = distRequestsResponse.left().value();
+ if (distributionRequests.isEmpty()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionMissingError, "markDistributionAsDeployed", did);
+ BeEcompErrorManager.getInstance().logBeDistributionMissingError("markDistributionAsDeployed", did);
+ log.info("distribution {} is not found", did);
+ ResponseFormat error = auditDeployError(did, user, auditAction, service, ActionStatus.DISTRIBUTION_REQUESTED_NOT_FOUND);
+ return Either.right(error);
+ }
+ boolean isRequestSucceeded = false;
+ for (ResourceAdminEvent event : distributionRequests) {
+ String eventStatus = event.getStatus();
+ if (eventStatus != null && eventStatus.equals(STATUS_SUCCESS_200)) {
+ isRequestSucceeded = true;
+ break;
+ }
+ }
+
+ // get all "DNotify" records for this distribution
+ // Either<List<ESTimeBasedEvent>, ActionStatus>
+ // distNotificationsResponse =
+ // auditingDao.getListOfDistributionByAction(did,
+ // AuditingActionEnum.DISTRIBUTION_NOTIFY.getName(), "",
+ // DistributionNotificationEvent.class);
+ Either<List<DistributionNotificationEvent>, ActionStatus> distNotificationsResponse = auditCassandraDao.getDistributionNotify(did, AuditingActionEnum.DISTRIBUTION_NOTIFY.getName());
+ if (distNotificationsResponse.isRight()) {
+ ResponseFormat error = auditDeployError(did, user, auditAction, service, distNotificationsResponse.right().value());
+ return Either.right(error);
+ }
+
+ List<DistributionNotificationEvent> distributionNotifications = distNotificationsResponse.left().value();
+ boolean isNotificationsSucceeded = false;
+ for (DistributionNotificationEvent event : distributionNotifications) {
+ String eventStatus = event.getStatus();
+ if (eventStatus != null && eventStatus.equals(STATUS_SUCCESS_200)) {
+ isNotificationsSucceeded = true;
+ break;
+ }
+ }
+
+ // if request failed OR there are notifications that failed
+ if (!(isRequestSucceeded && isNotificationsSucceeded)) {
+
+ log.info("distribution {} has failed", did);
+ ResponseFormat error = componentsUtils.getResponseFormat(ActionStatus.DISTRIBUTION_REQUESTED_FAILED, did);
+ auditDeployError(did, user, auditAction, service, ActionStatus.DISTRIBUTION_REQUESTED_FAILED, did);
+ return Either.right(error);
+ }
+ return Either.left(true);
+ }
+
+ private ResponseFormat auditDeployError(String did, User user, AuditingActionEnum auditAction, Service service, ActionStatus status, String... params) {
+
+ ResponseFormat error = componentsUtils.getResponseFormat(status, params);
+ String message = "";
+ if (error.getMessageId() != null) {
+ message = error.getMessageId() + ": ";
+ }
+ message += error.getFormattedMessage();
+
+ if (service != null) {
+ componentsUtils.auditServiceDistributionDeployed(auditAction, service.getName(), service.getVersion(), service.getUUID(), did, error.getStatus().toString(), message, user);
+ } else {
+ componentsUtils.auditServiceDistributionDeployed(auditAction, "", "", "", did, error.getStatus().toString(), message, user);
+ }
+ return error;
+ }
+
+ private Either<User, ResponseFormat> validateRoleForDeploy(String did, User user, AuditingActionEnum auditAction, Service service) {
+ Either<User, ActionStatus> eitherCreator = userAdmin.getUser(user.getUserId(), false);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUserMissingError, "Deploy Service", user.getUserId());
+ BeEcompErrorManager.getInstance().logBeUserMissingError("Deploy Service", user.getUserId());
+ log.debug("validateRoleForDeploy method - user is not listed. userId={}", user.getUserId());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.USER_NOT_FOUND, user.getUserId());
+ auditDeployError(did, user, auditAction, service, ActionStatus.USER_NOT_FOUND);
+ return Either.right(responseFormat);
+ }
+ user = eitherCreator.left().value();
+ log.debug("validate user role");
+ List<Role> roles = new ArrayList<>();
+ roles.add(Role.ADMIN);
+ roles.add(Role.OPS);
+ Either<Boolean, ResponseFormat> validateRes = validateUserRole(user, service, roles, auditAction, null);
+ if (validateRes.isRight()) {
+ log.info("role {} is not allowed to perform this action", user.getRole());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ auditDeployError(did, user, auditAction, service, ActionStatus.RESTRICTED_OPERATION);
+ return Either.right(responseFormat);
+ }
+ return Either.left(user);
+
+ }
+
+ @Override
+ public void setDeploymentArtifactsPlaceHolder(Component component, User user) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Either<List<String>, ResponseFormat> deleteMarkedComponents() {
+ return deleteMarkedComponents(ComponentTypeEnum.SERVICE);
+ }
+
+ private HealthCheckBusinessLogic getHealthCheckBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ HealthCheckBusinessLogic healthCheckBl = webApplicationContext.getBean(HealthCheckBusinessLogic.class);
+ return healthCheckBl;
+ }
+
+ @Override
+ public ComponentInstanceBusinessLogic getComponentInstanceBL() {
+ return serviceComponentInstanceBusinessLogic;
+ }
+
+ @Override
+ public Either<List<ComponentInstance>, ResponseFormat> getComponentInstancesFilteredByPropertiesAndInputs(String componentId, ComponentTypeEnum componentTypeEnum, String userId, String searchText) {
+
+ Either<User, ResponseFormat> resp = validateUserExists(userId, "Get Component Instances", false);
+ if (resp.isRight()) {
+ return Either.right(resp.right().value());
+ }
+
+ ComponentOperation componentOperation = getComponentOperation(componentTypeEnum);
+
+ Either<List<ComponentInstance>, StorageOperationStatus> componentInstancesResponse = componentOperation.getAllComponentInstncesMetadata(componentId, componentTypeEnum.getNodeType());
+ if (componentInstancesResponse.isRight()) {
+
+ if (componentInstancesResponse.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ return Either.left(new ArrayList<ComponentInstance>());
+ }
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(componentInstancesResponse.right().value()));
+ return Either.right(responseFormat);
+ }
+
+ List<ComponentInstance> componentInstances = componentInstancesResponse.left().value();
+ componentInstances = componentInstances.stream().filter(instance -> instance.getOriginType().equals(OriginTypeEnum.VF)).collect(Collectors.toList());
+
+ return Either.left(componentInstances);
+ }
+
+ public ICacheMangerOperation getCacheManagerOperation() {
+ return cacheManagerOperation;
+ }
+
+ public void setCacheManagerOperation(ICacheMangerOperation cacheManagerOperation) {
+ this.cacheManagerOperation = cacheManagerOperation;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceComponentInstanceBusinessLogic.java
new file mode 100644
index 0000000000..c42cf004ff
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceComponentInstanceBusinessLogic.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("serviceComponentInstanceBusinessLogic")
+public class ServiceComponentInstanceBusinessLogic extends ComponentInstanceBusinessLogic {
+
+ @Override
+ protected Either<Boolean, ResponseFormat> validateAllowedToContainCompInstances(org.openecomp.sdc.be.model.Component containerComponent) {
+ return Either.left(true);
+ }
+
+ @Override
+ protected NodeTypeEnum getNodeTypeOfComponentInstanceOrigin() {
+ return NodeTypeEnum.Resource;
+ }
+
+ @Override
+ protected ComponentOperation getContainerComponentOperation() {
+ return serviceOperation;
+ }
+
+ @Override
+ protected ComponentOperation getCompInstOriginComponentOperation() {
+ return resourceOperation;
+ }
+
+ @Override
+ protected ComponentTypeEnum getComponentTypeOfComponentInstance() {
+ return ComponentTypeEnum.RESOURCE_INSTANCE;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/VFComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/VFComponentInstanceBusinessLogic.java
new file mode 100644
index 0000000000..3e42897c79
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/VFComponentInstanceBusinessLogic.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("vfComponentInstanceBusinessLogic")
+public class VFComponentInstanceBusinessLogic extends ComponentInstanceBusinessLogic {
+
+ private static Logger log = LoggerFactory.getLogger(VFComponentInstanceBusinessLogic.class.getName());
+
+ @Override
+ protected Either<Boolean, ResponseFormat> validateAllowedToContainCompInstances(org.openecomp.sdc.be.model.Component containerComponent) {
+ Resource resource = (Resource) containerComponent;
+ ResourceTypeEnum resourceType = resource.getResourceType();
+ if (ResourceTypeEnum.VF != resourceType) {
+ log.debug("Cannot attach resource instances to container resource of type {}", resourceType);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES, resourceType.getValue()));
+ }
+ return Either.left(true);
+ }
+
+ @Override
+ protected NodeTypeEnum getNodeTypeOfComponentInstanceOrigin() {
+ return NodeTypeEnum.Resource;
+ }
+
+ @Override
+ protected ComponentOperation getContainerComponentOperation() {
+ return resourceOperation;
+ }
+
+ @Override
+ protected ComponentOperation getCompInstOriginComponentOperation() {
+ return resourceOperation;
+ }
+
+ @Override
+ protected ComponentTypeEnum getComponentTypeOfComponentInstance() {
+ return ComponentTypeEnum.RESOURCE_INSTANCE;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransition.java
new file mode 100644
index 0000000000..445b3a9750
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransition.java
@@ -0,0 +1,209 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class CertificationChangeTransition extends LifeCycleTransition {
+
+ private static Logger log = LoggerFactory.getLogger(CertificationChangeTransition.class.getName());
+
+ private LifecycleStateEnum nextState;
+ private LifeCycleTransitionEnum name;
+ private AuditingActionEnum auditingAction;
+ private ArtifactsBusinessLogic artifactsManager;
+
+ public CertificationChangeTransition(LifeCycleTransitionEnum name, ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation) {
+ super(componentUtils, lifecycleOperation);
+
+ this.name = name;
+
+ // authorized roles
+ Role[] certificationChangeRoles = { Role.ADMIN, Role.TESTER };
+ addAuthorizedRoles(ComponentTypeEnum.RESOURCE, Arrays.asList(certificationChangeRoles));
+ addAuthorizedRoles(ComponentTypeEnum.SERVICE, Arrays.asList(certificationChangeRoles));
+ // TODO to be later defined for product
+
+ switch (this.name) {
+ case CERTIFY:
+ this.auditingAction = AuditingActionEnum.CERTIFICATION_SUCCESS_RESOURCE;
+ this.nextState = LifecycleStateEnum.CERTIFIED;
+ break;
+ case FAIL_CERTIFICATION:
+ this.auditingAction = AuditingActionEnum.FAIL_CERTIFICATION_RESOURCE;
+ nextState = LifecycleStateEnum.NOT_CERTIFIED_CHECKIN;
+ break;
+ case CANCEL_CERTIFICATION:
+ this.auditingAction = AuditingActionEnum.CANCEL_CERTIFICATION_RESOURCE;
+ nextState = LifecycleStateEnum.READY_FOR_CERTIFICATION;
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ @Override
+ public LifeCycleTransitionEnum getName() {
+ return name;
+ }
+
+ @Override
+ public AuditingActionEnum getAuditingAction() {
+ return auditingAction;
+ }
+
+ public ArtifactsBusinessLogic getArtifactsManager() {
+ return artifactsManager;
+ }
+
+ public void setArtifactsManager(ArtifactsBusinessLogic artifactsManager) {
+ this.artifactsManager = artifactsManager;
+ }
+
+ private ResponseFormat formatCertificationError(Component component, StorageOperationStatus response, ComponentTypeEnum componentType) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Change LifecycleState - Certify failed on graph");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Change LifecycleState - Certify failed on graph");
+ log.debug("certification change failed on graph");
+
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(response);
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ return responseFormat;
+ }
+
+ @Override
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+ String componentName = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ log.debug("validate before certification change. resource name={}, oldState={}, owner userId={}", componentName, oldState, owner.getUserId());
+
+ // validate user
+ Either<Boolean, ResponseFormat> userValidationResponse = userRoleValidation(modifier, componentType, lifecycleChangeInfo);
+ if (userValidationResponse.isRight()) {
+ return userValidationResponse;
+ }
+
+ if (!oldState.equals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_NOT_READY_FOR_CERTIFICATION, componentName, componentType.name().toLowerCase());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS) && !modifier.equals(owner) && !modifier.getRole().equals(Role.ADMIN.name())) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ return Either.left(true);
+ }
+
+ @Override
+ public Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean shouldLock, boolean inTransaction) {
+
+ log.info("start performing certification change for resource {}", component.getUniqueId());
+ Either<? extends Component, ResponseFormat> result = null;
+ NodeTypeEnum nodeType = componentType.getNodeType();
+
+ try {
+ Either<? extends Component, StorageOperationStatus> certificationChangeResult = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ if (nextState.equals(LifecycleStateEnum.CERTIFIED)) {
+ certificationChangeResult = lifeCycleOperation.certifyComponent(nodeType, component, modifier, owner, true);
+ } else {
+ certificationChangeResult = lifeCycleOperation.cancelOrFailCertification(nodeType, component, modifier, owner, nextState, true);
+ }
+
+ if (certificationChangeResult.isRight()) {
+ ResponseFormat responseFormat = formatCertificationError(component, certificationChangeResult.right().value(), componentType);
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ if (nextState.equals(LifecycleStateEnum.CERTIFIED)) {
+ Either<Boolean, StorageOperationStatus> deleteOldComponentVersions = lifeCycleOperation.deleteOldComponentVersions(nodeType, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName(),
+ component.getComponentMetadataDefinition().getMetadataDataDefinition().getUUID(), true);
+ if (deleteOldComponentVersions.isRight()) {
+ ResponseFormat responseFormat = formatCertificationError(component, deleteOldComponentVersions.right().value(), componentType);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ }
+
+ result = Either.left(certificationChangeResult.left().value());
+ return result;
+ } finally {
+ if (result == null || result.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Change LifecycleState");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Change LifecycleState");
+ if (inTransaction == false) {
+ log.debug("operation failed. do rollback");
+ lifeCycleOperation.getResourceOperation().getTitanGenericDao().rollback();
+ }
+ } else {
+ if (inTransaction == false) {
+ log.debug("operation success. do commit");
+ lifeCycleOperation.getResourceOperation().getTitanGenericDao().commit();
+ }
+ }
+ }
+
+ }
+
+ public StorageOperationStatus deleteOldVersion(List<ArtifactDefinition> artifactsToDelete, Resource resourceToDelete) {
+ ResourceOperation resourceOperation = lifeCycleOperation.getResourceOperation();
+
+ Either<List<ArtifactDefinition>, StorageOperationStatus> artifactsRes = resourceOperation.getComponentArtifactsForDelete(resourceToDelete.getUniqueId(), NodeTypeEnum.Resource, true);
+ if (artifactsRes.isRight()) {
+ return artifactsRes.right().value();
+ }
+ Either<Resource, StorageOperationStatus> deleteResourceRes = resourceOperation.deleteResource(resourceToDelete.getUniqueId(), true);
+ if (deleteResourceRes.isRight()) {
+ return deleteResourceRes.right().value();
+ }
+ artifactsToDelete.addAll(artifactsRes.left().value());
+
+ return StorageOperationStatus.OK;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTransition.java
new file mode 100644
index 0000000000..69a9cbdb24
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTransition.java
@@ -0,0 +1,409 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+
+import org.openecomp.sdc.be.components.distribution.engine.ServiceDistributionArtifactsBuilder;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class CertificationRequestTransition extends LifeCycleTransition {
+
+ private static Logger log = LoggerFactory.getLogger(CertificationRequestTransition.class.getName());
+
+ private ServiceDistributionArtifactsBuilder serviceDistributionArtifactsBuilder;
+ private ResourceOperation resourceOperation;
+ private CapabilityOperation capabilityOperation;
+ private ServiceBusinessLogic serviceBusinessLogic;
+ private ToscaExportHandler toscaExportUtils;
+
+ public CertificationRequestTransition(ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation, ServiceDistributionArtifactsBuilder serviceDistributionArtifactsBuilder, ServiceBusinessLogic serviceBusinessLogic,
+ CapabilityOperation capabilityOperation, ToscaExportHandler toscaExportUtils) {
+ super(componentUtils, lifecycleOperation);
+
+ // authorized roles
+ Role[] resourceServiceCheckoutRoles = { Role.ADMIN, Role.DESIGNER };
+ // Role[] productCheckoutRoles = {Role.ADMIN, Role.PRODUCT_MANAGER,
+ // Role.PRODUCT_STRATEGIST};
+ addAuthorizedRoles(ComponentTypeEnum.RESOURCE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.SERVICE, Arrays.asList(resourceServiceCheckoutRoles));
+ // TODO to be later defined for product
+ // addAuthorizedRoles(ComponentTypeEnum.PRODUCT,
+ // Arrays.asList(productCheckoutRoles));
+
+ this.serviceDistributionArtifactsBuilder = serviceDistributionArtifactsBuilder;
+ if (lifeCycleOperation != null)
+ this.resourceOperation = lifeCycleOperation.getResourceOperation();
+ this.serviceBusinessLogic = serviceBusinessLogic;
+ this.capabilityOperation = capabilityOperation;
+ this.toscaExportUtils = toscaExportUtils;
+ }
+
+ @Override
+ public LifeCycleTransitionEnum getName() {
+ return LifeCycleTransitionEnum.CERTIFICATION_REQUEST;
+ }
+
+ @Override
+ public AuditingActionEnum getAuditingAction() {
+ return AuditingActionEnum.CERTIFICATION_REQUEST_RESOURCE;
+ }
+
+ protected Either<Boolean, ResponseFormat> validateAllResourceInstanceCertified(Component component) {
+ Either<Boolean, ResponseFormat> eitherResult = Either.left(true);
+
+ List<ComponentInstance> resourceInstance = component.getComponentInstances();
+ if (resourceInstance != null) {
+ Optional<ComponentInstance> nonCertifiedRIOptional = resourceInstance.stream().filter(p -> !ValidationUtils.validateCertifiedVersion(p.getComponentVersion())).findAny();
+ // Uncertified Resource Found
+ if (nonCertifiedRIOptional.isPresent()) {
+ ComponentInstance nonCertifiedRI = nonCertifiedRIOptional.get();
+ ResponseFormat resFormat = getRelevantResponseFormatUncertifiedRI(nonCertifiedRI, component.getComponentType());
+ eitherResult = Either.right(resFormat);
+ }
+ }
+ return eitherResult;
+ }
+
+ private ResponseFormat getRelevantResponseFormatUncertifiedRI(ComponentInstance nonCertifiedRI, ComponentTypeEnum componentType) {
+
+ ResponseFormat responseFormat;
+ Either<Resource, StorageOperationStatus> eitherResource = resourceOperation.getResource(nonCertifiedRI.getComponentUid());
+ if (eitherResource.isRight()) {
+
+ responseFormat = componentUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+
+ } else {
+ ActionStatus actionStatus;
+ Resource resource = eitherResource.left().value();
+ Either<List<Resource>, StorageOperationStatus> status = resourceOperation.findLastCertifiedResourceByUUID(resource);
+
+ if (ValidationUtils.validateMinorVersion(nonCertifiedRI.getComponentVersion())) {
+ if (status.isRight() || status.left().value() == null || status.left().value().isEmpty()) {
+ actionStatus = ActionStatus.VALIDATED_RESOURCE_NOT_FOUND;
+ } else {
+ actionStatus = ActionStatus.FOUND_ALREADY_VALIDATED_RESOURCE;
+ }
+ } else {
+ if (status.isRight() || status.left().value() == null || status.left().value().isEmpty())
+ actionStatus = ActionStatus.FOUND_LIST_VALIDATED_RESOURCES;
+ else {
+ actionStatus = ActionStatus.FOUND_ALREADY_VALIDATED_RESOURCE;
+ }
+
+ }
+ String compType = (componentType == ComponentTypeEnum.RESOURCE) ? "VF" : "service";
+ responseFormat = componentUtils.getResponseFormat(actionStatus, compType, resource.getName());
+ }
+ return responseFormat;
+ }
+
+ private Either<ActionStatus, Map<String, ArtifactDefinition>> validateMandatoryArtifactsSupplied(Map<String, ArtifactDefinition> artifacts) {
+
+ if (artifacts == null || true == artifacts.isEmpty()) {
+ return Either.left(ActionStatus.OK);
+ }
+
+ Map<String, ArtifactDefinition> invalidArtifacts = new HashMap<String, ArtifactDefinition>();
+ for (Entry<String, ArtifactDefinition> artifact : artifacts.entrySet()) {
+
+ ArtifactDefinition artifactDefinition = artifact.getValue();
+ if (true == artifactDefinition.getMandatory()) {
+ String artifactEsId = artifactDefinition.getEsId();
+ if (artifactEsId == null || true == artifactEsId.isEmpty()) {
+ invalidArtifacts.put(artifact.getKey(), artifactDefinition);
+ }
+ }
+ }
+
+ if (true == invalidArtifacts.isEmpty()) {
+ return Either.left(ActionStatus.OK);
+ } else {
+ return Either.right(invalidArtifacts);
+ }
+ }
+
+ @Override
+ public Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean shouldLock, boolean inTransaction) {
+
+ log.debug("start performing certification request for resource {}", component.getUniqueId());
+
+ // Either<ActionStatus, Map<String, ArtifactDefinition>>
+ // validateMandatoryArtifacts =
+ // validateMandatoryArtifactsSupplied(component.getArtifacts());
+ // log.debug("After checking mandatory artifacts were populated. Result
+ // is " + validateMandatoryArtifacts);
+ // if (validateMandatoryArtifacts.isRight()) {
+ // ResponseFormat responseFormat = componentUtils
+ // .getResponseFormatByMissingArtifacts(
+ // componentType,
+ // validateMandatoryArtifacts.right().value());
+ // return Either.right(responseFormat);
+ // }
+ ActionStatus actionStatus = null;
+ ResponseFormat responseFormat = null;
+
+ if (componentType == ComponentTypeEnum.SERVICE || (componentType == ComponentTypeEnum.RESOURCE && ((Resource) component).getResourceType() == ResourceTypeEnum.VF)) {
+
+ Either<Boolean, ResponseFormat> statusCert = validateAllResourceInstanceCertified(component);
+ if (statusCert.isRight()) {
+ return Either.right(statusCert.right().value());
+ }
+
+ statusCert = validateConfiguredAtomicReqCapSatisfied(component);
+ if (statusCert.isRight()) {
+ return Either.right(statusCert.right().value());
+ }
+ }
+ if (componentType == ComponentTypeEnum.SERVICE) {
+ Either<Boolean, StorageOperationStatus> status = validateDeloymentArtifactSupplied((Service) component);
+ if (status.isRight()) {
+ StorageOperationStatus operationStatus = status.right().value();
+ actionStatus = componentUtils.convertFromStorageResponse(operationStatus);
+ } else {
+ Boolean isDeploymentArtifactExists = status.left().value();
+ if (isDeploymentArtifactExists == null || isDeploymentArtifactExists.booleanValue() == false) {
+ actionStatus = ActionStatus.SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND;
+ } else {
+ Either<Service, ResponseFormat> generateHeatEnvResult = serviceBusinessLogic.generateHeatEnvArtifacts((Service) component, modifier, shouldLock);
+ if (generateHeatEnvResult.isRight()) {
+ return Either.right(generateHeatEnvResult.right().value());
+ }
+ Either<Service, ResponseFormat> generateVfModuleResult = serviceBusinessLogic.generateVfModuleArtifacts((Service) component, modifier, shouldLock);
+ if (generateVfModuleResult.isRight()) {
+ return Either.right(generateVfModuleResult.right().value());
+ }
+ }
+ }
+
+ if (actionStatus != null) {
+ responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ return Either.right(responseFormat);
+ }
+
+ }
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> eitherPopulated = componentBl.populateToscaArtifacts(component, modifier, true, inTransaction, shouldLock);
+ if (eitherPopulated != null && eitherPopulated.isRight()) {
+ return Either.right(eitherPopulated.right().value());
+ }
+
+ NodeTypeEnum nodeType = (componentType.equals(ComponentTypeEnum.SERVICE)) ? NodeTypeEnum.Service : NodeTypeEnum.Resource;
+ Either<? extends Component, StorageOperationStatus> certificationRequestResult = lifeCycleOperation.requestCertificationComponent(nodeType, component, modifier, owner, inTransaction);
+ if (certificationRequestResult.isRight()) {
+ log.debug("checkout failed on graph");
+ StorageOperationStatus response = certificationRequestResult.right().value();
+ actionStatus = componentUtils.convertFromStorageResponse(response);
+
+ if (response.equals(StorageOperationStatus.ENTITY_ALREADY_EXISTS)) {
+ actionStatus = ActionStatus.COMPONENT_VERSION_ALREADY_EXIST;
+ }
+ responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(certificationRequestResult.left().value());
+ }
+
+ private Either<Boolean, ResponseFormat> validateConfiguredAtomicReqCapSatisfied(Component component) {
+ log.debug("Submit for testing validation - Start validating configured req/cap satisfied for inner atomic instances, component id:{}", component.getUniqueId());
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null) {
+ // Prepare relationships data structures
+ // Better make it list than set in case we need to count req/cap
+ // occurrences in the future
+ Map<String, List<String>> reqName2Ids = new HashMap<>();
+ Map<String, List<String>> capName2Ids = new HashMap<>();
+ parseRelationsForReqCapVerification(component, reqName2Ids, capName2Ids);
+ Map<String, Set<String>> requirementsToFulfillBeforeCert = configurationManager.getConfiguration().getRequirementsToFulfillBeforeCert();
+ Map<String, Set<String>> capabilitiesToConsumeBeforeCert = configurationManager.getConfiguration().getCapabilitiesToConsumeBeforeCert();
+ for (ComponentInstance compInst : componentInstances) {
+ String compInstId = compInst.getUniqueId();
+ OriginTypeEnum originType = compInst.getOriginType();
+ if (originType == null) {
+ log.error("Origin type is not set for component instance {} - it shouldn't happen. Skipping this component instance...", compInst.getUniqueId());
+ continue;
+ }
+ String compInstType = originType.getValue();
+ // Validating configured requirements fulfilled
+ if (null != requirementsToFulfillBeforeCert) {
+ Set<String> reqToFulfillForType = requirementsToFulfillBeforeCert.get(compInstType);
+ if (reqToFulfillForType != null) {
+ for (String reqNameToFulfill : reqToFulfillForType) {
+ List<String> reqNameList = reqName2Ids.get(reqNameToFulfill);
+ if (reqNameList == null || !reqNameList.contains(compInstId)) {
+ log.debug("Requirement {} wasn't fulfilled for component instance {} of type {}", reqNameToFulfill, compInstId, compInstType);
+ ComponentTypeEnum componentType = component.getComponentType();
+ String compParam = (componentType == ComponentTypeEnum.RESOURCE) ? "VF" : componentType.getValue().toLowerCase();
+ ResponseFormat responseFormat = componentUtils.getResponseFormat(ActionStatus.REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION, component.getName(), compParam, originType.getDisplayValue(), compInst.getName(), "requirement",
+ reqNameToFulfill, "fulfilled");
+ return Either.right(responseFormat);
+ }
+ }
+ }
+ }
+ // Validating configured capabilities consumed
+ if (null != capabilitiesToConsumeBeforeCert) {
+ Set<String> capToConsumeForType = capabilitiesToConsumeBeforeCert.get(compInstType);
+ if (capToConsumeForType != null) {
+ for (String capNameToConsume : capToConsumeForType) {
+ List<String> capNameList = capName2Ids.get(capNameToConsume);
+ if (capNameList == null || !capNameList.contains(compInstId)) {
+ log.debug("Capability {} wasn't consumed for component instance {} of type {}", capNameToConsume, compInstId, compInstType);
+ ComponentTypeEnum componentType = component.getComponentType();
+ String compParam = (componentType == ComponentTypeEnum.RESOURCE) ? "VF" : componentType.getValue().toLowerCase();
+ ResponseFormat responseFormat = componentUtils.getResponseFormat(ActionStatus.REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION, component.getName(), compParam, originType.getDisplayValue(), compInst.getName(), "capability",
+ capNameToConsume, "consumed");
+ return Either.right(responseFormat);
+ }
+ }
+ }
+ }
+ }
+ }
+ log.debug("Submit for testing validation - validating configured req/cap satisfied for inner atomic instances finished successfully, component id:{}", component.getUniqueId());
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> parseRelationsForReqCapVerification(Component component, Map<String, List<String>> reqName2Ids, Map<String, List<String>> capName2Ids) {
+ log.debug("Submit for testing validation - Preparing relations for inner atomic instances validation");
+ List<RequirementCapabilityRelDef> componentInstancesRelations = component.getComponentInstancesRelations();
+ if (componentInstancesRelations != null) {
+ for (RequirementCapabilityRelDef reqCapRelDef : componentInstancesRelations) {
+ List<RequirementAndRelationshipPair> relationships = reqCapRelDef.getRelationships();
+ if (relationships != null) {
+ for (RequirementAndRelationshipPair reqRelPair : relationships) {
+ String capUniqueId = reqRelPair.getCapabilityUid();
+ Either<CapabilityDefinition, StorageOperationStatus> capability = capabilityOperation.getCapability(capUniqueId);
+ if (capability.isRight()) {
+ log.error("Couldn't fetch capability by id {}", capUniqueId);
+ return Either.right(componentUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ String reqCapType = capability.left().value().getType();
+ String capabilityOwnerId = reqRelPair.getCapabilityOwnerId();
+ String requirementOwnerId = reqRelPair.getRequirementOwnerId();
+ // Update req
+ List<String> reqIds = reqName2Ids.get(reqCapType);
+ if (reqIds == null) {
+ reqIds = new ArrayList<>();
+ reqName2Ids.put(reqCapType, reqIds);
+ }
+ reqIds.add(requirementOwnerId);
+ // Update cap
+ List<String> capIds = capName2Ids.get(reqCapType);
+ if (capIds == null) {
+ capIds = new ArrayList<>();
+ capName2Ids.put(reqCapType, capIds);
+ }
+ capIds.add(capabilityOwnerId);
+ }
+ }
+ }
+ log.debug("Parsed req for validation: {}, parsed cap for validation: {}", reqName2Ids, capName2Ids);
+ } else {
+ log.debug("There are no relations found for component {}", component.getUniqueId());
+ }
+ return Either.left(true);
+ }
+
+ @Override
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+ String componentName = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ log.debug("validate before certification request. resource name={}, oldState={}, owner userId={}", componentName, oldState, owner.getUserId());
+
+ // validate user
+ Either<Boolean, ResponseFormat> userValidationResponse = userRoleValidation(modifier, componentType, lifecycleChangeInfo);
+ if (userValidationResponse.isRight()) {
+ return userValidationResponse;
+ }
+
+ // case of "atomic" checkin and certification request - modifier must be
+ // the owner
+ if (oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT) && !modifier.equals(owner) && !modifier.getRole().equals(Role.ADMIN.name())) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ // other states
+ if (oldState.equals(LifecycleStateEnum.CERTIFIED)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_ALREADY_CERTIFIED, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+ if (oldState.equals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+ if (oldState.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_SENT_FOR_CERTIFICATION, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ return Either.left(true);
+ }
+
+ private Either<Boolean, StorageOperationStatus> validateDeloymentArtifactSupplied(Service service) {
+
+ Either<Boolean, StorageOperationStatus> serviceContainsDeploymentArtifacts = this.serviceDistributionArtifactsBuilder.isServiceContainsDeploymentArtifacts(service);
+
+ return serviceContainsDeploymentArtifacts;
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckinTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckinTransition.java
new file mode 100644
index 0000000000..28227285f1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckinTransition.java
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.Arrays;
+
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class CheckinTransition extends LifeCycleTransition {
+
+ private static Logger log = LoggerFactory.getLogger(CheckinTransition.class.getName());
+
+ public CheckinTransition(ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation) {
+ super(componentUtils, lifecycleOperation);
+
+ // authorized roles
+ Role[] resourceServiceCheckoutRoles = { Role.ADMIN, Role.DESIGNER };
+ Role[] productCheckoutRoles = { Role.ADMIN, Role.PRODUCT_MANAGER };
+ addAuthorizedRoles(ComponentTypeEnum.RESOURCE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.SERVICE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.PRODUCT, Arrays.asList(productCheckoutRoles));
+
+ }
+
+ @Override
+ public LifeCycleTransitionEnum getName() {
+ return LifeCycleTransitionEnum.CHECKIN;
+ }
+
+ @Override
+ public AuditingActionEnum getAuditingAction() {
+ return AuditingActionEnum.CHECKIN_RESOURCE;
+ }
+
+ @Override
+ public Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean shouldLock, boolean inTransaction) {
+ log.debug("start performing checkin for {} {}", componentType.name(), component.getUniqueId());
+
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ Either<? extends Component, StorageOperationStatus> checkinResourceResult = lifeCycleOperation.checkinComponent(nodeType, component, modifier, owner, inTransaction);
+ if (checkinResourceResult.isRight()) {
+ log.debug("checkout failed on graph");
+ StorageOperationStatus response = checkinResourceResult.right().value();
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(response);
+
+ if (response.equals(StorageOperationStatus.ENTITY_ALREADY_EXISTS)) {
+ actionStatus = ActionStatus.COMPONENT_VERSION_ALREADY_EXIST;
+ }
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(checkinResourceResult.left().value());
+ }
+
+ @Override
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+ String componentName = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ log.debug("validate before checkin. component name={}, oldState={}, owner userId={}", componentName, oldState, owner.getUserId());
+
+ // validate user
+ Either<Boolean, ResponseFormat> userValidationResponse = userRoleValidation(modifier, componentType, lifecycleChangeInfo);
+ if (userValidationResponse.isRight()) {
+ return userValidationResponse;
+ }
+
+ if (!oldState.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION) && !oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_ALREADY_CHECKED_IN, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT) && !modifier.equals(owner) && !modifier.getRole().equals(Role.ADMIN.name())) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION) && !modifier.equals(owner) && !modifier.getRole().equals(Role.ADMIN.name())) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_SENT_FOR_CERTIFICATION, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ return Either.left(true);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTransition.java
new file mode 100644
index 0000000000..a4e6bdb86c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTransition.java
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.Arrays;
+
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class CheckoutTransition extends LifeCycleTransition {
+
+ private static final String PLACE_HOLDER_RESOURCE_TYPES = "validForResourceTypes";
+
+ private static Logger log = LoggerFactory.getLogger(CheckoutTransition.class.getName());
+
+ public CheckoutTransition(ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation) {
+ super(componentUtils, lifecycleOperation);
+
+ // authorized roles
+ Role[] resourceServiceCheckoutRoles = { Role.ADMIN, Role.DESIGNER };
+ Role[] productCheckoutRoles = { Role.ADMIN, Role.PRODUCT_MANAGER };
+ addAuthorizedRoles(ComponentTypeEnum.RESOURCE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.SERVICE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.PRODUCT, Arrays.asList(productCheckoutRoles));
+
+ }
+
+ @Override
+ public LifeCycleTransitionEnum getName() {
+ return LifeCycleTransitionEnum.CHECKOUT;
+ }
+
+ @Override
+ public AuditingActionEnum getAuditingAction() {
+ return AuditingActionEnum.CHECKOUT_RESOURCE;
+ }
+
+ @Override
+ public Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean shouldLock, boolean inTransaction) {
+
+ log.debug("start performing {} for resource {}", getName().name(), component.getUniqueId());
+
+ if (componentBl != null)
+ componentBl.setDeploymentArtifactsPlaceHolder(component, modifier);
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ Either<? extends Component, StorageOperationStatus> checkoutResourceResult = lifeCycleOperation.checkoutComponent(nodeType, component, modifier, owner, inTransaction);
+
+ if (checkoutResourceResult.isRight()) {
+ log.debug("checkout failed on graph");
+ StorageOperationStatus response = checkoutResourceResult.right().value();
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(response);
+
+ if (response.equals(StorageOperationStatus.ENTITY_ALREADY_EXISTS)) {
+ actionStatus = ActionStatus.COMPONENT_VERSION_ALREADY_EXIST;
+ }
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(checkoutResourceResult.left().value());
+ }
+
+ @Override
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+ String componentName = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ log.debug("validate before checkout. resource name={}, oldState={}, owner userId={}", componentName, oldState, owner.getUserId());
+
+ // validate user
+ Either<Boolean, ResponseFormat> userValidationResponse = userRoleValidation(modifier, componentType, lifecycleChangeInfo);
+ if (userValidationResponse.isRight()) {
+ return userValidationResponse;
+ }
+
+ // Disabled as of 1604 patch after discussing with Ella/Eli/Michael
+
+ /*
+ * if (componentType == ComponentTypeEnum.PRODUCT){ Either<Boolean, ResponseFormat> productContactsEither = productContactsValidation((Product)component, modifier); if (productContactsEither.isRight()){ return productContactsEither; } }
+ */
+
+ // check resource is not locked by another user
+ if (oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_IN_CHECKOUT_STATE, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION)) {
+ if (!modifier.getRole().equals(Role.DESIGNER.name()) && !modifier.getRole().equals(Role.ADMIN.name())) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_SENT_FOR_CERTIFICATION, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+ }
+ return Either.left(true);
+ }
+
+ /*
+ * private Either<Boolean, ResponseFormat> productContactsValidation(Product product, User modifier) { // validate user Either<Boolean, ResponseFormat> eitherResponse = Either.left(true); String role = modifier.getRole(); if
+ * (UserRoleEnum.PRODUCT_MANAGER.getName().equals(role) || UserRoleEnum.PRODUCT_STRATEGIST.getName().equals(role)){ String userId = modifier.getUserId(); if (!product.getContacts().contains(userId)){ log.
+ * debug("User with userId {} cannot checkout product. userId not found in product contacts list" , userId); ResponseFormat responseFormat = componentUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION); return Either.right(responseFormat);
+ * } else { log. trace("Found user userId {} in product contacts - checkout request validated" ); } } return eitherResponse; }
+ */
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifeCycleTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifeCycleTransition.java
new file mode 100644
index 0000000000..49d94dc80b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifeCycleTransition.java
@@ -0,0 +1,160 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public abstract class LifeCycleTransition {
+
+ protected ConfigurationManager configurationManager;
+ protected ILifecycleOperation lifeCycleOperation;
+ protected ComponentsUtils componentUtils;
+
+ protected Map<ComponentTypeEnum, List<Role>> authorizedRoles;
+
+ protected LifeCycleTransition(ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation) {
+
+ // configurationManager = (ConfigurationManager)
+ // context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR);
+ // lifeCycleOperation = LifecycleOperation.getInstance();
+ this.configurationManager = ConfigurationManager.getConfigurationManager();
+ this.lifeCycleOperation = lifecycleOperation;
+ this.componentUtils = componentUtils;
+ this.authorizedRoles = new HashMap<>();
+
+ }
+
+ public abstract LifeCycleTransitionEnum getName();
+
+ public abstract AuditingActionEnum getAuditingAction();
+
+ public ConfigurationManager getConfigurationManager() {
+ return configurationManager;
+ }
+
+ public void setConfigurationManager(ConfigurationManager configurationManager) {
+ this.configurationManager = configurationManager;
+ }
+
+ public ILifecycleOperation getLifeCycleOperation() {
+ return lifeCycleOperation;
+ }
+
+ public void setLifeCycleOperation(ILifecycleOperation lifeCycleOperation) {
+ this.lifeCycleOperation = lifeCycleOperation;
+ }
+
+ public List<Role> getAuthorizedRoles(ComponentTypeEnum componentType) {
+ return authorizedRoles.get(componentType);
+ }
+
+ public void addAuthorizedRoles(ComponentTypeEnum componentType, List<Role> authorizedRoles) {
+ this.authorizedRoles.put(componentType, authorizedRoles);
+ }
+
+ //
+ // public Either<? extends Component, ResponseFormat>
+ // changeState(ComponentTypeEnum componentType, Component component,
+ // ComponentBusinessLogic componentBl, User modifier, User owner){
+ // return changeState(componentType, component, componentBl, modifier,
+ // owner, false);
+ // }
+
+ public abstract Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean needLock, boolean inTransaction);
+
+ public abstract Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo);
+
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState) {
+
+ return this.validateBeforeTransition(component, componentType, modifier, owner, oldState, null);
+ }
+
+ /**
+ * getComponentOwner
+ *
+ * @param resource
+ * @return
+ */
+ protected Either<User, ResponseFormat> getComponentOwner(Component component, ComponentTypeEnum componentType) {
+
+ return getComponentOwner(component, componentType, false);
+ }
+
+ protected Either<User, ResponseFormat> getComponentOwner(Component component, ComponentTypeEnum componentType, boolean inTransaction) {
+
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ Either<User, StorageOperationStatus> resourceOwnerResult = getLifeCycleOperation().getComponentOwner(component.getUniqueId(), nodeType, inTransaction);
+ if (resourceOwnerResult.isRight()) {
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(componentUtils.convertFromStorageResponse(resourceOwnerResult.right().value()), component, componentType);
+ return Either.right(responseFormat);
+ }
+ return Either.left(resourceOwnerResult.left().value());
+ }
+
+ /**
+ * isUserValidForRequest
+ *
+ * @param modifier
+ * @param action
+ * TODO
+ * @return
+ */
+ protected Either<Boolean, ResponseFormat> userRoleValidation(User modifier, ComponentTypeEnum componentType, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+
+ // validate user
+ if (getAuthorizedRoles(componentType).contains(Role.valueOf(modifier.getRole()))) {
+ return Either.left(true);
+ }
+
+ // this is only when creating vfc/cp when import vf from csar - when we
+ // create resources from node type, we create need to change the state
+ // to certified
+ if (lifecycleChangeInfo != null && lifecycleChangeInfo.getAction() != null && lifecycleChangeInfo.getAction() == LifecycleChanceActionEnum.CREATE_FROM_CSAR) {
+ return Either.left(true);
+ }
+
+ ResponseFormat responseFormat = componentUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ return Either.right(responseFormat);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleBusinessLogic.java
new file mode 100644
index 0000000000..27709bb332
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleBusinessLogic.java
@@ -0,0 +1,572 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.openecomp.sdc.be.components.distribution.engine.ServiceDistributionArtifactsBuilder;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.ProductOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("lifecycleBusinessLogic")
+public class LifecycleBusinessLogic {
+
+ private static final String COMMENT = "comment";
+
+ @Autowired
+ private IGraphLockOperation graphLockOperation = null;
+
+ @Autowired
+ private ArtifactsBusinessLogic artifactsBusinessLogic;
+
+ @Autowired
+ private ResourceOperation resourceOperation;
+
+ @Autowired
+ private ServiceOperation serviceOperation;
+
+ @Autowired
+ private ProductOperation productOperation;
+
+ @Autowired
+ private CapabilityOperation capabilityOperation;
+
+ private static Logger log = LoggerFactory.getLogger(LifecycleBusinessLogic.class.getName());
+
+ @javax.annotation.Resource
+ private ComponentsUtils componentUtils;
+
+ @javax.annotation.Resource
+ private ILifecycleOperation lifecycleOperation;
+ @javax.annotation.Resource
+ ArtifactsBusinessLogic artifactsManager;
+
+ @javax.annotation.Resource
+ private ServiceDistributionArtifactsBuilder serviceDistributionArtifactsBuilder;
+
+ @javax.annotation.Resource
+ private ServiceBusinessLogic serviceBusinessLogic;
+
+ @javax.annotation.Resource
+ private ResourceBusinessLogic resourceBusinessLogic;
+
+ @javax.annotation.Resource
+ private ProductBusinessLogic productBusinessLogic;
+
+ @Autowired
+ private ToscaExportHandler toscaExportUtils;
+
+ @Autowired
+ ICacheMangerOperation cacheManagerOperation;
+
+ private Map<String, LifeCycleTransition> stateTransitions;
+ private static volatile boolean isInitialized = false;
+
+ @PostConstruct
+ public void init() {
+ // init parameters
+ if (!isInitialized) {
+ synchronized (this) {
+ if (!isInitialized) {
+ initStateOperations();
+ isInitialized = true;
+ }
+ }
+ }
+ }
+
+ private void initStateOperations() {
+ stateTransitions = new HashMap<String, LifeCycleTransition>();
+
+ LifeCycleTransition checkoutOp = new CheckoutTransition(componentUtils, lifecycleOperation);
+ stateTransitions.put(checkoutOp.getName().name(), checkoutOp);
+
+ UndoCheckoutTransition undoCheckoutOp = new UndoCheckoutTransition(componentUtils, lifecycleOperation);
+ undoCheckoutOp.setArtifactsBusinessLogic(artifactsBusinessLogic);
+ stateTransitions.put(undoCheckoutOp.getName().name(), undoCheckoutOp);
+
+ LifeCycleTransition checkinOp = new CheckinTransition(componentUtils, lifecycleOperation);
+ stateTransitions.put(checkinOp.getName().name(), checkinOp);
+
+ LifeCycleTransition certificationRequest = new CertificationRequestTransition(componentUtils, lifecycleOperation, serviceDistributionArtifactsBuilder, serviceBusinessLogic, capabilityOperation, toscaExportUtils);
+ stateTransitions.put(certificationRequest.getName().name(), certificationRequest);
+
+ LifeCycleTransition startCertification = new StartCertificationTransition(componentUtils, lifecycleOperation);
+ stateTransitions.put(startCertification.getName().name(), startCertification);
+
+ LifeCycleTransition failCertification = new CertificationChangeTransition(LifeCycleTransitionEnum.FAIL_CERTIFICATION, componentUtils, lifecycleOperation);
+ stateTransitions.put(failCertification.getName().name(), failCertification);
+
+ LifeCycleTransition cancelCertification = new CertificationChangeTransition(LifeCycleTransitionEnum.CANCEL_CERTIFICATION, componentUtils, lifecycleOperation);
+ stateTransitions.put(cancelCertification.getName().name(), cancelCertification);
+
+ CertificationChangeTransition successCertification = new CertificationChangeTransition(LifeCycleTransitionEnum.CERTIFY, componentUtils, lifecycleOperation);
+ successCertification.setArtifactsManager(artifactsBusinessLogic);
+ stateTransitions.put(successCertification.getName().name(), successCertification);
+ }
+
+ public LifeCycleTransition getLifecycleTransition(LifeCycleTransitionEnum transitionEnum) {
+ return stateTransitions.get(transitionEnum.name());
+ }
+
+ public Either<Service, ResponseFormat> changeServiceState(String serviceId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction, boolean needLock) {
+ return (Either<Service, ResponseFormat>) changeComponentState(ComponentTypeEnum.SERVICE, serviceId, modifier, transitionEnum, changeInfo, inTransaction, needLock);
+ }
+
+ // TODO: rhalili - should use changeComponentState when possible
+ public Either<Resource, ResponseFormat> changeState(String resourceId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction, boolean needLock) {
+ return (Either<Resource, ResponseFormat>) changeComponentState(ComponentTypeEnum.RESOURCE, resourceId, modifier, transitionEnum, changeInfo, inTransaction, needLock);
+
+ // LifeCycleTransition lifeCycleTransition =
+ // stateTransitions.get(transitionEnum.name());
+ // if (lifeCycleTransition == null) {
+ // log.debug("state operation is not valid. operations allowed are: {}",
+ // LifeCycleTransitionEnum.valuesAsString());
+ // ResponseFormat error =
+ // componentUtils.getInvalidContentErrorAndAudit(modifier,
+ // AuditingActionEnum.CHECKOUT_RESOURCE);
+ // return Either.right(error);
+ // }
+ //
+ // Either<Resource, ResponseFormat> operationResult;
+ // Resource resource = null;
+ // boolean needToUnlockResource = false;
+ //
+ // log.debug("get resource from graph");
+ // ResponseFormat errorResponse;
+ // Either<Resource, ResponseFormat> eitherResourceResponse =
+ // getResourceForChange(resourceId, modifier, lifeCycleTransition);
+ // if (eitherResourceResponse.isRight()) {
+ // return eitherResourceResponse;
+ // }
+ // resource = eitherResourceResponse.left().value();
+ // String resourceCurrVersion = resource.getResourceVersion();
+ // LifecycleStateEnum resourceCurrState = resource.getLifecycleState();
+ //
+ // if (inTransaction == false) {
+ // // lock resource
+ // Either<Boolean, ResponseFormat> eitherLockResource =
+ // lockResource(resource);
+ // if (eitherLockResource.isRight()) {
+ // errorResponse = eitherLockResource.right().value();
+ // componentUtils.auditResource(errorResponse, modifier, resource,
+ // resourceCurrState.name(), resourceCurrVersion,
+ // lifeCycleTransition.getAuditingAction(), null);
+ // return Either.right(errorResponse);
+ // }
+ // needToUnlockResource = true;
+ // }
+ //
+ // try {
+ // Either<Boolean, ResponseFormat> resourceNotDeleted =
+ // validateResourceNotDeleted(modifier, lifeCycleTransition, resource,
+ // resourceCurrVersion);
+ // if (resourceNotDeleted.isRight()) {
+ // return Either.right(resourceNotDeleted.right().value());
+ // }
+ //
+ // Either<Boolean, ResponseFormat> validateHighestVersion =
+ // validateHighestVersion(modifier, lifeCycleTransition, resource,
+ // resourceCurrVersion);
+ // if (validateHighestVersion.isRight()) {
+ // return Either.right(validateHighestVersion.right().value());
+ // }
+ //
+ // Either<User, ResponseFormat> ownerResult =
+ // lifeCycleTransition.getResourceOwner(resource);
+ // if (ownerResult.isRight()) {
+ // return Either.right(ownerResult.right().value());
+ // }
+ // User owner = ownerResult.left().value();
+ // log.debug("owner of resource {} is {}", resource.getUniqueId(),
+ // owner.getUserId());
+ //
+ // LifecycleStateEnum oldState = resource.getLifecycleState();
+ //
+ // Either<String, ResponseFormat> commentValidationResult =
+ // validateComment(changeInfo, transitionEnum);
+ // if (commentValidationResult.isRight()) {
+ // errorResponse = commentValidationResult.right().value();
+ // EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new
+ // EnumMap<AuditingFieldsKeysEnum,
+ // Object>(AuditingFieldsKeysEnum.class);
+ // auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT,
+ // changeInfo.getUserRemarks());
+ // componentUtils.auditResource(errorResponse, modifier, resource,
+ // resourceCurrState.name(), resourceCurrVersion,
+ // lifeCycleTransition.getAuditingAction(), auditingFields);
+ // return Either.right(errorResponse);
+ // }
+ // changeInfo.setUserRemarks(commentValidationResult.left().value());
+ //
+ // Either<Boolean, ResponseFormat> stateValidationResult =
+ // lifeCycleTransition.validateResourceBeforeTransition(resource.getResourceName(),
+ // ComponentTypeEnum.RESOURCE, modifier, owner, oldState);
+ // if (stateValidationResult.isRight()) {
+ // errorResponse = stateValidationResult.right().value();
+ // componentUtils.auditResource(errorResponse, modifier, resource,
+ // resourceCurrState.name(), resourceCurrVersion,
+ // lifeCycleTransition.getAuditingAction(), null);
+ // return Either.right(errorResponse);
+ // }
+ //
+ // operationResult = lifeCycleTransition.changeStateOperation(resource,
+ // modifier, owner, inTransaction);
+ //
+ // if (operationResult.isRight()) {
+ // errorResponse = operationResult.right().value();
+ // log.debug("audit before sending response");
+ // componentUtils.auditResource(errorResponse, modifier, resource,
+ // resourceCurrState.name(), resourceCurrVersion,
+ // lifeCycleTransition.getAuditingAction(), null);
+ //
+ // return Either.right(errorResponse);
+ // }
+ // Resource resourceAfterOperation = operationResult.left().value();
+ // EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new
+ // EnumMap<AuditingFieldsKeysEnum,
+ // Object>(AuditingFieldsKeysEnum.class);
+ // auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT,
+ // changeInfo.getUserRemarks());
+ // componentUtils.auditResource(componentUtils.getResponseFormat(ActionStatus.OK),
+ // modifier, resourceAfterOperation, resourceCurrState.name(),
+ // resourceCurrVersion, lifeCycleTransition.getAuditingAction(),
+ // auditingFields);
+ // return operationResult;
+ //
+ // } finally {
+ // log.debug("unlock resource {}", resourceId);
+ // if (needToUnlockResource && resource != null) {
+ // resource.setUniqueId(resourceId);
+ // graphLockOperation.unlockResource(resource);
+ // }
+ // }
+
+ }
+
+ public Either<? extends Component, ResponseFormat> changeComponentState(ComponentTypeEnum componentType, String componentId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
+ boolean needLock) {
+
+ LifeCycleTransition lifeCycleTransition = stateTransitions.get(transitionEnum.name());
+ if (lifeCycleTransition == null) {
+ log.debug("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString());
+ ResponseFormat error = componentUtils.getInvalidContentErrorAndAudit(modifier, AuditingActionEnum.CHECKOUT_RESOURCE);
+ return Either.right(error);
+ }
+ ComponentBusinessLogic bl = getComponentBL(componentType);
+
+ Either<? extends Component, ResponseFormat> operationResult = null;
+ Component component = null;
+ // boolean needToUnlockResource = false;
+ log.debug("get resource from graph");
+ ResponseFormat errorResponse;
+
+ Either<? extends Component, ResponseFormat> eitherResourceResponse = getComponentForChange(componentType, componentId, modifier, lifeCycleTransition, changeInfo);
+ if (eitherResourceResponse.isRight()) {
+ return eitherResourceResponse;
+ }
+ component = eitherResourceResponse.left().value();
+ String resourceCurrVersion = component.getVersion();
+ LifecycleStateEnum resourceCurrState = component.getLifecycleState();
+
+ // lock resource
+ if (inTransaction == false && needLock) {
+ Either<Boolean, ResponseFormat> eitherLockResource = lockComponent(componentType, component);
+ if (eitherLockResource.isRight()) {
+ errorResponse = eitherLockResource.right().value();
+ componentUtils.auditComponent(errorResponse, modifier, component, resourceCurrState.name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), componentType, null);
+ return Either.right(errorResponse);
+ }
+ // needToUnlockResource = true;
+ }
+ try {
+ Either<String, ResponseFormat> commentValidationResult = validateComment(changeInfo, transitionEnum);
+ if (commentValidationResult.isRight()) {
+ errorResponse = commentValidationResult.right().value();
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, changeInfo.getUserRemarks());
+ componentUtils.auditComponent(errorResponse, modifier, component, resourceCurrState.name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), componentType, auditingFields);
+ return Either.right(errorResponse);
+ }
+ changeInfo.setUserRemarks(commentValidationResult.left().value());
+
+ Either<Boolean, ResponseFormat> validateHighestVersion = validateHighestVersion(modifier, lifeCycleTransition, component, resourceCurrVersion, componentType);
+ if (validateHighestVersion.isRight()) {
+ return Either.right(validateHighestVersion.right().value());
+ }
+
+ Either<User, ResponseFormat> ownerResult = lifeCycleTransition.getComponentOwner(component, componentType, inTransaction);
+ if (ownerResult.isRight()) {
+ return Either.right(ownerResult.right().value());
+ }
+ User owner = ownerResult.left().value();
+ log.debug("owner of resource {} is {}", component.getUniqueId(), owner.getUserId());
+
+ LifecycleStateEnum oldState = component.getLifecycleState();
+
+ Either<Boolean, ResponseFormat> stateValidationResult = lifeCycleTransition.validateBeforeTransition(component, componentType, modifier, owner, oldState, changeInfo);
+ if (stateValidationResult.isRight()) {
+ errorResponse = stateValidationResult.right().value();
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, changeInfo.getUserRemarks());
+ componentUtils.auditComponent(errorResponse, modifier, component, resourceCurrState.name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), componentType, auditingFields);
+ return Either.right(errorResponse);
+
+ }
+
+ operationResult = lifeCycleTransition.changeState(componentType, component, bl, modifier, owner, false, inTransaction);
+
+ if (operationResult.isRight()) {
+ errorResponse = operationResult.right().value();
+ log.debug("audit before sending response");
+ componentUtils.auditComponentAdmin(errorResponse, modifier, component, resourceCurrState.name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), componentType);
+
+ return Either.right(errorResponse);
+ }
+ Component resourceAfterOperation = operationResult.left().value();
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, changeInfo.getUserRemarks());
+ componentUtils.auditComponent(componentUtils.getResponseFormat(ActionStatus.OK), modifier, resourceAfterOperation, resourceCurrState.name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), componentType, auditingFields);
+ return operationResult;
+
+ } finally {
+ log.debug("unlock component {}", componentId);
+ if (inTransaction == false && needLock && component != null) {
+ component.setUniqueId(componentId);
+ NodeTypeEnum nodeType = componentType.getNodeType();
+
+ // Handle component change in the cache of the side affect of
+ // the operation
+ if (operationResult != null && operationResult.isLeft()) {
+ Component componentAfterOpertion = operationResult.left().value();
+ String uniqueId = componentAfterOpertion.getUniqueId();
+ if (false == componentId.equals(uniqueId)) {
+ log.debug("During change state, another component {} has been created/updated", uniqueId);
+ if (uniqueId != null) {
+ cacheManagerOperation.updateComponentInCache(uniqueId, componentAfterOpertion.getLastUpdateDate(), nodeType);
+ }
+ }
+ }
+
+ graphLockOperation.unlockComponent(componentId, nodeType);
+
+ }
+ }
+
+ }
+
+ private Either<? extends Component, ResponseFormat> getComponentForChange(ComponentTypeEnum componentType, String componentId, User modifier, LifeCycleTransition lifeCycleTransition, LifecycleChangeInfoWithAction changeInfo) {
+
+ Either<? extends Component, StorageOperationStatus> eitherResourceResponse = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ switch (componentType) {
+ case SERVICE:
+ eitherResourceResponse = serviceOperation.getComponent(componentId, true);
+ break;
+ case PRODUCT:
+ eitherResourceResponse = productOperation.getComponent(componentId, true);
+ break;
+ case RESOURCE:
+ eitherResourceResponse = resourceOperation.getComponent(componentId, true);
+ break;
+ default:
+ break;
+ }
+
+ ResponseFormat errorResponse = null;
+ if (eitherResourceResponse.isRight()) {
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(eitherResourceResponse.right().value(), componentType);
+ errorResponse = componentUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
+ log.debug("audit before sending response");
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, changeInfo.getUserRemarks());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, componentId);
+ componentUtils.auditComponent(errorResponse, modifier, null, Constants.EMPTY_STRING, Constants.EMPTY_STRING, lifeCycleTransition.getAuditingAction(), componentType, auditingFields);
+
+ return Either.right(errorResponse);
+ }
+ return Either.left(eitherResourceResponse.left().value());
+ }
+
+ private Either<Boolean, ResponseFormat> validateHighestVersion(User modifier, LifeCycleTransition lifeCycleTransition, Resource resource, String resourceCurrVersion) {
+ ResponseFormat errorResponse;
+ if (!resource.isHighestVersion()) {
+ log.debug("resource version {} is not the last version of resource {}", resource.getVersion(), resource.getName());
+ errorResponse = componentUtils.getResponseFormat(ActionStatus.COMPONENT_HAS_NEWER_VERSION, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase());
+ componentUtils.auditResource(errorResponse, modifier, resource, resource.getLifecycleState().name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), null);
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateResourceNotDeleted(User modifier, LifeCycleTransition lifeCycleTransition, Resource resource, String resourceCurrVersion) {
+
+ ResponseFormat errorResponse;
+ if ((resource.getIsDeleted() != null) && (resource.getIsDeleted() == true)) {
+ ActionStatus actionStatus = ActionStatus.RESOURCE_NOT_FOUND;
+ errorResponse = componentUtils.getResponseFormatByResource(actionStatus, resource.getName());
+ log.debug("resource {} {} is marked for delete", resource.getName(), resource.getVersion());
+ componentUtils.auditResource(errorResponse, modifier, null, "", "", lifeCycleTransition.getAuditingAction(), null);
+
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> validateHighestVersion(User modifier, LifeCycleTransition lifeCycleTransition, Component component, String resourceCurrVersion, ComponentTypeEnum componentType) {
+ ResponseFormat errorResponse;
+ if (!component.isHighestVersion()) {
+ log.debug("Component version {} is not the last version of component {}", component.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion(),
+ component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ errorResponse = componentUtils.getResponseFormat(ActionStatus.COMPONENT_HAS_NEWER_VERSION, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName(), componentType.getValue().toLowerCase());
+ componentUtils.auditComponentAdmin(errorResponse, modifier, component, component.getLifecycleState().name(), resourceCurrVersion, lifeCycleTransition.getAuditingAction(), componentType);
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+
+ private Either<Boolean, ResponseFormat> lockComponent(ComponentTypeEnum componentType, Component component) {
+ NodeTypeEnum nodeType = componentType.getNodeType();
+ StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponent(component.getUniqueId(), nodeType);
+
+ if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
+ return Either.left(true);
+ } else {
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(lockResourceStatus);
+ ResponseFormat responseFormat = componentUtils.getResponseFormat(actionStatus, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ return Either.right(responseFormat);
+ }
+
+ }
+
+ private Either<Resource, ResponseFormat> getResourceForChange(String resourceId, User modifier, LifeCycleTransition lifeCycleTransition) {
+ Either<Resource, StorageOperationStatus> eitherResourceResponse = resourceOperation.getResource(resourceId, true);
+
+ ResponseFormat errorResponse = null;
+ if (eitherResourceResponse.isRight()) {
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(eitherResourceResponse.right().value());
+ errorResponse = componentUtils.getResponseFormatByResource(actionStatus, "");
+ log.debug("audit before sending response");
+ // For audit of not found, resourceName should be uniqueID according
+ // to Ella
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceId);
+ componentUtils.auditResource(errorResponse, modifier, null, "", "", lifeCycleTransition.getAuditingAction(), null);
+
+ return Either.right(errorResponse);
+ }
+ Resource resource = eitherResourceResponse.left().value();
+
+ return Either.left(resource);
+
+ }
+
+ private Either<String, ResponseFormat> validateComment(LifecycleChangeInfoWithAction changeInfo, LifeCycleTransitionEnum transitionEnum) {
+ String comment = changeInfo.getUserRemarks();
+ if (LifeCycleTransitionEnum.CANCEL_CERTIFICATION == transitionEnum || LifeCycleTransitionEnum.CERTIFY == transitionEnum || LifeCycleTransitionEnum.FAIL_CERTIFICATION == transitionEnum || LifeCycleTransitionEnum.CHECKIN == transitionEnum
+ || LifeCycleTransitionEnum.CERTIFICATION_REQUEST == transitionEnum
+ // import?
+ ) {
+
+ if (!ValidationUtils.validateStringNotEmpty(comment)) {
+ log.debug("user comment cannot be empty or null.");
+ ResponseFormat errorResponse = componentUtils.getResponseFormat(ActionStatus.MISSING_DATA, COMMENT);
+ return Either.right(errorResponse);
+ }
+
+ comment = ValidationUtils.removeNoneUtf8Chars(comment);
+ comment = ValidationUtils.removeHtmlTags(comment);
+ comment = ValidationUtils.normaliseWhitespace(comment);
+ comment = ValidationUtils.stripOctets(comment);
+
+ if (!ValidationUtils.validateLength(comment, ValidationUtils.COMMENT_MAX_LENGTH)) {
+ log.debug("user comment exceeds limit.");
+ return Either.right(componentUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, COMMENT, String.valueOf(ValidationUtils.COMMENT_MAX_LENGTH)));
+ }
+ if (!ValidationUtils.validateIsEnglish(comment)) {
+ return Either.right(componentUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ }
+ return Either.left(comment);
+ }
+
+ private ComponentBusinessLogic getComponentBL(ComponentTypeEnum componentTypeEnum) {
+ ComponentBusinessLogic businessLogic;
+ switch (componentTypeEnum) {
+ case RESOURCE: {
+ businessLogic = this.resourceBusinessLogic;
+ break;
+ }
+ case SERVICE: {
+ businessLogic = this.serviceBusinessLogic;
+ break;
+ }
+ case PRODUCT: {
+ businessLogic = this.productBusinessLogic;
+ break;
+ }
+
+ default: {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "getComponentBL");
+ throw new IllegalArgumentException("Illegal component type:" + componentTypeEnum.getValue());
+ }
+ }
+ return businessLogic;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoBase.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoBase.java
new file mode 100644
index 0000000000..0c35caf41b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoBase.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+public class LifecycleChangeInfoBase {
+
+ public LifecycleChangeInfoBase() {
+ }
+
+ public LifecycleChangeInfoBase(String userRemarks) {
+ super();
+ this.userRemarks = userRemarks;
+ }
+
+ private String userRemarks;
+
+ public String getUserRemarks() {
+ return userRemarks;
+ }
+
+ public void setUserRemarks(String userRemarks) {
+ this.userRemarks = userRemarks;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoWithAction.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoWithAction.java
new file mode 100644
index 0000000000..170e187f15
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/LifecycleChangeInfoWithAction.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+public class LifecycleChangeInfoWithAction extends LifecycleChangeInfoBase {
+
+ public enum LifecycleChanceActionEnum {
+ CREATE_FROM_CSAR, UPDATE_FROM_EXTERNAL_API
+ };
+
+ private LifecycleChanceActionEnum action;
+
+ public LifecycleChangeInfoWithAction() {
+ }
+
+ public LifecycleChangeInfoWithAction(String userRemarks) {
+ super(userRemarks);
+ }
+
+ public LifecycleChangeInfoWithAction(String userRemarks, LifecycleChanceActionEnum action) {
+ super(userRemarks);
+ this.action = action;
+ }
+
+ public LifecycleChanceActionEnum getAction() {
+ return action;
+ }
+
+ public void setAction(LifecycleChanceActionEnum action) {
+ this.action = action;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/StartCertificationTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/StartCertificationTransition.java
new file mode 100644
index 0000000000..918140b0af
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/StartCertificationTransition.java
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.Arrays;
+
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class StartCertificationTransition extends LifeCycleTransition {
+
+ private static Logger log = LoggerFactory.getLogger(StartCertificationTransition.class.getName());
+
+ public StartCertificationTransition(ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation) {
+ super(componentUtils, lifecycleOperation);
+
+ // authorized roles
+ Role[] rsrcServiceStartCertificationRoles = { Role.ADMIN, Role.TESTER };
+ addAuthorizedRoles(ComponentTypeEnum.RESOURCE, Arrays.asList(rsrcServiceStartCertificationRoles));
+ addAuthorizedRoles(ComponentTypeEnum.SERVICE, Arrays.asList(rsrcServiceStartCertificationRoles));
+ // TODO to be later defined for product
+ }
+
+ @Override
+ public LifeCycleTransitionEnum getName() {
+ return LifeCycleTransitionEnum.START_CERTIFICATION;
+ }
+
+ @Override
+ public AuditingActionEnum getAuditingAction() {
+ return AuditingActionEnum.START_CERTIFICATION_RESOURCE;
+ }
+
+ @Override
+ public Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean shouldLock, boolean inTransaction) {
+
+ log.debug("start performing certification test for resource {}", component.getUniqueId());
+
+ NodeTypeEnum nodeType = (componentType.equals(ComponentTypeEnum.SERVICE)) ? NodeTypeEnum.Service : NodeTypeEnum.Resource;
+ Either<? extends Component, StorageOperationStatus> stateChangeResult = lifeCycleOperation.startComponentCertification(nodeType, component, modifier, owner, inTransaction);
+ if (stateChangeResult.isRight()) {
+ log.debug("start certification failed on graph");
+ StorageOperationStatus response = stateChangeResult.right().value();
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(response);
+
+ if (response.equals(StorageOperationStatus.ENTITY_ALREADY_EXISTS)) {
+ actionStatus = ActionStatus.COMPONENT_VERSION_ALREADY_EXIST;
+ }
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(stateChangeResult.left().value());
+ }
+
+ @Override
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+ String componentName = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ log.debug("validate before start certification test. resource name={}, oldState={}, owner userId={}", componentName, oldState, owner.getUserId());
+
+ // validate user
+ Either<Boolean, ResponseFormat> userValidationResponse = userRoleValidation(modifier, componentType, lifecycleChangeInfo);
+ if (userValidationResponse.isRight()) {
+ return userValidationResponse;
+ }
+
+ if (oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN) || oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_NOT_READY_FOR_CERTIFICATION, componentName, componentType.name().toLowerCase());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.CERTIFIED)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_ALREADY_CERTIFIED, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ if (oldState.equals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ return Either.left(true);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTransition.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTransition.java
new file mode 100644
index 0000000000..fcb211bc3e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTransition.java
@@ -0,0 +1,170 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class UndoCheckoutTransition extends LifeCycleTransition {
+ private static Logger log = LoggerFactory.getLogger(CheckoutTransition.class.getName());
+ private ArtifactsBusinessLogic artifactsManager;
+
+ public UndoCheckoutTransition(ComponentsUtils componentUtils, ILifecycleOperation lifecycleOperation) {
+ super(componentUtils, lifecycleOperation);
+
+ // authorized roles
+ Role[] resourceServiceCheckoutRoles = { Role.ADMIN, Role.DESIGNER };
+ Role[] productCheckoutRoles = { Role.ADMIN, Role.PRODUCT_MANAGER };
+ addAuthorizedRoles(ComponentTypeEnum.RESOURCE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.SERVICE, Arrays.asList(resourceServiceCheckoutRoles));
+ addAuthorizedRoles(ComponentTypeEnum.PRODUCT, Arrays.asList(productCheckoutRoles));
+
+ }
+
+ @Override
+ public LifeCycleTransitionEnum getName() {
+ return LifeCycleTransitionEnum.UNDO_CHECKOUT;
+ }
+
+ @Override
+ public AuditingActionEnum getAuditingAction() {
+ return AuditingActionEnum.UNDO_CHECKOUT_RESOURCE;
+ }
+
+ public ArtifactsBusinessLogic getArtifactsBusinessLogic() {
+ return artifactsManager;
+ }
+
+ public void setArtifactsBusinessLogic(ArtifactsBusinessLogic artifactsBusinessLogic) {
+ this.artifactsManager = artifactsBusinessLogic;
+ }
+
+ @Override
+ public Either<Boolean, ResponseFormat> validateBeforeTransition(Component component, ComponentTypeEnum componentType, User modifier, User owner, LifecycleStateEnum oldState, LifecycleChangeInfoWithAction lifecycleChangeInfo) {
+ String componentName = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ log.debug("validate before undo checkout. resource name={}, oldState={}, owner userId={}", componentName, oldState, owner.getUserId());
+
+ // validate user
+ Either<Boolean, ResponseFormat> userValidationResponse = userRoleValidation(modifier, componentType, lifecycleChangeInfo);
+ if (userValidationResponse.isRight()) {
+ return userValidationResponse;
+ }
+
+ // check resource is not locked by another user
+ if (!oldState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_ALREADY_CHECKED_IN, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ if (!modifier.equals(owner) && !modifier.getRole().equals(Role.ADMIN.name())) {
+ ResponseFormat error = componentUtils.getResponseFormat(ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, componentName, componentType.name().toLowerCase(), owner.getFirstName(), owner.getLastName(), owner.getUserId());
+ return Either.right(error);
+ }
+
+ return Either.left(true);
+ }
+
+ @Override
+ public Either<? extends Component, ResponseFormat> changeState(ComponentTypeEnum componentType, Component component, ComponentBusinessLogic componentBl, User modifier, User owner, boolean shouldLock, boolean inTransaction) {
+
+ Either<? extends Component, ResponseFormat> result = null;
+ log.debug("start performing undo-checkout for resource {}", component.getUniqueId());
+
+ Either<List<ArtifactDefinition>, StorageOperationStatus> artifactsRes = lifeCycleOperation.getComponentOperation(componentType.getNodeType()).getComponentArtifactsForDelete(component.getUniqueId(), componentType.getNodeType(), true);
+ if (artifactsRes.isRight()) {
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(artifactsRes.right().value());
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ // TODO - start transaction
+ try {
+ NodeTypeEnum nodeType = componentType.getNodeType();
+
+ // 1. perform undo checkout on graph (update status and delete
+ // current version)
+ Either<? extends Component, StorageOperationStatus> undoCheckoutResourceResult = lifeCycleOperation.undoCheckout(nodeType, component, modifier, owner, true);
+
+ if (undoCheckoutResourceResult.isRight()) {
+ log.debug("checkout failed on graph");
+ StorageOperationStatus response = undoCheckoutResourceResult.right().value();
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(response);
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ result = Either.right(responseFormat);
+ return result;
+ }
+
+ // 2. delete unrelated artifacts
+ // use artifacts API to delete artifacts from swift / elasticsearch
+
+ if (artifactsRes.left().value() != null) {
+ List<ArtifactDefinition> artifacts = artifactsRes.left().value();
+ StorageOperationStatus deleteAllResourceArtifacts = artifactsManager.deleteAllComponentArtifactsIfNotOnGraph(artifacts);
+
+ if (!deleteAllResourceArtifacts.equals(StorageOperationStatus.OK)) {
+ ActionStatus actionStatus = componentUtils.convertFromStorageResponse(deleteAllResourceArtifacts);
+ ResponseFormat responseFormat = componentUtils.getResponseFormatByComponent(actionStatus, component, componentType);
+ result = Either.right(responseFormat);
+ return result;
+ }
+ }
+
+ result = Either.left(undoCheckoutResourceResult.left().value());
+ } finally {
+ if (result == null || result.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Change LifecycleState - Undo Checkout failed on graph");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Change LifecycleState - Undo Checkout failed on graph");
+
+ log.debug("operation failed. do rollback");
+ lifeCycleOperation.getResourceOperation().getTitanGenericDao().rollback();
+ } else {
+ log.debug("operation success. do commit");
+ lifeCycleOperation.getResourceOperation().getTitanGenericDao().commit();
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/CategoryTypeEnum.java b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/CategoryTypeEnum.java
new file mode 100644
index 0000000000..edfe56ebfa
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/CategoryTypeEnum.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datamodel.api;
+
+import java.io.Serializable;
+
+public enum CategoryTypeEnum implements Serializable {
+
+ CATEGORY("category"), SUBCATEGORY("sub-category"), GROUPING("grouping");
+
+ private String value;
+
+ CategoryTypeEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/HighestFilterEnum.java b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/HighestFilterEnum.java
new file mode 100644
index 0000000000..42ff4c900a
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/api/HighestFilterEnum.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datamodel.api;
+
+public enum HighestFilterEnum {
+
+ ALL, HIGHEST_ONLY, NON_HIGHEST_ONLY;
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/ArtifactUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/ArtifactUtils.java
new file mode 100644
index 0000000000..9f66911461
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/ArtifactUtils.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datamodel.utils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+
+public class ArtifactUtils {
+ public static ArtifactDefinition findMasterArtifact(Map<String, ArtifactDefinition> deplymentArtifact, List<ArtifactDefinition> artifacts, List<String> artifactsList) {
+ for (String artifactUid : artifactsList) {
+ for (Entry<String, ArtifactDefinition> entry : deplymentArtifact.entrySet()) {
+ ArtifactDefinition artifact = entry.getValue();
+ if (artifactUid.equalsIgnoreCase(artifact.getUniqueId())) {
+ artifacts.add(artifact);
+ }
+
+ }
+ }
+ if (artifacts.size() == 1) {
+ return artifacts.get(0);
+ }
+ ArtifactDefinition masterArtifact = null;
+ for (ArtifactDefinition artifactInfo : artifacts) {
+ String atrifactType = artifactInfo.getArtifactType();
+ if (atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType()) || atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT_NET.getType())) {
+ masterArtifact = artifactInfo;
+ continue;
+ }
+ if (atrifactType.equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType())) {
+ masterArtifact = artifactInfo;
+ break;
+ }
+ }
+ return masterArtifact;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/NodeTypeConvertUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/NodeTypeConvertUtils.java
new file mode 100644
index 0000000000..ab71508e62
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/datamodel/utils/NodeTypeConvertUtils.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datamodel.utils;
+
+import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class NodeTypeConvertUtils {
+ public static NodeTypeEnum getCategoryNodeTypeByComponentParam(ComponentTypeEnum componentTypeEnum, CategoryTypeEnum categoryType) {
+ NodeTypeEnum res = null;
+ if (componentTypeEnum != null) {
+ switch (componentTypeEnum) {
+ case SERVICE:
+ switch (categoryType) {
+ case CATEGORY:
+ res = NodeTypeEnum.ServiceNewCategory;
+ break;
+
+ default:
+ // doesn't support subcategories or grouping
+ break;
+ }
+ break;
+ case RESOURCE:
+ switch (categoryType) {
+ case CATEGORY:
+ res = NodeTypeEnum.ResourceNewCategory;
+ break;
+ case SUBCATEGORY:
+ res = NodeTypeEnum.ResourceSubcategory;
+ break;
+ default:
+ // doesn't support grouping
+ break;
+ }
+ break;
+ case PRODUCT:
+ switch (categoryType) {
+ case CATEGORY:
+ res = NodeTypeEnum.ProductCategory;
+ break;
+ case SUBCATEGORY:
+ res = NodeTypeEnum.ProductSubcategory;
+ break;
+ case GROUPING:
+ res = NodeTypeEnum.ProductGrouping;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return res;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/AuditHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/AuditHandler.java
new file mode 100644
index 0000000000..0cc415c80d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/AuditHandler.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution;
+
+import org.openecomp.sdc.be.components.distribution.engine.CambriaErrorResponse;
+import org.openecomp.sdc.be.components.distribution.engine.SubscriberTypeEnum;
+import org.openecomp.sdc.be.distribution.api.client.RegistrationRequest;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+
+public class AuditHandler {
+ ComponentsUtils componentsUtils;
+ String instanceID;
+ private RegistrationRequest registrationRequest;
+
+ public AuditHandler(ComponentsUtils componentsUtils, String instanceID, RegistrationRequest registrationRequest) {
+ super();
+ this.componentsUtils = componentsUtils;
+ this.instanceID = instanceID;
+ this.registrationRequest = registrationRequest;
+ }
+
+ public void auditRegisterACL(CambriaErrorResponse registerResponse, SubscriberTypeEnum subscriberRole) {
+
+ String topicName = (subscriberRole == SubscriberTypeEnum.CONSUMER) ? DistributionBusinessLogic.getNotificationTopicName(registrationRequest.getDistrEnvName())
+ : DistributionBusinessLogic.getStatusTopicName(registrationRequest.getDistrEnvName());
+ componentsUtils.auditTopicACLKeys(AuditingActionEnum.ADD_KEY_TO_TOPIC_ACL, registrationRequest.getDistrEnvName(), topicName, subscriberRole.name(), registrationRequest.getApiPublicKey(), String.valueOf(registerResponse.getHttpCode()));
+ }
+
+ public void auditUnRegisterACL(CambriaErrorResponse registerResponse, SubscriberTypeEnum subscriberRole) {
+ String topicName = (subscriberRole == SubscriberTypeEnum.CONSUMER) ? DistributionBusinessLogic.getNotificationTopicName(registrationRequest.getDistrEnvName())
+ : DistributionBusinessLogic.getStatusTopicName(registrationRequest.getDistrEnvName());
+ componentsUtils.auditTopicACLKeys(AuditingActionEnum.REMOVE_KEY_FROM_TOPIC_ACL, registrationRequest.getDistrEnvName(), topicName, subscriberRole.name(), registrationRequest.getApiPublicKey(), String.valueOf(registerResponse.getHttpCode()));
+
+ }
+
+ public void auditRegisterRequest(CambriaErrorResponse registerResponse) {
+ componentsUtils.auditRegisterOrUnRegisterEvent(AuditingActionEnum.DISTRIBUTION_REGISTER, instanceID, registrationRequest.getApiPublicKey(), registrationRequest.getDistrEnvName(), String.valueOf(registerResponse.getHttpCode()),
+ registerResponse.getOperationStatus().name(), DistributionBusinessLogic.getNotificationTopicName(registrationRequest.getDistrEnvName()), DistributionBusinessLogic.getStatusTopicName(registrationRequest.getDistrEnvName()));
+
+ }
+
+ public void auditUnRegisterRequest(CambriaErrorResponse registerResponse) {
+ componentsUtils.auditRegisterOrUnRegisterEvent(AuditingActionEnum.DISTRIBUTION_UN_REGISTER, instanceID, registrationRequest.getApiPublicKey(), registrationRequest.getDistrEnvName(), String.valueOf(registerResponse.getHttpCode()),
+ registerResponse.getOperationStatus().name(), DistributionBusinessLogic.getNotificationTopicName(registrationRequest.getDistrEnvName()), DistributionBusinessLogic.getStatusTopicName(registrationRequest.getDistrEnvName()));
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogic.java
new file mode 100644
index 0000000000..ae1de21ea8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogic.java
@@ -0,0 +1,243 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaErrorResponse;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaHandler;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngine;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineInitTask;
+import org.openecomp.sdc.be.components.distribution.engine.SubscriberTypeEnum;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+import org.openecomp.sdc.be.distribution.api.client.RegistrationRequest;
+import org.openecomp.sdc.be.distribution.api.client.ServerListResponse;
+import org.openecomp.sdc.be.distribution.api.client.TopicRegistrationResponse;
+import org.openecomp.sdc.be.distribution.api.client.TopicUnregistrationResponse;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@Component("distributionBusinessLogic")
+public class DistributionBusinessLogic {
+ public static final String REGISTER_IN_DISTRIBUTION_ENGINE = "registerInDistributionEngine";
+ public static final String UN_REGISTER_IN_DISTRIBUTION_ENGINE = "unregisterInDistributionEngine";
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ private static Logger log = LoggerFactory.getLogger(DistributionBusinessLogic.class.getName());
+ @Resource
+ private DistributionEngine distributionEngine;
+
+ private ResponseFormatManager responseFormatManager = ResponseFormatManager.getInstance();
+ private CambriaHandler cambriaHandler;
+
+ public Either<ServerListResponse, ResponseFormat> getUebServerList() {
+
+ DistributionEngineConfiguration distributionEngineConfiguration = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+
+ List<String> serverList = distributionEngineConfiguration.getUebServers();
+
+ if (serverList != null && !serverList.isEmpty()) {
+
+ ServerListResponse serverListResponse = new ServerListResponse();
+
+ serverListResponse.setUebServerList(serverList);
+
+ return Either.left(serverListResponse);
+ } else {
+ ResponseFormat errorResponseWrapper = getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return Either.right(errorResponseWrapper);
+ }
+
+ }
+
+ public void handleRegistration(Wrapper<Response> responseWrapper, RegistrationRequest registrationRequest, AuditHandler auditHandler) {
+ CambriaErrorResponse registerResponse = null;
+ try {
+ registerResponse = registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.PRODUCER);
+ auditHandler.auditRegisterACL(registerResponse, SubscriberTypeEnum.PRODUCER);
+
+ if (responseWrapper.isEmpty()) {
+ registerResponse = registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.CONSUMER);
+ auditHandler.auditRegisterACL(registerResponse, SubscriberTypeEnum.CONSUMER);
+ // Second Register failed - unregister the first
+ if (!responseWrapper.isEmpty()) {
+ CambriaErrorResponse unRegisterResponse = unRegisterDistributionClientFromTopic(registrationRequest, SubscriberTypeEnum.PRODUCER);
+ auditHandler.auditUnRegisterACL(unRegisterResponse, SubscriberTypeEnum.PRODUCER);
+ }
+ }
+
+ if (responseWrapper.isEmpty()) {
+ TopicRegistrationResponse okTopicResponse = buildTopicResponse(registrationRequest);
+ responseWrapper.setInnerElement(Response.status(HttpStatus.SC_OK).entity(okTopicResponse).build());
+ }
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, REGISTER_IN_DISTRIBUTION_ENGINE, "registration of subscriber to topic");
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(REGISTER_IN_DISTRIBUTION_ENGINE, "registration of subscriber to topic");
+ Response errorResponse = buildErrorResponse(getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ responseWrapper.setInnerElement(errorResponse);
+ } finally {
+ auditHandler.auditRegisterRequest(registerResponse);
+ }
+ }
+
+ public void handleUnRegistration(Wrapper<Response> responseWrapper, RegistrationRequest unRegistrationRequest, AuditHandler auditHandler) {
+ Wrapper<CambriaErrorResponse> cambriaResponseWrapper = new Wrapper<>();
+ try {
+ CambriaErrorResponse unregisterClientProducerTopicResponse = unRegisterDistributionClientFromTopic(unRegistrationRequest, SubscriberTypeEnum.PRODUCER);
+ auditHandler.auditUnRegisterACL(unregisterClientProducerTopicResponse, SubscriberTypeEnum.PRODUCER);
+ updateResponseWrapper(cambriaResponseWrapper, unregisterClientProducerTopicResponse);
+
+ CambriaErrorResponse unregisterClientConsumerTopicResponse = unRegisterDistributionClientFromTopic(unRegistrationRequest, SubscriberTypeEnum.CONSUMER);
+ auditHandler.auditUnRegisterACL(unregisterClientConsumerTopicResponse, SubscriberTypeEnum.CONSUMER);
+ updateResponseWrapper(cambriaResponseWrapper, unregisterClientConsumerTopicResponse);
+
+ // Success unregister both topics
+ TopicUnregistrationResponse unregisterResponse = new TopicUnregistrationResponse(getNotificationTopicName(unRegistrationRequest.getDistrEnvName()), getStatusTopicName(unRegistrationRequest.getDistrEnvName()),
+ unregisterClientConsumerTopicResponse.getOperationStatus(), unregisterClientProducerTopicResponse.getOperationStatus());
+
+ if (cambriaResponseWrapper.getInnerElement().getOperationStatus() == CambriaOperationStatus.OK) {
+ responseWrapper.setInnerElement(Response.status(HttpStatus.SC_OK).entity(unregisterResponse).build());
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, UN_REGISTER_IN_DISTRIBUTION_ENGINE, "unregistration failed");
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(UN_REGISTER_IN_DISTRIBUTION_ENGINE, "unregistration failed");
+ responseWrapper.setInnerElement(Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(unregisterResponse).build());
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, UN_REGISTER_IN_DISTRIBUTION_ENGINE, "unregistration of subscriber to topic");
+ Response errorResponse = buildErrorResponse(getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ responseWrapper.setInnerElement(errorResponse);
+
+ } finally {
+ auditHandler.auditUnRegisterRequest(cambriaResponseWrapper.getInnerElement());
+ }
+ }
+
+ private void updateResponseWrapper(Wrapper<CambriaErrorResponse> cambriaResponseWrapper, CambriaErrorResponse currentResponse) {
+ if (cambriaResponseWrapper.isEmpty()) {
+ cambriaResponseWrapper.setInnerElement(currentResponse);
+ } else if (currentResponse.getOperationStatus() != CambriaOperationStatus.OK) {
+ cambriaResponseWrapper.setInnerElement(currentResponse);
+
+ }
+
+ }
+
+ public static String getNotificationTopicName(String envName) {
+ DistributionEngineConfiguration config = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+ return DistributionEngineInitTask.buildTopicName(config.getDistributionNotifTopicName(), envName);
+
+ }
+
+ public static String getStatusTopicName(String envName) {
+ DistributionEngineConfiguration config = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+ return DistributionEngineInitTask.buildTopicName(config.getDistributionStatusTopicName(), envName);
+
+ }
+
+ protected CambriaErrorResponse unRegisterDistributionClientFromTopic(RegistrationRequest unRegistrationRequest, SubscriberTypeEnum subscriberType) {
+ DistributionEngineConfiguration config = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+ String topicName;
+ if (subscriberType == SubscriberTypeEnum.PRODUCER) {
+ topicName = getStatusTopicName(unRegistrationRequest.getDistrEnvName());
+ } else {
+ topicName = getNotificationTopicName(unRegistrationRequest.getDistrEnvName());
+
+ }
+ log.debug("unregistering client as {} , from topic: {}", subscriberType.name(), topicName);
+ return getCambriaHandler().unRegisterFromTopic(config.getUebServers(), topicName, config.getUebPublicKey(), config.getUebSecretKey(), unRegistrationRequest.getApiPublicKey(), subscriberType);
+ }
+
+ private TopicRegistrationResponse buildTopicResponse(RegistrationRequest registrationRequest) {
+ DistributionEngineConfiguration config = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+ String statusTopicName = DistributionEngineInitTask.buildTopicName(config.getDistributionStatusTopicName(), registrationRequest.getDistrEnvName());
+ String notificationTopicName = DistributionEngineInitTask.buildTopicName(config.getDistributionNotifTopicName(), registrationRequest.getDistrEnvName());
+
+ TopicRegistrationResponse topicResponse = new TopicRegistrationResponse();
+ topicResponse.setDistrNotificationTopicName(notificationTopicName);
+ topicResponse.setDistrStatusTopicName(statusTopicName);
+ return topicResponse;
+ }
+
+ protected CambriaErrorResponse registerDistributionClientToTopic(Wrapper<Response> responseWrapper, RegistrationRequest registrationRequest, SubscriberTypeEnum subscriberType) {
+ DistributionEngineConfiguration config = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration();
+ String topicName, errorMsg;
+
+ // Register for notifications as consumer
+ if (subscriberType == SubscriberTypeEnum.CONSUMER) {
+ topicName = DistributionEngineInitTask.buildTopicName(config.getDistributionNotifTopicName(), registrationRequest.getDistrEnvName());
+ errorMsg = "registration of subscriber to topic:" + topicName + " as consumer failed";
+ }
+ // Register for status as producer
+ else {
+ topicName = DistributionEngineInitTask.buildTopicName(config.getDistributionStatusTopicName(), registrationRequest.getDistrEnvName());
+ errorMsg = "registration of subscriber to topic:" + topicName + " as producer failed";
+ }
+ log.debug("registering client as {} , from topic: {}", subscriberType.name(), topicName);
+ CambriaErrorResponse registerToTopic = getCambriaHandler().registerToTopic(config.getUebServers(), topicName, config.getUebPublicKey(), config.getUebSecretKey(), registrationRequest.getApiPublicKey(), subscriberType);
+
+ if (registerToTopic.getOperationStatus() != CambriaOperationStatus.OK) {
+ Response failedRegistrationResponse = buildErrorResponse(getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, REGISTER_IN_DISTRIBUTION_ENGINE, errorMsg);
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(REGISTER_IN_DISTRIBUTION_ENGINE, errorMsg);
+ responseWrapper.setInnerElement(failedRegistrationResponse);
+ }
+ return registerToTopic;
+ }
+
+ protected Response buildErrorResponse(ResponseFormat requestErrorWrapper) {
+ Response response = Response.status(requestErrorWrapper.getStatus()).entity(gson.toJson(requestErrorWrapper.getRequestError())).build();
+ return response;
+ }
+
+ public ResponseFormatManager getResponseFormatManager() {
+ return responseFormatManager;
+ }
+
+ public DistributionEngine getDistributionEngine() {
+ return distributionEngine;
+ }
+
+ public CambriaHandler getCambriaHandler() {
+ if (cambriaHandler == null) {
+ cambriaHandler = new CambriaHandler();
+ }
+ return cambriaHandler;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/CambriaOperationStatus.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/CambriaOperationStatus.java
new file mode 100644
index 0000000000..a6a6602cb0
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/CambriaOperationStatus.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.api.client;
+
+public enum CambriaOperationStatus {
+ OK, CONNNECTION_ERROR, NOT_FOUND, TOPIC_ALREADY_EXIST, OBJECT_NOT_FOUND, INTERNAL_SERVER_ERROR, AUTHENTICATION_ERROR, UNKNOWN_HOST_ERROR,
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/RegistrationRequest.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/RegistrationRequest.java
new file mode 100644
index 0000000000..ef14efe4f8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/RegistrationRequest.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.api.client;
+
+public class RegistrationRequest {
+ String apiPublicKey;
+ String distrEnvName;
+
+ public RegistrationRequest(String apiPublicKey, String distrEnvName) {
+ this.apiPublicKey = apiPublicKey;
+ this.distrEnvName = distrEnvName;
+ }
+
+ public String getApiPublicKey() {
+ return apiPublicKey;
+ }
+
+ public String getDistrEnvName() {
+ return distrEnvName;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/ServerListResponse.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/ServerListResponse.java
new file mode 100644
index 0000000000..267a691e29
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/ServerListResponse.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.api.client;
+
+import java.util.List;
+
+public class ServerListResponse {
+
+ private List<String> uebServerList;
+
+ public List<String> getUebServerList() {
+ return uebServerList;
+ }
+
+ public void setUebServerList(List<String> uebServerList) {
+ this.uebServerList = uebServerList;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicRegistrationResponse.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicRegistrationResponse.java
new file mode 100644
index 0000000000..e2a34a19d5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicRegistrationResponse.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.api.client;
+
+public class TopicRegistrationResponse {
+ String distrNotificationTopicName;
+ String distrStatusTopicName;
+
+ public void setDistrNotificationTopicName(String distrNotificationTopicName) {
+ this.distrNotificationTopicName = distrNotificationTopicName;
+ }
+
+ public void setDistrStatusTopicName(String distrStatusTopicName) {
+ this.distrStatusTopicName = distrStatusTopicName;
+ }
+
+ public String getDistrNotificationTopicName() {
+ return distrNotificationTopicName;
+ }
+
+ public String getDistrStatusTopicName() {
+ return distrStatusTopicName;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicUnregistrationResponse.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicUnregistrationResponse.java
new file mode 100644
index 0000000000..ffb9f9352f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/api/client/TopicUnregistrationResponse.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.api.client;
+
+public class TopicUnregistrationResponse {
+ String distrNotificationTopicName;
+ String distrStatusTopicName;
+ CambriaOperationStatus notificationUnregisterResult;
+ CambriaOperationStatus statusUnregisterResult;
+
+ public TopicUnregistrationResponse(String distrNotificationTopicName, String distrStatusTopicName, CambriaOperationStatus notificationUnregisterResult, CambriaOperationStatus statusUnregisterResult) {
+ super();
+ this.distrNotificationTopicName = distrNotificationTopicName;
+ this.distrStatusTopicName = distrStatusTopicName;
+ this.notificationUnregisterResult = notificationUnregisterResult;
+ this.statusUnregisterResult = statusUnregisterResult;
+ }
+
+ public String getDistrNotificationTopicName() {
+ return distrNotificationTopicName;
+ }
+
+ public String getDistrStatusTopicName() {
+ return distrStatusTopicName;
+ }
+
+ public CambriaOperationStatus getNotificationUnregisterResult() {
+ return notificationUnregisterResult;
+ }
+
+ public CambriaOperationStatus getStatusUnregisterResult() {
+ return statusUnregisterResult;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionCatalogServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionCatalogServlet.java
new file mode 100644
index 0000000000..a5d1093648
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionCatalogServlet.java
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.servlet;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.servlets.BeGenericServlet;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Singleton
+public class DistributionCatalogServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(DistributionCatalogServlet.class.getName());
+
+ // *******************************************************
+ // Download (GET) artifacts
+ // **********************************************************/
+
+ @GET
+ @Path("/services/{serviceName}/{serviceVersion}/artifacts/{artifactName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download service artifact", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact downloaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Artifact not found") })
+ public Response downloadServiceArtifact(@PathParam("serviceName") final String serviceName, @PathParam("serviceVersion") final String serviceVersion, @PathParam("artifactName") final String artifactName,
+ @Context final HttpServletRequest request) {
+ Response response = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ AuditingActionEnum auditingActionEnum = AuditingActionEnum.DISTRIBUTION_ARTIFACT_DOWNLOAD;
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, instanceIdHeader);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, requestURI);
+
+ if (instanceIdHeader == null || instanceIdHeader.isEmpty()) {
+ log.debug("Missing X-ECOMP-InstanceID header");
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<byte[], ResponseFormat> downloadRsrcArtifactEither = artifactsLogic.downloadServiceArtifactByNames(serviceName, serviceVersion, artifactName);
+ if (downloadRsrcArtifactEither.isRight()) {
+ ResponseFormat responseFormat = downloadRsrcArtifactEither.right().value();
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ response = buildErrorResponse(responseFormat);
+ } else {
+ byte[] value = downloadRsrcArtifactEither.left().value();
+ InputStream is = new ByteArrayInputStream(value);
+
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.CONTENT_DISPOSITION_HEADER, getContentDispositionValue(artifactName));
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ response = buildOkResponse(responseFormat, is, headers);
+ }
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "download Murano package artifact for service - external API");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("download Murano package artifact for service - external API");
+ log.debug("download artifact failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/services/{serviceName}/{serviceVersion}/resources/{resourceName}/{resourceVersion}/artifacts/{artifactName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download resource artifact", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact downloaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Artifact not found") })
+ public Response downloadResourceArtifact(@PathParam("serviceName") final String serviceName, @PathParam("serviceVersion") final String serviceVersion, @PathParam("resourceName") final String resourceName,
+ @PathParam("resourceVersion") final String resourceVersion, @PathParam("artifactName") final String artifactName, @Context final HttpServletRequest request) {
+ Response response = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ AuditingActionEnum auditingActionEnum = AuditingActionEnum.DISTRIBUTION_ARTIFACT_DOWNLOAD;
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, instanceIdHeader);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, requestURI);
+
+ if (instanceIdHeader == null || instanceIdHeader.isEmpty()) {
+ log.debug("Missing X-ECOMP-InstanceID header");
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<byte[], ResponseFormat> downloadRsrcArtifactEither = artifactsLogic.downloadRsrcArtifactByNames(serviceName, serviceVersion, resourceName, resourceVersion, artifactName);
+ if (downloadRsrcArtifactEither.isRight()) {
+ ResponseFormat responseFormat = downloadRsrcArtifactEither.right().value();
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ response = buildErrorResponse(responseFormat);
+ } else {
+ byte[] value = downloadRsrcArtifactEither.left().value();
+ // Returning 64-encoded as it was received during upload
+ InputStream is = new ByteArrayInputStream(value);
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.CONTENT_DISPOSITION_HEADER, getContentDispositionValue(artifactName));
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ response = buildOkResponse(responseFormat, is, headers);
+ }
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "download interface artifact for resource - external API");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("download interface artifact for resource - external API");
+ log.debug("download artifact failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ // --------------------------------
+
+ @GET
+ @Path("/services/{serviceName}/{serviceVersion}/resourceInstances/{resourceInstanceName}/artifacts/{artifactName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download resource artifact", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact downloaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Artifact not found") })
+ public Response downloadResourceInstanceArtifact(@PathParam("serviceName") final String serviceName, @PathParam("serviceVersion") final String serviceVersion, @PathParam("resourceInstanceName") final String resourceInstanceName,
+ @PathParam("artifactName") final String artifactName, @Context final HttpServletRequest request) {
+ Response response = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ AuditingActionEnum auditingActionEnum = AuditingActionEnum.DISTRIBUTION_ARTIFACT_DOWNLOAD;
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, instanceIdHeader);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, requestURI);
+
+ if (instanceIdHeader == null || instanceIdHeader.isEmpty()) {
+ log.debug("Missing X-ECOMP-InstanceID header");
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<byte[], ResponseFormat> downloadRsrcArtifactEither = artifactsLogic.downloadRsrcInstArtifactByNames(serviceName, serviceVersion, resourceInstanceName, artifactName);
+ if (downloadRsrcArtifactEither.isRight()) {
+ ResponseFormat responseFormat = downloadRsrcArtifactEither.right().value();
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ response = buildErrorResponse(responseFormat);
+ } else {
+ byte[] value = downloadRsrcArtifactEither.left().value();
+ // Returning 64-encoded as it was received during upload
+ InputStream is = new ByteArrayInputStream(value);
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.CONTENT_DISPOSITION_HEADER, getContentDispositionValue(artifactName));
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditDistributionDownload(responseFormat, auditingActionEnum, additionalParam);
+ response = buildOkResponse(responseFormat, is, headers);
+ }
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "download interface artifact for resource - external API");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("download interface artifact for resource - external API");
+ log.debug("download artifact failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ // --------------------------------
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionServlet.java
new file mode 100644
index 0000000000..b1556bafc3
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/distribution/servlet/DistributionServlet.java
@@ -0,0 +1,279 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.servlet;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.distribution.AuditHandler;
+import org.openecomp.sdc.be.distribution.DistributionBusinessLogic;
+import org.openecomp.sdc.be.distribution.api.client.RegistrationRequest;
+import org.openecomp.sdc.be.distribution.api.client.ServerListResponse;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.servlets.BeGenericServlet;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.HttpUtil;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1")
+@Singleton
+public class DistributionServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(DistributionServlet.class.getName());
+ @Resource
+ private DistributionBusinessLogic distributionLogic;
+
+ @GET
+ @Path("/distributionUebCluster")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getUebServerList(@Context final HttpServletRequest request, @HeaderParam(value = Constants.X_ECOMP_INSTANCE_ID_HEADER) String instanceId, @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId,
+ @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization, @HeaderParam(value = Constants.ACCEPT_HEADER) String accept) {
+ init(request);
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ Response response = null;
+ ResponseFormat responseFormat = null;
+ if (instanceId == null) {
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ response = buildErrorResponse(responseFormat);
+ getComponentsUtils().auditMissingInstanceId(AuditingActionEnum.GET_UEB_CLUSTER, responseFormat.getStatus().toString(), responseFormat.getFormattedMessage());
+ return response;
+ }
+ try {
+ Either<ServerListResponse, ResponseFormat> actionResponse = distributionLogic.getUebServerList();
+
+ if (actionResponse.isRight()) {
+ responseFormat = actionResponse.right().value();
+ response = buildErrorResponse(responseFormat);
+ } else {
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ response = buildOkResponse(responseFormat, actionResponse.left().value());
+ }
+
+ getComponentsUtils().auditGetUebCluster(AuditingActionEnum.GET_UEB_CLUSTER, instanceId, null, responseFormat.getStatus().toString(), responseFormat.getFormattedMessage());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "failed to get ueb serbver list from cofiguration");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("failed to get ueb serbver list from cofiguration");
+ log.debug("failed to get ueb serbver list from cofiguration", e);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ getComponentsUtils().auditGetUebCluster(AuditingActionEnum.GET_UEB_CLUSTER, instanceId, null, responseFormat.getStatus().toString(), responseFormat.getFormattedMessage());
+ response = buildErrorResponse(responseFormat);
+ return response;
+ }
+
+ }
+
+ /**
+ * Returns list of valid artifact types for validation done in the distribution client.<br>
+ * The list is the representation of the values of the enum ArtifactTypeEnum.
+ *
+ * @param request
+ * @param instanceId
+ * @param requestId
+ * @param authorization
+ * @param accept
+ * @return
+ */
+ @GET
+ @Path("/artifactTypes")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getValidArtifactTypes(@Context final HttpServletRequest request, @HeaderParam(value = Constants.X_ECOMP_INSTANCE_ID_HEADER) String instanceId, @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId,
+ @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization, @HeaderParam(value = Constants.ACCEPT_HEADER) String accept) {
+ init(request);
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ Response response = null;
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+
+ validateHeaders(responseWrapper, request, AuditingActionEnum.GET_VALID_ARTIFACT_TYPES);
+ if (responseWrapper.isEmpty()) {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), ArtifactTypeEnum.values());
+ } else {
+ response = responseWrapper.getInnerElement();
+ }
+ return response;
+ }
+
+ @POST
+ @Path("/registerForDistribution")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response registerForDistribution(@Context final HttpServletRequest request, String requestJson) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ init(request);
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ Wrapper<RegistrationRequest> registrationRequestWrapper = new Wrapper<>();
+
+ validateHeaders(responseWrapper, request, AuditingActionEnum.ADD_KEY_TO_TOPIC_ACL);
+
+ if (responseWrapper.isEmpty()) {
+ validateJson(responseWrapper, registrationRequestWrapper, requestJson);
+ }
+ if (responseWrapper.isEmpty()) {
+ validateEnv(responseWrapper, registrationRequestWrapper.getInnerElement().getDistrEnvName());
+ }
+
+ if (responseWrapper.isEmpty()) {
+ distributionLogic.handleRegistration(responseWrapper, registrationRequestWrapper.getInnerElement(), buildAuditHandler(request, registrationRequestWrapper.getInnerElement()));
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, DistributionBusinessLogic.REGISTER_IN_DISTRIBUTION_ENGINE, "registration validation failed");
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(DistributionBusinessLogic.REGISTER_IN_DISTRIBUTION_ENGINE, "registration validation failed");
+ }
+
+ return responseWrapper.getInnerElement();
+ }
+
+ @POST
+ @Path("/unRegisterForDistribution")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response unRegisterForDistribution(@Context final HttpServletRequest request, String requestJson) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ init(request);
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ Wrapper<RegistrationRequest> unRegistrationRequestWrapper = new Wrapper<>();
+
+ validateHeaders(responseWrapper, request, AuditingActionEnum.REMOVE_KEY_FROM_TOPIC_ACL);
+
+ if (responseWrapper.isEmpty()) {
+ validateJson(responseWrapper, unRegistrationRequestWrapper, requestJson);
+ }
+ if (responseWrapper.isEmpty()) {
+ validateEnv(responseWrapper, unRegistrationRequestWrapper.getInnerElement().getDistrEnvName());
+ }
+ if (responseWrapper.isEmpty()) {
+ distributionLogic.handleUnRegistration(responseWrapper, unRegistrationRequestWrapper.getInnerElement(), buildAuditHandler(request, unRegistrationRequestWrapper.getInnerElement()));
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDistributionEngineSystemError, DistributionBusinessLogic.UN_REGISTER_IN_DISTRIBUTION_ENGINE, "unregistration validation failed");
+ BeEcompErrorManager.getInstance().logBeDistributionEngineSystemError(DistributionBusinessLogic.UN_REGISTER_IN_DISTRIBUTION_ENGINE, "unregistration validation failed");
+ }
+
+ return responseWrapper.getInnerElement();
+ }
+
+ private void validateEnv(Wrapper<Response> responseWrapper, String distrEnvName) {
+
+ // DE194021
+ StorageOperationStatus environmentStatus = distributionLogic.getDistributionEngine().isEnvironmentAvailable();
+ // DE194021
+ // StorageOperationStatus environmentStatus =
+ // distributionLogic.getDistributionEngine().isEnvironmentAvailable(distrEnvName);
+ if (environmentStatus != StorageOperationStatus.OK) {
+ if (environmentStatus == StorageOperationStatus.DISTR_ENVIRONMENT_NOT_FOUND) {
+ Response missingHeaderResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.DISTRIBUTION_ENV_DOES_NOT_EXIST));
+ responseWrapper.setInnerElement(missingHeaderResponse);
+ } else {
+ Response missingHeaderResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ responseWrapper.setInnerElement(missingHeaderResponse);
+ }
+ }
+
+ }
+
+ private void init(HttpServletRequest request) {
+ if (distributionLogic == null) {
+ distributionLogic = getDistributionBL(request.getSession().getServletContext());
+ }
+ }
+
+ private void validateHeaders(Wrapper<Response> responseWrapper, HttpServletRequest request, AuditingActionEnum auditingAction) {
+ if (request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER) == null) {
+ Response missingHeaderResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID));
+ responseWrapper.setInnerElement(missingHeaderResponse);
+ // Audit
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditMissingInstanceId(auditingAction, responseFormat.getStatus().toString(), responseFormat.getFormattedMessage());
+
+ }
+
+ }
+
+ private void validateJson(Wrapper<Response> responseWrapper, Wrapper<RegistrationRequest> registrationRequestWrapper, String requestJson) {
+ if (requestJson == null || requestJson.isEmpty()) {
+ Response missingBodyResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.MISSING_BODY));
+ responseWrapper.setInnerElement(missingBodyResponse);
+ } else {
+ Either<RegistrationRequest, Exception> eitherRegistration = HttpUtil.convertJsonStringToObject(requestJson, RegistrationRequest.class);
+ if (eitherRegistration.isLeft()) {
+ RegistrationRequest registrationRequest = eitherRegistration.left().value();
+ if (registrationRequest.getApiPublicKey() == null) {
+ Response missingBodyResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.MISSING_PUBLIC_KEY));
+ responseWrapper.setInnerElement(missingBodyResponse);
+
+ } else if (registrationRequest.getDistrEnvName() == null) {
+ Response missingBodyResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.MISSING_ENV_NAME));
+ responseWrapper.setInnerElement(missingBodyResponse);
+ } else {
+ registrationRequestWrapper.setInnerElement(registrationRequest);
+ }
+ } else {
+ Response missingBodyResponse = buildErrorResponse(distributionLogic.getResponseFormatManager().getResponseFormat(ActionStatus.MISSING_BODY));
+ responseWrapper.setInnerElement(missingBodyResponse);
+ }
+ }
+
+ }
+
+ private DistributionBusinessLogic getDistributionBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ return webApplicationContext.getBean(DistributionBusinessLogic.class);
+ }
+
+ private AuditHandler buildAuditHandler(HttpServletRequest request, RegistrationRequest registrationRequest) {
+ return new AuditHandler(getComponentsUtils(), request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER), registrationRequest);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/EcompIntImpl.java b/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/EcompIntImpl.java
new file mode 100644
index 0000000000..5670239ef7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/EcompIntImpl.java
@@ -0,0 +1,376 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.ecomp;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.ecomp.converters.EcompRoleConverter;
+import org.openecomp.sdc.be.ecomp.converters.EcompUserConverter;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.ContextLoader;
+
+import org.openecomp.portalsdk.core.onboarding.crossapi.IPortalRestAPIService;
+import org.openecomp.portalsdk.core.onboarding.crossapi.PortalAPIException;
+import org.openecomp.portalsdk.core.restful.domain.EcompRole;
+import org.openecomp.portalsdk.core.restful.domain.EcompUser;
+
+import fj.data.Either;
+
+/*
+ * PortalAPIException(String message, Throwable cause);
+ */
+public class EcompIntImpl implements IPortalRestAPIService {
+ private static Logger log = LoggerFactory.getLogger(EcompIntImpl.class.getName());
+
+ public EcompIntImpl() {
+ log.debug("EcompIntImpl Class Instantiated");
+ }
+
+ @Override
+ public void pushUser(EcompUser user) throws PortalAPIException {
+ log.debug("Start handle request of ECOMP pushUser");
+ try {
+ if (user == null) {
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUser", "Recieved null for argument user", ErrorSeverity.INFO);
+ log.debug("Recieved null for argument user");
+ throw new PortalAPIException("Recieved null for argument user");
+ }
+
+ UserBusinessLogic userBusinessLogic = getUserBusinessLogic();
+
+ final String modifierAttId = "jh0003";
+ User modifier = new User();
+ modifier.setUserId(modifierAttId);
+ log.debug("modifier id is {}", modifierAttId);
+
+ Either<User, String> newASDCUser = EcompUserConverter.convertEcompUserToUser(user);
+ if (newASDCUser.isRight()) {
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUser", "Failed to convert user", ErrorSeverity.INFO);
+ log.debug("Failed to create user {}",user.toString());
+ throw new PortalAPIException("Failed to create user " + newASDCUser.right().value());
+ } else if (newASDCUser.left().value() == null) {
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUser", "NULL pointer returned from user converter", ErrorSeverity.INFO);
+ log.debug("Failed to create user {}", user.toString());
+ throw new PortalAPIException("Failed to create user " + newASDCUser.right().value());
+ }
+
+ User convertedAsdcUser = newASDCUser.left().value();
+ Either<User, ResponseFormat> createUserResponse = userBusinessLogic.createUser(modifier, convertedAsdcUser);
+
+ // ALREADY EXIST ResponseFormat
+ final String ALREADY_EXISTS_RESPONSE_ID = "SVC4006";
+
+ if (createUserResponse.isRight()) {
+ if (!createUserResponse.right().value().getMessageId().equals(ALREADY_EXISTS_RESPONSE_ID)) {
+ log.debug("Failed to create user {}", user.toString());
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUser", "Failed to create user", ErrorSeverity.ERROR);
+ throw new PortalAPIException("Failed to create user" + createUserResponse.right());
+ }
+ log.debug("User already exist {}", user.toString());
+ }
+ log.debug("User created {}", user.toString());
+ } catch (Exception e) {
+ log.debug("Failed to create user {}", user, e);
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUser", "Failed to create user", ErrorSeverity.ERROR);
+ throw new PortalAPIException("Failed to create user", e);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ *
+ * loginId - equals to userId
+ *
+ */
+ @Override
+ public void editUser(String loginId, EcompUser user) throws PortalAPIException {
+ log.debug("Start handle request of ECOMP editUser");
+
+ try {
+ if (user == null) {
+ log.debug("Recieved null for argument user");
+ BeEcompErrorManager.getInstance().logInvalidInputError("EditUser", "Recieved null for argument user", ErrorSeverity.INFO);
+ throw new PortalAPIException("Recieved null for argument user");
+ } else if (loginId == null) {
+ log.debug("Recieved null for argument loginId");
+ BeEcompErrorManager.getInstance().logInvalidInputError("EditUser", "Recieved null for argument loginId", ErrorSeverity.INFO);
+ throw new PortalAPIException("Recieved null for argument loginId");
+ }
+
+ UserBusinessLogic userBusinessLogic = getUserBusinessLogic();
+
+ if (user.getLoginId() != null && !user.getLoginId().equals(loginId)) {
+ log.debug("loginId and user loginId not equal");
+ BeEcompErrorManager.getInstance().logInvalidInputError("EditUser", "loginId and user loginId not equal", ErrorSeverity.INFO);
+ throw new PortalAPIException("loginId not equals to the user loginId field");
+ } else if (user.getLoginId() == null) {
+ user.setLoginId(loginId);
+ }
+
+ Either<User, String> asdcUser = EcompUserConverter.convertEcompUserToUser(user);
+ if (asdcUser.isRight()) {
+ log.debug("Failed to convert user");
+ BeEcompErrorManager.getInstance().logInvalidInputError("EditUser", "Failed to convert user", ErrorSeverity.INFO);
+ throw new PortalAPIException(asdcUser.right().value());
+ } else if (asdcUser.left().value() == null) {
+ log.debug("NULL pointer returned from user converter");
+ BeEcompErrorManager.getInstance().logInvalidInputError("EditUser", "NULL pointer returned from user converter", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to edit user");
+ }
+
+ Either<User, ResponseFormat> updateUserCredentialsResponse = userBusinessLogic.updateUserCredentials(asdcUser.left().value());
+
+ if (updateUserCredentialsResponse.isRight()) {
+ log.debug("Failed to updateUserCredentials");
+ BeEcompErrorManager.getInstance().logInvalidInputError("EditUser", "Failed to updateUserCredentials", ErrorSeverity.ERROR);
+ throw new PortalAPIException("Failed to edit user" + updateUserCredentialsResponse.right().value());
+ }
+ } catch (Exception e) {
+ log.debug("Failed to updateUserCredentials");
+ throw new PortalAPIException("Failed to edit user", e);
+ }
+
+ }
+
+ @Override
+ public EcompUser getUser(String loginId) throws PortalAPIException {
+ log.debug("Start handle request of ECOMP getUser");
+
+ try {
+
+ if (loginId == null) {
+ log.debug("Recieved null for argument loginId");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUser", "Recieved null for argument loginId", ErrorSeverity.INFO);
+ throw new PortalAPIException("Recieved null for argument loginId");
+ }
+
+ UserBusinessLogic userBusinessLogic = getUserBusinessLogic();
+
+ Either<User, ActionStatus> getUserResponse = userBusinessLogic.getUser(loginId, false);
+
+ if (getUserResponse.isRight()) {
+ log.debug("Failed to get User");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUser", "Failed to get User", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get User" + getUserResponse.right());
+ } else {
+ if (getUserResponse.left().value() != null) {
+ Either<EcompUser, String> ecompUser = EcompUserConverter.convertUserToEcompUser(getUserResponse.left().value());
+ if (ecompUser.isLeft() && ecompUser.left().value() != null) {
+ return ecompUser.left().value();
+ } else {
+ log.debug("Failed to get User");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUser", "Failed to get User", ErrorSeverity.INFO);
+ throw new PortalAPIException(ecompUser.right().value());
+ }
+ } else {
+ log.debug("Failed to get User");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUser", "Failed to get User", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get User" + getUserResponse.right());
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Failed to get User");
+ throw new PortalAPIException("Failed to get User", e);
+ }
+ }
+
+ @Override
+ public List<EcompUser> getUsers() throws PortalAPIException {
+ log.debug("Start handle request of ECOMP getUsers");
+
+ try {
+ UserBusinessLogic userBusinessLogic = getUserBusinessLogic();
+
+ final String modifierAttId = "jh0003";
+
+ Either<List<User>, ResponseFormat> getUsersResponse = userBusinessLogic.getUsersList(modifierAttId, null, null);
+
+ if (getUsersResponse.isRight()) {
+ log.debug("Failed to get Users");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUsers", "Failed to get users", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get Users" + getUsersResponse.right());
+ } else {
+ if (getUsersResponse.left().value() != null) {
+ List<EcompUser> ecompUserList = new LinkedList<>();
+ for (User user : getUsersResponse.left().value()) {
+ Either<EcompUser, String> ecompUser = EcompUserConverter.convertUserToEcompUser(user);
+ if (ecompUser.isRight()) {
+ log.debug("Failed to convert User {}", user);
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUsers", "Failed to convert User" + user.toString(), ErrorSeverity.WARNING);
+ continue;
+ } else if (ecompUser.left().value() == null) {
+ log.debug("Failed to convert User {}", user);
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUsers", "Failed to convert User" + user.toString(), ErrorSeverity.WARNING);
+ continue;
+ }
+ ecompUserList.add(ecompUser.left().value());
+ }
+ return ecompUserList;
+ } else {
+ log.debug("Failed to get users");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUsers", "Failed to get users", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get Users" + getUsersResponse.right());
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Failed to get users");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUsers", "Failed to get users", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get Users", e);
+ }
+ }
+
+ @Override
+ public List<EcompRole> getAvailableRoles() throws PortalAPIException {
+ log.debug("Start handle request of ECOMP getAvailableRoles");
+ try {
+ List<EcompRole> ecompRolesList = new LinkedList<>();
+ for (Role role : Role.values()) {
+ EcompRole ecompRole = new EcompRole();
+ ecompRole.setId(new Long(role.ordinal()));
+ ecompRole.setName(role.name());
+ ecompRolesList.add(ecompRole);
+ }
+
+ if (ecompRolesList.isEmpty()) {
+ throw new PortalAPIException();
+ }
+
+ return ecompRolesList;
+ } catch (Exception e) {
+ log.debug("Failed to fetch roles");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetAvailableRoles", "Failed to fetch roles", ErrorSeverity.INFO);
+ throw new PortalAPIException("Roles fetching failed", e);
+ }
+
+ }
+
+ /**
+ * The user role updated through this method only
+ */
+ @Override
+ public void pushUserRole(String loginId, List<EcompRole> roles) throws PortalAPIException {
+ log.debug("Start handle request of ECOMP pushUserRole");
+
+ final String modifierAttId = "jh0003";
+ User modifier = new User();
+ modifier.setUserId(modifierAttId);
+ log.debug("modifier id is {}", modifierAttId);
+
+ UserBusinessLogic userBusinessLogic = getUserBusinessLogic();
+
+ String updatedRole = null;
+
+ if (roles == null) {
+ throw new PortalAPIException("Error: Recieved null for roles");
+ } else if (roles.iterator().hasNext()) {
+ EcompRole ecompRole = roles.iterator().next();
+ updatedRole = EcompRoleConverter.convertEcompRoleToRole(ecompRole);
+ log.debug("pushing role: {} to user: {}", updatedRole, loginId);
+ } else {
+ log.debug("Error: No roles in List");
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUserRole", "Failed to fetch roles", ErrorSeverity.INFO);
+ throw new PortalAPIException("Error: No roles in List");
+ }
+
+ Either<User, ResponseFormat> updateUserRoleResponse = userBusinessLogic.updateUserRole(modifier, loginId, updatedRole);
+ if (updateUserRoleResponse.isRight()) {
+ log.debug("Error: Failed to update role");
+ BeEcompErrorManager.getInstance().logInvalidInputError("PushUserRole", "Failed to update role", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to update role" + updateUserRoleResponse.right().value().toString());
+ }
+ }
+
+ @Override
+ public List<EcompRole> getUserRoles(String loginId) throws PortalAPIException {
+ try {
+ log.debug("Start handle request of ECOMP getUserRoles");
+
+ UserBusinessLogic userBusinessLogic = getUserBusinessLogic();
+
+ Either<User, ActionStatus> getUserResponse = userBusinessLogic.getUser(loginId, false);
+
+ if (getUserResponse.isRight()) {
+ log.debug("Error: Failed to get Roles");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUserRoles", "Failed to get Roles", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get Roles" + getUserResponse.right());
+ } else {
+ if (getUserResponse.left().value() != null) {
+ Either<EcompUser, String> ecompUser = EcompUserConverter.convertUserToEcompUser(getUserResponse.left().value());
+ if (ecompUser.isRight()) {
+ log.debug("Error: Failed to convert Roles");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUserRoles", "Failed to convert Roles", ErrorSeverity.ERROR);
+ throw new PortalAPIException(ecompUser.right().value());
+ } else if (ecompUser.left().value() == null) {
+ log.debug("Error: Failed to convert Roles");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUserRoles", "Failed to convert Roles", ErrorSeverity.ERROR);
+ throw new PortalAPIException();
+ }
+
+ return new LinkedList<>(ecompUser.left().value().getRoles());
+ } else {
+ log.debug("Error: Failed to get Roles");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUserRoles", "Failed to get Roles", ErrorSeverity.ERROR);
+ throw new PortalAPIException("Failed to get Roles" + getUserResponse.right());
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Error: Failed to get Roles");
+ BeEcompErrorManager.getInstance().logInvalidInputError("GetUserRoles", "Failed to get Roles", ErrorSeverity.INFO);
+ throw new PortalAPIException("Failed to get Roles", e);
+ }
+ }
+
+ @Override
+ public boolean isAppAuthenticated(HttpServletRequest request) throws PortalAPIException {
+ // TODO Validation should be changed completely
+ final String USERNAME = request.getHeader("username");
+ final String PASSWORD = request.getHeader("password");
+
+ if (USERNAME != null && PASSWORD != null) {
+ if (!USERNAME.equals("") && !PASSWORD.equals("")) {
+ log.debug("User authenticated - Username: {} Password: {}", USERNAME, PASSWORD);
+ return true;
+ }
+ }
+
+ log.debug("User authentication failed");
+ return false;
+ }
+
+ private UserBusinessLogic getUserBusinessLogic() {
+ ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();
+ UserBusinessLogic userBusinessLogic = (UserBusinessLogic) ctx.getBean("userBusinessLogic");
+ return userBusinessLogic;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompRoleConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompRoleConverter.java
new file mode 100644
index 0000000000..ca970ce45f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompRoleConverter.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.ecomp.converters;
+
+import org.openecomp.sdc.be.user.Role;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.openecomp.portalsdk.core.restful.domain.EcompRole;
+
+public final class EcompRoleConverter {
+
+ private static Logger log = LoggerFactory.getLogger(EcompRoleConverter.class.getName());
+
+ private EcompRoleConverter() {
+ }
+
+ // TODO Add Either or Exception in case of convertation failure
+ public static String convertEcompRoleToRole(EcompRole ecompRole) {
+
+ log.debug("converting role");
+ if (ecompRole == null) {
+ log.debug("recieved null for roles");
+ return null;
+ }
+
+ for (Role role : Role.values()) {
+ if (role.ordinal() == ecompRole.getId()) {
+ return role.name();
+ }
+ }
+ log.debug("no roles converted");
+ return null;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompUserConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompUserConverter.java
new file mode 100644
index 0000000000..e83b53c2a4
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/ecomp/converters/EcompUserConverter.java
@@ -0,0 +1,118 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.ecomp.converters;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.user.Role;
+
+import org.openecomp.portalsdk.core.restful.domain.EcompRole;
+import org.openecomp.portalsdk.core.restful.domain.EcompUser;
+
+import fj.data.Either;
+
+public final class EcompUserConverter {
+
+ private EcompUserConverter() {
+ }
+
+ // private static final EcompUserConverter instance = new
+ // EcompUserConverter();
+
+ /*
+ * public static EcompUserConverter getInsatnce() { return instance; }
+ */
+
+ public static Either<EcompUser, String> convertUserToEcompUser(User asdcUser) {
+ EcompUser convertedUser = new EcompUser();
+
+ if (asdcUser == null) {
+ return Either.right("User is null");
+ }
+
+ convertedUser.setFirstName(asdcUser.getFirstName());
+ convertedUser.setLastName(asdcUser.getLastName());
+ convertedUser.setLoginId(asdcUser.getUserId());
+ convertedUser.setOrgUserId(asdcUser.getUserId());
+ convertedUser.setEmail(asdcUser.getEmail());
+
+ if (asdcUser.getStatus().equals(UserStatusEnum.ACTIVE)) {
+ convertedUser.setActive(true);
+ } else if (asdcUser.getStatus().equals(UserStatusEnum.INACTIVE)) {
+ convertedUser.setActive(false);
+ }
+
+ EcompRole convertedRole = new EcompRole();
+ for (Role role : Role.values()) {
+ if (role.name().equals(asdcUser.getRole()) || role.toString().equals(asdcUser.getRole())) {
+ convertedRole.setName(role.name());
+ convertedRole.setId(new Long(role.ordinal()));
+ break;
+ }
+ }
+
+ Set<EcompRole> convertedRoleSet = new HashSet<>();
+ convertedRoleSet.add(convertedRole);
+ convertedUser.setRoles(convertedRoleSet);
+
+ return Either.left(convertedUser);
+ }
+
+ public static Either<User, String> convertEcompUserToUser(EcompUser ecompUser) {
+ User convertedUser = new User();
+
+ if (ecompUser == null) {
+ return Either.right("EcompUser is null");
+ }
+
+ convertedUser.setFirstName(ecompUser.getFirstName());
+ convertedUser.setLastName(ecompUser.getLastName());
+
+ if (!ecompUser.getLoginId().isEmpty()) {
+ convertedUser.setUserId(ecompUser.getLoginId());
+ } else {
+ convertedUser.setUserId(ecompUser.getOrgUserId());
+ }
+
+ convertedUser.setEmail(ecompUser.getEmail());
+
+ if (ecompUser.getRoles() != null) {
+ Iterator<EcompRole> iter = ecompUser.getRoles().iterator();
+
+ if (iter.hasNext()) {
+ String updatedRole = EcompRoleConverter.convertEcompRoleToRole(iter.next());
+ convertedUser.setRole(updatedRole);
+ }
+ }
+
+ if (ecompUser.isActive()) {
+ convertedUser.setStatus(UserStatusEnum.ACTIVE);
+ } else {
+ convertedUser.setStatus(UserStatusEnum.INACTIVE);
+ }
+
+ return Either.left(convertedUser);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/ArtifactExternalServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/ArtifactExternalServlet.java
new file mode 100644
index 0000000000..ecdce79b29
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/ArtifactExternalServlet.java
@@ -0,0 +1,646 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.servlets.BeGenericServlet;
+import org.openecomp.sdc.be.servlets.RepresentationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+/**
+ * This Servlet serves external users operations on artifacts.
+ *
+ * @author mshitrit
+ *
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Singleton
+public class ArtifactExternalServlet extends BeGenericServlet {
+
+ @Context
+ private HttpServletRequest request;
+
+ private static Logger log = LoggerFactory.getLogger(ArtifactExternalServlet.class.getName());
+
+ /**
+ * Uploads an artifact to resource or service
+ *
+ * @param assetType
+ * @param uuid
+ * @return
+ */
+ @POST
+ @Path("/{assetType}/{uuid}/artifacts")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "uploads of artifact to a resource or service", httpMethod = "POST", notes = "uploads of artifact to a resource or service", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact uploaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response uploadArtifact(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @ApiParam(value = "json describe the artifact", required = true) String data) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("uploadArtifact: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("uploadArtifact: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty() && (userId == null || userId.isEmpty())) {
+ log.debug("uploadArtifact: Missing USER_ID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT);
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactEither = artifactsLogic.uploadArtifactToComponentByUUID(data, request, componentType, uuid, additionalParams);
+ if (uploadArtifactEither.isRight()) {
+ log.debug("failed to upload artifact");
+ responseFormat = uploadArtifactEither.right().value();
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(uploadArtifactEither.left().value());
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByString((String) representation));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to upload artifact to a resource or service";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * Uploads an artifact to resource instance
+ *
+ * @param assetType
+ * @param uuid
+ * @param resourceInstanceName
+ * @return
+ */
+ @POST
+ @Path("/{assetType}/{uuid}/resourceInstances/{resourceInstanceName}/artifacts")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "uploads an artifact to a resource instance", httpMethod = "POST", notes = "uploads an artifact to a resource instance", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact uploaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response uploadArtifactToInstance(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @PathParam("resourceInstanceName") final String resourceInstanceName,
+ @ApiParam(value = "json describe the artifact", required = true) String data) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("uploadArtifact: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("uploadArtifact: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty() && (userId == null || userId.isEmpty())) {
+ log.debug("uploadArtifact: Missing USER_ID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT);
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactEither = artifactsLogic.uploadArtifactToRiByUUID(data, request, componentType, uuid, resourceInstanceName, additionalParams);
+ if (uploadArtifactEither.isRight()) {
+ log.debug("failed to upload artifact");
+ responseFormat = uploadArtifactEither.right().value();
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(uploadArtifactEither.left().value());
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByString((String) representation));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalUploadArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to upload artifact to a resource instance";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * updates an artifact on a resource or service
+ *
+ * @param assetType
+ * @param uuid
+ * @param artifactUUID
+ * @return
+ */
+ @POST
+ @Path("/{assetType}/{uuid}/artifacts/{artifactUUID}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "updates an artifact on a resource or service", httpMethod = "POST", notes = "uploads of artifact to a resource or service", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact Updated"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response updateArtifact(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @PathParam("artifactUUID") final String artifactUUID,
+ @ApiParam(value = "json describe the artifact", required = true) String data) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("updateArtifact: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ // Mandatory
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("updateArtifact: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty() && (userId == null || userId.isEmpty())) {
+ log.debug("updateArtifact: Missing USER_ID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT);
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactEither = artifactsLogic.updateArtifactOnComponentByUUID(data, request, componentType, uuid, artifactUUID, additionalParams);
+ if (uploadArtifactEither.isRight()) {
+ log.debug("failed to update artifact");
+ responseFormat = uploadArtifactEither.right().value();
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(uploadArtifactEither.left().value());
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByString((String) representation));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to update artifact on a resource or service";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * updates an artifact on a resource instance
+ *
+ * @param assetType
+ * @param uuid
+ * @param resourceInstanceName
+ * @param artifactUUID
+ * @return
+ */
+ @POST
+ @Path("/{assetType}/{uuid}/resourceInstances/{resourceInstanceName}/artifacts/{artifactUUID}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "updates an artifact on a resource instance", httpMethod = "POST", notes = "uploads of artifact to a resource or service", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact Updated"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response updateArtifactOnResourceInstance(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @PathParam("resourceInstanceName") final String resourceInstanceName,
+ @PathParam("artifactUUID") final String artifactUUID, @ApiParam(value = "json describe the artifact", required = true) String data) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("updateArtifactOnResourceInstance: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("updateArtifactOnResourceInstance: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty() && (userId == null || userId.isEmpty())) {
+ log.debug("updateArtifactOnResourceInstance: Missing USER_ID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT);
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactEither = artifactsLogic.updateArtifactOnRiByUUID(data, request, componentType, uuid, resourceInstanceName, artifactUUID, additionalParams);
+ if (uploadArtifactEither.isRight()) {
+ log.debug("failed to update artifact");
+ responseFormat = uploadArtifactEither.right().value();
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(uploadArtifactEither.left().value());
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByString((String) representation));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalUpdateArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to update artifact on resource instance";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * deletes an artifact of a resource or service
+ *
+ * @param assetType
+ * @param uuid
+ * @param artifactUUID
+ * @return
+ */
+ @DELETE
+ @Path("/{assetType}/{uuid}/artifacts/{artifactUUID}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "deletes an artifact of a resource or service", httpMethod = "DELETE", notes = "deletes an artifact of a resource or service", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact Deleted"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response deleteArtifact(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @PathParam("artifactUUID") final String artifactUUID) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("deleteArtifact: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("deleteArtifact: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty() && (userId == null || userId.isEmpty())) {
+ log.debug("deleteArtifact: Missing USER_ID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT);
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactEither = artifactsLogic.deleteArtifactOnComponentByUUID(request, componentType, uuid, artifactUUID, additionalParams);
+ if (uploadArtifactEither.isRight()) {
+ log.debug("failed to delete artifact");
+ responseFormat = uploadArtifactEither.right().value();
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(uploadArtifactEither.left().value());
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByString((String) representation));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to delete an artifact of a resource or service";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * deletes an artifact of a resource instance
+ *
+ * @param assetType
+ * @param uuid
+ * @param resourceInstanceName
+ * @return
+ */
+ @DELETE
+ @Path("{assetType}/{uuid}/resourceInstances/{resourceInstanceName}/artifacts/{artifactUUID}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "deletes an artifact of a resource insatnce", httpMethod = "DELETE", notes = "deletes an artifact of a resource insatnce", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact Deleted"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response deleteArtifactOnResourceInstance(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @PathParam("resourceInstanceName") final String resourceInstanceName,
+ @PathParam("artifactUUID") final String artifactUUID) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("deleteArtifactOnResourceInsatnce: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParams = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("deleteArtifactOnResourceInsatnce: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty() && (userId == null || userId.isEmpty())) {
+ log.debug("deleteArtifactOnResourceInsatnce: Missing USER_ID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CONTACT);
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ArtifactDefinition, ResponseFormat> uploadArtifactEither = artifactsLogic.deleteArtifactOnRiByUUID(request, componentType, uuid, resourceInstanceName, artifactUUID, additionalParams);
+ if (uploadArtifactEither.isRight()) {
+ log.debug("failed to delete artifact");
+ responseFormat = uploadArtifactEither.right().value();
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(uploadArtifactEither.left().value());
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByString((String) representation));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalDeleteArtifact(responseFormat, componentType.getValue(), request, additionalParams);
+ responseWrapper.setInnerElement(buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to delete an artifact of a resource instance";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * downloads an artifact of a component (either a service or a resource) by artifactUUID
+ *
+ * @param assetType
+ * @param uuid
+ * @param artifactUUID
+ * @return
+ */
+ @GET
+ @Path("/{assetType}/{uuid}/artifacts/{artifactUUID}")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download component artifact", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact downloaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Artifact not found") })
+ public Response downloadComponentArtifact(
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("assetType") final String assetType,
+ @PathParam("uuid") final String uuid, @PathParam("artifactUUID") final String artifactUUID) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("downloadComponentArtifact: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("downloadComponentArtifact: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalDownloadArtifact(responseFormat, componentType.getValue(), request, additionalParam);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<byte[], ResponseFormat> downloadComponentArtifactEither = artifactsLogic.downloadComponentArtifactByUUIDs(componentType, uuid, artifactUUID, additionalParam);
+ if (downloadComponentArtifactEither.isRight()) {
+ responseFormat = downloadComponentArtifactEither.right().value();
+ getComponentsUtils().auditExternalDownloadArtifact(responseFormat, componentType.getValue(), request, additionalParam);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ byte[] value = downloadComponentArtifactEither.left().value();
+ InputStream is = new ByteArrayInputStream(value);
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByByteArray(value));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalDownloadArtifact(responseFormat, componentType.getValue(), request, additionalParam);
+ responseWrapper.setInnerElement(buildOkResponse(responseFormat, is, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to download an artifact of a resource or service";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ /**
+ * downloads an artifact of a resource instance of a component (either a service or a resource) by artifactUUID
+ *
+ * @param assetType
+ * @param uuid
+ * @param resourceInstanceName
+ * @param artifactUUID
+ * @return
+ */
+ @GET
+ @Path("/{assetType}/{uuid}/resourceInstances/{resourceInstanceName}/artifacts/{artifactUUID}")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download resource instance artifact", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact downloaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Artifact not found") })
+ public Response downloadResourceInstanceArtifact(
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("assetType") final String assetType,
+ @PathParam("uuid") final String uuid, @PathParam("resourceInstanceName") final String resourceInstanceName, @PathParam("artifactUUID") final String artifactUUID) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ ResponseFormat responseFormat;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ if (componentType == null) {
+ log.debug("downloadResourceInstanceArtifact: assetType parameter {} is not valid", assetType);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<>(AuditingFieldsKeysEnum.class);
+
+ if (responseWrapper.isEmpty() && (instanceIdHeader == null || instanceIdHeader.isEmpty())) {
+ log.debug("downloadResourceInstanceArtifact: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalDownloadArtifact(responseFormat, componentType.getValue(), request, additionalParam);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ }
+ if (responseWrapper.isEmpty()) {
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<byte[], ResponseFormat> downloadResourceArtifactEither = artifactsLogic.downloadResourceInstanceArtifactByUUIDs(componentType, uuid, resourceInstanceName, artifactUUID, additionalParam);
+ if (downloadResourceArtifactEither.isRight()) {
+ responseFormat = downloadResourceArtifactEither.right().value();
+ getComponentsUtils().auditExternalDownloadArtifact(responseFormat, componentType.getValue(), request, additionalParam);
+ responseWrapper.setInnerElement(buildErrorResponse(responseFormat));
+ } else {
+ byte[] value = downloadResourceArtifactEither.left().value();
+ InputStream is = new ByteArrayInputStream(value);
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.MD5_HEADER, GeneralUtility.calculateMD5ByByteArray(value));
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalDownloadArtifact(responseFormat, componentType.getValue(), request, additionalParam);
+ responseWrapper.setInnerElement(buildOkResponse(responseFormat, is, headers));
+ }
+ } catch (Exception e) {
+ final String message = "failed to download an artifact of a resource instance";
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
+ log.debug(message, e);
+ responseWrapper.setInnerElement(buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)));
+ }
+ }
+ return responseWrapper.getInnerElement();
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetMetadataConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetMetadataConverter.java
new file mode 100644
index 0000000000..20eac081c9
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetMetadataConverter.java
@@ -0,0 +1,381 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.distribution.servlet.DistributionCatalogServlet;
+import org.openecomp.sdc.be.externalapi.servlet.representation.ArtifactMetadata;
+import org.openecomp.sdc.be.externalapi.servlet.representation.AssetMetadata;
+import org.openecomp.sdc.be.externalapi.servlet.representation.ResourceAssetDetailedMetadata;
+import org.openecomp.sdc.be.externalapi.servlet.representation.ResourceAssetMetadata;
+import org.openecomp.sdc.be.externalapi.servlet.representation.ResourceInstanceMetadata;
+import org.openecomp.sdc.be.externalapi.servlet.representation.ServiceAssetDetailedMetadata;
+import org.openecomp.sdc.be.externalapi.servlet.representation.ServiceAssetMetadata;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("asset-metadata-utils")
+public class AssetMetadataConverter {
+ private static Logger log = LoggerFactory.getLogger(DistributionCatalogServlet.class.getName());
+
+ @Autowired
+ private ComponentsUtils componentsUtils;
+ @Autowired
+ private ResourceOperation resourceOperation;
+
+ /*
+ * Relative asset’s URL. Should be used in REST GET API to download the asset’s CSAR. https://{serverBaseURL}/{csarPath} can be obtained from (HttpServletRequest)request.getServerName()
+ */
+ public Either<List<? extends AssetMetadata>, ResponseFormat> convertToAssetMetadata(List<? extends Component> componentList, String serverBaseURL, boolean detailed) {
+ if (componentList == null || componentList.isEmpty()) {
+ return Either.left(new LinkedList<>());
+ }
+
+ List<AssetMetadata> retResList = new LinkedList<>();
+ Component component = componentList.iterator().next();
+ ComponentTypeEnum componentType = component.getComponentType();
+
+ for (Component curr : componentList) {
+
+ Either<? extends AssetMetadata, ResponseFormat> resMetaData = convertToMetadata(componentType, serverBaseURL, detailed, curr);
+
+ if (resMetaData.isRight()) {
+ return Either.right(resMetaData.right().value());
+ }
+
+ retResList.add(resMetaData.left().value());
+ }
+
+ return Either.left(retResList);
+
+ }
+
+ private Either<? extends AssetMetadata, ResponseFormat> convertToMetadata(ComponentTypeEnum componentType, String serverBaseURL, boolean detailed, Component curr) {
+
+ switch (componentType) {
+
+ case RESOURCE:
+
+ return generateResourceMeatdata(serverBaseURL, detailed, curr);
+
+ case SERVICE:
+
+ return generateServiceMetadata(serverBaseURL, detailed, curr);
+
+ // For future US's that include product
+ /*
+ * case PRODUCT: if (component instanceof Product) { List<ProductAssetMetadata> retResList = new LinkedList<>(); for (Component curr : componentList) { retResList.add(convertToProductAssetMetadata((Product) curr, serverBaseURL)); } return
+ * Either.left(retResList);
+ */
+ default:
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormatAdditionalProperty(ActionStatus.COMPONENT_INVALID_CATEGORY);
+ return Either.right(responseFormat);
+ }
+ }
+
+ private Either<? extends AssetMetadata, ResponseFormat> generateResourceMeatdata(String serverBaseURL, boolean detailed, Component curr) {
+ AssetMetadata metaData;
+ metaData = createMetadaObject(detailed, curr.getComponentType());
+ metaData = convertToResourceMetadata((ResourceAssetMetadata) metaData, (Resource) curr, serverBaseURL, detailed);
+
+ if (detailed) {
+ Either<ResourceAssetDetailedMetadata, StorageOperationStatus> converted = convertToResourceDetailedMetadata((ResourceAssetDetailedMetadata) metaData, (Resource) curr, serverBaseURL);
+ if (converted.isRight()) {
+ ActionStatus storageResponse = componentsUtils.convertFromStorageResponse(converted.right().value(), ComponentTypeEnum.RESOURCE);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(storageResponse);
+ return Either.right(responseFormat);
+ }
+ }
+
+ return Either.left(metaData);
+ }
+
+ private AssetMetadata createMetadaObject(boolean detailed, ComponentTypeEnum type) {
+ AssetMetadata metaData = null;
+ switch (type) {
+ case SERVICE:
+ if (!detailed) {
+ metaData = new ServiceAssetMetadata();
+ } else {
+ metaData = new ServiceAssetDetailedMetadata();
+ }
+ break;
+ case RESOURCE:
+ if (!detailed) {
+ metaData = new ResourceAssetMetadata();
+ } else {
+ metaData = new ResourceAssetDetailedMetadata();
+ }
+ break;
+ default:
+ break;
+ }
+ return metaData;
+ }
+
+ private Either<? extends AssetMetadata, ResponseFormat> generateServiceMetadata(String serverBaseURL, boolean detailed, Component curr) {
+ AssetMetadata metaData = createMetadaObject(detailed, curr.getComponentType());
+
+ metaData = convertToServiceAssetMetadata((ServiceAssetMetadata) metaData, (Service) curr, serverBaseURL, detailed);
+
+ if (detailed) {
+ Either<ServiceAssetDetailedMetadata, StorageOperationStatus> converted = convertToServiceDetailedMetadata((ServiceAssetDetailedMetadata) metaData, (Service) curr, serverBaseURL);
+ if (converted.isRight()) {
+ ActionStatus storageResponse = componentsUtils.convertFromStorageResponse(converted.right().value(), ComponentTypeEnum.RESOURCE);
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(storageResponse);
+ return Either.right(responseFormat);
+ }
+ }
+
+ return Either.left(metaData);
+ }
+
+ private <U extends AssetMetadata, T extends Component> U convertToAsset(U asset, T component, String serverBaseURL, boolean detailed) {
+ asset.setUuid(component.getUUID());
+ asset.setInvariantUUID(component.getInvariantUUID());
+ asset.setName(component.getName());
+ asset.setVersion(component.getVersion());
+ if (!detailed) {
+ asset.setToscaModelURL(serverBaseURL + "/" + component.getUUID() + "/toscaModel");
+ } else {
+ String toscaModelUrl = (new String(serverBaseURL)).replace("metadata", "toscaModel");
+ asset.setToscaModelURL(toscaModelUrl);
+ }
+
+ return asset;
+ }
+
+ private <T extends ResourceAssetMetadata> T convertToResourceMetadata(T assetToPopulate, Resource resource, String serverBaseURL, boolean detailed) {
+ assetToPopulate = convertToAsset(assetToPopulate, resource, serverBaseURL, detailed);
+ CategoryDefinition categoryDefinition = resource.getCategories().iterator().next();
+ assetToPopulate.setCategory(categoryDefinition.getName());
+
+ SubCategoryDefinition subCategoryDefinition = categoryDefinition.getSubcategories().iterator().next();
+
+ assetToPopulate.setSubCategory(subCategoryDefinition.getName());
+ assetToPopulate.setResourceType(resource.getResourceType().name());
+ assetToPopulate.setLifecycleState(resource.getLifecycleState().name());
+ assetToPopulate.setLastUpdaterUserId(resource.getLastUpdaterUserId());
+
+ return (T) assetToPopulate;
+ }
+
+ private <T extends ServiceAssetMetadata> T convertToServiceAssetMetadata(T assetToPopulate, Service service, String serverBaseURL, boolean detailed) {
+ assetToPopulate = convertToAsset(assetToPopulate, service, serverBaseURL, detailed);
+
+ CategoryDefinition categoryDefinition = service.getCategories().iterator().next();
+
+ assetToPopulate.setCategory(categoryDefinition.getName());
+ assetToPopulate.setLifecycleState(service.getLifecycleState().name());
+ assetToPopulate.setLastUpdaterUserId(service.getLastUpdaterUserId());
+ assetToPopulate.setDistributionStatus(service.getDistributionStatus().name());
+
+ return (T) assetToPopulate;
+ }
+
+ private <T extends ResourceAssetDetailedMetadata> Either<T, StorageOperationStatus> convertToResourceDetailedMetadata(T assetToPopulate, Resource resource, String serverBaseURL) {
+
+ List<ComponentInstance> componentInstances = resource.getComponentInstances();
+
+ if (componentInstances != null) {
+ Either<List<ResourceInstanceMetadata>, StorageOperationStatus> resourceInstanceMetadata = convertToResourceInstanceMetadata(componentInstances, ComponentTypeEnum.RESOURCE_PARAM_NAME, resource.getUUID());
+ if (resourceInstanceMetadata.isRight()) {
+ return Either.right(resourceInstanceMetadata.right().value());
+ }
+
+ assetToPopulate.setResources(resourceInstanceMetadata.left().value());
+ }
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = resource.getDeploymentArtifacts();
+ assetToPopulate = populateResourceWithArtifacts(assetToPopulate, resource, serverBaseURL, deploymentArtifacts);
+
+ assetToPopulate.setLastUpdaterFullName(resource.getLastUpdaterFullName());
+ assetToPopulate.setToscaResourceName(resource.getToscaResourceName());
+
+ return Either.left(assetToPopulate);
+ }
+
+ private <T extends ServiceAssetDetailedMetadata> Either<T, StorageOperationStatus> convertToServiceDetailedMetadata(T assetToPopulate, Service service, String serverBaseURL) {
+
+ List<ComponentInstance> componentInstances = service.getComponentInstances();
+
+ if (componentInstances != null) {
+ Either<List<ResourceInstanceMetadata>, StorageOperationStatus> resourceInstanceMetadata = convertToResourceInstanceMetadata(componentInstances, ComponentTypeEnum.SERVICE_PARAM_NAME, service.getUUID());
+ if (resourceInstanceMetadata.isRight()) {
+ return Either.right(resourceInstanceMetadata.right().value());
+ }
+
+ assetToPopulate.setResources(resourceInstanceMetadata.left().value());
+ }
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = service.getDeploymentArtifacts();
+ assetToPopulate = populateServiceWithArtifacts(assetToPopulate, service, deploymentArtifacts);
+
+ assetToPopulate.setLastUpdaterFullName(service.getLastUpdaterFullName());
+
+ return Either.left(assetToPopulate);
+ }
+
+ private <T extends ResourceAssetDetailedMetadata> T populateResourceWithArtifacts(T asset, Resource resource, String serverBaseURL, Map<String, ArtifactDefinition> artifacts) {
+
+ List<ArtifactMetadata> artifactMetaList = populateAssetWithArtifacts(resource, artifacts);
+
+ asset.setArtifacts(artifactMetaList);
+
+ return asset;
+ }
+
+ private <T extends ServiceAssetDetailedMetadata> T populateServiceWithArtifacts(T asset, Service service, Map<String, ArtifactDefinition> artifacts) {
+
+ List<ArtifactMetadata> artifactMetaList = populateAssetWithArtifacts(service, artifacts);
+
+ asset.setArtifacts(artifactMetaList);
+
+ return asset;
+ }
+
+ private List<ArtifactMetadata> populateAssetWithArtifacts(Component component, Map<String, ArtifactDefinition> artifacts) {
+ List<ArtifactMetadata> artifactMetaList = null;
+ if (artifacts != null) {
+ artifactMetaList = new LinkedList<>();
+ Collection<ArtifactDefinition> artefactDefList = artifacts.values();
+
+ for (ArtifactDefinition artifactDefinition : artefactDefList) {
+ if (artifactDefinition.getEsId() != null && !artifactDefinition.getEsId().isEmpty()) {
+ ArtifactMetadata convertedArtifactMetadata = convertToArtifactMetadata(artifactDefinition, ComponentTypeEnum.findParamByType(component.getComponentType()), component.getUUID(), null);
+ artifactMetaList.add(convertedArtifactMetadata);
+ }
+ }
+ }
+ return artifactMetaList.isEmpty() ? null : artifactMetaList;
+ }
+
+ private ArtifactMetadata convertToArtifactMetadata(ArtifactDefinition artifact, String componentType, String componentUUID, String resourceInstanceName) {
+ // /asdc/v1/catalog/{services/resources}/{componentUUID}/artifacts/{artifactUUID}
+ final String COMPONENT_ARTIFACT_URL = "/asdc/v1/catalog/%s/%s/artifacts/%s";
+
+ // /asdc/v1/catalog/{services/resources}/{componentUUID}/resourceInstances/{resourceInstanceName}/artifacts/{artifactUUID}
+ final String RESOURCE_INSTANCE_ARTIFACT_URL = "/asdc/v1/catalog/%s/%s/resourceInstances/%s/artifacts/%s";
+
+ ArtifactMetadata metadata = new ArtifactMetadata();
+
+ metadata.setArtifactName(artifact.getArtifactName());
+ metadata.setArtifactType(artifact.getArtifactType());
+
+ if (resourceInstanceName == null || resourceInstanceName.isEmpty()) {
+ metadata.setArtifactURL(String.format(COMPONENT_ARTIFACT_URL, componentType, componentUUID, artifact.getArtifactUUID()));
+ } else {
+ metadata.setArtifactURL(String.format(RESOURCE_INSTANCE_ARTIFACT_URL, componentType, componentUUID, resourceInstanceName, artifact.getArtifactUUID()));
+ }
+
+ metadata.setArtifactDescription(artifact.getDescription());
+ metadata.setArtifactTimeout(artifact.getTimeout() > 0 ? artifact.getTimeout() : null);
+ metadata.setArtifactChecksum(artifact.getArtifactChecksum());
+ metadata.setArtifactUUID(artifact.getArtifactUUID());
+ metadata.setArtifactVersion(artifact.getArtifactVersion());
+ metadata.setGeneratedFromUUID(artifact.getGeneratedFromId());
+
+ return metadata;
+ }
+
+ private Either<List<ResourceInstanceMetadata>, StorageOperationStatus> convertToResourceInstanceMetadata(List<ComponentInstance> componentInstances, String componentType, String componentUUID) {
+ List<ResourceInstanceMetadata> retList = new LinkedList<>();
+ Map<String, String> uuidDuplicatesMap = new HashMap<>();
+
+ for (ComponentInstance componentInstance : componentInstances) {
+ ResourceInstanceMetadata metadata = new ResourceInstanceMetadata();
+ String componentUid = componentInstance.getComponentUid();
+ String invariantUUID;
+
+ if (!uuidDuplicatesMap.containsKey(componentUid)) {
+ Either<String, StorageOperationStatus> getInvarUuidresponse = resourceOperation.getInvariantUUID(NodeTypeEnum.Resource, componentInstance.getComponentUid(), false);
+ if (getInvarUuidresponse.isRight()) {
+ log.debug("convertToResourceInstanceMetadata: Failed getting Invariant UUID");
+ return Either.right(getInvarUuidresponse.right().value());
+ } else {
+ invariantUUID = getInvarUuidresponse.left().value();
+ uuidDuplicatesMap.put(componentUid, invariantUUID);
+ }
+ } else {
+ invariantUUID = uuidDuplicatesMap.get(componentUid);
+ }
+
+ metadata.setResourceInvariantUUID(invariantUUID);
+ metadata.setResourceInstanceName(componentInstance.getName());
+ metadata.setResourceName(componentInstance.getComponentName());
+ metadata.setResourceVersion(componentInstance.getComponentVersion());
+ metadata.setResoucreType(componentInstance.getOriginType().getValue());
+ metadata.setResourceUUID(componentInstance.getComponentUid());
+
+ Collection<ArtifactDefinition> values = componentInstance.getDeploymentArtifacts().values();
+ LinkedList<ArtifactMetadata> artifactMetaList = new LinkedList<>();
+
+ for (ArtifactDefinition artifactDefinition : values) {
+ ArtifactMetadata converted = convertToArtifactMetadata(artifactDefinition, componentType, componentUUID, componentInstance.getNormalizedName());
+ artifactMetaList.add(converted);
+ }
+
+ metadata.setArtifacts(artifactMetaList);
+
+ retList.add(metadata);
+ }
+
+ return Either.left(retList);
+ }
+
+ // For future US to support Product
+ /*
+ * private ProductAssetMetadata convertToProductAssetMetadata(Product product, String serverBaseURL) { ProductAssetMetadata retProdAsset = new ProductAssetMetadata();
+ *
+ * retProdAsset = convertToAsset(retProdAsset, product, serverBaseURL); retProdAsset.setLifecycleState(product.getLifecycleState().name()); retProdAsset.setLastUpdaterUserId(product.getLastUpdaterUserId());
+ * retProdAsset.setActive(product.getIsActive()); retProdAsset.setContacts(product.getContacts());
+ *
+ * List<CategoryDefinition> categories = product.getCategories(); List<ProductCategoryGroupMetadata> categoryMetadataList = new LinkedList<>();
+ *
+ * if (categories == null || categories.isEmpty()) { return retProdAsset; } else { for (CategoryDefinition categoryDefinition : categories) { String categoryName = categoryDefinition.getName(); List<SubCategoryDefinition> subcategories =
+ * categoryDefinition.getSubcategories(); for (SubCategoryDefinition subCategoryDefinition : subcategories) { String subCategoryName = subCategoryDefinition.getName(); List<GroupDefinition> groups = product.getGroups(); for (GroupDefinition
+ * groupDefinition : groups) { String groupName = groupDefinition.getName(); categoryMetadataList.add(new ProductCategoryGroupMetadata(categoryName, subCategoryName, groupName)); } } } retProdAsset.setProductGroupings(categoryMetadataList);
+ * return retProdAsset; } }
+ */
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetsDataServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetsDataServlet.java
new file mode 100644
index 0000000000..1b8d6fdb75
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/AssetsDataServlet.java
@@ -0,0 +1,300 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.http.NameValuePair;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.externalapi.servlet.representation.AssetMetadata;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.servlets.BeGenericServlet;
+import org.openecomp.sdc.be.servlets.RepresentationUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Singleton
+public class AssetsDataServlet extends BeGenericServlet {
+
+ @Context
+ private HttpServletRequest request;
+
+ private AssetMetadataConverter assetMetadataUtils;
+ private static Logger log = LoggerFactory.getLogger(AssetsDataServlet.class.getName());
+
+ @GET
+ @Path("/{assetType}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Fetch list of assets", httpMethod = "GET", notes = "Returns list of assets", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Assets Fetched"), @ApiResponse(code = 400, message = "Invalid content / Missing content"), @ApiResponse(code = 401, message = "Authorization required"),
+ @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Asset not found") })
+ public Response getAssetList(@PathParam("assetType") final String assetType, @QueryParam("category") String category, @QueryParam("subCategory") String subCategory, @QueryParam("distributionStatus") String distributionStatus) {
+
+ Response response = null;
+ ResponseFormat responseFormat = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String query = request.getQueryString();
+ String requestURI = request.getRequestURI();
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ String serverBaseURL = request.getRequestURL().toString();
+
+ AuditingActionEnum auditingActionEnum = query == null ? AuditingActionEnum.GET_ASSET_LIST : AuditingActionEnum.GET_FILTERED_ASSET_LIST;
+
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, instanceIdHeader);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, query == null ? requestURI : requestURI + "?" + query);
+
+ // Mandatory
+ if (instanceIdHeader == null || instanceIdHeader.isEmpty()) {
+ log.debug("getAssetList: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ElementBusinessLogic elementLogic = getElementBL(context);
+
+ getAssetUtils(context);
+ Map<FilterKeyEnum, String> filters = new HashMap<FilterKeyEnum, String>();
+
+ if (category != null) {
+ filters.put(FilterKeyEnum.CATEGORY, category);
+ }
+ if (subCategory != null) {
+ filters.put(FilterKeyEnum.SUB_CATEGORY, subCategory);
+ }
+ if (distributionStatus != null) {
+ filters.put(FilterKeyEnum.DISTRIBUTION_STATUS, distributionStatus);
+ }
+
+ Either<List<? extends Component>, ResponseFormat> assetTypeData = elementLogic.getFilteredCatalogComponents(assetType, filters, query);
+
+ if (assetTypeData.isRight()) {
+ log.debug("getAssetList: Asset Fetching Failed");
+ responseFormat = assetTypeData.right().value();
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ } else {
+ log.debug("getAssetList: Asset Fetching Success");
+ Either<List<? extends AssetMetadata>, ResponseFormat> resMetadata = assetMetadataUtils.convertToAssetMetadata(assetTypeData.left().value(), serverBaseURL, false);
+ if (resMetadata.isRight()) {
+ log.debug("getAssetList: Asset conversion Failed");
+ responseFormat = resMetadata.right().value();
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+ Object result = RepresentationUtils.toRepresentation(resMetadata.left().value());
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+
+ response = buildOkResponse(responseFormat, result);
+ return response;
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Fetch filtered list of assets");
+ log.debug("getAssetList: Fetch list of assets failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/{assetType}/{uuid}/metadata")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Fetch metadata of asset by uuid", httpMethod = "GET", notes = "Returns metadata of asset", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Assets Fetched"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response getAssetListByUuid(@PathParam("assetType") final String assetType, @PathParam("uuid") final String uuid, @Context final HttpServletRequest request) {
+
+ Response response = null;
+ ResponseFormat responseFormat = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ AuditingActionEnum auditingActionEnum = AuditingActionEnum.GET_ASSET_METADATA;
+ String requestURI = request.getRequestURI();
+ String url = request.getMethod() + " " + requestURI;
+ log.debug("Start handle request of {}", url);
+ String serverBaseURL = request.getRequestURL().toString();
+
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, instanceIdHeader);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, requestURI);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, componentType.getValue());
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, uuid);
+
+ // Mandatory
+ if (instanceIdHeader == null || instanceIdHeader.isEmpty()) {
+ log.debug("getAssetList: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ ServletContext context = request.getSession().getServletContext();
+ ElementBusinessLogic elementLogic = getElementBL(context);
+ getAssetUtils(context);
+
+ Either<List<? extends Component>, ResponseFormat> assetTypeData = elementLogic.getCatalogComponentsByUuidAndAssetType(assetType, uuid);
+
+ if (assetTypeData.isRight()) {
+ log.debug("getAssetList: Asset Fetching Failed");
+ responseFormat = assetTypeData.right().value();
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+
+ return buildErrorResponse(responseFormat);
+ } else {
+ log.debug("getAssetList: Asset Fetching Success");
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, assetTypeData.left().value().iterator().next().getName());
+ Either<List<? extends AssetMetadata>, ResponseFormat> resMetadata = assetMetadataUtils.convertToAssetMetadata(assetTypeData.left().value(), serverBaseURL, true);
+ if (resMetadata.isRight()) {
+ log.debug("getAssetList: Asset conversion Failed");
+ responseFormat = resMetadata.right().value();
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+ Object result = RepresentationUtils.toRepresentation(resMetadata.left().value().iterator().next());
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+
+ response = buildOkResponse(responseFormat, result);
+ return response;
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Fetch filtered list of assets");
+ log.debug("getAssetList: Fetch list of assets failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ private void getAssetUtils(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ assetMetadataUtils = webApplicationContext.getBean(AssetMetadataConverter.class);
+ }
+
+ @GET
+ @Path("/{assetType}/{uuid}/toscaModel")
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Fetch asset csar", httpMethod = "GET", notes = "Returns asset csar", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Asset Model Fetched"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Asset not found") })
+ public Response getToscaModel(@PathParam("uuid") final String uuid,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("assetType") final String assetType,
+ @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization) {
+
+ String url = request.getRequestURI();
+ log.debug("Start handle request of {} {}", request.getMethod(), url);
+ Response response = null;
+ ResponseFormat responseFormat = null;
+ ServletContext context = request.getSession().getServletContext();
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
+ AuditingActionEnum auditingActionEnum = AuditingActionEnum.GET_TOSCA_MODEL;
+ String userId = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, userId);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, url);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, componentType.getValue());
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, uuid);
+
+ if (userId == null || userId.isEmpty()) {
+ log.debug("getToscaModel: Missing X-ECOMP-InstanceID header");
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ ComponentBusinessLogic componentBL = getComponentBL(componentType, context);
+
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> csarArtifact = componentBL.getToscaModelByComponentUuid(componentType, uuid, additionalParam);
+ if (csarArtifact.isRight()) {
+ responseFormat = csarArtifact.right().value();
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ response = buildErrorResponse(responseFormat);
+ } else {
+ byte[] value = csarArtifact.left().value().getRight();
+ InputStream is = new ByteArrayInputStream(value);
+ String contenetMD5 = GeneralUtility.calculateMD5ByByteArray(value);
+ Map<String, String> headers = new HashMap<>();
+ headers.put(Constants.CONTENT_DISPOSITION_HEADER, getContentDispositionValue(csarArtifact.left().value().getLeft()));
+ headers.put(Constants.MD5_HEADER, contenetMD5);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ response = buildOkResponse(responseFormat, is, headers);
+ }
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get asset tosca model");
+ log.debug("falied to get asset tosca model", e);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ response = buildErrorResponse(responseFormat);
+ getComponentsUtils().auditExternalGetAsset(responseFormat, auditingActionEnum, additionalParam);
+ return response;
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ArtifactMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ArtifactMetadata.java
new file mode 100644
index 0000000000..f4194cf2f5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ArtifactMetadata.java
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+public class ArtifactMetadata {
+ private String artifactName;
+ private String artifactType;
+ private String artifactURL;
+ private String artifactDescription;
+ private Integer artifactTimeout;
+ private String artifactChecksum;
+ private String artifactUUID;
+ private String artifactVersion;
+ private String generatedFromUUID;
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(String artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getArtifactURL() {
+ return artifactURL;
+ }
+
+ public void setArtifactURL(String artifactURL) {
+ this.artifactURL = artifactURL;
+ }
+
+ public String getArtifactDescription() {
+ return artifactDescription;
+ }
+
+ public void setArtifactDescription(String artifactDescription) {
+ this.artifactDescription = artifactDescription;
+ }
+
+ public Integer getArtifactTimeout() {
+ return artifactTimeout;
+ }
+
+ public void setArtifactTimeout(Integer artifactTimeout) {
+ this.artifactTimeout = artifactTimeout;
+ }
+
+ public String getArtifactChecksum() {
+ return artifactChecksum;
+ }
+
+ public void setArtifactChecksum(String artifactChecksum) {
+ this.artifactChecksum = artifactChecksum;
+ }
+
+ public String getArtifactUUID() {
+ return artifactUUID;
+ }
+
+ public void setArtifactUUID(String artifactUUID) {
+ this.artifactUUID = artifactUUID;
+ }
+
+ public String getArtifactVersion() {
+ return artifactVersion;
+ }
+
+ public void setArtifactVersion(String artifactVersion) {
+ this.artifactVersion = artifactVersion;
+ }
+
+ public String getGeneratedFromUUID() {
+ return generatedFromUUID;
+ }
+
+ public void setGeneratedFromUUID(String generatedFromUUID) {
+ this.generatedFromUUID = generatedFromUUID;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/AssetMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/AssetMetadata.java
new file mode 100644
index 0000000000..cb14f76aff
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/AssetMetadata.java
@@ -0,0 +1,129 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+public abstract class AssetMetadata implements IAssetMetadata {
+ private String uuid;
+ private String invariantUUID;
+ private String name;
+ private String version;
+ private String toscaModelURL;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# getUuid()
+ */
+ @Override
+ public String getUuid() {
+ return uuid;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# setUuid(java.lang.String)
+ */
+ @Override
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# getInvariantUUID()
+ */
+ @Override
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# setInvariantUUID(java.lang.String)
+ */
+ @Override
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# getName()
+ */
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# setName(java.lang.String)
+ */
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# getVersion()
+ */
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# setVersion(java.lang.String)
+ */
+ @Override
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# toscaModelURL()
+ */
+ @Override
+ public String getToscaModelURL() {
+ return toscaModelURL;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.distribution.servlet.representation.IAssetMetadata# toscaModelURL(java.lang.String)
+ */
+ @Override
+ public void setToscaModelURL(String toscaModelURL) {
+ this.toscaModelURL = toscaModelURL;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/IAssetMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/IAssetMetadata.java
new file mode 100644
index 0000000000..f95a8e9684
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/IAssetMetadata.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+public interface IAssetMetadata {
+
+ String getUuid();
+
+ void setUuid(String uuid);
+
+ String getInvariantUUID();
+
+ void setInvariantUUID(String invariantUUID);
+
+ String getName();
+
+ void setName(String name);
+
+ String getVersion();
+
+ void setVersion(String version);
+
+ String getToscaModelURL();
+
+ void setToscaModelURL(String toscaModelURL);
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductAssetMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductAssetMetadata.java
new file mode 100644
index 0000000000..d2d9c2c902
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductAssetMetadata.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+import java.util.List;
+
+public class ProductAssetMetadata extends AssetMetadata {
+ private String lifecycleState;
+ private String lastUpdaterUserId;
+ private boolean isActive;
+ private List<String> contacts;
+ private List<ProductCategoryGroupMetadata> productGroupings;
+
+ public String getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public void setLifecycleState(String lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ public boolean isActive() {
+ return isActive;
+ }
+
+ public void setActive(boolean isActive) {
+ this.isActive = isActive;
+ }
+
+ public List<String> getContacts() {
+ return contacts;
+ }
+
+ public void setContacts(List<String> contacts) {
+ this.contacts = contacts;
+ }
+
+ public List<ProductCategoryGroupMetadata> getProductGroupings() {
+ return productGroupings;
+ }
+
+ public void setProductGroupings(List<ProductCategoryGroupMetadata> productGroupings) {
+ this.productGroupings = productGroupings;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductCategoryGroupMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductCategoryGroupMetadata.java
new file mode 100644
index 0000000000..047f9d6a2c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ProductCategoryGroupMetadata.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+public class ProductCategoryGroupMetadata {
+ private String category;
+ private String subCategory;
+ private String group;
+
+ public ProductCategoryGroupMetadata(String category, String subCategory, String group) {
+ this.category = category;
+ this.subCategory = subCategory;
+ this.group = group;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getSubCategory() {
+ return subCategory;
+ }
+
+ public void setSubCategory(String subCategory) {
+ this.subCategory = subCategory;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetDetailedMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetDetailedMetadata.java
new file mode 100644
index 0000000000..ea282ec2e8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetDetailedMetadata.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+import java.util.List;
+
+public class ResourceAssetDetailedMetadata extends ResourceAssetMetadata {
+
+ private String lastUpdaterFullName;
+ private String toscaResourceName;
+ private List<ResourceInstanceMetadata> resources;
+ private List<ArtifactMetadata> artifacts;
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public String getToscaResourceName() {
+ return toscaResourceName;
+ }
+
+ public void setToscaResourceName(String toscaResourceName) {
+ this.toscaResourceName = toscaResourceName;
+ }
+
+ public List<ResourceInstanceMetadata> getResources() {
+ return resources;
+ }
+
+ public void setResources(List<ResourceInstanceMetadata> resources) {
+ this.resources = resources;
+ }
+
+ public List<ArtifactMetadata> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactMetadata> artifactMetaList) {
+ this.artifacts = artifactMetaList;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetMetadata.java
new file mode 100644
index 0000000000..2b75facc6c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceAssetMetadata.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+public class ResourceAssetMetadata extends AssetMetadata {
+ private String category;
+ private String subCategory;
+ private String resourceType;
+ private String lifecycleState;
+ private String lastUpdaterUserId;
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getSubCategory() {
+ return subCategory;
+ }
+
+ public void setSubCategory(String subCategory) {
+ this.subCategory = subCategory;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public void setLifecycleState(String lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceInstanceMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceInstanceMetadata.java
new file mode 100644
index 0000000000..a53422f311
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ResourceInstanceMetadata.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+import java.util.List;
+
+public class ResourceInstanceMetadata {
+ private String resourceInstanceName;
+ private String resourceName;
+ private String resourceInvariantUUID;
+ private String resourceVersion;
+ private String resoucreType;
+ private String resourceUUID;
+ private List<ArtifactMetadata> artifacts;
+
+ public String getResourceInstanceName() {
+ return resourceInstanceName;
+ }
+
+ public void setResourceInstanceName(String resourceInstanceName) {
+ this.resourceInstanceName = resourceInstanceName;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceInvariantUUID() {
+ return resourceInvariantUUID;
+ }
+
+ public void setResourceInvariantUUID(String resourceInvariantUUID) {
+ this.resourceInvariantUUID = resourceInvariantUUID;
+ }
+
+ public String getResourceVersion() {
+ return resourceVersion;
+ }
+
+ public void setResourceVersion(String resourceVersion) {
+ this.resourceVersion = resourceVersion;
+ }
+
+ public String getResoucreType() {
+ return resoucreType;
+ }
+
+ public void setResoucreType(String resoucreType) {
+ this.resoucreType = resoucreType;
+ }
+
+ public String getResourceUUID() {
+ return resourceUUID;
+ }
+
+ public void setResourceUUID(String resourceUUID) {
+ this.resourceUUID = resourceUUID;
+ }
+
+ public List<ArtifactMetadata> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactMetadata> artifacts) {
+ this.artifacts = artifacts;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetDetailedMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetDetailedMetadata.java
new file mode 100644
index 0000000000..edd88b495d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetDetailedMetadata.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+import java.util.List;
+
+public class ServiceAssetDetailedMetadata extends ServiceAssetMetadata {
+ private String lastUpdaterFullName;
+ private List<ResourceInstanceMetadata> resources;
+ private List<ArtifactMetadata> artifacts;
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public List<ResourceInstanceMetadata> getResources() {
+ return resources;
+ }
+
+ public void setResources(List<ResourceInstanceMetadata> resources) {
+ this.resources = resources;
+ }
+
+ public List<ArtifactMetadata> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactMetadata> artifacts) {
+ this.artifacts = artifacts;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetMetadata.java
new file mode 100644
index 0000000000..094f973553
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/representation/ServiceAssetMetadata.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.externalapi.servlet.representation;
+
+public class ServiceAssetMetadata extends AssetMetadata {
+ private String category;
+ private String lifecycleState;
+ private String lastUpdaterUserId;
+ private String distributionStatus;
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getLifecycleState() {
+ return lifecycleState;
+ }
+
+ public void setLifecycleState(String lifecycleState) {
+ this.lifecycleState = lifecycleState;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ public String getDistributionStatus() {
+ return distributionStatus;
+ }
+
+ public void setDistributionStatus(String distributionStatus) {
+ this.distributionStatus = distributionStatus;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/filters/BasicAuthenticationFilter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/filters/BasicAuthenticationFilter.java
new file mode 100644
index 0000000000..f7e1d758ac
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/filters/BasicAuthenticationFilter.java
@@ -0,0 +1,225 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.filters;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.StringTokenizer;
+
+import javax.annotation.Priority;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.commons.codec.binary.Base64;
+import org.openecomp.sdc.be.components.impl.ConsumerBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ConsumerDefinition;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.openecomp.sdc.security.Passwords;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@Priority(10)
+public class BasicAuthenticationFilter implements ContainerRequestFilter {
+
+ @Context
+ private HttpServletRequest sr;
+
+ protected Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ private String realm = "ASDC";
+
+ private static Logger log = LoggerFactory.getLogger(BasicAuthenticationFilter.class.getName());
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+
+ String authHeader = requestContext.getHeaderString(Constants.AUTHORIZATION_HEADER);
+ if (authHeader != null) {
+ StringTokenizer st = new StringTokenizer(authHeader);
+ if (st.hasMoreTokens()) {
+ String basic = st.nextToken();
+
+ if (basic.equalsIgnoreCase("Basic")) {
+ try {
+ String credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8");
+ log.debug("Credentials: {}", credentials);
+ checkUserCredentiles(requestContext, credentials);
+ } catch (UnsupportedEncodingException e) {
+ log.error("Authentication Filter Failed Couldn't retrieve authentication", e);
+ authInvalidHeaderError(requestContext);
+ }
+ } else {
+ log.error("Authentication Filter Failed Couldn't retrieve authentication, no basic autantication.");
+ authInvalidHeaderError(requestContext);
+ }
+ } else {
+ log.error("Authentication Filter Failed Couldn't retrieve authentication, no basic autantication.");
+ authInvalidHeaderError(requestContext);
+ }
+
+ } else {
+ log.error("Authentication Filter Failed no autharization header");
+ authRequiredError(requestContext);
+ }
+ }
+
+ private void checkUserCredentiles(ContainerRequestContext requestContext, String credentials) {
+ int p = credentials.indexOf(":");
+ if (p != -1) {
+ String _username = credentials.substring(0, p).trim();
+ String _password = credentials.substring(p + 1).trim();
+
+ ConsumerBusinessLogic consumerBL = getConsumerBusinessLogic();
+ if (consumerBL == null) {
+ log.error("Authentication Filter Failed to get consumerBL.");
+ requestContext.abortWith(Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build());
+ } else {
+ Either<ConsumerDefinition, ResponseFormat> result = consumerBL.getConsumer(_username);
+ validatePassword(requestContext, _username, _password, result);
+ }
+ } else {
+ log.error("Authentication Filter Failed Couldn't retrieve authentication, no basic autantication.");
+ authInvalidHeaderError(requestContext);
+
+ }
+ }
+
+ private void validatePassword(ContainerRequestContext requestContext, String _username, String _password, Either<ConsumerDefinition, ResponseFormat> result) {
+ if (result.isRight()) {
+ Integer status = result.right().value().getStatus();
+ if (status == Status.NOT_FOUND.getStatusCode()) {
+ log.error("Authentication Filter Failed Couldn't find user");
+ authUserNotFoundError(requestContext, _username);
+ } else {
+ log.error("Authentication Filter Failed to get consumerBL.");
+ requestContext.abortWith(Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ } else {
+ ConsumerDefinition consumerCredentials = result.left().value();
+ if (!Passwords.isExpectedPassword(_password, consumerCredentials.getConsumerSalt(), consumerCredentials.getConsumerPassword())) {
+ log.error("Authentication Filter Failed invalide password");
+ authInvalidePasswordError(requestContext, _username);
+ } else {
+ authSuccesessful(requestContext, _username);
+ }
+ }
+ }
+
+ private void authSuccesessful(ContainerRequestContext requestContext, String _username) {
+ ComponentsUtils componentUtils = getComponentsUtils();
+ if (componentUtils == null) {
+ log.error("Authentication Filter Failed to get component utils.");
+ requestContext.abortWith(Response.status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ componentUtils.auditAuthEvent(AuditingActionEnum.AUTH_REQUEST, requestContext.getUriInfo().getPath(), _username, AuthStatus.AUTH_SUCCESS.toString(), realm);
+ }
+
+ private void authInvalidePasswordError(ContainerRequestContext requestContext, String _username) {
+ ComponentsUtils componentUtils = getComponentsUtils();
+ if (componentUtils == null) {
+ log.error("Authentication Filter Failed to get component utils.");
+ requestContext.abortWith(Response.status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ componentUtils.auditAuthEvent(AuditingActionEnum.AUTH_REQUEST, requestContext.getUriInfo().getPath(), _username, AuthStatus.AUTH_FAILED_INVALID_PASSWORD.toString(), realm);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.AUTH_FAILED);
+ requestContext.abortWith(buildErrorResponse(responseFormat, false));
+ }
+
+ private void authUserNotFoundError(ContainerRequestContext requestContext, String _username) {
+ ComponentsUtils componentUtils = getComponentsUtils();
+ if (componentUtils == null) {
+ log.error("Authentication Filter Failed to get component utils.");
+ requestContext.abortWith(Response.status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ getComponentsUtils().auditAuthEvent(AuditingActionEnum.AUTH_REQUEST, requestContext.getUriInfo().getPath(), _username, AuthStatus.AUTH_FAILED_USER_NOT_FOUND.toString(), realm);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.AUTH_FAILED);
+ requestContext.abortWith(buildErrorResponse(responseFormat, false));
+ }
+
+ private void authInvalidHeaderError(ContainerRequestContext requestContext) {
+ ComponentsUtils componentUtils = getComponentsUtils();
+ if (componentUtils == null) {
+ log.error("Authentication Filter Failed to get component utils.");
+ requestContext.abortWith(Response.status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ getComponentsUtils().auditAuthEvent(AuditingActionEnum.AUTH_REQUEST, requestContext.getUriInfo().getPath(), "", AuthStatus.AUTH_FAILED_INVALID_AUTHENTICATION_HEADER.toString(), realm);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.AUTH_FAILED_INVALIDE_HEADER);
+ requestContext.abortWith(buildErrorResponse(responseFormat, false));
+ }
+
+ private void authRequiredError(ContainerRequestContext requestContext) {
+ ComponentsUtils componentUtils = getComponentsUtils();
+ if (componentUtils == null) {
+ log.error("Authentication Filter Failed to get component utils.");
+ requestContext.abortWith(Response.status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ getComponentsUtils().auditAuthEvent(AuditingActionEnum.AUTH_REQUEST, requestContext.getUriInfo().getPath(), "", AuthStatus.AUTH_REQUIRED.toString(), realm);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.AUTH_REQUIRED);
+ requestContext.abortWith(buildErrorResponse(responseFormat, true));
+ }
+
+ private ComponentsUtils getComponentsUtils() {
+ ServletContext context = sr.getSession().getServletContext();
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ ComponentsUtils componentsUtils = webApplicationContext.getBean(ComponentsUtils.class);
+ return componentsUtils;
+ }
+
+ private ConsumerBusinessLogic getConsumerBusinessLogic() {
+ ServletContext context = sr.getSession().getServletContext();
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ ConsumerBusinessLogic consumerBusinessLogic = webApplicationContext.getBean(ConsumerBusinessLogic.class);
+ return consumerBusinessLogic;
+ }
+
+ public enum AuthStatus {
+ AUTH_REQUIRED, AUTH_FAILED_USER_NOT_FOUND, AUTH_FAILED_INVALID_PASSWORD, AUTH_FAILED_INVALID_AUTHENTICATION_HEADER, AUTH_SUCCESS
+ }
+
+ protected Response buildErrorResponse(ResponseFormat requestErrorWrapper, boolean addWwwAuthenticationHeader) {
+ ResponseBuilder responseBuilder = Response.status(requestErrorWrapper.getStatus());
+ if (addWwwAuthenticationHeader) {
+ responseBuilder = responseBuilder.header("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
+ }
+ Response response = responseBuilder.entity(gson.toJson(requestErrorWrapper.getRequestError())).build();
+ return response;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/filters/BeServletFilter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/filters/BeServletFilter.java
new file mode 100644
index 0000000000..f6c11d062e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/filters/BeServletFilter.java
@@ -0,0 +1,202 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.filters;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import javax.annotation.Priority;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.GsonBuilder;
+
+@Provider
+@Priority(1)
+public class BeServletFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+ @Context
+ private HttpServletRequest sr;
+
+ private static Logger log = LoggerFactory.getLogger(BeServletFilter.class.getName());
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+ try {
+
+ MDC.clear();
+
+ // In case of 405 response code, this function is not entered, then
+ // we'll process
+ // the MDC fields and UUID during the response
+ ThreadLocalsHolder.setMdcProcessed(true);
+
+ // Timing HTTP request
+ ThreadLocalsHolder.setRequestStartTime(System.currentTimeMillis());
+
+ String uuid = processMdcFields(requestContext);
+
+ ThreadLocalsHolder.setUuid(uuid);
+
+ inHttpRequest();
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Error during request filter");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Error during request filter");
+ log.debug("Error during request filter: {} ", e);
+ }
+ }
+
+ @Override
+ public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
+ try {
+ // Formatting the response in case of 405
+ if (responseContext.getStatus() == Response.Status.METHOD_NOT_ALLOWED.getStatusCode()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NOT_ALLOWED);
+ responseContext.setEntity(new GsonBuilder().setPrettyPrinting().create().toJson(responseFormat.getRequestError()));
+ }
+
+ if (ThreadLocalsHolder.isMdcProcessed()) {
+ // filter() was executed during request - this is the regular
+ // flow
+ responseContext.getHeaders().add(Constants.X_ECOMP_REQUEST_ID_HEADER, ThreadLocalsHolder.getUuid());
+ Long startTime = ThreadLocalsHolder.getRequestStartTime();
+ if (startTime != null) {
+ long endTime = System.currentTimeMillis();
+ MDC.put("timer", Long.toString(endTime - startTime));
+ }
+ } else {
+ // this is the 405 response code case
+ // we have no MDC fields since filter() wasn't executed during
+ // request
+ String uuid = processMdcFields(requestContext);
+ responseContext.getHeaders().add(Constants.X_ECOMP_REQUEST_ID_HEADER, uuid);
+ }
+
+ outHttpResponse(responseContext);
+
+ // Cleaning up
+ MDC.clear();
+ ThreadLocalsHolder.cleanup();
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Error during request filter");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Error during request filter");
+ log.debug("Error during response filter: {} ", e);
+ }
+ }
+
+ private String processMdcFields(ContainerRequestContext requestContext) {
+ // userId for logging
+ String userId = requestContext.getHeaderString(Constants.USER_ID_HEADER);
+ MDC.put("userId", userId);
+
+ String serviceInstanceID = requestContext.getHeaderString(Constants.X_ECOMP_SERVICE_ID_HEADER);
+ MDC.put("serviceInstanceID", serviceInstanceID);
+
+ MDC.put("remoteAddr", sr.getRemoteAddr());
+ MDC.put("localAddr", sr.getLocalAddr());
+
+ // UUID
+ String uuid = requestContext.getHeaderString(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ if (uuid == null) {
+ // Generate the UUID
+ uuid = UUID.randomUUID().toString();
+
+ // Add to MDC for logging
+ MDC.put("uuid", uuid);
+
+ // This log message should already be with the UUID
+ uuidGeneration(uuid);
+
+ } else {
+ // According to Ella, in case this header exists, we don't have to
+ // perform any validations
+ // since it's not our responsibilty, so we log the UUID just as it
+ // was received.
+ MDC.put("uuid", uuid);
+ }
+ return uuid;
+ }
+
+ private ComponentsUtils getComponentsUtils() {
+ ServletContext context = this.sr.getSession().getServletContext();
+
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ return webApplicationContext.getBean(ComponentsUtils.class);
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void inHttpRequest() {
+ if (isInfoLog()) {
+ log.info("{} {} {}", sr.getMethod(), sr.getRequestURI(), sr.getProtocol());
+ } else {
+ log.debug("{} {} {}", sr.getMethod(), sr.getRequestURI(), sr.getProtocol());
+ }
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void outHttpResponse(ContainerResponseContext responseContext) {
+ if (isInfoLog()) {
+ log.info("{} {} {} SC=\"{}\"", sr.getMethod(), sr.getRequestURI(), sr.getProtocol(), responseContext.getStatus());
+ } else {
+ log.debug("{} {} {} SC=\"{}\"", sr.getMethod(), sr.getRequestURI(), sr.getProtocol(), responseContext.getStatus());
+ }
+ }
+
+ private boolean isInfoLog() {
+ boolean logRequest = true;
+ Configuration configuration = ConfigurationManager.getConfigurationManager().getConfiguration();
+ String requestURI = sr.getRequestURI();
+ if (requestURI != null && configuration.getUnLoggedUrls() != null) {
+ logRequest = !configuration.getUnLoggedUrls().contains(requestURI);
+ }
+
+ return logRequest;
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void uuidGeneration(String uuid) {
+ log.info("No requestID provided -> Generated UUID {}", uuid);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/filters/ComponentsAvailabilityFilter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/filters/ComponentsAvailabilityFilter.java
new file mode 100644
index 0000000000..c572e2e552
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/filters/ComponentsAvailabilityFilter.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.filters;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.Response.Status;
+
+import org.openecomp.sdc.be.components.impl.HealthCheckBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.HealthCheckInfo;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+@Priority(11)
+public class ComponentsAvailabilityFilter implements ContainerRequestFilter {
+
+ @Context
+ protected HttpServletRequest sr;
+ protected Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ private static Logger log = LoggerFactory.getLogger(ComponentsAvailabilityFilter.class.getName());
+
+ @Override
+ public void filter(ContainerRequestContext requestContext) throws IOException {
+
+ String requestUrl = requestContext.getUriInfo().getPath();
+ if (!requestUrl.equals("healthCheck")) {
+ List<HealthCheckInfo> beHealthCheckInfos = getBeHealthCheckInfos(this.sr.getSession().getServletContext());
+ ActionStatus status = getAggregateBeStatus(beHealthCheckInfos);
+
+ if (!status.equals(ActionStatus.OK)) {
+ log.error("Components Availability Filter Failed - ES/Cassandra is DOWN");
+ availabilityError(requestContext);
+ }
+ }
+
+ }
+
+ protected ActionStatus getAggregateBeStatus(List<HealthCheckInfo> beHealthCheckInfos) {
+ ActionStatus status = ActionStatus.OK;
+ for (HealthCheckInfo healthCheckInfo : beHealthCheckInfos) {
+ if (healthCheckInfo.getHealthCheckStatus().equals(HealthCheckStatus.DOWN)) {
+ status = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ }
+ return status;
+ }
+
+ protected List<HealthCheckInfo> getBeHealthCheckInfos(ServletContext servletContext) {
+
+ List<HealthCheckInfo> healthCheckInfos = new ArrayList<HealthCheckInfo>();
+ HealthCheckBusinessLogic healthCheckBusinessLogic = getHealthCheckBL(servletContext);
+ healthCheckBusinessLogic.getTitanHealthCheck(healthCheckInfos); // Titan
+ return healthCheckInfos;
+ }
+
+ protected ComponentsUtils getComponentsUtils() {
+ ServletContext context = sr.getSession().getServletContext();
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ ComponentsUtils componentsUtils = webApplicationContext.getBean(ComponentsUtils.class);
+ return componentsUtils;
+ }
+
+ protected void availabilityError(ContainerRequestContext requestContext) {
+ ComponentsUtils componentUtils = getComponentsUtils();
+ if (componentUtils == null) {
+ log.error("Components Availability Filter Failed to get component utils.");
+ requestContext.abortWith(Response.status(Status.INTERNAL_SERVER_ERROR).build());
+ }
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ ResponseBuilder responseBuilder = Response.status(responseFormat.getStatus());
+ Response response = responseBuilder.entity(gson.toJson(responseFormat.getRequestError())).build();
+ requestContext.abortWith(response);
+ }
+
+ private HealthCheckBusinessLogic getHealthCheckBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ HealthCheckBusinessLogic healthCheckBl = webApplicationContext.getBean(HealthCheckBusinessLogic.class);
+ return healthCheckBl;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java
new file mode 100644
index 0000000000..9e2abebdc5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java
@@ -0,0 +1,1461 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.impl;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.annotation.PostConstruct;
+import javax.servlet.http.HttpServletRequest;
+
+import org.codehaus.jackson.Version;
+import org.codehaus.jackson.map.DeserializationConfig;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.module.SimpleModule;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.AdditionalInformationEnum;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInfoParameterInfo;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ConsumerDefinition;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintJacksonDeserialiser;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.tosca.ToscaError;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("componentUtils")
+public class ComponentsUtils {
+
+ @javax.annotation.Resource
+ private IAuditingManager auditingManager;
+
+ private ResponseFormatManager responseFormatManager;
+
+ private static Logger log = LoggerFactory.getLogger(ComponentsUtils.class.getName());
+
+ @PostConstruct
+ public void Init() {
+ this.responseFormatManager = ResponseFormatManager.getInstance();
+ }
+
+ public IAuditingManager getAuditingManager() {
+ return auditingManager;
+ }
+
+ public void setAuditingManager(IAuditingManager auditingManager) {
+ this.auditingManager = auditingManager;
+ }
+
+ public <T> Either<T, ResponseFormat> convertJsonToObject(String data, User user, Class<T> clazz, AuditingActionEnum actionEnum) {
+ if (data == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("object is null after converting from json");
+ ResponseFormat responseFormat = getInvalidContentErrorAndAudit(user, actionEnum);
+ return Either.right(responseFormat);
+ }
+ try {
+ T obj = parseJsonToObject(data, clazz);
+ return Either.left(obj);
+ } catch (Exception e) {
+ // INVALID JSON
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("failed to convert from json {}", data, e);
+ ResponseFormat responseFormat = getInvalidContentErrorAndAudit(user, actionEnum);
+ return Either.right(responseFormat);
+ }
+ }
+
+ public static <T> T parseJsonToObject(String data, Class<T> clazz) {
+ Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+ Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
+ log.trace("convert json to object. json=\n{}", data);
+ T resource = gson.fromJson(data, clazz);
+ return resource;
+ }
+
+ public <T> Either<T, ResponseFormat> convertJsonToObjectUsingObjectMapper(String data, User user, Class<T> clazz, AuditingActionEnum actionEnum, ComponentTypeEnum typeEnum) {
+ T component = null;
+ ObjectMapper mapper = new ObjectMapper().configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ try {
+ log.trace("convert json to object. json=\n{}", data);
+
+ final SimpleModule module = new SimpleModule("customerSerializationModule", new Version(1, 0, 0, "static version"));
+ JsonDeserializer<PropertyConstraint> desrializer = new PropertyConstraintJacksonDeserialiser();
+ addDeserializer(module, PropertyConstraint.class, desrializer);
+ //
+ mapper.registerModule(module);
+
+ component = mapper.readValue(data, clazz);
+ if (component == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("object is null after converting from json");
+ ResponseFormat responseFormat = getInvalidContentErrorAndAuditComponent(user, actionEnum, typeEnum);
+ return Either.right(responseFormat);
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("failed to convert from json {}", data, e);
+ ResponseFormat responseFormat = getInvalidContentErrorAndAuditComponent(user, actionEnum, typeEnum);
+ return Either.right(responseFormat);
+ }
+ return Either.left(component);
+ }
+
+ public <T> void addDeserializer(SimpleModule module, Class<T> clazz, final JsonDeserializer<T> deserializer) {
+ module.addDeserializer(clazz, deserializer);
+ }
+
+ // Error response
+
+ public ResponseFormat getResponseFormat(ActionStatus actionStatus, String... params) {
+ return responseFormatManager.getResponseFormat(actionStatus, params);
+ }
+
+ /**
+ * Returns the response format of resource error with respective variables according to actionStatus. This is needed for cases where actionStatus is anonymously converted from storage operation, and the caller doesn't know what actionStatus he
+ * received. It's caller's Responsibility to fill the resource object passed to this function with needed fields.
+ *
+ * Note that RESOURCE_IN_USE case passes hardcoded "resource" string to the error parameter. This means that if Resource object will also be used for Service, this code needs to be refactored and we should tell Resource from Service.
+ *
+ * @param actionStatus
+ * @param resource
+ * @return
+ */
+ public ResponseFormat getResponseFormatByResource(ActionStatus actionStatus, Resource resource) {
+ if (resource == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case COMPONENT_VERSION_ALREADY_EXIST:
+ responseFormat = getResponseFormat(ActionStatus.COMPONENT_VERSION_ALREADY_EXIST, ComponentTypeEnum.RESOURCE.getValue(), resource.getVersion());
+ break;
+ case RESOURCE_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, resource.getName());
+ break;
+ case COMPONENT_NAME_ALREADY_EXIST:
+ responseFormat = getResponseFormat(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, ComponentTypeEnum.RESOURCE.getValue(), resource.getName());
+ break;
+ case COMPONENT_IN_USE:
+ responseFormat = getResponseFormat(ActionStatus.COMPONENT_IN_USE, ComponentTypeEnum.RESOURCE.name().toLowerCase(), resource.getUniqueId());
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public ResponseFormat getResponseFormatByResource(ActionStatus actionStatus, String resourceName) {
+ if (resourceName == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case RESOURCE_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, resourceName);
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public ResponseFormat getResponseFormatByCapabilityType(ActionStatus actionStatus, CapabilityTypeDefinition capabilityType) {
+ if (capabilityType == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case CAPABILITY_TYPE_ALREADY_EXIST:
+ responseFormat = getResponseFormat(ActionStatus.CAPABILITY_TYPE_ALREADY_EXIST, capabilityType.getType());
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ /**
+ * Returns the response format of resource error with respective variables according to actionStatus. This is needed for cases where actionStatus is anynomously converted from storage operation, and the caller doesn't know what actionStatus he
+ * received. It's caller's responisibility to fill the passed resource object with needed fields.
+ *
+ * @param actionStatus
+ * @param user
+ * @return
+ */
+ public ResponseFormat getResponseFormatByUser(ActionStatus actionStatus, User user) {
+ if (user == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat requestErrorWrapper;
+ switch (actionStatus) {
+ case INVALID_USER_ID:
+ requestErrorWrapper = getResponseFormat(actionStatus, user.getUserId());
+ break;
+ case INVALID_EMAIL_ADDRESS:
+ requestErrorWrapper = getResponseFormat(actionStatus, user.getEmail());
+ break;
+ case INVALID_ROLE:
+ requestErrorWrapper = getResponseFormat(actionStatus, user.getRole());
+ break;
+ case USER_NOT_FOUND:
+ case USER_ALREADY_EXIST:
+ case USER_INACTIVE:
+ case USER_HAS_ACTIVE_ELEMENTS:
+ requestErrorWrapper = getResponseFormat(actionStatus, user.getUserId());
+ break;
+ default:
+ requestErrorWrapper = getResponseFormat(actionStatus);
+ break;
+ }
+ return requestErrorWrapper;
+ }
+
+ public ResponseFormat getResponseFormatByUserId(ActionStatus actionStatus, String userId) {
+ User user = new User();
+ user.setUserId(userId);
+ return getResponseFormatByUser(actionStatus, user);
+ }
+
+ public ResponseFormat getResponseFormatByDE(ActionStatus actionStatus, String serviceId, String envName) {
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE:
+ responseFormat = getResponseFormat(ActionStatus.DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE, envName);
+ break;
+ case DISTRIBUTION_ENVIRONMENT_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.DISTRIBUTION_ENVIRONMENT_NOT_FOUND, envName);
+ break;
+ case DISTRIBUTION_ARTIFACT_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.DISTRIBUTION_ARTIFACT_NOT_FOUND, serviceId);
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public ResponseFormat getResponseFormatByArtifactId(ActionStatus actionStatus, String artifactId) {
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case RESOURCE_NOT_FOUND:
+ case ARTIFACT_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactId);
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public ResponseFormat getInvalidContentErrorAndAudit(User user, AuditingActionEnum actionEnum) {
+ ResponseFormat responseFormat = responseFormatManager.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ log.debug("audit before sending response");
+ auditResource(responseFormat, user, null, "", "", actionEnum, null);
+ return responseFormat;
+ }
+
+ public ResponseFormat getInvalidContentErrorAndAuditComponent(User user, AuditingActionEnum actionEnum, ComponentTypeEnum typeEnum) {
+ ResponseFormat responseFormat = responseFormatManager.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ log.debug("audit before sending response");
+ auditComponentAdmin(responseFormat, user, null, "", "", actionEnum, typeEnum);
+ return responseFormat;
+ }
+
+ public void auditResource(ResponseFormat responseFormat, User modifier, Resource resource, String prevState, String prevVersion, AuditingActionEnum actionEnum, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ if (actionEnum != null) {
+ log.trace("Inside auditing for audit action {}", actionEnum.name());
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+
+ updateUserFields(modifier, auditingFields);
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, ComponentTypeEnum.RESOURCE.getValue());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION, prevVersion);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE, prevState);
+ if (resource != null) {
+ // fields that are filled during creation and might still be
+ // empty
+ String resourceCurrVersion = (resource.getVersion() != null) ? resource.getVersion() : "";
+ String resourceCurrState = (resource.getLifecycleState() != null) ? resource.getLifecycleState().name() : "";
+ String uuid = (resource.getUUID() != null) ? resource.getUUID() : "";
+ String invariantUUID = (resource.getInvariantUUID() != null) ? resource.getInvariantUUID() : "";
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resource.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, resourceCurrVersion);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, resourceCurrState);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, uuid);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, invariantUUID);
+
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, "");
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ // In those specific cases we want some specific fields from
+ // resource object
+ switch (actionEnum) {
+ case IMPORT_RESOURCE:
+ if (resource != null) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE, resource.getToscaResourceName());
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE, "");
+ }
+ break;
+ default:
+ break;
+ }
+
+ // This is to add/overwrite anything set in this function if some
+ // params were passed from above,
+ // for example resourceName of resource import
+ if (additionalParams != null) {
+ auditingFields.putAll(additionalParams);
+ }
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+ }
+
+ private void updateUserFields(User modifier, EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ if (modifier != null) {
+ String firstName = modifier.getFirstName();
+ String lastName = modifier.getLastName();
+ if (firstName != null || lastName != null) {// to prevent "null
+ // null" names
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, firstName + " " + lastName);
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier.getUserId());
+ }
+ }
+
+ public void auditDistributionDownload(ResponseFormat responseFormat, AuditingActionEnum actionEnum, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ log.trace("Inside auditing for audit action {}", actionEnum.name());
+ int status = responseFormat.getStatus();
+ String message = "";
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ // This is to add/overwrite anything set in this function if some params
+ // were passed from above,
+ // for example resourceName of resource import
+ if (additionalParams != null) {
+ auditingFields.putAll(additionalParams);
+ }
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditExternalGetAsset(ResponseFormat responseFormat, AuditingActionEnum actionEnum, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ log.trace("Inside auditing for audit action {}", actionEnum.name());
+ int status = responseFormat.getStatus();
+ String message = "";
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ if (additionalParams != null) {
+ auditingFields.putAll(additionalParams);
+ }
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditExternalCrudApiArtifact(ResponseFormat responseFormat, String componentType, String actionEnum, HttpServletRequest request, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ log.trace("Inside auditing for audit action {}", actionEnum);
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+ int status = responseFormat.getStatus();
+ String message = "";
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, instanceIdHeader);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, requestURI);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, componentType);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ if (additionalParams != null) {
+ auditingFields.putAll(additionalParams);
+ }
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditExternalDownloadArtifact(ResponseFormat responseFormat, String componentType, HttpServletRequest request, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ auditExternalCrudApiArtifact(responseFormat, componentType, AuditingActionEnum.DOWNLOAD_ARTIFACT.getName(), request, additionalParams);
+ }
+
+ public void auditExternalUploadArtifact(ResponseFormat responseFormat, String componentType, HttpServletRequest request, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, request.getHeader(Constants.USER_ID_HEADER));
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID, "");
+ auditExternalCrudApiArtifact(responseFormat, componentType, AuditingActionEnum.ARTIFACT_UPLOAD_BY_API.getName(), request, additionalParams);
+ }
+
+ public void auditExternalUpdateArtifact(ResponseFormat responseFormat, String componentType, HttpServletRequest request, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, request.getHeader(Constants.USER_ID_HEADER));
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID, "");
+ auditExternalCrudApiArtifact(responseFormat, componentType, AuditingActionEnum.ARTIFACT_UPDATE_BY_API.getName(), request, additionalParams);
+ }
+
+ public void auditExternalDeleteArtifact(ResponseFormat responseFormat, String componentType, HttpServletRequest request, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, request.getHeader(Constants.USER_ID_HEADER));
+ additionalParams.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID, "");
+ auditExternalCrudApiArtifact(responseFormat, componentType, AuditingActionEnum.ARTIFACT_DELETE_BY_API.getName(), request, additionalParams);
+ }
+
+ public void auditCategory(ResponseFormat responseFormat, User modifier, String categoryName, String subCategoryName, String groupingName, AuditingActionEnum actionEnum, String componentType) {
+ log.trace("Inside auditing for audit action {}", actionEnum.name());
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ updateUserFields(modifier, auditingFields);
+ // String componentTypeStr = (componentTypeEnum != null ?
+ // componentTypeEnum.getValue() : null);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, componentType);
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_CATEGORY_NAME, categoryName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SUB_CATEGORY_NAME, subCategoryName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_GROUPING_NAME, groupingName);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public ActionStatus convertFromStorageResponse(StorageOperationStatus storageResponse) {
+
+ return convertFromStorageResponse(storageResponse, ComponentTypeEnum.RESOURCE);
+ }
+
+ public ActionStatus convertFromStorageResponse(StorageOperationStatus storageResponse, ComponentTypeEnum type) {
+
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+ if (storageResponse == null) {
+ return responseEnum;
+ }
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case CONNECTION_FAILURE:
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.COMPONENT_NAME_ALREADY_EXIST;
+ break;
+ case PARENT_RESOURCE_NOT_FOUND:
+ responseEnum = ActionStatus.PARENT_RESOURCE_NOT_FOUND;
+ break;
+ case MULTIPLE_PARENT_RESOURCE_FOUND:
+ responseEnum = ActionStatus.MULTIPLE_PARENT_RESOURCE_FOUND;
+ break;
+ case NOT_FOUND:
+ if (ComponentTypeEnum.RESOURCE == type) {
+ responseEnum = ActionStatus.RESOURCE_NOT_FOUND;
+ } else if (ComponentTypeEnum.PRODUCT == type) {
+ responseEnum = ActionStatus.PRODUCT_NOT_FOUND;
+ } else {
+ responseEnum = ActionStatus.SERVICE_NOT_FOUND;
+ }
+ break;
+ case FAILED_TO_LOCK_ELEMENT:
+ responseEnum = ActionStatus.COMPONENT_IN_USE;
+ break;
+ case ARTIFACT_NOT_FOUND:
+ responseEnum = ActionStatus.ARTIFACT_NOT_FOUND;
+ break;
+ case DISTR_ENVIRONMENT_NOT_AVAILABLE:
+ responseEnum = ActionStatus.DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE;
+ break;
+ case DISTR_ENVIRONMENT_NOT_FOUND:
+ responseEnum = ActionStatus.DISTRIBUTION_ENVIRONMENT_NOT_FOUND;
+ break;
+ case DISTR_ENVIRONMENT_SENT_IS_INVALID:
+ responseEnum = ActionStatus.DISTRIBUTION_ENVIRONMENT_INVALID;
+ break;
+ case DISTR_ARTIFACT_NOT_FOUND:
+ responseEnum = ActionStatus.DISTRIBUTION_ARTIFACT_NOT_FOUND;
+ break;
+ case INVALID_TYPE:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case INVALID_VALUE:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case CSAR_NOT_FOUND:
+ responseEnum = ActionStatus.CSAR_NOT_FOUND;
+ break;
+ case PROPERTY_NAME_ALREADY_EXISTS:
+ responseEnum = ActionStatus.PROPERTY_NAME_ALREADY_EXISTS;
+ break;
+ case MATCH_NOT_FOUND:
+ responseEnum = ActionStatus.COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY;
+ break;
+ case CATEGORY_NOT_FOUND:
+ responseEnum = ActionStatus.COMPONENT_CATEGORY_NOT_FOUND;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ActionStatus convertFromToscaError(ToscaError toscaError) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+ if (toscaError == null) {
+ return responseEnum;
+ }
+ switch (toscaError) {// TODO match errors
+ case NODE_TYPE_CAPABILITY_ERROR:
+ case NOT_SUPPORTED_TOSCA_TYPE:
+ case NODE_TYPE_REQUIREMENT_ERROR:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ return responseEnum;
+ }
+
+ public ActionStatus convertFromStorageResponseForCapabilityType(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case CONNECTION_FAILURE:
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.CAPABILITY_TYPE_ALREADY_EXIST;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.CAPABILITY_TYPE_ALREADY_EXIST;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ActionStatus convertFromStorageResponseForLifecycleType(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case CONNECTION_FAILURE:
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.LIFECYCLE_TYPE_ALREADY_EXIST;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.LIFECYCLE_TYPE_ALREADY_EXIST;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ActionStatus convertFromStorageResponseForResourceInstance(StorageOperationStatus storageResponse, boolean isRelation) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case INVALID_ID:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_BAD_REQUEST;
+ break;
+ case INVALID_PROPERTY:
+ responseEnum = ActionStatus.INVALID_PROPERTY;
+ break;
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case MATCH_NOT_FOUND:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_MATCH_NOT_FOUND;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_ALREADY_EXIST;
+ break;
+ case NOT_FOUND:
+ if (isRelation) {
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_RELATION_NOT_FOUND;
+ } else {
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_NOT_FOUND;
+ }
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ResponseFormat getResponseFormatForResourceInstance(ActionStatus actionStatus, String serviceName, String resourceInstanceName) {
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case RESOURCE_INSTANCE_NOT_FOUND:
+ responseFormat = getResponseFormat(actionStatus, resourceInstanceName);
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus, serviceName);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public ResponseFormat getResponseFormatForResourceInstanceProperty(ActionStatus actionStatus, String resourceInstanceName) {
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case RESOURCE_INSTANCE_NOT_FOUND:
+ responseFormat = getResponseFormat(actionStatus, resourceInstanceName);
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public ActionStatus convertFromStorageResponseForResourceInstanceProperty(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case INVALID_ID:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_BAD_REQUEST;
+ break;
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case MATCH_NOT_FOUND:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_MATCH_NOT_FOUND;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_ALREADY_EXIST;
+ break;
+ case NOT_FOUND:
+ responseEnum = ActionStatus.RESOURCE_INSTANCE_NOT_FOUND;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public void auditComponentAdmin(ResponseFormat responseFormat, User modifier, Component component, String prevState, String prevVersion, AuditingActionEnum actionEnum, ComponentTypeEnum type) {
+ auditComponent(responseFormat, modifier, component, prevState, prevVersion, actionEnum, type, null);
+ }
+
+ public void auditComponent(ResponseFormat responseFormat, User modifier, Component component, String prevState, String prevVersion, AuditingActionEnum actionEnum, ComponentTypeEnum type, EnumMap<AuditingFieldsKeysEnum, Object> additionalParams) {
+ if (actionEnum != null) {
+ log.trace("Inside auditing for audit action {}", actionEnum.name());
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+
+ updateUserFields(modifier, auditingFields);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, type.getValue().replace(" ", ""));
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION, prevVersion);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE, prevState);
+ if (component != null) {
+ // fields that are filled during creation and might still be
+ // empty
+ String resourceCurrVersion = component.getVersion();
+ String resourceCurrState = (component.getLifecycleState() != null) ? component.getLifecycleState().name() : "";
+ String resourceUuid = (component.getUUID() != null) ? component.getUUID() : "";
+ String invariantUUID = (component.getInvariantUUID() != null) ? component.getInvariantUUID() : "";
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, resourceCurrVersion);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, resourceCurrState);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, resourceUuid);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, invariantUUID);
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, "");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID, "");
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ // This is meant to overwrite anything set in this function if some
+ // params were passed from above,
+ // for example resourceName of resource import
+ if (additionalParams != null) {
+ auditingFields.putAll(additionalParams);
+ }
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+ }
+
+ public void auditDistributionEngine(AuditingActionEnum actionEnum, String environmentName, String topicName, String role, String apiKey, String status) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME, environmentName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, topicName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ROLE, role);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY, apiKey);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditDistributionNotification(AuditingActionEnum actionEnum, String serviceUUID, String resourceName, String resourceType, String currVersion, String modifierUid, String modifierName, String environmentName, String currState,
+ String topicName, String distributionId, String description, String status) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, serviceUUID);
+
+ // auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME,
+ // environmentName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, topicName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, currVersion);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, currState);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, resourceType);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, description);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifierUid);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifierName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceName);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditAuthEvent(AuditingActionEnum actionEnum, String url, String user, String authStatus, String realm) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_URL, url);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_USER, user);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_STATUS, authStatus);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_REALM, realm);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditDistributionStatusNotification(AuditingActionEnum actionEnum, String distributionId, String consumerId, String topicName, String resourceUrl, String statusTime, String status, String errorReason) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID, distributionId);
+ ThreadLocalsHolder.setUuid(distributionId);
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, consumerId);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, topicName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, resourceUrl);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME, statusTime);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, errorReason);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditGetUebCluster(AuditingActionEnum actionEnum, String consumerId, String statusTime, String status, String description) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, consumerId);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME, statusTime);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_DESC, description);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditMissingInstanceId(AuditingActionEnum actionEnum, String status, String description) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, null);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, null);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, description);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditTopicACLKeys(AuditingActionEnum actionEnum, String envName, String topicName, String role, String apiPublicKey, String status) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME, envName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, topicName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ROLE, role);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY, apiPublicKey);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditRegisterOrUnRegisterEvent(AuditingActionEnum actionEnum, String consumerId, String apiPublicKey, String envName, String status, String statusDesc, String notifTopicName, String statusTopicName) {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, consumerId);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY, apiPublicKey);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME, envName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_DESC, statusDesc);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME, notifTopicName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME, statusTopicName);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditServiceDistributionDeployed(AuditingActionEnum actionEnum, String serviceName, String serviceVersion, String serviceUUID, String distributionId, String status, String desc, User modifier) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, serviceUUID);
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifier.getFirstName() + " " + modifier.getLastName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier.getUserId());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, "Service");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, serviceName);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, serviceVersion);
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, desc);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditConsumerCredentialsEvent(AuditingActionEnum actionEnum, ConsumerDefinition consumer, ResponseFormat responseFormat, User modifier) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ if (modifier != null) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifier.getFirstName() + " " + modifier.getLastName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier.getUserId());
+ }
+ StringBuilder ecompUser = new StringBuilder();
+ if (consumer != null) {
+ if (consumer.getConsumerName() != null && !consumer.getConsumerName().trim().isEmpty()) {
+ ecompUser.append(consumer.getConsumerName());
+ }
+ if (consumer.getConsumerSalt() != null && !consumer.getConsumerSalt().trim().isEmpty()) {
+ if (ecompUser.length() > 0) {
+ ecompUser.append(",");
+ }
+ ecompUser.append(consumer.getConsumerSalt());
+ }
+ if (consumer.getConsumerPassword() != null && !consumer.getConsumerPassword().trim().isEmpty()) {
+ if (ecompUser.length() > 0) {
+ ecompUser.append(",");
+ }
+ ecompUser.append(consumer.getConsumerPassword());
+ }
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ECOMP_USER, ecompUser.toString());
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditGetUsersList(AuditingActionEnum actionEnum, User modifier, String details, ResponseFormat responseFormat) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ if (modifier != null) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifier.getFirstName() + " " + modifier.getLastName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier.getUserId());
+ }
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_DETAILS, details);
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditAdminUserAction(AuditingActionEnum actionEnum, User modifier, User userBefore, User userAfter, ResponseFormat responseFormat) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ if (modifier != null) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifier.getFirstName() + " " + modifier.getLastName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier.getUserId());
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, "");
+ }
+ if (userBefore != null) {
+ String userBeforeUserId = (userBefore.getUserId() != null) ? userBefore.getUserId() : "";
+ String userBeforFirstName = (userBefore.getFirstName() != null) ? ", " + userBefore.getFirstName() + " " : "";
+ String userBeforLastName = (userBefore.getLastName() != null) ? userBefore.getLastName() : "";
+ String userBeforEmail = (userBefore.getEmail() != null) ? ", " + userBefore.getEmail() : "";
+ String userBeforRloe = (userBefore.getRole() != null) ? ", " + userBefore.getRole() : "";
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_BEFORE, userBeforeUserId + userBeforFirstName + userBeforLastName + userBeforEmail + userBeforRloe);
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_BEFORE, "");
+ }
+ if (userAfter != null) {
+ String userAfterUserId = (userAfter.getUserId() != null) ? userAfter.getUserId() : "";
+ String userAfterFirstName = (userAfter.getFirstName() != null) ? ", " + userAfter.getFirstName() + " " : "";
+ String userAfterLastName = (userAfter.getLastName() != null) ? userAfter.getLastName() : "";
+ String userAfterEmail = (userAfter.getEmail() != null) ? ", " + userAfter.getEmail() : "";
+ String userAfterRloe = (userAfter.getRole() != null) ? ", " + userAfter.getRole() : "";
+
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_AFTER, userAfterUserId + userAfterFirstName + userAfterLastName + userAfterEmail + userAfterRloe);
+ } else {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_AFTER, "");
+ }
+
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditUserAccess(AuditingActionEnum actionEnum, User user, ResponseFormat responseFormat) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_USER_UID, user.getFirstName() + " " + user.getLastName() + '(' + user.getUserId() + ')');
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public void auditGetCategoryHierarchy(AuditingActionEnum actionEnum, User modifier, String details, ResponseFormat responseFormat) {
+
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, actionEnum.getName());
+ if (modifier != null) {
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifier.getFirstName() + " " + modifier.getLastName());
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifier.getUserId());
+ }
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DETAILS, details);
+ int status = responseFormat.getStatus();
+ String message = "";
+
+ if (responseFormat.getMessageId() != null) {
+ message = responseFormat.getMessageId() + ": ";
+ }
+ message += responseFormat.getFormattedMessage();
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, message);
+ getAuditingManager().auditEvent(auditingFields);
+ }
+
+ public ResponseFormat getResponseFormatByComponent(ActionStatus actionStatus, Component component, ComponentTypeEnum type) {
+ if (component == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case COMPONENT_VERSION_ALREADY_EXIST:
+ responseFormat = getResponseFormat(ActionStatus.COMPONENT_VERSION_ALREADY_EXIST, type.getValue(), component.getVersion());
+ break;
+ case RESOURCE_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ break;
+ case COMPONENT_NAME_ALREADY_EXIST:
+ responseFormat = getResponseFormat(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, type.getValue(), component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ break;
+ case COMPONENT_IN_USE:
+ responseFormat = getResponseFormat(ActionStatus.COMPONENT_IN_USE, type.name().toLowerCase(), component.getUniqueId());
+ break;
+ case SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND:
+ responseFormat = getResponseFormat(ActionStatus.SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+
+ public Either<Boolean, ResponseFormat> validateStringNotEmpty(User user, Component component, String value, ActionStatus badResult, AuditingActionEnum actionEnum) {
+ if ((value == null) || (value.trim().isEmpty())) {
+ log.info(badResult.name());
+ ResponseFormat errorResponse = getResponseFormat(badResult);
+ if (actionEnum != null) {
+ log.debug("audit before sending response");
+ auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, getComponentType(component));
+ }
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+
+ public Boolean validateStringNotEmpty(String value) {
+ if ((value == null) || (value.trim().isEmpty())) {
+ return false;
+ }
+ return true;
+ }
+
+ private ComponentTypeEnum getComponentType(Component component) {
+ if (component instanceof Service) {
+ return ComponentTypeEnum.SERVICE;
+ } // else if(component instanceof Resource){
+ return null;
+ }
+
+ public Either<Boolean, ResponseFormat> validateStringMatchesPattern(User user, Component component, String value, Pattern pattern, ActionStatus badResult, AuditingActionEnum actionEnum) {
+ if (!pattern.matcher(value).matches()) {
+ log.error(badResult.name());
+ ResponseFormat errorResponse = getResponseFormat(badResult);
+ if (actionEnum != null) {
+ log.debug("audit before sending response");
+ auditComponentAdmin(errorResponse, user, component, "", "", actionEnum, getComponentType(component));
+ }
+ return Either.right(errorResponse);
+ }
+ return Either.left(true);
+ }
+
+ /**
+ *
+ * " Error: Missing Mandatory Informational %s1 %s2 : %s3 " where %s1 - "resource"/"service" %s2 - "artifact"/ "artifacts" %s3 - Comma separated list of missing informational artifact types
+ *
+ * @param resource
+ * @param componentMissingMandatoryArtifacts
+ * @param value
+ * @return
+ */
+ public ResponseFormat getResponseFormatByMissingArtifacts(ComponentTypeEnum componentType, Map<String, ArtifactDefinition> artifacts) {
+
+ String artifactTitle = "artifact";
+ if (artifacts.size() > 1) {
+ artifactTitle = "artifacts";
+ }
+ Collection<ArtifactDefinition> artifactsLabels = artifacts.values();
+ StringBuilder artifactsLabelBuilder = new StringBuilder();
+
+ List<ArtifactDefinition> artifactsLabelsList = new ArrayList<ArtifactDefinition>();
+ artifactsLabelsList.addAll(artifactsLabels);
+ for (int i = 0; i < artifactsLabelsList.size(); i++) {
+ ArtifactDefinition artifactDef = artifactsLabelsList.get(i);
+ artifactsLabelBuilder.append(artifactDef.getArtifactDisplayName());
+ if (i < artifactsLabelsList.size() - 1) {
+ artifactsLabelBuilder.append(";");
+ }
+ }
+ ResponseFormat responseFormat = getResponseFormat(ActionStatus.COMPONENT_MISSING_MANDATORY_ARTIFACTS, componentType.name().toLowerCase(), artifactTitle, artifactsLabelBuilder.toString());
+
+ return responseFormat;
+ }
+
+ public ActionStatus convertFromStorageResponseForAdditionalInformation(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.COMPONENT_NAME_ALREADY_EXIST;
+ break;
+ case INVALID_ID:
+ responseEnum = ActionStatus.ADDITIONAL_INFORMATION_NOT_FOUND;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ResponseFormat getResponseFormatAdditionalProperty(ActionStatus actionStatus, AdditionalInfoParameterInfo additionalInfoParameterInfo, NodeTypeEnum nodeType, AdditionalInformationEnum labelOrValue) {
+
+ if (additionalInfoParameterInfo == null) {
+ additionalInfoParameterInfo = new AdditionalInfoParameterInfo();
+ }
+ if (labelOrValue == null) {
+ labelOrValue = AdditionalInformationEnum.None;
+ }
+
+ ResponseFormat responseFormat = null;
+ switch (actionStatus) {
+ case COMPONENT_NAME_ALREADY_EXIST:
+ responseFormat = getResponseFormat(actionStatus, "Additional parameter", additionalInfoParameterInfo.getKey());
+ break;
+ case ADDITIONAL_INFORMATION_EXCEEDS_LIMIT:
+ responseFormat = getResponseFormat(actionStatus, labelOrValue.name().toLowerCase(), ValidationUtils.ADDITIONAL_INFORMATION_KEY_MAX_LENGTH.toString());
+ break;
+ case ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED:
+ responseFormat = getResponseFormat(actionStatus, nodeType.name().toLowerCase());
+ break;
+ case ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ case ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ case ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ case ADDITIONAL_INFORMATION_NOT_FOUND:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+
+ return responseFormat;
+ }
+
+ public ResponseFormat getResponseFormatAdditionalProperty(ActionStatus actionStatus) {
+ return getResponseFormatAdditionalProperty(actionStatus, null, null, null);
+ }
+
+ public ActionStatus convertFromStorageResponseForConsumer(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case CONNECTION_FAILURE:
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.CONSUMER_ALREADY_EXISTS;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.CONSUMER_ALREADY_EXISTS;
+ break;
+ case NOT_FOUND:
+ responseEnum = ActionStatus.ECOMP_USER_NOT_FOUND;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ActionStatus convertFromStorageResponseForGroupType(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case CONNECTION_FAILURE:
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.GROUP_TYPE_ALREADY_EXIST;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.GROUP_TYPE_ALREADY_EXIST;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ActionStatus convertFromStorageResponseForDataType(StorageOperationStatus storageResponse) {
+ ActionStatus responseEnum = ActionStatus.GENERAL_ERROR;
+
+ switch (storageResponse) {
+ case OK:
+ responseEnum = ActionStatus.OK;
+ break;
+ case CONNECTION_FAILURE:
+ case GRAPH_IS_LOCK:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ case BAD_REQUEST:
+ responseEnum = ActionStatus.INVALID_CONTENT;
+ break;
+ case ENTITY_ALREADY_EXISTS:
+ responseEnum = ActionStatus.DATA_TYPE_ALREADY_EXIST;
+ break;
+ case SCHEMA_VIOLATION:
+ responseEnum = ActionStatus.DATA_TYPE_ALREADY_EXIST;
+ break;
+ case CANNOT_UPDATE_EXISTING_ENTITY:
+ responseEnum = ActionStatus.DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST;
+ break;
+ default:
+ responseEnum = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ log.debug("convert storage response {} to action response {}", storageResponse.name(), responseEnum.name());
+ return responseEnum;
+ }
+
+ public ResponseFormat getResponseFormatByGroupType(ActionStatus actionStatus, GroupTypeDefinition groupType) {
+ if (groupType == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case GROUP_MEMBER_EMPTY:
+ case GROUP_TYPE_ALREADY_EXIST:
+ responseFormat = getResponseFormat(actionStatus, groupType.getType());
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+
+ }
+
+ public ResponseFormat getResponseFormatByPolicyType(ActionStatus actionStatus, PolicyTypeDefinition policyType) {
+ if (policyType == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case POLICY_TYPE_ALREADY_EXIST:
+ responseFormat = getResponseFormat(actionStatus, policyType.getType());
+ break;
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+
+ }
+
+ public ResponseFormat getResponseFormatByDataType(ActionStatus actionStatus, DataTypeDefinition dataType, List<String> properties) {
+ if (dataType == null) {
+ return getResponseFormat(actionStatus);
+ }
+ ResponseFormat responseFormat;
+
+ switch (actionStatus) {
+ case DATA_TYPE_ALREADY_EXIST:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName());
+ break;
+ case DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName());
+ break;
+ case DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName());
+ break;
+ case DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName(), properties == null ? "" : String.valueOf(properties));
+ break;
+ case DATA_TYPE_DERIVED_IS_MISSING:
+ responseFormat = getResponseFormat(actionStatus, dataType.getDerivedFromName());
+ break;
+ case DATA_TYPE_DUPLICATE_PROPERTY:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName());
+ break;
+ case DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName(), properties == null ? "" : String.valueOf(properties));
+ break;
+ case DATA_TYPE_CANNOT_HAVE_PROPERTIES:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName());
+ break;
+ case DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST:
+ responseFormat = getResponseFormat(actionStatus, dataType.getName());
+ break;
+
+ default:
+ responseFormat = getResponseFormat(actionStatus);
+ break;
+ }
+ return responseFormat;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/impl/DownloadArtifactLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/DownloadArtifactLogic.java
new file mode 100644
index 0000000000..fff6fd15a4
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/DownloadArtifactLogic.java
@@ -0,0 +1,166 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpStatus;
+import org.eclipse.jgit.util.Base64;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.info.ArtifactAccessInfo;
+import org.openecomp.sdc.be.info.ArtifactAccessList;
+import org.openecomp.sdc.be.info.ServletJsonResponse;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class DownloadArtifactLogic {
+
+ private static Logger log = LoggerFactory.getLogger(DownloadArtifactLogic.class.getName());
+
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ public Response downloadArtifact(final String resourceName, final String resourceVersion, final String artifactName, Either<? extends ESArtifactData, ResourceUploadStatus> getArtifactStatus, String artifactId) {
+ Response response = null;
+
+ if (getArtifactStatus.isRight()) {
+ log.debug("Could not find artifact for with id: {}", artifactId);
+ ResourceUploadStatus status = getArtifactStatus.right().value();
+ if (status == status.COMPONENT_NOT_EXIST)
+ response = Response.status(HttpStatus.SC_NO_CONTENT).build();
+ else
+ response = Response.status(HttpStatus.SC_NOT_FOUND).build();
+
+ return response;
+ }
+ // convert artifact to inputstream
+ else {
+ ESArtifactData artifactData = getArtifactStatus.left().value();
+ byte[] artifactPayload = artifactData.getDataAsArray();
+
+ String payloadStr = new String(artifactPayload);
+ byte[] decodedPayload = artifactPayload;
+ boolean isEncoded = GeneralUtility.isBase64Encoded(payloadStr);
+ if (isEncoded) {
+ log.debug("payload is encoded. perform decode");
+ decodedPayload = Base64.decode(new String(artifactPayload));
+ }
+ final InputStream artifactStream = new ByteArrayInputStream(decodedPayload);
+ log.debug("found artifact for with id: {}", artifactId);
+ try {
+
+ // outputstream for response
+ StreamingOutput stream = new StreamingOutput() {
+ public void write(OutputStream output) throws IOException, WebApplicationException {
+ try {
+ IOUtils.copy(artifactStream, output);
+ } catch (Exception e) {
+ throw new WebApplicationException(e);
+ }
+ }
+ };
+ artifactStream.close();
+ return Response.ok(stream).type(MediaType.APPLICATION_OCTET_STREAM_TYPE).header("content-disposition", "attachment; filename = " + artifactName)
+ // .header(Constants.MD5_HEADER, new
+ // String(artifactData.getArtifactChecksum()))
+ .build();
+
+ } catch (IOException e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Failed to stream artifact data on the response");
+ BeEcompErrorManager.getInstance().logBeSystemError("Failed to stream artifact data on the response");
+ log.debug("Failed to stream artifact data on the response: {}", e.getMessage());
+ response = buildResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Failed to stream artifact data on the response");
+ return response;
+ }
+ }
+ }
+
+ public List<ArtifactAccessInfo> convertArtifactList(List<? extends ESArtifactData> artifactsList, String servletPath) {
+
+ List<ArtifactAccessInfo> artifactAccessList = new ArrayList<ArtifactAccessInfo>();
+ for (ESArtifactData artifact : artifactsList) {
+ ArtifactAccessInfo accessInfo = new ArtifactAccessInfo(artifact, servletPath);
+ artifactAccessList.add(accessInfo);
+ }
+ return artifactAccessList;
+ }
+
+ public Response createArtifactListResponse(final String serviceName, Either<List<ESArtifactData>, ResourceUploadStatus> getArtifactsStatus/*
+ * List < ? extends IResourceData> artifactsList
+ */, String servletPath) {
+ Response response;
+ List<ArtifactAccessInfo> artifactAccessInfos;
+ if (getArtifactsStatus.isRight()) {
+ // if there are no artifacts - return No-Content
+ ResourceUploadStatus status = getArtifactsStatus.right().value();
+ if (status == ResourceUploadStatus.COMPONENT_NOT_EXIST || status == ResourceUploadStatus.SERVICE_NOT_EXIST) {
+ log.debug("resource {} does not exist", serviceName);
+ response = Response.status(HttpStatus.SC_NOT_FOUND).entity("[]").build();
+
+ } else {
+ log.debug("No content was found for {}", serviceName);
+ response = Response.status(HttpStatus.SC_NO_CONTENT).entity("[]").build();
+ }
+ return response;
+ } else {
+ List<? extends ESArtifactData> artifactsList = getArtifactsStatus.left().value();
+ log.debug("{} artifacts were found for {}", artifactsList.size(), serviceName);
+ artifactAccessInfos = convertArtifactList(artifactsList, servletPath);
+
+ String artifactDataJson = gson.toJson(new ArtifactAccessList(artifactAccessInfos));
+ response = Response.status(HttpStatus.SC_OK).entity(artifactDataJson).type(MediaType.APPLICATION_JSON_TYPE).build();
+
+ return response;
+ }
+ }
+
+ public Response buildResponse(int status, String errorMessage) {
+
+ ServletJsonResponse jsonResponse = new ServletJsonResponse();
+ jsonResponse.setDescription(errorMessage);
+ jsonResponse.setSource(Constants.CATALOG_BE);
+
+ Response response = Response.status(status).entity(jsonResponse).build();
+
+ return response;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ServletUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ServletUtils.java
new file mode 100644
index 0000000000..3a5d9bf85f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ServletUtils.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.impl;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+
+@Component("servletUtils")
+public class ServletUtils {
+ @Resource
+ private ComponentsUtils componentsUtils;
+ private Gson gson = new Gson();
+ @Resource
+ private IUserBusinessLogic adminManager;
+
+ public ComponentsUtils getComponentsUtils() {
+ return componentsUtils;
+ }
+
+ public Gson getGson() {
+ return gson;
+ }
+
+ public IUserBusinessLogic getUserAdmin() {
+ return adminManager;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/impl/WebAppContextWrapper.java b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/WebAppContextWrapper.java
new file mode 100644
index 0000000000..cb21984a7f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/WebAppContextWrapper.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.impl;
+
+import javax.servlet.ServletContext;
+
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+public class WebAppContextWrapper {
+
+ public WebApplicationContext getWebAppContext(ServletContext context) {
+
+ return WebApplicationContextUtils.getWebApplicationContext(context);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessInfo.java
new file mode 100644
index 0000000000..deb446e32f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessInfo.java
@@ -0,0 +1,159 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+
+public class ArtifactAccessInfo {
+
+ public ArtifactAccessInfo() {
+ }
+
+ public ArtifactAccessInfo(ESArtifactData artifactData) {
+ // this.name = artifactData.getArtifactName();
+ this.id = artifactData.getId();
+ // this.type = artifactData.getArtifactType();
+ // this.description = artifactData.getArtifactDescription();
+ // this.creator = artifactData.getArtifactCreator();
+ // DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
+ // Date creationTimestamp = artifactData.getArtifactCreationTimestamp();
+ // this.creationTime = (creationTimestamp !=
+ // null)?dateFormat.format(creationTimestamp): null;
+ // Date updateTimestamp = artifactData.getArtifactLastUpdateTimestamp();
+ // this.lastUpdateTime = (updateTimestamp !=
+ // null)?dateFormat.format(updateTimestamp):null;
+ // this.lastUpdater = artifactData.getArtifactLastUpdater();
+ // if (artifactData.getArtifactChecksum() != null){
+ // this.checksum = new String(artifactData.getArtifactChecksum());
+ // } else {
+ // this.checksum = null;
+ // }
+ }
+
+ public ArtifactAccessInfo(ESArtifactData artifactData, String servletContext) {
+ // this.name = artifactData.getArtifactName();
+ StringBuilder urlBuilder = new StringBuilder();
+ urlBuilder = urlBuilder.append(servletContext).append("/");
+ // if (ArtifactDataEnum.COMPONENT_ARTIFACT.equals(resource)){
+ urlBuilder.append("resources/")
+ // .append(artifactData.getResourceId()).append("/")
+
+ .append("/artifacts/");
+ /*
+ * }else { ServiceArtifactData serviceArtifact = (ServiceArtifactData)artifactData; urlBuilder.append("services/") .append(serviceArtifact.getServiceName()).append("/") .append(serviceArtifact.getServiceVersion()) .append("/artifacts/")
+ * .append(serviceArtifact.getNodeTemplateName()) .append("/"); }
+ */
+ // urlBuilder.append(artifactData.getArtifactName());
+ this.url = urlBuilder.toString();
+
+ }
+
+ private String name;
+ private String url;
+ private String id;
+ private String type;
+ private String description;
+ private String creator;
+ private String creationTime;
+ private String lastUpdater;
+ private String lastUpdateTime;
+ private String checksum;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCreator() {
+ return creator;
+ }
+
+ public void setCreator(String creator) {
+ this.creator = creator;
+ }
+
+ public String getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(String creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public String getLastUpdater() {
+ return lastUpdater;
+ }
+
+ public void setLastUpdater(String lastUpdater) {
+ this.lastUpdater = lastUpdater;
+ }
+
+ public String getLastUpdateTime() {
+ return lastUpdateTime;
+ }
+
+ public void setLastUpdateTime(String lastUpdateTime) {
+ this.lastUpdateTime = lastUpdateTime;
+ }
+
+ public String getChecksum() {
+ return checksum;
+ }
+
+ public void setChecksum(String checksum) {
+ this.checksum = checksum;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessList.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessList.java
new file mode 100644
index 0000000000..ddeb0505d9
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactAccessList.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class ArtifactAccessList {
+
+ public ArtifactAccessList(List<ArtifactAccessInfo> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ private List<ArtifactAccessInfo> artifacts;
+
+ public List<ArtifactAccessInfo> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactAccessInfo> artifacts) {
+ this.artifacts = artifacts;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactDefinitionInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactDefinitionInfo.java
new file mode 100644
index 0000000000..163d220871
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactDefinitionInfo.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+public class ArtifactDefinitionInfo {
+
+ private String uniqueId;
+ /** Specifies the display name of the artifact. */
+ private String artifactName;
+ private String artifactDisplayName;
+ private String artifactVersion;
+ private String artifactUUID;
+
+ public ArtifactDefinitionInfo(ArtifactDefinition artifactDefinition) {
+ uniqueId = artifactDefinition.getUniqueId();
+ artifactName = artifactDefinition.getArtifactName();
+ artifactDisplayName = artifactDefinition.getArtifactDisplayName();
+ artifactVersion = artifactDefinition.getArtifactVersion();
+ artifactUUID = artifactDefinition.getArtifactUUID();
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactDisplayName() {
+ return artifactDisplayName;
+ }
+
+ public void setArtifactDisplayName(String artifactDisplayName) {
+ this.artifactDisplayName = artifactDisplayName;
+ }
+
+ public String getArtifactVersion() {
+ return artifactVersion;
+ }
+
+ public void setArtifactVersion(String artifactVersion) {
+ this.artifactVersion = artifactVersion;
+ }
+
+ public String getArtifactUUID() {
+ return artifactUUID;
+ }
+
+ public void setArtifactUUID(String artifactUUID) {
+ this.artifactUUID = artifactUUID;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java
new file mode 100644
index 0000000000..fca7bf38d8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTemplateInfo.java
@@ -0,0 +1,352 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.DeploymentArtifactTypeConfig;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+import fj.data.Either;
+
+public class ArtifactTemplateInfo {
+ public static final String TYPE = "type";
+ public static final String FILE_NAME = "fileName";
+ public static final String ENV = "env";
+ public static final String IS_BASE = "isBase";
+
+ private static final String CSAR_HEAT = "HEAT";
+ private static final String CSAR_ARTIFACT = "artifacts";
+ private static final String CSAR_NETWORK = "network";
+ private static final String CSAR_VOLUME = "volume";
+ private static final String CSAR_NESTED = "nested";
+ private static final Object DESC = "description";
+ private static Logger log = LoggerFactory.getLogger(ArtifactTemplateInfo.class.getName());
+ String type;
+ String fileName;
+ String env;
+ boolean isBase;
+ String groupName;
+ String description;
+
+ List<ArtifactTemplateInfo> relatedArtifactsInfo;
+ private static Gson gson = new Gson();
+
+ public ArtifactTemplateInfo() {
+ super();
+ }
+
+ public ArtifactTemplateInfo(String type, String fileName, String env, List<ArtifactTemplateInfo> relatedArtifactsInfo) {
+ super();
+ this.type = type;
+ this.fileName = fileName;
+ this.env = env;
+ this.relatedArtifactsInfo = relatedArtifactsInfo;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public String getEnv() {
+ return env;
+ }
+
+ public void setEnv(String env) {
+ this.env = env;
+ }
+
+ public List<ArtifactTemplateInfo> getRelatedArtifactsInfo() {
+ return relatedArtifactsInfo;
+ }
+
+ public void setRelatedArtifactsInfo(List<ArtifactTemplateInfo> relatedArtifactsInfo) {
+ this.relatedArtifactsInfo = relatedArtifactsInfo;
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ public boolean isBase() {
+ return isBase;
+ }
+
+ public void setBase(boolean isBase) {
+ this.isBase = isBase;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtifactTemplateInfo [type=" + type + ", fileName=" + fileName + ", env=" + env + ", isBase=" + isBase + ", groupName=" + groupName + ", description=" + description + ", relatedArtifactsInfo=" + relatedArtifactsInfo + "]";
+ }
+
+ public static Either<ArtifactTemplateInfo, ResponseFormat> createArtifactTemplateInfoFromJson(ComponentsUtils componentsUtils, String type, Map<String, Object> o, List<ArtifactTemplateInfo> createdArtifactTemplateInfoList,
+ ArtifactTemplateInfo parentArtifact) {
+ String content = gson.toJson(o);
+ JsonObject jsonElement = new JsonObject();
+ ArtifactTemplateInfo resourceInfo = new ArtifactTemplateInfo();
+
+ jsonElement = gson.fromJson(content, jsonElement.getClass());
+
+ Map<String, Object> artifactTemplateMap = componentsUtils.parseJsonToObject(jsonElement.toString(), HashMap.class);
+ if (artifactTemplateMap.containsKey(TYPE))
+ resourceInfo.setType((String) artifactTemplateMap.get(TYPE));
+ if (artifactTemplateMap.containsKey(FILE_NAME))
+ resourceInfo.setFileName((String) artifactTemplateMap.get(FILE_NAME));
+ if (artifactTemplateMap.containsKey(IS_BASE))
+ resourceInfo.setBase((Boolean) artifactTemplateMap.get(IS_BASE));
+ if (artifactTemplateMap.containsKey(ENV)) {
+ Object envObj = artifactTemplateMap.get(ENV);
+ String envStr = "";
+ if (envObj instanceof String) {
+ envStr = (String) envObj;
+ } else if (envObj instanceof Map) {
+ Map envMap = (Map) envObj;
+ if (envMap.containsKey(FILE_NAME)) {
+ envStr = (String) envMap.get(FILE_NAME);
+ }
+ }
+ resourceInfo.setEnv(envStr);
+ }
+ if (artifactTemplateMap.containsKey(DESC)) {
+ resourceInfo.setDescription((String) artifactTemplateMap.get(DESC));
+ } else {
+ resourceInfo.setDescription((String) artifactTemplateMap.get(FILE_NAME));
+ }
+
+ boolean artifactTypeExist = false;
+ String correctType = type;
+ if (type.equalsIgnoreCase(CSAR_NESTED))
+ correctType = ArtifactTypeEnum.HEAT_NESTED.getType();
+ else if ((type.equalsIgnoreCase(CSAR_VOLUME)))
+ correctType = ArtifactTypeEnum.HEAT_VOL.getType();
+ else if ((type.equalsIgnoreCase(CSAR_NETWORK)))
+ correctType = ArtifactTypeEnum.HEAT_NET.getType();
+ else if ((type.equalsIgnoreCase(CSAR_ARTIFACT)))
+ correctType = ArtifactTypeEnum.HEAT_ARTIFACT.getType();
+ else if ((type.equalsIgnoreCase(CSAR_HEAT)))
+ correctType = ArtifactTypeEnum.HEAT.getType();
+ else
+ correctType = ArtifactTypeEnum.OTHER.getType();
+ Either<List<ArtifactType>, ActionStatus> allArtifactTypes = getDeploymentArtifactTypes(NodeTypeEnum.Resource);
+
+ if (allArtifactTypes.isRight()) {
+ BeEcompErrorManager.getInstance().logBeInvalidConfigurationError("Artifact Upload / Update", "artifactTypes", allArtifactTypes.right().value().name());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.FAILED_RETRIVE_ARTIFACTS_TYPES));
+ }
+
+ for (ArtifactType artType : allArtifactTypes.left().value()) {
+
+ if (artType.getName().contains(correctType)) {
+ resourceInfo.type = artType.getName();
+ artifactTypeExist = true;
+ break;
+ }
+ }
+
+ if (!artifactTypeExist) {
+ BeEcompErrorManager.getInstance().logBeInvalidTypeError("Artifact", "-Not supported artifact type ", correctType);
+ log.debug("Not supported artifact type = {}", correctType);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_TYPE_NOT_SUPPORTED, correctType));
+ }
+
+ Either<Boolean, ResponseFormat> eitherNeedToCreate = validateEnv(componentsUtils, createdArtifactTemplateInfoList, resourceInfo);
+ if (eitherNeedToCreate.isRight())
+ return Either.right(eitherNeedToCreate.right().value());
+ eitherNeedToCreate = validateParentType(componentsUtils, resourceInfo, parentArtifact);
+ if (eitherNeedToCreate.isRight())
+ return Either.right(eitherNeedToCreate.right().value());
+ eitherNeedToCreate = validateIsAlreadyExist(componentsUtils, resourceInfo, createdArtifactTemplateInfoList, parentArtifact);
+ if (eitherNeedToCreate.isRight())
+ return Either.right(eitherNeedToCreate.right().value());
+ Set<String> keys = o.keySet();
+ for (String key : keys) {
+ if (o.get(key) instanceof List) {
+ List<Map<String, Object>> artifList = (List<Map<String, Object>>) o.get(key);
+ for (Map<String, Object> relatedArtifactsMap : artifList) {
+ Either<ArtifactTemplateInfo, ResponseFormat> relatedArtifact = ArtifactTemplateInfo.createArtifactTemplateInfoFromJson(componentsUtils, key, relatedArtifactsMap, createdArtifactTemplateInfoList, resourceInfo);
+ if (relatedArtifact.isRight())
+ return relatedArtifact;
+ if (resourceInfo.relatedArtifactsInfo == null)
+ resourceInfo.relatedArtifactsInfo = new ArrayList<ArtifactTemplateInfo>();
+ resourceInfo.relatedArtifactsInfo.add(relatedArtifact.left().value());
+ }
+ }
+ }
+ return Either.left(resourceInfo);
+ }
+
+ private static Either<Boolean, ResponseFormat> validateIsAlreadyExist(ComponentsUtils componentsUtils, ArtifactTemplateInfo resourceInfo, List<ArtifactTemplateInfo> createdArtifactTemplateInfoList, ArtifactTemplateInfo parentArtifact) {
+
+ if (parentArtifact == null) {
+ if (createdArtifactTemplateInfoList == null || createdArtifactTemplateInfoList.isEmpty())
+ return Either.left(true);
+ for (ArtifactTemplateInfo createdArtifact : createdArtifactTemplateInfoList) {
+ if (createdArtifact.getType().equalsIgnoreCase(resourceInfo.getType()) && createdArtifact.getFileName().equalsIgnoreCase(resourceInfo.getFileName())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR, resourceInfo.getFileName(), createdArtifact.type));
+ }
+ }
+ return Either.left(true);
+ } else {
+ List<ArtifactTemplateInfo> relatedArtifacts = parentArtifact.getRelatedArtifactsInfo();
+ if (relatedArtifacts == null || relatedArtifacts.isEmpty())
+ return Either.left(true);
+ for (ArtifactTemplateInfo relatedArtifact : relatedArtifacts) {
+ if (relatedArtifact.getType().equalsIgnoreCase(resourceInfo.getType())) {
+ if (relatedArtifact.getFileName().equalsIgnoreCase(resourceInfo.getFileName())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR, resourceInfo.getFileName(), parentArtifact.getFileName()));
+ }
+ }
+ }
+ return Either.left(true);
+ }
+ }
+
+ private static Either<Boolean, ResponseFormat> validateParentType(ComponentsUtils componentsUtils, ArtifactTemplateInfo resourceInfo, ArtifactTemplateInfo parentArtifact) {
+
+ if (parentArtifact == null)
+ return Either.left(true);
+ if (resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_ARTIFACT.getType()))
+ return Either.left(true);
+ if (resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType()) && parentArtifact != null) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_IN_MASTER, resourceInfo.getFileName(), resourceInfo.getType(), parentArtifact.getFileName(), parentArtifact.getType()));
+ }
+ if ((resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_NET.getType()) || resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType()))
+ && !parentArtifact.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_IN_MASTER, resourceInfo.getFileName(), resourceInfo.getType(), parentArtifact.getFileName(), parentArtifact.getType()));
+ }
+ if (parentArtifact.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_NESTED.getType())) {
+ if (resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_ARTIFACT.getType())) {
+ Either.left(true);
+ }
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_IN_MASTER, resourceInfo.getFileName(), resourceInfo.getType(), parentArtifact.getFileName(), parentArtifact.getType()));
+ }
+ if (parentArtifact.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType()) && resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType())) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_IN_MASTER, resourceInfo.getFileName(), resourceInfo.getType(), parentArtifact.getFileName(), parentArtifact.getType()));
+ }
+
+ if (parentArtifact.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT.getType())) {
+ Either.left(true);
+ }
+ return Either.left(true);
+ }
+
+ private static Either<Boolean, ResponseFormat> validateEnv(ComponentsUtils componentsUtils, List<ArtifactTemplateInfo> createdArtifactTemplateInfoList, ArtifactTemplateInfo resourceInfo) {
+
+ if (createdArtifactTemplateInfoList == null || createdArtifactTemplateInfoList.isEmpty())
+ return Either.left(true);
+ if (resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_NESTED.getType()) || resourceInfo.getType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_ARTIFACT.getType()))
+ return Either.left(true);
+ for (ArtifactTemplateInfo createdArtifactTemplateInfo : createdArtifactTemplateInfoList) {
+ // check if artifact with this name already parsed. If parsed check
+ // env name. it must be the same.
+ if (resourceInfo.getFileName().equalsIgnoreCase(createdArtifactTemplateInfo.getFileName())) {
+ if ((resourceInfo.getEnv() == null || resourceInfo.getEnv().isEmpty()) && (createdArtifactTemplateInfo.getEnv() != null && !createdArtifactTemplateInfo.getEnv().isEmpty())) {
+ log.debug("Artifact file with name {} type{} already parsed but with env {}", resourceInfo.getFileName(), resourceInfo.getType(), createdArtifactTemplateInfo.getEnv());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_ENV, resourceInfo.getFileName(), resourceInfo.getType(), resourceInfo.getEnv(), createdArtifactTemplateInfo.getEnv()));
+ }
+ if (resourceInfo.getEnv() != null && !resourceInfo.getEnv().isEmpty() && createdArtifactTemplateInfo.getEnv() != null && !createdArtifactTemplateInfo.getEnv().isEmpty()
+ && !createdArtifactTemplateInfo.getEnv().equalsIgnoreCase(resourceInfo.getEnv())) {
+ log.debug("Artifact file with name {} type{} env {} already parsed but with env {}", resourceInfo.getFileName(), resourceInfo.getType(), resourceInfo.getEnv(), createdArtifactTemplateInfo.getEnv());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_ENV, resourceInfo.getFileName(), resourceInfo.getType(), resourceInfo.getEnv(), createdArtifactTemplateInfo.getEnv()));
+ }
+ if ((resourceInfo.getEnv() != null && !resourceInfo.getEnv().isEmpty()) && (createdArtifactTemplateInfo.getEnv() == null || createdArtifactTemplateInfo.getEnv().isEmpty())) {
+ log.debug("Artifact file with name {} type{} env {} already parsed but with env {}", resourceInfo.getFileName(), resourceInfo.getType(), resourceInfo.getEnv(), createdArtifactTemplateInfo.getEnv());
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.ARTIFACT_NOT_VALID_ENV, resourceInfo.getFileName(), resourceInfo.getType(), resourceInfo.getEnv(), createdArtifactTemplateInfo.getEnv()));
+ }
+ }
+ List<ArtifactTemplateInfo> relatedArtifacts = createdArtifactTemplateInfo.getRelatedArtifactsInfo();
+ Either<Boolean, ResponseFormat> status = validateEnv(componentsUtils, relatedArtifacts, resourceInfo);
+ if (status.isRight())
+ return status;
+ }
+ return Either.left(true);
+ }
+
+ private static Either<List<ArtifactType>, ActionStatus> getDeploymentArtifactTypes(NodeTypeEnum parentType) {
+
+ Map<String, DeploymentArtifactTypeConfig> deploymentArtifacts = null;
+ List<ArtifactType> artifactTypes = new ArrayList<ArtifactType>();
+
+ if (parentType.equals(NodeTypeEnum.Service)) {
+ deploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getServiceDeploymentArtifacts();
+ } else {
+ deploymentArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getResourceDeploymentArtifacts();
+ }
+ if (deploymentArtifacts != null) {
+ for (String artifactType : deploymentArtifacts.keySet()) {
+ ArtifactType artifactT = new ArtifactType();
+ artifactT.setName(artifactType);
+ artifactTypes.add(artifactT);
+ }
+ return Either.left(artifactTypes);
+ } else {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ }
+
+ public static int compareByGroupName(ArtifactTemplateInfo art1, ArtifactTemplateInfo art2) {
+ return art1.isBase ? (art2.isBase ? 0 : -1) : (art2.isBase ? 1 : 0);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTypesInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTypesInfo.java
new file mode 100644
index 0000000000..249b420908
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ArtifactTypesInfo.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.model.ArtifactType;
+
+public class ArtifactTypesInfo {
+
+ Integer heatDefaultTimeout = 60;
+ List<ArtifactType> artifactTypes;
+
+ public List<ArtifactType> getArtifactTypes() {
+ return artifactTypes;
+ }
+
+ public void setArtifactTypes(List<ArtifactType> artifactTypes) {
+ this.artifactTypes = artifactTypes;
+ }
+
+ public Integer getHeatDefaultTimeout() {
+ return heatDefaultTimeout;
+ }
+
+ public void setHeatDefaultTimeout(Integer heatDefaultTimeout) {
+ this.heatDefaultTimeout = heatDefaultTimeout;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/CreateAndAssotiateInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/CreateAndAssotiateInfo.java
new file mode 100644
index 0000000000..b2fa493443
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/CreateAndAssotiateInfo.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+
+public class CreateAndAssotiateInfo {
+ private ComponentInstance node;
+ private RequirementCapabilityRelDef associate;
+
+ public CreateAndAssotiateInfo(ComponentInstance node, RequirementCapabilityRelDef associate) {
+ super();
+ this.node = node;
+ this.associate = associate;
+ }
+
+ public ComponentInstance getNode() {
+ return node;
+ }
+
+ public void setNode(ComponentInstance node) {
+ this.node = node;
+ }
+
+ public RequirementCapabilityRelDef getAssociate() {
+ return associate;
+ }
+
+ public void setAssociate(RequirementCapabilityRelDef associate) {
+ this.associate = associate;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatus.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatus.java
new file mode 100644
index 0000000000..d0f3ff868e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatus.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public enum DistributionStatus {
+ DEPLOYED("Deployed", "DEPLOYED");
+
+ private String name;
+ private String auditingStatus;
+
+ private static Logger log = LoggerFactory.getLogger(DistributionStatus.class.getName());
+
+ DistributionStatus(String name, String auditingStatus) {
+ this.name = name;
+ this.auditingStatus = auditingStatus;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAuditingStatus() {
+ return auditingStatus;
+ }
+
+ public static DistributionStatus getStatusByAuditingStatusName(String auditingStatus) {
+ DistributionStatus res = null;
+ DistributionStatus[] values = values();
+ for (DistributionStatus value : values) {
+ if (value.getAuditingStatus().equals(auditingStatus)) {
+ res = value;
+ break;
+ }
+ }
+ if (res == null) {
+ log.debug("No DistributionStatus is mapped to name {}", auditingStatus);
+ }
+ return res;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusInfo.java
new file mode 100644
index 0000000000..6f7596cd5f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusInfo.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+
+public class DistributionStatusInfo {
+ String omfComponentID;
+ String timestamp;
+ String url;
+ String status;
+ String errorReason;
+
+ public DistributionStatusInfo(ESTimeBasedEvent distributionStatusEvent) {
+ super();
+ omfComponentID = (String) distributionStatusEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID.getDisplayName());
+ timestamp = (String) distributionStatusEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME.getDisplayName());// distributionStatusEvent.getStatusTime();
+ url = (String) distributionStatusEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL.getDisplayName());// distributionStatusEvent.getResoureURL();
+ status = (String) distributionStatusEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName());// distributionStatusEvent.getStatus();
+ errorReason = (String) distributionStatusEvent.getFields().get(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName());
+
+ }
+
+ public DistributionStatusInfo(String omfComponentID, String timestamp, String url, String status) {
+ super();
+ this.omfComponentID = omfComponentID;
+ this.timestamp = timestamp;
+ this.url = url;
+ this.status = status;
+ }
+
+ public String getOmfComponentID() {
+ return omfComponentID;
+ }
+
+ public void setOmfComponentID(String omfComponentID) {
+ this.omfComponentID = omfComponentID;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getErrorReason() {
+ return errorReason;
+ }
+
+ public void setErrorReason(String errorReason) {
+ this.errorReason = errorReason;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusListResponse.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusListResponse.java
new file mode 100644
index 0000000000..ab122efc74
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusListResponse.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class DistributionStatusListResponse {
+
+ private List<DistributionStatusInfo> distribStatusInfoList;
+
+ public List<DistributionStatusInfo> getDistributionStatusList() {
+ return distribStatusInfoList;
+ }
+
+ public void setDistributionStatusList(List<DistributionStatusInfo> distribStatusInfoList) {
+ this.distribStatusInfoList = distribStatusInfoList;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceInfo.java
new file mode 100644
index 0000000000..dfa481d34d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceInfo.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+public class DistributionStatusOfServiceInfo {
+ String distributionID;
+ String timestamp;
+ String userId;
+ String deployementStatus;
+
+ public DistributionStatusOfServiceInfo() {
+ super();
+
+ }
+
+ public DistributionStatusOfServiceInfo(String distributionID, String timestamp, String userId, String deployementStatus) {
+ super();
+ this.distributionID = distributionID;
+ this.timestamp = timestamp;
+ this.userId = userId;
+ this.deployementStatus = deployementStatus;
+ }
+
+ public String getDistributionID() {
+ return distributionID;
+ }
+
+ public void setDistributionID(String distributionID) {
+ this.distributionID = distributionID;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getDeployementStatus() {
+ return deployementStatus;
+ }
+
+ public void setDeployementStatus(String deployementStatus) {
+ this.deployementStatus = deployementStatus;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceListResponce.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceListResponce.java
new file mode 100644
index 0000000000..8415736be1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/DistributionStatusOfServiceListResponce.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class DistributionStatusOfServiceListResponce {
+
+ private List<DistributionStatusOfServiceInfo> distribStatusOfServiceInfoList;
+
+ public List<DistributionStatusOfServiceInfo> getDistributionStatusOfServiceList() {
+ return distribStatusOfServiceInfoList;
+ }
+
+ public void setDistributionStatusOfServiceList(List<DistributionStatusOfServiceInfo> distribStatusOfServiceInfoList) {
+ this.distribStatusOfServiceInfoList = distribStatusOfServiceInfoList;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupDefinitionInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupDefinitionInfo.java
new file mode 100644
index 0000000000..24d9c5644d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupDefinitionInfo.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition;
+import org.openecomp.sdc.be.model.GroupDefinition;
+
+public class GroupDefinitionInfo {
+ private String name;
+
+ // the id is unique per group instance on graph.
+ private String uniqueId;
+
+ // the group UUID should be changed when one of the artifacts/component
+ // instances has been changed.
+ private String groupUUID;
+
+ // version should be changed when there is a change to the group's metadata
+ // or to the groups members
+ // (not necessarily when the VF version is changed). This field cannot be
+ // updated by user
+ private String version;
+
+ private String invariantUUID;
+
+ Boolean isBase = null;
+
+ // artifacts - list of artifact uid. All artifacts in the group must already
+ // be uploaded to the VF
+ private List<ArtifactDefinitionInfo> artifacts;
+
+ public GroupDefinitionInfo() {
+ super();
+ }
+
+ public GroupDefinitionInfo(GroupDefinition other) {
+ this.setName(other.getName());
+ this.setUniqueId(other.getUniqueId());
+ this.setVersion(other.getVersion());
+ this.setGroupUUID(other.getGroupUUID());
+ this.setInvariantUUID(other.getInvariantUUID());
+
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getGroupUUID() {
+ return groupUUID;
+ }
+
+ public void setGroupUUID(String groupUUID) {
+ this.groupUUID = groupUUID;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Boolean getIsBase() {
+ return isBase;
+ }
+
+ public void setIsBase(Boolean isBase) {
+ this.isBase = isBase;
+ }
+
+ public List<ArtifactDefinitionInfo> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<ArtifactDefinitionInfo> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupDefinitionInfo [" + super.toString() + ", isBase=" + isBase + ", artifacts=" + artifacts + "]";
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupTemplateInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupTemplateInfo.java
new file mode 100644
index 0000000000..8471a801ac
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/GroupTemplateInfo.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+import java.util.Map;
+
+public class GroupTemplateInfo {
+ String groupName;
+ boolean isBase;
+ ArtifactTemplateInfo artifactTemplateInfo;
+
+ public GroupTemplateInfo() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ public boolean isBase() {
+ return isBase;
+ }
+
+ public void setBase(boolean isBase) {
+ this.isBase = isBase;
+ }
+
+ public ArtifactTemplateInfo getArtifactTemplateInfo() {
+ return artifactTemplateInfo;
+ }
+
+ public void setArtifactTemplateInfo(ArtifactTemplateInfo artifactTemplateInfo) {
+ this.artifactTemplateInfo = artifactTemplateInfo;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/MergedArtifactInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/MergedArtifactInfo.java
new file mode 100644
index 0000000000..95038b1830
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/MergedArtifactInfo.java
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+
+public class MergedArtifactInfo {
+
+ private List<ArtifactDefinition> createdArtifact;
+ private ArtifactTemplateInfo jsonArtifactTemplate;
+ private Set<String> parsetArtifactsNames;
+
+ public List<ArtifactDefinition> getCreatedArtifact() {
+ return createdArtifact;
+ }
+
+ public void setCreatedArtifact(List<ArtifactDefinition> createdArtifact) {
+ this.createdArtifact = createdArtifact;
+ parsetArtifactsNames = new HashSet<String>();
+ parsetArtifactsNames.add(jsonArtifactTemplate.getFileName());
+ List<ArtifactTemplateInfo> relatedGroupTemplateList = jsonArtifactTemplate.getRelatedArtifactsInfo();
+ if (relatedGroupTemplateList != null && !relatedGroupTemplateList.isEmpty()) {
+ this.createArtifactsGroupSet(relatedGroupTemplateList, parsetArtifactsNames);
+ }
+
+ }
+
+ public ArtifactTemplateInfo getJsonArtifactTemplate() {
+ return jsonArtifactTemplate;
+ }
+
+ public void setJsonArtifactTemplate(ArtifactTemplateInfo jsonArtifactTemplate) {
+ this.jsonArtifactTemplate = jsonArtifactTemplate;
+ }
+
+ public List<ArtifactTemplateInfo> getListToAssociateArtifactToGroup() {
+ List<ArtifactTemplateInfo> resList = new ArrayList<ArtifactTemplateInfo>();
+ List<ArtifactTemplateInfo> relatedArtifacts = jsonArtifactTemplate.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null && !relatedArtifacts.isEmpty()) {
+ getNewArtifactsInGroup(resList, relatedArtifacts);
+ }
+ return resList;
+ }
+
+ public List<ArtifactDefinition> getListToDissotiateArtifactFromGroup(List<ArtifactDefinition> deletedArtifacts) {
+ List<ArtifactDefinition> resList = new ArrayList<ArtifactDefinition>();
+ for (ArtifactDefinition artifactDefinition : createdArtifact) {
+ if (!parsetArtifactsNames.contains(artifactDefinition.getArtifactName())) {
+ boolean isDeleted = false;
+ for (ArtifactDefinition deletedArtifact : deletedArtifacts) {
+ if (artifactDefinition.getUniqueId().equalsIgnoreCase(deletedArtifact.getUniqueId())) {
+ isDeleted = true;
+ break;
+ }
+
+ }
+ if (!isDeleted)
+ resList.add(artifactDefinition);
+ }
+
+ }
+
+ return resList;
+ }
+
+ public List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>> getListToUpdateArtifactInGroup() {
+ List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>> resList = new ArrayList<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>>();
+ for (ArtifactDefinition artifactDefinition : createdArtifact) {
+ if (artifactDefinition.getArtifactName().equalsIgnoreCase(jsonArtifactTemplate.getFileName())) {
+ resList.add(new ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>(artifactDefinition, jsonArtifactTemplate));
+ }
+ }
+ List<ArtifactTemplateInfo> relatedArtifacts = jsonArtifactTemplate.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null && !relatedArtifacts.isEmpty()) {
+ getUpdateArtifactsInGroup(resList, relatedArtifacts);
+ }
+ return resList;
+ }
+
+ private void getUpdateArtifactsInGroup(List<ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>> resList, List<ArtifactTemplateInfo> jsonArtifacts) {
+
+ for (ArtifactTemplateInfo artifactTemplateInfo : jsonArtifacts) {
+
+ for (ArtifactDefinition artifactDefinition : createdArtifact) {
+ if (artifactDefinition.getArtifactName().equalsIgnoreCase(artifactTemplateInfo.getFileName())) {
+ resList.add(new ImmutablePair<ArtifactDefinition, ArtifactTemplateInfo>(artifactDefinition, artifactTemplateInfo));
+ }
+ }
+
+ List<ArtifactTemplateInfo> relatedArtifacts = artifactTemplateInfo.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null && !relatedArtifacts.isEmpty()) {
+ getUpdateArtifactsInGroup(resList, relatedArtifacts);
+ }
+ }
+ }
+
+ private void getNewArtifactsInGroup(List<ArtifactTemplateInfo> resList, List<ArtifactTemplateInfo> jsonArtifacts) {
+
+ for (ArtifactTemplateInfo artifactTemplateInfo : jsonArtifacts) {
+ boolean isNewArtifact = true;
+ for (ArtifactDefinition artifactDefinition : createdArtifact) {
+ if (artifactDefinition.getArtifactName().equalsIgnoreCase(artifactTemplateInfo.getFileName())) {
+ isNewArtifact = false;
+ }
+ }
+ if (isNewArtifact)
+ resList.add(artifactTemplateInfo);
+ List<ArtifactTemplateInfo> relatedArtifacts = artifactTemplateInfo.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null && !relatedArtifacts.isEmpty()) {
+ getNewArtifactsInGroup(resList, relatedArtifacts);
+ }
+ }
+ }
+
+ private void createArtifactsGroupSet(List<ArtifactTemplateInfo> parsedGroupTemplateList, Set<String> parsedArtifactsName) {
+
+ for (ArtifactTemplateInfo parsedGroupTemplate : parsedGroupTemplateList) {
+ parsedArtifactsName.add(parsedGroupTemplate.getFileName());
+ List<ArtifactTemplateInfo> relatedArtifacts = parsedGroupTemplate.getRelatedArtifactsInfo();
+ if (relatedArtifacts != null && !relatedArtifacts.isEmpty()) {
+ createArtifactsGroupSet(relatedArtifacts, parsedArtifactsName);
+ }
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceInfo.java
new file mode 100644
index 0000000000..457c49ee8e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceInfo.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class ServiceInfo {
+
+ private String name;
+ private List<ServiceVersionInfo> versions;
+
+ public ServiceInfo(String serviceName, List<ServiceVersionInfo> list) {
+ this.name = serviceName;
+ this.versions = list;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<ServiceVersionInfo> getVersions() {
+ return versions;
+ }
+
+ public void setVersions(List<ServiceVersionInfo> versions) {
+ this.versions = versions;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceVersionInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceVersionInfo.java
new file mode 100644
index 0000000000..13f8a1307f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServiceVersionInfo.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+public class ServiceVersionInfo {
+
+ // private String serviceName;
+ private String version;
+ private String url;
+
+ public ServiceVersionInfo(String serviceName, String serviceVersion, String context) {
+ super();
+ // this.serviceName = serviceName;
+ this.version = serviceVersion;
+ StringBuilder sb = new StringBuilder(context);
+ sb.append("services/").append(serviceName).append("/").append(serviceVersion);
+ url = sb.toString();
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String serviceVersion) {
+ this.version = serviceVersion;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServicesWrapper.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServicesWrapper.java
new file mode 100644
index 0000000000..9c26cf5959
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServicesWrapper.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class ServicesWrapper {
+ private List<ServiceInfo> services;
+
+ public List<ServiceInfo> getServices() {
+ return services;
+ }
+
+ public void setServices(List<ServiceInfo> services) {
+ this.services = services;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServletJsonResponse.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServletJsonResponse.java
new file mode 100644
index 0000000000..5559311e9e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ServletJsonResponse.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+public class ServletJsonResponse {
+
+ String source;
+ String description;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInfo.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInfo.java
new file mode 100644
index 0000000000..11b27af7a1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInfo.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class ToscaNodeTypeInfo {
+
+ private String nodeName;
+ private String templateVersion;
+ private List<ToscaNodeTypeInterface> interfaces;
+ private String iconPath;
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public String getTemplateVersion() {
+ return templateVersion;
+ }
+
+ public void setTemplateVersion(String templateVersion) {
+ this.templateVersion = templateVersion;
+ }
+
+ public List<ToscaNodeTypeInterface> getInterfaces() {
+ return interfaces;
+ }
+
+ public void setInterfaces(List<ToscaNodeTypeInterface> interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ public String getIconPath() {
+ return iconPath;
+ }
+
+ public void setIconPath(String iconPath) {
+ this.iconPath = iconPath;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaNodeTypeInfo [nodeName=" + nodeName + ", templateVersion=" + templateVersion + ", interfaces=" + interfaces + ", iconPath=" + iconPath + "]";
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInterface.java b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInterface.java
new file mode 100644
index 0000000000..25bab766bf
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/info/ToscaNodeTypeInterface.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.info;
+
+import java.util.List;
+
+public class ToscaNodeTypeInterface {
+
+ List<String> scripts;
+
+ public List<String> getScripts() {
+ return scripts;
+ }
+
+ public void setScripts(List<String> scripts) {
+ this.scripts = scripts;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/listen/BEAppContextListener.java b/catalog-be/src/main/java/org/openecomp/sdc/be/listen/BEAppContextListener.java
new file mode 100644
index 0000000000..d40a3d4433
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/listen/BEAppContextListener.java
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.listen;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.impl.DownloadArtifactLogic;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.monitoring.BeMonitoringService;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.listener.AppContextListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import org.openecomp.portalsdk.core.onboarding.ueb.UebException;
+import org.openecomp.portalsdk.core.onboarding.ueb.UebManager;
+
+public class BEAppContextListener extends AppContextListener implements ServletContextListener {
+
+ private static final String MANIFEST_FILE_NAME = "/META-INF/MANIFEST.MF";
+ private static Logger log = LoggerFactory.getLogger(BEAppContextListener.class.getName());
+
+ private static UebManager uebManager = null;
+
+ public void contextInitialized(ServletContextEvent context) {
+
+ super.contextInitialized(context);
+
+ ConfigurationManager configurationManager = new ConfigurationManager(ExternalConfiguration.getConfigurationSource());
+ log.debug("loading configuration from configDir: {} appName: {}", ExternalConfiguration.getConfigDir(), ExternalConfiguration.getAppName());
+
+ context.getServletContext().setAttribute(Constants.CONFIGURATION_MANAGER_ATTR, configurationManager);
+
+ WebAppContextWrapper webAppContextWrapper = new WebAppContextWrapper();
+ context.getServletContext().setAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR, webAppContextWrapper);
+
+ DownloadArtifactLogic downloadArtifactLogic = new DownloadArtifactLogic();
+ context.getServletContext().setAttribute(Constants.DOWNLOAD_ARTIFACT_LOGIC_ATTR, downloadArtifactLogic);
+
+ context.getServletContext().setAttribute(Constants.SDC_RELEASE_VERSION_ATTR, getVersionFromManifest(context));
+
+ // Monitoring service
+ BeMonitoringService bms = new BeMonitoringService(context.getServletContext());
+ bms.start(configurationManager.getConfiguration().getSystemMonitoring().getProbeIntervalInSeconds(15));
+
+ initUebManager();
+
+ log.debug("After executing {}", this.getClass());
+
+ }
+
+ private void initUebManager() {
+ try {
+ if (uebManager == null) {
+ uebManager = UebManager.getInstance();
+ uebManager.initListener(null);
+ }
+ } catch (UebException ex) {
+ log.debug("Failed to initialize UebManager", ex);
+ BeEcompErrorManager.getInstance().logInternalConnectionError("InitUebManager", "Failed to initialize listener of UebManager", ErrorSeverity.ERROR);
+ }
+ log.debug("After init listener of UebManager");
+ }
+
+ public void contextDestroyed(ServletContextEvent context) {
+ if (uebManager != null) {
+ uebManager.shutdown();
+ uebManager = null;
+ }
+ super.contextDestroyed(context);
+
+ }
+
+ private IResourceOperation getResourceOperationManager(Class<? extends IResourceOperation> clazz, WebApplicationContext webContext) {
+
+ return webContext.getBean(clazz);
+ }
+
+ private String getVersionFromManifest(ServletContextEvent context) {
+ ServletContext servletContext = context.getServletContext();
+ InputStream inputStream = servletContext.getResourceAsStream(MANIFEST_FILE_NAME);
+
+ String version = null;
+ try {
+ Manifest mf = new Manifest(inputStream);
+ Attributes atts = mf.getMainAttributes();
+ version = atts.getValue(Constants.SDC_RELEASE_VERSION_ATTR);
+ if (version == null || version.isEmpty()) {
+ log.warn("failed to read ASDC version from MANIFEST.");
+ } else {
+ log.info("ASDC version from MANIFEST is {}", version);
+ }
+
+ } catch (IOException e) {
+ log.warn("failed to read ASDC version from MANIFEST", e.getMessage());
+ }
+
+ return version;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/monitoring/EsGateway.java b/catalog-be/src/main/java/org/openecomp/sdc/be/monitoring/EsGateway.java
new file mode 100644
index 0000000000..34a56cdd13
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/monitoring/EsGateway.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.monitoring;
+
+import java.net.URI;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.openecomp.sdc.be.components.impl.MonitoringBusinessLogic;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.common.api.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+public class EsGateway extends ProxyServlet {
+
+ private static final long serialVersionUID = 1L;
+ private static Logger log = LoggerFactory.getLogger(EsGateway.class.getName());
+
+ @Override
+ public URI rewriteURI(HttpServletRequest request) {
+
+ String originalUrl = request.getRequestURI();
+
+ String redirectedUrl = getModifiedUrl(request);
+
+ log.debug("EsGateway Redirecting request from: {} , to: {}", originalUrl, redirectedUrl);
+ return URI.create(redirectedUrl);
+ }
+
+ @Override
+ public void customizeProxyRequest(Request proxyRequest, HttpServletRequest request) {
+ super.customizeProxyRequest(proxyRequest, request);
+
+ }
+
+ @Override
+ protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse) {
+ super.onResponseSuccess(request, response, proxyResponse);
+ }
+
+ public String getModifiedUrl(HttpServletRequest request) {
+ String esHost = null;
+ String esPort = null;
+ MonitoringBusinessLogic monitoringBL = getMonitoringBL(request.getSession().getServletContext());
+ if (monitoringBL == null) {
+ log.error("failed to retrive monitoringBL.");
+ } else {
+ esHost = monitoringBL.getEsHost();
+ esPort = monitoringBL.getEsPort();
+ }
+
+ String scheme = request.getScheme();
+ String contextPath = request.getContextPath(); // /mywebapp
+ String servletPath = request.getServletPath(); // /servlet/MyServlet
+ String pathInfo = request.getPathInfo(); // /a/b;c=123
+ String queryString = request.getQueryString(); // d=789
+
+ StringBuilder url = new StringBuilder();
+ url.append(scheme).append("://").append(esHost);
+ url.append(":").append(esPort);
+ url.append(contextPath).append(servletPath);
+
+ if (pathInfo != null) {
+ url.append(pathInfo);
+ }
+ if (queryString != null) {
+ url.append("?").append(queryString);
+ }
+
+ String redirectedUrl = url.toString().replace("/sdc2/esGateway/", "/");
+ return redirectedUrl;
+
+ }
+
+ protected MonitoringBusinessLogic getMonitoringBL(ServletContext context) {
+
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ MonitoringBusinessLogic monitoringBusinessLogic = webApplicationContext.getBean(MonitoringBusinessLogic.class);
+
+ return monitoringBusinessLogic;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AbstractValidationsServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AbstractValidationsServlet.java
new file mode 100644
index 0000000000..0e8addf692
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AbstractValidationsServlet.java
@@ -0,0 +1,843 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+import java.util.zip.ZipInputStream;
+
+import javax.annotation.Resource;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.CsarValidationUtils;
+import org.openecomp.sdc.be.components.impl.ImportUtils;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaElementTypeEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.ServletUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.servlets.ResourceUploadServlet.ResourceAuthorityTypeEnum;
+import org.openecomp.sdc.be.user.IUserBusinessLogic;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.UploadArtifactInfo;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+import org.openecomp.sdc.common.util.ZipUtil;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.context.WebApplicationContext;
+import org.yaml.snakeyaml.Yaml;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+import fj.data.Either;
+
+public abstract class AbstractValidationsServlet extends BeGenericServlet {
+
+ @Resource
+ private ServletUtils servletUtils;
+
+ @Resource
+ private ResourceImportManager resourceImportManager;
+
+ @Autowired
+ protected ComponentsUtils componentsUtils;
+
+ private static final Object LOCK = new Object();
+ private Logger log = null;
+
+ protected void init(Logger log) {
+ initLog(log);
+ initSpringFromContext();
+
+ }
+
+ private void initLog(Logger log) {
+ if (this.log == null) {
+ synchronized (LOCK) {
+ if (this.log == null) {
+ this.log = log;
+ }
+
+ }
+ }
+ }
+
+ private void initSpringFromContext() {
+ if (servletUtils == null) {
+ synchronized (LOCK) {
+ if (servletUtils == null) {
+ ServletContext context = servletRequest.getSession().getServletContext();
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ servletUtils = webApplicationContext.getBean(ServletUtils.class);
+ resourceImportManager = webApplicationContext.getBean(ResourceImportManager.class);
+ }
+ }
+ }
+ }
+
+ protected void validateResourceDoesNotExist(Wrapper<Response> responseWrapper, User user, String resourceName) {
+ if (resourceImportManager.isResourceExist(resourceName)) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.RESOURCE_ALREADY_EXISTS);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceName);
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ }
+
+ protected void validateUserExist(Wrapper<Response> responseWrapper, Wrapper<User> userWrapper, String userId) {
+ log.debug("get user {} from DB", userId);
+ // get user details
+ if (userId == null) {
+ log.info("user userId is null");
+ Response response = returnMissingInformation(new User());
+ responseWrapper.setInnerElement(response);
+ }
+
+ else {
+ IUserBusinessLogic userAdmin = getServletUtils().getUserAdmin();
+ Either<User, ActionStatus> eitherCreator = userAdmin.getUser(userId, false);
+ if (eitherCreator.isRight()) {
+ log.info("user is not listed. userId={}", userId);
+ User user = new User();
+ user.setUserId(userId);
+ Response response = returnMissingInformation(user);
+ responseWrapper.setInnerElement(response);
+ } else {
+ userWrapper.setInnerElement(eitherCreator.left().value());
+ }
+ }
+ }
+
+ protected Response returnMissingInformation(User user) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ return buildErrorResponse(responseFormat);
+ }
+
+ protected void validateDataNotNull(Wrapper<Response> responseWrapper, Object... dataParams) {
+ for (Object dataElement : dataParams) {
+ if (dataElement == null) {
+ log.info("Invalid body was received.");
+ Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ responseWrapper.setInnerElement(response);
+ break;
+ }
+ }
+
+ }
+
+ protected void validateUserRole(Wrapper<Response> errorResponseWrapper, User user) {
+ log.debug("validate user role");
+ if (!user.getRole().equals(Role.ADMIN.name()) && !user.getRole().equals(Role.DESIGNER.name())) {
+ log.info("user is not in appropriate role to perform action");
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ log.debug("audit before sending response");
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+
+ Response response = buildErrorResponse(responseFormat);
+ errorResponseWrapper.setInnerElement(response);
+ }
+
+ }
+
+ protected void validateZip(Wrapper<Response> responseWrapper, File file, String payloadName) throws FileNotFoundException {
+ InputStream fileInputStream = new FileInputStream(file);
+ Map<String, byte[]> unzippedFolder = ZipUtil.readZip(new ZipInputStream(fileInputStream));
+ if (payloadName == null || payloadName.isEmpty() || !unzippedFolder.containsKey(payloadName)) {
+ log.info("Invalid json was received. payloadName should be yml file name");
+ Response errorResponse = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ responseWrapper.setInnerElement(errorResponse);
+ }
+
+ }
+
+ protected void fillZipContents(Wrapper<String> yamlStringWrapper, File file) throws FileNotFoundException {
+ InputStream fileInputStream = new FileInputStream(file);
+ Map<String, byte[]> unzippedFolder = ZipUtil.readZip(new ZipInputStream(fileInputStream));
+ String ymlName = unzippedFolder.keySet().iterator().next();
+ fillToscaTemplateFromZip(yamlStringWrapper, ymlName, file);
+ }
+
+ protected void fillToscaTemplateFromZip(Wrapper<String> yamlStringWrapper, String payloadName, File file) throws FileNotFoundException {
+ InputStream fileInputStream = new FileInputStream(file);
+ Map<String, byte[]> unzippedFolder = ZipUtil.readZip(new ZipInputStream(fileInputStream));
+ byte[] yamlFileInBytes = unzippedFolder.get(payloadName);
+ String yamlAsString = new String(yamlFileInBytes, StandardCharsets.UTF_8);
+ log.debug("received yaml: {}", yamlAsString);
+ yamlStringWrapper.setInnerElement(yamlAsString);
+ }
+
+ protected void validateUserRole(Wrapper<Response> errorResponseWrapper, User user, ResourceAuthorityTypeEnum resourceAuthority) {
+ log.debug("validate user role");
+ if (resourceAuthority == ResourceAuthorityTypeEnum.NORMATIVE_TYPE_BE) {
+ if (!user.getRole().equals(Role.ADMIN.name())) {
+ log.info("user is not in appropriate role to perform action");
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ log.debug("audit before sending response");
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+
+ Response response = buildErrorResponse(responseFormat);
+ errorResponseWrapper.setInnerElement(response);
+ }
+ } else {
+ validateUserRole(errorResponseWrapper, user);
+ }
+
+ }
+
+ protected void validateAndFillResourceJson(Wrapper<Response> responseWrapper, Wrapper<UploadResourceInfo> uploadResourceInfoWrapper, User user, ResourceAuthorityTypeEnum resourceAuthorityEnum, String resourceInfo) {
+ boolean isValid;
+ try {
+ log.debug("The received json is {}", resourceInfo);
+ UploadResourceInfo resourceInfoObject = gson.fromJson(resourceInfo, UploadResourceInfo.class);
+ if (resourceInfoObject == null) {
+ isValid = false;
+ } else {
+ if (!resourceAuthorityEnum.isBackEndImport()) {
+ isValid = resourceInfoObject.getPayloadName() != null && !resourceInfoObject.getPayloadName().isEmpty();
+ // TODO Tal only resource name is checked
+ } else {
+ isValid = true;
+ }
+ uploadResourceInfoWrapper.setInnerElement(resourceInfoObject);
+ }
+
+ } catch (JsonSyntaxException e) {
+ isValid = false;
+
+ }
+ if (!isValid) {
+ log.info("Invalid json was received.");
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, null);
+ Response errorResp = buildErrorResponse(responseFormat);
+ responseWrapper.setInnerElement(errorResp);
+ }
+ }
+
+ protected void validateAuthorityType(Wrapper<Response> responseWrapper, String authorityType) {
+ log.debug("The received authority type is {}", authorityType);
+ ResourceAuthorityTypeEnum authorityTypeEnum = ResourceAuthorityTypeEnum.findByUrlPath(authorityType);
+ if (authorityTypeEnum == null) {
+ log.info("Invalid authority type was received.");
+ Response errorResp = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ responseWrapper.setInnerElement(errorResp);
+ }
+ }
+
+ public ServletUtils getServletUtils() {
+ initSpringFromContext();
+ return servletUtils;
+ }
+
+ public Gson getGson() {
+ return getServletUtils().getGson();
+ }
+
+ public ComponentsUtils getComponentsUtils() {
+ return getServletUtils().getComponentsUtils();
+ }
+
+ protected void validatePayloadIsTosca(Wrapper<Response> responseWrapper, UploadResourceInfo uploadResourceInfo, User user, String toscaPayload) {
+ log.debug("checking payload is valid tosca");
+ boolean isValid;
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(toscaPayload)) ? new String(Base64.decodeBase64(toscaPayload)) : toscaPayload;
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+ Either<String, ResultStatusEnum> findFirstToscaStringElement = ImportUtils.findFirstToscaStringElement(mappedToscaTemplate, ToscaTagNamesEnum.TOSCA_VERSION);
+
+ if (findFirstToscaStringElement.isRight()) {
+ isValid = false;
+ } else {
+ String defenitionVersionFound = findFirstToscaStringElement.left().value();
+ if (defenitionVersionFound == null || defenitionVersionFound.isEmpty()) {
+ isValid = false;
+ } else {
+ isValid = ImportUtils.Constants.TOSCA_DEFINITION_VERSIONS.contains(defenitionVersionFound);
+ }
+ }
+
+ if (!isValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_TOSCA_TEMPLATE);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+
+ }
+
+ protected void validatePayloadIsYml(Wrapper<Response> responseWrapper, User user, UploadResourceInfo uploadResourceInfo, String toscaTamplatePayload) {
+ log.debug("checking tosca template is valid yml");
+ YamlToObjectConverter yamlConvertor = new YamlToObjectConverter();
+ boolean isYamlValid = yamlConvertor.isValidYaml(toscaTamplatePayload.getBytes());
+ if (!isYamlValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_YAML_FILE);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ }
+
+ protected void validatePayloadNameSpace(Wrapper<Response> responseWrapper, UploadResourceInfo resourceInfo, User user, String toscaPayload) {
+ boolean isValid;
+ String nameSpace = "";
+
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(toscaPayload)) ? new String(Base64.decodeBase64(toscaPayload)) : toscaPayload;
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+ Either<Map<String, Object>, ResultStatusEnum> toscaElement = ImportUtils.findFirstToscaMapElement(mappedToscaTemplate, ToscaTagNamesEnum.NODE_TYPES);
+ if (toscaElement.isRight() || toscaElement.left().value().size() != 1) {
+ isValid = false;
+ } else {
+ nameSpace = toscaElement.left().value().keySet().iterator().next();
+ isValid = nameSpace.startsWith(Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX);
+ }
+ if (!isValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_RESOURCE_NAMESPACE);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ } else {
+ String str1 = nameSpace.substring(Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX.length());
+ String[] findTypes = str1.split("\\.");
+ if (ResourceTypeEnum.contains(findTypes[0].toUpperCase())) {
+ String type = findTypes[0].toUpperCase();
+ resourceInfo.setResourceType(type);
+ } else {
+ resourceInfo.setResourceType(ResourceTypeEnum.VFC.name());
+ }
+ }
+
+ }
+
+ protected void validatePayloadIsSingleResource(Wrapper<Response> responseWrapper, UploadResourceInfo uploadResourceInfo, User user, String toscaPayload) {
+ log.debug("checking payload contains single resource");
+ boolean isValid;
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(toscaPayload)) ? new String(Base64.decodeBase64(toscaPayload)) : toscaPayload;
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+ Either<Map<String, Object>, ResultStatusEnum> toscaElement = ImportUtils.findFirstToscaMapElement(mappedToscaTemplate, ToscaTagNamesEnum.NODE_TYPES);
+ if (toscaElement.isRight()) {
+ isValid = false;
+ } else {
+ isValid = toscaElement.left().value().size() == 1;
+ }
+
+ if (!isValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NOT_SINGLE_RESOURCE);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+
+ }
+
+ protected void validatePayloadIsNotService(Wrapper<Response> responseWrapper, User user, UploadResourceInfo uploadResourceInfo, String toscaPayload) {
+ log.debug("checking payload is not a tosca service");
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(toscaPayload)) ? new String(Base64.decodeBase64(toscaPayload)) : toscaPayload;
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+ Either<Object, ResultStatusEnum> toscaElement = ImportUtils.findToscaElement(mappedToscaTemplate, ToscaTagNamesEnum.TOPOLOGY_TEMPLATE, ToscaElementTypeEnum.ALL);
+
+ if (toscaElement.isLeft()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NOT_RESOURCE_TOSCA_TEMPLATE);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+
+ }
+
+ protected void validatePayloadIsTopologyTemplate(Wrapper<Response> responseWrapper, User user, UploadResourceInfo uploadResourceInfo, String toscaPayload) {
+ log.debug("checking payload is a tosca topology template");
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(toscaPayload)) ? new String(Base64.decodeBase64(toscaPayload)) : toscaPayload;
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+ Either<Object, ResultStatusEnum> toscaElement = ImportUtils.findToscaElement(mappedToscaTemplate, ToscaTagNamesEnum.TOPOLOGY_TEMPLATE, ToscaElementTypeEnum.ALL);
+
+ if (toscaElement.isRight()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NOT_TOPOLOGY_TOSCA_TEMPLATE);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+
+ }
+
+ protected void validateToscaTemplatePayloadName(Wrapper<Response> responseWrapper, UploadResourceInfo uploadResourceInfo, User user) {
+ String toscaTemplatePayloadName = uploadResourceInfo.getPayloadName();
+ boolean isValidSuffix = false;
+ if (toscaTemplatePayloadName != null && !toscaTemplatePayloadName.isEmpty()) {
+ for (String validSuffix : ImportUtils.Constants.TOSCA_YML_CSAR_VALID_SUFFIX) {
+ isValidSuffix = isValidSuffix || toscaTemplatePayloadName.toLowerCase().endsWith(validSuffix);
+ }
+ }
+ if (!isValidSuffix) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_TOSCA_FILE_EXTENSION);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+
+ }
+
+ protected void validateMD5(Wrapper<Response> responseWrapper, User user, UploadResourceInfo resourceInfo, HttpServletRequest request, String resourceInfoJsonString) {
+ boolean isValid;
+ String recievedMD5 = request.getHeader(Constants.MD5_HEADER);
+ if (recievedMD5 == null) {
+ isValid = false;
+ } else {
+ String calculateMD5 = GeneralUtility.calculateMD5ByString(resourceInfoJsonString);
+ isValid = calculateMD5.equals(recievedMD5);
+ }
+ if (!isValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_RESOURCE_CHECKSUM);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ }
+
+ protected void validateComponentType(Wrapper<Response> responseWrapper, Wrapper<ComponentTypeEnum> componentWrapper, String componentType) {
+ boolean isValid;
+ if (componentType == null) {
+ isValid = false;
+ } else {
+ if (ComponentTypeEnum.RESOURCE_PARAM_NAME.equalsIgnoreCase(componentType)) {
+ isValid = true;
+ componentWrapper.setInnerElement(ComponentTypeEnum.RESOURCE);
+ } else if (ComponentTypeEnum.SERVICE_PARAM_NAME.equalsIgnoreCase(componentType)) {
+ isValid = true;
+ componentWrapper.setInnerElement(ComponentTypeEnum.SERVICE);
+ } else {
+ isValid = false;
+ }
+ }
+ if (!isValid) {
+ log.debug("Invalid componentType:{}", componentType);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ Response errorResp = buildErrorResponse(responseFormat);
+ responseWrapper.setInnerElement(errorResp);
+ }
+ }
+
+ protected void fillToscaTemplateFromJson(Wrapper<Response> responseWrapper, Wrapper<String> yamlStringWrapper, User user, UploadResourceInfo resourceInfo) {
+ if (resourceInfo.getPayloadData() == null || resourceInfo.getPayloadData().isEmpty()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_RESOURCE_PAYLOAD);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ } else {
+ String toscaPayload = resourceInfo.getPayloadData();
+ String decodedPayload = (GeneralUtility.isBase64Encoded(toscaPayload)) ? new String(Base64.decodeBase64(toscaPayload)) : toscaPayload;
+ yamlStringWrapper.setInnerElement(decodedPayload);
+ }
+
+ }
+
+ protected void fillPayload(Wrapper<Response> responseWrapper, Wrapper<UploadResourceInfo> uploadResourceInfoWrapper, Wrapper<String> yamlStringWrapper, User user, String resourceInfoJsonString, ResourceAuthorityTypeEnum resourceAuthorityEnum,
+ File file) throws FileNotFoundException {
+
+ if (responseWrapper.isEmpty()) {
+ if (resourceAuthorityEnum.isBackEndImport()) {
+ // PrePayload Validations
+ if (responseWrapper.isEmpty()) {
+ validateDataNotNull(responseWrapper, file, resourceInfoJsonString);
+ }
+ if (responseWrapper.isEmpty()) {
+ validateZip(responseWrapper, file, uploadResourceInfoWrapper.getInnerElement().getPayloadName());
+ }
+
+ // Fill PayLoad From File
+ if (responseWrapper.isEmpty()) {
+ fillToscaTemplateFromZip(yamlStringWrapper, uploadResourceInfoWrapper.getInnerElement().getPayloadName(), file);
+ }
+
+ } else {
+ // Fill PayLoad From JSON
+ if (responseWrapper.isEmpty()) {
+ fillToscaTemplateFromJson(responseWrapper, yamlStringWrapper, user, uploadResourceInfoWrapper.getInnerElement());
+ }
+ }
+
+ }
+
+ }
+
+ protected void specificResourceAuthorityValidations(Wrapper<Response> responseWrapper, Wrapper<UploadResourceInfo> uploadResourceInfoWrapper, Wrapper<String> yamlStringWrapper, User user, HttpServletRequest request, String resourceInfoJsonString,
+ ResourceAuthorityTypeEnum resourceAuthorityEnum) throws FileNotFoundException {
+
+ if (responseWrapper.isEmpty()) {
+ // UI Only Validation
+ if (!resourceAuthorityEnum.isBackEndImport()) {
+ importUIValidations(responseWrapper, uploadResourceInfoWrapper.getInnerElement(), user, request, resourceInfoJsonString);
+ }
+
+ // User Defined Type Resources
+ if (resourceAuthorityEnum.isUserTypeResource() && !CsarValidationUtils.isCsarPayloadName(uploadResourceInfoWrapper.getInnerElement().getPayloadName())) {
+ if (responseWrapper.isEmpty()) {
+ validatePayloadNameSpace(responseWrapper, uploadResourceInfoWrapper.getInnerElement(), user, yamlStringWrapper.getInnerElement());
+ }
+
+ }
+ }
+ }
+
+ protected void commonGeneralValidations(Wrapper<Response> responseWrapper, Wrapper<User> userWrapper, Wrapper<UploadResourceInfo> uploadResourceInfoWrapper, ResourceAuthorityTypeEnum resourceAuthorityEnum, String userId,
+ String resourceInfoJsonString) {
+
+ if (responseWrapper.isEmpty()) {
+ validateUserExist(responseWrapper, userWrapper, userId);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ validateUserRole(responseWrapper, userWrapper.getInnerElement(), resourceAuthorityEnum);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ validateAndFillResourceJson(responseWrapper, uploadResourceInfoWrapper, userWrapper.getInnerElement(), resourceAuthorityEnum, resourceInfoJsonString);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ validateToscaTemplatePayloadName(responseWrapper, uploadResourceInfoWrapper.getInnerElement(), userWrapper.getInnerElement());
+ }
+ if (responseWrapper.isEmpty()) {
+ validateResourceType(responseWrapper, uploadResourceInfoWrapper.getInnerElement(), userWrapper.getInnerElement());
+ }
+
+ }
+
+ private void validateResourceType(Wrapper<Response> responseWrapper, UploadResourceInfo uploadResourceInfo, User user) {
+ String resourceType = uploadResourceInfo.getResourceType();
+ if (resourceType == null || !ResourceTypeEnum.contains(resourceType)) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ Response errorResponse = buildErrorResponse(responseFormat);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, uploadResourceInfo.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ }
+
+ protected void importUIValidations(Wrapper<Response> responseWrapper, UploadResourceInfo resourceInfo, User user, HttpServletRequest request, String resourceInfoJsonString) {
+ if (responseWrapper.isEmpty()) {
+ validateMD5(responseWrapper, user, resourceInfo, request, resourceInfoJsonString);
+ }
+ if (responseWrapper.isEmpty() && request != null && request.getMethod() != null && request.getMethod().equals("POST")) {
+ validateResourceDoesNotExist(responseWrapper, user, resourceInfo.getName());
+ }
+ }
+
+ protected void commonPayloadValidations(Wrapper<Response> responseWrapper, Wrapper<String> yamlStringWrapper, User user, UploadResourceInfo uploadResourceInfo) {
+
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsYml(responseWrapper, user, uploadResourceInfo, yamlStringWrapper.getInnerElement());
+ }
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsTosca(responseWrapper, uploadResourceInfo, user, yamlStringWrapper.getInnerElement());
+ }
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsNotService(responseWrapper, user, uploadResourceInfo, yamlStringWrapper.getInnerElement());
+ }
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsSingleResource(responseWrapper, uploadResourceInfo, user, yamlStringWrapper.getInnerElement());
+ }
+ }
+
+ protected void topologyTemplatePayloadValidations(Wrapper<Response> responseWrapper, Wrapper<String> yamlStringWrapper, User user, UploadResourceInfo uploadResourceInfo) {
+
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsYml(responseWrapper, user, uploadResourceInfo, yamlStringWrapper.getInnerElement());
+ }
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsTosca(responseWrapper, uploadResourceInfo, user, yamlStringWrapper.getInnerElement());
+ }
+ if (responseWrapper.isEmpty()) {
+ validatePayloadIsTopologyTemplate(responseWrapper, user, uploadResourceInfo, yamlStringWrapper.getInnerElement());
+ }
+
+ }
+
+ protected void handleImport(Wrapper<Response> responseWrapper, User user, UploadResourceInfo resourceInfoObject, String yamlAsString, ResourceAuthorityTypeEnum authority, boolean createNewVersion, String resourceUniqueId) {
+ Either<ImmutablePair<org.openecomp.sdc.be.model.Resource, ActionStatus>, ResponseFormat> createOrUpdateResponse = null;
+ Response response = null;
+ Object representation = null;
+
+ if (CsarValidationUtils.isCsarPayloadName(resourceInfoObject.getPayloadName())) {
+ log.debug("import resource from csar");
+ createOrUpdateResponse = importResourceFromUICsar(resourceInfoObject, user, resourceUniqueId);
+ } else if (!authority.isUserTypeResource()) {
+ log.debug("import normative type resource");
+ createOrUpdateResponse = resourceImportManager.importNormativeResource(yamlAsString, resourceInfoObject, user, createNewVersion, true);
+ } else {
+ log.debug("import user resource (not normative type)");
+ createOrUpdateResponse = resourceImportManager.importUserDefinedResource(yamlAsString, resourceInfoObject, user, false, false);
+ }
+ if (createOrUpdateResponse.isRight()) {
+ response = buildErrorResponse(createOrUpdateResponse.right().value());
+ } else {
+ try {
+ representation = RepresentationUtils.toRepresentation(createOrUpdateResponse.left().value().getLeft());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ ActionStatus successStatus = createOrUpdateResponse.left().value().right;
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(successStatus), representation);
+ }
+ responseWrapper.setInnerElement(response);
+ }
+
+ private Either<ImmutablePair<org.openecomp.sdc.be.model.Resource, ActionStatus>, ResponseFormat> importResourceFromUICsar(UploadResourceInfo resourceInfoObject, User user, String resourceUniqueId) {
+
+ Either<org.openecomp.sdc.be.model.Resource, ResponseFormat> createOrUpdateResourceRes = null;
+ ImmutablePair<org.openecomp.sdc.be.model.Resource, ActionStatus> result = null;
+ ActionStatus actionStatus = null;
+ org.openecomp.sdc.be.model.Resource resource = new org.openecomp.sdc.be.model.Resource();
+ String payloadName = resourceInfoObject.getPayloadName();
+ fillResourceFromResourceInfoObject(resource, resourceInfoObject);
+
+ Either<Map<String, byte[]>, ResponseFormat> csarUIPayloadRes = getScarFromPayload(resourceInfoObject);
+ if (csarUIPayloadRes.isRight()) {
+ return Either.right(csarUIPayloadRes.right().value());
+ }
+ Map<String, byte[]> csarUIPayload = csarUIPayloadRes.left().value();
+
+ createOrUpdateResourceRes = getAndValidateCsarYaml(csarUIPayload, resource, user, payloadName);
+ if (createOrUpdateResourceRes.isRight()) {
+ return Either.right(createOrUpdateResourceRes.right().value());
+ }
+ if (resourceUniqueId == null || resourceUniqueId.isEmpty()) {
+ createOrUpdateResourceRes = resourceImportManager.getResourceBusinessLogic().createResource(resource, user, csarUIPayload, payloadName);
+ if (createOrUpdateResourceRes.isRight()) {
+ return Either.right(createOrUpdateResourceRes.right().value());
+ }
+ actionStatus = ActionStatus.CREATED;
+ } else {
+ createOrUpdateResourceRes = resourceImportManager.getResourceBusinessLogic().validateAndUpdateResourceFromCsar(resource, user, csarUIPayload, payloadName, resourceUniqueId);
+ if (createOrUpdateResourceRes.isRight()) {
+ return Either.right(createOrUpdateResourceRes.right().value());
+ }
+ actionStatus = ActionStatus.OK;
+ }
+ result = new ImmutablePair<org.openecomp.sdc.be.model.Resource, ActionStatus>(createOrUpdateResourceRes.left().value(), actionStatus);
+ return Either.left(result);
+ }
+
+ private Either<org.openecomp.sdc.be.model.Resource, ResponseFormat> getAndValidateCsarYaml(Map<String, byte[]> csarUIPayload, org.openecomp.sdc.be.model.Resource resource, User user, String csarUUID) {
+
+ Either<ImmutablePair<String, String>, ResponseFormat> getToscaYamlRes = CsarValidationUtils.getToscaYaml(csarUIPayload, csarUUID, getComponentsUtils());
+
+ if (getToscaYamlRes.isRight()) {
+ ResponseFormat responseFormat = getToscaYamlRes.right().value();
+ log.debug("Error when try to get csar toscayamlFile with csar ID {}, error: {}", csarUUID, responseFormat);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Creating resource from CSAR: fetching CSAR with id " + csarUUID + " failed");
+ getComponentsUtils().auditResource(responseFormat, user, resource, "", "", AuditingActionEnum.CREATE_RESOURCE, null);
+ return Either.right(responseFormat);
+ }
+ String toscaYaml = getToscaYamlRes.left().value().getValue();
+
+ log.debug("checking tosca template is valid yml");
+ YamlToObjectConverter yamlConvertor = new YamlToObjectConverter();
+ boolean isValid = yamlConvertor.isValidYaml(toscaYaml.getBytes());
+ if (!isValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_YAML_FILE);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resource.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ return Either.right(responseFormat);
+ }
+
+ log.debug("checking payload is valid tosca");
+ String heatDecodedPayload = (GeneralUtility.isBase64Encoded(toscaYaml)) ? new String(Base64.decodeBase64(toscaYaml)) : toscaYaml;
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(heatDecodedPayload);
+ Either<String, ResultStatusEnum> findFirstToscaStringElement = ImportUtils.findFirstToscaStringElement(mappedToscaTemplate, ToscaTagNamesEnum.TOSCA_VERSION);
+
+ if (findFirstToscaStringElement.isRight()) {
+ isValid = false;
+ } else {
+ String defenitionVersionFound = findFirstToscaStringElement.left().value();
+ if (defenitionVersionFound == null || defenitionVersionFound.isEmpty()) {
+ isValid = false;
+ } else {
+ isValid = ImportUtils.Constants.TOSCA_DEFINITION_VERSIONS.contains(defenitionVersionFound);
+ }
+ }
+
+ if (!isValid) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_TOSCA_TEMPLATE);
+ EnumMap<AuditingFieldsKeysEnum, Object> additionalParam = new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class);
+ additionalParam.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resource.getName());
+ getComponentsUtils().auditResource(responseFormat, user, null, "", "", AuditingActionEnum.IMPORT_RESOURCE, additionalParam);
+ return Either.right(responseFormat);
+ }
+ return Either.left(resource);
+ }
+
+ private void fillResourceFromResourceInfoObject(org.openecomp.sdc.be.model.Resource resource, UploadResourceInfo resourceInfoObject) {
+ if (resource != null && resourceInfoObject != null) {
+ resource.setDescription(resourceInfoObject.getDescription());
+ resource.setTags(resourceInfoObject.getTags());
+ resource.setCategories(resourceInfoObject.getCategories());
+ resource.setContactId(resourceInfoObject.getContactId());
+ resource.setName(resourceInfoObject.getName());
+ resource.setIcon(resourceInfoObject.getResourceIconPath());
+ resource.setVendorName(resourceInfoObject.getVendorName());
+ resource.setVendorRelease(resourceInfoObject.getVendorRelease());
+ resource.setResourceType(ResourceTypeEnum.valueOf(resourceInfoObject.getResourceType()));
+ List<UploadArtifactInfo> artifactList = resourceInfoObject.getArtifactList();
+ if (artifactList != null) {
+ Map<String, ArtifactDefinition> artifactsHM = new HashMap<String, ArtifactDefinition>();
+ for (UploadArtifactInfo artifact : artifactList) {
+ ArtifactDefinition artifactDef = new ArtifactDefinition();
+ artifactDef.setArtifactName(artifact.getArtifactName());
+ artifactDef.setArtifactType(artifact.getArtifactType().getType());
+ artifactDef.setDescription(artifact.getArtifactDescription());
+ artifactDef.setPayloadData(artifact.getArtifactData());
+ artifactDef.setArtifactRef(artifact.getArtifactPath());
+ artifactsHM.put(artifactDef.getArtifactName(), artifactDef);
+ }
+ resource.setArtifacts(artifactsHM);
+ }
+ }
+
+ }
+
+ private Either<Map<String, byte[]>, ResponseFormat> getScarFromPayload(UploadResourceInfo innerElement) {
+ String csarUUID = innerElement.getPayloadName();
+ String payloadData = innerElement.getPayloadData();
+ if (payloadData == null) {
+ log.info("Failed to decode received csar", csarUUID);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_NOT_FOUND, csarUUID));
+ }
+
+ byte[] decodedPayload = (GeneralUtility.isBase64Encoded(payloadData)) ? Base64.decodeBase64(payloadData.getBytes(StandardCharsets.UTF_8)) : payloadData.getBytes(StandardCharsets.UTF_8);
+ if (decodedPayload == null) {
+ log.info("Failed to decode received csar", csarUUID);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_NOT_FOUND, csarUUID));
+ }
+
+ Map<String, byte[]> csar = ZipUtil.readZip(decodedPayload);
+ if (csar == null) {
+ log.info("Failed to unzip received csar", csarUUID);
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.CSAR_INVALID, csarUUID));
+ }
+ return Either.left(csar);
+ }
+
+ protected void validateInputStream(final HttpServletRequest request, Wrapper<String> dataWrapper, Wrapper<ResponseFormat> errorWrapper) throws IOException {
+ InputStream inputStream = request.getInputStream();
+ byte[] bytes = IOUtils.toByteArray(inputStream);
+ if (bytes == null || bytes.length == 0) {
+ log.info("Empty body was sent.");
+ errorWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ } else {
+ dataWrapper.setInnerElement(new String(bytes, StandardCharsets.UTF_8));
+ }
+
+ }
+
+ protected <T> void validateClassParse(String data, Wrapper<T> parsedClassWrapper, Supplier<Class<T>> classGen, Wrapper<ResponseFormat> errorWrapper) {
+ try {
+ T parsedClass = gson.fromJson(data, classGen.get());
+ if (parsedClass == null) {
+ errorWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ } else {
+ parsedClassWrapper.setInnerElement(parsedClass);
+ }
+ } catch (JsonSyntaxException e) {
+ log.debug("Failed to decode received {} {} to object.", classGen.get().getName(), data, e);
+ errorWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+ }
+
+ protected void validateComponentInstanceBusinessLogic(HttpServletRequest request, String containerComponentType, Wrapper<ComponentInstanceBusinessLogic> blWrapper, Wrapper<ResponseFormat> errorWrapper) {
+ ServletContext context = request.getSession().getServletContext();
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ errorWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ } else {
+ blWrapper.setInnerElement(componentInstanceLogic);
+ }
+ }
+
+ protected <T> Response buildResponseFromElement(Wrapper<ResponseFormat> errorWrapper, Wrapper<T> attributeWrapper) throws IOException {
+ Response response;
+ if (errorWrapper.isEmpty()) {
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(attributeWrapper.getInnerElement());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+ } else {
+ response = buildErrorResponse(errorWrapper.getInnerElement());
+ }
+ return response;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AdditionalInformationServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AdditionalInformationServlet.java
new file mode 100644
index 0000000000..aaf8f96dca
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AdditionalInformationServlet.java
@@ -0,0 +1,472 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.AdditionalInformationBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.AdditionalInfoParameterInfo;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Additional Information Servlet", description = "Additional Information Servlet")
+@Singleton
+public class AdditionalInformationServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(AdditionalInformationServlet.class.getName());
+
+ @POST
+ @Path("/resources/{resourceId}/additionalinfo")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Additional Information Label and Value", httpMethod = "POST", notes = "Returns created Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Additional information created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response createResourceAdditionalInformationLabel(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "Additional information key value to be created", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return createAdditionalInformationLabelForComponent(NodeTypeEnum.Resource, resourceId, request, userId, data);
+
+ }
+
+ @POST
+ @Path("/services/{serviceId}/additionalinfo")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Additional Information Label and Value", httpMethod = "POST", notes = "Returns created Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Additional information created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response createServiceAdditionalInformationLabel(@ApiParam(value = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId,
+ @ApiParam(value = "Additional information key value to be created", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return createAdditionalInformationLabelForComponent(NodeTypeEnum.Service, serviceId, request, userId, data);
+
+ }
+
+ @PUT
+ @Path("/resources/{resourceId}/additionalinfo/{labelId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Additional Information Label and Value", httpMethod = "PUT", notes = "Returns updated Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Additional information updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response updateResourceAdditionalInformationLabel(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "label id", required = true) @PathParam("labelId") final String labelId, @ApiParam(value = "Additional information key value to be created", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return updateAdditionalInformationLabelForComponent(NodeTypeEnum.Resource, resourceId, labelId, request, userId, data);
+
+ }
+
+ @PUT
+ @Path("/services/{serviceId}/additionalinfo/{labelId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Additional Information Label and Value", httpMethod = "PUT", notes = "Returns updated Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Additional information updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response updateServiceAdditionalInformationLabel(@ApiParam(value = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId,
+ @ApiParam(value = "label id", required = true) @PathParam("labelId") final String labelId, @ApiParam(value = "Additional information key value to be created", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return updateAdditionalInformationLabelForComponent(NodeTypeEnum.Service, serviceId, labelId, request, userId, data);
+
+ }
+
+ @DELETE
+ @Path("/resources/{resourceId}/additionalinfo/{labelId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Additional Information Label and Value", httpMethod = "DELETE", notes = "Returns deleted Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Additional information deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response updateResourceAdditionalInformationLabel(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "label id", required = true) @PathParam("labelId") final String labelId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return deleteAdditionalInformationLabelForComponent(NodeTypeEnum.Resource, resourceId, labelId, request, userId);
+
+ }
+
+ @DELETE
+ @Path("/services/{serviceId}/additionalinfo/{labelId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Additional Information Label and Value", httpMethod = "DELETE", notes = "Returns deleted Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Additional information deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response deleteServiceAdditionalInformationLabel(@ApiParam(value = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId,
+ @ApiParam(value = "label id", required = true) @PathParam("labelId") final String labelId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return deleteAdditionalInformationLabelForComponent(NodeTypeEnum.Service, serviceId, labelId, request, userId);
+
+ }
+
+ @GET
+ @Path("/resources/{resourceId}/additionalinfo/{labelId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Additional Information by id", httpMethod = "GET", notes = "Returns Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "fetched additional information"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response getResourceAdditionalInformationLabel(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "label id", required = true) @PathParam("labelId") final String labelId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return getAdditionalInformationLabelForComponent(NodeTypeEnum.Resource, resourceId, labelId, request, userId);
+
+ }
+
+ @GET
+ @Path("/services/{serviceId}/additionalinfo/{labelId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Additional Information by id", httpMethod = "GET", notes = "Returns Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "fetched additional information"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response getServiceAdditionalInformationLabel(@ApiParam(value = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId,
+ @ApiParam(value = "label id", required = true) @PathParam("labelId") final String labelId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return getAdditionalInformationLabelForComponent(NodeTypeEnum.Service, serviceId, labelId, request, userId);
+
+ }
+
+ @GET
+ @Path("/resources/{resourceId}/additionalinfo")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get all Additional Information under resource", httpMethod = "GET", notes = "Returns Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "list of additional information"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response getAllResourceAdditionalInformationLabel(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return getAllAdditionalInformationLabelForComponent(NodeTypeEnum.Resource, resourceId, request, userId);
+
+ }
+
+ @GET
+ @Path("/services/{serviceId}/additionalinfo")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get all Additional Information under service", httpMethod = "GET", notes = "Returns Additional Inforamtion property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "list of additional information"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Additional information key already exist") })
+ public Response getAllServiceAdditionalInformationLabel(@ApiParam(value = "service id to update with new property", required = true) @PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ return getAllAdditionalInformationLabelForComponent(NodeTypeEnum.Service, serviceId, request, userId);
+
+ }
+
+ /**
+ *
+ * Create additional information property under given resource/service
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @param request
+ * @param userId
+ * @param data
+ * @return
+ */
+ protected Response createAdditionalInformationLabelForComponent(NodeTypeEnum nodeType, String uniqueId, HttpServletRequest request, String userId, String data) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+ log.debug("data is {}", data);
+
+ try {
+ // convert json to AdditionalInfoParameterInfo
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = gson.fromJson(data, AdditionalInfoParameterInfo.class);
+
+ // create the new property
+ AdditionalInformationBusinessLogic businessLogic = getBL(context);
+
+ Either<AdditionalInfoParameterInfo, ResponseFormat> either = businessLogic.createAdditionalInformation(nodeType, uniqueId, additionalInfoParameterInfo, null, userId);
+
+ if (either.isRight()) {
+ ResponseFormat responseFormat = either.right().value();
+ log.info("Failed to create additional information {}. REason - {}", additionalInfoParameterInfo, responseFormat);
+ return buildErrorResponse(responseFormat);
+ }
+
+ AdditionalInfoParameterInfo createdAI = either.left().value();
+
+ log.debug("Additional information {}={} created successfully with id {}", createdAI.getKey(), createdAI.getValue(), createdAI.getUniqueId());
+
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
+ return buildOkResponse(responseFormat, createdAI);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create Additional Information");
+ log.debug("Create additional information failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+
+ }
+
+ /**
+ * Update additional information property by id under given resource/service
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @param labelId
+ * @param request
+ * @param userId
+ * @param data
+ * @return
+ */
+ protected Response updateAdditionalInformationLabelForComponent(NodeTypeEnum nodeType, String uniqueId, String labelId, HttpServletRequest request, String userId, String data) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+ log.debug("data is {}", data);
+
+ try {
+ // convert json to AdditionalInfoParameterInfo
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = gson.fromJson(data, AdditionalInfoParameterInfo.class);
+
+ // create the new property
+ AdditionalInformationBusinessLogic businessLogic = getBL(context);
+
+ additionalInfoParameterInfo.setUniqueId(labelId);
+
+ Either<AdditionalInfoParameterInfo, ResponseFormat> either = businessLogic.updateAdditionalInformation(nodeType, uniqueId, additionalInfoParameterInfo, null, userId);
+
+ if (either.isRight()) {
+ ResponseFormat responseFormat = either.right().value();
+ log.info("Failed to update additional information property. Reason - {}", responseFormat);
+ return buildErrorResponse(responseFormat);
+ }
+
+ AdditionalInfoParameterInfo createdAI = either.left().value();
+
+ log.debug("Additional information {}={} updated successfully with id {}", createdAI.getKey(), createdAI.getValue(), createdAI.getUniqueId());
+
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ return buildOkResponse(responseFormat, createdAI);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Additional Information");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Additional Information");
+ log.debug("Update additional information failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+
+ }
+
+ /**
+ *
+ * Delete an additional information property by id under given resource/service
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @param labelId
+ * @param request
+ * @param userId
+ * @return
+ */
+ protected Response deleteAdditionalInformationLabelForComponent(NodeTypeEnum nodeType, String uniqueId, String labelId, HttpServletRequest request, String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ try {
+
+ AdditionalInformationBusinessLogic businessLogic = getBL(context);
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo();
+ additionalInfoParameterInfo.setUniqueId(labelId);
+
+ Either<AdditionalInfoParameterInfo, ResponseFormat> either = businessLogic.deleteAdditionalInformation(nodeType, uniqueId, additionalInfoParameterInfo, null, userId);
+
+ if (either.isRight()) {
+ ResponseFormat responseFormat = either.right().value();
+ log.info("Failed to update additional information property. Reason - {}", responseFormat);
+ return buildErrorResponse(responseFormat);
+ }
+
+ AdditionalInfoParameterInfo createdAI = either.left().value();
+
+ log.debug("Additional information {}={} deleted successfully with id {}", createdAI.getKey(), createdAI.getValue(), createdAI.getUniqueId());
+
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ return buildOkResponse(responseFormat, createdAI);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Additional Information");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Additional Information");
+ log.debug("Delete additional information failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+
+ }
+
+ /**
+ * Get a specific additional information property by a given id under given resource/service
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @param labelId
+ * @param request
+ * @param userId
+ * @return
+ */
+ protected Response getAdditionalInformationLabelForComponent(NodeTypeEnum nodeType, String uniqueId, String labelId, HttpServletRequest request, String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ try {
+
+ // create the new property
+ AdditionalInformationBusinessLogic businessLogic = getBL(context);
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo();
+ additionalInfoParameterInfo.setUniqueId(labelId);
+
+ Either<AdditionalInfoParameterInfo, ResponseFormat> either = businessLogic.getAdditionalInformation(nodeType, uniqueId, additionalInfoParameterInfo, null, userId);
+
+ if (either.isRight()) {
+ ResponseFormat responseFormat = either.right().value();
+ log.info("Failed to update additional information property. Reason - {}", responseFormat);
+ return buildErrorResponse(responseFormat);
+ }
+
+ AdditionalInfoParameterInfo createdAI = either.left().value();
+
+ log.debug("Additional information {}={} fetched successfully with id {}", createdAI.getKey(), createdAI.getValue(), createdAI.getUniqueId());
+
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ return buildOkResponse(responseFormat, createdAI);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Additional Information");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Additional Information");
+
+ log.debug("get additional information failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+
+ }
+
+ /**
+ * Get all additional information properties under given resource/service
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @param request
+ * @param userId
+ * @return
+ */
+ protected Response getAllAdditionalInformationLabelForComponent(NodeTypeEnum nodeType, String uniqueId, HttpServletRequest request, String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ try {
+
+ AdditionalInformationBusinessLogic businessLogic = getBL(context);
+
+ Either<AdditionalInformationDefinition, ResponseFormat> either = businessLogic.getAllAdditionalInformation(nodeType, uniqueId, null, userId);
+ if (either.isRight()) {
+ ResponseFormat responseFormat = either.right().value();
+ log.info("Failed to update additional information property. Reason - {}", responseFormat);
+ return buildErrorResponse(responseFormat);
+ }
+
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+
+ log.debug("All Additional information retrieved for component {} is {}", uniqueId, additionalInformationDefinition);
+
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ return buildOkResponse(responseFormat, additionalInformationDefinition);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get All Additional Information");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get All Additional Information");
+ log.debug("Get all addiotanl information properties failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+
+ }
+
+ private AdditionalInformationBusinessLogic getBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ AdditionalInformationBusinessLogic bl = webApplicationContext.getBean(AdditionalInformationBusinessLogic.class);
+ return bl;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ArtifactServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ArtifactServlet.java
new file mode 100644
index 0000000000..b7af00a7d2
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ArtifactServlet.java
@@ -0,0 +1,564 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic.ArtifactOperation;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactUiDownloadData;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Resource Artifact Servlet", description = "Resource Artifact Servlet")
+@Singleton
+public class ArtifactServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ArtifactServlet.class.getName());
+
+ private Gson gson = new Gson();
+
+ // *************** Resources
+ @POST
+ @Path("/resources/{resourceId}/artifacts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Artifact", httpMethod = "POST", notes = "Returns created ArtifactDefinition", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Artifact already exist") })
+ public Response loadArtifact(@PathParam("resourceId") final String resourceId, @ApiParam(value = "json describe the artifact", required = true) String data, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleUploadRequest(data, request, resourceId, ComponentTypeEnum.RESOURCE);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "loadArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("loadArtifact");
+ log.debug("loadArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/resources/{resourceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Artifact", httpMethod = "POST", notes = "Returns updated artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateArtifact(@PathParam("resourceId") final String resourceId, @PathParam("artifactId") final String artifactId, @ApiParam(value = "json describe the artifact", required = true) String data,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleUpdateRequest(data, request, resourceId, artifactId, ComponentTypeEnum.RESOURCE);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "updateArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("updateArtifact");
+ log.debug("updateArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @DELETE
+ @Path("/resources/{resourceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete Artifact", httpMethod = "DELETE", notes = "Returns delete artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response deleteArtifact(@PathParam("resourceId") final String resourceId, @PathParam("artifactId") final String artifactId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDeleteRequest(request, resourceId, artifactId, ComponentTypeEnum.RESOURCE, null, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "deleteArtifact");
+ log.debug("deleteArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ // *************** Services
+ @POST
+ @Path("/services/{serviceId}/artifacts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Artifact", httpMethod = "POST", notes = "Returns created ArtifactDefinition", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Artifact already exist") })
+ public Response loadInformationArtifact(@PathParam("serviceId") final String serviceId, @ApiParam(value = "json describe the artifact", required = true) String data, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleUploadRequest(data, request, serviceId, ComponentTypeEnum.SERVICE);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "loadInformationArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("loadInformationArtifact");
+ log.debug("loadInformationArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/services/{serviceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Artifact", httpMethod = "POST", notes = "Returns updated artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Service artifact created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateInformationArtifact(@PathParam("serviceId") final String serviceId, @PathParam("artifactId") final String artifactId, @ApiParam(value = "json describe the artifact", required = true) String data,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleUpdateRequest(data, request, serviceId, artifactId, ComponentTypeEnum.SERVICE);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "updateInformationArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("updateInformationArtifact");
+ log.debug("updateInformationArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ // *************** Services api artifacts
+ @POST
+ @Path("/services/{serviceId}/artifacts/api/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Api Artifact", httpMethod = "POST", notes = "Returns created ArtifactDefinition", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Api Artifact Updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateApiArtifact(@PathParam("serviceId") final String serviceId, @PathParam("artifactId") final String artifactId, @ApiParam(value = "json describe the artifact", required = true) String data,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam(value = Constants.MD5_HEADER) String origMd5) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleUpdateRequest(data, request, serviceId, artifactId, ComponentTypeEnum.SERVICE);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "updateApiArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("updateApiArtifact");
+ log.debug("updateApiArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @DELETE
+ @Path("/services/{serviceId}/artifacts/api/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete Api Artifact", httpMethod = "DELETE", notes = "Returns Deleted ArtifactDefinition", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "Api Artifact deleted"), @ApiResponse(code = 403, message = "Restricted operation") })
+ public Response deleteApiArtifact(@PathParam("serviceId") final String serviceId, @PathParam("artifactId") final String artifactId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+ @HeaderParam(value = Constants.MD5_HEADER) String origMd5) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDeleteRequest(request, serviceId, artifactId, ComponentTypeEnum.SERVICE, null, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "deleteApiArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("deleteApiArtifact");
+ log.debug("deleteApiArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @DELETE
+ @Path("/services/{serviceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete Artifact", httpMethod = "DELETE", notes = "Returns delete artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Service artifact deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response deleteInformationalArtifact(@PathParam("serviceId") final String serviceId, @PathParam("artifactId") final String artifactId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDeleteRequest(request, serviceId, artifactId, ComponentTypeEnum.SERVICE, null, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "deleteInformationalArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("deleteInformationalArtifact");
+ log.debug("deleteInformationalArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /*
+ * DOWNLOAD Artifacts by json body in base 64 (because of userId problem with href)
+ */
+
+ @GET
+ @Path("/services/{serviceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Download service Artifact in Base64", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service artifact downloaded"), @ApiResponse(code = 404, message = "Service/Artifact not found") })
+ public Response downloadServiceArtifactBase64(@PathParam("serviceId") final String serviceId, @PathParam("artifactId") final String artifactId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDownloadRequest(request, serviceId, artifactId, null, ComponentTypeEnum.SERVICE, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "downloadServiceArtifactBase64");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("downloadServiceArtifactBase64");
+ log.debug("downloadServiceArtifactBase64 unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/resources/{resourceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Download resource Artifact in Base64", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource artifact downloaded"), @ApiResponse(code = 404, message = "Resource/Artifact not found") })
+ public Response downloadResourceArtifactBase64(@PathParam("resourceId") final String resourceId, @PathParam("artifactId") final String artifactId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDownloadRequest(request, resourceId, artifactId, null, ComponentTypeEnum.RESOURCE, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "downloadResourceArtifactBase64");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("downloadResourceArtifactBase64");
+ log.debug("downloadResourceArtifactBase64 unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/{containerComponentType}/{componentId}/resourceInstances/{componentInstanceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Download component Artifact in Base64", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "ResourceInstance artifact downloaded"), @ApiResponse(code = 404, message = "ResourceInstance/Artifact not found") })
+ public Response downloadResourceInstanceArtifactBase64(
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId, @PathParam("artifactId") final String artifactId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDownloadRequest(request, componentInstanceId, artifactId, componentId, ComponentTypeEnum.RESOURCE_INSTANCE, containerComponentType);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "downloadResourceInstanceArtifactBase64");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("downloadResourceInstanceArtifactBase64");
+ log.debug("downloadResourceInstanceArtifactBase64 unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ // *************** Resource lifecycle ( interfces )
+
+ @POST
+ @Path("/resources/{resourceId}/{interfaceType}/{operation}/artifacts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Artifact and Attach to interface", httpMethod = "POST", notes = "Returns created resource", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Artifact already exist") })
+ public Response loadArtifactToInterface(@PathParam("resourceId") final String resourceId, @PathParam("interfaceType") final String interfaceType, @PathParam("operation") final String operation,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam(value = Constants.MD5_HEADER) String origMd5, @ApiParam(value = "json describe the artifact", required = true) String data,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleArtifactRequest(data, request, resourceId, interfaceType, operation, null, ComponentTypeEnum.RESOURCE, ArtifactOperation.Create, null, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "loadArtifactToInterface");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("loadArtifactToInterface");
+ log.debug("loadArtifactToInterface unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ @DELETE
+ @Path("/resources/{resourceId}/{interfaceType}/{operation}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "delete Artifact from interface", httpMethod = "delete", notes = "delete matching artifact from interface", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "delete artifact under interface deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Artifact already exist") })
+ public Response deleteArtifactToInterface(@PathParam("resourceId") final String resourceId, @PathParam("interfaceType") final String interfaceType, @PathParam("operation") final String operation, @PathParam("artifactId") final String artifactId,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleDeleteRequest(request, resourceId, artifactId, ComponentTypeEnum.RESOURCE, interfaceType, operation);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "deleteArtifactToInterface");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("deleteArtifactToInterface");
+ log.debug("deleteArtifactToInterface unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/resources/{resourceId}/{interfaceType}/{operation}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "update Artifact Attach to interface", httpMethod = "post", notes = "updates artifact by interface", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "delete artifact under interface deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Artifact already exist") })
+ public Response updateArtifactToInterface(@PathParam("resourceId") final String resourceId, @PathParam("interfaceType") final String interfaceType, @PathParam("operation") final String operation, @PathParam("artifactId") final String artifactId,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam(value = Constants.MD5_HEADER) String origMd5, @Context final HttpServletRequest request,
+ @ApiParam(value = "json describe the artifact", required = true) String data) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleArtifactRequest(data, request, resourceId, interfaceType, operation, artifactId, ComponentTypeEnum.RESOURCE, ArtifactOperation.Update, null, null);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "updateArtifactToInterface");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("updateArtifactToInterface");
+ log.debug("updateArtifactToInterface unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/artifacts/{artifactId}/heatParams")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource Instance HEAT_ENV parameters", httpMethod = "POST", notes = "Returns updated artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateRIArtifact(
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId, @PathParam("artifactId") final String artifactId,
+ @ApiParam(value = "json describe the artifact", required = true) String data, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleArtifactRequest(data, request, componentInstanceId, null, null, artifactId, ComponentTypeEnum.RESOURCE_INSTANCE, ArtifactOperation.Update, componentId, containerComponentType);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "updateRIArtifact");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("updateRIArtifact");
+ log.debug("updateRIArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource Instance artifact payload", httpMethod = "POST", notes = "Returns updated artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateComponentInstanceArtifact(@HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam(value = Constants.MD5_HEADER) String origMd5,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId, @PathParam("artifactId") final String artifactId,
+ @ApiParam(value = "json describe the artifact", required = true) String data, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleArtifactRequest(data, request, componentInstanceId, null, null, artifactId, ComponentTypeEnum.RESOURCE_INSTANCE, ArtifactOperation.Update, componentId, containerComponentType);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "loadResourceInstanceHeatEnvArtifact");
+ log.debug("loadResourceInstanceHeatEnvArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/artifacts")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Load Resource Instance artifact payload", httpMethod = "POST", notes = "Returns updated artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response loadComponentInstanceArtifact(@HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam(value = Constants.MD5_HEADER) String origMd5,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId, @ApiParam(value = "json describe the artifact", required = true) String data,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ return handleArtifactRequest(data, request, componentInstanceId, null, null, null, ComponentTypeEnum.RESOURCE_INSTANCE, ArtifactOperation.Create, componentId, containerComponentType);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "loadResourceInstanceHeatEnvArtifact");
+ log.debug("loadResourceInstanceHeatEnvArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @DELETE
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/artifacts/{artifactId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete Resource Instance artifact", httpMethod = "POST", notes = "Returns deleted artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response deleteComponentInstanceArtifact(@HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam(value = Constants.MD5_HEADER) String origMd5,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId, @PathParam("artifactId") final String artifactId,
+ @ApiParam(value = "json describe the artifact", required = true) String data, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(containerComponentType);
+ return handleDeleteRequest(request, componentInstanceId, artifactId, ComponentTypeEnum.RESOURCE_INSTANCE, null, null, componentId);
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "deleteArtifact");
+ log.debug("deleteArtifact unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ // ////////// API END ///////////////////////////
+
+ // ************ private *********************
+
+ private Response handleUploadRequest(String data, HttpServletRequest request, String componentId, ComponentTypeEnum componentType) {
+ return handleArtifactRequest(data, request, componentId, null, componentType, ArtifactOperation.Create);
+ }
+
+ private Response handleUpdateRequest(String data, HttpServletRequest request, String componentId, String artifactId, ComponentTypeEnum componentType) {
+ return handleArtifactRequest(data, request, componentId, artifactId, componentType, ArtifactOperation.Update);
+ }
+
+ private Response handleDownloadRequest(HttpServletRequest request, String componentId, String artifactId, String parentId, ComponentTypeEnum componentType, String containerComponentType) {
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<ImmutablePair<String, byte[]>, ResponseFormat> actionResult = artifactsLogic.handleDownloadRequestById(componentId, artifactId, userId, componentType, parentId, containerComponentType);
+
+ Response response;
+ if (actionResult.isRight()) {
+ response = buildErrorResponse(actionResult.right().value());
+ } else {
+ byte[] file = actionResult.left().value().getRight();
+ String base64Contents = new String(Base64.encodeBase64(file));
+ String artifactName = actionResult.left().value().getLeft();
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ ArtifactUiDownloadData artifactUiDownloadData = new ArtifactUiDownloadData();
+ artifactUiDownloadData.setArtifactName(artifactName);
+ artifactUiDownloadData.setBase64Contents(base64Contents);
+ response = buildOkResponse(responseFormat, artifactUiDownloadData);
+ }
+ return response;
+ }
+
+ private Response handleDeleteRequest(HttpServletRequest request, String componentId, String artifactId, ComponentTypeEnum componentType, String interfaceType, String operationName) {
+ return handleDeleteRequest(request, componentId, artifactId, componentType, interfaceType, operationName, null);
+ }
+
+ private Response handleDeleteRequest(HttpServletRequest request, String componentId, String artifactId, ComponentTypeEnum componentType, String interfaceType, String operationName, String parentId) {
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = artifactsLogic.handleArtifactRequest(componentId, userId, componentType, ArtifactOperation.Delete, artifactId, null, null, null, interfaceType, operationName,
+ parentId, null);
+ Response response;
+ if (actionResult.isRight()) {
+ response = buildErrorResponse(actionResult.right().value());
+ } else {
+ Either<ArtifactDefinition, Operation> result = actionResult.left().value();
+ if (result.isLeft()) {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result.left().value());
+ } else {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result.right().value());
+ }
+ }
+ return response;
+
+ }
+
+ private Response handleArtifactRequest(String data, HttpServletRequest request, String componentId, String interfaceName, String operationName, String artifactId, ComponentTypeEnum componentType, ArtifactOperation operation, String parentId,
+ String containerComponentType) {
+ ArtifactDefinition artifactInfo = RepresentationUtils.convertJsonToArtifactDefinition(data, ArtifactDefinition.class);
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ ServletContext context = request.getSession().getServletContext();
+ ArtifactsBusinessLogic artifactsLogic = getArtifactBL(context);
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> actionResult = artifactsLogic.handleArtifactRequest(componentId, userId, componentType, operation, artifactId, artifactInfo, origMd5, data, interfaceName, operationName, parentId,
+ containerComponentType);
+ Response response;
+ if (actionResult.isRight()) {
+ response = buildErrorResponse(actionResult.right().value());
+ } else {
+ Either<ArtifactDefinition, Operation> result = actionResult.left().value();
+ if (result.isLeft()) {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result.left().value());
+ } else {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result.right().value());
+ }
+ }
+ return response;
+
+ }
+
+ private Response handleArtifactRequest(String data, HttpServletRequest request, String componentId, String artifactId, ComponentTypeEnum componentType, ArtifactOperation operation) {
+ return handleArtifactRequest(data, servletRequest, componentId, null, null, artifactId, componentType, operation, null, null);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AttributeServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AttributeServlet.java
new file mode 100644
index 0000000000..b5e64ae00e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/AttributeServlet.java
@@ -0,0 +1,278 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.AttributeBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+/**
+ * Web Servlet for actions on Attributes
+ *
+ * @author mshitrit
+ *
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Resource Attribute Servlet", description = "Resource Attribute Servlet")
+@Singleton
+public class AttributeServlet extends AbstractValidationsServlet {
+ private static Logger log = LoggerFactory.getLogger(AttributeServlet.class.getName());
+
+ /**
+ * Creates new Attribute on a resource with given resource ID
+ *
+ * @param resourceId
+ * @param data
+ * @param request
+ * @param userId
+ * @return
+ */
+ @POST
+ @Path("resources/{resourceId}/attributes")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource Attribute", httpMethod = "POST", notes = "Returns created resource attribute", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource property created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Resource attribute already exist") })
+ public Response createAttribute(@ApiParam(value = "resource id to update with new attribute", required = true) @PathParam("resourceId") final String resourceId, @ApiParam(value = "Resource attribute to be created", required = true) String data,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+ log.debug("data is {}", data);
+
+ try {
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Wrapper<AttributeDefinition> attributesWrapper = new Wrapper<>();
+ // convert json to AttributeDefinition
+
+ buildAttributeFromString(data, attributesWrapper, errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ AttributeBusinessLogic businessLogic = getBusinessLogic(context, () -> AttributeBusinessLogic.class);
+ Either<AttributeDefinition, ResponseFormat> createAttribute = businessLogic.createAttribute(resourceId, attributesWrapper.getInnerElement(), userId);
+ if (createAttribute.isRight()) {
+ errorWrapper.setInnerElement(createAttribute.right().value());
+ } else {
+ attributesWrapper.setInnerElement(createAttribute.left().value());
+ }
+ }
+
+ Response response;
+ if (!errorWrapper.isEmpty()) {
+ log.info("Failed to create Attribute. Reason - ", errorWrapper.getInnerElement());
+ response = buildErrorResponse(errorWrapper.getInnerElement());
+ } else {
+ AttributeDefinition createdAttDef = attributesWrapper.getInnerElement();
+ log.debug("Attribute {} created successfully with id {}", createdAttDef.getName(), createdAttDef.getUniqueId());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
+ response = buildOkResponse(responseFormat, RepresentationUtils.toRepresentation(createdAttDef));
+ }
+
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Attribute");
+ log.debug("create property failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ /**
+ * Updates existing Attribute with given attributeID on a resource with given resourceID
+ *
+ * @param resourceId
+ * @param attributeId
+ * @param data
+ * @param request
+ * @param userId
+ * @return
+ */
+ @PUT
+ @Path("resources/{resourceId}/attributes/{attributeId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource Attribute", httpMethod = "PUT", notes = "Returns updated attribute", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource attribute updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateAttribute(@ApiParam(value = "resource id to update with new attribute", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "attribute id to update", required = true) @PathParam("attributeId") final String attributeId, @ApiParam(value = "Resource attribute to update", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ try {
+ // convert json to PropertyDefinition
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Wrapper<AttributeDefinition> attributesWrapper = new Wrapper<>();
+ // convert json to AttributeDefinition
+
+ buildAttributeFromString(data, attributesWrapper, errorWrapper);
+
+ if (errorWrapper.isEmpty()) {
+ AttributeBusinessLogic businessLogic = getBusinessLogic(context, () -> AttributeBusinessLogic.class);
+ Either<AttributeDefinition, ResponseFormat> eitherUpdateAttribute = businessLogic.updateAttribute(resourceId, attributeId, attributesWrapper.getInnerElement(), userId);
+ // update property
+ if (eitherUpdateAttribute.isRight()) {
+ errorWrapper.setInnerElement(eitherUpdateAttribute.right().value());
+ } else {
+ attributesWrapper.setInnerElement(eitherUpdateAttribute.left().value());
+ }
+ }
+
+ Response response;
+ if (!errorWrapper.isEmpty()) {
+ log.info("Failed to update Attribute. Reason - ", errorWrapper.getInnerElement());
+ response = buildErrorResponse(errorWrapper.getInnerElement());
+ } else {
+ AttributeDefinition updatedAttribute = attributesWrapper.getInnerElement();
+ log.debug("Attribute id {} updated successfully ", updatedAttribute.getUniqueId());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ response = buildOkResponse(responseFormat, RepresentationUtils.toRepresentation(updatedAttribute));
+ }
+
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Attribute");
+ log.debug("update attribute failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ /**
+ * Deletes existing Attribute with given attributeID on a resource with given resourceID
+ *
+ * @param resourceId
+ * @param attributeId
+ * @param request
+ * @param userId
+ * @return
+ */
+ @DELETE
+ @Path("resources/{resourceId}/attributes/{attributeId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource Attribute", httpMethod = "DELETE", notes = "Returns deleted attribute", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "deleted attribute"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 404, message = "Resource property not found") })
+ public Response deleteAttribute(@ApiParam(value = "resource id of attribute", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "Attribute id to delete", required = true) @PathParam("attributeId") final String attributeId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ try {
+
+ // delete the property
+ AttributeBusinessLogic businessLogic = getBusinessLogic(context, () -> AttributeBusinessLogic.class);
+ Either<AttributeDefinition, ResponseFormat> eitherAttribute = businessLogic.deleteAttribute(resourceId, attributeId, userId);
+ if (eitherAttribute.isRight()) {
+ log.debug("Failed to delete Attribute. Reason - ", eitherAttribute.right().value());
+ return buildErrorResponse(eitherAttribute.right().value());
+ }
+ AttributeDefinition attributeDefinition = eitherAttribute.left().value();
+ String name = attributeDefinition.getName();
+
+ log.debug("Attribute {} deleted successfully with id {}", name, attributeDefinition.getUniqueId());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT);
+ return buildOkResponse(responseFormat, RepresentationUtils.toRepresentation(attributeDefinition));
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Attribute");
+ log.debug("delete attribute failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ private void buildAttributeFromString(String data, Wrapper<AttributeDefinition> attributesWrapper, Wrapper<ResponseFormat> errorWrapper) {
+
+ try {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ final AttributeDefinition attribute = gson.fromJson(data, AttributeDefinition.class);
+ if (attribute == null) {
+ log.info("Attribute content is invalid - {}", data);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ errorWrapper.setInnerElement(responseFormat);
+ } else {
+ attributesWrapper.setInnerElement(attribute);
+ }
+
+ } catch (Exception e) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ errorWrapper.setInnerElement(responseFormat);
+ log.debug("Attribute content is invalid - {}", data, e);
+ log.info("Attribute content is invalid - {}", data);
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeGenericServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeGenericServlet.java
new file mode 100644
index 0000000000..f21b57f6fb
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeGenericServlet.java
@@ -0,0 +1,226 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Supplier;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import org.openecomp.sdc.be.components.clean.ComponentsCleanBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
+import org.openecomp.sdc.be.components.impl.GroupBusinessLogic;
+import org.openecomp.sdc.be.components.impl.MonitoringBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ProductComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.VFComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.IElementDAO;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.servlets.BasicServlet;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+public class BeGenericServlet extends BasicServlet {
+
+ @Context
+ protected HttpServletRequest servletRequest;
+
+ private static Logger log = LoggerFactory.getLogger(BeGenericServlet.class.getName());
+
+ /******************** New error response mechanism **************/
+
+ protected Response buildErrorResponse(ResponseFormat requestErrorWrapper) {
+ Response response = Response.status(requestErrorWrapper.getStatus()).entity(gson.toJson(requestErrorWrapper.getRequestError())).build();
+ return response;
+ }
+
+ protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity) {
+ return buildOkResponse(errorResponseWrapper, entity, null);
+ }
+
+ protected Response buildOkResponse(ResponseFormat errorResponseWrapper, Object entity, Map<String, String> additionalHeaders) {
+ int status = errorResponseWrapper.getStatus();
+ ResponseBuilder responseBuilder = Response.status(status);
+ if (entity != null) {
+ if (log.isTraceEnabled())
+ log.trace("returned entity is {}", entity.toString());
+ responseBuilder = responseBuilder.entity(entity);
+ }
+ if (additionalHeaders != null) {
+ for (Entry<String, String> additionalHeader : additionalHeaders.entrySet()) {
+ String headerName = additionalHeader.getKey();
+ String headerValue = additionalHeader.getValue();
+ if (log.isTraceEnabled())
+ log.trace("Adding header {} with value {} to the response", headerName, headerValue);
+ responseBuilder.header(headerName, headerValue);
+ }
+ }
+ return responseBuilder.build();
+ }
+
+ /*******************************************************************************************************/
+
+ protected UserBusinessLogic getUserAdminManager(ServletContext context) {
+ return getBusinessLogic(context, () -> UserBusinessLogic.class);
+ }
+
+ protected ResourceBusinessLogic getResourceBL(ServletContext context) {
+ return getBusinessLogic(context, () -> ResourceBusinessLogic.class);
+ }
+
+ protected ComponentsCleanBusinessLogic getComponentCleanerBL(ServletContext context) {
+ return getBusinessLogic(context, () -> ComponentsCleanBusinessLogic.class);
+ }
+
+ protected ServiceBusinessLogic getServiceBL(ServletContext context) {
+ return getBusinessLogic(context, () -> ServiceBusinessLogic.class);
+ }
+
+ protected ProductBusinessLogic getProductBL(ServletContext context) {
+ return getBusinessLogic(context, () -> ProductBusinessLogic.class);
+ }
+
+ protected ArtifactsBusinessLogic getArtifactBL(ServletContext context) {
+ return getBusinessLogic(context, () -> ArtifactsBusinessLogic.class);
+ }
+
+ protected ElementBusinessLogic getElementBL(ServletContext context) {
+ return getBusinessLogic(context, () -> ElementBusinessLogic.class);
+ }
+
+ protected MonitoringBusinessLogic getMonitoringBL(ServletContext context) {
+ return getBusinessLogic(context, () -> MonitoringBusinessLogic.class);
+ }
+
+ protected <SomeBusinessLogic> SomeBusinessLogic getBusinessLogic(ServletContext context, Supplier<Class<SomeBusinessLogic>> businessLogicClassGen) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ SomeBusinessLogic monitoringBusinessLogic = webApplicationContext.getBean(businessLogicClassGen.get());
+ return monitoringBusinessLogic;
+ }
+
+ protected GroupBusinessLogic getGroupBL(ServletContext context) {
+
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ GroupBusinessLogic groupBusinessLogic = webApplicationContext.getBean(GroupBusinessLogic.class);
+ return groupBusinessLogic;
+ }
+
+ protected ComponentInstanceBusinessLogic getComponentInstanceBL(ServletContext context, ComponentTypeEnum containerComponentType) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ if (containerComponentType == ComponentTypeEnum.RESOURCE) {
+ return webApplicationContext.getBean(VFComponentInstanceBusinessLogic.class);
+ }
+ if (containerComponentType == ComponentTypeEnum.SERVICE) {
+ return webApplicationContext.getBean(ServiceComponentInstanceBusinessLogic.class);
+ }
+ if (containerComponentType == ComponentTypeEnum.PRODUCT) {
+ return webApplicationContext.getBean(ProductComponentInstanceBusinessLogic.class);
+ }
+ return null;
+ }
+
+ protected IElementDAO getElementDao(Class<? extends IElementDAO> clazz, ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+
+ return webApplicationContext.getBean(clazz);
+ }
+
+ protected ComponentsUtils getComponentsUtils() {
+ ServletContext context = this.servletRequest.getSession().getServletContext();
+
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ ComponentsUtils componentsUtils = webApplicationContext.getBean(ComponentsUtils.class);
+ return componentsUtils;
+ }
+
+ /**
+ * Used to support Unit Test.<br>
+ * Header Params are not supported in Unit Tests
+ *
+ * @return
+ */
+ protected String initHeaderParam(String headerValue, HttpServletRequest request, String headerName) {
+ String retValue;
+ if (headerValue != null) {
+ retValue = headerValue;
+ } else {
+ retValue = request.getHeader(headerName);
+ }
+ return retValue;
+ }
+
+ protected String getContentDispositionValue(String artifactFileName) {
+ return new StringBuilder().append("attachment; filename=\"").append(artifactFileName).append("\"").toString();
+ }
+
+ protected ComponentBusinessLogic getComponentBL(ComponentTypeEnum componentTypeEnum, ServletContext context) {
+ ComponentBusinessLogic businessLogic;
+ switch (componentTypeEnum) {
+ case RESOURCE: {
+ businessLogic = getResourceBL(context);
+ break;
+ }
+ case SERVICE: {
+ businessLogic = getServiceBL(context);
+ break;
+ }
+ case PRODUCT: {
+ businessLogic = getProductBL(context);
+ break;
+ }
+ case RESOURCE_INSTANCE: {
+ businessLogic = getResourceBL(context);
+ break;
+ }
+ default: {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "getComponentBL");
+ BeEcompErrorManager.getInstance().logBeSystemError("getComponentBL");
+ throw new IllegalArgumentException("Illegal component type:" + componentTypeEnum.getValue());
+ }
+ }
+ return businessLogic;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeMonitoringServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeMonitoringServlet.java
new file mode 100644
index 0000000000..ffcd9c68fc
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/BeMonitoringServlet.java
@@ -0,0 +1,203 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.List;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.DistributionMonitoringBusinessLogic;
+import org.openecomp.sdc.be.components.impl.HealthCheckBusinessLogic;
+import org.openecomp.sdc.be.components.impl.MonitoringBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.HealthCheckInfo;
+import org.openecomp.sdc.common.api.HealthCheckWrapper;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckComponent;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.monitoring.MonitoringEvent;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.TRACE, trim = false)
+@Path("/")
+@Api(value = "BE Monitoring", description = "BE Monitoring")
+@Singleton
+public class BeMonitoringServlet extends BeGenericServlet {
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ private static Logger log = LoggerFactory.getLogger(ConfigServlet.class.getName());
+
+ @GET
+ @Path("/healthCheck")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "return aggregate BE health check of Titan, ES and BE", notes = "return BE health check", response = String.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Titan, ES and BE are all up"), @ApiResponse(code = 500, message = "One or more BE components (Titan, ES, BE) are down") })
+ public Response getHealthCheck(@Context final HttpServletRequest request) {
+ try {
+ HealthCheckBusinessLogic healthCheckBusinessLogic = getHealthCheckBL(request.getSession().getServletContext());
+ List<HealthCheckInfo> beHealthCheckInfos = healthCheckBusinessLogic.getBeHealthCheckInfosStatus();
+
+ // List<HealthCheckInfo> beHealthCheckInfos =
+ // HealthCheckBusinessLogic.getInstance().getBeHealthCheckInfos(request.getSession().getServletContext());
+ ActionStatus status = getAggregateBeStatus(beHealthCheckInfos);
+ String sdcVersion = getVersionFromContext(request);
+ if (sdcVersion == null || sdcVersion.isEmpty()) {
+ sdcVersion = "UNKNOWN";
+ }
+ String siteMode = healthCheckBusinessLogic.getSiteMode();
+ HealthCheckWrapper healthCheck = new HealthCheckWrapper(beHealthCheckInfos, sdcVersion, siteMode);
+ // The response can be either with 200 or 500 aggregate status - the
+ // body of individual statuses is returned either way
+
+ String healthCheckStr = prettyGson.toJson(healthCheck);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(status), healthCheckStr);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckError, "BeHealthCheck");
+ BeEcompErrorManager.getInstance().logBeHealthCheckError("BeHealthCheck");
+ log.debug("BE health check unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/monitoring")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response processMonitoringMetrics(@Context final HttpServletRequest request, String json) {
+ try {
+ MonitoringEvent monitoringEvent = convertContentToJson(json, MonitoringEvent.class);
+ if (monitoringEvent == null) {
+ return buildErrorResponse(getComponentsUtils().getResponseFormatAdditionalProperty(ActionStatus.GENERAL_ERROR));
+ }
+ log.trace("Received monitoring metrics: {}", monitoringEvent.toString());
+ ServletContext context = request.getSession().getServletContext();
+ MonitoringBusinessLogic bl = getMonitoringBL(context);
+ Either<Boolean, ResponseFormat> result = bl.logMonitoringEvent(monitoringEvent);
+ if (result.isRight()) {
+ return buildErrorResponse(result.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), null);
+
+ } catch (Exception e) {
+ log.debug("BE system metrics unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormatAdditionalProperty(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/version")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "return the ASDC application version", notes = "return the SDC application version", response = String.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "return SDC version"), @ApiResponse(code = 500, message = "Internal Error") })
+ public Response getSdcVersion(@Context final HttpServletRequest request) {
+ try {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ String version = getVersionFromContext(request);
+ log.debug("sdc version from manifest is: {}", version);
+ if (version == null || version.isEmpty()) {
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.SDC_VERSION_NOT_FOUND));
+ }
+
+ HealthCheckInfo versionInfo = new HealthCheckInfo();
+ versionInfo.setVersion(version);
+
+ // The response can be either with 200 or 500 aggregate status - the
+ // body of individual statuses is returned either way
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), versionInfo);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "getSDCVersion");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("getSDCVersion");
+ log.debug("BE get SDC version unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ private String getVersionFromContext(HttpServletRequest request) {
+ ServletContext servletContext = request.getSession().getServletContext();
+ String version = (String) servletContext.getAttribute(Constants.SDC_RELEASE_VERSION_ATTR);
+ return version;
+ }
+
+ private ActionStatus getAggregateBeStatus(List<HealthCheckInfo> beHealthCheckInfos) {
+ ActionStatus status = ActionStatus.OK;
+ for (HealthCheckInfo healthCheckInfo : beHealthCheckInfos) {
+ if (healthCheckInfo.getHealthCheckStatus().equals(HealthCheckStatus.DOWN) && healthCheckInfo.getHealthCheckComponent() != HealthCheckComponent.DE) {
+ status = ActionStatus.GENERAL_ERROR;
+ break;
+ }
+ }
+ return status;
+ }
+
+ protected MonitoringEvent convertContentToJson(String content, Class<MonitoringEvent> clazz) {
+
+ MonitoringEvent object = null;
+ try {
+ object = gson.fromJson(content, clazz);
+ object.setFields(null);
+ } catch (Exception e) {
+ log.debug("Failed to convert the content {} to object. {}", content.substring(0, Math.min(50, content.length())), e);
+ }
+
+ return object;
+ }
+
+ private HealthCheckBusinessLogic getHealthCheckBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ HealthCheckBusinessLogic healthCheckBl = webApplicationContext.getBean(HealthCheckBusinessLogic.class);
+ return healthCheckBl;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java
new file mode 100644
index 0000000000..4e6d0bcd7f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java
@@ -0,0 +1,744 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.io.IOUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.info.CreateAndAssotiateInfo;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Resource Instance Servlet", description = "Resource Instance Servlet")
+@Singleton
+public class ComponentInstanceServlet extends AbstractValidationsServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ComponentInstanceServlet.class.getName());
+
+ Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+
+ Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create ComponentInstance", httpMethod = "POST", notes = "Returns created ComponentInstance", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Component created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Component instance already exist") })
+ public Response createComponentInstance(@ApiParam(value = "RI object to be created", required = true) String data, @PathParam("componentId") final String containerComponentId,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @HeaderParam(value = Constants.USER_ID_HEADER) @ApiParam(value = "USER_ID of modifier user", required = true) String userId, @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+
+ try {
+
+ ComponentInstance componentInstance = RepresentationUtils.fromRepresentation(data, ComponentInstance.class);
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+ Either<ComponentInstance, ResponseFormat> actionResponse = componentInstanceLogic.createComponentInstance(containerComponentType, containerComponentId, userId, componentInstance);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create Component Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Component Instance");
+ log.debug("create component instance failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update resource instance", httpMethod = "POST", notes = "Returns updated resource instance", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource instance updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateComponentInstance(@PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId,
+ @ApiParam(value = "valid values: resources / services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+
+ log.debug("Start handle request of {}", url);
+
+ InputStream inputStream = request.getInputStream();
+
+ byte[] bytes = IOUtils.toByteArray(inputStream);
+
+ if (bytes == null || bytes.length == 0) {
+ log.info("Empty body was sent.");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ String data = new String(bytes);
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+ Either<ComponentInstance, ResponseFormat> convertResponse = convertToResourceInstance(data);
+
+ if (convertResponse.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Resource Instance - updateResourceInstance");
+ BeEcompErrorManager.getInstance().logBeSystemError("Resource Instance - updateResourceInstance");
+ log.debug("Failed to convert received data to BE format.");
+ return buildErrorResponse(convertResponse.right().value());
+ }
+
+ ComponentInstance resourceInstance = convertResponse.left().value();
+ Either<ComponentInstance, ResponseFormat> actionResponse = componentInstanceLogic.updateComponentInstance(containerComponentType, componentId, componentInstanceId, userId, resourceInstance);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Resource Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Resource Instance");
+ log.debug("update resource instance with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ // TODO Tal New Multiple Instance API
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/multipleComponentInstance")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update resource instance multiple component", httpMethod = "POST", notes = "Returns updated resource instance", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource instance updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateMultipleComponentInstance(@PathParam("componentId") final String componentId,
+ @ApiParam(value = "valid values: resources / services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @Context final HttpServletRequest request, @ApiParam(value = "Component Instance JSON Array", required = true) final String componentInstanceJsonArray) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ try {
+ log.debug("Start handle request of {}", url);
+
+ if (componentInstanceJsonArray == null || componentInstanceJsonArray.length() == 0) {
+ log.info("Empty JSON list was sent.");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<List<ComponentInstance>, ResponseFormat> convertResponse = convertToMultipleResourceInstance(componentInstanceJsonArray);
+
+ if (convertResponse.isRight()) {
+ // Using both ECOMP error methods, show to Sofer
+ BeEcompErrorManager.getInstance().logBeSystemError("Resource Instance - updateResourceInstance");
+ /*
+ * BeEcompErrorManager.getInstance().processEcompError( EcompErrorName.BeSystemError, "Resource Instance - updateResourceInstance");
+ */
+ log.debug("Failed to convert received data to BE format.");
+ return buildErrorResponse(convertResponse.right().value());
+ }
+
+ List<ComponentInstance> componentInstanceList = convertResponse.left().value();
+
+ Either<List<ComponentInstance>, ResponseFormat> actionResponse = componentInstanceLogic.updateComponentInstance(containerComponentType, componentId, userId, componentInstanceList, true, true);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Exception e) {
+ /*
+ * BeEcompErrorManager.getInstance().processEcompError( EcompErrorName.BeRestApiGeneralError, "Update Resource Instance" );
+ */
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Resource Instance");
+ log.debug("update resource instance with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ @DELETE
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{resourceInstanceId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete ResourceInstance", httpMethod = "DELETE", notes = "Returns delete resourceInstance", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "ResourceInstance deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response deleteResourceInstance(@PathParam("componentId") final String componentId, @PathParam("resourceInstanceId") final String resourceInstanceId,
+ @ApiParam(value = "valid values: resources / services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ Response response = null;
+ try {
+ log.debug("Start handle request of {}", url);
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ Either<ComponentInstance, ResponseFormat> actionResponse = componentInstanceLogic.deleteComponentInstance(containerComponentType, componentId, resourceInstanceId, userId);
+
+ if (actionResponse.isRight()) {
+ response = buildErrorResponse(actionResponse.right().value());
+ } else {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
+ }
+ return response;
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Resource Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Resource Instance");
+ log.debug("delete resource instance with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @ApiParam(value = "allowed values are resources /services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + "," + ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true)
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/associate")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Associate RI to RI", httpMethod = "POST", notes = "Returns created RelationshipInfo", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Relationship created"), @ApiResponse(code = 403, message = "Missing information"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Relationship already exist") })
+ public Response associateRIToRI(@ApiParam(value = "unique id of the container component") @PathParam("componentId") final String componentId,
+ @ApiParam(value = "allowed values are resources /services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true) @PathParam("containerComponentType") final String containerComponentType,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @ApiParam(value = "RelationshipInfo", required = true) String data, @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ Response response = null;
+
+ try {
+
+ log.debug("Start handle request of {}", url);
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<RequirementCapabilityRelDef, ResponseFormat> regInfoW = convertToRequirementCapabilityRelDef(data);
+
+ Either<RequirementCapabilityRelDef, ResponseFormat> resultOp;
+ if (regInfoW.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Resource Instance - associateRIToRI");
+ BeEcompErrorManager.getInstance().logBeSystemError("Resource Instance - associateRIToRI");
+ log.debug("Failed to convert received data to BE format.");
+ resultOp = Either.right(regInfoW.right().value());
+ } else {
+ RequirementCapabilityRelDef requirementDef = regInfoW.left().value();
+ resultOp = componentInstanceLogic.associateRIToRI(componentId, userId, requirementDef, componentTypeEnum);
+ }
+
+ Either<RequirementCapabilityRelDef, ResponseFormat> actionResponse = resultOp;
+
+ if (actionResponse.isRight()) {
+ response = buildErrorResponse(actionResponse.right().value());
+ } else {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+ }
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Associate Resource Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Associate Resource Instance");
+ log.debug("associate resource instance to another RI with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @PUT
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/dissociate")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Dissociate RI from RI", httpMethod = "PUT", notes = "Returns deleted RelationshipInfo", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Relationship deleted"), @ApiResponse(code = 403, message = "Missing information"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response dissociateRIFromRI(
+ @ApiParam(value = "allowed values are resources /services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true) @PathParam("containerComponentType") final String containerComponentType,
+ @ApiParam(value = "unique id of the container component") @PathParam("componentId") final String componentId, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @ApiParam(value = "RelationshipInfo", required = true) String data,
+ @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ try {
+
+ log.debug("Start handle request of {}", url);
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<RequirementCapabilityRelDef, ResponseFormat> regInfoW = convertToRequirementCapabilityRelDef(data);
+ if (regInfoW.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Resource Instance - dissociateRIFromRI");
+ BeEcompErrorManager.getInstance().logBeSystemError("Resource Instance - dissociateRIFromRI");
+ log.debug("Failed to convert received data to BE format.");
+ return buildErrorResponse(regInfoW.right().value());
+ }
+
+ RequirementCapabilityRelDef requirementDef = regInfoW.left().value();
+ Either<RequirementCapabilityRelDef, ResponseFormat> actionResponse = componentInstanceLogic.dissociateRIFromRI(componentId, userId, requirementDef, componentTypeEnum);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Dissociate Resource Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Dissociate Resource Instance");
+ log.debug("dissociate resource instance from service failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/createAndAssociate")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create RI and associate RI to RI", httpMethod = "POST", notes = "Returns created RI and RelationshipInfo", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "RI created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Relationship already exist") })
+ public Response createAndAssociateRIToRI(@PathParam("componentId") final String componentId,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+
+ log.debug("Start handle request of {}", url);
+
+ InputStream inputStream = request.getInputStream();
+
+ byte[] bytes = IOUtils.toByteArray(inputStream);
+
+ if (bytes == null || bytes.length == 0) {
+ log.info("Empty body was sent.");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ String data = new String(bytes);
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<CreateAndAssotiateInfo, ActionStatus> convertStatus = convertJsonToObject(data, CreateAndAssotiateInfo.class);
+ if (convertStatus.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Resource Instance - createAndAssociateRIToRI");
+ BeEcompErrorManager.getInstance().logBeSystemError("Resource Instance - createAndAssociateRIToRI");
+ log.debug("Failed to convert received data to BE format.");
+ Either<Object, ResponseFormat> formattedResponse = Either.right(getComponentsUtils().getResponseFormat(convertStatus.right().value()));
+ return buildErrorResponse(formattedResponse.right().value());
+ }
+
+ CreateAndAssotiateInfo createAndAssotiateInfo = convertStatus.left().value();
+ Either<CreateAndAssotiateInfo, ResponseFormat> actionResponse = componentInstanceLogic.createAndAssociateRIToRI(containerComponentType, componentId, userId, createAndAssotiateInfo);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResponse.left().value());
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create and Associate Resource Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create and Associate Resource Instance");
+ log.debug("create and associate RI failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/property")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update resource instance property", httpMethod = "POST", notes = "Returns updated resource instance property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource instance created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateResourceInstanceProperty(@ApiParam(value = "service id") @PathParam("componentId") final String componentId,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @ApiParam(value = "resource instance id") @PathParam("componentInstanceId") final String componentInstanceId, @ApiParam(value = "id of user initiating the operation") @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ try {
+ Wrapper<String> dataWrapper = new Wrapper<>();
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Wrapper<ComponentInstanceProperty> propertyWrapper = new Wrapper<>();
+
+ validateInputStream(request, dataWrapper, errorWrapper);
+
+ if (errorWrapper.isEmpty()) {
+ validateClassParse(dataWrapper.getInnerElement(), propertyWrapper, () -> ComponentInstanceProperty.class, errorWrapper);
+ }
+
+ if (!errorWrapper.isEmpty()) {
+ return buildErrorResponse(errorWrapper.getInnerElement());
+ }
+
+ ComponentInstanceProperty property = propertyWrapper.getInnerElement();
+
+ log.debug("Start handle request of updateResourceInstanceProperty. Received property is {}", property);
+
+ ServletContext context = request.getSession().getServletContext();
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<ComponentInstanceProperty, ResponseFormat> actionResponse = componentInstanceLogic.createOrUpdatePropertyValue(componentTypeEnum, componentId, componentInstanceId, property, userId);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+
+ ComponentInstanceProperty resourceInstanceProperty = actionResponse.left().value();
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(resourceInstanceProperty);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ log.error("create and associate RI failed with exception: ", e.getMessage());
+ log.debug("create and associate RI failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ /**
+ * Updates ResourceInstance Attribute
+ *
+ * @param componentId
+ * @param containerComponentType
+ * @param componentInstanceId
+ * @param userId
+ * @param request
+ * @return
+ */
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/attribute")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update resource instance attribute", httpMethod = "POST", notes = "Returns updated resource instance attribute", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource instance created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateResourceInstanceAttribute(@ApiParam(value = "service id") @PathParam("componentId") final String componentId,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @ApiParam(value = "resource instance id") @PathParam("componentInstanceId") final String componentInstanceId, @ApiParam(value = "id of user initiating the operation") @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ try {
+
+ Wrapper<ResponseFormat> errorWrapper = new Wrapper<>();
+ Wrapper<String> dataWrapper = new Wrapper<>();
+ Wrapper<ComponentInstanceAttribute> attributeWrapper = new Wrapper<>();
+ Wrapper<ComponentInstanceBusinessLogic> blWrapper = new Wrapper<>();
+
+ validateInputStream(request, dataWrapper, errorWrapper);
+
+ if (errorWrapper.isEmpty()) {
+ validateClassParse(dataWrapper.getInnerElement(), attributeWrapper, () -> ComponentInstanceAttribute.class, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ validateComponentInstanceBusinessLogic(request, containerComponentType, blWrapper, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ ComponentInstanceBusinessLogic componentInstanceLogic = blWrapper.getInnerElement();
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ log.debug("Start handle request of ComponentInstanceAttribute. Received attribute is {}", attributeWrapper.getInnerElement());
+ Either<ComponentInstanceAttribute, ResponseFormat> eitherAttribute = componentInstanceLogic.createOrUpdateAttributeValue(componentTypeEnum, componentId, componentInstanceId, attributeWrapper.getInnerElement(), userId);
+ if (eitherAttribute.isRight()) {
+ errorWrapper.setInnerElement(eitherAttribute.right().value());
+ } else {
+ attributeWrapper.setInnerElement(eitherAttribute.left().value());
+ }
+ }
+
+ return buildResponseFromElement(errorWrapper, attributeWrapper);
+
+ } catch (Exception e) {
+ log.error("create and associate RI failed with exception: ", e.getMessage());
+ log.debug("create and associate RI failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ @DELETE
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/property/{propertyId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update resource instance", httpMethod = "DELETE", notes = "Returns deleted resource instance property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource instance created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response deleteResourceInstanceProperty(@ApiParam(value = "service id") @PathParam("componentId") final String componentId,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @ApiParam(value = "resource instance id") @PathParam("componentInstanceId") final String componentInstanceId, @ApiParam(value = "property id") @PathParam("propertyId") final String propertyId,
+ @ApiParam(value = "id of user initiating the operation") @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @Context final HttpServletRequest request) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<ComponentInstanceProperty, ResponseFormat> actionResponse = componentInstanceLogic.deletePropertyValue(componentTypeEnum, componentId, componentInstanceId, propertyId, userId);
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
+ } catch (Exception e) {
+ log.error("create and associate RI failed with exception: ", e.getMessage());
+ log.debug("create and associate RI failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ @POST
+ @Path("/{containerComponentType}/{componentId}/resourceInstance/{componentInstanceId}/changeVersion")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update resource instance", httpMethod = "POST", notes = "Returns updated resource instance", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource instance created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response changeResourceInstanceVersion(@PathParam("componentId") final String componentId, @PathParam("componentInstanceId") final String componentInstanceId,
+ @ApiParam(value = "valid values: resources / services", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME) @PathParam("containerComponentType") final String containerComponentType,
+ @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ try {
+
+ log.debug("Start handle request of {}", url);
+
+ InputStream inputStream = request.getInputStream();
+
+ byte[] bytes = IOUtils.toByteArray(inputStream);
+
+ if (bytes == null || bytes.length == 0) {
+ log.info("Empty body was sent.");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+
+ String data = new String(bytes);
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ ComponentInstanceBusinessLogic componentInstanceLogic = getComponentInstanceBL(context, componentTypeEnum);
+ if (componentInstanceLogic == null) {
+ log.debug("Unsupported component type {}", containerComponentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType));
+ }
+
+ Either<ComponentInstance, ResponseFormat> convertResponse = convertToResourceInstance(data);
+
+ if (convertResponse.isRight()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "Resource Instance - updateResourceInstance");
+ BeEcompErrorManager.getInstance().logBeSystemError("Resource Instance - updateResourceInstance");
+ log.debug("Failed to convert received data to BE format.");
+ return buildErrorResponse(convertResponse.right().value());
+ }
+
+ ComponentInstance newResourceInstance = convertResponse.left().value();
+ Either<ComponentInstance, ResponseFormat> actionResponse = componentInstanceLogic.changeComponentInstanceVersion(containerComponentType, componentId, componentInstanceId, userId, newResourceInstance);
+
+ if (actionResponse.isRight()) {
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Resource Instance");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Resource Instance");
+ log.debug("update resource instance with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ private Either<ComponentInstance, ResponseFormat> convertToResourceInstance(String data) {
+
+ // Either<ComponentInstance, ActionStatus> convertStatus =
+ // convertJsonToObject(data, ComponentInstance.class);
+ Either<ComponentInstance, ResponseFormat> convertStatus = getComponentsUtils().convertJsonToObjectUsingObjectMapper(data, new User(), ComponentInstance.class, null, ComponentTypeEnum.RESOURCE_INSTANCE);
+ if (convertStatus.isRight()) {
+ return Either.right(convertStatus.right().value());
+ }
+ ComponentInstance resourceInstanceInfo = convertStatus.left().value();
+
+ return Either.left(resourceInstanceInfo);
+ }
+
+ // TODO Tal New API for Multiple Items move on canvas
+ private Either<List<ComponentInstance>, ResponseFormat> convertToMultipleResourceInstance(String dataList) {
+
+ Either<ComponentInstance[], ResponseFormat> convertStatus = getComponentsUtils().convertJsonToObjectUsingObjectMapper(dataList, new User(), ComponentInstance[].class, null, ComponentTypeEnum.RESOURCE_INSTANCE);
+ if (convertStatus.isRight()) {
+ return Either.right(convertStatus.right().value());
+ }
+
+ return Either.left(Arrays.asList(convertStatus.left().value()));
+ }
+
+ private Either<RequirementCapabilityRelDef, ResponseFormat> convertToRequirementCapabilityRelDef(String data) {
+
+ Either<RequirementCapabilityRelDef, ActionStatus> convertStatus = convertJsonToObject(data, RequirementCapabilityRelDef.class);
+ if (convertStatus.isRight()) {
+ return Either.right(getComponentsUtils().getResponseFormat(convertStatus.right().value()));
+ }
+ RequirementCapabilityRelDef requirementCapabilityRelDef = convertStatus.left().value();
+ return Either.left(requirementCapabilityRelDef);
+ }
+
+ private <T> Either<T, ActionStatus> convertJsonToObject(String data, Class<T> clazz) {
+ try {
+ log.trace("convert json to object. json=\n{}", data);
+ T t = null;
+ t = gson.fromJson(data, clazz);
+ if (t == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("object is null after converting from json");
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ return Either.left(t);
+ } catch (Exception e) {
+ // INVALID JSON
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("failed to convert from json", e);
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentServlet.java
new file mode 100644
index 0000000000..c5bebb41bf
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentServlet.java
@@ -0,0 +1,270 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datamodel.api.HighestFilterEnum;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.CapReqDef;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Component Servlet", description = "Component Servlet")
+@Singleton
+public class ComponentServlet extends BeGenericServlet {
+ private static Logger log = LoggerFactory.getLogger(ComponentServlet.class.getName());
+
+ @GET
+ @Path("/{componentType}/{componentId}/requirmentsCapabilities")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Component Requirments And Capabilities", httpMethod = "GET", notes = "Returns Requirments And Capabilities according to componentId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getRequirementAndCapabilities(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ Response response;
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ if (componentTypeEnum != null) {
+ ComponentBusinessLogic compBL = getComponentBL(componentTypeEnum, context);
+ Either<CapReqDef, ResponseFormat> eitherRequirementsAndCapabilities = compBL.getRequirementsAndCapabilities(componentId, componentTypeEnum, userId);
+ if (eitherRequirementsAndCapabilities.isRight()) {
+ response = buildErrorResponse(eitherRequirementsAndCapabilities.right().value());
+ } else {
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), gson.toJson(eitherRequirementsAndCapabilities.left().value()));
+ }
+ } else {
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ return response;
+ }
+
+ @GET
+ @Path("/{componentType}/latestversion/notabstract")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Component Requirments And Capabilities", httpMethod = "GET", notes = "Returns Requirments And Capabilities according to componentId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getLatestVersionNotAbstractCheckoutComponents(@PathParam("componentType") final String componentType, @Context final HttpServletRequest request, @QueryParam("internalComponentType") String internalComponentType,
+ @QueryParam("componentUids") List<String> componentUids, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ ComponentBusinessLogic businessLogic = getComponentBL(componentTypeEnum, context);
+
+ log.debug("Received componentUids size is {}", (componentUids == null ? 0 : componentUids.size()));
+
+ Either<List<Component>, ResponseFormat> actionResponse = businessLogic.getLatestVersionNotAbstractComponents(false, HighestFilterEnum.HIGHEST_ONLY, componentTypeEnum, internalComponentType, componentUids, userId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get all non abstract {}", componentType);
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ Object components = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), components);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Certified Non Abstract" + componentType);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Certified Non Abstract" + componentType);
+ log.debug("getCertifiedNotAbstractComponents failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+
+ }
+
+ @POST
+ @Path("/{componentType}/latestversion/notabstract")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Component Requirments And Capabilities", httpMethod = "GET", notes = "Returns Requirments And Capabilities according to componentId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getLatestVersionNotAbstractCheckoutComponentsByBody(@PathParam("componentType") final String componentType, @Context final HttpServletRequest request, @QueryParam("internalComponentType") String internalComponentType,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @ApiParam(value = "Consumer Object to be created", required = true) List<String> data) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ if (log.isDebugEnabled())
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ ComponentBusinessLogic businessLogic = getComponentBL(componentTypeEnum, context);
+ List<String> componentUids = data;
+ if (log.isDebugEnabled())
+ log.debug("Received componentUids size is {}", (componentUids == null ? 0 : componentUids.size()));
+
+ // long start = System.currentTimeMillis();
+
+ Either<List<Component>, ResponseFormat> actionResponse = businessLogic.getLatestVersionNotAbstractComponents(false, HighestFilterEnum.HIGHEST_ONLY, componentTypeEnum, internalComponentType, componentUids, userId);
+
+ // long endBl = System.currentTimeMillis();
+
+ if (actionResponse.isRight()) {
+ if (log.isDebugEnabled())
+ log.debug("failed to get all non abstract {}", componentType);
+ return buildErrorResponse(actionResponse.right().value());
+
+ }
+ Object components = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ Response responseToReturn = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), components);
+
+ long endResp = System.currentTimeMillis();
+
+ // log.info("********** Time calculation in ms: BL {} , Response {},
+ // Total {}", (endBl - start ), (endResp - endBl), (endResp -
+ // start));
+ return responseToReturn;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Certified Non Abstract" + componentType);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Certified Non Abstract" + componentType);
+ log.debug("getCertifiedNotAbstractComponents failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+
+ }
+
+ @GET
+ @Path("/{componentType}/latestversion/notabstract/uidonly")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Component uid only", httpMethod = "GET", notes = "Returns componentId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getLatestVersionNotAbstractCheckoutComponentsIdesOnly(@PathParam("componentType") final String componentType, @Context final HttpServletRequest request, @QueryParam("internalComponentType") String internalComponentType,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @ApiParam(value = "uid list", required = true) String data) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+ try {
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ ComponentBusinessLogic businessLogic = getComponentBL(componentTypeEnum, context);
+
+ Either<List<Map<String, String>>, ResponseFormat> actionResponse = businessLogic.getLatestVersionNotAbstractComponentsUidOnly(false, HighestFilterEnum.HIGHEST_ONLY, componentTypeEnum, internalComponentType, userId);
+ if (actionResponse.isRight()) {
+ log.debug("failed to get all non abstract {}", componentType);
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ Object components = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), components);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Certified Non Abstract" + componentType);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Certified Non Abstract" + componentType);
+ log.debug("getCertifiedNotAbstractComponents failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+
+ }
+
+ @GET
+ @Path("/{componentType}/{componentId}/componentInstances")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Component instances", httpMethod = "GET", notes = "Returns component instances", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getComponentInstancesFilteredByPropertiesAndInputs(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @Context final HttpServletRequest request,
+ @QueryParam("searchText") String searchText, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @ApiParam(value = "uid" + "" + "list", required = true) String data) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+ try {
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ ComponentBusinessLogic businessLogic = getComponentBL(componentTypeEnum, context);
+
+ Either<List<ComponentInstance>, ResponseFormat> actionResponse = businessLogic.getComponentInstancesFilteredByPropertiesAndInputs(componentId, componentTypeEnum, userId, searchText);
+ if (actionResponse.isRight()) {
+ log.debug("failed to get all component instances filtered by properties and inputs {}", componentType);
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ Object components = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), components);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Component Instances filtered by properties & inputs" + componentType);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Component Instances filtered by properties & inputs" + componentType);
+ log.debug("getComponentInstancesFilteredByPropertiesAndInputs failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigMgrServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigMgrServlet.java
new file mode 100644
index 0000000000..164b1d7665
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigMgrServlet.java
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.servlets.BasicServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/configmgr")
+public class ConfigMgrServlet extends BasicServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ConfigMgrServlet.class.getName());
+
+ @GET
+ @Path("/get")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String getConfig(@Context final HttpServletRequest request, @QueryParam("type") String type) {
+
+ String result = null;
+
+ ServletContext context = request.getSession().getServletContext();
+
+ ConfigurationManager configurationManager = (ConfigurationManager) context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR);
+
+ if (type == null || type.equals("configuration")) {
+
+ Configuration configuration = configurationManager.getConfiguration();
+ if (configuration == null) {
+ log.warn("Configuration of type {} was not found", Configuration.class);
+ } else {
+ log.info("The value returned from getConfig is {}", configuration);
+
+ result = gson.toJson(configuration);
+
+ }
+ }
+
+ return result;
+
+ }
+
+ @POST
+ @Path("/set1")
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public String setConfig1(@Context final HttpServletRequest request, Configuration configuration) {
+
+ log.debug("{}", configuration);
+
+ return "ok";
+
+ }
+
+ @POST
+ @Path("/set2")
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void setConfig2(@Context final HttpServletRequest request, Configuration configuration) {
+
+ log.debug("{}", configuration);
+
+ }
+
+ @PUT
+ @Path("/setput1")
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public String setConfig3(@Context final HttpServletRequest request, Configuration configuration) {
+
+ log.debug("{}", configuration);
+
+ return "ok";
+
+ }
+
+ @PUT
+ @Path("/setput2")
+ @Produces(MediaType.TEXT_PLAIN)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void setConfig4(@Context final HttpServletRequest request, Configuration configuration) {
+
+ log.debug("{}", configuration);
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigServlet.java
new file mode 100644
index 0000000000..2a3a87d942
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConfigServlet.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.servlets.BasicServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/config")
+public class ConfigServlet extends BasicServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ConfigServlet.class.getName());
+
+ @GET
+ @Path("/get")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String getConfig(@Context final HttpServletRequest request) {
+
+ String result = null;
+
+ ServletContext context = request.getSession().getServletContext();
+
+ ConfigurationSource configurationSource = (ConfigurationSource) context.getAttribute(Constants.CONFIGURATION_SOURCE_ATTR);
+ if (configurationSource != null) {
+ Configuration configuration = configurationSource.getAndWatchConfiguration(Configuration.class, null);
+
+ if (configuration == null) {
+ log.warn("Configuration of type {} was not found", Configuration.class);
+ }
+ log.debug("{}", configuration);
+ if (log.isInfoEnabled()) {
+ log.debug("Info level ENABLED...");
+ }
+ log.info("The value returned from getConfig is {}", configuration);
+
+ result = gson.toJson(configuration);
+
+ } else {
+ log.warn("Source Configuration object was not initialized in the context.");
+ }
+
+ return result;
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConsumerServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConsumerServlet.java
new file mode 100644
index 0000000000..0603f267ab
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ConsumerServlet.java
@@ -0,0 +1,227 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.ConsumerBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ConsumerDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/consumers")
+@Api(value = "Consumer Servlet", description = "Consumer Servlet")
+@Singleton
+public class ConsumerServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ConsumerServlet.class.getName());
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Consumer credentials", httpMethod = "POST", notes = "Returns created ECOMP consumer credentials", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Consumer credentials created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response createConsumer(@ApiParam(value = "Consumer Object to be created", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ try {
+ ConsumerBusinessLogic businessLogic = getConsumerBL(context);
+
+ Either<ConsumerDefinition, ResponseFormat> convertionResponse = convertJsonToObject(data, modifier, AuditingActionEnum.ADD_ECOMP_USER_CREDENTIALS);
+
+ if (convertionResponse.isRight()) {
+ log.debug("failed to create Consumer");
+ return buildErrorResponse(convertionResponse.right().value());
+ }
+
+ ConsumerDefinition consumer = convertionResponse.left().value();
+
+ Either<ConsumerDefinition, ResponseFormat> actionResult = businessLogic.createConsumer(modifier, consumer);
+
+ if (actionResult.isRight()) {
+ log.debug("failed to create Consumer");
+ return buildErrorResponse(actionResult.right().value());
+ }
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), actionResult.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create consumer");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create consumer");
+ log.debug("create consumer failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ @GET
+ @Path("/{consumerId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Consumer", httpMethod = "GET", notes = "Returns consumer according to ConsumerID", response = ConsumerDefinition.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Consumer found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Consumer not found") })
+ public Response getConsumer(@PathParam("consumerId") final String consumerId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ConsumerBusinessLogic businessLogic = getConsumerBL(context);
+
+ Either<ConsumerDefinition, ResponseFormat> actionResponse = businessLogic.getConsumer(consumerId, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get consumer");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Consumer");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Consumer");
+ log.debug("get consumer failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @DELETE
+ @Path("/{consumerId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Deletes Consumer", httpMethod = "DELETE", notes = "Returns deleted consumer according to ConsumerID", response = ConsumerDefinition.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "Consumer deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Consumer not found") })
+ public Response deleteConsumer(@PathParam("consumerId") final String consumerId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ConsumerBusinessLogic businessLogic = getConsumerBL(context);
+
+ Either<ConsumerDefinition, ResponseFormat> actionResponse = businessLogic.deleteConsumer(consumerId, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to delete consumer");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Consumer");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Consumer");
+ log.debug("delete consumer failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ private ConsumerBusinessLogic getConsumerBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ ConsumerBusinessLogic consumerBL = webApplicationContext.getBean(ConsumerBusinessLogic.class);
+
+ return consumerBL;
+ }
+
+ public Either<ConsumerDefinition, ResponseFormat> convertJsonToObject(String data, User user, AuditingActionEnum actionEnum) {
+ ConsumerDefinition consumer = null;
+ Gson gson = new Gson();
+ try {
+ log.trace("convert json to object. json=\n{}", data);
+ consumer = gson.fromJson(data, ConsumerDefinition.class);
+ if (consumer == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("object is null after converting from json");
+ ResponseFormat responseFormat = getComponentsUtils().getInvalidContentErrorAndAudit(user, actionEnum);
+ return Either.right(responseFormat);
+ }
+ } catch (Exception e) {
+ // INVALID JSON
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("failed to convert from json {}", data, e);
+ ResponseFormat responseFormat = getComponentsUtils().getInvalidContentErrorAndAudit(user, actionEnum);
+ return Either.right(responseFormat);
+ }
+ return Either.left(consumer);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/CsarBuildServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/CsarBuildServlet.java
new file mode 100644
index 0000000000..4ba6b7516d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/CsarBuildServlet.java
@@ -0,0 +1,296 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+//import org.openecomp.sdc.be.builders.tosca.api.TopologyService;
+//import org.openecomp.sdc.be.tosca.parsers.ParserMode;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/services")
+public class CsarBuildServlet extends ToscaDaoServlet {
+
+ private static Logger log = LoggerFactory.getLogger(CsarBuildServlet.class.getName());
+
+ @GET
+ @Path("/{serviceName}/{serviceVersion}")
+ public Response getDefaultTemplate(@Context final HttpServletRequest request, @PathParam("serviceName") final String serviceName, @PathParam("serviceVersion") final String serviceVersion) {
+
+ return null;// buildToscaCsar(request, serviceName, serviceVersion);
+
+ }
+
+ @GET
+ @Path("/{serviceName}/{serviceVersion}/csar")
+ public Response getToscaCsarTemplate(@Context final HttpServletRequest request, @PathParam("serviceName") final String serviceName, @PathParam("serviceVersion") final String serviceVersion) {
+
+ return null; // buildToscaCsar(request, serviceName, serviceVersion);
+
+ }
+
+ /*
+ * private Response buildToscaCsar(final HttpServletRequest request, String serviceName, String serviceVersion) { log.debug("Building CSAR for service:{} , version:{}", serviceName, serviceVersion); ServletContext context =
+ * request.getSession().getServletContext(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream); String fileName =
+ * serviceName+"_"+serviceVersion+".zip";
+ *
+ * IResourceUploader resourceUploader = getResourceUploader(context); TopologyService topologyService = getToscaYamlBuilder(context).getServiceYaml(serviceName, serviceVersion, ParserMode.CSAR, "Scripts"); try {
+ *
+ *
+ * //Add Service yml to Zip addZipEntry(zipOutputStream, "Definitions/"+serviceName+".yaml", topologyService.getTopologyServiceYaml());
+ *
+ * //Add Resources to Zip addResourcesToZip(zipOutputStream, topologyService);
+ *
+ * //Add Artifacts to Zip addArtifactsToZip(serviceName, serviceVersion, zipOutputStream, resourceUploader);
+ *
+ * //Prepare Tosca metadata addToscaMetaToZip(zipOutputStream, topologyService, resourceUploader);
+ *
+ * }
+ *
+ * catch (IOException e) { log.error("Failed to create CSAR file", e); return null; } finally{ try { byteArrayOutputStream.close(); zipOutputStream.close(); } catch (IOException e) { log.error("Failed to close output srream", e); }
+ *
+ * } return buildResponse(fileName, byteArrayOutputStream);
+ *
+ *
+ * }
+ */
+
+ public static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta";
+ // protected void addToscaMetaToZip(ZipOutputStream zipOutputStream,
+ // TopologyService topService, IResourceUploader resUploader) throws
+ // IOException {
+ // String serviceVersion = topService.getTopologyVersion();
+ // String serviceName = topService.getTopologyName();
+ // StringBuffer buff = new StringBuffer();
+ //
+ // String[] serviceToscaMeta = prepareToscaMetaHeader(serviceName);
+ //
+ // for( String toAppend : serviceToscaMeta){
+ // buff.append(toAppend);
+ // }
+ //
+ // List<String> toscaMetaComponents =
+ // prepareToscaMetaComponents(topService.getDependenciesYamls().keySet());
+ // for( String toAppend : toscaMetaComponents ){
+ // buff.append(toAppend);
+ // }
+ //
+ // Either<ServiceArtifactsDataCollection, ResourceUploadStatus>
+ // getServiceArtifactsCollectionStatus =
+ // Either.right(ResourceUploadStatus.ERROR);//resUploader.getServiceArtifactsCollection(serviceName,
+ // serviceVersion);
+ // if(getServiceArtifactsCollectionStatus.isLeft()){
+ // List<String> toscaMetaArtifacts =
+ // prepareToscaMetaArtifacts(getServiceArtifactsCollectionStatus.left().value());
+ // for( String toAppend : toscaMetaArtifacts ){
+ // buff.append(toAppend);
+ // }
+ // }
+ //
+ // addZipEntry(zipOutputStream, TOSCA_META_PATH, buff.toString());
+ // }
+
+ // protected List<String> prepareToscaMetaArtifacts(
+ // ServiceArtifactsDataCollection serviceArtifactsDataCollection) {
+ // List<String> artifactsForToscaMeta = new ArrayList<String>();
+ // Iterator<Entry<String, List<ArtifactData>>> nodeTemplateArtifactsItr =
+ // serviceArtifactsDataCollection.getServiceArtifactDataMap().entrySet().iterator();
+ // while( nodeTemplateArtifactsItr.hasNext() ){
+ // Entry<String, List<ArtifactData>> nodeNameArtifacts =
+ // nodeTemplateArtifactsItr.next();
+ // String nodeName = nodeNameArtifacts.getKey();
+ // Iterator<ArtifactData> artifactDataItr =
+ // nodeNameArtifacts.getValue().iterator();
+ // while( artifactDataItr.hasNext() ){
+ // ArtifactData artifactData = artifactDataItr.next();
+ // artifactsForToscaMeta.add("\n");
+ // artifactsForToscaMeta.add("Name: "+getArtifactPath(nodeName,
+ // artifactData)+"\n");
+ // artifactsForToscaMeta.add("Content-Type:
+ // application/"+getAppliactionMime(artifactData.getArtifactName())+"\n");
+ // }
+ // }
+ // return artifactsForToscaMeta;
+ // }
+
+ // protected List<String> prepareToscaMetaComponents(Set<String> components)
+ // {
+ // Iterator<String> resourceNameItr = components.iterator();
+ // List<String> componentsForToscaMeta = new ArrayList<String>();
+ // while( resourceNameItr.hasNext() ){
+ // String resourceName = resourceNameItr.next();
+ // componentsForToscaMeta.add("\n");
+ // componentsForToscaMeta.add("Name: "+getResourcePath(resourceName)+"\n");
+ // componentsForToscaMeta.add("Content-Type:
+ // application/vnd.oasis.tosca.definitions.yaml\n");
+ //
+ // }
+ // return componentsForToscaMeta;
+ // }
+
+ protected String[] prepareToscaMetaHeader(String serviceName) {
+ return new String[] { "TOSCA-Meta-File-Version: 1.0\n", "CSAR-Version: 1.1\n", "Created-By: INTERWISE\n", "\n", "Entry-Definitions: Definitions/" + serviceName + ".yaml\n", "\n", "Name: Definitions/" + serviceName + ".yaml\n",
+ "Content-Type: application/vnd.oasis.tosca.definitions.yaml\n" };
+ }
+
+ protected String getAppliactionMime(String fileName) {
+ String mimeType;
+ if (fileName.contains(".sh")) {
+ mimeType = "x-sh";
+ } else if (fileName.contains(".yang")) {
+ mimeType = "yang";
+ }
+
+ else if (fileName.contains(".rar")) {
+ mimeType = "x-rar-compressed";
+ }
+
+ else if (fileName.contains(".zip")) {
+ mimeType = "zip";
+ }
+
+ else if (fileName.contains(".tar")) {
+ mimeType = "x-tar";
+ }
+
+ else if (fileName.contains(".7z")) {
+ mimeType = "x-7z-compressed";
+ }
+
+ else {
+ // Undefined
+ mimeType = "undefined";
+ }
+ return mimeType;
+ }
+
+ // protected void addResourcesToZip(ZipOutputStream zipOutputStream,
+ // TopologyService topService) throws IOException {
+ // Iterator<Entry<String, String>> resourceNameDataItr =
+ // topService.getDependenciesYamls().entrySet().iterator();
+ // while( resourceNameDataItr.hasNext() ){
+ // Entry<String, String> resourceNameData = resourceNameDataItr.next();
+ // addZipEntry(zipOutputStream, getResourcePath(resourceNameData.getKey()),
+ // resourceNameData.getValue());
+ // }
+ // }
+
+ protected String getArtifactPath(String nodeTamplateName, ESArtifactData artifactData) {
+ // return "Scripts/"+nodeTamplateName+"/"+
+ // artifactData.getArtifactName();
+ return "Scripts/" + nodeTamplateName + "/" + artifactData.getId();
+ }
+
+ protected String getResourcePath(String resourceName) {
+ return "Definitions/" + resourceName + ".yaml";
+ }
+
+ // protected void addArtifactsToZip(String serviceName, String
+ // serviceVersion, ZipOutputStream zipOutputStream, IResourceUploader
+ // resourceUploader)
+ // throws IOException {
+ // //Add Artifacts to Zip
+ // Either<ServiceArtifactsDataCollection, ResourceUploadStatus>
+ // getServiceArtifactsCollectionStatus =
+ // Either.right(ResourceUploadStatus.ERROR);//resourceUploader.getServiceArtifactsCollection(serviceName,
+ // serviceVersion);
+ // if(getServiceArtifactsCollectionStatus.isLeft()){
+ // Iterator<Entry<String, List<ArtifactData>>> nodeTemplateArtifactsItr
+ // =getServiceArtifactsCollectionStatus.left().value().getServiceArtifactDataMap().entrySet().iterator();
+ // while( nodeTemplateArtifactsItr.hasNext() ){
+ // Entry<String, List<ArtifactData>> nodeTemplateArtifacts =
+ // nodeTemplateArtifactsItr.next();
+ // String nodeTamplateName = nodeTemplateArtifacts.getKey();
+ // List<ArtifactData> artifacts = nodeTemplateArtifacts.getValue();
+ // for(ArtifactData artifactData : artifacts ){
+ // addZipEntry(zipOutputStream,
+ // getArtifactPath(nodeTamplateName,artifactData), artifactData.getData());
+ // }
+ //
+ // }
+ // }
+ // }
+
+ // private Response buildResponse(String fileName, ByteArrayOutputStream
+ // fileOutputStream) {
+ // final InputStream inputStream = new
+ // ByteArrayInputStream(fileOutputStream.toByteArray());
+ //
+ // StreamingOutput stream = new StreamingOutput() {
+ // public void write(OutputStream output) throws WebApplicationException {
+ // try {
+ // IOUtils.copy(inputStream, output);
+ // } catch (Exception e) {
+ // throw new WebApplicationException(e);
+ // }
+ // }
+ // };
+ //
+ // return
+ // Response.ok(stream).type(MediaType.APPLICATION_OCTET_STREAM_TYPE).header("content-disposition","attachment;
+ // filename = "+fileName).build();
+ // }
+
+ // protected void addZipEntry(ZipOutputStream zipOutputStream, String
+ // filePath, String data) throws IOException {
+ // addZipEntry(zipOutputStream, filePath, encodeString(data));
+ // }
+
+ private byte[] encodeString(String data) throws CharacterCodingException {
+ Charset charset = Charset.forName("UTF-8");
+ CharsetEncoder encoder = charset.newEncoder();
+ ByteBuffer bb = encoder.encode(CharBuffer.wrap(data.toCharArray()));
+ byte[] ba = new byte[bb.limit()];
+ bb.get(ba);
+ return ba;
+ }
+
+ // private void addZipEntry(ZipOutputStream zipOutputStream, String
+ // filePath, byte[] data) throws IOException {
+ // log.debug("Adding to CSAR zip :{}", filePath);
+ // ZipEntry entry = new ZipEntry(filePath);
+ // zipOutputStream.putNextEntry(entry);
+ // zipOutputStream.write(data);
+ // zipOutputStream.closeEntry();
+ // }
+
+ @Override
+ public Logger getLogger() {
+ return log;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DistributionServiceServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DistributionServiceServlet.java
new file mode 100644
index 0000000000..62a321fa5b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DistributionServiceServlet.java
@@ -0,0 +1,169 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.DistributionMonitoringBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.info.DistributionStatusListResponse;
+import org.openecomp.sdc.be.info.DistributionStatusOfServiceListResponce;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Distribution Service Servlet", description = "Distribution Service Servlet")
+@Singleton
+public class DistributionServiceServlet extends BeGenericServlet {
+ private static Logger log = LoggerFactory.getLogger(DistributionServiceServlet.class.getName());
+
+ @Resource
+ private DistributionMonitoringBusinessLogic distributionMonitoringLogic;
+
+ @GET
+ @Path("/services/{serviceUUID}/distribution")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Distributions", httpMethod = "GET", notes = "Returns list bases on the information extracted from Auditing Records according to service uuid", response = DistributionStatusListResponse.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Service not found") })
+ public Response getServiceById(@PathParam("serviceUUID") final String serviceUUID, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ init(request);
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ Response response = null;
+ ResponseFormat responseFormat = null;
+
+ try {
+ Either<DistributionStatusOfServiceListResponce, ResponseFormat> actionResponse = distributionMonitoringLogic.getListOfDistributionServiceStatus(serviceUUID, userId);
+
+ if (actionResponse.isRight()) {
+
+ responseFormat = actionResponse.right().value();
+ response = buildErrorResponse(responseFormat);
+ } else {
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ response = buildOkResponse(responseFormat, actionResponse.left().value());
+
+ }
+
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Distribution list for Service");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Distribution list for Service");
+ log.debug("failed to get service distribution statuses", e);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+
+ response = buildErrorResponse(responseFormat);
+ return response;
+ }
+
+ }
+
+ @GET
+ @Path("/services/distribution/{did}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Distributions", httpMethod = "GET", notes = "Return the list of distribution status objects", response = DistributionStatusListResponse.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Status not found") })
+ public Response getListOfDistributionStatuses(@PathParam("did") final String did, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ init(request);
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ Response response = null;
+ ResponseFormat responseFormat = null;
+
+ try {
+ Either<DistributionStatusListResponse, ResponseFormat> actionResponse = distributionMonitoringLogic.getListOfDistributionStatus(did, userId);
+
+ if (actionResponse.isRight()) {
+
+ responseFormat = actionResponse.right().value();
+ log.debug("failed to fount statuses for did {} {}", did, responseFormat);
+ response = buildErrorResponse(responseFormat);
+ } else {
+
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ log.debug("success to fount statuses for did {} {}", did, actionResponse.left().value());
+ response = buildOkResponse(responseFormat, actionResponse.left().value());
+
+ }
+
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Distribution Status");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Distribution Status");
+ log.debug("failed to get distribution status ", e);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+
+ response = buildErrorResponse(responseFormat);
+ return response;
+ }
+
+ }
+
+ private void init(HttpServletRequest request) {
+ if (distributionMonitoringLogic == null) {
+ distributionMonitoringLogic = getDistributionBL(request.getSession().getServletContext());
+ }
+ }
+
+ private DistributionMonitoringBusinessLogic getDistributionBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ DistributionMonitoringBusinessLogic distributionBl = webApplicationContext.getBean(DistributionMonitoringBusinessLogic.class);
+ return distributionBl;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java
new file mode 100644
index 0000000000..433a1bfc58
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ElementServlet.java
@@ -0,0 +1,611 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.clean.ComponentsCleanBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.info.ArtifactTypesInfo;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.Category;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.PropertyScope;
+import org.openecomp.sdc.be.model.Tag;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Path("/v1/")
+
+/****
+ *
+ * UI oriented servlet - to return elements in specific format UI needs
+ *
+ *
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Api(value = "Element Servlet", description = "Element Servlet")
+@Singleton
+public class ElementServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ElementServlet.class.getName());
+
+ /*
+ ******************************************************************************
+ * NEW CATEGORIES category / \ subcategory subcategory / grouping
+ ******************************************************************************/
+
+ /*
+ *
+ *
+ * CATEGORIES
+ */
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all component categories
+ @GET
+ @Path("/categories/{componentType}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve the list of all resource/service/product categories/sub-categories/groupings", httpMethod = "GET", notes = "Retrieve the list of all resource/service/product categories/sub-categories/groupings.", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns categories Ok"), @ApiResponse(code = 403, message = "Missing information"), @ApiResponse(code = 400, message = "Invalid component type"),
+ @ApiResponse(code = 409, message = "Restricted operation"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getComponentCategories(@ApiParam(value = "allowed values are resources / services/ products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true) @PathParam(value = "componentType") final String componentType, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @Context final HttpServletRequest request) {
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<List<CategoryDefinition>, ResponseFormat> either = elementBL.getAllCategories(componentType, userId);
+ if (either.isRight()) {
+ log.debug("No categories were found for type {}", componentType);
+ return buildErrorResponse(either.right().value());
+ } else {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), either.left().value());
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Component Categories");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Component Categories");
+ log.debug("getComponentCategories failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @POST
+ @Path("/category/{componentType}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create new component category", httpMethod = "POST", notes = "Create new component category")
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Category created"), @ApiResponse(code = 400, message = "Invalid category data"), @ApiResponse(code = 403, message = "USER_ID header is missing"),
+ @ApiResponse(code = 409, message = "Category already exists / User not permitted to perform the action"), @ApiResponse(code = 500, message = "General Error") })
+ public Response createComponentCategory(
+ @ApiParam(value = "allowed values are resources /services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true) @PathParam(value = "componentType") final String componentType,
+ @ApiParam(value = "Category to be created", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ CategoryDefinition category = RepresentationUtils.fromRepresentation(data, CategoryDefinition.class);
+
+ Either<CategoryDefinition, ResponseFormat> createResourceCategory = elementBL.createCategory(category, componentType, userId);
+ if (createResourceCategory.isRight()) {
+ return buildErrorResponse(createResourceCategory.right().value());
+ }
+
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
+ return buildOkResponse(responseFormat, createResourceCategory.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create resource category");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create resource category");
+ log.debug("createResourceCategory failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @DELETE
+ @Path("/category/{componentType}/{categoryUniqueId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete component category", httpMethod = "DELETE", notes = "Delete component category", response = Category.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "Category deleted"), @ApiResponse(code = 403, message = "USER_ID header is missing"), @ApiResponse(code = 409, message = "User not permitted to perform the action"),
+ @ApiResponse(code = 404, message = "Category not found"), @ApiResponse(code = 500, message = "General Error") })
+ public Response deleteComponentCategory(@PathParam(value = "categoryUniqueId") final String categoryUniqueId, @PathParam(value = "componentType") final String componentType, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<CategoryDefinition, ResponseFormat> createResourceCategory = elementBL.deleteCategory(categoryUniqueId, componentType, userId);
+
+ if (createResourceCategory.isRight()) {
+ return buildErrorResponse(createResourceCategory.right().value());
+ }
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT);
+ return buildOkResponse(responseFormat, null);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create resource category");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create resource category");
+ log.debug("createResourceCategory failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ /*
+ *
+ *
+ * SUBCATEGORIES
+ *
+ */
+
+ @POST
+ @Path("/category/{componentType}/{categoryId}/subCategory")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create new component sub-category", httpMethod = "POST", notes = "Create new component sub-category for existing category")
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Subcategory created"), @ApiResponse(code = 400, message = "Invalid subcategory data"), @ApiResponse(code = 403, message = "USER_ID header is missing"),
+ @ApiResponse(code = 404, message = "Parent category wasn't found"), @ApiResponse(code = 409, message = "Subcategory already exists / User not permitted to perform the action"), @ApiResponse(code = 500, message = "General Error") })
+ public Response createComponentSubCategory(
+ @ApiParam(value = "allowed values are resources / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true) @PathParam(value = "componentType") final String componentType,
+ @ApiParam(value = "Parent category unique ID", required = true) @PathParam(value = "categoryId") final String categoryId, @ApiParam(value = "Subcategory to be created. \ne.g. {\"name\":\"Resource-subcat\"}", required = true) String data,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ SubCategoryDefinition subCategory = RepresentationUtils.fromRepresentation(data, SubCategoryDefinition.class);
+
+ Either<SubCategoryDefinition, ResponseFormat> createSubcategory = elementBL.createSubCategory(subCategory, componentType, categoryId, userId);
+ if (createSubcategory.isRight()) {
+ return buildErrorResponse(createSubcategory.right().value());
+ }
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
+ return buildOkResponse(responseFormat, createSubcategory.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create sub-category");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create sub-category");
+ log.debug("createComponentSubCategory failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @DELETE
+ @Path("/category/{componentType}/{categoryUniqueId}/subCategory/{subCategoryUniqueId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete component category", httpMethod = "DELETE", notes = "Delete component category", response = Category.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "Category deleted"), @ApiResponse(code = 403, message = "USER_ID header is missing"), @ApiResponse(code = 409, message = "User not permitted to perform the action"),
+ @ApiResponse(code = 404, message = "Category not found"), @ApiResponse(code = 500, message = "General Error") })
+ public Response deleteComponentSubCategory(@PathParam(value = "categoryUniqueId") final String categoryUniqueId, @PathParam(value = "subCategoryUniqueId") final String subCategoryUniqueId,
+ @PathParam(value = "componentType") final String componentType, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<SubCategoryDefinition, ResponseFormat> deleteSubResourceCategory = elementBL.deleteSubCategory(categoryUniqueId, subCategoryUniqueId, componentType, userId);
+ if (deleteSubResourceCategory.isRight()) {
+ return buildErrorResponse(deleteSubResourceCategory.right().value());
+ }
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT);
+ return buildOkResponse(responseFormat, null);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete component category");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete component category");
+ log.debug("deleteComponentSubCategory failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ /*
+ * GROUPINGS
+ */
+ @POST
+ @Path("/category/{componentType}/{categoryId}/subCategory/{subCategoryId}/grouping")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create new component grouping", httpMethod = "POST", notes = "Create new component grouping for existing sub-category")
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Grouping created"), @ApiResponse(code = 400, message = "Invalid grouping data"), @ApiResponse(code = 403, message = "USER_ID header is missing"),
+ @ApiResponse(code = 404, message = "Parent category or subcategory were not found"), @ApiResponse(code = 409, message = "Grouping already exists / User not permitted to perform the action"),
+ @ApiResponse(code = 500, message = "General Error") })
+ public Response createComponentGrouping(@ApiParam(value = "allowed values are products", allowableValues = ComponentTypeEnum.PRODUCT_PARAM_NAME, required = true) @PathParam(value = "componentType") final String componentType,
+ @ApiParam(value = "Parent category unique ID", required = true) @PathParam(value = "categoryId") final String grandParentCategoryId,
+ @ApiParam(value = "Parent sub-category unique ID", required = true) @PathParam(value = "subCategoryId") final String parentSubCategoryId, @ApiParam(value = "Subcategory to be created", required = true) String data,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ GroupingDefinition grouping = RepresentationUtils.fromRepresentation(data, GroupingDefinition.class);
+
+ Either<GroupingDefinition, ResponseFormat> createGrouping = elementBL.createGrouping(grouping, componentType, grandParentCategoryId, parentSubCategoryId, userId);
+ if (createGrouping.isRight()) {
+ return buildErrorResponse(createGrouping.right().value());
+ }
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
+ return buildOkResponse(responseFormat, createGrouping.left().value());
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create grouping");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create grouping");
+ log.debug("createComponentGrouping failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @DELETE
+ @Path("/category/{componentType}/{categoryUniqueId}/subCategory/{subCategoryUniqueId}/grouping/{groupingUniqueId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete component category", httpMethod = "DELETE", notes = "Delete component category", response = Category.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "Category deleted"), @ApiResponse(code = 403, message = "USER_ID header is missing"), @ApiResponse(code = 409, message = "User not permitted to perform the action"),
+ @ApiResponse(code = 404, message = "Category not found"), @ApiResponse(code = 500, message = "General Error") })
+ public Response deleteComponentGrouping(@PathParam(value = "categoryUniqueId") final String grandParentCategoryUniqueId, @PathParam(value = "subCategoryUniqueId") final String parentSubCategoryUniqueId,
+ @PathParam(value = "groupingUniqueId") final String groupingUniqueId, @PathParam(value = "componentType") final String componentType, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<GroupingDefinition, ResponseFormat> deleteGrouping = elementBL.deleteGrouping(grandParentCategoryUniqueId, parentSubCategoryUniqueId, groupingUniqueId, componentType, userId);
+ if (deleteGrouping.isRight()) {
+ return buildErrorResponse(deleteGrouping.right().value());
+ }
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT);
+ return buildOkResponse(responseFormat, null);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete component grouping");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete component grouping");
+ log.debug("deleteGrouping failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all tags
+ @GET
+ @Path("/tags")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve all tags", httpMethod = "GET", notes = "Retrieve all tags", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns tags Ok"), @ApiResponse(code = 404, message = "No tags were found"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getTags(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(getTags) Start handle request of {}", url);
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<List<Tag>, ActionStatus> either = elementBL.getAllTags(userId);
+ if (either.isRight() || either.left().value() == null) {
+ log.debug("No tags were found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), either.left().value());
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get All Tags");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get All Tags");
+ log.debug("getAllTags failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all property scopes
+ @GET
+ @Path("/propertyScopes")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve all propertyScopes", httpMethod = "GET", notes = "Retrieve all propertyScopes", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns propertyScopes Ok"), @ApiResponse(code = 404, message = "No propertyScopes were found"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getPropertyScopes(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(getPropertyScopes) Start handle request of {}", url);
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<List<PropertyScope>, ActionStatus> either = elementBL.getAllPropertyScopes(userId);
+ if (either.isRight() || either.left().value() == null) {
+ log.debug("No property scopes were found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), either.left().value());
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Property Scopes Categories");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Property Scopes Categories");
+ log.debug("getPropertyScopes failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all artifact types
+ @GET
+ @Path("/artifactTypes")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve all artifactTypes", httpMethod = "GET", notes = "Retrieve all artifactTypes", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns artifactTypes Ok"), @ApiResponse(code = 404, message = "No artifactTypes were found"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getArtifactTypes(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(getArtifactTypes) Start handle request of {}", url);
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<List<ArtifactType>, ActionStatus> either = elementBL.getAllArtifactTypes(userId);
+ if (either.isRight() || either.left().value() == null) {
+ log.debug("No artifact types were found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else {
+
+ Integer defaultHeatTimeout = ConfigurationManager.getConfigurationManager().getConfiguration().getDefaultHeatArtifactTimeoutMinutes();
+ ArtifactTypesInfo typesResponse = new ArtifactTypesInfo();
+ typesResponse.setArtifactTypes(either.left().value());
+ typesResponse.setHeatDefaultTimeout(defaultHeatTimeout);
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), typesResponse);
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Artifact Types");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Artifact Types");
+ log.debug("getArtifactTypes failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all artifact types
+ @GET
+ @Path("/configuration/ui")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve all artifactTypes", httpMethod = "GET", notes = "Retrieve all artifactTypes", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns artifactTypes Ok"), @ApiResponse(code = 404, message = "No artifactTypes were found"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getConfiguration(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(getConfiguration) Start handle request of {}", url);
+
+ try {
+ ElementBusinessLogic elementBL = getElementBL(request.getSession().getServletContext());
+ Either<List<ArtifactType>, ActionStatus> otherEither = elementBL.getAllArtifactTypes(userId);
+ Either<Integer, ActionStatus> defaultHeatTimeout = elementBL.getDefaultHeatTimeout();
+ Either<Map<String, Object>, ActionStatus> deploymentEither = elementBL.getAllDeploymentArtifactTypes();
+ Either<Map<String, String>, ActionStatus> resourceTypesMap = elementBL.getResourceTypesMap();
+
+ if (otherEither.isRight() || otherEither.left().value() == null) {
+ log.debug("No other artifact types were found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else if (deploymentEither.isRight() || deploymentEither.left().value() == null) {
+ log.debug("No deployment artifact types were found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else if (defaultHeatTimeout.isRight() || defaultHeatTimeout.left().value() == null) {
+ log.debug("heat default timeout was not found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else if (resourceTypesMap.isRight() || resourceTypesMap.left().value() == null) {
+ log.debug("No resource types were found");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT));
+ } else {
+ Map<String, Object> artifacts = new HashMap<String, Object>();
+ Map<String, Object> configuration = new HashMap<String, Object>();
+
+ artifacts.put("other", otherEither.left().value());
+ artifacts.put("deployment", deploymentEither.left().value());
+ configuration.put("artifacts", artifacts);
+ configuration.put("defaultHeatTimeout", defaultHeatTimeout.left().value());
+ configuration.put("componentTypes", elementBL.getAllComponentTypesParamNames());
+ configuration.put("roles", elementBL.getAllSupportedRoles());
+ configuration.put("resourceTypes", resourceTypesMap.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), configuration);
+ }
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Artifact Types");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Artifact Types");
+ log.debug("getArtifactTypes failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all followed resources and services
+ @GET
+ @Path("/followed")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve all followed", httpMethod = "GET", notes = "Retrieve all followed", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns followed Ok"), @ApiResponse(code = 404, message = "No followed were found"), @ApiResponse(code = 404, message = "User not found"),
+ @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getFollowedResourcesServices(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ Response res = null;
+ User userData = null;
+ try {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ UserBusinessLogic userAdminManager = getUserAdminManager(request.getSession().getServletContext());
+
+ // Getting the user
+ Either<User, ActionStatus> either = userAdminManager.getUser(userId, false);
+ if (either.isRight()) {
+ // Couldn't find or otherwise fetch the user
+ return buildErrorResponse(getComponentsUtils().getResponseFormatByUserId(either.right().value(), userId));
+ }
+
+ if (either.left().value() != null) {
+ userData = either.left().value();
+ Either<Map<String, List<? extends Component>>, ResponseFormat> followedResourcesServices = getElementBL(request.getSession().getServletContext()).getFollowed(userData);
+ // res =
+ // buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK),
+ // followedResourcesServices.left().value());
+ if (followedResourcesServices.isRight()) {
+ log.debug("failed to get followed resources services ");
+ return buildErrorResponse(followedResourcesServices.right().value());
+ }
+ Object data = RepresentationUtils.toRepresentation(followedResourcesServices.left().value());
+ res = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), data);
+ } else {
+ res = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Followed Resources / Services Categories");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Followed Resources / Services Categories");
+ log.debug("Getting followed resources/services failed with exception", e);
+ res = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ return res;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all certified resources and services and their last version
+ @GET
+ @Path("/screen")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve catalog resources and services", httpMethod = "GET", notes = "Retrieve catalog resources and services", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns resources and services Ok"), @ApiResponse(code = 404, message = "No resources and services were found"), @ApiResponse(code = 404, message = "User not found"),
+ @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getCatalogComponents(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ Response res = null;
+ try {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ Either<Map<String, List<? extends Component>>, ResponseFormat> catalogData = getElementBL(request.getSession().getServletContext()).getCatalogComponents(userId);
+
+ if (catalogData.isRight()) {
+ log.debug("failed to get catalog data");
+ return buildErrorResponse(catalogData.right().value());
+ }
+ Object data = RepresentationUtils.toRepresentation(catalogData.left().value());
+ res = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), data);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Catalog Components");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Catalog Components");
+ log.debug("Getting catalog components failed with exception", e);
+ res = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ return res;
+ }
+
+ @DELETE
+ @Path("/inactiveComponents/{componentType}")
+ public Response deleteMarkedResources(@PathParam("componentType") final String componentType, @Context final HttpServletRequest request) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ NodeTypeEnum nodeType = NodeTypeEnum.getByNameIgnoreCase(componentType);
+ if (nodeType == null) {
+ log.info("componentType is not valid: {}", componentType);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ }
+
+ List<NodeTypeEnum> componentsList = new ArrayList<NodeTypeEnum>();
+ componentsList.add(nodeType);
+ try {
+ ComponentsCleanBusinessLogic businessLogic = getComponentCleanerBL(context);
+ Map<NodeTypeEnum, Either<List<String>, ResponseFormat>> cleanComponentsResult = businessLogic.cleanComponents(componentsList);
+ Either<List<String>, ResponseFormat> cleanResult = cleanComponentsResult.get(nodeType);
+
+ if (cleanResult.isRight()) {
+ log.debug("failed to delete marked components of type {}", nodeType);
+ response = buildErrorResponse(cleanResult.right().value());
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), cleanResult.left().value());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Marked Components");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Marked Components");
+ log.debug("delete marked components failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/ecompPortalMenu")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve ecomp portal menu - MOC", httpMethod = "GET", notes = "Retrieve ecomp portal menu", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Retrieve ecomp portal menu") })
+ public Response getListOfCsars(@Context final HttpServletRequest request) {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK),
+ "[{\"menuId\":1,\"column\":2,\"text\":\"Design\",\"parentMenuId\":null,\"url\":\"\",\"appid\":null,\"roles\":null,\"children\":[{\"menuId\":11,\"column\":1,\"text\":\"ProductDesign\",\"parentMenuId\":1,\"url\":\"\",\"appid\":null,\"roles\":null},{\"menuId\":12,\"column\":2,\"text\":\"Service\",\"parentMenuId\":1,\"url\":\"\",\"appid\":null,\"roles\":null,\"children\":[{\"menuId\":21,\"column\":1,\"text\":\"ViewPolicies\",\"parentMenuId\":12,\"url\":\"\",\"appid\":null,\"roles\":null,\"children\":[{\"menuId\":90,\"column\":1,\"text\":\"4thLevelApp1aR16\",\"parentMenuId\":21,\"url\":\"http://google.com\",\"appid\":null,\"roles\":null}]},{\"menuId\":22,\"column\":2,\"text\":\"UpdatePolicies\",\"parentMenuId\":12,\"url\":\"\",\"appid\":null,\"roles\":null,\"children\":[{\"menuId\":91,\"column\":1,\"text\":\"4thLevelApp1bR16\",\"parentMenuId\":22,\"url\":\"http://jsonlint.com/\",\"appid\":null,\"roles\":null}]},{\"menuId\":23,\"column\":3,\"text\":\"UpdateRules\",\"parentMenuId\":12,\"url\":\"\",\"appid\":null,\"roles\":null},{\"menuId\":24,\"column\":4,\"text\":\"CreateSignatures?\",\"parentMenuId\":12,\"url\":\"\",\"appid\":null,\"roles\":null},{\"menuId\":25,\"column\":5,\"text\":\"Definedata\",\"parentMenuId\":12,\"url\":\"\",\"appid\":null,\"roles\":null}]}]}]");
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/GroupServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/GroupServlet.java
new file mode 100644
index 0000000000..bde8f93929
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/GroupServlet.java
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.GroupBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.info.GroupDefinitionInfo;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Resource Group Servlet", description = "Resource Group Servlet")
+@Singleton
+public class GroupServlet extends AbstractValidationsServlet {
+
+ private static Logger log = LoggerFactory.getLogger(GroupServlet.class.getName());
+
+ private Gson gson = new Gson();
+
+ @GET
+ @Path("/{containerComponentType}/{componentId}/groups/{groupId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get group artifacts ", httpMethod = "GET", notes = "Returns artifacts metadata according to groupId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "group found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Group not found") })
+ public Response getGroupArtifactById(@PathParam("containerComponentType") final String containerComponentType, @PathParam("componentId") final String componentId, @PathParam("groupId") final String groupId,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+
+ GroupBusinessLogic businessLogic = this.getGroupBL(context);
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ Either<GroupDefinitionInfo, ResponseFormat> actionResponse = businessLogic.getGroupWithArtifactsById(componentTypeEnum, componentId, groupId, userId, false);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get all non abstract {}", containerComponentType);
+ return buildErrorResponse(actionResponse.right().value());
+ }
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "getGroupArtifactById");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("getGroupArtifactById");
+ log.debug("getGroupArtifactById unexpected exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ @PUT
+ @Path("/{containerComponentType}/{componentId}/groups/{groupUniqueId}/metadata")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Group Metadata", httpMethod = "PUT", notes = "Returns updated group definition", response = GroupDefinition.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Group Updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateGroupMetadata(@PathParam("containerComponentType") final String containerComponentType, @PathParam("componentId") final String componentId, @PathParam("groupUniqueId") final String groupUniqueId,
+ @ApiParam(value = "Service object to be Updated", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User user = new User();
+ user.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ GroupBusinessLogic businessLogic = getGroupBL(context);
+
+ Either<GroupDefinition, ResponseFormat> convertResponse = parseToGroup(data);
+ if (convertResponse.isRight()) {
+ log.debug("failed to parse group");
+ response = buildErrorResponse(convertResponse.right().value());
+ return response;
+ }
+ GroupDefinition updatedGroup = convertResponse.left().value();
+
+ // Update GroupDefinition
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType);
+ Either<GroupDefinition, ResponseFormat> actionResponse = businessLogic.updateGroupMetadata(componentId, user, groupUniqueId, componentTypeEnum, updatedGroup, true);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to update GroupDefinition");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ GroupDefinition group = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(group);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Group Metadata");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Group Metadata");
+ log.debug("update group metadata failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ /**
+ * Convert data to GroupDefinition object
+ *
+ * @param groupJson
+ * @return
+ */
+ public Either<GroupDefinition, ResponseFormat> parseToGroup(String groupJson) {
+ try {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ GroupDefinition groupDefinition = gson.fromJson(groupJson, GroupDefinition.class);
+ return Either.left(groupDefinition);
+ } catch (Exception e) {
+ log.debug("Failed to parse json (group data) to GroupDefinition object");
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return Either.right(responseFormat);
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/InputsServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/InputsServlet.java
new file mode 100644
index 0000000000..e835c43e14
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/InputsServlet.java
@@ -0,0 +1,320 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.List;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.InputsBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ComponentInstInputsMap;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Input Catalog", description = "Input Servlet")
+@Singleton
+public class InputsServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ProductServlet.class.getName());
+
+ @GET
+ @Path("/services/{componentId}/inputs")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Inputs only", httpMethod = "GET", notes = "Returns Inputs list", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getComponentInputs(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @Context final HttpServletRequest request, @QueryParam("fromId") String fromName,
+ @QueryParam("amount") int amount, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+
+ InputsBusinessLogic businessLogic = getInputBL(context);
+
+ Either<List<InputDefinition>, ResponseFormat> inputsResponse = businessLogic.getInputs(userId, componentId, fromName, amount);
+ if (inputsResponse.isRight()) {
+ log.debug("failed to get inputs {}", componentType);
+ return buildErrorResponse(inputsResponse.right().value());
+ }
+ Object inputs = RepresentationUtils.toRepresentation(inputsResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), inputs);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Component Instance Inputs" + componentType);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Inputs" + componentType);
+ log.debug("getInputs failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/{componentType}/{componentId}/componentInstances/{instanceId}/{originComonentUid}/inputs")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get Inputs only", httpMethod = "GET", notes = "Returns Inputs list", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getComponentInstanceInputs(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @PathParam("instanceId") final String instanceId,
+ @PathParam("originComonentUid") final String originComonentUid, @Context final HttpServletRequest request, @QueryParam("fromName") String fromName, @QueryParam("amount") int amount,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+ InputsBusinessLogic businessLogic = getInputBL(context);
+
+ Either<List<InputDefinition>, ResponseFormat> inputsResponse = businessLogic.getInputs(userId, originComonentUid, fromName, amount);
+ if (inputsResponse.isRight()) {
+ log.debug("failed to get component instance inputs {}", componentType);
+ return buildErrorResponse(inputsResponse.right().value());
+ }
+ Object inputs = RepresentationUtils.toRepresentation(inputsResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), inputs);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Component Instance Inputs" + componentType);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Inputs" + componentType);
+ log.debug("getInputs failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/{componentType}/{componentId}/componentInstances/{instanceId}/{inputId}/properties")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get properties", httpMethod = "GET", notes = "Returns properties list", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getInputPropertiesForComponentInstance(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @PathParam("instanceId") final String instanceId,
+ @PathParam("inputId") final String inputId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+ InputsBusinessLogic businessLogic = getInputBL(context);
+
+ Either<List<ComponentInstanceProperty>, ResponseFormat> inputPropertiesRes = businessLogic.getComponentInstancePropertiesByInputId(userId, instanceId, inputId);
+ if (inputPropertiesRes.isRight()) {
+ log.debug("failed to get properties of input: {}, with instance id: {}", inputId, instanceId);
+ return buildErrorResponse(inputPropertiesRes.right().value());
+ }
+ Object properties = RepresentationUtils.toRepresentation(inputPropertiesRes.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get properties by input id {}, for component instance {} ", inputId, instanceId);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Properites by input id: " + inputId + " for instance with id: " + instanceId);
+ log.debug("getInputPropertiesForComponentInstance failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/{componentType}/{componentId}/inputs/{inputId}/inputs")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get inputs", httpMethod = "GET", notes = "Returns inputs list", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response getInputsForComponentInput(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @PathParam("inputId") final String inputId, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+ InputsBusinessLogic businessLogic = getInputBL(context);
+
+ Either<List<ComponentInstanceInput>, ResponseFormat> inputsRes = businessLogic.getInputsForComponentInput(userId, componentId, inputId);
+ if (inputsRes.isRight()) {
+ log.debug("failed to get inputs of input: {}, with instance id: {}", inputId, componentId);
+ return buildErrorResponse(inputsRes.right().value());
+ }
+ Object properties = RepresentationUtils.toRepresentation(inputsRes.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get inputs by input id {}, for component {} ", inputId, componentId);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get inputs by input id: " + inputId + " for component with id: " + componentId);
+ log.debug("getInputsForComponentInput failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ public Either<ComponentInstInputsMap, ResponseFormat> parseToComponentInstanceMap(String serviceJson, User user) {
+ return getComponentsUtils().convertJsonToObjectUsingObjectMapper(serviceJson, user, ComponentInstInputsMap.class, AuditingActionEnum.CREATE_RESOURCE, ComponentTypeEnum.SERVICE);
+ }
+
+ @POST
+ @Path("/{componentType}/{componentId}/create/inputs")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create inputs on service", httpMethod = "POST", notes = "Return inputs list", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Component found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Component not found") })
+ public Response createMultipleInputs(@PathParam("componentType") final String componentType, @PathParam("componentId") final String componentId, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @ApiParam(value = "ComponentIns Inputs Object to be created", required = true) String componentInstInputsMapObj) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+ InputsBusinessLogic businessLogic = getInputBL(context);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Either<ComponentInstInputsMap, ResponseFormat> componentInstInputsMapRes = parseToComponentInstanceMap(componentInstInputsMapObj, modifier);
+ if (componentInstInputsMapRes.isRight()) {
+ log.debug("failed to parse componentInstInputsMap");
+ response = buildErrorResponse(componentInstInputsMapRes.right().value());
+ return response;
+ }
+
+ ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(componentType);
+ ComponentInstInputsMap componentInstInputsMap = componentInstInputsMapRes.left().value();
+
+ Either<List<InputDefinition>, ResponseFormat> inputPropertiesRes = businessLogic.createMultipleInputs(userId, componentId, componentTypeEnum, componentInstInputsMap, true, false);
+ if (inputPropertiesRes.isRight()) {
+ log.debug("failed to create inputs for service: {}", componentId);
+ return buildErrorResponse(inputPropertiesRes.right().value());
+ }
+ Object properties = RepresentationUtils.toRepresentation(inputPropertiesRes.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), properties);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create inputs for service with id: {}", componentId);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create inputs for service with id: " + componentId);
+ log.debug("createMultipleInputs failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @DELETE
+ @Path("/{componentType}/{componentId}/delete/{inputId}/input")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Delete input from service", httpMethod = "DELETE", notes = "Delete service input", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Input deleted"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Input not found") })
+ public Response deleteInput (
+ @PathParam("componentType") final String componentType,
+ @PathParam("componentId") final String componentId,
+ @PathParam("inputId") final String inputId,
+ @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+ @ApiParam(value = "Service Input to be deleted", required = true) String componentInstInputsMapObj) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+ InputsBusinessLogic businessLogic = getInputBL(context);
+ Either<InputDefinition, ResponseFormat> deleteInput = businessLogic.deleteInput(componentType, componentId, userId, inputId, true);
+ if (deleteInput.isRight()){
+ ResponseFormat deleteResponseFormat = deleteInput.right().value();
+ response = buildErrorResponse(deleteResponseFormat);
+ return response;
+ }
+ InputDefinition inputDefinition = deleteInput.left().value();
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), inputDefinition);
+ } catch (Exception e){
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete input for service {} with id: {}", componentId, inputId);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete input for service + " + componentId + " + with id: " + inputId);
+ log.debug("Delete input failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ protected InputsBusinessLogic getInputBL(ServletContext context) {
+
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ InputsBusinessLogic inputsBusinessLogic = webApplicationContext.getBean(InputsBusinessLogic.class);
+ return inputsBusinessLogic;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java
new file mode 100644
index 0000000000..3f8bfd9149
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/LifecycleServlet.java
@@ -0,0 +1,216 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoBase;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Lifecycle Actions Servlet", description = "Lifecycle Actions Servlet")
+@Singleton
+public class LifecycleServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(LifecycleServlet.class.getName());
+
+ @POST
+ @Path("/{componentCollection}/{componentId}/lifecycleState/{lifecycleOperation}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Change Resource lifecycle State", httpMethod = "POST", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource state changed"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 409, message = "Resource already exist") })
+ public Response changeResourceState(@ApiParam(value = "LifecycleChangeInfo - relevant for checkin, failCertification, cancelCertification", required = false) String jsonChangeInfo,
+ @ApiParam(value = "validValues: resources / services / products", allowableValues = ComponentTypeEnum.RESOURCE_PARAM_NAME + "," + ComponentTypeEnum.SERVICE_PARAM_NAME + ","
+ + ComponentTypeEnum.PRODUCT_PARAM_NAME) @PathParam(value = "componentCollection") final String componentCollection,
+ @ApiParam(allowableValues = "checkout, undoCheckout, checkin, certificationRequest, startCertification, failCertification, cancelCertification, certify", required = true) @PathParam(value = "lifecycleOperation") final String lifecycleTransition,
+ @ApiParam(value = "id of component to be changed") @PathParam(value = "componentId") final String componentId, @Context final HttpServletRequest request,
+ @ApiParam(value = "id of user initiating the operation") @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ LifecycleBusinessLogic businessLogic = getLifecycleBL(context);
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ Response response = null;
+
+ // get modifier from graph
+ log.debug("get modifier properties");
+ Either<User, Response> eitherGetUser = getUser(request, userId);
+ if (eitherGetUser.isRight()) {
+ return eitherGetUser.right().value();
+ }
+ User user = eitherGetUser.left().value();
+
+ String resourceIdLower = componentId.toLowerCase();
+ log.debug("perform {} operation to resource with id {} ", lifecycleTransition, resourceIdLower);
+ Either<LifeCycleTransitionEnum, Response> validateEnum = validateTransitionEnum(lifecycleTransition, user);
+ if (validateEnum.isRight()) {
+ return validateEnum.right().value();
+ }
+
+ LifecycleChangeInfoWithAction changeInfo = new LifecycleChangeInfoWithAction();
+
+ try {
+ if (jsonChangeInfo != null && !jsonChangeInfo.isEmpty()) {
+ // Either<LifecycleChangeInfo, ResponseFormat > changeInfoResult
+ // =
+ // getComponentsUtils().convertJsonToObjectUsingObjectMapper(jsonChangeInfo,
+ // user, LifecycleChangeInfo.class,
+ // AuditingActionEnum.CHECKOUT_RESOURCE, null);
+ ObjectMapper mapper = new ObjectMapper();
+ changeInfo = new LifecycleChangeInfoWithAction(mapper.readValue(jsonChangeInfo, LifecycleChangeInfoBase.class).getUserRemarks());
+ }
+ }
+
+ catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInvalidJsonInput, "convertJsonToObject");
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
+ log.debug("failed to convert from json {}", jsonChangeInfo, e);
+ ResponseFormat responseFormat = getComponentsUtils().getInvalidContentErrorAndAudit(user, AuditingActionEnum.CHECKOUT_RESOURCE);
+ return buildErrorResponse(responseFormat);
+ }
+
+ try {
+ LifeCycleTransitionEnum transitionEnum = validateEnum.left().value();
+ ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(componentCollection);
+ // if (componentType == ComponentTypeEnum.RESOURCE){
+ // Either<Resource, ResponseFormat> actionResponse =
+ // businessLogic.changeState(resourceIdLower, user, transitionEnum,
+ // changeInfo, false);
+ //
+ // if (actionResponse.isRight()){
+ // log.info("failed to change resource state");
+ // response = buildErrorResponse(actionResponse.right().value());
+ // return response;
+ // }
+ //
+ // log.debug("change state successful !!!");
+ // Object resource =
+ // RepresentationUtils.toRepresentation(actionResponse.left().value());
+ // response =
+ // buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK),
+ // resource);
+ // return response;
+ //
+ // } else if (componentType == ComponentTypeEnum.SERVICE ||
+ // componentType == ComponentTypeEnum.PRODUCT){
+ if (componentType != null) {
+ Either<? extends Component, ResponseFormat> actionResponse = businessLogic.changeComponentState(componentType, componentId, user, transitionEnum, changeInfo, false, true);
+
+ if (actionResponse.isRight()) {
+ log.info("failed to change resource state");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ log.debug("change state successful !!!");
+ Object value = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), value);
+ return response;
+ } else {
+ log.info("componentCollection \"{}\" is not valid. Supported componentCollection values are \"{}\", \"{}\" or \"{}\"", componentCollection, ComponentTypeEnum.RESOURCE_PARAM_NAME, ComponentTypeEnum.SERVICE_PARAM_NAME,
+ ComponentTypeEnum.PRODUCT_PARAM_NAME);
+ ResponseFormat error = getComponentsUtils().getInvalidContentErrorAndAudit(user, AuditingActionEnum.CHECKOUT_RESOURCE);
+ return buildErrorResponse(error);
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Change Lifecycle State");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Change Lifecycle State");
+ log.debug("change lifecycle state failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ private Either<LifeCycleTransitionEnum, Response> validateTransitionEnum(final String lifecycleTransition, User user) {
+ LifeCycleTransitionEnum transitionEnum = LifeCycleTransitionEnum.CHECKOUT;
+ try {
+ transitionEnum = LifeCycleTransitionEnum.getFromDisplayName(lifecycleTransition);
+ } catch (IllegalArgumentException e) {
+ log.info("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString());
+ ResponseFormat error = getComponentsUtils().getInvalidContentErrorAndAudit(user, AuditingActionEnum.CHECKOUT_RESOURCE);
+ return Either.right(buildErrorResponse(error));
+ }
+ return Either.left(transitionEnum);
+ }
+
+ private LifecycleBusinessLogic getLifecycleBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ LifecycleBusinessLogic resourceBl = webApplicationContext.getBean(LifecycleBusinessLogic.class);
+ return resourceBl;
+ }
+
+ protected Either<User, Response> getUser(final HttpServletRequest request, String userId) {
+
+ Either<User, ActionStatus> eitherCreator = getUserAdminManager(request.getSession().getServletContext()).getUser(userId, false);
+ if (eitherCreator.isRight()) {
+ log.info("createResource method - user is not listed. userId={}", userId);
+ ResponseFormat errorResponse = getComponentsUtils().getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ User user = new User("", "", userId, "", null, null);
+
+ getComponentsUtils().auditResource(errorResponse, user, null, "", "", AuditingActionEnum.CHECKOUT_RESOURCE, null);
+ return Either.right(buildErrorResponse(errorResponse));
+ }
+ return Either.left(eitherCreator.left().value());
+
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ProductServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ProductServlet.java
new file mode 100644
index 0000000000..f4ac921cb7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ProductServlet.java
@@ -0,0 +1,311 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Product Catalog", description = "Product Servlet")
+@Singleton
+public class ProductServlet extends BeGenericServlet {
+ private static Logger log = LoggerFactory.getLogger(ProductServlet.class.getName());
+
+ @POST
+ @Path("/products")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create product", httpMethod = "POST", notes = "Returns created product", response = Product.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Product created"), @ApiResponse(code = 403, message = "Restricted operation / Empty USER_ID header"), @ApiResponse(code = 400, message = "Invalid/missing content"),
+ @ApiResponse(code = 409, message = "Product already exists / User not found / Wrong user role") })
+ public Response createProduct(@ApiParam(value = "Product object to be created", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) @ApiParam(value = "USER_ID of product strategist user", required = true) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ProductBusinessLogic businessLogic = getProductBL(context);
+ Product product = RepresentationUtils.fromRepresentation(data, Product.class);
+ Either<Product, ResponseFormat> actionResponse = businessLogic.createProduct(product, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("Failed to create product");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Object result = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), result);
+ return response;
+
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create Product");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Product");
+ log.debug("create product failed with error ", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @GET
+ @Path("/products/{productId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve product", httpMethod = "GET", notes = "Returns product according to productId", response = Product.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Product found"), @ApiResponse(code = 403, message = "Missing information"), @ApiResponse(code = 409, message = "Restricted operation"),
+ @ApiResponse(code = 500, message = "Internal Server Error"), @ApiResponse(code = 404, message = "Product not found"), })
+ public Response getProductById(@PathParam("productId") final String productId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ ProductBusinessLogic businessLogic = getProductBL(context);
+ log.trace("get product with id {}", productId);
+ Either<Product, ResponseFormat> actionResponse = businessLogic.getProduct(productId, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("Failed to get product");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Object product = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), product);
+
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Product");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Product");
+ log.debug("get product failed with error ", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @GET
+ @Path("/products/productName/{productName}/productVersion/{productVersion}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Service", httpMethod = "GET", notes = "Returns product according to name and version", response = Product.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Product found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Product not found") })
+ public Response getServiceByNameAndVersion(@PathParam("productName") final String productName, @PathParam("productVersion") final String productVersion, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ProductBusinessLogic businessLogic = getProductBL(context);
+ Either<Product, ResponseFormat> actionResponse = businessLogic.getProductByNameAndVersion(productName, productVersion, userId);
+
+ if (actionResponse.isRight()) {
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Product product = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(product);
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get product by name and version");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get product by name and version");
+ log.debug("get product failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @DELETE
+ @Path("/products/{productId}")
+ public Response deleteProduct(@PathParam("productId") final String productId, @Context final HttpServletRequest request) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ ProductBusinessLogic businessLogic = getProductBL(context);
+ log.trace("delete product with id {}", productId);
+ Either<Product, ResponseFormat> actionResponse = businessLogic.deleteProduct(productId, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("Failed to delete product");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Object product = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), product);
+ return response;
+
+ } catch (Throwable e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Resource");
+ log.debug("delete resource failed with error ", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @PUT
+ @Path("/products/{productId}/metadata")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Product Metadata", httpMethod = "PUT", notes = "Returns updated product", response = Product.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Product Updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateProductMetadata(@PathParam("productId") final String productId, @ApiParam(value = "Product object to be Updated", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+ Response response = null;
+
+ try {
+ String productIdLower = productId.toLowerCase();
+ ProductBusinessLogic businessLogic = getProductBL(context);
+ Product updatedProduct = RepresentationUtils.fromRepresentation(data, Product.class);
+ Either<Product, ResponseFormat> actionResponse = businessLogic.updateProductMetadata(productIdLower, updatedProduct, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to update product");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Product product = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(product);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Product Metadata");
+ log.debug("update product metadata failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @GET
+ @Path("/products/validate-name/{productName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "validate product name", httpMethod = "GET", notes = "checks if the chosen product name is available ", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation") })
+ public Response validateServiceName(@PathParam("productName") final String productName, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+ Response response = null;
+ try {
+ ProductBusinessLogic businessLogic = getProductBL(context);
+
+ Either<Map<String, Boolean>, ResponseFormat> actionResponse = businessLogic.validateProductNameExists(productName, userId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get validate service name");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Validate Service Name");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Validate Product Name");
+ log.debug("validate product name failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/PropertyServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/PropertyServlet.java
new file mode 100644
index 0000000000..2c693c71a8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/PropertyServlet.java
@@ -0,0 +1,562 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.openecomp.sdc.be.components.impl.PropertyBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintSerialiser;
+import org.openecomp.sdc.be.resources.data.EntryData;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Resource Property Servlet", description = "Resource Property Servlet")
+@Singleton
+public class PropertyServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(PropertyServlet.class.getName());
+
+ @POST
+ @Path("resources/{resourceId}/properties")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource Property", httpMethod = "POST", notes = "Returns created resource property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource property created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Resource property already exist") })
+ public Response createProperty(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId, @ApiParam(value = "Resource property to be created", required = true) String data,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+ log.debug("data is {}", data);
+
+ try {
+ // convert json to PropertyDefinition
+ Either<Map<String, PropertyDefinition>, ActionStatus> either = getPropertyModel(data);
+ if (either.isRight()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(either.right().value());
+ return buildErrorResponse(responseFormat);
+ }
+ Map<String, PropertyDefinition> properties = either.left().value();
+ if (properties == null || properties.size() != 1) {
+ log.info("Property conetnt is invalid - {}", data);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return buildErrorResponse(responseFormat);
+ }
+ Entry<String, PropertyDefinition> entry = properties.entrySet().iterator().next();
+ String propertyName = entry.getKey();
+ PropertyDefinition newPropertyDefinition = entry.getValue();
+
+ // create the new property
+ PropertyBusinessLogic businessLogic = getPropertyBL(context);
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> status = businessLogic.createProperty(resourceId, propertyName, newPropertyDefinition, userId);
+ if (status.isRight()) {
+ log.info("Failed to create Property. Reason - ", status.right().value());
+ return buildErrorResponse(status.right().value());
+ }
+ EntryData<String, PropertyDefinition> property = status.left().value();
+ String name = property.getKey();
+ PropertyDefinition propertyDefinition = property.getValue();
+
+ log.debug("Property {} created successfully with id {}", name, propertyDefinition.getUniqueId());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.CREATED);
+ return buildOkResponse(responseFormat, propertyToJson(property));
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create Property");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Property");
+ log.debug("create property failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ @GET
+ @Path("resources/{resourceId}/properties/{propertyId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource Property", httpMethod = "GET", notes = "Returns property of resource", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "property"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 404, message = "Resource property not found") })
+ public Response getProperty(@ApiParam(value = "resource id of property", required = true) @PathParam("resourceId") final String resourceId, @ApiParam(value = "proerty id to get", required = true) @PathParam("propertyId") final String propertyId,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ try {
+
+ //
+ PropertyBusinessLogic businessLogic = getPropertyBL(context);
+ Either<Entry<String, PropertyDefinition>, ResponseFormat> status = businessLogic.getProperty(resourceId, propertyId, userId);
+
+ if (status.isRight()) {
+ log.info("Failed to get Property. Reason - ", status.right().value());
+ return buildErrorResponse(status.right().value());
+ }
+ Entry<String, PropertyDefinition> property = status.left().value();
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ return buildOkResponse(responseFormat, propertyToJson(property));
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Property");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Property");
+ log.debug("get property failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ @DELETE
+ @Path("resources/{resourceId}/properties/{propertyId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource Property", httpMethod = "DELETE", notes = "Returns deleted property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 204, message = "deleted property"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 404, message = "Resource property not found") })
+ public Response deleteProperty(@ApiParam(value = "resource id of property", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "Property id to delete", required = true) @PathParam("propertyId") final String propertyId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ try {
+
+ // delete the property
+ PropertyBusinessLogic businessLogic = getPropertyBL(context);
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> status = businessLogic.deleteProperty(resourceId, propertyId, userId);
+ if (status.isRight()) {
+ log.debug("Failed to delete Property. Reason - ", status.right().value());
+ return buildErrorResponse(status.right().value());
+ }
+ EntryData<String, PropertyDefinition> property = status.left().value();
+ String name = property.getKey();
+ PropertyDefinition propertyDefinition = property.getValue();
+
+ log.debug("Property {} deleted successfully with id {}", name, propertyDefinition.getUniqueId());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT);
+ return buildOkResponse(responseFormat, propertyToJson(property));
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Property");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Property");
+ log.debug("delete property failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ @PUT
+ @Path("resources/{resourceId}/properties/{propertyId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource Property", httpMethod = "PUT", notes = "Returns updated property", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource property updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateProperty(@ApiParam(value = "resource id to update with new property", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "proerty id to update", required = true) @PathParam("propertyId") final String propertyId, @ApiParam(value = "Resource property to update", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ try {
+ // convert json to PropertyDefinition
+ Either<Map<String, PropertyDefinition>, ActionStatus> either = getPropertyModel(data);
+ if (either.isRight()) {
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(either.right().value());
+ return buildErrorResponse(responseFormat);
+ }
+ Map<String, PropertyDefinition> properties = either.left().value();
+ if (properties == null || properties.size() != 1) {
+ log.info("Property conetnt is invalid - {}", data);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return buildErrorResponse(responseFormat);
+ }
+ Entry<String, PropertyDefinition> entry = properties.entrySet().iterator().next();
+ PropertyDefinition newPropertyDefinition = entry.getValue();
+
+ // update property
+ PropertyBusinessLogic businessLogic = getPropertyBL(context);
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> status = businessLogic.updateProperty(resourceId, propertyId, newPropertyDefinition, userId);
+ if (status.isRight()) {
+ log.info("Failed to update Property. Reason - ", status.right().value());
+ return buildErrorResponse(status.right().value());
+ }
+ EntryData<String, PropertyDefinition> property = status.left().value();
+ PropertyDefinition propertyDefinition = property.getValue();
+
+ log.debug("Property id {} updated successfully ", propertyDefinition.getUniqueId());
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ return buildOkResponse(responseFormat, propertyToJson(property));
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Property");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Property");
+ log.debug("update property failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+
+ }
+ }
+
+ // private String propertyToJsonUi(Map.Entry<String, PropertyDefinition>
+ // property) {
+ // String name = property.getKey();
+ // PropertyDefinition propertyDefinition = property.getValue();
+ // JSONObject root = new JSONObject();
+ // root.put("type", propertyDefinition.getType());
+ // root.put("source", propertyDefinition.getDefaultValue());
+ // root.put("name", name);
+ // root.put("description", propertyDefinition.getDescription());
+ // root.put("uniqueId", propertyDefinition.getUniqueId());
+ // return root.toString();
+ //
+ // }
+ //
+
+ // private Either<String ,ActionStatus> getJsonModel(String data){
+ // Either<UiProperty,ActionStatus> uiPropertyEither = getUiProperty(data);
+ // if (uiPropertyEither.isRight()){
+ // log.error("Property conetnt is invalid - {}",data);
+ // return Either.right(ActionStatus.INVALID_CONTENT);
+ // }
+ // UiProperty uiProperty = uiPropertyEither.left().value();
+ // String json = convertUiPropertyToJsonModel(uiProperty);
+ // return Either.left(json);
+ // }
+ //
+
+ // private String convertUiPropertyToJsonModel(UiProperty uiProperty) {
+ // JSONObject root = new JSONObject();
+ // JSONObject propertyD = new JSONObject();
+ // propertyD.put("type",uiProperty.getType());
+ // propertyD.put("required",false);
+ // propertyD.put("description",uiProperty.getDescription());
+ // propertyD.put("isPassword",false);
+ // propertyD.put("defaultValue",uiProperty.getSource());
+ // JSONArray jsonArr = new JSONArray();
+ // JSONArray jsonR = new JSONArray();
+ // jsonR.add(100);
+ // jsonR.add(500);
+ // JSONObject o = new JSONObject();
+ // o.put("inRange", jsonR);
+ // jsonArr.add(o);
+ // propertyD.put("constraints",jsonArr);
+ // root.put(uiProperty.getName(), propertyD);
+ // return root.toString();
+ // }
+
+ private Either<Map<String, PropertyDefinition>, ActionStatus> getPropertyModel(String data) {
+ JSONParser parser = new JSONParser();
+ JSONObject root;
+ try {
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+ root = (JSONObject) parser.parse(data);
+
+ Set entrySet = root.entrySet();
+ Iterator iterator = entrySet.iterator();
+ while (iterator.hasNext()) {
+ Entry next = (Entry) iterator.next();
+ String propertyName = (String) next.getKey();
+ JSONObject value = (JSONObject) next.getValue();
+ String jsonString = value.toJSONString();
+ Either<PropertyDefinition, ActionStatus> convertJsonToObject = convertJsonToObject(jsonString, PropertyDefinition.class);
+ if (convertJsonToObject.isRight()) {
+ return Either.right(convertJsonToObject.right().value());
+ }
+ PropertyDefinition propertyDefinition = convertJsonToObject.left().value();
+ // PropertyDefinition propertyDefinition =
+ // gson.fromJson(jsonString , PropertyDefinition.class);
+ String uniqueId = UniqueIdBuilder.buildPropertyUniqueId("resourceId", (String) propertyName);
+ propertyDefinition.setUniqueId(uniqueId);
+ properties.put(propertyName, propertyDefinition);
+ }
+
+ // Set keySet = root.keySet();
+ // for (Object propertyName : keySet){
+ // JSONObject val = (JSONObject) root.get(propertyName);
+ // String jsonString = val.toJSONString();
+ // Either<PropertyDefinition,ActionStatus> convertJsonToObject =
+ // convertJsonToObject(jsonString, PropertyDefinition.class);
+ // if (convertJsonToObject.isRight()){
+ // return Either.right(convertJsonToObject.right().value());
+ // }
+ // PropertyDefinition propertyDefinition =
+ // convertJsonToObject.left().value();
+ // //PropertyDefinition propertyDefinition =
+ // gson.fromJson(jsonString , PropertyDefinition.class);
+ // String uniqueId =
+ // UniqueIdBuilder.buildPropertyUniqueId("resourceId",
+ // (String)propertyName);
+ // propertyDefinition.setUniqueId(uniqueId);
+ // properties.put((String)propertyName,propertyDefinition);
+ // }
+ return Either.left(properties);
+ } catch (ParseException e) {
+ log.info("Property conetnt is invalid - {}", data);
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ }
+
+ // private Either<Map<String,PropertyDefinition>,ActionStatus>
+ // getProperty(String data){
+ // Type constraintType = new TypeToken<PropertyConstraint>() {}.getType();
+ // PropertyConstraintDeserialiser propertyConstraintDeserialiser = new
+ // PropertyConstraintDeserialiser();
+ // Gson gson = new
+ // GsonBuilder().registerTypeAdapter(constraintType,propertyConstraintDeserialiser).create();
+ // JSONParser parser = new JSONParser();
+ // JSONObject root;
+ // try {
+ // Map<String,PropertyDefinition> properties = new
+ // HashMap<String,PropertyDefinition>();
+ // root = (JSONObject) parser.parse(data);
+ // Set keySet = root.keySet();
+ // for (Object propertyName : keySet){
+ // JSONObject val = (JSONObject) root.get(propertyName);
+ // String jsonString = val.toJSONString();
+ // PropertyDefinition propertyDefinition = gson.fromJson(jsonString ,
+ // PropertyDefinition.class);
+ // String uniqueId = UniqueIdBuilder.buildPropertyUniqueId("resourceId",
+ // (String)propertyName);
+ // propertyDefinition.setUniqueId(uniqueId);
+ // properties.put((String)propertyName,propertyDefinition);
+ // }
+ // return Either.left(properties);
+ // } catch (ParseException e) {
+ // log.error("Property conetnt is invalid - {}",data);
+ // return Either.right(ActionStatus.INVALID_CONTENT);
+ // }
+ // }
+ //
+ // private Either<UiProperty, ActionStatus> getUiProperty(String data) {
+ // try{
+ // Gson gson = new Gson();
+ // UiProperty uiProperty = gson.fromJson(data, UiProperty.class);
+ // return Either.left(uiProperty);
+ // }catch(JsonSyntaxException e){
+ // log.warn("Problem create UiProperty from {}",data);
+ // log.debug("Problem create UiProperty from {}",data,e);
+ // return Either.right(ActionStatus.INVALID_CONTENT);
+ // }
+ // }
+
+ private String propertyToJson(Map.Entry<String, PropertyDefinition> property) {
+ // Type constraintType = new TypeToken<PropertyConstraint>()
+ // {}.getType();
+ // Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new
+ // PropertyConstraintDeserialiser()).create();
+ JSONObject root = new JSONObject();
+ String propertyName = property.getKey();
+ PropertyDefinition propertyDefinition = property.getValue();
+ // String jsonPropertyDefinition = gson.toJson(propertyDefinition);
+ // root.put(propertyName, jsonPropertyDefinition);
+ JSONObject propertyDefinitionO = getPropertyDefinitionJSONObject(propertyDefinition);
+ root.put(propertyName, propertyDefinitionO);
+ propertyDefinition.getType();
+ return root.toString();
+ }
+
+ private JSONObject getPropertyDefinitionJSONObject(PropertyDefinition propertyDefinition) {
+
+ Either<String, ActionStatus> either = convertObjectToJson(propertyDefinition);
+ if (either.isRight()) {
+ return new JSONObject();
+ }
+ String value = either.left().value();
+ try {
+ JSONObject root = (JSONObject) new JSONParser().parse(value);
+ return root;
+ } catch (ParseException e) {
+ log.info("failed to convert input to json");
+ log.debug("failed to convert to json", e);
+ return new JSONObject();
+ }
+
+ }
+
+ // private JSONObject getPropertyDefinitionJSONObject(PropertyDefinition
+ // propertyDefinition) {
+ // JSONObject root = new JSONObject();
+ // root.put("type", propertyDefinition.getType());
+ // root.put("required", propertyDefinition.isRequired());
+ // root.put("defaultValue", propertyDefinition.getDefaultValue());
+ // root.put("description", propertyDefinition.getDescription());
+ // root.put("isPassword", propertyDefinition.isPassword());
+ // List<PropertyConstraint> constraints =
+ // propertyDefinition.getConstraints();
+ // for (PropertyConstraint p : constraints){
+ // p.toString();
+ // }
+ // root.put("constraints", propertyDefinition.getConstraints());
+ // return root;
+ // }
+
+ private <T> Either<T, ActionStatus> convertJsonToObject(String data, Class<T> clazz) {
+ T t = null;
+ Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+ Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
+ try {
+ log.trace("convert json to object. json=\n{}", data);
+ t = gson.fromJson(data, clazz);
+ if (t == null) {
+ log.info("object is null after converting from json");
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ } catch (Exception e) {
+ // INVALID JSON
+ log.info("failed to convert from json");
+ log.debug("failed to convert from json", e);
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ return Either.left(t);
+ }
+
+ private <T> Either<String, ActionStatus> convertObjectToJson(PropertyDefinition propertyDefinition) {
+ Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+ Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintSerialiser()).create();
+ try {
+ log.trace("convert object to json. propertyDefinition={}", propertyDefinition.toString());
+ String json = gson.toJson(propertyDefinition);
+ if (json == null) {
+ log.info("object is null after converting to json");
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ return Either.left(json);
+ } catch (Exception e) {
+ // INVALID JSON
+ log.info("failed to convert to json");
+ log.debug("failed to convert fto json", e);
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+
+ }
+
+ private PropertyBusinessLogic getPropertyBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ PropertyBusinessLogic propertytBl = webApplicationContext.getBean(PropertyBusinessLogic.class);
+ return propertytBl;
+ }
+
+ // private class UiProperty{
+ // String type;
+ // String source;
+ // String name;
+ // String description;
+ // public String getType() {
+ // return type;
+ // }
+ // public void setType(String type) {
+ // this.type = type;
+ // }
+ // public String getSource() {
+ // return source;
+ // }
+ // public void setSource(String source) {
+ // this.source = source;
+ // }
+ // public String getName() {
+ // return name;
+ // }
+ // public void setName(String name) {
+ // this.name = name;
+ // }
+ // public String getDescription() {
+ // return description;
+ // }
+ // public void setDescription(String description) {
+ // this.description = description;
+ // }
+ //
+ // }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RepresentationUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RepresentationUtils.java
new file mode 100644
index 0000000000..565911eaa9
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RepresentationUtils.java
@@ -0,0 +1,124 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.IOException;
+
+import org.codehaus.jackson.JsonGenerationException;
+import org.codehaus.jackson.map.DeserializationConfig;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.SerializationConfig.Feature;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public class RepresentationUtils {
+
+ private static Logger log = LoggerFactory.getLogger(RepresentationUtils.class.getName());
+
+ public static class ResourceRep {
+
+ }
+
+ /**
+ * Build Representation of given Object
+ *
+ * @param elementToRepresent
+ * @return
+ * @throws IOException
+ * @throws JsonGenerationException
+ * @throws JsonMappingException
+ */
+ public static <T> Object toRepresentation(T elementToRepresent) throws IOException, JsonGenerationException, JsonMappingException {
+
+ // return theResource;
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(Feature.FAIL_ON_EMPTY_BEANS, false);
+ mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
+ return mapper.writeValueAsString(elementToRepresent);
+ }
+
+ public static <T> T fromRepresentation(String json, Class<T> clazz) {
+ ObjectMapper mapper = new ObjectMapper();
+ T object = null;
+ mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ mapper.configure(Feature.FAIL_ON_EMPTY_BEANS, false);
+ mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
+ try {
+ object = mapper.readValue(json, clazz);
+ } catch (Exception e) {
+ log.error("Error when parsing JSON of object of type {}", clazz.getSimpleName(), e);
+ } // return null in case of exception
+
+ return object;
+ }
+
+ public static ArtifactDefinition convertJsonToArtifactDefinition(String content, Class<ArtifactDefinition> clazz) {
+
+ JsonObject jsonElement = new JsonObject();
+ ArtifactDefinition resourceInfo = null;
+ try {
+ Gson gson = new Gson();
+ jsonElement = gson.fromJson(content, jsonElement.getClass());
+ JsonElement artifactGroupValue = jsonElement.get(Constants.ARTIFACT_GROUP_TYPE_FIELD);
+ if (artifactGroupValue != null && !artifactGroupValue.isJsonNull()) {
+ String groupValueUpper = artifactGroupValue.getAsString().toUpperCase();
+ if (!ArtifactGroupTypeEnum.getAllTypes().contains(groupValueUpper)) {
+ StringBuilder sb = new StringBuilder();
+ for (String value : ArtifactGroupTypeEnum.getAllTypes()) {
+ sb.append(value).append(", ");
+ }
+ log.debug("artifactGroupType is {}. valid values are: {}", groupValueUpper, sb.toString());
+ return null;
+ } else {
+ jsonElement.remove(Constants.ARTIFACT_GROUP_TYPE_FIELD);
+ jsonElement.addProperty(Constants.ARTIFACT_GROUP_TYPE_FIELD, groupValueUpper);
+ }
+ }
+ String payload = null;
+ JsonElement artifactPayload = jsonElement.get(Constants.ARTIFACT_PAYLOAD_DATA);
+ if (artifactPayload != null && !artifactPayload.isJsonNull()) {
+ payload = artifactPayload.getAsString();
+ }
+ jsonElement.remove(Constants.ARTIFACT_PAYLOAD_DATA);
+ resourceInfo = gson.fromJson(jsonElement, clazz);
+ resourceInfo.setPayloadData(payload);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeArtifactInformationInvalidError, "Artifact Upload / Update");
+ BeEcompErrorManager.getInstance().logBeArtifactInformationInvalidError("Artifact Upload / Update");
+ log.debug("Failed to convert the content {} to object. {}", content.substring(0, Math.min(50, content.length())), e);
+ }
+
+ return resourceInfo;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RequirementsServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RequirementsServlet.java
new file mode 100644
index 0000000000..e853ee4f7f
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/RequirementsServlet.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonSyntaxException;
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+public class RequirementsServlet extends BeGenericServlet {
+
+ private static Logger log = LoggerFactory.getLogger(RequirementsServlet.class.getName());
+
+ @PUT
+ @Path("resources/{resourceId}/requirements/{requirementId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource Requirement", httpMethod = "PUT", notes = "Returns updated requirement", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource requirement updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateRequirement(@ApiParam(value = "resource id to update with new requirement", required = true) @PathParam("resourceId") final String resourceId,
+ @ApiParam(value = "requirement id to update", required = true) @PathParam("requirementId") final String requirementId, @ApiParam(value = "Resource property to update", required = true) String requirementData,
+ @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ // Convert RequirementDefinition from JSON
+ // TODO: it's going to be another object, probably. This is placeholder
+ // for sake of JSON validation
+ // RequirementDefinition requirementDefinition;
+ ResponseFormat responseFormat;
+ try {
+ // requirementDefinition = gson.fromJson(requirementData,
+ // RequirementDefinition.class);
+ // .....
+
+ // TODO pass real entity
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), null);
+ } catch (JsonSyntaxException e) {
+ // INVALID JSON
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT);
+ return buildErrorResponse(responseFormat);
+ } catch (Exception e) {
+ log.debug("Unexpected error: {}", e);
+ responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceArtifactDownloadServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceArtifactDownloadServlet.java
new file mode 100644
index 0000000000..673187b0a1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceArtifactDownloadServlet.java
@@ -0,0 +1,188 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.impl.DownloadArtifactLogic;
+import org.openecomp.sdc.be.info.ArtifactAccessInfo;
+import org.openecomp.sdc.be.resources.api.IResourceUploader;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.jcabi.aspects.Loggable;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog/resources/available")
+public class ResourceArtifactDownloadServlet extends ToscaDaoServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ResourceArtifactDownloadServlet.class.getName());
+
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ // @GET
+ // @Path("/{resourceName}/{resourceVersion}/artifacts")
+ // @Produces(MediaType.APPLICATION_JSON)
+ // public Response getResourceArtifactList(@PathParam("resourceName") final
+ // String resourceName,
+ // @PathParam("resourceVersion") final String resourceVersion,
+ // @Context final HttpServletRequest request){
+ //
+ //
+ // String url = request.getMethod() + " " + request.getRequestURI();
+ // log.info("Start handle request of {}", url);
+ //
+ // Response response = null;
+ //
+ // // get artifact list from dao
+ // IResourceUploader resourceDao =
+ // getResourceUploader(request.getSession().getServletContext());
+ // if (resourceDao == null){
+ // log.error("resource dao cannot be found");
+ // response = buildResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,
+ // "Resource dao cannot be found");
+ // return response;
+ // }
+ // Either<List<ESArtifactData>, ResourceUploadStatus> getArtifactsStatus =
+ // resourceDao.getArtifacts(resourceName, resourceVersion);
+ //
+ // response =
+ // getLogic(request.getSession().getServletContext()).createArtifactListResponse(resourceName,
+ // getArtifactsStatus, getServletPath(request));
+ //
+ // log.info("Finish handle request of {} | result = {}", url, response.getStatus() );
+ // return response;
+ //
+ // }
+
+ @GET
+ @Path("/{resourceName}/{resourceVersion}/artifacts/{artifactName}")
+ // @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ public Response getResourceArtifactByName(@PathParam("resourceName") final String resourceName, @PathParam("resourceVersion") final String resourceVersion, @PathParam("artifactName") final String artifactName,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ Response response = null;
+ try {
+ // get the artifact data
+ String artifactId = String.format(Constants.ARTIFACT_ID_FORMAT, resourceName, resourceVersion, artifactName);
+
+ IResourceUploader resouceUploader = getResourceUploader(request.getSession().getServletContext());
+ if (resouceUploader == null) {
+ return buildResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "");
+
+ }
+ Either<ESArtifactData, ResourceUploadStatus> getArtifactStatus = resouceUploader.getArtifact(artifactId);
+
+ DownloadArtifactLogic logic = getLogic(request.getSession().getServletContext());
+ if (logic == null) {
+ return buildResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "");
+
+ }
+ response = logic.downloadArtifact(resourceName, resourceVersion, artifactName, getArtifactStatus, artifactId);
+
+ log.info("Finish handle request of {} | result = {}", url, response.getStatus());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Resource Artifact By Name");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Resource Artifact By Name");
+ log.debug("getResourceArtifactByName failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @GET
+ @Path("/{resourceName}/{resourceVersion}/artifacts/{artifactName}/metadata")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getResourceArtifactMetadata(@PathParam("resourceName") final String resourceName, @PathParam("resourceVersion") final String resourceVersion, @PathParam("artifactName") final String artifactName,
+ @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ Response response = null;
+ try {
+ IResourceUploader resourceDao = getResourceUploader(request.getSession().getServletContext());
+ if (resourceDao == null) {
+ log.error("resource dao cannot be found");
+ response = buildResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Resource dao cannot be found");
+ return response;
+ }
+
+ String artifactId = String.format(Constants.ARTIFACT_ID_FORMAT, resourceName, resourceVersion, artifactName);
+ Either<ESArtifactData, ResourceUploadStatus> getArtifactStatus = resourceDao.getArtifact(artifactId);
+
+ if (getArtifactStatus.isRight()) {
+ ResourceUploadStatus status = getArtifactStatus.right().value();
+ if (status == ResourceUploadStatus.COMPONENT_NOT_EXIST) {
+ response = Response.status(HttpStatus.SC_NOT_FOUND).build();
+ log.debug("Could not find artifact for with id: {}", artifactId);
+ } else {
+ response = Response.status(HttpStatus.SC_NO_CONTENT).build();
+ log.debug("Could not find artifact for with id: {}", artifactId);
+ }
+ return response;
+ } else {
+ ESArtifactData artifactData = getArtifactStatus.left().value();
+ log.debug("found artifact with id: {}", artifactId);
+ ArtifactAccessInfo artifactInfo = new ArtifactAccessInfo(artifactData);
+ String artifactDataJson = gson.toJson(artifactInfo);
+ response = Response.status(HttpStatus.SC_OK).entity(artifactDataJson).type(MediaType.APPLICATION_JSON_TYPE).build();
+
+ log.info("Finish handle request of {} | result = {}", url, response.getStatus());
+ return response;
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Resource Artifact Metadata");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Resource Artifact Metadata");
+ log.debug("getResourceArtifactMetadata failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+
+ }
+
+ @Override
+ public Logger getLogger() {
+ return log;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java
new file mode 100644
index 0000000000..5cd765abe0
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java
@@ -0,0 +1,180 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.File;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog/upload")
+@Api(value = "Resources Catalog Upload", description = "Upload resource yaml")
+@Singleton
+public class ResourceUploadServlet extends AbstractValidationsServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ResourceUploadServlet.class.getName());
+ public static final String NORMATIVE_TYPE_RESOURCE = "multipart";
+ public static final String USER_TYPE_RESOURCE = "user-resource";
+ public static final String USER_TYPE_RESOURCE_UI_IMPORT = "user-resource-ui-import";
+
+ public enum ResourceAuthorityTypeEnum {
+ NORMATIVE_TYPE_BE(NORMATIVE_TYPE_RESOURCE, true, false), USER_TYPE_BE(USER_TYPE_RESOURCE, true, true), USER_TYPE_UI(USER_TYPE_RESOURCE_UI_IMPORT, false, true);
+
+ private String urlPath;
+ private boolean isBackEndImport, isUserTypeResource;
+
+ public static ResourceAuthorityTypeEnum findByUrlPath(String urlPath) {
+ ResourceAuthorityTypeEnum found = null;
+ for (ResourceAuthorityTypeEnum curr : ResourceAuthorityTypeEnum.values()) {
+ if (curr.getUrlPath().equals(urlPath)) {
+ found = curr;
+ break;
+ }
+ }
+ return found;
+ }
+
+ private ResourceAuthorityTypeEnum(String urlPath, boolean isBackEndImport, boolean isUserTypeResource) {
+ this.urlPath = urlPath;
+ this.isBackEndImport = isBackEndImport;
+ this.isUserTypeResource = isUserTypeResource;
+ }
+
+ public String getUrlPath() {
+ return urlPath;
+ }
+
+ public boolean isBackEndImport() {
+ return isBackEndImport;
+ }
+
+ public boolean isUserTypeResource() {
+ return isUserTypeResource;
+ }
+ }
+
+ @Resource
+ private ResourceImportManager resourceImportManager;
+
+ @POST
+ @Path("/{resourceAuthority}")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource from yaml", httpMethod = "POST", notes = "Returns created resource", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Resource already exist") })
+ public Response uploadMultipart(
+ @ApiParam(value = "validValues: normative-resource / user-resource", allowableValues = NORMATIVE_TYPE_RESOURCE + "," + USER_TYPE_RESOURCE + ","
+ + USER_TYPE_RESOURCE_UI_IMPORT) @PathParam(value = "resourceAuthority") final String resourceAuthority,
+ @ApiParam("FileInputStream") @FormDataParam("resourceZip") File file, @ApiParam("ContentDisposition") @FormDataParam("resourceZip") FormDataContentDisposition contentDispositionHeader,
+ @ApiParam("resourceMetadata") @FormDataParam("resourceMetadata") String resourceInfoJsonString, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+ // updateResourse Query Parameter if false checks if already exist
+ @DefaultValue("true") @QueryParam("createNewVersion") boolean createNewVersion) {
+
+ init(request.getSession().getServletContext());
+ try {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ Wrapper<User> userWrapper = new Wrapper<>();
+ Wrapper<UploadResourceInfo> uploadResourceInfoWrapper = new Wrapper<>();
+ Wrapper<String> yamlStringWrapper = new Wrapper<>();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // When we get an errorResponse it will be filled into the
+ // responseWrapper
+ validateAuthorityType(responseWrapper, resourceAuthority);
+
+ ResourceAuthorityTypeEnum resourceAuthorityEnum = ResourceAuthorityTypeEnum.findByUrlPath(resourceAuthority);
+
+ commonGeneralValidations(responseWrapper, userWrapper, uploadResourceInfoWrapper, resourceAuthorityEnum, userId, resourceInfoJsonString);
+
+ fillPayload(responseWrapper, uploadResourceInfoWrapper, yamlStringWrapper, userWrapper.getInnerElement(), resourceInfoJsonString, resourceAuthorityEnum, file);
+
+ // PayLoad Validations
+ commonPayloadValidations(responseWrapper, yamlStringWrapper, userWrapper.getInnerElement(), uploadResourceInfoWrapper.getInnerElement());
+
+ specificResourceAuthorityValidations(responseWrapper, uploadResourceInfoWrapper, yamlStringWrapper, userWrapper.getInnerElement(), request, resourceInfoJsonString, resourceAuthorityEnum);
+
+ if (responseWrapper.isEmpty()) {
+ handleImport(responseWrapper, userWrapper.getInnerElement(), uploadResourceInfoWrapper.getInnerElement(), yamlStringWrapper.getInnerElement(), resourceAuthorityEnum, createNewVersion, null);
+ }
+
+ return responseWrapper.getInnerElement();
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Upload Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Upload Resource");
+ log.debug("upload resource failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /********************************************************************************************************************/
+
+ private void init(ServletContext context) {
+ init(log);
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ resourceImportManager = webApplicationContext.getBean(ResourceImportManager.class);
+ resourceImportManager.init(context);
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java
new file mode 100644
index 0000000000..e3d39610a8
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourcesServlet.java
@@ -0,0 +1,692 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.json.JSONObject;
+import org.openecomp.sdc.be.components.impl.CsarValidationUtils;
+import org.openecomp.sdc.be.components.impl.ImportUtils;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datamodel.api.HighestFilterEnum;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.ResourceMetadataDefinition;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.servlets.ResourceUploadServlet.ResourceAuthorityTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Resources Catalog", description = "Resources Servlet")
+@Singleton
+public class ResourcesServlet extends AbstractValidationsServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ResourcesServlet.class.getName());
+
+ @POST
+ @Path("/resources")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource", httpMethod = "POST", notes = "Returns created resource", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Resource already exist") })
+ public Response createResource(@ApiParam(value = "Resource object to be created", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ userId = (userId != null) ? userId : request.getHeader(Constants.USER_ID_HEADER);
+ init(log);
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ // UI Import
+ if (isUIImport(data)) {
+ performUIImport(responseWrapper, data, request, userId, null);
+ }
+ // UI Create
+ else {
+
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+
+ Either<Resource, ResponseFormat> convertResponse = parseToResource(data, modifier);
+ if (convertResponse.isRight()) {
+ log.debug("failed to parse resource");
+ response = buildErrorResponse(convertResponse.right().value());
+ return response;
+ }
+
+ Resource resource = convertResponse.left().value();
+ Either<Resource, ResponseFormat> actionResponse = businessLogic.createResource(resource, modifier, null, null);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to create resource");
+ response = buildErrorResponse(actionResponse.right().value());
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), representation);
+ }
+ responseWrapper.setInnerElement(response);
+ }
+
+ return responseWrapper.getInnerElement();
+
+ // return response;
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Resource");
+ log.debug("create resource failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ private boolean isUIImport(String data) {
+ boolean isUIImport;
+ try {
+ JSONObject json = new JSONObject(data);
+ String payloadName = json.getString(ImportUtils.Constants.UI_JSON_PAYLOAD_NAME);
+ isUIImport = payloadName != null && !payloadName.isEmpty();
+ } catch (Exception e) {
+ log.debug("failed to parse json sent from client, json:{}", data);
+ isUIImport = false;
+ }
+ return isUIImport;
+ }
+
+ private void performUIImport(Wrapper<Response> responseWrapper, String data, final HttpServletRequest request, String userId, String resourceUniqueId) throws FileNotFoundException {
+
+ Wrapper<User> userWrapper = new Wrapper<>();
+ Wrapper<UploadResourceInfo> uploadResourceInfoWrapper = new Wrapper<>();
+ Wrapper<String> yamlStringWrapper = new Wrapper<>();
+ String resourceInfoJsonString = data;
+
+ ResourceAuthorityTypeEnum resourceAuthorityEnum = ResourceAuthorityTypeEnum.USER_TYPE_UI;
+
+ commonGeneralValidations(responseWrapper, userWrapper, uploadResourceInfoWrapper, resourceAuthorityEnum, userId, resourceInfoJsonString);
+
+ // TODO suspect next line is unnecessary
+ userWrapper.getInnerElement();
+ if (!CsarValidationUtils.isCsarPayloadName(uploadResourceInfoWrapper.getInnerElement().getPayloadName())) {
+ fillPayload(responseWrapper, uploadResourceInfoWrapper, yamlStringWrapper, userWrapper.getInnerElement(), resourceInfoJsonString, resourceAuthorityEnum, null);
+
+ // PayLoad Validations
+ commonPayloadValidations(responseWrapper, yamlStringWrapper, userWrapper.getInnerElement(), uploadResourceInfoWrapper.getInnerElement());
+ }
+ specificResourceAuthorityValidations(responseWrapper, uploadResourceInfoWrapper, yamlStringWrapper, userWrapper.getInnerElement(), request, resourceInfoJsonString, resourceAuthorityEnum);
+
+ if (responseWrapper.isEmpty()) {
+ handleImport(responseWrapper, userWrapper.getInnerElement(), uploadResourceInfoWrapper.getInnerElement(), yamlStringWrapper.getInnerElement(), resourceAuthorityEnum, true, resourceUniqueId);
+ }
+ }
+
+ public Either<Resource, ResponseFormat> parseToResource(String resourceJson, User user) {
+ return getComponentsUtils().convertJsonToObjectUsingObjectMapper(resourceJson, user, Resource.class, AuditingActionEnum.CREATE_RESOURCE, ComponentTypeEnum.RESOURCE);
+ }
+
+ public Either<Resource, ResponseFormat> parseToLightResource(String resourceJson, User user) {
+ Either<Resource, ResponseFormat> ret = getComponentsUtils().convertJsonToObjectUsingObjectMapper(resourceJson, user, Resource.class, AuditingActionEnum.UPDATE_RESOURCE_METADATA, ComponentTypeEnum.RESOURCE);
+ if (ret.isLeft()) {// drop unwanted data (sent from UI in update flow)
+ ret.left().value().setRequirements(null);
+ ret.left().value().setCapabilities(null);
+ }
+ return ret;
+ }
+
+ @DELETE
+ @Path("/resources/{resourceId}")
+ public Response deleteResource(@PathParam("resourceId") final String resourceId, @Context final HttpServletRequest request) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ String resourceIdLower = resourceId.toLowerCase();
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+ ResponseFormat actionResponse = businessLogic.deleteResource(resourceIdLower, modifier);
+
+ if (actionResponse.getStatus() != HttpStatus.SC_NO_CONTENT) {
+ log.debug("failed to delete resource");
+ response = buildErrorResponse(actionResponse);
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Resource");
+ log.debug("delete resource failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @DELETE
+ @Path("/resources/{resourceName}/{version}")
+ public Response deleteResourceByNameAndVersion(@PathParam("resourceName") final String resourceName, @PathParam("version") final String version, @Context final HttpServletRequest request) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+ ResponseFormat actionResponse = businessLogic.deleteResourceByNameAndVersion(resourceName, version, modifier);
+
+ if (actionResponse.getStatus() != HttpStatus.SC_NO_CONTENT) {
+ log.debug("failed to delete resource");
+ response = buildErrorResponse(actionResponse);
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Resource");
+ log.debug("delete resource failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/resources/{resourceId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Resource", httpMethod = "GET", notes = "Returns resource according to resourceId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Resource not found") })
+ public Response getResourceById(@PathParam("resourceId") final String resourceId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ String resourceIdLower = resourceId.toLowerCase();
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+ log.trace("get resource with id {}", resourceId);
+ Either<Resource, ResponseFormat> actionResponse = businessLogic.getResource(resourceIdLower, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get resource");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ Object resource = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), resource);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Resource");
+ log.debug("get resource failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @GET
+ @Path("/resources/resourceName/{resourceName}/resourceVersion/{resourceVersion}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Resource by name and version", httpMethod = "GET", notes = "Returns resource according to resourceId", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Resource not found") })
+ public Response getResourceByNameAndVersion(@PathParam("resourceName") final String resourceName, @PathParam("resourceVersion") final String resourceVersion, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+ Response response = null;
+ try {
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+ Either<List<Resource>, ResponseFormat> actionResponse = businessLogic.getResourceByNameAndVersion(resourceName, resourceVersion, userId);
+ if (actionResponse.isRight()) {
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ Object resource = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), resource);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Resource by name and version");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Resource by name and version");
+ log.debug("get resource failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @GET
+ @Path("/resources/validate-name/{resourceName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "validate resource name", httpMethod = "GET", notes = "checks if the chosen resource name is available ", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource found"), @ApiResponse(code = 403, message = "Restricted operation") })
+ public Response validateResourceName(@PathParam("resourceName") final String resourceName, @QueryParam("subtype") String resourceType, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+ Response response = null;
+ try {
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+
+ if (resourceType != null && !ResourceTypeEnum.contains(resourceType)) {
+ log.debug("invalid resource type received");
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
+ return response;
+
+ }
+ ResourceTypeEnum typeEnum = null;
+ if (resourceType != null) {
+ typeEnum = ResourceTypeEnum.valueOf(resourceType);
+ }
+ Either<Map<String, Boolean>, ResponseFormat> actionResponse = businessLogic.validateResourceNameExists(resourceName, typeEnum, userId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to validate resource name");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Validate Resource Name");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Validate Resource Name");
+ log.debug("validate resource name failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/resources/certified/abstract")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCertifiedAbstractResources(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ // TODO: any validations???
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+ try {
+
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+
+ Either<List<Resource>, ResponseFormat> actionResponse = businessLogic.getAllCertifiedResources(true, HighestFilterEnum.HIGHEST_ONLY, userId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get all abstract resources");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ Object resources = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), resources);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Certified Abstract Resources");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Certified Abstract Resources");
+ log.debug("getCertifiedAbstractResources failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/resources/certified/notabstract")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCertifiedNotAbstractResources(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ // TODO: any vlidations???
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+ Response response = null;
+
+ try {
+
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+
+ Either<List<Resource>, ResponseFormat> actionResponse = businessLogic.getAllCertifiedResources(false, HighestFilterEnum.ALL, userId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get all non abstract resources");
+ return buildErrorResponse(actionResponse.right().value());
+ }
+ Object resources = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), resources);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Certified Non Abstract Resources");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Certified Non Abstract Resources");
+ log.debug("getCertifiedNotAbstractResources failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+
+ }
+
+ @PUT
+ @Path("/resources/{resourceId}/metadata")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource Metadata", httpMethod = "PUT", notes = "Returns updated resource metadata", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource metadata updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content") })
+ public Response updateResourceMetadata(@PathParam("resourceId") final String resourceId, @ApiParam(value = "Resource metadata to be updated", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+ String resourceIdLower = resourceId.toLowerCase();
+ Either<Resource, ResponseFormat> updateInfoResource = getComponentsUtils().convertJsonToObjectUsingObjectMapper(data, modifier, Resource.class, AuditingActionEnum.UPDATE_RESOURCE_METADATA, ComponentTypeEnum.RESOURCE);
+ if (updateInfoResource.isRight()) {
+ log.debug("failed to parse resource metadata");
+ response = buildErrorResponse(updateInfoResource.right().value());
+ return response;
+ }
+ Either<Resource, ResponseFormat> actionResponse = businessLogic.updateResourceMetadata(resourceIdLower,
+ updateInfoResource.left().value(), null, modifier, false);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to update resource metadata");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ Object resource = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), resource);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Resource Metadata");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Resource Metadata");
+ log.debug("Update Resource Metadata failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @PUT
+ @Path("/resources/{resourceId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Resource", httpMethod = "PUT", notes = "Returns updated resource", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Resource updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Resource already exist") })
+ public Response updateResource(@ApiParam(value = "Resource object to be updated", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId,
+ @PathParam(value = "resourceId") String resourceId) {
+
+ userId = (userId != null) ? userId : request.getHeader(Constants.USER_ID_HEADER);
+ init(log);
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ // UI Import
+ if (isUIImport(data)) {
+ performUIImport(responseWrapper, data, request, userId, resourceId);
+ } else {
+
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+
+ Either<Resource, ResponseFormat> convertResponse = parseToLightResource(data, modifier);
+ if (convertResponse.isRight()) {
+ log.debug("failed to parse resource");
+ response = buildErrorResponse(convertResponse.right().value());
+ return response;
+ }
+
+ Resource resource = convertResponse.left().value();
+ Either<Resource, ResponseFormat> actionResponse = businessLogic.validateAndUpdateResourceFromCsar(resource, modifier, null, null, resourceId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to update resource");
+ response = buildErrorResponse(actionResponse.right().value());
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation);
+ }
+ responseWrapper.setInnerElement(response);
+ }
+
+ return responseWrapper.getInnerElement();
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Resource");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Resource");
+ log.debug("update resource failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ /*
+ * @GET
+ *
+ * @Path("/resources/latestversion/notabstract")
+ *
+ * @Consumes(MediaType.APPLICATION_JSON)
+ *
+ * @Produces(MediaType.APPLICATION_JSON) public Response getLatestVersionNotAbstractResources(@Context final HttpServletRequest request) { //TODO: any vlidations??? ServletContext context = request.getSession().getServletContext();
+ *
+ * String url = request.getMethod() + " " + request.getRequestURI(); log.debug("(get) Start handle request of {}", url); Response response=null;
+ *
+ * try {
+ *
+ * ResourceBusinessLogic businessLogic = getResourceBL(context);
+ *
+ * Either<List<Resource>, ResponseFormat> actionResponse = businessLogic.getLatestVersionResources(false, HighestFilterEnum.HIGHEST_ONLY);
+ *
+ *
+ * if (actionResponse.isRight()){ log.debug( "failed to get all non abstract resources"); return buildErrorResponse(actionResponse.right().value()); } return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK),
+ * actionResponse.left().value());
+ *
+ * } catch (Exception e){ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName. BeRestApiGeneralError, "Get Certified Non Abstract Resources"); log.debug("getCertifiedNotAbstractResources failed with exception", e); response =
+ * buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus. GENERAL_ERROR)); return response;
+ *
+ * } }
+ */
+ public static List<PropertyDefinition> convertMapToList(Map<String, PropertyDefinition> properties) {
+ if (properties == null) {
+ return null;
+ }
+
+ List<PropertyDefinition> definitions = new ArrayList<>();
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+ String name = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ propertyDefinition.setName(name);
+ definitions.add(propertyDefinition);
+ }
+
+ return definitions;
+ }
+
+ @GET
+ @Path("/resources/csar/{csaruuid}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Resource", httpMethod = "POST", notes = "Returns resource created from csar uuid", response = Resource.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Resource retrieced"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response getResourceFromCsar(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @PathParam(value = "csaruuid") String csarUUID) {
+
+ init(log);
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // retrieve user details
+ userId = (userId != null) ? userId : request.getHeader(Constants.USER_ID_HEADER);
+ User user = new User();
+ user.setUserId(userId);
+
+ log.debug("user id is {}", userId);
+
+ Response response = null;
+
+ try {
+
+ ResourceBusinessLogic businessLogic = getResourceBL(context);
+
+ Either<Resource, ResponseFormat> eitherResource = businessLogic.getLatestResourceFromCsarUuid(csarUUID, user);
+
+ // validate response
+ if (eitherResource.isRight()) {
+ log.debug("failed to get resource from csarUuid : {}", csarUUID);
+ // response =
+ // buildErrorResponse(eitherResource.right().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), eitherResource.right().value());
+ } else {
+ Object representation = RepresentationUtils.toRepresentation(eitherResource.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), representation);
+ }
+
+ return response;
+
+ } catch (Exception e) {
+ log.debug("get resource by csar failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ServiceServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ServiceServlet.java
new file mode 100644
index 0000000000..7e7068f5d7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ServiceServlet.java
@@ -0,0 +1,728 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Service Catalog", description = "Service Servlet")
+@Singleton
+public class ServiceServlet extends AbstractValidationsServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ServiceServlet.class.getName());
+
+ @POST
+ @Path("/services")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Service", httpMethod = "POST", notes = "Returns created service", response = Service.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Service created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Service already exist") })
+ public Response createService(@ApiParam(value = "Service object to be created", required = true) String data, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ Either<Service, ResponseFormat> convertResponse = parseToService(data, modifier);
+ if (convertResponse.isRight()) {
+ log.debug("failed to parse service");
+ response = buildErrorResponse(convertResponse.right().value());
+ return response;
+ }
+
+ Service service = convertResponse.left().value();
+ Either<Service, ResponseFormat> actionResponse = businessLogic.createService(service, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("Failed to create service");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Object result = RepresentationUtils.toRepresentation(actionResponse.left().value());
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), result);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Create Service");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create Service");
+ log.debug("create service failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ public Either<Service, ResponseFormat> parseToService(String serviceJson, User user) {
+ return getComponentsUtils().convertJsonToObjectUsingObjectMapper(serviceJson, user, Service.class, AuditingActionEnum.CREATE_RESOURCE, ComponentTypeEnum.SERVICE);
+ }
+
+ @GET
+ @Path("/services/validate-name/{serviceName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "validate service name", httpMethod = "GET", notes = "checks if the chosen service name is available ", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation") })
+ public Response validateServiceName(@PathParam("serviceName") final String serviceName, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+
+ Either<Map<String, Boolean>, ResponseFormat> actionResponse = businessLogic.validateServiceNameExists(serviceName, userId);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get validate service name");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), actionResponse.left().value());
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Validate Service Name");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Validate Service Name");
+ log.debug("validate service name failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/audit-records/{componentType}/{componentUniqueId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "get component audit records", httpMethod = "GET", notes = "get audit records for a service or a resource", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation") })
+ public Response getComponentAuditRecords(@PathParam("componentType") final String componentType, @PathParam("componentUniqueId") final String componentUniqueId, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+ init(log);
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+ Wrapper<Response> responseWrapper = new Wrapper<Response>();
+ Wrapper<String> uuidWrapper = new Wrapper<String>();
+ Wrapper<String> versionWrapper = new Wrapper<String>();
+ Wrapper<User> userWrapper = new Wrapper<User>();
+ Wrapper<ComponentTypeEnum> componentWrapper = new Wrapper<ComponentTypeEnum>();
+ try {
+ validateUserExist(responseWrapper, userWrapper, userId);
+
+ if (responseWrapper.isEmpty()) {
+ validateComponentType(responseWrapper, componentWrapper, componentType);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ fillUUIDAndVersion(responseWrapper, uuidWrapper, versionWrapper, userWrapper.getInnerElement(), componentWrapper.getInnerElement(), componentUniqueId, context);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ Either<List<Map<String, Object>>, ResponseFormat> eitherServiceAudit = getServiceBL(context).getComponentAuditRecords(versionWrapper.getInnerElement(), uuidWrapper.getInnerElement(), userId);
+
+ if (eitherServiceAudit.isRight()) {
+ Response errorResponse = buildErrorResponse(eitherServiceAudit.right().value());
+ responseWrapper.setInnerElement(errorResponse);
+ } else {
+ List<Map<String, Object>> auditRecords = eitherServiceAudit.left().value();
+ Response okResponse = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), auditRecords);
+ responseWrapper.setInnerElement(okResponse);
+
+ }
+ }
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Validate Service Name");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Validate Service Name");
+ log.debug("get Service Audit Records failed with exception", e);
+ Response errorResponse = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ responseWrapper.setInnerElement(errorResponse);
+ }
+ return responseWrapper.getInnerElement();
+ }
+
+ private void fillUUIDAndVersion(Wrapper<Response> responseWrapper, Wrapper<String> uuidWrapper, Wrapper<String> versionWrapper, User user, final ComponentTypeEnum componentTypeEnum, final String componentUniqueId, ServletContext context) {
+
+ if (componentTypeEnum == ComponentTypeEnum.RESOURCE) {
+ Either<Resource, ResponseFormat> eitherResource = getResourceBL(context).getResource(componentUniqueId, user);
+ if (eitherResource.isLeft()) {
+ uuidWrapper.setInnerElement(eitherResource.left().value().getUUID());
+ versionWrapper.setInnerElement(eitherResource.left().value().getVersion());
+ } else {
+ responseWrapper.setInnerElement(buildErrorResponse(eitherResource.right().value()));
+ }
+
+ } else {
+ Either<Service, ResponseFormat> eitherService = getServiceBL(context).getService(componentUniqueId, user);
+ if (eitherService.isLeft()) {
+ uuidWrapper.setInnerElement(eitherService.left().value().getUUID());
+ versionWrapper.setInnerElement(eitherService.left().value().getVersion());
+ } else {
+ responseWrapper.setInnerElement(buildErrorResponse(eitherService.right().value()));
+
+ }
+ }
+ }
+
+ @DELETE
+ @Path("/services/{serviceId}")
+ public Response deleteService(@PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ String serviceIdLower = serviceId.toLowerCase();
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ ResponseFormat actionResponse = businessLogic.deleteService(serviceIdLower, modifier);
+
+ if (actionResponse.getStatus() != HttpStatus.SC_NO_CONTENT) {
+ log.debug("failed to delete service");
+ response = buildErrorResponse(actionResponse);
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Service");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Service");
+ log.debug("delete service failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @DELETE
+ @Path("/services/{serviceName}/{version}")
+ public Response deleteServiceByNameAndVersion(@PathParam("serviceName") final String serviceName, @PathParam("version") final String version, @Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ String userId = request.getHeader(Constants.USER_ID_HEADER);
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ ResponseFormat actionResponse = businessLogic.deleteServiceByNameAndVersion(serviceName, version, modifier);
+
+ if (actionResponse.getStatus() != HttpStatus.SC_NO_CONTENT) {
+ log.debug("failed to delete service");
+ response = buildErrorResponse(actionResponse);
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.NO_CONTENT), null);
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Delete Service");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Delete Service");
+ log.debug("delete service failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @PUT
+ @Path("/services/{serviceId}/metadata")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Service Metadata", httpMethod = "PUT", notes = "Returns updated service", response = Service.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service Updated"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content") })
+ public Response updateServiceMetadata(@PathParam("serviceId") final String serviceId, @ApiParam(value = "Service object to be Updated", required = true) String data, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+
+ try {
+ String serviceIdLower = serviceId.toLowerCase();
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+
+ Either<Service, ResponseFormat> convertResponse = parseToService(data, modifier);
+ if (convertResponse.isRight()) {
+ log.debug("failed to parse service");
+ response = buildErrorResponse(convertResponse.right().value());
+ return response;
+ }
+ Service updatedService = convertResponse.left().value();
+ Either<Service, ResponseFormat> actionResponse = businessLogic.updateServiceMetadata(serviceIdLower, updatedService, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to update service");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Service service = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(service);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Service Metadata");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Service Metadata");
+ log.debug("update service metadata failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ @GET
+ @Path("/services/{serviceId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Service", httpMethod = "GET", notes = "Returns service according to serviceId", response = Service.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Service not found") })
+ public Response getServiceById(@PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ String serviceIdLower = serviceId.toLowerCase();
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ log.debug("get service with id {}", serviceId);
+ Either<Service, ResponseFormat> actionResponse = businessLogic.getService(serviceIdLower, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to get service");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Service service = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(service);
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Service");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Service");
+ log.debug("get service failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @GET
+ @Path("/services/serviceName/{serviceName}/serviceVersion/{serviceVersion}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve Service", httpMethod = "GET", notes = "Returns service according to name and version", response = Service.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service found"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 404, message = "Service not found") })
+ public Response getServiceByNameAndVersion(@PathParam("serviceName") final String serviceName, @PathParam("serviceVersion") final String serviceVersion, @Context final HttpServletRequest request,
+ @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ Either<Service, ResponseFormat> actionResponse = businessLogic.getServiceByNameAndVersion(serviceName, serviceVersion, userId);
+
+ if (actionResponse.isRight()) {
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+
+ Service service = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(service);
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get Service by name and version");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Service by name and version");
+ log.debug("get service failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+
+ }
+ }
+
+ @POST
+ @Path("/services/{serviceId}/distribution-state/{state}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Update Service Distribution State", httpMethod = "POST", notes = "service with the changed distribution status")
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service distribution state changed"), @ApiResponse(code = 409, message = "Restricted operation"), @ApiResponse(code = 403, message = "Service is not available for distribution"),
+ @ApiResponse(code = 400, message = "Invalid content / Missing content"), @ApiResponse(code = 404, message = "Requested service was not found"), @ApiResponse(code = 500, message = "Internal Server Error. Please try again later.") })
+ public Response updateServiceDistributionState(@ApiParam(value = "DistributionChangeInfo - get comment out of body", required = true) LifecycleChangeInfoWithAction jsonChangeInfo, @PathParam("serviceId") final String serviceId,
+ @ApiParam(allowableValues = "approve, reject", required = true) @PathParam("state") final String state, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ Either<Service, ResponseFormat> actionResponse = businessLogic.changeServiceDistributionState(serviceId, state, jsonChangeInfo, modifier);
+
+ if (actionResponse.isRight()) {
+ log.debug("failed to Update Service Distribution State");
+ response = buildErrorResponse(actionResponse.right().value());
+ return response;
+ }
+ Service service = actionResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(service);
+
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update Service Distribution State");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update Service Distribution State");
+ log.debug("updateServiceDistributionState failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @POST
+ @Path("/services/{serviceId}/distribution/{env}/activate")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Activate distribution", httpMethod = "POST", notes = "activate distribution")
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), @ApiResponse(code = 409, message = "Service cannot be distributed due to missing deployment artifacts"), @ApiResponse(code = 404, message = "Requested service was not found"),
+ @ApiResponse(code = 500, message = "Internal Server Error. Please try again later.") })
+ public Response activateDistribution(@PathParam("serviceId") final String serviceId, @PathParam("env") final String env, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ Either<Service, ResponseFormat> distResponse = businessLogic.activateDistribution(serviceId, env, modifier, request);
+
+ if (distResponse.isRight()) {
+ log.debug("failed to activate service distribution");
+ response = buildErrorResponse(distResponse.right().value());
+ return response;
+ }
+ Service service = distResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(service);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Activate Distribution");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Activate Distribution");
+ log.debug("activate distribution failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @POST
+ @Path("/services/{serviceId}/distribution/{did}/markDeployed")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Mark distribution as deployed", httpMethod = "POST", notes = "relevant audit record will be created")
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Service was marked as deployed"), @ApiResponse(code = 409, message = "Restricted operation"), @ApiResponse(code = 403, message = "Service is not available"),
+ @ApiResponse(code = 400, message = "Invalid content / Missing content"), @ApiResponse(code = 404, message = "Requested service was not found"), @ApiResponse(code = 500, message = "Internal Server Error. Please try again later.") })
+ public Response markDistributionAsDeployed(@PathParam("serviceId") final String serviceId, @PathParam("did") final String did, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ Either<Service, ResponseFormat> distResponse = businessLogic.markDistributionAsDeployed(serviceId, did, modifier);
+
+ if (distResponse.isRight()) {
+ log.debug("failed to mark distribution as deployed");
+ response = buildErrorResponse(distResponse.right().value());
+ return response;
+ }
+ Service service = distResponse.left().value();
+ Object result = RepresentationUtils.toRepresentation(service);
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), result);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Mark Distribution As Deployed");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Mark Distribution As Deployed");
+ log.debug("mark distribution as deployed failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @POST
+ @Path("/services/{serviceId}/tempUrlToBeDeleted")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"), @ApiResponse(code = 500, message = "Internal Server Error. Please try again later.") })
+ public Response tempUrlToBeDeleted(@PathParam("serviceId") final String serviceId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userId);
+ log.debug("modifier id is {}", userId);
+
+ Response response = null;
+ try {
+ ServiceBusinessLogic businessLogic = getServiceBL(context);
+ Service service = (businessLogic.getService(serviceId, modifier)).left().value();
+ Either<Service, ResponseFormat> res = (businessLogic.updateDistributionStatusForActivation(service, modifier, DistributionStatusEnum.DISTRIBUTED));
+
+ if (res.isRight()) {
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), null);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "tempUrlToBeDeleted");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("tempUrlToBeDeleted");
+ log.debug("failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+ }
+ }
+
+ @GET
+ @Path("/services/toscatoheat/{artifactName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_OCTET_STREAM)
+ @ApiOperation(value = "Download service artifact", httpMethod = "GET", notes = "Returns downloaded artifact", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Artifact downloaded"), @ApiResponse(code = 401, message = "Authorization required"), @ApiResponse(code = 403, message = "Restricted operation"),
+ @ApiResponse(code = 404, message = "Artifact not found") })
+ public Response downloadServiceArtifact(@PathParam("artifactName") final String artifactName, @Context final HttpServletRequest request) {
+ Response response = null;
+ String instanceIdHeader = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
+ String requestURI = request.getRequestURI();
+
+ try {
+ log.debug("artifact name = {}", artifactName);
+ ServletContext context = request.getSession().getServletContext();
+
+ Either<byte[], ResponseFormat> executeCommand = executeCommand(artifactName);
+
+ if (executeCommand.isRight()) {
+ log.debug("Failed to convert tosca {} to heat", artifactName);
+ ResponseFormat responseFormat = executeCommand.right().value();
+ response = buildErrorResponse(responseFormat);
+ } else {
+ log.debug("Succeed to convert tosca {} to heat", artifactName);
+ byte[] value = executeCommand.left().value();
+ InputStream is = new ByteArrayInputStream(value);
+
+ Map<String, String> headers = new HashMap<>();
+ String heatFileName = null;
+ if (artifactName.indexOf(".") > -1) {
+ heatFileName = artifactName.substring(0, artifactName.indexOf(".")) + ".heat";
+ } else {
+ heatFileName = artifactName + ".heat";
+ }
+ headers.put(Constants.CONTENT_DISPOSITION_HEADER, getContentDispositionValue(heatFileName));
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.OK);
+ response = buildOkResponse(responseFormat, is, headers);
+ }
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "download heat artifact");
+ log.error("download artifact failed with exception", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ private Either<byte[], ResponseFormat> executeCommand(String artifactName) {
+
+ Configuration configuration = ConfigurationManager.getConfigurationManager().getConfiguration();
+ String toscaFilesDir = configuration.getToscaFilesDir();
+ if (toscaFilesDir == null) {
+ toscaFilesDir = "/apps/jetty/base/be/config/tosca";
+ }
+ String heatTranslator = configuration.getHeatTranslatorPath();
+ if (heatTranslator == null) {
+ heatTranslator = "/home/m98835/heat-translator-0.3.0/heat_translator.py";
+ }
+
+ log.debug("toscaFilesDir={}", toscaFilesDir);
+ log.debug("heatTranslator={}", heatTranslator);
+
+ StringBuffer output = new StringBuffer();
+
+ String heatHeader = configuration.getHeatEnvArtifactHeader();
+ String heatFooter = configuration.getHeatEnvArtifactFooter();
+
+ output.append(heatHeader + "\n");
+
+ MessageFormat mf = new MessageFormat("python {0} --template-file={1}/{2} --template-type=tosca");
+
+ log.debug("After creating message format");
+
+ Object[] objArray = { heatTranslator, toscaFilesDir, artifactName };
+ String command = null;
+ try {
+ command = mf.format(objArray);
+ } catch (Exception e) {
+ log.debug("Failed to convert message format", e);
+ }
+
+ log.debug("Going to run command {}", command);
+
+ Process p;
+ try {
+ p = Runtime.getRuntime().exec(command);
+ int waitFor = p.waitFor();
+ log.debug("waitFor = {}", waitFor);
+
+ if (waitFor != 0) {
+ log.error("Failed running the command {}", command);
+ return Either.right(getComponentsUtils().getResponseFormat(ActionStatus.ARTIFACT_NOT_FOUND, artifactName));
+ }
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+
+ String line = "";
+ while ((line = reader.readLine()) != null) {
+ output.append(line + "\n");
+ }
+
+ } catch (Exception e) {
+ log.error("Failed running the command {} {}", command, e);
+ e.printStackTrace();
+ return Either.right(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ output.append(heatFooter);
+
+ return Either.left(output.toString().getBytes());
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ToscaDaoServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ToscaDaoServlet.java
new file mode 100644
index 0000000000..eea2bfdc42
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ToscaDaoServlet.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.impl.DownloadArtifactLogic;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.info.ServletJsonResponse;
+import org.openecomp.sdc.be.resources.api.IResourceUploader;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.springframework.web.context.WebApplicationContext;
+
+public abstract class ToscaDaoServlet extends BeGenericServlet {
+ public abstract Logger getLogger();
+
+ protected IResourceUploader getResourceUploader(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+
+ if (webApplicationContextWrapper == null) {
+ getLogger().error("Failed to get web application context from context.");
+ return null;
+ }
+
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+
+ return webApplicationContext.getBean(IResourceUploader.class);
+
+ }
+
+ // protected IToscaYamlBuilder getToscaYamlBuilder(ServletContext context){
+ // WebAppContextWrapper webApplicationContextWrapper =
+ // (WebAppContextWrapper) context
+ // .getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ //
+ // if (webApplicationContextWrapper == null) {
+ // getLogger().error("Failed to get web application context from context.");
+ // return null;
+ // }
+ //
+ // WebApplicationContext webApplicationContext =
+ // webApplicationContextWrapper
+ // .getWebAppContext(context);
+ //
+ // return webApplicationContext.getBean(IToscaYamlBuilder.class);
+ //
+ // }
+
+ protected DownloadArtifactLogic getLogic(ServletContext context) {
+ DownloadArtifactLogic downloadLogic = (DownloadArtifactLogic) context.getAttribute(Constants.DOWNLOAD_ARTIFACT_LOGIC_ATTR);
+
+ if (downloadLogic == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInitializationError, "DownloadArtifactLogic from context");
+ BeEcompErrorManager.getInstance().logBeInitializationError("DownloadArtifactLogic from context");
+ return null;
+ }
+ return downloadLogic;
+ }
+
+ protected Response buildResponse(int status, String errorMessage) {
+
+ ServletJsonResponse jsonResponse = new ServletJsonResponse();
+ jsonResponse.setDescription(errorMessage);
+ jsonResponse.setSource(Constants.CATALOG_BE);
+
+ Response response = Response.status(status).entity(jsonResponse).build();
+
+ return response;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesFetchServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesFetchServlet.java
new file mode 100644
index 0000000000..0825a25cbe
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesFetchServlet.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.Map;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.components.impl.PropertyBusinessLogic;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog")
+@Api(value = "Types Fetch Servlet", description = "Types Fetch Servlet")
+@Singleton
+public class TypesFetchServlet extends AbstractValidationsServlet {
+
+ private static Logger log = LoggerFactory.getLogger(TypesFetchServlet.class.getName());
+
+ @GET
+ @Path("dataTypes")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Get data types", httpMethod = "GET", notes = "Returns data types", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "datatypes"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 404, message = "Data types not found") })
+ public Response getAllDataTypesServlet(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
+
+ Wrapper<Response> responseWrapper = new Wrapper<Response>();
+ Wrapper<User> userWrapper = new Wrapper<User>();
+ ServletContext context = request.getSession().getServletContext();
+
+ try {
+ init(log);
+ validateUserExist(responseWrapper, userWrapper, userId);
+
+ if (responseWrapper.isEmpty()) {
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ PropertyBusinessLogic businessLogic = getPropertyBL(context);
+ Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = businessLogic.getAllDataTypes();
+
+ if (allDataTypes.isRight()) {
+ log.info("Failed to get all dara types. Reason - ", allDataTypes.right().value());
+ Response errorResponse = buildErrorResponse(allDataTypes.right().value());
+ responseWrapper.setInnerElement(errorResponse);
+
+ // return buildErrorResponse(allDataTypes.right().value());
+ } else {
+ Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
+ Response okResponse = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), dataTypes);
+ responseWrapper.setInnerElement(okResponse);
+ }
+ }
+
+ return responseWrapper.getInnerElement();
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get all data types");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get Property");
+ log.debug("get all data types failed with exception", e);
+ ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(responseFormat);
+ }
+ }
+
+ private PropertyBusinessLogic getPropertyBL(ServletContext context) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ PropertyBusinessLogic propertytBl = webApplicationContext.getBean(PropertyBusinessLogic.class);
+ return propertytBl;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java
new file mode 100644
index 0000000000..6ba8c521f1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java
@@ -0,0 +1,317 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.glassfish.jersey.media.multipart.FormDataParam;
+import org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager;
+import org.openecomp.sdc.be.components.impl.CategoriesImportManager;
+import org.openecomp.sdc.be.components.impl.DataTypeImportManager;
+import org.openecomp.sdc.be.components.impl.GroupTypeImportManager;
+import org.openecomp.sdc.be.components.impl.InterfaceLifecycleTypeImportManager;
+import org.openecomp.sdc.be.components.impl.PolicyTypeImportManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces.ConsumerTwoParam;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/catalog/uploadType")
+@Api(value = "Catalog Types Upload", description = "Upload Type from yaml")
+@Singleton
+public class TypesUploadServlet extends AbstractValidationsServlet {
+ @Resource
+ private CapabilityTypeImportManager capabilityTypeImportManager;
+
+ @Resource
+ private InterfaceLifecycleTypeImportManager interfaceLifecycleTypeImportManager;
+
+ @Resource
+ private CategoriesImportManager categoriesImportManager;
+
+ @Resource
+ private DataTypeImportManager dataTypeImportManager;
+
+ @Resource
+ private GroupTypeImportManager groupTypeImportManager;
+
+ @Resource
+ private PolicyTypeImportManager policyTypeImportManager;
+
+ private static Logger log = LoggerFactory.getLogger(TypesUploadServlet.class.getName());
+
+ @POST
+ @Path("/capability")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Capability Type from yaml", httpMethod = "POST", notes = "Returns created Capability Type", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Capability Type created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Capability Type already exist") })
+ public Response uploadCapabilityType(@ApiParam("FileInputStream") @FormDataParam("capabilityTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
+
+ capabilityTypeImportManager = initElementTypeImportManager(request.getSession().getServletContext(), () -> CapabilityTypeImportManager.class);
+ ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper, () -> capabilityTypeImportManager.createCapabilityTypes(ymlPayload));
+ return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.CapabilityType.name());
+
+ }
+
+ @POST
+ @Path("/interfaceLifecycle")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Interface Lyfecycle Type from yaml", httpMethod = "POST", notes = "Returns created Interface Lifecycle Type", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Interface Lifecycle Type created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Interface Lifecycle Type already exist") })
+ public Response uploadInterfaceLifecycleType(@ApiParam("FileInputStream") @FormDataParam("interfaceLifecycleTypeZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
+
+ interfaceLifecycleTypeImportManager = initElementTypeImportManager(request.getSession().getServletContext(), () -> InterfaceLifecycleTypeImportManager.class);
+ ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper, () -> interfaceLifecycleTypeImportManager.createLifecycleTypes(ymlPayload));
+ return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "Interface Types");
+ }
+
+ @POST
+ @Path("/categories")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Categories from yaml", httpMethod = "POST", notes = "Returns created categories", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Categories created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Category already exist") })
+ public Response uploadCategories(@ApiParam("FileInputStream") @FormDataParam("categoriesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
+
+ categoriesImportManager = initElementTypeImportManager(request.getSession().getServletContext(), () -> CategoriesImportManager.class);
+ ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createElementsType(responseWrapper, () -> categoriesImportManager.createCategories(ymlPayload));
+ return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, "categories");
+
+ }
+
+ @POST
+ @Path("/datatypes")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create Categories from yaml", httpMethod = "POST", notes = "Returns created data types", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "Data types created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "Data types already exist") })
+ public Response uploadDataTypes(@ApiParam("FileInputStream") @FormDataParam("dataTypesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
+
+ dataTypeImportManager = initElementTypeImportManager(request.getSession().getServletContext(), () -> DataTypeImportManager.class);
+ ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createDataTypes(responseWrapper, ymlPayload);
+ return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.DataType.getName());
+
+ }
+
+ @POST
+ @Path("/grouptypes")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create GroupTypes from yaml", httpMethod = "POST", notes = "Returns created group types", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "group types created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "group types already exist") })
+ public Response uploadGroupTypes(@ApiParam("FileInputStream") @FormDataParam("groupTypesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
+
+ groupTypeImportManager = initElementTypeImportManager(request.getSession().getServletContext(), () -> GroupTypeImportManager.class);
+ ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createGroupTypes(responseWrapper, ymlPayload);
+
+ return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.GroupType.getName());
+ }
+
+ @POST
+ @Path("/policytypes")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create PolicyTypes from yaml", httpMethod = "POST", notes = "Returns created policy types", response = Response.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "policy types created"), @ApiResponse(code = 403, message = "Restricted operation"), @ApiResponse(code = 400, message = "Invalid content / Missing content"),
+ @ApiResponse(code = 409, message = "policy types already exist") })
+ public Response uploadPolicyTypes(@ApiParam("FileInputStream") @FormDataParam("policyTypesZip") File file, @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator) {
+
+ policyTypeImportManager = initElementTypeImportManager(request.getSession().getServletContext(), () -> PolicyTypeImportManager.class);
+ ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod = (responseWrapper, ymlPayload) -> createPolicyTypes(responseWrapper, ymlPayload);
+
+ return uploadElementTypeServletLogic(createElementsMethod, file, request, creator, NodeTypeEnum.PolicyType.getName());
+
+ }
+
+ private Response uploadElementTypeServletLogic(ConsumerTwoParam<Wrapper<Response>, String> createElementsMethod, File file, final HttpServletRequest request, String creator, String elementTypeName) {
+ init(log);
+ String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER);
+ try {
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ Wrapper<User> userWrapper = new Wrapper<>();
+ Wrapper<String> yamlStringWrapper = new Wrapper<>();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ validateUserExist(responseWrapper, userWrapper, userId);
+
+ if (responseWrapper.isEmpty()) {
+ validateUserRole(responseWrapper, userWrapper.getInnerElement());
+ }
+
+ if (responseWrapper.isEmpty()) {
+ validateDataNotNull(responseWrapper, file);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ fillZipContents(yamlStringWrapper, file);
+ }
+
+ if (responseWrapper.isEmpty()) {
+ createElementsMethod.accept(responseWrapper, yamlStringWrapper.getInnerElement());
+ }
+
+ return responseWrapper.getInnerElement();
+
+ } catch (Exception e) {
+ log.debug("create {} failed with exception: {}", elementTypeName, e);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create " + elementTypeName);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ private <T> void createElementsType(Wrapper<Response> responseWrapper, Supplier<Either<T, ResponseFormat>> elementsCreater) {
+ Either<T, ResponseFormat> eitherResult = elementsCreater.get();
+ if (eitherResult.isRight()) {
+ Response response = buildErrorResponse(eitherResult.right().value());
+ responseWrapper.setInnerElement(response);
+ } else {
+ Response response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), eitherResult.left().value());
+ responseWrapper.setInnerElement(response);
+ }
+ }
+
+ // data types
+ private void createDataTypes(Wrapper<Response> responseWrapper, String dataTypesYml) {
+ final Supplier<Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> dataTypeImportManager.createDataTypes(dataTypesYml);
+ buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.DATA_TYPE_ALREADY_EXIST, NodeTypeEnum.DataType.name());
+ }
+
+ // group types
+ private void createGroupTypes(Wrapper<Response> responseWrapper, String groupTypesYml) {
+ final Supplier<Either<List<ImmutablePair<GroupTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> groupTypeImportManager.createGroupTypes(groupTypesYml);
+ buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.GROUP_TYPE_ALREADY_EXIST, NodeTypeEnum.GroupType.name());
+
+ }
+
+ // policy types
+ private void createPolicyTypes(Wrapper<Response> responseWrapper, String policyTypesYml) {
+ final Supplier<Either<List<ImmutablePair<PolicyTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml = () -> policyTypeImportManager.createPolicyTypes(policyTypesYml);
+ buildStatusForElementTypeCreate(responseWrapper, generateElementTypeFromYml, ActionStatus.POLICY_TYPE_ALREADY_EXIST, NodeTypeEnum.PolicyType.name());
+
+ }
+
+ // data types
+ private <ElementTypeDefinition> void buildStatusForElementTypeCreate(Wrapper<Response> responseWrapper, Supplier<Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat>> generateElementTypeFromYml,
+ ActionStatus alreadyExistStatus, String elementTypeName) {
+
+ Either<List<ImmutablePair<ElementTypeDefinition, Boolean>>, ResponseFormat> eitherResult = generateElementTypeFromYml.get();
+
+ if (eitherResult.isRight()) {
+ Response response = buildErrorResponse(eitherResult.right().value());
+ responseWrapper.setInnerElement(response);
+ } else {
+ Object representation;
+ try {
+ List<ImmutablePair<ElementTypeDefinition, Boolean>> list = eitherResult.left().value();
+ ActionStatus status = ActionStatus.OK;
+ if (list != null) {
+
+ // Group result by the right value - true or false.
+ // I.e., get the number of data types which are new and
+ // which are old.
+ Map<Boolean, List<ImmutablePair<ElementTypeDefinition, Boolean>>> collect = list.stream().collect(Collectors.groupingBy(ImmutablePair<ElementTypeDefinition, Boolean>::getRight));
+ if (collect != null) {
+ Set<Boolean> keySet = collect.keySet();
+ if (keySet.size() == 1) {
+ Boolean isNew = keySet.iterator().next();
+ if (isNew.booleanValue() == true) {
+ // all data types created at the first time
+ status = ActionStatus.CREATED;
+ } else {
+ // All data types already exists
+
+ status = alreadyExistStatus;
+ }
+ }
+ }
+ }
+ representation = RepresentationUtils.toRepresentation(eitherResult.left().value());
+
+ Response response = buildOkResponse(getComponentsUtils().getResponseFormat(status), representation);
+ responseWrapper.setInnerElement(response);
+
+ } catch (IOException e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Create " + elementTypeName);
+ log.debug("failed to convert {} to json", elementTypeName, e);
+ Response response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ responseWrapper.setInnerElement(response);
+ }
+ }
+ }
+
+ private <T> T initElementTypeImportManager(ServletContext context, Supplier<Class<T>> classGetter) {
+ WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
+ WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
+ T elementTypeImortManager = webApplicationContext.getBean(classGetter.get());
+ return elementTypeImortManager;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/UserAdminServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/UserAdminServlet.java
new file mode 100644
index 0000000000..c700c31a64
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/UserAdminServlet.java
@@ -0,0 +1,516 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Singleton;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.FunctionalMenuInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jcabi.aspects.Loggable;
+import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import com.wordnik.swagger.annotations.ApiParam;
+import com.wordnik.swagger.annotations.ApiResponse;
+import com.wordnik.swagger.annotations.ApiResponses;
+
+import fj.data.Either;
+
+@Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
+@Path("/v1/user")
+@Api(value = "User Administration", description = "User admininstarator operations")
+@Singleton
+public class UserAdminServlet extends BeGenericServlet {
+
+ private static final String ROLE_DELIMITER = ",";
+ private static Logger log = LoggerFactory.getLogger(UserAdminServlet.class.getName());
+
+ /***************************************
+ * API start
+ *************************************************************/
+
+ /* User by userId CRUD start */
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve all user details
+ @GET
+ @Path("/{userId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "retrieve user details", httpMethod = "GET", notes = "Returns user details according to userId", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns user Ok"), @ApiResponse(code = 404, message = "User not found"), @ApiResponse(code = 405, message = "Method Not Allowed"),
+ @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response get(@ApiParam(value = "userId of user to get", required = true) @PathParam("userId") final String userId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+
+ UserBusinessLogic userAdminManager = getUserAdminManager(request.getSession().getServletContext());
+
+ try {
+ Either<User, ActionStatus> either = userAdminManager.getUser(userId, false);
+
+ if (either.isRight()) {
+ return buildErrorResponse(getComponentsUtils().getResponseFormatByUserId(either.right().value(), userId));
+ } else {
+ if (either.left().value() != null) {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), either.left().value());
+ } else {
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get User");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get User");
+ log.debug("get user failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // update user - internal API
+ /*
+ * @POST
+ *
+ * @Path("/{userId}")
+ *
+ * @Consumes(MediaType.APPLICATION_JSON)
+ *
+ * @Produces(MediaType.APPLICATION_JSON)
+ *
+ * @ApiOperation(value = "update user - internal API", notes = "Update user", response = User.class)
+ *
+ * @ApiResponses(value = {
+ *
+ * @ApiResponse(code = 200, message = "Update user OK"),
+ *
+ * @ApiResponse(code = 400, message = "Invalid Content."),
+ *
+ * @ApiResponse(code = 403, message = "Missing information/Restricted operation"),
+ *
+ * @ApiResponse(code = 404, message = "User not found"),
+ *
+ * @ApiResponse(code = 405, message = "Method Not Allowed"),
+ *
+ * @ApiResponse(code = 409, message = "User already exists"),
+ *
+ * @ApiResponse(code = 500, message = "Internal Server Error") }) public Response updateUser(@ApiParam(value="userId of user to get", required=true) @PathParam("userId") final String UserIdUpdateUser,
+ *
+ * @Context final HttpServletRequest request,
+ *
+ * @ApiParam(value="json describe the update user", required=true) String data,
+ *
+ * @HeaderParam(value = Constants.USER_ID_HEADER) String modifierAttId) {
+ *
+ * ServletContext context = request.getSession().getServletContext();
+ *
+ * String url = request.getMethod() + " " + request.getRequestURI(); log.debug("Start handle request of {}", url);
+ *
+ * // get modifier id User modifier = new User(); modifier.setUserId(modifierAttId); log.debug("modifier id is {}", modifierAttId);
+ *
+ * Response response = null;
+ *
+ * try { UserAdminBuisinessLogic businessLogic = getUserAdminManager(context); User updateInfoUser = getComponentsUtils().convertJsonToObject(data, modifier, User.class, AuditingActionEnum.UPDATE_USER).left().value(); Either<User, ResponseFormat>
+ * updateUserResponse = null;// businessLogic.updateUser(modifier, UserIdUpdateUser, updateInfoUser);
+ *
+ * if (updateUserResponse.isRight()) { log.debug("failed to update user metadata"); response = buildErrorResponse(updateUserResponse.right().value()); return response; } response =
+ * buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), updateUserResponse.left().value()); return response;
+ *
+ * } catch (Exception e) { BeEcompErrorManager.getInstance().processEcompError(EcompErrorName. BeRestApiGeneralError, "Update User Metadata"); log.debug("Update User Metadata failed with exception", e); response =
+ * buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus. GENERAL_ERROR)); return response;
+ *
+ * } }
+ *
+ */
+ /* User userId CRUD end */
+
+ /* User role CRUD start */
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // retrieve user role
+ @GET
+ @Path("/{userId}/role")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "retrieve user role", notes = "Returns user role according to userId", response = String.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns user role Ok"), @ApiResponse(code = 404, message = "User not found"), @ApiResponse(code = 405, message = "Method Not Allowed"),
+ @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getRole(@ApiParam(value = "userId of user to get", required = true) @PathParam("userId") final String userId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(getRole) Start handle request of {}", url);
+
+ UserBusinessLogic userAdminManager = getUserAdminManager(request.getSession().getServletContext());
+
+ try {
+ Either<User, ActionStatus> either = userAdminManager.getUser(userId, false);
+ if (either.isRight()) {
+ return buildErrorResponse(getComponentsUtils().getResponseFormatByUserId(either.right().value(), userId));
+ } else {
+ if (either.left().value() != null) {
+ String roleJson = ("{ \"role\" : \"" + either.left().value().getRole().toString() + "\" }");
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), roleJson);
+ } else {
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get User Role");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get User Role");
+ log.debug("Get user role failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // update user role
+ @POST
+ @Path("/{userId}/role")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "update user role", notes = "Update user role", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Update user OK"), @ApiResponse(code = 400, message = "Invalid Content."), @ApiResponse(code = 403, message = "Missing information/Restricted operation"),
+ @ApiResponse(code = 404, message = "User not found"), @ApiResponse(code = 405, message = "Method Not Allowed"), @ApiResponse(code = 409, message = "User already exists"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response updateUserRole(@ApiParam(value = "userId of user to get", required = true) @PathParam("userId") final String UserIdUpdateUser, @Context final HttpServletRequest request,
+ @ApiParam(value = "json describe the update role", required = true) String data, @HeaderParam(value = Constants.USER_ID_HEADER) String modifierUserId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(modifierUserId);
+ log.debug("modifier id is {}", modifierUserId);
+
+ Response response = null;
+
+ try {
+ UserBusinessLogic businessLogic = getUserAdminManager(context);
+ User updateInfoUser = getComponentsUtils().convertJsonToObject(data, modifier, User.class, AuditingActionEnum.UPDATE_USER).left().value();
+ Either<User, ResponseFormat> updateUserResponse = businessLogic.updateUserRole(modifier, UserIdUpdateUser, updateInfoUser.getRole());
+
+ if (updateUserResponse.isRight()) {
+ log.debug("failed to update user role");
+ response = buildErrorResponse(updateUserResponse.right().value());
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), updateUserResponse.left().value());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update User Metadata");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update User Metadata");
+ log.debug("Update User Role failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ /* User role CRUD end */
+
+ /* New user CRUD start */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "add user", httpMethod = "POST", notes = "Provision new user", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 201, message = "New user created"), @ApiResponse(code = 400, message = "Invalid Content."), @ApiResponse(code = 403, message = "Missing information"),
+ @ApiResponse(code = 405, message = "Method Not Allowed"), @ApiResponse(code = 409, message = "User already exists"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response createUser(@Context final HttpServletRequest request, @ApiParam(value = "json describe the user", required = true) String newUserData, @HeaderParam(value = Constants.USER_ID_HEADER) String modifierAttId) {
+
+ ServletContext context = request.getSession().getServletContext();
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ // get modifier id
+ User modifier = new User();
+ modifier.setUserId(modifierAttId);
+ log.debug("modifier id is {}", modifierAttId);
+
+ Response response = null;
+
+ try {
+ UserBusinessLogic businessLogic = getUserAdminManager(context);
+ User newUserInfo = getComponentsUtils().convertJsonToObject(newUserData, modifier, User.class, AuditingActionEnum.ADD_USER).left().value();
+ Either<User, ResponseFormat> createUserResponse = businessLogic.createUser(modifier, newUserInfo);
+
+ if (createUserResponse.isRight()) {
+ log.debug("failed to create user");
+ response = buildErrorResponse(createUserResponse.right().value());
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), createUserResponse.left().value());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Update User Metadata");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Update User Metadata");
+ log.debug("Create User failed with exception", e);
+ response = buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ return response;
+
+ }
+ }
+
+ /* New user CRUD end */
+
+ /* User authorization start */
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // User Authorization
+ @GET
+ @Path("/authorize")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+
+ @ApiOperation(value = "authorize", notes = "authorize user", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns user Ok"), @ApiResponse(code = 403, message = "Restricted Access"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response authorize(@Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, @HeaderParam("HTTP_CSP_FIRSTNAME") String firstName, @HeaderParam("HTTP_CSP_LASTNAME") String lastName,
+ @HeaderParam("HTTP_CSP_EMAIL") String email) {
+
+ try {
+ userId = (userId != null ? URLDecoder.decode(userId, "UTF-8") : null);
+ firstName = (firstName != null ? URLDecoder.decode(firstName, "UTF-8") : null);
+ lastName = (lastName != null ? URLDecoder.decode(lastName, "UTF-8") : null);
+ email = (email != null ? URLDecoder.decode(email, "UTF-8") : null);
+ } catch (UnsupportedEncodingException e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Authorize User - decode headers");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Authorize User - decode headers");
+ ResponseFormat errorResponseWrapper = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return buildErrorResponse(errorResponseWrapper);
+ }
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User authUser = new User();
+ authUser.setUserId(userId);
+ authUser.setFirstName(firstName);
+ authUser.setLastName(lastName);
+ authUser.setEmail(email);
+ log.debug("auth user id is {}", userId);
+
+ Response response = null;
+ try {
+ UserBusinessLogic userAdminManager = getUserAdminManager(context);
+ Either<User, ResponseFormat> authorize = userAdminManager.authorize(authUser);
+
+ if (authorize.isRight()) {
+ log.debug("authorize user failed");
+ response = buildErrorResponse(authorize.right().value());
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), authorize.left().value());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get ASDC users");
+ log.debug("authorize user failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ /* User authorization end */
+
+ @GET
+ @Path("/admins")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "retrieve all administrators", httpMethod = "GET", notes = "Returns all administrators", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns user Ok"), @ApiResponse(code = 405, message = "Method Not Allowed"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getAdminsUser(@PathParam("userId") final String userId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+
+ UserBusinessLogic userAdminManager = getUserAdminManager(request.getSession().getServletContext());
+
+ try {
+ Either<List<User>, ResponseFormat> either = userAdminManager.getAllAdminUsers(request.getSession().getServletContext());
+
+ if (either.isRight()) {
+ log.debug("Failed to get all admin users");
+ return buildErrorResponse(either.right().value());
+ } else {
+ if (either.left().value() != null) {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), either.left().value());
+ } else {
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get All Administrators");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get All Administrators");
+ log.debug("get all admins failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/users")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve the list of all active ASDC users or only group of users having specific roles.", httpMethod = "GET", notes = "Returns list of users with the specified roles, or all of users in the case of empty 'roles' header", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns users Ok"), @ApiResponse(code = 204, message = "No provisioned ASDC users of requested role"), @ApiResponse(code = 403, message = "Restricted Access"),
+ @ApiResponse(code = 400, message = "Missing content"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getUsersList(@Context final HttpServletRequest request, @ApiParam(value = "Any active user's USER_ID") @HeaderParam(Constants.USER_ID_HEADER) final String userId,
+ @ApiParam(value = "TESTER,DESIGNER,PRODUCT_STRATEGIST,OPS,PRODUCT_MANAGER,GOVERNOR, ADMIN OR all users by not typing anything") @QueryParam("roles") final String roles) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+ log.debug("modifier id is {}", userId);
+
+ List<String> rolesList = new ArrayList<>();
+ if (roles != null && !roles.trim().isEmpty()) {
+ String[] rolesArr = roles.split(ROLE_DELIMITER);
+ for (String role : rolesArr) {
+ rolesList.add(role.trim());
+ }
+ }
+
+ try {
+ UserBusinessLogic userAdminManager = getUserAdminManager(context);
+ Either<List<User>, ResponseFormat> either = userAdminManager.getUsersList(userId, rolesList, roles);
+
+ if (either.isRight()) {
+ log.debug("Failed to get ASDC users");
+ return buildErrorResponse(either.right().value());
+ } else {
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), either.left().value());
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get ASDC users");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get ASDC users");
+ log.debug("get users failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////
+ // delete user
+ @DELETE
+ @Path("/{userId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "delete user", notes = "Delete user", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Update deleted OK"), @ApiResponse(code = 400, message = "Invalid Content."), @ApiResponse(code = 403, message = "Missing information"),
+ @ApiResponse(code = 404, message = "User not found"), @ApiResponse(code = 405, message = "Method Not Allowed"), @ApiResponse(code = 409, message = "Restricted operation"), @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response deActivateUser(@ApiParam(value = "userId of user to get", required = true) @PathParam("userId") final String userId, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userIdHeader) {
+
+ ServletContext context = request.getSession().getServletContext();
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("Start handle request of {}", url);
+
+ User modifier = new User();
+ modifier.setUserId(userIdHeader);
+ log.debug("modifier id is {}", userIdHeader);
+
+ Response response = null;
+ try {
+ UserBusinessLogic userAdminManager = getUserAdminManager(context);
+ Either<User, ResponseFormat> deactiveUserResponse = userAdminManager.deActivateUser(modifier, userId);
+
+ if (deactiveUserResponse.isRight()) {
+ log.debug("Failed to deactivate user");
+ response = buildErrorResponse(deactiveUserResponse.right().value());
+ return response;
+ }
+ response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), deactiveUserResponse.left().value());
+ return response;
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeRestApiGeneralError, "Get ASDC users");
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get ASDC users");
+ log.debug("deactivate user failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @GET
+ @Path("/{userId}/functionalmenu")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "retrieve user details", httpMethod = "GET", notes = "Returns user details according to userId", response = User.class)
+ @ApiResponses(value = { @ApiResponse(code = 200, message = "Returns user Ok"), @ApiResponse(code = 404, message = "User not found"), @ApiResponse(code = 405, message = "Method Not Allowed"),
+ @ApiResponse(code = 500, message = "Internal Server Error") })
+ public Response getFunctionalMenu(@ApiParam(value = "userId of user to get", required = true) @PathParam("userId") final String userId, @Context final HttpServletRequest request) {
+
+ String url = request.getMethod() + " " + request.getRequestURI();
+ log.debug("(get) Start handle request of {}", url);
+
+ UserBusinessLogic userAdminManager = getUserAdminManager(request.getSession().getServletContext());
+
+ try {
+ Either<FunctionalMenuInfo, ActionStatus> functionalMenuResp = userAdminManager.getFunctionalMenu(userId);
+
+ if (functionalMenuResp.isRight()) {
+ return buildErrorResponse(getComponentsUtils().getResponseFormatByUserId(functionalMenuResp.right().value(), userId));
+ } else {
+ FunctionalMenuInfo functionalMenuInfo = functionalMenuResp.left().value();
+ if (functionalMenuInfo != null && functionalMenuInfo.getFunctionalMenu() != null) {
+ log.debug("Functional menu fetched is {}", functionalMenuInfo.getFunctionalMenu());
+ return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), functionalMenuInfo.getFunctionalMenu());
+ } else {
+ log.debug("Functional menu is null");
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Get User");
+ log.debug("get user failed with unexpected error: {}", e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/switchover/detector/SwitchoverDetector.java b/catalog-be/src/main/java/org/openecomp/sdc/be/switchover/detector/SwitchoverDetector.java
new file mode 100644
index 0000000000..4027144911
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/switchover/detector/SwitchoverDetector.java
@@ -0,0 +1,315 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.switchover.detector;
+
+import java.net.InetAddress;
+import java.util.Properties;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.apache.commons.codec.binary.Base64;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.SwitchoverDetectorConfig;
+import org.openecomp.sdc.be.dao.rest.HttpRestClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component("switchover-detector")
+public class SwitchoverDetector {
+
+ protected static String SWITCHOVER_DETECTOR_LOG_CONTEXT = "switchover.detector";
+
+ private static Logger switchoverLogger = LoggerFactory.getLogger(SWITCHOVER_DETECTOR_LOG_CONTEXT);
+
+ private SwitchoverDetectorConfig switchoverDetectorConfig;
+
+ private Properties authHeader = null;
+
+ private long detectorInterval = 60;
+
+ private int maxBeQueryAttempts = 3;
+
+ private int maxFeQueryAttempts = 3;
+
+ private Boolean beMatch = null;
+
+ private Boolean feMatch = null;
+
+ private static Logger logger = LoggerFactory.getLogger(SwitchoverDetector.class.getName());
+
+ private volatile String siteMode = SwitchoverDetectorState.UNKNOWN.getState();
+
+ private ScheduledFuture<?> scheduledFuture = null;
+
+ ScheduledExecutorService switchoverDetectorScheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "Switchover-Detector-Task");
+ }
+ });
+
+ SwitchoverDetectorScheduledTask switchoverDetectorScheduledTask = null;
+
+ public enum SwitchoverDetectorState {
+
+ UNKNOWN("unknown"), ACTIVE("active"), STANDBY("standby");
+
+ private String state;
+
+ SwitchoverDetectorState(String state) {
+ this.state = state;
+ }
+
+ public String getState() {
+ return state;
+ }
+ }
+
+ public enum SwitchoverDetectorGroup {
+
+ BE_SET("beSet"), FE_SET("feSet");
+
+ private String group;
+
+ SwitchoverDetectorGroup(String group) {
+ this.group = group;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+ }
+
+ public String getSiteMode() {
+ return siteMode;
+ }
+
+ public void setSiteMode(String mode) {
+ this.siteMode = mode;
+ }
+
+ private Boolean queryBe() {
+ return queryGss(switchoverDetectorConfig.getgBeFqdn(), switchoverDetectorConfig.getBeVip(), maxBeQueryAttempts);
+ }
+
+ private Boolean queryFe() {
+ return queryGss(switchoverDetectorConfig.getgFeFqdn(), switchoverDetectorConfig.getFeVip(), maxFeQueryAttempts);
+ }
+
+ private void setAuthorizationProperties() {
+ String userInfo = switchoverDetectorConfig.getChangePriorityUser() + ":" + switchoverDetectorConfig.getChangePriorityPassword();
+ String auth = "Basic " + new String(new Base64().encode(userInfo.getBytes()));
+ authHeader = new Properties();
+ authHeader.put("Authorization", auth);
+ }
+
+ private void initializeSiteMode() {
+ while (siteMode == SwitchoverDetectorState.UNKNOWN.getState()) {
+
+ beMatch = queryBe();
+ feMatch = queryFe();
+
+ if (beMatch == feMatch && beMatch != null) {
+ if (beMatch) {
+ setSiteMode(SwitchoverDetectorState.ACTIVE.getState());
+ } else {
+ setSiteMode(SwitchoverDetectorState.STANDBY.getState());
+ }
+ }
+ }
+ }
+
+ private Boolean queryGss(String fqdn, String vip, int maxAttempts) {
+
+ Boolean result = null;
+ int attempts = 0;
+
+ while (result == null && (++attempts < maxAttempts)) {
+ try {
+ InetAddress inetAddress = InetAddress.getByName(fqdn);
+ result = inetAddress.getHostAddress().equals(vip);
+
+ } catch (Exception e) {
+ String message = e.getMessage();
+ if (message == null) {
+ message = e.getClass().getName();
+ }
+ switchoverLogger.debug("Error occured during switchover detector query, Result is {}", message);
+ }
+ }
+ if (null == result) {
+ BeEcompErrorManager.getInstance().logFqdnResolveError(SWITCHOVER_DETECTOR_LOG_CONTEXT, "host " + fqdn + " not resolved after " + attempts + " attempts");
+ }
+ return result;
+ }
+
+ public class SwitchoverDetectorScheduledTask implements Runnable {
+
+ public SwitchoverDetectorScheduledTask() {
+
+ }
+
+ @Override
+ public void run() {
+ switchoverLogger.trace("Executing Switchover Detector Task - Start");
+
+ initializeSiteMode();
+
+ Boolean beRes = queryBe();
+ Boolean feRes = queryFe();
+
+ Boolean updateRequired = siteMode == SwitchoverDetectorState.STANDBY.getState() && (beRes || feRes);
+
+ updateSiteModeAndPriority(beRes && feRes, siteMode == SwitchoverDetectorState.STANDBY.getState(), updateRequired);
+
+ beMatch = beRes;
+ feMatch = feRes;
+ }
+
+ ExecutorService switchoverDetectorExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "Switchover-Detector-Thread");
+ }
+ });
+
+ private void updateSiteModeAndPriority(Boolean bothMatch, Boolean previousModeStandby, Boolean updateRequired) {
+ if (bothMatch && previousModeStandby) {
+ logger.trace("Site switch over was done. Site is now in active mode");
+ setSiteMode(SwitchoverDetectorState.ACTIVE.getState());
+ BeEcompErrorManager.getInstance().logSiteSwitchoverInfo(SWITCHOVER_DETECTOR_LOG_CONTEXT, siteMode);
+ } else if (!bothMatch && !previousModeStandby) {
+ logger.trace("Site switch over was done. Site is now in stand-by mode");
+ setSiteMode(SwitchoverDetectorState.STANDBY.getState());
+ BeEcompErrorManager.getInstance().logSiteSwitchoverInfo(SWITCHOVER_DETECTOR_LOG_CONTEXT, siteMode);
+ }
+ if (updateRequired) {
+ changeSitePriority(SwitchoverDetectorGroup.BE_SET.getGroup());
+ changeSitePriority(SwitchoverDetectorGroup.FE_SET.getGroup());
+ publishNetwork();
+ }
+ }
+
+ private void changeSitePriority(String groupToSet) {
+
+ String url = switchoverDetectorConfig.getGroups().get(groupToSet).getChangePriorityUrl();
+ String body = switchoverDetectorConfig.getGroups().get(groupToSet).getChangePriorityBody();
+
+ HttpRestClient httpRestClient = new HttpRestClient();
+
+ try {
+ httpRestClient.doPUT(url, authHeader, body);
+
+ } catch (Exception e) {
+ String message = e.getMessage();
+ if (message == null) {
+ message = e.getClass().getName();
+ }
+ switchoverLogger.error("Error occured during change site priority request, Result is {}", message);
+ }
+
+ }
+
+ private void publishNetwork() {
+
+ String url = switchoverDetectorConfig.getPublishNetworkUrl();
+ String body = switchoverDetectorConfig.getPublishNetworkBody();
+
+ HttpRestClient httpRestClient = new HttpRestClient();
+
+ try {
+ httpRestClient.doPOST(url, authHeader, body);
+
+ } catch (Exception e) {
+ String message = e.getMessage();
+ if (message == null) {
+ message = e.getClass().getName();
+ }
+ switchoverLogger.error("Error occured during publish network request, Result is {}", message);
+ }
+ }
+
+ }
+
+ @PostConstruct
+ private void init() {
+ logger.info("Enter init method of SwitchoverDetector");
+
+ switchoverDetectorConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getSwitchoverDetector();
+
+ if (!switchoverDetectorConfig.getEnabled()) {
+ logger.info("switchover detector service is disabled");
+ return;
+ }
+
+ Long detectorIntervalConfig = switchoverDetectorConfig.getInterval();
+ if (detectorIntervalConfig != null) {
+ detectorInterval = detectorIntervalConfig.longValue();
+ }
+
+ Integer maxAttempts = switchoverDetectorConfig.getBeResolveAttempts();
+ if (maxAttempts != null) {
+ maxBeQueryAttempts = maxAttempts.intValue();
+ }
+ maxAttempts = switchoverDetectorConfig.getFeResolveAttempts();
+ if (maxAttempts != null) {
+ maxFeQueryAttempts = maxAttempts.intValue();
+ }
+
+ setAuthorizationProperties();
+ logger.info("switchover detector service is enabled, interval is {} seconds", detectorInterval);
+
+ this.switchoverDetectorScheduledTask = new SwitchoverDetectorScheduledTask();
+ startSwitchoverDetectorTask();
+ logger.trace("Exit init method of SwitchoverDetector");
+
+ }
+
+ @PreDestroy
+ private void destroy() {
+
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(true);
+ scheduledFuture = null;
+ }
+
+ if (switchoverDetectorScheduler != null) {
+ switchoverDetectorScheduler.shutdown();
+ }
+
+ }
+
+ public void startSwitchoverDetectorTask() {
+ if (this.scheduledFuture == null) {
+ this.scheduledFuture = this.switchoverDetectorScheduler.scheduleAtFixedRate(switchoverDetectorScheduledTask, 0, detectorInterval, TimeUnit.SECONDS);
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ArtifactTypes.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ArtifactTypes.java
new file mode 100644
index 0000000000..85b4493c8e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ArtifactTypes.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.util.List;
+
+import org.openecomp.sdc.generator.data.ArtifactType;
+
+public class ArtifactTypes {
+ private List<ArtifactType> artifactTypes;
+
+ public List<ArtifactType> getArtifactTypes() {
+ return artifactTypes;
+ }
+
+ public void setArtifactTypes(List<ArtifactType> artifactTypes) {
+ this.artifactTypes = artifactTypes;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CSARTool.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CSARTool.java
new file mode 100644
index 0000000000..7fecced358
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CSARTool.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import org.openecomp.sdc.be.model.Component;
+
+public class CSARTool {
+ public static byte[] createCsar(Component component) {
+
+ final String TOSCA_META_VERSION = "1.0";
+ final String CSAR_VERSION = component.getCsarVersion();
+ final String CREATED_BY = component.getCreatorFullName();
+ final String ENTRY_DEFINITIONS = component.getNormalizedName();
+
+ /*
+ * StringBuilder builder = new StringBuilder(); try( FileOutputStream f = new FileOutputStream("test.zip"); ZipOutputStream zip = new ZipOutputStream(new BufferedOutputStream(f)); ){ }
+ */
+
+ return null;
+ }
+
+ public static String createToscaBlock0(String metaFileVersion, String csarVersion, String createdBy, String entryDef) {
+ final String BLOCK_0_TEMPLATE = "TOSCA-Meta-File-Version: %s";
+
+ return null;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java
new file mode 100644
index 0000000000..d1f2557413
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabiltyRequirementConvertor.java
@@ -0,0 +1,261 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
+import org.openecomp.sdc.be.tosca.model.ToscaCapability;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaRequirement;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplateCapability;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class CapabiltyRequirementConvertor {
+ private static CapabiltyRequirementConvertor instance;
+
+ protected CapabiltyRequirementConvertor() {
+
+ }
+
+ public static synchronized CapabiltyRequirementConvertor getInstance() {
+ if (instance == null) {
+ instance = new CapabiltyRequirementConvertor();
+ }
+ return instance;
+ }
+
+ private static Logger log = LoggerFactory.getLogger(CapabiltyRequirementConvertor.class.getName());
+
+ public Either<ToscaNodeTemplate, ToscaError> convertComponentInstanceCapabilties(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes, ToscaNodeTemplate nodeTemplate) {
+
+ Map<String, List<CapabilityDefinition>> capabilitiesInst = componentInstance.getCapabilities();
+ if (capabilitiesInst != null && !capabilitiesInst.isEmpty()) {
+ Map<String, ToscaTemplateCapability> capabilties = new HashMap<>();
+ capabilitiesInst.entrySet().forEach(e -> {
+ List<CapabilityDefinition> capList = e.getValue();
+ if (capList != null && !capList.isEmpty()) {
+ capList.forEach(c -> {
+ convertOverridenProperties(componentInstance, dataTypes, capabilties, c);
+ });
+ }
+ });
+ if (capabilties != null && !capabilties.isEmpty()) {
+ nodeTemplate.setCapabilities(capabilties);
+ }
+ }
+ return Either.left(nodeTemplate);
+ }
+
+ private void convertOverridenProperties(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes, Map<String, ToscaTemplateCapability> capabilties, CapabilityDefinition c) {
+ List<ComponentInstanceProperty> properties = c.getProperties();
+ if (properties != null && !properties.isEmpty()) {
+ properties.stream().filter(p -> (p.getValueUniqueUid() != null)).forEach(p -> {
+ convertOverridenProperty(componentInstance, dataTypes, capabilties, c, p);
+ });
+ }
+ }
+
+ private void convertOverridenProperty(ComponentInstance componentInstance, Map<String, DataTypeDefinition> dataTypes, Map<String, ToscaTemplateCapability> capabilties, CapabilityDefinition c, ComponentInstanceProperty p) {
+ if (log.isDebugEnabled()) {
+ log.debug("Exist overriden property {} for capabity {} with value {}", p.getName(), c.getName(), p.getValue());
+ }
+ ToscaTemplateCapability toscaTemplateCapability = capabilties.get(c.getName());
+ if (toscaTemplateCapability == null) {
+ toscaTemplateCapability = new ToscaTemplateCapability();
+ capabilties.put(c.getName(), toscaTemplateCapability);
+ }
+ Map<String, Object> toscaCapProp = toscaTemplateCapability.getProperties();
+ if (toscaCapProp == null) {
+ toscaCapProp = new HashMap<>();
+ }
+ Object convertedValue = convertInstanceProperty(dataTypes, componentInstance, p);
+ toscaCapProp.put(p.getName(), convertedValue);
+ toscaTemplateCapability.setProperties(toscaCapProp);
+ }
+
+ private Object convertInstanceProperty(Map<String, DataTypeDefinition> dataTypes, ComponentInstance componentInstance, ComponentInstanceProperty prop) {
+ log.debug("Convert property {} for instance {}", prop.getName(), componentInstance.getUniqueId());
+ String propertyType = prop.getType();
+ String innerType = null;
+ if (prop.getSchema() != null && prop.getSchema().getProperty() != null) {
+ innerType = prop.getSchema().getProperty().getType();
+ }
+ Object convertedValue = PropertyConvertor.getInstance().convertToToscaObject(propertyType, prop.getValue(), innerType, dataTypes);
+ return convertedValue;
+ }
+
+ public Either<ToscaNodeType, ToscaError> convertRequirements(Component component, ToscaNodeType nodeType) {
+ List<Map<String, ToscaRequirement>> toscaRequirements = convertRequirementsAsList(component);
+ if (!toscaRequirements.isEmpty()) {
+ nodeType.setRequirements(toscaRequirements);
+ }
+ log.debug("Finish convert Requirements for node type");
+
+ return Either.left(nodeType);
+ }
+
+ public Either<SubstitutionMapping, ToscaError> convertRequirements(Component component, SubstitutionMapping substitutionMapping) {
+ Map<String, ToscaRequirement> toscaRequirements = convertRequirementsAsMap(component);
+ if (!toscaRequirements.isEmpty()) {
+ substitutionMapping.setRequirements(toscaRequirements);
+ }
+ log.debug("Finish convert Requirements for node type");
+
+ return Either.left(substitutionMapping);
+ }
+
+ private List<Map<String, ToscaRequirement>> convertRequirementsAsList(Component component) {
+ Map<String, List<RequirementDefinition>> requirements = component.getRequirements();
+ List<Map<String, ToscaRequirement>> toscaRequirements = new ArrayList<>();
+ if (requirements != null) {
+ boolean isNodeType = ToscaUtils.isNodeType(component);
+ for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
+ entry.getValue().stream().filter(r -> (!isNodeType || (isNodeType && component.getUniqueId().equals(r.getOwnerId())))).forEach(r -> {
+ ImmutablePair<String, ToscaRequirement> pair = convertRequirement(component, isNodeType, r);
+ Map<String, ToscaRequirement> requirement = new HashMap<>();
+
+ requirement.put(pair.left, pair.right);
+ toscaRequirements.add(requirement);
+ });
+
+ log.debug("Finish convert Requirements for node type");
+ }
+ } else {
+ log.debug("No Requirements for node type");
+ }
+ return toscaRequirements;
+ }
+
+ private Map<String, ToscaRequirement> convertRequirementsAsMap(Component component) {
+ Map<String, List<RequirementDefinition>> requirements = component.getRequirements();
+ Map<String, ToscaRequirement> toscaRequirements = new HashMap<>();
+ if (requirements != null) {
+ boolean isNodeType = ToscaUtils.isNodeType(component);
+ for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
+ entry.getValue().stream().filter(r -> (!isNodeType || (isNodeType && component.getUniqueId().equals(r.getOwnerId())))).forEach(r -> {
+ ImmutablePair<String, ToscaRequirement> pair = convertRequirement(component, isNodeType, r);
+ toscaRequirements.put(pair.left, pair.right);
+ });
+
+ log.debug("Finish convert Requirements for node type");
+ }
+ } else {
+ log.debug("No Requirements for node type");
+ }
+ return toscaRequirements;
+ }
+
+ private ImmutablePair<String, ToscaRequirement> convertRequirement(Component component, boolean isNodeType, RequirementDefinition r) {
+ String name = r.getName();
+ if (!isNodeType) {
+ name = r.getOwnerName() + "." + name;
+ }
+ log.debug("the requirement {} belongs to resource {} ", name, component.getUniqueId());
+ ToscaRequirement toscaRequirement = new ToscaRequirement();
+
+ List<Object> occurences = new ArrayList<>();
+ occurences.add(Integer.valueOf(r.getMinOccurrences()));
+ if (r.getMaxOccurrences().equals(RequirementData.MAX_OCCURRENCES)) {
+ occurences.add(r.getMaxOccurrences());
+ } else {
+ occurences.add(Integer.valueOf(r.getMaxOccurrences()));
+ }
+ toscaRequirement.setOccurrences(occurences);
+ // toscaRequirement.setOccurrences(createOcurrencesRange(requirementDefinition.getMinOccurrences(),
+ // requirementDefinition.getMaxOccurrences()));
+ toscaRequirement.setNode(r.getNode());
+ toscaRequirement.setCapability(r.getCapability());
+ toscaRequirement.setRelationship(r.getRelationship());
+
+ ImmutablePair<String, ToscaRequirement> pair = new ImmutablePair<String, ToscaRequirement>(name, toscaRequirement);
+ return pair;
+ }
+
+ public Map<String, ToscaCapability> convertCapabilities(Component component, Map<String, DataTypeDefinition> dataTypes) {
+ Map<String, List<CapabilityDefinition>> capabilities = component.getCapabilities();
+ Map<String, ToscaCapability> toscaCapabilities = new HashMap<>();
+ if (capabilities != null) {
+ boolean isNodeType = ToscaUtils.isNodeType(component);
+ for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
+ entry.getValue().stream().filter(c -> (!isNodeType || (isNodeType && component.getUniqueId().equals(c.getOwnerId())))).forEach(c -> {
+ convertCapabilty(component, toscaCapabilities, isNodeType, c, dataTypes);
+
+ });
+ }
+ } else {
+ log.debug("No Capabilities for node type");
+ }
+
+ return toscaCapabilities;
+ }
+
+ private void convertCapabilty(Component component, Map<String, ToscaCapability> toscaCapabilities, boolean isNodeType, CapabilityDefinition c, Map<String, DataTypeDefinition> dataTypes) {
+ String name = c.getName();
+ if (!isNodeType) {
+ name = c.getOwnerName() + "." + name;
+ }
+ log.debug("the capabilty {} belongs to resource {} ", name, component.getUniqueId());
+ ToscaCapability toscaCapability = new ToscaCapability();
+ toscaCapability.setDescription(c.getDescription());
+ toscaCapability.setType(c.getType());
+
+ List<Object> occurences = new ArrayList<>();
+ occurences.add(Integer.valueOf(c.getMinOccurrences()));
+ if (c.getMaxOccurrences().equals(CapabilityData.MAX_OCCURRENCES)) {
+ occurences.add(c.getMaxOccurrences());
+ } else {
+ occurences.add(Integer.valueOf(c.getMaxOccurrences()));
+ }
+ toscaCapability.setOccurrences(occurences);
+
+ toscaCapability.setValid_source_types(c.getValidSourceTypes());
+ List<ComponentInstanceProperty> properties = c.getProperties();
+ if (properties != null && !properties.isEmpty()) {
+ Map<String, ToscaProperty> toscaProperties = new HashMap<>();
+ for (PropertyDefinition property : properties) {
+ ToscaProperty toscaProperty = PropertyConvertor.getInstance().convertProperty(dataTypes, property, true);
+ toscaProperties.put(property.getName(), toscaProperty);
+ }
+ toscaCapability.setProperties(toscaProperties);
+ }
+ toscaCapabilities.put(name, toscaCapability);
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java
new file mode 100644
index 0000000000..c8aa188e30
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CsarUtils.java
@@ -0,0 +1,649 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.io.IOException;
+import org.apache.commons.codec.binary.Base64;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic.ArtifactOperation;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.ArtifactCassandraDao;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import org.openecomp.sdc.generator.data.Artifact;
+import org.openecomp.sdc.generator.data.ArtifactType;
+import org.openecomp.sdc.generator.data.GenerationData;
+import org.openecomp.sdc.generator.impl.ArtifactGenerationServiceImpl;
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+/**
+ * @author tg851x
+ *
+ */
+@org.springframework.stereotype.Component("csar-utils")
+public class CsarUtils {
+ private static Logger log = LoggerFactory.getLogger(ToscaExportHandler.class.getName());
+
+ @Autowired
+ private ArtifactCassandraDao artifactCassandraDao;
+ @Autowired
+ private ComponentsUtils componentsUtils;
+ @Autowired
+ private ToscaExportHandler toscaExportUtils;
+ @Autowired
+ private ArtifactsBusinessLogic artifactsBusinessLogic;
+ @Autowired
+ protected ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ private ServiceBusinessLogic serviceBusinessLogic;
+
+ private Gson gson = new Gson();
+
+ private static final String DEFINITIONS_PATH = "Definitions/";
+ private static final String ARTIFACTS_PATH = "Artifacts/";
+ private static final String TOSCA_META_PATH_FILE_NAME = "TOSCA-Metadata/TOSCA.meta";
+ private static final String TOSCA_META_VERSION = "1.0";
+ private static final String CSAR_VERSION = "1.1";
+
+ /**
+ *
+ * @param component
+ * @param getFromCS
+ * @param isInCertificationRequest
+ * @param shouldLock
+ * @param inTransaction
+ * @return
+ */
+ public Either<byte[], ResponseFormat> createCsar(Component component, boolean getFromCS, boolean isInCertificationRequest, boolean shouldLock, boolean inTransaction) {
+ return createCsar(component, getFromCS, isInCertificationRequest, false, shouldLock, inTransaction);
+ }
+
+ private Either<byte[], ResponseFormat> createCsar(Component component, boolean getFromCS, boolean isInCertificationRequest, boolean mockGenerator, boolean shouldLock, boolean inTransaction) {
+ final String CREATED_BY = component.getCreatorFullName();
+
+ String fileName;
+ Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
+ ArtifactDefinition artifactDefinition = toscaArtifacts.get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
+ fileName = artifactDefinition.getArtifactName();
+
+ String toscaBlock0 = createToscaBlock0(TOSCA_META_VERSION, CSAR_VERSION, CREATED_BY, fileName);
+
+ byte[] toscaBlock0Byte = toscaBlock0.getBytes();
+
+ Either<byte[], ResponseFormat> generateCsarZipResponse = generateCsarZip(toscaBlock0Byte, component, getFromCS, isInCertificationRequest, mockGenerator, shouldLock, inTransaction);
+
+ if (generateCsarZipResponse.isRight()) {
+ return Either.right(generateCsarZipResponse.right().value());
+ }
+
+ return Either.left(generateCsarZipResponse.left().value());
+ }
+
+ private Either<byte[], ResponseFormat> generateCsarZip(byte[] toscaBlock0Byte, Component component, boolean getFromCS, boolean isInCertificationRequest, boolean mockGenerator, boolean shouldLock, boolean inTransaction) {
+
+ ZipOutputStream zip = null;
+ ByteArrayOutputStream out = null;
+ try {
+ out = new ByteArrayOutputStream();
+ zip = new ZipOutputStream(out);
+
+ zip.putNextEntry(new ZipEntry(TOSCA_META_PATH_FILE_NAME));
+ zip.write(toscaBlock0Byte);
+ Either<ZipOutputStream, ResponseFormat> populateZip = populateZip(component, getFromCS, zip, isInCertificationRequest, mockGenerator, shouldLock, inTransaction);
+ if (populateZip.isRight()) {
+ log.debug("Failed to populate CSAR zip file {}", populateZip.right().value());
+ return Either.right(populateZip.right().value());
+ }
+ zip = populateZip.left().value();
+
+ zip.finish();
+ byte[] byteArray = out.toByteArray();
+
+ return Either.left(byteArray);
+ } catch (IOException e) {
+ log.debug("createCsar failed IOexception", e);
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return Either.right(responseFormat);
+ } finally {
+ try {
+ if (zip != null) {
+ zip.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ } catch (Exception e) {
+ log.error("Failed to close resources ", e);
+ }
+ }
+ }
+
+ private Either<ZipOutputStream, ResponseFormat> populateZip(Component component, boolean getFromCS, ZipOutputStream zip, boolean isInCertificationRequest, boolean mockGenerator, boolean shouldLock, boolean inTransaction) {
+
+ LifecycleStateEnum lifecycleState = component.getLifecycleState();
+ String componentYaml = null;
+ Either<ToscaRepresentation, ToscaError> exportComponent = null;
+ byte[] mainYaml = null;
+ // <file name, esid, component>
+ List<Triple<String, String, Component>> dependencies = null;
+ List<ImmutablePair<Component, byte[]>> generatorInputs = new LinkedList<>();
+
+ String fileName;
+ Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
+ ArtifactDefinition artifactDefinition = toscaArtifacts.get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
+ fileName = artifactDefinition.getArtifactName();
+
+ if (getFromCS || !(lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKIN || lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ String esId = artifactDefinition.getEsId();
+ Either<byte[], ActionStatus> fromCassandra = getFromCassandra(esId);
+ if (fromCassandra.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(fromCassandra.right().value());
+ return Either.right(responseFormat);
+ }
+ mainYaml = fromCassandra.left().value();
+
+ } else {
+ exportComponent = toscaExportUtils.exportComponent(component);
+ if (exportComponent.isRight()) {
+ log.debug("exportComponent failed", exportComponent.right().value());
+ ActionStatus convertedFromToscaError = componentsUtils.convertFromToscaError(exportComponent.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(convertedFromToscaError);
+ return Either.right(responseFormat);
+ }
+ ToscaRepresentation exportResult = exportComponent.left().value();
+ componentYaml = exportResult.getMainYaml();
+ mainYaml = componentYaml.getBytes();
+ dependencies = exportResult.getDependencies();
+ }
+
+ try {
+ zip.putNextEntry(new ZipEntry(DEFINITIONS_PATH + fileName));
+ zip.write(mainYaml);
+
+ generatorInputs.add(new ImmutablePair<Component, byte[]>(component, mainYaml));
+
+ if (dependencies == null) {
+ Either<ToscaTemplate, ToscaError> dependenciesRes = toscaExportUtils.getDependencies(component);
+ if (dependenciesRes.isRight()) {
+ log.debug("Failed to retrieve dependencies for component {}, error {}", component.getUniqueId(), dependenciesRes.right().value());
+ ActionStatus convertFromToscaError = componentsUtils.convertFromToscaError(dependenciesRes.right().value());
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(convertFromToscaError);
+ return Either.right(responseFormat);
+ }
+ dependencies = dependenciesRes.left().value().getDependencies();
+ }
+ if (dependencies != null && !dependencies.isEmpty()) {
+ for (Triple<String, String, Component> d : dependencies) {
+ String esId = d.getMiddle();
+ Component childComponent = d.getRight();
+ fileName = d.getLeft();
+ Either<byte[], ActionStatus> entryData = getEntryData(esId, childComponent);
+
+ if (entryData.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(entryData.right().value());
+ return Either.right(responseFormat);
+ }
+
+ byte[] content = entryData.left().value();
+ zip.putNextEntry(new ZipEntry(DEFINITIONS_PATH + fileName));
+ zip.write(content);
+
+ generatorInputs.add(new ImmutablePair<Component, byte[]>(childComponent, content));
+ }
+ }
+
+ List<ArtifactDefinition> aiiArtifactList = new LinkedList<>();
+ // Artifact Generation
+ if (component.getComponentType() == ComponentTypeEnum.SERVICE && (lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKIN || lifecycleState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ Either<List<ArtifactDefinition>, ResponseFormat> handleAAIArtifacts = handleAAIArtifacts(component, zip, mockGenerator, shouldLock, inTransaction, generatorInputs);
+
+ if (handleAAIArtifacts.isLeft()) {
+ aiiArtifactList = handleAAIArtifacts.left().value();
+ } else {
+ log.debug("AAI Artifacts handling failed");
+ return Either.right(handleAAIArtifacts.right().value());
+ }
+
+ if (isInCertificationRequest) {
+ Either<ActionStatus, ResponseFormat> handleAllAAIArtifactsInDataModel = handleAllAAIArtifactsInDataModel(component, aiiArtifactList, shouldLock, inTransaction);
+
+ if (handleAllAAIArtifactsInDataModel.isRight()) {
+ log.debug("AAI Artifacts handling (create, update, delete) failed");
+ return Either.right(handleAllAAIArtifactsInDataModel.right().value());
+ }
+ }
+
+ }
+
+ // Collecting All Deployment Artifacts
+ Either<ZipOutputStream, ActionStatus> collectAndWriteToScarDeploymentArtifacts = collectAndWriteToScarDeploymentArtifacts(zip, component, aiiArtifactList);
+
+ if (collectAndWriteToScarDeploymentArtifacts.isRight()) {
+ return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ } catch (IOException e) {
+ log.debug("Failed to create CSAR zip for component {}", component.getUniqueId(), e);
+ }
+
+ return Either.left(zip);
+ }
+
+ private Either<ZipOutputStream, ActionStatus> collectAndWriteToScarDeploymentArtifacts(ZipOutputStream zip, Component component, List<ArtifactDefinition> aiiArtifactList) throws IOException {
+
+ Collection<ArtifactDefinition> deploymentArtifactsToAdd = null;
+ Collection<ArtifactDefinition> allArtifactsToAdd = new LinkedList<>();
+
+ if (component.getComponentType() == ComponentTypeEnum.SERVICE) {
+ Either<Service, StorageOperationStatus> getServiceResponse = serviceOperation.getService(component.getUniqueId());
+
+ if (getServiceResponse.isLeft()) {
+ Service service = getServiceResponse.left().value();
+
+ if (!aiiArtifactList.isEmpty()) {
+ deploymentArtifactsToAdd = service.getDeploymentArtifacts().values().stream().filter(e -> e.getGenerated() == null || !e.getGenerated()).collect(Collectors.toList());
+ allArtifactsToAdd.addAll(aiiArtifactList);
+ allArtifactsToAdd.addAll(deploymentArtifactsToAdd);
+ } else {
+ allArtifactsToAdd.addAll(service.getDeploymentArtifacts().values());
+ }
+ }
+ }
+
+ if (!allArtifactsToAdd.isEmpty()) {
+
+ for (ArtifactDefinition deploymentArtifactDefinition : allArtifactsToAdd) {
+ String artifactFileName = deploymentArtifactDefinition.getArtifactName();
+ byte[] payloadData = deploymentArtifactDefinition.getPayloadData();
+
+ if (payloadData == null) {
+ String esId = deploymentArtifactDefinition.getEsId();
+ if (esId != null) {
+ Either<byte[], ActionStatus> fromCassandra = getFromCassandra(esId);
+
+ if (fromCassandra.isRight()) {
+ return Either.right(fromCassandra.right().value());
+ }
+ payloadData = fromCassandra.left().value();
+ } else {
+ log.debug("Artifact {} payload not supplied in ArtifactDefinition and not found in DB", artifactFileName);
+ continue;
+ }
+ }
+
+ byte[] decodedPayload = null;
+
+ if (Base64.isBase64(payloadData)) {
+ // decodedPayload = Base64.getDecoder().decode(payloadData);
+ decodedPayload = Base64.decodeBase64(payloadData);
+ } else {
+ decodedPayload = payloadData;
+ }
+
+ zip.putNextEntry(new ZipEntry(ARTIFACTS_PATH + artifactFileName));
+ zip.write(decodedPayload);
+ }
+ }
+
+ return Either.left(zip);
+ }
+
+ private Either<List<ArtifactDefinition>, ResponseFormat> handleAAIArtifacts(Component component, ZipOutputStream zip, boolean mockGenerator, boolean shouldLock, boolean inTransaction, List<ImmutablePair<Component, byte[]>> generatorInputs) {
+
+ ComponentTypeEnum componentType = component.getComponentType();
+ List<Artifact> generatedArtifacts = null;
+ List<ArtifactDefinition> aaiArtifacts = null;
+
+ if (componentType == ComponentTypeEnum.SERVICE && !generatorInputs.isEmpty()) {
+ List<Artifact> convertedGeneratorInputs = convertToGeneratorArtifactsInput(generatorInputs);
+
+ Either<List<Artifact>, ResponseFormat> generatorResponse;
+
+ if (mockGenerator) {
+ generatorResponse = artifactGenerator(convertedGeneratorInputs, ArtifactType.OTHER, component);
+ } else {
+ generatorResponse = artifactGenerator(convertedGeneratorInputs, ArtifactType.AAI, component);
+ }
+
+ if (generatorResponse.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.AAI_ARTIFACT_GENERATION_FAILED, component.getComponentType().getValue(), component.getName(), generatorResponse.toString());
+ return Either.right(responseFormat);
+ }
+
+ generatedArtifacts = generatorResponse.left().value();
+
+ aaiArtifacts = convertToArtifactDefinitionFromArtifactGeneratedData(generatedArtifacts);
+
+ }
+
+ return Either.left(aaiArtifacts);
+ }
+
+ private Either<ActionStatus, ResponseFormat> handleAllAAIArtifactsInDataModel(Component component, List<ArtifactDefinition> artifactsFromAAI, boolean shouldLock, boolean inTransaction) {
+
+ Either<ActionStatus, ResponseFormat> handleAAIArtifactsResponse = null;
+ User lastComponentUpdater = null;
+
+ List<ArtifactDefinition> aaiArtifatcsToCreate = getAAIArtifatcsForCreate(artifactsFromAAI, component);
+ List<ArtifactDefinition> aaiArtifatcsToDelete = getAAIArtifatcsForDelete(artifactsFromAAI, component);
+ List<ArtifactDefinition> aaiArtifatcsToUpdate = getAAIArtifatcsForUpdate(artifactsFromAAI, component);
+
+ String lastUpdaterUserId = component.getLastUpdaterUserId();
+ Either<User, ResponseFormat> validateUserExists = artifactsBusinessLogic.validateUserExists(lastUpdaterUserId, "CSAR creation util", true);
+
+ if (validateUserExists.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.AAI_ARTIFACT_GENERATION_FAILED, component.getComponentType().getValue(), component.getName(), "User not found");
+ return Either.right(responseFormat);
+ }
+
+ lastComponentUpdater = validateUserExists.left().value();
+
+ handleAAIArtifactsResponse = handleAAIArtifactsInDataModelByOperationType(component, aaiArtifatcsToDelete, ArtifactOperation.Delete, lastComponentUpdater, shouldLock, inTransaction);
+
+ if (handleAAIArtifactsResponse.isRight()) {
+ return handleAAIArtifactsResponse;
+ }
+
+ handleAAIArtifactsResponse = handleAAIArtifactsInDataModelByOperationType(component, aaiArtifatcsToCreate, ArtifactOperation.Create, lastComponentUpdater, shouldLock, inTransaction);
+
+ if (handleAAIArtifactsResponse.isRight()) {
+ return handleAAIArtifactsResponse;
+ }
+
+ return handleAAIArtifactsInDataModelByOperationType(component, aaiArtifatcsToUpdate, ArtifactOperation.Update, lastComponentUpdater, shouldLock, inTransaction);
+ }
+
+ private List<ArtifactDefinition> getAAIArtifatcsForUpdate(List<ArtifactDefinition> artifactsFromAAI, Component component) {
+
+ Set<String> componetDeploymentArtifactLables = component.getDeploymentArtifacts().keySet();
+ Set<String> componetInformationalArtifactLables = component.getArtifacts().keySet();
+
+ List<ArtifactDefinition> artifactsAaiUpdate = artifactsFromAAI.stream().filter(e -> (componetDeploymentArtifactLables.contains(e.getArtifactLabel()) || componetInformationalArtifactLables.contains(e.getArtifactLabel())))
+ .filter(e -> checkAaiForUpdate(component, e)).collect(Collectors.toList());
+
+ return artifactsAaiUpdate;
+ }
+
+ private boolean checkAaiForUpdate(Component component, ArtifactDefinition artifactDefinition) {
+ ArtifactDefinition artifactDefinitionComp = component.getDeploymentArtifacts().get(artifactDefinition.getArtifactLabel());
+
+ if (artifactDefinitionComp == null) {
+ log.warn("Failed to get {} artifact", artifactDefinition.getArtifactLabel());
+ return false;
+ }
+
+ // Old Artifacts before the generated flag introduction if contains "aai" ignore case prefix updated
+ if (artifactDefinitionComp.getGenerated() == null) {
+ if (artifactDefinitionComp.getArtifactLabel().toLowerCase().startsWith("aai")) {
+ return true;
+ } else {
+ log.warn("The artifact {} flag is null but AAI prefix is abssent Not updated", artifactDefinition.getArtifactLabel());
+ }
+ } else {
+ if (artifactDefinition.getGenerated()) {
+ return true;
+ } else {
+ log.warn("Generated artifact {} was already uploaded manually", artifactDefinition.getArtifactLabel());
+ }
+ }
+ return false;
+ }
+
+ private List<ArtifactDefinition> getAAIArtifatcsForDelete(List<ArtifactDefinition> artifactsFromAAI, Component component) {
+
+ Set<String> aaiLabels = artifactsFromAAI.stream().map(e -> e.getArtifactLabel()).collect(Collectors.toSet());
+
+ List<ArtifactDefinition> artifactsForDeleteDeployment = component.getDeploymentArtifacts().values().stream().
+ // Filter Out Artifacts that are not contained in artifacts returned
+ // from AAI API
+ filter(e -> !aaiLabels.contains(e.getArtifactLabel())).collect(Collectors.toList());
+
+ List<ArtifactDefinition> artifactsForDeleteInformational = component.getArtifacts().values().stream().
+ // Filter Out Artifacts that are not contained in artifacts returned
+ // from AAI API
+ filter(e -> !aaiLabels.contains(e.getArtifactLabel())).collect(Collectors.toList());
+
+ artifactsForDeleteDeployment.addAll(artifactsForDeleteInformational);
+
+ return artifactsForDeleteDeployment.stream().filter(e -> (e.getGenerated() != null && e.getGenerated().equals(Boolean.TRUE)) || (e.getGenerated() == null && e.getArtifactLabel().toLowerCase().startsWith("aai"))).collect(Collectors.toList());
+ }
+
+ private List<ArtifactDefinition> getAAIArtifatcsForCreate(List<ArtifactDefinition> artifactsFromAAI, Component component) {
+
+ Set<String> componentDeploymentLabels = component.getDeploymentArtifacts().keySet();
+ Set<String> componentInfoLabels = component.getArtifacts().keySet();
+
+ // If the artifact label does not exist in the service -
+ // store the artifact (generate uuid and version, "generated" flag is TRUE)
+ return artifactsFromAAI.stream().filter(e -> !componentDeploymentLabels.contains(e.getArtifactLabel()) && !componentInfoLabels.contains(e.getArtifactLabel())).collect(Collectors.toList());
+ }
+
+ private Either<ActionStatus, ResponseFormat> handleAAIArtifactsInDataModelByOperationType(Component component, List<ArtifactDefinition> generatedArtifactsDefinitions, ArtifactOperation operationType, User user, boolean shouldLock,
+ boolean inTransaction) {
+
+ String componentUniqueId = component.getUniqueId();
+ ComponentTypeEnum componentType = component.getComponentType();
+
+ for (ArtifactDefinition artDef : generatedArtifactsDefinitions) {
+ String data = gson.toJson(artDef);
+ String dataMD5 = GeneralUtility.calculateMD5ByString(data);
+ String artifactUniqueId = null;
+
+ if (operationType.equals(ArtifactOperation.Update) || operationType.equals(ArtifactOperation.Delete)) {
+ String artifactLabel = artDef.getArtifactLabel();
+ ArtifactDefinition artifactDefinition = component.getDeploymentArtifacts().get(artifactLabel);
+ if (artifactDefinition != null) {
+ artifactUniqueId = artifactDefinition.getUniqueId();
+ }
+ }
+
+ Either<Either<ArtifactDefinition, Operation>, ResponseFormat> validateAndHandleArtifact = artifactsBusinessLogic.validateAndHandleArtifact(componentUniqueId, componentType, operationType, artifactUniqueId, artDef, dataMD5, data, null,
+ null, null, user, component, shouldLock, inTransaction);
+
+ if (validateAndHandleArtifact.isRight()) {
+ if (ArtifactOperation.Create == operationType || ArtifactOperation.Update == operationType) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.AAI_ARTIFACT_GENERATION_FAILED, componentType.getValue(), component.getName(), validateAndHandleArtifact.right().value().toString());
+
+ Either.right(responseFormat);
+ } else {
+ log.warn("Generated artifact {} could not be deleted", artDef.getArtifactLabel());
+ }
+ }
+ }
+
+ return Either.left(ActionStatus.OK);
+ }
+
+ private List<ArtifactDefinition> convertToArtifactDefinitionFromArtifactGeneratedData(List<Artifact> generatorOutput) {
+ List<ArtifactDefinition> artifactDefList = new LinkedList<>();
+
+ for (Artifact artifact : generatorOutput) {
+ ArtifactDefinition newEntry = new ArtifactDefinition();
+ newEntry.setArtifactName(artifact.getName());
+ newEntry.setArtifactType(artifact.getType());
+ newEntry.setArtifactGroupType(ArtifactGroupTypeEnum.findType(artifact.getGroupType()));
+ newEntry.setDescription(artifact.getDescription());
+
+ // Normalizing the artifact label to match those stored in DB
+ String normalizeArtifactLabel = ValidationUtils.normalizeArtifactLabel(artifact.getLabel());
+ newEntry.setArtifactLabel(normalizeArtifactLabel);
+ newEntry.setPayload(artifact.getPayload());
+ newEntry.setArtifactChecksum(artifact.getChecksum());
+ // Flag that set to true in case that the artifact is generated by AI&I generator
+ newEntry.setGenerated(Boolean.TRUE);
+
+ artifactDefList.add(newEntry);
+ }
+
+ return artifactDefList;
+ }
+
+ // List<ImmutablePair<Component, byte[] artifactBytes>>
+ // artifact stored by label
+ private List<Artifact> convertToGeneratorArtifactsInput(List<ImmutablePair<Component, byte[]>> inputs) {
+ List<Artifact> listOfArtifactsInput = new LinkedList<>();
+ for (ImmutablePair<Component, byte[]> triple : inputs) {
+ Component component = triple.getLeft();
+
+ Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
+ ArtifactDefinition artifactDefinition = toscaArtifacts.get(ToscaExportHandler.ASSET_TOSCA_TEMPLATE);
+
+ String artifactName = artifactDefinition.getArtifactName();
+ String artifactType = artifactDefinition.getArtifactType();
+ String artifactGroupType = artifactDefinition.getArtifactGroupType().getType();
+ String artifactDescription = artifactDefinition.getDescription();
+ String artifactLabel = artifactDefinition.getArtifactLabel();
+ byte[] right = triple.getRight();
+ // The md5 calculated on the uncoded data
+ String md5Hex = DigestUtils.md5Hex(right);
+ // byte[] payload = Base64.getEncoder().encode(right);
+ byte[] payload = Base64.encodeBase64(right);
+ String artifactVersion = artifactDefinition.getArtifactVersion();
+
+ Artifact convertedArtifact = new Artifact(artifactType, artifactGroupType, md5Hex, payload);
+ convertedArtifact.setName(artifactName);
+ convertedArtifact.setDescription(artifactDescription);
+ convertedArtifact.setLabel(artifactLabel);
+ convertedArtifact.setVersion(artifactVersion);
+
+ listOfArtifactsInput.add(convertedArtifact);
+ }
+
+ return listOfArtifactsInput;
+ }
+
+ private Either<byte[], ActionStatus> getEntryData(String esId, Component childComponent) {
+ byte[] content;
+ if (esId == null || esId.isEmpty()) {
+ Either<ToscaRepresentation, ToscaError> exportRes = toscaExportUtils.exportComponent(childComponent);
+ if (exportRes.isRight()) {
+ log.debug("Failed to export tosca template for child component {} error {}", childComponent.getUniqueId(), exportRes.right().value());
+ return Either.right(componentsUtils.convertFromToscaError(exportRes.right().value()));
+ }
+ content = exportRes.left().value().getMainYaml().getBytes();
+ } else {
+ Either<byte[], ActionStatus> fromCassandra = getFromCassandra(esId);
+ if (fromCassandra.isRight()) {
+ return Either.right(fromCassandra.right().value());
+ } else {
+ content = fromCassandra.left().value();
+ }
+ }
+ return Either.left(content);
+ }
+
+ private Either<byte[], ActionStatus> getFromCassandra(String esId) {
+ Either<ESArtifactData, CassandraOperationStatus> artifactResponse = artifactCassandraDao.getArtifact(esId);
+
+ if (artifactResponse.isRight()) {
+ log.debug("In createCsar fetching of artifact from CS failed");
+ log.debug("Failed to fetch from Cassandra by id {} error {} ", esId, artifactResponse.right().value());
+
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertCassandraStatusToStorageStatus(artifactResponse.right().value());
+ ActionStatus convertedFromStorageResponse = componentsUtils.convertFromStorageResponse(storageStatus);
+ return Either.right(convertedFromStorageResponse);
+ } else {
+ ESArtifactData artifactData = artifactResponse.left().value();
+ return Either.left(artifactData.getDataAsArray());
+
+ }
+ }
+
+ private String createToscaBlock0(String metaFileVersion, String csarVersion, String createdBy, String entryDef) {
+ final String BLOCK_0_TEMPLATE = "TOSCA-Meta-File-Version: %s\nCSAR-Version: %s\nCreated-By: %s\nEntry-Definitions: Definitions/%s\n";
+ String readyBlock = String.format(BLOCK_0_TEMPLATE, metaFileVersion, csarVersion, createdBy, entryDef);
+ return readyBlock;
+ }
+
+ private Either<List<Artifact>, ResponseFormat> artifactGenerator(List<Artifact> artifactList, ArtifactType type, Component component) {
+
+ ArtifactGenerationServiceImpl artifactGenerationServiceImpl = new ArtifactGenerationServiceImpl();
+ ArtifactTypes artifactTypes = new ArtifactTypes();
+ List<ArtifactType> artifactTypesList = new LinkedList<>();
+ ArtifactType otherType;
+
+ if (type == null) {
+ otherType = ArtifactType.OTHER;
+ } else {
+ otherType = type;
+ }
+
+ artifactTypesList.add(otherType);
+ artifactTypes.setArtifactTypes(artifactTypesList);
+
+ String configJson = gson.toJson(artifactTypes);
+ GenerationData generatedArtifacts = artifactGenerationServiceImpl.generateArtifact(artifactList, configJson);
+
+ Map<String, List<String>> errorData = generatedArtifacts.getErrorData();
+
+ if (!errorData.isEmpty()) {
+ Set<String> keySet = errorData.keySet();
+
+ for (String key : keySet) {
+ List<String> errorList = errorData.get(key);
+ log.debug("The Artifact Generator Failed - {} with following: {}", key, errorList);
+ }
+
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.AAI_ARTIFACT_GENERATION_FAILED, component.getComponentType().getValue(), component.getName(), errorData.toString());
+ return Either.right(responseFormat);
+ }
+
+ return Either.left(generatedArtifacts.getResultData());
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java
new file mode 100644
index 0000000000..544b85361d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/PropertyConvertor.java
@@ -0,0 +1,189 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaMapValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaValueConverter;
+import org.openecomp.sdc.be.tosca.model.EntrySchema;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+
+import fj.data.Either;
+
+public class PropertyConvertor {
+ private static PropertyConvertor instance;
+ private JsonParser jsonParser = new JsonParser();
+ private static Logger log = LoggerFactory.getLogger(PropertyConvertor.class.getName());
+
+ protected PropertyConvertor() {
+
+ }
+
+ public static synchronized PropertyConvertor getInstance() {
+ if (instance == null) {
+ instance = new PropertyConvertor();
+ }
+ return instance;
+ }
+
+ public Either<ToscaNodeType, ToscaError> convertProperties(Component component, ToscaNodeType toscaNodeType, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (component instanceof Resource) {
+ Resource resource = (Resource) component;
+ List<PropertyDefinition> props = resource.getProperties();
+ if (props != null) {
+ Map<String, ToscaProperty> properties = new HashMap<>();
+
+ // take only the properties of this resource
+ props.stream().filter(p -> component.getUniqueId().equals(p.getParentUniqueId())).forEach(property -> {
+ ToscaProperty prop = convertProperty(dataTypes, property, false);
+
+ properties.put(property.getName(), prop);
+
+ });
+ if (!properties.isEmpty()) {
+ toscaNodeType.setProperties(properties);
+ }
+ }
+ }
+ return Either.left(toscaNodeType);
+ }
+
+ public ToscaProperty convertProperty(Map<String, DataTypeDefinition> dataTypes, PropertyDefinition property, boolean isCapabiltyProperty) {
+ ToscaProperty prop = new ToscaProperty();
+
+ String innerType = null;
+ SchemaDefinition schema = property.getSchema();
+ if (schema != null && schema.getProperty() != null && schema.getProperty().getType() != null && !schema.getProperty().getType().isEmpty()) {
+ innerType = schema.getProperty().getType();
+ EntrySchema eschema = new EntrySchema();
+ eschema.setType(innerType);
+ eschema.setDescription(schema.getProperty().getDescription());
+ prop.setEntry_schema(eschema);
+ }
+ log.debug("try to convert property {} from type {} with default value {}", property.getName(), property.getType(), property.getDefaultValue());
+ prop.setDefaultp(convertToToscaObject(property.getType(), property.getDefaultValue(), innerType, dataTypes));
+ prop.setType(property.getType());
+ prop.setDescription(property.getDescription());
+ if (isCapabiltyProperty) {
+ prop.setStatus(property.getStatus());
+ prop.setRequired(property.isRequired());
+ }
+ return prop;
+ }
+
+ public Object convertToToscaObject(String propertyType, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ log.debug("try to convert propertyType {} , value {}, innerType {}", propertyType, value, innerType);
+ if (value == null) {
+ return value;
+ }
+
+ ToscaMapValueConverter mapConverterInst = ToscaMapValueConverter.getInstance();
+ ToscaValueConverter innerConverter = null;
+ Boolean isScalar = true;
+
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+ if (type == null) {
+ log.debug("isn't prederfined type, get from all data types");
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
+ if (innerType == null) {
+ innerType = propertyType;
+ }
+
+ if ((type = mapConverterInst.isScalarType(dataTypeDefinition)) != null) {
+ log.debug("This is scalar type. get suitable converter for type {}", type);
+ innerConverter = type.getValueConverter();
+ } else {
+ isScalar = false;
+ }
+ } else {
+ ToscaPropertyType typeIfScalar = ToscaPropertyType.getTypeIfScalar(type.getType());
+ if (typeIfScalar == null) {
+ isScalar = false;
+ }
+
+ innerConverter = type.getValueConverter();
+ if (ToscaPropertyType.STRING.equals(type) && value.startsWith("/")) {
+ return innerConverter.convertToToscaValue(value, innerType, dataTypes);
+ }
+ }
+ JsonElement jsonElement = null;
+ try {
+ StringReader reader = new StringReader(value);
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+
+ jsonElement = jsonParser.parse(jsonReader);
+
+ if (value.equals("")) {
+ return value;
+ }
+
+ if (jsonElement.isJsonPrimitive() && isScalar) {
+ log.debug("It's well defined type. convert it");
+ ToscaValueConverter converter = type.getValueConverter();
+ return converter.convertToToscaValue(value, innerType, dataTypes);
+ } else {
+ log.debug("It's data type or inputs in primitive type. convert as map");
+ Object convertedValue;
+ if (innerConverter != null && (ToscaPropertyType.MAP.equals(type) || ToscaPropertyType.LIST.equals(type))) {
+ convertedValue = innerConverter.convertToToscaValue(value, innerType, dataTypes);
+ } else {
+ if (isScalar) {
+ // complex json for scalar type
+ convertedValue = mapConverterInst.handleComplexJsonValue(jsonElement);
+ } else {
+ if (innerConverter != null) {
+ convertedValue = innerConverter.convertToToscaValue(value, innerType, dataTypes);
+ } else {
+ convertedValue = mapConverterInst.convertDataTypeToToscaMap(innerType, dataTypes, innerConverter, isScalar, jsonElement);
+ }
+ }
+ }
+ return convertedValue;
+ }
+
+ } catch (JsonSyntaxException e) {
+ log.debug("convertToToscaValue failed to parse json value :", e);
+ return null;
+ }
+
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaError.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaError.java
new file mode 100644
index 0000000000..94dd559d2d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaError.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+public enum ToscaError {
+
+ NODE_TYPE_CAPABILITY_ERROR, NOT_SUPPORTED_TOSCA_TYPE, NODE_TYPE_REQUIREMENT_ERROR, GENERAL_ERROR
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java
new file mode 100644
index 0000000000..1bf940ffc0
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java
@@ -0,0 +1,629 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.beans.IntrospectionException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.apache.commons.lang3.tuple.Triple;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datamodel.utils.ArtifactUtils;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.tosca.model.IToscaMetadata;
+import org.openecomp.sdc.be.tosca.model.SubstitutionMapping;
+import org.openecomp.sdc.be.tosca.model.ToscaCapability;
+import org.openecomp.sdc.be.tosca.model.ToscaGroupTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaMetadata;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
+import org.openecomp.sdc.be.tosca.model.ToscaProperty;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplate;
+import org.openecomp.sdc.be.tosca.model.ToscaTemplateRequirement;
+import org.openecomp.sdc.be.tosca.model.ToscaTopolgyTemplate;
+import org.openecomp.sdc.be.tosca.model.VfModuleToscaMetadata;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("tosca-export-handler")
+public class ToscaExportHandler {
+
+ @Autowired
+ private ApplicationDataTypeCache dataTypeCache;
+
+ @Autowired
+ private IResourceOperation resourceOperation;
+
+ private CapabiltyRequirementConvertor capabiltyRequirementConvertor = CapabiltyRequirementConvertor.getInstance();
+ private PropertyConvertor propertyConvertor = PropertyConvertor.getInstance();
+
+ private static Logger log = LoggerFactory.getLogger(ToscaExportHandler.class.getName());
+
+ public static final String TOSCA_VERSION = "tosca_simple_yaml_1_0";
+ public static final String SERVICE_NODE_TYPE_PREFIX = "org.openecomp.service.";
+ public static final String IMPORTS_FILE_KEY = "file";
+ public static final String TOSCA_TEMPLATE_NAME = "-template.yml";
+ public static final String ASSET_TOSCA_TEMPLATE = "assettoscatemplate";
+ public static final String VF_MODULE_TYPE_KEY = "vf_module_type";
+ public static final String VF_MODULE_DESC_KEY = "vf_module_description";
+ public static final String VOLUME_GROUP_KEY = "volume_group";
+ public static final String VF_MODULE_TYPE_BASE = "Base";
+ public static final String VF_MODULE_TYPE_EXPANSION = "Expansion";
+
+ public Either<ToscaRepresentation, ToscaError> exportComponent(Component component) {
+
+ Either<ToscaTemplate, ToscaError> toscaTemplateRes = convertToToscaTemplate(component);
+ if (toscaTemplateRes.isRight()) {
+ return Either.right(toscaTemplateRes.right().value());
+ }
+
+ CustomRepresenter representer = new CustomRepresenter();
+ DumperOptions options = new DumperOptions();
+ options.setAllowReadOnlyProperties(false);
+ options.setPrettyFlow(true);
+
+ options.setDefaultFlowStyle(FlowStyle.FLOW);
+ options.setCanonical(false);
+
+ ToscaTemplate toscaTemplate = toscaTemplateRes.left().value();
+ representer.addClassTag(toscaTemplate.getClass(), Tag.MAP);
+
+ representer.setPropertyUtils(new UnsortedPropertyUtils());
+ Yaml yaml = new Yaml(representer, options);
+
+ String yamlAsString = yaml.dumpAsMap(toscaTemplate);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(ConfigurationManager.getConfigurationManager().getConfiguration().getHeatEnvArtifactHeader());
+ sb.append(yamlAsString);
+ sb.append(ConfigurationManager.getConfigurationManager().getConfiguration().getHeatEnvArtifactFooter());
+
+ ToscaRepresentation toscaRepresentation = new ToscaRepresentation();
+ toscaRepresentation.setMainYaml(sb.toString());
+ toscaRepresentation.setDependencies(toscaTemplate.getDependencies());
+
+ return Either.left(toscaRepresentation);
+ }
+
+ public Either<ToscaTemplate, ToscaError> getDependencies(Component component) {
+ ToscaTemplate toscaTemplate = new ToscaTemplate(null);
+ Either<ImmutablePair<ToscaTemplate, Map<String, Component>>, ToscaError> fillImports = fillImports(component, toscaTemplate);
+ if (fillImports.isRight()) {
+ return Either.right(fillImports.right().value());
+ }
+ return Either.left(fillImports.left().value().left);
+ }
+
+ private Either<ToscaTemplate, ToscaError> convertToToscaTemplate(Component component) {
+ log.debug("start tosca export for {}", component.getUniqueId());
+ ToscaTemplate toscaTemplate = new ToscaTemplate(TOSCA_VERSION);
+
+ toscaTemplate.setMetadata(convertMetadata(component, false));
+
+ Map<String, ToscaNodeType> node_types = new HashMap<>();
+ if (ToscaUtils.isNodeType(component)) {
+ log.debug("convert component as node type");
+ return convertNodeType(component, toscaTemplate, node_types);
+ } else {
+ log.debug("convert component as topology template");
+ return convertToscaTemplate(component, toscaTemplate);
+ }
+
+ }
+
+ private Either<ToscaTemplate, ToscaError> convertToscaTemplate(Component component, ToscaTemplate toscaNode) {
+
+ Either<ImmutablePair<ToscaTemplate, Map<String, Component>>, ToscaError> importsRes = fillImports(component, toscaNode);
+ if (importsRes.isRight()) {
+ return Either.right(importsRes.right().value());
+ }
+ toscaNode = importsRes.left().value().left;
+
+ Map<String, Component> componentCache = importsRes.left().value().right;
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> dataTypesEither = dataTypeCache.getAll();
+ if (dataTypesEither.isRight()) {
+ log.debug("Failed to retrieve all data types {}", dataTypesEither.right().value());
+ return Either.right(ToscaError.GENERAL_ERROR);
+ }
+ Map<String, DataTypeDefinition> dataTypes = dataTypesEither.left().value();
+
+ ToscaTopolgyTemplate topology_template = new ToscaTopolgyTemplate();
+
+ Either<ToscaTopolgyTemplate, ToscaError> inputs = fillInputs(component, topology_template, dataTypes);
+ if (inputs.isRight()) {
+ return Either.right(inputs.right().value());
+ }
+ topology_template = inputs.left().value();
+
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ Map<String, List<ComponentInstanceProperty>> componentInstancesProperties = component.getComponentInstancesProperties();
+ List<GroupDefinition> groups = component.getGroups();
+ if (componentInstances != null && !componentInstances.isEmpty()) {
+
+ Either<Map<String, ToscaNodeTemplate>, ToscaError> node_templates = convertNodeTemplates(component, componentInstances, componentInstancesProperties, componentCache, dataTypes);
+ if (node_templates.isRight()) {
+ return Either.right(node_templates.right().value());
+ }
+ log.debug("node templates converted");
+
+ topology_template.setNode_templates(node_templates.left().value());
+ }
+ if (groups != null && !groups.isEmpty()) {
+ Map<String, ToscaGroupTemplate> groupsMap = new HashMap<String, ToscaGroupTemplate>();
+ for (GroupDefinition group : groups) {
+ ToscaGroupTemplate toscaGroup = convertGroup(group, component);
+ groupsMap.put(group.getName(), toscaGroup);
+
+ }
+ topology_template.setGroups(groupsMap);
+ log.debug("groups converted");
+
+ }
+
+ SubstitutionMapping substitutionMapping = new SubstitutionMapping();
+ String toscaResourceName = null;
+ switch (component.getComponentType()) {
+ case RESOURCE:
+ toscaResourceName = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getToscaResourceName();
+ break;
+ case SERVICE:
+ toscaResourceName = SERVICE_NODE_TYPE_PREFIX + component.getComponentMetadataDefinition().getMetadataDataDefinition().getSystemName();
+ break;
+ default:
+ log.debug("Not supported component type {}", component.getComponentType());
+ return Either.right(ToscaError.NOT_SUPPORTED_TOSCA_TYPE);
+ }
+ substitutionMapping.setNode_type(toscaResourceName);
+
+ Either<SubstitutionMapping, ToscaError> capabilities = convertCapabilities(component, substitutionMapping, dataTypes);
+ if (capabilities.isRight()) {
+ return Either.right(capabilities.right().value());
+ }
+ substitutionMapping = capabilities.left().value();
+
+ Either<SubstitutionMapping, ToscaError> requirements = capabiltyRequirementConvertor.convertRequirements(component, substitutionMapping);
+ if (requirements.isRight()) {
+ return Either.right(requirements.right().value());
+ }
+ substitutionMapping = requirements.left().value();
+
+ topology_template.setSubstitution_mappings(substitutionMapping);
+
+ toscaNode.setTopology_template(topology_template);
+ return Either.left(toscaNode);
+ }
+
+ private Either<ToscaTopolgyTemplate, ToscaError> fillInputs(Component component, ToscaTopolgyTemplate topology_template, Map<String, DataTypeDefinition> dataTypes) {
+ if (log.isDebugEnabled())
+ log.debug("fillInputs for component {}", component.getUniqueId());
+ List<InputDefinition> inputDef = component.getInputs();
+ Map<String, ToscaProperty> inputs = new HashMap<>();
+
+ if (inputDef != null) {
+ inputDef.forEach(i -> {
+ ToscaProperty property = propertyConvertor.convertProperty(dataTypes, i, false);
+ inputs.put(i.getName(), property);
+ });
+ if (inputs != null && !inputs.isEmpty()) {
+ topology_template.setInputs(inputs);
+ }
+ }
+ return Either.left(topology_template);
+ }
+
+ private ToscaMetadata convertMetadata(Component component, boolean isInstance) {
+ ToscaMetadata toscaMetadata = new ToscaMetadata();
+ toscaMetadata.setName(component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
+ toscaMetadata.setInvariantUUID(component.getInvariantUUID());
+ toscaMetadata.setUUID(component.getUUID());
+ toscaMetadata.setDescription(component.getDescription());
+
+ List<CategoryDefinition> categories = component.getCategories();
+ CategoryDefinition categoryDefinition = categories.get(0);
+ toscaMetadata.setCategory(categoryDefinition.getName());
+
+ if (isInstance) {
+ toscaMetadata.setVersion(component.getVersion());
+ }
+ switch (component.getComponentType()) {
+ case RESOURCE:
+ Resource resource = (Resource) component;
+ toscaMetadata.setType(resource.getResourceType().name());
+ toscaMetadata.setSubcategory(categoryDefinition.getSubcategories().get(0).getName());
+ if (!isInstance) {
+ toscaMetadata.setResourceVendor(resource.getVendorName());
+ toscaMetadata.setResourceVendorRelease(resource.getVendorRelease());
+ }
+ break;
+ case SERVICE:
+ toscaMetadata.setType(component.getComponentType().getValue());
+ if (!isInstance) {
+ toscaMetadata.setServiceEcompNaming(false);
+ toscaMetadata.setServiceHoming(false);
+ }
+ break;
+ default:
+ log.debug("Not supported component type {}", component.getComponentType());
+ }
+ return toscaMetadata;
+ }
+
+ private Either<ImmutablePair<ToscaTemplate, Map<String, Component>>, ToscaError> fillImports(Component component, ToscaTemplate toscaTemplate) {
+ Map<String, Component> componentCache = new HashMap<>();
+ if (!ToscaUtils.isNodeType(component)) {
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null && !componentInstances.isEmpty()) {
+ List<Map<String, Map<String, String>>> imports = new LinkedList<Map<String, Map<String, String>>>();
+ List<Triple<String, String, Component>> dependecies = new ArrayList<>();
+
+ componentInstances.forEach(ci -> {
+ createDependency(componentCache, imports, dependecies, ci);
+ });
+ toscaTemplate.setImports(imports);
+ toscaTemplate.setDependencies(dependecies);
+ }
+ } else {
+ log.debug("currently imports supported for VF and service only");
+ }
+ return Either.left(new ImmutablePair<ToscaTemplate, Map<String, Component>>(toscaTemplate, componentCache));
+ }
+
+ private void createDependency(Map<String, Component> componentCache, List<Map<String, Map<String, String>>> imports, List<Triple<String, String, Component>> dependecies, ComponentInstance ci) {
+ Map<String, String> files = new HashMap<>();
+ Map<String, Map<String, String>> importsListMember = new HashMap<>();
+
+ Component componentRI = componentCache.get(ci.getComponentUid());
+ if (componentRI == null) {
+ // all resource must be only once!
+ Either<Resource, StorageOperationStatus> resource = resourceOperation.getResource(ci.getComponentUid(), true);
+ if (resource.isRight()) {
+ log.debug("Failed to fetch resource with id {} for instance {}");
+ }
+ Resource fetchedComponent = resource.left().value();
+ componentCache.put(fetchedComponent.getUniqueId(), fetchedComponent);
+ componentRI = fetchedComponent;
+
+ Map<String, ArtifactDefinition> toscaArtifacts = componentRI.getToscaArtifacts();
+ ArtifactDefinition artifactDefinition = toscaArtifacts.get(ASSET_TOSCA_TEMPLATE);
+ if (artifactDefinition != null) {
+ String artifactName = artifactDefinition.getArtifactName();
+ files.put(IMPORTS_FILE_KEY, artifactName);
+ importsListMember.put(ci.getComponentName(), files);
+ dependecies.add(new ImmutableTriple<String, String, Component>(artifactName, artifactDefinition.getEsId(), fetchedComponent));
+ }
+ }
+ if (!importsListMember.isEmpty()) {
+ imports.add(importsListMember);
+ }
+ }
+
+ private Either<ToscaTemplate, ToscaError> convertNodeType(Component component, ToscaTemplate toscaNode, Map<String, ToscaNodeType> node_types) {
+ log.debug("start convert node type for {}", component.getUniqueId());
+ ToscaNodeType toscaNodeType = createNodeType(component);
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> dataTypesEither = dataTypeCache.getAll();
+ if (dataTypesEither.isRight()) {
+ log.debug("Failed to fetch all data types :", dataTypesEither.right().value());
+ return Either.right(ToscaError.GENERAL_ERROR);
+ }
+
+ Map<String, DataTypeDefinition> dataTypes = dataTypesEither.left().value();
+ Either<ToscaNodeType, ToscaError> properties = propertyConvertor.convertProperties(component, toscaNodeType, dataTypes);
+ if (properties.isRight()) {
+ return Either.right(properties.right().value());
+ }
+ toscaNodeType = properties.left().value();
+ log.debug("Properties converted for {}", component.getUniqueId());
+
+ Either<ToscaNodeType, ToscaError> capabilities = convertCapabilities(component, toscaNodeType, dataTypes);
+ if (capabilities.isRight()) {
+ return Either.right(capabilities.right().value());
+ }
+ toscaNodeType = capabilities.left().value();
+ log.debug("Capabilities converted for {}", component.getUniqueId());
+
+ Either<ToscaNodeType, ToscaError> requirements = capabiltyRequirementConvertor.convertRequirements(component, toscaNodeType);
+ if (requirements.isRight()) {
+ return Either.right(requirements.right().value());
+ }
+ toscaNodeType = requirements.left().value();
+ log.debug("Requirements converted for {}", component.getUniqueId());
+
+ String toscaResourceName = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getToscaResourceName();
+ node_types.put(toscaResourceName, toscaNodeType);
+ toscaNode.setNode_types(node_types);
+ log.debug("finish convert node type for {}", component.getUniqueId());
+ return Either.left(toscaNode);
+ }
+
+ private Either<Map<String, ToscaNodeTemplate>, ToscaError> convertNodeTemplates(Component component, List<ComponentInstance> componentInstances, Map<String, List<ComponentInstanceProperty>> componentInstancesProperties,
+ Map<String, Component> componentCache, Map<String, DataTypeDefinition> dataTypes) {
+ log.debug("start convert topology template for {} for type {}", component.getUniqueId(), component.getComponentType());
+ Map<String, ToscaNodeTemplate> node_templates = new HashMap<>();
+
+ for (ComponentInstance componentInstance : componentInstances) {
+ ToscaNodeTemplate nodeTemplate = new ToscaNodeTemplate();
+ nodeTemplate.setType(componentInstance.getToscaComponentName());
+
+ Either<ToscaNodeTemplate, ToscaError> requirements = convertComponentInstanceRequirements(component, componentInstance, component.getComponentInstancesRelations(), nodeTemplate);
+ if (requirements.isRight()) {
+ return Either.right(requirements.right().value());
+ }
+ log.debug("Component instance Requirements converted for instance {}", componentInstance.getUniqueId());
+
+ nodeTemplate = requirements.left().value();
+
+ Component componentOfInstance = componentCache.get(componentInstance.getComponentUid());
+ nodeTemplate.setMetadata(convertMetadata(componentOfInstance, true));
+
+ Either<ToscaNodeTemplate, ToscaError> capabilties = capabiltyRequirementConvertor.convertComponentInstanceCapabilties(componentInstance, dataTypes, nodeTemplate);
+ if (capabilties.isRight()) {
+ return Either.right(requirements.right().value());
+ }
+ log.debug("Component instance Capabilties converted for instance {}", componentInstance.getUniqueId());
+
+ nodeTemplate = capabilties.left().value();
+
+ if (componentInstancesProperties.containsKey(componentInstance.getUniqueId())) {
+ Map<String, Object> props = null;
+ List<ComponentInstanceProperty> propList = componentInstancesProperties.get(componentInstance.getUniqueId());
+ List<ComponentInstanceProperty> collect = propList.stream().filter(e -> e.getValueUniqueUid() != null && !e.getValueUniqueUid().isEmpty()).collect(Collectors.toList());
+ if (collect != null && !collect.isEmpty()) {
+ props = new HashMap<String, Object>();
+ for (ComponentInstanceProperty prop : collect) {
+ Object convertedValue = convertInstanceProperty(dataTypes, componentInstance, prop);
+ props.put(prop.getName(), convertedValue);
+ }
+ }
+ if (props != null && !props.isEmpty()) {
+ nodeTemplate.setProperties(props);
+ }
+ }
+ node_templates.put(componentInstance.getName(), nodeTemplate);
+ }
+ log.debug("finish convert topology template for {} for type {}", component.getUniqueId(), component.getComponentType());
+ return Either.left(node_templates);
+ }
+
+ private Object convertInstanceProperty(Map<String, DataTypeDefinition> dataTypes, ComponentInstance componentInstance, ComponentInstanceProperty prop) {
+ log.debug("Convert property {} for instance {}", prop.getName(), componentInstance.getUniqueId());
+ String propertyType = prop.getType();
+ String innerType = null;
+ if (prop.getSchema() != null && prop.getSchema().getProperty() != null) {
+ innerType = prop.getSchema().getProperty().getType();
+ }
+ Object convertedValue = propertyConvertor.convertToToscaObject(propertyType, prop.getValue(), innerType, dataTypes);
+ return convertedValue;
+ }
+
+ private ToscaGroupTemplate convertGroup(GroupDefinition group, Component component) {
+ ToscaGroupTemplate toscaGroup = new ToscaGroupTemplate();
+ Map<String, String> members = group.getMembers();
+ toscaGroup.setType(group.getType());
+ if (members != null)
+ toscaGroup.setMembers(new ArrayList(members.keySet()));
+
+ boolean isVfModule = group.getType().equals(Constants.DEFAULT_GROUP_VF_MODULE) ? true : false;
+ IToscaMetadata toscaMetadata;
+ if (!isVfModule) {
+ toscaMetadata = new ToscaMetadata();
+ } else {
+ toscaMetadata = new VfModuleToscaMetadata();
+ Map<String, Object> properties = new HashMap<>();
+
+ for (GroupProperty gp : group.getProperties()) {
+ if (gp.getName().equals(Constants.IS_BASE)) {
+ Boolean isBase = Boolean.parseBoolean(gp.getValue());
+ String type = isBase ? VF_MODULE_TYPE_BASE : VF_MODULE_TYPE_EXPANSION;
+ properties.put(VF_MODULE_TYPE_KEY, type);
+ break;
+ }
+ }
+ properties.put(VF_MODULE_DESC_KEY, group.getDescription());
+ boolean isVolume = false;
+ List<String> artifactsList = group.getArtifacts();
+ if (artifactsList != null && !artifactsList.isEmpty()) {
+
+ for (String artifactId : artifactsList) {
+ Map<String, ArtifactDefinition> deploymentArtifacts = component.getDeploymentArtifacts();
+ Optional<ArtifactDefinition> findFirst = deploymentArtifacts.values().stream().filter(p -> p.getUniqueId().equals(artifactId)).findFirst();
+ if (findFirst.isPresent()) {
+ ArtifactDefinition artifactDefinition = findFirst.get();
+ if (artifactDefinition.getArtifactType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_VOL.getType())) {
+ isVolume = true;
+ break;
+ }
+ }
+ }
+ }
+ properties.put(VOLUME_GROUP_KEY, isVolume);
+ toscaGroup.setProperties(properties);
+ }
+ toscaMetadata.setName(group.getName());
+ toscaMetadata.setInvariantUUID(group.getInvariantUUID());
+ toscaMetadata.setUUID(group.getGroupUUID());
+ toscaMetadata.setVersion(group.getVersion());
+ toscaGroup.setMetadata(toscaMetadata);
+ return toscaGroup;
+ }
+
+ private ToscaNodeType createNodeType(Component component) {
+ ToscaNodeType toscaNodeType = new ToscaNodeType();
+ if (ToscaUtils.isNodeType(component) && ((Resource) component).getDerivedFrom() != null) {
+ toscaNodeType.setDerived_from(((Resource) component).getDerivedFrom().get(0));
+ }
+ toscaNodeType.setDescription(component.getDescription()); // or name??
+ return toscaNodeType;
+ }
+
+ private Either<ToscaNodeTemplate, ToscaError> convertComponentInstanceRequirements(Component component, ComponentInstance componentInstance, List<RequirementCapabilityRelDef> relations, ToscaNodeTemplate nodeTypeTemplate) {
+
+ List<ComponentInstance> instancesList = component.getComponentInstances();
+ List<Map<String, ToscaTemplateRequirement>> toscaRequirements = new ArrayList<>();
+ Map<String, List<RequirementDefinition>> reqMap = componentInstance.getRequirements();
+
+ relations.stream().filter(p -> componentInstance.getUniqueId().equals(p.getFromNode())).forEach(req -> {
+ ComponentInstance toComponentInstance = instancesList.stream().filter(i -> req.getToNode().equals(i.getUniqueId())).findFirst().orElse(null);
+ if (toComponentInstance == null) {
+ log.debug("Faild to create relation between node {} to node {}", componentInstance.getName(), req.getToNode());
+ return;
+
+ }
+ RequirementAndRelationshipPair reqAndRelationshopPair = req.getRelationships().get(0);
+ ToscaTemplateRequirement toscaRequirement = new ToscaTemplateRequirement();
+ toscaRequirement.setRelationship(reqAndRelationshopPair.getRelationship().getType());
+ toscaRequirement.setNode(toComponentInstance.getName());
+ Optional<RequirementDefinition> findAny = reqMap.values().stream().flatMap(e -> e.stream()).filter(e -> e.getName().equals(reqAndRelationshopPair.getRequirement())).findAny();
+ if (findAny.isPresent()) {
+ RequirementDefinition regDefinition = findAny.get();
+ toscaRequirement.setCapability(regDefinition.getCapability());
+ } else {
+ log.debug("Faild to find relation between node {} to node {}", componentInstance.getName(), req.getToNode());
+ return;
+ }
+ Map<String, ToscaTemplateRequirement> reqmap = new HashMap<String, ToscaTemplateRequirement>();
+ reqmap.put(reqAndRelationshopPair.getRequirement(), toscaRequirement);
+ toscaRequirements.add(reqmap);
+
+ });
+
+ if (!toscaRequirements.isEmpty()) {
+ nodeTypeTemplate.setRequirements(toscaRequirements);
+ }
+ log.debug("Finish convert Requirements for node type");
+ return Either.left(nodeTypeTemplate);
+ }
+
+ private Either<SubstitutionMapping, ToscaError> convertCapabilities(Component component, SubstitutionMapping substitutionMapping, Map<String, DataTypeDefinition> dataTypes) {
+ Map<String, ToscaCapability> toscaCapabilities = capabiltyRequirementConvertor.convertCapabilities(component, dataTypes);
+ if (!toscaCapabilities.isEmpty()) {
+ substitutionMapping.setCapabilities(toscaCapabilities);
+ }
+ log.debug("Finish convert Capabilities for node type");
+
+ return Either.left(substitutionMapping);
+ }
+
+ private Either<ToscaNodeType, ToscaError> convertCapabilities(Component component, ToscaNodeType nodeType, Map<String, DataTypeDefinition> dataTypes) {
+ Map<String, ToscaCapability> toscaCapabilities = capabiltyRequirementConvertor.convertCapabilities(component, dataTypes);
+ if (!toscaCapabilities.isEmpty()) {
+ nodeType.setCapabilities(toscaCapabilities);
+ }
+ log.debug("Finish convert Capabilities for node type");
+
+ return Either.left(nodeType);
+ }
+
+ private static class CustomRepresenter extends Representer {
+ public CustomRepresenter() {
+ super();
+ // null representer is exceptional and it is stored as an instance
+ // variable.
+ this.nullRepresenter = new RepresentNull();
+
+ }
+
+ @Override
+ protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
+ if (propertyValue == null) {
+ return null;
+ } else {
+ // skip not relevant for Tosca property
+ if (property.getName().equals("dependencies")) {
+ return null;
+ }
+ NodeTuple defaultNode = super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+
+ return property.getName().equals("_defaultp_") ? new NodeTuple(representData("default"), defaultNode.getValueNode()) : defaultNode;
+ }
+ }
+
+ @Override
+ protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
+ // remove the bean type from the output yaml (!! ...)
+ if (!classTags.containsKey(javaBean.getClass()))
+ addClassTag(javaBean.getClass(), Tag.MAP);
+
+ return super.representJavaBean(properties, javaBean);
+ }
+
+ private class RepresentNull implements Represent {
+ public Node representData(Object data) {
+ // possible values are here http://yaml.org/type/null.html
+ return representScalar(Tag.NULL, "");
+ }
+ }
+ }
+
+ private static class UnsortedPropertyUtils extends PropertyUtils {
+ @Override
+ protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess) throws IntrospectionException {
+ Collection<Property> fields = getPropertiesMap(type, BeanAccess.FIELD).values();
+ Set<Property> result = new LinkedHashSet<Property>(fields);
+ return result;
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaRepresentation.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaRepresentation.java
new file mode 100644
index 0000000000..6b1895b6ec
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaRepresentation.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.util.List;
+
+import org.apache.commons.lang3.tuple.Triple;
+import org.openecomp.sdc.be.model.Component;
+
+public class ToscaRepresentation {
+
+ private String mainYaml;
+ private List<Triple<String, String, Component>> dependencies;
+
+ public String getMainYaml() {
+ return mainYaml;
+ }
+
+ public void setMainYaml(String mainYaml) {
+ this.mainYaml = mainYaml;
+ }
+
+ public List<Triple<String, String, Component>> getDependencies() {
+ return dependencies;
+ }
+
+ public void setDependencies(List<Triple<String, String, Component>> dependancies) {
+ this.dependencies = dependancies;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java
new file mode 100644
index 0000000000..db03ad2307
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaUtils.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+
+public class ToscaUtils {
+
+ public static boolean isNodeType(Component component) {
+ ComponentTypeEnum componentType = component.getComponentType();
+ if (ComponentTypeEnum.RESOURCE.equals(componentType)) {
+ ResourceTypeEnum resourceType = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getResourceType();
+ if (ResourceTypeEnum.CP.equals(resourceType) || ResourceTypeEnum.VL.equals(resourceType) || ResourceTypeEnum.VFC.equals(resourceType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Map<String, Object> objectToMap(Object objectToConvert, Class clazz) throws IllegalArgumentException, IllegalAccessException {
+ Map<String, Object> map = new HashMap<>();
+ List<Field> fields = new ArrayList<>();
+
+ fields = getAllFields(fields, clazz);
+
+ for (Field field : fields) {
+ field.setAccessible(true);
+ map.put(field.getName(), field.get(objectToConvert));
+ }
+ return map;
+ }
+
+ public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
+ fields.addAll(Arrays.asList(type.getDeclaredFields()));
+
+ if (type.getSuperclass() != null) {
+ fields = getAllFields(fields, type.getSuperclass());
+ }
+ return fields;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/EntrySchema.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/EntrySchema.java
new file mode 100644
index 0000000000..0c586c7043
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/EntrySchema.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+public class EntrySchema {
+ private String type;
+ private String description;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/IToscaMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/IToscaMetadata.java
new file mode 100644
index 0000000000..46f4fd032e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/IToscaMetadata.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+public interface IToscaMetadata {
+
+ public void setName(String name);
+
+ public void setInvariantUUID(String invariantUUID);
+
+ public void setUUID(String uUID);
+
+ public void setVersion(String version);
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/SubstitutionMapping.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/SubstitutionMapping.java
new file mode 100644
index 0000000000..0fd9daef7d
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/SubstitutionMapping.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.Map;
+
+public class SubstitutionMapping {
+ private String node_type;
+
+ private Map<String, ToscaCapability> capabilities;
+ private Map<String, ToscaRequirement> requirements;
+
+ public SubstitutionMapping() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public String getNode_type() {
+ return node_type;
+ }
+
+ public void setNode_type(String node_type) {
+ this.node_type = node_type;
+ }
+
+ public Map<String, ToscaCapability> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map<String, ToscaCapability> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public Map<String, ToscaRequirement> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(Map<String, ToscaRequirement> requirements) {
+ this.requirements = requirements;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaCapability.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaCapability.java
new file mode 100644
index 0000000000..2688e5e6b3
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaCapability.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ToscaCapability {
+
+ private String type;
+ private String description;
+
+ private List<Object> occurrences;
+
+ private List<String> valid_source_types;
+
+ private Map<String, ToscaProperty> properties;
+
+ public List<String> getValid_source_types() {
+ return valid_source_types;
+ }
+
+ public void setValid_source_types(List<String> valid_source_types) {
+ this.valid_source_types = valid_source_types;
+ }
+
+ public ToscaCapability() {
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public List<Object> getOccurrences() {
+ return occurrences;
+ }
+
+ public void setOccurrences(List<Object> occurrences) {
+ this.occurrences = occurrences;
+ }
+
+ public Map<String, ToscaProperty> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, ToscaProperty> properties) {
+ this.properties = properties;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaGroupTemplate.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaGroupTemplate.java
new file mode 100644
index 0000000000..b91cffa5be
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaGroupTemplate.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ToscaGroupTemplate {
+ private String type;
+ List<String> members;
+ private IToscaMetadata metadata;
+ private Map<String, Object> properties;
+
+ public ToscaGroupTemplate() {
+ super();
+
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List<String> getMembers() {
+ return members;
+ }
+
+ public void setMembers(List<String> members) {
+ this.members = members;
+ }
+
+ public IToscaMetadata getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(IToscaMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.properties = properties;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java
new file mode 100644
index 0000000000..38cd002726
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+public class ToscaMetadata implements IToscaMetadata {
+ private String invariantUUID;
+ private String UUID;
+ private String version;
+ private String name;
+ private String description;
+ private String type;
+ private String category;
+ private String subcategory;
+ private String resourceVendor;
+ private String resourceVendorRelease;
+ private Boolean serviceEcompNaming;
+ private Boolean serviceHoming;
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ @Override
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ public String getUUID() {
+ return UUID;
+ }
+
+ @Override
+ public void setUUID(String uUID) {
+ UUID = uUID;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public String getSubcategory() {
+ return subcategory;
+ }
+
+ public void setSubcategory(String subcategory) {
+ this.subcategory = subcategory;
+ }
+
+ public String getResourceVendor() {
+ return resourceVendor;
+ }
+
+ public void setResourceVendor(String resourceVendor) {
+ this.resourceVendor = resourceVendor;
+ }
+
+ public String getResourceVendorRelease() {
+ return resourceVendorRelease;
+ }
+
+ public void setResourceVendorRelease(String resourceVendorRelease) {
+ this.resourceVendorRelease = resourceVendorRelease;
+ }
+
+ public Boolean isServiceEcompNaming() {
+ return serviceEcompNaming;
+ }
+
+ public void setServiceEcompNaming(Boolean serviceEcompNaming) {
+ this.serviceEcompNaming = serviceEcompNaming;
+ }
+
+ public Boolean isServiceHoming() {
+ return serviceHoming;
+ }
+
+ public void setServiceHoming(Boolean serviceHoming) {
+ this.serviceHoming = serviceHoming;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeTemplate.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeTemplate.java
new file mode 100644
index 0000000000..b2d7ef5ab6
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeTemplate.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ToscaNodeTemplate {
+ private String type;
+ private ToscaMetadata metadata;
+ private Map<String, Object> properties;
+ private List<Map<String, ToscaTemplateRequirement>> requirements;
+ private Map<String, ToscaTemplateCapability> capabilities;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.properties = properties;
+ }
+
+ public List<Map<String, ToscaTemplateRequirement>> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(List<Map<String, ToscaTemplateRequirement>> requirements) {
+ this.requirements = requirements;
+ }
+
+ public Map<String, ToscaTemplateCapability> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map<String, ToscaTemplateCapability> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public ToscaMetadata getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(ToscaMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeType.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeType.java
new file mode 100644
index 0000000000..df9070f49c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaNodeType.java
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ToscaNodeType {
+ public ToscaNodeType() {
+ }
+
+ private ToscaMetadata metadata;
+ private String derived_from;
+ private String description;
+
+ private Map<String, ToscaProperty> properties;
+ private Map<String, ToscaCapability> capabilities;
+
+ private List<Map<String, ToscaRequirement>> requirements;
+
+ public Map<String, ToscaProperty> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, ToscaProperty> properties) {
+ this.properties = properties;
+ }
+
+ public Map<String, ToscaCapability> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map<String, ToscaCapability> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public List<Map<String, ToscaRequirement>> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(List<Map<String, ToscaRequirement>> requirements) {
+ this.requirements = requirements;
+ }
+
+ public String getDerived_from() {
+ return derived_from;
+ }
+
+ public void setDerived_from(String derived_from) {
+ this.derived_from = derived_from;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ToscaMetadata getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(ToscaMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaProperty.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaProperty.java
new file mode 100644
index 0000000000..8b5c60b1d4
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaProperty.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+public class ToscaProperty {
+
+ private String type;
+ private Object _defaultp_;
+ private String description;
+ private Boolean required;
+ private EntrySchema entry_schema;
+ private String status;
+
+ public EntrySchema getEntry_schema() {
+ return entry_schema;
+ }
+
+ public void setEntry_schema(EntrySchema entry_schema) {
+ this.entry_schema = entry_schema;
+ }
+
+ public ToscaProperty() {
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Object getDefaultp() {
+ return _defaultp_;
+ }
+
+ public void setDefaultp(Object defaultp) {
+ this._defaultp_ = defaultp;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Boolean getRequired() {
+ return required;
+ }
+
+ public void setRequired(Boolean required) {
+ this.required = required;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java
new file mode 100644
index 0000000000..21fdc9f722
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaRequirement.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ToscaRequirement extends ToscaTemplateRequirement {
+
+ private List<Object> occurrences;
+
+ public ToscaRequirement() {
+ }
+
+ public List<Object> getOccurrences() {
+ return occurrences;
+ }
+
+ public void setOccurrences(List<Object> occurrences) {
+ this.occurrences = occurrences;
+ }
+
+ public Map<String, Object> toMap() throws IllegalArgumentException, IllegalAccessException {
+ return super.toMap();
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplate.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplate.java
new file mode 100644
index 0000000000..e80d52167c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplate.java
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.Triple;
+import org.openecomp.sdc.be.model.Component;
+
+public class ToscaTemplate {
+ private String tosca_definitions_version;
+ private ToscaMetadata metadata;
+ private List<Map<String, Map<String, String>>> imports;
+ private Map<String, ToscaNodeType> node_types;
+ private ToscaTopolgyTemplate topology_template;
+
+ private List<Triple<String, String, Component>> dependencies;
+
+ public ToscaTemplate(String tosca_definitions_version) {
+ this.tosca_definitions_version = tosca_definitions_version;
+ }
+
+ public Map<String, ToscaNodeType> getNode_types() {
+ return node_types;
+ }
+
+ public void setNode_types(Map<String, ToscaNodeType> node_types) {
+ this.node_types = node_types;
+ }
+
+ public List<Map<String, Map<String, String>>> getImports() {
+ return imports;
+ }
+
+ public void setImports(List<Map<String, Map<String, String>>> imports) {
+ this.imports = imports;
+ }
+
+ public String getTosca_definitions_version() {
+ return tosca_definitions_version;
+ }
+
+ public void setTosca_definitions_version(String tosca_definitions_version) {
+ this.tosca_definitions_version = tosca_definitions_version;
+ }
+
+ public ToscaMetadata getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(ToscaMetadata metadata) {
+ this.metadata = metadata;
+ }
+
+ public ToscaTopolgyTemplate getTopology_template() {
+ return topology_template;
+ }
+
+ public void setTopology_template(ToscaTopolgyTemplate topology_template) {
+ this.topology_template = topology_template;
+ }
+
+ public List<Triple<String, String, Component>> getDependencies() {
+ return dependencies;
+ }
+
+ public void setDependencies(List<Triple<String, String, Component>> dependencies) {
+ this.dependencies = dependencies;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateCapability.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateCapability.java
new file mode 100644
index 0000000000..9adb9edfe1
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateCapability.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ToscaTemplateCapability {
+ private List<String> valid_source_types;
+ private Map<String, Object> properties;
+
+ public List<String> getValid_source_types() {
+ return valid_source_types;
+ }
+
+ public void setValid_source_types(List<String> valid_source_types) {
+ this.valid_source_types = valid_source_types;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.properties = properties;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java
new file mode 100644
index 0000000000..71bae81f9c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTemplateRequirement.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ToscaTemplateRequirement {
+ private String capability;
+ private String node;
+ private String relationship;
+
+ public ToscaTemplateRequirement() {
+ }
+
+ public String getCapability() {
+ return capability;
+ }
+
+ public void setCapability(String capability) {
+ this.capability = capability;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ public String getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationship(String relationship) {
+ this.relationship = relationship;
+ }
+
+ public Map<String, Object> toMap() throws IllegalArgumentException, IllegalAccessException {
+ Map<String, Object> map = new HashMap<>();
+ Field[] fields = this.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ field.setAccessible(true);
+ map.put(field.getName(), field.get(this));
+ }
+ return map;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java
new file mode 100644
index 0000000000..6804bf6968
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaTopolgyTemplate.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+import java.util.Map;
+
+public class ToscaTopolgyTemplate {
+ private Map<String, ToscaProperty> inputs;
+ private Map<String, ToscaNodeTemplate> node_templates;
+ private Map<String, ToscaGroupTemplate> groups;
+ private SubstitutionMapping substitution_mappings;
+
+ public Map<String, ToscaNodeTemplate> getNode_templates() {
+ return node_templates;
+ }
+
+ public void setNode_templates(Map<String, ToscaNodeTemplate> node_templates) {
+ this.node_templates = node_templates;
+ }
+
+ public Map<String, ToscaGroupTemplate> getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Map<String, ToscaGroupTemplate> groups) {
+ this.groups = groups;
+ }
+
+ public SubstitutionMapping getSubstitution_mappings() {
+ return substitution_mappings;
+ }
+
+ public void setSubstitution_mappings(SubstitutionMapping substitution_mapping) {
+ this.substitution_mappings = substitution_mapping;
+ }
+
+ public Map<String, ToscaProperty> getInputs() {
+ return inputs;
+ }
+
+ public void setInputs(Map<String, ToscaProperty> inputs) {
+ this.inputs = inputs;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/VfModuleToscaMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/VfModuleToscaMetadata.java
new file mode 100644
index 0000000000..5f978227ef
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/VfModuleToscaMetadata.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.tosca.model;
+
+public class VfModuleToscaMetadata implements IToscaMetadata {
+
+ private String vfModuleModelName;
+ private String vfModuleModelInvariantUUID;
+ private String vfModuleModelUUID;
+ private String vfModuleModelVersion;
+
+ @Override
+ public void setName(String name) {
+ vfModuleModelName = name;
+ }
+
+ @Override
+ public void setInvariantUUID(String invariantUUID) {
+ vfModuleModelInvariantUUID = invariantUUID;
+ }
+
+ @Override
+ public void setUUID(String uUID) {
+ vfModuleModelUUID = uUID;
+ }
+
+ @Override
+ public void setVersion(String version) {
+ vfModuleModelVersion = version;
+ }
+
+ public String getVfModuleModelName() {
+ return vfModuleModelName;
+ }
+
+ public String getVfModuleModelInvariantUUID() {
+ return vfModuleModelInvariantUUID;
+ }
+
+ public String getVfModuleModelUUID() {
+ return vfModuleModelUUID;
+ }
+
+ public String getVfModuleModelVersion() {
+ return vfModuleModelVersion;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/user/IUserBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/user/IUserBusinessLogic.java
new file mode 100644
index 0000000000..5cfa4a12da
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/user/IUserBusinessLogic.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.user;
+
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.FunctionalMenuInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public interface IUserBusinessLogic {
+ public Either<User, ActionStatus> getUser(String userId, boolean inTransaction);
+
+ public Either<User, ResponseFormat> createUser(User modifier, User newUser);
+
+ public Either<User, ResponseFormat> updateUserRole(User modifier, String userIdToUpdate, String userRole);
+
+ public Either<List<User>, ResponseFormat> getAllAdminUsers(ServletContext context);
+
+ public Either<List<User>, ResponseFormat> getUsersList(String userId, List<String> roles, String rolesStr);
+
+ public Either<User, ResponseFormat> deActivateUser(User modifier, String userUniuqeIdToDeactive);
+
+ public Either<User, ResponseFormat> authorize(User authUser);
+
+ public Either<FunctionalMenuInfo, ActionStatus> getFunctionalMenu(String userId);
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/user/Role.java b/catalog-be/src/main/java/org/openecomp/sdc/be/user/Role.java
new file mode 100644
index 0000000000..ab58ea7bf0
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/user/Role.java
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.user;
+
+/*
+ * Warning changing the order of the Enum variables changes the ordianl() function output
+ * which may result in ecompRole id change
+ */
+public enum Role {
+ ADMIN, TESTER, DESIGNER, GOVERNOR, OPS, PRODUCT_MANAGER, PRODUCT_STRATEGIST
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminAction.java b/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminAction.java
new file mode 100644
index 0000000000..ffc5e4f947
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminAction.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.user;
+
+public enum UserAdminAction {
+
+ ADD_USER, UPDATE_USER, DELET_USER
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminValidator.java b/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminValidator.java
new file mode 100644
index 0000000000..8e736d4c0e
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserAdminValidator.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.user;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class UserAdminValidator {
+
+ private Pattern emailPat;
+ private Pattern userIdPat;
+ private Matcher matcher;
+
+ private static UserAdminValidator userAdminValidator = null;
+
+ public static synchronized UserAdminValidator getInstance() {
+ if (userAdminValidator == null) {
+ userAdminValidator = new UserAdminValidator();
+ }
+ return userAdminValidator;
+ }
+
+ private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
+
+ //private static final String USER_ID_PATTERN = "[mM]{1}[0-9]{5}|[a-zA-Z]{2}[0-9]{4}|[a-zA-Z]{2}[0-9]{4}|[a-zA-Z]{2}[0-9]{3}[a-zA-Z]{1}";
+ private static final String USER_ID_PATTERN = "^[\\s\\w_.-]{1,50}$";
+
+ private UserAdminValidator() {
+ emailPat = Pattern.compile(EMAIL_PATTERN);
+ userIdPat = Pattern.compile(USER_ID_PATTERN);
+ }
+
+ public boolean validateEmail(final String hex) {
+ matcher = emailPat.matcher(hex);
+ return matcher.matches();
+ }
+
+ public boolean validateUserId(String userId) {
+ matcher = userIdPat.matcher(userId);
+ return matcher.matches();
+ }
+
+ public boolean validateRole(String role) {
+ for (Role r : Role.values()) {
+ if (r.name().equals(role)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserBusinessLogic.java
new file mode 100644
index 0000000000..8a910fc566
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/user/UserBusinessLogic.java
@@ -0,0 +1,714 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.user;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+import javax.servlet.ServletContext;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.openecomp.portalsdk.core.onboarding.ueb.FunctionalMenu;
+import org.openecomp.portalsdk.core.onboarding.ueb.UebException;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.FunctionalMenuInfo;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IUserAdminOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.api.UserRoleEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.kpi.api.ASDCKpiApi;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("userBusinessLogic")
+public class UserBusinessLogic implements IUserBusinessLogic {
+
+ private static Logger log = LoggerFactory.getLogger(UserBusinessLogic.class.getName());
+ private static UserAdminValidator userAdminValidator = UserAdminValidator.getInstance();
+
+ @Resource
+ private IUserAdminOperation userAdminOperation;
+ @Resource
+ private ComponentsUtils componentsUtils;
+ @Autowired
+ private TitanGenericDao titanDao;
+
+ @Override
+ public Either<User, ActionStatus> getUser(String userId, boolean inTransaction) {
+ return userAdminOperation.getUserData(userId, inTransaction);
+ }
+
+ @Override
+ public Either<User, ResponseFormat> createUser(User modifier, User newUser) {
+
+ ResponseFormat responseFormat;
+ String modifierUserId = modifier.getUserId();
+
+ if (modifierUserId == null) {
+ modifier.setUserId("UNKNOWN");
+ log.debug("createUser method - user header is missing");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> eitherCreator = getUser(modifierUserId, false);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ log.debug("createUser method - user is not listed. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+
+ modifier = eitherCreator.left().value();
+ if (!modifier.getRole().equals(UserRoleEnum.ADMIN.getName())) {
+ log.debug("createUser method - user is not admin={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+
+ // verify user not exist
+ User userFromDb = new User();
+ // boolean isUserAlreadyExist = false;
+ Either<User, ActionStatus> eitherUserInDB = getUser(newUser.getUserId(), false);
+ if (eitherUserInDB.isRight()) {
+ ActionStatus status = eitherUserInDB.right().value();
+ if (!ActionStatus.USER_NOT_FOUND.equals(status) && !ActionStatus.USER_INACTIVE.equals(status)) {
+ responseFormat = componentsUtils.getResponseFormat(eitherUserInDB.right().value(), newUser.getUserId());
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+ } else {// User exist in DB
+ userFromDb = eitherUserInDB.left().value();
+ // isUserAlreadyExist = true;
+ if (userFromDb.getStatus() == UserStatusEnum.ACTIVE) {
+ responseFormat = componentsUtils.getResponseFormatByUserId(ActionStatus.USER_ALREADY_EXIST, newUser.getUserId());
+ log.debug("createUser method - user already exist with id: {}", modifier.getUserId(), userFromDb.getUserId());
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+ }
+
+ newUser.setStatus(UserStatusEnum.ACTIVE);
+
+ // validate Email
+ if (newUser.getEmail() != null && !userAdminValidator.validateEmail(newUser.getEmail())) {
+ log.debug("createUser method - user has invalid email={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_EMAIL_ADDRESS, newUser.getEmail());
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+
+ // validate Role
+ if (newUser.getRole() == null || newUser.getRole().length() == 0) {
+ newUser.setRole(Role.DESIGNER.name());
+ } else {
+ if (!userAdminValidator.validateRole(newUser.getRole())) {
+ log.debug("createUser method - user has invalid role={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_ROLE, newUser.getRole());
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+ }
+
+ // handle last login if user is import
+ if (newUser.getLastLoginTime() == null) {
+ newUser.setLastLoginTime(0L);
+ }
+
+ Either<User, StorageOperationStatus> addOrUpdateUserReq;
+
+ if (ActionStatus.USER_INACTIVE.equals(eitherUserInDB.right().value())) { // user
+ // exist
+ // with
+ // inactive
+ // state
+ // -
+ // update
+ // user
+ // data
+ newUser.setLastLoginTime(0L);
+ addOrUpdateUserReq = userAdminOperation.updateUserData(newUser);
+
+ } else { // user not exist - create new user
+ if (newUser.getUserId() != null && !userAdminValidator.validateUserId(newUser.getUserId())) {
+ log.debug("createUser method - user has invalid userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_USER_ID, newUser.getUserId());
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.right(responseFormat);
+ }
+ addOrUpdateUserReq = userAdminOperation.saveUserData(newUser);
+ }
+
+ if (addOrUpdateUserReq.isRight() || addOrUpdateUserReq.left().value() == null) {
+ log.debug("createUser method - failed to create user");
+ Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(addOrUpdateUserReq.right().value())));
+ }
+ log.debug("createUser method - user created");
+ User createdUser = addOrUpdateUserReq.left().value();
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.CREATED);
+ handleAuditing(modifier, null, createdUser, responseFormat, AuditingActionEnum.ADD_USER);
+ return Either.left(createdUser);
+ }
+
+ @Override
+ public Either<User, ResponseFormat> updateUserRole(User modifier, String userIdToUpdate, String userRole) {
+
+ ResponseFormat responseFormat;
+ String modifierUserId = modifier.getUserId();
+
+ if (modifierUserId == null) {
+ modifier.setUserId("UNKNOWN");
+ log.debug("updateUserRole method - user header is missing");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> eitherCreator = getUser(modifierUserId, false);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ log.debug("updateUserRole method - user is not listed. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+
+ modifier = eitherCreator.left().value();
+ if (!modifier.getRole().equals(UserRoleEnum.ADMIN.getName())) {
+ log.debug("updateUserRole method - user is not admin. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+
+ if (modifier.getUserId().equals(userIdToUpdate)) {
+ log.debug("updateUserRole method - admin role can only be updated by other admin. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.UPDATE_USER_ADMIN_CONFLICT);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> userToUpdateReq = getUser(userIdToUpdate, false);
+ if (userToUpdateReq.isRight() || userToUpdateReq.left().value() == null) {
+ log.debug("updateUserRole method - user not found. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.USER_NOT_FOUND, userIdToUpdate);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+
+ if (!userAdminValidator.validateRole(userRole)) {
+ log.debug("updateUserRole method - user has invalid role={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_ROLE, userRole);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+
+ User newUser = new User();
+ newUser.setRole(userRole);
+ newUser.setUserId(userIdToUpdate);
+ User userToUpdate = userToUpdateReq.left().value();
+ // if(!userRole.equals(UserRoleEnum.ADMIN.getName())){ //this is in
+ // comment until admin will be able to do do check-in/check-out from the
+ // UI
+
+ Either<List<Edge>, StorageOperationStatus> userPendingTasksReq = getPandingUserPandingTasksWithCommit(userToUpdate);
+ if (userPendingTasksReq.isRight()) {
+ log.debug("updateUserRole method - failed to get user pending tasks list", userIdToUpdate);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(userPendingTasksReq.right().value())));
+ }
+
+ List<Edge> userPendingTasks = userPendingTasksReq.left().value();
+ if (userPendingTasks.size() > 0) {
+ log.debug("updateUserRole method - User canot be updated, user have panding projects", userIdToUpdate);
+ String userTasksStatusForErrorMessage = getUserPandingTaskStatusByRole(UserRoleEnum.valueOf(userToUpdate.getRole()));
+ String userInfo = userToUpdate.getFirstName() + " " + userToUpdate.getLastName() + '(' + userToUpdate.getUserId() + ')';
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS, userInfo, userTasksStatusForErrorMessage);
+ handleAuditing(modifier, userToUpdate, userToUpdate, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.right(responseFormat);
+ }
+ // }
+ Either<User, StorageOperationStatus> updateUserReq = userAdminOperation.updateUserData(newUser);
+
+ if (updateUserReq.isRight() || updateUserReq.left().value() == null) {
+ log.debug("updateUser method - failed to update user data. userId={}", modifier.getUserId());
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateUserReq.right().value())));
+ }
+
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ User updatedUser = updateUserReq.left().value();
+ handleAuditing(modifier, userToUpdate, updatedUser, responseFormat, AuditingActionEnum.UPDATE_USER);
+ return Either.left(updatedUser);
+ }
+
+ @Override
+ public Either<List<User>, ResponseFormat> getAllAdminUsers(ServletContext context) {
+ Either<List<User>, ActionStatus> response = userAdminOperation.getAllUsersWithRole(Role.ADMIN.name(), null);
+
+ if (response.isRight()) {
+ ResponseFormat responseFormat = componentsUtils.getResponseFormat(response.right().value());
+ return Either.right(responseFormat);
+ }
+ return Either.left(response.left().value());
+ }
+
+ @Override
+ public Either<List<User>, ResponseFormat> getUsersList(String modifierAttId, List<String> roles, String rolesStr) {
+ ResponseFormat responseFormat;
+ User user = new User();
+ if (modifierAttId == null) {
+ user.setUserId("UNKNOWN");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ handleGetUsersListAuditing(user, responseFormat, rolesStr);
+ return Either.right(responseFormat);
+ }
+ Either<User, ActionStatus> userResult = getUser(modifierAttId, false);
+ if (userResult.isRight()) {
+ user.setUserId(modifierAttId);
+ if (userResult.right().value().equals(ActionStatus.USER_NOT_FOUND)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ } else {
+ responseFormat = componentsUtils.getResponseFormat(userResult.right().value());
+ }
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeUserMissingError, "Get users per roles", modifierAttId);
+ BeEcompErrorManager.getInstance().logBeUserMissingError("Get users per roles", modifierAttId);
+
+ handleGetUsersListAuditing(user, responseFormat, rolesStr);
+ return Either.right(responseFormat);
+ }
+ user = userResult.left().value();
+ Either<List<User>, ResponseFormat> getResponse = null;
+ List<User> resultList = new ArrayList<>();
+ if (roles != null && !roles.isEmpty()) {
+ for (String role : roles) {
+ if (!userAdminValidator.validateRole(role)) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.INVALID_ROLE, role);
+ handleGetUsersListAuditing(user, responseFormat, rolesStr);
+ return Either.right(responseFormat);
+ }
+ getResponse = getUsersPerRole(role, user, rolesStr);
+ resultList.addAll(getResponse.left().value());
+ }
+ } else {
+ rolesStr = "All";
+ getResponse = getUsersPerRole(null, user, rolesStr);
+ resultList.addAll(getResponse.left().value());
+ }
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleGetUsersListAuditing(user, responseFormat, rolesStr);
+ return Either.left(resultList);
+ }
+
+ private Either<List<User>, ResponseFormat> getUsersPerRole(String role, User user, String rolesStr) {
+ ResponseFormat responseFormat;
+ Either<List<User>, ActionStatus> response = userAdminOperation.getAllUsersWithRole(role, UserStatusEnum.ACTIVE.name());
+ if (response.isRight()) {
+ responseFormat = componentsUtils.getResponseFormat(response.right().value());
+ handleGetUsersListAuditing(user, responseFormat, rolesStr);
+ return Either.right(responseFormat);
+ }
+ return Either.left(response.left().value());
+ }
+
+ private void handleGetUsersListAuditing(User user, ResponseFormat responseFormat, String details) {
+ componentsUtils.auditGetUsersList(AuditingActionEnum.GET_USERS_LIST, user, details, responseFormat);
+ }
+
+ private void handleAuditing(User modifier, User userBefor, User userAfter, ResponseFormat responseFormat, AuditingActionEnum actionName) {
+ componentsUtils.auditAdminUserAction(actionName, modifier, userBefor, userAfter, responseFormat);
+ }
+
+ private void handleUserAccessAuditing(User user, ResponseFormat responseFormat, AuditingActionEnum actionName) {
+ componentsUtils.auditUserAccess(actionName, user, responseFormat);
+ }
+
+ @Override
+ public Either<User, ResponseFormat> deActivateUser(User modifier, String userUniuqeIdToDeactive) {
+
+ ResponseFormat responseFormat;
+ String userId = modifier.getUserId();
+
+ if (userId == null) {
+ modifier.setUserId("UNKNOWN");
+ log.debug("deActivateUser method - user header is missing");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> eitherCreator = getUser(userId, false);
+ if (eitherCreator.isRight() || eitherCreator.left().value() == null) {
+ log.debug("deActivateUser method - user is not listed. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(responseFormat);
+ }
+
+ modifier = eitherCreator.left().value();
+
+ if (!modifier.getRole().equals(UserRoleEnum.ADMIN.getName())) {
+ log.debug("deActivateUser method - user is not admin. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(responseFormat);
+ }
+
+ if (modifier.getUserId().equals(userUniuqeIdToDeactive)) {
+ log.debug("deActivateUser deActivateUser - admin can only be deactivate by other admin. userId={}", modifier.getUserId());
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.DELETE_USER_ADMIN_CONFLICT);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> getUserToDeleteResponse = getUser(userUniuqeIdToDeactive, false);
+ if (getUserToDeleteResponse.isRight() || getUserToDeleteResponse.left().value() == null) {
+ log.debug("deActivateUser method - failed to get user by id {}", userUniuqeIdToDeactive);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.USER_NOT_FOUND, userUniuqeIdToDeactive);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(componentsUtils.getResponseFormat(getUserToDeleteResponse.right().value(), userUniuqeIdToDeactive));
+ }
+
+ User userToDeactivate = getUserToDeleteResponse.left().value();
+ if (userToDeactivate.getStatus().equals(UserStatusEnum.INACTIVE)) {
+ log.debug("deActivateUser method - User already inactive", userUniuqeIdToDeactive);
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.USER_NOT_FOUND, userUniuqeIdToDeactive);
+ handleAuditing(modifier, null, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<List<Edge>, StorageOperationStatus> userPendingTasksReq = getPandingUserPandingTasksWithCommit(userToDeactivate);
+ if (userPendingTasksReq.isRight()) {
+ log.debug("deActivateUser method - failed to get user pending tasks list", userUniuqeIdToDeactive);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(userPendingTasksReq.right().value())));
+ }
+
+ List<Edge> userPendingTasks = userPendingTasksReq.left().value();
+ if (userPendingTasks.size() > 0) {
+ log.debug("deActivateUser method - User canot be deleted, user have panding projects", userUniuqeIdToDeactive);
+
+ String userTasksStatusForErrorMessage = getUserPandingTaskStatusByRole(UserRoleEnum.valueOf(userToDeactivate.getRole()));
+ String userInfo = userToDeactivate.getFirstName() + " " + userToDeactivate.getLastName() + '(' + userToDeactivate.getUserId() + ')';
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS, userInfo, userTasksStatusForErrorMessage);
+ handleAuditing(modifier, userToDeactivate, userToDeactivate, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, StorageOperationStatus> deactivateUserReq = userAdminOperation.deActivateUser(userToDeactivate);
+ if (deactivateUserReq.isRight()) {
+ log.debug("deActivateUser method - failed to deactivate user", userUniuqeIdToDeactive);
+ return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(deactivateUserReq.right().value())));
+ }
+ User deactivateUser = deactivateUserReq.left().value();
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleAuditing(modifier, userToDeactivate, null, responseFormat, AuditingActionEnum.DELETE_USER);
+ return Either.left(deactivateUser);
+ }
+
+ @Override
+ public Either<User, ResponseFormat> authorize(User authUser) {
+
+ ResponseFormat responseFormat;
+
+ String userId = authUser.getUserId();
+
+ if (userId == null) {
+ authUser.setUserId("UNKNOWN");
+ log.debug("deActivateUser method - user header is missing");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ handleUserAccessAuditing(authUser, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> eitherCreator = getUser(userId, false);
+ if (eitherCreator.isRight()) {
+ if (eitherCreator.right().value() == ActionStatus.USER_NOT_FOUND || eitherCreator.right().value() == ActionStatus.USER_INACTIVE) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_ACCESS);
+ handleUserAccessAuditing(authUser, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.right(responseFormat);
+ } else {
+ return Either.right(componentsUtils.getResponseFormatByUser(eitherCreator.right().value(), authUser));
+ }
+ } else {
+ if (eitherCreator.left().value() == null) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return Either.right(responseFormat);
+ }
+ }
+
+ User user = eitherCreator.left().value();
+
+ String firstName = authUser.getFirstName();
+ if (firstName != null && firstName.isEmpty() == false && !firstName.equals(user.getFirstName())) {
+ user.setFirstName(firstName);
+ }
+
+ String lastName = authUser.getLastName();
+ if (lastName != null && lastName.isEmpty() == false && !lastName.equals(user.getLastName())) {
+ user.setLastName(lastName);
+ }
+
+ String email = authUser.getEmail();
+ if (email != null && false == email.isEmpty() && !email.equals(user.getEmail())) {
+ user.setEmail(email);
+ }
+
+ // last login time stamp handle
+ user.setLastLoginTime();
+
+ Either<User, StorageOperationStatus> updateUserReq = userAdminOperation.updateUserData(user);
+
+ if (updateUserReq.isRight()) {
+ responseFormat = componentsUtils.getResponseFormatByUser(eitherCreator.right().value(), user);
+ handleUserAccessAuditing(user, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.right(componentsUtils.getResponseFormatByUser(eitherCreator.right().value(), user));
+ }
+
+ User updatedUser = updateUserReq.left().value();
+
+ Long lastLoginTime = user.getLastLoginTime();
+ if (lastLoginTime != null) {
+ updatedUser.setLastLoginTime(lastLoginTime);
+ } else {
+ updatedUser.setLastLoginTime(new Long(0));
+ }
+
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleUserAccessAuditing(updatedUser, responseFormat, AuditingActionEnum.USER_ACCESS);
+ ASDCKpiApi.countUsersAuthorizations();
+ return Either.left(updatedUser);
+ }
+
+ /*
+ * The method updates user credentials only, the role is neglected The role updated through updateRole method
+ */
+ public Either<User, ResponseFormat> updateUserCredentials(User updatedUserCred) {
+
+ ResponseFormat responseFormat;
+
+ String userId = updatedUserCred.getUserId();
+
+ if (userId == null) {
+ updatedUserCred.setUserId("UNKNOWN");
+ log.debug("updateUserCredentials method - user header is missing");
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.MISSING_INFORMATION);
+ handleUserAccessAuditing(updatedUserCred, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.right(responseFormat);
+ }
+
+ Either<User, ActionStatus> eitherCreator = getUser(userId, false);
+ if (eitherCreator.isRight()) {
+ ActionStatus status = eitherCreator.right().value();
+ if (status == ActionStatus.USER_NOT_FOUND || status == ActionStatus.USER_INACTIVE) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_ACCESS);
+ handleUserAccessAuditing(updatedUserCred, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.right(responseFormat);
+ } else {
+ return Either.right(componentsUtils.getResponseFormatByUser(status, updatedUserCred));
+ }
+ } else {
+ if (eitherCreator.left().value() == null) {
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR);
+ return Either.right(responseFormat);
+ }
+ }
+
+ User user = eitherCreator.left().value();
+
+ String firstName = updatedUserCred.getFirstName();
+ if (firstName != null && firstName.isEmpty() == false && !firstName.equals(user.getFirstName())) {
+ user.setFirstName(firstName);
+ }
+
+ String lastName = updatedUserCred.getLastName();
+ if (lastName != null && lastName.isEmpty() == false && !lastName.equals(user.getLastName())) {
+ user.setLastName(lastName);
+ }
+
+ String email = updatedUserCred.getEmail();
+ if (email != null && false == email.isEmpty() && !email.equals(user.getEmail())) {
+ user.setEmail(email);
+ }
+
+ if (updatedUserCred.getLastLoginTime() != null && user.getLastLoginTime() != null) {
+ if (updatedUserCred.getLastLoginTime() > user.getLastLoginTime()) {
+ user.setLastLoginTime(updatedUserCred.getLastLoginTime());
+ }
+ } else if (updatedUserCred.getLastLoginTime() != null && user.getLastLoginTime() == null) {
+ user.setLastLoginTime(updatedUserCred.getLastLoginTime());
+ }
+
+ Either<User, StorageOperationStatus> updateUserReq = userAdminOperation.updateUserData(user);
+
+ if (updateUserReq.isRight()) {
+ responseFormat = componentsUtils.getResponseFormatByUser(eitherCreator.right().value(), user);
+ handleUserAccessAuditing(user, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.right(componentsUtils.getResponseFormatByUser(eitherCreator.right().value(), user));
+ }
+
+ User updatedUser = updateUserReq.left().value();
+
+ responseFormat = componentsUtils.getResponseFormat(ActionStatus.OK);
+ handleUserAccessAuditing(updatedUser, responseFormat, AuditingActionEnum.USER_ACCESS);
+ return Either.left(updatedUser);
+ }
+
+ private Either<List<Edge>, StorageOperationStatus> getPandingUserPandingTasksWithCommit(User user) {
+
+ Either<List<Edge>, StorageOperationStatus> result = null;
+
+ try {
+ UserRoleEnum userRole = UserRoleEnum.valueOf(user.getRole());
+ Map<String, Object> properties = new HashMap<String, Object>();
+ switch (userRole) {
+ case DESIGNER:
+ case PRODUCT_STRATEGIST:
+ case PRODUCT_MANAGER:
+ properties.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ return userAdminOperation.getUserPandingTasksList(user, properties);
+ case TESTER:
+ properties.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ return userAdminOperation.getUserPandingTasksList(user, properties);
+ case ADMIN:
+ properties.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS.name());
+ properties.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ return userAdminOperation.getUserPandingTasksList(user, properties);
+ default:
+ return Either.left(new ArrayList<>());
+ }
+ } finally {
+ // commit will be perform outside!!!
+ if (result == null || result.isRight()) {
+ log.debug("getUserPandingTasksList failed to perform fetching");
+ titanDao.rollback();
+ } else {
+ titanDao.commit();
+ }
+ }
+ }
+
+ private String getUserPandingTaskStatusByRole(UserRoleEnum role) {
+
+ switch (role) {
+ case DESIGNER:
+ case PRODUCT_STRATEGIST:
+ case PRODUCT_MANAGER:
+ return "checked-out";
+
+ case TESTER:
+ return "in-certification";
+ case ADMIN:
+ return "in-certification/checked-out";
+ default:
+ return "";
+ }
+ }
+
+ /**
+ * return the functional menu of a given user
+ *
+ * @param userId
+ * @param inTransaction
+ * @return
+ */
+ public Either<FunctionalMenuInfo, ActionStatus> getFunctionalMenu(String userId) {
+
+ boolean toCommit = false;
+
+ FunctionalMenuInfo functionalMenuInfo = new FunctionalMenuInfo();
+
+ try {
+
+ Either<ImmutablePair<User, FunctionalMenuInfo>, ActionStatus> userResult = userAdminOperation.getUserDataWithFunctionalMenu(userId);
+ if (userResult.isRight()) {
+ ActionStatus actionStatus = userResult.right().value();
+ if (actionStatus == ActionStatus.USER_NOT_FOUND) {
+ actionStatus = ActionStatus.INVALID_USER_ID;
+ }
+ return Either.right(actionStatus);
+ }
+
+ ImmutablePair<User, FunctionalMenuInfo> immutablePair = userResult.left().value();
+ FunctionalMenuInfo currentFunctionalMenu = immutablePair.right;
+ String currentMenuStr = currentFunctionalMenu != null ? currentFunctionalMenu.getFunctionalMenu() : null;
+
+ String functionalMenu = getFunctionalMenuFromUeb(userId);
+
+ // functionalMenu can be null or since we catch UebException
+ if (functionalMenu != null && false == functionalMenu.isEmpty()) {
+ functionalMenuInfo.setFunctionalMenu(functionalMenu);
+ if (false == functionalMenu.equals(currentMenuStr)) {
+ log.debug("Going to update functional menu of user {}. Functional menu is {}", userId, functionalMenu);
+ userAdminOperation.createOrUpdateFunctionalMenu(userId, functionalMenu);
+ }
+ } else {
+ String menu = currentMenuStr;
+ if (menu == null) {
+ menu = "[]";
+ }
+ log.debug("Fetch functional menu from old request. Functional menu is {}", menu);
+ functionalMenuInfo.setFunctionalMenu(menu);
+ }
+
+ toCommit = true;
+
+ } finally {
+ if (toCommit) {
+ titanDao.commit();
+ } else {
+ titanDao.rollback();
+ }
+ }
+
+ return Either.left(functionalMenuInfo);
+ }
+
+ private String getFunctionalMenuFromUeb(String userId) {
+ String functionalMenu = null;
+ try {
+ log.debug("Before calling to FunctionalMenu method for user {}", userId);
+ functionalMenu = FunctionalMenu.get(userId);
+ log.debug("Functional menu fetched is {}", functionalMenu);
+
+ } catch (UebException e) {
+ log.debug("Failed to fetch 'functional menu' of user {} from ecomp portal(via UEB). {}", userId, e);
+ BeEcompErrorManager.getInstance().logInternalFlowError("FetchFunctionalMenu", "Failed to fetch 'functional menu'", ErrorSeverity.ERROR);
+ }
+ return functionalMenu;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ICommitHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ICommitHandler.java
new file mode 100644
index 0000000000..e70b33a938
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ICommitHandler.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.api;
+
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+
+public interface ICommitHandler extends IDBType {
+ DBActionCodeEnum doCommit();
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBAction.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBAction.java
new file mode 100644
index 0000000000..a36e5fe1b5
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBAction.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.api;
+
+public interface IDBAction {
+ <T> T doAction();
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBType.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBType.java
new file mode 100644
index 0000000000..4c19e41609
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/IDBType.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.api;
+
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+
+public interface IDBType {
+ DBTypeEnum getDBType();
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ITransactionSdnc.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ITransactionSdnc.java
new file mode 100644
index 0000000000..5a10e9029c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/ITransactionSdnc.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.api;
+
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ESActionTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.TransactionCodeEnum;
+
+import fj.data.Either;
+
+public interface ITransactionSdnc {
+ TransactionCodeEnum finishTransaction();
+
+ Either<DBActionCodeEnum, TransactionCodeEnum> invokeESAction(boolean isLastAction, ESActionTypeEnum esActiontype, ESArtifactData artifactData);
+
+ <T> Either<T, TransactionCodeEnum> invokeGeneralDBAction(boolean isLastAction, DBTypeEnum dbType, IDBAction dbAction, IDBAction dbRollbackAction);
+
+ <T> Either<T, TransactionCodeEnum> invokeTitanAction(boolean isLastAction, IDBAction dbAction);
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/RollbackHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/RollbackHandler.java
new file mode 100644
index 0000000000..97a8083e88
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/RollbackHandler.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.api;
+
+import java.util.Stack;
+
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.LogMessages;
+import org.openecomp.sdc.common.util.MethodActivationStatusEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class RollbackHandler implements IDBType {
+ private static Logger log = LoggerFactory.getLogger(RollbackHandler.class.getName());
+ private Stack<IDBAction> dbActionRollbacks;
+
+ private Integer transactionId;
+ private String userId, actionType;
+
+ protected RollbackHandler(Integer transactionId, String userId, String actionType) {
+ if (isRollbackForPersistenceData()) {
+ dbActionRollbacks = new Stack<>();
+ }
+ this.transactionId = transactionId;
+ this.userId = userId;
+ this.actionType = actionType;
+ }
+
+ public MethodActivationStatusEnum addRollbackAction(IDBAction rollbackAction) {
+ MethodActivationStatusEnum result = MethodActivationStatusEnum.SUCCESS;
+ if (isRollbackForPersistenceData()) {
+ dbActionRollbacks.push(rollbackAction);
+ } else {
+ result = MethodActivationStatusEnum.NOT_ALLOWED;
+ }
+ return result;
+ }
+
+ public DBActionCodeEnum doRollback() {
+ DBActionCodeEnum result;
+
+ try {
+ if (isRollbackForPersistenceData()) {
+ result = doPersistenceDataRollback();
+ } else {
+ log.debug(LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, getDBType().name(), transactionId, userId, actionType);
+ log.debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, getDBType().name(), transactionId, userId, actionType);
+ result = doNonPersistenceDataRollback();
+ }
+ if (result != DBActionCodeEnum.SUCCESS) {
+ log.error(LogMessages.ROLLBACK_FAILED_ON_DB, transactionId, getDBType().name(), userId, actionType);
+ log.error(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_FAILED_ON_DB, transactionId, getDBType().name(), userId, actionType);
+ }
+
+ } catch (Exception e) {
+ result = DBActionCodeEnum.FAIL_GENERAL;
+ log.error(LogMessages.ROLLBACK_FAILED_ON_DB_WITH_EXCEPTION, transactionId, getDBType().name(), e.getMessage(), userId, actionType);
+ log.debug(LogMessages.ROLLBACK_FAILED_ON_DB_WITH_EXCEPTION, transactionId, getDBType().name(), e.getMessage(), userId, actionType, e);
+ log.error(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_FAILED_ON_DB_WITH_EXCEPTION, transactionId, getDBType().name(), e.getMessage(), userId, actionType);
+ }
+
+ return result;
+ }
+
+ private <T> DBActionCodeEnum doPersistenceDataRollback() {
+ DBActionCodeEnum result = DBActionCodeEnum.SUCCESS;
+ while (!dbActionRollbacks.empty()) {
+ log.debug(LogMessages.ROLLBACK_PERSISTENT_ACTION, getDBType().name(), transactionId, userId, actionType);
+ log.debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_PERSISTENT_ACTION, getDBType().name(), transactionId, userId, actionType);
+ IDBAction rollbackAction = dbActionRollbacks.pop();
+ T rollbackResult = rollbackAction.doAction();
+ if (!isRollbackResultValid(rollbackResult)) {
+ result = DBActionCodeEnum.FAIL_GENERAL;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Override for specific logic
+ *
+ * @param <T>
+ */
+ public <T> boolean isRollbackResultValid(T rollbackResult) {
+ return true;
+ }
+
+ /**
+ * Override for specific logic
+ */
+ public DBActionCodeEnum doNonPersistenceDataRollback() {
+ return DBActionCodeEnum.SUCCESS;
+ }
+
+ protected abstract boolean isRollbackForPersistenceData();
+
+ /**
+ * Only Used for Unit Tests !
+ */
+ public static void setLog(Logger log) {
+ RollbackHandler.log = log;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/TransactionUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/TransactionUtils.java
new file mode 100644
index 0000000000..e90b9618d4
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/api/TransactionUtils.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.api;
+
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+
+public final class TransactionUtils {
+
+ private TransactionUtils() {
+
+ }
+
+ public enum DBTypeEnum {
+ ELASTIC_SEARCH, TITAN, MYSTERY, SWIFT
+ }
+
+ public enum TransactionCodeEnum {
+ ROLLBACK_SUCCESS, ROLLBACK_FAILED, SUCCESS, COMMIT_FAILED, MULTIPLE_LAST_ACTION, INTERNAL_ERROR, UNALLOWED_METHOD_USAGE, PREPARE_ROLLBACK_FAILED, TRANSACTION_CLOSED
+ }
+
+ public enum TransactionStatusEnum {
+ OPEN, CLOSED, FAILED_ROLLBACK
+ }
+
+ public enum DBActionCodeEnum {
+ SUCCESS, FAIL_GENERAL, FAIL_MULTIPLE_LAST_ACTION
+ }
+
+ public enum ESActionTypeEnum {
+ ADD_ARTIFACT, UPDATE_ARTIFACT, REMOVE_ARTIFACT
+ }
+
+ public enum ActionTypeEnum {
+ ADD_ARTIFACT, REMOVE_ARTIFACT, UPDATE_ARTIFACT, CREATE_RESOURCE, UPDATE_RESOURCE, DELETE_RESOURCE
+ }
+
+ public static final int MAX_SIZE_TRANSACTION_LIST = 1000;
+
+ public static final int TRANSACTION_ID_RESET_LIMIT = MAX_SIZE_TRANSACTION_LIST * 10;
+
+ public static final String TRANSACTION_MARKER_STR = "TRANSACTION_MARKER";
+
+ public static final String DUMMY_USER = "udummy";
+
+ public static final Marker TRANSACTION_MARKER = MarkerFactory.getMarker(TRANSACTION_MARKER_STR);
+
+ public static class LogMessages {
+ public static final String PRE_INVOKE_ACTION = "About to add Action to SdncTransaction ID:{} for DB:{}, user:{}, action:{}";
+ public static final String INVOKE_ACTION = "invoking Action in SdncTransactionID:{}, on DB:{}, user:{}, action:{}";
+ public static final String COMMIT_ACTION_ALL_DB = "Starting Commit for SdncTransactionID:{} for all DBs, user:{}, action:{}";
+ public static final String COMMIT_ACTION_SPECIFIC_DB = "Starting Commit for SdncTransactionID:{} on DB:{}, user:{}, action:{}";
+ public static final String COMMIT_ON_CLOSED_TRANSACTION = "Commit failed for SdncTransactionID:{} Transaction is in status:{}, user:{}, action:{}";
+ public static final String ACTION_ON_CLOSED_TRANSACTION = "Action failed for SdncTransactionID:{} Transaction is already closed, user:{}, action:{}";
+ public static final String DOUBLE_FINISH_FLAG_ACTION = "Transaction with SdncTransactionID:{} on DB:{} was called multiple times with last action flag, user:{}, action:{}";
+ public static final String DB_ACTION_FAILED_WITH_EXCEPTION = "Action on DB:{} has failed for SdncTransactionID:{}, message:{}, user:{}, action:{}";
+ public static final String ROLLBACK_SUCCEEDED_GENERAL = "Transaction rollback succeeded for SdncTransactionID:{}, user:{}, action:{}";
+ public static final String ROLLBACK_FAILED_GENERAL = "Transaction rollback failed for SdncTransactionID:{}, user:{}, action:{}";
+ public static final String ROLLBACK_FAILED_ON_DB = "Transaction rollback failed for SdncTransactionID:{} on DB:{}, user:{}, action:{}";
+ public static final String ROLLBACK_FAILED_ON_DB_WITH_EXCEPTION = "Transaction rollback failed for SdncTransactionID:{} on DB:{}, exception message:{}, user:{}, action:{}";
+ public static final String ROLLBACK_PERSISTENT_ACTION = "About To Rollback Action on DB{} for TransactionID:{} ... user:{}, action:{}";
+ public static final String ROLLBACK_NON_PERSISTENT_ACTION = "About To Rollback Actions on DB{} for TransactionID:{} ... user:{}, action:{}";
+ public static final String CREATE_ROLLBACK_HANDLER = "creating new Rollback Handler For DB:{} in SdncTransaction ID:{}, user:{}, action:{}";
+ public static final String FAILED_CREATE_ROLLBACK = "failed to create new Rollback action For DB:{} with SdncTransactionID:{}, user:{}, action:{}";
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESAction.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESAction.java
new file mode 100644
index 0000000000..33a8d1163b
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESAction.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.impl;
+
+import org.openecomp.sdc.be.dao.impl.ESCatalogDAO;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.exception.ResourceDAOException;
+import org.openecomp.sdc.common.transaction.api.IDBAction;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ESActionTypeEnum;
+
+public class ESAction implements IDBAction {
+
+ private ESCatalogDAO esCatalogDao;
+ private ESArtifactData artifactData;
+ private ESActionTypeEnum esActionType;
+
+ public ESAction(ESCatalogDAO esCatalogDao, ESArtifactData artifactData, ESActionTypeEnum esActiontype) {
+ this.esCatalogDao = esCatalogDao;
+ this.artifactData = artifactData;
+ this.esActionType = esActiontype;
+ }
+
+ @Override
+ public DBActionCodeEnum doAction() {
+ DBActionCodeEnum result = DBActionCodeEnum.SUCCESS;
+ try {
+ if (esActionType == ESActionTypeEnum.ADD_ARTIFACT || esActionType == ESActionTypeEnum.UPDATE_ARTIFACT) {
+ esCatalogDao.writeArtifact(artifactData);
+ } else if (esActionType == ESActionTypeEnum.REMOVE_ARTIFACT) {
+ esCatalogDao.deleteArtifact(artifactData.getId());
+ }
+
+ } catch (ResourceDAOException daoException) {
+ result = DBActionCodeEnum.FAIL_GENERAL;
+ }
+ return result;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESRollbackHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESRollbackHandler.java
new file mode 100644
index 0000000000..b9fb0d31b7
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/ESRollbackHandler.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.impl;
+
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.dao.impl.ESCatalogDAO;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.transaction.api.RollbackHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ESActionTypeEnum;
+import org.openecomp.sdc.common.util.MethodActivationStatusEnum;
+
+import fj.data.Either;
+
+public class ESRollbackHandler extends RollbackHandler {
+
+ public ESRollbackHandler(Integer transactionId, String userId, String actionType) {
+ super(transactionId, userId, actionType);
+ }
+
+ public DBTypeEnum getDBType() {
+ return DBTypeEnum.ELASTIC_SEARCH;
+ }
+
+ protected boolean isRollbackForPersistenceData() {
+ return true;
+ }
+
+ public boolean isRollbackResultValid(DBActionCodeEnum rollbackResult) {
+ return rollbackResult == DBActionCodeEnum.SUCCESS;
+ }
+
+ public Either<ESAction, MethodActivationStatusEnum> buildEsRollbackAction(ESCatalogDAO esCatalogDao, ESArtifactData artifactData, ESActionTypeEnum esActiontype) {
+ Either<ESAction, MethodActivationStatusEnum> result;
+
+ try {
+ ESAction esRollbackAction = null;
+ Either<ESArtifactData, ResourceUploadStatus> either = esCatalogDao.getArtifact(artifactData.getId());
+
+ switch (esActiontype) {
+ case ADD_ARTIFACT:
+
+ if (either.isRight() && either.right().value() == ResourceUploadStatus.NOT_EXIST) {
+ esRollbackAction = new ESAction(esCatalogDao, artifactData, ESActionTypeEnum.REMOVE_ARTIFACT);
+ }
+ break;
+ case REMOVE_ARTIFACT:
+ if (either.isLeft()) {
+ esRollbackAction = new ESAction(esCatalogDao, artifactData, ESActionTypeEnum.ADD_ARTIFACT);
+ }
+ break;
+ case UPDATE_ARTIFACT:
+
+ if (either.isLeft()) {
+ ESArtifactData originalArtifactData = either.left().value();
+ esRollbackAction = new ESAction(esCatalogDao, originalArtifactData, ESActionTypeEnum.UPDATE_ARTIFACT);
+ }
+ break;
+
+ }
+ if (esRollbackAction != null) {
+ result = Either.left(esRollbackAction);
+ } else {
+ result = Either.right(MethodActivationStatusEnum.FAILED);
+ }
+ } catch (Exception e) {
+ result = Either.right(MethodActivationStatusEnum.FAILED);
+ }
+
+ return result;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanCommitHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanCommitHandler.java
new file mode 100644
index 0000000000..0d2ec387cf
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanCommitHandler.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.impl;
+
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.common.transaction.api.ICommitHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+
+public class TitanCommitHandler implements ICommitHandler {
+
+ private TitanGenericDao titanGenericDao;
+
+ public TitanCommitHandler(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public DBActionCodeEnum doCommit() {
+ DBActionCodeEnum result = DBActionCodeEnum.SUCCESS;
+ TitanOperationStatus titanStatus = titanGenericDao.commit();
+ if (titanStatus != TitanOperationStatus.OK) {
+ result = DBActionCodeEnum.FAIL_GENERAL;
+ }
+ return result;
+ }
+
+ @Override
+ public DBTypeEnum getDBType() {
+ return DBTypeEnum.TITAN;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanRollbackHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanRollbackHandler.java
new file mode 100644
index 0000000000..c0c686723c
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/impl/TitanRollbackHandler.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.impl;
+
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.common.transaction.api.RollbackHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+
+public class TitanRollbackHandler extends RollbackHandler {
+
+ private TitanGenericDao titanGenericDao;
+
+ public TitanRollbackHandler(Integer transactionId, String userId, String actionType, TitanGenericDao titanGenericDao) {
+ super(transactionId, userId, actionType);
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ public DBTypeEnum getDBType() {
+ return DBTypeEnum.TITAN;
+ }
+
+ protected boolean isRollbackForPersistenceData() {
+ return false;
+ }
+
+ public DBActionCodeEnum doNonPersistenceDataRollback() {
+ DBActionCodeEnum result = DBActionCodeEnum.SUCCESS;
+ TitanOperationStatus titanStatus = titanGenericDao.rollback();
+ if (titanStatus != TitanOperationStatus.OK) {
+ result = DBActionCodeEnum.FAIL_GENERAL;
+ }
+ return result;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/CommitManager.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/CommitManager.java
new file mode 100644
index 0000000000..e802e58f27
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/CommitManager.java
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.mngr;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.transaction.api.ICommitHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.LogMessages;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CommitManager {
+ private List<ICommitHandler> commitHandlers;
+ private Integer transactionId;
+ private String userId, actionType;
+ private static Logger log = LoggerFactory.getLogger(CommitManager.class.getName());
+
+ CommitManager(Integer transactionId, String userId, String actionType, List<ICommitHandler> commitHandlers) {
+ this.commitHandlers = commitHandlers;
+ this.transactionId = transactionId;
+ this.userId = userId;
+ this.actionType = actionType;
+
+ }
+
+ public DBActionCodeEnum transactionCommit() {
+ log.debug(LogMessages.COMMIT_ACTION_ALL_DB, transactionId, userId, actionType);
+ DBActionCodeEnum commitResult = DBActionCodeEnum.SUCCESS;
+ ICommitHandler lastHandler = null;
+ try {
+ for (ICommitHandler handler : commitHandlers) {
+ lastHandler = handler;
+ log.debug(LogMessages.COMMIT_ACTION_SPECIFIC_DB, transactionId, handler.getDBType().name(), userId, actionType);
+ DBActionCodeEnum commitCode = handler.doCommit();
+ if (commitCode == DBActionCodeEnum.FAIL_GENERAL) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "transactionCommit on DB " + handler.getDBType().name());
+ BeEcompErrorManager.getInstance().logBeSystemError("transactionCommit on DB " + handler.getDBType().name());
+ log.debug("Commit failed for SdncTransactionID:{} on DB:{}", transactionId, handler.getDBType().name());
+ commitResult = DBActionCodeEnum.FAIL_GENERAL;
+ break;
+ }
+ log.debug("Commit succeeded for SdncTransactionID:{} on DB:{}", transactionId, handler.getDBType().name());
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "transactionCommit - on DB " + getDBName(lastHandler));
+ BeEcompErrorManager.getInstance().logBeSystemError("transactionCommit - on DB " + getDBName(lastHandler));
+ log.debug("Commit failed for SdncTransactionID:{} on DB:{}, Exception message:{}", transactionId, getDBName(lastHandler), e.getMessage(), e);
+ log.info(TransactionUtils.TRANSACTION_MARKER, "Commit failed for SdncTransactionID:{} on DB:{}", transactionId, getDBName(lastHandler));
+ commitResult = DBActionCodeEnum.FAIL_GENERAL;
+ }
+ return commitResult;
+ }
+
+ private String getDBName(ICommitHandler lastHandler) {
+ String dbName = "Unknown";
+ if (lastHandler != null) {
+ dbName = lastHandler.getDBType().name();
+ }
+ return dbName;
+ }
+
+ static void setLog(Logger log) {
+ CommitManager.log = log;
+ }
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/RollbackManager.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/RollbackManager.java
new file mode 100644
index 0000000000..0641524038
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/RollbackManager.java
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.mngr;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.common.transaction.api.RollbackHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+import org.openecomp.sdc.common.util.MethodActivationStatusEnum;
+
+import fj.data.Either;
+
+public class RollbackManager {
+ private Map<DBTypeEnum, RollbackHandler> rollBackHandlersMap;
+ private Integer transactionId;
+ private String userId, actionType;
+
+ RollbackManager(Integer transactionId, String userId, String actionType, List<RollbackHandler> roleBackHandlers) {
+ this.transactionId = transactionId;
+ this.userId = userId;
+ this.actionType = actionType;
+ rollBackHandlersMap = new HashMap<>();
+ for (RollbackHandler handler : roleBackHandlers) {
+ rollBackHandlersMap.put(handler.getDBType(), handler);
+ }
+
+ }
+
+ public DBActionCodeEnum transactionRollback() {
+ DBActionCodeEnum rollbackResult = DBActionCodeEnum.SUCCESS;
+ Iterator<RollbackHandler> handlersItr = rollBackHandlersMap.values().iterator();
+ while (handlersItr.hasNext()) {
+ RollbackHandler handler = handlersItr.next();
+ DBActionCodeEnum rollbackCode = handler.doRollback();
+ if (rollbackCode == DBActionCodeEnum.FAIL_GENERAL) {
+ rollbackResult = DBActionCodeEnum.FAIL_GENERAL;
+ }
+ }
+
+ return rollbackResult;
+ }
+
+ protected Either<RollbackHandler, MethodActivationStatusEnum> addRollbackHandler(RollbackHandler rollbackHandler) {
+ Either<RollbackHandler, MethodActivationStatusEnum> result;
+ if (rollBackHandlersMap.containsKey(rollbackHandler.getDBType())) {
+ result = Either.right(MethodActivationStatusEnum.NOT_ALLOWED);
+ } else {
+ rollBackHandlersMap.put(rollbackHandler.getDBType(), rollbackHandler);
+ result = Either.left(rollbackHandler);
+ }
+ return result;
+
+ }
+
+ protected Either<RollbackHandler, MethodActivationStatusEnum> createRollbackHandler(DBTypeEnum dbType) {
+
+ final DBTypeEnum dbTypeFinal = dbType;
+ RollbackHandler rollbackHandler = new RollbackHandler(transactionId, userId, actionType) {
+
+ @Override
+ public DBTypeEnum getDBType() {
+ return dbTypeFinal;
+ }
+
+ @Override
+ protected boolean isRollbackForPersistenceData() {
+ return true;
+ }
+ };
+ Either<RollbackHandler, MethodActivationStatusEnum> result = addRollbackHandler(rollbackHandler);
+
+ return result;
+ }
+
+ protected Either<RollbackHandler, MethodActivationStatusEnum> getRollbackHandler(DBTypeEnum dbType) {
+ Either<RollbackHandler, MethodActivationStatusEnum> result;
+ if (rollBackHandlersMap.containsKey(dbType)) {
+ result = Either.left(rollBackHandlersMap.get(dbType));
+ } else {
+ result = Either.right(MethodActivationStatusEnum.NOT_FOUND);
+ }
+ return result;
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionManager.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionManager.java
new file mode 100644
index 0000000000..c401586383
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionManager.java
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.mngr;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.dao.impl.ESCatalogDAO;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.common.datastructure.CapList;
+import org.openecomp.sdc.common.transaction.api.ITransactionSdnc;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ActionTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component("transactionManager")
+public class TransactionManager {
+
+ private static Logger log = LoggerFactory.getLogger(TransactionManager.class.getName());
+
+ private AtomicInteger transactionIDCounter = new AtomicInteger(0);
+
+ private List<ITransactionSdnc> transactions;
+ @Resource
+ private ESCatalogDAO esCatalogDao;
+ @Resource
+ private TitanGenericDao titanGenericDao;
+
+ /**
+ * userId and actionType parameters are used only for logging purposes.
+ */
+ public ITransactionSdnc getTransaction(String userId, ActionTypeEnum actionType) {
+ if (transactions == null) {
+ init();
+ }
+ log.debug("TransactionManager creating new SdncTransaction");
+ ITransactionSdnc tx = new TransactionSdncImpl(generateTransactionID(), userId, actionType, esCatalogDao, titanGenericDao);
+ transactions.add(tx);
+
+ return tx;
+
+ }
+
+ private Integer generateTransactionID() {
+ boolean generatedSuccessfully = false;
+ int nextId = 0;
+
+ while (!generatedSuccessfully) {
+ int prevId = transactionIDCounter.get();
+ if (prevId > TransactionUtils.TRANSACTION_ID_RESET_LIMIT) {
+ resetTransactionId();
+ }
+ nextId = prevId + 1;
+ generatedSuccessfully = transactionIDCounter.compareAndSet(prevId, nextId);
+ }
+ return nextId;
+ }
+
+ private void resetTransactionId() {
+
+ boolean resetSuccessfully = false;
+ while (!resetSuccessfully) {
+ int prevId = transactionIDCounter.get();
+ resetSuccessfully = transactionIDCounter.compareAndSet(prevId, 0);
+ }
+
+ }
+
+ private synchronized void init() {
+ if (transactions == null) {
+ log.info("TransactionManager Initialized");
+ transactions = new CapList<>(TransactionUtils.MAX_SIZE_TRANSACTION_LIST);
+ }
+ }
+
+}
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionSdncImpl.java b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionSdncImpl.java
new file mode 100644
index 0000000000..55eff24fa9
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/common/transaction/mngr/TransactionSdncImpl.java
@@ -0,0 +1,313 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.mngr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.impl.ESCatalogDAO;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.transaction.api.ICommitHandler;
+import org.openecomp.sdc.common.transaction.api.IDBAction;
+import org.openecomp.sdc.common.transaction.api.ITransactionSdnc;
+import org.openecomp.sdc.common.transaction.api.RollbackHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ActionTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ESActionTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.LogMessages;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.TransactionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.TransactionStatusEnum;
+import org.openecomp.sdc.common.transaction.impl.ESAction;
+import org.openecomp.sdc.common.transaction.impl.ESRollbackHandler;
+import org.openecomp.sdc.common.transaction.impl.TitanCommitHandler;
+import org.openecomp.sdc.common.transaction.impl.TitanRollbackHandler;
+import org.openecomp.sdc.common.util.MethodActivationStatusEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class TransactionSdncImpl implements ITransactionSdnc {
+ private boolean lastActionAlreadyCalled;
+ private RollbackManager rollbackManager;
+ private CommitManager commitManager;
+ private ESCatalogDAO esCatalogDao;
+ private TitanGenericDao titanGenericDao;
+ private Integer transactionId;
+ private TransactionStatusEnum status;
+ private String userId, actionType;
+ private static Logger log = LoggerFactory.getLogger(TransactionSdncImpl.class.getName());
+
+ TransactionSdncImpl(Integer transactionId, String userId, ActionTypeEnum actionTypeEnum, ESCatalogDAO esCatalogDao, TitanGenericDao titanGenericDao) {
+ this.esCatalogDao = esCatalogDao;
+ this.titanGenericDao = titanGenericDao;
+ this.transactionId = transactionId;
+ this.userId = userId;
+ actionType = actionTypeEnum.name();
+ rollbackManager = new RollbackManager(transactionId, userId, actionType, initRollbackHandlers());
+ commitManager = new CommitManager(transactionId, userId, actionType, initCommitHandlers());
+ status = TransactionStatusEnum.OPEN;
+
+ }
+
+ private List<ICommitHandler> initCommitHandlers() {
+ List<ICommitHandler> commitHandlers = new ArrayList<>();
+ commitHandlers.add(new TitanCommitHandler(titanGenericDao));
+ return commitHandlers;
+ }
+
+ private List<RollbackHandler> initRollbackHandlers() {
+ List<RollbackHandler> rolebackHandlers = new ArrayList<>();
+ rolebackHandlers.add(new TitanRollbackHandler(transactionId, userId, actionType, titanGenericDao));
+ rolebackHandlers.add(new ESRollbackHandler(transactionId, userId, actionType));
+ return rolebackHandlers;
+ }
+
+ private <T> Either<T, TransactionCodeEnum> invokeAction(boolean isLastAction, IDBAction dbAction, DBTypeEnum dbType) {
+
+ Either<T, DBActionCodeEnum> actionResult;
+ log.debug(LogMessages.INVOKE_ACTION, transactionId, dbType.name(), userId, actionType);
+ if (isLastAction) {
+ actionResult = getLastActionResult(dbAction, dbType);
+ } else {
+ actionResult = getActionResult(dbAction, dbType);
+ }
+
+ Either<T, TransactionCodeEnum> result;
+ boolean isRollbackNedded = actionResult.isRight();
+ if (isRollbackNedded) {
+ TransactionCodeEnum transactionCode = transactionRollback();
+ result = Either.right(transactionCode);
+ } else {
+ result = Either.left(actionResult.left().value());
+ }
+ return result;
+ }
+
+ private TransactionCodeEnum transactionRollback() {
+
+ TransactionCodeEnum result;
+ DBActionCodeEnum transactionRollback = rollbackManager.transactionRollback();
+ if (transactionRollback == DBActionCodeEnum.SUCCESS) {
+ result = TransactionCodeEnum.ROLLBACK_SUCCESS;
+ log.info(LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, userId, actionType);
+ log.info(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, userId, actionType);
+
+ } else {
+ result = TransactionCodeEnum.ROLLBACK_FAILED;
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "transactionCommit for transaction " + transactionId);
+ BeEcompErrorManager.getInstance().logBeSystemError("transactionCommit for transaction " + transactionId);
+
+ log.info(LogMessages.ROLLBACK_FAILED_GENERAL, transactionId, userId, actionType);
+ log.debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_FAILED_GENERAL, transactionId, userId, actionType);
+ }
+ return result;
+ }
+
+ public <T> Either<T, TransactionCodeEnum> invokeTitanAction(boolean isLastAction, IDBAction dbAction) {
+ Either<T, TransactionCodeEnum> result;
+ if (status == TransactionStatusEnum.OPEN) {
+ result = invokeAction(isLastAction, dbAction, DBTypeEnum.TITAN);
+ } else {
+ result = handleActionOnClosedTransaction();
+ }
+ updateTransactionStatus(result);
+ return result;
+ }
+
+ public <T> Either<T, TransactionCodeEnum> invokeGeneralDBAction(boolean isLastAction, DBTypeEnum dbType, IDBAction dbAction, IDBAction dbRollbackAction) {
+
+ Either<T, TransactionCodeEnum> result;
+ MethodActivationStatusEnum addingHandlerResult;
+ if (status == TransactionStatusEnum.OPEN) {
+ log.debug(LogMessages.PRE_INVOKE_ACTION, transactionId, dbType.name(), userId, actionType);
+ Either<RollbackHandler, MethodActivationStatusEnum> eitherRollbackHandler = rollbackManager.getRollbackHandler(dbType);
+
+ if (eitherRollbackHandler.isLeft()) {
+ RollbackHandler rollbackHandler = eitherRollbackHandler.left().value();
+ addingHandlerResult = rollbackHandler.addRollbackAction(dbRollbackAction);
+ } else {
+ addingHandlerResult = addToNewRollbackHandler(dbType, dbRollbackAction);
+ }
+
+ if (addingHandlerResult == MethodActivationStatusEnum.SUCCESS) {
+ result = invokeAction(isLastAction, dbAction, dbType);
+ } else {
+ result = Either.right(TransactionCodeEnum.PREPARE_ROLLBACK_FAILED);
+ }
+ } else {
+ result = handleActionOnClosedTransaction();
+ }
+ updateTransactionStatus(result);
+ return result;
+ }
+
+ private MethodActivationStatusEnum addToNewRollbackHandler(DBTypeEnum dbType, IDBAction dbRollbackAction) {
+ log.debug(LogMessages.CREATE_ROLLBACK_HANDLER, dbType.name(), transactionId, userId, actionType);
+ MethodActivationStatusEnum result;
+
+ Either<RollbackHandler, MethodActivationStatusEnum> eitherRollbackHandler = rollbackManager.createRollbackHandler(dbType);
+ if (eitherRollbackHandler.isRight()) {
+ result = eitherRollbackHandler.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "TransactionManager - addToNewRollbackHandler");
+ BeEcompErrorManager.getInstance().logBeSystemError("TransactionManager - addToNewRollbackHandler");
+ log.info(LogMessages.FAILED_CREATE_ROLLBACK, dbType.name(), transactionId, userId, actionType);
+ log.debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.FAILED_CREATE_ROLLBACK, dbType.name(), transactionId, userId, actionType);
+ } else {
+ RollbackHandler rollbackHandler = eitherRollbackHandler.left().value();
+ rollbackHandler.addRollbackAction(dbRollbackAction);
+ result = MethodActivationStatusEnum.SUCCESS;
+ }
+
+ return result;
+ }
+
+ public Either<DBActionCodeEnum, TransactionCodeEnum> invokeESAction(boolean isLastAction, ESActionTypeEnum esActiontype, ESArtifactData artifactData) {
+
+ Either<DBActionCodeEnum, TransactionCodeEnum> result;
+ if (status == TransactionStatusEnum.OPEN) {
+ Either<RollbackHandler, MethodActivationStatusEnum> eitherEsHandler = rollbackManager.getRollbackHandler(DBTypeEnum.ELASTIC_SEARCH);
+ if (eitherEsHandler.isRight()) {
+ result = Either.right(TransactionCodeEnum.INTERNAL_ERROR);
+ } else {
+ ESRollbackHandler esHandler = (ESRollbackHandler) eitherEsHandler.left().value();
+
+ Either<ESAction, MethodActivationStatusEnum> eitherEsRollbackAction = esHandler.buildEsRollbackAction(esCatalogDao, artifactData, esActiontype);
+ if (eitherEsRollbackAction.isLeft()) {
+ esHandler.addRollbackAction(eitherEsRollbackAction.left().value());
+ result = invokeAction(isLastAction, new ESAction(esCatalogDao, artifactData, esActiontype), DBTypeEnum.ELASTIC_SEARCH);
+ } else {
+ result = Either.right(TransactionCodeEnum.PREPARE_ROLLBACK_FAILED);
+ }
+
+ }
+ } else {
+ result = handleActionOnClosedTransaction();
+ }
+ updateTransactionStatus(result);
+ return result;
+ }
+
+ private <T> void updateTransactionStatus(Either<T, TransactionCodeEnum> result) {
+ if (result.isRight()) {
+ updateTransactionStatus(result.right().value());
+ }
+
+ }
+
+ private <T> Either<T, TransactionCodeEnum> handleActionOnClosedTransaction() {
+ Either<T, TransactionCodeEnum> result = Either.right(TransactionCodeEnum.TRANSACTION_CLOSED);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "TransactionManager - handleActionOnClosedTransaction");
+ log.debug(LogMessages.ACTION_ON_CLOSED_TRANSACTION, transactionId, userId, actionType);
+ log.info(TransactionUtils.TRANSACTION_MARKER, LogMessages.ACTION_ON_CLOSED_TRANSACTION, transactionId, userId, actionType);
+ return result;
+ }
+
+ private <T> Either<T, DBActionCodeEnum> getLastActionResult(IDBAction dataBaseAction, DBTypeEnum dbType) {
+ Either<T, DBActionCodeEnum> result;
+ if (isLastActionAlreadyCalled()) {
+ result = Either.right(DBActionCodeEnum.FAIL_MULTIPLE_LAST_ACTION);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "TransactionManager - getLastActionResult");
+ BeEcompErrorManager.getInstance().logBeSystemError("TransactionManager - getLastActionResult");
+ log.debug(LogMessages.DOUBLE_FINISH_FLAG_ACTION, transactionId, dbType.name(), userId, actionType);
+ log.info(TransactionUtils.TRANSACTION_MARKER, LogMessages.DOUBLE_FINISH_FLAG_ACTION, transactionId, dbType.name(), userId, actionType);
+ } else {
+ setLastActionAlreadyCalled(true);
+ result = getActionResult(dataBaseAction, dbType);
+ }
+ return result;
+ }
+
+ private <T> Either<T, DBActionCodeEnum> getActionResult(IDBAction dataBaseAction, DBTypeEnum dbType) {
+ Either<T, DBActionCodeEnum> result;
+ try {
+ T doAction = dataBaseAction.doAction();
+ result = Either.left(doAction);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "TransactionManager - getActionResult");
+ BeEcompErrorManager.getInstance().logBeSystemError("TransactionManager - getActionResult");
+ log.debug(LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION, dbType.name(), transactionId, e.getMessage(), userId, actionType, e);
+ log.info(TransactionUtils.TRANSACTION_MARKER, LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION, dbType.name(), transactionId, e.getMessage(), userId, actionType);
+
+ result = Either.right(DBActionCodeEnum.FAIL_GENERAL);
+ }
+ return result;
+ }
+
+ public TransactionCodeEnum finishTransaction() {
+ TransactionCodeEnum result;
+ if (status == TransactionStatusEnum.OPEN) {
+ DBActionCodeEnum transactionCommit = commitManager.transactionCommit();
+ if (transactionCommit == DBActionCodeEnum.SUCCESS) {
+ result = TransactionCodeEnum.SUCCESS;
+ status = TransactionStatusEnum.CLOSED;
+ } else {
+ result = transactionRollback();
+ }
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "TransactionManager - finishTransaction");
+ BeEcompErrorManager.getInstance().logBeSystemError("TransactionManager - finishTransaction");
+ log.debug(LogMessages.COMMIT_ON_CLOSED_TRANSACTION, transactionId, status.name(), userId, actionType);
+ log.info(TransactionUtils.TRANSACTION_MARKER, LogMessages.COMMIT_ON_CLOSED_TRANSACTION, transactionId, status.name(), userId, actionType);
+ result = TransactionCodeEnum.TRANSACTION_CLOSED;
+ }
+ updateTransactionStatus(result);
+ return result;
+ }
+
+ private void updateTransactionStatus(TransactionCodeEnum result) {
+ switch (result) {
+ case SUCCESS:
+ status = TransactionStatusEnum.CLOSED;
+ break;
+ case ROLLBACK_SUCCESS:
+ status = TransactionStatusEnum.CLOSED;
+ break;
+ case ROLLBACK_FAILED:
+ status = TransactionStatusEnum.FAILED_ROLLBACK;
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ private boolean isLastActionAlreadyCalled() {
+ return lastActionAlreadyCalled;
+ }
+
+ private void setLastActionAlreadyCalled(boolean lastAction) {
+ this.lastActionAlreadyCalled = lastAction;
+ }
+
+ static void setLog(Logger log) {
+ TransactionSdncImpl.log = log;
+ }
+
+ TransactionStatusEnum getStatus() {
+ return status;
+ }
+}
diff --git a/catalog-be/src/main/resources/application-context.xml b/catalog-be/src/main/resources/application-context.xml
new file mode 100644
index 0000000000..da0a49f6e2
--- /dev/null
+++ b/catalog-be/src/main/resources/application-context.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
+
+ <context:annotation-config />
+ <aop:aspectj-autoproxy proxy-target-class="true" />
+
+ <context:component-scan
+ base-package="org.openecomp.sdc.be.dao.impl,
+ org.openecomp.sdc.be.dao.es,
+ org.openecomp.sdc.be.resources.impl,
+ org.openecomp.sdc.be.dao.neo4j,
+ org.openecomp.sdc.be.model.operations.impl,
+ org.openecomp.sdc.be.model.cache,
+ org.openecomp.sdc.be.dao.titan,
+ org.openecomp.sdc.be.user,
+ org.openecomp.sdc.be.impl,
+ org.openecomp.sdc.be.auditing.impl,
+ org.openecomp.sdc.be.components.impl,
+ org.openecomp.sdc.be.components.distribution.engine,
+ org.openecomp.sdc.be.distribution,
+ org.openecomp.sdc.be.components.clean,
+ org.openecomp.sdc.be.dao.cassandra,
+ org.openecomp.sdc.be.switchover.detector,
+ org.openecomp.sdc.be.tosca,
+ org.openecomp.sdc.be.externalapi.servlet
+ ">
+
+ </context:component-scan>
+
+ <bean id="resourceImportManager" class="org.openecomp.sdc.be.components.impl.ResourceImportManager" />
+ <bean id="capabilityTypeImportManager" class="org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager" />
+ <bean id="servletUtils" class="org.openecomp.sdc.be.impl.ServletUtils" />
+ <bean id="resourceBusinessLogic" class="org.openecomp.sdc.be.components.impl.ResourceBusinessLogic" />
+ <bean id="serviceBusinessLogic" class="org.openecomp.sdc.be.components.impl.ServiceBusinessLogic" />
+ <bean id="productBusinessLogic" class="org.openecomp.sdc.be.components.impl.ProductBusinessLogic" />
+ <bean id="artifactBusinessLogic" class="org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic" />
+ <bean id="lifecycleBusinessLogic" class="org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic" />
+ <bean id="serviceComponentInstanceBusinessLogic" class="org.openecomp.sdc.be.components.impl.ServiceComponentInstanceBusinessLogic" />
+ <bean id="productComponentInstanceBusinessLogic" class="org.openecomp.sdc.be.components.impl.ProductComponentInstanceBusinessLogic" />
+ <bean id="vfComponentInstanceBusinessLogic" class="org.openecomp.sdc.be.components.impl.VFComponentInstanceBusinessLogic" />
+ <bean id="transactionManager" class="org.openecomp.sdc.common.transaction.mngr.TransactionManager" />
+ <bean id="userBusinessLogic" class="org.openecomp.sdc.be.user.UserBusinessLogic" />
+ <bean id="elementsBusinessLogic" class="org.openecomp.sdc.be.components.impl.ElementBusinessLogic" />
+ <bean id="propertyBusinessLogic" class="org.openecomp.sdc.be.components.impl.PropertyBusinessLogic" />
+ <bean id="auditingManager" class="org.openecomp.sdc.be.auditing.impl.AuditingManager" />
+ <bean id="distributionBusinessLogic" class="org.openecomp.sdc.be.distribution.DistributionBusinessLogic" />
+ <bean id="interfaceLifecycleTypeImportManager" class="org.openecomp.sdc.be.components.impl.InterfaceLifecycleTypeImportManager" />
+ <bean id="distributionMonitoringBusinessLogic" class="org.openecomp.sdc.be.components.impl.DistributionMonitoringBusinessLogic" />
+ <bean id="additionalInformationBusinessLogic" class="org.openecomp.sdc.be.components.impl.AdditionalInformationBusinessLogic" />
+ <bean id="distribution-engine-cluster-health" class="org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth" />
+ <bean id="categoriesImportManager" class="org.openecomp.sdc.be.components.impl.CategoriesImportManager" />
+ <bean id="asset-metadata-utils" class="org.openecomp.sdc.be.externalapi.servlet.AssetMetadataConverter" />
+
+
+
+ <util:properties id="elasticsearchConfig" location="file:${config.home}/elasticsearch.yml" />
+
+</beans>
diff --git a/catalog-be/src/main/resources/config/configuration.yaml b/catalog-be/src/main/resources/config/configuration.yaml
new file mode 100644
index 0000000000..b8e80dc17c
--- /dev/null
+++ b/catalog-be/src/main/resources/config/configuration.yaml
@@ -0,0 +1,478 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+
+# catalog backend hostname
+beFqdn: localhost
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: /home/vagrant/catalog-be/config/catalog-be/titan.properties
+titanInMemoryGraph: false
+titanLockTimeout: 1800
+titanReconnectIntervalInSeconds: 3
+titanHealthCheckReadTimeout: 1
+esReconnectIntervalInSeconds: 3
+uebHealthCheckReconnectIntervalInSeconds: 15
+uebHealthCheckReadTimeout: 4
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+neo4j:
+ host: neo4jhost
+ port: 7474
+ user: neo4j
+ password: "12345"
+
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # PLEASE NOTE: the timestamps are created in UTC/GMT timezone! This is needed so that timestamps will be correctly presented in Kibana.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+ - indexPrefix: monitoring_events
+ creationPeriod: month
+
+artifactTypes:
+ - CHEF
+ - PUPPET
+ - SHELL
+ - YANG
+ - YANG_XML
+ - HEAT
+ - BPEL
+ - DG_XML
+ - MURANO_PKG
+ - WORKFLOW
+ - NETWORK_CALL_FLOW
+ - TOSCA_TEMPLATE
+ - TOSCA_CSAR
+ - AAI_SERVICE_MODEL
+ - AAI_VF_MODEL
+ - AAI_VF_MODULE_MODEL
+ - AAI_VF_INSTANCE_MODEL
+ - OTHER
+
+licenseTypes:
+ - User
+ - Installation
+ - CPU
+
+#Deployment artifacts placeHolder
+resourceTypes: &allResourceTypes
+ - VFC
+ - CP
+ - VL
+ - VF
+
+# validForResourceTypes usage
+# validForResourceTypes:
+# - VF
+# - VL
+deploymentResourceArtifacts:
+# heat:
+# displayName: "Base HEAT Template"
+# type: HEAT
+# validForResourceTypes: *allResourceTypes
+# heatVol:
+# displayName: "Volume HEAT Template"
+# type: HEAT_VOL
+# validForResourceTypes: *allResourceTypes
+# heatNet:
+# displayName: "Network HEAT Template"
+# type: HEAT_NET
+# validForResourceTypes: *allResourceTypes
+
+deploymentResourceInstanceArtifacts:
+ heatEnv:
+ displayName: "HEAT ENV"
+ type: HEAT_ENV
+ description: "Auto-generated HEAT Environment deployment artifact"
+ fileExtension: "env"
+
+#tosca artifacts placeholders
+toscaArtifacts:
+ assetToscaTemplate:
+ artifactName: -template.yml
+ displayName: Tosca Template
+ type: TOSCA_TEMPLATE
+ description: TOSCA representation of the asset
+ assetToscaCsar:
+ artifactName: -csar.csar
+ displayName: Tosca Model
+ type: TOSCA_CSAR
+ description: TOSCA definition package of the asset
+
+
+#Informational artifacts placeHolder
+excludeResourceCategory:
+ - Generic
+informationalResourceArtifacts:
+ features:
+ displayName: Features
+ type: OTHER
+ capacity:
+ displayName: Capacity
+ type: OTHER
+ vendorTestResult:
+ displayName: Vendor Test Result
+ type: OTHER
+ testScripts:
+ displayName: Test Scripts
+ type: OTHER
+ cloudQuestionnaire:
+ displayName: Cloud Questionnaire (completed)
+ type: OTHER
+ HEATTemplateFromVendor:
+ displayName: HEAT Template from Vendor
+ type: HEAT
+ resourceSecurityTemplate:
+ displayName: Resource Security Template
+ type: OTHER
+
+excludeServiceCategory:
+
+informationalServiceArtifacts:
+ serviceArtifactPlan:
+ displayName: Service Artifact Plan
+ type: OTHER
+ summaryOfImpactsToECOMPElements:
+ displayName: Summary of impacts to ECOMP elements,OSSs, BSSs
+ type: OTHER
+ controlLoopFunctions:
+ displayName: Control Loop Functions
+ type: OTHER
+ dimensioningInfo:
+ displayName: Dimensioning Info
+ type: OTHER
+ affinityRules:
+ displayName: Affinity Rules
+ type: OTHER
+ operationalPolicies:
+ displayName: Operational Policies
+ type: OTHER
+ serviceSpecificPolicies:
+ displayName: Service-specific Policies
+ type: OTHER
+ engineeringRules:
+ displayName: Engineering Rules (ERD)
+ type: OTHER
+ distributionInstructions:
+ displayName: Distribution Instructions
+ type: OTHER
+ certificationTestResults:
+ displayName: TD Certification Test Results
+ type: OTHER
+ deploymentVotingRecord:
+ displayName: Deployment Voting Record
+ type: OTHER
+ serviceQuestionnaire:
+ displayName: Service Questionnaire
+ type: OTHER
+ serviceSecurityTemplate:
+ displayName: Service Security Template
+ type: OTHER
+
+serviceApiArtifacts:
+ configuration:
+ displayName: Configuration
+ type: OTHER
+ instantiation:
+ displayName: Instantiation
+ type: OTHER
+ monitoring:
+ displayName: Monitoring
+ type: OTHER
+ reporting:
+ displayName: Reporting
+ type: OTHER
+ logging:
+ displayName: Logging
+ type: OTHER
+ testing:
+ displayName: Testing
+ type: OTHER
+
+
+additionalInformationMaxNumberOfKeys: 50
+
+systemMonitoring:
+ enabled: false
+ isProxy: false
+ probeIntervalInSeconds: 15
+
+defaultHeatArtifactTimeoutMinutes: 60
+
+serviceDeploymentArtifacts:
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+#AAI Artifacts
+ AAI_SERVICE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_INSTANCE_MODEL:
+ acceptedTypes:
+ - xml
+ OTHER:
+ acceptedTypes:
+
+resourceDeploymentArtifacts:
+ HEAT:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_VOL:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NESTED:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_ARTIFACT:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+ HEAT_NET:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VF_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VENDOR_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ #APPC Artifatcs
+ APPC_CONFIG:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ #DCAE Artifacts
+ DCAE_TOSCA:
+ acceptedTypes:
+ - yml
+ - yaml
+ validForResourceTypes:
+ - VF
+ DCAE_JSON:
+ acceptedTypes:
+ - json
+ validForResourceTypes:
+ - VF
+ DCAE_POLICY:
+ acceptedTypes:
+ - emf
+ validForResourceTypes:
+ - VF
+ DCAE_DOC:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ DCAE_EVENT:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+#AAI Artifacts
+ AAI_VF_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ OTHER:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+
+resourceInstanceDeploymentArtifacts:
+ HEAT_ENV:
+ acceptedTypes:
+ - env
+ VF_MODULES_METADATA:
+ acceptedTypes:
+ - json
+#DCAE_VF Instance Artifacts
+ DCAE_INVENTORY_TOSCA:
+ acceptedTypes:
+ - yml
+ - yaml
+ DCAE_INVENTORY_JSON:
+ acceptedTypes:
+ - json
+ DCAE_INVENTORY_POLICY:
+ acceptedTypes:
+ - emf
+ DCAE_INVENTORY_DOC:
+ acceptedTypes:
+ DCAE_INVENTORY_BLUEPRINT:
+ acceptedTypes:
+ DCAE_INVENTORY_EVENT:
+ acceptedTypes:
+
+
+resourceInformationalDeployedArtifacts:
+
+
+requirementsToFulfillBeforeCert:
+
+capabilitiesToConsumeBeforeCert:
+
+unLoggedUrls:
+ - /sdc2/rest/healthCheck
+
+cleanComponentsConfiguration:
+ cleanIntervalInMinutes: 1440
+ componentsToClean:
+ - Resource
+ - Service
+
+artifactsIndex: resources
+
+cassandraConfig:
+ cassandraHosts: ['localhost']
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: false
+ username: koko
+ password: bobo
+ ssl: false
+ truststorePath : /path/path
+ truststorePassword : 123123
+ keySpaces:
+ - { name: sdcaudit, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdcartifact, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdccomponent, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+
+
+switchoverDetector:
+ gBeFqdn:
+ gFeFqdn:
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl: "http://localhost/crt/CipDomain.ECOMP-ASDC-DEVST/config/update_network?user=root"
+ publishNetworkBody: '{"note":"publish network"}'
+ groups:
+ beSet: { changePriorityUrl: "http://localhost/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"name":"AIO-BE.ecomp.idns.cip","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip","no_ad_redirection":false,"v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_be","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_be"],"failover_policy":["FAILALL"]},"comment":"AIO BE G-fqdn","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"comment":"AIO G-fqdn","name":"AIO-FE.ecomp.idns.cip","v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_fe","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_fe"],"failover_policy":["FAILALL"]},"no_ad_redirection":false,"intended_app_proto":"DNS","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip"}'}
+
+
+heatEnvArtifactHeader:
+ ""
+heatEnvArtifactFooter:
+ ""
+
+onboarding:
+ protocol: http
+ host: localhost
+ port: 8080
+ downloadCsarUri: "/onboarding-api/v1.0/vendor-software-products/packages"
+ #downloadCsarUri: "/onboardingci/onbrest/onboarding-api/v1.0/vendor-software-products/packages"
+
+applicationL1Cache:
+ datatypes:
+ enabled: true
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: true
+ catalogL1Cache:
+ enabled: true
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 43200
+ waitOnShutDownInMinutes: 10
+ numberOfCacheWorkers: 4
+
+toscaValidators:
+ stringMaxLength: 65536
+disableAudit: false \ No newline at end of file
diff --git a/catalog-be/src/main/resources/config/distribution-engine-configuration.yaml b/catalog-be/src/main/resources/config/distribution-engine-configuration.yaml
new file mode 100644
index 0000000000..8c56a26785
--- /dev/null
+++ b/catalog-be/src/main/resources/config/distribution-engine-configuration.yaml
@@ -0,0 +1,46 @@
+uebServers:
+ - uebsb91sfdc.it.att.com:3904
+ - uebsb92sfdc.it.att.com:3904
+ - uebsb93sfdc.it.att.com:3904
+
+# prev Kansas City Sandbox uebPublicKey: 8F3MDAtMSBwwpSMy
+uebPublicKey: sSJc5qiBnKy2qrlc
+
+# prev Kansas City Sandbox uebSecretKey: gzFmsTxSCtO5RQfAccM6PqqX
+uebSecretKey: 4ZRPzNJfEUK0sSNBvccd2m7X
+
+distributionNotifTopicName: ASDC-DISTR-NOTIF-TOPIC
+distributionStatusTopicName: ASDC-DISTR-STATUS-TOPIC
+
+initRetryIntervalSec: 5
+initMaxIntervalSec: 60
+
+distribNotifServiceArtifactTypes:
+ info:
+ - MURANO-PKG
+
+distribNotifResourceArtifactTypes:
+ lifecycle:
+ - HEAT
+ - DG-XML
+
+environments:
+ - PROD
+
+distributionStatusTopic:
+ pollingIntervalSec: 60
+ fetchTimeSec: 15
+ consumerGroup: asdc
+ consumerId: asdc-id
+
+distributionNotificationTopic:
+ minThreadPoolSize: 0
+ maxThreadPoolSize: 10
+ maxWaitingAfterSendingSeconds: 5
+
+
+createTopic:
+ partitionCount: 1
+ replicationCount: 1
+
+startDistributionEngine: true \ No newline at end of file
diff --git a/catalog-be/src/main/resources/config/ecomp-error-configuration.yaml b/catalog-be/src/main/resources/config/ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..9d7cd74a2b
--- /dev/null
+++ b/catalog-be/src/main/resources/config/ecomp-error-configuration.yaml
@@ -0,0 +1,383 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+## Range of BE codes - 3010-7999
+
+errors:
+
+ BeRestApiGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4000,
+ severity: ERROR,
+ description: "Unexpected error during BE REST API execution",
+ alarmSeverity: CRITICAL
+ }
+
+ BeHealthCheckError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3010,
+ severity: ERROR,
+ description: "Error during BE Health Check",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInitializationError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4019,
+ severity: ERROR,
+ description: "Catalog-BE was not initialized properly",
+ alarmSeverity: CRITICAL
+ }
+
+ BeResourceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3011,
+ severity: ERROR,
+ description: "Mandatory resource %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeServiceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3012,
+ severity: ERROR,
+ description: "Mandatory service %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedAddingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3013,
+ severity: ERROR,
+ description: "Failed to add resource instance of resource %s to service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeIncorrectServiceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3014,
+ severity: ERROR,
+ description: "Service %s is not valid",
+ alarmSeverity: MAJOR
+ }
+
+ BeRepositoryDeleteError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3015,
+ severity: ERROR,
+ description: "Failed to delete object %s from repository",
+ alarmSeverity: CRITICAL
+ }
+
+ BeRepositoryQueryError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3016,
+ severity: ERROR,
+ description: "Failed to fetch from repository %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeInvalidConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3017,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. Value configured is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4001,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3019,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebObjectNotFoundError: {
+ type: DATA_ERROR,
+ code: ASDC_4005,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Data not found: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3021,
+ severity: ERROR,
+ description: "Error occured in Distribution Engine. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebAuthenticationError: {
+ type: AUTHENTICATION_PROBLEM,
+ code: ASDC_4003,
+ severity: ERROR,
+ description: "Authentication problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebUnkownHostError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4002,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Cannot reach host %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineInvalidArtifactType: {
+ type: DATA_ERROR,
+ code: ASDC_4006,
+ severity: WARN,
+ description: "The artifact type %s does not appear in the list of valid artifacts %s",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4008,
+ severity: WARN,
+ description: "The type %s of %s is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidValueError: {
+ type: DATA_ERROR,
+ code: ASDC_3028,
+ severity: WARN,
+ description: "The value %s of %s from type %s is invalid",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeletingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3029,
+ severity: ERROR,
+ description: "Failed to delete resource instance %s from service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3030,
+ severity: FATAL,
+ description: "Configuration parameter %s is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeConfigurationInvalidListSizeError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3031,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. At least %s values shall be configured",
+ alarmSeverity: MAJOR
+ }
+
+ ErrorConfigFileFormat: {
+ type: CONFIG_ERROR,
+ code: ASDC_3032,
+ severity: ERROR,
+ description: "Error element not found in YAML name: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingArtifactInformationError: {
+ type: DATA_ERROR,
+ code: ASDC_4010,
+ severity: ERROR,
+ description: "Artifact uploaded has missing information. Missing %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4011,
+ severity: ERROR,
+ description: "Artifact %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactPayloadInvalid: {
+ type: DATA_ERROR,
+ code: ASDC_4012,
+ severity: ERROR,
+ description: "Payload of artifact uploaded is invalid (invalid MD5 or encryption)",
+ alarmSeverity: MAJOR
+ }
+
+ BeUserMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4009,
+ severity: ERROR,
+ description: "User %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactInformationInvalidError: {
+ type: DATA_ERROR,
+ code: ASDC_4013,
+ severity: ERROR,
+ description: "Input for artifact metadata is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeFailedAddingCapabilityTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4015,
+ severity: ERROR,
+ description: "Failed adding capability type",
+ alarmSeverity: CRITICAL
+ }
+
+ BeCapabilityTypeMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4016,
+ severity: ERROR,
+ description: "Capability Type %s not found",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInterfaceMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4020,
+ severity: ERROR,
+ description: "Interface %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeDaoSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4014,
+ severity: ERROR,
+ description: "Operation towards database failed",
+ alarmSeverity: CRITICAL
+ }
+
+ BeSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4017,
+ severity: ERROR,
+ description: "Unexpected error during operation",
+ alarmSeverity: CRITICAL
+ }
+
+ BeFailedLockObjectError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4007,
+ severity: WARN,
+ description: "Failed to lock object for update",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInvalidJsonInput: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4018,
+ severity: ERROR,
+ description: "Failed to convert json input to object",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4021,
+ severity: ERROR,
+ description: "Distribution %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeHealthCheckRecovery: {
+ type: RECOVERY,
+ code: ASDC_4022,
+ severity: INFO,
+ description: "BE Health Check Recovery",
+ alarmSeverity: INFORMATIONAL
+ }
+ BeFailedCreateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6000,
+ severity: ERROR,
+ description: "Failed to create node %s on graph. status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeFailedUpdateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6001,
+ severity: ERROR,
+ description: "Failed to update node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeleteNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6002,
+ severity: ERROR,
+ description: "Failed to delete node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedRetrieveNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6003,
+ severity: ERROR,
+ description: "Failed to retrieve node %s from graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeExecuteRollbackError: {
+ type: DATA_ERROR,
+ code: ASDC_6004,
+ severity: ERROR,
+ description: "Going to execute rollback on graph.",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindParentError: {
+ type: DATA_ERROR,
+ code: ASDC_6005,
+ severity: ERROR,
+ description: "Failed to find parent node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAllNodesError: {
+ type: DATA_ERROR,
+ code: ASDC_6006,
+ severity: ERROR,
+ description: "Failed to fetch all nodes with type %s of parent node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6007,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6008,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeComponentCleanerSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_6009,
+ severity: ERROR,
+ description: "Error occured in Component Cleaner Task. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+ \ No newline at end of file
diff --git a/catalog-be/src/main/resources/config/error-configuration.yaml b/catalog-be/src/main/resources/config/error-configuration.yaml
new file mode 100644
index 0000000000..0054ce2be9
--- /dev/null
+++ b/catalog-be/src/main/resources/config/error-configuration.yaml
@@ -0,0 +1,1694 @@
+# Errors
+errors:
+ OK: {
+ code: 200,
+ message: "OK"
+ }
+ CREATED: {
+ code: 201,
+ message: "OK"
+ }
+ NO_CONTENT: {
+ code: 204,
+ message: "No Content"
+ }
+#--------POL4050-----------------------------
+ NOT_ALLOWED: {
+ code: 405,
+ message: "Error: Method not allowed.",
+ messageId: "POL4050"
+ }
+#--------POL5000-----------------------------
+ GENERAL_ERROR: {
+ code: 500,
+ message: "Error: Internal Server Error. Please try again later.",
+ messageId: "POL5000"
+ }
+#---------POL5001------------------------------
+ MISSING_X_ECOMP_INSTANCE_ID: {
+ code: 400 ,
+ message: "Error: Missing 'X-ECOMP-InstanceID' HTTP header.",
+ messageId: "POL5001"
+ }
+#---------POL5002------------------------------
+ AUTH_REQUIRED: {
+ code: 401 ,
+ message: "Error: Authentication is required to use the API.",
+ messageId: "POL5002"
+ }
+#---------POL5003------------------------------
+ AUTH_FAILED: {
+ code: 403 ,
+ message: "Error: Not authorized to use the API.",
+ messageId: "POL5003"
+ }
+#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }
+#---------SVC4002-----------------------------
+ MISSING_INFORMATION: {
+ code: 403,
+ message: "Error: Missing information.",
+ messageId: "SVC4002"
+ }
+#---------SVC4003------------------------------
+# %1 - Users's USER_ID
+ USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: User '%1' was not found.",
+ messageId: "SVC4003"
+ }
+#---------SVC4004-----------------------------
+# %1 - Users's email address
+ INVALID_EMAIL_ADDRESS: {
+ code: 400,
+ message: "Error: Invalid email address '%1'.",
+ messageId: "SVC4004"
+ }
+#---------SVC4005------------------------------
+# %1 - role
+ INVALID_ROLE: {
+ code: 400,
+ message: "Error: Invalid role '%1'.",
+ messageId: "SVC4005"
+ }
+#---------SVC4006------------------------------
+# %1 - Users's USER_ID
+ USER_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: User with '%1' ID already exists.",
+ messageId: "SVC4006"
+ }
+#---------SVC4007------------------------------
+ DELETE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator can only be deleted by another administrator.",
+ messageId: "SVC4007"
+ }
+#---------SVC4008-----------------------------
+# %1 - Users's userId
+ INVALID_USER_ID: {
+ code: 400,
+ message: "Error: Invalid userId '%1'.",
+ messageId: "SVC4008"
+ }
+#---------SVC4049------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_CONTACT: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 contact id.",
+ messageId: "SVC4049"
+ }
+#---------SVC4050-----------------------------
+# %1 - Service/Resource/Additional parameter
+# %2 - service/resource/label name
+ COMPONENT_NAME_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: %1 with name '%2' already exists.",
+ messageId: "SVC4050"
+ }
+#---------SVC4051------------------------------
+# %1 - resource/service
+ COMPONENT_MISSING_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 category.",
+ messageId: "SVC4051"
+ }
+
+#---------SVC4052------------------------------
+ COMPONENT_MISSING_TAGS: {
+ code: 400,
+ message: "Error: Invalid Content. At least one tag has to be specified.",
+ messageId: "SVC4052"
+ }
+
+#---------SVC4053------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 description.",
+ messageId: "SVC4053"
+ }
+#---------SVC4054------------------------------
+# %1 - resource/service
+ COMPONENT_INVALID_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 category.",
+ messageId: "SVC4054"
+ }
+#---------SVC4055------------------------------
+ MISSING_VENDOR_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor name.",
+ messageId: "SVC4055"
+ }
+#---------SVC4056------------------------------
+ MISSING_VENDOR_RELEASE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor release.",
+ messageId: "SVC4056"
+ }
+
+#---------SVC4057------------------------------
+ MISSING_DERIVED_FROM_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing derived from template specification.",
+ messageId: "SVC4057"
+ }
+
+#---------SVC4058------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 icon.",
+ messageId: "SVC4058"
+ }
+#---------SVC4059------------------------------
+# %1 - service/resource
+ COMPONENT_INVALID_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 icon.",
+ messageId: "SVC4059"
+ }
+#---------SVC4060------------------------------
+ PARENT_RESOURCE_NOT_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Derived from resource template was not found.",
+ messageId: "SVC4060"
+ }
+#---------SVC4061------------------------------
+ MULTIPLE_PARENT_RESOURCE_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Multiple derived from resource template is not allowed.",
+ messageId: "SVC4061"
+ }
+
+#---------SVC4062------------------------------
+# %1 - service/resource
+ MISSING_COMPONENT_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 name.",
+ messageId: "SVC4062"
+ }
+#---------SVC4063------------------------------
+ #%1  -  resource/service name
+ RESOURCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource was not found.",
+ messageId: "SVC4063"
+ }
+
+#---------SVC4064------------------------------
+# %1 - Service/Resource
+ COMPONENT_INVALID_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description contains non-english characters.",
+ messageId: "SVC4064"
+ }
+#---------SVC4065------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name length
+ COMPONENT_DESCRIPTION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description exceeds limit of %2 characters.",
+ messageId: "SVC4065"
+ }
+#---------SVC4066------------------------------
+# %1 - max length
+ COMPONENT_TAGS_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Tags overall length exceeds limit of %1 characters.",
+ messageId: "SVC4066"
+ }
+#---------SVC4067------------------------------
+# %1 - max length
+ VENDOR_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor name exceeds limit of %1 characters.",
+ messageId: "SVC4067"
+ }
+#---------SVC4068------------------------------
+# %1 - max length
+ VENDOR_RELEASE_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor release exceeds limit of %1 characters.",
+ messageId: "SVC4068"
+ }
+
+#---------SVC4069------------------------------
+# %1 - Service/Resource/Product
+ COMPONENT_INVALID_CONTACT_ID: {
+ code: 400,
+ message: "Error: Invalid Content. %1 contact id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9",
+ messageId: "SVC4069"
+ }
+#---------SVC4070------------------------------
+# %1 - Service/Resource
+ INVALID_COMPONENT_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. %1 name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4070"
+ }
+
+#---------SVC4071------------------------------
+ INVALID_VENDOR_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4071"
+ }
+#---------SVC4072------------------------------
+ INVALID_VENDOR_RELEASE: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor release is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4072"
+ }
+#---------SVC4073------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name
+ COMPONENT_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 name exceeds limit of %2 characters.",
+ messageId: "SVC4073"
+ }
+#---------SVC4080------------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CHECKOUT_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for modification by %3 %4(%5).",
+ messageId: "SVC4080"
+ }
+#---------SVC4081-----------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CERT_IN_PROGRESS_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for certification by %3 %4(%5).",
+ messageId: "SVC4081"
+ }
+
+#-----------SVC4082---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_SENT_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is sent for certification by %3 %4(%5).",
+ messageId: "SVC4082"
+ }
+#-----------SVC4083---------------------------
+ COMPONENT_VERSION_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Version of this %1 was already promoted.",
+ messageId: "SVC4083"
+ }
+#-----------SVC4084---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CHECKED_IN: {
+ code: 409,
+ message: "Error: The current version of '%1' %2 was already checked-in by %3 %4(%5).",
+ messageId: "SVC4084"
+ }
+#-----------SVC4085---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_CHECKOUT_BY_ANOTHER_USER: {
+ code: 403,
+ message: "Error: %1 %2 has already been checked out by %3 %4(%5).",
+ messageId: "SVC4085"
+ }
+#-----------SVC4086---------------------------
+# %1  - resource/service name
+# %2  - resource/service
+ COMPONENT_IN_USE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is in use by another user.",
+ messageId: "SVC4086"
+ }
+#-----------SVC4087---------------------------
+# %1 - component name
+# %2 - resource/service/product
+ COMPONENT_HAS_NEWER_VERSION: {
+ code: 409,
+ message: "Error: Checking out of the requested version of the '%1' %2 is not allowed as a newer version exists.",
+ messageId: "SVC4087"
+ }
+#-----------SVC4088---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CERTIFIED: {
+ code: 403,
+ message: "Error: Requested %1 %2 has already been certified by %3 %4(%5).",
+ messageId: "SVC4088"
+ }
+#-----------SVC4089---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+ COMPONENT_NOT_READY_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification.",
+ messageId: "SVC4089"
+ }
+#-----------SVC4100---------------------------
+#%1 - property name
+ PROPERTY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' property was not found.",
+ messageId: "SVC4100"
+ }
+#-----------SVC4101---------------------------
+#%1 - property name
+ PROPERTY_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Property with '%1' name already exists.",
+ messageId: "SVC4101"
+ }
+
+#-----------SVC4102---------------------------
+# %1 - capability type name
+ CAPABILITY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Capability Type with name '%1' already exists.",
+ messageId: "SVC4102"
+ }
+#-----------SVC4114---------------------------
+ AUTH_FAILED_INVALIDE_HEADER: {
+ code: 400,
+ message: "Error: Invalid Authorization header.",
+ messageId: "SVC4114"
+ }
+#-----------SVC4115---------------------------
+# %1 - capability type name
+ MISSING_CAPABILITY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Capability Type '%1'.",
+ messageId: "SVC4115"
+ }
+ RESOURCE_INSTANCE_BAD_REQUEST: {
+ code: 400,
+ message: "Error: Invalid Content.",
+ messageId: "SVC4116"
+ }
+#-----------SVC4117---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_MATCH_NOT_FOUND: {
+ code: 404,
+ message: "Error: Match not found between resource instance '%1' and resource instance '%2' for requirement '%3'.",
+ messageId: "SVC4117"
+ }
+#-----------SVC4118---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Resource instances '%1' and '%2' are already associated with requirement '%3'.",
+ messageId: "SVC4118"
+ }
+#-----------SVC4119---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_RELATION_NOT_FOUND: {
+ code: 404,
+ message: "Error: No relation found between resource instances '%1' and '%2' for requirement '%3'.",
+ messageId: "SVC4119"
+ }
+#-----------SVC4120---------------------------
+# %1 - User's USER_ID
+ USER_INACTIVE: {
+ code: 404,
+ message: "Error: User %1 was not found.",
+ messageId: "SVC4120"
+ }
+#-----------SVC4121---------------------------
+# %1 - User's USER_ID
+ USER_HAS_ACTIVE_ELEMENTS: {
+ code: 403,
+ message: "Error: User with %1 ID can not be deleted since it has active elements(resources/services/artifacts).",
+ messageId: "SVC4121"
+ }
+#-----------SVC4122---------------------------
+# %1 - artifact type
+ ARTIFACT_TYPE_NOT_SUPPORTED: {
+ code: 400,
+ message: "Error: Invalid artifact type '%1'.",
+ messageId: "SVC4122"
+ }
+#-----------SVC4123---------------------------
+ ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Artifact logical name cannot be changed.",
+ messageId: "SVC4123"
+ }
+#-----------SVC4124---------------------------
+ MISSING_ARTIFACT_TYPE: {
+ code: 400,
+ message: "Error: Missing artifact type.",
+ messageId: "SVC4124"
+ }
+#-----------SVC4125---------------------------
+# %1-artifact name
+ ARTIFACT_EXIST: {
+ code: 400,
+ message: "Error: Artifact '%1' already exists.",
+ messageId: "SVC4125"
+ }
+#---------SVC4126------------------------------
+# %1 - resource/service/product/...
+# %2 - field (tag, vendor name...)
+ INVALID_FIELD_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 format.",
+ messageId: "SVC4126"
+ }
+#-----------SVC4127---------------------------
+ ARTIFACT_INVALID_MD5: {
+ code: 400,
+ message: "Error: Invalid artifact checksum.",
+ messageId: "SVC4127"
+ }
+#-----------SVC4128---------------------------
+ MISSING_ARTIFACT_NAME: {
+ code: 400,
+ message: "Error: Invalid content. Missing artifact name.",
+ messageId: "SVC4128"
+ }
+#-----------SVC4129---------------------------
+ MISSING_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing PROJECT_CODE number.",
+ messageId: "SVC4129"
+ }
+#-----------SVC4130---------------------------
+ INVALID_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. PROJECT_CODE number must be numeric from 5 up to 10 digits.",
+ messageId: "SVC4130"
+ }
+#-----------SVC4131---------------------------
+# %1-resource/service
+# %2-srtifact/artifacts
+# %3-semicolomn separated list of artifact
+ COMPONENT_MISSING_MANDATORY_ARTIFACTS: {
+ code: 403,
+ message: "Error: Missing mandatory informational %1 %2: [%3].",
+ messageId: "SVC4131"
+ }
+#-----------SVC4132---------------------------
+# %1 - lifecycle type name
+ LIFECYCLE_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Lifecycle Type with name '%1' already exists.",
+ messageId: "SVC4132"
+ }
+#-----------SVC4133---------------------------
+# %1 - service version
+# %2 - service name
+ SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION: {
+ code: 403,
+ message: "Error: Version %1 of '%2' service is not available for distribution.",
+ messageId: "SVC4133"
+ }
+#-----------SVC4134---------------------------
+ MISSING_LIFECYCLE_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing interface life-cycle type.",
+ messageId: "SVC4134"
+ }
+#---------SVC4135------------------------------
+ SERVICE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service category cannot be changed once the service is certified.",
+ messageId: "SVC4135"
+ }
+#---------SVC4136------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE: {
+ code: 500,
+ message: "Error: Requested distribution environment '%1' is not available.",
+ messageId: "SVC4136"
+ }
+#---------SVC4137------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_FOUND: {
+ code: 400,
+ message: "Error: Requested distribution environment '%1' was not found.",
+ messageId: "SVC4137"
+ }
+#---------SVC4138------------------------------
+ DISTRIBUTION_ENVIRONMENT_INVALID: {
+ code: 400,
+ message: "Error: Invalid distribution environment.",
+ messageId: "SVC4138"
+ }
+#---------SVC4139------------------------------
+# %1 - service name
+ DISTRIBUTION_ARTIFACT_NOT_FOUND: {
+ code: 409,
+ message: "Error: Service '%1' cannot be distributed due to missing deployment artifacts.",
+ messageId: "SVC4139"
+ }
+#---------SVC4200------------------------------
+# %1 - Service/Resource
+# %2 - max icon name length
+ COMPONENT_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 icon name exceeds limit of %2 characters.",
+ messageId: "SVC4200"
+ }
+#---------SVC4300------------------------------
+ RESTRICTED_ACCESS: {
+ code: 403,
+ message: "Error: Restricted access.",
+ messageId: "SVC4300"
+ }
+#---------SVC4301------------------------------
+ RESTRICTED_OPERATION: {
+ code: 409,
+ message: "Error: Restricted operation.",
+ messageId: "SVC4301"
+ }
+#---------SVC4500------------------------------
+ MISSING_BODY: {
+ code: 400 ,
+ message: "Error: Missing request body.",
+ messageId: "SVC4500"
+ }
+#---------SVC4501------------------------------
+ MISSING_PUBLIC_KEY: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'apiPublicKey'." ,
+ messageId: "SVC4501"
+ }
+#---------SVC4502------------------------------
+ DISTRIBUTION_ENV_DOES_NOT_EXIST: {
+ code: 400 ,
+ message: "Error: Invalid Body : Missing mandatory parameter 'distrEnvName'." ,
+ messageId: "SVC4502"
+ }
+#-----------SVC4503---------------------------
+# %1 - service name
+ SERVICE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' service was not found.",
+ messageId: "SVC4503"
+ }
+
+#---------SVC4504------------------------------
+# %1 - Service/Resource
+# %2 - service/resource version
+ COMPONENT_VERSION_NOT_FOUND: {
+ code: 404,
+ message: "Error: %1 version %2 was not found.",
+ messageId: "SVC4504"
+ }
+#-----------SVC4505---------------------------
+ #%1-artifact name
+
+ ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Artifact '%1' was not found.",
+ messageId: "SVC4505"
+ }
+#---------SVC4506------------------------------
+ MISSING_ENV_NAME: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'distrEnvName'.",
+ messageId: "SVC4506"
+ }
+#---------SVC4507------------------------------
+ COMPONENT_INVALID_TAGS_NO_COMP_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. One of the tags should be the component name.",
+ messageId: "SVC4507"
+ }
+
+#---------SVC4508------------------------------
+ SERVICE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service name cannot be changed once the service is certified.",
+ messageId: "SVC4508"
+ }
+
+#---------SVC4509------------------------------
+ SERVICE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the service is certified.",
+ messageId: "SVC4509"
+ }
+#---------SVC4510------------------------------
+# %1 - icon name max length
+ SERVICE_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Icon name exceeds limit of %1 characters.",
+ messageId: "SVC4510"
+ }
+#---------SVC4511------------------------------
+ DISTRIBUTION_REQUESTED_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested distribution was not found.",
+ messageId: "SVC4511"
+ }
+#---------SVC4512------------------------------
+# %1 - Distribution ID
+ DISTRIBUTION_REQUESTED_FAILED: {
+ code: 403,
+ message: "Error: Requested distribution '%1' failed.",
+ messageId: "SVC4512"
+ }
+#---------SVC4513------------------------------
+ RESOURCE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource category cannot be changed once the resource is certified.",
+ messageId: "SVC4513"
+ }
+#---------SVC4514------------------------------
+ RESOURCE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource name cannot be changed once the resource is certified.",
+ messageId: "SVC4514"
+ }
+#---------SVC4515------------------------------
+ RESOURCE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the resource is certified.",
+ messageId: "SVC4515"
+ }
+#---------SVC4516------------------------------
+ RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Vendor name cannot be changed once the resource is certified.",
+ messageId: "SVC4516"
+ }
+#---------SVC4517------------------------------
+ RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Derived from resource template cannot be changed once the resource is certified.",
+ messageId: "SVC4517"
+ }
+#---------SVC4518------------------------------
+# %1 - max length
+ COMPONENT_SINGLE_TAG_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Single tag exceeds limit of %1 characters.",
+ messageId: "SVC4518"
+ }
+#---------SVC4519------------------------------
+ INVALID_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: mismatch in data-type occurred for property %1. data type is %2 and default value found is %3.",
+ messageId: "SVC4519"
+ }
+#---------SVC4520------------------------------
+# %1 - service or resource
+ ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED: {
+ code: 409,
+ message: "Error: Maximal number of additional %1 parameters was reached.",
+ messageId: "SVC4520"
+ }
+#---------SVC4521------------------------------
+ ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED: {
+ code: 400,
+ message: "Error: Invalid Content. The Additional information label and value cannot be empty.",
+ messageId: "SVC4521"
+ }
+#---------SVC4522------------------------------
+# %1 - label/value
+# %2 - Maximal length of %1
+ ADDITIONAL_INFORMATION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Additional information %1 exceeds limit of %2 characters.",
+ messageId: "SVC4522"
+ }
+#---------SVC4523------------------------------
+ ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information label is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4523"
+ }
+#---------SVC4524------------------------------
+ ADDITIONAL_INFORMATION_NOT_FOUND: {
+ code: 409,
+ message: "Error: Requested additional information was not found.",
+ messageId: "SVC4524"
+ }
+#---------SVC4525------------------------------
+ ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information contains non-english characters.',
+ messageId: "SVC4525"
+ }
+#---------SVC4526------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found.",
+ messageId: "SVC4526"
+ }
+#---------SVC4527------------------------------
+ ASDC_VERSION_NOT_FOUND: {
+ code: 500,
+ message: 'Error: ASDC version cannot be displayed.',
+ messageId: "SVC4527"
+ }
+#---------SVC4528------------------------------
+# %1-artifact url/artifact label/artifact description/VNF Service Indicator
+ MISSING_DATA: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1.",
+ messageId: "SVC4528"
+ }
+#---------SVC4529------------------------------
+# %1-artifact url/artifact label/artifact description/artifact name
+# %2 - Maximal length of %1
+ EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 exceeds limit of %2 characters.",
+ messageId: "SVC4529"
+ }
+#---------SVC4530------------------------------
+ ARTIFACT_INVALID_TIMEOUT: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact Timeout should be set to valid positive non-zero number of minutes.",
+ messageId: "SVC4530"
+ }
+#---------SVC4531------------------------------
+ SERVICE_IS_VNF_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: VNF Indicator cannot be updated for certified service.",
+ messageId: "SVC4531"
+ }
+ #---------SVC4532------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found on the service '%2.",
+ messageId: "SVC4532"
+ }
+ #---------SVC4533------------------------------
+ # %1 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"/"YANG_XML"
+ WRONG_ARTIFACT_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for %1 artifact type.",
+ messageId: "SVC4533"
+ }
+
+#---------SVC4534------------------------------
+# %1 - "HEAT"/"HEAT_ENV"
+ INVALID_YAML: {
+ code: 400,
+ message: "Error: Uploaded YAML file for %1 artifact is invalid.",
+ messageId: "SVC4534"
+ }
+
+#---------SVC4535------------------------------
+# %1 - "HEAT"
+ INVALID_DEPLOYMENT_ARTIFACT_HEAT: {
+ code: 400,
+ message: "Error: Invalid %1 artifact.",
+ messageId: "SVC4535"
+ }
+#---------SVC4536------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"
+# %4 - "HEAT"/"HEAT_ENV"/"MURANO_PKG
+ DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact of %3 type .Please delete or update an existing %4 artifact.",
+ messageId: "SVC4536"
+ }
+
+#---------SVC4537------------------------------
+ MISSING_HEAT: {
+ code: 400,
+ message: "Error: Missing HEAT artifact. HEAT_ENV artifact cannot be uploaded without corresponding HEAT template.",
+ messageId: "SVC4537"
+ }
+#---------SVC4538------------------------------
+ MISMATCH_HEAT_VS_HEAT_ENV: {
+ code: 400,
+ message: "Error: Invalid artifact content. Parameter's set in HEAT_ENV '%1' artifact doesn't match the parameters in HEAT '%2' artifact.",
+ messageId: "SVC4538"
+ }
+#---------SVC4539------------------------------
+ INVALID_RESOURCE_PAYLOAD: {
+ code: 400,
+ message: "Error: Invalid resource payload.",
+ messageId: "SVC4539"
+ }
+#---------SVC4540------------------------------
+ INVALID_TOSCA_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for TOSCA template.",
+ messageId: "SVC4540"
+ }
+#---------SVC4541------------------------------
+ INVALID_YAML_FILE: {
+ code: 400,
+ message: "Error: Invalid YAML file.",
+ messageId: "SVC4541"
+ }
+#---------SVC4542------------------------------
+ INVALID_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid TOSCA template.",
+ messageId: "SVC4542"
+ }
+#---------SVC4543------------------------------
+ NOT_RESOURCE_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Imported Service TOSCA template.",
+ messageId: "SVC4543"
+ }
+#---------SVC4544------------------------------
+ NOT_SINGLE_RESOURCE: {
+ code: 400,
+ message: "Error: Imported TOSCA template should contain one resource definition.",
+ messageId: "SVC4544"
+ }
+#---------SVC4545------------------------------
+ INVALID_RESOURCE_NAMESPACE: {
+ code: 400,
+ message: "Error: Invalid resource namespace.",
+ messageId: "SVC4545"
+ }
+#---------SVC4546------------------------------
+ RESOURCE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: Imported resource already exists in ASDC Catalog.",
+ messageId: "SVC4546"
+ }
+#---------SVC4549------------------------------
+ INVALID_RESOURCE_CHECKSUM: {
+ code: 400,
+ message: "Error: Invalid resource checksum.",
+ messageId: "SVC4549"
+ }
+#---------SVC4550------------------------------
+ #%1  -  Consumer salt
+ INVALID_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 length.",
+ messageId: "SVC4550"
+ }
+ #---------SVC4551------------------------------
+ #%1  -  ECOMP User name
+ ECOMP_USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: ECOMP User '%1' was not found.",
+ messageId: "SVC4551"
+ }
+#---------SVC4552------------------------------
+ CONSUMER_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: ECOMP User already exists.",
+ messageId: "SVC4552"
+ }
+#---------SVC4553-----------------------------
+ #%1  -  Consumer name / Consumer password/ Consumer salt
+ INVALID_CONTENT_PARAM: {
+ code: 400,
+ message: "Error: %1 is invalid.",
+ messageId: "SVC4553"
+ }
+ #---------SVC4554------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested artifact doesn't belong to specified %1.",
+ messageId: "SVC4554"
+ }
+#---------SVC4554------------------------------
+# %1 - "Service name"
+ SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND: {
+ code: 403,
+ message: "Error: Requested '%1' service is not ready for certification. Service has to have at least one deployment artifact.",
+ messageId: "SVC4554"
+ }
+#---------SVC4555------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name length.",
+ messageId: "SVC4555"
+ }
+#---------SVC4556------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name format.",
+ messageId: "SVC4556"
+ }
+#---------SVC4557------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category name"
+ COMPONENT_CATEGORY_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: %1 category name '%2' already exists.",
+ messageId: "SVC4557"
+ }
+#---------SVC4558------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ VALIDATED_RESOURCE_NOT_FOUND: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource.",
+ messageId: "SVC4558"
+ }
+#---------SVC4559------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_ALREADY_VALIDATED_RESOURCE: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use already available validated resource version.",
+ messageId: "SVC4559"
+ }
+#---------SVC4560------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_LIST_VALIDATED_RESOURCES: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use one of available validated resource versions.",
+ messageId: "SVC4560"
+ }
+#---------SVC4561------------------------------
+# %1 - "resource"/"product"
+# %2 - "category"
+# %3 - "category name"
+ COMPONENT_CATEGORY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested %1 %2 '%3' was not found.",
+ messageId: "SVC4561"
+ }
+#---------SVC4562------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %3 - "category name"
+ COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY: {
+ code: 409,
+ message: "Error: %1 sub-category '%2' already exists under '%3' category.",
+ messageId: "SVC4562"
+ }
+#---------SVC4563------------------------------
+# %1 - "Product"
+# %2 - "grouping name"
+# %3 - "sub-category name"
+ COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY: {
+ code: 409,
+ message: "Error: %1 grouping '%2' already exists under '%3' sub-category.",
+ messageId: "SVC4563"
+ }
+#---------SVC4564------------------------------
+# %1 - product name
+ PRODUCT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' product was not found.",
+ messageId: "SVC4564"
+ }
+#---------SVC4565------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+# %3 - parameter name
+ INVALID_HEAT_PARAMETER_VALUE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Invalid %2 value set for '%3' parameter.",
+ messageId: "SVC4565"
+ }
+#---------SVC4566------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+ INVALID_HEAT_PARAMETER_TYPE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Unsupported '%2' parameter type.",
+ messageId: "SVC4566"
+ }
+#---------SVC4567------------------------------
+# %1 - "YANG_XML"
+ INVALID_XML: {
+ code: 400,
+ message: "Error: Uploaded XML file for %1 artifact is invalid.",
+ messageId: "SVC4567"
+ }
+#---------SVC4567------------------------------
+# %1 - "User Name and userId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: User cannot be deleted. User '%1' has %2 projects.",
+ messageId: "SVC4567"
+ }
+#---------SVC4568------------------------------
+# %1 - "User Name and userId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: Role cannot be changed. User '%1' has %2 projects.",
+ messageId: "SVC4568"
+ }
+#---------SVC4570------------------------------
+ UPDATE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator is not allowed to change his/her role.",
+ messageId: "SVC4570"
+ }
+#---------SVC4571------------------------------
+ SERVICE_CANNOT_CONTAIN_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Sub category cannot be defined for service",
+ messageId: "SVC4571"
+ }
+#---------SVC4572------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_TOO_MUCH_CATEGORIES: {
+ code: 400,
+ message: "Error: %1 must have only 1 category",
+ messageId: "SVC4572"
+ }
+#---------SVC4574------------------------------
+ RESOURCE_TOO_MUCH_SUBCATEGORIES: {
+ code: 400,
+ message: "Error: Resource must have only 1 sub category",
+ messageId: "SVC4574"
+ }
+#---------SVC4575------------------------------
+ COMPONENT_MISSING_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Missing sub category",
+ messageId: "SVC4575"
+ }
+ #---------SVC4576------------------------------
+# %1 - "component type"
+ UNSUPPORTED_ERROR: {
+ code: 400,
+ message: "Error : Requested component type %1 is unsupported.",
+ messageId: "SVC4576"
+ }
+ #---------SVC4577------------------------------
+# %1 - "resource type"
+ RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES: {
+ code: 409,
+ message: "Error : Resource of type %1 cannot contain resource instances.",
+ messageId: "SVC4577"
+ }
+#---------SVC4578------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "artifact name"
+ DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact named '%3'.",
+ messageId: "SVC4578"
+ }
+#---------SVC4579------------------------------
+# %1 - "Category"/"Sub-Category"/"Group"
+# %2 - category/sub-category/grouping name.
+ INVALID_GROUP_ASSOCIATION: {
+ code: 400,
+ message: "Error: Invalid group association. %1 '%2' was not found.",
+ messageId: "SVC4579"
+ }
+#---------SVC4580------------------------------
+ EMPTY_PRODUCT_CONTACTS_LIST: {
+ code: 400,
+ message: "Error: Invalid content. At least one Product Contact has to be specified.",
+ messageId: "SVC4580"
+ }
+#---------SVC4581------------------------------
+# %1 - userId
+ INVALID_PRODUCT_CONTACT: {
+ code: 400,
+ message: "Error: Invalid content. User '%1' cannot be set as Product Contact.",
+ messageId: "SVC4581"
+ }
+#---------SVC4582------------------------------
+# %1 - Product
+# %2 - "abbreviated"/"full"
+ MISSING_ONE_OF_COMPONENT_NAMES: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1 %2 name.",
+ messageId: "SVC4582"
+ }
+#---------SVC4583------------------------------
+# %1 - "Icon"
+# %2 - "resource"/"service"/"product"
+ COMPONENT_PARAMETER_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: %1 cannot be changed once the %2 is certified.",
+ messageId: "SVC4583"
+ }
+#---------SVC4584------------------------------
+# %1 - service/VF name
+# %2 - "service" /"VF"
+# %3 - resource instance origin type
+# %4 - resource instance name
+# %5 - requirement/capability
+# %6 - requirement/capability name
+# %7 - "fulfilled" (for req)/"consumed (for cap)"
+ REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification. %3 '%4' has to have %5 '%6' %7.",
+ messageId: "SVC4584"
+ }
+#---------SVC4585------------------------------
+ INVALID_OCCURRENCES: {
+ code: 400,
+ message: "Error: Invalid occurrences format.",
+ messageId: "SVC4585"
+ }
+#---------SVC4586------------------------------
+#---------SVC4586------------------------------
+ INVALID_SERVICE_API_URL: {
+ code: 400,
+ message: 'Error: Invalid Service API URL. Please check whether your URL has a valid domain extension and does not contain the following characters - #?&@%+;,=$<>~^`\[]{}|"*!',
+ messageId: "SVC4586"
+ }
+#---------SVC4587------------------------------
+# %1 - Data type name
+ DATA_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Data type %1 already exists.',
+ messageId: "SVC4587"
+ }
+#---------SVC4588------------------------------
+# %1 - Data type name
+ DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM: {
+ code: 400,
+ message: 'Error: Invalid Data type %1. Data type must have either a valid derived from declaration or at least one valid property',
+ messageId: "SVC4588"
+ }
+#---------SVC4589------------------------------
+# %1 - Data type name
+ DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Data type %1. 'properties' parameter cannot be empty if provided.",
+ messageId: "SVC4589"
+ }
+#---------SVC4590------------------------------
+# %1 - Property type name
+# %2 - Property name
+ INVALID_PROPERTY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Property type %1 in property %2.",
+ messageId: "SVC4590"
+ }
+#---------SVC4591------------------------------
+# %1 - Property inner type
+# %2 - Property name
+ INVALID_PROPERTY_INNER_TYPE: {
+ code: 400,
+ message: "Error: Invalid property inner type %1, in property %2",
+ messageId: "SVC4591"
+ }
+#---------SVC4592------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+ COMPONENT_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found.",
+ messageId: "SVC4592"
+ }
+#---------SVC4593------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+# %3 - "resource/"service"/"product"
+# %4 - container name
+ COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found on the %3 '%4'.",
+ messageId: "SVC4593"
+ }
+#---------SVC4594------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+ IMPORT_DUPLICATE_REQ_CAP_NAME: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains more than one %1 named '%2'.",
+ messageId: "SVC4594"
+ }
+#---------SVC4595------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+#%3 - parent containing the requirement
+ IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains %1 '%2' that is already defined by derived template %3.",
+ messageId: "SVC4595"
+ }
+#---------SVC4596------------------------------
+# %1 - Data type name
+ DATA_TYPE_DERIVED_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The ancestor data type %1 cannot be found in the system.",
+ messageId: "SVC4596"
+ }
+#---------SVC4597------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties named %2 which are already defined in one of its ancestors.",
+ messageId: "SVC4597"
+ }
+#---------SVC4598------------------------------
+# %1 - Data type name
+ DATA_TYPE_DUPLICATE_PROPERTY: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains duplicate property.",
+ messageId: "SVC4598"
+ }
+#---------SVC4599------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties %2 which their type is this data type.",
+ messageId: "SVC4599"
+ }
+#---------SVC4600------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_HAVE_PROPERTIES: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 cannot have properties since it is of type scalar",
+ messageId: "SVC4600"
+ }
+#---------SVC4601------------------------------
+ NOT_TOPOLOGY_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file %1 cannot be modeled to VF as it does not contain 'topology_template.",
+ messageId: "SVC4601"
+ }
+#---------SVC4602--------------------------------
+# %1 - yaml file name
+# %2 - node_template label
+# %3 - node_template type
+ INVALID_NODE_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file '%1' contains node_template '%2' of type '%3' that does not represent existing VFC/CP/VL",
+ messageId: "SVC4602"
+ }
+#---------SVC4603------------------------------
+# %1 - component type
+# %2 - component name
+# %3 - state
+ ILLEGAL_COMPONENT_STATE: {
+ code: 403,
+ message: "Error: Component instance of %1 can not be created because the component '%2' is in an illegal state %3.",
+ messageId: "SVC4603"
+ }
+#---------SVC4604------------------------------
+# %1 - csar file name
+ CSAR_INVALID: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. 'TOSCA-Metadata/Tosca.meta' file must be provided.",
+ messageId: "SVC4604"
+ }
+#---------SVC4605------------------------------
+# %1 - csar file name
+ CSAR_INVALID_FORMAT: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. Invalid 'TOSCA-Metadata/Tosca.meta' file format.",
+ messageId: "SVC4605"
+ }
+#---------SVC4606------------------------------
+# %1 - property name
+# %2 - property type
+# %3 - property innerType
+# %4 - default value is
+ INVALID_COMPLEX_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: Invalid default value of property %1. Data type is %2 with inner type %3 and default value found is %4.",
+ messageId: "SVC4606"
+ }
+#---------SVC4607------------------------------
+# %1 - csar file name
+ CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is not found.",
+ messageId: "SVC4607"
+ }
+#---------SVC4608------------------------------
+# %1 - artifact name
+# %2 - component type
+# %3 - actual component type
+ MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE: {
+ code: 400,
+ message: "Error: Artifact %1 is only compatible with component of type %2, but component type is %3.",
+ messageId: "SVC4608"
+ }
+
+#---------SVC4609------------------------------
+# %1 - "INVALID_JSON"
+ INVALID_JSON: {
+ code: 400,
+ message: "Error: Uploaded JSON file for %1 artifact is invalid.",
+ messageId: "SVC4609"
+ }
+#---------SVC4610------------------------------
+# %1 - csar file name
+# %2 - missing file name
+ YAML_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error - TOSCA CSAR %1 is invalid. TOSCA-Metadata/Tosca.meta refers to file %2 that is not provided.",
+ messageId: "SVC4610"
+ }
+#---------SVC4611------------------------------
+# %1 - group name
+ GROUP_MEMBER_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Group %1 member list was provided but does not have values",
+ messageId: "SVC4611"
+ }
+#---------SVC4612------------------------------
+# %1 - group name
+ GROUP_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Group type %1 already exists.',
+ messageId: "SVC4612"
+ }
+#---------SVC4613------------------------------
+# %1 - group name
+# %2 - VF name(component name)
+# %3 - actual component type [VF]
+ GROUP_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Group with name '%1' already exists in %2 %3.",
+ messageId: "SVC4613"
+ }
+#---------SVC4614------------------------------
+# %1 - group type
+ GROUP_TYPE_IS_INVALID: {
+ code: 400,
+ message: "Error: Invalid content. Group type %1 does not exist",
+ messageId: "SVC4614"
+ }
+#---------SVC4615------------------------------
+# %1 - group name
+ GROUP_MISSING_GROUP_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Group Type for group '%1'",
+ messageId: "SVC4615"
+ }
+#---------SVC4616------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - VF name
+# %4 - component type [VF ]
+ GROUP_INVALID_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: Member '%1' listed in group '%2' is not part of '%3' %4.",
+ messageId: "SVC4616"
+ }
+#---------SVC4617------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - group type
+ GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of allowed members of group type %3.",
+ messageId: "SVC4617"
+ }
+#---------SVC4618------------------------------
+# %1 - missing file name
+# %2 - csar file name
+ ARTIFACT_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 is defined in CSAR %2 manifest but is not provided",
+ messageId: "SVC4618"
+ }
+#---------SVC4619------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - existing artifact type
+ ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 already exists in type %3.",
+ messageId: "SVC4619"
+ }
+#---------SVC4620------------------------------
+ FAILED_RETRIVE_ARTIFACTS_TYPES: {
+ code: 400,
+ message: "Error: Failed to retrieve list of suported artifact types.",
+ messageId: "SVC4620"
+ }
+#---------SVC4621------------------------------
+# %1 - artifact name
+# %2 - master
+ ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 already exists in master %2 .",
+ messageId: "SVC4621"
+ }
+#---------SVC4622------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - master name
+# %4 - master type
+ ARTIFACT_NOT_VALID_IN_MASTER: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 can not be exists under master %3 in type %4.",
+ messageId: "SVC4622"
+ }
+#---------SVC4623------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - env name
+# %4 - existing env
+ ARTIFACT_NOT_VALID_ENV: {
+ code: 400,
+ message: "Error: Artifact %1 in type %2 with env %3 already exists with another env %4",
+ messageId: "SVC4623"
+ }
+#---------SVC4624------------------------------
+# %1 - groups names
+# %2 - VF name
+# %3 - component type [VF ]
+ GROUP_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The groups '%1' cannot be found under %2 %3.",
+ messageId: "SVC4624"
+ }
+#---------SVC4625------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_ASSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already associated to group '%1'.",
+ messageId: "SVC4625"
+ }
+#---------SVC4626------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_DISSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already dissociated from group '%1'.",
+ messageId: "SVC4626"
+ }
+#---------SVC4627------------------------------
+# %1 - property name
+# %2 - group name
+# %3 - group type name
+ GROUP_PROPERTY_NOT_FOUND: {
+ code: 400,
+ message: "Error: property %1 listed in group %2 is not exist in group type %3.",
+ messageId: "SVC4627"
+ }
+#---------SVC4628------------------------------
+# %1 - csarUUID
+# %2 - VF name
+ VSP_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: The VSP with UUID %1 was already imported for VF %2. Please select another or update the existing VF.",
+ messageId: "SVC4628"
+ }
+#---------SVC4629------------------------------
+# %1 - VF name
+ MISSING_CSAR_UUID: {
+ code: 400,
+ message: "Error: The Csar UUID or payload name is missing for VF %1.",
+ messageId: "SVC4629"
+ }
+#---------SVC4630------------------------------
+# %1 - VF name
+# %2 - new csarUUID
+# %3 - old csarUUID
+ RESOURCE_LINKED_TO_DIFFERENT_VSP: {
+ code: 400,
+ message: "Error: Resource %1 cannot be updated using CsarUUID %2 since the resource is linked to a different VSP with csarUUID %3.",
+ messageId: "SVC4630"
+ }
+#---------SVC4631------------------------------
+# %1 - policy name
+ POLICY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Policy type %1 already exists.",
+ messageId: "SVC4631"
+ }
+#---------SVC4632------------------------------
+# %1 - target name
+# %2 - policy type name
+ TARGETS_NON_VALID: {
+ code: 400,
+ message: "Error: target %1 listed in policy type %2 is not a group or resource.",
+ messageId: "SVC4632"
+ }
+#---------SVC4633------------------------------
+# %1 - policy name
+ TARGETS_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Policy %1 target list was provided but does not have values",
+ messageId: "SVC4633"
+ }
+#---------SVC4634------------------------------
+ DATA_TYPE_CANNOT_BE_EMPTY: {
+ code: 500,
+ message: "Error: Data types are empty. Please import the data types.",
+ messageId: "SVC4634"
+ }
+#---------SVC4635------------------------------
+# %1 - csar uuid
+ RESOURCE_FROM_CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: resource from csar uuid %1 not found",
+ messageId: "SVC4635"
+ }
+#---------SVC4636------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST: {
+ code: 400,
+ message: 'Error: Data type %1 cannot be upgraded. The new data type does not contain old properties or the type of one of the properties has been changed.',
+ messageId: "SVC4636"
+ }
+#-----------SVC4637---------------------------
+#%1 - attribute name
+ ATTRIBUTE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' attribute was not found.",
+ messageId: "SVC4637"
+ }
+#-----------SVC4638---------------------------
+#%1 - attribute name
+ ATTRIBUTE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Attribute with '%1' name already exists.",
+ messageId: "SVC4638"
+ }
+#-----------SVC4639---------------------------
+#%1 - property name
+ PROPERTY_NAME_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: Property with '%1' name and different type already exists.",
+ messageId: "SVC4639"
+ }
+#-----------SVC4640---------------------------
+#%1 - property name
+ INVALID_PROPERTY: {
+ code: 409,
+ message: "Error: Invalid property received.",
+ messageId: "SVC4640"
+ }
+#---------SVC4641-----------------------------
+#%1 - invalid filter
+#%2 - valid filters
+ INVALID_FILTER_KEY: {
+ code: 400,
+ message: "Error: The filter %1 is not applicable. Please use one of the following filters: %2",
+ messageId: "SVC4641"
+ }
+#---------SVC4642-----------------------------
+#%1 - asset type
+#%2 - filter
+ NO_ASSETS_FOUND: {
+ code: 404,
+ message: "No %1 were found to match criteria %2",
+ messageId: "SVC4642"
+ }
+#---------SVC4643------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %3 - "category name"
+ COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY: {
+ code: 404,
+ message: "Error: %1 sub-category '%2' not found under category '%3'.",
+ messageId: "SVC4643"
+ }
+#---------SVC4644------------------------------
+# %1 - Format
+ CORRUPTED_FORMAT: {
+ code: 400,
+ message: "Error: %1 format is corrupted.",
+ messageId: "SVC4644"
+ }
+#---------SVC4645------------------------------
+# %1 - "groupType"
+ INVALID_VF_MODULE_TYPE: {
+ code: 400,
+ message: "Error: Invalid group type '%1' (should be VfModule).",
+ messageId: "SVC4645"
+ }
+#---------SVC4646------------------------------
+# %1 - "groupName"
+ INVALID_VF_MODULE_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. VF Module name '%1' contains invalid characters",
+ messageId: "SVC4646"
+ }
+
+#---------SVC4647------------------------------
+# %1 - "modifiedName"
+ INVALID_VF_MODULE_NAME_MODIFICATION: {
+ code: 400,
+ message: "Error: Invalid VF Module name modification, can not modify '%1'",
+ messageId: "SVC4647"
+ }
+#---------SVC4648------------------------------
+# %1 - "inputId"
+# %2 - "componentId"
+ INPUT_IS_NOT_CHILD_OF_COMPONENT: {
+ code: 400,
+ message: "Error: Input id: '%1' is not child of component id: '%2'",
+ messageId: "SVC4648"
+ }
+#---------SVC4649------------------------------
+# %1 - "groupName"
+ GROUP_HAS_CYCLIC_DEPENDENCY: {
+ code: 400,
+ message: "Error: The group '%1' has cyclic dependency",
+ messageId: "SVC4649"
+ }
+#---------SVC4650------------------------------
+# %1 - "Component Type"
+# %2 - <ServiceName>
+# %3 - error description
+ AAI_ARTIFACT_GENERATION_FAILED: {
+ code: 500,
+ message: "Error: %1 %2 automatic generation of artifacts failed. Description: %3",
+ messageId: "SVC4650"
+ }
diff --git a/catalog-be/src/main/resources/config/logback.xml b/catalog-be/src/main/resources/config/logback.xml
new file mode 100644
index 0000000000..5d5e4c670d
--- /dev/null
+++ b/catalog-be/src/main/resources/config/logback.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="5 seconds">
+
+ <property scope="system" name="ECOMP-component-name" value="ASDC" />
+ <property scope="system" name="ECOMP-subcomponent-name" value="ASDC-BE" />
+ <property file="${config.home}/catalog-be/configuration.yaml" />
+ <property scope="context" name="enable-all-log" value="false" />
+
+ <!-- value used by pattern field list (| - is inter-field separator, || - unavailable or not applicable field value) (m - mandatory, o- optional)-->
+ <!--timestamp(m)| requestID(m)| serviceInstanceID(o)| threadID(m)| physicalServerName(o)| serviceName(m)| userID(m)| logLevel(m)| severity(o)| serverIpAddress(m)| serverName(m)| clientIpAddress(o)| className(m)| timer(o)| detailedMessage(o)-->
+ <property name="default-log-pattern"
+ value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{uuid}|%X{serviceInstanceID}|%thread||${ECOMP-subcomponent-name}|%X{userId}|%level|%X{alarmSeverity}|%X{localAddr}|${beFqdn}|%X{remoteAddr}|%logger{35}|%X{timer}|ActivityType=&lt;%M&gt;, Desc=&lt;%msg&gt;%n" />
+
+ <!-- All log -->
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender name="ALL_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log
+ </file>
+
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="ASYNC_ALL" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ALL_ROLLING" />
+ </appender>
+ </then>
+ </if>
+
+ <!-- Error log -->
+ <appender name="ERROR_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log
+ </file>
+
+ <!-- Audit messages filter - deny audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>AUDIT_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- Transaction messages filter - deny Transaction messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Debug log -->
+ <appender name="DEBUG_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log
+ </file>
+
+ <!-- No need to deny audit messages - they are INFO only, will be denied
+ anyway -->
+ <!-- Transaction messages filter - deny Transaction messages, there are
+ some DEBUG level messages among them -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- accept DEBUG and TRACE level -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
+ <expression>
+ e.level.toInt() &lt;= DEBUG.toInt()
+ </expression>
+ </evaluator>
+ <OnMismatch>DENY</OnMismatch>
+ <OnMatch>NEUTRAL</OnMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Audit log -->
+ <appender name="AUDIT_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/audit.log
+ </file>
+
+ <!-- Audit messages filter - accept audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>AUDIT_MARKER</marker>
+ </evaluator>
+ <onMismatch>DENY</onMismatch>
+ <onMatch>ACCEPT</onMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/audit.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- SdncTransaction log -->
+ <appender name="TRANSACTION_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/transaction.log
+ </file>
+
+ <!-- Transaction messages filter - accept audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>DENY</onMismatch>
+ <onMatch>ACCEPT</onMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/transaction.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Asynchronicity Configurations -->
+ <appender name="ASYNC_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="DEBUG_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_TRANSACTION" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="TRANSACTION_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ERROR_ROLLING" />
+ </appender>
+
+
+ <root level="INFO">
+ <appender-ref ref="ASYNC_ERROR" />
+ <appender-ref ref="ASYNC_DEBUG" />
+ <appender-ref ref="AUDIT_ROLLING" />
+ <appender-ref ref="ASYNC_TRANSACTION" />
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender-ref ref="ALL_ROLLING" />
+ </then>
+ </if>
+ </root>
+
+ <logger name="org.openecomp.sdc" level="INFO" />
+</configuration> \ No newline at end of file
diff --git a/catalog-be/src/main/resources/config/neo4j-errors-configuration.yaml b/catalog-be/src/main/resources/config/neo4j-errors-configuration.yaml
new file mode 100644
index 0000000000..7a0d6dbfd4
--- /dev/null
+++ b/catalog-be/src/main/resources/config/neo4j-errors-configuration.yaml
@@ -0,0 +1,60 @@
+# Errors
+errors:
+ Neo_ClientError_General_ReadOnly: "This is a read only database, writing or modifying the database is not allowed."
+ Neo_ClientError_LegacyIndex_NoSuchIndex: "The request (directly or indirectly) referred to a index that does not exist."
+ Neo_ClientError_Request_Invalid: "The client provided an invalid Request."
+ Neo_ClientError_Request_InvalidFormat: "The client provided a request that was missing required fields, or had values that are not allowed."
+ Neo_ClientError_Schema_ConstraintAlreadyExists: "Unable to perform operation because it would clash with a pre-existing constraint."
+ Neo_ClientError_Schema_ConstraintVerificationFailure: "Unable to create constraint because data that exists in the database violates it."
+ Neo_ClientError_Schema_ConstraintViolation: "A constraint imposed by the database was violated."
+ Neo_ClientError_Schema_IllegalTokenName: "A token name, such as a label, relationship type or property key, used is not valid. Tokens cannot be empty strings and cannot be null."
+ Neo_ClientError_Schema_IndexAlreadyExists: "Unable to perform operation because it would clash with a pre-existing index."
+ Neo_ClientError_Schema_IndexBelongsToConstraint: "A requested operation can not be performed on the specified index because the index is part of a constraint. If you want to drop the index, for instance, you must drop the constraint."
+ Neo_ClientError_Schema_IndexLimitReached: "The maximum number of index entries supported has been reached, no more entities can be indexed."
+ Neo_ClientError_Schema_LabelLimitReached: "The maximum number of labels supported has been reached, no more labels can be created."
+ Neo_ClientError_Schema_NoSuchConstraint: "The request (directly or indirectly) referred to a constraint that does not exist."
+ Neo_ClientError_Schema_NoSuchIndex: "The request (directly or indirectly) referred to an index that does not exist."
+ Neo_ClientError_Security_AuthenticationFailed: "The client provided an incorrect username and/or password."
+ Neo_ClientError_Security_AuthenticationRateLimit: "The client has provided incorrect authentication details too many times in a row."
+ Neo_ClientError_Security_AuthorizationFailed: "The client does not have privileges to perform the operation requested."
+ Neo_ClientError_Statement_ArithmeticError: "Invalid use of arithmetic, such as dividing by zero."
+ Neo_ClientError_Statement_ConstraintViolation: "A constraint imposed by the statement is violated by the data in the database."
+ Neo_ClientError_Statement_EntityNotFound: "The statement is directly referring to an entity that does not exist."
+ Neo_ClientError_Statement_InvalidArguments: "The statement is attempting to perform operations using invalid arguments"
+ Neo_ClientError_Statement_InvalidSemantics: "The statement is syntactically valid, but expresses something that the database cannot do."
+ Neo_ClientError_Statement_InvalidSyntax: "The statement contains invalid or unsupported syntax."
+ Neo_ClientError_Statement_InvalidType: "The statement is attempting to perform operations on values with types that are not supported by the operation."
+ Neo_ClientError_Statement_NoSuchLabel: "The statement is referring to a label that does not exist."
+ Neo_ClientError_Statement_NoSuchProperty: "The statement is referring to a property that does not exist."
+ Neo_ClientError_Statement_ParameterMissing: "The statement is referring to a parameter that was not provided in the Request."
+ Neo_ClientError_Transaction_ConcurrentRequest: "There were concurrent requests accessing the same transaction, which is not allowed."
+ Neo_ClientError_Transaction_EventHandlerThrewException: "A transaction event handler threw an exception. The transaction will be rolled back."
+ Neo_ClientError_Transaction_HookFailed: "Transaction hook failure."
+ Neo_ClientError_Transaction_InvalidType: "The transaction is of the wrong type to service the Request_ For instance, a transaction that has had schema modifications performed in it cannot be used to subsequently perform data operations, and vice versa."
+ Neo_ClientError_Transaction_MarkedAsFailed: "Transaction was marked as both successful and failed. Failure takes precedence and so this transaction was rolled back although it may have looked like it was going to be committed"
+ Neo_ClientError_Transaction_UnknownId: "The request referred to a transaction that does not exist."
+ Neo_ClientError_Transaction_ValidationFailed: "Transaction changes did not pass validation checks"
+ Neo_DatabaseError_General_CorruptSchemaRule: "A malformed schema rule was encountered. Please contact your support representative."
+ Neo_DatabaseError_General_FailedIndex: "The request (directly or indirectly) referred to an index that is in a failed state. The index needs to be dropped and recreated manually."
+ Neo_DatabaseError_General_UnknownFailure: "An unknown failure occurred."
+ Neo_DatabaseError_Schema_ConstraintCreationFailure: "Creating a requested constraint failed."
+ Neo_DatabaseError_Schema_ConstraintDropFailure: "The database failed to drop a requested constraint."
+ Neo_DatabaseError_Schema_IndexCreationFailure: "Failed to create an index."
+ Neo_DatabaseError_Schema_IndexDropFailure: "The database failed to drop a requested index."
+ Neo_DatabaseError_Schema_NoSuchLabel: "The request accessed a label that did not exist."
+ Neo_DatabaseError_Schema_NoSuchPropertyKey: "The request accessed a property that does not exist."
+ Neo_DatabaseError_Schema_NoSuchRelationshipType: "The request accessed a relationship type that does not exist."
+ Neo_DatabaseError_Schema_NoSuchSchemaRule: "The request referred to a schema rule that does not exist."
+ Neo_DatabaseError_Statement_ExecutionFailure: "The database was unable to execute the Statement."
+ Neo_DatabaseError_Transaction_CouldNotBegin: "The database was unable to start the Transaction."
+ Neo_DatabaseError_Transaction_CouldNotCommit: "The database was unable to commit the Transaction."
+ Neo_DatabaseError_Transaction_CouldNotRollback: "The database was unable to roll back the Transaction."
+ Neo_DatabaseError_Transaction_CouldNotWriteToLog: "The database was unable to write transaction to log."
+ Neo_DatabaseError_Transaction_ReleaseLocksFailed: "The transaction was unable to release one or more of its locks."
+ Neo_TransientError_General_DatabaseUnavailable: "The database is not currently available to serve your request, refer to the database logs for more details. Retrying your request at a later time may succeed."
+ Neo_TransientError_Network_UnknownFailure: "An unknown network failure occurred, a retry may resolve the issue."
+ Neo_TransientError_Schema_ModifiedConcurrently: "The database schema was modified while this transaction was running, the transaction should be retried."
+ Neo_TransientError_Security_ModifiedConcurrently: "The user was modified concurrently to this Request."
+ Neo_TransientError_Statement_ExternalResourceFailure: "The external resource is not available"
+ Neo_TransientError_Transaction_AcquireLockTimeout: "The transaction was unable to acquire a lock, for instance due to a timeout or the transaction thread being interrupted."
+ Neo_TransientError_Transaction_DeadlockDetected: "This transaction, and at least one more transaction, has acquired locks in a way that it will wait indefinitely, and the database has aborted it. Retrying this transaction will most likely be successful." \ No newline at end of file
diff --git a/catalog-be/src/main/resources/config/titan.properties b/catalog-be/src/main/resources/config/titan.properties
new file mode 100644
index 0000000000..7eb57235c7
--- /dev/null
+++ b/catalog-be/src/main/resources/config/titan.properties
@@ -0,0 +1,10 @@
+storage.backend=cassandra
+storage.hostname=localhost
+storage.port=9160
+
+cache.db-cache = false
+cache.db-cache-clean-wait = 20
+cache.db-cache-time = 180000
+cache.db-cache-size = 0.5
+
+cache.tx-cache-size = 500000
diff --git a/catalog-be/src/main/resources/elasticsearch.yml b/catalog-be/src/main/resources/elasticsearch.yml
new file mode 100644
index 0000000000..b4634c8213
--- /dev/null
+++ b/catalog-be/src/main/resources/elasticsearch.yml
@@ -0,0 +1,393 @@
+
+cluster.name: elasticsearch_1_5_2
+
+discovery.zen.ping.unicast.hosts: elasticsearch_host
+
+http.cors.enabled: true
+#plugin.types: "DeleteByQueryPlugin"
+#path.home: "/home/vagrant/catalog-be/config"
+
+elasticSearch.transportclient: true
+
+transport.client.initial_nodes:
+ - elasticsearch_host:9300
+
+##################### Elasticsearch Configuration Example #####################
+
+# This file contains an overview of various configuration settings,
+# targeted at operations staff. Application developers should
+# consult the guide at <http://elasticsearch.org/guide>.
+#
+# The installation procedure is covered at
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
+#
+# Elasticsearch comes with reasonable defaults for most settings,
+# so you can try it out without bothering with configuration.
+#
+# Most of the time, these defaults are just fine for running a production
+# cluster. If you're fine-tuning your cluster, or wondering about the
+# effect of certain configuration option, please _do ask_ on the
+# mailing list or IRC channel [http://elasticsearch.org/community].
+
+# Any element in the configuration can be replaced with environment variables
+# by placing them in ${...} notation. For example:
+#
+# node.rack: ${RACK_ENV_VAR}
+
+# For information on supported formats and syntax for the config file, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
+
+
+################################### Cluster ###################################
+
+# Cluster name identifies your cluster for auto-discovery. If you're running
+# multiple clusters on the same network, make sure you're using unique names.
+#
+# cluster.name: elasticsearch
+
+
+#################################### Node #####################################
+
+# Node names are generated dynamically on startup, so you're relieved
+# from configuring them manually. You can tie this node to a specific name:
+#
+# node.name: "Franz Kafka"
+
+# Every node can be configured to allow or deny being eligible as the master,
+# and to allow or deny to store the data.
+#
+# Allow this node to be eligible as a master node (enabled by default):
+#
+# node.master: true
+#
+# Allow this node to store data (enabled by default):
+#
+# node.data: true
+
+# You can exploit these settings to design advanced cluster topologies.
+#
+# 1. You want this node to never become a master node, only to hold data.
+# This will be the "workhorse" of your cluster.
+#
+# node.master: false
+# node.data: true
+#
+# 2. You want this node to only serve as a master: to not store any data and
+# to have free resources. This will be the "coordinator" of your cluster.
+#
+# node.master: true
+# node.data: false
+#
+# 3. You want this node to be neither master nor data node, but
+# to act as a "search load balancer" (fetching data from nodes,
+# aggregating results, etc.)
+#
+# node.master: false
+# node.data: false
+
+# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
+# Node Info API [http://localhost:9200/_nodes] or GUI tools
+# such as <http://www.elasticsearch.org/overview/marvel/>,
+# <http://github.com/karmi/elasticsearch-paramedic>,
+# <http://github.com/lukas-vlcek/bigdesk> and
+# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
+
+# A node can have generic attributes associated with it, which can later be used
+# for customized shard allocation filtering, or allocation awareness. An attribute
+# is a simple key value pair, similar to node.key: value, here is an example:
+#
+# node.rack: rack314
+
+# By default, multiple nodes are allowed to start from the same installation location
+# to disable it, set the following:
+# node.max_local_storage_nodes: 1
+
+
+#################################### Index ####################################
+
+# You can set a number of options (such as shard/replica options, mapping
+# or analyzer definitions, translog settings, ...) for indices globally,
+# in this file.
+#
+# Note, that it makes more sense to configure index settings specifically for
+# a certain index, either when creating it or by using the index templates API.
+#
+# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
+# for more information.
+
+# Set the number of shards (splits) of an index (5 by default):
+#
+# index.number_of_shards: 5
+
+# Set the number of replicas (additional copies) of an index (1 by default):
+#
+# index.number_of_replicas: 1
+
+# Note, that for development on a local machine, with small indices, it usually
+# makes sense to "disable" the distributed features:
+#
+index.number_of_shards: 1
+index.number_of_replicas: 0
+
+# These settings directly affect the performance of index and search operations
+# in your cluster. Assuming you have enough machines to hold shards and
+# replicas, the rule of thumb is:
+#
+# 1. Having more *shards* enhances the _indexing_ performance and allows to
+# _distribute_ a big index across machines.
+# 2. Having more *replicas* enhances the _search_ performance and improves the
+# cluster _availability_.
+#
+# The "number_of_shards" is a one-time setting for an index.
+#
+# The "number_of_replicas" can be increased or decreased anytime,
+# by using the Index Update Settings API.
+#
+# Elasticsearch takes care about load balancing, relocating, gathering the
+# results from nodes, etc. Experiment with different settings to fine-tune
+# your setup.
+
+# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
+# the index status.
+
+
+#################################### Paths ####################################
+
+# Path to directory containing configuration (this file and logging.yml):
+#
+path.conf: /src/test/resources
+#path.home: /src/test/resources
+
+# Path to directory where to store index data allocated for this node.
+#
+path.data: target/esdata
+#
+# Can optionally include more than one location, causing data to be striped across
+# the locations (a la RAID 0) on a file level, favouring locations with most free
+# space on creation. For example:
+#
+# path.data: /path/to/data1,/path/to/data2
+
+# Path to temporary files:
+#
+path.work: /target/eswork
+
+# Path to log files:
+#
+path.logs: /target/eslogs
+
+# Path to where plugins are installed:
+#
+# path.plugins: /path/to/plugins
+
+
+#################################### Plugin ###################################
+
+# If a plugin listed here is not installed for current node, the node will not start.
+#
+# plugin.mandatory: mapper-attachments,lang-groovy
+
+
+################################### Memory ####################################
+
+# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
+# it _never_ swaps.
+#
+# Set this property to true to lock the memory:
+#
+# bootstrap.mlockall: true
+
+# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
+# to the same value, and that the machine has enough memory to allocate
+# for Elasticsearch, leaving enough memory for the operating system itself.
+#
+# You should also make sure that the Elasticsearch process is allowed to lock
+# the memory, eg. by using `ulimit -l unlimited`.
+
+
+############################## Network And HTTP ###############################
+
+# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
+# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
+# communication. (the range means that if the port is busy, it will automatically
+# try the next port).
+
+# Set the bind address specifically (IPv4 or IPv6):
+#
+# network.bind_host: 192.168.0.1
+
+# Set the address other nodes will use to communicate with this node. If not
+# set, it is automatically derived. It must point to an actual IP address.
+#
+# network.publish_host: 192.168.0.1
+
+# Set both 'bind_host' and 'publish_host':
+#
+# network.host: 192.168.0.1
+
+# Set a custom port for the node to node communication (9300 by default):
+#
+# transport.tcp.port: 9300
+
+# Enable compression for all communication between nodes (disabled by default):
+#
+# transport.tcp.compress: true
+
+# Set a custom port to listen for HTTP traffic:
+#
+# http.port: 9200
+
+# Set a custom allowed content length:
+#
+# http.max_content_length: 100mb
+
+# Disable HTTP completely:
+#
+# http.enabled: false
+
+
+################################### Gateway ###################################
+
+# The gateway allows for persisting the cluster state between full cluster
+# restarts. Every change to the state (such as adding an index) will be stored
+# in the gateway, and when the cluster starts up for the first time,
+# it will read its state from the gateway.
+
+# There are several types of gateway implementations. For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
+
+# The default gateway type is the "local" gateway (recommended):
+#
+# gateway.type: local
+
+# Settings below control how and when to start the initial recovery process on
+# a full cluster restart (to reuse as much local data as possible when using shared
+# gateway).
+
+# Allow recovery process after N nodes in a cluster are up:
+#
+gateway.recover_after_nodes: 1
+
+# Set the timeout to initiate the recovery process, once the N nodes
+# from previous setting are up (accepts time value):
+#
+# gateway.recover_after_time: 5m
+
+# Set how many nodes are expected in this cluster. Once these N nodes
+# are up (and recover_after_nodes is met), begin recovery process immediately
+# (without waiting for recover_after_time to expire):
+#
+gateway.expected_nodes: 1
+
+
+############################# Recovery Throttling #############################
+
+# These settings allow to control the process of shards allocation between
+# nodes during initial recovery, replica allocation, rebalancing,
+# or when adding and removing nodes.
+
+# Set the number of concurrent recoveries happening on a node:
+#
+# 1. During the initial recovery
+#
+# cluster.routing.allocation.node_initial_primaries_recoveries: 4
+#
+# 2. During adding/removing nodes, rebalancing, etc
+#
+# cluster.routing.allocation.node_concurrent_recoveries: 2
+
+# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
+#
+# indices.recovery.max_bytes_per_sec: 20mb
+
+# Set to limit the number of open concurrent streams when
+# recovering a shard from a peer:
+#
+# indices.recovery.concurrent_streams: 5
+
+
+################################## Discovery ##################################
+
+# Discovery infrastructure ensures nodes can be found within a cluster
+# and master node is elected. Multicast discovery is the default.
+
+# Set to ensure a node sees N other master eligible nodes to be considered
+# operational within the cluster. Its recommended to set it to a higher value
+# than 1 when running more than 2 nodes in the cluster.
+#
+# discovery.zen.minimum_master_nodes: 1
+
+# Set the time to wait for ping responses from other nodes when discovering.
+# Set this option to a higher value on a slow or congested network
+# to minimize discovery failures:
+#
+# discovery.zen.ping.timeout: 3s
+
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
+
+# Unicast discovery allows to explicitly control which nodes will be used
+# to discover the cluster. It can be used when multicast is not present,
+# or to restrict the cluster communication-wise.
+#
+# 1. Disable multicast discovery (enabled by default):
+#
+# discovery.zen.ping.multicast.enabled: false
+#
+# 2. Configure an initial list of master nodes in the cluster
+# to perform discovery when new nodes (master or data) are started:
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
+
+# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
+#
+# You have to install the cloud-aws plugin for enabling the EC2 discovery.
+#
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
+#
+# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
+# for a step-by-step tutorial.
+
+# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
+#
+# You have to install the cloud-gce plugin for enabling the GCE discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
+
+# Azure discovery allows to use Azure API in order to perform discovery.
+#
+# You have to install the cloud-azure plugin for enabling the Azure discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
+
+################################## Slow Log ##################################
+
+# Shard level query and fetch threshold logging.
+
+#index.search.slowlog.threshold.query.warn: 10s
+#index.search.slowlog.threshold.query.info: 5s
+#index.search.slowlog.threshold.query.debug: 2s
+#index.search.slowlog.threshold.query.trace: 500ms
+
+#index.search.slowlog.threshold.fetch.warn: 1s
+#index.search.slowlog.threshold.fetch.info: 800ms
+#index.search.slowlog.threshold.fetch.debug: 500ms
+#index.search.slowlog.threshold.fetch.trace: 200ms
+
+#index.indexing.slowlog.threshold.index.warn: 10s
+#index.indexing.slowlog.threshold.index.info: 5s
+#index.indexing.slowlog.threshold.index.debug: 2s
+#index.indexing.slowlog.threshold.index.trace: 500ms
+
+################################## GC Logging ################################
+
+#monitor.jvm.gc.young.warn: 1000ms
+#monitor.jvm.gc.young.info: 700ms
+#monitor.jvm.gc.young.debug: 400ms
+
+#monitor.jvm.gc.old.warn: 10s
+#monitor.jvm.gc.old.info: 5s
+#monitor.jvm.gc.old.debug: 2s
+
diff --git a/catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.yml b/catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.yml
new file mode 100644
index 0000000000..d8d9f20d40
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.yml
@@ -0,0 +1,204 @@
+tosca.capabilities.Root:
+ description: The TOSCA root Capability Type all other TOSCA base Capability Types derive from
+tosca.capabilities.Attachment:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Node:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ cpu_frequency:
+ type: scalar-unit.frequency
+ required: false
+ constraints:
+ - greater_or_equal: 0.1 GHz
+ disk_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+ mem_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: PortDef
+ required: false
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ port_name:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ default: PRIVATE
+ initiator:
+ type: string
+ default: source
+ constraints:
+ - valid_values: [ source, target, peer ]
+ ports:
+ type: map
+ required: false
+ constraints:
+ - min_length: 1
+ entry_schema:
+ type: PortSpec
+ attributes:
+ ip_address:
+ type: string
+tosca.capabilities.DatabaseEndpoint:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.Endpoint.Public:
+ derived_from: tosca.capabilities.Endpoint
+ properties:
+ # Change the default network_name to use the first public network found
+ network_name: PUBLIC
+ floating:
+ description: >
+ indicates that the public address should be allocated from a pool of floating IPs that are associated with the network.
+ type: boolean
+ default: false
+ status: experimental
+ dns_name:
+ description: The optional name to register with DNS
+ type: string
+ required: false
+ status: experimental
+tosca.capabilities.Endpoint.Admin:
+ derived_from: tosca.capabilities.Endpoint
+ # Change Endpoint secure indicator to true from its default of false
+ properties:
+ secure: true
+tosca.capabilities.Endpoint.Database:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.OperatingSystem:
+ derived_from: tosca.capabilities.Root
+ properties:
+ architecture:
+ type: string
+ required: false
+ type:
+ type: string
+ required: false
+ distribution:
+ type: string
+ required: false
+ version:
+ type: version
+ required: false
+tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_instances:
+ type: integer
+ default: 1
+ max_instances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+tosca.capabilities.network.Bindable:
+ derived_from: tosca.capabilities.Node
+
+
+tosca.capabilities.Container.Docker:
+ derived_from: tosca.capabilities.Container
+ properties:
+ version:
+ type: list
+ required: false
+ entry_schema: version
+ publish_all:
+ type: boolean
+ default: false
+ required: false
+ publish_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ expose_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ volumes:
+ type: list
+ entry_schema: string
+ required: false
+tosca.capabilities.network.Linkable:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.nfv.Metric:
+ derived_from: tosca.capabilities.Endpoint
+org.openecomp.capabilities.Metric:
+ derived_from: tosca.capabilities.nfv.Metric
+ description: A node type that includes the Metric capability indicates that it can be monitored.
+ properties:
+ unit:
+ type: string
+ description: Unit of the metric value
+ required: true
+ status: SUPPORTED
+ description:
+ type: string
+ description: Description of the metric
+ required: false
+ status: SUPPORTED
+ type:
+ type: string
+ description: Type of the metric value, for an example, Cumulative, Delta, Gauge and etc.
+ required: true
+ status: SUPPORTED
+ category:
+ type: string
+ description: Category of the metric, for an example, compute, disk, network, storage and etc.
+ required: false
+ status: SUPPORTED
+ attributes:
+ value:
+ type: string
+ description: Runtime monitored value
+ status: SUPPORTED
+org.openecomp.capabilities.metric.Ceilometer:
+ derived_from: org.openecomp.capabilities.Metric
+ description: A node type that includes the Metric capability indicates that it can be monitored using ceilometer.
+ properties:
+ name:
+ type: string
+ description: Ceilometer metric type name to monitor. (The name ceilometer is using)
+ required: true
+ status: SUPPORTED
+org.openecomp.capabilities.metric.SnmpPolling:
+ derived_from: org.openecomp.capabilities.Metric
+ description: A node type that includes the Metric capability indicates that it can be monitored using snmp polling.
+ properties:
+ oid:
+ type: string
+ description: Object Id of the metric
+ required: true
+ status: SUPPORTED
+org.openecomp.capabilities.metric.SnmpTrap:
+ derived_from: org.openecomp.capabilities.Metric
+ description: A node type that includes the Metric capability indicates that it can be monitored using snmp trap.
+ properties:
+ oid:
+ type: string
+ description: Object Id of the metric
+ required: true
+ status: SUPPORTED
diff --git a/catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.zip b/catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.zip
new file mode 100644
index 0000000000..add3a8ce1f
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/capability-types/capabilityTypes.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/categories/categoryTypes.yml b/catalog-be/src/main/resources/import/tosca/categories/categoryTypes.yml
new file mode 100644
index 0000000000..9ddce45e4e
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/categories/categoryTypes.yml
@@ -0,0 +1,116 @@
+services:
+ Mobility:
+ name: "Mobility"
+ icons: ['mobility']
+ Network_L1_3:
+ name: "Network L1-3"
+ icons: ['network_l_1-3']
+ Network_L4:
+ name: "Network L4+"
+ icons: ['network_l_4']
+ VoIP_Call_Control:
+ name: "VoIP Call Control"
+ icons: ['call_controll']
+resources:
+ NetworkLayer23:
+ name: "Network L2-3"
+ subcategories:
+ Router:
+ name: "Router"
+ icons: ['router','vRouter']
+ Gateway:
+ name: "Gateway"
+ icons: ['gateway']
+ WAN_Connectors:
+ name: "WAN Connectors"
+ icons: ['network','connector','port']
+ LAN_Connectors:
+ name: "LAN Connectors"
+ icons: ['network','connector','port']
+ Infrastructure:
+ name: "Infrastructure"
+ icons: ['ucpe']
+ NetworkLayer4:
+ name: "Network L4+"
+ subcategories:
+ Common_Network_Resources:
+ name: "Common Network Resources"
+ icons: ['network']
+ ApplicationLayer4:
+ name: "Application L4+"
+ subcategories:
+ Border_Element:
+ name: "Border Element"
+ icons: ['borderElement']
+ Application_Server:
+ name: "Application Server"
+ icons: ['applicationServer']
+ Web_Server:
+ name: "Web Server"
+ icons: ['applicationServer']
+ Call_Control:
+ name: "Call Control"
+ icons: ['call_controll']
+ Media_Servers:
+ name: "Media Servers"
+ icons: ['applicationServer']
+ Load_Balancer:
+ name: "Load Balancer"
+ icons: ['loadBalancer']
+ Database:
+ name: "Database"
+ icons: ['database']
+ Firewall:
+ name: "Firewall"
+ icons: ['firewall']
+ Generic:
+ name: "Generic"
+ subcategories:
+ Infrastructure:
+ name: "Infrastructure"
+ icons: ['connector']
+ Abstract:
+ name: "Abstract"
+ icons: ['objectStorage', 'compute']
+ Network_Elements:
+ name: "Network Elements"
+ icons: ['network', 'connector']
+ Database:
+ name: "Database"
+ icons: ['database']
+ Rules:
+ name: "Rules"
+ icons: ['networkrules','securityrules']
+ NetworkConnectivity:
+ name: "Network Connectivity"
+ subcategories:
+ ConnectionPoints:
+ name: "Connection Points"
+ icons: ['cp']
+ VirtualLinks:
+ name: "Virtual Links"
+ icons: ['vl']
+ DcaeComponent:
+ name: "DCAE Component"
+ subcategories:
+ Source:
+ name: "Source"
+ icons: ['dcae_source']
+ Collector:
+ name: "Collector"
+ icons: ['dcae_collector']
+ Utility:
+ name: "Utility"
+ icons: ['dcae_utilty']
+ Microservice:
+ name: "Microservice"
+ icons: ['dcae_microservice']
+ Analytics:
+ name: "Analytics"
+ icons: ['dcae_analytics']
+ Database:
+ name: "Database"
+ icons: ['dcae_database']
+ Policy:
+ name: "Policy"
+ icons: ['dcae_policy'] \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/categories/categoryTypes.zip b/catalog-be/src/main/resources/import/tosca/categories/categoryTypes.zip
new file mode 100644
index 0000000000..c04a2faefc
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/categories/categoryTypes.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml b/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml
new file mode 100644
index 0000000000..1fdc3a2cd2
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml
@@ -0,0 +1,821 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+string:
+ derived_from: tosca.datatypes.Root
+
+boolean:
+ derived_from: tosca.datatypes.Root
+
+float:
+ derived_from: tosca.datatypes.Root
+
+json:
+ derived_from: tosca.datatypes.Root
+
+list:
+ derived_from: tosca.datatypes.Root
+
+map:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false
+ token_type:
+ type: string
+ default: password
+ token:
+ type: string
+ keys:
+ type: map
+ required: false
+ entry_schema:
+ type: string
+ user:
+ type: string
+ required: false
+
+tosca.datatypes.TimeInterval:
+ derived_from: tosca.datatypes.Root
+ properties:
+ start_time:
+ type: timestamp
+ required: true
+ end_time:
+ type: timestamp
+ required: true
+
+tosca.datatypes.network.NetworkInfo:
+ derived_from: tosca.datatypes.Root
+ properties:
+ network_name:
+ type: string
+ network_id:
+ type: string
+ addresses:
+ type: list
+ entry_schema:
+ type: string
+
+tosca.datatypes.network.PortInfo:
+ derived_from: tosca.datatypes.Root
+ properties:
+ port_name:
+ type: string
+ port_id:
+ type: string
+ network_id:
+ type: string
+ mac_address:
+ type: string
+ addresses:
+ type: list
+ entry_schema:
+ type: string
+
+tosca.datatypes.network.PortDef:
+ derived_from: integer
+ constraints:
+ - in_range: [ 1, 65535 ]
+
+tosca.datatypes.network.PortSpec:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: true
+ default: tcp
+ constraints:
+ - valid_values: [ udp, tcp, igmp ]
+ target:
+ type: tosca.datatypes.network.PortDef
+ target_range:
+ type: range
+ constraints:
+ - in_range: [ 1, 65535 ]
+ source:
+ type: tosca.datatypes.network.PortDef
+ source_range:
+ type: range
+ constraints:
+ - in_range: [ 1, 65535 ]
+
+
+
+org.openecomp.datatypes.heat.network.AddressPair:
+ derived_from: tosca.datatypes.Root
+ description: MAC/IP address pairs
+ properties:
+ mac_address:
+ type: string
+ description: MAC address
+ required: false
+ status: SUPPORTED
+ ip_address:
+ type: string
+ description: IP address
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.network.subnet.HostRoute:
+ derived_from: tosca.datatypes.Root
+ description: Host route info for the subnet
+ properties:
+ destination:
+ type: string
+ description: The destination for static route
+ required: false
+ status: SUPPORTED
+ nexthop:
+ type: string
+ description: The next hop for the destination
+ required: false
+ status: SUPPORTED
+
+org.openecomp.datatypes.heat.network.AllocationPool:
+ derived_from: tosca.datatypes.Root
+ description: The start and end addresses for the allocation pool
+ properties:
+ start:
+ type: string
+ description: Start address for the allocation pool
+ required: false
+ status: SUPPORTED
+ end:
+ type: string
+ description: End address for the allocation pool
+ required: false
+ status: SUPPORTED
+
+org.openecomp.datatypes.heat.network.neutron.Subnet:
+ derived_from: tosca.datatypes.Root
+ description: A subnet represents an IP address block that can be used for assigning IP addresses to virtual instances
+ properties:
+ tenant_id:
+ type: string
+ description: The ID of the tenant who owns the network
+ required: false
+ status: SUPPORTED
+ enable_dhcp:
+ type: boolean
+ description: Set to true if DHCP is enabled and false if DHCP is disabled
+ required: false
+ default: true
+ status: SUPPORTED
+ ipv6_address_mode:
+ type: string
+ description: IPv6 address mode
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - dhcpv6-stateful
+ - dhcpv6-stateless
+ - slaac
+ ipv6_ra_mode:
+ type: string
+ description: IPv6 RA (Router Advertisement) mode
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - dhcpv6-stateful
+ - dhcpv6-stateless
+ - slaac
+ value_specs:
+ type: map
+ description: Extra parameters to include in the request
+ required: false
+ default: {
+ }
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ allocation_pools:
+ type: list
+ description: The start and end addresses for the allocation pools
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.AllocationPool
+ subnetpool:
+ type: string
+ description: The name or ID of the subnet pool
+ required: false
+ status: SUPPORTED
+ dns_nameservers:
+ type: list
+ description: A specified set of DNS name servers to be used
+ required: false
+ default: [
+ ]
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ host_routes:
+ type: list
+ description: The gateway IP address
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.subnet.HostRoute
+ ip_version:
+ type: integer
+ description: The gateway IP address
+ required: false
+ default: 4
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - '4'
+ - '6'
+ name:
+ type: string
+ description: The name of the subnet
+ required: false
+ status: SUPPORTED
+ prefixlen:
+ type: integer
+ description: Prefix length for subnet allocation from subnet pool
+ required: false
+ status: SUPPORTED
+ constraints:
+ - greater_or_equal: 0
+ cidr:
+ type: string
+ description: The CIDR
+ required: false
+ status: SUPPORTED
+ gateway_ip:
+ type: string
+ description: The gateway IP address
+ required: false
+ status: SUPPORTED
+
+org.openecomp.datatypes.heat.novaServer.network.PortExtraProperties:
+ derived_from: tosca.datatypes.Root
+ description: Nova server network expand properties for port
+ properties:
+ port_security_enabled:
+ type: boolean
+ description: Flag to enable/disable port security on the port
+ required: false
+ status: SUPPORTED
+ mac_address:
+ type: string
+ description: MAC address to give to this port
+ required: false
+ status: SUPPORTED
+ admin_state_up:
+ type: boolean
+ description: The administrative state of this port
+ required: false
+ default: true
+ status: SUPPORTED
+ qos_policy:
+ type: string
+ description: The name or ID of QoS policy to attach to this port
+ required: false
+ status: SUPPORTED
+ allowed_address_pairs:
+ type: list
+ description: Additional MAC/IP address pairs allowed to pass through the port
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.AddressPair
+ binding:vnic_type:
+ type: string
+ description: The vnic type to be bound on the neutron port
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - macvtap
+ - direct
+ - normal
+ value_specs:
+ type: map
+ description: Extra parameters to include in the request
+ required: false
+ default: {
+ }
+ status: SUPPORTED
+ entry_schema:
+ type: string
+org.openecomp.datatypes.heat.novaServer.network.AddressInfo:
+ derived_from: tosca.datatypes.network.NetworkInfo
+ description: Network addresses with corresponding port id
+ properties:
+ port_id:
+ type: string
+ description: Port id
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.neutron.port.FixedIps:
+ derived_from: tosca.datatypes.Root
+ description: subnet/ip_address
+ properties:
+ subnet:
+ type: string
+ description: Subnet in which to allocate the IP address for this port
+ required: false
+ status: SUPPORTED
+ ip_address:
+ type: string
+ description: IP address desired in the subnet for this port
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.FileInfo:
+ derived_from: tosca.datatypes.Root
+ description: Heat File Info
+ properties:
+ file:
+ type: string
+ description: The required URI string (relative or absolute) which can be used to locate the file
+ required: true
+ status: SUPPORTED
+ file_type:
+ type: string
+ description: The type of the file
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - base
+ - env
+ - volume
+ - network
+org.openecomp.datatypes.heat.contrail.network.rule.PortPairs:
+ derived_from: tosca.datatypes.Root
+ description: source and destination port pairs
+ properties:
+ start_port:
+ type: string
+ description: Start port
+ required: false
+ status: SUPPORTED
+ end_port:
+ type: string
+ description: End port
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrail.network.rule.Rule:
+ derived_from: tosca.datatypes.Root
+ description: policy rule
+ properties:
+ src_ports:
+ type: list
+ description: Source ports
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrail.network.rule.PortPairs
+ protocol:
+ type: string
+ description: Protocol
+ required: false
+ status: SUPPORTED
+ dst_addresses:
+ type: list
+ description: Destination addresses
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork
+ apply_service:
+ type: string
+ description: Service to apply
+ required: false
+ status: SUPPORTED
+ dst_ports:
+ type: list
+ description: Destination ports
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrail.network.rule.PortPairs
+ src_addresses:
+ type: list
+ description: Source addresses
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork
+ direction:
+ type: string
+ description: Direction
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrail.network.rule.RuleList:
+ derived_from: tosca.datatypes.Root
+ description: list of policy rules
+ properties:
+ policy_rule:
+ type: list
+ description: Contrail network rule
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: corg.openecomp.datatypes.heat.contrail.network.rule.Rule
+org.openecomp.datatypes.heat.contrail.network.rule.VirtualNetwork:
+ derived_from: tosca.datatypes.Root
+ description: source and destination addresses
+ properties:
+ virtual_network:
+ type: string
+ description: Virtual network
+ required: false
+ status: SUPPORTED
+
+org.openecomp.datatypes.heat.network.neutron.SecurityRules.Rule:
+ derived_from: tosca.datatypes.Root
+ description: Rules Pairs
+ properties:
+ remote_group_id:
+ type: string
+ description: The remote group ID to be associated with this security group rule
+ required: false
+ status: SUPPORTED
+ protocol:
+ type: string
+ description: The protocol that is matched by the security group rule
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - tcp
+ - udp
+ - icmp
+ ethertype:
+ type: string
+ description: Ethertype of the traffic
+ required: false
+ default: IPv4
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - IPv4
+ - IPv6
+ port_range_max:
+ type: integer
+ description: 'The maximum port number in the range that is matched by the
+ security group rule. '
+ required: false
+ status: SUPPORTED
+ constraints:
+ - in_range:
+ - 0
+ - 65535
+ remote_ip_prefix:
+ type: string
+ description: The remote IP prefix (CIDR) to be associated with this security group rule
+ required: false
+ status: SUPPORTED
+ remote_mode:
+ type: string
+ description: Whether to specify a remote group or a remote IP prefix
+ required: false
+ default: remote_ip_prefix
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - remote_ip_prefix
+ - remote_group_id
+ direction:
+ type: string
+ description: The direction in which the security group rule is applied
+ required: false
+ default: ingress
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - egress
+ - ingress
+ port_range_min:
+ type: integer
+ description: The minimum port number in the range that is matched by the security group rule.
+ required: false
+ status: SUPPORTED
+ constraints:
+ - in_range:
+ - 0
+ - 65535
+org.openecomp.datatypes.heat.substitution.SubstitutionFiltering:
+ derived_from: tosca.datatypes.Root
+ description: Substitution Filter
+ properties:
+ substitute_service_template:
+ type: string
+ description: Substitute Service Template
+ required: true
+ status: SUPPORTED
+ index_value:
+ type: integer
+ description: Index value of the substitution service template runtime instance
+ required: false
+ default: 0
+ status: SUPPORTED
+ constraints:
+ - greater_or_equal: 0
+ count:
+ type: string
+ description: Count
+ required: false
+ default: 1
+ status: SUPPORTED
+ scaling_enabled:
+ type: boolean
+ description: Indicates whether service scaling is enabled
+ required: false
+ default: true
+ status: SUPPORTED
+ mandatory:
+ type: boolean
+ description: Mandatory
+ required: false
+ default: true
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefDataSequence:
+ derived_from: tosca.datatypes.Root
+ description: network policy refs data sequence
+ properties:
+ network_policy_refs_data_sequence_major:
+ type: integer
+ description: Network Policy ref data sequence Major
+ required: false
+ status: SUPPORTED
+ network_policy_refs_data_sequence_minor:
+ type: integer
+ description: Network Policy ref data sequence Minor
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefData:
+ derived_from: tosca.datatypes.Root
+ description: network policy refs data
+ properties:
+ network_policy_refs_data_sequence:
+ type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefDataSequence
+ description: Network Policy ref data sequence
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnet:
+ derived_from: tosca.datatypes.Root
+ description: Network Ipam Ref Data Subnet
+ properties:
+ network_ipam_refs_data_ipam_subnets_subnet_ip_prefix_len:
+ type: string
+ description: Network ipam refs data ipam subnets ip prefix len
+ required: false
+ status: SUPPORTED
+ network_ipam_refs_data_ipam_subnets_subnet_ip_prefix:
+ type: string
+ description: Network ipam refs data ipam subnets ip prefix
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnetList:
+ derived_from: tosca.datatypes.Root
+ description: Network Ipam Ref Data Subnet List
+ properties:
+ network_ipam_refs_data_ipam_subnets_subnet:
+ type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnet
+ description: Network ipam refs data ipam subnets
+ required: false
+ status: SUPPORTED
+ network_ipam_refs_data_ipam_subnets_addr_from_start:
+ type: string
+ description: Network ipam refs data ipam subnets addr from start
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.IpamRefData:
+ derived_from: tosca.datatypes.Root
+ description: Network Ipam Ref Data
+ properties:
+ network_ipam_refs_data_ipam_subnets:
+ type: list
+ description: Network ipam refs data ipam subnets
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.ref.data.IpamSubnetList
+org.openecomp.datatypes.heat.contrailV2.network.rule.SrcVirtualNetwork:
+ derived_from: tosca.datatypes.Root
+ description: source addresses
+ properties:
+ network_policy_entries_policy_rule_src_addresses_virtual_network:
+ type: string
+ description: Source addresses Virtual network
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.network.rule.DstVirtualNetwork:
+ derived_from: tosca.datatypes.Root
+ description: destination addresses
+ properties:
+ network_policy_entries_policy_rule_dst_addresses_virtual_network:
+ type: string
+ description: Destination addresses Virtual network
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.network.rule.DstPortPairs:
+ derived_from: tosca.datatypes.Root
+ description: destination port pairs
+ properties:
+ network_policy_entries_policy_rule_dst_ports_start_port:
+ type: string
+ description: Start port
+ required: false
+ status: SUPPORTED
+ network_policy_entries_policy_rule_dst_ports_end_port:
+ type: string
+ description: End port
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.network.rule.SrcPortPairs:
+ derived_from: tosca.datatypes.Root
+ description: source port pairs
+ properties:
+ network_policy_entries_policy_rule_src_ports_start_port:
+ type: string
+ description: Start port
+ required: false
+ status: SUPPORTED
+ network_policy_entries_policy_rule_src_ports_end_port:
+ type: string
+ description: End port
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList:
+ derived_from: tosca.datatypes.Root
+ description: Action List
+ properties:
+ network_policy_entries_policy_rule_action_list_simple_action:
+ type: string
+ description: Simple Action
+ required: false
+ status: SUPPORTED
+ network_policy_entries_policy_rule_action_list_apply_service:
+ type: list
+ description: Apply Service
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList:
+ derived_from: tosca.datatypes.Root
+ description: Action List
+ properties:
+ network_policy_entries_policy_rule_action_list_simple_action:
+ type: string
+ description: Simple Action
+ required: false
+ status: SUPPORTED
+ network_policy_entries_policy_rule_action_list_apply_service:
+ type: list
+ description: Apply Service
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+org.openecomp.datatypes.heat.contrailV2.network.rule.Rule:
+ derived_from: tosca.datatypes.Root
+ description: policy rule
+ properties:
+ network_policy_entries_policy_rule_dst_addresses:
+ type: list
+ description: Destination addresses
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.DstVirtualNetwork
+ network_policy_entries_policy_rule_dst_ports:
+ type: list
+ description: Destination ports
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.DstPortPairs
+ network_policy_entries_policy_rule_protocol:
+ type: string
+ description: Protocol
+ required: false
+ status: SUPPORTED
+ network_policy_entries_policy_rule_src_addresses:
+ type: list
+ description: Source addresses
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.SrcVirtualNetwork
+ network_policy_entries_policy_rule_direction:
+ type: string
+ description: Direction
+ required: false
+ status: SUPPORTED
+ network_policy_entries_policy_rule_src_ports:
+ type: list
+ description: Source ports
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.SrcPortPairs
+ network_policy_entries_policy_rule_action_list:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.ActionList
+ description: Action list
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.network.rule.RuleList:
+ derived_from: tosca.datatypes.Root
+ description: list of policy rules
+ properties:
+ network_policy_entries_policy_rule:
+ type: list
+ description: Contrail network rule
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.Rule
+org.openecomp.datatypes.heat.network.contrail.port.StaticRoute:
+ derived_from: tosca.datatypes.Root
+ description: static route
+ properties:
+ prefix:
+ type: string
+ description: Route prefix
+ required: false
+ status: SUPPORTED
+ next_hop:
+ type: string
+ description: Next hop
+ required: false
+ status: SUPPORTED
+ next_hop_type:
+ type: string
+ description: Next hop type
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.network.contrail.AddressPair:
+ derived_from: tosca.datatypes.Root
+ description: Address Pair
+ properties:
+ address_mode:
+ type: string
+ description: Address mode active-active or active-standy
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - active-active
+ - active-standby
+ prefix:
+ type: string
+ description: IP address prefix
+ required: false
+ status: SUPPORTED
+ mac_address:
+ type: string
+ description: Mac address
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.network.contrail.InterfaceData:
+ derived_from: tosca.datatypes.Root
+ description: Interface Data
+ properties:
+ static_routes:
+ type: list
+ description: An ordered list of static routes to be added to this interface
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.contrail.port.StaticRoute
+ virtual_network:
+ type: string
+ description: Virtual Network for this interface
+ required: true
+ status: SUPPORTED
+ allowed_address_pairs:
+ type: list
+ description: List of allowed address pair for this interface
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.contrail.AddressPair
+ ip_address:
+ type: string
+ description: IP for this interface
+ required: false
+ status: SUPPORTED
+org.openecomp.datatypes.heat.contrailV2.virtual.machine.interface.Properties:
+ derived_from: tosca.datatypes.Root
+ description: Virtual Machine Interface Properties.
+ properties:
+ virtual_machine_interface_properties_service_interface_type:
+ type: string
+ description: Service Interface Type.
+ required: false
+ status: SUPPORTED \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.zip b/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.zip
new file mode 100644
index 0000000000..2249a8bd5c
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/group-types/groupTypes.yml b/catalog-be/src/main/resources/import/tosca/group-types/groupTypes.yml
new file mode 100644
index 0000000000..1df2cbdd04
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/group-types/groupTypes.yml
@@ -0,0 +1,30 @@
+org.openecomp.groups.heat.HeatStack:
+ derived_from: tosca.groups.Root
+ description: Grouped all heat resources which are in the same heat stack
+ properties:
+ heat_file:
+ type: string
+ description: Heat file which associate to this group/heat stack
+ required: true
+ status: SUPPORTED
+ description:
+ type: string
+ description: group description
+ required: true
+ status: SUPPORTED
+org.openecomp.groups.VfModule:
+ derived_from: tosca.groups.Root
+ description: Grouped all heat resources which are in the same VF Module
+ properties:
+ isBase:
+ type: boolean
+ description: Whether this module should be deployed before other modules
+ required: true
+ default: false
+ status: SUPPORTED
+tosca.groups.Root:
+ description: The TOSCA Group Type all other TOSCA Group Types derive from
+ interfaces:
+ Standard:
+ type: tosca.interfaces.node.lifecycle.Standard
+
diff --git a/catalog-be/src/main/resources/import/tosca/group-types/groupTypes.zip b/catalog-be/src/main/resources/import/tosca/group-types/groupTypes.zip
new file mode 100644
index 0000000000..e386507c92
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/group-types/groupTypes.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.json b/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.json
new file mode 100644
index 0000000000..40f96f466e
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "abstractSubstitute.yml",
+ "contactId": "jh0003",
+ "name": "AbstractSubstitute",
+ "description": "Abstract node to be derived by nested elements.",
+ "resourceIconPath": "defaulticon",
+ "resourceType": "VFC",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "AbstractSubstitute"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.yml b/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.yml
new file mode 100644
index 0000000000..48db2b609a
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: AbstractSubstituteGlobalTypes
+ template_version: 1.0.0
+description: Abstract Substitute Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.abstract.nodes.AbstractSubstitute:
+ derived_from: tosca.nodes.Root
+ properties:
+ service_template_filter:
+ type: org.openecomp.datatypes.heat.substitution.SubstitutionFiltering
+ description: Substitution Filter
+ required: true
+ status: SUPPORTED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.zip b/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.zip
new file mode 100644
index 0000000000..07e4941e6b
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/abstractSubstitute/abstractSubstitute.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.json b/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.json
new file mode 100644
index 0000000000..249ee0f959
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "cinderVolume.yml",
+ "contactId": "jh0003",
+ "name": "CinderVolume",
+ "description": "Represents a server-local block storage device that provides persistent storage to guest virtual machines. ",
+ "resourceIconPath": "objectStorage",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "CinderVolume"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.yml b/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.yml
new file mode 100644
index 0000000000..3bb227d1ec
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.yml
@@ -0,0 +1,177 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: CinderVolumeGlobalTypes
+ template_version: 1.0.0
+description: Cinder Volume TOSCA Global Types
+relationship_types:
+ org.openecomp.relationships.cinder.VolumeAttachesTo:
+ derived_from: tosca.relationships.AttachesTo
+ description: This type represents an attachment relationship for associating volume
+ properties:
+ volume_id:
+ type: string
+ description: The ID of the volume to be attached
+ required: true
+ status: SUPPORTED
+ location:
+ type: string
+ description: The location where the volume is exposed on the instance, mountpoint
+ required: false
+ status: SUPPORTED
+ instance_uuid:
+ type: string
+ description: The ID of the server to which the volume attaches
+ required: true
+ status: SUPPORTED
+ attributes:
+ show:
+ type: string
+ description: Detailed information about resource
+ status: SUPPORTED
+node_types:
+ org.openecomp.resource.vfc.nodes.heat.cinder.Volume:
+ derived_from: tosca.nodes.BlockStorage
+ properties:
+ availability_zone:
+ type: string
+ description: The availability zone in which the volume will be created
+ required: false
+ status: SUPPORTED
+ image:
+ type: string
+ description: If specified, the name or ID of the image to create the volume from
+ required: false
+ status: SUPPORTED
+ metadata:
+ type: map
+ description: Key/value pairs to associate with the volume
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ volume_type:
+ type: string
+ description: If specified, the type of volume to use, mapping to a specific backend
+ required: false
+ status: SUPPORTED
+ description:
+ type: string
+ description: A description of the volume
+ required: false
+ status: SUPPORTED
+ device_type:
+ type: string
+ description: Device type
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - cdrom
+ - disk
+ disk_bus:
+ type: string
+ description: 'Bus of the device: hypervisor driver chooses a suitable default if omitted'
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - ide
+ - lame_bus
+ - scsi
+ - usb
+ - virtio
+ backup_id:
+ type: string
+ description: If specified, the backup to create the volume from
+ required: false
+ status: SUPPORTED
+ source_volid:
+ type: string
+ description: If specified, the volume to use as source
+ required: false
+ status: SUPPORTED
+ boot_index:
+ type: integer
+ description: Integer used for ordering the boot disks
+ required: false
+ status: SUPPORTED
+ size:
+ type: scalar-unit.size
+ description: The requested storage size (default unit is MB)
+ required: false
+ status: SUPPORTED
+ constraints:
+ - greater_or_equal: 1 GB
+ read_only:
+ type: boolean
+ description: Enables or disables read-only access mode of volume
+ required: false
+ status: SUPPORTED
+ name:
+ type: string
+ description: A name used to distinguish the volume
+ required: false
+ status: SUPPORTED
+ scheduler_hints:
+ type: map
+ description: Arbitrary key-value pairs specified by the client to help the Cinder scheduler creating a volume
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ swap_size:
+ type: scalar-unit.size
+ description: The size of the swap, in MB
+ required: false
+ status: SUPPORTED
+ delete_on_termination:
+ type: boolean
+ description: Indicate whether the volume should be deleted when the server is terminated
+ required: false
+ status: SUPPORTED
+ multiattach:
+ type: boolean
+ description: Whether allow the volume to be attached more than once
+ required: false
+ status: SUPPORTED
+ attributes:
+ display_description:
+ type: string
+ description: Description of the volume
+ status: SUPPORTED
+ attachments:
+ type: string
+ description: The list of attachments of the volume
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ encrypted:
+ type: boolean
+ description: Boolean indicating if the volume is encrypted or not
+ status: SUPPORTED
+ show:
+ type: string
+ description: Detailed information about resource
+ status: SUPPORTED
+ created_at:
+ type: timestamp
+ description: The timestamp indicating volume creation
+ status: SUPPORTED
+ display_name:
+ type: string
+ description: Name of the volume
+ status: SUPPORTED
+ metadata_values:
+ type: map
+ description: Key/value pairs associated with the volume in raw dict form
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ bootable:
+ type: boolean
+ description: Boolean indicating if the volume can be booted or not
+ status: SUPPORTED
+ status:
+ type: string
+ description: The current status of the volume
+ status: SUPPORTED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.zip b/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.zip
new file mode 100644
index 0000000000..88506743ac
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/cinderVolume/cinderVolume.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.json
new file mode 100644
index 0000000000..5742445784
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.json
@@ -0,0 +1,17 @@
+{
+ "payloadName": "contrailAbstractSubstitute.yml",
+ "contactId": "jh0003",
+ "name": "ContrailAbstractSubstitute",
+ "description": "Abstract node to be derived by contrail nested elements when using Contrail Service Instance",
+ "resourceIconPath": "defaulticon",
+ "resourceType": "VFC",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }],
+ "tags": ["ContrailAbstractSubstitute"]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.yml
new file mode 100644
index 0000000000..5bdee932a3
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.yml
@@ -0,0 +1,137 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailAbstractSubstituteGlobalTypes
+ template_version: 1.0.0
+description: Contrail Abstract Substitute Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.abstract.nodes.contrail.AbstractSubstitute:
+ derived_from: org.openecomp.resource.abstract.nodes.AbstractSubstitute
+ properties:
+ availability_zone:
+ type: string
+ description: Availability zone to create servers in
+ required: false
+ status: SUPPORTED
+ static_routes_list:
+ type: list
+ description: Static routes enabled
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: boolean
+ availability_zone_enable:
+ type: boolean
+ description: Indicates availability zone is enabled
+ required: false
+ default: false
+ status: SUPPORTED
+ service_template_name:
+ type: string
+ description: Service template name
+ required: false
+ status: SUPPORTED
+ ordered_interfaces:
+ type: boolean
+ description: Indicates if service interface are ordered
+ required: false
+ default: false
+ status: SUPPORTED
+ flavor:
+ type: string
+ description: flavor
+ required: false
+ status: SUPPORTED
+ image_name:
+ type: string
+ description: Image name
+ required: true
+ status: SUPPORTED
+ service_type:
+ type: string
+ description: Service type
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - firewall
+ - analyzer
+ - source-nat
+ - loadbalancer
+ service_interface_type_list:
+ type: list
+ description: List of interface types
+ required: true
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ constraints:
+ - valid_values:
+ - management
+ - left
+ - right
+ - other
+ service_instance_name:
+ type: string
+ description: Service instance name
+ required: true
+ status: SUPPORTED
+ interface_list:
+ type: list
+ description: List of interfaces
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.contrail.InterfaceData
+ service_mode:
+ type: string
+ description: Service mode
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - transparent
+ - in-network
+ - in-network-nat
+ shared_ip_list:
+ type: list
+ description: Shared ips enabled
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: boolean
+ attributes:
+ tenant_id:
+ type: string
+ description: Tenant id of the Service Instance
+ status: SUPPORTED
+ fq_name:
+ type: string
+ description: The FQ name of the service instance
+ status: SUPPORTED
+ service_template_name:
+ type: string
+ description: Service Template of the Service Instance
+ status: SUPPORTED
+ show:
+ type: string
+ description: All attributes
+ status: SUPPORTED
+ active_vms:
+ type: string
+ description: Number of service VMs active for this Service Instance
+ status: SUPPORTED
+ service_instance_name:
+ type: string
+ description: The name of the service instance
+ status: SUPPORTED
+ virtual_machines:
+ type: string
+ description: Service VMs for the Service Instance
+ status: SUPPORTED
+ status:
+ type: string
+ description: Status of the service instance
+ status: SUPPORTED \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.zip
new file mode 100644
index 0000000000..6a2094bb9a
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailAbstractSubstitute/contrailAbstractSubstitute.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.json
new file mode 100644
index 0000000000..144136acd1
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.json
@@ -0,0 +1,15 @@
+{
+ "payloadName": "contrailCompute.yml",
+ "contactId": "jh0003",
+ "name": "ContrailCompute",
+ "description": "Represents a virtual machine for contrail service template. The information provided will be used to create a VM that matches characteristics.",
+ "resourceIconPath": "compute",
+ "resourceType": "VFC",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Infrastructure"
+ }]
+ }],
+ "tags": ["ContrailCompute"]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.yml
new file mode 100644
index 0000000000..d6ad4f953c
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.yml
@@ -0,0 +1,89 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailComputeGlobalTypes
+ template_version: 1.0.0
+description: Contrail Compute TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vfc.nodes.heat.contrail.Compute:
+ derived_from: tosca.nodes.Compute
+ properties:
+ flavor:
+ type: string
+ description: flavor
+ required: false
+ status: SUPPORTED
+ image_name:
+ type: string
+ description: Image name
+ required: true
+ status: SUPPORTED
+ availability_zone:
+ type: string
+ description: Availability zone to create servers in
+ required: false
+ status: SUPPORTED
+ service_type:
+ type: string
+ description: Service type
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - firewall
+ - analyzer
+ - source-nat
+ - loadbalancer
+ availability_zone_enable:
+ type: boolean
+ description: Indicates availability zone is enabled
+ required: false
+ default: false
+ status: SUPPORTED
+ service_template_name:
+ type: string
+ description: Service template name
+ required: false
+ status: SUPPORTED
+ service_instance_name:
+ type: string
+ description: Service instance name
+ required: true
+ status: SUPPORTED
+ service_mode:
+ type: string
+ description: Service mode
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - transparent
+ - in-network
+ - in-network-nat
+ attributes:
+ tenant_id:
+ type: string
+ description: Tenant id of the VM
+ status: SUPPORTED
+ fq_name:
+ type: string
+ description: fq_name
+ status: SUPPORTED
+ show:
+ type: string
+ description: All attributes
+ status: SUPPORTED
+ active_vms:
+ type: string
+ description: Number of active VMs
+ status: SUPPORTED
+ virtual_machines:
+ type: string
+ description: VMs of this compute
+ status: SUPPORTED
+ status:
+ type: string
+ description: status of the compute
+ status: SUPPORTED \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.zip
new file mode 100644
index 0000000000..c9e3c8a062
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailCompute/contrailCompute.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.json
new file mode 100644
index 0000000000..e1987b9449
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "contrailNetworkRules.yml",
+ "contactId": "jh0003",
+ "name": "ContrailNetworkRules",
+ "description": "Configuration of policy rules to be applied over the network.",
+ "resourceIconPath": "networkrules",
+ "resourceType": "VFC",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Rules"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ContrailNetworkRules"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.yml
new file mode 100644
index 0000000000..b9d61e7769
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.yml
@@ -0,0 +1,49 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailNetworkRuleGlobalType
+ template_version: 1.0.0
+description: Contrail Network Rule Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vfc.rules.nodes.heat.network.contrail.NetworkRules:
+ derived_from: tosca.nodes.Root
+ properties:
+ entries:
+ type: org.openecomp.datatypes.heat.contrail.network.rule.RuleList
+ description: A symbolic name for this contrail network rule
+ required: false
+ status: SUPPORTED
+ name:
+ type: string
+ description: A symbolic name for this contrail network rule
+ required: false
+ status: SUPPORTED
+ attributes:
+ tenant_id:
+ type: string
+ description: tenant_id
+ status: SUPPORTED
+ fq_name:
+ type: string
+ description: fq_name
+ status: SUPPORTED
+ show:
+ type: string
+ description: All attributes.
+ status: SUPPORTED
+ rules:
+ type: list
+ description: List of rules
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ requirements:
+ - network:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.network.Network
+ relationship: org.openecomp.relationships.AttachesTo
+ occurrences:
+ - 0
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.zip
new file mode 100644
index 0000000000..b687616cd8
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailNetworkRules/contrailNetworkRules.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.json
new file mode 100644
index 0000000000..6edc7083b0
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "contrailPort.yml",
+ "contactId": "jh0003",
+ "name": "ContrailPort",
+ "description": "Represents a logical entity that associates between Compute and Network normative types for contrail.",
+ "resourceIconPath": "port",
+ "resourceType": "CP",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ContrailPort"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.yml
new file mode 100644
index 0000000000..a3e43a2fd1
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.yml
@@ -0,0 +1,64 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailPortGlobalTypes
+ template_version: 1.0.0
+description: Contrail Port TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.cp.nodes.heat.network.contrail.Port:
+ derived_from: tosca.nodes.network.Port
+ properties:
+ static_routes:
+ type: list
+ description: An ordered list of static routes to be added to this interface
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.contrail.port.StaticRoute
+ virtual_network:
+ type: string
+ description: Virtual Network for this interface
+ required: true
+ status: SUPPORTED
+ static_route:
+ type: boolean
+ description: Static route enabled
+ required: false
+ default: false
+ status: SUPPORTED
+ allowed_address_pairs:
+ type: list
+ description: List of allowed address pair for this interface
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.contrail.AddressPair
+ shared_ip:
+ type: boolean
+ description: Shared ip enabled
+ required: false
+ default: false
+ status: SUPPORTED
+ ip_address:
+ type: string
+ description: IP for this interface
+ required: false
+ status: SUPPORTED
+ interface_type:
+ type: string
+ description: Interface type
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - management
+ - left
+ - right
+ - other
+ attributes:
+ fq_name:
+ type: string
+ description: fq_name
+ status: SUPPORTED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.zip
new file mode 100644
index 0000000000..f9e5c415ae
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailPort/contrailPort.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.json
new file mode 100644
index 0000000000..13717b7da3
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "contrailV2NetworkRules.yml",
+ "contactId": "jh0003",
+ "name": "ContrailV2NetworkRules",
+ "description": "Configuration of policy rules to be applied over the network for contrail V2.",
+ "resourceIconPath": "networkrules",
+ "resourceType": "VFC",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Rules"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ContrailV2NetworkRules"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.yml
new file mode 100644
index 0000000000..ccc6d3a2ab
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailV2NetworkRuleGlobalType
+ template_version: 1.0.0
+description: Contrail V2 Network Rule Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vfc.rules.nodes.heat.network.contrailV2.NetworkRules:
+ derived_from: tosca.nodes.Root
+ properties:
+ name:
+ type: string
+ description: A symbolic name for this contrail v2 network rule
+ required: false
+ status: SUPPORTED
+ network_policy_entries:
+ type: org.openecomp.datatypes.heat.contrailV2.network.rule.RuleList
+ description: A symbolic name for this contrail v2 network rule
+ required: false
+ status: SUPPORTED
+ attributes:
+ fq_name:
+ type: string
+ description: fq_name
+ status: SUPPORTED
+ requirements:
+ - network:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.network.Network
+ relationship: org.openecomp.relationships.AttachesTo
+ occurrences:
+ - 0
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.zip
new file mode 100644
index 0000000000..eac99b3b90
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2NetworkRules/contrailV2NetworkRules.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.json
new file mode 100644
index 0000000000..5c7a725190
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.json
@@ -0,0 +1,15 @@
+{
+ "payloadName": "contrailV2VirtualMachineInterface.yml",
+ "contactId": "jh0003",
+ "name": "ContrailV2VirtualMachineInterface",
+ "description": "Represents a network interface. The interfaces are defined with specific MAC addresses and ports.",
+ "resourceIconPath": "network",
+ "resourceType": "VFC",
+ "categories": [{
+ "name": "Generic",
+ "subcategories": [{
+ "name": "Network Elements"
+ }]
+ }],
+ "tags": ["ContrailV2VirtualMachineInterface"]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.yml
new file mode 100644
index 0000000000..ba324cf51a
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.yml
@@ -0,0 +1,59 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailV2VirtualMachineInterfaceGlobalType
+ template_version: 1.0.0
+description: Contrail Virtual Machine Interface TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.cp.nodes.heat.contrailV2.VirtualMachineInterface:
+ derived_from: tosca.nodes.network.Port
+ properties:
+ virtual_machine_intefrace_mac_addresses:
+ type: list
+ description: List of mac addresses.
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ name:
+ type: string
+ description: Virtual Machine Interface name
+ required: false
+ status: SUPPORTED
+ security_group_refs:
+ type: list
+ description: List of security groups.
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ virtual_network_refs:
+ type: list
+ description: List of virtual networks.
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ virtual_machine_interface_properties:
+ type: org.openecomp.datatypes.heat.contrailV2.virtual.machine.interface.Properties
+ description: virtual machine interface properties.
+ required: false
+ status: SUPPORTED
+ port_tuple_refs:
+ type: list
+ description: List of port tuples.
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ attributes:
+ fq_name:
+ type: string
+ description: The FQ name of the Virtual Network.
+ status: SUPPORTED
+ show:
+ type: string
+ description: All attributes.
+ status: SUPPORTED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.zip
new file mode 100644
index 0000000000..4b48a6838d
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualMachineInterface/contrailV2VirtualMachineInterface.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.json
new file mode 100644
index 0000000000..c7802d2529
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "contrailV2VirtualNetwork.yml",
+ "contactId": "jh0003",
+ "name": "ContrailV2VirtualNetwork",
+ "description": "Represents a network service with optional subnets and advanced configurations for contrail V2.",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ContrailV2VirtualNetwork"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.yml
new file mode 100644
index 0000000000..1ff1402411
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.yml
@@ -0,0 +1,76 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailV2VirtualNetworkGlobalType
+ template_version: 1.0.0
+description: Contrail V2 Virtual Network Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vl.nodes.heat.network.contrailV2.VirtualNetwork:
+ derived_from: tosca.nodes.network.Network
+ properties:
+ network_ipam_refs_data:
+ type: list
+ description: IPAM references Data
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.IpamRefData
+ network_policy_refs_data:
+ type: list
+ description: Policy references data
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.contrailV2.virtual.network.rule.RefData
+ network_ipam_refs:
+ type: list
+ description: IPAM references
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ network_policy_refs:
+ type: list
+ description: Policy references
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets:
+ type: map
+ description: Network related subnets
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.Subnet
+ attributes:
+ fq_name:
+ type: string
+ description: fq_name
+ status: SUPPORTED
+ subnets_name:
+ type: list
+ description: Subnets name of this network
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets_show:
+ type: map
+ description: Detailed information about each subnet
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets:
+ type: map
+ description: Network related subnets
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.Subnet
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
+ occurrences:
+ - 1
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.zip
new file mode 100644
index 0000000000..637e78da25
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailV2VirtualNetwork/contrailV2VirtualNetwork.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.json b/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.json
new file mode 100644
index 0000000000..523315edcb
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "contrailVirtualNetwork.yml",
+ "contactId": "jh0003",
+ "name": "ContrailVirtualNetwork",
+ "description": "Represents a network service with optional subnets and advanced configurations.",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ContrailVirtualNetwork"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.yml b/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.yml
new file mode 100644
index 0000000000..a5424c3c83
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.yml
@@ -0,0 +1,84 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: ContrailVirtualNetworkGlobalType
+ template_version: 1.0.0
+description: Contrail Virtual Network Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vl.nodes.heat.network.contrail.VirtualNetwork:
+ derived_from: tosca.nodes.network.Network
+ properties:
+ shared:
+ type: string
+ description: Is virtual network shared
+ required: false
+ status: SUPPORTED
+ forwarding_mode:
+ type: string
+ description: forwarding mode of the virtual network
+ required: false
+ status: SUPPORTED
+ external:
+ type: string
+ description: Is virtual network external
+ required: false
+ status: SUPPORTED
+ allow_transit:
+ type: string
+ description: Whether this network should be transitive.
+ required: false
+ status: SUPPORTED
+ flood_unknown_unicast:
+ type: string
+ description: flood L2 packets on network
+ required: false
+ status: SUPPORTED
+ route_targets:
+ type: list
+ description: route targets associated with the virtual network
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets:
+ type: map
+ description: Network related subnets
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.Subnet
+ attributes:
+ fq_name:
+ type: string
+ description: fq_name
+ status: SUPPORTED
+ show:
+ type: string
+ description: All attributes.
+ status: SUPPORTED
+ subnets_name:
+ type: list
+ description: Subnets name of this network
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets_show:
+ type: map
+ description: Detailed information about each subnet
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets:
+ type: map
+ description: Network related subnets
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.Subnet
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
+ occurrences:
+ - 1
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.zip b/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.zip
new file mode 100644
index 0000000000..17b32db41a
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/contrailVirtualNetwork/contrailVirtualNetwork.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.json b/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.json
new file mode 100644
index 0000000000..f6f0ecb308
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "eline.yml",
+ "contactId": "jh0003",
+ "name": "VL ELINE",
+ "description": "The node represents an E-Line virtual link entity.",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Network Connectivity",
+ "subcategories": [
+ {
+ "name": "Virtual Links"
+ }
+ ]
+ }
+],
+ "tags": [
+ "VL ELINE"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.yml b/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.yml
new file mode 100644
index 0000000000..21241369c7
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vl.ELine:
+ derived_from: org.openecomp.resource.vl.VL
+ capabilities:
+ virtual_linkable:
+ type: tosca.capabilities.network.Linkable
+ occurrences: [0,2]
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.zip b/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.zip
new file mode 100644
index 0000000000..132c5eebc5
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/eline/eline.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.json b/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.json
new file mode 100644
index 0000000000..073e935871
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "neutronNet.yml",
+ "contactId": "jh0003",
+ "name": "NeutronNet",
+ "description": "Represents a network service with optional subnets and advanced configurations.",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "NeutronNet"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.yml b/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.yml
new file mode 100644
index 0000000000..e80e2727c7
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.yml
@@ -0,0 +1,97 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: NeutronNetGlobalTypes
+ template_version: 1.0.0
+description: Neutron Network TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vl.nodes.heat.network.neutron.Net:
+ derived_from: tosca.nodes.network.Network
+ properties:
+ dhcp_agent_ids:
+ type: list
+ description: The IDs of the DHCP agent to schedule the network
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ tenant_id:
+ type: string
+ description: The ID of the tenant which will own the network
+ required: false
+ status: SUPPORTED
+ port_security_enabled:
+ type: boolean
+ description: Flag to enable/disable port security on the network
+ required: false
+ status: SUPPORTED
+ shared:
+ type: boolean
+ description: Whether this network should be shared across all tenants
+ required: false
+ default: false
+ status: SUPPORTED
+ admin_state_up:
+ type: boolean
+ description: A boolean value specifying the administrative status of the network
+ required: false
+ default: true
+ status: SUPPORTED
+ qos_policy:
+ type: string
+ description: The name or ID of QoS policy to attach to this network
+ required: false
+ status: SUPPORTED
+ subnets:
+ type: map
+ description: Network related subnets
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.Subnet
+ value_specs:
+ type: map
+ description: Extra parameters to include in the request
+ required: false
+ default: {
+ }
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ attributes:
+ qos_policy_id:
+ type: string
+ description: The QoS policy ID attached to this network
+ status: SUPPORTED
+ show:
+ type: string
+ description: Detailed information about resource
+ status: SUPPORTED
+ subnets_name:
+ type: list
+ description: Subnets name of this network
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ subnets:
+ type: map
+ description: Network related subnets
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.Subnet
+ mtu:
+ type: scalar-unit.size
+ description: The maximum transmission unit size(in bytes) for the network
+ status: SUPPORTED
+ status:
+ type: string
+ description: The status of the network
+ status: SUPPORTED
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
+ occurrences:
+ - 1
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.zip b/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.zip
new file mode 100644
index 0000000000..0cefe8a184
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/neutronNet/neutronNet.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.json b/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.json
new file mode 100644
index 0000000000..db782fa536
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "neutronPort.yml",
+ "contactId": "jh0003",
+ "name": "NeutronPort",
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "resourceIconPath": "port",
+ "resourceType": "CP",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "NeutronPort"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.yml b/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.yml
new file mode 100644
index 0000000000..898615c6ce
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.yml
@@ -0,0 +1,136 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: NeutronPortGlobalTypes
+ template_version: 1.0.0
+description: Neutron Port TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.cp.nodes.heat.network.neutron.Port:
+ derived_from: tosca.nodes.network.Port
+ properties:
+ port_security_enabled:
+ type: boolean
+ description: Flag to enable/disable port security on the network
+ required: false
+ status: SUPPORTED
+ device_id:
+ type: string
+ description: Device ID of this port
+ required: false
+ status: SUPPORTED
+ qos_policy:
+ type: string
+ description: The name or ID of QoS policy to attach to this network
+ required: false
+ status: SUPPORTED
+ allowed_address_pairs:
+ type: list
+ description: Additional MAC/IP address pairs allowed to pass through the port
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.AddressPair
+ binding:vnic_type:
+ type: string
+ description: The vnic type to be bound on the neutron port
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - macvtap
+ - direct
+ - normal
+ value_specs:
+ type: map
+ description: Extra parameters to include in the request
+ required: false
+ default: {
+ }
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ device_owner:
+ type: string
+ description: Name of the network owning the port
+ required: false
+ status: SUPPORTED
+ network:
+ type: string
+ description: Network this port belongs to
+ required: false
+ status: SUPPORTED
+ replacement_policy:
+ type: string
+ description: Policy on how to respond to a stack-update for this resource
+ required: false
+ default: AUTO
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - REPLACE_ALWAYS
+ - AUTO
+ security_groups:
+ type: list
+ description: List of security group names or IDs
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ fixed_ips:
+ type: list
+ description: Desired IPs for this port
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.neutron.port.FixedIps
+ mac_address:
+ type: string
+ description: MAC address to give to this port
+ required: false
+ status: SUPPORTED
+ admin_state_up:
+ type: boolean
+ description: A boolean value specifying the administrative status of the network
+ required: false
+ default: true
+ status: SUPPORTED
+ name:
+ type: string
+ description: A symbolic name for this port
+ required: false
+ status: SUPPORTED
+ attributes:
+ tenant_id:
+ type: string
+ description: Tenant owning the port
+ status: SUPPORTED
+ network_id:
+ type: string
+ description: Unique identifier for the network owning the port
+ status: SUPPORTED
+ qos_policy_id:
+ type: string
+ description: The QoS policy ID attached to this network
+ status: SUPPORTED
+ show:
+ type: string
+ description: Detailed information about resource
+ status: SUPPORTED
+ subnets:
+ type: list
+ description: Subnets of this network
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ status:
+ type: string
+ description: The status of the network
+ status: SUPPORTED
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
+ occurrences:
+ - 1
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.zip b/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.zip
new file mode 100644
index 0000000000..f120afb16e
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/neutronPort/neutronPort.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.json b/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.json
new file mode 100644
index 0000000000..007106b61e
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.json
@@ -0,0 +1,24 @@
+{
+ "payloadName": "novaServer.yml",
+ "contactId": "jh0003",
+ "name": "NovaServer",
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing information are specified the cloud provider default
+ machine will be used. It is strongly recommended to specify the required CPUs and memory
+ at least.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "NovaServer"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.yml b/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.yml
new file mode 100644
index 0000000000..0ec79f5229
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.yml
@@ -0,0 +1,249 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: NovaServerGlobalTypes
+ template_version: 1.0.0
+description: Nova Server TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+data_types:
+ org.openecomp.datatypes.heat.novaServer.network.PortExtraProperties:
+ derived_from: tosca.datatypes.Root
+ description: Nova server network expand properties for port
+ properties:
+ port_security_enabled:
+ type: boolean
+ description: Flag to enable/disable port security on the port
+ required: false
+ status: SUPPORTED
+ mac_address:
+ type: string
+ description: MAC address to give to this port
+ required: false
+ status: SUPPORTED
+ admin_state_up:
+ type: boolean
+ description: The administrative state of this port
+ required: false
+ default: true
+ status: SUPPORTED
+ qos_policy:
+ type: string
+ description: The name or ID of QoS policy to attach to this port
+ required: false
+ status: SUPPORTED
+ allowed_address_pairs:
+ type: list
+ description: Additional MAC/IP address pairs allowed to pass through the port
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.AddressPair
+ binding:vnic_type:
+ type: string
+ description: The vnic type to be bound on the neutron port
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - macvtap
+ - direct
+ - normal
+ value_specs:
+ type: map
+ description: Extra parameters to include in the request
+ required: false
+ default: {
+ }
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ org.openecomp.datatypes.heat.novaServer.network.AddressInfo:
+ derived_from: tosca.datatypes.network.NetworkInfo
+ description: Network addresses with corresponding port id
+ properties:
+ port_id:
+ type: string
+ description: Port id
+ required: false
+ status: SUPPORTED
+node_types:
+ org.openecomp.resource.vfc.nodes.heat.nova.Server:
+ derived_from: tosca.nodes.Compute
+ properties:
+ admin_pass:
+ type: string
+ description: The administrator password for the server
+ required: false
+ status: SUPPORTED
+ availability_zone:
+ type: string
+ description: Availability zone to create servers in
+ required: false
+ status: SUPPORTED
+ image:
+ type: string
+ description: The ID or name of the image to boot with
+ required: false
+ status: SUPPORTED
+ image_update_policy:
+ type: string
+ description: Policy on how to apply an image-id update
+ required: false
+ default: REBUILD
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - REBUILD_PRESERVE_EPHEMERAL
+ - REPLACE
+ - REBUILD
+ metadata:
+ type: json
+ description: Arbitrary JSON metadata to store for this server
+ required: false
+ status: SUPPORTED
+ contrail_service_instance_ind:
+ type: boolean
+ description: Nova server related to service instance indicator
+ required: false
+ default: false
+ status: SUPPORTED
+ user_data_update_policy:
+ type: string
+ description: Policy on how to apply a user_data update
+ required: false
+ default: REPLACE
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - REPLACE
+ - IGNORE
+ flavor_update_policy:
+ type: string
+ description: Policy on how to apply a flavor update
+ required: false
+ default: RESIZE
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - RESIZE
+ - REPLACE
+ user_data:
+ type: string
+ description: User data script to be executed by cloud-init
+ required: false
+ default: ''
+ status: SUPPORTED
+ flavor:
+ type: string
+ description: The ID or name of the flavor to boot onto
+ required: true
+ status: SUPPORTED
+ key_name:
+ type: string
+ description: Name of keypair to inject into the server
+ required: false
+ status: SUPPORTED
+ reservation_id:
+ type: string
+ description: A UUID for the set of servers being requested
+ required: false
+ status: SUPPORTED
+ security_groups:
+ type: list
+ description: List of security group names or IDs
+ required: false
+ default: [
+ ]
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ config_drive:
+ type: boolean
+ description: enable config drive on the server
+ required: false
+ status: SUPPORTED
+ personality:
+ type: map
+ description: A map of files to create/overwrite on the server upon boot
+ required: false
+ default: {
+ }
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ software_config_transport:
+ type: string
+ description: How the server should receive the metadata required for software configuration
+ required: false
+ default: POLL_SERVER_CFN
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - POLL_SERVER_CFN
+ - POLL_SERVER_HEAT
+ - POLL_TEMP_URL
+ - ZAQAR_MESSAGE
+ user_data_format:
+ type: string
+ description: How the user_data should be formatted for the server
+ required: false
+ default: HEAT_CFNTOOLS
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - SOFTWARE_CONFIG
+ - RAW
+ - HEAT_CFNTOOLS
+ diskConfig:
+ type: string
+ description: Control how the disk is partitioned when the server is created
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - AUTO
+ - MANUAL
+ name:
+ type: string
+ description: Server name
+ required: false
+ status: SUPPORTED
+ scheduler_hints:
+ type: map
+ description: Arbitrary key-value pairs specified by the client to help boot a server
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: string
+ attributes:
+ accessIPv4:
+ type: string
+ description: The manually assigned alternative public IPv4 address of the server
+ status: SUPPORTED
+ addresses:
+ type: map
+ description: A dict of all network addresses with corresponding port_id
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.novaServer.network.AddressInfo
+ accessIPv6:
+ type: string
+ description: The manually assigned alternative public IPv6 address of the server
+ status: SUPPORTED
+ instance_name:
+ type: string
+ description: AWS compatible instance name
+ status: SUPPORTED
+ name:
+ type: string
+ description: Name of the server
+ status: SUPPORTED
+ show:
+ type: string
+ description: Detailed information about resource
+ status: SUPPORTED
+ console_urls:
+ type: string
+ description: URLs of servers consoles
+ status: SUPPORTED \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.zip b/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.zip
new file mode 100644
index 0000000000..aafefbc6b5
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/novaServer/novaServer.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.json b/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.json
new file mode 100644
index 0000000000..14d25a0ac2
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "securityRules.yml",
+ "contactId": "jh0003",
+ "name": "SecurityRules",
+ "description": "Configuration of policy rules to be applied on ports.",
+ "resourceIconPath": "securityrules",
+ "resourceType": "VFC",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Rules"
+ }
+ ]
+ }
+],
+ "tags": [
+ "SecurityRules"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.yml b/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.yml
new file mode 100644
index 0000000000..29e87e15fc
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.yml
@@ -0,0 +1,42 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+metadata:
+ template_name: NeutronSecurityRulesGlobalTypes
+ template_version: 1.0.0
+description: Neutron Security Rules TOSCA Global Types
+imports:
+ common_definitions:
+ file: CommonGlobalTypesServiceTemplate.yaml
+node_types:
+ org.openecomp.resource.vfc.rules.nodes.heat.network.neutron.SecurityRules:
+ derived_from: tosca.nodes.Root
+ properties:
+ description:
+ type: string
+ description: Description of the security group
+ required: false
+ status: SUPPORTED
+ name:
+ type: string
+ description: A symbolic name for this security group, which is not required to be unique.
+ required: false
+ status: SUPPORTED
+ rules:
+ type: list
+ description: List of security group rules
+ required: false
+ status: SUPPORTED
+ entry_schema:
+ type: org.openecomp.datatypes.heat.network.neutron.SecurityRules.Rule
+ attributes:
+ show:
+ type: string
+ description: Detailed information about resource
+ status: SUPPORTED
+ requirements:
+ - port:
+ capability: tosca.capabilities.Attachment
+ node: org.openecomp.resource.cp.nodes.heat.network.neutron.Port
+ relationship: corg.openecomp.relationships.AttachesTo
+ occurrences:
+ - 0
+ - UNBOUNDED
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.zip b/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.zip
new file mode 100644
index 0000000000..b1524a2d8f
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/securityRules/securityRules.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.json b/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.json
new file mode 100644
index 0000000000..5dd26b48ad
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "vl.yml",
+ "contactId": "jh0003",
+ "name": "VL",
+ "description": " Virtual link (VL) describes the basic topology of the connectivity as well as other required parameters (e.g. bandwidth and QoS class). ",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Network Connectivity",
+ "subcategories": [
+ {
+ "name": "Virtual Links"
+ }
+ ]
+ }
+],
+ "tags": [
+ "VL"
+ ]
+}
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.yml b/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.yml
new file mode 100644
index 0000000000..f2042f7798
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vl.VL:
+ derived_from: tosca.nodes.network.Network
+ properties:
+ vendor:
+ type: string
+ required: false
+ vl_name:
+ type: string
+ required: false
+
+ capabilities:
+ virtual_linkable:
+ type: tosca.capabilities.network.Linkable
+ end_point:
+ type: tosca.capabilities.Endpoint \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.zip b/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.zip
new file mode 100644
index 0000000000..c1889a9803
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/heat-types/vl/vl.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.yml b/catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.yml
new file mode 100644
index 0000000000..1b67118934
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.yml
@@ -0,0 +1,11 @@
+tosca.interfaces.node.lifecycle.Standard:
+ create:
+ description: Standard lifecycle create operation.
+ configure:
+ description: Standard lifecycle configure operation.
+ start:
+ description: Standard lifecycle start operation.
+ stop:
+ description: Standard lifecycle stop operation.
+ delete:
+ description: Standard lifecycle delete operation. \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.zip b/catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.zip
new file mode 100644
index 0000000000..9bcf93ab7d
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/interface-lifecycle-types/interfaceLifecycleTypes.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.json b/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.json
new file mode 100644
index 0000000000..addef2ae4b
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "DBMS.yml",
+ "contactId": "jh0003",
+ "name": "DBMS",
+ "description": "Represents a typical relational, SQL Database Management System software component or service.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "DBMS"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.yml b/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.yml
new file mode 100644
index 0000000000..28919d38e4
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.DBMS:
+ derived_from: tosca.nodes.SoftwareComponent
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [ tosca.nodes.Database ]
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.zip b/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.zip
new file mode 100644
index 0000000000..7f88b3ba53
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/DBMS/DBMS.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.json b/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.json
new file mode 100644
index 0000000000..30e3d74bc9
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "blockStorage.yml",
+ "contactId": "jh0003",
+ "name": "BlockStorage",
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "resourceIconPath": "objectStorage",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "BlockStorage"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.yml b/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.yml
new file mode 100644
index 0000000000..a82965215f
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.BlockStorage:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.zip b/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.zip
new file mode 100644
index 0000000000..b26f9e2c0e
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/blockStorage/blockStorage.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.json b/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.json
new file mode 100644
index 0000000000..31bb27be16
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.json
@@ -0,0 +1,24 @@
+{
+ "payloadName": "compute.yml",
+ "contactId": "jh0003",
+ "name": "Compute",
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing information are specified the cloud provider default
+ machine will be used. It is strongly recommended to specify the required CPUs and memory
+ at least.",
+ "resourceIconPath": "compute",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Compute"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.yml b/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.yml
new file mode 100644
index 0000000000..00b07fb908
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Compute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.zip b/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.zip
new file mode 100644
index 0000000000..6c008e866f
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/compute/compute.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.json b/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.json
new file mode 100644
index 0000000000..cd0a1386be
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "containerApplication.yml",
+ "contactId": "jh0003",
+ "name": "Application",
+ "description": "Represents an application that requires Container-level virtualization technology.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Application"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.yml b/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.yml
new file mode 100644
index 0000000000..8813f26e04
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Container.Application:
+ derived_from: tosca.nodes.Root
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Container
+ relationship: tosca.relationships.HostedOn
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.zip b/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.zip
new file mode 100644
index 0000000000..cec3e5df6c
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/containerApplication/containerApplication.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.json b/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.json
new file mode 100644
index 0000000000..85a0c63257
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "containerRuntime.yml",
+ "contactId": "jh0003",
+ "name": "Runtime",
+ "description": "Represents operating system-level virtualization technology used to run multiple application services on a single Compute host.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Runtime"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.yml b/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.yml
new file mode 100644
index 0000000000..86a10a0185
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.yml
@@ -0,0 +1,9 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Container.Runtime:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ scalable:
+ type: tosca.capabilities.Scalable
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.zip b/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.zip
new file mode 100644
index 0000000000..c30aff4098
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/containerRuntime/containerRuntime.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/database/database.json b/catalog-be/src/main/resources/import/tosca/normative-types/database/database.json
new file mode 100644
index 0000000000..f9cf01eb04
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/database/database.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "database.yml",
+ "contactId": "jh0003",
+ "name": "Database",
+ "description": "Represents a logical database that can be managed and hosted by a DBMS node.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Database"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Database"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/database/database.yml b/catalog-be/src/main/resources/import/tosca/normative-types/database/database.yml
new file mode 100644
index 0000000000..5166150c73
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/database/database.yml
@@ -0,0 +1,27 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Database:
+ derived_from: tosca.nodes.Root
+ properties:
+ name:
+ type: string
+ description: the logical name of the database
+ port:
+ type: integer
+ description: the port the underlying database service will listen to for data
+ user:
+ type: string
+ description: the optional user account name for DB administration
+ required: false
+ password:
+ type: string
+ description: the optional password for the DB user account
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.DBMS
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ database_endpoint:
+ type: tosca.capabilities.Endpoint.Database
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/database/database.zip b/catalog-be/src/main/resources/import/tosca/normative-types/database/database.zip
new file mode 100644
index 0000000000..289e7e5ec1
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/database/database.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.json b/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.json
new file mode 100644
index 0000000000..d108ee1cbb
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "loadBalancer.yml",
+ "contactId": "jh0003",
+ "name": "LoadBalancer",
+ "description": "Represents logical function that be used in conjunction with a Floating Address to distribute an application’s traffic (load) across a number of instances of the application (e.g., for a clustered or scaled application).",
+ "resourceIconPath": "loadBalancer",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "LoadBalancer"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.yml b/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.yml
new file mode 100644
index 0000000000..d577a2aa83
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.yml
@@ -0,0 +1,20 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.LoadBalancer:
+ derived_from: tosca.nodes.Root
+ properties:
+ # TBD
+ algorithm :
+ type: string
+ required: false
+ status: experimental
+ capabilities :
+ client:
+ type: tosca.capabilities.Endpoint.Public
+ occurrences: [0, UNBOUNDED]
+ description: the Floating (IP) client’s on the public network can connect to
+ requirements:
+ - application:
+ capability: tosca.capabilities.Endpoint
+ relationship: tosca.relationships.RoutesTo
+ occurrences: [0, UNBOUNDED]
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.zip b/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.zip
new file mode 100644
index 0000000000..3a847c6f56
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/loadBalancer/loadBalancer.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/network/network.json b/catalog-be/src/main/resources/import/tosca/normative-types/network/network.json
new file mode 100644
index 0000000000..4ba8311ce4
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/network/network.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "network.yml",
+ "contactId": "jh0003",
+ "name": "Network",
+ "description": "Represents a simple , logical network service.",
+ "resourceIconPath": "network",
+ "resourceType": "VL",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Network"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/network/network.yml b/catalog-be/src/main/resources/import/tosca/normative-types/network/network.yml
new file mode 100644
index 0000000000..9a71e80600
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/network/network.yml
@@ -0,0 +1,45 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.network.Network:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_version:
+ type: integer
+ required: false
+ default: 4
+ constraints:
+ - valid_values: [ 4, 6 ]
+ cidr:
+ type: string
+ required: false
+ start_ip:
+ type: string
+ required: false
+ end_ip:
+ type: string
+ required: false
+ gateway_ip:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ network_id:
+ type: string
+ required: false
+ segmentation_id:
+ type: string
+ required: false
+ network_type:
+ type: string
+ required: false
+ physical_network:
+ type: string
+ required: false
+ dhcp_enabled:
+ type: boolean
+ required: false
+ default: true
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/network/network.zip b/catalog-be/src/main/resources/import/tosca/normative-types/network/network.zip
new file mode 100644
index 0000000000..61788a9e39
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/network/network.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.json b/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.json
new file mode 100644
index 0000000000..dca5a4b89d
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "objectStorage.yml",
+ "contactId": "jh0003",
+ "name": "ObjectStorage",
+ "description": "Represents storage that provides the ability to store data as objects (or BLOBs of data) without consideration for the underlying filesystem or devices.",
+ "resourceIconPath": "objectStorage",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Infrastructure"
+ }
+ ]
+ }
+],
+ "tags": [
+ "ObjectStorage"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.yml b/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.yml
new file mode 100644
index 0000000000..9c338c3400
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.ObjectStorage:
+ derived_from: tosca.nodes.Root
+ properties:
+ name:
+ type: string
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 0 GB
+ maxsize:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 0 GB
+ capabilities:
+ storage_endpoint:
+ type: tosca.capabilities.Endpoint
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.zip b/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.zip
new file mode 100644
index 0000000000..6b15346064
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/objectStorage/objectStorage.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/port/port.json b/catalog-be/src/main/resources/import/tosca/normative-types/port/port.json
new file mode 100644
index 0000000000..2475bef065
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/port/port.json
@@ -0,0 +1,21 @@
+{
+ "payloadName": "port.yml",
+ "contactId": "jh0003",
+ "name": "Port",
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "resourceIconPath": "port",
+ "resourceType": "CP",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Network Elements"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Port"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/port/port.yml b/catalog-be/src/main/resources/import/tosca/normative-types/port/port.yml
new file mode 100644
index 0000000000..2d1540b27b
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/port/port.yml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.network.Port:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_address:
+ type: string
+ required: false
+ order:
+ type: integer
+ required: true
+ default: 0
+ constraints:
+ - greater_or_equal: 0
+ is_default:
+ type: boolean
+ required: false
+ default: false
+ ip_range_start:
+ type: string
+ required: false
+ ip_range_end:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - binding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/port/port.zip b/catalog-be/src/main/resources/import/tosca/normative-types/port/port.zip
new file mode 100644
index 0000000000..0bf97333ce
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/port/port.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/root/root.json b/catalog-be/src/main/resources/import/tosca/normative-types/root/root.json
new file mode 100644
index 0000000000..48846bc8be
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/root/root.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "root.yml",
+ "contactId": "jh0003",
+ "name": "Root",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "Root"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/root/root.yml b/catalog-be/src/main/resources/import/tosca/normative-types/root/root.yml
new file mode 100644
index 0000000000..e9b1de9518
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/root/root.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Root:
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.Root
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ interfaces:
+ Standard:
+ type: tosca.interfaces.node.lifecycle.Standard
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/root/root.zip b/catalog-be/src/main/resources/import/tosca/normative-types/root/root.zip
new file mode 100644
index 0000000000..b336a9dca3
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/root/root.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.json b/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.json
new file mode 100644
index 0000000000..ab8eda95eb
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "softwareComponent.yml",
+ "contactId": "jh0003",
+ "name": "SoftwareComponent",
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "SoftwareComponent"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.yml b/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.yml
new file mode 100644
index 0000000000..a154632c74
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.SoftwareComponent:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ admin_credential:
+ type: tosca.datatypes.Credential
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.zip b/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.zip
new file mode 100644
index 0000000000..f77816b83f
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/softwareComponent/softwareComponent.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.json b/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.json
new file mode 100644
index 0000000000..63c2936648
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "webApplication.yml",
+ "contactId": "jh0003",
+ "name": "WebApplication",
+ "description": "Represents a software application that can be managed and run by a Web Server node. Specific types of web applications such as Java, etc. could be derived from this type.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "WebApplication"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.yml b/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.yml
new file mode 100644
index 0000000000..5f9a775fe0
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.yml
@@ -0,0 +1,15 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.WebApplication:
+ derived_from: tosca.nodes.Root
+ properties:
+ context_root:
+ type: string
+ capabilities:
+ app_endpoint:
+ type: tosca.capabilities.Endpoint
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.WebServer
+ relationship: tosca.relationships.HostedOn
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.zip b/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.zip
new file mode 100644
index 0000000000..0b1889bd8e
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/webApplication/webApplication.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.json b/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.json
new file mode 100644
index 0000000000..058af97664
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.json
@@ -0,0 +1,20 @@
+{
+ "payloadName": "webServer.yml",
+ "contactId": "jh0003",
+ "name": "WebServer",
+ "description": "Represents an abstract software component or service that is capable of hosting and providing management operations for one or more Web Application nodes.",
+ "resourceIconPath": "defaulticon",
+ "categories": [
+ {
+ "name": "Generic",
+ "subcategories": [
+ {
+ "name": "Abstract"
+ }
+ ]
+ }
+],
+ "tags": [
+ "WebServer"
+ ]
+} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.yml b/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.yml
new file mode 100644
index 0000000000..7c957b5247
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.yml
@@ -0,0 +1,11 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.WebServer:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ # Private, layer 4 endpoints
+ data_endpoint: tosca.capabilities.Endpoint
+ admin_endpoint: tosca.capabilities.Endpoint.Admin
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [ tosca.nodes.WebApplication ] \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.zip b/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.zip
new file mode 100644
index 0000000000..45bee47eff
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/normative-types/webServer/webServer.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.yml b/catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.yml
new file mode 100644
index 0000000000..ceb22da2af
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.yml
@@ -0,0 +1,94 @@
+tosca.policies.Root:
+ description: The TOSCA Policy Type all other TOSCA Policy Types derive from
+tosca.policies.Placement:
+ derived_from: tosca.policies.Root
+ description: The TOSCA Policy Type definition that is used to govern placement of TOSCA nodes or groups of nodes.
+tosca.policies.Scaling:
+ derived_from: tosca.policies.Root
+ description: The TOSCA Policy Type definition that is used to govern scaling of TOSCA nodes or groups of nodes.
+tosca.policies.Update:
+ derived_from: tosca.policies.Root
+ description: The TOSCA Policy Type definition that is used to govern update of TOSCA nodes or groups of nodes.
+tosca.policies.Performance:
+ derived_from: tosca.policies.Root
+ description: The TOSCA Policy Type definition that is used to declare performance requirements for TOSCA nodes or groups of nodes.
+org.openecomp.policies.placement.Antilocate:
+ derived_from: tosca.policy.placement
+ description: My placement policy for separation based upon container type value
+ properties:
+ name:
+ type: string
+ description: The name of the policy
+ required: false
+ status: SUPPORTED
+ container_type:
+ type: string
+ description: container type
+ required: false
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - host
+ - region
+ - compute
+org.openecomp.policies.placement.Colocate:
+ derived_from: tosca.policy.placement
+ description: Keep associated nodes (groups of nodes) based upon affinity value
+ properties:
+ name:
+ type: string
+ description: The name of the policy
+ required: false
+ status: SUPPORTED
+ affinity:
+ type: string
+ description: affinity
+ required: true
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - host
+ - region
+ - compute
+org.openecomp.policies.placement.valet.Diversity:
+ derived_from: tosca.policy.placement
+ description: Valet Diversity
+ properties:
+ level:
+ type: string
+ description: diversity
+ required: false
+ default: host
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - host
+ - rack
+org.openecomp.policies.placement.valet.Exclusivity:
+ derived_from: tosca.policy.placement
+ description: Valet Exclusivity
+ properties:
+ level:
+ type: string
+ description: exclusivity
+ required: false
+ default: host
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - host
+ - rack
+org.openecomp.policies.placement.valet.Affinity:
+ derived_from: tosca.policy.placement
+ description: Valet Affinity
+ properties:
+ level:
+ type: string
+ description: affinity
+ required: false
+ default: host
+ status: SUPPORTED
+ constraints:
+ - valid_values:
+ - host
+ - rack \ No newline at end of file
diff --git a/catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.zip b/catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.zip
new file mode 100644
index 0000000000..a6c38ef9de
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/policy-types/policyTypes.zip
Binary files differ
diff --git a/catalog-be/src/main/resources/import/tosca/users/importUsers.yaml b/catalog-be/src/main/resources/import/tosca/users/importUsers.yaml
new file mode 100644
index 0000000000..368a2b06f3
--- /dev/null
+++ b/catalog-be/src/main/resources/import/tosca/users/importUsers.yaml
@@ -0,0 +1,13 @@
+---
+users:
+ jh0003:
+ firstName: Jimmy
+ lastName: Hendrix
+ role: ADMIN
+ email: admin@att.com
+ jh0006:
+ firstName: Jimmy
+ lastName: Hendrix
+ role: DESIGNER1
+ email: admin@att.com
+
diff --git a/catalog-be/src/main/resources/jetty-ssl.xml b/catalog-be/src/main/resources/jetty-ssl.xml
new file mode 100644
index 0000000000..9b375087f1
--- /dev/null
+++ b/catalog-be/src/main/resources/jetty-ssl.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<!-- ============================================================= -->
+<!-- Configure a TLS (SSL) Context Factory -->
+<!-- This configuration must be used in conjunction with jetty.xml -->
+<!-- and either jetty-https.xml or jetty-spdy.xml (but not both) -->
+<!-- ============================================================= -->
+<Configure id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
+ <Set name="KeyStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.keystore" default="etc/keystore"/></Set>
+ <Set name="KeyStorePassword"><Property name="jetty.keystore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+ <Set name="KeyManagerPassword"><Property name="jetty.keymanager.password" default="OBF:1u2u1wml1z7s1z7a1wnl1u2g"/></Set>
+ <Set name="TrustStorePath"><Property name="jetty.base" default="." />/<Property name="jetty.truststore" default="etc/keystore"/></Set>
+ <Set name="TrustStorePassword"><Property name="jetty.truststore.password" default="OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"/></Set>
+ <Set name="EndpointIdentificationAlgorithm"></Set>
+ <Set name="NeedClientAuth"><Property name="jetty.ssl.needClientAuth" default="false"/></Set>
+ <Set name="WantClientAuth"><Property name="jetty.ssl.wantClientAuth" default="false"/></Set>
+ <Set name="ExcludeCipherSuites">
+ <Array type="String">
+ <Item>SSL_RSA_WITH_DES_CBC_SHA</Item>
+ <Item>SSL_DHE_RSA_WITH_DES_CBC_SHA</Item>
+ <Item>SSL_DHE_DSS_WITH_DES_CBC_SHA</Item>
+ <Item>SSL_RSA_EXPORT_WITH_RC4_40_MD5</Item>
+ <Item>SSL_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
+ <Item>SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA</Item>
+ <Item>SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA</Item>
+ </Array>
+ </Set>
+
+ <!-- =========================================================== -->
+ <!-- Create a TLS specific HttpConfiguration based on the -->
+ <!-- common HttpConfiguration defined in jetty.xml -->
+ <!-- Add a SecureRequestCustomizer to extract certificate and -->
+ <!-- session information -->
+ <!-- =========================================================== -->
+ <New id="sslHttpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
+ <Arg><Ref refid="httpConfig"/></Arg>
+ <Call name="addCustomizer">
+ <Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
+ </Call>
+ </New>
+
+ <Set name="ExcludeProtocols">
+ <Array type="java.lang.String">
+ <Item>SSLv3</Item>
+ <Item>SSLv2</Item>
+ </Array>
+ </Set>
+
+
+</Configure>
diff --git a/catalog-be/src/main/resources/keystore/README.txt b/catalog-be/src/main/resources/keystore/README.txt
new file mode 100644
index 0000000000..bbbbd07e27
--- /dev/null
+++ b/catalog-be/src/main/resources/keystore/README.txt
@@ -0,0 +1,16 @@
+keytool -genkeypair -keystore catalogbe.jks -alias catalogbe -keypass Aa123456 -storepass Aa123456 -keyalg RSA -keysize 2048 -validity 3650 -dname "CN=Catalog BE, OU=Development, O=AT&T, L=TLV, C=IL"
+
+
+3650 – 10 years validity
+Eyal Sofer – creator
+Development – Organization unit
+AT&T – Organization
+TLV- City
+IL – Country code
+
+
+catalogbe.jks – name of keystore
+Aa123456 - password
+
+#In order to generate the password OBF:..., run the following command:
+java -cp ../jetty-distribution-9.2.7.v20150116/lib/jetty-http-9.2.7.v20150116.jar:../jetty-distribution-9.2.7.v20150116/lib/jetty-util-9.2.7.v20150116.jar org.eclipse.jetty.util.security.Password Aa123456 \ No newline at end of file
diff --git a/catalog-be/src/main/resources/keystore/catalogbe.jks b/catalog-be/src/main/resources/keystore/catalogbe.jks
new file mode 100644
index 0000000000..8493a46aca
--- /dev/null
+++ b/catalog-be/src/main/resources/keystore/catalogbe.jks
Binary files differ
diff --git a/catalog-be/src/main/resources/keystore/catalogbe.jks.pwd b/catalog-be/src/main/resources/keystore/catalogbe.jks.pwd
new file mode 100644
index 0000000000..b113d7d888
--- /dev/null
+++ b/catalog-be/src/main/resources/keystore/catalogbe.jks.pwd
@@ -0,0 +1 @@
+OBF:1cp61iuj194s194u194w194y1is31cok \ No newline at end of file
diff --git a/catalog-be/src/main/resources/portal.properties b/catalog-be/src/main/resources/portal.properties
new file mode 100644
index 0000000000..33ae0dac94
--- /dev/null
+++ b/catalog-be/src/main/resources/portal.properties
@@ -0,0 +1,25 @@
+# Portal REST URL, ends "/auxapi"
+ecomp_rest_url = https://localhost/ecompportal/auxapi
+
+# Java implementation of interface
+portal.api.impl.class = org.openecomp.sdc.be.ecomp.EcompIntImpl
+
+# CSP-SSO URL
+ecomp_redirect_url = https://localhost/ecomp_portal_ist/ecompportal/process_csp
+# Cookie set by CSP-SSO
+csp_cookie_name = attESSec
+# CSP setting, most use PROD; DEV also recognized
+csp_gate_keeper_prod_key = PROD
+
+# Comma-separated list of UEB server names
+ueb_url_list =
+# UEB topic where Portal listens
+ecomp_portal_inbox_name = ECOMP-PORTAL-INBOX-TEST
+# UEB key generated while on-boarding
+ueb_app_key = app_key_here
+# UEB secret generated while on-boarding
+ueb_app_secret = app_secret_here
+# UEB topic where App listens
+ueb_app_mailbox_name = app_topic_name_here
+# Consumer group name; most Apps should use {UUID}
+ueb_app_consumer_group_name = {UUID} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importCategoryTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importCategoryTypes.py
new file mode 100644
index 0000000000..aca21754e8
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importCategoryTypes.py
@@ -0,0 +1,74 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importNormativeElements import createNormativeElement
+
+from importCommon import *
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ]'
+
+
+def importCategories(beHost, bePort, adminUser, exitOnSuccess, fileDir):
+ result = createNormativeElement(beHost, bePort, adminUser, fileDir, "/sdc2/rest/v1/catalog/uploadType/categories", "categoryTypes", "categoriesZip")
+
+ printFrameLine()
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ if ( result[1] == None or result[1] not in [200, 201, 409] ):
+ errorAndExit(1, None)
+ else:
+ if (exitOnSuccess == True):
+ errorAndExit(0, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:h:",["ip=","port=","user="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ importCategories(beHost, bePort, adminUser, True, "../../../import/tosca/categories/")
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importCommon.py b/catalog-be/src/main/resources/scripts/import/tosca/importCommon.py
new file mode 100644
index 0000000000..acdb067849
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importCommon.py
@@ -0,0 +1,43 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+
+###############################################################################################################
+#
+#
+###############################################################################################################
+
+debugFlag = True
+
+def join_strings(lst):
+ concat = ""
+ for string in lst:
+ if (string != None):
+ if (type(string) == int):
+ string = str(string)
+ concat += (string + " ")
+ return concat
+
+def debug(desc, *args):
+ 'print only if debug enabled'
+ if (debugFlag == True):
+ print desc, join_strings(args)
+
+def log(desc, arg=None):
+ 'print log info'
+ print desc, arg
+
+def errorAndExit(errorCode, errorDesc):
+ if ( errorCode > 0 ):
+ print "status={0}. {1}".format(errorCode, '' if errorDesc == None else errorDesc)
+ else:
+ print "status={0}".format(errorCode)
+ sys.exit(errorCode)
+
+def printNameAndReturnCode(name, code):
+ print "{0:30} | {1:6}".format(name, code)
+
+def printFrameLine():
+ print "----------------------------------------"
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importDataTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importDataTypes.py
new file mode 100644
index 0000000000..5d7f5b7576
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importDataTypes.py
@@ -0,0 +1,74 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importNormativeElements import createNormativeElement
+
+from importCommon import *
+################################################################################################################################################
+# #
+# Import tosca data types #
+# #
+# activation : #
+# python importDataTypes.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importDataTypes.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ]'
+
+
+def importDataTypes(beHost, bePort, adminUser, exitOnSuccess, fileDir):
+ result = createNormativeElement(beHost, bePort, adminUser, fileDir, "/sdc2/rest/v1/catalog/uploadType/datatypes", "dataTypes", "dataTypesZip")
+
+ printFrameLine()
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ if ( result[1] == None or result[1] not in [200, 201, 409] ):
+ errorAndExit(1, None)
+ else:
+ if (exitOnSuccess == True):
+ errorAndExit(0, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:h:",["ip=","port=","user="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ importDataTypes(beHost, bePort, adminUser, True, "../../../import/tosca/data-types/")
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importGroupTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importGroupTypes.py
new file mode 100644
index 0000000000..2b6a5db6f8
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importGroupTypes.py
@@ -0,0 +1,74 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importNormativeElements import createNormativeElement
+
+from importCommon import *
+################################################################################################################################################
+# #
+# Import tosca data types #
+# #
+# activation : #
+# python importGroupTypes.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importGroupTypes.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ]'
+
+
+def importGroupTypes(beHost, bePort, adminUser, exitOnSuccess, fileDir):
+ result = createNormativeElement(beHost, bePort, adminUser, fileDir, "/sdc2/rest/v1/catalog/uploadType/grouptypes", "groupTypes", "groupTypesZip")
+
+ printFrameLine()
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ if ( result[1] == None or result[1] not in [200, 201, 409] ):
+ errorAndExit(1, None)
+ else:
+ if (exitOnSuccess == True):
+ errorAndExit(0, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:h:",["ip=","port=","user="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ importGroupTypes(beHost, bePort, adminUser, True, "../../../import/tosca/group-types/")
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importHeatTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importHeatTypes.py
new file mode 100644
index 0000000000..07eacf8d61
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importHeatTypes.py
@@ -0,0 +1,113 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importCommon import *
+from importNormativeTypes import *
+import importCommon
+
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def importHeatTypes(beHost, bePort, adminUser, fileDir, updateversion):
+
+ heatTypes = [ "cinderVolume",
+# "contrailVirtualNetwork",
+ "neutronNet",
+ "neutronPort",
+ "novaServer",
+ "vl",
+ "eline",
+ "abstractSubstitute",
+# "contrailNetworkRules",
+# "contrailPort",
+# "contrailV2NetworkRules",
+# "contrailV2VirtualNetwork",
+ "securityRules"
+# "contrailAbstractSubstitute",
+# "contrailCompute",
+# "contrailV2VirtualMachineInterface"
+ ]
+
+ responseCodes = [200, 201]
+
+ if(updateversion == 'false'):
+ responseCodes = [200, 201, 409]
+
+ results = []
+ for heatType in heatTypes:
+ result = createNormativeType(beHost, bePort, adminUser, fileDir, heatType, updateversion)
+ results.append(result)
+ if ( result[1] == None or result[1] not in responseCodes) :
+ print "Failed creating heat type " + heatType + ". " + str(result[1])
+ return results
+
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ updateversion = 'true'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:v:h:",["ip=","port=","user=","updateversion="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-v", "--updateversion"):
+ if (arg.lower() == "false" or arg.lower() == "no"):
+ updateversion = 'false'
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ results = importHeatTypes(beHost, bePort, adminUser, "../../../import/tosca/heat-types/", updateversion)
+
+ print "-----------------------------"
+ for result in results:
+ print "{0:20} | {1:6}".format(result[0], result[1])
+ print "-----------------------------"
+
+ responseCodes = [200, 201]
+
+ if(updateversion == 'false'):
+ responseCodes = [200, 201, 409]
+
+ failedNormatives = filter(lambda x: x[1] == None or x[1] not in responseCodes, results)
+ if (len(failedNormatives) > 0):
+ errorAndExit(1, None)
+ else:
+ errorAndExit(0, None)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importNodeType.py b/catalog-be/src/main/resources/scripts/import/tosca/importNodeType.py
new file mode 100644
index 0000000000..f35cc7bcee
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importNodeType.py
@@ -0,0 +1,156 @@
+import pycurl
+import sys, getopt, os
+from StringIO import StringIO
+import json
+import copy
+from importCommon import *
+import importCommon
+import zipfile
+################################################################################################################################################
+# #
+################################################################################################################################################
+
+def createZipFromYml(ymlFile, zipFile):
+ zip = zipfile.ZipFile(zipFile, 'w', zipfile.ZIP_DEFLATED)
+
+ zip.write(ymlFile, os.path.basename(ymlFile))
+ zip.close()
+
+def createUserNormativeType(beHost, bePort, adminUser, fileDir, ELEMENT_NAME):
+
+ try:
+ log("in create normative type ", ELEMENT_NAME)
+ debug("userId", adminUser)
+ debug("fileDir", fileDir)
+
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ url = 'http://' + beHost + ':' + bePort + '/sdc2/rest/v1/catalog/upload/multipart'
+ c.setopt(c.URL, url)
+ c.setopt(c.POST, 1)
+
+ adminHeader = 'USER_ID: ' + adminUser
+ #c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', adminHeader])
+ c.setopt(pycurl.HTTPHEADER, [adminHeader])
+
+ ymlFile = fileDir + ELEMENT_NAME + "/normative-types-new-" + ELEMENT_NAME + ".yml"
+ zipFile = fileDir + ELEMENT_NAME + "/normative-types-new-" + ELEMENT_NAME + ".zip"
+ debug(ymlFile)
+ debug(zipFile)
+ path = zipFile
+ debug("path=" + path)
+ CURRENT_JSON_FILE=fileDir + ELEMENT_NAME + "/" + ELEMENT_NAME + ".json"
+ debug(CURRENT_JSON_FILE)
+ jsonFile = open(CURRENT_JSON_FILE)
+
+ debug("before load json")
+ json_data = json.load(jsonFile, strict=False)
+ debug(json_data)
+
+ jsonAsStr = json.dumps(json_data)
+ debug(path)
+ send = [('resourceMetadata', jsonAsStr), ('resourceZip', (pycurl.FORM_FILE, path))]
+ debug(send)
+ c.setopt(pycurl.HTTPPOST, send)
+
+ c.setopt(c.WRITEFUNCTION, buffer.write)
+ res = c.perform()
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ if (httpRes != None):
+ debug("http response=", httpRes)
+ #print('Status: ' + str(responseCode))
+ debug(buffer.getvalue())
+ c.close()
+
+ return (ELEMENT_NAME, httpRes, buffer.getvalue())
+
+ except Exception as inst:
+ print("ERROR=" + str(inst))
+ return (ELEMENT_NAME, None, None)
+
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-l <directory base location> | --location=<directory base location>] [-e <element name> | --element=<element name>]'
+ print "----------------- Example -------------------"
+ print "python importNodeType.py -d false -l /home/vagrant/catalog-be-1604.0.2.15.6-SNAPSHOT/scripts/import/tosca/../../../import/tosca/user-normative-types/ -e root1"
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ debugf = None
+ location = None
+ element = None
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:d:l:e:h",["ip=","port=","user=","location=","element=", "debug="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-l", "--location"):
+ location = arg
+ elif opt in ("-e", "--element"):
+ element = arg
+ elif opt in ("-d", "--debug"):
+ print arg
+ debugf = bool(arg.lower() == "true" or arg.lower() == "yes")
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ if (debugf != None):
+ print 'set debug mode to ' + str(debugf)
+ importCommon.debugFlag = debugf
+
+ if (location == None):
+ print 'Missing file location'
+ usage()
+ sys.exit(3)
+
+ if (element == None):
+ print 'Missing element name. E.g. root, compute, ...'
+ usage()
+ sys.exit(3)
+
+ #pathdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+
+ #baseFileLocation = pathdir + "/../../../import/tosca/"
+ #fileDir = baseFileLocation + "user-normative-types/"
+
+ #normativeType = "root1"
+
+ result = createUserNormativeType(beHost, bePort, adminUser, location, element)
+ #result = createUserNormativeType(beHost, bePort, adminUser, fileDir, normativeType)
+ print "---------------------------------------"
+ print "{0:30} | {1:6}".format(result[0], result[1])
+ print "---------------------------------------"
+
+ if ( result[1] == None or result[1] not in [200, 201] ) :
+ print "Failed creating normative type " + element + ". " + str(result[1])
+ errorAndExit(1, None)
+
+ errorAndExit(0, None)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importNormativeAll.py b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeAll.py
new file mode 100644
index 0000000000..ec9e93e6e4
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeAll.py
@@ -0,0 +1,135 @@
+import pycurl
+import sys, getopt, os
+from StringIO import StringIO
+import json
+import copy
+import time
+#from importNormativeElements import createNormativeElement
+from importNormativeElements import *
+from importNormativeTypes import importNormativeTypes
+from importHeatTypes import importHeatTypes
+from importNormativeCapabilities import importNormativeCapabilities
+from importCategoryTypes import importCategories
+from importNormativeInterfaceLifecycleTypes import importNormativeInterfaceLifecycleType
+from importDataTypes import importDataTypes
+from importGroupTypes import importGroupTypes
+from importPolicyTypes import importPolicyTypes
+from importCommon import *
+import importCommon
+
+#################################################################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importNormativeAll.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>] #
+# [-v <true|false> | --updateversion=<true|false>] #
+# #
+# shortest activation (be host = localhost, be port = 8080, user = jh0003): # # #
+# python importNormativeAll.py #
+# #
+#################################################################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>] [-v <true|false> | --updateversion=<true|false>]'
+
+def handleResults(results, updateversion):
+ printFrameLine()
+ for result in results:
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ responseCodes = [200, 201]
+
+ if(updateversion == 'false'):
+ responseCodes = [200, 201, 409]
+
+ failedResults = filter(lambda x: x[1] == None or x[1] not in responseCodes, results)
+ if (len(failedResults) > 0):
+ errorAndExit(1, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ debugf = None
+ updateversion = 'true'
+ importCommon.debugFlag = False
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:d:v:h",["ip=","port=","user=","debug=","updateversion="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-d", "--debug"):
+ print arg
+ debugf = bool(arg.lower() == "true" or arg.lower() == "yes")
+ elif opt in ("-v", "--updateversion"):
+ print arg
+ if (arg.lower() == "false" or arg.lower() == "no"):
+ updateversion = 'false'
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser, ', debug =', debugf, ', updateversion =', updateversion
+
+ if (debugf != None):
+ print 'set debug mode to ' + str(debugf)
+ importCommon.debugFlag = debugf
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ print sys.argv[0]
+ pathdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ debug("path dir =" + pathdir)
+
+ baseFileLocation = pathdir + "/../../../import/tosca/"
+
+ fileLocation = baseFileLocation + "data-types/"
+ importDataTypes(beHost, bePort, adminUser, False, fileLocation)
+
+ print 'sleep until data type cache is updated'
+ time.sleep( 70 )
+
+ fileLocation = baseFileLocation + "capability-types/"
+ importNormativeCapabilities(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "interface-lifecycle-types/"
+ importNormativeInterfaceLifecycleType(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "categories/"
+ importCategories(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "normative-types/"
+ results = importNormativeTypes(beHost, bePort, adminUser, fileLocation, updateversion)
+ handleResults(results, updateversion)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ resultsHeat = importHeatTypes(beHost, bePort, adminUser, fileLocation, updateversion)
+ handleResults(resultsHeat, updateversion)
+
+ fileLocation = baseFileLocation + "group-types/"
+ importGroupTypes(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "policy-types/"
+ importPolicyTypes(beHost, bePort, adminUser, False, fileLocation)
+
+ errorAndExit(0, None)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importNormativeCapabilities.py b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeCapabilities.py
new file mode 100644
index 0000000000..bda02ddda9
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeCapabilities.py
@@ -0,0 +1,77 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importNormativeElements import createNormativeElement
+from importCommon import *
+import importCommon
+
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ]'
+
+
+def importNormativeCapabilities(beHost, bePort, adminUser, exitOnSuccess, fileDir):
+ result = createNormativeElement(beHost, bePort, adminUser, fileDir, "/sdc2/rest/v1/catalog/uploadType/capability", "capabilityTypes", "capabilityTypeZip")
+
+ printFrameLine()
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ if ( result[1] == None or result[1] not in [200, 201, 409] ):
+ importCommon.errorAndExit(1, None)
+ else:
+ if (exitOnSuccess == True):
+ importCommon.errorAndExit(0, None)
+
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:h:",["ip=","port=","user="])
+ except getopt.GetoptError:
+ usage()
+ importCommon.errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ importNormativeCapabilities(beHost, bePort, adminUser, True, "../../../import/tosca/capability-types/")
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importNormativeElements.py b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeElements.py
new file mode 100644
index 0000000000..af643da4f4
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeElements.py
@@ -0,0 +1,65 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importCommon import *
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def createNormativeElement(beHost, bePort, adminUser, fileDir, urlSuffix, ELEMENT_NAME, elementFormName):
+
+ try:
+ log("in create normative element ", ELEMENT_NAME)
+
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ url = 'http://' + beHost + ':' + bePort + urlSuffix
+ c.setopt(c.URL, url)
+ c.setopt(c.POST, 1)
+
+ adminHeader = 'USER_ID: ' + adminUser
+ #c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', adminHeader])
+ c.setopt(pycurl.HTTPHEADER, [adminHeader])
+
+
+ path = fileDir + "/" + ELEMENT_NAME + ".zip"
+ debug(path)
+
+ send = [(elementFormName, (pycurl.FORM_FILE, path))]
+ debug(send)
+ c.setopt(pycurl.HTTPPOST, send)
+
+ #data = json.dumps(user)
+ #c.setopt(c.POSTFIELDS, data)
+
+ #c.setopt(c.WRITEFUNCTION, lambda x: None)
+ c.setopt(c.WRITEFUNCTION, buffer.write)
+ #print("before perform")
+ res = c.perform()
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ if (httpRes != None):
+ debug("http response=", httpRes)
+ #print('Status: ' + str(responseCode))
+ debug("response buffer", buffer.getvalue())
+ c.close()
+
+ return (ELEMENT_NAME, httpRes, buffer.getvalue())
+
+ except Exception as inst:
+ print("ERROR=" + str(inst))
+ return (ELEMENT_NAME, None, None)
+
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importNormativeInterfaceLifecycleTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeInterfaceLifecycleTypes.py
new file mode 100644
index 0000000000..33f1cc42de
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeInterfaceLifecycleTypes.py
@@ -0,0 +1,75 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importNormativeElements import createNormativeElement
+from importCommon import *
+
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ]'
+
+
+def importNormativeInterfaceLifecycleType(beHost, bePort, adminUser, exitOnSuccess, fileDir):
+ result = createNormativeElement(beHost, bePort, adminUser, fileDir, "/sdc2/rest/v1/catalog/uploadType/interfaceLifecycle", "interfaceLifecycleTypes", "interfaceLifecycleTypeZip")
+
+ printFrameLine()
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ if ( result[1] == None or result[1] not in [200, 201, 409] ):
+ errorAndExit(1, None)
+ else:
+ if (exitOnSuccess == True):
+ errorAndExit(0, None)
+
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:h:",["ip=","port=","user="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ importNormativeInterfaceLifecycleType(beHost, bePort, adminUser, True, "../../../import/tosca/interface-lifecycle-types//")
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importNormativeTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeTypes.py
new file mode 100644
index 0000000000..76bae682c0
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importNormativeTypes.py
@@ -0,0 +1,159 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importCommon import *
+import importCommon
+################################################################################################################################################
+# #
+# Import all users from a given file #
+# #
+# activation : #
+# python importUsers.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# [-v <true|false> | --updateversion=<true|false>] #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsers.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def createNormativeType(beHost, bePort, adminUser, fileDir, ELEMENT_NAME, updateversion):
+
+ try:
+ log("in create normative type ", ELEMENT_NAME)
+ debug("userId", adminUser)
+ debug("fileDir", fileDir)
+
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ url = 'http://' + beHost + ':' + bePort + '/sdc2/rest/v1/catalog/upload/multipart'
+ if updateversion != None:
+ url += '?createNewVersion=' + updateversion
+ c.setopt(c.URL, url)
+ c.setopt(c.POST, 1)
+
+ adminHeader = 'USER_ID: ' + adminUser
+ #c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', adminHeader])
+ c.setopt(pycurl.HTTPHEADER, [adminHeader])
+
+
+ path = fileDir + ELEMENT_NAME + "/" + ELEMENT_NAME + ".zip"
+ debug(path)
+ CURRENT_JSON_FILE=fileDir + ELEMENT_NAME + "/" + ELEMENT_NAME + ".json"
+ #sed -i 's/"userId": ".*",/"userId": "'${ATT_UID}'",/' ${CURRENT_JSON_FILE}
+
+ jsonFile = open(CURRENT_JSON_FILE)
+
+ debug("before load json")
+ json_data = json.load(jsonFile, strict=False)
+ debug(json_data)
+
+ jsonAsStr = json.dumps(json_data)
+
+ send = [('resourceMetadata', jsonAsStr), ('resourceZip', (pycurl.FORM_FILE, path))]
+ debug(send)
+ c.setopt(pycurl.HTTPPOST, send)
+
+ #data = json.dumps(user)
+ #c.setopt(c.POSTFIELDS, data)
+
+ #c.setopt(c.WRITEFUNCTION, lambda x: None)
+ c.setopt(c.WRITEFUNCTION, buffer.write)
+ #print("before perform")
+ res = c.perform()
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ if (httpRes != None):
+ debug("http response=", httpRes)
+ #print('Status: ' + str(responseCode))
+ debug(buffer.getvalue())
+ c.close()
+
+ return (ELEMENT_NAME, httpRes, buffer.getvalue())
+
+ except Exception as inst:
+ print("ERROR=" + str(inst))
+ return (ELEMENT_NAME, None, None)
+
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-v <true|false> | --updateversion=<true|false>]'
+
+
+def importNormativeTypes(beHost, bePort, adminUser, fileDir, updateversion):
+
+ normativeTypes = [ "root", "compute", "softwareComponent", "webServer", "webApplication", "DBMS", "database", "objectStorage", "blockStorage", "containerRuntime", "containerApplication", "loadBalancer", "port", "network"]
+ #normativeTypes = [ "root" ]
+ responseCodes = [200, 201]
+
+ if(updateversion == 'false'):
+ responseCodes = [200, 201, 409]
+
+ results = []
+ for normativeType in normativeTypes:
+ result = createNormativeType(beHost, bePort, adminUser, fileDir, normativeType, updateversion)
+ results.append(result)
+ if ( result[1] == None or result[1] not in responseCodes ):
+ print "Failed creating normative type " + normativeType + ". " + str(result[1])
+ return results
+
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ updateversion = 'true'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:v:h:",["ip=","port=","user=","updateversion="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-v", "--updateversion"):
+ if (arg.lower() == "false" or arg.lower() == "no"):
+ updateversion = 'false'
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser, ', updateversion =', updateversion
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ results = importNormativeTypes(beHost, bePort, adminUser, "../../../import/tosca/normative-types/", updateversion)
+
+ print "-----------------------------"
+ for result in results:
+ print "{0:20} | {1:6}".format(result[0], result[1])
+ print "-----------------------------"
+
+ responseCodes = [200, 201]
+
+ if(updateversion == 'false'):
+ responseCodes = [200, 201, 409]
+
+ failedNormatives = filter(lambda x: x[1] == None or x[1] not in responseCodes, results)
+ if (len(failedNormatives) > 0):
+ errorAndExit(1, None)
+ else:
+ errorAndExit(0, None)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importPolicyTypes.py b/catalog-be/src/main/resources/scripts/import/tosca/importPolicyTypes.py
new file mode 100644
index 0000000000..c01e159264
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importPolicyTypes.py
@@ -0,0 +1,74 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+from importNormativeElements import createNormativeElement
+
+from importCommon import *
+################################################################################################################################################
+# #
+# Import tosca data types #
+# #
+# activation : #
+# python importPolicyTypes.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importPolicyTypes.py [-f <input file> | --ifile=<input file> ] #
+# #
+################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ]'
+
+
+def importPolicyTypes(beHost, bePort, adminUser, exitOnSuccess, fileDir):
+ result = createNormativeElement(beHost, bePort, adminUser, fileDir, "/sdc2/rest/v1/catalog/uploadType/policytypes", "policyTypes", "policyTypesZip")
+
+ printFrameLine()
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ if ( result[1] == None or result[1] not in [200, 201, 409] ):
+ errorAndExit(1, None)
+ else:
+ if (exitOnSuccess == True):
+ errorAndExit(0, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:h:",["ip=","port=","user="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ importPolicyTypes(beHost, bePort, adminUser, True, "../../../import/tosca/policy-types/")
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/importUsersFromYaml.py b/catalog-be/src/main/resources/scripts/import/tosca/importUsersFromYaml.py
new file mode 100644
index 0000000000..8509855f38
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/importUsersFromYaml.py
@@ -0,0 +1,221 @@
+import pycurl
+import sys, getopt
+from StringIO import StringIO
+import json
+import copy
+import yaml
+
+########################################################################################################################################################
+# #
+# Import all users from a given YAML file #
+# #
+# activation : #
+# python importUsersFromYaml.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ] #
+# #
+# shortest activation (be host = localhost, be port = 8080): # #
+# python importUsersFromYaml.py [-f <input file> | --ifile=<input file> ] #
+# #
+# PyYAML module shall be added to python. #
+# pip install PyYAML>=3.1.0 --proxy=http://one.proxy.att.com:8080 #
+########################################################################################################################################################
+
+
+def importUsers(beHost, bePort, users, adminUser):
+
+ result = []
+
+ for user in users:
+
+ #print("Going to add user " + user['userId'])
+
+ getRes = getUser(beHost, bePort, user)
+ userId = getRes[0]
+ error = getRes[1]
+ #print error
+ if ( error != None and error == 404 ):
+ res = createUser(beHost, bePort, user ,adminUser)
+ result.append(res)
+ else:
+ if ( error == 200 ):
+ curResult = (userId, 409)
+ result.append(curResult)
+ else:
+ result.append(getRes)
+
+ return result
+
+
+def getUser(beHost, bePort, user):
+
+ if (user.get('userId') == None):
+ print "Ignoring record", user
+ return ('NotExist', 200)
+ userId = user['userId']
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ #print type(userId)
+ url = 'http://' + beHost + ':' + bePort + '/sdc2/rest/v1/user/' + str(userId)
+ c.setopt(c.URL, url)
+
+ #adminHeader = 'USER_ID: ' + adminUser
+ c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json'])
+ c.setopt(c.WRITEFUNCTION, lambda x: None)
+ res = c.perform()
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ #print("After get response code")
+ responseCode = c.getinfo(c.RESPONSE_CODE)
+
+ #print('Status: ' + str(responseCode))
+
+ c.close()
+
+ return (userId, httpRes)
+
+ except Exception as inst:
+ print(inst)
+ return (userId, None)
+
+
+
+def createUser(beHost, bePort, user, adminUser):
+
+ if (user.get('userId') == None):
+ print "Ignoring record", user
+ return ('NotExist', 200)
+
+ userId = user['userId']
+ try:
+ buffer = StringIO()
+ c = pycurl.Curl()
+
+ url = 'http://' + beHost + ':' + bePort + '/sdc2/rest/v1/user'
+ c.setopt(c.URL, url)
+ c.setopt(c.POST, 1)
+
+ adminHeader = 'USER_ID: ' + adminUser
+ c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', adminHeader])
+
+ data = json.dumps(user)
+ c.setopt(c.POSTFIELDS, data)
+
+ c.setopt(c.WRITEFUNCTION, lambda x: None)
+ #print("before perform")
+ res = c.perform()
+ #print(res)
+
+ #print("Before get response code")
+ httpRes = c.getinfo(c.RESPONSE_CODE)
+ #print("After get response code")
+ responseCode = c.getinfo(c.RESPONSE_CODE)
+
+ #print('Status: ' + str(responseCode))
+
+ c.close()
+
+ return (userId, httpRes)
+
+ except Exception as inst:
+ print(inst)
+ return (userId, None)
+
+
+def errorAndExit(errorCode, errorDesc):
+ if ( errorCode > 0 ):
+ print("status=" + str(errorCode) + ". " + errorDesc)
+ else:
+ print("status=" + str(errorCode))
+ sys.exit(errorCode)
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-f <input file> | --ifile=<input file> ]'
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ inputfile = None
+
+ adminUser = 'jh0003'
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:f:h:",["ip=","port=","ifile="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-f", "--ifile"):
+ inputfile = arg
+
+ print 'be host =',beHost,', be port =', bePort,', users file =',inputfile
+
+ if ( inputfile == None ):
+ usage()
+ sys.exit(3)
+
+ print 'Input file is ', inputfile
+
+
+ usersAsYamlFile = open(inputfile, 'r')
+ usersDoc = yaml.load(usersAsYamlFile)
+ print usersDoc
+
+ cloneUsers = []
+ for users in usersDoc.values():
+ for x,y in users.items():
+ copiedUser = y
+ copiedUser['userId'] = x
+ #print copiedUser
+ cloneUsers.append(copiedUser)
+
+ print cloneUsers
+
+ usersAsYamlFile.close()
+
+ #activeUsers = filter(lambda x: x.get('status') == None or x['status'] == 'ACTIVE', cloneUsers)
+
+ resultTable = importUsers(beHost, bePort, cloneUsers, adminUser)
+
+ g = lambda x: x[1] != 201 and x[1] != 409
+
+ result = filter(g, resultTable)
+
+ if ( len(result) > 0 ):
+ #print("ERROR: Failed to load the users " + ', '.join(map(lambda x: x[0],result)))
+ errorAndExit(3, "Failed to load the users " + ', '.join(map(lambda x: x[0],result)))
+
+ g = lambda x: x[1] == 409
+ result = filter(g, resultTable)
+
+ print("-------------------------------------------")
+ print("Existing users: " + ', '.join(map(lambda x: x[0],result)))
+
+ result = filter(lambda x: x[1] == 201, resultTable)
+ if ( len(result) == 0 ):
+ print("-------------------------------------------")
+ print("No NEW user was loaded. All users are already exist")
+ print("-------------------------------------------")
+ else:
+ print("-------------------------------------------")
+ print("Loaded users: " + ', '.join(map(lambda x: x[0],result)))
+ print("-------------------------------------------")
+
+ errorAndExit(0, None)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormative.py b/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormative.py
new file mode 100644
index 0000000000..e6bb620692
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormative.py
@@ -0,0 +1,114 @@
+import pycurl
+import sys, getopt, os
+from StringIO import StringIO
+import json
+import copy
+import time
+from importCategoryTypes import importCategories
+from importHeatTypes import importHeatTypes
+from importNormativeCapabilities import importNormativeCapabilities
+from importDataTypes import importDataTypes
+from importGroupTypes import importGroupTypes
+from importPolicyTypes import importPolicyTypes
+from importCommon import *
+import importCommon
+
+#################################################################################################################################################################################################
+# #
+# Upgrades the normative types #
+# #
+# activation : #
+# python upgradeNormative.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>] #
+# #
+# #
+# shortest activation (be host = localhost, be port = 8080, user = jh0003): # # #
+# python upgradeNormative.py #
+# #
+#################################################################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>]'
+
+def handleResults(results, updateversion):
+ printFrameLine()
+ for result in results:
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ failedResults = filter(lambda x: x[1] == None or x[1] not in [200, 201, 409], results)
+ if (len(failedResults) > 0):
+ errorAndExit(1, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ debugf = None
+ updateversion = 'false'
+ importCommon.debugFlag = False
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:d:h",["ip=","port=","user=","debug="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-d", "--debug"):
+ print arg
+ debugf = bool(arg.lower() == "true" or arg.lower() == "yes")
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser, ', debug =', debugf
+
+ if (debugf != None):
+ print 'set debug mode to ' + str(debugf)
+ importCommon.debugFlag = debugf
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ print sys.argv[0]
+ pathdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ debug("path dir =" + pathdir)
+
+ baseFileLocation = pathdir + "/../../../import/tosca/"
+
+ fileLocation = baseFileLocation + "categories/"
+ importCategories(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "data-types/"
+ importDataTypes(beHost, bePort, adminUser, False, fileLocation)
+
+ print 'sleep until data type cache is updated'
+ time.sleep( 70 )
+ fileLocation = baseFileLocation + "capability-types/"
+ importNormativeCapabilities(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "group-types/"
+ importGroupTypes(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "policy-types/"
+ importPolicyTypes(beHost, bePort, adminUser, False, fileLocation)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ resultsHeat = importHeatTypes(beHost, bePort, adminUser, fileLocation, updateversion)
+ handleResults(resultsHeat, 'false')
+
+ errorAndExit(0, None)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersion.py b/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersion.py
new file mode 100644
index 0000000000..0f70174173
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersion.py
@@ -0,0 +1,143 @@
+import pycurl
+import sys, getopt, os
+from StringIO import StringIO
+import json
+import copy
+from importCommon import *
+from importNormativeTypes import createNormativeType
+import importCommon
+
+#################################################################################################################################################################################################
+# #
+# Upgrades the normative types #
+# #
+# activation : #
+# python upgradeNormative.py [-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>] #
+# #
+# #
+# shortest activation (be host = localhost, be port = 8080, user = jh0003): # # #
+# python upgradeNormative.py #
+# #
+#################################################################################################################################################################################################
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>]'
+
+def handleResults(results, updateversion):
+ printFrameLine()
+ for result in results:
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ failedResults = filter(lambda x: x[1] == None or x[1] not in [200, 201, 409], results)
+ if (len(failedResults) > 0):
+ errorAndExit(1, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ debugf = None
+ updateversion = 'true'
+ importCommon.debugFlag = False
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:d:h",["ip=","port=","user=","debug="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-d", "--debug"):
+ print arg
+ debugf = bool(arg.lower() == "true" or arg.lower() == "yes")
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser, ', debug =', debugf
+
+ if (debugf != None):
+ print 'set debug mode to ' + str(debugf)
+ importCommon.debugFlag = debugf
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ print sys.argv[0]
+ pathdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ debug("path dir =" + pathdir)
+
+ baseFileLocation = pathdir + "/../../../import/tosca/"
+ results = []
+
+
+ ##########################################################################
+ #---------------------------------for release 1610---------------------- #
+ ##########################################################################
+
+ fileLocation = baseFileLocation + "normative-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "compute", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "normative-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "network", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "abstractSubstitute", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "contrailAbstractSubstitute", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "contrailNetworkRules", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "novaServer", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "neutronPort", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "contrailVirtualNetwork", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "neutronNet", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "vl", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "contrailV2VirtualNetwork", updateversion)
+ results.append(result)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ result = createNormativeType(beHost, bePort, adminUser, fileLocation, "securityRules", updateversion)
+ results.append(result)
+
+ handleResults(results, 'false')
+
+ errorAndExit(0, None)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersionAll.py b/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersionAll.py
new file mode 100644
index 0000000000..72198ceab6
--- /dev/null
+++ b/catalog-be/src/main/resources/scripts/import/tosca/upgradeNormativeVersionAll.py
@@ -0,0 +1,93 @@
+import pycurl
+import sys, getopt, os
+from StringIO import StringIO
+import json
+import copy
+import time
+from importNormativeTypes import importNormativeTypes
+from importHeatTypes import importHeatTypes
+from importCommon import *
+import importCommon
+
+def usage():
+ print sys.argv[0], '[-i <be host> | --ip=<be host>] [-p <be port> | --port=<be port> ] [-u <user userId> | --user=<user userId> ] [-d <true|false> | --debug=<true|false>] [-v <true|false> | --updateversion=<true|false>]'
+
+def handleResults(results, updateversion):
+ printFrameLine()
+ for result in results:
+ printNameAndReturnCode(result[0], result[1])
+ printFrameLine()
+
+ responseCodes = [200, 201]
+
+ if(updateversion == 'false'):
+ responseCodes = [200, 201, 409]
+
+ failedResults = filter(lambda x: x[1] == None or x[1] not in responseCodes, results)
+ if (len(failedResults) > 0):
+ errorAndExit(1, None)
+
+def main(argv):
+ print 'Number of arguments:', len(sys.argv), 'arguments.'
+
+ beHost = 'localhost'
+ bePort = '8080'
+ adminUser = 'jh0003'
+ debugf = None
+ updateversion = 'true'
+ importCommon.debugFlag = False
+
+ try:
+ opts, args = getopt.getopt(argv,"i:p:u:d:v:h",["ip=","port=","user=","debug=","updateversion="])
+ except getopt.GetoptError:
+ usage()
+ errorAndExit(2, 'Invalid input')
+
+ for opt, arg in opts:
+ #print opt, arg
+ if opt == '-h':
+ usage()
+ sys.exit(3)
+ elif opt in ("-i", "--ip"):
+ beHost = arg
+ elif opt in ("-p", "--port"):
+ bePort = arg
+ elif opt in ("-u", "--user"):
+ adminUser = arg
+ elif opt in ("-d", "--debug"):
+ print arg
+ debugf = bool(arg.lower() == "true" or arg.lower() == "yes")
+ elif opt in ("-v", "--updateversion"):
+ print arg
+ if (arg.lower() == "false" or arg.lower() == "no"):
+ updateversion = 'false'
+
+ print 'be host =',beHost,', be port =', bePort,', user =', adminUser, ', debug =', debugf, ', updateversion =', updateversion
+
+ if (debugf != None):
+ print 'set debug mode to ' + str(debugf)
+ importCommon.debugFlag = debugf
+
+ if ( beHost == None ):
+ usage()
+ sys.exit(3)
+
+ print sys.argv[0]
+ pathdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+ debug("path dir =" + pathdir)
+
+ baseFileLocation = pathdir + "/../../../import/tosca/"
+
+ fileLocation = baseFileLocation + "normative-types/"
+ results = importNormativeTypes(beHost, bePort, adminUser, fileLocation, updateversion)
+ handleResults(results, updateversion)
+
+ fileLocation = baseFileLocation + "heat-types/"
+ resultsHeat = importHeatTypes(beHost, bePort, adminUser, fileLocation, updateversion)
+ handleResults(resultsHeat, updateversion)
+
+ errorAndExit(0, None)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
+
diff --git a/catalog-be/src/main/resources/swagger/css/print.css b/catalog-be/src/main/resources/swagger/css/print.css
new file mode 100644
index 0000000000..b4fc18036e
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/css/print.css
@@ -0,0 +1,1155 @@
+/* Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org> */
+.swagger-section pre code {
+ display: block;
+ padding: 0.5em;
+ background: #F0F0F0;
+}
+.swagger-section pre code,
+.swagger-section pre .subst,
+.swagger-section pre .tag .title,
+.swagger-section pre .lisp .title,
+.swagger-section pre .clojure .built_in,
+.swagger-section pre .nginx .title {
+ color: black;
+}
+.swagger-section pre .string,
+.swagger-section pre .title,
+.swagger-section pre .constant,
+.swagger-section pre .parent,
+.swagger-section pre .tag .value,
+.swagger-section pre .rules .value,
+.swagger-section pre .rules .value .number,
+.swagger-section pre .preprocessor,
+.swagger-section pre .ruby .symbol,
+.swagger-section pre .ruby .symbol .string,
+.swagger-section pre .aggregate,
+.swagger-section pre .template_tag,
+.swagger-section pre .django .variable,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .addition,
+.swagger-section pre .flow,
+.swagger-section pre .stream,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .apache .cbracket,
+.swagger-section pre .tex .command,
+.swagger-section pre .tex .special,
+.swagger-section pre .erlang_repl .function_or_atom,
+.swagger-section pre .markdown .header {
+ color: #800;
+}
+.swagger-section pre .comment,
+.swagger-section pre .annotation,
+.swagger-section pre .template_comment,
+.swagger-section pre .diff .header,
+.swagger-section pre .chunk,
+.swagger-section pre .markdown .blockquote {
+ color: #888;
+}
+.swagger-section pre .number,
+.swagger-section pre .date,
+.swagger-section pre .regexp,
+.swagger-section pre .literal,
+.swagger-section pre .smalltalk .symbol,
+.swagger-section pre .smalltalk .char,
+.swagger-section pre .go .constant,
+.swagger-section pre .change,
+.swagger-section pre .markdown .bullet,
+.swagger-section pre .markdown .link_url {
+ color: #080;
+}
+.swagger-section pre .label,
+.swagger-section pre .javadoc,
+.swagger-section pre .ruby .string,
+.swagger-section pre .decorator,
+.swagger-section pre .filter .argument,
+.swagger-section pre .localvars,
+.swagger-section pre .array,
+.swagger-section pre .attr_selector,
+.swagger-section pre .important,
+.swagger-section pre .pseudo,
+.swagger-section pre .pi,
+.swagger-section pre .doctype,
+.swagger-section pre .deletion,
+.swagger-section pre .envvar,
+.swagger-section pre .shebang,
+.swagger-section pre .apache .sqbracket,
+.swagger-section pre .nginx .built_in,
+.swagger-section pre .tex .formula,
+.swagger-section pre .erlang_repl .reserved,
+.swagger-section pre .prompt,
+.swagger-section pre .markdown .link_label,
+.swagger-section pre .vhdl .attribute,
+.swagger-section pre .clojure .attribute,
+.swagger-section pre .coffeescript .property {
+ color: #8888ff;
+}
+.swagger-section pre .keyword,
+.swagger-section pre .id,
+.swagger-section pre .phpdoc,
+.swagger-section pre .title,
+.swagger-section pre .built_in,
+.swagger-section pre .aggregate,
+.swagger-section pre .css .tag,
+.swagger-section pre .javadoctag,
+.swagger-section pre .phpdoc,
+.swagger-section pre .yardoctag,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .winutils,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .go .typename,
+.swagger-section pre .tex .command,
+.swagger-section pre .markdown .strong,
+.swagger-section pre .request,
+.swagger-section pre .status {
+ font-weight: bold;
+}
+.swagger-section pre .markdown .emphasis {
+ font-style: italic;
+}
+.swagger-section pre .nginx .built_in {
+ font-weight: normal;
+}
+.swagger-section pre .coffeescript .javascript,
+.swagger-section pre .javascript .xml,
+.swagger-section pre .tex .formula,
+.swagger-section pre .xml .javascript,
+.swagger-section pre .xml .vbscript,
+.swagger-section pre .xml .css,
+.swagger-section pre .xml .cdata {
+ opacity: 0.5;
+}
+.swagger-section .swagger-ui-wrap {
+ line-height: 1;
+ font-family: "Droid Sans", sans-serif;
+ max-width: 960px;
+ margin-left: auto;
+ margin-right: auto;
+}
+.swagger-section .swagger-ui-wrap b,
+.swagger-section .swagger-ui-wrap strong {
+ font-family: "Droid Sans", sans-serif;
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap q,
+.swagger-section .swagger-ui-wrap blockquote {
+ quotes: none;
+}
+.swagger-section .swagger-ui-wrap p {
+ line-height: 1.4em;
+ padding: 0 0 10px;
+ color: #333333;
+}
+.swagger-section .swagger-ui-wrap q:before,
+.swagger-section .swagger-ui-wrap q:after,
+.swagger-section .swagger-ui-wrap blockquote:before,
+.swagger-section .swagger-ui-wrap blockquote:after {
+ content: none;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu h1,
+.swagger-section .swagger-ui-wrap .heading_with_menu h2,
+.swagger-section .swagger-ui-wrap .heading_with_menu h3,
+.swagger-section .swagger-ui-wrap .heading_with_menu h4,
+.swagger-section .swagger-ui-wrap .heading_with_menu h5,
+.swagger-section .swagger-ui-wrap .heading_with_menu h6 {
+ display: block;
+ clear: none;
+ float: left;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 60%;
+}
+.swagger-section .swagger-ui-wrap table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+.swagger-section .swagger-ui-wrap table thead tr th {
+ padding: 5px;
+ font-size: 0.9em;
+ color: #666666;
+ border-bottom: 1px solid #999999;
+}
+.swagger-section .swagger-ui-wrap table tbody tr:last-child td {
+ border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap table tbody tr.offset {
+ background-color: #f0f0f0;
+}
+.swagger-section .swagger-ui-wrap table tbody tr td {
+ padding: 6px;
+ font-size: 0.9em;
+ border-bottom: 1px solid #cccccc;
+ vertical-align: top;
+ line-height: 1.3em;
+}
+.swagger-section .swagger-ui-wrap ol {
+ margin: 0px 0 10px;
+ padding: 0 0 0 18px;
+ list-style-type: decimal;
+}
+.swagger-section .swagger-ui-wrap ol li {
+ padding: 5px 0px;
+ font-size: 0.9em;
+ color: #333333;
+}
+.swagger-section .swagger-ui-wrap ol,
+.swagger-section .swagger-ui-wrap ul {
+ list-style: none;
+}
+.swagger-section .swagger-ui-wrap h1 a,
+.swagger-section .swagger-ui-wrap h2 a,
+.swagger-section .swagger-ui-wrap h3 a,
+.swagger-section .swagger-ui-wrap h4 a,
+.swagger-section .swagger-ui-wrap h5 a,
+.swagger-section .swagger-ui-wrap h6 a {
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap h1 a:hover,
+.swagger-section .swagger-ui-wrap h2 a:hover,
+.swagger-section .swagger-ui-wrap h3 a:hover,
+.swagger-section .swagger-ui-wrap h4 a:hover,
+.swagger-section .swagger-ui-wrap h5 a:hover,
+.swagger-section .swagger-ui-wrap h6 a:hover {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap h1 span.divider,
+.swagger-section .swagger-ui-wrap h2 span.divider,
+.swagger-section .swagger-ui-wrap h3 span.divider,
+.swagger-section .swagger-ui-wrap h4 span.divider,
+.swagger-section .swagger-ui-wrap h5 span.divider,
+.swagger-section .swagger-ui-wrap h6 span.divider {
+ color: #aaaaaa;
+}
+.swagger-section .swagger-ui-wrap a {
+ color: #547f00;
+}
+.swagger-section .swagger-ui-wrap a img {
+ border: none;
+}
+.swagger-section .swagger-ui-wrap article,
+.swagger-section .swagger-ui-wrap aside,
+.swagger-section .swagger-ui-wrap details,
+.swagger-section .swagger-ui-wrap figcaption,
+.swagger-section .swagger-ui-wrap figure,
+.swagger-section .swagger-ui-wrap footer,
+.swagger-section .swagger-ui-wrap header,
+.swagger-section .swagger-ui-wrap hgroup,
+.swagger-section .swagger-ui-wrap menu,
+.swagger-section .swagger-ui-wrap nav,
+.swagger-section .swagger-ui-wrap section,
+.swagger-section .swagger-ui-wrap summary {
+ display: block;
+}
+.swagger-section .swagger-ui-wrap pre {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ background-color: #fcf6db;
+ border: 1px solid #e5e0c6;
+ padding: 10px;
+}
+.swagger-section .swagger-ui-wrap pre code {
+ line-height: 1.6em;
+ background: none;
+}
+.swagger-section .swagger-ui-wrap .content > .content-type > div > label {
+ clear: both;
+ display: block;
+ color: #0F6AB4;
+ font-size: 1.1em;
+ margin: 0;
+ padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap .content pre {
+ font-size: 12px;
+ margin-top: 5px;
+ padding: 5px;
+}
+.swagger-section .swagger-ui-wrap .icon-btn {
+ cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .info_title {
+ padding-bottom: 10px;
+ font-weight: bold;
+ font-size: 25px;
+}
+.swagger-section .swagger-ui-wrap p.big,
+.swagger-section .swagger-ui-wrap div.big p {
+ font-size: 1em;
+ margin-bottom: 10px;
+}
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input {
+ width: 500px !important;
+}
+.swagger-section .swagger-ui-wrap .info_license {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_tos {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .message-fail {
+ color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap .info_url {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_email {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_name {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_description {
+ padding-bottom: 10px;
+ font-size: 15px;
+}
+.swagger-section .swagger-ui-wrap .markdown ol li,
+.swagger-section .swagger-ui-wrap .markdown ul li {
+ padding: 3px 0px;
+ line-height: 1.4em;
+ color: #333333;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input {
+ display: block;
+ padding: 4px;
+ width: auto;
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title {
+ font-size: 1.3em;
+}
+.swagger-section .swagger-ui-wrap table.fullwidth {
+ width: 100%;
+}
+.swagger-section .swagger-ui-wrap .model-signature {
+ font-family: "Droid Sans", sans-serif;
+ font-size: 1em;
+ line-height: 1.5em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a {
+ text-decoration: none;
+ color: #AAA;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover {
+ text-decoration: underline;
+ color: black;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected {
+ color: black;
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propType {
+ color: #5555aa;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre:hover {
+ background-color: #ffffdd;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+ font-size: .85em;
+ line-height: 1.2em;
+ overflow: auto;
+ max-height: 200px;
+ cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child {
+ padding-right: 0;
+ border-right: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li {
+ float: left;
+ margin: 0 5px 5px 0;
+ padding: 2px 5px 2px 0;
+ border-right: 1px solid #ddd;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOpt {
+ color: #555;
+}
+.swagger-section .swagger-ui-wrap .model-signature .snippet small {
+ font-size: 0.75em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOptKey {
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .strong {
+ font-weight: bold;
+ color: #000;
+ font-size: .9em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description div {
+ font-size: 0.9em;
+ line-height: 1.5em;
+ margin-left: 1em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .stronger {
+ font-weight: bold;
+ color: #000;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper {
+ border-spacing: 0;
+ position: absolute;
+ background-color: #ffffff;
+ border: 1px solid #bbbbbb;
+ display: none;
+ font-size: 11px;
+ max-width: 400px;
+ line-height: 30px;
+ color: black;
+ padding: 5px;
+ margin-left: 10px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th {
+ text-align: center;
+ background-color: #eeeeee;
+ border: 1px solid #bbbbbb;
+ font-size: 11px;
+ color: #666666;
+ font-weight: bold;
+ padding: 5px;
+ line-height: 15px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName {
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propName {
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-container {
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+ width: 300px;
+ height: 100px;
+ border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap .markdown p code,
+.swagger-section .swagger-ui-wrap .markdown li code {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ background-color: #f0f0f0;
+ color: black;
+ padding: 1px 3px;
+}
+.swagger-section .swagger-ui-wrap .required {
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+ width: 300px;
+ border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap h1 {
+ color: black;
+ font-size: 1.5em;
+ line-height: 1.3em;
+ padding: 10px 0 10px 0;
+ font-family: "Droid Sans", sans-serif;
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu ul {
+ display: block;
+ clear: none;
+ float: right;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ margin-top: 10px;
+}
+.swagger-section .swagger-ui-wrap h2 {
+ color: black;
+ font-size: 1.3em;
+ padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap h2 a {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub {
+ font-size: 0.7em;
+ color: #999999;
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub a {
+ color: #777777;
+}
+.swagger-section .swagger-ui-wrap span.weak {
+ color: #666666;
+}
+.swagger-section .swagger-ui-wrap .message-success {
+ color: #89BF04;
+}
+.swagger-section .swagger-ui-wrap caption,
+.swagger-section .swagger-ui-wrap th,
+.swagger-section .swagger-ui-wrap td {
+ text-align: left;
+ font-weight: normal;
+ vertical-align: middle;
+}
+.swagger-section .swagger-ui-wrap .code {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea {
+ font-family: "Droid Sans", sans-serif;
+ height: 250px;
+ padding: 4px;
+ display: block;
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select {
+ display: block;
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label {
+ display: block;
+ float: left;
+ clear: none;
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input {
+ display: block;
+ float: left;
+ clear: none;
+ margin: 0 5px 0 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label {
+ display: block;
+ clear: both;
+ width: auto;
+ padding: 0 0 3px;
+ color: #666666;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr {
+ padding-left: 3px;
+ color: #888888;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints {
+ margin-left: 0;
+ font-style: italic;
+ font-size: 0.9em;
+ margin: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons {
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap span.blank,
+.swagger-section .swagger-ui-wrap span.empty {
+ color: #888888;
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .markdown h3 {
+ color: #547f00;
+}
+.swagger-section .swagger-ui-wrap .markdown h4 {
+ color: #666666;
+}
+.swagger-section .swagger-ui-wrap .markdown pre {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ background-color: #fcf6db;
+ border: 1px solid #e5e0c6;
+ padding: 10px;
+ margin: 0 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown pre code {
+ line-height: 1.6em;
+}
+.swagger-section .swagger-ui-wrap div.gist {
+ margin: 20px 0 25px 0 !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources {
+ font-family: "Droid Sans", sans-serif;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource {
+ border-bottom: 1px solid #dddddd;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a {
+ color: #555555;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child {
+ border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading {
+ border: 1px solid transparent;
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+ overflow: hidden;
+ padding: 0;
+ display: block;
+ clear: none;
+ float: right;
+ margin: 14px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li {
+ float: left;
+ clear: none;
+ margin: 0;
+ padding: 2px 10px;
+ border-right: 1px solid #dddddd;
+ color: #666666;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a {
+ color: #aaaaaa;
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover {
+ text-decoration: underline;
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last {
+ padding-right: 0;
+ border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 {
+ color: #999999;
+ padding-left: 0;
+ display: block;
+ clear: none;
+ float: left;
+ font-family: "Droid Sans", sans-serif;
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a {
+ color: #999999;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+ margin: 0 0 10px;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 {
+ display: block;
+ clear: none;
+ float: left;
+ width: auto;
+ margin: 0;
+ padding: 0;
+ line-height: 1.1em;
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path {
+ padding-left: 10px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a {
+ color: black;
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a {
+ text-transform: uppercase;
+ text-decoration: none;
+ color: white;
+ display: inline-block;
+ width: 50px;
+ font-size: 0.7em;
+ text-align: center;
+ padding: 7px 0 4px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ -o-border-radius: 2px;
+ -ms-border-radius: 2px;
+ -khtml-border-radius: 2px;
+ border-radius: 2px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span {
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options {
+ overflow: hidden;
+ padding: 0;
+ display: block;
+ clear: none;
+ float: right;
+ margin: 6px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li {
+ float: left;
+ clear: none;
+ margin: 0;
+ padding: 2px 10px;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+ border-top: none;
+ padding: 10px;
+ -moz-border-radius-bottomleft: 6px;
+ -webkit-border-bottom-left-radius: 6px;
+ -o-border-bottom-left-radius: 6px;
+ -ms-border-bottom-left-radius: 6px;
+ -khtml-border-bottom-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ -moz-border-radius-bottomright: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ -o-border-bottom-right-radius: 6px;
+ -ms-border-bottom-right-radius: 6px;
+ -khtml-border-bottom-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+ margin: 0 0 20px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 {
+ font-size: 1.1em;
+ margin: 0;
+ padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a {
+ padding: 4px 0 0 10px;
+ display: inline-block;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit {
+ display: block;
+ clear: none;
+ float: left;
+ padding: 6px 8px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
+ background-image: url('../images/throbber.gif');
+ width: 128px;
+ height: 16px;
+ display: block;
+ clear: none;
+ float: right;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error {
+ outline: 2px solid black;
+ outline-color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ padding: 10px;
+ font-size: 0.9em;
+ max-height: 400px;
+ overflow-y: auto;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading {
+ background-color: #f9f2e9;
+ border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a {
+ background-color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #f0e0ca;
+ color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a {
+ color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content {
+ background-color: #faf5ee;
+ border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 {
+ color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a {
+ color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading {
+ background-color: #fcffcd;
+ border: 1px solid black;
+ border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a {
+ text-transform: uppercase;
+ background-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #ffd20f;
+ color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a {
+ color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content {
+ background-color: #fcffcd;
+ border: 1px solid black;
+ border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 {
+ color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a {
+ color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading {
+ background-color: #f5e8e8;
+ border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a {
+ text-transform: uppercase;
+ background-color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #e8c6c7;
+ color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a {
+ color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+ background-color: #f7eded;
+ border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 {
+ color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a {
+ color: #c8787a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading {
+ background-color: #e7f6ec;
+ border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a {
+ background-color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #c3e8d1;
+ color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a {
+ color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content {
+ background-color: #ebf7f0;
+ border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 {
+ color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a {
+ color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading {
+ background-color: #FCE9E3;
+ border: 1px solid #F5D5C3;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a {
+ background-color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #f0cecb;
+ color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a {
+ color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content {
+ background-color: #faf0ef;
+ border: 1px solid #f0cecb;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 {
+ color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a {
+ color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading {
+ background-color: #e7f0f7;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a {
+ background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #c3d9ec;
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content {
+ background-color: #ebf3f9;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a {
+ color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading {
+ background-color: #e7f0f7;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a {
+ background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #c3d9ec;
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content {
+ background-color: #ebf3f9;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a {
+ color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+ border-top: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last {
+ padding-right: 0;
+ border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap p#colophon {
+ margin: 0 15px 40px 15px;
+ padding: 10px 0;
+ font-size: 0.8em;
+ border-top: 1px solid #dddddd;
+ font-family: "Droid Sans", sans-serif;
+ color: #999999;
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap p#colophon a {
+ text-decoration: none;
+ color: #547f00;
+}
+.swagger-section .swagger-ui-wrap h3 {
+ color: black;
+ font-size: 1.1em;
+ padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown ol,
+.swagger-section .swagger-ui-wrap .markdown ul {
+ font-family: "Droid Sans", sans-serif;
+ margin: 5px 0 10px;
+ padding: 0 0 0 18px;
+ list-style-type: disc;
+}
+.swagger-section .swagger-ui-wrap form.form_box {
+ background-color: #ebf3f9;
+ border: 1px solid #c3d9ec;
+ padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box label {
+ color: #0f6ab4 !important;
+}
+.swagger-section .swagger-ui-wrap form.form_box input[type=submit] {
+ display: block;
+ padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box p.weak {
+ font-size: 0.8em;
+}
+.swagger-section .swagger-ui-wrap form.form_box p {
+ font-size: 0.9em;
+ padding: 0 0 15px;
+ color: #7e7b6d;
+}
+.swagger-section .swagger-ui-wrap form.form_box p a {
+ color: #646257;
+}
+.swagger-section .swagger-ui-wrap form.form_box p strong {
+ color: black;
+}
+.swagger-section .title {
+ font-style: bold;
+}
+.swagger-section .secondary_form {
+ display: none;
+}
+.swagger-section .main_image {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+.swagger-section .oauth_body {
+ margin-left: 100px;
+ margin-right: 100px;
+}
+.swagger-section .oauth_submit {
+ text-align: center;
+}
+.swagger-section .api-popup-dialog {
+ z-index: 10000;
+ position: absolute;
+ width: 500px;
+ background: #FFF;
+ padding: 20px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ display: none;
+ font-size: 13px;
+ color: #777;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+ font-size: 24px;
+ padding: 10px 0;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+ font-size: 24px;
+ padding: 10px 0;
+}
+.swagger-section .api-popup-dialog p.error-msg {
+ padding-left: 5px;
+ padding-bottom: 5px;
+}
+.swagger-section .api-popup-dialog button.api-popup-authbtn {
+ height: 30px;
+}
+.swagger-section .api-popup-dialog button.api-popup-cancel {
+ height: 30px;
+}
+.swagger-section .api-popup-scopes {
+ padding: 10px 20px;
+}
+.swagger-section .api-popup-scopes li {
+ padding: 5px 0;
+ line-height: 20px;
+}
+.swagger-section .api-popup-scopes .api-scope-desc {
+ padding-left: 20px;
+ font-style: italic;
+}
+.swagger-section .api-popup-scopes li input {
+ position: relative;
+ top: 2px;
+}
+.swagger-section .api-popup-actions {
+ padding-top: 10px;
+}
+#header {
+ display: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+ max-height: none;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+ width: 100px;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+ width: 100px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+ display: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints {
+ display: block !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+ display: block !important;
+}
diff --git a/catalog-be/src/main/resources/swagger/css/reset.css b/catalog-be/src/main/resources/swagger/css/reset.css
new file mode 100644
index 0000000000..b2b078943c
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/css/reset.css
@@ -0,0 +1,125 @@
+/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol,
+ul {
+ list-style: none;
+}
+blockquote,
+q {
+ quotes: none;
+}
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
diff --git a/catalog-be/src/main/resources/swagger/css/screen.css b/catalog-be/src/main/resources/swagger/css/screen.css
new file mode 100644
index 0000000000..32b199b17b
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/css/screen.css
@@ -0,0 +1,1256 @@
+/* Original style from softwaremaniacs.org (c) Ivan Sagalaev <Maniac@SoftwareManiacs.Org> */
+.swagger-section pre code {
+ display: block;
+ padding: 0.5em;
+ background: #F0F0F0;
+}
+.swagger-section pre code,
+.swagger-section pre .subst,
+.swagger-section pre .tag .title,
+.swagger-section pre .lisp .title,
+.swagger-section pre .clojure .built_in,
+.swagger-section pre .nginx .title {
+ color: black;
+}
+.swagger-section pre .string,
+.swagger-section pre .title,
+.swagger-section pre .constant,
+.swagger-section pre .parent,
+.swagger-section pre .tag .value,
+.swagger-section pre .rules .value,
+.swagger-section pre .rules .value .number,
+.swagger-section pre .preprocessor,
+.swagger-section pre .ruby .symbol,
+.swagger-section pre .ruby .symbol .string,
+.swagger-section pre .aggregate,
+.swagger-section pre .template_tag,
+.swagger-section pre .django .variable,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .addition,
+.swagger-section pre .flow,
+.swagger-section pre .stream,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .apache .cbracket,
+.swagger-section pre .tex .command,
+.swagger-section pre .tex .special,
+.swagger-section pre .erlang_repl .function_or_atom,
+.swagger-section pre .markdown .header {
+ color: #800;
+}
+.swagger-section pre .comment,
+.swagger-section pre .annotation,
+.swagger-section pre .template_comment,
+.swagger-section pre .diff .header,
+.swagger-section pre .chunk,
+.swagger-section pre .markdown .blockquote {
+ color: #888;
+}
+.swagger-section pre .number,
+.swagger-section pre .date,
+.swagger-section pre .regexp,
+.swagger-section pre .literal,
+.swagger-section pre .smalltalk .symbol,
+.swagger-section pre .smalltalk .char,
+.swagger-section pre .go .constant,
+.swagger-section pre .change,
+.swagger-section pre .markdown .bullet,
+.swagger-section pre .markdown .link_url {
+ color: #080;
+}
+.swagger-section pre .label,
+.swagger-section pre .javadoc,
+.swagger-section pre .ruby .string,
+.swagger-section pre .decorator,
+.swagger-section pre .filter .argument,
+.swagger-section pre .localvars,
+.swagger-section pre .array,
+.swagger-section pre .attr_selector,
+.swagger-section pre .important,
+.swagger-section pre .pseudo,
+.swagger-section pre .pi,
+.swagger-section pre .doctype,
+.swagger-section pre .deletion,
+.swagger-section pre .envvar,
+.swagger-section pre .shebang,
+.swagger-section pre .apache .sqbracket,
+.swagger-section pre .nginx .built_in,
+.swagger-section pre .tex .formula,
+.swagger-section pre .erlang_repl .reserved,
+.swagger-section pre .prompt,
+.swagger-section pre .markdown .link_label,
+.swagger-section pre .vhdl .attribute,
+.swagger-section pre .clojure .attribute,
+.swagger-section pre .coffeescript .property {
+ color: #8888ff;
+}
+.swagger-section pre .keyword,
+.swagger-section pre .id,
+.swagger-section pre .phpdoc,
+.swagger-section pre .title,
+.swagger-section pre .built_in,
+.swagger-section pre .aggregate,
+.swagger-section pre .css .tag,
+.swagger-section pre .javadoctag,
+.swagger-section pre .phpdoc,
+.swagger-section pre .yardoctag,
+.swagger-section pre .smalltalk .class,
+.swagger-section pre .winutils,
+.swagger-section pre .bash .variable,
+.swagger-section pre .apache .tag,
+.swagger-section pre .go .typename,
+.swagger-section pre .tex .command,
+.swagger-section pre .markdown .strong,
+.swagger-section pre .request,
+.swagger-section pre .status {
+ font-weight: bold;
+}
+.swagger-section pre .markdown .emphasis {
+ font-style: italic;
+}
+.swagger-section pre .nginx .built_in {
+ font-weight: normal;
+}
+.swagger-section pre .coffeescript .javascript,
+.swagger-section pre .javascript .xml,
+.swagger-section pre .tex .formula,
+.swagger-section pre .xml .javascript,
+.swagger-section pre .xml .vbscript,
+.swagger-section pre .xml .css,
+.swagger-section pre .xml .cdata {
+ opacity: 0.5;
+}
+.swagger-section .swagger-ui-wrap {
+ line-height: 1;
+ font-family: "Droid Sans", sans-serif;
+ max-width: 960px;
+ margin-left: auto;
+ margin-right: auto;
+}
+.swagger-section .swagger-ui-wrap b,
+.swagger-section .swagger-ui-wrap strong {
+ font-family: "Droid Sans", sans-serif;
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap q,
+.swagger-section .swagger-ui-wrap blockquote {
+ quotes: none;
+}
+.swagger-section .swagger-ui-wrap p {
+ line-height: 1.4em;
+ padding: 0 0 10px;
+ color: #333333;
+}
+.swagger-section .swagger-ui-wrap q:before,
+.swagger-section .swagger-ui-wrap q:after,
+.swagger-section .swagger-ui-wrap blockquote:before,
+.swagger-section .swagger-ui-wrap blockquote:after {
+ content: none;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu h1,
+.swagger-section .swagger-ui-wrap .heading_with_menu h2,
+.swagger-section .swagger-ui-wrap .heading_with_menu h3,
+.swagger-section .swagger-ui-wrap .heading_with_menu h4,
+.swagger-section .swagger-ui-wrap .heading_with_menu h5,
+.swagger-section .swagger-ui-wrap .heading_with_menu h6 {
+ display: block;
+ clear: none;
+ float: left;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 60%;
+}
+.swagger-section .swagger-ui-wrap table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+.swagger-section .swagger-ui-wrap table thead tr th {
+ padding: 5px;
+ font-size: 0.9em;
+ color: #666666;
+ border-bottom: 1px solid #999999;
+}
+.swagger-section .swagger-ui-wrap table tbody tr:last-child td {
+ border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap table tbody tr.offset {
+ background-color: #f0f0f0;
+}
+.swagger-section .swagger-ui-wrap table tbody tr td {
+ padding: 6px;
+ font-size: 0.9em;
+ border-bottom: 1px solid #cccccc;
+ vertical-align: top;
+ line-height: 1.3em;
+}
+.swagger-section .swagger-ui-wrap ol {
+ margin: 0px 0 10px;
+ padding: 0 0 0 18px;
+ list-style-type: decimal;
+}
+.swagger-section .swagger-ui-wrap ol li {
+ padding: 5px 0px;
+ font-size: 0.9em;
+ color: #333333;
+}
+.swagger-section .swagger-ui-wrap ol,
+.swagger-section .swagger-ui-wrap ul {
+ list-style: none;
+}
+.swagger-section .swagger-ui-wrap h1 a,
+.swagger-section .swagger-ui-wrap h2 a,
+.swagger-section .swagger-ui-wrap h3 a,
+.swagger-section .swagger-ui-wrap h4 a,
+.swagger-section .swagger-ui-wrap h5 a,
+.swagger-section .swagger-ui-wrap h6 a {
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap h1 a:hover,
+.swagger-section .swagger-ui-wrap h2 a:hover,
+.swagger-section .swagger-ui-wrap h3 a:hover,
+.swagger-section .swagger-ui-wrap h4 a:hover,
+.swagger-section .swagger-ui-wrap h5 a:hover,
+.swagger-section .swagger-ui-wrap h6 a:hover {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap h1 span.divider,
+.swagger-section .swagger-ui-wrap h2 span.divider,
+.swagger-section .swagger-ui-wrap h3 span.divider,
+.swagger-section .swagger-ui-wrap h4 span.divider,
+.swagger-section .swagger-ui-wrap h5 span.divider,
+.swagger-section .swagger-ui-wrap h6 span.divider {
+ color: #aaaaaa;
+}
+.swagger-section .swagger-ui-wrap a {
+ color: #547f00;
+}
+.swagger-section .swagger-ui-wrap a img {
+ border: none;
+}
+.swagger-section .swagger-ui-wrap article,
+.swagger-section .swagger-ui-wrap aside,
+.swagger-section .swagger-ui-wrap details,
+.swagger-section .swagger-ui-wrap figcaption,
+.swagger-section .swagger-ui-wrap figure,
+.swagger-section .swagger-ui-wrap footer,
+.swagger-section .swagger-ui-wrap header,
+.swagger-section .swagger-ui-wrap hgroup,
+.swagger-section .swagger-ui-wrap menu,
+.swagger-section .swagger-ui-wrap nav,
+.swagger-section .swagger-ui-wrap section,
+.swagger-section .swagger-ui-wrap summary {
+ display: block;
+}
+.swagger-section .swagger-ui-wrap pre {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ background-color: #fcf6db;
+ border: 1px solid #e5e0c6;
+ padding: 10px;
+}
+.swagger-section .swagger-ui-wrap pre code {
+ line-height: 1.6em;
+ background: none;
+}
+.swagger-section .swagger-ui-wrap .content > .content-type > div > label {
+ clear: both;
+ display: block;
+ color: #0F6AB4;
+ font-size: 1.1em;
+ margin: 0;
+ padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap .content pre {
+ font-size: 12px;
+ margin-top: 5px;
+ padding: 5px;
+}
+.swagger-section .swagger-ui-wrap .icon-btn {
+ cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .info_title {
+ padding-bottom: 10px;
+ font-weight: bold;
+ font-size: 25px;
+}
+.swagger-section .swagger-ui-wrap p.big,
+.swagger-section .swagger-ui-wrap div.big p {
+ font-size: 1em;
+ margin-bottom: 10px;
+}
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.string input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.url input,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.text textarea,
+.swagger-section .swagger-ui-wrap form.fullwidth ol li.numeric input {
+ width: 500px !important;
+}
+.swagger-section .swagger-ui-wrap .info_license {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_tos {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .message-fail {
+ color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap .info_url {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_email {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_name {
+ padding-bottom: 5px;
+}
+.swagger-section .swagger-ui-wrap .info_description {
+ padding-bottom: 10px;
+ font-size: 15px;
+}
+.swagger-section .swagger-ui-wrap .markdown ol li,
+.swagger-section .swagger-ui-wrap .markdown ul li {
+ padding: 3px 0px;
+ line-height: 1.4em;
+ color: #333333;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input {
+ display: block;
+ padding: 4px;
+ width: auto;
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.string input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.url input.title,
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.numeric input.title {
+ font-size: 1.3em;
+}
+.swagger-section .swagger-ui-wrap table.fullwidth {
+ width: 100%;
+}
+.swagger-section .swagger-ui-wrap .model-signature {
+ font-family: "Droid Sans", sans-serif;
+ font-size: 1em;
+ line-height: 1.5em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a {
+ text-decoration: none;
+ color: #AAA;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav a:hover {
+ text-decoration: underline;
+ color: black;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-nav .selected {
+ color: black;
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propType {
+ color: #5555aa;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre:hover {
+ background-color: #ffffdd;
+}
+.swagger-section .swagger-ui-wrap .model-signature pre {
+ font-size: .85em;
+ line-height: 1.2em;
+ overflow: auto;
+ max-height: 200px;
+ cursor: pointer;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav {
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li:last-child {
+ padding-right: 0;
+ border-right: none;
+}
+.swagger-section .swagger-ui-wrap .model-signature ul.signature-nav li {
+ float: left;
+ margin: 0 5px 5px 0;
+ padding: 2px 5px 2px 0;
+ border-right: 1px solid #ddd;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOpt {
+ color: #555;
+}
+.swagger-section .swagger-ui-wrap .model-signature .snippet small {
+ font-size: 0.75em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propOptKey {
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .strong {
+ font-weight: bold;
+ color: #000;
+ font-size: .9em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description div {
+ font-size: 0.9em;
+ line-height: 1.5em;
+ margin-left: 1em;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .stronger {
+ font-weight: bold;
+ color: #000;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper {
+ border-spacing: 0;
+ position: absolute;
+ background-color: #ffffff;
+ border: 1px solid #bbbbbb;
+ display: none;
+ font-size: 11px;
+ max-width: 400px;
+ line-height: 30px;
+ color: black;
+ padding: 5px;
+ margin-left: 10px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper th {
+ text-align: center;
+ background-color: #eeeeee;
+ border: 1px solid #bbbbbb;
+ font-size: 11px;
+ color: #666666;
+ font-weight: bold;
+ padding: 5px;
+ line-height: 15px;
+}
+.swagger-section .swagger-ui-wrap .model-signature .description .propWrap .optionsWrapper .optionName {
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .propName {
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .model-signature .signature-container {
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap .body-textarea {
+ width: 300px;
+ height: 100px;
+ border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap .markdown p code,
+.swagger-section .swagger-ui-wrap .markdown li code {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ background-color: #f0f0f0;
+ color: black;
+ padding: 1px 3px;
+}
+.swagger-section .swagger-ui-wrap .required {
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap input.parameter {
+ width: 300px;
+ border: 1px solid #aaa;
+}
+.swagger-section .swagger-ui-wrap h1 {
+ color: black;
+ font-size: 1.5em;
+ line-height: 1.3em;
+ padding: 10px 0 10px 0;
+ font-family: "Droid Sans", sans-serif;
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap .heading_with_menu ul {
+ display: block;
+ clear: none;
+ float: right;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ margin-top: 10px;
+}
+.swagger-section .swagger-ui-wrap h2 {
+ color: black;
+ font-size: 1.3em;
+ padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap h2 a {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub {
+ font-size: 0.7em;
+ color: #999999;
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap h2 span.sub a {
+ color: #777777;
+}
+.swagger-section .swagger-ui-wrap span.weak {
+ color: #666666;
+}
+.swagger-section .swagger-ui-wrap .message-success {
+ color: #89BF04;
+}
+.swagger-section .swagger-ui-wrap caption,
+.swagger-section .swagger-ui-wrap th,
+.swagger-section .swagger-ui-wrap td {
+ text-align: left;
+ font-weight: normal;
+ vertical-align: middle;
+}
+.swagger-section .swagger-ui-wrap .code {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.text textarea {
+ font-family: "Droid Sans", sans-serif;
+ height: 250px;
+ padding: 4px;
+ display: block;
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.select select {
+ display: block;
+ clear: both;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean label {
+ display: block;
+ float: left;
+ clear: none;
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.boolean input {
+ display: block;
+ float: left;
+ clear: none;
+ margin: 0 5px 0 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li.required label {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label {
+ display: block;
+ clear: both;
+ width: auto;
+ padding: 0 0 3px;
+ color: #666666;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li label abbr {
+ padding-left: 3px;
+ color: #888888;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.inputs ol li p.inline-hints {
+ margin-left: 0;
+ font-style: italic;
+ font-size: 0.9em;
+ margin: 0;
+}
+.swagger-section .swagger-ui-wrap form.formtastic fieldset.buttons {
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap span.blank,
+.swagger-section .swagger-ui-wrap span.empty {
+ color: #888888;
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap .markdown h3 {
+ color: #547f00;
+}
+.swagger-section .swagger-ui-wrap .markdown h4 {
+ color: #666666;
+}
+.swagger-section .swagger-ui-wrap .markdown pre {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ background-color: #fcf6db;
+ border: 1px solid #e5e0c6;
+ padding: 10px;
+ margin: 0 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown pre code {
+ line-height: 1.6em;
+}
+.swagger-section .swagger-ui-wrap div.gist {
+ margin: 20px 0 25px 0 !important;
+}
+.swagger-section .swagger-ui-wrap ul#resources {
+ font-family: "Droid Sans", sans-serif;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource {
+ border-bottom: 1px solid #dddddd;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading h2 a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading h2 a {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:hover div.heading ul.options li a,
+.swagger-section .swagger-ui-wrap ul#resources li.resource.active div.heading ul.options li a {
+ color: #555555;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource:last-child {
+ border-bottom: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading {
+ border: 1px solid transparent;
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options {
+ overflow: hidden;
+ padding: 0;
+ display: block;
+ clear: none;
+ float: right;
+ margin: 14px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li {
+ float: left;
+ clear: none;
+ margin: 0;
+ padding: 2px 10px;
+ border-right: 1px solid #dddddd;
+ color: #666666;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a {
+ color: #aaaaaa;
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover {
+ text-decoration: underline;
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li a.active {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options li.last {
+ padding-right: 0;
+ border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading ul.options.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 {
+ color: #999999;
+ padding-left: 0;
+ display: block;
+ clear: none;
+ float: left;
+ font-family: "Droid Sans", sans-serif;
+ font-weight: bold;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a {
+ color: #999999;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+ margin: 0 0 10px;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 {
+ display: block;
+ clear: none;
+ float: left;
+ width: auto;
+ margin: 0;
+ padding: 0;
+ line-height: 1.1em;
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path {
+ padding-left: 10px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a {
+ color: black;
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.path a:hover {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span.http_method a {
+ text-transform: uppercase;
+ text-decoration: none;
+ color: white;
+ display: inline-block;
+ width: 50px;
+ font-size: 0.7em;
+ text-align: center;
+ padding: 7px 0 4px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ -o-border-radius: 2px;
+ -ms-border-radius: 2px;
+ -khtml-border-radius: 2px;
+ border-radius: 2px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading h3 span {
+ margin: 0;
+ padding: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options {
+ overflow: hidden;
+ padding: 0;
+ display: block;
+ clear: none;
+ float: right;
+ margin: 6px 10px 0 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li {
+ float: left;
+ clear: none;
+ margin: 0;
+ padding: 2px 10px;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li a {
+ text-decoration: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.heading ul.options li.access {
+ color: black;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content {
+ border-top: none;
+ padding: 10px;
+ -moz-border-radius-bottomleft: 6px;
+ -webkit-border-bottom-left-radius: 6px;
+ -o-border-bottom-left-radius: 6px;
+ -ms-border-bottom-left-radius: 6px;
+ -khtml-border-bottom-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ -moz-border-radius-bottomright: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ -o-border-bottom-right-radius: 6px;
+ -ms-border-bottom-right-radius: 6px;
+ -khtml-border-bottom-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+ margin: 0 0 20px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content h4 {
+ font-size: 1.1em;
+ margin: 0;
+ padding: 15px 0 5px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header {
+ float: none;
+ clear: both;
+ overflow: hidden;
+ display: block;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header a {
+ padding: 4px 0 0 10px;
+ display: inline-block;
+ font-size: 0.9em;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header input.submit {
+ display: block;
+ clear: none;
+ float: left;
+ padding: 6px 8px;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.sandbox_header span.response_throbber {
+ background-image: url('../images/throbber.gif');
+ width: 128px;
+ height: 16px;
+ display: block;
+ clear: none;
+ float: right;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content form input[type='text'].error {
+ outline: 2px solid black;
+ outline-color: #cc0000;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation div.content div.response div.block pre {
+ font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
+ padding: 10px;
+ font-size: 0.9em;
+ max-height: 400px;
+ overflow-y: auto;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading {
+ background-color: #f9f2e9;
+ border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading h3 span.http_method a {
+ background-color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #f0e0ca;
+ color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li a {
+ color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content {
+ background-color: #faf5ee;
+ border: 1px solid #f0e0ca;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content h4 {
+ color: #c5862b;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content div.sandbox_header a {
+ color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading {
+ background-color: #fcffcd;
+ border: 1px solid black;
+ border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading h3 span.http_method a {
+ text-transform: uppercase;
+ background-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #ffd20f;
+ color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li a {
+ color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content {
+ background-color: #fcffcd;
+ border: 1px solid black;
+ border-color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content h4 {
+ color: #ffd20f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content div.sandbox_header a {
+ color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading {
+ background-color: #f5e8e8;
+ border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading h3 span.http_method a {
+ text-transform: uppercase;
+ background-color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #e8c6c7;
+ color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li a {
+ color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+ background-color: #f7eded;
+ border: 1px solid #e8c6c7;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content h4 {
+ color: #a41e22;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content div.sandbox_header a {
+ color: #c8787a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading {
+ background-color: #e7f6ec;
+ border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading h3 span.http_method a {
+ background-color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #c3e8d1;
+ color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li a {
+ color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content {
+ background-color: #ebf7f0;
+ border: 1px solid #c3e8d1;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content h4 {
+ color: #10a54a;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content div.sandbox_header a {
+ color: #6fc992;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading {
+ background-color: #FCE9E3;
+ border: 1px solid #F5D5C3;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading h3 span.http_method a {
+ background-color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #f0cecb;
+ color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li a {
+ color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content {
+ background-color: #faf0ef;
+ border: 1px solid #f0cecb;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content h4 {
+ color: #D38042;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content div.sandbox_header a {
+ color: #dcb67f;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading {
+ background-color: #e7f0f7;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading h3 span.http_method a {
+ background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #c3d9ec;
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li a {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content {
+ background-color: #ebf3f9;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content h4 {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content div.sandbox_header a {
+ color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading {
+ background-color: #e7f0f7;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading h3 span.http_method a {
+ background-color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li {
+ border-right: 1px solid #dddddd;
+ border-right-color: #c3d9ec;
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.heading ul.options li a {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content {
+ background-color: #ebf3f9;
+ border: 1px solid #c3d9ec;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content h4 {
+ color: #0f6ab4;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.options div.content div.sandbox_header a {
+ color: #6fa5d2;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.content,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.content {
+ border-top: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li:last-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.get div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.post div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.head div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.put div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.patch div.heading ul.options li.last,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operation.delete div.heading ul.options li.last {
+ padding-right: 0;
+ border-right: none;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:hover,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a:active,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li a.active {
+ text-decoration: underline;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations ul.options li.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations:first-child,
+.swagger-section .swagger-ui-wrap ul#resources li.resource ul.endpoints li.endpoint ul.operations.first {
+ padding-left: 0;
+}
+.swagger-section .swagger-ui-wrap p#colophon {
+ margin: 0 15px 40px 15px;
+ padding: 10px 0;
+ font-size: 0.8em;
+ border-top: 1px solid #dddddd;
+ font-family: "Droid Sans", sans-serif;
+ color: #999999;
+ font-style: italic;
+}
+.swagger-section .swagger-ui-wrap p#colophon a {
+ text-decoration: none;
+ color: #547f00;
+}
+.swagger-section .swagger-ui-wrap h3 {
+ color: black;
+ font-size: 1.1em;
+ padding: 10px 0 10px 0;
+}
+.swagger-section .swagger-ui-wrap .markdown ol,
+.swagger-section .swagger-ui-wrap .markdown ul {
+ font-family: "Droid Sans", sans-serif;
+ margin: 5px 0 10px;
+ padding: 0 0 0 18px;
+ list-style-type: disc;
+}
+.swagger-section .swagger-ui-wrap form.form_box {
+ background-color: #ebf3f9;
+ border: 1px solid #c3d9ec;
+ padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box label {
+ color: #0f6ab4 !important;
+}
+.swagger-section .swagger-ui-wrap form.form_box input[type=submit] {
+ display: block;
+ padding: 10px;
+}
+.swagger-section .swagger-ui-wrap form.form_box p.weak {
+ font-size: 0.8em;
+}
+.swagger-section .swagger-ui-wrap form.form_box p {
+ font-size: 0.9em;
+ padding: 0 0 15px;
+ color: #7e7b6d;
+}
+.swagger-section .swagger-ui-wrap form.form_box p a {
+ color: #646257;
+}
+.swagger-section .swagger-ui-wrap form.form_box p strong {
+ color: black;
+}
+.swagger-section .title {
+ font-style: bold;
+}
+.swagger-section .secondary_form {
+ display: none;
+}
+.swagger-section .main_image {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+.swagger-section .oauth_body {
+ margin-left: 100px;
+ margin-right: 100px;
+}
+.swagger-section .oauth_submit {
+ text-align: center;
+}
+.swagger-section .api-popup-dialog {
+ z-index: 10000;
+ position: absolute;
+ width: 500px;
+ background: #FFF;
+ padding: 20px;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ display: none;
+ font-size: 13px;
+ color: #777;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+ font-size: 24px;
+ padding: 10px 0;
+}
+.swagger-section .api-popup-dialog .api-popup-title {
+ font-size: 24px;
+ padding: 10px 0;
+}
+.swagger-section .api-popup-dialog p.error-msg {
+ padding-left: 5px;
+ padding-bottom: 5px;
+}
+.swagger-section .api-popup-dialog button.api-popup-authbtn {
+ height: 30px;
+}
+.swagger-section .api-popup-dialog button.api-popup-cancel {
+ height: 30px;
+}
+.swagger-section .api-popup-scopes {
+ padding: 10px 20px;
+}
+.swagger-section .api-popup-scopes li {
+ padding: 5px 0;
+ line-height: 20px;
+}
+.swagger-section .api-popup-scopes .api-scope-desc {
+ padding-left: 20px;
+ font-style: italic;
+}
+.swagger-section .api-popup-scopes li input {
+ position: relative;
+ top: 2px;
+}
+.swagger-section .api-popup-actions {
+ padding-top: 10px;
+}
+.swagger-section .access {
+ float: right;
+}
+.swagger-section .auth {
+ float: right;
+}
+.swagger-section #api_information_panel {
+ position: absolute;
+ background: #FFF;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ display: none;
+ font-size: 13px;
+ max-width: 300px;
+ line-height: 30px;
+ color: black;
+ padding: 5px;
+}
+.swagger-section #api_information_panel p .api-msg-enabled {
+ color: green;
+}
+.swagger-section #api_information_panel p .api-msg-disabled {
+ color: red;
+}
+.swagger-section .api-ic {
+ height: 18px;
+ vertical-align: middle;
+ display: inline-block;
+ background: url(../images/explorer_icons.png) no-repeat;
+}
+.swagger-section .ic-info {
+ background-position: 0 0;
+ width: 18px;
+ margin-top: -7px;
+ margin-left: 4px;
+}
+.swagger-section .ic-warning {
+ background-position: -60px 0;
+ width: 18px;
+ margin-top: -7px;
+ margin-left: 4px;
+}
+.swagger-section .ic-error {
+ background-position: -30px 0;
+ width: 18px;
+ margin-top: -7px;
+ margin-left: 4px;
+}
+.swagger-section .ic-off {
+ background-position: -90px 0;
+ width: 58px;
+ margin-top: -4px;
+ cursor: pointer;
+}
+.swagger-section .ic-on {
+ background-position: -160px 0;
+ width: 58px;
+ margin-top: -4px;
+ cursor: pointer;
+}
+.swagger-section #header {
+ background-color: #89bf04;
+ padding: 14px;
+}
+.swagger-section #header a#logo {
+ font-size: 1.5em;
+ font-weight: bold;
+ text-decoration: none;
+ background: transparent url(../images/logo_small.png) no-repeat left center;
+ padding: 20px 0 20px 40px;
+ color: white;
+}
+.swagger-section #header form#api_selector {
+ display: block;
+ clear: none;
+ float: right;
+}
+.swagger-section #header form#api_selector .input {
+ display: block;
+ clear: none;
+ float: left;
+ margin: 0 10px 0 0;
+}
+.swagger-section #header form#api_selector .input input#input_apiKey {
+ width: 200px;
+}
+.swagger-section #header form#api_selector .input input#input_baseUrl {
+ width: 400px;
+}
+.swagger-section #header form#api_selector .input a#explore {
+ display: block;
+ text-decoration: none;
+ font-weight: bold;
+ padding: 6px 8px;
+ font-size: 0.9em;
+ color: white;
+ background-color: #547f00;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -o-border-radius: 4px;
+ -ms-border-radius: 4px;
+ -khtml-border-radius: 4px;
+ border-radius: 4px;
+}
+.swagger-section #header form#api_selector .input a#explore:hover {
+ background-color: #547f00;
+}
+.swagger-section #header form#api_selector .input input {
+ font-size: 0.9em;
+ padding: 3px;
+ margin: 0;
+}
+.swagger-section #content_message {
+ margin: 10px 15px;
+ font-style: italic;
+ color: #999999;
+}
+.swagger-section #message-bar {
+ min-height: 30px;
+ text-align: center;
+ padding-top: 10px;
+}
diff --git a/catalog-be/src/main/resources/swagger/css/typography.css b/catalog-be/src/main/resources/swagger/css/typography.css
new file mode 100644
index 0000000000..27c3751ac2
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/css/typography.css
@@ -0,0 +1,26 @@
+/* droid-sans-regular - latin */
+@font-face {
+ font-family: 'Droid Sans';
+ font-style: normal;
+ font-weight: 400;
+ src: url('../fonts/droid-sans-v6-latin-regular.eot'); /* IE9 Compat Modes */
+ src: local('Droid Sans'), local('DroidSans'),
+ url('../fonts/droid-sans-v6-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url('../fonts/droid-sans-v6-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
+ url('../fonts/droid-sans-v6-latin-regular.woff') format('woff'), /* Modern Browsers */
+ url('../fonts/droid-sans-v6-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
+ url('../fonts/droid-sans-v6-latin-regular.svg#DroidSans') format('svg'); /* Legacy iOS */
+}
+/* droid-sans-700 - latin */
+@font-face {
+ font-family: 'Droid Sans';
+ font-style: normal;
+ font-weight: 700;
+ src: url('../fonts/droid-sans-v6-latin-700.eot'); /* IE9 Compat Modes */
+ src: local('Droid Sans Bold'), local('DroidSans-Bold'),
+ url('../fonts/droid-sans-v6-latin-700.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
+ url('../fonts/droid-sans-v6-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
+ url('../fonts/droid-sans-v6-latin-700.woff') format('woff'), /* Modern Browsers */
+ url('../fonts/droid-sans-v6-latin-700.ttf') format('truetype'), /* Safari, Android, iOS */
+ url('../fonts/droid-sans-v6-latin-700.svg#DroidSans') format('svg'); /* Legacy iOS */
+}
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.eot b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.eot
new file mode 100644
index 0000000000..d8524983ad
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.eot
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.svg b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.svg
new file mode 100644
index 0000000000..a54bbbbf25
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.svg
@@ -0,0 +1,411 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg">
+<defs >
+<font id="DroidSans" horiz-adv-x="1123" ><font-face
+ font-family="Droid Sans"
+ units-per-em="2048"
+ panose-1="2 11 8 6 3 8 4 2 2 4"
+ ascent="1907"
+ descent="-492"
+ alphabetic="0" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="532" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="586" d="M416 485H172L121 1462H467L416 485ZM117 143Q117 190 130 222T168 275T224 304T293 313Q328 313 359 304T415 275T453 223T467 143Q467 98 453 66T415 13T360 -17T293 -27Q256 -27 224 -18T168 13T131
+66T117 143Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="967" d="M412 1462L371 934H174L133 1462H412ZM834 1462L793 934H596L555 1462H834Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="1323" d="M999 844L952 612H1210V406H913L836 0H616L694 406H500L424 0H209L283 406H45V612H322L369 844H117V1053H406L483 1460H702L625 1053H823L901 1460H1116L1038 1053H1278V844H999ZM539 612H735L782
+844H586L539 612Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="1128" d="M1061 457Q1061 382 1035 318T956 206T825 127T645 86V-119H508V82Q442 84 386 90T281 107T188 133T100 168V432Q142 411 191 392T294 358T401 331T508 317V635Q500 638 491 642Q483 645 475 648T461
+653Q370 688 302 726T189 811T121 915T98 1044Q98 1119 126 1180T208 1287T337 1361T508 1399V1556H645V1405Q732 1400 823 1380T1014 1317L913 1083Q848 1109 778 1129T645 1155V862L684 848Q779 813 850 776T968 693T1038 590T1061 457ZM760 451Q760 475 754
+493T733 526T698 553T645 580V328Q704 337 732 367T760 451ZM399 1051Q399 1004 425 973T508 920V1153Q454 1147 427 1123T399 1051Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="1804" d="M315 1024Q315 897 337 835T410 772Q459 772 482 834T506 1024Q506 1274 410 1274Q360 1274 338 1213T315 1024ZM758 1026Q758 918 738 832T674 687T565 597T408 565Q323 565 259 596T151 687T85
+832T63 1026Q63 1134 83 1219T145 1362T253 1452T408 1483Q494 1483 559 1452T669 1363T735 1219T758 1026ZM1425 1462L614 0H375L1186 1462H1425ZM1298 440Q1298 313 1320 251T1393 188Q1442 188 1465 250T1489 440Q1489 690 1393 690Q1343 690 1321 629T1298
+440ZM1741 442Q1741 334 1721 249T1657 104T1548 14T1391 -18Q1306 -18 1242 13T1135 104T1069 248T1047 442Q1047 550 1067 635T1129 778T1236 868T1391 899Q1477 899 1542 868T1652 779T1718 635T1741 442Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="1479" d="M1475 0H1098L1001 100Q921 45 825 13T612 -20Q492 -20 395 10T228 94T120 225T82 395Q82 472 100 532T153 642T235 731T344 807Q306 853 280 895T237 979T214 1062T207 1149Q207 1227 237
+1288T322 1393T452 1460T618 1483Q704 1483 776 1462T901 1401T984 1301T1014 1165Q1014 1096 992 1039T931 932T842 842T731 766L991 498Q1026 564 1052 637T1098 784H1415Q1400 727 1380 664T1332 538T1270 411T1192 291L1475 0ZM403 424Q403 380 419 345T463
+286T530 249T614 236Q674 236 725 251T819 295L510 625Q459 583 431 535T403 424ZM731 1124Q731 1155 721 1176T695 1212T658 1232T616 1239Q594 1239 572 1233T531 1214T501 1178T489 1122Q489 1070 512 1024T575 932Q652 976 691 1020T731 1124Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="545" d="M412 1462L371 934H174L133 1462H412Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="694" d="M82 561Q82 686 100 807T155 1043T248 1263T383 1462H633Q492 1269 420 1038T348 563Q348 444 366 326T420 95T509 -124T631 -324H383Q305 -234 249 -131T155 84T100 317T82 561Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="694" d="M612 561Q612 437 594 317T539 85T446 -131T311 -324H63Q132 -230 185 -124T274 95T328 326T346 563Q346 807 274 1038T61 1462H311Q389 1369 445 1264T539 1044T594 808T612 561Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="1116" d="M688 1556L647 1188L1020 1292L1053 1040L713 1016L936 719L709 598L553 911L416 600L180 719L401 1016L63 1042L102 1292L467 1188L426 1556H688Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="1128" d="M455 612H88V831H455V1200H674V831H1040V612H674V248H455V612Z" />
+<glyph unicode="," glyph-name="comma" horiz-adv-x="594" d="M459 215Q445 161 426 100T383 -23T334 -146T283 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H444L459 215Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="659" d="M61 424V674H598V424H61Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="584" d="M117 143Q117 190 130 222T168 275T224 304T293 313Q328 313 359 304T415 275T453 223T467 143Q467 98 453 66T415 13T360 -17T293 -27Q256 -27 224 -18T168 13T131 66T117 143Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="846" d="M836 1462L291 0H14L559 1462H836Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="1128" d="M1065 731Q1065 554 1038 415T950 179T794 31T563 -20Q436 -20 342 31T186 179T94 415T63 731Q63 908 90 1048T178 1285T333 1433T563 1485Q689 1485 783 1434T940 1286T1034 1049T1065 731ZM371 731Q371
+481 414 355T563 229Q667 229 712 354T758 731Q758 982 713 1108T563 1235Q510 1235 474 1203T414 1108T381 951T371 731Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="1128" d="M817 0H508V846Q508 872 508 908T510 984T513 1064T516 1137Q511 1131 499 1119T472 1093T441 1063T410 1036L242 901L92 1087L563 1462H817V0Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="1128" d="M1063 0H82V215L426 586Q491 656 544 715T635 830T694 944T715 1069Q715 1143 671 1184T551 1225Q472 1225 399 1186T246 1075L78 1274Q123 1315 172 1352T280 1419T410 1465T569 1483Q674 1483 757
+1454T900 1372T990 1242T1022 1071Q1022 985 992 907T910 753T790 603T643 451L467 274V260H1063V0Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="1128" d="M1006 1135Q1006 1059 982 999T915 893T815 817T690 770V764Q867 742 958 657T1049 426Q1049 330 1015 249T909 107T729 14T473 -20Q355 -20 251 -1T57 59V322Q102 298 152 280T252 250T350 231T442
+225Q528 225 585 241T676 286T724 355T739 444Q739 489 721 525T661 587T552 627T387 641H283V858H385Q477 858 538 874T635 919T687 986T702 1067Q702 1145 654 1189T500 1233Q452 1233 411 1224T334 1200T269 1168T215 1133L59 1339Q101 1370 150 1396T258 1441T383
+1472T526 1483Q634 1483 722 1460T874 1392T971 1283T1006 1135Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="1128" d="M1085 303H909V0H608V303H4V518L625 1462H909V543H1085V303ZM608 543V791Q608 804 608 828T610 884T612 948T615 1011T618 1063T621 1096H612Q594 1054 572 1007T520 913L276 543H608Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="1128" d="M598 934Q692 934 773 905T914 820T1008 681T1042 489Q1042 370 1005 276T896 116T718 15T473 -20Q418 -20 364 -15T261 -1T167 24T86 59V326Q121 306 167 289T262 259T362 239T457 231Q591 231 661
+286T731 463Q731 571 663 627T451 684Q425 684 396 681T338 673T283 663T238 651L115 717L170 1462H942V1200H438L414 913Q446 920 488 927T598 934Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="1128" d="M76 621Q76 726 87 830T128 1029T208 1207T336 1349T522 1444T776 1479Q797 1479 822 1478T872 1476T922 1471T965 1464V1217Q927 1226 885 1231T799 1237Q664 1237 577 1204T439 1110T367 966T340
+780H352Q372 816 400 847T467 901T552 937T659 950Q754 950 830 919T958 829T1039 684T1067 487Q1067 368 1034 274T938 115T788 15T590 -20Q482 -20 388 18T225 136T116 335T76 621ZM584 227Q625 227 658 242T716 289T754 369T768 483Q768 590 724 651T588 713Q542
+713 504 695T439 648T398 583T383 510Q383 459 395 409T433 318T496 252T584 227Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="1128" d="M207 0L727 1200H55V1460H1063V1266L530 0H207Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="1128" d="M565 1481Q656 1481 737 1459T879 1393T976 1283T1012 1128Q1012 1062 992 1009T937 912T854 834T750 772Q808 741 863 703T962 618T1031 511T1057 379Q1057 288 1021 214T920 88T765 8T565 -20Q447
+-20 355 7T200 84T105 207T72 371Q72 446 94 506T154 614T243 699T352 764Q303 795 260 831T186 912T136 1011T117 1130Q117 1217 153 1282T252 1392T395 1459T565 1481ZM358 389Q358 349 371 316T409 258T473 221T561 207Q666 207 718 256T770 387Q770 429 753
+462T708 524T645 577T575 623L553 637Q509 615 473 590T412 534T372 467T358 389ZM563 1255Q530 1255 502 1245T453 1216T420 1169T408 1106Q408 1064 420 1034T454 980T504 938T565 901Q596 917 624 936T673 979T708 1035T721 1106Q721 1141 709 1169T676 1216T626
+1245T563 1255Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="1128" d="M1055 838Q1055 733 1044 629T1003 429T923 252T795 109T609 15T354 -20Q333 -20 308 -19T258 -17T208 -13T166 -6V242Q203 232 245 227T332 221Q467 221 554 254T692 348T764 493T791 678H778Q758
+642 730 611T664 557T578 521T471 508Q376 508 300 539T172 629T91 774T63 971Q63 1090 96 1184T192 1343T342 1444T541 1479Q649 1479 743 1441T906 1323T1015 1123T1055 838ZM547 1231Q506 1231 472 1216T414 1170T376 1090T362 975Q362 869 407 807T543 745Q589
+745 627 763T692 810T733 875T748 948Q748 999 736 1049T698 1140T635 1206T547 1231Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="584" d="M117 143Q117 190 130 222T168 275T224 304T293 313Q328 313 359 304T415 275T453 223T467 143Q467 98 453 66T415 13T360 -17T293 -27Q256 -27 224 -18T168 13T131 66T117 143ZM117 969Q117 1016
+130 1048T168 1101T224 1130T293 1139Q328 1139 359 1130T415 1101T453 1049T467 969Q467 924 453 892T415 839T360 809T293 799Q256 799 224 808T168 838T131 891T117 969Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="594" d="M444 238L459 215Q445 161 426 100T383 -23T334 -146T283 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H444ZM117 969Q117 1016 130 1048T168 1101T224 1130T293 1139Q328 1139 359 1130T415
+1101T453 1049T467 969Q467 924 453 892T415 839T360 809T293 799Q256 799 224 808T168 838T131 891T117 969Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="1128" d="M1040 203L88 641V784L1040 1280V1040L397 723L1040 442V203Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="1128" d="M88 807V1024H1040V807H88ZM88 418V637H1040V418H88Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="1128" d="M88 442L731 723L88 1040V1280L1040 784V641L88 203V442Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="940" d="M264 485V559Q264 610 274 651T306 730T362 803T444 877Q486 910 515 936T562 987T588 1041T596 1106Q596 1163 558 1200T440 1237Q371 1237 292 1208T127 1137L25 1358Q68 1383 118 1405T223 1445T334
+1473T444 1483Q546 1483 628 1459T767 1387T854 1273T885 1120Q885 1057 871 1008T830 916T761 834T664 750Q622 717 596 693T554 646T534 601T528 545V485H264ZM231 143Q231 190 244 222T282 275T338 304T408 313Q443 313 474 304T530 275T568 223T582 143Q582
+98 568 66T530 13T475 -17T408 -27Q371 -27 339 -18T282 13T245 66T231 143Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="1774" d="M1673 752Q1673 657 1651 564T1582 398T1467 279T1303 233Q1265 233 1232 242T1170 269T1122 310T1090 362H1075Q1056 337 1031 314T975 272T907 244T825 233Q742 233 678 261T569 342T502 468T479 631Q479
+734 510 820T599 968T740 1065T926 1100Q971 1100 1019 1095T1111 1082T1195 1064T1262 1044L1241 625Q1239 603 1239 582T1239 555Q1239 513 1245 486T1262 444T1286 422T1315 416Q1350 416 1376 443T1419 516T1445 623T1454 754Q1454 882 1416 982T1311 1151T1150
+1256T948 1292Q795 1292 679 1241T484 1099T365 882T324 608Q324 470 359 364T463 185T633 75T866 37Q922 37 981 44T1098 63T1213 92T1321 129V-63Q1227 -105 1113 -129T868 -154Q687 -154 545 -103T304 46T154 283T102 602Q102 726 129 839T207 1050T331 1227T499
+1363T706 1450T948 1481Q1106 1481 1239 1431T1468 1286T1619 1056T1673 752ZM711 627Q711 515 749 466T850 416Q892 416 922 435T972 490T1002 575T1016 686L1028 907Q1008 912 981 915T926 918Q867 918 826 893T760 827T723 734T711 627Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582 928L489 608H846Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="1315" d="M184 1462H612Q750 1462 854 1443T1028 1380T1133 1266T1169 1092Q1169 1030 1154 976T1110 881T1040 813T944 776V766Q999 754 1046 732T1129 670T1185 570T1206 424Q1206 324 1171 246T1071 113T912
+29T700 0H184V1462ZM494 883H655Q713 883 752 893T815 925T849 977T860 1051Q860 1135 808 1171T641 1208H494V883ZM494 637V256H676Q737 256 778 270T845 310T882 373T893 455Q893 496 882 529T845 587T775 624T668 637H494Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="1305" d="M805 1225Q716 1225 648 1191T533 1092T462 935T438 727Q438 610 459 519T525 366T639 271T805 238Q894 238 983 258T1178 315V55Q1130 35 1083 21T987 -2T887 -15T776 -20Q607 -20 483 34T278 186T158
+422T119 729Q119 895 164 1033T296 1272T511 1427T805 1483Q914 1483 1023 1456T1233 1380L1133 1128Q1051 1167 968 1196T805 1225Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="1434" d="M1315 745Q1315 560 1265 421T1119 188T885 47T569 0H184V1462H612Q773 1462 902 1416T1124 1280T1265 1055T1315 745ZM1001 737Q1001 859 977 947T906 1094T792 1180T637 1208H494V256H608Q804 256 902
+376T1001 737Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="1147" d="M1026 0H184V1462H1026V1208H494V887H989V633H494V256H1026V0Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="1124" d="M489 0H184V1462H1022V1208H489V831H985V578H489V0Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="1483" d="M739 821H1319V63Q1261 44 1202 29T1080 3T947 -14T799 -20Q635 -20 509 28T296 172T164 408T119 733Q119 905 169 1044T316 1280T556 1430T883 1483Q1000 1483 1112 1458T1317 1393L1214 1145Q1146 1179
+1061 1202T881 1225Q779 1225 698 1190T558 1089T469 932T438 727Q438 619 459 530T527 375T645 274T819 238Q885 238 930 244T1016 258V563H739V821Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="1485" d="M1300 0H991V631H494V0H184V1462H494V889H991V1462H1300V0Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="797" d="M731 0H66V176L244 258V1204L66 1286V1462H731V1286L553 1204V258L731 176V0Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="678" d="M-2 -430Q-67 -430 -116 -424T-199 -408V-150Q-162 -158 -122 -164T-33 -170Q13 -170 52 -160T121 -126T167 -60T184 43V1462H494V53Q494 -73 458 -164T356 -314T199 -402T-2 -430Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="1298" d="M1298 0H946L610 608L494 522V0H184V1462H494V758L616 965L950 1462H1294L827 803L1298 0Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="1096" d="M184 0V1462H494V256H1026V0H184Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="1870" d="M772 0L451 1147H442Q448 1055 452 969Q454 932 455 893T458 816T460 743T461 680V0H184V1462H606L922 344H928L1264 1462H1686V0H1397V692Q1397 718 1397 751T1399 821T1401 896T1404 970Q1408 1054
+1411 1145H1403L1057 0H772Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="1604" d="M1419 0H1026L451 1106H442Q448 1029 452 953Q456 888 458 817T461 688V0H184V1462H575L1149 367H1155Q1152 443 1148 517Q1147 549 1146 582T1143 649T1142 714T1141 770V1462H1419V0Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q944 1485 1067 1432T1270 1280T1390 1043T1430 733ZM438
+733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="1225" d="M494 774H555Q686 774 752 826T819 995Q819 1104 760 1156T573 1208H494V774ZM1133 1006Q1133 910 1104 822T1009 667T834 560T565 520H494V0H184V1462H590Q731 1462 833 1431T1002 1341T1101 1198T1133 1006Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="1548" d="M1430 733Q1430 614 1411 510T1352 319T1253 166T1112 55L1473 -348H1075L807 -18Q800 -18 794 -19Q789 -20 784 -20T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483
+1431T776 1485Q944 1485 1067 1432T1270 1280T1390 1043T1430 733ZM438 733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733Z"
+/>
+<glyph unicode="R" glyph-name="R" horiz-adv-x="1290" d="M494 813H578Q707 813 763 864T819 1016Q819 1120 759 1164T573 1208H494V813ZM494 561V0H184V1462H584Q865 1462 999 1354T1133 1024Q1133 949 1113 888T1060 780T983 697T891 637Q1002 459 1090 319Q1128
+259 1163 202T1227 100T1273 28L1290 0H946L629 561H494Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="1073" d="M985 406Q985 308 952 230T854 96T696 10T481 -20Q375 -20 277 2T94 68V356Q142 333 191 312T290 273T391 246T492 236Q543 236 579 247T638 279T671 328T682 391Q682 432 665 463T616 522T540 576T440
+631Q394 655 337 689T230 773T145 895T111 1067Q111 1165 143 1242T236 1373T381 1455T573 1483Q626 1483 676 1476T776 1456T876 1424T979 1380L879 1139Q834 1160 795 1176T719 1203T647 1219T575 1225Q497 1225 456 1184T414 1073Q414 1036 426 1008T466 954T537
+903T643 844Q718 804 781 763T889 671T960 556T985 406Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="1124" d="M717 0H408V1204H41V1462H1083V1204H717V0Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="1466" d="M1292 1462V516Q1292 402 1258 304T1153 134T976 21T727 -20Q592 -20 489 18T316 128T210 298T174 520V1462H483V543Q483 462 499 405T546 311T625 257T735 240Q866 240 924 316T983 545V1462H1292Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="1249" d="M936 1462H1249L793 0H455L0 1462H313L561 582Q566 565 574 525T592 437T611 341T625 260Q630 293 639 341T658 436T677 524T692 582L936 1462Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="1898" d="M1546 0H1194L1014 721Q1010 736 1005 763T992 824T978 895T965 967T955 1031T948 1079Q946 1061 942 1032T931 968T919 896T906 825T893 763T883 719L705 0H352L0 1462H305L471 664Q474 648 479 618T492
+549T506 469T521 387T534 313T543 256Q546 278 551 312T563 384T576 464T590 540T601 603T610 643L813 1462H1085L1288 643Q1291 631 1296 604T1308 541T1322 464T1335 385T1347 312T1356 256Q1359 278 1364 312T1377 387T1391 469T1406 549T1418 617T1427 664L1593
+1462H1898L1546 0Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="1284" d="M1284 0H930L631 553L332 0H0L444 754L31 1462H373L647 936L915 1462H1249L831 737L1284 0Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="1196" d="M598 860L862 1462H1196L752 569V0H444V559L0 1462H336L598 860Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="1104" d="M1055 0H49V201L668 1206H68V1462H1036V1262L418 256H1055V0Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="678" d="M627 -324H143V1462H627V1251H403V-113H627V-324Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="846" d="M289 1462L834 0H557L12 1462H289Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="678" d="M51 -113H274V1251H51V1462H535V-324H51V-113Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="1090" d="M8 520L446 1470H590L1085 520H846L524 1163Q455 1002 384 839T244 520H8Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="842" d="M846 -324H-4V-184H846V-324Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="1182" d="M645 1241Q611 1269 564 1310T470 1396T386 1480T332 1548V1569H674Q690 1535 711 1495T756 1414T803 1335T848 1268V1241H645Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289 838L190 1040Q274
+1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="1245" d="M756 1139Q842 1139 913 1102T1035 992T1114 811T1143 561Q1143 417 1115 309T1034 127T909 17T748 -20Q692 -20 649 -8T571 24T512 69T465 123H444L393 0H160V1556H465V1194Q465 1161 463 1123T459 1051Q456
+1012 453 973H465Q486 1008 513 1038T575 1090T656 1126T756 1139ZM653 895Q602 895 567 877T509 821T477 728T465 596V563Q465 482 474 419T506 314T564 249T655 227Q746 227 788 313T831 565Q831 730 789 812T653 895Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="1022" d="M625 -20Q505 -20 409 13T244 115T139 293T102 553Q102 720 139 832T245 1013T410 1110T625 1139Q711 1139 796 1118T956 1059L868 827Q802 856 741 874T625 893Q514 893 464 809T414 555Q414 387 464
+307T621 227Q708 227 779 249T924 307V53Q887 35 852 21T782 -2T708 -15T625 -20Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="1245" d="M489 -20Q403 -20 332 17T210 126T131 307T102 557Q102 701 130 809T211 991T337 1102T498 1139Q552 1139 597 1127T678 1092T742 1040T793 975H803Q797 1014 792 1054Q787 1088 784 1126T780 1198V1556H1085V0H852L793
+145H780Q759 111 732 81T670 28T590 -7T489 -20ZM600 223Q654 223 692 241T753 297T788 391T801 522V555Q801 636 791 699T758 804T696 869T598 891Q502 891 457 805T412 553Q412 388 457 306T600 223Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="1190" d="M612 922Q531 922 478 865T416 686H805Q804 737 792 780T756 854T696 904T612 922ZM651 -20Q531 -20 430 15T256 120T143 298T102 551Q102 698 139 808T242 991T402 1102T610 1139Q721 1139 810 1106T962
+1007T1058 848T1092 631V483H410Q412 419 430 368T482 281T563 226T672 207Q723 207 768 212T857 229T942 256T1028 295V59Q988 38 948 24T862 -1T765 -15T651 -20Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="793" d="M741 889H514V0H209V889H41V1036L209 1118V1200Q209 1307 235 1377T309 1490T425 1549T578 1567Q670 1567 733 1553T840 1520L768 1296Q737 1307 703 1316T623 1325Q563 1325 539 1287T514 1188V1118H741V889Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="1130" d="M1085 1116V950L922 899Q942 865 950 829T958 750Q958 665 931 595T851 474T718 397T532 369Q509 369 482 371T442 377Q422 360 411 342T399 297Q399 276 412 264T446 244T495 234T553 231H727Q808 231
+872 213T980 156T1049 60T1073 -80Q1073 -175 1035 -251T922 -381T734 -463T475 -492Q361 -492 276 -471T134 -409T49 -311T20 -182Q20 -121 41 -76T97 1T176 53T268 84Q247 93 227 109T190 146T163 192T152 246Q152 278 161 304T189 352T234 395T295 436Q207 474
+156 558T104 756Q104 846 132 917T214 1037T348 1113T532 1139Q552 1139 577 1137T626 1131T672 1123T705 1116H1085ZM285 -158Q285 -183 295 -206T330 -248T393 -276T489 -287Q645 -287 724 -243T803 -125Q803 -62 754 -41T602 -20H461Q434 -20 403 -26T346 -49T303
+-91T285 -158ZM395 752Q395 661 429 611T532 561Q604 561 636 611T668 752Q668 842 637 895T532 948Q395 948 395 752Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="1284" d="M1130 0H825V653Q825 774 788 834T672 895Q613 895 573 871T509 800T475 684T465 526V0H160V1556H465V1239Q465 1197 463 1151T458 1065Q454 1019 451 975H467Q516 1062 592 1100T764 1139Q847 1139 914
+1116T1030 1042T1104 915T1130 729V0Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="625" d="M147 1407Q147 1450 160 1478T195 1524T248 1549T313 1556Q347 1556 377 1549T429 1525T465 1479T479 1407Q479 1365 466 1336T430 1290T377 1265T313 1257Q279 1257 249 1264T196 1289T160 1336T147 1407ZM465
+0H160V1118H465V0Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="625" d="M102 -492Q54 -492 3 -485T-82 -467V-227Q-51 -237 -24 -241T37 -246Q62 -246 84 -239T123 -212T150 -160T160 -76V1118H465V-121Q465 -198 446 -265T383 -383T270 -463T102 -492ZM147 1407Q147 1450 160
+1478T195 1524T248 1549T313 1556Q347 1556 377 1549T429 1525T465 1479T479 1407Q479 1365 466 1336T430 1290T377 1265T313 1257Q279 1257 249 1264T196 1289T160 1336T147 1407Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="1208" d="M453 608L565 778L838 1118H1182L778 633L1208 0H856L584 430L465 348V0H160V1556H465V862L449 608H453Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="625" d="M465 0H160V1556H465V0Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="1929" d="M1120 0H815V653Q815 774 779 834T666 895Q608 895 570 871T508 800T475 684T465 526V0H160V1118H393L434 975H451Q475 1018 508 1049T582 1100T667 1129T758 1139Q873 1139 953 1100T1077 975H1102Q1126
+1018 1160 1049T1235 1100T1321 1129T1413 1139Q1593 1139 1684 1042T1776 729V0H1470V653Q1470 774 1434 834T1321 895Q1212 895 1166 809T1120 561V0Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="1284" d="M1130 0H825V653Q825 774 789 834T672 895Q612 895 572 871T509 800T475 684T465 526V0H160V1118H393L434 975H451Q475 1018 509 1049T585 1100T672 1129T766 1139Q848 1139 915 1116T1030 1042T1104
+915T1130 729V0Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="1227" d="M414 561Q414 394 461 310T614 225Q719 225 766 310T813 561Q813 728 766 810T612 893Q507 893 461 811T414 561ZM1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q499 -20 406 18T246 131T140 313T102
+561Q102 700 137 808T239 989T401 1101T616 1139Q727 1139 820 1101T980 990T1086 808T1124 561Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="1245" d="M748 -20Q693 -20 650 -8T572 24T512 69T465 123H449Q453 88 457 57Q460 31 462 4T465 -39V-492H160V1118H408L451 973H465Q486 1007 513 1037T575 1089T656 1125T756 1139Q843 1139 914 1102T1036 992T1115
+811T1143 561Q1143 418 1114 310T1033 128T908 17T748 -20ZM653 895Q602 895 567 877T509 821T477 728T465 596V563Q465 482 474 419T506 314T564 249T655 227Q746 227 788 313T831 565Q831 730 789 812T653 895Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="1245" d="M602 219Q657 219 694 237T755 293T789 386T801 518V555Q801 636 792 699T759 804T697 869T600 891Q504 891 459 805T414 553Q414 385 459 302T602 219ZM489 -20Q402 -20 331 17T209 126T130 307T102
+557Q102 700 130 808T211 990T337 1101T498 1139Q554 1139 599 1127T680 1092T745 1040T795 975H803L827 1118H1085V-492H780V-23Q780 -4 782 24T787 80Q790 112 793 145H780Q760 111 733 81T671 28T590 -7T489 -20Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="889" d="M743 1139Q755 1139 769 1139T797 1137T822 1134T840 1130V844Q832 846 818 848T789 851T758 853T733 854Q674 854 625 839T540 791T485 703T465 569V0H160V1118H391L436 950H451Q475 993 503 1028T565
+1087T643 1125T743 1139Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="985" d="M905 332Q905 244 873 178T782 68T639 2T451 -20Q396 -20 349 -17T260 -5T179 15T100 45V297Q142 276 188 259T281 230T370 210T451 203Q492 203 521 210T568 231T595 263T604 303Q604 324 598 340T568
+375T501 417T381 475Q308 508 255 540T167 613T115 704T98 827Q98 905 128 963T213 1061T345 1119T518 1139Q618 1139 708 1116T893 1047L801 831Q725 867 656 890T518 913Q456 913 429 891T401 831Q401 811 408 796T436 764T495 728T594 680Q665 649 722 619T820
+549T883 458T905 332Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="848" d="M614 223Q659 223 699 233T782 258V31Q739 9 676 -5T537 -20Q464 -20 401 -3T292 56T220 170T193 350V889H47V1018L215 1120L303 1356H498V1118H770V889H498V350Q498 285 530 254T614 223Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="1284" d="M891 0L850 143H834Q809 100 775 70T699 19T612 -10T518 -20Q436 -20 369 3T254 77T180 204T154 389V1118H459V465Q459 344 495 284T612 223Q672 223 712 247T775 318T809 434T819 592V1118H1124V0H891Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="1104" d="M395 0L0 1118H319L504 481Q521 424 533 363T549 252H555Q558 305 570 364T600 481L784 1118H1104L709 0H395Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="1651" d="M1014 0L928 391Q924 408 918 439T903 510T887 594T869 683Q849 786 825 905H819Q796 786 777 682Q769 638 761 593T744 509T730 437T719 387L629 0H301L0 1118H303L416 623Q425 584 434 530T452 420T468
+315T479 236H485Q486 255 489 285T498 351T508 422T519 491T529 547T537 582L659 1118H995L1112 582Q1117 560 1125 514T1141 416T1156 314T1163 236H1169Q1172 261 1179 310T1196 415T1215 528T1235 623L1352 1118H1651L1346 0H1014Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="1122" d="M389 571L29 1118H375L561 782L750 1118H1096L731 571L1112 0H766L561 362L356 0H10L389 571Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="1104" d="M0 1118H334L514 489Q530 437 537 378T547 272H553Q555 295 558 323T567 380T578 437T592 489L768 1118H1104L662 -143Q600 -320 493 -406T225 -492Q173 -492 135 -487T70 -475V-233Q91 -238 123 -242T190
+-246Q238 -246 272 -233T330 -197T372 -140T403 -66L422 -10L0 1118Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="936" d="M877 0H55V180L512 885H86V1118H858V920L416 233H877V0Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="745" d="M287 367T222 408T31 449V688Q93 688 141 697T223 728T272 784T287 866V1184Q287 1258 306 1310T374 1396T509 1446T725 1462V1237Q685 1236 653 1230T598 1209T563 1166T551 1096V797Q545 610
+317 575V563Q432 546 493 491T551 342V43Q551 0 563 -27T597 -69T652 -91T725 -98V-324Q594 -324 509 -308T375 -259T306 -172T287 -45V270Q287 367 222 408Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="1128" d="M455 1550H674V-465H455V1550Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="745" d="M469 -45Q469 -119 450 -172T382 -258T247 -308T31 -324V-98Q71 -97 103 -91T157 -70T192 -27T205 43V342Q202 436 263 491T438 563V575Q211 610 205 797V1096Q205 1139 193 1166T158 1208T103
+1230T31 1237V1462Q162 1462 247 1446T381 1397T450 1311T469 1184V866Q468 818 484 784T533 729T614 698T725 688V449Q600 449 535 408T469 270V-45Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="1128" d="M528 616Q491 632 463 643T411 660T366 669T322 672Q293 672 262 663T201 637T143 598T88 551V782Q139 836 202 863T344 891Q374 891 399 889T453 879T517 860T600 827Q638 811 666 801T719
+784T764 775T807 772Q836 772 867 781T928 807T986 845T1040 893V662Q939 553 784 553Q754 553 729 555T675 564T611 583T528 616Z" />
+<glyph unicode="&#xa0;" glyph-name="nbspace" horiz-adv-x="532" />
+<glyph unicode="&#xa1;" glyph-name="exclamdown" horiz-adv-x="586" d="M168 606H412L463 -369H117L168 606ZM467 948Q467 901 454 869T416 816T360 787T291 778Q256 778 225 787T169 816T131 868T117 948Q117 993 131 1025T169 1078T224 1108T291 1118Q328 1118
+360 1109T416 1079T453 1026T467 948Z" />
+<glyph unicode="&#xa2;" glyph-name="cent" horiz-adv-x="1128" d="M543 -20V186Q451 199 377 236T251 340T171 506T143 743Q143 884 171 985T251 1155T378 1260T543 1311V1483H721V1319Q759 1318 797 1313T870 1299T937 1281T993 1260L907 1034Q886 1044 860
+1053T805 1070T750 1082T698 1087Q632 1087 586 1067T511 1006T468 901T455 750Q455 579 512 500T698 420Q774 420 844 438T965 481V242Q914 213 852 198T721 180V-20H543Z" />
+<glyph unicode="&#xa3;" glyph-name="sterling" horiz-adv-x="1128" d="M680 1483Q790 1483 879 1459T1049 1401L956 1171Q885 1200 827 1217T705 1235Q638 1235 601 1197T563 1063V870H897V651H563V508Q563 453 550 413T514 343T466 294T412 260H1090V0H82V248Q124
+266 157 287T214 337T250 407T262 506V651H84V870H262V1065Q262 1178 293 1257T380 1387T512 1460T680 1483Z" />
+<glyph unicode="&#xa4;" glyph-name="currency" horiz-adv-x="1128" d="M168 723Q168 777 182 826T221 920L92 1047L240 1194L367 1067Q410 1092 461 1106T563 1120Q617 1120 665 1107T760 1065L887 1194L1036 1051L907 922Q932 880 946 829T961 723Q961 667 947
+618T907 524L1032 399L887 254L760 379Q716 356 667 342T563 328Q507 328 458 340T365 379L240 256L94 401L221 526Q168 617 168 723ZM375 723Q375 684 390 650T430 590T490 550T563 535Q603 535 638 549T699 589T741 649T756 723Q756 763 741 797T700 857T638
+898T563 913Q524 913 490 898T431 858T390 798T375 723Z" />
+<glyph unicode="&#xa5;" glyph-name="yen" horiz-adv-x="1128" d="M565 860L809 1462H1122L760 715H954V537H709V399H954V221H709V0H422V221H174V399H422V537H174V715H365L8 1462H324L565 860Z" />
+<glyph unicode="&#xa6;" glyph-name="brokenbar" horiz-adv-x="1128" d="M455 1550H674V735H455V1550ZM455 350H674V-465H455V350Z" />
+<glyph unicode="&#xa7;" glyph-name="section" horiz-adv-x="995" d="M121 805Q121 849 131 886T160 955T203 1012T254 1055Q191 1095 156 1154T121 1288Q121 1353 150 1406T232 1498T360 1556T526 1577Q628 1577 716 1554T889 1493L807 1303Q739 1335 669 1360T520
+1386Q439 1386 402 1363T365 1292Q365 1267 377 1246T415 1206T481 1167T578 1124Q649 1096 707 1062T807 987T872 895T895 782Q895 682 861 621T770 522Q832 482 863 430T895 303Q895 229 864 170T776 68T638 3T455 -20Q345 -20 261 0T106 59V266Q145 246 190
+229T281 198T371 176T455 168Q511 168 548 177T607 202T639 239T649 285Q649 310 642 329T612 368T549 408T442 457Q366 489 306 521T205 593T143 685T121 805ZM344 827Q344 764 400 716T575 616L590 610Q605 621 619 635T644 668T661 708T668 756Q668 788 658
+815T621 867T550 917T434 967Q416 960 400 947T372 915T352 875T344 827Z" />
+<glyph unicode="&#xa8;" glyph-name="dieresis" horiz-adv-x="1182" d="M248 1405Q248 1440 259 1465T288 1507T332 1532T387 1540Q416 1540 441 1532T486 1508T516 1466T528 1405Q528 1371 517 1346T486 1305T442 1280T387 1272Q358 1272 333 1280T289 1304T259
+1346T248 1405ZM651 1405Q651 1440 662 1465T692 1507T737 1532T793 1540Q821 1540 846 1532T891 1508T922 1466T934 1405Q934 1371 923 1346T892 1305T847 1280T793 1272Q733 1272 692 1305T651 1405Z" />
+<glyph unicode="&#xa9;" glyph-name="copyright" horiz-adv-x="1704" d="M895 1010Q798 1010 745 936T692 731Q692 596 740 524T895 451Q952 451 1018 466T1141 510V319Q1084 292 1025 277T889 262Q782 262 702 296T569 392T488 540T461 733Q461 836 487 921T565
+1068T697 1164T881 1198Q964 1198 1041 1176T1186 1120L1112 952Q999 1010 895 1010ZM100 731Q100 835 127 931T202 1110T320 1263T472 1380T652 1456T852 1483Q956 1483 1052 1456T1231 1381T1384 1263T1501 1111T1577 931T1604 731Q1604 627 1577 531T1502 352T1384
+200T1232 82T1052 7T852 -20Q748 -20 652 6T473 82T320 199T203 351T127 531T100 731ZM242 731Q242 604 290 493T420 300T614 169T852 121Q979 121 1090 169T1283 299T1414 493T1462 731Q1462 858 1414 969T1284 1162T1090 1293T852 1341Q725 1341 614 1293T421
+1163T290 969T242 731Z" />
+<glyph unicode="&#xaa;" glyph-name="ordfeminine" horiz-adv-x="743" d="M520 764L489 874Q449 816 393 784T268 752Q218 752 178 765T108 806T63 876T47 975Q47 1035 68 1076T130 1144T230 1184T365 1202L455 1206Q455 1269 426 1296T342 1323Q302 1323 253
+1306T152 1262L86 1397Q148 1429 222 1454T387 1479Q455 1479 505 1460T589 1405T638 1319T655 1206V764H520ZM373 1081Q335 1078 312 1068T275 1044T257 1012T252 977Q252 939 271 921T317 903Q349 903 374 914T418 944T445 991T455 1051V1087L373 1081Z" />
+<glyph unicode="&#xab;" glyph-name="guillemotleft" horiz-adv-x="1198" d="M82 573L391 1028L610 909L393 561L610 213L391 94L82 547V573ZM588 573L897 1028L1116 909L899 561L1116 213L897 94L588 547V573Z" />
+<glyph unicode="&#xac;" glyph-name="logicalnot" horiz-adv-x="1128" d="M1040 248H821V612H88V831H1040V248Z" />
+<glyph unicode="&#xad;" glyph-name="uni00AD" horiz-adv-x="659" d="M61 424V674H598V424H61Z" />
+<glyph unicode="&#xae;" glyph-name="registered" horiz-adv-x="1704" d="M1157 905Q1157 811 1119 756T1014 672L1251 272H997L819 610H772V272H543V1188H807Q989 1188 1073 1118T1157 905ZM772 778H803Q869 778 897 806T926 901Q926 936 919 959T896 995T857
+1014T801 1020H772V778ZM100 731Q100 835 127 931T202 1110T320 1263T472 1380T652 1456T852 1483Q956 1483 1052 1456T1231 1381T1384 1263T1501 1111T1577 931T1604 731Q1604 627 1577 531T1502 352T1384 200T1232 82T1052 7T852 -20Q748 -20 652 6T473 82T320
+199T203 351T127 531T100 731ZM242 731Q242 604 290 493T420 300T614 169T852 121Q979 121 1090 169T1283 299T1414 493T1462 731Q1462 858 1414 969T1284 1162T1090 1293T852 1341Q725 1341 614 1293T421 1163T290 969T242 731Z" />
+<glyph unicode="&#xaf;" glyph-name="overscore" horiz-adv-x="1024" d="M1030 1556H-6V1757H1030V1556Z" />
+<glyph unicode="&#xb0;" glyph-name="degree" horiz-adv-x="877" d="M92 1137Q92 1208 119 1271T193 1381T303 1455T438 1483Q510 1483 573 1456T683 1381T757 1271T784 1137Q784 1065 757 1002T684 893T574 820T438 793Q366 793 303 819T193 892T119 1002T92
+1137ZM283 1137Q283 1106 295 1078T328 1029T377 996T438 983Q470 983 498 995T548 1029T581 1078T594 1137Q594 1169 582 1197T548 1247T499 1281T438 1294Q406 1294 378 1282T328 1248T295 1198T283 1137Z" />
+<glyph unicode="&#xb1;" glyph-name="plusminus" horiz-adv-x="1128" d="M455 674H88V893H455V1262H674V893H1040V674H674V309H455V674ZM88 0V219H1040V0H88Z" />
+<glyph unicode="&#xb2;" glyph-name="twosuperior" horiz-adv-x="776" d="M702 586H55V754L279 973Q325 1018 355 1051T404 1111T430 1161T438 1212Q438 1250 414 1270T350 1290Q310 1290 267 1270T170 1202L47 1354Q112 1411 193 1447T383 1483Q449 1483 503
+1467T596 1419T656 1341T678 1233Q678 1187 666 1147T626 1065T557 980T455 881L350 786H702V586Z" />
+<glyph unicode="&#xb3;" glyph-name="threesuperior" horiz-adv-x="776" d="M666 1249Q666 1180 626 1130T496 1051V1038Q547 1028 584 1007T645 959T682 898T694 829Q694 708 606 639T332 569Q256 569 190 586T59 639V829Q125 789 191 764T330 739Q404 739 438
+766T473 846Q473 867 465 886T438 919T387 943T307 952H195V1112H287Q339 1112 371 1121T421 1145T445 1180T451 1221Q451 1259 426 1284T350 1309Q303 1309 261 1290T162 1231L61 1372Q123 1419 198 1450T377 1481Q439 1481 492 1465T583 1418T644 1345T666 1249Z"
+/>
+<glyph unicode="&#xb4;" glyph-name="acute" horiz-adv-x="1182" d="M332 1241V1268Q353 1297 377 1335T424 1413T469 1494T506 1569H848V1548Q837 1530 816 1506T768 1453T710 1396T648 1338T587 1285T535 1241H332Z" />
+<glyph unicode="&#xb5;" glyph-name="mu" horiz-adv-x="1290" d="M465 465Q465 344 502 284T621 223Q679 223 718 247T781 318T815 434T825 592V1118H1130V0H897L854 150H842Q807 65 755 23T627 -20Q573 -20 528 3T455 70Q457 28 460 -15Q462 -52 463 -94T465
+-172V-492H160V1118H465V465Z" />
+<glyph unicode="&#xb6;" glyph-name="paragraph" horiz-adv-x="1341" d="M1167 -260H1006V1356H840V-260H678V559Q617 541 532 541Q437 541 360 566T228 651T143 806T113 1042Q113 1189 145 1287T237 1446T380 1531T563 1556H1167V-260Z" />
+<glyph unicode="&#xb7;" glyph-name="middot" horiz-adv-x="584" d="M117 723Q117 770 130 802T168 855T224 884T293 893Q328 893 359 884T415 855T453 803T467 723Q467 678 453 646T415 593T360 563T293 553Q256 553 224 562T168 592T131 645T117 723Z" />
+<glyph unicode="&#xb8;" glyph-name="cedilla" horiz-adv-x="420" d="M418 -250Q418 -307 403 -352T351 -428T256 -475T109 -492Q64 -492 28 -486T-37 -471V-303Q-22 -307 -4 -310T34 -317T72 -322T106 -324Q135 -324 156 -311T178 -262Q178 -225 141 -197T12
+-154L90 0H283L256 -61Q287 -71 316 -88T367 -128T404 -182T418 -250Z" />
+<glyph unicode="&#xb9;" glyph-name="onesuperior" horiz-adv-x="776" d="M584 586H346V1032Q346 1052 346 1082T348 1144T351 1201T354 1239Q348 1231 339 1221T319 1199T298 1178T279 1161L201 1100L92 1227L393 1462H584V586Z" />
+<glyph unicode="&#xba;" glyph-name="ordmasculine" horiz-adv-x="754" d="M696 1116Q696 1029 674 962T609 848T508 777T375 752Q306 752 248 776T147 847T81 961T57 1116Q57 1203 79 1270T143 1384T244 1455T379 1479Q447 1479 504 1455T605 1385T672 1271T696
+1116ZM260 1116Q260 1016 287 966T377 915Q437 915 464 965T492 1116Q492 1216 465 1265T377 1315Q315 1315 288 1266T260 1116Z" />
+<glyph unicode="&#xbb;" glyph-name="guillemotright" horiz-adv-x="1198" d="M1118 547L809 94L590 213L807 561L590 909L809 1028L1118 573V547ZM612 547L303 94L84 213L301 561L84 909L303 1028L612 573V547Z" />
+<glyph unicode="&#xbc;" glyph-name="onequarter" horiz-adv-x="1804" d="M1370 1462L559 0H320L1131 1462H1370ZM794 586H556V1032Q556 1052 556 1082T558 1144T561 1201T564 1239Q558 1231 549 1221T529 1199T508 1178T489 1161L411 1100L302 1227L603 1462H794V586ZM1682
+152H1557V1H1319V152H936V306L1321 883H1557V320H1682V152ZM1319 320V484Q1319 526 1320 572T1325 668Q1320 655 1311 634T1290 590T1268 546T1248 511L1121 320H1319Z" />
+<glyph unicode="&#xbd;" glyph-name="onehalf" horiz-adv-x="1804" d="M1370 1462L559 0H320L1131 1462H1370ZM794 586H556V1032Q556 1052 556 1082T558 1144T561 1201T564 1239Q558 1231 549 1221T529 1199T508 1178T489 1161L411 1100L302 1227L603 1462H794V586ZM1716
+1H1069V169L1293 388Q1339 433 1369 466T1418 526T1444 576T1452 627Q1452 665 1428 685T1364 705Q1324 705 1281 685T1184 617L1061 769Q1126 826 1207 862T1397 898Q1463 898 1517 882T1610 834T1670 756T1692 648Q1692 602 1680 562T1640 480T1571 395T1469
+296L1364 201H1716V1Z" />
+<glyph unicode="&#xbe;" glyph-name="threequarters" horiz-adv-x="1804" d="M1441 1462L630 0H391L1202 1462H1441ZM1712 152H1587V1H1349V152H966V306L1351 883H1587V320H1712V152ZM1349 320V484Q1349 526 1350 572T1355 668Q1350 655 1341 634T1320 590T1298
+546T1278 511L1151 320H1349ZM697 1249Q697 1180 657 1130T527 1051V1038Q578 1028 615 1007T676 959T713 898T725 829Q725 708 637 639T363 569Q287 569 221 586T90 639V829Q156 789 222 764T361 739Q435 739 469 766T504 846Q504 867 496 886T469 919T418 943T338
+952H226V1112H318Q370 1112 402 1121T452 1145T476 1180T482 1221Q482 1259 457 1284T381 1309Q334 1309 292 1290T193 1231L92 1372Q154 1419 229 1450T408 1481Q470 1481 523 1465T614 1418T675 1345T697 1249Z" />
+<glyph unicode="&#xbf;" glyph-name="questiondown" horiz-adv-x="940" d="M686 606V532Q686 481 676 440T644 361T588 288T506 215Q464 182 435 156T388 105T362 51T354 -14Q354 -71 393 -108T510 -145Q579 -145 659 -116T823 -45L926 -266Q883 -292 832 -314T727
+-354T616 -381T506 -391Q404 -391 323 -367T184 -296T97 -182T66 -29Q66 34 79 83T121 175T190 258T287 342Q328 375 354 399T396 446T416 492T422 547V606H686ZM719 948Q719 901 706 869T668 816T612 787T543 778Q508 778 477 787T421 816T383 868T369 948Q369
+993 383 1025T421 1078T476 1108T543 1118Q580 1118 612 1109T668 1079T705 1026T719 948Z" />
+<glyph unicode="&#xc0;" glyph-name="Agrave" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582 928L489
+608H846ZM632 1579Q598 1607 551 1648T457 1734T373 1818T319 1886V1907H661Q677 1873 698 1833T743 1752T790 1673T835 1606V1579H632Z" />
+<glyph unicode="&#xc1;" glyph-name="Aacute" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582 928L489
+608H846ZM494 1579V1606Q515 1635 539 1673T586 1751T631 1832T668 1907H1010V1886Q999 1868 978 1844T930 1791T872 1734T810 1676T749 1623T697 1579H494Z" />
+<glyph unicode="&#xc2;" glyph-name="Acircumflex" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582
+928L489 608H846ZM879 1579Q828 1613 773 1656T666 1755Q612 1699 560 1656T457 1579H254V1606Q280 1635 311 1673T375 1751T438 1832T490 1907H846Q867 1873 897 1833T959 1752T1024 1673T1082 1606V1579H879Z" />
+<glyph unicode="&#xc3;" glyph-name="Atilde" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582 928L489
+608H846ZM504 1684Q473 1684 455 1658T424 1577H275Q281 1657 301 1715T353 1811T430 1867T527 1886Q568 1886 607 1870T684 1835T760 1799T834 1782Q865 1782 883 1808T914 1888H1063Q1057 1809 1037 1751T983 1655T907 1598T811 1579Q771 1579 731 1595T653 1631T578
+1667T504 1684Z" />
+<glyph unicode="&#xc4;" glyph-name="Adieresis" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582 928L489
+608H846ZM324 1743Q324 1778 335 1803T364 1845T408 1870T463 1878Q492 1878 517 1870T562 1846T592 1804T604 1743Q604 1709 593 1684T562 1643T518 1618T463 1610Q434 1610 409 1618T365 1642T335 1684T324 1743ZM727 1743Q727 1778 738 1803T768 1845T813 1870T869
+1878Q897 1878 922 1870T967 1846T998 1804T1010 1743Q1010 1709 999 1684T968 1643T923 1618T869 1610Q809 1610 768 1643T727 1743Z" />
+<glyph unicode="&#xc5;" glyph-name="Aring" horiz-adv-x="1331" d="M1018 0L918 348H414L313 0H0L475 1468H854L1331 0H1018ZM846 608L752 928Q746 946 734 987T709 1077T683 1177T666 1262Q662 1240 656 1210T641 1147T623 1079T606 1015T592 962T582 928L489
+608H846ZM918 1567Q918 1511 899 1467T845 1391T764 1344T664 1327Q609 1327 563 1343T485 1390T434 1465T416 1565Q416 1620 434 1664T484 1738T563 1785T664 1802Q717 1802 763 1786T843 1739T898 1665T918 1567ZM760 1565Q760 1610 733 1635T664 1661Q622 1661
+595 1636T568 1565Q568 1520 592 1494T664 1468Q706 1468 733 1494T760 1565Z" />
+<glyph unicode="&#xc6;" glyph-name="AE" horiz-adv-x="1888" d="M1767 0H926V348H465L315 0H0L655 1462H1767V1208H1235V887H1731V633H1235V256H1767V0ZM578 608H926V1198H829L578 608Z" />
+<glyph unicode="&#xc7;" glyph-name="Ccedilla" horiz-adv-x="1305" d="M805 1225Q716 1225 648 1191T533 1092T462 935T438 727Q438 610 459 519T525 366T639 271T805 238Q894 238 983 258T1178 315V55Q1130 35 1083 21T987 -2T887 -15T776 -20Q607 -20 483 34T278
+186T158 422T119 729Q119 895 164 1033T296 1272T511 1427T805 1483Q914 1483 1023 1456T1233 1380L1133 1128Q1051 1167 968 1196T805 1225ZM926 -250Q926 -307 911 -352T859 -428T764 -475T617 -492Q572 -492 536 -486T471 -471V-303Q486 -307 504 -310T542 -317T580
+-322T614 -324Q643 -324 664 -311T686 -262Q686 -225 649 -197T520 -154L598 0H791L764 -61Q795 -71 824 -88T875 -128T912 -182T926 -250Z" />
+<glyph unicode="&#xc8;" glyph-name="Egrave" horiz-adv-x="1147" d="M1026 0H184V1462H1026V1208H494V887H989V633H494V256H1026V0ZM572 1579Q538 1607 491 1648T397 1734T313 1818T259 1886V1907H601Q617 1873 638 1833T683 1752T730 1673T775 1606V1579H572Z" />
+<glyph unicode="&#xc9;" glyph-name="Eacute" horiz-adv-x="1147" d="M1026 0H184V1462H1026V1208H494V887H989V633H494V256H1026V0ZM424 1579V1606Q445 1635 469 1673T516 1751T561 1832T598 1907H940V1886Q929 1868 908 1844T860 1791T802 1734T740 1676T679
+1623T627 1579H424Z" />
+<glyph unicode="&#xca;" glyph-name="Ecircumflex" horiz-adv-x="1147" d="M1026 0H184V1462H1026V1208H494V887H989V633H494V256H1026V0ZM832 1579Q781 1613 726 1656T619 1755Q565 1699 513 1656T410 1579H207V1606Q233 1635 264 1673T328 1751T391 1832T443
+1907H799Q820 1873 850 1833T912 1752T977 1673T1035 1606V1579H832Z" />
+<glyph unicode="&#xcb;" glyph-name="Edieresis" horiz-adv-x="1147" d="M1026 0H184V1462H1026V1208H494V887H989V633H494V256H1026V0ZM273 1743Q273 1778 284 1803T313 1845T357 1870T412 1878Q441 1878 466 1870T511 1846T541 1804T553 1743Q553 1709 542 1684T511
+1643T467 1618T412 1610Q383 1610 358 1618T314 1642T284 1684T273 1743ZM676 1743Q676 1778 687 1803T717 1845T762 1870T818 1878Q846 1878 871 1870T916 1846T947 1804T959 1743Q959 1709 948 1684T917 1643T872 1618T818 1610Q758 1610 717 1643T676 1743Z"
+/>
+<glyph unicode="&#xcc;" glyph-name="Igrave" horiz-adv-x="797" d="M731 0H66V176L244 258V1204L66 1286V1462H731V1286L553 1204V258L731 176V0ZM355 1579Q321 1607 274 1648T180 1734T96 1818T42 1886V1907H384Q400 1873 421 1833T466 1752T513 1673T558 1606V1579H355Z"
+/>
+<glyph unicode="&#xcd;" glyph-name="Iacute" horiz-adv-x="797" d="M731 0H66V176L244 258V1204L66 1286V1462H731V1286L553 1204V258L731 176V0ZM237 1579V1606Q258 1635 282 1673T329 1751T374 1832T411 1907H753V1886Q742 1868 721 1844T673 1791T615 1734T553
+1676T492 1623T440 1579H237Z" />
+<glyph unicode="&#xce;" glyph-name="Icircumflex" horiz-adv-x="797" d="M731 0H66V176L244 258V1204L66 1286V1462H731V1286L553 1204V258L731 176V0ZM609 1579Q558 1613 503 1656T396 1755Q342 1699 290 1656T187 1579H-16V1606Q10 1635 41 1673T105 1751T168
+1832T220 1907H576Q597 1873 627 1833T689 1752T754 1673T812 1606V1579H609Z" />
+<glyph unicode="&#xcf;" glyph-name="Idieresis" horiz-adv-x="797" d="M731 0H66V176L244 258V1204L66 1286V1462H731V1286L553 1204V258L731 176V0ZM54 1743Q54 1778 65 1803T94 1845T138 1870T193 1878Q222 1878 247 1870T292 1846T322 1804T334 1743Q334 1709
+323 1684T292 1643T248 1618T193 1610Q164 1610 139 1618T95 1642T65 1684T54 1743ZM457 1743Q457 1778 468 1803T498 1845T543 1870T599 1878Q627 1878 652 1870T697 1846T728 1804T740 1743Q740 1709 729 1684T698 1643T653 1618T599 1610Q539 1610 498 1643T457
+1743Z" />
+<glyph unicode="&#xd0;" glyph-name="Eth" horiz-adv-x="1434" d="M47 850H184V1462H612Q773 1462 902 1416T1124 1280T1265 1055T1315 745Q1315 560 1265 421T1119 188T885 47T569 0H184V596H47V850ZM1001 737Q1001 859 977 947T906 1094T792 1180T637 1208H494V850H731V596H494V256H608Q804
+256 902 376T1001 737Z" />
+<glyph unicode="&#xd1;" glyph-name="Ntilde" horiz-adv-x="1604" d="M1419 0H1026L451 1106H442Q448 1029 452 953Q456 888 458 817T461 688V0H184V1462H575L1149 367H1155Q1152 443 1148 517Q1147 549 1146 582T1143 649T1142 714T1141 770V1462H1419V0ZM623
+1684Q592 1684 574 1658T543 1577H394Q400 1657 420 1715T472 1811T549 1867T646 1886Q687 1886 726 1870T803 1835T879 1799T953 1782Q984 1782 1002 1808T1033 1888H1182Q1176 1809 1156 1751T1102 1655T1026 1598T930 1579Q890 1579 850 1595T772 1631T697 1667T623
+1684Z" />
+<glyph unicode="&#xd2;" glyph-name="Ograve" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q944 1485 1067 1432T1270 1280T1390 1043T1430
+733ZM438 733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733ZM729 1579Q695 1607 648 1648T554 1734T470 1818T416 1886V1907H758Q774
+1873 795 1833T840 1752T887 1673T932 1606V1579H729Z" />
+<glyph unicode="&#xd3;" glyph-name="Oacute" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q944 1485 1067 1432T1270 1280T1390 1043T1430
+733ZM438 733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733ZM590 1579V1606Q611 1635 635 1673T682 1751T727 1832T764 1907H1106V1886Q1095
+1868 1074 1844T1026 1791T968 1734T906 1676T845 1623T793 1579H590Z" />
+<glyph unicode="&#xd4;" glyph-name="Ocircumflex" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q944 1485 1067 1432T1270 1280T1390
+1043T1430 733ZM438 733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733ZM975 1579Q924 1613 869 1656T762 1755Q708 1699
+656 1656T553 1579H350V1606Q376 1635 407 1673T471 1751T534 1832T586 1907H942Q963 1873 993 1833T1055 1752T1120 1673T1178 1606V1579H975Z" />
+<glyph unicode="&#xd5;" glyph-name="Otilde" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q944 1485 1067 1432T1270 1280T1390 1043T1430
+733ZM438 733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733ZM612 1684Q581 1684 563 1658T532 1577H383Q389 1657 409 1715T461
+1811T538 1867T635 1886Q676 1886 715 1870T792 1835T868 1799T942 1782Q973 1782 991 1808T1022 1888H1171Q1165 1809 1145 1751T1091 1655T1015 1598T919 1579Q879 1579 839 1595T761 1631T686 1667T612 1684Z" />
+<glyph unicode="&#xd6;" glyph-name="Odieresis" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q606 -20 483 34T279 187T159 425T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q944 1485 1067 1432T1270 1280T1390 1043T1430
+733ZM438 733Q438 618 458 527T519 372T624 274T774 240Q863 240 926 274T1030 371T1090 526T1110 733Q1110 848 1091 939T1031 1095T927 1193T776 1227Q689 1227 625 1193T520 1095T458 940T438 733ZM428 1743Q428 1778 439 1803T468 1845T512 1870T567 1878Q596
+1878 621 1870T666 1846T696 1804T708 1743Q708 1709 697 1684T666 1643T622 1618T567 1610Q538 1610 513 1618T469 1642T439 1684T428 1743ZM831 1743Q831 1778 842 1803T872 1845T917 1870T973 1878Q1001 1878 1026 1870T1071 1846T1102 1804T1114 1743Q1114
+1709 1103 1684T1072 1643T1027 1618T973 1610Q913 1610 872 1643T831 1743Z" />
+<glyph unicode="&#xd7;" glyph-name="multiply" horiz-adv-x="1128" d="M408 723L109 1024L260 1178L561 879L866 1178L1020 1028L715 723L1016 420L866 268L561 569L260 270L111 422L408 723Z" />
+<glyph unicode="&#xd8;" glyph-name="Oslash" horiz-adv-x="1548" d="M1430 733Q1430 564 1391 425T1270 187T1066 34T774 -20Q595 -20 467 41L395 -76L227 18L309 152Q212 252 166 400T119 735Q119 905 158 1043T279 1280T483 1431T776 1485Q867 1485 944 1469T1087
+1421L1157 1532L1323 1436L1243 1307Q1337 1208 1383 1063T1430 733ZM438 733Q438 553 485 438L942 1184Q873 1227 776 1227Q689 1227 625 1193T520 1095T458 940T438 733ZM1110 733Q1110 904 1067 1020L612 279Q646 260 686 250T774 240Q863 240 926 274T1030
+371T1090 526T1110 733Z" />
+<glyph unicode="&#xd9;" glyph-name="Ugrave" horiz-adv-x="1466" d="M1292 1462V516Q1292 402 1258 304T1153 134T976 21T727 -20Q592 -20 489 18T316 128T210 298T174 520V1462H483V543Q483 462 499 405T546 311T625 257T735 240Q866 240 924 316T983 545V1462H1292ZM706
+1579Q672 1607 625 1648T531 1734T447 1818T393 1886V1907H735Q751 1873 772 1833T817 1752T864 1673T909 1606V1579H706Z" />
+<glyph unicode="&#xda;" glyph-name="Uacute" horiz-adv-x="1466" d="M1292 1462V516Q1292 402 1258 304T1153 134T976 21T727 -20Q592 -20 489 18T316 128T210 298T174 520V1462H483V543Q483 462 499 405T546 311T625 257T735 240Q866 240 924 316T983 545V1462H1292ZM570
+1579V1606Q591 1635 615 1673T662 1751T707 1832T744 1907H1086V1886Q1075 1868 1054 1844T1006 1791T948 1734T886 1676T825 1623T773 1579H570Z" />
+<glyph unicode="&#xdb;" glyph-name="Ucircumflex" horiz-adv-x="1466" d="M1292 1462V516Q1292 402 1258 304T1153 134T976 21T727 -20Q592 -20 489 18T316 128T210 298T174 520V1462H483V543Q483 462 499 405T546 311T625 257T735 240Q866 240 924 316T983 545V1462H1292ZM942
+1579Q891 1613 836 1656T729 1755Q675 1699 623 1656T520 1579H317V1606Q343 1635 374 1673T438 1751T501 1832T553 1907H909Q930 1873 960 1833T1022 1752T1087 1673T1145 1606V1579H942Z" />
+<glyph unicode="&#xdc;" glyph-name="Udieresis" horiz-adv-x="1466" d="M1292 1462V516Q1292 402 1258 304T1153 134T976 21T727 -20Q592 -20 489 18T316 128T210 298T174 520V1462H483V543Q483 462 499 405T546 311T625 257T735 240Q866 240 924 316T983 545V1462H1292ZM393
+1743Q393 1778 404 1803T433 1845T477 1870T532 1878Q561 1878 586 1870T631 1846T661 1804T673 1743Q673 1709 662 1684T631 1643T587 1618T532 1610Q503 1610 478 1618T434 1642T404 1684T393 1743ZM796 1743Q796 1778 807 1803T837 1845T882 1870T938 1878Q966
+1878 991 1870T1036 1846T1067 1804T1079 1743Q1079 1709 1068 1684T1037 1643T992 1618T938 1610Q878 1610 837 1643T796 1743Z" />
+<glyph unicode="&#xdd;" glyph-name="Yacute" horiz-adv-x="1196" d="M598 860L862 1462H1196L752 569V0H444V559L0 1462H336L598 860ZM422 1579V1606Q443 1635 467 1673T514 1751T559 1832T596 1907H938V1886Q927 1868 906 1844T858 1791T800 1734T738 1676T677
+1623T625 1579H422Z" />
+<glyph unicode="&#xde;" glyph-name="Thorn" horiz-adv-x="1225" d="M1133 770Q1133 676 1108 590T1024 438T870 333T633 293H494V0H184V1462H494V1233H655Q779 1233 869 1200T1017 1107T1104 961T1133 770ZM494 543H578Q699 543 759 595T819 770Q819 878 766
+929T598 981H494V543Z" />
+<glyph unicode="&#xdf;" glyph-name="germandbls" horiz-adv-x="1395" d="M1188 1241Q1188 1177 1167 1129T1114 1042T1045 975T976 922T923 877T901 834Q901 814 913 797T952 760T1020 715T1118 651Q1167 620 1205 588T1269 517T1309 432T1323 326Q1323 154 1206
+67T862 -20Q764 -20 692 -6T559 43V285Q583 269 617 254T690 226T768 207T842 199Q922 199 966 229T1010 322Q1010 349 1003 370T976 412T918 457T821 516Q758 552 716 584T647 647T609 713T598 788Q598 841 618 880T670 950T737 1007T805 1059T856 1117T877 1188Q877
+1251 827 1290T680 1329Q572 1329 519 1281T465 1128V0H160V1139Q160 1248 197 1328T302 1462T467 1541T680 1567Q795 1567 889 1546T1049 1483T1152 1380T1188 1241Z" />
+<glyph unicode="&#xe0;" glyph-name="agrave" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289 838L190
+1040Q274 1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM808 1241Q774 1269 727 1310T633 1396T549 1480T495 1548V1569H837Q853
+1535 874 1495T919 1414T966 1335T1011 1268V1241H808Z" />
+<glyph unicode="&#xe1;" glyph-name="aacute" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289 838L190
+1040Q274 1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM441 1241V1268Q462 1297 486 1335T533 1413T578 1494T615
+1569H957V1548Q946 1530 925 1506T877 1453T819 1396T757 1338T696 1285T644 1241H441Z" />
+<glyph unicode="&#xe2;" glyph-name="acircumflex" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289
+838L190 1040Q274 1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM801 1496Q750 1530 695 1573T588 1672Q534 1616
+482 1573T379 1496H176V1523Q202 1552 233 1590T297 1668T360 1749T412 1824H768Q789 1790 819 1750T881 1669T946 1590T1004 1523V1496H801Z" />
+<glyph unicode="&#xe3;" glyph-name="atilde" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289 838L190
+1040Q274 1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM681 1346Q650 1346 632 1320T601 1239H452Q458 1319 478
+1377T530 1473T607 1529T704 1548Q745 1548 784 1532T861 1497T937 1461T1011 1444Q1042 1444 1060 1470T1091 1550H1240Q1234 1471 1214 1413T1160 1317T1084 1260T988 1241Q948 1241 908 1257T830 1293T755 1329T681 1346Z" />
+<glyph unicode="&#xe4;" glyph-name="adieresis" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289
+838L190 1040Q274 1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM254 1405Q254 1440 265 1465T294 1507T338 1532T393
+1540Q422 1540 447 1532T492 1508T522 1466T534 1405Q534 1371 523 1346T492 1305T448 1280T393 1272Q364 1272 339 1280T295 1304T265 1346T254 1405ZM657 1405Q657 1440 668 1465T698 1507T743 1532T799 1540Q827 1540 852 1532T897 1508T928 1466T940 1405Q940
+1371 929 1346T898 1305T853 1280T799 1272Q739 1272 698 1305T657 1405Z" />
+<glyph unicode="&#xe5;" glyph-name="aring" horiz-adv-x="1176" d="M809 0L750 152H741Q708 107 675 75T603 21T516 -10T403 -20Q335 -20 277 1T177 66T110 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427 894T289 838L190
+1040Q274 1087 376 1114T590 1141Q799 1141 910 1043T1022 745V0H809ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM842 1479Q842 1423 823 1379T769 1303T688 1256T588 1239Q533
+1239 487 1255T409 1302T358 1377T340 1477Q340 1532 358 1576T408 1650T487 1697T588 1714Q641 1714 687 1698T767 1651T822 1577T842 1479ZM684 1477Q684 1522 657 1547T588 1573Q546 1573 519 1548T492 1477Q492 1432 516 1406T588 1380Q630 1380 657 1406T684
+1477Z" />
+<glyph unicode="&#xe6;" glyph-name="ae" horiz-adv-x="1806" d="M1268 -20Q1137 -20 1030 30T854 186Q811 132 769 94T678 30T568 -8T424 -20Q356 -20 295 1T187 66T113 176T86 334Q86 512 200 596T541 690L719 696V780Q719 849 679 882T567 915Q495 915 427
+894T289 838L190 1040Q274 1087 376 1114T590 1141Q804 1141 913 1010Q1039 1139 1227 1139Q1338 1139 1427 1106T1579 1007T1674 848T1708 631V483H1026Q1028 419 1046 368T1098 281T1179 226T1288 207Q1382 207 1469 228T1645 295V59Q1605 38 1565 24T1479 -1T1382
+-15T1268 -20ZM719 518L618 514Q557 512 515 498T448 461T411 405T399 332Q399 262 433 233T522 203Q564 203 600 217T662 260T704 330T719 426V518ZM1229 922Q1147 922 1094 865T1032 686H1421Q1420 737 1408 780T1373 854T1313 904T1229 922Z" />
+<glyph unicode="&#xe7;" glyph-name="ccedilla" horiz-adv-x="1022" d="M625 -20Q505 -20 409 13T244 115T139 293T102 553Q102 720 139 832T245 1013T410 1110T625 1139Q711 1139 796 1118T956 1059L868 827Q802 856 741 874T625 893Q514 893 464 809T414 555Q414
+387 464 307T621 227Q708 227 779 249T924 307V53Q887 35 852 21T782 -2T708 -15T625 -20ZM778 -250Q778 -307 763 -352T711 -428T616 -475T469 -492Q424 -492 388 -486T323 -471V-303Q338 -307 356 -310T394 -317T432 -322T466 -324Q495 -324 516 -311T538 -262Q538
+-225 501 -197T372 -154L450 0H643L616 -61Q647 -71 676 -88T727 -128T764 -182T778 -250Z" />
+<glyph unicode="&#xe8;" glyph-name="egrave" horiz-adv-x="1190" d="M612 922Q531 922 478 865T416 686H805Q804 737 792 780T756 854T696 904T612 922ZM651 -20Q531 -20 430 15T256 120T143 298T102 551Q102 698 139 808T242 991T402 1102T610 1139Q721 1139
+810 1106T962 1007T1058 848T1092 631V483H410Q412 419 430 368T482 281T563 226T672 207Q723 207 768 212T857 229T942 256T1028 295V59Q988 38 948 24T862 -1T765 -15T651 -20ZM834 1241Q800 1269 753 1310T659 1396T575 1480T521 1548V1569H863Q879 1535 900
+1495T945 1414T992 1335T1037 1268V1241H834Z" />
+<glyph unicode="&#xe9;" glyph-name="eacute" horiz-adv-x="1190" d="M612 922Q531 922 478 865T416 686H805Q804 737 792 780T756 854T696 904T612 922ZM651 -20Q531 -20 430 15T256 120T143 298T102 551Q102 698 139 808T242 991T402 1102T610 1139Q721 1139
+810 1106T962 1007T1058 848T1092 631V483H410Q412 419 430 368T482 281T563 226T672 207Q723 207 768 212T857 229T942 256T1028 295V59Q988 38 948 24T862 -1T765 -15T651 -20ZM447 1241V1268Q468 1297 492 1335T539 1413T584 1494T621 1569H963V1548Q952 1530
+931 1506T883 1453T825 1396T763 1338T702 1285T650 1241H447Z" />
+<glyph unicode="&#xea;" glyph-name="ecircumflex" horiz-adv-x="1190" d="M612 922Q531 922 478 865T416 686H805Q804 737 792 780T756 854T696 904T612 922ZM651 -20Q531 -20 430 15T256 120T143 298T102 551Q102 698 139 808T242 991T402 1102T610 1139Q721
+1139 810 1106T962 1007T1058 848T1092 631V483H410Q412 419 430 368T482 281T563 226T672 207Q723 207 768 212T857 229T942 256T1028 295V59Q988 38 948 24T862 -1T765 -15T651 -20ZM819 1241Q768 1275 713 1318T606 1417Q552 1361 500 1318T397 1241H194V1268Q220
+1297 251 1335T315 1413T378 1494T430 1569H786Q807 1535 837 1495T899 1414T964 1335T1022 1268V1241H819Z" />
+<glyph unicode="&#xeb;" glyph-name="edieresis" horiz-adv-x="1190" d="M612 922Q531 922 478 865T416 686H805Q804 737 792 780T756 854T696 904T612 922ZM651 -20Q531 -20 430 15T256 120T143 298T102 551Q102 698 139 808T242 991T402 1102T610 1139Q721 1139
+810 1106T962 1007T1058 848T1092 631V483H410Q412 419 430 368T482 281T563 226T672 207Q723 207 768 212T857 229T942 256T1028 295V59Q988 38 948 24T862 -1T765 -15T651 -20ZM266 1405Q266 1440 277 1465T306 1507T350 1532T405 1540Q434 1540 459 1532T504
+1508T534 1466T546 1405Q546 1371 535 1346T504 1305T460 1280T405 1272Q376 1272 351 1280T307 1304T277 1346T266 1405ZM669 1405Q669 1440 680 1465T710 1507T755 1532T811 1540Q839 1540 864 1532T909 1508T940 1466T952 1405Q952 1371 941 1346T910 1305T865
+1280T811 1272Q751 1272 710 1305T669 1405Z" />
+<glyph unicode="&#xec;" glyph-name="igrave" horiz-adv-x="625" d="M465 0H160V1118H465V0ZM269 1241Q235 1269 188 1310T94 1396T10 1480T-44 1548V1569H298Q314 1535 335 1495T380 1414T427 1335T472 1268V1241H269Z" />
+<glyph unicode="&#xed;" glyph-name="iacute" horiz-adv-x="625" d="M465 0H160V1118H465V0ZM145 1241V1268Q166 1297 190 1335T237 1413T282 1494T319 1569H661V1548Q650 1530 629 1506T581 1453T523 1396T461 1338T400 1285T348 1241H145Z" />
+<glyph unicode="&#xee;" glyph-name="icircumflex" horiz-adv-x="625" d="M465 0H160V1118H465V0ZM521 1241Q470 1275 415 1318T308 1417Q254 1361 202 1318T99 1241H-104V1268Q-78 1297 -47 1335T17 1413T80 1494T132 1569H488Q509 1535 539 1495T601 1414T666
+1335T724 1268V1241H521Z" />
+<glyph unicode="&#xef;" glyph-name="idieresis" horiz-adv-x="625" d="M465 0H160V1118H465V0ZM-32 1405Q-32 1440 -21 1465T8 1507T52 1532T107 1540Q136 1540 161 1532T206 1508T236 1466T248 1405Q248 1371 237 1346T206 1305T162 1280T107 1272Q78 1272 53
+1280T9 1304T-21 1346T-32 1405ZM371 1405Q371 1440 382 1465T412 1507T457 1532T513 1540Q541 1540 566 1532T611 1508T642 1466T654 1405Q654 1371 643 1346T612 1305T567 1280T513 1272Q453 1272 412 1305T371 1405Z" />
+<glyph unicode="&#xf0;" glyph-name="eth" horiz-adv-x="1182" d="M457 1309Q423 1330 384 1354T303 1401L399 1571Q472 1537 536 1503T657 1430L883 1569L983 1415L809 1309Q881 1240 935 1162T1024 993T1078 798T1096 573Q1096 431 1060 321T957 135T795 20T582
+-20Q471 -20 378 14T218 113T112 272T74 489Q74 611 106 705T197 863T337 961T516 995Q612 995 680 964T780 883L801 885Q773 973 723 1050T606 1184L375 1040L274 1196L457 1309ZM784 532Q784 579 773 622T737 698T675 750T586 770Q478 770 432 700T385 487Q385
+424 396 372T432 283T495 226T586 205Q692 205 738 286T784 532Z" />
+<glyph unicode="&#xf1;" glyph-name="ntilde" horiz-adv-x="1284" d="M1130 0H825V653Q825 774 789 834T672 895Q612 895 572 871T509 800T475 684T465 526V0H160V1118H393L434 975H451Q475 1018 509 1049T585 1100T672 1129T766 1139Q848 1139 915 1116T1030
+1042T1104 915T1130 729V0ZM477 1346Q446 1346 428 1320T397 1239H248Q254 1319 274 1377T326 1473T403 1529T500 1548Q541 1548 580 1532T657 1497T733 1461T807 1444Q838 1444 856 1470T887 1550H1036Q1030 1471 1010 1413T956 1317T880 1260T784 1241Q744 1241
+704 1257T626 1293T551 1329T477 1346Z" />
+<glyph unicode="&#xf2;" glyph-name="ograve" horiz-adv-x="1227" d="M414 561Q414 394 461 310T614 225Q719 225 766 310T813 561Q813 728 766 810T612 893Q507 893 461 811T414 561ZM1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q499 -20 406 18T246
+131T140 313T102 561Q102 700 137 808T239 989T401 1101T616 1139Q727 1139 820 1101T980 990T1086 808T1124 561ZM841 1241Q807 1269 760 1310T666 1396T582 1480T528 1548V1569H870Q886 1535 907 1495T952 1414T999 1335T1044 1268V1241H841Z" />
+<glyph unicode="&#xf3;" glyph-name="oacute" horiz-adv-x="1227" d="M414 561Q414 394 461 310T614 225Q719 225 766 310T813 561Q813 728 766 810T612 893Q507 893 461 811T414 561ZM1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q499 -20 406 18T246
+131T140 313T102 561Q102 700 137 808T239 989T401 1101T616 1139Q727 1139 820 1101T980 990T1086 808T1124 561ZM434 1241V1268Q455 1297 479 1335T526 1413T571 1494T608 1569H950V1548Q939 1530 918 1506T870 1453T812 1396T750 1338T689 1285T637 1241H434Z"
+/>
+<glyph unicode="&#xf4;" glyph-name="ocircumflex" horiz-adv-x="1227" d="M414 561Q414 394 461 310T614 225Q719 225 766 310T813 561Q813 728 766 810T612 893Q507 893 461 811T414 561ZM1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q499 -20 406 18T246
+131T140 313T102 561Q102 700 137 808T239 989T401 1101T616 1139Q727 1139 820 1101T980 990T1086 808T1124 561ZM821 1241Q770 1275 715 1318T608 1417Q554 1361 502 1318T399 1241H196V1268Q222 1297 253 1335T317 1413T380 1494T432 1569H788Q809 1535 839
+1495T901 1414T966 1335T1024 1268V1241H821Z" />
+<glyph unicode="&#xf5;" glyph-name="otilde" horiz-adv-x="1227" d="M414 561Q414 394 461 310T614 225Q719 225 766 310T813 561Q813 728 766 810T612 893Q507 893 461 811T414 561ZM1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q499 -20 406 18T246
+131T140 313T102 561Q102 700 137 808T239 989T401 1101T616 1139Q727 1139 820 1101T980 990T1086 808T1124 561ZM444 1346Q413 1346 395 1320T364 1239H215Q221 1319 241 1377T293 1473T370 1529T467 1548Q508 1548 547 1532T624 1497T700 1461T774 1444Q805
+1444 823 1470T854 1550H1003Q997 1471 977 1413T923 1317T847 1260T751 1241Q711 1241 671 1257T593 1293T518 1329T444 1346Z" />
+<glyph unicode="&#xf6;" glyph-name="odieresis" horiz-adv-x="1227" d="M414 561Q414 394 461 310T614 225Q719 225 766 310T813 561Q813 728 766 810T612 893Q507 893 461 811T414 561ZM1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q499 -20 406 18T246
+131T140 313T102 561Q102 700 137 808T239 989T401 1101T616 1139Q727 1139 820 1101T980 990T1086 808T1124 561ZM266 1405Q266 1440 277 1465T306 1507T350 1532T405 1540Q434 1540 459 1532T504 1508T534 1466T546 1405Q546 1371 535 1346T504 1305T460 1280T405
+1272Q376 1272 351 1280T307 1304T277 1346T266 1405ZM669 1405Q669 1440 680 1465T710 1507T755 1532T811 1540Q839 1540 864 1532T909 1508T940 1466T952 1405Q952 1371 941 1346T910 1305T865 1280T811 1272Q751 1272 710 1305T669 1405Z" />
+<glyph unicode="&#xf7;" glyph-name="divide" horiz-adv-x="1128" d="M88 612V831H1040V612H88ZM424 373Q424 415 435 444T465 490T509 516T563 524Q591 524 616 516T660 491T690 444T702 373Q702 333 691 304T660 257T616 230T563 221Q535 221 510 229T465 256T435
+304T424 373ZM424 1071Q424 1113 435 1142T465 1189T509 1215T563 1223Q591 1223 616 1215T660 1189T690 1142T702 1071Q702 1031 691 1003T660 956T616 929T563 920Q535 920 510 928T465 955T435 1002T424 1071Z" />
+<glyph unicode="&#xf8;" glyph-name="oslash" horiz-adv-x="1227" d="M1124 561Q1124 421 1089 313T987 131T825 19T610 -20Q553 -20 501 -10T401 18L344 -76L182 14L250 125Q181 199 142 308T102 561Q102 700 137 808T239 989T401 1101T616 1139Q678 1139 735
+1126T844 1090L893 1169L1053 1073L991 975Q1054 903 1089 799T1124 561ZM414 561Q414 475 426 410L709 868Q669 893 612 893Q507 893 461 811T414 561ZM813 561Q813 625 807 674L539 240Q556 232 574 229T614 225Q719 225 766 310T813 561Z" />
+<glyph unicode="&#xf9;" glyph-name="ugrave" horiz-adv-x="1284" d="M891 0L850 143H834Q809 100 775 70T699 19T612 -10T518 -20Q436 -20 369 3T254 77T180 204T154 389V1118H459V465Q459 344 495 284T612 223Q672 223 712 247T775 318T809 434T819 592V1118H1124V0H891ZM839
+1241Q805 1269 758 1310T664 1396T580 1480T526 1548V1569H868Q884 1535 905 1495T950 1414T997 1335T1042 1268V1241H839Z" />
+<glyph unicode="&#xfa;" glyph-name="uacute" horiz-adv-x="1284" d="M891 0L850 143H834Q809 100 775 70T699 19T612 -10T518 -20Q436 -20 369 3T254 77T180 204T154 389V1118H459V465Q459 344 495 284T612 223Q672 223 712 247T775 318T809 434T819 592V1118H1124V0H891ZM461
+1241V1268Q482 1297 506 1335T553 1413T598 1494T635 1569H977V1548Q966 1530 945 1506T897 1453T839 1396T777 1338T716 1285T664 1241H461Z" />
+<glyph unicode="&#xfb;" glyph-name="ucircumflex" horiz-adv-x="1284" d="M891 0L850 143H834Q809 100 775 70T699 19T612 -10T518 -20Q436 -20 369 3T254 77T180 204T154 389V1118H459V465Q459 344 495 284T612 223Q672 223 712 247T775 318T809 434T819 592V1118H1124V0H891ZM842
+1241Q791 1275 736 1318T629 1417Q575 1361 523 1318T420 1241H217V1268Q243 1297 274 1335T338 1413T401 1494T453 1569H809Q830 1535 860 1495T922 1414T987 1335T1045 1268V1241H842Z" />
+<glyph unicode="&#xfc;" glyph-name="udieresis" horiz-adv-x="1284" d="M891 0L850 143H834Q809 100 775 70T699 19T612 -10T518 -20Q436 -20 369 3T254 77T180 204T154 389V1118H459V465Q459 344 495 284T612 223Q672 223 712 247T775 318T809 434T819 592V1118H1124V0H891ZM295
+1405Q295 1440 306 1465T335 1507T379 1532T434 1540Q463 1540 488 1532T533 1508T563 1466T575 1405Q575 1371 564 1346T533 1305T489 1280T434 1272Q405 1272 380 1280T336 1304T306 1346T295 1405ZM698 1405Q698 1440 709 1465T739 1507T784 1532T840 1540Q868
+1540 893 1532T938 1508T969 1466T981 1405Q981 1371 970 1346T939 1305T894 1280T840 1272Q780 1272 739 1305T698 1405Z" />
+<glyph unicode="&#xfd;" glyph-name="yacute" horiz-adv-x="1104" d="M0 1118H334L514 489Q530 437 537 378T547 272H553Q555 295 558 323T567 380T578 437T592 489L768 1118H1104L662 -143Q600 -320 493 -406T225 -492Q173 -492 135 -487T70 -475V-233Q91 -238
+123 -242T190 -246Q238 -246 272 -233T330 -197T372 -140T403 -66L422 -10L0 1118ZM393 1241V1268Q414 1297 438 1335T485 1413T530 1494T567 1569H909V1548Q898 1530 877 1506T829 1453T771 1396T709 1338T648 1285T596 1241H393Z" />
+<glyph unicode="&#xfe;" glyph-name="thorn" horiz-adv-x="1245" d="M465 973Q485 1008 512 1038T576 1090T656 1126T756 1139Q842 1139 913 1102T1035 992T1114 811T1143 561Q1143 418 1115 310T1036 128T914 17T756 -20Q701 -20 656 -10T576 20T513 64T465 117H451Q454
+85 458 55Q461 29 463 3T465 -39V-492H160V1556H465V1165Q465 1141 463 1108T458 1045Q454 1010 451 973H465ZM653 895Q602 895 567 877T509 821T477 728T465 596V563Q465 482 474 419T506 314T564 249T655 227Q746 227 788 313T831 565Q831 730 789 812T653 895Z"
+/>
+<glyph unicode="&#xff;" glyph-name="ydieresis" horiz-adv-x="1104" d="M0 1118H334L514 489Q530 437 537 378T547 272H553Q555 295 558 323T567 380T578 437T592 489L768 1118H1104L662 -143Q600 -320 493 -406T225 -492Q173 -492 135 -487T70 -475V-233Q91
+-238 123 -242T190 -246Q238 -246 272 -233T330 -197T372 -140T403 -66L422 -10L0 1118ZM466 1405Q466 1440 477 1465T506 1507T550 1532T605 1540Q634 1540 659 1532T704 1508T734 1466T746 1405Q746 1371 735 1346T704 1305T660 1280T605 1272Q576 1272 551 1280T507
+1304T477 1346T466 1405ZM869 1405Q869 1440 880 1465T910 1507T955 1532T1011 1540Q1039 1540 1064 1532T1109 1508T1140 1466T1152 1405Q1152 1371 1141 1346T1110 1305T1065 1280T1011 1272Q951 1272 910 1305T869 1405Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="1024" d="M82 436V666H942V436H82Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="2048" d="M82 436V666H1966V436H82Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="440" d="M37 961L23 983Q37 1037 56 1098T99 1221T148 1344T199 1462H418Q403 1401 389 1335T361 1204T336 1076T317 961H37Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="440" d="M403 1462L418 1440Q404 1385 385 1325T342 1202T293 1078T242 961H23Q37 1021 51 1087T79 1219T104 1347T123 1462H403Z" />
+<glyph unicode="&#x201a;" glyph-name="quotesinglbase" horiz-adv-x="594" d="M459 215Q445 161 426 100T383 -23T334 -146T283 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H444L459 215Z" />
+<glyph unicode="&#x201c;" glyph-name="quotedblleft" horiz-adv-x="907" d="M489 983Q503 1037 523 1098T566 1221T615 1344T666 1462H885Q870 1401 856 1335T828 1204T803 1076T784 961H504L489 983ZM23 983Q37 1037 56 1098T99 1221T148 1344T199 1462H418Q403
+1401 389 1335T361 1204T336 1076T317 961H37L23 983Z" />
+<glyph unicode="&#x201d;" glyph-name="quotedblright" horiz-adv-x="907" d="M418 1440Q404 1385 385 1325T342 1202T293 1078T242 961H23Q37 1021 51 1087T79 1219T104 1347T123 1462H403L418 1440ZM885 1440Q871 1385 852 1325T809 1202T760 1078T709 961H489Q504
+1021 518 1087T546 1219T571 1347T590 1462H870L885 1440Z" />
+<glyph unicode="&#x201e;" glyph-name="quotedblbase" horiz-adv-x="1061" d="M459 215Q445 161 426 100T383 -23T334 -146T283 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H444L459 215ZM926 215Q912 161 893 100T850 -23T801 -146T750 -264H530Q545 -203
+559 -137T587 -6T612 122T631 238H911L926 215Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="770" d="M98 748Q98 834 120 894T180 992T271 1047T385 1065Q444 1065 496 1048T588 992T649 894T672 748Q672 663 650 603T588 505T497 448T385 430Q324 430 272 448T181 504T120 603T98 748Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="692" d="M82 573L391 1028L610 909L393 561L610 213L391 94L82 547V573Z" />
+<glyph unicode="&#x203a;" glyph-name="guilsinglright" horiz-adv-x="692" d="M610 547L301 94L82 213L299 561L82 909L301 1028L610 573V547Z" />
+</font>
+</defs>
+</svg>
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.ttf b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.ttf
new file mode 100644
index 0000000000..15896c441f
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.ttf
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff
new file mode 100644
index 0000000000..67e3e25f83
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff2 b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff2
new file mode 100644
index 0000000000..1e726a7cfc
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-700.woff2
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.eot b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.eot
new file mode 100644
index 0000000000..ac2698e85a
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.eot
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.svg b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.svg
new file mode 100644
index 0000000000..d9f2a214f9
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.svg
@@ -0,0 +1,403 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg">
+<defs >
+<font id="DroidSans" horiz-adv-x="1062" ><font-face
+ font-family="Droid Sans"
+ units-per-em="2048"
+ panose-1="2 11 6 6 3 8 4 2 2 4"
+ ascent="1907"
+ descent="-492"
+ alphabetic="0" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="532" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="551" d="M336 414H215L164 1462H387L336 414ZM147 111Q147 149 157 175T184 218T224 242T274 250Q300 250 323 243T364 219T391 176T401 111Q401 74 391 48T364 4T324 -21T274 -29Q247 -29 224 -21T184 4T157
+47T147 111Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="823" d="M330 1462L289 934H174L133 1462H330ZM690 1462L649 934H535L494 1462H690Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="1323" d="M983 893L920 565H1200V428H893L811 0H664L748 428H457L375 0H231L309 428H51V565H336L401 893H127V1030H426L508 1462H655L573 1030H866L950 1462H1094L1010 1030H1272V893H983ZM483 565H774L838
+893H547L483 565Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="1128" d="M985 446Q985 376 960 319T889 220T776 151T625 111V-119H487V102Q437 102 386 106T287 120T197 142T123 172V344Q156 328 199 312T291 282T389 261T487 252V686Q398 716 333 749T224 824T160 922T139
+1051Q139 1118 163 1173T233 1270T343 1338T487 1374V1554H625V1378Q725 1373 809 1352T961 1300L895 1155Q839 1180 769 1200T625 1227V805Q713 774 780 741T893 667T962 572T985 446ZM809 446Q809 479 799 506T768 556T711 598T625 635V262Q718 276 763 325T809
+446ZM315 1049Q315 1013 323 985T352 933T405 890T487 854V1223Q398 1207 357 1163T315 1049Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="1690" d="M250 1026Q250 861 285 779T401 696Q557 696 557 1026Q557 1354 401 1354Q321 1354 286 1273T250 1026ZM705 1026Q705 918 687 832T632 687T538 597T401 565Q328 565 272 596T178 687T121 832T102
+1026Q102 1134 119 1219T173 1362T266 1452T401 1483Q476 1483 532 1452T627 1363T685 1219T705 1026ZM1133 440Q1133 275 1168 193T1284 111Q1440 111 1440 440Q1440 768 1284 768Q1204 768 1169 687T1133 440ZM1587 440Q1587 332 1570 247T1515 102T1421 12T1284
+-20Q1210 -20 1154 11T1061 102T1004 246T985 440Q985 548 1002 633T1056 776T1149 866T1284 897Q1359 897 1415 866T1510 777T1567 633T1587 440ZM1331 1462L520 0H362L1174 1462H1331Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="1438" d="M422 1165Q422 1131 430 1099T454 1034T497 968T559 897Q618 932 661 963T732 1026T774 1093T788 1169Q788 1205 776 1235T740 1288T683 1322T608 1335Q522 1335 472 1291T422 1165ZM557
+141Q615 141 664 152T755 184T833 231T901 289L514 696Q462 663 422 632T355 564T313 486T299 387Q299 333 316 288T367 210T448 159T557 141ZM109 381Q109 459 129 520T187 631T281 724T408 809Q377 845 347 883T295 965T258 1058T244 1165Q244 1240 269 1299T341
+1400T457 1463T614 1485Q697 1485 762 1464T873 1401T943 1300T967 1165Q967 1101 942 1047T875 946T779 860T664 784L1016 412Q1043 441 1064 471T1103 535T1133 608T1157 694H1341Q1326 628 1306 573T1259 468T1200 377T1128 293L1405 0H1180L1012 172Q963 127
+915 92T813 32T697 -6T557 -20Q452 -20 369 6T228 84T140 210T109 381Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="463" d="M330 1462L289 934H174L133 1462H330Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="616" d="M82 561Q82 686 100 807T155 1043T248 1263T383 1462H555Q415 1269 343 1038T270 563Q270 444 288 326T342 95T431 -124T553 -324H383Q305 -234 249 -131T155 84T100 317T82 561Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="616" d="M535 561Q535 437 517 317T462 85T368 -131T233 -324H63Q132 -230 185 -124T274 95T328 326T346 563Q346 807 274 1038T61 1462H233Q311 1369 367 1264T461 1044T517 808T535 561Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="1128" d="M664 1556L621 1163L1018 1274L1044 1081L666 1053L911 727L733 631L557 989L399 631L215 727L457 1053L82 1081L111 1274L502 1163L459 1556H664Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="1128" d="M489 647H102V797H489V1186H639V797H1026V647H639V262H489V647Z" />
+<glyph unicode="," glyph-name="comma" horiz-adv-x="512" d="M362 238L377 215Q363 161 344 100T301 -23T252 -146T201 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H362Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="659" d="M82 465V633H578V465H82Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="549" d="M147 111Q147 149 157 175T184 218T224 242T274 250Q300 250 323 243T364 219T391 176T401 111Q401 74 391 48T364 4T324 -21T274 -29Q247 -29 224 -21T184 4T157 47T147 111Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="764" d="M743 1462L199 0H20L565 1462H743Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="1128" d="M1032 733Q1032 556 1007 416T925 179T779 31T563 -20Q445 -20 358 31T213 179T127 416T98 733Q98 910 123 1050T204 1286T348 1434T563 1485Q682 1485 770 1435T916 1288T1003 1051T1032 733ZM283
+733Q283 583 298 471T346 285T432 173T563 135Q640 135 694 172T782 283T832 469T848 733Q848 883 833 995T783 1181T694 1292T563 1329Q486 1329 433 1292T346 1181T298 995T283 733Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="1128" d="M711 0H535V913Q535 956 535 1005T537 1102T540 1195T543 1274Q526 1256 513 1243T487 1218T458 1193T422 1161L274 1040L178 1163L561 1462H711V0Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="1128" d="M1008 0H96V156L446 537Q521 618 580 685T680 816T744 944T766 1085Q766 1144 749 1189T701 1265T626 1313T530 1329Q435 1329 359 1291T213 1192L111 1311Q151 1347 197 1378T296 1433T408 1469T532
+1483Q628 1483 705 1456T837 1379T920 1256T950 1092Q950 1007 924 930T851 779T740 629T600 473L319 174V166H1008V0Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="1128" d="M961 1120Q961 1047 938 987T874 883T774 811T645 770V764Q822 742 914 652T1006 416Q1006 320 974 240T875 102T708 12T469 -20Q360 -20 264 -3T82 59V229Q169 183 270 158T465 133Q557 133 624
+153T734 210T798 301T819 422Q819 490 793 538T717 618T598 665T438 680H305V831H438Q519 831 582 851T687 908T752 996T774 1108Q774 1160 756 1201T705 1270T626 1314T524 1329Q417 1329 336 1296T180 1208L88 1333Q126 1364 172 1391T274 1438T391 1471T524
+1483Q632 1483 713 1456T850 1381T933 1266T961 1120Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="1128" d="M1087 328H874V0H698V328H23V487L686 1470H874V494H1087V328ZM698 494V850Q698 906 699 967T703 1087T707 1197T711 1282H702Q695 1262 685 1238T662 1189T636 1141T612 1102L201 494H698Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="1128" d="M545 897Q644 897 729 870T878 788T978 654T1014 469Q1014 355 980 264T879 110T714 14T487 -20Q436 -20 387 -15T292 -1T205 24T131 59V231Q164 208 208 190T302 160T400 142T492 135Q571 135 633
+153T738 211T804 309T827 449Q827 592 739 667T483 743Q456 743 425 741T362 734T302 726T252 717L162 774L217 1462H907V1296H375L336 877Q368 883 420 890T545 897Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="1128" d="M113 625Q113 730 123 834T160 1033T233 1211T350 1353T520 1448T752 1483Q771 1483 794 1482T840 1479T885 1473T924 1464V1309Q889 1321 845 1327T758 1333Q668 1333 600 1312T481 1251T398 1158T343
+1039T312 899T299 745H311Q331 781 359 812T426 866T511 902T618 915Q713 915 790 886T921 799T1004 660T1034 471Q1034 357 1003 266T914 112T774 14T590 -20Q490 -20 403 19T251 138T150 339T113 625ZM588 133Q648 133 697 153T783 215T838 320T858 471Q858 541
+842 596T792 691T710 751T594 772Q527 772 472 749T377 688T317 602T295 506Q295 439 313 373T368 253T460 167T588 133Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="1128" d="M281 0L844 1296H90V1462H1030V1317L475 0H281Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="1128" d="M565 1485Q649 1485 723 1463T854 1397T944 1287T977 1133Q977 1066 957 1012T902 915T819 837T715 774Q773 743 828 705T927 620T997 513T1024 381Q1024 289 991 215T897 88T752 8T565 -20Q455 -20
+370 7T226 84T137 208T106 373Q106 448 128 508T189 616T279 701T389 766Q340 797 297 833T223 915T173 1014T154 1135Q154 1222 187 1287T278 1397T409 1463T565 1485ZM285 371Q285 318 301 274T351 198T437 149T561 131Q631 131 684 148T774 198T828 277T846
+379Q846 431 827 473T771 551T683 619T569 682L539 696Q413 636 349 559T285 371ZM563 1333Q457 1333 395 1280T332 1126Q332 1069 349 1028T398 955T472 898T567 848Q615 870 657 896T731 955T781 1030T799 1126Q799 1227 736 1280T563 1333Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="1128" d="M1028 838Q1028 733 1018 629T981 429T908 252T791 109T621 15T389 -20Q370 -20 347 -19T301 -16T256 -10T217 -2V154Q252 141 296 135T383 129Q518 129 605 176T743 303T815 491T842 717H829Q809
+681 781 650T715 596T629 560T522 547Q427 547 350 576T219 663T136 802T106 991Q106 1105 137 1196T226 1351T366 1449T551 1483Q652 1483 739 1444T890 1325T991 1124T1028 838ZM553 1329Q493 1329 444 1309T358 1247T303 1142T283 991Q283 921 299 866T349 771T431
+711T547 690Q615 690 670 713T764 774T824 860T846 956Q846 1023 828 1089T773 1209T681 1296T553 1329Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="549" d="M147 111Q147 149 157 175T184 218T224 242T274 250Q300 250 323 243T364 219T391 176T401 111Q401 74 391 48T364 4T324 -21T274 -29Q247 -29 224 -21T184 4T157 47T147 111ZM147 987Q147 1026 157
+1052T184 1095T224 1119T274 1126Q300 1126 323 1119T364 1096T391 1053T401 987Q401 950 391 924T364 881T324 856T274 848Q247 848 224 856T184 881T157 924T147 987Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="549" d="M362 238L377 215Q363 161 344 100T301 -23T252 -146T201 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H362ZM147 987Q147 1026 157 1052T184 1095T224 1119T274 1126Q300 1126 323 1119T364
+1096T391 1053T401 987Q401 950 391 924T364 881T324 856T274 848Q247 848 224 856T184 881T157 924T147 987Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="1128" d="M1026 238L102 662V764L1026 1245V1085L291 721L1026 399V238Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="1128" d="M102 852V1001H1026V852H102ZM102 442V592H1026V442H102Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="1128" d="M102 399L838 721L102 1085V1245L1026 764V662L102 238V399Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="872" d="M281 414V451Q281 508 288 554T315 640T368 718T451 799Q499 840 533 873T588 941T620 1015T631 1108Q631 1156 616 1195T573 1263T502 1307T403 1323Q320 1323 245 1297T100 1237L37 1382Q118
+1424 212 1453T403 1483Q496 1483 570 1458T697 1384T777 1267T805 1110Q805 1043 792 991T751 893T684 806T590 717Q538 672 505 639T453 574T427 509T420 432V414H281ZM233 111Q233 149 243 175T270 218T310 242T360 250Q386 250 409 243T450 219T477 176T487
+111Q487 74 477 48T450 4T410 -21T360 -29Q333 -29 310 -21T270 4T243 47T233 111Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="1774" d="M1665 731Q1665 669 1656 607T1628 488T1581 383T1514 298T1428 242T1321 221Q1276 221 1240 236T1177 276T1135 333T1112 401H1108Q1090 364 1063 331T1001 274T921 235T823 221Q746 221 687 249T586
+327T524 449T502 606Q502 707 531 791T616 936T751 1031T928 1065Q973 1065 1018 1061T1104 1050T1179 1035T1237 1018L1214 602Q1213 580 1213 567T1212 545T1212 533T1212 526Q1212 473 1222 439T1250 385T1288 358T1333 350Q1379 350 1414 380T1472 463T1508
+585T1520 733Q1520 875 1477 985T1358 1172T1178 1287T950 1327Q781 1327 652 1272T436 1117T303 881T258 582Q258 431 297 314T413 117T603 -4T864 -45Q925 -45 984 -38T1099 -19T1205 8T1298 41V-100Q1212 -138 1104 -160T866 -182Q687 -182 547 -131T309 17T160
+255T109 575Q109 763 168 925T336 1207T601 1394T950 1462Q1106 1462 1237 1412T1463 1267T1612 1037T1665 731ZM662 602Q662 469 712 410T848 350Q903 350 942 372T1006 436T1044 535T1061 662L1075 915Q1047 923 1009 929T928 936Q854 936 804 907T722 831T676
+724T662 602Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="1272" d="M199 1462H598Q726 1462 823 1443T986 1380T1085 1266T1118 1092Q1118 1030 1099 976T1042 881T951 813T827 776V766Q896 754 956 732T1062 670T1133 570T1159 424Q1159 324 1127 246T1033 113T883 29T684
+0H199V1462ZM385 842H629Q713 842 770 857T862 901T912 975T928 1079Q928 1199 851 1251T608 1303H385V842ZM385 686V158H651Q739 158 798 178T894 234T947 320T963 432Q963 488 947 535T893 615T793 667T639 686H385Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="1235" d="M793 1319Q686 1319 599 1279T451 1162T356 977T322 731Q322 590 351 481T440 296T587 182T793 143Q882 143 962 160T1120 201V39Q1081 24 1042 13T961 -6T870 -16T762 -20Q598 -20 478 34T280 187T163
+425T125 733Q125 899 168 1037T296 1274T506 1428T793 1483Q901 1483 999 1461T1176 1397L1098 1241Q1035 1273 961 1296T793 1319Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="1401" d="M1276 745Q1276 560 1228 421T1089 188T866 47T565 0H199V1462H606Q759 1462 883 1416T1094 1280T1228 1055T1276 745ZM1079 739Q1079 885 1046 991T950 1167T795 1269T586 1303H385V160H547Q811 160
+945 306T1079 739Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="1081" d="M958 0H199V1462H958V1298H385V846H920V684H385V164H958V0Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="1006" d="M385 0H199V1462H958V1298H385V782H920V618H385V0Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="1413" d="M782 772H1266V55Q1211 37 1155 23T1040 0T916 -15T776 -20Q619 -20 498 32T294 182T168 419T125 733Q125 905 172 1044T311 1280T535 1430T840 1483Q951 1483 1053 1461T1243 1397L1171 1235Q1135 1252
+1094 1267T1008 1293T918 1312T825 1319Q703 1319 609 1279T452 1162T355 977T322 731Q322 601 349 493T437 307T592 186T821 143Q865 143 901 145T969 152T1027 161T1081 172V608H782V772Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="1436" d="M1237 0H1051V682H385V0H199V1462H385V846H1051V1462H1237V0Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="694" d="M612 0H82V102L254 143V1319L82 1360V1462H612V1360L440 1319V143L612 102V0Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="555" d="M-29 -389Q-80 -389 -118 -383T-184 -365V-205Q-150 -214 -111 -219T-27 -225Q10 -225 47 -216T115 -181T165 -112T184 0V1462H371V20Q371 -85 342 -162T260 -289T134 -364T-29 -389Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="1186" d="M1186 0H975L524 698L385 584V0H199V1462H385V731L506 899L958 1462H1167L647 825L1186 0Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="1006" d="M199 0V1462H385V166H958V0H199Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="1782" d="M803 0L360 1280H352Q358 1206 362 1133Q366 1070 368 1001T371 874V0H199V1462H475L887 270H893L1307 1462H1583V0H1397V887Q1397 939 1399 1006T1404 1134Q1408 1205 1411 1278H1403L956 0H803Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="1493" d="M1294 0H1079L360 1210H352Q358 1133 362 1057Q366 992 368 921T371 793V0H199V1462H412L1128 258H1135Q1132 334 1128 408Q1127 440 1126 473T1123 540T1121 605T1120 662V1462H1294V0Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="1520" d="M1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125 905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352 1043T1393 733ZM322
+733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428 1165T348 980T322 733Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="1180" d="M1075 1034Q1075 943 1048 859T957 711T791 608T535 569H385V0H199V1462H561Q695 1462 792 1434T952 1351T1045 1216T1075 1034ZM385 727H514Q607 727 676 743T791 794T860 886T883 1024Q883 1166 801
+1234T545 1303H385V727Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="1518" d="M1393 733Q1393 602 1369 489T1297 286T1178 129T1014 25Q1057 -69 1125 -140T1284 -272L1163 -414Q1060 -341 974 -242T836 -16Q819 -18 799 -19T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125
+905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352 1043T1393 733ZM322 733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428
+1165T348 980T322 733Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="1208" d="M385 604V0H199V1462H555Q821 1462 948 1359T1075 1047Q1075 960 1051 895T986 784T893 706T786 655L1184 0H965L614 604H385ZM385 762H549Q639 762 702 779T805 831T864 917T883 1038Q883 1110 863 1160T801
+1242T696 1288T545 1303H385V762Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="1063" d="M969 391Q969 294 935 218T836 88T680 8T473 -20Q362 -20 266 -3T104 49V227Q138 211 181 196T273 168T372 149T473 141Q633 141 709 201T786 373Q786 427 772 467T721 540T623 605T469 674Q380 709 315
+750T207 844T144 962T123 1112Q123 1200 155 1269T245 1385T383 1458T561 1483Q680 1483 775 1461T944 1403L877 1247Q812 1276 730 1297T559 1319Q437 1319 370 1263T303 1110Q303 1053 318 1012T368 937T460 874T602 811Q693 775 761 737T876 651T945 540T969
+391Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="1063" d="M625 0H438V1298H20V1462H1042V1298H625V0Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="1430" d="M1245 1464V516Q1245 402 1212 304T1113 134T946 21T709 -20Q581 -20 483 18T319 128T218 298T184 520V1462H371V510Q371 335 457 239T719 143Q808 143 872 170T977 246T1038 363T1059 512V1464H1245Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="1163" d="M965 1462H1163L674 0H487L0 1462H197L492 535Q521 444 542 360T580 201Q595 275 618 359T672 541L965 1462Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="1810" d="M809 1462H1006L1235 606Q1250 550 1264 494T1291 386T1313 286T1329 201Q1333 239 1339 284T1353 378T1370 479T1391 580L1591 1462H1790L1423 0H1235L981 938Q967 989 954 1043T930 1144Q918 1199 907
+1251Q896 1200 885 1145Q875 1098 863 1042T836 932L594 0H406L20 1462H217L440 573Q452 527 462 478T480 379T496 285T508 201Q513 238 520 287T538 390T559 499T584 604L809 1462Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="1120" d="M1120 0H909L555 635L188 0H0L453 764L31 1462H229L561 903L895 1462H1085L664 770L1120 0Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="1079" d="M539 723L879 1462H1079L633 569V0H446V559L0 1462H203L539 723Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="1104" d="M1022 0H82V145L793 1296H102V1462H1001V1317L291 166H1022V0Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="621" d="M569 -324H164V1462H569V1313H346V-174H569V-324Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="764" d="M201 1462L745 0H567L23 1462H201Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="621" d="M51 -174H274V1313H51V1462H457V-324H51V-174Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="1090" d="M41 549L500 1473H602L1049 549H888L551 1284L202 549H41Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="842" d="M846 -324H-4V-184H846V-324Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="1182" d="M786 1241H666Q631 1269 590 1310T511 1396T441 1480T393 1548V1569H612Q628 1535 649 1495T694 1414T741 1335T786 1268V1241Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445 967 374 943T236
+885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="1200" d="M670 1118Q764 1118 841 1082T972 975T1057 797T1087 551Q1087 410 1057 304T973 125T841 17T670 -20Q611 -20 563 -7T477 27T409 78T356 139H344L307 0H174V1556H356V1180Q356 1145 355 1106T352 1032Q350
+992 348 954H356Q379 989 408 1019T475 1071T562 1105T670 1118ZM635 967Q555 967 502 942T416 864T370 734T356 551Q356 450 369 372T415 240T502 159T637 131Q772 131 835 240T899 553Q899 761 836 864T635 967Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="948" d="M594 -20Q493 -20 405 11T252 111T150 286T113 543Q113 700 151 809T255 987T411 1087T602 1118Q680 1118 754 1101T879 1059L825 905Q802 915 774 924T716 941T657 953T602 958Q445 958 373 858T301 545Q301
+334 373 237T594 139Q675 139 740 157T860 201V39Q806 10 745 -5T594 -20Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="1200" d="M852 147H844Q822 113 793 83T725 29T638 -7T530 -20Q437 -20 360 16T228 123T143 301T113 547Q113 688 143 794T228 973T360 1081T530 1118Q589 1118 637 1105T723 1070T792 1019T844 958H856Q853 992
+850 1023Q848 1049 846 1076T844 1120V1556H1026V0H879L852 147ZM565 131Q641 131 693 154T778 224T826 341T844 506V547Q844 648 831 726T785 858T698 939T563 967Q428 967 365 858T301 545Q301 336 364 234T565 131Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="1096" d="M608 -20Q498 -20 407 17T251 125T149 301T113 541Q113 677 146 784T239 965T382 1079T567 1118Q666 1118 745 1083T879 983T963 828T993 627V514H301Q306 321 382 230T610 139Q661 139 704 144T788 158T867
+182T944 215V53Q904 34 866 20T787 -3T703 -16T608 -20ZM563 967Q449 967 383 889T305 662H797Q797 730 784 786T742 883T669 945T563 967Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="674" d="M651 961H406V0H223V961H29V1036L223 1104V1200Q223 1307 245 1377T310 1490T415 1549T555 1567Q614 1567 663 1556T752 1530L705 1389Q674 1400 638 1408T561 1417Q521 1417 492 1408T444 1374T416 1309T406
+1202V1098H651V961Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="1061" d="M1020 1098V985L823 958Q851 923 870 869T889 745Q889 669 866 605T795 493T677 420T514 393Q492 393 470 393T434 397Q417 387 401 375T371 346T349 310T340 266Q340 239 352 223T384 197T433 185T492
+182H668Q761 182 825 159T929 95T988 1T1006 -115Q1006 -203 974 -273T874 -391T705 -466T463 -492Q356 -492 276 -471T143 -410T64 -314T37 -186Q37 -126 56 -81T109 -2T185 52T276 84Q234 103 207 144T180 238Q180 299 212 343T313 430Q270 448 235 479T175 551T137
+640T123 739Q123 828 148 898T222 1017T344 1092T514 1118Q551 1118 590 1113T657 1098H1020ZM209 -180Q209 -217 222 -249T264 -304T342 -340T463 -354Q649 -354 741 -297T834 -131Q834 -85 822 -56T783 -11T710 12T600 18H424Q389 18 351 10T282 -20T230 -80T209
+-180ZM301 745Q301 630 355 574T508 518Q608 518 659 573T711 748Q711 871 659 929T506 987Q407 987 354 927T301 745Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="1206" d="M860 0V707Q860 837 808 902T643 967Q562 967 507 941T419 864T371 739T356 569V0H174V1556H356V1094L348 950H358Q383 993 417 1024T493 1077T580 1108T674 1118Q857 1118 949 1023T1042 717V0H860Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="530" d="M356 0H174V1098H356V0ZM160 1395Q160 1455 190 1482T266 1509Q288 1509 307 1503T341 1482T364 1447T373 1395Q373 1337 342 1309T266 1280Q221 1280 191 1308T160 1395Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="530" d="M66 -492Q18 -492 -13 -485T-68 -467V-319Q-42 -329 -15 -334T47 -340Q74 -340 97 -333T137 -306T164 -254T174 -170V1098H356V-158Q356 -235 339 -296T286 -401T196 -468T66 -492ZM160 1395Q160 1455
+190 1482T266 1509Q288 1509 307 1503T341 1482T364 1447T373 1395Q373 1337 342 1309T266 1280Q221 1280 191 1308T160 1395Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="1016" d="M342 567L477 737L770 1098H981L580 623L1008 0H799L463 504L354 422V0H174V1556H354V842L338 567H342Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="530" d="M356 0H174V1556H356V0Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="1835" d="M1489 0V707Q1489 837 1439 902T1284 967Q1211 967 1160 944T1077 875T1029 762T1014 606V0H831V707Q831 837 782 902T627 967Q550 967 498 941T415 864T370 739T356 569V0H174V1098H322L348 950H358Q382
+993 415 1024T487 1077T571 1108T662 1118Q782 1118 861 1074T979 936H987Q1013 983 1049 1017T1129 1073T1221 1107T1319 1118Q1494 1118 1582 1023T1671 717V0H1489Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="1206" d="M860 0V707Q860 837 808 902T643 967Q562 967 507 941T419 864T371 739T356 569V0H174V1098H322L348 950H358Q383 993 417 1024T493 1077T580 1108T674 1118Q857 1118 949 1023T1042 717V0H860Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="1182" d="M1069 551Q1069 414 1036 308T940 129T788 18T588 -20Q485 -20 398 18T248 128T149 307T113 551Q113 687 146 792T242 970T393 1080T594 1118Q697 1118 784 1081T934 971T1033 793T1069 551ZM301 551Q301
+342 369 237T592 131Q746 131 813 236T881 551Q881 760 813 863T590 967Q436 967 369 864T301 551Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="1200" d="M670 -20Q611 -20 563 -7T477 27T409 78T356 139H344Q347 105 350 74Q352 48 354 21T356 -23V-492H174V1098H322L348 950H356Q379 985 408 1015T475 1068T562 1104T670 1118Q764 1118 841 1082T972 975T1057
+797T1087 551Q1087 410 1057 304T973 125T841 17T670 -20ZM635 967Q559 967 507 944T422 874T374 757T356 592V551Q356 450 369 372T415 240T502 159T637 131Q772 131 835 240T899 553Q899 761 836 864T635 967Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="1200" d="M565 131Q641 131 693 154T778 224T826 341T844 506V547Q844 648 831 726T785 858T698 939T563 967Q428 967 365 858T301 545Q301 336 364 234T565 131ZM530 -20Q437 -20 360 16T228 123T143 301T113
+547Q113 688 143 794T228 973T360 1081T530 1118Q589 1118 637 1105T723 1069T791 1016T844 950H852L879 1098H1026V-492H844V-23Q844 -4 846 25T850 81Q853 113 856 147H844Q822 113 793 83T725 29T638 -7T530 -20Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="817" d="M649 1118Q678 1118 714 1116T776 1108L752 940Q724 945 695 948T639 952Q576 952 524 927T435 854T377 740T356 592V0H174V1098H322L344 897H352Q377 940 405 980T469 1050T549 1099T649 1118Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="924" d="M831 301Q831 221 802 161T719 61T587 0T414 -20Q305 -20 227 -3T90 49V215Q121 199 159 184T239 156T325 137T414 129Q479 129 524 140T598 171T640 221T653 287Q653 318 643 343T607 392T534 442T416
+498Q344 529 287 559T189 626T128 711T106 827Q106 897 133 951T211 1043T331 1099T487 1118Q584 1118 664 1097T817 1042L754 895Q689 924 621 945T481 967Q379 967 330 934T281 838Q281 803 292 777T332 728T407 682T524 629Q596 599 652 569T749 502T810 416T831
+301Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="694" d="M506 129Q524 129 546 131T590 136T628 143T655 150V12Q642 6 622 0T578 -10T528 -17T477 -20Q415 -20 362 -4T271 51T210 156T188 324V961H33V1042L188 1120L266 1350H371V1098H647V961H371V324Q371 227
+402 178T506 129Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="1206" d="M885 0L858 147H848Q823 104 789 73T713 21T626 -10T532 -20Q441 -20 372 3T257 75T188 200T164 381V1098H346V391Q346 261 399 196T563 131Q644 131 699 157T787 233T835 358T850 528V1098H1032V0H885Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="981" d="M375 0L0 1098H188L387 487Q398 454 413 402T443 296T470 194T487 121H494Q499 146 511 194T538 296T568 402T594 487L793 1098H981L606 0H375Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="1528" d="M1008 0L840 616Q836 634 830 656T818 704T806 755T793 806Q779 864 764 926H758Q744 863 731 805Q720 755 708 702T684 612L512 0H301L20 1098H211L342 514Q352 469 362 417T381 313T397 216T408 141H414Q419
+167 427 210T446 302T468 398T489 479L668 1098H864L1036 479Q1045 445 1056 399T1079 306T1099 214T1112 141H1118Q1121 167 1127 210T1143 306T1162 412T1184 514L1321 1098H1507L1223 0H1008Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="1024" d="M408 563L55 1098H262L512 688L762 1098H969L614 563L987 0H780L512 436L242 0H35L408 563Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="1001" d="M10 1098H199L414 485Q428 445 442 401T469 313T491 228T504 152H510Q515 177 526 220T550 311T578 407T604 487L803 1098H991L557 -143Q529 -224 497 -288T421 -398T320 -467T182 -492Q130 -492 92 -487T27
+-475V-330Q48 -335 80 -338T147 -342Q195 -342 230 -331T291 -297T335 -243T369 -170L426 -10L10 1098Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="903" d="M821 0H82V125L618 961H115V1098H803V952L279 137H821V0Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="725" d="M500 -16Q500 -64 512 -94T546 -142T601 -166T674 -174V-324Q597 -323 532 -307T419 -255T344 -164T317 -31V303Q317 406 252 449T61 492V647Q186 647 251 690T317 836V1169Q317 1247 344 1302T418
+1392T531 1444T674 1462V1313Q634 1312 602 1306T547 1282T512 1234T500 1155V823Q500 718 441 657T266 575V563Q381 543 440 482T500 315V-16Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="1128" d="M489 1556H639V-492H489V1556Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="725" d="M225 315Q225 421 284 482T459 563V575Q344 595 285 656T225 823V1155Q225 1203 213 1233T179 1281T124 1305T51 1313V1462Q128 1461 193 1445T306 1393T381 1302T408 1169V836Q408 784 424 748T473
+690T554 657T664 647V492Q539 492 474 449T408 303V-31Q408 -109 381 -164T307 -254T194 -306T51 -324V-174Q91 -173 123 -167T178 -143T213 -95T225 -16V315Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="1128" d="M530 651Q493 667 466 678T416 695T373 704T330 707Q302 707 272 698T213 672T155 633T102 586V748Q202 856 350 856Q379 856 404 854T456 845T517 826T598 793Q635 777 662 766T713 749T757
+740T799 737Q827 737 857 746T916 772T974 811T1026 858V696Q927 588 778 588Q749 588 724 590T672 599T611 618T530 651Z" />
+<glyph unicode="&#xa0;" glyph-name="nbspace" horiz-adv-x="532" />
+<glyph unicode="&#xa1;" glyph-name="exclamdown" horiz-adv-x="551" d="M213 676H334L385 -373H162L213 676ZM401 979Q401 941 392 915T365 872T324 848T274 840Q248 840 225 847T185 871T157 914T147 979Q147 1016 157 1042T184 1085T225 1110T274 1118Q301
+1118 324 1110T364 1085T391 1042T401 979Z" />
+<glyph unicode="&#xa2;" glyph-name="cent" horiz-adv-x="1128" d="M886 212T831 197T700 180V-20H563V186Q476 199 407 236T289 340T214 506T188 743Q188 884 214 985T289 1155T407 1260T563 1311V1483H700V1319Q772 1316 840 1300T954 1260L901 1106Q878 1116
+850 1125T792 1142T733 1154T678 1159Q521 1159 449 1058T377 745Q377 535 449 438T670 340Q751 340 816 358T936 401V240Q886 212 831 197Z" />
+<glyph unicode="&#xa3;" glyph-name="sterling" horiz-adv-x="1128" d="M666 1481Q772 1481 859 1459T1012 1401L946 1257Q890 1286 820 1307T674 1329Q626 1329 585 1316T514 1273T468 1196T451 1083V788H827V651H451V440Q451 378 440 334T409 257T364 204T311
+166H1059V0H68V154Q112 165 148 185T211 240T253 322T268 438V651H70V788H268V1112Q268 1199 297 1267T379 1383T505 1456T666 1481Z" />
+<glyph unicode="&#xa4;" glyph-name="currency" horiz-adv-x="1128" d="M186 723Q186 782 203 835T252 936L123 1065L221 1163L348 1034Q395 1066 449 1084T563 1102Q623 1102 676 1084T776 1034L905 1163L1004 1067L874 938Q905 892 923 838T942 723Q942 663
+925 608T874 508L1001 381L905 285L776 412Q730 381 677 364T563 346Q503 346 448 364T348 414L221 287L125 383L252 510Q221 555 204 609T186 723ZM324 723Q324 673 342 630T393 554T469 502T563 483Q614 483 658 502T736 553T788 629T807 723Q807 774 788 818T736
+896T659 948T563 967Q513 967 470 948T394 896T343 819T324 723Z" />
+<glyph unicode="&#xa5;" glyph-name="yen" horiz-adv-x="1128" d="M563 723L909 1462H1100L715 694H954V557H653V399H954V262H653V0H475V262H174V399H475V557H174V694H408L29 1462H221L563 723Z" />
+<glyph unicode="&#xa6;" glyph-name="brokenbar" horiz-adv-x="1128" d="M489 1556H639V776H489V1556ZM489 289H639V-492H489V289Z" />
+<glyph unicode="&#xa7;" glyph-name="section" horiz-adv-x="995" d="M137 809Q137 860 150 901T185 975T237 1029T297 1067Q222 1105 180 1162T137 1303Q137 1364 164 1413T242 1496T362 1548T518 1567Q615 1567 693 1547T844 1495L788 1356Q723 1384 653 1403T512
+1423Q413 1423 362 1394T311 1307Q311 1280 323 1257T363 1212T439 1167T557 1114Q629 1086 685 1054T781 982T841 895T862 784Q862 732 850 690T818 613T771 555T717 514Q786 476 824 422T862 289Q862 218 833 163T749 69T618 10T444 -10Q336 -10 258 6T121 55V213Q152
+198 190 183T270 157T356 138T444 131Q513 131 559 143T633 174T672 219T684 272Q684 301 676 323T642 368T569 415T446 471Q373 502 316 533T218 603T158 692T137 809ZM291 831Q291 794 305 763T350 702T432 646T555 588L590 573Q610 586 630 604T667 645T694
+696T705 758Q705 796 692 828T647 889T560 947T424 1006Q399 998 376 983T333 945T303 893T291 831Z" />
+<glyph unicode="&#xa8;" glyph-name="dieresis" horiz-adv-x="1182" d="M307 1395Q307 1449 335 1473T403 1497Q442 1497 471 1473T500 1395Q500 1342 471 1317T403 1292Q363 1292 335 1317T307 1395ZM682 1395Q682 1449 710 1473T778 1497Q797 1497 814 1491T845
+1473T866 1441T874 1395Q874 1342 845 1317T778 1292Q738 1292 710 1317T682 1395Z" />
+<glyph unicode="&#xa9;" glyph-name="copyright" horiz-adv-x="1704" d="M891 1053Q830 1053 783 1031T704 968T656 866T639 731Q639 653 653 593T698 492T776 430T891 408Q914 408 941 411T996 421T1053 435T1106 453V322Q1082 311 1058 302T1007 286T950 276T885
+272Q783 272 707 305T581 399T505 545T479 733Q479 834 506 917T585 1061T714 1154T891 1188Q954 1188 1020 1172T1145 1126L1083 999Q1031 1025 983 1039T891 1053ZM100 731Q100 835 127 931T202 1110T320 1263T472 1380T652 1456T852 1483Q956 1483 1052 1456T1231
+1381T1384 1263T1501 1111T1577 931T1604 731Q1604 627 1577 531T1502 352T1384 200T1232 82T1052 7T852 -20Q748 -20 652 6T473 82T320 199T203 351T127 531T100 731ZM209 731Q209 598 259 481T397 277T602 139T852 88Q985 88 1102 138T1306 276T1444 481T1495
+731Q1495 864 1445 981T1307 1185T1102 1323T852 1374Q719 1374 602 1324T398 1186T260 981T209 731Z" />
+<glyph unicode="&#xaa;" glyph-name="ordfeminine" horiz-adv-x="678" d="M487 797L459 879Q441 857 422 840T379 810T327 791T264 784Q221 784 185 797T123 835T83 899T68 989Q68 1091 138 1145T352 1204L451 1208V1239Q451 1311 421 1339T334 1368Q286 1368
+241 1354T154 1317L106 1417Q157 1443 215 1461T334 1479Q459 1479 518 1426T578 1251V797H487ZM377 1110Q326 1107 292 1098T238 1074T208 1038T199 987Q199 936 224 914T291 891Q325 891 354 901T404 934T438 988T451 1065V1114L377 1110Z" />
+<glyph unicode="&#xab;" glyph-name="guillemotleft" horiz-adv-x="997" d="M82 553L391 967L508 889L270 541L508 193L391 115L82 526V553ZM489 553L799 967L915 889L678 541L915 193L799 115L489 526V553Z" />
+<glyph unicode="&#xac;" glyph-name="logicalnot" horiz-adv-x="1128" d="M1026 797V262H877V647H102V797H1026Z" />
+<glyph unicode="&#xad;" glyph-name="uni00AD" horiz-adv-x="659" d="M82 465V633H578V465H82Z" />
+<glyph unicode="&#xae;" glyph-name="registered" horiz-adv-x="1704" d="M743 768H815Q906 768 945 804T985 909Q985 983 944 1012T813 1042H743V768ZM1145 913Q1145 865 1132 828T1096 762T1045 713T985 680Q1052 570 1105 483Q1128 446 1149 411T1186 347T1213
+302L1223 285H1044L838 637H743V285H586V1178H819Q987 1178 1066 1113T1145 913ZM100 731Q100 835 127 931T202 1110T320 1263T472 1380T652 1456T852 1483Q956 1483 1052 1456T1231 1381T1384 1263T1501 1111T1577 931T1604 731Q1604 627 1577 531T1502 352T1384
+200T1232 82T1052 7T852 -20Q748 -20 652 6T473 82T320 199T203 351T127 531T100 731ZM209 731Q209 598 259 481T397 277T602 139T852 88Q985 88 1102 138T1306 276T1444 481T1495 731Q1495 864 1445 981T1307 1185T1102 1323T852 1374Q719 1374 602 1324T398 1186T260
+981T209 731Z" />
+<glyph unicode="&#xaf;" glyph-name="overscore" horiz-adv-x="1024" d="M1030 1556H-6V1696H1030V1556Z" />
+<glyph unicode="&#xb0;" glyph-name="degree" horiz-adv-x="877" d="M123 1167Q123 1232 148 1289T215 1390T315 1458T438 1483Q503 1483 560 1458T661 1390T729 1290T754 1167Q754 1102 729 1045T661 946T561 879T438 854Q373 854 316 878T216 945T148 1045T123
+1167ZM246 1167Q246 1128 261 1094T302 1033T363 992T438 977Q478 977 513 992T574 1033T616 1093T631 1167Q631 1207 616 1242T575 1304T513 1346T438 1362Q398 1362 363 1347T302 1305T261 1243T246 1167Z" />
+<glyph unicode="&#xb1;" glyph-name="plusminus" horiz-adv-x="1128" d="M489 647H102V797H489V1186H639V797H1026V647H639V262H489V647ZM102 0V150H1026V0H102Z" />
+<glyph unicode="&#xb2;" glyph-name="twosuperior" horiz-adv-x="678" d="M621 586H49V698L258 926Q315 988 351 1030T407 1106T434 1169T442 1233Q442 1298 409 1330T322 1362Q271 1362 225 1337T133 1274L55 1368Q109 1416 175 1448T324 1481Q384 1481 432 1465T515
+1417T567 1340T586 1237Q586 1187 572 1144T530 1059T464 971T373 870L225 713H621V586Z" />
+<glyph unicode="&#xb3;" glyph-name="threesuperior" horiz-adv-x="678" d="M590 1255Q590 1177 550 1124T440 1047Q528 1024 572 971T616 840Q616 780 596 730T535 645T430 589T281 569Q211 569 150 581T31 625V758Q94 724 160 705T279 686Q377 686 421 727T465
+842Q465 916 412 949T262 983H164V1096H262Q354 1096 396 1135T438 1239Q438 1271 428 1294T401 1333T360 1355T309 1362Q250 1362 202 1342T102 1284L33 1380Q62 1403 92 1421T157 1453T229 1473T311 1481Q380 1481 432 1464T520 1417T572 1346T590 1255Z" />
+<glyph unicode="&#xb4;" glyph-name="acute" horiz-adv-x="1182" d="M393 1268Q415 1297 438 1335T485 1413T530 1494T567 1569H786V1548Q770 1521 739 1481T669 1396T590 1311T514 1241H393V1268Z" />
+<glyph unicode="&#xb5;" glyph-name="mu" horiz-adv-x="1217" d="M356 391Q356 261 409 196T573 131Q655 131 710 157T798 233T846 358T860 528V1098H1042V0H895L868 147H858Q810 64 738 22T563 -20Q491 -20 438 3T350 68Q351 30 353 -10Q355 -45 355 -87T356
+-172V-492H174V1098H356V391Z" />
+<glyph unicode="&#xb6;" glyph-name="paragraph" horiz-adv-x="1341" d="M1126 -260H1006V1397H799V-260H678V559Q617 541 532 541Q437 541 360 566T228 651T143 806T113 1042Q113 1189 145 1287T237 1446T380 1531T563 1556H1126V-260Z" />
+<glyph unicode="&#xb7;" glyph-name="middot" horiz-adv-x="549" d="M147 723Q147 761 157 787T184 830T224 854T274 862Q300 862 323 855T364 831T391 788T401 723Q401 686 391 660T364 617T324 592T274 584Q247 584 224 592T184 617T157 660T147 723Z" />
+<glyph unicode="&#xb8;" glyph-name="cedilla" horiz-adv-x="420" d="M408 -287Q408 -384 338 -438T117 -492Q95 -492 73 -489T35 -483V-375Q50 -378 74 -379T115 -381Q186 -381 226 -360T266 -289Q266 -265 253 -248T217 -217T163 -195T94 -176L184 0H305L248
+-115Q282 -123 311 -136T361 -169T395 -219T408 -287Z" />
+<glyph unicode="&#xb9;" glyph-name="onesuperior" horiz-adv-x="678" d="M307 1462H442V586H297V1102Q297 1127 297 1157T299 1217T302 1275T305 1325Q291 1308 272 1288T231 1251L137 1178L63 1274L307 1462Z" />
+<glyph unicode="&#xba;" glyph-name="ordmasculine" horiz-adv-x="717" d="M651 1133Q651 1050 631 985T572 876T479 808T356 784Q293 784 240 807T148 875T88 985T66 1133Q66 1216 86 1280T145 1389T237 1456T360 1479Q422 1479 475 1456T568 1389T629 1281T651
+1133ZM197 1133Q197 1014 234 954T358 893Q443 893 480 953T518 1133Q518 1253 481 1310T358 1368Q272 1368 235 1311T197 1133Z" />
+<glyph unicode="&#xbb;" glyph-name="guillemotright" horiz-adv-x="997" d="M918 526L608 115L492 193L729 541L492 889L608 967L918 553V526ZM510 526L201 115L84 193L322 541L84 889L201 967L510 553V526Z" />
+<glyph unicode="&#xbc;" glyph-name="onequarter" horiz-adv-x="1509" d="M307 1462H442V586H297V1102Q297 1127 297 1157T299 1217T302 1275T305 1325Q291 1308 272 1288T231 1251L137 1178L63 1274L307 1462ZM1202 1462L391 0H234L1045 1462H1202ZM1419 193H1294V1H1151V193H776V304L1153
+883H1294V320H1419V193ZM1151 320V515Q1151 557 1152 606T1157 705Q1152 694 1142 676T1121 636T1098 595T1077 560L922 320H1151Z" />
+<glyph unicode="&#xbd;" glyph-name="onehalf" horiz-adv-x="1509" d="M544 1462H679V586H534V1102Q534 1127 534 1157T536 1217T539 1275T542 1325Q528 1308 509 1288T468 1251L374 1178L300 1274L544 1462ZM1181 1462L370 0H213L1024 1462H1181ZM1440 1H868V113L1077
+341Q1134 403 1170 445T1226 521T1253 584T1261 648Q1261 713 1228 745T1141 777Q1090 777 1044 752T952 689L874 783Q928 831 994 863T1143 896Q1203 896 1251 880T1334 832T1386 755T1405 652Q1405 602 1391 559T1349 474T1283 386T1192 285L1044 128H1440V1Z"
+/>
+<glyph unicode="&#xbe;" glyph-name="threequarters" horiz-adv-x="1509" d="M590 1255Q590 1177 550 1124T440 1047Q528 1024 572 971T616 840Q616 780 596 730T535 645T430 589T281 569Q211 569 150 581T31 625V758Q94 724 160 705T279 686Q377 686 421 727T465
+842Q465 916 412 949T262 983H164V1096H262Q354 1096 396 1135T438 1239Q438 1271 428 1294T401 1333T360 1355T309 1362Q250 1362 202 1342T102 1284L33 1380Q62 1403 92 1421T157 1453T229 1473T311 1481Q380 1481 432 1464T520 1417T572 1346T590 1255ZM1296
+1462L485 0H328L1139 1462H1296ZM1486 193H1361V1H1218V193H843V304L1220 883H1361V320H1486V193ZM1218 320V515Q1218 557 1219 606T1224 705Q1219 694 1209 676T1188 636T1165 595T1144 560L989 320H1218Z" />
+<glyph unicode="&#xbf;" glyph-name="questiondown" horiz-adv-x="872" d="M592 676V639Q592 581 584 536T557 450T505 371T422 291Q374 250 340 217T285 149T253 75T242 -18Q242 -66 257 -105T300 -173T371 -217T469 -233Q553 -233 628 -208T772 -147L836 -293Q754
+-335 660 -364T469 -393Q376 -393 302 -368T176 -294T96 -177T68 -20Q68 48 81 100T121 197T188 284T283 373Q335 418 368 451T420 516T446 580T453 657V676H592ZM639 979Q639 941 630 915T603 872T562 848T512 840Q486 840 463 847T423 871T395 914T385 979Q385
+1016 395 1042T422 1085T463 1110T512 1118Q539 1118 562 1110T602 1085T629 1042T639 979Z" />
+<glyph unicode="&#xc0;" glyph-name="Agrave" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836ZM719
+1579H599Q564 1607 523 1648T444 1734T374 1818T326 1886V1907H545Q561 1873 582 1833T627 1752T674 1673T719 1606V1579Z" />
+<glyph unicode="&#xc1;" glyph-name="Aacute" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836ZM534
+1606Q556 1635 579 1673T626 1751T671 1832T708 1907H927V1886Q911 1859 880 1819T810 1734T731 1649T655 1579H534V1606Z" />
+<glyph unicode="&#xc2;" glyph-name="Acircumflex" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836ZM953
+1579H832Q781 1613 727 1661T621 1765Q567 1710 514 1662T410 1579H289V1606Q315 1635 349 1673T416 1751T479 1832T525 1907H717Q733 1873 762 1833T825 1752T893 1673T953 1606V1579Z" />
+<glyph unicode="&#xc3;" glyph-name="Atilde" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836ZM772
+1581Q732 1581 693 1598T615 1637T542 1676T475 1694Q430 1694 406 1668T368 1579H264Q269 1639 285 1688T328 1771T392 1824T475 1843Q517 1843 557 1826T636 1787T708 1749T772 1731Q817 1731 840 1757T878 1845H983Q978 1785 962 1737T919 1654T855 1600T772
+1581Z" />
+<glyph unicode="&#xc4;" glyph-name="Adieresis" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836ZM340
+1733Q340 1787 368 1811T436 1835Q475 1835 504 1811T533 1733Q533 1680 504 1655T436 1630Q396 1630 368 1655T340 1733ZM715 1733Q715 1787 743 1811T811 1835Q830 1835 847 1829T878 1811T899 1779T907 1733Q907 1680 878 1655T811 1630Q771 1630 743 1655T715
+1733Z" />
+<glyph unicode="&#xc5;" glyph-name="Aring" horiz-adv-x="1245" d="M1055 0L895 453H350L188 0H0L537 1468H707L1245 0H1055ZM836 618L688 1042Q682 1060 674 1086T656 1142T638 1204T621 1268Q614 1237 605 1204T587 1141T570 1085T555 1042L410 618H836ZM848
+1583Q848 1532 831 1492T783 1423T710 1381T619 1366Q569 1366 528 1380T458 1423T412 1490T396 1581Q396 1632 412 1671T457 1739T528 1781T619 1796Q667 1796 709 1782T782 1740T830 1673T848 1583ZM731 1581Q731 1634 700 1664T619 1694Q569 1694 538 1664T506
+1581Q506 1528 534 1498T619 1468Q668 1468 699 1498T731 1581Z" />
+<glyph unicode="&#xc6;" glyph-name="AE" horiz-adv-x="1745" d="M1622 0H862V453H387L184 0H-2L653 1462H1622V1298H1049V846H1583V684H1049V164H1622V0ZM459 618H862V1298H754L459 618Z" />
+<glyph unicode="&#xc7;" glyph-name="Ccedilla" horiz-adv-x="1235" d="M793 1319Q686 1319 599 1279T451 1162T356 977T322 731Q322 590 351 481T440 296T587 182T793 143Q882 143 962 160T1120 201V39Q1081 24 1042 13T961 -6T870 -16T762 -20Q598 -20 478 34T280
+187T163 425T125 733Q125 899 168 1037T296 1274T506 1428T793 1483Q901 1483 999 1461T1176 1397L1098 1241Q1035 1273 961 1296T793 1319ZM916 -287Q916 -384 846 -438T625 -492Q603 -492 581 -489T543 -483V-375Q558 -378 582 -379T623 -381Q694 -381 734 -360T774
+-289Q774 -265 761 -248T725 -217T671 -195T602 -176L692 0H813L756 -115Q790 -123 819 -136T869 -169T903 -219T916 -287Z" />
+<glyph unicode="&#xc8;" glyph-name="Egrave" horiz-adv-x="1081" d="M958 0H199V1462H958V1298H385V846H920V684H385V164H958V0ZM713 1579H593Q558 1607 517 1648T438 1734T368 1818T320 1886V1907H539Q555 1873 576 1833T621 1752T668 1673T713 1606V1579Z" />
+<glyph unicode="&#xc9;" glyph-name="Eacute" horiz-adv-x="1081" d="M958 0H199V1462H958V1298H385V846H920V684H385V164H958V0ZM456 1606Q478 1635 501 1673T548 1751T593 1832T630 1907H849V1886Q833 1859 802 1819T732 1734T653 1649T577 1579H456V1606Z" />
+<glyph unicode="&#xca;" glyph-name="Ecircumflex" horiz-adv-x="1081" d="M958 0H199V1462H958V1298H385V846H920V684H385V164H958V0ZM907 1579H786Q735 1613 681 1661T575 1765Q521 1710 468 1662T364 1579H243V1606Q269 1635 303 1673T370 1751T433 1832T479
+1907H671Q687 1873 716 1833T779 1752T847 1673T907 1606V1579Z" />
+<glyph unicode="&#xcb;" glyph-name="Edieresis" horiz-adv-x="1081" d="M958 0H199V1462H958V1298H385V846H920V684H385V164H958V0ZM296 1733Q296 1787 324 1811T392 1835Q431 1835 460 1811T489 1733Q489 1680 460 1655T392 1630Q352 1630 324 1655T296 1733ZM671
+1733Q671 1787 699 1811T767 1835Q786 1835 803 1829T834 1811T855 1779T863 1733Q863 1680 834 1655T767 1630Q727 1630 699 1655T671 1733Z" />
+<glyph unicode="&#xcc;" glyph-name="Igrave" horiz-adv-x="694" d="M612 0H82V102L254 143V1319L82 1360V1462H612V1360L440 1319V143L612 102V0ZM455 1579H335Q300 1607 259 1648T180 1734T110 1818T62 1886V1907H281Q297 1873 318 1833T363 1752T410 1673T455
+1606V1579Z" />
+<glyph unicode="&#xcd;" glyph-name="Iacute" horiz-adv-x="694" d="M612 0H82V102L254 143V1319L82 1360V1462H612V1360L440 1319V143L612 102V0ZM257 1606Q279 1635 302 1673T349 1751T394 1832T431 1907H650V1886Q634 1859 603 1819T533 1734T454 1649T378
+1579H257V1606Z" />
+<glyph unicode="&#xce;" glyph-name="Icircumflex" horiz-adv-x="694" d="M612 0H82V102L254 143V1319L82 1360V1462H612V1360L440 1319V143L612 102V0ZM681 1579H560Q509 1613 455 1661T349 1765Q295 1710 242 1662T138 1579H17V1606Q43 1635 77 1673T144 1751T207
+1832T253 1907H445Q461 1873 490 1833T553 1752T621 1673T681 1606V1579Z" />
+<glyph unicode="&#xcf;" glyph-name="Idieresis" horiz-adv-x="694" d="M612 0H82V102L254 143V1319L82 1360V1462H612V1360L440 1319V143L612 102V0ZM64 1733Q64 1787 92 1811T160 1835Q199 1835 228 1811T257 1733Q257 1680 228 1655T160 1630Q120 1630 92 1655T64
+1733ZM439 1733Q439 1787 467 1811T535 1835Q554 1835 571 1829T602 1811T623 1779T631 1733Q631 1680 602 1655T535 1630Q495 1630 467 1655T439 1733Z" />
+<glyph unicode="&#xd0;" glyph-name="Eth" horiz-adv-x="1401" d="M47 805H199V1462H606Q759 1462 883 1416T1094 1280T1228 1055T1276 745Q1276 560 1228 421T1089 188T866 47T565 0H199V643H47V805ZM1079 739Q1079 885 1046 991T950 1167T795 1269T586 1303H385V805H721V643H385V160H547Q811
+160 945 306T1079 739Z" />
+<glyph unicode="&#xd1;" glyph-name="Ntilde" horiz-adv-x="1493" d="M1294 0H1079L360 1210H352Q358 1133 362 1057Q366 992 368 921T371 793V0H199V1462H412L1128 258H1135Q1132 334 1128 408Q1127 440 1126 473T1123 540T1121 605T1120 662V1462H1294V0ZM905
+1581Q865 1581 826 1598T748 1637T675 1676T608 1694Q563 1694 539 1668T501 1579H397Q402 1639 418 1688T461 1771T525 1824T608 1843Q650 1843 690 1826T769 1787T841 1749T905 1731Q950 1731 973 1757T1011 1845H1116Q1111 1785 1095 1737T1052 1654T988 1600T905
+1581Z" />
+<glyph unicode="&#xd2;" glyph-name="Ograve" horiz-adv-x="1520" d="M1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125 905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352 1043T1393
+733ZM322 733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428 1165T348 980T322 733ZM870 1579H750Q715 1607 674 1648T595 1734T525 1818T477 1886V1907H696Q712
+1873 733 1833T778 1752T825 1673T870 1606V1579Z" />
+<glyph unicode="&#xd3;" glyph-name="Oacute" horiz-adv-x="1520" d="M1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125 905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352 1043T1393
+733ZM322 733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428 1165T348 980T322 733ZM651 1606Q673 1635 696 1673T743 1751T788 1832T825 1907H1044V1886Q1028
+1859 997 1819T927 1734T848 1649T772 1579H651V1606Z" />
+<glyph unicode="&#xd4;" glyph-name="Ocircumflex" horiz-adv-x="1520" d="M1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125 905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352
+1043T1393 733ZM322 733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428 1165T348 980T322 733ZM1096 1579H975Q924 1613 870 1661T764 1765Q710
+1710 657 1662T553 1579H432V1606Q458 1635 492 1673T559 1751T622 1832T668 1907H860Q876 1873 905 1833T968 1752T1036 1673T1096 1606V1579Z" />
+<glyph unicode="&#xd5;" glyph-name="Otilde" horiz-adv-x="1520" d="M1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125 905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352 1043T1393
+733ZM322 733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428 1165T348 980T322 733ZM891 1581Q851 1581 812 1598T734 1637T661 1676T594 1694Q549
+1694 525 1668T487 1579H383Q388 1639 404 1688T447 1771T511 1824T594 1843Q636 1843 676 1826T755 1787T827 1749T891 1731Q936 1731 959 1757T997 1845H1102Q1097 1785 1081 1737T1038 1654T974 1600T891 1581Z" />
+<glyph unicode="&#xd6;" glyph-name="Odieresis" horiz-adv-x="1520" d="M1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q597 -20 478 34T280 187T163 425T125 735Q125 905 163 1043T280 1280T479 1431T762 1485Q917 1485 1034 1432T1232 1280T1352 1043T1393
+733ZM322 733Q322 596 348 487T427 301T563 184T760 143Q874 143 956 183T1092 300T1171 486T1196 733Q1196 871 1171 980T1093 1164T958 1280T762 1321Q648 1321 565 1281T428 1165T348 980T322 733ZM477 1733Q477 1787 505 1811T573 1835Q612 1835 641 1811T670
+1733Q670 1680 641 1655T573 1630Q533 1630 505 1655T477 1733ZM852 1733Q852 1787 880 1811T948 1835Q967 1835 984 1829T1015 1811T1036 1779T1044 1733Q1044 1680 1015 1655T948 1630Q908 1630 880 1655T852 1733Z" />
+<glyph unicode="&#xd7;" glyph-name="multiply" horiz-adv-x="1128" d="M459 723L141 1042L246 1147L563 829L885 1147L989 1044L668 723L987 403L885 301L563 618L246 303L143 406L459 723Z" />
+<glyph unicode="&#xd8;" glyph-name="Oslash" horiz-adv-x="1520" d="M1300 1454L1208 1305Q1299 1206 1346 1061T1393 733Q1393 564 1353 425T1232 187T1034 34T760 -20Q571 -20 438 51L360 -76L223 2L313 147Q216 247 171 396T125 735Q125 905 163 1043T280
+1280T479 1431T762 1485Q856 1485 936 1464T1083 1405L1163 1532L1300 1454ZM322 733Q322 602 345 498T416 315L995 1260Q947 1289 890 1305T762 1321Q648 1321 565 1281T428 1165T348 980T322 733ZM1196 733Q1196 990 1108 1141L530 201Q577 173 634 158T760 143Q874
+143 956 183T1092 300T1171 486T1196 733Z" />
+<glyph unicode="&#xd9;" glyph-name="Ugrave" horiz-adv-x="1430" d="M1245 1464V516Q1245 402 1212 304T1113 134T946 21T709 -20Q581 -20 483 18T319 128T218 298T184 520V1462H371V510Q371 335 457 239T719 143Q808 143 872 170T977 246T1038 363T1059 512V1464H1245ZM847
+1579H727Q692 1607 651 1648T572 1734T502 1818T454 1886V1907H673Q689 1873 710 1833T755 1752T802 1673T847 1606V1579Z" />
+<glyph unicode="&#xda;" glyph-name="Uacute" horiz-adv-x="1430" d="M1245 1464V516Q1245 402 1212 304T1113 134T946 21T709 -20Q581 -20 483 18T319 128T218 298T184 520V1462H371V510Q371 335 457 239T719 143Q808 143 872 170T977 246T1038 363T1059 512V1464H1245ZM590
+1606Q612 1635 635 1673T682 1751T727 1832T764 1907H983V1886Q967 1859 936 1819T866 1734T787 1649T711 1579H590V1606Z" />
+<glyph unicode="&#xdb;" glyph-name="Ucircumflex" horiz-adv-x="1430" d="M1245 1464V516Q1245 402 1212 304T1113 134T946 21T709 -20Q581 -20 483 18T319 128T218 298T184 520V1462H371V510Q371 335 457 239T719 143Q808 143 872 170T977 246T1038 363T1059
+512V1464H1245ZM1043 1579H922Q871 1613 817 1661T711 1765Q657 1710 604 1662T500 1579H379V1606Q405 1635 439 1673T506 1751T569 1832T615 1907H807Q823 1873 852 1833T915 1752T983 1673T1043 1606V1579Z" />
+<glyph unicode="&#xdc;" glyph-name="Udieresis" horiz-adv-x="1430" d="M1245 1464V516Q1245 402 1212 304T1113 134T946 21T709 -20Q581 -20 483 18T319 128T218 298T184 520V1462H371V510Q371 335 457 239T719 143Q808 143 872 170T977 246T1038 363T1059 512V1464H1245ZM432
+1733Q432 1787 460 1811T528 1835Q567 1835 596 1811T625 1733Q625 1680 596 1655T528 1630Q488 1630 460 1655T432 1733ZM807 1733Q807 1787 835 1811T903 1835Q922 1835 939 1829T970 1811T991 1779T999 1733Q999 1680 970 1655T903 1630Q863 1630 835 1655T807
+1733Z" />
+<glyph unicode="&#xdd;" glyph-name="Yacute" horiz-adv-x="1079" d="M539 723L879 1462H1079L633 569V0H446V559L0 1462H203L539 723ZM442 1606Q464 1635 487 1673T534 1751T579 1832T616 1907H835V1886Q819 1859 788 1819T718 1734T639 1649T563 1579H442V1606Z" />
+<glyph unicode="&#xde;" glyph-name="Thorn" horiz-adv-x="1180" d="M1075 782Q1075 691 1048 607T957 459T791 356T535 317H385V0H199V1462H385V1210H561Q695 1210 792 1182T952 1099T1045 964T1075 782ZM385 475H514Q607 475 676 491T791 542T860 634T883 772Q883
+915 801 983T545 1051H385V475Z" />
+<glyph unicode="&#xdf;" glyph-name="germandbls" horiz-adv-x="1233" d="M1010 1260Q1010 1203 989 1159T936 1078T867 1011T798 954T745 899T723 842Q723 821 730 805T756 769T811 725T903 662Q959 625 1003 589T1077 512T1124 423T1141 313Q1141 226 1113 163T1035
+60T914 0T758 -20Q661 -20 592 -3T469 49V215Q495 199 527 184T596 156T670 137T745 129Q801 129 841 141T908 176T946 231T958 303Q958 339 950 368T920 426T862 483T770 547Q707 587 665 621T596 688T558 757T547 834Q547 888 567 927T619 998T686 1057T753 1113T804
+1175T825 1253Q825 1295 809 1326T762 1377T691 1407T598 1417Q549 1417 505 1408T428 1374T376 1309T356 1202V0H174V1200Q174 1304 205 1374T293 1487T428 1548T598 1567Q690 1567 766 1548T896 1491T980 1395T1010 1260Z" />
+<glyph unicode="&#xe0;" glyph-name="agrave" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445 967
+374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127ZM934 1241H814Q779
+1269 738 1310T659 1396T589 1480T541 1548V1569H760Q776 1535 797 1495T842 1414T889 1335T934 1268V1241Z" />
+<glyph unicode="&#xe1;" glyph-name="aacute" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445 967
+374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127ZM446 1268Q468 1297
+491 1335T538 1413T583 1494T620 1569H839V1548Q823 1521 792 1481T722 1396T643 1311T567 1241H446V1268Z" />
+<glyph unicode="&#xe2;" glyph-name="acircumflex" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445
+967 374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127ZM1148 1241H1027Q976
+1275 922 1323T816 1427Q762 1372 709 1324T605 1241H484V1268Q510 1297 544 1335T611 1413T674 1494T720 1569H912Q928 1535 957 1495T1020 1414T1088 1335T1148 1268V1241Z" />
+<glyph unicode="&#xe3;" glyph-name="atilde" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445 967
+374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127ZM955 1243Q915 1243
+876 1260T798 1299T725 1338T658 1356Q613 1356 589 1330T551 1241H447Q452 1301 468 1350T511 1433T575 1486T658 1505Q700 1505 740 1488T819 1449T891 1411T955 1393Q1000 1393 1023 1419T1061 1507H1166Q1161 1447 1145 1399T1102 1316T1038 1262T955 1243Z"
+/>
+<glyph unicode="&#xe4;" glyph-name="adieresis" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445
+967 374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127ZM529 1395Q529
+1449 557 1473T625 1497Q664 1497 693 1473T722 1395Q722 1342 693 1317T625 1292Q585 1292 557 1317T529 1395ZM904 1395Q904 1449 932 1473T1000 1497Q1019 1497 1036 1491T1067 1473T1088 1441T1096 1395Q1096 1342 1067 1317T1000 1292Q960 1292 932 1317T904
+1395Z" />
+<glyph unicode="&#xe5;" glyph-name="aring" horiz-adv-x="1087" d="M793 0L756 152H748Q715 107 682 75T610 21T523 -10T412 -20Q343 -20 285 -1T185 59T118 161T94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445 967
+374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q630 1118 704 1098T827 1033T900 919T924 752V0H793ZM459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305Q283 213 331 170T459 127ZM1039 1458Q1039
+1407 1022 1367T974 1298T901 1256T810 1241Q760 1241 719 1255T649 1298T603 1365T587 1456Q587 1507 603 1546T648 1614T719 1656T810 1671Q858 1671 900 1657T973 1615T1021 1548T1039 1458ZM922 1456Q922 1509 891 1539T810 1569Q760 1569 729 1539T697 1456Q697
+1403 725 1373T810 1343Q859 1343 890 1373T922 1456Z" />
+<glyph unicode="&#xe6;" glyph-name="ae" horiz-adv-x="1706" d="M94 307Q94 471 209 559T561 655L745 662V731Q745 798 731 843T689 915T621 955T528 967Q445 967 374 943T236 885L172 1022Q246 1062 337 1090T528 1118Q659 1118 742 1076T868 940Q919 1025 1002
+1071T1188 1118Q1285 1118 1362 1083T1493 983T1575 828T1604 627V514H932Q937 321 1010 230T1231 139Q1280 139 1322 144T1404 158T1480 182T1554 215V53Q1515 34 1478 20T1401 -3T1319 -16T1227 -20Q1089 -20 988 37T825 209Q791 155 753 113T668 41T562 -4T430
+-20Q359 -20 298 -1T191 59T120 161T94 307ZM283 305Q283 213 331 170T459 127Q520 127 572 146T662 203T721 300T743 438V537L600 530Q510 526 449 510T352 466T299 397T283 305ZM1184 967Q1074 967 1011 889T936 662H1407Q1407 730 1394 786T1354 883T1284 945T1184
+967Z" />
+<glyph unicode="&#xe7;" glyph-name="ccedilla" horiz-adv-x="948" d="M594 -20Q493 -20 405 11T252 111T150 286T113 543Q113 700 151 809T255 987T411 1087T602 1118Q680 1118 754 1101T879 1059L825 905Q802 915 774 924T716 941T657 953T602 958Q445 958 373
+858T301 545Q301 334 373 237T594 139Q675 139 740 157T860 201V39Q806 10 745 -5T594 -20ZM730 -287Q730 -384 660 -438T439 -492Q417 -492 395 -489T357 -483V-375Q372 -378 396 -379T437 -381Q508 -381 548 -360T588 -289Q588 -265 575 -248T539 -217T485 -195T416
+-176L506 0H627L570 -115Q604 -123 633 -136T683 -169T717 -219T730 -287Z" />
+<glyph unicode="&#xe8;" glyph-name="egrave" horiz-adv-x="1096" d="M608 -20Q498 -20 407 17T251 125T149 301T113 541Q113 677 146 784T239 965T382 1079T567 1118Q666 1118 745 1083T879 983T963 828T993 627V514H301Q306 321 382 230T610 139Q661 139 704
+144T788 158T867 182T944 215V53Q904 34 866 20T787 -3T703 -16T608 -20ZM563 967Q449 967 383 889T305 662H797Q797 730 784 786T742 883T669 945T563 967ZM934 1241H814Q779 1269 738 1310T659 1396T589 1480T541 1548V1569H760Q776 1535 797 1495T842 1414T889
+1335T934 1268V1241Z" />
+<glyph unicode="&#xe9;" glyph-name="eacute" horiz-adv-x="1096" d="M608 -20Q498 -20 407 17T251 125T149 301T113 541Q113 677 146 784T239 965T382 1079T567 1118Q666 1118 745 1083T879 983T963 828T993 627V514H301Q306 321 382 230T610 139Q661 139 704
+144T788 158T867 182T944 215V53Q904 34 866 20T787 -3T703 -16T608 -20ZM563 967Q449 967 383 889T305 662H797Q797 730 784 786T742 883T669 945T563 967ZM475 1268Q497 1297 520 1335T567 1413T612 1494T649 1569H868V1548Q852 1521 821 1481T751 1396T672 1311T596
+1241H475V1268Z" />
+<glyph unicode="&#xea;" glyph-name="ecircumflex" horiz-adv-x="1096" d="M608 -20Q498 -20 407 17T251 125T149 301T113 541Q113 677 146 784T239 965T382 1079T567 1118Q666 1118 745 1083T879 983T963 828T993 627V514H301Q306 321 382 230T610 139Q661 139
+704 144T788 158T867 182T944 215V53Q904 34 866 20T787 -3T703 -16T608 -20ZM563 967Q449 967 383 889T305 662H797Q797 730 784 786T742 883T669 945T563 967ZM1144 1241H1023Q972 1275 918 1323T812 1427Q758 1372 705 1324T601 1241H480V1268Q506 1297 540
+1335T607 1413T670 1494T716 1569H908Q924 1535 953 1495T1016 1414T1084 1335T1144 1268V1241Z" />
+<glyph unicode="&#xeb;" glyph-name="edieresis" horiz-adv-x="1096" d="M608 -20Q498 -20 407 17T251 125T149 301T113 541Q113 677 146 784T239 965T382 1079T567 1118Q666 1118 745 1083T879 983T963 828T993 627V514H301Q306 321 382 230T610 139Q661 139
+704 144T788 158T867 182T944 215V53Q904 34 866 20T787 -3T703 -16T608 -20ZM563 967Q449 967 383 889T305 662H797Q797 730 784 786T742 883T669 945T563 967ZM525 1395Q525 1449 553 1473T621 1497Q660 1497 689 1473T718 1395Q718 1342 689 1317T621 1292Q581
+1292 553 1317T525 1395ZM900 1395Q900 1449 928 1473T996 1497Q1015 1497 1032 1491T1063 1473T1084 1441T1092 1395Q1092 1342 1063 1317T996 1292Q956 1292 928 1317T900 1395Z" />
+<glyph unicode="&#xec;" glyph-name="igrave" horiz-adv-x="530" d="M356 0H174V1098H356V0ZM359 1241H239Q204 1269 163 1310T84 1396T14 1480T-34 1548V1569H185Q201 1535 222 1495T267 1414T314 1335T359 1268V1241Z" />
+<glyph unicode="&#xed;" glyph-name="iacute" horiz-adv-x="530" d="M356 0H174V1098H356V0ZM185 1268Q207 1297 230 1335T277 1413T322 1494T359 1569H578V1548Q562 1521 531 1481T461 1396T382 1311T306 1241H185V1268Z" />
+<glyph unicode="&#xee;" glyph-name="icircumflex" horiz-adv-x="530" d="M356 0H174V1098H356V0ZM597 1241H476Q425 1275 371 1323T265 1427Q211 1372 158 1324T54 1241H-67V1268Q-41 1297 -7 1335T60 1413T123 1494T169 1569H361Q377 1535 406 1495T469 1414T537
+1335T597 1268V1241Z" />
+<glyph unicode="&#xef;" glyph-name="idieresis" horiz-adv-x="530" d="M356 0H174V1098H356V0ZM-18 1395Q-18 1449 10 1473T78 1497Q117 1497 146 1473T175 1395Q175 1342 146 1317T78 1292Q38 1292 10 1317T-18 1395ZM357 1395Q357 1449 385 1473T453 1497Q472
+1497 489 1491T520 1473T541 1441T549 1395Q549 1342 520 1317T453 1292Q413 1292 385 1317T357 1395Z" />
+<glyph unicode="&#xf0;" glyph-name="eth" horiz-adv-x="1182" d="M1069 573Q1069 431 1036 321T940 135T788 20T588 -20Q484 -20 397 13T246 109T147 265T111 477Q111 596 142 688T233 843T376 938T565 971Q667 971 744 942T864 852L872 856Q841 974 781 1070T631
+1247L375 1094L301 1208L518 1339Q478 1367 436 1394T346 1448L416 1571Q481 1539 542 1503T662 1423L889 1561L963 1448L768 1331Q835 1266 890 1188T985 1017T1047 813T1069 573ZM881 526Q881 582 864 635T812 730T722 796T592 821Q515 821 461 798T371 731T320
+622T303 471Q303 395 319 333T371 225T461 156T592 131Q746 131 813 230T881 526Z" />
+<glyph unicode="&#xf1;" glyph-name="ntilde" horiz-adv-x="1206" d="M860 0V707Q860 837 808 902T643 967Q562 967 507 941T419 864T371 739T356 569V0H174V1098H322L348 950H358Q383 993 417 1024T493 1077T580 1108T674 1118Q857 1118 949 1023T1042 717V0H860ZM1015
+1243Q975 1243 936 1260T858 1299T785 1338T718 1356Q673 1356 649 1330T611 1241H507Q512 1301 528 1350T571 1433T635 1486T718 1505Q760 1505 800 1488T879 1449T951 1411T1015 1393Q1060 1393 1083 1419T1121 1507H1226Q1221 1447 1205 1399T1162 1316T1098
+1262T1015 1243Z" />
+<glyph unicode="&#xf2;" glyph-name="ograve" horiz-adv-x="1182" d="M1069 551Q1069 414 1036 308T940 129T788 18T588 -20Q485 -20 398 18T248 128T149 307T113 551Q113 687 146 792T242 970T393 1080T594 1118Q697 1118 784 1081T934 971T1033 793T1069 551ZM301
+551Q301 342 369 237T592 131Q746 131 813 236T881 551Q881 760 813 863T590 967Q436 967 369 864T301 551ZM1002 1241H882Q847 1269 806 1310T727 1396T657 1480T609 1548V1569H828Q844 1535 865 1495T910 1414T957 1335T1002 1268V1241Z" />
+<glyph unicode="&#xf3;" glyph-name="oacute" horiz-adv-x="1182" d="M1069 551Q1069 414 1036 308T940 129T788 18T588 -20Q485 -20 398 18T248 128T149 307T113 551Q113 687 146 792T242 970T393 1080T594 1118Q697 1118 784 1081T934 971T1033 793T1069 551ZM301
+551Q301 342 369 237T592 131Q746 131 813 236T881 551Q881 760 813 863T590 967Q436 967 369 864T301 551ZM473 1268Q495 1297 518 1335T565 1413T610 1494T647 1569H866V1548Q850 1521 819 1481T749 1396T670 1311T594 1241H473V1268Z" />
+<glyph unicode="&#xf4;" glyph-name="ocircumflex" horiz-adv-x="1182" d="M1069 551Q1069 414 1036 308T940 129T788 18T588 -20Q485 -20 398 18T248 128T149 307T113 551Q113 687 146 792T242 970T393 1080T594 1118Q697 1118 784 1081T934 971T1033 793T1069
+551ZM301 551Q301 342 369 237T592 131Q746 131 813 236T881 551Q881 760 813 863T590 967Q436 967 369 864T301 551ZM1173 1241H1052Q1001 1275 947 1323T841 1427Q787 1372 734 1324T630 1241H509V1268Q535 1297 569 1335T636 1413T699 1494T745 1569H937Q953
+1535 982 1495T1045 1414T1113 1335T1173 1268V1241Z" />
+<glyph unicode="&#xf5;" glyph-name="otilde" horiz-adv-x="1182" d="M1069 551Q1069 414 1036 308T940 129T788 18T588 -20Q485 -20 398 18T248 128T149 307T113 551Q113 687 146 792T242 970T393 1080T594 1118Q697 1118 784 1081T934 971T1033 793T1069 551ZM301
+551Q301 342 369 237T592 131Q746 131 813 236T881 551Q881 760 813 863T590 967Q436 967 369 864T301 551ZM992 1243Q952 1243 913 1260T835 1299T762 1338T695 1356Q650 1356 626 1330T588 1241H484Q489 1301 505 1350T548 1433T612 1486T695 1505Q737 1505 777
+1488T856 1449T928 1411T992 1393Q1037 1393 1060 1419T1098 1507H1203Q1198 1447 1182 1399T1139 1316T1075 1262T992 1243Z" />
+<glyph unicode="&#xf6;" glyph-name="odieresis" horiz-adv-x="1182" d="M1069 551Q1069 414 1036 308T940 129T788 18T588 -20Q485 -20 398 18T248 128T149 307T113 551Q113 687 146 792T242 970T393 1080T594 1118Q697 1118 784 1081T934 971T1033 793T1069
+551ZM301 551Q301 342 369 237T592 131Q746 131 813 236T881 551Q881 760 813 863T590 967Q436 967 369 864T301 551ZM556 1395Q556 1449 584 1473T652 1497Q691 1497 720 1473T749 1395Q749 1342 720 1317T652 1292Q612 1292 584 1317T556 1395ZM931 1395Q931
+1449 959 1473T1027 1497Q1046 1497 1063 1491T1094 1473T1115 1441T1123 1395Q1123 1342 1094 1317T1027 1292Q987 1292 959 1317T931 1395Z" />
+<glyph unicode="&#xf7;" glyph-name="divide" horiz-adv-x="1128" d="M102 647V797H1026V647H102ZM449 373Q449 408 458 431T482 470T518 491T563 498Q586 498 607 492T644 470T669 432T678 373Q678 340 669 317T644 278T607 255T563 248Q539 248 519 255T483
+277T458 316T449 373ZM449 1071Q449 1106 458 1129T482 1168T518 1189T563 1196Q586 1196 607 1190T644 1168T669 1130T678 1071Q678 1038 669 1015T644 976T607 953T563 946Q539 946 519 953T483 975T458 1014T449 1071Z" />
+<glyph unicode="&#xf8;" glyph-name="oslash" horiz-adv-x="1182" d="M1071 551Q1071 414 1038 308T942 129T790 18T590 -20Q465 -20 367 33L299 -76L168 -2L248 129Q185 201 150 307T115 551Q115 687 148 792T244 970T395 1080T596 1118Q659 1118 715 1104T821
+1061L889 1169L1020 1096L940 967Q1002 894 1036 790T1071 551ZM303 551Q303 467 312 402T344 285L741 932Q712 949 675 958T592 967Q438 967 371 864T303 551ZM883 551Q883 710 844 809L446 164Q477 147 513 139T594 131Q748 131 815 236T883 551Z" />
+<glyph unicode="&#xf9;" glyph-name="ugrave" horiz-adv-x="1206" d="M885 0L858 147H848Q823 104 789 73T713 21T626 -10T532 -20Q441 -20 372 3T257 75T188 200T164 381V1098H346V391Q346 261 399 196T563 131Q644 131 699 157T787 233T835 358T850 528V1098H1032V0H885ZM949
+1241H829Q794 1269 753 1310T674 1396T604 1480T556 1548V1569H775Q791 1535 812 1495T857 1414T904 1335T949 1268V1241Z" />
+<glyph unicode="&#xfa;" glyph-name="uacute" horiz-adv-x="1206" d="M885 0L858 147H848Q823 104 789 73T713 21T626 -10T532 -20Q441 -20 372 3T257 75T188 200T164 381V1098H346V391Q346 261 399 196T563 131Q644 131 699 157T787 233T835 358T850 528V1098H1032V0H885ZM489
+1268Q511 1297 534 1335T581 1413T626 1494T663 1569H882V1548Q866 1521 835 1481T765 1396T686 1311T610 1241H489V1268Z" />
+<glyph unicode="&#xfb;" glyph-name="ucircumflex" horiz-adv-x="1206" d="M885 0L858 147H848Q823 104 789 73T713 21T626 -10T532 -20Q441 -20 372 3T257 75T188 200T164 381V1098H346V391Q346 261 399 196T563 131Q644 131 699 157T787 233T835 358T850 528V1098H1032V0H885ZM930
+1241H809Q758 1275 704 1323T598 1427Q544 1372 491 1324T387 1241H266V1268Q292 1297 326 1335T393 1413T456 1494T502 1569H694Q710 1535 739 1495T802 1414T870 1335T930 1268V1241Z" />
+<glyph unicode="&#xfc;" glyph-name="udieresis" horiz-adv-x="1206" d="M885 0L858 147H848Q823 104 789 73T713 21T626 -10T532 -20Q441 -20 372 3T257 75T188 200T164 381V1098H346V391Q346 261 399 196T563 131Q644 131 699 157T787 233T835 358T850 528V1098H1032V0H885ZM309
+1395Q309 1449 337 1473T405 1497Q444 1497 473 1473T502 1395Q502 1342 473 1317T405 1292Q365 1292 337 1317T309 1395ZM684 1395Q684 1449 712 1473T780 1497Q799 1497 816 1491T847 1473T868 1441T876 1395Q876 1342 847 1317T780 1292Q740 1292 712 1317T684
+1395Z" />
+<glyph unicode="&#xfd;" glyph-name="yacute" horiz-adv-x="1001" d="M10 1098H199L414 485Q428 445 442 401T469 313T491 228T504 152H510Q515 177 526 220T550 311T578 407T604 487L803 1098H991L557 -143Q529 -224 497 -288T421 -398T320 -467T182 -492Q130
+-492 92 -487T27 -475V-330Q48 -335 80 -338T147 -342Q195 -342 230 -331T291 -297T335 -243T369 -170L426 -10L10 1098ZM407 1268Q429 1297 452 1335T499 1413T544 1494T581 1569H800V1548Q784 1521 753 1481T683 1396T604 1311T528 1241H407V1268Z" />
+<glyph unicode="&#xfe;" glyph-name="thorn" horiz-adv-x="1200" d="M356 950Q379 985 408 1015T475 1068T562 1104T670 1118Q764 1118 841 1082T972 975T1057 797T1087 551Q1087 410 1057 304T973 125T841 17T670 -20Q611 -20 563 -7T477 27T409 78T356 139H344Q347
+105 350 74Q352 48 354 21T356 -23V-492H174V1556H356V1098L348 950H356ZM635 967Q559 967 507 944T422 874T374 757T356 592V551Q356 450 369 372T415 240T502 159T637 131Q772 131 835 240T899 553Q899 761 836 864T635 967Z" />
+<glyph unicode="&#xff;" glyph-name="ydieresis" horiz-adv-x="1001" d="M10 1098H199L414 485Q428 445 442 401T469 313T491 228T504 152H510Q515 177 526 220T550 311T578 407T604 487L803 1098H991L557 -143Q529 -224 497 -288T421 -398T320 -467T182 -492Q130
+-492 92 -487T27 -475V-330Q48 -335 80 -338T147 -342Q195 -342 230 -331T291 -297T335 -243T369 -170L426 -10L10 1098ZM484 1395Q484 1449 512 1473T580 1497Q619 1497 648 1473T677 1395Q677 1342 648 1317T580 1292Q540 1292 512 1317T484 1395ZM859 1395Q859
+1449 887 1473T955 1497Q974 1497 991 1491T1022 1473T1043 1441T1051 1395Q1051 1342 1022 1317T955 1292Q915 1292 887 1317T859 1395Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="1024" d="M82 465V633H942V465H82Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="2048" d="M82 465V633H1966V465H82Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="358" d="M37 961L23 983Q37 1037 56 1098T99 1221T148 1344T199 1462H336Q321 1401 307 1335T279 1204T255 1076T236 961H37Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="358" d="M322 1462L336 1440Q322 1385 303 1325T260 1202T211 1078T160 961H23Q37 1021 51 1087T79 1219T104 1347T123 1462H322Z" />
+<glyph unicode="&#x201a;" glyph-name="quotesinglbase" horiz-adv-x="512" d="M362 238L377 215Q363 161 344 100T301 -23T252 -146T201 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H362Z" />
+<glyph unicode="&#x201c;" glyph-name="quotedblleft" horiz-adv-x="743" d="M422 961L408 983Q422 1037 441 1098T484 1221T533 1344T584 1462H721Q706 1401 692 1335T664 1204T640 1076T621 961H422ZM37 961L23 983Q37 1037 56 1098T99 1221T148 1344T199 1462H336Q321
+1401 307 1335T279 1204T255 1076T236 961H37Z" />
+<glyph unicode="&#x201d;" glyph-name="quotedblright" horiz-adv-x="743" d="M322 1462L336 1440Q322 1385 303 1325T260 1202T211 1078T160 961H23Q37 1021 51 1087T79 1219T104 1347T123 1462H322ZM707 1462L721 1440Q707 1385 688 1325T645 1202T596 1078T545
+961H408Q422 1021 436 1087T464 1219T489 1347T508 1462H707Z" />
+<glyph unicode="&#x201e;" glyph-name="quotedblbase" horiz-adv-x="897" d="M362 238L377 215Q363 161 344 100T301 -23T252 -146T201 -264H63Q78 -203 92 -137T120 -6T145 122T164 238H362ZM748 238L762 215Q748 161 729 100T686 -23T637 -146T586 -264H449Q463
+-203 477 -137T505 -6T530 122T549 238H748Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="770" d="M150 748Q150 819 168 869T217 950T292 996T385 1010Q434 1010 477 996T552 951T602 869T621 748Q621 678 603 628T552 547T477 500T385 485Q335 485 292 500T218 546T168 628T150 748Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="590" d="M82 553L391 967L508 889L270 541L508 193L391 115L82 526V553Z" />
+<glyph unicode="&#x203a;" glyph-name="guilsinglright" horiz-adv-x="590" d="M508 526L199 115L82 193L319 541L82 889L199 967L508 553V526Z" />
+</font>
+</defs>
+</svg>
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.ttf b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.ttf
new file mode 100644
index 0000000000..fb8cea662b
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.ttf
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff
new file mode 100644
index 0000000000..abf19899f7
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff2 b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff2
new file mode 100644
index 0000000000..9f93f74c3b
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/fonts/droid-sans-v6-latin-regular.woff2
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/explorer_icons.png b/catalog-be/src/main/resources/swagger/images/explorer_icons.png
new file mode 100644
index 0000000000..ed9d2fffb6
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/explorer_icons.png
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/favicon-16x16.png b/catalog-be/src/main/resources/swagger/images/favicon-16x16.png
new file mode 100644
index 0000000000..66b1a5bfb9
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/favicon-16x16.png
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/favicon-32x32.png b/catalog-be/src/main/resources/swagger/images/favicon-32x32.png
new file mode 100644
index 0000000000..32f319f89b
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/favicon-32x32.png
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/favicon.ico b/catalog-be/src/main/resources/swagger/images/favicon.ico
new file mode 100644
index 0000000000..8b60bcf06a
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/favicon.ico
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/logo_small.png b/catalog-be/src/main/resources/swagger/images/logo_small.png
new file mode 100644
index 0000000000..5496a65579
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/logo_small.png
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/pet_store_api.png b/catalog-be/src/main/resources/swagger/images/pet_store_api.png
new file mode 100644
index 0000000000..f9f9cd4aeb
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/pet_store_api.png
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/throbber.gif b/catalog-be/src/main/resources/swagger/images/throbber.gif
new file mode 100644
index 0000000000..0639388924
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/throbber.gif
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/images/wordnik_api.png b/catalog-be/src/main/resources/swagger/images/wordnik_api.png
new file mode 100644
index 0000000000..dca4f1455a
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/images/wordnik_api.png
Binary files differ
diff --git a/catalog-be/src/main/resources/swagger/index.html b/catalog-be/src/main/resources/swagger/index.html
new file mode 100644
index 0000000000..0e9a9e8de3
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/index.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Swagger UI</title>
+ <link rel="icon" type="image/png" href="images/favicon-32x32.png" sizes="32x32" />
+ <link rel="icon" type="image/png" href="images/favicon-16x16.png" sizes="16x16" />
+ <link href='css/typography.css' media='screen' rel='stylesheet' type='text/css'/>
+ <link href='css/reset.css' media='screen' rel='stylesheet' type='text/css'/>
+ <link href='css/screen.css' media='screen' rel='stylesheet' type='text/css'/>
+ <link href='css/reset.css' media='print' rel='stylesheet' type='text/css'/>
+ <link href='css/print.css' media='print' rel='stylesheet' type='text/css'/>
+ <script src='lib/jquery-1.8.0.min.js' type='text/javascript'></script>
+ <script src='lib/jquery.slideto.min.js' type='text/javascript'></script>
+ <script src='lib/jquery.wiggle.min.js' type='text/javascript'></script>
+ <script src='lib/jquery.ba-bbq.min.js' type='text/javascript'></script>
+ <script src='lib/handlebars-2.0.0.js' type='text/javascript'></script>
+ <script src='lib/underscore-min.js' type='text/javascript'></script>
+ <script src='lib/backbone-min.js' type='text/javascript'></script>
+ <script src='swagger-ui.js' type='text/javascript'></script>
+ <script src='lib/highlight.7.3.pack.js' type='text/javascript'></script>
+ <script src='lib/marked.js' type='text/javascript'></script>
+
+ <script type="text/javascript">
+ $(function () {
+ /* var url = window.location.search.match(/url=([^&]+)/);
+ if (url && url.length > 1) {
+ url = decodeURIComponent(url[1]);
+ } else {
+ url = "http://petstore.swagger.io/v2/swagger.json";
+ } */
+ var hostData= {};
+ var conf = getConfiguration(hostData);
+ var url = "http://" + hostData.beFqdn + "/sdc2/rest/swagger.json";
+ window.swaggerUi = new SwaggerUi({
+ url: url,
+ dom_id: "swagger-ui-container",
+ supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
+ onComplete: function(swaggerApi, swaggerUi){
+ if(typeof initOAuth == "function") {
+ /*
+ initOAuth({
+ clientId: "your-client-id",
+ realm: "your-realms",
+ appName: "your-app-name"
+ });
+ */
+ }
+ $('pre code').each(function(i, e) {
+ hljs.highlightBlock(e)
+ });
+ },
+ onFailure: function(data) {
+ log("Unable to Load SwaggerUI");
+ },
+ docExpansion: "none",
+ sorter : "alpha"
+ });
+
+ function addApiKeyAuthorization() {
+ var key = encodeURIComponent($('#input_apiKey')[0].value);
+ log("key: " + key);
+ if(key && key.trim() != "") {
+ var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("api_key", key, "query");
+ window.swaggerUi.api.clientAuthorizations.add("api_key", apiKeyAuth);
+ log("added key " + key);
+ }
+ }
+
+ $('#input_apiKey').change(addApiKeyAuthorization);
+
+ // if you have an apiKey you would like to pre-populate on the page for demonstration purposes...
+ /*
+ var apiKey = "myApiKeyXXXX123456789";
+ $('#input_apiKey').val(apiKey);
+ addApiKeyAuthorization();
+ */
+
+ window.swaggerUi.load();
+
+ function log() {
+ if ('console' in window) {
+ console.log.apply(console, arguments);
+ }
+ }
+
+ function getConfiguration(hostData){
+ $.ajax({
+ url: "/sdc2/rest/configmgr/get",
+ type: "GET",
+ async: false,
+
+ complete: function(data) {
+ if( data.status == 200 ){
+ var jsonConf = JSON.parse(data.responseText);
+ hostData.beFqdn = jsonConf.beFqdn;
+
+ }
+ else{
+ hostData.beFqdn = "localhost:8080";
+ }
+ }
+ });
+ }
+ });
+ </script>
+</head>
+
+<body class="swagger-section">
+<div id='header'>
+ <div class="swagger-ui-wrap">
+ <a id="logo" href="http://swagger.io">swagger</a>
+ <form id='api_selector'>
+ <div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"/></div>
+ <div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
+ <div class='input'><a id="explore" href="#">Explore</a></div>
+ </form>
+ </div>
+</div>
+
+<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
+<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
+</body>
+</html>
diff --git a/catalog-be/src/main/resources/swagger/lib/backbone-min.js b/catalog-be/src/main/resources/swagger/lib/backbone-min.js
new file mode 100644
index 0000000000..f082fcffca
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/backbone-min.js
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+// Backbone.js 1.1.2
+
+(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r(e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t);var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}return r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof p)return t;e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().replace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}};return e});
+
+// From http://stackoverflow.com/a/19431552
+// Compatibility override - Backbone 1.1 got rid of the 'options' binding
+// automatically to views in the constructor - we need to keep that.
+Backbone.View = (function(View) {
+ return View.extend({
+ constructor: function(options) {
+ this.options = options || {};
+ View.apply(this, arguments);
+ }
+ });
+})(Backbone.View);
diff --git a/catalog-be/src/main/resources/swagger/lib/handlebars-2.0.0.js b/catalog-be/src/main/resources/swagger/lib/handlebars-2.0.0.js
new file mode 100644
index 0000000000..0abd7a6149
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/handlebars-2.0.0.js
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*!
+
+ handlebars v2.0.0
+
+Copyright (C) 2011-2014 by Yehuda Katz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+@license
+*/
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.Handlebars=a.Handlebars||b()}(this,function(){var a=function(){"use strict";function a(a){this.string=a}var b;return a.prototype.toString=function(){return""+this.string},b=a}(),b=function(a){"use strict";function b(a){return i[a]}function c(a){for(var b=1;b<arguments.length;b++)for(var c in arguments[b])Object.prototype.hasOwnProperty.call(arguments[b],c)&&(a[c]=arguments[b][c]);return a}function d(a){return a instanceof h?a.toString():null==a?"":a?(a=""+a,k.test(a)?a.replace(j,b):a):a+""}function e(a){return a||0===a?n(a)&&0===a.length?!0:!1:!0}function f(a,b){return(a?a+".":"")+b}var g={},h=a,i={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},j=/[&<>"'`]/g,k=/[&<>"'`]/;g.extend=c;var l=Object.prototype.toString;g.toString=l;var m=function(a){return"function"==typeof a};m(/x/)&&(m=function(a){return"function"==typeof a&&"[object Function]"===l.call(a)});var m;g.isFunction=m;var n=Array.isArray||function(a){return a&&"object"==typeof a?"[object Array]"===l.call(a):!1};return g.isArray=n,g.escapeExpression=d,g.isEmpty=e,g.appendContextPath=f,g}(a),c=function(){"use strict";function a(a,b){var d;b&&b.firstLine&&(d=b.firstLine,a+=" - "+d+":"+b.firstColumn);for(var e=Error.prototype.constructor.call(this,a),f=0;f<c.length;f++)this[c[f]]=e[c[f]];d&&(this.lineNumber=d,this.column=b.firstColumn)}var b,c=["description","fileName","lineNumber","message","name","number","stack"];return a.prototype=new Error,b=a}(),d=function(a,b){"use strict";function c(a,b){this.helpers=a||{},this.partials=b||{},d(this)}function d(a){a.registerHelper("helperMissing",function(){if(1===arguments.length)return void 0;throw new g("Missing helper: '"+arguments[arguments.length-1].name+"'")}),a.registerHelper("blockHelperMissing",function(b,c){var d=c.inverse,e=c.fn;if(b===!0)return e(this);if(b===!1||null==b)return d(this);if(k(b))return b.length>0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):d(this);if(c.data&&c.ids){var g=q(c.data);g.contextPath=f.appendContextPath(c.data.contextPath,c.name),c={data:g}}return e(b,c)}),a.registerHelper("each",function(a,b){if(!b)throw new g("Must pass iterator to #each");var c,d,e=b.fn,h=b.inverse,i=0,j="";if(b.data&&b.ids&&(d=f.appendContextPath(b.data.contextPath,b.ids[0])+"."),l(a)&&(a=a.call(this)),b.data&&(c=q(b.data)),a&&"object"==typeof a)if(k(a))for(var m=a.length;m>i;i++)c&&(c.index=i,c.first=0===i,c.last=i===a.length-1,d&&(c.contextPath=d+i)),j+=e(a[i],{data:c});else for(var n in a)a.hasOwnProperty(n)&&(c&&(c.key=n,c.index=i,c.first=0===i,d&&(c.contextPath=d+n)),j+=e(a[n],{data:c}),i++);return 0===i&&(j=h(this)),j}),a.registerHelper("if",function(a,b){return l(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||f.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})}),a.registerHelper("with",function(a,b){l(a)&&(a=a.call(this));var c=b.fn;if(f.isEmpty(a))return b.inverse(this);if(b.data&&b.ids){var d=q(b.data);d.contextPath=f.appendContextPath(b.data.contextPath,b.ids[0]),b={data:d}}return c(a,b)}),a.registerHelper("log",function(b,c){var d=c.data&&null!=c.data.level?parseInt(c.data.level,10):1;a.log(d,b)}),a.registerHelper("lookup",function(a,b){return a&&a[b]})}var e={},f=a,g=b,h="2.0.0";e.VERSION=h;var i=6;e.COMPILER_REVISION=i;var j={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1"};e.REVISION_CHANGES=j;var k=f.isArray,l=f.isFunction,m=f.toString,n="[object Object]";e.HandlebarsEnvironment=c,c.prototype={constructor:c,logger:o,log:p,registerHelper:function(a,b){if(m.call(a)===n){if(b)throw new g("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){m.call(a)===n?f.extend(this.partials,a):this.partials[a]=b},unregisterPartial:function(a){delete this.partials[a]}};var o={methodMap:{0:"debug",1:"info",2:"warn",3:"error"},DEBUG:0,INFO:1,WARN:2,ERROR:3,level:3,log:function(a,b){if(o.level<=a){var c=o.methodMap[a];"undefined"!=typeof console&&console[c]&&console[c].call(console,b)}}};e.logger=o;var p=o.log;e.log=p;var q=function(a){var b=f.extend({},a);return b._parent=a,b};return e.createFrame=q,e}(b,c),e=function(a,b,c){"use strict";function d(a){var b=a&&a[0]||1,c=m;if(b!==c){if(c>b){var d=n[c],e=n[b];throw new l("Template was precompiled with an older version of Handlebars than the current runtime. Please update your precompiler to a newer version ("+d+") or downgrade your runtime to an older version ("+e+").")}throw new l("Template was precompiled with a newer version of Handlebars than the current runtime. Please update your runtime to a newer version ("+a[1]+").")}}function e(a,b){if(!b)throw new l("No environment passed to template");if(!a||!a.main)throw new l("Unknown template object: "+typeof a);b.VM.checkRevision(a.compiler);var c=function(c,d,e,f,g,h,i,j,m){g&&(f=k.extend({},f,g));var n=b.VM.invokePartial.call(this,c,e,f,h,i,j,m);if(null==n&&b.compile){var o={helpers:h,partials:i,data:j,depths:m};i[e]=b.compile(c,{data:void 0!==j,compat:a.compat},b),n=i[e](f,o)}if(null!=n){if(d){for(var p=n.split("\n"),q=0,r=p.length;r>q&&(p[q]||q+1!==r);q++)p[q]=d+p[q];n=p.join("\n")}return n}throw new l("The partial "+e+" could not be compiled when running in runtime-only mode")},d={lookup:function(a,b){for(var c=a.length,d=0;c>d;d++)if(a[d]&&null!=a[d][b])return a[d][b]},lambda:function(a,b){return"function"==typeof a?a.call(b):a},escapeExpression:k.escapeExpression,invokePartial:c,fn:function(b){return a[b]},programs:[],program:function(a,b,c){var d=this.programs[a],e=this.fn(a);return b||c?d=f(this,a,e,b,c):d||(d=this.programs[a]=f(this,a,e)),d},data:function(a,b){for(;a&&b--;)a=a._parent;return a},merge:function(a,b){var c=a||b;return a&&b&&a!==b&&(c=k.extend({},b,a)),c},noop:b.VM.noop,compilerInfo:a.compiler},e=function(b,c){c=c||{};var f=c.data;e._setup(c),!c.partial&&a.useData&&(f=i(b,f));var g;return a.useDepths&&(g=c.depths?[b].concat(c.depths):[b]),a.main.call(d,b,d.helpers,d.partials,f,g)};return e.isTop=!0,e._setup=function(c){c.partial?(d.helpers=c.helpers,d.partials=c.partials):(d.helpers=d.merge(c.helpers,b.helpers),a.usePartial&&(d.partials=d.merge(c.partials,b.partials)))},e._child=function(b,c,e){if(a.useDepths&&!e)throw new l("must pass parent depths");return f(d,b,a[b],c,e)},e}function f(a,b,c,d,e){var f=function(b,f){return f=f||{},c.call(a,b,a.helpers,a.partials,f.data||d,e&&[b].concat(e))};return f.program=b,f.depth=e?e.length:0,f}function g(a,b,c,d,e,f,g){var h={partial:!0,helpers:d,partials:e,data:f,depths:g};if(void 0===a)throw new l("The partial "+b+" could not be found");return a instanceof Function?a(c,h):void 0}function h(){return""}function i(a,b){return b&&"root"in b||(b=b?o(b):{},b.root=a),b}var j={},k=a,l=b,m=c.COMPILER_REVISION,n=c.REVISION_CHANGES,o=c.createFrame;return j.checkRevision=d,j.template=e,j.program=f,j.invokePartial=g,j.noop=h,j}(b,c,d),f=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c,j=d,k=e,l=function(){var a=new g.HandlebarsEnvironment;return j.extend(a,g),a.SafeString=h,a.Exception=i,a.Utils=j,a.escapeExpression=j.escapeExpression,a.VM=k,a.template=function(b){return k.template(b,a)},a},m=l();return m.create=l,m["default"]=m,f=m}(d,a,c,b,e),g=function(a){"use strict";function b(a){a=a||{},this.firstLine=a.first_line,this.firstColumn=a.first_column,this.lastColumn=a.last_column,this.lastLine=a.last_line}var c,d=a,e={ProgramNode:function(a,c,d){b.call(this,d),this.type="program",this.statements=a,this.strip=c},MustacheNode:function(a,c,d,f,g){if(b.call(this,g),this.type="mustache",this.strip=f,null!=d&&d.charAt){var h=d.charAt(3)||d.charAt(2);this.escaped="{"!==h&&"&"!==h}else this.escaped=!!d;this.sexpr=a instanceof e.SexprNode?a:new e.SexprNode(a,c),this.id=this.sexpr.id,this.params=this.sexpr.params,this.hash=this.sexpr.hash,this.eligibleHelper=this.sexpr.eligibleHelper,this.isHelper=this.sexpr.isHelper},SexprNode:function(a,c,d){b.call(this,d),this.type="sexpr",this.hash=c;var e=this.id=a[0],f=this.params=a.slice(1);this.isHelper=!(!f.length&&!c),this.eligibleHelper=this.isHelper||e.isSimple},PartialNode:function(a,c,d,e,f){b.call(this,f),this.type="partial",this.partialName=a,this.context=c,this.hash=d,this.strip=e,this.strip.inlineStandalone=!0},BlockNode:function(a,c,d,e,f){b.call(this,f),this.type="block",this.mustache=a,this.program=c,this.inverse=d,this.strip=e,d&&!c&&(this.isInverse=!0)},RawBlockNode:function(a,c,f,g){if(b.call(this,g),a.sexpr.id.original!==f)throw new d(a.sexpr.id.original+" doesn't match "+f,this);c=new e.ContentNode(c,g),this.type="block",this.mustache=a,this.program=new e.ProgramNode([c],{},g)},ContentNode:function(a,c){b.call(this,c),this.type="content",this.original=this.string=a},HashNode:function(a,c){b.call(this,c),this.type="hash",this.pairs=a},IdNode:function(a,c){b.call(this,c),this.type="ID";for(var e="",f=[],g=0,h="",i=0,j=a.length;j>i;i++){var k=a[i].part;if(e+=(a[i].separator||"")+k,".."===k||"."===k||"this"===k){if(f.length>0)throw new d("Invalid path: "+e,this);".."===k?(g++,h+="../"):this.isScoped=!0}else f.push(k)}this.original=e,this.parts=f,this.string=f.join("."),this.depth=g,this.idName=h+this.string,this.isSimple=1===a.length&&!this.isScoped&&0===g,this.stringModeValue=this.string},PartialNameNode:function(a,c){b.call(this,c),this.type="PARTIAL_NAME",this.name=a.original},DataNode:function(a,c){b.call(this,c),this.type="DATA",this.id=a,this.stringModeValue=a.stringModeValue,this.idName="@"+a.stringModeValue},StringNode:function(a,c){b.call(this,c),this.type="STRING",this.original=this.string=this.stringModeValue=a},NumberNode:function(a,c){b.call(this,c),this.type="NUMBER",this.original=this.number=a,this.stringModeValue=Number(a)},BooleanNode:function(a,c){b.call(this,c),this.type="BOOLEAN",this.bool=a,this.stringModeValue="true"===a},CommentNode:function(a,c){b.call(this,c),this.type="comment",this.comment=a,this.strip={inlineStandalone:!0}}};return c=e}(c),h=function(){"use strict";var a,b=function(){function a(){this.yy={}}var b={trace:function(){},yy:{},symbols_:{error:2,root:3,program:4,EOF:5,program_repetition0:6,statement:7,mustache:8,block:9,rawBlock:10,partial:11,CONTENT:12,COMMENT:13,openRawBlock:14,END_RAW_BLOCK:15,OPEN_RAW_BLOCK:16,sexpr:17,CLOSE_RAW_BLOCK:18,openBlock:19,block_option0:20,closeBlock:21,openInverse:22,block_option1:23,OPEN_BLOCK:24,CLOSE:25,OPEN_INVERSE:26,inverseAndProgram:27,INVERSE:28,OPEN_ENDBLOCK:29,path:30,OPEN:31,OPEN_UNESCAPED:32,CLOSE_UNESCAPED:33,OPEN_PARTIAL:34,partialName:35,param:36,partial_option0:37,partial_option1:38,sexpr_repetition0:39,sexpr_option0:40,dataName:41,STRING:42,NUMBER:43,BOOLEAN:44,OPEN_SEXPR:45,CLOSE_SEXPR:46,hash:47,hash_repetition_plus0:48,hashSegment:49,ID:50,EQUALS:51,DATA:52,pathSegments:53,SEP:54,$accept:0,$end:1},terminals_:{2:"error",5:"EOF",12:"CONTENT",13:"COMMENT",15:"END_RAW_BLOCK",16:"OPEN_RAW_BLOCK",18:"CLOSE_RAW_BLOCK",24:"OPEN_BLOCK",25:"CLOSE",26:"OPEN_INVERSE",28:"INVERSE",29:"OPEN_ENDBLOCK",31:"OPEN",32:"OPEN_UNESCAPED",33:"CLOSE_UNESCAPED",34:"OPEN_PARTIAL",42:"STRING",43:"NUMBER",44:"BOOLEAN",45:"OPEN_SEXPR",46:"CLOSE_SEXPR",50:"ID",51:"EQUALS",52:"DATA",54:"SEP"},productions_:[0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[10,3],[14,3],[9,4],[9,4],[19,3],[22,3],[27,2],[21,3],[8,3],[8,3],[11,5],[11,4],[17,3],[17,1],[36,1],[36,1],[36,1],[36,1],[36,1],[36,3],[47,1],[49,3],[35,1],[35,1],[35,1],[41,2],[30,1],[53,3],[53,1],[6,0],[6,2],[20,0],[20,1],[23,0],[23,1],[37,0],[37,1],[38,0],[38,1],[39,0],[39,2],[40,0],[40,1],[48,1],[48,2]],performAction:function(a,b,c,d,e,f){var g=f.length-1;switch(e){case 1:return d.prepareProgram(f[g-1].statements,!0),f[g-1];case 2:this.$=new d.ProgramNode(d.prepareProgram(f[g]),{},this._$);break;case 3:this.$=f[g];break;case 4:this.$=f[g];break;case 5:this.$=f[g];break;case 6:this.$=f[g];break;case 7:this.$=new d.ContentNode(f[g],this._$);break;case 8:this.$=new d.CommentNode(f[g],this._$);break;case 9:this.$=new d.RawBlockNode(f[g-2],f[g-1],f[g],this._$);break;case 10:this.$=new d.MustacheNode(f[g-1],null,"","",this._$);break;case 11:this.$=d.prepareBlock(f[g-3],f[g-2],f[g-1],f[g],!1,this._$);break;case 12:this.$=d.prepareBlock(f[g-3],f[g-2],f[g-1],f[g],!0,this._$);break;case 13:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 14:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 15:this.$={strip:d.stripFlags(f[g-1],f[g-1]),program:f[g]};break;case 16:this.$={path:f[g-1],strip:d.stripFlags(f[g-2],f[g])};break;case 17:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 18:this.$=new d.MustacheNode(f[g-1],null,f[g-2],d.stripFlags(f[g-2],f[g]),this._$);break;case 19:this.$=new d.PartialNode(f[g-3],f[g-2],f[g-1],d.stripFlags(f[g-4],f[g]),this._$);break;case 20:this.$=new d.PartialNode(f[g-2],void 0,f[g-1],d.stripFlags(f[g-3],f[g]),this._$);break;case 21:this.$=new d.SexprNode([f[g-2]].concat(f[g-1]),f[g],this._$);break;case 22:this.$=new d.SexprNode([f[g]],null,this._$);break;case 23:this.$=f[g];break;case 24:this.$=new d.StringNode(f[g],this._$);break;case 25:this.$=new d.NumberNode(f[g],this._$);break;case 26:this.$=new d.BooleanNode(f[g],this._$);break;case 27:this.$=f[g];break;case 28:f[g-1].isHelper=!0,this.$=f[g-1];break;case 29:this.$=new d.HashNode(f[g],this._$);break;case 30:this.$=[f[g-2],f[g]];break;case 31:this.$=new d.PartialNameNode(f[g],this._$);break;case 32:this.$=new d.PartialNameNode(new d.StringNode(f[g],this._$),this._$);break;case 33:this.$=new d.PartialNameNode(new d.NumberNode(f[g],this._$));break;case 34:this.$=new d.DataNode(f[g],this._$);break;case 35:this.$=new d.IdNode(f[g],this._$);break;case 36:f[g-2].push({part:f[g],separator:f[g-1]}),this.$=f[g-2];break;case 37:this.$=[{part:f[g]}];break;case 38:this.$=[];break;case 39:f[g-1].push(f[g]);break;case 48:this.$=[];break;case 49:f[g-1].push(f[g]);break;case 52:this.$=[f[g]];break;case 53:f[g-1].push(f[g])}},table:[{3:1,4:2,5:[2,38],6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],31:[2,38],32:[2,38],34:[2,38]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:[1,10],13:[1,11],14:16,16:[1,20],19:14,22:15,24:[1,18],26:[1,19],28:[2,2],29:[2,2],31:[1,12],32:[1,13],34:[1,17]},{1:[2,1]},{5:[2,39],12:[2,39],13:[2,39],16:[2,39],24:[2,39],26:[2,39],28:[2,39],29:[2,39],31:[2,39],32:[2,39],34:[2,39]},{5:[2,3],12:[2,3],13:[2,3],16:[2,3],24:[2,3],26:[2,3],28:[2,3],29:[2,3],31:[2,3],32:[2,3],34:[2,3]},{5:[2,4],12:[2,4],13:[2,4],16:[2,4],24:[2,4],26:[2,4],28:[2,4],29:[2,4],31:[2,4],32:[2,4],34:[2,4]},{5:[2,5],12:[2,5],13:[2,5],16:[2,5],24:[2,5],26:[2,5],28:[2,5],29:[2,5],31:[2,5],32:[2,5],34:[2,5]},{5:[2,6],12:[2,6],13:[2,6],16:[2,6],24:[2,6],26:[2,6],28:[2,6],29:[2,6],31:[2,6],32:[2,6],34:[2,6]},{5:[2,7],12:[2,7],13:[2,7],16:[2,7],24:[2,7],26:[2,7],28:[2,7],29:[2,7],31:[2,7],32:[2,7],34:[2,7]},{5:[2,8],12:[2,8],13:[2,8],16:[2,8],24:[2,8],26:[2,8],28:[2,8],29:[2,8],31:[2,8],32:[2,8],34:[2,8]},{17:21,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:27,30:22,41:23,50:[1,26],52:[1,25],53:24},{4:28,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{4:29,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],28:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{12:[1,30]},{30:32,35:31,42:[1,33],43:[1,34],50:[1,26],53:24},{17:35,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:36,30:22,41:23,50:[1,26],52:[1,25],53:24},{17:37,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[1,38]},{18:[2,48],25:[2,48],33:[2,48],39:39,42:[2,48],43:[2,48],44:[2,48],45:[2,48],46:[2,48],50:[2,48],52:[2,48]},{18:[2,22],25:[2,22],33:[2,22],46:[2,22]},{18:[2,35],25:[2,35],33:[2,35],42:[2,35],43:[2,35],44:[2,35],45:[2,35],46:[2,35],50:[2,35],52:[2,35],54:[1,40]},{30:41,50:[1,26],53:24},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],52:[2,37],54:[2,37]},{33:[1,42]},{20:43,27:44,28:[1,45],29:[2,40]},{23:46,27:47,28:[1,45],29:[2,42]},{15:[1,48]},{25:[2,46],30:51,36:49,38:50,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],47:57,48:58,49:60,50:[1,59],52:[1,25],53:24},{25:[2,31],42:[2,31],43:[2,31],44:[2,31],45:[2,31],50:[2,31],52:[2,31]},{25:[2,32],42:[2,32],43:[2,32],44:[2,32],45:[2,32],50:[2,32],52:[2,32]},{25:[2,33],42:[2,33],43:[2,33],44:[2,33],45:[2,33],50:[2,33],52:[2,33]},{25:[1,61]},{25:[1,62]},{18:[1,63]},{5:[2,17],12:[2,17],13:[2,17],16:[2,17],24:[2,17],26:[2,17],28:[2,17],29:[2,17],31:[2,17],32:[2,17],34:[2,17]},{18:[2,50],25:[2,50],30:51,33:[2,50],36:65,40:64,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],46:[2,50],47:66,48:58,49:60,50:[1,59],52:[1,25],53:24},{50:[1,67]},{18:[2,34],25:[2,34],33:[2,34],42:[2,34],43:[2,34],44:[2,34],45:[2,34],46:[2,34],50:[2,34],52:[2,34]},{5:[2,18],12:[2,18],13:[2,18],16:[2,18],24:[2,18],26:[2,18],28:[2,18],29:[2,18],31:[2,18],32:[2,18],34:[2,18]},{21:68,29:[1,69]},{29:[2,41]},{4:70,6:3,12:[2,38],13:[2,38],16:[2,38],24:[2,38],26:[2,38],29:[2,38],31:[2,38],32:[2,38],34:[2,38]},{21:71,29:[1,69]},{29:[2,43]},{5:[2,9],12:[2,9],13:[2,9],16:[2,9],24:[2,9],26:[2,9],28:[2,9],29:[2,9],31:[2,9],32:[2,9],34:[2,9]},{25:[2,44],37:72,47:73,48:58,49:60,50:[1,74]},{25:[1,75]},{18:[2,23],25:[2,23],33:[2,23],42:[2,23],43:[2,23],44:[2,23],45:[2,23],46:[2,23],50:[2,23],52:[2,23]},{18:[2,24],25:[2,24],33:[2,24],42:[2,24],43:[2,24],44:[2,24],45:[2,24],46:[2,24],50:[2,24],52:[2,24]},{18:[2,25],25:[2,25],33:[2,25],42:[2,25],43:[2,25],44:[2,25],45:[2,25],46:[2,25],50:[2,25],52:[2,25]},{18:[2,26],25:[2,26],33:[2,26],42:[2,26],43:[2,26],44:[2,26],45:[2,26],46:[2,26],50:[2,26],52:[2,26]},{18:[2,27],25:[2,27],33:[2,27],42:[2,27],43:[2,27],44:[2,27],45:[2,27],46:[2,27],50:[2,27],52:[2,27]},{17:76,30:22,41:23,50:[1,26],52:[1,25],53:24},{25:[2,47]},{18:[2,29],25:[2,29],33:[2,29],46:[2,29],49:77,50:[1,74]},{18:[2,37],25:[2,37],33:[2,37],42:[2,37],43:[2,37],44:[2,37],45:[2,37],46:[2,37],50:[2,37],51:[1,78],52:[2,37],54:[2,37]},{18:[2,52],25:[2,52],33:[2,52],46:[2,52],50:[2,52]},{12:[2,13],13:[2,13],16:[2,13],24:[2,13],26:[2,13],28:[2,13],29:[2,13],31:[2,13],32:[2,13],34:[2,13]},{12:[2,14],13:[2,14],16:[2,14],24:[2,14],26:[2,14],28:[2,14],29:[2,14],31:[2,14],32:[2,14],34:[2,14]},{12:[2,10]},{18:[2,21],25:[2,21],33:[2,21],46:[2,21]},{18:[2,49],25:[2,49],33:[2,49],42:[2,49],43:[2,49],44:[2,49],45:[2,49],46:[2,49],50:[2,49],52:[2,49]},{18:[2,51],25:[2,51],33:[2,51],46:[2,51]},{18:[2,36],25:[2,36],33:[2,36],42:[2,36],43:[2,36],44:[2,36],45:[2,36],46:[2,36],50:[2,36],52:[2,36],54:[2,36]},{5:[2,11],12:[2,11],13:[2,11],16:[2,11],24:[2,11],26:[2,11],28:[2,11],29:[2,11],31:[2,11],32:[2,11],34:[2,11]},{30:79,50:[1,26],53:24},{29:[2,15]},{5:[2,12],12:[2,12],13:[2,12],16:[2,12],24:[2,12],26:[2,12],28:[2,12],29:[2,12],31:[2,12],32:[2,12],34:[2,12]},{25:[1,80]},{25:[2,45]},{51:[1,78]},{5:[2,20],12:[2,20],13:[2,20],16:[2,20],24:[2,20],26:[2,20],28:[2,20],29:[2,20],31:[2,20],32:[2,20],34:[2,20]},{46:[1,81]},{18:[2,53],25:[2,53],33:[2,53],46:[2,53],50:[2,53]},{30:51,36:82,41:55,42:[1,52],43:[1,53],44:[1,54],45:[1,56],50:[1,26],52:[1,25],53:24},{25:[1,83]},{5:[2,19],12:[2,19],13:[2,19],16:[2,19],24:[2,19],26:[2,19],28:[2,19],29:[2,19],31:[2,19],32:[2,19],34:[2,19]},{18:[2,28],25:[2,28],33:[2,28],42:[2,28],43:[2,28],44:[2,28],45:[2,28],46:[2,28],50:[2,28],52:[2,28]},{18:[2,30],25:[2,30],33:[2,30],46:[2,30],50:[2,30]},{5:[2,16],12:[2,16],13:[2,16],16:[2,16],24:[2,16],26:[2,16],28:[2,16],29:[2,16],31:[2,16],32:[2,16],34:[2,16]}],defaultActions:{4:[2,1],44:[2,41],47:[2,43],57:[2,47],63:[2,10],70:[2,15],73:[2,45]},parseError:function(a){throw new Error(a)},parse:function(a){function b(){var a;return a=c.lexer.lex()||1,"number"!=typeof a&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0;this.lexer.setInput(a),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,this.yy.parser=this,"undefined"==typeof this.lexer.yylloc&&(this.lexer.yylloc={});var l=this.lexer.yylloc;f.push(l);var m=this.lexer.options&&this.lexer.options.ranges;"function"==typeof this.yy.parseError&&(this.parseError=this.yy.parseError);for(var n,o,p,q,r,s,t,u,v,w={};;){if(p=d[d.length-1],this.defaultActions[p]?q=this.defaultActions[p]:((null===n||"undefined"==typeof n)&&(n=b()),q=g[p]&&g[p][n]),"undefined"==typeof q||!q.length||!q[0]){var x="";if(!k){v=[];for(s in g[p])this.terminals_[s]&&s>2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;g<f.length&&(c=this._input.match(this.rules[f[g]]),!c||b&&!(c[0].length>b[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substr(a,b.yyleng-c)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 12;break;case 1:return 12;case 2:return this.popState(),12;case 3:return b.yytext=b.yytext.substr(5,b.yyleng-9),this.popState(),15;case 4:return 12;case 5:return e(0,4),this.popState(),13;case 6:return 45;case 7:return 46;case 8:return 16;case 9:return this.popState(),this.begin("raw"),18;case 10:return 34;case 11:return 24;case 12:return 29;case 13:return this.popState(),28;case 14:return this.popState(),28;case 15:return 26;case 16:return 26;case 17:return 32;case 18:return 31;case 19:this.popState(),this.begin("com");break;case 20:return e(3,5),this.popState(),13;case 21:return 31;case 22:return 51;case 23:return 50;case 24:return 50;case 25:return 54;case 26:break;case 27:return this.popState(),33;case 28:return this.popState(),25;case 29:return b.yytext=e(1,2).replace(/\\"/g,'"'),42;case 30:return b.yytext=e(1,2).replace(/\\'/g,"'"),42;case 31:return 52;case 32:return 44;case 33:return 44;case 34:return 43;case 35:return 50;case 36:return b.yytext=e(1,2),50;case 37:return"INVALID";case 38:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[5],inclusive:!1},raw:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,1,38],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();return a=b}(),i=function(a){"use strict";function b(a,b){return{left:"~"===a.charAt(2),right:"~"===b.charAt(b.length-3)}}function c(a,b,c,d,i,k){if(a.sexpr.id.original!==d.path.original)throw new j(a.sexpr.id.original+" doesn't match "+d.path.original,a);var l=c&&c.program,m={left:a.strip.left,right:d.strip.right,openStandalone:f(b.statements),closeStandalone:e((l||b).statements)};if(a.strip.right&&g(b.statements,null,!0),l){var n=c.strip;n.left&&h(b.statements,null,!0),n.right&&g(l.statements,null,!0),d.strip.left&&h(l.statements,null,!0),e(b.statements)&&f(l.statements)&&(h(b.statements),g(l.statements))}else d.strip.left&&h(b.statements,null,!0);return i?new this.BlockNode(a,l,b,m,k):new this.BlockNode(a,b,l,m,k)}function d(a,b){for(var c=0,d=a.length;d>c;c++){var i=a[c],j=i.strip;if(j){var k=e(a,c,b,"partial"===i.type),l=f(a,c,b),m=j.openStandalone&&k,n=j.closeStandalone&&l,o=j.inlineStandalone&&k&&l;j.right&&g(a,c,!0),j.left&&h(a,c,!0),o&&(g(a,c),h(a,c)&&"partial"===i.type&&(i.indent=/([ \t]+$)/.exec(a[c-1].original)?RegExp.$1:"")),m&&(g((i.program||i.inverse).statements),h(a,c)),n&&(g(a,c),h((i.inverse||i.program).statements))}}return a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"content"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"content"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"content"===d.type&&(c||!d.rightStripped)){var e=d.string;d.string=d.string.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.string!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"content"===d.type&&(c||!d.leftStripped)){var e=d.string;return d.string=d.string.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.string!==e,d.leftStripped}}var i={},j=a;return i.stripFlags=b,i.prepareBlock=c,i.prepareProgram=d,i}(c),j=function(a,b,c,d){"use strict";function e(a){return a.constructor===h.ProgramNode?a:(g.yy=k,g.parse(a))}var f={},g=a,h=b,i=c,j=d.extend;f.parser=g;var k={};return j(k,i,h),f.parse=e,f}(h,g,i,b),k=function(a,b){"use strict";function c(){}function d(a,b,c){if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function e(a,b,c){function d(){var d=c.parse(a),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}if(null==a||"string"!=typeof a&&a.constructor!==c.AST.ProgramNode)throw new h("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var e,f=function(a,b){return e||(e=d()),e.call(this,a,b)};return f._setup=function(a){return e||(e=d()),e._setup(a)},f._child=function(a,b,c){return e||(e=d()),e._child(a,b,c)},f}function f(a,b){if(a===b)return!0;if(i(a)&&i(b)&&a.length===b.length){for(var c=0;c<a.length;c++)if(!f(a[c],b[c]))return!1;return!0}}var g={},h=a,i=b.isArray,j=[].slice;return g.Compiler=c,c.prototype={compiler:c,equals:function(a){var b=this.opcodes.length;if(a.opcodes.length!==b)return!1;for(var c=0;b>c;c++){var d=this.opcodes[c],e=a.opcodes[c];if(d.opcode!==e.opcode||!f(d.args,e.args))return!1}for(b=this.children.length,c=0;b>c;c++)if(!this.children[c].equals(a.children[c]))return!1;return!0},guid:0,compile:function(a,b){this.opcodes=[],this.children=[],this.depths={list:[]},this.options=b,this.stringParams=b.stringParams,this.trackIds=b.trackIds;var c=this.options.knownHelpers;if(this.options.knownHelpers={helperMissing:!0,blockHelperMissing:!0,each:!0,"if":!0,unless:!0,"with":!0,log:!0,lookup:!0},c)for(var d in c)this.options.knownHelpers[d]=c[d];return this.accept(a)},accept:function(a){return this[a.type](a)},program:function(a){for(var b=a.statements,c=0,d=b.length;d>c;c++)this.accept(b[c]);return this.isSimple=1===d,this.depths.list=this.depths.list.sort(function(a,b){return a-b}),this},compileProgram:function(a){var b,c=(new this.compiler).compile(a,this.options),d=this.guid++;
+this.usePartial=this.usePartial||c.usePartial,this.children[d]=c;for(var e=0,f=c.depths.list.length;f>e;e++)b=c.depths.list[e],2>b||this.addDepth(b-1);return d},block:function(a){var b=a.mustache,c=a.program,d=a.inverse;c&&(c=this.compileProgram(c)),d&&(d=this.compileProgram(d));var e=b.sexpr,f=this.classifySexpr(e);"helper"===f?this.helperSexpr(e,c,d):"simple"===f?(this.simpleSexpr(e),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("blockValue",e.id.original)):(this.ambiguousSexpr(e,c,d),this.opcode("pushProgram",c),this.opcode("pushProgram",d),this.opcode("emptyHash"),this.opcode("ambiguousBlockValue")),this.opcode("append")},hash:function(a){var b,c,d=a.pairs;for(this.opcode("pushHash"),b=0,c=d.length;c>b;b++)this.pushParam(d[b][1]);for(;b--;)this.opcode("assignToHash",d[b][0]);this.opcode("popHash")},partial:function(a){var b=a.partialName;this.usePartial=!0,a.hash?this.accept(a.hash):this.opcode("push","undefined"),a.context?this.accept(a.context):(this.opcode("getContext",0),this.opcode("pushContext")),this.opcode("invokePartial",b.name,a.indent||""),this.opcode("append")},content:function(a){a.string&&this.opcode("appendContent",a.string)},mustache:function(a){this.sexpr(a.sexpr),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},ambiguousSexpr:function(a,b,c){var d=a.id,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),this.ID(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.id;"DATA"===b.type?this.DATA(b):b.parts.length?this.ID(b):(this.addDepth(b.depth),this.opcode("getContext",b.depth),this.opcode("pushContext")),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.id,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new h("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.falsy=!0,this.ID(e),this.opcode("invokeHelper",d.length,e.original,e.isSimple)}},sexpr:function(a){var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ID:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0];b?this.opcode("lookupOnContext",a.parts,a.falsy,a.isScoped):this.opcode("pushContext")},DATA:function(a){this.options.data=!0,this.opcode("lookupData",a.id.depth,a.id.parts)},STRING:function(a){this.opcode("pushString",a.string)},NUMBER:function(a){this.opcode("pushLiteral",a.number)},BOOLEAN:function(a){this.opcode("pushLiteral",a.bool)},comment:function(){},opcode:function(a){this.opcodes.push({opcode:a,args:j.call(arguments,1)})},addDepth:function(a){0!==a&&(this.depths[a]||(this.depths[a]=!0,this.depths.list.push(a)))},classifySexpr:function(a){var b=a.isHelper,c=a.eligibleHelper,d=this.options;if(c&&!b){var e=a.id.parts[0];d.knownHelpers[e]?b=!0:d.knownHelpersOnly&&(c=!1)}return b?"helper":c?"ambiguous":"simple"},pushParams:function(a){for(var b=0,c=a.length;c>b;b++)this.pushParam(a[b])},pushParam:function(a){this.stringParams?(a.depth&&this.addDepth(a.depth),this.opcode("getContext",a.depth||0),this.opcode("pushStringParam",a.stringModeValue,a.type),"sexpr"===a.type&&this.sexpr(a)):(this.trackIds&&this.opcode("pushId",a.type,a.idName||a.stringModeValue),this.accept(a))},setupFullMustacheParams:function(a,b,c){var d=a.params;return this.pushParams(d),this.opcode("pushProgram",b),this.opcode("pushProgram",c),a.hash?this.hash(a.hash):this.opcode("emptyHash"),d}},g.precompile=d,g.compile=e,g}(c,b),l=function(a,b){"use strict";function c(a){this.value=a}function d(){}var e,f=a.COMPILER_REVISION,g=a.REVISION_CHANGES,h=b;d.prototype={nameLookup:function(a,b){return d.isValidJavaScriptVariableName(b)?a+"."+b:a+"['"+b+"']"},depthedLookup:function(a){return this.aliases.lookup="this.lookup",'lookup(depths, "'+a+'")'},compilerInfo:function(){var a=f,b=g[a];return[a,b]},appendToBuffer:function(a){return this.environment.isSimple?"return "+a+";":{appendToBuffer:!0,content:a,toString:function(){return"buffer += "+a+";"}}},initializeBuffer:function(){return this.quotedString("")},namespace:"Handlebars",compile:function(a,b,c,d){this.environment=a,this.options=b,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!d,this.name=this.environment.name,this.isChild=!!c,this.context=c||{programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.compileChildren(a,b),this.useDepths=this.useDepths||a.depths.list.length||this.options.compat;var e,f,g,i=a.opcodes;for(f=0,g=i.length;g>f;f++)e=i[f],this[e.opcode].apply(this,e.args);if(this.pushSource(""),this.stackSlot||this.inlineStack.length||this.compileStack.length)throw new h("Compile completed with content left on stack");var j=this.createFunctionContext(d);if(this.isChild)return j;var k={compiler:this.compilerInfo(),main:j},l=this.context.programs;for(f=0,g=l.length;g>f;f++)l[f]&&(k[f]=l[f]);return this.environment.usePartial&&(k.usePartial=!0),this.options.data&&(k.useData=!0),this.useDepths&&(k.useDepths=!0),this.options.compat&&(k.compat=!0),d||(k.compiler=JSON.stringify(k.compiler),k=this.objectLiteral(k)),k},preamble:function(){this.lastContext=0,this.source=[]},createFunctionContext:function(a){var b="",c=this.stackVars.concat(this.registers.list);c.length>0&&(b+=", "+c.join(", "));for(var d in this.aliases)this.aliases.hasOwnProperty(d)&&(b+=", "+d+"="+this.aliases[d]);var e=["depth0","helpers","partials","data"];this.useDepths&&e.push("depths");var f=this.mergeSource(b);return a?(e.push(f),Function.apply(this,e)):"function("+e.join(",")+") {\n "+f+"}"},mergeSource:function(a){for(var b,c,d="",e=!this.forceBuffer,f=0,g=this.source.length;g>f;f++){var h=this.source[f];h.appendToBuffer?b=b?b+"\n + "+h.content:h.content:(b&&(d?d+="buffer += "+b+";\n ":(c=!0,d=b+";\n "),b=void 0),d+=h+"\n ",this.environment.isSimple||(e=!1))}return e?(b||!d)&&(d+="return "+(b||'""')+";\n"):(a+=", buffer = "+(c?"":this.initializeBuffer()),d+=b?"return buffer + "+b+";\n":"return buffer;\n"),a&&(d="var "+a.substring(2)+(c?"":";\n ")+d),d},blockValue:function(a){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var b=[this.contextName(0)];this.setupParams(a,0,b);var c=this.popStack();b.splice(1,0,c),this.push("blockHelperMissing.call("+b.join(", ")+")")},ambiguousBlockValue:function(){this.aliases.blockHelperMissing="helpers.blockHelperMissing";var a=[this.contextName(0)];this.setupParams("",0,a,!0),this.flushInline();var b=this.topStack();a.splice(1,0,b),this.pushSource("if (!"+this.lastHelper+") { "+b+" = blockHelperMissing.call("+a.join(", ")+"); }")},appendContent:function(a){this.pendingContent&&(a=this.pendingContent+a),this.pendingContent=a},append:function(){this.flushInline();var a=this.popStack();this.pushSource("if ("+a+" != null) { "+this.appendToBuffer(a)+" }"),this.environment.isSimple&&this.pushSource("else { "+this.appendToBuffer("''")+" }")},appendEscaped:function(){this.aliases.escapeExpression="this.escapeExpression",this.pushSource(this.appendToBuffer("escapeExpression("+this.popStack()+")"))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c){var d=0,e=a.length;for(c||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[d++]));e>d;d++)this.replaceStack(function(c){var e=this.nameLookup(c,a[d],"context");return b?" && "+e:" != null ? "+e+" : "+c})},lookupData:function(a,b){a?this.pushStackLiteral("this.data(data, "+a+")"):this.pushStackLiteral("data");for(var c=b.length,d=0;c>d;d++)this.replaceStack(function(a){return" && "+this.nameLookup(a,b[d],"data")})},resolvePossibleLambda:function(){this.aliases.lambda="this.lambda",this.push("lambda("+this.popStack()+", "+this.contextName(0)+")")},pushStringParam:function(a,b){this.pushContext(),this.pushString(b),"sexpr"!==b&&("string"==typeof a?this.pushString(a):this.pushStackLiteral(a))},emptyHash:function(){this.pushStackLiteral("{}"),this.trackIds&&this.push("{}"),this.stringParams&&(this.push("{}"),this.push("{}"))},pushHash:function(){this.hash&&this.hashes.push(this.hash),this.hash={values:[],types:[],contexts:[],ids:[]}},popHash:function(){var a=this.hash;this.hash=this.hashes.pop(),this.trackIds&&this.push("{"+a.ids.join(",")+"}"),this.stringParams&&(this.push("{"+a.contexts.join(",")+"}"),this.push("{"+a.types.join(",")+"}")),this.push("{\n "+a.values.join(",\n ")+"\n }")},pushString:function(a){this.pushStackLiteral(this.quotedString(a))},push:function(a){return this.inlineStack.push(a),a},pushLiteral:function(a){this.pushStackLiteral(a)},pushProgram:function(a){null!=a?this.pushStackLiteral(this.programExpression(a)):this.pushStackLiteral(null)},invokeHelper:function(a,b,c){this.aliases.helperMissing="helpers.helperMissing";var d=this.popStack(),e=this.setupHelper(a,b),f=(c?e.name+" || ":"")+d+" || helperMissing";this.push("(("+f+").call("+e.callParams+"))")},invokeKnownHelper:function(a,b){var c=this.setupHelper(a,b);this.push(c.name+".call("+c.callParams+")")},invokeAmbiguous:function(a,b){this.aliases.functionType='"function"',this.aliases.helperMissing="helpers.helperMissing",this.useRegister("helper");var c=this.popStack();this.emptyHash();var d=this.setupHelper(0,a,b),e=this.lastHelper=this.nameLookup("helpers",a,"helper");this.push("((helper = (helper = "+e+" || "+c+") != null ? helper : helperMissing"+(d.paramsInit?"),("+d.paramsInit:"")+"),(typeof helper === functionType ? helper.call("+d.callParams+") : helper))")},invokePartial:function(a,b){var c=[this.nameLookup("partials",a,"partial"),"'"+b+"'","'"+a+"'",this.popStack(),this.popStack(),"helpers","partials"];this.options.data?c.push("data"):this.options.compat&&c.push("undefined"),this.options.compat&&c.push("depths"),this.push("this.invokePartial("+c.join(", ")+")")},assignToHash:function(a){var b,c,d,e=this.popStack();this.trackIds&&(d=this.popStack()),this.stringParams&&(c=this.popStack(),b=this.popStack());var f=this.hash;b&&f.contexts.push("'"+a+"': "+b),c&&f.types.push("'"+a+"': "+c),d&&f.ids.push("'"+a+"': "+d),f.values.push("'"+a+"': ("+e+")")},pushId:function(a,b){"ID"===a||"DATA"===a?this.pushString(b):"sexpr"===a?this.pushStackLiteral("true"):this.pushStackLiteral("null")},compiler:d,compileChildren:function(a,b){for(var c,d,e=a.children,f=0,g=e.length;g>f;f++){c=e[f],d=new this.compiler;var h=this.matchExistingProgram(c);null==h?(this.context.programs.push(""),h=this.context.programs.length,c.index=h,c.name="program"+h,this.context.programs[h]=d.compile(c,b,this.context,!this.precompile),this.context.environments[h]=c,this.useDepths=this.useDepths||d.useDepths):(c.index=h,c.name="program"+h)}},matchExistingProgram:function(a){for(var b=0,c=this.context.environments.length;c>b;b++){var d=this.context.environments[b];if(d&&d.equals(a))return b}},programExpression:function(a){var b=this.environment.children[a],c=(b.depths.list,this.useDepths),d=[b.index,"data"];return c&&d.push("depths"),"this.program("+d.join(", ")+")"},useRegister:function(a){this.registers[a]||(this.registers[a]=!0,this.registers.list.push(a))},pushStackLiteral:function(a){return this.push(new c(a))},pushSource:function(a){this.pendingContent&&(this.source.push(this.appendToBuffer(this.quotedString(this.pendingContent))),this.pendingContent=void 0),a&&this.source.push(a)},pushStack:function(a){this.flushInline();var b=this.incrStack();return this.pushSource(b+" = "+a+";"),this.compileStack.push(b),b},replaceStack:function(a){{var b,d,e,f="";this.isInline()}if(!this.isInline())throw new h("replaceStack on non-inline");var g=this.popStack(!0);if(g instanceof c)f=b=g.value,e=!0;else{d=!this.stackSlot;var i=d?this.incrStack():this.topStackName();f="("+this.push(i)+" = "+g+")",b=this.topStack()}var j=a.call(this,b);e||this.popStack(),d&&this.stackSlot--,this.push("("+f+j+")")},incrStack:function(){return this.stackSlot++,this.stackSlot>this.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;if(a.length){this.inlineStack=[];for(var b=0,d=a.length;d>b;b++){var e=a[b];e instanceof c?this.compileStack.push(e):this.pushStack(e)}}},isInline:function(){return this.inlineStack.length},popStack:function(a){var b=this.isInline(),d=(b?this.inlineStack:this.compileStack).pop();if(!a&&d instanceof c)return d.value;if(!b){if(!this.stackSlot)throw new h("Invalid stack pop");this.stackSlot--}return d},topStack:function(){var a=this.isInline()?this.inlineStack:this.compileStack,b=a[a.length-1];return b instanceof c?b.value:b},contextName:function(a){return this.useDepths&&a?"depths["+a+"]":"depth"+a},quotedString:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")+'"'},objectLiteral:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(this.quotedString(c)+":"+a[c]);return"{"+b.join(",")+"}"},setupHelper:function(a,b,c){var d=[],e=this.setupParams(b,a,d,c),f=this.nameLookup("helpers",b,"helper");return{params:d,paramsInit:e,name:f,callParams:[this.contextName(0)].concat(d).join(", ")}},setupOptions:function(a,b,c){var d,e,f,g={},h=[],i=[],j=[];g.name=this.quotedString(a),g.hash=this.popStack(),this.trackIds&&(g.hashIds=this.popStack()),this.stringParams&&(g.hashTypes=this.popStack(),g.hashContexts=this.popStack()),e=this.popStack(),f=this.popStack(),(f||e)&&(f||(f="this.noop"),e||(e="this.noop"),g.fn=f,g.inverse=e);for(var k=b;k--;)d=this.popStack(),c[k]=d,this.trackIds&&(j[k]=this.popStack()),this.stringParams&&(i[k]=this.popStack(),h[k]=this.popStack());return this.trackIds&&(g.ids="["+j.join(",")+"]"),this.stringParams&&(g.types="["+i.join(",")+"]",g.contexts="["+h.join(",")+"]"),this.options.data&&(g.data="data"),g},setupParams:function(a,b,c,d){var e=this.objectLiteral(this.setupOptions(a,b,c));return d?(this.useRegister("options"),c.push("options"),"options="+e):(c.push(e),"")}};for(var i="break else new var case finally return void catch for switch while continue function this with default if throw delete in try do instanceof typeof abstract enum int short boolean export interface static byte extends long super char final native synchronized class float package throws const goto private transient debugger implements protected volatile double import public let yield".split(" "),j=d.RESERVED_WORDS={},k=0,l=i.length;l>k;k++)j[i[k]]=!0;return d.isValidJavaScriptVariableName=function(a){return!d.RESERVED_WORDS[a]&&/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(a)},e=d}(d,c),m=function(a,b,c,d,e){"use strict";var f,g=a,h=b,i=c.parser,j=c.parse,k=d.Compiler,l=d.compile,m=d.precompile,n=e,o=g.create,p=function(){var a=o();return a.compile=function(b,c){return l(b,c,a)},a.precompile=function(b,c){return m(b,c,a)},a.AST=h,a.Compiler=k,a.JavaScriptCompiler=n,a.Parser=i,a.parse=j,a};return g=p(),g.create=p,g["default"]=g,f=g}(f,g,j,k,l);return m});
diff --git a/catalog-be/src/main/resources/swagger/lib/highlight.7.3.pack.js b/catalog-be/src/main/resources/swagger/lib/highlight.7.3.pack.js
new file mode 100644
index 0000000000..a61ad222c2
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/highlight.7.3.pack.js
@@ -0,0 +1,21 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o<p.length;o++){if(e[p[o]]||p[o]=="no-highlight"){return p[o]}}}function c(q){var o=[];(function p(r,s){for(var t=r.firstChild;t;t=t.nextSibling){if(t.nodeType==3){s+=t.nodeValue.length}else{if(t.nodeName=="BR"){s+=1}else{if(t.nodeType==1){o.push({event:"start",offset:s,node:t});s=p(t,s);o.push({event:"stop",offset:s,node:t})}}}}return s})(q,0);return o}function j(x,v,w){var p=0;var y="";var r=[];function t(){if(x.length&&v.length){if(x[0].offset!=v[0].offset){return(x[0].offset<v[0].offset)?x:v}else{return v[0].event=="start"?x:v}}else{return x.length?x:v}}function s(A){function z(B){return" "+B.nodeName+'="'+l(B.value)+'"'}return"<"+A.nodeName+Array.prototype.map.call(A.attributes,z).join("")+">"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("</"+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q<r.length){y+=s(r[q]);q++}}}}return y+l(w.substr(p))}function f(q){function o(s,r){return RegExp(s,"m"+(q.cI?"i":"")+(r?"g":""))}function p(y,w){if(y.compiled){return}y.compiled=true;var s=[];if(y.k){var r={};function z(A,t){t.split(" ").forEach(function(B){var C=B.split("|");r[C[0]]=[A,C[1]?Number(C[1]):1];s.push(C[0])})}y.lR=o(y.l||hljs.IR,true);if(typeof y.k=="string"){z("keyword",y.k)}else{for(var x in y.k){if(!y.k.hasOwnProperty(x)){continue}z(x,y.k[x])}}y.k=r}if(w){if(y.bWK){y.b="\\b("+s.join("|")+")\\s"}y.bR=o(y.b?y.b:"\\B|\\b");if(!y.e&&!y.eW){y.e="\\B|\\b"}if(y.e){y.eR=o(y.e)}y.tE=y.e||"";if(y.eW&&w.tE){y.tE+=(y.e?"|":"")+w.tE}}if(y.i){y.iR=o(y.i)}if(y.r===undefined){y.r=1}if(!y.c){y.c=[]}for(var v=0;v<y.c.length;v++){if(y.c[v]=="self"){y.c[v]=y}p(y.c[v],y)}if(y.starts){p(y.starts,w)}var u=[];for(var v=0;v<y.c.length;v++){u.push(y.c[v].b)}if(y.tE){u.push(y.tE)}if(y.i){u.push(y.i)}y.t=u.length?o(u.join("|"),true):{exec:function(t){return null}}}p(q)}function d(D,E){function o(r,M){for(var L=0;L<M.c.length;L++){var K=M.c[L].bR.exec(r);if(K&&K.index==0){return M.c[L]}}}function s(K,r){if(K.e&&K.eR.test(r)){return K}if(K.eW){return s(K.parent,r)}}function t(r,K){return K.i&&K.iR.test(r)}function y(L,r){var K=F.cI?r[0].toLowerCase():r[0];return L.k.hasOwnProperty(K)&&L.k[K]}function G(){var K=l(w);if(!A.k){return K}var r="";var N=0;A.lR.lastIndex=0;var L=A.lR.exec(K);while(L){r+=K.substr(N,L.index-N);var M=y(A,L);if(M){v+=M[1];r+='<span class="'+M[0]+'">'+L[0]+"</span>"}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return'<span class="'+r.language+'">'+r.value+"</span>"}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'<span class="'+L.cN+'">':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+="</span>"}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"<br>")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[b],starts:{e:"</style>",rE:true,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs);
diff --git a/catalog-be/src/main/resources/swagger/lib/jquery-1.8.0.min.js b/catalog-be/src/main/resources/swagger/lib/jquery-1.8.0.min.js
new file mode 100644
index 0000000000..2ebc792d3a
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/jquery-1.8.0.min.js
@@ -0,0 +1,22 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*! jQuery v@1.8.0 jquery.com | jquery.org/license */
+(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d<e;d++)p.event.add(b,c,h[c][d])}g.data&&(g.data=p.extend({},g.data))}function bE(a,b){var c;if(b.nodeType!==1)return;b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?(b.parentNode&&(b.outerHTML=a.outerHTML),p.support.html5Clone&&a.innerHTML&&!p.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):c==="input"&&bv.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text),b.removeAttribute(p.expando)}function bF(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bG(a){bv.test(a.type)&&(a.defaultChecked=a.checked)}function bX(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=bV.length;while(e--){b=bV[e]+c;if(b in a)return b}return d}function bY(a,b){return a=b||a,p.css(a,"display")==="none"||!p.contains(a.ownerDocument,a)}function bZ(a,b){var c,d,e=[],f=0,g=a.length;for(;f<g;f++){c=a[f];if(!c.style)continue;e[f]=p._data(c,"olddisplay"),b?(!e[f]&&c.style.display==="none"&&(c.style.display=""),c.style.display===""&&bY(c)&&(e[f]=p._data(c,"olddisplay",cb(c.nodeName)))):(d=bH(c,"display"),!e[f]&&d!=="none"&&p._data(c,"olddisplay",d))}for(f=0;f<g;f++){c=a[f];if(!c.style)continue;if(!b||c.style.display==="none"||c.style.display==="")c.style.display=b?e[f]||"":"none"}return a}function b$(a,b,c){var d=bO.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function b_(a,b,c,d){var e=c===(d?"border":"content")?4:b==="width"?1:0,f=0;for(;e<4;e+=2)c==="margin"&&(f+=p.css(a,c+bU[e],!0)),d?(c==="content"&&(f-=parseFloat(bH(a,"padding"+bU[e]))||0),c!=="margin"&&(f-=parseFloat(bH(a,"border"+bU[e]+"Width"))||0)):(f+=parseFloat(bH(a,"padding"+bU[e]))||0,c!=="padding"&&(f+=parseFloat(bH(a,"border"+bU[e]+"Width"))||0));return f}function ca(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=!0,f=p.support.boxSizing&&p.css(a,"boxSizing")==="border-box";if(d<=0){d=bH(a,b);if(d<0||d==null)d=a.style[b];if(bP.test(d))return d;e=f&&(p.support.boxSizingReliable||d===a.style[b]),d=parseFloat(d)||0}return d+b_(a,b,c||(f?"border":"content"),e)+"px"}function cb(a){if(bR[a])return bR[a];var b=p("<"+a+">").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write("<!doctype html><html><body>"),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bR[a]=c,c}function ch(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||cd.test(a)?d(a,e):ch(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ch(a+"["+e+"]",b[e],c,d);else d(a,b)}function cy(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h<i;h++)d=g[h],f=/^\+/.test(d),f&&(d=d.substr(1)||"*"),e=a[d]=a[d]||[],e[f?"unshift":"push"](c)}}function cz(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h,i=a[f],j=0,k=i?i.length:0,l=a===cu;for(;j<k&&(l||!h);j++)h=i[j](c,d,e),typeof h=="string"&&(!l||g[h]?h=b:(c.dataTypes.unshift(h),h=cz(a,c,d,e,h,g)));return(l||!h)&&!g["*"]&&(h=cz(a,c,d,e,"*",g)),h}function cA(a,c){var d,e,f=p.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((f[d]?a:e||(e={}))[d]=c[d]);e&&p.extend(!0,a,e)}function cB(a,c,d){var e,f,g,h,i=a.contents,j=a.dataTypes,k=a.responseFields;for(f in k)f in d&&(c[k[f]]=d[f]);while(j[0]==="*")j.shift(),e===b&&(e=a.mimeType||c.getResponseHeader("content-type"));if(e)for(f in i)if(i[f]&&i[f].test(e)){j.unshift(f);break}if(j[0]in d)g=j[0];else{for(f in d){if(!j[0]||a.converters[f+" "+j[0]]){g=f;break}h||(h=f)}g=g||h}if(g)return g!==j[0]&&j.unshift(g),d[g]}function cC(a,b){var c,d,e,f,g=a.dataTypes.slice(),h=g[0],i={},j=0;a.dataFilter&&(b=a.dataFilter(b,a.dataType));if(g[1])for(c in a.converters)i[c.toLowerCase()]=a.converters[c];for(;e=g[++j];)if(e!=="*"){if(h!=="*"&&h!==e){c=i[h+" "+e]||i["* "+e];if(!c)for(d in i){f=d.split(" ");if(f[1]===e){c=i[h+" "+f[0]]||i["* "+f[0]];if(c){c===!0?c=i[d]:i[d]!==!0&&(e=f[0],g.splice(j--,0,e));break}}}if(c!==!0)if(c&&a["throws"])b=c(b);else try{b=c(b)}catch(k){return{state:"parsererror",error:c?k:"No conversion from "+h+" to "+e}}}h=e}return{state:"success",data:b}}function cK(){try{return new a.XMLHttpRequest}catch(b){}}function cL(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function cT(){return setTimeout(function(){cM=b},0),cM=p.now()}function cU(a,b){p.each(b,function(b,c){var d=(cS[b]||[]).concat(cS["*"]),e=0,f=d.length;for(;e<f;e++)if(d[e].call(a,b,c))return})}function cV(a,b,c){var d,e=0,f=0,g=cR.length,h=p.Deferred().always(function(){delete i.elem}),i=function(){var b=cM||cT(),c=Math.max(0,j.startTime+j.duration-b),d=1-(c/j.duration||0),e=0,f=j.tweens.length;for(;e<f;e++)j.tweens[e].run(d);return h.notifyWith(a,[j,d,c]),d<1&&f?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:p.extend({},b),opts:p.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:cM||cT(),duration:c.duration,tweens:[],createTween:function(b,c,d){var e=p.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(e),e},stop:function(b){var c=0,d=b?j.tweens.length:0;for(;c<d;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;cW(k,j.opts.specialEasing);for(;e<g;e++){d=cR[e].call(j,a,k,j.opts);if(d)return d}return cU(j,k),p.isFunction(j.opts.start)&&j.opts.start.call(a,j),p.fx.timer(p.extend(i,{anim:j,queue:j.opts.queue,elem:a})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}function cW(a,b){var c,d,e,f,g;for(c in a){d=p.camelCase(c),e=b[d],f=a[c],p.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=p.cssHooks[d];if(g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}}function cX(a,b,c){var d,e,f,g,h,i,j,k,l=this,m=a.style,n={},o=[],q=a.nodeType&&bY(a);c.queue||(j=p._queueHooks(a,"fx"),j.unqueued==null&&(j.unqueued=0,k=j.empty.fire,j.empty.fire=function(){j.unqueued||k()}),j.unqueued++,l.always(function(){l.always(function(){j.unqueued--,p.queue(a,"fx").length||j.empty.fire()})})),a.nodeType===1&&("height"in b||"width"in b)&&(c.overflow=[m.overflow,m.overflowX,m.overflowY],p.css(a,"display")==="inline"&&p.css(a,"float")==="none"&&(!p.support.inlineBlockNeedsLayout||cb(a.nodeName)==="inline"?m.display="inline-block":m.zoom=1)),c.overflow&&(m.overflow="hidden",p.support.shrinkWrapBlocks||l.done(function(){m.overflow=c.overflow[0],m.overflowX=c.overflow[1],m.overflowY=c.overflow[2]}));for(d in b){f=b[d];if(cO.exec(f)){delete b[d];if(f===(q?"hide":"show"))continue;o.push(d)}}g=o.length;if(g){h=p._data(a,"fxshow")||p._data(a,"fxshow",{}),q?p(a).show():l.done(function(){p(a).hide()}),l.done(function(){var b;p.removeData(a,"fxshow",!0);for(b in n)p.style(a,b,n[b])});for(d=0;d<g;d++)e=o[d],i=l.createTween(e,q?h[e]:0),n[e]=h[e]||p.style(a,e),e in h||(h[e]=i.start,q&&(i.end=i.start,i.start=e==="width"||e==="height"?1:0))}}function cY(a,b,c,d,e){return new cY.prototype.init(a,b,c,d,e)}function cZ(a,b){var c,d={height:a},e=0;for(;e<4;e+=2-b)c=bU[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function c_(a){return p.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}var c,d,e=a.document,f=a.location,g=a.navigator,h=a.jQuery,i=a.$,j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf,m=Object.prototype.toString,n=Object.prototype.hasOwnProperty,o=String.prototype.trim,p=function(a,b){return new p.fn.init(a,b,c)},q=/[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,r=/\S/,s=/\s+/,t=r.test(" ")?/^[\s\xA0]+|[\s\xA0]+$/g:/^\s+|\s+$/g,u=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.0",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i<j;i++)if((a=arguments[i])!=null)for(c in a){d=h[c],e=a[c];if(h===e)continue;k&&e&&(p.isPlainObject(e)||(f=p.isArray(e)))?(f?(f=!1,g=d&&p.isArray(d)?d:[]):g=d&&p.isPlainObject(d)?d:{},h[c]=p.extend(k,g,e)):e!==b&&(h[c]=e)}return h},p.extend({noConflict:function(b){return a.$===p&&(a.$=i),b&&a.jQuery===p&&(a.jQuery=h),p},isReady:!1,readyWait:1,holdReady:function(a){a?p.readyWait++:p.ready(!0)},ready:function(a){if(a===!0?--p.readyWait:p.isReady)return;if(!e.body)return setTimeout(p.ready,1);p.isReady=!0;if(a!==!0&&--p.readyWait>0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f<g;)if(c.apply(a[f++],d)===!1)break}else if(h){for(e in a)if(c.call(a[e],e,a[e])===!1)break}else for(;f<g;)if(c.call(a[f],f,a[f++])===!1)break;return a},trim:o?function(a){return a==null?"":o.call(a)}:function(a){return a==null?"":a.toString().replace(t,"")},makeArray:function(a,b){var c,d=b||[];return a!=null&&(c=p.type(a),a.length==null||c==="string"||c==="function"||c==="regexp"||p.isWindow(a)?j.call(d,a):p.merge(d,a)),d},inArray:function(a,b,c){var d;if(b){if(l)return l.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=c.length,e=a.length,f=0;if(typeof d=="number")for(;f<d;f++)a[e++]=c[f];else while(c[f]!==b)a[e++]=c[f++];return a.length=e,a},grep:function(a,b,c){var d,e=[],f=0,g=a.length;c=!!c;for(;f<g;f++)d=!!b(a[f],f),c!==d&&e.push(a[f]);return e},map:function(a,c,d){var e,f,g=[],h=0,i=a.length,j=a instanceof p||i!==b&&typeof i=="number"&&(i>0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h<i;h++)e=c(a[h],h,d),e!=null&&(g[g.length]=e);else for(f in a)e=c(a[f],f,d),e!=null&&(g[g.length]=e);return g.concat.apply([],g)},guid:1,proxy:function(a,c){var d,e,f;return typeof c=="string"&&(d=a[c],c=a,a=d),p.isFunction(a)?(e=k.call(arguments,2),f=function(){return a.apply(c,e.concat(k.call(arguments)))},f.guid=a.guid=a.guid||f.guid||p.guid++,f):b},access:function(a,c,d,e,f,g,h){var i,j=d==null,k=0,l=a.length;if(d&&typeof d=="object"){for(k in d)p.access(a,c,k,d[k],1,g,e);f=1}else if(e!==b){i=h===b&&p.isFunction(e),j&&(i?(i=c,c=function(a,b,c){return i.call(p(a),c)}):(c.call(a,e),c=null));if(c)for(;k<l;k++)c(a[k],d,i?e.call(a[k],k,c(a[k],d)):e,h);f=1}return f?a:j?c.call(a):l?c(a[0],d):g},now:function(){return(new Date).getTime()}}),p.ready.promise=function(b){if(!d){d=p.Deferred();if(e.readyState==="complete"||e.readyState!=="loading"&&e.addEventListener)setTimeout(p.ready,1);else if(e.addEventListener)e.addEventListener("DOMContentLoaded",D,!1),a.addEventListener("load",p.ready,!1);else{e.attachEvent("onreadystatechange",D),a.attachEvent("onload",p.ready);var c=!1;try{c=a.frameElement==null&&e.documentElement}catch(f){}c&&c.doScroll&&function g(){if(!p.isReady){try{c.doScroll("left")}catch(a){return setTimeout(g,50)}p.ready()}}()}}return d.promise(b)},p.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){E["[object "+b+"]"]=b.toLowerCase()}),c=p(e);var F={};p.Callbacks=function(a){a=typeof a=="string"?F[a]||G(a):p.extend({},a);var c,d,e,f,g,h,i=[],j=!a.once&&[],k=function(b){c=a.memory&&b,d=!0,h=f||0,f=0,g=i.length,e=!0;for(;i&&h<g;h++)if(i[h].apply(b[0],b[1])===!1&&a.stopOnFalse){c=!1;break}e=!1,i&&(j?j.length&&k(j.shift()):c?i=[]:l.disable())},l={add:function(){if(i){var b=i.length;(function d(b){p.each(b,function(b,c){p.isFunction(c)&&(!a.unique||!l.has(c))?i.push(c):c&&c.length&&d(c)})})(arguments),e?g=i.length:c&&(f=b,k(c))}return this},remove:function(){return i&&p.each(arguments,function(a,b){var c;while((c=p.inArray(b,i,c))>-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b<d;b++)c[b]&&p.isFunction(c[b].promise)?c[b].promise().done(g(b,j,c)).fail(f.reject).progress(g(b,i,h)):--e}return e||f.resolveWith(j,c),f.promise()}}),p.support=function(){var b,c,d,f,g,h,i,j,k,l,m,n=e.createElement("div");n.setAttribute("className","t"),n.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="<div></div>",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e<f;e++)delete d[b[e]];if(!(c?K:p.isEmptyObject)(d))return}}if(!c){delete h[i].data;if(!K(h[i]))return}g?p.cleanData([a],!0):p.support.deleteExpando||h!=h.window?delete h[i]:h[i]=null},_data:function(a,b,c){return p.data(a,b,c,!0)},acceptData:function(a){var b=a.nodeName&&p.noData[a.nodeName.toLowerCase()];return!b||b!==!0&&a.getAttribute("classid")===b}}),p.fn.extend({data:function(a,c){var d,e,f,g,h,i=this[0],j=0,k=null;if(a===b){if(this.length){k=p.data(i);if(i.nodeType===1&&!p._data(i,"parsedAttrs")){f=i.attributes;for(h=f.length;j<h;j++)g=f[j].name,g.indexOf("data-")===0&&(g=p.camelCase(g.substring(5)),J(i,g,k[g]));p._data(i,"parsedAttrs",!0)}}return k}return typeof a=="object"?this.each(function(){p.data(this,a)}):(d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!",p.access(this,function(c){if(c===b)return k=this.triggerHandler("getData"+e,[d[0]]),k===b&&i&&(k=p.data(i,a),k=J(i,a,k)),k===b&&d[1]?this.data(d[0]):k;d[1]=c,this.each(function(){var b=p(this);b.triggerHandler("setData"+e,d),p.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.shift(),e=p._queueHooks(a,b),f=function(){p.dequeue(a,b)};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),delete e.stop,d.call(a,f,e)),!c.length&&e&&e.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length<d?p.queue(this[0],a):c===b?this:this.each(function(){var b=p.queue(this,a,c);p._queueHooks(this,a),a==="fx"&&b[0]!=="inprogress"&&p.dequeue(this,a)})},dequeue:function(a){return this.each(function(){p.dequeue(this,a)})},delay:function(a,b){return a=p.fx?p.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){var d,e=1,f=p.Deferred(),g=this,h=this.length,i=function(){--e||f.resolveWith(g,[g])};typeof a!="string"&&(c=a,a=b),a=a||"fx";while(h--)(d=p._data(g[h],a+"queueHooks"))&&d.empty&&(e++,d.empty.add(i));return i(),f.promise(c)}});var L,M,N,O=/[\t\r\n]/g,P=/\r/g,Q=/^(?:button|input)$/i,R=/^(?:button|input|object|select|textarea)$/i,S=/^a(?:rea|)$/i,T=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,U=p.support.getSetAttribute;p.fn.extend({attr:function(a,b){return p.access(this,p.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{f=" "+e.className+" ";for(g=0,h=b.length;g<h;g++)~f.indexOf(" "+b[g]+" ")||(f+=b[g]+" ");e.className=p.trim(f)}}}return this},removeClass:function(a){var c,d,e,f,g,h,i;if(p.isFunction(a))return this.each(function(b){p(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(s);for(h=0,i=this.length;h<i;h++){e=this[h];if(e.nodeType===1&&e.className){d=(" "+e.className+" ").replace(O," ");for(f=0,g=c.length;f<g;f++)while(d.indexOf(" "+c[f]+" ")>-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(O," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c<d;c++){e=h[c];if(e.selected&&(p.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!p.nodeName(e.parentNode,"optgroup"))){b=p(e).val();if(i)return b;g.push(b)}}return i&&!g.length&&h.length?p(h[f]).val():g},set:function(a,b){var c=p.makeArray(b);return p(a).find("option").each(function(){this.selected=p.inArray(p(this).val(),c)>=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g<d.length;g++)e=d[g],e&&(c=p.propFix[e]||e,f=T.test(e),f||p.attr(a,e,""),a.removeAttribute(U?e:c),f&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(Q.test(a.nodeName)&&a.parentNode)p.error("type property can't be changed");else if(!p.support.radioValue&&b==="radio"&&p.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}},value:{get:function(a,b){return L&&p.nodeName(a,"button")?L.get(a,b):b in a?a.value:null},set:function(a,b,c){if(L&&p.nodeName(a,"button"))return L.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,f,g,h=a.nodeType;if(!a||h===3||h===8||h===2)return;return g=h!==1||!p.isXMLDoc(a),g&&(c=p.propFix[c]||c,f=p.propHooks[c]),d!==b?f&&"set"in f&&(e=f.set(a,d,c))!==b?e:a[c]=d:f&&"get"in f&&(e=f.get(a,c))!==null?e:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):R.test(a.nodeName)||S.test(a.nodeName)&&a.href?0:b}}}}),M={get:function(a,c){var d,e=p.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;return b===!1?p.removeAttr(a,c):(d=p.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase())),c}},U||(N={name:!0,id:!0,coords:!0},L=p.valHooks.button={get:function(a,c){var d;return d=a.getAttributeNode(c),d&&(N[c]?d.value!=="":d.specified)?d.value:b},set:function(a,b,c){var d=a.getAttributeNode(c);return d||(d=e.createAttribute(c),a.setAttributeNode(d)),d.value=b+""}},p.each(["width","height"],function(a,b){p.attrHooks[b]=p.extend(p.attrHooks[b],{set:function(a,c){if(c==="")return a.setAttribute(b,"auto"),c}})}),p.attrHooks.contenteditable={get:L.get,set:function(a,b,c){b===""&&(b="false"),L.set(a,b,c)}}),p.support.hrefNormalized||p.each(["href","src","width","height"],function(a,c){p.attrHooks[c]=p.extend(p.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),p.support.style||(p.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),p.support.optSelected||(p.propHooks.selected=p.extend(p.propHooks.selected,{get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}})),p.support.enctype||(p.propFix.enctype="encoding"),p.support.checkOn||p.each(["radio","checkbox"],function(){p.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]=p.extend(p.valHooks[this],{set:function(a,b){if(p.isArray(b))return a.checked=p.inArray(p(a).val(),b)>=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j<c.length;j++){k=W.exec(c[j])||[],l=k[1],m=(k[2]||"").split(".").sort(),r=p.event.special[l]||{},l=(f?r.delegateType:r.bindType)||l,r=p.event.special[l]||{},n=p.extend({type:l,origType:k[1],data:e,handler:d,guid:d.guid,selector:f,namespace:m.join(".")},o),q=i[l];if(!q){q=i[l]=[],q.delegateCount=0;if(!r.setup||r.setup.call(a,e,m,h)===!1)a.addEventListener?a.addEventListener(l,h,!1):a.attachEvent&&a.attachEvent("on"+l,h)}r.add&&(r.add.call(a,n),n.handler.guid||(n.handler.guid=d.guid)),f?q.splice(q.delegateCount++,0,n):q.push(n),p.event.global[l]=!0}a=null},global:{},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,q,r=p.hasData(a)&&p._data(a);if(!r||!(m=r.events))return;b=p.trim(_(b||"")).split(" ");for(f=0;f<b.length;f++){g=W.exec(b[f])||[],h=i=g[1],j=g[2];if(!h){for(h in m)p.event.remove(a,h+b[f],c,d,!0);continue}n=p.event.special[h]||{},h=(d?n.delegateType:n.bindType)||h,o=m[h]||[],k=o.length,j=j?new RegExp("(^|\\.)"+j.split(".").sort().join("\\.(?:.*\\.|)")+"(\\.|$)"):null;for(l=0;l<o.length;l++)q=o[l],(e||i===q.origType)&&(!c||c.guid===q.guid)&&(!j||j.test(q.namespace))&&(!d||d===q.selector||d==="**"&&q.selector)&&(o.splice(l--,1),q.selector&&o.delegateCount--,n.remove&&n.remove.call(a,q));o.length===0&&k!==o.length&&((!n.teardown||n.teardown.call(a,j,r.handle)===!1)&&p.removeEvent(a,h,r.handle),delete m[h])}p.isEmptyObject(m)&&(delete r.handle,p.removeData(a,"events",!0))},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,f,g){if(!f||f.nodeType!==3&&f.nodeType!==8){var h,i,j,k,l,m,n,o,q,r,s=c.type||c,t=[];if($.test(s+p.event.triggered))return;s.indexOf("!")>=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j<q.length&&!c.isPropagationStopped();j++)k=q[j][0],c.type=q[j][1],o=(p._data(k,"events")||{})[c.type]&&p._data(k,"handle"),o&&o.apply(k,d),o=m&&k[m],o&&p.acceptData(k)&&o.apply(k,d)===!1&&c.preventDefault();return c.type=s,!g&&!c.isDefaultPrevented()&&(!n._default||n._default.apply(f.ownerDocument,d)===!1)&&(s!=="click"||!p.nodeName(f,"a"))&&p.acceptData(f)&&m&&f[s]&&(s!=="focus"&&s!=="blur"||c.target.offsetWidth!==0)&&!p.isWindow(f)&&(l=f[m],l&&(f[m]=null),p.event.triggered=s,f[s](),p.event.triggered=b,l&&(f[m]=l)),c.result}return},dispatch:function(c){c=p.event.fix(c||a.event);var d,e,f,g,h,i,j,k,l,m,n,o=(p._data(this,"events")||{})[c.type]||[],q=o.delegateCount,r=[].slice.call(arguments),s=!c.exclusive&&!c.namespace,t=p.event.special[c.type]||{},u=[];r[0]=c,c.delegateTarget=this;if(t.preDispatch&&t.preDispatch.call(this,c)===!1)return;if(q&&(!c.button||c.type!=="click")){g=p(this),g.context=this;for(f=c.target;f!=this;f=f.parentNode||this)if(f.disabled!==!0||c.type!=="click"){i={},k=[],g[0]=f;for(d=0;d<q;d++)l=o[d],m=l.selector,i[m]===b&&(i[m]=g.is(m)),i[m]&&k.push(l);k.length&&u.push({elem:f,matches:k})}}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d<u.length&&!c.isPropagationStopped();d++){j=u[d],c.currentTarget=j.elem;for(e=0;e<j.matches.length&&!c.isImmediatePropagationStopped();e++){l=j.matches[e];if(s||!c.namespace&&!l.namespace||c.namespace_re&&c.namespace_re.test(l.namespace))c.data=l.data,c.handleObj=l,h=((p.event.special[l.origType]||{}).handle||l.handler).apply(j.elem,r),h!==b&&(c.result=h,h===!1&&(c.preventDefault(),c.stopPropagation()))}}return t.postDispatch&&t.postDispatch.call(this,c),c.result},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,c){var d,f,g,h=c.button,i=c.fromElement;return a.pageX==null&&c.clientX!=null&&(d=a.target.ownerDocument||e,f=d.documentElement,g=d.body,a.pageX=c.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=c.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?c.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0),a}},fix:function(a){if(a[p.expando])return a;var b,c,d=a,f=p.event.fixHooks[a.type]||{},g=f.props?this.props.concat(f.props):this.props;a=p.Event(d);for(b=g.length;b;)c=g[--b],a[c]=d[c];return a.target||(a.target=d.srcElement||e),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,f.filter?f.filter(a,d):a},special:{ready:{setup:p.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){p.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=p.extend(new p.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?p.event.trigger(e,null,b):p.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},p.event.handle=p.event.dispatch,p.removeEvent=e.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]=="undefined"&&(a[d]=null),a.detachEvent(d,c))},p.Event=function(a,b){if(this instanceof p.Event)a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?bb:ba):this.type=a,b&&p.extend(this,b),this.timeStamp=a&&a.timeStamp||p.now(),this[p.expando]=!0;else return new p.Event(a,b)},p.Event.prototype={preventDefault:function(){this.isDefaultPrevented=bb;var a=this.originalEvent;if(!a)return;a.preventDefault?a.preventDefault():a.returnValue=!1},stopPropagation:function(){this.isPropagationStopped=bb;var a=this.originalEvent;if(!a)return;a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=bb,this.stopPropagation()},isDefaultPrevented:ba,isPropagationStopped:ba,isImmediatePropagationStopped:ba},p.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){p.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj,g=f.selector;if(!e||e!==d&&!p.contains(d,e))a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b;return c}}}),p.support.submitBubbles||(p.event.special.submit={setup:function(){if(p.nodeName(this,"form"))return!1;p.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=p.nodeName(c,"input")||p.nodeName(c,"button")?c.form:b;d&&!p._data(d,"_submit_attached")&&(p.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),p._data(d,"_submit_attached",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&p.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(p.nodeName(this,"form"))return!1;p.event.remove(this,"._submit")}}),p.support.changeBubbles||(p.event.special.change={setup:function(){if(V.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")p.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),p.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),p.event.simulate("change",this,a,!0)});return!1}p.event.add(this,"beforeactivate._change",function(a){var b=a.target;V.test(b.nodeName)&&!p._data(b,"_change_attached")&&(p.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&p.event.simulate("change",this.parentNode,a,!0)}),p._data(b,"_change_attached",!0))})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){return p.event.remove(this,"._change"),V.test(this.nodeName)}}),p.support.focusinBubbles||p.each({focus:"focusin",blur:"focusout"},function(a,b){var c=0,d=function(a){p.event.simulate(b,a.target,p.event.fix(a),!0)};p.event.special[b]={setup:function(){c++===0&&e.addEventListener(a,d,!0)},teardown:function(){--c===0&&e.removeEventListener(a,d,!0)}}}),p.fn.extend({on:function(a,c,d,e,f){var g,h;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(h in a)this.on(h,c,d,a[h],f);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=ba;else if(!e)return this;return f===1&&(g=e,e=function(a){return p().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=p.guid++)),this.each(function(){p.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){var e,f;if(a&&a.preventDefault&&a.handleObj)return e=a.handleObj,p(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler),this;if(typeof a=="object"){for(f in a)this.off(f,c,a[f]);return this}if(c===!1||typeof c=="function")d=c,c=b;return d===!1&&(d=ba),this.each(function(){p.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){return p(this.context).on(a,this.selector,b,c),this},die:function(a,b){return p(this.context).off(a,this.selector||"**",b),this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a||"**",c)},trigger:function(a,b){return this.each(function(){p.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return p.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||p.guid++,d=0,e=function(c){var e=(p._data(this,"lastToggle"+a.guid)||0)%d;return p._data(this,"lastToggle"+a.guid,e+1),c.preventDefault(),b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),p.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){p.fn[b]=function(a,c){return c==null&&(c=a,a=null),arguments.length>0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bd(a,b,c,d){var e=0,f=b.length;for(;e<f;e++)Z(a,b[e],c,d)}function be(a,b,c,d,e,f){var g,h=$.setFilters[b.toLowerCase()];return h||Z.error(b),(a||!(g=e))&&bd(a||"*",d,g=[],e),g.length>0?h(g,c,f):[]}function bf(a,c,d,e,f){var g,h,i,j,k,l,m,n,p=0,q=f.length,s=L.POS,t=new RegExp("^"+s.source+"(?!"+r+")","i"),u=function(){var a=1,c=arguments.length-2;for(;a<c;a++)arguments[a]===b&&(g[a]=b)};for(;p<q;p++){s.exec(""),a=f[p],j=[],i=0,k=e;while(g=s.exec(a)){n=s.lastIndex=g.index+g[0].length;if(n>i){m=a.slice(i,g.index),i=n,l=[c],B.test(m)&&(k&&(l=k),k=e);if(h=H.test(m))m=m.slice(0,-5).replace(B,"$&*");g.length>1&&g[0].replace(t,u),k=be(m,g[1],g[2],l,k,h)}}k?(j=j.concat(k),(m=a.slice(i))&&m!==")"?B.test(m)?bd(m,j,d,e):Z(m,c,d,e?e.concat(k):k):o.apply(d,j)):Z(a,c,d,e)}return q===1?d:Z.uniqueSort(d)}function bg(a,b,c){var d,e,f,g=[],i=0,j=D.exec(a),k=!j.pop()&&!j.pop(),l=k&&a.match(C)||[""],m=$.preFilter,n=$.filter,o=!c&&b!==h;for(;(e=l[i])!=null&&k;i++){g.push(d=[]),o&&(e=" "+e);while(e){k=!1;if(j=B.exec(e))e=e.slice(j[0].length),k=d.push({part:j.pop().replace(A," "),captures:j});for(f in n)(j=L[f].exec(e))&&(!m[f]||(j=m[f](j,b,c)))&&(e=e.slice(j.shift().length),k=d.push({part:f,captures:j}));if(!k)break}}return k||Z.error(a),g}function bh(a,b,e){var f=b.dir,g=m++;return a||(a=function(a){return a===e}),b.first?function(b,c){while(b=b[f])if(b.nodeType===1)return a(b,c)&&b}:function(b,e){var h,i=g+"."+d,j=i+"."+c;while(b=b[f])if(b.nodeType===1){if((h=b[q])===j)return b.sizset;if(typeof h=="string"&&h.indexOf(i)===0){if(b.sizset)return b}else{b[q]=j;if(a(b,e))return b.sizset=!0,b;b.sizset=!1}}}}function bi(a,b){return a?function(c,d){var e=b(c,d);return e&&a(e===!0?c:e,d)}:b}function bj(a,b,c){var d,e,f=0;for(;d=a[f];f++)$.relative[d.part]?e=bh(e,$.relative[d.part],b):(d.captures.push(b,c),e=bi(e,$.filter[d.part].apply(null,d.captures)));return e}function bk(a){return function(b,c){var d,e=0;for(;d=a[e];e++)if(d(b,c))return!0;return!1}}var c,d,e,f,g,h=a.document,i=h.documentElement,j="undefined",k=!1,l=!0,m=0,n=[].slice,o=[].push,q=("sizcache"+Math.random()).replace(".",""),r="[\\x20\\t\\r\\n\\f]",s="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",t=s.replace("w","w#"),u="([*^$|!~]?=)",v="\\["+r+"*("+s+")"+r+"*(?:"+u+r+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+t+")|)|)"+r+"*\\]",w=":("+s+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|((?:[^,]|\\\\,|(?:,(?=[^\\[]*\\]))|(?:,(?=[^\\(]*\\))))*))\\)|)",x=":(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\)|)(?=[^-]|$)",y=r+"*([\\x20\\t\\r\\n\\f>+~])"+r+"*",z="(?=[^\\x20\\t\\r\\n\\f])(?:\\\\.|"+v+"|"+w.replace(2,7)+"|[^\\\\(),])+",A=new RegExp("^"+r+"+|((?:^|[^\\\\])(?:\\\\.)*)"+r+"+$","g"),B=new RegExp("^"+y),C=new RegExp(z+"?(?="+r+"*,|$)","g"),D=new RegExp("^(?:(?!,)(?:(?:^|,)"+r+"*"+z+")*?|"+r+"*(.*?))(\\)|$)"),E=new RegExp(z.slice(19,-6)+"\\x20\\t\\r\\n\\f>+~])+|"+y,"g"),F=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,G=/[\x20\t\r\n\f]*[+~]/,H=/:not\($/,I=/h\d/i,J=/input|select|textarea|button/i,K=/\\(?!\\)/g,L={ID:new RegExp("^#("+s+")"),CLASS:new RegExp("^\\.("+s+")"),NAME:new RegExp("^\\[name=['\"]?("+s+")['\"]?\\]"),TAG:new RegExp("^("+s.replace("[-","[-\\*")+")"),ATTR:new RegExp("^"+v),PSEUDO:new RegExp("^"+w),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+r+"*(even|odd|(([+-]|)(\\d*)n|)"+r+"*(?:([+-]|)"+r+"*(\\d+)|))"+r+"*\\)|)","i"),POS:new RegExp(x,"ig"),needsContext:new RegExp("^"+r+"*[>+~]|"+x,"i")},M={},N=[],O={},P=[],Q=function(a){return a.sizzleFilter=!0,a},R=function(a){return function(b){return b.nodeName.toLowerCase()==="input"&&b.type===a}},S=function(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}},T=function(a){var b=!1,c=h.createElement("div");try{b=a(c)}catch(d){}return c=null,b},U=T(function(a){a.innerHTML="<select></select>";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),V=T(function(a){a.id=q+0,a.innerHTML="<a name='"+q+"'></a><div name='"+q+"'></div>",i.insertBefore(a,i.firstChild);var b=h.getElementsByName&&h.getElementsByName(q).length===2+h.getElementsByName(q+0).length;return g=!h.getElementById(q),i.removeChild(a),b}),W=T(function(a){return a.appendChild(h.createComment("")),a.getElementsByTagName("*").length===0}),X=T(function(a){return a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!==j&&a.firstChild.getAttribute("href")==="#"}),Y=T(function(a){return a.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",!a.getElementsByClassName||a.getElementsByClassName("e").length===0?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length!==1)}),Z=function(a,b,c,d){c=c||[],b=b||h;var e,f,g,i,j=b.nodeType;if(j!==1&&j!==9)return[];if(!a||typeof a!="string")return c;g=ba(b);if(!g&&!d)if(e=F.exec(a))if(i=e[1]){if(j===9){f=b.getElementById(i);if(!f||!f.parentNode)return c;if(f.id===i)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(i))&&bb(b,f)&&f.id===i)return c.push(f),c}else{if(e[2])return o.apply(c,n.call(b.getElementsByTagName(a),0)),c;if((i=e[3])&&Y&&b.getElementsByClassName)return o.apply(c,n.call(b.getElementsByClassName(i),0)),c}return bm(a,b,c,d,g)},$=Z.selectors={cacheLength:50,match:L,order:["ID","TAG"],attrHandle:{},createPseudo:Q,find:{ID:g?function(a,b,c){if(typeof b.getElementById!==j&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==j&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==j&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:W?function(a,b){if(typeof b.getElementsByTagName!==j)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(K,""),a[3]=(a[4]||a[5]||"").replace(K,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||Z.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&Z.error(a[0]),a},PSEUDO:function(a){var b,c=a[4];return L.CHILD.test(a[0])?null:(c&&(b=D.exec(c))&&b.pop()&&(a[0]=a[0].slice(0,b[0].length-c.length-1),c=b[0].slice(0,-1)),a.splice(2,3,c||a[3]),a)}},filter:{ID:g?function(a){return a=a.replace(K,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(K,""),function(b){var c=typeof b.getAttributeNode!==j&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(K,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=M[a];return b||(b=M[a]=new RegExp("(^|"+r+")"+a+"("+r+"|$)"),N.push(a),N.length>$.cacheLength&&delete M[N.shift()]),function(a){return b.test(a.className||typeof a.getAttribute!==j&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=Z.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return Z.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=m++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[q]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[q]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e=$.pseudos[a]||$.pseudos[a.toLowerCase()];return e||Z.error("unsupported pseudo: "+a),e.sizzleFilter?e(b,c,d):e}},pseudos:{not:Q(function(a,b,c){var d=bl(a.replace(A,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!$.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:Q(function(a){return function(b){return(b.textContent||b.innerText||bc(b)).indexOf(a)>-1}}),has:Q(function(a){return function(b){return Z(a,b).length>0}}),header:function(a){return I.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:R("radio"),checkbox:R("checkbox"),file:R("file"),password:R("password"),image:R("image"),submit:S("submit"),reset:S("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return J.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},odd:function(a,b,c){var d=[],e=c?0:1,f=a.length;for(;e<f;e=e+2)d.push(a[e]);return d},lt:function(a,b,c){return c?a.slice(+b):a.slice(0,+b)},gt:function(a,b,c){return c?a.slice(0,+b+1):a.slice(+b+1)},eq:function(a,b,c){var d=a.splice(+b,1);return c?a:d}}};$.setFilters.nth=$.setFilters.eq,$.filters=$.pseudos,X||($.attrHandle={href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}}),V&&($.order.push("NAME"),$.find.NAME=function(a,b){if(typeof b.getElementsByName!==j)return b.getElementsByName(a)}),Y&&($.order.splice(1,0,"CLASS"),$.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!==j&&!c)return b.getElementsByClassName(a)});try{n.call(i.childNodes,0)[0].nodeType}catch(_){n=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}var ba=Z.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},bb=Z.contains=i.compareDocumentPosition?function(a,b){return!!(a.compareDocumentPosition(b)&16)}:i.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc=Z.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=bc(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=bc(b);return c};Z.attr=function(a,b){var c,d=ba(a);return d||(b=b.toLowerCase()),$.attrHandle[b]?$.attrHandle[b](a):U||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},Z.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},[0,0].sort(function(){return l=0}),i.compareDocumentPosition?e=function(a,b){return a===b?(k=!0,0):(!a.compareDocumentPosition||!b.compareDocumentPosition?a.compareDocumentPosition:a.compareDocumentPosition(b)&4)?-1:1}:(e=function(a,b){if(a===b)return k=!0,0;if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],g=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return f(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)g.unshift(j),j=j.parentNode;c=e.length,d=g.length;for(var l=0;l<c&&l<d;l++)if(e[l]!==g[l])return f(e[l],g[l]);return l===c?f(a,g[l],-1):f(e[l],b,1)},f=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),Z.uniqueSort=function(a){var b,c=1;if(e){k=l,a.sort(e);if(k)for(;b=a[c];c++)b===a[c-1]&&a.splice(c--,1)}return a};var bl=Z.compile=function(a,b,c){var d,e,f,g=O[a];if(g&&g.context===b)return g;e=bg(a,b,c);for(f=0;d=e[f];f++)e[f]=bj(d,b,c);return g=O[a]=bk(e),g.context=b,g.runs=g.dirruns=0,P.push(a),P.length>$.cacheLength&&delete O[P.shift()],g};Z.matches=function(a,b){return Z(a,null,null,b)},Z.matchesSelector=function(a,b){return Z(b,null,null,[a]).length>0};var bm=function(a,b,e,f,g){a=a.replace(A,"$1");var h,i,j,k,l,m,p,q,r,s=a.match(C),t=a.match(E),u=b.nodeType;if(L.POS.test(a))return bf(a,b,e,f,s);if(f)h=n.call(f,0);else if(s&&s.length===1){if(t.length>1&&u===9&&!g&&(s=L.ID.exec(t[0]))){b=$.find.ID(s[1],b,g)[0];if(!b)return e;a=a.slice(t.shift().length)}q=(s=G.exec(t[0]))&&!s.index&&b.parentNode||b,r=t.pop(),m=r.split(":not")[0];for(j=0,k=$.order.length;j<k;j++){p=$.order[j];if(s=L[p].exec(m)){h=$.find[p]((s[1]||"").replace(K,""),q,g);if(h==null)continue;m===r&&(a=a.slice(0,a.length-r.length)+m.replace(L[p],""),a||o.apply(e,n.call(h,0)));break}}}if(a){i=bl(a,b,g),d=i.dirruns++,h==null&&(h=$.find.TAG("*",G.test(a)&&b.parentNode||b));for(j=0;l=h[j];j++)c=i.runs++,i(l,b)&&e.push(l)}return e};h.querySelectorAll&&function(){var a,b=bm,c=/'|\\/g,d=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,e=[],f=[":active"],g=i.matchesSelector||i.mozMatchesSelector||i.webkitMatchesSelector||i.oMatchesSelector||i.msMatchesSelector;T(function(a){a.innerHTML="<select><option selected></option></select>",a.querySelectorAll("[selected]").length||e.push("\\["+r+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="<p test=''></p>",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+r+"*(?:\"\"|'')"),a.innerHTML="<input type='hidden'>",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bm=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return o.apply(f,n.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j=d.getAttribute("id"),k=j||q,l=G.test(a)&&d.parentNode||d;j?k=k.replace(c,"\\$&"):d.setAttribute("id",k);try{return o.apply(f,n.call(l.querySelectorAll(a.replace(C,"[id='"+k+"'] $&")),0)),f}catch(i){}finally{j||d.removeAttribute("id")}}return b(a,d,f,g,h)},g&&(T(function(b){a=g.call(b,"div");try{g.call(b,"[test!='']:sizzle"),f.push($.match.PSEUDO)}catch(c){}}),f=new RegExp(f.join("|")),Z.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!ba(b)&&!f.test(c)&&(!e||!e.test(c)))try{var h=g.call(b,c);if(h||a||b.document&&b.document.nodeType!==11)return h}catch(i){}return Z(c,null,null,[b]).length>0})}(),Z.attr=p.attr,p.find=Z,p.expr=Z.selectors,p.expr[":"]=p.expr.pseudos,p.unique=Z.uniqueSort,p.text=Z.getText,p.isXMLDoc=Z.isXML,p.contains=Z.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b<c;b++)if(p.contains(h[b],this))return!0});g=this.pushStack("","find",a);for(b=0,c=this.length;b<c;b++){d=g.length,p.find(a,this[b],g);if(b>0)for(e=d;e<g.length;e++)for(f=0;f<d;f++)if(g[f]===g[e]){g.splice(e--,1);break}}return g},has:function(a){var b,c=p(a,this),d=c.length;return this.filter(function(){for(b=0;b<d;b++)if(p.contains(this,c[b]))return!0})},not:function(a){return this.pushStack(bj(this,a,!1),"not",a)},filter:function(a){return this.pushStack(bj(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?bf.test(a)?p(a,this.context).index(this[0])>=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d<e;d++){c=this[d];while(c&&c.ownerDocument&&c!==b&&c.nodeType!==11){if(g?g.index(c)>-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/<tbody/i,br=/<|&#?\w+;/,bs=/<(?:script|style|link)/i,bt=/<(?:script|object|embed|option|style)/i,bu=new RegExp("<(?:"+bl+")[\\s/>]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,bz={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X<div>","</div>"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(f){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){return bh(this[0])?this.length?this.pushStack(p(p.isFunction(a)?a():a),"replaceWith",a):this:p.isFunction(a)?this.each(function(b){var c=p(this),d=c.html();c.replaceWith(a.call(this,b,d))}):(typeof a!="string"&&(a=p(a).detach()),this.each(function(){var b=this.nextSibling,c=this.parentNode;p(this).remove(),b?p(b).before(a):p(c).append(a)}))},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){a=[].concat.apply([],a);var e,f,g,h,i=0,j=a[0],k=[],l=this.length;if(!p.support.checkClone&&l>1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i<l;i++)d.call(c&&p.nodeName(this[i],"table")?bC(this[i],"tbody"):this[i],i===h?g:p.clone(g,!0,!0))}g=f=null,k.length&&p.each(k,function(a,b){b.src?p.ajax?p.ajax({url:b.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):p.error("no ajax"):p.globalEval((b.text||b.textContent||b.innerHTML||"").replace(by,"")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),p.buildFragment=function(a,c,d){var f,g,h,i=a[0];return c=c||e,c=(c[0]||c).ownerDocument||c[0]||c,typeof c.createDocumentFragment=="undefined"&&(c=e),a.length===1&&typeof i=="string"&&i.length<512&&c===e&&i.charAt(0)==="<"&&!bt.test(i)&&(p.support.checkClone||!bw.test(i))&&(p.support.html5Clone||!bu.test(i))&&(g=!0,f=p.fragments[i],h=f!==b),f||(f=c.createDocumentFragment(),p.clean(a,c,f,d),g&&(p.fragments[i]=h&&f)),{fragment:f,cacheable:g}},p.fragments={},p.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){p.fn[a]=function(c){var d,e=0,f=[],g=p(c),h=g.length,i=this.length===1&&this[0].parentNode;if((i==null||i&&i.nodeType===11&&i.childNodes.length===1)&&h===1)return g[b](this[0]),this;for(;e<h;e++)d=(e>0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=0,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(g=b===e&&bA;(h=a[s])!=null;s++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{g=g||bk(b),l=l||g.appendChild(b.createElement("div")),h=h.replace(bo,"<$1></$2>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]==="<table>"&&!m?l.childNodes:[];for(f=n.length-1;f>=0;--f)p.nodeName(n[f],"tbody")&&!n[f].childNodes.length&&n[f].parentNode.removeChild(n[f])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l=g.lastChild}h.nodeType?t.push(h):t=p.merge(t,h)}l&&(g.removeChild(l),h=l=g=null);if(!p.support.appendChecked)for(s=0;(h=t[s])!=null;s++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(s=0;(h=t[s])!=null;s++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[s+1,0].concat(r)),s+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^margin/,bO=new RegExp("^("+q+")(.*)$","i"),bP=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bQ=new RegExp("^([-+])=("+q+")","i"),bR={},bS={position:"absolute",visibility:"hidden",display:"block"},bT={letterSpacing:0,fontWeight:400,lineHeight:1},bU=["Top","Right","Bottom","Left"],bV=["Webkit","O","Moz","ms"],bW=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return bZ(this,!0)},hide:function(){return bZ(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bW.apply(this,arguments):this.each(function(){(c?a:bY(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bX(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bQ.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bX(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bT&&(f=bT[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(a,b){var c,d,e,f,g=getComputedStyle(a,null),h=a.style;return g&&(c=g[b],c===""&&!p.contains(a.ownerDocument.documentElement,a)&&(c=p.style(a,b)),bP.test(c)&&bN.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=c,c=g.width,h.width=d,h.minWidth=e,h.maxWidth=f)),c}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bP.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0||bH(a,"display")!=="none"?ca(a,b,d):p.swap(a,bS,function(){return ca(a,b,d)})},set:function(a,c,d){return b$(a,c,d?b_(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bP.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bU[d]+b]=e[d]||e[d-2]||e[0];return f}},bN.test(a)||(p.cssHooks[a+b].set=b$)});var cc=/%20/g,cd=/\[\]$/,ce=/\r?\n/g,cf=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,cg=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||cg.test(this.nodeName)||cf.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(ce,"\r\n")}}):{name:b.name,value:c.replace(ce,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ch(d,a[d],c,f);return e.join("&").replace(cc,"+")};var ci,cj,ck=/#.*$/,cl=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cm=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,cn=/^(?:GET|HEAD)$/,co=/^\/\//,cp=/\?/,cq=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,cr=/([?&])_=[^&]*/,cs=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,ct=p.fn.load,cu={},cv={},cw=["*/"]+["*"];try{ci=f.href}catch(cx){ci=e.createElement("a"),ci.href="",ci=ci.href}cj=cs.exec(ci.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&ct)return ct.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("<div>").append(a.replace(cq,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cA(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cA(a,b),a},ajaxSettings:{url:ci,isLocal:cm.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cw},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cy(cu),ajaxTransport:cy(cv),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cB(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cC(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cl.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(ck,"").replace(co,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=cs.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==cj[1]&&i[2]==cj[2]&&(i[3]||(i[1]==="http:"?80:443))==(cj[3]||(cj[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cz(cu,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!cn.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cp.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cr,"$1_="+z);l.url=A+(A===l.url?(cp.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cw+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cz(cv,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cD=[],cE=/\?/,cF=/(=)\?(?=&|$)|\?\?/,cG=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cD.pop()||p.expando+"_"+cG++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cF.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cF.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cF,"$1"+f):m?c.data=i.replace(cF,"$1"+f):k&&(c.url+=(cE.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cD.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cH,cI=a.ActiveXObject?function(){for(var a in cH)cH[a](0,1)}:!1,cJ=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cK()||cL()}:cK,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cI&&delete cH[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cJ,cI&&(cH||(cH={},p(a).unload(cI)),cH[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cM,cN,cO=/^(?:toggle|show|hide)$/,cP=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cQ=/queueHooks$/,cR=[cX],cS={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cP.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cV,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d<e;d++)c=a[d],cS[c]=cS[c]||[],cS[c].unshift(b)},prefilter:function(a,b){b?cR.unshift(a):cR.push(a)}}),p.Tween=cY,cY.prototype={constructor:cY,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(p.cssNumber[c]?"":"px")},cur:function(){var a=cY.propHooks[this.prop];return a&&a.get?a.get(this):cY.propHooks._default.get(this)},run:function(a){var b,c=cY.propHooks[this.prop];return this.pos=b=p.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration),this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):cY.propHooks._default.set(this),this}},cY.prototype.init.prototype=cY.prototype,cY.propHooks={_default:{get:function(a){var b;return a.elem[a.prop]==null||!!a.elem.style&&a.elem.style[a.prop]!=null?(b=p.css(a.elem,a.prop,!1,""),!b||b==="auto"?0:b):a.elem[a.prop]},set:function(a){p.fx.step[a.prop]?p.fx.step[a.prop](a):a.elem.style&&(a.elem.style[p.cssProps[a.prop]]!=null||p.cssHooks[a.prop])?p.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},cY.propHooks.scrollTop=cY.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},p.each(["toggle","show","hide"],function(a,b){var c=p.fn[b];p.fn[b]=function(d,e,f){return d==null||typeof d=="boolean"||!a&&p.isFunction(d)&&p.isFunction(e)?c.apply(this,arguments):this.animate(cZ(b,!0),d,e,f)}}),p.fn.extend({fadeTo:function(a,b,c,d){return this.filter(bY).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=p.isEmptyObject(a),f=p.speed(b,c,d),g=function(){var b=cV(this,p.extend({},a),f);e&&b.stop(!0)};return e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,c,d){var e=function(a){var b=a.stop;delete a.stop,b(d)};return typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,c=a!=null&&a+"queueHooks",f=p.timers,g=p._data(this);if(c)g[c]&&g[c].stop&&e(g[c]);else for(c in g)g[c]&&g[c].stop&&cQ.test(c)&&e(g[c]);for(c=f.length;c--;)f[c].elem===this&&(a==null||f[c].queue===a)&&(f[c].anim.stop(d),b=!1,f.splice(c,1));(b||!d)&&p.dequeue(this,a)})}}),p.each({slideDown:cZ("show"),slideUp:cZ("hide"),slideToggle:cZ("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){p.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),p.speed=function(a,b,c){var d=a&&typeof a=="object"?p.extend({},a):{complete:c||!c&&b||p.isFunction(a)&&a,duration:a,easing:c&&b||b&&!p.isFunction(b)&&b};d.duration=p.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in p.fx.speeds?p.fx.speeds[d.duration]:p.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";return d.old=d.complete,d.complete=function(){p.isFunction(d.old)&&d.old.call(this),d.queue&&p.dequeue(this,d.queue)},d},p.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},p.timers=[],p.fx=cY.prototype.init,p.fx.tick=function(){var a,b=p.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||p.fx.stop()},p.fx.timer=function(a){a()&&p.timers.push(a)&&!cN&&(cN=setInterval(p.fx.tick,p.fx.interval))},p.fx.interval=13,p.fx.stop=function(){clearInterval(cN),cN=null},p.fx.speeds={slow:600,fast:200,_default:400},p.fx.step={},p.expr&&p.expr.filters&&(p.expr.filters.animated=function(a){return p.grep(p.timers,function(b){return a===b.elem}).length});var c$=/^(?:body|html)$/i;p.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){p.offset.setOffset(this,a,b)});var c,d,e,f,g,h,i,j,k,l,m=this[0],n=m&&m.ownerDocument;if(!n)return;return(e=n.body)===m?p.offset.bodyOffset(m):(d=n.documentElement,p.contains(d,m)?(c=m.getBoundingClientRect(),f=c_(n),g=d.clientTop||e.clientTop||0,h=d.clientLeft||e.clientLeft||0,i=f.pageYOffset||d.scrollTop,j=f.pageXOffset||d.scrollLeft,k=c.top+i-g,l=c.left+j-h,{top:k,left:l}):{top:0,left:0})},p.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;return p.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(p.css(a,"marginTop"))||0,c+=parseFloat(p.css(a,"marginLeft"))||0),{top:b,left:c}},setOffset:function(a,b,c){var d=p.css(a,"position");d==="static"&&(a.style.position="relative");var e=p(a),f=e.offset(),g=p.css(a,"top"),h=p.css(a,"left"),i=(d==="absolute"||d==="fixed")&&p.inArray("auto",[g,h])>-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c$.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c$.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=c_(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window);
diff --git a/catalog-be/src/main/resources/swagger/lib/jquery.ba-bbq.min.js b/catalog-be/src/main/resources/swagger/lib/jquery.ba-bbq.min.js
new file mode 100644
index 0000000000..0bcdb66da5
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/jquery.ba-bbq.min.js
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*
+ * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
+ * http://benalman.com/projects/jquery-bbq-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
+/*
+ * jQuery hashchange event - v1.2 - 2/11/2010
+ * http://benalman.com/projects/jquery-hashchange-plugin/
+ *
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);
diff --git a/catalog-be/src/main/resources/swagger/lib/jquery.slideto.min.js b/catalog-be/src/main/resources/swagger/lib/jquery.slideto.min.js
new file mode 100644
index 0000000000..df80c45ae6
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/jquery.slideto.min.js
@@ -0,0 +1,21 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);
diff --git a/catalog-be/src/main/resources/swagger/lib/jquery.wiggle.min.js b/catalog-be/src/main/resources/swagger/lib/jquery.wiggle.min.js
new file mode 100644
index 0000000000..0bdb2b2fd3
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/jquery.wiggle.min.js
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*
+jQuery Wiggle
+Author: WonderGroup, Jordan Thomas
+URL: http://labs.wondergroup.com/demos/mini-ui/index.html
+License: MIT (http://en.wikipedia.org/wiki/MIT_License)
+*/
+jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
+if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};
diff --git a/catalog-be/src/main/resources/swagger/lib/marked.js b/catalog-be/src/main/resources/swagger/lib/marked.js
new file mode 100644
index 0000000000..cfa182fe4d
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/marked.js
@@ -0,0 +1,1292 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+
+;(function() {
+
+/**
+ * Block-Level Grammar
+ */
+
+var block = {
+ newline: /^\n+/,
+ code: /^( {4}[^\n]+\n*)+/,
+ fences: noop,
+ hr: /^( *[-*_]){3,} *(?:\n+|$)/,
+ heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
+ nptable: noop,
+ lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
+ blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
+ list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
+ html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
+ def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
+ table: noop,
+ paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
+ text: /^[^\n]+/
+};
+
+block.bullet = /(?:[*+-]|\d+\.)/;
+block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
+block.item = replace(block.item, 'gm')
+ (/bull/g, block.bullet)
+ ();
+
+block.list = replace(block.list)
+ (/bull/g, block.bullet)
+ ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
+ ('def', '\\n+(?=' + block.def.source + ')')
+ ();
+
+block.blockquote = replace(block.blockquote)
+ ('def', block.def)
+ ();
+
+block._tag = '(?!(?:'
+ + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+ + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+ + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
+
+block.html = replace(block.html)
+ ('comment', /<!--[\s\S]*?-->/)
+ ('closed', /<(tag)[\s\S]+?<\/\1>/)
+ ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
+ (/tag/g, block._tag)
+ ();
+
+block.paragraph = replace(block.paragraph)
+ ('hr', block.hr)
+ ('heading', block.heading)
+ ('lheading', block.lheading)
+ ('blockquote', block.blockquote)
+ ('tag', '<' + block._tag)
+ ('def', block.def)
+ ();
+
+/**
+ * Normal Block Grammar
+ */
+
+block.normal = merge({}, block);
+
+/**
+ * GFM Block Grammar
+ */
+
+block.gfm = merge({}, block.normal, {
+ fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
+ paragraph: /^/
+});
+
+block.gfm.paragraph = replace(block.paragraph)
+ ('(?!', '(?!'
+ + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ + block.list.source.replace('\\1', '\\3') + '|')
+ ();
+
+/**
+ * GFM + Tables Block Grammar
+ */
+
+block.tables = merge({}, block.gfm, {
+ nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
+ table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
+});
+
+/**
+ * Block Lexer
+ */
+
+function Lexer(options) {
+ this.tokens = [];
+ this.tokens.links = {};
+ this.options = options || marked.defaults;
+ this.rules = block.normal;
+
+ if (this.options.gfm) {
+ if (this.options.tables) {
+ this.rules = block.tables;
+ } else {
+ this.rules = block.gfm;
+ }
+ }
+}
+
+/**
+ * Expose Block Rules
+ */
+
+Lexer.rules = block;
+
+/**
+ * Static Lex Method
+ */
+
+Lexer.lex = function(src, options) {
+ var lexer = new Lexer(options);
+ return lexer.lex(src);
+};
+
+/**
+ * Preprocessing
+ */
+
+Lexer.prototype.lex = function(src) {
+ src = src
+ .replace(/\r\n|\r/g, '\n')
+ .replace(/\t/g, ' ')
+ .replace(/\u00a0/g, ' ')
+ .replace(/\u2424/g, '\n');
+
+ return this.token(src, true);
+};
+
+/**
+ * Lexing
+ */
+
+Lexer.prototype.token = function(src, top, bq) {
+ var src = src.replace(/^ +$/gm, '')
+ , next
+ , loose
+ , cap
+ , bull
+ , b
+ , item
+ , space
+ , i
+ , l;
+
+ while (src) {
+ // newline
+ if (cap = this.rules.newline.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[0].length > 1) {
+ this.tokens.push({
+ type: 'space'
+ });
+ }
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ cap = cap[0].replace(/^ {4}/gm, '');
+ this.tokens.push({
+ type: 'code',
+ text: !this.options.pedantic
+ ? cap.replace(/\n+$/, '')
+ : cap
+ });
+ continue;
+ }
+
+ // fences (gfm)
+ if (cap = this.rules.fences.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'code',
+ lang: cap[2],
+ text: cap[3]
+ });
+ continue;
+ }
+
+ // heading
+ if (cap = this.rules.heading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[1].length,
+ text: cap[2]
+ });
+ continue;
+ }
+
+ // table no leading pipe (gfm)
+ if (top && (cap = this.rules.nptable.exec(src))) {
+ src = src.substring(cap[0].length);
+
+ item = {
+ type: 'table',
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3].replace(/\n$/, '').split('\n')
+ };
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = item.cells[i].split(/ *\| */);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+
+ // lheading
+ if (cap = this.rules.lheading.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'heading',
+ depth: cap[2] === '=' ? 1 : 2,
+ text: cap[1]
+ });
+ continue;
+ }
+
+ // hr
+ if (cap = this.rules.hr.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'hr'
+ });
+ continue;
+ }
+
+ // blockquote
+ if (cap = this.rules.blockquote.exec(src)) {
+ src = src.substring(cap[0].length);
+
+ this.tokens.push({
+ type: 'blockquote_start'
+ });
+
+ cap = cap[0].replace(/^ *> ?/gm, '');
+
+ // Pass `top` to keep the current
+ // "toplevel" state. This is exactly
+ // how markdown.pl works.
+ this.token(cap, top, true);
+
+ this.tokens.push({
+ type: 'blockquote_end'
+ });
+
+ continue;
+ }
+
+ // list
+ if (cap = this.rules.list.exec(src)) {
+ src = src.substring(cap[0].length);
+ bull = cap[2];
+
+ this.tokens.push({
+ type: 'list_start',
+ ordered: bull.length > 1
+ });
+
+ // Get each top-level item.
+ cap = cap[0].match(this.rules.item);
+
+ next = false;
+ l = cap.length;
+ i = 0;
+
+ for (; i < l; i++) {
+ item = cap[i];
+
+ // Remove the list item's bullet
+ // so it is seen as the next token.
+ space = item.length;
+ item = item.replace(/^ *([*+-]|\d+\.) +/, '');
+
+ // Outdent whatever the
+ // list item contains. Hacky.
+ if (~item.indexOf('\n ')) {
+ space -= item.length;
+ item = !this.options.pedantic
+ ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
+ : item.replace(/^ {1,4}/gm, '');
+ }
+
+ // Determine whether the next list item belongs here.
+ // Backpedal if it does not belong in this list.
+ if (this.options.smartLists && i !== l - 1) {
+ b = block.bullet.exec(cap[i + 1])[0];
+ if (bull !== b && !(bull.length > 1 && b.length > 1)) {
+ src = cap.slice(i + 1).join('\n') + src;
+ i = l - 1;
+ }
+ }
+
+ // Determine whether item is loose or not.
+ // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
+ // for discount behavior.
+ loose = next || /\n\n(?!\s*$)/.test(item);
+ if (i !== l - 1) {
+ next = item.charAt(item.length - 1) === '\n';
+ if (!loose) loose = next;
+ }
+
+ this.tokens.push({
+ type: loose
+ ? 'loose_item_start'
+ : 'list_item_start'
+ });
+
+ // Recurse.
+ this.token(item, false, bq);
+
+ this.tokens.push({
+ type: 'list_item_end'
+ });
+ }
+
+ this.tokens.push({
+ type: 'list_end'
+ });
+
+ continue;
+ }
+
+ // html
+ if (cap = this.rules.html.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: this.options.sanitize
+ ? 'paragraph'
+ : 'html',
+ pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
+ text: cap[0]
+ });
+ continue;
+ }
+
+ // def
+ if ((!bq && top) && (cap = this.rules.def.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.links[cap[1].toLowerCase()] = {
+ href: cap[2],
+ title: cap[3]
+ };
+ continue;
+ }
+
+ // table (gfm)
+ if (top && (cap = this.rules.table.exec(src))) {
+ src = src.substring(cap[0].length);
+
+ item = {
+ type: 'table',
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
+ cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+ };
+
+ for (i = 0; i < item.align.length; i++) {
+ if (/^ *-+: *$/.test(item.align[i])) {
+ item.align[i] = 'right';
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
+ item.align[i] = 'center';
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
+ item.align[i] = 'left';
+ } else {
+ item.align[i] = null;
+ }
+ }
+
+ for (i = 0; i < item.cells.length; i++) {
+ item.cells[i] = item.cells[i]
+ .replace(/^ *\| *| *\| *$/g, '')
+ .split(/ *\| */);
+ }
+
+ this.tokens.push(item);
+
+ continue;
+ }
+
+ // top-level paragraph
+ if (top && (cap = this.rules.paragraph.exec(src))) {
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'paragraph',
+ text: cap[1].charAt(cap[1].length - 1) === '\n'
+ ? cap[1].slice(0, -1)
+ : cap[1]
+ });
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ // Top-level should never reach here.
+ src = src.substring(cap[0].length);
+ this.tokens.push({
+ type: 'text',
+ text: cap[0]
+ });
+ continue;
+ }
+
+ if (src) {
+ throw new
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return this.tokens;
+};
+
+/**
+ * Inline-Level Grammar
+ */
+
+var inline = {
+ escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
+ autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
+ url: noop,
+ tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
+ link: /^!?\[(inside)\]\(href\)/,
+ reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
+ nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
+ strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
+ em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+ code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
+ br: /^ {2,}\n(?!\s*$)/,
+ del: noop,
+ text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
+};
+
+inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
+inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
+
+inline.link = replace(inline.link)
+ ('inside', inline._inside)
+ ('href', inline._href)
+ ();
+
+inline.reflink = replace(inline.reflink)
+ ('inside', inline._inside)
+ ();
+
+/**
+ * Normal Inline Grammar
+ */
+
+inline.normal = merge({}, inline);
+
+/**
+ * Pedantic Inline Grammar
+ */
+
+inline.pedantic = merge({}, inline.normal, {
+ strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
+ em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
+});
+
+/**
+ * GFM Inline Grammar
+ */
+
+inline.gfm = merge({}, inline.normal, {
+ escape: replace(inline.escape)('])', '~|])')(),
+ url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
+ del: /^~~(?=\S)([\s\S]*?\S)~~/,
+ text: replace(inline.text)
+ (']|', '~]|')
+ ('|', '|https?://|')
+ ()
+});
+
+/**
+ * GFM + Line Breaks Inline Grammar
+ */
+
+inline.breaks = merge({}, inline.gfm, {
+ br: replace(inline.br)('{2,}', '*')(),
+ text: replace(inline.gfm.text)('{2,}', '*')()
+});
+
+/**
+ * Inline Lexer & Compiler
+ */
+
+function InlineLexer(links, options) {
+ this.options = options || marked.defaults;
+ this.links = links;
+ this.rules = inline.normal;
+ this.renderer = this.options.renderer || new Renderer;
+ this.renderer.options = this.options;
+
+ if (!this.links) {
+ throw new
+ Error('Tokens array requires a `links` property.');
+ }
+
+ if (this.options.gfm) {
+ if (this.options.breaks) {
+ this.rules = inline.breaks;
+ } else {
+ this.rules = inline.gfm;
+ }
+ } else if (this.options.pedantic) {
+ this.rules = inline.pedantic;
+ }
+}
+
+/**
+ * Expose Inline Rules
+ */
+
+InlineLexer.rules = inline;
+
+/**
+ * Static Lexing/Compiling Method
+ */
+
+InlineLexer.output = function(src, links, options) {
+ var inline = new InlineLexer(links, options);
+ return inline.output(src);
+};
+
+/**
+ * Lexing/Compiling
+ */
+
+InlineLexer.prototype.output = function(src) {
+ var out = ''
+ , link
+ , text
+ , href
+ , cap;
+
+ while (src) {
+ // escape
+ if (cap = this.rules.escape.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += cap[1];
+ continue;
+ }
+
+ // autolink
+ if (cap = this.rules.autolink.exec(src)) {
+ src = src.substring(cap[0].length);
+ if (cap[2] === '@') {
+ text = cap[1].charAt(6) === ':'
+ ? this.mangle(cap[1].substring(7))
+ : this.mangle(cap[1]);
+ href = this.mangle('mailto:') + text;
+ } else {
+ text = escape(cap[1]);
+ href = text;
+ }
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // url (gfm)
+ if (!this.inLink && (cap = this.rules.url.exec(src))) {
+ src = src.substring(cap[0].length);
+ text = escape(cap[1]);
+ href = text;
+ out += this.renderer.link(href, null, text);
+ continue;
+ }
+
+ // tag
+ if (cap = this.rules.tag.exec(src)) {
+ if (!this.inLink && /^<a /i.test(cap[0])) {
+ this.inLink = true;
+ } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
+ this.inLink = false;
+ }
+ src = src.substring(cap[0].length);
+ out += this.options.sanitize
+ ? escape(cap[0])
+ : cap[0];
+ continue;
+ }
+
+ // link
+ if (cap = this.rules.link.exec(src)) {
+ src = src.substring(cap[0].length);
+ this.inLink = true;
+ out += this.outputLink(cap, {
+ href: cap[2],
+ title: cap[3]
+ });
+ this.inLink = false;
+ continue;
+ }
+
+ // reflink, nolink
+ if ((cap = this.rules.reflink.exec(src))
+ || (cap = this.rules.nolink.exec(src))) {
+ src = src.substring(cap[0].length);
+ link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
+ link = this.links[link.toLowerCase()];
+ if (!link || !link.href) {
+ out += cap[0].charAt(0);
+ src = cap[0].substring(1) + src;
+ continue;
+ }
+ this.inLink = true;
+ out += this.outputLink(cap, link);
+ this.inLink = false;
+ continue;
+ }
+
+ // strong
+ if (cap = this.rules.strong.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.strong(this.output(cap[2] || cap[1]));
+ continue;
+ }
+
+ // em
+ if (cap = this.rules.em.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.em(this.output(cap[2] || cap[1]));
+ continue;
+ }
+
+ // code
+ if (cap = this.rules.code.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.codespan(escape(cap[2], true));
+ continue;
+ }
+
+ // br
+ if (cap = this.rules.br.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.br();
+ continue;
+ }
+
+ // del (gfm)
+ if (cap = this.rules.del.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += this.renderer.del(this.output(cap[1]));
+ continue;
+ }
+
+ // text
+ if (cap = this.rules.text.exec(src)) {
+ src = src.substring(cap[0].length);
+ out += escape(this.smartypants(cap[0]));
+ continue;
+ }
+
+ if (src) {
+ throw new
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Compile Link
+ */
+
+InlineLexer.prototype.outputLink = function(cap, link) {
+ var href = escape(link.href)
+ , title = link.title ? escape(link.title) : null;
+
+ return cap[0].charAt(0) !== '!'
+ ? this.renderer.link(href, title, this.output(cap[1]))
+ : this.renderer.image(href, title, escape(cap[1]));
+};
+
+/**
+ * Smartypants Transformations
+ */
+
+InlineLexer.prototype.smartypants = function(text) {
+ if (!this.options.smartypants) return text;
+ return text
+ // em-dashes
+ .replace(/--/g, '\u2014')
+ // opening singles
+ .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
+ // closing singles & apostrophes
+ .replace(/'/g, '\u2019')
+ // opening doubles
+ .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
+ // closing doubles
+ .replace(/"/g, '\u201d')
+ // ellipses
+ .replace(/\.{3}/g, '\u2026');
+};
+
+/**
+ * Mangle Links
+ */
+
+InlineLexer.prototype.mangle = function(text) {
+ var out = ''
+ , l = text.length
+ , i = 0
+ , ch;
+
+ for (; i < l; i++) {
+ ch = text.charCodeAt(i);
+ if (Math.random() > 0.5) {
+ ch = 'x' + ch.toString(16);
+ }
+ out += '&#' + ch + ';';
+ }
+
+ return out;
+};
+
+/**
+ * Renderer
+ */
+
+function Renderer(options) {
+ this.options = options || {};
+}
+
+Renderer.prototype.code = function(code, lang, escaped) {
+ if (this.options.highlight) {
+ var out = this.options.highlight(code, lang);
+ if (out != null && out !== code) {
+ escaped = true;
+ code = out;
+ }
+ }
+
+ if (!lang) {
+ return '<pre><code>'
+ + (escaped ? code : escape(code, true))
+ + '\n</code></pre>';
+ }
+
+ return '<pre><code class="'
+ + this.options.langPrefix
+ + escape(lang, true)
+ + '">'
+ + (escaped ? code : escape(code, true))
+ + '\n</code></pre>\n';
+};
+
+Renderer.prototype.blockquote = function(quote) {
+ return '<blockquote>\n' + quote + '</blockquote>\n';
+};
+
+Renderer.prototype.html = function(html) {
+ return html;
+};
+
+Renderer.prototype.heading = function(text, level, raw) {
+ return '<h'
+ + level
+ + ' id="'
+ + this.options.headerPrefix
+ + raw.toLowerCase().replace(/[^\w]+/g, '-')
+ + '">'
+ + text
+ + '</h'
+ + level
+ + '>\n';
+};
+
+Renderer.prototype.hr = function() {
+ return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
+};
+
+Renderer.prototype.list = function(body, ordered) {
+ var type = ordered ? 'ol' : 'ul';
+ return '<' + type + '>\n' + body + '</' + type + '>\n';
+};
+
+Renderer.prototype.listitem = function(text) {
+ return '<li>' + text + '</li>\n';
+};
+
+Renderer.prototype.paragraph = function(text) {
+ return '<p>' + text + '</p>\n';
+};
+
+Renderer.prototype.table = function(header, body) {
+ return '<table>\n'
+ + '<thead>\n'
+ + header
+ + '</thead>\n'
+ + '<tbody>\n'
+ + body
+ + '</tbody>\n'
+ + '</table>\n';
+};
+
+Renderer.prototype.tablerow = function(content) {
+ return '<tr>\n' + content + '</tr>\n';
+};
+
+Renderer.prototype.tablecell = function(content, flags) {
+ var type = flags.header ? 'th' : 'td';
+ var tag = flags.align
+ ? '<' + type + ' style="text-align:' + flags.align + '">'
+ : '<' + type + '>';
+ return tag + content + '</' + type + '>\n';
+};
+
+// span level renderer
+Renderer.prototype.strong = function(text) {
+ return '<strong>' + text + '</strong>';
+};
+
+Renderer.prototype.em = function(text) {
+ return '<em>' + text + '</em>';
+};
+
+Renderer.prototype.codespan = function(text) {
+ return '<code>' + text + '</code>';
+};
+
+Renderer.prototype.br = function() {
+ return this.options.xhtml ? '<br/>' : '<br>';
+};
+
+Renderer.prototype.del = function(text) {
+ return '<del>' + text + '</del>';
+};
+
+Renderer.prototype.link = function(href, title, text) {
+ if (this.options.sanitize) {
+ try {
+ var prot = decodeURIComponent(unescape(href))
+ .replace(/[^\w:]/g, '')
+ .toLowerCase();
+ } catch (e) {
+ return '';
+ }
+ if (prot.indexOf('javascript:') === 0) {
+ return '';
+ }
+ }
+ var out = '<a href="' + href + '"';
+ if (title) {
+ out += ' title="' + title + '"';
+ }
+ out += '>' + text + '</a>';
+ return out;
+};
+
+Renderer.prototype.image = function(href, title, text) {
+ var out = '<img src="' + href + '" alt="' + text + '"';
+ if (title) {
+ out += ' title="' + title + '"';
+ }
+ out += this.options.xhtml ? '/>' : '>';
+ return out;
+};
+
+/**
+ * Parsing & Compiling
+ */
+
+function Parser(options) {
+ this.tokens = [];
+ this.token = null;
+ this.options = options || marked.defaults;
+ this.options.renderer = this.options.renderer || new Renderer;
+ this.renderer = this.options.renderer;
+ this.renderer.options = this.options;
+}
+
+/**
+ * Static Parse Method
+ */
+
+Parser.parse = function(src, options, renderer) {
+ var parser = new Parser(options, renderer);
+ return parser.parse(src);
+};
+
+/**
+ * Parse Loop
+ */
+
+Parser.prototype.parse = function(src) {
+ this.inline = new InlineLexer(src.links, this.options, this.renderer);
+ this.tokens = src.reverse();
+
+ var out = '';
+ while (this.next()) {
+ out += this.tok();
+ }
+
+ return out;
+};
+
+/**
+ * Next Token
+ */
+
+Parser.prototype.next = function() {
+ return this.token = this.tokens.pop();
+};
+
+/**
+ * Preview Next Token
+ */
+
+Parser.prototype.peek = function() {
+ return this.tokens[this.tokens.length - 1] || 0;
+};
+
+/**
+ * Parse Text Tokens
+ */
+
+Parser.prototype.parseText = function() {
+ var body = this.token.text;
+
+ while (this.peek().type === 'text') {
+ body += '\n' + this.next().text;
+ }
+
+ return this.inline.output(body);
+};
+
+/**
+ * Parse Current Token
+ */
+
+Parser.prototype.tok = function() {
+ switch (this.token.type) {
+ case 'space': {
+ return '';
+ }
+ case 'hr': {
+ return this.renderer.hr();
+ }
+ case 'heading': {
+ return this.renderer.heading(
+ this.inline.output(this.token.text),
+ this.token.depth,
+ this.token.text);
+ }
+ case 'code': {
+ return this.renderer.code(this.token.text,
+ this.token.lang,
+ this.token.escaped);
+ }
+ case 'table': {
+ var header = ''
+ , body = ''
+ , i
+ , row
+ , cell
+ , flags
+ , j;
+
+ // header
+ cell = '';
+ for (i = 0; i < this.token.header.length; i++) {
+ flags = { header: true, align: this.token.align[i] };
+ cell += this.renderer.tablecell(
+ this.inline.output(this.token.header[i]),
+ { header: true, align: this.token.align[i] }
+ );
+ }
+ header += this.renderer.tablerow(cell);
+
+ for (i = 0; i < this.token.cells.length; i++) {
+ row = this.token.cells[i];
+
+ cell = '';
+ for (j = 0; j < row.length; j++) {
+ cell += this.renderer.tablecell(
+ this.inline.output(row[j]),
+ { header: false, align: this.token.align[j] }
+ );
+ }
+
+ body += this.renderer.tablerow(cell);
+ }
+ return this.renderer.table(header, body);
+ }
+ case 'blockquote_start': {
+ var body = '';
+
+ while (this.next().type !== 'blockquote_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.blockquote(body);
+ }
+ case 'list_start': {
+ var body = ''
+ , ordered = this.token.ordered;
+
+ while (this.next().type !== 'list_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.list(body, ordered);
+ }
+ case 'list_item_start': {
+ var body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.token.type === 'text'
+ ? this.parseText()
+ : this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'loose_item_start': {
+ var body = '';
+
+ while (this.next().type !== 'list_item_end') {
+ body += this.tok();
+ }
+
+ return this.renderer.listitem(body);
+ }
+ case 'html': {
+ var html = !this.token.pre && !this.options.pedantic
+ ? this.inline.output(this.token.text)
+ : this.token.text;
+ return this.renderer.html(html);
+ }
+ case 'paragraph': {
+ return this.renderer.paragraph(this.inline.output(this.token.text));
+ }
+ case 'text': {
+ return this.renderer.paragraph(this.parseText());
+ }
+ }
+};
+
+/**
+ * Helpers
+ */
+
+function escape(html, encode) {
+ return html
+ .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/"/g, '&quot;')
+ .replace(/'/g, '&#39;');
+}
+
+function unescape(html) {
+ return html.replace(/&([#\w]+);/g, function(_, n) {
+ n = n.toLowerCase();
+ if (n === 'colon') return ':';
+ if (n.charAt(0) === '#') {
+ return n.charAt(1) === 'x'
+ ? String.fromCharCode(parseInt(n.substring(2), 16))
+ : String.fromCharCode(+n.substring(1));
+ }
+ return '';
+ });
+}
+
+function replace(regex, opt) {
+ regex = regex.source;
+ opt = opt || '';
+ return function self(name, val) {
+ if (!name) return new RegExp(regex, opt);
+ val = val.source || val;
+ val = val.replace(/(^|[^\[])\^/g, '$1');
+ regex = regex.replace(name, val);
+ return self;
+ };
+}
+
+function noop() {}
+noop.exec = noop;
+
+function merge(obj) {
+ var i = 1
+ , target
+ , key;
+
+ for (; i < arguments.length; i++) {
+ target = arguments[i];
+ for (key in target) {
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
+ obj[key] = target[key];
+ }
+ }
+ }
+
+ return obj;
+}
+
+
+/**
+ * Marked
+ */
+
+function marked(src, opt, callback) {
+ if (callback || typeof opt === 'function') {
+ if (!callback) {
+ callback = opt;
+ opt = null;
+ }
+
+ opt = merge({}, marked.defaults, opt || {});
+
+ var highlight = opt.highlight
+ , tokens
+ , pending
+ , i = 0;
+
+ try {
+ tokens = Lexer.lex(src, opt)
+ } catch (e) {
+ return callback(e);
+ }
+
+ pending = tokens.length;
+
+ var done = function(err) {
+ if (err) {
+ opt.highlight = highlight;
+ return callback(err);
+ }
+
+ var out;
+
+ try {
+ out = Parser.parse(tokens, opt);
+ } catch (e) {
+ err = e;
+ }
+
+ opt.highlight = highlight;
+
+ return err
+ ? callback(err)
+ : callback(null, out);
+ };
+
+ if (!highlight || highlight.length < 3) {
+ return done();
+ }
+
+ delete opt.highlight;
+
+ if (!pending) return done();
+
+ for (; i < tokens.length; i++) {
+ (function(token) {
+ if (token.type !== 'code') {
+ return --pending || done();
+ }
+ return highlight(token.text, token.lang, function(err, code) {
+ if (err) return done(err);
+ if (code == null || code === token.text) {
+ return --pending || done();
+ }
+ token.text = code;
+ token.escaped = true;
+ --pending || done();
+ });
+ })(tokens[i]);
+ }
+
+ return;
+ }
+ try {
+ if (opt) opt = merge({}, marked.defaults, opt);
+ return Parser.parse(Lexer.lex(src, opt), opt);
+ } catch (e) {
+ e.message += '\nPlease report this to https://github.com/chjj/marked.';
+ if ((opt || marked.defaults).silent) {
+ return '<p>An error occured:</p><pre>'
+ + escape(e.message + '', true)
+ + '</pre>';
+ }
+ throw e;
+ }
+}
+
+/**
+ * Options
+ */
+
+marked.options =
+marked.setOptions = function(opt) {
+ merge(marked.defaults, opt);
+ return marked;
+};
+
+marked.defaults = {
+ gfm: true,
+ tables: true,
+ breaks: false,
+ pedantic: false,
+ sanitize: false,
+ smartLists: false,
+ silent: false,
+ highlight: null,
+ langPrefix: 'lang-',
+ smartypants: false,
+ headerPrefix: '',
+ renderer: new Renderer,
+ xhtml: false
+};
+
+/**
+ * Expose
+ */
+
+marked.Parser = Parser;
+marked.parser = Parser.parse;
+
+marked.Renderer = Renderer;
+
+marked.Lexer = Lexer;
+marked.lexer = Lexer.lex;
+
+marked.InlineLexer = InlineLexer;
+marked.inlineLexer = InlineLexer.output;
+
+marked.parse = marked;
+
+if (typeof module !== 'undefined' && typeof exports === 'object') {
+ module.exports = marked;
+} else if (typeof define === 'function' && define.amd) {
+ define(function() { return marked; });
+} else {
+ this.marked = marked;
+}
+
+}).call(function() {
+ return this || (typeof window !== 'undefined' ? window : global);
+}());
diff --git a/catalog-be/src/main/resources/swagger/lib/swagger-oauth.js b/catalog-be/src/main/resources/swagger/lib/swagger-oauth.js
new file mode 100644
index 0000000000..d44bdf8fde
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/swagger-oauth.js
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var appName;
+var popupMask;
+var popupDialog;
+var clientId;
+var realm;
+var oauth2KeyName;
+var redirect_uri;
+
+function handleLogin() {
+ var scopes = [];
+
+ var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions;
+ if(auths) {
+ var key;
+ var defs = auths;
+ for(key in defs) {
+ var auth = defs[key];
+ if(auth.type === 'oauth2' && auth.scopes) {
+ oauth2KeyName = key;
+ var scope;
+ if(Array.isArray(auth.scopes)) {
+ // 1.2 support
+ var i;
+ for(i = 0; i < auth.scopes.length; i++) {
+ scopes.push(auth.scopes[i]);
+ }
+ }
+ else {
+ // 2.0 support
+ for(scope in auth.scopes) {
+ scopes.push({scope: scope, description: auth.scopes[scope]});
+ }
+ }
+ }
+ }
+ }
+
+ if(window.swaggerUi.api
+ && window.swaggerUi.api.info) {
+ appName = window.swaggerUi.api.info.title;
+ }
+
+ popupDialog = $(
+ [
+ '<div class="api-popup-dialog">',
+ '<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
+ '<div class="api-popup-content">',
+ '<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
+ '<a href="#">Learn how to use</a>',
+ '</p>',
+ '<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
+ '<ul class="api-popup-scopes">',
+ '</ul>',
+ '<p class="error-msg"></p>',
+ '<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
+ '</div>',
+ '</div>'].join(''));
+ $(document.body).append(popupDialog);
+
+ popup = popupDialog.find('ul.api-popup-scopes').empty();
+ for (i = 0; i < scopes.length; i ++) {
+ scope = scopes[i];
+ str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
+ if (scope.description) {
+ str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
+ }
+ str += '</label></li>';
+ popup.append(str);
+ }
+
+ var $win = $(window),
+ dw = $win.width(),
+ dh = $win.height(),
+ st = $win.scrollTop(),
+ dlgWd = popupDialog.outerWidth(),
+ dlgHt = popupDialog.outerHeight(),
+ top = (dh -dlgHt)/2 + st,
+ left = (dw - dlgWd)/2;
+
+ popupDialog.css({
+ top: (top < 0? 0 : top) + 'px',
+ left: (left < 0? 0 : left) + 'px'
+ });
+
+ popupDialog.find('button.api-popup-cancel').click(function() {
+ popupMask.hide();
+ popupDialog.hide();
+ popupDialog.empty();
+ popupDialog = [];
+ });
+
+ $('button.api-popup-authbtn').unbind();
+ popupDialog.find('button.api-popup-authbtn').click(function() {
+ popupMask.hide();
+ popupDialog.hide();
+
+ var authSchemes = window.swaggerUi.api.authSchemes;
+ var host = window.location;
+ var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
+ var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
+ var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
+ var url = null;
+
+ for (var key in authSchemes) {
+ if (authSchemes.hasOwnProperty(key)) {
+ var flow = authSchemes[key].flow;
+
+ if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
+ var dets = authSchemes[key];
+ url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
+ window.swaggerUi.tokenName = dets.tokenName || 'access_token';
+ window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
+ }
+ else if(authSchemes[key].grantTypes) {
+ // 1.2 support
+ var o = authSchemes[key].grantTypes;
+ for(var t in o) {
+ if(o.hasOwnProperty(t) && t === 'implicit') {
+ var dets = o[t];
+ var ep = dets.loginEndpoint.url;
+ url = dets.loginEndpoint.url + '?response_type=token';
+ window.swaggerUi.tokenName = dets.tokenName;
+ }
+ else if (o.hasOwnProperty(t) && t === 'accessCode') {
+ var dets = o[t];
+ var ep = dets.tokenRequestEndpoint.url;
+ url = dets.tokenRequestEndpoint.url + '?response_type=code';
+ window.swaggerUi.tokenName = dets.tokenName;
+ }
+ }
+ }
+ }
+ }
+ var scopes = []
+ var o = $('.api-popup-scopes').find('input:checked');
+
+ for(k =0; k < o.length; k++) {
+ var scope = $(o[k]).attr('scope');
+
+ if (scopes.indexOf(scope) === -1)
+ scopes.push(scope);
+ }
+
+ // Implicit auth recommends a state parameter.
+ var state = Math.random ();
+
+ window.enabledScopes=scopes;
+
+ redirect_uri = redirectUrl;
+
+ url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
+ url += '&realm=' + encodeURIComponent(realm);
+ url += '&client_id=' + encodeURIComponent(clientId);
+ url += '&scope=' + encodeURIComponent(scopes.join(' '));
+ url += '&state=' + encodeURIComponent(state);
+
+ window.open(url);
+ });
+
+ popupMask.show();
+ popupDialog.show();
+ return;
+}
+
+
+function handleLogout() {
+ for(key in window.authorizations.authz){
+ window.authorizations.remove(key)
+ }
+ window.enabledScopes = null;
+ $('.api-ic.ic-on').addClass('ic-off');
+ $('.api-ic.ic-on').removeClass('ic-on');
+
+ // set the info box
+ $('.api-ic.ic-warning').addClass('ic-error');
+ $('.api-ic.ic-warning').removeClass('ic-warning');
+}
+
+function initOAuth(opts) {
+ var o = (opts||{});
+ var errors = [];
+
+ appName = (o.appName||errors.push('missing appName'));
+ popupMask = (o.popupMask||$('#api-common-mask'));
+ popupDialog = (o.popupDialog||$('.api-popup-dialog'));
+ clientId = (o.clientId||errors.push('missing client id'));
+ realm = (o.realm||errors.push('missing realm'));
+
+ if(errors.length > 0){
+ log('auth unable initialize oauth: ' + errors);
+ return;
+ }
+
+ $('pre code').each(function(i, e) {hljs.highlightBlock(e)});
+ $('.api-ic').unbind();
+ $('.api-ic').click(function(s) {
+ if($(s.target).hasClass('ic-off'))
+ handleLogin();
+ else {
+ handleLogout();
+ }
+ false;
+ });
+}
+
+window.processOAuthCode = function processOAuthCode(data) {
+ var params = {
+ 'client_id': clientId,
+ 'code': data.code,
+ 'grant_type': 'authorization_code',
+ 'redirect_uri': redirect_uri
+ }
+ $.ajax(
+ {
+ url : window.swaggerUi.tokenUrl,
+ type: "POST",
+ data: params,
+ success:function(data, textStatus, jqXHR)
+ {
+ onOAuthComplete(data);
+ },
+ error: function(jqXHR, textStatus, errorThrown)
+ {
+ onOAuthComplete("");
+ }
+ });
+}
+
+window.onOAuthComplete = function onOAuthComplete(token) {
+ if(token) {
+ if(token.error) {
+ var checkbox = $('input[type=checkbox],.secured')
+ checkbox.each(function(pos){
+ checkbox[pos].checked = false;
+ });
+ alert(token.error);
+ }
+ else {
+ var b = token[window.swaggerUi.tokenName];
+ if(b){
+ // if all roles are satisfied
+ var o = null;
+ $.each($('.auth #api_information_panel'), function(k, v) {
+ var children = v;
+ if(children && children.childNodes) {
+ var requiredScopes = [];
+ $.each((children.childNodes), function (k1, v1){
+ var inner = v1.innerHTML;
+ if(inner)
+ requiredScopes.push(inner);
+ });
+ var diff = [];
+ for(var i=0; i < requiredScopes.length; i++) {
+ var s = requiredScopes[i];
+ if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
+ diff.push(s);
+ }
+ }
+ if(diff.length > 0){
+ o = v.parentNode;
+ $(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
+ $(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
+
+ // sorry, not all scopes are satisfied
+ $(o).find('.api-ic').addClass('ic-warning');
+ $(o).find('.api-ic').removeClass('ic-error');
+ }
+ else {
+ o = v.parentNode;
+ $(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
+ $(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
+
+ // all scopes are satisfied
+ $(o).find('.api-ic').addClass('ic-info');
+ $(o).find('.api-ic').removeClass('ic-warning');
+ $(o).find('.api-ic').removeClass('ic-error');
+ }
+ }
+ });
+ window.authorizations.add(oauth2KeyName, new ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header'));
+ }
+ }
+ }
+}
diff --git a/catalog-be/src/main/resources/swagger/lib/underscore-min.js b/catalog-be/src/main/resources/swagger/lib/underscore-min.js
new file mode 100644
index 0000000000..56cf7330d2
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/underscore-min.js
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+// Underscore.js 1.7.0
+// http://underscorejs.org
+// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
+(function(){var n=this,t=n._,r=Array.prototype,e=Object.prototype,u=Function.prototype,i=r.push,a=r.slice,o=r.concat,l=e.toString,c=e.hasOwnProperty,f=Array.isArray,s=Object.keys,p=u.bind,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=h),exports._=h):n._=h,h.VERSION="1.7.0";var g=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}};h.iteratee=function(n,t,r){return null==n?h.identity:h.isFunction(n)?g(n,t,r):h.isObject(n)?h.matches(n):h.property(n)},h.each=h.forEach=function(n,t,r){if(null==n)return n;t=g(t,r);var e,u=n.length;if(u===+u)for(e=0;u>e;e++)t(n[e],e,n);else{var i=h.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},h.map=h.collect=function(n,t,r){if(null==n)return[];t=h.iteratee(t,r);for(var e,u=n.length!==+n.length&&h.keys(n),i=(u||n).length,a=Array(i),o=0;i>o;o++)e=u?u[o]:o,a[o]=t(n[e],e,n);return a};var v="Reduce of empty array with no initial value";h.reduce=h.foldl=h.inject=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length,o=0;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[o++]:o++]}for(;a>o;o++)u=i?i[o]:o,r=t(r,n[u],u,n);return r},h.reduceRight=h.foldr=function(n,t,r,e){null==n&&(n=[]),t=g(t,e,4);var u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;if(arguments.length<3){if(!a)throw new TypeError(v);r=n[i?i[--a]:--a]}for(;a--;)u=i?i[a]:a,r=t(r,n[u],u,n);return r},h.find=h.detect=function(n,t,r){var e;return t=h.iteratee(t,r),h.some(n,function(n,r,u){return t(n,r,u)?(e=n,!0):void 0}),e},h.filter=h.select=function(n,t,r){var e=[];return null==n?e:(t=h.iteratee(t,r),h.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e)},h.reject=function(n,t,r){return h.filter(n,h.negate(h.iteratee(t)),r)},h.every=h.all=function(n,t,r){if(null==n)return!0;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,!t(n[u],u,n))return!1;return!0},h.some=h.any=function(n,t,r){if(null==n)return!1;t=h.iteratee(t,r);var e,u,i=n.length!==+n.length&&h.keys(n),a=(i||n).length;for(e=0;a>e;e++)if(u=i?i[e]:e,t(n[u],u,n))return!0;return!1},h.contains=h.include=function(n,t){return null==n?!1:(n.length!==+n.length&&(n=h.values(n)),h.indexOf(n,t)>=0)},h.invoke=function(n,t){var r=a.call(arguments,2),e=h.isFunction(t);return h.map(n,function(n){return(e?t:n[t]).apply(n,r)})},h.pluck=function(n,t){return h.map(n,h.property(t))},h.where=function(n,t){return h.filter(n,h.matches(t))},h.findWhere=function(n,t){return h.find(n,h.matches(t))},h.max=function(n,t,r){var e,u,i=-1/0,a=-1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],e>i&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(u>a||u===-1/0&&i===-1/0)&&(i=n,a=u)});return i},h.min=function(n,t,r){var e,u,i=1/0,a=1/0;if(null==t&&null!=n){n=n.length===+n.length?n:h.values(n);for(var o=0,l=n.length;l>o;o++)e=n[o],i>e&&(i=e)}else t=h.iteratee(t,r),h.each(n,function(n,r,e){u=t(n,r,e),(a>u||1/0===u&&1/0===i)&&(i=n,a=u)});return i},h.shuffle=function(n){for(var t,r=n&&n.length===+n.length?n:h.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=h.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},h.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=h.values(n)),n[h.random(n.length-1)]):h.shuffle(n).slice(0,Math.max(0,t))},h.sortBy=function(n,t,r){return t=h.iteratee(t,r),h.pluck(h.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var m=function(n){return function(t,r,e){var u={};return r=h.iteratee(r,e),h.each(t,function(e,i){var a=r(e,i,t);n(u,e,a)}),u}};h.groupBy=m(function(n,t,r){h.has(n,r)?n[r].push(t):n[r]=[t]}),h.indexBy=m(function(n,t,r){n[r]=t}),h.countBy=m(function(n,t,r){h.has(n,r)?n[r]++:n[r]=1}),h.sortedIndex=function(n,t,r,e){r=h.iteratee(r,e,1);for(var u=r(t),i=0,a=n.length;a>i;){var o=i+a>>>1;r(n[o])<u?i=o+1:a=o}return i},h.toArray=function(n){return n?h.isArray(n)?a.call(n):n.length===+n.length?h.map(n,h.identity):h.values(n):[]},h.size=function(n){return null==n?0:n.length===+n.length?n.length:h.keys(n).length},h.partition=function(n,t,r){t=h.iteratee(t,r);var e=[],u=[];return h.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},h.first=h.head=h.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:0>t?[]:a.call(n,0,t)},h.initial=function(n,t,r){return a.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},h.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:a.call(n,Math.max(n.length-t,0))},h.rest=h.tail=h.drop=function(n,t,r){return a.call(n,null==t||r?1:t)},h.compact=function(n){return h.filter(n,h.identity)};var y=function(n,t,r,e){if(t&&h.every(n,h.isArray))return o.apply(e,n);for(var u=0,a=n.length;a>u;u++){var l=n[u];h.isArray(l)||h.isArguments(l)?t?i.apply(e,l):y(l,t,r,e):r||e.push(l)}return e};h.flatten=function(n,t){return y(n,t,!1,[])},h.without=function(n){return h.difference(n,a.call(arguments,1))},h.uniq=h.unique=function(n,t,r,e){if(null==n)return[];h.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=h.iteratee(r,e));for(var u=[],i=[],a=0,o=n.length;o>a;a++){var l=n[a];if(t)a&&i===l||u.push(l),i=l;else if(r){var c=r(l,a,n);h.indexOf(i,c)<0&&(i.push(c),u.push(l))}else h.indexOf(u,l)<0&&u.push(l)}return u},h.union=function(){return h.uniq(y(arguments,!0,!0,[]))},h.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!h.contains(t,i)){for(var a=1;r>a&&h.contains(arguments[a],i);a++);a===r&&t.push(i)}}return t},h.difference=function(n){var t=y(a.call(arguments,1),!0,!0,[]);return h.filter(n,function(n){return!h.contains(t,n)})},h.zip=function(n){if(null==n)return[];for(var t=h.max(arguments,"length").length,r=Array(t),e=0;t>e;e++)r[e]=h.pluck(arguments,e);return r},h.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},h.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=h.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}for(;u>e;e++)if(n[e]===t)return e;return-1},h.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=n.length;for("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1));--e>=0;)if(n[e]===t)return e;return-1},h.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var d=function(){};h.bind=function(n,t){var r,e;if(p&&n.bind===p)return p.apply(n,a.call(arguments,1));if(!h.isFunction(n))throw new TypeError("Bind must be called on a function");return r=a.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(a.call(arguments)));d.prototype=n.prototype;var u=new d;d.prototype=null;var i=n.apply(u,r.concat(a.call(arguments)));return h.isObject(i)?i:u}},h.partial=function(n){var t=a.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===h&&(e[u]=arguments[r++]);for(;r<arguments.length;)e.push(arguments[r++]);return n.apply(this,e)}},h.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=h.bind(n[r],n);return n},h.memoize=function(n,t){var r=function(e){var u=r.cache,i=t?t.apply(this,arguments):e;return h.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},h.delay=function(n,t){var r=a.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},h.defer=function(n){return h.delay.apply(h,[n,1].concat(a.call(arguments,1)))},h.throttle=function(n,t,r){var e,u,i,a=null,o=0;r||(r={});var l=function(){o=r.leading===!1?0:h.now(),a=null,i=n.apply(e,u),a||(e=u=null)};return function(){var c=h.now();o||r.leading!==!1||(o=c);var f=t-(c-o);return e=this,u=arguments,0>=f||f>t?(clearTimeout(a),a=null,o=c,i=n.apply(e,u),a||(e=u=null)):a||r.trailing===!1||(a=setTimeout(l,f)),i}},h.debounce=function(n,t,r){var e,u,i,a,o,l=function(){var c=h.now()-a;t>c&&c>0?e=setTimeout(l,t-c):(e=null,r||(o=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,a=h.now();var c=r&&!e;return e||(e=setTimeout(l,t)),c&&(o=n.apply(i,u),i=u=null),o}},h.wrap=function(n,t){return h.partial(t,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},h.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},h.before=function(n,t){var r;return function(){return--n>0?r=t.apply(this,arguments):t=null,r}},h.once=h.partial(h.before,2),h.keys=function(n){if(!h.isObject(n))return[];if(s)return s(n);var t=[];for(var r in n)h.has(n,r)&&t.push(r);return t},h.values=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},h.pairs=function(n){for(var t=h.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},h.invert=function(n){for(var t={},r=h.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},h.functions=h.methods=function(n){var t=[];for(var r in n)h.isFunction(n[r])&&t.push(r);return t.sort()},h.extend=function(n){if(!h.isObject(n))return n;for(var t,r,e=1,u=arguments.length;u>e;e++){t=arguments[e];for(r in t)c.call(t,r)&&(n[r]=t[r])}return n},h.pick=function(n,t,r){var e,u={};if(null==n)return u;if(h.isFunction(t)){t=g(t,r);for(e in n){var i=n[e];t(i,e,n)&&(u[e]=i)}}else{var l=o.apply([],a.call(arguments,1));n=new Object(n);for(var c=0,f=l.length;f>c;c++)e=l[c],e in n&&(u[e]=n[e])}return u},h.omit=function(n,t,r){if(h.isFunction(t))t=h.negate(t);else{var e=h.map(o.apply([],a.call(arguments,1)),String);t=function(n,t){return!h.contains(e,t)}}return h.pick(n,t,r)},h.defaults=function(n){if(!h.isObject(n))return n;for(var t=1,r=arguments.length;r>t;t++){var e=arguments[t];for(var u in e)n[u]===void 0&&(n[u]=e[u])}return n},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,t){return t(n),n};var b=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof h&&(n=n._wrapped),t instanceof h&&(t=t._wrapped);var u=l.call(n);if(u!==l.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]===n)return e[i]===t;var a=n.constructor,o=t.constructor;if(a!==o&&"constructor"in n&&"constructor"in t&&!(h.isFunction(a)&&a instanceof a&&h.isFunction(o)&&o instanceof o))return!1;r.push(n),e.push(t);var c,f;if("[object Array]"===u){if(c=n.length,f=c===t.length)for(;c--&&(f=b(n[c],t[c],r,e)););}else{var s,p=h.keys(n);if(c=p.length,f=h.keys(t).length===c)for(;c--&&(s=p[c],f=h.has(t,s)&&b(n[s],t[s],r,e)););}return r.pop(),e.pop(),f};h.isEqual=function(n,t){return b(n,t,[],[])},h.isEmpty=function(n){if(null==n)return!0;if(h.isArray(n)||h.isString(n)||h.isArguments(n))return 0===n.length;for(var t in n)if(h.has(n,t))return!1;return!0},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=f||function(n){return"[object Array]"===l.call(n)},h.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp"],function(n){h["is"+n]=function(t){return l.call(t)==="[object "+n+"]"}}),h.isArguments(arguments)||(h.isArguments=function(n){return h.has(n,"callee")}),"function"!=typeof/./&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&n!==+n},h.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===l.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return n===void 0},h.has=function(n,t){return null!=n&&c.call(n,t)},h.noConflict=function(){return n._=t,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(n){return function(t){return t[n]}},h.matches=function(n){var t=h.pairs(n),r=t.length;return function(n){if(null==n)return!r;n=new Object(n);for(var e=0;r>e;e++){var u=t[e],i=u[0];if(u[1]!==n[i]||!(i in n))return!1}return!0}},h.times=function(n,t,r){var e=Array(Math.max(0,n));t=g(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},h.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},h.now=Date.now||function(){return(new Date).getTime()};var _={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},w=h.invert(_),j=function(n){var t=function(t){return n[t]},r="(?:"+h.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=j(_),h.unescape=j(w),h.result=function(n,t){if(null==n)return void 0;var r=n[t];return h.isFunction(r)?n[t]():r};var x=0;h.uniqueId=function(n){var t=++x+"";return n?n+t:t},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var A=/(.)^/,k={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},O=/\\|'|\r|\n|\u2028|\u2029/g,F=function(n){return"\\"+k[n]};h.template=function(n,t,r){!t&&r&&(t=r),t=h.defaults({},t,h.templateSettings);var e=RegExp([(t.escape||A).source,(t.interpolate||A).source,(t.evaluate||A).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,a,o){return i+=n.slice(u,o).replace(O,F),u=o+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":a&&(i+="';\n"+a+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var a=new Function(t.variable||"obj","_",i)}catch(o){throw o.source=i,o}var l=function(n){return a.call(this,n,h)},c=t.variable||"obj";return l.source="function("+c+"){\n"+i+"}",l},h.chain=function(n){var t=h(n);return t._chain=!0,t};var E=function(n){return this._chain?h(n).chain():n};h.mixin=function(n){h.each(h.functions(n),function(t){var r=h[t]=n[t];h.prototype[t]=function(){var n=[this._wrapped];return i.apply(n,arguments),E.call(this,r.apply(h,n))}})},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=r[n];h.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],E.call(this,r)}}),h.each(["concat","join","slice"],function(n){var t=r[n];h.prototype[n]=function(){return E.call(this,t.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}).call(this);
+//# sourceMappingURL=underscore-min.map
diff --git a/catalog-be/src/main/resources/swagger/lib/underscore-min.map b/catalog-be/src/main/resources/swagger/lib/underscore-min.map
new file mode 100644
index 0000000000..b31e43590a
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/lib/underscore-min.map
@@ -0,0 +1 @@
+{"version":3,"file":"underscore-min.js","sources":["underscore.js"],"names":["createReduce","dir","iterator","obj","iteratee","memo","keys","index","length","currentKey","context","optimizeCb","isArrayLike","_","arguments","createIndexFinder","array","predicate","cb","collectNonEnumProps","nonEnumIdx","nonEnumerableProps","constructor","proto","isFunction","prototype","ObjProto","prop","has","contains","push","root","this","previousUnderscore","ArrayProto","Array","Object","FuncProto","Function","slice","toString","hasOwnProperty","nativeIsArray","isArray","nativeKeys","nativeBind","bind","nativeCreate","create","Ctor","_wrapped","exports","module","VERSION","func","argCount","value","call","other","collection","accumulator","apply","identity","isObject","matcher","property","Infinity","createAssigner","keysFunc","undefinedOnly","source","l","i","key","baseCreate","result","MAX_ARRAY_INDEX","Math","pow","each","forEach","map","collect","results","reduce","foldl","inject","reduceRight","foldr","find","detect","findIndex","findKey","filter","select","list","reject","negate","every","all","some","any","includes","include","target","fromIndex","values","indexOf","invoke","method","args","isFunc","pluck","where","attrs","findWhere","max","computed","lastComputed","min","shuffle","rand","set","shuffled","random","sample","n","guard","sortBy","criteria","sort","left","right","a","b","group","behavior","groupBy","indexBy","countBy","toArray","size","partition","pass","fail","first","head","take","initial","last","rest","tail","drop","compact","flatten","input","shallow","strict","startIndex","output","idx","isArguments","j","len","without","difference","uniq","unique","isSorted","isBoolean","seen","union","intersection","argsLength","item","zip","unzip","object","sortedIndex","isNaN","lastIndexOf","from","findLastIndex","low","high","mid","floor","range","start","stop","step","ceil","executeBound","sourceFunc","boundFunc","callingContext","self","TypeError","bound","concat","partial","boundArgs","position","bindAll","Error","memoize","hasher","cache","address","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","now","remaining","clearTimeout","trailing","debounce","immediate","timestamp","callNow","wrap","wrapper","compose","after","times","before","once","hasEnumBug","propertyIsEnumerable","allKeys","mapObject","pairs","invert","functions","methods","names","extend","extendOwn","assign","pick","oiteratee","omit","String","defaults","clone","tap","interceptor","isMatch","eq","aStack","bStack","className","areArrays","aCtor","bCtor","pop","isEqual","isEmpty","isString","isElement","nodeType","type","name","Int8Array","isFinite","parseFloat","isNumber","isNull","isUndefined","noConflict","constant","noop","propertyOf","matches","accum","Date","getTime","escapeMap","&","<",">","\"","'","`","unescapeMap","createEscaper","escaper","match","join","testRegexp","RegExp","replaceRegexp","string","test","replace","escape","unescape","fallback","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","
","
","escapeChar","template","text","settings","oldSettings","offset","variable","render","e","data","argument","chain","instance","_chain","mixin","valueOf","toJSON","define","amd"],"mappings":";;;;CAKC,WAoKC,QAASA,GAAaC,GAGpB,QAASC,GAASC,EAAKC,EAAUC,EAAMC,EAAMC,EAAOC,GAClD,KAAOD,GAAS,GAAaC,EAARD,EAAgBA,GAASN,EAAK,CACjD,GAAIQ,GAAaH,EAAOA,EAAKC,GAASA,CACtCF,GAAOD,EAASC,EAAMF,EAAIM,GAAaA,EAAYN,GAErD,MAAOE,GAGT,MAAO,UAASF,EAAKC,EAAUC,EAAMK,GACnCN,EAAWO,EAAWP,EAAUM,EAAS,EACzC,IAAIJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OACvBD,EAAQN,EAAM,EAAI,EAAIO,EAAS,CAMnC,OAJIM,WAAUN,OAAS,IACrBH,EAAOF,EAAIG,EAAOA,EAAKC,GAASA,GAChCA,GAASN,GAEJC,EAASC,EAAKC,EAAUC,EAAMC,EAAMC,EAAOC,IA+btD,QAASO,GAAkBd,GACzB,MAAO,UAASe,EAAOC,EAAWP,GAChCO,EAAYC,EAAGD,EAAWP,EAG1B,KAFA,GAAIF,GAAkB,MAATQ,GAAiBA,EAAMR,OAChCD,EAAQN,EAAM,EAAI,EAAIO,EAAS,EAC5BD,GAAS,GAAaC,EAARD,EAAgBA,GAASN,EAC5C,GAAIgB,EAAUD,EAAMT,GAAQA,EAAOS,GAAQ,MAAOT,EAEpD,QAAQ,GAgQZ,QAASY,GAAoBhB,EAAKG,GAChC,GAAIc,GAAaC,EAAmBb,OAChCc,EAAcnB,EAAImB,YAClBC,EAASV,EAAEW,WAAWF,IAAgBA,EAAYG,WAAcC,EAGhEC,EAAO,aAGX,KAFId,EAAEe,IAAIzB,EAAKwB,KAAUd,EAAEgB,SAASvB,EAAMqB,IAAOrB,EAAKwB,KAAKH,GAEpDP,KACLO,EAAON,EAAmBD,GACtBO,IAAQxB,IAAOA,EAAIwB,KAAUJ,EAAMI,KAAUd,EAAEgB,SAASvB,EAAMqB,IAChErB,EAAKwB,KAAKH,GAt4BhB,GAAII,GAAOC,KAGPC,EAAqBF,EAAKlB,EAG1BqB,EAAaC,MAAMV,UAAWC,EAAWU,OAAOX,UAAWY,EAAYC,SAASb,UAIlFK,EAAmBI,EAAWJ,KAC9BS,EAAmBL,EAAWK,MAC9BC,EAAmBd,EAASc,SAC5BC,EAAmBf,EAASe,eAK5BC,EAAqBP,MAAMQ,QAC3BC,EAAqBR,OAAO9B,KAC5BuC,EAAqBR,EAAUS,KAC/BC,EAAqBX,OAAOY,OAG1BC,EAAO,aAGPpC,EAAI,SAASV,GACf,MAAIA,aAAeU,GAAUV,EACvB6B,eAAgBnB,QACtBmB,KAAKkB,SAAW/C,GADiB,GAAIU,GAAEV,GAOlB,oBAAZgD,UACa,mBAAXC,SAA0BA,OAAOD,UAC1CA,QAAUC,OAAOD,QAAUtC,GAE7BsC,QAAQtC,EAAIA,GAEZkB,EAAKlB,EAAIA,EAIXA,EAAEwC,QAAU,OAKZ,IAAI1C,GAAa,SAAS2C,EAAM5C,EAAS6C,GACvC,GAAI7C,QAAiB,GAAG,MAAO4C,EAC/B,QAAoB,MAAZC,EAAmB,EAAIA,GAC7B,IAAK,GAAG,MAAO,UAASC,GACtB,MAAOF,GAAKG,KAAK/C,EAAS8C,GAE5B,KAAK,GAAG,MAAO,UAASA,EAAOE,GAC7B,MAAOJ,GAAKG,KAAK/C,EAAS8C,EAAOE,GAEnC,KAAK,GAAG,MAAO,UAASF,EAAOjD,EAAOoD,GACpC,MAAOL,GAAKG,KAAK/C,EAAS8C,EAAOjD,EAAOoD,GAE1C,KAAK,GAAG,MAAO,UAASC,EAAaJ,EAAOjD,EAAOoD,GACjD,MAAOL,GAAKG,KAAK/C,EAASkD,EAAaJ,EAAOjD,EAAOoD,IAGzD,MAAO,YACL,MAAOL,GAAKO,MAAMnD,EAASI,aAO3BI,EAAK,SAASsC,EAAO9C,EAAS6C,GAChC,MAAa,OAATC,EAAsB3C,EAAEiD,SACxBjD,EAAEW,WAAWgC,GAAe7C,EAAW6C,EAAO9C,EAAS6C,GACvD1C,EAAEkD,SAASP,GAAe3C,EAAEmD,QAAQR,GACjC3C,EAAEoD,SAAST,GAEpB3C,GAAET,SAAW,SAASoD,EAAO9C,GAC3B,MAAOQ,GAAGsC,EAAO9C,EAASwD,KAI5B,IAAIC,GAAiB,SAASC,EAAUC,GACtC,MAAO,UAASlE,GACd,GAAIK,GAASM,UAAUN,MACvB,IAAa,EAATA,GAAqB,MAAPL,EAAa,MAAOA,EACtC,KAAK,GAAII,GAAQ,EAAWC,EAARD,EAAgBA,IAIlC,IAAK,GAHD+D,GAASxD,UAAUP,GACnBD,EAAO8D,EAASE,GAChBC,EAAIjE,EAAKE,OACJgE,EAAI,EAAOD,EAAJC,EAAOA,IAAK,CAC1B,GAAIC,GAAMnE,EAAKkE,EACVH,IAAiBlE,EAAIsE,SAAc,KAAGtE,EAAIsE,GAAOH,EAAOG,IAGjE,MAAOtE,KAKPuE,EAAa,SAASjD,GACxB,IAAKZ,EAAEkD,SAAStC,GAAY,QAC5B,IAAIsB,EAAc,MAAOA,GAAatB,EACtCwB,GAAKxB,UAAYA,CACjB,IAAIkD,GAAS,GAAI1B,EAEjB,OADAA,GAAKxB,UAAY,KACVkD,GAMLC,EAAkBC,KAAKC,IAAI,EAAG,IAAM,EACpClE,EAAc,SAAS+C,GACzB,GAAInD,GAASmD,GAAcA,EAAWnD,MACtC,OAAwB,gBAAVA,IAAsBA,GAAU,GAAeoE,GAAVpE,EASrDK,GAAEkE,KAAOlE,EAAEmE,QAAU,SAAS7E,EAAKC,EAAUM,GAC3CN,EAAWO,EAAWP,EAAUM,EAChC,IAAI8D,GAAGhE,CACP,IAAII,EAAYT,GACd,IAAKqE,EAAI,EAAGhE,EAASL,EAAIK,OAAYA,EAAJgE,EAAYA,IAC3CpE,EAASD,EAAIqE,GAAIA,EAAGrE,OAEjB,CACL,GAAIG,GAAOO,EAAEP,KAAKH,EAClB,KAAKqE,EAAI,EAAGhE,EAASF,EAAKE,OAAYA,EAAJgE,EAAYA,IAC5CpE,EAASD,EAAIG,EAAKkE,IAAKlE,EAAKkE,GAAIrE,GAGpC,MAAOA,IAITU,EAAEoE,IAAMpE,EAAEqE,QAAU,SAAS/E,EAAKC,EAAUM,GAC1CN,EAAWc,EAAGd,EAAUM,EAIxB,KAAK,GAHDJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OACvB2E,EAAUhD,MAAM3B,GACXD,EAAQ,EAAWC,EAARD,EAAgBA,IAAS,CAC3C,GAAIE,GAAaH,EAAOA,EAAKC,GAASA,CACtC4E,GAAQ5E,GAASH,EAASD,EAAIM,GAAaA,EAAYN,GAEzD,MAAOgF,IA+BTtE,EAAEuE,OAASvE,EAAEwE,MAAQxE,EAAEyE,OAAStF,EAAa,GAG7Ca,EAAE0E,YAAc1E,EAAE2E,MAAQxF,GAAc,GAGxCa,EAAE4E,KAAO5E,EAAE6E,OAAS,SAASvF,EAAKc,EAAWP,GAC3C,GAAI+D,EAMJ,OAJEA,GADE7D,EAAYT,GACRU,EAAE8E,UAAUxF,EAAKc,EAAWP,GAE5BG,EAAE+E,QAAQzF,EAAKc,EAAWP,GAE9B+D,QAAa,IAAKA,KAAS,EAAUtE,EAAIsE,GAA7C,QAKF5D,EAAEgF,OAAShF,EAAEiF,OAAS,SAAS3F,EAAKc,EAAWP,GAC7C,GAAIyE,KAKJ,OAJAlE,GAAYC,EAAGD,EAAWP,GAC1BG,EAAEkE,KAAK5E,EAAK,SAASqD,EAAOjD,EAAOwF,GAC7B9E,EAAUuC,EAAOjD,EAAOwF,IAAOZ,EAAQrD,KAAK0B,KAE3C2B,GAITtE,EAAEmF,OAAS,SAAS7F,EAAKc,EAAWP,GAClC,MAAOG,GAAEgF,OAAO1F,EAAKU,EAAEoF,OAAO/E,EAAGD,IAAaP,IAKhDG,EAAEqF,MAAQrF,EAAEsF,IAAM,SAAShG,EAAKc,EAAWP,GACzCO,EAAYC,EAAGD,EAAWP,EAG1B,KAAK,GAFDJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OAClBD,EAAQ,EAAWC,EAARD,EAAgBA,IAAS,CAC3C,GAAIE,GAAaH,EAAOA,EAAKC,GAASA,CACtC,KAAKU,EAAUd,EAAIM,GAAaA,EAAYN,GAAM,OAAO,EAE3D,OAAO,GAKTU,EAAEuF,KAAOvF,EAAEwF,IAAM,SAASlG,EAAKc,EAAWP,GACxCO,EAAYC,EAAGD,EAAWP,EAG1B,KAAK,GAFDJ,IAAQM,EAAYT,IAAQU,EAAEP,KAAKH,GACnCK,GAAUF,GAAQH,GAAKK,OAClBD,EAAQ,EAAWC,EAARD,EAAgBA,IAAS,CAC3C,GAAIE,GAAaH,EAAOA,EAAKC,GAASA,CACtC,IAAIU,EAAUd,EAAIM,GAAaA,EAAYN,GAAM,OAAO,EAE1D,OAAO,GAKTU,EAAEgB,SAAWhB,EAAEyF,SAAWzF,EAAE0F,QAAU,SAASpG,EAAKqG,EAAQC,GAE1D,MADK7F,GAAYT,KAAMA,EAAMU,EAAE6F,OAAOvG,IAC/BU,EAAE8F,QAAQxG,EAAKqG,EAA4B,gBAAbC,IAAyBA,IAAc,GAI9E5F,EAAE+F,OAAS,SAASzG,EAAK0G,GACvB,GAAIC,GAAOvE,EAAMkB,KAAK3C,UAAW,GAC7BiG,EAASlG,EAAEW,WAAWqF,EAC1B,OAAOhG,GAAEoE,IAAI9E,EAAK,SAASqD,GACzB,GAAIF,GAAOyD,EAASF,EAASrD,EAAMqD,EACnC,OAAe,OAARvD,EAAeA,EAAOA,EAAKO,MAAML,EAAOsD,MAKnDjG,EAAEmG,MAAQ,SAAS7G,EAAKsE,GACtB,MAAO5D,GAAEoE,IAAI9E,EAAKU,EAAEoD,SAASQ,KAK/B5D,EAAEoG,MAAQ,SAAS9G,EAAK+G,GACtB,MAAOrG,GAAEgF,OAAO1F,EAAKU,EAAEmD,QAAQkD,KAKjCrG,EAAEsG,UAAY,SAAShH,EAAK+G,GAC1B,MAAOrG,GAAE4E,KAAKtF,EAAKU,EAAEmD,QAAQkD,KAI/BrG,EAAEuG,IAAM,SAASjH,EAAKC,EAAUM,GAC9B,GACI8C,GAAO6D,EADP1C,GAAUT,IAAUoD,GAAgBpD,GAExC,IAAgB,MAAZ9D,GAA2B,MAAPD,EAAa,CACnCA,EAAMS,EAAYT,GAAOA,EAAMU,EAAE6F,OAAOvG,EACxC,KAAK,GAAIqE,GAAI,EAAGhE,EAASL,EAAIK,OAAYA,EAAJgE,EAAYA,IAC/ChB,EAAQrD,EAAIqE,GACRhB,EAAQmB,IACVA,EAASnB,OAIbpD,GAAWc,EAAGd,EAAUM,GACxBG,EAAEkE,KAAK5E,EAAK,SAASqD,EAAOjD,EAAOwF,GACjCsB,EAAWjH,EAASoD,EAAOjD,EAAOwF,IAC9BsB,EAAWC,GAAgBD,KAAcnD,KAAYS,KAAYT,OACnES,EAASnB,EACT8D,EAAeD,IAIrB,OAAO1C,IAIT9D,EAAE0G,IAAM,SAASpH,EAAKC,EAAUM,GAC9B,GACI8C,GAAO6D,EADP1C,EAAST,IAAUoD,EAAepD,GAEtC,IAAgB,MAAZ9D,GAA2B,MAAPD,EAAa,CACnCA,EAAMS,EAAYT,GAAOA,EAAMU,EAAE6F,OAAOvG,EACxC,KAAK,GAAIqE,GAAI,EAAGhE,EAASL,EAAIK,OAAYA,EAAJgE,EAAYA,IAC/ChB,EAAQrD,EAAIqE,GACAG,EAARnB,IACFmB,EAASnB,OAIbpD,GAAWc,EAAGd,EAAUM,GACxBG,EAAEkE,KAAK5E,EAAK,SAASqD,EAAOjD,EAAOwF,GACjCsB,EAAWjH,EAASoD,EAAOjD,EAAOwF,IACnBuB,EAAXD,GAAwCnD,MAAbmD,GAAoCnD,MAAXS,KACtDA,EAASnB,EACT8D,EAAeD,IAIrB,OAAO1C,IAKT9D,EAAE2G,QAAU,SAASrH,GAInB,IAAK,GAAesH,GAHhBC,EAAM9G,EAAYT,GAAOA,EAAMU,EAAE6F,OAAOvG,GACxCK,EAASkH,EAAIlH,OACbmH,EAAWxF,MAAM3B,GACZD,EAAQ,EAAiBC,EAARD,EAAgBA,IACxCkH,EAAO5G,EAAE+G,OAAO,EAAGrH,GACfkH,IAASlH,IAAOoH,EAASpH,GAASoH,EAASF,IAC/CE,EAASF,GAAQC,EAAInH,EAEvB,OAAOoH,IAMT9G,EAAEgH,OAAS,SAAS1H,EAAK2H,EAAGC,GAC1B,MAAS,OAALD,GAAaC,GACVnH,EAAYT,KAAMA,EAAMU,EAAE6F,OAAOvG,IAC/BA,EAAIU,EAAE+G,OAAOzH,EAAIK,OAAS,KAE5BK,EAAE2G,QAAQrH,GAAKoC,MAAM,EAAGsC,KAAKuC,IAAI,EAAGU,KAI7CjH,EAAEmH,OAAS,SAAS7H,EAAKC,EAAUM,GAEjC,MADAN,GAAWc,EAAGd,EAAUM,GACjBG,EAAEmG,MAAMnG,EAAEoE,IAAI9E,EAAK,SAASqD,EAAOjD,EAAOwF,GAC/C,OACEvC,MAAOA,EACPjD,MAAOA,EACP0H,SAAU7H,EAASoD,EAAOjD,EAAOwF,MAElCmC,KAAK,SAASC,EAAMC,GACrB,GAAIC,GAAIF,EAAKF,SACTK,EAAIF,EAAMH,QACd,IAAII,IAAMC,EAAG,CACX,GAAID,EAAIC,GAAKD,QAAW,GAAG,MAAO,EAClC,IAAQC,EAAJD,GAASC,QAAW,GAAG,OAAQ,EAErC,MAAOH,GAAK5H,MAAQ6H,EAAM7H,QACxB,SAIN,IAAIgI,GAAQ,SAASC,GACnB,MAAO,UAASrI,EAAKC,EAAUM,GAC7B,GAAIiE,KAMJ,OALAvE,GAAWc,EAAGd,EAAUM,GACxBG,EAAEkE,KAAK5E,EAAK,SAASqD,EAAOjD,GAC1B,GAAIkE,GAAMrE,EAASoD,EAAOjD,EAAOJ,EACjCqI,GAAS7D,EAAQnB,EAAOiB,KAEnBE,GAMX9D,GAAE4H,QAAUF,EAAM,SAAS5D,EAAQnB,EAAOiB,GACpC5D,EAAEe,IAAI+C,EAAQF,GAAME,EAAOF,GAAK3C,KAAK0B,GAAamB,EAAOF,IAAQjB,KAKvE3C,EAAE6H,QAAUH,EAAM,SAAS5D,EAAQnB,EAAOiB,GACxCE,EAAOF,GAAOjB,IAMhB3C,EAAE8H,QAAUJ,EAAM,SAAS5D,EAAQnB,EAAOiB,GACpC5D,EAAEe,IAAI+C,EAAQF,GAAME,EAAOF,KAAaE,EAAOF,GAAO,IAI5D5D,EAAE+H,QAAU,SAASzI,GACnB,MAAKA,GACDU,EAAE8B,QAAQxC,GAAaoC,EAAMkB,KAAKtD,GAClCS,EAAYT,GAAaU,EAAEoE,IAAI9E,EAAKU,EAAEiD,UACnCjD,EAAE6F,OAAOvG,OAIlBU,EAAEgI,KAAO,SAAS1I,GAChB,MAAW,OAAPA,EAAoB,EACjBS,EAAYT,GAAOA,EAAIK,OAASK,EAAEP,KAAKH,GAAKK,QAKrDK,EAAEiI,UAAY,SAAS3I,EAAKc,EAAWP,GACrCO,EAAYC,EAAGD,EAAWP,EAC1B,IAAIqI,MAAWC,IAIf,OAHAnI,GAAEkE,KAAK5E,EAAK,SAASqD,EAAOiB,EAAKtE,IAC9Bc,EAAUuC,EAAOiB,EAAKtE,GAAO4I,EAAOC,GAAMlH,KAAK0B,MAE1CuF,EAAMC,IAShBnI,EAAEoI,MAAQpI,EAAEqI,KAAOrI,EAAEsI,KAAO,SAASnI,EAAO8G,EAAGC,GAC7C,MAAa,OAAT/G,MAA2B,GACtB,MAAL8G,GAAaC,EAAc/G,EAAM,GAC9BH,EAAEuI,QAAQpI,EAAOA,EAAMR,OAASsH,IAMzCjH,EAAEuI,QAAU,SAASpI,EAAO8G,EAAGC,GAC7B,MAAOxF,GAAMkB,KAAKzC,EAAO,EAAG6D,KAAKuC,IAAI,EAAGpG,EAAMR,QAAe,MAALsH,GAAaC,EAAQ,EAAID,MAKnFjH,EAAEwI,KAAO,SAASrI,EAAO8G,EAAGC,GAC1B,MAAa,OAAT/G,MAA2B,GACtB,MAAL8G,GAAaC,EAAc/G,EAAMA,EAAMR,OAAS,GAC7CK,EAAEyI,KAAKtI,EAAO6D,KAAKuC,IAAI,EAAGpG,EAAMR,OAASsH,KAMlDjH,EAAEyI,KAAOzI,EAAE0I,KAAO1I,EAAE2I,KAAO,SAASxI,EAAO8G,EAAGC,GAC5C,MAAOxF,GAAMkB,KAAKzC,EAAY,MAAL8G,GAAaC,EAAQ,EAAID,IAIpDjH,EAAE4I,QAAU,SAASzI,GACnB,MAAOH,GAAEgF,OAAO7E,EAAOH,EAAEiD,UAI3B,IAAI4F,GAAU,SAASC,EAAOC,EAASC,EAAQC,GAE7C,IAAK,GADDC,MAAaC,EAAM,EACdxF,EAAIsF,GAAc,EAAGtJ,EAASmJ,GAASA,EAAMnJ,OAAYA,EAAJgE,EAAYA,IAAK,CAC7E,GAAIhB,GAAQmG,EAAMnF,EAClB,IAAI5D,EAAY4C,KAAW3C,EAAE8B,QAAQa,IAAU3C,EAAEoJ,YAAYzG,IAAS,CAE/DoG,IAASpG,EAAQkG,EAAQlG,EAAOoG,EAASC,GAC9C,IAAIK,GAAI,EAAGC,EAAM3G,EAAMhD,MAEvB,KADAuJ,EAAOvJ,QAAU2J,EACNA,EAAJD,GACLH,EAAOC,KAASxG,EAAM0G,SAEdL,KACVE,EAAOC,KAASxG,GAGpB,MAAOuG,GAITlJ,GAAE6I,QAAU,SAAS1I,EAAO4I,GAC1B,MAAOF,GAAQ1I,EAAO4I,GAAS,IAIjC/I,EAAEuJ,QAAU,SAASpJ,GACnB,MAAOH,GAAEwJ,WAAWrJ,EAAOuB,EAAMkB,KAAK3C,UAAW,KAMnDD,EAAEyJ,KAAOzJ,EAAE0J,OAAS,SAASvJ,EAAOwJ,EAAUpK,EAAUM,GACtD,GAAa,MAATM,EAAe,QACdH,GAAE4J,UAAUD,KACf9J,EAAUN,EACVA,EAAWoK,EACXA,GAAW,GAEG,MAAZpK,IAAkBA,EAAWc,EAAGd,EAAUM,GAG9C,KAAK,GAFDiE,MACA+F,KACKlG,EAAI,EAAGhE,EAASQ,EAAMR,OAAYA,EAAJgE,EAAYA,IAAK,CACtD,GAAIhB,GAAQxC,EAAMwD,GACd6C,EAAWjH,EAAWA,EAASoD,EAAOgB,EAAGxD,GAASwC,CAClDgH,IACGhG,GAAKkG,IAASrD,GAAU1C,EAAO7C,KAAK0B,GACzCkH,EAAOrD,GACEjH,EACJS,EAAEgB,SAAS6I,EAAMrD,KACpBqD,EAAK5I,KAAKuF,GACV1C,EAAO7C,KAAK0B,IAEJ3C,EAAEgB,SAAS8C,EAAQnB,IAC7BmB,EAAO7C,KAAK0B,GAGhB,MAAOmB,IAKT9D,EAAE8J,MAAQ,WACR,MAAO9J,GAAEyJ,KAAKZ,EAAQ5I,WAAW,GAAM,KAKzCD,EAAE+J,aAAe,SAAS5J,GACxB,GAAa,MAATA,EAAe,QAGnB,KAAK,GAFD2D,MACAkG,EAAa/J,UAAUN,OAClBgE,EAAI,EAAGhE,EAASQ,EAAMR,OAAYA,EAAJgE,EAAYA,IAAK,CACtD,GAAIsG,GAAO9J,EAAMwD,EACjB,KAAI3D,EAAEgB,SAAS8C,EAAQmG,GAAvB,CACA,IAAK,GAAIZ,GAAI,EAAOW,EAAJX,GACTrJ,EAAEgB,SAASf,UAAUoJ,GAAIY,GADAZ,KAG5BA,IAAMW,GAAYlG,EAAO7C,KAAKgJ,IAEpC,MAAOnG,IAKT9D,EAAEwJ,WAAa,SAASrJ,GACtB,GAAIsI,GAAOI,EAAQ5I,WAAW,GAAM,EAAM,EAC1C,OAAOD,GAAEgF,OAAO7E,EAAO,SAASwC,GAC9B,OAAQ3C,EAAEgB,SAASyH,EAAM9F,MAM7B3C,EAAEkK,IAAM,WACN,MAAOlK,GAAEmK,MAAMlK,YAKjBD,EAAEmK,MAAQ,SAAShK,GAIjB,IAAK,GAHDR,GAASQ,GAASH,EAAEuG,IAAIpG,EAAO,UAAUR,QAAU,EACnDmE,EAASxC,MAAM3B,GAEVD,EAAQ,EAAWC,EAARD,EAAgBA,IAClCoE,EAAOpE,GAASM,EAAEmG,MAAMhG,EAAOT,EAEjC,OAAOoE,IAMT9D,EAAEoK,OAAS,SAASlF,EAAMW,GAExB,IAAK,GADD/B,MACKH,EAAI,EAAGhE,EAASuF,GAAQA,EAAKvF,OAAYA,EAAJgE,EAAYA,IACpDkC,EACF/B,EAAOoB,EAAKvB,IAAMkC,EAAOlC,GAEzBG,EAAOoB,EAAKvB,GAAG,IAAMuB,EAAKvB,GAAG,EAGjC,OAAOG,IAOT9D,EAAE8F,QAAU,SAAS3F,EAAO8J,EAAMN,GAChC,GAAIhG,GAAI,EAAGhE,EAASQ,GAASA,EAAMR,MACnC,IAAuB,gBAAZgK,GACThG,EAAe,EAAXgG,EAAe3F,KAAKuC,IAAI,EAAG5G,EAASgK,GAAYA,MAC/C,IAAIA,GAAYhK,EAErB,MADAgE,GAAI3D,EAAEqK,YAAYlK,EAAO8J,GAClB9J,EAAMwD,KAAOsG,EAAOtG,GAAK,CAElC,IAAIsG,IAASA,EACX,MAAOjK,GAAE8E,UAAUpD,EAAMkB,KAAKzC,EAAOwD,GAAI3D,EAAEsK,MAE7C,MAAW3K,EAAJgE,EAAYA,IAAK,GAAIxD,EAAMwD,KAAOsG,EAAM,MAAOtG,EACtD,QAAQ,GAGV3D,EAAEuK,YAAc,SAASpK,EAAO8J,EAAMO,GACpC,GAAIrB,GAAMhJ,EAAQA,EAAMR,OAAS,CAIjC,IAHmB,gBAAR6K,KACTrB,EAAa,EAAPqB,EAAWrB,EAAMqB,EAAO,EAAIxG,KAAK0C,IAAIyC,EAAKqB,EAAO,IAErDP,IAASA,EACX,MAAOjK,GAAEyK,cAAc/I,EAAMkB,KAAKzC,EAAO,EAAGgJ,GAAMnJ,EAAEsK,MAEtD,QAASnB,GAAO,GAAG,GAAIhJ,EAAMgJ,KAASc,EAAM,MAAOd,EACnD,QAAQ,GAiBVnJ,EAAE8E,UAAY5E,EAAkB,GAEhCF,EAAEyK,cAAgBvK,GAAmB,GAIrCF,EAAEqK,YAAc,SAASlK,EAAOb,EAAKC,EAAUM,GAC7CN,EAAWc,EAAGd,EAAUM,EAAS,EAGjC,KAFA,GAAI8C,GAAQpD,EAASD,GACjBoL,EAAM,EAAGC,EAAOxK,EAAMR,OACbgL,EAAND,GAAY,CACjB,GAAIE,GAAM5G,KAAK6G,OAAOH,EAAMC,GAAQ,EAChCpL,GAASY,EAAMyK,IAAQjI,EAAO+H,EAAME,EAAM,EAAQD,EAAOC,EAE/D,MAAOF,IAMT1K,EAAE8K,MAAQ,SAASC,EAAOC,EAAMC,GAC1BhL,UAAUN,QAAU,IACtBqL,EAAOD,GAAS,EAChBA,EAAQ,GAEVE,EAAOA,GAAQ,CAKf,KAAK,GAHDtL,GAASqE,KAAKuC,IAAIvC,KAAKkH,MAAMF,EAAOD,GAASE,GAAO,GACpDH,EAAQxJ,MAAM3B,GAETwJ,EAAM,EAASxJ,EAANwJ,EAAcA,IAAO4B,GAASE,EAC9CH,EAAM3B,GAAO4B,CAGf,OAAOD,GAQT,IAAIK,GAAe,SAASC,EAAYC,EAAWxL,EAASyL,EAAgBrF,GAC1E,KAAMqF,YAA0BD,IAAY,MAAOD,GAAWpI,MAAMnD,EAASoG,EAC7E,IAAIsF,GAAO1H,EAAWuH,EAAWxK,WAC7BkD,EAASsH,EAAWpI,MAAMuI,EAAMtF,EACpC,OAAIjG,GAAEkD,SAASY,GAAgBA,EACxByH,EAMTvL,GAAEiC,KAAO,SAASQ,EAAM5C,GACtB,GAAImC,GAAcS,EAAKR,OAASD,EAAY,MAAOA,GAAWgB,MAAMP,EAAMf,EAAMkB,KAAK3C,UAAW,GAChG,KAAKD,EAAEW,WAAW8B,GAAO,KAAM,IAAI+I,WAAU,oCAC7C,IAAIvF,GAAOvE,EAAMkB,KAAK3C,UAAW,GAC7BwL,EAAQ,WACV,MAAON,GAAa1I,EAAMgJ,EAAO5L,EAASsB,KAAM8E,EAAKyF,OAAOhK,EAAMkB,KAAK3C,aAEzE,OAAOwL,IAMTzL,EAAE2L,QAAU,SAASlJ,GACnB,GAAImJ,GAAYlK,EAAMkB,KAAK3C,UAAW,GAClCwL,EAAQ,WAGV,IAAK,GAFDI,GAAW,EAAGlM,EAASiM,EAAUjM,OACjCsG,EAAO3E,MAAM3B,GACRgE,EAAI,EAAOhE,EAAJgE,EAAYA,IAC1BsC,EAAKtC,GAAKiI,EAAUjI,KAAO3D,EAAIC,UAAU4L,KAAcD,EAAUjI,EAEnE,MAAOkI,EAAW5L,UAAUN,QAAQsG,EAAKhF,KAAKhB,UAAU4L,KACxD,OAAOV,GAAa1I,EAAMgJ,EAAOtK,KAAMA,KAAM8E,GAE/C,OAAOwF,IAMTzL,EAAE8L,QAAU,SAASxM,GACnB,GAAIqE,GAA8BC,EAA3BjE,EAASM,UAAUN,MAC1B,IAAc,GAAVA,EAAa,KAAM,IAAIoM,OAAM,wCACjC,KAAKpI,EAAI,EAAOhE,EAAJgE,EAAYA,IACtBC,EAAM3D,UAAU0D,GAChBrE,EAAIsE,GAAO5D,EAAEiC,KAAK3C,EAAIsE,GAAMtE,EAE9B,OAAOA,IAITU,EAAEgM,QAAU,SAASvJ,EAAMwJ,GACzB,GAAID,GAAU,SAASpI,GACrB,GAAIsI,GAAQF,EAAQE,MAChBC,EAAU,IAAMF,EAASA,EAAOjJ,MAAM7B,KAAMlB,WAAa2D,EAE7D,OADK5D,GAAEe,IAAImL,EAAOC,KAAUD,EAAMC,GAAW1J,EAAKO,MAAM7B,KAAMlB,YACvDiM,EAAMC,GAGf,OADAH,GAAQE,SACDF,GAKThM,EAAEoM,MAAQ,SAAS3J,EAAM4J,GACvB,GAAIpG,GAAOvE,EAAMkB,KAAK3C,UAAW,EACjC,OAAOqM,YAAW,WAChB,MAAO7J,GAAKO,MAAM,KAAMiD,IACvBoG,IAKLrM,EAAEuM,MAAQvM,EAAE2L,QAAQ3L,EAAEoM,MAAOpM,EAAG,GAOhCA,EAAEwM,SAAW,SAAS/J,EAAM4J,EAAMI,GAChC,GAAI5M,GAASoG,EAAMnC,EACf4I,EAAU,KACVC,EAAW,CACVF,KAASA,KACd,IAAIG,GAAQ,WACVD,EAAWF,EAAQI,WAAY,EAAQ,EAAI7M,EAAE8M,MAC7CJ,EAAU,KACV5I,EAASrB,EAAKO,MAAMnD,EAASoG,GACxByG,IAAS7M,EAAUoG,EAAO,MAEjC,OAAO,YACL,GAAI6G,GAAM9M,EAAE8M,KACPH,IAAYF,EAAQI,WAAY,IAAOF,EAAWG,EACvD,IAAIC,GAAYV,GAAQS,EAAMH,EAc9B,OAbA9M,GAAUsB,KACV8E,EAAOhG,UACU,GAAb8M,GAAkBA,EAAYV,GAC5BK,IACFM,aAAaN,GACbA,EAAU,MAEZC,EAAWG,EACXhJ,EAASrB,EAAKO,MAAMnD,EAASoG,GACxByG,IAAS7M,EAAUoG,EAAO,OACrByG,GAAWD,EAAQQ,YAAa,IAC1CP,EAAUJ,WAAWM,EAAOG,IAEvBjJ,IAQX9D,EAAEkN,SAAW,SAASzK,EAAM4J,EAAMc,GAChC,GAAIT,GAASzG,EAAMpG,EAASuN,EAAWtJ,EAEnC8I,EAAQ,WACV,GAAIpE,GAAOxI,EAAE8M,MAAQM,CAEVf,GAAP7D,GAAeA,GAAQ,EACzBkE,EAAUJ,WAAWM,EAAOP,EAAO7D,IAEnCkE,EAAU,KACLS,IACHrJ,EAASrB,EAAKO,MAAMnD,EAASoG,GACxByG,IAAS7M,EAAUoG,EAAO,QAKrC,OAAO,YACLpG,EAAUsB,KACV8E,EAAOhG,UACPmN,EAAYpN,EAAE8M,KACd,IAAIO,GAAUF,IAAcT,CAO5B,OANKA,KAASA,EAAUJ,WAAWM,EAAOP,IACtCgB,IACFvJ,EAASrB,EAAKO,MAAMnD,EAASoG,GAC7BpG,EAAUoG,EAAO,MAGZnC,IAOX9D,EAAEsN,KAAO,SAAS7K,EAAM8K,GACtB,MAAOvN,GAAE2L,QAAQ4B,EAAS9K,IAI5BzC,EAAEoF,OAAS,SAAShF,GAClB,MAAO,YACL,OAAQA,EAAU4C,MAAM7B,KAAMlB,aAMlCD,EAAEwN,QAAU,WACV,GAAIvH,GAAOhG,UACP8K,EAAQ9E,EAAKtG,OAAS,CAC1B,OAAO,YAGL,IAFA,GAAIgE,GAAIoH,EACJjH,EAASmC,EAAK8E,GAAO/H,MAAM7B,KAAMlB,WAC9B0D,KAAKG,EAASmC,EAAKtC,GAAGf,KAAKzB,KAAM2C,EACxC,OAAOA,KAKX9D,EAAEyN,MAAQ,SAASC,EAAOjL,GACxB,MAAO,YACL,QAAMiL,EAAQ,EACLjL,EAAKO,MAAM7B,KAAMlB,WAD1B,SAOJD,EAAE2N,OAAS,SAASD,EAAOjL,GACzB,GAAIjD,EACJ,OAAO,YAKL,QAJMkO,EAAQ,IACZlO,EAAOiD,EAAKO,MAAM7B,KAAMlB,YAEb,GAATyN,IAAYjL,EAAO,MAChBjD,IAMXQ,EAAE4N,KAAO5N,EAAE2L,QAAQ3L,EAAE2N,OAAQ,EAM7B,IAAIE,KAAelM,SAAU,MAAMmM,qBAAqB,YACpDtN,GAAsB,UAAW,gBAAiB,WAClC,uBAAwB,iBAAkB,iBAqB9DR,GAAEP,KAAO,SAASH,GAChB,IAAKU,EAAEkD,SAAS5D,GAAM,QACtB,IAAIyC,EAAY,MAAOA,GAAWzC,EAClC,IAAIG,KACJ,KAAK,GAAImE,KAAOtE,GAASU,EAAEe,IAAIzB,EAAKsE,IAAMnE,EAAKwB,KAAK2C,EAGpD,OADIiK,IAAYvN,EAAoBhB,EAAKG,GAClCA,GAITO,EAAE+N,QAAU,SAASzO,GACnB,IAAKU,EAAEkD,SAAS5D,GAAM,QACtB,IAAIG,KACJ,KAAK,GAAImE,KAAOtE,GAAKG,EAAKwB,KAAK2C,EAG/B,OADIiK,IAAYvN,EAAoBhB,EAAKG,GAClCA,GAITO,EAAE6F,OAAS,SAASvG,GAIlB,IAAK,GAHDG,GAAOO,EAAEP,KAAKH,GACdK,EAASF,EAAKE,OACdkG,EAASvE,MAAM3B,GACVgE,EAAI,EAAOhE,EAAJgE,EAAYA,IAC1BkC,EAAOlC,GAAKrE,EAAIG,EAAKkE,GAEvB,OAAOkC,IAKT7F,EAAEgO,UAAY,SAAS1O,EAAKC,EAAUM,GACpCN,EAAWc,EAAGd,EAAUM,EAKtB,KAAK,GADDD,GAHFH,EAAQO,EAAEP,KAAKH,GACbK,EAASF,EAAKE,OACd2E,KAEK5E,EAAQ,EAAWC,EAARD,EAAgBA,IAClCE,EAAaH,EAAKC,GAClB4E,EAAQ1E,GAAcL,EAASD,EAAIM,GAAaA,EAAYN,EAE9D,OAAOgF,IAIXtE,EAAEiO,MAAQ,SAAS3O,GAIjB,IAAK,GAHDG,GAAOO,EAAEP,KAAKH,GACdK,EAASF,EAAKE,OACdsO,EAAQ3M,MAAM3B,GACTgE,EAAI,EAAOhE,EAAJgE,EAAYA,IAC1BsK,EAAMtK,IAAMlE,EAAKkE,GAAIrE,EAAIG,EAAKkE,IAEhC,OAAOsK,IAITjO,EAAEkO,OAAS,SAAS5O,GAGlB,IAAK,GAFDwE,MACArE,EAAOO,EAAEP,KAAKH,GACTqE,EAAI,EAAGhE,EAASF,EAAKE,OAAYA,EAAJgE,EAAYA,IAChDG,EAAOxE,EAAIG,EAAKkE,KAAOlE,EAAKkE,EAE9B,OAAOG,IAKT9D,EAAEmO,UAAYnO,EAAEoO,QAAU,SAAS9O,GACjC,GAAI+O,KACJ,KAAK,GAAIzK,KAAOtE,GACVU,EAAEW,WAAWrB,EAAIsE,KAAOyK,EAAMpN,KAAK2C,EAEzC,OAAOyK,GAAMhH,QAIfrH,EAAEsO,OAAShL,EAAetD,EAAE+N,SAI5B/N,EAAEuO,UAAYvO,EAAEwO,OAASlL,EAAetD,EAAEP,MAG1CO,EAAE+E,QAAU,SAASzF,EAAKc,EAAWP,GACnCO,EAAYC,EAAGD,EAAWP,EAE1B,KAAK,GADmB+D,GAApBnE,EAAOO,EAAEP,KAAKH,GACTqE,EAAI,EAAGhE,EAASF,EAAKE,OAAYA,EAAJgE,EAAYA,IAEhD,GADAC,EAAMnE,EAAKkE,GACPvD,EAAUd,EAAIsE,GAAMA,EAAKtE,GAAM,MAAOsE,IAK9C5D,EAAEyO,KAAO,SAASrE,EAAQsE,EAAW7O,GACnC,GAA+BN,GAAUE,EAArCqE,KAAaxE,EAAM8K,CACvB,IAAW,MAAP9K,EAAa,MAAOwE,EACpB9D,GAAEW,WAAW+N,IACfjP,EAAOO,EAAE+N,QAAQzO,GACjBC,EAAWO,EAAW4O,EAAW7O,KAEjCJ,EAAOoJ,EAAQ5I,WAAW,GAAO,EAAO,GACxCV,EAAW,SAASoD,EAAOiB,EAAKtE,GAAO,MAAOsE,KAAOtE,IACrDA,EAAMiC,OAAOjC,GAEf,KAAK,GAAIqE,GAAI,EAAGhE,EAASF,EAAKE,OAAYA,EAAJgE,EAAYA,IAAK,CACrD,GAAIC,GAAMnE,EAAKkE,GACXhB,EAAQrD,EAAIsE,EACZrE,GAASoD,EAAOiB,EAAKtE,KAAMwE,EAAOF,GAAOjB,GAE/C,MAAOmB,IAIT9D,EAAE2O,KAAO,SAASrP,EAAKC,EAAUM,GAC/B,GAAIG,EAAEW,WAAWpB,GACfA,EAAWS,EAAEoF,OAAO7F,OACf,CACL,GAAIE,GAAOO,EAAEoE,IAAIyE,EAAQ5I,WAAW,GAAO,EAAO,GAAI2O,OACtDrP,GAAW,SAASoD,EAAOiB,GACzB,OAAQ5D,EAAEgB,SAASvB,EAAMmE,IAG7B,MAAO5D,GAAEyO,KAAKnP,EAAKC,EAAUM,IAI/BG,EAAE6O,SAAWvL,EAAetD,EAAE+N,SAAS,GAGvC/N,EAAE8O,MAAQ,SAASxP,GACjB,MAAKU,GAAEkD,SAAS5D,GACTU,EAAE8B,QAAQxC,GAAOA,EAAIoC,QAAU1B,EAAEsO,UAAWhP,GADtBA,GAO/BU,EAAE+O,IAAM,SAASzP,EAAK0P,GAEpB,MADAA,GAAY1P,GACLA,GAITU,EAAEiP,QAAU,SAAS7E,EAAQ/D,GAC3B,GAAI5G,GAAOO,EAAEP,KAAK4G,GAAQ1G,EAASF,EAAKE,MACxC,IAAc,MAAVyK,EAAgB,OAAQzK,CAE5B,KAAK,GADDL,GAAMiC,OAAO6I,GACRzG,EAAI,EAAOhE,EAAJgE,EAAYA,IAAK,CAC/B,GAAIC,GAAMnE,EAAKkE,EACf,IAAI0C,EAAMzC,KAAStE,EAAIsE,MAAUA,IAAOtE,IAAM,OAAO,EAEvD,OAAO,EAKT,IAAI4P,GAAK,SAAS1H,EAAGC,EAAG0H,EAAQC,GAG9B,GAAI5H,IAAMC,EAAG,MAAa,KAAND,GAAW,EAAIA,IAAM,EAAIC,CAE7C,IAAS,MAALD,GAAkB,MAALC,EAAW,MAAOD,KAAMC,CAErCD,aAAaxH,KAAGwH,EAAIA,EAAEnF,UACtBoF,YAAazH,KAAGyH,EAAIA,EAAEpF,SAE1B,IAAIgN,GAAY1N,EAASiB,KAAK4E,EAC9B,IAAI6H,IAAc1N,EAASiB,KAAK6E,GAAI,OAAO,CAC3C,QAAQ4H,GAEN,IAAK,kBAEL,IAAK,kBAGH,MAAO,GAAK7H,GAAM,GAAKC,CACzB,KAAK,kBAGH,OAAKD,KAAOA,GAAWC,KAAOA,EAEhB,KAAND,EAAU,GAAKA,IAAM,EAAIC,GAAKD,KAAOC,CAC/C,KAAK,gBACL,IAAK,mBAIH,OAAQD,KAAOC,EAGnB,GAAI6H,GAA0B,mBAAdD,CAChB,KAAKC,EAAW,CACd,GAAgB,gBAAL9H,IAA6B,gBAALC,GAAe,OAAO,CAIzD,IAAI8H,GAAQ/H,EAAE/G,YAAa+O,EAAQ/H,EAAEhH,WACrC,IAAI8O,IAAUC,KAAWxP,EAAEW,WAAW4O,IAAUA,YAAiBA,IACxCvP,EAAEW,WAAW6O,IAAUA,YAAiBA,KACzC,eAAiBhI,IAAK,eAAiBC,GAC7D,OAAO,EAQX0H,EAASA,MACTC,EAASA,KAET,KADA,GAAIzP,GAASwP,EAAOxP,OACbA,KAGL,GAAIwP,EAAOxP,KAAY6H,EAAG,MAAO4H,GAAOzP,KAAY8H,CAQtD,IAJA0H,EAAOlO,KAAKuG,GACZ4H,EAAOnO,KAAKwG,GAGR6H,EAAW,CAGb,GADA3P,EAAS6H,EAAE7H,OACPA,IAAW8H,EAAE9H,OAAQ,OAAO,CAEhC,MAAOA,KACL,IAAKuP,EAAG1H,EAAE7H,GAAS8H,EAAE9H,GAASwP,EAAQC,GAAS,OAAO,MAEnD,CAEL,GAAsBxL,GAAlBnE,EAAOO,EAAEP,KAAK+H,EAGlB,IAFA7H,EAASF,EAAKE,OAEVK,EAAEP,KAAKgI,GAAG9H,SAAWA,EAAQ,OAAO,CACxC,MAAOA,KAGL,GADAiE,EAAMnE,EAAKE,IACLK,EAAEe,IAAI0G,EAAG7D,KAAQsL,EAAG1H,EAAE5D,GAAM6D,EAAE7D,GAAMuL,EAAQC,GAAU,OAAO,EAMvE,MAFAD,GAAOM,MACPL,EAAOK,OACA,EAITzP,GAAE0P,QAAU,SAASlI,EAAGC,GACtB,MAAOyH,GAAG1H,EAAGC,IAKfzH,EAAE2P,QAAU,SAASrQ,GACnB,MAAW,OAAPA,GAAoB,EACpBS,EAAYT,KAASU,EAAE8B,QAAQxC,IAAQU,EAAE4P,SAAStQ,IAAQU,EAAEoJ,YAAY9J,IAA6B,IAAfA,EAAIK,OAChE,IAAvBK,EAAEP,KAAKH,GAAKK,QAIrBK,EAAE6P,UAAY,SAASvQ,GACrB,SAAUA,GAAwB,IAAjBA,EAAIwQ,WAKvB9P,EAAE8B,QAAUD,GAAiB,SAASvC,GACpC,MAA8B,mBAAvBqC,EAASiB,KAAKtD,IAIvBU,EAAEkD,SAAW,SAAS5D,GACpB,GAAIyQ,SAAczQ,EAClB,OAAgB,aAATyQ,GAAgC,WAATA,KAAuBzQ,GAIvDU,EAAEkE,MAAM,YAAa,WAAY,SAAU,SAAU,OAAQ,SAAU,SAAU,SAAS8L,GACxFhQ,EAAE,KAAOgQ,GAAQ,SAAS1Q,GACxB,MAAOqC,GAASiB,KAAKtD,KAAS,WAAa0Q,EAAO,OAMjDhQ,EAAEoJ,YAAYnJ,aACjBD,EAAEoJ,YAAc,SAAS9J,GACvB,MAAOU,GAAEe,IAAIzB,EAAK,YAMJ,kBAAP,KAAyC,gBAAb2Q,aACrCjQ,EAAEW,WAAa,SAASrB,GACtB,MAAqB,kBAAPA,KAAqB,IAKvCU,EAAEkQ,SAAW,SAAS5Q,GACpB,MAAO4Q,UAAS5Q,KAASgL,MAAM6F,WAAW7Q,KAI5CU,EAAEsK,MAAQ,SAAShL,GACjB,MAAOU,GAAEoQ,SAAS9Q,IAAQA,KAASA,GAIrCU,EAAE4J,UAAY,SAAStK,GACrB,MAAOA,MAAQ,GAAQA,KAAQ,GAAgC,qBAAvBqC,EAASiB,KAAKtD,IAIxDU,EAAEqQ,OAAS,SAAS/Q,GAClB,MAAe,QAARA,GAITU,EAAEsQ,YAAc,SAAShR,GACvB,MAAOA,SAAa,IAKtBU,EAAEe,IAAM,SAASzB,EAAKsE,GACpB,MAAc,OAAPtE,GAAesC,EAAegB,KAAKtD,EAAKsE,IAQjD5D,EAAEuQ,WAAa,WAEb,MADArP,GAAKlB,EAAIoB,EACFD,MAITnB,EAAEiD,SAAW,SAASN,GACpB,MAAOA,IAIT3C,EAAEwQ,SAAW,SAAS7N,GACpB,MAAO,YACL,MAAOA,KAIX3C,EAAEyQ,KAAO,aAETzQ,EAAEoD,SAAW,SAASQ,GACpB,MAAO,UAAStE,GACd,MAAc,OAAPA,MAAmB,GAAIA,EAAIsE,KAKtC5D,EAAE0Q,WAAa,SAASpR,GACtB,MAAc,OAAPA,EAAc,aAAe,SAASsE,GAC3C,MAAOtE,GAAIsE,KAMf5D,EAAEmD,QAAUnD,EAAE2Q,QAAU,SAAStK,GAE/B,MADAA,GAAQrG,EAAEuO,aAAclI,GACjB,SAAS/G,GACd,MAAOU,GAAEiP,QAAQ3P,EAAK+G,KAK1BrG,EAAE0N,MAAQ,SAASzG,EAAG1H,EAAUM,GAC9B,GAAI+Q,GAAQtP,MAAM0C,KAAKuC,IAAI,EAAGU,GAC9B1H,GAAWO,EAAWP,EAAUM,EAAS,EACzC,KAAK,GAAI8D,GAAI,EAAOsD,EAAJtD,EAAOA,IAAKiN,EAAMjN,GAAKpE,EAASoE,EAChD,OAAOiN,IAIT5Q,EAAE+G,OAAS,SAASL,EAAKH,GAKvB,MAJW,OAAPA,IACFA,EAAMG,EACNA,EAAM,GAEDA,EAAM1C,KAAK6G,MAAM7G,KAAK+C,UAAYR,EAAMG,EAAM,KAIvD1G,EAAE8M,IAAM+D,KAAK/D,KAAO,WAClB,OAAO,GAAI+D,OAAOC,UAIpB,IAAIC,IACFC,IAAK,QACLC,IAAK,OACLC,IAAK,OACLC,IAAK,SACLC,IAAK,SACLC,IAAK,UAEHC,EAActR,EAAEkO,OAAO6C,GAGvBQ,EAAgB,SAASnN,GAC3B,GAAIoN,GAAU,SAASC,GACrB,MAAOrN,GAAIqN,IAGThO,EAAS,MAAQzD,EAAEP,KAAK2E,GAAKsN,KAAK,KAAO,IACzCC,EAAaC,OAAOnO,GACpBoO,EAAgBD,OAAOnO,EAAQ,IACnC,OAAO,UAASqO,GAEd,MADAA,GAAmB,MAAVA,EAAiB,GAAK,GAAKA,EAC7BH,EAAWI,KAAKD,GAAUA,EAAOE,QAAQH,EAAeL,GAAWM,GAG9E9R,GAAEiS,OAASV,EAAcR,GACzB/Q,EAAEkS,SAAWX,EAAcD,GAI3BtR,EAAE8D,OAAS,SAASsG,EAAQhH,EAAU+O,GACpC,GAAIxP,GAAkB,MAAVyH,MAAsB,GAAIA,EAAOhH,EAI7C,OAHIT,SAAe,KACjBA,EAAQwP,GAEHnS,EAAEW,WAAWgC,GAASA,EAAMC,KAAKwH,GAAUzH,EAKpD,IAAIyP,GAAY,CAChBpS,GAAEqS,SAAW,SAASC,GACpB,GAAIC,KAAOH,EAAY,EACvB,OAAOE,GAASA,EAASC,EAAKA,GAKhCvS,EAAEwS,kBACAC,SAAc,kBACdC,YAAc,mBACdT,OAAc,mBAMhB,IAAIU,GAAU,OAIVC,GACFxB,IAAU,IACVyB,KAAU,KACVC,KAAU,IACVC,KAAU,IACVC,SAAU,QACVC,SAAU,SAGRzB,EAAU,4BAEV0B,EAAa,SAASzB,GACxB,MAAO,KAAOmB,EAAQnB,GAOxBzR,GAAEmT,SAAW,SAASC,EAAMC,EAAUC,IAC/BD,GAAYC,IAAaD,EAAWC,GACzCD,EAAWrT,EAAE6O,YAAawE,EAAUrT,EAAEwS,iBAGtC,IAAIrP,GAAUyO,SACXyB,EAASpB,QAAUU,GAASlP,QAC5B4P,EAASX,aAAeC,GAASlP,QACjC4P,EAASZ,UAAYE,GAASlP,QAC/BiO,KAAK,KAAO,KAAM,KAGhBhS,EAAQ,EACR+D,EAAS,QACb2P,GAAKpB,QAAQ7O,EAAS,SAASsO,EAAOQ,EAAQS,EAAaD,EAAUc,GAanE,MAZA9P,IAAU2P,EAAK1R,MAAMhC,EAAO6T,GAAQvB,QAAQR,EAAS0B,GACrDxT,EAAQ6T,EAAS9B,EAAM9R,OAEnBsS,EACFxO,GAAU,cAAgBwO,EAAS,iCAC1BS,EACTjP,GAAU,cAAgBiP,EAAc,uBAC/BD,IACThP,GAAU,OAASgP,EAAW,YAIzBhB,IAEThO,GAAU,OAGL4P,EAASG,WAAU/P,EAAS,mBAAqBA,EAAS,OAE/DA,EAAS,2CACP,oDACAA,EAAS,eAEX,KACE,GAAIgQ,GAAS,GAAIhS,UAAS4R,EAASG,UAAY,MAAO,IAAK/P,GAC3D,MAAOiQ,GAEP,KADAA,GAAEjQ,OAASA,EACLiQ,EAGR,GAAIP,GAAW,SAASQ,GACtB,MAAOF,GAAO7Q,KAAKzB,KAAMwS,EAAM3T,IAI7B4T,EAAWP,EAASG,UAAY,KAGpC,OAFAL,GAAS1P,OAAS,YAAcmQ,EAAW,OAASnQ,EAAS,IAEtD0P,GAITnT,EAAE6T,MAAQ,SAASvU,GACjB,GAAIwU,GAAW9T,EAAEV,EAEjB,OADAwU,GAASC,QAAS,EACXD,EAUT,IAAIhQ,GAAS,SAASgQ,EAAUxU,GAC9B,MAAOwU,GAASC,OAAS/T,EAAEV,GAAKuU,QAAUvU,EAI5CU,GAAEgU,MAAQ,SAAS1U,GACjBU,EAAEkE,KAAKlE,EAAEmO,UAAU7O,GAAM,SAAS0Q,GAChC,GAAIvN,GAAOzC,EAAEgQ,GAAQ1Q,EAAI0Q,EACzBhQ,GAAEY,UAAUoP,GAAQ,WAClB,GAAI/J,IAAQ9E,KAAKkB,SAEjB,OADApB,GAAK+B,MAAMiD,EAAMhG,WACV6D,EAAO3C,KAAMsB,EAAKO,MAAMhD,EAAGiG,QAMxCjG,EAAEgU,MAAMhU,GAGRA,EAAEkE,MAAM,MAAO,OAAQ,UAAW,QAAS,OAAQ,SAAU,WAAY,SAAS8L,GAChF,GAAIhK,GAAS3E,EAAW2O,EACxBhQ,GAAEY,UAAUoP,GAAQ,WAClB,GAAI1Q,GAAM6B,KAAKkB,QAGf,OAFA2D,GAAOhD,MAAM1D,EAAKW,WACJ,UAAT+P,GAA6B,WAATA,GAAqC,IAAf1Q,EAAIK,cAAqBL,GAAI,GACrEwE,EAAO3C,KAAM7B,MAKxBU,EAAEkE,MAAM,SAAU,OAAQ,SAAU,SAAS8L,GAC3C,GAAIhK,GAAS3E,EAAW2O,EACxBhQ,GAAEY,UAAUoP,GAAQ,WAClB,MAAOlM,GAAO3C,KAAM6E,EAAOhD,MAAM7B,KAAKkB,SAAUpC,eAKpDD,EAAEY,UAAU+B,MAAQ,WAClB,MAAOxB,MAAKkB,UAKdrC,EAAEY,UAAUqT,QAAUjU,EAAEY,UAAUsT,OAASlU,EAAEY,UAAU+B,MAEvD3C,EAAEY,UAAUe,SAAW,WACrB,MAAO,GAAKR,KAAKkB,UAUG,kBAAX8R,SAAyBA,OAAOC,KACzCD,OAAO,gBAAkB,WACvB,MAAOnU,OAGX4C,KAAKzB"} \ No newline at end of file
diff --git a/catalog-be/src/main/resources/swagger/o2c.html b/catalog-be/src/main/resources/swagger/o2c.html
new file mode 100644
index 0000000000..88e8bf114b
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/o2c.html
@@ -0,0 +1,20 @@
+<script>
+var qp = null;
+if(window.location.hash) {
+ qp = location.hash.substring(1);
+}
+else {
+ qp = location.search.substring(1);
+}
+qp = qp ? JSON.parse('{"' + qp.replace(/&/g, '","').replace(/=/g,'":"') + '"}',
+ function(key, value) {
+ return key===""?value:decodeURIComponent(value) }
+ ):{}
+
+if (window.opener.swaggerUi.tokenUrl)
+ window.opener.processOAuthCode(qp);
+else
+ window.opener.onOAuthComplete(qp);
+
+window.close();
+</script> \ No newline at end of file
diff --git a/catalog-be/src/main/resources/swagger/swagger-ui.js b/catalog-be/src/main/resources/swagger/swagger-ui.js
new file mode 100644
index 0000000000..96b6ed7cb6
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/swagger-ui.js
@@ -0,0 +1,21738 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ * swagger-ui - Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API
+ * @version v2.1.0-M2
+ * @link http://swagger.io
+ * @license Apache 2.0
+ */
+(function(){this["Handlebars"] = this["Handlebars"] || {};
+this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {};
+this["Handlebars"]["templates"]["apikey_button_view"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return "<!--div class='auth_button' id='apikey_button'><img class='auth_icon' alt='apply api key' src='images/apikey.jpeg'></div-->\n<div class='auth_container' id='apikey_container'>\n <div class='key_input_container'>\n <div class='auth_label'>"
+ + escapeExpression(((helper = (helper = helpers.keyName || (depth0 != null ? depth0.keyName : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"keyName","hash":{},"data":data}) : helper)))
+ + "</div>\n <input placeholder=\"api_key\" class=\"auth_input\" id=\"input_apiKey_entry\" name=\"apiKey\" type=\"text\"/>\n <div class='auth_submit'><a class='auth_submit_button' id=\"apply_api_key\" href=\"#\">apply</a></div>\n </div>\n</div>\n\n";
+},"useData":true});
+this["Handlebars"]["templates"]["basic_auth_button_view"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ return "<div class='auth_button' id='basic_auth_button'><img class='auth_icon' src='images/password.jpeg'></div>\n<div class='auth_container' id='basic_auth_container'>\n <div class='key_input_container'>\n <div class=\"auth_label\">Username</div>\n <input placeholder=\"username\" class=\"auth_input\" id=\"input_username\" name=\"username\" type=\"text\"/>\n <div class=\"auth_label\">Password</div>\n <input placeholder=\"password\" class=\"auth_input\" id=\"input_password\" name=\"password\" type=\"password\"/>\n <div class='auth_submit'><a class='auth_submit_button' id=\"apply_basic_auth\" href=\"#\">apply</a></div>\n </div>\n</div>\n\n";
+ },"useData":true});
+this["Handlebars"]["templates"]["content_type"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.produces : depth0), {"name":"each","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"2":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, buffer = " <option value=\"";
+ stack1 = lambda(depth0, depth0);
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\">";
+ stack1 = lambda(depth0, depth0);
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</option>\n";
+},"4":function(depth0,helpers,partials,data) {
+ return " <option value=\"application/json\">application/json</option>\n";
+ },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "<label for=\"contentType\"></label>\n<select name=\"contentType\">\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.produces : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(4, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</select>\n";
+},"useData":true});
+'use strict';
+
+
+$(function() {
+
+ // Helper function for vertically aligning DOM elements
+ // http://www.seodenver.com/simple-vertical-align-plugin-for-jquery/
+ $.fn.vAlign = function() {
+ return this.each(function(){
+ var ah = $(this).height();
+ var ph = $(this).parent().height();
+ var mh = (ph - ah) / 2;
+ $(this).css('margin-top', mh);
+ });
+ };
+
+ $.fn.stretchFormtasticInputWidthToParent = function() {
+ return this.each(function(){
+ var p_width = $(this).closest("form").innerWidth();
+ var p_padding = parseInt($(this).closest("form").css('padding-left') ,10) + parseInt($(this).closest('form').css('padding-right'), 10);
+ var this_padding = parseInt($(this).css('padding-left'), 10) + parseInt($(this).css('padding-right'), 10);
+ $(this).css('width', p_width - p_padding - this_padding);
+ });
+ };
+
+ $('form.formtastic li.string input, form.formtastic textarea').stretchFormtasticInputWidthToParent();
+
+ // Vertically center these paragraphs
+ // Parent may need a min-height for this to work..
+ $('ul.downplayed li div.content p').vAlign();
+
+ // When a sandbox form is submitted..
+ $("form.sandbox").submit(function(){
+
+ var error_free = true;
+
+ // Cycle through the forms required inputs
+ $(this).find("input.required").each(function() {
+
+ // Remove any existing error styles from the input
+ $(this).removeClass('error');
+
+ // Tack the error style on if the input is empty..
+ if ($(this).val() === '') {
+ $(this).addClass('error');
+ $(this).wiggle();
+ error_free = false;
+ }
+
+ });
+
+ return error_free;
+ });
+
+});
+
+function clippyCopiedCallback() {
+ $('#api_key_copied').fadeIn().delay(1000).fadeOut();
+
+ // var b = $("#clippy_tooltip_" + a);
+ // b.length != 0 && (b.attr("title", "copied!").trigger("tipsy.reload"), setTimeout(function() {
+ // b.attr("title", "copy to clipboard")
+ // },
+ // 500))
+}
+
+// Logging function that accounts for browsers that don't have window.console
+function log(){
+ log.history = log.history || [];
+ log.history.push(arguments);
+ if(this.console){
+ console.log( Array.prototype.slice.call(arguments)[0] );
+ }
+}
+
+// Handle browsers that do console incorrectly (IE9 and below, see http://stackoverflow.com/a/5539378/7913)
+if (Function.prototype.bind && console && typeof console.log === "object") {
+ [
+ "log","info","warn","error","assert","dir","clear","profile","profileEnd"
+ ].forEach(function (method) {
+ console[method] = this.bind(console[method], console);
+ }, Function.prototype.call);
+}
+
+window.Docs = {
+
+ shebang: function() {
+
+ // If shebang has an operation nickname in it..
+ // e.g. /docs/#!/words/get_search
+ var fragments = $.param.fragment().split('/');
+ fragments.shift(); // get rid of the bang
+
+ switch (fragments.length) {
+ case 1:
+ // Expand all operations for the resource and scroll to it
+ var dom_id = 'resource_' + fragments[0];
+
+ Docs.expandEndpointListForResource(fragments[0]);
+ $("#"+dom_id).slideto({highlight: false});
+ break;
+ case 2:
+ // Refer to the endpoint DOM element, e.g. #words_get_search
+
+ // Expand Resource
+ Docs.expandEndpointListForResource(fragments[0]);
+ $("#"+dom_id).slideto({highlight: false});
+
+ // Expand operation
+ var li_dom_id = fragments.join('_');
+ var li_content_dom_id = li_dom_id + "_content";
+
+
+ Docs.expandOperation($('#'+li_content_dom_id));
+ $('#'+li_dom_id).slideto({highlight: false});
+ break;
+ }
+
+ },
+
+ toggleEndpointListForResource: function(resource) {
+ var elem = $('li#resource_' + Docs.escapeResourceName(resource) + ' ul.endpoints');
+ if (elem.is(':visible')) {
+ Docs.collapseEndpointListForResource(resource);
+ } else {
+ Docs.expandEndpointListForResource(resource);
+ }
+ },
+
+ // Expand resource
+ expandEndpointListForResource: function(resource) {
+ var resource = Docs.escapeResourceName(resource);
+ if (resource == '') {
+ $('.resource ul.endpoints').slideDown();
+ return;
+ }
+
+ $('li#resource_' + resource).addClass('active');
+
+ var elem = $('li#resource_' + resource + ' ul.endpoints');
+ elem.slideDown();
+ },
+
+ // Collapse resource and mark as explicitly closed
+ collapseEndpointListForResource: function(resource) {
+ var resource = Docs.escapeResourceName(resource);
+ if (resource == '') {
+ $('.resource ul.endpoints').slideUp();
+ return;
+ }
+
+ $('li#resource_' + resource).removeClass('active');
+
+ var elem = $('li#resource_' + resource + ' ul.endpoints');
+ elem.slideUp();
+ },
+
+ expandOperationsForResource: function(resource) {
+ // Make sure the resource container is open..
+ Docs.expandEndpointListForResource(resource);
+
+ if (resource == '') {
+ $('.resource ul.endpoints li.operation div.content').slideDown();
+ return;
+ }
+
+ $('li#resource_' + Docs.escapeResourceName(resource) + ' li.operation div.content').each(function() {
+ Docs.expandOperation($(this));
+ });
+ },
+
+ collapseOperationsForResource: function(resource) {
+ // Make sure the resource container is open..
+ Docs.expandEndpointListForResource(resource);
+
+ if (resource == '') {
+ $('.resource ul.endpoints li.operation div.content').slideUp();
+ return;
+ }
+
+ $('li#resource_' + Docs.escapeResourceName(resource) + ' li.operation div.content').each(function() {
+ Docs.collapseOperation($(this));
+ });
+ },
+
+ escapeResourceName: function(resource) {
+ return resource.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g, "\\$&");
+ },
+
+ expandOperation: function(elem) {
+ elem.slideDown();
+ },
+
+ collapseOperation: function(elem) {
+ elem.slideUp();
+ }
+};
+
+'use strict';
+
+Handlebars.registerHelper('sanitize', function(html) {
+ // Strip the script tags from the html, and return it as a Handlebars.SafeString
+ html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
+ return new Handlebars.SafeString(html);
+});
+this["Handlebars"]["templates"]["main"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression, buffer = " <div class=\"info_title\">"
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.title : stack1), depth0))
+ + "</div>\n <div class=\"info_description markdown\">";
+ stack1 = lambda(((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.description : stack1), depth0);
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</div>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.externalDocs : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += " ";
+ stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.termsOfServiceUrl : stack1), {"name":"if","hash":{},"fn":this.program(4, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n ";
+ stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.name : stack1), {"name":"if","hash":{},"fn":this.program(6, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n ";
+ stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.url : stack1), {"name":"if","hash":{},"fn":this.program(8, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n ";
+ stack1 = helpers['if'].call(depth0, ((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.email : stack1), {"name":"if","hash":{},"fn":this.program(10, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n ";
+ stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.license : stack1), {"name":"if","hash":{},"fn":this.program(12, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "\n";
+},"2":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return " <h5>More documentations</h5>\n <p>"
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.externalDocs : depth0)) != null ? stack1.description : stack1), depth0))
+ + "</p>\n <a href=\""
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.externalDocs : depth0)) != null ? stack1.url : stack1), depth0))
+ + "\" target=\"_blank\">"
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.externalDocs : depth0)) != null ? stack1.url : stack1), depth0))
+ + "</a>\n";
+},"4":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return "<div class=\"info_tos\"><a href=\""
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.termsOfServiceUrl : stack1), depth0))
+ + "\">Terms of service</a></div>";
+},"6":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return "<div class='info_name'>Created by "
+ + escapeExpression(lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.name : stack1), depth0))
+ + "</div>";
+},"8":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return "<div class='info_url'>See more at <a href=\""
+ + escapeExpression(lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.url : stack1), depth0))
+ + "\">"
+ + escapeExpression(lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.url : stack1), depth0))
+ + "</a></div>";
+},"10":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return "<div class='info_email'><a href=\"mailto:"
+ + escapeExpression(lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.contact : stack1)) != null ? stack1.email : stack1), depth0))
+ + "?subject="
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.title : stack1), depth0))
+ + "\">Contact the developer</a></div>";
+},"12":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return "<div class='info_license'><a href='"
+ + escapeExpression(lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.license : stack1)) != null ? stack1.url : stack1), depth0))
+ + "'>"
+ + escapeExpression(lambda(((stack1 = ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.license : stack1)) != null ? stack1.name : stack1), depth0))
+ + "</a></div>";
+},"14":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return " , <span style=\"font-variant: small-caps\">api version</span>: "
+ + escapeExpression(lambda(((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.version : stack1), depth0))
+ + "\n ";
+},"16":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <span style=\"float:right\"><a href=\""
+ + escapeExpression(((helper = (helper = helpers.validatorUrl || (depth0 != null ? depth0.validatorUrl : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"validatorUrl","hash":{},"data":data}) : helper)))
+ + "/debug?url="
+ + escapeExpression(((helper = (helper = helpers.url || (depth0 != null ? depth0.url : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"url","hash":{},"data":data}) : helper)))
+ + "\"><img id=\"validator\" src=\""
+ + escapeExpression(((helper = (helper = helpers.validatorUrl || (depth0 != null ? depth0.validatorUrl : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"validatorUrl","hash":{},"data":data}) : helper)))
+ + "?url="
+ + escapeExpression(((helper = (helper = helpers.url || (depth0 != null ? depth0.url : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"url","hash":{},"data":data}) : helper)))
+ + "\"></a>\n </span>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class='info' id='api_info'>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.info : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</div>\n<div class='container' id='resources_container'>\n <ul id='resources'></ul>\n\n <div class=\"footer\">\n <br>\n <br>\n <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: "
+ + escapeExpression(((helper = (helper = helpers.basePath || (depth0 != null ? depth0.basePath : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"basePath","hash":{},"data":data}) : helper)))
+ + "\n";
+ stack1 = helpers['if'].call(depth0, ((stack1 = (depth0 != null ? depth0.info : depth0)) != null ? stack1.version : stack1), {"name":"if","hash":{},"fn":this.program(14, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "]\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.validatorUrl : depth0), {"name":"if","hash":{},"fn":this.program(16, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + " </h4>\n </div>\n</div>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["operation"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ return "deprecated";
+ },"3":function(depth0,helpers,partials,data) {
+ return " <h4>Warning: Deprecated</h4>\n";
+ },"5":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, buffer = " <h4>Implementation Notes</h4>\n <div class=\"markdown\">";
+ stack1 = ((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"description","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</div>\n";
+},"7":function(depth0,helpers,partials,data) {
+ return " <div class=\"auth\">\n <span class=\"api-ic ic-error\"></span>";
+ },"9":function(depth0,helpers,partials,data) {
+ var stack1, buffer = " <div id=\"api_information_panel\" style=\"top: 526px; left: 776px; display: none;\">\n";
+ stack1 = helpers.each.call(depth0, depth0, {"name":"each","hash":{},"fn":this.program(10, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + " </div>\n";
+},"10":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, escapeExpression=this.escapeExpression, buffer = " <div title='";
+ stack1 = lambda((depth0 != null ? depth0.description : depth0), depth0);
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "'>"
+ + escapeExpression(lambda((depth0 != null ? depth0.scope : depth0), depth0))
+ + "</div>\n";
+},"12":function(depth0,helpers,partials,data) {
+ return "</div>";
+ },"14":function(depth0,helpers,partials,data) {
+ return " <div class='access'>\n <span class=\"api-ic ic-off\" title=\"click to authenticate\"></span>\n </div>\n";
+ },"16":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <h4>Response Class (Status "
+ + escapeExpression(((helper = (helper = helpers.successCode || (depth0 != null ? depth0.successCode : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"successCode","hash":{},"data":data}) : helper)))
+ + ")</h4>\n <p><span class=\"model-signature\" /></p>\n <br/>\n <div class=\"response-content-type\" />\n";
+},"18":function(depth0,helpers,partials,data) {
+ return " <h4>Parameters</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th style=\"width: 100px; max-width: 100px\">Parameter</th>\n <th style=\"width: 310px; max-width: 310px\">Value</th>\n <th style=\"width: 200px; max-width: 200px\">Description</th>\n <th style=\"width: 100px; max-width: 100px\">Parameter Type</th>\n <th style=\"width: 220px; max-width: 230px\">Data Type</th>\n </tr>\n </thead>\n <tbody class=\"operation-params\">\n\n </tbody>\n </table>\n";
+ },"20":function(depth0,helpers,partials,data) {
+ return " <div style='margin:0;padding:0;display:inline'></div>\n <h4>Response Messages</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th>HTTP Status Code</th>\n <th>Reason</th>\n <th>Response Model</th>\n <th>Headers</th>\n </tr>\n </thead>\n <tbody class=\"operation-status\">\n\n </tbody>\n </table>\n";
+ },"22":function(depth0,helpers,partials,data) {
+ return "";
+},"24":function(depth0,helpers,partials,data) {
+ return " <div class='sandbox_header'>\n <input class='submit' name='commit' type='button' value='Try it out!' />\n <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n <span class='response_throbber' style='display:none'></span>\n </div>\n";
+ },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, options, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, blockHelperMissing=helpers.blockHelperMissing, buffer = "\n <ul class='operations' >\n <li class='"
+ + escapeExpression(((helper = (helper = helpers.method || (depth0 != null ? depth0.method : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"method","hash":{},"data":data}) : helper)))
+ + " operation' id='"
+ + escapeExpression(((helper = (helper = helpers.parentId || (depth0 != null ? depth0.parentId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"parentId","hash":{},"data":data}) : helper)))
+ + "_"
+ + escapeExpression(((helper = (helper = helpers.nickname || (depth0 != null ? depth0.nickname : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nickname","hash":{},"data":data}) : helper)))
+ + "'>\n <div class='heading'>\n <h3>\n <span class='http_method'>\n <a href='#!/"
+ + escapeExpression(((helper = (helper = helpers.encodedParentId || (depth0 != null ? depth0.encodedParentId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"encodedParentId","hash":{},"data":data}) : helper)))
+ + "/"
+ + escapeExpression(((helper = (helper = helpers.nickname || (depth0 != null ? depth0.nickname : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nickname","hash":{},"data":data}) : helper)))
+ + "' class=\"toggleOperation\">"
+ + escapeExpression(((helper = (helper = helpers.method || (depth0 != null ? depth0.method : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"method","hash":{},"data":data}) : helper)))
+ + "</a>\n </span>\n <span class='path'>\n <a href='#!/"
+ + escapeExpression(((helper = (helper = helpers.encodedParentId || (depth0 != null ? depth0.encodedParentId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"encodedParentId","hash":{},"data":data}) : helper)))
+ + "/"
+ + escapeExpression(((helper = (helper = helpers.nickname || (depth0 != null ? depth0.nickname : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nickname","hash":{},"data":data}) : helper)))
+ + "' class=\"toggleOperation ";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.deprecated : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\">"
+ + escapeExpression(((helper = (helper = helpers.path || (depth0 != null ? depth0.path : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"path","hash":{},"data":data}) : helper)))
+ + "</a>\n </span>\n </h3>\n <ul class='options'>\n <li>\n <a href='#!/"
+ + escapeExpression(((helper = (helper = helpers.encodedParentId || (depth0 != null ? depth0.encodedParentId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"encodedParentId","hash":{},"data":data}) : helper)))
+ + "/"
+ + escapeExpression(((helper = (helper = helpers.nickname || (depth0 != null ? depth0.nickname : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nickname","hash":{},"data":data}) : helper)))
+ + "' class=\"toggleOperation\">";
+ stack1 = ((helper = (helper = helpers.summary || (depth0 != null ? depth0.summary : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"summary","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</a>\n </li>\n </ul>\n </div>\n <div class='content' id='"
+ + escapeExpression(((helper = (helper = helpers.parentId || (depth0 != null ? depth0.parentId : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"parentId","hash":{},"data":data}) : helper)))
+ + "_"
+ + escapeExpression(((helper = (helper = helpers.nickname || (depth0 != null ? depth0.nickname : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"nickname","hash":{},"data":data}) : helper)))
+ + "_content' style='display:none'>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.deprecated : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.description : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = ((helper = (helper = helpers.oauth || (depth0 != null ? depth0.oauth : depth0)) != null ? helper : helperMissing),(options={"name":"oauth","hash":{},"fn":this.program(7, data),"inverse":this.noop,"data":data}),(typeof helper === functionType ? helper.call(depth0, options) : helper));
+ if (!helpers.oauth) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n";
+ stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.oauth : depth0), {"name":"each","hash":{},"fn":this.program(9, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += " ";
+ stack1 = ((helper = (helper = helpers.oauth || (depth0 != null ? depth0.oauth : depth0)) != null ? helper : helperMissing),(options={"name":"oauth","hash":{},"fn":this.program(12, data),"inverse":this.noop,"data":data}),(typeof helper === functionType ? helper.call(depth0, options) : helper));
+ if (!helpers.oauth) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n";
+ stack1 = ((helper = (helper = helpers.oauth || (depth0 != null ? depth0.oauth : depth0)) != null ? helper : helperMissing),(options={"name":"oauth","hash":{},"fn":this.program(14, data),"inverse":this.noop,"data":data}),(typeof helper === functionType ? helper.call(depth0, options) : helper));
+ if (!helpers.oauth) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.type : depth0), {"name":"if","hash":{},"fn":this.program(16, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += " <form accept-charset='UTF-8' class='sandbox'>\n <div style='margin:0;padding:0;display:inline'></div>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.parameters : depth0), {"name":"if","hash":{},"fn":this.program(18, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.responseMessages : depth0), {"name":"if","hash":{},"fn":this.program(20, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isReadOnly : depth0), {"name":"if","hash":{},"fn":this.program(22, data),"inverse":this.program(24, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + " </form>\n <div class='response' style='display:none'>\n <h4>Request URL</h4>\n <div class='block request_url'></div>\n <h4>Response Body</h4>\n <div class='block response_body'></div>\n <h4>Response Code</h4>\n <div class='block response_code'></div>\n <h4>Response Headers</h4>\n <div class='block response_headers'></div>\n </div>\n </div>\n </li>\n </ul>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["param_list"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return "<td class='code required'>"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</td>\n";
+},"3":function(depth0,helpers,partials,data) {
+ return " multiple='multiple'";
+ },"5":function(depth0,helpers,partials,data) {
+ return "";
+},"7":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.program(8, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"8":function(depth0,helpers,partials,data) {
+ var stack1, helperMissing=helpers.helperMissing, buffer = "";
+ stack1 = ((helpers.isArray || (depth0 && depth0.isArray) || helperMissing).call(depth0, depth0, {"name":"isArray","hash":{},"fn":this.program(5, data),"inverse":this.program(9, data),"data":data}));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"9":function(depth0,helpers,partials,data) {
+ return " <option selected=\"\" value=''></option>\n";
+ },"11":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isDefault : depth0), {"name":"if","hash":{},"fn":this.program(12, data),"inverse":this.program(14, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"12":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <option selected=\"\" value='"
+ + escapeExpression(((helper = (helper = helpers.value || (depth0 != null ? depth0.value : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"value","hash":{},"data":data}) : helper)))
+ + "'>"
+ + escapeExpression(((helper = (helper = helpers.value || (depth0 != null ? depth0.value : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"value","hash":{},"data":data}) : helper)))
+ + " (default)</option>\n";
+},"14":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <option value='"
+ + escapeExpression(((helper = (helper = helpers.value || (depth0 != null ? depth0.value : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"value","hash":{},"data":data}) : helper)))
+ + "'>"
+ + escapeExpression(((helper = (helper = helpers.value || (depth0 != null ? depth0.value : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"value","hash":{},"data":data}) : helper)))
+ + "</option>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.required : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "<td class='code'>"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</td>\n<td>\n <select ";
+ stack1 = ((helpers.isArray || (depth0 && depth0.isArray) || helperMissing).call(depth0, depth0, {"name":"isArray","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data}));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += " class='parameter' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.required : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.program(7, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = helpers.each.call(depth0, ((stack1 = (depth0 != null ? depth0.allowableValues : depth0)) != null ? stack1.descriptiveValues : stack1), {"name":"each","hash":{},"fn":this.program(11, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += " </select>\n</td>\n<td class=\"markdown\">";
+ stack1 = ((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"description","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td>";
+ stack1 = ((helper = (helper = helpers.paramType || (depth0 != null ? depth0.paramType : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"paramType","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</td>\n<td><span class=\"model-signature\"></span></td>";
+},"useData":true});
+this["Handlebars"]["templates"]["param_readonly_required"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <textarea class='body-textarea' readonly='readonly' placeholder='(required)' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'>"
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "</textarea>\n";
+},"3":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(4, data),"inverse":this.program(6, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"4":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " "
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "\n";
+},"6":function(depth0,helpers,partials,data) {
+ return " (empty)\n";
+ },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<td class='code required'>"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</td>\n<td>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isBody : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td class=\"markdown\">";
+ stack1 = ((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"description","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td>";
+ stack1 = ((helper = (helper = helpers.paramType || (depth0 != null ? depth0.paramType : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"paramType","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</td>\n<td><span class=\"model-signature\"></span></td>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["param_readonly"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <textarea class='body-textarea' readonly='readonly' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'>"
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "</textarea>\n";
+},"3":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(4, data),"inverse":this.program(6, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"4":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " "
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "\n";
+},"6":function(depth0,helpers,partials,data) {
+ return " (empty)\n";
+ },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<td class='code'>"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</td>\n<td>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isBody : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(3, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td class=\"markdown\">";
+ stack1 = ((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"description","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td>";
+ stack1 = ((helper = (helper = helpers.paramType || (depth0 != null ? depth0.paramType : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"paramType","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</td>\n<td><span class=\"model-signature\"></span></td>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["param_required"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isFile : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.program(4, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"2":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input type=\"file\" name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'/>\n";
+},"4":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.program(7, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"5":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <textarea class='body-textarea required' placeholder='(required)' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'>"
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "</textarea>\n <br />\n <div class=\"parameter-content-type\" />\n";
+},"7":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <textarea class='body-textarea required' placeholder='(required)' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'></textarea>\n <br />\n <div class=\"parameter-content-type\" />\n";
+},"9":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isFile : depth0), {"name":"if","hash":{},"fn":this.program(10, data),"inverse":this.program(12, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"10":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input class='parameter' class='required' type='file' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'/>\n";
+},"12":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(13, data),"inverse":this.program(15, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"13":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input class='parameter required' minlength='1' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "' placeholder='(required)' type='text' value='"
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "'/>\n";
+},"15":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input class='parameter required' minlength='1' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "' placeholder='(required)' type='text' value=''/>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<td class='code required'>"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</td>\n<td>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isBody : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(9, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td>\n <strong><span class=\"markdown\">";
+ stack1 = ((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"description","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</span></strong>\n</td>\n<td>";
+ stack1 = ((helper = (helper = helpers.paramType || (depth0 != null ? depth0.paramType : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"paramType","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</td>\n<td><span class=\"model-signature\"></span></td>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["param"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isFile : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.program(4, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"2":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input type=\"file\" name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'/>\n <div class=\"parameter-content-type\" />\n";
+},"4":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(5, data),"inverse":this.program(7, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"5":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <textarea class='body-textarea' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'>"
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "</textarea>\n <br />\n <div class=\"parameter-content-type\" />\n";
+},"7":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <textarea class='body-textarea' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "'></textarea>\n <br />\n <div class=\"parameter-content-type\" />\n";
+},"9":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isFile : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.program(10, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"10":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0['default'] : depth0), {"name":"if","hash":{},"fn":this.program(11, data),"inverse":this.program(13, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"11":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input class='parameter' minlength='0' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "' placeholder='' type='text' value='"
+ + escapeExpression(((helper = (helper = helpers['default'] || (depth0 != null ? depth0['default'] : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"default","hash":{},"data":data}) : helper)))
+ + "'/>\n";
+},"13":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <input class='parameter' minlength='0' name='"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "' placeholder='' type='text' value=''/>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<td class='code'>"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</td>\n<td>\n\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.isBody : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(9, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n</td>\n<td class=\"markdown\">";
+ stack1 = ((helper = (helper = helpers.description || (depth0 != null ? depth0.description : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"description","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td>";
+ stack1 = ((helper = (helper = helpers.paramType || (depth0 != null ? depth0.paramType : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"paramType","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</td>\n<td>\n <span class=\"model-signature\"></span>\n</td>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["parameter_content_type"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.consumes : depth0), {"name":"each","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"2":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, buffer = " <option value=\"";
+ stack1 = lambda(depth0, depth0);
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\">";
+ stack1 = lambda(depth0, depth0);
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</option>\n";
+},"4":function(depth0,helpers,partials,data) {
+ return " <option value=\"application/json\">application/json</option>\n";
+ },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "<label for=\"parameterContentType\"></label>\n<select name=\"parameterContentType\">\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.consumes : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(4, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</select>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["resource"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ return " : ";
+ },"3":function(depth0,helpers,partials,data) {
+ var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
+ return " <li>\n <a href='"
+ + escapeExpression(((helper = (helper = helpers.url || (depth0 != null ? depth0.url : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"url","hash":{},"data":data}) : helper)))
+ + "'>Raw</a>\n </li>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, options, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, blockHelperMissing=helpers.blockHelperMissing, buffer = "<div class='heading'>\n <h2>\n <a href='#!/"
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "' class=\"toggleEndpointList\" data-id=\""
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "\">"
+ + escapeExpression(((helper = (helper = helpers.name || (depth0 != null ? depth0.name : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"name","hash":{},"data":data}) : helper)))
+ + "</a> ";
+ stack1 = ((helper = (helper = helpers.summary || (depth0 != null ? depth0.summary : depth0)) != null ? helper : helperMissing),(options={"name":"summary","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data}),(typeof helper === functionType ? helper.call(depth0, options) : helper));
+ if (!helpers.summary) { stack1 = blockHelperMissing.call(depth0, stack1, options); }
+ if (stack1 != null) { buffer += stack1; }
+ stack1 = ((helper = (helper = helpers.summary || (depth0 != null ? depth0.summary : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"summary","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\n </h2>\n <ul class='options'>\n <li>\n <a href='#!/"
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "' id='endpointListTogger_"
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "' class=\"toggleEndpointList\" data-id=\""
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "\">Show/Hide</a>\n </li>\n <li>\n <a href='#' class=\"collapseResource\" data-id=\""
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "\">\n List Operations\n </a>\n </li>\n <li>\n <a href='#' class=\"expandResource\" data-id=\""
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "\">\n Expand Operations\n </a>\n </li>\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.url : depth0), {"name":"if","hash":{},"fn":this.program(3, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + " </ul>\n</div>\n<ul class='endpoints' id='"
+ + escapeExpression(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"id","hash":{},"data":data}) : helper)))
+ + "_endpoint_list' style='display:none'>\n\n</ul>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["response_content_type"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "";
+ stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.produces : depth0), {"name":"each","hash":{},"fn":this.program(2, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer;
+},"2":function(depth0,helpers,partials,data) {
+ var stack1, lambda=this.lambda, buffer = " <option value=\"";
+ stack1 = lambda(depth0, depth0);
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "\">";
+ stack1 = lambda(depth0, depth0);
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</option>\n";
+},"4":function(depth0,helpers,partials,data) {
+ return " <option value=\"application/json\">application/json</option>\n";
+ },"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, buffer = "<label for=\"responseContentType\"></label>\n<select name=\"responseContentType\">\n";
+ stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.produces : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(4, data),"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "</select>\n";
+},"useData":true});
+this["Handlebars"]["templates"]["signature"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div>\n<ul class=\"signature-nav\">\n <li><a class=\"description-link\" href=\"#\">Model</a></li>\n <li><a class=\"snippet-link\" href=\"#\">Model Schema</a></li>\n</ul>\n<div>\n\n<div class=\"signature-container\">\n <div class=\"description\">\n ";
+ stack1 = ((helper = (helper = helpers.signature || (depth0 != null ? depth0.signature : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signature","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + "\n </div>\n\n <div class=\"snippet\">\n <pre><code>"
+ + escapeExpression(((helper = (helper = helpers.sampleJSON || (depth0 != null ? depth0.sampleJSON : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"sampleJSON","hash":{},"data":data}) : helper)))
+ + "</code></pre>\n <small class=\"notice\"></small>\n </div>\n</div>\n\n";
+},"useData":true});
+this["Handlebars"]["templates"]["status_code"] = Handlebars.template({"1":function(depth0,helpers,partials,data) {
+ var lambda=this.lambda, escapeExpression=this.escapeExpression;
+ return " <tr>\n <td>"
+ + escapeExpression(lambda((data && data.key), depth0))
+ + "</td>\n <td>"
+ + escapeExpression(lambda((depth0 != null ? depth0.description : depth0), depth0))
+ + "</td>\n <td>"
+ + escapeExpression(lambda((depth0 != null ? depth0.type : depth0), depth0))
+ + "</td>\n </tr>\n";
+},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
+ var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<td width='15%' class='code'>"
+ + escapeExpression(((helper = (helper = helpers.code || (depth0 != null ? depth0.code : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"code","hash":{},"data":data}) : helper)))
+ + "</td>\n<td>";
+ stack1 = ((helper = (helper = helpers.message || (depth0 != null ? depth0.message : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"message","hash":{},"data":data}) : helper));
+ if (stack1 != null) { buffer += stack1; }
+ buffer += "</td>\n<td width='50%'><span class=\"model-signature\" /></td>\n<td class=\"headers\">\n <table>\n <tbody>\n";
+ stack1 = helpers.each.call(depth0, (depth0 != null ? depth0.headers : depth0), {"name":"each","hash":{},"fn":this.program(1, data),"inverse":this.noop,"data":data});
+ if (stack1 != null) { buffer += stack1; }
+ return buffer + " </tbody>\n </table>\n</td>";
+},"useData":true});
+/**
+ * swagger-client - swagger-client is a javascript client for use with swaggering APIs.
+ * @version v2.1.0
+ * @link http://swagger.io
+ * @license apache 2.0
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SwaggerClient = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+'use strict';
+
+var auth = require('./lib/auth');
+var helpers = require('./lib/helpers');
+var SwaggerClient = require('./lib/client');
+var deprecationWrapper = function (url, options) {
+ helpers.log('This is deprecated, use "new SwaggerClient" instead.');
+
+ return new SwaggerClient(url, options);
+};
+
+/* Here for IE8 Support */
+if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(obj, start) {
+ for (var i = (start || 0), j = this.length; i < j; i++) {
+ if (this[i] === obj) { return i; }
+ }
+ return -1;
+ };
+};
+
+/* Here for node 10.x support */
+if (!String.prototype.endsWith) {
+ String.prototype.endsWith = function(suffix) {
+ return this.indexOf(suffix, this.length - suffix.length) !== -1;
+ };
+};
+
+module.exports = SwaggerClient;
+
+SwaggerClient.ApiKeyAuthorization = auth.ApiKeyAuthorization;
+SwaggerClient.PasswordAuthorization = auth.PasswordAuthorization;
+SwaggerClient.CookieAuthorization = auth.CookieAuthorization;
+SwaggerClient.SwaggerApi = deprecationWrapper;
+SwaggerClient.SwaggerClient = deprecationWrapper;
+
+},{"./lib/auth":2,"./lib/client":3,"./lib/helpers":4}],2:[function(require,module,exports){
+'use strict';
+
+var btoa = require('btoa'); // jshint ignore:line
+var CookieJar = require('cookiejar');
+
+/**
+ * SwaggerAuthorizations applys the correct authorization to an operation being executed
+ */
+var SwaggerAuthorizations = module.exports.SwaggerAuthorizations = function () {
+ this.authz = {};
+};
+
+SwaggerAuthorizations.prototype.add = function (name, auth) {
+ this.authz[name] = auth;
+
+ return auth;
+};
+
+SwaggerAuthorizations.prototype.remove = function (name) {
+ return delete this.authz[name];
+};
+
+SwaggerAuthorizations.prototype.apply = function (obj, authorizations) {
+ var status = null;
+ var key, name, value, result;
+
+ // if the 'authorizations' key is undefined, or has an empty array, add all keys
+ if (typeof authorizations === 'undefined' || Object.keys(authorizations).length === 0) {
+ for (key in this.authz) {
+ value = this.authz[key];
+ result = value.apply(obj, authorizations);
+
+ if (result === true) {
+ status = true;
+ }
+ }
+ } else {
+ // 2.0 support
+ if (Array.isArray(authorizations)) {
+ for (var i = 0; i < authorizations.length; i++) {
+ var auth = authorizations[i];
+
+ for (name in auth) {
+ for (key in this.authz) {
+ if (key === name) {
+ value = this.authz[key];
+ result = value.apply(obj, authorizations);
+
+ if (result === true) {
+ status = true;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // 1.2 support
+ for (name in authorizations) {
+ for (key in this.authz) {
+ if (key === name) {
+ value = this.authz[key];
+ result = value.apply(obj, authorizations);
+
+ if (result === true) {
+ status = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return status;
+};
+
+/**
+ * ApiKeyAuthorization allows a query param or header to be injected
+ */
+var ApiKeyAuthorization = module.exports.ApiKeyAuthorization = function (name, value, type) {
+ this.name = name;
+ this.value = value;
+ this.type = type;
+};
+
+ApiKeyAuthorization.prototype.apply = function (obj) {
+ if (this.type === 'query') {
+ if (obj.url.indexOf('?') > 0) {
+ obj.url = obj.url + '&' + this.name + '=' + this.value;
+ } else {
+ obj.url = obj.url + '?' + this.name + '=' + this.value;
+ }
+
+ return true;
+ } else if (this.type === 'header') {
+ obj.headers[this.name] = this.value;
+
+ return true;
+ }
+};
+
+var CookieAuthorization = module.exports.CookieAuthorization = function (cookie) {
+ this.cookie = cookie;
+};
+
+CookieAuthorization.prototype.apply = function (obj) {
+ obj.cookieJar = obj.cookieJar || new CookieJar();
+ obj.cookieJar.setCookie(this.cookie);
+
+ return true;
+};
+
+/**
+ * Password Authorization is a basic auth implementation
+ */
+var PasswordAuthorization = module.exports.PasswordAuthorization = function (name, username, password) {
+ this.name = name;
+ this.username = username;
+ this.password = password;
+};
+
+PasswordAuthorization.prototype.apply = function (obj) {
+ obj.headers.Authorization = 'Basic ' + btoa(this.username + ':' + this.password);
+
+ return true;
+};
+
+},{"btoa":16,"cookiejar":17}],3:[function(require,module,exports){
+'use strict';
+
+var _ = {
+ bind: require('lodash-compat/function/bind'),
+ cloneDeep: require('lodash-compat/lang/cloneDeep'),
+ find: require('lodash-compat/collection/find'),
+ forEach: require('lodash-compat/collection/forEach'),
+ indexOf: require('lodash-compat/array/indexOf'),
+ isArray: require('lodash-compat/lang/isArray'),
+ isFunction: require('lodash-compat/lang/isFunction'),
+ isPlainObject: require('lodash-compat/lang/isPlainObject'),
+ isUndefined: require('lodash-compat/lang/isUndefined')
+};
+var auth = require('./auth');
+var helpers = require('./helpers');
+var Model = require('./types/model');
+var Operation = require('./types/operation');
+var OperationGroup = require('./types/operationGroup');
+var Resolver = require('./resolver');
+var SwaggerHttp = require('./http');
+var SwaggerSpecConverter = require('./spec-converter');
+
+// We have to keep track of the function/property names to avoid collisions for tag names which are used to allow the
+// following usage: 'client.{tagName}'
+var reservedClientTags = [
+ 'authorizationScheme',
+ 'authorizations',
+ 'basePath',
+ 'build',
+ 'buildFrom1_1Spec',
+ 'buildFrom1_2Spec',
+ 'buildFromSpec',
+ 'clientAuthorizations',
+ 'convertInfo',
+ 'debug',
+ 'defaultErrorCallback',
+ 'defaultSuccessCallback',
+ 'fail',
+ 'failure',
+ 'finish',
+ 'help',
+ 'idFromOp',
+ 'info',
+ 'initialize',
+ 'isBuilt',
+ 'isValid',
+ 'models',
+ 'modelsArray',
+ 'options',
+ 'parseUri',
+ 'progress',
+ 'resourceCount',
+ 'sampleModels',
+ 'selfReflect',
+ 'setConsolidatedModels',
+ 'spec',
+ 'supportedSubmitMethods',
+ 'swaggerRequestHeaders',
+ 'tagFromLabel',
+ 'url',
+ 'useJQuery'
+];
+// We have to keep track of the function/property names to avoid collisions for tag names which are used to allow the
+// following usage: 'client.apis.{tagName}'
+var reservedApiTags = [
+ 'apis',
+ 'asCurl',
+ 'description',
+ 'externalDocs',
+ 'help',
+ 'label',
+ 'name',
+ 'operation',
+ 'operations',
+ 'operationsArray',
+ 'path',
+ 'tag'
+];
+var supportedOperationMethods = ['delete', 'get', 'head', 'options', 'patch', 'post', 'put'];
+var SwaggerClient = module.exports = function (url, options) {
+ this.authorizationScheme = null;
+ this.authorizations = null;
+ this.basePath = null;
+ this.debug = false;
+ this.info = null;
+ this.isBuilt = false;
+ this.isValid = false;
+ this.modelsArray = [];
+ this.resourceCount = 0;
+ this.url = null;
+ this.useJQuery = false;
+
+ if (typeof url !== 'undefined') {
+ return this.initialize(url, options);
+ } else {
+ return this;
+ }
+};
+
+SwaggerClient.prototype.initialize = function (url, options) {
+ this.models = {};
+ this.sampleModels = {};
+
+ options = (options || {});
+
+ if (typeof url === 'string') {
+ this.url = url;
+ } else if (typeof url === 'object') {
+ options = url;
+ this.url = options.url;
+ }
+
+ this.swaggerRequestHeaders = options.swaggerRequestHeaders || 'application/json;charset=utf-8,*/*';
+ this.defaultSuccessCallback = options.defaultSuccessCallback || null;
+ this.defaultErrorCallback = options.defaultErrorCallback || null;
+
+ if (typeof options.success === 'function') {
+ this.success = options.success;
+ }
+
+ if (options.useJQuery) {
+ this.useJQuery = options.useJQuery;
+ }
+
+ if (options.authorizations) {
+ this.clientAuthorizations = options.authorizations;
+ } else {
+ this.clientAuthorizations = new auth.SwaggerAuthorizations();
+ }
+
+ this.supportedSubmitMethods = options.supportedSubmitMethods || [];
+ this.failure = options.failure || function () {};
+ this.progress = options.progress || function () {};
+ this.spec = _.cloneDeep(options.spec); // Clone so we do not alter the provided document
+ this.options = options;
+
+ if (typeof options.success === 'function') {
+ this.ready = true;
+ this.build();
+ }
+};
+
+SwaggerClient.prototype.build = function (mock) {
+ if (this.isBuilt) {
+ return this;
+ }
+
+ var self = this;
+
+ this.progress('fetching resource list: ' + this.url);
+
+ var obj = {
+ useJQuery: this.useJQuery,
+ url: this.url,
+ method: 'get',
+ headers: {
+ accept: this.swaggerRequestHeaders
+ },
+ on: {
+ error: function (response) {
+ if (self.url.substring(0, 4) !== 'http') {
+ return self.fail('Please specify the protocol for ' + self.url);
+ } else if (response.status === 0) {
+ return self.fail('Can\'t read from server. It may not have the appropriate access-control-origin settings.');
+ } else if (response.status === 404) {
+ return self.fail('Can\'t read swagger JSON from ' + self.url);
+ } else {
+ return self.fail(response.status + ' : ' + response.statusText + ' ' + self.url);
+ }
+ },
+ response: function (resp) {
+ var responseObj = resp.obj || JSON.parse(resp.data);
+ self.swaggerVersion = responseObj.swaggerVersion;
+
+ if (responseObj.swagger && parseInt(responseObj.swagger) === 2) {
+ self.swaggerVersion = responseObj.swagger;
+
+ new Resolver().resolve(responseObj, self.buildFromSpec, self);
+
+ self.isValid = true;
+ } else {
+ var converter = new SwaggerSpecConverter();
+ converter.setDocumentationLocation(self.url);
+ converter.convert(responseObj, function(spec) {
+ new Resolver().resolve(spec, self.buildFromSpec, self);
+ self.isValid = true;
+ });
+ }
+ }
+ }
+ };
+
+ if (this.spec) {
+ setTimeout(function () {
+ new Resolver().resolve(self.spec, self.buildFromSpec, self);
+ }, 10);
+ } else {
+ this.clientAuthorizations.apply(obj);
+
+ if (mock) {
+ return obj;
+ }
+
+ new SwaggerHttp().execute(obj);
+ }
+
+ return this;
+};
+
+SwaggerClient.prototype.buildFromSpec = function (response) {
+ if (this.isBuilt) {
+ return this;
+ }
+
+ this.apis = {};
+ this.apisArray = [];
+ this.basePath = response.basePath || '';
+ this.consumes = response.consumes;
+ this.host = response.host || '';
+ this.info = response.info || {};
+ this.produces = response.produces;
+ this.schemes = response.schemes || [];
+ this.securityDefinitions = response.securityDefinitions;
+ this.title = response.title || '';
+
+ if (response.externalDocs) {
+ this.externalDocs = response.externalDocs;
+ }
+
+ // legacy support
+ this.authSchemes = response.securityDefinitions;
+
+ var definedTags = {};
+ var k;
+
+ if (Array.isArray(response.tags)) {
+ definedTags = {};
+
+ for (k = 0; k < response.tags.length; k++) {
+ var t = response.tags[k];
+
+ definedTags[t.name] = t;
+ }
+ }
+
+ var location;
+
+ if (typeof this.url === 'string') {
+ location = this.parseUri(this.url);
+ }
+
+ if (typeof this.schemes === 'undefined' || this.schemes.length === 0) {
+ this.scheme = location.scheme || 'http';
+ } else {
+ this.scheme = this.schemes[0];
+ }
+
+ if (typeof this.host === 'undefined' || this.host === '') {
+ this.host = location.host;
+
+ if (location.port) {
+ this.host = this.host + ':' + location.port;
+ }
+ }
+
+ this.definitions = response.definitions;
+
+ var key;
+
+ for (key in this.definitions) {
+ var model = new Model(key, this.definitions[key], this.models);
+
+ if (model) {
+ this.models[key] = model;
+ }
+ }
+
+ // get paths, create functions for each operationId
+ var self = this;
+
+ // Bind help to 'client.apis'
+ self.apis.help = _.bind(self.help, self);
+
+ _.forEach(response.paths, function (pathObj, path) {
+ // Only process a path if it's an object
+ if (!_.isPlainObject(pathObj)) {
+ return;
+ }
+
+ _.forEach(supportedOperationMethods, function (method) {
+ var operation = pathObj[method];
+
+ if (_.isUndefined(operation)) {
+ // Operation does not exist
+ return;
+ } else if (!_.isPlainObject(operation)) {
+ // Operation exists but it is not an Operation Object. Since this is invalid, log it.
+ helpers.log('The \'' + method + '\' operation for \'' + path + '\' path is not an Operation Object');
+
+ return;
+ }
+
+ var tags = operation.tags;
+
+ if (_.isUndefined(tags) || !_.isArray(tags) || tags.length === 0) {
+ tags = operation.tags = [ 'default' ];
+ }
+
+ var operationId = self.idFromOp(path, method, operation);
+ var operationObject = new Operation(self, operation.scheme, operationId, method, path, operation,
+ self.definitions, self.models, self.clientAuthorizations);
+
+ // bind self operation's execute command to the api
+ _.forEach(tags, function (tag) {
+ var clientProperty = _.indexOf(reservedClientTags, tag) > -1 ? '_' + tag : tag;
+ var apiProperty = _.indexOf(reservedApiTags, tag) > -1 ? '_' + tag : tag;
+ var operationGroup = self[clientProperty];
+
+ if (clientProperty !== tag) {
+ helpers.log('The \'' + tag + '\' tag conflicts with a SwaggerClient function/property name. Use \'client.' +
+ clientProperty + '\' or \'client.apis.' + tag + '\' instead of \'client.' + tag + '\'.');
+ }
+
+ if (apiProperty !== tag) {
+ helpers.log('The \'' + tag + '\' tag conflicts with a SwaggerClient operation function/property name. Use ' +
+ '\'client.apis.' + apiProperty + '\' instead of \'client.apis.' + tag + '\'.');
+ }
+
+ if (_.indexOf(reservedApiTags, operationId) > -1) {
+ helpers.log('The \'' + operationId + '\' operationId conflicts with a SwaggerClient operation ' +
+ 'function/property name. Use \'client.apis.' + apiProperty + '._' + operationId +
+ '\' instead of \'client.apis.' + apiProperty + '.' + operationId + '\'.');
+
+ operationId = '_' + operationId;
+ operationObject.nickname = operationId; // So 'client.apis.[tag].operationId.help() works properly
+ }
+
+ if (_.isUndefined(operationGroup)) {
+ operationGroup = self[clientProperty] = self.apis[apiProperty] = {};
+
+ operationGroup.operations = {};
+ operationGroup.label = apiProperty;
+ operationGroup.apis = {};
+
+ var tagDef = definedTags[tag];
+
+ if (!_.isUndefined(tagDef)) {
+ operationGroup.description = tagDef.description;
+ operationGroup.externalDocs = tagDef.externalDocs;
+ }
+
+ self[clientProperty].help = _.bind(self.help, operationGroup);
+ self.apisArray.push(new OperationGroup(tag, operationGroup.description, operationGroup.externalDocs, operationObject));
+ }
+
+ // Bind tag help
+ if (!_.isFunction(operationGroup.help)) {
+ operationGroup.help = _.bind(self.help, operationGroup);
+ }
+
+ // bind to the apis object
+ self.apis[apiProperty][operationId] = operationGroup[operationId]= _.bind(operationObject.execute,
+ operationObject);
+ self.apis[apiProperty][operationId].help = operationGroup[operationId].help = _.bind(operationObject.help,
+ operationObject);
+ self.apis[apiProperty][operationId].asCurl = operationGroup[operationId].asCurl = _.bind(operationObject.asCurl,
+ operationObject);
+
+ operationGroup.apis[operationId] = operationGroup.operations[operationId] = operationObject;
+
+ // legacy UI feature
+ var api = _.find(self.apisArray, function (api) {
+ return api.tag === tag;
+ });
+
+ if (api) {
+ api.operationsArray.push(operationObject);
+ }
+ });
+ });
+ });
+
+ this.isBuilt = true;
+
+ if (this.success) {
+ this.isValid = true;
+ this.isBuilt = true;
+ this.success();
+ }
+
+ return this;
+};
+
+SwaggerClient.prototype.parseUri = function (uri) {
+ var urlParseRE = /^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/;
+ var parts = urlParseRE.exec(uri);
+
+ return {
+ scheme: parts[4].replace(':',''),
+ host: parts[11],
+ port: parts[12],
+ path: parts[15]
+ };
+};
+
+SwaggerClient.prototype.help = function (dontPrint) {
+ var output = '';
+
+ if (this instanceof SwaggerClient) {
+ _.forEach(this.apis, function (api, name) {
+ if (_.isPlainObject(api)) {
+ output += 'operations for the \'' + name + '\' tag\n';
+
+ _.forEach(api.operations, function (operation, name) {
+ output += ' * ' + name + ': ' + operation.summary + '\n';
+ });
+ }
+ });
+ } else if (this instanceof OperationGroup || _.isPlainObject(this)) {
+ output += 'operations for the \'' + this.label + '\' tag\n';
+
+ _.forEach(this.apis, function (operation, name) {
+ output += ' * ' + name + ': ' + operation.summary + '\n';
+ });
+ }
+
+ if (dontPrint) {
+ return output;
+ } else {
+ helpers.log(output);
+
+ return output;
+ }
+};
+
+SwaggerClient.prototype.tagFromLabel = function (label) {
+ return label;
+};
+
+SwaggerClient.prototype.idFromOp = function (path, httpMethod, op) {
+ if(!op || !op.operationId) {
+ op = op || {};
+ op.operationId = httpMethod + '_' + path;
+ }
+ var opId = op.operationId.replace(/[\s!@#$%^&*()_+=\[{\]};:<>|.\/?,\\'""-]/g, '_') || (path.substring(1) + '_' + httpMethod);
+
+ opId = opId.replace(/((_){2,})/g, '_');
+ opId = opId.replace(/^(_)*/g, '');
+ opId = opId.replace(/([_])*$/g, '');
+ return opId;
+};
+
+SwaggerClient.prototype.fail = function (message) {
+ this.failure(message);
+
+ throw message;
+};
+
+},{"./auth":2,"./helpers":4,"./http":5,"./resolver":6,"./spec-converter":7,"./types/model":8,"./types/operation":9,"./types/operationGroup":10,"lodash-compat/array/indexOf":19,"lodash-compat/collection/find":21,"lodash-compat/collection/forEach":22,"lodash-compat/function/bind":25,"lodash-compat/lang/cloneDeep":94,"lodash-compat/lang/isArray":96,"lodash-compat/lang/isFunction":97,"lodash-compat/lang/isPlainObject":100,"lodash-compat/lang/isUndefined":103}],4:[function(require,module,exports){
+(function (process){
+'use strict';
+
+module.exports.__bind = function (fn, me) {
+ return function(){
+ return fn.apply(me, arguments);
+ };
+};
+
+var log = module.exports.log = function() {
+ log.history = log.history || [];
+ log.history.push(arguments);
+
+ // Only log if available and we're not testing
+ if (console && process.env.NODE_ENV !== 'test') {
+ console.log(Array.prototype.slice.call(arguments)[0]);
+ }
+};
+
+module.exports.fail = function (message) {
+ log(message);
+};
+
+module.exports.optionHtml = function (label, value) {
+ return '<tr><td class="optionName">' + label + ':</td><td>' + value + '</td></tr>';
+};
+
+module.exports.typeFromJsonSchema = function (type, format) {
+ var str;
+
+ if (type === 'integer' && format === 'int32') {
+ str = 'integer';
+ } else if (type === 'integer' && format === 'int64') {
+ str = 'long';
+ } else if (type === 'integer' && typeof format === 'undefined') {
+ str = 'long';
+ } else if (type === 'string' && format === 'date-time') {
+ str = 'date-time';
+ } else if (type === 'string' && format === 'date') {
+ str = 'date';
+ } else if (type === 'number' && format === 'float') {
+ str = 'float';
+ } else if (type === 'number' && format === 'double') {
+ str = 'double';
+ } else if (type === 'number' && typeof format === 'undefined') {
+ str = 'double';
+ } else if (type === 'boolean') {
+ str = 'boolean';
+ } else if (type === 'string') {
+ str = 'string';
+ }
+
+ return str;
+};
+
+var simpleRef = module.exports.simpleRef = function (name) {
+ if (typeof name === 'undefined') {
+ return null;
+ }
+
+ if (name.indexOf('#/definitions/') === 0) {
+ return name.substring('#/definitions/'.length);
+ } else {
+ return name;
+ }
+};
+
+var getStringSignature = module.exports.getStringSignature = function (obj, baseComponent) {
+ var str = '';
+
+ if (typeof obj.$ref !== 'undefined') {
+ str += simpleRef(obj.$ref);
+ } else if (typeof obj.type === 'undefined') {
+ str += 'object';
+ } else if (obj.type === 'array') {
+ if (baseComponent) {
+ str += getStringSignature((obj.items || obj.$ref || {}));
+ } else {
+ str += 'Array[';
+ str += getStringSignature((obj.items || obj.$ref || {}));
+ str += ']';
+ }
+ } else if (obj.type === 'integer' && obj.format === 'int32') {
+ str += 'integer';
+ } else if (obj.type === 'integer' && obj.format === 'int64') {
+ str += 'long';
+ } else if (obj.type === 'integer' && typeof obj.format === 'undefined') {
+ str += 'long';
+ } else if (obj.type === 'string' && obj.format === 'date-time') {
+ str += 'date-time';
+ } else if (obj.type === 'string' && obj.format === 'date') {
+ str += 'date';
+ } else if (obj.type === 'string' && typeof obj.format === 'undefined') {
+ str += 'string';
+ } else if (obj.type === 'number' && obj.format === 'float') {
+ str += 'float';
+ } else if (obj.type === 'number' && obj.format === 'double') {
+ str += 'double';
+ } else if (obj.type === 'number' && typeof obj.format === 'undefined') {
+ str += 'double';
+ } else if (obj.type === 'boolean') {
+ str += 'boolean';
+ } else if (obj.$ref) {
+ str += simpleRef(obj.$ref);
+ } else {
+ str += obj.type;
+ }
+
+ return str;
+};
+
+}).call(this,require('_process'))
+
+},{"_process":15}],5:[function(require,module,exports){
+'use strict';
+
+var helpers = require('./helpers');
+var jQuery = require('jquery');
+var request = require('superagent');
+
+/*
+ * JQueryHttpClient is a light-weight, node or browser HTTP client
+ */
+var JQueryHttpClient = function () {};
+
+/*
+ * SuperagentHttpClient is a light-weight, node or browser HTTP client
+ */
+var SuperagentHttpClient = function () {};
+
+/**
+ * SwaggerHttp is a wrapper for executing requests
+ */
+var SwaggerHttp = module.exports = function () {};
+
+SwaggerHttp.prototype.execute = function (obj, opts) {
+ if (obj && (typeof obj.useJQuery === 'boolean')) {
+ this.useJQuery = obj.useJQuery;
+ } else {
+ this.useJQuery = this.isIE8();
+ }
+
+ if (obj && typeof obj.body === 'object') {
+ // special processing for file uploads via jquery
+ if (obj.body.type && obj.body.type === 'formData'){
+ obj.contentType = false;
+ obj.processData = false;
+
+ delete obj.headers['Content-Type'];
+ } else {
+ obj.body = JSON.stringify(obj.body);
+ }
+ }
+
+ if (this.useJQuery) {
+ return new JQueryHttpClient(opts).execute(obj);
+ } else {
+ return new SuperagentHttpClient(opts).execute(obj);
+ }
+};
+
+SwaggerHttp.prototype.isIE8 = function () {
+ var detectedIE = false;
+
+ if (typeof navigator !== 'undefined' && navigator.userAgent) {
+ var nav = navigator.userAgent.toLowerCase();
+
+ if (nav.indexOf('msie') !== -1) {
+ var version = parseInt(nav.split('msie')[1]);
+
+ if (version <= 8) {
+ detectedIE = true;
+ }
+ }
+ }
+
+ return detectedIE;
+};
+
+JQueryHttpClient.prototype.execute = function (obj) {
+ var cb = obj.on;
+ var request = obj;
+
+ obj.type = obj.method;
+ obj.cache = false;
+ delete obj.useJQuery;
+
+ /*
+ obj.beforeSend = function (xhr) {
+ var key, results;
+ if (obj.headers) {
+ results = [];
+ for (key in obj.headers) {
+ if (key.toLowerCase() === 'content-type') {
+ results.push(obj.contentType = obj.headers[key]);
+ } else if (key.toLowerCase() === 'accept') {
+ results.push(obj.accepts = obj.headers[key]);
+ } else {
+ results.push(xhr.setRequestHeader(key, obj.headers[key]));
+ }
+ }
+ return results;
+ }
+ };*/
+
+ obj.data = obj.body;
+
+ delete obj.body;
+
+ obj.complete = function (response) {
+ var headers = {};
+ var headerArray = response.getAllResponseHeaders().split('\n');
+
+ for (var i = 0; i < headerArray.length; i++) {
+ var toSplit = headerArray[i].trim();
+
+ if (toSplit.length === 0) {
+ continue;
+ }
+
+ var separator = toSplit.indexOf(':');
+
+ if (separator === -1) {
+ // Name but no value in the header
+ headers[toSplit] = null;
+
+ continue;
+ }
+
+ var name = toSplit.substring(0, separator).trim();
+ var value = toSplit.substring(separator + 1).trim();
+
+ headers[name] = value;
+ }
+
+ var out = {
+ url: request.url,
+ method: request.method,
+ status: response.status,
+ statusText: response.statusText,
+ data: response.responseText,
+ headers: headers
+ };
+
+ var contentType = (headers['content-type'] || headers['Content-Type'] || null);
+
+ if (contentType) {
+ if (contentType.indexOf('application/json') === 0 || contentType.indexOf('+json') > 0) {
+ try {
+ out.obj = response.responseJSON || JSON.parse(out.data) || {};
+ } catch (ex) {
+ // do not set out.obj
+ helpers.log('unable to parse JSON content');
+ }
+ }
+ }
+
+ if (response.status >= 200 && response.status < 300) {
+ cb.response(out);
+ } else if (response.status === 0 || (response.status >= 400 && response.status < 599)) {
+ cb.error(out);
+ } else {
+ return cb.response(out);
+ }
+ };
+
+ jQuery.support.cors = true;
+
+ return jQuery.ajax(obj);
+};
+
+SuperagentHttpClient.prototype.execute = function (obj) {
+ var method = obj.method.toLowerCase();
+
+ if (method === 'delete') {
+ method = 'del';
+ }
+
+ var headers = obj.headers || {};
+ var r = request[method](obj.url);
+ var name;
+
+ for (name in headers) {
+ r.set(name, headers[name]);
+ }
+
+ if (obj.body) {
+ r.send(obj.body);
+ }
+
+ r.end(function (err, res) {
+ res = res || {
+ status: 0,
+ headers: {error: 'no response from server'}
+ };
+ var response = {
+ url: obj.url,
+ method: obj.method,
+ headers: res.headers
+ };
+ var cb;
+
+ if (!err && res.error) {
+ err = res.error;
+ }
+
+ if (err && obj.on && obj.on.error) {
+ response.obj = err;
+ response.status = res ? res.status : 500;
+ response.statusText = res ? res.text : err.message;
+ cb = obj.on.error;
+ } else if (res && obj.on && obj.on.response) {
+ response.obj = (typeof res.body !== 'undefined') ? res.body : res.text;
+ response.status = res.status;
+ response.statusText = res.text;
+ cb = obj.on.response;
+ }
+ response.data = response.statusText;
+
+ if (cb) {
+ cb(response);
+ }
+ });
+};
+
+},{"./helpers":4,"jquery":18,"superagent":111}],6:[function(require,module,exports){
+'use strict';
+
+var SwaggerHttp = require('./http');
+
+/**
+ * Resolves a spec's remote references
+ */
+var Resolver = module.exports = function () {};
+
+Resolver.prototype.resolve = function (spec, callback, scope) {
+ this.scope = (scope || this);
+
+ var host, name, path, property, propertyName;
+ var processedCalls = 0, resolvedRefs = {}, unresolvedRefs = {};
+ var resolutionTable = {}; // store objects for dereferencing
+
+ // models
+ for (name in spec.definitions) {
+ var model = spec.definitions[name];
+
+ for (propertyName in model.properties) {
+ property = model.properties[propertyName];
+
+ this.resolveTo(property, resolutionTable);
+ }
+ }
+
+ // operations
+ for (name in spec.paths) {
+ var method, operation, responseCode;
+
+ path = spec.paths[name];
+
+ for (method in path) {
+ if(method === '$ref') {
+ this.resolveInline(spec, path, resolutionTable, unresolvedRefs);
+ }
+ else {
+ operation = path[method];
+
+ var i, parameters = operation.parameters;
+
+ for (i in parameters) {
+ var parameter = parameters[i];
+
+ if (parameter.in === 'body' && parameter.schema) {
+ this.resolveTo(parameter.schema, resolutionTable);
+ }
+
+ if (parameter.$ref) {
+ this.resolveInline(spec, parameter, resolutionTable, unresolvedRefs);
+ }
+ }
+
+ for (responseCode in operation.responses) {
+ var response = operation.responses[responseCode];
+ if(typeof response === 'object') {
+ if(response.$ref) {
+ this.resolveInline(spec, response, resolutionTable, unresolvedRefs);
+ }
+ }
+ if (response.schema) {
+ this.resolveTo(response.schema, resolutionTable);
+ }
+ }
+ }
+ }
+ }
+
+ // get hosts
+ var opts = {}, expectedCalls = 0;
+
+ for (name in resolutionTable) {
+ var parts = name.split('#');
+
+ if (parts.length === 2) {
+ host = parts[0]; path = parts[1];
+
+ if (!Array.isArray(opts[host])) {
+ opts[host] = [];
+ expectedCalls += 1;
+ }
+
+ opts[host].push(path);
+ }
+ else {
+ if (!Array.isArray(opts[name])) {
+ opts[name] = [];
+ expectedCalls += 1;
+ }
+
+ opts[name].push(null);
+ }
+ }
+
+ for (name in opts) {
+ var self = this, opt = opts[name];
+
+ host = name;
+
+ var obj = {
+ useJQuery: false, // TODO
+ url: host,
+ method: 'get',
+ headers: {
+ accept: this.scope.swaggerRequestHeaders || 'application/json'
+ },
+ on: {
+ error: function () {
+ processedCalls += 1;
+
+ var i;
+
+ for (i = 0; i < opt.length; i++) {
+ // fail all of these
+ var resolved = host + '#' + opt[i];
+
+ unresolvedRefs[resolved] = null;
+ }
+
+ if (processedCalls === expectedCalls) {
+ self.finish(spec, resolutionTable, resolvedRefs, unresolvedRefs, callback);
+ }
+ }, // jshint ignore:line
+ response: function (response) {
+
+ var i, j, swagger = response.obj;
+
+ if(swagger === null || Object.keys(swagger).length === 0) {
+ try {
+ swagger = JSON.parse(response.data);
+ }
+ catch (e){
+ swagger = {};
+ }
+ }
+
+ processedCalls += 1;
+
+ for (i = 0; i < opt.length; i++) {
+ var path = opt[i];
+ if(path == null) {
+ resolvedRefs[name] = {
+ name: name,
+ obj: swagger
+ };
+ }
+ else {
+ var location = swagger, parts = path.split('/');
+ for (j = 0; j < parts.length; j++) {
+ var segment = parts[j];
+ if(segment.indexOf('~1') !== -1) {
+ segment = parts[j].replace(/~0/g, '~').replace(/~1/g, '/');
+ if(segment.charAt(0) !== '/') {
+ segment = '/' + segment;
+ }
+ }
+
+ if (typeof location === 'undefined') {
+ break;
+ }
+
+ if (segment.length > 0) {
+ location = location[segment];
+ }
+ }
+ var resolved = host + '#' + path, resolvedName = parts[j-1];
+
+ if (typeof location !== 'undefined') {
+ resolvedRefs[resolved] = {
+ name: resolvedName,
+ obj: location
+ };
+ } else {
+ unresolvedRefs[resolved] = null;
+ }
+ }
+ }
+ if (processedCalls === expectedCalls) {
+ self.finish(spec, resolutionTable, resolvedRefs, unresolvedRefs, callback);
+ }
+ }
+ } // jshint ignore:line
+ };
+
+ if (scope && scope.clientAuthorizations) {
+ scope.clientAuthorizations.apply(obj);
+ }
+
+ new SwaggerHttp().execute(obj);
+ }
+
+ if (Object.keys(opts).length === 0) {
+ callback.call(this.scope, spec, unresolvedRefs);
+ }
+};
+
+Resolver.prototype.finish = function (spec, resolutionTable, resolvedRefs, unresolvedRefs, callback) {
+ // walk resolution table and replace with resolved refs
+ var ref;
+
+ for (ref in resolutionTable) {
+ var i, locations = resolutionTable[ref];
+
+ for (i = 0; i < locations.length; i++) {
+ var resolvedTo = resolvedRefs[locations[i].obj.$ref];
+
+ if (resolvedTo) {
+ if (!spec.definitions) {
+ spec.definitions = {};
+ }
+
+ if (locations[i].resolveAs === '$ref') {
+ spec.definitions[resolvedTo.name] = resolvedTo.obj;
+ locations[i].obj.$ref = '#/definitions/' + resolvedTo.name;
+ } else if (locations[i].resolveAs === 'inline') {
+ var targetObj = locations[i].obj;
+ var key;
+
+ delete targetObj.$ref;
+
+ for (key in resolvedTo.obj) {
+ targetObj[key] = resolvedTo.obj[key];
+ }
+ }
+ }
+ }
+ }
+
+ callback.call(this.scope, spec, unresolvedRefs);
+};
+
+/**
+ * immediately in-lines local refs, queues remote refs
+ * for inline resolution
+ */
+Resolver.prototype.resolveInline = function (spec, property, objs, unresolvedRefs) {
+ var ref = property.$ref;
+
+ if (ref) {
+ if (ref.indexOf('http') === 0) {
+ if (Array.isArray(objs[ref])) {
+ objs[ref].push({obj: property, resolveAs: 'inline'});
+ } else {
+ objs[ref] = [{obj: property, resolveAs: 'inline'}];
+ }
+ } else if (ref.indexOf('#') === 0) {
+ // local resolve
+ var shortenedRef = ref.substring(1);
+ var i, parts = shortenedRef.split('/'), location = spec;
+
+ for (i = 0; i < parts.length; i++) {
+ var part = parts[i];
+
+ if (part.length > 0) {
+ location = location[part];
+ }
+ }
+
+ if (location) {
+ delete property.$ref;
+
+ var key;
+
+ for (key in location) {
+ property[key] = location[key];
+ }
+ } else {
+ unresolvedRefs[ref] = null;
+ }
+ }
+ } else if (property.type === 'array') {
+ this.resolveTo(property.items, objs);
+ }
+};
+
+Resolver.prototype.resolveTo = function (property, objs) {
+ var ref = property.$ref;
+
+ if (ref) {
+ if (ref.indexOf('http') === 0) {
+ if (Array.isArray(objs[ref])) {
+ objs[ref].push({obj: property, resolveAs: '$ref'});
+ } else {
+ objs[ref] = [{obj: property, resolveAs: '$ref'}];
+ }
+ }
+ } else if (property.type === 'array') {
+ var items = property.items;
+
+ this.resolveTo(items, objs);
+ }
+};
+
+},{"./http":5}],7:[function(require,module,exports){
+'use strict';
+
+var SwaggerClient = require('./client');
+var SwaggerHttp = require('./http');
+
+var SwaggerSpecConverter = module.exports = function () {
+ this.errors = [];
+ this.warnings = [];
+ this.modelMap = {};
+};
+
+SwaggerSpecConverter.prototype.setDocumentationLocation = function (location) {
+ this.docLocation = location;
+};
+
+/**
+ * converts a resource listing OR api declaration
+ **/
+SwaggerSpecConverter.prototype.convert = function (obj, callback) {
+ // not a valid spec
+ if(!obj || !Array.isArray(obj.apis)) {
+ return this.finish(callback, null);
+ }
+
+ // create a new swagger object to return
+ var swagger = { swagger: '2.0' };
+
+ swagger.originalVersion = obj.swaggerVersion;
+
+ // add the info
+ this.apiInfo(obj, swagger);
+
+ // add security definitions
+ this.securityDefinitions(obj, swagger);
+
+ // see if this is a single-file swagger definition
+ var isSingleFileSwagger = false;
+ var i;
+ for(i = 0; i < obj.apis.length; i++) {
+ var api = obj.apis[i];
+ if(Array.isArray(api.operations)) {
+ isSingleFileSwagger = true;
+ }
+ }
+ if(isSingleFileSwagger) {
+ this.declaration(obj, swagger);
+ this.finish(callback, swagger);
+ }
+ else {
+ this.resourceListing(obj, swagger, callback);
+ }
+};
+
+SwaggerSpecConverter.prototype.declaration = function(obj, swagger) {
+ var name, i;
+ if(!obj.apis) {
+ return;
+ }
+
+ var basePath = obj.basePath;
+ if(obj.basePath.indexOf('http://') === 0) {
+ var p = obj.basePath.substring('http://'.length);
+ var pos = p.indexOf('/');
+ if(pos > 0) {
+ swagger.host = p.substring(0, pos);
+ swagger.basePath = p.substring(pos);
+ }
+ else{
+ swagger.host = p;
+ swagger.basePath = '/';
+ }
+ }
+ var resourceLevelAuth;
+ if(obj.authorizations) {
+ resourceLevelAuth = obj.authorizations;
+ }
+ if(obj.consumes) {
+ swagger.consumes = obj.consumes;
+ }
+ if(obj.produces) {
+ swagger.produces = obj.produces;
+ }
+
+ // build a mapping of id to name for 1.0 model resolutions
+ if(typeof obj === 'object') {
+ for(name in obj.models) {
+ var existingModel = obj.models[name];
+ var key = (existingModel.id || name);
+ this.modelMap[key] = name;
+ }
+ }
+
+ for(i = 0; i < obj.apis.length; i++) {
+ var api = obj.apis[i];
+ var path = api.path;
+ var operations = api.operations;
+ this.operations(path, obj.resourcePath, operations, resourceLevelAuth, swagger);
+ }
+
+ var models = obj.models;
+ this.models(models, swagger);
+};
+
+SwaggerSpecConverter.prototype.models = function(obj, swagger) {
+ if(typeof obj !== 'object') {
+ return;
+ }
+ var name;
+
+ swagger.definitions = swagger.definitions || {};
+ for(name in obj) {
+ var existingModel = obj[name];
+ var _enum = [];
+ var schema = { properties: {}};
+ var propertyName;
+ for(propertyName in existingModel.properties) {
+ var existingProperty = existingModel.properties[propertyName];
+ var property = {};
+ this.dataType(existingProperty, property);
+ if(existingProperty.description) {
+ property.description = existingProperty.description;
+ }
+ if(existingProperty['enum']) {
+ property['enum'] = existingProperty['enum'];
+ }
+ if(typeof existingProperty.required === 'boolean' && existingProperty.required === true) {
+ _enum.push(propertyName);
+ }
+ if(typeof existingProperty.required === 'string' && existingProperty.required === 'true') {
+ _enum.push(propertyName);
+ }
+ schema.properties[propertyName] = property;
+ }
+ if(_enum.length > 0) {
+ schema['enum'] = _enum;
+ }
+ swagger.definitions[name] = schema;
+ }
+};
+
+SwaggerSpecConverter.prototype.extractTag = function(resourcePath) {
+ var pathString = resourcePath || 'default';
+ if(pathString.indexOf('http:') === 0 || pathString.indexOf('https:') === 0) {
+ pathString = pathString.split(['/']);
+ pathString = pathString[pathString.length -1].substring();
+ }
+ return pathString.replace('/','');
+}
+
+SwaggerSpecConverter.prototype.operations = function(path, resourcePath, obj, resourceLevelAuth, swagger) {
+ if(!Array.isArray(obj)) {
+ return;
+ }
+ var i;
+
+ if(!swagger.paths) {
+ swagger.paths = {};
+ }
+
+ var pathObj = swagger.paths[path] || {};
+ var tag = this.extractTag(resourcePath);
+ swagger.tags = swagger.tags || [];
+ var matched = false;
+ for(i = 0; i < swagger.tags.length; i++) {
+ var tagObject = swagger.tags[i];
+ if(tagObject.name === tag) {
+ matched = true;
+ }
+ }
+ if(!matched) {
+ swagger.tags.push({name: tag});
+ }
+
+ for(i = 0; i < obj.length; i++) {
+ var existingOperation = obj[i];
+ var method = (existingOperation.method || existingOperation.httpMethod).toLowerCase();
+ var operation = {tags: [tag]};
+ var existingAuthorizations = existingOperation.authorizations;
+
+ if(existingAuthorizations && Object.keys(existingAuthorizations).length === 0) {
+ existingAuthorizations = resourceLevelAuth;
+ }
+
+ if(typeof existingAuthorizations !== 'undefined') {
+ for(var key in existingAuthorizations) {
+ operation.security = operation.security || [];
+ var scopes = existingAuthorizations[key];
+ if(scopes) {
+ var securityScopes = [];
+ for(var j in scopes) {
+ securityScopes.push(scopes[j].scope);
+ }
+ var scopesObject = {};
+ scopesObject[key] = securityScopes;
+ operation.security.push(scopesObject);
+ }
+ else {
+ var scopesObject = {};
+ scopesObject[key] = [];
+ operation.security.push(scopesObject);
+ }
+ }
+ }
+
+ if(existingOperation.consumes) {
+ operation.consumes = existingOperation.consumes;
+ }
+ else if(swagger.consumes) {
+ operation.consumes = swagger.consumes;
+ }
+ if(existingOperation.produces) {
+ operation.produces = existingOperation.produces;
+ }
+ else if(swagger.produces) {
+ operation.produces = swagger.produces;
+ }
+ if(existingOperation.summary) {
+ operation.summary = existingOperation.summary;
+ }
+ if(existingOperation.notes) {
+ operation.description = existingOperation.notes;
+ }
+ if(existingOperation.nickname) {
+ operation.operationId = existingOperation.nickname;
+ }
+ if(existingOperation.deprecated) {
+ operation.deprecated = existingOperation.deprecated;
+ }
+
+ this.authorizations(existingAuthorizations, swagger);
+ this.parameters(operation, existingOperation.parameters, swagger);
+ this.responseMessages(operation, existingOperation, swagger);
+
+ pathObj[method] = operation;
+ }
+
+ swagger.paths[path] = pathObj;
+};
+
+SwaggerSpecConverter.prototype.responseMessages = function(operation, existingOperation, swagger) {
+ if(typeof existingOperation !== 'object') {
+ return;
+ }
+ // build default response from the operation (1.x)
+ var defaultResponse = {};
+ this.dataType(existingOperation, defaultResponse);
+ if(!defaultResponse.schema) {
+ defaultResponse = {schema: defaultResponse};
+ }
+
+ operation.responses = operation.responses || {};
+
+ // grab from responseMessages (1.2)
+ var has200 = false;
+ if(Array.isArray(existingOperation.responseMessages)) {
+ var i;
+ var existingResponses = existingOperation.responseMessages;
+ for(i = 0; i < existingResponses.length; i++) {
+ var existingResponse = existingResponses[i];
+ var response = { description: existingResponse.message };
+ if(existingResponse.code === 200) {
+ has200 = true;
+ }
+ operation.responses['' + existingResponse.code] = response;
+ // TODO: schema
+ }
+ }
+
+ if(has200) {
+ operation.responses['default'] = defaultResponse;
+ }
+ else {
+ operation.responses['200'] = defaultResponse;
+ }
+};
+
+SwaggerSpecConverter.prototype.authorizations = function(obj, swagger) {
+ // TODO
+ if(typeof obj !== 'object') {
+ return;
+ }
+};
+
+SwaggerSpecConverter.prototype.parameters = function(operation, obj, swagger) {
+ if(!Array.isArray(obj)) {
+ return;
+ }
+ var i;
+ for(i = 0; i < obj.length; i++) {
+ var existingParameter = obj[i];
+ var parameter = {};
+ parameter.name = existingParameter.name;
+ parameter.description = existingParameter.description;
+ parameter.required = existingParameter.required;
+ parameter.in = existingParameter.paramType;
+
+ // per #168
+ if(parameter.in === 'body') {
+ parameter.name = 'body';
+ }
+ if(parameter.in === 'form') {
+ parameter.in = 'formData';
+ }
+
+ if(existingParameter.allowMultiple === true || existingParameter.allowMultiple === 'true') {
+ var innerType = {};
+ this.dataType(existingParameter, innerType);
+ parameter.type = 'array';
+ parameter.items = innerType;
+
+ if(existingParameter.allowableValues) {
+ var av = existingParameter.allowableValues;
+ if(av.valueType === 'LIST') {
+ parameter['enum'] = av.values;
+ }
+ }
+ }
+ else {
+ this.dataType(existingParameter, parameter);
+ }
+
+ operation.parameters = operation.parameters || [];
+ operation.parameters.push(parameter);
+ }
+};
+
+SwaggerSpecConverter.prototype.dataType = function(source, target) {
+ if(typeof source !== 'object') {
+ return;
+ }
+
+ if(source.minimum) {
+ target.minimum = source.minimum;
+ }
+ if(source.maximum) {
+ target.maximum = source.maximum;
+ }
+ if(source.defaultValue) {
+ target.default = source.defaultValue;
+ }
+
+ var jsonSchemaType = this.toJsonSchema(source);
+ if(jsonSchemaType) {
+ target = target || {};
+ if(jsonSchemaType.type) {
+ target.type = jsonSchemaType.type;
+ }
+ if(jsonSchemaType.format) {
+ target.format = jsonSchemaType.format;
+ }
+ if(jsonSchemaType.$ref) {
+ target.schema = {$ref: jsonSchemaType.$ref};
+ }
+ if(jsonSchemaType.items) {
+ target.items = jsonSchemaType.items;
+ }
+ }
+};
+
+SwaggerSpecConverter.prototype.toJsonSchema = function(source) {
+ if(!source) {
+ return 'object';
+ }
+ var detectedType = (source.type || source.dataType || source.responseClass || '');
+ var lcType = detectedType.toLowerCase();
+ var format = (source.format || '').toLowerCase();
+
+ if(lcType.indexOf('list[') === 0) {
+ var innerType = detectedType.substring(5, detectedType.length - 1);
+ var jsonType = this.toJsonSchema({type: innerType});
+ return {type: 'array', items: jsonType};
+ }
+ else if(lcType === 'int' || (lcType === 'integer' && format === 'int32'))
+ {return {type: 'integer', format: 'int32'};}
+ else if(lcType === 'long' || (lcType === 'integer' && format === 'int64'))
+ {return {type: 'integer', format: 'int64'};}
+ else if(lcType === 'integer')
+ {return {type: 'integer', format: 'int64'};}
+ else if(lcType === 'float' || (lcType === 'number' && format === 'float'))
+ {return {type: 'number', format: 'float'};}
+ else if(lcType === 'double' || (lcType === 'number' && format === 'double'))
+ {return {type: 'number', format: 'double'};}
+ else if((lcType === 'string' && format === 'date-time') || (lcType === 'date'))
+ {return {type: 'string', format: 'date-time'};}
+ else if(lcType === 'string')
+ {return {type: 'string'};}
+ else if(lcType === 'file')
+ {return {type: 'file'};}
+ else if(lcType === 'boolean')
+ {return {type: 'boolean'};}
+ else if(lcType === 'array' || lcType === 'list') {
+ if(source.items) {
+ var it = this.toJsonSchema(source.items);
+ return {type: 'array', items: it};
+ }
+ else {
+ return {type: 'array', items: {type: 'object'}};
+ }
+ }
+ else if(source.$ref) {
+ return {$ref: '#/definitions/' + this.modelMap[source.$ref] || source.$ref};
+ }
+ else {
+ return {$ref: '#/definitions/' + this.modelMap[source.type] || source.type};
+ }
+};
+
+SwaggerSpecConverter.prototype.resourceListing = function(obj, swagger, callback) {
+ var i, processedCount = 0;
+ var self = this;
+ var expectedCount = obj.apis.length;
+ var _swagger = swagger;
+
+ if(expectedCount === 0) {
+ this.finish(callback, swagger);
+ }
+
+ for(i = 0; i < expectedCount; i++) {
+ var api = obj.apis[i];
+ var path = api.path;
+ var absolutePath = this.getAbsolutePath(obj.swaggerVersion, this.docLocation, path);
+
+ if(api.description) {
+ swagger.tags = swagger.tags || [];
+ swagger.tags.push({
+ name : this.extractTag(api.path),
+ description : api.description || ''
+ });
+ };
+ var http = {
+ url: absolutePath,
+ headers: {accept: 'application/json'},
+ on: {},
+ method: 'get'
+ };
+ http.on.response = function(data) {
+ processedCount += 1;
+ if(data.obj) {
+ self.declaration(data.obj, _swagger);
+ }
+ if(processedCount === expectedCount) {
+ self.finish(callback, _swagger);
+ }
+ };
+ http.on.error = function(data) {
+ console.error(data);
+ processedCount += 1;
+ if(processedCount === expectedCount) {
+ self.finish(callback, _swagger);
+ }
+ };
+ new SwaggerHttp().execute(http);
+ }
+};
+
+SwaggerSpecConverter.prototype.getAbsolutePath = function(version, docLocation, path) {
+ if(version === '1.0') {
+ if(docLocation.endsWith('.json')) {
+ // get root path
+ var pos = docLocation.lastIndexOf('/');
+ if(pos > 0) {
+ docLocation = docLocation.substring(0, pos);
+ }
+ }
+ }
+
+ var location = docLocation;
+ if(path.indexOf('http://') === 0 || path.indexOf('https://') === 0) {
+ location = path;
+ }
+ else {
+ if(docLocation.endsWith('/')) {
+ location = docLocation.substring(0, docLocation.length - 1);
+ }
+ location += path;
+ }
+ location = location.replace('{format}', 'json');
+ return location;
+};
+
+SwaggerSpecConverter.prototype.securityDefinitions = function(obj, swagger) {
+ if(obj.authorizations) {
+ var name;
+ for(name in obj.authorizations) {
+ var isValid = false;
+ var securityDefinition = {};
+ var definition = obj.authorizations[name];
+ if(definition.type === 'apiKey') {
+ securityDefinition.type = 'apiKey';
+ securityDefinition.in = definition.passAs;
+ securityDefinition.name = definition.keyname || name;
+ isValid = true;
+ }
+ else if(definition.type === 'oauth2') {
+ var existingScopes = definition.scopes || [];
+ var scopes = {};
+ var i;
+ for(i in existingScopes) {
+ var scope = existingScopes[i];
+ scopes[scope.scope] = scope.description;
+ }
+ securityDefinition.type = 'oauth2';
+ if(i > 0) {
+ securityDefinition.scopes = scopes;
+ }
+ if(definition.grantTypes) {
+ if(definition.grantTypes.implicit) {
+ var implicit = definition.grantTypes.implicit;
+ securityDefinition.flow = 'implicit';
+ securityDefinition.authorizationUrl = implicit.loginEndpoint;
+ isValid = true;
+ }
+ if(definition.grantTypes.authorization_code) {
+ if(!securityDefinition.flow) {
+ // cannot set if flow is already defined
+ var authCode = definition.grantTypes.authorization_code;
+ securityDefinition.flow = 'accessCode';
+ securityDefinition.authorizationUrl = authCode.tokenRequestEndpoint.url;
+ securityDefinition.tokenUrl = authCode.tokenEndpoint.url;
+ isValid = true;
+ }
+ }
+ }
+ }
+ if(isValid) {
+ swagger.securityDefinitions = swagger.securityDefinitions || {};
+ swagger.securityDefinitions[name] = securityDefinition;
+ }
+ }
+ }
+};
+
+SwaggerSpecConverter.prototype.apiInfo = function(obj, swagger) {
+ // info section
+ if(obj.info) {
+ var info = obj.info;
+ swagger.info = {};
+
+ if(info.contact) {
+ swagger.info.contact = {};
+ swagger.info.contact.email = info.contact;
+ }
+ if(info.description) {
+ swagger.info.description = info.description;
+ }
+ if(info.title) {
+ swagger.info.title = info.title;
+ }
+ if(info.termsOfServiceUrl) {
+ swagger.info.termsOfService = info.termsOfServiceUrl;
+ }
+ if(info.license || info.licenseUrl) {
+ swagger.license = {};
+ if(info.license) {
+ swagger.license.name = info.license;
+ }
+ if(info.licenseUrl) {
+ swagger.license.url = info.licenseUrl;
+ }
+ }
+ }
+ else {
+ this.warnings.push('missing info section');
+ }
+};
+
+SwaggerSpecConverter.prototype.finish = function (callback, obj) {
+ callback(obj);
+};
+},{"./client":3,"./http":5}],8:[function(require,module,exports){
+'use strict';
+
+var _ = {
+ forEach: require('lodash-compat/collection/forEach'),
+ indexOf: require('lodash-compat/array/indexOf'),
+ isArray: require('lodash-compat/lang/isArray'),
+ isPlainObject: require('lodash-compat/lang/isPlainObject'),
+ isString: require('lodash-compat/lang/isString'),
+ isUndefined: require('lodash-compat/lang/isUndefined'),
+ keys: require('lodash-compat/object/keys'),
+ map: require('lodash-compat/collection/map')
+};
+var helpers = require('../helpers');
+
+
+/**
+ * allows override of the default value based on the parameter being
+ * supplied
+ **/
+var applyParameterMacro = function (operation, parameter) {
+ // TODO the reference to operation.api is not available
+ if (operation.api && operation.api.parameterMacro) {
+ return operation.api.parameterMacro(operation, parameter);
+ } else {
+ return parameter.defaultValue;
+ }
+};
+
+/**
+ * allows overriding the default value of an model property
+ **/
+var applyModelPropertyMacro = function (model, property) {
+ // TODO the reference to model.api is not available
+ if (model.api && model.api.modelPropertyMacro) {
+ return model.api.modelPropertyMacro(model, property);
+ } else {
+ return property.default;
+ }
+};
+
+var Model = module.exports = function (name, definition, models) {
+ this.definition = definition || {};
+ this.isArray = definition.type === 'array';
+ this.models = models || {};
+ this.name = name || 'Inline Model';
+
+ return this;
+};
+
+var schemaToHTML = function (name, schema, models) {
+ var strongOpen = '<span class="strong">';
+ var strongClose = '</span>';
+ var references = {};
+ var seenModels = [];
+ var inlineModels = 0;
+ var addReference = function (schema, name, skipRef) {
+ var modelName = name;
+ var model;
+
+ if (schema.$ref) {
+ modelName = helpers.simpleRef(schema.$ref);
+ model = models[modelName];
+ } else if (_.isUndefined(name)) {
+ modelName = 'Inline Model ' + (++inlineModels);
+ model = new Model(modelName, schema, models);
+ }
+
+ if (skipRef !== true) {
+ references[modelName] = _.isUndefined(model) ? {} : model.definition;
+ }
+
+ return modelName;
+ };
+ var primitiveToHTML = function (schema) {
+ var html = '<span class="propType">';
+ var type = schema.type || 'object';
+
+ if (schema.$ref) {
+ html += addReference(schema, helpers.simpleRef(schema.$ref));
+ } else if (type === 'object') {
+ if (!_.isUndefined(schema.properties)) {
+ html += addReference(schema);
+ } else {
+ html += 'object';
+ }
+ } else if (type === 'array') {
+ html += 'Array[';
+
+ if (_.isArray(schema.items)) {
+ html += _.map(schema.items, addReference).join(',');
+ } else if (_.isPlainObject(schema.items)) {
+ if (_.isUndefined(schema.items.$ref)) {
+ if (!_.isUndefined(schema.items.type) && _.indexOf(['array', 'object'], schema.items.type) === -1) {
+ html += schema.items.type;
+ } else {
+ html += addReference(schema.items);
+ }
+ } else {
+ html += addReference(schema.items, helpers.simpleRef(schema.items.$ref));
+ }
+ } else {
+ helpers.log('Array type\'s \'items\' schema is not an array or an object, cannot process');
+ html += 'object';
+ }
+
+ html += ']';
+ } else {
+ html += schema.type;
+ }
+
+ html += '</span>';
+
+ return html;
+ };
+ var primitiveToOptionsHTML = function (schema, html) {
+ var options = '';
+ var type = schema.type || 'object';
+ var isArray = type === 'array';
+
+ if (isArray) {
+ if (_.isPlainObject(schema.items) && !_.isUndefined(schema.items.type)) {
+ type = schema.items.type;
+ } else {
+ type = 'object';
+ }
+ }
+
+ if (schema.default) {
+ options += helpers.optionHtml('Default', schema.default);
+ }
+
+ switch (type) {
+ case 'string':
+ if (schema.minLength) {
+ options += helpers.optionHtml('Min. Length', schema.minLength);
+ }
+
+ if (schema.maxLength) {
+ options += helpers.optionHtml('Max. Length', schema.maxLength);
+ }
+
+ if (schema.pattern) {
+ options += helpers.optionHtml('Reg. Exp.', schema.pattern);
+ }
+ break;
+ case 'integer':
+ case 'number':
+ if (schema.minimum) {
+ options += helpers.optionHtml('Min. Value', schema.minimum);
+ }
+
+ if (schema.exclusiveMinimum) {
+ options += helpers.optionHtml('Exclusive Min.', 'true');
+ }
+
+ if (schema.maximum) {
+ options += helpers.optionHtml('Max. Value', schema.maximum);
+ }
+
+ if (schema.exclusiveMaximum) {
+ options += helpers.optionHtml('Exclusive Max.', 'true');
+ }
+
+ if (schema.multipleOf) {
+ options += helpers.optionHtml('Multiple Of', schema.multipleOf);
+ }
+
+ break;
+ }
+
+ if (isArray) {
+ if (schema.minItems) {
+ options += helpers.optionHtml('Min. Items', schema.minItems);
+ }
+
+ if (schema.maxItems) {
+ options += helpers.optionHtml('Max. Items', schema.maxItems);
+ }
+
+ if (schema.uniqueItems) {
+ options += helpers.optionHtml('Unique Items', 'true');
+ }
+
+ if (schema.collectionFormat) {
+ options += helpers.optionHtml('Coll. Format', schema.collectionFormat);
+ }
+ }
+
+ if (_.isUndefined(schema.items)) {
+ if (_.isArray(schema.enum)) {
+ var enumString;
+
+ if (type === 'number' || type === 'integer') {
+ enumString = schema.enum.join(', ');
+ } else {
+ enumString = '"' + schema.enum.join('", "') + '"';
+ }
+
+ options += helpers.optionHtml('Enum', enumString);
+ }
+ }
+
+ if (options.length > 0) {
+ html = '<span class="propWrap">' + html + '<table class="optionsWrapper"><tr><th colspan="2">' + type + '</th></tr>' + options + '</table></span>';
+ }
+
+ return html;
+ };
+ var processModel = function (schema, name) {
+ var type = schema.type || 'object';
+ var isArray = schema.type === 'array';
+ var html = strongOpen + name + ' ' + (isArray ? '[' : '{') + strongClose;
+
+ if (name) {
+ seenModels.push(name);
+ }
+
+ if (isArray) {
+ if (_.isArray(schema.items)) {
+ html += '<div>' + _.map(schema.items, function (item) {
+ var type = item.type || 'object';
+
+ if (_.isUndefined(item.$ref)) {
+ if (_.indexOf(['array', 'object'], type) > -1) {
+ if (type === 'object' && _.isUndefined(item.properties)) {
+ return 'object';
+ } else {
+ return addReference(item);
+ }
+ } else {
+ return primitiveToOptionsHTML(item, type);
+ }
+ } else {
+ return addReference(item, helpers.simpleRef(item.$ref));
+ }
+ }).join(',</div><div>');
+ } else if (_.isPlainObject(schema.items)) {
+ if (_.isUndefined(schema.items.$ref)) {
+ if (_.indexOf(['array', 'object'], schema.items.type || 'object') > -1) {
+ if ((_.isUndefined(schema.items.type) || schema.items.type === 'object') && _.isUndefined(schema.items.properties)) {
+ html += '<div>object</div>';
+ } else {
+ html += '<div>' + addReference(schema.items) + '</div>';
+ }
+ } else {
+ html += '<div>' + primitiveToOptionsHTML(schema.items, schema.items.type) + '</div>';
+ }
+ } else {
+ html += '<div>' + addReference(schema.items, helpers.simpleRef(schema.items.$ref)) + '</div>';
+ }
+ } else {
+ helpers.log('Array type\'s \'items\' property is not an array or an object, cannot process');
+ html += '<div>object</div>';
+ }
+ } else {
+ if (schema.$ref) {
+ html += '<div>' + addReference(schema, name) + '</div>';
+ } else if (type === 'object') {
+ html += '<div>';
+
+ if (_.isPlainObject(schema.properties)) {
+ html += _.map(schema.properties, function (property, name) {
+ var required = _.indexOf(schema.required || [], name) > -1;
+ var html = '<span class="propName ' + required + '">' + name + '</span> (';
+
+ html += primitiveToHTML(property);
+
+ if (!required) {
+ html += ', <span class="propOptKey">optional</span>';
+ }
+
+ html += ')';
+
+ if (!_.isUndefined(property.description)) {
+ html += ': ' + property.description;
+ }
+
+ if (property.enum) {
+ html += ' = <span class="propVals">[\'' + property.enum.join('\' or \'') + '\']</span>';
+ }
+
+ return primitiveToOptionsHTML(property, html);
+ }).join(',</div><div>');
+ }
+
+ html += '</div>';
+ } else {
+ html = '<div>' + primitiveToOptionsHTML(schema, type) + '</div>';
+ }
+ }
+
+ return html + strongOpen + (isArray ? ']' : '}') + strongClose;
+ };
+
+
+ // Generate current HTML
+ var html = processModel(schema, name);
+
+ // Generate references HTML
+ while (_.keys(references).length > 0) {
+ _.forEach(references, function (schema, name) {
+ var seenModel = _.indexOf(seenModels, name) > -1;
+
+ delete references[name];
+
+ if (!seenModel) {
+ seenModels.push(name);
+
+ html += '<br />' + processModel(schema, name);
+ }
+ });
+ }
+
+ return html;
+};
+
+var schemaToJSON = function (schema, models, modelsToIgnore) {
+ var type = schema.type || 'object';
+ var model;
+ var output;
+
+ if (schema.example) {
+ output = schema.example;
+ } else if (_.isUndefined(schema.items) && _.isArray(schema.enum)) {
+ output = schema.enum[0];
+ }
+
+ if (_.isUndefined(output)) {
+ if (schema.$ref) {
+ model = models[helpers.simpleRef(schema.$ref)];
+
+ if (!_.isUndefined(model)) {
+ if (_.isUndefined(modelsToIgnore[model.name])) {
+ modelsToIgnore[model.name] = model;
+ output = schemaToJSON(model.definition, models, modelsToIgnore);
+ delete modelsToIgnore[model.name];
+ } else {
+ if (model.type === 'array') {
+ output = [];
+ } else {
+ output = {};
+ }
+ }
+ }
+ } else if (schema.default) {
+ output = schema.default;
+ } else if (type === 'date-time') {
+ output = new Date().toISOString();
+ } else if (type === 'date') {
+ output = new Date().toISOString().split('T')[0];
+ } else if (type === 'string') {
+ output = 'string';
+ } else if (type === 'integer') {
+ output = 0;
+ } else if (type === 'long') {
+ output = 0;
+ } else if (type === 'float') {
+ output = 0.0;
+ } else if (type === 'double') {
+ output = 0.0;
+ } else if (type === 'boolean') {
+ output = true;
+ } else if (type === 'object') {
+ output = {};
+
+ _.forEach(schema.properties, function (property, name) {
+ output[name] = schemaToJSON(property, models, modelsToIgnore);
+ });
+ } else if (type === 'array') {
+ output = [];
+
+ if (_.isArray(schema.items)) {
+ _.forEach(schema.items, function (item) {
+ output.push(schemaToJSON(item, models, modelsToIgnore));
+ });
+ } else if (_.isPlainObject(schema.items)) {
+ output.push(schemaToJSON(schema.items, models, modelsToIgnore));
+ } else if (_.isUndefined(schema.items)) {
+ output.push({});
+ } else {
+ helpers.log('Array type\'s \'items\' property is not an array or an object, cannot process');
+ }
+ }
+ }
+
+ return output;
+};
+
+Model.prototype.createJSONSample = Model.prototype.getSampleValue = function (modelsToIgnore) {
+ modelsToIgnore = modelsToIgnore || {};
+
+ modelsToIgnore[this.name] = this;
+
+ // Response support
+ if (this.examples && _.isPlainObject(this.examples) && this.examples['application/json']) {
+ this.definition.example = this.examples['application/json'];
+
+ if (_.isString(this.definition.example)) {
+ this.definition.example = JSON.parse(this.definition.example);
+ }
+ } else {
+ this.definition.example = this.examples;
+ }
+
+
+ return schemaToJSON(this.definition, this.models, modelsToIgnore);
+};
+
+Model.prototype.getMockSignature = function () {
+ return schemaToHTML(this.name, this.definition, this.models);
+};
+
+},{"../helpers":4,"lodash-compat/array/indexOf":19,"lodash-compat/collection/forEach":22,"lodash-compat/collection/map":23,"lodash-compat/lang/isArray":96,"lodash-compat/lang/isPlainObject":100,"lodash-compat/lang/isString":101,"lodash-compat/lang/isUndefined":103,"lodash-compat/object/keys":104}],9:[function(require,module,exports){
+'use strict';
+
+var _ = {
+ isUndefined: require('lodash-compat/lang/isUndefined')
+};
+var helpers = require('../helpers');
+var Model = require('./model');
+var SwaggerHttp = require('../http');
+
+var Operation = module.exports = function (parent, scheme, operationId, httpMethod, path, args, definitions, models, clientAuthorizations) {
+ var errors = [];
+
+ parent = parent || {};
+ args = args || {};
+
+ this.authorizations = args.security;
+ this.basePath = parent.basePath || '/';
+ this.clientAuthorizations = clientAuthorizations;
+ this.consumes = args.consumes;
+ this.deprecated = args.deprecated;
+ this.description = args.description;
+ this.host = parent.host || 'localhost';
+ this.method = (httpMethod || errors.push('Operation ' + operationId + ' is missing method.'));
+ this.models = models || {};
+ this.nickname = (operationId || errors.push('Operations must have a nickname.'));
+ this.operation = args;
+ this.operations = {};
+ this.parameters = args !== null ? (args.parameters || []) : {};
+ this.parent = parent;
+ this.path = (path || errors.push('Operation ' + this.nickname + ' is missing path.'));
+ this.produces = args.produces;
+ this.responses = (args.responses || {});
+ this.scheme = scheme || parent.scheme || 'http';
+ this.schemes = parent.schemes;
+ this.security = args.security;
+ this.summary = args.summary || '';
+ this.type = null;
+ this.useJQuery = parent.useJQuery;
+
+ if (typeof this.deprecated === 'string') {
+ switch(this.deprecated.toLowerCase()) {
+ case 'true': case 'yes': case '1': {
+ this.deprecated = true;
+ break;
+ }
+
+ case 'false': case 'no': case '0': case null: {
+ this.deprecated = false;
+ break;
+ }
+
+ default: this.deprecated = Boolean(this.deprecated);
+ }
+ }
+
+ var i, model;
+
+ if (definitions) {
+ // add to global models
+ var key;
+
+ for (key in this.definitions) {
+ model = new Model(key, definitions[key], this.models);
+
+ if (model) {
+ this.models[key] = model;
+ }
+ }
+ }
+ for (i = 0; i < this.parameters.length; i++) {
+ var param = this.parameters[i];
+
+ if (param.type === 'array') {
+ param.isList = true;
+ param.allowMultiple = true;
+ }
+
+ var innerType = this.getType(param);
+
+ if (innerType && innerType.toString().toLowerCase() === 'boolean') {
+ param.allowableValues = {};
+ param.isList = true;
+ param['enum'] = ['true', 'false'];
+ }
+
+ if (typeof param['enum'] !== 'undefined') {
+ var id;
+
+ param.allowableValues = {};
+ param.allowableValues.values = [];
+ param.allowableValues.descriptiveValues = [];
+
+ for (id = 0; id < param['enum'].length; id++) {
+ var value = param['enum'][id];
+ var isDefault = (value === param.default) ? true : false;
+
+ param.allowableValues.values.push(value);
+ param.allowableValues.descriptiveValues.push({value : value, isDefault: isDefault});
+ }
+ }
+
+ if (param.type === 'array') {
+ innerType = [innerType];
+
+ if (typeof param.allowableValues === 'undefined') {
+ // can't show as a list if no values to select from
+ delete param.isList;
+ delete param.allowMultiple;
+ }
+ }
+
+ param.signature = this.getModelSignature(innerType, this.models).toString();
+ param.sampleJSON = this.getModelSampleJSON(innerType, this.models);
+ param.responseClassSignature = param.signature;
+ }
+
+ var defaultResponseCode, response, responses = this.responses;
+
+ if (responses['200']) {
+ response = responses['200'];
+ defaultResponseCode = '200';
+ } else if (responses['201']) {
+ response = responses['201'];
+ defaultResponseCode = '201';
+ } else if (responses['202']) {
+ response = responses['202'];
+ defaultResponseCode = '202';
+ } else if (responses['203']) {
+ response = responses['203'];
+ defaultResponseCode = '203';
+ } else if (responses['204']) {
+ response = responses['204'];
+ defaultResponseCode = '204';
+ } else if (responses['205']) {
+ response = responses['205'];
+ defaultResponseCode = '205';
+ } else if (responses['206']) {
+ response = responses['206'];
+ defaultResponseCode = '206';
+ } else if (responses['default']) {
+ response = responses['default'];
+ defaultResponseCode = 'default';
+ }
+
+ if (response && response.schema) {
+ var resolvedModel = this.resolveModel(response.schema, definitions);
+ var successResponse;
+
+ delete responses[defaultResponseCode];
+
+ if (resolvedModel) {
+ this.successResponse = {};
+ successResponse = this.successResponse[defaultResponseCode] = resolvedModel;
+ } else if (!response.schema.type || response.schema.type === 'object' || response.schema.type === 'array') {
+ // Inline model
+ this.successResponse = {};
+ successResponse = this.successResponse[defaultResponseCode] = new Model(undefined, response.schema || {}, this.models);
+ } else {
+ // Primitive
+ this.successResponse = {};
+ successResponse = this.successResponse[defaultResponseCode] = response.schema;
+ }
+
+ if (successResponse) {
+ // Attach response properties
+ if (response.description) {
+ successResponse.description = response.description;
+ }
+
+ if (response.examples) {
+ successResponse.examples = response.examples;
+ }
+
+ if (response.headers) {
+ successResponse.headers = response.headers;
+ }
+ }
+
+ this.type = response;
+ }
+
+ if (errors.length > 0) {
+ if (this.resource && this.resource.api && this.resource.api.fail) {
+ this.resource.api.fail(errors);
+ }
+ }
+
+ return this;
+};
+
+Operation.prototype.getType = function (param) {
+ var type = param.type;
+ var format = param.format;
+ var isArray = false;
+ var str;
+
+ if (type === 'integer' && format === 'int32') {
+ str = 'integer';
+ } else if (type === 'integer' && format === 'int64') {
+ str = 'long';
+ } else if (type === 'integer') {
+ str = 'integer';
+ } else if (type === 'string') {
+ if (format === 'date-time') {
+ str = 'date-time';
+ } else if (format === 'date') {
+ str = 'date';
+ } else {
+ str = 'string';
+ }
+ } else if (type === 'number' && format === 'float') {
+ str = 'float';
+ } else if (type === 'number' && format === 'double') {
+ str = 'double';
+ } else if (type === 'number') {
+ str = 'double';
+ } else if (type === 'boolean') {
+ str = 'boolean';
+ } else if (type === 'array') {
+ isArray = true;
+
+ if (param.items) {
+ str = this.getType(param.items);
+ }
+ }
+
+ if (param.$ref) {
+ str = param.$ref;
+ }
+
+ var schema = param.schema;
+
+ if (schema) {
+ var ref = schema.$ref;
+
+ if (ref) {
+ ref = helpers.simpleRef(ref);
+
+ if (isArray) {
+ return [ ref ];
+ } else {
+ return ref;
+ }
+ } else {
+ return this.getType(schema);
+ }
+ }
+ if (isArray) {
+ return [ str ];
+ } else {
+ return str;
+ }
+};
+
+Operation.prototype.resolveModel = function (schema, definitions) {
+ if (typeof schema.$ref !== 'undefined') {
+ var ref = schema.$ref;
+
+ if (ref.indexOf('#/definitions/') === 0) {
+ ref = ref.substring('#/definitions/'.length);
+ }
+
+ if (definitions[ref]) {
+ return new Model(ref, definitions[ref], this.models);
+ }
+ } else if (schema.type === 'object' || _.isUndefined(schema.type)) {
+ return new Model(undefined, schema, this.models);
+ }
+
+ return null;
+};
+
+Operation.prototype.help = function (dontPrint) {
+ var out = this.nickname + ': ' + this.summary + '\n';
+
+ for (var i = 0; i < this.parameters.length; i++) {
+ var param = this.parameters[i];
+ var typeInfo = param.signature;
+
+ out += '\n * ' + param.name + ' (' + typeInfo + '): ' + param.description;
+ }
+
+ if (typeof dontPrint === 'undefined') {
+ helpers.log(out);
+ }
+
+ return out;
+};
+
+Operation.prototype.getModelSignature = function (type, definitions) {
+ var isPrimitive, listType;
+
+ if (type instanceof Array) {
+ listType = true;
+ type = type[0];
+ } else if (typeof type === 'undefined') {
+ type = 'undefined';
+ }
+
+ if (type === 'string') {
+ isPrimitive = true;
+ } else {
+ isPrimitive = (listType && definitions[listType]) || (definitions[type]) ? false : true;
+ }
+
+ if (isPrimitive) {
+ if (listType) {
+ return 'Array[' + type + ']';
+ } else {
+ return type.toString();
+ }
+ } else {
+ if (listType) {
+ return 'Array[' + definitions[type].getMockSignature() + ']';
+ } else {
+ return definitions[type].getMockSignature();
+ }
+ }
+};
+
+Operation.prototype.supportHeaderParams = function () {
+ return true;
+};
+
+Operation.prototype.supportedSubmitMethods = function () {
+ return this.parent.supportedSubmitMethods;
+};
+
+Operation.prototype.getHeaderParams = function (args) {
+ var headers = this.setContentTypes(args, {});
+
+ for (var i = 0; i < this.parameters.length; i++) {
+ var param = this.parameters[i];
+
+ if (typeof args[param.name] !== 'undefined') {
+ if (param.in === 'header') {
+ var value = args[param.name];
+
+ if (Array.isArray(value)) {
+ value = value.toString();
+ }
+
+ headers[param.name] = value;
+ }
+ }
+ }
+
+ return headers;
+};
+
+Operation.prototype.urlify = function (args) {
+ var formParams = {};
+ var requestUrl = this.path;
+ var querystring = ''; // grab params from the args, build the querystring along the way
+
+ for (var i = 0; i < this.parameters.length; i++) {
+ var param = this.parameters[i];
+
+ if (typeof args[param.name] !== 'undefined') {
+ if (param.in === 'path') {
+ var reg = new RegExp('\{' + param.name + '\}', 'gi');
+ var value = args[param.name];
+
+ if (Array.isArray(value)) {
+ value = this.encodePathCollection(param.collectionFormat, param.name, value);
+ } else {
+ value = this.encodePathParam(value);
+ }
+
+ requestUrl = requestUrl.replace(reg, value);
+ } else if (param.in === 'query' && typeof args[param.name] !== 'undefined') {
+ if (querystring === '') {
+ querystring += '?';
+ } else {
+ querystring += '&';
+ }
+
+ if (typeof param.collectionFormat !== 'undefined') {
+ var qp = args[param.name];
+
+ if (Array.isArray(qp)) {
+ querystring += this.encodeQueryCollection(param.collectionFormat, param.name, qp);
+ } else {
+ querystring += this.encodeQueryParam(param.name) + '=' + this.encodeQueryParam(args[param.name]);
+ }
+ } else {
+ querystring += this.encodeQueryParam(param.name) + '=' + this.encodeQueryParam(args[param.name]);
+ }
+ } else if (param.in === 'formData') {
+ formParams[param.name] = args[param.name];
+ }
+ }
+ }
+ var url = this.scheme + '://' + this.host;
+
+ if (this.basePath !== '/') {
+ url += this.basePath;
+ }
+ return url + requestUrl + querystring;
+};
+
+Operation.prototype.getMissingParams = function (args) {
+ var missingParams = []; // check required params, track the ones that are missing
+ var i;
+
+ for (i = 0; i < this.parameters.length; i++) {
+ var param = this.parameters[i];
+
+ if (param.required === true) {
+ if (typeof args[param.name] === 'undefined') {
+ missingParams = param.name;
+ }
+ }
+ }
+
+ return missingParams;
+};
+
+Operation.prototype.getBody = function (headers, args, opts) {
+ var formParams = {}, body, key, value;
+
+ for (var i = 0; i < this.parameters.length; i++) {
+ var param = this.parameters[i];
+
+ if (typeof args[param.name] !== 'undefined') {
+ if (param.in === 'body') {
+ body = args[param.name];
+ } else if (param.in === 'formData') {
+ formParams[param.name] = args[param.name];
+ }
+ }
+ }
+
+ // handle form params
+ if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
+ var encoded = '';
+
+ for (key in formParams) {
+ value = formParams[key];
+
+ if (typeof value !== 'undefined') {
+ if (encoded !== '') {
+ encoded += '&';
+ }
+
+ encoded += encodeURIComponent(key) + '=' + encodeURIComponent(value);
+ }
+ }
+
+ body = encoded;
+ } else if (headers['Content-Type'] && headers['Content-Type'].indexOf('multipart/form-data') >= 0) {
+ if (opts.useJQuery) {
+ var bodyParam = new FormData();
+
+ bodyParam.type = 'formData';
+
+ for (key in formParams) {
+ value = args[key];
+
+ if (typeof value !== 'undefined') {
+ // required for jquery file upload
+ if (value.type === 'file' && value.value) {
+ delete headers['Content-Type'];
+
+ bodyParam.append(key, value.value);
+ } else {
+ bodyParam.append(key, value);
+ }
+ }
+ }
+
+ body = bodyParam;
+ }
+ }
+
+ return body;
+};
+
+/**
+ * gets sample response for a single operation
+ **/
+Operation.prototype.getModelSampleJSON = function (type, models) {
+ var isPrimitive, listType, sampleJson;
+
+ listType = (type instanceof Array);
+ isPrimitive = models[type] ? false : true;
+ sampleJson = isPrimitive ? void 0 : models[type].createJSONSample();
+
+ if (sampleJson) {
+ sampleJson = listType ? [sampleJson] : sampleJson;
+
+ if (typeof sampleJson === 'string') {
+ return sampleJson;
+ } else if (typeof sampleJson === 'object') {
+ var t = sampleJson;
+
+ if (sampleJson instanceof Array && sampleJson.length > 0) {
+ t = sampleJson[0];
+ }
+
+ if (t.nodeName) {
+ var xmlString = new XMLSerializer().serializeToString(t);
+
+ return this.formatXml(xmlString);
+ } else {
+ return JSON.stringify(sampleJson, null, 2);
+ }
+ } else {
+ return sampleJson;
+ }
+ }
+};
+
+/**
+ * legacy binding
+ **/
+Operation.prototype.do = function (args, opts, callback, error, parent) {
+ return this.execute(args, opts, callback, error, parent);
+};
+
+/**
+ * executes an operation
+ **/
+Operation.prototype.execute = function (arg1, arg2, arg3, arg4, parent) {
+ var args = arg1 || {};
+ var opts = {}, success, error;
+
+ if (typeof arg2 === 'object') {
+ opts = arg2;
+ success = arg3;
+ error = arg4;
+ }
+
+ if (typeof arg2 === 'function') {
+ success = arg2;
+ error = arg3;
+ }
+
+ success = (success || helpers.log);
+ error = (error || helpers.log);
+
+ if (opts.useJQuery) {
+ this.useJQuery = opts.useJQuery;
+ }
+
+ var missingParams = this.getMissingParams(args);
+
+ if (missingParams.length > 0) {
+ var message = 'missing required params: ' + missingParams;
+
+ helpers.fail(message);
+
+ return;
+ }
+
+ var allHeaders = this.getHeaderParams(args);
+ var contentTypeHeaders = this.setContentTypes(args, opts);
+ var headers = {}, attrname;
+
+ for (attrname in allHeaders) { headers[attrname] = allHeaders[attrname]; }
+ for (attrname in contentTypeHeaders) { headers[attrname] = contentTypeHeaders[attrname]; }
+
+ var body = this.getBody(headers, args, opts);
+ var url = this.urlify(args);
+
+ if(url.indexOf('.{format}') > 0) {
+ if(headers) {
+ var format = headers.Accept || headers.accept;
+ if(format && format.indexOf('json') > 0) {
+ url = url.replace('.{format}', '.json');
+ }
+ else if(format && format.indexOf('xml') > 0) {
+ url = url.replace('.{format}', '.xml');
+ }
+ }
+ }
+
+ var obj = {
+ url: url,
+ method: this.method.toUpperCase(),
+ body: body,
+ useJQuery: this.useJQuery,
+ headers: headers,
+ on: {
+ response: function (response) {
+ return success(response, parent);
+ },
+ error: function (response) {
+ return error(response, parent);
+ }
+ }
+ };
+
+ this.clientAuthorizations.apply(obj, this.operation.security);
+ if (opts.mock === true) {
+ return obj;
+ } else {
+ new SwaggerHttp().execute(obj, opts);
+ }
+};
+
+Operation.prototype.setContentTypes = function (args, opts) {
+ // default type
+ var accepts = 'application/json';
+ var allDefinedParams = this.parameters;
+ var body;
+ var consumes = args.parameterContentType || 'application/json';
+ var definedFileParams = [];
+ var definedFormParams = [];
+ var headers = {};
+ var i;
+
+ // get params from the operation and set them in definedFileParams, definedFormParams, headers
+ for (i = 0; i < allDefinedParams.length; i++) {
+ var param = allDefinedParams[i];
+
+ if (param.in === 'formData') {
+ if (param.type === 'file') {
+ definedFileParams.push(param);
+ } else {
+ definedFormParams.push(param);
+ }
+ } else if (param.in === 'header' && opts) {
+ var key = param.name;
+ var headerValue = opts[param.name];
+
+ if (typeof opts[param.name] !== 'undefined') {
+ headers[key] = headerValue;
+ }
+ } else if (param.in === 'body' && typeof args[param.name] !== 'undefined') {
+ body = args[param.name];
+ }
+ }
+
+ // if there's a body, need to set the consumes header via requestContentType
+ if (body && (this.method === 'post' || this.method === 'put' || this.method === 'patch' || this.method === 'delete')) {
+ if (opts.requestContentType) {
+ consumes = opts.requestContentType;
+ }
+ } else {
+ // if any form params, content type must be set
+ if (definedFormParams.length > 0) {
+ if (opts.requestContentType) { // override if set
+ consumes = opts.requestContentType;
+ } else if (definedFileParams.length > 0) { // if a file, must be multipart/form-data
+ consumes = 'multipart/form-data';
+ } else { // default to x-www-from-urlencoded
+ consumes = 'application/x-www-form-urlencoded';
+ }
+ } else if (this.type === 'DELETE') {
+ body = '{}';
+ } else if (this.type !== 'DELETE') {
+ consumes = null;
+ }
+ }
+
+ if (consumes && this.consumes) {
+ if (this.consumes.indexOf(consumes) === -1) {
+ helpers.log('server doesn\'t consume ' + consumes + ', try ' + JSON.stringify(this.consumes));
+ }
+ }
+
+ if (opts.responseContentType) {
+ accepts = opts.responseContentType;
+ } else {
+ accepts = 'application/json';
+ }
+
+ if (accepts && this.produces) {
+ if (this.produces.indexOf(accepts) === -1) {
+ helpers.log('server can\'t produce ' + accepts);
+ }
+ }
+
+ if ((consumes && body !== '') || (consumes === 'application/x-www-form-urlencoded')) {
+ headers['Content-Type'] = consumes;
+ }
+
+ if (accepts) {
+ headers.Accept = accepts;
+ }
+
+ return headers;
+};
+
+Operation.prototype.asCurl = function (args) {
+ var obj = this.execute(args, {mock: true});
+
+ this.clientAuthorizations.apply(obj);
+
+ var results = [];
+
+ results.push('-X ' + this.method.toUpperCase());
+
+ if (obj.headers) {
+ var key;
+
+ for (key in obj.headers) {
+ results.push('--header "' + key + ': ' + obj.headers[key] + '"');
+ }
+ }
+
+ if (obj.body) {
+ var body;
+
+ if (typeof obj.body === 'object') {
+ body = JSON.stringify(obj.body);
+ } else {
+ body = obj.body;
+ }
+
+ results.push('-d "' + body.replace(/"/g, '\\"') + '"');
+ }
+
+ return 'curl ' + (results.join(' ')) + ' "' + obj.url + '"';
+};
+
+Operation.prototype.encodePathCollection = function (type, name, value) {
+ var encoded = '';
+ var i;
+ var separator = '';
+
+ if (type === 'ssv') {
+ separator = '%20';
+ } else if (type === 'tsv') {
+ separator = '\\t';
+ } else if (type === 'pipes') {
+ separator = '|';
+ } else {
+ separator = ',';
+ }
+
+ for (i = 0; i < value.length; i++) {
+ if (i === 0) {
+ encoded = this.encodeQueryParam(value[i]);
+ } else {
+ encoded += separator + this.encodeQueryParam(value[i]);
+ }
+ }
+
+ return encoded;
+};
+
+Operation.prototype.encodeQueryCollection = function (type, name, value) {
+ var encoded = '';
+ var i;
+
+ if (type === 'default' || type === 'multi') {
+ for (i = 0; i < value.length; i++) {
+ if (i > 0) {encoded += '&';}
+
+ encoded += this.encodeQueryParam(name) + '=' + this.encodeQueryParam(value[i]);
+ }
+ } else {
+ var separator = '';
+
+ if (type === 'csv') {
+ separator = ',';
+ } else if (type === 'ssv') {
+ separator = '%20';
+ } else if (type === 'tsv') {
+ separator = '\\t';
+ } else if (type === 'pipes') {
+ separator = '|';
+ } else if (type === 'brackets') {
+ for (i = 0; i < value.length; i++) {
+ if (i !== 0) {
+ encoded += '&';
+ }
+
+ encoded += this.encodeQueryParam(name) + '[]=' + this.encodeQueryParam(value[i]);
+ }
+ }
+
+ if (separator !== '') {
+ for (i = 0; i < value.length; i++) {
+ if (i === 0) {
+ encoded = this.encodeQueryParam(name) + '=' + this.encodeQueryParam(value[i]);
+ } else {
+ encoded += separator + this.encodeQueryParam(value[i]);
+ }
+ }
+ }
+ }
+
+ return encoded;
+};
+
+Operation.prototype.encodeQueryParam = function (arg) {
+ return encodeURIComponent(arg);
+};
+
+/**
+ * TODO revisit, might not want to leave '/'
+ **/
+Operation.prototype.encodePathParam = function (pathParam) {
+ var encParts, parts, i, len;
+
+ pathParam = pathParam.toString();
+
+ if (pathParam.indexOf('/') === -1) {
+ return encodeURIComponent(pathParam);
+ } else {
+ parts = pathParam.split('/');
+ encParts = [];
+
+ for (i = 0, len = parts.length; i < len; i++) {
+ encParts.push(encodeURIComponent(parts[i]));
+ }
+
+ return encParts.join('/');
+ }
+};
+
+},{"../helpers":4,"../http":5,"./model":8,"lodash-compat/lang/isUndefined":103}],10:[function(require,module,exports){
+'use strict';
+
+var OperationGroup = module.exports = function (tag, description, externalDocs, operation) {
+ this.description = description;
+ this.externalDocs = externalDocs;
+ this.name = tag;
+ this.operation = operation;
+ this.operationsArray = [];
+ this.path = tag;
+ this.tag = tag;
+};
+
+OperationGroup.prototype.sort = function () {
+
+};
+
+
+},{}],11:[function(require,module,exports){
+/*!
+ * The buffer module from node.js, for the browser.
+ *
+ * @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
+ * @license MIT
+ */
+
+var base64 = require('base64-js')
+var ieee754 = require('ieee754')
+var isArray = require('is-array')
+
+exports.Buffer = Buffer
+exports.SlowBuffer = SlowBuffer
+exports.INSPECT_MAX_BYTES = 50
+Buffer.poolSize = 8192 // not used by this implementation
+
+var kMaxLength = 0x3fffffff
+var rootParent = {}
+
+/**
+ * If `Buffer.TYPED_ARRAY_SUPPORT`:
+ * === true Use Uint8Array implementation (fastest)
+ * === false Use Object implementation (most compatible, even IE6)
+ *
+ * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
+ * Opera 11.6+, iOS 4.2+.
+ *
+ * Note:
+ *
+ * - Implementation must support adding new properties to `Uint8Array` instances.
+ * Firefox 4-29 lacked support, fixed in Firefox 30+.
+ * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
+ *
+ * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
+ *
+ * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
+ * incorrect length in some situations.
+ *
+ * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will
+ * get the Object implementation, which is slower but will work correctly.
+ */
+Buffer.TYPED_ARRAY_SUPPORT = (function () {
+ try {
+ var buf = new ArrayBuffer(0)
+ var arr = new Uint8Array(buf)
+ arr.foo = function () { return 42 }
+ return arr.foo() === 42 && // typed array instances can be augmented
+ typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
+ new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
+ } catch (e) {
+ return false
+ }
+})()
+
+/**
+ * Class: Buffer
+ * =============
+ *
+ * The Buffer constructor returns instances of `Uint8Array` that are augmented
+ * with function properties for all the node `Buffer` API functions. We use
+ * `Uint8Array` so that square bracket notation works as expected -- it returns
+ * a single octet.
+ *
+ * By augmenting the instances, we can avoid modifying the `Uint8Array`
+ * prototype.
+ */
+function Buffer (subject, encoding) {
+ var self = this
+ if (!(self instanceof Buffer)) return new Buffer(subject, encoding)
+
+ var type = typeof subject
+ var length
+
+ if (type === 'number') {
+ length = +subject
+ } else if (type === 'string') {
+ length = Buffer.byteLength(subject, encoding)
+ } else if (type === 'object' && subject !== null) {
+ // assume object is array-like
+ if (subject.type === 'Buffer' && isArray(subject.data)) subject = subject.data
+ length = +subject.length
+ } else {
+ throw new TypeError('must start with number, buffer, array or string')
+ }
+
+ if (length > kMaxLength) {
+ throw new RangeError('Attempt to allocate Buffer larger than maximum size: 0x' +
+ kMaxLength.toString(16) + ' bytes')
+ }
+
+ if (length < 0) length = 0
+ else length >>>= 0 // coerce to uint32
+
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ // Preferred: Return an augmented `Uint8Array` instance for best performance
+ self = Buffer._augment(new Uint8Array(length)) // eslint-disable-line consistent-this
+ } else {
+ // Fallback: Return THIS instance of Buffer (created by `new`)
+ self.length = length
+ self._isBuffer = true
+ }
+
+ var i
+ if (Buffer.TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') {
+ // Speed optimization -- use set if we're copying from a typed array
+ self._set(subject)
+ } else if (isArrayish(subject)) {
+ // Treat array-ish objects as a byte array
+ if (Buffer.isBuffer(subject)) {
+ for (i = 0; i < length; i++) {
+ self[i] = subject.readUInt8(i)
+ }
+ } else {
+ for (i = 0; i < length; i++) {
+ self[i] = ((subject[i] % 256) + 256) % 256
+ }
+ }
+ } else if (type === 'string') {
+ self.write(subject, 0, encoding)
+ } else if (type === 'number' && !Buffer.TYPED_ARRAY_SUPPORT) {
+ for (i = 0; i < length; i++) {
+ self[i] = 0
+ }
+ }
+
+ if (length > 0 && length <= Buffer.poolSize) self.parent = rootParent
+
+ return self
+}
+
+function SlowBuffer (subject, encoding) {
+ if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding)
+
+ var buf = new Buffer(subject, encoding)
+ delete buf.parent
+ return buf
+}
+
+Buffer.isBuffer = function isBuffer (b) {
+ return !!(b != null && b._isBuffer)
+}
+
+Buffer.compare = function compare (a, b) {
+ if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
+ throw new TypeError('Arguments must be Buffers')
+ }
+
+ if (a === b) return 0
+
+ var x = a.length
+ var y = b.length
+ for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
+ if (i !== len) {
+ x = a[i]
+ y = b[i]
+ }
+ if (x < y) return -1
+ if (y < x) return 1
+ return 0
+}
+
+Buffer.isEncoding = function isEncoding (encoding) {
+ switch (String(encoding).toLowerCase()) {
+ case 'hex':
+ case 'utf8':
+ case 'utf-8':
+ case 'ascii':
+ case 'binary':
+ case 'base64':
+ case 'raw':
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return true
+ default:
+ return false
+ }
+}
+
+Buffer.concat = function concat (list, totalLength) {
+ if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.')
+
+ if (list.length === 0) {
+ return new Buffer(0)
+ } else if (list.length === 1) {
+ return list[0]
+ }
+
+ var i
+ if (totalLength === undefined) {
+ totalLength = 0
+ for (i = 0; i < list.length; i++) {
+ totalLength += list[i].length
+ }
+ }
+
+ var buf = new Buffer(totalLength)
+ var pos = 0
+ for (i = 0; i < list.length; i++) {
+ var item = list[i]
+ item.copy(buf, pos)
+ pos += item.length
+ }
+ return buf
+}
+
+Buffer.byteLength = function byteLength (str, encoding) {
+ var ret
+ str = str + ''
+ switch (encoding || 'utf8') {
+ case 'ascii':
+ case 'binary':
+ case 'raw':
+ ret = str.length
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = str.length * 2
+ break
+ case 'hex':
+ ret = str.length >>> 1
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = utf8ToBytes(str).length
+ break
+ case 'base64':
+ ret = base64ToBytes(str).length
+ break
+ default:
+ ret = str.length
+ }
+ return ret
+}
+
+// pre-set for values that may exist in the future
+Buffer.prototype.length = undefined
+Buffer.prototype.parent = undefined
+
+// toString(encoding, start=0, end=buffer.length)
+Buffer.prototype.toString = function toString (encoding, start, end) {
+ var loweredCase = false
+
+ start = start >>> 0
+ end = end === undefined || end === Infinity ? this.length : end >>> 0
+
+ if (!encoding) encoding = 'utf8'
+ if (start < 0) start = 0
+ if (end > this.length) end = this.length
+ if (end <= start) return ''
+
+ while (true) {
+ switch (encoding) {
+ case 'hex':
+ return hexSlice(this, start, end)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Slice(this, start, end)
+
+ case 'ascii':
+ return asciiSlice(this, start, end)
+
+ case 'binary':
+ return binarySlice(this, start, end)
+
+ case 'base64':
+ return base64Slice(this, start, end)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return utf16leSlice(this, start, end)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = (encoding + '').toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+Buffer.prototype.equals = function equals (b) {
+ if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
+ if (this === b) return true
+ return Buffer.compare(this, b) === 0
+}
+
+Buffer.prototype.inspect = function inspect () {
+ var str = ''
+ var max = exports.INSPECT_MAX_BYTES
+ if (this.length > 0) {
+ str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
+ if (this.length > max) str += ' ... '
+ }
+ return '<Buffer ' + str + '>'
+}
+
+Buffer.prototype.compare = function compare (b) {
+ if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
+ if (this === b) return 0
+ return Buffer.compare(this, b)
+}
+
+Buffer.prototype.indexOf = function indexOf (val, byteOffset) {
+ if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff
+ else if (byteOffset < -0x80000000) byteOffset = -0x80000000
+ byteOffset >>= 0
+
+ if (this.length === 0) return -1
+ if (byteOffset >= this.length) return -1
+
+ // Negative offsets start from the end of the buffer
+ if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0)
+
+ if (typeof val === 'string') {
+ if (val.length === 0) return -1 // special case: looking for empty string always fails
+ return String.prototype.indexOf.call(this, val, byteOffset)
+ }
+ if (Buffer.isBuffer(val)) {
+ return arrayIndexOf(this, val, byteOffset)
+ }
+ if (typeof val === 'number') {
+ if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') {
+ return Uint8Array.prototype.indexOf.call(this, val, byteOffset)
+ }
+ return arrayIndexOf(this, [ val ], byteOffset)
+ }
+
+ function arrayIndexOf (arr, val, byteOffset) {
+ var foundIndex = -1
+ for (var i = 0; byteOffset + i < arr.length; i++) {
+ if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) {
+ if (foundIndex === -1) foundIndex = i
+ if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex
+ } else {
+ foundIndex = -1
+ }
+ }
+ return -1
+ }
+
+ throw new TypeError('val must be string, number or Buffer')
+}
+
+// `get` will be removed in Node 0.13+
+Buffer.prototype.get = function get (offset) {
+ console.log('.get() is deprecated. Access using array indexes instead.')
+ return this.readUInt8(offset)
+}
+
+// `set` will be removed in Node 0.13+
+Buffer.prototype.set = function set (v, offset) {
+ console.log('.set() is deprecated. Access using array indexes instead.')
+ return this.writeUInt8(v, offset)
+}
+
+function hexWrite (buf, string, offset, length) {
+ offset = Number(offset) || 0
+ var remaining = buf.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+
+ // must be an even number of digits
+ var strLen = string.length
+ if (strLen % 2 !== 0) throw new Error('Invalid hex string')
+
+ if (length > strLen / 2) {
+ length = strLen / 2
+ }
+ for (var i = 0; i < length; i++) {
+ var parsed = parseInt(string.substr(i * 2, 2), 16)
+ if (isNaN(parsed)) throw new Error('Invalid hex string')
+ buf[offset + i] = parsed
+ }
+ return i
+}
+
+function utf8Write (buf, string, offset, length) {
+ var charsWritten = blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
+ return charsWritten
+}
+
+function asciiWrite (buf, string, offset, length) {
+ var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+function binaryWrite (buf, string, offset, length) {
+ return asciiWrite(buf, string, offset, length)
+}
+
+function base64Write (buf, string, offset, length) {
+ var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length)
+ return charsWritten
+}
+
+function utf16leWrite (buf, string, offset, length) {
+ var charsWritten = blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
+ return charsWritten
+}
+
+Buffer.prototype.write = function write (string, offset, length, encoding) {
+ // Support both (string, offset, length, encoding)
+ // and the legacy (string, encoding, offset, length)
+ if (isFinite(offset)) {
+ if (!isFinite(length)) {
+ encoding = length
+ length = undefined
+ }
+ } else { // legacy
+ var swap = encoding
+ encoding = offset
+ offset = length
+ length = swap
+ }
+
+ offset = Number(offset) || 0
+
+ if (length < 0 || offset < 0 || offset > this.length) {
+ throw new RangeError('attempt to write outside buffer bounds')
+ }
+
+ var remaining = this.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+ encoding = String(encoding || 'utf8').toLowerCase()
+
+ var ret
+ switch (encoding) {
+ case 'hex':
+ ret = hexWrite(this, string, offset, length)
+ break
+ case 'utf8':
+ case 'utf-8':
+ ret = utf8Write(this, string, offset, length)
+ break
+ case 'ascii':
+ ret = asciiWrite(this, string, offset, length)
+ break
+ case 'binary':
+ ret = binaryWrite(this, string, offset, length)
+ break
+ case 'base64':
+ ret = base64Write(this, string, offset, length)
+ break
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ ret = utf16leWrite(this, string, offset, length)
+ break
+ default:
+ throw new TypeError('Unknown encoding: ' + encoding)
+ }
+ return ret
+}
+
+Buffer.prototype.toJSON = function toJSON () {
+ return {
+ type: 'Buffer',
+ data: Array.prototype.slice.call(this._arr || this, 0)
+ }
+}
+
+function base64Slice (buf, start, end) {
+ if (start === 0 && end === buf.length) {
+ return base64.fromByteArray(buf)
+ } else {
+ return base64.fromByteArray(buf.slice(start, end))
+ }
+}
+
+function utf8Slice (buf, start, end) {
+ var res = ''
+ var tmp = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ if (buf[i] <= 0x7F) {
+ res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
+ tmp = ''
+ } else {
+ tmp += '%' + buf[i].toString(16)
+ }
+ }
+
+ return res + decodeUtf8Char(tmp)
+}
+
+function asciiSlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ ret += String.fromCharCode(buf[i] & 0x7F)
+ }
+ return ret
+}
+
+function binarySlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; i++) {
+ ret += String.fromCharCode(buf[i])
+ }
+ return ret
+}
+
+function hexSlice (buf, start, end) {
+ var len = buf.length
+
+ if (!start || start < 0) start = 0
+ if (!end || end < 0 || end > len) end = len
+
+ var out = ''
+ for (var i = start; i < end; i++) {
+ out += toHex(buf[i])
+ }
+ return out
+}
+
+function utf16leSlice (buf, start, end) {
+ var bytes = buf.slice(start, end)
+ var res = ''
+ for (var i = 0; i < bytes.length; i += 2) {
+ res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
+ }
+ return res
+}
+
+Buffer.prototype.slice = function slice (start, end) {
+ var len = this.length
+ start = ~~start
+ end = end === undefined ? len : ~~end
+
+ if (start < 0) {
+ start += len
+ if (start < 0) start = 0
+ } else if (start > len) {
+ start = len
+ }
+
+ if (end < 0) {
+ end += len
+ if (end < 0) end = 0
+ } else if (end > len) {
+ end = len
+ }
+
+ if (end < start) end = start
+
+ var newBuf
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ newBuf = Buffer._augment(this.subarray(start, end))
+ } else {
+ var sliceLen = end - start
+ newBuf = new Buffer(sliceLen, undefined)
+ for (var i = 0; i < sliceLen; i++) {
+ newBuf[i] = this[i + start]
+ }
+ }
+
+ if (newBuf.length) newBuf.parent = this.parent || this
+
+ return newBuf
+}
+
+/*
+ * Need to make sure that buffer isn't trying to write out of bounds.
+ */
+function checkOffset (offset, ext, length) {
+ if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
+ if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
+}
+
+Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ checkOffset(offset, byteLength, this.length)
+ }
+
+ var val = this[offset + --byteLength]
+ var mul = 1
+ while (byteLength > 0 && (mul *= 0x100)) {
+ val += this[offset + --byteLength] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ return this[offset]
+}
+
+Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return this[offset] | (this[offset + 1] << 8)
+}
+
+Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return (this[offset] << 8) | this[offset + 1]
+}
+
+Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return ((this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16)) +
+ (this[offset + 3] * 0x1000000)
+}
+
+Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] * 0x1000000) +
+ ((this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ this[offset + 3])
+}
+
+Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var i = byteLength
+ var mul = 1
+ var val = this[offset + --i]
+ while (i > 0 && (mul *= 0x100)) {
+ val += this[offset + --i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ if (!(this[offset] & 0x80)) return (this[offset])
+ return ((0xff - this[offset] + 1) * -1)
+}
+
+Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset] | (this[offset + 1] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset + 1] | (this[offset] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16) |
+ (this[offset + 3] << 24)
+}
+
+Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] << 24) |
+ (this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ (this[offset + 3])
+}
+
+Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, true, 23, 4)
+}
+
+Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, false, 23, 4)
+}
+
+Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, true, 52, 8)
+}
+
+Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, false, 52, 8)
+}
+
+function checkInt (buf, value, offset, ext, max, min) {
+ if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance')
+ if (value > max || value < min) throw new RangeError('value is out of bounds')
+ if (offset + ext > buf.length) throw new RangeError('index out of range')
+}
+
+Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
+
+ var mul = 1
+ var i = 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) >>> 0 & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
+
+ var i = byteLength - 1
+ var mul = 1
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) >>> 0 & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
+ if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
+ this[offset] = value
+ return offset + 1
+}
+
+function objectWriteUInt16 (buf, value, offset, littleEndian) {
+ if (value < 0) value = 0xffff + value + 1
+ for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
+ buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
+ (littleEndian ? i : 1 - i) * 8
+ }
+}
+
+Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = value
+ this[offset + 1] = (value >>> 8)
+ } else {
+ objectWriteUInt16(this, value, offset, true)
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 8)
+ this[offset + 1] = value
+ } else {
+ objectWriteUInt16(this, value, offset, false)
+ }
+ return offset + 2
+}
+
+function objectWriteUInt32 (buf, value, offset, littleEndian) {
+ if (value < 0) value = 0xffffffff + value + 1
+ for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
+ buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
+ }
+}
+
+Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset + 3] = (value >>> 24)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 1] = (value >>> 8)
+ this[offset] = value
+ } else {
+ objectWriteUInt32(this, value, offset, true)
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = value
+ } else {
+ objectWriteUInt32(this, value, offset, false)
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ checkInt(
+ this, value, offset, byteLength,
+ Math.pow(2, 8 * byteLength - 1) - 1,
+ -Math.pow(2, 8 * byteLength - 1)
+ )
+ }
+
+ var i = 0
+ var mul = 1
+ var sub = value < 0 ? 1 : 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ checkInt(
+ this, value, offset, byteLength,
+ Math.pow(2, 8 * byteLength - 1) - 1,
+ -Math.pow(2, 8 * byteLength - 1)
+ )
+ }
+
+ var i = byteLength - 1
+ var mul = 1
+ var sub = value < 0 ? 1 : 0
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
+ if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
+ if (value < 0) value = 0xff + value + 1
+ this[offset] = value
+ return offset + 1
+}
+
+Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = value
+ this[offset + 1] = (value >>> 8)
+ } else {
+ objectWriteUInt16(this, value, offset, true)
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 8)
+ this[offset + 1] = value
+ } else {
+ objectWriteUInt16(this, value, offset, false)
+ }
+ return offset + 2
+}
+
+Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = value
+ this[offset + 1] = (value >>> 8)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 3] = (value >>> 24)
+ } else {
+ objectWriteUInt32(this, value, offset, true)
+ }
+ return offset + 4
+}
+
+Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ if (value < 0) value = 0xffffffff + value + 1
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = value
+ } else {
+ objectWriteUInt32(this, value, offset, false)
+ }
+ return offset + 4
+}
+
+function checkIEEE754 (buf, value, offset, ext, max, min) {
+ if (value > max || value < min) throw new RangeError('value is out of bounds')
+ if (offset + ext > buf.length) throw new RangeError('index out of range')
+ if (offset < 0) throw new RangeError('index out of range')
+}
+
+function writeFloat (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 23, 4)
+ return offset + 4
+}
+
+Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, false, noAssert)
+}
+
+function writeDouble (buf, value, offset, littleEndian, noAssert) {
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 52, 8)
+ return offset + 8
+}
+
+Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, false, noAssert)
+}
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function copy (target, target_start, start, end) {
+ if (!start) start = 0
+ if (!end && end !== 0) end = this.length
+ if (target_start >= target.length) target_start = target.length
+ if (!target_start) target_start = 0
+ if (end > 0 && end < start) end = start
+
+ // Copy 0 bytes; we're done
+ if (end === start) return 0
+ if (target.length === 0 || this.length === 0) return 0
+
+ // Fatal error conditions
+ if (target_start < 0) {
+ throw new RangeError('targetStart out of bounds')
+ }
+ if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
+ if (end < 0) throw new RangeError('sourceEnd out of bounds')
+
+ // Are we oob?
+ if (end > this.length) end = this.length
+ if (target.length - target_start < end - start) {
+ end = target.length - target_start + start
+ }
+
+ var len = end - start
+
+ if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
+ for (var i = 0; i < len; i++) {
+ target[i + target_start] = this[i + start]
+ }
+ } else {
+ target._set(this.subarray(start, start + len), target_start)
+ }
+
+ return len
+}
+
+// fill(value, start=0, end=buffer.length)
+Buffer.prototype.fill = function fill (value, start, end) {
+ if (!value) value = 0
+ if (!start) start = 0
+ if (!end) end = this.length
+
+ if (end < start) throw new RangeError('end < start')
+
+ // Fill 0 bytes; we're done
+ if (end === start) return
+ if (this.length === 0) return
+
+ if (start < 0 || start >= this.length) throw new RangeError('start out of bounds')
+ if (end < 0 || end > this.length) throw new RangeError('end out of bounds')
+
+ var i
+ if (typeof value === 'number') {
+ for (i = start; i < end; i++) {
+ this[i] = value
+ }
+ } else {
+ var bytes = utf8ToBytes(value.toString())
+ var len = bytes.length
+ for (i = start; i < end; i++) {
+ this[i] = bytes[i % len]
+ }
+ }
+
+ return this
+}
+
+/**
+ * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
+ * Added in Node 0.12. Only available in browsers that support ArrayBuffer.
+ */
+Buffer.prototype.toArrayBuffer = function toArrayBuffer () {
+ if (typeof Uint8Array !== 'undefined') {
+ if (Buffer.TYPED_ARRAY_SUPPORT) {
+ return (new Buffer(this)).buffer
+ } else {
+ var buf = new Uint8Array(this.length)
+ for (var i = 0, len = buf.length; i < len; i += 1) {
+ buf[i] = this[i]
+ }
+ return buf.buffer
+ }
+ } else {
+ throw new TypeError('Buffer.toArrayBuffer not supported in this browser')
+ }
+}
+
+// HELPER FUNCTIONS
+// ================
+
+var BP = Buffer.prototype
+
+/**
+ * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
+ */
+Buffer._augment = function _augment (arr) {
+ arr.constructor = Buffer
+ arr._isBuffer = true
+
+ // save reference to original Uint8Array set method before overwriting
+ arr._set = arr.set
+
+ // deprecated, will be removed in node 0.13+
+ arr.get = BP.get
+ arr.set = BP.set
+
+ arr.write = BP.write
+ arr.toString = BP.toString
+ arr.toLocaleString = BP.toString
+ arr.toJSON = BP.toJSON
+ arr.equals = BP.equals
+ arr.compare = BP.compare
+ arr.indexOf = BP.indexOf
+ arr.copy = BP.copy
+ arr.slice = BP.slice
+ arr.readUIntLE = BP.readUIntLE
+ arr.readUIntBE = BP.readUIntBE
+ arr.readUInt8 = BP.readUInt8
+ arr.readUInt16LE = BP.readUInt16LE
+ arr.readUInt16BE = BP.readUInt16BE
+ arr.readUInt32LE = BP.readUInt32LE
+ arr.readUInt32BE = BP.readUInt32BE
+ arr.readIntLE = BP.readIntLE
+ arr.readIntBE = BP.readIntBE
+ arr.readInt8 = BP.readInt8
+ arr.readInt16LE = BP.readInt16LE
+ arr.readInt16BE = BP.readInt16BE
+ arr.readInt32LE = BP.readInt32LE
+ arr.readInt32BE = BP.readInt32BE
+ arr.readFloatLE = BP.readFloatLE
+ arr.readFloatBE = BP.readFloatBE
+ arr.readDoubleLE = BP.readDoubleLE
+ arr.readDoubleBE = BP.readDoubleBE
+ arr.writeUInt8 = BP.writeUInt8
+ arr.writeUIntLE = BP.writeUIntLE
+ arr.writeUIntBE = BP.writeUIntBE
+ arr.writeUInt16LE = BP.writeUInt16LE
+ arr.writeUInt16BE = BP.writeUInt16BE
+ arr.writeUInt32LE = BP.writeUInt32LE
+ arr.writeUInt32BE = BP.writeUInt32BE
+ arr.writeIntLE = BP.writeIntLE
+ arr.writeIntBE = BP.writeIntBE
+ arr.writeInt8 = BP.writeInt8
+ arr.writeInt16LE = BP.writeInt16LE
+ arr.writeInt16BE = BP.writeInt16BE
+ arr.writeInt32LE = BP.writeInt32LE
+ arr.writeInt32BE = BP.writeInt32BE
+ arr.writeFloatLE = BP.writeFloatLE
+ arr.writeFloatBE = BP.writeFloatBE
+ arr.writeDoubleLE = BP.writeDoubleLE
+ arr.writeDoubleBE = BP.writeDoubleBE
+ arr.fill = BP.fill
+ arr.inspect = BP.inspect
+ arr.toArrayBuffer = BP.toArrayBuffer
+
+ return arr
+}
+
+var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g
+
+function base64clean (str) {
+ // Node strips out invalid characters like \n and \t from the string, base64-js does not
+ str = stringtrim(str).replace(INVALID_BASE64_RE, '')
+ // Node converts strings with length < 2 to ''
+ if (str.length < 2) return ''
+ // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
+ while (str.length % 4 !== 0) {
+ str = str + '='
+ }
+ return str
+}
+
+function stringtrim (str) {
+ if (str.trim) return str.trim()
+ return str.replace(/^\s+|\s+$/g, '')
+}
+
+function isArrayish (subject) {
+ return isArray(subject) || Buffer.isBuffer(subject) ||
+ subject && typeof subject === 'object' &&
+ typeof subject.length === 'number'
+}
+
+function toHex (n) {
+ if (n < 16) return '0' + n.toString(16)
+ return n.toString(16)
+}
+
+function utf8ToBytes (string, units) {
+ units = units || Infinity
+ var codePoint
+ var length = string.length
+ var leadSurrogate = null
+ var bytes = []
+ var i = 0
+
+ for (; i < length; i++) {
+ codePoint = string.charCodeAt(i)
+
+ // is surrogate component
+ if (codePoint > 0xD7FF && codePoint < 0xE000) {
+ // last char was a lead
+ if (leadSurrogate) {
+ // 2 leads in a row
+ if (codePoint < 0xDC00) {
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ leadSurrogate = codePoint
+ continue
+ } else {
+ // valid surrogate pair
+ codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
+ leadSurrogate = null
+ }
+ } else {
+ // no lead yet
+
+ if (codePoint > 0xDBFF) {
+ // unexpected trail
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ } else if (i + 1 === length) {
+ // unpaired lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ } else {
+ // valid lead
+ leadSurrogate = codePoint
+ continue
+ }
+ }
+ } else if (leadSurrogate) {
+ // valid bmp char, but last char was a lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ leadSurrogate = null
+ }
+
+ // encode utf8
+ if (codePoint < 0x80) {
+ if ((units -= 1) < 0) break
+ bytes.push(codePoint)
+ } else if (codePoint < 0x800) {
+ if ((units -= 2) < 0) break
+ bytes.push(
+ codePoint >> 0x6 | 0xC0,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x10000) {
+ if ((units -= 3) < 0) break
+ bytes.push(
+ codePoint >> 0xC | 0xE0,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x200000) {
+ if ((units -= 4) < 0) break
+ bytes.push(
+ codePoint >> 0x12 | 0xF0,
+ codePoint >> 0xC & 0x3F | 0x80,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else {
+ throw new Error('Invalid code point')
+ }
+ }
+
+ return bytes
+}
+
+function asciiToBytes (str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push(str.charCodeAt(i) & 0xFF)
+ }
+ return byteArray
+}
+
+function utf16leToBytes (str, units) {
+ var c, hi, lo
+ var byteArray = []
+ for (var i = 0; i < str.length; i++) {
+ if ((units -= 2) < 0) break
+
+ c = str.charCodeAt(i)
+ hi = c >> 8
+ lo = c % 256
+ byteArray.push(lo)
+ byteArray.push(hi)
+ }
+
+ return byteArray
+}
+
+function base64ToBytes (str) {
+ return base64.toByteArray(base64clean(str))
+}
+
+function blitBuffer (src, dst, offset, length) {
+ for (var i = 0; i < length; i++) {
+ if ((i + offset >= dst.length) || (i >= src.length)) break
+ dst[i + offset] = src[i]
+ }
+ return i
+}
+
+function decodeUtf8Char (str) {
+ try {
+ return decodeURIComponent(str)
+ } catch (err) {
+ return String.fromCharCode(0xFFFD) // UTF 8 invalid char
+ }
+}
+
+},{"base64-js":12,"ieee754":13,"is-array":14}],12:[function(require,module,exports){
+var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+;(function (exports) {
+ 'use strict';
+
+ var Arr = (typeof Uint8Array !== 'undefined')
+ ? Uint8Array
+ : Array
+
+ var PLUS = '+'.charCodeAt(0)
+ var SLASH = '/'.charCodeAt(0)
+ var NUMBER = '0'.charCodeAt(0)
+ var LOWER = 'a'.charCodeAt(0)
+ var UPPER = 'A'.charCodeAt(0)
+ var PLUS_URL_SAFE = '-'.charCodeAt(0)
+ var SLASH_URL_SAFE = '_'.charCodeAt(0)
+
+ function decode (elt) {
+ var code = elt.charCodeAt(0)
+ if (code === PLUS ||
+ code === PLUS_URL_SAFE)
+ return 62 // '+'
+ if (code === SLASH ||
+ code === SLASH_URL_SAFE)
+ return 63 // '/'
+ if (code < NUMBER)
+ return -1 //no match
+ if (code < NUMBER + 10)
+ return code - NUMBER + 26 + 26
+ if (code < UPPER + 26)
+ return code - UPPER
+ if (code < LOWER + 26)
+ return code - LOWER + 26
+ }
+
+ function b64ToByteArray (b64) {
+ var i, j, l, tmp, placeHolders, arr
+
+ if (b64.length % 4 > 0) {
+ throw new Error('Invalid string. Length must be a multiple of 4')
+ }
+
+ // the number of equal signs (place holders)
+ // if there are two placeholders, than the two characters before it
+ // represent one byte
+ // if there is only one, then the three characters before it represent 2 bytes
+ // this is just a cheap hack to not do indexOf twice
+ var len = b64.length
+ placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
+
+ // base64 is 4/3 + up to two characters of the original data
+ arr = new Arr(b64.length * 3 / 4 - placeHolders)
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ l = placeHolders > 0 ? b64.length - 4 : b64.length
+
+ var L = 0
+
+ function push (v) {
+ arr[L++] = v
+ }
+
+ for (i = 0, j = 0; i < l; i += 4, j += 3) {
+ tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
+ push((tmp & 0xFF0000) >> 16)
+ push((tmp & 0xFF00) >> 8)
+ push(tmp & 0xFF)
+ }
+
+ if (placeHolders === 2) {
+ tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
+ push(tmp & 0xFF)
+ } else if (placeHolders === 1) {
+ tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
+ push((tmp >> 8) & 0xFF)
+ push(tmp & 0xFF)
+ }
+
+ return arr
+ }
+
+ function uint8ToBase64 (uint8) {
+ var i,
+ extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
+ output = "",
+ temp, length
+
+ function encode (num) {
+ return lookup.charAt(num)
+ }
+
+ function tripletToBase64 (num) {
+ return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
+ }
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
+ temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
+ output += tripletToBase64(temp)
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ switch (extraBytes) {
+ case 1:
+ temp = uint8[uint8.length - 1]
+ output += encode(temp >> 2)
+ output += encode((temp << 4) & 0x3F)
+ output += '=='
+ break
+ case 2:
+ temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
+ output += encode(temp >> 10)
+ output += encode((temp >> 4) & 0x3F)
+ output += encode((temp << 2) & 0x3F)
+ output += '='
+ break
+ }
+
+ return output
+ }
+
+ exports.toByteArray = b64ToByteArray
+ exports.fromByteArray = uint8ToBase64
+}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
+
+},{}],13:[function(require,module,exports){
+exports.read = function(buffer, offset, isLE, mLen, nBytes) {
+ var e, m,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ nBits = -7,
+ i = isLE ? (nBytes - 1) : 0,
+ d = isLE ? -1 : 1,
+ s = buffer[offset + i];
+
+ i += d;
+
+ e = s & ((1 << (-nBits)) - 1);
+ s >>= (-nBits);
+ nBits += eLen;
+ for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ m = e & ((1 << (-nBits)) - 1);
+ e >>= (-nBits);
+ nBits += mLen;
+ for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
+
+ if (e === 0) {
+ e = 1 - eBias;
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
+ } else {
+ m = m + Math.pow(2, mLen);
+ e = e - eBias;
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
+};
+
+exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
+ var e, m, c,
+ eLen = nBytes * 8 - mLen - 1,
+ eMax = (1 << eLen) - 1,
+ eBias = eMax >> 1,
+ rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
+ i = isLE ? 0 : (nBytes - 1),
+ d = isLE ? 1 : -1,
+ s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
+
+ value = Math.abs(value);
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0;
+ e = eMax;
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2);
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--;
+ c *= 2;
+ }
+ if (e + eBias >= 1) {
+ value += rt / c;
+ } else {
+ value += rt * Math.pow(2, 1 - eBias);
+ }
+ if (value * c >= 2) {
+ e++;
+ c /= 2;
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0;
+ e = eMax;
+ } else if (e + eBias >= 1) {
+ m = (value * c - 1) * Math.pow(2, mLen);
+ e = e + eBias;
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
+ e = 0;
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
+
+ e = (e << mLen) | m;
+ eLen += mLen;
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
+
+ buffer[offset + i - d] |= s * 128;
+};
+
+},{}],14:[function(require,module,exports){
+
+/**
+ * isArray
+ */
+
+var isArray = Array.isArray;
+
+/**
+ * toString
+ */
+
+var str = Object.prototype.toString;
+
+/**
+ * Whether or not the given `val`
+ * is an array.
+ *
+ * example:
+ *
+ * isArray([]);
+ * // > true
+ * isArray(arguments);
+ * // > false
+ * isArray('');
+ * // > false
+ *
+ * @param {mixed} val
+ * @return {bool}
+ */
+
+module.exports = isArray || function (val) {
+ return !! val && '[object Array]' == str.call(val);
+};
+
+},{}],15:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ draining = true;
+ var currentQueue;
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ var i = -1;
+ while (++i < len) {
+ currentQueue[i]();
+ }
+ len = queue.length;
+ }
+ draining = false;
+}
+process.nextTick = function (fun) {
+ queue.push(fun);
+ if (!draining) {
+ setTimeout(drainQueue, 0);
+ }
+};
+
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],16:[function(require,module,exports){
+(function (Buffer){
+(function () {
+ "use strict";
+
+ function btoa(str) {
+ var buffer
+ ;
+
+ if (str instanceof Buffer) {
+ buffer = str;
+ } else {
+ buffer = new Buffer(str.toString(), 'binary');
+ }
+
+ return buffer.toString('base64');
+ }
+
+ module.exports = btoa;
+}());
+
+}).call(this,require("buffer").Buffer)
+
+},{"buffer":11}],17:[function(require,module,exports){
+/* jshint node: true */
+(function () {
+ "use strict";
+
+ function CookieAccessInfo(domain, path, secure, script) {
+ if (this instanceof CookieAccessInfo) {
+ this.domain = domain || undefined;
+ this.path = path || "/";
+ this.secure = !!secure;
+ this.script = !!script;
+ return this;
+ }
+ return new CookieAccessInfo(domain, path, secure, script);
+ }
+ exports.CookieAccessInfo = CookieAccessInfo;
+
+ function Cookie(cookiestr, request_domain, request_path) {
+ if (cookiestr instanceof Cookie) {
+ return cookiestr;
+ }
+ if (this instanceof Cookie) {
+ this.name = null;
+ this.value = null;
+ this.expiration_date = Infinity;
+ this.path = String(request_path || "/");
+ this.explicit_path = false;
+ this.domain = request_domain || null;
+ this.explicit_domain = false;
+ this.secure = false; //how to define default?
+ this.noscript = false; //httponly
+ if (cookiestr) {
+ this.parse(cookiestr, request_domain, request_path);
+ }
+ return this;
+ }
+ return new Cookie(cookiestr);
+ }
+ exports.Cookie = Cookie;
+
+ Cookie.prototype.toString = function toString() {
+ var str = [this.name + "=" + this.value];
+ if (this.expiration_date !== Infinity) {
+ str.push("expires=" + (new Date(this.expiration_date)).toGMTString());
+ }
+ if (this.domain) {
+ str.push("domain=" + this.domain);
+ }
+ if (this.path) {
+ str.push("path=" + this.path);
+ }
+ if (this.secure) {
+ str.push("secure");
+ }
+ if (this.noscript) {
+ str.push("httponly");
+ }
+ return str.join("; ");
+ };
+
+ Cookie.prototype.toValueString = function toValueString() {
+ return this.name + "=" + this.value;
+ };
+
+ var cookie_str_splitter = /[:](?=\s*[a-zA-Z0-9_\-]+\s*[=])/g;
+ Cookie.prototype.parse = function parse(str, request_domain, request_path) {
+ if (this instanceof Cookie) {
+ var parts = str.split(";").filter(function (value) {
+ return !!value;
+ }),
+ pair = parts[0].match(/([^=]+)=([\s\S]*)/),
+ key = pair[1],
+ value = pair[2],
+ i;
+ this.name = key;
+ this.value = value;
+
+ for (i = 1; i < parts.length; i += 1) {
+ pair = parts[i].match(/([^=]+)(?:=([\s\S]*))?/);
+ key = pair[1].trim().toLowerCase();
+ value = pair[2];
+ switch (key) {
+ case "httponly":
+ this.noscript = true;
+ break;
+ case "expires":
+ this.expiration_date = value ?
+ Number(Date.parse(value)) :
+ Infinity;
+ break;
+ case "path":
+ this.path = value ?
+ value.trim() :
+ "";
+ this.explicit_path = true;
+ break;
+ case "domain":
+ this.domain = value ?
+ value.trim() :
+ "";
+ this.explicit_domain = !!this.domain;
+ break;
+ case "secure":
+ this.secure = true;
+ break;
+ }
+ }
+
+ if (!this.explicit_path) {
+ this.path = request_path || "/";
+ }
+ if (!this.explicit_domain) {
+ this.domain = request_domain;
+ }
+
+ return this;
+ }
+ return new Cookie().parse(str, request_domain, request_path);
+ };
+
+ Cookie.prototype.matches = function matches(access_info) {
+ if (this.noscript && access_info.script ||
+ this.secure && !access_info.secure ||
+ !this.collidesWith(access_info)) {
+ return false;
+ }
+ return true;
+ };
+
+ Cookie.prototype.collidesWith = function collidesWith(access_info) {
+ if ((this.path && !access_info.path) || (this.domain && !access_info.domain)) {
+ return false;
+ }
+ if (this.path && access_info.path.indexOf(this.path) !== 0) {
+ return false;
+ }
+ if (!this.explicit_path) {
+ if (this.path !== access_info.path) {
+ return false;
+ }
+ }
+ var access_domain = access_info.domain && access_info.domain.replace(/^[\.]/,'');
+ var cookie_domain = this.domain && this.domain.replace(/^[\.]/,'');
+ if (cookie_domain === access_domain) {
+ return true;
+ }
+ if (cookie_domain) {
+ if (!this.explicit_domain) {
+ return false; // we already checked if the domains were exactly the same
+ }
+ var wildcard = access_domain.indexOf(cookie_domain);
+ if (wildcard === -1 || wildcard !== access_domain.length - cookie_domain.length) {
+ return false;
+ }
+ return true;
+ }
+ return true;
+ };
+
+ function CookieJar() {
+ var cookies, cookies_list, collidable_cookie;
+ if (this instanceof CookieJar) {
+ cookies = Object.create(null); //name: [Cookie]
+
+ this.setCookie = function setCookie(cookie, request_domain, request_path) {
+ var remove, i;
+ cookie = new Cookie(cookie, request_domain, request_path);
+ //Delete the cookie if the set is past the current time
+ remove = cookie.expiration_date <= Date.now();
+ if (cookies[cookie.name] !== undefined) {
+ cookies_list = cookies[cookie.name];
+ for (i = 0; i < cookies_list.length; i += 1) {
+ collidable_cookie = cookies_list[i];
+ if (collidable_cookie.collidesWith(cookie)) {
+ if (remove) {
+ cookies_list.splice(i, 1);
+ if (cookies_list.length === 0) {
+ delete cookies[cookie.name];
+ }
+ return false;
+ }
+ cookies_list[i] = cookie;
+ return cookie;
+ }
+ }
+ if (remove) {
+ return false;
+ }
+ cookies_list.push(cookie);
+ return cookie;
+ }
+ if (remove) {
+ return false;
+ }
+ cookies[cookie.name] = [cookie];
+ return cookies[cookie.name];
+ };
+ //returns a cookie
+ this.getCookie = function getCookie(cookie_name, access_info) {
+ var cookie, i;
+ cookies_list = cookies[cookie_name];
+ if (!cookies_list) {
+ return;
+ }
+ for (i = 0; i < cookies_list.length; i += 1) {
+ cookie = cookies_list[i];
+ if (cookie.expiration_date <= Date.now()) {
+ if (cookies_list.length === 0) {
+ delete cookies[cookie.name];
+ }
+ continue;
+ }
+ if (cookie.matches(access_info)) {
+ return cookie;
+ }
+ }
+ };
+ //returns a list of cookies
+ this.getCookies = function getCookies(access_info) {
+ var matches = [], cookie_name, cookie;
+ for (cookie_name in cookies) {
+ cookie = this.getCookie(cookie_name, access_info);
+ if (cookie) {
+ matches.push(cookie);
+ }
+ }
+ matches.toString = function toString() {
+ return matches.join(":");
+ };
+ matches.toValueString = function toValueString() {
+ return matches.map(function (c) {
+ return c.toValueString();
+ }).join(';');
+ };
+ return matches;
+ };
+
+ return this;
+ }
+ return new CookieJar();
+ }
+ exports.CookieJar = CookieJar;
+
+ //returns list of cookies that were set correctly. Cookies that are expired and removed are not returned.
+ CookieJar.prototype.setCookies = function setCookies(cookies, request_domain, request_path) {
+ cookies = Array.isArray(cookies) ?
+ cookies :
+ cookies.split(cookie_str_splitter);
+ var successful = [],
+ i,
+ cookie;
+ cookies = cookies.map(Cookie);
+ for (i = 0; i < cookies.length; i += 1) {
+ cookie = cookies[i];
+ if (this.setCookie(cookie, request_domain, request_path)) {
+ successful.push(cookie);
+ }
+ }
+ return successful;
+ };
+}());
+
+},{}],18:[function(require,module,exports){
+/*!
+ * jQuery JavaScript Library v2.1.3
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-12-18T15:11Z
+ */
+
+(function( global, factory ) {
+
+ if ( typeof module === "object" && typeof module.exports === "object" ) {
+ // For CommonJS and CommonJS-like environments where a proper `window`
+ // is present, execute the factory and get jQuery.
+ // For environments that do not have a `window` with a `document`
+ // (such as Node.js), expose a factory as module.exports.
+ // This accentuates the need for the creation of a real `window`.
+ // e.g. var jQuery = require("jquery")(window);
+ // See ticket #14549 for more info.
+ module.exports = global.document ?
+ factory( global, true ) :
+ function( w ) {
+ if ( !w.document ) {
+ throw new Error( "jQuery requires a window with a document" );
+ }
+ return factory( w );
+ };
+ } else {
+ factory( global );
+ }
+
+// Pass this if window is not defined yet
+}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Support: Firefox 18+
+// Can't be in strict mode, several libs including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+//
+
+var arr = [];
+
+var slice = arr.slice;
+
+var concat = arr.concat;
+
+var push = arr.push;
+
+var indexOf = arr.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var support = {};
+
+
+
+var
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+
+ version = "2.1.3",
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ // Need init if jQuery is called (just allow error to be thrown if not included)
+ return new jQuery.fn.init( selector, context );
+ },
+
+ // Support: Android<4.1
+ // Make sure we trim BOM and NBSP
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return letter.toUpperCase();
+ };
+
+jQuery.fn = jQuery.prototype = {
+ // The current version of jQuery being used
+ jquery: version,
+
+ constructor: jQuery,
+
+ // Start with an empty selector
+ selector: "",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ toArray: function() {
+ return slice.call( this );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num != null ?
+
+ // Return just the one element from the set
+ ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+
+ // Return all the elements in a clean array
+ slice.call( this );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems ) {
+
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+ ret.context = this.context;
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ) );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ eq: function( i ) {
+ var len = this.length,
+ j = +i + ( i < 0 ? len : 0 );
+ return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: arr.sort,
+ splice: arr.splice
+};
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+
+ // Skip the boolean and the target
+ target = arguments[ i ] || {};
+ i++;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // Extend jQuery itself if only one argument is passed
+ if ( i === length ) {
+ target = this;
+ i--;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ // Unique for each copy of jQuery on the page
+ expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+
+ // Assume jQuery is ready without the ready module
+ isReady: true,
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ noop: function() {},
+
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray,
+
+ isWindow: function( obj ) {
+ return obj != null && obj === obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ // parseFloat NaNs numeric-cast false positives (null|true|false|"")
+ // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+ // subtraction forces infinities to NaN
+ // adding 1 corrects loss of precision from parseFloat (#15100)
+ return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
+ },
+
+ isPlainObject: function( obj ) {
+ // Not plain objects:
+ // - Any object or value whose internal [[Class]] property is not "[object Object]"
+ // - DOM nodes
+ // - window
+ if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ if ( obj.constructor &&
+ !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
+ return false;
+ }
+
+ // If the function hasn't returned already, we're confident that
+ // |obj| is a plain object, created by {} or constructed with new Object
+ return true;
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ type: function( obj ) {
+ if ( obj == null ) {
+ return obj + "";
+ }
+ // Support: Android<4.0, iOS<6 (functionish RegExp)
+ return typeof obj === "object" || typeof obj === "function" ?
+ class2type[ toString.call(obj) ] || "object" :
+ typeof obj;
+ },
+
+ // Evaluates a script in a global context
+ globalEval: function( code ) {
+ var script,
+ indirect = eval;
+
+ code = jQuery.trim( code );
+
+ if ( code ) {
+ // If the code includes a valid, prologue position
+ // strict mode pragma, execute code by injecting a
+ // script tag into the document.
+ if ( code.indexOf("use strict") === 1 ) {
+ script = document.createElement("script");
+ script.text = code;
+ document.head.appendChild( script ).parentNode.removeChild( script );
+ } else {
+ // Otherwise, avoid the DOM node creation, insertion
+ // and removal by using an indirect global eval
+ indirect( code );
+ }
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Support: IE9-11+
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ // args is for internal usage only
+ each: function( obj, callback, args ) {
+ var value,
+ i = 0,
+ length = obj.length,
+ isArray = isArraylike( obj );
+
+ if ( args ) {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.apply( obj[ i ], args );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( i in obj ) {
+ value = callback.call( obj[ i ], i, obj[ i ] );
+
+ if ( value === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ // Support: Android<4.1
+ trim: function( text ) {
+ return text == null ?
+ "" :
+ ( text + "" ).replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var ret = results || [];
+
+ if ( arr != null ) {
+ if ( isArraylike( Object(arr) ) ) {
+ jQuery.merge( ret,
+ typeof arr === "string" ?
+ [ arr ] : arr
+ );
+ } else {
+ push.call( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ return arr == null ? -1 : indexOf.call( arr, elem, i );
+ },
+
+ merge: function( first, second ) {
+ var len = +second.length,
+ j = 0,
+ i = first.length;
+
+ for ( ; j < len; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, invert ) {
+ var callbackInverse,
+ matches = [],
+ i = 0,
+ length = elems.length,
+ callbackExpect = !invert;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ callbackInverse = !callback( elems[ i ], i );
+ if ( callbackInverse !== callbackExpect ) {
+ matches.push( elems[ i ] );
+ }
+ }
+
+ return matches;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value,
+ i = 0,
+ length = elems.length,
+ isArray = isArraylike( elems ),
+ ret = [];
+
+ // Go through the array, translating each of the items to their new values
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret.push( value );
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( i in elems ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret.push( value );
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var tmp, args, proxy;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ now: Date.now,
+
+ // jQuery.support is not used in Core but other projects attach their
+ // properties to it so it needs to exist.
+ support: support
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+ var length = obj.length,
+ type = jQuery.type( obj );
+
+ if ( type === "function" || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ if ( obj.nodeType === 1 && length ) {
+ return true;
+ }
+
+ return type === "array" || length === 0 ||
+ typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+}
+var Sizzle =
+/*!
+ * Sizzle CSS Selector Engine v2.2.0-pre
+ * http://sizzlejs.com/
+ *
+ * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-12-16
+ */
+(function( window ) {
+
+var i,
+ support,
+ Expr,
+ getText,
+ isXML,
+ tokenize,
+ compile,
+ select,
+ outermostContext,
+ sortInput,
+ hasDuplicate,
+
+ // Local document vars
+ setDocument,
+ document,
+ docElem,
+ documentIsHTML,
+ rbuggyQSA,
+ rbuggyMatches,
+ matches,
+ contains,
+
+ // Instance-specific data
+ expando = "sizzle" + 1 * new Date(),
+ preferredDoc = window.document,
+ dirruns = 0,
+ done = 0,
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ }
+ return 0;
+ },
+
+ // General-purpose constants
+ MAX_NEGATIVE = 1 << 31,
+
+ // Instance methods
+ hasOwn = ({}).hasOwnProperty,
+ arr = [],
+ pop = arr.pop,
+ push_native = arr.push,
+ push = arr.push,
+ slice = arr.slice,
+ // Use a stripped-down indexOf as it's faster than native
+ // http://jsperf.com/thor-indexof-vs-for/5
+ indexOf = function( list, elem ) {
+ var i = 0,
+ len = list.length;
+ for ( ; i < len; i++ ) {
+ if ( list[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+ // Regular expressions
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
+ // Operator (capture 2)
+ "*([*^$|!~]?=)" + whitespace +
+ // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+ "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+ "*\\]",
+
+ pseudos = ":(" + characterEncoding + ")(?:\\((" +
+ // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+ // 1. quoted (capture 3; capture 4 or capture 5)
+ "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+ // 2. simple (capture 6)
+ "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+ // 3. anything else (capture 2)
+ ".*" +
+ ")\\)|)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rwhitespace = new RegExp( whitespace + "+", "g" ),
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+ rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+ rpseudo = new RegExp( pseudos ),
+ ridentifier = new RegExp( "^" + identifier + "$" ),
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+ // For use in libraries implementing .is()
+ // We use this for POS matching in `select`
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+ whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ },
+
+ rinputs = /^(?:input|select|textarea|button)$/i,
+ rheader = /^h\d$/i,
+
+ rnative = /^[^{]+\{\s*\[native \w/,
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+ rsibling = /[+~]/,
+ rescape = /'|\\/g,
+
+ // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+ funescape = function( _, escaped, escapedWhitespace ) {
+ var high = "0x" + escaped - 0x10000;
+ // NaN means non-codepoint
+ // Support: Firefox<24
+ // Workaround erroneous numeric interpretation of +"0x"
+ return high !== high || escapedWhitespace ?
+ escaped :
+ high < 0 ?
+ // BMP codepoint
+ String.fromCharCode( high + 0x10000 ) :
+ // Supplemental Plane codepoint (surrogate pair)
+ String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ },
+
+ // Used for iframes
+ // See setDocument()
+ // Removing the function wrapper causes a "Permission Denied"
+ // error in IE
+ unloadHandler = function() {
+ setDocument();
+ };
+
+// Optimize for push.apply( _, NodeList )
+try {
+ push.apply(
+ (arr = slice.call( preferredDoc.childNodes )),
+ preferredDoc.childNodes
+ );
+ // Support: Android<4.0
+ // Detect silently failing push.apply
+ arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+ push = { apply: arr.length ?
+
+ // Leverage slice if possible
+ function( target, els ) {
+ push_native.apply( target, slice.call(els) );
+ } :
+
+ // Support: IE<9
+ // Otherwise append directly
+ function( target, els ) {
+ var j = target.length,
+ i = 0;
+ // Can't trust NodeList.length
+ while ( (target[j++] = els[i++]) ) {}
+ target.length = j - 1;
+ }
+ };
+}
+
+function Sizzle( selector, context, results, seed ) {
+ var match, elem, m, nodeType,
+ // QSA vars
+ i, groups, old, nid, newContext, newSelector;
+
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
+
+ context = context || document;
+ results = results || [];
+ nodeType = context.nodeType;
+
+ if ( typeof selector !== "string" || !selector ||
+ nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
+
+ return results;
+ }
+
+ if ( !seed && documentIsHTML ) {
+
+ // Try to shortcut find operations when possible (e.g., not under DocumentFragment)
+ if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document (jQuery #6963)
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, context.getElementsByTagName( selector ) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && support.getElementsByClassName ) {
+ push.apply( results, context.getElementsByClassName( m ) );
+ return results;
+ }
+ }
+
+ // QSA path
+ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+ nid = old = expando;
+ newContext = context;
+ newSelector = nodeType !== 1 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + toSelector( groups[i] );
+ }
+ newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results,
+ newContext.querySelectorAll( newSelector )
+ );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ * deleting the oldest entry
+ */
+function createCache() {
+ var keys = [];
+
+ function cache( key, value ) {
+ // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ if ( keys.push( key + " " ) > Expr.cacheLength ) {
+ // Only keep the most recent entries
+ delete cache[ keys.shift() ];
+ }
+ return (cache[ key + " " ] = value);
+ }
+ return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+ fn[ expando ] = true;
+ return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return !!fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // Remove from its parent by default
+ if ( div.parentNode ) {
+ div.parentNode.removeChild( div );
+ }
+ // release memory in IE
+ div = null;
+ }
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+ var arr = attrs.split("|"),
+ i = attrs.length;
+
+ while ( i-- ) {
+ Expr.attrHandle[ arr[i] ] = handler;
+ }
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+ var cur = b && a,
+ diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+ ( ~b.sourceIndex || MAX_NEGATIVE ) -
+ ( ~a.sourceIndex || MAX_NEGATIVE );
+
+ // Use IE sourceIndex if available on both nodes
+ if ( diff ) {
+ return diff;
+ }
+
+ // Check if b follows a
+ if ( cur ) {
+ while ( (cur = cur.nextSibling) ) {
+ if ( cur === b ) {
+ return -1;
+ }
+ }
+ }
+
+ return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+}
+
+/**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+ return context && typeof context.getElementsByTagName !== "undefined" && context;
+}
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+ var hasCompare, parent,
+ doc = node ? node.ownerDocument || node : preferredDoc;
+
+ // If no document and documentElement is available, return
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
+
+ // Set our document
+ document = doc;
+ docElem = doc.documentElement;
+ parent = doc.defaultView;
+
+ // Support: IE>8
+ // If iframe document is assigned to "document" variable and if iframe has been reloaded,
+ // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+ // IE6-8 do not support the defaultView property so parent will be undefined
+ if ( parent && parent !== parent.top ) {
+ // IE11 does not have attachEvent, so all must suffer
+ if ( parent.addEventListener ) {
+ parent.addEventListener( "unload", unloadHandler, false );
+ } else if ( parent.attachEvent ) {
+ parent.attachEvent( "onunload", unloadHandler );
+ }
+ }
+
+ /* Support tests
+ ---------------------------------------------------------------------- */
+ documentIsHTML = !isXML( doc );
+
+ /* Attributes
+ ---------------------------------------------------------------------- */
+
+ // Support: IE<8
+ // Verify that getAttribute really returns attributes and not properties
+ // (excepting IE8 booleans)
+ support.attributes = assert(function( div ) {
+ div.className = "i";
+ return !div.getAttribute("className");
+ });
+
+ /* getElement(s)By*
+ ---------------------------------------------------------------------- */
+
+ // Check if getElementsByTagName("*") returns only elements
+ support.getElementsByTagName = assert(function( div ) {
+ div.appendChild( doc.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
+
+ // Support: IE<9
+ support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
+
+ // Support: IE<10
+ // Check if getElementById returns elements by name
+ // The broken getElementById methods don't pick up programatically-set names,
+ // so use a roundabout getElementsByName test
+ support.getById = assert(function( div ) {
+ docElem.appendChild( div ).id = expando;
+ return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+ });
+
+ // ID find and filter
+ if ( support.getById ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [ m ] : [];
+ }
+ };
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ return elem.getAttribute("id") === attrId;
+ };
+ };
+ } else {
+ // Support: IE6/7
+ // getElementById is not reliable as a find shortcut
+ delete Expr.find["ID"];
+
+ Expr.filter["ID"] = function( id ) {
+ var attrId = id.replace( runescape, funescape );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ // Tag
+ Expr.find["TAG"] = support.getElementsByTagName ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( tag );
+
+ // DocumentFragment nodes don't have gEBTN
+ } else if ( support.qsa ) {
+ return context.querySelectorAll( tag );
+ }
+ } :
+
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
+ results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ };
+
+ // Class
+ Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+ if ( documentIsHTML ) {
+ return context.getElementsByClassName( className );
+ }
+ };
+
+ /* QSA/matchesSelector
+ ---------------------------------------------------------------------- */
+
+ // QSA and matchesSelector support
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ rbuggyMatches = [];
+
+ // qSa(:focus) reports false when true (Chrome 21)
+ // We allow this because of a bug in IE8/9 that throws an error
+ // whenever `document.activeElement` is accessed on an iframe
+ // So, we allow :focus to pass through QSA all the time to avoid the IE error
+ // See http://bugs.jquery.com/ticket/13378
+ rbuggyQSA = [];
+
+ if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explicitly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
+ "<select id='" + expando + "-\f]' msallowcapture=''>" +
+ "<option selected=''></option></select>";
+
+ // Support: IE8, Opera 11-12.16
+ // Nothing should be selected when empty strings follow ^= or $= or *=
+ // The test attribute must be unknown in Opera but "safe" for WinRT
+ // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+ if ( div.querySelectorAll("[msallowcapture^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+ }
+
+ // Support: IE8
+ // Boolean attributes and "value" are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ }
+
+ // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+
+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
+ rbuggyQSA.push("~=");
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+
+ // Support: Safari 8+, iOS 8+
+ // https://bugs.webkit.org/show_bug.cgi?id=136851
+ // In-page `selector#id sibing-combinator selector` fails
+ if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
+ rbuggyQSA.push(".#.+[+~]");
+ }
+ });
+
+ assert(function( div ) {
+ // Support: Windows 8 Native Apps
+ // The type and name attributes are restricted during .innerHTML assignment
+ var input = doc.createElement("input");
+ input.setAttribute( "type", "hidden" );
+ div.appendChild( input ).setAttribute( "name", "D" );
+
+ // Support: IE8
+ // Enforce case-sensitivity of name attribute
+ if ( div.querySelectorAll("[name=d]").length ) {
+ rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here and will not see later tests
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push( ":enabled", ":disabled" );
+ }
+
+ // Opera 10-11 does not throw on post-comma invalid pseudos
+ div.querySelectorAll("*,:x");
+ rbuggyQSA.push(",.*:");
+ });
+ }
+
+ if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
+ docElem.webkitMatchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector) )) ) {
+
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ support.disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( div, "[s!='']:x" );
+ rbuggyMatches.push( "!=", pseudos );
+ });
+ }
+
+ rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+ rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+ /* Contains
+ ---------------------------------------------------------------------- */
+ hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+ // Element contains another
+ // Purposefully does not implement inclusive descendent
+ // As in, an element does not contain itself
+ contains = hasCompare || rnative.test( docElem.contains ) ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && (
+ adown.contains ?
+ adown.contains( bup ) :
+ a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+ ));
+ } :
+ function( a, b ) {
+ if ( b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ };
+
+ /* Sorting
+ ---------------------------------------------------------------------- */
+
+ // Document order sorting
+ sortOrder = hasCompare ?
+ function( a, b ) {
+
+ // Flag for duplicate removal
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ // Sort on method existence if only one input has compareDocumentPosition
+ var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+ if ( compare ) {
+ return compare;
+ }
+
+ // Calculate position if both inputs belong to the same document
+ compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+ a.compareDocumentPosition( b ) :
+
+ // Otherwise we know they are disconnected
+ 1;
+
+ // Disconnected nodes
+ if ( compare & 1 ||
+ (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+ // Choose the first element that is related to our preferred document
+ if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+ return -1;
+ }
+ if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+ return 1;
+ }
+
+ // Maintain original order
+ return sortInput ?
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 0;
+ }
+
+ return compare & 4 ? -1 : 1;
+ } :
+ function( a, b ) {
+ // Exit early if the nodes are identical
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ var cur,
+ i = 0,
+ aup = a.parentNode,
+ bup = b.parentNode,
+ ap = [ a ],
+ bp = [ b ];
+
+ // Parentless nodes are either documents or disconnected
+ if ( !aup || !bup ) {
+ return a === doc ? -1 :
+ b === doc ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ sortInput ?
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 0;
+
+ // If the nodes are siblings, we can do a quick check
+ } else if ( aup === bup ) {
+ return siblingCheck( a, b );
+ }
+
+ // Otherwise we need full lists of their ancestors for comparison
+ cur = a;
+ while ( (cur = cur.parentNode) ) {
+ ap.unshift( cur );
+ }
+ cur = b;
+ while ( (cur = cur.parentNode) ) {
+ bp.unshift( cur );
+ }
+
+ // Walk down the tree looking for a discrepancy
+ while ( ap[i] === bp[i] ) {
+ i++;
+ }
+
+ return i ?
+ // Do a sibling check if the nodes have a common ancestor
+ siblingCheck( ap[i], bp[i] ) :
+
+ // Otherwise nodes in our document sort first
+ ap[i] === preferredDoc ? -1 :
+ bp[i] === preferredDoc ? 1 :
+ 0;
+ };
+
+ return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ if ( support.matchesSelector && documentIsHTML &&
+ ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+ ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
+
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || support.disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch (e) {}
+ }
+
+ return Sizzle( expr, document, null, [ elem ] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+ // Set document vars if needed
+ if ( ( context.ownerDocument || context ) !== document ) {
+ setDocument( context );
+ }
+ return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+ // Set document vars if needed
+ if ( ( elem.ownerDocument || elem ) !== document ) {
+ setDocument( elem );
+ }
+
+ var fn = Expr.attrHandle[ name.toLowerCase() ],
+ // Don't get fooled by Object.prototype properties (jQuery #13807)
+ val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+ fn( elem, name, !documentIsHTML ) :
+ undefined;
+
+ return val !== undefined ?
+ val :
+ support.attributes || !documentIsHTML ?
+ elem.getAttribute( name ) :
+ (val = elem.getAttributeNode(name)) && val.specified ?
+ val.value :
+ null;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ j = 0,
+ i = 0;
+
+ // Unless we *know* we can detect duplicates, assume their presence
+ hasDuplicate = !support.detectDuplicates;
+ sortInput = !support.sortStable && results.slice( 0 );
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ while ( (elem = results[i++]) ) {
+ if ( elem === results[ i ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ // Clear input after sorting to release objects
+ // See https://github.com/jquery/sizzle/pull/225
+ sortInput = null;
+
+ return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( !nodeType ) {
+ // If no nodeType, this is expected to be an array
+ while ( (node = elem[i++]) ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (jQuery #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+
+ return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ attrHandle: {},
+
+ find: {},
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( runescape, funescape );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 what (child|of-type)
+ 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 4 xn-component of xn+y argument ([+-]?\d*n|)
+ 5 sign of xn-component
+ 6 x of xn-component
+ 7 sign of y-component
+ 8 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1].slice( 0, 3 ) === "nth" ) {
+ // nth-* requires argument
+ if ( !match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+ match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[3] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var excess,
+ unquoted = !match[6] && match[2];
+
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ // Accept quoted arguments as-is
+ if ( match[3] ) {
+ match[2] = match[4] || match[5] || "";
+
+ // Strip excess characters from unquoted arguments
+ } else if ( unquoted && rpseudo.test( unquoted ) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ match[0] = match[0].slice( 0, excess );
+ match[2] = unquoted.slice( 0, excess );
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+
+ "TAG": function( nodeNameSelector ) {
+ var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ return nodeNameSelector === "*" ?
+ function() { return true; } :
+ function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.slice( -check.length ) === check :
+ operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, what, argument, first, last ) {
+ var simple = type.slice( 0, 3 ) !== "nth",
+ forward = type.slice( -4 ) !== "last",
+ ofType = what === "of-type";
+
+ return first === 1 && last === 0 ?
+
+ // Shortcut for :nth-*(n)
+ function( elem ) {
+ return !!elem.parentNode;
+ } :
+
+ function( elem, context, xml ) {
+ var cache, outerCache, node, diff, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ if ( parent ) {
+
+ // :(first|last|only)-(child|of-type)
+ if ( simple ) {
+ while ( dir ) {
+ node = elem;
+ while ( (node = node[ dir ]) ) {
+ if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+ return false;
+ }
+ }
+ // Reverse direction for :only-* (if we haven't yet done so)
+ start = dir = type === "only" && !start && "nextSibling";
+ }
+ return true;
+ }
+
+ start = [ forward ? parent.firstChild : parent.lastChild ];
+
+ // non-xml :nth-child(...) stores cache data on `parent`
+ if ( forward && useCache ) {
+ // Seek `elem` from a previously-cached index
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
+ cache = outerCache[ type ] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && cache[2];
+ node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+ // Fallback to seeking `elem` from the start
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ // When found, cache indexes on `parent` and break
+ if ( node.nodeType === 1 && ++diff && node === elem ) {
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ // Use previously-cached element index if available
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+ diff = cache[1];
+
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ } else {
+ // Use the same loop as above to seek `elem` from the start
+ while ( (node = ++nodeIndex && node && node[ dir ] ||
+ (diff = nodeIndex = 0) || start.pop()) ) {
+
+ if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+ // Cache the index of each encountered element
+ if ( useCache ) {
+ (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+ }
+
+ if ( node === elem ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset, then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ // Potentially complex pseudos
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ // Don't keep the element (issue #299)
+ input[0] = null;
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ text = text.replace( runescape, funescape );
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ // "Whether an element is represented by a :lang() selector
+ // is based solely on the element's language value
+ // being equal to the identifier C,
+ // or beginning with the identifier C immediately followed by "-".
+ // The matching of C against the element's language value is performed case-insensitively.
+ // The identifier C does not have to be a valid language name."
+ // http://www.w3.org/TR/selectors/#lang-pseudo
+ "lang": markFunction( function( lang ) {
+ // lang value must be a valid identifier
+ if ( !ridentifier.test(lang || "") ) {
+ Sizzle.error( "unsupported lang: " + lang );
+ }
+ lang = lang.replace( runescape, funescape ).toLowerCase();
+ return function( elem ) {
+ var elemLang;
+ do {
+ if ( (elemLang = documentIsHTML ?
+ elem.lang :
+ elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+ elemLang = elemLang.toLowerCase();
+ return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ }
+ } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+ return false;
+ };
+ }),
+
+ // Miscellaneous
+ "target": function( elem ) {
+ var hash = window.location && window.location.hash;
+ return hash && hash.slice( 1 ) === elem.id;
+ },
+
+ "root": function( elem ) {
+ return elem === docElem;
+ },
+
+ "focus": function( elem ) {
+ return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ // Boolean properties
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ // Contents
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+ // but not by others (comment: 8; processing instruction: 7; etc.)
+ // nodeType < 6 works because attributes (2) do not appear as children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ if ( elem.nodeType < 6 ) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ // Element/input types
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "text": function( elem ) {
+ var attr;
+ return elem.nodeName.toLowerCase() === "input" &&
+ elem.type === "text" &&
+
+ // Support: IE<8
+ // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+ },
+
+ // Position-in-collection
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 0;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ var i = 1;
+ for ( ; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ var i = argument < 0 ? argument + length : argument;
+ for ( ; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+ Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+ Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( (tokens = []) );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ // Cast descendant combinators to space
+ type: match[0].replace( rtrim, " " )
+ });
+ soFar = soFar.slice( matched.length );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+ matched = match.shift();
+ tokens.push({
+ value: matched,
+ type: type,
+ matches: match
+ });
+ soFar = soFar.slice( matched.length );
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+};
+
+function toSelector( tokens ) {
+ var i = 0,
+ len = tokens.length,
+ selector = "";
+ for ( ; i < len; i++ ) {
+ selector += tokens[i].value;
+ }
+ return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ var oldCache, outerCache,
+ newCache = [ dirruns, doneName ];
+
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ if ( matcher( elem, context, xml ) ) {
+ return true;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( elem.nodeType === 1 || checkNonElements ) {
+ outerCache = elem[ expando ] || (elem[ expando ] = {});
+ if ( (oldCache = outerCache[ dir ]) &&
+ oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+ // Assign to newCache so results back-propagate to previous elements
+ return (newCache[ 2 ] = oldCache[ 2 ]);
+ } else {
+ // Reuse newcache so results back-propagate to previous elements
+ outerCache[ dir ] = newCache;
+
+ // A match means we're done; a fail means we have to keep checking
+ if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ // Avoid hanging onto element (issue #299)
+ checkContext = null;
+ return ret;
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && toSelector(
+ // If the preceding token was a descendant combinator, insert an implicit any-element `*`
+ tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+ ).replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && toSelector( tokens )
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, outermost ) {
+ var elem, j, matcher,
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ setMatched = [],
+ contextBackup = outermostContext,
+ // We must always have either seed elements or outermost context
+ elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+ // Use integer dirruns iff this is the outermost matcher
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+ len = elems.length;
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+ // Support: IE<9, Safari
+ // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
+ for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ j = 0;
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ j = 0;
+ while ( (matcher = setMatchers[j++]) ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !match ) {
+ match = tokenize( selector );
+ }
+ i = match.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( match[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+ // Save selector and tokenization
+ cached.selector = selector;
+ }
+ return cached;
+};
+
+/**
+ * A low-level selection function that works with Sizzle's compiled
+ * selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ * selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+select = Sizzle.select = function( selector, context, results, seed ) {
+ var i, tokens, token, type, find,
+ compiled = typeof selector === "function" && selector,
+ match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+ results = results || [];
+
+ // Try to minimize operations if there is no seed and only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ support.getById && context.nodeType === 9 && documentIsHTML &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+ if ( !context ) {
+ return results;
+
+ // Precompiled matchers will still verify ancestry, so step up a level
+ } else if ( compiled ) {
+ context = context.parentNode;
+ }
+
+ selector = selector.slice( tokens.shift().value.length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+ while ( i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( runescape, funescape ),
+ rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && toSelector( tokens );
+ if ( !selector ) {
+ push.apply( results, seed );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function if one is not provided
+ // Provide `match` to avoid retokenization if we modified the selector above
+ ( compiled || compile( selector, match ) )(
+ seed,
+ context,
+ !documentIsHTML,
+ results,
+ rsibling.test( selector ) && testContext( context.parentNode ) || context
+ );
+ return results;
+};
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome 14-35+
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = !!hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+ // Should return 1, but returns 4 (following)
+ return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+ div.innerHTML = "<a href='#'></a>";
+ return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+ addHandle( "type|href|height|width", function( elem, name, isXML ) {
+ if ( !isXML ) {
+ return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+ }
+ });
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+ div.innerHTML = "<input/>";
+ div.firstChild.setAttribute( "value", "" );
+ return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+ addHandle( "value", function( elem, name, isXML ) {
+ if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+ return elem.defaultValue;
+ }
+ });
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+ return div.getAttribute("disabled") == null;
+}) ) {
+ addHandle( booleans, function( elem, name, isXML ) {
+ var val;
+ if ( !isXML ) {
+ return elem[ name ] === true ? name.toLowerCase() :
+ (val = elem.getAttributeNode( name )) && val.specified ?
+ val.value :
+ null;
+ }
+ });
+}
+
+return Sizzle;
+
+})( window );
+
+
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+
+var rneedsContext = jQuery.expr.match.needsContext;
+
+var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
+
+
+
+var risSimple = /^.[^:#\[\.,]*$/;
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep( elements, function( elem, i ) {
+ /* jshint -W018 */
+ return !!qualifier.call( elem, i, elem ) !== not;
+ });
+
+ }
+
+ if ( qualifier.nodeType ) {
+ return jQuery.grep( elements, function( elem ) {
+ return ( elem === qualifier ) !== not;
+ });
+
+ }
+
+ if ( typeof qualifier === "string" ) {
+ if ( risSimple.test( qualifier ) ) {
+ return jQuery.filter( qualifier, elements, not );
+ }
+
+ qualifier = jQuery.filter( qualifier, elements );
+ }
+
+ return jQuery.grep( elements, function( elem ) {
+ return ( indexOf.call( qualifier, elem ) >= 0 ) !== not;
+ });
+}
+
+jQuery.filter = function( expr, elems, not ) {
+ var elem = elems[ 0 ];
+
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 && elem.nodeType === 1 ?
+ jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+ jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+ return elem.nodeType === 1;
+ }));
+};
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var i,
+ len = this.length,
+ ret = [],
+ self = this;
+
+ if ( typeof selector !== "string" ) {
+ return this.pushStack( jQuery( selector ).filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ }) );
+ }
+
+ for ( i = 0; i < len; i++ ) {
+ jQuery.find( selector, self[ i ], ret );
+ }
+
+ // Needed because $( selector, context ) becomes $( context ).find( selector )
+ ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+ ret.selector = this.selector ? this.selector + " " + selector : selector;
+ return ret;
+ },
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector || [], false) );
+ },
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector || [], true) );
+ },
+ is: function( selector ) {
+ return !!winnow(
+ this,
+
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ typeof selector === "string" && rneedsContext.test( selector ) ?
+ jQuery( selector ) :
+ selector || [],
+ false
+ ).length;
+ }
+});
+
+
+// Initialize a jQuery object
+
+
+// A central reference to the root jQuery(document)
+var rootjQuery,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ // Strict HTML recognition (#11290: must start with <)
+ rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+ init = jQuery.fn.init = function( selector, context ) {
+ var match, elem;
+
+ // HANDLE: $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+
+ // Option to run scripts is true for back-compat
+ // Intentionally let the error be thrown if parseHTML is not present
+ jQuery.merge( this, jQuery.parseHTML(
+ match[1],
+ context && context.nodeType ? context.ownerDocument || context : document,
+ true
+ ) );
+
+ // HANDLE: $(html, props)
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ for ( match in context ) {
+ // Properties of context are called as methods if possible
+ if ( jQuery.isFunction( this[ match ] ) ) {
+ this[ match ]( context[ match ] );
+
+ // ...and otherwise set as attributes
+ } else {
+ this.attr( match, context[ match ] );
+ }
+ }
+ }
+
+ return this;
+
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Support: Blackberry 4.6
+ // gEBID returns nodes no longer in the document (#6963)
+ if ( elem && elem.parentNode ) {
+ // Inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(DOMElement)
+ } else if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return typeof rootjQuery.ready !== "undefined" ?
+ rootjQuery.ready( selector ) :
+ // Execute immediately if ready is not present
+ selector( jQuery );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ };
+
+// Give the init function the jQuery prototype for later instantiation
+init.prototype = jQuery.fn;
+
+// Initialize central reference
+rootjQuery = jQuery( document );
+
+
+var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ // Methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.extend({
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ truncate = until !== undefined;
+
+ while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
+ if ( elem.nodeType === 1 ) {
+ if ( truncate && jQuery( elem ).is( until ) ) {
+ break;
+ }
+ matched.push( elem );
+ }
+ }
+ return matched;
+ },
+
+ sibling: function( n, elem ) {
+ var matched = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ matched.push( n );
+ }
+ }
+
+ return matched;
+ }
+});
+
+jQuery.fn.extend({
+ has: function( target ) {
+ var targets = jQuery( target, this ),
+ l = targets.length;
+
+ return this.filter(function() {
+ var i = 0;
+ for ( ; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ matched = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( ; i < l; i++ ) {
+ for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+ // Always skip document fragments
+ if ( cur.nodeType < 11 && (pos ?
+ pos.index(cur) > -1 :
+
+ // Don't pass non-elements to Sizzle
+ cur.nodeType === 1 &&
+ jQuery.find.matchesSelector(cur, selectors)) ) {
+
+ matched.push( cur );
+ break;
+ }
+ }
+ }
+
+ return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
+ },
+
+ // Determine the position of an element within the set
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
+ }
+
+ // Index in selector
+ if ( typeof elem === "string" ) {
+ return indexOf.call( jQuery( elem ), this[ 0 ] );
+ }
+
+ // Locate the position of the desired element
+ return indexOf.call( this,
+
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[ 0 ] : elem
+ );
+ },
+
+ add: function( selector, context ) {
+ return this.pushStack(
+ jQuery.unique(
+ jQuery.merge( this.get(), jQuery( selector, context ) )
+ )
+ );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
+ }
+});
+
+function sibling( cur, dir ) {
+ while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
+ return cur;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return sibling( elem, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return sibling( elem, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return elem.contentDocument || jQuery.merge( [], elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var matched = jQuery.map( this, fn, until );
+
+ if ( name.slice( -5 ) !== "Until" ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ matched = jQuery.filter( selector, matched );
+ }
+
+ if ( this.length > 1 ) {
+ // Remove duplicates
+ if ( !guaranteedUnique[ name ] ) {
+ jQuery.unique( matched );
+ }
+
+ // Reverse order for parents* and prev-derivatives
+ if ( rparentsprev.test( name ) ) {
+ matched.reverse();
+ }
+ }
+
+ return this.pushStack( matched );
+ };
+});
+var rnotwhite = (/\S+/g);
+
+
+
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
+
+ var // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Check if a given callback is in the list.
+ // If no argument is given, return whether or not list has callbacks attached.
+ has: function( fn ) {
+ return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ firingLength = 0;
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( list && ( !fired || stack ) ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+
+
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ](function() {
+ var returned = fn && fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
+ } else {
+ newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+ }
+ });
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ]
+ deferred[ tuple[0] ] = function() {
+ deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+ return this;
+ };
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
+ if ( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // Add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
+ } else {
+ --remaining;
+ }
+ }
+ }
+
+ // If we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
+ return deferred.promise();
+ }
+});
+
+
+// The deferred used on DOM ready
+var readyList;
+
+jQuery.fn.ready = function( fn ) {
+ // Add the callback
+ jQuery.ready.promise().done( fn );
+
+ return this;
+};
+
+jQuery.extend({
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.triggerHandler ) {
+ jQuery( document ).triggerHandler( "ready" );
+ jQuery( document ).off( "ready" );
+ }
+ }
+});
+
+/**
+ * The ready event handler and self cleanup method
+ */
+function completed() {
+ document.removeEventListener( "DOMContentLoaded", completed, false );
+ window.removeEventListener( "load", completed, false );
+ jQuery.ready();
+}
+
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // We once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready );
+
+ } else {
+
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", completed, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", completed, false );
+ }
+ }
+ return readyList.promise( obj );
+};
+
+// Kick off the DOM ready check even if the user does not
+jQuery.ready.promise();
+
+
+
+
+// Multifunctional method to get and set values of a collection
+// The value/s can optionally be executed if it's a function
+var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ var i = 0,
+ len = elems.length,
+ bulk = key == null;
+
+ // Sets many values
+ if ( jQuery.type( key ) === "object" ) {
+ chainable = true;
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+ }
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ chainable = true;
+
+ if ( !jQuery.isFunction( value ) ) {
+ raw = true;
+ }
+
+ if ( bulk ) {
+ // Bulk operations run against the entire set
+ if ( raw ) {
+ fn.call( elems, value );
+ fn = null;
+
+ // ...except when executing function values
+ } else {
+ bulk = fn;
+ fn = function( elem, key, value ) {
+ return bulk.call( jQuery( elem ), value );
+ };
+ }
+ }
+
+ if ( fn ) {
+ for ( ; i < len; i++ ) {
+ fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+ }
+ }
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ len ? fn( elems[0], key ) : emptyGet;
+};
+
+
+/**
+ * Determines whether an object can have data
+ */
+jQuery.acceptData = function( owner ) {
+ // Accepts only:
+ // - Node
+ // - Node.ELEMENT_NODE
+ // - Node.DOCUMENT_NODE
+ // - Object
+ // - Any
+ /* jshint -W018 */
+ return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
+};
+
+
+function Data() {
+ // Support: Android<4,
+ // Old WebKit does not have Object.preventExtensions/freeze method,
+ // return new empty object instead with no [[set]] accessor
+ Object.defineProperty( this.cache = {}, 0, {
+ get: function() {
+ return {};
+ }
+ });
+
+ this.expando = jQuery.expando + Data.uid++;
+}
+
+Data.uid = 1;
+Data.accepts = jQuery.acceptData;
+
+Data.prototype = {
+ key: function( owner ) {
+ // We can accept data for non-element nodes in modern browsers,
+ // but we should not, see #8335.
+ // Always return the key for a frozen object.
+ if ( !Data.accepts( owner ) ) {
+ return 0;
+ }
+
+ var descriptor = {},
+ // Check if the owner object already has a cache key
+ unlock = owner[ this.expando ];
+
+ // If not, create one
+ if ( !unlock ) {
+ unlock = Data.uid++;
+
+ // Secure it in a non-enumerable, non-writable property
+ try {
+ descriptor[ this.expando ] = { value: unlock };
+ Object.defineProperties( owner, descriptor );
+
+ // Support: Android<4
+ // Fallback to a less secure definition
+ } catch ( e ) {
+ descriptor[ this.expando ] = unlock;
+ jQuery.extend( owner, descriptor );
+ }
+ }
+
+ // Ensure the cache object
+ if ( !this.cache[ unlock ] ) {
+ this.cache[ unlock ] = {};
+ }
+
+ return unlock;
+ },
+ set: function( owner, data, value ) {
+ var prop,
+ // There may be an unlock assigned to this node,
+ // if there is no entry for this "owner", create one inline
+ // and set the unlock as though an owner entry had always existed
+ unlock = this.key( owner ),
+ cache = this.cache[ unlock ];
+
+ // Handle: [ owner, key, value ] args
+ if ( typeof data === "string" ) {
+ cache[ data ] = value;
+
+ // Handle: [ owner, { properties } ] args
+ } else {
+ // Fresh assignments by object are shallow copied
+ if ( jQuery.isEmptyObject( cache ) ) {
+ jQuery.extend( this.cache[ unlock ], data );
+ // Otherwise, copy the properties one-by-one to the cache object
+ } else {
+ for ( prop in data ) {
+ cache[ prop ] = data[ prop ];
+ }
+ }
+ }
+ return cache;
+ },
+ get: function( owner, key ) {
+ // Either a valid cache is found, or will be created.
+ // New caches will be created and the unlock returned,
+ // allowing direct access to the newly created
+ // empty data object. A valid owner object must be provided.
+ var cache = this.cache[ this.key( owner ) ];
+
+ return key === undefined ?
+ cache : cache[ key ];
+ },
+ access: function( owner, key, value ) {
+ var stored;
+ // In cases where either:
+ //
+ // 1. No key was specified
+ // 2. A string key was specified, but no value provided
+ //
+ // Take the "read" path and allow the get method to determine
+ // which value to return, respectively either:
+ //
+ // 1. The entire cache object
+ // 2. The data stored at the key
+ //
+ if ( key === undefined ||
+ ((key && typeof key === "string") && value === undefined) ) {
+
+ stored = this.get( owner, key );
+
+ return stored !== undefined ?
+ stored : this.get( owner, jQuery.camelCase(key) );
+ }
+
+ // [*]When the key is not a string, or both a key and value
+ // are specified, set or extend (existing objects) with either:
+ //
+ // 1. An object of properties
+ // 2. A key and value
+ //
+ this.set( owner, key, value );
+
+ // Since the "set" path can have two possible entry points
+ // return the expected data based on which path was taken[*]
+ return value !== undefined ? value : key;
+ },
+ remove: function( owner, key ) {
+ var i, name, camel,
+ unlock = this.key( owner ),
+ cache = this.cache[ unlock ];
+
+ if ( key === undefined ) {
+ this.cache[ unlock ] = {};
+
+ } else {
+ // Support array or space separated string of keys
+ if ( jQuery.isArray( key ) ) {
+ // If "name" is an array of keys...
+ // When data is initially created, via ("key", "val") signature,
+ // keys will be converted to camelCase.
+ // Since there is no way to tell _how_ a key was added, remove
+ // both plain key and camelCase key. #12786
+ // This will only penalize the array argument path.
+ name = key.concat( key.map( jQuery.camelCase ) );
+ } else {
+ camel = jQuery.camelCase( key );
+ // Try the string as a key before any manipulation
+ if ( key in cache ) {
+ name = [ key, camel ];
+ } else {
+ // If a key with the spaces exists, use it.
+ // Otherwise, create an array by matching non-whitespace
+ name = camel;
+ name = name in cache ?
+ [ name ] : ( name.match( rnotwhite ) || [] );
+ }
+ }
+
+ i = name.length;
+ while ( i-- ) {
+ delete cache[ name[ i ] ];
+ }
+ }
+ },
+ hasData: function( owner ) {
+ return !jQuery.isEmptyObject(
+ this.cache[ owner[ this.expando ] ] || {}
+ );
+ },
+ discard: function( owner ) {
+ if ( owner[ this.expando ] ) {
+ delete this.cache[ owner[ this.expando ] ];
+ }
+ }
+};
+var data_priv = new Data();
+
+var data_user = new Data();
+
+
+
+// Implementation Summary
+//
+// 1. Enforce API surface and semantic compatibility with 1.9.x branch
+// 2. Improve the module's maintainability by reducing the storage
+// paths to a single mechanism.
+// 3. Use the same single mechanism to support "private" and "user" data.
+// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
+// 5. Avoid exposing implementation details on user objects (eg. expando properties)
+// 6. Provide a clear path for implementation upgrade to WeakMap in 2014
+
+var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+function dataAttr( elem, key, data ) {
+ var name;
+
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+ name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ data_user.set( elem, key, data );
+ } else {
+ data = undefined;
+ }
+ }
+ return data;
+}
+
+jQuery.extend({
+ hasData: function( elem ) {
+ return data_user.hasData( elem ) || data_priv.hasData( elem );
+ },
+
+ data: function( elem, name, data ) {
+ return data_user.access( elem, name, data );
+ },
+
+ removeData: function( elem, name ) {
+ data_user.remove( elem, name );
+ },
+
+ // TODO: Now that all calls to _data and _removeData have been replaced
+ // with direct calls to data_priv methods, these can be deprecated.
+ _data: function( elem, name, data ) {
+ return data_priv.access( elem, name, data );
+ },
+
+ _removeData: function( elem, name ) {
+ data_priv.remove( elem, name );
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var i, name, data,
+ elem = this[ 0 ],
+ attrs = elem && elem.attributes;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = data_user.get( elem );
+
+ if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
+ i = attrs.length;
+ while ( i-- ) {
+
+ // Support: IE11+
+ // The attrs elements can be null (#14894)
+ if ( attrs[ i ] ) {
+ name = attrs[ i ].name;
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.slice(5) );
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ }
+ data_priv.set( elem, "hasDataAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ data_user.set( this, key );
+ });
+ }
+
+ return access( this, function( value ) {
+ var data,
+ camelKey = jQuery.camelCase( key );
+
+ // The calling jQuery object (element matches) is not empty
+ // (and therefore has an element appears at this[ 0 ]) and the
+ // `value` parameter was not undefined. An empty jQuery object
+ // will result in `undefined` for elem = this[ 0 ] which will
+ // throw an exception if an attempt to read a data cache is made.
+ if ( elem && value === undefined ) {
+ // Attempt to get data from the cache
+ // with the key as-is
+ data = data_user.get( elem, key );
+ if ( data !== undefined ) {
+ return data;
+ }
+
+ // Attempt to get data from the cache
+ // with the key camelized
+ data = data_user.get( elem, camelKey );
+ if ( data !== undefined ) {
+ return data;
+ }
+
+ // Attempt to "discover" the data in
+ // HTML5 custom data-* attrs
+ data = dataAttr( elem, camelKey, undefined );
+ if ( data !== undefined ) {
+ return data;
+ }
+
+ // We tried really hard, but the data doesn't exist.
+ return;
+ }
+
+ // Set the data...
+ this.each(function() {
+ // First, attempt to store a copy or reference of any
+ // data that might've been store with a camelCased key.
+ var data = data_user.get( this, camelKey );
+
+ // For HTML5 data-* attribute interop, we have to
+ // store property names with dashes in a camelCase form.
+ // This might not apply to all properties...*
+ data_user.set( this, camelKey, value );
+
+ // *... In the case of properties that might _actually_
+ // have dashes, we need to also store a copy of that
+ // unchanged property.
+ if ( key.indexOf("-") !== -1 && data !== undefined ) {
+ data_user.set( this, key, value );
+ }
+ });
+ }, null, value, arguments.length > 1, null, true );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ data_user.remove( this, key );
+ });
+ }
+});
+
+
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ var queue;
+
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = data_priv.get( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray( data ) ) {
+ queue = data_priv.access( elem, type, jQuery.makeArray(data) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
+
+ if ( fn ) {
+
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ // Clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // Not public - generate a queueHooks object, or return the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return data_priv.get( elem, key ) || data_priv.access( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ data_priv.remove( elem, [ type + "queue", key ] );
+ })
+ });
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // Ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
+
+ while ( i-- ) {
+ tmp = data_priv.get( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
+});
+var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
+
+var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
+var isHidden = function( elem, el ) {
+ // isHidden might be called from jQuery#filter function;
+ // in that case, element will be second argument
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+ };
+
+var rcheckableType = (/^(?:checkbox|radio)$/i);
+
+
+
+(function() {
+ var fragment = document.createDocumentFragment(),
+ div = fragment.appendChild( document.createElement( "div" ) ),
+ input = document.createElement( "input" );
+
+ // Support: Safari<=5.1
+ // Check state lost if the name is set (#11217)
+ // Support: Windows Web Apps (WWA)
+ // `name` and `type` must use .setAttribute for WWA (#14901)
+ input.setAttribute( "type", "radio" );
+ input.setAttribute( "checked", "checked" );
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+
+ // Support: Safari<=5.1, Android<4.2
+ // Older WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Support: IE<=11+
+ // Make sure textarea (and checkbox) defaultValue is properly cloned
+ div.innerHTML = "<textarea>x</textarea>";
+ support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+})();
+var strundefined = typeof undefined;
+
+
+
+support.focusinBubbles = "onfocusin" in window;
+
+
+var
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+ return true;
+}
+
+function returnFalse() {
+ return false;
+}
+
+function safeActiveElement() {
+ try {
+ return document.activeElement;
+ } catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ global: {},
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var handleObjIn, eventHandle, tmp,
+ events, t, handleObj,
+ special, handlers, type, namespaces, origType,
+ elemData = data_priv.get( elem );
+
+ // Don't attach events to noData or text/comment nodes (but allow plain objects)
+ if ( !elemData ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ if ( !(events = elemData.events) ) {
+ events = elemData.events = {};
+ }
+ if ( !(eventHandle = elemData.handle) ) {
+ eventHandle = elemData.handle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
+ jQuery.event.dispatch.apply( elem, arguments ) : undefined;
+ };
+ }
+
+ // Handle multiple events separated by a space
+ types = ( types || "" ).match( rnotwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // There *must* be a type, no attaching namespace-only handlers
+ if ( !type ) {
+ continue;
+ }
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: origType,
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ if ( !(handlers = events[ type ]) ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ },
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var j, origCount, tmp,
+ events, t, handleObj,
+ special, handlers, type, namespaces, origType,
+ elemData = data_priv.hasData( elem ) && data_priv.get( elem );
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = ( types || "" ).match( rnotwhite ) || [ "" ];
+ t = types.length;
+ while ( t-- ) {
+ tmp = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tmp[1];
+ namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+ handlers = events[ type ] || [];
+ tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+ // Remove matching events
+ origCount = j = handlers.length;
+ while ( j-- ) {
+ handleObj = handlers[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !tmp || tmp.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ handlers.splice( j, 1 );
+
+ if ( handleObj.selector ) {
+ handlers.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( origCount && !handlers.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ delete elemData.handle;
+ data_priv.remove( elem, "events" );
+ }
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+
+ var i, cur, tmp, bubbleType, ontype, handle, special,
+ eventPath = [ elem || document ],
+ type = hasOwn.call( event, "type" ) ? event.type : event,
+ namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+ cur = tmp = elem = elem || document;
+
+ // Don't do events on text and comment nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+ return;
+ }
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf(".") >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+ ontype = type.indexOf(":") < 0 && "on" + type;
+
+ // Caller can pass in a jQuery.Event object, Object, or just an event type string
+ event = event[ jQuery.expando ] ?
+ event :
+ new jQuery.Event( type, typeof event === "object" && event );
+
+ // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+ event.isTrigger = onlyHandlers ? 2 : 3;
+ event.namespace = namespaces.join(".");
+ event.namespace_re = event.namespace ?
+ new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+ null;
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data == null ?
+ [ event ] :
+ jQuery.makeArray( data, [ event ] );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ if ( !rfocusMorph.test( bubbleType + type ) ) {
+ cur = cur.parentNode;
+ }
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push( cur );
+ tmp = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( tmp === (elem.ownerDocument || document) ) {
+ eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+ }
+ }
+
+ // Fire handlers on the event path
+ i = 0;
+ while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+ event.type = i > 1 ?
+ bubbleType :
+ special.bindType || type;
+
+ // jQuery handler
+ handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+
+ // Native handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
+ event.result = handle.apply( cur, data );
+ if ( event.result === false ) {
+ event.preventDefault();
+ }
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+ jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ tmp = elem[ ontype ];
+
+ if ( tmp ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( tmp ) {
+ elem[ ontype ] = tmp;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event );
+
+ var i, j, ret, matched, handleObj,
+ handlerQueue = [],
+ args = slice.call( arguments ),
+ handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
+ special = jQuery.event.special[ event.type ] || {};
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers
+ handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+ // Run delegates first; they may want to stop propagation beneath us
+ i = 0;
+ while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+ event.currentTarget = matched.elem;
+
+ j = 0;
+ while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+ // Triggered event must either 1) have no namespace, or 2) have namespace(s)
+ // a subset or equal to those in the bound event (both can have no namespace).
+ if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.handleObj = handleObj;
+ event.data = handleObj.data;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ if ( (event.result = ret) === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ handlers: function( event, handlers ) {
+ var i, matches, sel, handleObj,
+ handlerQueue = [],
+ delegateCount = handlers.delegateCount,
+ cur = event.target;
+
+ // Find delegate handlers
+ // Black-hole SVG <use> instance trees (#13180)
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+ for ( ; cur !== this; cur = cur.parentNode || this ) {
+
+ // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+ if ( cur.disabled !== true || event.type !== "click" ) {
+ matches = [];
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+
+ // Don't conflict with Object.prototype properties (#13203)
+ sel = handleObj.selector + " ";
+
+ if ( matches[ sel ] === undefined ) {
+ matches[ sel ] = handleObj.needsContext ?
+ jQuery( sel, this ).index( cur ) >= 0 :
+ jQuery.find( sel, this, null, [ cur ] ).length;
+ }
+ if ( matches[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, handlers: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( delegateCount < handlers.length ) {
+ handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+ }
+
+ return handlerQueue;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop, copy,
+ type = event.type,
+ originalEvent = event,
+ fixHook = this.fixHooks[ type ];
+
+ if ( !fixHook ) {
+ this.fixHooks[ type ] = fixHook =
+ rmouseEvent.test( type ) ? this.mouseHooks :
+ rkeyEvent.test( type ) ? this.keyHooks :
+ {};
+ }
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = new jQuery.Event( originalEvent );
+
+ i = copy.length;
+ while ( i-- ) {
+ prop = copy[ i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Support: Cordova 2.5 (WebKit) (#13255)
+ // All events should have a target; Cordova deviceready doesn't
+ if ( !event.target ) {
+ event.target = document;
+ }
+
+ // Support: Safari 6.0+, Chrome<28
+ // Target should not be a text node (#504, #13143)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+ focus: {
+ // Fire native event if possible so blur/focus sequence is correct
+ trigger: function() {
+ if ( this !== safeActiveElement() && this.focus ) {
+ this.focus();
+ return false;
+ }
+ },
+ delegateType: "focusin"
+ },
+ blur: {
+ trigger: function() {
+ if ( this === safeActiveElement() && this.blur ) {
+ this.blur();
+ return false;
+ }
+ },
+ delegateType: "focusout"
+ },
+ click: {
+ // For checkbox, fire native event so checked state will be right
+ trigger: function() {
+ if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
+ this.click();
+ return false;
+ }
+ },
+
+ // For cross-browser consistency, don't fire native .click() on links
+ _default: function( event ) {
+ return jQuery.nodeName( event.target, "a" );
+ }
+ },
+
+ beforeunload: {
+ postDispatch: function( event ) {
+
+ // Support: Firefox 20+
+ // Firefox doesn't alert if the returnValue field is not set.
+ if ( event.result !== undefined && event.originalEvent ) {
+ event.originalEvent.returnValue = event.result;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ {
+ type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+jQuery.removeEvent = function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+};
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = src.defaultPrevented ||
+ src.defaultPrevented === undefined &&
+ // Support: Android<4.0
+ src.returnValue === false ?
+ returnTrue :
+ returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse,
+
+ preventDefault: function() {
+ var e = this.originalEvent;
+
+ this.isDefaultPrevented = returnTrue;
+
+ if ( e && e.preventDefault ) {
+ e.preventDefault();
+ }
+ },
+ stopPropagation: function() {
+ var e = this.originalEvent;
+
+ this.isPropagationStopped = returnTrue;
+
+ if ( e && e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ },
+ stopImmediatePropagation: function() {
+ var e = this.originalEvent;
+
+ this.isImmediatePropagationStopped = returnTrue;
+
+ if ( e && e.stopImmediatePropagation ) {
+ e.stopImmediatePropagation();
+ }
+
+ this.stopPropagation();
+ }
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+// Support: Chrome 15+
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout",
+ pointerenter: "pointerover",
+ pointerleave: "pointerout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var ret,
+ target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// Support: Firefox, Chrome, Safari
+// Create "bubbling" focus and blur events
+if ( !support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler on the document while someone wants focusin/focusout
+ var handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ var doc = this.ownerDocument || this,
+ attaches = data_priv.access( doc, fix );
+
+ if ( !attaches ) {
+ doc.addEventListener( orig, handler, true );
+ }
+ data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
+ },
+ teardown: function() {
+ var doc = this.ownerDocument || this,
+ attaches = data_priv.access( doc, fix ) - 1;
+
+ if ( !attaches ) {
+ doc.removeEventListener( orig, handler, true );
+ data_priv.remove( doc, fix );
+
+ } else {
+ data_priv.access( doc, fix, attaches );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ var handleObj, type;
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ var elem = this[0];
+ if ( elem ) {
+ return jQuery.event.trigger( type, data, elem, true );
+ }
+ }
+});
+
+
+var
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+ rtagName = /<([\w:]+)/,
+ rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style|link)/i,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /^$|\/(?:java|ecma)script/i,
+ rscriptTypeMasked = /^true\/(.*)/,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+ // We have to close these tags to support XHTML (#13200)
+ wrapMap = {
+
+ // Support: IE9
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+
+ thead: [ 1, "<table>", "</table>" ],
+ col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+ _default: [ 0, "", "" ]
+ };
+
+// Support: IE9
+wrapMap.optgroup = wrapMap.option;
+
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// Support: 1.x compatibility
+// Manipulating tables requires a tbody
+function manipulationTarget( elem, content ) {
+ return jQuery.nodeName( elem, "table" ) &&
+ jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
+
+ elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+ elem;
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+ elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
+ return elem;
+}
+function restoreScript( elem ) {
+ var match = rscriptTypeMasked.exec( elem.type );
+
+ if ( match ) {
+ elem.type = match[ 1 ];
+ } else {
+ elem.removeAttribute("type");
+ }
+
+ return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+ var i = 0,
+ l = elems.length;
+
+ for ( ; i < l; i++ ) {
+ data_priv.set(
+ elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
+ );
+ }
+}
+
+function cloneCopyEvent( src, dest ) {
+ var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
+
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // 1. Copy private data: events, handlers, etc.
+ if ( data_priv.hasData( src ) ) {
+ pdataOld = data_priv.access( src );
+ pdataCur = data_priv.set( dest, pdataOld );
+ events = pdataOld.events;
+
+ if ( events ) {
+ delete pdataCur.handle;
+ pdataCur.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type, events[ type ][ i ] );
+ }
+ }
+ }
+ }
+
+ // 2. Copy user data
+ if ( data_user.hasData( src ) ) {
+ udataOld = data_user.access( src );
+ udataCur = jQuery.extend( {}, udataOld );
+
+ data_user.set( dest, udataCur );
+ }
+}
+
+function getAll( context, tag ) {
+ var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
+ context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
+ [];
+
+ return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+ jQuery.merge( [ context ], ret ) :
+ ret;
+}
+
+// Fix IE bugs, see support tests
+function fixInput( src, dest ) {
+ var nodeName = dest.nodeName.toLowerCase();
+
+ // Fails to persist the checked state of a cloned checkbox or radio button.
+ if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+ dest.checked = src.checked;
+
+ // Fails to return the selected option to the default selected state when cloning options
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var i, l, srcElements, destElements,
+ clone = elem.cloneNode( true ),
+ inPage = jQuery.contains( elem.ownerDocument, elem );
+
+ // Fix IE cloning issues
+ if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
+ !jQuery.isXMLDoc( elem ) ) {
+
+ // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+ destElements = getAll( clone );
+ srcElements = getAll( elem );
+
+ for ( i = 0, l = srcElements.length; i < l; i++ ) {
+ fixInput( srcElements[ i ], destElements[ i ] );
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ if ( deepDataAndEvents ) {
+ srcElements = srcElements || getAll( elem );
+ destElements = destElements || getAll( clone );
+
+ for ( i = 0, l = srcElements.length; i < l; i++ ) {
+ cloneCopyEvent( srcElements[ i ], destElements[ i ] );
+ }
+ } else {
+ cloneCopyEvent( elem, clone );
+ }
+ }
+
+ // Preserve script evaluation history
+ destElements = getAll( clone, "script" );
+ if ( destElements.length > 0 ) {
+ setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+ }
+
+ // Return the cloned set
+ return clone;
+ },
+
+ buildFragment: function( elems, context, scripts, selection ) {
+ var elem, tmp, tag, wrap, contains, j,
+ fragment = context.createDocumentFragment(),
+ nodes = [],
+ i = 0,
+ l = elems.length;
+
+ for ( ; i < l; i++ ) {
+ elem = elems[ i ];
+
+ if ( elem || elem === 0 ) {
+
+ // Add nodes directly
+ if ( jQuery.type( elem ) === "object" ) {
+ // Support: QtWebKit, PhantomJS
+ // push.apply(_, arraylike) throws on ancient WebKit
+ jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+ // Convert non-html into a text node
+ } else if ( !rhtml.test( elem ) ) {
+ nodes.push( context.createTextNode( elem ) );
+
+ // Convert html into DOM nodes
+ } else {
+ tmp = tmp || fragment.appendChild( context.createElement("div") );
+
+ // Deserialize a standard representation
+ tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
+ wrap = wrapMap[ tag ] || wrapMap._default;
+ tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
+
+ // Descend through wrappers to the right content
+ j = wrap[ 0 ];
+ while ( j-- ) {
+ tmp = tmp.lastChild;
+ }
+
+ // Support: QtWebKit, PhantomJS
+ // push.apply(_, arraylike) throws on ancient WebKit
+ jQuery.merge( nodes, tmp.childNodes );
+
+ // Remember the top-level container
+ tmp = fragment.firstChild;
+
+ // Ensure the created nodes are orphaned (#12392)
+ tmp.textContent = "";
+ }
+ }
+ }
+
+ // Remove wrapper from fragment
+ fragment.textContent = "";
+
+ i = 0;
+ while ( (elem = nodes[ i++ ]) ) {
+
+ // #4087 - If origin and destination elements are the same, and this is
+ // that element, do not do anything
+ if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+ continue;
+ }
+
+ contains = jQuery.contains( elem.ownerDocument, elem );
+
+ // Append to fragment
+ tmp = getAll( fragment.appendChild( elem ), "script" );
+
+ // Preserve script evaluation history
+ if ( contains ) {
+ setGlobalEval( tmp );
+ }
+
+ // Capture executables
+ if ( scripts ) {
+ j = 0;
+ while ( (elem = tmp[ j++ ]) ) {
+ if ( rscriptType.test( elem.type || "" ) ) {
+ scripts.push( elem );
+ }
+ }
+ }
+ }
+
+ return fragment;
+ },
+
+ cleanData: function( elems ) {
+ var data, elem, type, key,
+ special = jQuery.event.special,
+ i = 0;
+
+ for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
+ if ( jQuery.acceptData( elem ) ) {
+ key = elem[ data_priv.expando ];
+
+ if ( key && (data = data_priv.cache[ key ]) ) {
+ if ( data.events ) {
+ for ( type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+ if ( data_priv.cache[ key ] ) {
+ // Discard any remaining `private` data
+ delete data_priv.cache[ key ];
+ }
+ }
+ }
+ // Discard any remaining `user` data
+ delete data_user.cache[ elem[ data_user.expando ] ];
+ }
+ }
+});
+
+jQuery.fn.extend({
+ text: function( value ) {
+ return access( this, function( value ) {
+ return value === undefined ?
+ jQuery.text( this ) :
+ this.empty().each(function() {
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+ this.textContent = value;
+ }
+ });
+ }, null, value, arguments.length );
+ },
+
+ append: function() {
+ return this.domManip( arguments, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+ var target = manipulationTarget( this, elem );
+ target.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip( arguments, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+ var target = manipulationTarget( this, elem );
+ target.insertBefore( elem, target.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ return this.domManip( arguments, function( elem ) {
+ if ( this.parentNode ) {
+ this.parentNode.insertBefore( elem, this );
+ }
+ });
+ },
+
+ after: function() {
+ return this.domManip( arguments, function( elem ) {
+ if ( this.parentNode ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ }
+ });
+ },
+
+ remove: function( selector, keepData /* Internal Use Only */ ) {
+ var elem,
+ elems = selector ? jQuery.filter( selector, this ) : this,
+ i = 0;
+
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( getAll( elem ) );
+ }
+
+ if ( elem.parentNode ) {
+ if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+ setGlobalEval( getAll( elem, "script" ) );
+ }
+ elem.parentNode.removeChild( elem );
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ if ( elem.nodeType === 1 ) {
+
+ // Prevent memory leaks
+ jQuery.cleanData( getAll( elem, false ) );
+
+ // Remove any remaining nodes
+ elem.textContent = "";
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map(function() {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ return access( this, function( value ) {
+ var elem = this[ 0 ] || {},
+ i = 0,
+ l = this.length;
+
+ if ( value === undefined && elem.nodeType === 1 ) {
+ return elem.innerHTML;
+ }
+
+ // See if we can take a shortcut and just use innerHTML
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
+
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+ try {
+ for ( ; i < l; i++ ) {
+ elem = this[ i ] || {};
+
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( getAll( elem, false ) );
+ elem.innerHTML = value;
+ }
+ }
+
+ elem = 0;
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch( e ) {}
+ }
+
+ if ( elem ) {
+ this.empty().append( value );
+ }
+ }, null, value, arguments.length );
+ },
+
+ replaceWith: function() {
+ var arg = arguments[ 0 ];
+
+ // Make the changes, replacing each context element with the new content
+ this.domManip( arguments, function( elem ) {
+ arg = this.parentNode;
+
+ jQuery.cleanData( getAll( this ) );
+
+ if ( arg ) {
+ arg.replaceChild( elem, this );
+ }
+ });
+
+ // Force removal if there was no new content (e.g., from empty arguments)
+ return arg && (arg.length || arg.nodeType) ? this : this.remove();
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, callback ) {
+
+ // Flatten any nested arrays
+ args = concat.apply( [], args );
+
+ var fragment, first, scripts, hasScripts, node, doc,
+ i = 0,
+ l = this.length,
+ set = this,
+ iNoClone = l - 1,
+ value = args[ 0 ],
+ isFunction = jQuery.isFunction( value );
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( isFunction ||
+ ( l > 1 && typeof value === "string" &&
+ !support.checkClone && rchecked.test( value ) ) ) {
+ return this.each(function( index ) {
+ var self = set.eq( index );
+ if ( isFunction ) {
+ args[ 0 ] = value.call( this, index, self.html() );
+ }
+ self.domManip( args, callback );
+ });
+ }
+
+ if ( l ) {
+ fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
+ first = fragment.firstChild;
+
+ if ( fragment.childNodes.length === 1 ) {
+ fragment = first;
+ }
+
+ if ( first ) {
+ scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+ hasScripts = scripts.length;
+
+ // Use the original fragment for the last item instead of the first because it can end up
+ // being emptied incorrectly in certain situations (#8070).
+ for ( ; i < l; i++ ) {
+ node = fragment;
+
+ if ( i !== iNoClone ) {
+ node = jQuery.clone( node, true, true );
+
+ // Keep references to cloned scripts for later restoration
+ if ( hasScripts ) {
+ // Support: QtWebKit
+ // jQuery.merge because push.apply(_, arraylike) throws
+ jQuery.merge( scripts, getAll( node, "script" ) );
+ }
+ }
+
+ callback.call( this[ i ], node, i );
+ }
+
+ if ( hasScripts ) {
+ doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+ // Reenable scripts
+ jQuery.map( scripts, restoreScript );
+
+ // Evaluate executable scripts on first document insertion
+ for ( i = 0; i < hasScripts; i++ ) {
+ node = scripts[ i ];
+ if ( rscriptType.test( node.type || "" ) &&
+ !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+ if ( node.src ) {
+ // Optional AJAX dependency, but won't run scripts if not present
+ if ( jQuery._evalUrl ) {
+ jQuery._evalUrl( node.src );
+ }
+ } else {
+ jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return this;
+ }
+});
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var elems,
+ ret = [],
+ insert = jQuery( selector ),
+ last = insert.length - 1,
+ i = 0;
+
+ for ( ; i <= last; i++ ) {
+ elems = i === last ? this : this.clone( true );
+ jQuery( insert[ i ] )[ original ]( elems );
+
+ // Support: QtWebKit
+ // .get() because push.apply(_, arraylike) throws
+ push.apply( ret, elems.get() );
+ }
+
+ return this.pushStack( ret );
+ };
+});
+
+
+var iframe,
+ elemdisplay = {};
+
+/**
+ * Retrieve the actual display of a element
+ * @param {String} name nodeName of the element
+ * @param {Object} doc Document object
+ */
+// Called only from within defaultDisplay
+function actualDisplay( name, doc ) {
+ var style,
+ elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+
+ // getDefaultComputedStyle might be reliably used only on attached element
+ display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
+
+ // Use of this method is a temporary fix (more like optimization) until something better comes along,
+ // since it was removed from specification and supported only in FF
+ style.display : jQuery.css( elem[ 0 ], "display" );
+
+ // We don't have any data stored on the element,
+ // so use "detach" method as fast way to get rid of the element
+ elem.detach();
+
+ return display;
+}
+
+/**
+ * Try to determine the default display value of an element
+ * @param {String} nodeName
+ */
+function defaultDisplay( nodeName ) {
+ var doc = document,
+ display = elemdisplay[ nodeName ];
+
+ if ( !display ) {
+ display = actualDisplay( nodeName, doc );
+
+ // If the simple way fails, read from inside an iframe
+ if ( display === "none" || !display ) {
+
+ // Use the already-created iframe if possible
+ iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
+
+ // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+ doc = iframe[ 0 ].contentDocument;
+
+ // Support: IE
+ doc.write();
+ doc.close();
+
+ display = actualDisplay( nodeName, doc );
+ iframe.detach();
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return display;
+}
+var rmargin = (/^margin/);
+
+var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
+
+var getStyles = function( elem ) {
+ // Support: IE<=11+, Firefox<=30+ (#15098, #14150)
+ // IE throws on elements created in popups
+ // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
+ if ( elem.ownerDocument.defaultView.opener ) {
+ return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
+ }
+
+ return window.getComputedStyle( elem, null );
+ };
+
+
+
+function curCSS( elem, name, computed ) {
+ var width, minWidth, maxWidth, ret,
+ style = elem.style;
+
+ computed = computed || getStyles( elem );
+
+ // Support: IE9
+ // getPropertyValue is only needed for .css('filter') (#12537)
+ if ( computed ) {
+ ret = computed.getPropertyValue( name ) || computed[ name ];
+ }
+
+ if ( computed ) {
+
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+
+ // Support: iOS < 6
+ // A tribute to the "awesome hack by Dean Edwards"
+ // iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+ // Remember the original values
+ width = style.width;
+ minWidth = style.minWidth;
+ maxWidth = style.maxWidth;
+
+ // Put in the new values to get a computed value out
+ style.minWidth = style.maxWidth = style.width = ret;
+ ret = computed.width;
+
+ // Revert the changed values
+ style.width = width;
+ style.minWidth = minWidth;
+ style.maxWidth = maxWidth;
+ }
+ }
+
+ return ret !== undefined ?
+ // Support: IE
+ // IE returns zIndex value as an integer.
+ ret + "" :
+ ret;
+}
+
+
+function addGetHookIf( conditionFn, hookFn ) {
+ // Define the hook, we'll check on the first run if it's really needed.
+ return {
+ get: function() {
+ if ( conditionFn() ) {
+ // Hook not needed (or it's not possible to use it due
+ // to missing dependency), remove it.
+ delete this.get;
+ return;
+ }
+
+ // Hook needed; redefine it so that the support test is not executed again.
+ return (this.get = hookFn).apply( this, arguments );
+ }
+ };
+}
+
+
+(function() {
+ var pixelPositionVal, boxSizingReliableVal,
+ docElem = document.documentElement,
+ container = document.createElement( "div" ),
+ div = document.createElement( "div" );
+
+ if ( !div.style ) {
+ return;
+ }
+
+ // Support: IE9-11+
+ // Style of cloned element affects source element cloned (#8908)
+ div.style.backgroundClip = "content-box";
+ div.cloneNode( true ).style.backgroundClip = "";
+ support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+ container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" +
+ "position:absolute";
+ container.appendChild( div );
+
+ // Executing both pixelPosition & boxSizingReliable tests require only one layout
+ // so they're executed at the same time to save the second computation.
+ function computePixelPositionAndBoxSizingReliable() {
+ div.style.cssText =
+ // Support: Firefox<29, Android 2.3
+ // Vendor-prefix box-sizing
+ "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
+ "box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
+ "border:1px;padding:1px;width:4px;position:absolute";
+ div.innerHTML = "";
+ docElem.appendChild( container );
+
+ var divStyle = window.getComputedStyle( div, null );
+ pixelPositionVal = divStyle.top !== "1%";
+ boxSizingReliableVal = divStyle.width === "4px";
+
+ docElem.removeChild( container );
+ }
+
+ // Support: node.js jsdom
+ // Don't assume that getComputedStyle is a property of the global object
+ if ( window.getComputedStyle ) {
+ jQuery.extend( support, {
+ pixelPosition: function() {
+
+ // This test is executed only once but we still do memoizing
+ // since we can use the boxSizingReliable pre-computing.
+ // No need to check if the test was already performed, though.
+ computePixelPositionAndBoxSizingReliable();
+ return pixelPositionVal;
+ },
+ boxSizingReliable: function() {
+ if ( boxSizingReliableVal == null ) {
+ computePixelPositionAndBoxSizingReliable();
+ }
+ return boxSizingReliableVal;
+ },
+ reliableMarginRight: function() {
+
+ // Support: Android 2.3
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. (#3333)
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // This support function is only executed once so no memoizing is needed.
+ var ret,
+ marginDiv = div.appendChild( document.createElement( "div" ) );
+
+ // Reset CSS: box-sizing; display; margin; border; padding
+ marginDiv.style.cssText = div.style.cssText =
+ // Support: Firefox<29, Android 2.3
+ // Vendor-prefix box-sizing
+ "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
+ "box-sizing:content-box;display:block;margin:0;border:0;padding:0";
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
+ docElem.appendChild( container );
+
+ ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
+
+ docElem.removeChild( container );
+ div.removeChild( marginDiv );
+
+ return ret;
+ }
+ });
+ }
+})();
+
+
+// A method for quickly swapping in/out CSS properties to get correct calculations.
+jQuery.swap = function( elem, options, callback, args ) {
+ var ret, name,
+ old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.apply( elem, args || [] );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+};
+
+
+var
+ // Swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+ // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+ rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
+ rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssNormalTransform = {
+ letterSpacing: "0",
+ fontWeight: "400"
+ },
+
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+// Return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+ // Shortcut for names that are not vendor prefixed
+ if ( name in style ) {
+ return name;
+ }
+
+ // Check for vendor prefixed names
+ var capName = name[0].toUpperCase() + name.slice(1),
+ origName = name,
+ i = cssPrefixes.length;
+
+ while ( i-- ) {
+ name = cssPrefixes[ i ] + capName;
+ if ( name in style ) {
+ return name;
+ }
+ }
+
+ return origName;
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+ var matches = rnumsplit.exec( value );
+ return matches ?
+ // Guard against undefined "subtract", e.g., when used as in cssHooks
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+ value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
+ // If we already have the right measurement, avoid augmentation
+ 4 :
+ // Otherwise initialize for horizontal or vertical properties
+ name === "width" ? 1 : 0,
+
+ val = 0;
+
+ for ( ; i < 4; i += 2 ) {
+ // Both box models exclude margin, so add it if we want it
+ if ( extra === "margin" ) {
+ val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+ }
+
+ if ( isBorderBox ) {
+ // border-box includes padding, so remove it if we want content
+ if ( extra === "content" ) {
+ val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+ }
+
+ // At this point, extra isn't border nor margin, so remove border
+ if ( extra !== "margin" ) {
+ val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ }
+ } else {
+ // At this point, extra isn't content, so add padding
+ val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+ // At this point, extra isn't content nor padding, so add border
+ if ( extra !== "padding" ) {
+ val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ }
+ }
+ }
+
+ return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+ // Start with offset property, which is equivalent to the border-box value
+ var valueIsBorderBox = true,
+ val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ styles = getStyles( elem ),
+ isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+ // Some non-html elements return undefined for offsetWidth, so check for null/undefined
+ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+ if ( val <= 0 || val == null ) {
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name, styles );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+ }
+
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // Check for style in case a browser which returns unreliable values
+ // for getComputedStyle silently falls back to the reliable elem.style
+ valueIsBorderBox = isBorderBox &&
+ ( support.boxSizingReliable() || val === elem.style[ name ] );
+
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+ }
+
+ // Use the active box-sizing model to add/subtract irrelevant styles
+ return ( val +
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra || ( isBorderBox ? "border" : "content" ),
+ valueIsBorderBox,
+ styles
+ )
+ ) + "px";
+}
+
+function showHide( elements, show ) {
+ var display, elem, hidden,
+ values = [],
+ index = 0,
+ length = elements.length;
+
+ for ( ; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+
+ values[ index ] = data_priv.get( elem, "olddisplay" );
+ display = elem.style.display;
+ if ( show ) {
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !values[ index ] && display === "none" ) {
+ elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( elem.style.display === "" && isHidden( elem ) ) {
+ values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+ }
+ } else {
+ hidden = isHidden( elem );
+
+ if ( display !== "none" || !hidden ) {
+ data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( index = 0; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+ elem.style.display = show ? values[ index ] || "" : "none";
+ }
+ }
+
+ return elements;
+}
+
+jQuery.extend({
+
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity" );
+ return ret === "" ? "1" : ret;
+ }
+ }
+ }
+ },
+
+ // Don't automatically add "px" to these possibly-unitless properties
+ cssNumber: {
+ "columnCount": true,
+ "fillOpacity": true,
+ "flexGrow": true,
+ "flexShrink": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "order": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ "float": "cssFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, hooks,
+ origName = jQuery.camelCase( name ),
+ style = elem.style;
+
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+ // Gets hook for the prefixed version, then unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // Convert "+=" or "-=" to relative numbers (#7345)
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that null and NaN values aren't set (#7116)
+ if ( value == null || value !== value ) {
+ return;
+ }
+
+ // If a number, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // Support: IE9-11+
+ // background-* props affect original clone's values
+ if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
+ style[ name ] = "inherit";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+ style[ name ] = value;
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra, styles ) {
+ var val, num, hooks,
+ origName = jQuery.camelCase( name );
+
+ // Make sure that we're working with the right name
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+ // Try prefixed name followed by the unprefixed name
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks ) {
+ val = hooks.get( elem, true, extra );
+ }
+
+ // Otherwise, if a way to get the computed value exists, use that
+ if ( val === undefined ) {
+ val = curCSS( elem, name, styles );
+ }
+
+ // Convert "normal" to computed value
+ if ( val === "normal" && name in cssNormalTransform ) {
+ val = cssNormalTransform[ name ];
+ }
+
+ // Make numeric if forced or a qualifier was provided and val looks numeric
+ if ( extra === "" || extra ) {
+ num = parseFloat( val );
+ return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+ }
+ return val;
+ }
+});
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ if ( computed ) {
+
+ // Certain elements can have dimension info if we invisibly show them
+ // but it must have a current display style that would benefit
+ return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
+ jQuery.swap( elem, cssShow, function() {
+ return getWidthOrHeight( elem, name, extra );
+ }) :
+ getWidthOrHeight( elem, name, extra );
+ }
+ },
+
+ set: function( elem, value, extra ) {
+ var styles = extra && getStyles( elem );
+ return setPositiveNumber( elem, value, extra ?
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra,
+ jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+ styles
+ ) : 0
+ );
+ }
+ };
+});
+
+// Support: Android 2.3
+jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
+ function( elem, computed ) {
+ if ( computed ) {
+ return jQuery.swap( elem, { "display": "inline-block" },
+ curCSS, [ elem, "marginRight" ] );
+ }
+ }
+);
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+ margin: "",
+ padding: "",
+ border: "Width"
+}, function( prefix, suffix ) {
+ jQuery.cssHooks[ prefix + suffix ] = {
+ expand: function( value ) {
+ var i = 0,
+ expanded = {},
+
+ // Assumes a single number if not a string
+ parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+ for ( ; i < 4; i++ ) {
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+ }
+
+ return expanded;
+ }
+ };
+
+ if ( !rmargin.test( prefix ) ) {
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+ }
+});
+
+jQuery.fn.extend({
+ css: function( name, value ) {
+ return access( this, function( elem, name, value ) {
+ var styles, len,
+ map = {},
+ i = 0;
+
+ if ( jQuery.isArray( name ) ) {
+ styles = getStyles( elem );
+ len = name.length;
+
+ for ( ; i < len; i++ ) {
+ map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+ }
+
+ return map;
+ }
+
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+ },
+ show: function() {
+ return showHide( this, true );
+ },
+ hide: function() {
+ return showHide( this );
+ },
+ toggle: function( state ) {
+ if ( typeof state === "boolean" ) {
+ return state ? this.show() : this.hide();
+ }
+
+ return this.each(function() {
+ if ( isHidden( this ) ) {
+ jQuery( this ).show();
+ } else {
+ jQuery( this ).hide();
+ }
+ });
+ }
+});
+
+
+function Tween( elem, options, prop, end, easing ) {
+ return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+ constructor: Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || "swing";
+ this.options = options;
+ this.start = this.now = this.cur();
+ this.end = end;
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+ },
+ cur: function() {
+ var hooks = Tween.propHooks[ this.prop ];
+
+ return hooks && hooks.get ?
+ hooks.get( this ) :
+ Tween.propHooks._default.get( this );
+ },
+ run: function( percent ) {
+ var eased,
+ hooks = Tween.propHooks[ this.prop ];
+
+ if ( this.options.duration ) {
+ this.pos = eased = jQuery.easing[ this.easing ](
+ percent, this.options.duration * percent, 0, 1, this.options.duration
+ );
+ } else {
+ this.pos = eased = percent;
+ }
+ this.now = ( this.end - this.start ) * eased + this.start;
+
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ if ( hooks && hooks.set ) {
+ hooks.set( this );
+ } else {
+ Tween.propHooks._default.set( this );
+ }
+ return this;
+ }
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var result;
+
+ if ( tween.elem[ tween.prop ] != null &&
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+ return tween.elem[ tween.prop ];
+ }
+
+ // Passing an empty string as a 3rd parameter to .css will automatically
+ // attempt a parseFloat and fallback to a string if the parse fails.
+ // Simple values such as "10px" are parsed to Float;
+ // complex values such as "rotate(1rad)" are returned as-is.
+ result = jQuery.css( tween.elem, tween.prop, "" );
+ // Empty strings, null, undefined and "auto" are converted to 0.
+ return !result || result === "auto" ? 0 : result;
+ },
+ set: function( tween ) {
+ // Use step hook for back compat.
+ // Use cssHook if its there.
+ // Use .style if available and use plain properties where available.
+ if ( jQuery.fx.step[ tween.prop ] ) {
+ jQuery.fx.step[ tween.prop ]( tween );
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+ } else {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+ }
+};
+
+// Support: IE9
+// Panic based approach to setting things on disconnected nodes
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+ set: function( tween ) {
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+};
+
+jQuery.easing = {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return 0.5 - Math.cos( p * Math.PI ) / 2;
+ }
+};
+
+jQuery.fx = Tween.prototype.init;
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+
+
+
+var
+ fxNow, timerId,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
+ rrun = /queueHooks$/,
+ animationPrefilters = [ defaultPrefilter ],
+ tweeners = {
+ "*": [ function( prop, value ) {
+ var tween = this.createTween( prop, value ),
+ target = tween.cur(),
+ parts = rfxnum.exec( value ),
+ unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+ // Starting value computation is required for potential unit mismatches
+ start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+ rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+ scale = 1,
+ maxIterations = 20;
+
+ if ( start && start[ 3 ] !== unit ) {
+ // Trust units reported by jQuery.css
+ unit = unit || start[ 3 ];
+
+ // Make sure we update the tween properties later on
+ parts = parts || [];
+
+ // Iteratively approximate from a nonzero starting point
+ start = +target || 1;
+
+ do {
+ // If previous iteration zeroed out, double until we get *something*.
+ // Use string for doubling so we don't accidentally see scale as unchanged below
+ scale = scale || ".5";
+
+ // Adjust and apply
+ start = start / scale;
+ jQuery.style( tween.elem, prop, start + unit );
+
+ // Update scale, tolerating zero or NaN from tween.cur(),
+ // break the loop if scale is unchanged or perfect, or if we've just had enough
+ } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+ }
+
+ // Update tween properties
+ if ( parts ) {
+ start = tween.start = +start || +target || 0;
+ tween.unit = unit;
+ // If a +=/-= token was provided, we're doing a relative animation
+ tween.end = parts[ 1 ] ?
+ start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+ +parts[ 2 ];
+ }
+
+ return tween;
+ } ]
+ };
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout(function() {
+ fxNow = undefined;
+ });
+ return ( fxNow = jQuery.now() );
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+ var which,
+ i = 0,
+ attrs = { height: type };
+
+ // If we include width, step value is 1 to do all cssExpand values,
+ // otherwise step value is 2 to skip over Left and Right
+ includeWidth = includeWidth ? 1 : 0;
+ for ( ; i < 4 ; i += 2 - includeWidth ) {
+ which = cssExpand[ i ];
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+ }
+
+ if ( includeWidth ) {
+ attrs.opacity = attrs.width = type;
+ }
+
+ return attrs;
+}
+
+function createTween( value, prop, animation ) {
+ var tween,
+ collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+ index = 0,
+ length = collection.length;
+ for ( ; index < length; index++ ) {
+ if ( (tween = collection[ index ].call( animation, prop, value )) ) {
+
+ // We're done with this property
+ return tween;
+ }
+ }
+}
+
+function defaultPrefilter( elem, props, opts ) {
+ /* jshint validthis: true */
+ var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
+ anim = this,
+ orig = {},
+ style = elem.style,
+ hidden = elem.nodeType && isHidden( elem ),
+ dataShow = data_priv.get( elem, "fxshow" );
+
+ // Handle queue: false promises
+ if ( !opts.queue ) {
+ hooks = jQuery._queueHooks( elem, "fx" );
+ if ( hooks.unqueued == null ) {
+ hooks.unqueued = 0;
+ oldfire = hooks.empty.fire;
+ hooks.empty.fire = function() {
+ if ( !hooks.unqueued ) {
+ oldfire();
+ }
+ };
+ }
+ hooks.unqueued++;
+
+ anim.always(function() {
+ // Ensure the complete handler is called before this completes
+ anim.always(function() {
+ hooks.unqueued--;
+ if ( !jQuery.queue( elem, "fx" ).length ) {
+ hooks.empty.fire();
+ }
+ });
+ });
+ }
+
+ // Height/width overflow pass
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE9-10 do not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ display = jQuery.css( elem, "display" );
+
+ // Test default display if display is currently "none"
+ checkDisplay = display === "none" ?
+ data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
+
+ if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
+ style.display = "inline-block";
+ }
+ }
+
+ if ( opts.overflow ) {
+ style.overflow = "hidden";
+ anim.always(function() {
+ style.overflow = opts.overflow[ 0 ];
+ style.overflowX = opts.overflow[ 1 ];
+ style.overflowY = opts.overflow[ 2 ];
+ });
+ }
+
+ // show/hide pass
+ for ( prop in props ) {
+ value = props[ prop ];
+ if ( rfxtypes.exec( value ) ) {
+ delete props[ prop ];
+ toggle = toggle || value === "toggle";
+ if ( value === ( hidden ? "hide" : "show" ) ) {
+
+ // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
+ if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
+ hidden = true;
+ } else {
+ continue;
+ }
+ }
+ orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+
+ // Any non-fx value stops us from restoring the original display value
+ } else {
+ display = undefined;
+ }
+ }
+
+ if ( !jQuery.isEmptyObject( orig ) ) {
+ if ( dataShow ) {
+ if ( "hidden" in dataShow ) {
+ hidden = dataShow.hidden;
+ }
+ } else {
+ dataShow = data_priv.access( elem, "fxshow", {} );
+ }
+
+ // Store state if its toggle - enables .stop().toggle() to "reverse"
+ if ( toggle ) {
+ dataShow.hidden = !hidden;
+ }
+ if ( hidden ) {
+ jQuery( elem ).show();
+ } else {
+ anim.done(function() {
+ jQuery( elem ).hide();
+ });
+ }
+ anim.done(function() {
+ var prop;
+
+ data_priv.remove( elem, "fxshow" );
+ for ( prop in orig ) {
+ jQuery.style( elem, prop, orig[ prop ] );
+ }
+ });
+ for ( prop in orig ) {
+ tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
+
+ if ( !( prop in dataShow ) ) {
+ dataShow[ prop ] = tween.start;
+ if ( hidden ) {
+ tween.end = tween.start;
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
+ }
+ }
+ }
+
+ // If this is a noop like .hide().hide(), restore an overwritten display value
+ } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
+ style.display = display;
+ }
+}
+
+function propFilter( props, specialEasing ) {
+ var index, name, easing, value, hooks;
+
+ // camelCase, specialEasing and expand cssHook pass
+ for ( index in props ) {
+ name = jQuery.camelCase( index );
+ easing = specialEasing[ name ];
+ value = props[ index ];
+ if ( jQuery.isArray( value ) ) {
+ easing = value[ 1 ];
+ value = props[ index ] = value[ 0 ];
+ }
+
+ if ( index !== name ) {
+ props[ name ] = value;
+ delete props[ index ];
+ }
+
+ hooks = jQuery.cssHooks[ name ];
+ if ( hooks && "expand" in hooks ) {
+ value = hooks.expand( value );
+ delete props[ name ];
+
+ // Not quite $.extend, this won't overwrite existing keys.
+ // Reusing 'index' because we have the correct "name"
+ for ( index in value ) {
+ if ( !( index in props ) ) {
+ props[ index ] = value[ index ];
+ specialEasing[ index ] = easing;
+ }
+ }
+ } else {
+ specialEasing[ name ] = easing;
+ }
+ }
+}
+
+function Animation( elem, properties, options ) {
+ var result,
+ stopped,
+ index = 0,
+ length = animationPrefilters.length,
+ deferred = jQuery.Deferred().always( function() {
+ // Don't match elem in the :animated selector
+ delete tick.elem;
+ }),
+ tick = function() {
+ if ( stopped ) {
+ return false;
+ }
+ var currentTime = fxNow || createFxNow(),
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+ // Support: Android 2.3
+ // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
+ temp = remaining / animation.duration || 0,
+ percent = 1 - temp,
+ index = 0,
+ length = animation.tweens.length;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( percent );
+ }
+
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+ if ( percent < 1 && length ) {
+ return remaining;
+ } else {
+ deferred.resolveWith( elem, [ animation ] );
+ return false;
+ }
+ },
+ animation = deferred.promise({
+ elem: elem,
+ props: jQuery.extend( {}, properties ),
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
+ originalProperties: properties,
+ originalOptions: options,
+ startTime: fxNow || createFxNow(),
+ duration: options.duration,
+ tweens: [],
+ createTween: function( prop, end ) {
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
+ animation.tweens.push( tween );
+ return tween;
+ },
+ stop: function( gotoEnd ) {
+ var index = 0,
+ // If we are going to the end, we want to run all the tweens
+ // otherwise we skip this part
+ length = gotoEnd ? animation.tweens.length : 0;
+ if ( stopped ) {
+ return this;
+ }
+ stopped = true;
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( 1 );
+ }
+
+ // Resolve when we played the last frame; otherwise, reject
+ if ( gotoEnd ) {
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
+ } else {
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
+ }
+ return this;
+ }
+ }),
+ props = animation.props;
+
+ propFilter( props, animation.opts.specialEasing );
+
+ for ( ; index < length ; index++ ) {
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+ if ( result ) {
+ return result;
+ }
+ }
+
+ jQuery.map( props, createTween, animation );
+
+ if ( jQuery.isFunction( animation.opts.start ) ) {
+ animation.opts.start.call( elem, animation );
+ }
+
+ jQuery.fx.timer(
+ jQuery.extend( tick, {
+ elem: elem,
+ anim: animation,
+ queue: animation.opts.queue
+ })
+ );
+
+ // attach callbacks from options
+ return animation.progress( animation.opts.progress )
+ .done( animation.opts.done, animation.opts.complete )
+ .fail( animation.opts.fail )
+ .always( animation.opts.always );
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+ tweener: function( props, callback ) {
+ if ( jQuery.isFunction( props ) ) {
+ callback = props;
+ props = [ "*" ];
+ } else {
+ props = props.split(" ");
+ }
+
+ var prop,
+ index = 0,
+ length = props.length;
+
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
+ }
+ },
+
+ prefilter: function( callback, prepend ) {
+ if ( prepend ) {
+ animationPrefilters.unshift( callback );
+ } else {
+ animationPrefilters.push( callback );
+ }
+ }
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // Normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function() {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ }
+ };
+
+ return opt;
+};
+
+jQuery.fn.extend({
+ fadeTo: function( speed, to, easing, callback ) {
+
+ // Show any hidden elements after setting opacity to 0
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+ // Animate to the value specified
+ .end().animate({ opacity: to }, speed, easing, callback );
+ },
+ animate: function( prop, speed, easing, callback ) {
+ var empty = jQuery.isEmptyObject( prop ),
+ optall = jQuery.speed( speed, easing, callback ),
+ doAnimation = function() {
+ // Operate on a copy of prop so per-property easing won't be lost
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+ // Empty animations, or finishing resolves immediately
+ if ( empty || data_priv.get( this, "finish" ) ) {
+ anim.stop( true );
+ }
+ };
+ doAnimation.finish = doAnimation;
+
+ return empty || optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+ stop: function( type, clearQueue, gotoEnd ) {
+ var stopQueue = function( hooks ) {
+ var stop = hooks.stop;
+ delete hooks.stop;
+ stop( gotoEnd );
+ };
+
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var dequeue = true,
+ index = type != null && type + "queueHooks",
+ timers = jQuery.timers,
+ data = data_priv.get( this );
+
+ if ( index ) {
+ if ( data[ index ] && data[ index ].stop ) {
+ stopQueue( data[ index ] );
+ }
+ } else {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+ stopQueue( data[ index ] );
+ }
+ }
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ timers[ index ].anim.stop( gotoEnd );
+ dequeue = false;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // Start the next in the queue if the last step wasn't forced.
+ // Timers currently will call their complete callbacks, which
+ // will dequeue but only if they were gotoEnd.
+ if ( dequeue || !gotoEnd ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ finish: function( type ) {
+ if ( type !== false ) {
+ type = type || "fx";
+ }
+ return this.each(function() {
+ var index,
+ data = data_priv.get( this ),
+ queue = data[ type + "queue" ],
+ hooks = data[ type + "queueHooks" ],
+ timers = jQuery.timers,
+ length = queue ? queue.length : 0;
+
+ // Enable finishing flag on private data
+ data.finish = true;
+
+ // Empty the queue first
+ jQuery.queue( this, type, [] );
+
+ if ( hooks && hooks.stop ) {
+ hooks.stop.call( this, true );
+ }
+
+ // Look for any active animations, and finish them
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+ timers[ index ].anim.stop( true );
+ timers.splice( index, 1 );
+ }
+ }
+
+ // Look for any animations in the old queue and finish them
+ for ( index = 0; index < length; index++ ) {
+ if ( queue[ index ] && queue[ index ].finish ) {
+ queue[ index ].finish.call( this );
+ }
+ }
+
+ // Turn off finishing flag
+ delete data.finish;
+ });
+ }
+});
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+ var cssFn = jQuery.fn[ name ];
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return speed == null || typeof speed === "boolean" ?
+ cssFn.apply( this, arguments ) :
+ this.animate( genFx( name, true ), speed, easing, callback );
+ };
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show"),
+ slideUp: genFx("hide"),
+ slideToggle: genFx("toggle"),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.timers = [];
+jQuery.fx.tick = function() {
+ var timer,
+ i = 0,
+ timers = jQuery.timers;
+
+ fxNow = jQuery.now();
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+ jQuery.timers.push( timer );
+ if ( timer() ) {
+ jQuery.fx.start();
+ } else {
+ jQuery.timers.pop();
+ }
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+ if ( !timerId ) {
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+ }
+};
+
+jQuery.fx.stop = function() {
+ clearInterval( timerId );
+ timerId = null;
+};
+
+jQuery.fx.speeds = {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+};
+
+
+// Based off of the plugin by Clint Helfers, with permission.
+// http://blindsignals.com/index.php/2009/07/jquery-delay/
+jQuery.fn.delay = function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+};
+
+
+(function() {
+ var input = document.createElement( "input" ),
+ select = document.createElement( "select" ),
+ opt = select.appendChild( document.createElement( "option" ) );
+
+ input.type = "checkbox";
+
+ // Support: iOS<=5.1, Android<=4.2+
+ // Default value for a checkbox should be "on"
+ support.checkOn = input.value !== "";
+
+ // Support: IE<=11+
+ // Must access selectedIndex to make default options select
+ support.optSelected = opt.selected;
+
+ // Support: Android<=2.3
+ // Options inside disabled selects are incorrectly marked as disabled
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Support: IE<=11+
+ // An input loses its value after becoming a radio
+ input = document.createElement( "input" );
+ input.value = "t";
+ input.type = "radio";
+ support.radioValue = input.value === "t";
+})();
+
+
+var nodeHook, boolHook,
+ attrHandle = jQuery.expr.attrHandle;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ }
+});
+
+jQuery.extend({
+ attr: function( elem, name, value ) {
+ var hooks, ret,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === strundefined ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] ||
+ ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+
+ } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, value + "" );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ ret = jQuery.find.attr( elem, name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret == null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var name, propName,
+ i = 0,
+ attrNames = value && value.match( rnotwhite );
+
+ if ( attrNames && elem.nodeType === 1 ) {
+ while ( (name = attrNames[i++]) ) {
+ propName = jQuery.propFix[ name ] || name;
+
+ // Boolean attributes get special treatment (#10870)
+ if ( jQuery.expr.match.bool.test( name ) ) {
+ // Set corresponding property to false
+ elem[ propName ] = false;
+ }
+
+ elem.removeAttribute( name );
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ if ( !support.radioValue && value === "radio" &&
+ jQuery.nodeName( elem, "input" ) ) {
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ }
+ }
+});
+
+// Hooks for boolean attributes
+boolHook = {
+ set: function( elem, value, name ) {
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ elem.setAttribute( name, name );
+ }
+ return name;
+ }
+};
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+ var getter = attrHandle[ name ] || jQuery.find.attr;
+
+ attrHandle[ name ] = function( elem, name, isXML ) {
+ var ret, handle;
+ if ( !isXML ) {
+ // Avoid an infinite loop by temporarily removing this function from the getter
+ handle = attrHandle[ name ];
+ attrHandle[ name ] = ret;
+ ret = getter( elem, name, isXML ) != null ?
+ name.toLowerCase() :
+ null;
+ attrHandle[ name ] = handle;
+ }
+ return ret;
+ };
+});
+
+
+
+
+var rfocusable = /^(?:input|select|textarea|button)$/i;
+
+jQuery.fn.extend({
+ prop: function( name, value ) {
+ return access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ return this.each(function() {
+ delete this[ jQuery.propFix[ name ] || name ];
+ });
+ }
+});
+
+jQuery.extend({
+ propFix: {
+ "for": "htmlFor",
+ "class": "className"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // Don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+ ret :
+ ( elem[ name ] = value );
+
+ } else {
+ return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+ ret :
+ elem[ name ];
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
+ elem.tabIndex :
+ -1;
+ }
+ }
+ }
+});
+
+if ( !support.optSelected ) {
+ jQuery.propHooks.selected = {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+ if ( parent && parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ return null;
+ }
+ };
+}
+
+jQuery.each([
+ "tabIndex",
+ "readOnly",
+ "maxLength",
+ "cellSpacing",
+ "cellPadding",
+ "rowSpan",
+ "colSpan",
+ "useMap",
+ "frameBorder",
+ "contentEditable"
+], function() {
+ jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+
+
+
+var rclass = /[\t\r\n\f]/g;
+
+jQuery.fn.extend({
+ addClass: function( value ) {
+ var classes, elem, cur, clazz, j, finalValue,
+ proceed = typeof value === "string" && value,
+ i = 0,
+ len = this.length;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call( this, j, this.className ) );
+ });
+ }
+
+ if ( proceed ) {
+ // The disjunction here is for better compressibility (see removeClass)
+ classes = ( value || "" ).match( rnotwhite ) || [];
+
+ for ( ; i < len; i++ ) {
+ elem = this[ i ];
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ " "
+ );
+
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+ cur += clazz + " ";
+ }
+ }
+
+ // only assign if different to avoid unneeded rendering.
+ finalValue = jQuery.trim( cur );
+ if ( elem.className !== finalValue ) {
+ elem.className = finalValue;
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classes, elem, cur, clazz, j, finalValue,
+ proceed = arguments.length === 0 || typeof value === "string" && value,
+ i = 0,
+ len = this.length;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call( this, j, this.className ) );
+ });
+ }
+ if ( proceed ) {
+ classes = ( value || "" ).match( rnotwhite ) || [];
+
+ for ( ; i < len; i++ ) {
+ elem = this[ i ];
+ // This expression is here for better compressibility (see addClass)
+ cur = elem.nodeType === 1 && ( elem.className ?
+ ( " " + elem.className + " " ).replace( rclass, " " ) :
+ ""
+ );
+
+ if ( cur ) {
+ j = 0;
+ while ( (clazz = classes[j++]) ) {
+ // Remove *all* instances
+ while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+ cur = cur.replace( " " + clazz + " ", " " );
+ }
+ }
+
+ // Only assign if different to avoid unneeded rendering.
+ finalValue = value ? jQuery.trim( cur ) : "";
+ if ( elem.className !== finalValue ) {
+ elem.className = finalValue;
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value;
+
+ if ( typeof stateVal === "boolean" && type === "string" ) {
+ return stateVal ? this.addClass( value ) : this.removeClass( value );
+ }
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // Toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ classNames = value.match( rnotwhite ) || [];
+
+ while ( (className = classNames[ i++ ]) ) {
+ // Check each className given, space separated list
+ if ( self.hasClass( className ) ) {
+ self.removeClass( className );
+ } else {
+ self.addClass( className );
+ }
+ }
+
+ // Toggle whole class name
+ } else if ( type === strundefined || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ data_priv.set( this, "__className__", this.className );
+ }
+
+ // If the element has a class name or if we're passed `false`,
+ // then remove the whole classname (if there was one, the above saved it).
+ // Otherwise bring back whatever was previously saved (if anything),
+ // falling back to the empty string if nothing was stored.
+ this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+});
+
+
+
+
+var rreturn = /\r/g;
+
+jQuery.fn.extend({
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // Handle most common string cases
+ ret.replace(rreturn, "") :
+ // Handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, jQuery( this ).val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+
+ } else if ( typeof val === "number" ) {
+ val += "";
+
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map( val, function( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ var val = jQuery.find.attr( elem, "value" );
+ return val != null ?
+ val :
+ // Support: IE10-11+
+ // option.text throws exceptions (#14686, #14858)
+ jQuery.trim( jQuery.text( elem ) );
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, option,
+ options = elem.options,
+ index = elem.selectedIndex,
+ one = elem.type === "select-one" || index < 0,
+ values = one ? null : [],
+ max = one ? index + 1 : options.length,
+ i = index < 0 ?
+ max :
+ one ? index : 0;
+
+ // Loop through all the selected options
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // IE6-9 doesn't update selected after form reset (#2551)
+ if ( ( option.selected || i === index ) &&
+ // Don't return options that are disabled or in a disabled optgroup
+ ( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var optionSet, option,
+ options = elem.options,
+ values = jQuery.makeArray( value ),
+ i = options.length;
+
+ while ( i-- ) {
+ option = options[ i ];
+ if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) {
+ optionSet = true;
+ }
+ }
+
+ // Force browsers to behave consistently when non-matching value is set
+ if ( !optionSet ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ }
+});
+
+// Radios and checkboxes getter/setter
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ };
+ if ( !support.checkOn ) {
+ jQuery.valHooks[ this ].get = function( elem ) {
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ };
+ }
+});
+
+
+
+
+// Return jQuery for attributes-only inclusion
+
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+});
+
+jQuery.fn.extend({
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+ }
+});
+
+
+var nonce = jQuery.now();
+
+var rquery = (/\?/);
+
+
+
+// Support: Android 2.3
+// Workaround failure to string-cast null input
+jQuery.parseJSON = function( data ) {
+ return JSON.parse( data + "" );
+};
+
+
+// Cross-browser xml parsing
+jQuery.parseXML = function( data ) {
+ var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+
+ // Support: IE9
+ try {
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data, "text/xml" );
+ } catch ( e ) {
+ xml = undefined;
+ }
+
+ if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+};
+
+
+var
+ rhash = /#.*$/,
+ rts = /([?&])_=[^&]*/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ allTypes = "*/".concat( "*" ),
+
+ // Document location
+ ajaxLocation = window.location.href,
+
+ // Segment location into parts
+ ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ var dataType,
+ i = 0,
+ dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
+
+ if ( jQuery.isFunction( func ) ) {
+ // For each dataType in the dataTypeExpression
+ while ( (dataType = dataTypes[i++]) ) {
+ // Prepend if requested
+ if ( dataType[0] === "+" ) {
+ dataType = dataType.slice( 1 ) || "*";
+ (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+ // Otherwise append
+ } else {
+ (structure[ dataType ] = structure[ dataType ] || []).push( func );
+ }
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+ var inspected = {},
+ seekingTransport = ( structure === transports );
+
+ function inspect( dataType ) {
+ var selected;
+ inspected[ dataType ] = true;
+ jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+ var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+ if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+ options.dataTypes.unshift( dataTypeOrTransport );
+ inspect( dataTypeOrTransport );
+ return false;
+ } else if ( seekingTransport ) {
+ return !( selected = dataTypeOrTransport );
+ }
+ });
+ return selected;
+ }
+
+ return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+ var key, deep,
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+ for ( key in src ) {
+ if ( src[ key ] !== undefined ) {
+ ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+ }
+ }
+ if ( deep ) {
+ jQuery.extend( true, target, deep );
+ }
+
+ return target;
+}
+
+/* Handles responses to an ajax request:
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var ct, type, finalDataType, firstDataType,
+ contents = s.contents,
+ dataTypes = s.dataTypes;
+
+ // Remove auto dataType and get content-type in the process
+ while ( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+/* Chain conversions given the request and the original response
+ * Also sets the responseXXX fields on the jqXHR instance
+ */
+function ajaxConvert( s, response, jqXHR, isSuccess ) {
+ var conv2, current, conv, tmp, prev,
+ converters = {},
+ // Work with a copy of dataTypes in case we need to modify it for conversion
+ dataTypes = s.dataTypes.slice();
+
+ // Create converters map with lowercased keys
+ if ( dataTypes[ 1 ] ) {
+ for ( conv in s.converters ) {
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
+ }
+ }
+
+ current = dataTypes.shift();
+
+ // Convert to each sequential dataType
+ while ( current ) {
+
+ if ( s.responseFields[ current ] ) {
+ jqXHR[ s.responseFields[ current ] ] = response;
+ }
+
+ // Apply the dataFilter if provided
+ if ( !prev && isSuccess && s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ prev = current;
+ current = dataTypes.shift();
+
+ if ( current ) {
+
+ // There's only work to do if current dataType is non-auto
+ if ( current === "*" ) {
+
+ current = prev;
+
+ // Convert response if prev dataType is non-auto and differs from current
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Seek a direct converter
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+ // If none found, seek a pair
+ if ( !conv ) {
+ for ( conv2 in converters ) {
+
+ // If conv2 outputs current
+ tmp = conv2.split( " " );
+ if ( tmp[ 1 ] === current ) {
+
+ // If prev can be converted to accepted input
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
+ converters[ "* " + tmp[ 0 ] ];
+ if ( conv ) {
+ // Condense equivalence converters
+ if ( conv === true ) {
+ conv = converters[ conv2 ];
+
+ // Otherwise, insert the intermediate dataType
+ } else if ( converters[ conv2 ] !== true ) {
+ current = tmp[ 0 ];
+ dataTypes.unshift( tmp[ 1 ] );
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Apply converter (if not an equivalence)
+ if ( conv !== true ) {
+
+ // Unless errors are allowed to bubble, catch and return them
+ if ( conv && s[ "throws" ] ) {
+ response = conv( response );
+ } else {
+ try {
+ response = conv( response );
+ } catch ( e ) {
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return { state: "success", data: response };
+}
+
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {},
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ type: "GET",
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ processData: true,
+ async: true,
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ throws: false,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ "*": allTypes,
+ text: "text/plain",
+ html: "text/html",
+ xml: "application/xml, text/xml",
+ json: "application/json, text/javascript"
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText",
+ json: "responseJSON"
+ },
+
+ // Data converters
+ // Keys separate source (or catchall "*") and destination types with a single space
+ converters: {
+
+ // Convert anything to text
+ "* text": String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ url: true,
+ context: true
+ }
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ return settings ?
+
+ // Building a settings object
+ ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+ // Extending ajaxSettings
+ ajaxExtend( jQuery.ajaxSettings, target );
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var transport,
+ // URL without anti-cache param
+ cacheURL,
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events is callbackContext if it is a DOM node or jQuery collection
+ globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+ jQuery( callbackContext ) :
+ jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks("once memory"),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // The jqXHR state
+ state = 0,
+ // Default abort message
+ strAbort = "canceled",
+ // Fake xhr
+ jqXHR = {
+ readyState: 0,
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while ( (match = rheaders.exec( responseHeadersString )) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match == null ? null : match;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ var lname = name.toLowerCase();
+ if ( !state ) {
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Status-dependent callbacks
+ statusCode: function( map ) {
+ var code;
+ if ( map ) {
+ if ( state < 2 ) {
+ for ( code in map ) {
+ // Lazy-add the new callback in a way that preserves old ones
+ statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+ }
+ } else {
+ // Execute the appropriate callbacks
+ jqXHR.always( map[ jqXHR.status ] );
+ }
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ var finalText = statusText || strAbort;
+ if ( transport ) {
+ transport.abort( finalText );
+ }
+ done( 0, finalText );
+ return this;
+ }
+ };
+
+ // Attach deferreds
+ deferred.promise( jqXHR ).complete = completeDeferred.add;
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (prefilters might expect it)
+ // Handle falsy url in the settings object (#10093: consistency with old signature)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
+ .replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Alias method option to type as per ticket #12004
+ s.type = options.method || options.type || s.method || s.type;
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
+
+ // A cross-domain request is in order when we have a protocol:host:port mismatch
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefilter, stop there
+ if ( state === 2 ) {
+ return jqXHR;
+ }
+
+ // We can fire global events as of now if asked to
+ // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
+ fireGlobals = jQuery.event && s.global;
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger("ajaxStart");
+ }
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Save the URL in case we're toying with the If-Modified-Since
+ // and/or If-None-Match header later on
+ cacheURL = s.url;
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+ s.url = rts.test( cacheURL ) ?
+
+ // If there is already a '_' parameter, set its value
+ cacheURL.replace( rts, "$1_=" + nonce++ ) :
+
+ // Otherwise add one to the end
+ cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
+ }
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ if ( jQuery.lastModified[ cacheURL ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+ }
+ if ( jQuery.etag[ cacheURL ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already and return
+ return jqXHR.abort();
+ }
+
+ // Aborting is no longer a cancellation
+ strAbort = "abort";
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout(function() {
+ jqXHR.abort("timeout");
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch ( e ) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ // Callback for when everything is done
+ function done( status, nativeStatusText, responses, headers ) {
+ var isSuccess, success, error, response, modified,
+ statusText = nativeStatusText;
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ // Determine if successful
+ isSuccess = status >= 200 && status < 300 || status === 304;
+
+ // Get response data
+ if ( responses ) {
+ response = ajaxHandleResponses( s, jqXHR, responses );
+ }
+
+ // Convert no matter what (that way responseXXX fields are always set)
+ response = ajaxConvert( s, response, jqXHR, isSuccess );
+
+ // If successful, handle type chaining
+ if ( isSuccess ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ modified = jqXHR.getResponseHeader("Last-Modified");
+ if ( modified ) {
+ jQuery.lastModified[ cacheURL ] = modified;
+ }
+ modified = jqXHR.getResponseHeader("etag");
+ if ( modified ) {
+ jQuery.etag[ cacheURL ] = modified;
+ }
+ }
+
+ // if no content
+ if ( status === 204 || s.type === "HEAD" ) {
+ statusText = "nocontent";
+
+ // if not modified
+ } else if ( status === 304 ) {
+ statusText = "notmodified";
+
+ // If we have data, let's convert it
+ } else {
+ statusText = response.state;
+ success = response.data;
+ error = response.error;
+ isSuccess = !error;
+ }
+ } else {
+ // Extract error from statusText and normalize for non-aborts
+ error = statusText;
+ if ( status || !statusText ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger("ajaxStop");
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ }
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // Shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ url: url,
+ type: method,
+ dataType: type,
+ data: data,
+ success: callback
+ });
+ };
+});
+
+
+jQuery._evalUrl = function( url ) {
+ return jQuery.ajax({
+ url: url,
+ type: "GET",
+ dataType: "script",
+ async: false,
+ global: false,
+ "throws": true
+ });
+};
+
+
+jQuery.fn.extend({
+ wrapAll: function( html ) {
+ var wrap;
+
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[ 0 ] ) {
+
+ // The elements to wrap the target around
+ wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
+
+ if ( this[ 0 ].parentNode ) {
+ wrap.insertBefore( this[ 0 ] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstElementChild ) {
+ elem = elem.firstElementChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function( i ) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ }
+});
+
+
+jQuery.expr.filters.hidden = function( elem ) {
+ // Support: Opera <= 12.12
+ // Opera reports offsetWidths and offsetHeights less than zero on some elements
+ return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
+};
+jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+};
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+ rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+function buildParams( prefix, obj, traditional, add ) {
+ var name;
+
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // Item is non-scalar (array or object), encode its numeric index.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// Serialize an array of form elements or a set of
+// key/values into a query string
+jQuery.param = function( a, traditional ) {
+ var prefix,
+ s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+};
+
+jQuery.fn.extend({
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+ serializeArray: function() {
+ return this.map(function() {
+ // Can add propHook for "elements" to filter or add form elements
+ var elements = jQuery.prop( this, "elements" );
+ return elements ? jQuery.makeArray( elements ) : this;
+ })
+ .filter(function() {
+ var type = this.type;
+
+ // Use .is( ":disabled" ) so that fieldset[disabled] works
+ return this.name && !jQuery( this ).is( ":disabled" ) &&
+ rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+ ( this.checked || !rcheckableType.test( type ) );
+ })
+ .map(function( i, elem ) {
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val ) {
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+
+jQuery.ajaxSettings.xhr = function() {
+ try {
+ return new XMLHttpRequest();
+ } catch( e ) {}
+};
+
+var xhrId = 0,
+ xhrCallbacks = {},
+ xhrSuccessStatus = {
+ // file protocol always yields status code 0, assume 200
+ 0: 200,
+ // Support: IE9
+ // #1450: sometimes IE returns 1223 when it should be 204
+ 1223: 204
+ },
+ xhrSupported = jQuery.ajaxSettings.xhr();
+
+// Support: IE9
+// Open requests must be manually aborted on unload (#5280)
+// See https://support.microsoft.com/kb/2856746 for more info
+if ( window.attachEvent ) {
+ window.attachEvent( "onunload", function() {
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]();
+ }
+ });
+}
+
+support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+support.ajax = xhrSupported = !!xhrSupported;
+
+jQuery.ajaxTransport(function( options ) {
+ var callback;
+
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( support.cors || xhrSupported && !options.crossDomain ) {
+ return {
+ send: function( headers, complete ) {
+ var i,
+ xhr = options.xhr(),
+ id = ++xhrId;
+
+ xhr.open( options.type, options.url, options.async, options.username, options.password );
+
+ // Apply custom fields if provided
+ if ( options.xhrFields ) {
+ for ( i in options.xhrFields ) {
+ xhr[ i ] = options.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( options.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( options.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !options.crossDomain && !headers["X-Requested-With"] ) {
+ headers["X-Requested-With"] = "XMLHttpRequest";
+ }
+
+ // Set headers
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+
+ // Callback
+ callback = function( type ) {
+ return function() {
+ if ( callback ) {
+ delete xhrCallbacks[ id ];
+ callback = xhr.onload = xhr.onerror = null;
+
+ if ( type === "abort" ) {
+ xhr.abort();
+ } else if ( type === "error" ) {
+ complete(
+ // file: protocol always yields status 0; see #8605, #14207
+ xhr.status,
+ xhr.statusText
+ );
+ } else {
+ complete(
+ xhrSuccessStatus[ xhr.status ] || xhr.status,
+ xhr.statusText,
+ // Support: IE9
+ // Accessing binary-data responseText throws an exception
+ // (#11426)
+ typeof xhr.responseText === "string" ? {
+ text: xhr.responseText
+ } : undefined,
+ xhr.getAllResponseHeaders()
+ );
+ }
+ }
+ };
+ };
+
+ // Listen to events
+ xhr.onload = callback();
+ xhr.onerror = callback("error");
+
+ // Create the abort callback
+ callback = xhrCallbacks[ id ] = callback("abort");
+
+ try {
+ // Do send the request (this may raise an exception)
+ xhr.send( options.hasContent && options.data || null );
+ } catch ( e ) {
+ // #14683: Only rethrow if this hasn't been notified as an error yet
+ if ( callback ) {
+ throw e;
+ }
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback();
+ }
+ }
+ };
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /(?:java|ecma)script/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and crossDomain
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function( s ) {
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+ var script, callback;
+ return {
+ send: function( _, complete ) {
+ script = jQuery("<script>").prop({
+ async: true,
+ charset: s.scriptCharset,
+ src: s.url
+ }).on(
+ "load error",
+ callback = function( evt ) {
+ script.remove();
+ callback = null;
+ if ( evt ) {
+ complete( evt.type === "error" ? 404 : 200, evt.type );
+ }
+ }
+ );
+ document.head.appendChild( script[ 0 ] );
+ },
+ abort: function() {
+ if ( callback ) {
+ callback();
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var oldCallbacks = [],
+ rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+ this[ callback ] = true;
+ return callback;
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var callbackName, overwritten, responseContainer,
+ jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+ "url" :
+ typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+ );
+
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
+ if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+ // Get callback name, remembering preexisting value associated with it
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+ s.jsonpCallback() :
+ s.jsonpCallback;
+
+ // Insert callback into url or form data
+ if ( jsonProp ) {
+ s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+ } else if ( s.jsonp !== false ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+ }
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( callbackName + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Install callback
+ overwritten = window[ callbackName ];
+ window[ callbackName ] = function() {
+ responseContainer = arguments;
+ };
+
+ // Clean-up function (fires after converters)
+ jqXHR.always(function() {
+ // Restore preexisting value
+ window[ callbackName ] = overwritten;
+
+ // Save back as free
+ if ( s[ callbackName ] ) {
+ // make sure that re-using the options doesn't screw things around
+ s.jsonpCallback = originalSettings.jsonpCallback;
+
+ // save the callback name for future use
+ oldCallbacks.push( callbackName );
+ }
+
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+ overwritten( responseContainer[ 0 ] );
+ }
+
+ responseContainer = overwritten = undefined;
+ });
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// data: string of html
+// context (optional): If specified, the fragment will be created in this context, defaults to document
+// keepScripts (optional): If true, will include scripts passed in the html string
+jQuery.parseHTML = function( data, context, keepScripts ) {
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ keepScripts = context;
+ context = false;
+ }
+ context = context || document;
+
+ var parsed = rsingleTag.exec( data ),
+ scripts = !keepScripts && [];
+
+ // Single tag
+ if ( parsed ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts );
+
+ if ( scripts && scripts.length ) {
+ jQuery( scripts ).remove();
+ }
+
+ return jQuery.merge( [], parsed.childNodes );
+};
+
+
+// Keep a copy of the old load method
+var _load = jQuery.fn.load;
+
+/**
+ * Load a url into a page
+ */
+jQuery.fn.load = function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+ }
+
+ var selector, type, response,
+ self = this,
+ off = url.indexOf(" ");
+
+ if ( off >= 0 ) {
+ selector = jQuery.trim( url.slice( off ) );
+ url = url.slice( 0, off );
+ }
+
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( params && typeof params === "object" ) {
+ type = "POST";
+ }
+
+ // If we have elements to modify, make the request
+ if ( self.length > 0 ) {
+ jQuery.ajax({
+ url: url,
+
+ // if "type" variable is undefined, then "GET" method will be used
+ type: type,
+ dataType: "html",
+ data: params
+ }).done(function( responseText ) {
+
+ // Save response for use in complete callback
+ response = arguments;
+
+ self.html( selector ?
+
+ // If a selector was specified, locate the right elements in a dummy div
+ // Exclude scripts to avoid IE 'Permission Denied' errors
+ jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+ // Otherwise use the full result
+ responseText );
+
+ }).complete( callback && function( jqXHR, status ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+ });
+ }
+
+ return this;
+};
+
+
+
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
+ jQuery.fn[ type ] = function( fn ) {
+ return this.on( type, fn );
+ };
+});
+
+
+
+
+jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+};
+
+
+
+
+var docElem = window.document.documentElement;
+
+/**
+ * Gets a window from an element
+ */
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
+}
+
+jQuery.offset = {
+ setOffset: function( elem, options, i ) {
+ var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+ position = jQuery.css( elem, "position" ),
+ curElem = jQuery( elem ),
+ props = {};
+
+ // Set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ curOffset = curElem.offset();
+ curCSSTop = jQuery.css( elem, "top" );
+ curCSSLeft = jQuery.css( elem, "left" );
+ calculatePosition = ( position === "absolute" || position === "fixed" ) &&
+ ( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
+
+ // Need to be able to calculate position if either
+ // top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+jQuery.fn.extend({
+ offset: function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var docElem, win,
+ elem = this[ 0 ],
+ box = { top: 0, left: 0 },
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return;
+ }
+
+ docElem = doc.documentElement;
+
+ // Make sure it's not a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return box;
+ }
+
+ // Support: BlackBerry 5, iOS 3 (original iPhone)
+ // If we don't have gBCR, just use 0,0 rather than error
+ if ( typeof elem.getBoundingClientRect !== strundefined ) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow( doc );
+ return {
+ top: box.top + win.pageYOffset - docElem.clientTop,
+ left: box.left + win.pageXOffset - docElem.clientLeft
+ };
+ },
+
+ position: function() {
+ if ( !this[ 0 ] ) {
+ return;
+ }
+
+ var offsetParent, offset,
+ elem = this[ 0 ],
+ parentOffset = { top: 0, left: 0 };
+
+ // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
+ // Assume getBoundingClientRect is there when computed position is fixed
+ offset = elem.getBoundingClientRect();
+
+ } else {
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent();
+
+ // Get correct offsets
+ offset = this.offset();
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+ parentOffset = offsetParent.offset();
+ }
+
+ // Add offsetParent borders
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+ }
+
+ // Subtract parent offsets and element margins
+ return {
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || docElem;
+
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+
+ return offsetParent || docElem;
+ });
+ }
+});
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
+ var top = "pageYOffset" === prop;
+
+ jQuery.fn[ method ] = function( val ) {
+ return access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? win[ prop ] : elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : window.pageXOffset,
+ top ? val : window.pageYOffset
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+// Support: Safari<7+, Chrome<37+
+// Add the top/left cssHooks using jQuery.fn.position
+// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280
+// getComputedStyle returns percent when specified for top/left/bottom/right;
+// rather than make the css module depend on the offset module, just check for it here
+jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+ function( elem, computed ) {
+ if ( computed ) {
+ computed = curCSS( elem, prop );
+ // If curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( computed ) ?
+ jQuery( elem ).position()[ prop ] + "px" :
+ computed;
+ }
+ }
+ );
+});
+
+
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // Margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
+ // whichever is greatest
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
+ }
+
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, extra ) :
+
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable, null );
+ };
+ });
+});
+
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+ return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+
+
+
+// Register as a named AMD module, since jQuery can be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Lowercase jquery is used because AMD module names are
+// derived from file names, and jQuery is normally delivered in a lowercase
+// file name. Do this after creating the global so that if an AMD module wants
+// to call noConflict to hide this version of jQuery, it will work.
+
+// Note that for maximum portability, libraries that are not jQuery should
+// declare themselves as anonymous modules, and avoid setting a global if an
+// AMD loader is present. jQuery is a special case. For more information, see
+// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
+
+if ( typeof define === "function" && define.amd ) {
+ define( "jquery", [], function() {
+ return jQuery;
+ });
+}
+
+
+
+
+var
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$;
+
+jQuery.noConflict = function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+};
+
+// Expose jQuery and $ identifiers, even in AMD
+// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (#13566)
+if ( typeof noGlobal === strundefined ) {
+ window.jQuery = window.$ = jQuery;
+}
+
+
+
+
+return jQuery;
+
+}));
+
+},{}],19:[function(require,module,exports){
+var baseIndexOf = require('../internal/baseIndexOf'),
+ binaryIndex = require('../internal/binaryIndex');
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max;
+
+/**
+ * Gets the index at which the first occurrence of `value` is found in `array`
+ * using `SameValueZero` for equality comparisons. If `fromIndex` is negative,
+ * it is used as the offset from the end of `array`. If `array` is sorted
+ * providing `true` for `fromIndex` performs a faster binary search.
+ *
+ * **Note:** [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero)
+ * comparisons are like strict equality comparisons, e.g. `===`, except that
+ * `NaN` matches `NaN`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+ * to perform a binary search on a sorted array.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 1, 2], 2);
+ * // => 1
+ *
+ * // using `fromIndex`
+ * _.indexOf([1, 2, 1, 2], 2, 2);
+ * // => 3
+ *
+ * // performing a binary search
+ * _.indexOf([1, 1, 2, 2], 2, true);
+ * // => 2
+ */
+function indexOf(array, value, fromIndex) {
+ var length = array ? array.length : 0;
+ if (!length) {
+ return -1;
+ }
+ if (typeof fromIndex == 'number') {
+ fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex;
+ } else if (fromIndex) {
+ var index = binaryIndex(array, value),
+ other = array[index];
+
+ if (value === value ? (value === other) : (other !== other)) {
+ return index;
+ }
+ return -1;
+ }
+ return baseIndexOf(array, value, fromIndex || 0);
+}
+
+module.exports = indexOf;
+
+},{"../internal/baseIndexOf":42,"../internal/binaryIndex":54}],20:[function(require,module,exports){
+var LazyWrapper = require('../internal/LazyWrapper'),
+ LodashWrapper = require('../internal/LodashWrapper'),
+ baseLodash = require('../internal/baseLodash'),
+ isArray = require('../lang/isArray'),
+ isObjectLike = require('../internal/isObjectLike'),
+ wrapperClone = require('../internal/wrapperClone');
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Creates a `lodash` object which wraps `value` to enable implicit chaining.
+ * Methods that operate on and return arrays, collections, and functions can
+ * be chained together. Methods that return a boolean or single value will
+ * automatically end the chain returning the unwrapped value. Explicit chaining
+ * may be enabled using `_.chain`. The execution of chained methods is lazy,
+ * that is, execution is deferred until `_#value` is implicitly or explicitly
+ * called.
+ *
+ * Lazy evaluation allows several methods to support shortcut fusion. Shortcut
+ * fusion is an optimization that merges iteratees to avoid creating intermediate
+ * arrays and reduce the number of iteratee executions.
+ *
+ * Chaining is supported in custom builds as long as the `_#value` method is
+ * directly or indirectly included in the build.
+ *
+ * In addition to lodash methods, wrappers have `Array` and `String` methods.
+ *
+ * The wrapper `Array` methods are:
+ * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`,
+ * `splice`, and `unshift`
+ *
+ * The wrapper `String` methods are:
+ * `replace` and `split`
+ *
+ * The wrapper methods that support shortcut fusion are:
+ * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`,
+ * `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`,
+ * `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`,
+ * and `where`
+ *
+ * The chainable wrapper methods are:
+ * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`,
+ * `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`,
+ * `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`,
+ * `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`,
+ * `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`,
+ * `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`,
+ * `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`,
+ * `keysIn`, `map`, `mapValues`, `matches`, `matchesProperty`, `memoize`, `merge`,
+ * `mixin`, `negate`, `noop`, `omit`, `once`, `pairs`, `partial`, `partialRight`,
+ * `partition`, `pick`, `plant`, `pluck`, `property`, `propertyOf`, `pull`,
+ * `pullAt`, `push`, `range`, `rearg`, `reject`, `remove`, `rest`, `reverse`,
+ * `shuffle`, `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`,
+ * `spread`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`,
+ * `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `valuesIn`, `where`,
+ * `without`, `wrap`, `xor`, `zip`, and `zipObject`
+ *
+ * The wrapper methods that are **not** chainable by default are:
+ * `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`,
+ * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`,
+ * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `has`,
+ * `identity`, `includes`, `indexOf`, `inRange`, `isArguments`, `isArray`,
+ * `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`,
+ * `isFinite`,`isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`,
+ * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`,
+ * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`,
+ * `noConflict`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`,
+ * `random`, `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`,
+ * `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`,
+ * `startCase`, `startsWith`, `sum`, `template`, `trim`, `trimLeft`,
+ * `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words`
+ *
+ * The wrapper method `sample` will return a wrapped value when `n` is provided,
+ * otherwise an unwrapped value is returned.
+ *
+ * @name _
+ * @constructor
+ * @category Chain
+ * @param {*} value The value to wrap in a `lodash` instance.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @example
+ *
+ * var wrapped = _([1, 2, 3]);
+ *
+ * // returns an unwrapped value
+ * wrapped.reduce(function(sum, n) {
+ * return sum + n;
+ * });
+ * // => 6
+ *
+ * // returns a wrapped value
+ * var squares = wrapped.map(function(n) {
+ * return n * n;
+ * });
+ *
+ * _.isArray(squares);
+ * // => false
+ *
+ * _.isArray(squares.value());
+ * // => true
+ */
+function lodash(value) {
+ if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
+ if (value instanceof LodashWrapper) {
+ return value;
+ }
+ if (hasOwnProperty.call(value, '__chain__') && hasOwnProperty.call(value, '__wrapped__')) {
+ return wrapperClone(value);
+ }
+ }
+ return new LodashWrapper(value);
+}
+
+// Ensure wrappers are instances of `baseLodash`.
+lodash.prototype = baseLodash.prototype;
+
+module.exports = lodash;
+
+},{"../internal/LazyWrapper":27,"../internal/LodashWrapper":28,"../internal/baseLodash":47,"../internal/isObjectLike":82,"../internal/wrapperClone":93,"../lang/isArray":96}],21:[function(require,module,exports){
+var baseEach = require('../internal/baseEach'),
+ createFind = require('../internal/createFind');
+
+/**
+ * Iterates over elements of `collection`, returning the first element
+ * `predicate` returns truthy for. The predicate is bound to `thisArg` and
+ * invoked with three arguments: (value, index|key, collection).
+ *
+ * If a property name is provided for `predicate` the created `_.property`
+ * style callback returns the property value of the given element.
+ *
+ * If a value is also provided for `thisArg` the created `_.matchesProperty`
+ * style callback returns `true` for elements that have a matching property
+ * value, else `false`.
+ *
+ * If an object is provided for `predicate` the created `_.matches` style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @alias detect
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked
+ * per iteration.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false },
+ * { 'user': 'pebbles', 'age': 1, 'active': true }
+ * ];
+ *
+ * _.result(_.find(users, function(chr) {
+ * return chr.age < 40;
+ * }), 'user');
+ * // => 'barney'
+ *
+ * // using the `_.matches` callback shorthand
+ * _.result(_.find(users, { 'age': 1, 'active': true }), 'user');
+ * // => 'pebbles'
+ *
+ * // using the `_.matchesProperty` callback shorthand
+ * _.result(_.find(users, 'active', false), 'user');
+ * // => 'fred'
+ *
+ * // using the `_.property` callback shorthand
+ * _.result(_.find(users, 'active'), 'user');
+ * // => 'barney'
+ */
+var find = createFind(baseEach);
+
+module.exports = find;
+
+},{"../internal/baseEach":36,"../internal/createFind":64}],22:[function(require,module,exports){
+var arrayEach = require('../internal/arrayEach'),
+ baseEach = require('../internal/baseEach'),
+ createForEach = require('../internal/createForEach');
+
+/**
+ * Iterates over elements of `collection` invoking `iteratee` for each element.
+ * The `iteratee` is bound to `thisArg` and invoked with three arguments:
+ * (value, index|key, collection). Iterator functions may exit iteration early
+ * by explicitly returning `false`.
+ *
+ * **Note:** As with other "Collections" methods, objects with a `length` property
+ * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+ * may be used for object iteration.
+ *
+ * @static
+ * @memberOf _
+ * @alias each
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array|Object|string} Returns `collection`.
+ * @example
+ *
+ * _([1, 2]).forEach(function(n) {
+ * console.log(n);
+ * }).value();
+ * // => logs each value from left to right and returns the array
+ *
+ * _.forEach({ 'a': 1, 'b': 2 }, function(n, key) {
+ * console.log(n, key);
+ * });
+ * // => logs each value-key pair and returns the object (iteration order is not guaranteed)
+ */
+var forEach = createForEach(arrayEach, baseEach);
+
+module.exports = forEach;
+
+},{"../internal/arrayEach":30,"../internal/baseEach":36,"../internal/createForEach":65}],23:[function(require,module,exports){
+var arrayMap = require('../internal/arrayMap'),
+ baseCallback = require('../internal/baseCallback'),
+ baseMap = require('../internal/baseMap'),
+ isArray = require('../lang/isArray');
+
+/**
+ * Creates an array of values by running each element in `collection` through
+ * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three
+ * arguments: (value, index|key, collection).
+ *
+ * If a property name is provided for `iteratee` the created `_.property`
+ * style callback returns the property value of the given element.
+ *
+ * If a value is also provided for `thisArg` the created `_.matchesProperty`
+ * style callback returns `true` for elements that have a matching property
+ * value, else `false`.
+ *
+ * If an object is provided for `iteratee` the created `_.matches` style
+ * callback returns `true` for elements that have the properties of the given
+ * object, else `false`.
+ *
+ * Many lodash methods are guarded to work as interatees for methods like
+ * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+ *
+ * The guarded methods are:
+ * `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, `drop`,
+ * `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, `parseInt`,
+ * `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, `trimLeft`,
+ * `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, `uniq`, and `words`
+ *
+ * @static
+ * @memberOf _
+ * @alias collect
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked
+ * per iteration.
+ * create a `_.property` or `_.matches` style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @returns {Array} Returns the new mapped array.
+ * @example
+ *
+ * function timesThree(n) {
+ * return n * 3;
+ * }
+ *
+ * _.map([1, 2], timesThree);
+ * // => [3, 6]
+ *
+ * _.map({ 'a': 1, 'b': 2 }, timesThree);
+ * // => [3, 6] (iteration order is not guaranteed)
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * // using the `_.property` callback shorthand
+ * _.map(users, 'user');
+ * // => ['barney', 'fred']
+ */
+function map(collection, iteratee, thisArg) {
+ var func = isArray(collection) ? arrayMap : baseMap;
+ iteratee = baseCallback(iteratee, thisArg, 3);
+ return func(collection, iteratee);
+}
+
+module.exports = map;
+
+},{"../internal/arrayMap":31,"../internal/baseCallback":32,"../internal/baseMap":48,"../lang/isArray":96}],24:[function(require,module,exports){
+var isNative = require('../lang/isNative');
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeNow = isNative(nativeNow = Date.now) && nativeNow;
+
+/**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch
+ * (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @category Date
+ * @example
+ *
+ * _.defer(function(stamp) {
+ * console.log(_.now() - stamp);
+ * }, _.now());
+ * // => logs the number of milliseconds it took for the deferred function to be invoked
+ */
+var now = nativeNow || function() {
+ return new Date().getTime();
+};
+
+module.exports = now;
+
+},{"../lang/isNative":98}],25:[function(require,module,exports){
+var createWrapper = require('../internal/createWrapper'),
+ replaceHolders = require('../internal/replaceHolders'),
+ restParam = require('./restParam');
+
+/** Used to compose bitmasks for wrapper metadata. */
+var BIND_FLAG = 1,
+ PARTIAL_FLAG = 32;
+
+/**
+ * Creates a function that invokes `func` with the `this` binding of `thisArg`
+ * and prepends any additional `_.bind` arguments to those provided to the
+ * bound function.
+ *
+ * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** Unlike native `Function#bind` this method does not set the `length`
+ * property of bound functions.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
+ * @example
+ *
+ * var greet = function(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * };
+ *
+ * var object = { 'user': 'fred' };
+ *
+ * var bound = _.bind(greet, object, 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * // using placeholders
+ * var bound = _.bind(greet, object, _, '!');
+ * bound('hi');
+ * // => 'hi fred!'
+ */
+var bind = restParam(function(func, thisArg, partials) {
+ var bitmask = BIND_FLAG;
+ if (partials.length) {
+ var holders = replaceHolders(partials, bind.placeholder);
+ bitmask |= PARTIAL_FLAG;
+ }
+ return createWrapper(func, bitmask, thisArg, partials, holders);
+});
+
+// Assign default placeholders.
+bind.placeholder = {};
+
+module.exports = bind;
+
+},{"../internal/createWrapper":68,"../internal/replaceHolders":88,"./restParam":26}],26:[function(require,module,exports){
+/** Used as the `TypeError` message for "Functions" methods. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max;
+
+/**
+ * Creates a function that invokes `func` with the `this` binding of the
+ * created function and arguments from `start` and beyond provided as an array.
+ *
+ * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var say = _.restParam(function(what, names) {
+ * return what + ' ' + _.initial(names).join(', ') +
+ * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
+ * });
+ *
+ * say('hello', 'fred', 'barney', 'pebbles');
+ * // => 'hello fred, barney, & pebbles'
+ */
+function restParam(func, start) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ start = nativeMax(typeof start == 'undefined' ? (func.length - 1) : (+start || 0), 0);
+ return function() {
+ var args = arguments,
+ index = -1,
+ length = nativeMax(args.length - start, 0),
+ rest = Array(length);
+
+ while (++index < length) {
+ rest[index] = args[start + index];
+ }
+ switch (start) {
+ case 0: return func.call(this, rest);
+ case 1: return func.call(this, args[0], rest);
+ case 2: return func.call(this, args[0], args[1], rest);
+ }
+ var otherArgs = Array(start + 1);
+ index = -1;
+ while (++index < start) {
+ otherArgs[index] = args[index];
+ }
+ otherArgs[start] = rest;
+ return func.apply(this, otherArgs);
+ };
+}
+
+module.exports = restParam;
+
+},{}],27:[function(require,module,exports){
+var baseCreate = require('./baseCreate'),
+ baseLodash = require('./baseLodash');
+
+/** Used as references for `-Infinity` and `Infinity`. */
+var POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
+
+/**
+ * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ */
+function LazyWrapper(value) {
+ this.__wrapped__ = value;
+ this.__actions__ = null;
+ this.__dir__ = 1;
+ this.__dropCount__ = 0;
+ this.__filtered__ = false;
+ this.__iteratees__ = null;
+ this.__takeCount__ = POSITIVE_INFINITY;
+ this.__views__ = null;
+}
+
+LazyWrapper.prototype = baseCreate(baseLodash.prototype);
+LazyWrapper.prototype.constructor = LazyWrapper;
+
+module.exports = LazyWrapper;
+
+},{"./baseCreate":35,"./baseLodash":47}],28:[function(require,module,exports){
+var baseCreate = require('./baseCreate'),
+ baseLodash = require('./baseLodash');
+
+/**
+ * The base constructor for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ * @param {boolean} [chainAll] Enable chaining for all wrapper methods.
+ * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value.
+ */
+function LodashWrapper(value, chainAll, actions) {
+ this.__wrapped__ = value;
+ this.__actions__ = actions || [];
+ this.__chain__ = !!chainAll;
+}
+
+LodashWrapper.prototype = baseCreate(baseLodash.prototype);
+LodashWrapper.prototype.constructor = LodashWrapper;
+
+module.exports = LodashWrapper;
+
+},{"./baseCreate":35,"./baseLodash":47}],29:[function(require,module,exports){
+/**
+ * Copies the values of `source` to `array`.
+ *
+ * @private
+ * @param {Array} source The array to copy values from.
+ * @param {Array} [array=[]] The array to copy values to.
+ * @returns {Array} Returns `array`.
+ */
+function arrayCopy(source, array) {
+ var index = -1,
+ length = source.length;
+
+ array || (array = Array(length));
+ while (++index < length) {
+ array[index] = source[index];
+ }
+ return array;
+}
+
+module.exports = arrayCopy;
+
+},{}],30:[function(require,module,exports){
+/**
+ * A specialized version of `_.forEach` for arrays without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+function arrayEach(array, iteratee) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ if (iteratee(array[index], index, array) === false) {
+ break;
+ }
+ }
+ return array;
+}
+
+module.exports = arrayEach;
+
+},{}],31:[function(require,module,exports){
+/**
+ * A specialized version of `_.map` for arrays without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+function arrayMap(array, iteratee) {
+ var index = -1,
+ length = array.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iteratee(array[index], index, array);
+ }
+ return result;
+}
+
+module.exports = arrayMap;
+
+},{}],32:[function(require,module,exports){
+var baseMatches = require('./baseMatches'),
+ baseMatchesProperty = require('./baseMatchesProperty'),
+ baseProperty = require('./baseProperty'),
+ bindCallback = require('./bindCallback'),
+ identity = require('../utility/identity');
+
+/**
+ * The base implementation of `_.callback` which supports specifying the
+ * number of arguments to provide to `func`.
+ *
+ * @private
+ * @param {*} [func=_.identity] The value to convert to a callback.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {number} [argCount] The number of arguments to provide to `func`.
+ * @returns {Function} Returns the callback.
+ */
+function baseCallback(func, thisArg, argCount) {
+ var type = typeof func;
+ if (type == 'function') {
+ return typeof thisArg == 'undefined'
+ ? func
+ : bindCallback(func, thisArg, argCount);
+ }
+ if (func == null) {
+ return identity;
+ }
+ if (type == 'object') {
+ return baseMatches(func);
+ }
+ return typeof thisArg == 'undefined'
+ ? baseProperty(func + '')
+ : baseMatchesProperty(func + '', thisArg);
+}
+
+module.exports = baseCallback;
+
+},{"../utility/identity":109,"./baseMatches":49,"./baseMatchesProperty":50,"./baseProperty":51,"./bindCallback":56}],33:[function(require,module,exports){
+var arrayCopy = require('./arrayCopy'),
+ arrayEach = require('./arrayEach'),
+ baseCopy = require('./baseCopy'),
+ baseForOwn = require('./baseForOwn'),
+ initCloneArray = require('./initCloneArray'),
+ initCloneByTag = require('./initCloneByTag'),
+ initCloneObject = require('./initCloneObject'),
+ isArray = require('../lang/isArray'),
+ isHostObject = require('./isHostObject'),
+ isObject = require('../lang/isObject'),
+ keys = require('../object/keys');
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ mapTag = '[object Map]',
+ numberTag = '[object Number]',
+ objectTag = '[object Object]',
+ regexpTag = '[object RegExp]',
+ setTag = '[object Set]',
+ stringTag = '[object String]',
+ weakMapTag = '[object WeakMap]';
+
+var arrayBufferTag = '[object ArrayBuffer]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
+
+/** Used to identify `toStringTag` values supported by `_.clone`. */
+var cloneableTags = {};
+cloneableTags[argsTag] = cloneableTags[arrayTag] =
+cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
+cloneableTags[dateTag] = cloneableTags[float32Tag] =
+cloneableTags[float64Tag] = cloneableTags[int8Tag] =
+cloneableTags[int16Tag] = cloneableTags[int32Tag] =
+cloneableTags[numberTag] = cloneableTags[objectTag] =
+cloneableTags[regexpTag] = cloneableTags[stringTag] =
+cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+cloneableTags[errorTag] = cloneableTags[funcTag] =
+cloneableTags[mapTag] = cloneableTags[setTag] =
+cloneableTags[weakMapTag] = false;
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/**
+ * The base implementation of `_.clone` without support for argument juggling
+ * and `this` binding `customizer` functions.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @param {Function} [customizer] The function to customize cloning values.
+ * @param {string} [key] The key of `value`.
+ * @param {Object} [object] The object `value` belongs to.
+ * @param {Array} [stackA=[]] Tracks traversed source objects.
+ * @param {Array} [stackB=[]] Associates clones with source counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
+ var result;
+ if (customizer) {
+ result = object ? customizer(value, key, object) : customizer(value);
+ }
+ if (typeof result != 'undefined') {
+ return result;
+ }
+ if (!isObject(value)) {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isArr) {
+ result = initCloneArray(value);
+ if (!isDeep) {
+ return arrayCopy(value, result);
+ }
+ } else {
+ var tag = objToString.call(value),
+ isFunc = tag == funcTag;
+
+ if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+ if (isHostObject(value)) {
+ return object ? value : {};
+ }
+ result = initCloneObject(isFunc ? {} : value);
+ if (!isDeep) {
+ return baseCopy(value, result, keys(value));
+ }
+ } else {
+ return cloneableTags[tag]
+ ? initCloneByTag(value, tag, isDeep)
+ : (object ? value : {});
+ }
+ }
+ // Check for circular references and return corresponding clone.
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == value) {
+ return stackB[length];
+ }
+ }
+ // Add the source value to the stack of traversed objects and associate it with its clone.
+ stackA.push(value);
+ stackB.push(result);
+
+ // Recursively populate clone (susceptible to call stack limits).
+ (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
+ result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
+ });
+ return result;
+}
+
+module.exports = baseClone;
+
+},{"../lang/isArray":96,"../lang/isObject":99,"../object/keys":104,"./arrayCopy":29,"./arrayEach":30,"./baseCopy":34,"./baseForOwn":41,"./initCloneArray":75,"./initCloneByTag":76,"./initCloneObject":77,"./isHostObject":78}],34:[function(require,module,exports){
+/**
+ * Copies the properties of `source` to `object`.
+ *
+ * @private
+ * @param {Object} source The object to copy properties from.
+ * @param {Object} [object={}] The object to copy properties to.
+ * @param {Array} props The property names to copy.
+ * @returns {Object} Returns `object`.
+ */
+function baseCopy(source, object, props) {
+ if (!props) {
+ props = object;
+ object = {};
+ }
+ var index = -1,
+ length = props.length;
+
+ while (++index < length) {
+ var key = props[index];
+ object[key] = source[key];
+ }
+ return object;
+}
+
+module.exports = baseCopy;
+
+},{}],35:[function(require,module,exports){
+(function (global){
+var isObject = require('../lang/isObject');
+
+/**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} prototype The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+var baseCreate = (function() {
+ function Object() {}
+ return function(prototype) {
+ if (isObject(prototype)) {
+ Object.prototype = prototype;
+ var result = new Object;
+ Object.prototype = null;
+ }
+ return result || global.Object();
+ };
+}());
+
+module.exports = baseCreate;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"../lang/isObject":99}],36:[function(require,module,exports){
+var baseForOwn = require('./baseForOwn'),
+ createBaseEach = require('./createBaseEach');
+
+/**
+ * The base implementation of `_.forEach` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object|string} Returns `collection`.
+ */
+var baseEach = createBaseEach(baseForOwn);
+
+module.exports = baseEach;
+
+},{"./baseForOwn":41,"./createBaseEach":60}],37:[function(require,module,exports){
+/**
+ * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`,
+ * without support for callback shorthands and `this` binding, which iterates
+ * over `collection` using the provided `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @param {boolean} [retKey] Specify returning the key of the found element
+ * instead of the element itself.
+ * @returns {*} Returns the found element or its key, else `undefined`.
+ */
+function baseFind(collection, predicate, eachFunc, retKey) {
+ var result;
+ eachFunc(collection, function(value, key, collection) {
+ if (predicate(value, key, collection)) {
+ result = retKey ? key : value;
+ return false;
+ }
+ });
+ return result;
+}
+
+module.exports = baseFind;
+
+},{}],38:[function(require,module,exports){
+/**
+ * The base implementation of `_.findIndex` and `_.findLastIndex` without
+ * support for callback shorthands and `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function baseFindIndex(array, predicate, fromRight) {
+ var length = array.length,
+ index = fromRight ? length : -1;
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (predicate(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+module.exports = baseFindIndex;
+
+},{}],39:[function(require,module,exports){
+var createBaseFor = require('./createBaseFor');
+
+/**
+ * The base implementation of `baseForIn` and `baseForOwn` which iterates
+ * over `object` properties returned by `keysFunc` invoking `iteratee` for
+ * each property. Iterator functions may exit iteration early by explicitly
+ * returning `false`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */
+var baseFor = createBaseFor();
+
+module.exports = baseFor;
+
+},{"./createBaseFor":61}],40:[function(require,module,exports){
+var baseFor = require('./baseFor'),
+ keysIn = require('../object/keysIn');
+
+/**
+ * The base implementation of `_.forIn` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+function baseForIn(object, iteratee) {
+ return baseFor(object, iteratee, keysIn);
+}
+
+module.exports = baseForIn;
+
+},{"../object/keysIn":105,"./baseFor":39}],41:[function(require,module,exports){
+var baseFor = require('./baseFor'),
+ keys = require('../object/keys');
+
+/**
+ * The base implementation of `_.forOwn` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+function baseForOwn(object, iteratee) {
+ return baseFor(object, iteratee, keys);
+}
+
+module.exports = baseForOwn;
+
+},{"../object/keys":104,"./baseFor":39}],42:[function(require,module,exports){
+var indexOfNaN = require('./indexOfNaN');
+
+/**
+ * The base implementation of `_.indexOf` without support for binary searches.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function baseIndexOf(array, value, fromIndex) {
+ if (value !== value) {
+ return indexOfNaN(array, fromIndex);
+ }
+ var index = fromIndex - 1,
+ length = array.length;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+module.exports = baseIndexOf;
+
+},{"./indexOfNaN":74}],43:[function(require,module,exports){
+var baseIsEqualDeep = require('./baseIsEqualDeep');
+
+/**
+ * The base implementation of `_.isEqual` without support for `this` binding
+ * `customizer` functions.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {Function} [customizer] The function to customize comparing values.
+ * @param {boolean} [isLoose] Specify performing partial comparisons.
+ * @param {Array} [stackA] Tracks traversed `value` objects.
+ * @param {Array} [stackB] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
+ // Exit early for identical values.
+ if (value === other) {
+ // Treat `+0` vs. `-0` as not equal.
+ return value !== 0 || (1 / value == 1 / other);
+ }
+ var valType = typeof value,
+ othType = typeof other;
+
+ // Exit early for unlike primitive values.
+ if ((valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object') ||
+ value == null || other == null) {
+ // Return `false` unless both values are `NaN`.
+ return value !== value && other !== other;
+ }
+ return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
+}
+
+module.exports = baseIsEqual;
+
+},{"./baseIsEqualDeep":44}],44:[function(require,module,exports){
+var equalArrays = require('./equalArrays'),
+ equalByTag = require('./equalByTag'),
+ equalObjects = require('./equalObjects'),
+ isArray = require('../lang/isArray'),
+ isHostObject = require('./isHostObject'),
+ isTypedArray = require('../lang/isTypedArray');
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ funcTag = '[object Function]',
+ objectTag = '[object Object]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/**
+ * A specialized version of `baseIsEqual` for arrays and objects which performs
+ * deep comparisons and tracks traversed objects enabling objects with circular
+ * references to be compared.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparing objects.
+ * @param {boolean} [isLoose] Specify performing partial comparisons.
+ * @param {Array} [stackA=[]] Tracks traversed `value` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
+ var objIsArr = isArray(object),
+ othIsArr = isArray(other),
+ objTag = arrayTag,
+ othTag = arrayTag;
+
+ if (!objIsArr) {
+ objTag = objToString.call(object);
+ if (objTag == argsTag) {
+ objTag = objectTag;
+ } else if (objTag != objectTag) {
+ objIsArr = isTypedArray(object);
+ }
+ }
+ if (!othIsArr) {
+ othTag = objToString.call(other);
+ if (othTag == argsTag) {
+ othTag = objectTag;
+ } else if (othTag != objectTag) {
+ othIsArr = isTypedArray(other);
+ }
+ }
+ var objIsObj = (objTag == objectTag || (isLoose && objTag == funcTag)) && !isHostObject(object),
+ othIsObj = (othTag == objectTag || (isLoose && othTag == funcTag)) && !isHostObject(other),
+ isSameTag = objTag == othTag;
+
+ if (isSameTag && !(objIsArr || objIsObj)) {
+ return equalByTag(object, other, objTag);
+ }
+ if (isLoose) {
+ if (!isSameTag && !(objIsObj && othIsObj)) {
+ return false;
+ }
+ } else {
+ var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+ othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+ if (valWrapped || othWrapped) {
+ return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
+ }
+ if (!isSameTag) {
+ return false;
+ }
+ }
+ // Assume cyclic values are equal.
+ // For more information on detecting circular references see https://es5.github.io/#JO.
+ stackA || (stackA = []);
+ stackB || (stackB = []);
+
+ var length = stackA.length;
+ while (length--) {
+ if (stackA[length] == object) {
+ return stackB[length] == other;
+ }
+ }
+ // Add `object` and `other` to the stack of traversed objects.
+ stackA.push(object);
+ stackB.push(other);
+
+ var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
+
+ stackA.pop();
+ stackB.pop();
+
+ return result;
+}
+
+module.exports = baseIsEqualDeep;
+
+},{"../lang/isArray":96,"../lang/isTypedArray":102,"./equalArrays":69,"./equalByTag":70,"./equalObjects":71,"./isHostObject":78}],45:[function(require,module,exports){
+/**
+ * The base implementation of `_.isFunction` without support for environments
+ * with incorrect `typeof` results.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ */
+function baseIsFunction(value) {
+ // Avoid a Chakra JIT bug in compatibility modes of IE 11.
+ // See https://github.com/jashkenas/underscore/issues/1621 for more details.
+ return typeof value == 'function' || false;
+}
+
+module.exports = baseIsFunction;
+
+},{}],46:[function(require,module,exports){
+var baseIsEqual = require('./baseIsEqual');
+
+/**
+ * The base implementation of `_.isMatch` without support for callback
+ * shorthands and `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Array} props The source property names to match.
+ * @param {Array} values The source values to match.
+ * @param {Array} strictCompareFlags Strict comparison flags for source values.
+ * @param {Function} [customizer] The function to customize comparing objects.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ */
+function baseIsMatch(object, props, values, strictCompareFlags, customizer) {
+ var index = -1,
+ length = props.length,
+ noCustomizer = !customizer;
+
+ while (++index < length) {
+ if ((noCustomizer && strictCompareFlags[index])
+ ? values[index] !== object[props[index]]
+ : !(props[index] in object)
+ ) {
+ return false;
+ }
+ }
+ index = -1;
+ while (++index < length) {
+ var key = props[index],
+ objValue = object[key],
+ srcValue = values[index];
+
+ if (noCustomizer && strictCompareFlags[index]) {
+ var result = typeof objValue != 'undefined' || (key in object);
+ } else {
+ result = customizer ? customizer(objValue, srcValue, key) : undefined;
+ if (typeof result == 'undefined') {
+ result = baseIsEqual(srcValue, objValue, customizer, true);
+ }
+ }
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+}
+
+module.exports = baseIsMatch;
+
+},{"./baseIsEqual":43}],47:[function(require,module,exports){
+/**
+ * The function whose prototype all chaining wrappers inherit from.
+ *
+ * @private
+ */
+function baseLodash() {
+ // No operation performed.
+}
+
+module.exports = baseLodash;
+
+},{}],48:[function(require,module,exports){
+var baseEach = require('./baseEach');
+
+/**
+ * The base implementation of `_.map` without support for callback shorthands
+ * and `this` binding.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+function baseMap(collection, iteratee) {
+ var result = [];
+ baseEach(collection, function(value, key, collection) {
+ result.push(iteratee(value, key, collection));
+ });
+ return result;
+}
+
+module.exports = baseMap;
+
+},{"./baseEach":36}],49:[function(require,module,exports){
+var baseIsMatch = require('./baseIsMatch'),
+ constant = require('../utility/constant'),
+ isStrictComparable = require('./isStrictComparable'),
+ keys = require('../object/keys'),
+ toObject = require('./toObject');
+
+/**
+ * The base implementation of `_.matches` which does not clone `source`.
+ *
+ * @private
+ * @param {Object} source The object of property values to match.
+ * @returns {Function} Returns the new function.
+ */
+function baseMatches(source) {
+ var props = keys(source),
+ length = props.length;
+
+ if (!length) {
+ return constant(true);
+ }
+ if (length == 1) {
+ var key = props[0],
+ value = source[key];
+
+ if (isStrictComparable(value)) {
+ return function(object) {
+ return object != null && object[key] === value &&
+ (typeof value != 'undefined' || (key in toObject(object)));
+ };
+ }
+ }
+ var values = Array(length),
+ strictCompareFlags = Array(length);
+
+ while (length--) {
+ value = source[props[length]];
+ values[length] = value;
+ strictCompareFlags[length] = isStrictComparable(value);
+ }
+ return function(object) {
+ return object != null && baseIsMatch(toObject(object), props, values, strictCompareFlags);
+ };
+}
+
+module.exports = baseMatches;
+
+},{"../object/keys":104,"../utility/constant":108,"./baseIsMatch":46,"./isStrictComparable":83,"./toObject":92}],50:[function(require,module,exports){
+var baseIsEqual = require('./baseIsEqual'),
+ isStrictComparable = require('./isStrictComparable'),
+ toObject = require('./toObject');
+
+/**
+ * The base implementation of `_.matchesProperty` which does not coerce `key`
+ * to a string.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @param {*} value The value to compare.
+ * @returns {Function} Returns the new function.
+ */
+function baseMatchesProperty(key, value) {
+ if (isStrictComparable(value)) {
+ return function(object) {
+ return object != null && object[key] === value &&
+ (typeof value != 'undefined' || (key in toObject(object)));
+ };
+ }
+ return function(object) {
+ return object != null && baseIsEqual(value, object[key], null, true);
+ };
+}
+
+module.exports = baseMatchesProperty;
+
+},{"./baseIsEqual":43,"./isStrictComparable":83,"./toObject":92}],51:[function(require,module,exports){
+/**
+ * The base implementation of `_.property` which does not coerce `key` to a string.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @returns {Function} Returns the new function.
+ */
+function baseProperty(key) {
+ return function(object) {
+ return object == null ? undefined : object[key];
+ };
+}
+
+module.exports = baseProperty;
+
+},{}],52:[function(require,module,exports){
+var identity = require('../utility/identity'),
+ metaMap = require('./metaMap');
+
+/**
+ * The base implementation of `setData` without support for hot loop detection.
+ *
+ * @private
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
+ */
+var baseSetData = !metaMap ? identity : function(func, data) {
+ metaMap.set(func, data);
+ return func;
+};
+
+module.exports = baseSetData;
+
+},{"../utility/identity":109,"./metaMap":85}],53:[function(require,module,exports){
+/**
+ * Converts `value` to a string if it is not one. An empty string is returned
+ * for `null` or `undefined` values.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ */
+function baseToString(value) {
+ if (typeof value == 'string') {
+ return value;
+ }
+ return value == null ? '' : (value + '');
+}
+
+module.exports = baseToString;
+
+},{}],54:[function(require,module,exports){
+var binaryIndexBy = require('./binaryIndexBy'),
+ identity = require('../utility/identity');
+
+/** Used as references for the maximum length and index of an array. */
+var MAX_ARRAY_LENGTH = Math.pow(2, 32) - 1,
+ HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
+
+/**
+ * Performs a binary search of `array` to determine the index at which `value`
+ * should be inserted into `array` in order to maintain its sort order.
+ *
+ * @private
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {boolean} [retHighest] Specify returning the highest qualified index.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ */
+function binaryIndex(array, value, retHighest) {
+ var low = 0,
+ high = array ? array.length : low;
+
+ if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
+ while (low < high) {
+ var mid = (low + high) >>> 1,
+ computed = array[mid];
+
+ if (retHighest ? (computed <= value) : (computed < value)) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return high;
+ }
+ return binaryIndexBy(array, value, identity, retHighest);
+}
+
+module.exports = binaryIndex;
+
+},{"../utility/identity":109,"./binaryIndexBy":55}],55:[function(require,module,exports){
+/** Native method references. */
+var floor = Math.floor;
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMin = Math.min;
+
+/** Used as references for the maximum length and index of an array. */
+var MAX_ARRAY_LENGTH = Math.pow(2, 32) - 1,
+ MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1;
+
+/**
+ * This function is like `binaryIndex` except that it invokes `iteratee` for
+ * `value` and each element of `array` to compute their sort ranking. The
+ * iteratee is invoked with one argument; (value).
+ *
+ * @private
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {boolean} [retHighest] Specify returning the highest qualified index.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ */
+function binaryIndexBy(array, value, iteratee, retHighest) {
+ value = iteratee(value);
+
+ var low = 0,
+ high = array ? array.length : 0,
+ valIsNaN = value !== value,
+ valIsUndef = typeof value == 'undefined';
+
+ while (low < high) {
+ var mid = floor((low + high) / 2),
+ computed = iteratee(array[mid]),
+ isReflexive = computed === computed;
+
+ if (valIsNaN) {
+ var setLow = isReflexive || retHighest;
+ } else if (valIsUndef) {
+ setLow = isReflexive && (retHighest || typeof computed != 'undefined');
+ } else {
+ setLow = retHighest ? (computed <= value) : (computed < value);
+ }
+ if (setLow) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return nativeMin(high, MAX_ARRAY_INDEX);
+}
+
+module.exports = binaryIndexBy;
+
+},{}],56:[function(require,module,exports){
+var identity = require('../utility/identity');
+
+/**
+ * A specialized version of `baseCallback` which only supports `this` binding
+ * and specifying the number of arguments to provide to `func`.
+ *
+ * @private
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {number} [argCount] The number of arguments to provide to `func`.
+ * @returns {Function} Returns the callback.
+ */
+function bindCallback(func, thisArg, argCount) {
+ if (typeof func != 'function') {
+ return identity;
+ }
+ if (typeof thisArg == 'undefined') {
+ return func;
+ }
+ switch (argCount) {
+ case 1: return function(value) {
+ return func.call(thisArg, value);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(thisArg, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(thisArg, accumulator, value, index, collection);
+ };
+ case 5: return function(value, other, key, object, source) {
+ return func.call(thisArg, value, other, key, object, source);
+ };
+ }
+ return function() {
+ return func.apply(thisArg, arguments);
+ };
+}
+
+module.exports = bindCallback;
+
+},{"../utility/identity":109}],57:[function(require,module,exports){
+(function (global){
+var constant = require('../utility/constant'),
+ isNative = require('../lang/isNative');
+
+/** Native method references. */
+var ArrayBuffer = isNative(ArrayBuffer = global.ArrayBuffer) && ArrayBuffer,
+ bufferSlice = isNative(bufferSlice = ArrayBuffer && new ArrayBuffer(0).slice) && bufferSlice,
+ floor = Math.floor,
+ Uint8Array = isNative(Uint8Array = global.Uint8Array) && Uint8Array;
+
+/** Used to clone array buffers. */
+var Float64Array = (function() {
+ // Safari 5 errors when using an array buffer to initialize a typed array
+ // where the array buffer's `byteLength` is not a multiple of the typed
+ // array's `BYTES_PER_ELEMENT`.
+ try {
+ var func = isNative(func = global.Float64Array) && func,
+ result = new func(new ArrayBuffer(10), 0, 1) && func;
+ } catch(e) {}
+ return result;
+}());
+
+/** Used as the size, in bytes, of each `Float64Array` element. */
+var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0;
+
+/**
+ * Creates a clone of the given array buffer.
+ *
+ * @private
+ * @param {ArrayBuffer} buffer The array buffer to clone.
+ * @returns {ArrayBuffer} Returns the cloned array buffer.
+ */
+function bufferClone(buffer) {
+ return bufferSlice.call(buffer, 0);
+}
+if (!bufferSlice) {
+ // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`.
+ bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) {
+ var byteLength = buffer.byteLength,
+ floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0,
+ offset = floatLength * FLOAT64_BYTES_PER_ELEMENT,
+ result = new ArrayBuffer(byteLength);
+
+ if (floatLength) {
+ var view = new Float64Array(result, 0, floatLength);
+ view.set(new Float64Array(buffer, 0, floatLength));
+ }
+ if (byteLength != offset) {
+ view = new Uint8Array(result, offset);
+ view.set(new Uint8Array(buffer, offset));
+ }
+ return result;
+ };
+}
+
+module.exports = bufferClone;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"../lang/isNative":98,"../utility/constant":108}],58:[function(require,module,exports){
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max;
+
+/**
+ * Creates an array that is the composition of partially applied arguments,
+ * placeholders, and provided arguments into a single array of arguments.
+ *
+ * @private
+ * @param {Array|Object} args The provided arguments.
+ * @param {Array} partials The arguments to prepend to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @returns {Array} Returns the new array of composed arguments.
+ */
+function composeArgs(args, partials, holders) {
+ var holdersLength = holders.length,
+ argsIndex = -1,
+ argsLength = nativeMax(args.length - holdersLength, 0),
+ leftIndex = -1,
+ leftLength = partials.length,
+ result = Array(argsLength + leftLength);
+
+ while (++leftIndex < leftLength) {
+ result[leftIndex] = partials[leftIndex];
+ }
+ while (++argsIndex < holdersLength) {
+ result[holders[argsIndex]] = args[argsIndex];
+ }
+ while (argsLength--) {
+ result[leftIndex++] = args[argsIndex++];
+ }
+ return result;
+}
+
+module.exports = composeArgs;
+
+},{}],59:[function(require,module,exports){
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max;
+
+/**
+ * This function is like `composeArgs` except that the arguments composition
+ * is tailored for `_.partialRight`.
+ *
+ * @private
+ * @param {Array|Object} args The provided arguments.
+ * @param {Array} partials The arguments to append to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @returns {Array} Returns the new array of composed arguments.
+ */
+function composeArgsRight(args, partials, holders) {
+ var holdersIndex = -1,
+ holdersLength = holders.length,
+ argsIndex = -1,
+ argsLength = nativeMax(args.length - holdersLength, 0),
+ rightIndex = -1,
+ rightLength = partials.length,
+ result = Array(argsLength + rightLength);
+
+ while (++argsIndex < argsLength) {
+ result[argsIndex] = args[argsIndex];
+ }
+ var pad = argsIndex;
+ while (++rightIndex < rightLength) {
+ result[pad + rightIndex] = partials[rightIndex];
+ }
+ while (++holdersIndex < holdersLength) {
+ result[pad + holders[holdersIndex]] = args[argsIndex++];
+ }
+ return result;
+}
+
+module.exports = composeArgsRight;
+
+},{}],60:[function(require,module,exports){
+var isLength = require('./isLength'),
+ toObject = require('./toObject');
+
+/**
+ * Creates a `baseEach` or `baseEachRight` function.
+ *
+ * @private
+ * @param {Function} eachFunc The function to iterate over a collection.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
+ */
+function createBaseEach(eachFunc, fromRight) {
+ return function(collection, iteratee) {
+ var length = collection ? collection.length : 0;
+ if (!isLength(length)) {
+ return eachFunc(collection, iteratee);
+ }
+ var index = fromRight ? length : -1,
+ iterable = toObject(collection);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (iteratee(iterable[index], index, iterable) === false) {
+ break;
+ }
+ }
+ return collection;
+ };
+}
+
+module.exports = createBaseEach;
+
+},{"./isLength":81,"./toObject":92}],61:[function(require,module,exports){
+var toObject = require('./toObject');
+
+/**
+ * Creates a base function for `_.forIn` or `_.forInRight`.
+ *
+ * @private
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
+ */
+function createBaseFor(fromRight) {
+ return function(object, iteratee, keysFunc) {
+ var iterable = toObject(object),
+ props = keysFunc(object),
+ length = props.length,
+ index = fromRight ? length : -1;
+
+ while ((fromRight ? index-- : ++index < length)) {
+ var key = props[index];
+ if (iteratee(iterable[key], key, iterable) === false) {
+ break;
+ }
+ }
+ return object;
+ };
+}
+
+module.exports = createBaseFor;
+
+},{"./toObject":92}],62:[function(require,module,exports){
+(function (global){
+var createCtorWrapper = require('./createCtorWrapper');
+
+/**
+ * Creates a function that wraps `func` and invokes it with the `this`
+ * binding of `thisArg`.
+ *
+ * @private
+ * @param {Function} func The function to bind.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @returns {Function} Returns the new bound function.
+ */
+function createBindWrapper(func, thisArg) {
+ var Ctor = createCtorWrapper(func);
+
+ function wrapper() {
+ var fn = (this && this !== global && this instanceof wrapper) ? Ctor : func;
+ return fn.apply(thisArg, arguments);
+ }
+ return wrapper;
+}
+
+module.exports = createBindWrapper;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./createCtorWrapper":63}],63:[function(require,module,exports){
+var baseCreate = require('./baseCreate'),
+ isObject = require('../lang/isObject');
+
+/**
+ * Creates a function that produces an instance of `Ctor` regardless of
+ * whether it was invoked as part of a `new` expression or by `call` or `apply`.
+ *
+ * @private
+ * @param {Function} Ctor The constructor to wrap.
+ * @returns {Function} Returns the new wrapped function.
+ */
+function createCtorWrapper(Ctor) {
+ return function() {
+ var thisBinding = baseCreate(Ctor.prototype),
+ result = Ctor.apply(thisBinding, arguments);
+
+ // Mimic the constructor's `return` behavior.
+ // See https://es5.github.io/#x13.2.2 for more details.
+ return isObject(result) ? result : thisBinding;
+ };
+}
+
+module.exports = createCtorWrapper;
+
+},{"../lang/isObject":99,"./baseCreate":35}],64:[function(require,module,exports){
+var baseCallback = require('./baseCallback'),
+ baseFind = require('./baseFind'),
+ baseFindIndex = require('./baseFindIndex'),
+ isArray = require('../lang/isArray');
+
+/**
+ * Creates a `_.find` or `_.findLast` function.
+ *
+ * @private
+ * @param {Function} eachFunc The function to iterate over a collection.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new find function.
+ */
+function createFind(eachFunc, fromRight) {
+ return function(collection, predicate, thisArg) {
+ predicate = baseCallback(predicate, thisArg, 3);
+ if (isArray(collection)) {
+ var index = baseFindIndex(collection, predicate, fromRight);
+ return index > -1 ? collection[index] : undefined;
+ }
+ return baseFind(collection, predicate, eachFunc);
+ }
+}
+
+module.exports = createFind;
+
+},{"../lang/isArray":96,"./baseCallback":32,"./baseFind":37,"./baseFindIndex":38}],65:[function(require,module,exports){
+var bindCallback = require('./bindCallback'),
+ isArray = require('../lang/isArray');
+
+/**
+ * Creates a function for `_.forEach` or `_.forEachRight`.
+ *
+ * @private
+ * @param {Function} arrayFunc The function to iterate over an array.
+ * @param {Function} eachFunc The function to iterate over a collection.
+ * @returns {Function} Returns the new each function.
+ */
+function createForEach(arrayFunc, eachFunc) {
+ return function(collection, iteratee, thisArg) {
+ return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection))
+ ? arrayFunc(collection, iteratee)
+ : eachFunc(collection, bindCallback(iteratee, thisArg, 3));
+ };
+}
+
+module.exports = createForEach;
+
+},{"../lang/isArray":96,"./bindCallback":56}],66:[function(require,module,exports){
+(function (global){
+var arrayCopy = require('./arrayCopy'),
+ composeArgs = require('./composeArgs'),
+ composeArgsRight = require('./composeArgsRight'),
+ createCtorWrapper = require('./createCtorWrapper'),
+ isLaziable = require('./isLaziable'),
+ reorder = require('./reorder'),
+ replaceHolders = require('./replaceHolders'),
+ setData = require('./setData');
+
+/** Used to compose bitmasks for wrapper metadata. */
+var BIND_FLAG = 1,
+ BIND_KEY_FLAG = 2,
+ CURRY_BOUND_FLAG = 4,
+ CURRY_FLAG = 8,
+ CURRY_RIGHT_FLAG = 16,
+ PARTIAL_FLAG = 32,
+ PARTIAL_RIGHT_FLAG = 64,
+ ARY_FLAG = 128;
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max;
+
+/**
+ * Creates a function that wraps `func` and invokes it with optional `this`
+ * binding of, partial application, and currying.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to prepend to those provided to the new function.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [partialsRight] The arguments to append to those provided to the new function.
+ * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
+ var isAry = bitmask & ARY_FLAG,
+ isBind = bitmask & BIND_FLAG,
+ isBindKey = bitmask & BIND_KEY_FLAG,
+ isCurry = bitmask & CURRY_FLAG,
+ isCurryBound = bitmask & CURRY_BOUND_FLAG,
+ isCurryRight = bitmask & CURRY_RIGHT_FLAG;
+
+ var Ctor = !isBindKey && createCtorWrapper(func),
+ key = func;
+
+ function wrapper() {
+ // Avoid `arguments` object use disqualifying optimizations by
+ // converting it to an array before providing it to other functions.
+ var length = arguments.length,
+ index = length,
+ args = Array(length);
+
+ while (index--) {
+ args[index] = arguments[index];
+ }
+ if (partials) {
+ args = composeArgs(args, partials, holders);
+ }
+ if (partialsRight) {
+ args = composeArgsRight(args, partialsRight, holdersRight);
+ }
+ if (isCurry || isCurryRight) {
+ var placeholder = wrapper.placeholder,
+ argsHolders = replaceHolders(args, placeholder);
+
+ length -= argsHolders.length;
+ if (length < arity) {
+ var newArgPos = argPos ? arrayCopy(argPos) : null,
+ newArity = nativeMax(arity - length, 0),
+ newsHolders = isCurry ? argsHolders : null,
+ newHoldersRight = isCurry ? null : argsHolders,
+ newPartials = isCurry ? args : null,
+ newPartialsRight = isCurry ? null : args;
+
+ bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);
+ bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);
+
+ if (!isCurryBound) {
+ bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
+ }
+ var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity],
+ result = createHybridWrapper.apply(undefined, newData);
+
+ if (isLaziable(func)) {
+ setData(result, newData);
+ }
+ result.placeholder = placeholder;
+ return result;
+ }
+ }
+ var thisBinding = isBind ? thisArg : this;
+ if (isBindKey) {
+ func = thisBinding[key];
+ }
+ if (argPos) {
+ args = reorder(args, argPos);
+ }
+ if (isAry && ary < args.length) {
+ args.length = ary;
+ }
+ var fn = (this && this !== global && this instanceof wrapper) ? (Ctor || createCtorWrapper(func)) : func;
+ return fn.apply(thisBinding, args);
+ }
+ return wrapper;
+}
+
+module.exports = createHybridWrapper;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./arrayCopy":29,"./composeArgs":58,"./composeArgsRight":59,"./createCtorWrapper":63,"./isLaziable":80,"./reorder":87,"./replaceHolders":88,"./setData":89}],67:[function(require,module,exports){
+(function (global){
+var createCtorWrapper = require('./createCtorWrapper');
+
+/** Used to compose bitmasks for wrapper metadata. */
+var BIND_FLAG = 1;
+
+/**
+ * Creates a function that wraps `func` and invokes it with the optional `this`
+ * binding of `thisArg` and the `partials` prepended to those provided to
+ * the wrapper.
+ *
+ * @private
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} partials The arguments to prepend to those provided to the new function.
+ * @returns {Function} Returns the new bound function.
+ */
+function createPartialWrapper(func, bitmask, thisArg, partials) {
+ var isBind = bitmask & BIND_FLAG,
+ Ctor = createCtorWrapper(func);
+
+ function wrapper() {
+ // Avoid `arguments` object use disqualifying optimizations by
+ // converting it to an array before providing it `func`.
+ var argsIndex = -1,
+ argsLength = arguments.length,
+ leftIndex = -1,
+ leftLength = partials.length,
+ args = Array(argsLength + leftLength);
+
+ while (++leftIndex < leftLength) {
+ args[leftIndex] = partials[leftIndex];
+ }
+ while (argsLength--) {
+ args[leftIndex++] = arguments[++argsIndex];
+ }
+ var fn = (this && this !== global && this instanceof wrapper) ? Ctor : func;
+ return fn.apply(isBind ? thisArg : this, args);
+ }
+ return wrapper;
+}
+
+module.exports = createPartialWrapper;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./createCtorWrapper":63}],68:[function(require,module,exports){
+var baseSetData = require('./baseSetData'),
+ createBindWrapper = require('./createBindWrapper'),
+ createHybridWrapper = require('./createHybridWrapper'),
+ createPartialWrapper = require('./createPartialWrapper'),
+ getData = require('./getData'),
+ mergeData = require('./mergeData'),
+ setData = require('./setData');
+
+/** Used to compose bitmasks for wrapper metadata. */
+var BIND_FLAG = 1,
+ BIND_KEY_FLAG = 2,
+ PARTIAL_FLAG = 32,
+ PARTIAL_RIGHT_FLAG = 64;
+
+/** Used as the `TypeError` message for "Functions" methods. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max;
+
+/**
+ * Creates a function that either curries or invokes `func` with optional
+ * `this` binding and partially applied arguments.
+ *
+ * @private
+ * @param {Function|string} func The function or method name to reference.
+ * @param {number} bitmask The bitmask of flags.
+ * The bitmask may be composed of the following flags:
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry` or `_.curryRight` of a bound function
+ * 8 - `_.curry`
+ * 16 - `_.curryRight`
+ * 32 - `_.partial`
+ * 64 - `_.partialRight`
+ * 128 - `_.rearg`
+ * 256 - `_.ary`
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to be partially applied.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
+ */
+function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
+ var isBindKey = bitmask & BIND_KEY_FLAG;
+ if (!isBindKey && typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var length = partials ? partials.length : 0;
+ if (!length) {
+ bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);
+ partials = holders = null;
+ }
+ length -= (holders ? holders.length : 0);
+ if (bitmask & PARTIAL_RIGHT_FLAG) {
+ var partialsRight = partials,
+ holdersRight = holders;
+
+ partials = holders = null;
+ }
+ var data = isBindKey ? null : getData(func),
+ newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
+
+ if (data) {
+ mergeData(newData, data);
+ bitmask = newData[1];
+ arity = newData[9];
+ }
+ newData[9] = arity == null
+ ? (isBindKey ? 0 : func.length)
+ : (nativeMax(arity - length, 0) || 0);
+
+ if (bitmask == BIND_FLAG) {
+ var result = createBindWrapper(newData[0], newData[2]);
+ } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) {
+ result = createPartialWrapper.apply(undefined, newData);
+ } else {
+ result = createHybridWrapper.apply(undefined, newData);
+ }
+ var setter = data ? baseSetData : setData;
+ return setter(result, newData);
+}
+
+module.exports = createWrapper;
+
+},{"./baseSetData":52,"./createBindWrapper":62,"./createHybridWrapper":66,"./createPartialWrapper":67,"./getData":72,"./mergeData":84,"./setData":89}],69:[function(require,module,exports){
+/**
+ * A specialized version of `baseIsEqualDeep` for arrays with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Array} array The array to compare.
+ * @param {Array} other The other array to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparing arrays.
+ * @param {boolean} [isLoose] Specify performing partial comparisons.
+ * @param {Array} [stackA] Tracks traversed `value` objects.
+ * @param {Array} [stackB] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+ */
+function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
+ var index = -1,
+ arrLength = array.length,
+ othLength = other.length,
+ result = true;
+
+ if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
+ return false;
+ }
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (result && ++index < arrLength) {
+ var arrValue = array[index],
+ othValue = other[index];
+
+ result = undefined;
+ if (customizer) {
+ result = isLoose
+ ? customizer(othValue, arrValue, index)
+ : customizer(arrValue, othValue, index);
+ }
+ if (typeof result == 'undefined') {
+ // Recursively compare arrays (susceptible to call stack limits).
+ if (isLoose) {
+ var othIndex = othLength;
+ while (othIndex--) {
+ othValue = other[othIndex];
+ result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
+ if (result) {
+ break;
+ }
+ }
+ } else {
+ result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
+ }
+ }
+ }
+ return !!result;
+}
+
+module.exports = equalArrays;
+
+},{}],70:[function(require,module,exports){
+/** `Object#toString` result references. */
+var boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ numberTag = '[object Number]',
+ regexpTag = '[object RegExp]',
+ stringTag = '[object String]';
+
+/**
+ * A specialized version of `baseIsEqualDeep` for comparing objects of
+ * the same `toStringTag`.
+ *
+ * **Note:** This function only supports comparing values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ * @private
+ * @param {Object} value The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {string} tag The `toStringTag` of the objects to compare.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+function equalByTag(object, other, tag) {
+ switch (tag) {
+ case boolTag:
+ case dateTag:
+ // Coerce dates and booleans to numbers, dates to milliseconds and booleans
+ // to `1` or `0` treating invalid dates coerced to `NaN` as not equal.
+ return +object == +other;
+
+ case errorTag:
+ return object.name == other.name && object.message == other.message;
+
+ case numberTag:
+ // Treat `NaN` vs. `NaN` as equal.
+ return (object != +object)
+ ? other != +other
+ // But, treat `-0` vs. `+0` as not equal.
+ : (object == 0 ? ((1 / object) == (1 / other)) : object == +other);
+
+ case regexpTag:
+ case stringTag:
+ // Coerce regexes to strings and treat strings primitives and string
+ // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details.
+ return object == (other + '');
+ }
+ return false;
+}
+
+module.exports = equalByTag;
+
+},{}],71:[function(require,module,exports){
+var keys = require('../object/keys');
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * A specialized version of `baseIsEqualDeep` for objects with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparing values.
+ * @param {boolean} [isLoose] Specify performing partial comparisons.
+ * @param {Array} [stackA] Tracks traversed `value` objects.
+ * @param {Array} [stackB] Tracks traversed `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
+ var objProps = keys(object),
+ objLength = objProps.length,
+ othProps = keys(other),
+ othLength = othProps.length;
+
+ if (objLength != othLength && !isLoose) {
+ return false;
+ }
+ var skipCtor = isLoose,
+ index = -1;
+
+ while (++index < objLength) {
+ var key = objProps[index],
+ result = isLoose ? key in other : hasOwnProperty.call(other, key);
+
+ if (result) {
+ var objValue = object[key],
+ othValue = other[key];
+
+ result = undefined;
+ if (customizer) {
+ result = isLoose
+ ? customizer(othValue, objValue, key)
+ : customizer(objValue, othValue, key);
+ }
+ if (typeof result == 'undefined') {
+ // Recursively compare objects (susceptible to call stack limits).
+ result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB);
+ }
+ }
+ if (!result) {
+ return false;
+ }
+ skipCtor || (skipCtor = key == 'constructor');
+ }
+ if (!skipCtor) {
+ var objCtor = object.constructor,
+ othCtor = other.constructor;
+
+ // Non `Object` object instances with different constructors are not equal.
+ if (objCtor != othCtor &&
+ ('constructor' in object && 'constructor' in other) &&
+ !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+ typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+module.exports = equalObjects;
+
+},{"../object/keys":104}],72:[function(require,module,exports){
+var metaMap = require('./metaMap'),
+ noop = require('../utility/noop');
+
+/**
+ * Gets metadata for `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {*} Returns the metadata for `func`.
+ */
+var getData = !metaMap ? noop : function(func) {
+ return metaMap.get(func);
+};
+
+module.exports = getData;
+
+},{"../utility/noop":110,"./metaMap":85}],73:[function(require,module,exports){
+var baseProperty = require('./baseProperty'),
+ constant = require('../utility/constant'),
+ realNames = require('./realNames'),
+ support = require('../support');
+
+/**
+ * Gets the name of `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {string} Returns the function name.
+ */
+var getFuncName = (function() {
+ if (!support.funcNames) {
+ return constant('');
+ }
+ if (constant.name == 'constant') {
+ return baseProperty('name');
+ }
+ return function(func) {
+ var result = func.name,
+ array = realNames[result],
+ length = array ? array.length : 0;
+
+ while (length--) {
+ var data = array[length],
+ otherFunc = data.func;
+
+ if (otherFunc == null || otherFunc == func) {
+ return data.name;
+ }
+ }
+ return result;
+ };
+}());
+
+module.exports = getFuncName;
+
+},{"../support":107,"../utility/constant":108,"./baseProperty":51,"./realNames":86}],74:[function(require,module,exports){
+/**
+ * Gets the index at which the first occurrence of `NaN` is found in `array`.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {number} fromIndex The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched `NaN`, else `-1`.
+ */
+function indexOfNaN(array, fromIndex, fromRight) {
+ var length = array.length,
+ index = fromIndex + (fromRight ? 0 : -1);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ var other = array[index];
+ if (other !== other) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+module.exports = indexOfNaN;
+
+},{}],75:[function(require,module,exports){
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Initializes an array clone.
+ *
+ * @private
+ * @param {Array} array The array to clone.
+ * @returns {Array} Returns the initialized clone.
+ */
+function initCloneArray(array) {
+ var length = array.length,
+ result = new array.constructor(length);
+
+ // Add array properties assigned by `RegExp#exec`.
+ if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
+ result.index = array.index;
+ result.input = array.input;
+ }
+ return result;
+}
+
+module.exports = initCloneArray;
+
+},{}],76:[function(require,module,exports){
+(function (global){
+var bufferClone = require('./bufferClone');
+
+/** `Object#toString` result references. */
+var boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ numberTag = '[object Number]',
+ regexpTag = '[object RegExp]',
+ stringTag = '[object String]';
+
+var arrayBufferTag = '[object ArrayBuffer]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
+
+/** Used to match `RegExp` flags from their coerced string values. */
+var reFlags = /\w*$/;
+
+/** Used to lookup a type array constructors by `toStringTag`. */
+var ctorByTag = {};
+ctorByTag[float32Tag] = global.Float32Array;
+ctorByTag[float64Tag] = global.Float64Array;
+ctorByTag[int8Tag] = global.Int8Array;
+ctorByTag[int16Tag] = global.Int16Array;
+ctorByTag[int32Tag] = global.Int32Array;
+ctorByTag[uint8Tag] = global.Uint8Array;
+ctorByTag[uint8ClampedTag] = global.Uint8ClampedArray;
+ctorByTag[uint16Tag] = global.Uint16Array;
+ctorByTag[uint32Tag] = global.Uint32Array;
+
+/**
+ * Initializes an object clone based on its `toStringTag`.
+ *
+ * **Note:** This function only supports cloning values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @param {string} tag The `toStringTag` of the object to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+function initCloneByTag(object, tag, isDeep) {
+ var Ctor = object.constructor;
+ switch (tag) {
+ case arrayBufferTag:
+ return bufferClone(object);
+
+ case boolTag:
+ case dateTag:
+ return new Ctor(+object);
+
+ case float32Tag: case float64Tag:
+ case int8Tag: case int16Tag: case int32Tag:
+ case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
+ // Safari 5 mobile incorrectly has `Object` as the constructor of typed arrays.
+ if (Ctor instanceof Ctor) {
+ Ctor = ctorByTag[tag];
+ }
+ var buffer = object.buffer;
+ return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length);
+
+ case numberTag:
+ case stringTag:
+ return new Ctor(object);
+
+ case regexpTag:
+ var result = new Ctor(object.source, reFlags.exec(object));
+ result.lastIndex = object.lastIndex;
+ }
+ return result;
+}
+
+module.exports = initCloneByTag;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./bufferClone":57}],77:[function(require,module,exports){
+/**
+ * Initializes an object clone.
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+function initCloneObject(object) {
+ var Ctor = object.constructor;
+ if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) {
+ Ctor = Object;
+ }
+ return new Ctor;
+}
+
+module.exports = initCloneObject;
+
+},{}],78:[function(require,module,exports){
+/**
+ * Checks if `value` is a host object in IE < 9.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
+ */
+var isHostObject = (function() {
+ try {
+ Object({ 'toString': 0 } + '');
+ } catch(e) {
+ return function() { return false; };
+ }
+ return function(value) {
+ // IE < 9 presents many host objects as `Object` objects that can coerce
+ // to strings despite having improperly defined `toString` methods.
+ return typeof value.toString != 'function' && typeof (value + '') == 'string';
+ };
+}());
+
+module.exports = isHostObject;
+
+},{}],79:[function(require,module,exports){
+/**
+ * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
+ * of an array-like value.
+ */
+var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
+
+/**
+ * Checks if `value` is a valid array-like index.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+ * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+ */
+function isIndex(value, length) {
+ value = +value;
+ length = length == null ? MAX_SAFE_INTEGER : length;
+ return value > -1 && value % 1 == 0 && value < length;
+}
+
+module.exports = isIndex;
+
+},{}],80:[function(require,module,exports){
+var LazyWrapper = require('./LazyWrapper'),
+ getFuncName = require('./getFuncName'),
+ lodash = require('../chain/lodash');
+
+/**
+ * Checks if `func` has a lazy counterpart.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`.
+ */
+function isLaziable(func) {
+ var funcName = getFuncName(func);
+ return !!funcName && func === lodash[funcName] && funcName in LazyWrapper.prototype;
+}
+
+module.exports = isLaziable;
+
+},{"../chain/lodash":20,"./LazyWrapper":27,"./getFuncName":73}],81:[function(require,module,exports){
+/**
+ * Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer)
+ * of an array-like value.
+ */
+var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
+
+/**
+ * Checks if `value` is a valid array-like length.
+ *
+ * **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength).
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+ */
+function isLength(value) {
+ return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+}
+
+module.exports = isLength;
+
+},{}],82:[function(require,module,exports){
+/**
+ * Checks if `value` is object-like.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ */
+function isObjectLike(value) {
+ return !!value && typeof value == 'object';
+}
+
+module.exports = isObjectLike;
+
+},{}],83:[function(require,module,exports){
+var isObject = require('../lang/isObject');
+
+/**
+ * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` if suitable for strict
+ * equality comparisons, else `false`.
+ */
+function isStrictComparable(value) {
+ return value === value && (value === 0 ? ((1 / value) > 0) : !isObject(value));
+}
+
+module.exports = isStrictComparable;
+
+},{"../lang/isObject":99}],84:[function(require,module,exports){
+var arrayCopy = require('./arrayCopy'),
+ composeArgs = require('./composeArgs'),
+ composeArgsRight = require('./composeArgsRight'),
+ replaceHolders = require('./replaceHolders');
+
+/** Used to compose bitmasks for wrapper metadata. */
+var BIND_FLAG = 1,
+ CURRY_BOUND_FLAG = 4,
+ CURRY_FLAG = 8,
+ ARY_FLAG = 128,
+ REARG_FLAG = 256;
+
+/** Used as the internal argument placeholder. */
+var PLACEHOLDER = '__lodash_placeholder__';
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMin = Math.min;
+
+/**
+ * Merges the function metadata of `source` into `data`.
+ *
+ * Merging metadata reduces the number of wrappers required to invoke a function.
+ * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
+ * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg`
+ * augment function arguments, making the order in which they are executed important,
+ * preventing the merging of metadata. However, we make an exception for a safe
+ * common case where curried functions have `_.ary` and or `_.rearg` applied.
+ *
+ * @private
+ * @param {Array} data The destination metadata.
+ * @param {Array} source The source metadata.
+ * @returns {Array} Returns `data`.
+ */
+function mergeData(data, source) {
+ var bitmask = data[1],
+ srcBitmask = source[1],
+ newBitmask = bitmask | srcBitmask,
+ isCommon = newBitmask < ARY_FLAG;
+
+ var isCombo =
+ (srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) ||
+ (srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) ||
+ (srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG);
+
+ // Exit early if metadata can't be merged.
+ if (!(isCommon || isCombo)) {
+ return data;
+ }
+ // Use source `thisArg` if available.
+ if (srcBitmask & BIND_FLAG) {
+ data[2] = source[2];
+ // Set when currying a bound function.
+ newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG;
+ }
+ // Compose partial arguments.
+ var value = source[3];
+ if (value) {
+ var partials = data[3];
+ data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value);
+ data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]);
+ }
+ // Compose partial right arguments.
+ value = source[5];
+ if (value) {
+ partials = data[5];
+ data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value);
+ data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]);
+ }
+ // Use source `argPos` if available.
+ value = source[7];
+ if (value) {
+ data[7] = arrayCopy(value);
+ }
+ // Use source `ary` if it's smaller.
+ if (srcBitmask & ARY_FLAG) {
+ data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
+ }
+ // Use source `arity` if one is not provided.
+ if (data[9] == null) {
+ data[9] = source[9];
+ }
+ // Use source `func` and merge bitmasks.
+ data[0] = source[0];
+ data[1] = newBitmask;
+
+ return data;
+}
+
+module.exports = mergeData;
+
+},{"./arrayCopy":29,"./composeArgs":58,"./composeArgsRight":59,"./replaceHolders":88}],85:[function(require,module,exports){
+(function (global){
+var isNative = require('../lang/isNative');
+
+/** Native method references. */
+var WeakMap = isNative(WeakMap = global.WeakMap) && WeakMap;
+
+/** Used to store function metadata. */
+var metaMap = WeakMap && new WeakMap;
+
+module.exports = metaMap;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"../lang/isNative":98}],86:[function(require,module,exports){
+/** Used to lookup unminified function names. */
+var realNames = {};
+
+module.exports = realNames;
+
+},{}],87:[function(require,module,exports){
+var arrayCopy = require('./arrayCopy'),
+ isIndex = require('./isIndex');
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeMin = Math.min;
+
+/**
+ * Reorder `array` according to the specified indexes where the element at
+ * the first index is assigned as the first element, the element at
+ * the second index is assigned as the second element, and so on.
+ *
+ * @private
+ * @param {Array} array The array to reorder.
+ * @param {Array} indexes The arranged array indexes.
+ * @returns {Array} Returns `array`.
+ */
+function reorder(array, indexes) {
+ var arrLength = array.length,
+ length = nativeMin(indexes.length, arrLength),
+ oldArray = arrayCopy(array);
+
+ while (length--) {
+ var index = indexes[length];
+ array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
+ }
+ return array;
+}
+
+module.exports = reorder;
+
+},{"./arrayCopy":29,"./isIndex":79}],88:[function(require,module,exports){
+/** Used as the internal argument placeholder. */
+var PLACEHOLDER = '__lodash_placeholder__';
+
+/**
+ * Replaces all `placeholder` elements in `array` with an internal placeholder
+ * and returns an array of their indexes.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {*} placeholder The placeholder to replace.
+ * @returns {Array} Returns the new array of placeholder indexes.
+ */
+function replaceHolders(array, placeholder) {
+ var index = -1,
+ length = array.length,
+ resIndex = -1,
+ result = [];
+
+ while (++index < length) {
+ if (array[index] === placeholder) {
+ array[index] = PLACEHOLDER;
+ result[++resIndex] = index;
+ }
+ }
+ return result;
+}
+
+module.exports = replaceHolders;
+
+},{}],89:[function(require,module,exports){
+var baseSetData = require('./baseSetData'),
+ now = require('../date/now');
+
+/** Used to detect when a function becomes hot. */
+var HOT_COUNT = 150,
+ HOT_SPAN = 16;
+
+/**
+ * Sets metadata for `func`.
+ *
+ * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
+ * period of time, it will trip its breaker and transition to an identity function
+ * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070)
+ * for more details.
+ *
+ * @private
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
+ */
+var setData = (function() {
+ var count = 0,
+ lastCalled = 0;
+
+ return function(key, value) {
+ var stamp = now(),
+ remaining = HOT_SPAN - (stamp - lastCalled);
+
+ lastCalled = stamp;
+ if (remaining > 0) {
+ if (++count >= HOT_COUNT) {
+ return key;
+ }
+ } else {
+ count = 0;
+ }
+ return baseSetData(key, value);
+ };
+}());
+
+module.exports = setData;
+
+},{"../date/now":24,"./baseSetData":52}],90:[function(require,module,exports){
+var baseForIn = require('./baseForIn'),
+ isArguments = require('../lang/isArguments'),
+ isHostObject = require('./isHostObject'),
+ isObjectLike = require('./isObjectLike'),
+ support = require('../support');
+
+/** `Object#toString` result references. */
+var objectTag = '[object Object]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/**
+ * A fallback implementation of `_.isPlainObject` which checks if `value`
+ * is an object created by the `Object` constructor or has a `[[Prototype]]`
+ * of `null`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ */
+function shimIsPlainObject(value) {
+ var Ctor;
+
+ // Exit early for non `Object` objects.
+ if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value)) ||
+ (!hasOwnProperty.call(value, 'constructor') &&
+ (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor))) ||
+ (!support.argsTag && isArguments(value))) {
+ return false;
+ }
+ // IE < 9 iterates inherited properties before own properties. If the first
+ // iterated property is an object's own property then there are no inherited
+ // enumerable properties.
+ var result;
+ if (support.ownLast) {
+ baseForIn(value, function(subValue, key, object) {
+ result = hasOwnProperty.call(object, key);
+ return false;
+ });
+ return result !== false;
+ }
+ // In most environments an object's own properties are iterated before
+ // its inherited properties. If the last iterated property is an object's
+ // own property then there are no inherited enumerable properties.
+ baseForIn(value, function(subValue, key) {
+ result = key;
+ });
+ return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+}
+
+module.exports = shimIsPlainObject;
+
+},{"../lang/isArguments":95,"../support":107,"./baseForIn":40,"./isHostObject":78,"./isObjectLike":82}],91:[function(require,module,exports){
+var isArguments = require('../lang/isArguments'),
+ isArray = require('../lang/isArray'),
+ isIndex = require('./isIndex'),
+ isLength = require('./isLength'),
+ isString = require('../lang/isString'),
+ keysIn = require('../object/keysIn'),
+ support = require('../support');
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * A fallback implementation of `Object.keys` which creates an array of the
+ * own enumerable property names of `object`.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ */
+function shimKeys(object) {
+ var props = keysIn(object),
+ propsLength = props.length,
+ length = propsLength && object.length;
+
+ var allowIndexes = length && isLength(length) &&
+ (isArray(object) || (support.nonEnumStrings && isString(object)) ||
+ (support.nonEnumArgs && isArguments(object)));
+
+ var index = -1,
+ result = [];
+
+ while (++index < propsLength) {
+ var key = props[index];
+ if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
+ result.push(key);
+ }
+ }
+ return result;
+}
+
+module.exports = shimKeys;
+
+},{"../lang/isArguments":95,"../lang/isArray":96,"../lang/isString":101,"../object/keysIn":105,"../support":107,"./isIndex":79,"./isLength":81}],92:[function(require,module,exports){
+var isObject = require('../lang/isObject'),
+ isString = require('../lang/isString'),
+ support = require('../support');
+
+/**
+ * Converts `value` to an object if it is not one.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {Object} Returns the object.
+ */
+function toObject(value) {
+ if (support.unindexedChars && isString(value)) {
+ var index = -1,
+ length = value.length,
+ result = Object(value);
+
+ while (++index < length) {
+ result[index] = value.charAt(index);
+ }
+ return result;
+ }
+ return isObject(value) ? value : Object(value);
+}
+
+module.exports = toObject;
+
+},{"../lang/isObject":99,"../lang/isString":101,"../support":107}],93:[function(require,module,exports){
+var LazyWrapper = require('./LazyWrapper'),
+ LodashWrapper = require('./LodashWrapper'),
+ arrayCopy = require('./arrayCopy');
+
+/**
+ * Creates a clone of `wrapper`.
+ *
+ * @private
+ * @param {Object} wrapper The wrapper to clone.
+ * @returns {Object} Returns the cloned wrapper.
+ */
+function wrapperClone(wrapper) {
+ return wrapper instanceof LazyWrapper
+ ? wrapper.clone()
+ : new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__, arrayCopy(wrapper.__actions__));
+}
+
+module.exports = wrapperClone;
+
+},{"./LazyWrapper":27,"./LodashWrapper":28,"./arrayCopy":29}],94:[function(require,module,exports){
+var baseClone = require('../internal/baseClone'),
+ bindCallback = require('../internal/bindCallback');
+
+/**
+ * Creates a deep clone of `value`. If `customizer` is provided it is invoked
+ * to produce the cloned values. If `customizer` returns `undefined` cloning
+ * is handled by the method instead. The `customizer` is bound to `thisArg`
+ * and invoked with two argument; (value [, index|key, object]).
+ *
+ * **Note:** This method is loosely based on the
+ * [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm).
+ * The enumerable properties of `arguments` objects and objects created by
+ * constructors other than `Object` are cloned to plain `Object` objects. An
+ * empty object is returned for uncloneable values such as functions, DOM nodes,
+ * Maps, Sets, and WeakMaps.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to deep clone.
+ * @param {Function} [customizer] The function to customize cloning values.
+ * @param {*} [thisArg] The `this` binding of `customizer`.
+ * @returns {*} Returns the deep cloned value.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * var deep = _.cloneDeep(users);
+ * deep[0] === users[0];
+ * // => false
+ *
+ * // using a customizer callback
+ * var el = _.cloneDeep(document.body, function(value) {
+ * if (_.isElement(value)) {
+ * return value.cloneNode(true);
+ * }
+ * });
+ *
+ * el === document.body
+ * // => false
+ * el.nodeName
+ * // => BODY
+ * el.childNodes.length;
+ * // => 20
+ */
+function cloneDeep(value, customizer, thisArg) {
+ customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 1);
+ return baseClone(value, true, customizer);
+}
+
+module.exports = cloneDeep;
+
+},{"../internal/baseClone":33,"../internal/bindCallback":56}],95:[function(require,module,exports){
+var isLength = require('../internal/isLength'),
+ isObjectLike = require('../internal/isObjectLike'),
+ support = require('../support');
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/** Native method references. */
+var propertyIsEnumerable = objectProto.propertyIsEnumerable;
+
+/**
+ * Checks if `value` is classified as an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isArguments(function() { return arguments; }());
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+function isArguments(value) {
+ var length = isObjectLike(value) ? value.length : undefined;
+ return isLength(length) && objToString.call(value) == argsTag;
+}
+// Fallback for environments without a `toStringTag` for `arguments` objects.
+if (!support.argsTag) {
+ isArguments = function(value) {
+ var length = isObjectLike(value) ? value.length : undefined;
+ return isLength(length) && hasOwnProperty.call(value, 'callee') &&
+ !propertyIsEnumerable.call(value, 'callee');
+ };
+}
+
+module.exports = isArguments;
+
+},{"../internal/isLength":81,"../internal/isObjectLike":82,"../support":107}],96:[function(require,module,exports){
+var isLength = require('../internal/isLength'),
+ isNative = require('./isNative'),
+ isObjectLike = require('../internal/isObjectLike');
+
+/** `Object#toString` result references. */
+var arrayTag = '[object Array]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray;
+
+/**
+ * Checks if `value` is classified as an `Array` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ *
+ * _.isArray(function() { return arguments; }());
+ * // => false
+ */
+var isArray = nativeIsArray || function(value) {
+ return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
+};
+
+module.exports = isArray;
+
+},{"../internal/isLength":81,"../internal/isObjectLike":82,"./isNative":98}],97:[function(require,module,exports){
+(function (global){
+var baseIsFunction = require('../internal/baseIsFunction'),
+ isNative = require('./isNative');
+
+/** `Object#toString` result references. */
+var funcTag = '[object Function]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/** Native method references. */
+var Uint8Array = isNative(Uint8Array = global.Uint8Array) && Uint8Array;
+
+/**
+ * Checks if `value` is classified as a `Function` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
+ */
+var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) {
+ // The use of `Object#toString` avoids issues with the `typeof` operator
+ // in older versions of Chrome and Safari which return 'function' for regexes
+ // and Safari 8 equivalents which return 'object' for typed array constructors.
+ return objToString.call(value) == funcTag;
+};
+
+module.exports = isFunction;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"../internal/baseIsFunction":45,"./isNative":98}],98:[function(require,module,exports){
+var escapeRegExp = require('../string/escapeRegExp'),
+ isHostObject = require('../internal/isHostObject'),
+ isObjectLike = require('../internal/isObjectLike');
+
+/** `Object#toString` result references. */
+var funcTag = '[object Function]';
+
+/** Used to detect host constructors (Safari > 5). */
+var reHostCtor = /^\[object .+?Constructor\]$/;
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/** Used to resolve the decompiled source of functions. */
+var fnToString = Function.prototype.toString;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/** Used to detect if a method is native. */
+var reNative = RegExp('^' +
+ escapeRegExp(objToString)
+ .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+);
+
+/**
+ * Checks if `value` is a native function.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
+ * @example
+ *
+ * _.isNative(Array.prototype.push);
+ * // => true
+ *
+ * _.isNative(_);
+ * // => false
+ */
+function isNative(value) {
+ if (value == null) {
+ return false;
+ }
+ if (objToString.call(value) == funcTag) {
+ return reNative.test(fnToString.call(value));
+ }
+ return isObjectLike(value) && (isHostObject(value) ? reNative : reHostCtor).test(value);
+}
+
+module.exports = isNative;
+
+},{"../internal/isHostObject":78,"../internal/isObjectLike":82,"../string/escapeRegExp":106}],99:[function(require,module,exports){
+/**
+ * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
+ * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(1);
+ * // => false
+ */
+function isObject(value) {
+ // Avoid a V8 JIT bug in Chrome 19-20.
+ // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
+ var type = typeof value;
+ return type == 'function' || (!!value && type == 'object');
+}
+
+module.exports = isObject;
+
+},{}],100:[function(require,module,exports){
+var isArguments = require('./isArguments'),
+ isNative = require('./isNative'),
+ shimIsPlainObject = require('../internal/shimIsPlainObject'),
+ support = require('../support');
+
+/** `Object#toString` result references. */
+var objectTag = '[object Object]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/** Native method references. */
+var getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf;
+
+/**
+ * Checks if `value` is a plain object, that is, an object created by the
+ * `Object` constructor or one with a `[[Prototype]]` of `null`.
+ *
+ * **Note:** This method assumes objects created by the `Object` constructor
+ * have no inherited enumerable properties.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * }
+ *
+ * _.isPlainObject(new Foo);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ *
+ * _.isPlainObject(Object.create(null));
+ * // => true
+ */
+var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+ if (!(value && objToString.call(value) == objectTag) || (!support.argsTag && isArguments(value))) {
+ return false;
+ }
+ var valueOf = value.valueOf,
+ objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+ return objProto
+ ? (value == objProto || getPrototypeOf(value) == objProto)
+ : shimIsPlainObject(value);
+};
+
+module.exports = isPlainObject;
+
+},{"../internal/shimIsPlainObject":90,"../support":107,"./isArguments":95,"./isNative":98}],101:[function(require,module,exports){
+var isObjectLike = require('../internal/isObjectLike');
+
+/** `Object#toString` result references. */
+var stringTag = '[object String]';
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/**
+ * Checks if `value` is classified as a `String` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isString('abc');
+ * // => true
+ *
+ * _.isString(1);
+ * // => false
+ */
+function isString(value) {
+ return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
+}
+
+module.exports = isString;
+
+},{"../internal/isObjectLike":82}],102:[function(require,module,exports){
+var isLength = require('../internal/isLength'),
+ isObjectLike = require('../internal/isObjectLike');
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ mapTag = '[object Map]',
+ numberTag = '[object Number]',
+ objectTag = '[object Object]',
+ regexpTag = '[object RegExp]',
+ setTag = '[object Set]',
+ stringTag = '[object String]',
+ weakMapTag = '[object WeakMap]';
+
+var arrayBufferTag = '[object ArrayBuffer]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
+
+/** Used to identify `toStringTag` values of typed arrays. */
+var typedArrayTags = {};
+typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+typedArrayTags[uint32Tag] = true;
+typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+typedArrayTags[dateTag] = typedArrayTags[errorTag] =
+typedArrayTags[funcTag] = typedArrayTags[mapTag] =
+typedArrayTags[numberTag] = typedArrayTags[objectTag] =
+typedArrayTags[regexpTag] = typedArrayTags[setTag] =
+typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
+
+/** Used for native method references. */
+var objectProto = Object.prototype;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/**
+ * Checks if `value` is classified as a typed array.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isTypedArray(new Uint8Array);
+ * // => true
+ *
+ * _.isTypedArray([]);
+ * // => false
+ */
+function isTypedArray(value) {
+ return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
+}
+
+module.exports = isTypedArray;
+
+},{"../internal/isLength":81,"../internal/isObjectLike":82}],103:[function(require,module,exports){
+/**
+ * Checks if `value` is `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
+ * @example
+ *
+ * _.isUndefined(void 0);
+ * // => true
+ *
+ * _.isUndefined(null);
+ * // => false
+ */
+function isUndefined(value) {
+ return typeof value == 'undefined';
+}
+
+module.exports = isUndefined;
+
+},{}],104:[function(require,module,exports){
+var isLength = require('../internal/isLength'),
+ isNative = require('../lang/isNative'),
+ isObject = require('../lang/isObject'),
+ shimKeys = require('../internal/shimKeys'),
+ support = require('../support');
+
+/* Native method references for those with the same name as other `lodash` methods. */
+var nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys;
+
+/**
+ * Creates an array of the own enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects. See the
+ * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys)
+ * for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keys(new Foo);
+ * // => ['a', 'b'] (iteration order is not guaranteed)
+ *
+ * _.keys('hi');
+ * // => ['0', '1']
+ */
+var keys = !nativeKeys ? shimKeys : function(object) {
+ if (object) {
+ var Ctor = object.constructor,
+ length = object.length;
+ }
+ if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
+ (typeof object == 'function' ? support.enumPrototypes : (length && isLength(length)))) {
+ return shimKeys(object);
+ }
+ return isObject(object) ? nativeKeys(object) : [];
+};
+
+module.exports = keys;
+
+},{"../internal/isLength":81,"../internal/shimKeys":91,"../lang/isNative":98,"../lang/isObject":99,"../support":107}],105:[function(require,module,exports){
+var arrayEach = require('../internal/arrayEach'),
+ isArguments = require('../lang/isArguments'),
+ isArray = require('../lang/isArray'),
+ isFunction = require('../lang/isFunction'),
+ isIndex = require('../internal/isIndex'),
+ isLength = require('../internal/isLength'),
+ isObject = require('../lang/isObject'),
+ isString = require('../lang/isString'),
+ support = require('../support');
+
+/** `Object#toString` result references. */
+var arrayTag = '[object Array]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ numberTag = '[object Number]',
+ objectTag = '[object Object]',
+ regexpTag = '[object RegExp]',
+ stringTag = '[object String]';
+
+/** Used to fix the JScript `[[DontEnum]]` bug. */
+var shadowProps = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+];
+
+/** Used for native method references. */
+var errorProto = Error.prototype,
+ objectProto = Object.prototype,
+ stringProto = String.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/** Used to avoid iterating over non-enumerable properties in IE < 9. */
+var nonEnumProps = {};
+nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
+nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
+nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
+nonEnumProps[objectTag] = { 'constructor': true };
+
+arrayEach(shadowProps, function(key) {
+ for (var tag in nonEnumProps) {
+ if (hasOwnProperty.call(nonEnumProps, tag)) {
+ var props = nonEnumProps[tag];
+ props[key] = hasOwnProperty.call(props, key);
+ }
+ }
+});
+
+/**
+ * Creates an array of the own and inherited enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keysIn(new Foo);
+ * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
+ */
+function keysIn(object) {
+ if (object == null) {
+ return [];
+ }
+ if (!isObject(object)) {
+ object = Object(object);
+ }
+ var length = object.length;
+
+ length = (length && isLength(length) &&
+ (isArray(object) || (support.nonEnumStrings && isString(object)) ||
+ (support.nonEnumArgs && isArguments(object))) && length) || 0;
+
+ var Ctor = object.constructor,
+ index = -1,
+ proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
+ isProto = proto === object,
+ result = Array(length),
+ skipIndexes = length > 0,
+ skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
+ skipProto = support.enumPrototypes && isFunction(object);
+
+ while (++index < length) {
+ result[index] = (index + '');
+ }
+ // lodash skips the `constructor` property when it infers it is iterating
+ // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
+ // attribute of an existing property and the `constructor` property of a
+ // prototype defaults to non-enumerable.
+ for (var key in object) {
+ if (!(skipProto && key == 'prototype') &&
+ !(skipErrorProps && (key == 'message' || key == 'name')) &&
+ !(skipIndexes && isIndex(key, length)) &&
+ !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
+ result.push(key);
+ }
+ }
+ if (support.nonEnumShadows && object !== objectProto) {
+ var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
+ nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];
+
+ if (tag == objectTag) {
+ proto = objectProto;
+ }
+ length = shadowProps.length;
+ while (length--) {
+ key = shadowProps[length];
+ var nonEnum = nonEnums[key];
+ if (!(isProto && nonEnum) &&
+ (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
+ result.push(key);
+ }
+ }
+ }
+ return result;
+}
+
+module.exports = keysIn;
+
+},{"../internal/arrayEach":30,"../internal/isIndex":79,"../internal/isLength":81,"../lang/isArguments":95,"../lang/isArray":96,"../lang/isFunction":97,"../lang/isObject":99,"../lang/isString":101,"../support":107}],106:[function(require,module,exports){
+var baseToString = require('../internal/baseToString');
+
+/**
+ * Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
+ * In addition to special characters the forward slash is escaped to allow for
+ * easier `eval` use and `Function` compilation.
+ */
+var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
+ reHasRegExpChars = RegExp(reRegExpChars.source);
+
+/**
+ * Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?",
+ * "*", "+", "(", ")", "[", "]", "{" and "}" in `string`.
+ *
+ * @static
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escapeRegExp('[lodash](https://lodash.com/)');
+ * // => '\[lodash\]\(https:\/\/lodash\.com\/\)'
+ */
+function escapeRegExp(string) {
+ string = baseToString(string);
+ return (string && reHasRegExpChars.test(string))
+ ? string.replace(reRegExpChars, '\\$&')
+ : string;
+}
+
+module.exports = escapeRegExp;
+
+},{"../internal/baseToString":53}],107:[function(require,module,exports){
+(function (global){
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+ objectTag = '[object Object]';
+
+/** Used for native method references. */
+var arrayProto = Array.prototype,
+ errorProto = Error.prototype,
+ objectProto = Object.prototype;
+
+/** Used to detect DOM support. */
+var document = (document = global.window) && document.document;
+
+/**
+ * Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring)
+ * of values.
+ */
+var objToString = objectProto.toString;
+
+/** Native method references. */
+var propertyIsEnumerable = objectProto.propertyIsEnumerable,
+ splice = arrayProto.splice;
+
+/**
+ * An object environment feature flags.
+ *
+ * @static
+ * @memberOf _
+ * @type Object
+ */
+var support = {};
+
+(function(x) {
+ var Ctor = function() { this.x = 1; },
+ object = { '0': 1, 'length': 1 },
+ props = [];
+
+ Ctor.prototype = { 'valueOf': 1, 'y': 1 };
+ for (var key in new Ctor) { props.push(key); }
+
+ /**
+ * Detect if the `toStringTag` of `arguments` objects is resolvable
+ * (all but Firefox < 4, IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.argsTag = objToString.call(arguments) == argsTag;
+
+ /**
+ * Detect if `name` or `message` properties of `Error.prototype` are
+ * enumerable by default (IE < 9, Safari < 5.1).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
+ propertyIsEnumerable.call(errorProto, 'name');
+
+ /**
+ * Detect if `prototype` properties are enumerable by default.
+ *
+ * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
+ * (if the prototype or a property on the prototype has been set)
+ * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
+ * property to `true`.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');
+
+ /**
+ * Detect if functions can be decompiled by `Function#toString`
+ * (all but Firefox OS certified apps, older Opera mobile browsers, and
+ * the PlayStation 3; forced `false` for Windows 8 apps).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcDecomp = /\bthis\b/.test(function() { return this; });
+
+ /**
+ * Detect if `Function#name` is supported (all but IE).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.funcNames = typeof Function.name == 'string';
+
+ /**
+ * Detect if the `toStringTag` of DOM nodes is resolvable (all but IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nodeTag = objToString.call(document) != objectTag;
+
+ /**
+ * Detect if string indexes are non-enumerable
+ * (IE < 9, RingoJS, Rhino, Narwhal).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nonEnumStrings = !propertyIsEnumerable.call('x', 0);
+
+ /**
+ * Detect if properties shadowing those on `Object.prototype` are
+ * non-enumerable.
+ *
+ * In IE < 9 an object's own properties, shadowing non-enumerable ones,
+ * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.nonEnumShadows = !/valueOf/.test(props);
+
+ /**
+ * Detect if own properties are iterated after inherited properties (IE < 9).
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.ownLast = props[0] != 'x';
+
+ /**
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects
+ * correctly.
+ *
+ * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array `shift()`
+ * and `splice()` functions that fail to remove the last element, `value[0]`,
+ * of array-like objects even though the `length` property is set to `0`.
+ * The `shift()` method is buggy in compatibility modes of IE 8, while `splice()`
+ * is buggy regardless of mode in IE < 9.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
+
+ /**
+ * Detect lack of support for accessing string characters by index.
+ *
+ * IE < 8 can't access characters by index. IE 8 can only access characters
+ * by index on string literals, not string objects.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
+
+ /**
+ * Detect if the DOM is supported.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.dom = document.createDocumentFragment().nodeType === 11;
+ } catch(e) {
+ support.dom = false;
+ }
+
+ /**
+ * Detect if `arguments` object indexes are non-enumerable.
+ *
+ * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object
+ * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat
+ * `arguments` object indexes as non-enumerable and fail `hasOwnProperty`
+ * checks for indexes that exceed their function's formal parameters with
+ * associated values of `0`.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.nonEnumArgs = !propertyIsEnumerable.call(arguments, 1);
+ } catch(e) {
+ support.nonEnumArgs = true;
+ }
+}(0, 0));
+
+module.exports = support;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],108:[function(require,module,exports){
+/**
+ * Creates a function that returns `value`.
+ *
+ * @static
+ * @memberOf _
+ * @category Utility
+ * @param {*} value The value to return from the new function.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var object = { 'user': 'fred' };
+ * var getter = _.constant(object);
+ *
+ * getter() === object;
+ * // => true
+ */
+function constant(value) {
+ return function() {
+ return value;
+ };
+}
+
+module.exports = constant;
+
+},{}],109:[function(require,module,exports){
+/**
+ * This method returns the first argument provided to it.
+ *
+ * @static
+ * @memberOf _
+ * @category Utility
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'user': 'fred' };
+ *
+ * _.identity(object) === object;
+ * // => true
+ */
+function identity(value) {
+ return value;
+}
+
+module.exports = identity;
+
+},{}],110:[function(require,module,exports){
+/**
+ * A no-operation function which returns `undefined` regardless of the
+ * arguments it receives.
+ *
+ * @static
+ * @memberOf _
+ * @category Utility
+ * @example
+ *
+ * var object = { 'user': 'fred' };
+ *
+ * _.noop(object) === undefined;
+ * // => true
+ */
+function noop() {
+ // No operation performed.
+}
+
+module.exports = noop;
+
+},{}],111:[function(require,module,exports){
+/**
+ * Module dependencies.
+ */
+
+var Emitter = require('emitter');
+var reduce = require('reduce');
+
+/**
+ * Root reference for iframes.
+ */
+
+var root = 'undefined' == typeof window
+ ? this
+ : window;
+
+/**
+ * Noop.
+ */
+
+function noop(){};
+
+/**
+ * Check if `obj` is a host object,
+ * we don't want to serialize these :)
+ *
+ * TODO: future proof, move to compoent land
+ *
+ * @param {Object} obj
+ * @return {Boolean}
+ * @api private
+ */
+
+function isHost(obj) {
+ var str = {}.toString.call(obj);
+
+ switch (str) {
+ case '[object File]':
+ case '[object Blob]':
+ case '[object FormData]':
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Determine XHR.
+ */
+
+function getXHR() {
+ if (root.XMLHttpRequest
+ && ('file:' != root.location.protocol || !root.ActiveXObject)) {
+ return new XMLHttpRequest;
+ } else {
+ try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
+ try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {}
+ try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {}
+ try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}
+ }
+ return false;
+}
+
+/**
+ * Removes leading and trailing whitespace, added to support IE.
+ *
+ * @param {String} s
+ * @return {String}
+ * @api private
+ */
+
+var trim = ''.trim
+ ? function(s) { return s.trim(); }
+ : function(s) { return s.replace(/(^\s*|\s*$)/g, ''); };
+
+/**
+ * Check if `obj` is an object.
+ *
+ * @param {Object} obj
+ * @return {Boolean}
+ * @api private
+ */
+
+function isObject(obj) {
+ return obj === Object(obj);
+}
+
+/**
+ * Serialize the given `obj`.
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api private
+ */
+
+function serialize(obj) {
+ if (!isObject(obj)) return obj;
+ var pairs = [];
+ for (var key in obj) {
+ if (null != obj[key]) {
+ pairs.push(encodeURIComponent(key)
+ + '=' + encodeURIComponent(obj[key]));
+ }
+ }
+ return pairs.join('&');
+}
+
+/**
+ * Expose serialization method.
+ */
+
+ request.serializeObject = serialize;
+
+ /**
+ * Parse the given x-www-form-urlencoded `str`.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function parseString(str) {
+ var obj = {};
+ var pairs = str.split('&');
+ var parts;
+ var pair;
+
+ for (var i = 0, len = pairs.length; i < len; ++i) {
+ pair = pairs[i];
+ parts = pair.split('=');
+ obj[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
+ }
+
+ return obj;
+}
+
+/**
+ * Expose parser.
+ */
+
+request.parseString = parseString;
+
+/**
+ * Default MIME type map.
+ *
+ * superagent.types.xml = 'application/xml';
+ *
+ */
+
+request.types = {
+ html: 'text/html',
+ json: 'application/json',
+ xml: 'application/xml',
+ urlencoded: 'application/x-www-form-urlencoded',
+ 'form': 'application/x-www-form-urlencoded',
+ 'form-data': 'application/x-www-form-urlencoded'
+};
+
+/**
+ * Default serialization map.
+ *
+ * superagent.serialize['application/xml'] = function(obj){
+ * return 'generated xml here';
+ * };
+ *
+ */
+
+ request.serialize = {
+ 'application/x-www-form-urlencoded': serialize,
+ 'application/json': JSON.stringify
+ };
+
+ /**
+ * Default parsers.
+ *
+ * superagent.parse['application/xml'] = function(str){
+ * return { object parsed from str };
+ * };
+ *
+ */
+
+request.parse = {
+ 'application/x-www-form-urlencoded': parseString,
+ 'application/json': JSON.parse
+};
+
+/**
+ * Parse the given header `str` into
+ * an object containing the mapped fields.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function parseHeader(str) {
+ var lines = str.split(/\r?\n/);
+ var fields = {};
+ var index;
+ var line;
+ var field;
+ var val;
+
+ lines.pop(); // trailing CRLF
+
+ for (var i = 0, len = lines.length; i < len; ++i) {
+ line = lines[i];
+ index = line.indexOf(':');
+ field = line.slice(0, index).toLowerCase();
+ val = trim(line.slice(index + 1));
+ fields[field] = val;
+ }
+
+ return fields;
+}
+
+/**
+ * Return the mime type for the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+function type(str){
+ return str.split(/ *; */).shift();
+};
+
+/**
+ * Return header field parameters.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+function params(str){
+ return reduce(str.split(/ *; */), function(obj, str){
+ var parts = str.split(/ *= */)
+ , key = parts.shift()
+ , val = parts.shift();
+
+ if (key && val) obj[key] = val;
+ return obj;
+ }, {});
+};
+
+/**
+ * Initialize a new `Response` with the given `xhr`.
+ *
+ * - set flags (.ok, .error, etc)
+ * - parse header
+ *
+ * Examples:
+ *
+ * Aliasing `superagent` as `request` is nice:
+ *
+ * request = superagent;
+ *
+ * We can use the promise-like API, or pass callbacks:
+ *
+ * request.get('/').end(function(res){});
+ * request.get('/', function(res){});
+ *
+ * Sending data can be chained:
+ *
+ * request
+ * .post('/user')
+ * .send({ name: 'tj' })
+ * .end(function(res){});
+ *
+ * Or passed to `.send()`:
+ *
+ * request
+ * .post('/user')
+ * .send({ name: 'tj' }, function(res){});
+ *
+ * Or passed to `.post()`:
+ *
+ * request
+ * .post('/user', { name: 'tj' })
+ * .end(function(res){});
+ *
+ * Or further reduced to a single call for simple cases:
+ *
+ * request
+ * .post('/user', { name: 'tj' }, function(res){});
+ *
+ * @param {XMLHTTPRequest} xhr
+ * @param {Object} options
+ * @api private
+ */
+
+function Response(req, options) {
+ options = options || {};
+ this.req = req;
+ this.xhr = this.req.xhr;
+ this.text = this.req.method !='HEAD'
+ ? this.xhr.responseText
+ : null;
+ this.setStatusProperties(this.xhr.status);
+ this.header = this.headers = parseHeader(this.xhr.getAllResponseHeaders());
+ // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but
+ // getResponseHeader still works. so we get content-type even if getting
+ // other headers fails.
+ this.header['content-type'] = this.xhr.getResponseHeader('content-type');
+ this.setHeaderProperties(this.header);
+ this.body = this.req.method != 'HEAD'
+ ? this.parseBody(this.text)
+ : null;
+}
+
+/**
+ * Get case-insensitive `field` value.
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+Response.prototype.get = function(field){
+ return this.header[field.toLowerCase()];
+};
+
+/**
+ * Set header related properties:
+ *
+ * - `.type` the content type without params
+ *
+ * A response of "Content-Type: text/plain; charset=utf-8"
+ * will provide you with a `.type` of "text/plain".
+ *
+ * @param {Object} header
+ * @api private
+ */
+
+Response.prototype.setHeaderProperties = function(header){
+ // content-type
+ var ct = this.header['content-type'] || '';
+ this.type = type(ct);
+
+ // params
+ var obj = params(ct);
+ for (var key in obj) this[key] = obj[key];
+};
+
+/**
+ * Parse the given body `str`.
+ *
+ * Used for auto-parsing of bodies. Parsers
+ * are defined on the `superagent.parse` object.
+ *
+ * @param {String} str
+ * @return {Mixed}
+ * @api private
+ */
+
+Response.prototype.parseBody = function(str){
+ var parse = request.parse[this.type];
+ return parse && str && str.length
+ ? parse(str)
+ : null;
+};
+
+/**
+ * Set flags such as `.ok` based on `status`.
+ *
+ * For example a 2xx response will give you a `.ok` of __true__
+ * whereas 5xx will be __false__ and `.error` will be __true__. The
+ * `.clientError` and `.serverError` are also available to be more
+ * specific, and `.statusType` is the class of error ranging from 1..5
+ * sometimes useful for mapping respond colors etc.
+ *
+ * "sugar" properties are also defined for common cases. Currently providing:
+ *
+ * - .noContent
+ * - .badRequest
+ * - .unauthorized
+ * - .notAcceptable
+ * - .notFound
+ *
+ * @param {Number} status
+ * @api private
+ */
+
+Response.prototype.setStatusProperties = function(status){
+ var type = status / 100 | 0;
+
+ // status / class
+ this.status = status;
+ this.statusType = type;
+
+ // basics
+ this.info = 1 == type;
+ this.ok = 2 == type;
+ this.clientError = 4 == type;
+ this.serverError = 5 == type;
+ this.error = (4 == type || 5 == type)
+ ? this.toError()
+ : false;
+
+ // sugar
+ this.accepted = 202 == status;
+ this.noContent = 204 == status || 1223 == status;
+ this.badRequest = 400 == status;
+ this.unauthorized = 401 == status;
+ this.notAcceptable = 406 == status;
+ this.notFound = 404 == status;
+ this.forbidden = 403 == status;
+};
+
+/**
+ * Return an `Error` representative of this response.
+ *
+ * @return {Error}
+ * @api public
+ */
+
+Response.prototype.toError = function(){
+ var req = this.req;
+ var method = req.method;
+ var url = req.url;
+
+ var msg = 'cannot ' + method + ' ' + url + ' (' + this.status + ')';
+ var err = new Error(msg);
+ err.status = this.status;
+ err.method = method;
+ err.url = url;
+
+ return err;
+};
+
+/**
+ * Expose `Response`.
+ */
+
+request.Response = Response;
+
+/**
+ * Initialize a new `Request` with the given `method` and `url`.
+ *
+ * @param {String} method
+ * @param {String} url
+ * @api public
+ */
+
+function Request(method, url) {
+ var self = this;
+ Emitter.call(this);
+ this._query = this._query || [];
+ this.method = method;
+ this.url = url;
+ this.header = {};
+ this._header = {};
+ this.on('end', function(){
+ var err = null;
+ var res = null;
+
+ try {
+ res = new Response(self);
+ } catch(e) {
+ err = new Error('Parser is unable to parse the response');
+ err.parse = true;
+ err.original = e;
+ }
+
+ self.callback(err, res);
+ });
+}
+
+/**
+ * Mixin `Emitter`.
+ */
+
+Emitter(Request.prototype);
+
+/**
+ * Allow for extension
+ */
+
+Request.prototype.use = function(fn) {
+ fn(this);
+ return this;
+}
+
+/**
+ * Set timeout to `ms`.
+ *
+ * @param {Number} ms
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.timeout = function(ms){
+ this._timeout = ms;
+ return this;
+};
+
+/**
+ * Clear previous timeout.
+ *
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.clearTimeout = function(){
+ this._timeout = 0;
+ clearTimeout(this._timer);
+ return this;
+};
+
+/**
+ * Abort the request, and clear potential timeout.
+ *
+ * @return {Request}
+ * @api public
+ */
+
+Request.prototype.abort = function(){
+ if (this.aborted) return;
+ this.aborted = true;
+ this.xhr.abort();
+ this.clearTimeout();
+ this.emit('abort');
+ return this;
+};
+
+/**
+ * Set header `field` to `val`, or multiple fields with one object.
+ *
+ * Examples:
+ *
+ * req.get('/')
+ * .set('Accept', 'application/json')
+ * .set('X-API-Key', 'foobar')
+ * .end(callback);
+ *
+ * req.get('/')
+ * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
+ * .end(callback);
+ *
+ * @param {String|Object} field
+ * @param {String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.set = function(field, val){
+ if (isObject(field)) {
+ for (var key in field) {
+ this.set(key, field[key]);
+ }
+ return this;
+ }
+ this._header[field.toLowerCase()] = val;
+ this.header[field] = val;
+ return this;
+};
+
+/**
+ * Remove header `field`.
+ *
+ * Example:
+ *
+ * req.get('/')
+ * .unset('User-Agent')
+ * .end(callback);
+ *
+ * @param {String} field
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.unset = function(field){
+ delete this._header[field.toLowerCase()];
+ delete this.header[field];
+ return this;
+};
+
+/**
+ * Get case-insensitive header `field` value.
+ *
+ * @param {String} field
+ * @return {String}
+ * @api private
+ */
+
+Request.prototype.getHeader = function(field){
+ return this._header[field.toLowerCase()];
+};
+
+/**
+ * Set Content-Type to `type`, mapping values from `request.types`.
+ *
+ * Examples:
+ *
+ * superagent.types.xml = 'application/xml';
+ *
+ * request.post('/')
+ * .type('xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * request.post('/')
+ * .type('application/xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * @param {String} type
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.type = function(type){
+ this.set('Content-Type', request.types[type] || type);
+ return this;
+};
+
+/**
+ * Set Accept to `type`, mapping values from `request.types`.
+ *
+ * Examples:
+ *
+ * superagent.types.json = 'application/json';
+ *
+ * request.get('/agent')
+ * .accept('json')
+ * .end(callback);
+ *
+ * request.get('/agent')
+ * .accept('application/json')
+ * .end(callback);
+ *
+ * @param {String} accept
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.accept = function(type){
+ this.set('Accept', request.types[type] || type);
+ return this;
+};
+
+/**
+ * Set Authorization field value with `user` and `pass`.
+ *
+ * @param {String} user
+ * @param {String} pass
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.auth = function(user, pass){
+ var str = btoa(user + ':' + pass);
+ this.set('Authorization', 'Basic ' + str);
+ return this;
+};
+
+/**
+* Add query-string `val`.
+*
+* Examples:
+*
+* request.get('/shoes')
+* .query('size=10')
+* .query({ color: 'blue' })
+*
+* @param {Object|String} val
+* @return {Request} for chaining
+* @api public
+*/
+
+Request.prototype.query = function(val){
+ if ('string' != typeof val) val = serialize(val);
+ if (val) this._query.push(val);
+ return this;
+};
+
+/**
+ * Write the field `name` and `val` for "multipart/form-data"
+ * request bodies.
+ *
+ * ``` js
+ * request.post('/upload')
+ * .field('foo', 'bar')
+ * .end(callback);
+ * ```
+ *
+ * @param {String} name
+ * @param {String|Blob|File} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.field = function(name, val){
+ if (!this._formData) this._formData = new FormData();
+ this._formData.append(name, val);
+ return this;
+};
+
+/**
+ * Queue the given `file` as an attachment to the specified `field`,
+ * with optional `filename`.
+ *
+ * ``` js
+ * request.post('/upload')
+ * .attach(new Blob(['<a id="a"><b id="b">hey!</b></a>'], { type: "text/html"}))
+ * .end(callback);
+ * ```
+ *
+ * @param {String} field
+ * @param {Blob|File} file
+ * @param {String} filename
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.attach = function(field, file, filename){
+ if (!this._formData) this._formData = new FormData();
+ this._formData.append(field, file, filename);
+ return this;
+};
+
+/**
+ * Send `data`, defaulting the `.type()` to "json" when
+ * an object is given.
+ *
+ * Examples:
+ *
+ * // querystring
+ * request.get('/search')
+ * .end(callback)
+ *
+ * // multiple data "writes"
+ * request.get('/search')
+ * .send({ search: 'query' })
+ * .send({ range: '1..5' })
+ * .send({ order: 'desc' })
+ * .end(callback)
+ *
+ * // manual json
+ * request.post('/user')
+ * .type('json')
+ * .send('{"name":"tj"})
+ * .end(callback)
+ *
+ * // auto json
+ * request.post('/user')
+ * .send({ name: 'tj' })
+ * .end(callback)
+ *
+ * // manual x-www-form-urlencoded
+ * request.post('/user')
+ * .type('form')
+ * .send('name=tj')
+ * .end(callback)
+ *
+ * // auto x-www-form-urlencoded
+ * request.post('/user')
+ * .type('form')
+ * .send({ name: 'tj' })
+ * .end(callback)
+ *
+ * // defaults to x-www-form-urlencoded
+ * request.post('/user')
+ * .send('name=tobi')
+ * .send('species=ferret')
+ * .end(callback)
+ *
+ * @param {String|Object} data
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.send = function(data){
+ var obj = isObject(data);
+ var type = this.getHeader('Content-Type');
+
+ // merge
+ if (obj && isObject(this._data)) {
+ for (var key in data) {
+ this._data[key] = data[key];
+ }
+ } else if ('string' == typeof data) {
+ if (!type) this.type('form');
+ type = this.getHeader('Content-Type');
+ if ('application/x-www-form-urlencoded' == type) {
+ this._data = this._data
+ ? this._data + '&' + data
+ : data;
+ } else {
+ this._data = (this._data || '') + data;
+ }
+ } else {
+ this._data = data;
+ }
+
+ if (!obj) return this;
+ if (!type) this.type('json');
+ return this;
+};
+
+/**
+ * Invoke the callback with `err` and `res`
+ * and handle arity check.
+ *
+ * @param {Error} err
+ * @param {Response} res
+ * @api private
+ */
+
+Request.prototype.callback = function(err, res){
+ var fn = this._callback;
+ this.clearTimeout();
+ if (2 == fn.length) return fn(err, res);
+ if (err) return this.emit('error', err);
+ fn(res);
+};
+
+/**
+ * Invoke callback with x-domain error.
+ *
+ * @api private
+ */
+
+Request.prototype.crossDomainError = function(){
+ var err = new Error('Origin is not allowed by Access-Control-Allow-Origin');
+ err.crossDomain = true;
+ this.callback(err);
+};
+
+/**
+ * Invoke callback with timeout error.
+ *
+ * @api private
+ */
+
+Request.prototype.timeoutError = function(){
+ var timeout = this._timeout;
+ var err = new Error('timeout of ' + timeout + 'ms exceeded');
+ err.timeout = timeout;
+ this.callback(err);
+};
+
+/**
+ * Enable transmission of cookies with x-domain requests.
+ *
+ * Note that for this to work the origin must not be
+ * using "Access-Control-Allow-Origin" with a wildcard,
+ * and also must set "Access-Control-Allow-Credentials"
+ * to "true".
+ *
+ * @api public
+ */
+
+Request.prototype.withCredentials = function(){
+ this._withCredentials = true;
+ return this;
+};
+
+/**
+ * Initiate request, invoking callback `fn(res)`
+ * with an instanceof `Response`.
+ *
+ * @param {Function} fn
+ * @return {Request} for chaining
+ * @api public
+ */
+
+Request.prototype.end = function(fn){
+ var self = this;
+ var xhr = this.xhr = getXHR();
+ var query = this._query.join('&');
+ var timeout = this._timeout;
+ var data = this._formData || this._data;
+
+ // store callback
+ this._callback = fn || noop;
+
+ // state change
+ xhr.onreadystatechange = function(){
+ if (4 != xhr.readyState) return;
+ if (0 == xhr.status) {
+ if (self.aborted) return self.timeoutError();
+ return self.crossDomainError();
+ }
+ self.emit('end');
+ };
+
+ // progress
+ if (xhr.upload) {
+ xhr.upload.onprogress = function(e){
+ e.percent = e.loaded / e.total * 100;
+ self.emit('progress', e);
+ };
+ }
+
+ // timeout
+ if (timeout && !this._timer) {
+ this._timer = setTimeout(function(){
+ self.abort();
+ }, timeout);
+ }
+
+ // querystring
+ if (query) {
+ query = request.serializeObject(query);
+ this.url += ~this.url.indexOf('?')
+ ? '&' + query
+ : '?' + query;
+ }
+
+ // initiate request
+ xhr.open(this.method, this.url, true);
+
+ // CORS
+ if (this._withCredentials) xhr.withCredentials = true;
+
+ // body
+ if ('GET' != this.method && 'HEAD' != this.method && 'string' != typeof data && !isHost(data)) {
+ // serialize stuff
+ var serialize = request.serialize[this.getHeader('Content-Type')];
+ if (serialize) data = serialize(data);
+ }
+
+ // set header fields
+ for (var field in this.header) {
+ if (null == this.header[field]) continue;
+ xhr.setRequestHeader(field, this.header[field]);
+ }
+
+ // send stuff
+ this.emit('request', this);
+ xhr.send(data);
+ return this;
+};
+
+/**
+ * Expose `Request`.
+ */
+
+request.Request = Request;
+
+/**
+ * Issue a request:
+ *
+ * Examples:
+ *
+ * request('GET', '/users').end(callback)
+ * request('/users').end(callback)
+ * request('/users', callback)
+ *
+ * @param {String} method
+ * @param {String|Function} url or callback
+ * @return {Request}
+ * @api public
+ */
+
+function request(method, url) {
+ // callback
+ if ('function' == typeof url) {
+ return new Request('GET', method).end(url);
+ }
+
+ // url first
+ if (1 == arguments.length) {
+ return new Request('GET', method);
+ }
+
+ return new Request(method, url);
+}
+
+/**
+ * GET `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} data or fn
+ * @param {Function} fn
+ * @return {Request}
+ * @api public
+ */
+
+request.get = function(url, data, fn){
+ var req = request('GET', url);
+ if ('function' == typeof data) fn = data, data = null;
+ if (data) req.query(data);
+ if (fn) req.end(fn);
+ return req;
+};
+
+/**
+ * HEAD `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} data or fn
+ * @param {Function} fn
+ * @return {Request}
+ * @api public
+ */
+
+request.head = function(url, data, fn){
+ var req = request('HEAD', url);
+ if ('function' == typeof data) fn = data, data = null;
+ if (data) req.send(data);
+ if (fn) req.end(fn);
+ return req;
+};
+
+/**
+ * DELETE `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Function} fn
+ * @return {Request}
+ * @api public
+ */
+
+request.del = function(url, fn){
+ var req = request('DELETE', url);
+ if (fn) req.end(fn);
+ return req;
+};
+
+/**
+ * PATCH `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} data
+ * @param {Function} fn
+ * @return {Request}
+ * @api public
+ */
+
+request.patch = function(url, data, fn){
+ var req = request('PATCH', url);
+ if ('function' == typeof data) fn = data, data = null;
+ if (data) req.send(data);
+ if (fn) req.end(fn);
+ return req;
+};
+
+/**
+ * POST `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} data
+ * @param {Function} fn
+ * @return {Request}
+ * @api public
+ */
+
+request.post = function(url, data, fn){
+ var req = request('POST', url);
+ if ('function' == typeof data) fn = data, data = null;
+ if (data) req.send(data);
+ if (fn) req.end(fn);
+ return req;
+};
+
+/**
+ * PUT `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} data or fn
+ * @param {Function} fn
+ * @return {Request}
+ * @api public
+ */
+
+request.put = function(url, data, fn){
+ var req = request('PUT', url);
+ if ('function' == typeof data) fn = data, data = null;
+ if (data) req.send(data);
+ if (fn) req.end(fn);
+ return req;
+};
+
+/**
+ * Expose `request`.
+ */
+
+module.exports = request;
+
+},{"emitter":112,"reduce":113}],112:[function(require,module,exports){
+
+/**
+ * Expose `Emitter`.
+ */
+
+module.exports = Emitter;
+
+/**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+function Emitter(obj) {
+ if (obj) return mixin(obj);
+};
+
+/**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+function mixin(obj) {
+ for (var key in Emitter.prototype) {
+ obj[key] = Emitter.prototype[key];
+ }
+ return obj;
+}
+
+/**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.on =
+Emitter.prototype.addEventListener = function(event, fn){
+ this._callbacks = this._callbacks || {};
+ (this._callbacks[event] = this._callbacks[event] || [])
+ .push(fn);
+ return this;
+};
+
+/**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.once = function(event, fn){
+ var self = this;
+ this._callbacks = this._callbacks || {};
+
+ function on() {
+ self.off(event, on);
+ fn.apply(this, arguments);
+ }
+
+ on.fn = fn;
+ this.on(event, on);
+ return this;
+};
+
+/**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.off =
+Emitter.prototype.removeListener =
+Emitter.prototype.removeAllListeners =
+Emitter.prototype.removeEventListener = function(event, fn){
+ this._callbacks = this._callbacks || {};
+
+ // all
+ if (0 == arguments.length) {
+ this._callbacks = {};
+ return this;
+ }
+
+ // specific event
+ var callbacks = this._callbacks[event];
+ if (!callbacks) return this;
+
+ // remove all handlers
+ if (1 == arguments.length) {
+ delete this._callbacks[event];
+ return this;
+ }
+
+ // remove specific handler
+ var cb;
+ for (var i = 0; i < callbacks.length; i++) {
+ cb = callbacks[i];
+ if (cb === fn || cb.fn === fn) {
+ callbacks.splice(i, 1);
+ break;
+ }
+ }
+ return this;
+};
+
+/**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+Emitter.prototype.emit = function(event){
+ this._callbacks = this._callbacks || {};
+ var args = [].slice.call(arguments, 1)
+ , callbacks = this._callbacks[event];
+
+ if (callbacks) {
+ callbacks = callbacks.slice(0);
+ for (var i = 0, len = callbacks.length; i < len; ++i) {
+ callbacks[i].apply(this, args);
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+Emitter.prototype.listeners = function(event){
+ this._callbacks = this._callbacks || {};
+ return this._callbacks[event] || [];
+};
+
+/**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+Emitter.prototype.hasListeners = function(event){
+ return !! this.listeners(event).length;
+};
+
+},{}],113:[function(require,module,exports){
+
+/**
+ * Reduce `arr` with `fn`.
+ *
+ * @param {Array} arr
+ * @param {Function} fn
+ * @param {Mixed} initial
+ *
+ * TODO: combatible error handling?
+ */
+
+module.exports = function(arr, fn, initial){
+ var idx = 0;
+ var len = arr.length;
+ var curr = arguments.length == 3
+ ? initial
+ : arr[idx++];
+
+ while (idx < len) {
+ curr = fn.call(null, curr, arr[idx], ++idx, arr);
+ }
+
+ return curr;
+};
+},{}]},{},[1])(1)
+});
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJpbmRleC5qcyIsImxpYi9hdXRoLmpzIiwibGliL2NsaWVudC5qcyIsImxpYi9oZWxwZXJzLmpzIiwibGliL2h0dHAuanMiLCJsaWIvcmVzb2x2ZXIuanMiLCJsaWIvc3BlYy1jb252ZXJ0ZXIuanMiLCJsaWIvdHlwZXMvbW9kZWwuanMiLCJsaWIvdHlwZXMvb3BlcmF0aW9uLmpzIiwibGliL3R5cGVzL29wZXJhdGlvbkdyb3VwLmpzIiwibm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2J1ZmZlci9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9idWZmZXIvbm9kZV9tb2R1bGVzL2Jhc2U2NC1qcy9saWIvYjY0LmpzIiwibm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL2J1ZmZlci9ub2RlX21vZHVsZXMvaWVlZTc1NC9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9icm93c2VyaWZ5L25vZGVfbW9kdWxlcy9idWZmZXIvbm9kZV9tb2R1bGVzL2lzLWFycmF5L2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2Jyb3dzZXJpZnkvbm9kZV9tb2R1bGVzL3Byb2Nlc3MvYnJvd3Nlci5qcyIsIm5vZGVfbW9kdWxlcy9idG9hL2luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2Nvb2tpZWphci9jb29raWVqYXIuanMiLCJub2RlX21vZHVsZXMvanF1ZXJ5L2Rpc3QvanF1ZXJ5LmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvYXJyYXkvaW5kZXhPZi5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2NoYWluL2xvZGFzaC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2NvbGxlY3Rpb24vZmluZC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2NvbGxlY3Rpb24vZm9yRWFjaC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2NvbGxlY3Rpb24vbWFwLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvZGF0ZS9ub3cuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9mdW5jdGlvbi9iaW5kLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvZnVuY3Rpb24vcmVzdFBhcmFtLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvTGF6eVdyYXBwZXIuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9Mb2Rhc2hXcmFwcGVyLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYXJyYXlDb3B5LmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYXJyYXlFYWNoLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYXJyYXlNYXAuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlQ2FsbGJhY2suanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlQ2xvbmUuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlQ29weS5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VDcmVhdGUuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlRWFjaC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VGaW5kLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmFzZUZpbmRJbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VGb3IuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlRm9ySW4uanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlRm9yT3duLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmFzZUluZGV4T2YuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlSXNFcXVhbC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VJc0VxdWFsRGVlcC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VJc0Z1bmN0aW9uLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmFzZUlzTWF0Y2guanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iYXNlTG9kYXNoLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmFzZU1hcC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VNYXRjaGVzLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmFzZU1hdGNoZXNQcm9wZXJ0eS5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VQcm9wZXJ0eS5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2Jhc2VTZXREYXRhLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmFzZVRvU3RyaW5nLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmluYXJ5SW5kZXguanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9iaW5hcnlJbmRleEJ5LmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYmluZENhbGxiYWNrLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvYnVmZmVyQ2xvbmUuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9jb21wb3NlQXJncy5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2NvbXBvc2VBcmdzUmlnaHQuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9jcmVhdGVCYXNlRWFjaC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2NyZWF0ZUJhc2VGb3IuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9jcmVhdGVCaW5kV3JhcHBlci5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2NyZWF0ZUN0b3JXcmFwcGVyLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvY3JlYXRlRmluZC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2NyZWF0ZUZvckVhY2guanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9jcmVhdGVIeWJyaWRXcmFwcGVyLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvY3JlYXRlUGFydGlhbFdyYXBwZXIuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9jcmVhdGVXcmFwcGVyLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvZXF1YWxBcnJheXMuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9lcXVhbEJ5VGFnLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvZXF1YWxPYmplY3RzLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvZ2V0RGF0YS5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2dldEZ1bmNOYW1lLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvaW5kZXhPZk5hTi5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2luaXRDbG9uZUFycmF5LmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvaW5pdENsb25lQnlUYWcuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9pbml0Q2xvbmVPYmplY3QuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9pc0hvc3RPYmplY3QuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9pc0luZGV4LmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvaXNMYXppYWJsZS5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL2lzTGVuZ3RoLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvaXNPYmplY3RMaWtlLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvaXNTdHJpY3RDb21wYXJhYmxlLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvbWVyZ2VEYXRhLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvbWV0YU1hcC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL3JlYWxOYW1lcy5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL3Jlb3JkZXIuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9yZXBsYWNlSG9sZGVycy5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL3NldERhdGEuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC9zaGltSXNQbGFpbk9iamVjdC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2ludGVybmFsL3NoaW1LZXlzLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvaW50ZXJuYWwvdG9PYmplY3QuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9pbnRlcm5hbC93cmFwcGVyQ2xvbmUuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9sYW5nL2Nsb25lRGVlcC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2xhbmcvaXNBcmd1bWVudHMuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9sYW5nL2lzQXJyYXkuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9sYW5nL2lzRnVuY3Rpb24uanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9sYW5nL2lzTmF0aXZlLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvbGFuZy9pc09iamVjdC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2xhbmcvaXNQbGFpbk9iamVjdC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2xhbmcvaXNTdHJpbmcuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9sYW5nL2lzVHlwZWRBcnJheS5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L2xhbmcvaXNVbmRlZmluZWQuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9vYmplY3Qva2V5cy5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L29iamVjdC9rZXlzSW4uanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC9zdHJpbmcvZXNjYXBlUmVnRXhwLmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvc3VwcG9ydC5qcyIsIm5vZGVfbW9kdWxlcy9sb2Rhc2gtY29tcGF0L3V0aWxpdHkvY29uc3RhbnQuanMiLCJub2RlX21vZHVsZXMvbG9kYXNoLWNvbXBhdC91dGlsaXR5L2lkZW50aXR5LmpzIiwibm9kZV9tb2R1bGVzL2xvZGFzaC1jb21wYXQvdXRpbGl0eS9ub29wLmpzIiwibm9kZV9tb2R1bGVzL3N1cGVyYWdlbnQvbGliL2NsaWVudC5qcyIsIm5vZGVfbW9kdWxlcy9zdXBlcmFnZW50L25vZGVfbW9kdWxlcy9jb21wb25lbnQtZW1pdHRlci9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9zdXBlcmFnZW50L25vZGVfbW9kdWxlcy9yZWR1Y2UtY29tcG9uZW50L2luZGV4LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FDQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25DQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0hBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQ3pjQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDN0dBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xOQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDclNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeGpCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN3lCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbnpDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzVIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQzFEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7OztBQ2xCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcFFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDci9SQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDeEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN4REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbkNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcklBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQ3pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUN2QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN6QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMzQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDVkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZEE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2hCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDdkNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUN2REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDcENBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDM0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUN0QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQ3BCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7OztBQ2hIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDM0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN0RkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdERBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakRBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQzFCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUNoRkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNoQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDckJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3JCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDakJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNwQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDWkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDZkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUN6RkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUNUQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzdEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDNUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbEJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3REQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BEQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUN4Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7QUMxQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2REE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM1QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDL0RBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNuQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNyQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNqREE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQ2hDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7O0FDeExBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUN2QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ25CQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3pqQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3BLQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc30pIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgYXV0aCA9IHJlcXVpcmUoJy4vbGliL2F1dGgnKTtcbnZhciBoZWxwZXJzID0gcmVxdWlyZSgnLi9saWIvaGVscGVycycpO1xudmFyIFN3YWdnZXJDbGllbnQgPSByZXF1aXJlKCcuL2xpYi9jbGllbnQnKTtcbnZhciBkZXByZWNhdGlvbldyYXBwZXIgPSBmdW5jdGlvbiAodXJsLCBvcHRpb25zKSB7XG4gIGhlbHBlcnMubG9nKCdUaGlzIGlzIGRlcHJlY2F0ZWQsIHVzZSBcIm5ldyBTd2FnZ2VyQ2xpZW50XCIgaW5zdGVhZC4nKTtcblxuICByZXR1cm4gbmV3IFN3YWdnZXJDbGllbnQodXJsLCBvcHRpb25zKTtcbn07XG5cbi8qIEhlcmUgZm9yIElFOCBTdXBwb3J0ICovXG5pZiAoIUFycmF5LnByb3RvdHlwZS5pbmRleE9mKSB7XG4gIEFycmF5LnByb3RvdHlwZS5pbmRleE9mID0gZnVuY3Rpb24ob2JqLCBzdGFydCkge1xuICAgIGZvciAodmFyIGkgPSAoc3RhcnQgfHwgMCksIGogPSB0aGlzLmxlbmd0aDsgaSA8IGo7IGkrKykge1xuICAgICAgaWYgKHRoaXNbaV0gPT09IG9iaikgeyByZXR1cm4gaTsgfVxuICAgIH1cbiAgICByZXR1cm4gLTE7XG4gIH07XG59O1xuXG4vKiBIZXJlIGZvciBub2RlIDEwLnggc3VwcG9ydCAqL1xuaWYgKCFTdHJpbmcucHJvdG90eXBlLmVuZHNXaXRoKSB7XG4gIFN0cmluZy5wcm90b3R5cGUuZW5kc1dpdGggPSBmdW5jdGlvbihzdWZmaXgpIHtcbiAgICByZXR1cm4gdGhpcy5pbmRleE9mKHN1ZmZpeCwgdGhpcy5sZW5ndGggLSBzdWZmaXgubGVuZ3RoKSAhPT0gLTE7XG4gIH07XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFN3YWdnZXJDbGllbnQ7XG5cblN3YWdnZXJDbGllbnQuQXBpS2V5QXV0aG9yaXphdGlvbiA9IGF1dGguQXBpS2V5QXV0aG9yaXphdGlvbjtcblN3YWdnZXJDbGllbnQuUGFzc3dvcmRBdXRob3JpemF0aW9uID0gYXV0aC5QYXNzd29yZEF1dGhvcml6YXRpb247XG5Td2FnZ2VyQ2xpZW50LkNvb2tpZUF1dGhvcml6YXRpb24gPSBhdXRoLkNvb2tpZUF1dGhvcml6YXRpb247XG5Td2FnZ2VyQ2xpZW50LlN3YWdnZXJBcGkgPSBkZXByZWNhdGlvbldyYXBwZXI7XG5Td2FnZ2VyQ2xpZW50LlN3YWdnZXJDbGllbnQgPSBkZXByZWNhdGlvbldyYXBwZXI7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBidG9hID0gcmVxdWlyZSgnYnRvYScpOyAvLyBqc2hpbnQgaWdub3JlOmxpbmVcbnZhciBDb29raWVKYXIgPSByZXF1aXJlKCdjb29raWVqYXInKTtcblxuLyoqXG4gKiBTd2FnZ2VyQXV0aG9yaXphdGlvbnMgYXBwbHlzIHRoZSBjb3JyZWN0IGF1dGhvcml6YXRpb24gdG8gYW4gb3BlcmF0aW9uIGJlaW5nIGV4ZWN1dGVkXG4gKi9cbnZhciBTd2FnZ2VyQXV0aG9yaXphdGlvbnMgPSBtb2R1bGUuZXhwb3J0cy5Td2FnZ2VyQXV0aG9yaXphdGlvbnMgPSBmdW5jdGlvbiAoKSB7XG4gIHRoaXMuYXV0aHogPSB7fTtcbn07XG5cblN3YWdnZXJBdXRob3JpemF0aW9ucy5wcm90b3R5cGUuYWRkID0gZnVuY3Rpb24gKG5hbWUsIGF1dGgpIHtcbiAgdGhpcy5hdXRoeltuYW1lXSA9IGF1dGg7XG5cbiAgcmV0dXJuIGF1dGg7XG59O1xuXG5Td2FnZ2VyQXV0aG9yaXphdGlvbnMucHJvdG90eXBlLnJlbW92ZSA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gIHJldHVybiBkZWxldGUgdGhpcy5hdXRoeltuYW1lXTtcbn07XG5cblN3YWdnZXJBdXRob3JpemF0aW9ucy5wcm90b3R5cGUuYXBwbHkgPSBmdW5jdGlvbiAob2JqLCBhdXRob3JpemF0aW9ucykge1xuICB2YXIgc3RhdHVzID0gbnVsbDtcbiAgdmFyIGtleSwgbmFtZSwgdmFsdWUsIHJlc3VsdDtcblxuICAvLyBpZiB0aGUgJ2F1dGhvcml6YXRpb25zJyBrZXkgaXMgdW5kZWZpbmVkLCBvciBoYXMgYW4gZW1wdHkgYXJyYXksIGFkZCBhbGwga2V5c1xuICBpZiAodHlwZW9mIGF1dGhvcml6YXRpb25zID09PSAndW5kZWZpbmVkJyB8fCBPYmplY3Qua2V5cyhhdXRob3JpemF0aW9ucykubGVuZ3RoID09PSAwKSB7XG4gICAgZm9yIChrZXkgaW4gdGhpcy5hdXRoeikge1xuICAgICAgdmFsdWUgPSB0aGlzLmF1dGh6W2tleV07XG4gICAgICByZXN1bHQgPSB2YWx1ZS5hcHBseShvYmosIGF1dGhvcml6YXRpb25zKTtcblxuICAgICAgaWYgKHJlc3VsdCA9PT0gdHJ1ZSkge1xuICAgICAgICBzdGF0dXMgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyAyLjAgc3VwcG9ydFxuICAgIGlmIChBcnJheS5pc0FycmF5KGF1dGhvcml6YXRpb25zKSkge1xuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBhdXRob3JpemF0aW9ucy5sZW5ndGg7IGkrKykge1xuICAgICAgICB2YXIgYXV0aCA9IGF1dGhvcml6YXRpb25zW2ldO1xuXG4gICAgICAgIGZvciAobmFtZSBpbiBhdXRoKSB7XG4gICAgICAgICAgZm9yIChrZXkgaW4gdGhpcy5hdXRoeikge1xuICAgICAgICAgICAgaWYgKGtleSA9PT0gbmFtZSkge1xuICAgICAgICAgICAgICB2YWx1ZSA9IHRoaXMuYXV0aHpba2V5XTtcbiAgICAgICAgICAgICAgcmVzdWx0ID0gdmFsdWUuYXBwbHkob2JqLCBhdXRob3JpemF0aW9ucyk7XG5cbiAgICAgICAgICAgICAgaWYgKHJlc3VsdCA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICAgIHN0YXR1cyA9IHRydWU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gMS4yIHN1cHBvcnRcbiAgICAgIGZvciAobmFtZSBpbiBhdXRob3JpemF0aW9ucykge1xuICAgICAgICBmb3IgKGtleSBpbiB0aGlzLmF1dGh6KSB7XG4gICAgICAgICAgaWYgKGtleSA9PT0gbmFtZSkge1xuICAgICAgICAgICAgdmFsdWUgPSB0aGlzLmF1dGh6W2tleV07XG4gICAgICAgICAgICByZXN1bHQgPSB2YWx1ZS5hcHBseShvYmosIGF1dGhvcml6YXRpb25zKTtcblxuICAgICAgICAgICAgaWYgKHJlc3VsdCA9PT0gdHJ1ZSkge1xuICAgICAgICAgICAgICBzdGF0dXMgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzdGF0dXM7XG59O1xuXG4vKipcbiAqIEFwaUtleUF1dGhvcml6YXRpb24gYWxsb3dzIGEgcXVlcnkgcGFyYW0gb3IgaGVhZGVyIHRvIGJlIGluamVjdGVkXG4gKi9cbnZhciBBcGlLZXlBdXRob3JpemF0aW9uID0gbW9kdWxlLmV4cG9ydHMuQXBpS2V5QXV0aG9yaXphdGlvbiA9IGZ1bmN0aW9uIChuYW1lLCB2YWx1ZSwgdHlwZSkge1xuICB0aGlzLm5hbWUgPSBuYW1lO1xuICB0aGlzLnZhbHVlID0gdmFsdWU7XG4gIHRoaXMudHlwZSA9IHR5cGU7XG59O1xuXG5BcGlLZXlBdXRob3JpemF0aW9uLnByb3RvdHlwZS5hcHBseSA9IGZ1bmN0aW9uIChvYmopIHtcbiAgaWYgKHRoaXMudHlwZSA9PT0gJ3F1ZXJ5Jykge1xuICAgIGlmIChvYmoudXJsLmluZGV4T2YoJz8nKSA+IDApIHtcbiAgICAgIG9iai51cmwgPSBvYmoudXJsICsgJyYnICsgdGhpcy5uYW1lICsgJz0nICsgdGhpcy52YWx1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgb2JqLnVybCA9IG9iai51cmwgKyAnPycgKyB0aGlzLm5hbWUgKyAnPScgKyB0aGlzLnZhbHVlO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9IGVsc2UgaWYgKHRoaXMudHlwZSA9PT0gJ2hlYWRlcicpIHtcbiAgICBvYmouaGVhZGVyc1t0aGlzLm5hbWVdID0gdGhpcy52YWx1ZTtcblxuICAgIHJldHVybiB0cnVlO1xuICB9XG59O1xuXG52YXIgQ29va2llQXV0aG9yaXphdGlvbiA9IG1vZHVsZS5leHBvcnRzLkNvb2tpZUF1dGhvcml6YXRpb24gPSBmdW5jdGlvbiAoY29va2llKSB7XG4gIHRoaXMuY29va2llID0gY29va2llO1xufTtcblxuQ29va2llQXV0aG9yaXphdGlvbi5wcm90b3R5cGUuYXBwbHkgPSBmdW5jdGlvbiAob2JqKSB7XG4gIG9iai5jb29raWVKYXIgPSBvYmouY29va2llSmFyIHx8IG5ldyBDb29raWVKYXIoKTtcbiAgb2JqLmNvb2tpZUphci5zZXRDb29raWUodGhpcy5jb29raWUpO1xuXG4gIHJldHVybiB0cnVlO1xufTtcblxuLyoqXG4gKiBQYXNzd29yZCBBdXRob3JpemF0aW9uIGlzIGEgYmFzaWMgYXV0aCBpbXBsZW1lbnRhdGlvblxuICovXG52YXIgUGFzc3dvcmRBdXRob3JpemF0aW9uID0gbW9kdWxlLmV4cG9ydHMuUGFzc3dvcmRBdXRob3JpemF0aW9uID0gZnVuY3Rpb24gKG5hbWUsIHVzZXJuYW1lLCBwYXNzd29yZCkge1xuICB0aGlzLm5hbWUgPSBuYW1lO1xuICB0aGlzLnVzZXJuYW1lID0gdXNlcm5hbWU7XG4gIHRoaXMucGFzc3dvcmQgPSBwYXNzd29yZDtcbn07XG5cblBhc3N3b3JkQXV0aG9yaXphdGlvbi5wcm90b3R5cGUuYXBwbHkgPSBmdW5jdGlvbiAob2JqKSB7XG4gIG9iai5oZWFkZXJzLkF1dGhvcml6YXRpb24gPSAnQmFzaWMgJyArIGJ0b2EodGhpcy51c2VybmFtZSArICc6JyArIHRoaXMucGFzc3dvcmQpO1xuXG4gIHJldHVybiB0cnVlO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIF8gPSB7XG4gIGJpbmQ6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvZnVuY3Rpb24vYmluZCcpLFxuICBjbG9uZURlZXA6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvbGFuZy9jbG9uZURlZXAnKSxcbiAgZmluZDogcmVxdWlyZSgnbG9kYXNoLWNvbXBhdC9jb2xsZWN0aW9uL2ZpbmQnKSxcbiAgZm9yRWFjaDogcmVxdWlyZSgnbG9kYXNoLWNvbXBhdC9jb2xsZWN0aW9uL2ZvckVhY2gnKSxcbiAgaW5kZXhPZjogcmVxdWlyZSgnbG9kYXNoLWNvbXBhdC9hcnJheS9pbmRleE9mJyksXG4gIGlzQXJyYXk6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvbGFuZy9pc0FycmF5JyksXG4gIGlzRnVuY3Rpb246IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvbGFuZy9pc0Z1bmN0aW9uJyksXG4gIGlzUGxhaW5PYmplY3Q6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvbGFuZy9pc1BsYWluT2JqZWN0JyksXG4gIGlzVW5kZWZpbmVkOiByZXF1aXJlKCdsb2Rhc2gtY29tcGF0L2xhbmcvaXNVbmRlZmluZWQnKVxufTtcbnZhciBhdXRoID0gcmVxdWlyZSgnLi9hdXRoJyk7XG52YXIgaGVscGVycyA9IHJlcXVpcmUoJy4vaGVscGVycycpO1xudmFyIE1vZGVsID0gcmVxdWlyZSgnLi90eXBlcy9tb2RlbCcpO1xudmFyIE9wZXJhdGlvbiA9IHJlcXVpcmUoJy4vdHlwZXMvb3BlcmF0aW9uJyk7XG52YXIgT3BlcmF0aW9uR3JvdXAgPSByZXF1aXJlKCcuL3R5cGVzL29wZXJhdGlvbkdyb3VwJyk7XG52YXIgUmVzb2x2ZXIgPSByZXF1aXJlKCcuL3Jlc29sdmVyJyk7XG52YXIgU3dhZ2dlckh0dHAgPSByZXF1aXJlKCcuL2h0dHAnKTtcbnZhciBTd2FnZ2VyU3BlY0NvbnZlcnRlciA9IHJlcXVpcmUoJy4vc3BlYy1jb252ZXJ0ZXInKTtcblxuLy8gV2UgaGF2ZSB0byBrZWVwIHRyYWNrIG9mIHRoZSBmdW5jdGlvbi9wcm9wZXJ0eSBuYW1lcyB0byBhdm9pZCBjb2xsaXNpb25zIGZvciB0YWcgbmFtZXMgd2hpY2ggYXJlIHVzZWQgdG8gYWxsb3cgdGhlXG4vLyBmb2xsb3dpbmcgdXNhZ2U6ICdjbGllbnQue3RhZ05hbWV9J1xudmFyIHJlc2VydmVkQ2xpZW50VGFncyA9IFtcbiAgJ2F1dGhvcml6YXRpb25TY2hlbWUnLFxuICAnYXV0aG9yaXphdGlvbnMnLFxuICAnYmFzZVBhdGgnLFxuICAnYnVpbGQnLFxuICAnYnVpbGRGcm9tMV8xU3BlYycsXG4gICdidWlsZEZyb20xXzJTcGVjJyxcbiAgJ2J1aWxkRnJvbVNwZWMnLFxuICAnY2xpZW50QXV0aG9yaXphdGlvbnMnLFxuICAnY29udmVydEluZm8nLFxuICAnZGVidWcnLFxuICAnZGVmYXVsdEVycm9yQ2FsbGJhY2snLFxuICAnZGVmYXVsdFN1Y2Nlc3NDYWxsYmFjaycsXG4gICdmYWlsJyxcbiAgJ2ZhaWx1cmUnLFxuICAnZmluaXNoJyxcbiAgJ2hlbHAnLFxuICAnaWRGcm9tT3AnLFxuICAnaW5mbycsXG4gICdpbml0aWFsaXplJyxcbiAgJ2lzQnVpbHQnLFxuICAnaXNWYWxpZCcsXG4gICdtb2RlbHMnLFxuICAnbW9kZWxzQXJyYXknLFxuICAnb3B0aW9ucycsXG4gICdwYXJzZVVyaScsXG4gICdwcm9ncmVzcycsXG4gICdyZXNvdXJjZUNvdW50JyxcbiAgJ3NhbXBsZU1vZGVscycsXG4gICdzZWxmUmVmbGVjdCcsXG4gICdzZXRDb25zb2xpZGF0ZWRNb2RlbHMnLFxuICAnc3BlYycsXG4gICdzdXBwb3J0ZWRTdWJtaXRNZXRob2RzJyxcbiAgJ3N3YWdnZXJSZXF1ZXN0SGVhZGVycycsXG4gICd0YWdGcm9tTGFiZWwnLFxuICAndXJsJyxcbiAgJ3VzZUpRdWVyeSdcbl07XG4vLyBXZSBoYXZlIHRvIGtlZXAgdHJhY2sgb2YgdGhlIGZ1bmN0aW9uL3Byb3BlcnR5IG5hbWVzIHRvIGF2b2lkIGNvbGxpc2lvbnMgZm9yIHRhZyBuYW1lcyB3aGljaCBhcmUgdXNlZCB0byBhbGxvdyB0aGVcbi8vIGZvbGxvd2luZyB1c2FnZTogJ2NsaWVudC5hcGlzLnt0YWdOYW1lfSdcbnZhciByZXNlcnZlZEFwaVRhZ3MgPSBbXG4gICdhcGlzJyxcbiAgJ2FzQ3VybCcsXG4gICdkZXNjcmlwdGlvbicsXG4gICdleHRlcm5hbERvY3MnLFxuICAnaGVscCcsXG4gICdsYWJlbCcsXG4gICduYW1lJyxcbiAgJ29wZXJhdGlvbicsXG4gICdvcGVyYXRpb25zJyxcbiAgJ29wZXJhdGlvbnNBcnJheScsXG4gICdwYXRoJyxcbiAgJ3RhZydcbl07XG52YXIgc3VwcG9ydGVkT3BlcmF0aW9uTWV0aG9kcyA9IFsnZGVsZXRlJywgJ2dldCcsICdoZWFkJywgJ29wdGlvbnMnLCAncGF0Y2gnLCAncG9zdCcsICdwdXQnXTtcbnZhciBTd2FnZ2VyQ2xpZW50ID0gbW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAodXJsLCBvcHRpb25zKSB7XG4gIHRoaXMuYXV0aG9yaXphdGlvblNjaGVtZSA9IG51bGw7XG4gIHRoaXMuYXV0aG9yaXphdGlvbnMgPSBudWxsO1xuICB0aGlzLmJhc2VQYXRoID0gbnVsbDtcbiAgdGhpcy5kZWJ1ZyA9IGZhbHNlO1xuICB0aGlzLmluZm8gPSBudWxsO1xuICB0aGlzLmlzQnVpbHQgPSBmYWxzZTtcbiAgdGhpcy5pc1ZhbGlkID0gZmFsc2U7XG4gIHRoaXMubW9kZWxzQXJyYXkgPSBbXTtcbiAgdGhpcy5yZXNvdXJjZUNvdW50ID0gMDtcbiAgdGhpcy51cmwgPSBudWxsO1xuICB0aGlzLnVzZUpRdWVyeSA9IGZhbHNlO1xuXG4gIGlmICh0eXBlb2YgdXJsICE9PSAndW5kZWZpbmVkJykge1xuICAgIHJldHVybiB0aGlzLmluaXRpYWxpemUodXJsLCBvcHRpb25zKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxufTtcblxuU3dhZ2dlckNsaWVudC5wcm90b3R5cGUuaW5pdGlhbGl6ZSA9IGZ1bmN0aW9uICh1cmwsIG9wdGlvbnMpIHtcbiAgdGhpcy5tb2RlbHMgPSB7fTtcbiAgdGhpcy5zYW1wbGVNb2RlbHMgPSB7fTtcblxuICBvcHRpb25zID0gKG9wdGlvbnMgfHwge30pO1xuXG4gIGlmICh0eXBlb2YgdXJsID09PSAnc3RyaW5nJykge1xuICAgIHRoaXMudXJsID0gdXJsO1xuICB9IGVsc2UgaWYgKHR5cGVvZiB1cmwgPT09ICdvYmplY3QnKSB7XG4gICAgb3B0aW9ucyA9IHVybDtcbiAgICB0aGlzLnVybCA9IG9wdGlvbnMudXJsO1xuICB9XG5cbiAgdGhpcy5zd2FnZ2VyUmVxdWVzdEhlYWRlcnMgPSBvcHRpb25zLnN3YWdnZXJSZXF1ZXN0SGVhZGVycyB8fCAnYXBwbGljYXRpb24vanNvbjtjaGFyc2V0PXV0Zi04LCovKic7XG4gIHRoaXMuZGVmYXVsdFN1Y2Nlc3NDYWxsYmFjayA9IG9wdGlvbnMuZGVmYXVsdFN1Y2Nlc3NDYWxsYmFjayB8fCBudWxsO1xuICB0aGlzLmRlZmF1bHRFcnJvckNhbGxiYWNrID0gb3B0aW9ucy5kZWZhdWx0RXJyb3JDYWxsYmFjayB8fCBudWxsO1xuXG4gIGlmICh0eXBlb2Ygb3B0aW9ucy5zdWNjZXNzID09PSAnZnVuY3Rpb24nKSB7XG4gICAgdGhpcy5zdWNjZXNzID0gb3B0aW9ucy5zdWNjZXNzO1xuICB9XG5cbiAgaWYgKG9wdGlvbnMudXNlSlF1ZXJ5KSB7XG4gICAgdGhpcy51c2VKUXVlcnkgPSBvcHRpb25zLnVzZUpRdWVyeTtcbiAgfVxuXG4gIGlmIChvcHRpb25zLmF1dGhvcml6YXRpb25zKSB7XG4gICAgdGhpcy5jbGllbnRBdXRob3JpemF0aW9ucyA9IG9wdGlvbnMuYXV0aG9yaXphdGlvbnM7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5jbGllbnRBdXRob3JpemF0aW9ucyA9IG5ldyBhdXRoLlN3YWdnZXJBdXRob3JpemF0aW9ucygpO1xuICB9XG5cbiAgdGhpcy5zdXBwb3J0ZWRTdWJtaXRNZXRob2RzID0gb3B0aW9ucy5zdXBwb3J0ZWRTdWJtaXRNZXRob2RzIHx8IFtdO1xuICB0aGlzLmZhaWx1cmUgPSBvcHRpb25zLmZhaWx1cmUgfHwgZnVuY3Rpb24gKCkge307XG4gIHRoaXMucHJvZ3Jlc3MgPSBvcHRpb25zLnByb2dyZXNzIHx8IGZ1bmN0aW9uICgpIHt9O1xuICB0aGlzLnNwZWMgPSBfLmNsb25lRGVlcChvcHRpb25zLnNwZWMpOyAvLyBDbG9uZSBzbyB3ZSBkbyBub3QgYWx0ZXIgdGhlIHByb3ZpZGVkIGRvY3VtZW50XG4gIHRoaXMub3B0aW9ucyA9IG9wdGlvbnM7XG5cbiAgaWYgKHR5cGVvZiBvcHRpb25zLnN1Y2Nlc3MgPT09ICdmdW5jdGlvbicpIHtcbiAgICB0aGlzLnJlYWR5ID0gdHJ1ZTtcbiAgICB0aGlzLmJ1aWxkKCk7XG4gIH1cbn07XG5cblN3YWdnZXJDbGllbnQucHJvdG90eXBlLmJ1aWxkID0gZnVuY3Rpb24gKG1vY2spIHtcbiAgaWYgKHRoaXMuaXNCdWlsdCkge1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gIHRoaXMucHJvZ3Jlc3MoJ2ZldGNoaW5nIHJlc291cmNlIGxpc3Q6ICcgKyB0aGlzLnVybCk7XG5cbiAgdmFyIG9iaiA9IHtcbiAgICB1c2VKUXVlcnk6IHRoaXMudXNlSlF1ZXJ5LFxuICAgIHVybDogdGhpcy51cmwsXG4gICAgbWV0aG9kOiAnZ2V0JyxcbiAgICBoZWFkZXJzOiB7XG4gICAgICBhY2NlcHQ6IHRoaXMuc3dhZ2dlclJlcXVlc3RIZWFkZXJzXG4gICAgfSxcbiAgICBvbjoge1xuICAgICAgZXJyb3I6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICBpZiAoc2VsZi51cmwuc3Vic3RyaW5nKDAsIDQpICE9PSAnaHR0cCcpIHtcbiAgICAgICAgICByZXR1cm4gc2VsZi5mYWlsKCdQbGVhc2Ugc3BlY2lmeSB0aGUgcHJvdG9jb2wgZm9yICcgKyBzZWxmLnVybCk7XG4gICAgICAgIH0gZWxzZSBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSAwKSB7XG4gICAgICAgICAgcmV0dXJuIHNlbGYuZmFpbCgnQ2FuXFwndCByZWFkIGZyb20gc2VydmVyLiAgSXQgbWF5IG5vdCBoYXZlIHRoZSBhcHByb3ByaWF0ZSBhY2Nlc3MtY29udHJvbC1vcmlnaW4gc2V0dGluZ3MuJyk7XG4gICAgICAgIH0gZWxzZSBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDQpIHtcbiAgICAgICAgICByZXR1cm4gc2VsZi5mYWlsKCdDYW5cXCd0IHJlYWQgc3dhZ2dlciBKU09OIGZyb20gJyArIHNlbGYudXJsKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gc2VsZi5mYWlsKHJlc3BvbnNlLnN0YXR1cyArICcgOiAnICsgcmVzcG9uc2Uuc3RhdHVzVGV4dCArICcgJyArIHNlbGYudXJsKTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHJlc3BvbnNlOiBmdW5jdGlvbiAocmVzcCkge1xuICAgICAgICB2YXIgcmVzcG9uc2VPYmogPSByZXNwLm9iaiB8fCBKU09OLnBhcnNlKHJlc3AuZGF0YSk7XG4gICAgICAgIHNlbGYuc3dhZ2dlclZlcnNpb24gPSByZXNwb25zZU9iai5zd2FnZ2VyVmVyc2lvbjtcblxuICAgICAgICBpZiAocmVzcG9uc2VPYmouc3dhZ2dlciAmJiBwYXJzZUludChyZXNwb25zZU9iai5zd2FnZ2VyKSA9PT0gMikge1xuICAgICAgICAgIHNlbGYuc3dhZ2dlclZlcnNpb24gPSByZXNwb25zZU9iai5zd2FnZ2VyO1xuXG4gICAgICAgICAgbmV3IFJlc29sdmVyKCkucmVzb2x2ZShyZXNwb25zZU9iaiwgc2VsZi5idWlsZEZyb21TcGVjLCBzZWxmKTtcblxuICAgICAgICAgIHNlbGYuaXNWYWxpZCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdmFyIGNvbnZlcnRlciA9IG5ldyBTd2FnZ2VyU3BlY0NvbnZlcnRlcigpO1xuICAgICAgICAgIGNvbnZlcnRlci5zZXREb2N1bWVudGF0aW9uTG9jYXRpb24oc2VsZi51cmwpO1xuICAgICAgICAgIGNvbnZlcnRlci5jb252ZXJ0KHJlc3BvbnNlT2JqLCBmdW5jdGlvbihzcGVjKSB7XG4gICAgICAgICAgICBuZXcgUmVzb2x2ZXIoKS5yZXNvbHZlKHNwZWMsIHNlbGYuYnVpbGRGcm9tU3BlYywgc2VsZik7XG4gICAgICAgICAgICBzZWxmLmlzVmFsaWQgPSB0cnVlO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIGlmICh0aGlzLnNwZWMpIHtcbiAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcbiAgICAgIG5ldyBSZXNvbHZlcigpLnJlc29sdmUoc2VsZi5zcGVjLCBzZWxmLmJ1aWxkRnJvbVNwZWMsIHNlbGYpO1xuICAgfSwgMTApO1xuICB9IGVsc2Uge1xuICAgIHRoaXMuY2xpZW50QXV0aG9yaXphdGlvbnMuYXBwbHkob2JqKTtcblxuICAgIGlmIChtb2NrKSB7XG4gICAgICByZXR1cm4gb2JqO1xuICAgIH1cblxuICAgIG5ldyBTd2FnZ2VySHR0cCgpLmV4ZWN1dGUob2JqKTtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuU3dhZ2dlckNsaWVudC5wcm90b3R5cGUuYnVpbGRGcm9tU3BlYyA9IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICBpZiAodGhpcy5pc0J1aWx0KSB7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICB0aGlzLmFwaXMgPSB7fTtcbiAgdGhpcy5hcGlzQXJyYXkgPSBbXTtcbiAgdGhpcy5iYXNlUGF0aCA9IHJlc3BvbnNlLmJhc2VQYXRoIHx8ICcnO1xuICB0aGlzLmNvbnN1bWVzID0gcmVzcG9uc2UuY29uc3VtZXM7XG4gIHRoaXMuaG9zdCA9IHJlc3BvbnNlLmhvc3QgfHwgJyc7XG4gIHRoaXMuaW5mbyA9IHJlc3BvbnNlLmluZm8gfHwge307XG4gIHRoaXMucHJvZHVjZXMgPSByZXNwb25zZS5wcm9kdWNlcztcbiAgdGhpcy5zY2hlbWVzID0gcmVzcG9uc2Uuc2NoZW1lcyB8fCBbXTtcbiAgdGhpcy5zZWN1cml0eURlZmluaXRpb25zID0gcmVzcG9uc2Uuc2VjdXJpdHlEZWZpbml0aW9ucztcbiAgdGhpcy50aXRsZSA9IHJlc3BvbnNlLnRpdGxlIHx8ICcnO1xuXG4gIGlmIChyZXNwb25zZS5leHRlcm5hbERvY3MpIHtcbiAgICB0aGlzLmV4dGVybmFsRG9jcyA9IHJlc3BvbnNlLmV4dGVybmFsRG9jcztcbiAgfVxuXG4gIC8vIGxlZ2FjeSBzdXBwb3J0XG4gIHRoaXMuYXV0aFNjaGVtZXMgPSByZXNwb25zZS5zZWN1cml0eURlZmluaXRpb25zO1xuXG4gIHZhciBkZWZpbmVkVGFncyA9IHt9O1xuICB2YXIgaztcblxuICBpZiAoQXJyYXkuaXNBcnJheShyZXNwb25zZS50YWdzKSkge1xuICAgIGRlZmluZWRUYWdzID0ge307XG5cbiAgICBmb3IgKGsgPSAwOyBrIDwgcmVzcG9uc2UudGFncy5sZW5ndGg7IGsrKykge1xuICAgICAgdmFyIHQgPSByZXNwb25zZS50YWdzW2tdO1xuXG4gICAgICBkZWZpbmVkVGFnc1t0Lm5hbWVdID0gdDtcbiAgICB9XG4gIH1cblxuICB2YXIgbG9jYXRpb247XG5cbiAgaWYgKHR5cGVvZiB0aGlzLnVybCA9PT0gJ3N0cmluZycpIHtcbiAgICBsb2NhdGlvbiA9IHRoaXMucGFyc2VVcmkodGhpcy51cmwpO1xuICB9XG5cbiAgaWYgKHR5cGVvZiB0aGlzLnNjaGVtZXMgPT09ICd1bmRlZmluZWQnIHx8IHRoaXMuc2NoZW1lcy5sZW5ndGggPT09IDApIHtcbiAgICB0aGlzLnNjaGVtZSA9IGxvY2F0aW9uLnNjaGVtZSB8fCAnaHR0cCc7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy5zY2hlbWUgPSB0aGlzLnNjaGVtZXNbMF07XG4gIH1cblxuICBpZiAodHlwZW9mIHRoaXMuaG9zdCA9PT0gJ3VuZGVmaW5lZCcgfHwgdGhpcy5ob3N0ID09PSAnJykge1xuICAgIHRoaXMuaG9zdCA9IGxvY2F0aW9uLmhvc3Q7XG5cbiAgICBpZiAobG9jYXRpb24ucG9ydCkge1xuICAgICAgdGhpcy5ob3N0ID0gdGhpcy5ob3N0ICsgJzonICsgbG9jYXRpb24ucG9ydDtcbiAgICB9XG4gIH1cblxuICB0aGlzLmRlZmluaXRpb25zID0gcmVzcG9uc2UuZGVmaW5pdGlvbnM7XG5cbiAgdmFyIGtleTtcblxuICBmb3IgKGtleSBpbiB0aGlzLmRlZmluaXRpb25zKSB7XG4gICAgdmFyIG1vZGVsID0gbmV3IE1vZGVsKGtleSwgdGhpcy5kZWZpbml0aW9uc1trZXldLCB0aGlzLm1vZGVscyk7XG5cbiAgICBpZiAobW9kZWwpIHtcbiAgICAgIHRoaXMubW9kZWxzW2tleV0gPSBtb2RlbDtcbiAgICB9XG4gIH1cblxuICAvLyBnZXQgcGF0aHMsIGNyZWF0ZSBmdW5jdGlvbnMgZm9yIGVhY2ggb3BlcmF0aW9uSWRcbiAgdmFyIHNlbGYgPSB0aGlzO1xuXG4gIC8vIEJpbmQgaGVscCB0byAnY2xpZW50LmFwaXMnXG4gIHNlbGYuYXBpcy5oZWxwID0gXy5iaW5kKHNlbGYuaGVscCwgc2VsZik7XG5cbiAgXy5mb3JFYWNoKHJlc3BvbnNlLnBhdGhzLCBmdW5jdGlvbiAocGF0aE9iaiwgcGF0aCkge1xuICAgIC8vIE9ubHkgcHJvY2VzcyBhIHBhdGggaWYgaXQncyBhbiBvYmplY3RcbiAgICBpZiAoIV8uaXNQbGFpbk9iamVjdChwYXRoT2JqKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIF8uZm9yRWFjaChzdXBwb3J0ZWRPcGVyYXRpb25NZXRob2RzLCBmdW5jdGlvbiAobWV0aG9kKSB7XG4gICAgICB2YXIgb3BlcmF0aW9uID0gcGF0aE9ialttZXRob2RdO1xuXG4gICAgICBpZiAoXy5pc1VuZGVmaW5lZChvcGVyYXRpb24pKSB7XG4gICAgICAgIC8vIE9wZXJhdGlvbiBkb2VzIG5vdCBleGlzdFxuICAgICAgICByZXR1cm47XG4gICAgICB9IGVsc2UgaWYgKCFfLmlzUGxhaW5PYmplY3Qob3BlcmF0aW9uKSkge1xuICAgICAgICAvLyBPcGVyYXRpb24gZXhpc3RzIGJ1dCBpdCBpcyBub3QgYW4gT3BlcmF0aW9uIE9iamVjdC4gIFNpbmNlIHRoaXMgaXMgaW52YWxpZCwgbG9nIGl0LlxuICAgICAgICBoZWxwZXJzLmxvZygnVGhlIFxcJycgKyBtZXRob2QgKyAnXFwnIG9wZXJhdGlvbiBmb3IgXFwnJyArIHBhdGggKyAnXFwnIHBhdGggaXMgbm90IGFuIE9wZXJhdGlvbiBPYmplY3QnKTtcblxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBcbiAgICAgIHZhciB0YWdzID0gb3BlcmF0aW9uLnRhZ3M7XG5cbiAgICAgIGlmIChfLmlzVW5kZWZpbmVkKHRhZ3MpIHx8ICFfLmlzQXJyYXkodGFncykgfHwgdGFncy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGFncyA9IG9wZXJhdGlvbi50YWdzID0gWyAnZGVmYXVsdCcgXTtcbiAgICAgIH1cblxuICAgICAgdmFyIG9wZXJhdGlvbklkID0gc2VsZi5pZEZyb21PcChwYXRoLCBtZXRob2QsIG9wZXJhdGlvbik7XG4gICAgICB2YXIgb3BlcmF0aW9uT2JqZWN0ID0gbmV3IE9wZXJhdGlvbihzZWxmLCBvcGVyYXRpb24uc2NoZW1lLCBvcGVyYXRpb25JZCwgbWV0aG9kLCBwYXRoLCBvcGVyYXRpb24sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxmLmRlZmluaXRpb25zLCBzZWxmLm1vZGVscywgc2VsZi5jbGllbnRBdXRob3JpemF0aW9ucyk7XG5cbiAgICAgIC8vIGJpbmQgc2VsZiBvcGVyYXRpb24ncyBleGVjdXRlIGNvbW1hbmQgdG8gdGhlIGFwaVxuICAgICAgXy5mb3JFYWNoKHRhZ3MsIGZ1bmN0aW9uICh0YWcpIHtcbiAgICAgICAgdmFyIGNsaWVudFByb3BlcnR5ID0gXy5pbmRleE9mKHJlc2VydmVkQ2xpZW50VGFncywgdGFnKSA+IC0xID8gJ18nICsgdGFnIDogdGFnO1xuICAgICAgICB2YXIgYXBpUHJvcGVydHkgPSBfLmluZGV4T2YocmVzZXJ2ZWRBcGlUYWdzLCB0YWcpID4gLTEgPyAnXycgKyB0YWcgOiB0YWc7XG4gICAgICAgIHZhciBvcGVyYXRpb25Hcm91cCA9IHNlbGZbY2xpZW50UHJvcGVydHldO1xuXG4gICAgICAgIGlmIChjbGllbnRQcm9wZXJ0eSAhPT0gdGFnKSB7XG4gICAgICAgICAgaGVscGVycy5sb2coJ1RoZSBcXCcnICsgdGFnICsgJ1xcJyB0YWcgY29uZmxpY3RzIHdpdGggYSBTd2FnZ2VyQ2xpZW50IGZ1bmN0aW9uL3Byb3BlcnR5IG5hbWUuICBVc2UgXFwnY2xpZW50LicgK1xuICAgICAgICAgICAgICAgICAgICAgIGNsaWVudFByb3BlcnR5ICsgJ1xcJyBvciBcXCdjbGllbnQuYXBpcy4nICsgdGFnICsgJ1xcJyBpbnN0ZWFkIG9mIFxcJ2NsaWVudC4nICsgdGFnICsgJ1xcJy4nKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChhcGlQcm9wZXJ0eSAhPT0gdGFnKSB7XG4gICAgICAgICAgaGVscGVycy5sb2coJ1RoZSBcXCcnICsgdGFnICsgJ1xcJyB0YWcgY29uZmxpY3RzIHdpdGggYSBTd2FnZ2VyQ2xpZW50IG9wZXJhdGlvbiBmdW5jdGlvbi9wcm9wZXJ0eSBuYW1lLiAgVXNlICcgK1xuICAgICAgICAgICAgICAgICAgICAgICdcXCdjbGllbnQuYXBpcy4nICsgYXBpUHJvcGVydHkgKyAnXFwnIGluc3RlYWQgb2YgXFwnY2xpZW50LmFwaXMuJyArIHRhZyArICdcXCcuJyk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoXy5pbmRleE9mKHJlc2VydmVkQXBpVGFncywgb3BlcmF0aW9uSWQpID4gLTEpIHtcbiAgICAgICAgICBoZWxwZXJzLmxvZygnVGhlIFxcJycgKyBvcGVyYXRpb25JZCArICdcXCcgb3BlcmF0aW9uSWQgY29uZmxpY3RzIHdpdGggYSBTd2FnZ2VyQ2xpZW50IG9wZXJhdGlvbiAnICtcbiAgICAgICAgICAgICAgICAgICAgICAnZnVuY3Rpb24vcHJvcGVydHkgbmFtZS4gIFVzZSBcXCdjbGllbnQuYXBpcy4nICsgYXBpUHJvcGVydHkgKyAnLl8nICsgb3BlcmF0aW9uSWQgK1xuICAgICAgICAgICAgICAgICAgICAgICdcXCcgaW5zdGVhZCBvZiBcXCdjbGllbnQuYXBpcy4nICsgYXBpUHJvcGVydHkgKyAnLicgKyBvcGVyYXRpb25JZCArICdcXCcuJyk7XG5cbiAgICAgICAgICBvcGVyYXRpb25JZCA9ICdfJyArIG9wZXJhdGlvbklkO1xuICAgICAgICAgIG9wZXJhdGlvbk9iamVjdC5uaWNrbmFtZSA9IG9wZXJhdGlvbklkOyAvLyBTbyAnY2xpZW50LmFwaXMuW3RhZ10ub3BlcmF0aW9uSWQuaGVscCgpIHdvcmtzIHByb3Blcmx5XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoXy5pc1VuZGVmaW5lZChvcGVyYXRpb25Hcm91cCkpIHtcbiAgICAgICAgICBvcGVyYXRpb25Hcm91cCA9IHNlbGZbY2xpZW50UHJvcGVydHldID0gc2VsZi5hcGlzW2FwaVByb3BlcnR5XSA9IHt9O1xuXG4gICAgICAgICAgb3BlcmF0aW9uR3JvdXAub3BlcmF0aW9ucyA9IHt9O1xuICAgICAgICAgIG9wZXJhdGlvbkdyb3VwLmxhYmVsID0gYXBpUHJvcGVydHk7XG4gICAgICAgICAgb3BlcmF0aW9uR3JvdXAuYXBpcyA9IHt9O1xuXG4gICAgICAgICAgdmFyIHRhZ0RlZiA9IGRlZmluZWRUYWdzW3RhZ107XG5cbiAgICAgICAgICBpZiAoIV8uaXNVbmRlZmluZWQodGFnRGVmKSkge1xuICAgICAgICAgICAgb3BlcmF0aW9uR3JvdXAuZGVzY3JpcHRpb24gPSB0YWdEZWYuZGVzY3JpcHRpb247XG4gICAgICAgICAgICBvcGVyYXRpb25Hcm91cC5leHRlcm5hbERvY3MgPSB0YWdEZWYuZXh0ZXJuYWxEb2NzO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHNlbGZbY2xpZW50UHJvcGVydHldLmhlbHAgPSBfLmJpbmQoc2VsZi5oZWxwLCBvcGVyYXRpb25Hcm91cCk7XG4gICAgICAgICAgc2VsZi5hcGlzQXJyYXkucHVzaChuZXcgT3BlcmF0aW9uR3JvdXAodGFnLCBvcGVyYXRpb25Hcm91cC5kZXNjcmlwdGlvbiwgb3BlcmF0aW9uR3JvdXAuZXh0ZXJuYWxEb2NzLCBvcGVyYXRpb25PYmplY3QpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEJpbmQgdGFnIGhlbHBcbiAgICAgICAgaWYgKCFfLmlzRnVuY3Rpb24ob3BlcmF0aW9uR3JvdXAuaGVscCkpIHtcbiAgICAgICAgICBvcGVyYXRpb25Hcm91cC5oZWxwID0gXy5iaW5kKHNlbGYuaGVscCwgb3BlcmF0aW9uR3JvdXApO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gYmluZCB0byB0aGUgYXBpcyBvYmplY3RcbiAgICAgICAgc2VsZi5hcGlzW2FwaVByb3BlcnR5XVtvcGVyYXRpb25JZF0gPSBvcGVyYXRpb25Hcm91cFtvcGVyYXRpb25JZF09IF8uYmluZChvcGVyYXRpb25PYmplY3QuZXhlY3V0ZSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcGVyYXRpb25PYmplY3QpO1xuICAgICAgICBzZWxmLmFwaXNbYXBpUHJvcGVydHldW29wZXJhdGlvbklkXS5oZWxwID0gb3BlcmF0aW9uR3JvdXBbb3BlcmF0aW9uSWRdLmhlbHAgPSBfLmJpbmQob3BlcmF0aW9uT2JqZWN0LmhlbHAsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcGVyYXRpb25PYmplY3QpO1xuICAgICAgICBzZWxmLmFwaXNbYXBpUHJvcGVydHldW29wZXJhdGlvbklkXS5hc0N1cmwgPSBvcGVyYXRpb25Hcm91cFtvcGVyYXRpb25JZF0uYXNDdXJsID0gXy5iaW5kKG9wZXJhdGlvbk9iamVjdC5hc0N1cmwsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3BlcmF0aW9uT2JqZWN0KTtcblxuICAgICAgICBvcGVyYXRpb25Hcm91cC5hcGlzW29wZXJhdGlvbklkXSA9IG9wZXJhdGlvbkdyb3VwLm9wZXJhdGlvbnNbb3BlcmF0aW9uSWRdID0gb3BlcmF0aW9uT2JqZWN0O1xuXG4gICAgICAgIC8vIGxlZ2FjeSBVSSBmZWF0dXJlXG4gICAgICAgIHZhciBhcGkgPSBfLmZpbmQoc2VsZi5hcGlzQXJyYXksIGZ1bmN0aW9uIChhcGkpIHtcbiAgICAgICAgICByZXR1cm4gYXBpLnRhZyA9PT0gdGFnO1xuICAgICAgICB9KTtcblxuICAgICAgICBpZiAoYXBpKSB7XG4gICAgICAgICAgYXBpLm9wZXJhdGlvbnNBcnJheS5wdXNoKG9wZXJhdGlvbk9iamVjdCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICB0aGlzLmlzQnVpbHQgPSB0cnVlO1xuXG4gIGlmICh0aGlzLnN1Y2Nlc3MpIHtcbiAgICB0aGlzLmlzVmFsaWQgPSB0cnVlO1xuICAgIHRoaXMuaXNCdWlsdCA9IHRydWU7XG4gICAgdGhpcy5zdWNjZXNzKCk7XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cblN3YWdnZXJDbGllbnQucHJvdG90eXBlLnBhcnNlVXJpID0gZnVuY3Rpb24gKHVyaSkge1xuICB2YXIgdXJsUGFyc2VSRSA9IC9eKCgoKFteOlxcLyNcXD9dKzopPyg/OihcXC9cXC8pKCg/OigoW146QFxcLyNcXD9dKykoPzpcXDooW146QFxcLyNcXD9dKykpPylAKT8oKFteOlxcLyNcXD9cXF1cXFtdK3xcXFtbXlxcL1xcXUAjP10rXFxdKSg/OlxcOihbMC05XSspKT8pKT8pPyk/KChcXC8/KD86W15cXC9cXD8jXStcXC8rKSopKFteXFw/I10qKSkpPyhcXD9bXiNdKyk/KSgjLiopPy87XG4gIHZhciBwYXJ0cyA9IHVybFBhcnNlUkUuZXhlYyh1cmkpO1xuXG4gIHJldHVybiB7XG4gICAgc2NoZW1lOiBwYXJ0c1s0XS5yZXBsYWNlKCc6JywnJyksXG4gICAgaG9zdDogcGFydHNbMTFdLFxuICAgIHBvcnQ6IHBhcnRzWzEyXSxcbiAgICBwYXRoOiBwYXJ0c1sxNV1cbiAgfTtcbn07XG5cblN3YWdnZXJDbGllbnQucHJvdG90eXBlLmhlbHAgPSBmdW5jdGlvbiAoZG9udFByaW50KSB7XG4gIHZhciBvdXRwdXQgPSAnJztcblxuICBpZiAodGhpcyBpbnN0YW5jZW9mIFN3YWdnZXJDbGllbnQpIHtcbiAgICBfLmZvckVhY2godGhpcy5hcGlzLCBmdW5jdGlvbiAoYXBpLCBuYW1lKSB7XG4gICAgICBpZiAoXy5pc1BsYWluT2JqZWN0KGFwaSkpIHtcbiAgICAgICAgb3V0cHV0ICs9ICdvcGVyYXRpb25zIGZvciB0aGUgXFwnJyArIG5hbWUgKyAnXFwnIHRhZ1xcbic7XG5cbiAgICAgICAgXy5mb3JFYWNoKGFwaS5vcGVyYXRpb25zLCBmdW5jdGlvbiAob3BlcmF0aW9uLCBuYW1lKSB7XG4gICAgICAgICAgb3V0cHV0ICs9ICcgICogJyArIG5hbWUgKyAnOiAnICsgb3BlcmF0aW9uLnN1bW1hcnkgKyAnXFxuJztcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0gZWxzZSBpZiAodGhpcyBpbnN0YW5jZW9mIE9wZXJhdGlvbkdyb3VwIHx8IF8uaXNQbGFpbk9iamVjdCh0aGlzKSkge1xuICAgIG91dHB1dCArPSAnb3BlcmF0aW9ucyBmb3IgdGhlIFxcJycgKyB0aGlzLmxhYmVsICsgJ1xcJyB0YWdcXG4nO1xuXG4gICAgXy5mb3JFYWNoKHRoaXMuYXBpcywgZnVuY3Rpb24gKG9wZXJhdGlvbiwgbmFtZSkge1xuICAgICAgb3V0cHV0ICs9ICcgICogJyArIG5hbWUgKyAnOiAnICsgb3BlcmF0aW9uLnN1bW1hcnkgKyAnXFxuJztcbiAgICB9KTtcbiAgfVxuXG4gIGlmIChkb250UHJpbnQpIHtcbiAgICByZXR1cm4gb3V0cHV0O1xuICB9IGVsc2Uge1xuICAgIGhlbHBlcnMubG9nKG91dHB1dCk7XG5cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9XG59O1xuXG5Td2FnZ2VyQ2xpZW50LnByb3RvdHlwZS50YWdGcm9tTGFiZWwgPSBmdW5jdGlvbiAobGFiZWwpIHtcbiAgcmV0dXJuIGxhYmVsO1xufTtcblxuU3dhZ2dlckNsaWVudC5wcm90b3R5cGUuaWRGcm9tT3AgPSBmdW5jdGlvbiAocGF0aCwgaHR0cE1ldGhvZCwgb3ApIHtcbiAgaWYoIW9wIHx8ICFvcC5vcGVyYXRpb25JZCkge1xuICAgIG9wID0gb3AgfHwge307XG4gICAgb3Aub3BlcmF0aW9uSWQgPSBodHRwTWV0aG9kICsgJ18nICsgcGF0aDtcbiAgfVxuICB2YXIgb3BJZCA9IG9wLm9wZXJhdGlvbklkLnJlcGxhY2UoL1tcXHMhQCMkJV4mKigpXys9XFxbe1xcXX07Ojw+fC5cXC8/LFxcXFwnXCJcIi1dL2csICdfJykgfHwgKHBhdGguc3Vic3RyaW5nKDEpICsgJ18nICsgaHR0cE1ldGhvZCk7XG5cbiAgb3BJZCA9IG9wSWQucmVwbGFjZSgvKChfKXsyLH0pL2csICdfJyk7XG4gIG9wSWQgPSBvcElkLnJlcGxhY2UoL14oXykqL2csICcnKTtcbiAgb3BJZCA9IG9wSWQucmVwbGFjZSgvKFtfXSkqJC9nLCAnJyk7XG4gIHJldHVybiBvcElkO1xufTtcblxuU3dhZ2dlckNsaWVudC5wcm90b3R5cGUuZmFpbCA9IGZ1bmN0aW9uIChtZXNzYWdlKSB7XG4gIHRoaXMuZmFpbHVyZShtZXNzYWdlKTtcblxuICB0aHJvdyBtZXNzYWdlO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxubW9kdWxlLmV4cG9ydHMuX19iaW5kID0gZnVuY3Rpb24gKGZuLCBtZSkge1xuICByZXR1cm4gZnVuY3Rpb24oKXtcbiAgICByZXR1cm4gZm4uYXBwbHkobWUsIGFyZ3VtZW50cyk7XG4gIH07XG59O1xuXG52YXIgbG9nID0gbW9kdWxlLmV4cG9ydHMubG9nID0gZnVuY3Rpb24oKSB7XG4gIGxvZy5oaXN0b3J5ID0gbG9nLmhpc3RvcnkgfHwgW107XG4gIGxvZy5oaXN0b3J5LnB1c2goYXJndW1lbnRzKTtcblxuICAvLyBPbmx5IGxvZyBpZiBhdmFpbGFibGUgYW5kIHdlJ3JlIG5vdCB0ZXN0aW5nXG4gIGlmIChjb25zb2xlICYmIHByb2Nlc3MuZW52Lk5PREVfRU5WICE9PSAndGVzdCcpIHtcbiAgICBjb25zb2xlLmxvZyhBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpWzBdKTtcbiAgfVxufTtcblxubW9kdWxlLmV4cG9ydHMuZmFpbCA9IGZ1bmN0aW9uIChtZXNzYWdlKSB7XG4gIGxvZyhtZXNzYWdlKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzLm9wdGlvbkh0bWwgPSBmdW5jdGlvbiAobGFiZWwsIHZhbHVlKSB7XG4gIHJldHVybiAnPHRyPjx0ZCBjbGFzcz1cIm9wdGlvbk5hbWVcIj4nICsgbGFiZWwgKyAnOjwvdGQ+PHRkPicgKyB2YWx1ZSArICc8L3RkPjwvdHI+Jztcbn07XG5cbm1vZHVsZS5leHBvcnRzLnR5cGVGcm9tSnNvblNjaGVtYSA9IGZ1bmN0aW9uICh0eXBlLCBmb3JtYXQpIHtcbiAgdmFyIHN0cjtcblxuICBpZiAodHlwZSA9PT0gJ2ludGVnZXInICYmIGZvcm1hdCA9PT0gJ2ludDMyJykge1xuICAgIHN0ciA9ICdpbnRlZ2VyJztcbiAgfSBlbHNlIGlmICh0eXBlID09PSAnaW50ZWdlcicgJiYgZm9ybWF0ID09PSAnaW50NjQnKSB7XG4gICAgc3RyID0gJ2xvbmcnO1xuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdpbnRlZ2VyJyAmJiB0eXBlb2YgZm9ybWF0ID09PSAndW5kZWZpbmVkJykge1xuICAgIHN0ciA9ICdsb25nJztcbiAgfSBlbHNlIGlmICh0eXBlID09PSAnc3RyaW5nJyAmJiBmb3JtYXQgPT09ICdkYXRlLXRpbWUnKSB7XG4gICAgc3RyID0gJ2RhdGUtdGltZSc7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ3N0cmluZycgJiYgZm9ybWF0ID09PSAnZGF0ZScpIHtcbiAgICBzdHIgPSAnZGF0ZSc7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ251bWJlcicgJiYgZm9ybWF0ID09PSAnZmxvYXQnKSB7XG4gICAgc3RyID0gJ2Zsb2F0JztcbiAgfSBlbHNlIGlmICh0eXBlID09PSAnbnVtYmVyJyAmJiBmb3JtYXQgPT09ICdkb3VibGUnKSB7XG4gICAgc3RyID0gJ2RvdWJsZSc7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ251bWJlcicgJiYgdHlwZW9mIGZvcm1hdCA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICBzdHIgPSAnZG91YmxlJztcbiAgfSBlbHNlIGlmICh0eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICBzdHIgPSAnYm9vbGVhbic7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ3N0cmluZycpIHtcbiAgICBzdHIgPSAnc3RyaW5nJztcbiAgfVxuXG4gIHJldHVybiBzdHI7XG59O1xuXG52YXIgc2ltcGxlUmVmID0gbW9kdWxlLmV4cG9ydHMuc2ltcGxlUmVmID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgaWYgKHR5cGVvZiBuYW1lID09PSAndW5kZWZpbmVkJykge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgaWYgKG5hbWUuaW5kZXhPZignIy9kZWZpbml0aW9ucy8nKSA9PT0gMCkge1xuICAgIHJldHVybiBuYW1lLnN1YnN0cmluZygnIy9kZWZpbml0aW9ucy8nLmxlbmd0aCk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG5hbWU7XG4gIH1cbn07XG5cbnZhciBnZXRTdHJpbmdTaWduYXR1cmUgPSBtb2R1bGUuZXhwb3J0cy5nZXRTdHJpbmdTaWduYXR1cmUgPSBmdW5jdGlvbiAob2JqLCBiYXNlQ29tcG9uZW50KSB7XG4gIHZhciBzdHIgPSAnJztcblxuICBpZiAodHlwZW9mIG9iai4kcmVmICE9PSAndW5kZWZpbmVkJykge1xuICAgIHN0ciArPSBzaW1wbGVSZWYob2JqLiRyZWYpO1xuICB9IGVsc2UgaWYgKHR5cGVvZiBvYmoudHlwZSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICBzdHIgKz0gJ29iamVjdCc7XG4gIH0gZWxzZSBpZiAob2JqLnR5cGUgPT09ICdhcnJheScpIHtcbiAgICBpZiAoYmFzZUNvbXBvbmVudCkge1xuICAgICAgc3RyICs9IGdldFN0cmluZ1NpZ25hdHVyZSgob2JqLml0ZW1zIHx8IG9iai4kcmVmIHx8IHt9KSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN0ciArPSAnQXJyYXlbJztcbiAgICAgIHN0ciArPSBnZXRTdHJpbmdTaWduYXR1cmUoKG9iai5pdGVtcyB8fCBvYmouJHJlZiB8fCB7fSkpO1xuICAgICAgc3RyICs9ICddJztcbiAgICB9XG4gIH0gZWxzZSBpZiAob2JqLnR5cGUgPT09ICdpbnRlZ2VyJyAmJiBvYmouZm9ybWF0ID09PSAnaW50MzInKSB7XG4gICAgc3RyICs9ICdpbnRlZ2VyJztcbiAgfSBlbHNlIGlmIChvYmoudHlwZSA9PT0gJ2ludGVnZXInICYmIG9iai5mb3JtYXQgPT09ICdpbnQ2NCcpIHtcbiAgICBzdHIgKz0gJ2xvbmcnO1xuICB9IGVsc2UgaWYgKG9iai50eXBlID09PSAnaW50ZWdlcicgJiYgdHlwZW9mIG9iai5mb3JtYXQgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgc3RyICs9ICdsb25nJztcbiAgfSBlbHNlIGlmIChvYmoudHlwZSA9PT0gJ3N0cmluZycgJiYgb2JqLmZvcm1hdCA9PT0gJ2RhdGUtdGltZScpIHtcbiAgICBzdHIgKz0gJ2RhdGUtdGltZSc7XG4gIH0gZWxzZSBpZiAob2JqLnR5cGUgPT09ICdzdHJpbmcnICYmIG9iai5mb3JtYXQgPT09ICdkYXRlJykge1xuICAgIHN0ciArPSAnZGF0ZSc7XG4gIH0gZWxzZSBpZiAob2JqLnR5cGUgPT09ICdzdHJpbmcnICYmIHR5cGVvZiBvYmouZm9ybWF0ID09PSAndW5kZWZpbmVkJykge1xuICAgIHN0ciArPSAnc3RyaW5nJztcbiAgfSBlbHNlIGlmIChvYmoudHlwZSA9PT0gJ251bWJlcicgJiYgb2JqLmZvcm1hdCA9PT0gJ2Zsb2F0Jykge1xuICAgIHN0ciArPSAnZmxvYXQnO1xuICB9IGVsc2UgaWYgKG9iai50eXBlID09PSAnbnVtYmVyJyAmJiBvYmouZm9ybWF0ID09PSAnZG91YmxlJykge1xuICAgIHN0ciArPSAnZG91YmxlJztcbiAgfSBlbHNlIGlmIChvYmoudHlwZSA9PT0gJ251bWJlcicgJiYgdHlwZW9mIG9iai5mb3JtYXQgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgc3RyICs9ICdkb3VibGUnO1xuICB9IGVsc2UgaWYgKG9iai50eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICBzdHIgKz0gJ2Jvb2xlYW4nO1xuICB9IGVsc2UgaWYgKG9iai4kcmVmKSB7XG4gICAgc3RyICs9IHNpbXBsZVJlZihvYmouJHJlZik7XG4gIH0gZWxzZSB7XG4gICAgc3RyICs9IG9iai50eXBlO1xuICB9XG5cbiAgcmV0dXJuIHN0cjtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBoZWxwZXJzID0gcmVxdWlyZSgnLi9oZWxwZXJzJyk7XG52YXIgalF1ZXJ5ID0gcmVxdWlyZSgnanF1ZXJ5Jyk7XG52YXIgcmVxdWVzdCA9IHJlcXVpcmUoJ3N1cGVyYWdlbnQnKTtcblxuLypcbiAqIEpRdWVyeUh0dHBDbGllbnQgaXMgYSBsaWdodC13ZWlnaHQsIG5vZGUgb3IgYnJvd3NlciBIVFRQIGNsaWVudFxuICovXG52YXIgSlF1ZXJ5SHR0cENsaWVudCA9IGZ1bmN0aW9uICgpIHt9O1xuXG4vKlxuICogU3VwZXJhZ2VudEh0dHBDbGllbnQgaXMgYSBsaWdodC13ZWlnaHQsIG5vZGUgb3IgYnJvd3NlciBIVFRQIGNsaWVudFxuICovXG52YXIgU3VwZXJhZ2VudEh0dHBDbGllbnQgPSBmdW5jdGlvbiAoKSB7fTtcblxuLyoqXG4gKiBTd2FnZ2VySHR0cCBpcyBhIHdyYXBwZXIgZm9yIGV4ZWN1dGluZyByZXF1ZXN0c1xuICovXG52YXIgU3dhZ2dlckh0dHAgPSBtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHt9O1xuXG5Td2FnZ2VySHR0cC5wcm90b3R5cGUuZXhlY3V0ZSA9IGZ1bmN0aW9uIChvYmosIG9wdHMpIHtcbiAgaWYgKG9iaiAmJiAodHlwZW9mIG9iai51c2VKUXVlcnkgPT09ICdib29sZWFuJykpIHtcbiAgICB0aGlzLnVzZUpRdWVyeSA9IG9iai51c2VKUXVlcnk7XG4gIH0gZWxzZSB7XG4gICAgdGhpcy51c2VKUXVlcnkgPSB0aGlzLmlzSUU4KCk7XG4gIH1cblxuICBpZiAob2JqICYmIHR5cGVvZiBvYmouYm9keSA9PT0gJ29iamVjdCcpIHtcbiAgICAvLyBzcGVjaWFsIHByb2Nlc3NpbmcgZm9yIGZpbGUgdXBsb2FkcyB2aWEganF1ZXJ5XG4gICAgaWYgKG9iai5ib2R5LnR5cGUgJiYgb2JqLmJvZHkudHlwZSA9PT0gJ2Zvcm1EYXRhJyl7XG4gICAgICBvYmouY29udGVudFR5cGUgPSBmYWxzZTtcbiAgICAgIG9iai5wcm9jZXNzRGF0YSA9IGZhbHNlO1xuXG4gICAgICBkZWxldGUgb2JqLmhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddO1xuICAgIH0gZWxzZSB7XG4gICAgICBvYmouYm9keSA9IEpTT04uc3RyaW5naWZ5KG9iai5ib2R5KTtcbiAgICB9XG4gIH1cblxuICBpZiAodGhpcy51c2VKUXVlcnkpIHtcbiAgICByZXR1cm4gbmV3IEpRdWVyeUh0dHBDbGllbnQob3B0cykuZXhlY3V0ZShvYmopO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBuZXcgU3VwZXJhZ2VudEh0dHBDbGllbnQob3B0cykuZXhlY3V0ZShvYmopO1xuICB9XG59O1xuXG5Td2FnZ2VySHR0cC5wcm90b3R5cGUuaXNJRTggPSBmdW5jdGlvbiAoKSB7XG4gIHZhciBkZXRlY3RlZElFID0gZmFsc2U7XG5cbiAgaWYgKHR5cGVvZiBuYXZpZ2F0b3IgIT09ICd1bmRlZmluZWQnICYmIG5hdmlnYXRvci51c2VyQWdlbnQpIHtcbiAgICB2YXIgbmF2ID0gbmF2aWdhdG9yLnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpO1xuXG4gICAgaWYgKG5hdi5pbmRleE9mKCdtc2llJykgIT09IC0xKSB7XG4gICAgICB2YXIgdmVyc2lvbiA9IHBhcnNlSW50KG5hdi5zcGxpdCgnbXNpZScpWzFdKTtcblxuICAgICAgaWYgKHZlcnNpb24gPD0gOCkge1xuICAgICAgICBkZXRlY3RlZElFID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gZGV0ZWN0ZWRJRTtcbn07XG5cbkpRdWVyeUh0dHBDbGllbnQucHJvdG90eXBlLmV4ZWN1dGUgPSBmdW5jdGlvbiAob2JqKSB7XG4gIHZhciBjYiA9IG9iai5vbjtcbiAgdmFyIHJlcXVlc3QgPSBvYmo7XG5cbiAgb2JqLnR5cGUgPSBvYmoubWV0aG9kO1xuICBvYmouY2FjaGUgPSBmYWxzZTtcbiAgZGVsZXRlIG9iai51c2VKUXVlcnk7XG5cbiAgLypcbiAgb2JqLmJlZm9yZVNlbmQgPSBmdW5jdGlvbiAoeGhyKSB7XG4gICAgdmFyIGtleSwgcmVzdWx0cztcbiAgICBpZiAob2JqLmhlYWRlcnMpIHtcbiAgICAgIHJlc3VsdHMgPSBbXTtcbiAgICAgIGZvciAoa2V5IGluIG9iai5oZWFkZXJzKSB7XG4gICAgICAgIGlmIChrZXkudG9Mb3dlckNhc2UoKSA9PT0gJ2NvbnRlbnQtdHlwZScpIHtcbiAgICAgICAgICByZXN1bHRzLnB1c2gob2JqLmNvbnRlbnRUeXBlID0gb2JqLmhlYWRlcnNba2V5XSk7XG4gICAgICAgIH0gZWxzZSBpZiAoa2V5LnRvTG93ZXJDYXNlKCkgPT09ICdhY2NlcHQnKSB7XG4gICAgICAgICAgcmVzdWx0cy5wdXNoKG9iai5hY2NlcHRzID0gb2JqLmhlYWRlcnNba2V5XSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVzdWx0cy5wdXNoKHhoci5zZXRSZXF1ZXN0SGVhZGVyKGtleSwgb2JqLmhlYWRlcnNba2V5XSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0cztcbiAgICB9XG4gIH07Ki9cblxuICBvYmouZGF0YSA9IG9iai5ib2R5O1xuXG4gIGRlbGV0ZSBvYmouYm9keTtcblxuICBvYmouY29tcGxldGUgPSBmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICB2YXIgaGVhZGVycyA9IHt9O1xuICAgIHZhciBoZWFkZXJBcnJheSA9IHJlc3BvbnNlLmdldEFsbFJlc3BvbnNlSGVhZGVycygpLnNwbGl0KCdcXG4nKTtcblxuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgaGVhZGVyQXJyYXkubGVuZ3RoOyBpKyspIHtcbiAgICAgIHZhciB0b1NwbGl0ID0gaGVhZGVyQXJyYXlbaV0udHJpbSgpO1xuXG4gICAgICBpZiAodG9TcGxpdC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIHZhciBzZXBhcmF0b3IgPSB0b1NwbGl0LmluZGV4T2YoJzonKTtcblxuICAgICAgaWYgKHNlcGFyYXRvciA9PT0gLTEpIHtcbiAgICAgICAgLy8gTmFtZSBidXQgbm8gdmFsdWUgaW4gdGhlIGhlYWRlclxuICAgICAgICBoZWFkZXJzW3RvU3BsaXRdID0gbnVsbDtcblxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgdmFyIG5hbWUgPSB0b1NwbGl0LnN1YnN0cmluZygwLCBzZXBhcmF0b3IpLnRyaW0oKTtcbiAgICAgIHZhciB2YWx1ZSA9IHRvU3BsaXQuc3Vic3RyaW5nKHNlcGFyYXRvciArIDEpLnRyaW0oKTtcblxuICAgICAgaGVhZGVyc1tuYW1lXSA9IHZhbHVlO1xuICAgIH1cblxuICAgIHZhciBvdXQgPSB7XG4gICAgICB1cmw6IHJlcXVlc3QudXJsLFxuICAgICAgbWV0aG9kOiByZXF1ZXN0Lm1ldGhvZCxcbiAgICAgIHN0YXR1czogcmVzcG9uc2Uuc3RhdHVzLFxuICAgICAgc3RhdHVzVGV4dDogcmVzcG9uc2Uuc3RhdHVzVGV4dCxcbiAgICAgIGRhdGE6IHJlc3BvbnNlLnJlc3BvbnNlVGV4dCxcbiAgICAgIGhlYWRlcnM6IGhlYWRlcnNcbiAgICB9O1xuXG4gICAgdmFyIGNvbnRlbnRUeXBlID0gKGhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddIHx8IGhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddIHx8IG51bGwpO1xuXG4gICAgaWYgKGNvbnRlbnRUeXBlKSB7XG4gICAgICBpZiAoY29udGVudFR5cGUuaW5kZXhPZignYXBwbGljYXRpb24vanNvbicpID09PSAwIHx8IGNvbnRlbnRUeXBlLmluZGV4T2YoJytqc29uJykgPiAwKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgb3V0Lm9iaiA9IHJlc3BvbnNlLnJlc3BvbnNlSlNPTiB8fCBKU09OLnBhcnNlKG91dC5kYXRhKSB8fCB7fTtcbiAgICAgICAgfSBjYXRjaCAoZXgpIHtcbiAgICAgICAgICAvLyBkbyBub3Qgc2V0IG91dC5vYmpcbiAgICAgICAgICBoZWxwZXJzLmxvZygndW5hYmxlIHRvIHBhcnNlIEpTT04gY29udGVudCcpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHJlc3BvbnNlLnN0YXR1cyA+PSAyMDAgJiYgcmVzcG9uc2Uuc3RhdHVzIDwgMzAwKSB7XG4gICAgICBjYi5yZXNwb25zZShvdXQpO1xuICAgIH0gZWxzZSBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSAwIHx8IChyZXNwb25zZS5zdGF0dXMgPj0gNDAwICYmIHJlc3BvbnNlLnN0YXR1cyA8IDU5OSkpIHtcbiAgICAgIGNiLmVycm9yKG91dCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBjYi5yZXNwb25zZShvdXQpO1xuICAgIH1cbiAgfTtcblxuICBqUXVlcnkuc3VwcG9ydC5jb3JzID0gdHJ1ZTtcblxuICByZXR1cm4galF1ZXJ5LmFqYXgob2JqKTtcbn07XG5cblN1cGVyYWdlbnRIdHRwQ2xpZW50LnByb3RvdHlwZS5leGVjdXRlID0gZnVuY3Rpb24gKG9iaikge1xuICB2YXIgbWV0aG9kID0gb2JqLm1ldGhvZC50b0xvd2VyQ2FzZSgpO1xuXG4gIGlmIChtZXRob2QgPT09ICdkZWxldGUnKSB7XG4gICAgbWV0aG9kID0gJ2RlbCc7XG4gIH1cblxuICB2YXIgaGVhZGVycyA9IG9iai5oZWFkZXJzIHx8IHt9O1xuICB2YXIgciA9IHJlcXVlc3RbbWV0aG9kXShvYmoudXJsKTtcbiAgdmFyIG5hbWU7XG5cbiAgZm9yIChuYW1lIGluIGhlYWRlcnMpIHtcbiAgICByLnNldChuYW1lLCBoZWFkZXJzW25hbWVdKTtcbiAgfVxuXG4gIGlmIChvYmouYm9keSkge1xuICAgIHIuc2VuZChvYmouYm9keSk7XG4gIH1cblxuICByLmVuZChmdW5jdGlvbiAoZXJyLCByZXMpIHtcbiAgICByZXMgPSByZXMgfHwge1xuICAgICAgc3RhdHVzOiAwLFxuICAgICAgaGVhZGVyczoge2Vycm9yOiAnbm8gcmVzcG9uc2UgZnJvbSBzZXJ2ZXInfVxuICAgIH07XG4gICAgdmFyIHJlc3BvbnNlID0ge1xuICAgICAgdXJsOiBvYmoudXJsLFxuICAgICAgbWV0aG9kOiBvYmoubWV0aG9kLFxuICAgICAgaGVhZGVyczogcmVzLmhlYWRlcnNcbiAgICB9O1xuICAgIHZhciBjYjtcblxuICAgIGlmICghZXJyICYmIHJlcy5lcnJvcikge1xuICAgICAgZXJyID0gcmVzLmVycm9yO1xuICAgIH1cblxuICAgIGlmIChlcnIgJiYgb2JqLm9uICYmIG9iai5vbi5lcnJvcikge1xuICAgICAgcmVzcG9uc2Uub2JqID0gZXJyO1xuICAgICAgcmVzcG9uc2Uuc3RhdHVzID0gcmVzID8gcmVzLnN0YXR1cyA6IDUwMDtcbiAgICAgIHJlc3BvbnNlLnN0YXR1c1RleHQgPSByZXMgPyByZXMudGV4dCA6IGVyci5tZXNzYWdlO1xuICAgICAgY2IgPSBvYmoub24uZXJyb3I7XG4gICAgfSBlbHNlIGlmIChyZXMgJiYgb2JqLm9uICYmIG9iai5vbi5yZXNwb25zZSkge1xuICAgICAgcmVzcG9uc2Uub2JqID0gKHR5cGVvZiByZXMuYm9keSAhPT0gJ3VuZGVmaW5lZCcpID8gcmVzLmJvZHkgOiByZXMudGV4dDtcbiAgICAgIHJlc3BvbnNlLnN0YXR1cyA9IHJlcy5zdGF0dXM7XG4gICAgICByZXNwb25zZS5zdGF0dXNUZXh0ID0gcmVzLnRleHQ7XG4gICAgICBjYiA9IG9iai5vbi5yZXNwb25zZTtcbiAgICB9XG4gICAgcmVzcG9uc2UuZGF0YSA9IHJlc3BvbnNlLnN0YXR1c1RleHQ7XG5cbiAgICBpZiAoY2IpIHtcbiAgICAgIGNiKHJlc3BvbnNlKTtcbiAgICB9XG4gIH0pO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIFN3YWdnZXJIdHRwID0gcmVxdWlyZSgnLi9odHRwJyk7XG5cbi8qKiBcbiAqIFJlc29sdmVzIGEgc3BlYydzIHJlbW90ZSByZWZlcmVuY2VzXG4gKi9cbnZhciBSZXNvbHZlciA9IG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKCkge307XG5cblJlc29sdmVyLnByb3RvdHlwZS5yZXNvbHZlID0gZnVuY3Rpb24gKHNwZWMsIGNhbGxiYWNrLCBzY29wZSkge1xuICB0aGlzLnNjb3BlID0gKHNjb3BlIHx8IHRoaXMpO1xuXG4gIHZhciBob3N0LCBuYW1lLCBwYXRoLCBwcm9wZXJ0eSwgcHJvcGVydHlOYW1lO1xuICB2YXIgcHJvY2Vzc2VkQ2FsbHMgPSAwLCByZXNvbHZlZFJlZnMgPSB7fSwgdW5yZXNvbHZlZFJlZnMgPSB7fTtcbiAgdmFyIHJlc29sdXRpb25UYWJsZSA9IHt9OyAvLyBzdG9yZSBvYmplY3RzIGZvciBkZXJlZmVyZW5jaW5nXG5cbiAgLy8gbW9kZWxzXG4gIGZvciAobmFtZSBpbiBzcGVjLmRlZmluaXRpb25zKSB7XG4gICAgdmFyIG1vZGVsID0gc3BlYy5kZWZpbml0aW9uc1tuYW1lXTtcblxuICAgIGZvciAocHJvcGVydHlOYW1lIGluIG1vZGVsLnByb3BlcnRpZXMpIHtcbiAgICAgIHByb3BlcnR5ID0gbW9kZWwucHJvcGVydGllc1twcm9wZXJ0eU5hbWVdO1xuXG4gICAgICB0aGlzLnJlc29sdmVUbyhwcm9wZXJ0eSwgcmVzb2x1dGlvblRhYmxlKTtcbiAgICB9XG4gIH1cblxuICAvLyBvcGVyYXRpb25zXG4gIGZvciAobmFtZSBpbiBzcGVjLnBhdGhzKSB7XG4gICAgdmFyIG1ldGhvZCwgb3BlcmF0aW9uLCByZXNwb25zZUNvZGU7XG5cbiAgICBwYXRoID0gc3BlYy5wYXRoc1tuYW1lXTtcblxuICAgIGZvciAobWV0aG9kIGluIHBhdGgpIHtcbiAgICAgIGlmKG1ldGhvZCA9PT0gJyRyZWYnKSB7XG4gICAgICAgIHRoaXMucmVzb2x2ZUlubGluZShzcGVjLCBwYXRoLCByZXNvbHV0aW9uVGFibGUsIHVucmVzb2x2ZWRSZWZzKTtcbiAgICAgIH1cbiAgICAgIGVsc2Uge1xuICAgICAgICBvcGVyYXRpb24gPSBwYXRoW21ldGhvZF07XG5cbiAgICAgICAgdmFyIGksIHBhcmFtZXRlcnMgPSBvcGVyYXRpb24ucGFyYW1ldGVycztcblxuICAgICAgICBmb3IgKGkgaW4gcGFyYW1ldGVycykge1xuICAgICAgICAgIHZhciBwYXJhbWV0ZXIgPSBwYXJhbWV0ZXJzW2ldO1xuXG4gICAgICAgICAgaWYgKHBhcmFtZXRlci5pbiA9PT0gJ2JvZHknICYmIHBhcmFtZXRlci5zY2hlbWEpIHtcbiAgICAgICAgICAgIHRoaXMucmVzb2x2ZVRvKHBhcmFtZXRlci5zY2hlbWEsIHJlc29sdXRpb25UYWJsZSk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKHBhcmFtZXRlci4kcmVmKSB7XG4gICAgICAgICAgICB0aGlzLnJlc29sdmVJbmxpbmUoc3BlYywgcGFyYW1ldGVyLCByZXNvbHV0aW9uVGFibGUsIHVucmVzb2x2ZWRSZWZzKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBmb3IgKHJlc3BvbnNlQ29kZSBpbiBvcGVyYXRpb24ucmVzcG9uc2VzKSB7XG4gICAgICAgICAgdmFyIHJlc3BvbnNlID0gb3BlcmF0aW9uLnJlc3BvbnNlc1tyZXNwb25zZUNvZGVdO1xuICAgICAgICAgIGlmKHR5cGVvZiByZXNwb25zZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgICAgIGlmKHJlc3BvbnNlLiRyZWYpIHtcbiAgICAgICAgICAgICAgdGhpcy5yZXNvbHZlSW5saW5lKHNwZWMsIHJlc3BvbnNlLCByZXNvbHV0aW9uVGFibGUsIHVucmVzb2x2ZWRSZWZzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHJlc3BvbnNlLnNjaGVtYSkge1xuICAgICAgICAgICAgdGhpcy5yZXNvbHZlVG8ocmVzcG9uc2Uuc2NoZW1hLCByZXNvbHV0aW9uVGFibGUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIGdldCBob3N0c1xuICB2YXIgb3B0cyA9IHt9LCBleHBlY3RlZENhbGxzID0gMDtcblxuICBmb3IgKG5hbWUgaW4gcmVzb2x1dGlvblRhYmxlKSB7XG4gICAgdmFyIHBhcnRzID0gbmFtZS5zcGxpdCgnIycpO1xuXG4gICAgaWYgKHBhcnRzLmxlbmd0aCA9PT0gMikge1xuICAgICAgaG9zdCA9IHBhcnRzWzBdOyBwYXRoID0gcGFydHNbMV07XG5cbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShvcHRzW2hvc3RdKSkge1xuICAgICAgICBvcHRzW2hvc3RdID0gW107XG4gICAgICAgIGV4cGVjdGVkQ2FsbHMgKz0gMTtcbiAgICAgIH1cblxuICAgICAgb3B0c1tob3N0XS5wdXNoKHBhdGgpO1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShvcHRzW25hbWVdKSkge1xuICAgICAgICBvcHRzW25hbWVdID0gW107XG4gICAgICAgIGV4cGVjdGVkQ2FsbHMgKz0gMTtcbiAgICAgIH1cblxuICAgICAgb3B0c1tuYW1lXS5wdXNoKG51bGwpO1xuICAgIH1cbiAgfVxuXG4gIGZvciAobmFtZSBpbiBvcHRzKSB7XG4gICAgdmFyIHNlbGYgPSB0aGlzLCBvcHQgPSBvcHRzW25hbWVdO1xuXG4gICAgaG9zdCA9IG5hbWU7XG5cbiAgICB2YXIgb2JqID0ge1xuICAgICAgdXNlSlF1ZXJ5OiBmYWxzZSwgIC8vIFRPRE9cbiAgICAgIHVybDogaG9zdCxcbiAgICAgIG1ldGhvZDogJ2dldCcsXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgIGFjY2VwdDogdGhpcy5zY29wZS5zd2FnZ2VyUmVxdWVzdEhlYWRlcnMgfHwgJ2FwcGxpY2F0aW9uL2pzb24nXG4gICAgICB9LFxuICAgICAgb246IHtcbiAgICAgICAgZXJyb3I6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBwcm9jZXNzZWRDYWxscyArPSAxO1xuXG4gICAgICAgICAgdmFyIGk7XG5cbiAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgb3B0Lmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAvLyBmYWlsIGFsbCBvZiB0aGVzZVxuICAgICAgICAgICAgdmFyIHJlc29sdmVkID0gaG9zdCArICcjJyArIG9wdFtpXTtcblxuICAgICAgICAgICAgdW5yZXNvbHZlZFJlZnNbcmVzb2x2ZWRdID0gbnVsbDtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAocHJvY2Vzc2VkQ2FsbHMgPT09IGV4cGVjdGVkQ2FsbHMpIHtcbiAgICAgICAgICAgIHNlbGYuZmluaXNoKHNwZWMsIHJlc29sdXRpb25UYWJsZSwgcmVzb2x2ZWRSZWZzLCB1bnJlc29sdmVkUmVmcywgY2FsbGJhY2spO1xuICAgICAgICAgIH1cbiAgICAgICAgfSwgIC8vIGpzaGludCBpZ25vcmU6bGluZVxuICAgICAgICByZXNwb25zZTogZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG5cbiAgICAgICAgICB2YXIgaSwgaiwgc3dhZ2dlciA9IHJlc3BvbnNlLm9iajtcblxuICAgICAgICAgIGlmKHN3YWdnZXIgPT09IG51bGwgfHwgT2JqZWN0LmtleXMoc3dhZ2dlcikubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICBzd2FnZ2VyID0gSlNPTi5wYXJzZShyZXNwb25zZS5kYXRhKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhdGNoIChlKXtcbiAgICAgICAgICAgICAgc3dhZ2dlciA9IHt9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIHByb2Nlc3NlZENhbGxzICs9IDE7XG5cbiAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgb3B0Lmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICB2YXIgcGF0aCA9IG9wdFtpXTtcbiAgICAgICAgICAgIGlmKHBhdGggPT0gbnVsbCkge1xuICAgICAgICAgICAgICByZXNvbHZlZFJlZnNbbmFtZV0gPSB7XG4gICAgICAgICAgICAgICAgbmFtZTogbmFtZSxcbiAgICAgICAgICAgICAgICBvYmo6IHN3YWdnZXJcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICB2YXIgbG9jYXRpb24gPSBzd2FnZ2VyLCBwYXJ0cyA9IHBhdGguc3BsaXQoJy8nKTtcbiAgICAgICAgICAgICAgZm9yIChqID0gMDsgaiA8IHBhcnRzLmxlbmd0aDsgaisrKSB7XG4gICAgICAgICAgICAgICAgdmFyIHNlZ21lbnQgPSBwYXJ0c1tqXTtcbiAgICAgICAgICAgICAgICBpZihzZWdtZW50LmluZGV4T2YoJ34xJykgIT09IC0xKSB7XG4gICAgICAgICAgICAgICAgICBzZWdtZW50ID0gcGFydHNbal0ucmVwbGFjZSgvfjAvZywgJ34nKS5yZXBsYWNlKC9+MS9nLCAnLycpO1xuICAgICAgICAgICAgICAgICAgaWYoc2VnbWVudC5jaGFyQXQoMCkgIT09ICcvJykge1xuICAgICAgICAgICAgICAgICAgICBzZWdtZW50ID0gJy8nICsgc2VnbWVudDtcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGxvY2F0aW9uID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKHNlZ21lbnQubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgbG9jYXRpb24gPSBsb2NhdGlvbltzZWdtZW50XTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdmFyIHJlc29sdmVkID0gaG9zdCArICcjJyArIHBhdGgsIHJlc29sdmVkTmFtZSA9IHBhcnRzW2otMV07XG5cbiAgICAgICAgICAgICAgaWYgKHR5cGVvZiBsb2NhdGlvbiAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAgICAgICByZXNvbHZlZFJlZnNbcmVzb2x2ZWRdID0ge1xuICAgICAgICAgICAgICAgICAgbmFtZTogcmVzb2x2ZWROYW1lLFxuICAgICAgICAgICAgICAgICAgb2JqOiBsb2NhdGlvblxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdW5yZXNvbHZlZFJlZnNbcmVzb2x2ZWRdID0gbnVsbDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAocHJvY2Vzc2VkQ2FsbHMgPT09IGV4cGVjdGVkQ2FsbHMpIHtcbiAgICAgICAgICAgIHNlbGYuZmluaXNoKHNwZWMsIHJlc29sdXRpb25UYWJsZSwgcmVzb2x2ZWRSZWZzLCB1bnJlc29sdmVkUmVmcywgY2FsbGJhY2spO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSAvLyBqc2hpbnQgaWdub3JlOmxpbmVcbiAgICB9O1xuXG4gICAgaWYgKHNjb3BlICYmIHNjb3BlLmNsaWVudEF1dGhvcml6YXRpb25zKSB7XG4gICAgICBzY29wZS5jbGllbnRBdXRob3JpemF0aW9ucy5hcHBseShvYmopO1xuICAgIH1cblxuICAgIG5ldyBTd2FnZ2VySHR0cCgpLmV4ZWN1dGUob2JqKTtcbiAgfVxuXG4gIGlmIChPYmplY3Qua2V5cyhvcHRzKS5sZW5ndGggPT09IDApIHtcbiAgICBjYWxsYmFjay5jYWxsKHRoaXMuc2NvcGUsIHNwZWMsIHVucmVzb2x2ZWRSZWZzKTtcbiAgfVxufTtcblxuUmVzb2x2ZXIucHJvdG90eXBlLmZpbmlzaCA9IGZ1bmN0aW9uIChzcGVjLCByZXNvbHV0aW9uVGFibGUsIHJlc29sdmVkUmVmcywgdW5yZXNvbHZlZFJlZnMsIGNhbGxiYWNrKSB7XG4gIC8vIHdhbGsgcmVzb2x1dGlvbiB0YWJsZSBhbmQgcmVwbGFjZSB3aXRoIHJlc29sdmVkIHJlZnNcbiAgdmFyIHJlZjtcblxuICBmb3IgKHJlZiBpbiByZXNvbHV0aW9uVGFibGUpIHtcbiAgICB2YXIgaSwgbG9jYXRpb25zID0gcmVzb2x1dGlvblRhYmxlW3JlZl07XG5cbiAgICBmb3IgKGkgPSAwOyBpIDwgbG9jYXRpb25zLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgcmVzb2x2ZWRUbyA9IHJlc29sdmVkUmVmc1tsb2NhdGlvbnNbaV0ub2JqLiRyZWZdO1xuXG4gICAgICBpZiAocmVzb2x2ZWRUbykge1xuICAgICAgICBpZiAoIXNwZWMuZGVmaW5pdGlvbnMpIHtcbiAgICAgICAgICBzcGVjLmRlZmluaXRpb25zID0ge307XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobG9jYXRpb25zW2ldLnJlc29sdmVBcyA9PT0gJyRyZWYnKSB7XG4gICAgICAgICAgc3BlYy5kZWZpbml0aW9uc1tyZXNvbHZlZFRvLm5hbWVdID0gcmVzb2x2ZWRUby5vYmo7XG4gICAgICAgICAgbG9jYXRpb25zW2ldLm9iai4kcmVmID0gJyMvZGVmaW5pdGlvbnMvJyArIHJlc29sdmVkVG8ubmFtZTtcbiAgICAgICAgfSBlbHNlIGlmIChsb2NhdGlvbnNbaV0ucmVzb2x2ZUFzID09PSAnaW5saW5lJykge1xuICAgICAgICAgIHZhciB0YXJnZXRPYmogPSBsb2NhdGlvbnNbaV0ub2JqO1xuICAgICAgICAgIHZhciBrZXk7XG5cbiAgICAgICAgICBkZWxldGUgdGFyZ2V0T2JqLiRyZWY7XG5cbiAgICAgICAgICBmb3IgKGtleSBpbiByZXNvbHZlZFRvLm9iaikge1xuICAgICAgICAgICAgdGFyZ2V0T2JqW2tleV0gPSByZXNvbHZlZFRvLm9ialtrZXldO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNhbGxiYWNrLmNhbGwodGhpcy5zY29wZSwgc3BlYywgdW5yZXNvbHZlZFJlZnMpO1xufTtcblxuLyoqXG4gKiBpbW1lZGlhdGVseSBpbi1saW5lcyBsb2NhbCByZWZzLCBxdWV1ZXMgcmVtb3RlIHJlZnNcbiAqIGZvciBpbmxpbmUgcmVzb2x1dGlvblxuICovXG5SZXNvbHZlci5wcm90b3R5cGUucmVzb2x2ZUlubGluZSA9IGZ1bmN0aW9uIChzcGVjLCBwcm9wZXJ0eSwgb2JqcywgdW5yZXNvbHZlZFJlZnMpIHtcbiAgdmFyIHJlZiA9IHByb3BlcnR5LiRyZWY7XG5cbiAgaWYgKHJlZikge1xuICAgIGlmIChyZWYuaW5kZXhPZignaHR0cCcpID09PSAwKSB7XG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShvYmpzW3JlZl0pKSB7XG4gICAgICAgIG9ianNbcmVmXS5wdXNoKHtvYmo6IHByb3BlcnR5LCByZXNvbHZlQXM6ICdpbmxpbmUnfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvYmpzW3JlZl0gPSBbe29iajogcHJvcGVydHksIHJlc29sdmVBczogJ2lubGluZSd9XTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHJlZi5pbmRleE9mKCcjJykgPT09IDApIHtcbiAgICAgIC8vIGxvY2FsIHJlc29sdmVcbiAgICAgIHZhciBzaG9ydGVuZWRSZWYgPSByZWYuc3Vic3RyaW5nKDEpO1xuICAgICAgdmFyIGksIHBhcnRzID0gc2hvcnRlbmVkUmVmLnNwbGl0KCcvJyksIGxvY2F0aW9uID0gc3BlYztcblxuICAgICAgZm9yIChpID0gMDsgaSA8IHBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBwYXJ0ID0gcGFydHNbaV07XG5cbiAgICAgICAgaWYgKHBhcnQubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGxvY2F0aW9uID0gbG9jYXRpb25bcGFydF07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGxvY2F0aW9uKSB7XG4gICAgICAgIGRlbGV0ZSBwcm9wZXJ0eS4kcmVmO1xuXG4gICAgICAgIHZhciBrZXk7XG5cbiAgICAgICAgZm9yIChrZXkgaW4gbG9jYXRpb24pIHtcbiAgICAgICAgICBwcm9wZXJ0eVtrZXldID0gbG9jYXRpb25ba2V5XTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdW5yZXNvbHZlZFJlZnNbcmVmXSA9IG51bGw7XG4gICAgICB9XG4gICAgfVxuICB9IGVsc2UgaWYgKHByb3BlcnR5LnR5cGUgPT09ICdhcnJheScpIHtcbiAgICB0aGlzLnJlc29sdmVUbyhwcm9wZXJ0eS5pdGVtcywgb2Jqcyk7XG4gIH1cbn07XG5cblJlc29sdmVyLnByb3RvdHlwZS5yZXNvbHZlVG8gPSBmdW5jdGlvbiAocHJvcGVydHksIG9ianMpIHtcbiAgdmFyIHJlZiA9IHByb3BlcnR5LiRyZWY7XG5cbiAgaWYgKHJlZikge1xuICAgIGlmIChyZWYuaW5kZXhPZignaHR0cCcpID09PSAwKSB7XG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShvYmpzW3JlZl0pKSB7XG4gICAgICAgIG9ianNbcmVmXS5wdXNoKHtvYmo6IHByb3BlcnR5LCByZXNvbHZlQXM6ICckcmVmJ30pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb2Jqc1tyZWZdID0gW3tvYmo6IHByb3BlcnR5LCByZXNvbHZlQXM6ICckcmVmJ31dO1xuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIGlmIChwcm9wZXJ0eS50eXBlID09PSAnYXJyYXknKSB7XG4gICAgdmFyIGl0ZW1zID0gcHJvcGVydHkuaXRlbXM7XG5cbiAgICB0aGlzLnJlc29sdmVUbyhpdGVtcywgb2Jqcyk7XG4gIH1cbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBTd2FnZ2VyQ2xpZW50ID0gcmVxdWlyZSgnLi9jbGllbnQnKTtcbnZhciBTd2FnZ2VySHR0cCA9IHJlcXVpcmUoJy4vaHR0cCcpO1xuXG52YXIgU3dhZ2dlclNwZWNDb252ZXJ0ZXIgPSBtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICgpIHtcbiAgdGhpcy5lcnJvcnMgPSBbXTtcbiAgdGhpcy53YXJuaW5ncyA9IFtdO1xuICB0aGlzLm1vZGVsTWFwID0ge307XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUuc2V0RG9jdW1lbnRhdGlvbkxvY2F0aW9uID0gZnVuY3Rpb24gKGxvY2F0aW9uKSB7XG4gIHRoaXMuZG9jTG9jYXRpb24gPSBsb2NhdGlvbjtcbn07XG5cbi8qKlxuICogY29udmVydHMgYSByZXNvdXJjZSBsaXN0aW5nIE9SIGFwaSBkZWNsYXJhdGlvblxuICoqL1xuU3dhZ2dlclNwZWNDb252ZXJ0ZXIucHJvdG90eXBlLmNvbnZlcnQgPSBmdW5jdGlvbiAob2JqLCBjYWxsYmFjaykge1xuICAvLyBub3QgYSB2YWxpZCBzcGVjXG4gIGlmKCFvYmogfHwgIUFycmF5LmlzQXJyYXkob2JqLmFwaXMpKSB7XG4gICAgcmV0dXJuIHRoaXMuZmluaXNoKGNhbGxiYWNrLCBudWxsKTtcbiAgfVxuXG4gIC8vIGNyZWF0ZSBhIG5ldyBzd2FnZ2VyIG9iamVjdCB0byByZXR1cm5cbiAgdmFyIHN3YWdnZXIgPSB7IHN3YWdnZXI6ICcyLjAnIH07XG5cbiAgc3dhZ2dlci5vcmlnaW5hbFZlcnNpb24gPSBvYmouc3dhZ2dlclZlcnNpb247XG5cbiAgLy8gYWRkIHRoZSBpbmZvXG4gIHRoaXMuYXBpSW5mbyhvYmosIHN3YWdnZXIpO1xuXG4gIC8vIGFkZCBzZWN1cml0eSBkZWZpbml0aW9uc1xuICB0aGlzLnNlY3VyaXR5RGVmaW5pdGlvbnMob2JqLCBzd2FnZ2VyKTtcblxuICAvLyBzZWUgaWYgdGhpcyBpcyBhIHNpbmdsZS1maWxlIHN3YWdnZXIgZGVmaW5pdGlvblxuICB2YXIgaXNTaW5nbGVGaWxlU3dhZ2dlciA9IGZhbHNlO1xuICB2YXIgaTtcbiAgZm9yKGkgPSAwOyBpIDwgb2JqLmFwaXMubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgYXBpID0gb2JqLmFwaXNbaV07XG4gICAgaWYoQXJyYXkuaXNBcnJheShhcGkub3BlcmF0aW9ucykpIHtcbiAgICAgIGlzU2luZ2xlRmlsZVN3YWdnZXIgPSB0cnVlO1xuICAgIH1cbiAgfVxuICBpZihpc1NpbmdsZUZpbGVTd2FnZ2VyKSB7XG4gICAgdGhpcy5kZWNsYXJhdGlvbihvYmosIHN3YWdnZXIpO1xuICAgIHRoaXMuZmluaXNoKGNhbGxiYWNrLCBzd2FnZ2VyKTtcbiAgfVxuICBlbHNlIHtcbiAgICB0aGlzLnJlc291cmNlTGlzdGluZyhvYmosIHN3YWdnZXIsIGNhbGxiYWNrKTtcbiAgfVxufTtcblxuU3dhZ2dlclNwZWNDb252ZXJ0ZXIucHJvdG90eXBlLmRlY2xhcmF0aW9uID0gZnVuY3Rpb24ob2JqLCBzd2FnZ2VyKSB7XG4gIHZhciBuYW1lLCBpO1xuICBpZighb2JqLmFwaXMpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICB2YXIgYmFzZVBhdGggPSBvYmouYmFzZVBhdGg7XG4gIGlmKG9iai5iYXNlUGF0aC5pbmRleE9mKCdodHRwOi8vJykgPT09IDApIHtcbiAgICB2YXIgcCA9IG9iai5iYXNlUGF0aC5zdWJzdHJpbmcoJ2h0dHA6Ly8nLmxlbmd0aCk7XG4gICAgdmFyIHBvcyA9IHAuaW5kZXhPZignLycpO1xuICAgIGlmKHBvcyA+IDApIHtcbiAgICAgIHN3YWdnZXIuaG9zdCA9IHAuc3Vic3RyaW5nKDAsIHBvcyk7XG4gICAgICBzd2FnZ2VyLmJhc2VQYXRoID0gcC5zdWJzdHJpbmcocG9zKTtcbiAgICB9XG4gICAgZWxzZXtcbiAgICAgIHN3YWdnZXIuaG9zdCA9IHA7XG4gICAgICBzd2FnZ2VyLmJhc2VQYXRoID0gJy8nO1xuICAgIH1cbiAgfVxuICB2YXIgcmVzb3VyY2VMZXZlbEF1dGg7XG4gIGlmKG9iai5hdXRob3JpemF0aW9ucykge1xuICAgIHJlc291cmNlTGV2ZWxBdXRoID0gb2JqLmF1dGhvcml6YXRpb25zO1xuICB9XG4gIGlmKG9iai5jb25zdW1lcykge1xuICAgIHN3YWdnZXIuY29uc3VtZXMgPSBvYmouY29uc3VtZXM7XG4gIH1cbiAgaWYob2JqLnByb2R1Y2VzKSB7XG4gICAgc3dhZ2dlci5wcm9kdWNlcyA9IG9iai5wcm9kdWNlcztcbiAgfVxuXG4gIC8vIGJ1aWxkIGEgbWFwcGluZyBvZiBpZCB0byBuYW1lIGZvciAxLjAgbW9kZWwgcmVzb2x1dGlvbnNcbiAgaWYodHlwZW9mIG9iaiA9PT0gJ29iamVjdCcpIHtcbiAgICBmb3IobmFtZSBpbiBvYmoubW9kZWxzKSB7XG4gICAgICB2YXIgZXhpc3RpbmdNb2RlbCA9IG9iai5tb2RlbHNbbmFtZV07XG4gICAgICB2YXIga2V5ID0gKGV4aXN0aW5nTW9kZWwuaWQgfHwgbmFtZSk7XG4gICAgICB0aGlzLm1vZGVsTWFwW2tleV0gPSBuYW1lO1xuICAgIH1cbiAgfVxuXG4gIGZvcihpID0gMDsgaSA8IG9iai5hcGlzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIGFwaSA9IG9iai5hcGlzW2ldO1xuICAgIHZhciBwYXRoID0gYXBpLnBhdGg7XG4gICAgdmFyIG9wZXJhdGlvbnMgPSBhcGkub3BlcmF0aW9ucztcbiAgICB0aGlzLm9wZXJhdGlvbnMocGF0aCwgb2JqLnJlc291cmNlUGF0aCwgb3BlcmF0aW9ucywgcmVzb3VyY2VMZXZlbEF1dGgsIHN3YWdnZXIpO1xuICB9XG5cbiAgdmFyIG1vZGVscyA9IG9iai5tb2RlbHM7XG4gIHRoaXMubW9kZWxzKG1vZGVscywgc3dhZ2dlcik7XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUubW9kZWxzID0gZnVuY3Rpb24ob2JqLCBzd2FnZ2VyKSB7XG4gIGlmKHR5cGVvZiBvYmogIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHZhciBuYW1lO1xuXG4gIHN3YWdnZXIuZGVmaW5pdGlvbnMgPSBzd2FnZ2VyLmRlZmluaXRpb25zIHx8IHt9O1xuICBmb3IobmFtZSBpbiBvYmopIHtcbiAgICB2YXIgZXhpc3RpbmdNb2RlbCA9IG9ialtuYW1lXTtcbiAgICB2YXIgX2VudW0gPSBbXTtcbiAgICB2YXIgc2NoZW1hID0geyBwcm9wZXJ0aWVzOiB7fX07XG4gICAgdmFyIHByb3BlcnR5TmFtZTtcbiAgICBmb3IocHJvcGVydHlOYW1lIGluIGV4aXN0aW5nTW9kZWwucHJvcGVydGllcykge1xuICAgICAgdmFyIGV4aXN0aW5nUHJvcGVydHkgPSBleGlzdGluZ01vZGVsLnByb3BlcnRpZXNbcHJvcGVydHlOYW1lXTtcbiAgICAgIHZhciBwcm9wZXJ0eSA9IHt9O1xuICAgICAgdGhpcy5kYXRhVHlwZShleGlzdGluZ1Byb3BlcnR5LCBwcm9wZXJ0eSk7XG4gICAgICBpZihleGlzdGluZ1Byb3BlcnR5LmRlc2NyaXB0aW9uKSB7XG4gICAgICAgIHByb3BlcnR5LmRlc2NyaXB0aW9uID0gZXhpc3RpbmdQcm9wZXJ0eS5kZXNjcmlwdGlvbjtcbiAgICAgIH1cbiAgICAgIGlmKGV4aXN0aW5nUHJvcGVydHlbJ2VudW0nXSkge1xuICAgICAgICBwcm9wZXJ0eVsnZW51bSddID0gZXhpc3RpbmdQcm9wZXJ0eVsnZW51bSddO1xuICAgICAgfVxuICAgICAgaWYodHlwZW9mIGV4aXN0aW5nUHJvcGVydHkucmVxdWlyZWQgPT09ICdib29sZWFuJyAmJiBleGlzdGluZ1Byb3BlcnR5LnJlcXVpcmVkID09PSB0cnVlKSB7XG4gICAgICAgIF9lbnVtLnB1c2gocHJvcGVydHlOYW1lKTtcbiAgICAgIH1cbiAgICAgIGlmKHR5cGVvZiBleGlzdGluZ1Byb3BlcnR5LnJlcXVpcmVkID09PSAnc3RyaW5nJyAmJiBleGlzdGluZ1Byb3BlcnR5LnJlcXVpcmVkID09PSAndHJ1ZScpIHtcbiAgICAgICAgX2VudW0ucHVzaChwcm9wZXJ0eU5hbWUpO1xuICAgICAgfVxuICAgICAgc2NoZW1hLnByb3BlcnRpZXNbcHJvcGVydHlOYW1lXSA9IHByb3BlcnR5O1xuICAgIH1cbiAgICBpZihfZW51bS5sZW5ndGggPiAwKSB7XG4gICAgICBzY2hlbWFbJ2VudW0nXSA9IF9lbnVtO1xuICAgIH1cbiAgICBzd2FnZ2VyLmRlZmluaXRpb25zW25hbWVdID0gc2NoZW1hO1xuICB9XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUuZXh0cmFjdFRhZyA9IGZ1bmN0aW9uKHJlc291cmNlUGF0aCkge1xuICB2YXIgcGF0aFN0cmluZyA9IHJlc291cmNlUGF0aCB8fCAnZGVmYXVsdCc7XG4gIGlmKHBhdGhTdHJpbmcuaW5kZXhPZignaHR0cDonKSA9PT0gMCB8fCBwYXRoU3RyaW5nLmluZGV4T2YoJ2h0dHBzOicpID09PSAwKSB7XG4gICAgcGF0aFN0cmluZyA9IHBhdGhTdHJpbmcuc3BsaXQoWycvJ10pO1xuICAgIHBhdGhTdHJpbmcgPSBwYXRoU3RyaW5nW3BhdGhTdHJpbmcubGVuZ3RoIC0xXS5zdWJzdHJpbmcoKTtcbiAgfVxuICByZXR1cm4gcGF0aFN0cmluZy5yZXBsYWNlKCcvJywnJyk7XG59XG5cblN3YWdnZXJTcGVjQ29udmVydGVyLnByb3RvdHlwZS5vcGVyYXRpb25zID0gZnVuY3Rpb24ocGF0aCwgcmVzb3VyY2VQYXRoLCBvYmosIHJlc291cmNlTGV2ZWxBdXRoLCBzd2FnZ2VyKSB7XG4gIGlmKCFBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgdmFyIGk7XG5cbiAgaWYoIXN3YWdnZXIucGF0aHMpIHtcbiAgICBzd2FnZ2VyLnBhdGhzID0ge307XG4gIH1cblxuICB2YXIgcGF0aE9iaiA9IHN3YWdnZXIucGF0aHNbcGF0aF0gfHwge307XG4gIHZhciB0YWcgPSB0aGlzLmV4dHJhY3RUYWcocmVzb3VyY2VQYXRoKTtcbiAgc3dhZ2dlci50YWdzID0gc3dhZ2dlci50YWdzIHx8IFtdO1xuICB2YXIgbWF0Y2hlZCA9IGZhbHNlO1xuICBmb3IoaSA9IDA7IGkgPCBzd2FnZ2VyLnRhZ3MubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgdGFnT2JqZWN0ID0gc3dhZ2dlci50YWdzW2ldO1xuICAgIGlmKHRhZ09iamVjdC5uYW1lID09PSB0YWcpIHtcbiAgICAgIG1hdGNoZWQgPSB0cnVlO1xuICAgIH1cbiAgfVxuICBpZighbWF0Y2hlZCkge1xuICAgIHN3YWdnZXIudGFncy5wdXNoKHtuYW1lOiB0YWd9KTtcbiAgfVxuXG4gIGZvcihpID0gMDsgaSA8IG9iai5sZW5ndGg7IGkrKykge1xuICAgIHZhciBleGlzdGluZ09wZXJhdGlvbiA9IG9ialtpXTtcbiAgICB2YXIgbWV0aG9kID0gKGV4aXN0aW5nT3BlcmF0aW9uLm1ldGhvZCB8fCBleGlzdGluZ09wZXJhdGlvbi5odHRwTWV0aG9kKS50b0xvd2VyQ2FzZSgpO1xuICAgIHZhciBvcGVyYXRpb24gPSB7dGFnczogW3RhZ119O1xuICAgIHZhciBleGlzdGluZ0F1dGhvcml6YXRpb25zID0gZXhpc3RpbmdPcGVyYXRpb24uYXV0aG9yaXphdGlvbnM7XG5cbiAgICBpZihleGlzdGluZ0F1dGhvcml6YXRpb25zICYmIE9iamVjdC5rZXlzKGV4aXN0aW5nQXV0aG9yaXphdGlvbnMpLmxlbmd0aCA9PT0gMCkge1xuICAgICAgZXhpc3RpbmdBdXRob3JpemF0aW9ucyA9IHJlc291cmNlTGV2ZWxBdXRoO1xuICAgIH1cblxuICAgIGlmKHR5cGVvZiBleGlzdGluZ0F1dGhvcml6YXRpb25zICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgZm9yKHZhciBrZXkgaW4gZXhpc3RpbmdBdXRob3JpemF0aW9ucykge1xuICAgICAgICBvcGVyYXRpb24uc2VjdXJpdHkgPSBvcGVyYXRpb24uc2VjdXJpdHkgfHwgW107XG4gICAgICAgIHZhciBzY29wZXMgPSBleGlzdGluZ0F1dGhvcml6YXRpb25zW2tleV07XG4gICAgICAgIGlmKHNjb3Blcykge1xuICAgICAgICAgIHZhciBzZWN1cml0eVNjb3BlcyA9IFtdO1xuICAgICAgICAgIGZvcih2YXIgaiBpbiBzY29wZXMpIHtcbiAgICAgICAgICAgIHNlY3VyaXR5U2NvcGVzLnB1c2goc2NvcGVzW2pdLnNjb3BlKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdmFyIHNjb3Blc09iamVjdCA9IHt9O1xuICAgICAgICAgIHNjb3Blc09iamVjdFtrZXldID0gc2VjdXJpdHlTY29wZXM7XG4gICAgICAgICAgb3BlcmF0aW9uLnNlY3VyaXR5LnB1c2goc2NvcGVzT2JqZWN0KTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICB2YXIgc2NvcGVzT2JqZWN0ID0ge307XG4gICAgICAgICAgc2NvcGVzT2JqZWN0W2tleV0gPSBbXTtcbiAgICAgICAgICBvcGVyYXRpb24uc2VjdXJpdHkucHVzaChzY29wZXNPYmplY3QpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYoZXhpc3RpbmdPcGVyYXRpb24uY29uc3VtZXMpIHtcbiAgICAgIG9wZXJhdGlvbi5jb25zdW1lcyA9IGV4aXN0aW5nT3BlcmF0aW9uLmNvbnN1bWVzO1xuICAgIH1cbiAgICBlbHNlIGlmKHN3YWdnZXIuY29uc3VtZXMpIHtcbiAgICAgIG9wZXJhdGlvbi5jb25zdW1lcyA9IHN3YWdnZXIuY29uc3VtZXM7XG4gICAgfVxuICAgIGlmKGV4aXN0aW5nT3BlcmF0aW9uLnByb2R1Y2VzKSB7XG4gICAgICBvcGVyYXRpb24ucHJvZHVjZXMgPSBleGlzdGluZ09wZXJhdGlvbi5wcm9kdWNlcztcbiAgICB9XG4gICAgZWxzZSBpZihzd2FnZ2VyLnByb2R1Y2VzKSB7XG4gICAgICBvcGVyYXRpb24ucHJvZHVjZXMgPSBzd2FnZ2VyLnByb2R1Y2VzO1xuICAgIH1cbiAgICBpZihleGlzdGluZ09wZXJhdGlvbi5zdW1tYXJ5KSB7XG4gICAgICBvcGVyYXRpb24uc3VtbWFyeSA9IGV4aXN0aW5nT3BlcmF0aW9uLnN1bW1hcnk7XG4gICAgfVxuICAgIGlmKGV4aXN0aW5nT3BlcmF0aW9uLm5vdGVzKSB7XG4gICAgICBvcGVyYXRpb24uZGVzY3JpcHRpb24gPSBleGlzdGluZ09wZXJhdGlvbi5ub3RlcztcbiAgICB9XG4gICAgaWYoZXhpc3RpbmdPcGVyYXRpb24ubmlja25hbWUpIHtcbiAgICAgIG9wZXJhdGlvbi5vcGVyYXRpb25JZCA9IGV4aXN0aW5nT3BlcmF0aW9uLm5pY2tuYW1lO1xuICAgIH1cbiAgICBpZihleGlzdGluZ09wZXJhdGlvbi5kZXByZWNhdGVkKSB7XG4gICAgICBvcGVyYXRpb24uZGVwcmVjYXRlZCA9IGV4aXN0aW5nT3BlcmF0aW9uLmRlcHJlY2F0ZWQ7XG4gICAgfVxuXG4gICAgdGhpcy5hdXRob3JpemF0aW9ucyhleGlzdGluZ0F1dGhvcml6YXRpb25zLCBzd2FnZ2VyKTtcbiAgICB0aGlzLnBhcmFtZXRlcnMob3BlcmF0aW9uLCBleGlzdGluZ09wZXJhdGlvbi5wYXJhbWV0ZXJzLCBzd2FnZ2VyKTtcbiAgICB0aGlzLnJlc3BvbnNlTWVzc2FnZXMob3BlcmF0aW9uLCBleGlzdGluZ09wZXJhdGlvbiwgc3dhZ2dlcik7XG5cbiAgICBwYXRoT2JqW21ldGhvZF0gPSBvcGVyYXRpb247XG4gIH1cblxuICBzd2FnZ2VyLnBhdGhzW3BhdGhdID0gcGF0aE9iajtcbn07XG5cblN3YWdnZXJTcGVjQ29udmVydGVyLnByb3RvdHlwZS5yZXNwb25zZU1lc3NhZ2VzID0gZnVuY3Rpb24ob3BlcmF0aW9uLCBleGlzdGluZ09wZXJhdGlvbiwgc3dhZ2dlcikge1xuICBpZih0eXBlb2YgZXhpc3RpbmdPcGVyYXRpb24gIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIGJ1aWxkIGRlZmF1bHQgcmVzcG9uc2UgZnJvbSB0aGUgb3BlcmF0aW9uICgxLngpXG4gIHZhciBkZWZhdWx0UmVzcG9uc2UgPSB7fTtcbiAgdGhpcy5kYXRhVHlwZShleGlzdGluZ09wZXJhdGlvbiwgZGVmYXVsdFJlc3BvbnNlKTtcbiAgaWYoIWRlZmF1bHRSZXNwb25zZS5zY2hlbWEpIHtcbiAgICBkZWZhdWx0UmVzcG9uc2UgPSB7c2NoZW1hOiBkZWZhdWx0UmVzcG9uc2V9O1xuICB9XG5cbiAgb3BlcmF0aW9uLnJlc3BvbnNlcyA9IG9wZXJhdGlvbi5yZXNwb25zZXMgfHwge307XG5cbiAgLy8gZ3JhYiBmcm9tIHJlc3BvbnNlTWVzc2FnZXMgKDEuMilcbiAgdmFyIGhhczIwMCA9IGZhbHNlO1xuICBpZihBcnJheS5pc0FycmF5KGV4aXN0aW5nT3BlcmF0aW9uLnJlc3BvbnNlTWVzc2FnZXMpKSB7XG4gICAgdmFyIGk7XG4gICAgdmFyIGV4aXN0aW5nUmVzcG9uc2VzID0gZXhpc3RpbmdPcGVyYXRpb24ucmVzcG9uc2VNZXNzYWdlcztcbiAgICBmb3IoaSA9IDA7IGkgPCBleGlzdGluZ1Jlc3BvbnNlcy5sZW5ndGg7IGkrKykge1xuICAgICAgdmFyIGV4aXN0aW5nUmVzcG9uc2UgPSBleGlzdGluZ1Jlc3BvbnNlc1tpXTtcbiAgICAgIHZhciByZXNwb25zZSA9IHsgZGVzY3JpcHRpb246IGV4aXN0aW5nUmVzcG9uc2UubWVzc2FnZSB9O1xuICAgICAgaWYoZXhpc3RpbmdSZXNwb25zZS5jb2RlID09PSAyMDApIHtcbiAgICAgICAgaGFzMjAwID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIG9wZXJhdGlvbi5yZXNwb25zZXNbJycgKyBleGlzdGluZ1Jlc3BvbnNlLmNvZGVdID0gcmVzcG9uc2U7XG4gICAgICAvLyBUT0RPOiBzY2hlbWFcbiAgICB9XG4gIH1cblxuICBpZihoYXMyMDApIHtcbiAgICBvcGVyYXRpb24ucmVzcG9uc2VzWydkZWZhdWx0J10gPSBkZWZhdWx0UmVzcG9uc2U7XG4gIH1cbiAgZWxzZSB7XG4gICAgb3BlcmF0aW9uLnJlc3BvbnNlc1snMjAwJ10gPSBkZWZhdWx0UmVzcG9uc2U7XG4gIH1cbn07XG5cblN3YWdnZXJTcGVjQ29udmVydGVyLnByb3RvdHlwZS5hdXRob3JpemF0aW9ucyA9IGZ1bmN0aW9uKG9iaiwgc3dhZ2dlcikge1xuICAvLyBUT0RPXG4gIGlmKHR5cGVvZiBvYmogIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuO1xuICB9XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUucGFyYW1ldGVycyA9IGZ1bmN0aW9uKG9wZXJhdGlvbiwgb2JqLCBzd2FnZ2VyKSB7XG4gIGlmKCFBcnJheS5pc0FycmF5KG9iaikpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgdmFyIGk7XG4gIGZvcihpID0gMDsgaSA8IG9iai5sZW5ndGg7IGkrKykge1xuICAgIHZhciBleGlzdGluZ1BhcmFtZXRlciA9IG9ialtpXTtcbiAgICB2YXIgcGFyYW1ldGVyID0ge307XG4gICAgcGFyYW1ldGVyLm5hbWUgPSBleGlzdGluZ1BhcmFtZXRlci5uYW1lO1xuICAgIHBhcmFtZXRlci5kZXNjcmlwdGlvbiA9IGV4aXN0aW5nUGFyYW1ldGVyLmRlc2NyaXB0aW9uO1xuICAgIHBhcmFtZXRlci5yZXF1aXJlZCA9IGV4aXN0aW5nUGFyYW1ldGVyLnJlcXVpcmVkO1xuICAgIHBhcmFtZXRlci5pbiA9IGV4aXN0aW5nUGFyYW1ldGVyLnBhcmFtVHlwZTtcblxuICAgIC8vIHBlciAjMTY4XG4gICAgaWYocGFyYW1ldGVyLmluID09PSAnYm9keScpIHtcbiAgICAgIHBhcmFtZXRlci5uYW1lID0gJ2JvZHknO1xuICAgIH1cbiAgICBpZihwYXJhbWV0ZXIuaW4gPT09ICdmb3JtJykge1xuICAgICAgcGFyYW1ldGVyLmluID0gJ2Zvcm1EYXRhJztcbiAgICB9XG5cbiAgICBpZihleGlzdGluZ1BhcmFtZXRlci5hbGxvd011bHRpcGxlID09PSB0cnVlIHx8IGV4aXN0aW5nUGFyYW1ldGVyLmFsbG93TXVsdGlwbGUgPT09ICd0cnVlJykge1xuICAgICAgdmFyIGlubmVyVHlwZSA9IHt9O1xuICAgICAgdGhpcy5kYXRhVHlwZShleGlzdGluZ1BhcmFtZXRlciwgaW5uZXJUeXBlKTtcbiAgICAgIHBhcmFtZXRlci50eXBlID0gJ2FycmF5JztcbiAgICAgIHBhcmFtZXRlci5pdGVtcyA9IGlubmVyVHlwZTtcblxuICAgICAgaWYoZXhpc3RpbmdQYXJhbWV0ZXIuYWxsb3dhYmxlVmFsdWVzKSB7XG4gICAgICAgIHZhciBhdiA9IGV4aXN0aW5nUGFyYW1ldGVyLmFsbG93YWJsZVZhbHVlcztcbiAgICAgICAgaWYoYXYudmFsdWVUeXBlID09PSAnTElTVCcpIHtcbiAgICAgICAgICBwYXJhbWV0ZXJbJ2VudW0nXSA9IGF2LnZhbHVlcztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHRoaXMuZGF0YVR5cGUoZXhpc3RpbmdQYXJhbWV0ZXIsIHBhcmFtZXRlcik7XG4gICAgfVxuXG4gICAgb3BlcmF0aW9uLnBhcmFtZXRlcnMgPSBvcGVyYXRpb24ucGFyYW1ldGVycyB8fCBbXTtcbiAgICBvcGVyYXRpb24ucGFyYW1ldGVycy5wdXNoKHBhcmFtZXRlcik7XG4gIH1cbn07XG5cblN3YWdnZXJTcGVjQ29udmVydGVyLnByb3RvdHlwZS5kYXRhVHlwZSA9IGZ1bmN0aW9uKHNvdXJjZSwgdGFyZ2V0KSB7XG4gIGlmKHR5cGVvZiBzb3VyY2UgIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgaWYoc291cmNlLm1pbmltdW0pIHtcbiAgICB0YXJnZXQubWluaW11bSA9IHNvdXJjZS5taW5pbXVtO1xuICB9XG4gIGlmKHNvdXJjZS5tYXhpbXVtKSB7XG4gICAgdGFyZ2V0Lm1heGltdW0gPSBzb3VyY2UubWF4aW11bTtcbiAgfVxuICBpZihzb3VyY2UuZGVmYXVsdFZhbHVlKSB7XG4gICAgdGFyZ2V0LmRlZmF1bHQgPSBzb3VyY2UuZGVmYXVsdFZhbHVlO1xuICB9XG5cbiAgdmFyIGpzb25TY2hlbWFUeXBlID0gdGhpcy50b0pzb25TY2hlbWEoc291cmNlKTtcbiAgaWYoanNvblNjaGVtYVR5cGUpIHtcbiAgICB0YXJnZXQgPSB0YXJnZXQgfHwge307XG4gICAgaWYoanNvblNjaGVtYVR5cGUudHlwZSkge1xuICAgICAgdGFyZ2V0LnR5cGUgPSBqc29uU2NoZW1hVHlwZS50eXBlO1xuICAgIH1cbiAgICBpZihqc29uU2NoZW1hVHlwZS5mb3JtYXQpIHtcbiAgICAgIHRhcmdldC5mb3JtYXQgPSBqc29uU2NoZW1hVHlwZS5mb3JtYXQ7XG4gICAgfVxuICAgIGlmKGpzb25TY2hlbWFUeXBlLiRyZWYpIHtcbiAgICAgIHRhcmdldC5zY2hlbWEgPSB7JHJlZjoganNvblNjaGVtYVR5cGUuJHJlZn07XG4gICAgfVxuICAgIGlmKGpzb25TY2hlbWFUeXBlLml0ZW1zKSB7XG4gICAgICB0YXJnZXQuaXRlbXMgPSBqc29uU2NoZW1hVHlwZS5pdGVtcztcbiAgICB9XG4gIH1cbn07XG5cblN3YWdnZXJTcGVjQ29udmVydGVyLnByb3RvdHlwZS50b0pzb25TY2hlbWEgPSBmdW5jdGlvbihzb3VyY2UpIHtcbiAgaWYoIXNvdXJjZSkge1xuICAgIHJldHVybiAnb2JqZWN0JztcbiAgfVxuICB2YXIgZGV0ZWN0ZWRUeXBlID0gKHNvdXJjZS50eXBlIHx8IHNvdXJjZS5kYXRhVHlwZSB8fCBzb3VyY2UucmVzcG9uc2VDbGFzcyB8fCAnJyk7XG4gIHZhciBsY1R5cGUgPSBkZXRlY3RlZFR5cGUudG9Mb3dlckNhc2UoKTtcbiAgdmFyIGZvcm1hdCA9IChzb3VyY2UuZm9ybWF0IHx8ICcnKS50b0xvd2VyQ2FzZSgpO1xuXG4gIGlmKGxjVHlwZS5pbmRleE9mKCdsaXN0WycpID09PSAwKSB7XG4gICAgdmFyIGlubmVyVHlwZSA9IGRldGVjdGVkVHlwZS5zdWJzdHJpbmcoNSwgZGV0ZWN0ZWRUeXBlLmxlbmd0aCAtIDEpO1xuICAgIHZhciBqc29uVHlwZSA9IHRoaXMudG9Kc29uU2NoZW1hKHt0eXBlOiBpbm5lclR5cGV9KTtcbiAgICByZXR1cm4ge3R5cGU6ICdhcnJheScsIGl0ZW1zOiBqc29uVHlwZX07XG4gIH1cbiAgZWxzZSBpZihsY1R5cGUgPT09ICdpbnQnIHx8IChsY1R5cGUgPT09ICdpbnRlZ2VyJyAmJiBmb3JtYXQgPT09ICdpbnQzMicpKVxuICAgIHtyZXR1cm4ge3R5cGU6ICdpbnRlZ2VyJywgZm9ybWF0OiAnaW50MzInfTt9XG4gIGVsc2UgaWYobGNUeXBlID09PSAnbG9uZycgfHwgKGxjVHlwZSA9PT0gJ2ludGVnZXInICYmIGZvcm1hdCA9PT0gJ2ludDY0JykpXG4gICAge3JldHVybiB7dHlwZTogJ2ludGVnZXInLCBmb3JtYXQ6ICdpbnQ2NCd9O31cbiAgZWxzZSBpZihsY1R5cGUgPT09ICdpbnRlZ2VyJylcbiAgICB7cmV0dXJuIHt0eXBlOiAnaW50ZWdlcicsIGZvcm1hdDogJ2ludDY0J307fVxuICBlbHNlIGlmKGxjVHlwZSA9PT0gJ2Zsb2F0JyB8fCAobGNUeXBlID09PSAnbnVtYmVyJyAmJiBmb3JtYXQgPT09ICdmbG9hdCcpKVxuICAgIHtyZXR1cm4ge3R5cGU6ICdudW1iZXInLCBmb3JtYXQ6ICdmbG9hdCd9O31cbiAgZWxzZSBpZihsY1R5cGUgPT09ICdkb3VibGUnIHx8IChsY1R5cGUgPT09ICdudW1iZXInICYmIGZvcm1hdCA9PT0gJ2RvdWJsZScpKVxuICAgIHtyZXR1cm4ge3R5cGU6ICdudW1iZXInLCBmb3JtYXQ6ICdkb3VibGUnfTt9XG4gIGVsc2UgaWYoKGxjVHlwZSA9PT0gJ3N0cmluZycgJiYgZm9ybWF0ID09PSAnZGF0ZS10aW1lJykgfHwgKGxjVHlwZSA9PT0gJ2RhdGUnKSlcbiAgICB7cmV0dXJuIHt0eXBlOiAnc3RyaW5nJywgZm9ybWF0OiAnZGF0ZS10aW1lJ307fVxuICBlbHNlIGlmKGxjVHlwZSA9PT0gJ3N0cmluZycpXG4gICAge3JldHVybiB7dHlwZTogJ3N0cmluZyd9O31cbiAgZWxzZSBpZihsY1R5cGUgPT09ICdmaWxlJylcbiAgICB7cmV0dXJuIHt0eXBlOiAnZmlsZSd9O31cbiAgZWxzZSBpZihsY1R5cGUgPT09ICdib29sZWFuJylcbiAgICB7cmV0dXJuIHt0eXBlOiAnYm9vbGVhbid9O31cbiAgZWxzZSBpZihsY1R5cGUgPT09ICdhcnJheScgfHwgbGNUeXBlID09PSAnbGlzdCcpIHtcbiAgICBpZihzb3VyY2UuaXRlbXMpIHtcbiAgICAgIHZhciBpdCA9IHRoaXMudG9Kc29uU2NoZW1hKHNvdXJjZS5pdGVtcyk7XG4gICAgICByZXR1cm4ge3R5cGU6ICdhcnJheScsIGl0ZW1zOiBpdH07XG4gICAgfVxuICAgIGVsc2Uge1xuICAgICAgcmV0dXJuIHt0eXBlOiAnYXJyYXknLCBpdGVtczoge3R5cGU6ICdvYmplY3QnfX07XG4gICAgfVxuICB9XG4gIGVsc2UgaWYoc291cmNlLiRyZWYpIHtcbiAgICByZXR1cm4geyRyZWY6ICcjL2RlZmluaXRpb25zLycgKyB0aGlzLm1vZGVsTWFwW3NvdXJjZS4kcmVmXSB8fCBzb3VyY2UuJHJlZn07XG4gIH1cbiAgZWxzZSB7XG4gICAgcmV0dXJuIHskcmVmOiAnIy9kZWZpbml0aW9ucy8nICsgdGhpcy5tb2RlbE1hcFtzb3VyY2UudHlwZV0gfHwgc291cmNlLnR5cGV9O1xuICB9XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUucmVzb3VyY2VMaXN0aW5nID0gZnVuY3Rpb24ob2JqLCBzd2FnZ2VyLCBjYWxsYmFjaykge1xuICB2YXIgaSwgcHJvY2Vzc2VkQ291bnQgPSAwO1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIHZhciBleHBlY3RlZENvdW50ID0gb2JqLmFwaXMubGVuZ3RoO1xuICB2YXIgX3N3YWdnZXIgPSBzd2FnZ2VyO1xuXG4gIGlmKGV4cGVjdGVkQ291bnQgPT09IDApIHtcbiAgICB0aGlzLmZpbmlzaChjYWxsYmFjaywgc3dhZ2dlcik7XG4gIH1cblxuICBmb3IoaSA9IDA7IGkgPCBleHBlY3RlZENvdW50OyBpKyspIHtcbiAgICB2YXIgYXBpID0gb2JqLmFwaXNbaV07XG4gICAgdmFyIHBhdGggPSBhcGkucGF0aDtcbiAgICB2YXIgYWJzb2x1dGVQYXRoID0gdGhpcy5nZXRBYnNvbHV0ZVBhdGgob2JqLnN3YWdnZXJWZXJzaW9uLCB0aGlzLmRvY0xvY2F0aW9uLCBwYXRoKTtcblxuICAgIGlmKGFwaS5kZXNjcmlwdGlvbikge1xuICAgICAgc3dhZ2dlci50YWdzID0gc3dhZ2dlci50YWdzIHx8IFtdO1xuICAgICAgc3dhZ2dlci50YWdzLnB1c2goe1xuICAgICAgICBuYW1lIDogdGhpcy5leHRyYWN0VGFnKGFwaS5wYXRoKSxcbiAgICAgICAgZGVzY3JpcHRpb24gOiBhcGkuZGVzY3JpcHRpb24gfHwgJydcbiAgICAgIH0pO1xuICAgIH07XG4gICAgdmFyIGh0dHAgPSB7XG4gICAgICB1cmw6IGFic29sdXRlUGF0aCxcbiAgICAgIGhlYWRlcnM6IHthY2NlcHQ6ICdhcHBsaWNhdGlvbi9qc29uJ30sXG4gICAgICBvbjoge30sXG4gICAgICBtZXRob2Q6ICdnZXQnXG4gICAgfTtcbiAgICBodHRwLm9uLnJlc3BvbnNlID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgcHJvY2Vzc2VkQ291bnQgKz0gMTtcbiAgICAgIGlmKGRhdGEub2JqKSB7XG4gICAgICAgIHNlbGYuZGVjbGFyYXRpb24oZGF0YS5vYmosIF9zd2FnZ2VyKTtcbiAgICAgIH1cbiAgICAgIGlmKHByb2Nlc3NlZENvdW50ID09PSBleHBlY3RlZENvdW50KSB7XG4gICAgICAgIHNlbGYuZmluaXNoKGNhbGxiYWNrLCBfc3dhZ2dlcik7XG4gICAgICB9XG4gICAgfTtcbiAgICBodHRwLm9uLmVycm9yID0gZnVuY3Rpb24oZGF0YSkge1xuICAgICAgY29uc29sZS5lcnJvcihkYXRhKTtcbiAgICAgIHByb2Nlc3NlZENvdW50ICs9IDE7XG4gICAgICBpZihwcm9jZXNzZWRDb3VudCA9PT0gZXhwZWN0ZWRDb3VudCkge1xuICAgICAgICBzZWxmLmZpbmlzaChjYWxsYmFjaywgX3N3YWdnZXIpO1xuICAgICAgfVxuICAgIH07XG4gICAgbmV3IFN3YWdnZXJIdHRwKCkuZXhlY3V0ZShodHRwKTtcbiAgfVxufTtcblxuU3dhZ2dlclNwZWNDb252ZXJ0ZXIucHJvdG90eXBlLmdldEFic29sdXRlUGF0aCA9IGZ1bmN0aW9uKHZlcnNpb24sIGRvY0xvY2F0aW9uLCBwYXRoKSAge1xuICBpZih2ZXJzaW9uID09PSAnMS4wJykge1xuICAgIGlmKGRvY0xvY2F0aW9uLmVuZHNXaXRoKCcuanNvbicpKSB7XG4gICAgICAvLyBnZXQgcm9vdCBwYXRoXG4gICAgICB2YXIgcG9zID0gZG9jTG9jYXRpb24ubGFzdEluZGV4T2YoJy8nKTtcbiAgICAgIGlmKHBvcyA+IDApIHtcbiAgICAgICAgZG9jTG9jYXRpb24gPSBkb2NMb2NhdGlvbi5zdWJzdHJpbmcoMCwgcG9zKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICB2YXIgbG9jYXRpb24gPSBkb2NMb2NhdGlvbjtcbiAgaWYocGF0aC5pbmRleE9mKCdodHRwOi8vJykgPT09IDAgfHwgcGF0aC5pbmRleE9mKCdodHRwczovLycpID09PSAwKSB7XG4gICAgbG9jYXRpb24gPSBwYXRoO1xuICB9XG4gIGVsc2Uge1xuICAgIGlmKGRvY0xvY2F0aW9uLmVuZHNXaXRoKCcvJykpIHtcbiAgICAgIGxvY2F0aW9uID0gZG9jTG9jYXRpb24uc3Vic3RyaW5nKDAsIGRvY0xvY2F0aW9uLmxlbmd0aCAtIDEpO1xuICAgIH1cbiAgICBsb2NhdGlvbiArPSBwYXRoO1xuICB9XG4gIGxvY2F0aW9uID0gbG9jYXRpb24ucmVwbGFjZSgne2Zvcm1hdH0nLCAnanNvbicpO1xuICByZXR1cm4gbG9jYXRpb247XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUuc2VjdXJpdHlEZWZpbml0aW9ucyA9IGZ1bmN0aW9uKG9iaiwgc3dhZ2dlcikge1xuICBpZihvYmouYXV0aG9yaXphdGlvbnMpIHtcbiAgICB2YXIgbmFtZTtcbiAgICBmb3IobmFtZSBpbiBvYmouYXV0aG9yaXphdGlvbnMpIHtcbiAgICAgIHZhciBpc1ZhbGlkID0gZmFsc2U7XG4gICAgICB2YXIgc2VjdXJpdHlEZWZpbml0aW9uID0ge307XG4gICAgICB2YXIgZGVmaW5pdGlvbiA9IG9iai5hdXRob3JpemF0aW9uc1tuYW1lXTtcbiAgICAgIGlmKGRlZmluaXRpb24udHlwZSA9PT0gJ2FwaUtleScpIHtcbiAgICAgICAgc2VjdXJpdHlEZWZpbml0aW9uLnR5cGUgPSAnYXBpS2V5JztcbiAgICAgICAgc2VjdXJpdHlEZWZpbml0aW9uLmluID0gZGVmaW5pdGlvbi5wYXNzQXM7XG4gICAgICAgIHNlY3VyaXR5RGVmaW5pdGlvbi5uYW1lID0gZGVmaW5pdGlvbi5rZXluYW1lIHx8IG5hbWU7XG4gICAgICAgIGlzVmFsaWQgPSB0cnVlO1xuICAgICAgfVxuICAgICAgZWxzZSBpZihkZWZpbml0aW9uLnR5cGUgPT09ICdvYXV0aDInKSB7XG4gICAgICAgIHZhciBleGlzdGluZ1Njb3BlcyA9IGRlZmluaXRpb24uc2NvcGVzIHx8IFtdO1xuICAgICAgICB2YXIgc2NvcGVzID0ge307XG4gICAgICAgIHZhciBpO1xuICAgICAgICBmb3IoaSBpbiBleGlzdGluZ1Njb3Blcykge1xuICAgICAgICAgIHZhciBzY29wZSA9IGV4aXN0aW5nU2NvcGVzW2ldO1xuICAgICAgICAgIHNjb3Blc1tzY29wZS5zY29wZV0gPSBzY29wZS5kZXNjcmlwdGlvbjtcbiAgICAgICAgfVxuICAgICAgICBzZWN1cml0eURlZmluaXRpb24udHlwZSA9ICdvYXV0aDInO1xuICAgICAgICBpZihpID4gMCkge1xuICAgICAgICAgIHNlY3VyaXR5RGVmaW5pdGlvbi5zY29wZXMgPSBzY29wZXM7ICAgICAgICBcbiAgICAgICAgfVxuICAgICAgICBpZihkZWZpbml0aW9uLmdyYW50VHlwZXMpIHtcbiAgICAgICAgICBpZihkZWZpbml0aW9uLmdyYW50VHlwZXMuaW1wbGljaXQpIHtcbiAgICAgICAgICAgIHZhciBpbXBsaWNpdCA9IGRlZmluaXRpb24uZ3JhbnRUeXBlcy5pbXBsaWNpdDtcbiAgICAgICAgICAgIHNlY3VyaXR5RGVmaW5pdGlvbi5mbG93ID0gJ2ltcGxpY2l0JztcbiAgICAgICAgICAgIHNlY3VyaXR5RGVmaW5pdGlvbi5hdXRob3JpemF0aW9uVXJsID0gaW1wbGljaXQubG9naW5FbmRwb2ludDtcbiAgICAgICAgICAgIGlzVmFsaWQgPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZihkZWZpbml0aW9uLmdyYW50VHlwZXMuYXV0aG9yaXphdGlvbl9jb2RlKSB7XG4gICAgICAgICAgICBpZighc2VjdXJpdHlEZWZpbml0aW9uLmZsb3cpIHtcbiAgICAgICAgICAgICAgLy8gY2Fubm90IHNldCBpZiBmbG93IGlzIGFscmVhZHkgZGVmaW5lZFxuICAgICAgICAgICAgICB2YXIgYXV0aENvZGUgPSBkZWZpbml0aW9uLmdyYW50VHlwZXMuYXV0aG9yaXphdGlvbl9jb2RlO1xuICAgICAgICAgICAgICBzZWN1cml0eURlZmluaXRpb24uZmxvdyA9ICdhY2Nlc3NDb2RlJztcbiAgICAgICAgICAgICAgc2VjdXJpdHlEZWZpbml0aW9uLmF1dGhvcml6YXRpb25VcmwgPSBhdXRoQ29kZS50b2tlblJlcXVlc3RFbmRwb2ludC51cmw7XG4gICAgICAgICAgICAgIHNlY3VyaXR5RGVmaW5pdGlvbi50b2tlblVybCA9IGF1dGhDb2RlLnRva2VuRW5kcG9pbnQudXJsO1xuICAgICAgICAgICAgICBpc1ZhbGlkID0gdHJ1ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmKGlzVmFsaWQpIHtcbiAgICAgICAgc3dhZ2dlci5zZWN1cml0eURlZmluaXRpb25zID0gc3dhZ2dlci5zZWN1cml0eURlZmluaXRpb25zIHx8IHt9O1xuICAgICAgICBzd2FnZ2VyLnNlY3VyaXR5RGVmaW5pdGlvbnNbbmFtZV0gPSBzZWN1cml0eURlZmluaXRpb247XG4gICAgICB9XG4gICAgfVxuICB9XG59O1xuXG5Td2FnZ2VyU3BlY0NvbnZlcnRlci5wcm90b3R5cGUuYXBpSW5mbyA9IGZ1bmN0aW9uKG9iaiwgc3dhZ2dlcikge1xuICAvLyBpbmZvIHNlY3Rpb25cbiAgaWYob2JqLmluZm8pIHtcbiAgICB2YXIgaW5mbyA9IG9iai5pbmZvO1xuICAgIHN3YWdnZXIuaW5mbyA9IHt9O1xuXG4gICAgaWYoaW5mby5jb250YWN0KSB7XG4gICAgICBzd2FnZ2VyLmluZm8uY29udGFjdCA9IHt9O1xuICAgICAgc3dhZ2dlci5pbmZvLmNvbnRhY3QuZW1haWwgPSBpbmZvLmNvbnRhY3Q7XG4gICAgfVxuICAgIGlmKGluZm8uZGVzY3JpcHRpb24pIHtcbiAgICAgIHN3YWdnZXIuaW5mby5kZXNjcmlwdGlvbiA9IGluZm8uZGVzY3JpcHRpb247XG4gICAgfVxuICAgIGlmKGluZm8udGl0bGUpIHtcbiAgICAgIHN3YWdnZXIuaW5mby50aXRsZSA9IGluZm8udGl0bGU7XG4gICAgfVxuICAgIGlmKGluZm8udGVybXNPZlNlcnZpY2VVcmwpIHtcbiAgICAgIHN3YWdnZXIuaW5mby50ZXJtc09mU2VydmljZSA9IGluZm8udGVybXNPZlNlcnZpY2VVcmw7XG4gICAgfVxuICAgIGlmKGluZm8ubGljZW5zZSB8fCBpbmZvLmxpY2Vuc2VVcmwpIHtcbiAgICAgIHN3YWdnZXIubGljZW5zZSA9IHt9O1xuICAgICAgaWYoaW5mby5saWNlbnNlKSB7XG4gICAgICAgIHN3YWdnZXIubGljZW5zZS5uYW1lID0gaW5mby5saWNlbnNlO1xuICAgICAgfVxuICAgICAgaWYoaW5mby5saWNlbnNlVXJsKSB7XG4gICAgICAgIHN3YWdnZXIubGljZW5zZS51cmwgPSBpbmZvLmxpY2Vuc2VVcmw7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGVsc2Uge1xuICAgIHRoaXMud2FybmluZ3MucHVzaCgnbWlzc2luZyBpbmZvIHNlY3Rpb24nKTtcbiAgfVxufTtcblxuU3dhZ2dlclNwZWNDb252ZXJ0ZXIucHJvdG90eXBlLmZpbmlzaCA9IGZ1bmN0aW9uIChjYWxsYmFjaywgb2JqKSB7XG4gIGNhbGxiYWNrKG9iaik7XG59OyIsIid1c2Ugc3RyaWN0JztcblxudmFyIF8gPSB7XG4gIGZvckVhY2g6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvY29sbGVjdGlvbi9mb3JFYWNoJyksXG4gIGluZGV4T2Y6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvYXJyYXkvaW5kZXhPZicpLFxuICBpc0FycmF5OiByZXF1aXJlKCdsb2Rhc2gtY29tcGF0L2xhbmcvaXNBcnJheScpLFxuICBpc1BsYWluT2JqZWN0OiByZXF1aXJlKCdsb2Rhc2gtY29tcGF0L2xhbmcvaXNQbGFpbk9iamVjdCcpLFxuICBpc1N0cmluZzogcmVxdWlyZSgnbG9kYXNoLWNvbXBhdC9sYW5nL2lzU3RyaW5nJyksXG4gIGlzVW5kZWZpbmVkOiByZXF1aXJlKCdsb2Rhc2gtY29tcGF0L2xhbmcvaXNVbmRlZmluZWQnKSxcbiAga2V5czogcmVxdWlyZSgnbG9kYXNoLWNvbXBhdC9vYmplY3Qva2V5cycpLFxuICBtYXA6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvY29sbGVjdGlvbi9tYXAnKVxufTtcbnZhciBoZWxwZXJzID0gcmVxdWlyZSgnLi4vaGVscGVycycpO1xuXG5cbi8qKlxuICogYWxsb3dzIG92ZXJyaWRlIG9mIHRoZSBkZWZhdWx0IHZhbHVlIGJhc2VkIG9uIHRoZSBwYXJhbWV0ZXIgYmVpbmdcbiAqIHN1cHBsaWVkXG4gKiovXG52YXIgYXBwbHlQYXJhbWV0ZXJNYWNybyA9IGZ1bmN0aW9uIChvcGVyYXRpb24sIHBhcmFtZXRlcikge1xuICAvLyBUT0RPIHRoZSByZWZlcmVuY2UgdG8gb3BlcmF0aW9uLmFwaSBpcyBub3QgYXZhaWxhYmxlXG4gIGlmIChvcGVyYXRpb24uYXBpICYmIG9wZXJhdGlvbi5hcGkucGFyYW1ldGVyTWFjcm8pIHtcbiAgICByZXR1cm4gb3BlcmF0aW9uLmFwaS5wYXJhbWV0ZXJNYWNybyhvcGVyYXRpb24sIHBhcmFtZXRlcik7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHBhcmFtZXRlci5kZWZhdWx0VmFsdWU7XG4gIH1cbn07XG5cbi8qKlxuICogYWxsb3dzIG92ZXJyaWRpbmcgdGhlIGRlZmF1bHQgdmFsdWUgb2YgYW4gbW9kZWwgcHJvcGVydHlcbiAqKi9cbnZhciBhcHBseU1vZGVsUHJvcGVydHlNYWNybyA9IGZ1bmN0aW9uIChtb2RlbCwgcHJvcGVydHkpIHtcbiAgLy8gVE9ETyB0aGUgcmVmZXJlbmNlIHRvIG1vZGVsLmFwaSBpcyBub3QgYXZhaWxhYmxlXG4gIGlmIChtb2RlbC5hcGkgJiYgbW9kZWwuYXBpLm1vZGVsUHJvcGVydHlNYWNybykge1xuICAgIHJldHVybiBtb2RlbC5hcGkubW9kZWxQcm9wZXJ0eU1hY3JvKG1vZGVsLCBwcm9wZXJ0eSk7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHByb3BlcnR5LmRlZmF1bHQ7XG4gIH1cbn07XG5cbnZhciBNb2RlbCA9IG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKG5hbWUsIGRlZmluaXRpb24sIG1vZGVscykge1xuICB0aGlzLmRlZmluaXRpb24gPSBkZWZpbml0aW9uIHx8IHt9O1xuICB0aGlzLmlzQXJyYXkgPSBkZWZpbml0aW9uLnR5cGUgPT09ICdhcnJheSc7XG4gIHRoaXMubW9kZWxzID0gbW9kZWxzIHx8IHt9O1xuICB0aGlzLm5hbWUgPSBuYW1lIHx8ICdJbmxpbmUgTW9kZWwnO1xuXG4gIHJldHVybiB0aGlzO1xufTtcblxudmFyIHNjaGVtYVRvSFRNTCA9IGZ1bmN0aW9uIChuYW1lLCBzY2hlbWEsIG1vZGVscykge1xuICB2YXIgc3Ryb25nT3BlbiA9ICc8c3BhbiBjbGFzcz1cInN0cm9uZ1wiPic7XG4gIHZhciBzdHJvbmdDbG9zZSA9ICc8L3NwYW4+JztcbiAgdmFyIHJlZmVyZW5jZXMgPSB7fTtcbiAgdmFyIHNlZW5Nb2RlbHMgPSBbXTtcbiAgdmFyIGlubGluZU1vZGVscyA9IDA7XG4gIHZhciBhZGRSZWZlcmVuY2UgPSBmdW5jdGlvbiAoc2NoZW1hLCBuYW1lLCBza2lwUmVmKSB7XG4gICAgdmFyIG1vZGVsTmFtZSA9IG5hbWU7XG4gICAgdmFyIG1vZGVsO1xuXG4gICAgaWYgKHNjaGVtYS4kcmVmKSB7XG4gICAgICBtb2RlbE5hbWUgPSBoZWxwZXJzLnNpbXBsZVJlZihzY2hlbWEuJHJlZik7XG4gICAgICBtb2RlbCA9IG1vZGVsc1ttb2RlbE5hbWVdO1xuICAgIH0gZWxzZSBpZiAoXy5pc1VuZGVmaW5lZChuYW1lKSkge1xuICAgICAgbW9kZWxOYW1lID0gJ0lubGluZSBNb2RlbCAnICsgKCsraW5saW5lTW9kZWxzKTtcbiAgICAgIG1vZGVsID0gbmV3IE1vZGVsKG1vZGVsTmFtZSwgc2NoZW1hLCBtb2RlbHMpO1xuICAgIH1cblxuICAgIGlmIChza2lwUmVmICE9PSB0cnVlKSB7XG4gICAgICByZWZlcmVuY2VzW21vZGVsTmFtZV0gPSBfLmlzVW5kZWZpbmVkKG1vZGVsKSA/IHt9IDogbW9kZWwuZGVmaW5pdGlvbjtcbiAgICB9XG5cbiAgICByZXR1cm4gbW9kZWxOYW1lO1xuICB9O1xuICB2YXIgcHJpbWl0aXZlVG9IVE1MID0gZnVuY3Rpb24gKHNjaGVtYSkge1xuICAgIHZhciBodG1sID0gJzxzcGFuIGNsYXNzPVwicHJvcFR5cGVcIj4nO1xuICAgIHZhciB0eXBlID0gc2NoZW1hLnR5cGUgfHwgJ29iamVjdCc7XG5cbiAgICBpZiAoc2NoZW1hLiRyZWYpIHtcbiAgICAgIGh0bWwgKz0gYWRkUmVmZXJlbmNlKHNjaGVtYSwgaGVscGVycy5zaW1wbGVSZWYoc2NoZW1hLiRyZWYpKTtcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdvYmplY3QnKSB7XG4gICAgICBpZiAoIV8uaXNVbmRlZmluZWQoc2NoZW1hLnByb3BlcnRpZXMpKSB7XG4gICAgICAgIGh0bWwgKz0gYWRkUmVmZXJlbmNlKHNjaGVtYSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBodG1sICs9ICdvYmplY3QnO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ2FycmF5Jykge1xuICAgICAgaHRtbCArPSAnQXJyYXlbJztcblxuICAgICAgaWYgKF8uaXNBcnJheShzY2hlbWEuaXRlbXMpKSB7XG4gICAgICAgIGh0bWwgKz0gXy5tYXAoc2NoZW1hLml0ZW1zLCBhZGRSZWZlcmVuY2UpLmpvaW4oJywnKTtcbiAgICAgIH0gZWxzZSBpZiAoXy5pc1BsYWluT2JqZWN0KHNjaGVtYS5pdGVtcykpIHtcbiAgICAgICAgaWYgKF8uaXNVbmRlZmluZWQoc2NoZW1hLml0ZW1zLiRyZWYpKSB7XG4gICAgICAgICAgaWYgKCFfLmlzVW5kZWZpbmVkKHNjaGVtYS5pdGVtcy50eXBlKSAmJiBfLmluZGV4T2YoWydhcnJheScsICdvYmplY3QnXSwgc2NoZW1hLml0ZW1zLnR5cGUpID09PSAtMSkge1xuICAgICAgICAgICAgaHRtbCArPSBzY2hlbWEuaXRlbXMudHlwZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaHRtbCArPSBhZGRSZWZlcmVuY2Uoc2NoZW1hLml0ZW1zKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaHRtbCArPSBhZGRSZWZlcmVuY2Uoc2NoZW1hLml0ZW1zLCBoZWxwZXJzLnNpbXBsZVJlZihzY2hlbWEuaXRlbXMuJHJlZikpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBoZWxwZXJzLmxvZygnQXJyYXkgdHlwZVxcJ3MgXFwnaXRlbXNcXCcgc2NoZW1hIGlzIG5vdCBhbiBhcnJheSBvciBhbiBvYmplY3QsIGNhbm5vdCBwcm9jZXNzJyk7XG4gICAgICAgIGh0bWwgKz0gJ29iamVjdCc7XG4gICAgICB9XG5cbiAgICAgIGh0bWwgKz0gJ10nO1xuICAgIH0gZWxzZSB7XG4gICAgICBodG1sICs9IHNjaGVtYS50eXBlO1xuICAgIH1cblxuICAgIGh0bWwgKz0gJzwvc3Bhbj4nO1xuXG4gICAgcmV0dXJuIGh0bWw7XG4gIH07XG4gIHZhciBwcmltaXRpdmVUb09wdGlvbnNIVE1MID0gZnVuY3Rpb24gKHNjaGVtYSwgaHRtbCkge1xuICAgIHZhciBvcHRpb25zID0gJyc7XG4gICAgdmFyIHR5cGUgPSBzY2hlbWEudHlwZSB8fCAnb2JqZWN0JztcbiAgICB2YXIgaXNBcnJheSA9IHR5cGUgPT09ICdhcnJheSc7XG5cbiAgICBpZiAoaXNBcnJheSkge1xuICAgICAgaWYgKF8uaXNQbGFpbk9iamVjdChzY2hlbWEuaXRlbXMpICYmICFfLmlzVW5kZWZpbmVkKHNjaGVtYS5pdGVtcy50eXBlKSkge1xuICAgICAgICB0eXBlID0gc2NoZW1hLml0ZW1zLnR5cGU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0eXBlID0gJ29iamVjdCc7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHNjaGVtYS5kZWZhdWx0KSB7XG4gICAgICBvcHRpb25zICs9IGhlbHBlcnMub3B0aW9uSHRtbCgnRGVmYXVsdCcsIHNjaGVtYS5kZWZhdWx0KTtcbiAgICB9XG5cbiAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICBjYXNlICdzdHJpbmcnOlxuICAgICAgaWYgKHNjaGVtYS5taW5MZW5ndGgpIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ01pbi4gTGVuZ3RoJywgc2NoZW1hLm1pbkxlbmd0aCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChzY2hlbWEubWF4TGVuZ3RoKSB7XG4gICAgICAgIG9wdGlvbnMgKz0gaGVscGVycy5vcHRpb25IdG1sKCdNYXguIExlbmd0aCcsIHNjaGVtYS5tYXhMZW5ndGgpO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2NoZW1hLnBhdHRlcm4pIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ1JlZy4gRXhwLicsIHNjaGVtYS5wYXR0ZXJuKTtcbiAgICAgIH1cbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgJ2ludGVnZXInOlxuICAgIGNhc2UgJ251bWJlcic6XG4gICAgICBpZiAoc2NoZW1hLm1pbmltdW0pIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ01pbi4gVmFsdWUnLCBzY2hlbWEubWluaW11bSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChzY2hlbWEuZXhjbHVzaXZlTWluaW11bSkge1xuICAgICAgICBvcHRpb25zICs9IGhlbHBlcnMub3B0aW9uSHRtbCgnRXhjbHVzaXZlIE1pbi4nLCAndHJ1ZScpO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2NoZW1hLm1heGltdW0pIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ01heC4gVmFsdWUnLCBzY2hlbWEubWF4aW11bSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChzY2hlbWEuZXhjbHVzaXZlTWF4aW11bSkge1xuICAgICAgICBvcHRpb25zICs9IGhlbHBlcnMub3B0aW9uSHRtbCgnRXhjbHVzaXZlIE1heC4nLCAndHJ1ZScpO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2NoZW1hLm11bHRpcGxlT2YpIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ011bHRpcGxlIE9mJywgc2NoZW1hLm11bHRpcGxlT2YpO1xuICAgICAgfVxuXG4gICAgICBicmVhaztcbiAgICB9XG5cbiAgICBpZiAoaXNBcnJheSkge1xuICAgICAgaWYgKHNjaGVtYS5taW5JdGVtcykge1xuICAgICAgICBvcHRpb25zICs9IGhlbHBlcnMub3B0aW9uSHRtbCgnTWluLiBJdGVtcycsIHNjaGVtYS5taW5JdGVtcyk7XG4gICAgICB9XG5cbiAgICAgIGlmIChzY2hlbWEubWF4SXRlbXMpIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ01heC4gSXRlbXMnLCBzY2hlbWEubWF4SXRlbXMpO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2NoZW1hLnVuaXF1ZUl0ZW1zKSB7XG4gICAgICAgIG9wdGlvbnMgKz0gaGVscGVycy5vcHRpb25IdG1sKCdVbmlxdWUgSXRlbXMnLCAndHJ1ZScpO1xuICAgICAgfVxuXG4gICAgICBpZiAoc2NoZW1hLmNvbGxlY3Rpb25Gb3JtYXQpIHtcbiAgICAgICAgb3B0aW9ucyArPSBoZWxwZXJzLm9wdGlvbkh0bWwoJ0NvbGwuIEZvcm1hdCcsIHNjaGVtYS5jb2xsZWN0aW9uRm9ybWF0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoXy5pc1VuZGVmaW5lZChzY2hlbWEuaXRlbXMpKSB7XG4gICAgICBpZiAoXy5pc0FycmF5KHNjaGVtYS5lbnVtKSkge1xuICAgICAgICB2YXIgZW51bVN0cmluZztcblxuICAgICAgICBpZiAodHlwZSA9PT0gJ251bWJlcicgfHwgdHlwZSA9PT0gJ2ludGVnZXInKSB7XG4gICAgICAgICAgZW51bVN0cmluZyA9IHNjaGVtYS5lbnVtLmpvaW4oJywgJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZW51bVN0cmluZyA9ICdcIicgKyBzY2hlbWEuZW51bS5qb2luKCdcIiwgXCInKSArICdcIic7XG4gICAgICAgIH1cblxuICAgICAgICBvcHRpb25zICs9IGhlbHBlcnMub3B0aW9uSHRtbCgnRW51bScsIGVudW1TdHJpbmcpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChvcHRpb25zLmxlbmd0aCA+IDApIHtcbiAgICAgIGh0bWwgPSAnPHNwYW4gY2xhc3M9XCJwcm9wV3JhcFwiPicgKyBodG1sICsgJzx0YWJsZSBjbGFzcz1cIm9wdGlvbnNXcmFwcGVyXCI+PHRyPjx0aCBjb2xzcGFuPVwiMlwiPicgKyB0eXBlICsgJzwvdGg+PC90cj4nICsgb3B0aW9ucyArICc8L3RhYmxlPjwvc3Bhbj4nO1xuICAgIH1cblxuICAgIHJldHVybiBodG1sO1xuICB9O1xuICB2YXIgcHJvY2Vzc01vZGVsID0gZnVuY3Rpb24gKHNjaGVtYSwgbmFtZSkge1xuICAgIHZhciB0eXBlID0gc2NoZW1hLnR5cGUgfHwgJ29iamVjdCc7XG4gICAgdmFyIGlzQXJyYXkgPSBzY2hlbWEudHlwZSA9PT0gJ2FycmF5JztcbiAgICB2YXIgaHRtbCA9IHN0cm9uZ09wZW4gKyBuYW1lICsgJyAnICsgKGlzQXJyYXkgPyAnWycgOiAneycpICsgc3Ryb25nQ2xvc2U7XG5cbiAgICBpZiAobmFtZSkge1xuICAgICAgc2Vlbk1vZGVscy5wdXNoKG5hbWUpO1xuICAgIH1cblxuICAgIGlmIChpc0FycmF5KSB7XG4gICAgICBpZiAoXy5pc0FycmF5KHNjaGVtYS5pdGVtcykpIHtcbiAgICAgICAgaHRtbCArPSAnPGRpdj4nICsgXy5tYXAoc2NoZW1hLml0ZW1zLCBmdW5jdGlvbiAoaXRlbSkge1xuICAgICAgICAgIHZhciB0eXBlID0gaXRlbS50eXBlIHx8ICdvYmplY3QnO1xuXG4gICAgICAgICAgaWYgKF8uaXNVbmRlZmluZWQoaXRlbS4kcmVmKSkge1xuICAgICAgICAgICAgaWYgKF8uaW5kZXhPZihbJ2FycmF5JywgJ29iamVjdCddLCB0eXBlKSA+IC0xKSB7XG4gICAgICAgICAgICAgIGlmICh0eXBlID09PSAnb2JqZWN0JyAmJiBfLmlzVW5kZWZpbmVkKGl0ZW0ucHJvcGVydGllcykpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gJ29iamVjdCc7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGFkZFJlZmVyZW5jZShpdGVtKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHByaW1pdGl2ZVRvT3B0aW9uc0hUTUwoaXRlbSwgdHlwZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBhZGRSZWZlcmVuY2UoaXRlbSwgaGVscGVycy5zaW1wbGVSZWYoaXRlbS4kcmVmKSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KS5qb2luKCcsPC9kaXY+PGRpdj4nKTtcbiAgICAgIH0gZWxzZSBpZiAoXy5pc1BsYWluT2JqZWN0KHNjaGVtYS5pdGVtcykpIHtcbiAgICAgICAgaWYgKF8uaXNVbmRlZmluZWQoc2NoZW1hLml0ZW1zLiRyZWYpKSB7XG4gICAgICAgICAgaWYgKF8uaW5kZXhPZihbJ2FycmF5JywgJ29iamVjdCddLCBzY2hlbWEuaXRlbXMudHlwZSB8fCAnb2JqZWN0JykgPiAtMSkge1xuICAgICAgICAgICAgaWYgKChfLmlzVW5kZWZpbmVkKHNjaGVtYS5pdGVtcy50eXBlKSB8fCBzY2hlbWEuaXRlbXMudHlwZSA9PT0gJ29iamVjdCcpICYmIF8uaXNVbmRlZmluZWQoc2NoZW1hLml0ZW1zLnByb3BlcnRpZXMpKSB7XG4gICAgICAgICAgICAgIGh0bWwgKz0gJzxkaXY+b2JqZWN0PC9kaXY+JztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGh0bWwgKz0gJzxkaXY+JyArIGFkZFJlZmVyZW5jZShzY2hlbWEuaXRlbXMpICsgJzwvZGl2Pic7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGh0bWwgKz0gJzxkaXY+JyArIHByaW1pdGl2ZVRvT3B0aW9uc0hUTUwoc2NoZW1hLml0ZW1zLCBzY2hlbWEuaXRlbXMudHlwZSkgKyAnPC9kaXY+JztcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaHRtbCArPSAnPGRpdj4nICsgYWRkUmVmZXJlbmNlKHNjaGVtYS5pdGVtcywgaGVscGVycy5zaW1wbGVSZWYoc2NoZW1hLml0ZW1zLiRyZWYpKSArICc8L2Rpdj4nO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBoZWxwZXJzLmxvZygnQXJyYXkgdHlwZVxcJ3MgXFwnaXRlbXNcXCcgcHJvcGVydHkgaXMgbm90IGFuIGFycmF5IG9yIGFuIG9iamVjdCwgY2Fubm90IHByb2Nlc3MnKTtcbiAgICAgICAgaHRtbCArPSAnPGRpdj5vYmplY3Q8L2Rpdj4nO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoc2NoZW1hLiRyZWYpIHtcbiAgICAgICAgaHRtbCArPSAnPGRpdj4nICsgYWRkUmVmZXJlbmNlKHNjaGVtYSwgbmFtZSkgKyAnPC9kaXY+JztcbiAgICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ29iamVjdCcpIHtcbiAgICAgICAgaHRtbCArPSAnPGRpdj4nO1xuXG4gICAgICAgIGlmIChfLmlzUGxhaW5PYmplY3Qoc2NoZW1hLnByb3BlcnRpZXMpKSB7XG4gICAgICAgICAgaHRtbCArPSBfLm1hcChzY2hlbWEucHJvcGVydGllcywgZnVuY3Rpb24gKHByb3BlcnR5LCBuYW1lKSB7XG4gICAgICAgICAgICB2YXIgcmVxdWlyZWQgPSBfLmluZGV4T2Yoc2NoZW1hLnJlcXVpcmVkIHx8IFtdLCBuYW1lKSA+IC0xO1xuICAgICAgICAgICAgdmFyIGh0bWwgPSAnPHNwYW4gY2xhc3M9XCJwcm9wTmFtZSAnICsgcmVxdWlyZWQgKyAnXCI+JyArIG5hbWUgKyAnPC9zcGFuPiAoJztcblxuICAgICAgICAgICAgaHRtbCArPSBwcmltaXRpdmVUb0hUTUwocHJvcGVydHkpO1xuXG4gICAgICAgICAgICBpZiAoIXJlcXVpcmVkKSB7XG4gICAgICAgICAgICAgIGh0bWwgKz0gJywgPHNwYW4gY2xhc3M9XCJwcm9wT3B0S2V5XCI+b3B0aW9uYWw8L3NwYW4+JztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaHRtbCArPSAnKSc7XG5cbiAgICAgICAgICAgIGlmICghXy5pc1VuZGVmaW5lZChwcm9wZXJ0eS5kZXNjcmlwdGlvbikpIHtcbiAgICAgICAgICAgICAgaHRtbCArPSAnOiAnICsgcHJvcGVydHkuZGVzY3JpcHRpb247XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChwcm9wZXJ0eS5lbnVtKSB7XG4gICAgICAgICAgICAgIGh0bWwgKz0gJyA9IDxzcGFuIGNsYXNzPVwicHJvcFZhbHNcIj5bXFwnJyArIHByb3BlcnR5LmVudW0uam9pbignXFwnIG9yIFxcJycpICsgJ1xcJ108L3NwYW4+JztcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHByaW1pdGl2ZVRvT3B0aW9uc0hUTUwocHJvcGVydHksIGh0bWwpO1xuICAgICAgICAgIH0pLmpvaW4oJyw8L2Rpdj48ZGl2PicpO1xuICAgICAgICB9XG5cbiAgICAgICAgaHRtbCArPSAnPC9kaXY+JztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGh0bWwgPSAnPGRpdj4nICsgcHJpbWl0aXZlVG9PcHRpb25zSFRNTChzY2hlbWEsIHR5cGUpICsgJzwvZGl2Pic7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGh0bWwgKyBzdHJvbmdPcGVuICsgKGlzQXJyYXkgPyAnXScgOiAnfScpICsgc3Ryb25nQ2xvc2U7XG4gIH07XG5cbiAgXG4gIC8vIEdlbmVyYXRlIGN1cnJlbnQgSFRNTFxuICB2YXIgaHRtbCA9IHByb2Nlc3NNb2RlbChzY2hlbWEsIG5hbWUpO1xuICBcbiAgLy8gR2VuZXJhdGUgcmVmZXJlbmNlcyBIVE1MXG4gIHdoaWxlIChfLmtleXMocmVmZXJlbmNlcykubGVuZ3RoID4gMCkge1xuICAgIF8uZm9yRWFjaChyZWZlcmVuY2VzLCBmdW5jdGlvbiAoc2NoZW1hLCBuYW1lKSB7XG4gICAgICB2YXIgc2Vlbk1vZGVsID0gXy5pbmRleE9mKHNlZW5Nb2RlbHMsIG5hbWUpID4gLTE7XG5cbiAgICAgIGRlbGV0ZSByZWZlcmVuY2VzW25hbWVdO1xuXG4gICAgICBpZiAoIXNlZW5Nb2RlbCkge1xuICAgICAgICBzZWVuTW9kZWxzLnB1c2gobmFtZSk7XG5cbiAgICAgICAgaHRtbCArPSAnPGJyIC8+JyArIHByb2Nlc3NNb2RlbChzY2hlbWEsIG5hbWUpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIGh0bWw7XG59O1xuXG52YXIgc2NoZW1hVG9KU09OID0gZnVuY3Rpb24gKHNjaGVtYSwgbW9kZWxzLCBtb2RlbHNUb0lnbm9yZSkge1xuICB2YXIgdHlwZSA9IHNjaGVtYS50eXBlIHx8ICdvYmplY3QnO1xuICB2YXIgbW9kZWw7XG4gIHZhciBvdXRwdXQ7XG5cbiAgaWYgKHNjaGVtYS5leGFtcGxlKSB7XG4gICAgb3V0cHV0ID0gc2NoZW1hLmV4YW1wbGU7XG4gIH0gZWxzZSBpZiAoXy5pc1VuZGVmaW5lZChzY2hlbWEuaXRlbXMpICYmIF8uaXNBcnJheShzY2hlbWEuZW51bSkpIHtcbiAgICBvdXRwdXQgPSBzY2hlbWEuZW51bVswXTtcbiAgfVxuXG4gIGlmIChfLmlzVW5kZWZpbmVkKG91dHB1dCkpIHtcbiAgICBpZiAoc2NoZW1hLiRyZWYpIHtcbiAgICAgIG1vZGVsID0gbW9kZWxzW2hlbHBlcnMuc2ltcGxlUmVmKHNjaGVtYS4kcmVmKV07XG5cbiAgICAgIGlmICghXy5pc1VuZGVmaW5lZChtb2RlbCkpIHtcbiAgICAgICAgaWYgKF8uaXNVbmRlZmluZWQobW9kZWxzVG9JZ25vcmVbbW9kZWwubmFtZV0pKSB7XG4gICAgICAgICAgbW9kZWxzVG9JZ25vcmVbbW9kZWwubmFtZV0gPSBtb2RlbDtcbiAgICAgICAgICBvdXRwdXQgPSBzY2hlbWFUb0pTT04obW9kZWwuZGVmaW5pdGlvbiwgbW9kZWxzLCBtb2RlbHNUb0lnbm9yZSk7XG4gICAgICAgICAgZGVsZXRlIG1vZGVsc1RvSWdub3JlW21vZGVsLm5hbWVdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGlmIChtb2RlbC50eXBlID09PSAnYXJyYXknKSB7XG4gICAgICAgICAgICBvdXRwdXQgPSBbXTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3V0cHV0ID0ge307XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChzY2hlbWEuZGVmYXVsdCkge1xuICAgICAgb3V0cHV0ID0gc2NoZW1hLmRlZmF1bHQ7XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAnZGF0ZS10aW1lJykge1xuICAgICAgb3V0cHV0ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuICAgIH0gZWxzZSBpZiAodHlwZSA9PT0gJ2RhdGUnKSB7XG4gICAgICBvdXRwdXQgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc3BsaXQoJ1QnKVswXTtcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICBvdXRwdXQgPSAnc3RyaW5nJztcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdpbnRlZ2VyJykge1xuICAgICAgb3V0cHV0ID0gMDtcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdsb25nJykge1xuICAgICAgb3V0cHV0ID0gMDtcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdmbG9hdCcpIHtcbiAgICAgIG91dHB1dCA9IDAuMDtcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdkb3VibGUnKSB7XG4gICAgICBvdXRwdXQgPSAwLjA7XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICAgIG91dHB1dCA9IHRydWU7XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAnb2JqZWN0Jykge1xuICAgICAgb3V0cHV0ID0ge307XG5cbiAgICAgIF8uZm9yRWFjaChzY2hlbWEucHJvcGVydGllcywgZnVuY3Rpb24gKHByb3BlcnR5LCBuYW1lKSB7XG4gICAgICAgIG91dHB1dFtuYW1lXSA9IHNjaGVtYVRvSlNPTihwcm9wZXJ0eSwgbW9kZWxzLCBtb2RlbHNUb0lnbm9yZSk7XG4gICAgICB9KTtcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdhcnJheScpIHtcbiAgICAgIG91dHB1dCA9IFtdO1xuXG4gICAgICBpZiAoXy5pc0FycmF5KHNjaGVtYS5pdGVtcykpIHtcbiAgICAgICAgXy5mb3JFYWNoKHNjaGVtYS5pdGVtcywgZnVuY3Rpb24gKGl0ZW0pIHtcbiAgICAgICAgICBvdXRwdXQucHVzaChzY2hlbWFUb0pTT04oaXRlbSwgbW9kZWxzLCBtb2RlbHNUb0lnbm9yZSkpO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAoXy5pc1BsYWluT2JqZWN0KHNjaGVtYS5pdGVtcykpIHtcbiAgICAgICAgb3V0cHV0LnB1c2goc2NoZW1hVG9KU09OKHNjaGVtYS5pdGVtcywgbW9kZWxzLCBtb2RlbHNUb0lnbm9yZSkpO1xuICAgICAgfSBlbHNlIGlmIChfLmlzVW5kZWZpbmVkKHNjaGVtYS5pdGVtcykpIHtcbiAgICAgICAgb3V0cHV0LnB1c2goe30pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaGVscGVycy5sb2coJ0FycmF5IHR5cGVcXCdzIFxcJ2l0ZW1zXFwnIHByb3BlcnR5IGlzIG5vdCBhbiBhcnJheSBvciBhbiBvYmplY3QsIGNhbm5vdCBwcm9jZXNzJyk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIG91dHB1dDtcbn07XG5cbk1vZGVsLnByb3RvdHlwZS5jcmVhdGVKU09OU2FtcGxlID0gTW9kZWwucHJvdG90eXBlLmdldFNhbXBsZVZhbHVlID0gZnVuY3Rpb24gKG1vZGVsc1RvSWdub3JlKSB7XG4gIG1vZGVsc1RvSWdub3JlID0gbW9kZWxzVG9JZ25vcmUgfHwge307XG5cbiAgbW9kZWxzVG9JZ25vcmVbdGhpcy5uYW1lXSA9IHRoaXM7XG5cbiAgLy8gUmVzcG9uc2Ugc3VwcG9ydFxuICBpZiAodGhpcy5leGFtcGxlcyAmJiBfLmlzUGxhaW5PYmplY3QodGhpcy5leGFtcGxlcykgJiYgdGhpcy5leGFtcGxlc1snYXBwbGljYXRpb24vanNvbiddKSB7XG4gICAgdGhpcy5kZWZpbml0aW9uLmV4YW1wbGUgPSB0aGlzLmV4YW1wbGVzWydhcHBsaWNhdGlvbi9qc29uJ107XG5cbiAgICBpZiAoXy5pc1N0cmluZyh0aGlzLmRlZmluaXRpb24uZXhhbXBsZSkpIHtcbiAgICAgIHRoaXMuZGVmaW5pdGlvbi5leGFtcGxlID0gSlNPTi5wYXJzZSh0aGlzLmRlZmluaXRpb24uZXhhbXBsZSk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHRoaXMuZGVmaW5pdGlvbi5leGFtcGxlID0gdGhpcy5leGFtcGxlcztcbiAgfVxuXG4gIFxuICByZXR1cm4gc2NoZW1hVG9KU09OKHRoaXMuZGVmaW5pdGlvbiwgdGhpcy5tb2RlbHMsIG1vZGVsc1RvSWdub3JlKTtcbn07XG5cbk1vZGVsLnByb3RvdHlwZS5nZXRNb2NrU2lnbmF0dXJlID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gc2NoZW1hVG9IVE1MKHRoaXMubmFtZSwgdGhpcy5kZWZpbml0aW9uLCB0aGlzLm1vZGVscyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgXyA9IHtcbiAgaXNVbmRlZmluZWQ6IHJlcXVpcmUoJ2xvZGFzaC1jb21wYXQvbGFuZy9pc1VuZGVmaW5lZCcpXG59O1xudmFyIGhlbHBlcnMgPSByZXF1aXJlKCcuLi9oZWxwZXJzJyk7XG52YXIgTW9kZWwgPSByZXF1aXJlKCcuL21vZGVsJyk7XG52YXIgU3dhZ2dlckh0dHAgPSByZXF1aXJlKCcuLi9odHRwJyk7XG5cbnZhciBPcGVyYXRpb24gPSBtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChwYXJlbnQsIHNjaGVtZSwgb3BlcmF0aW9uSWQsIGh0dHBNZXRob2QsIHBhdGgsIGFyZ3MsIGRlZmluaXRpb25zLCBtb2RlbHMsIGNsaWVudEF1dGhvcml6YXRpb25zKSB7XG4gIHZhciBlcnJvcnMgPSBbXTtcblxuICBwYXJlbnQgPSBwYXJlbnQgfHwge307XG4gIGFyZ3MgPSBhcmdzIHx8IHt9O1xuXG4gIHRoaXMuYXV0aG9yaXphdGlvbnMgPSBhcmdzLnNlY3VyaXR5O1xuICB0aGlzLmJhc2VQYXRoID0gcGFyZW50LmJhc2VQYXRoIHx8ICcvJztcbiAgdGhpcy5jbGllbnRBdXRob3JpemF0aW9ucyA9IGNsaWVudEF1dGhvcml6YXRpb25zO1xuICB0aGlzLmNvbnN1bWVzID0gYXJncy5jb25zdW1lcztcbiAgdGhpcy5kZXByZWNhdGVkID0gYXJncy5kZXByZWNhdGVkO1xuICB0aGlzLmRlc2NyaXB0aW9uID0gYXJncy5kZXNjcmlwdGlvbjtcbiAgdGhpcy5ob3N0ID0gcGFyZW50Lmhvc3QgfHwgJ2xvY2FsaG9zdCc7XG4gIHRoaXMubWV0aG9kID0gKGh0dHBNZXRob2QgfHwgZXJyb3JzLnB1c2goJ09wZXJhdGlvbiAnICsgb3BlcmF0aW9uSWQgKyAnIGlzIG1pc3NpbmcgbWV0aG9kLicpKTtcbiAgdGhpcy5tb2RlbHMgPSBtb2RlbHMgfHwge307XG4gIHRoaXMubmlja25hbWUgPSAob3BlcmF0aW9uSWQgfHwgZXJyb3JzLnB1c2goJ09wZXJhdGlvbnMgbXVzdCBoYXZlIGEgbmlja25hbWUuJykpO1xuICB0aGlzLm9wZXJhdGlvbiA9IGFyZ3M7XG4gIHRoaXMub3BlcmF0aW9ucyA9IHt9O1xuICB0aGlzLnBhcmFtZXRlcnMgPSBhcmdzICE9PSBudWxsID8gKGFyZ3MucGFyYW1ldGVycyB8fCBbXSkgOiB7fTtcbiAgdGhpcy5wYXJlbnQgPSBwYXJlbnQ7XG4gIHRoaXMucGF0aCA9IChwYXRoIHx8IGVycm9ycy5wdXNoKCdPcGVyYXRpb24gJyArIHRoaXMubmlja25hbWUgKyAnIGlzIG1pc3NpbmcgcGF0aC4nKSk7XG4gIHRoaXMucHJvZHVjZXMgPSBhcmdzLnByb2R1Y2VzO1xuICB0aGlzLnJlc3BvbnNlcyA9IChhcmdzLnJlc3BvbnNlcyB8fCB7fSk7XG4gIHRoaXMuc2NoZW1lID0gc2NoZW1lIHx8IHBhcmVudC5zY2hlbWUgfHwgJ2h0dHAnO1xuICB0aGlzLnNjaGVtZXMgPSBwYXJlbnQuc2NoZW1lcztcbiAgdGhpcy5zZWN1cml0eSA9IGFyZ3Muc2VjdXJpdHk7XG4gIHRoaXMuc3VtbWFyeSA9IGFyZ3Muc3VtbWFyeSB8fCAnJztcbiAgdGhpcy50eXBlID0gbnVsbDtcbiAgdGhpcy51c2VKUXVlcnkgPSBwYXJlbnQudXNlSlF1ZXJ5O1xuXG4gIGlmICh0eXBlb2YgdGhpcy5kZXByZWNhdGVkID09PSAnc3RyaW5nJykge1xuICAgIHN3aXRjaCh0aGlzLmRlcHJlY2F0ZWQudG9Mb3dlckNhc2UoKSkge1xuICAgICAgY2FzZSAndHJ1ZSc6IGNhc2UgJ3llcyc6IGNhc2UgJzEnOiB7XG4gICAgICAgIHRoaXMuZGVwcmVjYXRlZCA9IHRydWU7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICBjYXNlICdmYWxzZSc6IGNhc2UgJ25vJzogY2FzZSAnMCc6IGNhc2UgbnVsbDoge1xuICAgICAgICB0aGlzLmRlcHJlY2F0ZWQgPSBmYWxzZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG5cbiAgICAgIGRlZmF1bHQ6IHRoaXMuZGVwcmVjYXRlZCA9IEJvb2xlYW4odGhpcy5kZXByZWNhdGVkKTtcbiAgICB9XG4gIH1cblxuICB2YXIgaSwgbW9kZWw7XG5cbiAgaWYgKGRlZmluaXRpb25zKSB7XG4gICAgLy8gYWRkIHRvIGdsb2JhbCBtb2RlbHNcbiAgICB2YXIga2V5O1xuXG4gICAgZm9yIChrZXkgaW4gdGhpcy5kZWZpbml0aW9ucykge1xuICAgICAgbW9kZWwgPSBuZXcgTW9kZWwoa2V5LCBkZWZpbml0aW9uc1trZXldLCB0aGlzLm1vZGVscyk7XG5cbiAgICAgIGlmIChtb2RlbCkge1xuICAgICAgICB0aGlzLm1vZGVsc1trZXldID0gbW9kZWw7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGZvciAoaSA9IDA7IGkgPCB0aGlzLnBhcmFtZXRlcnMubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgcGFyYW0gPSB0aGlzLnBhcmFtZXRlcnNbaV07XG5cbiAgICBpZiAocGFyYW0udHlwZSA9PT0gJ2FycmF5Jykge1xuICAgICAgcGFyYW0uaXNMaXN0ID0gdHJ1ZTtcbiAgICAgIHBhcmFtLmFsbG93TXVsdGlwbGUgPSB0cnVlO1xuICAgIH1cblxuICAgIHZhciBpbm5lclR5cGUgPSB0aGlzLmdldFR5cGUocGFyYW0pO1xuXG4gICAgaWYgKGlubmVyVHlwZSAmJiBpbm5lclR5cGUudG9TdHJpbmcoKS50b0xvd2VyQ2FzZSgpID09PSAnYm9vbGVhbicpIHtcbiAgICAgIHBhcmFtLmFsbG93YWJsZVZhbHVlcyA9IHt9O1xuICAgICAgcGFyYW0uaXNMaXN0ID0gdHJ1ZTtcbiAgICAgIHBhcmFtWydlbnVtJ10gPSBbJ3RydWUnLCAnZmFsc2UnXTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIHBhcmFtWydlbnVtJ10gIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB2YXIgaWQ7XG5cbiAgICAgIHBhcmFtLmFsbG93YWJsZVZhbHVlcyA9IHt9O1xuICAgICAgcGFyYW0uYWxsb3dhYmxlVmFsdWVzLnZhbHVlcyA9IFtdO1xuICAgICAgcGFyYW0uYWxsb3dhYmxlVmFsdWVzLmRlc2NyaXB0aXZlVmFsdWVzID0gW107XG5cbiAgICAgIGZvciAoaWQgPSAwOyBpZCA8IHBhcmFtWydlbnVtJ10ubGVuZ3RoOyBpZCsrKSB7XG4gICAgICAgIHZhciB2YWx1ZSA9IHBhcmFtWydlbnVtJ11baWRdO1xuICAgICAgICB2YXIgaXNEZWZhdWx0ID0gKHZhbHVlID09PSBwYXJhbS5kZWZhdWx0KSA/IHRydWUgOiBmYWxzZTtcblxuICAgICAgICBwYXJhbS5hbGxvd2FibGVWYWx1ZXMudmFsdWVzLnB1c2godmFsdWUpO1xuICAgICAgICBwYXJhbS5hbGxvd2FibGVWYWx1ZXMuZGVzY3JpcHRpdmVWYWx1ZXMucHVzaCh7dmFsdWUgOiB2YWx1ZSwgaXNEZWZhdWx0OiBpc0RlZmF1bHR9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAocGFyYW0udHlwZSA9PT0gJ2FycmF5Jykge1xuICAgICAgaW5uZXJUeXBlID0gW2lubmVyVHlwZV07XG5cbiAgICAgIGlmICh0eXBlb2YgcGFyYW0uYWxsb3dhYmxlVmFsdWVzID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAvLyBjYW4ndCBzaG93IGFzIGEgbGlzdCBpZiBubyB2YWx1ZXMgdG8gc2VsZWN0IGZyb21cbiAgICAgICAgZGVsZXRlIHBhcmFtLmlzTGlzdDtcbiAgICAgICAgZGVsZXRlIHBhcmFtLmFsbG93TXVsdGlwbGU7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcGFyYW0uc2lnbmF0dXJlID0gdGhpcy5nZXRNb2RlbFNpZ25hdHVyZShpbm5lclR5cGUsIHRoaXMubW9kZWxzKS50b1N0cmluZygpO1xuICAgIHBhcmFtLnNhbXBsZUpTT04gPSB0aGlzLmdldE1vZGVsU2FtcGxlSlNPTihpbm5lclR5cGUsIHRoaXMubW9kZWxzKTtcbiAgICBwYXJhbS5yZXNwb25zZUNsYXNzU2lnbmF0dXJlID0gcGFyYW0uc2lnbmF0dXJlO1xuICB9XG5cbiAgdmFyIGRlZmF1bHRSZXNwb25zZUNvZGUsIHJlc3BvbnNlLCByZXNwb25zZXMgPSB0aGlzLnJlc3BvbnNlcztcblxuICBpZiAocmVzcG9uc2VzWycyMDAnXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDAnXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwMCc7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWycyMDEnXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDEnXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwMSc7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWycyMDInXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDInXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwMic7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWycyMDMnXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDMnXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwMyc7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWycyMDQnXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDQnXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwNCc7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWycyMDUnXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDUnXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwNSc7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWycyMDYnXSkge1xuICAgIHJlc3BvbnNlID0gcmVzcG9uc2VzWycyMDYnXTtcbiAgICBkZWZhdWx0UmVzcG9uc2VDb2RlID0gJzIwNic7XG4gIH0gZWxzZSBpZiAocmVzcG9uc2VzWydkZWZhdWx0J10pIHtcbiAgICByZXNwb25zZSA9IHJlc3BvbnNlc1snZGVmYXVsdCddO1xuICAgIGRlZmF1bHRSZXNwb25zZUNvZGUgPSAnZGVmYXVsdCc7XG4gIH1cbiAgXG4gIGlmIChyZXNwb25zZSAmJiByZXNwb25zZS5zY2hlbWEpIHtcbiAgICB2YXIgcmVzb2x2ZWRNb2RlbCA9IHRoaXMucmVzb2x2ZU1vZGVsKHJlc3BvbnNlLnNjaGVtYSwgZGVmaW5pdGlvbnMpO1xuICAgIHZhciBzdWNjZXNzUmVzcG9uc2U7XG5cbiAgICBkZWxldGUgcmVzcG9uc2VzW2RlZmF1bHRSZXNwb25zZUNvZGVdO1xuXG4gICAgaWYgKHJlc29sdmVkTW9kZWwpIHtcbiAgICAgIHRoaXMuc3VjY2Vzc1Jlc3BvbnNlID0ge307XG4gICAgICBzdWNjZXNzUmVzcG9uc2UgPSB0aGlzLnN1Y2Nlc3NSZXNwb25zZVtkZWZhdWx0UmVzcG9uc2VDb2RlXSA9IHJlc29sdmVkTW9kZWw7IFxuICAgIH0gZWxzZSBpZiAoIXJlc3BvbnNlLnNjaGVtYS50eXBlIHx8IHJlc3BvbnNlLnNjaGVtYS50eXBlID09PSAnb2JqZWN0JyB8fCByZXNwb25zZS5zY2hlbWEudHlwZSA9PT0gJ2FycmF5Jykge1xuICAgICAgLy8gSW5saW5lIG1vZGVsXG4gICAgICB0aGlzLnN1Y2Nlc3NSZXNwb25zZSA9IHt9O1xuICAgICAgc3VjY2Vzc1Jlc3BvbnNlID0gdGhpcy5zdWNjZXNzUmVzcG9uc2VbZGVmYXVsdFJlc3BvbnNlQ29kZV0gPSBuZXcgTW9kZWwodW5kZWZpbmVkLCByZXNwb25zZS5zY2hlbWEgfHwge30sIHRoaXMubW9kZWxzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gUHJpbWl0aXZlXG4gICAgICB0aGlzLnN1Y2Nlc3NSZXNwb25zZSA9IHt9O1xuICAgICAgc3VjY2Vzc1Jlc3BvbnNlID0gdGhpcy5zdWNjZXNzUmVzcG9uc2VbZGVmYXVsdFJlc3BvbnNlQ29kZV0gPSByZXNwb25zZS5zY2hlbWE7XG4gICAgfVxuXG4gICAgaWYgKHN1Y2Nlc3NSZXNwb25zZSkge1xuICAgICAgLy8gQXR0YWNoIHJlc3BvbnNlIHByb3BlcnRpZXNcbiAgICAgIGlmIChyZXNwb25zZS5kZXNjcmlwdGlvbikge1xuICAgICAgICBzdWNjZXNzUmVzcG9uc2UuZGVzY3JpcHRpb24gPSByZXNwb25zZS5kZXNjcmlwdGlvbjtcbiAgICAgIH1cblxuICAgICAgaWYgKHJlc3BvbnNlLmV4YW1wbGVzKSB7XG4gICAgICAgIHN1Y2Nlc3NSZXNwb25zZS5leGFtcGxlcyA9IHJlc3BvbnNlLmV4YW1wbGVzO1xuICAgICAgfVxuXG4gICAgICBpZiAocmVzcG9uc2UuaGVhZGVycykge1xuICAgICAgICBzdWNjZXNzUmVzcG9uc2UuaGVhZGVycyA9IHJlc3BvbnNlLmhlYWRlcnM7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy50eXBlID0gcmVzcG9uc2U7XG4gIH1cblxuICBpZiAoZXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICBpZiAodGhpcy5yZXNvdXJjZSAmJiB0aGlzLnJlc291cmNlLmFwaSAmJiB0aGlzLnJlc291cmNlLmFwaS5mYWlsKSB7XG4gICAgICB0aGlzLnJlc291cmNlLmFwaS5mYWlsKGVycm9ycyk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRoaXM7XG59O1xuXG5PcGVyYXRpb24ucHJvdG90eXBlLmdldFR5cGUgPSBmdW5jdGlvbiAocGFyYW0pIHtcbiAgdmFyIHR5cGUgPSBwYXJhbS50eXBlO1xuICB2YXIgZm9ybWF0ID0gcGFyYW0uZm9ybWF0O1xuICB2YXIgaXNBcnJheSA9IGZhbHNlO1xuICB2YXIgc3RyO1xuXG4gIGlmICh0eXBlID09PSAnaW50ZWdlcicgJiYgZm9ybWF0ID09PSAnaW50MzInKSB7XG4gICAgc3RyID0gJ2ludGVnZXInO1xuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdpbnRlZ2VyJyAmJiBmb3JtYXQgPT09ICdpbnQ2NCcpIHtcbiAgICBzdHIgPSAnbG9uZyc7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ2ludGVnZXInKSB7XG4gICAgc3RyID0gJ2ludGVnZXInO1xuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgaWYgKGZvcm1hdCA9PT0gJ2RhdGUtdGltZScpIHtcbiAgICAgIHN0ciA9ICdkYXRlLXRpbWUnO1xuICAgIH0gZWxzZSBpZiAoZm9ybWF0ID09PSAnZGF0ZScpIHtcbiAgICAgIHN0ciA9ICdkYXRlJztcbiAgICB9IGVsc2Uge1xuICAgICAgc3RyID0gJ3N0cmluZyc7XG4gICAgfVxuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdudW1iZXInICYmIGZvcm1hdCA9PT0gJ2Zsb2F0Jykge1xuICAgIHN0ciA9ICdmbG9hdCc7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ251bWJlcicgJiYgZm9ybWF0ID09PSAnZG91YmxlJykge1xuICAgIHN0ciA9ICdkb3VibGUnO1xuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdudW1iZXInKSB7XG4gICAgc3RyID0gJ2RvdWJsZSc7XG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XG4gICAgc3RyID0gJ2Jvb2xlYW4nO1xuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdhcnJheScpIHtcbiAgICBpc0FycmF5ID0gdHJ1ZTtcblxuICAgIGlmIChwYXJhbS5pdGVtcykge1xuICAgICAgc3RyID0gdGhpcy5nZXRUeXBlKHBhcmFtLml0ZW1zKTtcbiAgICB9XG4gIH1cblxuICBpZiAocGFyYW0uJHJlZikge1xuICAgIHN0ciA9IHBhcmFtLiRyZWY7XG4gIH1cblxuICB2YXIgc2NoZW1hID0gcGFyYW0uc2NoZW1hO1xuXG4gIGlmIChzY2hlbWEpIHtcbiAgICB2YXIgcmVmID0gc2NoZW1hLiRyZWY7XG5cbiAgICBpZiAocmVmKSB7XG4gICAgICByZWYgPSBoZWxwZXJzLnNpbXBsZVJlZihyZWYpO1xuXG4gICAgICBpZiAoaXNBcnJheSkge1xuICAgICAgICByZXR1cm4gWyByZWYgXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiByZWY7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB0aGlzLmdldFR5cGUoc2NoZW1hKTtcbiAgICB9XG4gIH1cbiAgaWYgKGlzQXJyYXkpIHtcbiAgICByZXR1cm4gWyBzdHIgXTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gc3RyO1xuICB9XG59O1xuXG5PcGVyYXRpb24ucHJvdG90eXBlLnJlc29sdmVNb2RlbCA9IGZ1bmN0aW9uIChzY2hlbWEsIGRlZmluaXRpb25zKSB7XG4gIGlmICh0eXBlb2Ygc2NoZW1hLiRyZWYgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgdmFyIHJlZiA9IHNjaGVtYS4kcmVmO1xuXG4gICAgaWYgKHJlZi5pbmRleE9mKCcjL2RlZmluaXRpb25zLycpID09PSAwKSB7XG4gICAgICByZWYgPSByZWYuc3Vic3RyaW5nKCcjL2RlZmluaXRpb25zLycubGVuZ3RoKTtcbiAgICB9XG5cbiAgICBpZiAoZGVmaW5pdGlvbnNbcmVmXSkge1xuICAgICAgcmV0dXJuIG5ldyBNb2RlbChyZWYsIGRlZmluaXRpb25zW3JlZl0sIHRoaXMubW9kZWxzKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoc2NoZW1hLnR5cGUgPT09ICdvYmplY3QnIHx8IF8uaXNVbmRlZmluZWQoc2NoZW1hLnR5cGUpKSB7XG4gICAgcmV0dXJuIG5ldyBNb2RlbCh1bmRlZmluZWQsIHNjaGVtYSwgdGhpcy5tb2RlbHMpO1xuICB9XG5cbiAgcmV0dXJuIG51bGw7XG59O1xuXG5PcGVyYXRpb24ucHJvdG90eXBlLmhlbHAgPSBmdW5jdGlvbiAoZG9udFByaW50KSB7XG4gIHZhciBvdXQgPSB0aGlzLm5pY2tuYW1lICsgJzogJyArIHRoaXMuc3VtbWFyeSArICdcXG4nO1xuXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgdGhpcy5wYXJhbWV0ZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIHBhcmFtID0gdGhpcy5wYXJhbWV0ZXJzW2ldO1xuICAgIHZhciB0eXBlSW5mbyA9IHBhcmFtLnNpZ25hdHVyZTtcblxuICAgIG91dCArPSAnXFxuICAqICcgKyBwYXJhbS5uYW1lICsgJyAoJyArIHR5cGVJbmZvICsgJyk6ICcgKyBwYXJhbS5kZXNjcmlwdGlvbjtcbiAgfVxuXG4gIGlmICh0eXBlb2YgZG9udFByaW50ID09PSAndW5kZWZpbmVkJykge1xuICAgIGhlbHBlcnMubG9nKG91dCk7XG4gIH1cblxuICByZXR1cm4gb3V0O1xufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS5nZXRNb2RlbFNpZ25hdHVyZSA9IGZ1bmN0aW9uICh0eXBlLCBkZWZpbml0aW9ucykge1xuICB2YXIgaXNQcmltaXRpdmUsIGxpc3RUeXBlO1xuXG4gIGlmICh0eXBlIGluc3RhbmNlb2YgQXJyYXkpIHtcbiAgICBsaXN0VHlwZSA9IHRydWU7XG4gICAgdHlwZSA9IHR5cGVbMF07XG4gIH0gZWxzZSBpZiAodHlwZW9mIHR5cGUgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgdHlwZSA9ICd1bmRlZmluZWQnO1xuICB9XG5cbiAgaWYgKHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgaXNQcmltaXRpdmUgPSB0cnVlO1xuICB9IGVsc2Uge1xuICAgIGlzUHJpbWl0aXZlID0gKGxpc3RUeXBlICYmIGRlZmluaXRpb25zW2xpc3RUeXBlXSkgfHwgKGRlZmluaXRpb25zW3R5cGVdKSA/IGZhbHNlIDogdHJ1ZTtcbiAgfVxuXG4gIGlmIChpc1ByaW1pdGl2ZSkge1xuICAgIGlmIChsaXN0VHlwZSkge1xuICAgICAgcmV0dXJuICdBcnJheVsnICsgdHlwZSArICddJztcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHR5cGUudG9TdHJpbmcoKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgaWYgKGxpc3RUeXBlKSB7XG4gICAgICByZXR1cm4gJ0FycmF5WycgKyBkZWZpbml0aW9uc1t0eXBlXS5nZXRNb2NrU2lnbmF0dXJlKCkgKyAnXSc7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBkZWZpbml0aW9uc1t0eXBlXS5nZXRNb2NrU2lnbmF0dXJlKCk7XG4gICAgfVxuICB9XG59O1xuXG5PcGVyYXRpb24ucHJvdG90eXBlLnN1cHBvcnRIZWFkZXJQYXJhbXMgPSBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiB0cnVlO1xufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS5zdXBwb3J0ZWRTdWJtaXRNZXRob2RzID0gZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdGhpcy5wYXJlbnQuc3VwcG9ydGVkU3VibWl0TWV0aG9kcztcbn07XG5cbk9wZXJhdGlvbi5wcm90b3R5cGUuZ2V0SGVhZGVyUGFyYW1zID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgdmFyIGhlYWRlcnMgPSB0aGlzLnNldENvbnRlbnRUeXBlcyhhcmdzLCB7fSk7XG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLnBhcmFtZXRlcnMubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgcGFyYW0gPSB0aGlzLnBhcmFtZXRlcnNbaV07XG5cbiAgICBpZiAodHlwZW9mIGFyZ3NbcGFyYW0ubmFtZV0gIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBpZiAocGFyYW0uaW4gPT09ICdoZWFkZXInKSB7XG4gICAgICAgIHZhciB2YWx1ZSA9IGFyZ3NbcGFyYW0ubmFtZV07XG5cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgICAgdmFsdWUgPSB2YWx1ZS50b1N0cmluZygpO1xuICAgICAgICB9XG5cbiAgICAgICAgaGVhZGVyc1twYXJhbS5uYW1lXSA9IHZhbHVlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBoZWFkZXJzO1xufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS51cmxpZnkgPSBmdW5jdGlvbiAoYXJncykge1xuICB2YXIgZm9ybVBhcmFtcyA9IHt9O1xuICB2YXIgcmVxdWVzdFVybCA9IHRoaXMucGF0aDtcbiAgdmFyIHF1ZXJ5c3RyaW5nID0gJyc7IC8vIGdyYWIgcGFyYW1zIGZyb20gdGhlIGFyZ3MsIGJ1aWxkIHRoZSBxdWVyeXN0cmluZyBhbG9uZyB0aGUgd2F5XG5cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCB0aGlzLnBhcmFtZXRlcnMubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgcGFyYW0gPSB0aGlzLnBhcmFtZXRlcnNbaV07XG5cbiAgICBpZiAodHlwZW9mIGFyZ3NbcGFyYW0ubmFtZV0gIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICBpZiAocGFyYW0uaW4gPT09ICdwYXRoJykge1xuICAgICAgICB2YXIgcmVnID0gbmV3IFJlZ0V4cCgnXFx7JyArIHBhcmFtLm5hbWUgKyAnXFx9JywgJ2dpJyk7XG4gICAgICAgIHZhciB2YWx1ZSA9IGFyZ3NbcGFyYW0ubmFtZV07XG5cbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgICAgdmFsdWUgPSB0aGlzLmVuY29kZVBhdGhDb2xsZWN0aW9uKHBhcmFtLmNvbGxlY3Rpb25Gb3JtYXQsIHBhcmFtLm5hbWUsIHZhbHVlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB2YWx1ZSA9IHRoaXMuZW5jb2RlUGF0aFBhcmFtKHZhbHVlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJlcXVlc3RVcmwgPSByZXF1ZXN0VXJsLnJlcGxhY2UocmVnLCB2YWx1ZSk7XG4gICAgICB9IGVsc2UgaWYgKHBhcmFtLmluID09PSAncXVlcnknICYmIHR5cGVvZiBhcmdzW3BhcmFtLm5hbWVdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICBpZiAocXVlcnlzdHJpbmcgPT09ICcnKSB7XG4gICAgICAgICAgcXVlcnlzdHJpbmcgKz0gJz8nO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHF1ZXJ5c3RyaW5nICs9ICcmJztcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0eXBlb2YgcGFyYW0uY29sbGVjdGlvbkZvcm1hdCAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICB2YXIgcXAgPSBhcmdzW3BhcmFtLm5hbWVdO1xuXG4gICAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkocXApKSB7XG4gICAgICAgICAgICBxdWVyeXN0cmluZyArPSB0aGlzLmVuY29kZVF1ZXJ5Q29sbGVjdGlvbihwYXJhbS5jb2xsZWN0aW9uRm9ybWF0LCBwYXJhbS5uYW1lLCBxcCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHF1ZXJ5c3RyaW5nICs9IHRoaXMuZW5jb2RlUXVlcnlQYXJhbShwYXJhbS5uYW1lKSArICc9JyArIHRoaXMuZW5jb2RlUXVlcnlQYXJhbShhcmdzW3BhcmFtLm5hbWVdKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcXVlcnlzdHJpbmcgKz0gdGhpcy5lbmNvZGVRdWVyeVBhcmFtKHBhcmFtLm5hbWUpICsgJz0nICsgdGhpcy5lbmNvZGVRdWVyeVBhcmFtKGFyZ3NbcGFyYW0ubmFtZV0pO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHBhcmFtLmluID09PSAnZm9ybURhdGEnKSB7XG4gICAgICAgIGZvcm1QYXJhbXNbcGFyYW0ubmFtZV0gPSBhcmdzW3BhcmFtLm5hbWVdO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICB2YXIgdXJsID0gdGhpcy5zY2hlbWUgKyAnOi8vJyArIHRoaXMuaG9zdDtcblxuICBpZiAodGhpcy5iYXNlUGF0aCAhPT0gJy8nKSB7XG4gICAgdXJsICs9IHRoaXMuYmFzZVBhdGg7XG4gIH1cbiAgcmV0dXJuIHVybCArIHJlcXVlc3RVcmwgKyBxdWVyeXN0cmluZztcbn07XG5cbk9wZXJhdGlvbi5wcm90b3R5cGUuZ2V0TWlzc2luZ1BhcmFtcyA9IGZ1bmN0aW9uIChhcmdzKSB7XG4gIHZhciBtaXNzaW5nUGFyYW1zID0gW107IC8vIGNoZWNrIHJlcXVpcmVkIHBhcmFtcywgdHJhY2sgdGhlIG9uZXMgdGhhdCBhcmUgbWlzc2luZ1xuICB2YXIgaTtcblxuICBmb3IgKGkgPSAwOyBpIDwgdGhpcy5wYXJhbWV0ZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIHBhcmFtID0gdGhpcy5wYXJhbWV0ZXJzW2ldO1xuXG4gICAgaWYgKHBhcmFtLnJlcXVpcmVkID09PSB0cnVlKSB7XG4gICAgICBpZiAodHlwZW9mIGFyZ3NbcGFyYW0ubmFtZV0gPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIG1pc3NpbmdQYXJhbXMgPSBwYXJhbS5uYW1lO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBtaXNzaW5nUGFyYW1zO1xufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS5nZXRCb2R5ID0gZnVuY3Rpb24gKGhlYWRlcnMsIGFyZ3MsIG9wdHMpIHtcbiAgdmFyIGZvcm1QYXJhbXMgPSB7fSwgYm9keSwga2V5LCB2YWx1ZTtcblxuICBmb3IgKHZhciBpID0gMDsgaSA8IHRoaXMucGFyYW1ldGVycy5sZW5ndGg7IGkrKykge1xuICAgIHZhciBwYXJhbSA9IHRoaXMucGFyYW1ldGVyc1tpXTtcblxuICAgIGlmICh0eXBlb2YgYXJnc1twYXJhbS5uYW1lXSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIGlmIChwYXJhbS5pbiA9PT0gJ2JvZHknKSB7XG4gICAgICAgIGJvZHkgPSBhcmdzW3BhcmFtLm5hbWVdO1xuICAgICAgfSBlbHNlIGlmIChwYXJhbS5pbiA9PT0gJ2Zvcm1EYXRhJykge1xuICAgICAgICBmb3JtUGFyYW1zW3BhcmFtLm5hbWVdID0gYXJnc1twYXJhbS5uYW1lXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBoYW5kbGUgZm9ybSBwYXJhbXNcbiAgaWYgKGhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddID09PSAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykge1xuICAgIHZhciBlbmNvZGVkID0gJyc7XG5cbiAgICBmb3IgKGtleSBpbiBmb3JtUGFyYW1zKSB7XG4gICAgICB2YWx1ZSA9IGZvcm1QYXJhbXNba2V5XTtcblxuICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgaWYgKGVuY29kZWQgIT09ICcnKSB7XG4gICAgICAgICAgZW5jb2RlZCArPSAnJic7XG4gICAgICAgIH1cblxuICAgICAgICBlbmNvZGVkICs9IGVuY29kZVVSSUNvbXBvbmVudChrZXkpICsgJz0nICsgZW5jb2RlVVJJQ29tcG9uZW50KHZhbHVlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBib2R5ID0gZW5jb2RlZDtcbiAgfSBlbHNlIGlmIChoZWFkZXJzWydDb250ZW50LVR5cGUnXSAmJiBoZWFkZXJzWydDb250ZW50LVR5cGUnXS5pbmRleE9mKCdtdWx0aXBhcnQvZm9ybS1kYXRhJykgPj0gMCkge1xuICAgIGlmIChvcHRzLnVzZUpRdWVyeSkge1xuICAgICAgdmFyIGJvZHlQYXJhbSA9IG5ldyBGb3JtRGF0YSgpO1xuXG4gICAgICBib2R5UGFyYW0udHlwZSA9ICdmb3JtRGF0YSc7XG5cbiAgICAgIGZvciAoa2V5IGluIGZvcm1QYXJhbXMpIHtcbiAgICAgICAgdmFsdWUgPSBhcmdzW2tleV07XG5cbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAvLyByZXF1aXJlZCBmb3IganF1ZXJ5IGZpbGUgdXBsb2FkXG4gICAgICAgICAgaWYgKHZhbHVlLnR5cGUgPT09ICdmaWxlJyAmJiB2YWx1ZS52YWx1ZSkge1xuICAgICAgICAgICAgZGVsZXRlIGhlYWRlcnNbJ0NvbnRlbnQtVHlwZSddO1xuXG4gICAgICAgICAgICBib2R5UGFyYW0uYXBwZW5kKGtleSwgdmFsdWUudmFsdWUpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBib2R5UGFyYW0uYXBwZW5kKGtleSwgdmFsdWUpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBib2R5ID0gYm9keVBhcmFtO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBib2R5O1xufTtcblxuLyoqXG4gKiBnZXRzIHNhbXBsZSByZXNwb25zZSBmb3IgYSBzaW5nbGUgb3BlcmF0aW9uXG4gKiovXG5PcGVyYXRpb24ucHJvdG90eXBlLmdldE1vZGVsU2FtcGxlSlNPTiA9IGZ1bmN0aW9uICh0eXBlLCBtb2RlbHMpIHtcbiAgdmFyIGlzUHJpbWl0aXZlLCBsaXN0VHlwZSwgc2FtcGxlSnNvbjtcblxuICBsaXN0VHlwZSA9ICh0eXBlIGluc3RhbmNlb2YgQXJyYXkpO1xuICBpc1ByaW1pdGl2ZSA9IG1vZGVsc1t0eXBlXSA/IGZhbHNlIDogdHJ1ZTtcbiAgc2FtcGxlSnNvbiA9IGlzUHJpbWl0aXZlID8gdm9pZCAwIDogbW9kZWxzW3R5cGVdLmNyZWF0ZUpTT05TYW1wbGUoKTtcblxuICBpZiAoc2FtcGxlSnNvbikge1xuICAgIHNhbXBsZUpzb24gPSBsaXN0VHlwZSA/IFtzYW1wbGVKc29uXSA6IHNhbXBsZUpzb247XG5cbiAgICBpZiAodHlwZW9mIHNhbXBsZUpzb24gPT09ICdzdHJpbmcnKSB7XG4gICAgICByZXR1cm4gc2FtcGxlSnNvbjtcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBzYW1wbGVKc29uID09PSAnb2JqZWN0Jykge1xuICAgICAgdmFyIHQgPSBzYW1wbGVKc29uO1xuXG4gICAgICBpZiAoc2FtcGxlSnNvbiBpbnN0YW5jZW9mIEFycmF5ICYmIHNhbXBsZUpzb24ubGVuZ3RoID4gMCkge1xuICAgICAgICB0ID0gc2FtcGxlSnNvblswXTtcbiAgICAgIH1cblxuICAgICAgaWYgKHQubm9kZU5hbWUpIHtcbiAgICAgICAgdmFyIHhtbFN0cmluZyA9IG5ldyBYTUxTZXJpYWxpemVyKCkuc2VyaWFsaXplVG9TdHJpbmcodCk7XG5cbiAgICAgICAgcmV0dXJuIHRoaXMuZm9ybWF0WG1sKHhtbFN0cmluZyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoc2FtcGxlSnNvbiwgbnVsbCwgMik7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBzYW1wbGVKc29uO1xuICAgIH1cbiAgfVxufTtcblxuLyoqXG4gKiBsZWdhY3kgYmluZGluZ1xuICoqL1xuT3BlcmF0aW9uLnByb3RvdHlwZS5kbyA9IGZ1bmN0aW9uIChhcmdzLCBvcHRzLCBjYWxsYmFjaywgZXJyb3IsIHBhcmVudCkge1xuICByZXR1cm4gdGhpcy5leGVjdXRlKGFyZ3MsIG9wdHMsIGNhbGxiYWNrLCBlcnJvciwgcGFyZW50KTtcbn07XG5cbi8qKlxuICogZXhlY3V0ZXMgYW4gb3BlcmF0aW9uXG4gKiovXG5PcGVyYXRpb24ucHJvdG90eXBlLmV4ZWN1dGUgPSBmdW5jdGlvbiAoYXJnMSwgYXJnMiwgYXJnMywgYXJnNCwgcGFyZW50KSB7XG4gIHZhciBhcmdzID0gYXJnMSB8fCB7fTtcbiAgdmFyIG9wdHMgPSB7fSwgc3VjY2VzcywgZXJyb3I7XG5cbiAgaWYgKHR5cGVvZiBhcmcyID09PSAnb2JqZWN0Jykge1xuICAgIG9wdHMgPSBhcmcyO1xuICAgIHN1Y2Nlc3MgPSBhcmczO1xuICAgIGVycm9yID0gYXJnNDtcbiAgfVxuXG4gIGlmICh0eXBlb2YgYXJnMiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIHN1Y2Nlc3MgPSBhcmcyO1xuICAgIGVycm9yID0gYXJnMztcbiAgfVxuXG4gIHN1Y2Nlc3MgPSAoc3VjY2VzcyB8fCBoZWxwZXJzLmxvZyk7XG4gIGVycm9yID0gKGVycm9yIHx8IGhlbHBlcnMubG9nKTtcblxuICBpZiAob3B0cy51c2VKUXVlcnkpIHtcbiAgICB0aGlzLnVzZUpRdWVyeSA9IG9wdHMudXNlSlF1ZXJ5O1xuICB9XG5cbiAgdmFyIG1pc3NpbmdQYXJhbXMgPSB0aGlzLmdldE1pc3NpbmdQYXJhbXMoYXJncyk7XG5cbiAgaWYgKG1pc3NpbmdQYXJhbXMubGVuZ3RoID4gMCkge1xuICAgIHZhciBtZXNzYWdlID0gJ21pc3NpbmcgcmVxdWlyZWQgcGFyYW1zOiAnICsgbWlzc2luZ1BhcmFtcztcblxuICAgIGhlbHBlcnMuZmFpbChtZXNzYWdlKTtcblxuICAgIHJldHVybjtcbiAgfVxuXG4gIHZhciBhbGxIZWFkZXJzID0gdGhpcy5nZXRIZWFkZXJQYXJhbXMoYXJncyk7XG4gIHZhciBjb250ZW50VHlwZUhlYWRlcnMgPSB0aGlzLnNldENvbnRlbnRUeXBlcyhhcmdzLCBvcHRzKTtcbiAgdmFyIGhlYWRlcnMgPSB7fSwgYXR0cm5hbWU7XG5cbiAgZm9yIChhdHRybmFtZSBpbiBhbGxIZWFkZXJzKSB7IGhlYWRlcnNbYXR0cm5hbWVdID0gYWxsSGVhZGVyc1thdHRybmFtZV07IH1cbiAgZm9yIChhdHRybmFtZSBpbiBjb250ZW50VHlwZUhlYWRlcnMpIHsgaGVhZGVyc1thdHRybmFtZV0gPSBjb250ZW50VHlwZUhlYWRlcnNbYXR0cm5hbWVdOyB9XG5cbiAgdmFyIGJvZHkgPSB0aGlzLmdldEJvZHkoaGVhZGVycywgYXJncywgb3B0cyk7XG4gIHZhciB1cmwgPSB0aGlzLnVybGlmeShhcmdzKTtcblxuICBpZih1cmwuaW5kZXhPZignLntmb3JtYXR9JykgPiAwKSB7XG4gICAgaWYoaGVhZGVycykge1xuICAgICAgdmFyIGZvcm1hdCA9IGhlYWRlcnMuQWNjZXB0IHx8IGhlYWRlcnMuYWNjZXB0O1xuICAgICAgaWYoZm9ybWF0ICYmIGZvcm1hdC5pbmRleE9mKCdqc29uJykgPiAwKSB7XG4gICAgICAgIHVybCA9IHVybC5yZXBsYWNlKCcue2Zvcm1hdH0nLCAnLmpzb24nKTtcbiAgICAgIH1cbiAgICAgIGVsc2UgaWYoZm9ybWF0ICYmIGZvcm1hdC5pbmRleE9mKCd4bWwnKSA+IDApIHtcbiAgICAgICAgdXJsID0gdXJsLnJlcGxhY2UoJy57Zm9ybWF0fScsICcueG1sJyk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgdmFyIG9iaiA9IHtcbiAgICB1cmw6IHVybCxcbiAgICBtZXRob2Q6IHRoaXMubWV0aG9kLnRvVXBwZXJDYXNlKCksXG4gICAgYm9keTogYm9keSxcbiAgICB1c2VKUXVlcnk6IHRoaXMudXNlSlF1ZXJ5LFxuICAgIGhlYWRlcnM6IGhlYWRlcnMsXG4gICAgb246IHtcbiAgICAgIHJlc3BvbnNlOiBmdW5jdGlvbiAocmVzcG9uc2UpIHtcbiAgICAgICAgcmV0dXJuIHN1Y2Nlc3MocmVzcG9uc2UsIHBhcmVudCk7XG4gICAgICB9LFxuICAgICAgZXJyb3I6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuICAgICAgICByZXR1cm4gZXJyb3IocmVzcG9uc2UsIHBhcmVudCk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIHRoaXMuY2xpZW50QXV0aG9yaXphdGlvbnMuYXBwbHkob2JqLCB0aGlzLm9wZXJhdGlvbi5zZWN1cml0eSk7XG4gIGlmIChvcHRzLm1vY2sgPT09IHRydWUpIHtcbiAgICByZXR1cm4gb2JqO1xuICB9IGVsc2Uge1xuICAgIG5ldyBTd2FnZ2VySHR0cCgpLmV4ZWN1dGUob2JqLCBvcHRzKTtcbiAgfVxufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS5zZXRDb250ZW50VHlwZXMgPSBmdW5jdGlvbiAoYXJncywgb3B0cykge1xuICAvLyBkZWZhdWx0IHR5cGVcbiAgdmFyIGFjY2VwdHMgPSAnYXBwbGljYXRpb24vanNvbic7XG4gIHZhciBhbGxEZWZpbmVkUGFyYW1zID0gdGhpcy5wYXJhbWV0ZXJzO1xuICB2YXIgYm9keTtcbiAgdmFyIGNvbnN1bWVzID0gYXJncy5wYXJhbWV0ZXJDb250ZW50VHlwZSB8fCAnYXBwbGljYXRpb24vanNvbic7XG4gIHZhciBkZWZpbmVkRmlsZVBhcmFtcyA9IFtdO1xuICB2YXIgZGVmaW5lZEZvcm1QYXJhbXMgPSBbXTtcbiAgdmFyIGhlYWRlcnMgPSB7fTtcbiAgdmFyIGk7XG5cbiAgLy8gZ2V0IHBhcmFtcyBmcm9tIHRoZSBvcGVyYXRpb24gYW5kIHNldCB0aGVtIGluIGRlZmluZWRGaWxlUGFyYW1zLCBkZWZpbmVkRm9ybVBhcmFtcywgaGVhZGVyc1xuICBmb3IgKGkgPSAwOyBpIDwgYWxsRGVmaW5lZFBhcmFtcy5sZW5ndGg7IGkrKykge1xuICAgIHZhciBwYXJhbSA9IGFsbERlZmluZWRQYXJhbXNbaV07XG5cbiAgICBpZiAocGFyYW0uaW4gPT09ICdmb3JtRGF0YScpIHtcbiAgICAgIGlmIChwYXJhbS50eXBlID09PSAnZmlsZScpIHtcbiAgICAgICAgZGVmaW5lZEZpbGVQYXJhbXMucHVzaChwYXJhbSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkZWZpbmVkRm9ybVBhcmFtcy5wdXNoKHBhcmFtKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHBhcmFtLmluID09PSAnaGVhZGVyJyAmJiBvcHRzKSB7XG4gICAgICB2YXIga2V5ID0gcGFyYW0ubmFtZTtcbiAgICAgIHZhciBoZWFkZXJWYWx1ZSA9IG9wdHNbcGFyYW0ubmFtZV07XG5cbiAgICAgIGlmICh0eXBlb2Ygb3B0c1twYXJhbS5uYW1lXSAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgaGVhZGVyc1trZXldID0gaGVhZGVyVmFsdWU7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChwYXJhbS5pbiA9PT0gJ2JvZHknICYmIHR5cGVvZiBhcmdzW3BhcmFtLm5hbWVdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgYm9keSA9IGFyZ3NbcGFyYW0ubmFtZV07XG4gICAgfVxuICB9XG5cbiAgLy8gaWYgdGhlcmUncyBhIGJvZHksIG5lZWQgdG8gc2V0IHRoZSBjb25zdW1lcyBoZWFkZXIgdmlhIHJlcXVlc3RDb250ZW50VHlwZVxuICBpZiAoYm9keSAmJiAodGhpcy5tZXRob2QgPT09ICdwb3N0JyB8fCB0aGlzLm1ldGhvZCA9PT0gJ3B1dCcgfHwgdGhpcy5tZXRob2QgPT09ICdwYXRjaCcgfHwgdGhpcy5tZXRob2QgPT09ICdkZWxldGUnKSkge1xuICAgIGlmIChvcHRzLnJlcXVlc3RDb250ZW50VHlwZSkge1xuICAgICAgY29uc3VtZXMgPSBvcHRzLnJlcXVlc3RDb250ZW50VHlwZTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgLy8gaWYgYW55IGZvcm0gcGFyYW1zLCBjb250ZW50IHR5cGUgbXVzdCBiZSBzZXRcbiAgICBpZiAoZGVmaW5lZEZvcm1QYXJhbXMubGVuZ3RoID4gMCkge1xuICAgICAgaWYgKG9wdHMucmVxdWVzdENvbnRlbnRUeXBlKSB7ICAgICAgICAgICAgIC8vIG92ZXJyaWRlIGlmIHNldFxuICAgICAgICBjb25zdW1lcyA9IG9wdHMucmVxdWVzdENvbnRlbnRUeXBlO1xuICAgICAgfSBlbHNlIGlmIChkZWZpbmVkRmlsZVBhcmFtcy5sZW5ndGggPiAwKSB7IC8vIGlmIGEgZmlsZSwgbXVzdCBiZSBtdWx0aXBhcnQvZm9ybS1kYXRhXG4gICAgICAgIGNvbnN1bWVzID0gJ211bHRpcGFydC9mb3JtLWRhdGEnO1xuICAgICAgfSBlbHNlIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGRlZmF1bHQgdG8geC13d3ctZnJvbS11cmxlbmNvZGVkXG4gICAgICAgIGNvbnN1bWVzID0gJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCc7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0aGlzLnR5cGUgPT09ICdERUxFVEUnKSB7XG4gICAgICBib2R5ID0gJ3t9JztcbiAgICB9IGVsc2UgaWYgKHRoaXMudHlwZSAhPT0gJ0RFTEVURScpIHtcbiAgICAgIGNvbnN1bWVzID0gbnVsbDtcbiAgICB9XG4gIH1cblxuICBpZiAoY29uc3VtZXMgJiYgdGhpcy5jb25zdW1lcykge1xuICAgIGlmICh0aGlzLmNvbnN1bWVzLmluZGV4T2YoY29uc3VtZXMpID09PSAtMSkge1xuICAgICAgaGVscGVycy5sb2coJ3NlcnZlciBkb2VzblxcJ3QgY29uc3VtZSAnICsgY29uc3VtZXMgKyAnLCB0cnkgJyArIEpTT04uc3RyaW5naWZ5KHRoaXMuY29uc3VtZXMpKTtcbiAgICB9XG4gIH1cblxuICBpZiAob3B0cy5yZXNwb25zZUNvbnRlbnRUeXBlKSB7XG4gICAgYWNjZXB0cyA9IG9wdHMucmVzcG9uc2VDb250ZW50VHlwZTtcbiAgfSBlbHNlIHtcbiAgICBhY2NlcHRzID0gJ2FwcGxpY2F0aW9uL2pzb24nO1xuICB9XG5cbiAgaWYgKGFjY2VwdHMgJiYgdGhpcy5wcm9kdWNlcykge1xuICAgIGlmICh0aGlzLnByb2R1Y2VzLmluZGV4T2YoYWNjZXB0cykgPT09IC0xKSB7XG4gICAgICBoZWxwZXJzLmxvZygnc2VydmVyIGNhblxcJ3QgcHJvZHVjZSAnICsgYWNjZXB0cyk7XG4gICAgfVxuICB9XG5cbiAgaWYgKChjb25zdW1lcyAmJiBib2R5ICE9PSAnJykgfHwgKGNvbnN1bWVzID09PSAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykpIHtcbiAgICBoZWFkZXJzWydDb250ZW50LVR5cGUnXSA9IGNvbnN1bWVzO1xuICB9XG5cbiAgaWYgKGFjY2VwdHMpIHtcbiAgICBoZWFkZXJzLkFjY2VwdCA9IGFjY2VwdHM7XG4gIH1cblxuICByZXR1cm4gaGVhZGVycztcbn07XG5cbk9wZXJhdGlvbi5wcm90b3R5cGUuYXNDdXJsID0gZnVuY3Rpb24gKGFyZ3MpIHtcbiAgdmFyIG9iaiA9IHRoaXMuZXhlY3V0ZShhcmdzLCB7bW9jazogdHJ1ZX0pO1xuXG4gIHRoaXMuY2xpZW50QXV0aG9yaXphdGlvbnMuYXBwbHkob2JqKTtcblxuICB2YXIgcmVzdWx0cyA9IFtdO1xuXG4gIHJlc3VsdHMucHVzaCgnLVggJyArIHRoaXMubWV0aG9kLnRvVXBwZXJDYXNlKCkpO1xuXG4gIGlmIChvYmouaGVhZGVycykge1xuICAgIHZhciBrZXk7XG5cbiAgICBmb3IgKGtleSBpbiBvYmouaGVhZGVycykge1xuICAgICAgcmVzdWx0cy5wdXNoKCctLWhlYWRlciBcIicgKyBrZXkgKyAnOiAnICsgb2JqLmhlYWRlcnNba2V5XSArICdcIicpO1xuICAgIH1cbiAgfVxuXG4gIGlmIChvYmouYm9keSkge1xuICAgIHZhciBib2R5O1xuXG4gICAgaWYgKHR5cGVvZiBvYmouYm9keSA9PT0gJ29iamVjdCcpIHtcbiAgICAgIGJvZHkgPSBKU09OLnN0cmluZ2lmeShvYmouYm9keSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJvZHkgPSBvYmouYm9keTtcbiAgICB9XG5cbiAgICByZXN1bHRzLnB1c2goJy1kIFwiJyArIGJvZHkucmVwbGFjZSgvXCIvZywgJ1xcXFxcIicpICsgJ1wiJyk7XG4gIH1cblxuICByZXR1cm4gJ2N1cmwgJyArIChyZXN1bHRzLmpvaW4oJyAnKSkgKyAnIFwiJyArIG9iai51cmwgKyAnXCInO1xufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS5lbmNvZGVQYXRoQ29sbGVjdGlvbiA9IGZ1bmN0aW9uICh0eXBlLCBuYW1lLCB2YWx1ZSkge1xuICB2YXIgZW5jb2RlZCA9ICcnO1xuICB2YXIgaTtcbiAgdmFyIHNlcGFyYXRvciA9ICcnO1xuXG4gIGlmICh0eXBlID09PSAnc3N2Jykge1xuICAgIHNlcGFyYXRvciA9ICclMjAnO1xuICB9IGVsc2UgaWYgKHR5cGUgPT09ICd0c3YnKSB7XG4gICAgc2VwYXJhdG9yID0gJ1xcXFx0JztcbiAgfSBlbHNlIGlmICh0eXBlID09PSAncGlwZXMnKSB7XG4gICAgc2VwYXJhdG9yID0gJ3wnO1xuICB9IGVsc2Uge1xuICAgIHNlcGFyYXRvciA9ICcsJztcbiAgfVxuXG4gIGZvciAoaSA9IDA7IGkgPCB2YWx1ZS5sZW5ndGg7IGkrKykge1xuICAgIGlmIChpID09PSAwKSB7XG4gICAgICBlbmNvZGVkID0gdGhpcy5lbmNvZGVRdWVyeVBhcmFtKHZhbHVlW2ldKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZW5jb2RlZCArPSBzZXBhcmF0b3IgKyB0aGlzLmVuY29kZVF1ZXJ5UGFyYW0odmFsdWVbaV0pO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBlbmNvZGVkO1xufTtcblxuT3BlcmF0aW9uLnByb3RvdHlwZS5lbmNvZGVRdWVyeUNvbGxlY3Rpb24gPSBmdW5jdGlvbiAodHlwZSwgbmFtZSwgdmFsdWUpIHtcbiAgdmFyIGVuY29kZWQgPSAnJztcbiAgdmFyIGk7XG5cbiAgaWYgKHR5cGUgPT09ICdkZWZhdWx0JyB8fCB0eXBlID09PSAnbXVsdGknKSB7XG4gICAgZm9yIChpID0gMDsgaSA8IHZhbHVlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAoaSA+IDApIHtlbmNvZGVkICs9ICcmJzt9XG5cbiAgICAgIGVuY29kZWQgKz0gdGhpcy5lbmNvZGVRdWVyeVBhcmFtKG5hbWUpICsgJz0nICsgdGhpcy5lbmNvZGVRdWVyeVBhcmFtKHZhbHVlW2ldKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgdmFyIHNlcGFyYXRvciA9ICcnO1xuXG4gICAgaWYgKHR5cGUgPT09ICdjc3YnKSB7XG4gICAgICBzZXBhcmF0b3IgPSAnLCc7XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAnc3N2Jykge1xuICAgICAgc2VwYXJhdG9yID0gJyUyMCc7XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAndHN2Jykge1xuICAgICAgc2VwYXJhdG9yID0gJ1xcXFx0JztcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdwaXBlcycpIHtcbiAgICAgIHNlcGFyYXRvciA9ICd8JztcbiAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICdicmFja2V0cycpIHsgXG4gICAgICBmb3IgKGkgPSAwOyBpIDwgdmFsdWUubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGkgIT09IDApIHtcbiAgICAgICAgICBlbmNvZGVkICs9ICcmJztcbiAgICAgICAgfVxuXG4gICAgICAgIGVuY29kZWQgKz0gdGhpcy5lbmNvZGVRdWVyeVBhcmFtKG5hbWUpICsgJ1tdPScgKyB0aGlzLmVuY29kZVF1ZXJ5UGFyYW0odmFsdWVbaV0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChzZXBhcmF0b3IgIT09ICcnKSB7XG4gICAgICBmb3IgKGkgPSAwOyBpIDwgdmFsdWUubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGkgPT09IDApIHtcbiAgICAgICAgICBlbmNvZGVkID0gdGhpcy5lbmNvZGVRdWVyeVBhcmFtKG5hbWUpICsgJz0nICsgdGhpcy5lbmNvZGVRdWVyeVBhcmFtKHZhbHVlW2ldKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBlbmNvZGVkICs9IHNlcGFyYXRvciArIHRoaXMuZW5jb2RlUXVlcnlQYXJhbSh2YWx1ZVtpXSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gZW5jb2RlZDtcbn07XG5cbk9wZXJhdGlvbi5wcm90b3R5cGUuZW5jb2RlUXVlcnlQYXJhbSA9IGZ1bmN0aW9uIChhcmcpIHtcbiAgcmV0dXJuIGVuY29kZVVSSUNvbXBvbmVudChhcmcpO1xufTtcblxuLyoqXG4gKiBUT0RPIHJldmlzaXQsIG1pZ2h0IG5vdCB3YW50IHRvIGxlYXZlICcvJ1xuICoqL1xuT3BlcmF0aW9uLnByb3RvdHlwZS5lbmNvZGVQYXRoUGFyYW0gPSBmdW5jdGlvbiAocGF0aFBhcmFtKSB7XG4gIHZhciBlbmNQYXJ0cywgcGFydHMsIGksIGxlbjtcblxuICBwYXRoUGFyYW0gPSBwYXRoUGFyYW0udG9TdHJpbmcoKTtcblxuICBpZiAocGF0aFBhcmFtLmluZGV4T2YoJy8nKSA9PT0gLTEpIHtcbiAgICByZXR1cm4gZW5jb2RlVVJJQ29tcG9uZW50KHBhdGhQYXJhbSk7XG4gIH0gZWxzZSB7XG4gICAgcGFydHMgPSBwYXRoUGFyYW0uc3BsaXQoJy8nKTtcbiAgICBlbmNQYXJ0cyA9IFtdO1xuXG4gICAgZm9yIChpID0gMCwgbGVuID0gcGFydHMubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIGVuY1BhcnRzLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KHBhcnRzW2ldKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGVuY1BhcnRzLmpvaW4oJy8nKTtcbiAgfVxufTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIE9wZXJhdGlvbkdyb3VwID0gbW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAodGFnLCBkZXNjcmlwdGlvbiwgZXh0ZXJuYWxEb2NzLCBvcGVyYXRpb24pIHtcbiAgdGhpcy5kZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uO1xuICB0aGlzLmV4dGVybmFsRG9jcyA9IGV4dGVybmFsRG9jcztcbiAgdGhpcy5uYW1lID0gdGFnO1xuICB0aGlzLm9wZXJhdGlvbiA9IG9wZXJhdGlvbjtcbiAgdGhpcy5vcGVyYXRpb25zQXJyYXkgPSBbXTtcbiAgdGhpcy5wYXRoID0gdGFnO1xuICB0aGlzLnRhZyA9IHRhZztcbn07XG5cbk9wZXJhdGlvbkdyb3VwLnByb3RvdHlwZS5zb3J0ID0gZnVuY3Rpb24gKCkge1xuXG59O1xuXG4iLCIvKiFcbiAqIFRoZSBidWZmZXIgbW9kdWxlIGZyb20gbm9kZS5qcywgZm9yIHRoZSBicm93c2VyLlxuICpcbiAqIEBhdXRob3IgICBGZXJvc3MgQWJvdWtoYWRpamVoIDxmZXJvc3NAZmVyb3NzLm9yZz4gPGh0dHA6Ly9mZXJvc3Mub3JnPlxuICogQGxpY2Vuc2UgIE1JVFxuICovXG5cbnZhciBiYXNlNjQgPSByZXF1aXJlKCdiYXNlNjQtanMnKVxudmFyIGllZWU3NTQgPSByZXF1aXJlKCdpZWVlNzU0JylcbnZhciBpc0FycmF5ID0gcmVxdWlyZSgnaXMtYXJyYXknKVxuXG5leHBvcnRzLkJ1ZmZlciA9IEJ1ZmZlclxuZXhwb3J0cy5TbG93QnVmZmVyID0gU2xvd0J1ZmZlclxuZXhwb3J0cy5JTlNQRUNUX01BWF9CWVRFUyA9IDUwXG5CdWZmZXIucG9vbFNpemUgPSA4MTkyIC8vIG5vdCB1c2VkIGJ5IHRoaXMgaW1wbGVtZW50YXRpb25cblxudmFyIGtNYXhMZW5ndGggPSAweDNmZmZmZmZmXG52YXIgcm9vdFBhcmVudCA9IHt9XG5cbi8qKlxuICogSWYgYEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUYDpcbiAqICAgPT09IHRydWUgICAgVXNlIFVpbnQ4QXJyYXkgaW1wbGVtZW50YXRpb24gKGZhc3Rlc3QpXG4gKiAgID09PSBmYWxzZSAgIFVzZSBPYmplY3QgaW1wbGVtZW50YXRpb24gKG1vc3QgY29tcGF0aWJsZSwgZXZlbiBJRTYpXG4gKlxuICogQnJvd3NlcnMgdGhhdCBzdXBwb3J0IHR5cGVkIGFycmF5cyBhcmUgSUUgMTArLCBGaXJlZm94IDQrLCBDaHJvbWUgNyssIFNhZmFyaSA1LjErLFxuICogT3BlcmEgMTEuNissIGlPUyA0LjIrLlxuICpcbiAqIE5vdGU6XG4gKlxuICogLSBJbXBsZW1lbnRhdGlvbiBtdXN0IHN1cHBvcnQgYWRkaW5nIG5ldyBwcm9wZXJ0aWVzIHRvIGBVaW50OEFycmF5YCBpbnN0YW5jZXMuXG4gKiAgIEZpcmVmb3ggNC0yOSBsYWNrZWQgc3VwcG9ydCwgZml4ZWQgaW4gRmlyZWZveCAzMCsuXG4gKiAgIFNlZTogaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9Njk1NDM4LlxuICpcbiAqICAtIENocm9tZSA5LTEwIGlzIG1pc3NpbmcgdGhlIGBUeXBlZEFycmF5LnByb3RvdHlwZS5zdWJhcnJheWAgZnVuY3Rpb24uXG4gKlxuICogIC0gSUUxMCBoYXMgYSBicm9rZW4gYFR5cGVkQXJyYXkucHJvdG90eXBlLnN1YmFycmF5YCBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGFycmF5cyBvZlxuICogICAgaW5jb3JyZWN0IGxlbmd0aCBpbiBzb21lIHNpdHVhdGlvbnMuXG4gKlxuICogV2UgZGV0ZWN0IHRoZXNlIGJ1Z2d5IGJyb3dzZXJzIGFuZCBzZXQgYEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUYCB0byBgZmFsc2VgIHNvIHRoZXkgd2lsbFxuICogZ2V0IHRoZSBPYmplY3QgaW1wbGVtZW50YXRpb24sIHdoaWNoIGlzIHNsb3dlciBidXQgd2lsbCB3b3JrIGNvcnJlY3RseS5cbiAqL1xuQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQgPSAoZnVuY3Rpb24gKCkge1xuICB0cnkge1xuICAgIHZhciBidWYgPSBuZXcgQXJyYXlCdWZmZXIoMClcbiAgICB2YXIgYXJyID0gbmV3IFVpbnQ4QXJyYXkoYnVmKVxuICAgIGFyci5mb28gPSBmdW5jdGlvbiAoKSB7IHJldHVybiA0MiB9XG4gICAgcmV0dXJuIGFyci5mb28oKSA9PT0gNDIgJiYgLy8gdHlwZWQgYXJyYXkgaW5zdGFuY2VzIGNhbiBiZSBhdWdtZW50ZWRcbiAgICAgICAgdHlwZW9mIGFyci5zdWJhcnJheSA9PT0gJ2Z1bmN0aW9uJyAmJiAvLyBjaHJvbWUgOS0xMCBsYWNrIGBzdWJhcnJheWBcbiAgICAgICAgbmV3IFVpbnQ4QXJyYXkoMSkuc3ViYXJyYXkoMSwgMSkuYnl0ZUxlbmd0aCA9PT0gMCAvLyBpZTEwIGhhcyBicm9rZW4gYHN1YmFycmF5YFxuICB9IGNhdGNoIChlKSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cbn0pKClcblxuLyoqXG4gKiBDbGFzczogQnVmZmVyXG4gKiA9PT09PT09PT09PT09XG4gKlxuICogVGhlIEJ1ZmZlciBjb25zdHJ1Y3RvciByZXR1cm5zIGluc3RhbmNlcyBvZiBgVWludDhBcnJheWAgdGhhdCBhcmUgYXVnbWVudGVkXG4gKiB3aXRoIGZ1bmN0aW9uIHByb3BlcnRpZXMgZm9yIGFsbCB0aGUgbm9kZSBgQnVmZmVyYCBBUEkgZnVuY3Rpb25zLiBXZSB1c2VcbiAqIGBVaW50OEFycmF5YCBzbyB0aGF0IHNxdWFyZSBicmFja2V0IG5vdGF0aW9uIHdvcmtzIGFzIGV4cGVjdGVkIC0tIGl0IHJldHVybnNcbiAqIGEgc2luZ2xlIG9jdGV0LlxuICpcbiAqIEJ5IGF1Z21lbnRpbmcgdGhlIGluc3RhbmNlcywgd2UgY2FuIGF2b2lkIG1vZGlmeWluZyB0aGUgYFVpbnQ4QXJyYXlgXG4gKiBwcm90b3R5cGUuXG4gKi9cbmZ1bmN0aW9uIEJ1ZmZlciAoc3ViamVjdCwgZW5jb2RpbmcpIHtcbiAgdmFyIHNlbGYgPSB0aGlzXG4gIGlmICghKHNlbGYgaW5zdGFuY2VvZiBCdWZmZXIpKSByZXR1cm4gbmV3IEJ1ZmZlcihzdWJqZWN0LCBlbmNvZGluZylcblxuICB2YXIgdHlwZSA9IHR5cGVvZiBzdWJqZWN0XG4gIHZhciBsZW5ndGhcblxuICBpZiAodHlwZSA9PT0gJ251bWJlcicpIHtcbiAgICBsZW5ndGggPSArc3ViamVjdFxuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgbGVuZ3RoID0gQnVmZmVyLmJ5dGVMZW5ndGgoc3ViamVjdCwgZW5jb2RpbmcpXG4gIH0gZWxzZSBpZiAodHlwZSA9PT0gJ29iamVjdCcgJiYgc3ViamVjdCAhPT0gbnVsbCkge1xuICAgIC8vIGFzc3VtZSBvYmplY3QgaXMgYXJyYXktbGlrZVxuICAgIGlmIChzdWJqZWN0LnR5cGUgPT09ICdCdWZmZXInICYmIGlzQXJyYXkoc3ViamVjdC5kYXRhKSkgc3ViamVjdCA9IHN1YmplY3QuZGF0YVxuICAgIGxlbmd0aCA9ICtzdWJqZWN0Lmxlbmd0aFxuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ211c3Qgc3RhcnQgd2l0aCBudW1iZXIsIGJ1ZmZlciwgYXJyYXkgb3Igc3RyaW5nJylcbiAgfVxuXG4gIGlmIChsZW5ndGggPiBrTWF4TGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0F0dGVtcHQgdG8gYWxsb2NhdGUgQnVmZmVyIGxhcmdlciB0aGFuIG1heGltdW0gc2l6ZTogMHgnICtcbiAgICAgIGtNYXhMZW5ndGgudG9TdHJpbmcoMTYpICsgJyBieXRlcycpXG4gIH1cblxuICBpZiAobGVuZ3RoIDwgMCkgbGVuZ3RoID0gMFxuICBlbHNlIGxlbmd0aCA+Pj49IDAgLy8gY29lcmNlIHRvIHVpbnQzMlxuXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIC8vIFByZWZlcnJlZDogUmV0dXJuIGFuIGF1Z21lbnRlZCBgVWludDhBcnJheWAgaW5zdGFuY2UgZm9yIGJlc3QgcGVyZm9ybWFuY2VcbiAgICBzZWxmID0gQnVmZmVyLl9hdWdtZW50KG5ldyBVaW50OEFycmF5KGxlbmd0aCkpIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgY29uc2lzdGVudC10aGlzXG4gIH0gZWxzZSB7XG4gICAgLy8gRmFsbGJhY2s6IFJldHVybiBUSElTIGluc3RhbmNlIG9mIEJ1ZmZlciAoY3JlYXRlZCBieSBgbmV3YClcbiAgICBzZWxmLmxlbmd0aCA9IGxlbmd0aFxuICAgIHNlbGYuX2lzQnVmZmVyID0gdHJ1ZVxuICB9XG5cbiAgdmFyIGlcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUICYmIHR5cGVvZiBzdWJqZWN0LmJ5dGVMZW5ndGggPT09ICdudW1iZXInKSB7XG4gICAgLy8gU3BlZWQgb3B0aW1pemF0aW9uIC0tIHVzZSBzZXQgaWYgd2UncmUgY29weWluZyBmcm9tIGEgdHlwZWQgYXJyYXlcbiAgICBzZWxmLl9zZXQoc3ViamVjdClcbiAgfSBlbHNlIGlmIChpc0FycmF5aXNoKHN1YmplY3QpKSB7XG4gICAgLy8gVHJlYXQgYXJyYXktaXNoIG9iamVjdHMgYXMgYSBieXRlIGFycmF5XG4gICAgaWYgKEJ1ZmZlci5pc0J1ZmZlcihzdWJqZWN0KSkge1xuICAgICAgZm9yIChpID0gMDsgaSA8IGxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHNlbGZbaV0gPSBzdWJqZWN0LnJlYWRVSW50OChpKVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBmb3IgKGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgc2VsZltpXSA9ICgoc3ViamVjdFtpXSAlIDI1NikgKyAyNTYpICUgMjU2XG4gICAgICB9XG4gICAgfVxuICB9IGVsc2UgaWYgKHR5cGUgPT09ICdzdHJpbmcnKSB7XG4gICAgc2VsZi53cml0ZShzdWJqZWN0LCAwLCBlbmNvZGluZylcbiAgfSBlbHNlIGlmICh0eXBlID09PSAnbnVtYmVyJyAmJiAhQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHtcbiAgICBmb3IgKGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgIHNlbGZbaV0gPSAwXG4gICAgfVxuICB9XG5cbiAgaWYgKGxlbmd0aCA+IDAgJiYgbGVuZ3RoIDw9IEJ1ZmZlci5wb29sU2l6ZSkgc2VsZi5wYXJlbnQgPSByb290UGFyZW50XG5cbiAgcmV0dXJuIHNlbGZcbn1cblxuZnVuY3Rpb24gU2xvd0J1ZmZlciAoc3ViamVjdCwgZW5jb2RpbmcpIHtcbiAgaWYgKCEodGhpcyBpbnN0YW5jZW9mIFNsb3dCdWZmZXIpKSByZXR1cm4gbmV3IFNsb3dCdWZmZXIoc3ViamVjdCwgZW5jb2RpbmcpXG5cbiAgdmFyIGJ1ZiA9IG5ldyBCdWZmZXIoc3ViamVjdCwgZW5jb2RpbmcpXG4gIGRlbGV0ZSBidWYucGFyZW50XG4gIHJldHVybiBidWZcbn1cblxuQnVmZmVyLmlzQnVmZmVyID0gZnVuY3Rpb24gaXNCdWZmZXIgKGIpIHtcbiAgcmV0dXJuICEhKGIgIT0gbnVsbCAmJiBiLl9pc0J1ZmZlcilcbn1cblxuQnVmZmVyLmNvbXBhcmUgPSBmdW5jdGlvbiBjb21wYXJlIChhLCBiKSB7XG4gIGlmICghQnVmZmVyLmlzQnVmZmVyKGEpIHx8ICFCdWZmZXIuaXNCdWZmZXIoYikpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdBcmd1bWVudHMgbXVzdCBiZSBCdWZmZXJzJylcbiAgfVxuXG4gIGlmIChhID09PSBiKSByZXR1cm4gMFxuXG4gIHZhciB4ID0gYS5sZW5ndGhcbiAgdmFyIHkgPSBiLmxlbmd0aFxuICBmb3IgKHZhciBpID0gMCwgbGVuID0gTWF0aC5taW4oeCwgeSk7IGkgPCBsZW4gJiYgYVtpXSA9PT0gYltpXTsgaSsrKSB7fVxuICBpZiAoaSAhPT0gbGVuKSB7XG4gICAgeCA9IGFbaV1cbiAgICB5ID0gYltpXVxuICB9XG4gIGlmICh4IDwgeSkgcmV0dXJuIC0xXG4gIGlmICh5IDwgeCkgcmV0dXJuIDFcbiAgcmV0dXJuIDBcbn1cblxuQnVmZmVyLmlzRW5jb2RpbmcgPSBmdW5jdGlvbiBpc0VuY29kaW5nIChlbmNvZGluZykge1xuICBzd2l0Y2ggKFN0cmluZyhlbmNvZGluZykudG9Mb3dlckNhc2UoKSkge1xuICAgIGNhc2UgJ2hleCc6XG4gICAgY2FzZSAndXRmOCc6XG4gICAgY2FzZSAndXRmLTgnOlxuICAgIGNhc2UgJ2FzY2lpJzpcbiAgICBjYXNlICdiaW5hcnknOlxuICAgIGNhc2UgJ2Jhc2U2NCc6XG4gICAgY2FzZSAncmF3JzpcbiAgICBjYXNlICd1Y3MyJzpcbiAgICBjYXNlICd1Y3MtMic6XG4gICAgY2FzZSAndXRmMTZsZSc6XG4gICAgY2FzZSAndXRmLTE2bGUnOlxuICAgICAgcmV0dXJuIHRydWVcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuIGZhbHNlXG4gIH1cbn1cblxuQnVmZmVyLmNvbmNhdCA9IGZ1bmN0aW9uIGNvbmNhdCAobGlzdCwgdG90YWxMZW5ndGgpIHtcbiAgaWYgKCFpc0FycmF5KGxpc3QpKSB0aHJvdyBuZXcgVHlwZUVycm9yKCdsaXN0IGFyZ3VtZW50IG11c3QgYmUgYW4gQXJyYXkgb2YgQnVmZmVycy4nKVxuXG4gIGlmIChsaXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBuZXcgQnVmZmVyKDApXG4gIH0gZWxzZSBpZiAobGlzdC5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gbGlzdFswXVxuICB9XG5cbiAgdmFyIGlcbiAgaWYgKHRvdGFsTGVuZ3RoID09PSB1bmRlZmluZWQpIHtcbiAgICB0b3RhbExlbmd0aCA9IDBcbiAgICBmb3IgKGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7IGkrKykge1xuICAgICAgdG90YWxMZW5ndGggKz0gbGlzdFtpXS5sZW5ndGhcbiAgICB9XG4gIH1cblxuICB2YXIgYnVmID0gbmV3IEJ1ZmZlcih0b3RhbExlbmd0aClcbiAgdmFyIHBvcyA9IDBcbiAgZm9yIChpID0gMDsgaSA8IGxpc3QubGVuZ3RoOyBpKyspIHtcbiAgICB2YXIgaXRlbSA9IGxpc3RbaV1cbiAgICBpdGVtLmNvcHkoYnVmLCBwb3MpXG4gICAgcG9zICs9IGl0ZW0ubGVuZ3RoXG4gIH1cbiAgcmV0dXJuIGJ1ZlxufVxuXG5CdWZmZXIuYnl0ZUxlbmd0aCA9IGZ1bmN0aW9uIGJ5dGVMZW5ndGggKHN0ciwgZW5jb2RpbmcpIHtcbiAgdmFyIHJldFxuICBzdHIgPSBzdHIgKyAnJ1xuICBzd2l0Y2ggKGVuY29kaW5nIHx8ICd1dGY4Jykge1xuICAgIGNhc2UgJ2FzY2lpJzpcbiAgICBjYXNlICdiaW5hcnknOlxuICAgIGNhc2UgJ3Jhdyc6XG4gICAgICByZXQgPSBzdHIubGVuZ3RoXG4gICAgICBicmVha1xuICAgIGNhc2UgJ3VjczInOlxuICAgIGNhc2UgJ3Vjcy0yJzpcbiAgICBjYXNlICd1dGYxNmxlJzpcbiAgICBjYXNlICd1dGYtMTZsZSc6XG4gICAgICByZXQgPSBzdHIubGVuZ3RoICogMlxuICAgICAgYnJlYWtcbiAgICBjYXNlICdoZXgnOlxuICAgICAgcmV0ID0gc3RyLmxlbmd0aCA+Pj4gMVxuICAgICAgYnJlYWtcbiAgICBjYXNlICd1dGY4JzpcbiAgICBjYXNlICd1dGYtOCc6XG4gICAgICByZXQgPSB1dGY4VG9CeXRlcyhzdHIpLmxlbmd0aFxuICAgICAgYnJlYWtcbiAgICBjYXNlICdiYXNlNjQnOlxuICAgICAgcmV0ID0gYmFzZTY0VG9CeXRlcyhzdHIpLmxlbmd0aFxuICAgICAgYnJlYWtcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0ID0gc3RyLmxlbmd0aFxuICB9XG4gIHJldHVybiByZXRcbn1cblxuLy8gcHJlLXNldCBmb3IgdmFsdWVzIHRoYXQgbWF5IGV4aXN0IGluIHRoZSBmdXR1cmVcbkJ1ZmZlci5wcm90b3R5cGUubGVuZ3RoID0gdW5kZWZpbmVkXG5CdWZmZXIucHJvdG90eXBlLnBhcmVudCA9IHVuZGVmaW5lZFxuXG4vLyB0b1N0cmluZyhlbmNvZGluZywgc3RhcnQ9MCwgZW5kPWJ1ZmZlci5sZW5ndGgpXG5CdWZmZXIucHJvdG90eXBlLnRvU3RyaW5nID0gZnVuY3Rpb24gdG9TdHJpbmcgKGVuY29kaW5nLCBzdGFydCwgZW5kKSB7XG4gIHZhciBsb3dlcmVkQ2FzZSA9IGZhbHNlXG5cbiAgc3RhcnQgPSBzdGFydCA+Pj4gMFxuICBlbmQgPSBlbmQgPT09IHVuZGVmaW5lZCB8fCBlbmQgPT09IEluZmluaXR5ID8gdGhpcy5sZW5ndGggOiBlbmQgPj4+IDBcblxuICBpZiAoIWVuY29kaW5nKSBlbmNvZGluZyA9ICd1dGY4J1xuICBpZiAoc3RhcnQgPCAwKSBzdGFydCA9IDBcbiAgaWYgKGVuZCA+IHRoaXMubGVuZ3RoKSBlbmQgPSB0aGlzLmxlbmd0aFxuICBpZiAoZW5kIDw9IHN0YXJ0KSByZXR1cm4gJydcblxuICB3aGlsZSAodHJ1ZSkge1xuICAgIHN3aXRjaCAoZW5jb2RpbmcpIHtcbiAgICAgIGNhc2UgJ2hleCc6XG4gICAgICAgIHJldHVybiBoZXhTbGljZSh0aGlzLCBzdGFydCwgZW5kKVxuXG4gICAgICBjYXNlICd1dGY4JzpcbiAgICAgIGNhc2UgJ3V0Zi04JzpcbiAgICAgICAgcmV0dXJuIHV0ZjhTbGljZSh0aGlzLCBzdGFydCwgZW5kKVxuXG4gICAgICBjYXNlICdhc2NpaSc6XG4gICAgICAgIHJldHVybiBhc2NpaVNsaWNlKHRoaXMsIHN0YXJ0LCBlbmQpXG5cbiAgICAgIGNhc2UgJ2JpbmFyeSc6XG4gICAgICAgIHJldHVybiBiaW5hcnlTbGljZSh0aGlzLCBzdGFydCwgZW5kKVxuXG4gICAgICBjYXNlICdiYXNlNjQnOlxuICAgICAgICByZXR1cm4gYmFzZTY0U2xpY2UodGhpcywgc3RhcnQsIGVuZClcblxuICAgICAgY2FzZSAndWNzMic6XG4gICAgICBjYXNlICd1Y3MtMic6XG4gICAgICBjYXNlICd1dGYxNmxlJzpcbiAgICAgIGNhc2UgJ3V0Zi0xNmxlJzpcbiAgICAgICAgcmV0dXJuIHV0ZjE2bGVTbGljZSh0aGlzLCBzdGFydCwgZW5kKVxuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBpZiAobG93ZXJlZENhc2UpIHRocm93IG5ldyBUeXBlRXJyb3IoJ1Vua25vd24gZW5jb2Rpbmc6ICcgKyBlbmNvZGluZylcbiAgICAgICAgZW5jb2RpbmcgPSAoZW5jb2RpbmcgKyAnJykudG9Mb3dlckNhc2UoKVxuICAgICAgICBsb3dlcmVkQ2FzZSA9IHRydWVcbiAgICB9XG4gIH1cbn1cblxuQnVmZmVyLnByb3RvdHlwZS5lcXVhbHMgPSBmdW5jdGlvbiBlcXVhbHMgKGIpIHtcbiAgaWYgKCFCdWZmZXIuaXNCdWZmZXIoYikpIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FyZ3VtZW50IG11c3QgYmUgYSBCdWZmZXInKVxuICBpZiAodGhpcyA9PT0gYikgcmV0dXJuIHRydWVcbiAgcmV0dXJuIEJ1ZmZlci5jb21wYXJlKHRoaXMsIGIpID09PSAwXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUuaW5zcGVjdCA9IGZ1bmN0aW9uIGluc3BlY3QgKCkge1xuICB2YXIgc3RyID0gJydcbiAgdmFyIG1heCA9IGV4cG9ydHMuSU5TUEVDVF9NQVhfQllURVNcbiAgaWYgKHRoaXMubGVuZ3RoID4gMCkge1xuICAgIHN0ciA9IHRoaXMudG9TdHJpbmcoJ2hleCcsIDAsIG1heCkubWF0Y2goLy57Mn0vZykuam9pbignICcpXG4gICAgaWYgKHRoaXMubGVuZ3RoID4gbWF4KSBzdHIgKz0gJyAuLi4gJ1xuICB9XG4gIHJldHVybiAnPEJ1ZmZlciAnICsgc3RyICsgJz4nXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUuY29tcGFyZSA9IGZ1bmN0aW9uIGNvbXBhcmUgKGIpIHtcbiAgaWYgKCFCdWZmZXIuaXNCdWZmZXIoYikpIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FyZ3VtZW50IG11c3QgYmUgYSBCdWZmZXInKVxuICBpZiAodGhpcyA9PT0gYikgcmV0dXJuIDBcbiAgcmV0dXJuIEJ1ZmZlci5jb21wYXJlKHRoaXMsIGIpXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUuaW5kZXhPZiA9IGZ1bmN0aW9uIGluZGV4T2YgKHZhbCwgYnl0ZU9mZnNldCkge1xuICBpZiAoYnl0ZU9mZnNldCA+IDB4N2ZmZmZmZmYpIGJ5dGVPZmZzZXQgPSAweDdmZmZmZmZmXG4gIGVsc2UgaWYgKGJ5dGVPZmZzZXQgPCAtMHg4MDAwMDAwMCkgYnl0ZU9mZnNldCA9IC0weDgwMDAwMDAwXG4gIGJ5dGVPZmZzZXQgPj49IDBcblxuICBpZiAodGhpcy5sZW5ndGggPT09IDApIHJldHVybiAtMVxuICBpZiAoYnl0ZU9mZnNldCA+PSB0aGlzLmxlbmd0aCkgcmV0dXJuIC0xXG5cbiAgLy8gTmVnYXRpdmUgb2Zmc2V0cyBzdGFydCBmcm9tIHRoZSBlbmQgb2YgdGhlIGJ1ZmZlclxuICBpZiAoYnl0ZU9mZnNldCA8IDApIGJ5dGVPZmZzZXQgPSBNYXRoLm1heCh0aGlzLmxlbmd0aCArIGJ5dGVPZmZzZXQsIDApXG5cbiAgaWYgKHR5cGVvZiB2YWwgPT09ICdzdHJpbmcnKSB7XG4gICAgaWYgKHZhbC5sZW5ndGggPT09IDApIHJldHVybiAtMSAvLyBzcGVjaWFsIGNhc2U6IGxvb2tpbmcgZm9yIGVtcHR5IHN0cmluZyBhbHdheXMgZmFpbHNcbiAgICByZXR1cm4gU3RyaW5nLnByb3RvdHlwZS5pbmRleE9mLmNhbGwodGhpcywgdmFsLCBieXRlT2Zmc2V0KVxuICB9XG4gIGlmIChCdWZmZXIuaXNCdWZmZXIodmFsKSkge1xuICAgIHJldHVybiBhcnJheUluZGV4T2YodGhpcywgdmFsLCBieXRlT2Zmc2V0KVxuICB9XG4gIGlmICh0eXBlb2YgdmFsID09PSAnbnVtYmVyJykge1xuICAgIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCAmJiBVaW50OEFycmF5LnByb3RvdHlwZS5pbmRleE9mID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gVWludDhBcnJheS5wcm90b3R5cGUuaW5kZXhPZi5jYWxsKHRoaXMsIHZhbCwgYnl0ZU9mZnNldClcbiAgICB9XG4gICAgcmV0dXJuIGFycmF5SW5kZXhPZih0aGlzLCBbIHZhbCBdLCBieXRlT2Zmc2V0KVxuICB9XG5cbiAgZnVuY3Rpb24gYXJyYXlJbmRleE9mIChhcnIsIHZhbCwgYnl0ZU9mZnNldCkge1xuICAgIHZhciBmb3VuZEluZGV4ID0gLTFcbiAgICBmb3IgKHZhciBpID0gMDsgYnl0ZU9mZnNldCArIGkgPCBhcnIubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChhcnJbYnl0ZU9mZnNldCArIGldID09PSB2YWxbZm91bmRJbmRleCA9PT0gLTEgPyAwIDogaSAtIGZvdW5kSW5kZXhdKSB7XG4gICAgICAgIGlmIChmb3VuZEluZGV4ID09PSAtMSkgZm91bmRJbmRleCA9IGlcbiAgICAgICAgaWYgKGkgLSBmb3VuZEluZGV4ICsgMSA9PT0gdmFsLmxlbmd0aCkgcmV0dXJuIGJ5dGVPZmZzZXQgKyBmb3VuZEluZGV4XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmb3VuZEluZGV4ID0gLTFcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIC0xXG4gIH1cblxuICB0aHJvdyBuZXcgVHlwZUVycm9yKCd2YWwgbXVzdCBiZSBzdHJpbmcsIG51bWJlciBvciBCdWZmZXInKVxufVxuXG4vLyBgZ2V0YCB3aWxsIGJlIHJlbW92ZWQgaW4gTm9kZSAwLjEzK1xuQnVmZmVyLnByb3RvdHlwZS5nZXQgPSBmdW5jdGlvbiBnZXQgKG9mZnNldCkge1xuICBjb25zb2xlLmxvZygnLmdldCgpIGlzIGRlcHJlY2F0ZWQuIEFjY2VzcyB1c2luZyBhcnJheSBpbmRleGVzIGluc3RlYWQuJylcbiAgcmV0dXJuIHRoaXMucmVhZFVJbnQ4KG9mZnNldClcbn1cblxuLy8gYHNldGAgd2lsbCBiZSByZW1vdmVkIGluIE5vZGUgMC4xMytcbkJ1ZmZlci5wcm90b3R5cGUuc2V0ID0gZnVuY3Rpb24gc2V0ICh2LCBvZmZzZXQpIHtcbiAgY29uc29sZS5sb2coJy5zZXQoKSBpcyBkZXByZWNhdGVkLiBBY2Nlc3MgdXNpbmcgYXJyYXkgaW5kZXhlcyBpbnN0ZWFkLicpXG4gIHJldHVybiB0aGlzLndyaXRlVUludDgodiwgb2Zmc2V0KVxufVxuXG5mdW5jdGlvbiBoZXhXcml0ZSAoYnVmLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKSB7XG4gIG9mZnNldCA9IE51bWJlcihvZmZzZXQpIHx8IDBcbiAgdmFyIHJlbWFpbmluZyA9IGJ1Zi5sZW5ndGggLSBvZmZzZXRcbiAgaWYgKCFsZW5ndGgpIHtcbiAgICBsZW5ndGggPSByZW1haW5pbmdcbiAgfSBlbHNlIHtcbiAgICBsZW5ndGggPSBOdW1iZXIobGVuZ3RoKVxuICAgIGlmIChsZW5ndGggPiByZW1haW5pbmcpIHtcbiAgICAgIGxlbmd0aCA9IHJlbWFpbmluZ1xuICAgIH1cbiAgfVxuXG4gIC8vIG11c3QgYmUgYW4gZXZlbiBudW1iZXIgb2YgZGlnaXRzXG4gIHZhciBzdHJMZW4gPSBzdHJpbmcubGVuZ3RoXG4gIGlmIChzdHJMZW4gJSAyICE9PSAwKSB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgaGV4IHN0cmluZycpXG5cbiAgaWYgKGxlbmd0aCA+IHN0ckxlbiAvIDIpIHtcbiAgICBsZW5ndGggPSBzdHJMZW4gLyAyXG4gIH1cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgIHZhciBwYXJzZWQgPSBwYXJzZUludChzdHJpbmcuc3Vic3RyKGkgKiAyLCAyKSwgMTYpXG4gICAgaWYgKGlzTmFOKHBhcnNlZCkpIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBoZXggc3RyaW5nJylcbiAgICBidWZbb2Zmc2V0ICsgaV0gPSBwYXJzZWRcbiAgfVxuICByZXR1cm4gaVxufVxuXG5mdW5jdGlvbiB1dGY4V3JpdGUgKGJ1Ziwgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aCkge1xuICB2YXIgY2hhcnNXcml0dGVuID0gYmxpdEJ1ZmZlcih1dGY4VG9CeXRlcyhzdHJpbmcsIGJ1Zi5sZW5ndGggLSBvZmZzZXQpLCBidWYsIG9mZnNldCwgbGVuZ3RoKVxuICByZXR1cm4gY2hhcnNXcml0dGVuXG59XG5cbmZ1bmN0aW9uIGFzY2lpV3JpdGUgKGJ1Ziwgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aCkge1xuICB2YXIgY2hhcnNXcml0dGVuID0gYmxpdEJ1ZmZlcihhc2NpaVRvQnl0ZXMoc3RyaW5nKSwgYnVmLCBvZmZzZXQsIGxlbmd0aClcbiAgcmV0dXJuIGNoYXJzV3JpdHRlblxufVxuXG5mdW5jdGlvbiBiaW5hcnlXcml0ZSAoYnVmLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKSB7XG4gIHJldHVybiBhc2NpaVdyaXRlKGJ1Ziwgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aClcbn1cblxuZnVuY3Rpb24gYmFzZTY0V3JpdGUgKGJ1Ziwgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aCkge1xuICB2YXIgY2hhcnNXcml0dGVuID0gYmxpdEJ1ZmZlcihiYXNlNjRUb0J5dGVzKHN0cmluZyksIGJ1Ziwgb2Zmc2V0LCBsZW5ndGgpXG4gIHJldHVybiBjaGFyc1dyaXR0ZW5cbn1cblxuZnVuY3Rpb24gdXRmMTZsZVdyaXRlIChidWYsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpIHtcbiAgdmFyIGNoYXJzV3JpdHRlbiA9IGJsaXRCdWZmZXIodXRmMTZsZVRvQnl0ZXMoc3RyaW5nLCBidWYubGVuZ3RoIC0gb2Zmc2V0KSwgYnVmLCBvZmZzZXQsIGxlbmd0aClcbiAgcmV0dXJuIGNoYXJzV3JpdHRlblxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlID0gZnVuY3Rpb24gd3JpdGUgKHN0cmluZywgb2Zmc2V0LCBsZW5ndGgsIGVuY29kaW5nKSB7XG4gIC8vIFN1cHBvcnQgYm90aCAoc3RyaW5nLCBvZmZzZXQsIGxlbmd0aCwgZW5jb2RpbmcpXG4gIC8vIGFuZCB0aGUgbGVnYWN5IChzdHJpbmcsIGVuY29kaW5nLCBvZmZzZXQsIGxlbmd0aClcbiAgaWYgKGlzRmluaXRlKG9mZnNldCkpIHtcbiAgICBpZiAoIWlzRmluaXRlKGxlbmd0aCkpIHtcbiAgICAgIGVuY29kaW5nID0gbGVuZ3RoXG4gICAgICBsZW5ndGggPSB1bmRlZmluZWRcbiAgICB9XG4gIH0gZWxzZSB7ICAvLyBsZWdhY3lcbiAgICB2YXIgc3dhcCA9IGVuY29kaW5nXG4gICAgZW5jb2RpbmcgPSBvZmZzZXRcbiAgICBvZmZzZXQgPSBsZW5ndGhcbiAgICBsZW5ndGggPSBzd2FwXG4gIH1cblxuICBvZmZzZXQgPSBOdW1iZXIob2Zmc2V0KSB8fCAwXG5cbiAgaWYgKGxlbmd0aCA8IDAgfHwgb2Zmc2V0IDwgMCB8fCBvZmZzZXQgPiB0aGlzLmxlbmd0aCkge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdhdHRlbXB0IHRvIHdyaXRlIG91dHNpZGUgYnVmZmVyIGJvdW5kcycpXG4gIH1cblxuICB2YXIgcmVtYWluaW5nID0gdGhpcy5sZW5ndGggLSBvZmZzZXRcbiAgaWYgKCFsZW5ndGgpIHtcbiAgICBsZW5ndGggPSByZW1haW5pbmdcbiAgfSBlbHNlIHtcbiAgICBsZW5ndGggPSBOdW1iZXIobGVuZ3RoKVxuICAgIGlmIChsZW5ndGggPiByZW1haW5pbmcpIHtcbiAgICAgIGxlbmd0aCA9IHJlbWFpbmluZ1xuICAgIH1cbiAgfVxuICBlbmNvZGluZyA9IFN0cmluZyhlbmNvZGluZyB8fCAndXRmOCcpLnRvTG93ZXJDYXNlKClcblxuICB2YXIgcmV0XG4gIHN3aXRjaCAoZW5jb2RpbmcpIHtcbiAgICBjYXNlICdoZXgnOlxuICAgICAgcmV0ID0gaGV4V3JpdGUodGhpcywgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aClcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAndXRmOCc6XG4gICAgY2FzZSAndXRmLTgnOlxuICAgICAgcmV0ID0gdXRmOFdyaXRlKHRoaXMsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpXG4gICAgICBicmVha1xuICAgIGNhc2UgJ2FzY2lpJzpcbiAgICAgIHJldCA9IGFzY2lpV3JpdGUodGhpcywgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aClcbiAgICAgIGJyZWFrXG4gICAgY2FzZSAnYmluYXJ5JzpcbiAgICAgIHJldCA9IGJpbmFyeVdyaXRlKHRoaXMsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpXG4gICAgICBicmVha1xuICAgIGNhc2UgJ2Jhc2U2NCc6XG4gICAgICByZXQgPSBiYXNlNjRXcml0ZSh0aGlzLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKVxuICAgICAgYnJlYWtcbiAgICBjYXNlICd1Y3MyJzpcbiAgICBjYXNlICd1Y3MtMic6XG4gICAgY2FzZSAndXRmMTZsZSc6XG4gICAgY2FzZSAndXRmLTE2bGUnOlxuICAgICAgcmV0ID0gdXRmMTZsZVdyaXRlKHRoaXMsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpXG4gICAgICBicmVha1xuICAgIGRlZmF1bHQ6XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdVbmtub3duIGVuY29kaW5nOiAnICsgZW5jb2RpbmcpXG4gIH1cbiAgcmV0dXJuIHJldFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnRvSlNPTiA9IGZ1bmN0aW9uIHRvSlNPTiAoKSB7XG4gIHJldHVybiB7XG4gICAgdHlwZTogJ0J1ZmZlcicsXG4gICAgZGF0YTogQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodGhpcy5fYXJyIHx8IHRoaXMsIDApXG4gIH1cbn1cblxuZnVuY3Rpb24gYmFzZTY0U2xpY2UgKGJ1Ziwgc3RhcnQsIGVuZCkge1xuICBpZiAoc3RhcnQgPT09IDAgJiYgZW5kID09PSBidWYubGVuZ3RoKSB7XG4gICAgcmV0dXJuIGJhc2U2NC5mcm9tQnl0ZUFycmF5KGJ1ZilcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gYmFzZTY0LmZyb21CeXRlQXJyYXkoYnVmLnNsaWNlKHN0YXJ0LCBlbmQpKVxuICB9XG59XG5cbmZ1bmN0aW9uIHV0ZjhTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciByZXMgPSAnJ1xuICB2YXIgdG1wID0gJydcbiAgZW5kID0gTWF0aC5taW4oYnVmLmxlbmd0aCwgZW5kKVxuXG4gIGZvciAodmFyIGkgPSBzdGFydDsgaSA8IGVuZDsgaSsrKSB7XG4gICAgaWYgKGJ1ZltpXSA8PSAweDdGKSB7XG4gICAgICByZXMgKz0gZGVjb2RlVXRmOENoYXIodG1wKSArIFN0cmluZy5mcm9tQ2hhckNvZGUoYnVmW2ldKVxuICAgICAgdG1wID0gJydcbiAgICB9IGVsc2Uge1xuICAgICAgdG1wICs9ICclJyArIGJ1ZltpXS50b1N0cmluZygxNilcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcmVzICsgZGVjb2RlVXRmOENoYXIodG1wKVxufVxuXG5mdW5jdGlvbiBhc2NpaVNsaWNlIChidWYsIHN0YXJ0LCBlbmQpIHtcbiAgdmFyIHJldCA9ICcnXG4gIGVuZCA9IE1hdGgubWluKGJ1Zi5sZW5ndGgsIGVuZClcblxuICBmb3IgKHZhciBpID0gc3RhcnQ7IGkgPCBlbmQ7IGkrKykge1xuICAgIHJldCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ1ZltpXSAmIDB4N0YpXG4gIH1cbiAgcmV0dXJuIHJldFxufVxuXG5mdW5jdGlvbiBiaW5hcnlTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciByZXQgPSAnJ1xuICBlbmQgPSBNYXRoLm1pbihidWYubGVuZ3RoLCBlbmQpXG5cbiAgZm9yICh2YXIgaSA9IHN0YXJ0OyBpIDwgZW5kOyBpKyspIHtcbiAgICByZXQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShidWZbaV0pXG4gIH1cbiAgcmV0dXJuIHJldFxufVxuXG5mdW5jdGlvbiBoZXhTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciBsZW4gPSBidWYubGVuZ3RoXG5cbiAgaWYgKCFzdGFydCB8fCBzdGFydCA8IDApIHN0YXJ0ID0gMFxuICBpZiAoIWVuZCB8fCBlbmQgPCAwIHx8IGVuZCA+IGxlbikgZW5kID0gbGVuXG5cbiAgdmFyIG91dCA9ICcnXG4gIGZvciAodmFyIGkgPSBzdGFydDsgaSA8IGVuZDsgaSsrKSB7XG4gICAgb3V0ICs9IHRvSGV4KGJ1ZltpXSlcbiAgfVxuICByZXR1cm4gb3V0XG59XG5cbmZ1bmN0aW9uIHV0ZjE2bGVTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciBieXRlcyA9IGJ1Zi5zbGljZShzdGFydCwgZW5kKVxuICB2YXIgcmVzID0gJydcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBieXRlcy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHJlcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ5dGVzW2ldICsgYnl0ZXNbaSArIDFdICogMjU2KVxuICB9XG4gIHJldHVybiByZXNcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5zbGljZSA9IGZ1bmN0aW9uIHNsaWNlIChzdGFydCwgZW5kKSB7XG4gIHZhciBsZW4gPSB0aGlzLmxlbmd0aFxuICBzdGFydCA9IH5+c3RhcnRcbiAgZW5kID0gZW5kID09PSB1bmRlZmluZWQgPyBsZW4gOiB+fmVuZFxuXG4gIGlmIChzdGFydCA8IDApIHtcbiAgICBzdGFydCArPSBsZW5cbiAgICBpZiAoc3RhcnQgPCAwKSBzdGFydCA9IDBcbiAgfSBlbHNlIGlmIChzdGFydCA+IGxlbikge1xuICAgIHN0YXJ0ID0gbGVuXG4gIH1cblxuICBpZiAoZW5kIDwgMCkge1xuICAgIGVuZCArPSBsZW5cbiAgICBpZiAoZW5kIDwgMCkgZW5kID0gMFxuICB9IGVsc2UgaWYgKGVuZCA+IGxlbikge1xuICAgIGVuZCA9IGxlblxuICB9XG5cbiAgaWYgKGVuZCA8IHN0YXJ0KSBlbmQgPSBzdGFydFxuXG4gIHZhciBuZXdCdWZcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgbmV3QnVmID0gQnVmZmVyLl9hdWdtZW50KHRoaXMuc3ViYXJyYXkoc3RhcnQsIGVuZCkpXG4gIH0gZWxzZSB7XG4gICAgdmFyIHNsaWNlTGVuID0gZW5kIC0gc3RhcnRcbiAgICBuZXdCdWYgPSBuZXcgQnVmZmVyKHNsaWNlTGVuLCB1bmRlZmluZWQpXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzbGljZUxlbjsgaSsrKSB7XG4gICAgICBuZXdCdWZbaV0gPSB0aGlzW2kgKyBzdGFydF1cbiAgICB9XG4gIH1cblxuICBpZiAobmV3QnVmLmxlbmd0aCkgbmV3QnVmLnBhcmVudCA9IHRoaXMucGFyZW50IHx8IHRoaXNcblxuICByZXR1cm4gbmV3QnVmXG59XG5cbi8qXG4gKiBOZWVkIHRvIG1ha2Ugc3VyZSB0aGF0IGJ1ZmZlciBpc24ndCB0cnlpbmcgdG8gd3JpdGUgb3V0IG9mIGJvdW5kcy5cbiAqL1xuZnVuY3Rpb24gY2hlY2tPZmZzZXQgKG9mZnNldCwgZXh0LCBsZW5ndGgpIHtcbiAgaWYgKChvZmZzZXQgJSAxKSAhPT0gMCB8fCBvZmZzZXQgPCAwKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignb2Zmc2V0IGlzIG5vdCB1aW50JylcbiAgaWYgKG9mZnNldCArIGV4dCA+IGxlbmd0aCkgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1RyeWluZyB0byBhY2Nlc3MgYmV5b25kIGJ1ZmZlciBsZW5ndGgnKVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRVSW50TEUgPSBmdW5jdGlvbiByZWFkVUludExFIChvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBieXRlTGVuZ3RoID0gYnl0ZUxlbmd0aCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIGJ5dGVMZW5ndGgsIHRoaXMubGVuZ3RoKVxuXG4gIHZhciB2YWwgPSB0aGlzW29mZnNldF1cbiAgdmFyIG11bCA9IDFcbiAgdmFyIGkgPSAwXG4gIHdoaWxlICgrK2kgPCBieXRlTGVuZ3RoICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdmFsICs9IHRoaXNbb2Zmc2V0ICsgaV0gKiBtdWxcbiAgfVxuXG4gIHJldHVybiB2YWxcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkVUludEJFID0gZnVuY3Rpb24gcmVhZFVJbnRCRSAob2Zmc2V0LCBieXRlTGVuZ3RoLCBub0Fzc2VydCkge1xuICBvZmZzZXQgPSBvZmZzZXQgPj4+IDBcbiAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggPj4+IDBcbiAgaWYgKCFub0Fzc2VydCkge1xuICAgIGNoZWNrT2Zmc2V0KG9mZnNldCwgYnl0ZUxlbmd0aCwgdGhpcy5sZW5ndGgpXG4gIH1cblxuICB2YXIgdmFsID0gdGhpc1tvZmZzZXQgKyAtLWJ5dGVMZW5ndGhdXG4gIHZhciBtdWwgPSAxXG4gIHdoaWxlIChieXRlTGVuZ3RoID4gMCAmJiAobXVsICo9IDB4MTAwKSkge1xuICAgIHZhbCArPSB0aGlzW29mZnNldCArIC0tYnl0ZUxlbmd0aF0gKiBtdWxcbiAgfVxuXG4gIHJldHVybiB2YWxcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkVUludDggPSBmdW5jdGlvbiByZWFkVUludDggKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCAxLCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuIHRoaXNbb2Zmc2V0XVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRVSW50MTZMRSA9IGZ1bmN0aW9uIHJlYWRVSW50MTZMRSAob2Zmc2V0LCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIDIsIHRoaXMubGVuZ3RoKVxuICByZXR1cm4gdGhpc1tvZmZzZXRdIHwgKHRoaXNbb2Zmc2V0ICsgMV0gPDwgOClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkVUludDE2QkUgPSBmdW5jdGlvbiByZWFkVUludDE2QkUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCAyLCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuICh0aGlzW29mZnNldF0gPDwgOCkgfCB0aGlzW29mZnNldCArIDFdXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZFVJbnQzMkxFID0gZnVuY3Rpb24gcmVhZFVJbnQzMkxFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgNCwgdGhpcy5sZW5ndGgpXG5cbiAgcmV0dXJuICgodGhpc1tvZmZzZXRdKSB8XG4gICAgICAodGhpc1tvZmZzZXQgKyAxXSA8PCA4KSB8XG4gICAgICAodGhpc1tvZmZzZXQgKyAyXSA8PCAxNikpICtcbiAgICAgICh0aGlzW29mZnNldCArIDNdICogMHgxMDAwMDAwKVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRVSW50MzJCRSA9IGZ1bmN0aW9uIHJlYWRVSW50MzJCRSAob2Zmc2V0LCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIDQsIHRoaXMubGVuZ3RoKVxuXG4gIHJldHVybiAodGhpc1tvZmZzZXRdICogMHgxMDAwMDAwKSArXG4gICAgKCh0aGlzW29mZnNldCArIDFdIDw8IDE2KSB8XG4gICAgKHRoaXNbb2Zmc2V0ICsgMl0gPDwgOCkgfFxuICAgIHRoaXNbb2Zmc2V0ICsgM10pXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZEludExFID0gZnVuY3Rpb24gcmVhZEludExFIChvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBieXRlTGVuZ3RoID0gYnl0ZUxlbmd0aCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIGJ5dGVMZW5ndGgsIHRoaXMubGVuZ3RoKVxuXG4gIHZhciB2YWwgPSB0aGlzW29mZnNldF1cbiAgdmFyIG11bCA9IDFcbiAgdmFyIGkgPSAwXG4gIHdoaWxlICgrK2kgPCBieXRlTGVuZ3RoICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdmFsICs9IHRoaXNbb2Zmc2V0ICsgaV0gKiBtdWxcbiAgfVxuICBtdWwgKj0gMHg4MFxuXG4gIGlmICh2YWwgPj0gbXVsKSB2YWwgLT0gTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGgpXG5cbiAgcmV0dXJuIHZhbFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnRCRSA9IGZ1bmN0aW9uIHJlYWRJbnRCRSAob2Zmc2V0LCBieXRlTGVuZ3RoLCBub0Fzc2VydCkge1xuICBvZmZzZXQgPSBvZmZzZXQgPj4+IDBcbiAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggPj4+IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCBieXRlTGVuZ3RoLCB0aGlzLmxlbmd0aClcblxuICB2YXIgaSA9IGJ5dGVMZW5ndGhcbiAgdmFyIG11bCA9IDFcbiAgdmFyIHZhbCA9IHRoaXNbb2Zmc2V0ICsgLS1pXVxuICB3aGlsZSAoaSA+IDAgJiYgKG11bCAqPSAweDEwMCkpIHtcbiAgICB2YWwgKz0gdGhpc1tvZmZzZXQgKyAtLWldICogbXVsXG4gIH1cbiAgbXVsICo9IDB4ODBcblxuICBpZiAodmFsID49IG11bCkgdmFsIC09IE1hdGgucG93KDIsIDggKiBieXRlTGVuZ3RoKVxuXG4gIHJldHVybiB2YWxcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkSW50OCA9IGZ1bmN0aW9uIHJlYWRJbnQ4IChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgMSwgdGhpcy5sZW5ndGgpXG4gIGlmICghKHRoaXNbb2Zmc2V0XSAmIDB4ODApKSByZXR1cm4gKHRoaXNbb2Zmc2V0XSlcbiAgcmV0dXJuICgoMHhmZiAtIHRoaXNbb2Zmc2V0XSArIDEpICogLTEpXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZEludDE2TEUgPSBmdW5jdGlvbiByZWFkSW50MTZMRSAob2Zmc2V0LCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIDIsIHRoaXMubGVuZ3RoKVxuICB2YXIgdmFsID0gdGhpc1tvZmZzZXRdIHwgKHRoaXNbb2Zmc2V0ICsgMV0gPDwgOClcbiAgcmV0dXJuICh2YWwgJiAweDgwMDApID8gdmFsIHwgMHhGRkZGMDAwMCA6IHZhbFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnQxNkJFID0gZnVuY3Rpb24gcmVhZEludDE2QkUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCAyLCB0aGlzLmxlbmd0aClcbiAgdmFyIHZhbCA9IHRoaXNbb2Zmc2V0ICsgMV0gfCAodGhpc1tvZmZzZXRdIDw8IDgpXG4gIHJldHVybiAodmFsICYgMHg4MDAwKSA/IHZhbCB8IDB4RkZGRjAwMDAgOiB2YWxcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkSW50MzJMRSA9IGZ1bmN0aW9uIHJlYWRJbnQzMkxFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgNCwgdGhpcy5sZW5ndGgpXG5cbiAgcmV0dXJuICh0aGlzW29mZnNldF0pIHxcbiAgICAodGhpc1tvZmZzZXQgKyAxXSA8PCA4KSB8XG4gICAgKHRoaXNbb2Zmc2V0ICsgMl0gPDwgMTYpIHxcbiAgICAodGhpc1tvZmZzZXQgKyAzXSA8PCAyNClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkSW50MzJCRSA9IGZ1bmN0aW9uIHJlYWRJbnQzMkJFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgNCwgdGhpcy5sZW5ndGgpXG5cbiAgcmV0dXJuICh0aGlzW29mZnNldF0gPDwgMjQpIHxcbiAgICAodGhpc1tvZmZzZXQgKyAxXSA8PCAxNikgfFxuICAgICh0aGlzW29mZnNldCArIDJdIDw8IDgpIHxcbiAgICAodGhpc1tvZmZzZXQgKyAzXSlcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkRmxvYXRMRSA9IGZ1bmN0aW9uIHJlYWRGbG9hdExFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgNCwgdGhpcy5sZW5ndGgpXG4gIHJldHVybiBpZWVlNzU0LnJlYWQodGhpcywgb2Zmc2V0LCB0cnVlLCAyMywgNClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkRmxvYXRCRSA9IGZ1bmN0aW9uIHJlYWRGbG9hdEJFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgNCwgdGhpcy5sZW5ndGgpXG4gIHJldHVybiBpZWVlNzU0LnJlYWQodGhpcywgb2Zmc2V0LCBmYWxzZSwgMjMsIDQpXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZERvdWJsZUxFID0gZnVuY3Rpb24gcmVhZERvdWJsZUxFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgOCwgdGhpcy5sZW5ndGgpXG4gIHJldHVybiBpZWVlNzU0LnJlYWQodGhpcywgb2Zmc2V0LCB0cnVlLCA1MiwgOClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkRG91YmxlQkUgPSBmdW5jdGlvbiByZWFkRG91YmxlQkUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA4LCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuIGllZWU3NTQucmVhZCh0aGlzLCBvZmZzZXQsIGZhbHNlLCA1MiwgOClcbn1cblxuZnVuY3Rpb24gY2hlY2tJbnQgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgZXh0LCBtYXgsIG1pbikge1xuICBpZiAoIUJ1ZmZlci5pc0J1ZmZlcihidWYpKSB0aHJvdyBuZXcgVHlwZUVycm9yKCdidWZmZXIgbXVzdCBiZSBhIEJ1ZmZlciBpbnN0YW5jZScpXG4gIGlmICh2YWx1ZSA+IG1heCB8fCB2YWx1ZSA8IG1pbikgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ3ZhbHVlIGlzIG91dCBvZiBib3VuZHMnKVxuICBpZiAob2Zmc2V0ICsgZXh0ID4gYnVmLmxlbmd0aCkgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ2luZGV4IG91dCBvZiByYW5nZScpXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVVSW50TEUgPSBmdW5jdGlvbiB3cml0ZVVJbnRMRSAodmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0ID4+PiAwXG4gIGJ5dGVMZW5ndGggPSBieXRlTGVuZ3RoID4+PiAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIGJ5dGVMZW5ndGgsIE1hdGgucG93KDIsIDggKiBieXRlTGVuZ3RoKSwgMClcblxuICB2YXIgbXVsID0gMVxuICB2YXIgaSA9IDBcbiAgdGhpc1tvZmZzZXRdID0gdmFsdWUgJiAweEZGXG4gIHdoaWxlICgrK2kgPCBieXRlTGVuZ3RoICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdGhpc1tvZmZzZXQgKyBpXSA9ICh2YWx1ZSAvIG11bCkgPj4+IDAgJiAweEZGXG4gIH1cblxuICByZXR1cm4gb2Zmc2V0ICsgYnl0ZUxlbmd0aFxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlVUludEJFID0gZnVuY3Rpb24gd3JpdGVVSW50QkUgKHZhbHVlLCBvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBieXRlTGVuZ3RoID0gYnl0ZUxlbmd0aCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCBieXRlTGVuZ3RoLCBNYXRoLnBvdygyLCA4ICogYnl0ZUxlbmd0aCksIDApXG5cbiAgdmFyIGkgPSBieXRlTGVuZ3RoIC0gMVxuICB2YXIgbXVsID0gMVxuICB0aGlzW29mZnNldCArIGldID0gdmFsdWUgJiAweEZGXG4gIHdoaWxlICgtLWkgPj0gMCAmJiAobXVsICo9IDB4MTAwKSkge1xuICAgIHRoaXNbb2Zmc2V0ICsgaV0gPSAodmFsdWUgLyBtdWwpID4+PiAwICYgMHhGRlxuICB9XG5cbiAgcmV0dXJuIG9mZnNldCArIGJ5dGVMZW5ndGhcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZVVJbnQ4ID0gZnVuY3Rpb24gd3JpdGVVSW50OCAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0ID4+PiAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDEsIDB4ZmYsIDApXG4gIGlmICghQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHZhbHVlID0gTWF0aC5mbG9vcih2YWx1ZSlcbiAgdGhpc1tvZmZzZXRdID0gdmFsdWVcbiAgcmV0dXJuIG9mZnNldCArIDFcbn1cblxuZnVuY3Rpb24gb2JqZWN0V3JpdGVVSW50MTYgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgbGl0dGxlRW5kaWFuKSB7XG4gIGlmICh2YWx1ZSA8IDApIHZhbHVlID0gMHhmZmZmICsgdmFsdWUgKyAxXG4gIGZvciAodmFyIGkgPSAwLCBqID0gTWF0aC5taW4oYnVmLmxlbmd0aCAtIG9mZnNldCwgMik7IGkgPCBqOyBpKyspIHtcbiAgICBidWZbb2Zmc2V0ICsgaV0gPSAodmFsdWUgJiAoMHhmZiA8PCAoOCAqIChsaXR0bGVFbmRpYW4gPyBpIDogMSAtIGkpKSkpID4+PlxuICAgICAgKGxpdHRsZUVuZGlhbiA/IGkgOiAxIC0gaSkgKiA4XG4gIH1cbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZVVJbnQxNkxFID0gZnVuY3Rpb24gd3JpdGVVSW50MTZMRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0ID4+PiAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDIsIDB4ZmZmZiwgMClcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXRdID0gdmFsdWVcbiAgICB0aGlzW29mZnNldCArIDFdID0gKHZhbHVlID4+PiA4KVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDE2KHRoaXMsIHZhbHVlLCBvZmZzZXQsIHRydWUpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDJcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZVVJbnQxNkJFID0gZnVuY3Rpb24gd3JpdGVVSW50MTZCRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0ID4+PiAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDIsIDB4ZmZmZiwgMClcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXRdID0gKHZhbHVlID4+PiA4KVxuICAgIHRoaXNbb2Zmc2V0ICsgMV0gPSB2YWx1ZVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDE2KHRoaXMsIHZhbHVlLCBvZmZzZXQsIGZhbHNlKVxuICB9XG4gIHJldHVybiBvZmZzZXQgKyAyXG59XG5cbmZ1bmN0aW9uIG9iamVjdFdyaXRlVUludDMyIChidWYsIHZhbHVlLCBvZmZzZXQsIGxpdHRsZUVuZGlhbikge1xuICBpZiAodmFsdWUgPCAwKSB2YWx1ZSA9IDB4ZmZmZmZmZmYgKyB2YWx1ZSArIDFcbiAgZm9yICh2YXIgaSA9IDAsIGogPSBNYXRoLm1pbihidWYubGVuZ3RoIC0gb2Zmc2V0LCA0KTsgaSA8IGo7IGkrKykge1xuICAgIGJ1ZltvZmZzZXQgKyBpXSA9ICh2YWx1ZSA+Pj4gKGxpdHRsZUVuZGlhbiA/IGkgOiAzIC0gaSkgKiA4KSAmIDB4ZmZcbiAgfVxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlVUludDMyTEUgPSBmdW5jdGlvbiB3cml0ZVVJbnQzMkxFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICB2YWx1ZSA9ICt2YWx1ZVxuICBvZmZzZXQgPSBvZmZzZXQgPj4+IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgNCwgMHhmZmZmZmZmZiwgMClcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXQgKyAzXSA9ICh2YWx1ZSA+Pj4gMjQpXG4gICAgdGhpc1tvZmZzZXQgKyAyXSA9ICh2YWx1ZSA+Pj4gMTYpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gOClcbiAgICB0aGlzW29mZnNldF0gPSB2YWx1ZVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDMyKHRoaXMsIHZhbHVlLCBvZmZzZXQsIHRydWUpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDRcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZVVJbnQzMkJFID0gZnVuY3Rpb24gd3JpdGVVSW50MzJCRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0ID4+PiAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDQsIDB4ZmZmZmZmZmYsIDApXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIHRoaXNbb2Zmc2V0XSA9ICh2YWx1ZSA+Pj4gMjQpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gMTYpXG4gICAgdGhpc1tvZmZzZXQgKyAyXSA9ICh2YWx1ZSA+Pj4gOClcbiAgICB0aGlzW29mZnNldCArIDNdID0gdmFsdWVcbiAgfSBlbHNlIHtcbiAgICBvYmplY3RXcml0ZVVJbnQzMih0aGlzLCB2YWx1ZSwgb2Zmc2V0LCBmYWxzZSlcbiAgfVxuICByZXR1cm4gb2Zmc2V0ICsgNFxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlSW50TEUgPSBmdW5jdGlvbiB3cml0ZUludExFICh2YWx1ZSwgb2Zmc2V0LCBieXRlTGVuZ3RoLCBub0Fzc2VydCkge1xuICB2YWx1ZSA9ICt2YWx1ZVxuICBvZmZzZXQgPSBvZmZzZXQgPj4+IDBcbiAgaWYgKCFub0Fzc2VydCkge1xuICAgIGNoZWNrSW50KFxuICAgICAgdGhpcywgdmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCxcbiAgICAgIE1hdGgucG93KDIsIDggKiBieXRlTGVuZ3RoIC0gMSkgLSAxLFxuICAgICAgLU1hdGgucG93KDIsIDggKiBieXRlTGVuZ3RoIC0gMSlcbiAgICApXG4gIH1cblxuICB2YXIgaSA9IDBcbiAgdmFyIG11bCA9IDFcbiAgdmFyIHN1YiA9IHZhbHVlIDwgMCA/IDEgOiAwXG4gIHRoaXNbb2Zmc2V0XSA9IHZhbHVlICYgMHhGRlxuICB3aGlsZSAoKytpIDwgYnl0ZUxlbmd0aCAmJiAobXVsICo9IDB4MTAwKSkge1xuICAgIHRoaXNbb2Zmc2V0ICsgaV0gPSAoKHZhbHVlIC8gbXVsKSA+PiAwKSAtIHN1YiAmIDB4RkZcbiAgfVxuXG4gIHJldHVybiBvZmZzZXQgKyBieXRlTGVuZ3RoXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVJbnRCRSA9IGZ1bmN0aW9uIHdyaXRlSW50QkUgKHZhbHVlLCBvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSB7XG4gICAgY2hlY2tJbnQoXG4gICAgICB0aGlzLCB2YWx1ZSwgb2Zmc2V0LCBieXRlTGVuZ3RoLFxuICAgICAgTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGggLSAxKSAtIDEsXG4gICAgICAtTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGggLSAxKVxuICAgIClcbiAgfVxuXG4gIHZhciBpID0gYnl0ZUxlbmd0aCAtIDFcbiAgdmFyIG11bCA9IDFcbiAgdmFyIHN1YiA9IHZhbHVlIDwgMCA/IDEgOiAwXG4gIHRoaXNbb2Zmc2V0ICsgaV0gPSB2YWx1ZSAmIDB4RkZcbiAgd2hpbGUgKC0taSA+PSAwICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdGhpc1tvZmZzZXQgKyBpXSA9ICgodmFsdWUgLyBtdWwpID4+IDApIC0gc3ViICYgMHhGRlxuICB9XG5cbiAgcmV0dXJuIG9mZnNldCArIGJ5dGVMZW5ndGhcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDggPSBmdW5jdGlvbiB3cml0ZUludDggKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCAxLCAweDdmLCAtMHg4MClcbiAgaWYgKCFCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkgdmFsdWUgPSBNYXRoLmZsb29yKHZhbHVlKVxuICBpZiAodmFsdWUgPCAwKSB2YWx1ZSA9IDB4ZmYgKyB2YWx1ZSArIDFcbiAgdGhpc1tvZmZzZXRdID0gdmFsdWVcbiAgcmV0dXJuIG9mZnNldCArIDFcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDE2TEUgPSBmdW5jdGlvbiB3cml0ZUludDE2TEUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCAyLCAweDdmZmYsIC0weDgwMDApXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIHRoaXNbb2Zmc2V0XSA9IHZhbHVlXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gOClcbiAgfSBlbHNlIHtcbiAgICBvYmplY3RXcml0ZVVJbnQxNih0aGlzLCB2YWx1ZSwgb2Zmc2V0LCB0cnVlKVxuICB9XG4gIHJldHVybiBvZmZzZXQgKyAyXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVJbnQxNkJFID0gZnVuY3Rpb24gd3JpdGVJbnQxNkJFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICB2YWx1ZSA9ICt2YWx1ZVxuICBvZmZzZXQgPSBvZmZzZXQgPj4+IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgMiwgMHg3ZmZmLCAtMHg4MDAwKVxuICBpZiAoQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHtcbiAgICB0aGlzW29mZnNldF0gPSAodmFsdWUgPj4+IDgpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9IHZhbHVlXG4gIH0gZWxzZSB7XG4gICAgb2JqZWN0V3JpdGVVSW50MTYodGhpcywgdmFsdWUsIG9mZnNldCwgZmFsc2UpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDJcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDMyTEUgPSBmdW5jdGlvbiB3cml0ZUludDMyTEUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCA0LCAweDdmZmZmZmZmLCAtMHg4MDAwMDAwMClcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXRdID0gdmFsdWVcbiAgICB0aGlzW29mZnNldCArIDFdID0gKHZhbHVlID4+PiA4KVxuICAgIHRoaXNbb2Zmc2V0ICsgMl0gPSAodmFsdWUgPj4+IDE2KVxuICAgIHRoaXNbb2Zmc2V0ICsgM10gPSAodmFsdWUgPj4+IDI0KVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDMyKHRoaXMsIHZhbHVlLCBvZmZzZXQsIHRydWUpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDRcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDMyQkUgPSBmdW5jdGlvbiB3cml0ZUludDMyQkUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCA+Pj4gMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCA0LCAweDdmZmZmZmZmLCAtMHg4MDAwMDAwMClcbiAgaWYgKHZhbHVlIDwgMCkgdmFsdWUgPSAweGZmZmZmZmZmICsgdmFsdWUgKyAxXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIHRoaXNbb2Zmc2V0XSA9ICh2YWx1ZSA+Pj4gMjQpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gMTYpXG4gICAgdGhpc1tvZmZzZXQgKyAyXSA9ICh2YWx1ZSA+Pj4gOClcbiAgICB0aGlzW29mZnNldCArIDNdID0gdmFsdWVcbiAgfSBlbHNlIHtcbiAgICBvYmplY3RXcml0ZVVJbnQzMih0aGlzLCB2YWx1ZSwgb2Zmc2V0LCBmYWxzZSlcbiAgfVxuICByZXR1cm4gb2Zmc2V0ICsgNFxufVxuXG5mdW5jdGlvbiBjaGVja0lFRUU3NTQgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgZXh0LCBtYXgsIG1pbikge1xuICBpZiAodmFsdWUgPiBtYXggfHwgdmFsdWUgPCBtaW4pIHRocm93IG5ldyBSYW5nZUVycm9yKCd2YWx1ZSBpcyBvdXQgb2YgYm91bmRzJylcbiAgaWYgKG9mZnNldCArIGV4dCA+IGJ1Zi5sZW5ndGgpIHRocm93IG5ldyBSYW5nZUVycm9yKCdpbmRleCBvdXQgb2YgcmFuZ2UnKVxuICBpZiAob2Zmc2V0IDwgMCkgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ2luZGV4IG91dCBvZiByYW5nZScpXG59XG5cbmZ1bmN0aW9uIHdyaXRlRmxvYXQgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgbGl0dGxlRW5kaWFuLCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSB7XG4gICAgY2hlY2tJRUVFNzU0KGJ1ZiwgdmFsdWUsIG9mZnNldCwgNCwgMy40MDI4MjM0NjYzODUyODg2ZSszOCwgLTMuNDAyODIzNDY2Mzg1Mjg4NmUrMzgpXG4gIH1cbiAgaWVlZTc1NC53cml0ZShidWYsIHZhbHVlLCBvZmZzZXQsIGxpdHRsZUVuZGlhbiwgMjMsIDQpXG4gIHJldHVybiBvZmZzZXQgKyA0XG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVGbG9hdExFID0gZnVuY3Rpb24gd3JpdGVGbG9hdExFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICByZXR1cm4gd3JpdGVGbG9hdCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCB0cnVlLCBub0Fzc2VydClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUZsb2F0QkUgPSBmdW5jdGlvbiB3cml0ZUZsb2F0QkUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHJldHVybiB3cml0ZUZsb2F0KHRoaXMsIHZhbHVlLCBvZmZzZXQsIGZhbHNlLCBub0Fzc2VydClcbn1cblxuZnVuY3Rpb24gd3JpdGVEb3VibGUgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgbGl0dGxlRW5kaWFuLCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSB7XG4gICAgY2hlY2tJRUVFNzU0KGJ1ZiwgdmFsdWUsIG9mZnNldCwgOCwgMS43OTc2OTMxMzQ4NjIzMTU3RSszMDgsIC0xLjc5NzY5MzEzNDg2MjMxNTdFKzMwOClcbiAgfVxuICBpZWVlNzU0LndyaXRlKGJ1ZiwgdmFsdWUsIG9mZnNldCwgbGl0dGxlRW5kaWFuLCA1MiwgOClcbiAgcmV0dXJuIG9mZnNldCArIDhcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZURvdWJsZUxFID0gZnVuY3Rpb24gd3JpdGVEb3VibGVMRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgcmV0dXJuIHdyaXRlRG91YmxlKHRoaXMsIHZhbHVlLCBvZmZzZXQsIHRydWUsIG5vQXNzZXJ0KVxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlRG91YmxlQkUgPSBmdW5jdGlvbiB3cml0ZURvdWJsZUJFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICByZXR1cm4gd3JpdGVEb3VibGUodGhpcywgdmFsdWUsIG9mZnNldCwgZmFsc2UsIG5vQXNzZXJ0KVxufVxuXG4vLyBjb3B5KHRhcmdldEJ1ZmZlciwgdGFyZ2V0U3RhcnQ9MCwgc291cmNlU3RhcnQ9MCwgc291cmNlRW5kPWJ1ZmZlci5sZW5ndGgpXG5CdWZmZXIucHJvdG90eXBlLmNvcHkgPSBmdW5jdGlvbiBjb3B5ICh0YXJnZXQsIHRhcmdldF9zdGFydCwgc3RhcnQsIGVuZCkge1xuICBpZiAoIXN0YXJ0KSBzdGFydCA9IDBcbiAgaWYgKCFlbmQgJiYgZW5kICE9PSAwKSBlbmQgPSB0aGlzLmxlbmd0aFxuICBpZiAodGFyZ2V0X3N0YXJ0ID49IHRhcmdldC5sZW5ndGgpIHRhcmdldF9zdGFydCA9IHRhcmdldC5sZW5ndGhcbiAgaWYgKCF0YXJnZXRfc3RhcnQpIHRhcmdldF9zdGFydCA9IDBcbiAgaWYgKGVuZCA+IDAgJiYgZW5kIDwgc3RhcnQpIGVuZCA9IHN0YXJ0XG5cbiAgLy8gQ29weSAwIGJ5dGVzOyB3ZSdyZSBkb25lXG4gIGlmIChlbmQgPT09IHN0YXJ0KSByZXR1cm4gMFxuICBpZiAodGFyZ2V0Lmxlbmd0aCA9PT0gMCB8fCB0aGlzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIDBcblxuICAvLyBGYXRhbCBlcnJvciBjb25kaXRpb25zXG4gIGlmICh0YXJnZXRfc3RhcnQgPCAwKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ3RhcmdldFN0YXJ0IG91dCBvZiBib3VuZHMnKVxuICB9XG4gIGlmIChzdGFydCA8IDAgfHwgc3RhcnQgPj0gdGhpcy5sZW5ndGgpIHRocm93IG5ldyBSYW5nZUVycm9yKCdzb3VyY2VTdGFydCBvdXQgb2YgYm91bmRzJylcbiAgaWYgKGVuZCA8IDApIHRocm93IG5ldyBSYW5nZUVycm9yKCdzb3VyY2VFbmQgb3V0IG9mIGJvdW5kcycpXG5cbiAgLy8gQXJlIHdlIG9vYj9cbiAgaWYgKGVuZCA+IHRoaXMubGVuZ3RoKSBlbmQgPSB0aGlzLmxlbmd0aFxuICBpZiAodGFyZ2V0Lmxlbmd0aCAtIHRhcmdldF9zdGFydCA8IGVuZCAtIHN0YXJ0KSB7XG4gICAgZW5kID0gdGFyZ2V0Lmxlbmd0aCAtIHRhcmdldF9zdGFydCArIHN0YXJ0XG4gIH1cblxuICB2YXIgbGVuID0gZW5kIC0gc3RhcnRcblxuICBpZiAobGVuIDwgMTAwMCB8fCAhQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICB0YXJnZXRbaSArIHRhcmdldF9zdGFydF0gPSB0aGlzW2kgKyBzdGFydF1cbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgdGFyZ2V0Ll9zZXQodGhpcy5zdWJhcnJheShzdGFydCwgc3RhcnQgKyBsZW4pLCB0YXJnZXRfc3RhcnQpXG4gIH1cblxuICByZXR1cm4gbGVuXG59XG5cbi8vIGZpbGwodmFsdWUsIHN0YXJ0PTAsIGVuZD1idWZmZXIubGVuZ3RoKVxuQnVmZmVyLnByb3RvdHlwZS5maWxsID0gZnVuY3Rpb24gZmlsbCAodmFsdWUsIHN0YXJ0LCBlbmQpIHtcbiAgaWYgKCF2YWx1ZSkgdmFsdWUgPSAwXG4gIGlmICghc3RhcnQpIHN0YXJ0ID0gMFxuICBpZiAoIWVuZCkgZW5kID0gdGhpcy5sZW5ndGhcblxuICBpZiAoZW5kIDwgc3RhcnQpIHRocm93IG5ldyBSYW5nZUVycm9yKCdlbmQgPCBzdGFydCcpXG5cbiAgLy8gRmlsbCAwIGJ5dGVzOyB3ZSdyZSBkb25lXG4gIGlmIChlbmQgPT09IHN0YXJ0KSByZXR1cm5cbiAgaWYgKHRoaXMubGVuZ3RoID09PSAwKSByZXR1cm5cblxuICBpZiAoc3RhcnQgPCAwIHx8IHN0YXJ0ID49IHRoaXMubGVuZ3RoKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignc3RhcnQgb3V0IG9mIGJvdW5kcycpXG4gIGlmIChlbmQgPCAwIHx8IGVuZCA+IHRoaXMubGVuZ3RoKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignZW5kIG91dCBvZiBib3VuZHMnKVxuXG4gIHZhciBpXG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInKSB7XG4gICAgZm9yIChpID0gc3RhcnQ7IGkgPCBlbmQ7IGkrKykge1xuICAgICAgdGhpc1tpXSA9IHZhbHVlXG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHZhciBieXRlcyA9IHV0ZjhUb0J5dGVzKHZhbHVlLnRvU3RyaW5nKCkpXG4gICAgdmFyIGxlbiA9IGJ5dGVzLmxlbmd0aFxuICAgIGZvciAoaSA9IHN0YXJ0OyBpIDwgZW5kOyBpKyspIHtcbiAgICAgIHRoaXNbaV0gPSBieXRlc1tpICUgbGVuXVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0aGlzXG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIG5ldyBgQXJyYXlCdWZmZXJgIHdpdGggdGhlICpjb3BpZWQqIG1lbW9yeSBvZiB0aGUgYnVmZmVyIGluc3RhbmNlLlxuICogQWRkZWQgaW4gTm9kZSAwLjEyLiBPbmx5IGF2YWlsYWJsZSBpbiBicm93c2VycyB0aGF0IHN1cHBvcnQgQXJyYXlCdWZmZXIuXG4gKi9cbkJ1ZmZlci5wcm90b3R5cGUudG9BcnJheUJ1ZmZlciA9IGZ1bmN0aW9uIHRvQXJyYXlCdWZmZXIgKCkge1xuICBpZiAodHlwZW9mIFVpbnQ4QXJyYXkgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgICByZXR1cm4gKG5ldyBCdWZmZXIodGhpcykpLmJ1ZmZlclxuICAgIH0gZWxzZSB7XG4gICAgICB2YXIgYnVmID0gbmV3IFVpbnQ4QXJyYXkodGhpcy5sZW5ndGgpXG4gICAgICBmb3IgKHZhciBpID0gMCwgbGVuID0gYnVmLmxlbmd0aDsgaSA8IGxlbjsgaSArPSAxKSB7XG4gICAgICAgIGJ1ZltpXSA9IHRoaXNbaV1cbiAgICAgIH1cbiAgICAgIHJldHVybiBidWYuYnVmZmVyXG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0J1ZmZlci50b0FycmF5QnVmZmVyIG5vdCBzdXBwb3J0ZWQgaW4gdGhpcyBicm93c2VyJylcbiAgfVxufVxuXG4vLyBIRUxQRVIgRlVOQ1RJT05TXG4vLyA9PT09PT09PT09PT09PT09XG5cbnZhciBCUCA9IEJ1ZmZlci5wcm90b3R5cGVcblxuLyoqXG4gKiBBdWdtZW50IGEgVWludDhBcnJheSAqaW5zdGFuY2UqIChub3QgdGhlIFVpbnQ4QXJyYXkgY2xhc3MhKSB3aXRoIEJ1ZmZlciBtZXRob2RzXG4gKi9cbkJ1ZmZlci5fYXVnbWVudCA9IGZ1bmN0aW9uIF9hdWdtZW50IChhcnIpIHtcbiAgYXJyLmNvbnN0cnVjdG9yID0gQnVmZmVyXG4gIGFyci5faXNCdWZmZXIgPSB0cnVlXG5cbiAgLy8gc2F2ZSByZWZlcmVuY2UgdG8gb3JpZ2luYWwgVWludDhBcnJheSBzZXQgbWV0aG9kIGJlZm9yZSBvdmVyd3JpdGluZ1xuICBhcnIuX3NldCA9IGFyci5zZXRcblxuICAvLyBkZXByZWNhdGVkLCB3aWxsIGJlIHJlbW92ZWQgaW4gbm9kZSAwLjEzK1xuICBhcnIuZ2V0ID0gQlAuZ2V0XG4gIGFyci5zZXQgPSBCUC5zZXRcblxuICBhcnIud3JpdGUgPSBCUC53cml0ZVxuICBhcnIudG9TdHJpbmcgPSBCUC50b1N0cmluZ1xuICBhcnIudG9Mb2NhbGVTdHJpbmcgPSBCUC50b1N0cmluZ1xuICBhcnIudG9KU09OID0gQlAudG9KU09OXG4gIGFyci5lcXVhbHMgPSBCUC5lcXVhbHNcbiAgYXJyLmNvbXBhcmUgPSBCUC5jb21wYXJlXG4gIGFyci5pbmRleE9mID0gQlAuaW5kZXhPZlxuICBhcnIuY29weSA9IEJQLmNvcHlcbiAgYXJyLnNsaWNlID0gQlAuc2xpY2VcbiAgYXJyLnJlYWRVSW50TEUgPSBCUC5yZWFkVUludExFXG4gIGFyci5yZWFkVUludEJFID0gQlAucmVhZFVJbnRCRVxuICBhcnIucmVhZFVJbnQ4ID0gQlAucmVhZFVJbnQ4XG4gIGFyci5yZWFkVUludDE2TEUgPSBCUC5yZWFkVUludDE2TEVcbiAgYXJyLnJlYWRVSW50MTZCRSA9IEJQLnJlYWRVSW50MTZCRVxuICBhcnIucmVhZFVJbnQzMkxFID0gQlAucmVhZFVJbnQzMkxFXG4gIGFyci5yZWFkVUludDMyQkUgPSBCUC5yZWFkVUludDMyQkVcbiAgYXJyLnJlYWRJbnRMRSA9IEJQLnJlYWRJbnRMRVxuICBhcnIucmVhZEludEJFID0gQlAucmVhZEludEJFXG4gIGFyci5yZWFkSW50OCA9IEJQLnJlYWRJbnQ4XG4gIGFyci5yZWFkSW50MTZMRSA9IEJQLnJlYWRJbnQxNkxFXG4gIGFyci5yZWFkSW50MTZCRSA9IEJQLnJlYWRJbnQxNkJFXG4gIGFyci5yZWFkSW50MzJMRSA9IEJQLnJlYWRJbnQzMkxFXG4gIGFyci5yZWFkSW50MzJCRSA9IEJQLnJlYWRJbnQzMkJFXG4gIGFyci5yZWFkRmxvYXRMRSA9IEJQLnJlYWRGbG9hdExFXG4gIGFyci5yZWFkRmxvYXRCRSA9IEJQLnJlYWRGbG9hdEJFXG4gIGFyci5yZWFkRG91YmxlTEUgPSBCUC5yZWFkRG91YmxlTEVcbiAgYXJyLnJlYWREb3VibGVCRSA9IEJQLnJlYWREb3VibGVCRVxuICBhcnIud3JpdGVVSW50OCA9IEJQLndyaXRlVUludDhcbiAgYXJyLndyaXRlVUludExFID0gQlAud3JpdGVVSW50TEVcbiAgYXJyLndyaXRlVUludEJFID0gQlAud3JpdGVVSW50QkVcbiAgYXJyLndyaXRlVUludDE2TEUgPSBCUC53cml0ZVVJbnQxNkxFXG4gIGFyci53cml0ZVVJbnQxNkJFID0gQlAud3JpdGVVSW50MTZCRVxuICBhcnIud3JpdGVVSW50MzJMRSA9IEJQLndyaXRlVUludDMyTEVcbiAgYXJyLndyaXRlVUludDMyQkUgPSBCUC53cml0ZVVJbnQzMkJFXG4gIGFyci53cml0ZUludExFID0gQlAud3JpdGVJbnRMRVxuICBhcnIud3JpdGVJbnRCRSA9IEJQLndyaXRlSW50QkVcbiAgYXJyLndyaXRlSW50OCA9IEJQLndyaXRlSW50OFxuICBhcnIud3JpdGVJbnQxNkxFID0gQlAud3JpdGVJbnQxNkxFXG4gIGFyci53cml0ZUludDE2QkUgPSBCUC53cml0ZUludDE2QkVcbiAgYXJyLndyaXRlSW50MzJMRSA9IEJQLndyaXRlSW50MzJMRVxuICBhcnIud3JpdGVJbnQzMkJFID0gQlAud3JpdGVJbnQzMkJFXG4gIGFyci53cml0ZUZsb2F0TEUgPSBCUC53cml0ZUZsb2F0TEVcbiAgYXJyLndyaXRlRmxvYXRCRSA9IEJQLndyaXRlRmxvYXRCRVxuICBhcnIud3JpdGVEb3VibGVMRSA9IEJQLndyaXRlRG91YmxlTEVcbiAgYXJyLndyaXRlRG91YmxlQkUgPSBCUC53cml0ZURvdWJsZUJFXG4gIGFyci5maWxsID0gQlAuZmlsbFxuICBhcnIuaW5zcGVjdCA9IEJQLmluc3BlY3RcbiAgYXJyLnRvQXJyYXlCdWZmZXIgPSBCUC50b0FycmF5QnVmZmVyXG5cbiAgcmV0dXJuIGFyclxufVxuXG52YXIgSU5WQUxJRF9CQVNFNjRfUkUgPSAvW14rXFwvMC05QS16XFwtXS9nXG5cbmZ1bmN0aW9uIGJhc2U2NGNsZWFuIChzdHIpIHtcbiAgLy8gTm9kZSBzdHJpcHMgb3V0IGludmFsaWQgY2hhcmFjdGVycyBsaWtlIFxcbiBhbmQgXFx0IGZyb20gdGhlIHN0cmluZywgYmFzZTY0LWpzIGRvZXMgbm90XG4gIHN0ciA9IHN0cmluZ3RyaW0oc3RyKS5yZXBsYWNlKElOVkFMSURfQkFTRTY0X1JFLCAnJylcbiAgLy8gTm9kZSBjb252ZXJ0cyBzdHJpbmdzIHdpdGggbGVuZ3RoIDwgMiB0byAnJ1xuICBpZiAoc3RyLmxlbmd0aCA8IDIpIHJldHVybiAnJ1xuICAvLyBOb2RlIGFsbG93cyBmb3Igbm9uLXBhZGRlZCBiYXNlNjQgc3RyaW5ncyAobWlzc2luZyB0cmFpbGluZyA9PT0pLCBiYXNlNjQtanMgZG9lcyBub3RcbiAgd2hpbGUgKHN0ci5sZW5ndGggJSA0ICE9PSAwKSB7XG4gICAgc3RyID0gc3RyICsgJz0nXG4gIH1cbiAgcmV0dXJuIHN0clxufVxuXG5mdW5jdGlvbiBzdHJpbmd0cmltIChzdHIpIHtcbiAgaWYgKHN0ci50cmltKSByZXR1cm4gc3RyLnRyaW0oKVxuICByZXR1cm4gc3RyLnJlcGxhY2UoL15cXHMrfFxccyskL2csICcnKVxufVxuXG5mdW5jdGlvbiBpc0FycmF5aXNoIChzdWJqZWN0KSB7XG4gIHJldHVybiBpc0FycmF5KHN1YmplY3QpIHx8IEJ1ZmZlci5pc0J1ZmZlcihzdWJqZWN0KSB8fFxuICAgICAgc3ViamVjdCAmJiB0eXBlb2Ygc3ViamVjdCA9PT0gJ29iamVjdCcgJiZcbiAgICAgIHR5cGVvZiBzdWJqZWN0Lmxlbmd0aCA9PT0gJ251bWJlcidcbn1cblxuZnVuY3Rpb24gdG9IZXggKG4pIHtcbiAgaWYgKG4gPCAxNikgcmV0dXJuICcwJyArIG4udG9TdHJpbmcoMTYpXG4gIHJldHVybiBuLnRvU3RyaW5nKDE2KVxufVxuXG5mdW5jdGlvbiB1dGY4VG9CeXRlcyAoc3RyaW5nLCB1bml0cykge1xuICB1bml0cyA9IHVuaXRzIHx8IEluZmluaXR5XG4gIHZhciBjb2RlUG9pbnRcbiAgdmFyIGxlbmd0aCA9IHN0cmluZy5sZW5ndGhcbiAgdmFyIGxlYWRTdXJyb2dhdGUgPSBudWxsXG4gIHZhciBieXRlcyA9IFtdXG4gIHZhciBpID0gMFxuXG4gIGZvciAoOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICBjb2RlUG9pbnQgPSBzdHJpbmcuY2hhckNvZGVBdChpKVxuXG4gICAgLy8gaXMgc3Vycm9nYXRlIGNvbXBvbmVudFxuICAgIGlmIChjb2RlUG9pbnQgPiAweEQ3RkYgJiYgY29kZVBvaW50IDwgMHhFMDAwKSB7XG4gICAgICAvLyBsYXN0IGNoYXIgd2FzIGEgbGVhZFxuICAgICAgaWYgKGxlYWRTdXJyb2dhdGUpIHtcbiAgICAgICAgLy8gMiBsZWFkcyBpbiBhIHJvd1xuICAgICAgICBpZiAoY29kZVBvaW50IDwgMHhEQzAwKSB7XG4gICAgICAgICAgaWYgKCh1bml0cyAtPSAzKSA+IC0xKSBieXRlcy5wdXNoKDB4RUYsIDB4QkYsIDB4QkQpXG4gICAgICAgICAgbGVhZFN1cnJvZ2F0ZSA9IGNvZGVQb2ludFxuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gdmFsaWQgc3Vycm9nYXRlIHBhaXJcbiAgICAgICAgICBjb2RlUG9pbnQgPSBsZWFkU3Vycm9nYXRlIC0gMHhEODAwIDw8IDEwIHwgY29kZVBvaW50IC0gMHhEQzAwIHwgMHgxMDAwMFxuICAgICAgICAgIGxlYWRTdXJyb2dhdGUgPSBudWxsXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG5vIGxlYWQgeWV0XG5cbiAgICAgICAgaWYgKGNvZGVQb2ludCA+IDB4REJGRikge1xuICAgICAgICAgIC8vIHVuZXhwZWN0ZWQgdHJhaWxcbiAgICAgICAgICBpZiAoKHVuaXRzIC09IDMpID4gLTEpIGJ5dGVzLnB1c2goMHhFRiwgMHhCRiwgMHhCRClcbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9IGVsc2UgaWYgKGkgKyAxID09PSBsZW5ndGgpIHtcbiAgICAgICAgICAvLyB1bnBhaXJlZCBsZWFkXG4gICAgICAgICAgaWYgKCh1bml0cyAtPSAzKSA+IC0xKSBieXRlcy5wdXNoKDB4RUYsIDB4QkYsIDB4QkQpXG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyB2YWxpZCBsZWFkXG4gICAgICAgICAgbGVhZFN1cnJvZ2F0ZSA9IGNvZGVQb2ludFxuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGxlYWRTdXJyb2dhdGUpIHtcbiAgICAgIC8vIHZhbGlkIGJtcCBjaGFyLCBidXQgbGFzdCBjaGFyIHdhcyBhIGxlYWRcbiAgICAgIGlmICgodW5pdHMgLT0gMykgPiAtMSkgYnl0ZXMucHVzaCgweEVGLCAweEJGLCAweEJEKVxuICAgICAgbGVhZFN1cnJvZ2F0ZSA9IG51bGxcbiAgICB9XG5cbiAgICAvLyBlbmNvZGUgdXRmOFxuICAgIGlmIChjb2RlUG9pbnQgPCAweDgwKSB7XG4gICAgICBpZiAoKHVuaXRzIC09IDEpIDwgMCkgYnJlYWtcbiAgICAgIGJ5dGVzLnB1c2goY29kZVBvaW50KVxuICAgIH0gZWxzZSBpZiAoY29kZVBvaW50IDwgMHg4MDApIHtcbiAgICAgIGlmICgodW5pdHMgLT0gMikgPCAwKSBicmVha1xuICAgICAgYnl0ZXMucHVzaChcbiAgICAgICAgY29kZVBvaW50ID4+IDB4NiB8IDB4QzAsXG4gICAgICAgIGNvZGVQb2ludCAmIDB4M0YgfCAweDgwXG4gICAgICApXG4gICAgfSBlbHNlIGlmIChjb2RlUG9pbnQgPCAweDEwMDAwKSB7XG4gICAgICBpZiAoKHVuaXRzIC09IDMpIDwgMCkgYnJlYWtcbiAgICAgIGJ5dGVzLnB1c2goXG4gICAgICAgIGNvZGVQb2ludCA+PiAweEMgfCAweEUwLFxuICAgICAgICBjb2RlUG9pbnQgPj4gMHg2ICYgMHgzRiB8IDB4ODAsXG4gICAgICAgIGNvZGVQb2ludCAmIDB4M0YgfCAweDgwXG4gICAgICApXG4gICAgfSBlbHNlIGlmIChjb2RlUG9pbnQgPCAweDIwMDAwMCkge1xuICAgICAgaWYgKCh1bml0cyAtPSA0KSA8IDApIGJyZWFrXG4gICAgICBieXRlcy5wdXNoKFxuICAgICAgICBjb2RlUG9pbnQgPj4gMHgxMiB8IDB4RjAsXG4gICAgICAgIGNvZGVQb2ludCA+PiAweEMgJiAweDNGIHwgMHg4MCxcbiAgICAgICAgY29kZVBvaW50ID4+IDB4NiAmIDB4M0YgfCAweDgwLFxuICAgICAgICBjb2RlUG9pbnQgJiAweDNGIHwgMHg4MFxuICAgICAgKVxuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgY29kZSBwb2ludCcpXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGJ5dGVzXG59XG5cbmZ1bmN0aW9uIGFzY2lpVG9CeXRlcyAoc3RyKSB7XG4gIHZhciBieXRlQXJyYXkgPSBbXVxuICBmb3IgKHZhciBpID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xuICAgIC8vIE5vZGUncyBjb2RlIHNlZW1zIHRvIGJlIGRvaW5nIHRoaXMgYW5kIG5vdCAmIDB4N0YuLlxuICAgIGJ5dGVBcnJheS5wdXNoKHN0ci5jaGFyQ29kZUF0KGkpICYgMHhGRilcbiAgfVxuICByZXR1cm4gYnl0ZUFycmF5XG59XG5cbmZ1bmN0aW9uIHV0ZjE2bGVUb0J5dGVzIChzdHIsIHVuaXRzKSB7XG4gIHZhciBjLCBoaSwgbG9cbiAgdmFyIGJ5dGVBcnJheSA9IFtdXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKCh1bml0cyAtPSAyKSA8IDApIGJyZWFrXG5cbiAgICBjID0gc3RyLmNoYXJDb2RlQXQoaSlcbiAgICBoaSA9IGMgPj4gOFxuICAgIGxvID0gYyAlIDI1NlxuICAgIGJ5dGVBcnJheS5wdXNoKGxvKVxuICAgIGJ5dGVBcnJheS5wdXNoKGhpKVxuICB9XG5cbiAgcmV0dXJuIGJ5dGVBcnJheVxufVxuXG5mdW5jdGlvbiBiYXNlNjRUb0J5dGVzIChzdHIpIHtcbiAgcmV0dXJuIGJhc2U2NC50b0J5dGVBcnJheShiYXNlNjRjbGVhbihzdHIpKVxufVxuXG5mdW5jdGlvbiBibGl0QnVmZmVyIChzcmMsIGRzdCwgb2Zmc2V0LCBsZW5ndGgpIHtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW5ndGg7IGkrKykge1xuICAgIGlmICgoaSArIG9mZnNldCA+PSBkc3QubGVuZ3RoKSB8fCAoaSA+PSBzcmMubGVuZ3RoKSkgYnJlYWtcbiAgICBkc3RbaSArIG9mZnNldF0gPSBzcmNbaV1cbiAgfVxuICByZXR1cm4gaVxufVxuXG5mdW5jdGlvbiBkZWNvZGVVdGY4Q2hhciAoc3RyKSB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGRlY29kZVVSSUNvbXBvbmVudChzdHIpXG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHJldHVybiBTdHJpbmcuZnJvbUNoYXJDb2RlKDB4RkZGRCkgLy8gVVRGIDggaW52YWxpZCBjaGFyXG4gIH1cbn1cbiIsInZhciBsb29rdXAgPSAnQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLyc7XG5cbjsoZnVuY3Rpb24gKGV4cG9ydHMpIHtcblx0J3VzZSBzdHJpY3QnO1xuXG4gIHZhciBBcnIgPSAodHlwZW9mIFVpbnQ4QXJyYXkgIT09ICd1bmRlZmluZWQnKVxuICAgID8gVWludDhBcnJheVxuICAgIDogQXJyYXlcblxuXHR2YXIgUExVUyAgID0gJysnLmNoYXJDb2RlQXQoMClcblx0dmFyIFNMQVNIICA9ICcvJy5jaGFyQ29kZUF0KDApXG5cdHZhciBOVU1CRVIgPSAnMCcuY2hhckNvZGVBdCgwKVxuXHR2YXIgTE9XRVIgID0gJ2EnLmNoYXJDb2RlQXQoMClcblx0dmFyIFVQUEVSICA9ICdBJy5jaGFyQ29kZUF0KDApXG5cdHZhciBQTFVTX1VSTF9TQUZFID0gJy0nLmNoYXJDb2RlQXQoMClcblx0dmFyIFNMQVNIX1VSTF9TQUZFID0gJ18nLmNoYXJDb2RlQXQoMClcblxuXHRmdW5jdGlvbiBkZWNvZGUgKGVsdCkge1xuXHRcdHZhciBjb2RlID0gZWx0LmNoYXJDb2RlQXQoMClcblx0XHRpZiAoY29kZSA9PT0gUExVUyB8fFxuXHRcdCAgICBjb2RlID09PSBQTFVTX1VSTF9TQUZFKVxuXHRcdFx0cmV0dXJuIDYyIC8vICcrJ1xuXHRcdGlmIChjb2RlID09PSBTTEFTSCB8fFxuXHRcdCAgICBjb2RlID09PSBTTEFTSF9VUkxfU0FGRSlcblx0XHRcdHJldHVybiA2MyAvLyAnLydcblx0XHRpZiAoY29kZSA8IE5VTUJFUilcblx0XHRcdHJldHVybiAtMSAvL25vIG1hdGNoXG5cdFx0aWYgKGNvZGUgPCBOVU1CRVIgKyAxMClcblx0XHRcdHJldHVybiBjb2RlIC0gTlVNQkVSICsgMjYgKyAyNlxuXHRcdGlmIChjb2RlIDwgVVBQRVIgKyAyNilcblx0XHRcdHJldHVybiBjb2RlIC0gVVBQRVJcblx0XHRpZiAoY29kZSA8IExPV0VSICsgMjYpXG5cdFx0XHRyZXR1cm4gY29kZSAtIExPV0VSICsgMjZcblx0fVxuXG5cdGZ1bmN0aW9uIGI2NFRvQnl0ZUFycmF5IChiNjQpIHtcblx0XHR2YXIgaSwgaiwgbCwgdG1wLCBwbGFjZUhvbGRlcnMsIGFyclxuXG5cdFx0aWYgKGI2NC5sZW5ndGggJSA0ID4gMCkge1xuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHN0cmluZy4gTGVuZ3RoIG11c3QgYmUgYSBtdWx0aXBsZSBvZiA0Jylcblx0XHR9XG5cblx0XHQvLyB0aGUgbnVtYmVyIG9mIGVxdWFsIHNpZ25zIChwbGFjZSBob2xkZXJzKVxuXHRcdC8vIGlmIHRoZXJlIGFyZSB0d28gcGxhY2Vob2xkZXJzLCB0aGFuIHRoZSB0d28gY2hhcmFjdGVycyBiZWZvcmUgaXRcblx0XHQvLyByZXByZXNlbnQgb25lIGJ5dGVcblx0XHQvLyBpZiB0aGVyZSBpcyBvbmx5IG9uZSwgdGhlbiB0aGUgdGhyZWUgY2hhcmFjdGVycyBiZWZvcmUgaXQgcmVwcmVzZW50IDIgYnl0ZXNcblx0XHQvLyB0aGlzIGlzIGp1c3QgYSBjaGVhcCBoYWNrIHRvIG5vdCBkbyBpbmRleE9mIHR3aWNlXG5cdFx0dmFyIGxlbiA9IGI2NC5sZW5ndGhcblx0XHRwbGFjZUhvbGRlcnMgPSAnPScgPT09IGI2NC5jaGFyQXQobGVuIC0gMikgPyAyIDogJz0nID09PSBiNjQuY2hhckF0KGxlbiAtIDEpID8gMSA6IDBcblxuXHRcdC8vIGJhc2U2NCBpcyA0LzMgKyB1cCB0byB0d28gY2hhcmFjdGVycyBvZiB0aGUgb3JpZ2luYWwgZGF0YVxuXHRcdGFyciA9IG5ldyBBcnIoYjY0Lmxlbmd0aCAqIDMgLyA0IC0gcGxhY2VIb2xkZXJzKVxuXG5cdFx0Ly8gaWYgdGhlcmUgYXJlIHBsYWNlaG9sZGVycywgb25seSBnZXQgdXAgdG8gdGhlIGxhc3QgY29tcGxldGUgNCBjaGFyc1xuXHRcdGwgPSBwbGFjZUhvbGRlcnMgPiAwID8gYjY0Lmxlbmd0aCAtIDQgOiBiNjQubGVuZ3RoXG5cblx0XHR2YXIgTCA9IDBcblxuXHRcdGZ1bmN0aW9uIHB1c2ggKHYpIHtcblx0XHRcdGFycltMKytdID0gdlxuXHRcdH1cblxuXHRcdGZvciAoaSA9IDAsIGogPSAwOyBpIDwgbDsgaSArPSA0LCBqICs9IDMpIHtcblx0XHRcdHRtcCA9IChkZWNvZGUoYjY0LmNoYXJBdChpKSkgPDwgMTgpIHwgKGRlY29kZShiNjQuY2hhckF0KGkgKyAxKSkgPDwgMTIpIHwgKGRlY29kZShiNjQuY2hhckF0KGkgKyAyKSkgPDwgNikgfCBkZWNvZGUoYjY0LmNoYXJBdChpICsgMykpXG5cdFx0XHRwdXNoKCh0bXAgJiAweEZGMDAwMCkgPj4gMTYpXG5cdFx0XHRwdXNoKCh0bXAgJiAweEZGMDApID4+IDgpXG5cdFx0XHRwdXNoKHRtcCAmIDB4RkYpXG5cdFx0fVxuXG5cdFx0aWYgKHBsYWNlSG9sZGVycyA9PT0gMikge1xuXHRcdFx0dG1wID0gKGRlY29kZShiNjQuY2hhckF0KGkpKSA8PCAyKSB8IChkZWNvZGUoYjY0LmNoYXJBdChpICsgMSkpID4+IDQpXG5cdFx0XHRwdXNoKHRtcCAmIDB4RkYpXG5cdFx0fSBlbHNlIGlmIChwbGFjZUhvbGRlcnMgPT09IDEpIHtcblx0XHRcdHRtcCA9IChkZWNvZGUoYjY0LmNoYXJBdChpKSkgPDwgMTApIHwgKGRlY29kZShiNjQuY2hhckF0KGkgKyAxKSkgPDwgNCkgfCAoZGVjb2RlKGI2NC5jaGFyQXQoaSArIDIpKSA+PiAyKVxuXHRcdFx0cHVzaCgodG1wID4+IDgpICYgMHhGRilcblx0XHRcdHB1c2godG1wICYgMHhGRilcblx0XHR9XG5cblx0XHRyZXR1cm4gYXJyXG5cdH1cblxuXHRmdW5jdGlvbiB1aW50OFRvQmFzZTY0ICh1aW50OCkge1xuXHRcdHZhciBpLFxuXHRcdFx0ZXh0cmFCeXRlcyA9IHVpbnQ4Lmxlbmd0aCAlIDMsIC8vIGlmIHdlIGhhdmUgMSBieXRlIGxlZnQsIHBhZCAyIGJ5dGVzXG5cdFx0XHRvdXRwdXQgPSBcIlwiLFxuXHRcdFx0dGVtcCwgbGVuZ3RoXG5cblx0XHRmdW5jdGlvbiBlbmNvZGUgKG51bSkge1xuXHRcdFx0cmV0dXJuIGxvb2t1cC5jaGFyQXQobnVtKVxuXHRcdH1cblxuXHRcdGZ1bmN0aW9uIHRyaXBsZXRUb0Jhc2U2NCAobnVtKSB7XG5cdFx0XHRyZXR1cm4gZW5jb2RlKG51bSA+PiAxOCAmIDB4M0YpICsgZW5jb2RlKG51bSA+PiAxMiAmIDB4M0YpICsgZW5jb2RlKG51bSA+PiA2ICYgMHgzRikgKyBlbmNvZGUobnVtICYgMHgzRilcblx0XHR9XG5cblx0XHQvLyBnbyB0aHJvdWdoIHRoZSBhcnJheSBldmVyeSB0aHJlZSBieXRlcywgd2UnbGwgZGVhbCB3aXRoIHRyYWlsaW5nIHN0dWZmIGxhdGVyXG5cdFx0Zm9yIChpID0gMCwgbGVuZ3RoID0gdWludDgubGVuZ3RoIC0gZXh0cmFCeXRlczsgaSA8IGxlbmd0aDsgaSArPSAzKSB7XG5cdFx0XHR0ZW1wID0gKHVpbnQ4W2ldIDw8IDE2KSArICh1aW50OFtpICsgMV0gPDwgOCkgKyAodWludDhbaSArIDJdKVxuXHRcdFx0b3V0cHV0ICs9IHRyaXBsZXRUb0Jhc2U2NCh0ZW1wKVxuXHRcdH1cblxuXHRcdC8vIHBhZCB0aGUgZW5kIHdpdGggemVyb3MsIGJ1dCBtYWtlIHN1cmUgdG8gbm90IGZvcmdldCB0aGUgZXh0cmEgYnl0ZXNcblx0XHRzd2l0Y2ggKGV4dHJhQnl0ZXMpIHtcblx0XHRcdGNhc2UgMTpcblx0XHRcdFx0dGVtcCA9IHVpbnQ4W3VpbnQ4Lmxlbmd0aCAtIDFdXG5cdFx0XHRcdG91dHB1dCArPSBlbmNvZGUodGVtcCA+PiAyKVxuXHRcdFx0XHRvdXRwdXQgKz0gZW5jb2RlKCh0ZW1wIDw8IDQpICYgMHgzRilcblx0XHRcdFx0b3V0cHV0ICs9ICc9PSdcblx0XHRcdFx0YnJlYWtcblx0XHRcdGNhc2UgMjpcblx0XHRcdFx0dGVtcCA9ICh1aW50OFt1aW50OC5sZW5ndGggLSAyXSA8PCA4KSArICh1aW50OFt1aW50OC5sZW5ndGggLSAxXSlcblx0XHRcdFx0b3V0cHV0ICs9IGVuY29kZSh0ZW1wID4+IDEwKVxuXHRcdFx0XHRvdXRwdXQgKz0gZW5jb2RlKCh0ZW1wID4+IDQpICYgMHgzRilcblx0XHRcdFx0b3V0cHV0ICs9IGVuY29kZSgodGVtcCA8PCAyKSAmIDB4M0YpXG5cdFx0XHRcdG91dHB1dCArPSAnPSdcblx0XHRcdFx0YnJlYWtcblx0XHR9XG5cblx0XHRyZXR1cm4gb3V0cHV0XG5cdH1cblxuXHRleHBvcnRzLnRvQnl0ZUFycmF5ID0gYjY0VG9CeXRlQXJyYXlcblx0ZXhwb3J0cy5mcm9tQnl0ZUFycmF5ID0gdWludDhUb0Jhc2U2NFxufSh0eXBlb2YgZXhwb3J0cyA9PT0gJ3VuZGVmaW5lZCcgPyAodGhpcy5iYXNlNjRqcyA9IHt9KSA6IGV4cG9ydHMpKVxuIiwiZXhwb3J0cy5yZWFkID0gZnVuY3Rpb24oYnVmZmVyLCBvZmZzZXQsIGlzTEUsIG1MZW4sIG5CeXRlcykge1xuICB2YXIgZSwgbSxcbiAgICAgIGVMZW4gPSBuQnl0ZXMgKiA4IC0gbUxlbiAtIDEsXG4gICAgICBlTWF4ID0gKDEgPDwgZUxlbikgLSAxLFxuICAgICAgZUJpYXMgPSBlTWF4ID4+IDEsXG4gICAgICBuQml0cyA9IC03LFxuICAgICAgaSA9IGlzTEUgPyAobkJ5dGVzIC0gMSkgOiAwLFxuICAgICAgZCA9IGlzTEUgPyAtMSA6IDEsXG4gICAgICBzID0gYnVmZmVyW29mZnNldCArIGldO1xuXG4gIGkgKz0gZDtcblxuICBlID0gcyAmICgoMSA8PCAoLW5CaXRzKSkgLSAxKTtcbiAgcyA+Pj0gKC1uQml0cyk7XG4gIG5CaXRzICs9IGVMZW47XG4gIGZvciAoOyBuQml0cyA+IDA7IGUgPSBlICogMjU2ICsgYnVmZmVyW29mZnNldCArIGldLCBpICs9IGQsIG5CaXRzIC09IDgpO1xuXG4gIG0gPSBlICYgKCgxIDw8ICgtbkJpdHMpKSAtIDEpO1xuICBlID4+PSAoLW5CaXRzKTtcbiAgbkJpdHMgKz0gbUxlbjtcbiAgZm9yICg7IG5CaXRzID4gMDsgbSA9IG0gKiAyNTYgKyBidWZmZXJbb2Zmc2V0ICsgaV0sIGkgKz0gZCwgbkJpdHMgLT0gOCk7XG5cbiAgaWYgKGUgPT09IDApIHtcbiAgICBlID0gMSAtIGVCaWFzO1xuICB9IGVsc2UgaWYgKGUgPT09IGVNYXgpIHtcbiAgICByZXR1cm4gbSA/IE5hTiA6ICgocyA/IC0xIDogMSkgKiBJbmZpbml0eSk7XG4gIH0gZWxzZSB7XG4gICAgbSA9IG0gKyBNYXRoLnBvdygyLCBtTGVuKTtcbiAgICBlID0gZSAtIGVCaWFzO1xuICB9XG4gIHJldHVybiAocyA/IC0xIDogMSkgKiBtICogTWF0aC5wb3coMiwgZSAtIG1MZW4pO1xufTtcblxuZXhwb3J0cy53cml0ZSA9IGZ1bmN0aW9uKGJ1ZmZlciwgdmFsdWUsIG9mZnNldCwgaXNMRSwgbUxlbiwgbkJ5dGVzKSB7XG4gIHZhciBlLCBtLCBjLFxuICAgICAgZUxlbiA9IG5CeXRlcyAqIDggLSBtTGVuIC0gMSxcbiAgICAgIGVNYXggPSAoMSA8PCBlTGVuKSAtIDEsXG4gICAgICBlQmlhcyA9IGVNYXggPj4gMSxcbiAgICAgIHJ0ID0gKG1MZW4gPT09IDIzID8gTWF0aC5wb3coMiwgLTI0KSAtIE1hdGgucG93KDIsIC03NykgOiAwKSxcbiAgICAgIGkgPSBpc0xFID8gMCA6IChuQnl0ZXMgLSAxKSxcbiAgICAgIGQgPSBpc0xFID8gMSA6IC0xLFxuICAgICAgcyA9IHZhbHVlIDwgMCB8fCAodmFsdWUgPT09IDAgJiYgMSAvIHZhbHVlIDwgMCkgPyAxIDogMDtcblxuICB2YWx1ZSA9IE1hdGguYWJzKHZhbHVlKTtcblxuICBpZiAoaXNOYU4odmFsdWUpIHx8IHZhbHVlID09PSBJbmZpbml0eSkge1xuICAgIG0gPSBpc05hTih2YWx1ZSkgPyAxIDogMDtcbiAgICBlID0gZU1heDtcbiAgfSBlbHNlIHtcbiAgICBlID0gTWF0aC5mbG9vcihNYXRoLmxvZyh2YWx1ZSkgLyBNYXRoLkxOMik7XG4gICAgaWYgKHZhbHVlICogKGMgPSBNYXRoLnBvdygyLCAtZSkpIDwgMSkge1xuICAgICAgZS0tO1xuICAgICAgYyAqPSAyO1xuICAgIH1cbiAgICBpZiAoZSArIGVCaWFzID49IDEpIHtcbiAgICAgIHZhbHVlICs9IHJ0IC8gYztcbiAgICB9IGVsc2Uge1xuICAgICAgdmFsdWUgKz0gcnQgKiBNYXRoLnBvdygyLCAxIC0gZUJpYXMpO1xuICAgIH1cbiAgICBpZiAodmFsdWUgKiBjID49IDIpIHtcbiAgICAgIGUrKztcbiAgICAgIGMgLz0gMjtcbiAgICB9XG5cbiAgICBpZiAoZSArIGVCaWFzID49IGVNYXgpIHtcbiAgICAgIG0gPSAwO1xuICAgICAgZSA9IGVNYXg7XG4gICAgfSBlbHNlIGlmIChlICsgZUJpYXMgPj0gMSkge1xuICAgICAgbSA9ICh2YWx1ZSAqIGMgLSAxKSAqIE1hdGgucG93KDIsIG1MZW4pO1xuICAgICAgZSA9IGUgKyBlQmlhcztcbiAgICB9IGVsc2Uge1xuICAgICAgbSA9IHZhbHVlICogTWF0aC5wb3coMiwgZUJpYXMgLSAxKSAqIE1hdGgucG93KDIsIG1MZW4pO1xuICAgICAgZSA9IDA7XG4gICAgfVxuICB9XG5cbiAgZm9yICg7IG1MZW4gPj0gODsgYnVmZmVyW29mZnNldCArIGldID0gbSAmIDB4ZmYsIGkgKz0gZCwgbSAvPSAyNTYsIG1MZW4gLT0gOCk7XG5cbiAgZSA9IChlIDw8IG1MZW4pIHwgbTtcbiAgZUxlbiArPSBtTGVuO1xuICBmb3IgKDsgZUxlbiA+IDA7IGJ1ZmZlcltvZmZzZXQgKyBpXSA9IGUgJiAweGZmLCBpICs9IGQsIGUgLz0gMjU2LCBlTGVuIC09IDgpO1xuXG4gIGJ1ZmZlcltvZmZzZXQgKyBpIC0gZF0gfD0gcyAqIDEyODtcbn07XG4iLCJcbi8qKlxuICogaXNBcnJheVxuICovXG5cbnZhciBpc0FycmF5ID0gQXJyYXkuaXNBcnJheTtcblxuLyoqXG4gKiB0b1N0cmluZ1xuICovXG5cbnZhciBzdHIgPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nO1xuXG4vKipcbiAqIFdoZXRoZXIgb3Igbm90IHRoZSBnaXZlbiBgdmFsYFxuICogaXMgYW4gYXJyYXkuXG4gKlxuICogZXhhbXBsZTpcbiAqXG4gKiAgICAgICAgaXNBcnJheShbXSk7XG4gKiAgICAgICAgLy8gPiB0cnVlXG4gKiAgICAgICAgaXNBcnJheShhcmd1bWVudHMpO1xuICogICAgICAgIC8vID4gZmFsc2VcbiAqICAgICAgICBpc0FycmF5KCcnKTtcbiAqICAgICAgICAvLyA+IGZhbHNlXG4gKlxuICogQHBhcmFtIHttaXhlZH0gdmFsXG4gKiBAcmV0dXJuIHtib29sfVxuICovXG5cbm1vZHVsZS5leHBvcnRzID0gaXNBcnJheSB8fCBmdW5jdGlvbiAodmFsKSB7XG4gIHJldHVybiAhISB2YWwgJiYgJ1tvYmplY3QgQXJyYXldJyA9PSBzdHIuY2FsbCh2YWwpO1xufTtcbiIsIi8vIHNoaW0gZm9yIHVzaW5nIHByb2Nlc3MgaW4gYnJvd3NlclxuXG52YXIgcHJvY2VzcyA9IG1vZHVsZS5leHBvcnRzID0ge307XG52YXIgcXVldWUgPSBbXTtcbnZhciBkcmFpbmluZyA9IGZhbHNlO1xuXG5mdW5jdGlvbiBkcmFpblF1ZXVlKCkge1xuICAgIGlmIChkcmFpbmluZykge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGRyYWluaW5nID0gdHJ1ZTtcbiAgICB2YXIgY3VycmVudFF1ZXVlO1xuICAgIHZhciBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgd2hpbGUobGVuKSB7XG4gICAgICAgIGN1cnJlbnRRdWV1ZSA9IHF1ZXVlO1xuICAgICAgICBxdWV1ZSA9IFtdO1xuICAgICAgICB2YXIgaSA9IC0xO1xuICAgICAgICB3aGlsZSAoKytpIDwgbGVuKSB7XG4gICAgICAgICAgICBjdXJyZW50UXVldWVbaV0oKTtcbiAgICAgICAgfVxuICAgICAgICBsZW4gPSBxdWV1ZS5sZW5ndGg7XG4gICAgfVxuICAgIGRyYWluaW5nID0gZmFsc2U7XG59XG5wcm9jZXNzLm5leHRUaWNrID0gZnVuY3Rpb24gKGZ1bikge1xuICAgIHF1ZXVlLnB1c2goZnVuKTtcbiAgICBpZiAoIWRyYWluaW5nKSB7XG4gICAgICAgIHNldFRpbWVvdXQoZHJhaW5RdWV1ZSwgMCk7XG4gICAgfVxufTtcblxucHJvY2Vzcy50aXRsZSA9ICdicm93c2VyJztcbnByb2Nlc3MuYnJvd3NlciA9IHRydWU7XG5wcm9jZXNzLmVudiA9IHt9O1xucHJvY2Vzcy5hcmd2ID0gW107XG5wcm9jZXNzLnZlcnNpb24gPSAnJzsgLy8gZW1wdHkgc3RyaW5nIHRvIGF2b2lkIHJlZ2V4cCBpc3N1ZXNcbnByb2Nlc3MudmVyc2lvbnMgPSB7fTtcblxuZnVuY3Rpb24gbm9vcCgpIHt9XG5cbnByb2Nlc3Mub24gPSBub29wO1xucHJvY2Vzcy5hZGRMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLm9uY2UgPSBub29wO1xucHJvY2Vzcy5vZmYgPSBub29wO1xucHJvY2Vzcy5yZW1vdmVMaXN0ZW5lciA9IG5vb3A7XG5wcm9jZXNzLnJlbW92ZUFsbExpc3RlbmVycyA9IG5vb3A7XG5wcm9jZXNzLmVtaXQgPSBub29wO1xuXG5wcm9jZXNzLmJpbmRpbmcgPSBmdW5jdGlvbiAobmFtZSkge1xuICAgIHRocm93IG5ldyBFcnJvcigncHJvY2Vzcy5iaW5kaW5nIGlzIG5vdCBzdXBwb3J0ZWQnKTtcbn07XG5cbi8vIFRPRE8oc2h0eWxtYW4pXG5wcm9jZXNzLmN3ZCA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuICcvJyB9O1xucHJvY2Vzcy5jaGRpciA9IGZ1bmN0aW9uIChkaXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3Byb2Nlc3MuY2hkaXIgaXMgbm90IHN1cHBvcnRlZCcpO1xufTtcbnByb2Nlc3MudW1hc2sgPSBmdW5jdGlvbigpIHsgcmV0dXJuIDA7IH07XG4iLCIoZnVuY3Rpb24gKCkge1xuICBcInVzZSBzdHJpY3RcIjtcblxuICBmdW5jdGlvbiBidG9hKHN0cikge1xuICAgIHZhciBidWZmZXJcbiAgICAgIDtcblxuICAgIGlmIChzdHIgaW5zdGFuY2VvZiBCdWZmZXIpIHtcbiAgICAgIGJ1ZmZlciA9IHN0cjtcbiAgICB9IGVsc2Uge1xuICAgICAgYnVmZmVyID0gbmV3IEJ1ZmZlcihzdHIudG9TdHJpbmcoKSwgJ2JpbmFyeScpO1xuICAgIH1cblxuICAgIHJldHVybiBidWZmZXIudG9TdHJpbmcoJ2Jhc2U2NCcpO1xuICB9XG5cbiAgbW9kdWxlLmV4cG9ydHMgPSBidG9hO1xufSgpKTtcbiIsIi8qIGpzaGludCBub2RlOiB0cnVlICovXG4oZnVuY3Rpb24gKCkge1xuICAgIFwidXNlIHN0cmljdFwiO1xuXG4gICAgZnVuY3Rpb24gQ29va2llQWNjZXNzSW5mbyhkb21haW4sIHBhdGgsIHNlY3VyZSwgc2NyaXB0KSB7XG4gICAgICAgIGlmICh0aGlzIGluc3RhbmNlb2YgQ29va2llQWNjZXNzSW5mbykge1xuICAgICAgICAgICAgdGhpcy5kb21haW4gPSBkb21haW4gfHwgdW5kZWZpbmVkO1xuICAgICAgICAgICAgdGhpcy5wYXRoID0gcGF0aCB8fCBcIi9cIjtcbiAgICAgICAgICAgIHRoaXMuc2VjdXJlID0gISFzZWN1cmU7XG4gICAgICAgICAgICB0aGlzLnNjcmlwdCA9ICEhc2NyaXB0O1xuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBDb29raWVBY2Nlc3NJbmZvKGRvbWFpbiwgcGF0aCwgc2VjdXJlLCBzY3JpcHQpO1xuICAgIH1cbiAgICBleHBvcnRzLkNvb2tpZUFjY2Vzc0luZm8gPSBDb29raWVBY2Nlc3NJbmZvO1xuXG4gICAgZnVuY3Rpb24gQ29va2llKGNvb2tpZXN0ciwgcmVxdWVzdF9kb21haW4sIHJlcXVlc3RfcGF0aCkge1xuICAgICAgICBpZiAoY29va2llc3RyIGluc3RhbmNlb2YgQ29va2llKSB7XG4gICAgICAgICAgICByZXR1cm4gY29va2llc3RyO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzIGluc3RhbmNlb2YgQ29va2llKSB7XG4gICAgICAgICAgICB0aGlzLm5hbWUgPSBudWxsO1xuICAgICAgICAgICAgdGhpcy52YWx1ZSA9IG51bGw7XG4gICAgICAgICAgICB0aGlzLmV4cGlyYXRpb25fZGF0ZSA9IEluZmluaXR5O1xuICAgICAgICAgICAgdGhpcy5wYXRoID0gU3RyaW5nKHJlcXVlc3RfcGF0aCB8fCBcIi9cIik7XG4gICAgICAgICAgICB0aGlzLmV4cGxpY2l0X3BhdGggPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuZG9tYWluID0gcmVxdWVzdF9kb21haW4gfHwgbnVsbDtcbiAgICAgICAgICAgIHRoaXMuZXhwbGljaXRfZG9tYWluID0gZmFsc2U7XG4gICAgICAgICAgICB0aGlzLnNlY3VyZSA9IGZhbHNlOyAvL2hvdyB0byBkZWZpbmUgZGVmYXVsdD9cbiAgICAgICAgICAgIHRoaXMubm9zY3JpcHQgPSBmYWxzZTsgLy9odHRwb25seVxuICAgICAgICAgICAgaWYgKGNvb2tpZXN0cikge1xuICAgICAgICAgICAgICAgIHRoaXMucGFyc2UoY29va2llc3RyLCByZXF1ZXN0X2RvbWFpbiwgcmVxdWVzdF9wYXRoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXcgQ29va2llKGNvb2tpZXN0cik7XG4gICAgfVxuICAgIGV4cG9ydHMuQ29va2llID0gQ29va2llO1xuXG4gICAgQ29va2llLnByb3RvdHlwZS50b1N0cmluZyA9IGZ1bmN0aW9uIHRvU3RyaW5nKCkge1xuICAgICAgICB2YXIgc3RyID0gW3RoaXMubmFtZSArIFwiPVwiICsgdGhpcy52YWx1ZV07XG4gICAgICAgIGlmICh0aGlzLmV4cGlyYXRpb25fZGF0ZSAhPT0gSW5maW5pdHkpIHtcbiAgICAgICAgICAgIHN0ci5wdXNoKFwiZXhwaXJlcz1cIiArIChuZXcgRGF0ZSh0aGlzLmV4cGlyYXRpb25fZGF0ZSkpLnRvR01UU3RyaW5nKCkpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLmRvbWFpbikge1xuICAgICAgICAgICAgc3RyLnB1c2goXCJkb21haW49XCIgKyB0aGlzLmRvbWFpbik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMucGF0aCkge1xuICAgICAgICAgICAgc3RyLnB1c2goXCJwYXRoPVwiICsgdGhpcy5wYXRoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5zZWN1cmUpIHtcbiAgICAgICAgICAgIHN0ci5wdXNoKFwic2VjdXJlXCIpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLm5vc2NyaXB0KSB7XG4gICAgICAgICAgICBzdHIucHVzaChcImh0dHBvbmx5XCIpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzdHIuam9pbihcIjsgXCIpO1xuICAgIH07XG5cbiAgICBDb29raWUucHJvdG90eXBlLnRvVmFsdWVTdHJpbmcgPSBmdW5jdGlvbiB0b1ZhbHVlU3RyaW5nKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5uYW1lICsgXCI9XCIgKyB0aGlzLnZhbHVlO1xuICAgIH07XG5cbiAgICB2YXIgY29va2llX3N0cl9zcGxpdHRlciA9IC9bOl0oPz1cXHMqW2EtekEtWjAtOV9cXC1dK1xccypbPV0pL2c7XG4gICAgQ29va2llLnByb3RvdHlwZS5wYXJzZSA9IGZ1bmN0aW9uIHBhcnNlKHN0ciwgcmVxdWVzdF9kb21haW4sIHJlcXVlc3RfcGF0aCkge1xuICAgICAgICBpZiAodGhpcyBpbnN0YW5jZW9mIENvb2tpZSkge1xuICAgICAgICAgICAgdmFyIHBhcnRzID0gc3RyLnNwbGl0KFwiO1wiKS5maWx0ZXIoZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiAhIXZhbHVlO1xuICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICAgIHBhaXIgPSBwYXJ0c1swXS5tYXRjaCgvKFtePV0rKT0oW1xcc1xcU10qKS8pLFxuICAgICAgICAgICAgICAgIGtleSA9IHBhaXJbMV0sXG4gICAgICAgICAgICAgICAgdmFsdWUgPSBwYWlyWzJdLFxuICAgICAgICAgICAgICAgIGk7XG4gICAgICAgICAgICB0aGlzLm5hbWUgPSBrZXk7XG4gICAgICAgICAgICB0aGlzLnZhbHVlID0gdmFsdWU7XG5cbiAgICAgICAgICAgIGZvciAoaSA9IDE7IGkgPCBwYXJ0cy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICAgICAgICAgIHBhaXIgPSBwYXJ0c1tpXS5tYXRjaCgvKFtePV0rKSg/Oj0oW1xcc1xcU10qKSk/Lyk7XG4gICAgICAgICAgICAgICAga2V5ID0gcGFpclsxXS50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgICAgICAgICB2YWx1ZSA9IHBhaXJbMl07XG4gICAgICAgICAgICAgICAgc3dpdGNoIChrZXkpIHtcbiAgICAgICAgICAgICAgICBjYXNlIFwiaHR0cG9ubHlcIjpcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5ub3NjcmlwdCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgXCJleHBpcmVzXCI6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZXhwaXJhdGlvbl9kYXRlID0gdmFsdWUgP1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlcihEYXRlLnBhcnNlKHZhbHVlKSkgOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluZmluaXR5O1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlIFwicGF0aFwiOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBhdGggPSB2YWx1ZSA/XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUudHJpbSgpIDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIlwiO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmV4cGxpY2l0X3BhdGggPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlIFwiZG9tYWluXCI6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZG9tYWluID0gdmFsdWUgP1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLnRyaW0oKSA6XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCJcIjtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5leHBsaWNpdF9kb21haW4gPSAhIXRoaXMuZG9tYWluO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICBjYXNlIFwic2VjdXJlXCI6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VjdXJlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIXRoaXMuZXhwbGljaXRfcGF0aCkge1xuICAgICAgICAgICAgICAgdGhpcy5wYXRoID0gcmVxdWVzdF9wYXRoIHx8IFwiL1wiO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCF0aGlzLmV4cGxpY2l0X2RvbWFpbikge1xuICAgICAgICAgICAgICAgdGhpcy5kb21haW4gPSByZXF1ZXN0X2RvbWFpbjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ldyBDb29raWUoKS5wYXJzZShzdHIsIHJlcXVlc3RfZG9tYWluLCByZXF1ZXN0X3BhdGgpO1xuICAgIH07XG5cbiAgICBDb29raWUucHJvdG90eXBlLm1hdGNoZXMgPSBmdW5jdGlvbiBtYXRjaGVzKGFjY2Vzc19pbmZvKSB7XG4gICAgICAgIGlmICh0aGlzLm5vc2NyaXB0ICYmIGFjY2Vzc19pbmZvLnNjcmlwdCB8fFxuICAgICAgICAgICAgICAgIHRoaXMuc2VjdXJlICYmICFhY2Nlc3NfaW5mby5zZWN1cmUgfHxcbiAgICAgICAgICAgICAgICAhdGhpcy5jb2xsaWRlc1dpdGgoYWNjZXNzX2luZm8pKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfTtcblxuICAgIENvb2tpZS5wcm90b3R5cGUuY29sbGlkZXNXaXRoID0gZnVuY3Rpb24gY29sbGlkZXNXaXRoKGFjY2Vzc19pbmZvKSB7XG4gICAgICAgIGlmICgodGhpcy5wYXRoICYmICFhY2Nlc3NfaW5mby5wYXRoKSB8fCAodGhpcy5kb21haW4gJiYgIWFjY2Vzc19pbmZvLmRvbWFpbikpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5wYXRoICYmIGFjY2Vzc19pbmZvLnBhdGguaW5kZXhPZih0aGlzLnBhdGgpICE9PSAwKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCF0aGlzLmV4cGxpY2l0X3BhdGgpIHtcbiAgICAgICAgICAgaWYgKHRoaXMucGF0aCAhPT0gYWNjZXNzX2luZm8ucGF0aCkge1xuICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdmFyIGFjY2Vzc19kb21haW4gPSBhY2Nlc3NfaW5mby5kb21haW4gJiYgYWNjZXNzX2luZm8uZG9tYWluLnJlcGxhY2UoL15bXFwuXS8sJycpO1xuICAgICAgICB2YXIgY29va2llX2RvbWFpbiA9IHRoaXMuZG9tYWluICYmIHRoaXMuZG9tYWluLnJlcGxhY2UoL15bXFwuXS8sJycpO1xuICAgICAgICBpZiAoY29va2llX2RvbWFpbiA9PT0gYWNjZXNzX2RvbWFpbikge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvb2tpZV9kb21haW4pIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5leHBsaWNpdF9kb21haW4pIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7IC8vIHdlIGFscmVhZHkgY2hlY2tlZCBpZiB0aGUgZG9tYWlucyB3ZXJlIGV4YWN0bHkgdGhlIHNhbWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHZhciB3aWxkY2FyZCA9IGFjY2Vzc19kb21haW4uaW5kZXhPZihjb29raWVfZG9tYWluKTtcbiAgICAgICAgICAgIGlmICh3aWxkY2FyZCA9PT0gLTEgfHwgd2lsZGNhcmQgIT09IGFjY2Vzc19kb21haW4ubGVuZ3RoIC0gY29va2llX2RvbWFpbi5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9O1xuXG4gICAgZnVuY3Rpb24gQ29va2llSmFyKCkge1xuICAgICAgICB2YXIgY29va2llcywgY29va2llc19saXN0LCBjb2xsaWRhYmxlX2Nvb2tpZTtcbiAgICAgICAgaWYgKHRoaXMgaW5zdGFuY2VvZiBDb29raWVKYXIpIHtcbiAgICAgICAgICAgIGNvb2tpZXMgPSBPYmplY3QuY3JlYXRlKG51bGwpOyAvL25hbWU6IFtDb29raWVdXG5cbiAgICAgICAgICAgIHRoaXMuc2V0Q29va2llID0gZnVuY3Rpb24gc2V0Q29va2llKGNvb2tpZSwgcmVxdWVzdF9kb21haW4sIHJlcXVlc3RfcGF0aCkge1xuICAgICAgICAgICAgICAgIHZhciByZW1vdmUsIGk7XG4gICAgICAgICAgICAgICAgY29va2llID0gbmV3IENvb2tpZShjb29raWUsIHJlcXVlc3RfZG9tYWluLCByZXF1ZXN0X3BhdGgpO1xuICAgICAgICAgICAgICAgIC8vRGVsZXRlIHRoZSBjb29raWUgaWYgdGhlIHNldCBpcyBwYXN0IHRoZSBjdXJyZW50IHRpbWVcbiAgICAgICAgICAgICAgICByZW1vdmUgPSBjb29raWUuZXhwaXJhdGlvbl9kYXRlIDw9IERhdGUubm93KCk7XG4gICAgICAgICAgICAgICAgaWYgKGNvb2tpZXNbY29va2llLm5hbWVdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgY29va2llc19saXN0ID0gY29va2llc1tjb29raWUubmFtZV07XG4gICAgICAgICAgICAgICAgICAgIGZvciAoaSA9IDA7IGkgPCBjb29raWVzX2xpc3QubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxpZGFibGVfY29va2llID0gY29va2llc19saXN0W2ldO1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNvbGxpZGFibGVfY29va2llLmNvbGxpZGVzV2l0aChjb29raWUpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHJlbW92ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb29raWVzX2xpc3Quc3BsaWNlKGksIDEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoY29va2llc19saXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsZXRlIGNvb2tpZXNbY29va2llLm5hbWVdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29va2llc19saXN0W2ldID0gY29va2llO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjb29raWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlbW92ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvb2tpZXNfbGlzdC5wdXNoKGNvb2tpZSk7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBjb29raWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChyZW1vdmUpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBjb29raWVzW2Nvb2tpZS5uYW1lXSA9IFtjb29raWVdO1xuICAgICAgICAgICAgICAgIHJldHVybiBjb29raWVzW2Nvb2tpZS5uYW1lXTtcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICAvL3JldHVybnMgYSBjb29raWVcbiAgICAgICAgICAgIHRoaXMuZ2V0Q29va2llID0gZnVuY3Rpb24gZ2V0Q29va2llKGNvb2tpZV9uYW1lLCBhY2Nlc3NfaW5mbykge1xuICAgICAgICAgICAgICAgIHZhciBjb29raWUsIGk7XG4gICAgICAgICAgICAgICAgY29va2llc19saXN0ID0gY29va2llc1tjb29raWVfbmFtZV07XG4gICAgICAgICAgICAgICAgaWYgKCFjb29raWVzX2xpc3QpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBmb3IgKGkgPSAwOyBpIDwgY29va2llc19saXN0Lmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvb2tpZSA9IGNvb2tpZXNfbGlzdFtpXTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNvb2tpZS5leHBpcmF0aW9uX2RhdGUgPD0gRGF0ZS5ub3coKSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNvb2tpZXNfbGlzdC5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWxldGUgY29va2llc1tjb29raWUubmFtZV07XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBpZiAoY29va2llLm1hdGNoZXMoYWNjZXNzX2luZm8pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gY29va2llO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIC8vcmV0dXJucyBhIGxpc3Qgb2YgY29va2llc1xuICAgICAgICAgICAgdGhpcy5nZXRDb29raWVzID0gZnVuY3Rpb24gZ2V0Q29va2llcyhhY2Nlc3NfaW5mbykge1xuICAgICAgICAgICAgICAgIHZhciBtYXRjaGVzID0gW10sIGNvb2tpZV9uYW1lLCBjb29raWU7XG4gICAgICAgICAgICAgICAgZm9yIChjb29raWVfbmFtZSBpbiBjb29raWVzKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvb2tpZSA9IHRoaXMuZ2V0Q29va2llKGNvb2tpZV9uYW1lLCBhY2Nlc3NfaW5mbyk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChjb29raWUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoZXMucHVzaChjb29raWUpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG1hdGNoZXMudG9TdHJpbmcgPSBmdW5jdGlvbiB0b1N0cmluZygpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG1hdGNoZXMuam9pbihcIjpcIik7XG4gICAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgICBtYXRjaGVzLnRvVmFsdWVTdHJpbmcgPSBmdW5jdGlvbiB0b1ZhbHVlU3RyaW5nKCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWF0Y2hlcy5tYXAoZnVuY3Rpb24gKGMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBjLnRvVmFsdWVTdHJpbmcoKTtcbiAgICAgICAgICAgICAgICAgICAgfSkuam9pbignOycpO1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgcmV0dXJuIG1hdGNoZXM7XG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gbmV3IENvb2tpZUphcigpO1xuICAgIH1cbiAgICBleHBvcnRzLkNvb2tpZUphciA9IENvb2tpZUphcjtcblxuICAgIC8vcmV0dXJucyBsaXN0IG9mIGNvb2tpZXMgdGhhdCB3ZXJlIHNldCBjb3JyZWN0bHkuIENvb2tpZXMgdGhhdCBhcmUgZXhwaXJlZCBhbmQgcmVtb3ZlZCBhcmUgbm90IHJldHVybmVkLlxuICAgIENvb2tpZUphci5wcm90b3R5cGUuc2V0Q29va2llcyA9IGZ1bmN0aW9uIHNldENvb2tpZXMoY29va2llcywgcmVxdWVzdF9kb21haW4sIHJlcXVlc3RfcGF0aCkge1xuICAgICAgICBjb29raWVzID0gQXJyYXkuaXNBcnJheShjb29raWVzKSA/XG4gICAgICAgICAgICAgICAgY29va2llcyA6XG4gICAgICAgICAgICAgICAgY29va2llcy5zcGxpdChjb29raWVfc3RyX3NwbGl0dGVyKTtcbiAgICAgICAgdmFyIHN1Y2Nlc3NmdWwgPSBbXSxcbiAgICAgICAgICAgIGksXG4gICAgICAgICAgICBjb29raWU7XG4gICAgICAgIGNvb2tpZXMgPSBjb29raWVzLm1hcChDb29raWUpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgY29va2llcy5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICAgICAgY29va2llID0gY29va2llc1tpXTtcbiAgICAgICAgICAgIGlmICh0aGlzLnNldENvb2tpZShjb29raWUsIHJlcXVlc3RfZG9tYWluLCByZXF1ZXN0X3BhdGgpKSB7XG4gICAgICAgICAgICAgICAgc3VjY2Vzc2Z1bC5wdXNoKGNvb2tpZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHN1Y2Nlc3NmdWw7XG4gICAgfTtcbn0oKSk7XG4iLCIvKiFcbiAqIGpRdWVyeSBKYXZhU2NyaXB0IExpYnJhcnkgdjIuMS4zXG4gKiBodHRwOi8vanF1ZXJ5LmNvbS9cbiAqXG4gKiBJbmNsdWRlcyBTaXp6bGUuanNcbiAqIGh0dHA6Ly9zaXp6bGVqcy5jb20vXG4gKlxuICogQ29weXJpZ2h0IDIwMDUsIDIwMTQgalF1ZXJ5IEZvdW5kYXRpb24sIEluYy4gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKlxuICogRGF0ZTogMjAxNC0xMi0xOFQxNToxMVpcbiAqL1xuXG4oZnVuY3Rpb24oIGdsb2JhbCwgZmFjdG9yeSApIHtcblxuXHRpZiAoIHR5cGVvZiBtb2R1bGUgPT09IFwib2JqZWN0XCIgJiYgdHlwZW9mIG1vZHVsZS5leHBvcnRzID09PSBcIm9iamVjdFwiICkge1xuXHRcdC8vIEZvciBDb21tb25KUyBhbmQgQ29tbW9uSlMtbGlrZSBlbnZpcm9ubWVudHMgd2hlcmUgYSBwcm9wZXIgYHdpbmRvd2Bcblx0XHQvLyBpcyBwcmVzZW50LCBleGVjdXRlIHRoZSBmYWN0b3J5IGFuZCBnZXQgalF1ZXJ5LlxuXHRcdC8vIEZvciBlbnZpcm9ubWVudHMgdGhhdCBkbyBub3QgaGF2ZSBhIGB3aW5kb3dgIHdpdGggYSBgZG9jdW1lbnRgXG5cdFx0Ly8gKHN1Y2ggYXMgTm9kZS5qcyksIGV4cG9zZSBhIGZhY3RvcnkgYXMgbW9kdWxlLmV4cG9ydHMuXG5cdFx0Ly8gVGhpcyBhY2NlbnR1YXRlcyB0aGUgbmVlZCBmb3IgdGhlIGNyZWF0aW9uIG9mIGEgcmVhbCBgd2luZG93YC5cblx0XHQvLyBlLmcuIHZhciBqUXVlcnkgPSByZXF1aXJlKFwianF1ZXJ5XCIpKHdpbmRvdyk7XG5cdFx0Ly8gU2VlIHRpY2tldCAjMTQ1NDkgZm9yIG1vcmUgaW5mby5cblx0XHRtb2R1bGUuZXhwb3J0cyA9IGdsb2JhbC5kb2N1bWVudCA/XG5cdFx0XHRmYWN0b3J5KCBnbG9iYWwsIHRydWUgKSA6XG5cdFx0XHRmdW5jdGlvbiggdyApIHtcblx0XHRcdFx0aWYgKCAhdy5kb2N1bWVudCApIHtcblx0XHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoIFwialF1ZXJ5IHJlcXVpcmVzIGEgd2luZG93IHdpdGggYSBkb2N1bWVudFwiICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIGZhY3RvcnkoIHcgKTtcblx0XHRcdH07XG5cdH0gZWxzZSB7XG5cdFx0ZmFjdG9yeSggZ2xvYmFsICk7XG5cdH1cblxuLy8gUGFzcyB0aGlzIGlmIHdpbmRvdyBpcyBub3QgZGVmaW5lZCB5ZXRcbn0odHlwZW9mIHdpbmRvdyAhPT0gXCJ1bmRlZmluZWRcIiA/IHdpbmRvdyA6IHRoaXMsIGZ1bmN0aW9uKCB3aW5kb3csIG5vR2xvYmFsICkge1xuXG4vLyBTdXBwb3J0OiBGaXJlZm94IDE4K1xuLy8gQ2FuJ3QgYmUgaW4gc3RyaWN0IG1vZGUsIHNldmVyYWwgbGlicyBpbmNsdWRpbmcgQVNQLk5FVCB0cmFjZVxuLy8gdGhlIHN0YWNrIHZpYSBhcmd1bWVudHMuY2FsbGVyLmNhbGxlZSBhbmQgRmlyZWZveCBkaWVzIGlmXG4vLyB5b3UgdHJ5IHRvIHRyYWNlIHRocm91Z2ggXCJ1c2Ugc3RyaWN0XCIgY2FsbCBjaGFpbnMuICgjMTMzMzUpXG4vL1xuXG52YXIgYXJyID0gW107XG5cbnZhciBzbGljZSA9IGFyci5zbGljZTtcblxudmFyIGNvbmNhdCA9IGFyci5jb25jYXQ7XG5cbnZhciBwdXNoID0gYXJyLnB1c2g7XG5cbnZhciBpbmRleE9mID0gYXJyLmluZGV4T2Y7XG5cbnZhciBjbGFzczJ0eXBlID0ge307XG5cbnZhciB0b1N0cmluZyA9IGNsYXNzMnR5cGUudG9TdHJpbmc7XG5cbnZhciBoYXNPd24gPSBjbGFzczJ0eXBlLmhhc093blByb3BlcnR5O1xuXG52YXIgc3VwcG9ydCA9IHt9O1xuXG5cblxudmFyXG5cdC8vIFVzZSB0aGUgY29ycmVjdCBkb2N1bWVudCBhY2NvcmRpbmdseSB3aXRoIHdpbmRvdyBhcmd1bWVudCAoc2FuZGJveClcblx0ZG9jdW1lbnQgPSB3aW5kb3cuZG9jdW1lbnQsXG5cblx0dmVyc2lvbiA9IFwiMi4xLjNcIixcblxuXHQvLyBEZWZpbmUgYSBsb2NhbCBjb3B5IG9mIGpRdWVyeVxuXHRqUXVlcnkgPSBmdW5jdGlvbiggc2VsZWN0b3IsIGNvbnRleHQgKSB7XG5cdFx0Ly8gVGhlIGpRdWVyeSBvYmplY3QgaXMgYWN0dWFsbHkganVzdCB0aGUgaW5pdCBjb25zdHJ1Y3RvciAnZW5oYW5jZWQnXG5cdFx0Ly8gTmVlZCBpbml0IGlmIGpRdWVyeSBpcyBjYWxsZWQgKGp1c3QgYWxsb3cgZXJyb3IgdG8gYmUgdGhyb3duIGlmIG5vdCBpbmNsdWRlZClcblx0XHRyZXR1cm4gbmV3IGpRdWVyeS5mbi5pbml0KCBzZWxlY3RvciwgY29udGV4dCApO1xuXHR9LFxuXG5cdC8vIFN1cHBvcnQ6IEFuZHJvaWQ8NC4xXG5cdC8vIE1ha2Ugc3VyZSB3ZSB0cmltIEJPTSBhbmQgTkJTUFxuXHRydHJpbSA9IC9eW1xcc1xcdUZFRkZcXHhBMF0rfFtcXHNcXHVGRUZGXFx4QTBdKyQvZyxcblxuXHQvLyBNYXRjaGVzIGRhc2hlZCBzdHJpbmcgZm9yIGNhbWVsaXppbmdcblx0cm1zUHJlZml4ID0gL14tbXMtLyxcblx0cmRhc2hBbHBoYSA9IC8tKFtcXGRhLXpdKS9naSxcblxuXHQvLyBVc2VkIGJ5IGpRdWVyeS5jYW1lbENhc2UgYXMgY2FsbGJhY2sgdG8gcmVwbGFjZSgpXG5cdGZjYW1lbENhc2UgPSBmdW5jdGlvbiggYWxsLCBsZXR0ZXIgKSB7XG5cdFx0cmV0dXJuIGxldHRlci50b1VwcGVyQ2FzZSgpO1xuXHR9O1xuXG5qUXVlcnkuZm4gPSBqUXVlcnkucHJvdG90eXBlID0ge1xuXHQvLyBUaGUgY3VycmVudCB2ZXJzaW9uIG9mIGpRdWVyeSBiZWluZyB1c2VkXG5cdGpxdWVyeTogdmVyc2lvbixcblxuXHRjb25zdHJ1Y3RvcjogalF1ZXJ5LFxuXG5cdC8vIFN0YXJ0IHdpdGggYW4gZW1wdHkgc2VsZWN0b3Jcblx0c2VsZWN0b3I6IFwiXCIsXG5cblx0Ly8gVGhlIGRlZmF1bHQgbGVuZ3RoIG9mIGEgalF1ZXJ5IG9iamVjdCBpcyAwXG5cdGxlbmd0aDogMCxcblxuXHR0b0FycmF5OiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gc2xpY2UuY2FsbCggdGhpcyApO1xuXHR9LFxuXG5cdC8vIEdldCB0aGUgTnRoIGVsZW1lbnQgaW4gdGhlIG1hdGNoZWQgZWxlbWVudCBzZXQgT1Jcblx0Ly8gR2V0IHRoZSB3aG9sZSBtYXRjaGVkIGVsZW1lbnQgc2V0IGFzIGEgY2xlYW4gYXJyYXlcblx0Z2V0OiBmdW5jdGlvbiggbnVtICkge1xuXHRcdHJldHVybiBudW0gIT0gbnVsbCA/XG5cblx0XHRcdC8vIFJldHVybiBqdXN0IHRoZSBvbmUgZWxlbWVudCBmcm9tIHRoZSBzZXRcblx0XHRcdCggbnVtIDwgMCA/IHRoaXNbIG51bSArIHRoaXMubGVuZ3RoIF0gOiB0aGlzWyBudW0gXSApIDpcblxuXHRcdFx0Ly8gUmV0dXJuIGFsbCB0aGUgZWxlbWVudHMgaW4gYSBjbGVhbiBhcnJheVxuXHRcdFx0c2xpY2UuY2FsbCggdGhpcyApO1xuXHR9LFxuXG5cdC8vIFRha2UgYW4gYXJyYXkgb2YgZWxlbWVudHMgYW5kIHB1c2ggaXQgb250byB0aGUgc3RhY2tcblx0Ly8gKHJldHVybmluZyB0aGUgbmV3IG1hdGNoZWQgZWxlbWVudCBzZXQpXG5cdHB1c2hTdGFjazogZnVuY3Rpb24oIGVsZW1zICkge1xuXG5cdFx0Ly8gQnVpbGQgYSBuZXcgalF1ZXJ5IG1hdGNoZWQgZWxlbWVudCBzZXRcblx0XHR2YXIgcmV0ID0galF1ZXJ5Lm1lcmdlKCB0aGlzLmNvbnN0cnVjdG9yKCksIGVsZW1zICk7XG5cblx0XHQvLyBBZGQgdGhlIG9sZCBvYmplY3Qgb250byB0aGUgc3RhY2sgKGFzIGEgcmVmZXJlbmNlKVxuXHRcdHJldC5wcmV2T2JqZWN0ID0gdGhpcztcblx0XHRyZXQuY29udGV4dCA9IHRoaXMuY29udGV4dDtcblxuXHRcdC8vIFJldHVybiB0aGUgbmV3bHktZm9ybWVkIGVsZW1lbnQgc2V0XG5cdFx0cmV0dXJuIHJldDtcblx0fSxcblxuXHQvLyBFeGVjdXRlIGEgY2FsbGJhY2sgZm9yIGV2ZXJ5IGVsZW1lbnQgaW4gdGhlIG1hdGNoZWQgc2V0LlxuXHQvLyAoWW91IGNhbiBzZWVkIHRoZSBhcmd1bWVudHMgd2l0aCBhbiBhcnJheSBvZiBhcmdzLCBidXQgdGhpcyBpc1xuXHQvLyBvbmx5IHVzZWQgaW50ZXJuYWxseS4pXG5cdGVhY2g6IGZ1bmN0aW9uKCBjYWxsYmFjaywgYXJncyApIHtcblx0XHRyZXR1cm4galF1ZXJ5LmVhY2goIHRoaXMsIGNhbGxiYWNrLCBhcmdzICk7XG5cdH0sXG5cblx0bWFwOiBmdW5jdGlvbiggY2FsbGJhY2sgKSB7XG5cdFx0cmV0dXJuIHRoaXMucHVzaFN0YWNrKCBqUXVlcnkubWFwKHRoaXMsIGZ1bmN0aW9uKCBlbGVtLCBpICkge1xuXHRcdFx0cmV0dXJuIGNhbGxiYWNrLmNhbGwoIGVsZW0sIGksIGVsZW0gKTtcblx0XHR9KSk7XG5cdH0sXG5cblx0c2xpY2U6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLnB1c2hTdGFjayggc2xpY2UuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApICk7XG5cdH0sXG5cblx0Zmlyc3Q6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLmVxKCAwICk7XG5cdH0sXG5cblx0bGFzdDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuZXEoIC0xICk7XG5cdH0sXG5cblx0ZXE6IGZ1bmN0aW9uKCBpICkge1xuXHRcdHZhciBsZW4gPSB0aGlzLmxlbmd0aCxcblx0XHRcdGogPSAraSArICggaSA8IDAgPyBsZW4gOiAwICk7XG5cdFx0cmV0dXJuIHRoaXMucHVzaFN0YWNrKCBqID49IDAgJiYgaiA8IGxlbiA/IFsgdGhpc1tqXSBdIDogW10gKTtcblx0fSxcblxuXHRlbmQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLnByZXZPYmplY3QgfHwgdGhpcy5jb25zdHJ1Y3RvcihudWxsKTtcblx0fSxcblxuXHQvLyBGb3IgaW50ZXJuYWwgdXNlIG9ubHkuXG5cdC8vIEJlaGF2ZXMgbGlrZSBhbiBBcnJheSdzIG1ldGhvZCwgbm90IGxpa2UgYSBqUXVlcnkgbWV0aG9kLlxuXHRwdXNoOiBwdXNoLFxuXHRzb3J0OiBhcnIuc29ydCxcblx0c3BsaWNlOiBhcnIuc3BsaWNlXG59O1xuXG5qUXVlcnkuZXh0ZW5kID0galF1ZXJ5LmZuLmV4dGVuZCA9IGZ1bmN0aW9uKCkge1xuXHR2YXIgb3B0aW9ucywgbmFtZSwgc3JjLCBjb3B5LCBjb3B5SXNBcnJheSwgY2xvbmUsXG5cdFx0dGFyZ2V0ID0gYXJndW1lbnRzWzBdIHx8IHt9LFxuXHRcdGkgPSAxLFxuXHRcdGxlbmd0aCA9IGFyZ3VtZW50cy5sZW5ndGgsXG5cdFx0ZGVlcCA9IGZhbHNlO1xuXG5cdC8vIEhhbmRsZSBhIGRlZXAgY29weSBzaXR1YXRpb25cblx0aWYgKCB0eXBlb2YgdGFyZ2V0ID09PSBcImJvb2xlYW5cIiApIHtcblx0XHRkZWVwID0gdGFyZ2V0O1xuXG5cdFx0Ly8gU2tpcCB0aGUgYm9vbGVhbiBhbmQgdGhlIHRhcmdldFxuXHRcdHRhcmdldCA9IGFyZ3VtZW50c1sgaSBdIHx8IHt9O1xuXHRcdGkrKztcblx0fVxuXG5cdC8vIEhhbmRsZSBjYXNlIHdoZW4gdGFyZ2V0IGlzIGEgc3RyaW5nIG9yIHNvbWV0aGluZyAocG9zc2libGUgaW4gZGVlcCBjb3B5KVxuXHRpZiAoIHR5cGVvZiB0YXJnZXQgIT09IFwib2JqZWN0XCIgJiYgIWpRdWVyeS5pc0Z1bmN0aW9uKHRhcmdldCkgKSB7XG5cdFx0dGFyZ2V0ID0ge307XG5cdH1cblxuXHQvLyBFeHRlbmQgalF1ZXJ5IGl0c2VsZiBpZiBvbmx5IG9uZSBhcmd1bWVudCBpcyBwYXNzZWRcblx0aWYgKCBpID09PSBsZW5ndGggKSB7XG5cdFx0dGFyZ2V0ID0gdGhpcztcblx0XHRpLS07XG5cdH1cblxuXHRmb3IgKCA7IGkgPCBsZW5ndGg7IGkrKyApIHtcblx0XHQvLyBPbmx5IGRlYWwgd2l0aCBub24tbnVsbC91bmRlZmluZWQgdmFsdWVzXG5cdFx0aWYgKCAob3B0aW9ucyA9IGFyZ3VtZW50c1sgaSBdKSAhPSBudWxsICkge1xuXHRcdFx0Ly8gRXh0ZW5kIHRoZSBiYXNlIG9iamVjdFxuXHRcdFx0Zm9yICggbmFtZSBpbiBvcHRpb25zICkge1xuXHRcdFx0XHRzcmMgPSB0YXJnZXRbIG5hbWUgXTtcblx0XHRcdFx0Y29weSA9IG9wdGlvbnNbIG5hbWUgXTtcblxuXHRcdFx0XHQvLyBQcmV2ZW50IG5ldmVyLWVuZGluZyBsb29wXG5cdFx0XHRcdGlmICggdGFyZ2V0ID09PSBjb3B5ICkge1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gUmVjdXJzZSBpZiB3ZSdyZSBtZXJnaW5nIHBsYWluIG9iamVjdHMgb3IgYXJyYXlzXG5cdFx0XHRcdGlmICggZGVlcCAmJiBjb3B5ICYmICggalF1ZXJ5LmlzUGxhaW5PYmplY3QoY29weSkgfHwgKGNvcHlJc0FycmF5ID0galF1ZXJ5LmlzQXJyYXkoY29weSkpICkgKSB7XG5cdFx0XHRcdFx0aWYgKCBjb3B5SXNBcnJheSApIHtcblx0XHRcdFx0XHRcdGNvcHlJc0FycmF5ID0gZmFsc2U7XG5cdFx0XHRcdFx0XHRjbG9uZSA9IHNyYyAmJiBqUXVlcnkuaXNBcnJheShzcmMpID8gc3JjIDogW107XG5cblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0Y2xvbmUgPSBzcmMgJiYgalF1ZXJ5LmlzUGxhaW5PYmplY3Qoc3JjKSA/IHNyYyA6IHt9O1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIE5ldmVyIG1vdmUgb3JpZ2luYWwgb2JqZWN0cywgY2xvbmUgdGhlbVxuXHRcdFx0XHRcdHRhcmdldFsgbmFtZSBdID0galF1ZXJ5LmV4dGVuZCggZGVlcCwgY2xvbmUsIGNvcHkgKTtcblxuXHRcdFx0XHQvLyBEb24ndCBicmluZyBpbiB1bmRlZmluZWQgdmFsdWVzXG5cdFx0XHRcdH0gZWxzZSBpZiAoIGNvcHkgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0XHR0YXJnZXRbIG5hbWUgXSA9IGNvcHk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvLyBSZXR1cm4gdGhlIG1vZGlmaWVkIG9iamVjdFxuXHRyZXR1cm4gdGFyZ2V0O1xufTtcblxualF1ZXJ5LmV4dGVuZCh7XG5cdC8vIFVuaXF1ZSBmb3IgZWFjaCBjb3B5IG9mIGpRdWVyeSBvbiB0aGUgcGFnZVxuXHRleHBhbmRvOiBcImpRdWVyeVwiICsgKCB2ZXJzaW9uICsgTWF0aC5yYW5kb20oKSApLnJlcGxhY2UoIC9cXEQvZywgXCJcIiApLFxuXG5cdC8vIEFzc3VtZSBqUXVlcnkgaXMgcmVhZHkgd2l0aG91dCB0aGUgcmVhZHkgbW9kdWxlXG5cdGlzUmVhZHk6IHRydWUsXG5cblx0ZXJyb3I6IGZ1bmN0aW9uKCBtc2cgKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKCBtc2cgKTtcblx0fSxcblxuXHRub29wOiBmdW5jdGlvbigpIHt9LFxuXG5cdGlzRnVuY3Rpb246IGZ1bmN0aW9uKCBvYmogKSB7XG5cdFx0cmV0dXJuIGpRdWVyeS50eXBlKG9iaikgPT09IFwiZnVuY3Rpb25cIjtcblx0fSxcblxuXHRpc0FycmF5OiBBcnJheS5pc0FycmF5LFxuXG5cdGlzV2luZG93OiBmdW5jdGlvbiggb2JqICkge1xuXHRcdHJldHVybiBvYmogIT0gbnVsbCAmJiBvYmogPT09IG9iai53aW5kb3c7XG5cdH0sXG5cblx0aXNOdW1lcmljOiBmdW5jdGlvbiggb2JqICkge1xuXHRcdC8vIHBhcnNlRmxvYXQgTmFOcyBudW1lcmljLWNhc3QgZmFsc2UgcG9zaXRpdmVzIChudWxsfHRydWV8ZmFsc2V8XCJcIilcblx0XHQvLyAuLi5idXQgbWlzaW50ZXJwcmV0cyBsZWFkaW5nLW51bWJlciBzdHJpbmdzLCBwYXJ0aWN1bGFybHkgaGV4IGxpdGVyYWxzIChcIjB4Li4uXCIpXG5cdFx0Ly8gc3VidHJhY3Rpb24gZm9yY2VzIGluZmluaXRpZXMgdG8gTmFOXG5cdFx0Ly8gYWRkaW5nIDEgY29ycmVjdHMgbG9zcyBvZiBwcmVjaXNpb24gZnJvbSBwYXJzZUZsb2F0ICgjMTUxMDApXG5cdFx0cmV0dXJuICFqUXVlcnkuaXNBcnJheSggb2JqICkgJiYgKG9iaiAtIHBhcnNlRmxvYXQoIG9iaiApICsgMSkgPj0gMDtcblx0fSxcblxuXHRpc1BsYWluT2JqZWN0OiBmdW5jdGlvbiggb2JqICkge1xuXHRcdC8vIE5vdCBwbGFpbiBvYmplY3RzOlxuXHRcdC8vIC0gQW55IG9iamVjdCBvciB2YWx1ZSB3aG9zZSBpbnRlcm5hbCBbW0NsYXNzXV0gcHJvcGVydHkgaXMgbm90IFwiW29iamVjdCBPYmplY3RdXCJcblx0XHQvLyAtIERPTSBub2Rlc1xuXHRcdC8vIC0gd2luZG93XG5cdFx0aWYgKCBqUXVlcnkudHlwZSggb2JqICkgIT09IFwib2JqZWN0XCIgfHwgb2JqLm5vZGVUeXBlIHx8IGpRdWVyeS5pc1dpbmRvdyggb2JqICkgKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXG5cdFx0aWYgKCBvYmouY29uc3RydWN0b3IgJiZcblx0XHRcdFx0IWhhc093bi5jYWxsKCBvYmouY29uc3RydWN0b3IucHJvdG90eXBlLCBcImlzUHJvdG90eXBlT2ZcIiApICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblxuXHRcdC8vIElmIHRoZSBmdW5jdGlvbiBoYXNuJ3QgcmV0dXJuZWQgYWxyZWFkeSwgd2UncmUgY29uZmlkZW50IHRoYXRcblx0XHQvLyB8b2JqfCBpcyBhIHBsYWluIG9iamVjdCwgY3JlYXRlZCBieSB7fSBvciBjb25zdHJ1Y3RlZCB3aXRoIG5ldyBPYmplY3Rcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHRpc0VtcHR5T2JqZWN0OiBmdW5jdGlvbiggb2JqICkge1xuXHRcdHZhciBuYW1lO1xuXHRcdGZvciAoIG5hbWUgaW4gb2JqICkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHR0eXBlOiBmdW5jdGlvbiggb2JqICkge1xuXHRcdGlmICggb2JqID09IG51bGwgKSB7XG5cdFx0XHRyZXR1cm4gb2JqICsgXCJcIjtcblx0XHR9XG5cdFx0Ly8gU3VwcG9ydDogQW5kcm9pZDw0LjAsIGlPUzw2IChmdW5jdGlvbmlzaCBSZWdFeHApXG5cdFx0cmV0dXJuIHR5cGVvZiBvYmogPT09IFwib2JqZWN0XCIgfHwgdHlwZW9mIG9iaiA9PT0gXCJmdW5jdGlvblwiID9cblx0XHRcdGNsYXNzMnR5cGVbIHRvU3RyaW5nLmNhbGwob2JqKSBdIHx8IFwib2JqZWN0XCIgOlxuXHRcdFx0dHlwZW9mIG9iajtcblx0fSxcblxuXHQvLyBFdmFsdWF0ZXMgYSBzY3JpcHQgaW4gYSBnbG9iYWwgY29udGV4dFxuXHRnbG9iYWxFdmFsOiBmdW5jdGlvbiggY29kZSApIHtcblx0XHR2YXIgc2NyaXB0LFxuXHRcdFx0aW5kaXJlY3QgPSBldmFsO1xuXG5cdFx0Y29kZSA9IGpRdWVyeS50cmltKCBjb2RlICk7XG5cblx0XHRpZiAoIGNvZGUgKSB7XG5cdFx0XHQvLyBJZiB0aGUgY29kZSBpbmNsdWRlcyBhIHZhbGlkLCBwcm9sb2d1ZSBwb3NpdGlvblxuXHRcdFx0Ly8gc3RyaWN0IG1vZGUgcHJhZ21hLCBleGVjdXRlIGNvZGUgYnkgaW5qZWN0aW5nIGFcblx0XHRcdC8vIHNjcmlwdCB0YWcgaW50byB0aGUgZG9jdW1lbnQuXG5cdFx0XHRpZiAoIGNvZGUuaW5kZXhPZihcInVzZSBzdHJpY3RcIikgPT09IDEgKSB7XG5cdFx0XHRcdHNjcmlwdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzY3JpcHRcIik7XG5cdFx0XHRcdHNjcmlwdC50ZXh0ID0gY29kZTtcblx0XHRcdFx0ZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZCggc2NyaXB0ICkucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCggc2NyaXB0ICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gT3RoZXJ3aXNlLCBhdm9pZCB0aGUgRE9NIG5vZGUgY3JlYXRpb24sIGluc2VydGlvblxuXHRcdFx0Ly8gYW5kIHJlbW92YWwgYnkgdXNpbmcgYW4gaW5kaXJlY3QgZ2xvYmFsIGV2YWxcblx0XHRcdFx0aW5kaXJlY3QoIGNvZGUgKTtcblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gQ29udmVydCBkYXNoZWQgdG8gY2FtZWxDYXNlOyB1c2VkIGJ5IHRoZSBjc3MgYW5kIGRhdGEgbW9kdWxlc1xuXHQvLyBTdXBwb3J0OiBJRTktMTErXG5cdC8vIE1pY3Jvc29mdCBmb3Jnb3QgdG8gaHVtcCB0aGVpciB2ZW5kb3IgcHJlZml4ICgjOTU3Milcblx0Y2FtZWxDYXNlOiBmdW5jdGlvbiggc3RyaW5nICkge1xuXHRcdHJldHVybiBzdHJpbmcucmVwbGFjZSggcm1zUHJlZml4LCBcIm1zLVwiICkucmVwbGFjZSggcmRhc2hBbHBoYSwgZmNhbWVsQ2FzZSApO1xuXHR9LFxuXG5cdG5vZGVOYW1lOiBmdW5jdGlvbiggZWxlbSwgbmFtZSApIHtcblx0XHRyZXR1cm4gZWxlbS5ub2RlTmFtZSAmJiBlbGVtLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCkgPT09IG5hbWUudG9Mb3dlckNhc2UoKTtcblx0fSxcblxuXHQvLyBhcmdzIGlzIGZvciBpbnRlcm5hbCB1c2FnZSBvbmx5XG5cdGVhY2g6IGZ1bmN0aW9uKCBvYmosIGNhbGxiYWNrLCBhcmdzICkge1xuXHRcdHZhciB2YWx1ZSxcblx0XHRcdGkgPSAwLFxuXHRcdFx0bGVuZ3RoID0gb2JqLmxlbmd0aCxcblx0XHRcdGlzQXJyYXkgPSBpc0FycmF5bGlrZSggb2JqICk7XG5cblx0XHRpZiAoIGFyZ3MgKSB7XG5cdFx0XHRpZiAoIGlzQXJyYXkgKSB7XG5cdFx0XHRcdGZvciAoIDsgaSA8IGxlbmd0aDsgaSsrICkge1xuXHRcdFx0XHRcdHZhbHVlID0gY2FsbGJhY2suYXBwbHkoIG9ialsgaSBdLCBhcmdzICk7XG5cblx0XHRcdFx0XHRpZiAoIHZhbHVlID09PSBmYWxzZSApIHtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Zm9yICggaSBpbiBvYmogKSB7XG5cdFx0XHRcdFx0dmFsdWUgPSBjYWxsYmFjay5hcHBseSggb2JqWyBpIF0sIGFyZ3MgKTtcblxuXHRcdFx0XHRcdGlmICggdmFsdWUgPT09IGZhbHNlICkge1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHQvLyBBIHNwZWNpYWwsIGZhc3QsIGNhc2UgZm9yIHRoZSBtb3N0IGNvbW1vbiB1c2Ugb2YgZWFjaFxuXHRcdH0gZWxzZSB7XG5cdFx0XHRpZiAoIGlzQXJyYXkgKSB7XG5cdFx0XHRcdGZvciAoIDsgaSA8IGxlbmd0aDsgaSsrICkge1xuXHRcdFx0XHRcdHZhbHVlID0gY2FsbGJhY2suY2FsbCggb2JqWyBpIF0sIGksIG9ialsgaSBdICk7XG5cblx0XHRcdFx0XHRpZiAoIHZhbHVlID09PSBmYWxzZSApIHtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Zm9yICggaSBpbiBvYmogKSB7XG5cdFx0XHRcdFx0dmFsdWUgPSBjYWxsYmFjay5jYWxsKCBvYmpbIGkgXSwgaSwgb2JqWyBpIF0gKTtcblxuXHRcdFx0XHRcdGlmICggdmFsdWUgPT09IGZhbHNlICkge1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG9iajtcblx0fSxcblxuXHQvLyBTdXBwb3J0OiBBbmRyb2lkPDQuMVxuXHR0cmltOiBmdW5jdGlvbiggdGV4dCApIHtcblx0XHRyZXR1cm4gdGV4dCA9PSBudWxsID9cblx0XHRcdFwiXCIgOlxuXHRcdFx0KCB0ZXh0ICsgXCJcIiApLnJlcGxhY2UoIHJ0cmltLCBcIlwiICk7XG5cdH0sXG5cblx0Ly8gcmVzdWx0cyBpcyBmb3IgaW50ZXJuYWwgdXNhZ2Ugb25seVxuXHRtYWtlQXJyYXk6IGZ1bmN0aW9uKCBhcnIsIHJlc3VsdHMgKSB7XG5cdFx0dmFyIHJldCA9IHJlc3VsdHMgfHwgW107XG5cblx0XHRpZiAoIGFyciAhPSBudWxsICkge1xuXHRcdFx0aWYgKCBpc0FycmF5bGlrZSggT2JqZWN0KGFycikgKSApIHtcblx0XHRcdFx0alF1ZXJ5Lm1lcmdlKCByZXQsXG5cdFx0XHRcdFx0dHlwZW9mIGFyciA9PT0gXCJzdHJpbmdcIiA/XG5cdFx0XHRcdFx0WyBhcnIgXSA6IGFyclxuXHRcdFx0XHQpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cHVzaC5jYWxsKCByZXQsIGFyciApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiByZXQ7XG5cdH0sXG5cblx0aW5BcnJheTogZnVuY3Rpb24oIGVsZW0sIGFyciwgaSApIHtcblx0XHRyZXR1cm4gYXJyID09IG51bGwgPyAtMSA6IGluZGV4T2YuY2FsbCggYXJyLCBlbGVtLCBpICk7XG5cdH0sXG5cblx0bWVyZ2U6IGZ1bmN0aW9uKCBmaXJzdCwgc2Vjb25kICkge1xuXHRcdHZhciBsZW4gPSArc2Vjb25kLmxlbmd0aCxcblx0XHRcdGogPSAwLFxuXHRcdFx0aSA9IGZpcnN0Lmxlbmd0aDtcblxuXHRcdGZvciAoIDsgaiA8IGxlbjsgaisrICkge1xuXHRcdFx0Zmlyc3RbIGkrKyBdID0gc2Vjb25kWyBqIF07XG5cdFx0fVxuXG5cdFx0Zmlyc3QubGVuZ3RoID0gaTtcblxuXHRcdHJldHVybiBmaXJzdDtcblx0fSxcblxuXHRncmVwOiBmdW5jdGlvbiggZWxlbXMsIGNhbGxiYWNrLCBpbnZlcnQgKSB7XG5cdFx0dmFyIGNhbGxiYWNrSW52ZXJzZSxcblx0XHRcdG1hdGNoZXMgPSBbXSxcblx0XHRcdGkgPSAwLFxuXHRcdFx0bGVuZ3RoID0gZWxlbXMubGVuZ3RoLFxuXHRcdFx0Y2FsbGJhY2tFeHBlY3QgPSAhaW52ZXJ0O1xuXG5cdFx0Ly8gR28gdGhyb3VnaCB0aGUgYXJyYXksIG9ubHkgc2F2aW5nIHRoZSBpdGVtc1xuXHRcdC8vIHRoYXQgcGFzcyB0aGUgdmFsaWRhdG9yIGZ1bmN0aW9uXG5cdFx0Zm9yICggOyBpIDwgbGVuZ3RoOyBpKysgKSB7XG5cdFx0XHRjYWxsYmFja0ludmVyc2UgPSAhY2FsbGJhY2soIGVsZW1zWyBpIF0sIGkgKTtcblx0XHRcdGlmICggY2FsbGJhY2tJbnZlcnNlICE9PSBjYWxsYmFja0V4cGVjdCApIHtcblx0XHRcdFx0bWF0Y2hlcy5wdXNoKCBlbGVtc1sgaSBdICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG1hdGNoZXM7XG5cdH0sXG5cblx0Ly8gYXJnIGlzIGZvciBpbnRlcm5hbCB1c2FnZSBvbmx5XG5cdG1hcDogZnVuY3Rpb24oIGVsZW1zLCBjYWxsYmFjaywgYXJnICkge1xuXHRcdHZhciB2YWx1ZSxcblx0XHRcdGkgPSAwLFxuXHRcdFx0bGVuZ3RoID0gZWxlbXMubGVuZ3RoLFxuXHRcdFx0aXNBcnJheSA9IGlzQXJyYXlsaWtlKCBlbGVtcyApLFxuXHRcdFx0cmV0ID0gW107XG5cblx0XHQvLyBHbyB0aHJvdWdoIHRoZSBhcnJheSwgdHJhbnNsYXRpbmcgZWFjaCBvZiB0aGUgaXRlbXMgdG8gdGhlaXIgbmV3IHZhbHVlc1xuXHRcdGlmICggaXNBcnJheSApIHtcblx0XHRcdGZvciAoIDsgaSA8IGxlbmd0aDsgaSsrICkge1xuXHRcdFx0XHR2YWx1ZSA9IGNhbGxiYWNrKCBlbGVtc1sgaSBdLCBpLCBhcmcgKTtcblxuXHRcdFx0XHRpZiAoIHZhbHVlICE9IG51bGwgKSB7XG5cdFx0XHRcdFx0cmV0LnB1c2goIHZhbHVlICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdC8vIEdvIHRocm91Z2ggZXZlcnkga2V5IG9uIHRoZSBvYmplY3QsXG5cdFx0fSBlbHNlIHtcblx0XHRcdGZvciAoIGkgaW4gZWxlbXMgKSB7XG5cdFx0XHRcdHZhbHVlID0gY2FsbGJhY2soIGVsZW1zWyBpIF0sIGksIGFyZyApO1xuXG5cdFx0XHRcdGlmICggdmFsdWUgIT0gbnVsbCApIHtcblx0XHRcdFx0XHRyZXQucHVzaCggdmFsdWUgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIEZsYXR0ZW4gYW55IG5lc3RlZCBhcnJheXNcblx0XHRyZXR1cm4gY29uY2F0LmFwcGx5KCBbXSwgcmV0ICk7XG5cdH0sXG5cblx0Ly8gQSBnbG9iYWwgR1VJRCBjb3VudGVyIGZvciBvYmplY3RzXG5cdGd1aWQ6IDEsXG5cblx0Ly8gQmluZCBhIGZ1bmN0aW9uIHRvIGEgY29udGV4dCwgb3B0aW9uYWxseSBwYXJ0aWFsbHkgYXBwbHlpbmcgYW55XG5cdC8vIGFyZ3VtZW50cy5cblx0cHJveHk6IGZ1bmN0aW9uKCBmbiwgY29udGV4dCApIHtcblx0XHR2YXIgdG1wLCBhcmdzLCBwcm94eTtcblxuXHRcdGlmICggdHlwZW9mIGNvbnRleHQgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHR0bXAgPSBmblsgY29udGV4dCBdO1xuXHRcdFx0Y29udGV4dCA9IGZuO1xuXHRcdFx0Zm4gPSB0bXA7XG5cdFx0fVxuXG5cdFx0Ly8gUXVpY2sgY2hlY2sgdG8gZGV0ZXJtaW5lIGlmIHRhcmdldCBpcyBjYWxsYWJsZSwgaW4gdGhlIHNwZWNcblx0XHQvLyB0aGlzIHRocm93cyBhIFR5cGVFcnJvciwgYnV0IHdlIHdpbGwganVzdCByZXR1cm4gdW5kZWZpbmVkLlxuXHRcdGlmICggIWpRdWVyeS5pc0Z1bmN0aW9uKCBmbiApICkge1xuXHRcdFx0cmV0dXJuIHVuZGVmaW5lZDtcblx0XHR9XG5cblx0XHQvLyBTaW11bGF0ZWQgYmluZFxuXHRcdGFyZ3MgPSBzbGljZS5jYWxsKCBhcmd1bWVudHMsIDIgKTtcblx0XHRwcm94eSA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIGZuLmFwcGx5KCBjb250ZXh0IHx8IHRoaXMsIGFyZ3MuY29uY2F0KCBzbGljZS5jYWxsKCBhcmd1bWVudHMgKSApICk7XG5cdFx0fTtcblxuXHRcdC8vIFNldCB0aGUgZ3VpZCBvZiB1bmlxdWUgaGFuZGxlciB0byB0aGUgc2FtZSBvZiBvcmlnaW5hbCBoYW5kbGVyLCBzbyBpdCBjYW4gYmUgcmVtb3ZlZFxuXHRcdHByb3h5Lmd1aWQgPSBmbi5ndWlkID0gZm4uZ3VpZCB8fCBqUXVlcnkuZ3VpZCsrO1xuXG5cdFx0cmV0dXJuIHByb3h5O1xuXHR9LFxuXG5cdG5vdzogRGF0ZS5ub3csXG5cblx0Ly8galF1ZXJ5LnN1cHBvcnQgaXMgbm90IHVzZWQgaW4gQ29yZSBidXQgb3RoZXIgcHJvamVjdHMgYXR0YWNoIHRoZWlyXG5cdC8vIHByb3BlcnRpZXMgdG8gaXQgc28gaXQgbmVlZHMgdG8gZXhpc3QuXG5cdHN1cHBvcnQ6IHN1cHBvcnRcbn0pO1xuXG4vLyBQb3B1bGF0ZSB0aGUgY2xhc3MydHlwZSBtYXBcbmpRdWVyeS5lYWNoKFwiQm9vbGVhbiBOdW1iZXIgU3RyaW5nIEZ1bmN0aW9uIEFycmF5IERhdGUgUmVnRXhwIE9iamVjdCBFcnJvclwiLnNwbGl0KFwiIFwiKSwgZnVuY3Rpb24oaSwgbmFtZSkge1xuXHRjbGFzczJ0eXBlWyBcIltvYmplY3QgXCIgKyBuYW1lICsgXCJdXCIgXSA9IG5hbWUudG9Mb3dlckNhc2UoKTtcbn0pO1xuXG5mdW5jdGlvbiBpc0FycmF5bGlrZSggb2JqICkge1xuXHR2YXIgbGVuZ3RoID0gb2JqLmxlbmd0aCxcblx0XHR0eXBlID0galF1ZXJ5LnR5cGUoIG9iaiApO1xuXG5cdGlmICggdHlwZSA9PT0gXCJmdW5jdGlvblwiIHx8IGpRdWVyeS5pc1dpbmRvdyggb2JqICkgKSB7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG5cblx0aWYgKCBvYmoubm9kZVR5cGUgPT09IDEgJiYgbGVuZ3RoICkge1xuXHRcdHJldHVybiB0cnVlO1xuXHR9XG5cblx0cmV0dXJuIHR5cGUgPT09IFwiYXJyYXlcIiB8fCBsZW5ndGggPT09IDAgfHxcblx0XHR0eXBlb2YgbGVuZ3RoID09PSBcIm51bWJlclwiICYmIGxlbmd0aCA+IDAgJiYgKCBsZW5ndGggLSAxICkgaW4gb2JqO1xufVxudmFyIFNpenpsZSA9XG4vKiFcbiAqIFNpenpsZSBDU1MgU2VsZWN0b3IgRW5naW5lIHYyLjIuMC1wcmVcbiAqIGh0dHA6Ly9zaXp6bGVqcy5jb20vXG4gKlxuICogQ29weXJpZ2h0IDIwMDgsIDIwMTQgalF1ZXJ5IEZvdW5kYXRpb24sIEluYy4gYW5kIG90aGVyIGNvbnRyaWJ1dG9yc1xuICogUmVsZWFzZWQgdW5kZXIgdGhlIE1JVCBsaWNlbnNlXG4gKiBodHRwOi8vanF1ZXJ5Lm9yZy9saWNlbnNlXG4gKlxuICogRGF0ZTogMjAxNC0xMi0xNlxuICovXG4oZnVuY3Rpb24oIHdpbmRvdyApIHtcblxudmFyIGksXG5cdHN1cHBvcnQsXG5cdEV4cHIsXG5cdGdldFRleHQsXG5cdGlzWE1MLFxuXHR0b2tlbml6ZSxcblx0Y29tcGlsZSxcblx0c2VsZWN0LFxuXHRvdXRlcm1vc3RDb250ZXh0LFxuXHRzb3J0SW5wdXQsXG5cdGhhc0R1cGxpY2F0ZSxcblxuXHQvLyBMb2NhbCBkb2N1bWVudCB2YXJzXG5cdHNldERvY3VtZW50LFxuXHRkb2N1bWVudCxcblx0ZG9jRWxlbSxcblx0ZG9jdW1lbnRJc0hUTUwsXG5cdHJidWdneVFTQSxcblx0cmJ1Z2d5TWF0Y2hlcyxcblx0bWF0Y2hlcyxcblx0Y29udGFpbnMsXG5cblx0Ly8gSW5zdGFuY2Utc3BlY2lmaWMgZGF0YVxuXHRleHBhbmRvID0gXCJzaXp6bGVcIiArIDEgKiBuZXcgRGF0ZSgpLFxuXHRwcmVmZXJyZWREb2MgPSB3aW5kb3cuZG9jdW1lbnQsXG5cdGRpcnJ1bnMgPSAwLFxuXHRkb25lID0gMCxcblx0Y2xhc3NDYWNoZSA9IGNyZWF0ZUNhY2hlKCksXG5cdHRva2VuQ2FjaGUgPSBjcmVhdGVDYWNoZSgpLFxuXHRjb21waWxlckNhY2hlID0gY3JlYXRlQ2FjaGUoKSxcblx0c29ydE9yZGVyID0gZnVuY3Rpb24oIGEsIGIgKSB7XG5cdFx0aWYgKCBhID09PSBiICkge1xuXHRcdFx0aGFzRHVwbGljYXRlID0gdHJ1ZTtcblx0XHR9XG5cdFx0cmV0dXJuIDA7XG5cdH0sXG5cblx0Ly8gR2VuZXJhbC1wdXJwb3NlIGNvbnN0YW50c1xuXHRNQVhfTkVHQVRJVkUgPSAxIDw8IDMxLFxuXG5cdC8vIEluc3RhbmNlIG1ldGhvZHNcblx0aGFzT3duID0gKHt9KS5oYXNPd25Qcm9wZXJ0eSxcblx0YXJyID0gW10sXG5cdHBvcCA9IGFyci5wb3AsXG5cdHB1c2hfbmF0aXZlID0gYXJyLnB1c2gsXG5cdHB1c2ggPSBhcnIucHVzaCxcblx0c2xpY2UgPSBhcnIuc2xpY2UsXG5cdC8vIFVzZSBhIHN0cmlwcGVkLWRvd24gaW5kZXhPZiBhcyBpdCdzIGZhc3RlciB0aGFuIG5hdGl2ZVxuXHQvLyBodHRwOi8vanNwZXJmLmNvbS90aG9yLWluZGV4b2YtdnMtZm9yLzVcblx0aW5kZXhPZiA9IGZ1bmN0aW9uKCBsaXN0LCBlbGVtICkge1xuXHRcdHZhciBpID0gMCxcblx0XHRcdGxlbiA9IGxpc3QubGVuZ3RoO1xuXHRcdGZvciAoIDsgaSA8IGxlbjsgaSsrICkge1xuXHRcdFx0aWYgKCBsaXN0W2ldID09PSBlbGVtICkge1xuXHRcdFx0XHRyZXR1cm4gaTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIC0xO1xuXHR9LFxuXG5cdGJvb2xlYW5zID0gXCJjaGVja2VkfHNlbGVjdGVkfGFzeW5jfGF1dG9mb2N1c3xhdXRvcGxheXxjb250cm9sc3xkZWZlcnxkaXNhYmxlZHxoaWRkZW58aXNtYXB8bG9vcHxtdWx0aXBsZXxvcGVufHJlYWRvbmx5fHJlcXVpcmVkfHNjb3BlZFwiLFxuXG5cdC8vIFJlZ3VsYXIgZXhwcmVzc2lvbnNcblxuXHQvLyBXaGl0ZXNwYWNlIGNoYXJhY3RlcnMgaHR0cDovL3d3dy53My5vcmcvVFIvY3NzMy1zZWxlY3RvcnMvI3doaXRlc3BhY2Vcblx0d2hpdGVzcGFjZSA9IFwiW1xcXFx4MjBcXFxcdFxcXFxyXFxcXG5cXFxcZl1cIixcblx0Ly8gaHR0cDovL3d3dy53My5vcmcvVFIvY3NzMy1zeW50YXgvI2NoYXJhY3RlcnNcblx0Y2hhcmFjdGVyRW5jb2RpbmcgPSBcIig/OlxcXFxcXFxcLnxbXFxcXHctXXxbXlxcXFx4MDAtXFxcXHhhMF0pK1wiLFxuXG5cdC8vIExvb3NlbHkgbW9kZWxlZCBvbiBDU1MgaWRlbnRpZmllciBjaGFyYWN0ZXJzXG5cdC8vIEFuIHVucXVvdGVkIHZhbHVlIHNob3VsZCBiZSBhIENTUyBpZGVudGlmaWVyIGh0dHA6Ly93d3cudzMub3JnL1RSL2NzczMtc2VsZWN0b3JzLyNhdHRyaWJ1dGUtc2VsZWN0b3JzXG5cdC8vIFByb3BlciBzeW50YXg6IGh0dHA6Ly93d3cudzMub3JnL1RSL0NTUzIxL3N5bmRhdGEuaHRtbCN2YWx1ZS1kZWYtaWRlbnRpZmllclxuXHRpZGVudGlmaWVyID0gY2hhcmFjdGVyRW5jb2RpbmcucmVwbGFjZSggXCJ3XCIsIFwidyNcIiApLFxuXG5cdC8vIEF0dHJpYnV0ZSBzZWxlY3RvcnM6IGh0dHA6Ly93d3cudzMub3JnL1RSL3NlbGVjdG9ycy8jYXR0cmlidXRlLXNlbGVjdG9yc1xuXHRhdHRyaWJ1dGVzID0gXCJcXFxcW1wiICsgd2hpdGVzcGFjZSArIFwiKihcIiArIGNoYXJhY3RlckVuY29kaW5nICsgXCIpKD86XCIgKyB3aGl0ZXNwYWNlICtcblx0XHQvLyBPcGVyYXRvciAoY2FwdHVyZSAyKVxuXHRcdFwiKihbKl4kfCF+XT89KVwiICsgd2hpdGVzcGFjZSArXG5cdFx0Ly8gXCJBdHRyaWJ1dGUgdmFsdWVzIG11c3QgYmUgQ1NTIGlkZW50aWZpZXJzIFtjYXB0dXJlIDVdIG9yIHN0cmluZ3MgW2NhcHR1cmUgMyBvciBjYXB0dXJlIDRdXCJcblx0XHRcIiooPzonKCg/OlxcXFxcXFxcLnxbXlxcXFxcXFxcJ10pKiknfFxcXCIoKD86XFxcXFxcXFwufFteXFxcXFxcXFxcXFwiXSkqKVxcXCJ8KFwiICsgaWRlbnRpZmllciArIFwiKSl8KVwiICsgd2hpdGVzcGFjZSArXG5cdFx0XCIqXFxcXF1cIixcblxuXHRwc2V1ZG9zID0gXCI6KFwiICsgY2hhcmFjdGVyRW5jb2RpbmcgKyBcIikoPzpcXFxcKChcIiArXG5cdFx0Ly8gVG8gcmVkdWNlIHRoZSBudW1iZXIgb2Ygc2VsZWN0b3JzIG5lZWRpbmcgdG9rZW5pemUgaW4gdGhlIHByZUZpbHRlciwgcHJlZmVyIGFyZ3VtZW50czpcblx0XHQvLyAxLiBxdW90ZWQgKGNhcHR1cmUgMzsgY2FwdHVyZSA0IG9yIGNhcHR1cmUgNSlcblx0XHRcIignKCg/OlxcXFxcXFxcLnxbXlxcXFxcXFxcJ10pKiknfFxcXCIoKD86XFxcXFxcXFwufFteXFxcXFxcXFxcXFwiXSkqKVxcXCIpfFwiICtcblx0XHQvLyAyLiBzaW1wbGUgKGNhcHR1cmUgNilcblx0XHRcIigoPzpcXFxcXFxcXC58W15cXFxcXFxcXCgpW1xcXFxdXXxcIiArIGF0dHJpYnV0ZXMgKyBcIikqKXxcIiArXG5cdFx0Ly8gMy4gYW55dGhpbmcgZWxzZSAoY2FwdHVyZSAyKVxuXHRcdFwiLipcIiArXG5cdFx0XCIpXFxcXCl8KVwiLFxuXG5cdC8vIExlYWRpbmcgYW5kIG5vbi1lc2NhcGVkIHRyYWlsaW5nIHdoaXRlc3BhY2UsIGNhcHR1cmluZyBzb21lIG5vbi13aGl0ZXNwYWNlIGNoYXJhY3RlcnMgcHJlY2VkaW5nIHRoZSBsYXR0ZXJcblx0cndoaXRlc3BhY2UgPSBuZXcgUmVnRXhwKCB3aGl0ZXNwYWNlICsgXCIrXCIsIFwiZ1wiICksXG5cdHJ0cmltID0gbmV3IFJlZ0V4cCggXCJeXCIgKyB3aGl0ZXNwYWNlICsgXCIrfCgoPzpefFteXFxcXFxcXFxdKSg/OlxcXFxcXFxcLikqKVwiICsgd2hpdGVzcGFjZSArIFwiKyRcIiwgXCJnXCIgKSxcblxuXHRyY29tbWEgPSBuZXcgUmVnRXhwKCBcIl5cIiArIHdoaXRlc3BhY2UgKyBcIiosXCIgKyB3aGl0ZXNwYWNlICsgXCIqXCIgKSxcblx0cmNvbWJpbmF0b3JzID0gbmV3IFJlZ0V4cCggXCJeXCIgKyB3aGl0ZXNwYWNlICsgXCIqKFs+K35dfFwiICsgd2hpdGVzcGFjZSArIFwiKVwiICsgd2hpdGVzcGFjZSArIFwiKlwiICksXG5cblx0cmF0dHJpYnV0ZVF1b3RlcyA9IG5ldyBSZWdFeHAoIFwiPVwiICsgd2hpdGVzcGFjZSArIFwiKihbXlxcXFxdJ1xcXCJdKj8pXCIgKyB3aGl0ZXNwYWNlICsgXCIqXFxcXF1cIiwgXCJnXCIgKSxcblxuXHRycHNldWRvID0gbmV3IFJlZ0V4cCggcHNldWRvcyApLFxuXHRyaWRlbnRpZmllciA9IG5ldyBSZWdFeHAoIFwiXlwiICsgaWRlbnRpZmllciArIFwiJFwiICksXG5cblx0bWF0Y2hFeHByID0ge1xuXHRcdFwiSURcIjogbmV3IFJlZ0V4cCggXCJeIyhcIiArIGNoYXJhY3RlckVuY29kaW5nICsgXCIpXCIgKSxcblx0XHRcIkNMQVNTXCI6IG5ldyBSZWdFeHAoIFwiXlxcXFwuKFwiICsgY2hhcmFjdGVyRW5jb2RpbmcgKyBcIilcIiApLFxuXHRcdFwiVEFHXCI6IG5ldyBSZWdFeHAoIFwiXihcIiArIGNoYXJhY3RlckVuY29kaW5nLnJlcGxhY2UoIFwid1wiLCBcIncqXCIgKSArIFwiKVwiICksXG5cdFx0XCJBVFRSXCI6IG5ldyBSZWdFeHAoIFwiXlwiICsgYXR0cmlidXRlcyApLFxuXHRcdFwiUFNFVURPXCI6IG5ldyBSZWdFeHAoIFwiXlwiICsgcHNldWRvcyApLFxuXHRcdFwiQ0hJTERcIjogbmV3IFJlZ0V4cCggXCJeOihvbmx5fGZpcnN0fGxhc3R8bnRofG50aC1sYXN0KS0oY2hpbGR8b2YtdHlwZSkoPzpcXFxcKFwiICsgd2hpdGVzcGFjZSArXG5cdFx0XHRcIiooZXZlbnxvZGR8KChbKy1dfCkoXFxcXGQqKW58KVwiICsgd2hpdGVzcGFjZSArIFwiKig/OihbKy1dfClcIiArIHdoaXRlc3BhY2UgK1xuXHRcdFx0XCIqKFxcXFxkKyl8KSlcIiArIHdoaXRlc3BhY2UgKyBcIipcXFxcKXwpXCIsIFwiaVwiICksXG5cdFx0XCJib29sXCI6IG5ldyBSZWdFeHAoIFwiXig/OlwiICsgYm9vbGVhbnMgKyBcIikkXCIsIFwiaVwiICksXG5cdFx0Ly8gRm9yIHVzZSBpbiBsaWJyYXJpZXMgaW1wbGVtZW50aW5nIC5pcygpXG5cdFx0Ly8gV2UgdXNlIHRoaXMgZm9yIFBPUyBtYXRjaGluZyBpbiBgc2VsZWN0YFxuXHRcdFwibmVlZHNDb250ZXh0XCI6IG5ldyBSZWdFeHAoIFwiXlwiICsgd2hpdGVzcGFjZSArIFwiKls+K35dfDooZXZlbnxvZGR8ZXF8Z3R8bHR8bnRofGZpcnN0fGxhc3QpKD86XFxcXChcIiArXG5cdFx0XHR3aGl0ZXNwYWNlICsgXCIqKCg/Oi1cXFxcZCk/XFxcXGQqKVwiICsgd2hpdGVzcGFjZSArIFwiKlxcXFwpfCkoPz1bXi1dfCQpXCIsIFwiaVwiIClcblx0fSxcblxuXHRyaW5wdXRzID0gL14oPzppbnB1dHxzZWxlY3R8dGV4dGFyZWF8YnV0dG9uKSQvaSxcblx0cmhlYWRlciA9IC9eaFxcZCQvaSxcblxuXHRybmF0aXZlID0gL15bXntdK1xce1xccypcXFtuYXRpdmUgXFx3LyxcblxuXHQvLyBFYXNpbHktcGFyc2VhYmxlL3JldHJpZXZhYmxlIElEIG9yIFRBRyBvciBDTEFTUyBzZWxlY3RvcnNcblx0cnF1aWNrRXhwciA9IC9eKD86IyhbXFx3LV0rKXwoXFx3Kyl8XFwuKFtcXHctXSspKSQvLFxuXG5cdHJzaWJsaW5nID0gL1srfl0vLFxuXHRyZXNjYXBlID0gLyd8XFxcXC9nLFxuXG5cdC8vIENTUyBlc2NhcGVzIGh0dHA6Ly93d3cudzMub3JnL1RSL0NTUzIxL3N5bmRhdGEuaHRtbCNlc2NhcGVkLWNoYXJhY3RlcnNcblx0cnVuZXNjYXBlID0gbmV3IFJlZ0V4cCggXCJcXFxcXFxcXChbXFxcXGRhLWZdezEsNn1cIiArIHdoaXRlc3BhY2UgKyBcIj98KFwiICsgd2hpdGVzcGFjZSArIFwiKXwuKVwiLCBcImlnXCIgKSxcblx0ZnVuZXNjYXBlID0gZnVuY3Rpb24oIF8sIGVzY2FwZWQsIGVzY2FwZWRXaGl0ZXNwYWNlICkge1xuXHRcdHZhciBoaWdoID0gXCIweFwiICsgZXNjYXBlZCAtIDB4MTAwMDA7XG5cdFx0Ly8gTmFOIG1lYW5zIG5vbi1jb2RlcG9pbnRcblx0XHQvLyBTdXBwb3J0OiBGaXJlZm94PDI0XG5cdFx0Ly8gV29ya2Fyb3VuZCBlcnJvbmVvdXMgbnVtZXJpYyBpbnRlcnByZXRhdGlvbiBvZiArXCIweFwiXG5cdFx0cmV0dXJuIGhpZ2ggIT09IGhpZ2ggfHwgZXNjYXBlZFdoaXRlc3BhY2UgP1xuXHRcdFx0ZXNjYXBlZCA6XG5cdFx0XHRoaWdoIDwgMCA/XG5cdFx0XHRcdC8vIEJNUCBjb2RlcG9pbnRcblx0XHRcdFx0U3RyaW5nLmZyb21DaGFyQ29kZSggaGlnaCArIDB4MTAwMDAgKSA6XG5cdFx0XHRcdC8vIFN1cHBsZW1lbnRhbCBQbGFuZSBjb2RlcG9pbnQgKHN1cnJvZ2F0ZSBwYWlyKVxuXHRcdFx0XHRTdHJpbmcuZnJvbUNoYXJDb2RlKCBoaWdoID4+IDEwIHwgMHhEODAwLCBoaWdoICYgMHgzRkYgfCAweERDMDAgKTtcblx0fSxcblxuXHQvLyBVc2VkIGZvciBpZnJhbWVzXG5cdC8vIFNlZSBzZXREb2N1bWVudCgpXG5cdC8vIFJlbW92aW5nIHRoZSBmdW5jdGlvbiB3cmFwcGVyIGNhdXNlcyBhIFwiUGVybWlzc2lvbiBEZW5pZWRcIlxuXHQvLyBlcnJvciBpbiBJRVxuXHR1bmxvYWRIYW5kbGVyID0gZnVuY3Rpb24oKSB7XG5cdFx0c2V0RG9jdW1lbnQoKTtcblx0fTtcblxuLy8gT3B0aW1pemUgZm9yIHB1c2guYXBwbHkoIF8sIE5vZGVMaXN0IClcbnRyeSB7XG5cdHB1c2guYXBwbHkoXG5cdFx0KGFyciA9IHNsaWNlLmNhbGwoIHByZWZlcnJlZERvYy5jaGlsZE5vZGVzICkpLFxuXHRcdHByZWZlcnJlZERvYy5jaGlsZE5vZGVzXG5cdCk7XG5cdC8vIFN1cHBvcnQ6IEFuZHJvaWQ8NC4wXG5cdC8vIERldGVjdCBzaWxlbnRseSBmYWlsaW5nIHB1c2guYXBwbHlcblx0YXJyWyBwcmVmZXJyZWREb2MuY2hpbGROb2Rlcy5sZW5ndGggXS5ub2RlVHlwZTtcbn0gY2F0Y2ggKCBlICkge1xuXHRwdXNoID0geyBhcHBseTogYXJyLmxlbmd0aCA/XG5cblx0XHQvLyBMZXZlcmFnZSBzbGljZSBpZiBwb3NzaWJsZVxuXHRcdGZ1bmN0aW9uKCB0YXJnZXQsIGVscyApIHtcblx0XHRcdHB1c2hfbmF0aXZlLmFwcGx5KCB0YXJnZXQsIHNsaWNlLmNhbGwoZWxzKSApO1xuXHRcdH0gOlxuXG5cdFx0Ly8gU3VwcG9ydDogSUU8OVxuXHRcdC8vIE90aGVyd2lzZSBhcHBlbmQgZGlyZWN0bHlcblx0XHRmdW5jdGlvbiggdGFyZ2V0LCBlbHMgKSB7XG5cdFx0XHR2YXIgaiA9IHRhcmdldC5sZW5ndGgsXG5cdFx0XHRcdGkgPSAwO1xuXHRcdFx0Ly8gQ2FuJ3QgdHJ1c3QgTm9kZUxpc3QubGVuZ3RoXG5cdFx0XHR3aGlsZSAoICh0YXJnZXRbaisrXSA9IGVsc1tpKytdKSApIHt9XG5cdFx0XHR0YXJnZXQubGVuZ3RoID0gaiAtIDE7XG5cdFx0fVxuXHR9O1xufVxuXG5mdW5jdGlvbiBTaXp6bGUoIHNlbGVjdG9yLCBjb250ZXh0LCByZXN1bHRzLCBzZWVkICkge1xuXHR2YXIgbWF0Y2gsIGVsZW0sIG0sIG5vZGVUeXBlLFxuXHRcdC8vIFFTQSB2YXJzXG5cdFx0aSwgZ3JvdXBzLCBvbGQsIG5pZCwgbmV3Q29udGV4dCwgbmV3U2VsZWN0b3I7XG5cblx0aWYgKCAoIGNvbnRleHQgPyBjb250ZXh0Lm93bmVyRG9jdW1lbnQgfHwgY29udGV4dCA6IHByZWZlcnJlZERvYyApICE9PSBkb2N1bWVudCApIHtcblx0XHRzZXREb2N1bWVudCggY29udGV4dCApO1xuXHR9XG5cblx0Y29udGV4dCA9IGNvbnRleHQgfHwgZG9jdW1lbnQ7XG5cdHJlc3VsdHMgPSByZXN1bHRzIHx8IFtdO1xuXHRub2RlVHlwZSA9IGNvbnRleHQubm9kZVR5cGU7XG5cblx0aWYgKCB0eXBlb2Ygc2VsZWN0b3IgIT09IFwic3RyaW5nXCIgfHwgIXNlbGVjdG9yIHx8XG5cdFx0bm9kZVR5cGUgIT09IDEgJiYgbm9kZVR5cGUgIT09IDkgJiYgbm9kZVR5cGUgIT09IDExICkge1xuXG5cdFx0cmV0dXJuIHJlc3VsdHM7XG5cdH1cblxuXHRpZiAoICFzZWVkICYmIGRvY3VtZW50SXNIVE1MICkge1xuXG5cdFx0Ly8gVHJ5IHRvIHNob3J0Y3V0IGZpbmQgb3BlcmF0aW9ucyB3aGVuIHBvc3NpYmxlIChlLmcuLCBub3QgdW5kZXIgRG9jdW1lbnRGcmFnbWVudClcblx0XHRpZiAoIG5vZGVUeXBlICE9PSAxMSAmJiAobWF0Y2ggPSBycXVpY2tFeHByLmV4ZWMoIHNlbGVjdG9yICkpICkge1xuXHRcdFx0Ly8gU3BlZWQtdXA6IFNpenpsZShcIiNJRFwiKVxuXHRcdFx0aWYgKCAobSA9IG1hdGNoWzFdKSApIHtcblx0XHRcdFx0aWYgKCBub2RlVHlwZSA9PT0gOSApIHtcblx0XHRcdFx0XHRlbGVtID0gY29udGV4dC5nZXRFbGVtZW50QnlJZCggbSApO1xuXHRcdFx0XHRcdC8vIENoZWNrIHBhcmVudE5vZGUgdG8gY2F0Y2ggd2hlbiBCbGFja2JlcnJ5IDQuNiByZXR1cm5zXG5cdFx0XHRcdFx0Ly8gbm9kZXMgdGhhdCBhcmUgbm8gbG9uZ2VyIGluIHRoZSBkb2N1bWVudCAoalF1ZXJ5ICM2OTYzKVxuXHRcdFx0XHRcdGlmICggZWxlbSAmJiBlbGVtLnBhcmVudE5vZGUgKSB7XG5cdFx0XHRcdFx0XHQvLyBIYW5kbGUgdGhlIGNhc2Ugd2hlcmUgSUUsIE9wZXJhLCBhbmQgV2Via2l0IHJldHVybiBpdGVtc1xuXHRcdFx0XHRcdFx0Ly8gYnkgbmFtZSBpbnN0ZWFkIG9mIElEXG5cdFx0XHRcdFx0XHRpZiAoIGVsZW0uaWQgPT09IG0gKSB7XG5cdFx0XHRcdFx0XHRcdHJlc3VsdHMucHVzaCggZWxlbSApO1xuXHRcdFx0XHRcdFx0XHRyZXR1cm4gcmVzdWx0cztcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0cmV0dXJuIHJlc3VsdHM7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdC8vIENvbnRleHQgaXMgbm90IGEgZG9jdW1lbnRcblx0XHRcdFx0XHRpZiAoIGNvbnRleHQub3duZXJEb2N1bWVudCAmJiAoZWxlbSA9IGNvbnRleHQub3duZXJEb2N1bWVudC5nZXRFbGVtZW50QnlJZCggbSApKSAmJlxuXHRcdFx0XHRcdFx0Y29udGFpbnMoIGNvbnRleHQsIGVsZW0gKSAmJiBlbGVtLmlkID09PSBtICkge1xuXHRcdFx0XHRcdFx0cmVzdWx0cy5wdXNoKCBlbGVtICk7XG5cdFx0XHRcdFx0XHRyZXR1cm4gcmVzdWx0cztcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0Ly8gU3BlZWQtdXA6IFNpenpsZShcIlRBR1wiKVxuXHRcdFx0fSBlbHNlIGlmICggbWF0Y2hbMl0gKSB7XG5cdFx0XHRcdHB1c2guYXBwbHkoIHJlc3VsdHMsIGNvbnRleHQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoIHNlbGVjdG9yICkgKTtcblx0XHRcdFx0cmV0dXJuIHJlc3VsdHM7XG5cblx0XHRcdC8vIFNwZWVkLXVwOiBTaXp6bGUoXCIuQ0xBU1NcIilcblx0XHRcdH0gZWxzZSBpZiAoIChtID0gbWF0Y2hbM10pICYmIHN1cHBvcnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSApIHtcblx0XHRcdFx0cHVzaC5hcHBseSggcmVzdWx0cywgY29udGV4dC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCBtICkgKTtcblx0XHRcdFx0cmV0dXJuIHJlc3VsdHM7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gUVNBIHBhdGhcblx0XHRpZiAoIHN1cHBvcnQucXNhICYmICghcmJ1Z2d5UVNBIHx8ICFyYnVnZ3lRU0EudGVzdCggc2VsZWN0b3IgKSkgKSB7XG5cdFx0XHRuaWQgPSBvbGQgPSBleHBhbmRvO1xuXHRcdFx0bmV3Q29udGV4dCA9IGNvbnRleHQ7XG5cdFx0XHRuZXdTZWxlY3RvciA9IG5vZGVUeXBlICE9PSAxICYmIHNlbGVjdG9yO1xuXG5cdFx0XHQvLyBxU0Egd29ya3Mgc3RyYW5nZWx5IG9uIEVsZW1lbnQtcm9vdGVkIHF1ZXJpZXNcblx0XHRcdC8vIFdlIGNhbiB3b3JrIGFyb3VuZCB0aGlzIGJ5IHNwZWNpZnlpbmcgYW4gZXh0cmEgSUQgb24gdGhlIHJvb3Rcblx0XHRcdC8vIGFuZCB3b3JraW5nIHVwIGZyb20gdGhlcmUgKFRoYW5rcyB0byBBbmRyZXcgRHVwb250IGZvciB0aGUgdGVjaG5pcXVlKVxuXHRcdFx0Ly8gSUUgOCBkb2Vzbid0IHdvcmsgb24gb2JqZWN0IGVsZW1lbnRzXG5cdFx0XHRpZiAoIG5vZGVUeXBlID09PSAxICYmIGNvbnRleHQubm9kZU5hbWUudG9Mb3dlckNhc2UoKSAhPT0gXCJvYmplY3RcIiApIHtcblx0XHRcdFx0Z3JvdXBzID0gdG9rZW5pemUoIHNlbGVjdG9yICk7XG5cblx0XHRcdFx0aWYgKCAob2xkID0gY29udGV4dC5nZXRBdHRyaWJ1dGUoXCJpZFwiKSkgKSB7XG5cdFx0XHRcdFx0bmlkID0gb2xkLnJlcGxhY2UoIHJlc2NhcGUsIFwiXFxcXCQmXCIgKTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRjb250ZXh0LnNldEF0dHJpYnV0ZSggXCJpZFwiLCBuaWQgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRuaWQgPSBcIltpZD0nXCIgKyBuaWQgKyBcIiddIFwiO1xuXG5cdFx0XHRcdGkgPSBncm91cHMubGVuZ3RoO1xuXHRcdFx0XHR3aGlsZSAoIGktLSApIHtcblx0XHRcdFx0XHRncm91cHNbaV0gPSBuaWQgKyB0b1NlbGVjdG9yKCBncm91cHNbaV0gKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRuZXdDb250ZXh0ID0gcnNpYmxpbmcudGVzdCggc2VsZWN0b3IgKSAmJiB0ZXN0Q29udGV4dCggY29udGV4dC5wYXJlbnROb2RlICkgfHwgY29udGV4dDtcblx0XHRcdFx0bmV3U2VsZWN0b3IgPSBncm91cHMuam9pbihcIixcIik7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggbmV3U2VsZWN0b3IgKSB7XG5cdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0cHVzaC5hcHBseSggcmVzdWx0cyxcblx0XHRcdFx0XHRcdG5ld0NvbnRleHQucXVlcnlTZWxlY3RvckFsbCggbmV3U2VsZWN0b3IgKVxuXHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0cmV0dXJuIHJlc3VsdHM7XG5cdFx0XHRcdH0gY2F0Y2gocXNhRXJyb3IpIHtcblx0XHRcdFx0fSBmaW5hbGx5IHtcblx0XHRcdFx0XHRpZiAoICFvbGQgKSB7XG5cdFx0XHRcdFx0XHRjb250ZXh0LnJlbW92ZUF0dHJpYnV0ZShcImlkXCIpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC8vIEFsbCBvdGhlcnNcblx0cmV0dXJuIHNlbGVjdCggc2VsZWN0b3IucmVwbGFjZSggcnRyaW0sIFwiJDFcIiApLCBjb250ZXh0LCByZXN1bHRzLCBzZWVkICk7XG59XG5cbi8qKlxuICogQ3JlYXRlIGtleS12YWx1ZSBjYWNoZXMgb2YgbGltaXRlZCBzaXplXG4gKiBAcmV0dXJucyB7RnVuY3Rpb24oc3RyaW5nLCBPYmplY3QpfSBSZXR1cm5zIHRoZSBPYmplY3QgZGF0YSBhZnRlciBzdG9yaW5nIGl0IG9uIGl0c2VsZiB3aXRoXG4gKlx0cHJvcGVydHkgbmFtZSB0aGUgKHNwYWNlLXN1ZmZpeGVkKSBzdHJpbmcgYW5kIChpZiB0aGUgY2FjaGUgaXMgbGFyZ2VyIHRoYW4gRXhwci5jYWNoZUxlbmd0aClcbiAqXHRkZWxldGluZyB0aGUgb2xkZXN0IGVudHJ5XG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUNhY2hlKCkge1xuXHR2YXIga2V5cyA9IFtdO1xuXG5cdGZ1bmN0aW9uIGNhY2hlKCBrZXksIHZhbHVlICkge1xuXHRcdC8vIFVzZSAoa2V5ICsgXCIgXCIpIHRvIGF2b2lkIGNvbGxpc2lvbiB3aXRoIG5hdGl2ZSBwcm90b3R5cGUgcHJvcGVydGllcyAoc2VlIElzc3VlICMxNTcpXG5cdFx0aWYgKCBrZXlzLnB1c2goIGtleSArIFwiIFwiICkgPiBFeHByLmNhY2hlTGVuZ3RoICkge1xuXHRcdFx0Ly8gT25seSBrZWVwIHRoZSBtb3N0IHJlY2VudCBlbnRyaWVzXG5cdFx0XHRkZWxldGUgY2FjaGVbIGtleXMuc2hpZnQoKSBdO1xuXHRcdH1cblx0XHRyZXR1cm4gKGNhY2hlWyBrZXkgKyBcIiBcIiBdID0gdmFsdWUpO1xuXHR9XG5cdHJldHVybiBjYWNoZTtcbn1cblxuLyoqXG4gKiBNYXJrIGEgZnVuY3Rpb24gZm9yIHNwZWNpYWwgdXNlIGJ5IFNpenpsZVxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGZ1bmN0aW9uIHRvIG1hcmtcbiAqL1xuZnVuY3Rpb24gbWFya0Z1bmN0aW9uKCBmbiApIHtcblx0Zm5bIGV4cGFuZG8gXSA9IHRydWU7XG5cdHJldHVybiBmbjtcbn1cblxuLyoqXG4gKiBTdXBwb3J0IHRlc3RpbmcgdXNpbmcgYW4gZWxlbWVudFxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gUGFzc2VkIHRoZSBjcmVhdGVkIGRpdiBhbmQgZXhwZWN0cyBhIGJvb2xlYW4gcmVzdWx0XG4gKi9cbmZ1bmN0aW9uIGFzc2VydCggZm4gKSB7XG5cdHZhciBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuXG5cdHRyeSB7XG5cdFx0cmV0dXJuICEhZm4oIGRpdiApO1xuXHR9IGNhdGNoIChlKSB7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9IGZpbmFsbHkge1xuXHRcdC8vIFJlbW92ZSBmcm9tIGl0cyBwYXJlbnQgYnkgZGVmYXVsdFxuXHRcdGlmICggZGl2LnBhcmVudE5vZGUgKSB7XG5cdFx0XHRkaXYucGFyZW50Tm9kZS5yZW1vdmVDaGlsZCggZGl2ICk7XG5cdFx0fVxuXHRcdC8vIHJlbGVhc2UgbWVtb3J5IGluIElFXG5cdFx0ZGl2ID0gbnVsbDtcblx0fVxufVxuXG4vKipcbiAqIEFkZHMgdGhlIHNhbWUgaGFuZGxlciBmb3IgYWxsIG9mIHRoZSBzcGVjaWZpZWQgYXR0cnNcbiAqIEBwYXJhbSB7U3RyaW5nfSBhdHRycyBQaXBlLXNlcGFyYXRlZCBsaXN0IG9mIGF0dHJpYnV0ZXNcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGhhbmRsZXIgVGhlIG1ldGhvZCB0aGF0IHdpbGwgYmUgYXBwbGllZFxuICovXG5mdW5jdGlvbiBhZGRIYW5kbGUoIGF0dHJzLCBoYW5kbGVyICkge1xuXHR2YXIgYXJyID0gYXR0cnMuc3BsaXQoXCJ8XCIpLFxuXHRcdGkgPSBhdHRycy5sZW5ndGg7XG5cblx0d2hpbGUgKCBpLS0gKSB7XG5cdFx0RXhwci5hdHRySGFuZGxlWyBhcnJbaV0gXSA9IGhhbmRsZXI7XG5cdH1cbn1cblxuLyoqXG4gKiBDaGVja3MgZG9jdW1lbnQgb3JkZXIgb2YgdHdvIHNpYmxpbmdzXG4gKiBAcGFyYW0ge0VsZW1lbnR9IGFcbiAqIEBwYXJhbSB7RWxlbWVudH0gYlxuICogQHJldHVybnMge051bWJlcn0gUmV0dXJucyBsZXNzIHRoYW4gMCBpZiBhIHByZWNlZGVzIGIsIGdyZWF0ZXIgdGhhbiAwIGlmIGEgZm9sbG93cyBiXG4gKi9cbmZ1bmN0aW9uIHNpYmxpbmdDaGVjayggYSwgYiApIHtcblx0dmFyIGN1ciA9IGIgJiYgYSxcblx0XHRkaWZmID0gY3VyICYmIGEubm9kZVR5cGUgPT09IDEgJiYgYi5ub2RlVHlwZSA9PT0gMSAmJlxuXHRcdFx0KCB+Yi5zb3VyY2VJbmRleCB8fCBNQVhfTkVHQVRJVkUgKSAtXG5cdFx0XHQoIH5hLnNvdXJjZUluZGV4IHx8IE1BWF9ORUdBVElWRSApO1xuXG5cdC8vIFVzZSBJRSBzb3VyY2VJbmRleCBpZiBhdmFpbGFibGUgb24gYm90aCBub2Rlc1xuXHRpZiAoIGRpZmYgKSB7XG5cdFx0cmV0dXJuIGRpZmY7XG5cdH1cblxuXHQvLyBDaGVjayBpZiBiIGZvbGxvd3MgYVxuXHRpZiAoIGN1ciApIHtcblx0XHR3aGlsZSAoIChjdXIgPSBjdXIubmV4dFNpYmxpbmcpICkge1xuXHRcdFx0aWYgKCBjdXIgPT09IGIgKSB7XG5cdFx0XHRcdHJldHVybiAtMTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gYSA/IDEgOiAtMTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGEgZnVuY3Rpb24gdG8gdXNlIGluIHBzZXVkb3MgZm9yIGlucHV0IHR5cGVzXG4gKiBAcGFyYW0ge1N0cmluZ30gdHlwZVxuICovXG5mdW5jdGlvbiBjcmVhdGVJbnB1dFBzZXVkbyggdHlwZSApIHtcblx0cmV0dXJuIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdHZhciBuYW1lID0gZWxlbS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO1xuXHRcdHJldHVybiBuYW1lID09PSBcImlucHV0XCIgJiYgZWxlbS50eXBlID09PSB0eXBlO1xuXHR9O1xufVxuXG4vKipcbiAqIFJldHVybnMgYSBmdW5jdGlvbiB0byB1c2UgaW4gcHNldWRvcyBmb3IgYnV0dG9uc1xuICogQHBhcmFtIHtTdHJpbmd9IHR5cGVcbiAqL1xuZnVuY3Rpb24gY3JlYXRlQnV0dG9uUHNldWRvKCB0eXBlICkge1xuXHRyZXR1cm4gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0dmFyIG5hbWUgPSBlbGVtLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7XG5cdFx0cmV0dXJuIChuYW1lID09PSBcImlucHV0XCIgfHwgbmFtZSA9PT0gXCJidXR0b25cIikgJiYgZWxlbS50eXBlID09PSB0eXBlO1xuXHR9O1xufVxuXG4vKipcbiAqIFJldHVybnMgYSBmdW5jdGlvbiB0byB1c2UgaW4gcHNldWRvcyBmb3IgcG9zaXRpb25hbHNcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZVBvc2l0aW9uYWxQc2V1ZG8oIGZuICkge1xuXHRyZXR1cm4gbWFya0Z1bmN0aW9uKGZ1bmN0aW9uKCBhcmd1bWVudCApIHtcblx0XHRhcmd1bWVudCA9ICthcmd1bWVudDtcblx0XHRyZXR1cm4gbWFya0Z1bmN0aW9uKGZ1bmN0aW9uKCBzZWVkLCBtYXRjaGVzICkge1xuXHRcdFx0dmFyIGosXG5cdFx0XHRcdG1hdGNoSW5kZXhlcyA9IGZuKCBbXSwgc2VlZC5sZW5ndGgsIGFyZ3VtZW50ICksXG5cdFx0XHRcdGkgPSBtYXRjaEluZGV4ZXMubGVuZ3RoO1xuXG5cdFx0XHQvLyBNYXRjaCBlbGVtZW50cyBmb3VuZCBhdCB0aGUgc3BlY2lmaWVkIGluZGV4ZXNcblx0XHRcdHdoaWxlICggaS0tICkge1xuXHRcdFx0XHRpZiAoIHNlZWRbIChqID0gbWF0Y2hJbmRleGVzW2ldKSBdICkge1xuXHRcdFx0XHRcdHNlZWRbal0gPSAhKG1hdGNoZXNbal0gPSBzZWVkW2pdKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0pO1xuXHR9KTtcbn1cblxuLyoqXG4gKiBDaGVja3MgYSBub2RlIGZvciB2YWxpZGl0eSBhcyBhIFNpenpsZSBjb250ZXh0XG4gKiBAcGFyYW0ge0VsZW1lbnR8T2JqZWN0PX0gY29udGV4dFxuICogQHJldHVybnMge0VsZW1lbnR8T2JqZWN0fEJvb2xlYW59IFRoZSBpbnB1dCBub2RlIGlmIGFjY2VwdGFibGUsIG90aGVyd2lzZSBhIGZhbHN5IHZhbHVlXG4gKi9cbmZ1bmN0aW9uIHRlc3RDb250ZXh0KCBjb250ZXh0ICkge1xuXHRyZXR1cm4gY29udGV4dCAmJiB0eXBlb2YgY29udGV4dC5nZXRFbGVtZW50c0J5VGFnTmFtZSAhPT0gXCJ1bmRlZmluZWRcIiAmJiBjb250ZXh0O1xufVxuXG4vLyBFeHBvc2Ugc3VwcG9ydCB2YXJzIGZvciBjb252ZW5pZW5jZVxuc3VwcG9ydCA9IFNpenpsZS5zdXBwb3J0ID0ge307XG5cbi8qKlxuICogRGV0ZWN0cyBYTUwgbm9kZXNcbiAqIEBwYXJhbSB7RWxlbWVudHxPYmplY3R9IGVsZW0gQW4gZWxlbWVudCBvciBhIGRvY3VtZW50XG4gKiBAcmV0dXJucyB7Qm9vbGVhbn0gVHJ1ZSBpZmYgZWxlbSBpcyBhIG5vbi1IVE1MIFhNTCBub2RlXG4gKi9cbmlzWE1MID0gU2l6emxlLmlzWE1MID0gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdC8vIGRvY3VtZW50RWxlbWVudCBpcyB2ZXJpZmllZCBmb3IgY2FzZXMgd2hlcmUgaXQgZG9lc24ndCB5ZXQgZXhpc3Rcblx0Ly8gKHN1Y2ggYXMgbG9hZGluZyBpZnJhbWVzIGluIElFIC0gIzQ4MzMpXG5cdHZhciBkb2N1bWVudEVsZW1lbnQgPSBlbGVtICYmIChlbGVtLm93bmVyRG9jdW1lbnQgfHwgZWxlbSkuZG9jdW1lbnRFbGVtZW50O1xuXHRyZXR1cm4gZG9jdW1lbnRFbGVtZW50ID8gZG9jdW1lbnRFbGVtZW50Lm5vZGVOYW1lICE9PSBcIkhUTUxcIiA6IGZhbHNlO1xufTtcblxuLyoqXG4gKiBTZXRzIGRvY3VtZW50LXJlbGF0ZWQgdmFyaWFibGVzIG9uY2UgYmFzZWQgb24gdGhlIGN1cnJlbnQgZG9jdW1lbnRcbiAqIEBwYXJhbSB7RWxlbWVudHxPYmplY3R9IFtkb2NdIEFuIGVsZW1lbnQgb3IgZG9jdW1lbnQgb2JqZWN0IHRvIHVzZSB0byBzZXQgdGhlIGRvY3VtZW50XG4gKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIHRoZSBjdXJyZW50IGRvY3VtZW50XG4gKi9cbnNldERvY3VtZW50ID0gU2l6emxlLnNldERvY3VtZW50ID0gZnVuY3Rpb24oIG5vZGUgKSB7XG5cdHZhciBoYXNDb21wYXJlLCBwYXJlbnQsXG5cdFx0ZG9jID0gbm9kZSA/IG5vZGUub3duZXJEb2N1bWVudCB8fCBub2RlIDogcHJlZmVycmVkRG9jO1xuXG5cdC8vIElmIG5vIGRvY3VtZW50IGFuZCBkb2N1bWVudEVsZW1lbnQgaXMgYXZhaWxhYmxlLCByZXR1cm5cblx0aWYgKCBkb2MgPT09IGRvY3VtZW50IHx8IGRvYy5ub2RlVHlwZSAhPT0gOSB8fCAhZG9jLmRvY3VtZW50RWxlbWVudCApIHtcblx0XHRyZXR1cm4gZG9jdW1lbnQ7XG5cdH1cblxuXHQvLyBTZXQgb3VyIGRvY3VtZW50XG5cdGRvY3VtZW50ID0gZG9jO1xuXHRkb2NFbGVtID0gZG9jLmRvY3VtZW50RWxlbWVudDtcblx0cGFyZW50ID0gZG9jLmRlZmF1bHRWaWV3O1xuXG5cdC8vIFN1cHBvcnQ6IElFPjhcblx0Ly8gSWYgaWZyYW1lIGRvY3VtZW50IGlzIGFzc2lnbmVkIHRvIFwiZG9jdW1lbnRcIiB2YXJpYWJsZSBhbmQgaWYgaWZyYW1lIGhhcyBiZWVuIHJlbG9hZGVkLFxuXHQvLyBJRSB3aWxsIHRocm93IFwicGVybWlzc2lvbiBkZW5pZWRcIiBlcnJvciB3aGVuIGFjY2Vzc2luZyBcImRvY3VtZW50XCIgdmFyaWFibGUsIHNlZSBqUXVlcnkgIzEzOTM2XG5cdC8vIElFNi04IGRvIG5vdCBzdXBwb3J0IHRoZSBkZWZhdWx0VmlldyBwcm9wZXJ0eSBzbyBwYXJlbnQgd2lsbCBiZSB1bmRlZmluZWRcblx0aWYgKCBwYXJlbnQgJiYgcGFyZW50ICE9PSBwYXJlbnQudG9wICkge1xuXHRcdC8vIElFMTEgZG9lcyBub3QgaGF2ZSBhdHRhY2hFdmVudCwgc28gYWxsIG11c3Qgc3VmZmVyXG5cdFx0aWYgKCBwYXJlbnQuYWRkRXZlbnRMaXN0ZW5lciApIHtcblx0XHRcdHBhcmVudC5hZGRFdmVudExpc3RlbmVyKCBcInVubG9hZFwiLCB1bmxvYWRIYW5kbGVyLCBmYWxzZSApO1xuXHRcdH0gZWxzZSBpZiAoIHBhcmVudC5hdHRhY2hFdmVudCApIHtcblx0XHRcdHBhcmVudC5hdHRhY2hFdmVudCggXCJvbnVubG9hZFwiLCB1bmxvYWRIYW5kbGVyICk7XG5cdFx0fVxuXHR9XG5cblx0LyogU3VwcG9ydCB0ZXN0c1xuXHQtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG5cdGRvY3VtZW50SXNIVE1MID0gIWlzWE1MKCBkb2MgKTtcblxuXHQvKiBBdHRyaWJ1dGVzXG5cdC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cblxuXHQvLyBTdXBwb3J0OiBJRTw4XG5cdC8vIFZlcmlmeSB0aGF0IGdldEF0dHJpYnV0ZSByZWFsbHkgcmV0dXJucyBhdHRyaWJ1dGVzIGFuZCBub3QgcHJvcGVydGllc1xuXHQvLyAoZXhjZXB0aW5nIElFOCBib29sZWFucylcblx0c3VwcG9ydC5hdHRyaWJ1dGVzID0gYXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdFx0ZGl2LmNsYXNzTmFtZSA9IFwiaVwiO1xuXHRcdHJldHVybiAhZGl2LmdldEF0dHJpYnV0ZShcImNsYXNzTmFtZVwiKTtcblx0fSk7XG5cblx0LyogZ2V0RWxlbWVudChzKUJ5KlxuXHQtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG5cblx0Ly8gQ2hlY2sgaWYgZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCIqXCIpIHJldHVybnMgb25seSBlbGVtZW50c1xuXHRzdXBwb3J0LmdldEVsZW1lbnRzQnlUYWdOYW1lID0gYXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdFx0ZGl2LmFwcGVuZENoaWxkKCBkb2MuY3JlYXRlQ29tbWVudChcIlwiKSApO1xuXHRcdHJldHVybiAhZGl2LmdldEVsZW1lbnRzQnlUYWdOYW1lKFwiKlwiKS5sZW5ndGg7XG5cdH0pO1xuXG5cdC8vIFN1cHBvcnQ6IElFPDlcblx0c3VwcG9ydC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lID0gcm5hdGl2ZS50ZXN0KCBkb2MuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSApO1xuXG5cdC8vIFN1cHBvcnQ6IElFPDEwXG5cdC8vIENoZWNrIGlmIGdldEVsZW1lbnRCeUlkIHJldHVybnMgZWxlbWVudHMgYnkgbmFtZVxuXHQvLyBUaGUgYnJva2VuIGdldEVsZW1lbnRCeUlkIG1ldGhvZHMgZG9uJ3QgcGljayB1cCBwcm9ncmFtYXRpY2FsbHktc2V0IG5hbWVzLFxuXHQvLyBzbyB1c2UgYSByb3VuZGFib3V0IGdldEVsZW1lbnRzQnlOYW1lIHRlc3Rcblx0c3VwcG9ydC5nZXRCeUlkID0gYXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdFx0ZG9jRWxlbS5hcHBlbmRDaGlsZCggZGl2ICkuaWQgPSBleHBhbmRvO1xuXHRcdHJldHVybiAhZG9jLmdldEVsZW1lbnRzQnlOYW1lIHx8ICFkb2MuZ2V0RWxlbWVudHNCeU5hbWUoIGV4cGFuZG8gKS5sZW5ndGg7XG5cdH0pO1xuXG5cdC8vIElEIGZpbmQgYW5kIGZpbHRlclxuXHRpZiAoIHN1cHBvcnQuZ2V0QnlJZCApIHtcblx0XHRFeHByLmZpbmRbXCJJRFwiXSA9IGZ1bmN0aW9uKCBpZCwgY29udGV4dCApIHtcblx0XHRcdGlmICggdHlwZW9mIGNvbnRleHQuZ2V0RWxlbWVudEJ5SWQgIT09IFwidW5kZWZpbmVkXCIgJiYgZG9jdW1lbnRJc0hUTUwgKSB7XG5cdFx0XHRcdHZhciBtID0gY29udGV4dC5nZXRFbGVtZW50QnlJZCggaWQgKTtcblx0XHRcdFx0Ly8gQ2hlY2sgcGFyZW50Tm9kZSB0byBjYXRjaCB3aGVuIEJsYWNrYmVycnkgNC42IHJldHVybnNcblx0XHRcdFx0Ly8gbm9kZXMgdGhhdCBhcmUgbm8gbG9uZ2VyIGluIHRoZSBkb2N1bWVudCAjNjk2M1xuXHRcdFx0XHRyZXR1cm4gbSAmJiBtLnBhcmVudE5vZGUgPyBbIG0gXSA6IFtdO1xuXHRcdFx0fVxuXHRcdH07XG5cdFx0RXhwci5maWx0ZXJbXCJJRFwiXSA9IGZ1bmN0aW9uKCBpZCApIHtcblx0XHRcdHZhciBhdHRySWQgPSBpZC5yZXBsYWNlKCBydW5lc2NhcGUsIGZ1bmVzY2FwZSApO1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0XHRyZXR1cm4gZWxlbS5nZXRBdHRyaWJ1dGUoXCJpZFwiKSA9PT0gYXR0cklkO1xuXHRcdFx0fTtcblx0XHR9O1xuXHR9IGVsc2Uge1xuXHRcdC8vIFN1cHBvcnQ6IElFNi83XG5cdFx0Ly8gZ2V0RWxlbWVudEJ5SWQgaXMgbm90IHJlbGlhYmxlIGFzIGEgZmluZCBzaG9ydGN1dFxuXHRcdGRlbGV0ZSBFeHByLmZpbmRbXCJJRFwiXTtcblxuXHRcdEV4cHIuZmlsdGVyW1wiSURcIl0gPSAgZnVuY3Rpb24oIGlkICkge1xuXHRcdFx0dmFyIGF0dHJJZCA9IGlkLnJlcGxhY2UoIHJ1bmVzY2FwZSwgZnVuZXNjYXBlICk7XG5cdFx0XHRyZXR1cm4gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRcdHZhciBub2RlID0gdHlwZW9mIGVsZW0uZ2V0QXR0cmlidXRlTm9kZSAhPT0gXCJ1bmRlZmluZWRcIiAmJiBlbGVtLmdldEF0dHJpYnV0ZU5vZGUoXCJpZFwiKTtcblx0XHRcdFx0cmV0dXJuIG5vZGUgJiYgbm9kZS52YWx1ZSA9PT0gYXR0cklkO1xuXHRcdFx0fTtcblx0XHR9O1xuXHR9XG5cblx0Ly8gVGFnXG5cdEV4cHIuZmluZFtcIlRBR1wiXSA9IHN1cHBvcnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUgP1xuXHRcdGZ1bmN0aW9uKCB0YWcsIGNvbnRleHQgKSB7XG5cdFx0XHRpZiAoIHR5cGVvZiBjb250ZXh0LmdldEVsZW1lbnRzQnlUYWdOYW1lICE9PSBcInVuZGVmaW5lZFwiICkge1xuXHRcdFx0XHRyZXR1cm4gY29udGV4dC5nZXRFbGVtZW50c0J5VGFnTmFtZSggdGFnICk7XG5cblx0XHRcdC8vIERvY3VtZW50RnJhZ21lbnQgbm9kZXMgZG9uJ3QgaGF2ZSBnRUJUTlxuXHRcdFx0fSBlbHNlIGlmICggc3VwcG9ydC5xc2EgKSB7XG5cdFx0XHRcdHJldHVybiBjb250ZXh0LnF1ZXJ5U2VsZWN0b3JBbGwoIHRhZyApO1xuXHRcdFx0fVxuXHRcdH0gOlxuXG5cdFx0ZnVuY3Rpb24oIHRhZywgY29udGV4dCApIHtcblx0XHRcdHZhciBlbGVtLFxuXHRcdFx0XHR0bXAgPSBbXSxcblx0XHRcdFx0aSA9IDAsXG5cdFx0XHRcdC8vIEJ5IGhhcHB5IGNvaW5jaWRlbmNlLCBhIChicm9rZW4pIGdFQlROIGFwcGVhcnMgb24gRG9jdW1lbnRGcmFnbWVudCBub2RlcyB0b29cblx0XHRcdFx0cmVzdWx0cyA9IGNvbnRleHQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoIHRhZyApO1xuXG5cdFx0XHQvLyBGaWx0ZXIgb3V0IHBvc3NpYmxlIGNvbW1lbnRzXG5cdFx0XHRpZiAoIHRhZyA9PT0gXCIqXCIgKSB7XG5cdFx0XHRcdHdoaWxlICggKGVsZW0gPSByZXN1bHRzW2krK10pICkge1xuXHRcdFx0XHRcdGlmICggZWxlbS5ub2RlVHlwZSA9PT0gMSApIHtcblx0XHRcdFx0XHRcdHRtcC5wdXNoKCBlbGVtICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIHRtcDtcblx0XHRcdH1cblx0XHRcdHJldHVybiByZXN1bHRzO1xuXHRcdH07XG5cblx0Ly8gQ2xhc3Ncblx0RXhwci5maW5kW1wiQ0xBU1NcIl0gPSBzdXBwb3J0LmdldEVsZW1lbnRzQnlDbGFzc05hbWUgJiYgZnVuY3Rpb24oIGNsYXNzTmFtZSwgY29udGV4dCApIHtcblx0XHRpZiAoIGRvY3VtZW50SXNIVE1MICkge1xuXHRcdFx0cmV0dXJuIGNvbnRleHQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSggY2xhc3NOYW1lICk7XG5cdFx0fVxuXHR9O1xuXG5cdC8qIFFTQS9tYXRjaGVzU2VsZWN0b3Jcblx0LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG5cdC8vIFFTQSBhbmQgbWF0Y2hlc1NlbGVjdG9yIHN1cHBvcnRcblxuXHQvLyBtYXRjaGVzU2VsZWN0b3IoOmFjdGl2ZSkgcmVwb3J0cyBmYWxzZSB3aGVuIHRydWUgKElFOS9PcGVyYSAxMS41KVxuXHRyYnVnZ3lNYXRjaGVzID0gW107XG5cblx0Ly8gcVNhKDpmb2N1cykgcmVwb3J0cyBmYWxzZSB3aGVuIHRydWUgKENocm9tZSAyMSlcblx0Ly8gV2UgYWxsb3cgdGhpcyBiZWNhdXNlIG9mIGEgYnVnIGluIElFOC85IHRoYXQgdGhyb3dzIGFuIGVycm9yXG5cdC8vIHdoZW5ldmVyIGBkb2N1bWVudC5hY3RpdmVFbGVtZW50YCBpcyBhY2Nlc3NlZCBvbiBhbiBpZnJhbWVcblx0Ly8gU28sIHdlIGFsbG93IDpmb2N1cyB0byBwYXNzIHRocm91Z2ggUVNBIGFsbCB0aGUgdGltZSB0byBhdm9pZCB0aGUgSUUgZXJyb3Jcblx0Ly8gU2VlIGh0dHA6Ly9idWdzLmpxdWVyeS5jb20vdGlja2V0LzEzMzc4XG5cdHJidWdneVFTQSA9IFtdO1xuXG5cdGlmICggKHN1cHBvcnQucXNhID0gcm5hdGl2ZS50ZXN0KCBkb2MucXVlcnlTZWxlY3RvckFsbCApKSApIHtcblx0XHQvLyBCdWlsZCBRU0EgcmVnZXhcblx0XHQvLyBSZWdleCBzdHJhdGVneSBhZG9wdGVkIGZyb20gRGllZ28gUGVyaW5pXG5cdFx0YXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdFx0XHQvLyBTZWxlY3QgaXMgc2V0IHRvIGVtcHR5IHN0cmluZyBvbiBwdXJwb3NlXG5cdFx0XHQvLyBUaGlzIGlzIHRvIHRlc3QgSUUncyB0cmVhdG1lbnQgb2Ygbm90IGV4cGxpY2l0bHlcblx0XHRcdC8vIHNldHRpbmcgYSBib29sZWFuIGNvbnRlbnQgYXR0cmlidXRlLFxuXHRcdFx0Ly8gc2luY2UgaXRzIHByZXNlbmNlIHNob3VsZCBiZSBlbm91Z2hcblx0XHRcdC8vIGh0dHA6Ly9idWdzLmpxdWVyeS5jb20vdGlja2V0LzEyMzU5XG5cdFx0XHRkb2NFbGVtLmFwcGVuZENoaWxkKCBkaXYgKS5pbm5lckhUTUwgPSBcIjxhIGlkPSdcIiArIGV4cGFuZG8gKyBcIic+PC9hPlwiICtcblx0XHRcdFx0XCI8c2VsZWN0IGlkPSdcIiArIGV4cGFuZG8gKyBcIi1cXGZdJyBtc2FsbG93Y2FwdHVyZT0nJz5cIiArXG5cdFx0XHRcdFwiPG9wdGlvbiBzZWxlY3RlZD0nJz48L29wdGlvbj48L3NlbGVjdD5cIjtcblxuXHRcdFx0Ly8gU3VwcG9ydDogSUU4LCBPcGVyYSAxMS0xMi4xNlxuXHRcdFx0Ly8gTm90aGluZyBzaG91bGQgYmUgc2VsZWN0ZWQgd2hlbiBlbXB0eSBzdHJpbmdzIGZvbGxvdyBePSBvciAkPSBvciAqPVxuXHRcdFx0Ly8gVGhlIHRlc3QgYXR0cmlidXRlIG11c3QgYmUgdW5rbm93biBpbiBPcGVyYSBidXQgXCJzYWZlXCIgZm9yIFdpblJUXG5cdFx0XHQvLyBodHRwOi8vbXNkbi5taWNyb3NvZnQuY29tL2VuLXVzL2xpYnJhcnkvaWUvaGg0NjUzODguYXNweCNhdHRyaWJ1dGVfc2VjdGlvblxuXHRcdFx0aWYgKCBkaXYucXVlcnlTZWxlY3RvckFsbChcIlttc2FsbG93Y2FwdHVyZV49JyddXCIpLmxlbmd0aCApIHtcblx0XHRcdFx0cmJ1Z2d5UVNBLnB1c2goIFwiWypeJF09XCIgKyB3aGl0ZXNwYWNlICsgXCIqKD86Jyd8XFxcIlxcXCIpXCIgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gU3VwcG9ydDogSUU4XG5cdFx0XHQvLyBCb29sZWFuIGF0dHJpYnV0ZXMgYW5kIFwidmFsdWVcIiBhcmUgbm90IHRyZWF0ZWQgY29ycmVjdGx5XG5cdFx0XHRpZiAoICFkaXYucXVlcnlTZWxlY3RvckFsbChcIltzZWxlY3RlZF1cIikubGVuZ3RoICkge1xuXHRcdFx0XHRyYnVnZ3lRU0EucHVzaCggXCJcXFxcW1wiICsgd2hpdGVzcGFjZSArIFwiKig/OnZhbHVlfFwiICsgYm9vbGVhbnMgKyBcIilcIiApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTdXBwb3J0OiBDaHJvbWU8MjksIEFuZHJvaWQ8NC4yKywgU2FmYXJpPDcuMCssIGlPUzw3LjArLCBQaGFudG9tSlM8MS45LjcrXG5cdFx0XHRpZiAoICFkaXYucXVlcnlTZWxlY3RvckFsbCggXCJbaWR+PVwiICsgZXhwYW5kbyArIFwiLV1cIiApLmxlbmd0aCApIHtcblx0XHRcdFx0cmJ1Z2d5UVNBLnB1c2goXCJ+PVwiKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gV2Via2l0L09wZXJhIC0gOmNoZWNrZWQgc2hvdWxkIHJldHVybiBzZWxlY3RlZCBvcHRpb24gZWxlbWVudHNcblx0XHRcdC8vIGh0dHA6Ly93d3cudzMub3JnL1RSLzIwMTEvUkVDLWNzczMtc2VsZWN0b3JzLTIwMTEwOTI5LyNjaGVja2VkXG5cdFx0XHQvLyBJRTggdGhyb3dzIGVycm9yIGhlcmUgYW5kIHdpbGwgbm90IHNlZSBsYXRlciB0ZXN0c1xuXHRcdFx0aWYgKCAhZGl2LnF1ZXJ5U2VsZWN0b3JBbGwoXCI6Y2hlY2tlZFwiKS5sZW5ndGggKSB7XG5cdFx0XHRcdHJidWdneVFTQS5wdXNoKFwiOmNoZWNrZWRcIik7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFN1cHBvcnQ6IFNhZmFyaSA4KywgaU9TIDgrXG5cdFx0XHQvLyBodHRwczovL2J1Z3Mud2Via2l0Lm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTM2ODUxXG5cdFx0XHQvLyBJbi1wYWdlIGBzZWxlY3RvciNpZCBzaWJpbmctY29tYmluYXRvciBzZWxlY3RvcmAgZmFpbHNcblx0XHRcdGlmICggIWRpdi5xdWVyeVNlbGVjdG9yQWxsKCBcImEjXCIgKyBleHBhbmRvICsgXCIrKlwiICkubGVuZ3RoICkge1xuXHRcdFx0XHRyYnVnZ3lRU0EucHVzaChcIi4jLitbK35dXCIpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXG5cdFx0YXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdFx0XHQvLyBTdXBwb3J0OiBXaW5kb3dzIDggTmF0aXZlIEFwcHNcblx0XHRcdC8vIFRoZSB0eXBlIGFuZCBuYW1lIGF0dHJpYnV0ZXMgYXJlIHJlc3RyaWN0ZWQgZHVyaW5nIC5pbm5lckhUTUwgYXNzaWdubWVudFxuXHRcdFx0dmFyIGlucHV0ID0gZG9jLmNyZWF0ZUVsZW1lbnQoXCJpbnB1dFwiKTtcblx0XHRcdGlucHV0LnNldEF0dHJpYnV0ZSggXCJ0eXBlXCIsIFwiaGlkZGVuXCIgKTtcblx0XHRcdGRpdi5hcHBlbmRDaGlsZCggaW5wdXQgKS5zZXRBdHRyaWJ1dGUoIFwibmFtZVwiLCBcIkRcIiApO1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBJRThcblx0XHRcdC8vIEVuZm9yY2UgY2FzZS1zZW5zaXRpdml0eSBvZiBuYW1lIGF0dHJpYnV0ZVxuXHRcdFx0aWYgKCBkaXYucXVlcnlTZWxlY3RvckFsbChcIltuYW1lPWRdXCIpLmxlbmd0aCApIHtcblx0XHRcdFx0cmJ1Z2d5UVNBLnB1c2goIFwibmFtZVwiICsgd2hpdGVzcGFjZSArIFwiKlsqXiR8IX5dPz1cIiApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBGRiAzLjUgLSA6ZW5hYmxlZC86ZGlzYWJsZWQgYW5kIGhpZGRlbiBlbGVtZW50cyAoaGlkZGVuIGVsZW1lbnRzIGFyZSBzdGlsbCBlbmFibGVkKVxuXHRcdFx0Ly8gSUU4IHRocm93cyBlcnJvciBoZXJlIGFuZCB3aWxsIG5vdCBzZWUgbGF0ZXIgdGVzdHNcblx0XHRcdGlmICggIWRpdi5xdWVyeVNlbGVjdG9yQWxsKFwiOmVuYWJsZWRcIikubGVuZ3RoICkge1xuXHRcdFx0XHRyYnVnZ3lRU0EucHVzaCggXCI6ZW5hYmxlZFwiLCBcIjpkaXNhYmxlZFwiICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIE9wZXJhIDEwLTExIGRvZXMgbm90IHRocm93IG9uIHBvc3QtY29tbWEgaW52YWxpZCBwc2V1ZG9zXG5cdFx0XHRkaXYucXVlcnlTZWxlY3RvckFsbChcIiosOnhcIik7XG5cdFx0XHRyYnVnZ3lRU0EucHVzaChcIiwuKjpcIik7XG5cdFx0fSk7XG5cdH1cblxuXHRpZiAoIChzdXBwb3J0Lm1hdGNoZXNTZWxlY3RvciA9IHJuYXRpdmUudGVzdCggKG1hdGNoZXMgPSBkb2NFbGVtLm1hdGNoZXMgfHxcblx0XHRkb2NFbGVtLndlYmtpdE1hdGNoZXNTZWxlY3RvciB8fFxuXHRcdGRvY0VsZW0ubW96TWF0Y2hlc1NlbGVjdG9yIHx8XG5cdFx0ZG9jRWxlbS5vTWF0Y2hlc1NlbGVjdG9yIHx8XG5cdFx0ZG9jRWxlbS5tc01hdGNoZXNTZWxlY3RvcikgKSkgKSB7XG5cblx0XHRhc3NlcnQoZnVuY3Rpb24oIGRpdiApIHtcblx0XHRcdC8vIENoZWNrIHRvIHNlZSBpZiBpdCdzIHBvc3NpYmxlIHRvIGRvIG1hdGNoZXNTZWxlY3RvclxuXHRcdFx0Ly8gb24gYSBkaXNjb25uZWN0ZWQgbm9kZSAoSUUgOSlcblx0XHRcdHN1cHBvcnQuZGlzY29ubmVjdGVkTWF0Y2ggPSBtYXRjaGVzLmNhbGwoIGRpdiwgXCJkaXZcIiApO1xuXG5cdFx0XHQvLyBUaGlzIHNob3VsZCBmYWlsIHdpdGggYW4gZXhjZXB0aW9uXG5cdFx0XHQvLyBHZWNrbyBkb2VzIG5vdCBlcnJvciwgcmV0dXJucyBmYWxzZSBpbnN0ZWFkXG5cdFx0XHRtYXRjaGVzLmNhbGwoIGRpdiwgXCJbcyE9JyddOnhcIiApO1xuXHRcdFx0cmJ1Z2d5TWF0Y2hlcy5wdXNoKCBcIiE9XCIsIHBzZXVkb3MgKTtcblx0XHR9KTtcblx0fVxuXG5cdHJidWdneVFTQSA9IHJidWdneVFTQS5sZW5ndGggJiYgbmV3IFJlZ0V4cCggcmJ1Z2d5UVNBLmpvaW4oXCJ8XCIpICk7XG5cdHJidWdneU1hdGNoZXMgPSByYnVnZ3lNYXRjaGVzLmxlbmd0aCAmJiBuZXcgUmVnRXhwKCByYnVnZ3lNYXRjaGVzLmpvaW4oXCJ8XCIpICk7XG5cblx0LyogQ29udGFpbnNcblx0LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXHRoYXNDb21wYXJlID0gcm5hdGl2ZS50ZXN0KCBkb2NFbGVtLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uICk7XG5cblx0Ly8gRWxlbWVudCBjb250YWlucyBhbm90aGVyXG5cdC8vIFB1cnBvc2VmdWxseSBkb2VzIG5vdCBpbXBsZW1lbnQgaW5jbHVzaXZlIGRlc2NlbmRlbnRcblx0Ly8gQXMgaW4sIGFuIGVsZW1lbnQgZG9lcyBub3QgY29udGFpbiBpdHNlbGZcblx0Y29udGFpbnMgPSBoYXNDb21wYXJlIHx8IHJuYXRpdmUudGVzdCggZG9jRWxlbS5jb250YWlucyApID9cblx0XHRmdW5jdGlvbiggYSwgYiApIHtcblx0XHRcdHZhciBhZG93biA9IGEubm9kZVR5cGUgPT09IDkgPyBhLmRvY3VtZW50RWxlbWVudCA6IGEsXG5cdFx0XHRcdGJ1cCA9IGIgJiYgYi5wYXJlbnROb2RlO1xuXHRcdFx0cmV0dXJuIGEgPT09IGJ1cCB8fCAhISggYnVwICYmIGJ1cC5ub2RlVHlwZSA9PT0gMSAmJiAoXG5cdFx0XHRcdGFkb3duLmNvbnRhaW5zID9cblx0XHRcdFx0XHRhZG93bi5jb250YWlucyggYnVwICkgOlxuXHRcdFx0XHRcdGEuY29tcGFyZURvY3VtZW50UG9zaXRpb24gJiYgYS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbiggYnVwICkgJiAxNlxuXHRcdFx0KSk7XG5cdFx0fSA6XG5cdFx0ZnVuY3Rpb24oIGEsIGIgKSB7XG5cdFx0XHRpZiAoIGIgKSB7XG5cdFx0XHRcdHdoaWxlICggKGIgPSBiLnBhcmVudE5vZGUpICkge1xuXHRcdFx0XHRcdGlmICggYiA9PT0gYSApIHtcblx0XHRcdFx0XHRcdHJldHVybiB0cnVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH07XG5cblx0LyogU29ydGluZ1xuXHQtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICovXG5cblx0Ly8gRG9jdW1lbnQgb3JkZXIgc29ydGluZ1xuXHRzb3J0T3JkZXIgPSBoYXNDb21wYXJlID9cblx0ZnVuY3Rpb24oIGEsIGIgKSB7XG5cblx0XHQvLyBGbGFnIGZvciBkdXBsaWNhdGUgcmVtb3ZhbFxuXHRcdGlmICggYSA9PT0gYiApIHtcblx0XHRcdGhhc0R1cGxpY2F0ZSA9IHRydWU7XG5cdFx0XHRyZXR1cm4gMDtcblx0XHR9XG5cblx0XHQvLyBTb3J0IG9uIG1ldGhvZCBleGlzdGVuY2UgaWYgb25seSBvbmUgaW5wdXQgaGFzIGNvbXBhcmVEb2N1bWVudFBvc2l0aW9uXG5cdFx0dmFyIGNvbXBhcmUgPSAhYS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbiAtICFiLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uO1xuXHRcdGlmICggY29tcGFyZSApIHtcblx0XHRcdHJldHVybiBjb21wYXJlO1xuXHRcdH1cblxuXHRcdC8vIENhbGN1bGF0ZSBwb3NpdGlvbiBpZiBib3RoIGlucHV0cyBiZWxvbmcgdG8gdGhlIHNhbWUgZG9jdW1lbnRcblx0XHRjb21wYXJlID0gKCBhLm93bmVyRG9jdW1lbnQgfHwgYSApID09PSAoIGIub3duZXJEb2N1bWVudCB8fCBiICkgP1xuXHRcdFx0YS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbiggYiApIDpcblxuXHRcdFx0Ly8gT3RoZXJ3aXNlIHdlIGtub3cgdGhleSBhcmUgZGlzY29ubmVjdGVkXG5cdFx0XHQxO1xuXG5cdFx0Ly8gRGlzY29ubmVjdGVkIG5vZGVzXG5cdFx0aWYgKCBjb21wYXJlICYgMSB8fFxuXHRcdFx0KCFzdXBwb3J0LnNvcnREZXRhY2hlZCAmJiBiLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uKCBhICkgPT09IGNvbXBhcmUpICkge1xuXG5cdFx0XHQvLyBDaG9vc2UgdGhlIGZpcnN0IGVsZW1lbnQgdGhhdCBpcyByZWxhdGVkIHRvIG91ciBwcmVmZXJyZWQgZG9jdW1lbnRcblx0XHRcdGlmICggYSA9PT0gZG9jIHx8IGEub3duZXJEb2N1bWVudCA9PT0gcHJlZmVycmVkRG9jICYmIGNvbnRhaW5zKHByZWZlcnJlZERvYywgYSkgKSB7XG5cdFx0XHRcdHJldHVybiAtMTtcblx0XHRcdH1cblx0XHRcdGlmICggYiA9PT0gZG9jIHx8IGIub3duZXJEb2N1bWVudCA9PT0gcHJlZmVycmVkRG9jICYmIGNvbnRhaW5zKHByZWZlcnJlZERvYywgYikgKSB7XG5cdFx0XHRcdHJldHVybiAxO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBNYWludGFpbiBvcmlnaW5hbCBvcmRlclxuXHRcdFx0cmV0dXJuIHNvcnRJbnB1dCA/XG5cdFx0XHRcdCggaW5kZXhPZiggc29ydElucHV0LCBhICkgLSBpbmRleE9mKCBzb3J0SW5wdXQsIGIgKSApIDpcblx0XHRcdFx0MDtcblx0XHR9XG5cblx0XHRyZXR1cm4gY29tcGFyZSAmIDQgPyAtMSA6IDE7XG5cdH0gOlxuXHRmdW5jdGlvbiggYSwgYiApIHtcblx0XHQvLyBFeGl0IGVhcmx5IGlmIHRoZSBub2RlcyBhcmUgaWRlbnRpY2FsXG5cdFx0aWYgKCBhID09PSBiICkge1xuXHRcdFx0aGFzRHVwbGljYXRlID0gdHJ1ZTtcblx0XHRcdHJldHVybiAwO1xuXHRcdH1cblxuXHRcdHZhciBjdXIsXG5cdFx0XHRpID0gMCxcblx0XHRcdGF1cCA9IGEucGFyZW50Tm9kZSxcblx0XHRcdGJ1cCA9IGIucGFyZW50Tm9kZSxcblx0XHRcdGFwID0gWyBhIF0sXG5cdFx0XHRicCA9IFsgYiBdO1xuXG5cdFx0Ly8gUGFyZW50bGVzcyBub2RlcyBhcmUgZWl0aGVyIGRvY3VtZW50cyBvciBkaXNjb25uZWN0ZWRcblx0XHRpZiAoICFhdXAgfHwgIWJ1cCApIHtcblx0XHRcdHJldHVybiBhID09PSBkb2MgPyAtMSA6XG5cdFx0XHRcdGIgPT09IGRvYyA/IDEgOlxuXHRcdFx0XHRhdXAgPyAtMSA6XG5cdFx0XHRcdGJ1cCA/IDEgOlxuXHRcdFx0XHRzb3J0SW5wdXQgP1xuXHRcdFx0XHQoIGluZGV4T2YoIHNvcnRJbnB1dCwgYSApIC0gaW5kZXhPZiggc29ydElucHV0LCBiICkgKSA6XG5cdFx0XHRcdDA7XG5cblx0XHQvLyBJZiB0aGUgbm9kZXMgYXJlIHNpYmxpbmdzLCB3ZSBjYW4gZG8gYSBxdWljayBjaGVja1xuXHRcdH0gZWxzZSBpZiAoIGF1cCA9PT0gYnVwICkge1xuXHRcdFx0cmV0dXJuIHNpYmxpbmdDaGVjayggYSwgYiApO1xuXHRcdH1cblxuXHRcdC8vIE90aGVyd2lzZSB3ZSBuZWVkIGZ1bGwgbGlzdHMgb2YgdGhlaXIgYW5jZXN0b3JzIGZvciBjb21wYXJpc29uXG5cdFx0Y3VyID0gYTtcblx0XHR3aGlsZSAoIChjdXIgPSBjdXIucGFyZW50Tm9kZSkgKSB7XG5cdFx0XHRhcC51bnNoaWZ0KCBjdXIgKTtcblx0XHR9XG5cdFx0Y3VyID0gYjtcblx0XHR3aGlsZSAoIChjdXIgPSBjdXIucGFyZW50Tm9kZSkgKSB7XG5cdFx0XHRicC51bnNoaWZ0KCBjdXIgKTtcblx0XHR9XG5cblx0XHQvLyBXYWxrIGRvd24gdGhlIHRyZWUgbG9va2luZyBmb3IgYSBkaXNjcmVwYW5jeVxuXHRcdHdoaWxlICggYXBbaV0gPT09IGJwW2ldICkge1xuXHRcdFx0aSsrO1xuXHRcdH1cblxuXHRcdHJldHVybiBpID9cblx0XHRcdC8vIERvIGEgc2libGluZyBjaGVjayBpZiB0aGUgbm9kZXMgaGF2ZSBhIGNvbW1vbiBhbmNlc3RvclxuXHRcdFx0c2libGluZ0NoZWNrKCBhcFtpXSwgYnBbaV0gKSA6XG5cblx0XHRcdC8vIE90aGVyd2lzZSBub2RlcyBpbiBvdXIgZG9jdW1lbnQgc29ydCBmaXJzdFxuXHRcdFx0YXBbaV0gPT09IHByZWZlcnJlZERvYyA/IC0xIDpcblx0XHRcdGJwW2ldID09PSBwcmVmZXJyZWREb2MgPyAxIDpcblx0XHRcdDA7XG5cdH07XG5cblx0cmV0dXJuIGRvYztcbn07XG5cblNpenpsZS5tYXRjaGVzID0gZnVuY3Rpb24oIGV4cHIsIGVsZW1lbnRzICkge1xuXHRyZXR1cm4gU2l6emxlKCBleHByLCBudWxsLCBudWxsLCBlbGVtZW50cyApO1xufTtcblxuU2l6emxlLm1hdGNoZXNTZWxlY3RvciA9IGZ1bmN0aW9uKCBlbGVtLCBleHByICkge1xuXHQvLyBTZXQgZG9jdW1lbnQgdmFycyBpZiBuZWVkZWRcblx0aWYgKCAoIGVsZW0ub3duZXJEb2N1bWVudCB8fCBlbGVtICkgIT09IGRvY3VtZW50ICkge1xuXHRcdHNldERvY3VtZW50KCBlbGVtICk7XG5cdH1cblxuXHQvLyBNYWtlIHN1cmUgdGhhdCBhdHRyaWJ1dGUgc2VsZWN0b3JzIGFyZSBxdW90ZWRcblx0ZXhwciA9IGV4cHIucmVwbGFjZSggcmF0dHJpYnV0ZVF1b3RlcywgXCI9JyQxJ11cIiApO1xuXG5cdGlmICggc3VwcG9ydC5tYXRjaGVzU2VsZWN0b3IgJiYgZG9jdW1lbnRJc0hUTUwgJiZcblx0XHQoICFyYnVnZ3lNYXRjaGVzIHx8ICFyYnVnZ3lNYXRjaGVzLnRlc3QoIGV4cHIgKSApICYmXG5cdFx0KCAhcmJ1Z2d5UVNBICAgICB8fCAhcmJ1Z2d5UVNBLnRlc3QoIGV4cHIgKSApICkge1xuXG5cdFx0dHJ5IHtcblx0XHRcdHZhciByZXQgPSBtYXRjaGVzLmNhbGwoIGVsZW0sIGV4cHIgKTtcblxuXHRcdFx0Ly8gSUUgOSdzIG1hdGNoZXNTZWxlY3RvciByZXR1cm5zIGZhbHNlIG9uIGRpc2Nvbm5lY3RlZCBub2Rlc1xuXHRcdFx0aWYgKCByZXQgfHwgc3VwcG9ydC5kaXNjb25uZWN0ZWRNYXRjaCB8fFxuXHRcdFx0XHRcdC8vIEFzIHdlbGwsIGRpc2Nvbm5lY3RlZCBub2RlcyBhcmUgc2FpZCB0byBiZSBpbiBhIGRvY3VtZW50XG5cdFx0XHRcdFx0Ly8gZnJhZ21lbnQgaW4gSUUgOVxuXHRcdFx0XHRcdGVsZW0uZG9jdW1lbnQgJiYgZWxlbS5kb2N1bWVudC5ub2RlVHlwZSAhPT0gMTEgKSB7XG5cdFx0XHRcdHJldHVybiByZXQ7XG5cdFx0XHR9XG5cdFx0fSBjYXRjaCAoZSkge31cblx0fVxuXG5cdHJldHVybiBTaXp6bGUoIGV4cHIsIGRvY3VtZW50LCBudWxsLCBbIGVsZW0gXSApLmxlbmd0aCA+IDA7XG59O1xuXG5TaXp6bGUuY29udGFpbnMgPSBmdW5jdGlvbiggY29udGV4dCwgZWxlbSApIHtcblx0Ly8gU2V0IGRvY3VtZW50IHZhcnMgaWYgbmVlZGVkXG5cdGlmICggKCBjb250ZXh0Lm93bmVyRG9jdW1lbnQgfHwgY29udGV4dCApICE9PSBkb2N1bWVudCApIHtcblx0XHRzZXREb2N1bWVudCggY29udGV4dCApO1xuXHR9XG5cdHJldHVybiBjb250YWlucyggY29udGV4dCwgZWxlbSApO1xufTtcblxuU2l6emxlLmF0dHIgPSBmdW5jdGlvbiggZWxlbSwgbmFtZSApIHtcblx0Ly8gU2V0IGRvY3VtZW50IHZhcnMgaWYgbmVlZGVkXG5cdGlmICggKCBlbGVtLm93bmVyRG9jdW1lbnQgfHwgZWxlbSApICE9PSBkb2N1bWVudCApIHtcblx0XHRzZXREb2N1bWVudCggZWxlbSApO1xuXHR9XG5cblx0dmFyIGZuID0gRXhwci5hdHRySGFuZGxlWyBuYW1lLnRvTG93ZXJDYXNlKCkgXSxcblx0XHQvLyBEb24ndCBnZXQgZm9vbGVkIGJ5IE9iamVjdC5wcm90b3R5cGUgcHJvcGVydGllcyAoalF1ZXJ5ICMxMzgwNylcblx0XHR2YWwgPSBmbiAmJiBoYXNPd24uY2FsbCggRXhwci5hdHRySGFuZGxlLCBuYW1lLnRvTG93ZXJDYXNlKCkgKSA/XG5cdFx0XHRmbiggZWxlbSwgbmFtZSwgIWRvY3VtZW50SXNIVE1MICkgOlxuXHRcdFx0dW5kZWZpbmVkO1xuXG5cdHJldHVybiB2YWwgIT09IHVuZGVmaW5lZCA/XG5cdFx0dmFsIDpcblx0XHRzdXBwb3J0LmF0dHJpYnV0ZXMgfHwgIWRvY3VtZW50SXNIVE1MID9cblx0XHRcdGVsZW0uZ2V0QXR0cmlidXRlKCBuYW1lICkgOlxuXHRcdFx0KHZhbCA9IGVsZW0uZ2V0QXR0cmlidXRlTm9kZShuYW1lKSkgJiYgdmFsLnNwZWNpZmllZCA/XG5cdFx0XHRcdHZhbC52YWx1ZSA6XG5cdFx0XHRcdG51bGw7XG59O1xuXG5TaXp6bGUuZXJyb3IgPSBmdW5jdGlvbiggbXNnICkge1xuXHR0aHJvdyBuZXcgRXJyb3IoIFwiU3ludGF4IGVycm9yLCB1bnJlY29nbml6ZWQgZXhwcmVzc2lvbjogXCIgKyBtc2cgKTtcbn07XG5cbi8qKlxuICogRG9jdW1lbnQgc29ydGluZyBhbmQgcmVtb3ZpbmcgZHVwbGljYXRlc1xuICogQHBhcmFtIHtBcnJheUxpa2V9IHJlc3VsdHNcbiAqL1xuU2l6emxlLnVuaXF1ZVNvcnQgPSBmdW5jdGlvbiggcmVzdWx0cyApIHtcblx0dmFyIGVsZW0sXG5cdFx0ZHVwbGljYXRlcyA9IFtdLFxuXHRcdGogPSAwLFxuXHRcdGkgPSAwO1xuXG5cdC8vIFVubGVzcyB3ZSAqa25vdyogd2UgY2FuIGRldGVjdCBkdXBsaWNhdGVzLCBhc3N1bWUgdGhlaXIgcHJlc2VuY2Vcblx0aGFzRHVwbGljYXRlID0gIXN1cHBvcnQuZGV0ZWN0RHVwbGljYXRlcztcblx0c29ydElucHV0ID0gIXN1cHBvcnQuc29ydFN0YWJsZSAmJiByZXN1bHRzLnNsaWNlKCAwICk7XG5cdHJlc3VsdHMuc29ydCggc29ydE9yZGVyICk7XG5cblx0aWYgKCBoYXNEdXBsaWNhdGUgKSB7XG5cdFx0d2hpbGUgKCAoZWxlbSA9IHJlc3VsdHNbaSsrXSkgKSB7XG5cdFx0XHRpZiAoIGVsZW0gPT09IHJlc3VsdHNbIGkgXSApIHtcblx0XHRcdFx0aiA9IGR1cGxpY2F0ZXMucHVzaCggaSApO1xuXHRcdFx0fVxuXHRcdH1cblx0XHR3aGlsZSAoIGotLSApIHtcblx0XHRcdHJlc3VsdHMuc3BsaWNlKCBkdXBsaWNhdGVzWyBqIF0sIDEgKTtcblx0XHR9XG5cdH1cblxuXHQvLyBDbGVhciBpbnB1dCBhZnRlciBzb3J0aW5nIHRvIHJlbGVhc2Ugb2JqZWN0c1xuXHQvLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2pxdWVyeS9zaXp6bGUvcHVsbC8yMjVcblx0c29ydElucHV0ID0gbnVsbDtcblxuXHRyZXR1cm4gcmVzdWx0cztcbn07XG5cbi8qKlxuICogVXRpbGl0eSBmdW5jdGlvbiBmb3IgcmV0cmlldmluZyB0aGUgdGV4dCB2YWx1ZSBvZiBhbiBhcnJheSBvZiBET00gbm9kZXNcbiAqIEBwYXJhbSB7QXJyYXl8RWxlbWVudH0gZWxlbVxuICovXG5nZXRUZXh0ID0gU2l6emxlLmdldFRleHQgPSBmdW5jdGlvbiggZWxlbSApIHtcblx0dmFyIG5vZGUsXG5cdFx0cmV0ID0gXCJcIixcblx0XHRpID0gMCxcblx0XHRub2RlVHlwZSA9IGVsZW0ubm9kZVR5cGU7XG5cblx0aWYgKCAhbm9kZVR5cGUgKSB7XG5cdFx0Ly8gSWYgbm8gbm9kZVR5cGUsIHRoaXMgaXMgZXhwZWN0ZWQgdG8gYmUgYW4gYXJyYXlcblx0XHR3aGlsZSAoIChub2RlID0gZWxlbVtpKytdKSApIHtcblx0XHRcdC8vIERvIG5vdCB0cmF2ZXJzZSBjb21tZW50IG5vZGVzXG5cdFx0XHRyZXQgKz0gZ2V0VGV4dCggbm9kZSApO1xuXHRcdH1cblx0fSBlbHNlIGlmICggbm9kZVR5cGUgPT09IDEgfHwgbm9kZVR5cGUgPT09IDkgfHwgbm9kZVR5cGUgPT09IDExICkge1xuXHRcdC8vIFVzZSB0ZXh0Q29udGVudCBmb3IgZWxlbWVudHNcblx0XHQvLyBpbm5lclRleHQgdXNhZ2UgcmVtb3ZlZCBmb3IgY29uc2lzdGVuY3kgb2YgbmV3IGxpbmVzIChqUXVlcnkgIzExMTUzKVxuXHRcdGlmICggdHlwZW9mIGVsZW0udGV4dENvbnRlbnQgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRyZXR1cm4gZWxlbS50ZXh0Q29udGVudDtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gVHJhdmVyc2UgaXRzIGNoaWxkcmVuXG5cdFx0XHRmb3IgKCBlbGVtID0gZWxlbS5maXJzdENoaWxkOyBlbGVtOyBlbGVtID0gZWxlbS5uZXh0U2libGluZyApIHtcblx0XHRcdFx0cmV0ICs9IGdldFRleHQoIGVsZW0gKTtcblx0XHRcdH1cblx0XHR9XG5cdH0gZWxzZSBpZiAoIG5vZGVUeXBlID09PSAzIHx8IG5vZGVUeXBlID09PSA0ICkge1xuXHRcdHJldHVybiBlbGVtLm5vZGVWYWx1ZTtcblx0fVxuXHQvLyBEbyBub3QgaW5jbHVkZSBjb21tZW50IG9yIHByb2Nlc3NpbmcgaW5zdHJ1Y3Rpb24gbm9kZXNcblxuXHRyZXR1cm4gcmV0O1xufTtcblxuRXhwciA9IFNpenpsZS5zZWxlY3RvcnMgPSB7XG5cblx0Ly8gQ2FuIGJlIGFkanVzdGVkIGJ5IHRoZSB1c2VyXG5cdGNhY2hlTGVuZ3RoOiA1MCxcblxuXHRjcmVhdGVQc2V1ZG86IG1hcmtGdW5jdGlvbixcblxuXHRtYXRjaDogbWF0Y2hFeHByLFxuXG5cdGF0dHJIYW5kbGU6IHt9LFxuXG5cdGZpbmQ6IHt9LFxuXG5cdHJlbGF0aXZlOiB7XG5cdFx0XCI+XCI6IHsgZGlyOiBcInBhcmVudE5vZGVcIiwgZmlyc3Q6IHRydWUgfSxcblx0XHRcIiBcIjogeyBkaXI6IFwicGFyZW50Tm9kZVwiIH0sXG5cdFx0XCIrXCI6IHsgZGlyOiBcInByZXZpb3VzU2libGluZ1wiLCBmaXJzdDogdHJ1ZSB9LFxuXHRcdFwiflwiOiB7IGRpcjogXCJwcmV2aW91c1NpYmxpbmdcIiB9XG5cdH0sXG5cblx0cHJlRmlsdGVyOiB7XG5cdFx0XCJBVFRSXCI6IGZ1bmN0aW9uKCBtYXRjaCApIHtcblx0XHRcdG1hdGNoWzFdID0gbWF0Y2hbMV0ucmVwbGFjZSggcnVuZXNjYXBlLCBmdW5lc2NhcGUgKTtcblxuXHRcdFx0Ly8gTW92ZSB0aGUgZ2l2ZW4gdmFsdWUgdG8gbWF0Y2hbM10gd2hldGhlciBxdW90ZWQgb3IgdW5xdW90ZWRcblx0XHRcdG1hdGNoWzNdID0gKCBtYXRjaFszXSB8fCBtYXRjaFs0XSB8fCBtYXRjaFs1XSB8fCBcIlwiICkucmVwbGFjZSggcnVuZXNjYXBlLCBmdW5lc2NhcGUgKTtcblxuXHRcdFx0aWYgKCBtYXRjaFsyXSA9PT0gXCJ+PVwiICkge1xuXHRcdFx0XHRtYXRjaFszXSA9IFwiIFwiICsgbWF0Y2hbM10gKyBcIiBcIjtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG1hdGNoLnNsaWNlKCAwLCA0ICk7XG5cdFx0fSxcblxuXHRcdFwiQ0hJTERcIjogZnVuY3Rpb24oIG1hdGNoICkge1xuXHRcdFx0LyogbWF0Y2hlcyBmcm9tIG1hdGNoRXhwcltcIkNISUxEXCJdXG5cdFx0XHRcdDEgdHlwZSAob25seXxudGh8Li4uKVxuXHRcdFx0XHQyIHdoYXQgKGNoaWxkfG9mLXR5cGUpXG5cdFx0XHRcdDMgYXJndW1lbnQgKGV2ZW58b2RkfFxcZCp8XFxkKm4oWystXVxcZCspP3wuLi4pXG5cdFx0XHRcdDQgeG4tY29tcG9uZW50IG9mIHhuK3kgYXJndW1lbnQgKFsrLV0/XFxkKm58KVxuXHRcdFx0XHQ1IHNpZ24gb2YgeG4tY29tcG9uZW50XG5cdFx0XHRcdDYgeCBvZiB4bi1jb21wb25lbnRcblx0XHRcdFx0NyBzaWduIG9mIHktY29tcG9uZW50XG5cdFx0XHRcdDggeSBvZiB5LWNvbXBvbmVudFxuXHRcdFx0Ki9cblx0XHRcdG1hdGNoWzFdID0gbWF0Y2hbMV0udG9Mb3dlckNhc2UoKTtcblxuXHRcdFx0aWYgKCBtYXRjaFsxXS5zbGljZSggMCwgMyApID09PSBcIm50aFwiICkge1xuXHRcdFx0XHQvLyBudGgtKiByZXF1aXJlcyBhcmd1bWVudFxuXHRcdFx0XHRpZiAoICFtYXRjaFszXSApIHtcblx0XHRcdFx0XHRTaXp6bGUuZXJyb3IoIG1hdGNoWzBdICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBudW1lcmljIHggYW5kIHkgcGFyYW1ldGVycyBmb3IgRXhwci5maWx0ZXIuQ0hJTERcblx0XHRcdFx0Ly8gcmVtZW1iZXIgdGhhdCBmYWxzZS90cnVlIGNhc3QgcmVzcGVjdGl2ZWx5IHRvIDAvMVxuXHRcdFx0XHRtYXRjaFs0XSA9ICsoIG1hdGNoWzRdID8gbWF0Y2hbNV0gKyAobWF0Y2hbNl0gfHwgMSkgOiAyICogKCBtYXRjaFszXSA9PT0gXCJldmVuXCIgfHwgbWF0Y2hbM10gPT09IFwib2RkXCIgKSApO1xuXHRcdFx0XHRtYXRjaFs1XSA9ICsoICggbWF0Y2hbN10gKyBtYXRjaFs4XSApIHx8IG1hdGNoWzNdID09PSBcIm9kZFwiICk7XG5cblx0XHRcdC8vIG90aGVyIHR5cGVzIHByb2hpYml0IGFyZ3VtZW50c1xuXHRcdFx0fSBlbHNlIGlmICggbWF0Y2hbM10gKSB7XG5cdFx0XHRcdFNpenpsZS5lcnJvciggbWF0Y2hbMF0gKTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIG1hdGNoO1xuXHRcdH0sXG5cblx0XHRcIlBTRVVET1wiOiBmdW5jdGlvbiggbWF0Y2ggKSB7XG5cdFx0XHR2YXIgZXhjZXNzLFxuXHRcdFx0XHR1bnF1b3RlZCA9ICFtYXRjaFs2XSAmJiBtYXRjaFsyXTtcblxuXHRcdFx0aWYgKCBtYXRjaEV4cHJbXCJDSElMRFwiXS50ZXN0KCBtYXRjaFswXSApICkge1xuXHRcdFx0XHRyZXR1cm4gbnVsbDtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQWNjZXB0IHF1b3RlZCBhcmd1bWVudHMgYXMtaXNcblx0XHRcdGlmICggbWF0Y2hbM10gKSB7XG5cdFx0XHRcdG1hdGNoWzJdID0gbWF0Y2hbNF0gfHwgbWF0Y2hbNV0gfHwgXCJcIjtcblxuXHRcdFx0Ly8gU3RyaXAgZXhjZXNzIGNoYXJhY3RlcnMgZnJvbSB1bnF1b3RlZCBhcmd1bWVudHNcblx0XHRcdH0gZWxzZSBpZiAoIHVucXVvdGVkICYmIHJwc2V1ZG8udGVzdCggdW5xdW90ZWQgKSAmJlxuXHRcdFx0XHQvLyBHZXQgZXhjZXNzIGZyb20gdG9rZW5pemUgKHJlY3Vyc2l2ZWx5KVxuXHRcdFx0XHQoZXhjZXNzID0gdG9rZW5pemUoIHVucXVvdGVkLCB0cnVlICkpICYmXG5cdFx0XHRcdC8vIGFkdmFuY2UgdG8gdGhlIG5leHQgY2xvc2luZyBwYXJlbnRoZXNpc1xuXHRcdFx0XHQoZXhjZXNzID0gdW5xdW90ZWQuaW5kZXhPZiggXCIpXCIsIHVucXVvdGVkLmxlbmd0aCAtIGV4Y2VzcyApIC0gdW5xdW90ZWQubGVuZ3RoKSApIHtcblxuXHRcdFx0XHQvLyBleGNlc3MgaXMgYSBuZWdhdGl2ZSBpbmRleFxuXHRcdFx0XHRtYXRjaFswXSA9IG1hdGNoWzBdLnNsaWNlKCAwLCBleGNlc3MgKTtcblx0XHRcdFx0bWF0Y2hbMl0gPSB1bnF1b3RlZC5zbGljZSggMCwgZXhjZXNzICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFJldHVybiBvbmx5IGNhcHR1cmVzIG5lZWRlZCBieSB0aGUgcHNldWRvIGZpbHRlciBtZXRob2QgKHR5cGUgYW5kIGFyZ3VtZW50KVxuXHRcdFx0cmV0dXJuIG1hdGNoLnNsaWNlKCAwLCAzICk7XG5cdFx0fVxuXHR9LFxuXG5cdGZpbHRlcjoge1xuXG5cdFx0XCJUQUdcIjogZnVuY3Rpb24oIG5vZGVOYW1lU2VsZWN0b3IgKSB7XG5cdFx0XHR2YXIgbm9kZU5hbWUgPSBub2RlTmFtZVNlbGVjdG9yLnJlcGxhY2UoIHJ1bmVzY2FwZSwgZnVuZXNjYXBlICkudG9Mb3dlckNhc2UoKTtcblx0XHRcdHJldHVybiBub2RlTmFtZVNlbGVjdG9yID09PSBcIipcIiA/XG5cdFx0XHRcdGZ1bmN0aW9uKCkgeyByZXR1cm4gdHJ1ZTsgfSA6XG5cdFx0XHRcdGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0XHRcdHJldHVybiBlbGVtLm5vZGVOYW1lICYmIGVsZW0ubm9kZU5hbWUudG9Mb3dlckNhc2UoKSA9PT0gbm9kZU5hbWU7XG5cdFx0XHRcdH07XG5cdFx0fSxcblxuXHRcdFwiQ0xBU1NcIjogZnVuY3Rpb24oIGNsYXNzTmFtZSApIHtcblx0XHRcdHZhciBwYXR0ZXJuID0gY2xhc3NDYWNoZVsgY2xhc3NOYW1lICsgXCIgXCIgXTtcblxuXHRcdFx0cmV0dXJuIHBhdHRlcm4gfHxcblx0XHRcdFx0KHBhdHRlcm4gPSBuZXcgUmVnRXhwKCBcIihefFwiICsgd2hpdGVzcGFjZSArIFwiKVwiICsgY2xhc3NOYW1lICsgXCIoXCIgKyB3aGl0ZXNwYWNlICsgXCJ8JClcIiApKSAmJlxuXHRcdFx0XHRjbGFzc0NhY2hlKCBjbGFzc05hbWUsIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0XHRcdHJldHVybiBwYXR0ZXJuLnRlc3QoIHR5cGVvZiBlbGVtLmNsYXNzTmFtZSA9PT0gXCJzdHJpbmdcIiAmJiBlbGVtLmNsYXNzTmFtZSB8fCB0eXBlb2YgZWxlbS5nZXRBdHRyaWJ1dGUgIT09IFwidW5kZWZpbmVkXCIgJiYgZWxlbS5nZXRBdHRyaWJ1dGUoXCJjbGFzc1wiKSB8fCBcIlwiICk7XG5cdFx0XHRcdH0pO1xuXHRcdH0sXG5cblx0XHRcIkFUVFJcIjogZnVuY3Rpb24oIG5hbWUsIG9wZXJhdG9yLCBjaGVjayApIHtcblx0XHRcdHJldHVybiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdFx0dmFyIHJlc3VsdCA9IFNpenpsZS5hdHRyKCBlbGVtLCBuYW1lICk7XG5cblx0XHRcdFx0aWYgKCByZXN1bHQgPT0gbnVsbCApIHtcblx0XHRcdFx0XHRyZXR1cm4gb3BlcmF0b3IgPT09IFwiIT1cIjtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoICFvcGVyYXRvciApIHtcblx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJlc3VsdCArPSBcIlwiO1xuXG5cdFx0XHRcdHJldHVybiBvcGVyYXRvciA9PT0gXCI9XCIgPyByZXN1bHQgPT09IGNoZWNrIDpcblx0XHRcdFx0XHRvcGVyYXRvciA9PT0gXCIhPVwiID8gcmVzdWx0ICE9PSBjaGVjayA6XG5cdFx0XHRcdFx0b3BlcmF0b3IgPT09IFwiXj1cIiA/IGNoZWNrICYmIHJlc3VsdC5pbmRleE9mKCBjaGVjayApID09PSAwIDpcblx0XHRcdFx0XHRvcGVyYXRvciA9PT0gXCIqPVwiID8gY2hlY2sgJiYgcmVzdWx0LmluZGV4T2YoIGNoZWNrICkgPiAtMSA6XG5cdFx0XHRcdFx0b3BlcmF0b3IgPT09IFwiJD1cIiA/IGNoZWNrICYmIHJlc3VsdC5zbGljZSggLWNoZWNrLmxlbmd0aCApID09PSBjaGVjayA6XG5cdFx0XHRcdFx0b3BlcmF0b3IgPT09IFwifj1cIiA/ICggXCIgXCIgKyByZXN1bHQucmVwbGFjZSggcndoaXRlc3BhY2UsIFwiIFwiICkgKyBcIiBcIiApLmluZGV4T2YoIGNoZWNrICkgPiAtMSA6XG5cdFx0XHRcdFx0b3BlcmF0b3IgPT09IFwifD1cIiA/IHJlc3VsdCA9PT0gY2hlY2sgfHwgcmVzdWx0LnNsaWNlKCAwLCBjaGVjay5sZW5ndGggKyAxICkgPT09IGNoZWNrICsgXCItXCIgOlxuXHRcdFx0XHRcdGZhbHNlO1xuXHRcdFx0fTtcblx0XHR9LFxuXG5cdFx0XCJDSElMRFwiOiBmdW5jdGlvbiggdHlwZSwgd2hhdCwgYXJndW1lbnQsIGZpcnN0LCBsYXN0ICkge1xuXHRcdFx0dmFyIHNpbXBsZSA9IHR5cGUuc2xpY2UoIDAsIDMgKSAhPT0gXCJudGhcIixcblx0XHRcdFx0Zm9yd2FyZCA9IHR5cGUuc2xpY2UoIC00ICkgIT09IFwibGFzdFwiLFxuXHRcdFx0XHRvZlR5cGUgPSB3aGF0ID09PSBcIm9mLXR5cGVcIjtcblxuXHRcdFx0cmV0dXJuIGZpcnN0ID09PSAxICYmIGxhc3QgPT09IDAgP1xuXG5cdFx0XHRcdC8vIFNob3J0Y3V0IGZvciA6bnRoLSoobilcblx0XHRcdFx0ZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRcdFx0cmV0dXJuICEhZWxlbS5wYXJlbnROb2RlO1xuXHRcdFx0XHR9IDpcblxuXHRcdFx0XHRmdW5jdGlvbiggZWxlbSwgY29udGV4dCwgeG1sICkge1xuXHRcdFx0XHRcdHZhciBjYWNoZSwgb3V0ZXJDYWNoZSwgbm9kZSwgZGlmZiwgbm9kZUluZGV4LCBzdGFydCxcblx0XHRcdFx0XHRcdGRpciA9IHNpbXBsZSAhPT0gZm9yd2FyZCA/IFwibmV4dFNpYmxpbmdcIiA6IFwicHJldmlvdXNTaWJsaW5nXCIsXG5cdFx0XHRcdFx0XHRwYXJlbnQgPSBlbGVtLnBhcmVudE5vZGUsXG5cdFx0XHRcdFx0XHRuYW1lID0gb2ZUeXBlICYmIGVsZW0ubm9kZU5hbWUudG9Mb3dlckNhc2UoKSxcblx0XHRcdFx0XHRcdHVzZUNhY2hlID0gIXhtbCAmJiAhb2ZUeXBlO1xuXG5cdFx0XHRcdFx0aWYgKCBwYXJlbnQgKSB7XG5cblx0XHRcdFx0XHRcdC8vIDooZmlyc3R8bGFzdHxvbmx5KS0oY2hpbGR8b2YtdHlwZSlcblx0XHRcdFx0XHRcdGlmICggc2ltcGxlICkge1xuXHRcdFx0XHRcdFx0XHR3aGlsZSAoIGRpciApIHtcblx0XHRcdFx0XHRcdFx0XHRub2RlID0gZWxlbTtcblx0XHRcdFx0XHRcdFx0XHR3aGlsZSAoIChub2RlID0gbm9kZVsgZGlyIF0pICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0aWYgKCBvZlR5cGUgPyBub2RlLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCkgPT09IG5hbWUgOiBub2RlLm5vZGVUeXBlID09PSAxICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHRcdC8vIFJldmVyc2UgZGlyZWN0aW9uIGZvciA6b25seS0qIChpZiB3ZSBoYXZlbid0IHlldCBkb25lIHNvKVxuXHRcdFx0XHRcdFx0XHRcdHN0YXJ0ID0gZGlyID0gdHlwZSA9PT0gXCJvbmx5XCIgJiYgIXN0YXJ0ICYmIFwibmV4dFNpYmxpbmdcIjtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0c3RhcnQgPSBbIGZvcndhcmQgPyBwYXJlbnQuZmlyc3RDaGlsZCA6IHBhcmVudC5sYXN0Q2hpbGQgXTtcblxuXHRcdFx0XHRcdFx0Ly8gbm9uLXhtbCA6bnRoLWNoaWxkKC4uLikgc3RvcmVzIGNhY2hlIGRhdGEgb24gYHBhcmVudGBcblx0XHRcdFx0XHRcdGlmICggZm9yd2FyZCAmJiB1c2VDYWNoZSApIHtcblx0XHRcdFx0XHRcdFx0Ly8gU2VlayBgZWxlbWAgZnJvbSBhIHByZXZpb3VzbHktY2FjaGVkIGluZGV4XG5cdFx0XHRcdFx0XHRcdG91dGVyQ2FjaGUgPSBwYXJlbnRbIGV4cGFuZG8gXSB8fCAocGFyZW50WyBleHBhbmRvIF0gPSB7fSk7XG5cdFx0XHRcdFx0XHRcdGNhY2hlID0gb3V0ZXJDYWNoZVsgdHlwZSBdIHx8IFtdO1xuXHRcdFx0XHRcdFx0XHRub2RlSW5kZXggPSBjYWNoZVswXSA9PT0gZGlycnVucyAmJiBjYWNoZVsxXTtcblx0XHRcdFx0XHRcdFx0ZGlmZiA9IGNhY2hlWzBdID09PSBkaXJydW5zICYmIGNhY2hlWzJdO1xuXHRcdFx0XHRcdFx0XHRub2RlID0gbm9kZUluZGV4ICYmIHBhcmVudC5jaGlsZE5vZGVzWyBub2RlSW5kZXggXTtcblxuXHRcdFx0XHRcdFx0XHR3aGlsZSAoIChub2RlID0gKytub2RlSW5kZXggJiYgbm9kZSAmJiBub2RlWyBkaXIgXSB8fFxuXG5cdFx0XHRcdFx0XHRcdFx0Ly8gRmFsbGJhY2sgdG8gc2Vla2luZyBgZWxlbWAgZnJvbSB0aGUgc3RhcnRcblx0XHRcdFx0XHRcdFx0XHQoZGlmZiA9IG5vZGVJbmRleCA9IDApIHx8IHN0YXJ0LnBvcCgpKSApIHtcblxuXHRcdFx0XHRcdFx0XHRcdC8vIFdoZW4gZm91bmQsIGNhY2hlIGluZGV4ZXMgb24gYHBhcmVudGAgYW5kIGJyZWFrXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBub2RlLm5vZGVUeXBlID09PSAxICYmICsrZGlmZiAmJiBub2RlID09PSBlbGVtICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0b3V0ZXJDYWNoZVsgdHlwZSBdID0gWyBkaXJydW5zLCBub2RlSW5kZXgsIGRpZmYgXTtcblx0XHRcdFx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHQvLyBVc2UgcHJldmlvdXNseS1jYWNoZWQgZWxlbWVudCBpbmRleCBpZiBhdmFpbGFibGVcblx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoIHVzZUNhY2hlICYmIChjYWNoZSA9IChlbGVtWyBleHBhbmRvIF0gfHwgKGVsZW1bIGV4cGFuZG8gXSA9IHt9KSlbIHR5cGUgXSkgJiYgY2FjaGVbMF0gPT09IGRpcnJ1bnMgKSB7XG5cdFx0XHRcdFx0XHRcdGRpZmYgPSBjYWNoZVsxXTtcblxuXHRcdFx0XHRcdFx0Ly8geG1sIDpudGgtY2hpbGQoLi4uKSBvciA6bnRoLWxhc3QtY2hpbGQoLi4uKSBvciA6bnRoKC1sYXN0KT8tb2YtdHlwZSguLi4pXG5cdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHQvLyBVc2UgdGhlIHNhbWUgbG9vcCBhcyBhYm92ZSB0byBzZWVrIGBlbGVtYCBmcm9tIHRoZSBzdGFydFxuXHRcdFx0XHRcdFx0XHR3aGlsZSAoIChub2RlID0gKytub2RlSW5kZXggJiYgbm9kZSAmJiBub2RlWyBkaXIgXSB8fFxuXHRcdFx0XHRcdFx0XHRcdChkaWZmID0gbm9kZUluZGV4ID0gMCkgfHwgc3RhcnQucG9wKCkpICkge1xuXG5cdFx0XHRcdFx0XHRcdFx0aWYgKCAoIG9mVHlwZSA/IG5vZGUubm9kZU5hbWUudG9Mb3dlckNhc2UoKSA9PT0gbmFtZSA6IG5vZGUubm9kZVR5cGUgPT09IDEgKSAmJiArK2RpZmYgKSB7XG5cdFx0XHRcdFx0XHRcdFx0XHQvLyBDYWNoZSB0aGUgaW5kZXggb2YgZWFjaCBlbmNvdW50ZXJlZCBlbGVtZW50XG5cdFx0XHRcdFx0XHRcdFx0XHRpZiAoIHVzZUNhY2hlICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0XHQobm9kZVsgZXhwYW5kbyBdIHx8IChub2RlWyBleHBhbmRvIF0gPSB7fSkpWyB0eXBlIF0gPSBbIGRpcnJ1bnMsIGRpZmYgXTtcblx0XHRcdFx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0XHRcdFx0aWYgKCBub2RlID09PSBlbGVtICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdFx0Ly8gSW5jb3Jwb3JhdGUgdGhlIG9mZnNldCwgdGhlbiBjaGVjayBhZ2FpbnN0IGN5Y2xlIHNpemVcblx0XHRcdFx0XHRcdGRpZmYgLT0gbGFzdDtcblx0XHRcdFx0XHRcdHJldHVybiBkaWZmID09PSBmaXJzdCB8fCAoIGRpZmYgJSBmaXJzdCA9PT0gMCAmJiBkaWZmIC8gZmlyc3QgPj0gMCApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fTtcblx0XHR9LFxuXG5cdFx0XCJQU0VVRE9cIjogZnVuY3Rpb24oIHBzZXVkbywgYXJndW1lbnQgKSB7XG5cdFx0XHQvLyBwc2V1ZG8tY2xhc3MgbmFtZXMgYXJlIGNhc2UtaW5zZW5zaXRpdmVcblx0XHRcdC8vIGh0dHA6Ly93d3cudzMub3JnL1RSL3NlbGVjdG9ycy8jcHNldWRvLWNsYXNzZXNcblx0XHRcdC8vIFByaW9yaXRpemUgYnkgY2FzZSBzZW5zaXRpdml0eSBpbiBjYXNlIGN1c3RvbSBwc2V1ZG9zIGFyZSBhZGRlZCB3aXRoIHVwcGVyY2FzZSBsZXR0ZXJzXG5cdFx0XHQvLyBSZW1lbWJlciB0aGF0IHNldEZpbHRlcnMgaW5oZXJpdHMgZnJvbSBwc2V1ZG9zXG5cdFx0XHR2YXIgYXJncyxcblx0XHRcdFx0Zm4gPSBFeHByLnBzZXVkb3NbIHBzZXVkbyBdIHx8IEV4cHIuc2V0RmlsdGVyc1sgcHNldWRvLnRvTG93ZXJDYXNlKCkgXSB8fFxuXHRcdFx0XHRcdFNpenpsZS5lcnJvciggXCJ1bnN1cHBvcnRlZCBwc2V1ZG86IFwiICsgcHNldWRvICk7XG5cblx0XHRcdC8vIFRoZSB1c2VyIG1heSB1c2UgY3JlYXRlUHNldWRvIHRvIGluZGljYXRlIHRoYXRcblx0XHRcdC8vIGFyZ3VtZW50cyBhcmUgbmVlZGVkIHRvIGNyZWF0ZSB0aGUgZmlsdGVyIGZ1bmN0aW9uXG5cdFx0XHQvLyBqdXN0IGFzIFNpenpsZSBkb2VzXG5cdFx0XHRpZiAoIGZuWyBleHBhbmRvIF0gKSB7XG5cdFx0XHRcdHJldHVybiBmbiggYXJndW1lbnQgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQnV0IG1haW50YWluIHN1cHBvcnQgZm9yIG9sZCBzaWduYXR1cmVzXG5cdFx0XHRpZiAoIGZuLmxlbmd0aCA+IDEgKSB7XG5cdFx0XHRcdGFyZ3MgPSBbIHBzZXVkbywgcHNldWRvLCBcIlwiLCBhcmd1bWVudCBdO1xuXHRcdFx0XHRyZXR1cm4gRXhwci5zZXRGaWx0ZXJzLmhhc093blByb3BlcnR5KCBwc2V1ZG8udG9Mb3dlckNhc2UoKSApID9cblx0XHRcdFx0XHRtYXJrRnVuY3Rpb24oZnVuY3Rpb24oIHNlZWQsIG1hdGNoZXMgKSB7XG5cdFx0XHRcdFx0XHR2YXIgaWR4LFxuXHRcdFx0XHRcdFx0XHRtYXRjaGVkID0gZm4oIHNlZWQsIGFyZ3VtZW50ICksXG5cdFx0XHRcdFx0XHRcdGkgPSBtYXRjaGVkLmxlbmd0aDtcblx0XHRcdFx0XHRcdHdoaWxlICggaS0tICkge1xuXHRcdFx0XHRcdFx0XHRpZHggPSBpbmRleE9mKCBzZWVkLCBtYXRjaGVkW2ldICk7XG5cdFx0XHRcdFx0XHRcdHNlZWRbIGlkeCBdID0gISggbWF0Y2hlc1sgaWR4IF0gPSBtYXRjaGVkW2ldICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fSkgOlxuXHRcdFx0XHRcdGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIGZuKCBlbGVtLCAwLCBhcmdzICk7XG5cdFx0XHRcdFx0fTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGZuO1xuXHRcdH1cblx0fSxcblxuXHRwc2V1ZG9zOiB7XG5cdFx0Ly8gUG90ZW50aWFsbHkgY29tcGxleCBwc2V1ZG9zXG5cdFx0XCJub3RcIjogbWFya0Z1bmN0aW9uKGZ1bmN0aW9uKCBzZWxlY3RvciApIHtcblx0XHRcdC8vIFRyaW0gdGhlIHNlbGVjdG9yIHBhc3NlZCB0byBjb21waWxlXG5cdFx0XHQvLyB0byBhdm9pZCB0cmVhdGluZyBsZWFkaW5nIGFuZCB0cmFpbGluZ1xuXHRcdFx0Ly8gc3BhY2VzIGFzIGNvbWJpbmF0b3JzXG5cdFx0XHR2YXIgaW5wdXQgPSBbXSxcblx0XHRcdFx0cmVzdWx0cyA9IFtdLFxuXHRcdFx0XHRtYXRjaGVyID0gY29tcGlsZSggc2VsZWN0b3IucmVwbGFjZSggcnRyaW0sIFwiJDFcIiApICk7XG5cblx0XHRcdHJldHVybiBtYXRjaGVyWyBleHBhbmRvIF0gP1xuXHRcdFx0XHRtYXJrRnVuY3Rpb24oZnVuY3Rpb24oIHNlZWQsIG1hdGNoZXMsIGNvbnRleHQsIHhtbCApIHtcblx0XHRcdFx0XHR2YXIgZWxlbSxcblx0XHRcdFx0XHRcdHVubWF0Y2hlZCA9IG1hdGNoZXIoIHNlZWQsIG51bGwsIHhtbCwgW10gKSxcblx0XHRcdFx0XHRcdGkgPSBzZWVkLmxlbmd0aDtcblxuXHRcdFx0XHRcdC8vIE1hdGNoIGVsZW1lbnRzIHVubWF0Y2hlZCBieSBgbWF0Y2hlcmBcblx0XHRcdFx0XHR3aGlsZSAoIGktLSApIHtcblx0XHRcdFx0XHRcdGlmICggKGVsZW0gPSB1bm1hdGNoZWRbaV0pICkge1xuXHRcdFx0XHRcdFx0XHRzZWVkW2ldID0gIShtYXRjaGVzW2ldID0gZWxlbSk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9KSA6XG5cdFx0XHRcdGZ1bmN0aW9uKCBlbGVtLCBjb250ZXh0LCB4bWwgKSB7XG5cdFx0XHRcdFx0aW5wdXRbMF0gPSBlbGVtO1xuXHRcdFx0XHRcdG1hdGNoZXIoIGlucHV0LCBudWxsLCB4bWwsIHJlc3VsdHMgKTtcblx0XHRcdFx0XHQvLyBEb24ndCBrZWVwIHRoZSBlbGVtZW50IChpc3N1ZSAjMjk5KVxuXHRcdFx0XHRcdGlucHV0WzBdID0gbnVsbDtcblx0XHRcdFx0XHRyZXR1cm4gIXJlc3VsdHMucG9wKCk7XG5cdFx0XHRcdH07XG5cdFx0fSksXG5cblx0XHRcImhhc1wiOiBtYXJrRnVuY3Rpb24oZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0XHRyZXR1cm4gU2l6emxlKCBzZWxlY3RvciwgZWxlbSApLmxlbmd0aCA+IDA7XG5cdFx0XHR9O1xuXHRcdH0pLFxuXG5cdFx0XCJjb250YWluc1wiOiBtYXJrRnVuY3Rpb24oZnVuY3Rpb24oIHRleHQgKSB7XG5cdFx0XHR0ZXh0ID0gdGV4dC5yZXBsYWNlKCBydW5lc2NhcGUsIGZ1bmVzY2FwZSApO1xuXHRcdFx0cmV0dXJuIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0XHRyZXR1cm4gKCBlbGVtLnRleHRDb250ZW50IHx8IGVsZW0uaW5uZXJUZXh0IHx8IGdldFRleHQoIGVsZW0gKSApLmluZGV4T2YoIHRleHQgKSA+IC0xO1xuXHRcdFx0fTtcblx0XHR9KSxcblxuXHRcdC8vIFwiV2hldGhlciBhbiBlbGVtZW50IGlzIHJlcHJlc2VudGVkIGJ5IGEgOmxhbmcoKSBzZWxlY3RvclxuXHRcdC8vIGlzIGJhc2VkIHNvbGVseSBvbiB0aGUgZWxlbWVudCdzIGxhbmd1YWdlIHZhbHVlXG5cdFx0Ly8gYmVpbmcgZXF1YWwgdG8gdGhlIGlkZW50aWZpZXIgQyxcblx0XHQvLyBvciBiZWdpbm5pbmcgd2l0aCB0aGUgaWRlbnRpZmllciBDIGltbWVkaWF0ZWx5IGZvbGxvd2VkIGJ5IFwiLVwiLlxuXHRcdC8vIFRoZSBtYXRjaGluZyBvZiBDIGFnYWluc3QgdGhlIGVsZW1lbnQncyBsYW5ndWFnZSB2YWx1ZSBpcyBwZXJmb3JtZWQgY2FzZS1pbnNlbnNpdGl2ZWx5LlxuXHRcdC8vIFRoZSBpZGVudGlmaWVyIEMgZG9lcyBub3QgaGF2ZSB0byBiZSBhIHZhbGlkIGxhbmd1YWdlIG5hbWUuXCJcblx0XHQvLyBodHRwOi8vd3d3LnczLm9yZy9UUi9zZWxlY3RvcnMvI2xhbmctcHNldWRvXG5cdFx0XCJsYW5nXCI6IG1hcmtGdW5jdGlvbiggZnVuY3Rpb24oIGxhbmcgKSB7XG5cdFx0XHQvLyBsYW5nIHZhbHVlIG11c3QgYmUgYSB2YWxpZCBpZGVudGlmaWVyXG5cdFx0XHRpZiAoICFyaWRlbnRpZmllci50ZXN0KGxhbmcgfHwgXCJcIikgKSB7XG5cdFx0XHRcdFNpenpsZS5lcnJvciggXCJ1bnN1cHBvcnRlZCBsYW5nOiBcIiArIGxhbmcgKTtcblx0XHRcdH1cblx0XHRcdGxhbmcgPSBsYW5nLnJlcGxhY2UoIHJ1bmVzY2FwZSwgZnVuZXNjYXBlICkudG9Mb3dlckNhc2UoKTtcblx0XHRcdHJldHVybiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdFx0dmFyIGVsZW1MYW5nO1xuXHRcdFx0XHRkbyB7XG5cdFx0XHRcdFx0aWYgKCAoZWxlbUxhbmcgPSBkb2N1bWVudElzSFRNTCA/XG5cdFx0XHRcdFx0XHRlbGVtLmxhbmcgOlxuXHRcdFx0XHRcdFx0ZWxlbS5nZXRBdHRyaWJ1dGUoXCJ4bWw6bGFuZ1wiKSB8fCBlbGVtLmdldEF0dHJpYnV0ZShcImxhbmdcIikpICkge1xuXG5cdFx0XHRcdFx0XHRlbGVtTGFuZyA9IGVsZW1MYW5nLnRvTG93ZXJDYXNlKCk7XG5cdFx0XHRcdFx0XHRyZXR1cm4gZWxlbUxhbmcgPT09IGxhbmcgfHwgZWxlbUxhbmcuaW5kZXhPZiggbGFuZyArIFwiLVwiICkgPT09IDA7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9IHdoaWxlICggKGVsZW0gPSBlbGVtLnBhcmVudE5vZGUpICYmIGVsZW0ubm9kZVR5cGUgPT09IDEgKTtcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fTtcblx0XHR9KSxcblxuXHRcdC8vIE1pc2NlbGxhbmVvdXNcblx0XHRcInRhcmdldFwiOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHZhciBoYXNoID0gd2luZG93LmxvY2F0aW9uICYmIHdpbmRvdy5sb2NhdGlvbi5oYXNoO1xuXHRcdFx0cmV0dXJuIGhhc2ggJiYgaGFzaC5zbGljZSggMSApID09PSBlbGVtLmlkO1xuXHRcdH0sXG5cblx0XHRcInJvb3RcIjogZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRyZXR1cm4gZWxlbSA9PT0gZG9jRWxlbTtcblx0XHR9LFxuXG5cdFx0XCJmb2N1c1wiOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHJldHVybiBlbGVtID09PSBkb2N1bWVudC5hY3RpdmVFbGVtZW50ICYmICghZG9jdW1lbnQuaGFzRm9jdXMgfHwgZG9jdW1lbnQuaGFzRm9jdXMoKSkgJiYgISEoZWxlbS50eXBlIHx8IGVsZW0uaHJlZiB8fCB+ZWxlbS50YWJJbmRleCk7XG5cdFx0fSxcblxuXHRcdC8vIEJvb2xlYW4gcHJvcGVydGllc1xuXHRcdFwiZW5hYmxlZFwiOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHJldHVybiBlbGVtLmRpc2FibGVkID09PSBmYWxzZTtcblx0XHR9LFxuXG5cdFx0XCJkaXNhYmxlZFwiOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHJldHVybiBlbGVtLmRpc2FibGVkID09PSB0cnVlO1xuXHRcdH0sXG5cblx0XHRcImNoZWNrZWRcIjogZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHQvLyBJbiBDU1MzLCA6Y2hlY2tlZCBzaG91bGQgcmV0dXJuIGJvdGggY2hlY2tlZCBhbmQgc2VsZWN0ZWQgZWxlbWVudHNcblx0XHRcdC8vIGh0dHA6Ly93d3cudzMub3JnL1RSLzIwMTEvUkVDLWNzczMtc2VsZWN0b3JzLTIwMTEwOTI5LyNjaGVja2VkXG5cdFx0XHR2YXIgbm9kZU5hbWUgPSBlbGVtLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7XG5cdFx0XHRyZXR1cm4gKG5vZGVOYW1lID09PSBcImlucHV0XCIgJiYgISFlbGVtLmNoZWNrZWQpIHx8IChub2RlTmFtZSA9PT0gXCJvcHRpb25cIiAmJiAhIWVsZW0uc2VsZWN0ZWQpO1xuXHRcdH0sXG5cblx0XHRcInNlbGVjdGVkXCI6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0Ly8gQWNjZXNzaW5nIHRoaXMgcHJvcGVydHkgbWFrZXMgc2VsZWN0ZWQtYnktZGVmYXVsdFxuXHRcdFx0Ly8gb3B0aW9ucyBpbiBTYWZhcmkgd29yayBwcm9wZXJseVxuXHRcdFx0aWYgKCBlbGVtLnBhcmVudE5vZGUgKSB7XG5cdFx0XHRcdGVsZW0ucGFyZW50Tm9kZS5zZWxlY3RlZEluZGV4O1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZWxlbS5zZWxlY3RlZCA9PT0gdHJ1ZTtcblx0XHR9LFxuXG5cdFx0Ly8gQ29udGVudHNcblx0XHRcImVtcHR5XCI6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0Ly8gaHR0cDovL3d3dy53My5vcmcvVFIvc2VsZWN0b3JzLyNlbXB0eS1wc2V1ZG9cblx0XHRcdC8vIDplbXB0eSBpcyBuZWdhdGVkIGJ5IGVsZW1lbnQgKDEpIG9yIGNvbnRlbnQgbm9kZXMgKHRleHQ6IDM7IGNkYXRhOiA0OyBlbnRpdHkgcmVmOiA1KSxcblx0XHRcdC8vICAgYnV0IG5vdCBieSBvdGhlcnMgKGNvbW1lbnQ6IDg7IHByb2Nlc3NpbmcgaW5zdHJ1Y3Rpb246IDc7IGV0Yy4pXG5cdFx0XHQvLyBub2RlVHlwZSA8IDYgd29ya3MgYmVjYXVzZSBhdHRyaWJ1dGVzICgyKSBkbyBub3QgYXBwZWFyIGFzIGNoaWxkcmVuXG5cdFx0XHRmb3IgKCBlbGVtID0gZWxlbS5maXJzdENoaWxkOyBlbGVtOyBlbGVtID0gZWxlbS5uZXh0U2libGluZyApIHtcblx0XHRcdFx0aWYgKCBlbGVtLm5vZGVUeXBlIDwgNiApIHtcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0sXG5cblx0XHRcInBhcmVudFwiOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHJldHVybiAhRXhwci5wc2V1ZG9zW1wiZW1wdHlcIl0oIGVsZW0gKTtcblx0XHR9LFxuXG5cdFx0Ly8gRWxlbWVudC9pbnB1dCB0eXBlc1xuXHRcdFwiaGVhZGVyXCI6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0cmV0dXJuIHJoZWFkZXIudGVzdCggZWxlbS5ub2RlTmFtZSApO1xuXHRcdH0sXG5cblx0XHRcImlucHV0XCI6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0cmV0dXJuIHJpbnB1dHMudGVzdCggZWxlbS5ub2RlTmFtZSApO1xuXHRcdH0sXG5cblx0XHRcImJ1dHRvblwiOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHZhciBuYW1lID0gZWxlbS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpO1xuXHRcdFx0cmV0dXJuIG5hbWUgPT09IFwiaW5wdXRcIiAmJiBlbGVtLnR5cGUgPT09IFwiYnV0dG9uXCIgfHwgbmFtZSA9PT0gXCJidXR0b25cIjtcblx0XHR9LFxuXG5cdFx0XCJ0ZXh0XCI6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0dmFyIGF0dHI7XG5cdFx0XHRyZXR1cm4gZWxlbS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpID09PSBcImlucHV0XCIgJiZcblx0XHRcdFx0ZWxlbS50eXBlID09PSBcInRleHRcIiAmJlxuXG5cdFx0XHRcdC8vIFN1cHBvcnQ6IElFPDhcblx0XHRcdFx0Ly8gTmV3IEhUTUw1IGF0dHJpYnV0ZSB2YWx1ZXMgKGUuZy4sIFwic2VhcmNoXCIpIGFwcGVhciB3aXRoIGVsZW0udHlwZSA9PT0gXCJ0ZXh0XCJcblx0XHRcdFx0KCAoYXR0ciA9IGVsZW0uZ2V0QXR0cmlidXRlKFwidHlwZVwiKSkgPT0gbnVsbCB8fCBhdHRyLnRvTG93ZXJDYXNlKCkgPT09IFwidGV4dFwiICk7XG5cdFx0fSxcblxuXHRcdC8vIFBvc2l0aW9uLWluLWNvbGxlY3Rpb25cblx0XHRcImZpcnN0XCI6IGNyZWF0ZVBvc2l0aW9uYWxQc2V1ZG8oZnVuY3Rpb24oKSB7XG5cdFx0XHRyZXR1cm4gWyAwIF07XG5cdFx0fSksXG5cblx0XHRcImxhc3RcIjogY3JlYXRlUG9zaXRpb25hbFBzZXVkbyhmdW5jdGlvbiggbWF0Y2hJbmRleGVzLCBsZW5ndGggKSB7XG5cdFx0XHRyZXR1cm4gWyBsZW5ndGggLSAxIF07XG5cdFx0fSksXG5cblx0XHRcImVxXCI6IGNyZWF0ZVBvc2l0aW9uYWxQc2V1ZG8oZnVuY3Rpb24oIG1hdGNoSW5kZXhlcywgbGVuZ3RoLCBhcmd1bWVudCApIHtcblx0XHRcdHJldHVybiBbIGFyZ3VtZW50IDwgMCA/IGFyZ3VtZW50ICsgbGVuZ3RoIDogYXJndW1lbnQgXTtcblx0XHR9KSxcblxuXHRcdFwiZXZlblwiOiBjcmVhdGVQb3NpdGlvbmFsUHNldWRvKGZ1bmN0aW9uKCBtYXRjaEluZGV4ZXMsIGxlbmd0aCApIHtcblx0XHRcdHZhciBpID0gMDtcblx0XHRcdGZvciAoIDsgaSA8IGxlbmd0aDsgaSArPSAyICkge1xuXHRcdFx0XHRtYXRjaEluZGV4ZXMucHVzaCggaSApO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIG1hdGNoSW5kZXhlcztcblx0XHR9KSxcblxuXHRcdFwib2RkXCI6IGNyZWF0ZVBvc2l0aW9uYWxQc2V1ZG8oZnVuY3Rpb24oIG1hdGNoSW5kZXhlcywgbGVuZ3RoICkge1xuXHRcdFx0dmFyIGkgPSAxO1xuXHRcdFx0Zm9yICggOyBpIDwgbGVuZ3RoOyBpICs9IDIgKSB7XG5cdFx0XHRcdG1hdGNoSW5kZXhlcy5wdXNoKCBpICk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gbWF0Y2hJbmRleGVzO1xuXHRcdH0pLFxuXG5cdFx0XCJsdFwiOiBjcmVhdGVQb3NpdGlvbmFsUHNldWRvKGZ1bmN0aW9uKCBtYXRjaEluZGV4ZXMsIGxlbmd0aCwgYXJndW1lbnQgKSB7XG5cdFx0XHR2YXIgaSA9IGFyZ3VtZW50IDwgMCA/IGFyZ3VtZW50ICsgbGVuZ3RoIDogYXJndW1lbnQ7XG5cdFx0XHRmb3IgKCA7IC0taSA+PSAwOyApIHtcblx0XHRcdFx0bWF0Y2hJbmRleGVzLnB1c2goIGkgKTtcblx0XHRcdH1cblx0XHRcdHJldHVybiBtYXRjaEluZGV4ZXM7XG5cdFx0fSksXG5cblx0XHRcImd0XCI6IGNyZWF0ZVBvc2l0aW9uYWxQc2V1ZG8oZnVuY3Rpb24oIG1hdGNoSW5kZXhlcywgbGVuZ3RoLCBhcmd1bWVudCApIHtcblx0XHRcdHZhciBpID0gYXJndW1lbnQgPCAwID8gYXJndW1lbnQgKyBsZW5ndGggOiBhcmd1bWVudDtcblx0XHRcdGZvciAoIDsgKytpIDwgbGVuZ3RoOyApIHtcblx0XHRcdFx0bWF0Y2hJbmRleGVzLnB1c2goIGkgKTtcblx0XHRcdH1cblx0XHRcdHJldHVybiBtYXRjaEluZGV4ZXM7XG5cdFx0fSlcblx0fVxufTtcblxuRXhwci5wc2V1ZG9zW1wibnRoXCJdID0gRXhwci5wc2V1ZG9zW1wiZXFcIl07XG5cbi8vIEFkZCBidXR0b24vaW5wdXQgdHlwZSBwc2V1ZG9zXG5mb3IgKCBpIGluIHsgcmFkaW86IHRydWUsIGNoZWNrYm94OiB0cnVlLCBmaWxlOiB0cnVlLCBwYXNzd29yZDogdHJ1ZSwgaW1hZ2U6IHRydWUgfSApIHtcblx0RXhwci5wc2V1ZG9zWyBpIF0gPSBjcmVhdGVJbnB1dFBzZXVkbyggaSApO1xufVxuZm9yICggaSBpbiB7IHN1Ym1pdDogdHJ1ZSwgcmVzZXQ6IHRydWUgfSApIHtcblx0RXhwci5wc2V1ZG9zWyBpIF0gPSBjcmVhdGVCdXR0b25Qc2V1ZG8oIGkgKTtcbn1cblxuLy8gRWFzeSBBUEkgZm9yIGNyZWF0aW5nIG5ldyBzZXRGaWx0ZXJzXG5mdW5jdGlvbiBzZXRGaWx0ZXJzKCkge31cbnNldEZpbHRlcnMucHJvdG90eXBlID0gRXhwci5maWx0ZXJzID0gRXhwci5wc2V1ZG9zO1xuRXhwci5zZXRGaWx0ZXJzID0gbmV3IHNldEZpbHRlcnMoKTtcblxudG9rZW5pemUgPSBTaXp6bGUudG9rZW5pemUgPSBmdW5jdGlvbiggc2VsZWN0b3IsIHBhcnNlT25seSApIHtcblx0dmFyIG1hdGNoZWQsIG1hdGNoLCB0b2tlbnMsIHR5cGUsXG5cdFx0c29GYXIsIGdyb3VwcywgcHJlRmlsdGVycyxcblx0XHRjYWNoZWQgPSB0b2tlbkNhY2hlWyBzZWxlY3RvciArIFwiIFwiIF07XG5cblx0aWYgKCBjYWNoZWQgKSB7XG5cdFx0cmV0dXJuIHBhcnNlT25seSA/IDAgOiBjYWNoZWQuc2xpY2UoIDAgKTtcblx0fVxuXG5cdHNvRmFyID0gc2VsZWN0b3I7XG5cdGdyb3VwcyA9IFtdO1xuXHRwcmVGaWx0ZXJzID0gRXhwci5wcmVGaWx0ZXI7XG5cblx0d2hpbGUgKCBzb0ZhciApIHtcblxuXHRcdC8vIENvbW1hIGFuZCBmaXJzdCBydW5cblx0XHRpZiAoICFtYXRjaGVkIHx8IChtYXRjaCA9IHJjb21tYS5leGVjKCBzb0ZhciApKSApIHtcblx0XHRcdGlmICggbWF0Y2ggKSB7XG5cdFx0XHRcdC8vIERvbid0IGNvbnN1bWUgdHJhaWxpbmcgY29tbWFzIGFzIHZhbGlkXG5cdFx0XHRcdHNvRmFyID0gc29GYXIuc2xpY2UoIG1hdGNoWzBdLmxlbmd0aCApIHx8IHNvRmFyO1xuXHRcdFx0fVxuXHRcdFx0Z3JvdXBzLnB1c2goICh0b2tlbnMgPSBbXSkgKTtcblx0XHR9XG5cblx0XHRtYXRjaGVkID0gZmFsc2U7XG5cblx0XHQvLyBDb21iaW5hdG9yc1xuXHRcdGlmICggKG1hdGNoID0gcmNvbWJpbmF0b3JzLmV4ZWMoIHNvRmFyICkpICkge1xuXHRcdFx0bWF0Y2hlZCA9IG1hdGNoLnNoaWZ0KCk7XG5cdFx0XHR0b2tlbnMucHVzaCh7XG5cdFx0XHRcdHZhbHVlOiBtYXRjaGVkLFxuXHRcdFx0XHQvLyBDYXN0IGRlc2NlbmRhbnQgY29tYmluYXRvcnMgdG8gc3BhY2Vcblx0XHRcdFx0dHlwZTogbWF0Y2hbMF0ucmVwbGFjZSggcnRyaW0sIFwiIFwiIClcblx0XHRcdH0pO1xuXHRcdFx0c29GYXIgPSBzb0Zhci5zbGljZSggbWF0Y2hlZC5sZW5ndGggKTtcblx0XHR9XG5cblx0XHQvLyBGaWx0ZXJzXG5cdFx0Zm9yICggdHlwZSBpbiBFeHByLmZpbHRlciApIHtcblx0XHRcdGlmICggKG1hdGNoID0gbWF0Y2hFeHByWyB0eXBlIF0uZXhlYyggc29GYXIgKSkgJiYgKCFwcmVGaWx0ZXJzWyB0eXBlIF0gfHxcblx0XHRcdFx0KG1hdGNoID0gcHJlRmlsdGVyc1sgdHlwZSBdKCBtYXRjaCApKSkgKSB7XG5cdFx0XHRcdG1hdGNoZWQgPSBtYXRjaC5zaGlmdCgpO1xuXHRcdFx0XHR0b2tlbnMucHVzaCh7XG5cdFx0XHRcdFx0dmFsdWU6IG1hdGNoZWQsXG5cdFx0XHRcdFx0dHlwZTogdHlwZSxcblx0XHRcdFx0XHRtYXRjaGVzOiBtYXRjaFxuXHRcdFx0XHR9KTtcblx0XHRcdFx0c29GYXIgPSBzb0Zhci5zbGljZSggbWF0Y2hlZC5sZW5ndGggKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoICFtYXRjaGVkICkge1xuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHR9XG5cblx0Ly8gUmV0dXJuIHRoZSBsZW5ndGggb2YgdGhlIGludmFsaWQgZXhjZXNzXG5cdC8vIGlmIHdlJ3JlIGp1c3QgcGFyc2luZ1xuXHQvLyBPdGhlcndpc2UsIHRocm93IGFuIGVycm9yIG9yIHJldHVybiB0b2tlbnNcblx0cmV0dXJuIHBhcnNlT25seSA/XG5cdFx0c29GYXIubGVuZ3RoIDpcblx0XHRzb0ZhciA/XG5cdFx0XHRTaXp6bGUuZXJyb3IoIHNlbGVjdG9yICkgOlxuXHRcdFx0Ly8gQ2FjaGUgdGhlIHRva2Vuc1xuXHRcdFx0dG9rZW5DYWNoZSggc2VsZWN0b3IsIGdyb3VwcyApLnNsaWNlKCAwICk7XG59O1xuXG5mdW5jdGlvbiB0b1NlbGVjdG9yKCB0b2tlbnMgKSB7XG5cdHZhciBpID0gMCxcblx0XHRsZW4gPSB0b2tlbnMubGVuZ3RoLFxuXHRcdHNlbGVjdG9yID0gXCJcIjtcblx0Zm9yICggOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0c2VsZWN0b3IgKz0gdG9rZW5zW2ldLnZhbHVlO1xuXHR9XG5cdHJldHVybiBzZWxlY3Rvcjtcbn1cblxuZnVuY3Rpb24gYWRkQ29tYmluYXRvciggbWF0Y2hlciwgY29tYmluYXRvciwgYmFzZSApIHtcblx0dmFyIGRpciA9IGNvbWJpbmF0b3IuZGlyLFxuXHRcdGNoZWNrTm9uRWxlbWVudHMgPSBiYXNlICYmIGRpciA9PT0gXCJwYXJlbnROb2RlXCIsXG5cdFx0ZG9uZU5hbWUgPSBkb25lKys7XG5cblx0cmV0dXJuIGNvbWJpbmF0b3IuZmlyc3QgP1xuXHRcdC8vIENoZWNrIGFnYWluc3QgY2xvc2VzdCBhbmNlc3Rvci9wcmVjZWRpbmcgZWxlbWVudFxuXHRcdGZ1bmN0aW9uKCBlbGVtLCBjb250ZXh0LCB4bWwgKSB7XG5cdFx0XHR3aGlsZSAoIChlbGVtID0gZWxlbVsgZGlyIF0pICkge1xuXHRcdFx0XHRpZiAoIGVsZW0ubm9kZVR5cGUgPT09IDEgfHwgY2hlY2tOb25FbGVtZW50cyApIHtcblx0XHRcdFx0XHRyZXR1cm4gbWF0Y2hlciggZWxlbSwgY29udGV4dCwgeG1sICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9IDpcblxuXHRcdC8vIENoZWNrIGFnYWluc3QgYWxsIGFuY2VzdG9yL3ByZWNlZGluZyBlbGVtZW50c1xuXHRcdGZ1bmN0aW9uKCBlbGVtLCBjb250ZXh0LCB4bWwgKSB7XG5cdFx0XHR2YXIgb2xkQ2FjaGUsIG91dGVyQ2FjaGUsXG5cdFx0XHRcdG5ld0NhY2hlID0gWyBkaXJydW5zLCBkb25lTmFtZSBdO1xuXG5cdFx0XHQvLyBXZSBjYW4ndCBzZXQgYXJiaXRyYXJ5IGRhdGEgb24gWE1MIG5vZGVzLCBzbyB0aGV5IGRvbid0IGJlbmVmaXQgZnJvbSBkaXIgY2FjaGluZ1xuXHRcdFx0aWYgKCB4bWwgKSB7XG5cdFx0XHRcdHdoaWxlICggKGVsZW0gPSBlbGVtWyBkaXIgXSkgKSB7XG5cdFx0XHRcdFx0aWYgKCBlbGVtLm5vZGVUeXBlID09PSAxIHx8IGNoZWNrTm9uRWxlbWVudHMgKSB7XG5cdFx0XHRcdFx0XHRpZiAoIG1hdGNoZXIoIGVsZW0sIGNvbnRleHQsIHhtbCApICkge1xuXHRcdFx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHdoaWxlICggKGVsZW0gPSBlbGVtWyBkaXIgXSkgKSB7XG5cdFx0XHRcdFx0aWYgKCBlbGVtLm5vZGVUeXBlID09PSAxIHx8IGNoZWNrTm9uRWxlbWVudHMgKSB7XG5cdFx0XHRcdFx0XHRvdXRlckNhY2hlID0gZWxlbVsgZXhwYW5kbyBdIHx8IChlbGVtWyBleHBhbmRvIF0gPSB7fSk7XG5cdFx0XHRcdFx0XHRpZiAoIChvbGRDYWNoZSA9IG91dGVyQ2FjaGVbIGRpciBdKSAmJlxuXHRcdFx0XHRcdFx0XHRvbGRDYWNoZVsgMCBdID09PSBkaXJydW5zICYmIG9sZENhY2hlWyAxIF0gPT09IGRvbmVOYW1lICkge1xuXG5cdFx0XHRcdFx0XHRcdC8vIEFzc2lnbiB0byBuZXdDYWNoZSBzbyByZXN1bHRzIGJhY2stcHJvcGFnYXRlIHRvIHByZXZpb3VzIGVsZW1lbnRzXG5cdFx0XHRcdFx0XHRcdHJldHVybiAobmV3Q2FjaGVbIDIgXSA9IG9sZENhY2hlWyAyIF0pO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0Ly8gUmV1c2UgbmV3Y2FjaGUgc28gcmVzdWx0cyBiYWNrLXByb3BhZ2F0ZSB0byBwcmV2aW91cyBlbGVtZW50c1xuXHRcdFx0XHRcdFx0XHRvdXRlckNhY2hlWyBkaXIgXSA9IG5ld0NhY2hlO1xuXG5cdFx0XHRcdFx0XHRcdC8vIEEgbWF0Y2ggbWVhbnMgd2UncmUgZG9uZTsgYSBmYWlsIG1lYW5zIHdlIGhhdmUgdG8ga2VlcCBjaGVja2luZ1xuXHRcdFx0XHRcdFx0XHRpZiAoIChuZXdDYWNoZVsgMiBdID0gbWF0Y2hlciggZWxlbSwgY29udGV4dCwgeG1sICkpICkge1xuXHRcdFx0XHRcdFx0XHRcdHJldHVybiB0cnVlO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fTtcbn1cblxuZnVuY3Rpb24gZWxlbWVudE1hdGNoZXIoIG1hdGNoZXJzICkge1xuXHRyZXR1cm4gbWF0Y2hlcnMubGVuZ3RoID4gMSA/XG5cdFx0ZnVuY3Rpb24oIGVsZW0sIGNvbnRleHQsIHhtbCApIHtcblx0XHRcdHZhciBpID0gbWF0Y2hlcnMubGVuZ3RoO1xuXHRcdFx0d2hpbGUgKCBpLS0gKSB7XG5cdFx0XHRcdGlmICggIW1hdGNoZXJzW2ldKCBlbGVtLCBjb250ZXh0LCB4bWwgKSApIHtcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0gOlxuXHRcdG1hdGNoZXJzWzBdO1xufVxuXG5mdW5jdGlvbiBtdWx0aXBsZUNvbnRleHRzKCBzZWxlY3RvciwgY29udGV4dHMsIHJlc3VsdHMgKSB7XG5cdHZhciBpID0gMCxcblx0XHRsZW4gPSBjb250ZXh0cy5sZW5ndGg7XG5cdGZvciAoIDsgaSA8IGxlbjsgaSsrICkge1xuXHRcdFNpenpsZSggc2VsZWN0b3IsIGNvbnRleHRzW2ldLCByZXN1bHRzICk7XG5cdH1cblx0cmV0dXJuIHJlc3VsdHM7XG59XG5cbmZ1bmN0aW9uIGNvbmRlbnNlKCB1bm1hdGNoZWQsIG1hcCwgZmlsdGVyLCBjb250ZXh0LCB4bWwgKSB7XG5cdHZhciBlbGVtLFxuXHRcdG5ld1VubWF0Y2hlZCA9IFtdLFxuXHRcdGkgPSAwLFxuXHRcdGxlbiA9IHVubWF0Y2hlZC5sZW5ndGgsXG5cdFx0bWFwcGVkID0gbWFwICE9IG51bGw7XG5cblx0Zm9yICggOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0aWYgKCAoZWxlbSA9IHVubWF0Y2hlZFtpXSkgKSB7XG5cdFx0XHRpZiAoICFmaWx0ZXIgfHwgZmlsdGVyKCBlbGVtLCBjb250ZXh0LCB4bWwgKSApIHtcblx0XHRcdFx0bmV3VW5tYXRjaGVkLnB1c2goIGVsZW0gKTtcblx0XHRcdFx0aWYgKCBtYXBwZWQgKSB7XG5cdFx0XHRcdFx0bWFwLnB1c2goIGkgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdHJldHVybiBuZXdVbm1hdGNoZWQ7XG59XG5cbmZ1bmN0aW9uIHNldE1hdGNoZXIoIHByZUZpbHRlciwgc2VsZWN0b3IsIG1hdGNoZXIsIHBvc3RGaWx0ZXIsIHBvc3RGaW5kZXIsIHBvc3RTZWxlY3RvciApIHtcblx0aWYgKCBwb3N0RmlsdGVyICYmICFwb3N0RmlsdGVyWyBleHBhbmRvIF0gKSB7XG5cdFx0cG9zdEZpbHRlciA9IHNldE1hdGNoZXIoIHBvc3RGaWx0ZXIgKTtcblx0fVxuXHRpZiAoIHBvc3RGaW5kZXIgJiYgIXBvc3RGaW5kZXJbIGV4cGFuZG8gXSApIHtcblx0XHRwb3N0RmluZGVyID0gc2V0TWF0Y2hlciggcG9zdEZpbmRlciwgcG9zdFNlbGVjdG9yICk7XG5cdH1cblx0cmV0dXJuIG1hcmtGdW5jdGlvbihmdW5jdGlvbiggc2VlZCwgcmVzdWx0cywgY29udGV4dCwgeG1sICkge1xuXHRcdHZhciB0ZW1wLCBpLCBlbGVtLFxuXHRcdFx0cHJlTWFwID0gW10sXG5cdFx0XHRwb3N0TWFwID0gW10sXG5cdFx0XHRwcmVleGlzdGluZyA9IHJlc3VsdHMubGVuZ3RoLFxuXG5cdFx0XHQvLyBHZXQgaW5pdGlhbCBlbGVtZW50cyBmcm9tIHNlZWQgb3IgY29udGV4dFxuXHRcdFx0ZWxlbXMgPSBzZWVkIHx8IG11bHRpcGxlQ29udGV4dHMoIHNlbGVjdG9yIHx8IFwiKlwiLCBjb250ZXh0Lm5vZGVUeXBlID8gWyBjb250ZXh0IF0gOiBjb250ZXh0LCBbXSApLFxuXG5cdFx0XHQvLyBQcmVmaWx0ZXIgdG8gZ2V0IG1hdGNoZXIgaW5wdXQsIHByZXNlcnZpbmcgYSBtYXAgZm9yIHNlZWQtcmVzdWx0cyBzeW5jaHJvbml6YXRpb25cblx0XHRcdG1hdGNoZXJJbiA9IHByZUZpbHRlciAmJiAoIHNlZWQgfHwgIXNlbGVjdG9yICkgP1xuXHRcdFx0XHRjb25kZW5zZSggZWxlbXMsIHByZU1hcCwgcHJlRmlsdGVyLCBjb250ZXh0LCB4bWwgKSA6XG5cdFx0XHRcdGVsZW1zLFxuXG5cdFx0XHRtYXRjaGVyT3V0ID0gbWF0Y2hlciA/XG5cdFx0XHRcdC8vIElmIHdlIGhhdmUgYSBwb3N0RmluZGVyLCBvciBmaWx0ZXJlZCBzZWVkLCBvciBub24tc2VlZCBwb3N0RmlsdGVyIG9yIHByZWV4aXN0aW5nIHJlc3VsdHMsXG5cdFx0XHRcdHBvc3RGaW5kZXIgfHwgKCBzZWVkID8gcHJlRmlsdGVyIDogcHJlZXhpc3RpbmcgfHwgcG9zdEZpbHRlciApID9cblxuXHRcdFx0XHRcdC8vIC4uLmludGVybWVkaWF0ZSBwcm9jZXNzaW5nIGlzIG5lY2Vzc2FyeVxuXHRcdFx0XHRcdFtdIDpcblxuXHRcdFx0XHRcdC8vIC4uLm90aGVyd2lzZSB1c2UgcmVzdWx0cyBkaXJlY3RseVxuXHRcdFx0XHRcdHJlc3VsdHMgOlxuXHRcdFx0XHRtYXRjaGVySW47XG5cblx0XHQvLyBGaW5kIHByaW1hcnkgbWF0Y2hlc1xuXHRcdGlmICggbWF0Y2hlciApIHtcblx0XHRcdG1hdGNoZXIoIG1hdGNoZXJJbiwgbWF0Y2hlck91dCwgY29udGV4dCwgeG1sICk7XG5cdFx0fVxuXG5cdFx0Ly8gQXBwbHkgcG9zdEZpbHRlclxuXHRcdGlmICggcG9zdEZpbHRlciApIHtcblx0XHRcdHRlbXAgPSBjb25kZW5zZSggbWF0Y2hlck91dCwgcG9zdE1hcCApO1xuXHRcdFx0cG9zdEZpbHRlciggdGVtcCwgW10sIGNvbnRleHQsIHhtbCApO1xuXG5cdFx0XHQvLyBVbi1tYXRjaCBmYWlsaW5nIGVsZW1lbnRzIGJ5IG1vdmluZyB0aGVtIGJhY2sgdG8gbWF0Y2hlckluXG5cdFx0XHRpID0gdGVtcC5sZW5ndGg7XG5cdFx0XHR3aGlsZSAoIGktLSApIHtcblx0XHRcdFx0aWYgKCAoZWxlbSA9IHRlbXBbaV0pICkge1xuXHRcdFx0XHRcdG1hdGNoZXJPdXRbIHBvc3RNYXBbaV0gXSA9ICEobWF0Y2hlckluWyBwb3N0TWFwW2ldIF0gPSBlbGVtKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGlmICggc2VlZCApIHtcblx0XHRcdGlmICggcG9zdEZpbmRlciB8fCBwcmVGaWx0ZXIgKSB7XG5cdFx0XHRcdGlmICggcG9zdEZpbmRlciApIHtcblx0XHRcdFx0XHQvLyBHZXQgdGhlIGZpbmFsIG1hdGNoZXJPdXQgYnkgY29uZGVuc2luZyB0aGlzIGludGVybWVkaWF0ZSBpbnRvIHBvc3RGaW5kZXIgY29udGV4dHNcblx0XHRcdFx0XHR0ZW1wID0gW107XG5cdFx0XHRcdFx0aSA9IG1hdGNoZXJPdXQubGVuZ3RoO1xuXHRcdFx0XHRcdHdoaWxlICggaS0tICkge1xuXHRcdFx0XHRcdFx0aWYgKCAoZWxlbSA9IG1hdGNoZXJPdXRbaV0pICkge1xuXHRcdFx0XHRcdFx0XHQvLyBSZXN0b3JlIG1hdGNoZXJJbiBzaW5jZSBlbGVtIGlzIG5vdCB5ZXQgYSBmaW5hbCBtYXRjaFxuXHRcdFx0XHRcdFx0XHR0ZW1wLnB1c2goIChtYXRjaGVySW5baV0gPSBlbGVtKSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRwb3N0RmluZGVyKCBudWxsLCAobWF0Y2hlck91dCA9IFtdKSwgdGVtcCwgeG1sICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBNb3ZlIG1hdGNoZWQgZWxlbWVudHMgZnJvbSBzZWVkIHRvIHJlc3VsdHMgdG8ga2VlcCB0aGVtIHN5bmNocm9uaXplZFxuXHRcdFx0XHRpID0gbWF0Y2hlck91dC5sZW5ndGg7XG5cdFx0XHRcdHdoaWxlICggaS0tICkge1xuXHRcdFx0XHRcdGlmICggKGVsZW0gPSBtYXRjaGVyT3V0W2ldKSAmJlxuXHRcdFx0XHRcdFx0KHRlbXAgPSBwb3N0RmluZGVyID8gaW5kZXhPZiggc2VlZCwgZWxlbSApIDogcHJlTWFwW2ldKSA+IC0xICkge1xuXG5cdFx0XHRcdFx0XHRzZWVkW3RlbXBdID0gIShyZXN1bHRzW3RlbXBdID0gZWxlbSk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHQvLyBBZGQgZWxlbWVudHMgdG8gcmVzdWx0cywgdGhyb3VnaCBwb3N0RmluZGVyIGlmIGRlZmluZWRcblx0XHR9IGVsc2Uge1xuXHRcdFx0bWF0Y2hlck91dCA9IGNvbmRlbnNlKFxuXHRcdFx0XHRtYXRjaGVyT3V0ID09PSByZXN1bHRzID9cblx0XHRcdFx0XHRtYXRjaGVyT3V0LnNwbGljZSggcHJlZXhpc3RpbmcsIG1hdGNoZXJPdXQubGVuZ3RoICkgOlxuXHRcdFx0XHRcdG1hdGNoZXJPdXRcblx0XHRcdCk7XG5cdFx0XHRpZiAoIHBvc3RGaW5kZXIgKSB7XG5cdFx0XHRcdHBvc3RGaW5kZXIoIG51bGwsIHJlc3VsdHMsIG1hdGNoZXJPdXQsIHhtbCApO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0cHVzaC5hcHBseSggcmVzdWx0cywgbWF0Y2hlck91dCApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSk7XG59XG5cbmZ1bmN0aW9uIG1hdGNoZXJGcm9tVG9rZW5zKCB0b2tlbnMgKSB7XG5cdHZhciBjaGVja0NvbnRleHQsIG1hdGNoZXIsIGosXG5cdFx0bGVuID0gdG9rZW5zLmxlbmd0aCxcblx0XHRsZWFkaW5nUmVsYXRpdmUgPSBFeHByLnJlbGF0aXZlWyB0b2tlbnNbMF0udHlwZSBdLFxuXHRcdGltcGxpY2l0UmVsYXRpdmUgPSBsZWFkaW5nUmVsYXRpdmUgfHwgRXhwci5yZWxhdGl2ZVtcIiBcIl0sXG5cdFx0aSA9IGxlYWRpbmdSZWxhdGl2ZSA/IDEgOiAwLFxuXG5cdFx0Ly8gVGhlIGZvdW5kYXRpb25hbCBtYXRjaGVyIGVuc3VyZXMgdGhhdCBlbGVtZW50cyBhcmUgcmVhY2hhYmxlIGZyb20gdG9wLWxldmVsIGNvbnRleHQocylcblx0XHRtYXRjaENvbnRleHQgPSBhZGRDb21iaW5hdG9yKCBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHJldHVybiBlbGVtID09PSBjaGVja0NvbnRleHQ7XG5cdFx0fSwgaW1wbGljaXRSZWxhdGl2ZSwgdHJ1ZSApLFxuXHRcdG1hdGNoQW55Q29udGV4dCA9IGFkZENvbWJpbmF0b3IoIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0cmV0dXJuIGluZGV4T2YoIGNoZWNrQ29udGV4dCwgZWxlbSApID4gLTE7XG5cdFx0fSwgaW1wbGljaXRSZWxhdGl2ZSwgdHJ1ZSApLFxuXHRcdG1hdGNoZXJzID0gWyBmdW5jdGlvbiggZWxlbSwgY29udGV4dCwgeG1sICkge1xuXHRcdFx0dmFyIHJldCA9ICggIWxlYWRpbmdSZWxhdGl2ZSAmJiAoIHhtbCB8fCBjb250ZXh0ICE9PSBvdXRlcm1vc3RDb250ZXh0ICkgKSB8fCAoXG5cdFx0XHRcdChjaGVja0NvbnRleHQgPSBjb250ZXh0KS5ub2RlVHlwZSA/XG5cdFx0XHRcdFx0bWF0Y2hDb250ZXh0KCBlbGVtLCBjb250ZXh0LCB4bWwgKSA6XG5cdFx0XHRcdFx0bWF0Y2hBbnlDb250ZXh0KCBlbGVtLCBjb250ZXh0LCB4bWwgKSApO1xuXHRcdFx0Ly8gQXZvaWQgaGFuZ2luZyBvbnRvIGVsZW1lbnQgKGlzc3VlICMyOTkpXG5cdFx0XHRjaGVja0NvbnRleHQgPSBudWxsO1xuXHRcdFx0cmV0dXJuIHJldDtcblx0XHR9IF07XG5cblx0Zm9yICggOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0aWYgKCAobWF0Y2hlciA9IEV4cHIucmVsYXRpdmVbIHRva2Vuc1tpXS50eXBlIF0pICkge1xuXHRcdFx0bWF0Y2hlcnMgPSBbIGFkZENvbWJpbmF0b3IoZWxlbWVudE1hdGNoZXIoIG1hdGNoZXJzICksIG1hdGNoZXIpIF07XG5cdFx0fSBlbHNlIHtcblx0XHRcdG1hdGNoZXIgPSBFeHByLmZpbHRlclsgdG9rZW5zW2ldLnR5cGUgXS5hcHBseSggbnVsbCwgdG9rZW5zW2ldLm1hdGNoZXMgKTtcblxuXHRcdFx0Ly8gUmV0dXJuIHNwZWNpYWwgdXBvbiBzZWVpbmcgYSBwb3NpdGlvbmFsIG1hdGNoZXJcblx0XHRcdGlmICggbWF0Y2hlclsgZXhwYW5kbyBdICkge1xuXHRcdFx0XHQvLyBGaW5kIHRoZSBuZXh0IHJlbGF0aXZlIG9wZXJhdG9yIChpZiBhbnkpIGZvciBwcm9wZXIgaGFuZGxpbmdcblx0XHRcdFx0aiA9ICsraTtcblx0XHRcdFx0Zm9yICggOyBqIDwgbGVuOyBqKysgKSB7XG5cdFx0XHRcdFx0aWYgKCBFeHByLnJlbGF0aXZlWyB0b2tlbnNbal0udHlwZSBdICkge1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiBzZXRNYXRjaGVyKFxuXHRcdFx0XHRcdGkgPiAxICYmIGVsZW1lbnRNYXRjaGVyKCBtYXRjaGVycyApLFxuXHRcdFx0XHRcdGkgPiAxICYmIHRvU2VsZWN0b3IoXG5cdFx0XHRcdFx0XHQvLyBJZiB0aGUgcHJlY2VkaW5nIHRva2VuIHdhcyBhIGRlc2NlbmRhbnQgY29tYmluYXRvciwgaW5zZXJ0IGFuIGltcGxpY2l0IGFueS1lbGVtZW50IGAqYFxuXHRcdFx0XHRcdFx0dG9rZW5zLnNsaWNlKCAwLCBpIC0gMSApLmNvbmNhdCh7IHZhbHVlOiB0b2tlbnNbIGkgLSAyIF0udHlwZSA9PT0gXCIgXCIgPyBcIipcIiA6IFwiXCIgfSlcblx0XHRcdFx0XHQpLnJlcGxhY2UoIHJ0cmltLCBcIiQxXCIgKSxcblx0XHRcdFx0XHRtYXRjaGVyLFxuXHRcdFx0XHRcdGkgPCBqICYmIG1hdGNoZXJGcm9tVG9rZW5zKCB0b2tlbnMuc2xpY2UoIGksIGogKSApLFxuXHRcdFx0XHRcdGogPCBsZW4gJiYgbWF0Y2hlckZyb21Ub2tlbnMoICh0b2tlbnMgPSB0b2tlbnMuc2xpY2UoIGogKSkgKSxcblx0XHRcdFx0XHRqIDwgbGVuICYmIHRvU2VsZWN0b3IoIHRva2VucyApXG5cdFx0XHRcdCk7XG5cdFx0XHR9XG5cdFx0XHRtYXRjaGVycy5wdXNoKCBtYXRjaGVyICk7XG5cdFx0fVxuXHR9XG5cblx0cmV0dXJuIGVsZW1lbnRNYXRjaGVyKCBtYXRjaGVycyApO1xufVxuXG5mdW5jdGlvbiBtYXRjaGVyRnJvbUdyb3VwTWF0Y2hlcnMoIGVsZW1lbnRNYXRjaGVycywgc2V0TWF0Y2hlcnMgKSB7XG5cdHZhciBieVNldCA9IHNldE1hdGNoZXJzLmxlbmd0aCA+IDAsXG5cdFx0YnlFbGVtZW50ID0gZWxlbWVudE1hdGNoZXJzLmxlbmd0aCA+IDAsXG5cdFx0c3VwZXJNYXRjaGVyID0gZnVuY3Rpb24oIHNlZWQsIGNvbnRleHQsIHhtbCwgcmVzdWx0cywgb3V0ZXJtb3N0ICkge1xuXHRcdFx0dmFyIGVsZW0sIGosIG1hdGNoZXIsXG5cdFx0XHRcdG1hdGNoZWRDb3VudCA9IDAsXG5cdFx0XHRcdGkgPSBcIjBcIixcblx0XHRcdFx0dW5tYXRjaGVkID0gc2VlZCAmJiBbXSxcblx0XHRcdFx0c2V0TWF0Y2hlZCA9IFtdLFxuXHRcdFx0XHRjb250ZXh0QmFja3VwID0gb3V0ZXJtb3N0Q29udGV4dCxcblx0XHRcdFx0Ly8gV2UgbXVzdCBhbHdheXMgaGF2ZSBlaXRoZXIgc2VlZCBlbGVtZW50cyBvciBvdXRlcm1vc3QgY29udGV4dFxuXHRcdFx0XHRlbGVtcyA9IHNlZWQgfHwgYnlFbGVtZW50ICYmIEV4cHIuZmluZFtcIlRBR1wiXSggXCIqXCIsIG91dGVybW9zdCApLFxuXHRcdFx0XHQvLyBVc2UgaW50ZWdlciBkaXJydW5zIGlmZiB0aGlzIGlzIHRoZSBvdXRlcm1vc3QgbWF0Y2hlclxuXHRcdFx0XHRkaXJydW5zVW5pcXVlID0gKGRpcnJ1bnMgKz0gY29udGV4dEJhY2t1cCA9PSBudWxsID8gMSA6IE1hdGgucmFuZG9tKCkgfHwgMC4xKSxcblx0XHRcdFx0bGVuID0gZWxlbXMubGVuZ3RoO1xuXG5cdFx0XHRpZiAoIG91dGVybW9zdCApIHtcblx0XHRcdFx0b3V0ZXJtb3N0Q29udGV4dCA9IGNvbnRleHQgIT09IGRvY3VtZW50ICYmIGNvbnRleHQ7XG5cdFx0XHR9XG5cblx0XHRcdC8vIEFkZCBlbGVtZW50cyBwYXNzaW5nIGVsZW1lbnRNYXRjaGVycyBkaXJlY3RseSB0byByZXN1bHRzXG5cdFx0XHQvLyBLZWVwIGBpYCBhIHN0cmluZyBpZiB0aGVyZSBhcmUgbm8gZWxlbWVudHMgc28gYG1hdGNoZWRDb3VudGAgd2lsbCBiZSBcIjAwXCIgYmVsb3dcblx0XHRcdC8vIFN1cHBvcnQ6IElFPDksIFNhZmFyaVxuXHRcdFx0Ly8gVG9sZXJhdGUgTm9kZUxpc3QgcHJvcGVydGllcyAoSUU6IFwibGVuZ3RoXCI7IFNhZmFyaTogPG51bWJlcj4pIG1hdGNoaW5nIGVsZW1lbnRzIGJ5IGlkXG5cdFx0XHRmb3IgKCA7IGkgIT09IGxlbiAmJiAoZWxlbSA9IGVsZW1zW2ldKSAhPSBudWxsOyBpKysgKSB7XG5cdFx0XHRcdGlmICggYnlFbGVtZW50ICYmIGVsZW0gKSB7XG5cdFx0XHRcdFx0aiA9IDA7XG5cdFx0XHRcdFx0d2hpbGUgKCAobWF0Y2hlciA9IGVsZW1lbnRNYXRjaGVyc1tqKytdKSApIHtcblx0XHRcdFx0XHRcdGlmICggbWF0Y2hlciggZWxlbSwgY29udGV4dCwgeG1sICkgKSB7XG5cdFx0XHRcdFx0XHRcdHJlc3VsdHMucHVzaCggZWxlbSApO1xuXHRcdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0aWYgKCBvdXRlcm1vc3QgKSB7XG5cdFx0XHRcdFx0XHRkaXJydW5zID0gZGlycnVuc1VuaXF1ZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBUcmFjayB1bm1hdGNoZWQgZWxlbWVudHMgZm9yIHNldCBmaWx0ZXJzXG5cdFx0XHRcdGlmICggYnlTZXQgKSB7XG5cdFx0XHRcdFx0Ly8gVGhleSB3aWxsIGhhdmUgZ29uZSB0aHJvdWdoIGFsbCBwb3NzaWJsZSBtYXRjaGVyc1xuXHRcdFx0XHRcdGlmICggKGVsZW0gPSAhbWF0Y2hlciAmJiBlbGVtKSApIHtcblx0XHRcdFx0XHRcdG1hdGNoZWRDb3VudC0tO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIExlbmd0aGVuIHRoZSBhcnJheSBmb3IgZXZlcnkgZWxlbWVudCwgbWF0Y2hlZCBvciBub3Rcblx0XHRcdFx0XHRpZiAoIHNlZWQgKSB7XG5cdFx0XHRcdFx0XHR1bm1hdGNoZWQucHVzaCggZWxlbSApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBBcHBseSBzZXQgZmlsdGVycyB0byB1bm1hdGNoZWQgZWxlbWVudHNcblx0XHRcdG1hdGNoZWRDb3VudCArPSBpO1xuXHRcdFx0aWYgKCBieVNldCAmJiBpICE9PSBtYXRjaGVkQ291bnQgKSB7XG5cdFx0XHRcdGogPSAwO1xuXHRcdFx0XHR3aGlsZSAoIChtYXRjaGVyID0gc2V0TWF0Y2hlcnNbaisrXSkgKSB7XG5cdFx0XHRcdFx0bWF0Y2hlciggdW5tYXRjaGVkLCBzZXRNYXRjaGVkLCBjb250ZXh0LCB4bWwgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGlmICggc2VlZCApIHtcblx0XHRcdFx0XHQvLyBSZWludGVncmF0ZSBlbGVtZW50IG1hdGNoZXMgdG8gZWxpbWluYXRlIHRoZSBuZWVkIGZvciBzb3J0aW5nXG5cdFx0XHRcdFx0aWYgKCBtYXRjaGVkQ291bnQgPiAwICkge1xuXHRcdFx0XHRcdFx0d2hpbGUgKCBpLS0gKSB7XG5cdFx0XHRcdFx0XHRcdGlmICggISh1bm1hdGNoZWRbaV0gfHwgc2V0TWF0Y2hlZFtpXSkgKSB7XG5cdFx0XHRcdFx0XHRcdFx0c2V0TWF0Y2hlZFtpXSA9IHBvcC5jYWxsKCByZXN1bHRzICk7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBEaXNjYXJkIGluZGV4IHBsYWNlaG9sZGVyIHZhbHVlcyB0byBnZXQgb25seSBhY3R1YWwgbWF0Y2hlc1xuXHRcdFx0XHRcdHNldE1hdGNoZWQgPSBjb25kZW5zZSggc2V0TWF0Y2hlZCApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gQWRkIG1hdGNoZXMgdG8gcmVzdWx0c1xuXHRcdFx0XHRwdXNoLmFwcGx5KCByZXN1bHRzLCBzZXRNYXRjaGVkICk7XG5cblx0XHRcdFx0Ly8gU2VlZGxlc3Mgc2V0IG1hdGNoZXMgc3VjY2VlZGluZyBtdWx0aXBsZSBzdWNjZXNzZnVsIG1hdGNoZXJzIHN0aXB1bGF0ZSBzb3J0aW5nXG5cdFx0XHRcdGlmICggb3V0ZXJtb3N0ICYmICFzZWVkICYmIHNldE1hdGNoZWQubGVuZ3RoID4gMCAmJlxuXHRcdFx0XHRcdCggbWF0Y2hlZENvdW50ICsgc2V0TWF0Y2hlcnMubGVuZ3RoICkgPiAxICkge1xuXG5cdFx0XHRcdFx0U2l6emxlLnVuaXF1ZVNvcnQoIHJlc3VsdHMgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBPdmVycmlkZSBtYW5pcHVsYXRpb24gb2YgZ2xvYmFscyBieSBuZXN0ZWQgbWF0Y2hlcnNcblx0XHRcdGlmICggb3V0ZXJtb3N0ICkge1xuXHRcdFx0XHRkaXJydW5zID0gZGlycnVuc1VuaXF1ZTtcblx0XHRcdFx0b3V0ZXJtb3N0Q29udGV4dCA9IGNvbnRleHRCYWNrdXA7XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB1bm1hdGNoZWQ7XG5cdFx0fTtcblxuXHRyZXR1cm4gYnlTZXQgP1xuXHRcdG1hcmtGdW5jdGlvbiggc3VwZXJNYXRjaGVyICkgOlxuXHRcdHN1cGVyTWF0Y2hlcjtcbn1cblxuY29tcGlsZSA9IFNpenpsZS5jb21waWxlID0gZnVuY3Rpb24oIHNlbGVjdG9yLCBtYXRjaCAvKiBJbnRlcm5hbCBVc2UgT25seSAqLyApIHtcblx0dmFyIGksXG5cdFx0c2V0TWF0Y2hlcnMgPSBbXSxcblx0XHRlbGVtZW50TWF0Y2hlcnMgPSBbXSxcblx0XHRjYWNoZWQgPSBjb21waWxlckNhY2hlWyBzZWxlY3RvciArIFwiIFwiIF07XG5cblx0aWYgKCAhY2FjaGVkICkge1xuXHRcdC8vIEdlbmVyYXRlIGEgZnVuY3Rpb24gb2YgcmVjdXJzaXZlIGZ1bmN0aW9ucyB0aGF0IGNhbiBiZSB1c2VkIHRvIGNoZWNrIGVhY2ggZWxlbWVudFxuXHRcdGlmICggIW1hdGNoICkge1xuXHRcdFx0bWF0Y2ggPSB0b2tlbml6ZSggc2VsZWN0b3IgKTtcblx0XHR9XG5cdFx0aSA9IG1hdGNoLmxlbmd0aDtcblx0XHR3aGlsZSAoIGktLSApIHtcblx0XHRcdGNhY2hlZCA9IG1hdGNoZXJGcm9tVG9rZW5zKCBtYXRjaFtpXSApO1xuXHRcdFx0aWYgKCBjYWNoZWRbIGV4cGFuZG8gXSApIHtcblx0XHRcdFx0c2V0TWF0Y2hlcnMucHVzaCggY2FjaGVkICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRlbGVtZW50TWF0Y2hlcnMucHVzaCggY2FjaGVkICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gQ2FjaGUgdGhlIGNvbXBpbGVkIGZ1bmN0aW9uXG5cdFx0Y2FjaGVkID0gY29tcGlsZXJDYWNoZSggc2VsZWN0b3IsIG1hdGNoZXJGcm9tR3JvdXBNYXRjaGVycyggZWxlbWVudE1hdGNoZXJzLCBzZXRNYXRjaGVycyApICk7XG5cblx0XHQvLyBTYXZlIHNlbGVjdG9yIGFuZCB0b2tlbml6YXRpb25cblx0XHRjYWNoZWQuc2VsZWN0b3IgPSBzZWxlY3Rvcjtcblx0fVxuXHRyZXR1cm4gY2FjaGVkO1xufTtcblxuLyoqXG4gKiBBIGxvdy1sZXZlbCBzZWxlY3Rpb24gZnVuY3Rpb24gdGhhdCB3b3JrcyB3aXRoIFNpenpsZSdzIGNvbXBpbGVkXG4gKiAgc2VsZWN0b3IgZnVuY3Rpb25zXG4gKiBAcGFyYW0ge1N0cmluZ3xGdW5jdGlvbn0gc2VsZWN0b3IgQSBzZWxlY3RvciBvciBhIHByZS1jb21waWxlZFxuICogIHNlbGVjdG9yIGZ1bmN0aW9uIGJ1aWx0IHdpdGggU2l6emxlLmNvbXBpbGVcbiAqIEBwYXJhbSB7RWxlbWVudH0gY29udGV4dFxuICogQHBhcmFtIHtBcnJheX0gW3Jlc3VsdHNdXG4gKiBAcGFyYW0ge0FycmF5fSBbc2VlZF0gQSBzZXQgb2YgZWxlbWVudHMgdG8gbWF0Y2ggYWdhaW5zdFxuICovXG5zZWxlY3QgPSBTaXp6bGUuc2VsZWN0ID0gZnVuY3Rpb24oIHNlbGVjdG9yLCBjb250ZXh0LCByZXN1bHRzLCBzZWVkICkge1xuXHR2YXIgaSwgdG9rZW5zLCB0b2tlbiwgdHlwZSwgZmluZCxcblx0XHRjb21waWxlZCA9IHR5cGVvZiBzZWxlY3RvciA9PT0gXCJmdW5jdGlvblwiICYmIHNlbGVjdG9yLFxuXHRcdG1hdGNoID0gIXNlZWQgJiYgdG9rZW5pemUoIChzZWxlY3RvciA9IGNvbXBpbGVkLnNlbGVjdG9yIHx8IHNlbGVjdG9yKSApO1xuXG5cdHJlc3VsdHMgPSByZXN1bHRzIHx8IFtdO1xuXG5cdC8vIFRyeSB0byBtaW5pbWl6ZSBvcGVyYXRpb25zIGlmIHRoZXJlIGlzIG5vIHNlZWQgYW5kIG9ubHkgb25lIGdyb3VwXG5cdGlmICggbWF0Y2gubGVuZ3RoID09PSAxICkge1xuXG5cdFx0Ly8gVGFrZSBhIHNob3J0Y3V0IGFuZCBzZXQgdGhlIGNvbnRleHQgaWYgdGhlIHJvb3Qgc2VsZWN0b3IgaXMgYW4gSURcblx0XHR0b2tlbnMgPSBtYXRjaFswXSA9IG1hdGNoWzBdLnNsaWNlKCAwICk7XG5cdFx0aWYgKCB0b2tlbnMubGVuZ3RoID4gMiAmJiAodG9rZW4gPSB0b2tlbnNbMF0pLnR5cGUgPT09IFwiSURcIiAmJlxuXHRcdFx0XHRzdXBwb3J0LmdldEJ5SWQgJiYgY29udGV4dC5ub2RlVHlwZSA9PT0gOSAmJiBkb2N1bWVudElzSFRNTCAmJlxuXHRcdFx0XHRFeHByLnJlbGF0aXZlWyB0b2tlbnNbMV0udHlwZSBdICkge1xuXG5cdFx0XHRjb250ZXh0ID0gKCBFeHByLmZpbmRbXCJJRFwiXSggdG9rZW4ubWF0Y2hlc1swXS5yZXBsYWNlKHJ1bmVzY2FwZSwgZnVuZXNjYXBlKSwgY29udGV4dCApIHx8IFtdIClbMF07XG5cdFx0XHRpZiAoICFjb250ZXh0ICkge1xuXHRcdFx0XHRyZXR1cm4gcmVzdWx0cztcblxuXHRcdFx0Ly8gUHJlY29tcGlsZWQgbWF0Y2hlcnMgd2lsbCBzdGlsbCB2ZXJpZnkgYW5jZXN0cnksIHNvIHN0ZXAgdXAgYSBsZXZlbFxuXHRcdFx0fSBlbHNlIGlmICggY29tcGlsZWQgKSB7XG5cdFx0XHRcdGNvbnRleHQgPSBjb250ZXh0LnBhcmVudE5vZGU7XG5cdFx0XHR9XG5cblx0XHRcdHNlbGVjdG9yID0gc2VsZWN0b3Iuc2xpY2UoIHRva2Vucy5zaGlmdCgpLnZhbHVlLmxlbmd0aCApO1xuXHRcdH1cblxuXHRcdC8vIEZldGNoIGEgc2VlZCBzZXQgZm9yIHJpZ2h0LXRvLWxlZnQgbWF0Y2hpbmdcblx0XHRpID0gbWF0Y2hFeHByW1wibmVlZHNDb250ZXh0XCJdLnRlc3QoIHNlbGVjdG9yICkgPyAwIDogdG9rZW5zLmxlbmd0aDtcblx0XHR3aGlsZSAoIGktLSApIHtcblx0XHRcdHRva2VuID0gdG9rZW5zW2ldO1xuXG5cdFx0XHQvLyBBYm9ydCBpZiB3ZSBoaXQgYSBjb21iaW5hdG9yXG5cdFx0XHRpZiAoIEV4cHIucmVsYXRpdmVbICh0eXBlID0gdG9rZW4udHlwZSkgXSApIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIChmaW5kID0gRXhwci5maW5kWyB0eXBlIF0pICkge1xuXHRcdFx0XHQvLyBTZWFyY2gsIGV4cGFuZGluZyBjb250ZXh0IGZvciBsZWFkaW5nIHNpYmxpbmcgY29tYmluYXRvcnNcblx0XHRcdFx0aWYgKCAoc2VlZCA9IGZpbmQoXG5cdFx0XHRcdFx0dG9rZW4ubWF0Y2hlc1swXS5yZXBsYWNlKCBydW5lc2NhcGUsIGZ1bmVzY2FwZSApLFxuXHRcdFx0XHRcdHJzaWJsaW5nLnRlc3QoIHRva2Vuc1swXS50eXBlICkgJiYgdGVzdENvbnRleHQoIGNvbnRleHQucGFyZW50Tm9kZSApIHx8IGNvbnRleHRcblx0XHRcdFx0KSkgKSB7XG5cblx0XHRcdFx0XHQvLyBJZiBzZWVkIGlzIGVtcHR5IG9yIG5vIHRva2VucyByZW1haW4sIHdlIGNhbiByZXR1cm4gZWFybHlcblx0XHRcdFx0XHR0b2tlbnMuc3BsaWNlKCBpLCAxICk7XG5cdFx0XHRcdFx0c2VsZWN0b3IgPSBzZWVkLmxlbmd0aCAmJiB0b1NlbGVjdG9yKCB0b2tlbnMgKTtcblx0XHRcdFx0XHRpZiAoICFzZWxlY3RvciApIHtcblx0XHRcdFx0XHRcdHB1c2guYXBwbHkoIHJlc3VsdHMsIHNlZWQgKTtcblx0XHRcdFx0XHRcdHJldHVybiByZXN1bHRzO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0Ly8gQ29tcGlsZSBhbmQgZXhlY3V0ZSBhIGZpbHRlcmluZyBmdW5jdGlvbiBpZiBvbmUgaXMgbm90IHByb3ZpZGVkXG5cdC8vIFByb3ZpZGUgYG1hdGNoYCB0byBhdm9pZCByZXRva2VuaXphdGlvbiBpZiB3ZSBtb2RpZmllZCB0aGUgc2VsZWN0b3IgYWJvdmVcblx0KCBjb21waWxlZCB8fCBjb21waWxlKCBzZWxlY3RvciwgbWF0Y2ggKSApKFxuXHRcdHNlZWQsXG5cdFx0Y29udGV4dCxcblx0XHQhZG9jdW1lbnRJc0hUTUwsXG5cdFx0cmVzdWx0cyxcblx0XHRyc2libGluZy50ZXN0KCBzZWxlY3RvciApICYmIHRlc3RDb250ZXh0KCBjb250ZXh0LnBhcmVudE5vZGUgKSB8fCBjb250ZXh0XG5cdCk7XG5cdHJldHVybiByZXN1bHRzO1xufTtcblxuLy8gT25lLXRpbWUgYXNzaWdubWVudHNcblxuLy8gU29ydCBzdGFiaWxpdHlcbnN1cHBvcnQuc29ydFN0YWJsZSA9IGV4cGFuZG8uc3BsaXQoXCJcIikuc29ydCggc29ydE9yZGVyICkuam9pbihcIlwiKSA9PT0gZXhwYW5kbztcblxuLy8gU3VwcG9ydDogQ2hyb21lIDE0LTM1K1xuLy8gQWx3YXlzIGFzc3VtZSBkdXBsaWNhdGVzIGlmIHRoZXkgYXJlbid0IHBhc3NlZCB0byB0aGUgY29tcGFyaXNvbiBmdW5jdGlvblxuc3VwcG9ydC5kZXRlY3REdXBsaWNhdGVzID0gISFoYXNEdXBsaWNhdGU7XG5cbi8vIEluaXRpYWxpemUgYWdhaW5zdCB0aGUgZGVmYXVsdCBkb2N1bWVudFxuc2V0RG9jdW1lbnQoKTtcblxuLy8gU3VwcG9ydDogV2Via2l0PDUzNy4zMiAtIFNhZmFyaSA2LjAuMy9DaHJvbWUgMjUgKGZpeGVkIGluIENocm9tZSAyNylcbi8vIERldGFjaGVkIG5vZGVzIGNvbmZvdW5kaW5nbHkgZm9sbG93ICplYWNoIG90aGVyKlxuc3VwcG9ydC5zb3J0RGV0YWNoZWQgPSBhc3NlcnQoZnVuY3Rpb24oIGRpdjEgKSB7XG5cdC8vIFNob3VsZCByZXR1cm4gMSwgYnV0IHJldHVybnMgNCAoZm9sbG93aW5nKVxuXHRyZXR1cm4gZGl2MS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbiggZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKSApICYgMTtcbn0pO1xuXG4vLyBTdXBwb3J0OiBJRTw4XG4vLyBQcmV2ZW50IGF0dHJpYnV0ZS9wcm9wZXJ0eSBcImludGVycG9sYXRpb25cIlxuLy8gaHR0cDovL21zZG4ubWljcm9zb2Z0LmNvbS9lbi11cy9saWJyYXJ5L21zNTM2NDI5JTI4VlMuODUlMjkuYXNweFxuaWYgKCAhYXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdGRpdi5pbm5lckhUTUwgPSBcIjxhIGhyZWY9JyMnPjwvYT5cIjtcblx0cmV0dXJuIGRpdi5maXJzdENoaWxkLmdldEF0dHJpYnV0ZShcImhyZWZcIikgPT09IFwiI1wiIDtcbn0pICkge1xuXHRhZGRIYW5kbGUoIFwidHlwZXxocmVmfGhlaWdodHx3aWR0aFwiLCBmdW5jdGlvbiggZWxlbSwgbmFtZSwgaXNYTUwgKSB7XG5cdFx0aWYgKCAhaXNYTUwgKSB7XG5cdFx0XHRyZXR1cm4gZWxlbS5nZXRBdHRyaWJ1dGUoIG5hbWUsIG5hbWUudG9Mb3dlckNhc2UoKSA9PT0gXCJ0eXBlXCIgPyAxIDogMiApO1xuXHRcdH1cblx0fSk7XG59XG5cbi8vIFN1cHBvcnQ6IElFPDlcbi8vIFVzZSBkZWZhdWx0VmFsdWUgaW4gcGxhY2Ugb2YgZ2V0QXR0cmlidXRlKFwidmFsdWVcIilcbmlmICggIXN1cHBvcnQuYXR0cmlidXRlcyB8fCAhYXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdGRpdi5pbm5lckhUTUwgPSBcIjxpbnB1dC8+XCI7XG5cdGRpdi5maXJzdENoaWxkLnNldEF0dHJpYnV0ZSggXCJ2YWx1ZVwiLCBcIlwiICk7XG5cdHJldHVybiBkaXYuZmlyc3RDaGlsZC5nZXRBdHRyaWJ1dGUoIFwidmFsdWVcIiApID09PSBcIlwiO1xufSkgKSB7XG5cdGFkZEhhbmRsZSggXCJ2YWx1ZVwiLCBmdW5jdGlvbiggZWxlbSwgbmFtZSwgaXNYTUwgKSB7XG5cdFx0aWYgKCAhaXNYTUwgJiYgZWxlbS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpID09PSBcImlucHV0XCIgKSB7XG5cdFx0XHRyZXR1cm4gZWxlbS5kZWZhdWx0VmFsdWU7XG5cdFx0fVxuXHR9KTtcbn1cblxuLy8gU3VwcG9ydDogSUU8OVxuLy8gVXNlIGdldEF0dHJpYnV0ZU5vZGUgdG8gZmV0Y2ggYm9vbGVhbnMgd2hlbiBnZXRBdHRyaWJ1dGUgbGllc1xuaWYgKCAhYXNzZXJ0KGZ1bmN0aW9uKCBkaXYgKSB7XG5cdHJldHVybiBkaXYuZ2V0QXR0cmlidXRlKFwiZGlzYWJsZWRcIikgPT0gbnVsbDtcbn0pICkge1xuXHRhZGRIYW5kbGUoIGJvb2xlYW5zLCBmdW5jdGlvbiggZWxlbSwgbmFtZSwgaXNYTUwgKSB7XG5cdFx0dmFyIHZhbDtcblx0XHRpZiAoICFpc1hNTCApIHtcblx0XHRcdHJldHVybiBlbGVtWyBuYW1lIF0gPT09IHRydWUgPyBuYW1lLnRvTG93ZXJDYXNlKCkgOlxuXHRcdFx0XHRcdCh2YWwgPSBlbGVtLmdldEF0dHJpYnV0ZU5vZGUoIG5hbWUgKSkgJiYgdmFsLnNwZWNpZmllZCA/XG5cdFx0XHRcdFx0dmFsLnZhbHVlIDpcblx0XHRcdFx0bnVsbDtcblx0XHR9XG5cdH0pO1xufVxuXG5yZXR1cm4gU2l6emxlO1xuXG59KSggd2luZG93ICk7XG5cblxuXG5qUXVlcnkuZmluZCA9IFNpenpsZTtcbmpRdWVyeS5leHByID0gU2l6emxlLnNlbGVjdG9ycztcbmpRdWVyeS5leHByW1wiOlwiXSA9IGpRdWVyeS5leHByLnBzZXVkb3M7XG5qUXVlcnkudW5pcXVlID0gU2l6emxlLnVuaXF1ZVNvcnQ7XG5qUXVlcnkudGV4dCA9IFNpenpsZS5nZXRUZXh0O1xualF1ZXJ5LmlzWE1MRG9jID0gU2l6emxlLmlzWE1MO1xualF1ZXJ5LmNvbnRhaW5zID0gU2l6emxlLmNvbnRhaW5zO1xuXG5cblxudmFyIHJuZWVkc0NvbnRleHQgPSBqUXVlcnkuZXhwci5tYXRjaC5uZWVkc0NvbnRleHQ7XG5cbnZhciByc2luZ2xlVGFnID0gKC9ePChcXHcrKVxccypcXC8/Pig/OjxcXC9cXDE+fCkkLyk7XG5cblxuXG52YXIgcmlzU2ltcGxlID0gL14uW146I1xcW1xcLixdKiQvO1xuXG4vLyBJbXBsZW1lbnQgdGhlIGlkZW50aWNhbCBmdW5jdGlvbmFsaXR5IGZvciBmaWx0ZXIgYW5kIG5vdFxuZnVuY3Rpb24gd2lubm93KCBlbGVtZW50cywgcXVhbGlmaWVyLCBub3QgKSB7XG5cdGlmICggalF1ZXJ5LmlzRnVuY3Rpb24oIHF1YWxpZmllciApICkge1xuXHRcdHJldHVybiBqUXVlcnkuZ3JlcCggZWxlbWVudHMsIGZ1bmN0aW9uKCBlbGVtLCBpICkge1xuXHRcdFx0LyoganNoaW50IC1XMDE4ICovXG5cdFx0XHRyZXR1cm4gISFxdWFsaWZpZXIuY2FsbCggZWxlbSwgaSwgZWxlbSApICE9PSBub3Q7XG5cdFx0fSk7XG5cblx0fVxuXG5cdGlmICggcXVhbGlmaWVyLm5vZGVUeXBlICkge1xuXHRcdHJldHVybiBqUXVlcnkuZ3JlcCggZWxlbWVudHMsIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0cmV0dXJuICggZWxlbSA9PT0gcXVhbGlmaWVyICkgIT09IG5vdDtcblx0XHR9KTtcblxuXHR9XG5cblx0aWYgKCB0eXBlb2YgcXVhbGlmaWVyID09PSBcInN0cmluZ1wiICkge1xuXHRcdGlmICggcmlzU2ltcGxlLnRlc3QoIHF1YWxpZmllciApICkge1xuXHRcdFx0cmV0dXJuIGpRdWVyeS5maWx0ZXIoIHF1YWxpZmllciwgZWxlbWVudHMsIG5vdCApO1xuXHRcdH1cblxuXHRcdHF1YWxpZmllciA9IGpRdWVyeS5maWx0ZXIoIHF1YWxpZmllciwgZWxlbWVudHMgKTtcblx0fVxuXG5cdHJldHVybiBqUXVlcnkuZ3JlcCggZWxlbWVudHMsIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdHJldHVybiAoIGluZGV4T2YuY2FsbCggcXVhbGlmaWVyLCBlbGVtICkgPj0gMCApICE9PSBub3Q7XG5cdH0pO1xufVxuXG5qUXVlcnkuZmlsdGVyID0gZnVuY3Rpb24oIGV4cHIsIGVsZW1zLCBub3QgKSB7XG5cdHZhciBlbGVtID0gZWxlbXNbIDAgXTtcblxuXHRpZiAoIG5vdCApIHtcblx0XHRleHByID0gXCI6bm90KFwiICsgZXhwciArIFwiKVwiO1xuXHR9XG5cblx0cmV0dXJuIGVsZW1zLmxlbmd0aCA9PT0gMSAmJiBlbGVtLm5vZGVUeXBlID09PSAxID9cblx0XHRqUXVlcnkuZmluZC5tYXRjaGVzU2VsZWN0b3IoIGVsZW0sIGV4cHIgKSA/IFsgZWxlbSBdIDogW10gOlxuXHRcdGpRdWVyeS5maW5kLm1hdGNoZXMoIGV4cHIsIGpRdWVyeS5ncmVwKCBlbGVtcywgZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRyZXR1cm4gZWxlbS5ub2RlVHlwZSA9PT0gMTtcblx0XHR9KSk7XG59O1xuXG5qUXVlcnkuZm4uZXh0ZW5kKHtcblx0ZmluZDogZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHRcdHZhciBpLFxuXHRcdFx0bGVuID0gdGhpcy5sZW5ndGgsXG5cdFx0XHRyZXQgPSBbXSxcblx0XHRcdHNlbGYgPSB0aGlzO1xuXG5cdFx0aWYgKCB0eXBlb2Ygc2VsZWN0b3IgIT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5wdXNoU3RhY2soIGpRdWVyeSggc2VsZWN0b3IgKS5maWx0ZXIoZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRcdFx0aWYgKCBqUXVlcnkuY29udGFpbnMoIHNlbGZbIGkgXSwgdGhpcyApICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9KSApO1xuXHRcdH1cblxuXHRcdGZvciAoIGkgPSAwOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRqUXVlcnkuZmluZCggc2VsZWN0b3IsIHNlbGZbIGkgXSwgcmV0ICk7XG5cdFx0fVxuXG5cdFx0Ly8gTmVlZGVkIGJlY2F1c2UgJCggc2VsZWN0b3IsIGNvbnRleHQgKSBiZWNvbWVzICQoIGNvbnRleHQgKS5maW5kKCBzZWxlY3RvciApXG5cdFx0cmV0ID0gdGhpcy5wdXNoU3RhY2soIGxlbiA+IDEgPyBqUXVlcnkudW5pcXVlKCByZXQgKSA6IHJldCApO1xuXHRcdHJldC5zZWxlY3RvciA9IHRoaXMuc2VsZWN0b3IgPyB0aGlzLnNlbGVjdG9yICsgXCIgXCIgKyBzZWxlY3RvciA6IHNlbGVjdG9yO1xuXHRcdHJldHVybiByZXQ7XG5cdH0sXG5cdGZpbHRlcjogZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHRcdHJldHVybiB0aGlzLnB1c2hTdGFjayggd2lubm93KHRoaXMsIHNlbGVjdG9yIHx8IFtdLCBmYWxzZSkgKTtcblx0fSxcblx0bm90OiBmdW5jdGlvbiggc2VsZWN0b3IgKSB7XG5cdFx0cmV0dXJuIHRoaXMucHVzaFN0YWNrKCB3aW5ub3codGhpcywgc2VsZWN0b3IgfHwgW10sIHRydWUpICk7XG5cdH0sXG5cdGlzOiBmdW5jdGlvbiggc2VsZWN0b3IgKSB7XG5cdFx0cmV0dXJuICEhd2lubm93KFxuXHRcdFx0dGhpcyxcblxuXHRcdFx0Ly8gSWYgdGhpcyBpcyBhIHBvc2l0aW9uYWwvcmVsYXRpdmUgc2VsZWN0b3IsIGNoZWNrIG1lbWJlcnNoaXAgaW4gdGhlIHJldHVybmVkIHNldFxuXHRcdFx0Ly8gc28gJChcInA6Zmlyc3RcIikuaXMoXCJwOmxhc3RcIikgd29uJ3QgcmV0dXJuIHRydWUgZm9yIGEgZG9jIHdpdGggdHdvIFwicFwiLlxuXHRcdFx0dHlwZW9mIHNlbGVjdG9yID09PSBcInN0cmluZ1wiICYmIHJuZWVkc0NvbnRleHQudGVzdCggc2VsZWN0b3IgKSA/XG5cdFx0XHRcdGpRdWVyeSggc2VsZWN0b3IgKSA6XG5cdFx0XHRcdHNlbGVjdG9yIHx8IFtdLFxuXHRcdFx0ZmFsc2Vcblx0XHQpLmxlbmd0aDtcblx0fVxufSk7XG5cblxuLy8gSW5pdGlhbGl6ZSBhIGpRdWVyeSBvYmplY3RcblxuXG4vLyBBIGNlbnRyYWwgcmVmZXJlbmNlIHRvIHRoZSByb290IGpRdWVyeShkb2N1bWVudClcbnZhciByb290alF1ZXJ5LFxuXG5cdC8vIEEgc2ltcGxlIHdheSB0byBjaGVjayBmb3IgSFRNTCBzdHJpbmdzXG5cdC8vIFByaW9yaXRpemUgI2lkIG92ZXIgPHRhZz4gdG8gYXZvaWQgWFNTIHZpYSBsb2NhdGlvbi5oYXNoICgjOTUyMSlcblx0Ly8gU3RyaWN0IEhUTUwgcmVjb2duaXRpb24gKCMxMTI5MDogbXVzdCBzdGFydCB3aXRoIDwpXG5cdHJxdWlja0V4cHIgPSAvXig/OlxccyooPFtcXHdcXFddKz4pW14+XSp8IyhbXFx3LV0qKSkkLyxcblxuXHRpbml0ID0galF1ZXJ5LmZuLmluaXQgPSBmdW5jdGlvbiggc2VsZWN0b3IsIGNvbnRleHQgKSB7XG5cdFx0dmFyIG1hdGNoLCBlbGVtO1xuXG5cdFx0Ly8gSEFORExFOiAkKFwiXCIpLCAkKG51bGwpLCAkKHVuZGVmaW5lZCksICQoZmFsc2UpXG5cdFx0aWYgKCAhc2VsZWN0b3IgKSB7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cblx0XHQvLyBIYW5kbGUgSFRNTCBzdHJpbmdzXG5cdFx0aWYgKCB0eXBlb2Ygc2VsZWN0b3IgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRpZiAoIHNlbGVjdG9yWzBdID09PSBcIjxcIiAmJiBzZWxlY3Rvclsgc2VsZWN0b3IubGVuZ3RoIC0gMSBdID09PSBcIj5cIiAmJiBzZWxlY3Rvci5sZW5ndGggPj0gMyApIHtcblx0XHRcdFx0Ly8gQXNzdW1lIHRoYXQgc3RyaW5ncyB0aGF0IHN0YXJ0IGFuZCBlbmQgd2l0aCA8PiBhcmUgSFRNTCBhbmQgc2tpcCB0aGUgcmVnZXggY2hlY2tcblx0XHRcdFx0bWF0Y2ggPSBbIG51bGwsIHNlbGVjdG9yLCBudWxsIF07XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdG1hdGNoID0gcnF1aWNrRXhwci5leGVjKCBzZWxlY3RvciApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBNYXRjaCBodG1sIG9yIG1ha2Ugc3VyZSBubyBjb250ZXh0IGlzIHNwZWNpZmllZCBmb3IgI2lkXG5cdFx0XHRpZiAoIG1hdGNoICYmIChtYXRjaFsxXSB8fCAhY29udGV4dCkgKSB7XG5cblx0XHRcdFx0Ly8gSEFORExFOiAkKGh0bWwpIC0+ICQoYXJyYXkpXG5cdFx0XHRcdGlmICggbWF0Y2hbMV0gKSB7XG5cdFx0XHRcdFx0Y29udGV4dCA9IGNvbnRleHQgaW5zdGFuY2VvZiBqUXVlcnkgPyBjb250ZXh0WzBdIDogY29udGV4dDtcblxuXHRcdFx0XHRcdC8vIE9wdGlvbiB0byBydW4gc2NyaXB0cyBpcyB0cnVlIGZvciBiYWNrLWNvbXBhdFxuXHRcdFx0XHRcdC8vIEludGVudGlvbmFsbHkgbGV0IHRoZSBlcnJvciBiZSB0aHJvd24gaWYgcGFyc2VIVE1MIGlzIG5vdCBwcmVzZW50XG5cdFx0XHRcdFx0alF1ZXJ5Lm1lcmdlKCB0aGlzLCBqUXVlcnkucGFyc2VIVE1MKFxuXHRcdFx0XHRcdFx0bWF0Y2hbMV0sXG5cdFx0XHRcdFx0XHRjb250ZXh0ICYmIGNvbnRleHQubm9kZVR5cGUgPyBjb250ZXh0Lm93bmVyRG9jdW1lbnQgfHwgY29udGV4dCA6IGRvY3VtZW50LFxuXHRcdFx0XHRcdFx0dHJ1ZVxuXHRcdFx0XHRcdCkgKTtcblxuXHRcdFx0XHRcdC8vIEhBTkRMRTogJChodG1sLCBwcm9wcylcblx0XHRcdFx0XHRpZiAoIHJzaW5nbGVUYWcudGVzdCggbWF0Y2hbMV0gKSAmJiBqUXVlcnkuaXNQbGFpbk9iamVjdCggY29udGV4dCApICkge1xuXHRcdFx0XHRcdFx0Zm9yICggbWF0Y2ggaW4gY29udGV4dCApIHtcblx0XHRcdFx0XHRcdFx0Ly8gUHJvcGVydGllcyBvZiBjb250ZXh0IGFyZSBjYWxsZWQgYXMgbWV0aG9kcyBpZiBwb3NzaWJsZVxuXHRcdFx0XHRcdFx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCB0aGlzWyBtYXRjaCBdICkgKSB7XG5cdFx0XHRcdFx0XHRcdFx0dGhpc1sgbWF0Y2ggXSggY29udGV4dFsgbWF0Y2ggXSApO1xuXG5cdFx0XHRcdFx0XHRcdC8vIC4uLmFuZCBvdGhlcndpc2Ugc2V0IGFzIGF0dHJpYnV0ZXNcblx0XHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0XHR0aGlzLmF0dHIoIG1hdGNoLCBjb250ZXh0WyBtYXRjaCBdICk7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRyZXR1cm4gdGhpcztcblxuXHRcdFx0XHQvLyBIQU5ETEU6ICQoI2lkKVxuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGVsZW0gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCggbWF0Y2hbMl0gKTtcblxuXHRcdFx0XHRcdC8vIFN1cHBvcnQ6IEJsYWNrYmVycnkgNC42XG5cdFx0XHRcdFx0Ly8gZ0VCSUQgcmV0dXJucyBub2RlcyBubyBsb25nZXIgaW4gdGhlIGRvY3VtZW50ICgjNjk2Mylcblx0XHRcdFx0XHRpZiAoIGVsZW0gJiYgZWxlbS5wYXJlbnROb2RlICkge1xuXHRcdFx0XHRcdFx0Ly8gSW5qZWN0IHRoZSBlbGVtZW50IGRpcmVjdGx5IGludG8gdGhlIGpRdWVyeSBvYmplY3Rcblx0XHRcdFx0XHRcdHRoaXMubGVuZ3RoID0gMTtcblx0XHRcdFx0XHRcdHRoaXNbMF0gPSBlbGVtO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRoaXMuY29udGV4dCA9IGRvY3VtZW50O1xuXHRcdFx0XHRcdHRoaXMuc2VsZWN0b3IgPSBzZWxlY3Rvcjtcblx0XHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdFx0fVxuXG5cdFx0XHQvLyBIQU5ETEU6ICQoZXhwciwgJCguLi4pKVxuXHRcdFx0fSBlbHNlIGlmICggIWNvbnRleHQgfHwgY29udGV4dC5qcXVlcnkgKSB7XG5cdFx0XHRcdHJldHVybiAoIGNvbnRleHQgfHwgcm9vdGpRdWVyeSApLmZpbmQoIHNlbGVjdG9yICk7XG5cblx0XHRcdC8vIEhBTkRMRTogJChleHByLCBjb250ZXh0KVxuXHRcdFx0Ly8gKHdoaWNoIGlzIGp1c3QgZXF1aXZhbGVudCB0bzogJChjb250ZXh0KS5maW5kKGV4cHIpXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5jb25zdHJ1Y3RvciggY29udGV4dCApLmZpbmQoIHNlbGVjdG9yICk7XG5cdFx0XHR9XG5cblx0XHQvLyBIQU5ETEU6ICQoRE9NRWxlbWVudClcblx0XHR9IGVsc2UgaWYgKCBzZWxlY3Rvci5ub2RlVHlwZSApIHtcblx0XHRcdHRoaXMuY29udGV4dCA9IHRoaXNbMF0gPSBzZWxlY3Rvcjtcblx0XHRcdHRoaXMubGVuZ3RoID0gMTtcblx0XHRcdHJldHVybiB0aGlzO1xuXG5cdFx0Ly8gSEFORExFOiAkKGZ1bmN0aW9uKVxuXHRcdC8vIFNob3J0Y3V0IGZvciBkb2N1bWVudCByZWFkeVxuXHRcdH0gZWxzZSBpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBzZWxlY3RvciApICkge1xuXHRcdFx0cmV0dXJuIHR5cGVvZiByb290alF1ZXJ5LnJlYWR5ICE9PSBcInVuZGVmaW5lZFwiID9cblx0XHRcdFx0cm9vdGpRdWVyeS5yZWFkeSggc2VsZWN0b3IgKSA6XG5cdFx0XHRcdC8vIEV4ZWN1dGUgaW1tZWRpYXRlbHkgaWYgcmVhZHkgaXMgbm90IHByZXNlbnRcblx0XHRcdFx0c2VsZWN0b3IoIGpRdWVyeSApO1xuXHRcdH1cblxuXHRcdGlmICggc2VsZWN0b3Iuc2VsZWN0b3IgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdHRoaXMuc2VsZWN0b3IgPSBzZWxlY3Rvci5zZWxlY3Rvcjtcblx0XHRcdHRoaXMuY29udGV4dCA9IHNlbGVjdG9yLmNvbnRleHQ7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGpRdWVyeS5tYWtlQXJyYXkoIHNlbGVjdG9yLCB0aGlzICk7XG5cdH07XG5cbi8vIEdpdmUgdGhlIGluaXQgZnVuY3Rpb24gdGhlIGpRdWVyeSBwcm90b3R5cGUgZm9yIGxhdGVyIGluc3RhbnRpYXRpb25cbmluaXQucHJvdG90eXBlID0galF1ZXJ5LmZuO1xuXG4vLyBJbml0aWFsaXplIGNlbnRyYWwgcmVmZXJlbmNlXG5yb290alF1ZXJ5ID0galF1ZXJ5KCBkb2N1bWVudCApO1xuXG5cbnZhciBycGFyZW50c3ByZXYgPSAvXig/OnBhcmVudHN8cHJldig/OlVudGlsfEFsbCkpLyxcblx0Ly8gTWV0aG9kcyBndWFyYW50ZWVkIHRvIHByb2R1Y2UgYSB1bmlxdWUgc2V0IHdoZW4gc3RhcnRpbmcgZnJvbSBhIHVuaXF1ZSBzZXRcblx0Z3VhcmFudGVlZFVuaXF1ZSA9IHtcblx0XHRjaGlsZHJlbjogdHJ1ZSxcblx0XHRjb250ZW50czogdHJ1ZSxcblx0XHRuZXh0OiB0cnVlLFxuXHRcdHByZXY6IHRydWVcblx0fTtcblxualF1ZXJ5LmV4dGVuZCh7XG5cdGRpcjogZnVuY3Rpb24oIGVsZW0sIGRpciwgdW50aWwgKSB7XG5cdFx0dmFyIG1hdGNoZWQgPSBbXSxcblx0XHRcdHRydW5jYXRlID0gdW50aWwgIT09IHVuZGVmaW5lZDtcblxuXHRcdHdoaWxlICggKGVsZW0gPSBlbGVtWyBkaXIgXSkgJiYgZWxlbS5ub2RlVHlwZSAhPT0gOSApIHtcblx0XHRcdGlmICggZWxlbS5ub2RlVHlwZSA9PT0gMSApIHtcblx0XHRcdFx0aWYgKCB0cnVuY2F0ZSAmJiBqUXVlcnkoIGVsZW0gKS5pcyggdW50aWwgKSApIHtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0fVxuXHRcdFx0XHRtYXRjaGVkLnB1c2goIGVsZW0gKTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIG1hdGNoZWQ7XG5cdH0sXG5cblx0c2libGluZzogZnVuY3Rpb24oIG4sIGVsZW0gKSB7XG5cdFx0dmFyIG1hdGNoZWQgPSBbXTtcblxuXHRcdGZvciAoIDsgbjsgbiA9IG4ubmV4dFNpYmxpbmcgKSB7XG5cdFx0XHRpZiAoIG4ubm9kZVR5cGUgPT09IDEgJiYgbiAhPT0gZWxlbSApIHtcblx0XHRcdFx0bWF0Y2hlZC5wdXNoKCBuICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG1hdGNoZWQ7XG5cdH1cbn0pO1xuXG5qUXVlcnkuZm4uZXh0ZW5kKHtcblx0aGFzOiBmdW5jdGlvbiggdGFyZ2V0ICkge1xuXHRcdHZhciB0YXJnZXRzID0galF1ZXJ5KCB0YXJnZXQsIHRoaXMgKSxcblx0XHRcdGwgPSB0YXJnZXRzLmxlbmd0aDtcblxuXHRcdHJldHVybiB0aGlzLmZpbHRlcihmdW5jdGlvbigpIHtcblx0XHRcdHZhciBpID0gMDtcblx0XHRcdGZvciAoIDsgaSA8IGw7IGkrKyApIHtcblx0XHRcdFx0aWYgKCBqUXVlcnkuY29udGFpbnMoIHRoaXMsIHRhcmdldHNbaV0gKSApIHtcblx0XHRcdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0pO1xuXHR9LFxuXG5cdGNsb3Nlc3Q6IGZ1bmN0aW9uKCBzZWxlY3RvcnMsIGNvbnRleHQgKSB7XG5cdFx0dmFyIGN1cixcblx0XHRcdGkgPSAwLFxuXHRcdFx0bCA9IHRoaXMubGVuZ3RoLFxuXHRcdFx0bWF0Y2hlZCA9IFtdLFxuXHRcdFx0cG9zID0gcm5lZWRzQ29udGV4dC50ZXN0KCBzZWxlY3RvcnMgKSB8fCB0eXBlb2Ygc2VsZWN0b3JzICE9PSBcInN0cmluZ1wiID9cblx0XHRcdFx0alF1ZXJ5KCBzZWxlY3RvcnMsIGNvbnRleHQgfHwgdGhpcy5jb250ZXh0ICkgOlxuXHRcdFx0XHQwO1xuXG5cdFx0Zm9yICggOyBpIDwgbDsgaSsrICkge1xuXHRcdFx0Zm9yICggY3VyID0gdGhpc1tpXTsgY3VyICYmIGN1ciAhPT0gY29udGV4dDsgY3VyID0gY3VyLnBhcmVudE5vZGUgKSB7XG5cdFx0XHRcdC8vIEFsd2F5cyBza2lwIGRvY3VtZW50IGZyYWdtZW50c1xuXHRcdFx0XHRpZiAoIGN1ci5ub2RlVHlwZSA8IDExICYmIChwb3MgP1xuXHRcdFx0XHRcdHBvcy5pbmRleChjdXIpID4gLTEgOlxuXG5cdFx0XHRcdFx0Ly8gRG9uJ3QgcGFzcyBub24tZWxlbWVudHMgdG8gU2l6emxlXG5cdFx0XHRcdFx0Y3VyLm5vZGVUeXBlID09PSAxICYmXG5cdFx0XHRcdFx0XHRqUXVlcnkuZmluZC5tYXRjaGVzU2VsZWN0b3IoY3VyLCBzZWxlY3RvcnMpKSApIHtcblxuXHRcdFx0XHRcdG1hdGNoZWQucHVzaCggY3VyICk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5wdXNoU3RhY2soIG1hdGNoZWQubGVuZ3RoID4gMSA/IGpRdWVyeS51bmlxdWUoIG1hdGNoZWQgKSA6IG1hdGNoZWQgKTtcblx0fSxcblxuXHQvLyBEZXRlcm1pbmUgdGhlIHBvc2l0aW9uIG9mIGFuIGVsZW1lbnQgd2l0aGluIHRoZSBzZXRcblx0aW5kZXg6IGZ1bmN0aW9uKCBlbGVtICkge1xuXG5cdFx0Ly8gTm8gYXJndW1lbnQsIHJldHVybiBpbmRleCBpbiBwYXJlbnRcblx0XHRpZiAoICFlbGVtICkge1xuXHRcdFx0cmV0dXJuICggdGhpc1sgMCBdICYmIHRoaXNbIDAgXS5wYXJlbnROb2RlICkgPyB0aGlzLmZpcnN0KCkucHJldkFsbCgpLmxlbmd0aCA6IC0xO1xuXHRcdH1cblxuXHRcdC8vIEluZGV4IGluIHNlbGVjdG9yXG5cdFx0aWYgKCB0eXBlb2YgZWxlbSA9PT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdHJldHVybiBpbmRleE9mLmNhbGwoIGpRdWVyeSggZWxlbSApLCB0aGlzWyAwIF0gKTtcblx0XHR9XG5cblx0XHQvLyBMb2NhdGUgdGhlIHBvc2l0aW9uIG9mIHRoZSBkZXNpcmVkIGVsZW1lbnRcblx0XHRyZXR1cm4gaW5kZXhPZi5jYWxsKCB0aGlzLFxuXG5cdFx0XHQvLyBJZiBpdCByZWNlaXZlcyBhIGpRdWVyeSBvYmplY3QsIHRoZSBmaXJzdCBlbGVtZW50IGlzIHVzZWRcblx0XHRcdGVsZW0uanF1ZXJ5ID8gZWxlbVsgMCBdIDogZWxlbVxuXHRcdCk7XG5cdH0sXG5cblx0YWRkOiBmdW5jdGlvbiggc2VsZWN0b3IsIGNvbnRleHQgKSB7XG5cdFx0cmV0dXJuIHRoaXMucHVzaFN0YWNrKFxuXHRcdFx0alF1ZXJ5LnVuaXF1ZShcblx0XHRcdFx0alF1ZXJ5Lm1lcmdlKCB0aGlzLmdldCgpLCBqUXVlcnkoIHNlbGVjdG9yLCBjb250ZXh0ICkgKVxuXHRcdFx0KVxuXHRcdCk7XG5cdH0sXG5cblx0YWRkQmFjazogZnVuY3Rpb24oIHNlbGVjdG9yICkge1xuXHRcdHJldHVybiB0aGlzLmFkZCggc2VsZWN0b3IgPT0gbnVsbCA/XG5cdFx0XHR0aGlzLnByZXZPYmplY3QgOiB0aGlzLnByZXZPYmplY3QuZmlsdGVyKHNlbGVjdG9yKVxuXHRcdCk7XG5cdH1cbn0pO1xuXG5mdW5jdGlvbiBzaWJsaW5nKCBjdXIsIGRpciApIHtcblx0d2hpbGUgKCAoY3VyID0gY3VyW2Rpcl0pICYmIGN1ci5ub2RlVHlwZSAhPT0gMSApIHt9XG5cdHJldHVybiBjdXI7XG59XG5cbmpRdWVyeS5lYWNoKHtcblx0cGFyZW50OiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHR2YXIgcGFyZW50ID0gZWxlbS5wYXJlbnROb2RlO1xuXHRcdHJldHVybiBwYXJlbnQgJiYgcGFyZW50Lm5vZGVUeXBlICE9PSAxMSA/IHBhcmVudCA6IG51bGw7XG5cdH0sXG5cdHBhcmVudHM6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdHJldHVybiBqUXVlcnkuZGlyKCBlbGVtLCBcInBhcmVudE5vZGVcIiApO1xuXHR9LFxuXHRwYXJlbnRzVW50aWw6IGZ1bmN0aW9uKCBlbGVtLCBpLCB1bnRpbCApIHtcblx0XHRyZXR1cm4galF1ZXJ5LmRpciggZWxlbSwgXCJwYXJlbnROb2RlXCIsIHVudGlsICk7XG5cdH0sXG5cdG5leHQ6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdHJldHVybiBzaWJsaW5nKCBlbGVtLCBcIm5leHRTaWJsaW5nXCIgKTtcblx0fSxcblx0cHJldjogZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0cmV0dXJuIHNpYmxpbmcoIGVsZW0sIFwicHJldmlvdXNTaWJsaW5nXCIgKTtcblx0fSxcblx0bmV4dEFsbDogZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0cmV0dXJuIGpRdWVyeS5kaXIoIGVsZW0sIFwibmV4dFNpYmxpbmdcIiApO1xuXHR9LFxuXHRwcmV2QWxsOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRyZXR1cm4galF1ZXJ5LmRpciggZWxlbSwgXCJwcmV2aW91c1NpYmxpbmdcIiApO1xuXHR9LFxuXHRuZXh0VW50aWw6IGZ1bmN0aW9uKCBlbGVtLCBpLCB1bnRpbCApIHtcblx0XHRyZXR1cm4galF1ZXJ5LmRpciggZWxlbSwgXCJuZXh0U2libGluZ1wiLCB1bnRpbCApO1xuXHR9LFxuXHRwcmV2VW50aWw6IGZ1bmN0aW9uKCBlbGVtLCBpLCB1bnRpbCApIHtcblx0XHRyZXR1cm4galF1ZXJ5LmRpciggZWxlbSwgXCJwcmV2aW91c1NpYmxpbmdcIiwgdW50aWwgKTtcblx0fSxcblx0c2libGluZ3M6IGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdHJldHVybiBqUXVlcnkuc2libGluZyggKCBlbGVtLnBhcmVudE5vZGUgfHwge30gKS5maXJzdENoaWxkLCBlbGVtICk7XG5cdH0sXG5cdGNoaWxkcmVuOiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRyZXR1cm4galF1ZXJ5LnNpYmxpbmcoIGVsZW0uZmlyc3RDaGlsZCApO1xuXHR9LFxuXHRjb250ZW50czogZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0cmV0dXJuIGVsZW0uY29udGVudERvY3VtZW50IHx8IGpRdWVyeS5tZXJnZSggW10sIGVsZW0uY2hpbGROb2RlcyApO1xuXHR9XG59LCBmdW5jdGlvbiggbmFtZSwgZm4gKSB7XG5cdGpRdWVyeS5mblsgbmFtZSBdID0gZnVuY3Rpb24oIHVudGlsLCBzZWxlY3RvciApIHtcblx0XHR2YXIgbWF0Y2hlZCA9IGpRdWVyeS5tYXAoIHRoaXMsIGZuLCB1bnRpbCApO1xuXG5cdFx0aWYgKCBuYW1lLnNsaWNlKCAtNSApICE9PSBcIlVudGlsXCIgKSB7XG5cdFx0XHRzZWxlY3RvciA9IHVudGlsO1xuXHRcdH1cblxuXHRcdGlmICggc2VsZWN0b3IgJiYgdHlwZW9mIHNlbGVjdG9yID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0bWF0Y2hlZCA9IGpRdWVyeS5maWx0ZXIoIHNlbGVjdG9yLCBtYXRjaGVkICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB0aGlzLmxlbmd0aCA+IDEgKSB7XG5cdFx0XHQvLyBSZW1vdmUgZHVwbGljYXRlc1xuXHRcdFx0aWYgKCAhZ3VhcmFudGVlZFVuaXF1ZVsgbmFtZSBdICkge1xuXHRcdFx0XHRqUXVlcnkudW5pcXVlKCBtYXRjaGVkICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFJldmVyc2Ugb3JkZXIgZm9yIHBhcmVudHMqIGFuZCBwcmV2LWRlcml2YXRpdmVzXG5cdFx0XHRpZiAoIHJwYXJlbnRzcHJldi50ZXN0KCBuYW1lICkgKSB7XG5cdFx0XHRcdG1hdGNoZWQucmV2ZXJzZSgpO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLnB1c2hTdGFjayggbWF0Y2hlZCApO1xuXHR9O1xufSk7XG52YXIgcm5vdHdoaXRlID0gKC9cXFMrL2cpO1xuXG5cblxuLy8gU3RyaW5nIHRvIE9iamVjdCBvcHRpb25zIGZvcm1hdCBjYWNoZVxudmFyIG9wdGlvbnNDYWNoZSA9IHt9O1xuXG4vLyBDb252ZXJ0IFN0cmluZy1mb3JtYXR0ZWQgb3B0aW9ucyBpbnRvIE9iamVjdC1mb3JtYXR0ZWQgb25lcyBhbmQgc3RvcmUgaW4gY2FjaGVcbmZ1bmN0aW9uIGNyZWF0ZU9wdGlvbnMoIG9wdGlvbnMgKSB7XG5cdHZhciBvYmplY3QgPSBvcHRpb25zQ2FjaGVbIG9wdGlvbnMgXSA9IHt9O1xuXHRqUXVlcnkuZWFjaCggb3B0aW9ucy5tYXRjaCggcm5vdHdoaXRlICkgfHwgW10sIGZ1bmN0aW9uKCBfLCBmbGFnICkge1xuXHRcdG9iamVjdFsgZmxhZyBdID0gdHJ1ZTtcblx0fSk7XG5cdHJldHVybiBvYmplY3Q7XG59XG5cbi8qXG4gKiBDcmVhdGUgYSBjYWxsYmFjayBsaXN0IHVzaW5nIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVyczpcbiAqXG4gKlx0b3B0aW9uczogYW4gb3B0aW9uYWwgbGlzdCBvZiBzcGFjZS1zZXBhcmF0ZWQgb3B0aW9ucyB0aGF0IHdpbGwgY2hhbmdlIGhvd1xuICpcdFx0XHR0aGUgY2FsbGJhY2sgbGlzdCBiZWhhdmVzIG9yIGEgbW9yZSB0cmFkaXRpb25hbCBvcHRpb24gb2JqZWN0XG4gKlxuICogQnkgZGVmYXVsdCBhIGNhbGxiYWNrIGxpc3Qgd2lsbCBhY3QgbGlrZSBhbiBldmVudCBjYWxsYmFjayBsaXN0IGFuZCBjYW4gYmVcbiAqIFwiZmlyZWRcIiBtdWx0aXBsZSB0aW1lcy5cbiAqXG4gKiBQb3NzaWJsZSBvcHRpb25zOlxuICpcbiAqXHRvbmNlOlx0XHRcdHdpbGwgZW5zdXJlIHRoZSBjYWxsYmFjayBsaXN0IGNhbiBvbmx5IGJlIGZpcmVkIG9uY2UgKGxpa2UgYSBEZWZlcnJlZClcbiAqXG4gKlx0bWVtb3J5Olx0XHRcdHdpbGwga2VlcCB0cmFjayBvZiBwcmV2aW91cyB2YWx1ZXMgYW5kIHdpbGwgY2FsbCBhbnkgY2FsbGJhY2sgYWRkZWRcbiAqXHRcdFx0XHRcdGFmdGVyIHRoZSBsaXN0IGhhcyBiZWVuIGZpcmVkIHJpZ2h0IGF3YXkgd2l0aCB0aGUgbGF0ZXN0IFwibWVtb3JpemVkXCJcbiAqXHRcdFx0XHRcdHZhbHVlcyAobGlrZSBhIERlZmVycmVkKVxuICpcbiAqXHR1bmlxdWU6XHRcdFx0d2lsbCBlbnN1cmUgYSBjYWxsYmFjayBjYW4gb25seSBiZSBhZGRlZCBvbmNlIChubyBkdXBsaWNhdGUgaW4gdGhlIGxpc3QpXG4gKlxuICpcdHN0b3BPbkZhbHNlOlx0aW50ZXJydXB0IGNhbGxpbmdzIHdoZW4gYSBjYWxsYmFjayByZXR1cm5zIGZhbHNlXG4gKlxuICovXG5qUXVlcnkuQ2FsbGJhY2tzID0gZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cblx0Ly8gQ29udmVydCBvcHRpb25zIGZyb20gU3RyaW5nLWZvcm1hdHRlZCB0byBPYmplY3QtZm9ybWF0dGVkIGlmIG5lZWRlZFxuXHQvLyAod2UgY2hlY2sgaW4gY2FjaGUgZmlyc3QpXG5cdG9wdGlvbnMgPSB0eXBlb2Ygb3B0aW9ucyA9PT0gXCJzdHJpbmdcIiA/XG5cdFx0KCBvcHRpb25zQ2FjaGVbIG9wdGlvbnMgXSB8fCBjcmVhdGVPcHRpb25zKCBvcHRpb25zICkgKSA6XG5cdFx0alF1ZXJ5LmV4dGVuZCgge30sIG9wdGlvbnMgKTtcblxuXHR2YXIgLy8gTGFzdCBmaXJlIHZhbHVlIChmb3Igbm9uLWZvcmdldHRhYmxlIGxpc3RzKVxuXHRcdG1lbW9yeSxcblx0XHQvLyBGbGFnIHRvIGtub3cgaWYgbGlzdCB3YXMgYWxyZWFkeSBmaXJlZFxuXHRcdGZpcmVkLFxuXHRcdC8vIEZsYWcgdG8ga25vdyBpZiBsaXN0IGlzIGN1cnJlbnRseSBmaXJpbmdcblx0XHRmaXJpbmcsXG5cdFx0Ly8gRmlyc3QgY2FsbGJhY2sgdG8gZmlyZSAodXNlZCBpbnRlcm5hbGx5IGJ5IGFkZCBhbmQgZmlyZVdpdGgpXG5cdFx0ZmlyaW5nU3RhcnQsXG5cdFx0Ly8gRW5kIG9mIHRoZSBsb29wIHdoZW4gZmlyaW5nXG5cdFx0ZmlyaW5nTGVuZ3RoLFxuXHRcdC8vIEluZGV4IG9mIGN1cnJlbnRseSBmaXJpbmcgY2FsbGJhY2sgKG1vZGlmaWVkIGJ5IHJlbW92ZSBpZiBuZWVkZWQpXG5cdFx0ZmlyaW5nSW5kZXgsXG5cdFx0Ly8gQWN0dWFsIGNhbGxiYWNrIGxpc3Rcblx0XHRsaXN0ID0gW10sXG5cdFx0Ly8gU3RhY2sgb2YgZmlyZSBjYWxscyBmb3IgcmVwZWF0YWJsZSBsaXN0c1xuXHRcdHN0YWNrID0gIW9wdGlvbnMub25jZSAmJiBbXSxcblx0XHQvLyBGaXJlIGNhbGxiYWNrc1xuXHRcdGZpcmUgPSBmdW5jdGlvbiggZGF0YSApIHtcblx0XHRcdG1lbW9yeSA9IG9wdGlvbnMubWVtb3J5ICYmIGRhdGE7XG5cdFx0XHRmaXJlZCA9IHRydWU7XG5cdFx0XHRmaXJpbmdJbmRleCA9IGZpcmluZ1N0YXJ0IHx8IDA7XG5cdFx0XHRmaXJpbmdTdGFydCA9IDA7XG5cdFx0XHRmaXJpbmdMZW5ndGggPSBsaXN0Lmxlbmd0aDtcblx0XHRcdGZpcmluZyA9IHRydWU7XG5cdFx0XHRmb3IgKCA7IGxpc3QgJiYgZmlyaW5nSW5kZXggPCBmaXJpbmdMZW5ndGg7IGZpcmluZ0luZGV4KysgKSB7XG5cdFx0XHRcdGlmICggbGlzdFsgZmlyaW5nSW5kZXggXS5hcHBseSggZGF0YVsgMCBdLCBkYXRhWyAxIF0gKSA9PT0gZmFsc2UgJiYgb3B0aW9ucy5zdG9wT25GYWxzZSApIHtcblx0XHRcdFx0XHRtZW1vcnkgPSBmYWxzZTsgLy8gVG8gcHJldmVudCBmdXJ0aGVyIGNhbGxzIHVzaW5nIGFkZFxuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHRmaXJpbmcgPSBmYWxzZTtcblx0XHRcdGlmICggbGlzdCApIHtcblx0XHRcdFx0aWYgKCBzdGFjayApIHtcblx0XHRcdFx0XHRpZiAoIHN0YWNrLmxlbmd0aCApIHtcblx0XHRcdFx0XHRcdGZpcmUoIHN0YWNrLnNoaWZ0KCkgKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZWxzZSBpZiAoIG1lbW9yeSApIHtcblx0XHRcdFx0XHRsaXN0ID0gW107XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0c2VsZi5kaXNhYmxlKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9LFxuXHRcdC8vIEFjdHVhbCBDYWxsYmFja3Mgb2JqZWN0XG5cdFx0c2VsZiA9IHtcblx0XHRcdC8vIEFkZCBhIGNhbGxiYWNrIG9yIGEgY29sbGVjdGlvbiBvZiBjYWxsYmFja3MgdG8gdGhlIGxpc3Rcblx0XHRcdGFkZDogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggbGlzdCApIHtcblx0XHRcdFx0XHQvLyBGaXJzdCwgd2Ugc2F2ZSB0aGUgY3VycmVudCBsZW5ndGhcblx0XHRcdFx0XHR2YXIgc3RhcnQgPSBsaXN0Lmxlbmd0aDtcblx0XHRcdFx0XHQoZnVuY3Rpb24gYWRkKCBhcmdzICkge1xuXHRcdFx0XHRcdFx0alF1ZXJ5LmVhY2goIGFyZ3MsIGZ1bmN0aW9uKCBfLCBhcmcgKSB7XG5cdFx0XHRcdFx0XHRcdHZhciB0eXBlID0galF1ZXJ5LnR5cGUoIGFyZyApO1xuXHRcdFx0XHRcdFx0XHRpZiAoIHR5cGUgPT09IFwiZnVuY3Rpb25cIiApIHtcblx0XHRcdFx0XHRcdFx0XHRpZiAoICFvcHRpb25zLnVuaXF1ZSB8fCAhc2VsZi5oYXMoIGFyZyApICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0bGlzdC5wdXNoKCBhcmcgKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoIGFyZyAmJiBhcmcubGVuZ3RoICYmIHR5cGUgIT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRcdFx0XHRcdFx0Ly8gSW5zcGVjdCByZWN1cnNpdmVseVxuXHRcdFx0XHRcdFx0XHRcdGFkZCggYXJnICk7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH0pO1xuXHRcdFx0XHRcdH0pKCBhcmd1bWVudHMgKTtcblx0XHRcdFx0XHQvLyBEbyB3ZSBuZWVkIHRvIGFkZCB0aGUgY2FsbGJhY2tzIHRvIHRoZVxuXHRcdFx0XHRcdC8vIGN1cnJlbnQgZmlyaW5nIGJhdGNoP1xuXHRcdFx0XHRcdGlmICggZmlyaW5nICkge1xuXHRcdFx0XHRcdFx0ZmlyaW5nTGVuZ3RoID0gbGlzdC5sZW5ndGg7XG5cdFx0XHRcdFx0Ly8gV2l0aCBtZW1vcnksIGlmIHdlJ3JlIG5vdCBmaXJpbmcgdGhlblxuXHRcdFx0XHRcdC8vIHdlIHNob3VsZCBjYWxsIHJpZ2h0IGF3YXlcblx0XHRcdFx0XHR9IGVsc2UgaWYgKCBtZW1vcnkgKSB7XG5cdFx0XHRcdFx0XHRmaXJpbmdTdGFydCA9IHN0YXJ0O1xuXHRcdFx0XHRcdFx0ZmlyZSggbWVtb3J5ICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXHRcdFx0fSxcblx0XHRcdC8vIFJlbW92ZSBhIGNhbGxiYWNrIGZyb20gdGhlIGxpc3Rcblx0XHRcdHJlbW92ZTogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggbGlzdCApIHtcblx0XHRcdFx0XHRqUXVlcnkuZWFjaCggYXJndW1lbnRzLCBmdW5jdGlvbiggXywgYXJnICkge1xuXHRcdFx0XHRcdFx0dmFyIGluZGV4O1xuXHRcdFx0XHRcdFx0d2hpbGUgKCAoIGluZGV4ID0galF1ZXJ5LmluQXJyYXkoIGFyZywgbGlzdCwgaW5kZXggKSApID4gLTEgKSB7XG5cdFx0XHRcdFx0XHRcdGxpc3Quc3BsaWNlKCBpbmRleCwgMSApO1xuXHRcdFx0XHRcdFx0XHQvLyBIYW5kbGUgZmlyaW5nIGluZGV4ZXNcblx0XHRcdFx0XHRcdFx0aWYgKCBmaXJpbmcgKSB7XG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBpbmRleCA8PSBmaXJpbmdMZW5ndGggKSB7XG5cdFx0XHRcdFx0XHRcdFx0XHRmaXJpbmdMZW5ndGgtLTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdFx0aWYgKCBpbmRleCA8PSBmaXJpbmdJbmRleCApIHtcblx0XHRcdFx0XHRcdFx0XHRcdGZpcmluZ0luZGV4LS07XG5cdFx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHR9LFxuXHRcdFx0Ly8gQ2hlY2sgaWYgYSBnaXZlbiBjYWxsYmFjayBpcyBpbiB0aGUgbGlzdC5cblx0XHRcdC8vIElmIG5vIGFyZ3VtZW50IGlzIGdpdmVuLCByZXR1cm4gd2hldGhlciBvciBub3QgbGlzdCBoYXMgY2FsbGJhY2tzIGF0dGFjaGVkLlxuXHRcdFx0aGFzOiBmdW5jdGlvbiggZm4gKSB7XG5cdFx0XHRcdHJldHVybiBmbiA/IGpRdWVyeS5pbkFycmF5KCBmbiwgbGlzdCApID4gLTEgOiAhISggbGlzdCAmJiBsaXN0Lmxlbmd0aCApO1xuXHRcdFx0fSxcblx0XHRcdC8vIFJlbW92ZSBhbGwgY2FsbGJhY2tzIGZyb20gdGhlIGxpc3Rcblx0XHRcdGVtcHR5OiBmdW5jdGlvbigpIHtcblx0XHRcdFx0bGlzdCA9IFtdO1xuXHRcdFx0XHRmaXJpbmdMZW5ndGggPSAwO1xuXHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdH0sXG5cdFx0XHQvLyBIYXZlIHRoZSBsaXN0IGRvIG5vdGhpbmcgYW55bW9yZVxuXHRcdFx0ZGlzYWJsZTogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGxpc3QgPSBzdGFjayA9IG1lbW9yeSA9IHVuZGVmaW5lZDtcblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHR9LFxuXHRcdFx0Ly8gSXMgaXQgZGlzYWJsZWQ/XG5cdFx0XHRkaXNhYmxlZDogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHJldHVybiAhbGlzdDtcblx0XHRcdH0sXG5cdFx0XHQvLyBMb2NrIHRoZSBsaXN0IGluIGl0cyBjdXJyZW50IHN0YXRlXG5cdFx0XHRsb2NrOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0c3RhY2sgPSB1bmRlZmluZWQ7XG5cdFx0XHRcdGlmICggIW1lbW9yeSApIHtcblx0XHRcdFx0XHRzZWxmLmRpc2FibGUoKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdH0sXG5cdFx0XHQvLyBJcyBpdCBsb2NrZWQ/XG5cdFx0XHRsb2NrZWQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRyZXR1cm4gIXN0YWNrO1xuXHRcdFx0fSxcblx0XHRcdC8vIENhbGwgYWxsIGNhbGxiYWNrcyB3aXRoIHRoZSBnaXZlbiBjb250ZXh0IGFuZCBhcmd1bWVudHNcblx0XHRcdGZpcmVXaXRoOiBmdW5jdGlvbiggY29udGV4dCwgYXJncyApIHtcblx0XHRcdFx0aWYgKCBsaXN0ICYmICggIWZpcmVkIHx8IHN0YWNrICkgKSB7XG5cdFx0XHRcdFx0YXJncyA9IGFyZ3MgfHwgW107XG5cdFx0XHRcdFx0YXJncyA9IFsgY29udGV4dCwgYXJncy5zbGljZSA/IGFyZ3Muc2xpY2UoKSA6IGFyZ3MgXTtcblx0XHRcdFx0XHRpZiAoIGZpcmluZyApIHtcblx0XHRcdFx0XHRcdHN0YWNrLnB1c2goIGFyZ3MgKTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0ZmlyZSggYXJncyApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdH0sXG5cdFx0XHQvLyBDYWxsIGFsbCB0aGUgY2FsbGJhY2tzIHdpdGggdGhlIGdpdmVuIGFyZ3VtZW50c1xuXHRcdFx0ZmlyZTogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHNlbGYuZmlyZVdpdGgoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdH0sXG5cdFx0XHQvLyBUbyBrbm93IGlmIHRoZSBjYWxsYmFja3MgaGF2ZSBhbHJlYWR5IGJlZW4gY2FsbGVkIGF0IGxlYXN0IG9uY2Vcblx0XHRcdGZpcmVkOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0cmV0dXJuICEhZmlyZWQ7XG5cdFx0XHR9XG5cdFx0fTtcblxuXHRyZXR1cm4gc2VsZjtcbn07XG5cblxualF1ZXJ5LmV4dGVuZCh7XG5cblx0RGVmZXJyZWQ6IGZ1bmN0aW9uKCBmdW5jICkge1xuXHRcdHZhciB0dXBsZXMgPSBbXG5cdFx0XHRcdC8vIGFjdGlvbiwgYWRkIGxpc3RlbmVyLCBsaXN0ZW5lciBsaXN0LCBmaW5hbCBzdGF0ZVxuXHRcdFx0XHRbIFwicmVzb2x2ZVwiLCBcImRvbmVcIiwgalF1ZXJ5LkNhbGxiYWNrcyhcIm9uY2UgbWVtb3J5XCIpLCBcInJlc29sdmVkXCIgXSxcblx0XHRcdFx0WyBcInJlamVjdFwiLCBcImZhaWxcIiwgalF1ZXJ5LkNhbGxiYWNrcyhcIm9uY2UgbWVtb3J5XCIpLCBcInJlamVjdGVkXCIgXSxcblx0XHRcdFx0WyBcIm5vdGlmeVwiLCBcInByb2dyZXNzXCIsIGpRdWVyeS5DYWxsYmFja3MoXCJtZW1vcnlcIikgXVxuXHRcdFx0XSxcblx0XHRcdHN0YXRlID0gXCJwZW5kaW5nXCIsXG5cdFx0XHRwcm9taXNlID0ge1xuXHRcdFx0XHRzdGF0ZTogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0cmV0dXJuIHN0YXRlO1xuXHRcdFx0XHR9LFxuXHRcdFx0XHRhbHdheXM6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGRlZmVycmVkLmRvbmUoIGFyZ3VtZW50cyApLmZhaWwoIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRcdHJldHVybiB0aGlzO1xuXHRcdFx0XHR9LFxuXHRcdFx0XHR0aGVuOiBmdW5jdGlvbiggLyogZm5Eb25lLCBmbkZhaWwsIGZuUHJvZ3Jlc3MgKi8gKSB7XG5cdFx0XHRcdFx0dmFyIGZucyA9IGFyZ3VtZW50cztcblx0XHRcdFx0XHRyZXR1cm4galF1ZXJ5LkRlZmVycmVkKGZ1bmN0aW9uKCBuZXdEZWZlciApIHtcblx0XHRcdFx0XHRcdGpRdWVyeS5lYWNoKCB0dXBsZXMsIGZ1bmN0aW9uKCBpLCB0dXBsZSApIHtcblx0XHRcdFx0XHRcdFx0dmFyIGZuID0galF1ZXJ5LmlzRnVuY3Rpb24oIGZuc1sgaSBdICkgJiYgZm5zWyBpIF07XG5cdFx0XHRcdFx0XHRcdC8vIGRlZmVycmVkWyBkb25lIHwgZmFpbCB8IHByb2dyZXNzIF0gZm9yIGZvcndhcmRpbmcgYWN0aW9ucyB0byBuZXdEZWZlclxuXHRcdFx0XHRcdFx0XHRkZWZlcnJlZFsgdHVwbGVbMV0gXShmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdFx0XHR2YXIgcmV0dXJuZWQgPSBmbiAmJiBmbi5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0XHRcdFx0XHRcdFx0aWYgKCByZXR1cm5lZCAmJiBqUXVlcnkuaXNGdW5jdGlvbiggcmV0dXJuZWQucHJvbWlzZSApICkge1xuXHRcdFx0XHRcdFx0XHRcdFx0cmV0dXJuZWQucHJvbWlzZSgpXG5cdFx0XHRcdFx0XHRcdFx0XHRcdC5kb25lKCBuZXdEZWZlci5yZXNvbHZlIClcblx0XHRcdFx0XHRcdFx0XHRcdFx0LmZhaWwoIG5ld0RlZmVyLnJlamVjdCApXG5cdFx0XHRcdFx0XHRcdFx0XHRcdC5wcm9ncmVzcyggbmV3RGVmZXIubm90aWZ5ICk7XG5cdFx0XHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0XHRcdG5ld0RlZmVyWyB0dXBsZVsgMCBdICsgXCJXaXRoXCIgXSggdGhpcyA9PT0gcHJvbWlzZSA/IG5ld0RlZmVyLnByb21pc2UoKSA6IHRoaXMsIGZuID8gWyByZXR1cm5lZCBdIDogYXJndW1lbnRzICk7XG5cdFx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHR9KTtcblx0XHRcdFx0XHRcdH0pO1xuXHRcdFx0XHRcdFx0Zm5zID0gbnVsbDtcblx0XHRcdFx0XHR9KS5wcm9taXNlKCk7XG5cdFx0XHRcdH0sXG5cdFx0XHRcdC8vIEdldCBhIHByb21pc2UgZm9yIHRoaXMgZGVmZXJyZWRcblx0XHRcdFx0Ly8gSWYgb2JqIGlzIHByb3ZpZGVkLCB0aGUgcHJvbWlzZSBhc3BlY3QgaXMgYWRkZWQgdG8gdGhlIG9iamVjdFxuXHRcdFx0XHRwcm9taXNlOiBmdW5jdGlvbiggb2JqICkge1xuXHRcdFx0XHRcdHJldHVybiBvYmogIT0gbnVsbCA/IGpRdWVyeS5leHRlbmQoIG9iaiwgcHJvbWlzZSApIDogcHJvbWlzZTtcblx0XHRcdFx0fVxuXHRcdFx0fSxcblx0XHRcdGRlZmVycmVkID0ge307XG5cblx0XHQvLyBLZWVwIHBpcGUgZm9yIGJhY2stY29tcGF0XG5cdFx0cHJvbWlzZS5waXBlID0gcHJvbWlzZS50aGVuO1xuXG5cdFx0Ly8gQWRkIGxpc3Qtc3BlY2lmaWMgbWV0aG9kc1xuXHRcdGpRdWVyeS5lYWNoKCB0dXBsZXMsIGZ1bmN0aW9uKCBpLCB0dXBsZSApIHtcblx0XHRcdHZhciBsaXN0ID0gdHVwbGVbIDIgXSxcblx0XHRcdFx0c3RhdGVTdHJpbmcgPSB0dXBsZVsgMyBdO1xuXG5cdFx0XHQvLyBwcm9taXNlWyBkb25lIHwgZmFpbCB8IHByb2dyZXNzIF0gPSBsaXN0LmFkZFxuXHRcdFx0cHJvbWlzZVsgdHVwbGVbMV0gXSA9IGxpc3QuYWRkO1xuXG5cdFx0XHQvLyBIYW5kbGUgc3RhdGVcblx0XHRcdGlmICggc3RhdGVTdHJpbmcgKSB7XG5cdFx0XHRcdGxpc3QuYWRkKGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdC8vIHN0YXRlID0gWyByZXNvbHZlZCB8IHJlamVjdGVkIF1cblx0XHRcdFx0XHRzdGF0ZSA9IHN0YXRlU3RyaW5nO1xuXG5cdFx0XHRcdC8vIFsgcmVqZWN0X2xpc3QgfCByZXNvbHZlX2xpc3QgXS5kaXNhYmxlOyBwcm9ncmVzc19saXN0LmxvY2tcblx0XHRcdFx0fSwgdHVwbGVzWyBpIF4gMSBdWyAyIF0uZGlzYWJsZSwgdHVwbGVzWyAyIF1bIDIgXS5sb2NrICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIGRlZmVycmVkWyByZXNvbHZlIHwgcmVqZWN0IHwgbm90aWZ5IF1cblx0XHRcdGRlZmVycmVkWyB0dXBsZVswXSBdID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGRlZmVycmVkWyB0dXBsZVswXSArIFwiV2l0aFwiIF0oIHRoaXMgPT09IGRlZmVycmVkID8gcHJvbWlzZSA6IHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdH07XG5cdFx0XHRkZWZlcnJlZFsgdHVwbGVbMF0gKyBcIldpdGhcIiBdID0gbGlzdC5maXJlV2l0aDtcblx0XHR9KTtcblxuXHRcdC8vIE1ha2UgdGhlIGRlZmVycmVkIGEgcHJvbWlzZVxuXHRcdHByb21pc2UucHJvbWlzZSggZGVmZXJyZWQgKTtcblxuXHRcdC8vIENhbGwgZ2l2ZW4gZnVuYyBpZiBhbnlcblx0XHRpZiAoIGZ1bmMgKSB7XG5cdFx0XHRmdW5jLmNhbGwoIGRlZmVycmVkLCBkZWZlcnJlZCApO1xuXHRcdH1cblxuXHRcdC8vIEFsbCBkb25lIVxuXHRcdHJldHVybiBkZWZlcnJlZDtcblx0fSxcblxuXHQvLyBEZWZlcnJlZCBoZWxwZXJcblx0d2hlbjogZnVuY3Rpb24oIHN1Ym9yZGluYXRlIC8qICwgLi4uLCBzdWJvcmRpbmF0ZU4gKi8gKSB7XG5cdFx0dmFyIGkgPSAwLFxuXHRcdFx0cmVzb2x2ZVZhbHVlcyA9IHNsaWNlLmNhbGwoIGFyZ3VtZW50cyApLFxuXHRcdFx0bGVuZ3RoID0gcmVzb2x2ZVZhbHVlcy5sZW5ndGgsXG5cblx0XHRcdC8vIHRoZSBjb3VudCBvZiB1bmNvbXBsZXRlZCBzdWJvcmRpbmF0ZXNcblx0XHRcdHJlbWFpbmluZyA9IGxlbmd0aCAhPT0gMSB8fCAoIHN1Ym9yZGluYXRlICYmIGpRdWVyeS5pc0Z1bmN0aW9uKCBzdWJvcmRpbmF0ZS5wcm9taXNlICkgKSA/IGxlbmd0aCA6IDAsXG5cblx0XHRcdC8vIHRoZSBtYXN0ZXIgRGVmZXJyZWQuIElmIHJlc29sdmVWYWx1ZXMgY29uc2lzdCBvZiBvbmx5IGEgc2luZ2xlIERlZmVycmVkLCBqdXN0IHVzZSB0aGF0LlxuXHRcdFx0ZGVmZXJyZWQgPSByZW1haW5pbmcgPT09IDEgPyBzdWJvcmRpbmF0ZSA6IGpRdWVyeS5EZWZlcnJlZCgpLFxuXG5cdFx0XHQvLyBVcGRhdGUgZnVuY3Rpb24gZm9yIGJvdGggcmVzb2x2ZSBhbmQgcHJvZ3Jlc3MgdmFsdWVzXG5cdFx0XHR1cGRhdGVGdW5jID0gZnVuY3Rpb24oIGksIGNvbnRleHRzLCB2YWx1ZXMgKSB7XG5cdFx0XHRcdHJldHVybiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0XHRcdFx0Y29udGV4dHNbIGkgXSA9IHRoaXM7XG5cdFx0XHRcdFx0dmFsdWVzWyBpIF0gPSBhcmd1bWVudHMubGVuZ3RoID4gMSA/IHNsaWNlLmNhbGwoIGFyZ3VtZW50cyApIDogdmFsdWU7XG5cdFx0XHRcdFx0aWYgKCB2YWx1ZXMgPT09IHByb2dyZXNzVmFsdWVzICkge1xuXHRcdFx0XHRcdFx0ZGVmZXJyZWQubm90aWZ5V2l0aCggY29udGV4dHMsIHZhbHVlcyApO1xuXHRcdFx0XHRcdH0gZWxzZSBpZiAoICEoIC0tcmVtYWluaW5nICkgKSB7XG5cdFx0XHRcdFx0XHRkZWZlcnJlZC5yZXNvbHZlV2l0aCggY29udGV4dHMsIHZhbHVlcyApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fTtcblx0XHRcdH0sXG5cblx0XHRcdHByb2dyZXNzVmFsdWVzLCBwcm9ncmVzc0NvbnRleHRzLCByZXNvbHZlQ29udGV4dHM7XG5cblx0XHQvLyBBZGQgbGlzdGVuZXJzIHRvIERlZmVycmVkIHN1Ym9yZGluYXRlczsgdHJlYXQgb3RoZXJzIGFzIHJlc29sdmVkXG5cdFx0aWYgKCBsZW5ndGggPiAxICkge1xuXHRcdFx0cHJvZ3Jlc3NWYWx1ZXMgPSBuZXcgQXJyYXkoIGxlbmd0aCApO1xuXHRcdFx0cHJvZ3Jlc3NDb250ZXh0cyA9IG5ldyBBcnJheSggbGVuZ3RoICk7XG5cdFx0XHRyZXNvbHZlQ29udGV4dHMgPSBuZXcgQXJyYXkoIGxlbmd0aCApO1xuXHRcdFx0Zm9yICggOyBpIDwgbGVuZ3RoOyBpKysgKSB7XG5cdFx0XHRcdGlmICggcmVzb2x2ZVZhbHVlc1sgaSBdICYmIGpRdWVyeS5pc0Z1bmN0aW9uKCByZXNvbHZlVmFsdWVzWyBpIF0ucHJvbWlzZSApICkge1xuXHRcdFx0XHRcdHJlc29sdmVWYWx1ZXNbIGkgXS5wcm9taXNlKClcblx0XHRcdFx0XHRcdC5kb25lKCB1cGRhdGVGdW5jKCBpLCByZXNvbHZlQ29udGV4dHMsIHJlc29sdmVWYWx1ZXMgKSApXG5cdFx0XHRcdFx0XHQuZmFpbCggZGVmZXJyZWQucmVqZWN0IClcblx0XHRcdFx0XHRcdC5wcm9ncmVzcyggdXBkYXRlRnVuYyggaSwgcHJvZ3Jlc3NDb250ZXh0cywgcHJvZ3Jlc3NWYWx1ZXMgKSApO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdC0tcmVtYWluaW5nO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gSWYgd2UncmUgbm90IHdhaXRpbmcgb24gYW55dGhpbmcsIHJlc29sdmUgdGhlIG1hc3RlclxuXHRcdGlmICggIXJlbWFpbmluZyApIHtcblx0XHRcdGRlZmVycmVkLnJlc29sdmVXaXRoKCByZXNvbHZlQ29udGV4dHMsIHJlc29sdmVWYWx1ZXMgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZGVmZXJyZWQucHJvbWlzZSgpO1xuXHR9XG59KTtcblxuXG4vLyBUaGUgZGVmZXJyZWQgdXNlZCBvbiBET00gcmVhZHlcbnZhciByZWFkeUxpc3Q7XG5cbmpRdWVyeS5mbi5yZWFkeSA9IGZ1bmN0aW9uKCBmbiApIHtcblx0Ly8gQWRkIHRoZSBjYWxsYmFja1xuXHRqUXVlcnkucmVhZHkucHJvbWlzZSgpLmRvbmUoIGZuICk7XG5cblx0cmV0dXJuIHRoaXM7XG59O1xuXG5qUXVlcnkuZXh0ZW5kKHtcblx0Ly8gSXMgdGhlIERPTSByZWFkeSB0byBiZSB1c2VkPyBTZXQgdG8gdHJ1ZSBvbmNlIGl0IG9jY3Vycy5cblx0aXNSZWFkeTogZmFsc2UsXG5cblx0Ly8gQSBjb3VudGVyIHRvIHRyYWNrIGhvdyBtYW55IGl0ZW1zIHRvIHdhaXQgZm9yIGJlZm9yZVxuXHQvLyB0aGUgcmVhZHkgZXZlbnQgZmlyZXMuIFNlZSAjNjc4MVxuXHRyZWFkeVdhaXQ6IDEsXG5cblx0Ly8gSG9sZCAob3IgcmVsZWFzZSkgdGhlIHJlYWR5IGV2ZW50XG5cdGhvbGRSZWFkeTogZnVuY3Rpb24oIGhvbGQgKSB7XG5cdFx0aWYgKCBob2xkICkge1xuXHRcdFx0alF1ZXJ5LnJlYWR5V2FpdCsrO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRqUXVlcnkucmVhZHkoIHRydWUgKTtcblx0XHR9XG5cdH0sXG5cblx0Ly8gSGFuZGxlIHdoZW4gdGhlIERPTSBpcyByZWFkeVxuXHRyZWFkeTogZnVuY3Rpb24oIHdhaXQgKSB7XG5cblx0XHQvLyBBYm9ydCBpZiB0aGVyZSBhcmUgcGVuZGluZyBob2xkcyBvciB3ZSdyZSBhbHJlYWR5IHJlYWR5XG5cdFx0aWYgKCB3YWl0ID09PSB0cnVlID8gLS1qUXVlcnkucmVhZHlXYWl0IDogalF1ZXJ5LmlzUmVhZHkgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gUmVtZW1iZXIgdGhhdCB0aGUgRE9NIGlzIHJlYWR5XG5cdFx0alF1ZXJ5LmlzUmVhZHkgPSB0cnVlO1xuXG5cdFx0Ly8gSWYgYSBub3JtYWwgRE9NIFJlYWR5IGV2ZW50IGZpcmVkLCBkZWNyZW1lbnQsIGFuZCB3YWl0IGlmIG5lZWQgYmVcblx0XHRpZiAoIHdhaXQgIT09IHRydWUgJiYgLS1qUXVlcnkucmVhZHlXYWl0ID4gMCApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBJZiB0aGVyZSBhcmUgZnVuY3Rpb25zIGJvdW5kLCB0byBleGVjdXRlXG5cdFx0cmVhZHlMaXN0LnJlc29sdmVXaXRoKCBkb2N1bWVudCwgWyBqUXVlcnkgXSApO1xuXG5cdFx0Ly8gVHJpZ2dlciBhbnkgYm91bmQgcmVhZHkgZXZlbnRzXG5cdFx0aWYgKCBqUXVlcnkuZm4udHJpZ2dlckhhbmRsZXIgKSB7XG5cdFx0XHRqUXVlcnkoIGRvY3VtZW50ICkudHJpZ2dlckhhbmRsZXIoIFwicmVhZHlcIiApO1xuXHRcdFx0alF1ZXJ5KCBkb2N1bWVudCApLm9mZiggXCJyZWFkeVwiICk7XG5cdFx0fVxuXHR9XG59KTtcblxuLyoqXG4gKiBUaGUgcmVhZHkgZXZlbnQgaGFuZGxlciBhbmQgc2VsZiBjbGVhbnVwIG1ldGhvZFxuICovXG5mdW5jdGlvbiBjb21wbGV0ZWQoKSB7XG5cdGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIFwiRE9NQ29udGVudExvYWRlZFwiLCBjb21wbGV0ZWQsIGZhbHNlICk7XG5cdHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCBcImxvYWRcIiwgY29tcGxldGVkLCBmYWxzZSApO1xuXHRqUXVlcnkucmVhZHkoKTtcbn1cblxualF1ZXJ5LnJlYWR5LnByb21pc2UgPSBmdW5jdGlvbiggb2JqICkge1xuXHRpZiAoICFyZWFkeUxpc3QgKSB7XG5cblx0XHRyZWFkeUxpc3QgPSBqUXVlcnkuRGVmZXJyZWQoKTtcblxuXHRcdC8vIENhdGNoIGNhc2VzIHdoZXJlICQoZG9jdW1lbnQpLnJlYWR5KCkgaXMgY2FsbGVkIGFmdGVyIHRoZSBicm93c2VyIGV2ZW50IGhhcyBhbHJlYWR5IG9jY3VycmVkLlxuXHRcdC8vIFdlIG9uY2UgdHJpZWQgdG8gdXNlIHJlYWR5U3RhdGUgXCJpbnRlcmFjdGl2ZVwiIGhlcmUsIGJ1dCBpdCBjYXVzZWQgaXNzdWVzIGxpa2UgdGhlIG9uZVxuXHRcdC8vIGRpc2NvdmVyZWQgYnkgQ2hyaXNTIGhlcmU6IGh0dHA6Ly9idWdzLmpxdWVyeS5jb20vdGlja2V0LzEyMjgyI2NvbW1lbnQ6MTVcblx0XHRpZiAoIGRvY3VtZW50LnJlYWR5U3RhdGUgPT09IFwiY29tcGxldGVcIiApIHtcblx0XHRcdC8vIEhhbmRsZSBpdCBhc3luY2hyb25vdXNseSB0byBhbGxvdyBzY3JpcHRzIHRoZSBvcHBvcnR1bml0eSB0byBkZWxheSByZWFkeVxuXHRcdFx0c2V0VGltZW91dCggalF1ZXJ5LnJlYWR5ICk7XG5cblx0XHR9IGVsc2Uge1xuXG5cdFx0XHQvLyBVc2UgdGhlIGhhbmR5IGV2ZW50IGNhbGxiYWNrXG5cdFx0XHRkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCBcIkRPTUNvbnRlbnRMb2FkZWRcIiwgY29tcGxldGVkLCBmYWxzZSApO1xuXG5cdFx0XHQvLyBBIGZhbGxiYWNrIHRvIHdpbmRvdy5vbmxvYWQsIHRoYXQgd2lsbCBhbHdheXMgd29ya1xuXHRcdFx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoIFwibG9hZFwiLCBjb21wbGV0ZWQsIGZhbHNlICk7XG5cdFx0fVxuXHR9XG5cdHJldHVybiByZWFkeUxpc3QucHJvbWlzZSggb2JqICk7XG59O1xuXG4vLyBLaWNrIG9mZiB0aGUgRE9NIHJlYWR5IGNoZWNrIGV2ZW4gaWYgdGhlIHVzZXIgZG9lcyBub3RcbmpRdWVyeS5yZWFkeS5wcm9taXNlKCk7XG5cblxuXG5cbi8vIE11bHRpZnVuY3Rpb25hbCBtZXRob2QgdG8gZ2V0IGFuZCBzZXQgdmFsdWVzIG9mIGEgY29sbGVjdGlvblxuLy8gVGhlIHZhbHVlL3MgY2FuIG9wdGlvbmFsbHkgYmUgZXhlY3V0ZWQgaWYgaXQncyBhIGZ1bmN0aW9uXG52YXIgYWNjZXNzID0galF1ZXJ5LmFjY2VzcyA9IGZ1bmN0aW9uKCBlbGVtcywgZm4sIGtleSwgdmFsdWUsIGNoYWluYWJsZSwgZW1wdHlHZXQsIHJhdyApIHtcblx0dmFyIGkgPSAwLFxuXHRcdGxlbiA9IGVsZW1zLmxlbmd0aCxcblx0XHRidWxrID0ga2V5ID09IG51bGw7XG5cblx0Ly8gU2V0cyBtYW55IHZhbHVlc1xuXHRpZiAoIGpRdWVyeS50eXBlKCBrZXkgKSA9PT0gXCJvYmplY3RcIiApIHtcblx0XHRjaGFpbmFibGUgPSB0cnVlO1xuXHRcdGZvciAoIGkgaW4ga2V5ICkge1xuXHRcdFx0alF1ZXJ5LmFjY2VzcyggZWxlbXMsIGZuLCBpLCBrZXlbaV0sIHRydWUsIGVtcHR5R2V0LCByYXcgKTtcblx0XHR9XG5cblx0Ly8gU2V0cyBvbmUgdmFsdWVcblx0fSBlbHNlIGlmICggdmFsdWUgIT09IHVuZGVmaW5lZCApIHtcblx0XHRjaGFpbmFibGUgPSB0cnVlO1xuXG5cdFx0aWYgKCAhalF1ZXJ5LmlzRnVuY3Rpb24oIHZhbHVlICkgKSB7XG5cdFx0XHRyYXcgPSB0cnVlO1xuXHRcdH1cblxuXHRcdGlmICggYnVsayApIHtcblx0XHRcdC8vIEJ1bGsgb3BlcmF0aW9ucyBydW4gYWdhaW5zdCB0aGUgZW50aXJlIHNldFxuXHRcdFx0aWYgKCByYXcgKSB7XG5cdFx0XHRcdGZuLmNhbGwoIGVsZW1zLCB2YWx1ZSApO1xuXHRcdFx0XHRmbiA9IG51bGw7XG5cblx0XHRcdC8vIC4uLmV4Y2VwdCB3aGVuIGV4ZWN1dGluZyBmdW5jdGlvbiB2YWx1ZXNcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGJ1bGsgPSBmbjtcblx0XHRcdFx0Zm4gPSBmdW5jdGlvbiggZWxlbSwga2V5LCB2YWx1ZSApIHtcblx0XHRcdFx0XHRyZXR1cm4gYnVsay5jYWxsKCBqUXVlcnkoIGVsZW0gKSwgdmFsdWUgKTtcblx0XHRcdFx0fTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIGZuICkge1xuXHRcdFx0Zm9yICggOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRcdGZuKCBlbGVtc1tpXSwga2V5LCByYXcgPyB2YWx1ZSA6IHZhbHVlLmNhbGwoIGVsZW1zW2ldLCBpLCBmbiggZWxlbXNbaV0sIGtleSApICkgKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gY2hhaW5hYmxlID9cblx0XHRlbGVtcyA6XG5cblx0XHQvLyBHZXRzXG5cdFx0YnVsayA/XG5cdFx0XHRmbi5jYWxsKCBlbGVtcyApIDpcblx0XHRcdGxlbiA/IGZuKCBlbGVtc1swXSwga2V5ICkgOiBlbXB0eUdldDtcbn07XG5cblxuLyoqXG4gKiBEZXRlcm1pbmVzIHdoZXRoZXIgYW4gb2JqZWN0IGNhbiBoYXZlIGRhdGFcbiAqL1xualF1ZXJ5LmFjY2VwdERhdGEgPSBmdW5jdGlvbiggb3duZXIgKSB7XG5cdC8vIEFjY2VwdHMgb25seTpcblx0Ly8gIC0gTm9kZVxuXHQvLyAgICAtIE5vZGUuRUxFTUVOVF9OT0RFXG5cdC8vICAgIC0gTm9kZS5ET0NVTUVOVF9OT0RFXG5cdC8vICAtIE9iamVjdFxuXHQvLyAgICAtIEFueVxuXHQvKiBqc2hpbnQgLVcwMTggKi9cblx0cmV0dXJuIG93bmVyLm5vZGVUeXBlID09PSAxIHx8IG93bmVyLm5vZGVUeXBlID09PSA5IHx8ICEoICtvd25lci5ub2RlVHlwZSApO1xufTtcblxuXG5mdW5jdGlvbiBEYXRhKCkge1xuXHQvLyBTdXBwb3J0OiBBbmRyb2lkPDQsXG5cdC8vIE9sZCBXZWJLaXQgZG9lcyBub3QgaGF2ZSBPYmplY3QucHJldmVudEV4dGVuc2lvbnMvZnJlZXplIG1ldGhvZCxcblx0Ly8gcmV0dXJuIG5ldyBlbXB0eSBvYmplY3QgaW5zdGVhZCB3aXRoIG5vIFtbc2V0XV0gYWNjZXNzb3Jcblx0T2JqZWN0LmRlZmluZVByb3BlcnR5KCB0aGlzLmNhY2hlID0ge30sIDAsIHtcblx0XHRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0cmV0dXJuIHt9O1xuXHRcdH1cblx0fSk7XG5cblx0dGhpcy5leHBhbmRvID0galF1ZXJ5LmV4cGFuZG8gKyBEYXRhLnVpZCsrO1xufVxuXG5EYXRhLnVpZCA9IDE7XG5EYXRhLmFjY2VwdHMgPSBqUXVlcnkuYWNjZXB0RGF0YTtcblxuRGF0YS5wcm90b3R5cGUgPSB7XG5cdGtleTogZnVuY3Rpb24oIG93bmVyICkge1xuXHRcdC8vIFdlIGNhbiBhY2NlcHQgZGF0YSBmb3Igbm9uLWVsZW1lbnQgbm9kZXMgaW4gbW9kZXJuIGJyb3dzZXJzLFxuXHRcdC8vIGJ1dCB3ZSBzaG91bGQgbm90LCBzZWUgIzgzMzUuXG5cdFx0Ly8gQWx3YXlzIHJldHVybiB0aGUga2V5IGZvciBhIGZyb3plbiBvYmplY3QuXG5cdFx0aWYgKCAhRGF0YS5hY2NlcHRzKCBvd25lciApICkge1xuXHRcdFx0cmV0dXJuIDA7XG5cdFx0fVxuXG5cdFx0dmFyIGRlc2NyaXB0b3IgPSB7fSxcblx0XHRcdC8vIENoZWNrIGlmIHRoZSBvd25lciBvYmplY3QgYWxyZWFkeSBoYXMgYSBjYWNoZSBrZXlcblx0XHRcdHVubG9jayA9IG93bmVyWyB0aGlzLmV4cGFuZG8gXTtcblxuXHRcdC8vIElmIG5vdCwgY3JlYXRlIG9uZVxuXHRcdGlmICggIXVubG9jayApIHtcblx0XHRcdHVubG9jayA9IERhdGEudWlkKys7XG5cblx0XHRcdC8vIFNlY3VyZSBpdCBpbiBhIG5vbi1lbnVtZXJhYmxlLCBub24td3JpdGFibGUgcHJvcGVydHlcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGRlc2NyaXB0b3JbIHRoaXMuZXhwYW5kbyBdID0geyB2YWx1ZTogdW5sb2NrIH07XG5cdFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKCBvd25lciwgZGVzY3JpcHRvciApO1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBBbmRyb2lkPDRcblx0XHRcdC8vIEZhbGxiYWNrIHRvIGEgbGVzcyBzZWN1cmUgZGVmaW5pdGlvblxuXHRcdFx0fSBjYXRjaCAoIGUgKSB7XG5cdFx0XHRcdGRlc2NyaXB0b3JbIHRoaXMuZXhwYW5kbyBdID0gdW5sb2NrO1xuXHRcdFx0XHRqUXVlcnkuZXh0ZW5kKCBvd25lciwgZGVzY3JpcHRvciApO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIEVuc3VyZSB0aGUgY2FjaGUgb2JqZWN0XG5cdFx0aWYgKCAhdGhpcy5jYWNoZVsgdW5sb2NrIF0gKSB7XG5cdFx0XHR0aGlzLmNhY2hlWyB1bmxvY2sgXSA9IHt9O1xuXHRcdH1cblxuXHRcdHJldHVybiB1bmxvY2s7XG5cdH0sXG5cdHNldDogZnVuY3Rpb24oIG93bmVyLCBkYXRhLCB2YWx1ZSApIHtcblx0XHR2YXIgcHJvcCxcblx0XHRcdC8vIFRoZXJlIG1heSBiZSBhbiB1bmxvY2sgYXNzaWduZWQgdG8gdGhpcyBub2RlLFxuXHRcdFx0Ly8gaWYgdGhlcmUgaXMgbm8gZW50cnkgZm9yIHRoaXMgXCJvd25lclwiLCBjcmVhdGUgb25lIGlubGluZVxuXHRcdFx0Ly8gYW5kIHNldCB0aGUgdW5sb2NrIGFzIHRob3VnaCBhbiBvd25lciBlbnRyeSBoYWQgYWx3YXlzIGV4aXN0ZWRcblx0XHRcdHVubG9jayA9IHRoaXMua2V5KCBvd25lciApLFxuXHRcdFx0Y2FjaGUgPSB0aGlzLmNhY2hlWyB1bmxvY2sgXTtcblxuXHRcdC8vIEhhbmRsZTogWyBvd25lciwga2V5LCB2YWx1ZSBdIGFyZ3Ncblx0XHRpZiAoIHR5cGVvZiBkYXRhID09PSBcInN0cmluZ1wiICkge1xuXHRcdFx0Y2FjaGVbIGRhdGEgXSA9IHZhbHVlO1xuXG5cdFx0Ly8gSGFuZGxlOiBbIG93bmVyLCB7IHByb3BlcnRpZXMgfSBdIGFyZ3Ncblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gRnJlc2ggYXNzaWdubWVudHMgYnkgb2JqZWN0IGFyZSBzaGFsbG93IGNvcGllZFxuXHRcdFx0aWYgKCBqUXVlcnkuaXNFbXB0eU9iamVjdCggY2FjaGUgKSApIHtcblx0XHRcdFx0alF1ZXJ5LmV4dGVuZCggdGhpcy5jYWNoZVsgdW5sb2NrIF0sIGRhdGEgKTtcblx0XHRcdC8vIE90aGVyd2lzZSwgY29weSB0aGUgcHJvcGVydGllcyBvbmUtYnktb25lIHRvIHRoZSBjYWNoZSBvYmplY3Rcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGZvciAoIHByb3AgaW4gZGF0YSApIHtcblx0XHRcdFx0XHRjYWNoZVsgcHJvcCBdID0gZGF0YVsgcHJvcCBdO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiBjYWNoZTtcblx0fSxcblx0Z2V0OiBmdW5jdGlvbiggb3duZXIsIGtleSApIHtcblx0XHQvLyBFaXRoZXIgYSB2YWxpZCBjYWNoZSBpcyBmb3VuZCwgb3Igd2lsbCBiZSBjcmVhdGVkLlxuXHRcdC8vIE5ldyBjYWNoZXMgd2lsbCBiZSBjcmVhdGVkIGFuZCB0aGUgdW5sb2NrIHJldHVybmVkLFxuXHRcdC8vIGFsbG93aW5nIGRpcmVjdCBhY2Nlc3MgdG8gdGhlIG5ld2x5IGNyZWF0ZWRcblx0XHQvLyBlbXB0eSBkYXRhIG9iamVjdC4gQSB2YWxpZCBvd25lciBvYmplY3QgbXVzdCBiZSBwcm92aWRlZC5cblx0XHR2YXIgY2FjaGUgPSB0aGlzLmNhY2hlWyB0aGlzLmtleSggb3duZXIgKSBdO1xuXG5cdFx0cmV0dXJuIGtleSA9PT0gdW5kZWZpbmVkID9cblx0XHRcdGNhY2hlIDogY2FjaGVbIGtleSBdO1xuXHR9LFxuXHRhY2Nlc3M6IGZ1bmN0aW9uKCBvd25lciwga2V5LCB2YWx1ZSApIHtcblx0XHR2YXIgc3RvcmVkO1xuXHRcdC8vIEluIGNhc2VzIHdoZXJlIGVpdGhlcjpcblx0XHQvL1xuXHRcdC8vICAgMS4gTm8ga2V5IHdhcyBzcGVjaWZpZWRcblx0XHQvLyAgIDIuIEEgc3RyaW5nIGtleSB3YXMgc3BlY2lmaWVkLCBidXQgbm8gdmFsdWUgcHJvdmlkZWRcblx0XHQvL1xuXHRcdC8vIFRha2UgdGhlIFwicmVhZFwiIHBhdGggYW5kIGFsbG93IHRoZSBnZXQgbWV0aG9kIHRvIGRldGVybWluZVxuXHRcdC8vIHdoaWNoIHZhbHVlIHRvIHJldHVybiwgcmVzcGVjdGl2ZWx5IGVpdGhlcjpcblx0XHQvL1xuXHRcdC8vICAgMS4gVGhlIGVudGlyZSBjYWNoZSBvYmplY3Rcblx0XHQvLyAgIDIuIFRoZSBkYXRhIHN0b3JlZCBhdCB0aGUga2V5XG5cdFx0Ly9cblx0XHRpZiAoIGtleSA9PT0gdW5kZWZpbmVkIHx8XG5cdFx0XHRcdCgoa2V5ICYmIHR5cGVvZiBrZXkgPT09IFwic3RyaW5nXCIpICYmIHZhbHVlID09PSB1bmRlZmluZWQpICkge1xuXG5cdFx0XHRzdG9yZWQgPSB0aGlzLmdldCggb3duZXIsIGtleSApO1xuXG5cdFx0XHRyZXR1cm4gc3RvcmVkICE9PSB1bmRlZmluZWQgP1xuXHRcdFx0XHRzdG9yZWQgOiB0aGlzLmdldCggb3duZXIsIGpRdWVyeS5jYW1lbENhc2Uoa2V5KSApO1xuXHRcdH1cblxuXHRcdC8vIFsqXVdoZW4gdGhlIGtleSBpcyBub3QgYSBzdHJpbmcsIG9yIGJvdGggYSBrZXkgYW5kIHZhbHVlXG5cdFx0Ly8gYXJlIHNwZWNpZmllZCwgc2V0IG9yIGV4dGVuZCAoZXhpc3Rpbmcgb2JqZWN0cykgd2l0aCBlaXRoZXI6XG5cdFx0Ly9cblx0XHQvLyAgIDEuIEFuIG9iamVjdCBvZiBwcm9wZXJ0aWVzXG5cdFx0Ly8gICAyLiBBIGtleSBhbmQgdmFsdWVcblx0XHQvL1xuXHRcdHRoaXMuc2V0KCBvd25lciwga2V5LCB2YWx1ZSApO1xuXG5cdFx0Ly8gU2luY2UgdGhlIFwic2V0XCIgcGF0aCBjYW4gaGF2ZSB0d28gcG9zc2libGUgZW50cnkgcG9pbnRzXG5cdFx0Ly8gcmV0dXJuIHRoZSBleHBlY3RlZCBkYXRhIGJhc2VkIG9uIHdoaWNoIHBhdGggd2FzIHRha2VuWypdXG5cdFx0cmV0dXJuIHZhbHVlICE9PSB1bmRlZmluZWQgPyB2YWx1ZSA6IGtleTtcblx0fSxcblx0cmVtb3ZlOiBmdW5jdGlvbiggb3duZXIsIGtleSApIHtcblx0XHR2YXIgaSwgbmFtZSwgY2FtZWwsXG5cdFx0XHR1bmxvY2sgPSB0aGlzLmtleSggb3duZXIgKSxcblx0XHRcdGNhY2hlID0gdGhpcy5jYWNoZVsgdW5sb2NrIF07XG5cblx0XHRpZiAoIGtleSA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0dGhpcy5jYWNoZVsgdW5sb2NrIF0gPSB7fTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBTdXBwb3J0IGFycmF5IG9yIHNwYWNlIHNlcGFyYXRlZCBzdHJpbmcgb2Yga2V5c1xuXHRcdFx0aWYgKCBqUXVlcnkuaXNBcnJheSgga2V5ICkgKSB7XG5cdFx0XHRcdC8vIElmIFwibmFtZVwiIGlzIGFuIGFycmF5IG9mIGtleXMuLi5cblx0XHRcdFx0Ly8gV2hlbiBkYXRhIGlzIGluaXRpYWxseSBjcmVhdGVkLCB2aWEgKFwia2V5XCIsIFwidmFsXCIpIHNpZ25hdHVyZSxcblx0XHRcdFx0Ly8ga2V5cyB3aWxsIGJlIGNvbnZlcnRlZCB0byBjYW1lbENhc2UuXG5cdFx0XHRcdC8vIFNpbmNlIHRoZXJlIGlzIG5vIHdheSB0byB0ZWxsIF9ob3dfIGEga2V5IHdhcyBhZGRlZCwgcmVtb3ZlXG5cdFx0XHRcdC8vIGJvdGggcGxhaW4ga2V5IGFuZCBjYW1lbENhc2Uga2V5LiAjMTI3ODZcblx0XHRcdFx0Ly8gVGhpcyB3aWxsIG9ubHkgcGVuYWxpemUgdGhlIGFycmF5IGFyZ3VtZW50IHBhdGguXG5cdFx0XHRcdG5hbWUgPSBrZXkuY29uY2F0KCBrZXkubWFwKCBqUXVlcnkuY2FtZWxDYXNlICkgKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGNhbWVsID0galF1ZXJ5LmNhbWVsQ2FzZSgga2V5ICk7XG5cdFx0XHRcdC8vIFRyeSB0aGUgc3RyaW5nIGFzIGEga2V5IGJlZm9yZSBhbnkgbWFuaXB1bGF0aW9uXG5cdFx0XHRcdGlmICgga2V5IGluIGNhY2hlICkge1xuXHRcdFx0XHRcdG5hbWUgPSBbIGtleSwgY2FtZWwgXTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHQvLyBJZiBhIGtleSB3aXRoIHRoZSBzcGFjZXMgZXhpc3RzLCB1c2UgaXQuXG5cdFx0XHRcdFx0Ly8gT3RoZXJ3aXNlLCBjcmVhdGUgYW4gYXJyYXkgYnkgbWF0Y2hpbmcgbm9uLXdoaXRlc3BhY2Vcblx0XHRcdFx0XHRuYW1lID0gY2FtZWw7XG5cdFx0XHRcdFx0bmFtZSA9IG5hbWUgaW4gY2FjaGUgP1xuXHRcdFx0XHRcdFx0WyBuYW1lIF0gOiAoIG5hbWUubWF0Y2goIHJub3R3aGl0ZSApIHx8IFtdICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0aSA9IG5hbWUubGVuZ3RoO1xuXHRcdFx0d2hpbGUgKCBpLS0gKSB7XG5cdFx0XHRcdGRlbGV0ZSBjYWNoZVsgbmFtZVsgaSBdIF07XG5cdFx0XHR9XG5cdFx0fVxuXHR9LFxuXHRoYXNEYXRhOiBmdW5jdGlvbiggb3duZXIgKSB7XG5cdFx0cmV0dXJuICFqUXVlcnkuaXNFbXB0eU9iamVjdChcblx0XHRcdHRoaXMuY2FjaGVbIG93bmVyWyB0aGlzLmV4cGFuZG8gXSBdIHx8IHt9XG5cdFx0KTtcblx0fSxcblx0ZGlzY2FyZDogZnVuY3Rpb24oIG93bmVyICkge1xuXHRcdGlmICggb3duZXJbIHRoaXMuZXhwYW5kbyBdICkge1xuXHRcdFx0ZGVsZXRlIHRoaXMuY2FjaGVbIG93bmVyWyB0aGlzLmV4cGFuZG8gXSBdO1xuXHRcdH1cblx0fVxufTtcbnZhciBkYXRhX3ByaXYgPSBuZXcgRGF0YSgpO1xuXG52YXIgZGF0YV91c2VyID0gbmV3IERhdGEoKTtcblxuXG5cbi8vXHRJbXBsZW1lbnRhdGlvbiBTdW1tYXJ5XG4vL1xuLy9cdDEuIEVuZm9yY2UgQVBJIHN1cmZhY2UgYW5kIHNlbWFudGljIGNvbXBhdGliaWxpdHkgd2l0aCAxLjkueCBicmFuY2hcbi8vXHQyLiBJbXByb3ZlIHRoZSBtb2R1bGUncyBtYWludGFpbmFiaWxpdHkgYnkgcmVkdWNpbmcgdGhlIHN0b3JhZ2Vcbi8vXHRcdHBhdGhzIHRvIGEgc2luZ2xlIG1lY2hhbmlzbS5cbi8vXHQzLiBVc2UgdGhlIHNhbWUgc2luZ2xlIG1lY2hhbmlzbSB0byBzdXBwb3J0IFwicHJpdmF0ZVwiIGFuZCBcInVzZXJcIiBkYXRhLlxuLy9cdDQuIF9OZXZlcl8gZXhwb3NlIFwicHJpdmF0ZVwiIGRhdGEgdG8gdXNlciBjb2RlIChUT0RPOiBEcm9wIF9kYXRhLCBfcmVtb3ZlRGF0YSlcbi8vXHQ1LiBBdm9pZCBleHBvc2luZyBpbXBsZW1lbnRhdGlvbiBkZXRhaWxzIG9uIHVzZXIgb2JqZWN0cyAoZWcuIGV4cGFuZG8gcHJvcGVydGllcylcbi8vXHQ2LiBQcm92aWRlIGEgY2xlYXIgcGF0aCBmb3IgaW1wbGVtZW50YXRpb24gdXBncmFkZSB0byBXZWFrTWFwIGluIDIwMTRcblxudmFyIHJicmFjZSA9IC9eKD86XFx7W1xcd1xcV10qXFx9fFxcW1tcXHdcXFddKlxcXSkkLyxcblx0cm11bHRpRGFzaCA9IC8oW0EtWl0pL2c7XG5cbmZ1bmN0aW9uIGRhdGFBdHRyKCBlbGVtLCBrZXksIGRhdGEgKSB7XG5cdHZhciBuYW1lO1xuXG5cdC8vIElmIG5vdGhpbmcgd2FzIGZvdW5kIGludGVybmFsbHksIHRyeSB0byBmZXRjaCBhbnlcblx0Ly8gZGF0YSBmcm9tIHRoZSBIVE1MNSBkYXRhLSogYXR0cmlidXRlXG5cdGlmICggZGF0YSA9PT0gdW5kZWZpbmVkICYmIGVsZW0ubm9kZVR5cGUgPT09IDEgKSB7XG5cdFx0bmFtZSA9IFwiZGF0YS1cIiArIGtleS5yZXBsYWNlKCBybXVsdGlEYXNoLCBcIi0kMVwiICkudG9Mb3dlckNhc2UoKTtcblx0XHRkYXRhID0gZWxlbS5nZXRBdHRyaWJ1dGUoIG5hbWUgKTtcblxuXHRcdGlmICggdHlwZW9mIGRhdGEgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRkYXRhID0gZGF0YSA9PT0gXCJ0cnVlXCIgPyB0cnVlIDpcblx0XHRcdFx0XHRkYXRhID09PSBcImZhbHNlXCIgPyBmYWxzZSA6XG5cdFx0XHRcdFx0ZGF0YSA9PT0gXCJudWxsXCIgPyBudWxsIDpcblx0XHRcdFx0XHQvLyBPbmx5IGNvbnZlcnQgdG8gYSBudW1iZXIgaWYgaXQgZG9lc24ndCBjaGFuZ2UgdGhlIHN0cmluZ1xuXHRcdFx0XHRcdCtkYXRhICsgXCJcIiA9PT0gZGF0YSA/ICtkYXRhIDpcblx0XHRcdFx0XHRyYnJhY2UudGVzdCggZGF0YSApID8galF1ZXJ5LnBhcnNlSlNPTiggZGF0YSApIDpcblx0XHRcdFx0XHRkYXRhO1xuXHRcdFx0fSBjYXRjaCggZSApIHt9XG5cblx0XHRcdC8vIE1ha2Ugc3VyZSB3ZSBzZXQgdGhlIGRhdGEgc28gaXQgaXNuJ3QgY2hhbmdlZCBsYXRlclxuXHRcdFx0ZGF0YV91c2VyLnNldCggZWxlbSwga2V5LCBkYXRhICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGRhdGEgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHR9XG5cdHJldHVybiBkYXRhO1xufVxuXG5qUXVlcnkuZXh0ZW5kKHtcblx0aGFzRGF0YTogZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0cmV0dXJuIGRhdGFfdXNlci5oYXNEYXRhKCBlbGVtICkgfHwgZGF0YV9wcml2Lmhhc0RhdGEoIGVsZW0gKTtcblx0fSxcblxuXHRkYXRhOiBmdW5jdGlvbiggZWxlbSwgbmFtZSwgZGF0YSApIHtcblx0XHRyZXR1cm4gZGF0YV91c2VyLmFjY2VzcyggZWxlbSwgbmFtZSwgZGF0YSApO1xuXHR9LFxuXG5cdHJlbW92ZURhdGE6IGZ1bmN0aW9uKCBlbGVtLCBuYW1lICkge1xuXHRcdGRhdGFfdXNlci5yZW1vdmUoIGVsZW0sIG5hbWUgKTtcblx0fSxcblxuXHQvLyBUT0RPOiBOb3cgdGhhdCBhbGwgY2FsbHMgdG8gX2RhdGEgYW5kIF9yZW1vdmVEYXRhIGhhdmUgYmVlbiByZXBsYWNlZFxuXHQvLyB3aXRoIGRpcmVjdCBjYWxscyB0byBkYXRhX3ByaXYgbWV0aG9kcywgdGhlc2UgY2FuIGJlIGRlcHJlY2F0ZWQuXG5cdF9kYXRhOiBmdW5jdGlvbiggZWxlbSwgbmFtZSwgZGF0YSApIHtcblx0XHRyZXR1cm4gZGF0YV9wcml2LmFjY2VzcyggZWxlbSwgbmFtZSwgZGF0YSApO1xuXHR9LFxuXG5cdF9yZW1vdmVEYXRhOiBmdW5jdGlvbiggZWxlbSwgbmFtZSApIHtcblx0XHRkYXRhX3ByaXYucmVtb3ZlKCBlbGVtLCBuYW1lICk7XG5cdH1cbn0pO1xuXG5qUXVlcnkuZm4uZXh0ZW5kKHtcblx0ZGF0YTogZnVuY3Rpb24oIGtleSwgdmFsdWUgKSB7XG5cdFx0dmFyIGksIG5hbWUsIGRhdGEsXG5cdFx0XHRlbGVtID0gdGhpc1sgMCBdLFxuXHRcdFx0YXR0cnMgPSBlbGVtICYmIGVsZW0uYXR0cmlidXRlcztcblxuXHRcdC8vIEdldHMgYWxsIHZhbHVlc1xuXHRcdGlmICgga2V5ID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRpZiAoIHRoaXMubGVuZ3RoICkge1xuXHRcdFx0XHRkYXRhID0gZGF0YV91c2VyLmdldCggZWxlbSApO1xuXG5cdFx0XHRcdGlmICggZWxlbS5ub2RlVHlwZSA9PT0gMSAmJiAhZGF0YV9wcml2LmdldCggZWxlbSwgXCJoYXNEYXRhQXR0cnNcIiApICkge1xuXHRcdFx0XHRcdGkgPSBhdHRycy5sZW5ndGg7XG5cdFx0XHRcdFx0d2hpbGUgKCBpLS0gKSB7XG5cblx0XHRcdFx0XHRcdC8vIFN1cHBvcnQ6IElFMTErXG5cdFx0XHRcdFx0XHQvLyBUaGUgYXR0cnMgZWxlbWVudHMgY2FuIGJlIG51bGwgKCMxNDg5NClcblx0XHRcdFx0XHRcdGlmICggYXR0cnNbIGkgXSApIHtcblx0XHRcdFx0XHRcdFx0bmFtZSA9IGF0dHJzWyBpIF0ubmFtZTtcblx0XHRcdFx0XHRcdFx0aWYgKCBuYW1lLmluZGV4T2YoIFwiZGF0YS1cIiApID09PSAwICkge1xuXHRcdFx0XHRcdFx0XHRcdG5hbWUgPSBqUXVlcnkuY2FtZWxDYXNlKCBuYW1lLnNsaWNlKDUpICk7XG5cdFx0XHRcdFx0XHRcdFx0ZGF0YUF0dHIoIGVsZW0sIG5hbWUsIGRhdGFbIG5hbWUgXSApO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGRhdGFfcHJpdi5zZXQoIGVsZW0sIFwiaGFzRGF0YUF0dHJzXCIsIHRydWUgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZGF0YTtcblx0XHR9XG5cblx0XHQvLyBTZXRzIG11bHRpcGxlIHZhbHVlc1xuXHRcdGlmICggdHlwZW9mIGtleSA9PT0gXCJvYmplY3RcIiApIHtcblx0XHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGRhdGFfdXNlci5zZXQoIHRoaXMsIGtleSApO1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGFjY2VzcyggdGhpcywgZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdFx0dmFyIGRhdGEsXG5cdFx0XHRcdGNhbWVsS2V5ID0galF1ZXJ5LmNhbWVsQ2FzZSgga2V5ICk7XG5cblx0XHRcdC8vIFRoZSBjYWxsaW5nIGpRdWVyeSBvYmplY3QgKGVsZW1lbnQgbWF0Y2hlcykgaXMgbm90IGVtcHR5XG5cdFx0XHQvLyAoYW5kIHRoZXJlZm9yZSBoYXMgYW4gZWxlbWVudCBhcHBlYXJzIGF0IHRoaXNbIDAgXSkgYW5kIHRoZVxuXHRcdFx0Ly8gYHZhbHVlYCBwYXJhbWV0ZXIgd2FzIG5vdCB1bmRlZmluZWQuIEFuIGVtcHR5IGpRdWVyeSBvYmplY3Rcblx0XHRcdC8vIHdpbGwgcmVzdWx0IGluIGB1bmRlZmluZWRgIGZvciBlbGVtID0gdGhpc1sgMCBdIHdoaWNoIHdpbGxcblx0XHRcdC8vIHRocm93IGFuIGV4Y2VwdGlvbiBpZiBhbiBhdHRlbXB0IHRvIHJlYWQgYSBkYXRhIGNhY2hlIGlzIG1hZGUuXG5cdFx0XHRpZiAoIGVsZW0gJiYgdmFsdWUgPT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0Ly8gQXR0ZW1wdCB0byBnZXQgZGF0YSBmcm9tIHRoZSBjYWNoZVxuXHRcdFx0XHQvLyB3aXRoIHRoZSBrZXkgYXMtaXNcblx0XHRcdFx0ZGF0YSA9IGRhdGFfdXNlci5nZXQoIGVsZW0sIGtleSApO1xuXHRcdFx0XHRpZiAoIGRhdGEgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0XHRyZXR1cm4gZGF0YTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIEF0dGVtcHQgdG8gZ2V0IGRhdGEgZnJvbSB0aGUgY2FjaGVcblx0XHRcdFx0Ly8gd2l0aCB0aGUga2V5IGNhbWVsaXplZFxuXHRcdFx0XHRkYXRhID0gZGF0YV91c2VyLmdldCggZWxlbSwgY2FtZWxLZXkgKTtcblx0XHRcdFx0aWYgKCBkYXRhICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdFx0cmV0dXJuIGRhdGE7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBBdHRlbXB0IHRvIFwiZGlzY292ZXJcIiB0aGUgZGF0YSBpblxuXHRcdFx0XHQvLyBIVE1MNSBjdXN0b20gZGF0YS0qIGF0dHJzXG5cdFx0XHRcdGRhdGEgPSBkYXRhQXR0ciggZWxlbSwgY2FtZWxLZXksIHVuZGVmaW5lZCApO1xuXHRcdFx0XHRpZiAoIGRhdGEgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0XHRyZXR1cm4gZGF0YTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIFdlIHRyaWVkIHJlYWxseSBoYXJkLCBidXQgdGhlIGRhdGEgZG9lc24ndCBleGlzdC5cblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTZXQgdGhlIGRhdGEuLi5cblx0XHRcdHRoaXMuZWFjaChmdW5jdGlvbigpIHtcblx0XHRcdFx0Ly8gRmlyc3QsIGF0dGVtcHQgdG8gc3RvcmUgYSBjb3B5IG9yIHJlZmVyZW5jZSBvZiBhbnlcblx0XHRcdFx0Ly8gZGF0YSB0aGF0IG1pZ2h0J3ZlIGJlZW4gc3RvcmUgd2l0aCBhIGNhbWVsQ2FzZWQga2V5LlxuXHRcdFx0XHR2YXIgZGF0YSA9IGRhdGFfdXNlci5nZXQoIHRoaXMsIGNhbWVsS2V5ICk7XG5cblx0XHRcdFx0Ly8gRm9yIEhUTUw1IGRhdGEtKiBhdHRyaWJ1dGUgaW50ZXJvcCwgd2UgaGF2ZSB0b1xuXHRcdFx0XHQvLyBzdG9yZSBwcm9wZXJ0eSBuYW1lcyB3aXRoIGRhc2hlcyBpbiBhIGNhbWVsQ2FzZSBmb3JtLlxuXHRcdFx0XHQvLyBUaGlzIG1pZ2h0IG5vdCBhcHBseSB0byBhbGwgcHJvcGVydGllcy4uLipcblx0XHRcdFx0ZGF0YV91c2VyLnNldCggdGhpcywgY2FtZWxLZXksIHZhbHVlICk7XG5cblx0XHRcdFx0Ly8gKi4uLiBJbiB0aGUgY2FzZSBvZiBwcm9wZXJ0aWVzIHRoYXQgbWlnaHQgX2FjdHVhbGx5X1xuXHRcdFx0XHQvLyBoYXZlIGRhc2hlcywgd2UgbmVlZCB0byBhbHNvIHN0b3JlIGEgY29weSBvZiB0aGF0XG5cdFx0XHRcdC8vIHVuY2hhbmdlZCBwcm9wZXJ0eS5cblx0XHRcdFx0aWYgKCBrZXkuaW5kZXhPZihcIi1cIikgIT09IC0xICYmIGRhdGEgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0XHRkYXRhX3VzZXIuc2V0KCB0aGlzLCBrZXksIHZhbHVlICk7XG5cdFx0XHRcdH1cblx0XHRcdH0pO1xuXHRcdH0sIG51bGwsIHZhbHVlLCBhcmd1bWVudHMubGVuZ3RoID4gMSwgbnVsbCwgdHJ1ZSApO1xuXHR9LFxuXG5cdHJlbW92ZURhdGE6IGZ1bmN0aW9uKCBrZXkgKSB7XG5cdFx0cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcblx0XHRcdGRhdGFfdXNlci5yZW1vdmUoIHRoaXMsIGtleSApO1xuXHRcdH0pO1xuXHR9XG59KTtcblxuXG5qUXVlcnkuZXh0ZW5kKHtcblx0cXVldWU6IGZ1bmN0aW9uKCBlbGVtLCB0eXBlLCBkYXRhICkge1xuXHRcdHZhciBxdWV1ZTtcblxuXHRcdGlmICggZWxlbSApIHtcblx0XHRcdHR5cGUgPSAoIHR5cGUgfHwgXCJmeFwiICkgKyBcInF1ZXVlXCI7XG5cdFx0XHRxdWV1ZSA9IGRhdGFfcHJpdi5nZXQoIGVsZW0sIHR5cGUgKTtcblxuXHRcdFx0Ly8gU3BlZWQgdXAgZGVxdWV1ZSBieSBnZXR0aW5nIG91dCBxdWlja2x5IGlmIHRoaXMgaXMganVzdCBhIGxvb2t1cFxuXHRcdFx0aWYgKCBkYXRhICkge1xuXHRcdFx0XHRpZiAoICFxdWV1ZSB8fCBqUXVlcnkuaXNBcnJheSggZGF0YSApICkge1xuXHRcdFx0XHRcdHF1ZXVlID0gZGF0YV9wcml2LmFjY2VzcyggZWxlbSwgdHlwZSwgalF1ZXJ5Lm1ha2VBcnJheShkYXRhKSApO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHF1ZXVlLnB1c2goIGRhdGEgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHF1ZXVlIHx8IFtdO1xuXHRcdH1cblx0fSxcblxuXHRkZXF1ZXVlOiBmdW5jdGlvbiggZWxlbSwgdHlwZSApIHtcblx0XHR0eXBlID0gdHlwZSB8fCBcImZ4XCI7XG5cblx0XHR2YXIgcXVldWUgPSBqUXVlcnkucXVldWUoIGVsZW0sIHR5cGUgKSxcblx0XHRcdHN0YXJ0TGVuZ3RoID0gcXVldWUubGVuZ3RoLFxuXHRcdFx0Zm4gPSBxdWV1ZS5zaGlmdCgpLFxuXHRcdFx0aG9va3MgPSBqUXVlcnkuX3F1ZXVlSG9va3MoIGVsZW0sIHR5cGUgKSxcblx0XHRcdG5leHQgPSBmdW5jdGlvbigpIHtcblx0XHRcdFx0alF1ZXJ5LmRlcXVldWUoIGVsZW0sIHR5cGUgKTtcblx0XHRcdH07XG5cblx0XHQvLyBJZiB0aGUgZnggcXVldWUgaXMgZGVxdWV1ZWQsIGFsd2F5cyByZW1vdmUgdGhlIHByb2dyZXNzIHNlbnRpbmVsXG5cdFx0aWYgKCBmbiA9PT0gXCJpbnByb2dyZXNzXCIgKSB7XG5cdFx0XHRmbiA9IHF1ZXVlLnNoaWZ0KCk7XG5cdFx0XHRzdGFydExlbmd0aC0tO1xuXHRcdH1cblxuXHRcdGlmICggZm4gKSB7XG5cblx0XHRcdC8vIEFkZCBhIHByb2dyZXNzIHNlbnRpbmVsIHRvIHByZXZlbnQgdGhlIGZ4IHF1ZXVlIGZyb20gYmVpbmdcblx0XHRcdC8vIGF1dG9tYXRpY2FsbHkgZGVxdWV1ZWRcblx0XHRcdGlmICggdHlwZSA9PT0gXCJmeFwiICkge1xuXHRcdFx0XHRxdWV1ZS51bnNoaWZ0KCBcImlucHJvZ3Jlc3NcIiApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBDbGVhciB1cCB0aGUgbGFzdCBxdWV1ZSBzdG9wIGZ1bmN0aW9uXG5cdFx0XHRkZWxldGUgaG9va3Muc3RvcDtcblx0XHRcdGZuLmNhbGwoIGVsZW0sIG5leHQsIGhvb2tzICk7XG5cdFx0fVxuXG5cdFx0aWYgKCAhc3RhcnRMZW5ndGggJiYgaG9va3MgKSB7XG5cdFx0XHRob29rcy5lbXB0eS5maXJlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8vIE5vdCBwdWJsaWMgLSBnZW5lcmF0ZSBhIHF1ZXVlSG9va3Mgb2JqZWN0LCBvciByZXR1cm4gdGhlIGN1cnJlbnQgb25lXG5cdF9xdWV1ZUhvb2tzOiBmdW5jdGlvbiggZWxlbSwgdHlwZSApIHtcblx0XHR2YXIga2V5ID0gdHlwZSArIFwicXVldWVIb29rc1wiO1xuXHRcdHJldHVybiBkYXRhX3ByaXYuZ2V0KCBlbGVtLCBrZXkgKSB8fCBkYXRhX3ByaXYuYWNjZXNzKCBlbGVtLCBrZXksIHtcblx0XHRcdGVtcHR5OiBqUXVlcnkuQ2FsbGJhY2tzKFwib25jZSBtZW1vcnlcIikuYWRkKGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRkYXRhX3ByaXYucmVtb3ZlKCBlbGVtLCBbIHR5cGUgKyBcInF1ZXVlXCIsIGtleSBdICk7XG5cdFx0XHR9KVxuXHRcdH0pO1xuXHR9XG59KTtcblxualF1ZXJ5LmZuLmV4dGVuZCh7XG5cdHF1ZXVlOiBmdW5jdGlvbiggdHlwZSwgZGF0YSApIHtcblx0XHR2YXIgc2V0dGVyID0gMjtcblxuXHRcdGlmICggdHlwZW9mIHR5cGUgIT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRkYXRhID0gdHlwZTtcblx0XHRcdHR5cGUgPSBcImZ4XCI7XG5cdFx0XHRzZXR0ZXItLTtcblx0XHR9XG5cblx0XHRpZiAoIGFyZ3VtZW50cy5sZW5ndGggPCBzZXR0ZXIgKSB7XG5cdFx0XHRyZXR1cm4galF1ZXJ5LnF1ZXVlKCB0aGlzWzBdLCB0eXBlICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGRhdGEgPT09IHVuZGVmaW5lZCA/XG5cdFx0XHR0aGlzIDpcblx0XHRcdHRoaXMuZWFjaChmdW5jdGlvbigpIHtcblx0XHRcdFx0dmFyIHF1ZXVlID0galF1ZXJ5LnF1ZXVlKCB0aGlzLCB0eXBlLCBkYXRhICk7XG5cblx0XHRcdFx0Ly8gRW5zdXJlIGEgaG9va3MgZm9yIHRoaXMgcXVldWVcblx0XHRcdFx0alF1ZXJ5Ll9xdWV1ZUhvb2tzKCB0aGlzLCB0eXBlICk7XG5cblx0XHRcdFx0aWYgKCB0eXBlID09PSBcImZ4XCIgJiYgcXVldWVbMF0gIT09IFwiaW5wcm9ncmVzc1wiICkge1xuXHRcdFx0XHRcdGpRdWVyeS5kZXF1ZXVlKCB0aGlzLCB0eXBlICk7XG5cdFx0XHRcdH1cblx0XHRcdH0pO1xuXHR9LFxuXHRkZXF1ZXVlOiBmdW5jdGlvbiggdHlwZSApIHtcblx0XHRyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuXHRcdFx0alF1ZXJ5LmRlcXVldWUoIHRoaXMsIHR5cGUgKTtcblx0XHR9KTtcblx0fSxcblx0Y2xlYXJRdWV1ZTogZnVuY3Rpb24oIHR5cGUgKSB7XG5cdFx0cmV0dXJuIHRoaXMucXVldWUoIHR5cGUgfHwgXCJmeFwiLCBbXSApO1xuXHR9LFxuXHQvLyBHZXQgYSBwcm9taXNlIHJlc29sdmVkIHdoZW4gcXVldWVzIG9mIGEgY2VydGFpbiB0eXBlXG5cdC8vIGFyZSBlbXB0aWVkIChmeCBpcyB0aGUgdHlwZSBieSBkZWZhdWx0KVxuXHRwcm9taXNlOiBmdW5jdGlvbiggdHlwZSwgb2JqICkge1xuXHRcdHZhciB0bXAsXG5cdFx0XHRjb3VudCA9IDEsXG5cdFx0XHRkZWZlciA9IGpRdWVyeS5EZWZlcnJlZCgpLFxuXHRcdFx0ZWxlbWVudHMgPSB0aGlzLFxuXHRcdFx0aSA9IHRoaXMubGVuZ3RoLFxuXHRcdFx0cmVzb2x2ZSA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRpZiAoICEoIC0tY291bnQgKSApIHtcblx0XHRcdFx0XHRkZWZlci5yZXNvbHZlV2l0aCggZWxlbWVudHMsIFsgZWxlbWVudHMgXSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9O1xuXG5cdFx0aWYgKCB0eXBlb2YgdHlwZSAhPT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdG9iaiA9IHR5cGU7XG5cdFx0XHR0eXBlID0gdW5kZWZpbmVkO1xuXHRcdH1cblx0XHR0eXBlID0gdHlwZSB8fCBcImZ4XCI7XG5cblx0XHR3aGlsZSAoIGktLSApIHtcblx0XHRcdHRtcCA9IGRhdGFfcHJpdi5nZXQoIGVsZW1lbnRzWyBpIF0sIHR5cGUgKyBcInF1ZXVlSG9va3NcIiApO1xuXHRcdFx0aWYgKCB0bXAgJiYgdG1wLmVtcHR5ICkge1xuXHRcdFx0XHRjb3VudCsrO1xuXHRcdFx0XHR0bXAuZW1wdHkuYWRkKCByZXNvbHZlICk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJlc29sdmUoKTtcblx0XHRyZXR1cm4gZGVmZXIucHJvbWlzZSggb2JqICk7XG5cdH1cbn0pO1xudmFyIHBudW0gPSAoL1srLV0/KD86XFxkKlxcLnwpXFxkKyg/OltlRV1bKy1dP1xcZCt8KS8pLnNvdXJjZTtcblxudmFyIGNzc0V4cGFuZCA9IFsgXCJUb3BcIiwgXCJSaWdodFwiLCBcIkJvdHRvbVwiLCBcIkxlZnRcIiBdO1xuXG52YXIgaXNIaWRkZW4gPSBmdW5jdGlvbiggZWxlbSwgZWwgKSB7XG5cdFx0Ly8gaXNIaWRkZW4gbWlnaHQgYmUgY2FsbGVkIGZyb20galF1ZXJ5I2ZpbHRlciBmdW5jdGlvbjtcblx0XHQvLyBpbiB0aGF0IGNhc2UsIGVsZW1lbnQgd2lsbCBiZSBzZWNvbmQgYXJndW1lbnRcblx0XHRlbGVtID0gZWwgfHwgZWxlbTtcblx0XHRyZXR1cm4galF1ZXJ5LmNzcyggZWxlbSwgXCJkaXNwbGF5XCIgKSA9PT0gXCJub25lXCIgfHwgIWpRdWVyeS5jb250YWlucyggZWxlbS5vd25lckRvY3VtZW50LCBlbGVtICk7XG5cdH07XG5cbnZhciByY2hlY2thYmxlVHlwZSA9ICgvXig/OmNoZWNrYm94fHJhZGlvKSQvaSk7XG5cblxuXG4oZnVuY3Rpb24oKSB7XG5cdHZhciBmcmFnbWVudCA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKSxcblx0XHRkaXYgPSBmcmFnbWVudC5hcHBlbmRDaGlsZCggZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggXCJkaXZcIiApICksXG5cdFx0aW5wdXQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCBcImlucHV0XCIgKTtcblxuXHQvLyBTdXBwb3J0OiBTYWZhcmk8PTUuMVxuXHQvLyBDaGVjayBzdGF0ZSBsb3N0IGlmIHRoZSBuYW1lIGlzIHNldCAoIzExMjE3KVxuXHQvLyBTdXBwb3J0OiBXaW5kb3dzIFdlYiBBcHBzIChXV0EpXG5cdC8vIGBuYW1lYCBhbmQgYHR5cGVgIG11c3QgdXNlIC5zZXRBdHRyaWJ1dGUgZm9yIFdXQSAoIzE0OTAxKVxuXHRpbnB1dC5zZXRBdHRyaWJ1dGUoIFwidHlwZVwiLCBcInJhZGlvXCIgKTtcblx0aW5wdXQuc2V0QXR0cmlidXRlKCBcImNoZWNrZWRcIiwgXCJjaGVja2VkXCIgKTtcblx0aW5wdXQuc2V0QXR0cmlidXRlKCBcIm5hbWVcIiwgXCJ0XCIgKTtcblxuXHRkaXYuYXBwZW5kQ2hpbGQoIGlucHV0ICk7XG5cblx0Ly8gU3VwcG9ydDogU2FmYXJpPD01LjEsIEFuZHJvaWQ8NC4yXG5cdC8vIE9sZGVyIFdlYktpdCBkb2Vzbid0IGNsb25lIGNoZWNrZWQgc3RhdGUgY29ycmVjdGx5IGluIGZyYWdtZW50c1xuXHRzdXBwb3J0LmNoZWNrQ2xvbmUgPSBkaXYuY2xvbmVOb2RlKCB0cnVlICkuY2xvbmVOb2RlKCB0cnVlICkubGFzdENoaWxkLmNoZWNrZWQ7XG5cblx0Ly8gU3VwcG9ydDogSUU8PTExK1xuXHQvLyBNYWtlIHN1cmUgdGV4dGFyZWEgKGFuZCBjaGVja2JveCkgZGVmYXVsdFZhbHVlIGlzIHByb3Blcmx5IGNsb25lZFxuXHRkaXYuaW5uZXJIVE1MID0gXCI8dGV4dGFyZWE+eDwvdGV4dGFyZWE+XCI7XG5cdHN1cHBvcnQubm9DbG9uZUNoZWNrZWQgPSAhIWRpdi5jbG9uZU5vZGUoIHRydWUgKS5sYXN0Q2hpbGQuZGVmYXVsdFZhbHVlO1xufSkoKTtcbnZhciBzdHJ1bmRlZmluZWQgPSB0eXBlb2YgdW5kZWZpbmVkO1xuXG5cblxuc3VwcG9ydC5mb2N1c2luQnViYmxlcyA9IFwib25mb2N1c2luXCIgaW4gd2luZG93O1xuXG5cbnZhclxuXHRya2V5RXZlbnQgPSAvXmtleS8sXG5cdHJtb3VzZUV2ZW50ID0gL14oPzptb3VzZXxwb2ludGVyfGNvbnRleHRtZW51KXxjbGljay8sXG5cdHJmb2N1c01vcnBoID0gL14oPzpmb2N1c2luZm9jdXN8Zm9jdXNvdXRibHVyKSQvLFxuXHRydHlwZW5hbWVzcGFjZSA9IC9eKFteLl0qKSg/OlxcLiguKyl8KSQvO1xuXG5mdW5jdGlvbiByZXR1cm5UcnVlKCkge1xuXHRyZXR1cm4gdHJ1ZTtcbn1cblxuZnVuY3Rpb24gcmV0dXJuRmFsc2UoKSB7XG5cdHJldHVybiBmYWxzZTtcbn1cblxuZnVuY3Rpb24gc2FmZUFjdGl2ZUVsZW1lbnQoKSB7XG5cdHRyeSB7XG5cdFx0cmV0dXJuIGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQ7XG5cdH0gY2F0Y2ggKCBlcnIgKSB7IH1cbn1cblxuLypcbiAqIEhlbHBlciBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGV2ZW50cyAtLSBub3QgcGFydCBvZiB0aGUgcHVibGljIGludGVyZmFjZS5cbiAqIFByb3BzIHRvIERlYW4gRWR3YXJkcycgYWRkRXZlbnQgbGlicmFyeSBmb3IgbWFueSBvZiB0aGUgaWRlYXMuXG4gKi9cbmpRdWVyeS5ldmVudCA9IHtcblxuXHRnbG9iYWw6IHt9LFxuXG5cdGFkZDogZnVuY3Rpb24oIGVsZW0sIHR5cGVzLCBoYW5kbGVyLCBkYXRhLCBzZWxlY3RvciApIHtcblxuXHRcdHZhciBoYW5kbGVPYmpJbiwgZXZlbnRIYW5kbGUsIHRtcCxcblx0XHRcdGV2ZW50cywgdCwgaGFuZGxlT2JqLFxuXHRcdFx0c3BlY2lhbCwgaGFuZGxlcnMsIHR5cGUsIG5hbWVzcGFjZXMsIG9yaWdUeXBlLFxuXHRcdFx0ZWxlbURhdGEgPSBkYXRhX3ByaXYuZ2V0KCBlbGVtICk7XG5cblx0XHQvLyBEb24ndCBhdHRhY2ggZXZlbnRzIHRvIG5vRGF0YSBvciB0ZXh0L2NvbW1lbnQgbm9kZXMgKGJ1dCBhbGxvdyBwbGFpbiBvYmplY3RzKVxuXHRcdGlmICggIWVsZW1EYXRhICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIENhbGxlciBjYW4gcGFzcyBpbiBhbiBvYmplY3Qgb2YgY3VzdG9tIGRhdGEgaW4gbGlldSBvZiB0aGUgaGFuZGxlclxuXHRcdGlmICggaGFuZGxlci5oYW5kbGVyICkge1xuXHRcdFx0aGFuZGxlT2JqSW4gPSBoYW5kbGVyO1xuXHRcdFx0aGFuZGxlciA9IGhhbmRsZU9iakluLmhhbmRsZXI7XG5cdFx0XHRzZWxlY3RvciA9IGhhbmRsZU9iakluLnNlbGVjdG9yO1xuXHRcdH1cblxuXHRcdC8vIE1ha2Ugc3VyZSB0aGF0IHRoZSBoYW5kbGVyIGhhcyBhIHVuaXF1ZSBJRCwgdXNlZCB0byBmaW5kL3JlbW92ZSBpdCBsYXRlclxuXHRcdGlmICggIWhhbmRsZXIuZ3VpZCApIHtcblx0XHRcdGhhbmRsZXIuZ3VpZCA9IGpRdWVyeS5ndWlkKys7XG5cdFx0fVxuXG5cdFx0Ly8gSW5pdCB0aGUgZWxlbWVudCdzIGV2ZW50IHN0cnVjdHVyZSBhbmQgbWFpbiBoYW5kbGVyLCBpZiB0aGlzIGlzIHRoZSBmaXJzdFxuXHRcdGlmICggIShldmVudHMgPSBlbGVtRGF0YS5ldmVudHMpICkge1xuXHRcdFx0ZXZlbnRzID0gZWxlbURhdGEuZXZlbnRzID0ge307XG5cdFx0fVxuXHRcdGlmICggIShldmVudEhhbmRsZSA9IGVsZW1EYXRhLmhhbmRsZSkgKSB7XG5cdFx0XHRldmVudEhhbmRsZSA9IGVsZW1EYXRhLmhhbmRsZSA9IGZ1bmN0aW9uKCBlICkge1xuXHRcdFx0XHQvLyBEaXNjYXJkIHRoZSBzZWNvbmQgZXZlbnQgb2YgYSBqUXVlcnkuZXZlbnQudHJpZ2dlcigpIGFuZFxuXHRcdFx0XHQvLyB3aGVuIGFuIGV2ZW50IGlzIGNhbGxlZCBhZnRlciBhIHBhZ2UgaGFzIHVubG9hZGVkXG5cdFx0XHRcdHJldHVybiB0eXBlb2YgalF1ZXJ5ICE9PSBzdHJ1bmRlZmluZWQgJiYgalF1ZXJ5LmV2ZW50LnRyaWdnZXJlZCAhPT0gZS50eXBlID9cblx0XHRcdFx0XHRqUXVlcnkuZXZlbnQuZGlzcGF0Y2guYXBwbHkoIGVsZW0sIGFyZ3VtZW50cyApIDogdW5kZWZpbmVkO1xuXHRcdFx0fTtcblx0XHR9XG5cblx0XHQvLyBIYW5kbGUgbXVsdGlwbGUgZXZlbnRzIHNlcGFyYXRlZCBieSBhIHNwYWNlXG5cdFx0dHlwZXMgPSAoIHR5cGVzIHx8IFwiXCIgKS5tYXRjaCggcm5vdHdoaXRlICkgfHwgWyBcIlwiIF07XG5cdFx0dCA9IHR5cGVzLmxlbmd0aDtcblx0XHR3aGlsZSAoIHQtLSApIHtcblx0XHRcdHRtcCA9IHJ0eXBlbmFtZXNwYWNlLmV4ZWMoIHR5cGVzW3RdICkgfHwgW107XG5cdFx0XHR0eXBlID0gb3JpZ1R5cGUgPSB0bXBbMV07XG5cdFx0XHRuYW1lc3BhY2VzID0gKCB0bXBbMl0gfHwgXCJcIiApLnNwbGl0KCBcIi5cIiApLnNvcnQoKTtcblxuXHRcdFx0Ly8gVGhlcmUgKm11c3QqIGJlIGEgdHlwZSwgbm8gYXR0YWNoaW5nIG5hbWVzcGFjZS1vbmx5IGhhbmRsZXJzXG5cdFx0XHRpZiAoICF0eXBlICkge1xuXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSWYgZXZlbnQgY2hhbmdlcyBpdHMgdHlwZSwgdXNlIHRoZSBzcGVjaWFsIGV2ZW50IGhhbmRsZXJzIGZvciB0aGUgY2hhbmdlZCB0eXBlXG5cdFx0XHRzcGVjaWFsID0galF1ZXJ5LmV2ZW50LnNwZWNpYWxbIHR5cGUgXSB8fCB7fTtcblxuXHRcdFx0Ly8gSWYgc2VsZWN0b3IgZGVmaW5lZCwgZGV0ZXJtaW5lIHNwZWNpYWwgZXZlbnQgYXBpIHR5cGUsIG90aGVyd2lzZSBnaXZlbiB0eXBlXG5cdFx0XHR0eXBlID0gKCBzZWxlY3RvciA/IHNwZWNpYWwuZGVsZWdhdGVUeXBlIDogc3BlY2lhbC5iaW5kVHlwZSApIHx8IHR5cGU7XG5cblx0XHRcdC8vIFVwZGF0ZSBzcGVjaWFsIGJhc2VkIG9uIG5ld2x5IHJlc2V0IHR5cGVcblx0XHRcdHNwZWNpYWwgPSBqUXVlcnkuZXZlbnQuc3BlY2lhbFsgdHlwZSBdIHx8IHt9O1xuXG5cdFx0XHQvLyBoYW5kbGVPYmogaXMgcGFzc2VkIHRvIGFsbCBldmVudCBoYW5kbGVyc1xuXHRcdFx0aGFuZGxlT2JqID0galF1ZXJ5LmV4dGVuZCh7XG5cdFx0XHRcdHR5cGU6IHR5cGUsXG5cdFx0XHRcdG9yaWdUeXBlOiBvcmlnVHlwZSxcblx0XHRcdFx0ZGF0YTogZGF0YSxcblx0XHRcdFx0aGFuZGxlcjogaGFuZGxlcixcblx0XHRcdFx0Z3VpZDogaGFuZGxlci5ndWlkLFxuXHRcdFx0XHRzZWxlY3Rvcjogc2VsZWN0b3IsXG5cdFx0XHRcdG5lZWRzQ29udGV4dDogc2VsZWN0b3IgJiYgalF1ZXJ5LmV4cHIubWF0Y2gubmVlZHNDb250ZXh0LnRlc3QoIHNlbGVjdG9yICksXG5cdFx0XHRcdG5hbWVzcGFjZTogbmFtZXNwYWNlcy5qb2luKFwiLlwiKVxuXHRcdFx0fSwgaGFuZGxlT2JqSW4gKTtcblxuXHRcdFx0Ly8gSW5pdCB0aGUgZXZlbnQgaGFuZGxlciBxdWV1ZSBpZiB3ZSdyZSB0aGUgZmlyc3Rcblx0XHRcdGlmICggIShoYW5kbGVycyA9IGV2ZW50c1sgdHlwZSBdKSApIHtcblx0XHRcdFx0aGFuZGxlcnMgPSBldmVudHNbIHR5cGUgXSA9IFtdO1xuXHRcdFx0XHRoYW5kbGVycy5kZWxlZ2F0ZUNvdW50ID0gMDtcblxuXHRcdFx0XHQvLyBPbmx5IHVzZSBhZGRFdmVudExpc3RlbmVyIGlmIHRoZSBzcGVjaWFsIGV2ZW50cyBoYW5kbGVyIHJldHVybnMgZmFsc2Vcblx0XHRcdFx0aWYgKCAhc3BlY2lhbC5zZXR1cCB8fCBzcGVjaWFsLnNldHVwLmNhbGwoIGVsZW0sIGRhdGEsIG5hbWVzcGFjZXMsIGV2ZW50SGFuZGxlICkgPT09IGZhbHNlICkge1xuXHRcdFx0XHRcdGlmICggZWxlbS5hZGRFdmVudExpc3RlbmVyICkge1xuXHRcdFx0XHRcdFx0ZWxlbS5hZGRFdmVudExpc3RlbmVyKCB0eXBlLCBldmVudEhhbmRsZSwgZmFsc2UgKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblxuXHRcdFx0aWYgKCBzcGVjaWFsLmFkZCApIHtcblx0XHRcdFx0c3BlY2lhbC5hZGQuY2FsbCggZWxlbSwgaGFuZGxlT2JqICk7XG5cblx0XHRcdFx0aWYgKCAhaGFuZGxlT2JqLmhhbmRsZXIuZ3VpZCApIHtcblx0XHRcdFx0XHRoYW5kbGVPYmouaGFuZGxlci5ndWlkID0gaGFuZGxlci5ndWlkO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdC8vIEFkZCB0byB0aGUgZWxlbWVudCdzIGhhbmRsZXIgbGlzdCwgZGVsZWdhdGVzIGluIGZyb250XG5cdFx0XHRpZiAoIHNlbGVjdG9yICkge1xuXHRcdFx0XHRoYW5kbGVycy5zcGxpY2UoIGhhbmRsZXJzLmRlbGVnYXRlQ291bnQrKywgMCwgaGFuZGxlT2JqICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRoYW5kbGVycy5wdXNoKCBoYW5kbGVPYmogKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gS2VlcCB0cmFjayBvZiB3aGljaCBldmVudHMgaGF2ZSBldmVyIGJlZW4gdXNlZCwgZm9yIGV2ZW50IG9wdGltaXphdGlvblxuXHRcdFx0alF1ZXJ5LmV2ZW50Lmdsb2JhbFsgdHlwZSBdID0gdHJ1ZTtcblx0XHR9XG5cblx0fSxcblxuXHQvLyBEZXRhY2ggYW4gZXZlbnQgb3Igc2V0IG9mIGV2ZW50cyBmcm9tIGFuIGVsZW1lbnRcblx0cmVtb3ZlOiBmdW5jdGlvbiggZWxlbSwgdHlwZXMsIGhhbmRsZXIsIHNlbGVjdG9yLCBtYXBwZWRUeXBlcyApIHtcblxuXHRcdHZhciBqLCBvcmlnQ291bnQsIHRtcCxcblx0XHRcdGV2ZW50cywgdCwgaGFuZGxlT2JqLFxuXHRcdFx0c3BlY2lhbCwgaGFuZGxlcnMsIHR5cGUsIG5hbWVzcGFjZXMsIG9yaWdUeXBlLFxuXHRcdFx0ZWxlbURhdGEgPSBkYXRhX3ByaXYuaGFzRGF0YSggZWxlbSApICYmIGRhdGFfcHJpdi5nZXQoIGVsZW0gKTtcblxuXHRcdGlmICggIWVsZW1EYXRhIHx8ICEoZXZlbnRzID0gZWxlbURhdGEuZXZlbnRzKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBPbmNlIGZvciBlYWNoIHR5cGUubmFtZXNwYWNlIGluIHR5cGVzOyB0eXBlIG1heSBiZSBvbWl0dGVkXG5cdFx0dHlwZXMgPSAoIHR5cGVzIHx8IFwiXCIgKS5tYXRjaCggcm5vdHdoaXRlICkgfHwgWyBcIlwiIF07XG5cdFx0dCA9IHR5cGVzLmxlbmd0aDtcblx0XHR3aGlsZSAoIHQtLSApIHtcblx0XHRcdHRtcCA9IHJ0eXBlbmFtZXNwYWNlLmV4ZWMoIHR5cGVzW3RdICkgfHwgW107XG5cdFx0XHR0eXBlID0gb3JpZ1R5cGUgPSB0bXBbMV07XG5cdFx0XHRuYW1lc3BhY2VzID0gKCB0bXBbMl0gfHwgXCJcIiApLnNwbGl0KCBcIi5cIiApLnNvcnQoKTtcblxuXHRcdFx0Ly8gVW5iaW5kIGFsbCBldmVudHMgKG9uIHRoaXMgbmFtZXNwYWNlLCBpZiBwcm92aWRlZCkgZm9yIHRoZSBlbGVtZW50XG5cdFx0XHRpZiAoICF0eXBlICkge1xuXHRcdFx0XHRmb3IgKCB0eXBlIGluIGV2ZW50cyApIHtcblx0XHRcdFx0XHRqUXVlcnkuZXZlbnQucmVtb3ZlKCBlbGVtLCB0eXBlICsgdHlwZXNbIHQgXSwgaGFuZGxlciwgc2VsZWN0b3IsIHRydWUgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdH1cblxuXHRcdFx0c3BlY2lhbCA9IGpRdWVyeS5ldmVudC5zcGVjaWFsWyB0eXBlIF0gfHwge307XG5cdFx0XHR0eXBlID0gKCBzZWxlY3RvciA/IHNwZWNpYWwuZGVsZWdhdGVUeXBlIDogc3BlY2lhbC5iaW5kVHlwZSApIHx8IHR5cGU7XG5cdFx0XHRoYW5kbGVycyA9IGV2ZW50c1sgdHlwZSBdIHx8IFtdO1xuXHRcdFx0dG1wID0gdG1wWzJdICYmIG5ldyBSZWdFeHAoIFwiKF58XFxcXC4pXCIgKyBuYW1lc3BhY2VzLmpvaW4oXCJcXFxcLig/Oi4qXFxcXC58KVwiKSArIFwiKFxcXFwufCQpXCIgKTtcblxuXHRcdFx0Ly8gUmVtb3ZlIG1hdGNoaW5nIGV2ZW50c1xuXHRcdFx0b3JpZ0NvdW50ID0gaiA9IGhhbmRsZXJzLmxlbmd0aDtcblx0XHRcdHdoaWxlICggai0tICkge1xuXHRcdFx0XHRoYW5kbGVPYmogPSBoYW5kbGVyc1sgaiBdO1xuXG5cdFx0XHRcdGlmICggKCBtYXBwZWRUeXBlcyB8fCBvcmlnVHlwZSA9PT0gaGFuZGxlT2JqLm9yaWdUeXBlICkgJiZcblx0XHRcdFx0XHQoICFoYW5kbGVyIHx8IGhhbmRsZXIuZ3VpZCA9PT0gaGFuZGxlT2JqLmd1aWQgKSAmJlxuXHRcdFx0XHRcdCggIXRtcCB8fCB0bXAudGVzdCggaGFuZGxlT2JqLm5hbWVzcGFjZSApICkgJiZcblx0XHRcdFx0XHQoICFzZWxlY3RvciB8fCBzZWxlY3RvciA9PT0gaGFuZGxlT2JqLnNlbGVjdG9yIHx8IHNlbGVjdG9yID09PSBcIioqXCIgJiYgaGFuZGxlT2JqLnNlbGVjdG9yICkgKSB7XG5cdFx0XHRcdFx0aGFuZGxlcnMuc3BsaWNlKCBqLCAxICk7XG5cblx0XHRcdFx0XHRpZiAoIGhhbmRsZU9iai5zZWxlY3RvciApIHtcblx0XHRcdFx0XHRcdGhhbmRsZXJzLmRlbGVnYXRlQ291bnQtLTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0aWYgKCBzcGVjaWFsLnJlbW92ZSApIHtcblx0XHRcdFx0XHRcdHNwZWNpYWwucmVtb3ZlLmNhbGwoIGVsZW0sIGhhbmRsZU9iaiApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBSZW1vdmUgZ2VuZXJpYyBldmVudCBoYW5kbGVyIGlmIHdlIHJlbW92ZWQgc29tZXRoaW5nIGFuZCBubyBtb3JlIGhhbmRsZXJzIGV4aXN0XG5cdFx0XHQvLyAoYXZvaWRzIHBvdGVudGlhbCBmb3IgZW5kbGVzcyByZWN1cnNpb24gZHVyaW5nIHJlbW92YWwgb2Ygc3BlY2lhbCBldmVudCBoYW5kbGVycylcblx0XHRcdGlmICggb3JpZ0NvdW50ICYmICFoYW5kbGVycy5sZW5ndGggKSB7XG5cdFx0XHRcdGlmICggIXNwZWNpYWwudGVhcmRvd24gfHwgc3BlY2lhbC50ZWFyZG93bi5jYWxsKCBlbGVtLCBuYW1lc3BhY2VzLCBlbGVtRGF0YS5oYW5kbGUgKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRcdFx0alF1ZXJ5LnJlbW92ZUV2ZW50KCBlbGVtLCB0eXBlLCBlbGVtRGF0YS5oYW5kbGUgKTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdGRlbGV0ZSBldmVudHNbIHR5cGUgXTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBSZW1vdmUgdGhlIGV4cGFuZG8gaWYgaXQncyBubyBsb25nZXIgdXNlZFxuXHRcdGlmICggalF1ZXJ5LmlzRW1wdHlPYmplY3QoIGV2ZW50cyApICkge1xuXHRcdFx0ZGVsZXRlIGVsZW1EYXRhLmhhbmRsZTtcblx0XHRcdGRhdGFfcHJpdi5yZW1vdmUoIGVsZW0sIFwiZXZlbnRzXCIgKTtcblx0XHR9XG5cdH0sXG5cblx0dHJpZ2dlcjogZnVuY3Rpb24oIGV2ZW50LCBkYXRhLCBlbGVtLCBvbmx5SGFuZGxlcnMgKSB7XG5cblx0XHR2YXIgaSwgY3VyLCB0bXAsIGJ1YmJsZVR5cGUsIG9udHlwZSwgaGFuZGxlLCBzcGVjaWFsLFxuXHRcdFx0ZXZlbnRQYXRoID0gWyBlbGVtIHx8IGRvY3VtZW50IF0sXG5cdFx0XHR0eXBlID0gaGFzT3duLmNhbGwoIGV2ZW50LCBcInR5cGVcIiApID8gZXZlbnQudHlwZSA6IGV2ZW50LFxuXHRcdFx0bmFtZXNwYWNlcyA9IGhhc093bi5jYWxsKCBldmVudCwgXCJuYW1lc3BhY2VcIiApID8gZXZlbnQubmFtZXNwYWNlLnNwbGl0KFwiLlwiKSA6IFtdO1xuXG5cdFx0Y3VyID0gdG1wID0gZWxlbSA9IGVsZW0gfHwgZG9jdW1lbnQ7XG5cblx0XHQvLyBEb24ndCBkbyBldmVudHMgb24gdGV4dCBhbmQgY29tbWVudCBub2Rlc1xuXHRcdGlmICggZWxlbS5ub2RlVHlwZSA9PT0gMyB8fCBlbGVtLm5vZGVUeXBlID09PSA4ICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIGZvY3VzL2JsdXIgbW9ycGhzIHRvIGZvY3VzaW4vb3V0OyBlbnN1cmUgd2UncmUgbm90IGZpcmluZyB0aGVtIHJpZ2h0IG5vd1xuXHRcdGlmICggcmZvY3VzTW9ycGgudGVzdCggdHlwZSArIGpRdWVyeS5ldmVudC50cmlnZ2VyZWQgKSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRpZiAoIHR5cGUuaW5kZXhPZihcIi5cIikgPj0gMCApIHtcblx0XHRcdC8vIE5hbWVzcGFjZWQgdHJpZ2dlcjsgY3JlYXRlIGEgcmVnZXhwIHRvIG1hdGNoIGV2ZW50IHR5cGUgaW4gaGFuZGxlKClcblx0XHRcdG5hbWVzcGFjZXMgPSB0eXBlLnNwbGl0KFwiLlwiKTtcblx0XHRcdHR5cGUgPSBuYW1lc3BhY2VzLnNoaWZ0KCk7XG5cdFx0XHRuYW1lc3BhY2VzLnNvcnQoKTtcblx0XHR9XG5cdFx0b250eXBlID0gdHlwZS5pbmRleE9mKFwiOlwiKSA8IDAgJiYgXCJvblwiICsgdHlwZTtcblxuXHRcdC8vIENhbGxlciBjYW4gcGFzcyBpbiBhIGpRdWVyeS5FdmVudCBvYmplY3QsIE9iamVjdCwgb3IganVzdCBhbiBldmVudCB0eXBlIHN0cmluZ1xuXHRcdGV2ZW50ID0gZXZlbnRbIGpRdWVyeS5leHBhbmRvIF0gP1xuXHRcdFx0ZXZlbnQgOlxuXHRcdFx0bmV3IGpRdWVyeS5FdmVudCggdHlwZSwgdHlwZW9mIGV2ZW50ID09PSBcIm9iamVjdFwiICYmIGV2ZW50ICk7XG5cblx0XHQvLyBUcmlnZ2VyIGJpdG1hc2s6ICYgMSBmb3IgbmF0aXZlIGhhbmRsZXJzOyAmIDIgZm9yIGpRdWVyeSAoYWx3YXlzIHRydWUpXG5cdFx0ZXZlbnQuaXNUcmlnZ2VyID0gb25seUhhbmRsZXJzID8gMiA6IDM7XG5cdFx0ZXZlbnQubmFtZXNwYWNlID0gbmFtZXNwYWNlcy5qb2luKFwiLlwiKTtcblx0XHRldmVudC5uYW1lc3BhY2VfcmUgPSBldmVudC5uYW1lc3BhY2UgP1xuXHRcdFx0bmV3IFJlZ0V4cCggXCIoXnxcXFxcLilcIiArIG5hbWVzcGFjZXMuam9pbihcIlxcXFwuKD86LipcXFxcLnwpXCIpICsgXCIoXFxcXC58JClcIiApIDpcblx0XHRcdG51bGw7XG5cblx0XHQvLyBDbGVhbiB1cCB0aGUgZXZlbnQgaW4gY2FzZSBpdCBpcyBiZWluZyByZXVzZWRcblx0XHRldmVudC5yZXN1bHQgPSB1bmRlZmluZWQ7XG5cdFx0aWYgKCAhZXZlbnQudGFyZ2V0ICkge1xuXHRcdFx0ZXZlbnQudGFyZ2V0ID0gZWxlbTtcblx0XHR9XG5cblx0XHQvLyBDbG9uZSBhbnkgaW5jb21pbmcgZGF0YSBhbmQgcHJlcGVuZCB0aGUgZXZlbnQsIGNyZWF0aW5nIHRoZSBoYW5kbGVyIGFyZyBsaXN0XG5cdFx0ZGF0YSA9IGRhdGEgPT0gbnVsbCA/XG5cdFx0XHRbIGV2ZW50IF0gOlxuXHRcdFx0alF1ZXJ5Lm1ha2VBcnJheSggZGF0YSwgWyBldmVudCBdICk7XG5cblx0XHQvLyBBbGxvdyBzcGVjaWFsIGV2ZW50cyB0byBkcmF3IG91dHNpZGUgdGhlIGxpbmVzXG5cdFx0c3BlY2lhbCA9IGpRdWVyeS5ldmVudC5zcGVjaWFsWyB0eXBlIF0gfHwge307XG5cdFx0aWYgKCAhb25seUhhbmRsZXJzICYmIHNwZWNpYWwudHJpZ2dlciAmJiBzcGVjaWFsLnRyaWdnZXIuYXBwbHkoIGVsZW0sIGRhdGEgKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRGV0ZXJtaW5lIGV2ZW50IHByb3BhZ2F0aW9uIHBhdGggaW4gYWR2YW5jZSwgcGVyIFczQyBldmVudHMgc3BlYyAoIzk5NTEpXG5cdFx0Ly8gQnViYmxlIHVwIHRvIGRvY3VtZW50LCB0aGVuIHRvIHdpbmRvdzsgd2F0Y2ggZm9yIGEgZ2xvYmFsIG93bmVyRG9jdW1lbnQgdmFyICgjOTcyNClcblx0XHRpZiAoICFvbmx5SGFuZGxlcnMgJiYgIXNwZWNpYWwubm9CdWJibGUgJiYgIWpRdWVyeS5pc1dpbmRvdyggZWxlbSApICkge1xuXG5cdFx0XHRidWJibGVUeXBlID0gc3BlY2lhbC5kZWxlZ2F0ZVR5cGUgfHwgdHlwZTtcblx0XHRcdGlmICggIXJmb2N1c01vcnBoLnRlc3QoIGJ1YmJsZVR5cGUgKyB0eXBlICkgKSB7XG5cdFx0XHRcdGN1ciA9IGN1ci5wYXJlbnROb2RlO1xuXHRcdFx0fVxuXHRcdFx0Zm9yICggOyBjdXI7IGN1ciA9IGN1ci5wYXJlbnROb2RlICkge1xuXHRcdFx0XHRldmVudFBhdGgucHVzaCggY3VyICk7XG5cdFx0XHRcdHRtcCA9IGN1cjtcblx0XHRcdH1cblxuXHRcdFx0Ly8gT25seSBhZGQgd2luZG93IGlmIHdlIGdvdCB0byBkb2N1bWVudCAoZS5nLiwgbm90IHBsYWluIG9iaiBvciBkZXRhY2hlZCBET00pXG5cdFx0XHRpZiAoIHRtcCA9PT0gKGVsZW0ub3duZXJEb2N1bWVudCB8fCBkb2N1bWVudCkgKSB7XG5cdFx0XHRcdGV2ZW50UGF0aC5wdXNoKCB0bXAuZGVmYXVsdFZpZXcgfHwgdG1wLnBhcmVudFdpbmRvdyB8fCB3aW5kb3cgKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBGaXJlIGhhbmRsZXJzIG9uIHRoZSBldmVudCBwYXRoXG5cdFx0aSA9IDA7XG5cdFx0d2hpbGUgKCAoY3VyID0gZXZlbnRQYXRoW2krK10pICYmICFldmVudC5pc1Byb3BhZ2F0aW9uU3RvcHBlZCgpICkge1xuXG5cdFx0XHRldmVudC50eXBlID0gaSA+IDEgP1xuXHRcdFx0XHRidWJibGVUeXBlIDpcblx0XHRcdFx0c3BlY2lhbC5iaW5kVHlwZSB8fCB0eXBlO1xuXG5cdFx0XHQvLyBqUXVlcnkgaGFuZGxlclxuXHRcdFx0aGFuZGxlID0gKCBkYXRhX3ByaXYuZ2V0KCBjdXIsIFwiZXZlbnRzXCIgKSB8fCB7fSApWyBldmVudC50eXBlIF0gJiYgZGF0YV9wcml2LmdldCggY3VyLCBcImhhbmRsZVwiICk7XG5cdFx0XHRpZiAoIGhhbmRsZSApIHtcblx0XHRcdFx0aGFuZGxlLmFwcGx5KCBjdXIsIGRhdGEgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gTmF0aXZlIGhhbmRsZXJcblx0XHRcdGhhbmRsZSA9IG9udHlwZSAmJiBjdXJbIG9udHlwZSBdO1xuXHRcdFx0aWYgKCBoYW5kbGUgJiYgaGFuZGxlLmFwcGx5ICYmIGpRdWVyeS5hY2NlcHREYXRhKCBjdXIgKSApIHtcblx0XHRcdFx0ZXZlbnQucmVzdWx0ID0gaGFuZGxlLmFwcGx5KCBjdXIsIGRhdGEgKTtcblx0XHRcdFx0aWYgKCBldmVudC5yZXN1bHQgPT09IGZhbHNlICkge1xuXHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0ZXZlbnQudHlwZSA9IHR5cGU7XG5cblx0XHQvLyBJZiBub2JvZHkgcHJldmVudGVkIHRoZSBkZWZhdWx0IGFjdGlvbiwgZG8gaXQgbm93XG5cdFx0aWYgKCAhb25seUhhbmRsZXJzICYmICFldmVudC5pc0RlZmF1bHRQcmV2ZW50ZWQoKSApIHtcblxuXHRcdFx0aWYgKCAoIXNwZWNpYWwuX2RlZmF1bHQgfHwgc3BlY2lhbC5fZGVmYXVsdC5hcHBseSggZXZlbnRQYXRoLnBvcCgpLCBkYXRhICkgPT09IGZhbHNlKSAmJlxuXHRcdFx0XHRqUXVlcnkuYWNjZXB0RGF0YSggZWxlbSApICkge1xuXG5cdFx0XHRcdC8vIENhbGwgYSBuYXRpdmUgRE9NIG1ldGhvZCBvbiB0aGUgdGFyZ2V0IHdpdGggdGhlIHNhbWUgbmFtZSBuYW1lIGFzIHRoZSBldmVudC5cblx0XHRcdFx0Ly8gRG9uJ3QgZG8gZGVmYXVsdCBhY3Rpb25zIG9uIHdpbmRvdywgdGhhdCdzIHdoZXJlIGdsb2JhbCB2YXJpYWJsZXMgYmUgKCM2MTcwKVxuXHRcdFx0XHRpZiAoIG9udHlwZSAmJiBqUXVlcnkuaXNGdW5jdGlvbiggZWxlbVsgdHlwZSBdICkgJiYgIWpRdWVyeS5pc1dpbmRvdyggZWxlbSApICkge1xuXG5cdFx0XHRcdFx0Ly8gRG9uJ3QgcmUtdHJpZ2dlciBhbiBvbkZPTyBldmVudCB3aGVuIHdlIGNhbGwgaXRzIEZPTygpIG1ldGhvZFxuXHRcdFx0XHRcdHRtcCA9IGVsZW1bIG9udHlwZSBdO1xuXG5cdFx0XHRcdFx0aWYgKCB0bXAgKSB7XG5cdFx0XHRcdFx0XHRlbGVtWyBvbnR5cGUgXSA9IG51bGw7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gUHJldmVudCByZS10cmlnZ2VyaW5nIG9mIHRoZSBzYW1lIGV2ZW50LCBzaW5jZSB3ZSBhbHJlYWR5IGJ1YmJsZWQgaXQgYWJvdmVcblx0XHRcdFx0XHRqUXVlcnkuZXZlbnQudHJpZ2dlcmVkID0gdHlwZTtcblx0XHRcdFx0XHRlbGVtWyB0eXBlIF0oKTtcblx0XHRcdFx0XHRqUXVlcnkuZXZlbnQudHJpZ2dlcmVkID0gdW5kZWZpbmVkO1xuXG5cdFx0XHRcdFx0aWYgKCB0bXAgKSB7XG5cdFx0XHRcdFx0XHRlbGVtWyBvbnR5cGUgXSA9IHRtcDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gZXZlbnQucmVzdWx0O1xuXHR9LFxuXG5cdGRpc3BhdGNoOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cblx0XHQvLyBNYWtlIGEgd3JpdGFibGUgalF1ZXJ5LkV2ZW50IGZyb20gdGhlIG5hdGl2ZSBldmVudCBvYmplY3Rcblx0XHRldmVudCA9IGpRdWVyeS5ldmVudC5maXgoIGV2ZW50ICk7XG5cblx0XHR2YXIgaSwgaiwgcmV0LCBtYXRjaGVkLCBoYW5kbGVPYmosXG5cdFx0XHRoYW5kbGVyUXVldWUgPSBbXSxcblx0XHRcdGFyZ3MgPSBzbGljZS5jYWxsKCBhcmd1bWVudHMgKSxcblx0XHRcdGhhbmRsZXJzID0gKCBkYXRhX3ByaXYuZ2V0KCB0aGlzLCBcImV2ZW50c1wiICkgfHwge30gKVsgZXZlbnQudHlwZSBdIHx8IFtdLFxuXHRcdFx0c3BlY2lhbCA9IGpRdWVyeS5ldmVudC5zcGVjaWFsWyBldmVudC50eXBlIF0gfHwge307XG5cblx0XHQvLyBVc2UgdGhlIGZpeC1lZCBqUXVlcnkuRXZlbnQgcmF0aGVyIHRoYW4gdGhlIChyZWFkLW9ubHkpIG5hdGl2ZSBldmVudFxuXHRcdGFyZ3NbMF0gPSBldmVudDtcblx0XHRldmVudC5kZWxlZ2F0ZVRhcmdldCA9IHRoaXM7XG5cblx0XHQvLyBDYWxsIHRoZSBwcmVEaXNwYXRjaCBob29rIGZvciB0aGUgbWFwcGVkIHR5cGUsIGFuZCBsZXQgaXQgYmFpbCBpZiBkZXNpcmVkXG5cdFx0aWYgKCBzcGVjaWFsLnByZURpc3BhdGNoICYmIHNwZWNpYWwucHJlRGlzcGF0Y2guY2FsbCggdGhpcywgZXZlbnQgKSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRGV0ZXJtaW5lIGhhbmRsZXJzXG5cdFx0aGFuZGxlclF1ZXVlID0galF1ZXJ5LmV2ZW50LmhhbmRsZXJzLmNhbGwoIHRoaXMsIGV2ZW50LCBoYW5kbGVycyApO1xuXG5cdFx0Ly8gUnVuIGRlbGVnYXRlcyBmaXJzdDsgdGhleSBtYXkgd2FudCB0byBzdG9wIHByb3BhZ2F0aW9uIGJlbmVhdGggdXNcblx0XHRpID0gMDtcblx0XHR3aGlsZSAoIChtYXRjaGVkID0gaGFuZGxlclF1ZXVlWyBpKysgXSkgJiYgIWV2ZW50LmlzUHJvcGFnYXRpb25TdG9wcGVkKCkgKSB7XG5cdFx0XHRldmVudC5jdXJyZW50VGFyZ2V0ID0gbWF0Y2hlZC5lbGVtO1xuXG5cdFx0XHRqID0gMDtcblx0XHRcdHdoaWxlICggKGhhbmRsZU9iaiA9IG1hdGNoZWQuaGFuZGxlcnNbIGorKyBdKSAmJiAhZXZlbnQuaXNJbW1lZGlhdGVQcm9wYWdhdGlvblN0b3BwZWQoKSApIHtcblxuXHRcdFx0XHQvLyBUcmlnZ2VyZWQgZXZlbnQgbXVzdCBlaXRoZXIgMSkgaGF2ZSBubyBuYW1lc3BhY2UsIG9yIDIpIGhhdmUgbmFtZXNwYWNlKHMpXG5cdFx0XHRcdC8vIGEgc3Vic2V0IG9yIGVxdWFsIHRvIHRob3NlIGluIHRoZSBib3VuZCBldmVudCAoYm90aCBjYW4gaGF2ZSBubyBuYW1lc3BhY2UpLlxuXHRcdFx0XHRpZiAoICFldmVudC5uYW1lc3BhY2VfcmUgfHwgZXZlbnQubmFtZXNwYWNlX3JlLnRlc3QoIGhhbmRsZU9iai5uYW1lc3BhY2UgKSApIHtcblxuXHRcdFx0XHRcdGV2ZW50LmhhbmRsZU9iaiA9IGhhbmRsZU9iajtcblx0XHRcdFx0XHRldmVudC5kYXRhID0gaGFuZGxlT2JqLmRhdGE7XG5cblx0XHRcdFx0XHRyZXQgPSAoIChqUXVlcnkuZXZlbnQuc3BlY2lhbFsgaGFuZGxlT2JqLm9yaWdUeXBlIF0gfHwge30pLmhhbmRsZSB8fCBoYW5kbGVPYmouaGFuZGxlciApXG5cdFx0XHRcdFx0XHRcdC5hcHBseSggbWF0Y2hlZC5lbGVtLCBhcmdzICk7XG5cblx0XHRcdFx0XHRpZiAoIHJldCAhPT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHRcdFx0aWYgKCAoZXZlbnQucmVzdWx0ID0gcmV0KSA9PT0gZmFsc2UgKSB7XG5cdFx0XHRcdFx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0XHRcdFx0XHRcdGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIENhbGwgdGhlIHBvc3REaXNwYXRjaCBob29rIGZvciB0aGUgbWFwcGVkIHR5cGVcblx0XHRpZiAoIHNwZWNpYWwucG9zdERpc3BhdGNoICkge1xuXHRcdFx0c3BlY2lhbC5wb3N0RGlzcGF0Y2guY2FsbCggdGhpcywgZXZlbnQgKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZXZlbnQucmVzdWx0O1xuXHR9LFxuXG5cdGhhbmRsZXJzOiBmdW5jdGlvbiggZXZlbnQsIGhhbmRsZXJzICkge1xuXHRcdHZhciBpLCBtYXRjaGVzLCBzZWwsIGhhbmRsZU9iaixcblx0XHRcdGhhbmRsZXJRdWV1ZSA9IFtdLFxuXHRcdFx0ZGVsZWdhdGVDb3VudCA9IGhhbmRsZXJzLmRlbGVnYXRlQ291bnQsXG5cdFx0XHRjdXIgPSBldmVudC50YXJnZXQ7XG5cblx0XHQvLyBGaW5kIGRlbGVnYXRlIGhhbmRsZXJzXG5cdFx0Ly8gQmxhY2staG9sZSBTVkcgPHVzZT4gaW5zdGFuY2UgdHJlZXMgKCMxMzE4MClcblx0XHQvLyBBdm9pZCBub24tbGVmdC1jbGljayBidWJibGluZyBpbiBGaXJlZm94ICgjMzg2MSlcblx0XHRpZiAoIGRlbGVnYXRlQ291bnQgJiYgY3VyLm5vZGVUeXBlICYmICghZXZlbnQuYnV0dG9uIHx8IGV2ZW50LnR5cGUgIT09IFwiY2xpY2tcIikgKSB7XG5cblx0XHRcdGZvciAoIDsgY3VyICE9PSB0aGlzOyBjdXIgPSBjdXIucGFyZW50Tm9kZSB8fCB0aGlzICkge1xuXG5cdFx0XHRcdC8vIERvbid0IHByb2Nlc3MgY2xpY2tzIG9uIGRpc2FibGVkIGVsZW1lbnRzICgjNjkxMSwgIzgxNjUsICMxMTM4MiwgIzExNzY0KVxuXHRcdFx0XHRpZiAoIGN1ci5kaXNhYmxlZCAhPT0gdHJ1ZSB8fCBldmVudC50eXBlICE9PSBcImNsaWNrXCIgKSB7XG5cdFx0XHRcdFx0bWF0Y2hlcyA9IFtdO1xuXHRcdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgZGVsZWdhdGVDb3VudDsgaSsrICkge1xuXHRcdFx0XHRcdFx0aGFuZGxlT2JqID0gaGFuZGxlcnNbIGkgXTtcblxuXHRcdFx0XHRcdFx0Ly8gRG9uJ3QgY29uZmxpY3Qgd2l0aCBPYmplY3QucHJvdG90eXBlIHByb3BlcnRpZXMgKCMxMzIwMylcblx0XHRcdFx0XHRcdHNlbCA9IGhhbmRsZU9iai5zZWxlY3RvciArIFwiIFwiO1xuXG5cdFx0XHRcdFx0XHRpZiAoIG1hdGNoZXNbIHNlbCBdID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdFx0XHRcdG1hdGNoZXNbIHNlbCBdID0gaGFuZGxlT2JqLm5lZWRzQ29udGV4dCA/XG5cdFx0XHRcdFx0XHRcdFx0alF1ZXJ5KCBzZWwsIHRoaXMgKS5pbmRleCggY3VyICkgPj0gMCA6XG5cdFx0XHRcdFx0XHRcdFx0alF1ZXJ5LmZpbmQoIHNlbCwgdGhpcywgbnVsbCwgWyBjdXIgXSApLmxlbmd0aDtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGlmICggbWF0Y2hlc1sgc2VsIF0gKSB7XG5cdFx0XHRcdFx0XHRcdG1hdGNoZXMucHVzaCggaGFuZGxlT2JqICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGlmICggbWF0Y2hlcy5sZW5ndGggKSB7XG5cdFx0XHRcdFx0XHRoYW5kbGVyUXVldWUucHVzaCh7IGVsZW06IGN1ciwgaGFuZGxlcnM6IG1hdGNoZXMgfSk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gQWRkIHRoZSByZW1haW5pbmcgKGRpcmVjdGx5LWJvdW5kKSBoYW5kbGVyc1xuXHRcdGlmICggZGVsZWdhdGVDb3VudCA8IGhhbmRsZXJzLmxlbmd0aCApIHtcblx0XHRcdGhhbmRsZXJRdWV1ZS5wdXNoKHsgZWxlbTogdGhpcywgaGFuZGxlcnM6IGhhbmRsZXJzLnNsaWNlKCBkZWxlZ2F0ZUNvdW50ICkgfSk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGhhbmRsZXJRdWV1ZTtcblx0fSxcblxuXHQvLyBJbmNsdWRlcyBzb21lIGV2ZW50IHByb3BzIHNoYXJlZCBieSBLZXlFdmVudCBhbmQgTW91c2VFdmVudFxuXHRwcm9wczogXCJhbHRLZXkgYnViYmxlcyBjYW5jZWxhYmxlIGN0cmxLZXkgY3VycmVudFRhcmdldCBldmVudFBoYXNlIG1ldGFLZXkgcmVsYXRlZFRhcmdldCBzaGlmdEtleSB0YXJnZXQgdGltZVN0YW1wIHZpZXcgd2hpY2hcIi5zcGxpdChcIiBcIiksXG5cblx0Zml4SG9va3M6IHt9LFxuXG5cdGtleUhvb2tzOiB7XG5cdFx0cHJvcHM6IFwiY2hhciBjaGFyQ29kZSBrZXkga2V5Q29kZVwiLnNwbGl0KFwiIFwiKSxcblx0XHRmaWx0ZXI6IGZ1bmN0aW9uKCBldmVudCwgb3JpZ2luYWwgKSB7XG5cblx0XHRcdC8vIEFkZCB3aGljaCBmb3Iga2V5IGV2ZW50c1xuXHRcdFx0aWYgKCBldmVudC53aGljaCA9PSBudWxsICkge1xuXHRcdFx0XHRldmVudC53aGljaCA9IG9yaWdpbmFsLmNoYXJDb2RlICE9IG51bGwgPyBvcmlnaW5hbC5jaGFyQ29kZSA6IG9yaWdpbmFsLmtleUNvZGU7XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiBldmVudDtcblx0XHR9XG5cdH0sXG5cblx0bW91c2VIb29rczoge1xuXHRcdHByb3BzOiBcImJ1dHRvbiBidXR0b25zIGNsaWVudFggY2xpZW50WSBvZmZzZXRYIG9mZnNldFkgcGFnZVggcGFnZVkgc2NyZWVuWCBzY3JlZW5ZIHRvRWxlbWVudFwiLnNwbGl0KFwiIFwiKSxcblx0XHRmaWx0ZXI6IGZ1bmN0aW9uKCBldmVudCwgb3JpZ2luYWwgKSB7XG5cdFx0XHR2YXIgZXZlbnREb2MsIGRvYywgYm9keSxcblx0XHRcdFx0YnV0dG9uID0gb3JpZ2luYWwuYnV0dG9uO1xuXG5cdFx0XHQvLyBDYWxjdWxhdGUgcGFnZVgvWSBpZiBtaXNzaW5nIGFuZCBjbGllbnRYL1kgYXZhaWxhYmxlXG5cdFx0XHRpZiAoIGV2ZW50LnBhZ2VYID09IG51bGwgJiYgb3JpZ2luYWwuY2xpZW50WCAhPSBudWxsICkge1xuXHRcdFx0XHRldmVudERvYyA9IGV2ZW50LnRhcmdldC5vd25lckRvY3VtZW50IHx8IGRvY3VtZW50O1xuXHRcdFx0XHRkb2MgPSBldmVudERvYy5kb2N1bWVudEVsZW1lbnQ7XG5cdFx0XHRcdGJvZHkgPSBldmVudERvYy5ib2R5O1xuXG5cdFx0XHRcdGV2ZW50LnBhZ2VYID0gb3JpZ2luYWwuY2xpZW50WCArICggZG9jICYmIGRvYy5zY3JvbGxMZWZ0IHx8IGJvZHkgJiYgYm9keS5zY3JvbGxMZWZ0IHx8IDAgKSAtICggZG9jICYmIGRvYy5jbGllbnRMZWZ0IHx8IGJvZHkgJiYgYm9keS5jbGllbnRMZWZ0IHx8IDAgKTtcblx0XHRcdFx0ZXZlbnQucGFnZVkgPSBvcmlnaW5hbC5jbGllbnRZICsgKCBkb2MgJiYgZG9jLnNjcm9sbFRvcCAgfHwgYm9keSAmJiBib2R5LnNjcm9sbFRvcCAgfHwgMCApIC0gKCBkb2MgJiYgZG9jLmNsaWVudFRvcCAgfHwgYm9keSAmJiBib2R5LmNsaWVudFRvcCAgfHwgMCApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBBZGQgd2hpY2ggZm9yIGNsaWNrOiAxID09PSBsZWZ0OyAyID09PSBtaWRkbGU7IDMgPT09IHJpZ2h0XG5cdFx0XHQvLyBOb3RlOiBidXR0b24gaXMgbm90IG5vcm1hbGl6ZWQsIHNvIGRvbid0IHVzZSBpdFxuXHRcdFx0aWYgKCAhZXZlbnQud2hpY2ggJiYgYnV0dG9uICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdGV2ZW50LndoaWNoID0gKCBidXR0b24gJiAxID8gMSA6ICggYnV0dG9uICYgMiA/IDMgOiAoIGJ1dHRvbiAmIDQgPyAyIDogMCApICkgKTtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIGV2ZW50O1xuXHRcdH1cblx0fSxcblxuXHRmaXg6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRpZiAoIGV2ZW50WyBqUXVlcnkuZXhwYW5kbyBdICkge1xuXHRcdFx0cmV0dXJuIGV2ZW50O1xuXHRcdH1cblxuXHRcdC8vIENyZWF0ZSBhIHdyaXRhYmxlIGNvcHkgb2YgdGhlIGV2ZW50IG9iamVjdCBhbmQgbm9ybWFsaXplIHNvbWUgcHJvcGVydGllc1xuXHRcdHZhciBpLCBwcm9wLCBjb3B5LFxuXHRcdFx0dHlwZSA9IGV2ZW50LnR5cGUsXG5cdFx0XHRvcmlnaW5hbEV2ZW50ID0gZXZlbnQsXG5cdFx0XHRmaXhIb29rID0gdGhpcy5maXhIb29rc1sgdHlwZSBdO1xuXG5cdFx0aWYgKCAhZml4SG9vayApIHtcblx0XHRcdHRoaXMuZml4SG9va3NbIHR5cGUgXSA9IGZpeEhvb2sgPVxuXHRcdFx0XHRybW91c2VFdmVudC50ZXN0KCB0eXBlICkgPyB0aGlzLm1vdXNlSG9va3MgOlxuXHRcdFx0XHRya2V5RXZlbnQudGVzdCggdHlwZSApID8gdGhpcy5rZXlIb29rcyA6XG5cdFx0XHRcdHt9O1xuXHRcdH1cblx0XHRjb3B5ID0gZml4SG9vay5wcm9wcyA/IHRoaXMucHJvcHMuY29uY2F0KCBmaXhIb29rLnByb3BzICkgOiB0aGlzLnByb3BzO1xuXG5cdFx0ZXZlbnQgPSBuZXcgalF1ZXJ5LkV2ZW50KCBvcmlnaW5hbEV2ZW50ICk7XG5cblx0XHRpID0gY29weS5sZW5ndGg7XG5cdFx0d2hpbGUgKCBpLS0gKSB7XG5cdFx0XHRwcm9wID0gY29weVsgaSBdO1xuXHRcdFx0ZXZlbnRbIHByb3AgXSA9IG9yaWdpbmFsRXZlbnRbIHByb3AgXTtcblx0XHR9XG5cblx0XHQvLyBTdXBwb3J0OiBDb3Jkb3ZhIDIuNSAoV2ViS2l0KSAoIzEzMjU1KVxuXHRcdC8vIEFsbCBldmVudHMgc2hvdWxkIGhhdmUgYSB0YXJnZXQ7IENvcmRvdmEgZGV2aWNlcmVhZHkgZG9lc24ndFxuXHRcdGlmICggIWV2ZW50LnRhcmdldCApIHtcblx0XHRcdGV2ZW50LnRhcmdldCA9IGRvY3VtZW50O1xuXHRcdH1cblxuXHRcdC8vIFN1cHBvcnQ6IFNhZmFyaSA2LjArLCBDaHJvbWU8Mjhcblx0XHQvLyBUYXJnZXQgc2hvdWxkIG5vdCBiZSBhIHRleHQgbm9kZSAoIzUwNCwgIzEzMTQzKVxuXHRcdGlmICggZXZlbnQudGFyZ2V0Lm5vZGVUeXBlID09PSAzICkge1xuXHRcdFx0ZXZlbnQudGFyZ2V0ID0gZXZlbnQudGFyZ2V0LnBhcmVudE5vZGU7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGZpeEhvb2suZmlsdGVyID8gZml4SG9vay5maWx0ZXIoIGV2ZW50LCBvcmlnaW5hbEV2ZW50ICkgOiBldmVudDtcblx0fSxcblxuXHRzcGVjaWFsOiB7XG5cdFx0bG9hZDoge1xuXHRcdFx0Ly8gUHJldmVudCB0cmlnZ2VyZWQgaW1hZ2UubG9hZCBldmVudHMgZnJvbSBidWJibGluZyB0byB3aW5kb3cubG9hZFxuXHRcdFx0bm9CdWJibGU6IHRydWVcblx0XHR9LFxuXHRcdGZvY3VzOiB7XG5cdFx0XHQvLyBGaXJlIG5hdGl2ZSBldmVudCBpZiBwb3NzaWJsZSBzbyBibHVyL2ZvY3VzIHNlcXVlbmNlIGlzIGNvcnJlY3Rcblx0XHRcdHRyaWdnZXI6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRpZiAoIHRoaXMgIT09IHNhZmVBY3RpdmVFbGVtZW50KCkgJiYgdGhpcy5mb2N1cyApIHtcblx0XHRcdFx0XHR0aGlzLmZvY3VzKCk7XG5cdFx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXHRcdFx0ZGVsZWdhdGVUeXBlOiBcImZvY3VzaW5cIlxuXHRcdH0sXG5cdFx0Ymx1cjoge1xuXHRcdFx0dHJpZ2dlcjogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggdGhpcyA9PT0gc2FmZUFjdGl2ZUVsZW1lbnQoKSAmJiB0aGlzLmJsdXIgKSB7XG5cdFx0XHRcdFx0dGhpcy5ibHVyKCk7XG5cdFx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0XHR9XG5cdFx0XHR9LFxuXHRcdFx0ZGVsZWdhdGVUeXBlOiBcImZvY3Vzb3V0XCJcblx0XHR9LFxuXHRcdGNsaWNrOiB7XG5cdFx0XHQvLyBGb3IgY2hlY2tib3gsIGZpcmUgbmF0aXZlIGV2ZW50IHNvIGNoZWNrZWQgc3RhdGUgd2lsbCBiZSByaWdodFxuXHRcdFx0dHJpZ2dlcjogZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGlmICggdGhpcy50eXBlID09PSBcImNoZWNrYm94XCIgJiYgdGhpcy5jbGljayAmJiBqUXVlcnkubm9kZU5hbWUoIHRoaXMsIFwiaW5wdXRcIiApICkge1xuXHRcdFx0XHRcdHRoaXMuY2xpY2soKTtcblx0XHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHRcdH1cblx0XHRcdH0sXG5cblx0XHRcdC8vIEZvciBjcm9zcy1icm93c2VyIGNvbnNpc3RlbmN5LCBkb24ndCBmaXJlIG5hdGl2ZSAuY2xpY2soKSBvbiBsaW5rc1xuXHRcdFx0X2RlZmF1bHQ6IGZ1bmN0aW9uKCBldmVudCApIHtcblx0XHRcdFx0cmV0dXJuIGpRdWVyeS5ub2RlTmFtZSggZXZlbnQudGFyZ2V0LCBcImFcIiApO1xuXHRcdFx0fVxuXHRcdH0sXG5cblx0XHRiZWZvcmV1bmxvYWQ6IHtcblx0XHRcdHBvc3REaXNwYXRjaDogZnVuY3Rpb24oIGV2ZW50ICkge1xuXG5cdFx0XHRcdC8vIFN1cHBvcnQ6IEZpcmVmb3ggMjArXG5cdFx0XHRcdC8vIEZpcmVmb3ggZG9lc24ndCBhbGVydCBpZiB0aGUgcmV0dXJuVmFsdWUgZmllbGQgaXMgbm90IHNldC5cblx0XHRcdFx0aWYgKCBldmVudC5yZXN1bHQgIT09IHVuZGVmaW5lZCAmJiBldmVudC5vcmlnaW5hbEV2ZW50ICkge1xuXHRcdFx0XHRcdGV2ZW50Lm9yaWdpbmFsRXZlbnQucmV0dXJuVmFsdWUgPSBldmVudC5yZXN1bHQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0c2ltdWxhdGU6IGZ1bmN0aW9uKCB0eXBlLCBlbGVtLCBldmVudCwgYnViYmxlICkge1xuXHRcdC8vIFBpZ2d5YmFjayBvbiBhIGRvbm9yIGV2ZW50IHRvIHNpbXVsYXRlIGEgZGlmZmVyZW50IG9uZS5cblx0XHQvLyBGYWtlIG9yaWdpbmFsRXZlbnQgdG8gYXZvaWQgZG9ub3IncyBzdG9wUHJvcGFnYXRpb24sIGJ1dCBpZiB0aGVcblx0XHQvLyBzaW11bGF0ZWQgZXZlbnQgcHJldmVudHMgZGVmYXVsdCB0aGVuIHdlIGRvIHRoZSBzYW1lIG9uIHRoZSBkb25vci5cblx0XHR2YXIgZSA9IGpRdWVyeS5leHRlbmQoXG5cdFx0XHRuZXcgalF1ZXJ5LkV2ZW50KCksXG5cdFx0XHRldmVudCxcblx0XHRcdHtcblx0XHRcdFx0dHlwZTogdHlwZSxcblx0XHRcdFx0aXNTaW11bGF0ZWQ6IHRydWUsXG5cdFx0XHRcdG9yaWdpbmFsRXZlbnQ6IHt9XG5cdFx0XHR9XG5cdFx0KTtcblx0XHRpZiAoIGJ1YmJsZSApIHtcblx0XHRcdGpRdWVyeS5ldmVudC50cmlnZ2VyKCBlLCBudWxsLCBlbGVtICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGpRdWVyeS5ldmVudC5kaXNwYXRjaC5jYWxsKCBlbGVtLCBlICk7XG5cdFx0fVxuXHRcdGlmICggZS5pc0RlZmF1bHRQcmV2ZW50ZWQoKSApIHtcblx0XHRcdGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG5cdFx0fVxuXHR9XG59O1xuXG5qUXVlcnkucmVtb3ZlRXZlbnQgPSBmdW5jdGlvbiggZWxlbSwgdHlwZSwgaGFuZGxlICkge1xuXHRpZiAoIGVsZW0ucmVtb3ZlRXZlbnRMaXN0ZW5lciApIHtcblx0XHRlbGVtLnJlbW92ZUV2ZW50TGlzdGVuZXIoIHR5cGUsIGhhbmRsZSwgZmFsc2UgKTtcblx0fVxufTtcblxualF1ZXJ5LkV2ZW50ID0gZnVuY3Rpb24oIHNyYywgcHJvcHMgKSB7XG5cdC8vIEFsbG93IGluc3RhbnRpYXRpb24gd2l0aG91dCB0aGUgJ25ldycga2V5d29yZFxuXHRpZiAoICEodGhpcyBpbnN0YW5jZW9mIGpRdWVyeS5FdmVudCkgKSB7XG5cdFx0cmV0dXJuIG5ldyBqUXVlcnkuRXZlbnQoIHNyYywgcHJvcHMgKTtcblx0fVxuXG5cdC8vIEV2ZW50IG9iamVjdFxuXHRpZiAoIHNyYyAmJiBzcmMudHlwZSApIHtcblx0XHR0aGlzLm9yaWdpbmFsRXZlbnQgPSBzcmM7XG5cdFx0dGhpcy50eXBlID0gc3JjLnR5cGU7XG5cblx0XHQvLyBFdmVudHMgYnViYmxpbmcgdXAgdGhlIGRvY3VtZW50IG1heSBoYXZlIGJlZW4gbWFya2VkIGFzIHByZXZlbnRlZFxuXHRcdC8vIGJ5IGEgaGFuZGxlciBsb3dlciBkb3duIHRoZSB0cmVlOyByZWZsZWN0IHRoZSBjb3JyZWN0IHZhbHVlLlxuXHRcdHRoaXMuaXNEZWZhdWx0UHJldmVudGVkID0gc3JjLmRlZmF1bHRQcmV2ZW50ZWQgfHxcblx0XHRcdFx0c3JjLmRlZmF1bHRQcmV2ZW50ZWQgPT09IHVuZGVmaW5lZCAmJlxuXHRcdFx0XHQvLyBTdXBwb3J0OiBBbmRyb2lkPDQuMFxuXHRcdFx0XHRzcmMucmV0dXJuVmFsdWUgPT09IGZhbHNlID9cblx0XHRcdHJldHVyblRydWUgOlxuXHRcdFx0cmV0dXJuRmFsc2U7XG5cblx0Ly8gRXZlbnQgdHlwZVxuXHR9IGVsc2Uge1xuXHRcdHRoaXMudHlwZSA9IHNyYztcblx0fVxuXG5cdC8vIFB1dCBleHBsaWNpdGx5IHByb3ZpZGVkIHByb3BlcnRpZXMgb250byB0aGUgZXZlbnQgb2JqZWN0XG5cdGlmICggcHJvcHMgKSB7XG5cdFx0alF1ZXJ5LmV4dGVuZCggdGhpcywgcHJvcHMgKTtcblx0fVxuXG5cdC8vIENyZWF0ZSBhIHRpbWVzdGFtcCBpZiBpbmNvbWluZyBldmVudCBkb2Vzbid0IGhhdmUgb25lXG5cdHRoaXMudGltZVN0YW1wID0gc3JjICYmIHNyYy50aW1lU3RhbXAgfHwgalF1ZXJ5Lm5vdygpO1xuXG5cdC8vIE1hcmsgaXQgYXMgZml4ZWRcblx0dGhpc1sgalF1ZXJ5LmV4cGFuZG8gXSA9IHRydWU7XG59O1xuXG4vLyBqUXVlcnkuRXZlbnQgaXMgYmFzZWQgb24gRE9NMyBFdmVudHMgYXMgc3BlY2lmaWVkIGJ5IHRoZSBFQ01BU2NyaXB0IExhbmd1YWdlIEJpbmRpbmdcbi8vIGh0dHA6Ly93d3cudzMub3JnL1RSLzIwMDMvV0QtRE9NLUxldmVsLTMtRXZlbnRzLTIwMDMwMzMxL2VjbWEtc2NyaXB0LWJpbmRpbmcuaHRtbFxualF1ZXJ5LkV2ZW50LnByb3RvdHlwZSA9IHtcblx0aXNEZWZhdWx0UHJldmVudGVkOiByZXR1cm5GYWxzZSxcblx0aXNQcm9wYWdhdGlvblN0b3BwZWQ6IHJldHVybkZhbHNlLFxuXHRpc0ltbWVkaWF0ZVByb3BhZ2F0aW9uU3RvcHBlZDogcmV0dXJuRmFsc2UsXG5cblx0cHJldmVudERlZmF1bHQ6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBlID0gdGhpcy5vcmlnaW5hbEV2ZW50O1xuXG5cdFx0dGhpcy5pc0RlZmF1bHRQcmV2ZW50ZWQgPSByZXR1cm5UcnVlO1xuXG5cdFx0aWYgKCBlICYmIGUucHJldmVudERlZmF1bHQgKSB7XG5cdFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XG5cdFx0fVxuXHR9LFxuXHRzdG9wUHJvcGFnYXRpb246IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBlID0gdGhpcy5vcmlnaW5hbEV2ZW50O1xuXG5cdFx0dGhpcy5pc1Byb3BhZ2F0aW9uU3RvcHBlZCA9IHJldHVyblRydWU7XG5cblx0XHRpZiAoIGUgJiYgZS5zdG9wUHJvcGFnYXRpb24gKSB7XG5cdFx0XHRlLnN0b3BQcm9wYWdhdGlvbigpO1xuXHRcdH1cblx0fSxcblx0c3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uOiBmdW5jdGlvbigpIHtcblx0XHR2YXIgZSA9IHRoaXMub3JpZ2luYWxFdmVudDtcblxuXHRcdHRoaXMuaXNJbW1lZGlhdGVQcm9wYWdhdGlvblN0b3BwZWQgPSByZXR1cm5UcnVlO1xuXG5cdFx0aWYgKCBlICYmIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uICkge1xuXHRcdFx0ZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKTtcblx0XHR9XG5cblx0XHR0aGlzLnN0b3BQcm9wYWdhdGlvbigpO1xuXHR9XG59O1xuXG4vLyBDcmVhdGUgbW91c2VlbnRlci9sZWF2ZSBldmVudHMgdXNpbmcgbW91c2VvdmVyL291dCBhbmQgZXZlbnQtdGltZSBjaGVja3Ncbi8vIFN1cHBvcnQ6IENocm9tZSAxNStcbmpRdWVyeS5lYWNoKHtcblx0bW91c2VlbnRlcjogXCJtb3VzZW92ZXJcIixcblx0bW91c2VsZWF2ZTogXCJtb3VzZW91dFwiLFxuXHRwb2ludGVyZW50ZXI6IFwicG9pbnRlcm92ZXJcIixcblx0cG9pbnRlcmxlYXZlOiBcInBvaW50ZXJvdXRcIlxufSwgZnVuY3Rpb24oIG9yaWcsIGZpeCApIHtcblx0alF1ZXJ5LmV2ZW50LnNwZWNpYWxbIG9yaWcgXSA9IHtcblx0XHRkZWxlZ2F0ZVR5cGU6IGZpeCxcblx0XHRiaW5kVHlwZTogZml4LFxuXG5cdFx0aGFuZGxlOiBmdW5jdGlvbiggZXZlbnQgKSB7XG5cdFx0XHR2YXIgcmV0LFxuXHRcdFx0XHR0YXJnZXQgPSB0aGlzLFxuXHRcdFx0XHRyZWxhdGVkID0gZXZlbnQucmVsYXRlZFRhcmdldCxcblx0XHRcdFx0aGFuZGxlT2JqID0gZXZlbnQuaGFuZGxlT2JqO1xuXG5cdFx0XHQvLyBGb3IgbW91c2VudGVyL2xlYXZlIGNhbGwgdGhlIGhhbmRsZXIgaWYgcmVsYXRlZCBpcyBvdXRzaWRlIHRoZSB0YXJnZXQuXG5cdFx0XHQvLyBOQjogTm8gcmVsYXRlZFRhcmdldCBpZiB0aGUgbW91c2UgbGVmdC9lbnRlcmVkIHRoZSBicm93c2VyIHdpbmRvd1xuXHRcdFx0aWYgKCAhcmVsYXRlZCB8fCAocmVsYXRlZCAhPT0gdGFyZ2V0ICYmICFqUXVlcnkuY29udGFpbnMoIHRhcmdldCwgcmVsYXRlZCApKSApIHtcblx0XHRcdFx0ZXZlbnQudHlwZSA9IGhhbmRsZU9iai5vcmlnVHlwZTtcblx0XHRcdFx0cmV0ID0gaGFuZGxlT2JqLmhhbmRsZXIuYXBwbHkoIHRoaXMsIGFyZ3VtZW50cyApO1xuXHRcdFx0XHRldmVudC50eXBlID0gZml4O1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHJldDtcblx0XHR9XG5cdH07XG59KTtcblxuLy8gU3VwcG9ydDogRmlyZWZveCwgQ2hyb21lLCBTYWZhcmlcbi8vIENyZWF0ZSBcImJ1YmJsaW5nXCIgZm9jdXMgYW5kIGJsdXIgZXZlbnRzXG5pZiAoICFzdXBwb3J0LmZvY3VzaW5CdWJibGVzICkge1xuXHRqUXVlcnkuZWFjaCh7IGZvY3VzOiBcImZvY3VzaW5cIiwgYmx1cjogXCJmb2N1c291dFwiIH0sIGZ1bmN0aW9uKCBvcmlnLCBmaXggKSB7XG5cblx0XHQvLyBBdHRhY2ggYSBzaW5nbGUgY2FwdHVyaW5nIGhhbmRsZXIgb24gdGhlIGRvY3VtZW50IHdoaWxlIHNvbWVvbmUgd2FudHMgZm9jdXNpbi9mb2N1c291dFxuXHRcdHZhciBoYW5kbGVyID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHRqUXVlcnkuZXZlbnQuc2ltdWxhdGUoIGZpeCwgZXZlbnQudGFyZ2V0LCBqUXVlcnkuZXZlbnQuZml4KCBldmVudCApLCB0cnVlICk7XG5cdFx0XHR9O1xuXG5cdFx0alF1ZXJ5LmV2ZW50LnNwZWNpYWxbIGZpeCBdID0ge1xuXHRcdFx0c2V0dXA6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHR2YXIgZG9jID0gdGhpcy5vd25lckRvY3VtZW50IHx8IHRoaXMsXG5cdFx0XHRcdFx0YXR0YWNoZXMgPSBkYXRhX3ByaXYuYWNjZXNzKCBkb2MsIGZpeCApO1xuXG5cdFx0XHRcdGlmICggIWF0dGFjaGVzICkge1xuXHRcdFx0XHRcdGRvYy5hZGRFdmVudExpc3RlbmVyKCBvcmlnLCBoYW5kbGVyLCB0cnVlICk7XG5cdFx0XHRcdH1cblx0XHRcdFx0ZGF0YV9wcml2LmFjY2VzcyggZG9jLCBmaXgsICggYXR0YWNoZXMgfHwgMCApICsgMSApO1xuXHRcdFx0fSxcblx0XHRcdHRlYXJkb3duOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0dmFyIGRvYyA9IHRoaXMub3duZXJEb2N1bWVudCB8fCB0aGlzLFxuXHRcdFx0XHRcdGF0dGFjaGVzID0gZGF0YV9wcml2LmFjY2VzcyggZG9jLCBmaXggKSAtIDE7XG5cblx0XHRcdFx0aWYgKCAhYXR0YWNoZXMgKSB7XG5cdFx0XHRcdFx0ZG9jLnJlbW92ZUV2ZW50TGlzdGVuZXIoIG9yaWcsIGhhbmRsZXIsIHRydWUgKTtcblx0XHRcdFx0XHRkYXRhX3ByaXYucmVtb3ZlKCBkb2MsIGZpeCApO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0ZGF0YV9wcml2LmFjY2VzcyggZG9jLCBmaXgsIGF0dGFjaGVzICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9O1xuXHR9KTtcbn1cblxualF1ZXJ5LmZuLmV4dGVuZCh7XG5cblx0b246IGZ1bmN0aW9uKCB0eXBlcywgc2VsZWN0b3IsIGRhdGEsIGZuLCAvKklOVEVSTkFMKi8gb25lICkge1xuXHRcdHZhciBvcmlnRm4sIHR5cGU7XG5cblx0XHQvLyBUeXBlcyBjYW4gYmUgYSBtYXAgb2YgdHlwZXMvaGFuZGxlcnNcblx0XHRpZiAoIHR5cGVvZiB0eXBlcyA9PT0gXCJvYmplY3RcIiApIHtcblx0XHRcdC8vICggdHlwZXMtT2JqZWN0LCBzZWxlY3RvciwgZGF0YSApXG5cdFx0XHRpZiAoIHR5cGVvZiBzZWxlY3RvciAhPT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdFx0Ly8gKCB0eXBlcy1PYmplY3QsIGRhdGEgKVxuXHRcdFx0XHRkYXRhID0gZGF0YSB8fCBzZWxlY3Rvcjtcblx0XHRcdFx0c2VsZWN0b3IgPSB1bmRlZmluZWQ7XG5cdFx0XHR9XG5cdFx0XHRmb3IgKCB0eXBlIGluIHR5cGVzICkge1xuXHRcdFx0XHR0aGlzLm9uKCB0eXBlLCBzZWxlY3RvciwgZGF0YSwgdHlwZXNbIHR5cGUgXSwgb25lICk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cblx0XHRpZiAoIGRhdGEgPT0gbnVsbCAmJiBmbiA9PSBudWxsICkge1xuXHRcdFx0Ly8gKCB0eXBlcywgZm4gKVxuXHRcdFx0Zm4gPSBzZWxlY3Rvcjtcblx0XHRcdGRhdGEgPSBzZWxlY3RvciA9IHVuZGVmaW5lZDtcblx0XHR9IGVsc2UgaWYgKCBmbiA9PSBudWxsICkge1xuXHRcdFx0aWYgKCB0eXBlb2Ygc2VsZWN0b3IgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRcdC8vICggdHlwZXMsIHNlbGVjdG9yLCBmbiApXG5cdFx0XHRcdGZuID0gZGF0YTtcblx0XHRcdFx0ZGF0YSA9IHVuZGVmaW5lZDtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vICggdHlwZXMsIGRhdGEsIGZuIClcblx0XHRcdFx0Zm4gPSBkYXRhO1xuXHRcdFx0XHRkYXRhID0gc2VsZWN0b3I7XG5cdFx0XHRcdHNlbGVjdG9yID0gdW5kZWZpbmVkO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZiAoIGZuID09PSBmYWxzZSApIHtcblx0XHRcdGZuID0gcmV0dXJuRmFsc2U7XG5cdFx0fSBlbHNlIGlmICggIWZuICkge1xuXHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0fVxuXG5cdFx0aWYgKCBvbmUgPT09IDEgKSB7XG5cdFx0XHRvcmlnRm4gPSBmbjtcblx0XHRcdGZuID0gZnVuY3Rpb24oIGV2ZW50ICkge1xuXHRcdFx0XHQvLyBDYW4gdXNlIGFuIGVtcHR5IHNldCwgc2luY2UgZXZlbnQgY29udGFpbnMgdGhlIGluZm9cblx0XHRcdFx0alF1ZXJ5KCkub2ZmKCBldmVudCApO1xuXHRcdFx0XHRyZXR1cm4gb3JpZ0ZuLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKTtcblx0XHRcdH07XG5cdFx0XHQvLyBVc2Ugc2FtZSBndWlkIHNvIGNhbGxlciBjYW4gcmVtb3ZlIHVzaW5nIG9yaWdGblxuXHRcdFx0Zm4uZ3VpZCA9IG9yaWdGbi5ndWlkIHx8ICggb3JpZ0ZuLmd1aWQgPSBqUXVlcnkuZ3VpZCsrICk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLmVhY2goIGZ1bmN0aW9uKCkge1xuXHRcdFx0alF1ZXJ5LmV2ZW50LmFkZCggdGhpcywgdHlwZXMsIGZuLCBkYXRhLCBzZWxlY3RvciApO1xuXHRcdH0pO1xuXHR9LFxuXHRvbmU6IGZ1bmN0aW9uKCB0eXBlcywgc2VsZWN0b3IsIGRhdGEsIGZuICkge1xuXHRcdHJldHVybiB0aGlzLm9uKCB0eXBlcywgc2VsZWN0b3IsIGRhdGEsIGZuLCAxICk7XG5cdH0sXG5cdG9mZjogZnVuY3Rpb24oIHR5cGVzLCBzZWxlY3RvciwgZm4gKSB7XG5cdFx0dmFyIGhhbmRsZU9iaiwgdHlwZTtcblx0XHRpZiAoIHR5cGVzICYmIHR5cGVzLnByZXZlbnREZWZhdWx0ICYmIHR5cGVzLmhhbmRsZU9iaiApIHtcblx0XHRcdC8vICggZXZlbnQgKSAgZGlzcGF0Y2hlZCBqUXVlcnkuRXZlbnRcblx0XHRcdGhhbmRsZU9iaiA9IHR5cGVzLmhhbmRsZU9iajtcblx0XHRcdGpRdWVyeSggdHlwZXMuZGVsZWdhdGVUYXJnZXQgKS5vZmYoXG5cdFx0XHRcdGhhbmRsZU9iai5uYW1lc3BhY2UgPyBoYW5kbGVPYmoub3JpZ1R5cGUgKyBcIi5cIiArIGhhbmRsZU9iai5uYW1lc3BhY2UgOiBoYW5kbGVPYmoub3JpZ1R5cGUsXG5cdFx0XHRcdGhhbmRsZU9iai5zZWxlY3Rvcixcblx0XHRcdFx0aGFuZGxlT2JqLmhhbmRsZXJcblx0XHRcdCk7XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cdFx0aWYgKCB0eXBlb2YgdHlwZXMgPT09IFwib2JqZWN0XCIgKSB7XG5cdFx0XHQvLyAoIHR5cGVzLW9iamVjdCBbLCBzZWxlY3Rvcl0gKVxuXHRcdFx0Zm9yICggdHlwZSBpbiB0eXBlcyApIHtcblx0XHRcdFx0dGhpcy5vZmYoIHR5cGUsIHNlbGVjdG9yLCB0eXBlc1sgdHlwZSBdICk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gdGhpcztcblx0XHR9XG5cdFx0aWYgKCBzZWxlY3RvciA9PT0gZmFsc2UgfHwgdHlwZW9mIHNlbGVjdG9yID09PSBcImZ1bmN0aW9uXCIgKSB7XG5cdFx0XHQvLyAoIHR5cGVzIFssIGZuXSApXG5cdFx0XHRmbiA9IHNlbGVjdG9yO1xuXHRcdFx0c2VsZWN0b3IgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHRcdGlmICggZm4gPT09IGZhbHNlICkge1xuXHRcdFx0Zm4gPSByZXR1cm5GYWxzZTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcblx0XHRcdGpRdWVyeS5ldmVudC5yZW1vdmUoIHRoaXMsIHR5cGVzLCBmbiwgc2VsZWN0b3IgKTtcblx0XHR9KTtcblx0fSxcblxuXHR0cmlnZ2VyOiBmdW5jdGlvbiggdHlwZSwgZGF0YSApIHtcblx0XHRyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuXHRcdFx0alF1ZXJ5LmV2ZW50LnRyaWdnZXIoIHR5cGUsIGRhdGEsIHRoaXMgKTtcblx0XHR9KTtcblx0fSxcblx0dHJpZ2dlckhhbmRsZXI6IGZ1bmN0aW9uKCB0eXBlLCBkYXRhICkge1xuXHRcdHZhciBlbGVtID0gdGhpc1swXTtcblx0XHRpZiAoIGVsZW0gKSB7XG5cdFx0XHRyZXR1cm4galF1ZXJ5LmV2ZW50LnRyaWdnZXIoIHR5cGUsIGRhdGEsIGVsZW0sIHRydWUgKTtcblx0XHR9XG5cdH1cbn0pO1xuXG5cbnZhclxuXHRyeGh0bWxUYWcgPSAvPCg/IWFyZWF8YnJ8Y29sfGVtYmVkfGhyfGltZ3xpbnB1dHxsaW5rfG1ldGF8cGFyYW0pKChbXFx3Ol0rKVtePl0qKVxcLz4vZ2ksXG5cdHJ0YWdOYW1lID0gLzwoW1xcdzpdKykvLFxuXHRyaHRtbCA9IC88fCYjP1xcdys7Lyxcblx0cm5vSW5uZXJodG1sID0gLzwoPzpzY3JpcHR8c3R5bGV8bGluaykvaSxcblx0Ly8gY2hlY2tlZD1cImNoZWNrZWRcIiBvciBjaGVja2VkXG5cdHJjaGVja2VkID0gL2NoZWNrZWRcXHMqKD86W149XXw9XFxzKi5jaGVja2VkLikvaSxcblx0cnNjcmlwdFR5cGUgPSAvXiR8XFwvKD86amF2YXxlY21hKXNjcmlwdC9pLFxuXHRyc2NyaXB0VHlwZU1hc2tlZCA9IC9edHJ1ZVxcLyguKikvLFxuXHRyY2xlYW5TY3JpcHQgPSAvXlxccyo8ISg/OlxcW0NEQVRBXFxbfC0tKXwoPzpcXF1cXF18LS0pPlxccyokL2csXG5cblx0Ly8gV2UgaGF2ZSB0byBjbG9zZSB0aGVzZSB0YWdzIHRvIHN1cHBvcnQgWEhUTUwgKCMxMzIwMClcblx0d3JhcE1hcCA9IHtcblxuXHRcdC8vIFN1cHBvcnQ6IElFOVxuXHRcdG9wdGlvbjogWyAxLCBcIjxzZWxlY3QgbXVsdGlwbGU9J211bHRpcGxlJz5cIiwgXCI8L3NlbGVjdD5cIiBdLFxuXG5cdFx0dGhlYWQ6IFsgMSwgXCI8dGFibGU+XCIsIFwiPC90YWJsZT5cIiBdLFxuXHRcdGNvbDogWyAyLCBcIjx0YWJsZT48Y29sZ3JvdXA+XCIsIFwiPC9jb2xncm91cD48L3RhYmxlPlwiIF0sXG5cdFx0dHI6IFsgMiwgXCI8dGFibGU+PHRib2R5PlwiLCBcIjwvdGJvZHk+PC90YWJsZT5cIiBdLFxuXHRcdHRkOiBbIDMsIFwiPHRhYmxlPjx0Ym9keT48dHI+XCIsIFwiPC90cj48L3Rib2R5PjwvdGFibGU+XCIgXSxcblxuXHRcdF9kZWZhdWx0OiBbIDAsIFwiXCIsIFwiXCIgXVxuXHR9O1xuXG4vLyBTdXBwb3J0OiBJRTlcbndyYXBNYXAub3B0Z3JvdXAgPSB3cmFwTWFwLm9wdGlvbjtcblxud3JhcE1hcC50Ym9keSA9IHdyYXBNYXAudGZvb3QgPSB3cmFwTWFwLmNvbGdyb3VwID0gd3JhcE1hcC5jYXB0aW9uID0gd3JhcE1hcC50aGVhZDtcbndyYXBNYXAudGggPSB3cmFwTWFwLnRkO1xuXG4vLyBTdXBwb3J0OiAxLnggY29tcGF0aWJpbGl0eVxuLy8gTWFuaXB1bGF0aW5nIHRhYmxlcyByZXF1aXJlcyBhIHRib2R5XG5mdW5jdGlvbiBtYW5pcHVsYXRpb25UYXJnZXQoIGVsZW0sIGNvbnRlbnQgKSB7XG5cdHJldHVybiBqUXVlcnkubm9kZU5hbWUoIGVsZW0sIFwidGFibGVcIiApICYmXG5cdFx0alF1ZXJ5Lm5vZGVOYW1lKCBjb250ZW50Lm5vZGVUeXBlICE9PSAxMSA/IGNvbnRlbnQgOiBjb250ZW50LmZpcnN0Q2hpbGQsIFwidHJcIiApID9cblxuXHRcdGVsZW0uZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJ0Ym9keVwiKVswXSB8fFxuXHRcdFx0ZWxlbS5hcHBlbmRDaGlsZCggZWxlbS5vd25lckRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJ0Ym9keVwiKSApIDpcblx0XHRlbGVtO1xufVxuXG4vLyBSZXBsYWNlL3Jlc3RvcmUgdGhlIHR5cGUgYXR0cmlidXRlIG9mIHNjcmlwdCBlbGVtZW50cyBmb3Igc2FmZSBET00gbWFuaXB1bGF0aW9uXG5mdW5jdGlvbiBkaXNhYmxlU2NyaXB0KCBlbGVtICkge1xuXHRlbGVtLnR5cGUgPSAoZWxlbS5nZXRBdHRyaWJ1dGUoXCJ0eXBlXCIpICE9PSBudWxsKSArIFwiL1wiICsgZWxlbS50eXBlO1xuXHRyZXR1cm4gZWxlbTtcbn1cbmZ1bmN0aW9uIHJlc3RvcmVTY3JpcHQoIGVsZW0gKSB7XG5cdHZhciBtYXRjaCA9IHJzY3JpcHRUeXBlTWFza2VkLmV4ZWMoIGVsZW0udHlwZSApO1xuXG5cdGlmICggbWF0Y2ggKSB7XG5cdFx0ZWxlbS50eXBlID0gbWF0Y2hbIDEgXTtcblx0fSBlbHNlIHtcblx0XHRlbGVtLnJlbW92ZUF0dHJpYnV0ZShcInR5cGVcIik7XG5cdH1cblxuXHRyZXR1cm4gZWxlbTtcbn1cblxuLy8gTWFyayBzY3JpcHRzIGFzIGhhdmluZyBhbHJlYWR5IGJlZW4gZXZhbHVhdGVkXG5mdW5jdGlvbiBzZXRHbG9iYWxFdmFsKCBlbGVtcywgcmVmRWxlbWVudHMgKSB7XG5cdHZhciBpID0gMCxcblx0XHRsID0gZWxlbXMubGVuZ3RoO1xuXG5cdGZvciAoIDsgaSA8IGw7IGkrKyApIHtcblx0XHRkYXRhX3ByaXYuc2V0KFxuXHRcdFx0ZWxlbXNbIGkgXSwgXCJnbG9iYWxFdmFsXCIsICFyZWZFbGVtZW50cyB8fCBkYXRhX3ByaXYuZ2V0KCByZWZFbGVtZW50c1sgaSBdLCBcImdsb2JhbEV2YWxcIiApXG5cdFx0KTtcblx0fVxufVxuXG5mdW5jdGlvbiBjbG9uZUNvcHlFdmVudCggc3JjLCBkZXN0ICkge1xuXHR2YXIgaSwgbCwgdHlwZSwgcGRhdGFPbGQsIHBkYXRhQ3VyLCB1ZGF0YU9sZCwgdWRhdGFDdXIsIGV2ZW50cztcblxuXHRpZiAoIGRlc3Qubm9kZVR5cGUgIT09IDEgKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Ly8gMS4gQ29weSBwcml2YXRlIGRhdGE6IGV2ZW50cywgaGFuZGxlcnMsIGV0Yy5cblx0aWYgKCBkYXRhX3ByaXYuaGFzRGF0YSggc3JjICkgKSB7XG5cdFx0cGRhdGFPbGQgPSBkYXRhX3ByaXYuYWNjZXNzKCBzcmMgKTtcblx0XHRwZGF0YUN1ciA9IGRhdGFfcHJpdi5zZXQoIGRlc3QsIHBkYXRhT2xkICk7XG5cdFx0ZXZlbnRzID0gcGRhdGFPbGQuZXZlbnRzO1xuXG5cdFx0aWYgKCBldmVudHMgKSB7XG5cdFx0XHRkZWxldGUgcGRhdGFDdXIuaGFuZGxlO1xuXHRcdFx0cGRhdGFDdXIuZXZlbnRzID0ge307XG5cblx0XHRcdGZvciAoIHR5cGUgaW4gZXZlbnRzICkge1xuXHRcdFx0XHRmb3IgKCBpID0gMCwgbCA9IGV2ZW50c1sgdHlwZSBdLmxlbmd0aDsgaSA8IGw7IGkrKyApIHtcblx0XHRcdFx0XHRqUXVlcnkuZXZlbnQuYWRkKCBkZXN0LCB0eXBlLCBldmVudHNbIHR5cGUgXVsgaSBdICk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvLyAyLiBDb3B5IHVzZXIgZGF0YVxuXHRpZiAoIGRhdGFfdXNlci5oYXNEYXRhKCBzcmMgKSApIHtcblx0XHR1ZGF0YU9sZCA9IGRhdGFfdXNlci5hY2Nlc3MoIHNyYyApO1xuXHRcdHVkYXRhQ3VyID0galF1ZXJ5LmV4dGVuZCgge30sIHVkYXRhT2xkICk7XG5cblx0XHRkYXRhX3VzZXIuc2V0KCBkZXN0LCB1ZGF0YUN1ciApO1xuXHR9XG59XG5cbmZ1bmN0aW9uIGdldEFsbCggY29udGV4dCwgdGFnICkge1xuXHR2YXIgcmV0ID0gY29udGV4dC5nZXRFbGVtZW50c0J5VGFnTmFtZSA/IGNvbnRleHQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoIHRhZyB8fCBcIipcIiApIDpcblx0XHRcdGNvbnRleHQucXVlcnlTZWxlY3RvckFsbCA/IGNvbnRleHQucXVlcnlTZWxlY3RvckFsbCggdGFnIHx8IFwiKlwiICkgOlxuXHRcdFx0W107XG5cblx0cmV0dXJuIHRhZyA9PT0gdW5kZWZpbmVkIHx8IHRhZyAmJiBqUXVlcnkubm9kZU5hbWUoIGNvbnRleHQsIHRhZyApID9cblx0XHRqUXVlcnkubWVyZ2UoIFsgY29udGV4dCBdLCByZXQgKSA6XG5cdFx0cmV0O1xufVxuXG4vLyBGaXggSUUgYnVncywgc2VlIHN1cHBvcnQgdGVzdHNcbmZ1bmN0aW9uIGZpeElucHV0KCBzcmMsIGRlc3QgKSB7XG5cdHZhciBub2RlTmFtZSA9IGRlc3Qubm9kZU5hbWUudG9Mb3dlckNhc2UoKTtcblxuXHQvLyBGYWlscyB0byBwZXJzaXN0IHRoZSBjaGVja2VkIHN0YXRlIG9mIGEgY2xvbmVkIGNoZWNrYm94IG9yIHJhZGlvIGJ1dHRvbi5cblx0aWYgKCBub2RlTmFtZSA9PT0gXCJpbnB1dFwiICYmIHJjaGVja2FibGVUeXBlLnRlc3QoIHNyYy50eXBlICkgKSB7XG5cdFx0ZGVzdC5jaGVja2VkID0gc3JjLmNoZWNrZWQ7XG5cblx0Ly8gRmFpbHMgdG8gcmV0dXJuIHRoZSBzZWxlY3RlZCBvcHRpb24gdG8gdGhlIGRlZmF1bHQgc2VsZWN0ZWQgc3RhdGUgd2hlbiBjbG9uaW5nIG9wdGlvbnNcblx0fSBlbHNlIGlmICggbm9kZU5hbWUgPT09IFwiaW5wdXRcIiB8fCBub2RlTmFtZSA9PT0gXCJ0ZXh0YXJlYVwiICkge1xuXHRcdGRlc3QuZGVmYXVsdFZhbHVlID0gc3JjLmRlZmF1bHRWYWx1ZTtcblx0fVxufVxuXG5qUXVlcnkuZXh0ZW5kKHtcblx0Y2xvbmU6IGZ1bmN0aW9uKCBlbGVtLCBkYXRhQW5kRXZlbnRzLCBkZWVwRGF0YUFuZEV2ZW50cyApIHtcblx0XHR2YXIgaSwgbCwgc3JjRWxlbWVudHMsIGRlc3RFbGVtZW50cyxcblx0XHRcdGNsb25lID0gZWxlbS5jbG9uZU5vZGUoIHRydWUgKSxcblx0XHRcdGluUGFnZSA9IGpRdWVyeS5jb250YWlucyggZWxlbS5vd25lckRvY3VtZW50LCBlbGVtICk7XG5cblx0XHQvLyBGaXggSUUgY2xvbmluZyBpc3N1ZXNcblx0XHRpZiAoICFzdXBwb3J0Lm5vQ2xvbmVDaGVja2VkICYmICggZWxlbS5ub2RlVHlwZSA9PT0gMSB8fCBlbGVtLm5vZGVUeXBlID09PSAxMSApICYmXG5cdFx0XHRcdCFqUXVlcnkuaXNYTUxEb2MoIGVsZW0gKSApIHtcblxuXHRcdFx0Ly8gV2UgZXNjaGV3IFNpenpsZSBoZXJlIGZvciBwZXJmb3JtYW5jZSByZWFzb25zOiBodHRwOi8vanNwZXJmLmNvbS9nZXRhbGwtdnMtc2l6emxlLzJcblx0XHRcdGRlc3RFbGVtZW50cyA9IGdldEFsbCggY2xvbmUgKTtcblx0XHRcdHNyY0VsZW1lbnRzID0gZ2V0QWxsKCBlbGVtICk7XG5cblx0XHRcdGZvciAoIGkgPSAwLCBsID0gc3JjRWxlbWVudHMubGVuZ3RoOyBpIDwgbDsgaSsrICkge1xuXHRcdFx0XHRmaXhJbnB1dCggc3JjRWxlbWVudHNbIGkgXSwgZGVzdEVsZW1lbnRzWyBpIF0gKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBDb3B5IHRoZSBldmVudHMgZnJvbSB0aGUgb3JpZ2luYWwgdG8gdGhlIGNsb25lXG5cdFx0aWYgKCBkYXRhQW5kRXZlbnRzICkge1xuXHRcdFx0aWYgKCBkZWVwRGF0YUFuZEV2ZW50cyApIHtcblx0XHRcdFx0c3JjRWxlbWVudHMgPSBzcmNFbGVtZW50cyB8fCBnZXRBbGwoIGVsZW0gKTtcblx0XHRcdFx0ZGVzdEVsZW1lbnRzID0gZGVzdEVsZW1lbnRzIHx8IGdldEFsbCggY2xvbmUgKTtcblxuXHRcdFx0XHRmb3IgKCBpID0gMCwgbCA9IHNyY0VsZW1lbnRzLmxlbmd0aDsgaSA8IGw7IGkrKyApIHtcblx0XHRcdFx0XHRjbG9uZUNvcHlFdmVudCggc3JjRWxlbWVudHNbIGkgXSwgZGVzdEVsZW1lbnRzWyBpIF0gKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y2xvbmVDb3B5RXZlbnQoIGVsZW0sIGNsb25lICk7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gUHJlc2VydmUgc2NyaXB0IGV2YWx1YXRpb24gaGlzdG9yeVxuXHRcdGRlc3RFbGVtZW50cyA9IGdldEFsbCggY2xvbmUsIFwic2NyaXB0XCIgKTtcblx0XHRpZiAoIGRlc3RFbGVtZW50cy5sZW5ndGggPiAwICkge1xuXHRcdFx0c2V0R2xvYmFsRXZhbCggZGVzdEVsZW1lbnRzLCAhaW5QYWdlICYmIGdldEFsbCggZWxlbSwgXCJzY3JpcHRcIiApICk7XG5cdFx0fVxuXG5cdFx0Ly8gUmV0dXJuIHRoZSBjbG9uZWQgc2V0XG5cdFx0cmV0dXJuIGNsb25lO1xuXHR9LFxuXG5cdGJ1aWxkRnJhZ21lbnQ6IGZ1bmN0aW9uKCBlbGVtcywgY29udGV4dCwgc2NyaXB0cywgc2VsZWN0aW9uICkge1xuXHRcdHZhciBlbGVtLCB0bXAsIHRhZywgd3JhcCwgY29udGFpbnMsIGosXG5cdFx0XHRmcmFnbWVudCA9IGNvbnRleHQuY3JlYXRlRG9jdW1lbnRGcmFnbWVudCgpLFxuXHRcdFx0bm9kZXMgPSBbXSxcblx0XHRcdGkgPSAwLFxuXHRcdFx0bCA9IGVsZW1zLmxlbmd0aDtcblxuXHRcdGZvciAoIDsgaSA8IGw7IGkrKyApIHtcblx0XHRcdGVsZW0gPSBlbGVtc1sgaSBdO1xuXG5cdFx0XHRpZiAoIGVsZW0gfHwgZWxlbSA9PT0gMCApIHtcblxuXHRcdFx0XHQvLyBBZGQgbm9kZXMgZGlyZWN0bHlcblx0XHRcdFx0aWYgKCBqUXVlcnkudHlwZSggZWxlbSApID09PSBcIm9iamVjdFwiICkge1xuXHRcdFx0XHRcdC8vIFN1cHBvcnQ6IFF0V2ViS2l0LCBQaGFudG9tSlNcblx0XHRcdFx0XHQvLyBwdXNoLmFwcGx5KF8sIGFycmF5bGlrZSkgdGhyb3dzIG9uIGFuY2llbnQgV2ViS2l0XG5cdFx0XHRcdFx0alF1ZXJ5Lm1lcmdlKCBub2RlcywgZWxlbS5ub2RlVHlwZSA/IFsgZWxlbSBdIDogZWxlbSApO1xuXG5cdFx0XHRcdC8vIENvbnZlcnQgbm9uLWh0bWwgaW50byBhIHRleHQgbm9kZVxuXHRcdFx0XHR9IGVsc2UgaWYgKCAhcmh0bWwudGVzdCggZWxlbSApICkge1xuXHRcdFx0XHRcdG5vZGVzLnB1c2goIGNvbnRleHQuY3JlYXRlVGV4dE5vZGUoIGVsZW0gKSApO1xuXG5cdFx0XHRcdC8vIENvbnZlcnQgaHRtbCBpbnRvIERPTSBub2Rlc1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRtcCA9IHRtcCB8fCBmcmFnbWVudC5hcHBlbmRDaGlsZCggY29udGV4dC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpICk7XG5cblx0XHRcdFx0XHQvLyBEZXNlcmlhbGl6ZSBhIHN0YW5kYXJkIHJlcHJlc2VudGF0aW9uXG5cdFx0XHRcdFx0dGFnID0gKCBydGFnTmFtZS5leGVjKCBlbGVtICkgfHwgWyBcIlwiLCBcIlwiIF0gKVsgMSBdLnRvTG93ZXJDYXNlKCk7XG5cdFx0XHRcdFx0d3JhcCA9IHdyYXBNYXBbIHRhZyBdIHx8IHdyYXBNYXAuX2RlZmF1bHQ7XG5cdFx0XHRcdFx0dG1wLmlubmVySFRNTCA9IHdyYXBbIDEgXSArIGVsZW0ucmVwbGFjZSggcnhodG1sVGFnLCBcIjwkMT48LyQyPlwiICkgKyB3cmFwWyAyIF07XG5cblx0XHRcdFx0XHQvLyBEZXNjZW5kIHRocm91Z2ggd3JhcHBlcnMgdG8gdGhlIHJpZ2h0IGNvbnRlbnRcblx0XHRcdFx0XHRqID0gd3JhcFsgMCBdO1xuXHRcdFx0XHRcdHdoaWxlICggai0tICkge1xuXHRcdFx0XHRcdFx0dG1wID0gdG1wLmxhc3RDaGlsZDtcblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHQvLyBTdXBwb3J0OiBRdFdlYktpdCwgUGhhbnRvbUpTXG5cdFx0XHRcdFx0Ly8gcHVzaC5hcHBseShfLCBhcnJheWxpa2UpIHRocm93cyBvbiBhbmNpZW50IFdlYktpdFxuXHRcdFx0XHRcdGpRdWVyeS5tZXJnZSggbm9kZXMsIHRtcC5jaGlsZE5vZGVzICk7XG5cblx0XHRcdFx0XHQvLyBSZW1lbWJlciB0aGUgdG9wLWxldmVsIGNvbnRhaW5lclxuXHRcdFx0XHRcdHRtcCA9IGZyYWdtZW50LmZpcnN0Q2hpbGQ7XG5cblx0XHRcdFx0XHQvLyBFbnN1cmUgdGhlIGNyZWF0ZWQgbm9kZXMgYXJlIG9ycGhhbmVkICgjMTIzOTIpXG5cdFx0XHRcdFx0dG1wLnRleHRDb250ZW50ID0gXCJcIjtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIFJlbW92ZSB3cmFwcGVyIGZyb20gZnJhZ21lbnRcblx0XHRmcmFnbWVudC50ZXh0Q29udGVudCA9IFwiXCI7XG5cblx0XHRpID0gMDtcblx0XHR3aGlsZSAoIChlbGVtID0gbm9kZXNbIGkrKyBdKSApIHtcblxuXHRcdFx0Ly8gIzQwODcgLSBJZiBvcmlnaW4gYW5kIGRlc3RpbmF0aW9uIGVsZW1lbnRzIGFyZSB0aGUgc2FtZSwgYW5kIHRoaXMgaXNcblx0XHRcdC8vIHRoYXQgZWxlbWVudCwgZG8gbm90IGRvIGFueXRoaW5nXG5cdFx0XHRpZiAoIHNlbGVjdGlvbiAmJiBqUXVlcnkuaW5BcnJheSggZWxlbSwgc2VsZWN0aW9uICkgIT09IC0xICkge1xuXHRcdFx0XHRjb250aW51ZTtcblx0XHRcdH1cblxuXHRcdFx0Y29udGFpbnMgPSBqUXVlcnkuY29udGFpbnMoIGVsZW0ub3duZXJEb2N1bWVudCwgZWxlbSApO1xuXG5cdFx0XHQvLyBBcHBlbmQgdG8gZnJhZ21lbnRcblx0XHRcdHRtcCA9IGdldEFsbCggZnJhZ21lbnQuYXBwZW5kQ2hpbGQoIGVsZW0gKSwgXCJzY3JpcHRcIiApO1xuXG5cdFx0XHQvLyBQcmVzZXJ2ZSBzY3JpcHQgZXZhbHVhdGlvbiBoaXN0b3J5XG5cdFx0XHRpZiAoIGNvbnRhaW5zICkge1xuXHRcdFx0XHRzZXRHbG9iYWxFdmFsKCB0bXAgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQ2FwdHVyZSBleGVjdXRhYmxlc1xuXHRcdFx0aWYgKCBzY3JpcHRzICkge1xuXHRcdFx0XHRqID0gMDtcblx0XHRcdFx0d2hpbGUgKCAoZWxlbSA9IHRtcFsgaisrIF0pICkge1xuXHRcdFx0XHRcdGlmICggcnNjcmlwdFR5cGUudGVzdCggZWxlbS50eXBlIHx8IFwiXCIgKSApIHtcblx0XHRcdFx0XHRcdHNjcmlwdHMucHVzaCggZWxlbSApO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiBmcmFnbWVudDtcblx0fSxcblxuXHRjbGVhbkRhdGE6IGZ1bmN0aW9uKCBlbGVtcyApIHtcblx0XHR2YXIgZGF0YSwgZWxlbSwgdHlwZSwga2V5LFxuXHRcdFx0c3BlY2lhbCA9IGpRdWVyeS5ldmVudC5zcGVjaWFsLFxuXHRcdFx0aSA9IDA7XG5cblx0XHRmb3IgKCA7IChlbGVtID0gZWxlbXNbIGkgXSkgIT09IHVuZGVmaW5lZDsgaSsrICkge1xuXHRcdFx0aWYgKCBqUXVlcnkuYWNjZXB0RGF0YSggZWxlbSApICkge1xuXHRcdFx0XHRrZXkgPSBlbGVtWyBkYXRhX3ByaXYuZXhwYW5kbyBdO1xuXG5cdFx0XHRcdGlmICgga2V5ICYmIChkYXRhID0gZGF0YV9wcml2LmNhY2hlWyBrZXkgXSkgKSB7XG5cdFx0XHRcdFx0aWYgKCBkYXRhLmV2ZW50cyApIHtcblx0XHRcdFx0XHRcdGZvciAoIHR5cGUgaW4gZGF0YS5ldmVudHMgKSB7XG5cdFx0XHRcdFx0XHRcdGlmICggc3BlY2lhbFsgdHlwZSBdICkge1xuXHRcdFx0XHRcdFx0XHRcdGpRdWVyeS5ldmVudC5yZW1vdmUoIGVsZW0sIHR5cGUgKTtcblxuXHRcdFx0XHRcdFx0XHQvLyBUaGlzIGlzIGEgc2hvcnRjdXQgdG8gYXZvaWQgalF1ZXJ5LmV2ZW50LnJlbW92ZSdzIG92ZXJoZWFkXG5cdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdFx0alF1ZXJ5LnJlbW92ZUV2ZW50KCBlbGVtLCB0eXBlLCBkYXRhLmhhbmRsZSApO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGlmICggZGF0YV9wcml2LmNhY2hlWyBrZXkgXSApIHtcblx0XHRcdFx0XHRcdC8vIERpc2NhcmQgYW55IHJlbWFpbmluZyBgcHJpdmF0ZWAgZGF0YVxuXHRcdFx0XHRcdFx0ZGVsZXRlIGRhdGFfcHJpdi5jYWNoZVsga2V5IF07XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHQvLyBEaXNjYXJkIGFueSByZW1haW5pbmcgYHVzZXJgIGRhdGFcblx0XHRcdGRlbGV0ZSBkYXRhX3VzZXIuY2FjaGVbIGVsZW1bIGRhdGFfdXNlci5leHBhbmRvIF0gXTtcblx0XHR9XG5cdH1cbn0pO1xuXG5qUXVlcnkuZm4uZXh0ZW5kKHtcblx0dGV4dDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHJldHVybiBhY2Nlc3MoIHRoaXMsIGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHRcdHJldHVybiB2YWx1ZSA9PT0gdW5kZWZpbmVkID9cblx0XHRcdFx0alF1ZXJ5LnRleHQoIHRoaXMgKSA6XG5cdFx0XHRcdHRoaXMuZW1wdHkoKS5lYWNoKGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRcdGlmICggdGhpcy5ub2RlVHlwZSA9PT0gMSB8fCB0aGlzLm5vZGVUeXBlID09PSAxMSB8fCB0aGlzLm5vZGVUeXBlID09PSA5ICkge1xuXHRcdFx0XHRcdFx0dGhpcy50ZXh0Q29udGVudCA9IHZhbHVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSk7XG5cdFx0fSwgbnVsbCwgdmFsdWUsIGFyZ3VtZW50cy5sZW5ndGggKTtcblx0fSxcblxuXHRhcHBlbmQ6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLmRvbU1hbmlwKCBhcmd1bWVudHMsIGZ1bmN0aW9uKCBlbGVtICkge1xuXHRcdFx0aWYgKCB0aGlzLm5vZGVUeXBlID09PSAxIHx8IHRoaXMubm9kZVR5cGUgPT09IDExIHx8IHRoaXMubm9kZVR5cGUgPT09IDkgKSB7XG5cdFx0XHRcdHZhciB0YXJnZXQgPSBtYW5pcHVsYXRpb25UYXJnZXQoIHRoaXMsIGVsZW0gKTtcblx0XHRcdFx0dGFyZ2V0LmFwcGVuZENoaWxkKCBlbGVtICk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH0sXG5cblx0cHJlcGVuZDogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuZG9tTWFuaXAoIGFyZ3VtZW50cywgZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRpZiAoIHRoaXMubm9kZVR5cGUgPT09IDEgfHwgdGhpcy5ub2RlVHlwZSA9PT0gMTEgfHwgdGhpcy5ub2RlVHlwZSA9PT0gOSApIHtcblx0XHRcdFx0dmFyIHRhcmdldCA9IG1hbmlwdWxhdGlvblRhcmdldCggdGhpcywgZWxlbSApO1xuXHRcdFx0XHR0YXJnZXQuaW5zZXJ0QmVmb3JlKCBlbGVtLCB0YXJnZXQuZmlyc3RDaGlsZCApO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9LFxuXG5cdGJlZm9yZTogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIHRoaXMuZG9tTWFuaXAoIGFyZ3VtZW50cywgZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRpZiAoIHRoaXMucGFyZW50Tm9kZSApIHtcblx0XHRcdFx0dGhpcy5wYXJlbnROb2RlLmluc2VydEJlZm9yZSggZWxlbSwgdGhpcyApO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9LFxuXG5cdGFmdGVyOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5kb21NYW5pcCggYXJndW1lbnRzLCBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdGlmICggdGhpcy5wYXJlbnROb2RlICkge1xuXHRcdFx0XHR0aGlzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKCBlbGVtLCB0aGlzLm5leHRTaWJsaW5nICk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH0sXG5cblx0cmVtb3ZlOiBmdW5jdGlvbiggc2VsZWN0b3IsIGtlZXBEYXRhIC8qIEludGVybmFsIFVzZSBPbmx5ICovICkge1xuXHRcdHZhciBlbGVtLFxuXHRcdFx0ZWxlbXMgPSBzZWxlY3RvciA/IGpRdWVyeS5maWx0ZXIoIHNlbGVjdG9yLCB0aGlzICkgOiB0aGlzLFxuXHRcdFx0aSA9IDA7XG5cblx0XHRmb3IgKCA7IChlbGVtID0gZWxlbXNbaV0pICE9IG51bGw7IGkrKyApIHtcblx0XHRcdGlmICggIWtlZXBEYXRhICYmIGVsZW0ubm9kZVR5cGUgPT09IDEgKSB7XG5cdFx0XHRcdGpRdWVyeS5jbGVhbkRhdGEoIGdldEFsbCggZWxlbSApICk7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggZWxlbS5wYXJlbnROb2RlICkge1xuXHRcdFx0XHRpZiAoIGtlZXBEYXRhICYmIGpRdWVyeS5jb250YWlucyggZWxlbS5vd25lckRvY3VtZW50LCBlbGVtICkgKSB7XG5cdFx0XHRcdFx0c2V0R2xvYmFsRXZhbCggZ2V0QWxsKCBlbGVtLCBcInNjcmlwdFwiICkgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRlbGVtLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoIGVsZW0gKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fSxcblxuXHRlbXB0eTogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGVsZW0sXG5cdFx0XHRpID0gMDtcblxuXHRcdGZvciAoIDsgKGVsZW0gPSB0aGlzW2ldKSAhPSBudWxsOyBpKysgKSB7XG5cdFx0XHRpZiAoIGVsZW0ubm9kZVR5cGUgPT09IDEgKSB7XG5cblx0XHRcdFx0Ly8gUHJldmVudCBtZW1vcnkgbGVha3Ncblx0XHRcdFx0alF1ZXJ5LmNsZWFuRGF0YSggZ2V0QWxsKCBlbGVtLCBmYWxzZSApICk7XG5cblx0XHRcdFx0Ly8gUmVtb3ZlIGFueSByZW1haW5pbmcgbm9kZXNcblx0XHRcdFx0ZWxlbS50ZXh0Q29udGVudCA9IFwiXCI7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXM7XG5cdH0sXG5cblx0Y2xvbmU6IGZ1bmN0aW9uKCBkYXRhQW5kRXZlbnRzLCBkZWVwRGF0YUFuZEV2ZW50cyApIHtcblx0XHRkYXRhQW5kRXZlbnRzID0gZGF0YUFuZEV2ZW50cyA9PSBudWxsID8gZmFsc2UgOiBkYXRhQW5kRXZlbnRzO1xuXHRcdGRlZXBEYXRhQW5kRXZlbnRzID0gZGVlcERhdGFBbmRFdmVudHMgPT0gbnVsbCA/IGRhdGFBbmRFdmVudHMgOiBkZWVwRGF0YUFuZEV2ZW50cztcblxuXHRcdHJldHVybiB0aGlzLm1hcChmdW5jdGlvbigpIHtcblx0XHRcdHJldHVybiBqUXVlcnkuY2xvbmUoIHRoaXMsIGRhdGFBbmRFdmVudHMsIGRlZXBEYXRhQW5kRXZlbnRzICk7XG5cdFx0fSk7XG5cdH0sXG5cblx0aHRtbDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHJldHVybiBhY2Nlc3MoIHRoaXMsIGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHRcdHZhciBlbGVtID0gdGhpc1sgMCBdIHx8IHt9LFxuXHRcdFx0XHRpID0gMCxcblx0XHRcdFx0bCA9IHRoaXMubGVuZ3RoO1xuXG5cdFx0XHRpZiAoIHZhbHVlID09PSB1bmRlZmluZWQgJiYgZWxlbS5ub2RlVHlwZSA9PT0gMSApIHtcblx0XHRcdFx0cmV0dXJuIGVsZW0uaW5uZXJIVE1MO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTZWUgaWYgd2UgY2FuIHRha2UgYSBzaG9ydGN1dCBhbmQganVzdCB1c2UgaW5uZXJIVE1MXG5cdFx0XHRpZiAoIHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiAmJiAhcm5vSW5uZXJodG1sLnRlc3QoIHZhbHVlICkgJiZcblx0XHRcdFx0IXdyYXBNYXBbICggcnRhZ05hbWUuZXhlYyggdmFsdWUgKSB8fCBbIFwiXCIsIFwiXCIgXSApWyAxIF0udG9Mb3dlckNhc2UoKSBdICkge1xuXG5cdFx0XHRcdHZhbHVlID0gdmFsdWUucmVwbGFjZSggcnhodG1sVGFnLCBcIjwkMT48LyQyPlwiICk7XG5cblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRmb3IgKCA7IGkgPCBsOyBpKysgKSB7XG5cdFx0XHRcdFx0XHRlbGVtID0gdGhpc1sgaSBdIHx8IHt9O1xuXG5cdFx0XHRcdFx0XHQvLyBSZW1vdmUgZWxlbWVudCBub2RlcyBhbmQgcHJldmVudCBtZW1vcnkgbGVha3Ncblx0XHRcdFx0XHRcdGlmICggZWxlbS5ub2RlVHlwZSA9PT0gMSApIHtcblx0XHRcdFx0XHRcdFx0alF1ZXJ5LmNsZWFuRGF0YSggZ2V0QWxsKCBlbGVtLCBmYWxzZSApICk7XG5cdFx0XHRcdFx0XHRcdGVsZW0uaW5uZXJIVE1MID0gdmFsdWU7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0ZWxlbSA9IDA7XG5cblx0XHRcdFx0Ly8gSWYgdXNpbmcgaW5uZXJIVE1MIHRocm93cyBhbiBleGNlcHRpb24sIHVzZSB0aGUgZmFsbGJhY2sgbWV0aG9kXG5cdFx0XHRcdH0gY2F0Y2goIGUgKSB7fVxuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIGVsZW0gKSB7XG5cdFx0XHRcdHRoaXMuZW1wdHkoKS5hcHBlbmQoIHZhbHVlICk7XG5cdFx0XHR9XG5cdFx0fSwgbnVsbCwgdmFsdWUsIGFyZ3VtZW50cy5sZW5ndGggKTtcblx0fSxcblxuXHRyZXBsYWNlV2l0aDogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGFyZyA9IGFyZ3VtZW50c1sgMCBdO1xuXG5cdFx0Ly8gTWFrZSB0aGUgY2hhbmdlcywgcmVwbGFjaW5nIGVhY2ggY29udGV4dCBlbGVtZW50IHdpdGggdGhlIG5ldyBjb250ZW50XG5cdFx0dGhpcy5kb21NYW5pcCggYXJndW1lbnRzLCBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdGFyZyA9IHRoaXMucGFyZW50Tm9kZTtcblxuXHRcdFx0alF1ZXJ5LmNsZWFuRGF0YSggZ2V0QWxsKCB0aGlzICkgKTtcblxuXHRcdFx0aWYgKCBhcmcgKSB7XG5cdFx0XHRcdGFyZy5yZXBsYWNlQ2hpbGQoIGVsZW0sIHRoaXMgKTtcblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdC8vIEZvcmNlIHJlbW92YWwgaWYgdGhlcmUgd2FzIG5vIG5ldyBjb250ZW50IChlLmcuLCBmcm9tIGVtcHR5IGFyZ3VtZW50cylcblx0XHRyZXR1cm4gYXJnICYmIChhcmcubGVuZ3RoIHx8IGFyZy5ub2RlVHlwZSkgPyB0aGlzIDogdGhpcy5yZW1vdmUoKTtcblx0fSxcblxuXHRkZXRhY2g6IGZ1bmN0aW9uKCBzZWxlY3RvciApIHtcblx0XHRyZXR1cm4gdGhpcy5yZW1vdmUoIHNlbGVjdG9yLCB0cnVlICk7XG5cdH0sXG5cblx0ZG9tTWFuaXA6IGZ1bmN0aW9uKCBhcmdzLCBjYWxsYmFjayApIHtcblxuXHRcdC8vIEZsYXR0ZW4gYW55IG5lc3RlZCBhcnJheXNcblx0XHRhcmdzID0gY29uY2F0LmFwcGx5KCBbXSwgYXJncyApO1xuXG5cdFx0dmFyIGZyYWdtZW50LCBmaXJzdCwgc2NyaXB0cywgaGFzU2NyaXB0cywgbm9kZSwgZG9jLFxuXHRcdFx0aSA9IDAsXG5cdFx0XHRsID0gdGhpcy5sZW5ndGgsXG5cdFx0XHRzZXQgPSB0aGlzLFxuXHRcdFx0aU5vQ2xvbmUgPSBsIC0gMSxcblx0XHRcdHZhbHVlID0gYXJnc1sgMCBdLFxuXHRcdFx0aXNGdW5jdGlvbiA9IGpRdWVyeS5pc0Z1bmN0aW9uKCB2YWx1ZSApO1xuXG5cdFx0Ly8gV2UgY2FuJ3QgY2xvbmVOb2RlIGZyYWdtZW50cyB0aGF0IGNvbnRhaW4gY2hlY2tlZCwgaW4gV2ViS2l0XG5cdFx0aWYgKCBpc0Z1bmN0aW9uIHx8XG5cdFx0XHRcdCggbCA+IDEgJiYgdHlwZW9mIHZhbHVlID09PSBcInN0cmluZ1wiICYmXG5cdFx0XHRcdFx0IXN1cHBvcnQuY2hlY2tDbG9uZSAmJiByY2hlY2tlZC50ZXN0KCB2YWx1ZSApICkgKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCBpbmRleCApIHtcblx0XHRcdFx0dmFyIHNlbGYgPSBzZXQuZXEoIGluZGV4ICk7XG5cdFx0XHRcdGlmICggaXNGdW5jdGlvbiApIHtcblx0XHRcdFx0XHRhcmdzWyAwIF0gPSB2YWx1ZS5jYWxsKCB0aGlzLCBpbmRleCwgc2VsZi5odG1sKCkgKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRzZWxmLmRvbU1hbmlwKCBhcmdzLCBjYWxsYmFjayApO1xuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0aWYgKCBsICkge1xuXHRcdFx0ZnJhZ21lbnQgPSBqUXVlcnkuYnVpbGRGcmFnbWVudCggYXJncywgdGhpc1sgMCBdLm93bmVyRG9jdW1lbnQsIGZhbHNlLCB0aGlzICk7XG5cdFx0XHRmaXJzdCA9IGZyYWdtZW50LmZpcnN0Q2hpbGQ7XG5cblx0XHRcdGlmICggZnJhZ21lbnQuY2hpbGROb2Rlcy5sZW5ndGggPT09IDEgKSB7XG5cdFx0XHRcdGZyYWdtZW50ID0gZmlyc3Q7XG5cdFx0XHR9XG5cblx0XHRcdGlmICggZmlyc3QgKSB7XG5cdFx0XHRcdHNjcmlwdHMgPSBqUXVlcnkubWFwKCBnZXRBbGwoIGZyYWdtZW50LCBcInNjcmlwdFwiICksIGRpc2FibGVTY3JpcHQgKTtcblx0XHRcdFx0aGFzU2NyaXB0cyA9IHNjcmlwdHMubGVuZ3RoO1xuXG5cdFx0XHRcdC8vIFVzZSB0aGUgb3JpZ2luYWwgZnJhZ21lbnQgZm9yIHRoZSBsYXN0IGl0ZW0gaW5zdGVhZCBvZiB0aGUgZmlyc3QgYmVjYXVzZSBpdCBjYW4gZW5kIHVwXG5cdFx0XHRcdC8vIGJlaW5nIGVtcHRpZWQgaW5jb3JyZWN0bHkgaW4gY2VydGFpbiBzaXR1YXRpb25zICgjODA3MCkuXG5cdFx0XHRcdGZvciAoIDsgaSA8IGw7IGkrKyApIHtcblx0XHRcdFx0XHRub2RlID0gZnJhZ21lbnQ7XG5cblx0XHRcdFx0XHRpZiAoIGkgIT09IGlOb0Nsb25lICkge1xuXHRcdFx0XHRcdFx0bm9kZSA9IGpRdWVyeS5jbG9uZSggbm9kZSwgdHJ1ZSwgdHJ1ZSApO1xuXG5cdFx0XHRcdFx0XHQvLyBLZWVwIHJlZmVyZW5jZXMgdG8gY2xvbmVkIHNjcmlwdHMgZm9yIGxhdGVyIHJlc3RvcmF0aW9uXG5cdFx0XHRcdFx0XHRpZiAoIGhhc1NjcmlwdHMgKSB7XG5cdFx0XHRcdFx0XHRcdC8vIFN1cHBvcnQ6IFF0V2ViS2l0XG5cdFx0XHRcdFx0XHRcdC8vIGpRdWVyeS5tZXJnZSBiZWNhdXNlIHB1c2guYXBwbHkoXywgYXJyYXlsaWtlKSB0aHJvd3Ncblx0XHRcdFx0XHRcdFx0alF1ZXJ5Lm1lcmdlKCBzY3JpcHRzLCBnZXRBbGwoIG5vZGUsIFwic2NyaXB0XCIgKSApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGNhbGxiYWNrLmNhbGwoIHRoaXNbIGkgXSwgbm9kZSwgaSApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0aWYgKCBoYXNTY3JpcHRzICkge1xuXHRcdFx0XHRcdGRvYyA9IHNjcmlwdHNbIHNjcmlwdHMubGVuZ3RoIC0gMSBdLm93bmVyRG9jdW1lbnQ7XG5cblx0XHRcdFx0XHQvLyBSZWVuYWJsZSBzY3JpcHRzXG5cdFx0XHRcdFx0alF1ZXJ5Lm1hcCggc2NyaXB0cywgcmVzdG9yZVNjcmlwdCApO1xuXG5cdFx0XHRcdFx0Ly8gRXZhbHVhdGUgZXhlY3V0YWJsZSBzY3JpcHRzIG9uIGZpcnN0IGRvY3VtZW50IGluc2VydGlvblxuXHRcdFx0XHRcdGZvciAoIGkgPSAwOyBpIDwgaGFzU2NyaXB0czsgaSsrICkge1xuXHRcdFx0XHRcdFx0bm9kZSA9IHNjcmlwdHNbIGkgXTtcblx0XHRcdFx0XHRcdGlmICggcnNjcmlwdFR5cGUudGVzdCggbm9kZS50eXBlIHx8IFwiXCIgKSAmJlxuXHRcdFx0XHRcdFx0XHQhZGF0YV9wcml2LmFjY2Vzcyggbm9kZSwgXCJnbG9iYWxFdmFsXCIgKSAmJiBqUXVlcnkuY29udGFpbnMoIGRvYywgbm9kZSApICkge1xuXG5cdFx0XHRcdFx0XHRcdGlmICggbm9kZS5zcmMgKSB7XG5cdFx0XHRcdFx0XHRcdFx0Ly8gT3B0aW9uYWwgQUpBWCBkZXBlbmRlbmN5LCBidXQgd29uJ3QgcnVuIHNjcmlwdHMgaWYgbm90IHByZXNlbnRcblx0XHRcdFx0XHRcdFx0XHRpZiAoIGpRdWVyeS5fZXZhbFVybCApIHtcblx0XHRcdFx0XHRcdFx0XHRcdGpRdWVyeS5fZXZhbFVybCggbm9kZS5zcmMgKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdFx0alF1ZXJ5Lmdsb2JhbEV2YWwoIG5vZGUudGV4dENvbnRlbnQucmVwbGFjZSggcmNsZWFuU2NyaXB0LCBcIlwiICkgKTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG59KTtcblxualF1ZXJ5LmVhY2goe1xuXHRhcHBlbmRUbzogXCJhcHBlbmRcIixcblx0cHJlcGVuZFRvOiBcInByZXBlbmRcIixcblx0aW5zZXJ0QmVmb3JlOiBcImJlZm9yZVwiLFxuXHRpbnNlcnRBZnRlcjogXCJhZnRlclwiLFxuXHRyZXBsYWNlQWxsOiBcInJlcGxhY2VXaXRoXCJcbn0sIGZ1bmN0aW9uKCBuYW1lLCBvcmlnaW5hbCApIHtcblx0alF1ZXJ5LmZuWyBuYW1lIF0gPSBmdW5jdGlvbiggc2VsZWN0b3IgKSB7XG5cdFx0dmFyIGVsZW1zLFxuXHRcdFx0cmV0ID0gW10sXG5cdFx0XHRpbnNlcnQgPSBqUXVlcnkoIHNlbGVjdG9yICksXG5cdFx0XHRsYXN0ID0gaW5zZXJ0Lmxlbmd0aCAtIDEsXG5cdFx0XHRpID0gMDtcblxuXHRcdGZvciAoIDsgaSA8PSBsYXN0OyBpKysgKSB7XG5cdFx0XHRlbGVtcyA9IGkgPT09IGxhc3QgPyB0aGlzIDogdGhpcy5jbG9uZSggdHJ1ZSApO1xuXHRcdFx0alF1ZXJ5KCBpbnNlcnRbIGkgXSApWyBvcmlnaW5hbCBdKCBlbGVtcyApO1xuXG5cdFx0XHQvLyBTdXBwb3J0OiBRdFdlYktpdFxuXHRcdFx0Ly8gLmdldCgpIGJlY2F1c2UgcHVzaC5hcHBseShfLCBhcnJheWxpa2UpIHRocm93c1xuXHRcdFx0cHVzaC5hcHBseSggcmV0LCBlbGVtcy5nZXQoKSApO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLnB1c2hTdGFjayggcmV0ICk7XG5cdH07XG59KTtcblxuXG52YXIgaWZyYW1lLFxuXHRlbGVtZGlzcGxheSA9IHt9O1xuXG4vKipcbiAqIFJldHJpZXZlIHRoZSBhY3R1YWwgZGlzcGxheSBvZiBhIGVsZW1lbnRcbiAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lIG5vZGVOYW1lIG9mIHRoZSBlbGVtZW50XG4gKiBAcGFyYW0ge09iamVjdH0gZG9jIERvY3VtZW50IG9iamVjdFxuICovXG4vLyBDYWxsZWQgb25seSBmcm9tIHdpdGhpbiBkZWZhdWx0RGlzcGxheVxuZnVuY3Rpb24gYWN0dWFsRGlzcGxheSggbmFtZSwgZG9jICkge1xuXHR2YXIgc3R5bGUsXG5cdFx0ZWxlbSA9IGpRdWVyeSggZG9jLmNyZWF0ZUVsZW1lbnQoIG5hbWUgKSApLmFwcGVuZFRvKCBkb2MuYm9keSApLFxuXG5cdFx0Ly8gZ2V0RGVmYXVsdENvbXB1dGVkU3R5bGUgbWlnaHQgYmUgcmVsaWFibHkgdXNlZCBvbmx5IG9uIGF0dGFjaGVkIGVsZW1lbnRcblx0XHRkaXNwbGF5ID0gd2luZG93LmdldERlZmF1bHRDb21wdXRlZFN0eWxlICYmICggc3R5bGUgPSB3aW5kb3cuZ2V0RGVmYXVsdENvbXB1dGVkU3R5bGUoIGVsZW1bIDAgXSApICkgP1xuXG5cdFx0XHQvLyBVc2Ugb2YgdGhpcyBtZXRob2QgaXMgYSB0ZW1wb3JhcnkgZml4IChtb3JlIGxpa2Ugb3B0aW1pemF0aW9uKSB1bnRpbCBzb21ldGhpbmcgYmV0dGVyIGNvbWVzIGFsb25nLFxuXHRcdFx0Ly8gc2luY2UgaXQgd2FzIHJlbW92ZWQgZnJvbSBzcGVjaWZpY2F0aW9uIGFuZCBzdXBwb3J0ZWQgb25seSBpbiBGRlxuXHRcdFx0c3R5bGUuZGlzcGxheSA6IGpRdWVyeS5jc3MoIGVsZW1bIDAgXSwgXCJkaXNwbGF5XCIgKTtcblxuXHQvLyBXZSBkb24ndCBoYXZlIGFueSBkYXRhIHN0b3JlZCBvbiB0aGUgZWxlbWVudCxcblx0Ly8gc28gdXNlIFwiZGV0YWNoXCIgbWV0aG9kIGFzIGZhc3Qgd2F5IHRvIGdldCByaWQgb2YgdGhlIGVsZW1lbnRcblx0ZWxlbS5kZXRhY2goKTtcblxuXHRyZXR1cm4gZGlzcGxheTtcbn1cblxuLyoqXG4gKiBUcnkgdG8gZGV0ZXJtaW5lIHRoZSBkZWZhdWx0IGRpc3BsYXkgdmFsdWUgb2YgYW4gZWxlbWVudFxuICogQHBhcmFtIHtTdHJpbmd9IG5vZGVOYW1lXG4gKi9cbmZ1bmN0aW9uIGRlZmF1bHREaXNwbGF5KCBub2RlTmFtZSApIHtcblx0dmFyIGRvYyA9IGRvY3VtZW50LFxuXHRcdGRpc3BsYXkgPSBlbGVtZGlzcGxheVsgbm9kZU5hbWUgXTtcblxuXHRpZiAoICFkaXNwbGF5ICkge1xuXHRcdGRpc3BsYXkgPSBhY3R1YWxEaXNwbGF5KCBub2RlTmFtZSwgZG9jICk7XG5cblx0XHQvLyBJZiB0aGUgc2ltcGxlIHdheSBmYWlscywgcmVhZCBmcm9tIGluc2lkZSBhbiBpZnJhbWVcblx0XHRpZiAoIGRpc3BsYXkgPT09IFwibm9uZVwiIHx8ICFkaXNwbGF5ICkge1xuXG5cdFx0XHQvLyBVc2UgdGhlIGFscmVhZHktY3JlYXRlZCBpZnJhbWUgaWYgcG9zc2libGVcblx0XHRcdGlmcmFtZSA9IChpZnJhbWUgfHwgalF1ZXJ5KCBcIjxpZnJhbWUgZnJhbWVib3JkZXI9JzAnIHdpZHRoPScwJyBoZWlnaHQ9JzAnLz5cIiApKS5hcHBlbmRUbyggZG9jLmRvY3VtZW50RWxlbWVudCApO1xuXG5cdFx0XHQvLyBBbHdheXMgd3JpdGUgYSBuZXcgSFRNTCBza2VsZXRvbiBzbyBXZWJraXQgYW5kIEZpcmVmb3ggZG9uJ3QgY2hva2Ugb24gcmV1c2Vcblx0XHRcdGRvYyA9IGlmcmFtZVsgMCBdLmNvbnRlbnREb2N1bWVudDtcblxuXHRcdFx0Ly8gU3VwcG9ydDogSUVcblx0XHRcdGRvYy53cml0ZSgpO1xuXHRcdFx0ZG9jLmNsb3NlKCk7XG5cblx0XHRcdGRpc3BsYXkgPSBhY3R1YWxEaXNwbGF5KCBub2RlTmFtZSwgZG9jICk7XG5cdFx0XHRpZnJhbWUuZGV0YWNoKCk7XG5cdFx0fVxuXG5cdFx0Ly8gU3RvcmUgdGhlIGNvcnJlY3QgZGVmYXVsdCBkaXNwbGF5XG5cdFx0ZWxlbWRpc3BsYXlbIG5vZGVOYW1lIF0gPSBkaXNwbGF5O1xuXHR9XG5cblx0cmV0dXJuIGRpc3BsYXk7XG59XG52YXIgcm1hcmdpbiA9ICgvXm1hcmdpbi8pO1xuXG52YXIgcm51bW5vbnB4ID0gbmV3IFJlZ0V4cCggXCJeKFwiICsgcG51bSArIFwiKSg/IXB4KVthLXolXSskXCIsIFwiaVwiICk7XG5cbnZhciBnZXRTdHlsZXMgPSBmdW5jdGlvbiggZWxlbSApIHtcblx0XHQvLyBTdXBwb3J0OiBJRTw9MTErLCBGaXJlZm94PD0zMCsgKCMxNTA5OCwgIzE0MTUwKVxuXHRcdC8vIElFIHRocm93cyBvbiBlbGVtZW50cyBjcmVhdGVkIGluIHBvcHVwc1xuXHRcdC8vIEZGIG1lYW53aGlsZSB0aHJvd3Mgb24gZnJhbWUgZWxlbWVudHMgdGhyb3VnaCBcImRlZmF1bHRWaWV3LmdldENvbXB1dGVkU3R5bGVcIlxuXHRcdGlmICggZWxlbS5vd25lckRvY3VtZW50LmRlZmF1bHRWaWV3Lm9wZW5lciApIHtcblx0XHRcdHJldHVybiBlbGVtLm93bmVyRG9jdW1lbnQuZGVmYXVsdFZpZXcuZ2V0Q29tcHV0ZWRTdHlsZSggZWxlbSwgbnVsbCApO1xuXHRcdH1cblxuXHRcdHJldHVybiB3aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZSggZWxlbSwgbnVsbCApO1xuXHR9O1xuXG5cblxuZnVuY3Rpb24gY3VyQ1NTKCBlbGVtLCBuYW1lLCBjb21wdXRlZCApIHtcblx0dmFyIHdpZHRoLCBtaW5XaWR0aCwgbWF4V2lkdGgsIHJldCxcblx0XHRzdHlsZSA9IGVsZW0uc3R5bGU7XG5cblx0Y29tcHV0ZWQgPSBjb21wdXRlZCB8fCBnZXRTdHlsZXMoIGVsZW0gKTtcblxuXHQvLyBTdXBwb3J0OiBJRTlcblx0Ly8gZ2V0UHJvcGVydHlWYWx1ZSBpcyBvbmx5IG5lZWRlZCBmb3IgLmNzcygnZmlsdGVyJykgKCMxMjUzNylcblx0aWYgKCBjb21wdXRlZCApIHtcblx0XHRyZXQgPSBjb21wdXRlZC5nZXRQcm9wZXJ0eVZhbHVlKCBuYW1lICkgfHwgY29tcHV0ZWRbIG5hbWUgXTtcblx0fVxuXG5cdGlmICggY29tcHV0ZWQgKSB7XG5cblx0XHRpZiAoIHJldCA9PT0gXCJcIiAmJiAhalF1ZXJ5LmNvbnRhaW5zKCBlbGVtLm93bmVyRG9jdW1lbnQsIGVsZW0gKSApIHtcblx0XHRcdHJldCA9IGpRdWVyeS5zdHlsZSggZWxlbSwgbmFtZSApO1xuXHRcdH1cblxuXHRcdC8vIFN1cHBvcnQ6IGlPUyA8IDZcblx0XHQvLyBBIHRyaWJ1dGUgdG8gdGhlIFwiYXdlc29tZSBoYWNrIGJ5IERlYW4gRWR3YXJkc1wiXG5cdFx0Ly8gaU9TIDwgNiAoYXQgbGVhc3QpIHJldHVybnMgcGVyY2VudGFnZSBmb3IgYSBsYXJnZXIgc2V0IG9mIHZhbHVlcywgYnV0IHdpZHRoIHNlZW1zIHRvIGJlIHJlbGlhYmx5IHBpeGVsc1xuXHRcdC8vIHRoaXMgaXMgYWdhaW5zdCB0aGUgQ1NTT00gZHJhZnQgc3BlYzogaHR0cDovL2Rldi53My5vcmcvY3Nzd2cvY3Nzb20vI3Jlc29sdmVkLXZhbHVlc1xuXHRcdGlmICggcm51bW5vbnB4LnRlc3QoIHJldCApICYmIHJtYXJnaW4udGVzdCggbmFtZSApICkge1xuXG5cdFx0XHQvLyBSZW1lbWJlciB0aGUgb3JpZ2luYWwgdmFsdWVzXG5cdFx0XHR3aWR0aCA9IHN0eWxlLndpZHRoO1xuXHRcdFx0bWluV2lkdGggPSBzdHlsZS5taW5XaWR0aDtcblx0XHRcdG1heFdpZHRoID0gc3R5bGUubWF4V2lkdGg7XG5cblx0XHRcdC8vIFB1dCBpbiB0aGUgbmV3IHZhbHVlcyB0byBnZXQgYSBjb21wdXRlZCB2YWx1ZSBvdXRcblx0XHRcdHN0eWxlLm1pbldpZHRoID0gc3R5bGUubWF4V2lkdGggPSBzdHlsZS53aWR0aCA9IHJldDtcblx0XHRcdHJldCA9IGNvbXB1dGVkLndpZHRoO1xuXG5cdFx0XHQvLyBSZXZlcnQgdGhlIGNoYW5nZWQgdmFsdWVzXG5cdFx0XHRzdHlsZS53aWR0aCA9IHdpZHRoO1xuXHRcdFx0c3R5bGUubWluV2lkdGggPSBtaW5XaWR0aDtcblx0XHRcdHN0eWxlLm1heFdpZHRoID0gbWF4V2lkdGg7XG5cdFx0fVxuXHR9XG5cblx0cmV0dXJuIHJldCAhPT0gdW5kZWZpbmVkID9cblx0XHQvLyBTdXBwb3J0OiBJRVxuXHRcdC8vIElFIHJldHVybnMgekluZGV4IHZhbHVlIGFzIGFuIGludGVnZXIuXG5cdFx0cmV0ICsgXCJcIiA6XG5cdFx0cmV0O1xufVxuXG5cbmZ1bmN0aW9uIGFkZEdldEhvb2tJZiggY29uZGl0aW9uRm4sIGhvb2tGbiApIHtcblx0Ly8gRGVmaW5lIHRoZSBob29rLCB3ZSdsbCBjaGVjayBvbiB0aGUgZmlyc3QgcnVuIGlmIGl0J3MgcmVhbGx5IG5lZWRlZC5cblx0cmV0dXJuIHtcblx0XHRnZXQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCBjb25kaXRpb25GbigpICkge1xuXHRcdFx0XHQvLyBIb29rIG5vdCBuZWVkZWQgKG9yIGl0J3Mgbm90IHBvc3NpYmxlIHRvIHVzZSBpdCBkdWVcblx0XHRcdFx0Ly8gdG8gbWlzc2luZyBkZXBlbmRlbmN5KSwgcmVtb3ZlIGl0LlxuXHRcdFx0XHRkZWxldGUgdGhpcy5nZXQ7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSG9vayBuZWVkZWQ7IHJlZGVmaW5lIGl0IHNvIHRoYXQgdGhlIHN1cHBvcnQgdGVzdCBpcyBub3QgZXhlY3V0ZWQgYWdhaW4uXG5cdFx0XHRyZXR1cm4gKHRoaXMuZ2V0ID0gaG9va0ZuKS5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdFx0fVxuXHR9O1xufVxuXG5cbihmdW5jdGlvbigpIHtcblx0dmFyIHBpeGVsUG9zaXRpb25WYWwsIGJveFNpemluZ1JlbGlhYmxlVmFsLFxuXHRcdGRvY0VsZW0gPSBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQsXG5cdFx0Y29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggXCJkaXZcIiApLFxuXHRcdGRpdiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIFwiZGl2XCIgKTtcblxuXHRpZiAoICFkaXYuc3R5bGUgKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Ly8gU3VwcG9ydDogSUU5LTExK1xuXHQvLyBTdHlsZSBvZiBjbG9uZWQgZWxlbWVudCBhZmZlY3RzIHNvdXJjZSBlbGVtZW50IGNsb25lZCAoIzg5MDgpXG5cdGRpdi5zdHlsZS5iYWNrZ3JvdW5kQ2xpcCA9IFwiY29udGVudC1ib3hcIjtcblx0ZGl2LmNsb25lTm9kZSggdHJ1ZSApLnN0eWxlLmJhY2tncm91bmRDbGlwID0gXCJcIjtcblx0c3VwcG9ydC5jbGVhckNsb25lU3R5bGUgPSBkaXYuc3R5bGUuYmFja2dyb3VuZENsaXAgPT09IFwiY29udGVudC1ib3hcIjtcblxuXHRjb250YWluZXIuc3R5bGUuY3NzVGV4dCA9IFwiYm9yZGVyOjA7d2lkdGg6MDtoZWlnaHQ6MDt0b3A6MDtsZWZ0Oi05OTk5cHg7bWFyZ2luLXRvcDoxcHg7XCIgK1xuXHRcdFwicG9zaXRpb246YWJzb2x1dGVcIjtcblx0Y29udGFpbmVyLmFwcGVuZENoaWxkKCBkaXYgKTtcblxuXHQvLyBFeGVjdXRpbmcgYm90aCBwaXhlbFBvc2l0aW9uICYgYm94U2l6aW5nUmVsaWFibGUgdGVzdHMgcmVxdWlyZSBvbmx5IG9uZSBsYXlvdXRcblx0Ly8gc28gdGhleSdyZSBleGVjdXRlZCBhdCB0aGUgc2FtZSB0aW1lIHRvIHNhdmUgdGhlIHNlY29uZCBjb21wdXRhdGlvbi5cblx0ZnVuY3Rpb24gY29tcHV0ZVBpeGVsUG9zaXRpb25BbmRCb3hTaXppbmdSZWxpYWJsZSgpIHtcblx0XHRkaXYuc3R5bGUuY3NzVGV4dCA9XG5cdFx0XHQvLyBTdXBwb3J0OiBGaXJlZm94PDI5LCBBbmRyb2lkIDIuM1xuXHRcdFx0Ly8gVmVuZG9yLXByZWZpeCBib3gtc2l6aW5nXG5cdFx0XHRcIi13ZWJraXQtYm94LXNpemluZzpib3JkZXItYm94Oy1tb3otYm94LXNpemluZzpib3JkZXItYm94O1wiICtcblx0XHRcdFwiYm94LXNpemluZzpib3JkZXItYm94O2Rpc3BsYXk6YmxvY2s7bWFyZ2luLXRvcDoxJTt0b3A6MSU7XCIgK1xuXHRcdFx0XCJib3JkZXI6MXB4O3BhZGRpbmc6MXB4O3dpZHRoOjRweDtwb3NpdGlvbjphYnNvbHV0ZVwiO1xuXHRcdGRpdi5pbm5lckhUTUwgPSBcIlwiO1xuXHRcdGRvY0VsZW0uYXBwZW5kQ2hpbGQoIGNvbnRhaW5lciApO1xuXG5cdFx0dmFyIGRpdlN0eWxlID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUoIGRpdiwgbnVsbCApO1xuXHRcdHBpeGVsUG9zaXRpb25WYWwgPSBkaXZTdHlsZS50b3AgIT09IFwiMSVcIjtcblx0XHRib3hTaXppbmdSZWxpYWJsZVZhbCA9IGRpdlN0eWxlLndpZHRoID09PSBcIjRweFwiO1xuXG5cdFx0ZG9jRWxlbS5yZW1vdmVDaGlsZCggY29udGFpbmVyICk7XG5cdH1cblxuXHQvLyBTdXBwb3J0OiBub2RlLmpzIGpzZG9tXG5cdC8vIERvbid0IGFzc3VtZSB0aGF0IGdldENvbXB1dGVkU3R5bGUgaXMgYSBwcm9wZXJ0eSBvZiB0aGUgZ2xvYmFsIG9iamVjdFxuXHRpZiAoIHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlICkge1xuXHRcdGpRdWVyeS5leHRlbmQoIHN1cHBvcnQsIHtcblx0XHRcdHBpeGVsUG9zaXRpb246IGZ1bmN0aW9uKCkge1xuXG5cdFx0XHRcdC8vIFRoaXMgdGVzdCBpcyBleGVjdXRlZCBvbmx5IG9uY2UgYnV0IHdlIHN0aWxsIGRvIG1lbW9pemluZ1xuXHRcdFx0XHQvLyBzaW5jZSB3ZSBjYW4gdXNlIHRoZSBib3hTaXppbmdSZWxpYWJsZSBwcmUtY29tcHV0aW5nLlxuXHRcdFx0XHQvLyBObyBuZWVkIHRvIGNoZWNrIGlmIHRoZSB0ZXN0IHdhcyBhbHJlYWR5IHBlcmZvcm1lZCwgdGhvdWdoLlxuXHRcdFx0XHRjb21wdXRlUGl4ZWxQb3NpdGlvbkFuZEJveFNpemluZ1JlbGlhYmxlKCk7XG5cdFx0XHRcdHJldHVybiBwaXhlbFBvc2l0aW9uVmFsO1xuXHRcdFx0fSxcblx0XHRcdGJveFNpemluZ1JlbGlhYmxlOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0aWYgKCBib3hTaXppbmdSZWxpYWJsZVZhbCA9PSBudWxsICkge1xuXHRcdFx0XHRcdGNvbXB1dGVQaXhlbFBvc2l0aW9uQW5kQm94U2l6aW5nUmVsaWFibGUoKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRyZXR1cm4gYm94U2l6aW5nUmVsaWFibGVWYWw7XG5cdFx0XHR9LFxuXHRcdFx0cmVsaWFibGVNYXJnaW5SaWdodDogZnVuY3Rpb24oKSB7XG5cblx0XHRcdFx0Ly8gU3VwcG9ydDogQW5kcm9pZCAyLjNcblx0XHRcdFx0Ly8gQ2hlY2sgaWYgZGl2IHdpdGggZXhwbGljaXQgd2lkdGggYW5kIG5vIG1hcmdpbi1yaWdodCBpbmNvcnJlY3RseVxuXHRcdFx0XHQvLyBnZXRzIGNvbXB1dGVkIG1hcmdpbi1yaWdodCBiYXNlZCBvbiB3aWR0aCBvZiBjb250YWluZXIuICgjMzMzMylcblx0XHRcdFx0Ly8gV2ViS2l0IEJ1ZyAxMzM0MyAtIGdldENvbXB1dGVkU3R5bGUgcmV0dXJucyB3cm9uZyB2YWx1ZSBmb3IgbWFyZ2luLXJpZ2h0XG5cdFx0XHRcdC8vIFRoaXMgc3VwcG9ydCBmdW5jdGlvbiBpcyBvbmx5IGV4ZWN1dGVkIG9uY2Ugc28gbm8gbWVtb2l6aW5nIGlzIG5lZWRlZC5cblx0XHRcdFx0dmFyIHJldCxcblx0XHRcdFx0XHRtYXJnaW5EaXYgPSBkaXYuYXBwZW5kQ2hpbGQoIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoIFwiZGl2XCIgKSApO1xuXG5cdFx0XHRcdC8vIFJlc2V0IENTUzogYm94LXNpemluZzsgZGlzcGxheTsgbWFyZ2luOyBib3JkZXI7IHBhZGRpbmdcblx0XHRcdFx0bWFyZ2luRGl2LnN0eWxlLmNzc1RleHQgPSBkaXYuc3R5bGUuY3NzVGV4dCA9XG5cdFx0XHRcdFx0Ly8gU3VwcG9ydDogRmlyZWZveDwyOSwgQW5kcm9pZCAyLjNcblx0XHRcdFx0XHQvLyBWZW5kb3ItcHJlZml4IGJveC1zaXppbmdcblx0XHRcdFx0XHRcIi13ZWJraXQtYm94LXNpemluZzpjb250ZW50LWJveDstbW96LWJveC1zaXppbmc6Y29udGVudC1ib3g7XCIgK1xuXHRcdFx0XHRcdFwiYm94LXNpemluZzpjb250ZW50LWJveDtkaXNwbGF5OmJsb2NrO21hcmdpbjowO2JvcmRlcjowO3BhZGRpbmc6MFwiO1xuXHRcdFx0XHRtYXJnaW5EaXYuc3R5bGUubWFyZ2luUmlnaHQgPSBtYXJnaW5EaXYuc3R5bGUud2lkdGggPSBcIjBcIjtcblx0XHRcdFx0ZGl2LnN0eWxlLndpZHRoID0gXCIxcHhcIjtcblx0XHRcdFx0ZG9jRWxlbS5hcHBlbmRDaGlsZCggY29udGFpbmVyICk7XG5cblx0XHRcdFx0cmV0ID0gIXBhcnNlRmxvYXQoIHdpbmRvdy5nZXRDb21wdXRlZFN0eWxlKCBtYXJnaW5EaXYsIG51bGwgKS5tYXJnaW5SaWdodCApO1xuXG5cdFx0XHRcdGRvY0VsZW0ucmVtb3ZlQ2hpbGQoIGNvbnRhaW5lciApO1xuXHRcdFx0XHRkaXYucmVtb3ZlQ2hpbGQoIG1hcmdpbkRpdiApO1xuXG5cdFx0XHRcdHJldHVybiByZXQ7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cbn0pKCk7XG5cblxuLy8gQSBtZXRob2QgZm9yIHF1aWNrbHkgc3dhcHBpbmcgaW4vb3V0IENTUyBwcm9wZXJ0aWVzIHRvIGdldCBjb3JyZWN0IGNhbGN1bGF0aW9ucy5cbmpRdWVyeS5zd2FwID0gZnVuY3Rpb24oIGVsZW0sIG9wdGlvbnMsIGNhbGxiYWNrLCBhcmdzICkge1xuXHR2YXIgcmV0LCBuYW1lLFxuXHRcdG9sZCA9IHt9O1xuXG5cdC8vIFJlbWVtYmVyIHRoZSBvbGQgdmFsdWVzLCBhbmQgaW5zZXJ0IHRoZSBuZXcgb25lc1xuXHRmb3IgKCBuYW1lIGluIG9wdGlvbnMgKSB7XG5cdFx0b2xkWyBuYW1lIF0gPSBlbGVtLnN0eWxlWyBuYW1lIF07XG5cdFx0ZWxlbS5zdHlsZVsgbmFtZSBdID0gb3B0aW9uc1sgbmFtZSBdO1xuXHR9XG5cblx0cmV0ID0gY2FsbGJhY2suYXBwbHkoIGVsZW0sIGFyZ3MgfHwgW10gKTtcblxuXHQvLyBSZXZlcnQgdGhlIG9sZCB2YWx1ZXNcblx0Zm9yICggbmFtZSBpbiBvcHRpb25zICkge1xuXHRcdGVsZW0uc3R5bGVbIG5hbWUgXSA9IG9sZFsgbmFtZSBdO1xuXHR9XG5cblx0cmV0dXJuIHJldDtcbn07XG5cblxudmFyXG5cdC8vIFN3YXBwYWJsZSBpZiBkaXNwbGF5IGlzIG5vbmUgb3Igc3RhcnRzIHdpdGggdGFibGUgZXhjZXB0IFwidGFibGVcIiwgXCJ0YWJsZS1jZWxsXCIsIG9yIFwidGFibGUtY2FwdGlvblwiXG5cdC8vIFNlZSBoZXJlIGZvciBkaXNwbGF5IHZhbHVlczogaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9DU1MvZGlzcGxheVxuXHRyZGlzcGxheXN3YXAgPSAvXihub25lfHRhYmxlKD8hLWNbZWFdKS4rKS8sXG5cdHJudW1zcGxpdCA9IG5ldyBSZWdFeHAoIFwiXihcIiArIHBudW0gKyBcIikoLiopJFwiLCBcImlcIiApLFxuXHRycmVsTnVtID0gbmV3IFJlZ0V4cCggXCJeKFsrLV0pPShcIiArIHBudW0gKyBcIilcIiwgXCJpXCIgKSxcblxuXHRjc3NTaG93ID0geyBwb3NpdGlvbjogXCJhYnNvbHV0ZVwiLCB2aXNpYmlsaXR5OiBcImhpZGRlblwiLCBkaXNwbGF5OiBcImJsb2NrXCIgfSxcblx0Y3NzTm9ybWFsVHJhbnNmb3JtID0ge1xuXHRcdGxldHRlclNwYWNpbmc6IFwiMFwiLFxuXHRcdGZvbnRXZWlnaHQ6IFwiNDAwXCJcblx0fSxcblxuXHRjc3NQcmVmaXhlcyA9IFsgXCJXZWJraXRcIiwgXCJPXCIsIFwiTW96XCIsIFwibXNcIiBdO1xuXG4vLyBSZXR1cm4gYSBjc3MgcHJvcGVydHkgbWFwcGVkIHRvIGEgcG90ZW50aWFsbHkgdmVuZG9yIHByZWZpeGVkIHByb3BlcnR5XG5mdW5jdGlvbiB2ZW5kb3JQcm9wTmFtZSggc3R5bGUsIG5hbWUgKSB7XG5cblx0Ly8gU2hvcnRjdXQgZm9yIG5hbWVzIHRoYXQgYXJlIG5vdCB2ZW5kb3IgcHJlZml4ZWRcblx0aWYgKCBuYW1lIGluIHN0eWxlICkge1xuXHRcdHJldHVybiBuYW1lO1xuXHR9XG5cblx0Ly8gQ2hlY2sgZm9yIHZlbmRvciBwcmVmaXhlZCBuYW1lc1xuXHR2YXIgY2FwTmFtZSA9IG5hbWVbMF0udG9VcHBlckNhc2UoKSArIG5hbWUuc2xpY2UoMSksXG5cdFx0b3JpZ05hbWUgPSBuYW1lLFxuXHRcdGkgPSBjc3NQcmVmaXhlcy5sZW5ndGg7XG5cblx0d2hpbGUgKCBpLS0gKSB7XG5cdFx0bmFtZSA9IGNzc1ByZWZpeGVzWyBpIF0gKyBjYXBOYW1lO1xuXHRcdGlmICggbmFtZSBpbiBzdHlsZSApIHtcblx0XHRcdHJldHVybiBuYW1lO1xuXHRcdH1cblx0fVxuXG5cdHJldHVybiBvcmlnTmFtZTtcbn1cblxuZnVuY3Rpb24gc2V0UG9zaXRpdmVOdW1iZXIoIGVsZW0sIHZhbHVlLCBzdWJ0cmFjdCApIHtcblx0dmFyIG1hdGNoZXMgPSBybnVtc3BsaXQuZXhlYyggdmFsdWUgKTtcblx0cmV0dXJuIG1hdGNoZXMgP1xuXHRcdC8vIEd1YXJkIGFnYWluc3QgdW5kZWZpbmVkIFwic3VidHJhY3RcIiwgZS5nLiwgd2hlbiB1c2VkIGFzIGluIGNzc0hvb2tzXG5cdFx0TWF0aC5tYXgoIDAsIG1hdGNoZXNbIDEgXSAtICggc3VidHJhY3QgfHwgMCApICkgKyAoIG1hdGNoZXNbIDIgXSB8fCBcInB4XCIgKSA6XG5cdFx0dmFsdWU7XG59XG5cbmZ1bmN0aW9uIGF1Z21lbnRXaWR0aE9ySGVpZ2h0KCBlbGVtLCBuYW1lLCBleHRyYSwgaXNCb3JkZXJCb3gsIHN0eWxlcyApIHtcblx0dmFyIGkgPSBleHRyYSA9PT0gKCBpc0JvcmRlckJveCA/IFwiYm9yZGVyXCIgOiBcImNvbnRlbnRcIiApID9cblx0XHQvLyBJZiB3ZSBhbHJlYWR5IGhhdmUgdGhlIHJpZ2h0IG1lYXN1cmVtZW50LCBhdm9pZCBhdWdtZW50YXRpb25cblx0XHQ0IDpcblx0XHQvLyBPdGhlcndpc2UgaW5pdGlhbGl6ZSBmb3IgaG9yaXpvbnRhbCBvciB2ZXJ0aWNhbCBwcm9wZXJ0aWVzXG5cdFx0bmFtZSA9PT0gXCJ3aWR0aFwiID8gMSA6IDAsXG5cblx0XHR2YWwgPSAwO1xuXG5cdGZvciAoIDsgaSA8IDQ7IGkgKz0gMiApIHtcblx0XHQvLyBCb3RoIGJveCBtb2RlbHMgZXhjbHVkZSBtYXJnaW4sIHNvIGFkZCBpdCBpZiB3ZSB3YW50IGl0XG5cdFx0aWYgKCBleHRyYSA9PT0gXCJtYXJnaW5cIiApIHtcblx0XHRcdHZhbCArPSBqUXVlcnkuY3NzKCBlbGVtLCBleHRyYSArIGNzc0V4cGFuZFsgaSBdLCB0cnVlLCBzdHlsZXMgKTtcblx0XHR9XG5cblx0XHRpZiAoIGlzQm9yZGVyQm94ICkge1xuXHRcdFx0Ly8gYm9yZGVyLWJveCBpbmNsdWRlcyBwYWRkaW5nLCBzbyByZW1vdmUgaXQgaWYgd2Ugd2FudCBjb250ZW50XG5cdFx0XHRpZiAoIGV4dHJhID09PSBcImNvbnRlbnRcIiApIHtcblx0XHRcdFx0dmFsIC09IGpRdWVyeS5jc3MoIGVsZW0sIFwicGFkZGluZ1wiICsgY3NzRXhwYW5kWyBpIF0sIHRydWUsIHN0eWxlcyApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBBdCB0aGlzIHBvaW50LCBleHRyYSBpc24ndCBib3JkZXIgbm9yIG1hcmdpbiwgc28gcmVtb3ZlIGJvcmRlclxuXHRcdFx0aWYgKCBleHRyYSAhPT0gXCJtYXJnaW5cIiApIHtcblx0XHRcdFx0dmFsIC09IGpRdWVyeS5jc3MoIGVsZW0sIFwiYm9yZGVyXCIgKyBjc3NFeHBhbmRbIGkgXSArIFwiV2lkdGhcIiwgdHJ1ZSwgc3R5bGVzICk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEF0IHRoaXMgcG9pbnQsIGV4dHJhIGlzbid0IGNvbnRlbnQsIHNvIGFkZCBwYWRkaW5nXG5cdFx0XHR2YWwgKz0galF1ZXJ5LmNzcyggZWxlbSwgXCJwYWRkaW5nXCIgKyBjc3NFeHBhbmRbIGkgXSwgdHJ1ZSwgc3R5bGVzICk7XG5cblx0XHRcdC8vIEF0IHRoaXMgcG9pbnQsIGV4dHJhIGlzbid0IGNvbnRlbnQgbm9yIHBhZGRpbmcsIHNvIGFkZCBib3JkZXJcblx0XHRcdGlmICggZXh0cmEgIT09IFwicGFkZGluZ1wiICkge1xuXHRcdFx0XHR2YWwgKz0galF1ZXJ5LmNzcyggZWxlbSwgXCJib3JkZXJcIiArIGNzc0V4cGFuZFsgaSBdICsgXCJXaWR0aFwiLCB0cnVlLCBzdHlsZXMgKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gdmFsO1xufVxuXG5mdW5jdGlvbiBnZXRXaWR0aE9ySGVpZ2h0KCBlbGVtLCBuYW1lLCBleHRyYSApIHtcblxuXHQvLyBTdGFydCB3aXRoIG9mZnNldCBwcm9wZXJ0eSwgd2hpY2ggaXMgZXF1aXZhbGVudCB0byB0aGUgYm9yZGVyLWJveCB2YWx1ZVxuXHR2YXIgdmFsdWVJc0JvcmRlckJveCA9IHRydWUsXG5cdFx0dmFsID0gbmFtZSA9PT0gXCJ3aWR0aFwiID8gZWxlbS5vZmZzZXRXaWR0aCA6IGVsZW0ub2Zmc2V0SGVpZ2h0LFxuXHRcdHN0eWxlcyA9IGdldFN0eWxlcyggZWxlbSApLFxuXHRcdGlzQm9yZGVyQm94ID0galF1ZXJ5LmNzcyggZWxlbSwgXCJib3hTaXppbmdcIiwgZmFsc2UsIHN0eWxlcyApID09PSBcImJvcmRlci1ib3hcIjtcblxuXHQvLyBTb21lIG5vbi1odG1sIGVsZW1lbnRzIHJldHVybiB1bmRlZmluZWQgZm9yIG9mZnNldFdpZHRoLCBzbyBjaGVjayBmb3IgbnVsbC91bmRlZmluZWRcblx0Ly8gc3ZnIC0gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9NjQ5Mjg1XG5cdC8vIE1hdGhNTCAtIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTQ5MTY2OFxuXHRpZiAoIHZhbCA8PSAwIHx8IHZhbCA9PSBudWxsICkge1xuXHRcdC8vIEZhbGwgYmFjayB0byBjb21wdXRlZCB0aGVuIHVuY29tcHV0ZWQgY3NzIGlmIG5lY2Vzc2FyeVxuXHRcdHZhbCA9IGN1ckNTUyggZWxlbSwgbmFtZSwgc3R5bGVzICk7XG5cdFx0aWYgKCB2YWwgPCAwIHx8IHZhbCA9PSBudWxsICkge1xuXHRcdFx0dmFsID0gZWxlbS5zdHlsZVsgbmFtZSBdO1xuXHRcdH1cblxuXHRcdC8vIENvbXB1dGVkIHVuaXQgaXMgbm90IHBpeGVscy4gU3RvcCBoZXJlIGFuZCByZXR1cm4uXG5cdFx0aWYgKCBybnVtbm9ucHgudGVzdCh2YWwpICkge1xuXHRcdFx0cmV0dXJuIHZhbDtcblx0XHR9XG5cblx0XHQvLyBDaGVjayBmb3Igc3R5bGUgaW4gY2FzZSBhIGJyb3dzZXIgd2hpY2ggcmV0dXJucyB1bnJlbGlhYmxlIHZhbHVlc1xuXHRcdC8vIGZvciBnZXRDb21wdXRlZFN0eWxlIHNpbGVudGx5IGZhbGxzIGJhY2sgdG8gdGhlIHJlbGlhYmxlIGVsZW0uc3R5bGVcblx0XHR2YWx1ZUlzQm9yZGVyQm94ID0gaXNCb3JkZXJCb3ggJiZcblx0XHRcdCggc3VwcG9ydC5ib3hTaXppbmdSZWxpYWJsZSgpIHx8IHZhbCA9PT0gZWxlbS5zdHlsZVsgbmFtZSBdICk7XG5cblx0XHQvLyBOb3JtYWxpemUgXCJcIiwgYXV0bywgYW5kIHByZXBhcmUgZm9yIGV4dHJhXG5cdFx0dmFsID0gcGFyc2VGbG9hdCggdmFsICkgfHwgMDtcblx0fVxuXG5cdC8vIFVzZSB0aGUgYWN0aXZlIGJveC1zaXppbmcgbW9kZWwgdG8gYWRkL3N1YnRyYWN0IGlycmVsZXZhbnQgc3R5bGVzXG5cdHJldHVybiAoIHZhbCArXG5cdFx0YXVnbWVudFdpZHRoT3JIZWlnaHQoXG5cdFx0XHRlbGVtLFxuXHRcdFx0bmFtZSxcblx0XHRcdGV4dHJhIHx8ICggaXNCb3JkZXJCb3ggPyBcImJvcmRlclwiIDogXCJjb250ZW50XCIgKSxcblx0XHRcdHZhbHVlSXNCb3JkZXJCb3gsXG5cdFx0XHRzdHlsZXNcblx0XHQpXG5cdCkgKyBcInB4XCI7XG59XG5cbmZ1bmN0aW9uIHNob3dIaWRlKCBlbGVtZW50cywgc2hvdyApIHtcblx0dmFyIGRpc3BsYXksIGVsZW0sIGhpZGRlbixcblx0XHR2YWx1ZXMgPSBbXSxcblx0XHRpbmRleCA9IDAsXG5cdFx0bGVuZ3RoID0gZWxlbWVudHMubGVuZ3RoO1xuXG5cdGZvciAoIDsgaW5kZXggPCBsZW5ndGg7IGluZGV4KysgKSB7XG5cdFx0ZWxlbSA9IGVsZW1lbnRzWyBpbmRleCBdO1xuXHRcdGlmICggIWVsZW0uc3R5bGUgKSB7XG5cdFx0XHRjb250aW51ZTtcblx0XHR9XG5cblx0XHR2YWx1ZXNbIGluZGV4IF0gPSBkYXRhX3ByaXYuZ2V0KCBlbGVtLCBcIm9sZGRpc3BsYXlcIiApO1xuXHRcdGRpc3BsYXkgPSBlbGVtLnN0eWxlLmRpc3BsYXk7XG5cdFx0aWYgKCBzaG93ICkge1xuXHRcdFx0Ly8gUmVzZXQgdGhlIGlubGluZSBkaXNwbGF5IG9mIHRoaXMgZWxlbWVudCB0byBsZWFybiBpZiBpdCBpc1xuXHRcdFx0Ly8gYmVpbmcgaGlkZGVuIGJ5IGNhc2NhZGVkIHJ1bGVzIG9yIG5vdFxuXHRcdFx0aWYgKCAhdmFsdWVzWyBpbmRleCBdICYmIGRpc3BsYXkgPT09IFwibm9uZVwiICkge1xuXHRcdFx0XHRlbGVtLnN0eWxlLmRpc3BsYXkgPSBcIlwiO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTZXQgZWxlbWVudHMgd2hpY2ggaGF2ZSBiZWVuIG92ZXJyaWRkZW4gd2l0aCBkaXNwbGF5OiBub25lXG5cdFx0XHQvLyBpbiBhIHN0eWxlc2hlZXQgdG8gd2hhdGV2ZXIgdGhlIGRlZmF1bHQgYnJvd3NlciBzdHlsZSBpc1xuXHRcdFx0Ly8gZm9yIHN1Y2ggYW4gZWxlbWVudFxuXHRcdFx0aWYgKCBlbGVtLnN0eWxlLmRpc3BsYXkgPT09IFwiXCIgJiYgaXNIaWRkZW4oIGVsZW0gKSApIHtcblx0XHRcdFx0dmFsdWVzWyBpbmRleCBdID0gZGF0YV9wcml2LmFjY2VzcyggZWxlbSwgXCJvbGRkaXNwbGF5XCIsIGRlZmF1bHREaXNwbGF5KGVsZW0ubm9kZU5hbWUpICk7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGhpZGRlbiA9IGlzSGlkZGVuKCBlbGVtICk7XG5cblx0XHRcdGlmICggZGlzcGxheSAhPT0gXCJub25lXCIgfHwgIWhpZGRlbiApIHtcblx0XHRcdFx0ZGF0YV9wcml2LnNldCggZWxlbSwgXCJvbGRkaXNwbGF5XCIsIGhpZGRlbiA/IGRpc3BsYXkgOiBqUXVlcnkuY3NzKCBlbGVtLCBcImRpc3BsYXlcIiApICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0Ly8gU2V0IHRoZSBkaXNwbGF5IG9mIG1vc3Qgb2YgdGhlIGVsZW1lbnRzIGluIGEgc2Vjb25kIGxvb3Bcblx0Ly8gdG8gYXZvaWQgdGhlIGNvbnN0YW50IHJlZmxvd1xuXHRmb3IgKCBpbmRleCA9IDA7IGluZGV4IDwgbGVuZ3RoOyBpbmRleCsrICkge1xuXHRcdGVsZW0gPSBlbGVtZW50c1sgaW5kZXggXTtcblx0XHRpZiAoICFlbGVtLnN0eWxlICkge1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGlmICggIXNob3cgfHwgZWxlbS5zdHlsZS5kaXNwbGF5ID09PSBcIm5vbmVcIiB8fCBlbGVtLnN0eWxlLmRpc3BsYXkgPT09IFwiXCIgKSB7XG5cdFx0XHRlbGVtLnN0eWxlLmRpc3BsYXkgPSBzaG93ID8gdmFsdWVzWyBpbmRleCBdIHx8IFwiXCIgOiBcIm5vbmVcIjtcblx0XHR9XG5cdH1cblxuXHRyZXR1cm4gZWxlbWVudHM7XG59XG5cbmpRdWVyeS5leHRlbmQoe1xuXG5cdC8vIEFkZCBpbiBzdHlsZSBwcm9wZXJ0eSBob29rcyBmb3Igb3ZlcnJpZGluZyB0aGUgZGVmYXVsdFxuXHQvLyBiZWhhdmlvciBvZiBnZXR0aW5nIGFuZCBzZXR0aW5nIGEgc3R5bGUgcHJvcGVydHlcblx0Y3NzSG9va3M6IHtcblx0XHRvcGFjaXR5OiB7XG5cdFx0XHRnZXQ6IGZ1bmN0aW9uKCBlbGVtLCBjb21wdXRlZCApIHtcblx0XHRcdFx0aWYgKCBjb21wdXRlZCApIHtcblxuXHRcdFx0XHRcdC8vIFdlIHNob3VsZCBhbHdheXMgZ2V0IGEgbnVtYmVyIGJhY2sgZnJvbSBvcGFjaXR5XG5cdFx0XHRcdFx0dmFyIHJldCA9IGN1ckNTUyggZWxlbSwgXCJvcGFjaXR5XCIgKTtcblx0XHRcdFx0XHRyZXR1cm4gcmV0ID09PSBcIlwiID8gXCIxXCIgOiByZXQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH0sXG5cblx0Ly8gRG9uJ3QgYXV0b21hdGljYWxseSBhZGQgXCJweFwiIHRvIHRoZXNlIHBvc3NpYmx5LXVuaXRsZXNzIHByb3BlcnRpZXNcblx0Y3NzTnVtYmVyOiB7XG5cdFx0XCJjb2x1bW5Db3VudFwiOiB0cnVlLFxuXHRcdFwiZmlsbE9wYWNpdHlcIjogdHJ1ZSxcblx0XHRcImZsZXhHcm93XCI6IHRydWUsXG5cdFx0XCJmbGV4U2hyaW5rXCI6IHRydWUsXG5cdFx0XCJmb250V2VpZ2h0XCI6IHRydWUsXG5cdFx0XCJsaW5lSGVpZ2h0XCI6IHRydWUsXG5cdFx0XCJvcGFjaXR5XCI6IHRydWUsXG5cdFx0XCJvcmRlclwiOiB0cnVlLFxuXHRcdFwib3JwaGFuc1wiOiB0cnVlLFxuXHRcdFwid2lkb3dzXCI6IHRydWUsXG5cdFx0XCJ6SW5kZXhcIjogdHJ1ZSxcblx0XHRcInpvb21cIjogdHJ1ZVxuXHR9LFxuXG5cdC8vIEFkZCBpbiBwcm9wZXJ0aWVzIHdob3NlIG5hbWVzIHlvdSB3aXNoIHRvIGZpeCBiZWZvcmVcblx0Ly8gc2V0dGluZyBvciBnZXR0aW5nIHRoZSB2YWx1ZVxuXHRjc3NQcm9wczoge1xuXHRcdFwiZmxvYXRcIjogXCJjc3NGbG9hdFwiXG5cdH0sXG5cblx0Ly8gR2V0IGFuZCBzZXQgdGhlIHN0eWxlIHByb3BlcnR5IG9uIGEgRE9NIE5vZGVcblx0c3R5bGU6IGZ1bmN0aW9uKCBlbGVtLCBuYW1lLCB2YWx1ZSwgZXh0cmEgKSB7XG5cblx0XHQvLyBEb24ndCBzZXQgc3R5bGVzIG9uIHRleHQgYW5kIGNvbW1lbnQgbm9kZXNcblx0XHRpZiAoICFlbGVtIHx8IGVsZW0ubm9kZVR5cGUgPT09IDMgfHwgZWxlbS5ub2RlVHlwZSA9PT0gOCB8fCAhZWxlbS5zdHlsZSApIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBNYWtlIHN1cmUgdGhhdCB3ZSdyZSB3b3JraW5nIHdpdGggdGhlIHJpZ2h0IG5hbWVcblx0XHR2YXIgcmV0LCB0eXBlLCBob29rcyxcblx0XHRcdG9yaWdOYW1lID0galF1ZXJ5LmNhbWVsQ2FzZSggbmFtZSApLFxuXHRcdFx0c3R5bGUgPSBlbGVtLnN0eWxlO1xuXG5cdFx0bmFtZSA9IGpRdWVyeS5jc3NQcm9wc1sgb3JpZ05hbWUgXSB8fCAoIGpRdWVyeS5jc3NQcm9wc1sgb3JpZ05hbWUgXSA9IHZlbmRvclByb3BOYW1lKCBzdHlsZSwgb3JpZ05hbWUgKSApO1xuXG5cdFx0Ly8gR2V0cyBob29rIGZvciB0aGUgcHJlZml4ZWQgdmVyc2lvbiwgdGhlbiB1bnByZWZpeGVkIHZlcnNpb25cblx0XHRob29rcyA9IGpRdWVyeS5jc3NIb29rc1sgbmFtZSBdIHx8IGpRdWVyeS5jc3NIb29rc1sgb3JpZ05hbWUgXTtcblxuXHRcdC8vIENoZWNrIGlmIHdlJ3JlIHNldHRpbmcgYSB2YWx1ZVxuXHRcdGlmICggdmFsdWUgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdHR5cGUgPSB0eXBlb2YgdmFsdWU7XG5cblx0XHRcdC8vIENvbnZlcnQgXCIrPVwiIG9yIFwiLT1cIiB0byByZWxhdGl2ZSBudW1iZXJzICgjNzM0NSlcblx0XHRcdGlmICggdHlwZSA9PT0gXCJzdHJpbmdcIiAmJiAocmV0ID0gcnJlbE51bS5leGVjKCB2YWx1ZSApKSApIHtcblx0XHRcdFx0dmFsdWUgPSAoIHJldFsxXSArIDEgKSAqIHJldFsyXSArIHBhcnNlRmxvYXQoIGpRdWVyeS5jc3MoIGVsZW0sIG5hbWUgKSApO1xuXHRcdFx0XHQvLyBGaXhlcyBidWcgIzkyMzdcblx0XHRcdFx0dHlwZSA9IFwibnVtYmVyXCI7XG5cdFx0XHR9XG5cblx0XHRcdC8vIE1ha2Ugc3VyZSB0aGF0IG51bGwgYW5kIE5hTiB2YWx1ZXMgYXJlbid0IHNldCAoIzcxMTYpXG5cdFx0XHRpZiAoIHZhbHVlID09IG51bGwgfHwgdmFsdWUgIT09IHZhbHVlICkge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cblx0XHRcdC8vIElmIGEgbnVtYmVyLCBhZGQgJ3B4JyB0byB0aGUgKGV4Y2VwdCBmb3IgY2VydGFpbiBDU1MgcHJvcGVydGllcylcblx0XHRcdGlmICggdHlwZSA9PT0gXCJudW1iZXJcIiAmJiAhalF1ZXJ5LmNzc051bWJlclsgb3JpZ05hbWUgXSApIHtcblx0XHRcdFx0dmFsdWUgKz0gXCJweFwiO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTdXBwb3J0OiBJRTktMTErXG5cdFx0XHQvLyBiYWNrZ3JvdW5kLSogcHJvcHMgYWZmZWN0IG9yaWdpbmFsIGNsb25lJ3MgdmFsdWVzXG5cdFx0XHRpZiAoICFzdXBwb3J0LmNsZWFyQ2xvbmVTdHlsZSAmJiB2YWx1ZSA9PT0gXCJcIiAmJiBuYW1lLmluZGV4T2YoIFwiYmFja2dyb3VuZFwiICkgPT09IDAgKSB7XG5cdFx0XHRcdHN0eWxlWyBuYW1lIF0gPSBcImluaGVyaXRcIjtcblx0XHRcdH1cblxuXHRcdFx0Ly8gSWYgYSBob29rIHdhcyBwcm92aWRlZCwgdXNlIHRoYXQgdmFsdWUsIG90aGVyd2lzZSBqdXN0IHNldCB0aGUgc3BlY2lmaWVkIHZhbHVlXG5cdFx0XHRpZiAoICFob29rcyB8fCAhKFwic2V0XCIgaW4gaG9va3MpIHx8ICh2YWx1ZSA9IGhvb2tzLnNldCggZWxlbSwgdmFsdWUsIGV4dHJhICkpICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdHN0eWxlWyBuYW1lIF0gPSB2YWx1ZTtcblx0XHRcdH1cblxuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBJZiBhIGhvb2sgd2FzIHByb3ZpZGVkIGdldCB0aGUgbm9uLWNvbXB1dGVkIHZhbHVlIGZyb20gdGhlcmVcblx0XHRcdGlmICggaG9va3MgJiYgXCJnZXRcIiBpbiBob29rcyAmJiAocmV0ID0gaG9va3MuZ2V0KCBlbGVtLCBmYWxzZSwgZXh0cmEgKSkgIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0cmV0dXJuIHJldDtcblx0XHRcdH1cblxuXHRcdFx0Ly8gT3RoZXJ3aXNlIGp1c3QgZ2V0IHRoZSB2YWx1ZSBmcm9tIHRoZSBzdHlsZSBvYmplY3Rcblx0XHRcdHJldHVybiBzdHlsZVsgbmFtZSBdO1xuXHRcdH1cblx0fSxcblxuXHRjc3M6IGZ1bmN0aW9uKCBlbGVtLCBuYW1lLCBleHRyYSwgc3R5bGVzICkge1xuXHRcdHZhciB2YWwsIG51bSwgaG9va3MsXG5cdFx0XHRvcmlnTmFtZSA9IGpRdWVyeS5jYW1lbENhc2UoIG5hbWUgKTtcblxuXHRcdC8vIE1ha2Ugc3VyZSB0aGF0IHdlJ3JlIHdvcmtpbmcgd2l0aCB0aGUgcmlnaHQgbmFtZVxuXHRcdG5hbWUgPSBqUXVlcnkuY3NzUHJvcHNbIG9yaWdOYW1lIF0gfHwgKCBqUXVlcnkuY3NzUHJvcHNbIG9yaWdOYW1lIF0gPSB2ZW5kb3JQcm9wTmFtZSggZWxlbS5zdHlsZSwgb3JpZ05hbWUgKSApO1xuXG5cdFx0Ly8gVHJ5IHByZWZpeGVkIG5hbWUgZm9sbG93ZWQgYnkgdGhlIHVucHJlZml4ZWQgbmFtZVxuXHRcdGhvb2tzID0galF1ZXJ5LmNzc0hvb2tzWyBuYW1lIF0gfHwgalF1ZXJ5LmNzc0hvb2tzWyBvcmlnTmFtZSBdO1xuXG5cdFx0Ly8gSWYgYSBob29rIHdhcyBwcm92aWRlZCBnZXQgdGhlIGNvbXB1dGVkIHZhbHVlIGZyb20gdGhlcmVcblx0XHRpZiAoIGhvb2tzICYmIFwiZ2V0XCIgaW4gaG9va3MgKSB7XG5cdFx0XHR2YWwgPSBob29rcy5nZXQoIGVsZW0sIHRydWUsIGV4dHJhICk7XG5cdFx0fVxuXG5cdFx0Ly8gT3RoZXJ3aXNlLCBpZiBhIHdheSB0byBnZXQgdGhlIGNvbXB1dGVkIHZhbHVlIGV4aXN0cywgdXNlIHRoYXRcblx0XHRpZiAoIHZhbCA9PT0gdW5kZWZpbmVkICkge1xuXHRcdFx0dmFsID0gY3VyQ1NTKCBlbGVtLCBuYW1lLCBzdHlsZXMgKTtcblx0XHR9XG5cblx0XHQvLyBDb252ZXJ0IFwibm9ybWFsXCIgdG8gY29tcHV0ZWQgdmFsdWVcblx0XHRpZiAoIHZhbCA9PT0gXCJub3JtYWxcIiAmJiBuYW1lIGluIGNzc05vcm1hbFRyYW5zZm9ybSApIHtcblx0XHRcdHZhbCA9IGNzc05vcm1hbFRyYW5zZm9ybVsgbmFtZSBdO1xuXHRcdH1cblxuXHRcdC8vIE1ha2UgbnVtZXJpYyBpZiBmb3JjZWQgb3IgYSBxdWFsaWZpZXIgd2FzIHByb3ZpZGVkIGFuZCB2YWwgbG9va3MgbnVtZXJpY1xuXHRcdGlmICggZXh0cmEgPT09IFwiXCIgfHwgZXh0cmEgKSB7XG5cdFx0XHRudW0gPSBwYXJzZUZsb2F0KCB2YWwgKTtcblx0XHRcdHJldHVybiBleHRyYSA9PT0gdHJ1ZSB8fCBqUXVlcnkuaXNOdW1lcmljKCBudW0gKSA/IG51bSB8fCAwIDogdmFsO1xuXHRcdH1cblx0XHRyZXR1cm4gdmFsO1xuXHR9XG59KTtcblxualF1ZXJ5LmVhY2goWyBcImhlaWdodFwiLCBcIndpZHRoXCIgXSwgZnVuY3Rpb24oIGksIG5hbWUgKSB7XG5cdGpRdWVyeS5jc3NIb29rc1sgbmFtZSBdID0ge1xuXHRcdGdldDogZnVuY3Rpb24oIGVsZW0sIGNvbXB1dGVkLCBleHRyYSApIHtcblx0XHRcdGlmICggY29tcHV0ZWQgKSB7XG5cblx0XHRcdFx0Ly8gQ2VydGFpbiBlbGVtZW50cyBjYW4gaGF2ZSBkaW1lbnNpb24gaW5mbyBpZiB3ZSBpbnZpc2libHkgc2hvdyB0aGVtXG5cdFx0XHRcdC8vIGJ1dCBpdCBtdXN0IGhhdmUgYSBjdXJyZW50IGRpc3BsYXkgc3R5bGUgdGhhdCB3b3VsZCBiZW5lZml0XG5cdFx0XHRcdHJldHVybiByZGlzcGxheXN3YXAudGVzdCggalF1ZXJ5LmNzcyggZWxlbSwgXCJkaXNwbGF5XCIgKSApICYmIGVsZW0ub2Zmc2V0V2lkdGggPT09IDAgP1xuXHRcdFx0XHRcdGpRdWVyeS5zd2FwKCBlbGVtLCBjc3NTaG93LCBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRcdHJldHVybiBnZXRXaWR0aE9ySGVpZ2h0KCBlbGVtLCBuYW1lLCBleHRyYSApO1xuXHRcdFx0XHRcdH0pIDpcblx0XHRcdFx0XHRnZXRXaWR0aE9ySGVpZ2h0KCBlbGVtLCBuYW1lLCBleHRyYSApO1xuXHRcdFx0fVxuXHRcdH0sXG5cblx0XHRzZXQ6IGZ1bmN0aW9uKCBlbGVtLCB2YWx1ZSwgZXh0cmEgKSB7XG5cdFx0XHR2YXIgc3R5bGVzID0gZXh0cmEgJiYgZ2V0U3R5bGVzKCBlbGVtICk7XG5cdFx0XHRyZXR1cm4gc2V0UG9zaXRpdmVOdW1iZXIoIGVsZW0sIHZhbHVlLCBleHRyYSA/XG5cdFx0XHRcdGF1Z21lbnRXaWR0aE9ySGVpZ2h0KFxuXHRcdFx0XHRcdGVsZW0sXG5cdFx0XHRcdFx0bmFtZSxcblx0XHRcdFx0XHRleHRyYSxcblx0XHRcdFx0XHRqUXVlcnkuY3NzKCBlbGVtLCBcImJveFNpemluZ1wiLCBmYWxzZSwgc3R5bGVzICkgPT09IFwiYm9yZGVyLWJveFwiLFxuXHRcdFx0XHRcdHN0eWxlc1xuXHRcdFx0XHQpIDogMFxuXHRcdFx0KTtcblx0XHR9XG5cdH07XG59KTtcblxuLy8gU3VwcG9ydDogQW5kcm9pZCAyLjNcbmpRdWVyeS5jc3NIb29rcy5tYXJnaW5SaWdodCA9IGFkZEdldEhvb2tJZiggc3VwcG9ydC5yZWxpYWJsZU1hcmdpblJpZ2h0LFxuXHRmdW5jdGlvbiggZWxlbSwgY29tcHV0ZWQgKSB7XG5cdFx0aWYgKCBjb21wdXRlZCApIHtcblx0XHRcdHJldHVybiBqUXVlcnkuc3dhcCggZWxlbSwgeyBcImRpc3BsYXlcIjogXCJpbmxpbmUtYmxvY2tcIiB9LFxuXHRcdFx0XHRjdXJDU1MsIFsgZWxlbSwgXCJtYXJnaW5SaWdodFwiIF0gKTtcblx0XHR9XG5cdH1cbik7XG5cbi8vIFRoZXNlIGhvb2tzIGFyZSB1c2VkIGJ5IGFuaW1hdGUgdG8gZXhwYW5kIHByb3BlcnRpZXNcbmpRdWVyeS5lYWNoKHtcblx0bWFyZ2luOiBcIlwiLFxuXHRwYWRkaW5nOiBcIlwiLFxuXHRib3JkZXI6IFwiV2lkdGhcIlxufSwgZnVuY3Rpb24oIHByZWZpeCwgc3VmZml4ICkge1xuXHRqUXVlcnkuY3NzSG9va3NbIHByZWZpeCArIHN1ZmZpeCBdID0ge1xuXHRcdGV4cGFuZDogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdFx0dmFyIGkgPSAwLFxuXHRcdFx0XHRleHBhbmRlZCA9IHt9LFxuXG5cdFx0XHRcdC8vIEFzc3VtZXMgYSBzaW5nbGUgbnVtYmVyIGlmIG5vdCBhIHN0cmluZ1xuXHRcdFx0XHRwYXJ0cyA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiA/IHZhbHVlLnNwbGl0KFwiIFwiKSA6IFsgdmFsdWUgXTtcblxuXHRcdFx0Zm9yICggOyBpIDwgNDsgaSsrICkge1xuXHRcdFx0XHRleHBhbmRlZFsgcHJlZml4ICsgY3NzRXhwYW5kWyBpIF0gKyBzdWZmaXggXSA9XG5cdFx0XHRcdFx0cGFydHNbIGkgXSB8fCBwYXJ0c1sgaSAtIDIgXSB8fCBwYXJ0c1sgMCBdO1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gZXhwYW5kZWQ7XG5cdFx0fVxuXHR9O1xuXG5cdGlmICggIXJtYXJnaW4udGVzdCggcHJlZml4ICkgKSB7XG5cdFx0alF1ZXJ5LmNzc0hvb2tzWyBwcmVmaXggKyBzdWZmaXggXS5zZXQgPSBzZXRQb3NpdGl2ZU51bWJlcjtcblx0fVxufSk7XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHRjc3M6IGZ1bmN0aW9uKCBuYW1lLCB2YWx1ZSApIHtcblx0XHRyZXR1cm4gYWNjZXNzKCB0aGlzLCBmdW5jdGlvbiggZWxlbSwgbmFtZSwgdmFsdWUgKSB7XG5cdFx0XHR2YXIgc3R5bGVzLCBsZW4sXG5cdFx0XHRcdG1hcCA9IHt9LFxuXHRcdFx0XHRpID0gMDtcblxuXHRcdFx0aWYgKCBqUXVlcnkuaXNBcnJheSggbmFtZSApICkge1xuXHRcdFx0XHRzdHlsZXMgPSBnZXRTdHlsZXMoIGVsZW0gKTtcblx0XHRcdFx0bGVuID0gbmFtZS5sZW5ndGg7XG5cblx0XHRcdFx0Zm9yICggOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRcdFx0bWFwWyBuYW1lWyBpIF0gXSA9IGpRdWVyeS5jc3MoIGVsZW0sIG5hbWVbIGkgXSwgZmFsc2UsIHN0eWxlcyApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0cmV0dXJuIG1hcDtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuIHZhbHVlICE9PSB1bmRlZmluZWQgP1xuXHRcdFx0XHRqUXVlcnkuc3R5bGUoIGVsZW0sIG5hbWUsIHZhbHVlICkgOlxuXHRcdFx0XHRqUXVlcnkuY3NzKCBlbGVtLCBuYW1lICk7XG5cdFx0fSwgbmFtZSwgdmFsdWUsIGFyZ3VtZW50cy5sZW5ndGggPiAxICk7XG5cdH0sXG5cdHNob3c6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiBzaG93SGlkZSggdGhpcywgdHJ1ZSApO1xuXHR9LFxuXHRoaWRlOiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gc2hvd0hpZGUoIHRoaXMgKTtcblx0fSxcblx0dG9nZ2xlOiBmdW5jdGlvbiggc3RhdGUgKSB7XG5cdFx0aWYgKCB0eXBlb2Ygc3RhdGUgPT09IFwiYm9vbGVhblwiICkge1xuXHRcdFx0cmV0dXJuIHN0YXRlID8gdGhpcy5zaG93KCkgOiB0aGlzLmhpZGUoKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCkge1xuXHRcdFx0aWYgKCBpc0hpZGRlbiggdGhpcyApICkge1xuXHRcdFx0XHRqUXVlcnkoIHRoaXMgKS5zaG93KCk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRqUXVlcnkoIHRoaXMgKS5oaWRlKCk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cbn0pO1xuXG5cbmZ1bmN0aW9uIFR3ZWVuKCBlbGVtLCBvcHRpb25zLCBwcm9wLCBlbmQsIGVhc2luZyApIHtcblx0cmV0dXJuIG5ldyBUd2Vlbi5wcm90b3R5cGUuaW5pdCggZWxlbSwgb3B0aW9ucywgcHJvcCwgZW5kLCBlYXNpbmcgKTtcbn1cbmpRdWVyeS5Ud2VlbiA9IFR3ZWVuO1xuXG5Ud2Vlbi5wcm90b3R5cGUgPSB7XG5cdGNvbnN0cnVjdG9yOiBUd2Vlbixcblx0aW5pdDogZnVuY3Rpb24oIGVsZW0sIG9wdGlvbnMsIHByb3AsIGVuZCwgZWFzaW5nLCB1bml0ICkge1xuXHRcdHRoaXMuZWxlbSA9IGVsZW07XG5cdFx0dGhpcy5wcm9wID0gcHJvcDtcblx0XHR0aGlzLmVhc2luZyA9IGVhc2luZyB8fCBcInN3aW5nXCI7XG5cdFx0dGhpcy5vcHRpb25zID0gb3B0aW9ucztcblx0XHR0aGlzLnN0YXJ0ID0gdGhpcy5ub3cgPSB0aGlzLmN1cigpO1xuXHRcdHRoaXMuZW5kID0gZW5kO1xuXHRcdHRoaXMudW5pdCA9IHVuaXQgfHwgKCBqUXVlcnkuY3NzTnVtYmVyWyBwcm9wIF0gPyBcIlwiIDogXCJweFwiICk7XG5cdH0sXG5cdGN1cjogZnVuY3Rpb24oKSB7XG5cdFx0dmFyIGhvb2tzID0gVHdlZW4ucHJvcEhvb2tzWyB0aGlzLnByb3AgXTtcblxuXHRcdHJldHVybiBob29rcyAmJiBob29rcy5nZXQgP1xuXHRcdFx0aG9va3MuZ2V0KCB0aGlzICkgOlxuXHRcdFx0VHdlZW4ucHJvcEhvb2tzLl9kZWZhdWx0LmdldCggdGhpcyApO1xuXHR9LFxuXHRydW46IGZ1bmN0aW9uKCBwZXJjZW50ICkge1xuXHRcdHZhciBlYXNlZCxcblx0XHRcdGhvb2tzID0gVHdlZW4ucHJvcEhvb2tzWyB0aGlzLnByb3AgXTtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLmR1cmF0aW9uICkge1xuXHRcdFx0dGhpcy5wb3MgPSBlYXNlZCA9IGpRdWVyeS5lYXNpbmdbIHRoaXMuZWFzaW5nIF0oXG5cdFx0XHRcdHBlcmNlbnQsIHRoaXMub3B0aW9ucy5kdXJhdGlvbiAqIHBlcmNlbnQsIDAsIDEsIHRoaXMub3B0aW9ucy5kdXJhdGlvblxuXHRcdFx0KTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy5wb3MgPSBlYXNlZCA9IHBlcmNlbnQ7XG5cdFx0fVxuXHRcdHRoaXMubm93ID0gKCB0aGlzLmVuZCAtIHRoaXMuc3RhcnQgKSAqIGVhc2VkICsgdGhpcy5zdGFydDtcblxuXHRcdGlmICggdGhpcy5vcHRpb25zLnN0ZXAgKSB7XG5cdFx0XHR0aGlzLm9wdGlvbnMuc3RlcC5jYWxsKCB0aGlzLmVsZW0sIHRoaXMubm93LCB0aGlzICk7XG5cdFx0fVxuXG5cdFx0aWYgKCBob29rcyAmJiBob29rcy5zZXQgKSB7XG5cdFx0XHRob29rcy5zZXQoIHRoaXMgKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0VHdlZW4ucHJvcEhvb2tzLl9kZWZhdWx0LnNldCggdGhpcyApO1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fVxufTtcblxuVHdlZW4ucHJvdG90eXBlLmluaXQucHJvdG90eXBlID0gVHdlZW4ucHJvdG90eXBlO1xuXG5Ud2Vlbi5wcm9wSG9va3MgPSB7XG5cdF9kZWZhdWx0OiB7XG5cdFx0Z2V0OiBmdW5jdGlvbiggdHdlZW4gKSB7XG5cdFx0XHR2YXIgcmVzdWx0O1xuXG5cdFx0XHRpZiAoIHR3ZWVuLmVsZW1bIHR3ZWVuLnByb3AgXSAhPSBudWxsICYmXG5cdFx0XHRcdCghdHdlZW4uZWxlbS5zdHlsZSB8fCB0d2Vlbi5lbGVtLnN0eWxlWyB0d2Vlbi5wcm9wIF0gPT0gbnVsbCkgKSB7XG5cdFx0XHRcdHJldHVybiB0d2Vlbi5lbGVtWyB0d2Vlbi5wcm9wIF07XG5cdFx0XHR9XG5cblx0XHRcdC8vIFBhc3NpbmcgYW4gZW1wdHkgc3RyaW5nIGFzIGEgM3JkIHBhcmFtZXRlciB0byAuY3NzIHdpbGwgYXV0b21hdGljYWxseVxuXHRcdFx0Ly8gYXR0ZW1wdCBhIHBhcnNlRmxvYXQgYW5kIGZhbGxiYWNrIHRvIGEgc3RyaW5nIGlmIHRoZSBwYXJzZSBmYWlscy5cblx0XHRcdC8vIFNpbXBsZSB2YWx1ZXMgc3VjaCBhcyBcIjEwcHhcIiBhcmUgcGFyc2VkIHRvIEZsb2F0O1xuXHRcdFx0Ly8gY29tcGxleCB2YWx1ZXMgc3VjaCBhcyBcInJvdGF0ZSgxcmFkKVwiIGFyZSByZXR1cm5lZCBhcy1pcy5cblx0XHRcdHJlc3VsdCA9IGpRdWVyeS5jc3MoIHR3ZWVuLmVsZW0sIHR3ZWVuLnByb3AsIFwiXCIgKTtcblx0XHRcdC8vIEVtcHR5IHN0cmluZ3MsIG51bGwsIHVuZGVmaW5lZCBhbmQgXCJhdXRvXCIgYXJlIGNvbnZlcnRlZCB0byAwLlxuXHRcdFx0cmV0dXJuICFyZXN1bHQgfHwgcmVzdWx0ID09PSBcImF1dG9cIiA/IDAgOiByZXN1bHQ7XG5cdFx0fSxcblx0XHRzZXQ6IGZ1bmN0aW9uKCB0d2VlbiApIHtcblx0XHRcdC8vIFVzZSBzdGVwIGhvb2sgZm9yIGJhY2sgY29tcGF0LlxuXHRcdFx0Ly8gVXNlIGNzc0hvb2sgaWYgaXRzIHRoZXJlLlxuXHRcdFx0Ly8gVXNlIC5zdHlsZSBpZiBhdmFpbGFibGUgYW5kIHVzZSBwbGFpbiBwcm9wZXJ0aWVzIHdoZXJlIGF2YWlsYWJsZS5cblx0XHRcdGlmICggalF1ZXJ5LmZ4LnN0ZXBbIHR3ZWVuLnByb3AgXSApIHtcblx0XHRcdFx0alF1ZXJ5LmZ4LnN0ZXBbIHR3ZWVuLnByb3AgXSggdHdlZW4gKTtcblx0XHRcdH0gZWxzZSBpZiAoIHR3ZWVuLmVsZW0uc3R5bGUgJiYgKCB0d2Vlbi5lbGVtLnN0eWxlWyBqUXVlcnkuY3NzUHJvcHNbIHR3ZWVuLnByb3AgXSBdICE9IG51bGwgfHwgalF1ZXJ5LmNzc0hvb2tzWyB0d2Vlbi5wcm9wIF0gKSApIHtcblx0XHRcdFx0alF1ZXJ5LnN0eWxlKCB0d2Vlbi5lbGVtLCB0d2Vlbi5wcm9wLCB0d2Vlbi5ub3cgKyB0d2Vlbi51bml0ICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0d2Vlbi5lbGVtWyB0d2Vlbi5wcm9wIF0gPSB0d2Vlbi5ub3c7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG59O1xuXG4vLyBTdXBwb3J0OiBJRTlcbi8vIFBhbmljIGJhc2VkIGFwcHJvYWNoIHRvIHNldHRpbmcgdGhpbmdzIG9uIGRpc2Nvbm5lY3RlZCBub2Rlc1xuVHdlZW4ucHJvcEhvb2tzLnNjcm9sbFRvcCA9IFR3ZWVuLnByb3BIb29rcy5zY3JvbGxMZWZ0ID0ge1xuXHRzZXQ6IGZ1bmN0aW9uKCB0d2VlbiApIHtcblx0XHRpZiAoIHR3ZWVuLmVsZW0ubm9kZVR5cGUgJiYgdHdlZW4uZWxlbS5wYXJlbnROb2RlICkge1xuXHRcdFx0dHdlZW4uZWxlbVsgdHdlZW4ucHJvcCBdID0gdHdlZW4ubm93O1xuXHRcdH1cblx0fVxufTtcblxualF1ZXJ5LmVhc2luZyA9IHtcblx0bGluZWFyOiBmdW5jdGlvbiggcCApIHtcblx0XHRyZXR1cm4gcDtcblx0fSxcblx0c3dpbmc6IGZ1bmN0aW9uKCBwICkge1xuXHRcdHJldHVybiAwLjUgLSBNYXRoLmNvcyggcCAqIE1hdGguUEkgKSAvIDI7XG5cdH1cbn07XG5cbmpRdWVyeS5meCA9IFR3ZWVuLnByb3RvdHlwZS5pbml0O1xuXG4vLyBCYWNrIENvbXBhdCA8MS44IGV4dGVuc2lvbiBwb2ludFxualF1ZXJ5LmZ4LnN0ZXAgPSB7fTtcblxuXG5cblxudmFyXG5cdGZ4Tm93LCB0aW1lcklkLFxuXHRyZnh0eXBlcyA9IC9eKD86dG9nZ2xlfHNob3d8aGlkZSkkLyxcblx0cmZ4bnVtID0gbmV3IFJlZ0V4cCggXCJeKD86KFsrLV0pPXwpKFwiICsgcG51bSArIFwiKShbYS16JV0qKSRcIiwgXCJpXCIgKSxcblx0cnJ1biA9IC9xdWV1ZUhvb2tzJC8sXG5cdGFuaW1hdGlvblByZWZpbHRlcnMgPSBbIGRlZmF1bHRQcmVmaWx0ZXIgXSxcblx0dHdlZW5lcnMgPSB7XG5cdFx0XCIqXCI6IFsgZnVuY3Rpb24oIHByb3AsIHZhbHVlICkge1xuXHRcdFx0dmFyIHR3ZWVuID0gdGhpcy5jcmVhdGVUd2VlbiggcHJvcCwgdmFsdWUgKSxcblx0XHRcdFx0dGFyZ2V0ID0gdHdlZW4uY3VyKCksXG5cdFx0XHRcdHBhcnRzID0gcmZ4bnVtLmV4ZWMoIHZhbHVlICksXG5cdFx0XHRcdHVuaXQgPSBwYXJ0cyAmJiBwYXJ0c1sgMyBdIHx8ICggalF1ZXJ5LmNzc051bWJlclsgcHJvcCBdID8gXCJcIiA6IFwicHhcIiApLFxuXG5cdFx0XHRcdC8vIFN0YXJ0aW5nIHZhbHVlIGNvbXB1dGF0aW9uIGlzIHJlcXVpcmVkIGZvciBwb3RlbnRpYWwgdW5pdCBtaXNtYXRjaGVzXG5cdFx0XHRcdHN0YXJ0ID0gKCBqUXVlcnkuY3NzTnVtYmVyWyBwcm9wIF0gfHwgdW5pdCAhPT0gXCJweFwiICYmICt0YXJnZXQgKSAmJlxuXHRcdFx0XHRcdHJmeG51bS5leGVjKCBqUXVlcnkuY3NzKCB0d2Vlbi5lbGVtLCBwcm9wICkgKSxcblx0XHRcdFx0c2NhbGUgPSAxLFxuXHRcdFx0XHRtYXhJdGVyYXRpb25zID0gMjA7XG5cblx0XHRcdGlmICggc3RhcnQgJiYgc3RhcnRbIDMgXSAhPT0gdW5pdCApIHtcblx0XHRcdFx0Ly8gVHJ1c3QgdW5pdHMgcmVwb3J0ZWQgYnkgalF1ZXJ5LmNzc1xuXHRcdFx0XHR1bml0ID0gdW5pdCB8fCBzdGFydFsgMyBdO1xuXG5cdFx0XHRcdC8vIE1ha2Ugc3VyZSB3ZSB1cGRhdGUgdGhlIHR3ZWVuIHByb3BlcnRpZXMgbGF0ZXIgb25cblx0XHRcdFx0cGFydHMgPSBwYXJ0cyB8fCBbXTtcblxuXHRcdFx0XHQvLyBJdGVyYXRpdmVseSBhcHByb3hpbWF0ZSBmcm9tIGEgbm9uemVybyBzdGFydGluZyBwb2ludFxuXHRcdFx0XHRzdGFydCA9ICt0YXJnZXQgfHwgMTtcblxuXHRcdFx0XHRkbyB7XG5cdFx0XHRcdFx0Ly8gSWYgcHJldmlvdXMgaXRlcmF0aW9uIHplcm9lZCBvdXQsIGRvdWJsZSB1bnRpbCB3ZSBnZXQgKnNvbWV0aGluZyouXG5cdFx0XHRcdFx0Ly8gVXNlIHN0cmluZyBmb3IgZG91Ymxpbmcgc28gd2UgZG9uJ3QgYWNjaWRlbnRhbGx5IHNlZSBzY2FsZSBhcyB1bmNoYW5nZWQgYmVsb3dcblx0XHRcdFx0XHRzY2FsZSA9IHNjYWxlIHx8IFwiLjVcIjtcblxuXHRcdFx0XHRcdC8vIEFkanVzdCBhbmQgYXBwbHlcblx0XHRcdFx0XHRzdGFydCA9IHN0YXJ0IC8gc2NhbGU7XG5cdFx0XHRcdFx0alF1ZXJ5LnN0eWxlKCB0d2Vlbi5lbGVtLCBwcm9wLCBzdGFydCArIHVuaXQgKTtcblxuXHRcdFx0XHQvLyBVcGRhdGUgc2NhbGUsIHRvbGVyYXRpbmcgemVybyBvciBOYU4gZnJvbSB0d2Vlbi5jdXIoKSxcblx0XHRcdFx0Ly8gYnJlYWsgdGhlIGxvb3AgaWYgc2NhbGUgaXMgdW5jaGFuZ2VkIG9yIHBlcmZlY3QsIG9yIGlmIHdlJ3ZlIGp1c3QgaGFkIGVub3VnaFxuXHRcdFx0XHR9IHdoaWxlICggc2NhbGUgIT09IChzY2FsZSA9IHR3ZWVuLmN1cigpIC8gdGFyZ2V0KSAmJiBzY2FsZSAhPT0gMSAmJiAtLW1heEl0ZXJhdGlvbnMgKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gVXBkYXRlIHR3ZWVuIHByb3BlcnRpZXNcblx0XHRcdGlmICggcGFydHMgKSB7XG5cdFx0XHRcdHN0YXJ0ID0gdHdlZW4uc3RhcnQgPSArc3RhcnQgfHwgK3RhcmdldCB8fCAwO1xuXHRcdFx0XHR0d2Vlbi51bml0ID0gdW5pdDtcblx0XHRcdFx0Ly8gSWYgYSArPS8tPSB0b2tlbiB3YXMgcHJvdmlkZWQsIHdlJ3JlIGRvaW5nIGEgcmVsYXRpdmUgYW5pbWF0aW9uXG5cdFx0XHRcdHR3ZWVuLmVuZCA9IHBhcnRzWyAxIF0gP1xuXHRcdFx0XHRcdHN0YXJ0ICsgKCBwYXJ0c1sgMSBdICsgMSApICogcGFydHNbIDIgXSA6XG5cdFx0XHRcdFx0K3BhcnRzWyAyIF07XG5cdFx0XHR9XG5cblx0XHRcdHJldHVybiB0d2Vlbjtcblx0XHR9IF1cblx0fTtcblxuLy8gQW5pbWF0aW9ucyBjcmVhdGVkIHN5bmNocm9ub3VzbHkgd2lsbCBydW4gc3luY2hyb25vdXNseVxuZnVuY3Rpb24gY3JlYXRlRnhOb3coKSB7XG5cdHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG5cdFx0ZnhOb3cgPSB1bmRlZmluZWQ7XG5cdH0pO1xuXHRyZXR1cm4gKCBmeE5vdyA9IGpRdWVyeS5ub3coKSApO1xufVxuXG4vLyBHZW5lcmF0ZSBwYXJhbWV0ZXJzIHRvIGNyZWF0ZSBhIHN0YW5kYXJkIGFuaW1hdGlvblxuZnVuY3Rpb24gZ2VuRngoIHR5cGUsIGluY2x1ZGVXaWR0aCApIHtcblx0dmFyIHdoaWNoLFxuXHRcdGkgPSAwLFxuXHRcdGF0dHJzID0geyBoZWlnaHQ6IHR5cGUgfTtcblxuXHQvLyBJZiB3ZSBpbmNsdWRlIHdpZHRoLCBzdGVwIHZhbHVlIGlzIDEgdG8gZG8gYWxsIGNzc0V4cGFuZCB2YWx1ZXMsXG5cdC8vIG90aGVyd2lzZSBzdGVwIHZhbHVlIGlzIDIgdG8gc2tpcCBvdmVyIExlZnQgYW5kIFJpZ2h0XG5cdGluY2x1ZGVXaWR0aCA9IGluY2x1ZGVXaWR0aCA/IDEgOiAwO1xuXHRmb3IgKCA7IGkgPCA0IDsgaSArPSAyIC0gaW5jbHVkZVdpZHRoICkge1xuXHRcdHdoaWNoID0gY3NzRXhwYW5kWyBpIF07XG5cdFx0YXR0cnNbIFwibWFyZ2luXCIgKyB3aGljaCBdID0gYXR0cnNbIFwicGFkZGluZ1wiICsgd2hpY2ggXSA9IHR5cGU7XG5cdH1cblxuXHRpZiAoIGluY2x1ZGVXaWR0aCApIHtcblx0XHRhdHRycy5vcGFjaXR5ID0gYXR0cnMud2lkdGggPSB0eXBlO1xuXHR9XG5cblx0cmV0dXJuIGF0dHJzO1xufVxuXG5mdW5jdGlvbiBjcmVhdGVUd2VlbiggdmFsdWUsIHByb3AsIGFuaW1hdGlvbiApIHtcblx0dmFyIHR3ZWVuLFxuXHRcdGNvbGxlY3Rpb24gPSAoIHR3ZWVuZXJzWyBwcm9wIF0gfHwgW10gKS5jb25jYXQoIHR3ZWVuZXJzWyBcIipcIiBdICksXG5cdFx0aW5kZXggPSAwLFxuXHRcdGxlbmd0aCA9IGNvbGxlY3Rpb24ubGVuZ3RoO1xuXHRmb3IgKCA7IGluZGV4IDwgbGVuZ3RoOyBpbmRleCsrICkge1xuXHRcdGlmICggKHR3ZWVuID0gY29sbGVjdGlvblsgaW5kZXggXS5jYWxsKCBhbmltYXRpb24sIHByb3AsIHZhbHVlICkpICkge1xuXG5cdFx0XHQvLyBXZSdyZSBkb25lIHdpdGggdGhpcyBwcm9wZXJ0eVxuXHRcdFx0cmV0dXJuIHR3ZWVuO1xuXHRcdH1cblx0fVxufVxuXG5mdW5jdGlvbiBkZWZhdWx0UHJlZmlsdGVyKCBlbGVtLCBwcm9wcywgb3B0cyApIHtcblx0LyoganNoaW50IHZhbGlkdGhpczogdHJ1ZSAqL1xuXHR2YXIgcHJvcCwgdmFsdWUsIHRvZ2dsZSwgdHdlZW4sIGhvb2tzLCBvbGRmaXJlLCBkaXNwbGF5LCBjaGVja0Rpc3BsYXksXG5cdFx0YW5pbSA9IHRoaXMsXG5cdFx0b3JpZyA9IHt9LFxuXHRcdHN0eWxlID0gZWxlbS5zdHlsZSxcblx0XHRoaWRkZW4gPSBlbGVtLm5vZGVUeXBlICYmIGlzSGlkZGVuKCBlbGVtICksXG5cdFx0ZGF0YVNob3cgPSBkYXRhX3ByaXYuZ2V0KCBlbGVtLCBcImZ4c2hvd1wiICk7XG5cblx0Ly8gSGFuZGxlIHF1ZXVlOiBmYWxzZSBwcm9taXNlc1xuXHRpZiAoICFvcHRzLnF1ZXVlICkge1xuXHRcdGhvb2tzID0galF1ZXJ5Ll9xdWV1ZUhvb2tzKCBlbGVtLCBcImZ4XCIgKTtcblx0XHRpZiAoIGhvb2tzLnVucXVldWVkID09IG51bGwgKSB7XG5cdFx0XHRob29rcy51bnF1ZXVlZCA9IDA7XG5cdFx0XHRvbGRmaXJlID0gaG9va3MuZW1wdHkuZmlyZTtcblx0XHRcdGhvb2tzLmVtcHR5LmZpcmUgPSBmdW5jdGlvbigpIHtcblx0XHRcdFx0aWYgKCAhaG9va3MudW5xdWV1ZWQgKSB7XG5cdFx0XHRcdFx0b2xkZmlyZSgpO1xuXHRcdFx0XHR9XG5cdFx0XHR9O1xuXHRcdH1cblx0XHRob29rcy51bnF1ZXVlZCsrO1xuXG5cdFx0YW5pbS5hbHdheXMoZnVuY3Rpb24oKSB7XG5cdFx0XHQvLyBFbnN1cmUgdGhlIGNvbXBsZXRlIGhhbmRsZXIgaXMgY2FsbGVkIGJlZm9yZSB0aGlzIGNvbXBsZXRlc1xuXHRcdFx0YW5pbS5hbHdheXMoZnVuY3Rpb24oKSB7XG5cdFx0XHRcdGhvb2tzLnVucXVldWVkLS07XG5cdFx0XHRcdGlmICggIWpRdWVyeS5xdWV1ZSggZWxlbSwgXCJmeFwiICkubGVuZ3RoICkge1xuXHRcdFx0XHRcdGhvb2tzLmVtcHR5LmZpcmUoKTtcblx0XHRcdFx0fVxuXHRcdFx0fSk7XG5cdFx0fSk7XG5cdH1cblxuXHQvLyBIZWlnaHQvd2lkdGggb3ZlcmZsb3cgcGFzc1xuXHRpZiAoIGVsZW0ubm9kZVR5cGUgPT09IDEgJiYgKCBcImhlaWdodFwiIGluIHByb3BzIHx8IFwid2lkdGhcIiBpbiBwcm9wcyApICkge1xuXHRcdC8vIE1ha2Ugc3VyZSB0aGF0IG5vdGhpbmcgc25lYWtzIG91dFxuXHRcdC8vIFJlY29yZCBhbGwgMyBvdmVyZmxvdyBhdHRyaWJ1dGVzIGJlY2F1c2UgSUU5LTEwIGRvIG5vdFxuXHRcdC8vIGNoYW5nZSB0aGUgb3ZlcmZsb3cgYXR0cmlidXRlIHdoZW4gb3ZlcmZsb3dYIGFuZFxuXHRcdC8vIG92ZXJmbG93WSBhcmUgc2V0IHRvIHRoZSBzYW1lIHZhbHVlXG5cdFx0b3B0cy5vdmVyZmxvdyA9IFsgc3R5bGUub3ZlcmZsb3csIHN0eWxlLm92ZXJmbG93WCwgc3R5bGUub3ZlcmZsb3dZIF07XG5cblx0XHQvLyBTZXQgZGlzcGxheSBwcm9wZXJ0eSB0byBpbmxpbmUtYmxvY2sgZm9yIGhlaWdodC93aWR0aFxuXHRcdC8vIGFuaW1hdGlvbnMgb24gaW5saW5lIGVsZW1lbnRzIHRoYXQgYXJlIGhhdmluZyB3aWR0aC9oZWlnaHQgYW5pbWF0ZWRcblx0XHRkaXNwbGF5ID0galF1ZXJ5LmNzcyggZWxlbSwgXCJkaXNwbGF5XCIgKTtcblxuXHRcdC8vIFRlc3QgZGVmYXVsdCBkaXNwbGF5IGlmIGRpc3BsYXkgaXMgY3VycmVudGx5IFwibm9uZVwiXG5cdFx0Y2hlY2tEaXNwbGF5ID0gZGlzcGxheSA9PT0gXCJub25lXCIgP1xuXHRcdFx0ZGF0YV9wcml2LmdldCggZWxlbSwgXCJvbGRkaXNwbGF5XCIgKSB8fCBkZWZhdWx0RGlzcGxheSggZWxlbS5ub2RlTmFtZSApIDogZGlzcGxheTtcblxuXHRcdGlmICggY2hlY2tEaXNwbGF5ID09PSBcImlubGluZVwiICYmIGpRdWVyeS5jc3MoIGVsZW0sIFwiZmxvYXRcIiApID09PSBcIm5vbmVcIiApIHtcblx0XHRcdHN0eWxlLmRpc3BsYXkgPSBcImlubGluZS1ibG9ja1wiO1xuXHRcdH1cblx0fVxuXG5cdGlmICggb3B0cy5vdmVyZmxvdyApIHtcblx0XHRzdHlsZS5vdmVyZmxvdyA9IFwiaGlkZGVuXCI7XG5cdFx0YW5pbS5hbHdheXMoZnVuY3Rpb24oKSB7XG5cdFx0XHRzdHlsZS5vdmVyZmxvdyA9IG9wdHMub3ZlcmZsb3dbIDAgXTtcblx0XHRcdHN0eWxlLm92ZXJmbG93WCA9IG9wdHMub3ZlcmZsb3dbIDEgXTtcblx0XHRcdHN0eWxlLm92ZXJmbG93WSA9IG9wdHMub3ZlcmZsb3dbIDIgXTtcblx0XHR9KTtcblx0fVxuXG5cdC8vIHNob3cvaGlkZSBwYXNzXG5cdGZvciAoIHByb3AgaW4gcHJvcHMgKSB7XG5cdFx0dmFsdWUgPSBwcm9wc1sgcHJvcCBdO1xuXHRcdGlmICggcmZ4dHlwZXMuZXhlYyggdmFsdWUgKSApIHtcblx0XHRcdGRlbGV0ZSBwcm9wc1sgcHJvcCBdO1xuXHRcdFx0dG9nZ2xlID0gdG9nZ2xlIHx8IHZhbHVlID09PSBcInRvZ2dsZVwiO1xuXHRcdFx0aWYgKCB2YWx1ZSA9PT0gKCBoaWRkZW4gPyBcImhpZGVcIiA6IFwic2hvd1wiICkgKSB7XG5cblx0XHRcdFx0Ly8gSWYgdGhlcmUgaXMgZGF0YVNob3cgbGVmdCBvdmVyIGZyb20gYSBzdG9wcGVkIGhpZGUgb3Igc2hvdyBhbmQgd2UgYXJlIGdvaW5nIHRvIHByb2NlZWQgd2l0aCBzaG93LCB3ZSBzaG91bGQgcHJldGVuZCB0byBiZSBoaWRkZW5cblx0XHRcdFx0aWYgKCB2YWx1ZSA9PT0gXCJzaG93XCIgJiYgZGF0YVNob3cgJiYgZGF0YVNob3dbIHByb3AgXSAhPT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHRcdGhpZGRlbiA9IHRydWU7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdG9yaWdbIHByb3AgXSA9IGRhdGFTaG93ICYmIGRhdGFTaG93WyBwcm9wIF0gfHwgalF1ZXJ5LnN0eWxlKCBlbGVtLCBwcm9wICk7XG5cblx0XHQvLyBBbnkgbm9uLWZ4IHZhbHVlIHN0b3BzIHVzIGZyb20gcmVzdG9yaW5nIHRoZSBvcmlnaW5hbCBkaXNwbGF5IHZhbHVlXG5cdFx0fSBlbHNlIHtcblx0XHRcdGRpc3BsYXkgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHR9XG5cblx0aWYgKCAhalF1ZXJ5LmlzRW1wdHlPYmplY3QoIG9yaWcgKSApIHtcblx0XHRpZiAoIGRhdGFTaG93ICkge1xuXHRcdFx0aWYgKCBcImhpZGRlblwiIGluIGRhdGFTaG93ICkge1xuXHRcdFx0XHRoaWRkZW4gPSBkYXRhU2hvdy5oaWRkZW47XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGRhdGFTaG93ID0gZGF0YV9wcml2LmFjY2VzcyggZWxlbSwgXCJmeHNob3dcIiwge30gKTtcblx0XHR9XG5cblx0XHQvLyBTdG9yZSBzdGF0ZSBpZiBpdHMgdG9nZ2xlIC0gZW5hYmxlcyAuc3RvcCgpLnRvZ2dsZSgpIHRvIFwicmV2ZXJzZVwiXG5cdFx0aWYgKCB0b2dnbGUgKSB7XG5cdFx0XHRkYXRhU2hvdy5oaWRkZW4gPSAhaGlkZGVuO1xuXHRcdH1cblx0XHRpZiAoIGhpZGRlbiApIHtcblx0XHRcdGpRdWVyeSggZWxlbSApLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0YW5pbS5kb25lKGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRqUXVlcnkoIGVsZW0gKS5oaWRlKCk7XG5cdFx0XHR9KTtcblx0XHR9XG5cdFx0YW5pbS5kb25lKGZ1bmN0aW9uKCkge1xuXHRcdFx0dmFyIHByb3A7XG5cblx0XHRcdGRhdGFfcHJpdi5yZW1vdmUoIGVsZW0sIFwiZnhzaG93XCIgKTtcblx0XHRcdGZvciAoIHByb3AgaW4gb3JpZyApIHtcblx0XHRcdFx0alF1ZXJ5LnN0eWxlKCBlbGVtLCBwcm9wLCBvcmlnWyBwcm9wIF0gKTtcblx0XHRcdH1cblx0XHR9KTtcblx0XHRmb3IgKCBwcm9wIGluIG9yaWcgKSB7XG5cdFx0XHR0d2VlbiA9IGNyZWF0ZVR3ZWVuKCBoaWRkZW4gPyBkYXRhU2hvd1sgcHJvcCBdIDogMCwgcHJvcCwgYW5pbSApO1xuXG5cdFx0XHRpZiAoICEoIHByb3AgaW4gZGF0YVNob3cgKSApIHtcblx0XHRcdFx0ZGF0YVNob3dbIHByb3AgXSA9IHR3ZWVuLnN0YXJ0O1xuXHRcdFx0XHRpZiAoIGhpZGRlbiApIHtcblx0XHRcdFx0XHR0d2Vlbi5lbmQgPSB0d2Vlbi5zdGFydDtcblx0XHRcdFx0XHR0d2Vlbi5zdGFydCA9IHByb3AgPT09IFwid2lkdGhcIiB8fCBwcm9wID09PSBcImhlaWdodFwiID8gMSA6IDA7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0Ly8gSWYgdGhpcyBpcyBhIG5vb3AgbGlrZSAuaGlkZSgpLmhpZGUoKSwgcmVzdG9yZSBhbiBvdmVyd3JpdHRlbiBkaXNwbGF5IHZhbHVlXG5cdH0gZWxzZSBpZiAoIChkaXNwbGF5ID09PSBcIm5vbmVcIiA/IGRlZmF1bHREaXNwbGF5KCBlbGVtLm5vZGVOYW1lICkgOiBkaXNwbGF5KSA9PT0gXCJpbmxpbmVcIiApIHtcblx0XHRzdHlsZS5kaXNwbGF5ID0gZGlzcGxheTtcblx0fVxufVxuXG5mdW5jdGlvbiBwcm9wRmlsdGVyKCBwcm9wcywgc3BlY2lhbEVhc2luZyApIHtcblx0dmFyIGluZGV4LCBuYW1lLCBlYXNpbmcsIHZhbHVlLCBob29rcztcblxuXHQvLyBjYW1lbENhc2UsIHNwZWNpYWxFYXNpbmcgYW5kIGV4cGFuZCBjc3NIb29rIHBhc3Ncblx0Zm9yICggaW5kZXggaW4gcHJvcHMgKSB7XG5cdFx0bmFtZSA9IGpRdWVyeS5jYW1lbENhc2UoIGluZGV4ICk7XG5cdFx0ZWFzaW5nID0gc3BlY2lhbEVhc2luZ1sgbmFtZSBdO1xuXHRcdHZhbHVlID0gcHJvcHNbIGluZGV4IF07XG5cdFx0aWYgKCBqUXVlcnkuaXNBcnJheSggdmFsdWUgKSApIHtcblx0XHRcdGVhc2luZyA9IHZhbHVlWyAxIF07XG5cdFx0XHR2YWx1ZSA9IHByb3BzWyBpbmRleCBdID0gdmFsdWVbIDAgXTtcblx0XHR9XG5cblx0XHRpZiAoIGluZGV4ICE9PSBuYW1lICkge1xuXHRcdFx0cHJvcHNbIG5hbWUgXSA9IHZhbHVlO1xuXHRcdFx0ZGVsZXRlIHByb3BzWyBpbmRleCBdO1xuXHRcdH1cblxuXHRcdGhvb2tzID0galF1ZXJ5LmNzc0hvb2tzWyBuYW1lIF07XG5cdFx0aWYgKCBob29rcyAmJiBcImV4cGFuZFwiIGluIGhvb2tzICkge1xuXHRcdFx0dmFsdWUgPSBob29rcy5leHBhbmQoIHZhbHVlICk7XG5cdFx0XHRkZWxldGUgcHJvcHNbIG5hbWUgXTtcblxuXHRcdFx0Ly8gTm90IHF1aXRlICQuZXh0ZW5kLCB0aGlzIHdvbid0IG92ZXJ3cml0ZSBleGlzdGluZyBrZXlzLlxuXHRcdFx0Ly8gUmV1c2luZyAnaW5kZXgnIGJlY2F1c2Ugd2UgaGF2ZSB0aGUgY29ycmVjdCBcIm5hbWVcIlxuXHRcdFx0Zm9yICggaW5kZXggaW4gdmFsdWUgKSB7XG5cdFx0XHRcdGlmICggISggaW5kZXggaW4gcHJvcHMgKSApIHtcblx0XHRcdFx0XHRwcm9wc1sgaW5kZXggXSA9IHZhbHVlWyBpbmRleCBdO1xuXHRcdFx0XHRcdHNwZWNpYWxFYXNpbmdbIGluZGV4IF0gPSBlYXNpbmc7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0c3BlY2lhbEVhc2luZ1sgbmFtZSBdID0gZWFzaW5nO1xuXHRcdH1cblx0fVxufVxuXG5mdW5jdGlvbiBBbmltYXRpb24oIGVsZW0sIHByb3BlcnRpZXMsIG9wdGlvbnMgKSB7XG5cdHZhciByZXN1bHQsXG5cdFx0c3RvcHBlZCxcblx0XHRpbmRleCA9IDAsXG5cdFx0bGVuZ3RoID0gYW5pbWF0aW9uUHJlZmlsdGVycy5sZW5ndGgsXG5cdFx0ZGVmZXJyZWQgPSBqUXVlcnkuRGVmZXJyZWQoKS5hbHdheXMoIGZ1bmN0aW9uKCkge1xuXHRcdFx0Ly8gRG9uJ3QgbWF0Y2ggZWxlbSBpbiB0aGUgOmFuaW1hdGVkIHNlbGVjdG9yXG5cdFx0XHRkZWxldGUgdGljay5lbGVtO1xuXHRcdH0pLFxuXHRcdHRpY2sgPSBmdW5jdGlvbigpIHtcblx0XHRcdGlmICggc3RvcHBlZCApIHtcblx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0fVxuXHRcdFx0dmFyIGN1cnJlbnRUaW1lID0gZnhOb3cgfHwgY3JlYXRlRnhOb3coKSxcblx0XHRcdFx0cmVtYWluaW5nID0gTWF0aC5tYXgoIDAsIGFuaW1hdGlvbi5zdGFydFRpbWUgKyBhbmltYXRpb24uZHVyYXRpb24gLSBjdXJyZW50VGltZSApLFxuXHRcdFx0XHQvLyBTdXBwb3J0OiBBbmRyb2lkIDIuM1xuXHRcdFx0XHQvLyBBcmNoYWljIGNyYXNoIGJ1ZyB3b24ndCBhbGxvdyB1cyB0byB1c2UgYDEgLSAoIDAuNSB8fCAwIClgICgjMTI0OTcpXG5cdFx0XHRcdHRlbXAgPSByZW1haW5pbmcgLyBhbmltYXRpb24uZHVyYXRpb24gfHwgMCxcblx0XHRcdFx0cGVyY2VudCA9IDEgLSB0ZW1wLFxuXHRcdFx0XHRpbmRleCA9IDAsXG5cdFx0XHRcdGxlbmd0aCA9IGFuaW1hdGlvbi50d2VlbnMubGVuZ3RoO1xuXG5cdFx0XHRmb3IgKCA7IGluZGV4IDwgbGVuZ3RoIDsgaW5kZXgrKyApIHtcblx0XHRcdFx0YW5pbWF0aW9uLnR3ZWVuc1sgaW5kZXggXS5ydW4oIHBlcmNlbnQgKTtcblx0XHRcdH1cblxuXHRcdFx0ZGVmZXJyZWQubm90aWZ5V2l0aCggZWxlbSwgWyBhbmltYXRpb24sIHBlcmNlbnQsIHJlbWFpbmluZyBdKTtcblxuXHRcdFx0aWYgKCBwZXJjZW50IDwgMSAmJiBsZW5ndGggKSB7XG5cdFx0XHRcdHJldHVybiByZW1haW5pbmc7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRkZWZlcnJlZC5yZXNvbHZlV2l0aCggZWxlbSwgWyBhbmltYXRpb24gXSApO1xuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHR9XG5cdFx0fSxcblx0XHRhbmltYXRpb24gPSBkZWZlcnJlZC5wcm9taXNlKHtcblx0XHRcdGVsZW06IGVsZW0sXG5cdFx0XHRwcm9wczogalF1ZXJ5LmV4dGVuZCgge30sIHByb3BlcnRpZXMgKSxcblx0XHRcdG9wdHM6IGpRdWVyeS5leHRlbmQoIHRydWUsIHsgc3BlY2lhbEVhc2luZzoge30gfSwgb3B0aW9ucyApLFxuXHRcdFx0b3JpZ2luYWxQcm9wZXJ0aWVzOiBwcm9wZXJ0aWVzLFxuXHRcdFx0b3JpZ2luYWxPcHRpb25zOiBvcHRpb25zLFxuXHRcdFx0c3RhcnRUaW1lOiBmeE5vdyB8fCBjcmVhdGVGeE5vdygpLFxuXHRcdFx0ZHVyYXRpb246IG9wdGlvbnMuZHVyYXRpb24sXG5cdFx0XHR0d2VlbnM6IFtdLFxuXHRcdFx0Y3JlYXRlVHdlZW46IGZ1bmN0aW9uKCBwcm9wLCBlbmQgKSB7XG5cdFx0XHRcdHZhciB0d2VlbiA9IGpRdWVyeS5Ud2VlbiggZWxlbSwgYW5pbWF0aW9uLm9wdHMsIHByb3AsIGVuZCxcblx0XHRcdFx0XHRcdGFuaW1hdGlvbi5vcHRzLnNwZWNpYWxFYXNpbmdbIHByb3AgXSB8fCBhbmltYXRpb24ub3B0cy5lYXNpbmcgKTtcblx0XHRcdFx0YW5pbWF0aW9uLnR3ZWVucy5wdXNoKCB0d2VlbiApO1xuXHRcdFx0XHRyZXR1cm4gdHdlZW47XG5cdFx0XHR9LFxuXHRcdFx0c3RvcDogZnVuY3Rpb24oIGdvdG9FbmQgKSB7XG5cdFx0XHRcdHZhciBpbmRleCA9IDAsXG5cdFx0XHRcdFx0Ly8gSWYgd2UgYXJlIGdvaW5nIHRvIHRoZSBlbmQsIHdlIHdhbnQgdG8gcnVuIGFsbCB0aGUgdHdlZW5zXG5cdFx0XHRcdFx0Ly8gb3RoZXJ3aXNlIHdlIHNraXAgdGhpcyBwYXJ0XG5cdFx0XHRcdFx0bGVuZ3RoID0gZ290b0VuZCA/IGFuaW1hdGlvbi50d2VlbnMubGVuZ3RoIDogMDtcblx0XHRcdFx0aWYgKCBzdG9wcGVkICkge1xuXHRcdFx0XHRcdHJldHVybiB0aGlzO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHN0b3BwZWQgPSB0cnVlO1xuXHRcdFx0XHRmb3IgKCA7IGluZGV4IDwgbGVuZ3RoIDsgaW5kZXgrKyApIHtcblx0XHRcdFx0XHRhbmltYXRpb24udHdlZW5zWyBpbmRleCBdLnJ1biggMSApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gUmVzb2x2ZSB3aGVuIHdlIHBsYXllZCB0aGUgbGFzdCBmcmFtZTsgb3RoZXJ3aXNlLCByZWplY3Rcblx0XHRcdFx0aWYgKCBnb3RvRW5kICkge1xuXHRcdFx0XHRcdGRlZmVycmVkLnJlc29sdmVXaXRoKCBlbGVtLCBbIGFuaW1hdGlvbiwgZ290b0VuZCBdICk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0ZGVmZXJyZWQucmVqZWN0V2l0aCggZWxlbSwgWyBhbmltYXRpb24sIGdvdG9FbmQgXSApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiB0aGlzO1xuXHRcdFx0fVxuXHRcdH0pLFxuXHRcdHByb3BzID0gYW5pbWF0aW9uLnByb3BzO1xuXG5cdHByb3BGaWx0ZXIoIHByb3BzLCBhbmltYXRpb24ub3B0cy5zcGVjaWFsRWFzaW5nICk7XG5cblx0Zm9yICggOyBpbmRleCA8IGxlbmd0aCA7IGluZGV4KysgKSB7XG5cdFx0cmVzdWx0ID0gYW5pbWF0aW9uUHJlZmlsdGVyc1sgaW5kZXggXS5jYWxsKCBhbmltYXRpb24sIGVsZW0sIHByb3BzLCBhbmltYXRpb24ub3B0cyApO1xuXHRcdGlmICggcmVzdWx0ICkge1xuXHRcdFx0cmV0dXJuIHJlc3VsdDtcblx0XHR9XG5cdH1cblxuXHRqUXVlcnkubWFwKCBwcm9wcywgY3JlYXRlVHdlZW4sIGFuaW1hdGlvbiApO1xuXG5cdGlmICggalF1ZXJ5LmlzRnVuY3Rpb24oIGFuaW1hdGlvbi5vcHRzLnN0YXJ0ICkgKSB7XG5cdFx0YW5pbWF0aW9uLm9wdHMuc3RhcnQuY2FsbCggZWxlbSwgYW5pbWF0aW9uICk7XG5cdH1cblxuXHRqUXVlcnkuZngudGltZXIoXG5cdFx0alF1ZXJ5LmV4dGVuZCggdGljaywge1xuXHRcdFx0ZWxlbTogZWxlbSxcblx0XHRcdGFuaW06IGFuaW1hdGlvbixcblx0XHRcdHF1ZXVlOiBhbmltYXRpb24ub3B0cy5xdWV1ZVxuXHRcdH0pXG5cdCk7XG5cblx0Ly8gYXR0YWNoIGNhbGxiYWNrcyBmcm9tIG9wdGlvbnNcblx0cmV0dXJuIGFuaW1hdGlvbi5wcm9ncmVzcyggYW5pbWF0aW9uLm9wdHMucHJvZ3Jlc3MgKVxuXHRcdC5kb25lKCBhbmltYXRpb24ub3B0cy5kb25lLCBhbmltYXRpb24ub3B0cy5jb21wbGV0ZSApXG5cdFx0LmZhaWwoIGFuaW1hdGlvbi5vcHRzLmZhaWwgKVxuXHRcdC5hbHdheXMoIGFuaW1hdGlvbi5vcHRzLmFsd2F5cyApO1xufVxuXG5qUXVlcnkuQW5pbWF0aW9uID0galF1ZXJ5LmV4dGVuZCggQW5pbWF0aW9uLCB7XG5cblx0dHdlZW5lcjogZnVuY3Rpb24oIHByb3BzLCBjYWxsYmFjayApIHtcblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBwcm9wcyApICkge1xuXHRcdFx0Y2FsbGJhY2sgPSBwcm9wcztcblx0XHRcdHByb3BzID0gWyBcIipcIiBdO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRwcm9wcyA9IHByb3BzLnNwbGl0KFwiIFwiKTtcblx0XHR9XG5cblx0XHR2YXIgcHJvcCxcblx0XHRcdGluZGV4ID0gMCxcblx0XHRcdGxlbmd0aCA9IHByb3BzLmxlbmd0aDtcblxuXHRcdGZvciAoIDsgaW5kZXggPCBsZW5ndGggOyBpbmRleCsrICkge1xuXHRcdFx0cHJvcCA9IHByb3BzWyBpbmRleCBdO1xuXHRcdFx0dHdlZW5lcnNbIHByb3AgXSA9IHR3ZWVuZXJzWyBwcm9wIF0gfHwgW107XG5cdFx0XHR0d2VlbmVyc1sgcHJvcCBdLnVuc2hpZnQoIGNhbGxiYWNrICk7XG5cdFx0fVxuXHR9LFxuXG5cdHByZWZpbHRlcjogZnVuY3Rpb24oIGNhbGxiYWNrLCBwcmVwZW5kICkge1xuXHRcdGlmICggcHJlcGVuZCApIHtcblx0XHRcdGFuaW1hdGlvblByZWZpbHRlcnMudW5zaGlmdCggY2FsbGJhY2sgKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0YW5pbWF0aW9uUHJlZmlsdGVycy5wdXNoKCBjYWxsYmFjayApO1xuXHRcdH1cblx0fVxufSk7XG5cbmpRdWVyeS5zcGVlZCA9IGZ1bmN0aW9uKCBzcGVlZCwgZWFzaW5nLCBmbiApIHtcblx0dmFyIG9wdCA9IHNwZWVkICYmIHR5cGVvZiBzcGVlZCA9PT0gXCJvYmplY3RcIiA/IGpRdWVyeS5leHRlbmQoIHt9LCBzcGVlZCApIDoge1xuXHRcdGNvbXBsZXRlOiBmbiB8fCAhZm4gJiYgZWFzaW5nIHx8XG5cdFx0XHRqUXVlcnkuaXNGdW5jdGlvbiggc3BlZWQgKSAmJiBzcGVlZCxcblx0XHRkdXJhdGlvbjogc3BlZWQsXG5cdFx0ZWFzaW5nOiBmbiAmJiBlYXNpbmcgfHwgZWFzaW5nICYmICFqUXVlcnkuaXNGdW5jdGlvbiggZWFzaW5nICkgJiYgZWFzaW5nXG5cdH07XG5cblx0b3B0LmR1cmF0aW9uID0galF1ZXJ5LmZ4Lm9mZiA/IDAgOiB0eXBlb2Ygb3B0LmR1cmF0aW9uID09PSBcIm51bWJlclwiID8gb3B0LmR1cmF0aW9uIDpcblx0XHRvcHQuZHVyYXRpb24gaW4galF1ZXJ5LmZ4LnNwZWVkcyA/IGpRdWVyeS5meC5zcGVlZHNbIG9wdC5kdXJhdGlvbiBdIDogalF1ZXJ5LmZ4LnNwZWVkcy5fZGVmYXVsdDtcblxuXHQvLyBOb3JtYWxpemUgb3B0LnF1ZXVlIC0gdHJ1ZS91bmRlZmluZWQvbnVsbCAtPiBcImZ4XCJcblx0aWYgKCBvcHQucXVldWUgPT0gbnVsbCB8fCBvcHQucXVldWUgPT09IHRydWUgKSB7XG5cdFx0b3B0LnF1ZXVlID0gXCJmeFwiO1xuXHR9XG5cblx0Ly8gUXVldWVpbmdcblx0b3B0Lm9sZCA9IG9wdC5jb21wbGV0ZTtcblxuXHRvcHQuY29tcGxldGUgPSBmdW5jdGlvbigpIHtcblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBvcHQub2xkICkgKSB7XG5cdFx0XHRvcHQub2xkLmNhbGwoIHRoaXMgKTtcblx0XHR9XG5cblx0XHRpZiAoIG9wdC5xdWV1ZSApIHtcblx0XHRcdGpRdWVyeS5kZXF1ZXVlKCB0aGlzLCBvcHQucXVldWUgKTtcblx0XHR9XG5cdH07XG5cblx0cmV0dXJuIG9wdDtcbn07XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHRmYWRlVG86IGZ1bmN0aW9uKCBzcGVlZCwgdG8sIGVhc2luZywgY2FsbGJhY2sgKSB7XG5cblx0XHQvLyBTaG93IGFueSBoaWRkZW4gZWxlbWVudHMgYWZ0ZXIgc2V0dGluZyBvcGFjaXR5IHRvIDBcblx0XHRyZXR1cm4gdGhpcy5maWx0ZXIoIGlzSGlkZGVuICkuY3NzKCBcIm9wYWNpdHlcIiwgMCApLnNob3coKVxuXG5cdFx0XHQvLyBBbmltYXRlIHRvIHRoZSB2YWx1ZSBzcGVjaWZpZWRcblx0XHRcdC5lbmQoKS5hbmltYXRlKHsgb3BhY2l0eTogdG8gfSwgc3BlZWQsIGVhc2luZywgY2FsbGJhY2sgKTtcblx0fSxcblx0YW5pbWF0ZTogZnVuY3Rpb24oIHByb3AsIHNwZWVkLCBlYXNpbmcsIGNhbGxiYWNrICkge1xuXHRcdHZhciBlbXB0eSA9IGpRdWVyeS5pc0VtcHR5T2JqZWN0KCBwcm9wICksXG5cdFx0XHRvcHRhbGwgPSBqUXVlcnkuc3BlZWQoIHNwZWVkLCBlYXNpbmcsIGNhbGxiYWNrICksXG5cdFx0XHRkb0FuaW1hdGlvbiA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHQvLyBPcGVyYXRlIG9uIGEgY29weSBvZiBwcm9wIHNvIHBlci1wcm9wZXJ0eSBlYXNpbmcgd29uJ3QgYmUgbG9zdFxuXHRcdFx0XHR2YXIgYW5pbSA9IEFuaW1hdGlvbiggdGhpcywgalF1ZXJ5LmV4dGVuZCgge30sIHByb3AgKSwgb3B0YWxsICk7XG5cblx0XHRcdFx0Ly8gRW1wdHkgYW5pbWF0aW9ucywgb3IgZmluaXNoaW5nIHJlc29sdmVzIGltbWVkaWF0ZWx5XG5cdFx0XHRcdGlmICggZW1wdHkgfHwgZGF0YV9wcml2LmdldCggdGhpcywgXCJmaW5pc2hcIiApICkge1xuXHRcdFx0XHRcdGFuaW0uc3RvcCggdHJ1ZSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9O1xuXHRcdFx0ZG9BbmltYXRpb24uZmluaXNoID0gZG9BbmltYXRpb247XG5cblx0XHRyZXR1cm4gZW1wdHkgfHwgb3B0YWxsLnF1ZXVlID09PSBmYWxzZSA/XG5cdFx0XHR0aGlzLmVhY2goIGRvQW5pbWF0aW9uICkgOlxuXHRcdFx0dGhpcy5xdWV1ZSggb3B0YWxsLnF1ZXVlLCBkb0FuaW1hdGlvbiApO1xuXHR9LFxuXHRzdG9wOiBmdW5jdGlvbiggdHlwZSwgY2xlYXJRdWV1ZSwgZ290b0VuZCApIHtcblx0XHR2YXIgc3RvcFF1ZXVlID0gZnVuY3Rpb24oIGhvb2tzICkge1xuXHRcdFx0dmFyIHN0b3AgPSBob29rcy5zdG9wO1xuXHRcdFx0ZGVsZXRlIGhvb2tzLnN0b3A7XG5cdFx0XHRzdG9wKCBnb3RvRW5kICk7XG5cdFx0fTtcblxuXHRcdGlmICggdHlwZW9mIHR5cGUgIT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRnb3RvRW5kID0gY2xlYXJRdWV1ZTtcblx0XHRcdGNsZWFyUXVldWUgPSB0eXBlO1xuXHRcdFx0dHlwZSA9IHVuZGVmaW5lZDtcblx0XHR9XG5cdFx0aWYgKCBjbGVhclF1ZXVlICYmIHR5cGUgIT09IGZhbHNlICkge1xuXHRcdFx0dGhpcy5xdWV1ZSggdHlwZSB8fCBcImZ4XCIsIFtdICk7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpIHtcblx0XHRcdHZhciBkZXF1ZXVlID0gdHJ1ZSxcblx0XHRcdFx0aW5kZXggPSB0eXBlICE9IG51bGwgJiYgdHlwZSArIFwicXVldWVIb29rc1wiLFxuXHRcdFx0XHR0aW1lcnMgPSBqUXVlcnkudGltZXJzLFxuXHRcdFx0XHRkYXRhID0gZGF0YV9wcml2LmdldCggdGhpcyApO1xuXG5cdFx0XHRpZiAoIGluZGV4ICkge1xuXHRcdFx0XHRpZiAoIGRhdGFbIGluZGV4IF0gJiYgZGF0YVsgaW5kZXggXS5zdG9wICkge1xuXHRcdFx0XHRcdHN0b3BRdWV1ZSggZGF0YVsgaW5kZXggXSApO1xuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRmb3IgKCBpbmRleCBpbiBkYXRhICkge1xuXHRcdFx0XHRcdGlmICggZGF0YVsgaW5kZXggXSAmJiBkYXRhWyBpbmRleCBdLnN0b3AgJiYgcnJ1bi50ZXN0KCBpbmRleCApICkge1xuXHRcdFx0XHRcdFx0c3RvcFF1ZXVlKCBkYXRhWyBpbmRleCBdICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdGZvciAoIGluZGV4ID0gdGltZXJzLmxlbmd0aDsgaW5kZXgtLTsgKSB7XG5cdFx0XHRcdGlmICggdGltZXJzWyBpbmRleCBdLmVsZW0gPT09IHRoaXMgJiYgKHR5cGUgPT0gbnVsbCB8fCB0aW1lcnNbIGluZGV4IF0ucXVldWUgPT09IHR5cGUpICkge1xuXHRcdFx0XHRcdHRpbWVyc1sgaW5kZXggXS5hbmltLnN0b3AoIGdvdG9FbmQgKTtcblx0XHRcdFx0XHRkZXF1ZXVlID0gZmFsc2U7XG5cdFx0XHRcdFx0dGltZXJzLnNwbGljZSggaW5kZXgsIDEgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBTdGFydCB0aGUgbmV4dCBpbiB0aGUgcXVldWUgaWYgdGhlIGxhc3Qgc3RlcCB3YXNuJ3QgZm9yY2VkLlxuXHRcdFx0Ly8gVGltZXJzIGN1cnJlbnRseSB3aWxsIGNhbGwgdGhlaXIgY29tcGxldGUgY2FsbGJhY2tzLCB3aGljaFxuXHRcdFx0Ly8gd2lsbCBkZXF1ZXVlIGJ1dCBvbmx5IGlmIHRoZXkgd2VyZSBnb3RvRW5kLlxuXHRcdFx0aWYgKCBkZXF1ZXVlIHx8ICFnb3RvRW5kICkge1xuXHRcdFx0XHRqUXVlcnkuZGVxdWV1ZSggdGhpcywgdHlwZSApO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9LFxuXHRmaW5pc2g6IGZ1bmN0aW9uKCB0eXBlICkge1xuXHRcdGlmICggdHlwZSAhPT0gZmFsc2UgKSB7XG5cdFx0XHR0eXBlID0gdHlwZSB8fCBcImZ4XCI7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgaW5kZXgsXG5cdFx0XHRcdGRhdGEgPSBkYXRhX3ByaXYuZ2V0KCB0aGlzICksXG5cdFx0XHRcdHF1ZXVlID0gZGF0YVsgdHlwZSArIFwicXVldWVcIiBdLFxuXHRcdFx0XHRob29rcyA9IGRhdGFbIHR5cGUgKyBcInF1ZXVlSG9va3NcIiBdLFxuXHRcdFx0XHR0aW1lcnMgPSBqUXVlcnkudGltZXJzLFxuXHRcdFx0XHRsZW5ndGggPSBxdWV1ZSA/IHF1ZXVlLmxlbmd0aCA6IDA7XG5cblx0XHRcdC8vIEVuYWJsZSBmaW5pc2hpbmcgZmxhZyBvbiBwcml2YXRlIGRhdGFcblx0XHRcdGRhdGEuZmluaXNoID0gdHJ1ZTtcblxuXHRcdFx0Ly8gRW1wdHkgdGhlIHF1ZXVlIGZpcnN0XG5cdFx0XHRqUXVlcnkucXVldWUoIHRoaXMsIHR5cGUsIFtdICk7XG5cblx0XHRcdGlmICggaG9va3MgJiYgaG9va3Muc3RvcCApIHtcblx0XHRcdFx0aG9va3Muc3RvcC5jYWxsKCB0aGlzLCB0cnVlICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIExvb2sgZm9yIGFueSBhY3RpdmUgYW5pbWF0aW9ucywgYW5kIGZpbmlzaCB0aGVtXG5cdFx0XHRmb3IgKCBpbmRleCA9IHRpbWVycy5sZW5ndGg7IGluZGV4LS07ICkge1xuXHRcdFx0XHRpZiAoIHRpbWVyc1sgaW5kZXggXS5lbGVtID09PSB0aGlzICYmIHRpbWVyc1sgaW5kZXggXS5xdWV1ZSA9PT0gdHlwZSApIHtcblx0XHRcdFx0XHR0aW1lcnNbIGluZGV4IF0uYW5pbS5zdG9wKCB0cnVlICk7XG5cdFx0XHRcdFx0dGltZXJzLnNwbGljZSggaW5kZXgsIDEgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBMb29rIGZvciBhbnkgYW5pbWF0aW9ucyBpbiB0aGUgb2xkIHF1ZXVlIGFuZCBmaW5pc2ggdGhlbVxuXHRcdFx0Zm9yICggaW5kZXggPSAwOyBpbmRleCA8IGxlbmd0aDsgaW5kZXgrKyApIHtcblx0XHRcdFx0aWYgKCBxdWV1ZVsgaW5kZXggXSAmJiBxdWV1ZVsgaW5kZXggXS5maW5pc2ggKSB7XG5cdFx0XHRcdFx0cXVldWVbIGluZGV4IF0uZmluaXNoLmNhbGwoIHRoaXMgKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBUdXJuIG9mZiBmaW5pc2hpbmcgZmxhZ1xuXHRcdFx0ZGVsZXRlIGRhdGEuZmluaXNoO1xuXHRcdH0pO1xuXHR9XG59KTtcblxualF1ZXJ5LmVhY2goWyBcInRvZ2dsZVwiLCBcInNob3dcIiwgXCJoaWRlXCIgXSwgZnVuY3Rpb24oIGksIG5hbWUgKSB7XG5cdHZhciBjc3NGbiA9IGpRdWVyeS5mblsgbmFtZSBdO1xuXHRqUXVlcnkuZm5bIG5hbWUgXSA9IGZ1bmN0aW9uKCBzcGVlZCwgZWFzaW5nLCBjYWxsYmFjayApIHtcblx0XHRyZXR1cm4gc3BlZWQgPT0gbnVsbCB8fCB0eXBlb2Ygc3BlZWQgPT09IFwiYm9vbGVhblwiID9cblx0XHRcdGNzc0ZuLmFwcGx5KCB0aGlzLCBhcmd1bWVudHMgKSA6XG5cdFx0XHR0aGlzLmFuaW1hdGUoIGdlbkZ4KCBuYW1lLCB0cnVlICksIHNwZWVkLCBlYXNpbmcsIGNhbGxiYWNrICk7XG5cdH07XG59KTtcblxuLy8gR2VuZXJhdGUgc2hvcnRjdXRzIGZvciBjdXN0b20gYW5pbWF0aW9uc1xualF1ZXJ5LmVhY2goe1xuXHRzbGlkZURvd246IGdlbkZ4KFwic2hvd1wiKSxcblx0c2xpZGVVcDogZ2VuRngoXCJoaWRlXCIpLFxuXHRzbGlkZVRvZ2dsZTogZ2VuRngoXCJ0b2dnbGVcIiksXG5cdGZhZGVJbjogeyBvcGFjaXR5OiBcInNob3dcIiB9LFxuXHRmYWRlT3V0OiB7IG9wYWNpdHk6IFwiaGlkZVwiIH0sXG5cdGZhZGVUb2dnbGU6IHsgb3BhY2l0eTogXCJ0b2dnbGVcIiB9XG59LCBmdW5jdGlvbiggbmFtZSwgcHJvcHMgKSB7XG5cdGpRdWVyeS5mblsgbmFtZSBdID0gZnVuY3Rpb24oIHNwZWVkLCBlYXNpbmcsIGNhbGxiYWNrICkge1xuXHRcdHJldHVybiB0aGlzLmFuaW1hdGUoIHByb3BzLCBzcGVlZCwgZWFzaW5nLCBjYWxsYmFjayApO1xuXHR9O1xufSk7XG5cbmpRdWVyeS50aW1lcnMgPSBbXTtcbmpRdWVyeS5meC50aWNrID0gZnVuY3Rpb24oKSB7XG5cdHZhciB0aW1lcixcblx0XHRpID0gMCxcblx0XHR0aW1lcnMgPSBqUXVlcnkudGltZXJzO1xuXG5cdGZ4Tm93ID0galF1ZXJ5Lm5vdygpO1xuXG5cdGZvciAoIDsgaSA8IHRpbWVycy5sZW5ndGg7IGkrKyApIHtcblx0XHR0aW1lciA9IHRpbWVyc1sgaSBdO1xuXHRcdC8vIENoZWNrcyB0aGUgdGltZXIgaGFzIG5vdCBhbHJlYWR5IGJlZW4gcmVtb3ZlZFxuXHRcdGlmICggIXRpbWVyKCkgJiYgdGltZXJzWyBpIF0gPT09IHRpbWVyICkge1xuXHRcdFx0dGltZXJzLnNwbGljZSggaS0tLCAxICk7XG5cdFx0fVxuXHR9XG5cblx0aWYgKCAhdGltZXJzLmxlbmd0aCApIHtcblx0XHRqUXVlcnkuZnguc3RvcCgpO1xuXHR9XG5cdGZ4Tm93ID0gdW5kZWZpbmVkO1xufTtcblxualF1ZXJ5LmZ4LnRpbWVyID0gZnVuY3Rpb24oIHRpbWVyICkge1xuXHRqUXVlcnkudGltZXJzLnB1c2goIHRpbWVyICk7XG5cdGlmICggdGltZXIoKSApIHtcblx0XHRqUXVlcnkuZnguc3RhcnQoKTtcblx0fSBlbHNlIHtcblx0XHRqUXVlcnkudGltZXJzLnBvcCgpO1xuXHR9XG59O1xuXG5qUXVlcnkuZnguaW50ZXJ2YWwgPSAxMztcblxualF1ZXJ5LmZ4LnN0YXJ0ID0gZnVuY3Rpb24oKSB7XG5cdGlmICggIXRpbWVySWQgKSB7XG5cdFx0dGltZXJJZCA9IHNldEludGVydmFsKCBqUXVlcnkuZngudGljaywgalF1ZXJ5LmZ4LmludGVydmFsICk7XG5cdH1cbn07XG5cbmpRdWVyeS5meC5zdG9wID0gZnVuY3Rpb24oKSB7XG5cdGNsZWFySW50ZXJ2YWwoIHRpbWVySWQgKTtcblx0dGltZXJJZCA9IG51bGw7XG59O1xuXG5qUXVlcnkuZnguc3BlZWRzID0ge1xuXHRzbG93OiA2MDAsXG5cdGZhc3Q6IDIwMCxcblx0Ly8gRGVmYXVsdCBzcGVlZFxuXHRfZGVmYXVsdDogNDAwXG59O1xuXG5cbi8vIEJhc2VkIG9mZiBvZiB0aGUgcGx1Z2luIGJ5IENsaW50IEhlbGZlcnMsIHdpdGggcGVybWlzc2lvbi5cbi8vIGh0dHA6Ly9ibGluZHNpZ25hbHMuY29tL2luZGV4LnBocC8yMDA5LzA3L2pxdWVyeS1kZWxheS9cbmpRdWVyeS5mbi5kZWxheSA9IGZ1bmN0aW9uKCB0aW1lLCB0eXBlICkge1xuXHR0aW1lID0galF1ZXJ5LmZ4ID8galF1ZXJ5LmZ4LnNwZWVkc1sgdGltZSBdIHx8IHRpbWUgOiB0aW1lO1xuXHR0eXBlID0gdHlwZSB8fCBcImZ4XCI7XG5cblx0cmV0dXJuIHRoaXMucXVldWUoIHR5cGUsIGZ1bmN0aW9uKCBuZXh0LCBob29rcyApIHtcblx0XHR2YXIgdGltZW91dCA9IHNldFRpbWVvdXQoIG5leHQsIHRpbWUgKTtcblx0XHRob29rcy5zdG9wID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRjbGVhclRpbWVvdXQoIHRpbWVvdXQgKTtcblx0XHR9O1xuXHR9KTtcbn07XG5cblxuKGZ1bmN0aW9uKCkge1xuXHR2YXIgaW5wdXQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCBcImlucHV0XCIgKSxcblx0XHRzZWxlY3QgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCBcInNlbGVjdFwiICksXG5cdFx0b3B0ID0gc2VsZWN0LmFwcGVuZENoaWxkKCBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCBcIm9wdGlvblwiICkgKTtcblxuXHRpbnB1dC50eXBlID0gXCJjaGVja2JveFwiO1xuXG5cdC8vIFN1cHBvcnQ6IGlPUzw9NS4xLCBBbmRyb2lkPD00LjIrXG5cdC8vIERlZmF1bHQgdmFsdWUgZm9yIGEgY2hlY2tib3ggc2hvdWxkIGJlIFwib25cIlxuXHRzdXBwb3J0LmNoZWNrT24gPSBpbnB1dC52YWx1ZSAhPT0gXCJcIjtcblxuXHQvLyBTdXBwb3J0OiBJRTw9MTErXG5cdC8vIE11c3QgYWNjZXNzIHNlbGVjdGVkSW5kZXggdG8gbWFrZSBkZWZhdWx0IG9wdGlvbnMgc2VsZWN0XG5cdHN1cHBvcnQub3B0U2VsZWN0ZWQgPSBvcHQuc2VsZWN0ZWQ7XG5cblx0Ly8gU3VwcG9ydDogQW5kcm9pZDw9Mi4zXG5cdC8vIE9wdGlvbnMgaW5zaWRlIGRpc2FibGVkIHNlbGVjdHMgYXJlIGluY29ycmVjdGx5IG1hcmtlZCBhcyBkaXNhYmxlZFxuXHRzZWxlY3QuZGlzYWJsZWQgPSB0cnVlO1xuXHRzdXBwb3J0Lm9wdERpc2FibGVkID0gIW9wdC5kaXNhYmxlZDtcblxuXHQvLyBTdXBwb3J0OiBJRTw9MTErXG5cdC8vIEFuIGlucHV0IGxvc2VzIGl0cyB2YWx1ZSBhZnRlciBiZWNvbWluZyBhIHJhZGlvXG5cdGlucHV0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCggXCJpbnB1dFwiICk7XG5cdGlucHV0LnZhbHVlID0gXCJ0XCI7XG5cdGlucHV0LnR5cGUgPSBcInJhZGlvXCI7XG5cdHN1cHBvcnQucmFkaW9WYWx1ZSA9IGlucHV0LnZhbHVlID09PSBcInRcIjtcbn0pKCk7XG5cblxudmFyIG5vZGVIb29rLCBib29sSG9vayxcblx0YXR0ckhhbmRsZSA9IGpRdWVyeS5leHByLmF0dHJIYW5kbGU7XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHRhdHRyOiBmdW5jdGlvbiggbmFtZSwgdmFsdWUgKSB7XG5cdFx0cmV0dXJuIGFjY2VzcyggdGhpcywgalF1ZXJ5LmF0dHIsIG5hbWUsIHZhbHVlLCBhcmd1bWVudHMubGVuZ3RoID4gMSApO1xuXHR9LFxuXG5cdHJlbW92ZUF0dHI6IGZ1bmN0aW9uKCBuYW1lICkge1xuXHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHRqUXVlcnkucmVtb3ZlQXR0ciggdGhpcywgbmFtZSApO1xuXHRcdH0pO1xuXHR9XG59KTtcblxualF1ZXJ5LmV4dGVuZCh7XG5cdGF0dHI6IGZ1bmN0aW9uKCBlbGVtLCBuYW1lLCB2YWx1ZSApIHtcblx0XHR2YXIgaG9va3MsIHJldCxcblx0XHRcdG5UeXBlID0gZWxlbS5ub2RlVHlwZTtcblxuXHRcdC8vIGRvbid0IGdldC9zZXQgYXR0cmlidXRlcyBvbiB0ZXh0LCBjb21tZW50IGFuZCBhdHRyaWJ1dGUgbm9kZXNcblx0XHRpZiAoICFlbGVtIHx8IG5UeXBlID09PSAzIHx8IG5UeXBlID09PSA4IHx8IG5UeXBlID09PSAyICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIEZhbGxiYWNrIHRvIHByb3Agd2hlbiBhdHRyaWJ1dGVzIGFyZSBub3Qgc3VwcG9ydGVkXG5cdFx0aWYgKCB0eXBlb2YgZWxlbS5nZXRBdHRyaWJ1dGUgPT09IHN0cnVuZGVmaW5lZCApIHtcblx0XHRcdHJldHVybiBqUXVlcnkucHJvcCggZWxlbSwgbmFtZSwgdmFsdWUgKTtcblx0XHR9XG5cblx0XHQvLyBBbGwgYXR0cmlidXRlcyBhcmUgbG93ZXJjYXNlXG5cdFx0Ly8gR3JhYiBuZWNlc3NhcnkgaG9vayBpZiBvbmUgaXMgZGVmaW5lZFxuXHRcdGlmICggblR5cGUgIT09IDEgfHwgIWpRdWVyeS5pc1hNTERvYyggZWxlbSApICkge1xuXHRcdFx0bmFtZSA9IG5hbWUudG9Mb3dlckNhc2UoKTtcblx0XHRcdGhvb2tzID0galF1ZXJ5LmF0dHJIb29rc1sgbmFtZSBdIHx8XG5cdFx0XHRcdCggalF1ZXJ5LmV4cHIubWF0Y2guYm9vbC50ZXN0KCBuYW1lICkgPyBib29sSG9vayA6IG5vZGVIb29rICk7XG5cdFx0fVxuXG5cdFx0aWYgKCB2YWx1ZSAhPT0gdW5kZWZpbmVkICkge1xuXG5cdFx0XHRpZiAoIHZhbHVlID09PSBudWxsICkge1xuXHRcdFx0XHRqUXVlcnkucmVtb3ZlQXR0ciggZWxlbSwgbmFtZSApO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBob29rcyAmJiBcInNldFwiIGluIGhvb2tzICYmIChyZXQgPSBob29rcy5zZXQoIGVsZW0sIHZhbHVlLCBuYW1lICkpICE9PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdHJldHVybiByZXQ7XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGVsZW0uc2V0QXR0cmlidXRlKCBuYW1lLCB2YWx1ZSArIFwiXCIgKTtcblx0XHRcdFx0cmV0dXJuIHZhbHVlO1xuXHRcdFx0fVxuXG5cdFx0fSBlbHNlIGlmICggaG9va3MgJiYgXCJnZXRcIiBpbiBob29rcyAmJiAocmV0ID0gaG9va3MuZ2V0KCBlbGVtLCBuYW1lICkpICE9PSBudWxsICkge1xuXHRcdFx0cmV0dXJuIHJldDtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHRyZXQgPSBqUXVlcnkuZmluZC5hdHRyKCBlbGVtLCBuYW1lICk7XG5cblx0XHRcdC8vIE5vbi1leGlzdGVudCBhdHRyaWJ1dGVzIHJldHVybiBudWxsLCB3ZSBub3JtYWxpemUgdG8gdW5kZWZpbmVkXG5cdFx0XHRyZXR1cm4gcmV0ID09IG51bGwgP1xuXHRcdFx0XHR1bmRlZmluZWQgOlxuXHRcdFx0XHRyZXQ7XG5cdFx0fVxuXHR9LFxuXG5cdHJlbW92ZUF0dHI6IGZ1bmN0aW9uKCBlbGVtLCB2YWx1ZSApIHtcblx0XHR2YXIgbmFtZSwgcHJvcE5hbWUsXG5cdFx0XHRpID0gMCxcblx0XHRcdGF0dHJOYW1lcyA9IHZhbHVlICYmIHZhbHVlLm1hdGNoKCBybm90d2hpdGUgKTtcblxuXHRcdGlmICggYXR0ck5hbWVzICYmIGVsZW0ubm9kZVR5cGUgPT09IDEgKSB7XG5cdFx0XHR3aGlsZSAoIChuYW1lID0gYXR0ck5hbWVzW2krK10pICkge1xuXHRcdFx0XHRwcm9wTmFtZSA9IGpRdWVyeS5wcm9wRml4WyBuYW1lIF0gfHwgbmFtZTtcblxuXHRcdFx0XHQvLyBCb29sZWFuIGF0dHJpYnV0ZXMgZ2V0IHNwZWNpYWwgdHJlYXRtZW50ICgjMTA4NzApXG5cdFx0XHRcdGlmICggalF1ZXJ5LmV4cHIubWF0Y2guYm9vbC50ZXN0KCBuYW1lICkgKSB7XG5cdFx0XHRcdFx0Ly8gU2V0IGNvcnJlc3BvbmRpbmcgcHJvcGVydHkgdG8gZmFsc2Vcblx0XHRcdFx0XHRlbGVtWyBwcm9wTmFtZSBdID0gZmFsc2U7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRlbGVtLnJlbW92ZUF0dHJpYnV0ZSggbmFtZSApO1xuXHRcdFx0fVxuXHRcdH1cblx0fSxcblxuXHRhdHRySG9va3M6IHtcblx0XHR0eXBlOiB7XG5cdFx0XHRzZXQ6IGZ1bmN0aW9uKCBlbGVtLCB2YWx1ZSApIHtcblx0XHRcdFx0aWYgKCAhc3VwcG9ydC5yYWRpb1ZhbHVlICYmIHZhbHVlID09PSBcInJhZGlvXCIgJiZcblx0XHRcdFx0XHRqUXVlcnkubm9kZU5hbWUoIGVsZW0sIFwiaW5wdXRcIiApICkge1xuXHRcdFx0XHRcdHZhciB2YWwgPSBlbGVtLnZhbHVlO1xuXHRcdFx0XHRcdGVsZW0uc2V0QXR0cmlidXRlKCBcInR5cGVcIiwgdmFsdWUgKTtcblx0XHRcdFx0XHRpZiAoIHZhbCApIHtcblx0XHRcdFx0XHRcdGVsZW0udmFsdWUgPSB2YWw7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHJldHVybiB2YWx1ZTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblx0fVxufSk7XG5cbi8vIEhvb2tzIGZvciBib29sZWFuIGF0dHJpYnV0ZXNcbmJvb2xIb29rID0ge1xuXHRzZXQ6IGZ1bmN0aW9uKCBlbGVtLCB2YWx1ZSwgbmFtZSApIHtcblx0XHRpZiAoIHZhbHVlID09PSBmYWxzZSApIHtcblx0XHRcdC8vIFJlbW92ZSBib29sZWFuIGF0dHJpYnV0ZXMgd2hlbiBzZXQgdG8gZmFsc2Vcblx0XHRcdGpRdWVyeS5yZW1vdmVBdHRyKCBlbGVtLCBuYW1lICk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGVsZW0uc2V0QXR0cmlidXRlKCBuYW1lLCBuYW1lICk7XG5cdFx0fVxuXHRcdHJldHVybiBuYW1lO1xuXHR9XG59O1xualF1ZXJ5LmVhY2goIGpRdWVyeS5leHByLm1hdGNoLmJvb2wuc291cmNlLm1hdGNoKCAvXFx3Ky9nICksIGZ1bmN0aW9uKCBpLCBuYW1lICkge1xuXHR2YXIgZ2V0dGVyID0gYXR0ckhhbmRsZVsgbmFtZSBdIHx8IGpRdWVyeS5maW5kLmF0dHI7XG5cblx0YXR0ckhhbmRsZVsgbmFtZSBdID0gZnVuY3Rpb24oIGVsZW0sIG5hbWUsIGlzWE1MICkge1xuXHRcdHZhciByZXQsIGhhbmRsZTtcblx0XHRpZiAoICFpc1hNTCApIHtcblx0XHRcdC8vIEF2b2lkIGFuIGluZmluaXRlIGxvb3AgYnkgdGVtcG9yYXJpbHkgcmVtb3ZpbmcgdGhpcyBmdW5jdGlvbiBmcm9tIHRoZSBnZXR0ZXJcblx0XHRcdGhhbmRsZSA9IGF0dHJIYW5kbGVbIG5hbWUgXTtcblx0XHRcdGF0dHJIYW5kbGVbIG5hbWUgXSA9IHJldDtcblx0XHRcdHJldCA9IGdldHRlciggZWxlbSwgbmFtZSwgaXNYTUwgKSAhPSBudWxsID9cblx0XHRcdFx0bmFtZS50b0xvd2VyQ2FzZSgpIDpcblx0XHRcdFx0bnVsbDtcblx0XHRcdGF0dHJIYW5kbGVbIG5hbWUgXSA9IGhhbmRsZTtcblx0XHR9XG5cdFx0cmV0dXJuIHJldDtcblx0fTtcbn0pO1xuXG5cblxuXG52YXIgcmZvY3VzYWJsZSA9IC9eKD86aW5wdXR8c2VsZWN0fHRleHRhcmVhfGJ1dHRvbikkL2k7XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHRwcm9wOiBmdW5jdGlvbiggbmFtZSwgdmFsdWUgKSB7XG5cdFx0cmV0dXJuIGFjY2VzcyggdGhpcywgalF1ZXJ5LnByb3AsIG5hbWUsIHZhbHVlLCBhcmd1bWVudHMubGVuZ3RoID4gMSApO1xuXHR9LFxuXG5cdHJlbW92ZVByb3A6IGZ1bmN0aW9uKCBuYW1lICkge1xuXHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHRkZWxldGUgdGhpc1sgalF1ZXJ5LnByb3BGaXhbIG5hbWUgXSB8fCBuYW1lIF07XG5cdFx0fSk7XG5cdH1cbn0pO1xuXG5qUXVlcnkuZXh0ZW5kKHtcblx0cHJvcEZpeDoge1xuXHRcdFwiZm9yXCI6IFwiaHRtbEZvclwiLFxuXHRcdFwiY2xhc3NcIjogXCJjbGFzc05hbWVcIlxuXHR9LFxuXG5cdHByb3A6IGZ1bmN0aW9uKCBlbGVtLCBuYW1lLCB2YWx1ZSApIHtcblx0XHR2YXIgcmV0LCBob29rcywgbm90eG1sLFxuXHRcdFx0blR5cGUgPSBlbGVtLm5vZGVUeXBlO1xuXG5cdFx0Ly8gRG9uJ3QgZ2V0L3NldCBwcm9wZXJ0aWVzIG9uIHRleHQsIGNvbW1lbnQgYW5kIGF0dHJpYnV0ZSBub2Rlc1xuXHRcdGlmICggIWVsZW0gfHwgblR5cGUgPT09IDMgfHwgblR5cGUgPT09IDggfHwgblR5cGUgPT09IDIgKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0bm90eG1sID0gblR5cGUgIT09IDEgfHwgIWpRdWVyeS5pc1hNTERvYyggZWxlbSApO1xuXG5cdFx0aWYgKCBub3R4bWwgKSB7XG5cdFx0XHQvLyBGaXggbmFtZSBhbmQgYXR0YWNoIGhvb2tzXG5cdFx0XHRuYW1lID0galF1ZXJ5LnByb3BGaXhbIG5hbWUgXSB8fCBuYW1lO1xuXHRcdFx0aG9va3MgPSBqUXVlcnkucHJvcEhvb2tzWyBuYW1lIF07XG5cdFx0fVxuXG5cdFx0aWYgKCB2YWx1ZSAhPT0gdW5kZWZpbmVkICkge1xuXHRcdFx0cmV0dXJuIGhvb2tzICYmIFwic2V0XCIgaW4gaG9va3MgJiYgKHJldCA9IGhvb2tzLnNldCggZWxlbSwgdmFsdWUsIG5hbWUgKSkgIT09IHVuZGVmaW5lZCA/XG5cdFx0XHRcdHJldCA6XG5cdFx0XHRcdCggZWxlbVsgbmFtZSBdID0gdmFsdWUgKTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHRyZXR1cm4gaG9va3MgJiYgXCJnZXRcIiBpbiBob29rcyAmJiAocmV0ID0gaG9va3MuZ2V0KCBlbGVtLCBuYW1lICkpICE9PSBudWxsID9cblx0XHRcdFx0cmV0IDpcblx0XHRcdFx0ZWxlbVsgbmFtZSBdO1xuXHRcdH1cblx0fSxcblxuXHRwcm9wSG9va3M6IHtcblx0XHR0YWJJbmRleDoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdFx0cmV0dXJuIGVsZW0uaGFzQXR0cmlidXRlKCBcInRhYmluZGV4XCIgKSB8fCByZm9jdXNhYmxlLnRlc3QoIGVsZW0ubm9kZU5hbWUgKSB8fCBlbGVtLmhyZWYgP1xuXHRcdFx0XHRcdGVsZW0udGFiSW5kZXggOlxuXHRcdFx0XHRcdC0xO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxufSk7XG5cbmlmICggIXN1cHBvcnQub3B0U2VsZWN0ZWQgKSB7XG5cdGpRdWVyeS5wcm9wSG9va3Muc2VsZWN0ZWQgPSB7XG5cdFx0Z2V0OiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdHZhciBwYXJlbnQgPSBlbGVtLnBhcmVudE5vZGU7XG5cdFx0XHRpZiAoIHBhcmVudCAmJiBwYXJlbnQucGFyZW50Tm9kZSApIHtcblx0XHRcdFx0cGFyZW50LnBhcmVudE5vZGUuc2VsZWN0ZWRJbmRleDtcblx0XHRcdH1cblx0XHRcdHJldHVybiBudWxsO1xuXHRcdH1cblx0fTtcbn1cblxualF1ZXJ5LmVhY2goW1xuXHRcInRhYkluZGV4XCIsXG5cdFwicmVhZE9ubHlcIixcblx0XCJtYXhMZW5ndGhcIixcblx0XCJjZWxsU3BhY2luZ1wiLFxuXHRcImNlbGxQYWRkaW5nXCIsXG5cdFwicm93U3BhblwiLFxuXHRcImNvbFNwYW5cIixcblx0XCJ1c2VNYXBcIixcblx0XCJmcmFtZUJvcmRlclwiLFxuXHRcImNvbnRlbnRFZGl0YWJsZVwiXG5dLCBmdW5jdGlvbigpIHtcblx0alF1ZXJ5LnByb3BGaXhbIHRoaXMudG9Mb3dlckNhc2UoKSBdID0gdGhpcztcbn0pO1xuXG5cblxuXG52YXIgcmNsYXNzID0gL1tcXHRcXHJcXG5cXGZdL2c7XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHRhZGRDbGFzczogZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdHZhciBjbGFzc2VzLCBlbGVtLCBjdXIsIGNsYXp6LCBqLCBmaW5hbFZhbHVlLFxuXHRcdFx0cHJvY2VlZCA9IHR5cGVvZiB2YWx1ZSA9PT0gXCJzdHJpbmdcIiAmJiB2YWx1ZSxcblx0XHRcdGkgPSAwLFxuXHRcdFx0bGVuID0gdGhpcy5sZW5ndGg7XG5cblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCB2YWx1ZSApICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbiggaiApIHtcblx0XHRcdFx0alF1ZXJ5KCB0aGlzICkuYWRkQ2xhc3MoIHZhbHVlLmNhbGwoIHRoaXMsIGosIHRoaXMuY2xhc3NOYW1lICkgKTtcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdGlmICggcHJvY2VlZCApIHtcblx0XHRcdC8vIFRoZSBkaXNqdW5jdGlvbiBoZXJlIGlzIGZvciBiZXR0ZXIgY29tcHJlc3NpYmlsaXR5IChzZWUgcmVtb3ZlQ2xhc3MpXG5cdFx0XHRjbGFzc2VzID0gKCB2YWx1ZSB8fCBcIlwiICkubWF0Y2goIHJub3R3aGl0ZSApIHx8IFtdO1xuXG5cdFx0XHRmb3IgKCA7IGkgPCBsZW47IGkrKyApIHtcblx0XHRcdFx0ZWxlbSA9IHRoaXNbIGkgXTtcblx0XHRcdFx0Y3VyID0gZWxlbS5ub2RlVHlwZSA9PT0gMSAmJiAoIGVsZW0uY2xhc3NOYW1lID9cblx0XHRcdFx0XHQoIFwiIFwiICsgZWxlbS5jbGFzc05hbWUgKyBcIiBcIiApLnJlcGxhY2UoIHJjbGFzcywgXCIgXCIgKSA6XG5cdFx0XHRcdFx0XCIgXCJcblx0XHRcdFx0KTtcblxuXHRcdFx0XHRpZiAoIGN1ciApIHtcblx0XHRcdFx0XHRqID0gMDtcblx0XHRcdFx0XHR3aGlsZSAoIChjbGF6eiA9IGNsYXNzZXNbaisrXSkgKSB7XG5cdFx0XHRcdFx0XHRpZiAoIGN1ci5pbmRleE9mKCBcIiBcIiArIGNsYXp6ICsgXCIgXCIgKSA8IDAgKSB7XG5cdFx0XHRcdFx0XHRcdGN1ciArPSBjbGF6eiArIFwiIFwiO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIG9ubHkgYXNzaWduIGlmIGRpZmZlcmVudCB0byBhdm9pZCB1bm5lZWRlZCByZW5kZXJpbmcuXG5cdFx0XHRcdFx0ZmluYWxWYWx1ZSA9IGpRdWVyeS50cmltKCBjdXIgKTtcblx0XHRcdFx0XHRpZiAoIGVsZW0uY2xhc3NOYW1lICE9PSBmaW5hbFZhbHVlICkge1xuXHRcdFx0XHRcdFx0ZWxlbS5jbGFzc05hbWUgPSBmaW5hbFZhbHVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHJlbW92ZUNsYXNzOiBmdW5jdGlvbiggdmFsdWUgKSB7XG5cdFx0dmFyIGNsYXNzZXMsIGVsZW0sIGN1ciwgY2xhenosIGosIGZpbmFsVmFsdWUsXG5cdFx0XHRwcm9jZWVkID0gYXJndW1lbnRzLmxlbmd0aCA9PT0gMCB8fCB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgJiYgdmFsdWUsXG5cdFx0XHRpID0gMCxcblx0XHRcdGxlbiA9IHRoaXMubGVuZ3RoO1xuXG5cdFx0aWYgKCBqUXVlcnkuaXNGdW5jdGlvbiggdmFsdWUgKSApIHtcblx0XHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oIGogKSB7XG5cdFx0XHRcdGpRdWVyeSggdGhpcyApLnJlbW92ZUNsYXNzKCB2YWx1ZS5jYWxsKCB0aGlzLCBqLCB0aGlzLmNsYXNzTmFtZSApICk7XG5cdFx0XHR9KTtcblx0XHR9XG5cdFx0aWYgKCBwcm9jZWVkICkge1xuXHRcdFx0Y2xhc3NlcyA9ICggdmFsdWUgfHwgXCJcIiApLm1hdGNoKCBybm90d2hpdGUgKSB8fCBbXTtcblxuXHRcdFx0Zm9yICggOyBpIDwgbGVuOyBpKysgKSB7XG5cdFx0XHRcdGVsZW0gPSB0aGlzWyBpIF07XG5cdFx0XHRcdC8vIFRoaXMgZXhwcmVzc2lvbiBpcyBoZXJlIGZvciBiZXR0ZXIgY29tcHJlc3NpYmlsaXR5IChzZWUgYWRkQ2xhc3MpXG5cdFx0XHRcdGN1ciA9IGVsZW0ubm9kZVR5cGUgPT09IDEgJiYgKCBlbGVtLmNsYXNzTmFtZSA/XG5cdFx0XHRcdFx0KCBcIiBcIiArIGVsZW0uY2xhc3NOYW1lICsgXCIgXCIgKS5yZXBsYWNlKCByY2xhc3MsIFwiIFwiICkgOlxuXHRcdFx0XHRcdFwiXCJcblx0XHRcdFx0KTtcblxuXHRcdFx0XHRpZiAoIGN1ciApIHtcblx0XHRcdFx0XHRqID0gMDtcblx0XHRcdFx0XHR3aGlsZSAoIChjbGF6eiA9IGNsYXNzZXNbaisrXSkgKSB7XG5cdFx0XHRcdFx0XHQvLyBSZW1vdmUgKmFsbCogaW5zdGFuY2VzXG5cdFx0XHRcdFx0XHR3aGlsZSAoIGN1ci5pbmRleE9mKCBcIiBcIiArIGNsYXp6ICsgXCIgXCIgKSA+PSAwICkge1xuXHRcdFx0XHRcdFx0XHRjdXIgPSBjdXIucmVwbGFjZSggXCIgXCIgKyBjbGF6eiArIFwiIFwiLCBcIiBcIiApO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdC8vIE9ubHkgYXNzaWduIGlmIGRpZmZlcmVudCB0byBhdm9pZCB1bm5lZWRlZCByZW5kZXJpbmcuXG5cdFx0XHRcdFx0ZmluYWxWYWx1ZSA9IHZhbHVlID8galF1ZXJ5LnRyaW0oIGN1ciApIDogXCJcIjtcblx0XHRcdFx0XHRpZiAoIGVsZW0uY2xhc3NOYW1lICE9PSBmaW5hbFZhbHVlICkge1xuXHRcdFx0XHRcdFx0ZWxlbS5jbGFzc05hbWUgPSBmaW5hbFZhbHVlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHRvZ2dsZUNsYXNzOiBmdW5jdGlvbiggdmFsdWUsIHN0YXRlVmFsICkge1xuXHRcdHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuXG5cdFx0aWYgKCB0eXBlb2Ygc3RhdGVWYWwgPT09IFwiYm9vbGVhblwiICYmIHR5cGUgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRyZXR1cm4gc3RhdGVWYWwgPyB0aGlzLmFkZENsYXNzKCB2YWx1ZSApIDogdGhpcy5yZW1vdmVDbGFzcyggdmFsdWUgKTtcblx0XHR9XG5cblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCB2YWx1ZSApICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbiggaSApIHtcblx0XHRcdFx0alF1ZXJ5KCB0aGlzICkudG9nZ2xlQ2xhc3MoIHZhbHVlLmNhbGwodGhpcywgaSwgdGhpcy5jbGFzc05hbWUsIHN0YXRlVmFsKSwgc3RhdGVWYWwgKTtcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHRpZiAoIHR5cGUgPT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRcdC8vIFRvZ2dsZSBpbmRpdmlkdWFsIGNsYXNzIG5hbWVzXG5cdFx0XHRcdHZhciBjbGFzc05hbWUsXG5cdFx0XHRcdFx0aSA9IDAsXG5cdFx0XHRcdFx0c2VsZiA9IGpRdWVyeSggdGhpcyApLFxuXHRcdFx0XHRcdGNsYXNzTmFtZXMgPSB2YWx1ZS5tYXRjaCggcm5vdHdoaXRlICkgfHwgW107XG5cblx0XHRcdFx0d2hpbGUgKCAoY2xhc3NOYW1lID0gY2xhc3NOYW1lc1sgaSsrIF0pICkge1xuXHRcdFx0XHRcdC8vIENoZWNrIGVhY2ggY2xhc3NOYW1lIGdpdmVuLCBzcGFjZSBzZXBhcmF0ZWQgbGlzdFxuXHRcdFx0XHRcdGlmICggc2VsZi5oYXNDbGFzcyggY2xhc3NOYW1lICkgKSB7XG5cdFx0XHRcdFx0XHRzZWxmLnJlbW92ZUNsYXNzKCBjbGFzc05hbWUgKTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0c2VsZi5hZGRDbGFzcyggY2xhc3NOYW1lICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdC8vIFRvZ2dsZSB3aG9sZSBjbGFzcyBuYW1lXG5cdFx0XHR9IGVsc2UgaWYgKCB0eXBlID09PSBzdHJ1bmRlZmluZWQgfHwgdHlwZSA9PT0gXCJib29sZWFuXCIgKSB7XG5cdFx0XHRcdGlmICggdGhpcy5jbGFzc05hbWUgKSB7XG5cdFx0XHRcdFx0Ly8gc3RvcmUgY2xhc3NOYW1lIGlmIHNldFxuXHRcdFx0XHRcdGRhdGFfcHJpdi5zZXQoIHRoaXMsIFwiX19jbGFzc05hbWVfX1wiLCB0aGlzLmNsYXNzTmFtZSApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gSWYgdGhlIGVsZW1lbnQgaGFzIGEgY2xhc3MgbmFtZSBvciBpZiB3ZSdyZSBwYXNzZWQgYGZhbHNlYCxcblx0XHRcdFx0Ly8gdGhlbiByZW1vdmUgdGhlIHdob2xlIGNsYXNzbmFtZSAoaWYgdGhlcmUgd2FzIG9uZSwgdGhlIGFib3ZlIHNhdmVkIGl0KS5cblx0XHRcdFx0Ly8gT3RoZXJ3aXNlIGJyaW5nIGJhY2sgd2hhdGV2ZXIgd2FzIHByZXZpb3VzbHkgc2F2ZWQgKGlmIGFueXRoaW5nKSxcblx0XHRcdFx0Ly8gZmFsbGluZyBiYWNrIHRvIHRoZSBlbXB0eSBzdHJpbmcgaWYgbm90aGluZyB3YXMgc3RvcmVkLlxuXHRcdFx0XHR0aGlzLmNsYXNzTmFtZSA9IHRoaXMuY2xhc3NOYW1lIHx8IHZhbHVlID09PSBmYWxzZSA/IFwiXCIgOiBkYXRhX3ByaXYuZ2V0KCB0aGlzLCBcIl9fY2xhc3NOYW1lX19cIiApIHx8IFwiXCI7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH0sXG5cblx0aGFzQ2xhc3M6IGZ1bmN0aW9uKCBzZWxlY3RvciApIHtcblx0XHR2YXIgY2xhc3NOYW1lID0gXCIgXCIgKyBzZWxlY3RvciArIFwiIFwiLFxuXHRcdFx0aSA9IDAsXG5cdFx0XHRsID0gdGhpcy5sZW5ndGg7XG5cdFx0Zm9yICggOyBpIDwgbDsgaSsrICkge1xuXHRcdFx0aWYgKCB0aGlzW2ldLm5vZGVUeXBlID09PSAxICYmIChcIiBcIiArIHRoaXNbaV0uY2xhc3NOYW1lICsgXCIgXCIpLnJlcGxhY2UocmNsYXNzLCBcIiBcIikuaW5kZXhPZiggY2xhc3NOYW1lICkgPj0gMCApIHtcblx0XHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG59KTtcblxuXG5cblxudmFyIHJyZXR1cm4gPSAvXFxyL2c7XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHR2YWw6IGZ1bmN0aW9uKCB2YWx1ZSApIHtcblx0XHR2YXIgaG9va3MsIHJldCwgaXNGdW5jdGlvbixcblx0XHRcdGVsZW0gPSB0aGlzWzBdO1xuXG5cdFx0aWYgKCAhYXJndW1lbnRzLmxlbmd0aCApIHtcblx0XHRcdGlmICggZWxlbSApIHtcblx0XHRcdFx0aG9va3MgPSBqUXVlcnkudmFsSG9va3NbIGVsZW0udHlwZSBdIHx8IGpRdWVyeS52YWxIb29rc1sgZWxlbS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpIF07XG5cblx0XHRcdFx0aWYgKCBob29rcyAmJiBcImdldFwiIGluIGhvb2tzICYmIChyZXQgPSBob29rcy5nZXQoIGVsZW0sIFwidmFsdWVcIiApKSAhPT0gdW5kZWZpbmVkICkge1xuXHRcdFx0XHRcdHJldHVybiByZXQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXQgPSBlbGVtLnZhbHVlO1xuXG5cdFx0XHRcdHJldHVybiB0eXBlb2YgcmV0ID09PSBcInN0cmluZ1wiID9cblx0XHRcdFx0XHQvLyBIYW5kbGUgbW9zdCBjb21tb24gc3RyaW5nIGNhc2VzXG5cdFx0XHRcdFx0cmV0LnJlcGxhY2UocnJldHVybiwgXCJcIikgOlxuXHRcdFx0XHRcdC8vIEhhbmRsZSBjYXNlcyB3aGVyZSB2YWx1ZSBpcyBudWxsL3VuZGVmIG9yIG51bWJlclxuXHRcdFx0XHRcdHJldCA9PSBudWxsID8gXCJcIiA6IHJldDtcblx0XHRcdH1cblxuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGlzRnVuY3Rpb24gPSBqUXVlcnkuaXNGdW5jdGlvbiggdmFsdWUgKTtcblxuXHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oIGkgKSB7XG5cdFx0XHR2YXIgdmFsO1xuXG5cdFx0XHRpZiAoIHRoaXMubm9kZVR5cGUgIT09IDEgKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblxuXHRcdFx0aWYgKCBpc0Z1bmN0aW9uICkge1xuXHRcdFx0XHR2YWwgPSB2YWx1ZS5jYWxsKCB0aGlzLCBpLCBqUXVlcnkoIHRoaXMgKS52YWwoKSApO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dmFsID0gdmFsdWU7XG5cdFx0XHR9XG5cblx0XHRcdC8vIFRyZWF0IG51bGwvdW5kZWZpbmVkIGFzIFwiXCI7IGNvbnZlcnQgbnVtYmVycyB0byBzdHJpbmdcblx0XHRcdGlmICggdmFsID09IG51bGwgKSB7XG5cdFx0XHRcdHZhbCA9IFwiXCI7XG5cblx0XHRcdH0gZWxzZSBpZiAoIHR5cGVvZiB2YWwgPT09IFwibnVtYmVyXCIgKSB7XG5cdFx0XHRcdHZhbCArPSBcIlwiO1xuXG5cdFx0XHR9IGVsc2UgaWYgKCBqUXVlcnkuaXNBcnJheSggdmFsICkgKSB7XG5cdFx0XHRcdHZhbCA9IGpRdWVyeS5tYXAoIHZhbCwgZnVuY3Rpb24oIHZhbHVlICkge1xuXHRcdFx0XHRcdHJldHVybiB2YWx1ZSA9PSBudWxsID8gXCJcIiA6IHZhbHVlICsgXCJcIjtcblx0XHRcdFx0fSk7XG5cdFx0XHR9XG5cblx0XHRcdGhvb2tzID0galF1ZXJ5LnZhbEhvb2tzWyB0aGlzLnR5cGUgXSB8fCBqUXVlcnkudmFsSG9va3NbIHRoaXMubm9kZU5hbWUudG9Mb3dlckNhc2UoKSBdO1xuXG5cdFx0XHQvLyBJZiBzZXQgcmV0dXJucyB1bmRlZmluZWQsIGZhbGwgYmFjayB0byBub3JtYWwgc2V0dGluZ1xuXHRcdFx0aWYgKCAhaG9va3MgfHwgIShcInNldFwiIGluIGhvb2tzKSB8fCBob29rcy5zZXQoIHRoaXMsIHZhbCwgXCJ2YWx1ZVwiICkgPT09IHVuZGVmaW5lZCApIHtcblx0XHRcdFx0dGhpcy52YWx1ZSA9IHZhbDtcblx0XHRcdH1cblx0XHR9KTtcblx0fVxufSk7XG5cbmpRdWVyeS5leHRlbmQoe1xuXHR2YWxIb29rczoge1xuXHRcdG9wdGlvbjoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdFx0dmFyIHZhbCA9IGpRdWVyeS5maW5kLmF0dHIoIGVsZW0sIFwidmFsdWVcIiApO1xuXHRcdFx0XHRyZXR1cm4gdmFsICE9IG51bGwgP1xuXHRcdFx0XHRcdHZhbCA6XG5cdFx0XHRcdFx0Ly8gU3VwcG9ydDogSUUxMC0xMStcblx0XHRcdFx0XHQvLyBvcHRpb24udGV4dCB0aHJvd3MgZXhjZXB0aW9ucyAoIzE0Njg2LCAjMTQ4NTgpXG5cdFx0XHRcdFx0alF1ZXJ5LnRyaW0oIGpRdWVyeS50ZXh0KCBlbGVtICkgKTtcblx0XHRcdH1cblx0XHR9LFxuXHRcdHNlbGVjdDoge1xuXHRcdFx0Z2V0OiBmdW5jdGlvbiggZWxlbSApIHtcblx0XHRcdFx0dmFyIHZhbHVlLCBvcHRpb24sXG5cdFx0XHRcdFx0b3B0aW9ucyA9IGVsZW0ub3B0aW9ucyxcblx0XHRcdFx0XHRpbmRleCA9IGVsZW0uc2VsZWN0ZWRJbmRleCxcblx0XHRcdFx0XHRvbmUgPSBlbGVtLnR5cGUgPT09IFwic2VsZWN0LW9uZVwiIHx8IGluZGV4IDwgMCxcblx0XHRcdFx0XHR2YWx1ZXMgPSBvbmUgPyBudWxsIDogW10sXG5cdFx0XHRcdFx0bWF4ID0gb25lID8gaW5kZXggKyAxIDogb3B0aW9ucy5sZW5ndGgsXG5cdFx0XHRcdFx0aSA9IGluZGV4IDwgMCA/XG5cdFx0XHRcdFx0XHRtYXggOlxuXHRcdFx0XHRcdFx0b25lID8gaW5kZXggOiAwO1xuXG5cdFx0XHRcdC8vIExvb3AgdGhyb3VnaCBhbGwgdGhlIHNlbGVjdGVkIG9wdGlvbnNcblx0XHRcdFx0Zm9yICggOyBpIDwgbWF4OyBpKysgKSB7XG5cdFx0XHRcdFx0b3B0aW9uID0gb3B0aW9uc1sgaSBdO1xuXG5cdFx0XHRcdFx0Ly8gSUU2LTkgZG9lc24ndCB1cGRhdGUgc2VsZWN0ZWQgYWZ0ZXIgZm9ybSByZXNldCAoIzI1NTEpXG5cdFx0XHRcdFx0aWYgKCAoIG9wdGlvbi5zZWxlY3RlZCB8fCBpID09PSBpbmRleCApICYmXG5cdFx0XHRcdFx0XHRcdC8vIERvbid0IHJldHVybiBvcHRpb25zIHRoYXQgYXJlIGRpc2FibGVkIG9yIGluIGEgZGlzYWJsZWQgb3B0Z3JvdXBcblx0XHRcdFx0XHRcdFx0KCBzdXBwb3J0Lm9wdERpc2FibGVkID8gIW9wdGlvbi5kaXNhYmxlZCA6IG9wdGlvbi5nZXRBdHRyaWJ1dGUoIFwiZGlzYWJsZWRcIiApID09PSBudWxsICkgJiZcblx0XHRcdFx0XHRcdFx0KCAhb3B0aW9uLnBhcmVudE5vZGUuZGlzYWJsZWQgfHwgIWpRdWVyeS5ub2RlTmFtZSggb3B0aW9uLnBhcmVudE5vZGUsIFwib3B0Z3JvdXBcIiApICkgKSB7XG5cblx0XHRcdFx0XHRcdC8vIEdldCB0aGUgc3BlY2lmaWMgdmFsdWUgZm9yIHRoZSBvcHRpb25cblx0XHRcdFx0XHRcdHZhbHVlID0galF1ZXJ5KCBvcHRpb24gKS52YWwoKTtcblxuXHRcdFx0XHRcdFx0Ly8gV2UgZG9uJ3QgbmVlZCBhbiBhcnJheSBmb3Igb25lIHNlbGVjdHNcblx0XHRcdFx0XHRcdGlmICggb25lICkge1xuXHRcdFx0XHRcdFx0XHRyZXR1cm4gdmFsdWU7XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdC8vIE11bHRpLVNlbGVjdHMgcmV0dXJuIGFuIGFycmF5XG5cdFx0XHRcdFx0XHR2YWx1ZXMucHVzaCggdmFsdWUgKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gdmFsdWVzO1xuXHRcdFx0fSxcblxuXHRcdFx0c2V0OiBmdW5jdGlvbiggZWxlbSwgdmFsdWUgKSB7XG5cdFx0XHRcdHZhciBvcHRpb25TZXQsIG9wdGlvbixcblx0XHRcdFx0XHRvcHRpb25zID0gZWxlbS5vcHRpb25zLFxuXHRcdFx0XHRcdHZhbHVlcyA9IGpRdWVyeS5tYWtlQXJyYXkoIHZhbHVlICksXG5cdFx0XHRcdFx0aSA9IG9wdGlvbnMubGVuZ3RoO1xuXG5cdFx0XHRcdHdoaWxlICggaS0tICkge1xuXHRcdFx0XHRcdG9wdGlvbiA9IG9wdGlvbnNbIGkgXTtcblx0XHRcdFx0XHRpZiAoIChvcHRpb24uc2VsZWN0ZWQgPSBqUXVlcnkuaW5BcnJheSggb3B0aW9uLnZhbHVlLCB2YWx1ZXMgKSA+PSAwKSApIHtcblx0XHRcdFx0XHRcdG9wdGlvblNldCA9IHRydWU7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gRm9yY2UgYnJvd3NlcnMgdG8gYmVoYXZlIGNvbnNpc3RlbnRseSB3aGVuIG5vbi1tYXRjaGluZyB2YWx1ZSBpcyBzZXRcblx0XHRcdFx0aWYgKCAhb3B0aW9uU2V0ICkge1xuXHRcdFx0XHRcdGVsZW0uc2VsZWN0ZWRJbmRleCA9IC0xO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJldHVybiB2YWx1ZXM7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG59KTtcblxuLy8gUmFkaW9zIGFuZCBjaGVja2JveGVzIGdldHRlci9zZXR0ZXJcbmpRdWVyeS5lYWNoKFsgXCJyYWRpb1wiLCBcImNoZWNrYm94XCIgXSwgZnVuY3Rpb24oKSB7XG5cdGpRdWVyeS52YWxIb29rc1sgdGhpcyBdID0ge1xuXHRcdHNldDogZnVuY3Rpb24oIGVsZW0sIHZhbHVlICkge1xuXHRcdFx0aWYgKCBqUXVlcnkuaXNBcnJheSggdmFsdWUgKSApIHtcblx0XHRcdFx0cmV0dXJuICggZWxlbS5jaGVja2VkID0galF1ZXJ5LmluQXJyYXkoIGpRdWVyeShlbGVtKS52YWwoKSwgdmFsdWUgKSA+PSAwICk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9O1xuXHRpZiAoICFzdXBwb3J0LmNoZWNrT24gKSB7XG5cdFx0alF1ZXJ5LnZhbEhvb2tzWyB0aGlzIF0uZ2V0ID0gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdFx0XHRyZXR1cm4gZWxlbS5nZXRBdHRyaWJ1dGUoXCJ2YWx1ZVwiKSA9PT0gbnVsbCA/IFwib25cIiA6IGVsZW0udmFsdWU7XG5cdFx0fTtcblx0fVxufSk7XG5cblxuXG5cbi8vIFJldHVybiBqUXVlcnkgZm9yIGF0dHJpYnV0ZXMtb25seSBpbmNsdXNpb25cblxuXG5qUXVlcnkuZWFjaCggKFwiYmx1ciBmb2N1cyBmb2N1c2luIGZvY3Vzb3V0IGxvYWQgcmVzaXplIHNjcm9sbCB1bmxvYWQgY2xpY2sgZGJsY2xpY2sgXCIgK1xuXHRcIm1vdXNlZG93biBtb3VzZXVwIG1vdXNlbW92ZSBtb3VzZW92ZXIgbW91c2VvdXQgbW91c2VlbnRlciBtb3VzZWxlYXZlIFwiICtcblx0XCJjaGFuZ2Ugc2VsZWN0IHN1Ym1pdCBrZXlkb3duIGtleXByZXNzIGtleXVwIGVycm9yIGNvbnRleHRtZW51XCIpLnNwbGl0KFwiIFwiKSwgZnVuY3Rpb24oIGksIG5hbWUgKSB7XG5cblx0Ly8gSGFuZGxlIGV2ZW50IGJpbmRpbmdcblx0alF1ZXJ5LmZuWyBuYW1lIF0gPSBmdW5jdGlvbiggZGF0YSwgZm4gKSB7XG5cdFx0cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPiAwID9cblx0XHRcdHRoaXMub24oIG5hbWUsIG51bGwsIGRhdGEsIGZuICkgOlxuXHRcdFx0dGhpcy50cmlnZ2VyKCBuYW1lICk7XG5cdH07XG59KTtcblxualF1ZXJ5LmZuLmV4dGVuZCh7XG5cdGhvdmVyOiBmdW5jdGlvbiggZm5PdmVyLCBmbk91dCApIHtcblx0XHRyZXR1cm4gdGhpcy5tb3VzZWVudGVyKCBmbk92ZXIgKS5tb3VzZWxlYXZlKCBmbk91dCB8fCBmbk92ZXIgKTtcblx0fSxcblxuXHRiaW5kOiBmdW5jdGlvbiggdHlwZXMsIGRhdGEsIGZuICkge1xuXHRcdHJldHVybiB0aGlzLm9uKCB0eXBlcywgbnVsbCwgZGF0YSwgZm4gKTtcblx0fSxcblx0dW5iaW5kOiBmdW5jdGlvbiggdHlwZXMsIGZuICkge1xuXHRcdHJldHVybiB0aGlzLm9mZiggdHlwZXMsIG51bGwsIGZuICk7XG5cdH0sXG5cblx0ZGVsZWdhdGU6IGZ1bmN0aW9uKCBzZWxlY3RvciwgdHlwZXMsIGRhdGEsIGZuICkge1xuXHRcdHJldHVybiB0aGlzLm9uKCB0eXBlcywgc2VsZWN0b3IsIGRhdGEsIGZuICk7XG5cdH0sXG5cdHVuZGVsZWdhdGU6IGZ1bmN0aW9uKCBzZWxlY3RvciwgdHlwZXMsIGZuICkge1xuXHRcdC8vICggbmFtZXNwYWNlICkgb3IgKCBzZWxlY3RvciwgdHlwZXMgWywgZm5dIClcblx0XHRyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA9PT0gMSA/IHRoaXMub2ZmKCBzZWxlY3RvciwgXCIqKlwiICkgOiB0aGlzLm9mZiggdHlwZXMsIHNlbGVjdG9yIHx8IFwiKipcIiwgZm4gKTtcblx0fVxufSk7XG5cblxudmFyIG5vbmNlID0galF1ZXJ5Lm5vdygpO1xuXG52YXIgcnF1ZXJ5ID0gKC9cXD8vKTtcblxuXG5cbi8vIFN1cHBvcnQ6IEFuZHJvaWQgMi4zXG4vLyBXb3JrYXJvdW5kIGZhaWx1cmUgdG8gc3RyaW5nLWNhc3QgbnVsbCBpbnB1dFxualF1ZXJ5LnBhcnNlSlNPTiA9IGZ1bmN0aW9uKCBkYXRhICkge1xuXHRyZXR1cm4gSlNPTi5wYXJzZSggZGF0YSArIFwiXCIgKTtcbn07XG5cblxuLy8gQ3Jvc3MtYnJvd3NlciB4bWwgcGFyc2luZ1xualF1ZXJ5LnBhcnNlWE1MID0gZnVuY3Rpb24oIGRhdGEgKSB7XG5cdHZhciB4bWwsIHRtcDtcblx0aWYgKCAhZGF0YSB8fCB0eXBlb2YgZGF0YSAhPT0gXCJzdHJpbmdcIiApIHtcblx0XHRyZXR1cm4gbnVsbDtcblx0fVxuXG5cdC8vIFN1cHBvcnQ6IElFOVxuXHR0cnkge1xuXHRcdHRtcCA9IG5ldyBET01QYXJzZXIoKTtcblx0XHR4bWwgPSB0bXAucGFyc2VGcm9tU3RyaW5nKCBkYXRhLCBcInRleHQveG1sXCIgKTtcblx0fSBjYXRjaCAoIGUgKSB7XG5cdFx0eG1sID0gdW5kZWZpbmVkO1xuXHR9XG5cblx0aWYgKCAheG1sIHx8IHhtbC5nZXRFbGVtZW50c0J5VGFnTmFtZSggXCJwYXJzZXJlcnJvclwiICkubGVuZ3RoICkge1xuXHRcdGpRdWVyeS5lcnJvciggXCJJbnZhbGlkIFhNTDogXCIgKyBkYXRhICk7XG5cdH1cblx0cmV0dXJuIHhtbDtcbn07XG5cblxudmFyXG5cdHJoYXNoID0gLyMuKiQvLFxuXHRydHMgPSAvKFs/Jl0pXz1bXiZdKi8sXG5cdHJoZWFkZXJzID0gL14oLio/KTpbIFxcdF0qKFteXFxyXFxuXSopJC9tZyxcblx0Ly8gIzc2NTMsICM4MTI1LCAjODE1MjogbG9jYWwgcHJvdG9jb2wgZGV0ZWN0aW9uXG5cdHJsb2NhbFByb3RvY29sID0gL14oPzphYm91dHxhcHB8YXBwLXN0b3JhZ2V8ListZXh0ZW5zaW9ufGZpbGV8cmVzfHdpZGdldCk6JC8sXG5cdHJub0NvbnRlbnQgPSAvXig/OkdFVHxIRUFEKSQvLFxuXHRycHJvdG9jb2wgPSAvXlxcL1xcLy8sXG5cdHJ1cmwgPSAvXihbXFx3ListXSs6KSg/OlxcL1xcLyg/OlteXFwvPyNdKkB8KShbXlxcLz8jOl0qKSg/OjooXFxkKyl8KXwpLyxcblxuXHQvKiBQcmVmaWx0ZXJzXG5cdCAqIDEpIFRoZXkgYXJlIHVzZWZ1bCB0byBpbnRyb2R1Y2UgY3VzdG9tIGRhdGFUeXBlcyAoc2VlIGFqYXgvanNvbnAuanMgZm9yIGFuIGV4YW1wbGUpXG5cdCAqIDIpIFRoZXNlIGFyZSBjYWxsZWQ6XG5cdCAqICAgIC0gQkVGT1JFIGFza2luZyBmb3IgYSB0cmFuc3BvcnRcblx0ICogICAgLSBBRlRFUiBwYXJhbSBzZXJpYWxpemF0aW9uIChzLmRhdGEgaXMgYSBzdHJpbmcgaWYgcy5wcm9jZXNzRGF0YSBpcyB0cnVlKVxuXHQgKiAzKSBrZXkgaXMgdGhlIGRhdGFUeXBlXG5cdCAqIDQpIHRoZSBjYXRjaGFsbCBzeW1ib2wgXCIqXCIgY2FuIGJlIHVzZWRcblx0ICogNSkgZXhlY3V0aW9uIHdpbGwgc3RhcnQgd2l0aCB0cmFuc3BvcnQgZGF0YVR5cGUgYW5kIFRIRU4gY29udGludWUgZG93biB0byBcIipcIiBpZiBuZWVkZWRcblx0ICovXG5cdHByZWZpbHRlcnMgPSB7fSxcblxuXHQvKiBUcmFuc3BvcnRzIGJpbmRpbmdzXG5cdCAqIDEpIGtleSBpcyB0aGUgZGF0YVR5cGVcblx0ICogMikgdGhlIGNhdGNoYWxsIHN5bWJvbCBcIipcIiBjYW4gYmUgdXNlZFxuXHQgKiAzKSBzZWxlY3Rpb24gd2lsbCBzdGFydCB3aXRoIHRyYW5zcG9ydCBkYXRhVHlwZSBhbmQgVEhFTiBnbyB0byBcIipcIiBpZiBuZWVkZWRcblx0ICovXG5cdHRyYW5zcG9ydHMgPSB7fSxcblxuXHQvLyBBdm9pZCBjb21tZW50LXByb2xvZyBjaGFyIHNlcXVlbmNlICgjMTAwOTgpOyBtdXN0IGFwcGVhc2UgbGludCBhbmQgZXZhZGUgY29tcHJlc3Npb25cblx0YWxsVHlwZXMgPSBcIiovXCIuY29uY2F0KCBcIipcIiApLFxuXG5cdC8vIERvY3VtZW50IGxvY2F0aW9uXG5cdGFqYXhMb2NhdGlvbiA9IHdpbmRvdy5sb2NhdGlvbi5ocmVmLFxuXG5cdC8vIFNlZ21lbnQgbG9jYXRpb24gaW50byBwYXJ0c1xuXHRhamF4TG9jUGFydHMgPSBydXJsLmV4ZWMoIGFqYXhMb2NhdGlvbi50b0xvd2VyQ2FzZSgpICkgfHwgW107XG5cbi8vIEJhc2UgXCJjb25zdHJ1Y3RvclwiIGZvciBqUXVlcnkuYWpheFByZWZpbHRlciBhbmQgalF1ZXJ5LmFqYXhUcmFuc3BvcnRcbmZ1bmN0aW9uIGFkZFRvUHJlZmlsdGVyc09yVHJhbnNwb3J0cyggc3RydWN0dXJlICkge1xuXG5cdC8vIGRhdGFUeXBlRXhwcmVzc2lvbiBpcyBvcHRpb25hbCBhbmQgZGVmYXVsdHMgdG8gXCIqXCJcblx0cmV0dXJuIGZ1bmN0aW9uKCBkYXRhVHlwZUV4cHJlc3Npb24sIGZ1bmMgKSB7XG5cblx0XHRpZiAoIHR5cGVvZiBkYXRhVHlwZUV4cHJlc3Npb24gIT09IFwic3RyaW5nXCIgKSB7XG5cdFx0XHRmdW5jID0gZGF0YVR5cGVFeHByZXNzaW9uO1xuXHRcdFx0ZGF0YVR5cGVFeHByZXNzaW9uID0gXCIqXCI7XG5cdFx0fVxuXG5cdFx0dmFyIGRhdGFUeXBlLFxuXHRcdFx0aSA9IDAsXG5cdFx0XHRkYXRhVHlwZXMgPSBkYXRhVHlwZUV4cHJlc3Npb24udG9Mb3dlckNhc2UoKS5tYXRjaCggcm5vdHdoaXRlICkgfHwgW107XG5cblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBmdW5jICkgKSB7XG5cdFx0XHQvLyBGb3IgZWFjaCBkYXRhVHlwZSBpbiB0aGUgZGF0YVR5cGVFeHByZXNzaW9uXG5cdFx0XHR3aGlsZSAoIChkYXRhVHlwZSA9IGRhdGFUeXBlc1tpKytdKSApIHtcblx0XHRcdFx0Ly8gUHJlcGVuZCBpZiByZXF1ZXN0ZWRcblx0XHRcdFx0aWYgKCBkYXRhVHlwZVswXSA9PT0gXCIrXCIgKSB7XG5cdFx0XHRcdFx0ZGF0YVR5cGUgPSBkYXRhVHlwZS5zbGljZSggMSApIHx8IFwiKlwiO1xuXHRcdFx0XHRcdChzdHJ1Y3R1cmVbIGRhdGFUeXBlIF0gPSBzdHJ1Y3R1cmVbIGRhdGFUeXBlIF0gfHwgW10pLnVuc2hpZnQoIGZ1bmMgKTtcblxuXHRcdFx0XHQvLyBPdGhlcndpc2UgYXBwZW5kXG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0KHN0cnVjdHVyZVsgZGF0YVR5cGUgXSA9IHN0cnVjdHVyZVsgZGF0YVR5cGUgXSB8fCBbXSkucHVzaCggZnVuYyApO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9O1xufVxuXG4vLyBCYXNlIGluc3BlY3Rpb24gZnVuY3Rpb24gZm9yIHByZWZpbHRlcnMgYW5kIHRyYW5zcG9ydHNcbmZ1bmN0aW9uIGluc3BlY3RQcmVmaWx0ZXJzT3JUcmFuc3BvcnRzKCBzdHJ1Y3R1cmUsIG9wdGlvbnMsIG9yaWdpbmFsT3B0aW9ucywganFYSFIgKSB7XG5cblx0dmFyIGluc3BlY3RlZCA9IHt9LFxuXHRcdHNlZWtpbmdUcmFuc3BvcnQgPSAoIHN0cnVjdHVyZSA9PT0gdHJhbnNwb3J0cyApO1xuXG5cdGZ1bmN0aW9uIGluc3BlY3QoIGRhdGFUeXBlICkge1xuXHRcdHZhciBzZWxlY3RlZDtcblx0XHRpbnNwZWN0ZWRbIGRhdGFUeXBlIF0gPSB0cnVlO1xuXHRcdGpRdWVyeS5lYWNoKCBzdHJ1Y3R1cmVbIGRhdGFUeXBlIF0gfHwgW10sIGZ1bmN0aW9uKCBfLCBwcmVmaWx0ZXJPckZhY3RvcnkgKSB7XG5cdFx0XHR2YXIgZGF0YVR5cGVPclRyYW5zcG9ydCA9IHByZWZpbHRlck9yRmFjdG9yeSggb3B0aW9ucywgb3JpZ2luYWxPcHRpb25zLCBqcVhIUiApO1xuXHRcdFx0aWYgKCB0eXBlb2YgZGF0YVR5cGVPclRyYW5zcG9ydCA9PT0gXCJzdHJpbmdcIiAmJiAhc2Vla2luZ1RyYW5zcG9ydCAmJiAhaW5zcGVjdGVkWyBkYXRhVHlwZU9yVHJhbnNwb3J0IF0gKSB7XG5cdFx0XHRcdG9wdGlvbnMuZGF0YVR5cGVzLnVuc2hpZnQoIGRhdGFUeXBlT3JUcmFuc3BvcnQgKTtcblx0XHRcdFx0aW5zcGVjdCggZGF0YVR5cGVPclRyYW5zcG9ydCApO1xuXHRcdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0XHR9IGVsc2UgaWYgKCBzZWVraW5nVHJhbnNwb3J0ICkge1xuXHRcdFx0XHRyZXR1cm4gISggc2VsZWN0ZWQgPSBkYXRhVHlwZU9yVHJhbnNwb3J0ICk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdFx0cmV0dXJuIHNlbGVjdGVkO1xuXHR9XG5cblx0cmV0dXJuIGluc3BlY3QoIG9wdGlvbnMuZGF0YVR5cGVzWyAwIF0gKSB8fCAhaW5zcGVjdGVkWyBcIipcIiBdICYmIGluc3BlY3QoIFwiKlwiICk7XG59XG5cbi8vIEEgc3BlY2lhbCBleHRlbmQgZm9yIGFqYXggb3B0aW9uc1xuLy8gdGhhdCB0YWtlcyBcImZsYXRcIiBvcHRpb25zIChub3QgdG8gYmUgZGVlcCBleHRlbmRlZClcbi8vIEZpeGVzICM5ODg3XG5mdW5jdGlvbiBhamF4RXh0ZW5kKCB0YXJnZXQsIHNyYyApIHtcblx0dmFyIGtleSwgZGVlcCxcblx0XHRmbGF0T3B0aW9ucyA9IGpRdWVyeS5hamF4U2V0dGluZ3MuZmxhdE9wdGlvbnMgfHwge307XG5cblx0Zm9yICgga2V5IGluIHNyYyApIHtcblx0XHRpZiAoIHNyY1sga2V5IF0gIT09IHVuZGVmaW5lZCApIHtcblx0XHRcdCggZmxhdE9wdGlvbnNbIGtleSBdID8gdGFyZ2V0IDogKCBkZWVwIHx8IChkZWVwID0ge30pICkgKVsga2V5IF0gPSBzcmNbIGtleSBdO1xuXHRcdH1cblx0fVxuXHRpZiAoIGRlZXAgKSB7XG5cdFx0alF1ZXJ5LmV4dGVuZCggdHJ1ZSwgdGFyZ2V0LCBkZWVwICk7XG5cdH1cblxuXHRyZXR1cm4gdGFyZ2V0O1xufVxuXG4vKiBIYW5kbGVzIHJlc3BvbnNlcyB0byBhbiBhamF4IHJlcXVlc3Q6XG4gKiAtIGZpbmRzIHRoZSByaWdodCBkYXRhVHlwZSAobWVkaWF0ZXMgYmV0d2VlbiBjb250ZW50LXR5cGUgYW5kIGV4cGVjdGVkIGRhdGFUeXBlKVxuICogLSByZXR1cm5zIHRoZSBjb3JyZXNwb25kaW5nIHJlc3BvbnNlXG4gKi9cbmZ1bmN0aW9uIGFqYXhIYW5kbGVSZXNwb25zZXMoIHMsIGpxWEhSLCByZXNwb25zZXMgKSB7XG5cblx0dmFyIGN0LCB0eXBlLCBmaW5hbERhdGFUeXBlLCBmaXJzdERhdGFUeXBlLFxuXHRcdGNvbnRlbnRzID0gcy5jb250ZW50cyxcblx0XHRkYXRhVHlwZXMgPSBzLmRhdGFUeXBlcztcblxuXHQvLyBSZW1vdmUgYXV0byBkYXRhVHlwZSBhbmQgZ2V0IGNvbnRlbnQtdHlwZSBpbiB0aGUgcHJvY2Vzc1xuXHR3aGlsZSAoIGRhdGFUeXBlc1sgMCBdID09PSBcIipcIiApIHtcblx0XHRkYXRhVHlwZXMuc2hpZnQoKTtcblx0XHRpZiAoIGN0ID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRjdCA9IHMubWltZVR5cGUgfHwganFYSFIuZ2V0UmVzcG9uc2VIZWFkZXIoXCJDb250ZW50LVR5cGVcIik7XG5cdFx0fVxuXHR9XG5cblx0Ly8gQ2hlY2sgaWYgd2UncmUgZGVhbGluZyB3aXRoIGEga25vd24gY29udGVudC10eXBlXG5cdGlmICggY3QgKSB7XG5cdFx0Zm9yICggdHlwZSBpbiBjb250ZW50cyApIHtcblx0XHRcdGlmICggY29udGVudHNbIHR5cGUgXSAmJiBjb250ZW50c1sgdHlwZSBdLnRlc3QoIGN0ICkgKSB7XG5cdFx0XHRcdGRhdGFUeXBlcy51bnNoaWZ0KCB0eXBlICk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblx0fVxuXG5cdC8vIENoZWNrIHRvIHNlZSBpZiB3ZSBoYXZlIGEgcmVzcG9uc2UgZm9yIHRoZSBleHBlY3RlZCBkYXRhVHlwZVxuXHRpZiAoIGRhdGFUeXBlc1sgMCBdIGluIHJlc3BvbnNlcyApIHtcblx0XHRmaW5hbERhdGFUeXBlID0gZGF0YVR5cGVzWyAwIF07XG5cdH0gZWxzZSB7XG5cdFx0Ly8gVHJ5IGNvbnZlcnRpYmxlIGRhdGFUeXBlc1xuXHRcdGZvciAoIHR5cGUgaW4gcmVzcG9uc2VzICkge1xuXHRcdFx0aWYgKCAhZGF0YVR5cGVzWyAwIF0gfHwgcy5jb252ZXJ0ZXJzWyB0eXBlICsgXCIgXCIgKyBkYXRhVHlwZXNbMF0gXSApIHtcblx0XHRcdFx0ZmluYWxEYXRhVHlwZSA9IHR5cGU7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKCAhZmlyc3REYXRhVHlwZSApIHtcblx0XHRcdFx0Zmlyc3REYXRhVHlwZSA9IHR5cGU7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdC8vIE9yIGp1c3QgdXNlIGZpcnN0IG9uZVxuXHRcdGZpbmFsRGF0YVR5cGUgPSBmaW5hbERhdGFUeXBlIHx8IGZpcnN0RGF0YVR5cGU7XG5cdH1cblxuXHQvLyBJZiB3ZSBmb3VuZCBhIGRhdGFUeXBlXG5cdC8vIFdlIGFkZCB0aGUgZGF0YVR5cGUgdG8gdGhlIGxpc3QgaWYgbmVlZGVkXG5cdC8vIGFuZCByZXR1cm4gdGhlIGNvcnJlc3BvbmRpbmcgcmVzcG9uc2Vcblx0aWYgKCBmaW5hbERhdGFUeXBlICkge1xuXHRcdGlmICggZmluYWxEYXRhVHlwZSAhPT0gZGF0YVR5cGVzWyAwIF0gKSB7XG5cdFx0XHRkYXRhVHlwZXMudW5zaGlmdCggZmluYWxEYXRhVHlwZSApO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVzcG9uc2VzWyBmaW5hbERhdGFUeXBlIF07XG5cdH1cbn1cblxuLyogQ2hhaW4gY29udmVyc2lvbnMgZ2l2ZW4gdGhlIHJlcXVlc3QgYW5kIHRoZSBvcmlnaW5hbCByZXNwb25zZVxuICogQWxzbyBzZXRzIHRoZSByZXNwb25zZVhYWCBmaWVsZHMgb24gdGhlIGpxWEhSIGluc3RhbmNlXG4gKi9cbmZ1bmN0aW9uIGFqYXhDb252ZXJ0KCBzLCByZXNwb25zZSwganFYSFIsIGlzU3VjY2VzcyApIHtcblx0dmFyIGNvbnYyLCBjdXJyZW50LCBjb252LCB0bXAsIHByZXYsXG5cdFx0Y29udmVydGVycyA9IHt9LFxuXHRcdC8vIFdvcmsgd2l0aCBhIGNvcHkgb2YgZGF0YVR5cGVzIGluIGNhc2Ugd2UgbmVlZCB0byBtb2RpZnkgaXQgZm9yIGNvbnZlcnNpb25cblx0XHRkYXRhVHlwZXMgPSBzLmRhdGFUeXBlcy5zbGljZSgpO1xuXG5cdC8vIENyZWF0ZSBjb252ZXJ0ZXJzIG1hcCB3aXRoIGxvd2VyY2FzZWQga2V5c1xuXHRpZiAoIGRhdGFUeXBlc1sgMSBdICkge1xuXHRcdGZvciAoIGNvbnYgaW4gcy5jb252ZXJ0ZXJzICkge1xuXHRcdFx0Y29udmVydGVyc1sgY29udi50b0xvd2VyQ2FzZSgpIF0gPSBzLmNvbnZlcnRlcnNbIGNvbnYgXTtcblx0XHR9XG5cdH1cblxuXHRjdXJyZW50ID0gZGF0YVR5cGVzLnNoaWZ0KCk7XG5cblx0Ly8gQ29udmVydCB0byBlYWNoIHNlcXVlbnRpYWwgZGF0YVR5cGVcblx0d2hpbGUgKCBjdXJyZW50ICkge1xuXG5cdFx0aWYgKCBzLnJlc3BvbnNlRmllbGRzWyBjdXJyZW50IF0gKSB7XG5cdFx0XHRqcVhIUlsgcy5yZXNwb25zZUZpZWxkc1sgY3VycmVudCBdIF0gPSByZXNwb25zZTtcblx0XHR9XG5cblx0XHQvLyBBcHBseSB0aGUgZGF0YUZpbHRlciBpZiBwcm92aWRlZFxuXHRcdGlmICggIXByZXYgJiYgaXNTdWNjZXNzICYmIHMuZGF0YUZpbHRlciApIHtcblx0XHRcdHJlc3BvbnNlID0gcy5kYXRhRmlsdGVyKCByZXNwb25zZSwgcy5kYXRhVHlwZSApO1xuXHRcdH1cblxuXHRcdHByZXYgPSBjdXJyZW50O1xuXHRcdGN1cnJlbnQgPSBkYXRhVHlwZXMuc2hpZnQoKTtcblxuXHRcdGlmICggY3VycmVudCApIHtcblxuXHRcdC8vIFRoZXJlJ3Mgb25seSB3b3JrIHRvIGRvIGlmIGN1cnJlbnQgZGF0YVR5cGUgaXMgbm9uLWF1dG9cblx0XHRcdGlmICggY3VycmVudCA9PT0gXCIqXCIgKSB7XG5cblx0XHRcdFx0Y3VycmVudCA9IHByZXY7XG5cblx0XHRcdC8vIENvbnZlcnQgcmVzcG9uc2UgaWYgcHJldiBkYXRhVHlwZSBpcyBub24tYXV0byBhbmQgZGlmZmVycyBmcm9tIGN1cnJlbnRcblx0XHRcdH0gZWxzZSBpZiAoIHByZXYgIT09IFwiKlwiICYmIHByZXYgIT09IGN1cnJlbnQgKSB7XG5cblx0XHRcdFx0Ly8gU2VlayBhIGRpcmVjdCBjb252ZXJ0ZXJcblx0XHRcdFx0Y29udiA9IGNvbnZlcnRlcnNbIHByZXYgKyBcIiBcIiArIGN1cnJlbnQgXSB8fCBjb252ZXJ0ZXJzWyBcIiogXCIgKyBjdXJyZW50IF07XG5cblx0XHRcdFx0Ly8gSWYgbm9uZSBmb3VuZCwgc2VlayBhIHBhaXJcblx0XHRcdFx0aWYgKCAhY29udiApIHtcblx0XHRcdFx0XHRmb3IgKCBjb252MiBpbiBjb252ZXJ0ZXJzICkge1xuXG5cdFx0XHRcdFx0XHQvLyBJZiBjb252MiBvdXRwdXRzIGN1cnJlbnRcblx0XHRcdFx0XHRcdHRtcCA9IGNvbnYyLnNwbGl0KCBcIiBcIiApO1xuXHRcdFx0XHRcdFx0aWYgKCB0bXBbIDEgXSA9PT0gY3VycmVudCApIHtcblxuXHRcdFx0XHRcdFx0XHQvLyBJZiBwcmV2IGNhbiBiZSBjb252ZXJ0ZWQgdG8gYWNjZXB0ZWQgaW5wdXRcblx0XHRcdFx0XHRcdFx0Y29udiA9IGNvbnZlcnRlcnNbIHByZXYgKyBcIiBcIiArIHRtcFsgMCBdIF0gfHxcblx0XHRcdFx0XHRcdFx0XHRjb252ZXJ0ZXJzWyBcIiogXCIgKyB0bXBbIDAgXSBdO1xuXHRcdFx0XHRcdFx0XHRpZiAoIGNvbnYgKSB7XG5cdFx0XHRcdFx0XHRcdFx0Ly8gQ29uZGVuc2UgZXF1aXZhbGVuY2UgY29udmVydGVyc1xuXHRcdFx0XHRcdFx0XHRcdGlmICggY29udiA9PT0gdHJ1ZSApIHtcblx0XHRcdFx0XHRcdFx0XHRcdGNvbnYgPSBjb252ZXJ0ZXJzWyBjb252MiBdO1xuXG5cdFx0XHRcdFx0XHRcdFx0Ly8gT3RoZXJ3aXNlLCBpbnNlcnQgdGhlIGludGVybWVkaWF0ZSBkYXRhVHlwZVxuXHRcdFx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoIGNvbnZlcnRlcnNbIGNvbnYyIF0gIT09IHRydWUgKSB7XG5cdFx0XHRcdFx0XHRcdFx0XHRjdXJyZW50ID0gdG1wWyAwIF07XG5cdFx0XHRcdFx0XHRcdFx0XHRkYXRhVHlwZXMudW5zaGlmdCggdG1wWyAxIF0gKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBBcHBseSBjb252ZXJ0ZXIgKGlmIG5vdCBhbiBlcXVpdmFsZW5jZSlcblx0XHRcdFx0aWYgKCBjb252ICE9PSB0cnVlICkge1xuXG5cdFx0XHRcdFx0Ly8gVW5sZXNzIGVycm9ycyBhcmUgYWxsb3dlZCB0byBidWJibGUsIGNhdGNoIGFuZCByZXR1cm4gdGhlbVxuXHRcdFx0XHRcdGlmICggY29udiAmJiBzWyBcInRocm93c1wiIF0gKSB7XG5cdFx0XHRcdFx0XHRyZXNwb25zZSA9IGNvbnYoIHJlc3BvbnNlICk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRcdHJlc3BvbnNlID0gY29udiggcmVzcG9uc2UgKTtcblx0XHRcdFx0XHRcdH0gY2F0Y2ggKCBlICkge1xuXHRcdFx0XHRcdFx0XHRyZXR1cm4geyBzdGF0ZTogXCJwYXJzZXJlcnJvclwiLCBlcnJvcjogY29udiA/IGUgOiBcIk5vIGNvbnZlcnNpb24gZnJvbSBcIiArIHByZXYgKyBcIiB0byBcIiArIGN1cnJlbnQgfTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHRyZXR1cm4geyBzdGF0ZTogXCJzdWNjZXNzXCIsIGRhdGE6IHJlc3BvbnNlIH07XG59XG5cbmpRdWVyeS5leHRlbmQoe1xuXG5cdC8vIENvdW50ZXIgZm9yIGhvbGRpbmcgdGhlIG51bWJlciBvZiBhY3RpdmUgcXVlcmllc1xuXHRhY3RpdmU6IDAsXG5cblx0Ly8gTGFzdC1Nb2RpZmllZCBoZWFkZXIgY2FjaGUgZm9yIG5leHQgcmVxdWVzdFxuXHRsYXN0TW9kaWZpZWQ6IHt9LFxuXHRldGFnOiB7fSxcblxuXHRhamF4U2V0dGluZ3M6IHtcblx0XHR1cmw6IGFqYXhMb2NhdGlvbixcblx0XHR0eXBlOiBcIkdFVFwiLFxuXHRcdGlzTG9jYWw6IHJsb2NhbFByb3RvY29sLnRlc3QoIGFqYXhMb2NQYXJ0c1sgMSBdICksXG5cdFx0Z2xvYmFsOiB0cnVlLFxuXHRcdHByb2Nlc3NEYXRhOiB0cnVlLFxuXHRcdGFzeW5jOiB0cnVlLFxuXHRcdGNvbnRlbnRUeXBlOiBcImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOFwiLFxuXHRcdC8qXG5cdFx0dGltZW91dDogMCxcblx0XHRkYXRhOiBudWxsLFxuXHRcdGRhdGFUeXBlOiBudWxsLFxuXHRcdHVzZXJuYW1lOiBudWxsLFxuXHRcdHBhc3N3b3JkOiBudWxsLFxuXHRcdGNhY2hlOiBudWxsLFxuXHRcdHRocm93czogZmFsc2UsXG5cdFx0dHJhZGl0aW9uYWw6IGZhbHNlLFxuXHRcdGhlYWRlcnM6IHt9LFxuXHRcdCovXG5cblx0XHRhY2NlcHRzOiB7XG5cdFx0XHRcIipcIjogYWxsVHlwZXMsXG5cdFx0XHR0ZXh0OiBcInRleHQvcGxhaW5cIixcblx0XHRcdGh0bWw6IFwidGV4dC9odG1sXCIsXG5cdFx0XHR4bWw6IFwiYXBwbGljYXRpb24veG1sLCB0ZXh0L3htbFwiLFxuXHRcdFx0anNvbjogXCJhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L2phdmFzY3JpcHRcIlxuXHRcdH0sXG5cblx0XHRjb250ZW50czoge1xuXHRcdFx0eG1sOiAveG1sLyxcblx0XHRcdGh0bWw6IC9odG1sLyxcblx0XHRcdGpzb246IC9qc29uL1xuXHRcdH0sXG5cblx0XHRyZXNwb25zZUZpZWxkczoge1xuXHRcdFx0eG1sOiBcInJlc3BvbnNlWE1MXCIsXG5cdFx0XHR0ZXh0OiBcInJlc3BvbnNlVGV4dFwiLFxuXHRcdFx0anNvbjogXCJyZXNwb25zZUpTT05cIlxuXHRcdH0sXG5cblx0XHQvLyBEYXRhIGNvbnZlcnRlcnNcblx0XHQvLyBLZXlzIHNlcGFyYXRlIHNvdXJjZSAob3IgY2F0Y2hhbGwgXCIqXCIpIGFuZCBkZXN0aW5hdGlvbiB0eXBlcyB3aXRoIGEgc2luZ2xlIHNwYWNlXG5cdFx0Y29udmVydGVyczoge1xuXG5cdFx0XHQvLyBDb252ZXJ0IGFueXRoaW5nIHRvIHRleHRcblx0XHRcdFwiKiB0ZXh0XCI6IFN0cmluZyxcblxuXHRcdFx0Ly8gVGV4dCB0byBodG1sICh0cnVlID0gbm8gdHJhbnNmb3JtYXRpb24pXG5cdFx0XHRcInRleHQgaHRtbFwiOiB0cnVlLFxuXG5cdFx0XHQvLyBFdmFsdWF0ZSB0ZXh0IGFzIGEganNvbiBleHByZXNzaW9uXG5cdFx0XHRcInRleHQganNvblwiOiBqUXVlcnkucGFyc2VKU09OLFxuXG5cdFx0XHQvLyBQYXJzZSB0ZXh0IGFzIHhtbFxuXHRcdFx0XCJ0ZXh0IHhtbFwiOiBqUXVlcnkucGFyc2VYTUxcblx0XHR9LFxuXG5cdFx0Ly8gRm9yIG9wdGlvbnMgdGhhdCBzaG91bGRuJ3QgYmUgZGVlcCBleHRlbmRlZDpcblx0XHQvLyB5b3UgY2FuIGFkZCB5b3VyIG93biBjdXN0b20gb3B0aW9ucyBoZXJlIGlmXG5cdFx0Ly8gYW5kIHdoZW4geW91IGNyZWF0ZSBvbmUgdGhhdCBzaG91bGRuJ3QgYmVcblx0XHQvLyBkZWVwIGV4dGVuZGVkIChzZWUgYWpheEV4dGVuZClcblx0XHRmbGF0T3B0aW9uczoge1xuXHRcdFx0dXJsOiB0cnVlLFxuXHRcdFx0Y29udGV4dDogdHJ1ZVxuXHRcdH1cblx0fSxcblxuXHQvLyBDcmVhdGVzIGEgZnVsbCBmbGVkZ2VkIHNldHRpbmdzIG9iamVjdCBpbnRvIHRhcmdldFxuXHQvLyB3aXRoIGJvdGggYWpheFNldHRpbmdzIGFuZCBzZXR0aW5ncyBmaWVsZHMuXG5cdC8vIElmIHRhcmdldCBpcyBvbWl0dGVkLCB3cml0ZXMgaW50byBhamF4U2V0dGluZ3MuXG5cdGFqYXhTZXR1cDogZnVuY3Rpb24oIHRhcmdldCwgc2V0dGluZ3MgKSB7XG5cdFx0cmV0dXJuIHNldHRpbmdzID9cblxuXHRcdFx0Ly8gQnVpbGRpbmcgYSBzZXR0aW5ncyBvYmplY3Rcblx0XHRcdGFqYXhFeHRlbmQoIGFqYXhFeHRlbmQoIHRhcmdldCwgalF1ZXJ5LmFqYXhTZXR0aW5ncyApLCBzZXR0aW5ncyApIDpcblxuXHRcdFx0Ly8gRXh0ZW5kaW5nIGFqYXhTZXR0aW5nc1xuXHRcdFx0YWpheEV4dGVuZCggalF1ZXJ5LmFqYXhTZXR0aW5ncywgdGFyZ2V0ICk7XG5cdH0sXG5cblx0YWpheFByZWZpbHRlcjogYWRkVG9QcmVmaWx0ZXJzT3JUcmFuc3BvcnRzKCBwcmVmaWx0ZXJzICksXG5cdGFqYXhUcmFuc3BvcnQ6IGFkZFRvUHJlZmlsdGVyc09yVHJhbnNwb3J0cyggdHJhbnNwb3J0cyApLFxuXG5cdC8vIE1haW4gbWV0aG9kXG5cdGFqYXg6IGZ1bmN0aW9uKCB1cmwsIG9wdGlvbnMgKSB7XG5cblx0XHQvLyBJZiB1cmwgaXMgYW4gb2JqZWN0LCBzaW11bGF0ZSBwcmUtMS41IHNpZ25hdHVyZVxuXHRcdGlmICggdHlwZW9mIHVybCA9PT0gXCJvYmplY3RcIiApIHtcblx0XHRcdG9wdGlvbnMgPSB1cmw7XG5cdFx0XHR1cmwgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXG5cdFx0Ly8gRm9yY2Ugb3B0aW9ucyB0byBiZSBhbiBvYmplY3Rcblx0XHRvcHRpb25zID0gb3B0aW9ucyB8fCB7fTtcblxuXHRcdHZhciB0cmFuc3BvcnQsXG5cdFx0XHQvLyBVUkwgd2l0aG91dCBhbnRpLWNhY2hlIHBhcmFtXG5cdFx0XHRjYWNoZVVSTCxcblx0XHRcdC8vIFJlc3BvbnNlIGhlYWRlcnNcblx0XHRcdHJlc3BvbnNlSGVhZGVyc1N0cmluZyxcblx0XHRcdHJlc3BvbnNlSGVhZGVycyxcblx0XHRcdC8vIHRpbWVvdXQgaGFuZGxlXG5cdFx0XHR0aW1lb3V0VGltZXIsXG5cdFx0XHQvLyBDcm9zcy1kb21haW4gZGV0ZWN0aW9uIHZhcnNcblx0XHRcdHBhcnRzLFxuXHRcdFx0Ly8gVG8ga25vdyBpZiBnbG9iYWwgZXZlbnRzIGFyZSB0byBiZSBkaXNwYXRjaGVkXG5cdFx0XHRmaXJlR2xvYmFscyxcblx0XHRcdC8vIExvb3AgdmFyaWFibGVcblx0XHRcdGksXG5cdFx0XHQvLyBDcmVhdGUgdGhlIGZpbmFsIG9wdGlvbnMgb2JqZWN0XG5cdFx0XHRzID0galF1ZXJ5LmFqYXhTZXR1cCgge30sIG9wdGlvbnMgKSxcblx0XHRcdC8vIENhbGxiYWNrcyBjb250ZXh0XG5cdFx0XHRjYWxsYmFja0NvbnRleHQgPSBzLmNvbnRleHQgfHwgcyxcblx0XHRcdC8vIENvbnRleHQgZm9yIGdsb2JhbCBldmVudHMgaXMgY2FsbGJhY2tDb250ZXh0IGlmIGl0IGlzIGEgRE9NIG5vZGUgb3IgalF1ZXJ5IGNvbGxlY3Rpb25cblx0XHRcdGdsb2JhbEV2ZW50Q29udGV4dCA9IHMuY29udGV4dCAmJiAoIGNhbGxiYWNrQ29udGV4dC5ub2RlVHlwZSB8fCBjYWxsYmFja0NvbnRleHQuanF1ZXJ5ICkgP1xuXHRcdFx0XHRqUXVlcnkoIGNhbGxiYWNrQ29udGV4dCApIDpcblx0XHRcdFx0alF1ZXJ5LmV2ZW50LFxuXHRcdFx0Ly8gRGVmZXJyZWRzXG5cdFx0XHRkZWZlcnJlZCA9IGpRdWVyeS5EZWZlcnJlZCgpLFxuXHRcdFx0Y29tcGxldGVEZWZlcnJlZCA9IGpRdWVyeS5DYWxsYmFja3MoXCJvbmNlIG1lbW9yeVwiKSxcblx0XHRcdC8vIFN0YXR1cy1kZXBlbmRlbnQgY2FsbGJhY2tzXG5cdFx0XHRzdGF0dXNDb2RlID0gcy5zdGF0dXNDb2RlIHx8IHt9LFxuXHRcdFx0Ly8gSGVhZGVycyAodGhleSBhcmUgc2VudCBhbGwgYXQgb25jZSlcblx0XHRcdHJlcXVlc3RIZWFkZXJzID0ge30sXG5cdFx0XHRyZXF1ZXN0SGVhZGVyc05hbWVzID0ge30sXG5cdFx0XHQvLyBUaGUganFYSFIgc3RhdGVcblx0XHRcdHN0YXRlID0gMCxcblx0XHRcdC8vIERlZmF1bHQgYWJvcnQgbWVzc2FnZVxuXHRcdFx0c3RyQWJvcnQgPSBcImNhbmNlbGVkXCIsXG5cdFx0XHQvLyBGYWtlIHhoclxuXHRcdFx0anFYSFIgPSB7XG5cdFx0XHRcdHJlYWR5U3RhdGU6IDAsXG5cblx0XHRcdFx0Ly8gQnVpbGRzIGhlYWRlcnMgaGFzaHRhYmxlIGlmIG5lZWRlZFxuXHRcdFx0XHRnZXRSZXNwb25zZUhlYWRlcjogZnVuY3Rpb24oIGtleSApIHtcblx0XHRcdFx0XHR2YXIgbWF0Y2g7XG5cdFx0XHRcdFx0aWYgKCBzdGF0ZSA9PT0gMiApIHtcblx0XHRcdFx0XHRcdGlmICggIXJlc3BvbnNlSGVhZGVycyApIHtcblx0XHRcdFx0XHRcdFx0cmVzcG9uc2VIZWFkZXJzID0ge307XG5cdFx0XHRcdFx0XHRcdHdoaWxlICggKG1hdGNoID0gcmhlYWRlcnMuZXhlYyggcmVzcG9uc2VIZWFkZXJzU3RyaW5nICkpICkge1xuXHRcdFx0XHRcdFx0XHRcdHJlc3BvbnNlSGVhZGVyc1sgbWF0Y2hbMV0udG9Mb3dlckNhc2UoKSBdID0gbWF0Y2hbIDIgXTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0bWF0Y2ggPSByZXNwb25zZUhlYWRlcnNbIGtleS50b0xvd2VyQ2FzZSgpIF07XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdHJldHVybiBtYXRjaCA9PSBudWxsID8gbnVsbCA6IG1hdGNoO1xuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdC8vIFJhdyBzdHJpbmdcblx0XHRcdFx0Z2V0QWxsUmVzcG9uc2VIZWFkZXJzOiBmdW5jdGlvbigpIHtcblx0XHRcdFx0XHRyZXR1cm4gc3RhdGUgPT09IDIgPyByZXNwb25zZUhlYWRlcnNTdHJpbmcgOiBudWxsO1xuXHRcdFx0XHR9LFxuXG5cdFx0XHRcdC8vIENhY2hlcyB0aGUgaGVhZGVyXG5cdFx0XHRcdHNldFJlcXVlc3RIZWFkZXI6IGZ1bmN0aW9uKCBuYW1lLCB2YWx1ZSApIHtcblx0XHRcdFx0XHR2YXIgbG5hbWUgPSBuYW1lLnRvTG93ZXJDYXNlKCk7XG5cdFx0XHRcdFx0aWYgKCAhc3RhdGUgKSB7XG5cdFx0XHRcdFx0XHRuYW1lID0gcmVxdWVzdEhlYWRlcnNOYW1lc1sgbG5hbWUgXSA9IHJlcXVlc3RIZWFkZXJzTmFtZXNbIGxuYW1lIF0gfHwgbmFtZTtcblx0XHRcdFx0XHRcdHJlcXVlc3RIZWFkZXJzWyBuYW1lIF0gPSB2YWx1ZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0Ly8gT3ZlcnJpZGVzIHJlc3BvbnNlIGNvbnRlbnQtdHlwZSBoZWFkZXJcblx0XHRcdFx0b3ZlcnJpZGVNaW1lVHlwZTogZnVuY3Rpb24oIHR5cGUgKSB7XG5cdFx0XHRcdFx0aWYgKCAhc3RhdGUgKSB7XG5cdFx0XHRcdFx0XHRzLm1pbWVUeXBlID0gdHlwZTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0Ly8gU3RhdHVzLWRlcGVuZGVudCBjYWxsYmFja3Ncblx0XHRcdFx0c3RhdHVzQ29kZTogZnVuY3Rpb24oIG1hcCApIHtcblx0XHRcdFx0XHR2YXIgY29kZTtcblx0XHRcdFx0XHRpZiAoIG1hcCApIHtcblx0XHRcdFx0XHRcdGlmICggc3RhdGUgPCAyICkge1xuXHRcdFx0XHRcdFx0XHRmb3IgKCBjb2RlIGluIG1hcCApIHtcblx0XHRcdFx0XHRcdFx0XHQvLyBMYXp5LWFkZCB0aGUgbmV3IGNhbGxiYWNrIGluIGEgd2F5IHRoYXQgcHJlc2VydmVzIG9sZCBvbmVzXG5cdFx0XHRcdFx0XHRcdFx0c3RhdHVzQ29kZVsgY29kZSBdID0gWyBzdGF0dXNDb2RlWyBjb2RlIF0sIG1hcFsgY29kZSBdIF07XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdC8vIEV4ZWN1dGUgdGhlIGFwcHJvcHJpYXRlIGNhbGxiYWNrc1xuXHRcdFx0XHRcdFx0XHRqcVhIUi5hbHdheXMoIG1hcFsganFYSFIuc3RhdHVzIF0gKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHRcdH0sXG5cblx0XHRcdFx0Ly8gQ2FuY2VsIHRoZSByZXF1ZXN0XG5cdFx0XHRcdGFib3J0OiBmdW5jdGlvbiggc3RhdHVzVGV4dCApIHtcblx0XHRcdFx0XHR2YXIgZmluYWxUZXh0ID0gc3RhdHVzVGV4dCB8fCBzdHJBYm9ydDtcblx0XHRcdFx0XHRpZiAoIHRyYW5zcG9ydCApIHtcblx0XHRcdFx0XHRcdHRyYW5zcG9ydC5hYm9ydCggZmluYWxUZXh0ICk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGRvbmUoIDAsIGZpbmFsVGV4dCApO1xuXHRcdFx0XHRcdHJldHVybiB0aGlzO1xuXHRcdFx0XHR9XG5cdFx0XHR9O1xuXG5cdFx0Ly8gQXR0YWNoIGRlZmVycmVkc1xuXHRcdGRlZmVycmVkLnByb21pc2UoIGpxWEhSICkuY29tcGxldGUgPSBjb21wbGV0ZURlZmVycmVkLmFkZDtcblx0XHRqcVhIUi5zdWNjZXNzID0ganFYSFIuZG9uZTtcblx0XHRqcVhIUi5lcnJvciA9IGpxWEhSLmZhaWw7XG5cblx0XHQvLyBSZW1vdmUgaGFzaCBjaGFyYWN0ZXIgKCM3NTMxOiBhbmQgc3RyaW5nIHByb21vdGlvbilcblx0XHQvLyBBZGQgcHJvdG9jb2wgaWYgbm90IHByb3ZpZGVkIChwcmVmaWx0ZXJzIG1pZ2h0IGV4cGVjdCBpdClcblx0XHQvLyBIYW5kbGUgZmFsc3kgdXJsIGluIHRoZSBzZXR0aW5ncyBvYmplY3QgKCMxMDA5MzogY29uc2lzdGVuY3kgd2l0aCBvbGQgc2lnbmF0dXJlKVxuXHRcdC8vIFdlIGFsc28gdXNlIHRoZSB1cmwgcGFyYW1ldGVyIGlmIGF2YWlsYWJsZVxuXHRcdHMudXJsID0gKCAoIHVybCB8fCBzLnVybCB8fCBhamF4TG9jYXRpb24gKSArIFwiXCIgKS5yZXBsYWNlKCByaGFzaCwgXCJcIiApXG5cdFx0XHQucmVwbGFjZSggcnByb3RvY29sLCBhamF4TG9jUGFydHNbIDEgXSArIFwiLy9cIiApO1xuXG5cdFx0Ly8gQWxpYXMgbWV0aG9kIG9wdGlvbiB0byB0eXBlIGFzIHBlciB0aWNrZXQgIzEyMDA0XG5cdFx0cy50eXBlID0gb3B0aW9ucy5tZXRob2QgfHwgb3B0aW9ucy50eXBlIHx8IHMubWV0aG9kIHx8IHMudHlwZTtcblxuXHRcdC8vIEV4dHJhY3QgZGF0YVR5cGVzIGxpc3Rcblx0XHRzLmRhdGFUeXBlcyA9IGpRdWVyeS50cmltKCBzLmRhdGFUeXBlIHx8IFwiKlwiICkudG9Mb3dlckNhc2UoKS5tYXRjaCggcm5vdHdoaXRlICkgfHwgWyBcIlwiIF07XG5cblx0XHQvLyBBIGNyb3NzLWRvbWFpbiByZXF1ZXN0IGlzIGluIG9yZGVyIHdoZW4gd2UgaGF2ZSBhIHByb3RvY29sOmhvc3Q6cG9ydCBtaXNtYXRjaFxuXHRcdGlmICggcy5jcm9zc0RvbWFpbiA9PSBudWxsICkge1xuXHRcdFx0cGFydHMgPSBydXJsLmV4ZWMoIHMudXJsLnRvTG93ZXJDYXNlKCkgKTtcblx0XHRcdHMuY3Jvc3NEb21haW4gPSAhISggcGFydHMgJiZcblx0XHRcdFx0KCBwYXJ0c1sgMSBdICE9PSBhamF4TG9jUGFydHNbIDEgXSB8fCBwYXJ0c1sgMiBdICE9PSBhamF4TG9jUGFydHNbIDIgXSB8fFxuXHRcdFx0XHRcdCggcGFydHNbIDMgXSB8fCAoIHBhcnRzWyAxIF0gPT09IFwiaHR0cDpcIiA/IFwiODBcIiA6IFwiNDQzXCIgKSApICE9PVxuXHRcdFx0XHRcdFx0KCBhamF4TG9jUGFydHNbIDMgXSB8fCAoIGFqYXhMb2NQYXJ0c1sgMSBdID09PSBcImh0dHA6XCIgPyBcIjgwXCIgOiBcIjQ0M1wiICkgKSApXG5cdFx0XHQpO1xuXHRcdH1cblxuXHRcdC8vIENvbnZlcnQgZGF0YSBpZiBub3QgYWxyZWFkeSBhIHN0cmluZ1xuXHRcdGlmICggcy5kYXRhICYmIHMucHJvY2Vzc0RhdGEgJiYgdHlwZW9mIHMuZGF0YSAhPT0gXCJzdHJpbmdcIiApIHtcblx0XHRcdHMuZGF0YSA9IGpRdWVyeS5wYXJhbSggcy5kYXRhLCBzLnRyYWRpdGlvbmFsICk7XG5cdFx0fVxuXG5cdFx0Ly8gQXBwbHkgcHJlZmlsdGVyc1xuXHRcdGluc3BlY3RQcmVmaWx0ZXJzT3JUcmFuc3BvcnRzKCBwcmVmaWx0ZXJzLCBzLCBvcHRpb25zLCBqcVhIUiApO1xuXG5cdFx0Ly8gSWYgcmVxdWVzdCB3YXMgYWJvcnRlZCBpbnNpZGUgYSBwcmVmaWx0ZXIsIHN0b3AgdGhlcmVcblx0XHRpZiAoIHN0YXRlID09PSAyICkge1xuXHRcdFx0cmV0dXJuIGpxWEhSO1xuXHRcdH1cblxuXHRcdC8vIFdlIGNhbiBmaXJlIGdsb2JhbCBldmVudHMgYXMgb2Ygbm93IGlmIGFza2VkIHRvXG5cdFx0Ly8gRG9uJ3QgZmlyZSBldmVudHMgaWYgalF1ZXJ5LmV2ZW50IGlzIHVuZGVmaW5lZCBpbiBhbiBBTUQtdXNhZ2Ugc2NlbmFyaW8gKCMxNTExOClcblx0XHRmaXJlR2xvYmFscyA9IGpRdWVyeS5ldmVudCAmJiBzLmdsb2JhbDtcblxuXHRcdC8vIFdhdGNoIGZvciBhIG5ldyBzZXQgb2YgcmVxdWVzdHNcblx0XHRpZiAoIGZpcmVHbG9iYWxzICYmIGpRdWVyeS5hY3RpdmUrKyA9PT0gMCApIHtcblx0XHRcdGpRdWVyeS5ldmVudC50cmlnZ2VyKFwiYWpheFN0YXJ0XCIpO1xuXHRcdH1cblxuXHRcdC8vIFVwcGVyY2FzZSB0aGUgdHlwZVxuXHRcdHMudHlwZSA9IHMudHlwZS50b1VwcGVyQ2FzZSgpO1xuXG5cdFx0Ly8gRGV0ZXJtaW5lIGlmIHJlcXVlc3QgaGFzIGNvbnRlbnRcblx0XHRzLmhhc0NvbnRlbnQgPSAhcm5vQ29udGVudC50ZXN0KCBzLnR5cGUgKTtcblxuXHRcdC8vIFNhdmUgdGhlIFVSTCBpbiBjYXNlIHdlJ3JlIHRveWluZyB3aXRoIHRoZSBJZi1Nb2RpZmllZC1TaW5jZVxuXHRcdC8vIGFuZC9vciBJZi1Ob25lLU1hdGNoIGhlYWRlciBsYXRlciBvblxuXHRcdGNhY2hlVVJMID0gcy51cmw7XG5cblx0XHQvLyBNb3JlIG9wdGlvbnMgaGFuZGxpbmcgZm9yIHJlcXVlc3RzIHdpdGggbm8gY29udGVudFxuXHRcdGlmICggIXMuaGFzQ29udGVudCApIHtcblxuXHRcdFx0Ly8gSWYgZGF0YSBpcyBhdmFpbGFibGUsIGFwcGVuZCBkYXRhIHRvIHVybFxuXHRcdFx0aWYgKCBzLmRhdGEgKSB7XG5cdFx0XHRcdGNhY2hlVVJMID0gKCBzLnVybCArPSAoIHJxdWVyeS50ZXN0KCBjYWNoZVVSTCApID8gXCImXCIgOiBcIj9cIiApICsgcy5kYXRhICk7XG5cdFx0XHRcdC8vICM5NjgyOiByZW1vdmUgZGF0YSBzbyB0aGF0IGl0J3Mgbm90IHVzZWQgaW4gYW4gZXZlbnR1YWwgcmV0cnlcblx0XHRcdFx0ZGVsZXRlIHMuZGF0YTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gQWRkIGFudGktY2FjaGUgaW4gdXJsIGlmIG5lZWRlZFxuXHRcdFx0aWYgKCBzLmNhY2hlID09PSBmYWxzZSApIHtcblx0XHRcdFx0cy51cmwgPSBydHMudGVzdCggY2FjaGVVUkwgKSA/XG5cblx0XHRcdFx0XHQvLyBJZiB0aGVyZSBpcyBhbHJlYWR5IGEgJ18nIHBhcmFtZXRlciwgc2V0IGl0cyB2YWx1ZVxuXHRcdFx0XHRcdGNhY2hlVVJMLnJlcGxhY2UoIHJ0cywgXCIkMV89XCIgKyBub25jZSsrICkgOlxuXG5cdFx0XHRcdFx0Ly8gT3RoZXJ3aXNlIGFkZCBvbmUgdG8gdGhlIGVuZFxuXHRcdFx0XHRcdGNhY2hlVVJMICsgKCBycXVlcnkudGVzdCggY2FjaGVVUkwgKSA/IFwiJlwiIDogXCI/XCIgKSArIFwiXz1cIiArIG5vbmNlKys7XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gU2V0IHRoZSBJZi1Nb2RpZmllZC1TaW5jZSBhbmQvb3IgSWYtTm9uZS1NYXRjaCBoZWFkZXIsIGlmIGluIGlmTW9kaWZpZWQgbW9kZS5cblx0XHRpZiAoIHMuaWZNb2RpZmllZCApIHtcblx0XHRcdGlmICggalF1ZXJ5Lmxhc3RNb2RpZmllZFsgY2FjaGVVUkwgXSApIHtcblx0XHRcdFx0anFYSFIuc2V0UmVxdWVzdEhlYWRlciggXCJJZi1Nb2RpZmllZC1TaW5jZVwiLCBqUXVlcnkubGFzdE1vZGlmaWVkWyBjYWNoZVVSTCBdICk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIGpRdWVyeS5ldGFnWyBjYWNoZVVSTCBdICkge1xuXHRcdFx0XHRqcVhIUi5zZXRSZXF1ZXN0SGVhZGVyKCBcIklmLU5vbmUtTWF0Y2hcIiwgalF1ZXJ5LmV0YWdbIGNhY2hlVVJMIF0gKTtcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBTZXQgdGhlIGNvcnJlY3QgaGVhZGVyLCBpZiBkYXRhIGlzIGJlaW5nIHNlbnRcblx0XHRpZiAoIHMuZGF0YSAmJiBzLmhhc0NvbnRlbnQgJiYgcy5jb250ZW50VHlwZSAhPT0gZmFsc2UgfHwgb3B0aW9ucy5jb250ZW50VHlwZSApIHtcblx0XHRcdGpxWEhSLnNldFJlcXVlc3RIZWFkZXIoIFwiQ29udGVudC1UeXBlXCIsIHMuY29udGVudFR5cGUgKTtcblx0XHR9XG5cblx0XHQvLyBTZXQgdGhlIEFjY2VwdHMgaGVhZGVyIGZvciB0aGUgc2VydmVyLCBkZXBlbmRpbmcgb24gdGhlIGRhdGFUeXBlXG5cdFx0anFYSFIuc2V0UmVxdWVzdEhlYWRlcihcblx0XHRcdFwiQWNjZXB0XCIsXG5cdFx0XHRzLmRhdGFUeXBlc1sgMCBdICYmIHMuYWNjZXB0c1sgcy5kYXRhVHlwZXNbMF0gXSA/XG5cdFx0XHRcdHMuYWNjZXB0c1sgcy5kYXRhVHlwZXNbMF0gXSArICggcy5kYXRhVHlwZXNbIDAgXSAhPT0gXCIqXCIgPyBcIiwgXCIgKyBhbGxUeXBlcyArIFwiOyBxPTAuMDFcIiA6IFwiXCIgKSA6XG5cdFx0XHRcdHMuYWNjZXB0c1sgXCIqXCIgXVxuXHRcdCk7XG5cblx0XHQvLyBDaGVjayBmb3IgaGVhZGVycyBvcHRpb25cblx0XHRmb3IgKCBpIGluIHMuaGVhZGVycyApIHtcblx0XHRcdGpxWEhSLnNldFJlcXVlc3RIZWFkZXIoIGksIHMuaGVhZGVyc1sgaSBdICk7XG5cdFx0fVxuXG5cdFx0Ly8gQWxsb3cgY3VzdG9tIGhlYWRlcnMvbWltZXR5cGVzIGFuZCBlYXJseSBhYm9ydFxuXHRcdGlmICggcy5iZWZvcmVTZW5kICYmICggcy5iZWZvcmVTZW5kLmNhbGwoIGNhbGxiYWNrQ29udGV4dCwganFYSFIsIHMgKSA9PT0gZmFsc2UgfHwgc3RhdGUgPT09IDIgKSApIHtcblx0XHRcdC8vIEFib3J0IGlmIG5vdCBkb25lIGFscmVhZHkgYW5kIHJldHVyblxuXHRcdFx0cmV0dXJuIGpxWEhSLmFib3J0KCk7XG5cdFx0fVxuXG5cdFx0Ly8gQWJvcnRpbmcgaXMgbm8gbG9uZ2VyIGEgY2FuY2VsbGF0aW9uXG5cdFx0c3RyQWJvcnQgPSBcImFib3J0XCI7XG5cblx0XHQvLyBJbnN0YWxsIGNhbGxiYWNrcyBvbiBkZWZlcnJlZHNcblx0XHRmb3IgKCBpIGluIHsgc3VjY2VzczogMSwgZXJyb3I6IDEsIGNvbXBsZXRlOiAxIH0gKSB7XG5cdFx0XHRqcVhIUlsgaSBdKCBzWyBpIF0gKTtcblx0XHR9XG5cblx0XHQvLyBHZXQgdHJhbnNwb3J0XG5cdFx0dHJhbnNwb3J0ID0gaW5zcGVjdFByZWZpbHRlcnNPclRyYW5zcG9ydHMoIHRyYW5zcG9ydHMsIHMsIG9wdGlvbnMsIGpxWEhSICk7XG5cblx0XHQvLyBJZiBubyB0cmFuc3BvcnQsIHdlIGF1dG8tYWJvcnRcblx0XHRpZiAoICF0cmFuc3BvcnQgKSB7XG5cdFx0XHRkb25lKCAtMSwgXCJObyBUcmFuc3BvcnRcIiApO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRqcVhIUi5yZWFkeVN0YXRlID0gMTtcblxuXHRcdFx0Ly8gU2VuZCBnbG9iYWwgZXZlbnRcblx0XHRcdGlmICggZmlyZUdsb2JhbHMgKSB7XG5cdFx0XHRcdGdsb2JhbEV2ZW50Q29udGV4dC50cmlnZ2VyKCBcImFqYXhTZW5kXCIsIFsganFYSFIsIHMgXSApO1xuXHRcdFx0fVxuXHRcdFx0Ly8gVGltZW91dFxuXHRcdFx0aWYgKCBzLmFzeW5jICYmIHMudGltZW91dCA+IDAgKSB7XG5cdFx0XHRcdHRpbWVvdXRUaW1lciA9IHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0anFYSFIuYWJvcnQoXCJ0aW1lb3V0XCIpO1xuXHRcdFx0XHR9LCBzLnRpbWVvdXQgKTtcblx0XHRcdH1cblxuXHRcdFx0dHJ5IHtcblx0XHRcdFx0c3RhdGUgPSAxO1xuXHRcdFx0XHR0cmFuc3BvcnQuc2VuZCggcmVxdWVzdEhlYWRlcnMsIGRvbmUgKTtcblx0XHRcdH0gY2F0Y2ggKCBlICkge1xuXHRcdFx0XHQvLyBQcm9wYWdhdGUgZXhjZXB0aW9uIGFzIGVycm9yIGlmIG5vdCBkb25lXG5cdFx0XHRcdGlmICggc3RhdGUgPCAyICkge1xuXHRcdFx0XHRcdGRvbmUoIC0xLCBlICk7XG5cdFx0XHRcdC8vIFNpbXBseSByZXRocm93IG90aGVyd2lzZVxuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHRocm93IGU7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBDYWxsYmFjayBmb3Igd2hlbiBldmVyeXRoaW5nIGlzIGRvbmVcblx0XHRmdW5jdGlvbiBkb25lKCBzdGF0dXMsIG5hdGl2ZVN0YXR1c1RleHQsIHJlc3BvbnNlcywgaGVhZGVycyApIHtcblx0XHRcdHZhciBpc1N1Y2Nlc3MsIHN1Y2Nlc3MsIGVycm9yLCByZXNwb25zZSwgbW9kaWZpZWQsXG5cdFx0XHRcdHN0YXR1c1RleHQgPSBuYXRpdmVTdGF0dXNUZXh0O1xuXG5cdFx0XHQvLyBDYWxsZWQgb25jZVxuXHRcdFx0aWYgKCBzdGF0ZSA9PT0gMiApIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBTdGF0ZSBpcyBcImRvbmVcIiBub3dcblx0XHRcdHN0YXRlID0gMjtcblxuXHRcdFx0Ly8gQ2xlYXIgdGltZW91dCBpZiBpdCBleGlzdHNcblx0XHRcdGlmICggdGltZW91dFRpbWVyICkge1xuXHRcdFx0XHRjbGVhclRpbWVvdXQoIHRpbWVvdXRUaW1lciApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBEZXJlZmVyZW5jZSB0cmFuc3BvcnQgZm9yIGVhcmx5IGdhcmJhZ2UgY29sbGVjdGlvblxuXHRcdFx0Ly8gKG5vIG1hdHRlciBob3cgbG9uZyB0aGUganFYSFIgb2JqZWN0IHdpbGwgYmUgdXNlZClcblx0XHRcdHRyYW5zcG9ydCA9IHVuZGVmaW5lZDtcblxuXHRcdFx0Ly8gQ2FjaGUgcmVzcG9uc2UgaGVhZGVyc1xuXHRcdFx0cmVzcG9uc2VIZWFkZXJzU3RyaW5nID0gaGVhZGVycyB8fCBcIlwiO1xuXG5cdFx0XHQvLyBTZXQgcmVhZHlTdGF0ZVxuXHRcdFx0anFYSFIucmVhZHlTdGF0ZSA9IHN0YXR1cyA+IDAgPyA0IDogMDtcblxuXHRcdFx0Ly8gRGV0ZXJtaW5lIGlmIHN1Y2Nlc3NmdWxcblx0XHRcdGlzU3VjY2VzcyA9IHN0YXR1cyA+PSAyMDAgJiYgc3RhdHVzIDwgMzAwIHx8IHN0YXR1cyA9PT0gMzA0O1xuXG5cdFx0XHQvLyBHZXQgcmVzcG9uc2UgZGF0YVxuXHRcdFx0aWYgKCByZXNwb25zZXMgKSB7XG5cdFx0XHRcdHJlc3BvbnNlID0gYWpheEhhbmRsZVJlc3BvbnNlcyggcywganFYSFIsIHJlc3BvbnNlcyApO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBDb252ZXJ0IG5vIG1hdHRlciB3aGF0ICh0aGF0IHdheSByZXNwb25zZVhYWCBmaWVsZHMgYXJlIGFsd2F5cyBzZXQpXG5cdFx0XHRyZXNwb25zZSA9IGFqYXhDb252ZXJ0KCBzLCByZXNwb25zZSwganFYSFIsIGlzU3VjY2VzcyApO1xuXG5cdFx0XHQvLyBJZiBzdWNjZXNzZnVsLCBoYW5kbGUgdHlwZSBjaGFpbmluZ1xuXHRcdFx0aWYgKCBpc1N1Y2Nlc3MgKSB7XG5cblx0XHRcdFx0Ly8gU2V0IHRoZSBJZi1Nb2RpZmllZC1TaW5jZSBhbmQvb3IgSWYtTm9uZS1NYXRjaCBoZWFkZXIsIGlmIGluIGlmTW9kaWZpZWQgbW9kZS5cblx0XHRcdFx0aWYgKCBzLmlmTW9kaWZpZWQgKSB7XG5cdFx0XHRcdFx0bW9kaWZpZWQgPSBqcVhIUi5nZXRSZXNwb25zZUhlYWRlcihcIkxhc3QtTW9kaWZpZWRcIik7XG5cdFx0XHRcdFx0aWYgKCBtb2RpZmllZCApIHtcblx0XHRcdFx0XHRcdGpRdWVyeS5sYXN0TW9kaWZpZWRbIGNhY2hlVVJMIF0gPSBtb2RpZmllZDtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0bW9kaWZpZWQgPSBqcVhIUi5nZXRSZXNwb25zZUhlYWRlcihcImV0YWdcIik7XG5cdFx0XHRcdFx0aWYgKCBtb2RpZmllZCApIHtcblx0XHRcdFx0XHRcdGpRdWVyeS5ldGFnWyBjYWNoZVVSTCBdID0gbW9kaWZpZWQ7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gaWYgbm8gY29udGVudFxuXHRcdFx0XHRpZiAoIHN0YXR1cyA9PT0gMjA0IHx8IHMudHlwZSA9PT0gXCJIRUFEXCIgKSB7XG5cdFx0XHRcdFx0c3RhdHVzVGV4dCA9IFwibm9jb250ZW50XCI7XG5cblx0XHRcdFx0Ly8gaWYgbm90IG1vZGlmaWVkXG5cdFx0XHRcdH0gZWxzZSBpZiAoIHN0YXR1cyA9PT0gMzA0ICkge1xuXHRcdFx0XHRcdHN0YXR1c1RleHQgPSBcIm5vdG1vZGlmaWVkXCI7XG5cblx0XHRcdFx0Ly8gSWYgd2UgaGF2ZSBkYXRhLCBsZXQncyBjb252ZXJ0IGl0XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0c3RhdHVzVGV4dCA9IHJlc3BvbnNlLnN0YXRlO1xuXHRcdFx0XHRcdHN1Y2Nlc3MgPSByZXNwb25zZS5kYXRhO1xuXHRcdFx0XHRcdGVycm9yID0gcmVzcG9uc2UuZXJyb3I7XG5cdFx0XHRcdFx0aXNTdWNjZXNzID0gIWVycm9yO1xuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQvLyBFeHRyYWN0IGVycm9yIGZyb20gc3RhdHVzVGV4dCBhbmQgbm9ybWFsaXplIGZvciBub24tYWJvcnRzXG5cdFx0XHRcdGVycm9yID0gc3RhdHVzVGV4dDtcblx0XHRcdFx0aWYgKCBzdGF0dXMgfHwgIXN0YXR1c1RleHQgKSB7XG5cdFx0XHRcdFx0c3RhdHVzVGV4dCA9IFwiZXJyb3JcIjtcblx0XHRcdFx0XHRpZiAoIHN0YXR1cyA8IDAgKSB7XG5cdFx0XHRcdFx0XHRzdGF0dXMgPSAwO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fVxuXG5cdFx0XHQvLyBTZXQgZGF0YSBmb3IgdGhlIGZha2UgeGhyIG9iamVjdFxuXHRcdFx0anFYSFIuc3RhdHVzID0gc3RhdHVzO1xuXHRcdFx0anFYSFIuc3RhdHVzVGV4dCA9ICggbmF0aXZlU3RhdHVzVGV4dCB8fCBzdGF0dXNUZXh0ICkgKyBcIlwiO1xuXG5cdFx0XHQvLyBTdWNjZXNzL0Vycm9yXG5cdFx0XHRpZiAoIGlzU3VjY2VzcyApIHtcblx0XHRcdFx0ZGVmZXJyZWQucmVzb2x2ZVdpdGgoIGNhbGxiYWNrQ29udGV4dCwgWyBzdWNjZXNzLCBzdGF0dXNUZXh0LCBqcVhIUiBdICk7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRkZWZlcnJlZC5yZWplY3RXaXRoKCBjYWxsYmFja0NvbnRleHQsIFsganFYSFIsIHN0YXR1c1RleHQsIGVycm9yIF0gKTtcblx0XHRcdH1cblxuXHRcdFx0Ly8gU3RhdHVzLWRlcGVuZGVudCBjYWxsYmFja3Ncblx0XHRcdGpxWEhSLnN0YXR1c0NvZGUoIHN0YXR1c0NvZGUgKTtcblx0XHRcdHN0YXR1c0NvZGUgPSB1bmRlZmluZWQ7XG5cblx0XHRcdGlmICggZmlyZUdsb2JhbHMgKSB7XG5cdFx0XHRcdGdsb2JhbEV2ZW50Q29udGV4dC50cmlnZ2VyKCBpc1N1Y2Nlc3MgPyBcImFqYXhTdWNjZXNzXCIgOiBcImFqYXhFcnJvclwiLFxuXHRcdFx0XHRcdFsganFYSFIsIHMsIGlzU3VjY2VzcyA/IHN1Y2Nlc3MgOiBlcnJvciBdICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIENvbXBsZXRlXG5cdFx0XHRjb21wbGV0ZURlZmVycmVkLmZpcmVXaXRoKCBjYWxsYmFja0NvbnRleHQsIFsganFYSFIsIHN0YXR1c1RleHQgXSApO1xuXG5cdFx0XHRpZiAoIGZpcmVHbG9iYWxzICkge1xuXHRcdFx0XHRnbG9iYWxFdmVudENvbnRleHQudHJpZ2dlciggXCJhamF4Q29tcGxldGVcIiwgWyBqcVhIUiwgcyBdICk7XG5cdFx0XHRcdC8vIEhhbmRsZSB0aGUgZ2xvYmFsIEFKQVggY291bnRlclxuXHRcdFx0XHRpZiAoICEoIC0talF1ZXJ5LmFjdGl2ZSApICkge1xuXHRcdFx0XHRcdGpRdWVyeS5ldmVudC50cmlnZ2VyKFwiYWpheFN0b3BcIik7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRyZXR1cm4ganFYSFI7XG5cdH0sXG5cblx0Z2V0SlNPTjogZnVuY3Rpb24oIHVybCwgZGF0YSwgY2FsbGJhY2sgKSB7XG5cdFx0cmV0dXJuIGpRdWVyeS5nZXQoIHVybCwgZGF0YSwgY2FsbGJhY2ssIFwianNvblwiICk7XG5cdH0sXG5cblx0Z2V0U2NyaXB0OiBmdW5jdGlvbiggdXJsLCBjYWxsYmFjayApIHtcblx0XHRyZXR1cm4galF1ZXJ5LmdldCggdXJsLCB1bmRlZmluZWQsIGNhbGxiYWNrLCBcInNjcmlwdFwiICk7XG5cdH1cbn0pO1xuXG5qUXVlcnkuZWFjaCggWyBcImdldFwiLCBcInBvc3RcIiBdLCBmdW5jdGlvbiggaSwgbWV0aG9kICkge1xuXHRqUXVlcnlbIG1ldGhvZCBdID0gZnVuY3Rpb24oIHVybCwgZGF0YSwgY2FsbGJhY2ssIHR5cGUgKSB7XG5cdFx0Ly8gU2hpZnQgYXJndW1lbnRzIGlmIGRhdGEgYXJndW1lbnQgd2FzIG9taXR0ZWRcblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBkYXRhICkgKSB7XG5cdFx0XHR0eXBlID0gdHlwZSB8fCBjYWxsYmFjaztcblx0XHRcdGNhbGxiYWNrID0gZGF0YTtcblx0XHRcdGRhdGEgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGpRdWVyeS5hamF4KHtcblx0XHRcdHVybDogdXJsLFxuXHRcdFx0dHlwZTogbWV0aG9kLFxuXHRcdFx0ZGF0YVR5cGU6IHR5cGUsXG5cdFx0XHRkYXRhOiBkYXRhLFxuXHRcdFx0c3VjY2VzczogY2FsbGJhY2tcblx0XHR9KTtcblx0fTtcbn0pO1xuXG5cbmpRdWVyeS5fZXZhbFVybCA9IGZ1bmN0aW9uKCB1cmwgKSB7XG5cdHJldHVybiBqUXVlcnkuYWpheCh7XG5cdFx0dXJsOiB1cmwsXG5cdFx0dHlwZTogXCJHRVRcIixcblx0XHRkYXRhVHlwZTogXCJzY3JpcHRcIixcblx0XHRhc3luYzogZmFsc2UsXG5cdFx0Z2xvYmFsOiBmYWxzZSxcblx0XHRcInRocm93c1wiOiB0cnVlXG5cdH0pO1xufTtcblxuXG5qUXVlcnkuZm4uZXh0ZW5kKHtcblx0d3JhcEFsbDogZnVuY3Rpb24oIGh0bWwgKSB7XG5cdFx0dmFyIHdyYXA7XG5cblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBodG1sICkgKSB7XG5cdFx0XHRyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCBpICkge1xuXHRcdFx0XHRqUXVlcnkoIHRoaXMgKS53cmFwQWxsKCBodG1sLmNhbGwodGhpcywgaSkgKTtcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdGlmICggdGhpc1sgMCBdICkge1xuXG5cdFx0XHQvLyBUaGUgZWxlbWVudHMgdG8gd3JhcCB0aGUgdGFyZ2V0IGFyb3VuZFxuXHRcdFx0d3JhcCA9IGpRdWVyeSggaHRtbCwgdGhpc1sgMCBdLm93bmVyRG9jdW1lbnQgKS5lcSggMCApLmNsb25lKCB0cnVlICk7XG5cblx0XHRcdGlmICggdGhpc1sgMCBdLnBhcmVudE5vZGUgKSB7XG5cdFx0XHRcdHdyYXAuaW5zZXJ0QmVmb3JlKCB0aGlzWyAwIF0gKTtcblx0XHRcdH1cblxuXHRcdFx0d3JhcC5tYXAoZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHZhciBlbGVtID0gdGhpcztcblxuXHRcdFx0XHR3aGlsZSAoIGVsZW0uZmlyc3RFbGVtZW50Q2hpbGQgKSB7XG5cdFx0XHRcdFx0ZWxlbSA9IGVsZW0uZmlyc3RFbGVtZW50Q2hpbGQ7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHRyZXR1cm4gZWxlbTtcblx0XHRcdH0pLmFwcGVuZCggdGhpcyApO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzO1xuXHR9LFxuXG5cdHdyYXBJbm5lcjogZnVuY3Rpb24oIGh0bWwgKSB7XG5cdFx0aWYgKCBqUXVlcnkuaXNGdW5jdGlvbiggaHRtbCApICkge1xuXHRcdFx0cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbiggaSApIHtcblx0XHRcdFx0alF1ZXJ5KCB0aGlzICkud3JhcElubmVyKCBodG1sLmNhbGwodGhpcywgaSkgKTtcblx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgc2VsZiA9IGpRdWVyeSggdGhpcyApLFxuXHRcdFx0XHRjb250ZW50cyA9IHNlbGYuY29udGVudHMoKTtcblxuXHRcdFx0aWYgKCBjb250ZW50cy5sZW5ndGggKSB7XG5cdFx0XHRcdGNvbnRlbnRzLndyYXBBbGwoIGh0bWwgKTtcblxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0c2VsZi5hcHBlbmQoIGh0bWwgKTtcblx0XHRcdH1cblx0XHR9KTtcblx0fSxcblxuXHR3cmFwOiBmdW5jdGlvbiggaHRtbCApIHtcblx0XHR2YXIgaXNGdW5jdGlvbiA9IGpRdWVyeS5pc0Z1bmN0aW9uKCBodG1sICk7XG5cblx0XHRyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCBpICkge1xuXHRcdFx0alF1ZXJ5KCB0aGlzICkud3JhcEFsbCggaXNGdW5jdGlvbiA/IGh0bWwuY2FsbCh0aGlzLCBpKSA6IGh0bWwgKTtcblx0XHR9KTtcblx0fSxcblxuXHR1bndyYXA6IGZ1bmN0aW9uKCkge1xuXHRcdHJldHVybiB0aGlzLnBhcmVudCgpLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHRpZiAoICFqUXVlcnkubm9kZU5hbWUoIHRoaXMsIFwiYm9keVwiICkgKSB7XG5cdFx0XHRcdGpRdWVyeSggdGhpcyApLnJlcGxhY2VXaXRoKCB0aGlzLmNoaWxkTm9kZXMgKTtcblx0XHRcdH1cblx0XHR9KS5lbmQoKTtcblx0fVxufSk7XG5cblxualF1ZXJ5LmV4cHIuZmlsdGVycy5oaWRkZW4gPSBmdW5jdGlvbiggZWxlbSApIHtcblx0Ly8gU3VwcG9ydDogT3BlcmEgPD0gMTIuMTJcblx0Ly8gT3BlcmEgcmVwb3J0cyBvZmZzZXRXaWR0aHMgYW5kIG9mZnNldEhlaWdodHMgbGVzcyB0aGFuIHplcm8gb24gc29tZSBlbGVtZW50c1xuXHRyZXR1cm4gZWxlbS5vZmZzZXRXaWR0aCA8PSAwICYmIGVsZW0ub2Zmc2V0SGVpZ2h0IDw9IDA7XG59O1xualF1ZXJ5LmV4cHIuZmlsdGVycy52aXNpYmxlID0gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdHJldHVybiAhalF1ZXJ5LmV4cHIuZmlsdGVycy5oaWRkZW4oIGVsZW0gKTtcbn07XG5cblxuXG5cbnZhciByMjAgPSAvJTIwL2csXG5cdHJicmFja2V0ID0gL1xcW1xcXSQvLFxuXHRyQ1JMRiA9IC9cXHI/XFxuL2csXG5cdHJzdWJtaXR0ZXJUeXBlcyA9IC9eKD86c3VibWl0fGJ1dHRvbnxpbWFnZXxyZXNldHxmaWxlKSQvaSxcblx0cnN1Ym1pdHRhYmxlID0gL14oPzppbnB1dHxzZWxlY3R8dGV4dGFyZWF8a2V5Z2VuKS9pO1xuXG5mdW5jdGlvbiBidWlsZFBhcmFtcyggcHJlZml4LCBvYmosIHRyYWRpdGlvbmFsLCBhZGQgKSB7XG5cdHZhciBuYW1lO1xuXG5cdGlmICggalF1ZXJ5LmlzQXJyYXkoIG9iaiApICkge1xuXHRcdC8vIFNlcmlhbGl6ZSBhcnJheSBpdGVtLlxuXHRcdGpRdWVyeS5lYWNoKCBvYmosIGZ1bmN0aW9uKCBpLCB2ICkge1xuXHRcdFx0aWYgKCB0cmFkaXRpb25hbCB8fCByYnJhY2tldC50ZXN0KCBwcmVmaXggKSApIHtcblx0XHRcdFx0Ly8gVHJlYXQgZWFjaCBhcnJheSBpdGVtIGFzIGEgc2NhbGFyLlxuXHRcdFx0XHRhZGQoIHByZWZpeCwgdiApO1xuXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQvLyBJdGVtIGlzIG5vbi1zY2FsYXIgKGFycmF5IG9yIG9iamVjdCksIGVuY29kZSBpdHMgbnVtZXJpYyBpbmRleC5cblx0XHRcdFx0YnVpbGRQYXJhbXMoIHByZWZpeCArIFwiW1wiICsgKCB0eXBlb2YgdiA9PT0gXCJvYmplY3RcIiA/IGkgOiBcIlwiICkgKyBcIl1cIiwgdiwgdHJhZGl0aW9uYWwsIGFkZCApO1xuXHRcdFx0fVxuXHRcdH0pO1xuXG5cdH0gZWxzZSBpZiAoICF0cmFkaXRpb25hbCAmJiBqUXVlcnkudHlwZSggb2JqICkgPT09IFwib2JqZWN0XCIgKSB7XG5cdFx0Ly8gU2VyaWFsaXplIG9iamVjdCBpdGVtLlxuXHRcdGZvciAoIG5hbWUgaW4gb2JqICkge1xuXHRcdFx0YnVpbGRQYXJhbXMoIHByZWZpeCArIFwiW1wiICsgbmFtZSArIFwiXVwiLCBvYmpbIG5hbWUgXSwgdHJhZGl0aW9uYWwsIGFkZCApO1xuXHRcdH1cblxuXHR9IGVsc2Uge1xuXHRcdC8vIFNlcmlhbGl6ZSBzY2FsYXIgaXRlbS5cblx0XHRhZGQoIHByZWZpeCwgb2JqICk7XG5cdH1cbn1cblxuLy8gU2VyaWFsaXplIGFuIGFycmF5IG9mIGZvcm0gZWxlbWVudHMgb3IgYSBzZXQgb2Zcbi8vIGtleS92YWx1ZXMgaW50byBhIHF1ZXJ5IHN0cmluZ1xualF1ZXJ5LnBhcmFtID0gZnVuY3Rpb24oIGEsIHRyYWRpdGlvbmFsICkge1xuXHR2YXIgcHJlZml4LFxuXHRcdHMgPSBbXSxcblx0XHRhZGQgPSBmdW5jdGlvbigga2V5LCB2YWx1ZSApIHtcblx0XHRcdC8vIElmIHZhbHVlIGlzIGEgZnVuY3Rpb24sIGludm9rZSBpdCBhbmQgcmV0dXJuIGl0cyB2YWx1ZVxuXHRcdFx0dmFsdWUgPSBqUXVlcnkuaXNGdW5jdGlvbiggdmFsdWUgKSA/IHZhbHVlKCkgOiAoIHZhbHVlID09IG51bGwgPyBcIlwiIDogdmFsdWUgKTtcblx0XHRcdHNbIHMubGVuZ3RoIF0gPSBlbmNvZGVVUklDb21wb25lbnQoIGtleSApICsgXCI9XCIgKyBlbmNvZGVVUklDb21wb25lbnQoIHZhbHVlICk7XG5cdFx0fTtcblxuXHQvLyBTZXQgdHJhZGl0aW9uYWwgdG8gdHJ1ZSBmb3IgalF1ZXJ5IDw9IDEuMy4yIGJlaGF2aW9yLlxuXHRpZiAoIHRyYWRpdGlvbmFsID09PSB1bmRlZmluZWQgKSB7XG5cdFx0dHJhZGl0aW9uYWwgPSBqUXVlcnkuYWpheFNldHRpbmdzICYmIGpRdWVyeS5hamF4U2V0dGluZ3MudHJhZGl0aW9uYWw7XG5cdH1cblxuXHQvLyBJZiBhbiBhcnJheSB3YXMgcGFzc2VkIGluLCBhc3N1bWUgdGhhdCBpdCBpcyBhbiBhcnJheSBvZiBmb3JtIGVsZW1lbnRzLlxuXHRpZiAoIGpRdWVyeS5pc0FycmF5KCBhICkgfHwgKCBhLmpxdWVyeSAmJiAhalF1ZXJ5LmlzUGxhaW5PYmplY3QoIGEgKSApICkge1xuXHRcdC8vIFNlcmlhbGl6ZSB0aGUgZm9ybSBlbGVtZW50c1xuXHRcdGpRdWVyeS5lYWNoKCBhLCBmdW5jdGlvbigpIHtcblx0XHRcdGFkZCggdGhpcy5uYW1lLCB0aGlzLnZhbHVlICk7XG5cdFx0fSk7XG5cblx0fSBlbHNlIHtcblx0XHQvLyBJZiB0cmFkaXRpb25hbCwgZW5jb2RlIHRoZSBcIm9sZFwiIHdheSAodGhlIHdheSAxLjMuMiBvciBvbGRlclxuXHRcdC8vIGRpZCBpdCksIG90aGVyd2lzZSBlbmNvZGUgcGFyYW1zIHJlY3Vyc2l2ZWx5LlxuXHRcdGZvciAoIHByZWZpeCBpbiBhICkge1xuXHRcdFx0YnVpbGRQYXJhbXMoIHByZWZpeCwgYVsgcHJlZml4IF0sIHRyYWRpdGlvbmFsLCBhZGQgKTtcblx0XHR9XG5cdH1cblxuXHQvLyBSZXR1cm4gdGhlIHJlc3VsdGluZyBzZXJpYWxpemF0aW9uXG5cdHJldHVybiBzLmpvaW4oIFwiJlwiICkucmVwbGFjZSggcjIwLCBcIitcIiApO1xufTtcblxualF1ZXJ5LmZuLmV4dGVuZCh7XG5cdHNlcmlhbGl6ZTogZnVuY3Rpb24oKSB7XG5cdFx0cmV0dXJuIGpRdWVyeS5wYXJhbSggdGhpcy5zZXJpYWxpemVBcnJheSgpICk7XG5cdH0sXG5cdHNlcmlhbGl6ZUFycmF5OiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5tYXAoZnVuY3Rpb24oKSB7XG5cdFx0XHQvLyBDYW4gYWRkIHByb3BIb29rIGZvciBcImVsZW1lbnRzXCIgdG8gZmlsdGVyIG9yIGFkZCBmb3JtIGVsZW1lbnRzXG5cdFx0XHR2YXIgZWxlbWVudHMgPSBqUXVlcnkucHJvcCggdGhpcywgXCJlbGVtZW50c1wiICk7XG5cdFx0XHRyZXR1cm4gZWxlbWVudHMgPyBqUXVlcnkubWFrZUFycmF5KCBlbGVtZW50cyApIDogdGhpcztcblx0XHR9KVxuXHRcdC5maWx0ZXIoZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgdHlwZSA9IHRoaXMudHlwZTtcblxuXHRcdFx0Ly8gVXNlIC5pcyggXCI6ZGlzYWJsZWRcIiApIHNvIHRoYXQgZmllbGRzZXRbZGlzYWJsZWRdIHdvcmtzXG5cdFx0XHRyZXR1cm4gdGhpcy5uYW1lICYmICFqUXVlcnkoIHRoaXMgKS5pcyggXCI6ZGlzYWJsZWRcIiApICYmXG5cdFx0XHRcdHJzdWJtaXR0YWJsZS50ZXN0KCB0aGlzLm5vZGVOYW1lICkgJiYgIXJzdWJtaXR0ZXJUeXBlcy50ZXN0KCB0eXBlICkgJiZcblx0XHRcdFx0KCB0aGlzLmNoZWNrZWQgfHwgIXJjaGVja2FibGVUeXBlLnRlc3QoIHR5cGUgKSApO1xuXHRcdH0pXG5cdFx0Lm1hcChmdW5jdGlvbiggaSwgZWxlbSApIHtcblx0XHRcdHZhciB2YWwgPSBqUXVlcnkoIHRoaXMgKS52YWwoKTtcblxuXHRcdFx0cmV0dXJuIHZhbCA9PSBudWxsID9cblx0XHRcdFx0bnVsbCA6XG5cdFx0XHRcdGpRdWVyeS5pc0FycmF5KCB2YWwgKSA/XG5cdFx0XHRcdFx0alF1ZXJ5Lm1hcCggdmFsLCBmdW5jdGlvbiggdmFsICkge1xuXHRcdFx0XHRcdFx0cmV0dXJuIHsgbmFtZTogZWxlbS5uYW1lLCB2YWx1ZTogdmFsLnJlcGxhY2UoIHJDUkxGLCBcIlxcclxcblwiICkgfTtcblx0XHRcdFx0XHR9KSA6XG5cdFx0XHRcdFx0eyBuYW1lOiBlbGVtLm5hbWUsIHZhbHVlOiB2YWwucmVwbGFjZSggckNSTEYsIFwiXFxyXFxuXCIgKSB9O1xuXHRcdH0pLmdldCgpO1xuXHR9XG59KTtcblxuXG5qUXVlcnkuYWpheFNldHRpbmdzLnhociA9IGZ1bmN0aW9uKCkge1xuXHR0cnkge1xuXHRcdHJldHVybiBuZXcgWE1MSHR0cFJlcXVlc3QoKTtcblx0fSBjYXRjaCggZSApIHt9XG59O1xuXG52YXIgeGhySWQgPSAwLFxuXHR4aHJDYWxsYmFja3MgPSB7fSxcblx0eGhyU3VjY2Vzc1N0YXR1cyA9IHtcblx0XHQvLyBmaWxlIHByb3RvY29sIGFsd2F5cyB5aWVsZHMgc3RhdHVzIGNvZGUgMCwgYXNzdW1lIDIwMFxuXHRcdDA6IDIwMCxcblx0XHQvLyBTdXBwb3J0OiBJRTlcblx0XHQvLyAjMTQ1MDogc29tZXRpbWVzIElFIHJldHVybnMgMTIyMyB3aGVuIGl0IHNob3VsZCBiZSAyMDRcblx0XHQxMjIzOiAyMDRcblx0fSxcblx0eGhyU3VwcG9ydGVkID0galF1ZXJ5LmFqYXhTZXR0aW5ncy54aHIoKTtcblxuLy8gU3VwcG9ydDogSUU5XG4vLyBPcGVuIHJlcXVlc3RzIG11c3QgYmUgbWFudWFsbHkgYWJvcnRlZCBvbiB1bmxvYWQgKCM1MjgwKVxuLy8gU2VlIGh0dHBzOi8vc3VwcG9ydC5taWNyb3NvZnQuY29tL2tiLzI4NTY3NDYgZm9yIG1vcmUgaW5mb1xuaWYgKCB3aW5kb3cuYXR0YWNoRXZlbnQgKSB7XG5cdHdpbmRvdy5hdHRhY2hFdmVudCggXCJvbnVubG9hZFwiLCBmdW5jdGlvbigpIHtcblx0XHRmb3IgKCB2YXIga2V5IGluIHhockNhbGxiYWNrcyApIHtcblx0XHRcdHhockNhbGxiYWNrc1sga2V5IF0oKTtcblx0XHR9XG5cdH0pO1xufVxuXG5zdXBwb3J0LmNvcnMgPSAhIXhoclN1cHBvcnRlZCAmJiAoIFwid2l0aENyZWRlbnRpYWxzXCIgaW4geGhyU3VwcG9ydGVkICk7XG5zdXBwb3J0LmFqYXggPSB4aHJTdXBwb3J0ZWQgPSAhIXhoclN1cHBvcnRlZDtcblxualF1ZXJ5LmFqYXhUcmFuc3BvcnQoZnVuY3Rpb24oIG9wdGlvbnMgKSB7XG5cdHZhciBjYWxsYmFjaztcblxuXHQvLyBDcm9zcyBkb21haW4gb25seSBhbGxvd2VkIGlmIHN1cHBvcnRlZCB0aHJvdWdoIFhNTEh0dHBSZXF1ZXN0XG5cdGlmICggc3VwcG9ydC5jb3JzIHx8IHhoclN1cHBvcnRlZCAmJiAhb3B0aW9ucy5jcm9zc0RvbWFpbiApIHtcblx0XHRyZXR1cm4ge1xuXHRcdFx0c2VuZDogZnVuY3Rpb24oIGhlYWRlcnMsIGNvbXBsZXRlICkge1xuXHRcdFx0XHR2YXIgaSxcblx0XHRcdFx0XHR4aHIgPSBvcHRpb25zLnhocigpLFxuXHRcdFx0XHRcdGlkID0gKyt4aHJJZDtcblxuXHRcdFx0XHR4aHIub3Blbiggb3B0aW9ucy50eXBlLCBvcHRpb25zLnVybCwgb3B0aW9ucy5hc3luYywgb3B0aW9ucy51c2VybmFtZSwgb3B0aW9ucy5wYXNzd29yZCApO1xuXG5cdFx0XHRcdC8vIEFwcGx5IGN1c3RvbSBmaWVsZHMgaWYgcHJvdmlkZWRcblx0XHRcdFx0aWYgKCBvcHRpb25zLnhockZpZWxkcyApIHtcblx0XHRcdFx0XHRmb3IgKCBpIGluIG9wdGlvbnMueGhyRmllbGRzICkge1xuXHRcdFx0XHRcdFx0eGhyWyBpIF0gPSBvcHRpb25zLnhockZpZWxkc1sgaSBdO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdC8vIE92ZXJyaWRlIG1pbWUgdHlwZSBpZiBuZWVkZWRcblx0XHRcdFx0aWYgKCBvcHRpb25zLm1pbWVUeXBlICYmIHhoci5vdmVycmlkZU1pbWVUeXBlICkge1xuXHRcdFx0XHRcdHhoci5vdmVycmlkZU1pbWVUeXBlKCBvcHRpb25zLm1pbWVUeXBlICk7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBYLVJlcXVlc3RlZC1XaXRoIGhlYWRlclxuXHRcdFx0XHQvLyBGb3IgY3Jvc3MtZG9tYWluIHJlcXVlc3RzLCBzZWVpbmcgYXMgY29uZGl0aW9ucyBmb3IgYSBwcmVmbGlnaHQgYXJlXG5cdFx0XHRcdC8vIGFraW4gdG8gYSBqaWdzYXcgcHV6emxlLCB3ZSBzaW1wbHkgbmV2ZXIgc2V0IGl0IHRvIGJlIHN1cmUuXG5cdFx0XHRcdC8vIChpdCBjYW4gYWx3YXlzIGJlIHNldCBvbiBhIHBlci1yZXF1ZXN0IGJhc2lzIG9yIGV2ZW4gdXNpbmcgYWpheFNldHVwKVxuXHRcdFx0XHQvLyBGb3Igc2FtZS1kb21haW4gcmVxdWVzdHMsIHdvbid0IGNoYW5nZSBoZWFkZXIgaWYgYWxyZWFkeSBwcm92aWRlZC5cblx0XHRcdFx0aWYgKCAhb3B0aW9ucy5jcm9zc0RvbWFpbiAmJiAhaGVhZGVyc1tcIlgtUmVxdWVzdGVkLVdpdGhcIl0gKSB7XG5cdFx0XHRcdFx0aGVhZGVyc1tcIlgtUmVxdWVzdGVkLVdpdGhcIl0gPSBcIlhNTEh0dHBSZXF1ZXN0XCI7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBTZXQgaGVhZGVyc1xuXHRcdFx0XHRmb3IgKCBpIGluIGhlYWRlcnMgKSB7XG5cdFx0XHRcdFx0eGhyLnNldFJlcXVlc3RIZWFkZXIoIGksIGhlYWRlcnNbIGkgXSApO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gQ2FsbGJhY2tcblx0XHRcdFx0Y2FsbGJhY2sgPSBmdW5jdGlvbiggdHlwZSApIHtcblx0XHRcdFx0XHRyZXR1cm4gZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0XHRpZiAoIGNhbGxiYWNrICkge1xuXHRcdFx0XHRcdFx0XHRkZWxldGUgeGhyQ2FsbGJhY2tzWyBpZCBdO1xuXHRcdFx0XHRcdFx0XHRjYWxsYmFjayA9IHhoci5vbmxvYWQgPSB4aHIub25lcnJvciA9IG51bGw7XG5cblx0XHRcdFx0XHRcdFx0aWYgKCB0eXBlID09PSBcImFib3J0XCIgKSB7XG5cdFx0XHRcdFx0XHRcdFx0eGhyLmFib3J0KCk7XG5cdFx0XHRcdFx0XHRcdH0gZWxzZSBpZiAoIHR5cGUgPT09IFwiZXJyb3JcIiApIHtcblx0XHRcdFx0XHRcdFx0XHRjb21wbGV0ZShcblx0XHRcdFx0XHRcdFx0XHRcdC8vIGZpbGU6IHByb3RvY29sIGFsd2F5cyB5aWVsZHMgc3RhdHVzIDA7IHNlZSAjODYwNSwgIzE0MjA3XG5cdFx0XHRcdFx0XHRcdFx0XHR4aHIuc3RhdHVzLFxuXHRcdFx0XHRcdFx0XHRcdFx0eGhyLnN0YXR1c1RleHRcblx0XHRcdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHRcdGNvbXBsZXRlKFxuXHRcdFx0XHRcdFx0XHRcdFx0eGhyU3VjY2Vzc1N0YXR1c1sgeGhyLnN0YXR1cyBdIHx8IHhoci5zdGF0dXMsXG5cdFx0XHRcdFx0XHRcdFx0XHR4aHIuc3RhdHVzVGV4dCxcblx0XHRcdFx0XHRcdFx0XHRcdC8vIFN1cHBvcnQ6IElFOVxuXHRcdFx0XHRcdFx0XHRcdFx0Ly8gQWNjZXNzaW5nIGJpbmFyeS1kYXRhIHJlc3BvbnNlVGV4dCB0aHJvd3MgYW4gZXhjZXB0aW9uXG5cdFx0XHRcdFx0XHRcdFx0XHQvLyAoIzExNDI2KVxuXHRcdFx0XHRcdFx0XHRcdFx0dHlwZW9mIHhoci5yZXNwb25zZVRleHQgPT09IFwic3RyaW5nXCIgPyB7XG5cdFx0XHRcdFx0XHRcdFx0XHRcdHRleHQ6IHhoci5yZXNwb25zZVRleHRcblx0XHRcdFx0XHRcdFx0XHRcdH0gOiB1bmRlZmluZWQsXG5cdFx0XHRcdFx0XHRcdFx0XHR4aHIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKClcblx0XHRcdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fTtcblx0XHRcdFx0fTtcblxuXHRcdFx0XHQvLyBMaXN0ZW4gdG8gZXZlbnRzXG5cdFx0XHRcdHhoci5vbmxvYWQgPSBjYWxsYmFjaygpO1xuXHRcdFx0XHR4aHIub25lcnJvciA9IGNhbGxiYWNrKFwiZXJyb3JcIik7XG5cblx0XHRcdFx0Ly8gQ3JlYXRlIHRoZSBhYm9ydCBjYWxsYmFja1xuXHRcdFx0XHRjYWxsYmFjayA9IHhockNhbGxiYWNrc1sgaWQgXSA9IGNhbGxiYWNrKFwiYWJvcnRcIik7XG5cblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHQvLyBEbyBzZW5kIHRoZSByZXF1ZXN0ICh0aGlzIG1heSByYWlzZSBhbiBleGNlcHRpb24pXG5cdFx0XHRcdFx0eGhyLnNlbmQoIG9wdGlvbnMuaGFzQ29udGVudCAmJiBvcHRpb25zLmRhdGEgfHwgbnVsbCApO1xuXHRcdFx0XHR9IGNhdGNoICggZSApIHtcblx0XHRcdFx0XHQvLyAjMTQ2ODM6IE9ubHkgcmV0aHJvdyBpZiB0aGlzIGhhc24ndCBiZWVuIG5vdGlmaWVkIGFzIGFuIGVycm9yIHlldFxuXHRcdFx0XHRcdGlmICggY2FsbGJhY2sgKSB7XG5cdFx0XHRcdFx0XHR0aHJvdyBlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSxcblxuXHRcdFx0YWJvcnQ6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRpZiAoIGNhbGxiYWNrICkge1xuXHRcdFx0XHRcdGNhbGxiYWNrKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9O1xuXHR9XG59KTtcblxuXG5cblxuLy8gSW5zdGFsbCBzY3JpcHQgZGF0YVR5cGVcbmpRdWVyeS5hamF4U2V0dXAoe1xuXHRhY2NlcHRzOiB7XG5cdFx0c2NyaXB0OiBcInRleHQvamF2YXNjcmlwdCwgYXBwbGljYXRpb24vamF2YXNjcmlwdCwgYXBwbGljYXRpb24vZWNtYXNjcmlwdCwgYXBwbGljYXRpb24veC1lY21hc2NyaXB0XCJcblx0fSxcblx0Y29udGVudHM6IHtcblx0XHRzY3JpcHQ6IC8oPzpqYXZhfGVjbWEpc2NyaXB0L1xuXHR9LFxuXHRjb252ZXJ0ZXJzOiB7XG5cdFx0XCJ0ZXh0IHNjcmlwdFwiOiBmdW5jdGlvbiggdGV4dCApIHtcblx0XHRcdGpRdWVyeS5nbG9iYWxFdmFsKCB0ZXh0ICk7XG5cdFx0XHRyZXR1cm4gdGV4dDtcblx0XHR9XG5cdH1cbn0pO1xuXG4vLyBIYW5kbGUgY2FjaGUncyBzcGVjaWFsIGNhc2UgYW5kIGNyb3NzRG9tYWluXG5qUXVlcnkuYWpheFByZWZpbHRlciggXCJzY3JpcHRcIiwgZnVuY3Rpb24oIHMgKSB7XG5cdGlmICggcy5jYWNoZSA9PT0gdW5kZWZpbmVkICkge1xuXHRcdHMuY2FjaGUgPSBmYWxzZTtcblx0fVxuXHRpZiAoIHMuY3Jvc3NEb21haW4gKSB7XG5cdFx0cy50eXBlID0gXCJHRVRcIjtcblx0fVxufSk7XG5cbi8vIEJpbmQgc2NyaXB0IHRhZyBoYWNrIHRyYW5zcG9ydFxualF1ZXJ5LmFqYXhUcmFuc3BvcnQoIFwic2NyaXB0XCIsIGZ1bmN0aW9uKCBzICkge1xuXHQvLyBUaGlzIHRyYW5zcG9ydCBvbmx5IGRlYWxzIHdpdGggY3Jvc3MgZG9tYWluIHJlcXVlc3RzXG5cdGlmICggcy5jcm9zc0RvbWFpbiApIHtcblx0XHR2YXIgc2NyaXB0LCBjYWxsYmFjaztcblx0XHRyZXR1cm4ge1xuXHRcdFx0c2VuZDogZnVuY3Rpb24oIF8sIGNvbXBsZXRlICkge1xuXHRcdFx0XHRzY3JpcHQgPSBqUXVlcnkoXCI8c2NyaXB0PlwiKS5wcm9wKHtcblx0XHRcdFx0XHRhc3luYzogdHJ1ZSxcblx0XHRcdFx0XHRjaGFyc2V0OiBzLnNjcmlwdENoYXJzZXQsXG5cdFx0XHRcdFx0c3JjOiBzLnVybFxuXHRcdFx0XHR9KS5vbihcblx0XHRcdFx0XHRcImxvYWQgZXJyb3JcIixcblx0XHRcdFx0XHRjYWxsYmFjayA9IGZ1bmN0aW9uKCBldnQgKSB7XG5cdFx0XHRcdFx0XHRzY3JpcHQucmVtb3ZlKCk7XG5cdFx0XHRcdFx0XHRjYWxsYmFjayA9IG51bGw7XG5cdFx0XHRcdFx0XHRpZiAoIGV2dCApIHtcblx0XHRcdFx0XHRcdFx0Y29tcGxldGUoIGV2dC50eXBlID09PSBcImVycm9yXCIgPyA0MDQgOiAyMDAsIGV2dC50eXBlICk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHQpO1xuXHRcdFx0XHRkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKCBzY3JpcHRbIDAgXSApO1xuXHRcdFx0fSxcblx0XHRcdGFib3J0OiBmdW5jdGlvbigpIHtcblx0XHRcdFx0aWYgKCBjYWxsYmFjayApIHtcblx0XHRcdFx0XHRjYWxsYmFjaygpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fTtcblx0fVxufSk7XG5cblxuXG5cbnZhciBvbGRDYWxsYmFja3MgPSBbXSxcblx0cmpzb25wID0gLyg9KVxcPyg/PSZ8JCl8XFw/XFw/LztcblxuLy8gRGVmYXVsdCBqc29ucCBzZXR0aW5nc1xualF1ZXJ5LmFqYXhTZXR1cCh7XG5cdGpzb25wOiBcImNhbGxiYWNrXCIsXG5cdGpzb25wQ2FsbGJhY2s6IGZ1bmN0aW9uKCkge1xuXHRcdHZhciBjYWxsYmFjayA9IG9sZENhbGxiYWNrcy5wb3AoKSB8fCAoIGpRdWVyeS5leHBhbmRvICsgXCJfXCIgKyAoIG5vbmNlKysgKSApO1xuXHRcdHRoaXNbIGNhbGxiYWNrIF0gPSB0cnVlO1xuXHRcdHJldHVybiBjYWxsYmFjaztcblx0fVxufSk7XG5cbi8vIERldGVjdCwgbm9ybWFsaXplIG9wdGlvbnMgYW5kIGluc3RhbGwgY2FsbGJhY2tzIGZvciBqc29ucCByZXF1ZXN0c1xualF1ZXJ5LmFqYXhQcmVmaWx0ZXIoIFwianNvbiBqc29ucFwiLCBmdW5jdGlvbiggcywgb3JpZ2luYWxTZXR0aW5ncywganFYSFIgKSB7XG5cblx0dmFyIGNhbGxiYWNrTmFtZSwgb3ZlcndyaXR0ZW4sIHJlc3BvbnNlQ29udGFpbmVyLFxuXHRcdGpzb25Qcm9wID0gcy5qc29ucCAhPT0gZmFsc2UgJiYgKCByanNvbnAudGVzdCggcy51cmwgKSA/XG5cdFx0XHRcInVybFwiIDpcblx0XHRcdHR5cGVvZiBzLmRhdGEgPT09IFwic3RyaW5nXCIgJiYgISggcy5jb250ZW50VHlwZSB8fCBcIlwiICkuaW5kZXhPZihcImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFwiKSAmJiByanNvbnAudGVzdCggcy5kYXRhICkgJiYgXCJkYXRhXCJcblx0XHQpO1xuXG5cdC8vIEhhbmRsZSBpZmYgdGhlIGV4cGVjdGVkIGRhdGEgdHlwZSBpcyBcImpzb25wXCIgb3Igd2UgaGF2ZSBhIHBhcmFtZXRlciB0byBzZXRcblx0aWYgKCBqc29uUHJvcCB8fCBzLmRhdGFUeXBlc1sgMCBdID09PSBcImpzb25wXCIgKSB7XG5cblx0XHQvLyBHZXQgY2FsbGJhY2sgbmFtZSwgcmVtZW1iZXJpbmcgcHJlZXhpc3RpbmcgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIGl0XG5cdFx0Y2FsbGJhY2tOYW1lID0gcy5qc29ucENhbGxiYWNrID0galF1ZXJ5LmlzRnVuY3Rpb24oIHMuanNvbnBDYWxsYmFjayApID9cblx0XHRcdHMuanNvbnBDYWxsYmFjaygpIDpcblx0XHRcdHMuanNvbnBDYWxsYmFjaztcblxuXHRcdC8vIEluc2VydCBjYWxsYmFjayBpbnRvIHVybCBvciBmb3JtIGRhdGFcblx0XHRpZiAoIGpzb25Qcm9wICkge1xuXHRcdFx0c1sganNvblByb3AgXSA9IHNbIGpzb25Qcm9wIF0ucmVwbGFjZSggcmpzb25wLCBcIiQxXCIgKyBjYWxsYmFja05hbWUgKTtcblx0XHR9IGVsc2UgaWYgKCBzLmpzb25wICE9PSBmYWxzZSApIHtcblx0XHRcdHMudXJsICs9ICggcnF1ZXJ5LnRlc3QoIHMudXJsICkgPyBcIiZcIiA6IFwiP1wiICkgKyBzLmpzb25wICsgXCI9XCIgKyBjYWxsYmFja05hbWU7XG5cdFx0fVxuXG5cdFx0Ly8gVXNlIGRhdGEgY29udmVydGVyIHRvIHJldHJpZXZlIGpzb24gYWZ0ZXIgc2NyaXB0IGV4ZWN1dGlvblxuXHRcdHMuY29udmVydGVyc1tcInNjcmlwdCBqc29uXCJdID0gZnVuY3Rpb24oKSB7XG5cdFx0XHRpZiAoICFyZXNwb25zZUNvbnRhaW5lciApIHtcblx0XHRcdFx0alF1ZXJ5LmVycm9yKCBjYWxsYmFja05hbWUgKyBcIiB3YXMgbm90IGNhbGxlZFwiICk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gcmVzcG9uc2VDb250YWluZXJbIDAgXTtcblx0XHR9O1xuXG5cdFx0Ly8gZm9yY2UganNvbiBkYXRhVHlwZVxuXHRcdHMuZGF0YVR5cGVzWyAwIF0gPSBcImpzb25cIjtcblxuXHRcdC8vIEluc3RhbGwgY2FsbGJhY2tcblx0XHRvdmVyd3JpdHRlbiA9IHdpbmRvd1sgY2FsbGJhY2tOYW1lIF07XG5cdFx0d2luZG93WyBjYWxsYmFja05hbWUgXSA9IGZ1bmN0aW9uKCkge1xuXHRcdFx0cmVzcG9uc2VDb250YWluZXIgPSBhcmd1bWVudHM7XG5cdFx0fTtcblxuXHRcdC8vIENsZWFuLXVwIGZ1bmN0aW9uIChmaXJlcyBhZnRlciBjb252ZXJ0ZXJzKVxuXHRcdGpxWEhSLmFsd2F5cyhmdW5jdGlvbigpIHtcblx0XHRcdC8vIFJlc3RvcmUgcHJlZXhpc3RpbmcgdmFsdWVcblx0XHRcdHdpbmRvd1sgY2FsbGJhY2tOYW1lIF0gPSBvdmVyd3JpdHRlbjtcblxuXHRcdFx0Ly8gU2F2ZSBiYWNrIGFzIGZyZWVcblx0XHRcdGlmICggc1sgY2FsbGJhY2tOYW1lIF0gKSB7XG5cdFx0XHRcdC8vIG1ha2Ugc3VyZSB0aGF0IHJlLXVzaW5nIHRoZSBvcHRpb25zIGRvZXNuJ3Qgc2NyZXcgdGhpbmdzIGFyb3VuZFxuXHRcdFx0XHRzLmpzb25wQ2FsbGJhY2sgPSBvcmlnaW5hbFNldHRpbmdzLmpzb25wQ2FsbGJhY2s7XG5cblx0XHRcdFx0Ly8gc2F2ZSB0aGUgY2FsbGJhY2sgbmFtZSBmb3IgZnV0dXJlIHVzZVxuXHRcdFx0XHRvbGRDYWxsYmFja3MucHVzaCggY2FsbGJhY2tOYW1lICk7XG5cdFx0XHR9XG5cblx0XHRcdC8vIENhbGwgaWYgaXQgd2FzIGEgZnVuY3Rpb24gYW5kIHdlIGhhdmUgYSByZXNwb25zZVxuXHRcdFx0aWYgKCByZXNwb25zZUNvbnRhaW5lciAmJiBqUXVlcnkuaXNGdW5jdGlvbiggb3ZlcndyaXR0ZW4gKSApIHtcblx0XHRcdFx0b3ZlcndyaXR0ZW4oIHJlc3BvbnNlQ29udGFpbmVyWyAwIF0gKTtcblx0XHRcdH1cblxuXHRcdFx0cmVzcG9uc2VDb250YWluZXIgPSBvdmVyd3JpdHRlbiA9IHVuZGVmaW5lZDtcblx0XHR9KTtcblxuXHRcdC8vIERlbGVnYXRlIHRvIHNjcmlwdFxuXHRcdHJldHVybiBcInNjcmlwdFwiO1xuXHR9XG59KTtcblxuXG5cblxuLy8gZGF0YTogc3RyaW5nIG9mIGh0bWxcbi8vIGNvbnRleHQgKG9wdGlvbmFsKTogSWYgc3BlY2lmaWVkLCB0aGUgZnJhZ21lbnQgd2lsbCBiZSBjcmVhdGVkIGluIHRoaXMgY29udGV4dCwgZGVmYXVsdHMgdG8gZG9jdW1lbnRcbi8vIGtlZXBTY3JpcHRzIChvcHRpb25hbCk6IElmIHRydWUsIHdpbGwgaW5jbHVkZSBzY3JpcHRzIHBhc3NlZCBpbiB0aGUgaHRtbCBzdHJpbmdcbmpRdWVyeS5wYXJzZUhUTUwgPSBmdW5jdGlvbiggZGF0YSwgY29udGV4dCwga2VlcFNjcmlwdHMgKSB7XG5cdGlmICggIWRhdGEgfHwgdHlwZW9mIGRhdGEgIT09IFwic3RyaW5nXCIgKSB7XG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cblx0aWYgKCB0eXBlb2YgY29udGV4dCA9PT0gXCJib29sZWFuXCIgKSB7XG5cdFx0a2VlcFNjcmlwdHMgPSBjb250ZXh0O1xuXHRcdGNvbnRleHQgPSBmYWxzZTtcblx0fVxuXHRjb250ZXh0ID0gY29udGV4dCB8fCBkb2N1bWVudDtcblxuXHR2YXIgcGFyc2VkID0gcnNpbmdsZVRhZy5leGVjKCBkYXRhICksXG5cdFx0c2NyaXB0cyA9ICFrZWVwU2NyaXB0cyAmJiBbXTtcblxuXHQvLyBTaW5nbGUgdGFnXG5cdGlmICggcGFyc2VkICkge1xuXHRcdHJldHVybiBbIGNvbnRleHQuY3JlYXRlRWxlbWVudCggcGFyc2VkWzFdICkgXTtcblx0fVxuXG5cdHBhcnNlZCA9IGpRdWVyeS5idWlsZEZyYWdtZW50KCBbIGRhdGEgXSwgY29udGV4dCwgc2NyaXB0cyApO1xuXG5cdGlmICggc2NyaXB0cyAmJiBzY3JpcHRzLmxlbmd0aCApIHtcblx0XHRqUXVlcnkoIHNjcmlwdHMgKS5yZW1vdmUoKTtcblx0fVxuXG5cdHJldHVybiBqUXVlcnkubWVyZ2UoIFtdLCBwYXJzZWQuY2hpbGROb2RlcyApO1xufTtcblxuXG4vLyBLZWVwIGEgY29weSBvZiB0aGUgb2xkIGxvYWQgbWV0aG9kXG52YXIgX2xvYWQgPSBqUXVlcnkuZm4ubG9hZDtcblxuLyoqXG4gKiBMb2FkIGEgdXJsIGludG8gYSBwYWdlXG4gKi9cbmpRdWVyeS5mbi5sb2FkID0gZnVuY3Rpb24oIHVybCwgcGFyYW1zLCBjYWxsYmFjayApIHtcblx0aWYgKCB0eXBlb2YgdXJsICE9PSBcInN0cmluZ1wiICYmIF9sb2FkICkge1xuXHRcdHJldHVybiBfbG9hZC5hcHBseSggdGhpcywgYXJndW1lbnRzICk7XG5cdH1cblxuXHR2YXIgc2VsZWN0b3IsIHR5cGUsIHJlc3BvbnNlLFxuXHRcdHNlbGYgPSB0aGlzLFxuXHRcdG9mZiA9IHVybC5pbmRleE9mKFwiIFwiKTtcblxuXHRpZiAoIG9mZiA+PSAwICkge1xuXHRcdHNlbGVjdG9yID0galF1ZXJ5LnRyaW0oIHVybC5zbGljZSggb2ZmICkgKTtcblx0XHR1cmwgPSB1cmwuc2xpY2UoIDAsIG9mZiApO1xuXHR9XG5cblx0Ly8gSWYgaXQncyBhIGZ1bmN0aW9uXG5cdGlmICggalF1ZXJ5LmlzRnVuY3Rpb24oIHBhcmFtcyApICkge1xuXG5cdFx0Ly8gV2UgYXNzdW1lIHRoYXQgaXQncyB0aGUgY2FsbGJhY2tcblx0XHRjYWxsYmFjayA9IHBhcmFtcztcblx0XHRwYXJhbXMgPSB1bmRlZmluZWQ7XG5cblx0Ly8gT3RoZXJ3aXNlLCBidWlsZCBhIHBhcmFtIHN0cmluZ1xuXHR9IGVsc2UgaWYgKCBwYXJhbXMgJiYgdHlwZW9mIHBhcmFtcyA9PT0gXCJvYmplY3RcIiApIHtcblx0XHR0eXBlID0gXCJQT1NUXCI7XG5cdH1cblxuXHQvLyBJZiB3ZSBoYXZlIGVsZW1lbnRzIHRvIG1vZGlmeSwgbWFrZSB0aGUgcmVxdWVzdFxuXHRpZiAoIHNlbGYubGVuZ3RoID4gMCApIHtcblx0XHRqUXVlcnkuYWpheCh7XG5cdFx0XHR1cmw6IHVybCxcblxuXHRcdFx0Ly8gaWYgXCJ0eXBlXCIgdmFyaWFibGUgaXMgdW5kZWZpbmVkLCB0aGVuIFwiR0VUXCIgbWV0aG9kIHdpbGwgYmUgdXNlZFxuXHRcdFx0dHlwZTogdHlwZSxcblx0XHRcdGRhdGFUeXBlOiBcImh0bWxcIixcblx0XHRcdGRhdGE6IHBhcmFtc1xuXHRcdH0pLmRvbmUoZnVuY3Rpb24oIHJlc3BvbnNlVGV4dCApIHtcblxuXHRcdFx0Ly8gU2F2ZSByZXNwb25zZSBmb3IgdXNlIGluIGNvbXBsZXRlIGNhbGxiYWNrXG5cdFx0XHRyZXNwb25zZSA9IGFyZ3VtZW50cztcblxuXHRcdFx0c2VsZi5odG1sKCBzZWxlY3RvciA/XG5cblx0XHRcdFx0Ly8gSWYgYSBzZWxlY3RvciB3YXMgc3BlY2lmaWVkLCBsb2NhdGUgdGhlIHJpZ2h0IGVsZW1lbnRzIGluIGEgZHVtbXkgZGl2XG5cdFx0XHRcdC8vIEV4Y2x1ZGUgc2NyaXB0cyB0byBhdm9pZCBJRSAnUGVybWlzc2lvbiBEZW5pZWQnIGVycm9yc1xuXHRcdFx0XHRqUXVlcnkoXCI8ZGl2PlwiKS5hcHBlbmQoIGpRdWVyeS5wYXJzZUhUTUwoIHJlc3BvbnNlVGV4dCApICkuZmluZCggc2VsZWN0b3IgKSA6XG5cblx0XHRcdFx0Ly8gT3RoZXJ3aXNlIHVzZSB0aGUgZnVsbCByZXN1bHRcblx0XHRcdFx0cmVzcG9uc2VUZXh0ICk7XG5cblx0XHR9KS5jb21wbGV0ZSggY2FsbGJhY2sgJiYgZnVuY3Rpb24oIGpxWEhSLCBzdGF0dXMgKSB7XG5cdFx0XHRzZWxmLmVhY2goIGNhbGxiYWNrLCByZXNwb25zZSB8fCBbIGpxWEhSLnJlc3BvbnNlVGV4dCwgc3RhdHVzLCBqcVhIUiBdICk7XG5cdFx0fSk7XG5cdH1cblxuXHRyZXR1cm4gdGhpcztcbn07XG5cblxuXG5cbi8vIEF0dGFjaCBhIGJ1bmNoIG9mIGZ1bmN0aW9ucyBmb3IgaGFuZGxpbmcgY29tbW9uIEFKQVggZXZlbnRzXG5qUXVlcnkuZWFjaCggWyBcImFqYXhTdGFydFwiLCBcImFqYXhTdG9wXCIsIFwiYWpheENvbXBsZXRlXCIsIFwiYWpheEVycm9yXCIsIFwiYWpheFN1Y2Nlc3NcIiwgXCJhamF4U2VuZFwiIF0sIGZ1bmN0aW9uKCBpLCB0eXBlICkge1xuXHRqUXVlcnkuZm5bIHR5cGUgXSA9IGZ1bmN0aW9uKCBmbiApIHtcblx0XHRyZXR1cm4gdGhpcy5vbiggdHlwZSwgZm4gKTtcblx0fTtcbn0pO1xuXG5cblxuXG5qUXVlcnkuZXhwci5maWx0ZXJzLmFuaW1hdGVkID0gZnVuY3Rpb24oIGVsZW0gKSB7XG5cdHJldHVybiBqUXVlcnkuZ3JlcChqUXVlcnkudGltZXJzLCBmdW5jdGlvbiggZm4gKSB7XG5cdFx0cmV0dXJuIGVsZW0gPT09IGZuLmVsZW07XG5cdH0pLmxlbmd0aDtcbn07XG5cblxuXG5cbnZhciBkb2NFbGVtID0gd2luZG93LmRvY3VtZW50LmRvY3VtZW50RWxlbWVudDtcblxuLyoqXG4gKiBHZXRzIGEgd2luZG93IGZyb20gYW4gZWxlbWVudFxuICovXG5mdW5jdGlvbiBnZXRXaW5kb3coIGVsZW0gKSB7XG5cdHJldHVybiBqUXVlcnkuaXNXaW5kb3coIGVsZW0gKSA/IGVsZW0gOiBlbGVtLm5vZGVUeXBlID09PSA5ICYmIGVsZW0uZGVmYXVsdFZpZXc7XG59XG5cbmpRdWVyeS5vZmZzZXQgPSB7XG5cdHNldE9mZnNldDogZnVuY3Rpb24oIGVsZW0sIG9wdGlvbnMsIGkgKSB7XG5cdFx0dmFyIGN1clBvc2l0aW9uLCBjdXJMZWZ0LCBjdXJDU1NUb3AsIGN1clRvcCwgY3VyT2Zmc2V0LCBjdXJDU1NMZWZ0LCBjYWxjdWxhdGVQb3NpdGlvbixcblx0XHRcdHBvc2l0aW9uID0galF1ZXJ5LmNzcyggZWxlbSwgXCJwb3NpdGlvblwiICksXG5cdFx0XHRjdXJFbGVtID0galF1ZXJ5KCBlbGVtICksXG5cdFx0XHRwcm9wcyA9IHt9O1xuXG5cdFx0Ly8gU2V0IHBvc2l0aW9uIGZpcnN0LCBpbi1jYXNlIHRvcC9sZWZ0IGFyZSBzZXQgZXZlbiBvbiBzdGF0aWMgZWxlbVxuXHRcdGlmICggcG9zaXRpb24gPT09IFwic3RhdGljXCIgKSB7XG5cdFx0XHRlbGVtLnN0eWxlLnBvc2l0aW9uID0gXCJyZWxhdGl2ZVwiO1xuXHRcdH1cblxuXHRcdGN1ck9mZnNldCA9IGN1ckVsZW0ub2Zmc2V0KCk7XG5cdFx0Y3VyQ1NTVG9wID0galF1ZXJ5LmNzcyggZWxlbSwgXCJ0b3BcIiApO1xuXHRcdGN1ckNTU0xlZnQgPSBqUXVlcnkuY3NzKCBlbGVtLCBcImxlZnRcIiApO1xuXHRcdGNhbGN1bGF0ZVBvc2l0aW9uID0gKCBwb3NpdGlvbiA9PT0gXCJhYnNvbHV0ZVwiIHx8IHBvc2l0aW9uID09PSBcImZpeGVkXCIgKSAmJlxuXHRcdFx0KCBjdXJDU1NUb3AgKyBjdXJDU1NMZWZ0ICkuaW5kZXhPZihcImF1dG9cIikgPiAtMTtcblxuXHRcdC8vIE5lZWQgdG8gYmUgYWJsZSB0byBjYWxjdWxhdGUgcG9zaXRpb24gaWYgZWl0aGVyXG5cdFx0Ly8gdG9wIG9yIGxlZnQgaXMgYXV0byBhbmQgcG9zaXRpb24gaXMgZWl0aGVyIGFic29sdXRlIG9yIGZpeGVkXG5cdFx0aWYgKCBjYWxjdWxhdGVQb3NpdGlvbiApIHtcblx0XHRcdGN1clBvc2l0aW9uID0gY3VyRWxlbS5wb3NpdGlvbigpO1xuXHRcdFx0Y3VyVG9wID0gY3VyUG9zaXRpb24udG9wO1xuXHRcdFx0Y3VyTGVmdCA9IGN1clBvc2l0aW9uLmxlZnQ7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0Y3VyVG9wID0gcGFyc2VGbG9hdCggY3VyQ1NTVG9wICkgfHwgMDtcblx0XHRcdGN1ckxlZnQgPSBwYXJzZUZsb2F0KCBjdXJDU1NMZWZ0ICkgfHwgMDtcblx0XHR9XG5cblx0XHRpZiAoIGpRdWVyeS5pc0Z1bmN0aW9uKCBvcHRpb25zICkgKSB7XG5cdFx0XHRvcHRpb25zID0gb3B0aW9ucy5jYWxsKCBlbGVtLCBpLCBjdXJPZmZzZXQgKTtcblx0XHR9XG5cblx0XHRpZiAoIG9wdGlvbnMudG9wICE9IG51bGwgKSB7XG5cdFx0XHRwcm9wcy50b3AgPSAoIG9wdGlvbnMudG9wIC0gY3VyT2Zmc2V0LnRvcCApICsgY3VyVG9wO1xuXHRcdH1cblx0XHRpZiAoIG9wdGlvbnMubGVmdCAhPSBudWxsICkge1xuXHRcdFx0cHJvcHMubGVmdCA9ICggb3B0aW9ucy5sZWZ0IC0gY3VyT2Zmc2V0LmxlZnQgKSArIGN1ckxlZnQ7XG5cdFx0fVxuXG5cdFx0aWYgKCBcInVzaW5nXCIgaW4gb3B0aW9ucyApIHtcblx0XHRcdG9wdGlvbnMudXNpbmcuY2FsbCggZWxlbSwgcHJvcHMgKTtcblxuXHRcdH0gZWxzZSB7XG5cdFx0XHRjdXJFbGVtLmNzcyggcHJvcHMgKTtcblx0XHR9XG5cdH1cbn07XG5cbmpRdWVyeS5mbi5leHRlbmQoe1xuXHRvZmZzZXQ6IGZ1bmN0aW9uKCBvcHRpb25zICkge1xuXHRcdGlmICggYXJndW1lbnRzLmxlbmd0aCApIHtcblx0XHRcdHJldHVybiBvcHRpb25zID09PSB1bmRlZmluZWQgP1xuXHRcdFx0XHR0aGlzIDpcblx0XHRcdFx0dGhpcy5lYWNoKGZ1bmN0aW9uKCBpICkge1xuXHRcdFx0XHRcdGpRdWVyeS5vZmZzZXQuc2V0T2Zmc2V0KCB0aGlzLCBvcHRpb25zLCBpICk7XG5cdFx0XHRcdH0pO1xuXHRcdH1cblxuXHRcdHZhciBkb2NFbGVtLCB3aW4sXG5cdFx0XHRlbGVtID0gdGhpc1sgMCBdLFxuXHRcdFx0Ym94ID0geyB0b3A6IDAsIGxlZnQ6IDAgfSxcblx0XHRcdGRvYyA9IGVsZW0gJiYgZWxlbS5vd25lckRvY3VtZW50O1xuXG5cdFx0aWYgKCAhZG9jICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdGRvY0VsZW0gPSBkb2MuZG9jdW1lbnRFbGVtZW50O1xuXG5cdFx0Ly8gTWFrZSBzdXJlIGl0J3Mgbm90IGEgZGlzY29ubmVjdGVkIERPTSBub2RlXG5cdFx0aWYgKCAhalF1ZXJ5LmNvbnRhaW5zKCBkb2NFbGVtLCBlbGVtICkgKSB7XG5cdFx0XHRyZXR1cm4gYm94O1xuXHRcdH1cblxuXHRcdC8vIFN1cHBvcnQ6IEJsYWNrQmVycnkgNSwgaU9TIDMgKG9yaWdpbmFsIGlQaG9uZSlcblx0XHQvLyBJZiB3ZSBkb24ndCBoYXZlIGdCQ1IsIGp1c3QgdXNlIDAsMCByYXRoZXIgdGhhbiBlcnJvclxuXHRcdGlmICggdHlwZW9mIGVsZW0uZ2V0Qm91bmRpbmdDbGllbnRSZWN0ICE9PSBzdHJ1bmRlZmluZWQgKSB7XG5cdFx0XHRib3ggPSBlbGVtLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRcdH1cblx0XHR3aW4gPSBnZXRXaW5kb3coIGRvYyApO1xuXHRcdHJldHVybiB7XG5cdFx0XHR0b3A6IGJveC50b3AgKyB3aW4ucGFnZVlPZmZzZXQgLSBkb2NFbGVtLmNsaWVudFRvcCxcblx0XHRcdGxlZnQ6IGJveC5sZWZ0ICsgd2luLnBhZ2VYT2Zmc2V0IC0gZG9jRWxlbS5jbGllbnRMZWZ0XG5cdFx0fTtcblx0fSxcblxuXHRwb3NpdGlvbjogZnVuY3Rpb24oKSB7XG5cdFx0aWYgKCAhdGhpc1sgMCBdICkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdHZhciBvZmZzZXRQYXJlbnQsIG9mZnNldCxcblx0XHRcdGVsZW0gPSB0aGlzWyAwIF0sXG5cdFx0XHRwYXJlbnRPZmZzZXQgPSB7IHRvcDogMCwgbGVmdDogMCB9O1xuXG5cdFx0Ly8gRml4ZWQgZWxlbWVudHMgYXJlIG9mZnNldCBmcm9tIHdpbmRvdyAocGFyZW50T2Zmc2V0ID0ge3RvcDowLCBsZWZ0OiAwfSwgYmVjYXVzZSBpdCBpcyBpdHMgb25seSBvZmZzZXQgcGFyZW50XG5cdFx0aWYgKCBqUXVlcnkuY3NzKCBlbGVtLCBcInBvc2l0aW9uXCIgKSA9PT0gXCJmaXhlZFwiICkge1xuXHRcdFx0Ly8gQXNzdW1lIGdldEJvdW5kaW5nQ2xpZW50UmVjdCBpcyB0aGVyZSB3aGVuIGNvbXB1dGVkIHBvc2l0aW9uIGlzIGZpeGVkXG5cdFx0XHRvZmZzZXQgPSBlbGVtLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIEdldCAqcmVhbCogb2Zmc2V0UGFyZW50XG5cdFx0XHRvZmZzZXRQYXJlbnQgPSB0aGlzLm9mZnNldFBhcmVudCgpO1xuXG5cdFx0XHQvLyBHZXQgY29ycmVjdCBvZmZzZXRzXG5cdFx0XHRvZmZzZXQgPSB0aGlzLm9mZnNldCgpO1xuXHRcdFx0aWYgKCAhalF1ZXJ5Lm5vZGVOYW1lKCBvZmZzZXRQYXJlbnRbIDAgXSwgXCJodG1sXCIgKSApIHtcblx0XHRcdFx0cGFyZW50T2Zmc2V0ID0gb2Zmc2V0UGFyZW50Lm9mZnNldCgpO1xuXHRcdFx0fVxuXG5cdFx0XHQvLyBBZGQgb2Zmc2V0UGFyZW50IGJvcmRlcnNcblx0XHRcdHBhcmVudE9mZnNldC50b3AgKz0galF1ZXJ5LmNzcyggb2Zmc2V0UGFyZW50WyAwIF0sIFwiYm9yZGVyVG9wV2lkdGhcIiwgdHJ1ZSApO1xuXHRcdFx0cGFyZW50T2Zmc2V0LmxlZnQgKz0galF1ZXJ5LmNzcyggb2Zmc2V0UGFyZW50WyAwIF0sIFwiYm9yZGVyTGVmdFdpZHRoXCIsIHRydWUgKTtcblx0XHR9XG5cblx0XHQvLyBTdWJ0cmFjdCBwYXJlbnQgb2Zmc2V0cyBhbmQgZWxlbWVudCBtYXJnaW5zXG5cdFx0cmV0dXJuIHtcblx0XHRcdHRvcDogb2Zmc2V0LnRvcCAtIHBhcmVudE9mZnNldC50b3AgLSBqUXVlcnkuY3NzKCBlbGVtLCBcIm1hcmdpblRvcFwiLCB0cnVlICksXG5cdFx0XHRsZWZ0OiBvZmZzZXQubGVmdCAtIHBhcmVudE9mZnNldC5sZWZ0IC0galF1ZXJ5LmNzcyggZWxlbSwgXCJtYXJnaW5MZWZ0XCIsIHRydWUgKVxuXHRcdH07XG5cdH0sXG5cblx0b2Zmc2V0UGFyZW50OiBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4gdGhpcy5tYXAoZnVuY3Rpb24oKSB7XG5cdFx0XHR2YXIgb2Zmc2V0UGFyZW50ID0gdGhpcy5vZmZzZXRQYXJlbnQgfHwgZG9jRWxlbTtcblxuXHRcdFx0d2hpbGUgKCBvZmZzZXRQYXJlbnQgJiYgKCAhalF1ZXJ5Lm5vZGVOYW1lKCBvZmZzZXRQYXJlbnQsIFwiaHRtbFwiICkgJiYgalF1ZXJ5LmNzcyggb2Zmc2V0UGFyZW50LCBcInBvc2l0aW9uXCIgKSA9PT0gXCJzdGF0aWNcIiApICkge1xuXHRcdFx0XHRvZmZzZXRQYXJlbnQgPSBvZmZzZXRQYXJlbnQub2Zmc2V0UGFyZW50O1xuXHRcdFx0fVxuXG5cdFx0XHRyZXR1cm4gb2Zmc2V0UGFyZW50IHx8IGRvY0VsZW07XG5cdFx0fSk7XG5cdH1cbn0pO1xuXG4vLyBDcmVhdGUgc2Nyb2xsTGVmdCBhbmQgc2Nyb2xsVG9wIG1ldGhvZHNcbmpRdWVyeS5lYWNoKCB7IHNjcm9sbExlZnQ6IFwicGFnZVhPZmZzZXRcIiwgc2Nyb2xsVG9wOiBcInBhZ2VZT2Zmc2V0XCIgfSwgZnVuY3Rpb24oIG1ldGhvZCwgcHJvcCApIHtcblx0dmFyIHRvcCA9IFwicGFnZVlPZmZzZXRcIiA9PT0gcHJvcDtcblxuXHRqUXVlcnkuZm5bIG1ldGhvZCBdID0gZnVuY3Rpb24oIHZhbCApIHtcblx0XHRyZXR1cm4gYWNjZXNzKCB0aGlzLCBmdW5jdGlvbiggZWxlbSwgbWV0aG9kLCB2YWwgKSB7XG5cdFx0XHR2YXIgd2luID0gZ2V0V2luZG93KCBlbGVtICk7XG5cblx0XHRcdGlmICggdmFsID09PSB1bmRlZmluZWQgKSB7XG5cdFx0XHRcdHJldHVybiB3aW4gPyB3aW5bIHByb3AgXSA6IGVsZW1bIG1ldGhvZCBdO1xuXHRcdFx0fVxuXG5cdFx0XHRpZiAoIHdpbiApIHtcblx0XHRcdFx0d2luLnNjcm9sbFRvKFxuXHRcdFx0XHRcdCF0b3AgPyB2YWwgOiB3aW5kb3cucGFnZVhPZmZzZXQsXG5cdFx0XHRcdFx0dG9wID8gdmFsIDogd2luZG93LnBhZ2VZT2Zmc2V0XG5cdFx0XHRcdCk7XG5cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGVsZW1bIG1ldGhvZCBdID0gdmFsO1xuXHRcdFx0fVxuXHRcdH0sIG1ldGhvZCwgdmFsLCBhcmd1bWVudHMubGVuZ3RoLCBudWxsICk7XG5cdH07XG59KTtcblxuLy8gU3VwcG9ydDogU2FmYXJpPDcrLCBDaHJvbWU8MzcrXG4vLyBBZGQgdGhlIHRvcC9sZWZ0IGNzc0hvb2tzIHVzaW5nIGpRdWVyeS5mbi5wb3NpdGlvblxuLy8gV2Via2l0IGJ1ZzogaHR0cHM6Ly9idWdzLndlYmtpdC5vcmcvc2hvd19idWcuY2dpP2lkPTI5MDg0XG4vLyBCbGluayBidWc6IGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0yMjkyODBcbi8vIGdldENvbXB1dGVkU3R5bGUgcmV0dXJucyBwZXJjZW50IHdoZW4gc3BlY2lmaWVkIGZvciB0b3AvbGVmdC9ib3R0b20vcmlnaHQ7XG4vLyByYXRoZXIgdGhhbiBtYWtlIHRoZSBjc3MgbW9kdWxlIGRlcGVuZCBvbiB0aGUgb2Zmc2V0IG1vZHVsZSwganVzdCBjaGVjayBmb3IgaXQgaGVyZVxualF1ZXJ5LmVhY2goIFsgXCJ0b3BcIiwgXCJsZWZ0XCIgXSwgZnVuY3Rpb24oIGksIHByb3AgKSB7XG5cdGpRdWVyeS5jc3NIb29rc1sgcHJvcCBdID0gYWRkR2V0SG9va0lmKCBzdXBwb3J0LnBpeGVsUG9zaXRpb24sXG5cdFx0ZnVuY3Rpb24oIGVsZW0sIGNvbXB1dGVkICkge1xuXHRcdFx0aWYgKCBjb21wdXRlZCApIHtcblx0XHRcdFx0Y29tcHV0ZWQgPSBjdXJDU1MoIGVsZW0sIHByb3AgKTtcblx0XHRcdFx0Ly8gSWYgY3VyQ1NTIHJldHVybnMgcGVyY2VudGFnZSwgZmFsbGJhY2sgdG8gb2Zmc2V0XG5cdFx0XHRcdHJldHVybiBybnVtbm9ucHgudGVzdCggY29tcHV0ZWQgKSA/XG5cdFx0XHRcdFx0alF1ZXJ5KCBlbGVtICkucG9zaXRpb24oKVsgcHJvcCBdICsgXCJweFwiIDpcblx0XHRcdFx0XHRjb21wdXRlZDtcblx0XHRcdH1cblx0XHR9XG5cdCk7XG59KTtcblxuXG4vLyBDcmVhdGUgaW5uZXJIZWlnaHQsIGlubmVyV2lkdGgsIGhlaWdodCwgd2lkdGgsIG91dGVySGVpZ2h0IGFuZCBvdXRlcldpZHRoIG1ldGhvZHNcbmpRdWVyeS5lYWNoKCB7IEhlaWdodDogXCJoZWlnaHRcIiwgV2lkdGg6IFwid2lkdGhcIiB9LCBmdW5jdGlvbiggbmFtZSwgdHlwZSApIHtcblx0alF1ZXJ5LmVhY2goIHsgcGFkZGluZzogXCJpbm5lclwiICsgbmFtZSwgY29udGVudDogdHlwZSwgXCJcIjogXCJvdXRlclwiICsgbmFtZSB9LCBmdW5jdGlvbiggZGVmYXVsdEV4dHJhLCBmdW5jTmFtZSApIHtcblx0XHQvLyBNYXJnaW4gaXMgb25seSBmb3Igb3V0ZXJIZWlnaHQsIG91dGVyV2lkdGhcblx0XHRqUXVlcnkuZm5bIGZ1bmNOYW1lIF0gPSBmdW5jdGlvbiggbWFyZ2luLCB2YWx1ZSApIHtcblx0XHRcdHZhciBjaGFpbmFibGUgPSBhcmd1bWVudHMubGVuZ3RoICYmICggZGVmYXVsdEV4dHJhIHx8IHR5cGVvZiBtYXJnaW4gIT09IFwiYm9vbGVhblwiICksXG5cdFx0XHRcdGV4dHJhID0gZGVmYXVsdEV4dHJhIHx8ICggbWFyZ2luID09PSB0cnVlIHx8IHZhbHVlID09PSB0cnVlID8gXCJtYXJnaW5cIiA6IFwiYm9yZGVyXCIgKTtcblxuXHRcdFx0cmV0dXJuIGFjY2VzcyggdGhpcywgZnVuY3Rpb24oIGVsZW0sIHR5cGUsIHZhbHVlICkge1xuXHRcdFx0XHR2YXIgZG9jO1xuXG5cdFx0XHRcdGlmICggalF1ZXJ5LmlzV2luZG93KCBlbGVtICkgKSB7XG5cdFx0XHRcdFx0Ly8gQXMgb2YgNS84LzIwMTIgdGhpcyB3aWxsIHlpZWxkIGluY29ycmVjdCByZXN1bHRzIGZvciBNb2JpbGUgU2FmYXJpLCBidXQgdGhlcmVcblx0XHRcdFx0XHQvLyBpc24ndCBhIHdob2xlIGxvdCB3ZSBjYW4gZG8uIFNlZSBwdWxsIHJlcXVlc3QgYXQgdGhpcyBVUkwgZm9yIGRpc2N1c3Npb246XG5cdFx0XHRcdFx0Ly8gaHR0cHM6Ly9naXRodWIuY29tL2pxdWVyeS9qcXVlcnkvcHVsbC83NjRcblx0XHRcdFx0XHRyZXR1cm4gZWxlbS5kb2N1bWVudC5kb2N1bWVudEVsZW1lbnRbIFwiY2xpZW50XCIgKyBuYW1lIF07XG5cdFx0XHRcdH1cblxuXHRcdFx0XHQvLyBHZXQgZG9jdW1lbnQgd2lkdGggb3IgaGVpZ2h0XG5cdFx0XHRcdGlmICggZWxlbS5ub2RlVHlwZSA9PT0gOSApIHtcblx0XHRcdFx0XHRkb2MgPSBlbGVtLmRvY3VtZW50RWxlbWVudDtcblxuXHRcdFx0XHRcdC8vIEVpdGhlciBzY3JvbGxbV2lkdGgvSGVpZ2h0XSBvciBvZmZzZXRbV2lkdGgvSGVpZ2h0XSBvciBjbGllbnRbV2lkdGgvSGVpZ2h0XSxcblx0XHRcdFx0XHQvLyB3aGljaGV2ZXIgaXMgZ3JlYXRlc3Rcblx0XHRcdFx0XHRyZXR1cm4gTWF0aC5tYXgoXG5cdFx0XHRcdFx0XHRlbGVtLmJvZHlbIFwic2Nyb2xsXCIgKyBuYW1lIF0sIGRvY1sgXCJzY3JvbGxcIiArIG5hbWUgXSxcblx0XHRcdFx0XHRcdGVsZW0uYm9keVsgXCJvZmZzZXRcIiArIG5hbWUgXSwgZG9jWyBcIm9mZnNldFwiICsgbmFtZSBdLFxuXHRcdFx0XHRcdFx0ZG9jWyBcImNsaWVudFwiICsgbmFtZSBdXG5cdFx0XHRcdFx0KTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHJldHVybiB2YWx1ZSA9PT0gdW5kZWZpbmVkID9cblx0XHRcdFx0XHQvLyBHZXQgd2lkdGggb3IgaGVpZ2h0IG9uIHRoZSBlbGVtZW50LCByZXF1ZXN0aW5nIGJ1dCBub3QgZm9yY2luZyBwYXJzZUZsb2F0XG5cdFx0XHRcdFx0alF1ZXJ5LmNzcyggZWxlbSwgdHlwZSwgZXh0cmEgKSA6XG5cblx0XHRcdFx0XHQvLyBTZXQgd2lkdGggb3IgaGVpZ2h0IG9uIHRoZSBlbGVtZW50XG5cdFx0XHRcdFx0alF1ZXJ5LnN0eWxlKCBlbGVtLCB0eXBlLCB2YWx1ZSwgZXh0cmEgKTtcblx0XHRcdH0sIHR5cGUsIGNoYWluYWJsZSA/IG1hcmdpbiA6IHVuZGVmaW5lZCwgY2hhaW5hYmxlLCBudWxsICk7XG5cdFx0fTtcblx0fSk7XG59KTtcblxuXG4vLyBUaGUgbnVtYmVyIG9mIGVsZW1lbnRzIGNvbnRhaW5lZCBpbiB0aGUgbWF0Y2hlZCBlbGVtZW50IHNldFxualF1ZXJ5LmZuLnNpemUgPSBmdW5jdGlvbigpIHtcblx0cmV0dXJuIHRoaXMubGVuZ3RoO1xufTtcblxualF1ZXJ5LmZuLmFuZFNlbGYgPSBqUXVlcnkuZm4uYWRkQmFjaztcblxuXG5cblxuLy8gUmVnaXN0ZXIgYXMgYSBuYW1lZCBBTUQgbW9kdWxlLCBzaW5jZSBqUXVlcnkgY2FuIGJlIGNvbmNhdGVuYXRlZCB3aXRoIG90aGVyXG4vLyBmaWxlcyB0aGF0IG1heSB1c2UgZGVmaW5lLCBidXQgbm90IHZpYSBhIHByb3BlciBjb25jYXRlbmF0aW9uIHNjcmlwdCB0aGF0XG4vLyB1bmRlcnN0YW5kcyBhbm9ueW1vdXMgQU1EIG1vZHVsZXMuIEEgbmFtZWQgQU1EIGlzIHNhZmVzdCBhbmQgbW9zdCByb2J1c3Rcbi8vIHdheSB0byByZWdpc3Rlci4gTG93ZXJjYXNlIGpxdWVyeSBpcyB1c2VkIGJlY2F1c2UgQU1EIG1vZHVsZSBuYW1lcyBhcmVcbi8vIGRlcml2ZWQgZnJvbSBmaWxlIG5hbWVzLCBhbmQgalF1ZXJ5IGlzIG5vcm1hbGx5IGRlbGl2ZXJlZCBpbiBhIGxvd2VyY2FzZVxuLy8gZmlsZSBuYW1lLiBEbyB0aGlzIGFmdGVyIGNyZWF0aW5nIHRoZSBnbG9iYWwgc28gdGhhdCBpZiBhbiBBTUQgbW9kdWxlIHdhbnRzXG4vLyB0byBjYWxsIG5vQ29uZmxpY3QgdG8gaGlkZSB0aGlzIHZlcnNpb24gb2YgalF1ZXJ5LCBpdCB3aWxsIHdvcmsuXG5cbi8vIE5vdGUgdGhhdCBmb3IgbWF4aW11bSBwb3J0YWJpbGl0eSwgbGlicmFyaWVzIHRoYXQgYXJlIG5vdCBqUXVlcnkgc2hvdWxkXG4vLyBkZWNsYXJlIHRoZW1zZWx2ZXMgYXMgYW5vbnltb3VzIG1vZHVsZXMsIGFuZCBhdm9pZCBzZXR0aW5nIGEgZ2xvYmFsIGlmIGFuXG4vLyBBTUQgbG9hZGVyIGlzIHByZXNlbnQuIGpRdWVyeSBpcyBhIHNwZWNpYWwgY2FzZS4gRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZVxuLy8gaHR0cHM6Ly9naXRodWIuY29tL2pyYnVya2UvcmVxdWlyZWpzL3dpa2kvVXBkYXRpbmctZXhpc3RpbmctbGlicmFyaWVzI3dpa2ktYW5vblxuXG5pZiAoIHR5cGVvZiBkZWZpbmUgPT09IFwiZnVuY3Rpb25cIiAmJiBkZWZpbmUuYW1kICkge1xuXHRkZWZpbmUoIFwianF1ZXJ5XCIsIFtdLCBmdW5jdGlvbigpIHtcblx0XHRyZXR1cm4galF1ZXJ5O1xuXHR9KTtcbn1cblxuXG5cblxudmFyXG5cdC8vIE1hcCBvdmVyIGpRdWVyeSBpbiBjYXNlIG9mIG92ZXJ3cml0ZVxuXHRfalF1ZXJ5ID0gd2luZG93LmpRdWVyeSxcblxuXHQvLyBNYXAgb3ZlciB0aGUgJCBpbiBjYXNlIG9mIG92ZXJ3cml0ZVxuXHRfJCA9IHdpbmRvdy4kO1xuXG5qUXVlcnkubm9Db25mbGljdCA9IGZ1bmN0aW9uKCBkZWVwICkge1xuXHRpZiAoIHdpbmRvdy4kID09PSBqUXVlcnkgKSB7XG5cdFx0d2luZG93LiQgPSBfJDtcblx0fVxuXG5cdGlmICggZGVlcCAmJiB3aW5kb3cualF1ZXJ5ID09PSBqUXVlcnkgKSB7XG5cdFx0d2luZG93LmpRdWVyeSA9IF9qUXVlcnk7XG5cdH1cblxuXHRyZXR1cm4galF1ZXJ5O1xufTtcblxuLy8gRXhwb3NlIGpRdWVyeSBhbmQgJCBpZGVudGlmaWVycywgZXZlbiBpbiBBTURcbi8vICgjNzEwMiNjb21tZW50OjEwLCBodHRwczovL2dpdGh1Yi5jb20vanF1ZXJ5L2pxdWVyeS9wdWxsLzU1Nylcbi8vIGFuZCBDb21tb25KUyBmb3IgYnJvd3NlciBlbXVsYXRvcnMgKCMxMzU2NilcbmlmICggdHlwZW9mIG5vR2xvYmFsID09PSBzdHJ1bmRlZmluZWQgKSB7XG5cdHdpbmRvdy5qUXVlcnkgPSB3aW5kb3cuJCA9IGpRdWVyeTtcbn1cblxuXG5cblxucmV0dXJuIGpRdWVyeTtcblxufSkpO1xuIiwidmFyIGJhc2VJbmRleE9mID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvYmFzZUluZGV4T2YnKSxcbiAgICBiaW5hcnlJbmRleCA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2JpbmFyeUluZGV4Jyk7XG5cbi8qIE5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXg7XG5cbi8qKlxuICogR2V0cyB0aGUgaW5kZXggYXQgd2hpY2ggdGhlIGZpcnN0IG9jY3VycmVuY2Ugb2YgYHZhbHVlYCBpcyBmb3VuZCBpbiBgYXJyYXlgXG4gKiB1c2luZyBgU2FtZVZhbHVlWmVyb2AgZm9yIGVxdWFsaXR5IGNvbXBhcmlzb25zLiBJZiBgZnJvbUluZGV4YCBpcyBuZWdhdGl2ZSxcbiAqIGl0IGlzIHVzZWQgYXMgdGhlIG9mZnNldCBmcm9tIHRoZSBlbmQgb2YgYGFycmF5YC4gSWYgYGFycmF5YCBpcyBzb3J0ZWRcbiAqIHByb3ZpZGluZyBgdHJ1ZWAgZm9yIGBmcm9tSW5kZXhgIHBlcmZvcm1zIGEgZmFzdGVyIGJpbmFyeSBzZWFyY2guXG4gKlxuICogKipOb3RlOioqIFtgU2FtZVZhbHVlWmVyb2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1zYW1ldmFsdWV6ZXJvKVxuICogY29tcGFyaXNvbnMgYXJlIGxpa2Ugc3RyaWN0IGVxdWFsaXR5IGNvbXBhcmlzb25zLCBlLmcuIGA9PT1gLCBleGNlcHQgdGhhdFxuICogYE5hTmAgbWF0Y2hlcyBgTmFOYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IEFycmF5XG4gKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgYXJyYXkgdG8gc2VhcmNoLlxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gc2VhcmNoIGZvci5cbiAqIEBwYXJhbSB7Ym9vbGVhbnxudW1iZXJ9IFtmcm9tSW5kZXg9MF0gVGhlIGluZGV4IHRvIHNlYXJjaCBmcm9tIG9yIGB0cnVlYFxuICogIHRvIHBlcmZvcm0gYSBiaW5hcnkgc2VhcmNoIG9uIGEgc29ydGVkIGFycmF5LlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgaW5kZXggb2YgdGhlIG1hdGNoZWQgdmFsdWUsIGVsc2UgYC0xYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pbmRleE9mKFsxLCAyLCAxLCAyXSwgMik7XG4gKiAvLyA9PiAxXG4gKlxuICogLy8gdXNpbmcgYGZyb21JbmRleGBcbiAqIF8uaW5kZXhPZihbMSwgMiwgMSwgMl0sIDIsIDIpO1xuICogLy8gPT4gM1xuICpcbiAqIC8vIHBlcmZvcm1pbmcgYSBiaW5hcnkgc2VhcmNoXG4gKiBfLmluZGV4T2YoWzEsIDEsIDIsIDJdLCAyLCB0cnVlKTtcbiAqIC8vID0+IDJcbiAqL1xuZnVuY3Rpb24gaW5kZXhPZihhcnJheSwgdmFsdWUsIGZyb21JbmRleCkge1xuICB2YXIgbGVuZ3RoID0gYXJyYXkgPyBhcnJheS5sZW5ndGggOiAwO1xuICBpZiAoIWxlbmd0aCkge1xuICAgIHJldHVybiAtMTtcbiAgfVxuICBpZiAodHlwZW9mIGZyb21JbmRleCA9PSAnbnVtYmVyJykge1xuICAgIGZyb21JbmRleCA9IGZyb21JbmRleCA8IDAgPyBuYXRpdmVNYXgobGVuZ3RoICsgZnJvbUluZGV4LCAwKSA6IGZyb21JbmRleDtcbiAgfSBlbHNlIGlmIChmcm9tSW5kZXgpIHtcbiAgICB2YXIgaW5kZXggPSBiaW5hcnlJbmRleChhcnJheSwgdmFsdWUpLFxuICAgICAgICBvdGhlciA9IGFycmF5W2luZGV4XTtcblxuICAgIGlmICh2YWx1ZSA9PT0gdmFsdWUgPyAodmFsdWUgPT09IG90aGVyKSA6IChvdGhlciAhPT0gb3RoZXIpKSB7XG4gICAgICByZXR1cm4gaW5kZXg7XG4gICAgfVxuICAgIHJldHVybiAtMTtcbiAgfVxuICByZXR1cm4gYmFzZUluZGV4T2YoYXJyYXksIHZhbHVlLCBmcm9tSW5kZXggfHwgMCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaW5kZXhPZjtcbiIsInZhciBMYXp5V3JhcHBlciA9IHJlcXVpcmUoJy4uL2ludGVybmFsL0xhenlXcmFwcGVyJyksXG4gICAgTG9kYXNoV3JhcHBlciA9IHJlcXVpcmUoJy4uL2ludGVybmFsL0xvZGFzaFdyYXBwZXInKSxcbiAgICBiYXNlTG9kYXNoID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvYmFzZUxvZGFzaCcpLFxuICAgIGlzQXJyYXkgPSByZXF1aXJlKCcuLi9sYW5nL2lzQXJyYXknKSxcbiAgICBpc09iamVjdExpa2UgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9pc09iamVjdExpa2UnKSxcbiAgICB3cmFwcGVyQ2xvbmUgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC93cmFwcGVyQ2xvbmUnKTtcblxuLyoqIFVzZWQgZm9yIG5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKiBVc2VkIHRvIGNoZWNrIG9iamVjdHMgZm9yIG93biBwcm9wZXJ0aWVzLiAqL1xudmFyIGhhc093blByb3BlcnR5ID0gb2JqZWN0UHJvdG8uaGFzT3duUHJvcGVydHk7XG5cbi8qKlxuICogQ3JlYXRlcyBhIGBsb2Rhc2hgIG9iamVjdCB3aGljaCB3cmFwcyBgdmFsdWVgIHRvIGVuYWJsZSBpbXBsaWNpdCBjaGFpbmluZy5cbiAqIE1ldGhvZHMgdGhhdCBvcGVyYXRlIG9uIGFuZCByZXR1cm4gYXJyYXlzLCBjb2xsZWN0aW9ucywgYW5kIGZ1bmN0aW9ucyBjYW5cbiAqIGJlIGNoYWluZWQgdG9nZXRoZXIuIE1ldGhvZHMgdGhhdCByZXR1cm4gYSBib29sZWFuIG9yIHNpbmdsZSB2YWx1ZSB3aWxsXG4gKiBhdXRvbWF0aWNhbGx5IGVuZCB0aGUgY2hhaW4gcmV0dXJuaW5nIHRoZSB1bndyYXBwZWQgdmFsdWUuIEV4cGxpY2l0IGNoYWluaW5nXG4gKiBtYXkgYmUgZW5hYmxlZCB1c2luZyBgXy5jaGFpbmAuIFRoZSBleGVjdXRpb24gb2YgY2hhaW5lZCBtZXRob2RzIGlzIGxhenksXG4gKiB0aGF0IGlzLCBleGVjdXRpb24gaXMgZGVmZXJyZWQgdW50aWwgYF8jdmFsdWVgIGlzIGltcGxpY2l0bHkgb3IgZXhwbGljaXRseVxuICogY2FsbGVkLlxuICpcbiAqIExhenkgZXZhbHVhdGlvbiBhbGxvd3Mgc2V2ZXJhbCBtZXRob2RzIHRvIHN1cHBvcnQgc2hvcnRjdXQgZnVzaW9uLiBTaG9ydGN1dFxuICogZnVzaW9uIGlzIGFuIG9wdGltaXphdGlvbiB0aGF0IG1lcmdlcyBpdGVyYXRlZXMgdG8gYXZvaWQgY3JlYXRpbmcgaW50ZXJtZWRpYXRlXG4gKiBhcnJheXMgYW5kIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGl0ZXJhdGVlIGV4ZWN1dGlvbnMuXG4gKlxuICogQ2hhaW5pbmcgaXMgc3VwcG9ydGVkIGluIGN1c3RvbSBidWlsZHMgYXMgbG9uZyBhcyB0aGUgYF8jdmFsdWVgIG1ldGhvZCBpc1xuICogZGlyZWN0bHkgb3IgaW5kaXJlY3RseSBpbmNsdWRlZCBpbiB0aGUgYnVpbGQuXG4gKlxuICogSW4gYWRkaXRpb24gdG8gbG9kYXNoIG1ldGhvZHMsIHdyYXBwZXJzIGhhdmUgYEFycmF5YCBhbmQgYFN0cmluZ2AgbWV0aG9kcy5cbiAqXG4gKiBUaGUgd3JhcHBlciBgQXJyYXlgIG1ldGhvZHMgYXJlOlxuICogYGNvbmNhdGAsIGBqb2luYCwgYHBvcGAsIGBwdXNoYCwgYHJldmVyc2VgLCBgc2hpZnRgLCBgc2xpY2VgLCBgc29ydGAsXG4gKiBgc3BsaWNlYCwgYW5kIGB1bnNoaWZ0YFxuICpcbiAqIFRoZSB3cmFwcGVyIGBTdHJpbmdgIG1ldGhvZHMgYXJlOlxuICogYHJlcGxhY2VgIGFuZCBgc3BsaXRgXG4gKlxuICogVGhlIHdyYXBwZXIgbWV0aG9kcyB0aGF0IHN1cHBvcnQgc2hvcnRjdXQgZnVzaW9uIGFyZTpcbiAqIGBjb21wYWN0YCwgYGRyb3BgLCBgZHJvcFJpZ2h0YCwgYGRyb3BSaWdodFdoaWxlYCwgYGRyb3BXaGlsZWAsIGBmaWx0ZXJgLFxuICogYGZpcnN0YCwgYGluaXRpYWxgLCBgbGFzdGAsIGBtYXBgLCBgcGx1Y2tgLCBgcmVqZWN0YCwgYHJlc3RgLCBgcmV2ZXJzZWAsXG4gKiBgc2xpY2VgLCBgdGFrZWAsIGB0YWtlUmlnaHRgLCBgdGFrZVJpZ2h0V2hpbGVgLCBgdGFrZVdoaWxlYCwgYHRvQXJyYXlgLFxuICogYW5kIGB3aGVyZWBcbiAqXG4gKiBUaGUgY2hhaW5hYmxlIHdyYXBwZXIgbWV0aG9kcyBhcmU6XG4gKiBgYWZ0ZXJgLCBgYXJ5YCwgYGFzc2lnbmAsIGBhdGAsIGBiZWZvcmVgLCBgYmluZGAsIGBiaW5kQWxsYCwgYGJpbmRLZXlgLFxuICogYGNhbGxiYWNrYCwgYGNoYWluYCwgYGNodW5rYCwgYGNvbW1pdGAsIGBjb21wYWN0YCwgYGNvbmNhdGAsIGBjb25zdGFudGAsXG4gKiBgY291bnRCeWAsIGBjcmVhdGVgLCBgY3VycnlgLCBgZGVib3VuY2VgLCBgZGVmYXVsdHNgLCBgZGVmZXJgLCBgZGVsYXlgLFxuICogYGRpZmZlcmVuY2VgLCBgZHJvcGAsIGBkcm9wUmlnaHRgLCBgZHJvcFJpZ2h0V2hpbGVgLCBgZHJvcFdoaWxlYCwgYGZpbGxgLFxuICogYGZpbHRlcmAsIGBmbGF0dGVuYCwgYGZsYXR0ZW5EZWVwYCwgYGZsb3dgLCBgZmxvd1JpZ2h0YCwgYGZvckVhY2hgLFxuICogYGZvckVhY2hSaWdodGAsIGBmb3JJbmAsIGBmb3JJblJpZ2h0YCwgYGZvck93bmAsIGBmb3JPd25SaWdodGAsIGBmdW5jdGlvbnNgLFxuICogYGdyb3VwQnlgLCBgaW5kZXhCeWAsIGBpbml0aWFsYCwgYGludGVyc2VjdGlvbmAsIGBpbnZlcnRgLCBgaW52b2tlYCwgYGtleXNgLFxuICogYGtleXNJbmAsIGBtYXBgLCBgbWFwVmFsdWVzYCwgYG1hdGNoZXNgLCBgbWF0Y2hlc1Byb3BlcnR5YCwgYG1lbW9pemVgLCBgbWVyZ2VgLFxuICogYG1peGluYCwgYG5lZ2F0ZWAsIGBub29wYCwgYG9taXRgLCBgb25jZWAsIGBwYWlyc2AsIGBwYXJ0aWFsYCwgYHBhcnRpYWxSaWdodGAsXG4gKiBgcGFydGl0aW9uYCwgYHBpY2tgLCBgcGxhbnRgLCBgcGx1Y2tgLCBgcHJvcGVydHlgLCBgcHJvcGVydHlPZmAsIGBwdWxsYCxcbiAqIGBwdWxsQXRgLCBgcHVzaGAsIGByYW5nZWAsIGByZWFyZ2AsIGByZWplY3RgLCBgcmVtb3ZlYCwgYHJlc3RgLCBgcmV2ZXJzZWAsXG4gKiBgc2h1ZmZsZWAsIGBzbGljZWAsIGBzb3J0YCwgYHNvcnRCeWAsIGBzb3J0QnlBbGxgLCBgc29ydEJ5T3JkZXJgLCBgc3BsaWNlYCxcbiAqIGBzcHJlYWRgLCBgdGFrZWAsIGB0YWtlUmlnaHRgLCBgdGFrZVJpZ2h0V2hpbGVgLCBgdGFrZVdoaWxlYCwgYHRhcGAsXG4gKiBgdGhyb3R0bGVgLCBgdGhydWAsIGB0aW1lc2AsIGB0b0FycmF5YCwgYHRvUGxhaW5PYmplY3RgLCBgdHJhbnNmb3JtYCxcbiAqIGB1bmlvbmAsIGB1bmlxYCwgYHVuc2hpZnRgLCBgdW56aXBgLCBgdmFsdWVzYCwgYHZhbHVlc0luYCwgYHdoZXJlYCxcbiAqIGB3aXRob3V0YCwgYHdyYXBgLCBgeG9yYCwgYHppcGAsIGFuZCBgemlwT2JqZWN0YFxuICpcbiAqIFRoZSB3cmFwcGVyIG1ldGhvZHMgdGhhdCBhcmUgKipub3QqKiBjaGFpbmFibGUgYnkgZGVmYXVsdCBhcmU6XG4gKiBgYWRkYCwgYGF0dGVtcHRgLCBgY2FtZWxDYXNlYCwgYGNhcGl0YWxpemVgLCBgY2xvbmVgLCBgY2xvbmVEZWVwYCwgYGRlYnVycmAsXG4gKiBgZW5kc1dpdGhgLCBgZXNjYXBlYCwgYGVzY2FwZVJlZ0V4cGAsIGBldmVyeWAsIGBmaW5kYCwgYGZpbmRJbmRleGAsIGBmaW5kS2V5YCxcbiAqIGBmaW5kTGFzdGAsIGBmaW5kTGFzdEluZGV4YCwgYGZpbmRMYXN0S2V5YCwgYGZpbmRXaGVyZWAsIGBmaXJzdGAsIGBoYXNgLFxuICogYGlkZW50aXR5YCwgYGluY2x1ZGVzYCwgYGluZGV4T2ZgLCBgaW5SYW5nZWAsIGBpc0FyZ3VtZW50c2AsIGBpc0FycmF5YCxcbiAqIGBpc0Jvb2xlYW5gLCBgaXNEYXRlYCwgYGlzRWxlbWVudGAsIGBpc0VtcHR5YCwgYGlzRXF1YWxgLCBgaXNFcnJvcmAsXG4gKiBgaXNGaW5pdGVgLGBpc0Z1bmN0aW9uYCwgYGlzTWF0Y2hgLCBgaXNOYXRpdmVgLCBgaXNOYU5gLCBgaXNOdWxsYCwgYGlzTnVtYmVyYCxcbiAqIGBpc09iamVjdGAsIGBpc1BsYWluT2JqZWN0YCwgYGlzUmVnRXhwYCwgYGlzU3RyaW5nYCwgYGlzVW5kZWZpbmVkYCxcbiAqIGBpc1R5cGVkQXJyYXlgLCBgam9pbmAsIGBrZWJhYkNhc2VgLCBgbGFzdGAsIGBsYXN0SW5kZXhPZmAsIGBtYXhgLCBgbWluYCxcbiAqIGBub0NvbmZsaWN0YCwgYG5vd2AsIGBwYWRgLCBgcGFkTGVmdGAsIGBwYWRSaWdodGAsIGBwYXJzZUludGAsIGBwb3BgLFxuICogYHJhbmRvbWAsIGByZWR1Y2VgLCBgcmVkdWNlUmlnaHRgLCBgcmVwZWF0YCwgYHJlc3VsdGAsIGBydW5JbkNvbnRleHRgLFxuICogYHNoaWZ0YCwgYHNpemVgLCBgc25ha2VDYXNlYCwgYHNvbWVgLCBgc29ydGVkSW5kZXhgLCBgc29ydGVkTGFzdEluZGV4YCxcbiAqIGBzdGFydENhc2VgLCBgc3RhcnRzV2l0aGAsIGBzdW1gLCBgdGVtcGxhdGVgLCBgdHJpbWAsIGB0cmltTGVmdGAsXG4gKiBgdHJpbVJpZ2h0YCwgYHRydW5jYCwgYHVuZXNjYXBlYCwgYHVuaXF1ZUlkYCwgYHZhbHVlYCwgYW5kIGB3b3Jkc2BcbiAqXG4gKiBUaGUgd3JhcHBlciBtZXRob2QgYHNhbXBsZWAgd2lsbCByZXR1cm4gYSB3cmFwcGVkIHZhbHVlIHdoZW4gYG5gIGlzIHByb3ZpZGVkLFxuICogb3RoZXJ3aXNlIGFuIHVud3JhcHBlZCB2YWx1ZSBpcyByZXR1cm5lZC5cbiAqXG4gKiBAbmFtZSBfXG4gKiBAY29uc3RydWN0b3JcbiAqIEBjYXRlZ29yeSBDaGFpblxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gd3JhcCBpbiBhIGBsb2Rhc2hgIGluc3RhbmNlLlxuICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyB0aGUgbmV3IGBsb2Rhc2hgIHdyYXBwZXIgaW5zdGFuY2UuXG4gKiBAZXhhbXBsZVxuICpcbiAqIHZhciB3cmFwcGVkID0gXyhbMSwgMiwgM10pO1xuICpcbiAqIC8vIHJldHVybnMgYW4gdW53cmFwcGVkIHZhbHVlXG4gKiB3cmFwcGVkLnJlZHVjZShmdW5jdGlvbihzdW0sIG4pIHtcbiAqICAgcmV0dXJuIHN1bSArIG47XG4gKiB9KTtcbiAqIC8vID0+IDZcbiAqXG4gKiAvLyByZXR1cm5zIGEgd3JhcHBlZCB2YWx1ZVxuICogdmFyIHNxdWFyZXMgPSB3cmFwcGVkLm1hcChmdW5jdGlvbihuKSB7XG4gKiAgIHJldHVybiBuICogbjtcbiAqIH0pO1xuICpcbiAqIF8uaXNBcnJheShzcXVhcmVzKTtcbiAqIC8vID0+IGZhbHNlXG4gKlxuICogXy5pc0FycmF5KHNxdWFyZXMudmFsdWUoKSk7XG4gKiAvLyA9PiB0cnVlXG4gKi9cbmZ1bmN0aW9uIGxvZGFzaCh2YWx1ZSkge1xuICBpZiAoaXNPYmplY3RMaWtlKHZhbHVlKSAmJiAhaXNBcnJheSh2YWx1ZSkgJiYgISh2YWx1ZSBpbnN0YW5jZW9mIExhenlXcmFwcGVyKSkge1xuICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIExvZGFzaFdyYXBwZXIpIHtcbiAgICAgIHJldHVybiB2YWx1ZTtcbiAgICB9XG4gICAgaWYgKGhhc093blByb3BlcnR5LmNhbGwodmFsdWUsICdfX2NoYWluX18nKSAmJiBoYXNPd25Qcm9wZXJ0eS5jYWxsKHZhbHVlLCAnX193cmFwcGVkX18nKSkge1xuICAgICAgcmV0dXJuIHdyYXBwZXJDbG9uZSh2YWx1ZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBuZXcgTG9kYXNoV3JhcHBlcih2YWx1ZSk7XG59XG5cbi8vIEVuc3VyZSB3cmFwcGVycyBhcmUgaW5zdGFuY2VzIG9mIGBiYXNlTG9kYXNoYC5cbmxvZGFzaC5wcm90b3R5cGUgPSBiYXNlTG9kYXNoLnByb3RvdHlwZTtcblxubW9kdWxlLmV4cG9ydHMgPSBsb2Rhc2g7XG4iLCJ2YXIgYmFzZUVhY2ggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9iYXNlRWFjaCcpLFxuICAgIGNyZWF0ZUZpbmQgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9jcmVhdGVGaW5kJyk7XG5cbi8qKlxuICogSXRlcmF0ZXMgb3ZlciBlbGVtZW50cyBvZiBgY29sbGVjdGlvbmAsIHJldHVybmluZyB0aGUgZmlyc3QgZWxlbWVudFxuICogYHByZWRpY2F0ZWAgcmV0dXJucyB0cnV0aHkgZm9yLiBUaGUgcHJlZGljYXRlIGlzIGJvdW5kIHRvIGB0aGlzQXJnYCBhbmRcbiAqIGludm9rZWQgd2l0aCB0aHJlZSBhcmd1bWVudHM6ICh2YWx1ZSwgaW5kZXh8a2V5LCBjb2xsZWN0aW9uKS5cbiAqXG4gKiBJZiBhIHByb3BlcnR5IG5hbWUgaXMgcHJvdmlkZWQgZm9yIGBwcmVkaWNhdGVgIHRoZSBjcmVhdGVkIGBfLnByb3BlcnR5YFxuICogc3R5bGUgY2FsbGJhY2sgcmV0dXJucyB0aGUgcHJvcGVydHkgdmFsdWUgb2YgdGhlIGdpdmVuIGVsZW1lbnQuXG4gKlxuICogSWYgYSB2YWx1ZSBpcyBhbHNvIHByb3ZpZGVkIGZvciBgdGhpc0FyZ2AgdGhlIGNyZWF0ZWQgYF8ubWF0Y2hlc1Byb3BlcnR5YFxuICogc3R5bGUgY2FsbGJhY2sgcmV0dXJucyBgdHJ1ZWAgZm9yIGVsZW1lbnRzIHRoYXQgaGF2ZSBhIG1hdGNoaW5nIHByb3BlcnR5XG4gKiB2YWx1ZSwgZWxzZSBgZmFsc2VgLlxuICpcbiAqIElmIGFuIG9iamVjdCBpcyBwcm92aWRlZCBmb3IgYHByZWRpY2F0ZWAgdGhlIGNyZWF0ZWQgYF8ubWF0Y2hlc2Agc3R5bGVcbiAqIGNhbGxiYWNrIHJldHVybnMgYHRydWVgIGZvciBlbGVtZW50cyB0aGF0IGhhdmUgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGdpdmVuXG4gKiBvYmplY3QsIGVsc2UgYGZhbHNlYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGFsaWFzIGRldGVjdFxuICogQGNhdGVnb3J5IENvbGxlY3Rpb25cbiAqIEBwYXJhbSB7QXJyYXl8T2JqZWN0fHN0cmluZ30gY29sbGVjdGlvbiBUaGUgY29sbGVjdGlvbiB0byBzZWFyY2guXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufE9iamVjdHxzdHJpbmd9IFtwcmVkaWNhdGU9Xy5pZGVudGl0eV0gVGhlIGZ1bmN0aW9uIGludm9rZWRcbiAqICBwZXIgaXRlcmF0aW9uLlxuICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVGhlIGB0aGlzYCBiaW5kaW5nIG9mIGBwcmVkaWNhdGVgLlxuICogQHJldHVybnMgeyp9IFJldHVybnMgdGhlIG1hdGNoZWQgZWxlbWVudCwgZWxzZSBgdW5kZWZpbmVkYC5cbiAqIEBleGFtcGxlXG4gKlxuICogdmFyIHVzZXJzID0gW1xuICogICB7ICd1c2VyJzogJ2Jhcm5leScsICAnYWdlJzogMzYsICdhY3RpdmUnOiB0cnVlIH0sXG4gKiAgIHsgJ3VzZXInOiAnZnJlZCcsICAgICdhZ2UnOiA0MCwgJ2FjdGl2ZSc6IGZhbHNlIH0sXG4gKiAgIHsgJ3VzZXInOiAncGViYmxlcycsICdhZ2UnOiAxLCAgJ2FjdGl2ZSc6IHRydWUgfVxuICogXTtcbiAqXG4gKiBfLnJlc3VsdChfLmZpbmQodXNlcnMsIGZ1bmN0aW9uKGNocikge1xuICogICByZXR1cm4gY2hyLmFnZSA8IDQwO1xuICogfSksICd1c2VyJyk7XG4gKiAvLyA9PiAnYmFybmV5J1xuICpcbiAqIC8vIHVzaW5nIHRoZSBgXy5tYXRjaGVzYCBjYWxsYmFjayBzaG9ydGhhbmRcbiAqIF8ucmVzdWx0KF8uZmluZCh1c2VycywgeyAnYWdlJzogMSwgJ2FjdGl2ZSc6IHRydWUgfSksICd1c2VyJyk7XG4gKiAvLyA9PiAncGViYmxlcydcbiAqXG4gKiAvLyB1c2luZyB0aGUgYF8ubWF0Y2hlc1Byb3BlcnR5YCBjYWxsYmFjayBzaG9ydGhhbmRcbiAqIF8ucmVzdWx0KF8uZmluZCh1c2VycywgJ2FjdGl2ZScsIGZhbHNlKSwgJ3VzZXInKTtcbiAqIC8vID0+ICdmcmVkJ1xuICpcbiAqIC8vIHVzaW5nIHRoZSBgXy5wcm9wZXJ0eWAgY2FsbGJhY2sgc2hvcnRoYW5kXG4gKiBfLnJlc3VsdChfLmZpbmQodXNlcnMsICdhY3RpdmUnKSwgJ3VzZXInKTtcbiAqIC8vID0+ICdiYXJuZXknXG4gKi9cbnZhciBmaW5kID0gY3JlYXRlRmluZChiYXNlRWFjaCk7XG5cbm1vZHVsZS5leHBvcnRzID0gZmluZDtcbiIsInZhciBhcnJheUVhY2ggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9hcnJheUVhY2gnKSxcbiAgICBiYXNlRWFjaCA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2Jhc2VFYWNoJyksXG4gICAgY3JlYXRlRm9yRWFjaCA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2NyZWF0ZUZvckVhY2gnKTtcblxuLyoqXG4gKiBJdGVyYXRlcyBvdmVyIGVsZW1lbnRzIG9mIGBjb2xsZWN0aW9uYCBpbnZva2luZyBgaXRlcmF0ZWVgIGZvciBlYWNoIGVsZW1lbnQuXG4gKiBUaGUgYGl0ZXJhdGVlYCBpcyBib3VuZCB0byBgdGhpc0FyZ2AgYW5kIGludm9rZWQgd2l0aCB0aHJlZSBhcmd1bWVudHM6XG4gKiAodmFsdWUsIGluZGV4fGtleSwgY29sbGVjdGlvbikuIEl0ZXJhdG9yIGZ1bmN0aW9ucyBtYXkgZXhpdCBpdGVyYXRpb24gZWFybHlcbiAqIGJ5IGV4cGxpY2l0bHkgcmV0dXJuaW5nIGBmYWxzZWAuXG4gKlxuICogKipOb3RlOioqIEFzIHdpdGggb3RoZXIgXCJDb2xsZWN0aW9uc1wiIG1ldGhvZHMsIG9iamVjdHMgd2l0aCBhIGBsZW5ndGhgIHByb3BlcnR5XG4gKiBhcmUgaXRlcmF0ZWQgbGlrZSBhcnJheXMuIFRvIGF2b2lkIHRoaXMgYmVoYXZpb3IgYF8uZm9ySW5gIG9yIGBfLmZvck93bmBcbiAqIG1heSBiZSB1c2VkIGZvciBvYmplY3QgaXRlcmF0aW9uLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAYWxpYXMgZWFjaFxuICogQGNhdGVnb3J5IENvbGxlY3Rpb25cbiAqIEBwYXJhbSB7QXJyYXl8T2JqZWN0fHN0cmluZ30gY29sbGVjdGlvbiBUaGUgY29sbGVjdGlvbiB0byBpdGVyYXRlIG92ZXIuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBbaXRlcmF0ZWU9Xy5pZGVudGl0eV0gVGhlIGZ1bmN0aW9uIGludm9rZWQgcGVyIGl0ZXJhdGlvbi5cbiAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFRoZSBgdGhpc2AgYmluZGluZyBvZiBgaXRlcmF0ZWVgLlxuICogQHJldHVybnMge0FycmF5fE9iamVjdHxzdHJpbmd9IFJldHVybnMgYGNvbGxlY3Rpb25gLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfKFsxLCAyXSkuZm9yRWFjaChmdW5jdGlvbihuKSB7XG4gKiAgIGNvbnNvbGUubG9nKG4pO1xuICogfSkudmFsdWUoKTtcbiAqIC8vID0+IGxvZ3MgZWFjaCB2YWx1ZSBmcm9tIGxlZnQgdG8gcmlnaHQgYW5kIHJldHVybnMgdGhlIGFycmF5XG4gKlxuICogXy5mb3JFYWNoKHsgJ2EnOiAxLCAnYic6IDIgfSwgZnVuY3Rpb24obiwga2V5KSB7XG4gKiAgIGNvbnNvbGUubG9nKG4sIGtleSk7XG4gKiB9KTtcbiAqIC8vID0+IGxvZ3MgZWFjaCB2YWx1ZS1rZXkgcGFpciBhbmQgcmV0dXJucyB0aGUgb2JqZWN0IChpdGVyYXRpb24gb3JkZXIgaXMgbm90IGd1YXJhbnRlZWQpXG4gKi9cbnZhciBmb3JFYWNoID0gY3JlYXRlRm9yRWFjaChhcnJheUVhY2gsIGJhc2VFYWNoKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmb3JFYWNoO1xuIiwidmFyIGFycmF5TWFwID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvYXJyYXlNYXAnKSxcbiAgICBiYXNlQ2FsbGJhY2sgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9iYXNlQ2FsbGJhY2snKSxcbiAgICBiYXNlTWFwID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvYmFzZU1hcCcpLFxuICAgIGlzQXJyYXkgPSByZXF1aXJlKCcuLi9sYW5nL2lzQXJyYXknKTtcblxuLyoqXG4gKiBDcmVhdGVzIGFuIGFycmF5IG9mIHZhbHVlcyBieSBydW5uaW5nIGVhY2ggZWxlbWVudCBpbiBgY29sbGVjdGlvbmAgdGhyb3VnaFxuICogYGl0ZXJhdGVlYC4gVGhlIGBpdGVyYXRlZWAgaXMgYm91bmQgdG8gYHRoaXNBcmdgIGFuZCBpbnZva2VkIHdpdGggdGhyZWVcbiAqIGFyZ3VtZW50czogKHZhbHVlLCBpbmRleHxrZXksIGNvbGxlY3Rpb24pLlxuICpcbiAqIElmIGEgcHJvcGVydHkgbmFtZSBpcyBwcm92aWRlZCBmb3IgYGl0ZXJhdGVlYCB0aGUgY3JlYXRlZCBgXy5wcm9wZXJ0eWBcbiAqIHN0eWxlIGNhbGxiYWNrIHJldHVybnMgdGhlIHByb3BlcnR5IHZhbHVlIG9mIHRoZSBnaXZlbiBlbGVtZW50LlxuICpcbiAqIElmIGEgdmFsdWUgaXMgYWxzbyBwcm92aWRlZCBmb3IgYHRoaXNBcmdgIHRoZSBjcmVhdGVkIGBfLm1hdGNoZXNQcm9wZXJ0eWBcbiAqIHN0eWxlIGNhbGxiYWNrIHJldHVybnMgYHRydWVgIGZvciBlbGVtZW50cyB0aGF0IGhhdmUgYSBtYXRjaGluZyBwcm9wZXJ0eVxuICogdmFsdWUsIGVsc2UgYGZhbHNlYC5cbiAqXG4gKiBJZiBhbiBvYmplY3QgaXMgcHJvdmlkZWQgZm9yIGBpdGVyYXRlZWAgdGhlIGNyZWF0ZWQgYF8ubWF0Y2hlc2Agc3R5bGVcbiAqIGNhbGxiYWNrIHJldHVybnMgYHRydWVgIGZvciBlbGVtZW50cyB0aGF0IGhhdmUgdGhlIHByb3BlcnRpZXMgb2YgdGhlIGdpdmVuXG4gKiBvYmplY3QsIGVsc2UgYGZhbHNlYC5cbiAqXG4gKiBNYW55IGxvZGFzaCBtZXRob2RzIGFyZSBndWFyZGVkIHRvIHdvcmsgYXMgaW50ZXJhdGVlcyBmb3IgbWV0aG9kcyBsaWtlXG4gKiBgXy5ldmVyeWAsIGBfLmZpbHRlcmAsIGBfLm1hcGAsIGBfLm1hcFZhbHVlc2AsIGBfLnJlamVjdGAsIGFuZCBgXy5zb21lYC5cbiAqXG4gKiBUaGUgZ3VhcmRlZCBtZXRob2RzIGFyZTpcbiAqIGBhcnlgLCBgY2FsbGJhY2tgLCBgY2h1bmtgLCBgY2xvbmVgLCBgY3JlYXRlYCwgYGN1cnJ5YCwgYGN1cnJ5UmlnaHRgLCBgZHJvcGAsXG4gKiBgZHJvcFJpZ2h0YCwgYGV2ZXJ5YCwgYGZpbGxgLCBgZmxhdHRlbmAsIGBpbnZlcnRgLCBgbWF4YCwgYG1pbmAsIGBwYXJzZUludGAsXG4gKiBgc2xpY2VgLCBgc29ydEJ5YCwgYHRha2VgLCBgdGFrZVJpZ2h0YCwgYHRlbXBsYXRlYCwgYHRyaW1gLCBgdHJpbUxlZnRgLFxuICogYHRyaW1SaWdodGAsIGB0cnVuY2AsIGByYW5kb21gLCBgcmFuZ2VgLCBgc2FtcGxlYCwgYHNvbWVgLCBgdW5pcWAsIGFuZCBgd29yZHNgXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBhbGlhcyBjb2xsZWN0XG4gKiBAY2F0ZWdvcnkgQ29sbGVjdGlvblxuICogQHBhcmFtIHtBcnJheXxPYmplY3R8c3RyaW5nfSBjb2xsZWN0aW9uIFRoZSBjb2xsZWN0aW9uIHRvIGl0ZXJhdGUgb3Zlci5cbiAqIEBwYXJhbSB7RnVuY3Rpb258T2JqZWN0fHN0cmluZ30gW2l0ZXJhdGVlPV8uaWRlbnRpdHldIFRoZSBmdW5jdGlvbiBpbnZva2VkXG4gKiAgcGVyIGl0ZXJhdGlvbi5cbiAqICBjcmVhdGUgYSBgXy5wcm9wZXJ0eWAgb3IgYF8ubWF0Y2hlc2Agc3R5bGUgY2FsbGJhY2sgcmVzcGVjdGl2ZWx5LlxuICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVGhlIGB0aGlzYCBiaW5kaW5nIG9mIGBpdGVyYXRlZWAuXG4gKiBAcmV0dXJucyB7QXJyYXl9IFJldHVybnMgdGhlIG5ldyBtYXBwZWQgYXJyYXkuXG4gKiBAZXhhbXBsZVxuICpcbiAqIGZ1bmN0aW9uIHRpbWVzVGhyZWUobikge1xuICogICByZXR1cm4gbiAqIDM7XG4gKiB9XG4gKlxuICogXy5tYXAoWzEsIDJdLCB0aW1lc1RocmVlKTtcbiAqIC8vID0+IFszLCA2XVxuICpcbiAqIF8ubWFwKHsgJ2EnOiAxLCAnYic6IDIgfSwgdGltZXNUaHJlZSk7XG4gKiAvLyA9PiBbMywgNl0gKGl0ZXJhdGlvbiBvcmRlciBpcyBub3QgZ3VhcmFudGVlZClcbiAqXG4gKiB2YXIgdXNlcnMgPSBbXG4gKiAgIHsgJ3VzZXInOiAnYmFybmV5JyB9LFxuICogICB7ICd1c2VyJzogJ2ZyZWQnIH1cbiAqIF07XG4gKlxuICogLy8gdXNpbmcgdGhlIGBfLnByb3BlcnR5YCBjYWxsYmFjayBzaG9ydGhhbmRcbiAqIF8ubWFwKHVzZXJzLCAndXNlcicpO1xuICogLy8gPT4gWydiYXJuZXknLCAnZnJlZCddXG4gKi9cbmZ1bmN0aW9uIG1hcChjb2xsZWN0aW9uLCBpdGVyYXRlZSwgdGhpc0FyZykge1xuICB2YXIgZnVuYyA9IGlzQXJyYXkoY29sbGVjdGlvbikgPyBhcnJheU1hcCA6IGJhc2VNYXA7XG4gIGl0ZXJhdGVlID0gYmFzZUNhbGxiYWNrKGl0ZXJhdGVlLCB0aGlzQXJnLCAzKTtcbiAgcmV0dXJuIGZ1bmMoY29sbGVjdGlvbiwgaXRlcmF0ZWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IG1hcDtcbiIsInZhciBpc05hdGl2ZSA9IHJlcXVpcmUoJy4uL2xhbmcvaXNOYXRpdmUnKTtcblxuLyogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVOb3cgPSBpc05hdGl2ZShuYXRpdmVOb3cgPSBEYXRlLm5vdykgJiYgbmF0aXZlTm93O1xuXG4vKipcbiAqIEdldHMgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIFVuaXggZXBvY2hcbiAqICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAY2F0ZWdvcnkgRGF0ZVxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmRlZmVyKGZ1bmN0aW9uKHN0YW1wKSB7XG4gKiAgIGNvbnNvbGUubG9nKF8ubm93KCkgLSBzdGFtcCk7XG4gKiB9LCBfLm5vdygpKTtcbiAqIC8vID0+IGxvZ3MgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgaXQgdG9vayBmb3IgdGhlIGRlZmVycmVkIGZ1bmN0aW9uIHRvIGJlIGludm9rZWRcbiAqL1xudmFyIG5vdyA9IG5hdGl2ZU5vdyB8fCBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBub3c7XG4iLCJ2YXIgY3JlYXRlV3JhcHBlciA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2NyZWF0ZVdyYXBwZXInKSxcbiAgICByZXBsYWNlSG9sZGVycyA9IHJlcXVpcmUoJy4uL2ludGVybmFsL3JlcGxhY2VIb2xkZXJzJyksXG4gICAgcmVzdFBhcmFtID0gcmVxdWlyZSgnLi9yZXN0UGFyYW0nKTtcblxuLyoqIFVzZWQgdG8gY29tcG9zZSBiaXRtYXNrcyBmb3Igd3JhcHBlciBtZXRhZGF0YS4gKi9cbnZhciBCSU5EX0ZMQUcgPSAxLFxuICAgIFBBUlRJQUxfRkxBRyA9IDMyO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IGludm9rZXMgYGZ1bmNgIHdpdGggdGhlIGB0aGlzYCBiaW5kaW5nIG9mIGB0aGlzQXJnYFxuICogYW5kIHByZXBlbmRzIGFueSBhZGRpdGlvbmFsIGBfLmJpbmRgIGFyZ3VtZW50cyB0byB0aG9zZSBwcm92aWRlZCB0byB0aGVcbiAqIGJvdW5kIGZ1bmN0aW9uLlxuICpcbiAqIFRoZSBgXy5iaW5kLnBsYWNlaG9sZGVyYCB2YWx1ZSwgd2hpY2ggZGVmYXVsdHMgdG8gYF9gIGluIG1vbm9saXRoaWMgYnVpbGRzLFxuICogbWF5IGJlIHVzZWQgYXMgYSBwbGFjZWhvbGRlciBmb3IgcGFydGlhbGx5IGFwcGxpZWQgYXJndW1lbnRzLlxuICpcbiAqICoqTm90ZToqKiBVbmxpa2UgbmF0aXZlIGBGdW5jdGlvbiNiaW5kYCB0aGlzIG1ldGhvZCBkb2VzIG5vdCBzZXQgdGhlIGBsZW5ndGhgXG4gKiBwcm9wZXJ0eSBvZiBib3VuZCBmdW5jdGlvbnMuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gYmluZC5cbiAqIEBwYXJhbSB7Kn0gdGhpc0FyZyBUaGUgYHRoaXNgIGJpbmRpbmcgb2YgYGZ1bmNgLlxuICogQHBhcmFtIHsuLi4qfSBbcGFydGlhbHNdIFRoZSBhcmd1bWVudHMgdG8gYmUgcGFydGlhbGx5IGFwcGxpZWQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBib3VuZCBmdW5jdGlvbi5cbiAqIEBleGFtcGxlXG4gKlxuICogdmFyIGdyZWV0ID0gZnVuY3Rpb24oZ3JlZXRpbmcsIHB1bmN0dWF0aW9uKSB7XG4gKiAgIHJldHVybiBncmVldGluZyArICcgJyArIHRoaXMudXNlciArIHB1bmN0dWF0aW9uO1xuICogfTtcbiAqXG4gKiB2YXIgb2JqZWN0ID0geyAndXNlcic6ICdmcmVkJyB9O1xuICpcbiAqIHZhciBib3VuZCA9IF8uYmluZChncmVldCwgb2JqZWN0LCAnaGknKTtcbiAqIGJvdW5kKCchJyk7XG4gKiAvLyA9PiAnaGkgZnJlZCEnXG4gKlxuICogLy8gdXNpbmcgcGxhY2Vob2xkZXJzXG4gKiB2YXIgYm91bmQgPSBfLmJpbmQoZ3JlZXQsIG9iamVjdCwgXywgJyEnKTtcbiAqIGJvdW5kKCdoaScpO1xuICogLy8gPT4gJ2hpIGZyZWQhJ1xuICovXG52YXIgYmluZCA9IHJlc3RQYXJhbShmdW5jdGlvbihmdW5jLCB0aGlzQXJnLCBwYXJ0aWFscykge1xuICB2YXIgYml0bWFzayA9IEJJTkRfRkxBRztcbiAgaWYgKHBhcnRpYWxzLmxlbmd0aCkge1xuICAgIHZhciBob2xkZXJzID0gcmVwbGFjZUhvbGRlcnMocGFydGlhbHMsIGJpbmQucGxhY2Vob2xkZXIpO1xuICAgIGJpdG1hc2sgfD0gUEFSVElBTF9GTEFHO1xuICB9XG4gIHJldHVybiBjcmVhdGVXcmFwcGVyKGZ1bmMsIGJpdG1hc2ssIHRoaXNBcmcsIHBhcnRpYWxzLCBob2xkZXJzKTtcbn0pO1xuXG4vLyBBc3NpZ24gZGVmYXVsdCBwbGFjZWhvbGRlcnMuXG5iaW5kLnBsYWNlaG9sZGVyID0ge307XG5cbm1vZHVsZS5leHBvcnRzID0gYmluZDtcbiIsIi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiBOYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZU1heCA9IE1hdGgubWF4O1xuXG4vKipcbiAqIENyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IGludm9rZXMgYGZ1bmNgIHdpdGggdGhlIGB0aGlzYCBiaW5kaW5nIG9mIHRoZVxuICogY3JlYXRlZCBmdW5jdGlvbiBhbmQgYXJndW1lbnRzIGZyb20gYHN0YXJ0YCBhbmQgYmV5b25kIHByb3ZpZGVkIGFzIGFuIGFycmF5LlxuICpcbiAqICoqTm90ZToqKiBUaGlzIG1ldGhvZCBpcyBiYXNlZCBvbiB0aGUgW3Jlc3QgcGFyYW1ldGVyXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9KYXZhU2NyaXB0L1JlZmVyZW5jZS9GdW5jdGlvbnMvcmVzdF9wYXJhbWV0ZXJzKS5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IEZ1bmN0aW9uXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBhcHBseSBhIHJlc3QgcGFyYW1ldGVyIHRvLlxuICogQHBhcmFtIHtudW1iZXJ9IFtzdGFydD1mdW5jLmxlbmd0aC0xXSBUaGUgc3RhcnQgcG9zaXRpb24gb2YgdGhlIHJlc3QgcGFyYW1ldGVyLlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZnVuY3Rpb24uXG4gKiBAZXhhbXBsZVxuICpcbiAqIHZhciBzYXkgPSBfLnJlc3RQYXJhbShmdW5jdGlvbih3aGF0LCBuYW1lcykge1xuICogICByZXR1cm4gd2hhdCArICcgJyArIF8uaW5pdGlhbChuYW1lcykuam9pbignLCAnKSArXG4gKiAgICAgKF8uc2l6ZShuYW1lcykgPiAxID8gJywgJiAnIDogJycpICsgXy5sYXN0KG5hbWVzKTtcbiAqIH0pO1xuICpcbiAqIHNheSgnaGVsbG8nLCAnZnJlZCcsICdiYXJuZXknLCAncGViYmxlcycpO1xuICogLy8gPT4gJ2hlbGxvIGZyZWQsIGJhcm5leSwgJiBwZWJibGVzJ1xuICovXG5mdW5jdGlvbiByZXN0UGFyYW0oZnVuYywgc3RhcnQpIHtcbiAgaWYgKHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgc3RhcnQgPSBuYXRpdmVNYXgodHlwZW9mIHN0YXJ0ID09ICd1bmRlZmluZWQnID8gKGZ1bmMubGVuZ3RoIC0gMSkgOiAoK3N0YXJ0IHx8IDApLCAwKTtcbiAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgIHZhciBhcmdzID0gYXJndW1lbnRzLFxuICAgICAgICBpbmRleCA9IC0xLFxuICAgICAgICBsZW5ndGggPSBuYXRpdmVNYXgoYXJncy5sZW5ndGggLSBzdGFydCwgMCksXG4gICAgICAgIHJlc3QgPSBBcnJheShsZW5ndGgpO1xuXG4gICAgd2hpbGUgKCsraW5kZXggPCBsZW5ndGgpIHtcbiAgICAgIHJlc3RbaW5kZXhdID0gYXJnc1tzdGFydCArIGluZGV4XTtcbiAgICB9XG4gICAgc3dpdGNoIChzdGFydCkge1xuICAgICAgY2FzZSAwOiByZXR1cm4gZnVuYy5jYWxsKHRoaXMsIHJlc3QpO1xuICAgICAgY2FzZSAxOiByZXR1cm4gZnVuYy5jYWxsKHRoaXMsIGFyZ3NbMF0sIHJlc3QpO1xuICAgICAgY2FzZSAyOiByZXR1cm4gZnVuYy5jYWxsKHRoaXMsIGFyZ3NbMF0sIGFyZ3NbMV0sIHJlc3QpO1xuICAgIH1cbiAgICB2YXIgb3RoZXJBcmdzID0gQXJyYXkoc3RhcnQgKyAxKTtcbiAgICBpbmRleCA9IC0xO1xuICAgIHdoaWxlICgrK2luZGV4IDwgc3RhcnQpIHtcbiAgICAgIG90aGVyQXJnc1tpbmRleF0gPSBhcmdzW2luZGV4XTtcbiAgICB9XG4gICAgb3RoZXJBcmdzW3N0YXJ0XSA9IHJlc3Q7XG4gICAgcmV0dXJuIGZ1bmMuYXBwbHkodGhpcywgb3RoZXJBcmdzKTtcbiAgfTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSByZXN0UGFyYW07XG4iLCJ2YXIgYmFzZUNyZWF0ZSA9IHJlcXVpcmUoJy4vYmFzZUNyZWF0ZScpLFxuICAgIGJhc2VMb2Rhc2ggPSByZXF1aXJlKCcuL2Jhc2VMb2Rhc2gnKTtcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgYC1JbmZpbml0eWAgYW5kIGBJbmZpbml0eWAuICovXG52YXIgUE9TSVRJVkVfSU5GSU5JVFkgPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG5cbi8qKlxuICogQ3JlYXRlcyBhIGxhenkgd3JhcHBlciBvYmplY3Qgd2hpY2ggd3JhcHMgYHZhbHVlYCB0byBlbmFibGUgbGF6eSBldmFsdWF0aW9uLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byB3cmFwLlxuICovXG5mdW5jdGlvbiBMYXp5V3JhcHBlcih2YWx1ZSkge1xuICB0aGlzLl9fd3JhcHBlZF9fID0gdmFsdWU7XG4gIHRoaXMuX19hY3Rpb25zX18gPSBudWxsO1xuICB0aGlzLl9fZGlyX18gPSAxO1xuICB0aGlzLl9fZHJvcENvdW50X18gPSAwO1xuICB0aGlzLl9fZmlsdGVyZWRfXyA9IGZhbHNlO1xuICB0aGlzLl9faXRlcmF0ZWVzX18gPSBudWxsO1xuICB0aGlzLl9fdGFrZUNvdW50X18gPSBQT1NJVElWRV9JTkZJTklUWTtcbiAgdGhpcy5fX3ZpZXdzX18gPSBudWxsO1xufVxuXG5MYXp5V3JhcHBlci5wcm90b3R5cGUgPSBiYXNlQ3JlYXRlKGJhc2VMb2Rhc2gucHJvdG90eXBlKTtcbkxhenlXcmFwcGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IExhenlXcmFwcGVyO1xuXG5tb2R1bGUuZXhwb3J0cyA9IExhenlXcmFwcGVyO1xuIiwidmFyIGJhc2VDcmVhdGUgPSByZXF1aXJlKCcuL2Jhc2VDcmVhdGUnKSxcbiAgICBiYXNlTG9kYXNoID0gcmVxdWlyZSgnLi9iYXNlTG9kYXNoJyk7XG5cbi8qKlxuICogVGhlIGJhc2UgY29uc3RydWN0b3IgZm9yIGNyZWF0aW5nIGBsb2Rhc2hgIHdyYXBwZXIgb2JqZWN0cy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gd3JhcC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2NoYWluQWxsXSBFbmFibGUgY2hhaW5pbmcgZm9yIGFsbCB3cmFwcGVyIG1ldGhvZHMuXG4gKiBAcGFyYW0ge0FycmF5fSBbYWN0aW9ucz1bXV0gQWN0aW9ucyB0byBwZWZvcm0gdG8gcmVzb2x2ZSB0aGUgdW53cmFwcGVkIHZhbHVlLlxuICovXG5mdW5jdGlvbiBMb2Rhc2hXcmFwcGVyKHZhbHVlLCBjaGFpbkFsbCwgYWN0aW9ucykge1xuICB0aGlzLl9fd3JhcHBlZF9fID0gdmFsdWU7XG4gIHRoaXMuX19hY3Rpb25zX18gPSBhY3Rpb25zIHx8IFtdO1xuICB0aGlzLl9fY2hhaW5fXyA9ICEhY2hhaW5BbGw7XG59XG5cbkxvZGFzaFdyYXBwZXIucHJvdG90eXBlID0gYmFzZUNyZWF0ZShiYXNlTG9kYXNoLnByb3RvdHlwZSk7XG5Mb2Rhc2hXcmFwcGVyLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IExvZGFzaFdyYXBwZXI7XG5cbm1vZHVsZS5leHBvcnRzID0gTG9kYXNoV3JhcHBlcjtcbiIsIi8qKlxuICogQ29waWVzIHRoZSB2YWx1ZXMgb2YgYHNvdXJjZWAgdG8gYGFycmF5YC5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheX0gc291cmNlIFRoZSBhcnJheSB0byBjb3B5IHZhbHVlcyBmcm9tLlxuICogQHBhcmFtIHtBcnJheX0gW2FycmF5PVtdXSBUaGUgYXJyYXkgdG8gY29weSB2YWx1ZXMgdG8uXG4gKiBAcmV0dXJucyB7QXJyYXl9IFJldHVybnMgYGFycmF5YC5cbiAqL1xuZnVuY3Rpb24gYXJyYXlDb3B5KHNvdXJjZSwgYXJyYXkpIHtcbiAgdmFyIGluZGV4ID0gLTEsXG4gICAgICBsZW5ndGggPSBzb3VyY2UubGVuZ3RoO1xuXG4gIGFycmF5IHx8IChhcnJheSA9IEFycmF5KGxlbmd0aCkpO1xuICB3aGlsZSAoKytpbmRleCA8IGxlbmd0aCkge1xuICAgIGFycmF5W2luZGV4XSA9IHNvdXJjZVtpbmRleF07XG4gIH1cbiAgcmV0dXJuIGFycmF5O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGFycmF5Q29weTtcbiIsIi8qKlxuICogQSBzcGVjaWFsaXplZCB2ZXJzaW9uIG9mIGBfLmZvckVhY2hgIGZvciBhcnJheXMgd2l0aG91dCBzdXBwb3J0IGZvciBjYWxsYmFja1xuICogc2hvcnRoYW5kcyBhbmQgYHRoaXNgIGJpbmRpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7QXJyYXl9IGFycmF5IFRoZSBhcnJheSB0byBpdGVyYXRlIG92ZXIuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBpdGVyYXRlZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIGBhcnJheWAuXG4gKi9cbmZ1bmN0aW9uIGFycmF5RWFjaChhcnJheSwgaXRlcmF0ZWUpIHtcbiAgdmFyIGluZGV4ID0gLTEsXG4gICAgICBsZW5ndGggPSBhcnJheS5sZW5ndGg7XG5cbiAgd2hpbGUgKCsraW5kZXggPCBsZW5ndGgpIHtcbiAgICBpZiAoaXRlcmF0ZWUoYXJyYXlbaW5kZXhdLCBpbmRleCwgYXJyYXkpID09PSBmYWxzZSkge1xuICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIHJldHVybiBhcnJheTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBhcnJheUVhY2g7XG4iLCIvKipcbiAqIEEgc3BlY2lhbGl6ZWQgdmVyc2lvbiBvZiBgXy5tYXBgIGZvciBhcnJheXMgd2l0aG91dCBzdXBwb3J0IGZvciBjYWxsYmFja1xuICogc2hvcnRoYW5kcyBhbmQgYHRoaXNgIGJpbmRpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7QXJyYXl9IGFycmF5IFRoZSBhcnJheSB0byBpdGVyYXRlIG92ZXIuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBpdGVyYXRlZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIHRoZSBuZXcgbWFwcGVkIGFycmF5LlxuICovXG5mdW5jdGlvbiBhcnJheU1hcChhcnJheSwgaXRlcmF0ZWUpIHtcbiAgdmFyIGluZGV4ID0gLTEsXG4gICAgICBsZW5ndGggPSBhcnJheS5sZW5ndGgsXG4gICAgICByZXN1bHQgPSBBcnJheShsZW5ndGgpO1xuXG4gIHdoaWxlICgrK2luZGV4IDwgbGVuZ3RoKSB7XG4gICAgcmVzdWx0W2luZGV4XSA9IGl0ZXJhdGVlKGFycmF5W2luZGV4XSwgaW5kZXgsIGFycmF5KTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGFycmF5TWFwO1xuIiwidmFyIGJhc2VNYXRjaGVzID0gcmVxdWlyZSgnLi9iYXNlTWF0Y2hlcycpLFxuICAgIGJhc2VNYXRjaGVzUHJvcGVydHkgPSByZXF1aXJlKCcuL2Jhc2VNYXRjaGVzUHJvcGVydHknKSxcbiAgICBiYXNlUHJvcGVydHkgPSByZXF1aXJlKCcuL2Jhc2VQcm9wZXJ0eScpLFxuICAgIGJpbmRDYWxsYmFjayA9IHJlcXVpcmUoJy4vYmluZENhbGxiYWNrJyksXG4gICAgaWRlbnRpdHkgPSByZXF1aXJlKCcuLi91dGlsaXR5L2lkZW50aXR5Jyk7XG5cbi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYF8uY2FsbGJhY2tgIHdoaWNoIHN1cHBvcnRzIHNwZWNpZnlpbmcgdGhlXG4gKiBudW1iZXIgb2YgYXJndW1lbnRzIHRvIHByb3ZpZGUgdG8gYGZ1bmNgLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0geyp9IFtmdW5jPV8uaWRlbnRpdHldIFRoZSB2YWx1ZSB0byBjb252ZXJ0IHRvIGEgY2FsbGJhY2suXG4gKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBUaGUgYHRoaXNgIGJpbmRpbmcgb2YgYGZ1bmNgLlxuICogQHBhcmFtIHtudW1iZXJ9IFthcmdDb3VudF0gVGhlIG51bWJlciBvZiBhcmd1bWVudHMgdG8gcHJvdmlkZSB0byBgZnVuY2AuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIGNhbGxiYWNrLlxuICovXG5mdW5jdGlvbiBiYXNlQ2FsbGJhY2soZnVuYywgdGhpc0FyZywgYXJnQ291bnQpIHtcbiAgdmFyIHR5cGUgPSB0eXBlb2YgZnVuYztcbiAgaWYgKHR5cGUgPT0gJ2Z1bmN0aW9uJykge1xuICAgIHJldHVybiB0eXBlb2YgdGhpc0FyZyA9PSAndW5kZWZpbmVkJ1xuICAgICAgPyBmdW5jXG4gICAgICA6IGJpbmRDYWxsYmFjayhmdW5jLCB0aGlzQXJnLCBhcmdDb3VudCk7XG4gIH1cbiAgaWYgKGZ1bmMgPT0gbnVsbCkge1xuICAgIHJldHVybiBpZGVudGl0eTtcbiAgfVxuICBpZiAodHlwZSA9PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiBiYXNlTWF0Y2hlcyhmdW5jKTtcbiAgfVxuICByZXR1cm4gdHlwZW9mIHRoaXNBcmcgPT0gJ3VuZGVmaW5lZCdcbiAgICA/IGJhc2VQcm9wZXJ0eShmdW5jICsgJycpXG4gICAgOiBiYXNlTWF0Y2hlc1Byb3BlcnR5KGZ1bmMgKyAnJywgdGhpc0FyZyk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUNhbGxiYWNrO1xuIiwidmFyIGFycmF5Q29weSA9IHJlcXVpcmUoJy4vYXJyYXlDb3B5JyksXG4gICAgYXJyYXlFYWNoID0gcmVxdWlyZSgnLi9hcnJheUVhY2gnKSxcbiAgICBiYXNlQ29weSA9IHJlcXVpcmUoJy4vYmFzZUNvcHknKSxcbiAgICBiYXNlRm9yT3duID0gcmVxdWlyZSgnLi9iYXNlRm9yT3duJyksXG4gICAgaW5pdENsb25lQXJyYXkgPSByZXF1aXJlKCcuL2luaXRDbG9uZUFycmF5JyksXG4gICAgaW5pdENsb25lQnlUYWcgPSByZXF1aXJlKCcuL2luaXRDbG9uZUJ5VGFnJyksXG4gICAgaW5pdENsb25lT2JqZWN0ID0gcmVxdWlyZSgnLi9pbml0Q2xvbmVPYmplY3QnKSxcbiAgICBpc0FycmF5ID0gcmVxdWlyZSgnLi4vbGFuZy9pc0FycmF5JyksXG4gICAgaXNIb3N0T2JqZWN0ID0gcmVxdWlyZSgnLi9pc0hvc3RPYmplY3QnKSxcbiAgICBpc09iamVjdCA9IHJlcXVpcmUoJy4uL2xhbmcvaXNPYmplY3QnKSxcbiAgICBrZXlzID0gcmVxdWlyZSgnLi4vb2JqZWN0L2tleXMnKTtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIGFyZ3NUYWcgPSAnW29iamVjdCBBcmd1bWVudHNdJyxcbiAgICBhcnJheVRhZyA9ICdbb2JqZWN0IEFycmF5XScsXG4gICAgYm9vbFRhZyA9ICdbb2JqZWN0IEJvb2xlYW5dJyxcbiAgICBkYXRlVGFnID0gJ1tvYmplY3QgRGF0ZV0nLFxuICAgIGVycm9yVGFnID0gJ1tvYmplY3QgRXJyb3JdJyxcbiAgICBmdW5jVGFnID0gJ1tvYmplY3QgRnVuY3Rpb25dJyxcbiAgICBtYXBUYWcgPSAnW29iamVjdCBNYXBdJyxcbiAgICBudW1iZXJUYWcgPSAnW29iamVjdCBOdW1iZXJdJyxcbiAgICBvYmplY3RUYWcgPSAnW29iamVjdCBPYmplY3RdJyxcbiAgICByZWdleHBUYWcgPSAnW29iamVjdCBSZWdFeHBdJyxcbiAgICBzZXRUYWcgPSAnW29iamVjdCBTZXRdJyxcbiAgICBzdHJpbmdUYWcgPSAnW29iamVjdCBTdHJpbmddJyxcbiAgICB3ZWFrTWFwVGFnID0gJ1tvYmplY3QgV2Vha01hcF0nO1xuXG52YXIgYXJyYXlCdWZmZXJUYWcgPSAnW29iamVjdCBBcnJheUJ1ZmZlcl0nLFxuICAgIGZsb2F0MzJUYWcgPSAnW29iamVjdCBGbG9hdDMyQXJyYXldJyxcbiAgICBmbG9hdDY0VGFnID0gJ1tvYmplY3QgRmxvYXQ2NEFycmF5XScsXG4gICAgaW50OFRhZyA9ICdbb2JqZWN0IEludDhBcnJheV0nLFxuICAgIGludDE2VGFnID0gJ1tvYmplY3QgSW50MTZBcnJheV0nLFxuICAgIGludDMyVGFnID0gJ1tvYmplY3QgSW50MzJBcnJheV0nLFxuICAgIHVpbnQ4VGFnID0gJ1tvYmplY3QgVWludDhBcnJheV0nLFxuICAgIHVpbnQ4Q2xhbXBlZFRhZyA9ICdbb2JqZWN0IFVpbnQ4Q2xhbXBlZEFycmF5XScsXG4gICAgdWludDE2VGFnID0gJ1tvYmplY3QgVWludDE2QXJyYXldJyxcbiAgICB1aW50MzJUYWcgPSAnW29iamVjdCBVaW50MzJBcnJheV0nO1xuXG4vKiogVXNlZCB0byBpZGVudGlmeSBgdG9TdHJpbmdUYWdgIHZhbHVlcyBzdXBwb3J0ZWQgYnkgYF8uY2xvbmVgLiAqL1xudmFyIGNsb25lYWJsZVRhZ3MgPSB7fTtcbmNsb25lYWJsZVRhZ3NbYXJnc1RhZ10gPSBjbG9uZWFibGVUYWdzW2FycmF5VGFnXSA9XG5jbG9uZWFibGVUYWdzW2FycmF5QnVmZmVyVGFnXSA9IGNsb25lYWJsZVRhZ3NbYm9vbFRhZ10gPVxuY2xvbmVhYmxlVGFnc1tkYXRlVGFnXSA9IGNsb25lYWJsZVRhZ3NbZmxvYXQzMlRhZ10gPVxuY2xvbmVhYmxlVGFnc1tmbG9hdDY0VGFnXSA9IGNsb25lYWJsZVRhZ3NbaW50OFRhZ10gPVxuY2xvbmVhYmxlVGFnc1tpbnQxNlRhZ10gPSBjbG9uZWFibGVUYWdzW2ludDMyVGFnXSA9XG5jbG9uZWFibGVUYWdzW251bWJlclRhZ10gPSBjbG9uZWFibGVUYWdzW29iamVjdFRhZ10gPVxuY2xvbmVhYmxlVGFnc1tyZWdleHBUYWddID0gY2xvbmVhYmxlVGFnc1tzdHJpbmdUYWddID1cbmNsb25lYWJsZVRhZ3NbdWludDhUYWddID0gY2xvbmVhYmxlVGFnc1t1aW50OENsYW1wZWRUYWddID1cbmNsb25lYWJsZVRhZ3NbdWludDE2VGFnXSA9IGNsb25lYWJsZVRhZ3NbdWludDMyVGFnXSA9IHRydWU7XG5jbG9uZWFibGVUYWdzW2Vycm9yVGFnXSA9IGNsb25lYWJsZVRhZ3NbZnVuY1RhZ10gPVxuY2xvbmVhYmxlVGFnc1ttYXBUYWddID0gY2xvbmVhYmxlVGFnc1tzZXRUYWddID1cbmNsb25lYWJsZVRhZ3Nbd2Vha01hcFRhZ10gPSBmYWxzZTtcblxuLyoqIFVzZWQgZm9yIG5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZSBbYHRvU3RyaW5nVGFnYF0oaHR0cHM6Ly9wZW9wbGUubW96aWxsYS5vcmcvfmpvcmVuZG9yZmYvZXM2LWRyYWZ0Lmh0bWwjc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmpUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKipcbiAqIFRoZSBiYXNlIGltcGxlbWVudGF0aW9uIG9mIGBfLmNsb25lYCB3aXRob3V0IHN1cHBvcnQgZm9yIGFyZ3VtZW50IGp1Z2dsaW5nXG4gKiBhbmQgYHRoaXNgIGJpbmRpbmcgYGN1c3RvbWl6ZXJgIGZ1bmN0aW9ucy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2xvbmUuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0RlZXBdIFNwZWNpZnkgYSBkZWVwIGNsb25lLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gW2N1c3RvbWl6ZXJdIFRoZSBmdW5jdGlvbiB0byBjdXN0b21pemUgY2xvbmluZyB2YWx1ZXMuXG4gKiBAcGFyYW0ge3N0cmluZ30gW2tleV0gVGhlIGtleSBvZiBgdmFsdWVgLlxuICogQHBhcmFtIHtPYmplY3R9IFtvYmplY3RdIFRoZSBvYmplY3QgYHZhbHVlYCBiZWxvbmdzIHRvLlxuICogQHBhcmFtIHtBcnJheX0gW3N0YWNrQT1bXV0gVHJhY2tzIHRyYXZlcnNlZCBzb3VyY2Ugb2JqZWN0cy5cbiAqIEBwYXJhbSB7QXJyYXl9IFtzdGFja0I9W11dIEFzc29jaWF0ZXMgY2xvbmVzIHdpdGggc291cmNlIGNvdW50ZXJwYXJ0cy5cbiAqIEByZXR1cm5zIHsqfSBSZXR1cm5zIHRoZSBjbG9uZWQgdmFsdWUuXG4gKi9cbmZ1bmN0aW9uIGJhc2VDbG9uZSh2YWx1ZSwgaXNEZWVwLCBjdXN0b21pemVyLCBrZXksIG9iamVjdCwgc3RhY2tBLCBzdGFja0IpIHtcbiAgdmFyIHJlc3VsdDtcbiAgaWYgKGN1c3RvbWl6ZXIpIHtcbiAgICByZXN1bHQgPSBvYmplY3QgPyBjdXN0b21pemVyKHZhbHVlLCBrZXksIG9iamVjdCkgOiBjdXN0b21pemVyKHZhbHVlKTtcbiAgfVxuICBpZiAodHlwZW9mIHJlc3VsdCAhPSAndW5kZWZpbmVkJykge1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgaWYgKCFpc09iamVjdCh2YWx1ZSkpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgdmFyIGlzQXJyID0gaXNBcnJheSh2YWx1ZSk7XG4gIGlmIChpc0Fycikge1xuICAgIHJlc3VsdCA9IGluaXRDbG9uZUFycmF5KHZhbHVlKTtcbiAgICBpZiAoIWlzRGVlcCkge1xuICAgICAgcmV0dXJuIGFycmF5Q29weSh2YWx1ZSwgcmVzdWx0KTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgdmFyIHRhZyA9IG9ialRvU3RyaW5nLmNhbGwodmFsdWUpLFxuICAgICAgICBpc0Z1bmMgPSB0YWcgPT0gZnVuY1RhZztcblxuICAgIGlmICh0YWcgPT0gb2JqZWN0VGFnIHx8IHRhZyA9PSBhcmdzVGFnIHx8IChpc0Z1bmMgJiYgIW9iamVjdCkpIHtcbiAgICAgIGlmIChpc0hvc3RPYmplY3QodmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBvYmplY3QgPyB2YWx1ZSA6IHt9O1xuICAgICAgfVxuICAgICAgcmVzdWx0ID0gaW5pdENsb25lT2JqZWN0KGlzRnVuYyA/IHt9IDogdmFsdWUpO1xuICAgICAgaWYgKCFpc0RlZXApIHtcbiAgICAgICAgcmV0dXJuIGJhc2VDb3B5KHZhbHVlLCByZXN1bHQsIGtleXModmFsdWUpKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIGNsb25lYWJsZVRhZ3NbdGFnXVxuICAgICAgICA/IGluaXRDbG9uZUJ5VGFnKHZhbHVlLCB0YWcsIGlzRGVlcClcbiAgICAgICAgOiAob2JqZWN0ID8gdmFsdWUgOiB7fSk7XG4gICAgfVxuICB9XG4gIC8vIENoZWNrIGZvciBjaXJjdWxhciByZWZlcmVuY2VzIGFuZCByZXR1cm4gY29ycmVzcG9uZGluZyBjbG9uZS5cbiAgc3RhY2tBIHx8IChzdGFja0EgPSBbXSk7XG4gIHN0YWNrQiB8fCAoc3RhY2tCID0gW10pO1xuXG4gIHZhciBsZW5ndGggPSBzdGFja0EubGVuZ3RoO1xuICB3aGlsZSAobGVuZ3RoLS0pIHtcbiAgICBpZiAoc3RhY2tBW2xlbmd0aF0gPT0gdmFsdWUpIHtcbiAgICAgIHJldHVybiBzdGFja0JbbGVuZ3RoXTtcbiAgICB9XG4gIH1cbiAgLy8gQWRkIHRoZSBzb3VyY2UgdmFsdWUgdG8gdGhlIHN0YWNrIG9mIHRyYXZlcnNlZCBvYmplY3RzIGFuZCBhc3NvY2lhdGUgaXQgd2l0aCBpdHMgY2xvbmUuXG4gIHN0YWNrQS5wdXNoKHZhbHVlKTtcbiAgc3RhY2tCLnB1c2gocmVzdWx0KTtcblxuICAvLyBSZWN1cnNpdmVseSBwb3B1bGF0ZSBjbG9uZSAoc3VzY2VwdGlibGUgdG8gY2FsbCBzdGFjayBsaW1pdHMpLlxuICAoaXNBcnIgPyBhcnJheUVhY2ggOiBiYXNlRm9yT3duKSh2YWx1ZSwgZnVuY3Rpb24oc3ViVmFsdWUsIGtleSkge1xuICAgIHJlc3VsdFtrZXldID0gYmFzZUNsb25lKHN1YlZhbHVlLCBpc0RlZXAsIGN1c3RvbWl6ZXIsIGtleSwgdmFsdWUsIHN0YWNrQSwgc3RhY2tCKTtcbiAgfSk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUNsb25lO1xuIiwiLyoqXG4gKiBDb3BpZXMgdGhlIHByb3BlcnRpZXMgb2YgYHNvdXJjZWAgdG8gYG9iamVjdGAuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBzb3VyY2UgVGhlIG9iamVjdCB0byBjb3B5IHByb3BlcnRpZXMgZnJvbS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBbb2JqZWN0PXt9XSBUaGUgb2JqZWN0IHRvIGNvcHkgcHJvcGVydGllcyB0by5cbiAqIEBwYXJhbSB7QXJyYXl9IHByb3BzIFRoZSBwcm9wZXJ0eSBuYW1lcyB0byBjb3B5LlxuICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBgb2JqZWN0YC5cbiAqL1xuZnVuY3Rpb24gYmFzZUNvcHkoc291cmNlLCBvYmplY3QsIHByb3BzKSB7XG4gIGlmICghcHJvcHMpIHtcbiAgICBwcm9wcyA9IG9iamVjdDtcbiAgICBvYmplY3QgPSB7fTtcbiAgfVxuICB2YXIgaW5kZXggPSAtMSxcbiAgICAgIGxlbmd0aCA9IHByb3BzLmxlbmd0aDtcblxuICB3aGlsZSAoKytpbmRleCA8IGxlbmd0aCkge1xuICAgIHZhciBrZXkgPSBwcm9wc1tpbmRleF07XG4gICAgb2JqZWN0W2tleV0gPSBzb3VyY2Vba2V5XTtcbiAgfVxuICByZXR1cm4gb2JqZWN0O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VDb3B5O1xuIiwidmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi4vbGFuZy9pc09iamVjdCcpO1xuXG4vKipcbiAqIFRoZSBiYXNlIGltcGxlbWVudGF0aW9uIG9mIGBfLmNyZWF0ZWAgd2l0aG91dCBzdXBwb3J0IGZvciBhc3NpZ25pbmdcbiAqIHByb3BlcnRpZXMgdG8gdGhlIGNyZWF0ZWQgb2JqZWN0LlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge09iamVjdH0gcHJvdG90eXBlIFRoZSBvYmplY3QgdG8gaW5oZXJpdCBmcm9tLlxuICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyB0aGUgbmV3IG9iamVjdC5cbiAqL1xudmFyIGJhc2VDcmVhdGUgPSAoZnVuY3Rpb24oKSB7XG4gIGZ1bmN0aW9uIE9iamVjdCgpIHt9XG4gIHJldHVybiBmdW5jdGlvbihwcm90b3R5cGUpIHtcbiAgICBpZiAoaXNPYmplY3QocHJvdG90eXBlKSkge1xuICAgICAgT2JqZWN0LnByb3RvdHlwZSA9IHByb3RvdHlwZTtcbiAgICAgIHZhciByZXN1bHQgPSBuZXcgT2JqZWN0O1xuICAgICAgT2JqZWN0LnByb3RvdHlwZSA9IG51bGw7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQgfHwgZ2xvYmFsLk9iamVjdCgpO1xuICB9O1xufSgpKTtcblxubW9kdWxlLmV4cG9ydHMgPSBiYXNlQ3JlYXRlO1xuIiwidmFyIGJhc2VGb3JPd24gPSByZXF1aXJlKCcuL2Jhc2VGb3JPd24nKSxcbiAgICBjcmVhdGVCYXNlRWFjaCA9IHJlcXVpcmUoJy4vY3JlYXRlQmFzZUVhY2gnKTtcblxuLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgXy5mb3JFYWNoYCB3aXRob3V0IHN1cHBvcnQgZm9yIGNhbGxiYWNrXG4gKiBzaG9ydGhhbmRzIGFuZCBgdGhpc2AgYmluZGluZy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheXxPYmplY3R8c3RyaW5nfSBjb2xsZWN0aW9uIFRoZSBjb2xsZWN0aW9uIHRvIGl0ZXJhdGUgb3Zlci5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGl0ZXJhdGVlIFRoZSBmdW5jdGlvbiBpbnZva2VkIHBlciBpdGVyYXRpb24uXG4gKiBAcmV0dXJucyB7QXJyYXl8T2JqZWN0fHN0cmluZ30gUmV0dXJucyBgY29sbGVjdGlvbmAuXG4gKi9cbnZhciBiYXNlRWFjaCA9IGNyZWF0ZUJhc2VFYWNoKGJhc2VGb3JPd24pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VFYWNoO1xuIiwiLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgXy5maW5kYCwgYF8uZmluZExhc3RgLCBgXy5maW5kS2V5YCwgYW5kIGBfLmZpbmRMYXN0S2V5YCxcbiAqIHdpdGhvdXQgc3VwcG9ydCBmb3IgY2FsbGJhY2sgc2hvcnRoYW5kcyBhbmQgYHRoaXNgIGJpbmRpbmcsIHdoaWNoIGl0ZXJhdGVzXG4gKiBvdmVyIGBjb2xsZWN0aW9uYCB1c2luZyB0aGUgcHJvdmlkZWQgYGVhY2hGdW5jYC5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheXxPYmplY3R8c3RyaW5nfSBjb2xsZWN0aW9uIFRoZSBjb2xsZWN0aW9uIHRvIHNlYXJjaC5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IHByZWRpY2F0ZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZWFjaEZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGl0ZXJhdGUgb3ZlciBgY29sbGVjdGlvbmAuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtyZXRLZXldIFNwZWNpZnkgcmV0dXJuaW5nIHRoZSBrZXkgb2YgdGhlIGZvdW5kIGVsZW1lbnRcbiAqICBpbnN0ZWFkIG9mIHRoZSBlbGVtZW50IGl0c2VsZi5cbiAqIEByZXR1cm5zIHsqfSBSZXR1cm5zIHRoZSBmb3VuZCBlbGVtZW50IG9yIGl0cyBrZXksIGVsc2UgYHVuZGVmaW5lZGAuXG4gKi9cbmZ1bmN0aW9uIGJhc2VGaW5kKGNvbGxlY3Rpb24sIHByZWRpY2F0ZSwgZWFjaEZ1bmMsIHJldEtleSkge1xuICB2YXIgcmVzdWx0O1xuICBlYWNoRnVuYyhjb2xsZWN0aW9uLCBmdW5jdGlvbih2YWx1ZSwga2V5LCBjb2xsZWN0aW9uKSB7XG4gICAgaWYgKHByZWRpY2F0ZSh2YWx1ZSwga2V5LCBjb2xsZWN0aW9uKSkge1xuICAgICAgcmVzdWx0ID0gcmV0S2V5ID8ga2V5IDogdmFsdWU7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9KTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBiYXNlRmluZDtcbiIsIi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYF8uZmluZEluZGV4YCBhbmQgYF8uZmluZExhc3RJbmRleGAgd2l0aG91dFxuICogc3VwcG9ydCBmb3IgY2FsbGJhY2sgc2hvcnRoYW5kcyBhbmQgYHRoaXNgIGJpbmRpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7QXJyYXl9IGFycmF5IFRoZSBhcnJheSB0byBzZWFyY2guXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBwcmVkaWNhdGUgVGhlIGZ1bmN0aW9uIGludm9rZWQgcGVyIGl0ZXJhdGlvbi5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2Zyb21SaWdodF0gU3BlY2lmeSBpdGVyYXRpbmcgZnJvbSByaWdodCB0byBsZWZ0LlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgaW5kZXggb2YgdGhlIG1hdGNoZWQgdmFsdWUsIGVsc2UgYC0xYC5cbiAqL1xuZnVuY3Rpb24gYmFzZUZpbmRJbmRleChhcnJheSwgcHJlZGljYXRlLCBmcm9tUmlnaHQpIHtcbiAgdmFyIGxlbmd0aCA9IGFycmF5Lmxlbmd0aCxcbiAgICAgIGluZGV4ID0gZnJvbVJpZ2h0ID8gbGVuZ3RoIDogLTE7XG5cbiAgd2hpbGUgKChmcm9tUmlnaHQgPyBpbmRleC0tIDogKytpbmRleCA8IGxlbmd0aCkpIHtcbiAgICBpZiAocHJlZGljYXRlKGFycmF5W2luZGV4XSwgaW5kZXgsIGFycmF5KSkge1xuICAgICAgcmV0dXJuIGluZGV4O1xuICAgIH1cbiAgfVxuICByZXR1cm4gLTE7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUZpbmRJbmRleDtcbiIsInZhciBjcmVhdGVCYXNlRm9yID0gcmVxdWlyZSgnLi9jcmVhdGVCYXNlRm9yJyk7XG5cbi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYGJhc2VGb3JJbmAgYW5kIGBiYXNlRm9yT3duYCB3aGljaCBpdGVyYXRlc1xuICogb3ZlciBgb2JqZWN0YCBwcm9wZXJ0aWVzIHJldHVybmVkIGJ5IGBrZXlzRnVuY2AgaW52b2tpbmcgYGl0ZXJhdGVlYCBmb3JcbiAqIGVhY2ggcHJvcGVydHkuIEl0ZXJhdG9yIGZ1bmN0aW9ucyBtYXkgZXhpdCBpdGVyYXRpb24gZWFybHkgYnkgZXhwbGljaXRseVxuICogcmV0dXJuaW5nIGBmYWxzZWAuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3QgVGhlIG9iamVjdCB0byBpdGVyYXRlIG92ZXIuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBpdGVyYXRlZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHBhcmFtIHtGdW5jdGlvbn0ga2V5c0Z1bmMgVGhlIGZ1bmN0aW9uIHRvIGdldCB0aGUga2V5cyBvZiBgb2JqZWN0YC5cbiAqIEByZXR1cm5zIHtPYmplY3R9IFJldHVybnMgYG9iamVjdGAuXG4gKi9cbnZhciBiYXNlRm9yID0gY3JlYXRlQmFzZUZvcigpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VGb3I7XG4iLCJ2YXIgYmFzZUZvciA9IHJlcXVpcmUoJy4vYmFzZUZvcicpLFxuICAgIGtleXNJbiA9IHJlcXVpcmUoJy4uL29iamVjdC9rZXlzSW4nKTtcblxuLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgXy5mb3JJbmAgd2l0aG91dCBzdXBwb3J0IGZvciBjYWxsYmFja1xuICogc2hvcnRoYW5kcyBhbmQgYHRoaXNgIGJpbmRpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3QgVGhlIG9iamVjdCB0byBpdGVyYXRlIG92ZXIuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBpdGVyYXRlZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBgb2JqZWN0YC5cbiAqL1xuZnVuY3Rpb24gYmFzZUZvckluKG9iamVjdCwgaXRlcmF0ZWUpIHtcbiAgcmV0dXJuIGJhc2VGb3Iob2JqZWN0LCBpdGVyYXRlZSwga2V5c0luKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBiYXNlRm9ySW47XG4iLCJ2YXIgYmFzZUZvciA9IHJlcXVpcmUoJy4vYmFzZUZvcicpLFxuICAgIGtleXMgPSByZXF1aXJlKCcuLi9vYmplY3Qva2V5cycpO1xuXG4vKipcbiAqIFRoZSBiYXNlIGltcGxlbWVudGF0aW9uIG9mIGBfLmZvck93bmAgd2l0aG91dCBzdXBwb3J0IGZvciBjYWxsYmFja1xuICogc2hvcnRoYW5kcyBhbmQgYHRoaXNgIGJpbmRpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3QgVGhlIG9iamVjdCB0byBpdGVyYXRlIG92ZXIuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBpdGVyYXRlZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyBgb2JqZWN0YC5cbiAqL1xuZnVuY3Rpb24gYmFzZUZvck93bihvYmplY3QsIGl0ZXJhdGVlKSB7XG4gIHJldHVybiBiYXNlRm9yKG9iamVjdCwgaXRlcmF0ZWUsIGtleXMpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VGb3JPd247XG4iLCJ2YXIgaW5kZXhPZk5hTiA9IHJlcXVpcmUoJy4vaW5kZXhPZk5hTicpO1xuXG4vKipcbiAqIFRoZSBiYXNlIGltcGxlbWVudGF0aW9uIG9mIGBfLmluZGV4T2ZgIHdpdGhvdXQgc3VwcG9ydCBmb3IgYmluYXJ5IHNlYXJjaGVzLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgYXJyYXkgdG8gc2VhcmNoLlxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gc2VhcmNoIGZvci5cbiAqIEBwYXJhbSB7bnVtYmVyfSBmcm9tSW5kZXggVGhlIGluZGV4IHRvIHNlYXJjaCBmcm9tLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgaW5kZXggb2YgdGhlIG1hdGNoZWQgdmFsdWUsIGVsc2UgYC0xYC5cbiAqL1xuZnVuY3Rpb24gYmFzZUluZGV4T2YoYXJyYXksIHZhbHVlLCBmcm9tSW5kZXgpIHtcbiAgaWYgKHZhbHVlICE9PSB2YWx1ZSkge1xuICAgIHJldHVybiBpbmRleE9mTmFOKGFycmF5LCBmcm9tSW5kZXgpO1xuICB9XG4gIHZhciBpbmRleCA9IGZyb21JbmRleCAtIDEsXG4gICAgICBsZW5ndGggPSBhcnJheS5sZW5ndGg7XG5cbiAgd2hpbGUgKCsraW5kZXggPCBsZW5ndGgpIHtcbiAgICBpZiAoYXJyYXlbaW5kZXhdID09PSB2YWx1ZSkge1xuICAgICAgcmV0dXJuIGluZGV4O1xuICAgIH1cbiAgfVxuICByZXR1cm4gLTE7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUluZGV4T2Y7XG4iLCJ2YXIgYmFzZUlzRXF1YWxEZWVwID0gcmVxdWlyZSgnLi9iYXNlSXNFcXVhbERlZXAnKTtcblxuLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgXy5pc0VxdWFsYCB3aXRob3V0IHN1cHBvcnQgZm9yIGB0aGlzYCBiaW5kaW5nXG4gKiBgY3VzdG9taXplcmAgZnVuY3Rpb25zLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjb21wYXJlLlxuICogQHBhcmFtIHsqfSBvdGhlciBUaGUgb3RoZXIgdmFsdWUgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IFtjdXN0b21pemVyXSBUaGUgZnVuY3Rpb24gdG8gY3VzdG9taXplIGNvbXBhcmluZyB2YWx1ZXMuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0xvb3NlXSBTcGVjaWZ5IHBlcmZvcm1pbmcgcGFydGlhbCBjb21wYXJpc29ucy5cbiAqIEBwYXJhbSB7QXJyYXl9IFtzdGFja0FdIFRyYWNrcyB0cmF2ZXJzZWQgYHZhbHVlYCBvYmplY3RzLlxuICogQHBhcmFtIHtBcnJheX0gW3N0YWNrQl0gVHJhY2tzIHRyYXZlcnNlZCBgb3RoZXJgIG9iamVjdHMuXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIHZhbHVlcyBhcmUgZXF1aXZhbGVudCwgZWxzZSBgZmFsc2VgLlxuICovXG5mdW5jdGlvbiBiYXNlSXNFcXVhbCh2YWx1ZSwgb3RoZXIsIGN1c3RvbWl6ZXIsIGlzTG9vc2UsIHN0YWNrQSwgc3RhY2tCKSB7XG4gIC8vIEV4aXQgZWFybHkgZm9yIGlkZW50aWNhbCB2YWx1ZXMuXG4gIGlmICh2YWx1ZSA9PT0gb3RoZXIpIHtcbiAgICAvLyBUcmVhdCBgKzBgIHZzLiBgLTBgIGFzIG5vdCBlcXVhbC5cbiAgICByZXR1cm4gdmFsdWUgIT09IDAgfHwgKDEgLyB2YWx1ZSA9PSAxIC8gb3RoZXIpO1xuICB9XG4gIHZhciB2YWxUeXBlID0gdHlwZW9mIHZhbHVlLFxuICAgICAgb3RoVHlwZSA9IHR5cGVvZiBvdGhlcjtcblxuICAvLyBFeGl0IGVhcmx5IGZvciB1bmxpa2UgcHJpbWl0aXZlIHZhbHVlcy5cbiAgaWYgKCh2YWxUeXBlICE9ICdmdW5jdGlvbicgJiYgdmFsVHlwZSAhPSAnb2JqZWN0JyAmJiBvdGhUeXBlICE9ICdmdW5jdGlvbicgJiYgb3RoVHlwZSAhPSAnb2JqZWN0JykgfHxcbiAgICAgIHZhbHVlID09IG51bGwgfHwgb3RoZXIgPT0gbnVsbCkge1xuICAgIC8vIFJldHVybiBgZmFsc2VgIHVubGVzcyBib3RoIHZhbHVlcyBhcmUgYE5hTmAuXG4gICAgcmV0dXJuIHZhbHVlICE9PSB2YWx1ZSAmJiBvdGhlciAhPT0gb3RoZXI7XG4gIH1cbiAgcmV0dXJuIGJhc2VJc0VxdWFsRGVlcCh2YWx1ZSwgb3RoZXIsIGJhc2VJc0VxdWFsLCBjdXN0b21pemVyLCBpc0xvb3NlLCBzdGFja0EsIHN0YWNrQik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUlzRXF1YWw7XG4iLCJ2YXIgZXF1YWxBcnJheXMgPSByZXF1aXJlKCcuL2VxdWFsQXJyYXlzJyksXG4gICAgZXF1YWxCeVRhZyA9IHJlcXVpcmUoJy4vZXF1YWxCeVRhZycpLFxuICAgIGVxdWFsT2JqZWN0cyA9IHJlcXVpcmUoJy4vZXF1YWxPYmplY3RzJyksXG4gICAgaXNBcnJheSA9IHJlcXVpcmUoJy4uL2xhbmcvaXNBcnJheScpLFxuICAgIGlzSG9zdE9iamVjdCA9IHJlcXVpcmUoJy4vaXNIb3N0T2JqZWN0JyksXG4gICAgaXNUeXBlZEFycmF5ID0gcmVxdWlyZSgnLi4vbGFuZy9pc1R5cGVkQXJyYXknKTtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIGFyZ3NUYWcgPSAnW29iamVjdCBBcmd1bWVudHNdJyxcbiAgICBhcnJheVRhZyA9ICdbb2JqZWN0IEFycmF5XScsXG4gICAgZnVuY1RhZyA9ICdbb2JqZWN0IEZ1bmN0aW9uXScsXG4gICAgb2JqZWN0VGFnID0gJ1tvYmplY3QgT2JqZWN0XSc7XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKiogVXNlZCB0byBjaGVjayBvYmplY3RzIGZvciBvd24gcHJvcGVydGllcy4gKi9cbnZhciBoYXNPd25Qcm9wZXJ0eSA9IG9iamVjdFByb3RvLmhhc093blByb3BlcnR5O1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGUgW2B0b1N0cmluZ1RhZ2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqVG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyoqXG4gKiBBIHNwZWNpYWxpemVkIHZlcnNpb24gb2YgYGJhc2VJc0VxdWFsYCBmb3IgYXJyYXlzIGFuZCBvYmplY3RzIHdoaWNoIHBlcmZvcm1zXG4gKiBkZWVwIGNvbXBhcmlzb25zIGFuZCB0cmFja3MgdHJhdmVyc2VkIG9iamVjdHMgZW5hYmxpbmcgb2JqZWN0cyB3aXRoIGNpcmN1bGFyXG4gKiByZWZlcmVuY2VzIHRvIGJlIGNvbXBhcmVkLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqZWN0IFRoZSBvYmplY3QgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvdGhlciBUaGUgb3RoZXIgb2JqZWN0IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlcXVhbEZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRldGVybWluZSBlcXVpdmFsZW50cyBvZiB2YWx1ZXMuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBbY3VzdG9taXplcl0gVGhlIGZ1bmN0aW9uIHRvIGN1c3RvbWl6ZSBjb21wYXJpbmcgb2JqZWN0cy5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2lzTG9vc2VdIFNwZWNpZnkgcGVyZm9ybWluZyBwYXJ0aWFsIGNvbXBhcmlzb25zLlxuICogQHBhcmFtIHtBcnJheX0gW3N0YWNrQT1bXV0gVHJhY2tzIHRyYXZlcnNlZCBgdmFsdWVgIG9iamVjdHMuXG4gKiBAcGFyYW0ge0FycmF5fSBbc3RhY2tCPVtdXSBUcmFja3MgdHJhdmVyc2VkIGBvdGhlcmAgb2JqZWN0cy5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgb2JqZWN0cyBhcmUgZXF1aXZhbGVudCwgZWxzZSBgZmFsc2VgLlxuICovXG5mdW5jdGlvbiBiYXNlSXNFcXVhbERlZXAob2JqZWN0LCBvdGhlciwgZXF1YWxGdW5jLCBjdXN0b21pemVyLCBpc0xvb3NlLCBzdGFja0EsIHN0YWNrQikge1xuICB2YXIgb2JqSXNBcnIgPSBpc0FycmF5KG9iamVjdCksXG4gICAgICBvdGhJc0FyciA9IGlzQXJyYXkob3RoZXIpLFxuICAgICAgb2JqVGFnID0gYXJyYXlUYWcsXG4gICAgICBvdGhUYWcgPSBhcnJheVRhZztcblxuICBpZiAoIW9iaklzQXJyKSB7XG4gICAgb2JqVGFnID0gb2JqVG9TdHJpbmcuY2FsbChvYmplY3QpO1xuICAgIGlmIChvYmpUYWcgPT0gYXJnc1RhZykge1xuICAgICAgb2JqVGFnID0gb2JqZWN0VGFnO1xuICAgIH0gZWxzZSBpZiAob2JqVGFnICE9IG9iamVjdFRhZykge1xuICAgICAgb2JqSXNBcnIgPSBpc1R5cGVkQXJyYXkob2JqZWN0KTtcbiAgICB9XG4gIH1cbiAgaWYgKCFvdGhJc0Fycikge1xuICAgIG90aFRhZyA9IG9ialRvU3RyaW5nLmNhbGwob3RoZXIpO1xuICAgIGlmIChvdGhUYWcgPT0gYXJnc1RhZykge1xuICAgICAgb3RoVGFnID0gb2JqZWN0VGFnO1xuICAgIH0gZWxzZSBpZiAob3RoVGFnICE9IG9iamVjdFRhZykge1xuICAgICAgb3RoSXNBcnIgPSBpc1R5cGVkQXJyYXkob3RoZXIpO1xuICAgIH1cbiAgfVxuICB2YXIgb2JqSXNPYmogPSAob2JqVGFnID09IG9iamVjdFRhZyB8fCAoaXNMb29zZSAmJiBvYmpUYWcgPT0gZnVuY1RhZykpICYmICFpc0hvc3RPYmplY3Qob2JqZWN0KSxcbiAgICAgIG90aElzT2JqID0gKG90aFRhZyA9PSBvYmplY3RUYWcgfHwgKGlzTG9vc2UgJiYgb3RoVGFnID09IGZ1bmNUYWcpKSAmJiAhaXNIb3N0T2JqZWN0KG90aGVyKSxcbiAgICAgIGlzU2FtZVRhZyA9IG9ialRhZyA9PSBvdGhUYWc7XG5cbiAgaWYgKGlzU2FtZVRhZyAmJiAhKG9iaklzQXJyIHx8IG9iaklzT2JqKSkge1xuICAgIHJldHVybiBlcXVhbEJ5VGFnKG9iamVjdCwgb3RoZXIsIG9ialRhZyk7XG4gIH1cbiAgaWYgKGlzTG9vc2UpIHtcbiAgICBpZiAoIWlzU2FtZVRhZyAmJiAhKG9iaklzT2JqICYmIG90aElzT2JqKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICB2YXIgdmFsV3JhcHBlZCA9IG9iaklzT2JqICYmIGhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCAnX193cmFwcGVkX18nKSxcbiAgICAgICAgb3RoV3JhcHBlZCA9IG90aElzT2JqICYmIGhhc093blByb3BlcnR5LmNhbGwob3RoZXIsICdfX3dyYXBwZWRfXycpO1xuXG4gICAgaWYgKHZhbFdyYXBwZWQgfHwgb3RoV3JhcHBlZCkge1xuICAgICAgcmV0dXJuIGVxdWFsRnVuYyh2YWxXcmFwcGVkID8gb2JqZWN0LnZhbHVlKCkgOiBvYmplY3QsIG90aFdyYXBwZWQgPyBvdGhlci52YWx1ZSgpIDogb3RoZXIsIGN1c3RvbWl6ZXIsIGlzTG9vc2UsIHN0YWNrQSwgc3RhY2tCKTtcbiAgICB9XG4gICAgaWYgKCFpc1NhbWVUYWcpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgLy8gQXNzdW1lIGN5Y2xpYyB2YWx1ZXMgYXJlIGVxdWFsLlxuICAvLyBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBkZXRlY3RpbmcgY2lyY3VsYXIgcmVmZXJlbmNlcyBzZWUgaHR0cHM6Ly9lczUuZ2l0aHViLmlvLyNKTy5cbiAgc3RhY2tBIHx8IChzdGFja0EgPSBbXSk7XG4gIHN0YWNrQiB8fCAoc3RhY2tCID0gW10pO1xuXG4gIHZhciBsZW5ndGggPSBzdGFja0EubGVuZ3RoO1xuICB3aGlsZSAobGVuZ3RoLS0pIHtcbiAgICBpZiAoc3RhY2tBW2xlbmd0aF0gPT0gb2JqZWN0KSB7XG4gICAgICByZXR1cm4gc3RhY2tCW2xlbmd0aF0gPT0gb3RoZXI7XG4gICAgfVxuICB9XG4gIC8vIEFkZCBgb2JqZWN0YCBhbmQgYG90aGVyYCB0byB0aGUgc3RhY2sgb2YgdHJhdmVyc2VkIG9iamVjdHMuXG4gIHN0YWNrQS5wdXNoKG9iamVjdCk7XG4gIHN0YWNrQi5wdXNoKG90aGVyKTtcblxuICB2YXIgcmVzdWx0ID0gKG9iaklzQXJyID8gZXF1YWxBcnJheXMgOiBlcXVhbE9iamVjdHMpKG9iamVjdCwgb3RoZXIsIGVxdWFsRnVuYywgY3VzdG9taXplciwgaXNMb29zZSwgc3RhY2tBLCBzdGFja0IpO1xuXG4gIHN0YWNrQS5wb3AoKTtcbiAgc3RhY2tCLnBvcCgpO1xuXG4gIHJldHVybiByZXN1bHQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUlzRXF1YWxEZWVwO1xuIiwiLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgXy5pc0Z1bmN0aW9uYCB3aXRob3V0IHN1cHBvcnQgZm9yIGVudmlyb25tZW50c1xuICogd2l0aCBpbmNvcnJlY3QgYHR5cGVvZmAgcmVzdWx0cy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBjb3JyZWN0bHkgY2xhc3NpZmllZCwgZWxzZSBgZmFsc2VgLlxuICovXG5mdW5jdGlvbiBiYXNlSXNGdW5jdGlvbih2YWx1ZSkge1xuICAvLyBBdm9pZCBhIENoYWtyYSBKSVQgYnVnIGluIGNvbXBhdGliaWxpdHkgbW9kZXMgb2YgSUUgMTEuXG4gIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vamFzaGtlbmFzL3VuZGVyc2NvcmUvaXNzdWVzLzE2MjEgZm9yIG1vcmUgZGV0YWlscy5cbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnZnVuY3Rpb24nIHx8IGZhbHNlO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VJc0Z1bmN0aW9uO1xuIiwidmFyIGJhc2VJc0VxdWFsID0gcmVxdWlyZSgnLi9iYXNlSXNFcXVhbCcpO1xuXG4vKipcbiAqIFRoZSBiYXNlIGltcGxlbWVudGF0aW9uIG9mIGBfLmlzTWF0Y2hgIHdpdGhvdXQgc3VwcG9ydCBmb3IgY2FsbGJhY2tcbiAqIHNob3J0aGFuZHMgYW5kIGB0aGlzYCBiaW5kaW5nLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqZWN0IFRoZSBvYmplY3QgdG8gaW5zcGVjdC5cbiAqIEBwYXJhbSB7QXJyYXl9IHByb3BzIFRoZSBzb3VyY2UgcHJvcGVydHkgbmFtZXMgdG8gbWF0Y2guXG4gKiBAcGFyYW0ge0FycmF5fSB2YWx1ZXMgVGhlIHNvdXJjZSB2YWx1ZXMgdG8gbWF0Y2guXG4gKiBAcGFyYW0ge0FycmF5fSBzdHJpY3RDb21wYXJlRmxhZ3MgU3RyaWN0IGNvbXBhcmlzb24gZmxhZ3MgZm9yIHNvdXJjZSB2YWx1ZXMuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBbY3VzdG9taXplcl0gVGhlIGZ1bmN0aW9uIHRvIGN1c3RvbWl6ZSBjb21wYXJpbmcgb2JqZWN0cy5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgb2JqZWN0YCBpcyBhIG1hdGNoLCBlbHNlIGBmYWxzZWAuXG4gKi9cbmZ1bmN0aW9uIGJhc2VJc01hdGNoKG9iamVjdCwgcHJvcHMsIHZhbHVlcywgc3RyaWN0Q29tcGFyZUZsYWdzLCBjdXN0b21pemVyKSB7XG4gIHZhciBpbmRleCA9IC0xLFxuICAgICAgbGVuZ3RoID0gcHJvcHMubGVuZ3RoLFxuICAgICAgbm9DdXN0b21pemVyID0gIWN1c3RvbWl6ZXI7XG5cbiAgd2hpbGUgKCsraW5kZXggPCBsZW5ndGgpIHtcbiAgICBpZiAoKG5vQ3VzdG9taXplciAmJiBzdHJpY3RDb21wYXJlRmxhZ3NbaW5kZXhdKVxuICAgICAgICAgID8gdmFsdWVzW2luZGV4XSAhPT0gb2JqZWN0W3Byb3BzW2luZGV4XV1cbiAgICAgICAgICA6ICEocHJvcHNbaW5kZXhdIGluIG9iamVjdClcbiAgICAgICAgKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG4gIGluZGV4ID0gLTE7XG4gIHdoaWxlICgrK2luZGV4IDwgbGVuZ3RoKSB7XG4gICAgdmFyIGtleSA9IHByb3BzW2luZGV4XSxcbiAgICAgICAgb2JqVmFsdWUgPSBvYmplY3Rba2V5XSxcbiAgICAgICAgc3JjVmFsdWUgPSB2YWx1ZXNbaW5kZXhdO1xuXG4gICAgaWYgKG5vQ3VzdG9taXplciAmJiBzdHJpY3RDb21wYXJlRmxhZ3NbaW5kZXhdKSB7XG4gICAgICB2YXIgcmVzdWx0ID0gdHlwZW9mIG9ialZhbHVlICE9ICd1bmRlZmluZWQnIHx8IChrZXkgaW4gb2JqZWN0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVzdWx0ID0gY3VzdG9taXplciA/IGN1c3RvbWl6ZXIob2JqVmFsdWUsIHNyY1ZhbHVlLCBrZXkpIDogdW5kZWZpbmVkO1xuICAgICAgaWYgKHR5cGVvZiByZXN1bHQgPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgcmVzdWx0ID0gYmFzZUlzRXF1YWwoc3JjVmFsdWUsIG9ialZhbHVlLCBjdXN0b21pemVyLCB0cnVlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZUlzTWF0Y2g7XG4iLCIvKipcbiAqIFRoZSBmdW5jdGlvbiB3aG9zZSBwcm90b3R5cGUgYWxsIGNoYWluaW5nIHdyYXBwZXJzIGluaGVyaXQgZnJvbS5cbiAqXG4gKiBAcHJpdmF0ZVxuICovXG5mdW5jdGlvbiBiYXNlTG9kYXNoKCkge1xuICAvLyBObyBvcGVyYXRpb24gcGVyZm9ybWVkLlxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VMb2Rhc2g7XG4iLCJ2YXIgYmFzZUVhY2ggPSByZXF1aXJlKCcuL2Jhc2VFYWNoJyk7XG5cbi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYF8ubWFwYCB3aXRob3V0IHN1cHBvcnQgZm9yIGNhbGxiYWNrIHNob3J0aGFuZHNcbiAqIGFuZCBgdGhpc2AgYmluZGluZy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheXxPYmplY3R8c3RyaW5nfSBjb2xsZWN0aW9uIFRoZSBjb2xsZWN0aW9uIHRvIGl0ZXJhdGUgb3Zlci5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGl0ZXJhdGVlIFRoZSBmdW5jdGlvbiBpbnZva2VkIHBlciBpdGVyYXRpb24uXG4gKiBAcmV0dXJucyB7QXJyYXl9IFJldHVybnMgdGhlIG5ldyBtYXBwZWQgYXJyYXkuXG4gKi9cbmZ1bmN0aW9uIGJhc2VNYXAoY29sbGVjdGlvbiwgaXRlcmF0ZWUpIHtcbiAgdmFyIHJlc3VsdCA9IFtdO1xuICBiYXNlRWFjaChjb2xsZWN0aW9uLCBmdW5jdGlvbih2YWx1ZSwga2V5LCBjb2xsZWN0aW9uKSB7XG4gICAgcmVzdWx0LnB1c2goaXRlcmF0ZWUodmFsdWUsIGtleSwgY29sbGVjdGlvbikpO1xuICB9KTtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBiYXNlTWFwO1xuIiwidmFyIGJhc2VJc01hdGNoID0gcmVxdWlyZSgnLi9iYXNlSXNNYXRjaCcpLFxuICAgIGNvbnN0YW50ID0gcmVxdWlyZSgnLi4vdXRpbGl0eS9jb25zdGFudCcpLFxuICAgIGlzU3RyaWN0Q29tcGFyYWJsZSA9IHJlcXVpcmUoJy4vaXNTdHJpY3RDb21wYXJhYmxlJyksXG4gICAga2V5cyA9IHJlcXVpcmUoJy4uL29iamVjdC9rZXlzJyksXG4gICAgdG9PYmplY3QgPSByZXF1aXJlKCcuL3RvT2JqZWN0Jyk7XG5cbi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYF8ubWF0Y2hlc2Agd2hpY2ggZG9lcyBub3QgY2xvbmUgYHNvdXJjZWAuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBzb3VyY2UgVGhlIG9iamVjdCBvZiBwcm9wZXJ0eSB2YWx1ZXMgdG8gbWF0Y2guXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBmdW5jdGlvbi5cbiAqL1xuZnVuY3Rpb24gYmFzZU1hdGNoZXMoc291cmNlKSB7XG4gIHZhciBwcm9wcyA9IGtleXMoc291cmNlKSxcbiAgICAgIGxlbmd0aCA9IHByb3BzLmxlbmd0aDtcblxuICBpZiAoIWxlbmd0aCkge1xuICAgIHJldHVybiBjb25zdGFudCh0cnVlKTtcbiAgfVxuICBpZiAobGVuZ3RoID09IDEpIHtcbiAgICB2YXIga2V5ID0gcHJvcHNbMF0sXG4gICAgICAgIHZhbHVlID0gc291cmNlW2tleV07XG5cbiAgICBpZiAoaXNTdHJpY3RDb21wYXJhYmxlKHZhbHVlKSkge1xuICAgICAgcmV0dXJuIGZ1bmN0aW9uKG9iamVjdCkge1xuICAgICAgICByZXR1cm4gb2JqZWN0ICE9IG51bGwgJiYgb2JqZWN0W2tleV0gPT09IHZhbHVlICYmXG4gICAgICAgICAgKHR5cGVvZiB2YWx1ZSAhPSAndW5kZWZpbmVkJyB8fCAoa2V5IGluIHRvT2JqZWN0KG9iamVjdCkpKTtcbiAgICAgIH07XG4gICAgfVxuICB9XG4gIHZhciB2YWx1ZXMgPSBBcnJheShsZW5ndGgpLFxuICAgICAgc3RyaWN0Q29tcGFyZUZsYWdzID0gQXJyYXkobGVuZ3RoKTtcblxuICB3aGlsZSAobGVuZ3RoLS0pIHtcbiAgICB2YWx1ZSA9IHNvdXJjZVtwcm9wc1tsZW5ndGhdXTtcbiAgICB2YWx1ZXNbbGVuZ3RoXSA9IHZhbHVlO1xuICAgIHN0cmljdENvbXBhcmVGbGFnc1tsZW5ndGhdID0gaXNTdHJpY3RDb21wYXJhYmxlKHZhbHVlKTtcbiAgfVxuICByZXR1cm4gZnVuY3Rpb24ob2JqZWN0KSB7XG4gICAgcmV0dXJuIG9iamVjdCAhPSBudWxsICYmIGJhc2VJc01hdGNoKHRvT2JqZWN0KG9iamVjdCksIHByb3BzLCB2YWx1ZXMsIHN0cmljdENvbXBhcmVGbGFncyk7XG4gIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZU1hdGNoZXM7XG4iLCJ2YXIgYmFzZUlzRXF1YWwgPSByZXF1aXJlKCcuL2Jhc2VJc0VxdWFsJyksXG4gICAgaXNTdHJpY3RDb21wYXJhYmxlID0gcmVxdWlyZSgnLi9pc1N0cmljdENvbXBhcmFibGUnKSxcbiAgICB0b09iamVjdCA9IHJlcXVpcmUoJy4vdG9PYmplY3QnKTtcblxuLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgXy5tYXRjaGVzUHJvcGVydHlgIHdoaWNoIGRvZXMgbm90IGNvZXJjZSBga2V5YFxuICogdG8gYSBzdHJpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgVGhlIGtleSBvZiB0aGUgcHJvcGVydHkgdG8gZ2V0LlxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY29tcGFyZS5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGZ1bmN0aW9uLlxuICovXG5mdW5jdGlvbiBiYXNlTWF0Y2hlc1Byb3BlcnR5KGtleSwgdmFsdWUpIHtcbiAgaWYgKGlzU3RyaWN0Q29tcGFyYWJsZSh2YWx1ZSkpIHtcbiAgICByZXR1cm4gZnVuY3Rpb24ob2JqZWN0KSB7XG4gICAgICByZXR1cm4gb2JqZWN0ICE9IG51bGwgJiYgb2JqZWN0W2tleV0gPT09IHZhbHVlICYmXG4gICAgICAgICh0eXBlb2YgdmFsdWUgIT0gJ3VuZGVmaW5lZCcgfHwgKGtleSBpbiB0b09iamVjdChvYmplY3QpKSk7XG4gICAgfTtcbiAgfVxuICByZXR1cm4gZnVuY3Rpb24ob2JqZWN0KSB7XG4gICAgcmV0dXJuIG9iamVjdCAhPSBudWxsICYmIGJhc2VJc0VxdWFsKHZhbHVlLCBvYmplY3Rba2V5XSwgbnVsbCwgdHJ1ZSk7XG4gIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZU1hdGNoZXNQcm9wZXJ0eTtcbiIsIi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYF8ucHJvcGVydHlgIHdoaWNoIGRvZXMgbm90IGNvZXJjZSBga2V5YCB0byBhIHN0cmluZy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtzdHJpbmd9IGtleSBUaGUga2V5IG9mIHRoZSBwcm9wZXJ0eSB0byBnZXQuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBmdW5jdGlvbi5cbiAqL1xuZnVuY3Rpb24gYmFzZVByb3BlcnR5KGtleSkge1xuICByZXR1cm4gZnVuY3Rpb24ob2JqZWN0KSB7XG4gICAgcmV0dXJuIG9iamVjdCA9PSBudWxsID8gdW5kZWZpbmVkIDogb2JqZWN0W2tleV07XG4gIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZVByb3BlcnR5O1xuIiwidmFyIGlkZW50aXR5ID0gcmVxdWlyZSgnLi4vdXRpbGl0eS9pZGVudGl0eScpLFxuICAgIG1ldGFNYXAgPSByZXF1aXJlKCcuL21ldGFNYXAnKTtcblxuLyoqXG4gKiBUaGUgYmFzZSBpbXBsZW1lbnRhdGlvbiBvZiBgc2V0RGF0YWAgd2l0aG91dCBzdXBwb3J0IGZvciBob3QgbG9vcCBkZXRlY3Rpb24uXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGFzc29jaWF0ZSBtZXRhZGF0YSB3aXRoLlxuICogQHBhcmFtIHsqfSBkYXRhIFRoZSBtZXRhZGF0YS5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyBgZnVuY2AuXG4gKi9cbnZhciBiYXNlU2V0RGF0YSA9ICFtZXRhTWFwID8gaWRlbnRpdHkgOiBmdW5jdGlvbihmdW5jLCBkYXRhKSB7XG4gIG1ldGFNYXAuc2V0KGZ1bmMsIGRhdGEpO1xuICByZXR1cm4gZnVuYztcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gYmFzZVNldERhdGE7XG4iLCIvKipcbiAqIENvbnZlcnRzIGB2YWx1ZWAgdG8gYSBzdHJpbmcgaWYgaXQgaXMgbm90IG9uZS4gQW4gZW1wdHkgc3RyaW5nIGlzIHJldHVybmVkXG4gKiBmb3IgYG51bGxgIG9yIGB1bmRlZmluZWRgIHZhbHVlcy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcHJvY2Vzcy5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IFJldHVybnMgdGhlIHN0cmluZy5cbiAqL1xuZnVuY3Rpb24gYmFzZVRvU3RyaW5nKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgcmV0dXJuIHZhbHVlID09IG51bGwgPyAnJyA6ICh2YWx1ZSArICcnKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBiYXNlVG9TdHJpbmc7XG4iLCJ2YXIgYmluYXJ5SW5kZXhCeSA9IHJlcXVpcmUoJy4vYmluYXJ5SW5kZXhCeScpLFxuICAgIGlkZW50aXR5ID0gcmVxdWlyZSgnLi4vdXRpbGl0eS9pZGVudGl0eScpO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB0aGUgbWF4aW11bSBsZW5ndGggYW5kIGluZGV4IG9mIGFuIGFycmF5LiAqL1xudmFyIE1BWF9BUlJBWV9MRU5HVEggPSBNYXRoLnBvdygyLCAzMikgLSAxLFxuICAgIEhBTEZfTUFYX0FSUkFZX0xFTkdUSCA9IE1BWF9BUlJBWV9MRU5HVEggPj4+IDE7XG5cbi8qKlxuICogUGVyZm9ybXMgYSBiaW5hcnkgc2VhcmNoIG9mIGBhcnJheWAgdG8gZGV0ZXJtaW5lIHRoZSBpbmRleCBhdCB3aGljaCBgdmFsdWVgXG4gKiBzaG91bGQgYmUgaW5zZXJ0ZWQgaW50byBgYXJyYXlgIGluIG9yZGVyIHRvIG1haW50YWluIGl0cyBzb3J0IG9yZGVyLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgc29ydGVkIGFycmF5IHRvIGluc3BlY3QuXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBldmFsdWF0ZS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3JldEhpZ2hlc3RdIFNwZWNpZnkgcmV0dXJuaW5nIHRoZSBoaWdoZXN0IHF1YWxpZmllZCBpbmRleC5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIGluZGV4IGF0IHdoaWNoIGB2YWx1ZWAgc2hvdWxkIGJlIGluc2VydGVkXG4gKiAgaW50byBgYXJyYXlgLlxuICovXG5mdW5jdGlvbiBiaW5hcnlJbmRleChhcnJheSwgdmFsdWUsIHJldEhpZ2hlc3QpIHtcbiAgdmFyIGxvdyA9IDAsXG4gICAgICBoaWdoID0gYXJyYXkgPyBhcnJheS5sZW5ndGggOiBsb3c7XG5cbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PSAnbnVtYmVyJyAmJiB2YWx1ZSA9PT0gdmFsdWUgJiYgaGlnaCA8PSBIQUxGX01BWF9BUlJBWV9MRU5HVEgpIHtcbiAgICB3aGlsZSAobG93IDwgaGlnaCkge1xuICAgICAgdmFyIG1pZCA9IChsb3cgKyBoaWdoKSA+Pj4gMSxcbiAgICAgICAgICBjb21wdXRlZCA9IGFycmF5W21pZF07XG5cbiAgICAgIGlmIChyZXRIaWdoZXN0ID8gKGNvbXB1dGVkIDw9IHZhbHVlKSA6IChjb21wdXRlZCA8IHZhbHVlKSkge1xuICAgICAgICBsb3cgPSBtaWQgKyAxO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaGlnaCA9IG1pZDtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGhpZ2g7XG4gIH1cbiAgcmV0dXJuIGJpbmFyeUluZGV4QnkoYXJyYXksIHZhbHVlLCBpZGVudGl0eSwgcmV0SGlnaGVzdCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmluYXJ5SW5kZXg7XG4iLCIvKiogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIGZsb29yID0gTWF0aC5mbG9vcjtcblxuLyogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqIFVzZWQgYXMgcmVmZXJlbmNlcyBmb3IgdGhlIG1heGltdW0gbGVuZ3RoIGFuZCBpbmRleCBvZiBhbiBhcnJheS4gKi9cbnZhciBNQVhfQVJSQVlfTEVOR1RIID0gTWF0aC5wb3coMiwgMzIpIC0gMSxcbiAgICBNQVhfQVJSQVlfSU5ERVggPSAgTUFYX0FSUkFZX0xFTkdUSCAtIDE7XG5cbi8qKlxuICogVGhpcyBmdW5jdGlvbiBpcyBsaWtlIGBiaW5hcnlJbmRleGAgZXhjZXB0IHRoYXQgaXQgaW52b2tlcyBgaXRlcmF0ZWVgIGZvclxuICogYHZhbHVlYCBhbmQgZWFjaCBlbGVtZW50IG9mIGBhcnJheWAgdG8gY29tcHV0ZSB0aGVpciBzb3J0IHJhbmtpbmcuIFRoZVxuICogaXRlcmF0ZWUgaXMgaW52b2tlZCB3aXRoIG9uZSBhcmd1bWVudDsgKHZhbHVlKS5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheX0gYXJyYXkgVGhlIHNvcnRlZCBhcnJheSB0byBpbnNwZWN0LlxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gZXZhbHVhdGUuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBpdGVyYXRlZSBUaGUgZnVuY3Rpb24gaW52b2tlZCBwZXIgaXRlcmF0aW9uLlxuICogQHBhcmFtIHtib29sZWFufSBbcmV0SGlnaGVzdF0gU3BlY2lmeSByZXR1cm5pbmcgdGhlIGhpZ2hlc3QgcXVhbGlmaWVkIGluZGV4LlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgaW5kZXggYXQgd2hpY2ggYHZhbHVlYCBzaG91bGQgYmUgaW5zZXJ0ZWRcbiAqICBpbnRvIGBhcnJheWAuXG4gKi9cbmZ1bmN0aW9uIGJpbmFyeUluZGV4QnkoYXJyYXksIHZhbHVlLCBpdGVyYXRlZSwgcmV0SGlnaGVzdCkge1xuICB2YWx1ZSA9IGl0ZXJhdGVlKHZhbHVlKTtcblxuICB2YXIgbG93ID0gMCxcbiAgICAgIGhpZ2ggPSBhcnJheSA/IGFycmF5Lmxlbmd0aCA6IDAsXG4gICAgICB2YWxJc05hTiA9IHZhbHVlICE9PSB2YWx1ZSxcbiAgICAgIHZhbElzVW5kZWYgPSB0eXBlb2YgdmFsdWUgPT0gJ3VuZGVmaW5lZCc7XG5cbiAgd2hpbGUgKGxvdyA8IGhpZ2gpIHtcbiAgICB2YXIgbWlkID0gZmxvb3IoKGxvdyArIGhpZ2gpIC8gMiksXG4gICAgICAgIGNvbXB1dGVkID0gaXRlcmF0ZWUoYXJyYXlbbWlkXSksXG4gICAgICAgIGlzUmVmbGV4aXZlID0gY29tcHV0ZWQgPT09IGNvbXB1dGVkO1xuXG4gICAgaWYgKHZhbElzTmFOKSB7XG4gICAgICB2YXIgc2V0TG93ID0gaXNSZWZsZXhpdmUgfHwgcmV0SGlnaGVzdDtcbiAgICB9IGVsc2UgaWYgKHZhbElzVW5kZWYpIHtcbiAgICAgIHNldExvdyA9IGlzUmVmbGV4aXZlICYmIChyZXRIaWdoZXN0IHx8IHR5cGVvZiBjb21wdXRlZCAhPSAndW5kZWZpbmVkJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNldExvdyA9IHJldEhpZ2hlc3QgPyAoY29tcHV0ZWQgPD0gdmFsdWUpIDogKGNvbXB1dGVkIDwgdmFsdWUpO1xuICAgIH1cbiAgICBpZiAoc2V0TG93KSB7XG4gICAgICBsb3cgPSBtaWQgKyAxO1xuICAgIH0gZWxzZSB7XG4gICAgICBoaWdoID0gbWlkO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbmF0aXZlTWluKGhpZ2gsIE1BWF9BUlJBWV9JTkRFWCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYmluYXJ5SW5kZXhCeTtcbiIsInZhciBpZGVudGl0eSA9IHJlcXVpcmUoJy4uL3V0aWxpdHkvaWRlbnRpdHknKTtcblxuLyoqXG4gKiBBIHNwZWNpYWxpemVkIHZlcnNpb24gb2YgYGJhc2VDYWxsYmFja2Agd2hpY2ggb25seSBzdXBwb3J0cyBgdGhpc2AgYmluZGluZ1xuICogYW5kIHNwZWNpZnlpbmcgdGhlIG51bWJlciBvZiBhcmd1bWVudHMgdG8gcHJvdmlkZSB0byBgZnVuY2AuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGJpbmQuXG4gKiBAcGFyYW0geyp9IHRoaXNBcmcgVGhlIGB0aGlzYCBiaW5kaW5nIG9mIGBmdW5jYC5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbYXJnQ291bnRdIFRoZSBudW1iZXIgb2YgYXJndW1lbnRzIHRvIHByb3ZpZGUgdG8gYGZ1bmNgLlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBjYWxsYmFjay5cbiAqL1xuZnVuY3Rpb24gYmluZENhbGxiYWNrKGZ1bmMsIHRoaXNBcmcsIGFyZ0NvdW50KSB7XG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgcmV0dXJuIGlkZW50aXR5O1xuICB9XG4gIGlmICh0eXBlb2YgdGhpc0FyZyA9PSAndW5kZWZpbmVkJykge1xuICAgIHJldHVybiBmdW5jO1xuICB9XG4gIHN3aXRjaCAoYXJnQ291bnQpIHtcbiAgICBjYXNlIDE6IHJldHVybiBmdW5jdGlvbih2YWx1ZSkge1xuICAgICAgcmV0dXJuIGZ1bmMuY2FsbCh0aGlzQXJnLCB2YWx1ZSk7XG4gICAgfTtcbiAgICBjYXNlIDM6IHJldHVybiBmdW5jdGlvbih2YWx1ZSwgaW5kZXgsIGNvbGxlY3Rpb24pIHtcbiAgICAgIHJldHVybiBmdW5jLmNhbGwodGhpc0FyZywgdmFsdWUsIGluZGV4LCBjb2xsZWN0aW9uKTtcbiAgICB9O1xuICAgIGNhc2UgNDogcmV0dXJuIGZ1bmN0aW9uKGFjY3VtdWxhdG9yLCB2YWx1ZSwgaW5kZXgsIGNvbGxlY3Rpb24pIHtcbiAgICAgIHJldHVybiBmdW5jLmNhbGwodGhpc0FyZywgYWNjdW11bGF0b3IsIHZhbHVlLCBpbmRleCwgY29sbGVjdGlvbik7XG4gICAgfTtcbiAgICBjYXNlIDU6IHJldHVybiBmdW5jdGlvbih2YWx1ZSwgb3RoZXIsIGtleSwgb2JqZWN0LCBzb3VyY2UpIHtcbiAgICAgIHJldHVybiBmdW5jLmNhbGwodGhpc0FyZywgdmFsdWUsIG90aGVyLCBrZXksIG9iamVjdCwgc291cmNlKTtcbiAgICB9O1xuICB9XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICByZXR1cm4gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmd1bWVudHMpO1xuICB9O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJpbmRDYWxsYmFjaztcbiIsInZhciBjb25zdGFudCA9IHJlcXVpcmUoJy4uL3V0aWxpdHkvY29uc3RhbnQnKSxcbiAgICBpc05hdGl2ZSA9IHJlcXVpcmUoJy4uL2xhbmcvaXNOYXRpdmUnKTtcblxuLyoqIE5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBBcnJheUJ1ZmZlciA9IGlzTmF0aXZlKEFycmF5QnVmZmVyID0gZ2xvYmFsLkFycmF5QnVmZmVyKSAmJiBBcnJheUJ1ZmZlcixcbiAgICBidWZmZXJTbGljZSA9IGlzTmF0aXZlKGJ1ZmZlclNsaWNlID0gQXJyYXlCdWZmZXIgJiYgbmV3IEFycmF5QnVmZmVyKDApLnNsaWNlKSAmJiBidWZmZXJTbGljZSxcbiAgICBmbG9vciA9IE1hdGguZmxvb3IsXG4gICAgVWludDhBcnJheSA9IGlzTmF0aXZlKFVpbnQ4QXJyYXkgPSBnbG9iYWwuVWludDhBcnJheSkgJiYgVWludDhBcnJheTtcblxuLyoqIFVzZWQgdG8gY2xvbmUgYXJyYXkgYnVmZmVycy4gKi9cbnZhciBGbG9hdDY0QXJyYXkgPSAoZnVuY3Rpb24oKSB7XG4gIC8vIFNhZmFyaSA1IGVycm9ycyB3aGVuIHVzaW5nIGFuIGFycmF5IGJ1ZmZlciB0byBpbml0aWFsaXplIGEgdHlwZWQgYXJyYXlcbiAgLy8gd2hlcmUgdGhlIGFycmF5IGJ1ZmZlcidzIGBieXRlTGVuZ3RoYCBpcyBub3QgYSBtdWx0aXBsZSBvZiB0aGUgdHlwZWRcbiAgLy8gYXJyYXkncyBgQllURVNfUEVSX0VMRU1FTlRgLlxuICB0cnkge1xuICAgIHZhciBmdW5jID0gaXNOYXRpdmUoZnVuYyA9IGdsb2JhbC5GbG9hdDY0QXJyYXkpICYmIGZ1bmMsXG4gICAgICAgIHJlc3VsdCA9IG5ldyBmdW5jKG5ldyBBcnJheUJ1ZmZlcigxMCksIDAsIDEpICYmIGZ1bmM7XG4gIH0gY2F0Y2goZSkge31cbiAgcmV0dXJuIHJlc3VsdDtcbn0oKSk7XG5cbi8qKiBVc2VkIGFzIHRoZSBzaXplLCBpbiBieXRlcywgb2YgZWFjaCBgRmxvYXQ2NEFycmF5YCBlbGVtZW50LiAqL1xudmFyIEZMT0FUNjRfQllURVNfUEVSX0VMRU1FTlQgPSBGbG9hdDY0QXJyYXkgPyBGbG9hdDY0QXJyYXkuQllURVNfUEVSX0VMRU1FTlQgOiAwO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBjbG9uZSBvZiB0aGUgZ2l2ZW4gYXJyYXkgYnVmZmVyLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5QnVmZmVyfSBidWZmZXIgVGhlIGFycmF5IGJ1ZmZlciB0byBjbG9uZS5cbiAqIEByZXR1cm5zIHtBcnJheUJ1ZmZlcn0gUmV0dXJucyB0aGUgY2xvbmVkIGFycmF5IGJ1ZmZlci5cbiAqL1xuZnVuY3Rpb24gYnVmZmVyQ2xvbmUoYnVmZmVyKSB7XG4gIHJldHVybiBidWZmZXJTbGljZS5jYWxsKGJ1ZmZlciwgMCk7XG59XG5pZiAoIWJ1ZmZlclNsaWNlKSB7XG4gIC8vIFBoYW50b21KUyBoYXMgYEFycmF5QnVmZmVyYCBhbmQgYFVpbnQ4QXJyYXlgIGJ1dCBub3QgYEZsb2F0NjRBcnJheWAuXG4gIGJ1ZmZlckNsb25lID0gIShBcnJheUJ1ZmZlciAmJiBVaW50OEFycmF5KSA/IGNvbnN0YW50KG51bGwpIDogZnVuY3Rpb24oYnVmZmVyKSB7XG4gICAgdmFyIGJ5dGVMZW5ndGggPSBidWZmZXIuYnl0ZUxlbmd0aCxcbiAgICAgICAgZmxvYXRMZW5ndGggPSBGbG9hdDY0QXJyYXkgPyBmbG9vcihieXRlTGVuZ3RoIC8gRkxPQVQ2NF9CWVRFU19QRVJfRUxFTUVOVCkgOiAwLFxuICAgICAgICBvZmZzZXQgPSBmbG9hdExlbmd0aCAqIEZMT0FUNjRfQllURVNfUEVSX0VMRU1FTlQsXG4gICAgICAgIHJlc3VsdCA9IG5ldyBBcnJheUJ1ZmZlcihieXRlTGVuZ3RoKTtcblxuICAgIGlmIChmbG9hdExlbmd0aCkge1xuICAgICAgdmFyIHZpZXcgPSBuZXcgRmxvYXQ2NEFycmF5KHJlc3VsdCwgMCwgZmxvYXRMZW5ndGgpO1xuICAgICAgdmlldy5zZXQobmV3IEZsb2F0NjRBcnJheShidWZmZXIsIDAsIGZsb2F0TGVuZ3RoKSk7XG4gICAgfVxuICAgIGlmIChieXRlTGVuZ3RoICE9IG9mZnNldCkge1xuICAgICAgdmlldyA9IG5ldyBVaW50OEFycmF5KHJlc3VsdCwgb2Zmc2V0KTtcbiAgICAgIHZpZXcuc2V0KG5ldyBVaW50OEFycmF5KGJ1ZmZlciwgb2Zmc2V0KSk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYnVmZmVyQ2xvbmU7XG4iLCIvKiBOYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZU1heCA9IE1hdGgubWF4O1xuXG4vKipcbiAqIENyZWF0ZXMgYW4gYXJyYXkgdGhhdCBpcyB0aGUgY29tcG9zaXRpb24gb2YgcGFydGlhbGx5IGFwcGxpZWQgYXJndW1lbnRzLFxuICogcGxhY2Vob2xkZXJzLCBhbmQgcHJvdmlkZWQgYXJndW1lbnRzIGludG8gYSBzaW5nbGUgYXJyYXkgb2YgYXJndW1lbnRzLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fE9iamVjdH0gYXJncyBUaGUgcHJvdmlkZWQgYXJndW1lbnRzLlxuICogQHBhcmFtIHtBcnJheX0gcGFydGlhbHMgVGhlIGFyZ3VtZW50cyB0byBwcmVwZW5kIHRvIHRob3NlIHByb3ZpZGVkLlxuICogQHBhcmFtIHtBcnJheX0gaG9sZGVycyBUaGUgYHBhcnRpYWxzYCBwbGFjZWhvbGRlciBpbmRleGVzLlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIHRoZSBuZXcgYXJyYXkgb2YgY29tcG9zZWQgYXJndW1lbnRzLlxuICovXG5mdW5jdGlvbiBjb21wb3NlQXJncyhhcmdzLCBwYXJ0aWFscywgaG9sZGVycykge1xuICB2YXIgaG9sZGVyc0xlbmd0aCA9IGhvbGRlcnMubGVuZ3RoLFxuICAgICAgYXJnc0luZGV4ID0gLTEsXG4gICAgICBhcmdzTGVuZ3RoID0gbmF0aXZlTWF4KGFyZ3MubGVuZ3RoIC0gaG9sZGVyc0xlbmd0aCwgMCksXG4gICAgICBsZWZ0SW5kZXggPSAtMSxcbiAgICAgIGxlZnRMZW5ndGggPSBwYXJ0aWFscy5sZW5ndGgsXG4gICAgICByZXN1bHQgPSBBcnJheShhcmdzTGVuZ3RoICsgbGVmdExlbmd0aCk7XG5cbiAgd2hpbGUgKCsrbGVmdEluZGV4IDwgbGVmdExlbmd0aCkge1xuICAgIHJlc3VsdFtsZWZ0SW5kZXhdID0gcGFydGlhbHNbbGVmdEluZGV4XTtcbiAgfVxuICB3aGlsZSAoKythcmdzSW5kZXggPCBob2xkZXJzTGVuZ3RoKSB7XG4gICAgcmVzdWx0W2hvbGRlcnNbYXJnc0luZGV4XV0gPSBhcmdzW2FyZ3NJbmRleF07XG4gIH1cbiAgd2hpbGUgKGFyZ3NMZW5ndGgtLSkge1xuICAgIHJlc3VsdFtsZWZ0SW5kZXgrK10gPSBhcmdzW2FyZ3NJbmRleCsrXTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbXBvc2VBcmdzO1xuIiwiLyogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heDtcblxuLyoqXG4gKiBUaGlzIGZ1bmN0aW9uIGlzIGxpa2UgYGNvbXBvc2VBcmdzYCBleGNlcHQgdGhhdCB0aGUgYXJndW1lbnRzIGNvbXBvc2l0aW9uXG4gKiBpcyB0YWlsb3JlZCBmb3IgYF8ucGFydGlhbFJpZ2h0YC5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheXxPYmplY3R9IGFyZ3MgVGhlIHByb3ZpZGVkIGFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7QXJyYXl9IHBhcnRpYWxzIFRoZSBhcmd1bWVudHMgdG8gYXBwZW5kIHRvIHRob3NlIHByb3ZpZGVkLlxuICogQHBhcmFtIHtBcnJheX0gaG9sZGVycyBUaGUgYHBhcnRpYWxzYCBwbGFjZWhvbGRlciBpbmRleGVzLlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIHRoZSBuZXcgYXJyYXkgb2YgY29tcG9zZWQgYXJndW1lbnRzLlxuICovXG5mdW5jdGlvbiBjb21wb3NlQXJnc1JpZ2h0KGFyZ3MsIHBhcnRpYWxzLCBob2xkZXJzKSB7XG4gIHZhciBob2xkZXJzSW5kZXggPSAtMSxcbiAgICAgIGhvbGRlcnNMZW5ndGggPSBob2xkZXJzLmxlbmd0aCxcbiAgICAgIGFyZ3NJbmRleCA9IC0xLFxuICAgICAgYXJnc0xlbmd0aCA9IG5hdGl2ZU1heChhcmdzLmxlbmd0aCAtIGhvbGRlcnNMZW5ndGgsIDApLFxuICAgICAgcmlnaHRJbmRleCA9IC0xLFxuICAgICAgcmlnaHRMZW5ndGggPSBwYXJ0aWFscy5sZW5ndGgsXG4gICAgICByZXN1bHQgPSBBcnJheShhcmdzTGVuZ3RoICsgcmlnaHRMZW5ndGgpO1xuXG4gIHdoaWxlICgrK2FyZ3NJbmRleCA8IGFyZ3NMZW5ndGgpIHtcbiAgICByZXN1bHRbYXJnc0luZGV4XSA9IGFyZ3NbYXJnc0luZGV4XTtcbiAgfVxuICB2YXIgcGFkID0gYXJnc0luZGV4O1xuICB3aGlsZSAoKytyaWdodEluZGV4IDwgcmlnaHRMZW5ndGgpIHtcbiAgICByZXN1bHRbcGFkICsgcmlnaHRJbmRleF0gPSBwYXJ0aWFsc1tyaWdodEluZGV4XTtcbiAgfVxuICB3aGlsZSAoKytob2xkZXJzSW5kZXggPCBob2xkZXJzTGVuZ3RoKSB7XG4gICAgcmVzdWx0W3BhZCArIGhvbGRlcnNbaG9sZGVyc0luZGV4XV0gPSBhcmdzW2FyZ3NJbmRleCsrXTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbXBvc2VBcmdzUmlnaHQ7XG4iLCJ2YXIgaXNMZW5ndGggPSByZXF1aXJlKCcuL2lzTGVuZ3RoJyksXG4gICAgdG9PYmplY3QgPSByZXF1aXJlKCcuL3RvT2JqZWN0Jyk7XG5cbi8qKlxuICogQ3JlYXRlcyBhIGBiYXNlRWFjaGAgb3IgYGJhc2VFYWNoUmlnaHRgIGZ1bmN0aW9uLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlYWNoRnVuYyBUaGUgZnVuY3Rpb24gdG8gaXRlcmF0ZSBvdmVyIGEgY29sbGVjdGlvbi5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2Zyb21SaWdodF0gU3BlY2lmeSBpdGVyYXRpbmcgZnJvbSByaWdodCB0byBsZWZ0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgYmFzZSBmdW5jdGlvbi5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlQmFzZUVhY2goZWFjaEZ1bmMsIGZyb21SaWdodCkge1xuICByZXR1cm4gZnVuY3Rpb24oY29sbGVjdGlvbiwgaXRlcmF0ZWUpIHtcbiAgICB2YXIgbGVuZ3RoID0gY29sbGVjdGlvbiA/IGNvbGxlY3Rpb24ubGVuZ3RoIDogMDtcbiAgICBpZiAoIWlzTGVuZ3RoKGxlbmd0aCkpIHtcbiAgICAgIHJldHVybiBlYWNoRnVuYyhjb2xsZWN0aW9uLCBpdGVyYXRlZSk7XG4gICAgfVxuICAgIHZhciBpbmRleCA9IGZyb21SaWdodCA/IGxlbmd0aCA6IC0xLFxuICAgICAgICBpdGVyYWJsZSA9IHRvT2JqZWN0KGNvbGxlY3Rpb24pO1xuXG4gICAgd2hpbGUgKChmcm9tUmlnaHQgPyBpbmRleC0tIDogKytpbmRleCA8IGxlbmd0aCkpIHtcbiAgICAgIGlmIChpdGVyYXRlZShpdGVyYWJsZVtpbmRleF0sIGluZGV4LCBpdGVyYWJsZSkgPT09IGZhbHNlKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gY29sbGVjdGlvbjtcbiAgfTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBjcmVhdGVCYXNlRWFjaDtcbiIsInZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vdG9PYmplY3QnKTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgYmFzZSBmdW5jdGlvbiBmb3IgYF8uZm9ySW5gIG9yIGBfLmZvckluUmlnaHRgLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtmcm9tUmlnaHRdIFNwZWNpZnkgaXRlcmF0aW5nIGZyb20gcmlnaHQgdG8gbGVmdC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGJhc2UgZnVuY3Rpb24uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUJhc2VGb3IoZnJvbVJpZ2h0KSB7XG4gIHJldHVybiBmdW5jdGlvbihvYmplY3QsIGl0ZXJhdGVlLCBrZXlzRnVuYykge1xuICAgIHZhciBpdGVyYWJsZSA9IHRvT2JqZWN0KG9iamVjdCksXG4gICAgICAgIHByb3BzID0ga2V5c0Z1bmMob2JqZWN0KSxcbiAgICAgICAgbGVuZ3RoID0gcHJvcHMubGVuZ3RoLFxuICAgICAgICBpbmRleCA9IGZyb21SaWdodCA/IGxlbmd0aCA6IC0xO1xuXG4gICAgd2hpbGUgKChmcm9tUmlnaHQgPyBpbmRleC0tIDogKytpbmRleCA8IGxlbmd0aCkpIHtcbiAgICAgIHZhciBrZXkgPSBwcm9wc1tpbmRleF07XG4gICAgICBpZiAoaXRlcmF0ZWUoaXRlcmFibGVba2V5XSwga2V5LCBpdGVyYWJsZSkgPT09IGZhbHNlKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gb2JqZWN0O1xuICB9O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNyZWF0ZUJhc2VGb3I7XG4iLCJ2YXIgY3JlYXRlQ3RvcldyYXBwZXIgPSByZXF1aXJlKCcuL2NyZWF0ZUN0b3JXcmFwcGVyJyk7XG5cbi8qKlxuICogQ3JlYXRlcyBhIGZ1bmN0aW9uIHRoYXQgd3JhcHMgYGZ1bmNgIGFuZCBpbnZva2VzIGl0IHdpdGggdGhlIGB0aGlzYFxuICogYmluZGluZyBvZiBgdGhpc0FyZ2AuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGJpbmQuXG4gKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBUaGUgYHRoaXNgIGJpbmRpbmcgb2YgYGZ1bmNgLlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgYm91bmQgZnVuY3Rpb24uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUJpbmRXcmFwcGVyKGZ1bmMsIHRoaXNBcmcpIHtcbiAgdmFyIEN0b3IgPSBjcmVhdGVDdG9yV3JhcHBlcihmdW5jKTtcblxuICBmdW5jdGlvbiB3cmFwcGVyKCkge1xuICAgIHZhciBmbiA9ICh0aGlzICYmIHRoaXMgIT09IGdsb2JhbCAmJiB0aGlzIGluc3RhbmNlb2Ygd3JhcHBlcikgPyBDdG9yIDogZnVuYztcbiAgICByZXR1cm4gZm4uYXBwbHkodGhpc0FyZywgYXJndW1lbnRzKTtcbiAgfVxuICByZXR1cm4gd3JhcHBlcjtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBjcmVhdGVCaW5kV3JhcHBlcjtcbiIsInZhciBiYXNlQ3JlYXRlID0gcmVxdWlyZSgnLi9iYXNlQ3JlYXRlJyksXG4gICAgaXNPYmplY3QgPSByZXF1aXJlKCcuLi9sYW5nL2lzT2JqZWN0Jyk7XG5cbi8qKlxuICogQ3JlYXRlcyBhIGZ1bmN0aW9uIHRoYXQgcHJvZHVjZXMgYW4gaW5zdGFuY2Ugb2YgYEN0b3JgIHJlZ2FyZGxlc3Mgb2ZcbiAqIHdoZXRoZXIgaXQgd2FzIGludm9rZWQgYXMgcGFydCBvZiBhIGBuZXdgIGV4cHJlc3Npb24gb3IgYnkgYGNhbGxgIG9yIGBhcHBseWAuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7RnVuY3Rpb259IEN0b3IgVGhlIGNvbnN0cnVjdG9yIHRvIHdyYXAuXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyB3cmFwcGVkIGZ1bmN0aW9uLlxuICovXG5mdW5jdGlvbiBjcmVhdGVDdG9yV3JhcHBlcihDdG9yKSB7XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICB2YXIgdGhpc0JpbmRpbmcgPSBiYXNlQ3JlYXRlKEN0b3IucHJvdG90eXBlKSxcbiAgICAgICAgcmVzdWx0ID0gQ3Rvci5hcHBseSh0aGlzQmluZGluZywgYXJndW1lbnRzKTtcblxuICAgIC8vIE1pbWljIHRoZSBjb25zdHJ1Y3RvcidzIGByZXR1cm5gIGJlaGF2aW9yLlxuICAgIC8vIFNlZSBodHRwczovL2VzNS5naXRodWIuaW8vI3gxMy4yLjIgZm9yIG1vcmUgZGV0YWlscy5cbiAgICByZXR1cm4gaXNPYmplY3QocmVzdWx0KSA/IHJlc3VsdCA6IHRoaXNCaW5kaW5nO1xuICB9O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNyZWF0ZUN0b3JXcmFwcGVyO1xuIiwidmFyIGJhc2VDYWxsYmFjayA9IHJlcXVpcmUoJy4vYmFzZUNhbGxiYWNrJyksXG4gICAgYmFzZUZpbmQgPSByZXF1aXJlKCcuL2Jhc2VGaW5kJyksXG4gICAgYmFzZUZpbmRJbmRleCA9IHJlcXVpcmUoJy4vYmFzZUZpbmRJbmRleCcpLFxuICAgIGlzQXJyYXkgPSByZXF1aXJlKCcuLi9sYW5nL2lzQXJyYXknKTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgYF8uZmluZGAgb3IgYF8uZmluZExhc3RgIGZ1bmN0aW9uLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlYWNoRnVuYyBUaGUgZnVuY3Rpb24gdG8gaXRlcmF0ZSBvdmVyIGEgY29sbGVjdGlvbi5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2Zyb21SaWdodF0gU3BlY2lmeSBpdGVyYXRpbmcgZnJvbSByaWdodCB0byBsZWZ0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZmluZCBmdW5jdGlvbi5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlRmluZChlYWNoRnVuYywgZnJvbVJpZ2h0KSB7XG4gIHJldHVybiBmdW5jdGlvbihjb2xsZWN0aW9uLCBwcmVkaWNhdGUsIHRoaXNBcmcpIHtcbiAgICBwcmVkaWNhdGUgPSBiYXNlQ2FsbGJhY2socHJlZGljYXRlLCB0aGlzQXJnLCAzKTtcbiAgICBpZiAoaXNBcnJheShjb2xsZWN0aW9uKSkge1xuICAgICAgdmFyIGluZGV4ID0gYmFzZUZpbmRJbmRleChjb2xsZWN0aW9uLCBwcmVkaWNhdGUsIGZyb21SaWdodCk7XG4gICAgICByZXR1cm4gaW5kZXggPiAtMSA/IGNvbGxlY3Rpb25baW5kZXhdIDogdW5kZWZpbmVkO1xuICAgIH1cbiAgICByZXR1cm4gYmFzZUZpbmQoY29sbGVjdGlvbiwgcHJlZGljYXRlLCBlYWNoRnVuYyk7XG4gIH1cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBjcmVhdGVGaW5kO1xuIiwidmFyIGJpbmRDYWxsYmFjayA9IHJlcXVpcmUoJy4vYmluZENhbGxiYWNrJyksXG4gICAgaXNBcnJheSA9IHJlcXVpcmUoJy4uL2xhbmcvaXNBcnJheScpO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBmdW5jdGlvbiBmb3IgYF8uZm9yRWFjaGAgb3IgYF8uZm9yRWFjaFJpZ2h0YC5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtGdW5jdGlvbn0gYXJyYXlGdW5jIFRoZSBmdW5jdGlvbiB0byBpdGVyYXRlIG92ZXIgYW4gYXJyYXkuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlYWNoRnVuYyBUaGUgZnVuY3Rpb24gdG8gaXRlcmF0ZSBvdmVyIGEgY29sbGVjdGlvbi5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGVhY2ggZnVuY3Rpb24uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZUZvckVhY2goYXJyYXlGdW5jLCBlYWNoRnVuYykge1xuICByZXR1cm4gZnVuY3Rpb24oY29sbGVjdGlvbiwgaXRlcmF0ZWUsIHRoaXNBcmcpIHtcbiAgICByZXR1cm4gKHR5cGVvZiBpdGVyYXRlZSA9PSAnZnVuY3Rpb24nICYmIHR5cGVvZiB0aGlzQXJnID09ICd1bmRlZmluZWQnICYmIGlzQXJyYXkoY29sbGVjdGlvbikpXG4gICAgICA/IGFycmF5RnVuYyhjb2xsZWN0aW9uLCBpdGVyYXRlZSlcbiAgICAgIDogZWFjaEZ1bmMoY29sbGVjdGlvbiwgYmluZENhbGxiYWNrKGl0ZXJhdGVlLCB0aGlzQXJnLCAzKSk7XG4gIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gY3JlYXRlRm9yRWFjaDtcbiIsInZhciBhcnJheUNvcHkgPSByZXF1aXJlKCcuL2FycmF5Q29weScpLFxuICAgIGNvbXBvc2VBcmdzID0gcmVxdWlyZSgnLi9jb21wb3NlQXJncycpLFxuICAgIGNvbXBvc2VBcmdzUmlnaHQgPSByZXF1aXJlKCcuL2NvbXBvc2VBcmdzUmlnaHQnKSxcbiAgICBjcmVhdGVDdG9yV3JhcHBlciA9IHJlcXVpcmUoJy4vY3JlYXRlQ3RvcldyYXBwZXInKSxcbiAgICBpc0xhemlhYmxlID0gcmVxdWlyZSgnLi9pc0xhemlhYmxlJyksXG4gICAgcmVvcmRlciA9IHJlcXVpcmUoJy4vcmVvcmRlcicpLFxuICAgIHJlcGxhY2VIb2xkZXJzID0gcmVxdWlyZSgnLi9yZXBsYWNlSG9sZGVycycpLFxuICAgIHNldERhdGEgPSByZXF1aXJlKCcuL3NldERhdGEnKTtcblxuLyoqIFVzZWQgdG8gY29tcG9zZSBiaXRtYXNrcyBmb3Igd3JhcHBlciBtZXRhZGF0YS4gKi9cbnZhciBCSU5EX0ZMQUcgPSAxLFxuICAgIEJJTkRfS0VZX0ZMQUcgPSAyLFxuICAgIENVUlJZX0JPVU5EX0ZMQUcgPSA0LFxuICAgIENVUlJZX0ZMQUcgPSA4LFxuICAgIENVUlJZX1JJR0hUX0ZMQUcgPSAxNixcbiAgICBQQVJUSUFMX0ZMQUcgPSAzMixcbiAgICBQQVJUSUFMX1JJR0hUX0ZMQUcgPSA2NCxcbiAgICBBUllfRkxBRyA9IDEyODtcblxuLyogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heDtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZnVuY3Rpb24gdGhhdCB3cmFwcyBgZnVuY2AgYW5kIGludm9rZXMgaXQgd2l0aCBvcHRpb25hbCBgdGhpc2BcbiAqIGJpbmRpbmcgb2YsIHBhcnRpYWwgYXBwbGljYXRpb24sIGFuZCBjdXJyeWluZy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtGdW5jdGlvbnxzdHJpbmd9IGZ1bmMgVGhlIGZ1bmN0aW9uIG9yIG1ldGhvZCBuYW1lIHRvIHJlZmVyZW5jZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBiaXRtYXNrIFRoZSBiaXRtYXNrIG9mIGZsYWdzLiBTZWUgYGNyZWF0ZVdyYXBwZXJgIGZvciBtb3JlIGRldGFpbHMuXG4gKiBAcGFyYW0geyp9IFt0aGlzQXJnXSBUaGUgYHRoaXNgIGJpbmRpbmcgb2YgYGZ1bmNgLlxuICogQHBhcmFtIHtBcnJheX0gW3BhcnRpYWxzXSBUaGUgYXJndW1lbnRzIHRvIHByZXBlbmQgdG8gdGhvc2UgcHJvdmlkZWQgdG8gdGhlIG5ldyBmdW5jdGlvbi5cbiAqIEBwYXJhbSB7QXJyYXl9IFtob2xkZXJzXSBUaGUgYHBhcnRpYWxzYCBwbGFjZWhvbGRlciBpbmRleGVzLlxuICogQHBhcmFtIHtBcnJheX0gW3BhcnRpYWxzUmlnaHRdIFRoZSBhcmd1bWVudHMgdG8gYXBwZW5kIHRvIHRob3NlIHByb3ZpZGVkIHRvIHRoZSBuZXcgZnVuY3Rpb24uXG4gKiBAcGFyYW0ge0FycmF5fSBbaG9sZGVyc1JpZ2h0XSBUaGUgYHBhcnRpYWxzUmlnaHRgIHBsYWNlaG9sZGVyIGluZGV4ZXMuXG4gKiBAcGFyYW0ge0FycmF5fSBbYXJnUG9zXSBUaGUgYXJndW1lbnQgcG9zaXRpb25zIG9mIHRoZSBuZXcgZnVuY3Rpb24uXG4gKiBAcGFyYW0ge251bWJlcn0gW2FyeV0gVGhlIGFyaXR5IGNhcCBvZiBgZnVuY2AuXG4gKiBAcGFyYW0ge251bWJlcn0gW2FyaXR5XSBUaGUgYXJpdHkgb2YgYGZ1bmNgLlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgd3JhcHBlZCBmdW5jdGlvbi5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlSHlicmlkV3JhcHBlcihmdW5jLCBiaXRtYXNrLCB0aGlzQXJnLCBwYXJ0aWFscywgaG9sZGVycywgcGFydGlhbHNSaWdodCwgaG9sZGVyc1JpZ2h0LCBhcmdQb3MsIGFyeSwgYXJpdHkpIHtcbiAgdmFyIGlzQXJ5ID0gYml0bWFzayAmIEFSWV9GTEFHLFxuICAgICAgaXNCaW5kID0gYml0bWFzayAmIEJJTkRfRkxBRyxcbiAgICAgIGlzQmluZEtleSA9IGJpdG1hc2sgJiBCSU5EX0tFWV9GTEFHLFxuICAgICAgaXNDdXJyeSA9IGJpdG1hc2sgJiBDVVJSWV9GTEFHLFxuICAgICAgaXNDdXJyeUJvdW5kID0gYml0bWFzayAmIENVUlJZX0JPVU5EX0ZMQUcsXG4gICAgICBpc0N1cnJ5UmlnaHQgPSBiaXRtYXNrICYgQ1VSUllfUklHSFRfRkxBRztcblxuICB2YXIgQ3RvciA9ICFpc0JpbmRLZXkgJiYgY3JlYXRlQ3RvcldyYXBwZXIoZnVuYyksXG4gICAgICBrZXkgPSBmdW5jO1xuXG4gIGZ1bmN0aW9uIHdyYXBwZXIoKSB7XG4gICAgLy8gQXZvaWQgYGFyZ3VtZW50c2Agb2JqZWN0IHVzZSBkaXNxdWFsaWZ5aW5nIG9wdGltaXphdGlvbnMgYnlcbiAgICAvLyBjb252ZXJ0aW5nIGl0IHRvIGFuIGFycmF5IGJlZm9yZSBwcm92aWRpbmcgaXQgdG8gb3RoZXIgZnVuY3Rpb25zLlxuICAgIHZhciBsZW5ndGggPSBhcmd1bWVudHMubGVuZ3RoLFxuICAgICAgICBpbmRleCA9IGxlbmd0aCxcbiAgICAgICAgYXJncyA9IEFycmF5KGxlbmd0aCk7XG5cbiAgICB3aGlsZSAoaW5kZXgtLSkge1xuICAgICAgYXJnc1tpbmRleF0gPSBhcmd1bWVudHNbaW5kZXhdO1xuICAgIH1cbiAgICBpZiAocGFydGlhbHMpIHtcbiAgICAgIGFyZ3MgPSBjb21wb3NlQXJncyhhcmdzLCBwYXJ0aWFscywgaG9sZGVycyk7XG4gICAgfVxuICAgIGlmIChwYXJ0aWFsc1JpZ2h0KSB7XG4gICAgICBhcmdzID0gY29tcG9zZUFyZ3NSaWdodChhcmdzLCBwYXJ0aWFsc1JpZ2h0LCBob2xkZXJzUmlnaHQpO1xuICAgIH1cbiAgICBpZiAoaXNDdXJyeSB8fCBpc0N1cnJ5UmlnaHQpIHtcbiAgICAgIHZhciBwbGFjZWhvbGRlciA9IHdyYXBwZXIucGxhY2Vob2xkZXIsXG4gICAgICAgICAgYXJnc0hvbGRlcnMgPSByZXBsYWNlSG9sZGVycyhhcmdzLCBwbGFjZWhvbGRlcik7XG5cbiAgICAgIGxlbmd0aCAtPSBhcmdzSG9sZGVycy5sZW5ndGg7XG4gICAgICBpZiAobGVuZ3RoIDwgYXJpdHkpIHtcbiAgICAgICAgdmFyIG5ld0FyZ1BvcyA9IGFyZ1BvcyA/IGFycmF5Q29weShhcmdQb3MpIDogbnVsbCxcbiAgICAgICAgICAgIG5ld0FyaXR5ID0gbmF0aXZlTWF4KGFyaXR5IC0gbGVuZ3RoLCAwKSxcbiAgICAgICAgICAgIG5ld3NIb2xkZXJzID0gaXNDdXJyeSA/IGFyZ3NIb2xkZXJzIDogbnVsbCxcbiAgICAgICAgICAgIG5ld0hvbGRlcnNSaWdodCA9IGlzQ3VycnkgPyBudWxsIDogYXJnc0hvbGRlcnMsXG4gICAgICAgICAgICBuZXdQYXJ0aWFscyA9IGlzQ3VycnkgPyBhcmdzIDogbnVsbCxcbiAgICAgICAgICAgIG5ld1BhcnRpYWxzUmlnaHQgPSBpc0N1cnJ5ID8gbnVsbCA6IGFyZ3M7XG5cbiAgICAgICAgYml0bWFzayB8PSAoaXNDdXJyeSA/IFBBUlRJQUxfRkxBRyA6IFBBUlRJQUxfUklHSFRfRkxBRyk7XG4gICAgICAgIGJpdG1hc2sgJj0gfihpc0N1cnJ5ID8gUEFSVElBTF9SSUdIVF9GTEFHIDogUEFSVElBTF9GTEFHKTtcblxuICAgICAgICBpZiAoIWlzQ3VycnlCb3VuZCkge1xuICAgICAgICAgIGJpdG1hc2sgJj0gfihCSU5EX0ZMQUcgfCBCSU5EX0tFWV9GTEFHKTtcbiAgICAgICAgfVxuICAgICAgICB2YXIgbmV3RGF0YSA9IFtmdW5jLCBiaXRtYXNrLCB0aGlzQXJnLCBuZXdQYXJ0aWFscywgbmV3c0hvbGRlcnMsIG5ld1BhcnRpYWxzUmlnaHQsIG5ld0hvbGRlcnNSaWdodCwgbmV3QXJnUG9zLCBhcnksIG5ld0FyaXR5XSxcbiAgICAgICAgICAgIHJlc3VsdCA9IGNyZWF0ZUh5YnJpZFdyYXBwZXIuYXBwbHkodW5kZWZpbmVkLCBuZXdEYXRhKTtcblxuICAgICAgICBpZiAoaXNMYXppYWJsZShmdW5jKSkge1xuICAgICAgICAgIHNldERhdGEocmVzdWx0LCBuZXdEYXRhKTtcbiAgICAgICAgfVxuICAgICAgICByZXN1bHQucGxhY2Vob2xkZXIgPSBwbGFjZWhvbGRlcjtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH1cbiAgICB9XG4gICAgdmFyIHRoaXNCaW5kaW5nID0gaXNCaW5kID8gdGhpc0FyZyA6IHRoaXM7XG4gICAgaWYgKGlzQmluZEtleSkge1xuICAgICAgZnVuYyA9IHRoaXNCaW5kaW5nW2tleV07XG4gICAgfVxuICAgIGlmIChhcmdQb3MpIHtcbiAgICAgIGFyZ3MgPSByZW9yZGVyKGFyZ3MsIGFyZ1Bvcyk7XG4gICAgfVxuICAgIGlmIChpc0FyeSAmJiBhcnkgPCBhcmdzLmxlbmd0aCkge1xuICAgICAgYXJncy5sZW5ndGggPSBhcnk7XG4gICAgfVxuICAgIHZhciBmbiA9ICh0aGlzICYmIHRoaXMgIT09IGdsb2JhbCAmJiB0aGlzIGluc3RhbmNlb2Ygd3JhcHBlcikgPyAoQ3RvciB8fCBjcmVhdGVDdG9yV3JhcHBlcihmdW5jKSkgOiBmdW5jO1xuICAgIHJldHVybiBmbi5hcHBseSh0aGlzQmluZGluZywgYXJncyk7XG4gIH1cbiAgcmV0dXJuIHdyYXBwZXI7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gY3JlYXRlSHlicmlkV3JhcHBlcjtcbiIsInZhciBjcmVhdGVDdG9yV3JhcHBlciA9IHJlcXVpcmUoJy4vY3JlYXRlQ3RvcldyYXBwZXInKTtcblxuLyoqIFVzZWQgdG8gY29tcG9zZSBiaXRtYXNrcyBmb3Igd3JhcHBlciBtZXRhZGF0YS4gKi9cbnZhciBCSU5EX0ZMQUcgPSAxO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBmdW5jdGlvbiB0aGF0IHdyYXBzIGBmdW5jYCBhbmQgaW52b2tlcyBpdCB3aXRoIHRoZSBvcHRpb25hbCBgdGhpc2BcbiAqIGJpbmRpbmcgb2YgYHRoaXNBcmdgIGFuZCB0aGUgYHBhcnRpYWxzYCBwcmVwZW5kZWQgdG8gdGhvc2UgcHJvdmlkZWQgdG9cbiAqIHRoZSB3cmFwcGVyLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBwYXJ0aWFsbHkgYXBwbHkgYXJndW1lbnRzIHRvLlxuICogQHBhcmFtIHtudW1iZXJ9IGJpdG1hc2sgVGhlIGJpdG1hc2sgb2YgZmxhZ3MuIFNlZSBgY3JlYXRlV3JhcHBlcmAgZm9yIG1vcmUgZGV0YWlscy5cbiAqIEBwYXJhbSB7Kn0gdGhpc0FyZyBUaGUgYHRoaXNgIGJpbmRpbmcgb2YgYGZ1bmNgLlxuICogQHBhcmFtIHtBcnJheX0gcGFydGlhbHMgVGhlIGFyZ3VtZW50cyB0byBwcmVwZW5kIHRvIHRob3NlIHByb3ZpZGVkIHRvIHRoZSBuZXcgZnVuY3Rpb24uXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IFJldHVybnMgdGhlIG5ldyBib3VuZCBmdW5jdGlvbi5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlUGFydGlhbFdyYXBwZXIoZnVuYywgYml0bWFzaywgdGhpc0FyZywgcGFydGlhbHMpIHtcbiAgdmFyIGlzQmluZCA9IGJpdG1hc2sgJiBCSU5EX0ZMQUcsXG4gICAgICBDdG9yID0gY3JlYXRlQ3RvcldyYXBwZXIoZnVuYyk7XG5cbiAgZnVuY3Rpb24gd3JhcHBlcigpIHtcbiAgICAvLyBBdm9pZCBgYXJndW1lbnRzYCBvYmplY3QgdXNlIGRpc3F1YWxpZnlpbmcgb3B0aW1pemF0aW9ucyBieVxuICAgIC8vIGNvbnZlcnRpbmcgaXQgdG8gYW4gYXJyYXkgYmVmb3JlIHByb3ZpZGluZyBpdCBgZnVuY2AuXG4gICAgdmFyIGFyZ3NJbmRleCA9IC0xLFxuICAgICAgICBhcmdzTGVuZ3RoID0gYXJndW1lbnRzLmxlbmd0aCxcbiAgICAgICAgbGVmdEluZGV4ID0gLTEsXG4gICAgICAgIGxlZnRMZW5ndGggPSBwYXJ0aWFscy5sZW5ndGgsXG4gICAgICAgIGFyZ3MgPSBBcnJheShhcmdzTGVuZ3RoICsgbGVmdExlbmd0aCk7XG5cbiAgICB3aGlsZSAoKytsZWZ0SW5kZXggPCBsZWZ0TGVuZ3RoKSB7XG4gICAgICBhcmdzW2xlZnRJbmRleF0gPSBwYXJ0aWFsc1tsZWZ0SW5kZXhdO1xuICAgIH1cbiAgICB3aGlsZSAoYXJnc0xlbmd0aC0tKSB7XG4gICAgICBhcmdzW2xlZnRJbmRleCsrXSA9IGFyZ3VtZW50c1srK2FyZ3NJbmRleF07XG4gICAgfVxuICAgIHZhciBmbiA9ICh0aGlzICYmIHRoaXMgIT09IGdsb2JhbCAmJiB0aGlzIGluc3RhbmNlb2Ygd3JhcHBlcikgPyBDdG9yIDogZnVuYztcbiAgICByZXR1cm4gZm4uYXBwbHkoaXNCaW5kID8gdGhpc0FyZyA6IHRoaXMsIGFyZ3MpO1xuICB9XG4gIHJldHVybiB3cmFwcGVyO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNyZWF0ZVBhcnRpYWxXcmFwcGVyO1xuIiwidmFyIGJhc2VTZXREYXRhID0gcmVxdWlyZSgnLi9iYXNlU2V0RGF0YScpLFxuICAgIGNyZWF0ZUJpbmRXcmFwcGVyID0gcmVxdWlyZSgnLi9jcmVhdGVCaW5kV3JhcHBlcicpLFxuICAgIGNyZWF0ZUh5YnJpZFdyYXBwZXIgPSByZXF1aXJlKCcuL2NyZWF0ZUh5YnJpZFdyYXBwZXInKSxcbiAgICBjcmVhdGVQYXJ0aWFsV3JhcHBlciA9IHJlcXVpcmUoJy4vY3JlYXRlUGFydGlhbFdyYXBwZXInKSxcbiAgICBnZXREYXRhID0gcmVxdWlyZSgnLi9nZXREYXRhJyksXG4gICAgbWVyZ2VEYXRhID0gcmVxdWlyZSgnLi9tZXJnZURhdGEnKSxcbiAgICBzZXREYXRhID0gcmVxdWlyZSgnLi9zZXREYXRhJyk7XG5cbi8qKiBVc2VkIHRvIGNvbXBvc2UgYml0bWFza3MgZm9yIHdyYXBwZXIgbWV0YWRhdGEuICovXG52YXIgQklORF9GTEFHID0gMSxcbiAgICBCSU5EX0tFWV9GTEFHID0gMixcbiAgICBQQVJUSUFMX0ZMQUcgPSAzMixcbiAgICBQQVJUSUFMX1JJR0hUX0ZMQUcgPSA2NDtcblxuLyoqIFVzZWQgYXMgdGhlIGBUeXBlRXJyb3JgIG1lc3NhZ2UgZm9yIFwiRnVuY3Rpb25zXCIgbWV0aG9kcy4gKi9cbnZhciBGVU5DX0VSUk9SX1RFWFQgPSAnRXhwZWN0ZWQgYSBmdW5jdGlvbic7XG5cbi8qIE5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcyBmb3IgdGhvc2Ugd2l0aCB0aGUgc2FtZSBuYW1lIGFzIG90aGVyIGBsb2Rhc2hgIG1ldGhvZHMuICovXG52YXIgbmF0aXZlTWF4ID0gTWF0aC5tYXg7XG5cbi8qKlxuICogQ3JlYXRlcyBhIGZ1bmN0aW9uIHRoYXQgZWl0aGVyIGN1cnJpZXMgb3IgaW52b2tlcyBgZnVuY2Agd2l0aCBvcHRpb25hbFxuICogYHRoaXNgIGJpbmRpbmcgYW5kIHBhcnRpYWxseSBhcHBsaWVkIGFyZ3VtZW50cy5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtGdW5jdGlvbnxzdHJpbmd9IGZ1bmMgVGhlIGZ1bmN0aW9uIG9yIG1ldGhvZCBuYW1lIHRvIHJlZmVyZW5jZS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBiaXRtYXNrIFRoZSBiaXRtYXNrIG9mIGZsYWdzLlxuICogIFRoZSBiaXRtYXNrIG1heSBiZSBjb21wb3NlZCBvZiB0aGUgZm9sbG93aW5nIGZsYWdzOlxuICogICAgIDEgLSBgXy5iaW5kYFxuICogICAgIDIgLSBgXy5iaW5kS2V5YFxuICogICAgIDQgLSBgXy5jdXJyeWAgb3IgYF8uY3VycnlSaWdodGAgb2YgYSBib3VuZCBmdW5jdGlvblxuICogICAgIDggLSBgXy5jdXJyeWBcbiAqICAgIDE2IC0gYF8uY3VycnlSaWdodGBcbiAqICAgIDMyIC0gYF8ucGFydGlhbGBcbiAqICAgIDY0IC0gYF8ucGFydGlhbFJpZ2h0YFxuICogICAxMjggLSBgXy5yZWFyZ2BcbiAqICAgMjU2IC0gYF8uYXJ5YFxuICogQHBhcmFtIHsqfSBbdGhpc0FyZ10gVGhlIGB0aGlzYCBiaW5kaW5nIG9mIGBmdW5jYC5cbiAqIEBwYXJhbSB7QXJyYXl9IFtwYXJ0aWFsc10gVGhlIGFyZ3VtZW50cyB0byBiZSBwYXJ0aWFsbHkgYXBwbGllZC5cbiAqIEBwYXJhbSB7QXJyYXl9IFtob2xkZXJzXSBUaGUgYHBhcnRpYWxzYCBwbGFjZWhvbGRlciBpbmRleGVzLlxuICogQHBhcmFtIHtBcnJheX0gW2FyZ1Bvc10gVGhlIGFyZ3VtZW50IHBvc2l0aW9ucyBvZiB0aGUgbmV3IGZ1bmN0aW9uLlxuICogQHBhcmFtIHtudW1iZXJ9IFthcnldIFRoZSBhcml0eSBjYXAgb2YgYGZ1bmNgLlxuICogQHBhcmFtIHtudW1iZXJ9IFthcml0eV0gVGhlIGFyaXR5IG9mIGBmdW5jYC5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IHdyYXBwZWQgZnVuY3Rpb24uXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZVdyYXBwZXIoZnVuYywgYml0bWFzaywgdGhpc0FyZywgcGFydGlhbHMsIGhvbGRlcnMsIGFyZ1BvcywgYXJ5LCBhcml0eSkge1xuICB2YXIgaXNCaW5kS2V5ID0gYml0bWFzayAmIEJJTkRfS0VZX0ZMQUc7XG4gIGlmICghaXNCaW5kS2V5ICYmIHR5cGVvZiBmdW5jICE9ICdmdW5jdGlvbicpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKEZVTkNfRVJST1JfVEVYVCk7XG4gIH1cbiAgdmFyIGxlbmd0aCA9IHBhcnRpYWxzID8gcGFydGlhbHMubGVuZ3RoIDogMDtcbiAgaWYgKCFsZW5ndGgpIHtcbiAgICBiaXRtYXNrICY9IH4oUEFSVElBTF9GTEFHIHwgUEFSVElBTF9SSUdIVF9GTEFHKTtcbiAgICBwYXJ0aWFscyA9IGhvbGRlcnMgPSBudWxsO1xuICB9XG4gIGxlbmd0aCAtPSAoaG9sZGVycyA/IGhvbGRlcnMubGVuZ3RoIDogMCk7XG4gIGlmIChiaXRtYXNrICYgUEFSVElBTF9SSUdIVF9GTEFHKSB7XG4gICAgdmFyIHBhcnRpYWxzUmlnaHQgPSBwYXJ0aWFscyxcbiAgICAgICAgaG9sZGVyc1JpZ2h0ID0gaG9sZGVycztcblxuICAgIHBhcnRpYWxzID0gaG9sZGVycyA9IG51bGw7XG4gIH1cbiAgdmFyIGRhdGEgPSBpc0JpbmRLZXkgPyBudWxsIDogZ2V0RGF0YShmdW5jKSxcbiAgICAgIG5ld0RhdGEgPSBbZnVuYywgYml0bWFzaywgdGhpc0FyZywgcGFydGlhbHMsIGhvbGRlcnMsIHBhcnRpYWxzUmlnaHQsIGhvbGRlcnNSaWdodCwgYXJnUG9zLCBhcnksIGFyaXR5XTtcblxuICBpZiAoZGF0YSkge1xuICAgIG1lcmdlRGF0YShuZXdEYXRhLCBkYXRhKTtcbiAgICBiaXRtYXNrID0gbmV3RGF0YVsxXTtcbiAgICBhcml0eSA9IG5ld0RhdGFbOV07XG4gIH1cbiAgbmV3RGF0YVs5XSA9IGFyaXR5ID09IG51bGxcbiAgICA/IChpc0JpbmRLZXkgPyAwIDogZnVuYy5sZW5ndGgpXG4gICAgOiAobmF0aXZlTWF4KGFyaXR5IC0gbGVuZ3RoLCAwKSB8fCAwKTtcblxuICBpZiAoYml0bWFzayA9PSBCSU5EX0ZMQUcpIHtcbiAgICB2YXIgcmVzdWx0ID0gY3JlYXRlQmluZFdyYXBwZXIobmV3RGF0YVswXSwgbmV3RGF0YVsyXSk7XG4gIH0gZWxzZSBpZiAoKGJpdG1hc2sgPT0gUEFSVElBTF9GTEFHIHx8IGJpdG1hc2sgPT0gKEJJTkRfRkxBRyB8IFBBUlRJQUxfRkxBRykpICYmICFuZXdEYXRhWzRdLmxlbmd0aCkge1xuICAgIHJlc3VsdCA9IGNyZWF0ZVBhcnRpYWxXcmFwcGVyLmFwcGx5KHVuZGVmaW5lZCwgbmV3RGF0YSk7XG4gIH0gZWxzZSB7XG4gICAgcmVzdWx0ID0gY3JlYXRlSHlicmlkV3JhcHBlci5hcHBseSh1bmRlZmluZWQsIG5ld0RhdGEpO1xuICB9XG4gIHZhciBzZXR0ZXIgPSBkYXRhID8gYmFzZVNldERhdGEgOiBzZXREYXRhO1xuICByZXR1cm4gc2V0dGVyKHJlc3VsdCwgbmV3RGF0YSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gY3JlYXRlV3JhcHBlcjtcbiIsIi8qKlxuICogQSBzcGVjaWFsaXplZCB2ZXJzaW9uIG9mIGBiYXNlSXNFcXVhbERlZXBgIGZvciBhcnJheXMgd2l0aCBzdXBwb3J0IGZvclxuICogcGFydGlhbCBkZWVwIGNvbXBhcmlzb25zLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgYXJyYXkgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSB7QXJyYXl9IG90aGVyIFRoZSBvdGhlciBhcnJheSB0byBjb21wYXJlLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZXF1YWxGdW5jIFRoZSBmdW5jdGlvbiB0byBkZXRlcm1pbmUgZXF1aXZhbGVudHMgb2YgdmFsdWVzLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gW2N1c3RvbWl6ZXJdIFRoZSBmdW5jdGlvbiB0byBjdXN0b21pemUgY29tcGFyaW5nIGFycmF5cy5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2lzTG9vc2VdIFNwZWNpZnkgcGVyZm9ybWluZyBwYXJ0aWFsIGNvbXBhcmlzb25zLlxuICogQHBhcmFtIHtBcnJheX0gW3N0YWNrQV0gVHJhY2tzIHRyYXZlcnNlZCBgdmFsdWVgIG9iamVjdHMuXG4gKiBAcGFyYW0ge0FycmF5fSBbc3RhY2tCXSBUcmFja3MgdHJhdmVyc2VkIGBvdGhlcmAgb2JqZWN0cy5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgYXJyYXlzIGFyZSBlcXVpdmFsZW50LCBlbHNlIGBmYWxzZWAuXG4gKi9cbmZ1bmN0aW9uIGVxdWFsQXJyYXlzKGFycmF5LCBvdGhlciwgZXF1YWxGdW5jLCBjdXN0b21pemVyLCBpc0xvb3NlLCBzdGFja0EsIHN0YWNrQikge1xuICB2YXIgaW5kZXggPSAtMSxcbiAgICAgIGFyckxlbmd0aCA9IGFycmF5Lmxlbmd0aCxcbiAgICAgIG90aExlbmd0aCA9IG90aGVyLmxlbmd0aCxcbiAgICAgIHJlc3VsdCA9IHRydWU7XG5cbiAgaWYgKGFyckxlbmd0aCAhPSBvdGhMZW5ndGggJiYgIShpc0xvb3NlICYmIG90aExlbmd0aCA+IGFyckxlbmd0aCkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgLy8gRGVlcCBjb21wYXJlIHRoZSBjb250ZW50cywgaWdub3Jpbmcgbm9uLW51bWVyaWMgcHJvcGVydGllcy5cbiAgd2hpbGUgKHJlc3VsdCAmJiArK2luZGV4IDwgYXJyTGVuZ3RoKSB7XG4gICAgdmFyIGFyclZhbHVlID0gYXJyYXlbaW5kZXhdLFxuICAgICAgICBvdGhWYWx1ZSA9IG90aGVyW2luZGV4XTtcblxuICAgIHJlc3VsdCA9IHVuZGVmaW5lZDtcbiAgICBpZiAoY3VzdG9taXplcikge1xuICAgICAgcmVzdWx0ID0gaXNMb29zZVxuICAgICAgICA/IGN1c3RvbWl6ZXIob3RoVmFsdWUsIGFyclZhbHVlLCBpbmRleClcbiAgICAgICAgOiBjdXN0b21pemVyKGFyclZhbHVlLCBvdGhWYWx1ZSwgaW5kZXgpO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIHJlc3VsdCA9PSAndW5kZWZpbmVkJykge1xuICAgICAgLy8gUmVjdXJzaXZlbHkgY29tcGFyZSBhcnJheXMgKHN1c2NlcHRpYmxlIHRvIGNhbGwgc3RhY2sgbGltaXRzKS5cbiAgICAgIGlmIChpc0xvb3NlKSB7XG4gICAgICAgIHZhciBvdGhJbmRleCA9IG90aExlbmd0aDtcbiAgICAgICAgd2hpbGUgKG90aEluZGV4LS0pIHtcbiAgICAgICAgICBvdGhWYWx1ZSA9IG90aGVyW290aEluZGV4XTtcbiAgICAgICAgICByZXN1bHQgPSAoYXJyVmFsdWUgJiYgYXJyVmFsdWUgPT09IG90aFZhbHVlKSB8fCBlcXVhbEZ1bmMoYXJyVmFsdWUsIG90aFZhbHVlLCBjdXN0b21pemVyLCBpc0xvb3NlLCBzdGFja0EsIHN0YWNrQik7XG4gICAgICAgICAgaWYgKHJlc3VsdCkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgPSAoYXJyVmFsdWUgJiYgYXJyVmFsdWUgPT09IG90aFZhbHVlKSB8fCBlcXVhbEZ1bmMoYXJyVmFsdWUsIG90aFZhbHVlLCBjdXN0b21pemVyLCBpc0xvb3NlLCBzdGFja0EsIHN0YWNrQik7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiAhIXJlc3VsdDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBlcXVhbEFycmF5cztcbiIsIi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBib29sVGFnID0gJ1tvYmplY3QgQm9vbGVhbl0nLFxuICAgIGRhdGVUYWcgPSAnW29iamVjdCBEYXRlXScsXG4gICAgZXJyb3JUYWcgPSAnW29iamVjdCBFcnJvcl0nLFxuICAgIG51bWJlclRhZyA9ICdbb2JqZWN0IE51bWJlcl0nLFxuICAgIHJlZ2V4cFRhZyA9ICdbb2JqZWN0IFJlZ0V4cF0nLFxuICAgIHN0cmluZ1RhZyA9ICdbb2JqZWN0IFN0cmluZ10nO1xuXG4vKipcbiAqIEEgc3BlY2lhbGl6ZWQgdmVyc2lvbiBvZiBgYmFzZUlzRXF1YWxEZWVwYCBmb3IgY29tcGFyaW5nIG9iamVjdHMgb2ZcbiAqIHRoZSBzYW1lIGB0b1N0cmluZ1RhZ2AuXG4gKlxuICogKipOb3RlOioqIFRoaXMgZnVuY3Rpb24gb25seSBzdXBwb3J0cyBjb21wYXJpbmcgdmFsdWVzIHdpdGggdGFncyBvZlxuICogYEJvb2xlYW5gLCBgRGF0ZWAsIGBFcnJvcmAsIGBOdW1iZXJgLCBgUmVnRXhwYCwgb3IgYFN0cmluZ2AuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSB2YWx1ZSBUaGUgb2JqZWN0IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0ge09iamVjdH0gb3RoZXIgVGhlIG90aGVyIG9iamVjdCB0byBjb21wYXJlLlxuICogQHBhcmFtIHtzdHJpbmd9IHRhZyBUaGUgYHRvU3RyaW5nVGFnYCBvZiB0aGUgb2JqZWN0cyB0byBjb21wYXJlLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIHRoZSBvYmplY3RzIGFyZSBlcXVpdmFsZW50LCBlbHNlIGBmYWxzZWAuXG4gKi9cbmZ1bmN0aW9uIGVxdWFsQnlUYWcob2JqZWN0LCBvdGhlciwgdGFnKSB7XG4gIHN3aXRjaCAodGFnKSB7XG4gICAgY2FzZSBib29sVGFnOlxuICAgIGNhc2UgZGF0ZVRhZzpcbiAgICAgIC8vIENvZXJjZSBkYXRlcyBhbmQgYm9vbGVhbnMgdG8gbnVtYmVycywgZGF0ZXMgdG8gbWlsbGlzZWNvbmRzIGFuZCBib29sZWFuc1xuICAgICAgLy8gdG8gYDFgIG9yIGAwYCB0cmVhdGluZyBpbnZhbGlkIGRhdGVzIGNvZXJjZWQgdG8gYE5hTmAgYXMgbm90IGVxdWFsLlxuICAgICAgcmV0dXJuICtvYmplY3QgPT0gK290aGVyO1xuXG4gICAgY2FzZSBlcnJvclRhZzpcbiAgICAgIHJldHVybiBvYmplY3QubmFtZSA9PSBvdGhlci5uYW1lICYmIG9iamVjdC5tZXNzYWdlID09IG90aGVyLm1lc3NhZ2U7XG5cbiAgICBjYXNlIG51bWJlclRhZzpcbiAgICAgIC8vIFRyZWF0IGBOYU5gIHZzLiBgTmFOYCBhcyBlcXVhbC5cbiAgICAgIHJldHVybiAob2JqZWN0ICE9ICtvYmplY3QpXG4gICAgICAgID8gb3RoZXIgIT0gK290aGVyXG4gICAgICAgIC8vIEJ1dCwgdHJlYXQgYC0wYCB2cy4gYCswYCBhcyBub3QgZXF1YWwuXG4gICAgICAgIDogKG9iamVjdCA9PSAwID8gKCgxIC8gb2JqZWN0KSA9PSAoMSAvIG90aGVyKSkgOiBvYmplY3QgPT0gK290aGVyKTtcblxuICAgIGNhc2UgcmVnZXhwVGFnOlxuICAgIGNhc2Ugc3RyaW5nVGFnOlxuICAgICAgLy8gQ29lcmNlIHJlZ2V4ZXMgdG8gc3RyaW5ncyBhbmQgdHJlYXQgc3RyaW5ncyBwcmltaXRpdmVzIGFuZCBzdHJpbmdcbiAgICAgIC8vIG9iamVjdHMgYXMgZXF1YWwuIFNlZSBodHRwczovL2VzNS5naXRodWIuaW8vI3gxNS4xMC42LjQgZm9yIG1vcmUgZGV0YWlscy5cbiAgICAgIHJldHVybiBvYmplY3QgPT0gKG90aGVyICsgJycpO1xuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBlcXVhbEJ5VGFnO1xuIiwidmFyIGtleXMgPSByZXF1aXJlKCcuLi9vYmplY3Qva2V5cycpO1xuXG4vKiogVXNlZCBmb3IgbmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqIFVzZWQgdG8gY2hlY2sgb2JqZWN0cyBmb3Igb3duIHByb3BlcnRpZXMuICovXG52YXIgaGFzT3duUHJvcGVydHkgPSBvYmplY3RQcm90by5oYXNPd25Qcm9wZXJ0eTtcblxuLyoqXG4gKiBBIHNwZWNpYWxpemVkIHZlcnNpb24gb2YgYGJhc2VJc0VxdWFsRGVlcGAgZm9yIG9iamVjdHMgd2l0aCBzdXBwb3J0IGZvclxuICogcGFydGlhbCBkZWVwIGNvbXBhcmlzb25zLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqZWN0IFRoZSBvYmplY3QgdG8gY29tcGFyZS5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvdGhlciBUaGUgb3RoZXIgb2JqZWN0IHRvIGNvbXBhcmUuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlcXVhbEZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGRldGVybWluZSBlcXVpdmFsZW50cyBvZiB2YWx1ZXMuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBbY3VzdG9taXplcl0gVGhlIGZ1bmN0aW9uIHRvIGN1c3RvbWl6ZSBjb21wYXJpbmcgdmFsdWVzLlxuICogQHBhcmFtIHtib29sZWFufSBbaXNMb29zZV0gU3BlY2lmeSBwZXJmb3JtaW5nIHBhcnRpYWwgY29tcGFyaXNvbnMuXG4gKiBAcGFyYW0ge0FycmF5fSBbc3RhY2tBXSBUcmFja3MgdHJhdmVyc2VkIGB2YWx1ZWAgb2JqZWN0cy5cbiAqIEBwYXJhbSB7QXJyYXl9IFtzdGFja0JdIFRyYWNrcyB0cmF2ZXJzZWQgYG90aGVyYCBvYmplY3RzLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIHRoZSBvYmplY3RzIGFyZSBlcXVpdmFsZW50LCBlbHNlIGBmYWxzZWAuXG4gKi9cbmZ1bmN0aW9uIGVxdWFsT2JqZWN0cyhvYmplY3QsIG90aGVyLCBlcXVhbEZ1bmMsIGN1c3RvbWl6ZXIsIGlzTG9vc2UsIHN0YWNrQSwgc3RhY2tCKSB7XG4gIHZhciBvYmpQcm9wcyA9IGtleXMob2JqZWN0KSxcbiAgICAgIG9iakxlbmd0aCA9IG9ialByb3BzLmxlbmd0aCxcbiAgICAgIG90aFByb3BzID0ga2V5cyhvdGhlciksXG4gICAgICBvdGhMZW5ndGggPSBvdGhQcm9wcy5sZW5ndGg7XG5cbiAgaWYgKG9iakxlbmd0aCAhPSBvdGhMZW5ndGggJiYgIWlzTG9vc2UpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgdmFyIHNraXBDdG9yID0gaXNMb29zZSxcbiAgICAgIGluZGV4ID0gLTE7XG5cbiAgd2hpbGUgKCsraW5kZXggPCBvYmpMZW5ndGgpIHtcbiAgICB2YXIga2V5ID0gb2JqUHJvcHNbaW5kZXhdLFxuICAgICAgICByZXN1bHQgPSBpc0xvb3NlID8ga2V5IGluIG90aGVyIDogaGFzT3duUHJvcGVydHkuY2FsbChvdGhlciwga2V5KTtcblxuICAgIGlmIChyZXN1bHQpIHtcbiAgICAgIHZhciBvYmpWYWx1ZSA9IG9iamVjdFtrZXldLFxuICAgICAgICAgIG90aFZhbHVlID0gb3RoZXJba2V5XTtcblxuICAgICAgcmVzdWx0ID0gdW5kZWZpbmVkO1xuICAgICAgaWYgKGN1c3RvbWl6ZXIpIHtcbiAgICAgICAgcmVzdWx0ID0gaXNMb29zZVxuICAgICAgICAgID8gY3VzdG9taXplcihvdGhWYWx1ZSwgb2JqVmFsdWUsIGtleSlcbiAgICAgICAgICA6IGN1c3RvbWl6ZXIob2JqVmFsdWUsIG90aFZhbHVlLCBrZXkpO1xuICAgICAgfVxuICAgICAgaWYgKHR5cGVvZiByZXN1bHQgPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgLy8gUmVjdXJzaXZlbHkgY29tcGFyZSBvYmplY3RzIChzdXNjZXB0aWJsZSB0byBjYWxsIHN0YWNrIGxpbWl0cykuXG4gICAgICAgIHJlc3VsdCA9IChvYmpWYWx1ZSAmJiBvYmpWYWx1ZSA9PT0gb3RoVmFsdWUpIHx8IGVxdWFsRnVuYyhvYmpWYWx1ZSwgb3RoVmFsdWUsIGN1c3RvbWl6ZXIsIGlzTG9vc2UsIHN0YWNrQSwgc3RhY2tCKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgc2tpcEN0b3IgfHwgKHNraXBDdG9yID0ga2V5ID09ICdjb25zdHJ1Y3RvcicpO1xuICB9XG4gIGlmICghc2tpcEN0b3IpIHtcbiAgICB2YXIgb2JqQ3RvciA9IG9iamVjdC5jb25zdHJ1Y3RvcixcbiAgICAgICAgb3RoQ3RvciA9IG90aGVyLmNvbnN0cnVjdG9yO1xuXG4gICAgLy8gTm9uIGBPYmplY3RgIG9iamVjdCBpbnN0YW5jZXMgd2l0aCBkaWZmZXJlbnQgY29uc3RydWN0b3JzIGFyZSBub3QgZXF1YWwuXG4gICAgaWYgKG9iakN0b3IgIT0gb3RoQ3RvciAmJlxuICAgICAgICAoJ2NvbnN0cnVjdG9yJyBpbiBvYmplY3QgJiYgJ2NvbnN0cnVjdG9yJyBpbiBvdGhlcikgJiZcbiAgICAgICAgISh0eXBlb2Ygb2JqQ3RvciA9PSAnZnVuY3Rpb24nICYmIG9iakN0b3IgaW5zdGFuY2VvZiBvYmpDdG9yICYmXG4gICAgICAgICAgdHlwZW9mIG90aEN0b3IgPT0gJ2Z1bmN0aW9uJyAmJiBvdGhDdG9yIGluc3RhbmNlb2Ygb3RoQ3RvcikpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZXF1YWxPYmplY3RzO1xuIiwidmFyIG1ldGFNYXAgPSByZXF1aXJlKCcuL21ldGFNYXAnKSxcbiAgICBub29wID0gcmVxdWlyZSgnLi4vdXRpbGl0eS9ub29wJyk7XG5cbi8qKlxuICogR2V0cyBtZXRhZGF0YSBmb3IgYGZ1bmNgLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBxdWVyeS5cbiAqIEByZXR1cm5zIHsqfSBSZXR1cm5zIHRoZSBtZXRhZGF0YSBmb3IgYGZ1bmNgLlxuICovXG52YXIgZ2V0RGF0YSA9ICFtZXRhTWFwID8gbm9vcCA6IGZ1bmN0aW9uKGZ1bmMpIHtcbiAgcmV0dXJuIG1ldGFNYXAuZ2V0KGZ1bmMpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBnZXREYXRhO1xuIiwidmFyIGJhc2VQcm9wZXJ0eSA9IHJlcXVpcmUoJy4vYmFzZVByb3BlcnR5JyksXG4gICAgY29uc3RhbnQgPSByZXF1aXJlKCcuLi91dGlsaXR5L2NvbnN0YW50JyksXG4gICAgcmVhbE5hbWVzID0gcmVxdWlyZSgnLi9yZWFsTmFtZXMnKSxcbiAgICBzdXBwb3J0ID0gcmVxdWlyZSgnLi4vc3VwcG9ydCcpO1xuXG4vKipcbiAqIEdldHMgdGhlIG5hbWUgb2YgYGZ1bmNgLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBxdWVyeS5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IFJldHVybnMgdGhlIGZ1bmN0aW9uIG5hbWUuXG4gKi9cbnZhciBnZXRGdW5jTmFtZSA9IChmdW5jdGlvbigpIHtcbiAgaWYgKCFzdXBwb3J0LmZ1bmNOYW1lcykge1xuICAgIHJldHVybiBjb25zdGFudCgnJyk7XG4gIH1cbiAgaWYgKGNvbnN0YW50Lm5hbWUgPT0gJ2NvbnN0YW50Jykge1xuICAgIHJldHVybiBiYXNlUHJvcGVydHkoJ25hbWUnKTtcbiAgfVxuICByZXR1cm4gZnVuY3Rpb24oZnVuYykge1xuICAgIHZhciByZXN1bHQgPSBmdW5jLm5hbWUsXG4gICAgICAgIGFycmF5ID0gcmVhbE5hbWVzW3Jlc3VsdF0sXG4gICAgICAgIGxlbmd0aCA9IGFycmF5ID8gYXJyYXkubGVuZ3RoIDogMDtcblxuICAgIHdoaWxlIChsZW5ndGgtLSkge1xuICAgICAgdmFyIGRhdGEgPSBhcnJheVtsZW5ndGhdLFxuICAgICAgICAgIG90aGVyRnVuYyA9IGRhdGEuZnVuYztcblxuICAgICAgaWYgKG90aGVyRnVuYyA9PSBudWxsIHx8IG90aGVyRnVuYyA9PSBmdW5jKSB7XG4gICAgICAgIHJldHVybiBkYXRhLm5hbWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG59KCkpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGdldEZ1bmNOYW1lO1xuIiwiLyoqXG4gKiBHZXRzIHRoZSBpbmRleCBhdCB3aGljaCB0aGUgZmlyc3Qgb2NjdXJyZW5jZSBvZiBgTmFOYCBpcyBmb3VuZCBpbiBgYXJyYXlgLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgYXJyYXkgdG8gc2VhcmNoLlxuICogQHBhcmFtIHtudW1iZXJ9IGZyb21JbmRleCBUaGUgaW5kZXggdG8gc2VhcmNoIGZyb20uXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtmcm9tUmlnaHRdIFNwZWNpZnkgaXRlcmF0aW5nIGZyb20gcmlnaHQgdG8gbGVmdC5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IFJldHVybnMgdGhlIGluZGV4IG9mIHRoZSBtYXRjaGVkIGBOYU5gLCBlbHNlIGAtMWAuXG4gKi9cbmZ1bmN0aW9uIGluZGV4T2ZOYU4oYXJyYXksIGZyb21JbmRleCwgZnJvbVJpZ2h0KSB7XG4gIHZhciBsZW5ndGggPSBhcnJheS5sZW5ndGgsXG4gICAgICBpbmRleCA9IGZyb21JbmRleCArIChmcm9tUmlnaHQgPyAwIDogLTEpO1xuXG4gIHdoaWxlICgoZnJvbVJpZ2h0ID8gaW5kZXgtLSA6ICsraW5kZXggPCBsZW5ndGgpKSB7XG4gICAgdmFyIG90aGVyID0gYXJyYXlbaW5kZXhdO1xuICAgIGlmIChvdGhlciAhPT0gb3RoZXIpIHtcbiAgICAgIHJldHVybiBpbmRleDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIC0xO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGluZGV4T2ZOYU47XG4iLCIvKiogVXNlZCBmb3IgbmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqIFVzZWQgdG8gY2hlY2sgb2JqZWN0cyBmb3Igb3duIHByb3BlcnRpZXMuICovXG52YXIgaGFzT3duUHJvcGVydHkgPSBvYmplY3RQcm90by5oYXNPd25Qcm9wZXJ0eTtcblxuLyoqXG4gKiBJbml0aWFsaXplcyBhbiBhcnJheSBjbG9uZS5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheX0gYXJyYXkgVGhlIGFycmF5IHRvIGNsb25lLlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIHRoZSBpbml0aWFsaXplZCBjbG9uZS5cbiAqL1xuZnVuY3Rpb24gaW5pdENsb25lQXJyYXkoYXJyYXkpIHtcbiAgdmFyIGxlbmd0aCA9IGFycmF5Lmxlbmd0aCxcbiAgICAgIHJlc3VsdCA9IG5ldyBhcnJheS5jb25zdHJ1Y3RvcihsZW5ndGgpO1xuXG4gIC8vIEFkZCBhcnJheSBwcm9wZXJ0aWVzIGFzc2lnbmVkIGJ5IGBSZWdFeHAjZXhlY2AuXG4gIGlmIChsZW5ndGggJiYgdHlwZW9mIGFycmF5WzBdID09ICdzdHJpbmcnICYmIGhhc093blByb3BlcnR5LmNhbGwoYXJyYXksICdpbmRleCcpKSB7XG4gICAgcmVzdWx0LmluZGV4ID0gYXJyYXkuaW5kZXg7XG4gICAgcmVzdWx0LmlucHV0ID0gYXJyYXkuaW5wdXQ7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBpbml0Q2xvbmVBcnJheTtcbiIsInZhciBidWZmZXJDbG9uZSA9IHJlcXVpcmUoJy4vYnVmZmVyQ2xvbmUnKTtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIGJvb2xUYWcgPSAnW29iamVjdCBCb29sZWFuXScsXG4gICAgZGF0ZVRhZyA9ICdbb2JqZWN0IERhdGVdJyxcbiAgICBudW1iZXJUYWcgPSAnW29iamVjdCBOdW1iZXJdJyxcbiAgICByZWdleHBUYWcgPSAnW29iamVjdCBSZWdFeHBdJyxcbiAgICBzdHJpbmdUYWcgPSAnW29iamVjdCBTdHJpbmddJztcblxudmFyIGFycmF5QnVmZmVyVGFnID0gJ1tvYmplY3QgQXJyYXlCdWZmZXJdJyxcbiAgICBmbG9hdDMyVGFnID0gJ1tvYmplY3QgRmxvYXQzMkFycmF5XScsXG4gICAgZmxvYXQ2NFRhZyA9ICdbb2JqZWN0IEZsb2F0NjRBcnJheV0nLFxuICAgIGludDhUYWcgPSAnW29iamVjdCBJbnQ4QXJyYXldJyxcbiAgICBpbnQxNlRhZyA9ICdbb2JqZWN0IEludDE2QXJyYXldJyxcbiAgICBpbnQzMlRhZyA9ICdbb2JqZWN0IEludDMyQXJyYXldJyxcbiAgICB1aW50OFRhZyA9ICdbb2JqZWN0IFVpbnQ4QXJyYXldJyxcbiAgICB1aW50OENsYW1wZWRUYWcgPSAnW29iamVjdCBVaW50OENsYW1wZWRBcnJheV0nLFxuICAgIHVpbnQxNlRhZyA9ICdbb2JqZWN0IFVpbnQxNkFycmF5XScsXG4gICAgdWludDMyVGFnID0gJ1tvYmplY3QgVWludDMyQXJyYXldJztcblxuLyoqIFVzZWQgdG8gbWF0Y2ggYFJlZ0V4cGAgZmxhZ3MgZnJvbSB0aGVpciBjb2VyY2VkIHN0cmluZyB2YWx1ZXMuICovXG52YXIgcmVGbGFncyA9IC9cXHcqJC87XG5cbi8qKiBVc2VkIHRvIGxvb2t1cCBhIHR5cGUgYXJyYXkgY29uc3RydWN0b3JzIGJ5IGB0b1N0cmluZ1RhZ2AuICovXG52YXIgY3RvckJ5VGFnID0ge307XG5jdG9yQnlUYWdbZmxvYXQzMlRhZ10gPSBnbG9iYWwuRmxvYXQzMkFycmF5O1xuY3RvckJ5VGFnW2Zsb2F0NjRUYWddID0gZ2xvYmFsLkZsb2F0NjRBcnJheTtcbmN0b3JCeVRhZ1tpbnQ4VGFnXSA9IGdsb2JhbC5JbnQ4QXJyYXk7XG5jdG9yQnlUYWdbaW50MTZUYWddID0gZ2xvYmFsLkludDE2QXJyYXk7XG5jdG9yQnlUYWdbaW50MzJUYWddID0gZ2xvYmFsLkludDMyQXJyYXk7XG5jdG9yQnlUYWdbdWludDhUYWddID0gZ2xvYmFsLlVpbnQ4QXJyYXk7XG5jdG9yQnlUYWdbdWludDhDbGFtcGVkVGFnXSA9IGdsb2JhbC5VaW50OENsYW1wZWRBcnJheTtcbmN0b3JCeVRhZ1t1aW50MTZUYWddID0gZ2xvYmFsLlVpbnQxNkFycmF5O1xuY3RvckJ5VGFnW3VpbnQzMlRhZ10gPSBnbG9iYWwuVWludDMyQXJyYXk7XG5cbi8qKlxuICogSW5pdGlhbGl6ZXMgYW4gb2JqZWN0IGNsb25lIGJhc2VkIG9uIGl0cyBgdG9TdHJpbmdUYWdgLlxuICpcbiAqICoqTm90ZToqKiBUaGlzIGZ1bmN0aW9uIG9ubHkgc3VwcG9ydHMgY2xvbmluZyB2YWx1ZXMgd2l0aCB0YWdzIG9mXG4gKiBgQm9vbGVhbmAsIGBEYXRlYCwgYEVycm9yYCwgYE51bWJlcmAsIGBSZWdFeHBgLCBvciBgU3RyaW5nYC5cbiAqXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3QgVGhlIG9iamVjdCB0byBjbG9uZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSB0YWcgVGhlIGB0b1N0cmluZ1RhZ2Agb2YgdGhlIG9iamVjdCB0byBjbG9uZS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2lzRGVlcF0gU3BlY2lmeSBhIGRlZXAgY2xvbmUuXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIHRoZSBpbml0aWFsaXplZCBjbG9uZS5cbiAqL1xuZnVuY3Rpb24gaW5pdENsb25lQnlUYWcob2JqZWN0LCB0YWcsIGlzRGVlcCkge1xuICB2YXIgQ3RvciA9IG9iamVjdC5jb25zdHJ1Y3RvcjtcbiAgc3dpdGNoICh0YWcpIHtcbiAgICBjYXNlIGFycmF5QnVmZmVyVGFnOlxuICAgICAgcmV0dXJuIGJ1ZmZlckNsb25lKG9iamVjdCk7XG5cbiAgICBjYXNlIGJvb2xUYWc6XG4gICAgY2FzZSBkYXRlVGFnOlxuICAgICAgcmV0dXJuIG5ldyBDdG9yKCtvYmplY3QpO1xuXG4gICAgY2FzZSBmbG9hdDMyVGFnOiBjYXNlIGZsb2F0NjRUYWc6XG4gICAgY2FzZSBpbnQ4VGFnOiBjYXNlIGludDE2VGFnOiBjYXNlIGludDMyVGFnOlxuICAgIGNhc2UgdWludDhUYWc6IGNhc2UgdWludDhDbGFtcGVkVGFnOiBjYXNlIHVpbnQxNlRhZzogY2FzZSB1aW50MzJUYWc6XG4gICAgICAvLyBTYWZhcmkgNSBtb2JpbGUgaW5jb3JyZWN0bHkgaGFzIGBPYmplY3RgIGFzIHRoZSBjb25zdHJ1Y3RvciBvZiB0eXBlZCBhcnJheXMuXG4gICAgICBpZiAoQ3RvciBpbnN0YW5jZW9mIEN0b3IpIHtcbiAgICAgICAgQ3RvciA9IGN0b3JCeVRhZ1t0YWddO1xuICAgICAgfVxuICAgICAgdmFyIGJ1ZmZlciA9IG9iamVjdC5idWZmZXI7XG4gICAgICByZXR1cm4gbmV3IEN0b3IoaXNEZWVwID8gYnVmZmVyQ2xvbmUoYnVmZmVyKSA6IGJ1ZmZlciwgb2JqZWN0LmJ5dGVPZmZzZXQsIG9iamVjdC5sZW5ndGgpO1xuXG4gICAgY2FzZSBudW1iZXJUYWc6XG4gICAgY2FzZSBzdHJpbmdUYWc6XG4gICAgICByZXR1cm4gbmV3IEN0b3Iob2JqZWN0KTtcblxuICAgIGNhc2UgcmVnZXhwVGFnOlxuICAgICAgdmFyIHJlc3VsdCA9IG5ldyBDdG9yKG9iamVjdC5zb3VyY2UsIHJlRmxhZ3MuZXhlYyhvYmplY3QpKTtcbiAgICAgIHJlc3VsdC5sYXN0SW5kZXggPSBvYmplY3QubGFzdEluZGV4O1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaW5pdENsb25lQnlUYWc7XG4iLCIvKipcbiAqIEluaXRpYWxpemVzIGFuIG9iamVjdCBjbG9uZS5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtPYmplY3R9IG9iamVjdCBUaGUgb2JqZWN0IHRvIGNsb25lLlxuICogQHJldHVybnMge09iamVjdH0gUmV0dXJucyB0aGUgaW5pdGlhbGl6ZWQgY2xvbmUuXG4gKi9cbmZ1bmN0aW9uIGluaXRDbG9uZU9iamVjdChvYmplY3QpIHtcbiAgdmFyIEN0b3IgPSBvYmplY3QuY29uc3RydWN0b3I7XG4gIGlmICghKHR5cGVvZiBDdG9yID09ICdmdW5jdGlvbicgJiYgQ3RvciBpbnN0YW5jZW9mIEN0b3IpKSB7XG4gICAgQ3RvciA9IE9iamVjdDtcbiAgfVxuICByZXR1cm4gbmV3IEN0b3I7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaW5pdENsb25lT2JqZWN0O1xuIiwiLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBhIGhvc3Qgb2JqZWN0IGluIElFIDwgOS5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIGhvc3Qgb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKi9cbnZhciBpc0hvc3RPYmplY3QgPSAoZnVuY3Rpb24oKSB7XG4gIHRyeSB7XG4gICAgT2JqZWN0KHsgJ3RvU3RyaW5nJzogMCB9ICsgJycpO1xuICB9IGNhdGNoKGUpIHtcbiAgICByZXR1cm4gZnVuY3Rpb24oKSB7IHJldHVybiBmYWxzZTsgfTtcbiAgfVxuICByZXR1cm4gZnVuY3Rpb24odmFsdWUpIHtcbiAgICAvLyBJRSA8IDkgcHJlc2VudHMgbWFueSBob3N0IG9iamVjdHMgYXMgYE9iamVjdGAgb2JqZWN0cyB0aGF0IGNhbiBjb2VyY2VcbiAgICAvLyB0byBzdHJpbmdzIGRlc3BpdGUgaGF2aW5nIGltcHJvcGVybHkgZGVmaW5lZCBgdG9TdHJpbmdgIG1ldGhvZHMuXG4gICAgcmV0dXJuIHR5cGVvZiB2YWx1ZS50b1N0cmluZyAhPSAnZnVuY3Rpb24nICYmIHR5cGVvZiAodmFsdWUgKyAnJykgPT0gJ3N0cmluZyc7XG4gIH07XG59KCkpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGlzSG9zdE9iamVjdDtcbiIsIi8qKlxuICogVXNlZCBhcyB0aGUgW21heGltdW0gbGVuZ3RoXShodHRwczovL3Blb3BsZS5tb3ppbGxhLm9yZy9+am9yZW5kb3JmZi9lczYtZHJhZnQuaHRtbCNzZWMtbnVtYmVyLm1heF9zYWZlX2ludGVnZXIpXG4gKiBvZiBhbiBhcnJheS1saWtlIHZhbHVlLlxuICovXG52YXIgTUFYX1NBRkVfSU5URUdFUiA9IE1hdGgucG93KDIsIDUzKSAtIDE7XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgYSB2YWxpZCBhcnJheS1saWtlIGluZGV4LlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEBwYXJhbSB7bnVtYmVyfSBbbGVuZ3RoPU1BWF9TQUZFX0lOVEVHRVJdIFRoZSB1cHBlciBib3VuZHMgb2YgYSB2YWxpZCBpbmRleC5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGEgdmFsaWQgaW5kZXgsIGVsc2UgYGZhbHNlYC5cbiAqL1xuZnVuY3Rpb24gaXNJbmRleCh2YWx1ZSwgbGVuZ3RoKSB7XG4gIHZhbHVlID0gK3ZhbHVlO1xuICBsZW5ndGggPSBsZW5ndGggPT0gbnVsbCA/IE1BWF9TQUZFX0lOVEVHRVIgOiBsZW5ndGg7XG4gIHJldHVybiB2YWx1ZSA+IC0xICYmIHZhbHVlICUgMSA9PSAwICYmIHZhbHVlIDwgbGVuZ3RoO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGlzSW5kZXg7XG4iLCJ2YXIgTGF6eVdyYXBwZXIgPSByZXF1aXJlKCcuL0xhenlXcmFwcGVyJyksXG4gICAgZ2V0RnVuY05hbWUgPSByZXF1aXJlKCcuL2dldEZ1bmNOYW1lJyksXG4gICAgbG9kYXNoID0gcmVxdWlyZSgnLi4vY2hhaW4vbG9kYXNoJyk7XG5cbi8qKlxuICogQ2hlY2tzIGlmIGBmdW5jYCBoYXMgYSBsYXp5IGNvdW50ZXJwYXJ0LlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmdW5jIFRoZSBmdW5jdGlvbiB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgZnVuY2AgaGFzIGEgbGF6eSBjb3VudGVycGFydCwgZWxzZSBgZmFsc2VgLlxuICovXG5mdW5jdGlvbiBpc0xhemlhYmxlKGZ1bmMpIHtcbiAgdmFyIGZ1bmNOYW1lID0gZ2V0RnVuY05hbWUoZnVuYyk7XG4gIHJldHVybiAhIWZ1bmNOYW1lICYmIGZ1bmMgPT09IGxvZGFzaFtmdW5jTmFtZV0gJiYgZnVuY05hbWUgaW4gTGF6eVdyYXBwZXIucHJvdG90eXBlO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGlzTGF6aWFibGU7XG4iLCIvKipcbiAqIFVzZWQgYXMgdGhlIFttYXhpbXVtIGxlbmd0aF0oaHR0cHM6Ly9wZW9wbGUubW96aWxsYS5vcmcvfmpvcmVuZG9yZmYvZXM2LWRyYWZ0Lmh0bWwjc2VjLW51bWJlci5tYXhfc2FmZV9pbnRlZ2VyKVxuICogb2YgYW4gYXJyYXktbGlrZSB2YWx1ZS5cbiAqL1xudmFyIE1BWF9TQUZFX0lOVEVHRVIgPSBNYXRoLnBvdygyLCA1MykgLSAxO1xuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGEgdmFsaWQgYXJyYXktbGlrZSBsZW5ndGguXG4gKlxuICogKipOb3RlOioqIFRoaXMgZnVuY3Rpb24gaXMgYmFzZWQgb24gW2BUb0xlbmd0aGBdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy10b2xlbmd0aCkuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYSB2YWxpZCBsZW5ndGgsIGVsc2UgYGZhbHNlYC5cbiAqL1xuZnVuY3Rpb24gaXNMZW5ndGgodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PSAnbnVtYmVyJyAmJiB2YWx1ZSA+IC0xICYmIHZhbHVlICUgMSA9PSAwICYmIHZhbHVlIDw9IE1BWF9TQUZFX0lOVEVHRVI7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaXNMZW5ndGg7XG4iLCIvKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0TGlrZSh2YWx1ZSkge1xuICByZXR1cm4gISF2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT0gJ29iamVjdCc7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaXNPYmplY3RMaWtlO1xuIiwidmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi4vbGFuZy9pc09iamVjdCcpO1xuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIHN1aXRhYmxlIGZvciBzdHJpY3QgZXF1YWxpdHkgY29tcGFyaXNvbnMsIGkuZS4gYD09PWAuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaWYgc3VpdGFibGUgZm9yIHN0cmljdFxuICogIGVxdWFsaXR5IGNvbXBhcmlzb25zLCBlbHNlIGBmYWxzZWAuXG4gKi9cbmZ1bmN0aW9uIGlzU3RyaWN0Q29tcGFyYWJsZSh2YWx1ZSkge1xuICByZXR1cm4gdmFsdWUgPT09IHZhbHVlICYmICh2YWx1ZSA9PT0gMCA/ICgoMSAvIHZhbHVlKSA+IDApIDogIWlzT2JqZWN0KHZhbHVlKSk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaXNTdHJpY3RDb21wYXJhYmxlO1xuIiwidmFyIGFycmF5Q29weSA9IHJlcXVpcmUoJy4vYXJyYXlDb3B5JyksXG4gICAgY29tcG9zZUFyZ3MgPSByZXF1aXJlKCcuL2NvbXBvc2VBcmdzJyksXG4gICAgY29tcG9zZUFyZ3NSaWdodCA9IHJlcXVpcmUoJy4vY29tcG9zZUFyZ3NSaWdodCcpLFxuICAgIHJlcGxhY2VIb2xkZXJzID0gcmVxdWlyZSgnLi9yZXBsYWNlSG9sZGVycycpO1xuXG4vKiogVXNlZCB0byBjb21wb3NlIGJpdG1hc2tzIGZvciB3cmFwcGVyIG1ldGFkYXRhLiAqL1xudmFyIEJJTkRfRkxBRyA9IDEsXG4gICAgQ1VSUllfQk9VTkRfRkxBRyA9IDQsXG4gICAgQ1VSUllfRkxBRyA9IDgsXG4gICAgQVJZX0ZMQUcgPSAxMjgsXG4gICAgUkVBUkdfRkxBRyA9IDI1NjtcblxuLyoqIFVzZWQgYXMgdGhlIGludGVybmFsIGFyZ3VtZW50IHBsYWNlaG9sZGVyLiAqL1xudmFyIFBMQUNFSE9MREVSID0gJ19fbG9kYXNoX3BsYWNlaG9sZGVyX18nO1xuXG4vKiBOYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZU1pbiA9IE1hdGgubWluO1xuXG4vKipcbiAqIE1lcmdlcyB0aGUgZnVuY3Rpb24gbWV0YWRhdGEgb2YgYHNvdXJjZWAgaW50byBgZGF0YWAuXG4gKlxuICogTWVyZ2luZyBtZXRhZGF0YSByZWR1Y2VzIHRoZSBudW1iZXIgb2Ygd3JhcHBlcnMgcmVxdWlyZWQgdG8gaW52b2tlIGEgZnVuY3Rpb24uXG4gKiBUaGlzIGlzIHBvc3NpYmxlIGJlY2F1c2UgbWV0aG9kcyBsaWtlIGBfLmJpbmRgLCBgXy5jdXJyeWAsIGFuZCBgXy5wYXJ0aWFsYFxuICogbWF5IGJlIGFwcGxpZWQgcmVnYXJkbGVzcyBvZiBleGVjdXRpb24gb3JkZXIuIE1ldGhvZHMgbGlrZSBgXy5hcnlgIGFuZCBgXy5yZWFyZ2BcbiAqIGF1Z21lbnQgZnVuY3Rpb24gYXJndW1lbnRzLCBtYWtpbmcgdGhlIG9yZGVyIGluIHdoaWNoIHRoZXkgYXJlIGV4ZWN1dGVkIGltcG9ydGFudCxcbiAqIHByZXZlbnRpbmcgdGhlIG1lcmdpbmcgb2YgbWV0YWRhdGEuIEhvd2V2ZXIsIHdlIG1ha2UgYW4gZXhjZXB0aW9uIGZvciBhIHNhZmVcbiAqIGNvbW1vbiBjYXNlIHdoZXJlIGN1cnJpZWQgZnVuY3Rpb25zIGhhdmUgYF8uYXJ5YCBhbmQgb3IgYF8ucmVhcmdgIGFwcGxpZWQuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7QXJyYXl9IGRhdGEgVGhlIGRlc3RpbmF0aW9uIG1ldGFkYXRhLlxuICogQHBhcmFtIHtBcnJheX0gc291cmNlIFRoZSBzb3VyY2UgbWV0YWRhdGEuXG4gKiBAcmV0dXJucyB7QXJyYXl9IFJldHVybnMgYGRhdGFgLlxuICovXG5mdW5jdGlvbiBtZXJnZURhdGEoZGF0YSwgc291cmNlKSB7XG4gIHZhciBiaXRtYXNrID0gZGF0YVsxXSxcbiAgICAgIHNyY0JpdG1hc2sgPSBzb3VyY2VbMV0sXG4gICAgICBuZXdCaXRtYXNrID0gYml0bWFzayB8IHNyY0JpdG1hc2ssXG4gICAgICBpc0NvbW1vbiA9IG5ld0JpdG1hc2sgPCBBUllfRkxBRztcblxuICB2YXIgaXNDb21ibyA9XG4gICAgKHNyY0JpdG1hc2sgPT0gQVJZX0ZMQUcgJiYgYml0bWFzayA9PSBDVVJSWV9GTEFHKSB8fFxuICAgIChzcmNCaXRtYXNrID09IEFSWV9GTEFHICYmIGJpdG1hc2sgPT0gUkVBUkdfRkxBRyAmJiBkYXRhWzddLmxlbmd0aCA8PSBzb3VyY2VbOF0pIHx8XG4gICAgKHNyY0JpdG1hc2sgPT0gKEFSWV9GTEFHIHwgUkVBUkdfRkxBRykgJiYgYml0bWFzayA9PSBDVVJSWV9GTEFHKTtcblxuICAvLyBFeGl0IGVhcmx5IGlmIG1ldGFkYXRhIGNhbid0IGJlIG1lcmdlZC5cbiAgaWYgKCEoaXNDb21tb24gfHwgaXNDb21ibykpIHtcbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuICAvLyBVc2Ugc291cmNlIGB0aGlzQXJnYCBpZiBhdmFpbGFibGUuXG4gIGlmIChzcmNCaXRtYXNrICYgQklORF9GTEFHKSB7XG4gICAgZGF0YVsyXSA9IHNvdXJjZVsyXTtcbiAgICAvLyBTZXQgd2hlbiBjdXJyeWluZyBhIGJvdW5kIGZ1bmN0aW9uLlxuICAgIG5ld0JpdG1hc2sgfD0gKGJpdG1hc2sgJiBCSU5EX0ZMQUcpID8gMCA6IENVUlJZX0JPVU5EX0ZMQUc7XG4gIH1cbiAgLy8gQ29tcG9zZSBwYXJ0aWFsIGFyZ3VtZW50cy5cbiAgdmFyIHZhbHVlID0gc291cmNlWzNdO1xuICBpZiAodmFsdWUpIHtcbiAgICB2YXIgcGFydGlhbHMgPSBkYXRhWzNdO1xuICAgIGRhdGFbM10gPSBwYXJ0aWFscyA/IGNvbXBvc2VBcmdzKHBhcnRpYWxzLCB2YWx1ZSwgc291cmNlWzRdKSA6IGFycmF5Q29weSh2YWx1ZSk7XG4gICAgZGF0YVs0XSA9IHBhcnRpYWxzID8gcmVwbGFjZUhvbGRlcnMoZGF0YVszXSwgUExBQ0VIT0xERVIpIDogYXJyYXlDb3B5KHNvdXJjZVs0XSk7XG4gIH1cbiAgLy8gQ29tcG9zZSBwYXJ0aWFsIHJpZ2h0IGFyZ3VtZW50cy5cbiAgdmFsdWUgPSBzb3VyY2VbNV07XG4gIGlmICh2YWx1ZSkge1xuICAgIHBhcnRpYWxzID0gZGF0YVs1XTtcbiAgICBkYXRhWzVdID0gcGFydGlhbHMgPyBjb21wb3NlQXJnc1JpZ2h0KHBhcnRpYWxzLCB2YWx1ZSwgc291cmNlWzZdKSA6IGFycmF5Q29weSh2YWx1ZSk7XG4gICAgZGF0YVs2XSA9IHBhcnRpYWxzID8gcmVwbGFjZUhvbGRlcnMoZGF0YVs1XSwgUExBQ0VIT0xERVIpIDogYXJyYXlDb3B5KHNvdXJjZVs2XSk7XG4gIH1cbiAgLy8gVXNlIHNvdXJjZSBgYXJnUG9zYCBpZiBhdmFpbGFibGUuXG4gIHZhbHVlID0gc291cmNlWzddO1xuICBpZiAodmFsdWUpIHtcbiAgICBkYXRhWzddID0gYXJyYXlDb3B5KHZhbHVlKTtcbiAgfVxuICAvLyBVc2Ugc291cmNlIGBhcnlgIGlmIGl0J3Mgc21hbGxlci5cbiAgaWYgKHNyY0JpdG1hc2sgJiBBUllfRkxBRykge1xuICAgIGRhdGFbOF0gPSBkYXRhWzhdID09IG51bGwgPyBzb3VyY2VbOF0gOiBuYXRpdmVNaW4oZGF0YVs4XSwgc291cmNlWzhdKTtcbiAgfVxuICAvLyBVc2Ugc291cmNlIGBhcml0eWAgaWYgb25lIGlzIG5vdCBwcm92aWRlZC5cbiAgaWYgKGRhdGFbOV0gPT0gbnVsbCkge1xuICAgIGRhdGFbOV0gPSBzb3VyY2VbOV07XG4gIH1cbiAgLy8gVXNlIHNvdXJjZSBgZnVuY2AgYW5kIG1lcmdlIGJpdG1hc2tzLlxuICBkYXRhWzBdID0gc291cmNlWzBdO1xuICBkYXRhWzFdID0gbmV3Qml0bWFzaztcblxuICByZXR1cm4gZGF0YTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBtZXJnZURhdGE7XG4iLCJ2YXIgaXNOYXRpdmUgPSByZXF1aXJlKCcuLi9sYW5nL2lzTmF0aXZlJyk7XG5cbi8qKiBOYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgV2Vha01hcCA9IGlzTmF0aXZlKFdlYWtNYXAgPSBnbG9iYWwuV2Vha01hcCkgJiYgV2Vha01hcDtcblxuLyoqIFVzZWQgdG8gc3RvcmUgZnVuY3Rpb24gbWV0YWRhdGEuICovXG52YXIgbWV0YU1hcCA9IFdlYWtNYXAgJiYgbmV3IFdlYWtNYXA7XG5cbm1vZHVsZS5leHBvcnRzID0gbWV0YU1hcDtcbiIsIi8qKiBVc2VkIHRvIGxvb2t1cCB1bm1pbmlmaWVkIGZ1bmN0aW9uIG5hbWVzLiAqL1xudmFyIHJlYWxOYW1lcyA9IHt9O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHJlYWxOYW1lcztcbiIsInZhciBhcnJheUNvcHkgPSByZXF1aXJlKCcuL2FycmF5Q29weScpLFxuICAgIGlzSW5kZXggPSByZXF1aXJlKCcuL2lzSW5kZXgnKTtcblxuLyogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBSZW9yZGVyIGBhcnJheWAgYWNjb3JkaW5nIHRvIHRoZSBzcGVjaWZpZWQgaW5kZXhlcyB3aGVyZSB0aGUgZWxlbWVudCBhdFxuICogdGhlIGZpcnN0IGluZGV4IGlzIGFzc2lnbmVkIGFzIHRoZSBmaXJzdCBlbGVtZW50LCB0aGUgZWxlbWVudCBhdFxuICogdGhlIHNlY29uZCBpbmRleCBpcyBhc3NpZ25lZCBhcyB0aGUgc2Vjb25kIGVsZW1lbnQsIGFuZCBzbyBvbi5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtBcnJheX0gYXJyYXkgVGhlIGFycmF5IHRvIHJlb3JkZXIuXG4gKiBAcGFyYW0ge0FycmF5fSBpbmRleGVzIFRoZSBhcnJhbmdlZCBhcnJheSBpbmRleGVzLlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIGBhcnJheWAuXG4gKi9cbmZ1bmN0aW9uIHJlb3JkZXIoYXJyYXksIGluZGV4ZXMpIHtcbiAgdmFyIGFyckxlbmd0aCA9IGFycmF5Lmxlbmd0aCxcbiAgICAgIGxlbmd0aCA9IG5hdGl2ZU1pbihpbmRleGVzLmxlbmd0aCwgYXJyTGVuZ3RoKSxcbiAgICAgIG9sZEFycmF5ID0gYXJyYXlDb3B5KGFycmF5KTtcblxuICB3aGlsZSAobGVuZ3RoLS0pIHtcbiAgICB2YXIgaW5kZXggPSBpbmRleGVzW2xlbmd0aF07XG4gICAgYXJyYXlbbGVuZ3RoXSA9IGlzSW5kZXgoaW5kZXgsIGFyckxlbmd0aCkgPyBvbGRBcnJheVtpbmRleF0gOiB1bmRlZmluZWQ7XG4gIH1cbiAgcmV0dXJuIGFycmF5O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHJlb3JkZXI7XG4iLCIvKiogVXNlZCBhcyB0aGUgaW50ZXJuYWwgYXJndW1lbnQgcGxhY2Vob2xkZXIuICovXG52YXIgUExBQ0VIT0xERVIgPSAnX19sb2Rhc2hfcGxhY2Vob2xkZXJfXyc7XG5cbi8qKlxuICogUmVwbGFjZXMgYWxsIGBwbGFjZWhvbGRlcmAgZWxlbWVudHMgaW4gYGFycmF5YCB3aXRoIGFuIGludGVybmFsIHBsYWNlaG9sZGVyXG4gKiBhbmQgcmV0dXJucyBhbiBhcnJheSBvZiB0aGVpciBpbmRleGVzLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fSBhcnJheSBUaGUgYXJyYXkgdG8gbW9kaWZ5LlxuICogQHBhcmFtIHsqfSBwbGFjZWhvbGRlciBUaGUgcGxhY2Vob2xkZXIgdG8gcmVwbGFjZS5cbiAqIEByZXR1cm5zIHtBcnJheX0gUmV0dXJucyB0aGUgbmV3IGFycmF5IG9mIHBsYWNlaG9sZGVyIGluZGV4ZXMuXG4gKi9cbmZ1bmN0aW9uIHJlcGxhY2VIb2xkZXJzKGFycmF5LCBwbGFjZWhvbGRlcikge1xuICB2YXIgaW5kZXggPSAtMSxcbiAgICAgIGxlbmd0aCA9IGFycmF5Lmxlbmd0aCxcbiAgICAgIHJlc0luZGV4ID0gLTEsXG4gICAgICByZXN1bHQgPSBbXTtcblxuICB3aGlsZSAoKytpbmRleCA8IGxlbmd0aCkge1xuICAgIGlmIChhcnJheVtpbmRleF0gPT09IHBsYWNlaG9sZGVyKSB7XG4gICAgICBhcnJheVtpbmRleF0gPSBQTEFDRUhPTERFUjtcbiAgICAgIHJlc3VsdFsrK3Jlc0luZGV4XSA9IGluZGV4O1xuICAgIH1cbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHJlcGxhY2VIb2xkZXJzO1xuIiwidmFyIGJhc2VTZXREYXRhID0gcmVxdWlyZSgnLi9iYXNlU2V0RGF0YScpLFxuICAgIG5vdyA9IHJlcXVpcmUoJy4uL2RhdGUvbm93Jyk7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCB3aGVuIGEgZnVuY3Rpb24gYmVjb21lcyBob3QuICovXG52YXIgSE9UX0NPVU5UID0gMTUwLFxuICAgIEhPVF9TUEFOID0gMTY7XG5cbi8qKlxuICogU2V0cyBtZXRhZGF0YSBmb3IgYGZ1bmNgLlxuICpcbiAqICoqTm90ZToqKiBJZiB0aGlzIGZ1bmN0aW9uIGJlY29tZXMgaG90LCBpLmUuIGlzIGludm9rZWQgYSBsb3QgaW4gYSBzaG9ydFxuICogcGVyaW9kIG9mIHRpbWUsIGl0IHdpbGwgdHJpcCBpdHMgYnJlYWtlciBhbmQgdHJhbnNpdGlvbiB0byBhbiBpZGVudGl0eSBmdW5jdGlvblxuICogdG8gYXZvaWQgZ2FyYmFnZSBjb2xsZWN0aW9uIHBhdXNlcyBpbiBWOC4gU2VlIFtWOCBpc3N1ZSAyMDcwXShodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3Y4L2lzc3Vlcy9kZXRhaWw/aWQ9MjA3MClcbiAqIGZvciBtb3JlIGRldGFpbHMuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZ1bmMgVGhlIGZ1bmN0aW9uIHRvIGFzc29jaWF0ZSBtZXRhZGF0YSB3aXRoLlxuICogQHBhcmFtIHsqfSBkYXRhIFRoZSBtZXRhZGF0YS5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyBgZnVuY2AuXG4gKi9cbnZhciBzZXREYXRhID0gKGZ1bmN0aW9uKCkge1xuICB2YXIgY291bnQgPSAwLFxuICAgICAgbGFzdENhbGxlZCA9IDA7XG5cbiAgcmV0dXJuIGZ1bmN0aW9uKGtleSwgdmFsdWUpIHtcbiAgICB2YXIgc3RhbXAgPSBub3coKSxcbiAgICAgICAgcmVtYWluaW5nID0gSE9UX1NQQU4gLSAoc3RhbXAgLSBsYXN0Q2FsbGVkKTtcblxuICAgIGxhc3RDYWxsZWQgPSBzdGFtcDtcbiAgICBpZiAocmVtYWluaW5nID4gMCkge1xuICAgICAgaWYgKCsrY291bnQgPj0gSE9UX0NPVU5UKSB7XG4gICAgICAgIHJldHVybiBrZXk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvdW50ID0gMDtcbiAgICB9XG4gICAgcmV0dXJuIGJhc2VTZXREYXRhKGtleSwgdmFsdWUpO1xuICB9O1xufSgpKTtcblxubW9kdWxlLmV4cG9ydHMgPSBzZXREYXRhO1xuIiwidmFyIGJhc2VGb3JJbiA9IHJlcXVpcmUoJy4vYmFzZUZvckluJyksXG4gICAgaXNBcmd1bWVudHMgPSByZXF1aXJlKCcuLi9sYW5nL2lzQXJndW1lbnRzJyksXG4gICAgaXNIb3N0T2JqZWN0ID0gcmVxdWlyZSgnLi9pc0hvc3RPYmplY3QnKSxcbiAgICBpc09iamVjdExpa2UgPSByZXF1aXJlKCcuL2lzT2JqZWN0TGlrZScpLFxuICAgIHN1cHBvcnQgPSByZXF1aXJlKCcuLi9zdXBwb3J0Jyk7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RUYWcgPSAnW29iamVjdCBPYmplY3RdJztcblxuLyoqIFVzZWQgZm9yIG5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKiBVc2VkIHRvIGNoZWNrIG9iamVjdHMgZm9yIG93biBwcm9wZXJ0aWVzLiAqL1xudmFyIGhhc093blByb3BlcnR5ID0gb2JqZWN0UHJvdG8uaGFzT3duUHJvcGVydHk7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZSBbYHRvU3RyaW5nVGFnYF0oaHR0cHM6Ly9wZW9wbGUubW96aWxsYS5vcmcvfmpvcmVuZG9yZmYvZXM2LWRyYWZ0Lmh0bWwjc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmpUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKipcbiAqIEEgZmFsbGJhY2sgaW1wbGVtZW50YXRpb24gb2YgYF8uaXNQbGFpbk9iamVjdGAgd2hpY2ggY2hlY2tzIGlmIGB2YWx1ZWBcbiAqIGlzIGFuIG9iamVjdCBjcmVhdGVkIGJ5IHRoZSBgT2JqZWN0YCBjb25zdHJ1Y3RvciBvciBoYXMgYSBgW1tQcm90b3R5cGVdXWBcbiAqIG9mIGBudWxsYC5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHBsYWluIG9iamVjdCwgZWxzZSBgZmFsc2VgLlxuICovXG5mdW5jdGlvbiBzaGltSXNQbGFpbk9iamVjdCh2YWx1ZSkge1xuICB2YXIgQ3RvcjtcblxuICAvLyBFeGl0IGVhcmx5IGZvciBub24gYE9iamVjdGAgb2JqZWN0cy5cbiAgaWYgKCEoaXNPYmplY3RMaWtlKHZhbHVlKSAmJiBvYmpUb1N0cmluZy5jYWxsKHZhbHVlKSA9PSBvYmplY3RUYWcgJiYgIWlzSG9zdE9iamVjdCh2YWx1ZSkpIHx8XG4gICAgICAoIWhhc093blByb3BlcnR5LmNhbGwodmFsdWUsICdjb25zdHJ1Y3RvcicpICYmXG4gICAgICAgIChDdG9yID0gdmFsdWUuY29uc3RydWN0b3IsIHR5cGVvZiBDdG9yID09ICdmdW5jdGlvbicgJiYgIShDdG9yIGluc3RhbmNlb2YgQ3RvcikpKSB8fFxuICAgICAgKCFzdXBwb3J0LmFyZ3NUYWcgJiYgaXNBcmd1bWVudHModmFsdWUpKSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICAvLyBJRSA8IDkgaXRlcmF0ZXMgaW5oZXJpdGVkIHByb3BlcnRpZXMgYmVmb3JlIG93biBwcm9wZXJ0aWVzLiBJZiB0aGUgZmlyc3RcbiAgLy8gaXRlcmF0ZWQgcHJvcGVydHkgaXMgYW4gb2JqZWN0J3Mgb3duIHByb3BlcnR5IHRoZW4gdGhlcmUgYXJlIG5vIGluaGVyaXRlZFxuICAvLyBlbnVtZXJhYmxlIHByb3BlcnRpZXMuXG4gIHZhciByZXN1bHQ7XG4gIGlmIChzdXBwb3J0Lm93bkxhc3QpIHtcbiAgICBiYXNlRm9ySW4odmFsdWUsIGZ1bmN0aW9uKHN1YlZhbHVlLCBrZXksIG9iamVjdCkge1xuICAgICAgcmVzdWx0ID0gaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3QsIGtleSk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfSk7XG4gICAgcmV0dXJuIHJlc3VsdCAhPT0gZmFsc2U7XG4gIH1cbiAgLy8gSW4gbW9zdCBlbnZpcm9ubWVudHMgYW4gb2JqZWN0J3Mgb3duIHByb3BlcnRpZXMgYXJlIGl0ZXJhdGVkIGJlZm9yZVxuICAvLyBpdHMgaW5oZXJpdGVkIHByb3BlcnRpZXMuIElmIHRoZSBsYXN0IGl0ZXJhdGVkIHByb3BlcnR5IGlzIGFuIG9iamVjdCdzXG4gIC8vIG93biBwcm9wZXJ0eSB0aGVuIHRoZXJlIGFyZSBubyBpbmhlcml0ZWQgZW51bWVyYWJsZSBwcm9wZXJ0aWVzLlxuICBiYXNlRm9ySW4odmFsdWUsIGZ1bmN0aW9uKHN1YlZhbHVlLCBrZXkpIHtcbiAgICByZXN1bHQgPSBrZXk7XG4gIH0pO1xuICByZXR1cm4gdHlwZW9mIHJlc3VsdCA9PSAndW5kZWZpbmVkJyB8fCBoYXNPd25Qcm9wZXJ0eS5jYWxsKHZhbHVlLCByZXN1bHQpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHNoaW1Jc1BsYWluT2JqZWN0O1xuIiwidmFyIGlzQXJndW1lbnRzID0gcmVxdWlyZSgnLi4vbGFuZy9pc0FyZ3VtZW50cycpLFxuICAgIGlzQXJyYXkgPSByZXF1aXJlKCcuLi9sYW5nL2lzQXJyYXknKSxcbiAgICBpc0luZGV4ID0gcmVxdWlyZSgnLi9pc0luZGV4JyksXG4gICAgaXNMZW5ndGggPSByZXF1aXJlKCcuL2lzTGVuZ3RoJyksXG4gICAgaXNTdHJpbmcgPSByZXF1aXJlKCcuLi9sYW5nL2lzU3RyaW5nJyksXG4gICAga2V5c0luID0gcmVxdWlyZSgnLi4vb2JqZWN0L2tleXNJbicpLFxuICAgIHN1cHBvcnQgPSByZXF1aXJlKCcuLi9zdXBwb3J0Jyk7XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKiogVXNlZCB0byBjaGVjayBvYmplY3RzIGZvciBvd24gcHJvcGVydGllcy4gKi9cbnZhciBoYXNPd25Qcm9wZXJ0eSA9IG9iamVjdFByb3RvLmhhc093blByb3BlcnR5O1xuXG4vKipcbiAqIEEgZmFsbGJhY2sgaW1wbGVtZW50YXRpb24gb2YgYE9iamVjdC5rZXlzYCB3aGljaCBjcmVhdGVzIGFuIGFycmF5IG9mIHRoZVxuICogb3duIGVudW1lcmFibGUgcHJvcGVydHkgbmFtZXMgb2YgYG9iamVjdGAuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3QgVGhlIG9iamVjdCB0byBpbnNwZWN0LlxuICogQHJldHVybnMge0FycmF5fSBSZXR1cm5zIHRoZSBhcnJheSBvZiBwcm9wZXJ0eSBuYW1lcy5cbiAqL1xuZnVuY3Rpb24gc2hpbUtleXMob2JqZWN0KSB7XG4gIHZhciBwcm9wcyA9IGtleXNJbihvYmplY3QpLFxuICAgICAgcHJvcHNMZW5ndGggPSBwcm9wcy5sZW5ndGgsXG4gICAgICBsZW5ndGggPSBwcm9wc0xlbmd0aCAmJiBvYmplY3QubGVuZ3RoO1xuXG4gIHZhciBhbGxvd0luZGV4ZXMgPSBsZW5ndGggJiYgaXNMZW5ndGgobGVuZ3RoKSAmJlxuICAgIChpc0FycmF5KG9iamVjdCkgfHwgKHN1cHBvcnQubm9uRW51bVN0cmluZ3MgJiYgaXNTdHJpbmcob2JqZWN0KSkgfHxcbiAgICAgIChzdXBwb3J0Lm5vbkVudW1BcmdzICYmIGlzQXJndW1lbnRzKG9iamVjdCkpKTtcblxuICB2YXIgaW5kZXggPSAtMSxcbiAgICAgIHJlc3VsdCA9IFtdO1xuXG4gIHdoaWxlICgrK2luZGV4IDwgcHJvcHNMZW5ndGgpIHtcbiAgICB2YXIga2V5ID0gcHJvcHNbaW5kZXhdO1xuICAgIGlmICgoYWxsb3dJbmRleGVzICYmIGlzSW5kZXgoa2V5LCBsZW5ndGgpKSB8fCBoYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwga2V5KSkge1xuICAgICAgcmVzdWx0LnB1c2goa2V5KTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBzaGltS2V5cztcbiIsInZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4uL2xhbmcvaXNPYmplY3QnKSxcbiAgICBpc1N0cmluZyA9IHJlcXVpcmUoJy4uL2xhbmcvaXNTdHJpbmcnKSxcbiAgICBzdXBwb3J0ID0gcmVxdWlyZSgnLi4vc3VwcG9ydCcpO1xuXG4vKipcbiAqIENvbnZlcnRzIGB2YWx1ZWAgdG8gYW4gb2JqZWN0IGlmIGl0IGlzIG5vdCBvbmUuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIHByb2Nlc3MuXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBSZXR1cm5zIHRoZSBvYmplY3QuXG4gKi9cbmZ1bmN0aW9uIHRvT2JqZWN0KHZhbHVlKSB7XG4gIGlmIChzdXBwb3J0LnVuaW5kZXhlZENoYXJzICYmIGlzU3RyaW5nKHZhbHVlKSkge1xuICAgIHZhciBpbmRleCA9IC0xLFxuICAgICAgICBsZW5ndGggPSB2YWx1ZS5sZW5ndGgsXG4gICAgICAgIHJlc3VsdCA9IE9iamVjdCh2YWx1ZSk7XG5cbiAgICB3aGlsZSAoKytpbmRleCA8IGxlbmd0aCkge1xuICAgICAgcmVzdWx0W2luZGV4XSA9IHZhbHVlLmNoYXJBdChpbmRleCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgcmV0dXJuIGlzT2JqZWN0KHZhbHVlKSA/IHZhbHVlIDogT2JqZWN0KHZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB0b09iamVjdDtcbiIsInZhciBMYXp5V3JhcHBlciA9IHJlcXVpcmUoJy4vTGF6eVdyYXBwZXInKSxcbiAgICBMb2Rhc2hXcmFwcGVyID0gcmVxdWlyZSgnLi9Mb2Rhc2hXcmFwcGVyJyksXG4gICAgYXJyYXlDb3B5ID0gcmVxdWlyZSgnLi9hcnJheUNvcHknKTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgY2xvbmUgb2YgYHdyYXBwZXJgLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge09iamVjdH0gd3JhcHBlciBUaGUgd3JhcHBlciB0byBjbG9uZS5cbiAqIEByZXR1cm5zIHtPYmplY3R9IFJldHVybnMgdGhlIGNsb25lZCB3cmFwcGVyLlxuICovXG5mdW5jdGlvbiB3cmFwcGVyQ2xvbmUod3JhcHBlcikge1xuICByZXR1cm4gd3JhcHBlciBpbnN0YW5jZW9mIExhenlXcmFwcGVyXG4gICAgPyB3cmFwcGVyLmNsb25lKClcbiAgICA6IG5ldyBMb2Rhc2hXcmFwcGVyKHdyYXBwZXIuX193cmFwcGVkX18sIHdyYXBwZXIuX19jaGFpbl9fLCBhcnJheUNvcHkod3JhcHBlci5fX2FjdGlvbnNfXykpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHdyYXBwZXJDbG9uZTtcbiIsInZhciBiYXNlQ2xvbmUgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9iYXNlQ2xvbmUnKSxcbiAgICBiaW5kQ2FsbGJhY2sgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9iaW5kQ2FsbGJhY2snKTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVlcCBjbG9uZSBvZiBgdmFsdWVgLiBJZiBgY3VzdG9taXplcmAgaXMgcHJvdmlkZWQgaXQgaXMgaW52b2tlZFxuICogdG8gcHJvZHVjZSB0aGUgY2xvbmVkIHZhbHVlcy4gSWYgYGN1c3RvbWl6ZXJgIHJldHVybnMgYHVuZGVmaW5lZGAgY2xvbmluZ1xuICogaXMgaGFuZGxlZCBieSB0aGUgbWV0aG9kIGluc3RlYWQuIFRoZSBgY3VzdG9taXplcmAgaXMgYm91bmQgdG8gYHRoaXNBcmdgXG4gKiBhbmQgaW52b2tlZCB3aXRoIHR3byBhcmd1bWVudDsgKHZhbHVlIFssIGluZGV4fGtleSwgb2JqZWN0XSkuXG4gKlxuICogKipOb3RlOioqIFRoaXMgbWV0aG9kIGlzIGxvb3NlbHkgYmFzZWQgb24gdGhlXG4gKiBbc3RydWN0dXJlZCBjbG9uZSBhbGdvcml0aG1dKGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWw1L2luZnJhc3RydWN0dXJlLmh0bWwjaW50ZXJuYWwtc3RydWN0dXJlZC1jbG9uaW5nLWFsZ29yaXRobSkuXG4gKiBUaGUgZW51bWVyYWJsZSBwcm9wZXJ0aWVzIG9mIGBhcmd1bWVudHNgIG9iamVjdHMgYW5kIG9iamVjdHMgY3JlYXRlZCBieVxuICogY29uc3RydWN0b3JzIG90aGVyIHRoYW4gYE9iamVjdGAgYXJlIGNsb25lZCB0byBwbGFpbiBgT2JqZWN0YCBvYmplY3RzLiBBblxuICogZW1wdHkgb2JqZWN0IGlzIHJldHVybmVkIGZvciB1bmNsb25lYWJsZSB2YWx1ZXMgc3VjaCBhcyBmdW5jdGlvbnMsIERPTSBub2RlcyxcbiAqIE1hcHMsIFNldHMsIGFuZCBXZWFrTWFwcy5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGRlZXAgY2xvbmUuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBbY3VzdG9taXplcl0gVGhlIGZ1bmN0aW9uIHRvIGN1c3RvbWl6ZSBjbG9uaW5nIHZhbHVlcy5cbiAqIEBwYXJhbSB7Kn0gW3RoaXNBcmddIFRoZSBgdGhpc2AgYmluZGluZyBvZiBgY3VzdG9taXplcmAuXG4gKiBAcmV0dXJucyB7Kn0gUmV0dXJucyB0aGUgZGVlcCBjbG9uZWQgdmFsdWUuXG4gKiBAZXhhbXBsZVxuICpcbiAqIHZhciB1c2VycyA9IFtcbiAqICAgeyAndXNlcic6ICdiYXJuZXknIH0sXG4gKiAgIHsgJ3VzZXInOiAnZnJlZCcgfVxuICogXTtcbiAqXG4gKiB2YXIgZGVlcCA9IF8uY2xvbmVEZWVwKHVzZXJzKTtcbiAqIGRlZXBbMF0gPT09IHVzZXJzWzBdO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiAvLyB1c2luZyBhIGN1c3RvbWl6ZXIgY2FsbGJhY2tcbiAqIHZhciBlbCA9IF8uY2xvbmVEZWVwKGRvY3VtZW50LmJvZHksIGZ1bmN0aW9uKHZhbHVlKSB7XG4gKiAgIGlmIChfLmlzRWxlbWVudCh2YWx1ZSkpIHtcbiAqICAgICByZXR1cm4gdmFsdWUuY2xvbmVOb2RlKHRydWUpO1xuICogICB9XG4gKiB9KTtcbiAqXG4gKiBlbCA9PT0gZG9jdW1lbnQuYm9keVxuICogLy8gPT4gZmFsc2VcbiAqIGVsLm5vZGVOYW1lXG4gKiAvLyA9PiBCT0RZXG4gKiBlbC5jaGlsZE5vZGVzLmxlbmd0aDtcbiAqIC8vID0+IDIwXG4gKi9cbmZ1bmN0aW9uIGNsb25lRGVlcCh2YWx1ZSwgY3VzdG9taXplciwgdGhpc0FyZykge1xuICBjdXN0b21pemVyID0gdHlwZW9mIGN1c3RvbWl6ZXIgPT0gJ2Z1bmN0aW9uJyAmJiBiaW5kQ2FsbGJhY2soY3VzdG9taXplciwgdGhpc0FyZywgMSk7XG4gIHJldHVybiBiYXNlQ2xvbmUodmFsdWUsIHRydWUsIGN1c3RvbWl6ZXIpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNsb25lRGVlcDtcbiIsInZhciBpc0xlbmd0aCA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2lzTGVuZ3RoJyksXG4gICAgaXNPYmplY3RMaWtlID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvaXNPYmplY3RMaWtlJyksXG4gICAgc3VwcG9ydCA9IHJlcXVpcmUoJy4uL3N1cHBvcnQnKTtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIGFyZ3NUYWcgPSAnW29iamVjdCBBcmd1bWVudHNdJztcblxuLyoqIFVzZWQgZm9yIG5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKiBVc2VkIHRvIGNoZWNrIG9iamVjdHMgZm9yIG93biBwcm9wZXJ0aWVzLiAqL1xudmFyIGhhc093blByb3BlcnR5ID0gb2JqZWN0UHJvdG8uaGFzT3duUHJvcGVydHk7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZSBbYHRvU3RyaW5nVGFnYF0oaHR0cHM6Ly9wZW9wbGUubW96aWxsYS5vcmcvfmpvcmVuZG9yZmYvZXM2LWRyYWZ0Lmh0bWwjc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmpUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIHByb3BlcnR5SXNFbnVtZXJhYmxlID0gb2JqZWN0UHJvdG8ucHJvcGVydHlJc0VudW1lcmFibGU7XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhbiBgYXJndW1lbnRzYCBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGNvcnJlY3RseSBjbGFzc2lmaWVkLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNBcmd1bWVudHMoZnVuY3Rpb24oKSB7IHJldHVybiBhcmd1bWVudHM7IH0oKSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc0FyZ3VtZW50cyhbMSwgMiwgM10pO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNBcmd1bWVudHModmFsdWUpIHtcbiAgdmFyIGxlbmd0aCA9IGlzT2JqZWN0TGlrZSh2YWx1ZSkgPyB2YWx1ZS5sZW5ndGggOiB1bmRlZmluZWQ7XG4gIHJldHVybiBpc0xlbmd0aChsZW5ndGgpICYmIG9ialRvU3RyaW5nLmNhbGwodmFsdWUpID09IGFyZ3NUYWc7XG59XG4vLyBGYWxsYmFjayBmb3IgZW52aXJvbm1lbnRzIHdpdGhvdXQgYSBgdG9TdHJpbmdUYWdgIGZvciBgYXJndW1lbnRzYCBvYmplY3RzLlxuaWYgKCFzdXBwb3J0LmFyZ3NUYWcpIHtcbiAgaXNBcmd1bWVudHMgPSBmdW5jdGlvbih2YWx1ZSkge1xuICAgIHZhciBsZW5ndGggPSBpc09iamVjdExpa2UodmFsdWUpID8gdmFsdWUubGVuZ3RoIDogdW5kZWZpbmVkO1xuICAgIHJldHVybiBpc0xlbmd0aChsZW5ndGgpICYmIGhhc093blByb3BlcnR5LmNhbGwodmFsdWUsICdjYWxsZWUnKSAmJlxuICAgICAgIXByb3BlcnR5SXNFbnVtZXJhYmxlLmNhbGwodmFsdWUsICdjYWxsZWUnKTtcbiAgfTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBpc0FyZ3VtZW50cztcbiIsInZhciBpc0xlbmd0aCA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2lzTGVuZ3RoJyksXG4gICAgaXNOYXRpdmUgPSByZXF1aXJlKCcuL2lzTmF0aXZlJyksXG4gICAgaXNPYmplY3RMaWtlID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvaXNPYmplY3RMaWtlJyk7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBhcnJheVRhZyA9ICdbb2JqZWN0IEFycmF5XSc7XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGUgW2B0b1N0cmluZ1RhZ2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqVG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVJc0FycmF5ID0gaXNOYXRpdmUobmF0aXZlSXNBcnJheSA9IEFycmF5LmlzQXJyYXkpICYmIG5hdGl2ZUlzQXJyYXk7XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhbiBgQXJyYXlgIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgY29ycmVjdGx5IGNsYXNzaWZpZWQsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc0FycmF5KFsxLCAyLCAzXSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc0FycmF5KGZ1bmN0aW9uKCkgeyByZXR1cm4gYXJndW1lbnRzOyB9KCkpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xudmFyIGlzQXJyYXkgPSBuYXRpdmVJc0FycmF5IHx8IGZ1bmN0aW9uKHZhbHVlKSB7XG4gIHJldHVybiBpc09iamVjdExpa2UodmFsdWUpICYmIGlzTGVuZ3RoKHZhbHVlLmxlbmd0aCkgJiYgb2JqVG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gYXJyYXlUYWc7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGlzQXJyYXk7XG4iLCJ2YXIgYmFzZUlzRnVuY3Rpb24gPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9iYXNlSXNGdW5jdGlvbicpLFxuICAgIGlzTmF0aXZlID0gcmVxdWlyZSgnLi9pc05hdGl2ZScpO1xuXG4vKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgZnVuY1RhZyA9ICdbb2JqZWN0IEZ1bmN0aW9uXSc7XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGUgW2B0b1N0cmluZ1RhZ2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqVG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyoqIE5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBVaW50OEFycmF5ID0gaXNOYXRpdmUoVWludDhBcnJheSA9IGdsb2JhbC5VaW50OEFycmF5KSAmJiBVaW50OEFycmF5O1xuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGNsYXNzaWZpZWQgYXMgYSBgRnVuY3Rpb25gIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgY29ycmVjdGx5IGNsYXNzaWZpZWQsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc0Z1bmN0aW9uKF8pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNGdW5jdGlvbigvYWJjLyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG52YXIgaXNGdW5jdGlvbiA9ICEoYmFzZUlzRnVuY3Rpb24oL3gvKSB8fCAoVWludDhBcnJheSAmJiAhYmFzZUlzRnVuY3Rpb24oVWludDhBcnJheSkpKSA/IGJhc2VJc0Z1bmN0aW9uIDogZnVuY3Rpb24odmFsdWUpIHtcbiAgLy8gVGhlIHVzZSBvZiBgT2JqZWN0I3RvU3RyaW5nYCBhdm9pZHMgaXNzdWVzIHdpdGggdGhlIGB0eXBlb2ZgIG9wZXJhdG9yXG4gIC8vIGluIG9sZGVyIHZlcnNpb25zIG9mIENocm9tZSBhbmQgU2FmYXJpIHdoaWNoIHJldHVybiAnZnVuY3Rpb24nIGZvciByZWdleGVzXG4gIC8vIGFuZCBTYWZhcmkgOCBlcXVpdmFsZW50cyB3aGljaCByZXR1cm4gJ29iamVjdCcgZm9yIHR5cGVkIGFycmF5IGNvbnN0cnVjdG9ycy5cbiAgcmV0dXJuIG9ialRvU3RyaW5nLmNhbGwodmFsdWUpID09IGZ1bmNUYWc7XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGlzRnVuY3Rpb247XG4iLCJ2YXIgZXNjYXBlUmVnRXhwID0gcmVxdWlyZSgnLi4vc3RyaW5nL2VzY2FwZVJlZ0V4cCcpLFxuICAgIGlzSG9zdE9iamVjdCA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2lzSG9zdE9iamVjdCcpLFxuICAgIGlzT2JqZWN0TGlrZSA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2lzT2JqZWN0TGlrZScpO1xuXG4vKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgZnVuY1RhZyA9ICdbb2JqZWN0IEZ1bmN0aW9uXSc7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBob3N0IGNvbnN0cnVjdG9ycyAoU2FmYXJpID4gNSkuICovXG52YXIgcmVIb3N0Q3RvciA9IC9eXFxbb2JqZWN0IC4rP0NvbnN0cnVjdG9yXFxdJC87XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKiogVXNlZCB0byByZXNvbHZlIHRoZSBkZWNvbXBpbGVkIHNvdXJjZSBvZiBmdW5jdGlvbnMuICovXG52YXIgZm5Ub1N0cmluZyA9IEZ1bmN0aW9uLnByb3RvdHlwZS50b1N0cmluZztcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlIFtgdG9TdHJpbmdUYWdgXShodHRwczovL3Blb3BsZS5tb3ppbGxhLm9yZy9+am9yZW5kb3JmZi9lczYtZHJhZnQuaHRtbCNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9ialRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBpZiBhIG1ldGhvZCBpcyBuYXRpdmUuICovXG52YXIgcmVOYXRpdmUgPSBSZWdFeHAoJ14nICtcbiAgZXNjYXBlUmVnRXhwKG9ialRvU3RyaW5nKVxuICAucmVwbGFjZSgvdG9TdHJpbmd8KGZ1bmN0aW9uKS4qPyg/PVxcXFxcXCgpfCBmb3IgLis/KD89XFxcXFxcXSkvZywgJyQxLio/JykgKyAnJCdcbik7XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgYSBuYXRpdmUgZnVuY3Rpb24uXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGEgbmF0aXZlIGZ1bmN0aW9uLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNOYXRpdmUoQXJyYXkucHJvdG90eXBlLnB1c2gpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNOYXRpdmUoXyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc05hdGl2ZSh2YWx1ZSkge1xuICBpZiAodmFsdWUgPT0gbnVsbCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBpZiAob2JqVG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gZnVuY1RhZykge1xuICAgIHJldHVybiByZU5hdGl2ZS50ZXN0KGZuVG9TdHJpbmcuY2FsbCh2YWx1ZSkpO1xuICB9XG4gIHJldHVybiBpc09iamVjdExpa2UodmFsdWUpICYmIChpc0hvc3RPYmplY3QodmFsdWUpID8gcmVOYXRpdmUgOiByZUhvc3RDdG9yKS50ZXN0KHZhbHVlKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBpc05hdGl2ZTtcbiIsIi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlIFtsYW5ndWFnZSB0eXBlXShodHRwczovL2VzNS5naXRodWIuaW8vI3g4KSBvZiBgT2JqZWN0YC5cbiAqIChlLmcuIGFycmF5cywgZnVuY3Rpb25zLCBvYmplY3RzLCByZWdleGVzLCBgbmV3IE51bWJlcigwKWAsIGFuZCBgbmV3IFN0cmluZygnJylgKVxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhbiBvYmplY3QsIGVsc2UgYGZhbHNlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogXy5pc09iamVjdCh7fSk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoMSk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc09iamVjdCh2YWx1ZSkge1xuICAvLyBBdm9pZCBhIFY4IEpJVCBidWcgaW4gQ2hyb21lIDE5LTIwLlxuICAvLyBTZWUgaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC92OC9pc3N1ZXMvZGV0YWlsP2lkPTIyOTEgZm9yIG1vcmUgZGV0YWlscy5cbiAgdmFyIHR5cGUgPSB0eXBlb2YgdmFsdWU7XG4gIHJldHVybiB0eXBlID09ICdmdW5jdGlvbicgfHwgKCEhdmFsdWUgJiYgdHlwZSA9PSAnb2JqZWN0Jyk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaXNPYmplY3Q7XG4iLCJ2YXIgaXNBcmd1bWVudHMgPSByZXF1aXJlKCcuL2lzQXJndW1lbnRzJyksXG4gICAgaXNOYXRpdmUgPSByZXF1aXJlKCcuL2lzTmF0aXZlJyksXG4gICAgc2hpbUlzUGxhaW5PYmplY3QgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9zaGltSXNQbGFpbk9iamVjdCcpLFxuICAgIHN1cHBvcnQgPSByZXF1aXJlKCcuLi9zdXBwb3J0Jyk7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RUYWcgPSAnW29iamVjdCBPYmplY3RdJztcblxuLyoqIFVzZWQgZm9yIG5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKlxuICogVXNlZCB0byByZXNvbHZlIHRoZSBbYHRvU3RyaW5nVGFnYF0oaHR0cHM6Ly9wZW9wbGUubW96aWxsYS5vcmcvfmpvcmVuZG9yZmYvZXM2LWRyYWZ0Lmh0bWwjc2VjLW9iamVjdC5wcm90b3R5cGUudG9zdHJpbmcpXG4gKiBvZiB2YWx1ZXMuXG4gKi9cbnZhciBvYmpUb1N0cmluZyA9IG9iamVjdFByb3RvLnRvU3RyaW5nO1xuXG4vKiogTmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIGdldFByb3RvdHlwZU9mID0gaXNOYXRpdmUoZ2V0UHJvdG90eXBlT2YgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YpICYmIGdldFByb3RvdHlwZU9mO1xuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGEgcGxhaW4gb2JqZWN0LCB0aGF0IGlzLCBhbiBvYmplY3QgY3JlYXRlZCBieSB0aGVcbiAqIGBPYmplY3RgIGNvbnN0cnVjdG9yIG9yIG9uZSB3aXRoIGEgYFtbUHJvdG90eXBlXV1gIG9mIGBudWxsYC5cbiAqXG4gKiAqKk5vdGU6KiogVGhpcyBtZXRob2QgYXNzdW1lcyBvYmplY3RzIGNyZWF0ZWQgYnkgdGhlIGBPYmplY3RgIGNvbnN0cnVjdG9yXG4gKiBoYXZlIG5vIGluaGVyaXRlZCBlbnVtZXJhYmxlIHByb3BlcnRpZXMuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGEgcGxhaW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIGZ1bmN0aW9uIEZvbygpIHtcbiAqICAgdGhpcy5hID0gMTtcbiAqIH1cbiAqXG4gKiBfLmlzUGxhaW5PYmplY3QobmV3IEZvbyk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNQbGFpbk9iamVjdChbMSwgMiwgM10pO1xuICogLy8gPT4gZmFsc2VcbiAqXG4gKiBfLmlzUGxhaW5PYmplY3QoeyAneCc6IDAsICd5JzogMCB9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzUGxhaW5PYmplY3QoT2JqZWN0LmNyZWF0ZShudWxsKSk7XG4gKiAvLyA9PiB0cnVlXG4gKi9cbnZhciBpc1BsYWluT2JqZWN0ID0gIWdldFByb3RvdHlwZU9mID8gc2hpbUlzUGxhaW5PYmplY3QgOiBmdW5jdGlvbih2YWx1ZSkge1xuICBpZiAoISh2YWx1ZSAmJiBvYmpUb1N0cmluZy5jYWxsKHZhbHVlKSA9PSBvYmplY3RUYWcpIHx8ICghc3VwcG9ydC5hcmdzVGFnICYmIGlzQXJndW1lbnRzKHZhbHVlKSkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgdmFyIHZhbHVlT2YgPSB2YWx1ZS52YWx1ZU9mLFxuICAgICAgb2JqUHJvdG8gPSBpc05hdGl2ZSh2YWx1ZU9mKSAmJiAob2JqUHJvdG8gPSBnZXRQcm90b3R5cGVPZih2YWx1ZU9mKSkgJiYgZ2V0UHJvdG90eXBlT2Yob2JqUHJvdG8pO1xuXG4gIHJldHVybiBvYmpQcm90b1xuICAgID8gKHZhbHVlID09IG9ialByb3RvIHx8IGdldFByb3RvdHlwZU9mKHZhbHVlKSA9PSBvYmpQcm90bylcbiAgICA6IHNoaW1Jc1BsYWluT2JqZWN0KHZhbHVlKTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gaXNQbGFpbk9iamVjdDtcbiIsInZhciBpc09iamVjdExpa2UgPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9pc09iamVjdExpa2UnKTtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN0cmluZ1RhZyA9ICdbb2JqZWN0IFN0cmluZ10nO1xuXG4vKiogVXNlZCBmb3IgbmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlIFtgdG9TdHJpbmdUYWdgXShodHRwczovL3Blb3BsZS5tb3ppbGxhLm9yZy9+am9yZW5kb3JmZi9lczYtZHJhZnQuaHRtbCNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9ialRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgY2xhc3NpZmllZCBhcyBhIGBTdHJpbmdgIHByaW1pdGl2ZSBvciBvYmplY3QuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGNvcnJlY3RseSBjbGFzc2lmaWVkLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNTdHJpbmcoJ2FiYycpO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNTdHJpbmcoMSk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N0cmluZyh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzdHJpbmcnIHx8IChpc09iamVjdExpa2UodmFsdWUpICYmIG9ialRvU3RyaW5nLmNhbGwodmFsdWUpID09IHN0cmluZ1RhZyk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaXNTdHJpbmc7XG4iLCJ2YXIgaXNMZW5ndGggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9pc0xlbmd0aCcpLFxuICAgIGlzT2JqZWN0TGlrZSA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2lzT2JqZWN0TGlrZScpO1xuXG4vKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgYXJnc1RhZyA9ICdbb2JqZWN0IEFyZ3VtZW50c10nLFxuICAgIGFycmF5VGFnID0gJ1tvYmplY3QgQXJyYXldJyxcbiAgICBib29sVGFnID0gJ1tvYmplY3QgQm9vbGVhbl0nLFxuICAgIGRhdGVUYWcgPSAnW29iamVjdCBEYXRlXScsXG4gICAgZXJyb3JUYWcgPSAnW29iamVjdCBFcnJvcl0nLFxuICAgIGZ1bmNUYWcgPSAnW29iamVjdCBGdW5jdGlvbl0nLFxuICAgIG1hcFRhZyA9ICdbb2JqZWN0IE1hcF0nLFxuICAgIG51bWJlclRhZyA9ICdbb2JqZWN0IE51bWJlcl0nLFxuICAgIG9iamVjdFRhZyA9ICdbb2JqZWN0IE9iamVjdF0nLFxuICAgIHJlZ2V4cFRhZyA9ICdbb2JqZWN0IFJlZ0V4cF0nLFxuICAgIHNldFRhZyA9ICdbb2JqZWN0IFNldF0nLFxuICAgIHN0cmluZ1RhZyA9ICdbb2JqZWN0IFN0cmluZ10nLFxuICAgIHdlYWtNYXBUYWcgPSAnW29iamVjdCBXZWFrTWFwXSc7XG5cbnZhciBhcnJheUJ1ZmZlclRhZyA9ICdbb2JqZWN0IEFycmF5QnVmZmVyXScsXG4gICAgZmxvYXQzMlRhZyA9ICdbb2JqZWN0IEZsb2F0MzJBcnJheV0nLFxuICAgIGZsb2F0NjRUYWcgPSAnW29iamVjdCBGbG9hdDY0QXJyYXldJyxcbiAgICBpbnQ4VGFnID0gJ1tvYmplY3QgSW50OEFycmF5XScsXG4gICAgaW50MTZUYWcgPSAnW29iamVjdCBJbnQxNkFycmF5XScsXG4gICAgaW50MzJUYWcgPSAnW29iamVjdCBJbnQzMkFycmF5XScsXG4gICAgdWludDhUYWcgPSAnW29iamVjdCBVaW50OEFycmF5XScsXG4gICAgdWludDhDbGFtcGVkVGFnID0gJ1tvYmplY3QgVWludDhDbGFtcGVkQXJyYXldJyxcbiAgICB1aW50MTZUYWcgPSAnW29iamVjdCBVaW50MTZBcnJheV0nLFxuICAgIHVpbnQzMlRhZyA9ICdbb2JqZWN0IFVpbnQzMkFycmF5XSc7XG5cbi8qKiBVc2VkIHRvIGlkZW50aWZ5IGB0b1N0cmluZ1RhZ2AgdmFsdWVzIG9mIHR5cGVkIGFycmF5cy4gKi9cbnZhciB0eXBlZEFycmF5VGFncyA9IHt9O1xudHlwZWRBcnJheVRhZ3NbZmxvYXQzMlRhZ10gPSB0eXBlZEFycmF5VGFnc1tmbG9hdDY0VGFnXSA9XG50eXBlZEFycmF5VGFnc1tpbnQ4VGFnXSA9IHR5cGVkQXJyYXlUYWdzW2ludDE2VGFnXSA9XG50eXBlZEFycmF5VGFnc1tpbnQzMlRhZ10gPSB0eXBlZEFycmF5VGFnc1t1aW50OFRhZ10gPVxudHlwZWRBcnJheVRhZ3NbdWludDhDbGFtcGVkVGFnXSA9IHR5cGVkQXJyYXlUYWdzW3VpbnQxNlRhZ10gPVxudHlwZWRBcnJheVRhZ3NbdWludDMyVGFnXSA9IHRydWU7XG50eXBlZEFycmF5VGFnc1thcmdzVGFnXSA9IHR5cGVkQXJyYXlUYWdzW2FycmF5VGFnXSA9XG50eXBlZEFycmF5VGFnc1thcnJheUJ1ZmZlclRhZ10gPSB0eXBlZEFycmF5VGFnc1tib29sVGFnXSA9XG50eXBlZEFycmF5VGFnc1tkYXRlVGFnXSA9IHR5cGVkQXJyYXlUYWdzW2Vycm9yVGFnXSA9XG50eXBlZEFycmF5VGFnc1tmdW5jVGFnXSA9IHR5cGVkQXJyYXlUYWdzW21hcFRhZ10gPVxudHlwZWRBcnJheVRhZ3NbbnVtYmVyVGFnXSA9IHR5cGVkQXJyYXlUYWdzW29iamVjdFRhZ10gPVxudHlwZWRBcnJheVRhZ3NbcmVnZXhwVGFnXSA9IHR5cGVkQXJyYXlUYWdzW3NldFRhZ10gPVxudHlwZWRBcnJheVRhZ3Nbc3RyaW5nVGFnXSA9IHR5cGVkQXJyYXlUYWdzW3dlYWtNYXBUYWddID0gZmFsc2U7XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgb2JqZWN0UHJvdG8gPSBPYmplY3QucHJvdG90eXBlO1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGUgW2B0b1N0cmluZ1RhZ2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqVG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgdHlwZWQgYXJyYXkuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIGNvcnJlY3RseSBjbGFzc2lmaWVkLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNUeXBlZEFycmF5KG5ldyBVaW50OEFycmF5KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzVHlwZWRBcnJheShbXSk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1R5cGVkQXJyYXkodmFsdWUpIHtcbiAgcmV0dXJuIGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgaXNMZW5ndGgodmFsdWUubGVuZ3RoKSAmJiAhIXR5cGVkQXJyYXlUYWdzW29ialRvU3RyaW5nLmNhbGwodmFsdWUpXTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBpc1R5cGVkQXJyYXk7XG4iLCIvKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIGB1bmRlZmluZWRgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBgdW5kZWZpbmVkYCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzVW5kZWZpbmVkKHZvaWQgMCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1VuZGVmaW5lZChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzVW5kZWZpbmVkKHZhbHVlKSB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT0gJ3VuZGVmaW5lZCc7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gaXNVbmRlZmluZWQ7XG4iLCJ2YXIgaXNMZW5ndGggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9pc0xlbmd0aCcpLFxuICAgIGlzTmF0aXZlID0gcmVxdWlyZSgnLi4vbGFuZy9pc05hdGl2ZScpLFxuICAgIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi4vbGFuZy9pc09iamVjdCcpLFxuICAgIHNoaW1LZXlzID0gcmVxdWlyZSgnLi4vaW50ZXJuYWwvc2hpbUtleXMnKSxcbiAgICBzdXBwb3J0ID0gcmVxdWlyZSgnLi4vc3VwcG9ydCcpO1xuXG4vKiBOYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMgZm9yIHRob3NlIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBvdGhlciBgbG9kYXNoYCBtZXRob2RzLiAqL1xudmFyIG5hdGl2ZUtleXMgPSBpc05hdGl2ZShuYXRpdmVLZXlzID0gT2JqZWN0LmtleXMpICYmIG5hdGl2ZUtleXM7XG5cbi8qKlxuICogQ3JlYXRlcyBhbiBhcnJheSBvZiB0aGUgb3duIGVudW1lcmFibGUgcHJvcGVydHkgbmFtZXMgb2YgYG9iamVjdGAuXG4gKlxuICogKipOb3RlOioqIE5vbi1vYmplY3QgdmFsdWVzIGFyZSBjb2VyY2VkIHRvIG9iamVjdHMuIFNlZSB0aGVcbiAqIFtFUyBzcGVjXShodHRwczovL3Blb3BsZS5tb3ppbGxhLm9yZy9+am9yZW5kb3JmZi9lczYtZHJhZnQuaHRtbCNzZWMtb2JqZWN0LmtleXMpXG4gKiBmb3IgbW9yZSBkZXRhaWxzLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAY2F0ZWdvcnkgT2JqZWN0XG4gKiBAcGFyYW0ge09iamVjdH0gb2JqZWN0IFRoZSBvYmplY3QgdG8gaW5zcGVjdC5cbiAqIEByZXR1cm5zIHtBcnJheX0gUmV0dXJucyB0aGUgYXJyYXkgb2YgcHJvcGVydHkgbmFtZXMuXG4gKiBAZXhhbXBsZVxuICpcbiAqIGZ1bmN0aW9uIEZvbygpIHtcbiAqICAgdGhpcy5hID0gMTtcbiAqICAgdGhpcy5iID0gMjtcbiAqIH1cbiAqXG4gKiBGb28ucHJvdG90eXBlLmMgPSAzO1xuICpcbiAqIF8ua2V5cyhuZXcgRm9vKTtcbiAqIC8vID0+IFsnYScsICdiJ10gKGl0ZXJhdGlvbiBvcmRlciBpcyBub3QgZ3VhcmFudGVlZClcbiAqXG4gKiBfLmtleXMoJ2hpJyk7XG4gKiAvLyA9PiBbJzAnLCAnMSddXG4gKi9cbnZhciBrZXlzID0gIW5hdGl2ZUtleXMgPyBzaGltS2V5cyA6IGZ1bmN0aW9uKG9iamVjdCkge1xuICBpZiAob2JqZWN0KSB7XG4gICAgdmFyIEN0b3IgPSBvYmplY3QuY29uc3RydWN0b3IsXG4gICAgICAgIGxlbmd0aCA9IG9iamVjdC5sZW5ndGg7XG4gIH1cbiAgaWYgKCh0eXBlb2YgQ3RvciA9PSAnZnVuY3Rpb24nICYmIEN0b3IucHJvdG90eXBlID09PSBvYmplY3QpIHx8XG4gICAgICAodHlwZW9mIG9iamVjdCA9PSAnZnVuY3Rpb24nID8gc3VwcG9ydC5lbnVtUHJvdG90eXBlcyA6IChsZW5ndGggJiYgaXNMZW5ndGgobGVuZ3RoKSkpKSB7XG4gICAgcmV0dXJuIHNoaW1LZXlzKG9iamVjdCk7XG4gIH1cbiAgcmV0dXJuIGlzT2JqZWN0KG9iamVjdCkgPyBuYXRpdmVLZXlzKG9iamVjdCkgOiBbXTtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0ga2V5cztcbiIsInZhciBhcnJheUVhY2ggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9hcnJheUVhY2gnKSxcbiAgICBpc0FyZ3VtZW50cyA9IHJlcXVpcmUoJy4uL2xhbmcvaXNBcmd1bWVudHMnKSxcbiAgICBpc0FycmF5ID0gcmVxdWlyZSgnLi4vbGFuZy9pc0FycmF5JyksXG4gICAgaXNGdW5jdGlvbiA9IHJlcXVpcmUoJy4uL2xhbmcvaXNGdW5jdGlvbicpLFxuICAgIGlzSW5kZXggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9pc0luZGV4JyksXG4gICAgaXNMZW5ndGggPSByZXF1aXJlKCcuLi9pbnRlcm5hbC9pc0xlbmd0aCcpLFxuICAgIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi4vbGFuZy9pc09iamVjdCcpLFxuICAgIGlzU3RyaW5nID0gcmVxdWlyZSgnLi4vbGFuZy9pc1N0cmluZycpLFxuICAgIHN1cHBvcnQgPSByZXF1aXJlKCcuLi9zdXBwb3J0Jyk7XG5cbi8qKiBgT2JqZWN0I3RvU3RyaW5nYCByZXN1bHQgcmVmZXJlbmNlcy4gKi9cbnZhciBhcnJheVRhZyA9ICdbb2JqZWN0IEFycmF5XScsXG4gICAgYm9vbFRhZyA9ICdbb2JqZWN0IEJvb2xlYW5dJyxcbiAgICBkYXRlVGFnID0gJ1tvYmplY3QgRGF0ZV0nLFxuICAgIGVycm9yVGFnID0gJ1tvYmplY3QgRXJyb3JdJyxcbiAgICBmdW5jVGFnID0gJ1tvYmplY3QgRnVuY3Rpb25dJyxcbiAgICBudW1iZXJUYWcgPSAnW29iamVjdCBOdW1iZXJdJyxcbiAgICBvYmplY3RUYWcgPSAnW29iamVjdCBPYmplY3RdJyxcbiAgICByZWdleHBUYWcgPSAnW29iamVjdCBSZWdFeHBdJyxcbiAgICBzdHJpbmdUYWcgPSAnW29iamVjdCBTdHJpbmddJztcblxuLyoqIFVzZWQgdG8gZml4IHRoZSBKU2NyaXB0IGBbW0RvbnRFbnVtXV1gIGJ1Zy4gKi9cbnZhciBzaGFkb3dQcm9wcyA9IFtcbiAgJ2NvbnN0cnVjdG9yJywgJ2hhc093blByb3BlcnR5JywgJ2lzUHJvdG90eXBlT2YnLCAncHJvcGVydHlJc0VudW1lcmFibGUnLFxuICAndG9Mb2NhbGVTdHJpbmcnLCAndG9TdHJpbmcnLCAndmFsdWVPZidcbl07XG5cbi8qKiBVc2VkIGZvciBuYXRpdmUgbWV0aG9kIHJlZmVyZW5jZXMuICovXG52YXIgZXJyb3JQcm90byA9IEVycm9yLnByb3RvdHlwZSxcbiAgICBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGUsXG4gICAgc3RyaW5nUHJvdG8gPSBTdHJpbmcucHJvdG90eXBlO1xuXG4vKiogVXNlZCB0byBjaGVjayBvYmplY3RzIGZvciBvd24gcHJvcGVydGllcy4gKi9cbnZhciBoYXNPd25Qcm9wZXJ0eSA9IG9iamVjdFByb3RvLmhhc093blByb3BlcnR5O1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGUgW2B0b1N0cmluZ1RhZ2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqVG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyoqIFVzZWQgdG8gYXZvaWQgaXRlcmF0aW5nIG92ZXIgbm9uLWVudW1lcmFibGUgcHJvcGVydGllcyBpbiBJRSA8IDkuICovXG52YXIgbm9uRW51bVByb3BzID0ge307XG5ub25FbnVtUHJvcHNbYXJyYXlUYWddID0gbm9uRW51bVByb3BzW2RhdGVUYWddID0gbm9uRW51bVByb3BzW251bWJlclRhZ10gPSB7ICdjb25zdHJ1Y3Rvcic6IHRydWUsICd0b0xvY2FsZVN0cmluZyc6IHRydWUsICd0b1N0cmluZyc6IHRydWUsICd2YWx1ZU9mJzogdHJ1ZSB9O1xubm9uRW51bVByb3BzW2Jvb2xUYWddID0gbm9uRW51bVByb3BzW3N0cmluZ1RhZ10gPSB7ICdjb25zdHJ1Y3Rvcic6IHRydWUsICd0b1N0cmluZyc6IHRydWUsICd2YWx1ZU9mJzogdHJ1ZSB9O1xubm9uRW51bVByb3BzW2Vycm9yVGFnXSA9IG5vbkVudW1Qcm9wc1tmdW5jVGFnXSA9IG5vbkVudW1Qcm9wc1tyZWdleHBUYWddID0geyAnY29uc3RydWN0b3InOiB0cnVlLCAndG9TdHJpbmcnOiB0cnVlIH07XG5ub25FbnVtUHJvcHNbb2JqZWN0VGFnXSA9IHsgJ2NvbnN0cnVjdG9yJzogdHJ1ZSB9O1xuXG5hcnJheUVhY2goc2hhZG93UHJvcHMsIGZ1bmN0aW9uKGtleSkge1xuICBmb3IgKHZhciB0YWcgaW4gbm9uRW51bVByb3BzKSB7XG4gICAgaWYgKGhhc093blByb3BlcnR5LmNhbGwobm9uRW51bVByb3BzLCB0YWcpKSB7XG4gICAgICB2YXIgcHJvcHMgPSBub25FbnVtUHJvcHNbdGFnXTtcbiAgICAgIHByb3BzW2tleV0gPSBoYXNPd25Qcm9wZXJ0eS5jYWxsKHByb3BzLCBrZXkpO1xuICAgIH1cbiAgfVxufSk7XG5cbi8qKlxuICogQ3JlYXRlcyBhbiBhcnJheSBvZiB0aGUgb3duIGFuZCBpbmhlcml0ZWQgZW51bWVyYWJsZSBwcm9wZXJ0eSBuYW1lcyBvZiBgb2JqZWN0YC5cbiAqXG4gKiAqKk5vdGU6KiogTm9uLW9iamVjdCB2YWx1ZXMgYXJlIGNvZXJjZWQgdG8gb2JqZWN0cy5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IE9iamVjdFxuICogQHBhcmFtIHtPYmplY3R9IG9iamVjdCBUaGUgb2JqZWN0IHRvIGluc3BlY3QuXG4gKiBAcmV0dXJucyB7QXJyYXl9IFJldHVybnMgdGhlIGFycmF5IG9mIHByb3BlcnR5IG5hbWVzLlxuICogQGV4YW1wbGVcbiAqXG4gKiBmdW5jdGlvbiBGb28oKSB7XG4gKiAgIHRoaXMuYSA9IDE7XG4gKiAgIHRoaXMuYiA9IDI7XG4gKiB9XG4gKlxuICogRm9vLnByb3RvdHlwZS5jID0gMztcbiAqXG4gKiBfLmtleXNJbihuZXcgRm9vKTtcbiAqIC8vID0+IFsnYScsICdiJywgJ2MnXSAoaXRlcmF0aW9uIG9yZGVyIGlzIG5vdCBndWFyYW50ZWVkKVxuICovXG5mdW5jdGlvbiBrZXlzSW4ob2JqZWN0KSB7XG4gIGlmIChvYmplY3QgPT0gbnVsbCkge1xuICAgIHJldHVybiBbXTtcbiAgfVxuICBpZiAoIWlzT2JqZWN0KG9iamVjdCkpIHtcbiAgICBvYmplY3QgPSBPYmplY3Qob2JqZWN0KTtcbiAgfVxuICB2YXIgbGVuZ3RoID0gb2JqZWN0Lmxlbmd0aDtcblxuICBsZW5ndGggPSAobGVuZ3RoICYmIGlzTGVuZ3RoKGxlbmd0aCkgJiZcbiAgICAoaXNBcnJheShvYmplY3QpIHx8IChzdXBwb3J0Lm5vbkVudW1TdHJpbmdzICYmIGlzU3RyaW5nKG9iamVjdCkpIHx8XG4gICAgICAoc3VwcG9ydC5ub25FbnVtQXJncyAmJiBpc0FyZ3VtZW50cyhvYmplY3QpKSkgJiYgbGVuZ3RoKSB8fCAwO1xuXG4gIHZhciBDdG9yID0gb2JqZWN0LmNvbnN0cnVjdG9yLFxuICAgICAgaW5kZXggPSAtMSxcbiAgICAgIHByb3RvID0gKGlzRnVuY3Rpb24oQ3RvcikgJiYgQ3Rvci5wcm90b3R5cGUpIHx8IG9iamVjdFByb3RvLFxuICAgICAgaXNQcm90byA9IHByb3RvID09PSBvYmplY3QsXG4gICAgICByZXN1bHQgPSBBcnJheShsZW5ndGgpLFxuICAgICAgc2tpcEluZGV4ZXMgPSBsZW5ndGggPiAwLFxuICAgICAgc2tpcEVycm9yUHJvcHMgPSBzdXBwb3J0LmVudW1FcnJvclByb3BzICYmIChvYmplY3QgPT09IGVycm9yUHJvdG8gfHwgb2JqZWN0IGluc3RhbmNlb2YgRXJyb3IpLFxuICAgICAgc2tpcFByb3RvID0gc3VwcG9ydC5lbnVtUHJvdG90eXBlcyAmJiBpc0Z1bmN0aW9uKG9iamVjdCk7XG5cbiAgd2hpbGUgKCsraW5kZXggPCBsZW5ndGgpIHtcbiAgICByZXN1bHRbaW5kZXhdID0gKGluZGV4ICsgJycpO1xuICB9XG4gIC8vIGxvZGFzaCBza2lwcyB0aGUgYGNvbnN0cnVjdG9yYCBwcm9wZXJ0eSB3aGVuIGl0IGluZmVycyBpdCBpcyBpdGVyYXRpbmdcbiAgLy8gb3ZlciBhIGBwcm90b3R5cGVgIG9iamVjdCBiZWNhdXNlIElFIDwgOSBjYW4ndCBzZXQgdGhlIGBbW0VudW1lcmFibGVdXWBcbiAgLy8gYXR0cmlidXRlIG9mIGFuIGV4aXN0aW5nIHByb3BlcnR5IGFuZCB0aGUgYGNvbnN0cnVjdG9yYCBwcm9wZXJ0eSBvZiBhXG4gIC8vIHByb3RvdHlwZSBkZWZhdWx0cyB0byBub24tZW51bWVyYWJsZS5cbiAgZm9yICh2YXIga2V5IGluIG9iamVjdCkge1xuICAgIGlmICghKHNraXBQcm90byAmJiBrZXkgPT0gJ3Byb3RvdHlwZScpICYmXG4gICAgICAgICEoc2tpcEVycm9yUHJvcHMgJiYgKGtleSA9PSAnbWVzc2FnZScgfHwga2V5ID09ICduYW1lJykpICYmXG4gICAgICAgICEoc2tpcEluZGV4ZXMgJiYgaXNJbmRleChrZXksIGxlbmd0aCkpICYmXG4gICAgICAgICEoa2V5ID09ICdjb25zdHJ1Y3RvcicgJiYgKGlzUHJvdG8gfHwgIWhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBrZXkpKSkpIHtcbiAgICAgIHJlc3VsdC5wdXNoKGtleSk7XG4gICAgfVxuICB9XG4gIGlmIChzdXBwb3J0Lm5vbkVudW1TaGFkb3dzICYmIG9iamVjdCAhPT0gb2JqZWN0UHJvdG8pIHtcbiAgICB2YXIgdGFnID0gb2JqZWN0ID09PSBzdHJpbmdQcm90byA/IHN0cmluZ1RhZyA6IChvYmplY3QgPT09IGVycm9yUHJvdG8gPyBlcnJvclRhZyA6IG9ialRvU3RyaW5nLmNhbGwob2JqZWN0KSksXG4gICAgICAgIG5vbkVudW1zID0gbm9uRW51bVByb3BzW3RhZ10gfHwgbm9uRW51bVByb3BzW29iamVjdFRhZ107XG5cbiAgICBpZiAodGFnID09IG9iamVjdFRhZykge1xuICAgICAgcHJvdG8gPSBvYmplY3RQcm90bztcbiAgICB9XG4gICAgbGVuZ3RoID0gc2hhZG93UHJvcHMubGVuZ3RoO1xuICAgIHdoaWxlIChsZW5ndGgtLSkge1xuICAgICAga2V5ID0gc2hhZG93UHJvcHNbbGVuZ3RoXTtcbiAgICAgIHZhciBub25FbnVtID0gbm9uRW51bXNba2V5XTtcbiAgICAgIGlmICghKGlzUHJvdG8gJiYgbm9uRW51bSkgJiZcbiAgICAgICAgICAobm9uRW51bSA/IGhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBrZXkpIDogb2JqZWN0W2tleV0gIT09IHByb3RvW2tleV0pKSB7XG4gICAgICAgIHJlc3VsdC5wdXNoKGtleSk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ga2V5c0luO1xuIiwidmFyIGJhc2VUb1N0cmluZyA9IHJlcXVpcmUoJy4uL2ludGVybmFsL2Jhc2VUb1N0cmluZycpO1xuXG4vKipcbiAqIFVzZWQgdG8gbWF0Y2ggYFJlZ0V4cGAgW3NwZWNpYWwgY2hhcmFjdGVyc10oaHR0cDovL3d3dy5yZWd1bGFyLWV4cHJlc3Npb25zLmluZm8vY2hhcmFjdGVycy5odG1sI3NwZWNpYWwpLlxuICogSW4gYWRkaXRpb24gdG8gc3BlY2lhbCBjaGFyYWN0ZXJzIHRoZSBmb3J3YXJkIHNsYXNoIGlzIGVzY2FwZWQgdG8gYWxsb3cgZm9yXG4gKiBlYXNpZXIgYGV2YWxgIHVzZSBhbmQgYEZ1bmN0aW9uYCBjb21waWxhdGlvbi5cbiAqL1xudmFyIHJlUmVnRXhwQ2hhcnMgPSAvWy4qKz9eJHt9KCl8W1xcXVxcL1xcXFxdL2csXG4gICAgcmVIYXNSZWdFeHBDaGFycyA9IFJlZ0V4cChyZVJlZ0V4cENoYXJzLnNvdXJjZSk7XG5cbi8qKlxuICogRXNjYXBlcyB0aGUgYFJlZ0V4cGAgc3BlY2lhbCBjaGFyYWN0ZXJzIFwiXFxcIiwgXCIvXCIsIFwiXlwiLCBcIiRcIiwgXCIuXCIsIFwifFwiLCBcIj9cIixcbiAqIFwiKlwiLCBcIitcIiwgXCIoXCIsIFwiKVwiLCBcIltcIiwgXCJdXCIsIFwie1wiIGFuZCBcIn1cIiBpbiBgc3RyaW5nYC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IFN0cmluZ1xuICogQHBhcmFtIHtzdHJpbmd9IFtzdHJpbmc9JyddIFRoZSBzdHJpbmcgdG8gZXNjYXBlLlxuICogQHJldHVybnMge3N0cmluZ30gUmV0dXJucyB0aGUgZXNjYXBlZCBzdHJpbmcuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZXNjYXBlUmVnRXhwKCdbbG9kYXNoXShodHRwczovL2xvZGFzaC5jb20vKScpO1xuICogLy8gPT4gJ1xcW2xvZGFzaFxcXVxcKGh0dHBzOlxcL1xcL2xvZGFzaFxcLmNvbVxcL1xcKSdcbiAqL1xuZnVuY3Rpb24gZXNjYXBlUmVnRXhwKHN0cmluZykge1xuICBzdHJpbmcgPSBiYXNlVG9TdHJpbmcoc3RyaW5nKTtcbiAgcmV0dXJuIChzdHJpbmcgJiYgcmVIYXNSZWdFeHBDaGFycy50ZXN0KHN0cmluZykpXG4gICAgPyBzdHJpbmcucmVwbGFjZShyZVJlZ0V4cENoYXJzLCAnXFxcXCQmJylcbiAgICA6IHN0cmluZztcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBlc2NhcGVSZWdFeHA7XG4iLCIvKiogYE9iamVjdCN0b1N0cmluZ2AgcmVzdWx0IHJlZmVyZW5jZXMuICovXG52YXIgYXJnc1RhZyA9ICdbb2JqZWN0IEFyZ3VtZW50c10nLFxuICAgIG9iamVjdFRhZyA9ICdbb2JqZWN0IE9iamVjdF0nO1xuXG4vKiogVXNlZCBmb3IgbmF0aXZlIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIGFycmF5UHJvdG8gPSBBcnJheS5wcm90b3R5cGUsXG4gICAgZXJyb3JQcm90byA9IEVycm9yLnByb3RvdHlwZSxcbiAgICBvYmplY3RQcm90byA9IE9iamVjdC5wcm90b3R5cGU7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBET00gc3VwcG9ydC4gKi9cbnZhciBkb2N1bWVudCA9IChkb2N1bWVudCA9IGdsb2JhbC53aW5kb3cpICYmIGRvY3VtZW50LmRvY3VtZW50O1xuXG4vKipcbiAqIFVzZWQgdG8gcmVzb2x2ZSB0aGUgW2B0b1N0cmluZ1RhZ2BdKGh0dHBzOi8vcGVvcGxlLm1vemlsbGEub3JnL35qb3JlbmRvcmZmL2VzNi1kcmFmdC5odG1sI3NlYy1vYmplY3QucHJvdG90eXBlLnRvc3RyaW5nKVxuICogb2YgdmFsdWVzLlxuICovXG52YXIgb2JqVG9TdHJpbmcgPSBvYmplY3RQcm90by50b1N0cmluZztcblxuLyoqIE5hdGl2ZSBtZXRob2QgcmVmZXJlbmNlcy4gKi9cbnZhciBwcm9wZXJ0eUlzRW51bWVyYWJsZSA9IG9iamVjdFByb3RvLnByb3BlcnR5SXNFbnVtZXJhYmxlLFxuICAgIHNwbGljZSA9IGFycmF5UHJvdG8uc3BsaWNlO1xuXG4vKipcbiAqIEFuIG9iamVjdCBlbnZpcm9ubWVudCBmZWF0dXJlIGZsYWdzLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAdHlwZSBPYmplY3RcbiAqL1xudmFyIHN1cHBvcnQgPSB7fTtcblxuKGZ1bmN0aW9uKHgpIHtcbiAgdmFyIEN0b3IgPSBmdW5jdGlvbigpIHsgdGhpcy54ID0gMTsgfSxcbiAgICAgIG9iamVjdCA9IHsgJzAnOiAxLCAnbGVuZ3RoJzogMSB9LFxuICAgICAgcHJvcHMgPSBbXTtcblxuICBDdG9yLnByb3RvdHlwZSA9IHsgJ3ZhbHVlT2YnOiAxLCAneSc6IDEgfTtcbiAgZm9yICh2YXIga2V5IGluIG5ldyBDdG9yKSB7IHByb3BzLnB1c2goa2V5KTsgfVxuXG4gIC8qKlxuICAgKiBEZXRlY3QgaWYgdGhlIGB0b1N0cmluZ1RhZ2Agb2YgYGFyZ3VtZW50c2Agb2JqZWN0cyBpcyByZXNvbHZhYmxlXG4gICAqIChhbGwgYnV0IEZpcmVmb3ggPCA0LCBJRSA8IDkpLlxuICAgKlxuICAgKiBAbWVtYmVyT2YgXy5zdXBwb3J0XG4gICAqIEB0eXBlIGJvb2xlYW5cbiAgICovXG4gIHN1cHBvcnQuYXJnc1RhZyA9IG9ialRvU3RyaW5nLmNhbGwoYXJndW1lbnRzKSA9PSBhcmdzVGFnO1xuXG4gIC8qKlxuICAgKiBEZXRlY3QgaWYgYG5hbWVgIG9yIGBtZXNzYWdlYCBwcm9wZXJ0aWVzIG9mIGBFcnJvci5wcm90b3R5cGVgIGFyZVxuICAgKiBlbnVtZXJhYmxlIGJ5IGRlZmF1bHQgKElFIDwgOSwgU2FmYXJpIDwgNS4xKS5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICBzdXBwb3J0LmVudW1FcnJvclByb3BzID0gcHJvcGVydHlJc0VudW1lcmFibGUuY2FsbChlcnJvclByb3RvLCAnbWVzc2FnZScpIHx8XG4gICAgcHJvcGVydHlJc0VudW1lcmFibGUuY2FsbChlcnJvclByb3RvLCAnbmFtZScpO1xuXG4gIC8qKlxuICAgKiBEZXRlY3QgaWYgYHByb3RvdHlwZWAgcHJvcGVydGllcyBhcmUgZW51bWVyYWJsZSBieSBkZWZhdWx0LlxuICAgKlxuICAgKiBGaXJlZm94IDwgMy42LCBPcGVyYSA+IDkuNTAgLSBPcGVyYSA8IDExLjYwLCBhbmQgU2FmYXJpIDwgNS4xXG4gICAqIChpZiB0aGUgcHJvdG90eXBlIG9yIGEgcHJvcGVydHkgb24gdGhlIHByb3RvdHlwZSBoYXMgYmVlbiBzZXQpXG4gICAqIGluY29ycmVjdGx5IHNldCB0aGUgYFtbRW51bWVyYWJsZV1dYCB2YWx1ZSBvZiBhIGZ1bmN0aW9uJ3MgYHByb3RvdHlwZWBcbiAgICogcHJvcGVydHkgdG8gYHRydWVgLlxuICAgKlxuICAgKiBAbWVtYmVyT2YgXy5zdXBwb3J0XG4gICAqIEB0eXBlIGJvb2xlYW5cbiAgICovXG4gIHN1cHBvcnQuZW51bVByb3RvdHlwZXMgPSBwcm9wZXJ0eUlzRW51bWVyYWJsZS5jYWxsKEN0b3IsICdwcm90b3R5cGUnKTtcblxuICAvKipcbiAgICogRGV0ZWN0IGlmIGZ1bmN0aW9ucyBjYW4gYmUgZGVjb21waWxlZCBieSBgRnVuY3Rpb24jdG9TdHJpbmdgXG4gICAqIChhbGwgYnV0IEZpcmVmb3ggT1MgY2VydGlmaWVkIGFwcHMsIG9sZGVyIE9wZXJhIG1vYmlsZSBicm93c2VycywgYW5kXG4gICAqIHRoZSBQbGF5U3RhdGlvbiAzOyBmb3JjZWQgYGZhbHNlYCBmb3IgV2luZG93cyA4IGFwcHMpLlxuICAgKlxuICAgKiBAbWVtYmVyT2YgXy5zdXBwb3J0XG4gICAqIEB0eXBlIGJvb2xlYW5cbiAgICovXG4gIHN1cHBvcnQuZnVuY0RlY29tcCA9IC9cXGJ0aGlzXFxiLy50ZXN0KGZ1bmN0aW9uKCkgeyByZXR1cm4gdGhpczsgfSk7XG5cbiAgLyoqXG4gICAqIERldGVjdCBpZiBgRnVuY3Rpb24jbmFtZWAgaXMgc3VwcG9ydGVkIChhbGwgYnV0IElFKS5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICBzdXBwb3J0LmZ1bmNOYW1lcyA9IHR5cGVvZiBGdW5jdGlvbi5uYW1lID09ICdzdHJpbmcnO1xuXG4gIC8qKlxuICAgKiBEZXRlY3QgaWYgdGhlIGB0b1N0cmluZ1RhZ2Agb2YgRE9NIG5vZGVzIGlzIHJlc29sdmFibGUgKGFsbCBidXQgSUUgPCA5KS5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICBzdXBwb3J0Lm5vZGVUYWcgPSBvYmpUb1N0cmluZy5jYWxsKGRvY3VtZW50KSAhPSBvYmplY3RUYWc7XG5cbiAgLyoqXG4gICAqIERldGVjdCBpZiBzdHJpbmcgaW5kZXhlcyBhcmUgbm9uLWVudW1lcmFibGVcbiAgICogKElFIDwgOSwgUmluZ29KUywgUmhpbm8sIE5hcndoYWwpLlxuICAgKlxuICAgKiBAbWVtYmVyT2YgXy5zdXBwb3J0XG4gICAqIEB0eXBlIGJvb2xlYW5cbiAgICovXG4gIHN1cHBvcnQubm9uRW51bVN0cmluZ3MgPSAhcHJvcGVydHlJc0VudW1lcmFibGUuY2FsbCgneCcsIDApO1xuXG4gIC8qKlxuICAgKiBEZXRlY3QgaWYgcHJvcGVydGllcyBzaGFkb3dpbmcgdGhvc2Ugb24gYE9iamVjdC5wcm90b3R5cGVgIGFyZVxuICAgKiBub24tZW51bWVyYWJsZS5cbiAgICpcbiAgICogSW4gSUUgPCA5IGFuIG9iamVjdCdzIG93biBwcm9wZXJ0aWVzLCBzaGFkb3dpbmcgbm9uLWVudW1lcmFibGUgb25lcyxcbiAgICogYXJlIG1hZGUgbm9uLWVudW1lcmFibGUgYXMgd2VsbCAoYS5rLmEgdGhlIEpTY3JpcHQgYFtbRG9udEVudW1dXWAgYnVnKS5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICBzdXBwb3J0Lm5vbkVudW1TaGFkb3dzID0gIS92YWx1ZU9mLy50ZXN0KHByb3BzKTtcblxuICAvKipcbiAgICogRGV0ZWN0IGlmIG93biBwcm9wZXJ0aWVzIGFyZSBpdGVyYXRlZCBhZnRlciBpbmhlcml0ZWQgcHJvcGVydGllcyAoSUUgPCA5KS5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICBzdXBwb3J0Lm93bkxhc3QgPSBwcm9wc1swXSAhPSAneCc7XG5cbiAgLyoqXG4gICAqIERldGVjdCBpZiBgQXJyYXkjc2hpZnRgIGFuZCBgQXJyYXkjc3BsaWNlYCBhdWdtZW50IGFycmF5LWxpa2Ugb2JqZWN0c1xuICAgKiBjb3JyZWN0bHkuXG4gICAqXG4gICAqIEZpcmVmb3ggPCAxMCwgY29tcGF0aWJpbGl0eSBtb2RlcyBvZiBJRSA4LCBhbmQgSUUgPCA5IGhhdmUgYnVnZ3kgQXJyYXkgYHNoaWZ0KClgXG4gICAqIGFuZCBgc3BsaWNlKClgIGZ1bmN0aW9ucyB0aGF0IGZhaWwgdG8gcmVtb3ZlIHRoZSBsYXN0IGVsZW1lbnQsIGB2YWx1ZVswXWAsXG4gICAqIG9mIGFycmF5LWxpa2Ugb2JqZWN0cyBldmVuIHRob3VnaCB0aGUgYGxlbmd0aGAgcHJvcGVydHkgaXMgc2V0IHRvIGAwYC5cbiAgICogVGhlIGBzaGlmdCgpYCBtZXRob2QgaXMgYnVnZ3kgaW4gY29tcGF0aWJpbGl0eSBtb2RlcyBvZiBJRSA4LCB3aGlsZSBgc3BsaWNlKClgXG4gICAqIGlzIGJ1Z2d5IHJlZ2FyZGxlc3Mgb2YgbW9kZSBpbiBJRSA8IDkuXG4gICAqXG4gICAqIEBtZW1iZXJPZiBfLnN1cHBvcnRcbiAgICogQHR5cGUgYm9vbGVhblxuICAgKi9cbiAgc3VwcG9ydC5zcGxpY2VPYmplY3RzID0gKHNwbGljZS5jYWxsKG9iamVjdCwgMCwgMSksICFvYmplY3RbMF0pO1xuXG4gIC8qKlxuICAgKiBEZXRlY3QgbGFjayBvZiBzdXBwb3J0IGZvciBhY2Nlc3Npbmcgc3RyaW5nIGNoYXJhY3RlcnMgYnkgaW5kZXguXG4gICAqXG4gICAqIElFIDwgOCBjYW4ndCBhY2Nlc3MgY2hhcmFjdGVycyBieSBpbmRleC4gSUUgOCBjYW4gb25seSBhY2Nlc3MgY2hhcmFjdGVyc1xuICAgKiBieSBpbmRleCBvbiBzdHJpbmcgbGl0ZXJhbHMsIG5vdCBzdHJpbmcgb2JqZWN0cy5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICBzdXBwb3J0LnVuaW5kZXhlZENoYXJzID0gKCd4J1swXSArIE9iamVjdCgneCcpWzBdKSAhPSAneHgnO1xuXG4gIC8qKlxuICAgKiBEZXRlY3QgaWYgdGhlIERPTSBpcyBzdXBwb3J0ZWQuXG4gICAqXG4gICAqIEBtZW1iZXJPZiBfLnN1cHBvcnRcbiAgICogQHR5cGUgYm9vbGVhblxuICAgKi9cbiAgdHJ5IHtcbiAgICBzdXBwb3J0LmRvbSA9IGRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKS5ub2RlVHlwZSA9PT0gMTE7XG4gIH0gY2F0Y2goZSkge1xuICAgIHN1cHBvcnQuZG9tID0gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZWN0IGlmIGBhcmd1bWVudHNgIG9iamVjdCBpbmRleGVzIGFyZSBub24tZW51bWVyYWJsZS5cbiAgICpcbiAgICogSW4gRmlyZWZveCA8IDQsIElFIDwgOSwgUGhhbnRvbUpTLCBhbmQgU2FmYXJpIDwgNS4xIGBhcmd1bWVudHNgIG9iamVjdFxuICAgKiBpbmRleGVzIGFyZSBub24tZW51bWVyYWJsZS4gQ2hyb21lIDwgMjUgYW5kIE5vZGUuanMgPCAwLjExLjAgdHJlYXRcbiAgICogYGFyZ3VtZW50c2Agb2JqZWN0IGluZGV4ZXMgYXMgbm9uLWVudW1lcmFibGUgYW5kIGZhaWwgYGhhc093blByb3BlcnR5YFxuICAgKiBjaGVja3MgZm9yIGluZGV4ZXMgdGhhdCBleGNlZWQgdGhlaXIgZnVuY3Rpb24ncyBmb3JtYWwgcGFyYW1ldGVycyB3aXRoXG4gICAqIGFzc29jaWF0ZWQgdmFsdWVzIG9mIGAwYC5cbiAgICpcbiAgICogQG1lbWJlck9mIF8uc3VwcG9ydFxuICAgKiBAdHlwZSBib29sZWFuXG4gICAqL1xuICB0cnkge1xuICAgIHN1cHBvcnQubm9uRW51bUFyZ3MgPSAhcHJvcGVydHlJc0VudW1lcmFibGUuY2FsbChhcmd1bWVudHMsIDEpO1xuICB9IGNhdGNoKGUpIHtcbiAgICBzdXBwb3J0Lm5vbkVudW1BcmdzID0gdHJ1ZTtcbiAgfVxufSgwLCAwKSk7XG5cbm1vZHVsZS5leHBvcnRzID0gc3VwcG9ydDtcbiIsIi8qKlxuICogQ3JlYXRlcyBhIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBgdmFsdWVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAY2F0ZWdvcnkgVXRpbGl0eVxuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gcmV0dXJuIGZyb20gdGhlIG5ldyBmdW5jdGlvbi5cbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gUmV0dXJucyB0aGUgbmV3IGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiB2YXIgb2JqZWN0ID0geyAndXNlcic6ICdmcmVkJyB9O1xuICogdmFyIGdldHRlciA9IF8uY29uc3RhbnQob2JqZWN0KTtcbiAqXG4gKiBnZXR0ZXIoKSA9PT0gb2JqZWN0O1xuICogLy8gPT4gdHJ1ZVxuICovXG5mdW5jdGlvbiBjb25zdGFudCh2YWx1ZSkge1xuICByZXR1cm4gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGNvbnN0YW50O1xuIiwiLyoqXG4gKiBUaGlzIG1ldGhvZCByZXR1cm5zIHRoZSBmaXJzdCBhcmd1bWVudCBwcm92aWRlZCB0byBpdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQGNhdGVnb3J5IFV0aWxpdHlcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgQW55IHZhbHVlLlxuICogQHJldHVybnMgeyp9IFJldHVybnMgYHZhbHVlYC5cbiAqIEBleGFtcGxlXG4gKlxuICogdmFyIG9iamVjdCA9IHsgJ3VzZXInOiAnZnJlZCcgfTtcbiAqXG4gKiBfLmlkZW50aXR5KG9iamVjdCkgPT09IG9iamVjdDtcbiAqIC8vID0+IHRydWVcbiAqL1xuZnVuY3Rpb24gaWRlbnRpdHkodmFsdWUpIHtcbiAgcmV0dXJuIHZhbHVlO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGlkZW50aXR5O1xuIiwiLyoqXG4gKiBBIG5vLW9wZXJhdGlvbiBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGB1bmRlZmluZWRgIHJlZ2FyZGxlc3Mgb2YgdGhlXG4gKiBhcmd1bWVudHMgaXQgcmVjZWl2ZXMuXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBjYXRlZ29yeSBVdGlsaXR5XG4gKiBAZXhhbXBsZVxuICpcbiAqIHZhciBvYmplY3QgPSB7ICd1c2VyJzogJ2ZyZWQnIH07XG4gKlxuICogXy5ub29wKG9iamVjdCkgPT09IHVuZGVmaW5lZDtcbiAqIC8vID0+IHRydWVcbiAqL1xuZnVuY3Rpb24gbm9vcCgpIHtcbiAgLy8gTm8gb3BlcmF0aW9uIHBlcmZvcm1lZC5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBub29wO1xuIiwiLyoqXG4gKiBNb2R1bGUgZGVwZW5kZW5jaWVzLlxuICovXG5cbnZhciBFbWl0dGVyID0gcmVxdWlyZSgnZW1pdHRlcicpO1xudmFyIHJlZHVjZSA9IHJlcXVpcmUoJ3JlZHVjZScpO1xuXG4vKipcbiAqIFJvb3QgcmVmZXJlbmNlIGZvciBpZnJhbWVzLlxuICovXG5cbnZhciByb290ID0gJ3VuZGVmaW5lZCcgPT0gdHlwZW9mIHdpbmRvd1xuICA/IHRoaXNcbiAgOiB3aW5kb3c7XG5cbi8qKlxuICogTm9vcC5cbiAqL1xuXG5mdW5jdGlvbiBub29wKCl7fTtcblxuLyoqXG4gKiBDaGVjayBpZiBgb2JqYCBpcyBhIGhvc3Qgb2JqZWN0LFxuICogd2UgZG9uJ3Qgd2FudCB0byBzZXJpYWxpemUgdGhlc2UgOilcbiAqXG4gKiBUT0RPOiBmdXR1cmUgcHJvb2YsIG1vdmUgdG8gY29tcG9lbnQgbGFuZFxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmpcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuXG5mdW5jdGlvbiBpc0hvc3Qob2JqKSB7XG4gIHZhciBzdHIgPSB7fS50b1N0cmluZy5jYWxsKG9iaik7XG5cbiAgc3dpdGNoIChzdHIpIHtcbiAgICBjYXNlICdbb2JqZWN0IEZpbGVdJzpcbiAgICBjYXNlICdbb2JqZWN0IEJsb2JdJzpcbiAgICBjYXNlICdbb2JqZWN0IEZvcm1EYXRhXSc6XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogRGV0ZXJtaW5lIFhIUi5cbiAqL1xuXG5mdW5jdGlvbiBnZXRYSFIoKSB7XG4gIGlmIChyb290LlhNTEh0dHBSZXF1ZXN0XG4gICAgJiYgKCdmaWxlOicgIT0gcm9vdC5sb2NhdGlvbi5wcm90b2NvbCB8fCAhcm9vdC5BY3RpdmVYT2JqZWN0KSkge1xuICAgIHJldHVybiBuZXcgWE1MSHR0cFJlcXVlc3Q7XG4gIH0gZWxzZSB7XG4gICAgdHJ5IHsgcmV0dXJuIG5ldyBBY3RpdmVYT2JqZWN0KCdNaWNyb3NvZnQuWE1MSFRUUCcpOyB9IGNhdGNoKGUpIHt9XG4gICAgdHJ5IHsgcmV0dXJuIG5ldyBBY3RpdmVYT2JqZWN0KCdNc3htbDIuWE1MSFRUUC42LjAnKTsgfSBjYXRjaChlKSB7fVxuICAgIHRyeSB7IHJldHVybiBuZXcgQWN0aXZlWE9iamVjdCgnTXN4bWwyLlhNTEhUVFAuMy4wJyk7IH0gY2F0Y2goZSkge31cbiAgICB0cnkgeyByZXR1cm4gbmV3IEFjdGl2ZVhPYmplY3QoJ01zeG1sMi5YTUxIVFRQJyk7IH0gY2F0Y2goZSkge31cbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogUmVtb3ZlcyBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLCBhZGRlZCB0byBzdXBwb3J0IElFLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBzXG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuXG52YXIgdHJpbSA9ICcnLnRyaW1cbiAgPyBmdW5jdGlvbihzKSB7IHJldHVybiBzLnRyaW0oKTsgfVxuICA6IGZ1bmN0aW9uKHMpIHsgcmV0dXJuIHMucmVwbGFjZSgvKF5cXHMqfFxccyokKS9nLCAnJyk7IH07XG5cbi8qKlxuICogQ2hlY2sgaWYgYG9iamAgaXMgYW4gb2JqZWN0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmpcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuXG5mdW5jdGlvbiBpc09iamVjdChvYmopIHtcbiAgcmV0dXJuIG9iaiA9PT0gT2JqZWN0KG9iaik7XG59XG5cbi8qKlxuICogU2VyaWFsaXplIHRoZSBnaXZlbiBgb2JqYC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqXG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuXG5mdW5jdGlvbiBzZXJpYWxpemUob2JqKSB7XG4gIGlmICghaXNPYmplY3Qob2JqKSkgcmV0dXJuIG9iajtcbiAgdmFyIHBhaXJzID0gW107XG4gIGZvciAodmFyIGtleSBpbiBvYmopIHtcbiAgICBpZiAobnVsbCAhPSBvYmpba2V5XSkge1xuICAgICAgcGFpcnMucHVzaChlbmNvZGVVUklDb21wb25lbnQoa2V5KVxuICAgICAgICArICc9JyArIGVuY29kZVVSSUNvbXBvbmVudChvYmpba2V5XSkpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gcGFpcnMuam9pbignJicpO1xufVxuXG4vKipcbiAqIEV4cG9zZSBzZXJpYWxpemF0aW9uIG1ldGhvZC5cbiAqL1xuXG4gcmVxdWVzdC5zZXJpYWxpemVPYmplY3QgPSBzZXJpYWxpemU7XG5cbiAvKipcbiAgKiBQYXJzZSB0aGUgZ2l2ZW4geC13d3ctZm9ybS11cmxlbmNvZGVkIGBzdHJgLlxuICAqXG4gICogQHBhcmFtIHtTdHJpbmd9IHN0clxuICAqIEByZXR1cm4ge09iamVjdH1cbiAgKiBAYXBpIHByaXZhdGVcbiAgKi9cblxuZnVuY3Rpb24gcGFyc2VTdHJpbmcoc3RyKSB7XG4gIHZhciBvYmogPSB7fTtcbiAgdmFyIHBhaXJzID0gc3RyLnNwbGl0KCcmJyk7XG4gIHZhciBwYXJ0cztcbiAgdmFyIHBhaXI7XG5cbiAgZm9yICh2YXIgaSA9IDAsIGxlbiA9IHBhaXJzLmxlbmd0aDsgaSA8IGxlbjsgKytpKSB7XG4gICAgcGFpciA9IHBhaXJzW2ldO1xuICAgIHBhcnRzID0gcGFpci5zcGxpdCgnPScpO1xuICAgIG9ialtkZWNvZGVVUklDb21wb25lbnQocGFydHNbMF0pXSA9IGRlY29kZVVSSUNvbXBvbmVudChwYXJ0c1sxXSk7XG4gIH1cblxuICByZXR1cm4gb2JqO1xufVxuXG4vKipcbiAqIEV4cG9zZSBwYXJzZXIuXG4gKi9cblxucmVxdWVzdC5wYXJzZVN0cmluZyA9IHBhcnNlU3RyaW5nO1xuXG4vKipcbiAqIERlZmF1bHQgTUlNRSB0eXBlIG1hcC5cbiAqXG4gKiAgICAgc3VwZXJhZ2VudC50eXBlcy54bWwgPSAnYXBwbGljYXRpb24veG1sJztcbiAqXG4gKi9cblxucmVxdWVzdC50eXBlcyA9IHtcbiAgaHRtbDogJ3RleHQvaHRtbCcsXG4gIGpzb246ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgeG1sOiAnYXBwbGljYXRpb24veG1sJyxcbiAgdXJsZW5jb2RlZDogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcsXG4gICdmb3JtJzogJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcsXG4gICdmb3JtLWRhdGEnOiAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJ1xufTtcblxuLyoqXG4gKiBEZWZhdWx0IHNlcmlhbGl6YXRpb24gbWFwLlxuICpcbiAqICAgICBzdXBlcmFnZW50LnNlcmlhbGl6ZVsnYXBwbGljYXRpb24veG1sJ10gPSBmdW5jdGlvbihvYmope1xuICogICAgICAgcmV0dXJuICdnZW5lcmF0ZWQgeG1sIGhlcmUnO1xuICogICAgIH07XG4gKlxuICovXG5cbiByZXF1ZXN0LnNlcmlhbGl6ZSA9IHtcbiAgICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnOiBzZXJpYWxpemUsXG4gICAnYXBwbGljYXRpb24vanNvbic6IEpTT04uc3RyaW5naWZ5XG4gfTtcblxuIC8qKlxuICAqIERlZmF1bHQgcGFyc2Vycy5cbiAgKlxuICAqICAgICBzdXBlcmFnZW50LnBhcnNlWydhcHBsaWNhdGlvbi94bWwnXSA9IGZ1bmN0aW9uKHN0cil7XG4gICogICAgICAgcmV0dXJuIHsgb2JqZWN0IHBhcnNlZCBmcm9tIHN0ciB9O1xuICAqICAgICB9O1xuICAqXG4gICovXG5cbnJlcXVlc3QucGFyc2UgPSB7XG4gICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnOiBwYXJzZVN0cmluZyxcbiAgJ2FwcGxpY2F0aW9uL2pzb24nOiBKU09OLnBhcnNlXG59O1xuXG4vKipcbiAqIFBhcnNlIHRoZSBnaXZlbiBoZWFkZXIgYHN0cmAgaW50b1xuICogYW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIG1hcHBlZCBmaWVsZHMuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IHN0clxuICogQHJldHVybiB7T2JqZWN0fVxuICogQGFwaSBwcml2YXRlXG4gKi9cblxuZnVuY3Rpb24gcGFyc2VIZWFkZXIoc3RyKSB7XG4gIHZhciBsaW5lcyA9IHN0ci5zcGxpdCgvXFxyP1xcbi8pO1xuICB2YXIgZmllbGRzID0ge307XG4gIHZhciBpbmRleDtcbiAgdmFyIGxpbmU7XG4gIHZhciBmaWVsZDtcbiAgdmFyIHZhbDtcblxuICBsaW5lcy5wb3AoKTsgLy8gdHJhaWxpbmcgQ1JMRlxuXG4gIGZvciAodmFyIGkgPSAwLCBsZW4gPSBsaW5lcy5sZW5ndGg7IGkgPCBsZW47ICsraSkge1xuICAgIGxpbmUgPSBsaW5lc1tpXTtcbiAgICBpbmRleCA9IGxpbmUuaW5kZXhPZignOicpO1xuICAgIGZpZWxkID0gbGluZS5zbGljZSgwLCBpbmRleCkudG9Mb3dlckNhc2UoKTtcbiAgICB2YWwgPSB0cmltKGxpbmUuc2xpY2UoaW5kZXggKyAxKSk7XG4gICAgZmllbGRzW2ZpZWxkXSA9IHZhbDtcbiAgfVxuXG4gIHJldHVybiBmaWVsZHM7XG59XG5cbi8qKlxuICogUmV0dXJuIHRoZSBtaW1lIHR5cGUgZm9yIHRoZSBnaXZlbiBgc3RyYC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gc3RyXG4gKiBAcmV0dXJuIHtTdHJpbmd9XG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuXG5mdW5jdGlvbiB0eXBlKHN0cil7XG4gIHJldHVybiBzdHIuc3BsaXQoLyAqOyAqLykuc2hpZnQoKTtcbn07XG5cbi8qKlxuICogUmV0dXJuIGhlYWRlciBmaWVsZCBwYXJhbWV0ZXJzLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBzdHJcbiAqIEByZXR1cm4ge09iamVjdH1cbiAqIEBhcGkgcHJpdmF0ZVxuICovXG5cbmZ1bmN0aW9uIHBhcmFtcyhzdHIpe1xuICByZXR1cm4gcmVkdWNlKHN0ci5zcGxpdCgvICo7ICovKSwgZnVuY3Rpb24ob2JqLCBzdHIpe1xuICAgIHZhciBwYXJ0cyA9IHN0ci5zcGxpdCgvICo9ICovKVxuICAgICAgLCBrZXkgPSBwYXJ0cy5zaGlmdCgpXG4gICAgICAsIHZhbCA9IHBhcnRzLnNoaWZ0KCk7XG5cbiAgICBpZiAoa2V5ICYmIHZhbCkgb2JqW2tleV0gPSB2YWw7XG4gICAgcmV0dXJuIG9iajtcbiAgfSwge30pO1xufTtcblxuLyoqXG4gKiBJbml0aWFsaXplIGEgbmV3IGBSZXNwb25zZWAgd2l0aCB0aGUgZ2l2ZW4gYHhocmAuXG4gKlxuICogIC0gc2V0IGZsYWdzICgub2ssIC5lcnJvciwgZXRjKVxuICogIC0gcGFyc2UgaGVhZGVyXG4gKlxuICogRXhhbXBsZXM6XG4gKlxuICogIEFsaWFzaW5nIGBzdXBlcmFnZW50YCBhcyBgcmVxdWVzdGAgaXMgbmljZTpcbiAqXG4gKiAgICAgIHJlcXVlc3QgPSBzdXBlcmFnZW50O1xuICpcbiAqICBXZSBjYW4gdXNlIHRoZSBwcm9taXNlLWxpa2UgQVBJLCBvciBwYXNzIGNhbGxiYWNrczpcbiAqXG4gKiAgICAgIHJlcXVlc3QuZ2V0KCcvJykuZW5kKGZ1bmN0aW9uKHJlcyl7fSk7XG4gKiAgICAgIHJlcXVlc3QuZ2V0KCcvJywgZnVuY3Rpb24ocmVzKXt9KTtcbiAqXG4gKiAgU2VuZGluZyBkYXRhIGNhbiBiZSBjaGFpbmVkOlxuICpcbiAqICAgICAgcmVxdWVzdFxuICogICAgICAgIC5wb3N0KCcvdXNlcicpXG4gKiAgICAgICAgLnNlbmQoeyBuYW1lOiAndGonIH0pXG4gKiAgICAgICAgLmVuZChmdW5jdGlvbihyZXMpe30pO1xuICpcbiAqICBPciBwYXNzZWQgdG8gYC5zZW5kKClgOlxuICpcbiAqICAgICAgcmVxdWVzdFxuICogICAgICAgIC5wb3N0KCcvdXNlcicpXG4gKiAgICAgICAgLnNlbmQoeyBuYW1lOiAndGonIH0sIGZ1bmN0aW9uKHJlcyl7fSk7XG4gKlxuICogIE9yIHBhc3NlZCB0byBgLnBvc3QoKWA6XG4gKlxuICogICAgICByZXF1ZXN0XG4gKiAgICAgICAgLnBvc3QoJy91c2VyJywgeyBuYW1lOiAndGonIH0pXG4gKiAgICAgICAgLmVuZChmdW5jdGlvbihyZXMpe30pO1xuICpcbiAqIE9yIGZ1cnRoZXIgcmVkdWNlZCB0byBhIHNpbmdsZSBjYWxsIGZvciBzaW1wbGUgY2FzZXM6XG4gKlxuICogICAgICByZXF1ZXN0XG4gKiAgICAgICAgLnBvc3QoJy91c2VyJywgeyBuYW1lOiAndGonIH0sIGZ1bmN0aW9uKHJlcyl7fSk7XG4gKlxuICogQHBhcmFtIHtYTUxIVFRQUmVxdWVzdH0geGhyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9uc1xuICogQGFwaSBwcml2YXRlXG4gKi9cblxuZnVuY3Rpb24gUmVzcG9uc2UocmVxLCBvcHRpb25zKSB7XG4gIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuICB0aGlzLnJlcSA9IHJlcTtcbiAgdGhpcy54aHIgPSB0aGlzLnJlcS54aHI7XG4gIHRoaXMudGV4dCA9IHRoaXMucmVxLm1ldGhvZCAhPSdIRUFEJyBcbiAgICAgPyB0aGlzLnhoci5yZXNwb25zZVRleHQgXG4gICAgIDogbnVsbDtcbiAgdGhpcy5zZXRTdGF0dXNQcm9wZXJ0aWVzKHRoaXMueGhyLnN0YXR1cyk7XG4gIHRoaXMuaGVhZGVyID0gdGhpcy5oZWFkZXJzID0gcGFyc2VIZWFkZXIodGhpcy54aHIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkpO1xuICAvLyBnZXRBbGxSZXNwb25zZUhlYWRlcnMgc29tZXRpbWVzIGZhbHNlbHkgcmV0dXJucyBcIlwiIGZvciBDT1JTIHJlcXVlc3RzLCBidXRcbiAgLy8gZ2V0UmVzcG9uc2VIZWFkZXIgc3RpbGwgd29ya3MuIHNvIHdlIGdldCBjb250ZW50LXR5cGUgZXZlbiBpZiBnZXR0aW5nXG4gIC8vIG90aGVyIGhlYWRlcnMgZmFpbHMuXG4gIHRoaXMuaGVhZGVyWydjb250ZW50LXR5cGUnXSA9IHRoaXMueGhyLmdldFJlc3BvbnNlSGVhZGVyKCdjb250ZW50LXR5cGUnKTtcbiAgdGhpcy5zZXRIZWFkZXJQcm9wZXJ0aWVzKHRoaXMuaGVhZGVyKTtcbiAgdGhpcy5ib2R5ID0gdGhpcy5yZXEubWV0aG9kICE9ICdIRUFEJ1xuICAgID8gdGhpcy5wYXJzZUJvZHkodGhpcy50ZXh0KVxuICAgIDogbnVsbDtcbn1cblxuLyoqXG4gKiBHZXQgY2FzZS1pbnNlbnNpdGl2ZSBgZmllbGRgIHZhbHVlLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBmaWVsZFxuICogQHJldHVybiB7U3RyaW5nfVxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5SZXNwb25zZS5wcm90b3R5cGUuZ2V0ID0gZnVuY3Rpb24oZmllbGQpe1xuICByZXR1cm4gdGhpcy5oZWFkZXJbZmllbGQudG9Mb3dlckNhc2UoKV07XG59O1xuXG4vKipcbiAqIFNldCBoZWFkZXIgcmVsYXRlZCBwcm9wZXJ0aWVzOlxuICpcbiAqICAgLSBgLnR5cGVgIHRoZSBjb250ZW50IHR5cGUgd2l0aG91dCBwYXJhbXNcbiAqXG4gKiBBIHJlc3BvbnNlIG9mIFwiQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PXV0Zi04XCJcbiAqIHdpbGwgcHJvdmlkZSB5b3Ugd2l0aCBhIGAudHlwZWAgb2YgXCJ0ZXh0L3BsYWluXCIuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGhlYWRlclxuICogQGFwaSBwcml2YXRlXG4gKi9cblxuUmVzcG9uc2UucHJvdG90eXBlLnNldEhlYWRlclByb3BlcnRpZXMgPSBmdW5jdGlvbihoZWFkZXIpe1xuICAvLyBjb250ZW50LXR5cGVcbiAgdmFyIGN0ID0gdGhpcy5oZWFkZXJbJ2NvbnRlbnQtdHlwZSddIHx8ICcnO1xuICB0aGlzLnR5cGUgPSB0eXBlKGN0KTtcblxuICAvLyBwYXJhbXNcbiAgdmFyIG9iaiA9IHBhcmFtcyhjdCk7XG4gIGZvciAodmFyIGtleSBpbiBvYmopIHRoaXNba2V5XSA9IG9ialtrZXldO1xufTtcblxuLyoqXG4gKiBQYXJzZSB0aGUgZ2l2ZW4gYm9keSBgc3RyYC5cbiAqXG4gKiBVc2VkIGZvciBhdXRvLXBhcnNpbmcgb2YgYm9kaWVzLiBQYXJzZXJzXG4gKiBhcmUgZGVmaW5lZCBvbiB0aGUgYHN1cGVyYWdlbnQucGFyc2VgIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gc3RyXG4gKiBAcmV0dXJuIHtNaXhlZH1cbiAqIEBhcGkgcHJpdmF0ZVxuICovXG5cblJlc3BvbnNlLnByb3RvdHlwZS5wYXJzZUJvZHkgPSBmdW5jdGlvbihzdHIpe1xuICB2YXIgcGFyc2UgPSByZXF1ZXN0LnBhcnNlW3RoaXMudHlwZV07XG4gIHJldHVybiBwYXJzZSAmJiBzdHIgJiYgc3RyLmxlbmd0aFxuICAgID8gcGFyc2Uoc3RyKVxuICAgIDogbnVsbDtcbn07XG5cbi8qKlxuICogU2V0IGZsYWdzIHN1Y2ggYXMgYC5va2AgYmFzZWQgb24gYHN0YXR1c2AuXG4gKlxuICogRm9yIGV4YW1wbGUgYSAyeHggcmVzcG9uc2Ugd2lsbCBnaXZlIHlvdSBhIGAub2tgIG9mIF9fdHJ1ZV9fXG4gKiB3aGVyZWFzIDV4eCB3aWxsIGJlIF9fZmFsc2VfXyBhbmQgYC5lcnJvcmAgd2lsbCBiZSBfX3RydWVfXy4gVGhlXG4gKiBgLmNsaWVudEVycm9yYCBhbmQgYC5zZXJ2ZXJFcnJvcmAgYXJlIGFsc28gYXZhaWxhYmxlIHRvIGJlIG1vcmVcbiAqIHNwZWNpZmljLCBhbmQgYC5zdGF0dXNUeXBlYCBpcyB0aGUgY2xhc3Mgb2YgZXJyb3IgcmFuZ2luZyBmcm9tIDEuLjVcbiAqIHNvbWV0aW1lcyB1c2VmdWwgZm9yIG1hcHBpbmcgcmVzcG9uZCBjb2xvcnMgZXRjLlxuICpcbiAqIFwic3VnYXJcIiBwcm9wZXJ0aWVzIGFyZSBhbHNvIGRlZmluZWQgZm9yIGNvbW1vbiBjYXNlcy4gQ3VycmVudGx5IHByb3ZpZGluZzpcbiAqXG4gKiAgIC0gLm5vQ29udGVudFxuICogICAtIC5iYWRSZXF1ZXN0XG4gKiAgIC0gLnVuYXV0aG9yaXplZFxuICogICAtIC5ub3RBY2NlcHRhYmxlXG4gKiAgIC0gLm5vdEZvdW5kXG4gKlxuICogQHBhcmFtIHtOdW1iZXJ9IHN0YXR1c1xuICogQGFwaSBwcml2YXRlXG4gKi9cblxuUmVzcG9uc2UucHJvdG90eXBlLnNldFN0YXR1c1Byb3BlcnRpZXMgPSBmdW5jdGlvbihzdGF0dXMpe1xuICB2YXIgdHlwZSA9IHN0YXR1cyAvIDEwMCB8IDA7XG5cbiAgLy8gc3RhdHVzIC8gY2xhc3NcbiAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7XG4gIHRoaXMuc3RhdHVzVHlwZSA9IHR5cGU7XG5cbiAgLy8gYmFzaWNzXG4gIHRoaXMuaW5mbyA9IDEgPT0gdHlwZTtcbiAgdGhpcy5vayA9IDIgPT0gdHlwZTtcbiAgdGhpcy5jbGllbnRFcnJvciA9IDQgPT0gdHlwZTtcbiAgdGhpcy5zZXJ2ZXJFcnJvciA9IDUgPT0gdHlwZTtcbiAgdGhpcy5lcnJvciA9ICg0ID09IHR5cGUgfHwgNSA9PSB0eXBlKVxuICAgID8gdGhpcy50b0Vycm9yKClcbiAgICA6IGZhbHNlO1xuXG4gIC8vIHN1Z2FyXG4gIHRoaXMuYWNjZXB0ZWQgPSAyMDIgPT0gc3RhdHVzO1xuICB0aGlzLm5vQ29udGVudCA9IDIwNCA9PSBzdGF0dXMgfHwgMTIyMyA9PSBzdGF0dXM7XG4gIHRoaXMuYmFkUmVxdWVzdCA9IDQwMCA9PSBzdGF0dXM7XG4gIHRoaXMudW5hdXRob3JpemVkID0gNDAxID09IHN0YXR1cztcbiAgdGhpcy5ub3RBY2NlcHRhYmxlID0gNDA2ID09IHN0YXR1cztcbiAgdGhpcy5ub3RGb3VuZCA9IDQwNCA9PSBzdGF0dXM7XG4gIHRoaXMuZm9yYmlkZGVuID0gNDAzID09IHN0YXR1cztcbn07XG5cbi8qKlxuICogUmV0dXJuIGFuIGBFcnJvcmAgcmVwcmVzZW50YXRpdmUgb2YgdGhpcyByZXNwb25zZS5cbiAqXG4gKiBAcmV0dXJuIHtFcnJvcn1cbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVzcG9uc2UucHJvdG90eXBlLnRvRXJyb3IgPSBmdW5jdGlvbigpe1xuICB2YXIgcmVxID0gdGhpcy5yZXE7XG4gIHZhciBtZXRob2QgPSByZXEubWV0aG9kO1xuICB2YXIgdXJsID0gcmVxLnVybDtcblxuICB2YXIgbXNnID0gJ2Nhbm5vdCAnICsgbWV0aG9kICsgJyAnICsgdXJsICsgJyAoJyArIHRoaXMuc3RhdHVzICsgJyknO1xuICB2YXIgZXJyID0gbmV3IEVycm9yKG1zZyk7XG4gIGVyci5zdGF0dXMgPSB0aGlzLnN0YXR1cztcbiAgZXJyLm1ldGhvZCA9IG1ldGhvZDtcbiAgZXJyLnVybCA9IHVybDtcblxuICByZXR1cm4gZXJyO1xufTtcblxuLyoqXG4gKiBFeHBvc2UgYFJlc3BvbnNlYC5cbiAqL1xuXG5yZXF1ZXN0LlJlc3BvbnNlID0gUmVzcG9uc2U7XG5cbi8qKlxuICogSW5pdGlhbGl6ZSBhIG5ldyBgUmVxdWVzdGAgd2l0aCB0aGUgZ2l2ZW4gYG1ldGhvZGAgYW5kIGB1cmxgLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBtZXRob2RcbiAqIEBwYXJhbSB7U3RyaW5nfSB1cmxcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuZnVuY3Rpb24gUmVxdWVzdChtZXRob2QsIHVybCkge1xuICB2YXIgc2VsZiA9IHRoaXM7XG4gIEVtaXR0ZXIuY2FsbCh0aGlzKTtcbiAgdGhpcy5fcXVlcnkgPSB0aGlzLl9xdWVyeSB8fCBbXTtcbiAgdGhpcy5tZXRob2QgPSBtZXRob2Q7XG4gIHRoaXMudXJsID0gdXJsO1xuICB0aGlzLmhlYWRlciA9IHt9O1xuICB0aGlzLl9oZWFkZXIgPSB7fTtcbiAgdGhpcy5vbignZW5kJywgZnVuY3Rpb24oKXtcbiAgICB2YXIgZXJyID0gbnVsbDtcbiAgICB2YXIgcmVzID0gbnVsbDtcblxuICAgIHRyeSB7XG4gICAgICByZXMgPSBuZXcgUmVzcG9uc2Uoc2VsZik7IFxuICAgIH0gY2F0Y2goZSkge1xuICAgICAgZXJyID0gbmV3IEVycm9yKCdQYXJzZXIgaXMgdW5hYmxlIHRvIHBhcnNlIHRoZSByZXNwb25zZScpO1xuICAgICAgZXJyLnBhcnNlID0gdHJ1ZTtcbiAgICAgIGVyci5vcmlnaW5hbCA9IGU7XG4gICAgfVxuXG4gICAgc2VsZi5jYWxsYmFjayhlcnIsIHJlcyk7XG4gIH0pO1xufVxuXG4vKipcbiAqIE1peGluIGBFbWl0dGVyYC5cbiAqL1xuXG5FbWl0dGVyKFJlcXVlc3QucHJvdG90eXBlKTtcblxuLyoqXG4gKiBBbGxvdyBmb3IgZXh0ZW5zaW9uXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUudXNlID0gZnVuY3Rpb24oZm4pIHtcbiAgZm4odGhpcyk7XG4gIHJldHVybiB0aGlzO1xufVxuXG4vKipcbiAqIFNldCB0aW1lb3V0IHRvIGBtc2AuXG4gKlxuICogQHBhcmFtIHtOdW1iZXJ9IG1zXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUudGltZW91dCA9IGZ1bmN0aW9uKG1zKXtcbiAgdGhpcy5fdGltZW91dCA9IG1zO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogQ2xlYXIgcHJldmlvdXMgdGltZW91dC5cbiAqXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuY2xlYXJUaW1lb3V0ID0gZnVuY3Rpb24oKXtcbiAgdGhpcy5fdGltZW91dCA9IDA7XG4gIGNsZWFyVGltZW91dCh0aGlzLl90aW1lcik7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBBYm9ydCB0aGUgcmVxdWVzdCwgYW5kIGNsZWFyIHBvdGVudGlhbCB0aW1lb3V0LlxuICpcbiAqIEByZXR1cm4ge1JlcXVlc3R9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5cblJlcXVlc3QucHJvdG90eXBlLmFib3J0ID0gZnVuY3Rpb24oKXtcbiAgaWYgKHRoaXMuYWJvcnRlZCkgcmV0dXJuO1xuICB0aGlzLmFib3J0ZWQgPSB0cnVlO1xuICB0aGlzLnhoci5hYm9ydCgpO1xuICB0aGlzLmNsZWFyVGltZW91dCgpO1xuICB0aGlzLmVtaXQoJ2Fib3J0Jyk7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBTZXQgaGVhZGVyIGBmaWVsZGAgdG8gYHZhbGAsIG9yIG11bHRpcGxlIGZpZWxkcyB3aXRoIG9uZSBvYmplY3QuXG4gKlxuICogRXhhbXBsZXM6XG4gKlxuICogICAgICByZXEuZ2V0KCcvJylcbiAqICAgICAgICAuc2V0KCdBY2NlcHQnLCAnYXBwbGljYXRpb24vanNvbicpXG4gKiAgICAgICAgLnNldCgnWC1BUEktS2V5JywgJ2Zvb2JhcicpXG4gKiAgICAgICAgLmVuZChjYWxsYmFjayk7XG4gKlxuICogICAgICByZXEuZ2V0KCcvJylcbiAqICAgICAgICAuc2V0KHsgQWNjZXB0OiAnYXBwbGljYXRpb24vanNvbicsICdYLUFQSS1LZXknOiAnZm9vYmFyJyB9KVxuICogICAgICAgIC5lbmQoY2FsbGJhY2spO1xuICpcbiAqIEBwYXJhbSB7U3RyaW5nfE9iamVjdH0gZmllbGRcbiAqIEBwYXJhbSB7U3RyaW5nfSB2YWxcbiAqIEByZXR1cm4ge1JlcXVlc3R9IGZvciBjaGFpbmluZ1xuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5SZXF1ZXN0LnByb3RvdHlwZS5zZXQgPSBmdW5jdGlvbihmaWVsZCwgdmFsKXtcbiAgaWYgKGlzT2JqZWN0KGZpZWxkKSkge1xuICAgIGZvciAodmFyIGtleSBpbiBmaWVsZCkge1xuICAgICAgdGhpcy5zZXQoa2V5LCBmaWVsZFtrZXldKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cbiAgdGhpcy5faGVhZGVyW2ZpZWxkLnRvTG93ZXJDYXNlKCldID0gdmFsO1xuICB0aGlzLmhlYWRlcltmaWVsZF0gPSB2YWw7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBSZW1vdmUgaGVhZGVyIGBmaWVsZGAuXG4gKlxuICogRXhhbXBsZTpcbiAqXG4gKiAgICAgIHJlcS5nZXQoJy8nKVxuICogICAgICAgIC51bnNldCgnVXNlci1BZ2VudCcpXG4gKiAgICAgICAgLmVuZChjYWxsYmFjayk7XG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IGZpZWxkXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUudW5zZXQgPSBmdW5jdGlvbihmaWVsZCl7XG4gIGRlbGV0ZSB0aGlzLl9oZWFkZXJbZmllbGQudG9Mb3dlckNhc2UoKV07XG4gIGRlbGV0ZSB0aGlzLmhlYWRlcltmaWVsZF07XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBHZXQgY2FzZS1pbnNlbnNpdGl2ZSBoZWFkZXIgYGZpZWxkYCB2YWx1ZS5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gZmllbGRcbiAqIEByZXR1cm4ge1N0cmluZ31cbiAqIEBhcGkgcHJpdmF0ZVxuICovXG5cblJlcXVlc3QucHJvdG90eXBlLmdldEhlYWRlciA9IGZ1bmN0aW9uKGZpZWxkKXtcbiAgcmV0dXJuIHRoaXMuX2hlYWRlcltmaWVsZC50b0xvd2VyQ2FzZSgpXTtcbn07XG5cbi8qKlxuICogU2V0IENvbnRlbnQtVHlwZSB0byBgdHlwZWAsIG1hcHBpbmcgdmFsdWVzIGZyb20gYHJlcXVlc3QudHlwZXNgLlxuICpcbiAqIEV4YW1wbGVzOlxuICpcbiAqICAgICAgc3VwZXJhZ2VudC50eXBlcy54bWwgPSAnYXBwbGljYXRpb24veG1sJztcbiAqXG4gKiAgICAgIHJlcXVlc3QucG9zdCgnLycpXG4gKiAgICAgICAgLnR5cGUoJ3htbCcpXG4gKiAgICAgICAgLnNlbmQoeG1sc3RyaW5nKVxuICogICAgICAgIC5lbmQoY2FsbGJhY2spO1xuICpcbiAqICAgICAgcmVxdWVzdC5wb3N0KCcvJylcbiAqICAgICAgICAudHlwZSgnYXBwbGljYXRpb24veG1sJylcbiAqICAgICAgICAuc2VuZCh4bWxzdHJpbmcpXG4gKiAgICAgICAgLmVuZChjYWxsYmFjayk7XG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IHR5cGVcbiAqIEByZXR1cm4ge1JlcXVlc3R9IGZvciBjaGFpbmluZ1xuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5SZXF1ZXN0LnByb3RvdHlwZS50eXBlID0gZnVuY3Rpb24odHlwZSl7XG4gIHRoaXMuc2V0KCdDb250ZW50LVR5cGUnLCByZXF1ZXN0LnR5cGVzW3R5cGVdIHx8IHR5cGUpO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogU2V0IEFjY2VwdCB0byBgdHlwZWAsIG1hcHBpbmcgdmFsdWVzIGZyb20gYHJlcXVlc3QudHlwZXNgLlxuICpcbiAqIEV4YW1wbGVzOlxuICpcbiAqICAgICAgc3VwZXJhZ2VudC50eXBlcy5qc29uID0gJ2FwcGxpY2F0aW9uL2pzb24nO1xuICpcbiAqICAgICAgcmVxdWVzdC5nZXQoJy9hZ2VudCcpXG4gKiAgICAgICAgLmFjY2VwdCgnanNvbicpXG4gKiAgICAgICAgLmVuZChjYWxsYmFjayk7XG4gKlxuICogICAgICByZXF1ZXN0LmdldCgnL2FnZW50JylcbiAqICAgICAgICAuYWNjZXB0KCdhcHBsaWNhdGlvbi9qc29uJylcbiAqICAgICAgICAuZW5kKGNhbGxiYWNrKTtcbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gYWNjZXB0XG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuYWNjZXB0ID0gZnVuY3Rpb24odHlwZSl7XG4gIHRoaXMuc2V0KCdBY2NlcHQnLCByZXF1ZXN0LnR5cGVzW3R5cGVdIHx8IHR5cGUpO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogU2V0IEF1dGhvcml6YXRpb24gZmllbGQgdmFsdWUgd2l0aCBgdXNlcmAgYW5kIGBwYXNzYC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gdXNlclxuICogQHBhcmFtIHtTdHJpbmd9IHBhc3NcbiAqIEByZXR1cm4ge1JlcXVlc3R9IGZvciBjaGFpbmluZ1xuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5SZXF1ZXN0LnByb3RvdHlwZS5hdXRoID0gZnVuY3Rpb24odXNlciwgcGFzcyl7XG4gIHZhciBzdHIgPSBidG9hKHVzZXIgKyAnOicgKyBwYXNzKTtcbiAgdGhpcy5zZXQoJ0F1dGhvcml6YXRpb24nLCAnQmFzaWMgJyArIHN0cik7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4qIEFkZCBxdWVyeS1zdHJpbmcgYHZhbGAuXG4qXG4qIEV4YW1wbGVzOlxuKlxuKiAgIHJlcXVlc3QuZ2V0KCcvc2hvZXMnKVxuKiAgICAgLnF1ZXJ5KCdzaXplPTEwJylcbiogICAgIC5xdWVyeSh7IGNvbG9yOiAnYmx1ZScgfSlcbipcbiogQHBhcmFtIHtPYmplY3R8U3RyaW5nfSB2YWxcbiogQHJldHVybiB7UmVxdWVzdH0gZm9yIGNoYWluaW5nXG4qIEBhcGkgcHVibGljXG4qL1xuXG5SZXF1ZXN0LnByb3RvdHlwZS5xdWVyeSA9IGZ1bmN0aW9uKHZhbCl7XG4gIGlmICgnc3RyaW5nJyAhPSB0eXBlb2YgdmFsKSB2YWwgPSBzZXJpYWxpemUodmFsKTtcbiAgaWYgKHZhbCkgdGhpcy5fcXVlcnkucHVzaCh2YWwpO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogV3JpdGUgdGhlIGZpZWxkIGBuYW1lYCBhbmQgYHZhbGAgZm9yIFwibXVsdGlwYXJ0L2Zvcm0tZGF0YVwiXG4gKiByZXF1ZXN0IGJvZGllcy5cbiAqXG4gKiBgYGAganNcbiAqIHJlcXVlc3QucG9zdCgnL3VwbG9hZCcpXG4gKiAgIC5maWVsZCgnZm9vJywgJ2JhcicpXG4gKiAgIC5lbmQoY2FsbGJhY2spO1xuICogYGBgXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IG5hbWVcbiAqIEBwYXJhbSB7U3RyaW5nfEJsb2J8RmlsZX0gdmFsXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuZmllbGQgPSBmdW5jdGlvbihuYW1lLCB2YWwpe1xuICBpZiAoIXRoaXMuX2Zvcm1EYXRhKSB0aGlzLl9mb3JtRGF0YSA9IG5ldyBGb3JtRGF0YSgpO1xuICB0aGlzLl9mb3JtRGF0YS5hcHBlbmQobmFtZSwgdmFsKTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIFF1ZXVlIHRoZSBnaXZlbiBgZmlsZWAgYXMgYW4gYXR0YWNobWVudCB0byB0aGUgc3BlY2lmaWVkIGBmaWVsZGAsXG4gKiB3aXRoIG9wdGlvbmFsIGBmaWxlbmFtZWAuXG4gKlxuICogYGBgIGpzXG4gKiByZXF1ZXN0LnBvc3QoJy91cGxvYWQnKVxuICogICAuYXR0YWNoKG5ldyBCbG9iKFsnPGEgaWQ9XCJhXCI+PGIgaWQ9XCJiXCI+aGV5ITwvYj48L2E+J10sIHsgdHlwZTogXCJ0ZXh0L2h0bWxcIn0pKVxuICogICAuZW5kKGNhbGxiYWNrKTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBmaWVsZFxuICogQHBhcmFtIHtCbG9ifEZpbGV9IGZpbGVcbiAqIEBwYXJhbSB7U3RyaW5nfSBmaWxlbmFtZVxuICogQHJldHVybiB7UmVxdWVzdH0gZm9yIGNoYWluaW5nXG4gKiBAYXBpIHB1YmxpY1xuICovXG5cblJlcXVlc3QucHJvdG90eXBlLmF0dGFjaCA9IGZ1bmN0aW9uKGZpZWxkLCBmaWxlLCBmaWxlbmFtZSl7XG4gIGlmICghdGhpcy5fZm9ybURhdGEpIHRoaXMuX2Zvcm1EYXRhID0gbmV3IEZvcm1EYXRhKCk7XG4gIHRoaXMuX2Zvcm1EYXRhLmFwcGVuZChmaWVsZCwgZmlsZSwgZmlsZW5hbWUpO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogU2VuZCBgZGF0YWAsIGRlZmF1bHRpbmcgdGhlIGAudHlwZSgpYCB0byBcImpzb25cIiB3aGVuXG4gKiBhbiBvYmplY3QgaXMgZ2l2ZW4uXG4gKlxuICogRXhhbXBsZXM6XG4gKlxuICogICAgICAgLy8gcXVlcnlzdHJpbmdcbiAqICAgICAgIHJlcXVlc3QuZ2V0KCcvc2VhcmNoJylcbiAqICAgICAgICAgLmVuZChjYWxsYmFjaylcbiAqXG4gKiAgICAgICAvLyBtdWx0aXBsZSBkYXRhIFwid3JpdGVzXCJcbiAqICAgICAgIHJlcXVlc3QuZ2V0KCcvc2VhcmNoJylcbiAqICAgICAgICAgLnNlbmQoeyBzZWFyY2g6ICdxdWVyeScgfSlcbiAqICAgICAgICAgLnNlbmQoeyByYW5nZTogJzEuLjUnIH0pXG4gKiAgICAgICAgIC5zZW5kKHsgb3JkZXI6ICdkZXNjJyB9KVxuICogICAgICAgICAuZW5kKGNhbGxiYWNrKVxuICpcbiAqICAgICAgIC8vIG1hbnVhbCBqc29uXG4gKiAgICAgICByZXF1ZXN0LnBvc3QoJy91c2VyJylcbiAqICAgICAgICAgLnR5cGUoJ2pzb24nKVxuICogICAgICAgICAuc2VuZCgne1wibmFtZVwiOlwidGpcIn0pXG4gKiAgICAgICAgIC5lbmQoY2FsbGJhY2spXG4gKlxuICogICAgICAgLy8gYXV0byBqc29uXG4gKiAgICAgICByZXF1ZXN0LnBvc3QoJy91c2VyJylcbiAqICAgICAgICAgLnNlbmQoeyBuYW1lOiAndGonIH0pXG4gKiAgICAgICAgIC5lbmQoY2FsbGJhY2spXG4gKlxuICogICAgICAgLy8gbWFudWFsIHgtd3d3LWZvcm0tdXJsZW5jb2RlZFxuICogICAgICAgcmVxdWVzdC5wb3N0KCcvdXNlcicpXG4gKiAgICAgICAgIC50eXBlKCdmb3JtJylcbiAqICAgICAgICAgLnNlbmQoJ25hbWU9dGonKVxuICogICAgICAgICAuZW5kKGNhbGxiYWNrKVxuICpcbiAqICAgICAgIC8vIGF1dG8geC13d3ctZm9ybS11cmxlbmNvZGVkXG4gKiAgICAgICByZXF1ZXN0LnBvc3QoJy91c2VyJylcbiAqICAgICAgICAgLnR5cGUoJ2Zvcm0nKVxuICogICAgICAgICAuc2VuZCh7IG5hbWU6ICd0aicgfSlcbiAqICAgICAgICAgLmVuZChjYWxsYmFjaylcbiAqXG4gKiAgICAgICAvLyBkZWZhdWx0cyB0byB4LXd3dy1mb3JtLXVybGVuY29kZWRcbiAgKiAgICAgIHJlcXVlc3QucG9zdCgnL3VzZXInKVxuICAqICAgICAgICAuc2VuZCgnbmFtZT10b2JpJylcbiAgKiAgICAgICAgLnNlbmQoJ3NwZWNpZXM9ZmVycmV0JylcbiAgKiAgICAgICAgLmVuZChjYWxsYmFjaylcbiAqXG4gKiBAcGFyYW0ge1N0cmluZ3xPYmplY3R9IGRhdGFcbiAqIEByZXR1cm4ge1JlcXVlc3R9IGZvciBjaGFpbmluZ1xuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5SZXF1ZXN0LnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24oZGF0YSl7XG4gIHZhciBvYmogPSBpc09iamVjdChkYXRhKTtcbiAgdmFyIHR5cGUgPSB0aGlzLmdldEhlYWRlcignQ29udGVudC1UeXBlJyk7XG5cbiAgLy8gbWVyZ2VcbiAgaWYgKG9iaiAmJiBpc09iamVjdCh0aGlzLl9kYXRhKSkge1xuICAgIGZvciAodmFyIGtleSBpbiBkYXRhKSB7XG4gICAgICB0aGlzLl9kYXRhW2tleV0gPSBkYXRhW2tleV07XG4gICAgfVxuICB9IGVsc2UgaWYgKCdzdHJpbmcnID09IHR5cGVvZiBkYXRhKSB7XG4gICAgaWYgKCF0eXBlKSB0aGlzLnR5cGUoJ2Zvcm0nKTtcbiAgICB0eXBlID0gdGhpcy5nZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScpO1xuICAgIGlmICgnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyA9PSB0eXBlKSB7XG4gICAgICB0aGlzLl9kYXRhID0gdGhpcy5fZGF0YVxuICAgICAgICA/IHRoaXMuX2RhdGEgKyAnJicgKyBkYXRhXG4gICAgICAgIDogZGF0YTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5fZGF0YSA9ICh0aGlzLl9kYXRhIHx8ICcnKSArIGRhdGE7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHRoaXMuX2RhdGEgPSBkYXRhO1xuICB9XG5cbiAgaWYgKCFvYmopIHJldHVybiB0aGlzO1xuICBpZiAoIXR5cGUpIHRoaXMudHlwZSgnanNvbicpO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogSW52b2tlIHRoZSBjYWxsYmFjayB3aXRoIGBlcnJgIGFuZCBgcmVzYFxuICogYW5kIGhhbmRsZSBhcml0eSBjaGVjay5cbiAqXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJcbiAqIEBwYXJhbSB7UmVzcG9uc2V9IHJlc1xuICogQGFwaSBwcml2YXRlXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuY2FsbGJhY2sgPSBmdW5jdGlvbihlcnIsIHJlcyl7XG4gIHZhciBmbiA9IHRoaXMuX2NhbGxiYWNrO1xuICB0aGlzLmNsZWFyVGltZW91dCgpO1xuICBpZiAoMiA9PSBmbi5sZW5ndGgpIHJldHVybiBmbihlcnIsIHJlcyk7XG4gIGlmIChlcnIpIHJldHVybiB0aGlzLmVtaXQoJ2Vycm9yJywgZXJyKTtcbiAgZm4ocmVzKTtcbn07XG5cbi8qKlxuICogSW52b2tlIGNhbGxiYWNrIHdpdGggeC1kb21haW4gZXJyb3IuXG4gKlxuICogQGFwaSBwcml2YXRlXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuY3Jvc3NEb21haW5FcnJvciA9IGZ1bmN0aW9uKCl7XG4gIHZhciBlcnIgPSBuZXcgRXJyb3IoJ09yaWdpbiBpcyBub3QgYWxsb3dlZCBieSBBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW4nKTtcbiAgZXJyLmNyb3NzRG9tYWluID0gdHJ1ZTtcbiAgdGhpcy5jYWxsYmFjayhlcnIpO1xufTtcblxuLyoqXG4gKiBJbnZva2UgY2FsbGJhY2sgd2l0aCB0aW1lb3V0IGVycm9yLlxuICpcbiAqIEBhcGkgcHJpdmF0ZVxuICovXG5cblJlcXVlc3QucHJvdG90eXBlLnRpbWVvdXRFcnJvciA9IGZ1bmN0aW9uKCl7XG4gIHZhciB0aW1lb3V0ID0gdGhpcy5fdGltZW91dDtcbiAgdmFyIGVyciA9IG5ldyBFcnJvcigndGltZW91dCBvZiAnICsgdGltZW91dCArICdtcyBleGNlZWRlZCcpO1xuICBlcnIudGltZW91dCA9IHRpbWVvdXQ7XG4gIHRoaXMuY2FsbGJhY2soZXJyKTtcbn07XG5cbi8qKlxuICogRW5hYmxlIHRyYW5zbWlzc2lvbiBvZiBjb29raWVzIHdpdGggeC1kb21haW4gcmVxdWVzdHMuXG4gKlxuICogTm90ZSB0aGF0IGZvciB0aGlzIHRvIHdvcmsgdGhlIG9yaWdpbiBtdXN0IG5vdCBiZVxuICogdXNpbmcgXCJBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW5cIiB3aXRoIGEgd2lsZGNhcmQsXG4gKiBhbmQgYWxzbyBtdXN0IHNldCBcIkFjY2Vzcy1Db250cm9sLUFsbG93LUNyZWRlbnRpYWxzXCJcbiAqIHRvIFwidHJ1ZVwiLlxuICpcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUud2l0aENyZWRlbnRpYWxzID0gZnVuY3Rpb24oKXtcbiAgdGhpcy5fd2l0aENyZWRlbnRpYWxzID0gdHJ1ZTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIEluaXRpYXRlIHJlcXVlc3QsIGludm9raW5nIGNhbGxiYWNrIGBmbihyZXMpYFxuICogd2l0aCBhbiBpbnN0YW5jZW9mIGBSZXNwb25zZWAuXG4gKlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm5cbiAqIEByZXR1cm4ge1JlcXVlc3R9IGZvciBjaGFpbmluZ1xuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5SZXF1ZXN0LnByb3RvdHlwZS5lbmQgPSBmdW5jdGlvbihmbil7XG4gIHZhciBzZWxmID0gdGhpcztcbiAgdmFyIHhociA9IHRoaXMueGhyID0gZ2V0WEhSKCk7XG4gIHZhciBxdWVyeSA9IHRoaXMuX3F1ZXJ5LmpvaW4oJyYnKTtcbiAgdmFyIHRpbWVvdXQgPSB0aGlzLl90aW1lb3V0O1xuICB2YXIgZGF0YSA9IHRoaXMuX2Zvcm1EYXRhIHx8IHRoaXMuX2RhdGE7XG5cbiAgLy8gc3RvcmUgY2FsbGJhY2tcbiAgdGhpcy5fY2FsbGJhY2sgPSBmbiB8fCBub29wO1xuXG4gIC8vIHN0YXRlIGNoYW5nZVxuICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gZnVuY3Rpb24oKXtcbiAgICBpZiAoNCAhPSB4aHIucmVhZHlTdGF0ZSkgcmV0dXJuO1xuICAgIGlmICgwID09IHhoci5zdGF0dXMpIHtcbiAgICAgIGlmIChzZWxmLmFib3J0ZWQpIHJldHVybiBzZWxmLnRpbWVvdXRFcnJvcigpO1xuICAgICAgcmV0dXJuIHNlbGYuY3Jvc3NEb21haW5FcnJvcigpO1xuICAgIH1cbiAgICBzZWxmLmVtaXQoJ2VuZCcpO1xuICB9O1xuXG4gIC8vIHByb2dyZXNzXG4gIGlmICh4aHIudXBsb2FkKSB7XG4gICAgeGhyLnVwbG9hZC5vbnByb2dyZXNzID0gZnVuY3Rpb24oZSl7XG4gICAgICBlLnBlcmNlbnQgPSBlLmxvYWRlZCAvIGUudG90YWwgKiAxMDA7XG4gICAgICBzZWxmLmVtaXQoJ3Byb2dyZXNzJywgZSk7XG4gICAgfTtcbiAgfVxuXG4gIC8vIHRpbWVvdXRcbiAgaWYgKHRpbWVvdXQgJiYgIXRoaXMuX3RpbWVyKSB7XG4gICAgdGhpcy5fdGltZXIgPSBzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7XG4gICAgICBzZWxmLmFib3J0KCk7XG4gICAgfSwgdGltZW91dCk7XG4gIH1cblxuICAvLyBxdWVyeXN0cmluZ1xuICBpZiAocXVlcnkpIHtcbiAgICBxdWVyeSA9IHJlcXVlc3Quc2VyaWFsaXplT2JqZWN0KHF1ZXJ5KTtcbiAgICB0aGlzLnVybCArPSB+dGhpcy51cmwuaW5kZXhPZignPycpXG4gICAgICA/ICcmJyArIHF1ZXJ5XG4gICAgICA6ICc/JyArIHF1ZXJ5O1xuICB9XG5cbiAgLy8gaW5pdGlhdGUgcmVxdWVzdFxuICB4aHIub3Blbih0aGlzLm1ldGhvZCwgdGhpcy51cmwsIHRydWUpO1xuXG4gIC8vIENPUlNcbiAgaWYgKHRoaXMuX3dpdGhDcmVkZW50aWFscykgeGhyLndpdGhDcmVkZW50aWFscyA9IHRydWU7XG5cbiAgLy8gYm9keVxuICBpZiAoJ0dFVCcgIT0gdGhpcy5tZXRob2QgJiYgJ0hFQUQnICE9IHRoaXMubWV0aG9kICYmICdzdHJpbmcnICE9IHR5cGVvZiBkYXRhICYmICFpc0hvc3QoZGF0YSkpIHtcbiAgICAvLyBzZXJpYWxpemUgc3R1ZmZcbiAgICB2YXIgc2VyaWFsaXplID0gcmVxdWVzdC5zZXJpYWxpemVbdGhpcy5nZXRIZWFkZXIoJ0NvbnRlbnQtVHlwZScpXTtcbiAgICBpZiAoc2VyaWFsaXplKSBkYXRhID0gc2VyaWFsaXplKGRhdGEpO1xuICB9XG5cbiAgLy8gc2V0IGhlYWRlciBmaWVsZHNcbiAgZm9yICh2YXIgZmllbGQgaW4gdGhpcy5oZWFkZXIpIHtcbiAgICBpZiAobnVsbCA9PSB0aGlzLmhlYWRlcltmaWVsZF0pIGNvbnRpbnVlO1xuICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKGZpZWxkLCB0aGlzLmhlYWRlcltmaWVsZF0pO1xuICB9XG5cbiAgLy8gc2VuZCBzdHVmZlxuICB0aGlzLmVtaXQoJ3JlcXVlc3QnLCB0aGlzKTtcbiAgeGhyLnNlbmQoZGF0YSk7XG4gIHJldHVybiB0aGlzO1xufTtcblxuLyoqXG4gKiBFeHBvc2UgYFJlcXVlc3RgLlxuICovXG5cbnJlcXVlc3QuUmVxdWVzdCA9IFJlcXVlc3Q7XG5cbi8qKlxuICogSXNzdWUgYSByZXF1ZXN0OlxuICpcbiAqIEV4YW1wbGVzOlxuICpcbiAqICAgIHJlcXVlc3QoJ0dFVCcsICcvdXNlcnMnKS5lbmQoY2FsbGJhY2spXG4gKiAgICByZXF1ZXN0KCcvdXNlcnMnKS5lbmQoY2FsbGJhY2spXG4gKiAgICByZXF1ZXN0KCcvdXNlcnMnLCBjYWxsYmFjaylcbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gbWV0aG9kXG4gKiBAcGFyYW0ge1N0cmluZ3xGdW5jdGlvbn0gdXJsIG9yIGNhbGxiYWNrXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fVxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5mdW5jdGlvbiByZXF1ZXN0KG1ldGhvZCwgdXJsKSB7XG4gIC8vIGNhbGxiYWNrXG4gIGlmICgnZnVuY3Rpb24nID09IHR5cGVvZiB1cmwpIHtcbiAgICByZXR1cm4gbmV3IFJlcXVlc3QoJ0dFVCcsIG1ldGhvZCkuZW5kKHVybCk7XG4gIH1cblxuICAvLyB1cmwgZmlyc3RcbiAgaWYgKDEgPT0gYXJndW1lbnRzLmxlbmd0aCkge1xuICAgIHJldHVybiBuZXcgUmVxdWVzdCgnR0VUJywgbWV0aG9kKTtcbiAgfVxuXG4gIHJldHVybiBuZXcgUmVxdWVzdChtZXRob2QsIHVybCk7XG59XG5cbi8qKlxuICogR0VUIGB1cmxgIHdpdGggb3B0aW9uYWwgY2FsbGJhY2sgYGZuKHJlcylgLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSB1cmxcbiAqIEBwYXJhbSB7TWl4ZWR8RnVuY3Rpb259IGRhdGEgb3IgZm5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fVxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5yZXF1ZXN0LmdldCA9IGZ1bmN0aW9uKHVybCwgZGF0YSwgZm4pe1xuICB2YXIgcmVxID0gcmVxdWVzdCgnR0VUJywgdXJsKTtcbiAgaWYgKCdmdW5jdGlvbicgPT0gdHlwZW9mIGRhdGEpIGZuID0gZGF0YSwgZGF0YSA9IG51bGw7XG4gIGlmIChkYXRhKSByZXEucXVlcnkoZGF0YSk7XG4gIGlmIChmbikgcmVxLmVuZChmbik7XG4gIHJldHVybiByZXE7XG59O1xuXG4vKipcbiAqIEhFQUQgYHVybGAgd2l0aCBvcHRpb25hbCBjYWxsYmFjayBgZm4ocmVzKWAuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IHVybFxuICogQHBhcmFtIHtNaXhlZHxGdW5jdGlvbn0gZGF0YSBvciBmblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm5cbiAqIEByZXR1cm4ge1JlcXVlc3R9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5cbnJlcXVlc3QuaGVhZCA9IGZ1bmN0aW9uKHVybCwgZGF0YSwgZm4pe1xuICB2YXIgcmVxID0gcmVxdWVzdCgnSEVBRCcsIHVybCk7XG4gIGlmICgnZnVuY3Rpb24nID09IHR5cGVvZiBkYXRhKSBmbiA9IGRhdGEsIGRhdGEgPSBudWxsO1xuICBpZiAoZGF0YSkgcmVxLnNlbmQoZGF0YSk7XG4gIGlmIChmbikgcmVxLmVuZChmbik7XG4gIHJldHVybiByZXE7XG59O1xuXG4vKipcbiAqIERFTEVURSBgdXJsYCB3aXRoIG9wdGlvbmFsIGNhbGxiYWNrIGBmbihyZXMpYC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gdXJsXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmblxuICogQHJldHVybiB7UmVxdWVzdH1cbiAqIEBhcGkgcHVibGljXG4gKi9cblxucmVxdWVzdC5kZWwgPSBmdW5jdGlvbih1cmwsIGZuKXtcbiAgdmFyIHJlcSA9IHJlcXVlc3QoJ0RFTEVURScsIHVybCk7XG4gIGlmIChmbikgcmVxLmVuZChmbik7XG4gIHJldHVybiByZXE7XG59O1xuXG4vKipcbiAqIFBBVENIIGB1cmxgIHdpdGggb3B0aW9uYWwgYGRhdGFgIGFuZCBjYWxsYmFjayBgZm4ocmVzKWAuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IHVybFxuICogQHBhcmFtIHtNaXhlZH0gZGF0YVxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm5cbiAqIEByZXR1cm4ge1JlcXVlc3R9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5cbnJlcXVlc3QucGF0Y2ggPSBmdW5jdGlvbih1cmwsIGRhdGEsIGZuKXtcbiAgdmFyIHJlcSA9IHJlcXVlc3QoJ1BBVENIJywgdXJsKTtcbiAgaWYgKCdmdW5jdGlvbicgPT0gdHlwZW9mIGRhdGEpIGZuID0gZGF0YSwgZGF0YSA9IG51bGw7XG4gIGlmIChkYXRhKSByZXEuc2VuZChkYXRhKTtcbiAgaWYgKGZuKSByZXEuZW5kKGZuKTtcbiAgcmV0dXJuIHJlcTtcbn07XG5cbi8qKlxuICogUE9TVCBgdXJsYCB3aXRoIG9wdGlvbmFsIGBkYXRhYCBhbmQgY2FsbGJhY2sgYGZuKHJlcylgLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSB1cmxcbiAqIEBwYXJhbSB7TWl4ZWR9IGRhdGFcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fVxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5yZXF1ZXN0LnBvc3QgPSBmdW5jdGlvbih1cmwsIGRhdGEsIGZuKXtcbiAgdmFyIHJlcSA9IHJlcXVlc3QoJ1BPU1QnLCB1cmwpO1xuICBpZiAoJ2Z1bmN0aW9uJyA9PSB0eXBlb2YgZGF0YSkgZm4gPSBkYXRhLCBkYXRhID0gbnVsbDtcbiAgaWYgKGRhdGEpIHJlcS5zZW5kKGRhdGEpO1xuICBpZiAoZm4pIHJlcS5lbmQoZm4pO1xuICByZXR1cm4gcmVxO1xufTtcblxuLyoqXG4gKiBQVVQgYHVybGAgd2l0aCBvcHRpb25hbCBgZGF0YWAgYW5kIGNhbGxiYWNrIGBmbihyZXMpYC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gdXJsXG4gKiBAcGFyYW0ge01peGVkfEZ1bmN0aW9ufSBkYXRhIG9yIGZuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmblxuICogQHJldHVybiB7UmVxdWVzdH1cbiAqIEBhcGkgcHVibGljXG4gKi9cblxucmVxdWVzdC5wdXQgPSBmdW5jdGlvbih1cmwsIGRhdGEsIGZuKXtcbiAgdmFyIHJlcSA9IHJlcXVlc3QoJ1BVVCcsIHVybCk7XG4gIGlmICgnZnVuY3Rpb24nID09IHR5cGVvZiBkYXRhKSBmbiA9IGRhdGEsIGRhdGEgPSBudWxsO1xuICBpZiAoZGF0YSkgcmVxLnNlbmQoZGF0YSk7XG4gIGlmIChmbikgcmVxLmVuZChmbik7XG4gIHJldHVybiByZXE7XG59O1xuXG4vKipcbiAqIEV4cG9zZSBgcmVxdWVzdGAuXG4gKi9cblxubW9kdWxlLmV4cG9ydHMgPSByZXF1ZXN0O1xuIiwiXG4vKipcbiAqIEV4cG9zZSBgRW1pdHRlcmAuXG4gKi9cblxubW9kdWxlLmV4cG9ydHMgPSBFbWl0dGVyO1xuXG4vKipcbiAqIEluaXRpYWxpemUgYSBuZXcgYEVtaXR0ZXJgLlxuICpcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuZnVuY3Rpb24gRW1pdHRlcihvYmopIHtcbiAgaWYgKG9iaikgcmV0dXJuIG1peGluKG9iaik7XG59O1xuXG4vKipcbiAqIE1peGluIHRoZSBlbWl0dGVyIHByb3BlcnRpZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9ialxuICogQHJldHVybiB7T2JqZWN0fVxuICogQGFwaSBwcml2YXRlXG4gKi9cblxuZnVuY3Rpb24gbWl4aW4ob2JqKSB7XG4gIGZvciAodmFyIGtleSBpbiBFbWl0dGVyLnByb3RvdHlwZSkge1xuICAgIG9ialtrZXldID0gRW1pdHRlci5wcm90b3R5cGVba2V5XTtcbiAgfVxuICByZXR1cm4gb2JqO1xufVxuXG4vKipcbiAqIExpc3RlbiBvbiB0aGUgZ2l2ZW4gYGV2ZW50YCB3aXRoIGBmbmAuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IGV2ZW50XG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmblxuICogQHJldHVybiB7RW1pdHRlcn1cbiAqIEBhcGkgcHVibGljXG4gKi9cblxuRW1pdHRlci5wcm90b3R5cGUub24gPVxuRW1pdHRlci5wcm90b3R5cGUuYWRkRXZlbnRMaXN0ZW5lciA9IGZ1bmN0aW9uKGV2ZW50LCBmbil7XG4gIHRoaXMuX2NhbGxiYWNrcyA9IHRoaXMuX2NhbGxiYWNrcyB8fCB7fTtcbiAgKHRoaXMuX2NhbGxiYWNrc1tldmVudF0gPSB0aGlzLl9jYWxsYmFja3NbZXZlbnRdIHx8IFtdKVxuICAgIC5wdXNoKGZuKTtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIEFkZHMgYW4gYGV2ZW50YCBsaXN0ZW5lciB0aGF0IHdpbGwgYmUgaW52b2tlZCBhIHNpbmdsZVxuICogdGltZSB0aGVuIGF1dG9tYXRpY2FsbHkgcmVtb3ZlZC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gZXZlbnRcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuXG4gKiBAcmV0dXJuIHtFbWl0dGVyfVxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5FbWl0dGVyLnByb3RvdHlwZS5vbmNlID0gZnVuY3Rpb24oZXZlbnQsIGZuKXtcbiAgdmFyIHNlbGYgPSB0aGlzO1xuICB0aGlzLl9jYWxsYmFja3MgPSB0aGlzLl9jYWxsYmFja3MgfHwge307XG5cbiAgZnVuY3Rpb24gb24oKSB7XG4gICAgc2VsZi5vZmYoZXZlbnQsIG9uKTtcbiAgICBmbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICB9XG5cbiAgb24uZm4gPSBmbjtcbiAgdGhpcy5vbihldmVudCwgb24pO1xuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogUmVtb3ZlIHRoZSBnaXZlbiBjYWxsYmFjayBmb3IgYGV2ZW50YCBvciBhbGxcbiAqIHJlZ2lzdGVyZWQgY2FsbGJhY2tzLlxuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBldmVudFxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm5cbiAqIEByZXR1cm4ge0VtaXR0ZXJ9XG4gKiBAYXBpIHB1YmxpY1xuICovXG5cbkVtaXR0ZXIucHJvdG90eXBlLm9mZiA9XG5FbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVMaXN0ZW5lciA9XG5FbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVBbGxMaXN0ZW5lcnMgPVxuRW1pdHRlci5wcm90b3R5cGUucmVtb3ZlRXZlbnRMaXN0ZW5lciA9IGZ1bmN0aW9uKGV2ZW50LCBmbil7XG4gIHRoaXMuX2NhbGxiYWNrcyA9IHRoaXMuX2NhbGxiYWNrcyB8fCB7fTtcblxuICAvLyBhbGxcbiAgaWYgKDAgPT0gYXJndW1lbnRzLmxlbmd0aCkge1xuICAgIHRoaXMuX2NhbGxiYWNrcyA9IHt9O1xuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLy8gc3BlY2lmaWMgZXZlbnRcbiAgdmFyIGNhbGxiYWNrcyA9IHRoaXMuX2NhbGxiYWNrc1tldmVudF07XG4gIGlmICghY2FsbGJhY2tzKSByZXR1cm4gdGhpcztcblxuICAvLyByZW1vdmUgYWxsIGhhbmRsZXJzXG4gIGlmICgxID09IGFyZ3VtZW50cy5sZW5ndGgpIHtcbiAgICBkZWxldGUgdGhpcy5fY2FsbGJhY2tzW2V2ZW50XTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIC8vIHJlbW92ZSBzcGVjaWZpYyBoYW5kbGVyXG4gIHZhciBjYjtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBjYWxsYmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICBjYiA9IGNhbGxiYWNrc1tpXTtcbiAgICBpZiAoY2IgPT09IGZuIHx8IGNiLmZuID09PSBmbikge1xuICAgICAgY2FsbGJhY2tzLnNwbGljZShpLCAxKTtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogRW1pdCBgZXZlbnRgIHdpdGggdGhlIGdpdmVuIGFyZ3MuXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IGV2ZW50XG4gKiBAcGFyYW0ge01peGVkfSAuLi5cbiAqIEByZXR1cm4ge0VtaXR0ZXJ9XG4gKi9cblxuRW1pdHRlci5wcm90b3R5cGUuZW1pdCA9IGZ1bmN0aW9uKGV2ZW50KXtcbiAgdGhpcy5fY2FsbGJhY2tzID0gdGhpcy5fY2FsbGJhY2tzIHx8IHt9O1xuICB2YXIgYXJncyA9IFtdLnNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKVxuICAgICwgY2FsbGJhY2tzID0gdGhpcy5fY2FsbGJhY2tzW2V2ZW50XTtcblxuICBpZiAoY2FsbGJhY2tzKSB7XG4gICAgY2FsbGJhY2tzID0gY2FsbGJhY2tzLnNsaWNlKDApO1xuICAgIGZvciAodmFyIGkgPSAwLCBsZW4gPSBjYWxsYmFja3MubGVuZ3RoOyBpIDwgbGVuOyArK2kpIHtcbiAgICAgIGNhbGxiYWNrc1tpXS5hcHBseSh0aGlzLCBhcmdzKTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gdGhpcztcbn07XG5cbi8qKlxuICogUmV0dXJuIGFycmF5IG9mIGNhbGxiYWNrcyBmb3IgYGV2ZW50YC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gZXZlbnRcbiAqIEByZXR1cm4ge0FycmF5fVxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5FbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lcnMgPSBmdW5jdGlvbihldmVudCl7XG4gIHRoaXMuX2NhbGxiYWNrcyA9IHRoaXMuX2NhbGxiYWNrcyB8fCB7fTtcbiAgcmV0dXJuIHRoaXMuX2NhbGxiYWNrc1tldmVudF0gfHwgW107XG59O1xuXG4vKipcbiAqIENoZWNrIGlmIHRoaXMgZW1pdHRlciBoYXMgYGV2ZW50YCBoYW5kbGVycy5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gZXZlbnRcbiAqIEByZXR1cm4ge0Jvb2xlYW59XG4gKiBAYXBpIHB1YmxpY1xuICovXG5cbkVtaXR0ZXIucHJvdG90eXBlLmhhc0xpc3RlbmVycyA9IGZ1bmN0aW9uKGV2ZW50KXtcbiAgcmV0dXJuICEhIHRoaXMubGlzdGVuZXJzKGV2ZW50KS5sZW5ndGg7XG59O1xuIiwiXG4vKipcbiAqIFJlZHVjZSBgYXJyYCB3aXRoIGBmbmAuXG4gKlxuICogQHBhcmFtIHtBcnJheX0gYXJyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmblxuICogQHBhcmFtIHtNaXhlZH0gaW5pdGlhbFxuICpcbiAqIFRPRE86IGNvbWJhdGlibGUgZXJyb3IgaGFuZGxpbmc/XG4gKi9cblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbihhcnIsIGZuLCBpbml0aWFsKXsgIFxuICB2YXIgaWR4ID0gMDtcbiAgdmFyIGxlbiA9IGFyci5sZW5ndGg7XG4gIHZhciBjdXJyID0gYXJndW1lbnRzLmxlbmd0aCA9PSAzXG4gICAgPyBpbml0aWFsXG4gICAgOiBhcnJbaWR4KytdO1xuXG4gIHdoaWxlIChpZHggPCBsZW4pIHtcbiAgICBjdXJyID0gZm4uY2FsbChudWxsLCBjdXJyLCBhcnJbaWR4XSwgKytpZHgsIGFycik7XG4gIH1cbiAgXG4gIHJldHVybiBjdXJyO1xufTsiXX0=
+
+'use strict';
+
+window.SwaggerUi = Backbone.Router.extend({
+
+ dom_id: 'swagger_ui',
+
+ // Attributes
+ options: null,
+ api: null,
+ headerView: null,
+ mainView: null,
+
+ // SwaggerUi accepts all the same options as SwaggerApi
+ initialize: function(options) {
+ options = options || {};
+
+ // Allow dom_id to be overridden
+ if (options.dom_id) {
+ this.dom_id = options.dom_id;
+ delete options.dom_id;
+ }
+
+ if (!options.supportedSubmitMethods){
+ options.supportedSubmitMethods = [
+ 'get',
+ 'put',
+ 'post',
+ 'delete',
+ 'head',
+ 'options',
+ 'patch'
+ ];
+ }
+
+ if (typeof options.oauth2RedirectUrl === 'string') {
+ window.oAuthRedirectUrl = options.redirectUrl;
+ }
+
+ // Create an empty div which contains the dom_id
+ if (! $('#' + this.dom_id).length){
+ $('body').append('<div id="' + this.dom_id + '"></div>') ;
+ }
+
+ this.options = options;
+
+ // set marked options
+ marked.setOptions({gfm: true});
+
+ // Set the callbacks
+ var that = this;
+ this.options.success = function() { return that.render(); };
+ this.options.progress = function(d) { return that.showMessage(d); };
+ this.options.failure = function(d) { return that.onLoadFailure(d); };
+
+ // Create view to handle the header inputs
+ this.headerView = new SwaggerUi.Views.HeaderView({el: $('#header')});
+
+ // Event handler for when the baseUrl/apiKey is entered by user
+ this.headerView.on('update-swagger-ui', function(data) {
+ return that.updateSwaggerUi(data);
+ });
+ },
+
+ // Set an option after initializing
+ setOption: function(option, value) {
+ this.options[option] = value;
+ },
+
+ // Get the value of a previously set option
+ getOption: function(option) {
+ return this.options[option];
+ },
+
+ // Event handler for when url/key is received from user
+ updateSwaggerUi: function(data){
+ this.options.url = data.url;
+ this.load();
+ },
+
+ // Create an api and render
+ load: function(){
+ // Initialize the API object
+ if (this.mainView) {
+ this.mainView.clear();
+ }
+ var url = this.options.url;
+ if (url && url.indexOf('http') !== 0) {
+ url = this.buildUrl(window.location.href.toString(), url);
+ }
+
+ this.options.url = url;
+ this.headerView.update(url);
+
+ this.api = new SwaggerClient(this.options);
+ },
+
+ // collapse all sections
+ collapseAll: function(){
+ Docs.collapseEndpointListForResource('');
+ },
+
+ // list operations for all sections
+ listAll: function(){
+ Docs.collapseOperationsForResource('');
+ },
+
+ // expand operations for all sections
+ expandAll: function(){
+ Docs.expandOperationsForResource('');
+ },
+
+ // This is bound to success handler for SwaggerApi
+ // so it gets called when SwaggerApi completes loading
+ render: function(){
+ this.showMessage('Finished Loading Resource Information. Rendering Swagger UI...');
+ this.mainView = new SwaggerUi.Views.MainView({
+ model: this.api,
+ el: $('#' + this.dom_id),
+ swaggerOptions: this.options,
+ router: this
+ }).render();
+ this.showMessage();
+ switch (this.options.docExpansion) {
+ case 'full':
+ this.expandAll(); break;
+ case 'list':
+ this.listAll(); break;
+ default:
+ break;
+ }
+ this.renderGFM();
+
+ if (this.options.onComplete){
+ this.options.onComplete(this.api, this);
+ }
+
+ setTimeout(Docs.shebang.bind(this), 100);
+ },
+
+ buildUrl: function(base, url){
+ if (url.indexOf('/') === 0) {
+ var parts = base.split('/');
+ base = parts[0] + '//' + parts[2];
+ return base + url;
+ } else {
+ var endOfPath = base.length;
+
+ if (base.indexOf('?') > -1){
+ endOfPath = Math.min(endOfPath, base.indexOf('?'));
+ }
+
+ if (base.indexOf('#') > -1){
+ endOfPath = Math.min(endOfPath, base.indexOf('#'));
+ }
+
+ base = base.substring(0, endOfPath);
+
+ if (base.indexOf('/', base.length - 1 ) !== -1){
+ return base + url;
+ }
+
+ return base + '/' + url;
+ }
+ },
+
+ // Shows message on topbar of the ui
+ showMessage: function(data){
+ if (data === undefined) {
+ data = '';
+ }
+ $('#message-bar').removeClass('message-fail');
+ $('#message-bar').addClass('message-success');
+ $('#message-bar').html(data);
+ },
+
+ // shows message in red
+ onLoadFailure: function(data){
+ if (data === undefined) {
+ data = '';
+ }
+ $('#message-bar').removeClass('message-success');
+ $('#message-bar').addClass('message-fail');
+
+ var val = $('#message-bar').html(data);
+
+ if (this.options.onFailure) {
+ this.options.onFailure(data);
+ }
+
+ return val;
+ },
+
+ // Renders GFM for elements with 'markdown' class
+ renderGFM: function(){
+ $('.markdown').each(function(){
+ $(this).html(marked($(this).html()));
+ });
+ }
+
+});
+
+window.SwaggerUi.Views = {};
+
+// don't break backward compatibility with previous versions and warn users to upgrade their code
+(function(){
+ window.authorizations = {
+ add: function() {
+ warn('Using window.authorizations is deprecated. Please use SwaggerUi.api.clientAuthorizations.add().');
+
+ if (typeof window.swaggerUi === 'undefined') {
+ throw new TypeError('window.swaggerUi is not defined');
+ }
+
+ if (window.swaggerUi instanceof SwaggerUi) {
+ window.swaggerUi.api.clientAuthorizations.add.apply(window.swaggerUi.api.clientAuthorizations, arguments);
+ }
+ }
+ };
+
+ window.ApiKeyAuthorization = function() {
+ warn('window.ApiKeyAuthorization is deprecated. Please use SwaggerClient.ApiKeyAuthorization.');
+ SwaggerClient.ApiKeyAuthorization.apply(window, arguments);
+ };
+
+ window.PasswordAuthorization = function() {
+ warn('window.PasswordAuthorization is deprecated. Please use SwaggerClient.PasswordAuthorization.');
+ SwaggerClient.PasswordAuthorization.apply(window, arguments);
+ };
+
+ function warn(message) {
+ if ('console' in window && typeof window.console.warn === 'function') {
+ console.warn(message);
+ }
+ }
+})();
+
+
+// UMD
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['b'], function (b) {
+ return (root.SwaggerUi = factory(b));
+ });
+ } else if (typeof exports === 'object') {
+ // Node. Does not work with strict CommonJS, but
+ // only CommonJS-like enviroments that support module.exports,
+ // like Node.
+ module.exports = factory(require('b'));
+ } else {
+ // Browser globals
+ root.SwaggerUi = factory(root.b);
+ }
+}(this, function () {
+ return SwaggerUi;
+}));
+'use strict';
+
+SwaggerUi.Views.ApiKeyButton = Backbone.View.extend({ // TODO: append this to global SwaggerUi
+
+ events:{
+ 'click #apikey_button' : 'toggleApiKeyContainer',
+ 'click #apply_api_key' : 'applyApiKey'
+ },
+
+ initialize: function(opts){
+ this.options = opts || {};
+ this.router = this.options.router;
+ },
+
+ render: function(){
+ var template = this.template();
+ $(this.el).html(template(this.model));
+
+ return this;
+ },
+
+
+ applyApiKey: function(){
+ var keyAuth = new SwaggerClient.ApiKeyAuthorization(
+ this.model.name,
+ $('#input_apiKey_entry').val(),
+ this.model.in
+ );
+ this.router.api.clientAuthorizations.add(this.model.name, keyAuth);
+ this.router.load();
+ $('#apikey_container').show();
+ },
+
+ toggleApiKeyContainer: function(){
+ if ($('#apikey_container').length) {
+
+ var elem = $('#apikey_container').first();
+
+ if (elem.is(':visible')){
+ elem.hide();
+ } else {
+
+ // hide others
+ $('.auth_container').hide();
+ elem.show();
+ }
+ }
+ },
+
+ template: function(){
+ return Handlebars.templates.apikey_button_view;
+ }
+
+});
+'use strict';
+
+SwaggerUi.Views.BasicAuthButton = Backbone.View.extend({
+
+
+ initialize: function (opts) {
+ this.options = opts || {};
+ this.router = this.options.router;
+ },
+
+ render: function(){
+ var template = this.template();
+ $(this.el).html(template(this.model));
+
+ return this;
+ },
+
+ events: {
+ 'click #basic_auth_button' : 'togglePasswordContainer',
+ 'click #apply_basic_auth' : 'applyPassword'
+ },
+
+ applyPassword: function(){
+ var username = $('.input_username').val();
+ var password = $('.input_password').val();
+ var basicAuth = new SwaggerClient.PasswordAuthorization('basic', username, password);
+ this.router.api.clientAuthorizations.add(this.model.type, basicAuth);
+ this.router.load();
+ $('#basic_auth_container').hide();
+ },
+
+ togglePasswordContainer: function(){
+ if ($('#basic_auth_container').length) {
+ var elem = $('#basic_auth_container').show();
+ if (elem.is(':visible')){
+ elem.slideUp();
+ } else {
+ // hide others
+ $('.auth_container').hide();
+ elem.show();
+ }
+ }
+ },
+
+ template: function(){
+ return Handlebars.templates.basic_auth_button_view;
+ }
+
+});
+'use strict';
+
+SwaggerUi.Views.ContentTypeView = Backbone.View.extend({
+ initialize: function() {},
+
+ render: function(){
+ $(this.el).html(Handlebars.templates.content_type(this.model));
+
+ $('label[for=contentType]', $(this.el)).text('Response Content Type');
+
+ return this;
+ }
+});
+'use strict';
+
+SwaggerUi.Views.HeaderView = Backbone.View.extend({
+ events: {
+ 'click #show-pet-store-icon' : 'showPetStore',
+ 'click #show-wordnik-dev-icon' : 'showWordnikDev',
+ 'click #explore' : 'showCustom',
+ 'keyup #input_baseUrl' : 'showCustomOnKeyup',
+ 'keyup #input_apiKey' : 'showCustomOnKeyup'
+ },
+
+ initialize: function(){},
+
+ showPetStore: function(){
+ this.trigger('update-swagger-ui', {
+ url:'http://petstore.swagger.io/v2/swagger.json'
+ });
+ },
+
+ showWordnikDev: function(){
+ this.trigger('update-swagger-ui', {
+ url: 'http://api.wordnik.com/v4/resources.json'
+ });
+ },
+
+ showCustomOnKeyup: function(e){
+ if (e.keyCode === 13) {
+ this.showCustom();
+ }
+ },
+
+ showCustom: function(e){
+ if (e) {
+ e.preventDefault();
+ }
+
+ this.trigger('update-swagger-ui', {
+ url: $('#input_baseUrl').val(),
+ apiKey: $('#input_apiKey').val()
+ });
+ },
+
+ update: function(url, apiKey, trigger){
+ if (trigger === undefined) {
+ trigger = false;
+ }
+
+ $('#input_baseUrl').val(url);
+
+ //$('#input_apiKey').val(apiKey);
+ if (trigger) {
+ this.trigger('update-swagger-ui', {url:url});
+ }
+ }
+});
+
+'use strict';
+
+SwaggerUi.Views.MainView = Backbone.View.extend({
+ apisSorter : {
+ alpha : function(a,b){ return a.name.localeCompare(b.name); }
+ },
+ operationsSorters : {
+ alpha : function(a,b){ return a.path.localeCompare(b.path); },
+ method : function(a,b){ return a.method.localeCompare(b.method); }
+ },
+ initialize: function(opts){
+ var sorterOption, sorterFn, key, value;
+ opts = opts || {};
+
+ this.router = opts.router;
+
+ // Sort APIs
+ if (opts.swaggerOptions.apisSorter) {
+ sorterOption = opts.swaggerOptions.apisSorter;
+ if (_.isFunction(sorterOption)) {
+ sorterFn = sorterOption;
+ } else {
+ sorterFn = this.apisSorter[sorterOption];
+ }
+ if (_.isFunction(sorterFn)) {
+ this.model.apisArray.sort(sorterFn);
+ }
+ }
+ // Sort operations of each API
+ if (opts.swaggerOptions.operationsSorter) {
+ sorterOption = opts.swaggerOptions.operationsSorter;
+ if (_.isFunction(sorterOption)) {
+ sorterFn = sorterOption;
+ } else {
+ sorterFn = this.operationsSorters[sorterOption];
+ }
+ if (_.isFunction(sorterFn)) {
+ for (key in this.model.apisArray) {
+ this.model.apisArray[key].operationsArray.sort(sorterFn);
+ }
+ }
+ }
+
+ // set up the UI for input
+ this.model.auths = [];
+
+ for (key in this.model.securityDefinitions) {
+ value = this.model.securityDefinitions[key];
+
+ this.model.auths.push({
+ name: key,
+ type: value.type,
+ value: value
+ });
+ }
+
+ if (this.model.swaggerVersion === '2.0') {
+ if ('validatorUrl' in opts.swaggerOptions) {
+
+ // Validator URL specified explicitly
+ this.model.validatorUrl = opts.swaggerOptions.validatorUrl;
+
+ } else if (this.model.url.indexOf('localhost') > 0) {
+
+ // Localhost override
+ this.model.validatorUrl = null;
+
+ } else {
+
+ // Default validator
+ this.model.validatorUrl = 'http://online.swagger.io/validator';
+ }
+ }
+ },
+
+ render: function(){
+ if (this.model.securityDefinitions) {
+ for (var name in this.model.securityDefinitions) {
+ var auth = this.model.securityDefinitions[name];
+ var button;
+
+ if (auth.type === 'apiKey' && $('#apikey_button').length === 0) {
+ button = new SwaggerUi.Views.ApiKeyButton({model: auth, router: this.router}).render().el;
+ $('.auth_main_container').append(button);
+ }
+
+ if (auth.type === 'basicAuth' && $('#basic_auth_button').length === 0) {
+ button = new SwaggerUi.Views.BasicAuthButton({model: auth, router: this.router}).render().el;
+ $('.auth_main_container').append(button);
+ }
+ }
+ }
+
+ // Render the outer container for resources
+ $(this.el).html(Handlebars.templates.main(this.model));
+
+ // Render each resource
+
+ var resources = {};
+ var counter = 0;
+ for (var i = 0; i < this.model.apisArray.length; i++) {
+ var resource = this.model.apisArray[i];
+ var id = resource.name;
+ while (typeof resources[id] !== 'undefined') {
+ id = id + '_' + counter;
+ counter += 1;
+ }
+ resource.id = id;
+ resources[id] = resource;
+ this.addResource(resource, this.model.auths);
+ }
+
+ $('.propWrap').hover(function onHover(){
+ $('.optionsWrapper', $(this)).show();
+ }, function offhover(){
+ $('.optionsWrapper', $(this)).hide();
+ });
+ return this;
+ },
+
+ addResource: function(resource, auths){
+ // Render a resource and add it to resources li
+ resource.id = resource.id.replace(/\s/g, '_');
+ var resourceView = new SwaggerUi.Views.ResourceView({
+ model: resource,
+ router: this.router,
+ tagName: 'li',
+ id: 'resource_' + resource.id,
+ className: 'resource',
+ auths: auths,
+ swaggerOptions: this.options.swaggerOptions
+ });
+ $('#resources').append(resourceView.render().el);
+ },
+
+ clear: function(){
+ $(this.el).html('');
+ }
+});
+
+'use strict';
+
+SwaggerUi.Views.OperationView = Backbone.View.extend({
+ invocationUrl: null,
+
+ events: {
+ 'submit .sandbox' : 'submitOperation',
+ 'click .submit' : 'submitOperation',
+ 'click .response_hider' : 'hideResponse',
+ 'click .toggleOperation' : 'toggleOperationContent',
+ 'mouseenter .api-ic' : 'mouseEnter',
+ 'mouseout .api-ic' : 'mouseExit',
+ },
+
+ initialize: function(opts) {
+ opts = opts || {};
+ this.router = opts.router;
+ this.auths = opts.auths;
+ this.parentId = this.model.parentId;
+ this.nickname = this.model.nickname;
+ this.model.encodedParentId = encodeURIComponent(this.parentId);
+ return this;
+ },
+
+ mouseEnter: function(e) {
+ var elem = $(this.el).find('.content');
+ var x = e.pageX;
+ var y = e.pageY;
+ var scX = $(window).scrollLeft();
+ var scY = $(window).scrollTop();
+ var scMaxX = scX + $(window).width();
+ var scMaxY = scY + $(window).height();
+ var wd = elem.width();
+ var hgh = elem.height();
+
+ if (x + wd > scMaxX) {
+ x = scMaxX - wd;
+ }
+
+ if (x < scX) {
+ x = scX;
+ }
+
+ if (y + hgh > scMaxY) {
+ y = scMaxY - hgh;
+ }
+
+ if (y < scY) {
+ y = scY;
+ }
+
+ var pos = {};
+ pos.top = y;
+ pos.left = x;
+ elem.css(pos);
+ $(e.currentTarget.parentNode).find('#api_information_panel').show();
+ },
+
+ mouseExit: function(e) {
+ $(e.currentTarget.parentNode).find('#api_information_panel').hide();
+ },
+
+ // Note: copied from CoffeeScript compiled file
+ // TODO: redactor
+ render: function() {
+ var a, auth, auths, code, contentTypeModel, isMethodSubmissionSupported, k, key, l, len, len1, len2, len3, len4, m, modelAuths, n, o, p, param, q, ref, ref1, ref2, ref3, ref4, ref5, responseContentTypeView, responseSignatureView, schema, schemaObj, scopeIndex, signatureModel, statusCode, successResponse, type, v, value;
+ isMethodSubmissionSupported = jQuery.inArray(this.model.method, this.model.supportedSubmitMethods()) >= 0;
+ if (!isMethodSubmissionSupported) {
+ this.model.isReadOnly = true;
+ }
+ this.model.description = this.model.description || this.model.notes;
+ this.model.oauth = null;
+ modelAuths = this.model.authorizations || this.model.security;
+ if (modelAuths) {
+ if (Array.isArray(modelAuths)) {
+ for (l = 0, len = modelAuths.length; l < len; l++) {
+ auths = modelAuths[l];
+ for (key in auths) {
+ auth = auths[key];
+ for (a in this.auths) {
+ auth = this.auths[a];
+ if (auth.type === 'oauth2') {
+ this.model.oauth = {};
+ this.model.oauth.scopes = [];
+ ref1 = auth.value.scopes;
+ for (k in ref1) {
+ v = ref1[k];
+ scopeIndex = auths[key].indexOf(k);
+ if (scopeIndex >= 0) {
+ o = {
+ scope: k,
+ description: v
+ };
+ this.model.oauth.scopes.push(o);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ for (k in modelAuths) {
+ v = modelAuths[k];
+ if (k === 'oauth2') {
+ if (this.model.oauth === null) {
+ this.model.oauth = {};
+ }
+ if (this.model.oauth.scopes === void 0) {
+ this.model.oauth.scopes = [];
+ }
+ for (m = 0, len1 = v.length; m < len1; m++) {
+ o = v[m];
+ this.model.oauth.scopes.push(o);
+ }
+ }
+ }
+ }
+ }
+ if (typeof this.model.responses !== 'undefined') {
+ this.model.responseMessages = [];
+ ref2 = this.model.responses;
+ for (code in ref2) {
+ value = ref2[code];
+ schema = null;
+ schemaObj = this.model.responses[code].schema;
+ if (schemaObj && schemaObj.$ref) {
+ schema = schemaObj.$ref;
+ if (schema.indexOf('#/definitions/') === 0) {
+ schema = schema.substring('#/definitions/'.length);
+ }
+ }
+ this.model.responseMessages.push({
+ code: code,
+ message: value.description,
+ responseModel: schema
+ });
+ }
+ }
+ if (typeof this.model.responseMessages === 'undefined') {
+ this.model.responseMessages = [];
+ }
+ signatureModel = null;
+ if (this.model.successResponse) {
+ successResponse = this.model.successResponse;
+ for (key in successResponse) {
+ value = successResponse[key];
+ this.model.successCode = key;
+ if (typeof value === 'object' && typeof value.createJSONSample === 'function') {
+ signatureModel = {
+ sampleJSON: JSON.stringify(value.createJSONSample(), void 0, 2),
+ isParam: false,
+ signature: value.getMockSignature()
+ };
+ }
+ }
+ } else if (this.model.responseClassSignature && this.model.responseClassSignature !== 'string') {
+ signatureModel = {
+ sampleJSON: this.model.responseSampleJSON,
+ isParam: false,
+ signature: this.model.responseClassSignature
+ };
+ }
+ $(this.el).html(Handlebars.templates.operation(this.model));
+ if (signatureModel) {
+ responseSignatureView = new SwaggerUi.Views.SignatureView({
+ model: signatureModel,
+ router: this.router,
+ tagName: 'div'
+ });
+ $('.model-signature', $(this.el)).append(responseSignatureView.render().el);
+ } else {
+ this.model.responseClassSignature = 'string';
+ $('.model-signature', $(this.el)).html(this.model.type);
+ }
+ contentTypeModel = {
+ isParam: false
+ };
+ contentTypeModel.consumes = this.model.consumes;
+ contentTypeModel.produces = this.model.produces;
+ ref3 = this.model.parameters;
+ for (n = 0, len2 = ref3.length; n < len2; n++) {
+ param = ref3[n];
+ type = param.type || param.dataType || '';
+ if (typeof type === 'undefined') {
+ schema = param.schema;
+ if (schema && schema.$ref) {
+ ref = schema.$ref;
+ if (ref.indexOf('#/definitions/') === 0) {
+ type = ref.substring('#/definitions/'.length);
+ } else {
+ type = ref;
+ }
+ }
+ }
+ if (type && type.toLowerCase() === 'file') {
+ if (!contentTypeModel.consumes) {
+ contentTypeModel.consumes = 'multipart/form-data';
+ }
+ }
+ param.type = type;
+ }
+ responseContentTypeView = new SwaggerUi.Views.ResponseContentTypeView({
+ model: contentTypeModel,
+ router: this.router
+ });
+ $('.response-content-type', $(this.el)).append(responseContentTypeView.render().el);
+ ref4 = this.model.parameters;
+ for (p = 0, len3 = ref4.length; p < len3; p++) {
+ param = ref4[p];
+ this.addParameter(param, contentTypeModel.consumes);
+ }
+ ref5 = this.model.responseMessages;
+ for (q = 0, len4 = ref5.length; q < len4; q++) {
+ statusCode = ref5[q];
+ this.addStatusCode(statusCode);
+ }
+ return this;
+ },
+
+ addParameter: function(param, consumes) {
+ // Render a parameter
+ param.consumes = consumes;
+ var paramView = new SwaggerUi.Views.ParameterView({
+ model: param,
+ tagName: 'tr',
+ readOnly: this.model.isReadOnly
+ });
+ $('.operation-params', $(this.el)).append(paramView.render().el);
+ },
+
+ addStatusCode: function(statusCode) {
+ // Render status codes
+ var statusCodeView = new SwaggerUi.Views.StatusCodeView({
+ model: statusCode,
+ tagName: 'tr',
+ router: this.router
+ });
+ $('.operation-status', $(this.el)).append(statusCodeView.render().el);
+ },
+
+ // Note: copied from CoffeeScript compiled file
+ // TODO: redactor
+ submitOperation: function(e) {
+ var error_free, form, isFileUpload, l, len, len1, len2, m, map, n, o, opts, ref1, ref2, ref3, val;
+ if (e !== null) {
+ e.preventDefault();
+ }
+ form = $('.sandbox', $(this.el));
+ error_free = true;
+ form.find('input.required').each(function() {
+ $(this).removeClass('error');
+ if (jQuery.trim($(this).val()) === '') {
+ $(this).addClass('error');
+ $(this).wiggle({
+ callback: (function(_this) {
+ return function() {
+ $(_this).focus();
+ };
+ })(this)
+ });
+ error_free = false;
+ }
+ });
+ form.find('textarea.required').each(function() {
+ $(this).removeClass('error');
+ if (jQuery.trim($(this).val()) === '') {
+ $(this).addClass('error');
+ $(this).wiggle({
+ callback: (function(_this) {
+ return function() {
+ return $(_this).focus();
+ };
+ })(this)
+ });
+ error_free = false;
+ }
+ });
+ if (error_free) {
+ map = {};
+ opts = {
+ parent: this
+ };
+ isFileUpload = false;
+ ref1 = form.find('input');
+ for (l = 0, len = ref1.length; l < len; l++) {
+ o = ref1[l];
+ if ((o.value !== null) && jQuery.trim(o.value).length > 0) {
+ map[o.name] = o.value;
+ }
+ if (o.type === 'file') {
+ map[o.name] = o.files[0];
+ isFileUpload = true;
+ }
+ }
+ ref2 = form.find('textarea');
+ for (m = 0, len1 = ref2.length; m < len1; m++) {
+ o = ref2[m];
+ if ((o.value !== null) && jQuery.trim(o.value).length > 0) {
+ map[o.name] = o.value;
+ }
+ }
+ ref3 = form.find('select');
+ for (n = 0, len2 = ref3.length; n < len2; n++) {
+ o = ref3[n];
+ val = this.getSelectedValue(o);
+ if ((val !== null) && jQuery.trim(val).length > 0) {
+ map[o.name] = val;
+ }
+ }
+ opts.responseContentType = $('div select[name=responseContentType]', $(this.el)).val();
+ opts.requestContentType = $('div select[name=parameterContentType]', $(this.el)).val();
+ $('.response_throbber', $(this.el)).show();
+ if (isFileUpload) {
+ return this.handleFileUpload(map, form);
+ } else {
+ return this.model['do'](map, opts, this.showCompleteStatus, this.showErrorStatus, this);
+ }
+ }
+ },
+
+ success: function(response, parent) {
+ parent.showCompleteStatus(response);
+ },
+
+ // Note: This is compiled code
+ // TODO: Refactor
+ handleFileUpload: function(map, form) {
+ var bodyParam, el, headerParams, l, len, len1, len2, len3, m, n, o, obj, p, param, params, ref1, ref2, ref3, ref4;
+ ref1 = form.serializeArray();
+ for (l = 0, len = ref1.length; l < len; l++) {
+ o = ref1[l];
+ if ((o.value !== null) && jQuery.trim(o.value).length > 0) {
+ map[o.name] = o.value;
+ }
+ }
+ bodyParam = new FormData();
+ params = 0;
+ ref2 = this.model.parameters;
+ for (m = 0, len1 = ref2.length; m < len1; m++) {
+ param = ref2[m];
+ if (param.paramType === 'form' || param['in'] === 'formData') {
+ if (param.type.toLowerCase() !== 'file' && map[param.name] !== void 0) {
+ bodyParam.append(param.name, map[param.name]);
+ }
+ }
+ }
+ headerParams = {};
+ ref3 = this.model.parameters;
+ for (n = 0, len2 = ref3.length; n < len2; n++) {
+ param = ref3[n];
+ if (param.paramType === 'header') {
+ headerParams[param.name] = map[param.name];
+ }
+ }
+ ref4 = form.find('input[type~="file"]');
+ for (p = 0, len3 = ref4.length; p < len3; p++) {
+ el = ref4[p];
+ if (typeof el.files[0] !== 'undefined') {
+ bodyParam.append($(el).attr('name'), el.files[0]);
+ params += 1;
+ }
+ }
+ this.invocationUrl = this.model.supportHeaderParams() ? (headerParams = this.model.getHeaderParams(map), delete headerParams['Content-Type'], this.model.urlify(map, false)) : this.model.urlify(map, true);
+ $('.request_url', $(this.el)).html('<pre></pre>');
+ $('.request_url pre', $(this.el)).text(this.invocationUrl);
+ obj = {
+ type: this.model.method,
+ url: this.invocationUrl,
+ headers: headerParams,
+ data: bodyParam,
+ dataType: 'json',
+ contentType: false,
+ processData: false,
+ error: (function(_this) {
+ return function(data) {
+ return _this.showErrorStatus(_this.wrap(data), _this);
+ };
+ })(this),
+ success: (function(_this) {
+ return function(data) {
+ return _this.showResponse(data, _this);
+ };
+ })(this),
+ complete: (function(_this) {
+ return function(data) {
+ return _this.showCompleteStatus(_this.wrap(data), _this);
+ };
+ })(this)
+ };
+ if (window.authorizations) {
+ window.authorizations.apply(obj);
+ }
+ jQuery.ajax(obj);
+ return false;
+ // end of file-upload nastiness
+ },
+ // wraps a jquery response as a shred response
+
+ wrap: function(data) {
+ var h, headerArray, headers, i, l, len, o;
+ headers = {};
+ headerArray = data.getAllResponseHeaders().split('\r');
+ for (l = 0, len = headerArray.length; l < len; l++) {
+ i = headerArray[l];
+ h = i.match(/^([^:]*?):(.*)$/);
+ if (!h) {
+ h = [];
+ }
+ h.shift();
+ if (h[0] !== void 0 && h[1] !== void 0) {
+ headers[h[0].trim()] = h[1].trim();
+ }
+ }
+ o = {};
+ o.content = {};
+ o.content.data = data.responseText;
+ o.headers = headers;
+ o.request = {};
+ o.request.url = this.invocationUrl;
+ o.status = data.status;
+ return o;
+ },
+
+ getSelectedValue: function(select) {
+ if (!select.multiple) {
+ return select.value;
+ } else {
+ var options = [];
+ for (var l = 0, len = select.options.length; l < len; l++) {
+ var opt = select.options[l];
+ if (opt.selected) {
+ options.push(opt.value);
+ }
+ }
+ if (options.length > 0) {
+ return options;
+ } else {
+ return null;
+ }
+ }
+ },
+
+ // handler for hide response link
+ hideResponse: function(e) {
+ if (e) { e.preventDefault(); }
+ $('.response', $(this.el)).slideUp();
+ $('.response_hider', $(this.el)).fadeOut();
+ },
+
+ // Show response from server
+ showResponse: function(response) {
+ var prettyJson = JSON.stringify(response, null, '\t').replace(/\n/g, '<br>');
+ $('.response_body', $(this.el)).html(_.escape(prettyJson));
+ },
+
+ // Show error from server
+ showErrorStatus: function(data, parent) {
+ parent.showStatus(data);
+ },
+
+ // show the status codes
+ showCompleteStatus: function(data, parent){
+ parent.showStatus(data);
+ },
+
+ // Adapted from http://stackoverflow.com/a/2893259/454004
+ // Note: directly ported from CoffeeScript
+ // TODO: Cleanup CoffeeScript artifacts
+ formatXml: function(xml) {
+ var contexp, fn, formatted, indent, l, lastType, len, lines, ln, pad, reg, transitions, wsexp;
+ reg = /(>)(<)(\/*)/g;
+ wsexp = /[ ]*(.*)[ ]+\n/g;
+ contexp = /(<.+>)(.+\n)/g;
+ xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
+ pad = 0;
+ formatted = '';
+ lines = xml.split('\n');
+ indent = 0;
+ lastType = 'other';
+ transitions = {
+ 'single->single': 0,
+ 'single->closing': -1,
+ 'single->opening': 0,
+ 'single->other': 0,
+ 'closing->single': 0,
+ 'closing->closing': -1,
+ 'closing->opening': 0,
+ 'closing->other': 0,
+ 'opening->single': 1,
+ 'opening->closing': 0,
+ 'opening->opening': 1,
+ 'opening->other': 1,
+ 'other->single': 0,
+ 'other->closing': -1,
+ 'other->opening': 0,
+ 'other->other': 0
+ };
+ fn = function(ln) {
+ var fromTo, j, key, padding, type, types, value;
+ types = {
+ single: Boolean(ln.match(/<.+\/>/)),
+ closing: Boolean(ln.match(/<\/.+>/)),
+ opening: Boolean(ln.match(/<[^!?].*>/))
+ };
+ type = ((function() {
+ var results;
+ results = [];
+ for (key in types) {
+ value = types[key];
+ if (value) {
+ results.push(key);
+ }
+ }
+ return results;
+ })())[0];
+ type = type === void 0 ? 'other' : type;
+ fromTo = lastType + '->' + type;
+ lastType = type;
+ padding = '';
+ indent += transitions[fromTo];
+ padding = ((function() {
+ var m, ref1, results;
+ results = [];
+ for (j = m = 0, ref1 = indent; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) {
+ results.push(' ');
+ }
+ return results;
+ })()).join('');
+ if (fromTo === 'opening->closing') {
+ formatted = formatted.substr(0, formatted.length - 1) + ln + '\n';
+ } else {
+ formatted += padding + ln + '\n';
+ }
+ };
+ for (l = 0, len = lines.length; l < len; l++) {
+ ln = lines[l];
+ fn(ln);
+ }
+ return formatted;
+ },
+
+ // puts the response data in UI
+ showStatus: function(response) {
+ var url, content;
+ if (response.content === undefined) {
+ content = response.data;
+ url = response.url;
+ } else {
+ content = response.content.data;
+ url = response.request.url;
+ }
+ var headers = response.headers;
+
+ // if server is nice, and sends content-type back, we can use it
+ var contentType = null;
+ if (headers) {
+ contentType = headers['Content-Type'] || headers['content-type'];
+ if (contentType) {
+ contentType = contentType.split(';')[0].trim();
+ }
+ }
+ $('.response_body', $(this.el)).removeClass('json');
+ $('.response_body', $(this.el)).removeClass('xml');
+
+ var supportsAudioPlayback = function(contentType){
+ var audioElement = document.createElement('audio');
+ return !!(audioElement.canPlayType && audioElement.canPlayType(contentType).replace(/no/, ''));
+ };
+
+ var pre;
+ var code;
+ if (!content) {
+ code = $('<code />').text('no content');
+ pre = $('<pre class="json" />').append(code);
+
+ // JSON
+ } else if (contentType === 'application/json' || /\+json$/.test(contentType)) {
+ var json = null;
+ try {
+ json = JSON.stringify(JSON.parse(content), null, ' ');
+ } catch (_error) {
+ json = 'can\'t parse JSON. Raw result:\n\n' + content;
+ }
+ code = $('<code />').text(json);
+ pre = $('<pre class="json" />').append(code);
+
+ // XML
+ } else if (contentType === 'application/xml' || /\+xml$/.test(contentType)) {
+ code = $('<code />').text(this.formatXml(content));
+ pre = $('<pre class="xml" />').append(code);
+
+ // HTML
+ } else if (contentType === 'text/html') {
+ code = $('<code />').html(_.escape(content));
+ pre = $('<pre class="xml" />').append(code);
+
+ // Image
+ } else if (/^image\//.test(contentType)) {
+ pre = $('<img>').attr('src', url);
+
+ // Audio
+ } else if (/^audio\//.test(contentType) && supportsAudioPlayback(contentType)) {
+ pre = $('<audio controls>').append($('<source>').attr('src', url).attr('type', contentType));
+
+ // Download
+ } else if (headers['Content-Disposition'].test(/attachment/) ||
+ headers['content-disposition'].test(/attachment/) ||
+ headers['Content-Description'].test(/File Transfer/) ||
+ headers['content-description'].test(/File Transfer/)) {
+
+ if ('Blob' in window) {
+ var type = contentType || 'text/html';
+ var blob = new Blob([content], {type: type});
+ var a = document.createElement('a');
+ var href = window.URL.createObjectURL(blob);
+ var fileName = response.url.substr(response.url.lastIndexOf('/') + 1);
+ var download = [type, fileName, href].join(':');
+
+ a.setAttribute('href', href);
+ a.setAttribute('download', download);
+ a.innerText = 'Download ' + fileName;
+
+ pre = $('<div/>').append(a);
+ } else {
+ pre = $('<pre class="json" />').append('Download headers detected but your browser does not support downloading binary via XHR (Blob).');
+ }
+
+ // Location header based redirect download
+ } else if(headers.location || headers.Location) {
+ window.location = response.url;
+
+ // Anything else (CORS)
+ } else {
+ code = $('<code />').text(content);
+ pre = $('<pre class="json" />').append(code);
+ }
+ var response_body = pre;
+ $('.request_url', $(this.el)).html('<pre></pre>');
+ $('.request_url pre', $(this.el)).text(url);
+ $('.response_code', $(this.el)).html('<pre>' + response.status + '</pre>');
+ $('.response_body', $(this.el)).html(response_body);
+ $('.response_headers', $(this.el)).html('<pre>' + _.escape(JSON.stringify(response.headers, null, ' ')).replace(/\n/g, '<br>') + '</pre>');
+ $('.response', $(this.el)).slideDown();
+ $('.response_hider', $(this.el)).show();
+ $('.response_throbber', $(this.el)).hide();
+ var response_body_el = $('.response_body', $(this.el))[0];
+
+ // only highlight the response if response is less than threshold, default state is highlight response
+ var opts = this.options.swaggerOptions;
+ if (opts.highlightSizeThreshold && response.data.length > opts.highlightSizeThreshold) {
+ return response_body_el;
+ } else {
+ return hljs.highlightBlock(response_body_el);
+ }
+ },
+
+ toggleOperationContent: function() {
+ var elem = $('#' + Docs.escapeResourceName(this.parentId + '_' + this.nickname + '_content'));
+ if (elem.is(':visible')){
+ Docs.collapseOperation(elem);
+ } else {
+ Docs.expandOperation(elem);
+ }
+ }
+});
+
+'use strict';
+
+SwaggerUi.Views.ParameterContentTypeView = Backbone.View.extend({
+ initialize: function () {},
+
+ render: function(){
+ $(this.el).html(Handlebars.templates.parameter_content_type(this.model));
+
+ $('label[for=parameterContentType]', $(this.el)).text('Parameter content type:');
+
+ return this;
+ }
+
+});
+'use strict';
+
+SwaggerUi.Views.ParameterView = Backbone.View.extend({
+ initialize: function(){
+ Handlebars.registerHelper('isArray', function(param, opts) {
+ if (param.type.toLowerCase() === 'array' || param.allowMultiple) {
+ opts.fn(this);
+ } else {
+ opts.inverse(this);
+ }
+ });
+ },
+
+ render: function() {
+ var type = this.model.type || this.model.dataType;
+
+ if (typeof type === 'undefined') {
+ var schema = this.model.schema;
+ if (schema && schema.$ref) {
+ var ref = schema.$ref;
+ if (ref.indexOf('#/definitions/') === 0) {
+ type = ref.substring('#/definitions/'.length);
+ } else {
+ type = ref;
+ }
+ }
+ }
+
+ this.model.type = type;
+ this.model.paramType = this.model.in || this.model.paramType;
+ this.model.isBody = this.model.paramType === 'body' || this.model.in === 'body';
+ this.model.isFile = type && type.toLowerCase() === 'file';
+ this.model.default = (this.model.default || this.model.defaultValue);
+
+ if (this.model.allowableValues) {
+ this.model.isList = true;
+ }
+
+ var template = this.template();
+ $(this.el).html(template(this.model));
+
+ var signatureModel = {
+ sampleJSON: this.model.sampleJSON,
+ isParam: true,
+ signature: this.model.signature
+ };
+
+ if (this.model.sampleJSON) {
+ var signatureView = new SwaggerUi.Views.SignatureView({model: signatureModel, tagName: 'div'});
+ $('.model-signature', $(this.el)).append(signatureView.render().el);
+ }
+ else {
+ $('.model-signature', $(this.el)).html(this.model.signature);
+ }
+
+ var isParam = false;
+
+ if (this.model.isBody) {
+ isParam = true;
+ }
+
+ var contentTypeModel = {
+ isParam: isParam
+ };
+
+ contentTypeModel.consumes = this.model.consumes;
+
+ if (isParam) {
+ var parameterContentTypeView = new SwaggerUi.Views.ParameterContentTypeView({model: contentTypeModel});
+ $('.parameter-content-type', $(this.el)).append(parameterContentTypeView.render().el);
+ }
+
+ else {
+ var responseContentTypeView = new SwaggerUi.Views.ResponseContentTypeView({model: contentTypeModel});
+ $('.response-content-type', $(this.el)).append(responseContentTypeView.render().el);
+ }
+
+ return this;
+ },
+
+ // Return an appropriate template based on if the parameter is a list, readonly, required
+ template: function(){
+ if (this.model.isList) {
+ return Handlebars.templates.param_list;
+ } else {
+ if (this.options.readOnly) {
+ if (this.model.required) {
+ return Handlebars.templates.param_readonly_required;
+ } else {
+ return Handlebars.templates.param_readonly;
+ }
+ } else {
+ if (this.model.required) {
+ return Handlebars.templates.param_required;
+ } else {
+ return Handlebars.templates.param;
+ }
+ }
+ }
+ }
+});
+'use strict';
+
+SwaggerUi.Views.ResourceView = Backbone.View.extend({
+ initialize: function(opts) {
+ opts = opts || {};
+ this.router = opts.router;
+ this.auths = opts.auths;
+ if ('' === this.model.description) {
+ this.model.description = null;
+ }
+ if (this.model.description) {
+ this.model.summary = this.model.description;
+ }
+ },
+
+ render: function(){
+ var methods = {};
+
+
+ $(this.el).html(Handlebars.templates.resource(this.model));
+
+ // Render each operation
+ for (var i = 0; i < this.model.operationsArray.length; i++) {
+ var operation = this.model.operationsArray[i];
+ var counter = 0;
+ var id = operation.nickname;
+
+ while (typeof methods[id] !== 'undefined') {
+ id = id + '_' + counter;
+ counter += 1;
+ }
+
+ methods[id] = operation;
+
+ operation.nickname = id;
+ operation.parentId = this.model.id;
+ this.addOperation(operation);
+ }
+
+ $('.toggleEndpointList', this.el).click(this.callDocs.bind(this, 'toggleEndpointListForResource'));
+ $('.collapseResource', this.el).click(this.callDocs.bind(this, 'collapseOperationsForResource'));
+ $('.expandResource', this.el).click(this.callDocs.bind(this, 'expandOperationsForResource'));
+
+ return this;
+ },
+
+ addOperation: function(operation) {
+
+ operation.number = this.number;
+
+ // Render an operation and add it to operations li
+ var operationView = new SwaggerUi.Views.OperationView({
+ model: operation,
+ router: this.router,
+ tagName: 'li',
+ className: 'endpoint',
+ swaggerOptions: this.options.swaggerOptions,
+ auths: this.auths
+ });
+
+ $('.endpoints', $(this.el)).append(operationView.render().el);
+
+ this.number++;
+
+ },
+ // Generic Event handler (`Docs` is global)
+
+
+ callDocs: function(fnName, e) {
+ e.preventDefault();
+ Docs[fnName](e.currentTarget.getAttribute('data-id'));
+ }
+});
+'use strict';
+
+SwaggerUi.Views.ResponseContentTypeView = Backbone.View.extend({
+ initialize: function(){},
+
+ render: function(){
+ $(this.el).html(Handlebars.templates.response_content_type(this.model));
+
+ $('label[for=responseContentType]', $(this.el)).text('Response Content Type');
+
+ return this;
+ }
+});
+'use strict';
+
+SwaggerUi.Views.SignatureView = Backbone.View.extend({
+ events: {
+ 'click a.description-link' : 'switchToDescription',
+ 'click a.snippet-link' : 'switchToSnippet',
+ 'mousedown .snippet' : 'snippetToTextArea'
+ },
+
+ initialize: function () {
+
+ },
+
+ render: function(){
+
+ $(this.el).html(Handlebars.templates.signature(this.model));
+
+ this.switchToSnippet();
+
+ this.isParam = this.model.isParam;
+
+ if (this.isParam) {
+ $('.notice', $(this.el)).text('Click to set as parameter value');
+ }
+
+ return this;
+ },
+
+ // handler for show signature
+ switchToDescription: function(e){
+ if (e) { e.preventDefault(); }
+
+ $('.snippet', $(this.el)).hide();
+ $('.description', $(this.el)).show();
+ $('.description-link', $(this.el)).addClass('selected');
+ $('.snippet-link', $(this.el)).removeClass('selected');
+ },
+
+ // handler for show sample
+ switchToSnippet: function(e){
+ if (e) { e.preventDefault(); }
+
+ $('.description', $(this.el)).hide();
+ $('.snippet', $(this.el)).show();
+ $('.snippet-link', $(this.el)).addClass('selected');
+ $('.description-link', $(this.el)).removeClass('selected');
+ },
+
+ // handler for snippet to text area
+ snippetToTextArea: function(e) {
+ if (this.isParam) {
+ if (e) { e.preventDefault(); }
+
+ var textArea = $('textarea', $(this.el.parentNode.parentNode.parentNode));
+ if ($.trim(textArea.val()) === '') {
+ textArea.val(this.model.sampleJSON);
+ }
+ }
+ }
+});
+'use strict';
+
+SwaggerUi.Views.StatusCodeView = Backbone.View.extend({
+ initialize: function (opts) {
+ this.options = opts || {};
+ this.router = this.options.router;
+ },
+
+ render: function(){
+ $(this.el).html(Handlebars.templates.status_code(this.model));
+
+ if (this.router.api.models.hasOwnProperty(this.model.responseModel)) {
+ var responseModel = {
+ sampleJSON: JSON.stringify(this.router.api.models[this.model.responseModel].createJSONSample(), null, 2),
+ isParam: false,
+ signature: this.router.api.models[this.model.responseModel].getMockSignature(),
+ };
+
+ var responseModelView = new SwaggerUi.Views.SignatureView({model: responseModel, tagName: 'div'});
+ $('.model-signature', this.$el).append(responseModelView.render().el);
+ } else {
+ $('.model-signature', this.$el).html('');
+ }
+ return this;
+ }
+});}).call(this);
diff --git a/catalog-be/src/main/resources/swagger/swagger-ui.min.js b/catalog-be/src/main/resources/swagger/swagger-ui.min.js
new file mode 100644
index 0000000000..0e21b8852c
--- /dev/null
+++ b/catalog-be/src/main/resources/swagger/swagger-ui.min.js
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+(function(){function e(){e.history=e.history||[],e.history.push(arguments),this.console&&console.log(Array.prototype.slice.call(arguments)[0])}this.Handlebars=this.Handlebars||{},this.Handlebars.templates=this.Handlebars.templates||{},this.Handlebars.templates.apikey_button_view=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return"<!--div class='auth_button' id='apikey_button'><img class='auth_icon' alt='apply api key' src='images/apikey.jpeg'></div-->\n<div class='auth_container' id='apikey_container'>\n <div class='key_input_container'>\n <div class='auth_label'>"+s((i=null!=(i=t.keyName||(null!=e?e.keyName:e))?i:a,typeof i===o?i.call(e,{name:"keyName",hash:{},data:r}):i))+'</div>\n <input placeholder="api_key" class="auth_input" id="input_apiKey_entry" name="apiKey" type="text"/>\n <div class=\'auth_submit\'><a class=\'auth_submit_button\' id="apply_api_key" href="#">apply</a></div>\n </div>\n</div>\n\n'},useData:!0}),this.Handlebars.templates.basic_auth_button_view=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(){return'<div class=\'auth_button\' id=\'basic_auth_button\'><img class=\'auth_icon\' src=\'images/password.jpeg\'></div>\n<div class=\'auth_container\' id=\'basic_auth_container\'>\n <div class=\'key_input_container\'>\n <div class="auth_label">Username</div>\n <input placeholder="username" class="auth_input" id="input_username" name="username" type="text"/>\n <div class="auth_label">Password</div>\n <input placeholder="password" class="auth_input" id="input_password" name="password" type="password"/>\n <div class=\'auth_submit\'><a class=\'auth_submit_button\' id="apply_basic_auth" href="#">apply</a></div>\n </div>\n</div>\n\n'},useData:!0}),this.Handlebars.templates.content_type=Handlebars.template({1:function(e,t,n,r){var i,o="";return i=t.each.call(e,null!=e?e.produces:e,{name:"each",hash:{},fn:this.program(2,r),inverse:this.noop,data:r}),null!=i&&(o+=i),o},2:function(e){var t,n=this.lambda,r=' <option value="';return t=n(e,e),null!=t&&(r+=t),r+='">',t=n(e,e),null!=t&&(r+=t),r+"</option>\n"},4:function(){return' <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o='<label for="contentType"></label>\n<select name="contentType">\n';return i=t["if"].call(e,null!=e?e.produces:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(4,r),data:r}),null!=i&&(o+=i),o+"</select>\n"},useData:!0}),$(function(){$.fn.vAlign=function(){return this.each(function(){var e=$(this).height(),t=$(this).parent().height(),n=(t-e)/2;$(this).css("margin-top",n)})},$.fn.stretchFormtasticInputWidthToParent=function(){return this.each(function(){var e=$(this).closest("form").innerWidth(),t=parseInt($(this).closest("form").css("padding-left"),10)+parseInt($(this).closest("form").css("padding-right"),10),n=parseInt($(this).css("padding-left"),10)+parseInt($(this).css("padding-right"),10);$(this).css("width",e-t-n)})},$("form.formtastic li.string input, form.formtastic textarea").stretchFormtasticInputWidthToParent(),$("ul.downplayed li div.content p").vAlign(),$("form.sandbox").submit(function(){var e=!0;return $(this).find("input.required").each(function(){$(this).removeClass("error"),""===$(this).val()&&($(this).addClass("error"),$(this).wiggle(),e=!1)}),e})}),Function.prototype.bind&&console&&"object"==typeof console.log&&["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(e){console[e]=this.bind(console[e],console)},Function.prototype.call),window.Docs={shebang:function(){var e=$.param.fragment().split("/");switch(e.shift(),e.length){case 1:var t="resource_"+e[0];Docs.expandEndpointListForResource(e[0]),$("#"+t).slideto({highlight:!1});break;case 2:Docs.expandEndpointListForResource(e[0]),$("#"+t).slideto({highlight:!1});var n=e.join("_"),r=n+"_content";Docs.expandOperation($("#"+r)),$("#"+n).slideto({highlight:!1})}},toggleEndpointListForResource:function(e){var t=$("li#resource_"+Docs.escapeResourceName(e)+" ul.endpoints");t.is(":visible")?Docs.collapseEndpointListForResource(e):Docs.expandEndpointListForResource(e)},expandEndpointListForResource:function(e){var e=Docs.escapeResourceName(e);if(""==e)return void $(".resource ul.endpoints").slideDown();$("li#resource_"+e).addClass("active");var t=$("li#resource_"+e+" ul.endpoints");t.slideDown()},collapseEndpointListForResource:function(e){var e=Docs.escapeResourceName(e);if(""==e)return void $(".resource ul.endpoints").slideUp();$("li#resource_"+e).removeClass("active");var t=$("li#resource_"+e+" ul.endpoints");t.slideUp()},expandOperationsForResource:function(e){return Docs.expandEndpointListForResource(e),""==e?void $(".resource ul.endpoints li.operation div.content").slideDown():void $("li#resource_"+Docs.escapeResourceName(e)+" li.operation div.content").each(function(){Docs.expandOperation($(this))})},collapseOperationsForResource:function(e){return Docs.expandEndpointListForResource(e),""==e?void $(".resource ul.endpoints li.operation div.content").slideUp():void $("li#resource_"+Docs.escapeResourceName(e)+" li.operation div.content").each(function(){Docs.collapseOperation($(this))})},escapeResourceName:function(e){return e.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]\^`{|}~]/g,"\\$&")},expandOperation:function(e){e.slideDown()},collapseOperation:function(e){e.slideUp()}},Handlebars.registerHelper("sanitize",function(e){return e=e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,""),new Handlebars.SafeString(e)}),this.Handlebars.templates.main=Handlebars.template({1:function(e,t,n,r){var i,o=this.lambda,a=this.escapeExpression,s=' <div class="info_title">'+a(o(null!=(i=null!=e?e.info:e)?i.title:i,e))+'</div>\n <div class="info_description markdown">';return i=o(null!=(i=null!=e?e.info:e)?i.description:i,e),null!=i&&(s+=i),s+="</div>\n",i=t["if"].call(e,null!=e?e.externalDocs:e,{name:"if",hash:{},fn:this.program(2,r),inverse:this.noop,data:r}),null!=i&&(s+=i),s+=" ",i=t["if"].call(e,null!=(i=null!=e?e.info:e)?i.termsOfServiceUrl:i,{name:"if",hash:{},fn:this.program(4,r),inverse:this.noop,data:r}),null!=i&&(s+=i),s+="\n ",i=t["if"].call(e,null!=(i=null!=(i=null!=e?e.info:e)?i.contact:i)?i.name:i,{name:"if",hash:{},fn:this.program(6,r),inverse:this.noop,data:r}),null!=i&&(s+=i),s+="\n ",i=t["if"].call(e,null!=(i=null!=(i=null!=e?e.info:e)?i.contact:i)?i.url:i,{name:"if",hash:{},fn:this.program(8,r),inverse:this.noop,data:r}),null!=i&&(s+=i),s+="\n ",i=t["if"].call(e,null!=(i=null!=(i=null!=e?e.info:e)?i.contact:i)?i.email:i,{name:"if",hash:{},fn:this.program(10,r),inverse:this.noop,data:r}),null!=i&&(s+=i),s+="\n ",i=t["if"].call(e,null!=(i=null!=e?e.info:e)?i.license:i,{name:"if",hash:{},fn:this.program(12,r),inverse:this.noop,data:r}),null!=i&&(s+=i),s+"\n"},2:function(e){var t,n=this.lambda,r=this.escapeExpression;return" <h5>More documentations</h5>\n <p>"+r(n(null!=(t=null!=e?e.externalDocs:e)?t.description:t,e))+'</p>\n <a href="'+r(n(null!=(t=null!=e?e.externalDocs:e)?t.url:t,e))+'" target="_blank">'+r(n(null!=(t=null!=e?e.externalDocs:e)?t.url:t,e))+"</a>\n"},4:function(e){var t,n=this.lambda,r=this.escapeExpression;return'<div class="info_tos"><a href="'+r(n(null!=(t=null!=e?e.info:e)?t.termsOfServiceUrl:t,e))+'">Terms of service</a></div>'},6:function(e){var t,n=this.lambda,r=this.escapeExpression;return"<div class='info_name'>Created by "+r(n(null!=(t=null!=(t=null!=e?e.info:e)?t.contact:t)?t.name:t,e))+"</div>"},8:function(e){var t,n=this.lambda,r=this.escapeExpression;return"<div class='info_url'>See more at <a href=\""+r(n(null!=(t=null!=(t=null!=e?e.info:e)?t.contact:t)?t.url:t,e))+'">'+r(n(null!=(t=null!=(t=null!=e?e.info:e)?t.contact:t)?t.url:t,e))+"</a></div>"},10:function(e){var t,n=this.lambda,r=this.escapeExpression;return"<div class='info_email'><a href=\"mailto:"+r(n(null!=(t=null!=(t=null!=e?e.info:e)?t.contact:t)?t.email:t,e))+"?subject="+r(n(null!=(t=null!=e?e.info:e)?t.title:t,e))+'">Contact the developer</a></div>'},12:function(e){var t,n=this.lambda,r=this.escapeExpression;return"<div class='info_license'><a href='"+r(n(null!=(t=null!=(t=null!=e?e.info:e)?t.license:t)?t.url:t,e))+"'>"+r(n(null!=(t=null!=(t=null!=e?e.info:e)?t.license:t)?t.name:t,e))+"</a></div>"},14:function(e){var t,n=this.lambda,r=this.escapeExpression;return' , <span style="font-variant: small-caps">api version</span>: '+r(n(null!=(t=null!=e?e.info:e)?t.version:t,e))+"\n "},16:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return' <span style="float:right"><a href="'+s((i=null!=(i=t.validatorUrl||(null!=e?e.validatorUrl:e))?i:a,typeof i===o?i.call(e,{name:"validatorUrl",hash:{},data:r}):i))+"/debug?url="+s((i=null!=(i=t.url||(null!=e?e.url:e))?i:a,typeof i===o?i.call(e,{name:"url",hash:{},data:r}):i))+'"><img id="validator" src="'+s((i=null!=(i=t.validatorUrl||(null!=e?e.validatorUrl:e))?i:a,typeof i===o?i.call(e,{name:"validatorUrl",hash:{},data:r}):i))+"?url="+s((i=null!=(i=t.url||(null!=e?e.url:e))?i:a,typeof i===o?i.call(e,{name:"url",hash:{},data:r}):i))+'"></a>\n </span>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="<div class='info' id='api_info'>\n";return i=t["if"].call(e,null!=e?e.info:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+="</div>\n<div class='container' id='resources_container'>\n <ul id='resources'></ul>\n\n <div class=\"footer\">\n <br>\n <br>\n <h4 style=\"color: #999\">[ <span style=\"font-variant: small-caps\">base url</span>: "+l((o=null!=(o=t.basePath||(null!=e?e.basePath:e))?o:s,typeof o===a?o.call(e,{name:"basePath",hash:{},data:r}):o))+"\n",i=t["if"].call(e,null!=(i=null!=e?e.info:e)?i.version:i,{name:"if",hash:{},fn:this.program(14,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+="]\n",i=t["if"].call(e,null!=e?e.validatorUrl:e,{name:"if",hash:{},fn:this.program(16,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+" </h4>\n </div>\n</div>\n"},useData:!0}),this.Handlebars.templates.operation=Handlebars.template({1:function(){return"deprecated"},3:function(){return" <h4>Warning: Deprecated</h4>\n"},5:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=' <h4>Implementation Notes</h4>\n <div class="markdown">';return o=null!=(o=t.description||(null!=e?e.description:e))?o:s,i=typeof o===a?o.call(e,{name:"description",hash:{},data:r}):o,null!=i&&(l+=i),l+"</div>\n"},7:function(){return' <div class="auth">\n <span class="api-ic ic-error"></span>'},9:function(e,t,n,r){var i,o=' <div id="api_information_panel" style="top: 526px; left: 776px; display: none;">\n';return i=t.each.call(e,e,{name:"each",hash:{},fn:this.program(10,r),inverse:this.noop,data:r}),null!=i&&(o+=i),o+" </div>\n"},10:function(e){var t,n=this.lambda,r=this.escapeExpression,i=" <div title='";return t=n(null!=e?e.description:e,e),null!=t&&(i+=t),i+"'>"+r(n(null!=e?e.scope:e,e))+"</div>\n"},12:function(){return"</div>"},14:function(){return' <div class=\'access\'>\n <span class="api-ic ic-off" title="click to authenticate"></span>\n </div>\n'},16:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <h4>Response Class (Status "+s((i=null!=(i=t.successCode||(null!=e?e.successCode:e))?i:a,typeof i===o?i.call(e,{name:"successCode",hash:{},data:r}):i))+')</h4>\n <p><span class="model-signature" /></p>\n <br/>\n <div class="response-content-type" />\n'},18:function(){return' <h4>Parameters</h4>\n <table class=\'fullwidth\'>\n <thead>\n <tr>\n <th style="width: 100px; max-width: 100px">Parameter</th>\n <th style="width: 310px; max-width: 310px">Value</th>\n <th style="width: 200px; max-width: 200px">Description</th>\n <th style="width: 100px; max-width: 100px">Parameter Type</th>\n <th style="width: 220px; max-width: 230px">Data Type</th>\n </tr>\n </thead>\n <tbody class="operation-params">\n\n </tbody>\n </table>\n'},20:function(){return" <div style='margin:0;padding:0;display:inline'></div>\n <h4>Response Messages</h4>\n <table class='fullwidth'>\n <thead>\n <tr>\n <th>HTTP Status Code</th>\n <th>Reason</th>\n <th>Response Model</th>\n <th>Headers</th>\n </tr>\n </thead>\n <tbody class=\"operation-status\">\n\n </tbody>\n </table>\n"},22:function(){return""},24:function(){return" <div class='sandbox_header'>\n <input class='submit' name='commit' type='button' value='Try it out!' />\n <a href='#' class='response_hider' style='display:none'>Hide Response</a>\n <span class='response_throbber' style='display:none'></span>\n </div>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a,s="function",l=t.helperMissing,u=this.escapeExpression,c=t.blockHelperMissing,p="\n <ul class='operations' >\n <li class='"+u((o=null!=(o=t.method||(null!=e?e.method:e))?o:l,typeof o===s?o.call(e,{name:"method",hash:{},data:r}):o))+" operation' id='"+u((o=null!=(o=t.parentId||(null!=e?e.parentId:e))?o:l,typeof o===s?o.call(e,{name:"parentId",hash:{},data:r}):o))+"_"+u((o=null!=(o=t.nickname||(null!=e?e.nickname:e))?o:l,typeof o===s?o.call(e,{name:"nickname",hash:{},data:r}):o))+"'>\n <div class='heading'>\n <h3>\n <span class='http_method'>\n <a href='#!/"+u((o=null!=(o=t.encodedParentId||(null!=e?e.encodedParentId:e))?o:l,typeof o===s?o.call(e,{name:"encodedParentId",hash:{},data:r}):o))+"/"+u((o=null!=(o=t.nickname||(null!=e?e.nickname:e))?o:l,typeof o===s?o.call(e,{name:"nickname",hash:{},data:r}):o))+'\' class="toggleOperation">'+u((o=null!=(o=t.method||(null!=e?e.method:e))?o:l,typeof o===s?o.call(e,{name:"method",hash:{},data:r}):o))+"</a>\n </span>\n <span class='path'>\n <a href='#!/"+u((o=null!=(o=t.encodedParentId||(null!=e?e.encodedParentId:e))?o:l,typeof o===s?o.call(e,{name:"encodedParentId",hash:{},data:r}):o))+"/"+u((o=null!=(o=t.nickname||(null!=e?e.nickname:e))?o:l,typeof o===s?o.call(e,{name:"nickname",hash:{},data:r}):o))+"' class=\"toggleOperation ";return i=t["if"].call(e,null!=e?e.deprecated:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.noop,data:r}),null!=i&&(p+=i),p+='">'+u((o=null!=(o=t.path||(null!=e?e.path:e))?o:l,typeof o===s?o.call(e,{name:"path",hash:{},data:r}):o))+"</a>\n </span>\n </h3>\n <ul class='options'>\n <li>\n <a href='#!/"+u((o=null!=(o=t.encodedParentId||(null!=e?e.encodedParentId:e))?o:l,typeof o===s?o.call(e,{name:"encodedParentId",hash:{},data:r}):o))+"/"+u((o=null!=(o=t.nickname||(null!=e?e.nickname:e))?o:l,typeof o===s?o.call(e,{name:"nickname",hash:{},data:r}):o))+'\' class="toggleOperation">',o=null!=(o=t.summary||(null!=e?e.summary:e))?o:l,i=typeof o===s?o.call(e,{name:"summary",hash:{},data:r}):o,null!=i&&(p+=i),p+="</a>\n </li>\n </ul>\n </div>\n <div class='content' id='"+u((o=null!=(o=t.parentId||(null!=e?e.parentId:e))?o:l,typeof o===s?o.call(e,{name:"parentId",hash:{},data:r}):o))+"_"+u((o=null!=(o=t.nickname||(null!=e?e.nickname:e))?o:l,typeof o===s?o.call(e,{name:"nickname",hash:{},data:r}):o))+"_content' style='display:none'>\n",i=t["if"].call(e,null!=e?e.deprecated:e,{name:"if",hash:{},fn:this.program(3,r),inverse:this.noop,data:r}),null!=i&&(p+=i),i=t["if"].call(e,null!=e?e.description:e,{name:"if",hash:{},fn:this.program(5,r),inverse:this.noop,data:r}),null!=i&&(p+=i),o=null!=(o=t.oauth||(null!=e?e.oauth:e))?o:l,a={name:"oauth",hash:{},fn:this.program(7,r),inverse:this.noop,data:r},i=typeof o===s?o.call(e,a):o,t.oauth||(i=c.call(e,i,a)),null!=i&&(p+=i),p+="\n",i=t.each.call(e,null!=e?e.oauth:e,{name:"each",hash:{},fn:this.program(9,r),inverse:this.noop,data:r}),null!=i&&(p+=i),p+=" ",o=null!=(o=t.oauth||(null!=e?e.oauth:e))?o:l,a={name:"oauth",hash:{},fn:this.program(12,r),inverse:this.noop,data:r},i=typeof o===s?o.call(e,a):o,t.oauth||(i=c.call(e,i,a)),null!=i&&(p+=i),p+="\n",o=null!=(o=t.oauth||(null!=e?e.oauth:e))?o:l,a={name:"oauth",hash:{},fn:this.program(14,r),inverse:this.noop,data:r},i=typeof o===s?o.call(e,a):o,t.oauth||(i=c.call(e,i,a)),null!=i&&(p+=i),i=t["if"].call(e,null!=e?e.type:e,{name:"if",hash:{},fn:this.program(16,r),inverse:this.noop,data:r}),null!=i&&(p+=i),p+=" <form accept-charset='UTF-8' class='sandbox'>\n <div style='margin:0;padding:0;display:inline'></div>\n",i=t["if"].call(e,null!=e?e.parameters:e,{name:"if",hash:{},fn:this.program(18,r),inverse:this.noop,data:r}),null!=i&&(p+=i),i=t["if"].call(e,null!=e?e.responseMessages:e,{name:"if",hash:{},fn:this.program(20,r),inverse:this.noop,data:r}),null!=i&&(p+=i),i=t["if"].call(e,null!=e?e.isReadOnly:e,{name:"if",hash:{},fn:this.program(22,r),inverse:this.program(24,r),data:r}),null!=i&&(p+=i),p+" </form>\n <div class='response' style='display:none'>\n <h4>Request URL</h4>\n <div class='block request_url'></div>\n <h4>Response Body</h4>\n <div class='block response_body'></div>\n <h4>Response Code</h4>\n <div class='block response_code'></div>\n <h4>Response Headers</h4>\n <div class='block response_headers'></div>\n </div>\n </div>\n </li>\n </ul>\n"},useData:!0}),this.Handlebars.templates.param_list=Handlebars.template({1:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return"<td class='code required'>"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"</td>\n"},3:function(){return" multiple='multiple'"},5:function(){return""},7:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,r),inverse:this.program(8,r),data:r}),null!=i&&(o+=i),o},8:function(e,t,n,r){var i,o=t.helperMissing,a="";return i=(t.isArray||e&&e.isArray||o).call(e,e,{name:"isArray",hash:{},fn:this.program(5,r),inverse:this.program(9,r),data:r}),null!=i&&(a+=i),a},9:function(){return" <option selected=\"\" value=''></option>\n"},11:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e.isDefault:e,{name:"if",hash:{},fn:this.program(12,r),inverse:this.program(14,r),data:r}),null!=i&&(o+=i),o},12:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return' <option selected="" value=\''+s((i=null!=(i=t.value||(null!=e?e.value:e))?i:a,typeof i===o?i.call(e,{name:"value",hash:{},data:r}):i))+"'>"+s((i=null!=(i=t.value||(null!=e?e.value:e))?i:a,typeof i===o?i.call(e,{name:"value",hash:{},data:r}):i))+" (default)</option>\n"},14:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <option value='"+s((i=null!=(i=t.value||(null!=e?e.value:e))?i:a,typeof i===o?i.call(e,{name:"value",hash:{},data:r}):i))+"'>"+s((i=null!=(i=t.value||(null!=e?e.value:e))?i:a,typeof i===o?i.call(e,{name:"value",hash:{},data:r}):i))+"</option>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="";return i=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+="<td class='code'>"+l((o=null!=(o=t.name||(null!=e?e.name:e))?o:s,typeof o===a?o.call(e,{name:"name",hash:{},data:r}):o))+"</td>\n<td>\n <select ",i=(t.isArray||e&&e.isArray||s).call(e,e,{name:"isArray",hash:{},fn:this.program(3,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+=" class='parameter' name='"+l((o=null!=(o=t.name||(null!=e?e.name:e))?o:s,typeof o===a?o.call(e,{name:"name",hash:{},data:r}):o))+"'>\n",i=t["if"].call(e,null!=e?e.required:e,{name:"if",hash:{},fn:this.program(5,r),inverse:this.program(7,r),data:r}),null!=i&&(u+=i),i=t.each.call(e,null!=(i=null!=e?e.allowableValues:e)?i.descriptiveValues:i,{name:"each",hash:{},fn:this.program(11,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+=' </select>\n</td>\n<td class="markdown">',o=null!=(o=t.description||(null!=e?e.description:e))?o:s,i=typeof o===a?o.call(e,{name:"description",hash:{},data:r}):o,null!=i&&(u+=i),u+="</td>\n<td>",o=null!=(o=t.paramType||(null!=e?e.paramType:e))?o:s,i=typeof o===a?o.call(e,{name:"paramType",hash:{},data:r}):o,null!=i&&(u+=i),u+'</td>\n<td><span class="model-signature"></span></td>'},useData:!0}),this.Handlebars.templates.param_readonly_required=Handlebars.template({1:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <textarea class='body-textarea' readonly='readonly' placeholder='(required)' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"'>"+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+"</textarea>\n"},3:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(4,r),inverse:this.program(6,r),data:r}),null!=i&&(o+=i),o},4:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" "+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+"\n"},6:function(){return" (empty)\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code required'>"+l((o=null!=(o=t.name||(null!=e?e.name:e))?o:s,typeof o===a?o.call(e,{name:"name",hash:{},data:r}):o))+"</td>\n<td>\n";return i=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(3,r),data:r}),null!=i&&(u+=i),u+='</td>\n<td class="markdown">',o=null!=(o=t.description||(null!=e?e.description:e))?o:s,i=typeof o===a?o.call(e,{name:"description",hash:{},data:r}):o,null!=i&&(u+=i),u+="</td>\n<td>",o=null!=(o=t.paramType||(null!=e?e.paramType:e))?o:s,i=typeof o===a?o.call(e,{name:"paramType",hash:{},data:r}):o,null!=i&&(u+=i),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_readonly=Handlebars.template({1:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <textarea class='body-textarea' readonly='readonly' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"'>"+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+"</textarea>\n"},3:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(4,r),inverse:this.program(6,r),data:r}),null!=i&&(o+=i),o},4:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" "+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+"\n"},6:function(){return" (empty)\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code'>"+l((o=null!=(o=t.name||(null!=e?e.name:e))?o:s,typeof o===a?o.call(e,{name:"name",hash:{},data:r}):o))+"</td>\n<td>\n";return i=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(3,r),data:r}),null!=i&&(u+=i),u+='</td>\n<td class="markdown">',o=null!=(o=t.description||(null!=e?e.description:e))?o:s,i=typeof o===a?o.call(e,{name:"description",hash:{},data:r}):o,null!=i&&(u+=i),u+="</td>\n<td>",o=null!=(o=t.paramType||(null!=e?e.paramType:e))?o:s,i=typeof o===a?o.call(e,{name:"paramType",hash:{},data:r}):o,null!=i&&(u+=i),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param_required=Handlebars.template({1:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,r),inverse:this.program(4,r),data:r}),null!=i&&(o+=i),o},2:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return' <input type="file" name=\''+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"'/>\n"},4:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,r),inverse:this.program(7,r),data:r}),null!=i&&(o+=i),o},5:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <textarea class='body-textarea required' placeholder='(required)' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"'>"+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+'</textarea>\n <br />\n <div class="parameter-content-type" />\n'},7:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <textarea class='body-textarea required' placeholder='(required)' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+'\'></textarea>\n <br />\n <div class="parameter-content-type" />\n'},9:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(10,r),inverse:this.program(12,r),data:r}),null!=i&&(o+=i),o},10:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <input class='parameter' class='required' type='file' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"'/>\n"},12:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(13,r),inverse:this.program(15,r),data:r}),null!=i&&(o+=i),o},13:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <input class='parameter required' minlength='1' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"' placeholder='(required)' type='text' value='"+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+"'/>\n"},15:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <input class='parameter required' minlength='1' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"' placeholder='(required)' type='text' value=''/>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code required'>"+l((o=null!=(o=t.name||(null!=e?e.name:e))?o:s,typeof o===a?o.call(e,{name:"name",hash:{},data:r}):o))+"</td>\n<td>\n";return i=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(9,r),data:r}),null!=i&&(u+=i),u+='</td>\n<td>\n <strong><span class="markdown">',o=null!=(o=t.description||(null!=e?e.description:e))?o:s,i=typeof o===a?o.call(e,{name:"description",hash:{},data:r}):o,null!=i&&(u+=i),u+="</span></strong>\n</td>\n<td>",o=null!=(o=t.paramType||(null!=e?e.paramType:e))?o:s,i=typeof o===a?o.call(e,{name:"paramType",hash:{},data:r}):o,null!=i&&(u+=i),u+'</td>\n<td><span class="model-signature"></span></td>\n'},useData:!0}),this.Handlebars.templates.param=Handlebars.template({1:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,r),inverse:this.program(4,r),data:r}),null!=i&&(o+=i),o},2:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return' <input type="file" name=\''+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+'\'/>\n <div class="parameter-content-type" />\n'},4:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(5,r),inverse:this.program(7,r),data:r}),null!=i&&(o+=i),o},5:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <textarea class='body-textarea' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"'>"+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+'</textarea>\n <br />\n <div class="parameter-content-type" />\n'},7:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <textarea class='body-textarea' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+'\'></textarea>\n <br />\n <div class="parameter-content-type" />\n'},9:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e.isFile:e,{name:"if",hash:{},fn:this.program(2,r),inverse:this.program(10,r),data:r}),null!=i&&(o+=i),o},10:function(e,t,n,r){var i,o="";return i=t["if"].call(e,null!=e?e["default"]:e,{name:"if",hash:{},fn:this.program(11,r),inverse:this.program(13,r),data:r}),null!=i&&(o+=i),o},11:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <input class='parameter' minlength='0' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"' placeholder='' type='text' value='"+s((i=null!=(i=t["default"]||(null!=e?e["default"]:e))?i:a,typeof i===o?i.call(e,{name:"default",hash:{},data:r}):i))+"'/>\n"},13:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <input class='parameter' minlength='0' name='"+s((i=null!=(i=t.name||(null!=e?e.name:e))?i:a,typeof i===o?i.call(e,{name:"name",hash:{},data:r}):i))+"' placeholder='' type='text' value=''/>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="<td class='code'>"+l((o=null!=(o=t.name||(null!=e?e.name:e))?o:s,typeof o===a?o.call(e,{name:"name",hash:{},data:r}):o))+"</td>\n<td>\n\n";return i=t["if"].call(e,null!=e?e.isBody:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(9,r),data:r}),null!=i&&(u+=i),u+='\n</td>\n<td class="markdown">',o=null!=(o=t.description||(null!=e?e.description:e))?o:s,i=typeof o===a?o.call(e,{name:"description",hash:{},data:r}):o,null!=i&&(u+=i),u+="</td>\n<td>",o=null!=(o=t.paramType||(null!=e?e.paramType:e))?o:s,i=typeof o===a?o.call(e,{name:"paramType",hash:{},data:r}):o,null!=i&&(u+=i),u+'</td>\n<td>\n <span class="model-signature"></span>\n</td>\n'},useData:!0}),this.Handlebars.templates.parameter_content_type=Handlebars.template({1:function(e,t,n,r){var i,o="";return i=t.each.call(e,null!=e?e.consumes:e,{name:"each",hash:{},fn:this.program(2,r),inverse:this.noop,data:r}),null!=i&&(o+=i),o},2:function(e){var t,n=this.lambda,r=' <option value="';return t=n(e,e),null!=t&&(r+=t),r+='">',t=n(e,e),null!=t&&(r+=t),r+"</option>\n"},4:function(){return' <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o='<label for="parameterContentType"></label>\n<select name="parameterContentType">\n';return i=t["if"].call(e,null!=e?e.consumes:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(4,r),data:r}),null!=i&&(o+=i),o+"</select>\n"},useData:!0}),this.Handlebars.templates.resource=Handlebars.template({1:function(){return" : "},3:function(e,t,n,r){var i,o="function",a=t.helperMissing,s=this.escapeExpression;return" <li>\n <a href='"+s((i=null!=(i=t.url||(null!=e?e.url:e))?i:a,typeof i===o?i.call(e,{name:"url",hash:{},data:r}):i))+"'>Raw</a>\n </li>\n"
+},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a,s="function",l=t.helperMissing,u=this.escapeExpression,c=t.blockHelperMissing,p="<div class='heading'>\n <h2>\n <a href='#!/"+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+'\' class="toggleEndpointList" data-id="'+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+'">'+u((o=null!=(o=t.name||(null!=e?e.name:e))?o:l,typeof o===s?o.call(e,{name:"name",hash:{},data:r}):o))+"</a> ";return o=null!=(o=t.summary||(null!=e?e.summary:e))?o:l,a={name:"summary",hash:{},fn:this.program(1,r),inverse:this.noop,data:r},i=typeof o===s?o.call(e,a):o,t.summary||(i=c.call(e,i,a)),null!=i&&(p+=i),o=null!=(o=t.summary||(null!=e?e.summary:e))?o:l,i=typeof o===s?o.call(e,{name:"summary",hash:{},data:r}):o,null!=i&&(p+=i),p+="\n </h2>\n <ul class='options'>\n <li>\n <a href='#!/"+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+"' id='endpointListTogger_"+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+'\' class="toggleEndpointList" data-id="'+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+'">Show/Hide</a>\n </li>\n <li>\n <a href=\'#\' class="collapseResource" data-id="'+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+'">\n List Operations\n </a>\n </li>\n <li>\n <a href=\'#\' class="expandResource" data-id="'+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+'">\n Expand Operations\n </a>\n </li>\n',i=t["if"].call(e,null!=e?e.url:e,{name:"if",hash:{},fn:this.program(3,r),inverse:this.noop,data:r}),null!=i&&(p+=i),p+" </ul>\n</div>\n<ul class='endpoints' id='"+u((o=null!=(o=t.id||(null!=e?e.id:e))?o:l,typeof o===s?o.call(e,{name:"id",hash:{},data:r}):o))+"_endpoint_list' style='display:none'>\n\n</ul>\n"},useData:!0}),this.Handlebars.templates.response_content_type=Handlebars.template({1:function(e,t,n,r){var i,o="";return i=t.each.call(e,null!=e?e.produces:e,{name:"each",hash:{},fn:this.program(2,r),inverse:this.noop,data:r}),null!=i&&(o+=i),o},2:function(e){var t,n=this.lambda,r=' <option value="';return t=n(e,e),null!=t&&(r+=t),r+='">',t=n(e,e),null!=t&&(r+=t),r+"</option>\n"},4:function(){return' <option value="application/json">application/json</option>\n'},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o='<label for="responseContentType"></label>\n<select name="responseContentType">\n';return i=t["if"].call(e,null!=e?e.produces:e,{name:"if",hash:{},fn:this.program(1,r),inverse:this.program(4,r),data:r}),null!=i&&(o+=i),o+"</select>\n"},useData:!0}),this.Handlebars.templates.signature=Handlebars.template({compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u='<div>\n<ul class="signature-nav">\n <li><a class="description-link" href="#">Model</a></li>\n <li><a class="snippet-link" href="#">Model Schema</a></li>\n</ul>\n<div>\n\n<div class="signature-container">\n <div class="description">\n ';return o=null!=(o=t.signature||(null!=e?e.signature:e))?o:s,i=typeof o===a?o.call(e,{name:"signature",hash:{},data:r}):o,null!=i&&(u+=i),u+'\n </div>\n\n <div class="snippet">\n <pre><code>'+l((o=null!=(o=t.sampleJSON||(null!=e?e.sampleJSON:e))?o:s,typeof o===a?o.call(e,{name:"sampleJSON",hash:{},data:r}):o))+'</code></pre>\n <small class="notice"></small>\n </div>\n</div>\n\n'},useData:!0}),this.Handlebars.templates.status_code=Handlebars.template({1:function(e,t,n,r){var i=this.lambda,o=this.escapeExpression;return" <tr>\n <td>"+o(i(r&&r.key,e))+"</td>\n <td>"+o(i(null!=e?e.description:e,e))+"</td>\n <td>"+o(i(null!=e?e.type:e,e))+"</td>\n </tr>\n"},compiler:[6,">= 2.0.0-beta.1"],main:function(e,t,n,r){var i,o,a="function",s=t.helperMissing,l=this.escapeExpression,u="<td width='15%' class='code'>"+l((o=null!=(o=t.code||(null!=e?e.code:e))?o:s,typeof o===a?o.call(e,{name:"code",hash:{},data:r}):o))+"</td>\n<td>";return o=null!=(o=t.message||(null!=e?e.message:e))?o:s,i=typeof o===a?o.call(e,{name:"message",hash:{},data:r}):o,null!=i&&(u+=i),u+='</td>\n<td width=\'50%\'><span class="model-signature" /></td>\n<td class="headers">\n <table>\n <tbody>\n',i=t.each.call(e,null!=e?e.headers:e,{name:"each",hash:{},fn:this.program(1,r),inverse:this.noop,data:r}),null!=i&&(u+=i),u+" </tbody>\n </table>\n</td>"},useData:!0}),function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.SwaggerClient=e()}}(function(){var e;return function t(e,n,r){function i(a,s){if(!n[a]){if(!e[a]){var l="function"==typeof require&&require;if(!s&&l)return l(a,!0);if(o)return o(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;a<r.length;a++)i(r[a]);return i}({1:[function(e,t){"use strict";var n=e("./lib/auth"),r=e("./lib/helpers"),i=e("./lib/client"),o=function(e,t){return r.log('This is deprecated, use "new SwaggerClient" instead.'),new i(e,t)};Array.prototype.indexOf||(Array.prototype.indexOf=function(e,t){for(var n=t||0,r=this.length;r>n;n++)if(this[n]===e)return n;return-1}),String.prototype.endsWith||(String.prototype.endsWith=function(e){return-1!==this.indexOf(e,this.length-e.length)}),t.exports=i,i.ApiKeyAuthorization=n.ApiKeyAuthorization,i.PasswordAuthorization=n.PasswordAuthorization,i.CookieAuthorization=n.CookieAuthorization,i.SwaggerApi=o,i.SwaggerClient=o},{"./lib/auth":2,"./lib/client":3,"./lib/helpers":4}],2:[function(e,t){"use strict";var n=e("btoa"),r=e("cookiejar"),i=t.exports.SwaggerAuthorizations=function(){this.authz={}};i.prototype.add=function(e,t){return this.authz[e]=t,t},i.prototype.remove=function(e){return delete this.authz[e]},i.prototype.apply=function(e,t){var n,r,i,o,a=null;if("undefined"==typeof t||0===Object.keys(t).length)for(n in this.authz)i=this.authz[n],o=i.apply(e,t),o===!0&&(a=!0);else if(Array.isArray(t))for(var s=0;s<t.length;s++){var l=t[s];for(r in l)for(n in this.authz)n===r&&(i=this.authz[n],o=i.apply(e,t),o===!0&&(a=!0))}else for(r in t)for(n in this.authz)n===r&&(i=this.authz[n],o=i.apply(e,t),o===!0&&(a=!0));return a};var o=t.exports.ApiKeyAuthorization=function(e,t,n){this.name=e,this.value=t,this.type=n};o.prototype.apply=function(e){return"query"===this.type?(e.url=e.url.indexOf("?")>0?e.url+"&"+this.name+"="+this.value:e.url+"?"+this.name+"="+this.value,!0):"header"===this.type?(e.headers[this.name]=this.value,!0):void 0};var a=t.exports.CookieAuthorization=function(e){this.cookie=e};a.prototype.apply=function(e){return e.cookieJar=e.cookieJar||new r,e.cookieJar.setCookie(this.cookie),!0};var s=t.exports.PasswordAuthorization=function(e,t,n){this.name=e,this.username=t,this.password=n};s.prototype.apply=function(e){return e.headers.Authorization="Basic "+n(this.username+":"+this.password),!0}},{btoa:16,cookiejar:17}],3:[function(e,t){"use strict";var n={bind:e("lodash-compat/function/bind"),cloneDeep:e("lodash-compat/lang/cloneDeep"),find:e("lodash-compat/collection/find"),forEach:e("lodash-compat/collection/forEach"),indexOf:e("lodash-compat/array/indexOf"),isArray:e("lodash-compat/lang/isArray"),isFunction:e("lodash-compat/lang/isFunction"),isPlainObject:e("lodash-compat/lang/isPlainObject"),isUndefined:e("lodash-compat/lang/isUndefined")},r=e("./auth"),i=e("./helpers"),o=e("./types/model"),a=e("./types/operation"),s=e("./types/operationGroup"),l=e("./resolver"),u=e("./http"),c=e("./spec-converter"),p=["authorizationScheme","authorizations","basePath","build","buildFrom1_1Spec","buildFrom1_2Spec","buildFromSpec","clientAuthorizations","convertInfo","debug","defaultErrorCallback","defaultSuccessCallback","fail","failure","finish","help","idFromOp","info","initialize","isBuilt","isValid","models","modelsArray","options","parseUri","progress","resourceCount","sampleModels","selfReflect","setConsolidatedModels","spec","supportedSubmitMethods","swaggerRequestHeaders","tagFromLabel","url","useJQuery"],h=["apis","asCurl","description","externalDocs","help","label","name","operation","operations","operationsArray","path","tag"],f=["delete","get","head","options","patch","post","put"],d=t.exports=function(e,t){return this.authorizationScheme=null,this.authorizations=null,this.basePath=null,this.debug=!1,this.info=null,this.isBuilt=!1,this.isValid=!1,this.modelsArray=[],this.resourceCount=0,this.url=null,this.useJQuery=!1,"undefined"!=typeof e?this.initialize(e,t):this};d.prototype.initialize=function(e,t){this.models={},this.sampleModels={},t=t||{},"string"==typeof e?this.url=e:"object"==typeof e&&(t=e,this.url=t.url),this.swaggerRequestHeaders=t.swaggerRequestHeaders||"application/json;charset=utf-8,*/*",this.defaultSuccessCallback=t.defaultSuccessCallback||null,this.defaultErrorCallback=t.defaultErrorCallback||null,"function"==typeof t.success&&(this.success=t.success),t.useJQuery&&(this.useJQuery=t.useJQuery),this.clientAuthorizations=t.authorizations?t.authorizations:new r.SwaggerAuthorizations,this.supportedSubmitMethods=t.supportedSubmitMethods||[],this.failure=t.failure||function(){},this.progress=t.progress||function(){},this.spec=n.cloneDeep(t.spec),this.options=t,"function"==typeof t.success&&(this.ready=!0,this.build())},d.prototype.build=function(e){if(this.isBuilt)return this;var t=this;this.progress("fetching resource list: "+this.url);var n={useJQuery:this.useJQuery,url:this.url,method:"get",headers:{accept:this.swaggerRequestHeaders},on:{error:function(e){return t.fail("http"!==t.url.substring(0,4)?"Please specify the protocol for "+t.url:0===e.status?"Can't read from server. It may not have the appropriate access-control-origin settings.":404===e.status?"Can't read swagger JSON from "+t.url:e.status+" : "+e.statusText+" "+t.url)},response:function(e){var n=e.obj||JSON.parse(e.data);if(t.swaggerVersion=n.swaggerVersion,n.swagger&&2===parseInt(n.swagger))t.swaggerVersion=n.swagger,(new l).resolve(n,t.buildFromSpec,t),t.isValid=!0;else{var r=new c;r.setDocumentationLocation(t.url),r.convert(n,function(e){(new l).resolve(e,t.buildFromSpec,t),t.isValid=!0})}}}};if(this.spec)setTimeout(function(){(new l).resolve(t.spec,t.buildFromSpec,t)},10);else{if(this.clientAuthorizations.apply(n),e)return n;(new u).execute(n)}return this},d.prototype.buildFromSpec=function(e){if(this.isBuilt)return this;this.apis={},this.apisArray=[],this.basePath=e.basePath||"",this.consumes=e.consumes,this.host=e.host||"",this.info=e.info||{},this.produces=e.produces,this.schemes=e.schemes||[],this.securityDefinitions=e.securityDefinitions,this.title=e.title||"",e.externalDocs&&(this.externalDocs=e.externalDocs),this.authSchemes=e.securityDefinitions;var t,r={};if(Array.isArray(e.tags))for(r={},t=0;t<e.tags.length;t++){var l=e.tags[t];r[l.name]=l}var u;"string"==typeof this.url&&(u=this.parseUri(this.url)),this.scheme="undefined"==typeof this.schemes||0===this.schemes.length?u.scheme||"http":this.schemes[0],("undefined"==typeof this.host||""===this.host)&&(this.host=u.host,u.port&&(this.host=this.host+":"+u.port)),this.definitions=e.definitions;var c;for(c in this.definitions){var d=new o(c,this.definitions[c],this.models);d&&(this.models[c]=d)}var m=this;return m.apis.help=n.bind(m.help,m),n.forEach(e.paths,function(e,t){n.isPlainObject(e)&&n.forEach(f,function(o){var l=e[o];if(!n.isUndefined(l)){if(!n.isPlainObject(l))return void i.log("The '"+o+"' operation for '"+t+"' path is not an Operation Object");var u=l.tags;(n.isUndefined(u)||!n.isArray(u)||0===u.length)&&(u=l.tags=["default"]);var c=m.idFromOp(t,o,l),f=new a(m,l.scheme,c,o,t,l,m.definitions,m.models,m.clientAuthorizations);n.forEach(u,function(e){var t=n.indexOf(p,e)>-1?"_"+e:e,o=n.indexOf(h,e)>-1?"_"+e:e,a=m[t];if(t!==e&&i.log("The '"+e+"' tag conflicts with a SwaggerClient function/property name. Use 'client."+t+"' or 'client.apis."+e+"' instead of 'client."+e+"'."),o!==e&&i.log("The '"+e+"' tag conflicts with a SwaggerClient operation function/property name. Use 'client.apis."+o+"' instead of 'client.apis."+e+"'."),n.indexOf(h,c)>-1&&(i.log("The '"+c+"' operationId conflicts with a SwaggerClient operation function/property name. Use 'client.apis."+o+"._"+c+"' instead of 'client.apis."+o+"."+c+"'."),c="_"+c,f.nickname=c),n.isUndefined(a)){a=m[t]=m.apis[o]={},a.operations={},a.label=o,a.apis={};var l=r[e];n.isUndefined(l)||(a.description=l.description,a.externalDocs=l.externalDocs),m[t].help=n.bind(m.help,a),m.apisArray.push(new s(e,a.description,a.externalDocs,f))}n.isFunction(a.help)||(a.help=n.bind(m.help,a)),m.apis[o][c]=a[c]=n.bind(f.execute,f),m.apis[o][c].help=a[c].help=n.bind(f.help,f),m.apis[o][c].asCurl=a[c].asCurl=n.bind(f.asCurl,f),a.apis[c]=a.operations[c]=f;var u=n.find(m.apisArray,function(t){return t.tag===e});u&&u.operationsArray.push(f)})}})}),this.isBuilt=!0,this.success&&(this.isValid=!0,this.isBuilt=!0,this.success()),this},d.prototype.parseUri=function(e){var t=/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,n=t.exec(e);return{scheme:n[4].replace(":",""),host:n[11],port:n[12],path:n[15]}},d.prototype.help=function(e){var t="";return this instanceof d?n.forEach(this.apis,function(e,r){n.isPlainObject(e)&&(t+="operations for the '"+r+"' tag\n",n.forEach(e.operations,function(e,n){t+=" * "+n+": "+e.summary+"\n"}))}):(this instanceof s||n.isPlainObject(this))&&(t+="operations for the '"+this.label+"' tag\n",n.forEach(this.apis,function(e,n){t+=" * "+n+": "+e.summary+"\n"})),e?t:(i.log(t),t)},d.prototype.tagFromLabel=function(e){return e},d.prototype.idFromOp=function(e,t,n){n&&n.operationId||(n=n||{},n.operationId=t+"_"+e);var r=n.operationId.replace(/[\s!@#$%^&*()_+=\[{\]};:<>|.\/?,\\'""-]/g,"_")||e.substring(1)+"_"+t;return r=r.replace(/((_){2,})/g,"_"),r=r.replace(/^(_)*/g,""),r=r.replace(/([_])*$/g,"")},d.prototype.fail=function(e){throw this.failure(e),e}},{"./auth":2,"./helpers":4,"./http":5,"./resolver":6,"./spec-converter":7,"./types/model":8,"./types/operation":9,"./types/operationGroup":10,"lodash-compat/array/indexOf":19,"lodash-compat/collection/find":21,"lodash-compat/collection/forEach":22,"lodash-compat/function/bind":25,"lodash-compat/lang/cloneDeep":94,"lodash-compat/lang/isArray":96,"lodash-compat/lang/isFunction":97,"lodash-compat/lang/isPlainObject":100,"lodash-compat/lang/isUndefined":103}],4:[function(e,t){(function(e){"use strict";t.exports.__bind=function(e,t){return function(){return e.apply(t,arguments)}};var n=t.exports.log=function(){n.history=n.history||[],n.history.push(arguments),console&&"test"!==e.env.NODE_ENV&&console.log(Array.prototype.slice.call(arguments)[0])};t.exports.fail=function(e){n(e)},t.exports.optionHtml=function(e,t){return'<tr><td class="optionName">'+e+":</td><td>"+t+"</td></tr>"},t.exports.typeFromJsonSchema=function(e,t){var n;return"integer"===e&&"int32"===t?n="integer":"integer"===e&&"int64"===t?n="long":"integer"===e&&"undefined"==typeof t?n="long":"string"===e&&"date-time"===t?n="date-time":"string"===e&&"date"===t?n="date":"number"===e&&"float"===t?n="float":"number"===e&&"double"===t?n="double":"number"===e&&"undefined"==typeof t?n="double":"boolean"===e?n="boolean":"string"===e&&(n="string"),n};var r=t.exports.simpleRef=function(e){return"undefined"==typeof e?null:0===e.indexOf("#/definitions/")?e.substring("#/definitions/".length):e},i=t.exports.getStringSignature=function(e,t){var n="";return"undefined"!=typeof e.$ref?n+=r(e.$ref):"undefined"==typeof e.type?n+="object":"array"===e.type?t?n+=i(e.items||e.$ref||{}):(n+="Array[",n+=i(e.items||e.$ref||{}),n+="]"):n+="integer"===e.type&&"int32"===e.format?"integer":"integer"===e.type&&"int64"===e.format?"long":"integer"===e.type&&"undefined"==typeof e.format?"long":"string"===e.type&&"date-time"===e.format?"date-time":"string"===e.type&&"date"===e.format?"date":"string"===e.type&&"undefined"==typeof e.format?"string":"number"===e.type&&"float"===e.format?"float":"number"===e.type&&"double"===e.format?"double":"number"===e.type&&"undefined"==typeof e.format?"double":"boolean"===e.type?"boolean":e.$ref?r(e.$ref):e.type,n}}).call(this,e("_process"))},{_process:15}],5:[function(e,t){"use strict";var n=e("./helpers"),r=e("jquery"),i=e("superagent"),o=function(){},a=function(){},s=t.exports=function(){};s.prototype.execute=function(e,t){return this.useJQuery=e&&"boolean"==typeof e.useJQuery?e.useJQuery:this.isIE8(),e&&"object"==typeof e.body&&(e.body.type&&"formData"===e.body.type?(e.contentType=!1,e.processData=!1,delete e.headers["Content-Type"]):e.body=JSON.stringify(e.body)),this.useJQuery?new o(t).execute(e):new a(t).execute(e)},s.prototype.isIE8=function(){var e=!1;if("undefined"!=typeof navigator&&navigator.userAgent){var t=navigator.userAgent.toLowerCase();if(-1!==t.indexOf("msie")){var n=parseInt(t.split("msie")[1]);8>=n&&(e=!0)}}return e},o.prototype.execute=function(e){var t=e.on,i=e;return e.type=e.method,e.cache=!1,delete e.useJQuery,e.data=e.body,delete e.body,e.complete=function(e){for(var r={},o=e.getAllResponseHeaders().split("\n"),a=0;a<o.length;a++){var s=o[a].trim();if(0!==s.length){var l=s.indexOf(":");if(-1!==l){var u=s.substring(0,l).trim(),c=s.substring(l+1).trim();r[u]=c}else r[s]=null}}var p={url:i.url,method:i.method,status:e.status,statusText:e.statusText,data:e.responseText,headers:r},h=r["content-type"]||r["Content-Type"]||null;if(h&&(0===h.indexOf("application/json")||h.indexOf("+json")>0))try{p.obj=e.responseJSON||JSON.parse(p.data)||{}}catch(f){n.log("unable to parse JSON content")}if(e.status>=200&&e.status<300)t.response(p);else{if(!(0===e.status||e.status>=400&&e.status<599))return t.response(p);t.error(p)}},r.support.cors=!0,r.ajax(e)},a.prototype.execute=function(e){var t=e.method.toLowerCase();"delete"===t&&(t="del");var n,r=e.headers||{},o=i[t](e.url);for(n in r)o.set(n,r[n]);e.body&&o.send(e.body),o.end(function(t,n){n=n||{status:0,headers:{error:"no response from server"}};var r,i={url:e.url,method:e.method,headers:n.headers};!t&&n.error&&(t=n.error),t&&e.on&&e.on.error?(i.obj=t,i.status=n?n.status:500,i.statusText=n?n.text:t.message,r=e.on.error):n&&e.on&&e.on.response&&(i.obj="undefined"!=typeof n.body?n.body:n.text,i.status=n.status,i.statusText=n.text,r=e.on.response),i.data=i.statusText,r&&r(i)})}},{"./helpers":4,jquery:18,superagent:111}],6:[function(e,t){"use strict";var n=e("./http"),r=t.exports=function(){};r.prototype.resolve=function(e,t,r){this.scope=r||this;var i,o,a,s,l,u=0,c={},p={},h={};for(o in e.definitions){var f=e.definitions[o];for(l in f.properties)s=f.properties[l],this.resolveTo(s,h)}for(o in e.paths){var d,m,g;a=e.paths[o];for(d in a)if("$ref"===d)this.resolveInline(e,a,h,p);else{m=a[d];var y,v=m.parameters;for(y in v){var b=v[y];"body"===b["in"]&&b.schema&&this.resolveTo(b.schema,h),b.$ref&&this.resolveInline(e,b,h,p)}for(g in m.responses){var w=m.responses[g];"object"==typeof w&&w.$ref&&this.resolveInline(e,w,h,p),w.schema&&this.resolveTo(w.schema,h)}}}var x={},A=0;for(o in h){var E=o.split("#");2===E.length?(i=E[0],a=E[1],Array.isArray(x[i])||(x[i]=[],A+=1),x[i].push(a)):(Array.isArray(x[o])||(x[o]=[],A+=1),x[o].push(null))}for(o in x){var j=this,C=x[o];i=o;var T={useJQuery:!1,url:i,method:"get",headers:{accept:this.scope.swaggerRequestHeaders||"application/json"},on:{error:function(){u+=1;var n;for(n=0;n<C.length;n++){var r=i+"#"+C[n];p[r]=null}u===A&&j.finish(e,h,c,p,t)},response:function(n){var r,a,s=n.obj;if(null===s||0===Object.keys(s).length)try{s=JSON.parse(n.data)}catch(l){s={}}for(u+=1,r=0;r<C.length;r++){var f=C[r];if(null==f)c[o]={name:o,obj:s};else{var d=s,m=f.split("/");for(a=0;a<m.length;a++){var g=m[a];if(-1!==g.indexOf("~1")&&(g=m[a].replace(/~0/g,"~").replace(/~1/g,"/"),"/"!==g.charAt(0)&&(g="/"+g)),"undefined"==typeof d)break;g.length>0&&(d=d[g])}var y=i+"#"+f,v=m[a-1];"undefined"!=typeof d?c[y]={name:v,obj:d}:p[y]=null}}u===A&&j.finish(e,h,c,p,t)}}};r&&r.clientAuthorizations&&r.clientAuthorizations.apply(T),(new n).execute(T)}0===Object.keys(x).length&&t.call(this.scope,e,p)},r.prototype.finish=function(e,t,n,r,i){var o;for(o in t){var a,s=t[o];for(a=0;a<s.length;a++){var l=n[s[a].obj.$ref];if(l)if(e.definitions||(e.definitions={}),"$ref"===s[a].resolveAs)e.definitions[l.name]=l.obj,s[a].obj.$ref="#/definitions/"+l.name;else if("inline"===s[a].resolveAs){var u,c=s[a].obj;delete c.$ref;for(u in l.obj)c[u]=l.obj[u]}}}i.call(this.scope,e,r)},r.prototype.resolveInline=function(e,t,n,r){var i=t.$ref;if(i){if(0===i.indexOf("http"))Array.isArray(n[i])?n[i].push({obj:t,resolveAs:"inline"}):n[i]=[{obj:t,resolveAs:"inline"}];else if(0===i.indexOf("#")){var o,a=i.substring(1),s=a.split("/"),l=e;for(o=0;o<s.length;o++){var u=s[o];u.length>0&&(l=l[u])}if(l){delete t.$ref;var c;for(c in l)t[c]=l[c]}else r[i]=null}}else"array"===t.type&&this.resolveTo(t.items,n)},r.prototype.resolveTo=function(e,t){var n=e.$ref;if(n)0===n.indexOf("http")&&(Array.isArray(t[n])?t[n].push({obj:e,resolveAs:"$ref"}):t[n]=[{obj:e,resolveAs:"$ref"}]);else if("array"===e.type){var r=e.items;this.resolveTo(r,t)}}},{"./http":5}],7:[function(e,t){"use strict";var n=(e("./client"),e("./http")),r=t.exports=function(){this.errors=[],this.warnings=[],this.modelMap={}};r.prototype.setDocumentationLocation=function(e){this.docLocation=e},r.prototype.convert=function(e,t){if(!e||!Array.isArray(e.apis))return this.finish(t,null);var n={swagger:"2.0"};n.originalVersion=e.swaggerVersion,this.apiInfo(e,n),this.securityDefinitions(e,n);var r,i=!1;for(r=0;r<e.apis.length;r++){var o=e.apis[r];Array.isArray(o.operations)&&(i=!0)}i?(this.declaration(e,n),this.finish(t,n)):this.resourceListing(e,n,t)},r.prototype.declaration=function(e,t){var n,r;if(e.apis){{e.basePath}if(0===e.basePath.indexOf("http://")){var i=e.basePath.substring("http://".length),o=i.indexOf("/");o>0?(t.host=i.substring(0,o),t.basePath=i.substring(o)):(t.host=i,t.basePath="/")}var a;if(e.authorizations&&(a=e.authorizations),e.consumes&&(t.consumes=e.consumes),e.produces&&(t.produces=e.produces),"object"==typeof e)for(n in e.models){var s=e.models[n],l=s.id||n;this.modelMap[l]=n}for(r=0;r<e.apis.length;r++){var u=e.apis[r],c=u.path,p=u.operations;this.operations(c,e.resourcePath,p,a,t)}var h=e.models;this.models(h,t)}},r.prototype.models=function(e,t){if("object"==typeof e){var n;t.definitions=t.definitions||{};for(n in e){var r,i=e[n],o=[],a={properties:{}};for(r in i.properties){var s=i.properties[r],l={};this.dataType(s,l),s.description&&(l.description=s.description),s["enum"]&&(l["enum"]=s["enum"]),"boolean"==typeof s.required&&s.required===!0&&o.push(r),"string"==typeof s.required&&"true"===s.required&&o.push(r),a.properties[r]=l}o.length>0&&(a["enum"]=o),t.definitions[n]=a}}},r.prototype.extractTag=function(e){var t=e||"default";return(0===t.indexOf("http:")||0===t.indexOf("https:"))&&(t=t.split(["/"]),t=t[t.length-1].substring()),t.replace("/","")},r.prototype.operations=function(e,t,n,r,i){if(Array.isArray(n)){var o;i.paths||(i.paths={});var a=i.paths[e]||{},s=this.extractTag(t);i.tags=i.tags||[];var l=!1;for(o=0;o<i.tags.length;o++){var u=i.tags[o];u.name===s&&(l=!0)}for(l||i.tags.push({name:s}),o=0;o<n.length;o++){var c=n[o],p=(c.method||c.httpMethod).toLowerCase(),h={tags:[s]},f=c.authorizations;if(f&&0===Object.keys(f).length&&(f=r),"undefined"!=typeof f)for(var d in f){h.security=h.security||[];var m=f[d];if(m){var g=[];for(var y in m)g.push(m[y].scope);var v={};v[d]=g,h.security.push(v)}else{var v={};v[d]=[],h.security.push(v)}}c.consumes?h.consumes=c.consumes:i.consumes&&(h.consumes=i.consumes),c.produces?h.produces=c.produces:i.produces&&(h.produces=i.produces),c.summary&&(h.summary=c.summary),c.notes&&(h.description=c.notes),c.nickname&&(h.operationId=c.nickname),c.deprecated&&(h.deprecated=c.deprecated),this.authorizations(f,i),this.parameters(h,c.parameters,i),this.responseMessages(h,c,i),a[p]=h}i.paths[e]=a}},r.prototype.responseMessages=function(e,t){if("object"==typeof t){var n={};this.dataType(t,n),n.schema||(n={schema:n}),e.responses=e.responses||{};var r=!1;if(Array.isArray(t.responseMessages)){var i,o=t.responseMessages;for(i=0;i<o.length;i++){var a=o[i],s={description:a.message};200===a.code&&(r=!0),e.responses[""+a.code]=s}}r?e.responses["default"]=n:e.responses[200]=n}},r.prototype.authorizations=function(e){},r.prototype.parameters=function(e,t){if(Array.isArray(t)){var n;for(n=0;n<t.length;n++){var r=t[n],i={};if(i.name=r.name,i.description=r.description,i.required=r.required,i["in"]=r.paramType,"body"===i["in"]&&(i.name="body"),"form"===i["in"]&&(i["in"]="formData"),r.allowMultiple===!0||"true"===r.allowMultiple){var o={};if(this.dataType(r,o),i.type="array",i.items=o,r.allowableValues){var a=r.allowableValues;"LIST"===a.valueType&&(i["enum"]=a.values)}}else this.dataType(r,i);e.parameters=e.parameters||[],e.parameters.push(i)}}},r.prototype.dataType=function(e,t){if("object"==typeof e){e.minimum&&(t.minimum=e.minimum),e.maximum&&(t.maximum=e.maximum),e.defaultValue&&(t["default"]=e.defaultValue);var n=this.toJsonSchema(e);n&&(t=t||{},n.type&&(t.type=n.type),n.format&&(t.format=n.format),n.$ref&&(t.schema={$ref:n.$ref}),n.items&&(t.items=n.items))}},r.prototype.toJsonSchema=function(e){if(!e)return"object";var t=e.type||e.dataType||e.responseClass||"",n=t.toLowerCase(),r=(e.format||"").toLowerCase();if(0===n.indexOf("list[")){var i=t.substring(5,t.length-1),o=this.toJsonSchema({type:i});return{type:"array",items:o}}if("int"===n||"integer"===n&&"int32"===r)return{type:"integer",format:"int32"};if("long"===n||"integer"===n&&"int64"===r)return{type:"integer",format:"int64"};if("integer"===n)return{type:"integer",format:"int64"};if("float"===n||"number"===n&&"float"===r)return{type:"number",format:"float"};if("double"===n||"number"===n&&"double"===r)return{type:"number",format:"double"};if("string"===n&&"date-time"===r||"date"===n)return{type:"string",format:"date-time"};if("string"===n)return{type:"string"};if("file"===n)return{type:"file"};if("boolean"===n)return{type:"boolean"};if("array"===n||"list"===n){if(e.items){var a=this.toJsonSchema(e.items);return{type:"array",items:a}}return{type:"array",items:{type:"object"}}}return e.$ref?{$ref:"#/definitions/"+this.modelMap[e.$ref]||e.$ref}:{$ref:"#/definitions/"+this.modelMap[e.type]||e.type}},r.prototype.resourceListing=function(e,t,r){var i,o=0,a=this,s=e.apis.length,l=t;for(0===s&&this.finish(r,t),i=0;s>i;i++){var u=e.apis[i],c=u.path,p=this.getAbsolutePath(e.swaggerVersion,this.docLocation,c);u.description&&(t.tags=t.tags||[],t.tags.push({name:this.extractTag(u.path),description:u.description||""}));var h={url:p,headers:{accept:"application/json"},on:{},method:"get"};h.on.response=function(e){o+=1,e.obj&&a.declaration(e.obj,l),o===s&&a.finish(r,l)},h.on.error=function(e){console.error(e),o+=1,o===s&&a.finish(r,l)},(new n).execute(h)}},r.prototype.getAbsolutePath=function(e,t,n){if("1.0"===e&&t.endsWith(".json")){var r=t.lastIndexOf("/");r>0&&(t=t.substring(0,r))}var i=t;return 0===n.indexOf("http://")||0===n.indexOf("https://")?i=n:(t.endsWith("/")&&(i=t.substring(0,t.length-1)),i+=n),i=i.replace("{format}","json")},r.prototype.securityDefinitions=function(e,t){if(e.authorizations){var n;for(n in e.authorizations){var r=!1,i={},o=e.authorizations[n];if("apiKey"===o.type)i.type="apiKey",i["in"]=o.passAs,i.name=o.keyname||n,r=!0;else if("oauth2"===o.type){var a,s=o.scopes||[],l={};for(a in s){var u=s[a];l[u.scope]=u.description}if(i.type="oauth2",a>0&&(i.scopes=l),o.grantTypes){if(o.grantTypes.implicit){var c=o.grantTypes.implicit;i.flow="implicit",i.authorizationUrl=c.loginEndpoint,r=!0}if(o.grantTypes.authorization_code&&!i.flow){var p=o.grantTypes.authorization_code;i.flow="accessCode",i.authorizationUrl=p.tokenRequestEndpoint.url,i.tokenUrl=p.tokenEndpoint.url,r=!0}}}r&&(t.securityDefinitions=t.securityDefinitions||{},t.securityDefinitions[n]=i)}}},r.prototype.apiInfo=function(e,t){if(e.info){var n=e.info;t.info={},n.contact&&(t.info.contact={},t.info.contact.email=n.contact),n.description&&(t.info.description=n.description),n.title&&(t.info.title=n.title),n.termsOfServiceUrl&&(t.info.termsOfService=n.termsOfServiceUrl),(n.license||n.licenseUrl)&&(t.license={},n.license&&(t.license.name=n.license),n.licenseUrl&&(t.license.url=n.licenseUrl))}else this.warnings.push("missing info section")},r.prototype.finish=function(e,t){e(t)}},{"./client":3,"./http":5}],8:[function(e,t){"use strict";var n={forEach:e("lodash-compat/collection/forEach"),indexOf:e("lodash-compat/array/indexOf"),isArray:e("lodash-compat/lang/isArray"),isPlainObject:e("lodash-compat/lang/isPlainObject"),isString:e("lodash-compat/lang/isString"),isUndefined:e("lodash-compat/lang/isUndefined"),keys:e("lodash-compat/object/keys"),map:e("lodash-compat/collection/map")},r=e("../helpers"),i=t.exports=function(e,t,n){return this.definition=t||{},this.isArray="array"===t.type,this.models=n||{},this.name=e||"Inline Model",this},o=function(e,t,o){for(var a='<span class="strong">',s="</span>",l={},u=[],c=0,p=function(e,t,a){var s,u=t;return e.$ref?(u=r.simpleRef(e.$ref),s=o[u]):n.isUndefined(t)&&(u="Inline Model "+ ++c,s=new i(u,e,o)),a!==!0&&(l[u]=n.isUndefined(s)?{}:s.definition),u},h=function(e){var t='<span class="propType">',i=e.type||"object";return e.$ref?t+=p(e,r.simpleRef(e.$ref)):"object"===i?t+=n.isUndefined(e.properties)?"object":p(e):"array"===i?(t+="Array[",n.isArray(e.items)?t+=n.map(e.items,p).join(","):n.isPlainObject(e.items)?t+=n.isUndefined(e.items.$ref)?n.isUndefined(e.items.type)||-1!==n.indexOf(["array","object"],e.items.type)?p(e.items):e.items.type:p(e.items,r.simpleRef(e.items.$ref)):(r.log("Array type's 'items' schema is not an array or an object, cannot process"),t+="object"),t+="]"):t+=e.type,t+="</span>"},f=function(e,t){var i="",o=e.type||"object",a="array"===o;switch(a&&(o=n.isPlainObject(e.items)&&!n.isUndefined(e.items.type)?e.items.type:"object"),e["default"]&&(i+=r.optionHtml("Default",e["default"])),o){case"string":e.minLength&&(i+=r.optionHtml("Min. Length",e.minLength)),e.maxLength&&(i+=r.optionHtml("Max. Length",e.maxLength)),e.pattern&&(i+=r.optionHtml("Reg. Exp.",e.pattern));break;case"integer":case"number":e.minimum&&(i+=r.optionHtml("Min. Value",e.minimum)),e.exclusiveMinimum&&(i+=r.optionHtml("Exclusive Min.","true")),e.maximum&&(i+=r.optionHtml("Max. Value",e.maximum)),e.exclusiveMaximum&&(i+=r.optionHtml("Exclusive Max.","true")),e.multipleOf&&(i+=r.optionHtml("Multiple Of",e.multipleOf))}if(a&&(e.minItems&&(i+=r.optionHtml("Min. Items",e.minItems)),e.maxItems&&(i+=r.optionHtml("Max. Items",e.maxItems)),e.uniqueItems&&(i+=r.optionHtml("Unique Items","true")),e.collectionFormat&&(i+=r.optionHtml("Coll. Format",e.collectionFormat))),n.isUndefined(e.items)&&n.isArray(e["enum"])){var s;s="number"===o||"integer"===o?e["enum"].join(", "):'"'+e["enum"].join('", "')+'"',i+=r.optionHtml("Enum",s)}return i.length>0&&(t='<span class="propWrap">'+t+'<table class="optionsWrapper"><tr><th colspan="2">'+o+"</th></tr>"+i+"</table></span>"),t},d=function(e,t){var i=e.type||"object",o="array"===e.type,l=a+t+" "+(o?"[":"{")+s;return t&&u.push(t),o?n.isArray(e.items)?l+="<div>"+n.map(e.items,function(e){var t=e.type||"object";return n.isUndefined(e.$ref)?n.indexOf(["array","object"],t)>-1?"object"===t&&n.isUndefined(e.properties)?"object":p(e):f(e,t):p(e,r.simpleRef(e.$ref))}).join(",</div><div>"):n.isPlainObject(e.items)?l+=n.isUndefined(e.items.$ref)?n.indexOf(["array","object"],e.items.type||"object")>-1?(n.isUndefined(e.items.type)||"object"===e.items.type)&&n.isUndefined(e.items.properties)?"<div>object</div>":"<div>"+p(e.items)+"</div>":"<div>"+f(e.items,e.items.type)+"</div>":"<div>"+p(e.items,r.simpleRef(e.items.$ref))+"</div>":(r.log("Array type's 'items' property is not an array or an object, cannot process"),l+="<div>object</div>"):e.$ref?l+="<div>"+p(e,t)+"</div>":"object"===i?(l+="<div>",n.isPlainObject(e.properties)&&(l+=n.map(e.properties,function(t,r){var i=n.indexOf(e.required||[],r)>-1,o='<span class="propName '+i+'">'+r+"</span> (";
+return o+=h(t),i||(o+=', <span class="propOptKey">optional</span>'),o+=")",n.isUndefined(t.description)||(o+=": "+t.description),t["enum"]&&(o+=' = <span class="propVals">[\''+t["enum"].join("' or '")+"']</span>"),f(t,o)}).join(",</div><div>")),l+="</div>"):l="<div>"+f(e,i)+"</div>",l+a+(o?"]":"}")+s},m=d(t,e);n.keys(l).length>0;)n.forEach(l,function(e,t){var r=n.indexOf(u,t)>-1;delete l[t],r||(u.push(t),m+="<br />"+d(e,t))});return m},a=function(e,t,i){var o,s,l=e.type||"object";return e.example?s=e.example:n.isUndefined(e.items)&&n.isArray(e["enum"])&&(s=e["enum"][0]),n.isUndefined(s)&&(e.$ref?(o=t[r.simpleRef(e.$ref)],n.isUndefined(o)||(n.isUndefined(i[o.name])?(i[o.name]=o,s=a(o.definition,t,i),delete i[o.name]):s="array"===o.type?[]:{})):e["default"]?s=e["default"]:"date-time"===l?s=(new Date).toISOString():"date"===l?s=(new Date).toISOString().split("T")[0]:"string"===l?s="string":"integer"===l?s=0:"long"===l?s=0:"float"===l?s=0:"double"===l?s=0:"boolean"===l?s=!0:"object"===l?(s={},n.forEach(e.properties,function(e,n){s[n]=a(e,t,i)})):"array"===l&&(s=[],n.isArray(e.items)?n.forEach(e.items,function(e){s.push(a(e,t,i))}):n.isPlainObject(e.items)?s.push(a(e.items,t,i)):n.isUndefined(e.items)?s.push({}):r.log("Array type's 'items' property is not an array or an object, cannot process"))),s};i.prototype.createJSONSample=i.prototype.getSampleValue=function(e){return e=e||{},e[this.name]=this,this.examples&&n.isPlainObject(this.examples)&&this.examples["application/json"]?(this.definition.example=this.examples["application/json"],n.isString(this.definition.example)&&(this.definition.example=JSON.parse(this.definition.example))):this.definition.example=this.examples,a(this.definition,this.models,e)},i.prototype.getMockSignature=function(){return o(this.name,this.definition,this.models)}},{"../helpers":4,"lodash-compat/array/indexOf":19,"lodash-compat/collection/forEach":22,"lodash-compat/collection/map":23,"lodash-compat/lang/isArray":96,"lodash-compat/lang/isPlainObject":100,"lodash-compat/lang/isString":101,"lodash-compat/lang/isUndefined":103,"lodash-compat/object/keys":104}],9:[function(e,t){"use strict";var n={isUndefined:e("lodash-compat/lang/isUndefined")},r=e("../helpers"),i=e("./model"),o=e("../http"),a=t.exports=function(e,t,n,r,o,a,s,l,u){var c=[];if(e=e||{},a=a||{},this.authorizations=a.security,this.basePath=e.basePath||"/",this.clientAuthorizations=u,this.consumes=a.consumes,this.deprecated=a.deprecated,this.description=a.description,this.host=e.host||"localhost",this.method=r||c.push("Operation "+n+" is missing method."),this.models=l||{},this.nickname=n||c.push("Operations must have a nickname."),this.operation=a,this.operations={},this.parameters=null!==a?a.parameters||[]:{},this.parent=e,this.path=o||c.push("Operation "+this.nickname+" is missing path."),this.produces=a.produces,this.responses=a.responses||{},this.scheme=t||e.scheme||"http",this.schemes=e.schemes,this.security=a.security,this.summary=a.summary||"",this.type=null,this.useJQuery=e.useJQuery,"string"==typeof this.deprecated)switch(this.deprecated.toLowerCase()){case"true":case"yes":case"1":this.deprecated=!0;break;case"false":case"no":case"0":case null:this.deprecated=!1;break;default:this.deprecated=Boolean(this.deprecated)}var p,h;if(s){var f;for(f in this.definitions)h=new i(f,s[f],this.models),h&&(this.models[f]=h)}for(p=0;p<this.parameters.length;p++){var d=this.parameters[p];"array"===d.type&&(d.isList=!0,d.allowMultiple=!0);var m=this.getType(d);if(m&&"boolean"===m.toString().toLowerCase()&&(d.allowableValues={},d.isList=!0,d["enum"]=["true","false"]),"undefined"!=typeof d["enum"]){var g;for(d.allowableValues={},d.allowableValues.values=[],d.allowableValues.descriptiveValues=[],g=0;g<d["enum"].length;g++){var y=d["enum"][g],v=y===d["default"]?!0:!1;d.allowableValues.values.push(y),d.allowableValues.descriptiveValues.push({value:y,isDefault:v})}}"array"===d.type&&(m=[m],"undefined"==typeof d.allowableValues&&(delete d.isList,delete d.allowMultiple)),d.signature=this.getModelSignature(m,this.models).toString(),d.sampleJSON=this.getModelSampleJSON(m,this.models),d.responseClassSignature=d.signature}var b,w,x=this.responses;if(x[200]?(w=x[200],b="200"):x[201]?(w=x[201],b="201"):x[202]?(w=x[202],b="202"):x[203]?(w=x[203],b="203"):x[204]?(w=x[204],b="204"):x[205]?(w=x[205],b="205"):x[206]?(w=x[206],b="206"):x["default"]&&(w=x["default"],b="default"),w&&w.schema){var A,E=this.resolveModel(w.schema,s);delete x[b],E?(this.successResponse={},A=this.successResponse[b]=E):w.schema.type&&"object"!==w.schema.type&&"array"!==w.schema.type?(this.successResponse={},A=this.successResponse[b]=w.schema):(this.successResponse={},A=this.successResponse[b]=new i(void 0,w.schema||{},this.models)),A&&(w.description&&(A.description=w.description),w.examples&&(A.examples=w.examples),w.headers&&(A.headers=w.headers)),this.type=w}return c.length>0&&this.resource&&this.resource.api&&this.resource.api.fail&&this.resource.api.fail(c),this};a.prototype.getType=function(e){var t,n=e.type,i=e.format,o=!1;"integer"===n&&"int32"===i?t="integer":"integer"===n&&"int64"===i?t="long":"integer"===n?t="integer":"string"===n?t="date-time"===i?"date-time":"date"===i?"date":"string":"number"===n&&"float"===i?t="float":"number"===n&&"double"===i?t="double":"number"===n?t="double":"boolean"===n?t="boolean":"array"===n&&(o=!0,e.items&&(t=this.getType(e.items))),e.$ref&&(t=e.$ref);var a=e.schema;if(a){var s=a.$ref;return s?(s=r.simpleRef(s),o?[s]:s):this.getType(a)}return o?[t]:t},a.prototype.resolveModel=function(e,t){if("undefined"!=typeof e.$ref){var r=e.$ref;if(0===r.indexOf("#/definitions/")&&(r=r.substring("#/definitions/".length)),t[r])return new i(r,t[r],this.models)}else if("object"===e.type||n.isUndefined(e.type))return new i(void 0,e,this.models);return null},a.prototype.help=function(e){for(var t=this.nickname+": "+this.summary+"\n",n=0;n<this.parameters.length;n++){var i=this.parameters[n],o=i.signature;t+="\n * "+i.name+" ("+o+"): "+i.description}return"undefined"==typeof e&&r.log(t),t},a.prototype.getModelSignature=function(e,t){var n,r;return e instanceof Array?(r=!0,e=e[0]):"undefined"==typeof e&&(e="undefined"),n="string"===e?!0:r&&t[r]||t[e]?!1:!0,n?r?"Array["+e+"]":e.toString():r?"Array["+t[e].getMockSignature()+"]":t[e].getMockSignature()},a.prototype.supportHeaderParams=function(){return!0},a.prototype.supportedSubmitMethods=function(){return this.parent.supportedSubmitMethods},a.prototype.getHeaderParams=function(e){for(var t=this.setContentTypes(e,{}),n=0;n<this.parameters.length;n++){var r=this.parameters[n];if("undefined"!=typeof e[r.name]&&"header"===r["in"]){var i=e[r.name];Array.isArray(i)&&(i=i.toString()),t[r.name]=i}}return t},a.prototype.urlify=function(e){for(var t={},n=this.path,r="",i=0;i<this.parameters.length;i++){var o=this.parameters[i];if("undefined"!=typeof e[o.name])if("path"===o["in"]){var a=new RegExp("{"+o.name+"}","gi"),s=e[o.name];s=Array.isArray(s)?this.encodePathCollection(o.collectionFormat,o.name,s):this.encodePathParam(s),n=n.replace(a,s)}else if("query"===o["in"]&&"undefined"!=typeof e[o.name])if(r+=""===r?"?":"&","undefined"!=typeof o.collectionFormat){var l=e[o.name];r+=Array.isArray(l)?this.encodeQueryCollection(o.collectionFormat,o.name,l):this.encodeQueryParam(o.name)+"="+this.encodeQueryParam(e[o.name])}else r+=this.encodeQueryParam(o.name)+"="+this.encodeQueryParam(e[o.name]);else"formData"===o["in"]&&(t[o.name]=e[o.name])}var u=this.scheme+"://"+this.host;return"/"!==this.basePath&&(u+=this.basePath),u+n+r},a.prototype.getMissingParams=function(e){var t,n=[];for(t=0;t<this.parameters.length;t++){var r=this.parameters[t];r.required===!0&&"undefined"==typeof e[r.name]&&(n=r.name)}return n},a.prototype.getBody=function(e,t,n){for(var r,i,o,a={},s=0;s<this.parameters.length;s++){var l=this.parameters[s];"undefined"!=typeof t[l.name]&&("body"===l["in"]?r=t[l.name]:"formData"===l["in"]&&(a[l.name]=t[l.name]))}if("application/x-www-form-urlencoded"===e["Content-Type"]){var u="";for(i in a)o=a[i],"undefined"!=typeof o&&(""!==u&&(u+="&"),u+=encodeURIComponent(i)+"="+encodeURIComponent(o));r=u}else if(e["Content-Type"]&&e["Content-Type"].indexOf("multipart/form-data")>=0&&n.useJQuery){var c=new FormData;c.type="formData";for(i in a)o=t[i],"undefined"!=typeof o&&("file"===o.type&&o.value?(delete e["Content-Type"],c.append(i,o.value)):c.append(i,o));r=c}return r},a.prototype.getModelSampleJSON=function(e,t){var n,r,i;if(r=e instanceof Array,n=t[e]?!1:!0,i=n?void 0:t[e].createJSONSample()){if(i=r?[i]:i,"string"==typeof i)return i;if("object"==typeof i){var o=i;if(i instanceof Array&&i.length>0&&(o=i[0]),o.nodeName){var a=(new XMLSerializer).serializeToString(o);return this.formatXml(a)}return JSON.stringify(i,null,2)}return i}},a.prototype["do"]=function(e,t,n,r,i){return this.execute(e,t,n,r,i)},a.prototype.execute=function(e,t,n,i,a){var s,l,u=e||{},c={};"object"==typeof t&&(c=t,s=n,l=i),"function"==typeof t&&(s=t,l=n),s=s||r.log,l=l||r.log,c.useJQuery&&(this.useJQuery=c.useJQuery);var p=this.getMissingParams(u);if(p.length>0){var h="missing required params: "+p;return void r.fail(h)}var f,d=this.getHeaderParams(u),m=this.setContentTypes(u,c),g={};for(f in d)g[f]=d[f];for(f in m)g[f]=m[f];var y=this.getBody(g,u,c),v=this.urlify(u);if(v.indexOf(".{format}")>0&&g){var b=g.Accept||g.accept;b&&b.indexOf("json")>0?v=v.replace(".{format}",".json"):b&&b.indexOf("xml")>0&&(v=v.replace(".{format}",".xml"))}var w={url:v,method:this.method.toUpperCase(),body:y,useJQuery:this.useJQuery,headers:g,on:{response:function(e){return s(e,a)},error:function(e){return l(e,a)}}};return this.clientAuthorizations.apply(w,this.operation.security),c.mock===!0?w:void(new o).execute(w,c)},a.prototype.setContentTypes=function(e,t){var n,i,o="application/json",a=this.parameters,s=e.parameterContentType||"application/json",l=[],u=[],c={};for(i=0;i<a.length;i++){var p=a[i];if("formData"===p["in"])"file"===p.type?l.push(p):u.push(p);else if("header"===p["in"]&&t){var h=p.name,f=t[p.name];"undefined"!=typeof t[p.name]&&(c[h]=f)}else"body"===p["in"]&&"undefined"!=typeof e[p.name]&&(n=e[p.name])}return!n||"post"!==this.method&&"put"!==this.method&&"patch"!==this.method&&"delete"!==this.method?u.length>0?s=t.requestContentType?t.requestContentType:l.length>0?"multipart/form-data":"application/x-www-form-urlencoded":"DELETE"===this.type?n="{}":"DELETE"!==this.type&&(s=null):t.requestContentType&&(s=t.requestContentType),s&&this.consumes&&-1===this.consumes.indexOf(s)&&r.log("server doesn't consume "+s+", try "+JSON.stringify(this.consumes)),o=t.responseContentType?t.responseContentType:"application/json",o&&this.produces&&-1===this.produces.indexOf(o)&&r.log("server can't produce "+o),(s&&""!==n||"application/x-www-form-urlencoded"===s)&&(c["Content-Type"]=s),o&&(c.Accept=o),c},a.prototype.asCurl=function(e){var t=this.execute(e,{mock:!0});this.clientAuthorizations.apply(t);var n=[];if(n.push("-X "+this.method.toUpperCase()),t.headers){var r;for(r in t.headers)n.push('--header "'+r+": "+t.headers[r]+'"')}if(t.body){var i;i="object"==typeof t.body?JSON.stringify(t.body):t.body,n.push('-d "'+i.replace(/"/g,'\\"')+'"')}return"curl "+n.join(" ")+' "'+t.url+'"'},a.prototype.encodePathCollection=function(e,t,n){var r,i="",o="";for(o="ssv"===e?"%20":"tsv"===e?"\\t":"pipes"===e?"|":",",r=0;r<n.length;r++)0===r?i=this.encodeQueryParam(n[r]):i+=o+this.encodeQueryParam(n[r]);return i},a.prototype.encodeQueryCollection=function(e,t,n){var r,i="";if("default"===e||"multi"===e)for(r=0;r<n.length;r++)r>0&&(i+="&"),i+=this.encodeQueryParam(t)+"="+this.encodeQueryParam(n[r]);else{var o="";if("csv"===e)o=",";else if("ssv"===e)o="%20";else if("tsv"===e)o="\\t";else if("pipes"===e)o="|";else if("brackets"===e)for(r=0;r<n.length;r++)0!==r&&(i+="&"),i+=this.encodeQueryParam(t)+"[]="+this.encodeQueryParam(n[r]);if(""!==o)for(r=0;r<n.length;r++)0===r?i=this.encodeQueryParam(t)+"="+this.encodeQueryParam(n[r]):i+=o+this.encodeQueryParam(n[r])}return i},a.prototype.encodeQueryParam=function(e){return encodeURIComponent(e)},a.prototype.encodePathParam=function(e){var t,n,r,i;if(e=e.toString(),-1===e.indexOf("/"))return encodeURIComponent(e);for(n=e.split("/"),t=[],r=0,i=n.length;i>r;r++)t.push(encodeURIComponent(n[r]));return t.join("/")}},{"../helpers":4,"../http":5,"./model":8,"lodash-compat/lang/isUndefined":103}],10:[function(e,t){"use strict";var n=t.exports=function(e,t,n,r){this.description=t,this.externalDocs=n,this.name=e,this.operation=r,this.operationsArray=[],this.path=e,this.tag=e};n.prototype.sort=function(){}},{}],11:[function(e,t,n){function r(e,t){var n=this;if(!(n instanceof r))return new r(e,t);var i,o=typeof e;if("number"===o)i=+e;else if("string"===o)i=r.byteLength(e,t);else{if("object"!==o||null===e)throw new TypeError("must start with number, buffer, array or string");"Buffer"===e.type&&I(e.data)&&(e=e.data),i=+e.length}if(i>R)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+R.toString(16)+" bytes");0>i?i=0:i>>>=0,r.TYPED_ARRAY_SUPPORT?n=r._augment(new Uint8Array(i)):(n.length=i,n._isBuffer=!0);var a;if(r.TYPED_ARRAY_SUPPORT&&"number"==typeof e.byteLength)n._set(e);else if(T(e))if(r.isBuffer(e))for(a=0;i>a;a++)n[a]=e.readUInt8(a);else for(a=0;i>a;a++)n[a]=(e[a]%256+256)%256;else if("string"===o)n.write(e,0,t);else if("number"===o&&!r.TYPED_ARRAY_SUPPORT)for(a=0;i>a;a++)n[a]=0;return i>0&&i<=r.poolSize&&(n.parent=M),n}function i(e,t){if(!(this instanceof i))return new i(e,t);var n=new r(e,t);return delete n.parent,n}function o(e,t,n,r){n=Number(n)||0;var i=e.length-n;r?(r=Number(r),r>i&&(r=i)):r=i;var o=t.length;if(o%2!==0)throw new Error("Invalid hex string");r>o/2&&(r=o/2);for(var a=0;r>a;a++){var s=parseInt(t.substr(2*a,2),16);if(isNaN(s))throw new Error("Invalid hex string");e[n+a]=s}return a}function a(e,t,n,r){var i=D(_(t,e.length-n),e,n,r);return i}function s(e,t,n,r){var i=D(O(t),e,n,r);return i}function l(e,t,n,r){return s(e,t,n,r)}function u(e,t,n,r){var i=D($(t),e,n,r);return i}function c(e,t,n,r){var i=D(k(t,e.length-n),e,n,r);return i}function p(e,t,n){return N.fromByteArray(0===t&&n===e.length?e:e.slice(t,n))}function h(e,t,n){var r="",i="";n=Math.min(e.length,n);for(var o=t;n>o;o++)e[o]<=127?(r+=L(i)+String.fromCharCode(e[o]),i=""):i+="%"+e[o].toString(16);return r+L(i)}function f(e,t,n){var r="";n=Math.min(e.length,n);for(var i=t;n>i;i++)r+=String.fromCharCode(127&e[i]);return r}function d(e,t,n){var r="";n=Math.min(e.length,n);for(var i=t;n>i;i++)r+=String.fromCharCode(e[i]);return r}function m(e,t,n){var r=e.length;(!t||0>t)&&(t=0),(!n||0>n||n>r)&&(n=r);for(var i="",o=t;n>o;o++)i+=S(e[o]);return i}function g(e,t,n){for(var r=e.slice(t,n),i="",o=0;o<r.length;o+=2)i+=String.fromCharCode(r[o]+256*r[o+1]);return i}function y(e,t,n){if(e%1!==0||0>e)throw new RangeError("offset is not uint");if(e+t>n)throw new RangeError("Trying to access beyond buffer length")}function v(e,t,n,i,o,a){if(!r.isBuffer(e))throw new TypeError("buffer must be a Buffer instance");if(t>o||a>t)throw new RangeError("value is out of bounds");if(n+i>e.length)throw new RangeError("index out of range")}function b(e,t,n,r){0>t&&(t=65535+t+1);for(var i=0,o=Math.min(e.length-n,2);o>i;i++)e[n+i]=(t&255<<8*(r?i:1-i))>>>8*(r?i:1-i)}function w(e,t,n,r){0>t&&(t=4294967295+t+1);for(var i=0,o=Math.min(e.length-n,4);o>i;i++)e[n+i]=t>>>8*(r?i:3-i)&255}function x(e,t,n,r,i,o){if(t>i||o>t)throw new RangeError("value is out of bounds");if(n+r>e.length)throw new RangeError("index out of range");if(0>n)throw new RangeError("index out of range")}function A(e,t,n,r,i){return i||x(e,t,n,4,3.4028234663852886e38,-3.4028234663852886e38),P.write(e,t,n,r,23,4),n+4}function E(e,t,n,r,i){return i||x(e,t,n,8,1.7976931348623157e308,-1.7976931348623157e308),P.write(e,t,n,r,52,8),n+8}function j(e){if(e=C(e).replace(H,""),e.length<2)return"";for(;e.length%4!==0;)e+="=";return e}function C(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function T(e){return I(e)||r.isBuffer(e)||e&&"object"==typeof e&&"number"==typeof e.length}function S(e){return 16>e?"0"+e.toString(16):e.toString(16)}function _(e,t){t=t||1/0;for(var n,r=e.length,i=null,o=[],a=0;r>a;a++){if(n=e.charCodeAt(a),n>55295&&57344>n){if(!i){if(n>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&o.push(239,191,189);continue}i=n;continue}if(56320>n){(t-=3)>-1&&o.push(239,191,189),i=n;continue}n=i-55296<<10|n-56320|65536,i=null}else i&&((t-=3)>-1&&o.push(239,191,189),i=null);if(128>n){if((t-=1)<0)break;o.push(n)}else if(2048>n){if((t-=2)<0)break;o.push(n>>6|192,63&n|128)}else if(65536>n){if((t-=3)<0)break;o.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(2097152>n))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return o}function O(e){for(var t=[],n=0;n<e.length;n++)t.push(255&e.charCodeAt(n));return t}function k(e,t){for(var n,r,i,o=[],a=0;a<e.length&&!((t-=2)<0);a++)n=e.charCodeAt(a),r=n>>8,i=n%256,o.push(i),o.push(r);return o}function $(e){return N.toByteArray(j(e))}function D(e,t,n,r){for(var i=0;r>i&&!(i+n>=t.length||i>=e.length);i++)t[i+n]=e[i];return i}function L(e){try{return decodeURIComponent(e)}catch(t){return String.fromCharCode(65533)}}var N=e("base64-js"),P=e("ieee754"),I=e("is-array");n.Buffer=r,n.SlowBuffer=i,n.INSPECT_MAX_BYTES=50,r.poolSize=8192;var R=1073741823,M={};r.TYPED_ARRAY_SUPPORT=function(){try{var e=new ArrayBuffer(0),t=new Uint8Array(e);return t.foo=function(){return 42},42===t.foo()&&"function"==typeof t.subarray&&0===new Uint8Array(1).subarray(1,1).byteLength}catch(n){return!1}}(),r.isBuffer=function(e){return!(null==e||!e._isBuffer)},r.compare=function(e,t){if(!r.isBuffer(e)||!r.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var n=e.length,i=t.length,o=0,a=Math.min(n,i);a>o&&e[o]===t[o];o++);return o!==a&&(n=e[o],i=t[o]),i>n?-1:n>i?1:0},r.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},r.concat=function(e,t){if(!I(e))throw new TypeError("list argument must be an Array of Buffers.");if(0===e.length)return new r(0);if(1===e.length)return e[0];var n;if(void 0===t)for(t=0,n=0;n<e.length;n++)t+=e[n].length;var i=new r(t),o=0;for(n=0;n<e.length;n++){var a=e[n];a.copy(i,o),o+=a.length}return i},r.byteLength=function(e,t){var n;switch(e+="",t||"utf8"){case"ascii":case"binary":case"raw":n=e.length;break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":n=2*e.length;break;case"hex":n=e.length>>>1;break;case"utf8":case"utf-8":n=_(e).length;break;case"base64":n=$(e).length;break;default:n=e.length}return n},r.prototype.length=void 0,r.prototype.parent=void 0,r.prototype.toString=function(e,t,n){var r=!1;if(t>>>=0,n=void 0===n||1/0===n?this.length:n>>>0,e||(e="utf8"),0>t&&(t=0),n>this.length&&(n=this.length),t>=n)return"";for(;;)switch(e){case"hex":return m(this,t,n);case"utf8":case"utf-8":return h(this,t,n);case"ascii":return f(this,t,n);case"binary":return d(this,t,n);case"base64":return p(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return g(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}},r.prototype.equals=function(e){if(!r.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e?!0:0===r.compare(this,e)},r.prototype.inspect=function(){var e="",t=n.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,t).match(/.{2}/g).join(" "),this.length>t&&(e+=" ... ")),"<Buffer "+e+">"},r.prototype.compare=function(e){if(!r.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e?0:r.compare(this,e)},r.prototype.indexOf=function(e,t){function n(e,t,n){for(var r=-1,i=0;n+i<e.length;i++)if(e[n+i]===t[-1===r?0:i-r]){if(-1===r&&(r=i),i-r+1===t.length)return n+r}else r=-1;return-1}if(t>2147483647?t=2147483647:-2147483648>t&&(t=-2147483648),t>>=0,0===this.length)return-1;if(t>=this.length)return-1;if(0>t&&(t=Math.max(this.length+t,0)),"string"==typeof e)return 0===e.length?-1:String.prototype.indexOf.call(this,e,t);if(r.isBuffer(e))return n(this,e,t);if("number"==typeof e)return r.TYPED_ARRAY_SUPPORT&&"function"===Uint8Array.prototype.indexOf?Uint8Array.prototype.indexOf.call(this,e,t):n(this,[e],t);throw new TypeError("val must be string, number or Buffer")},r.prototype.get=function(e){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(e)},r.prototype.set=function(e,t){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(e,t)},r.prototype.write=function(e,t,n,r){if(isFinite(t))isFinite(n)||(r=n,n=void 0);else{var i=r;r=t,t=n,n=i}if(t=Number(t)||0,0>n||0>t||t>this.length)throw new RangeError("attempt to write outside buffer bounds");var p=this.length-t;n?(n=Number(n),n>p&&(n=p)):n=p,r=String(r||"utf8").toLowerCase();var h;switch(r){case"hex":h=o(this,e,t,n);break;case"utf8":case"utf-8":h=a(this,e,t,n);break;case"ascii":h=s(this,e,t,n);break;case"binary":h=l(this,e,t,n);break;case"base64":h=u(this,e,t,n);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":h=c(this,e,t,n);break;default:throw new TypeError("Unknown encoding: "+r)}return h},r.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},r.prototype.slice=function(e,t){var n=this.length;e=~~e,t=void 0===t?n:~~t,0>e?(e+=n,0>e&&(e=0)):e>n&&(e=n),0>t?(t+=n,0>t&&(t=0)):t>n&&(t=n),e>t&&(t=e);var i;if(r.TYPED_ARRAY_SUPPORT)i=r._augment(this.subarray(e,t));else{var o=t-e;i=new r(o,void 0);for(var a=0;o>a;a++)i[a]=this[a+e]}return i.length&&(i.parent=this.parent||this),i},r.prototype.readUIntLE=function(e,t,n){e>>>=0,t>>>=0,n||y(e,t,this.length);for(var r=this[e],i=1,o=0;++o<t&&(i*=256);)r+=this[e+o]*i;return r},r.prototype.readUIntBE=function(e,t,n){e>>>=0,t>>>=0,n||y(e,t,this.length);for(var r=this[e+--t],i=1;t>0&&(i*=256);)r+=this[e+--t]*i;return r},r.prototype.readUInt8=function(e,t){return t||y(e,1,this.length),this[e]},r.prototype.readUInt16LE=function(e,t){return t||y(e,2,this.length),this[e]|this[e+1]<<8},r.prototype.readUInt16BE=function(e,t){return t||y(e,2,this.length),this[e]<<8|this[e+1]},r.prototype.readUInt32LE=function(e,t){return t||y(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},r.prototype.readUInt32BE=function(e,t){return t||y(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},r.prototype.readIntLE=function(e,t,n){e>>>=0,t>>>=0,n||y(e,t,this.length);for(var r=this[e],i=1,o=0;++o<t&&(i*=256);)r+=this[e+o]*i;return i*=128,r>=i&&(r-=Math.pow(2,8*t)),r},r.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||y(e,t,this.length);for(var r=t,i=1,o=this[e+--r];r>0&&(i*=256);)o+=this[e+--r]*i;return i*=128,o>=i&&(o-=Math.pow(2,8*t)),o},r.prototype.readInt8=function(e,t){return t||y(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},r.prototype.readInt16LE=function(e,t){t||y(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},r.prototype.readInt16BE=function(e,t){t||y(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},r.prototype.readInt32LE=function(e,t){return t||y(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},r.prototype.readInt32BE=function(e,t){return t||y(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},r.prototype.readFloatLE=function(e,t){return t||y(e,4,this.length),P.read(this,e,!0,23,4)},r.prototype.readFloatBE=function(e,t){return t||y(e,4,this.length),P.read(this,e,!1,23,4)},r.prototype.readDoubleLE=function(e,t){return t||y(e,8,this.length),P.read(this,e,!0,52,8)},r.prototype.readDoubleBE=function(e,t){return t||y(e,8,this.length),P.read(this,e,!1,52,8)},r.prototype.writeUIntLE=function(e,t,n,r){e=+e,t>>>=0,n>>>=0,r||v(this,e,t,n,Math.pow(2,8*n),0);var i=1,o=0;for(this[t]=255&e;++o<n&&(i*=256);)this[t+o]=e/i>>>0&255;return t+n},r.prototype.writeUIntBE=function(e,t,n,r){e=+e,t>>>=0,n>>>=0,r||v(this,e,t,n,Math.pow(2,8*n),0);var i=n-1,o=1;for(this[t+i]=255&e;--i>=0&&(o*=256);)this[t+i]=e/o>>>0&255;return t+n},r.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,1,255,0),r.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=e,t+1},r.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,2,65535,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8):b(this,e,t,!0),t+2},r.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,2,65535,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=e):b(this,e,t,!1),t+2},r.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,4,4294967295,0),r.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=e):w(this,e,t,!0),t+4},r.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,4,4294967295,0),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e):w(this,e,t,!1),t+4},r.prototype.writeIntLE=function(e,t,n,r){e=+e,t>>>=0,r||v(this,e,t,n,Math.pow(2,8*n-1)-1,-Math.pow(2,8*n-1));var i=0,o=1,a=0>e?1:0;for(this[t]=255&e;++i<n&&(o*=256);)this[t+i]=(e/o>>0)-a&255;return t+n},r.prototype.writeIntBE=function(e,t,n,r){e=+e,t>>>=0,r||v(this,e,t,n,Math.pow(2,8*n-1)-1,-Math.pow(2,8*n-1));var i=n-1,o=1,a=0>e?1:0;for(this[t+i]=255&e;--i>=0&&(o*=256);)this[t+i]=(e/o>>0)-a&255;return t+n},r.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,1,127,-128),r.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),0>e&&(e=255+e+1),this[t]=e,t+1},r.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,2,32767,-32768),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8):b(this,e,t,!0),t+2},r.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,2,32767,-32768),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=e):b(this,e,t,!1),t+2},r.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,4,2147483647,-2147483648),r.TYPED_ARRAY_SUPPORT?(this[t]=e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):w(this,e,t,!0),t+4},r.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||v(this,e,t,4,2147483647,-2147483648),0>e&&(e=4294967295+e+1),r.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=e):w(this,e,t,!1),t+4},r.prototype.writeFloatLE=function(e,t,n){return A(this,e,t,!0,n)},r.prototype.writeFloatBE=function(e,t,n){return A(this,e,t,!1,n)},r.prototype.writeDoubleLE=function(e,t,n){return E(this,e,t,!0,n)},r.prototype.writeDoubleBE=function(e,t,n){return E(this,e,t,!1,n)},r.prototype.copy=function(e,t,n,i){if(n||(n=0),i||0===i||(i=this.length),t>=e.length&&(t=e.length),t||(t=0),i>0&&n>i&&(i=n),i===n)return 0;if(0===e.length||0===this.length)return 0;if(0>t)throw new RangeError("targetStart out of bounds");if(0>n||n>=this.length)throw new RangeError("sourceStart out of bounds");if(0>i)throw new RangeError("sourceEnd out of bounds");i>this.length&&(i=this.length),e.length-t<i-n&&(i=e.length-t+n);var o=i-n;if(1e3>o||!r.TYPED_ARRAY_SUPPORT)for(var a=0;o>a;a++)e[a+t]=this[a+n];else e._set(this.subarray(n,n+o),t);return o},r.prototype.fill=function(e,t,n){if(e||(e=0),t||(t=0),n||(n=this.length),t>n)throw new RangeError("end < start");if(n!==t&&0!==this.length){if(0>t||t>=this.length)throw new RangeError("start out of bounds");if(0>n||n>this.length)throw new RangeError("end out of bounds");var r;if("number"==typeof e)for(r=t;n>r;r++)this[r]=e;else{var i=_(e.toString()),o=i.length;for(r=t;n>r;r++)this[r]=i[r%o]}return this}},r.prototype.toArrayBuffer=function(){if("undefined"!=typeof Uint8Array){if(r.TYPED_ARRAY_SUPPORT)return new r(this).buffer;for(var e=new Uint8Array(this.length),t=0,n=e.length;n>t;t+=1)e[t]=this[t];return e.buffer}throw new TypeError("Buffer.toArrayBuffer not supported in this browser")};var U=r.prototype;r._augment=function(e){return e.constructor=r,e._isBuffer=!0,e._set=e.set,e.get=U.get,e.set=U.set,e.write=U.write,e.toString=U.toString,e.toLocaleString=U.toString,e.toJSON=U.toJSON,e.equals=U.equals,e.compare=U.compare,e.indexOf=U.indexOf,e.copy=U.copy,e.slice=U.slice,e.readUIntLE=U.readUIntLE,e.readUIntBE=U.readUIntBE,e.readUInt8=U.readUInt8,e.readUInt16LE=U.readUInt16LE,e.readUInt16BE=U.readUInt16BE,e.readUInt32LE=U.readUInt32LE,e.readUInt32BE=U.readUInt32BE,e.readIntLE=U.readIntLE,e.readIntBE=U.readIntBE,e.readInt8=U.readInt8,e.readInt16LE=U.readInt16LE,e.readInt16BE=U.readInt16BE,e.readInt32LE=U.readInt32LE,e.readInt32BE=U.readInt32BE,e.readFloatLE=U.readFloatLE,e.readFloatBE=U.readFloatBE,e.readDoubleLE=U.readDoubleLE,e.readDoubleBE=U.readDoubleBE,e.writeUInt8=U.writeUInt8,e.writeUIntLE=U.writeUIntLE,e.writeUIntBE=U.writeUIntBE,e.writeUInt16LE=U.writeUInt16LE,e.writeUInt16BE=U.writeUInt16BE,e.writeUInt32LE=U.writeUInt32LE,e.writeUInt32BE=U.writeUInt32BE,e.writeIntLE=U.writeIntLE,e.writeIntBE=U.writeIntBE,e.writeInt8=U.writeInt8,e.writeInt16LE=U.writeInt16LE,e.writeInt16BE=U.writeInt16BE,e.writeInt32LE=U.writeInt32LE,e.writeInt32BE=U.writeInt32BE,e.writeFloatLE=U.writeFloatLE,e.writeFloatBE=U.writeFloatBE,e.writeDoubleLE=U.writeDoubleLE,e.writeDoubleBE=U.writeDoubleBE,e.fill=U.fill,e.inspect=U.inspect,e.toArrayBuffer=U.toArrayBuffer,e};var H=/[^+\/0-9A-z\-]/g},{"base64-js":12,ieee754:13,"is-array":14}],12:[function(e,t,n){var r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(e){"use strict";function t(e){var t=e.charCodeAt(0);return t===a||t===p?62:t===s||t===h?63:l>t?-1:l+10>t?t-l+26+26:c+26>t?t-c:u+26>t?t-u+26:void 0}function n(e){function n(e){u[p++]=e}var r,i,a,s,l,u;if(e.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var c=e.length;l="="===e.charAt(c-2)?2:"="===e.charAt(c-1)?1:0,u=new o(3*e.length/4-l),a=l>0?e.length-4:e.length;var p=0;for(r=0,i=0;a>r;r+=4,i+=3)s=t(e.charAt(r))<<18|t(e.charAt(r+1))<<12|t(e.charAt(r+2))<<6|t(e.charAt(r+3)),n((16711680&s)>>16),n((65280&s)>>8),n(255&s);return 2===l?(s=t(e.charAt(r))<<2|t(e.charAt(r+1))>>4,n(255&s)):1===l&&(s=t(e.charAt(r))<<10|t(e.charAt(r+1))<<4|t(e.charAt(r+2))>>2,n(s>>8&255),n(255&s)),u}function i(e){function t(e){return r.charAt(e)}function n(e){return t(e>>18&63)+t(e>>12&63)+t(e>>6&63)+t(63&e)}var i,o,a,s=e.length%3,l="";for(i=0,a=e.length-s;a>i;i+=3)o=(e[i]<<16)+(e[i+1]<<8)+e[i+2],l+=n(o);switch(s){case 1:o=e[e.length-1],l+=t(o>>2),l+=t(o<<4&63),l+="==";break;case 2:o=(e[e.length-2]<<8)+e[e.length-1],l+=t(o>>10),l+=t(o>>4&63),l+=t(o<<2&63),l+="="}return l}var o="undefined"!=typeof Uint8Array?Uint8Array:Array,a="+".charCodeAt(0),s="/".charCodeAt(0),l="0".charCodeAt(0),u="a".charCodeAt(0),c="A".charCodeAt(0),p="-".charCodeAt(0),h="_".charCodeAt(0);e.toByteArray=n,e.fromByteArray=i}("undefined"==typeof n?this.base64js={}:n)},{}],13:[function(e,t,n){n.read=function(e,t,n,r,i){var o,a,s=8*i-r-1,l=(1<<s)-1,u=l>>1,c=-7,p=n?i-1:0,h=n?-1:1,f=e[t+p];for(p+=h,o=f&(1<<-c)-1,f>>=-c,c+=s;c>0;o=256*o+e[t+p],p+=h,c-=8);for(a=o&(1<<-c)-1,o>>=-c,c+=r;c>0;a=256*a+e[t+p],p+=h,c-=8);if(0===o)o=1-u;else{if(o===l)return a?0/0:1/0*(f?-1:1);a+=Math.pow(2,r),o-=u}return(f?-1:1)*a*Math.pow(2,o-r)},n.write=function(e,t,n,r,i,o){var a,s,l,u=8*o-i-1,c=(1<<u)-1,p=c>>1,h=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,f=r?0:o-1,d=r?1:-1,m=0>t||0===t&&0>1/t?1:0;for(t=Math.abs(t),isNaN(t)||1/0===t?(s=isNaN(t)?1:0,a=c):(a=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-a))<1&&(a--,l*=2),t+=a+p>=1?h/l:h*Math.pow(2,1-p),t*l>=2&&(a++,l/=2),a+p>=c?(s=0,a=c):a+p>=1?(s=(t*l-1)*Math.pow(2,i),a+=p):(s=t*Math.pow(2,p-1)*Math.pow(2,i),a=0));i>=8;e[n+f]=255&s,f+=d,s/=256,i-=8);for(a=a<<i|s,u+=i;u>0;e[n+f]=255&a,f+=d,a/=256,u-=8);e[n+f-d]|=128*m}},{}],14:[function(e,t){var n=Array.isArray,r=Object.prototype.toString;t.exports=n||function(e){return!!e&&"[object Array]"==r.call(e)}},{}],15:[function(e,t){function n(){if(!a){a=!0;for(var e,t=o.length;t;){e=o,o=[];for(var n=-1;++n<t;)e[n]();t=o.length}a=!1}}function r(){}var i=t.exports={},o=[],a=!1;i.nextTick=function(e){o.push(e),a||setTimeout(n,0)
+},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=r,i.addListener=r,i.once=r,i.off=r,i.removeListener=r,i.removeAllListeners=r,i.emit=r,i.binding=function(){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},{}],16:[function(e,t){(function(e){!function(){"use strict";function n(t){var n;return n=t instanceof e?t:new e(t.toString(),"binary"),n.toString("base64")}t.exports=n}()}).call(this,e("buffer").Buffer)},{buffer:11}],17:[function(e,t,n){!function(){"use strict";function e(t,n,r,i){return this instanceof e?(this.domain=t||void 0,this.path=n||"/",this.secure=!!r,this.script=!!i,this):new e(t,n,r,i)}function t(e,n,r){return e instanceof t?e:this instanceof t?(this.name=null,this.value=null,this.expiration_date=1/0,this.path=String(r||"/"),this.explicit_path=!1,this.domain=n||null,this.explicit_domain=!1,this.secure=!1,this.noscript=!1,e&&this.parse(e,n,r),this):new t(e)}function r(){var e,n,i;return this instanceof r?(e=Object.create(null),this.setCookie=function(r,o,a){var s,l;if(r=new t(r,o,a),s=r.expiration_date<=Date.now(),void 0!==e[r.name]){for(n=e[r.name],l=0;l<n.length;l+=1)if(i=n[l],i.collidesWith(r))return s?(n.splice(l,1),0===n.length&&delete e[r.name],!1):(n[l]=r,r);return s?!1:(n.push(r),r)}return s?!1:(e[r.name]=[r],e[r.name])},this.getCookie=function(t,r){var i,o;if(n=e[t])for(o=0;o<n.length;o+=1)if(i=n[o],i.expiration_date<=Date.now())0===n.length&&delete e[i.name];else if(i.matches(r))return i},this.getCookies=function(t){var n,r,i=[];for(n in e)r=this.getCookie(n,t),r&&i.push(r);return i.toString=function(){return i.join(":")},i.toValueString=function(){return i.map(function(e){return e.toValueString()}).join(";")},i},this):new r}n.CookieAccessInfo=e,n.Cookie=t,t.prototype.toString=function(){var e=[this.name+"="+this.value];return 1/0!==this.expiration_date&&e.push("expires="+new Date(this.expiration_date).toGMTString()),this.domain&&e.push("domain="+this.domain),this.path&&e.push("path="+this.path),this.secure&&e.push("secure"),this.noscript&&e.push("httponly"),e.join("; ")},t.prototype.toValueString=function(){return this.name+"="+this.value};var i=/[:](?=\s*[a-zA-Z0-9_\-]+\s*[=])/g;t.prototype.parse=function(e,n,r){if(this instanceof t){var i,o=e.split(";").filter(function(e){return!!e}),a=o[0].match(/([^=]+)=([\s\S]*)/),s=a[1],l=a[2];for(this.name=s,this.value=l,i=1;i<o.length;i+=1)switch(a=o[i].match(/([^=]+)(?:=([\s\S]*))?/),s=a[1].trim().toLowerCase(),l=a[2],s){case"httponly":this.noscript=!0;break;case"expires":this.expiration_date=l?Number(Date.parse(l)):1/0;break;case"path":this.path=l?l.trim():"",this.explicit_path=!0;break;case"domain":this.domain=l?l.trim():"",this.explicit_domain=!!this.domain;break;case"secure":this.secure=!0}return this.explicit_path||(this.path=r||"/"),this.explicit_domain||(this.domain=n),this}return(new t).parse(e,n,r)},t.prototype.matches=function(e){return this.noscript&&e.script||this.secure&&!e.secure||!this.collidesWith(e)?!1:!0},t.prototype.collidesWith=function(e){if(this.path&&!e.path||this.domain&&!e.domain)return!1;if(this.path&&0!==e.path.indexOf(this.path))return!1;if(!this.explicit_path&&this.path!==e.path)return!1;var t=e.domain&&e.domain.replace(/^[\.]/,""),n=this.domain&&this.domain.replace(/^[\.]/,"");if(n===t)return!0;if(n){if(!this.explicit_domain)return!1;var r=t.indexOf(n);return-1===r||r!==t.length-n.length?!1:!0}return!0},n.CookieJar=r,r.prototype.setCookies=function(e,n,r){e=Array.isArray(e)?e:e.split(i);var o,a,s=[];for(e=e.map(t),o=0;o<e.length;o+=1)a=e[o],this.setCookie(a,n,r)&&s.push(a);return s}}()},{}],18:[function(t,n){!function(e,t){"object"==typeof n&&"object"==typeof n.exports?n.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(t,n){function r(e){var t=e.length,n=et.type(e);return"function"===n||et.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e}function i(e,t,n){if(et.isFunction(t))return et.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return et.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(lt.test(t))return et.filter(t,e,n);t=et.filter(t,e)}return et.grep(e,function(e){return J.call(t,e)>=0!==n})}function o(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function a(e){var t=mt[e]={};return et.each(e.match(dt)||[],function(e,n){t[n]=!0}),t}function s(){G.removeEventListener("DOMContentLoaded",s,!1),t.removeEventListener("load",s,!1),et.ready()}function l(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=et.expando+l.uid++}function u(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(xt,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:wt.test(n)?et.parseJSON(n):n}catch(i){}bt.set(e,t,n)}else n=void 0;return n}function c(){return!0}function p(){return!1}function h(){try{return G.activeElement}catch(e){}}function f(e,t){return et.nodeName(e,"table")&&et.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function d(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function m(e){var t=Rt.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function g(e,t){for(var n=0,r=e.length;r>n;n++)vt.set(e[n],"globalEval",!t||vt.get(t[n],"globalEval"))}function y(e,t){var n,r,i,o,a,s,l,u;if(1===t.nodeType){if(vt.hasData(e)&&(o=vt.access(e),a=vt.set(t,o),u=o.events)){delete a.handle,a.events={};for(i in u)for(n=0,r=u[i].length;r>n;n++)et.event.add(t,i,u[i][n])}bt.hasData(e)&&(s=bt.access(e),l=et.extend({},s),bt.set(t,l))}}function v(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return void 0===t||t&&et.nodeName(e,t)?et.merge([e],n):n}function b(e,t){var n=t.nodeName.toLowerCase();"input"===n&&Ct.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}function w(e,n){var r,i=et(n.createElement(e)).appendTo(n.body),o=t.getDefaultComputedStyle&&(r=t.getDefaultComputedStyle(i[0]))?r.display:et.css(i[0],"display");return i.detach(),o}function x(e){var t=G,n=Ft[e];return n||(n=w(e,t),"none"!==n&&n||(Ht=(Ht||et("<iframe frameborder='0' width='0' height='0'/>")).appendTo(t.documentElement),t=Ht[0].contentDocument,t.write(),t.close(),n=w(e,t),Ht.detach()),Ft[e]=n),n}function A(e,t,n){var r,i,o,a,s=e.style;return n=n||zt(e),n&&(a=n.getPropertyValue(t)||n[t]),n&&(""!==a||et.contains(e.ownerDocument,e)||(a=et.style(e,t)),qt.test(a)&&Bt.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function E(e,t){return{get:function(){return e()?void delete this.get:(this.get=t).apply(this,arguments)}}}function j(e,t){if(t in e)return t;for(var n=t[0].toUpperCase()+t.slice(1),r=t,i=Xt.length;i--;)if(t=Xt[i]+n,t in e)return t;return r}function C(e,t,n){var r=Wt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function T(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;4>o;o+=2)"margin"===n&&(a+=et.css(e,n+Et[o],!0,i)),r?("content"===n&&(a-=et.css(e,"padding"+Et[o],!0,i)),"margin"!==n&&(a-=et.css(e,"border"+Et[o]+"Width",!0,i))):(a+=et.css(e,"padding"+Et[o],!0,i),"padding"!==n&&(a+=et.css(e,"border"+Et[o]+"Width",!0,i)));return a}function S(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=zt(e),a="border-box"===et.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=A(e,t,o),(0>i||null==i)&&(i=e.style[t]),qt.test(i))return i;r=a&&(K.boxSizingReliable()||i===e.style[t]),i=parseFloat(i)||0}return i+T(e,t,n||(a?"border":"content"),r,o)+"px"}function _(e,t){for(var n,r,i,o=[],a=0,s=e.length;s>a;a++)r=e[a],r.style&&(o[a]=vt.get(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&jt(r)&&(o[a]=vt.access(r,"olddisplay",x(r.nodeName)))):(i=jt(r),"none"===n&&i||vt.set(r,"olddisplay",i?n:et.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}function O(e,t,n,r,i){return new O.prototype.init(e,t,n,r,i)}function k(){return setTimeout(function(){Kt=void 0}),Kt=et.now()}function $(e,t){var n,r=0,i={height:e};for(t=t?1:0;4>r;r+=2-t)n=Et[r],i["margin"+n]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function D(e,t,n){for(var r,i=(rn[t]||[]).concat(rn["*"]),o=0,a=i.length;a>o;o++)if(r=i[o].call(n,t,e))return r}function L(e,t,n){var r,i,o,a,s,l,u,c,p=this,h={},f=e.style,d=e.nodeType&&jt(e),m=vt.get(e,"fxshow");n.queue||(s=et._queueHooks(e,"fx"),null==s.unqueued&&(s.unqueued=0,l=s.empty.fire,s.empty.fire=function(){s.unqueued||l()}),s.unqueued++,p.always(function(){p.always(function(){s.unqueued--,et.queue(e,"fx").length||s.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[f.overflow,f.overflowX,f.overflowY],u=et.css(e,"display"),c="none"===u?vt.get(e,"olddisplay")||x(e.nodeName):u,"inline"===c&&"none"===et.css(e,"float")&&(f.display="inline-block")),n.overflow&&(f.overflow="hidden",p.always(function(){f.overflow=n.overflow[0],f.overflowX=n.overflow[1],f.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Zt.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(d?"hide":"show")){if("show"!==i||!m||void 0===m[r])continue;d=!0}h[r]=m&&m[r]||et.style(e,r)}else u=void 0;if(et.isEmptyObject(h))"inline"===("none"===u?x(e.nodeName):u)&&(f.display=u);else{m?"hidden"in m&&(d=m.hidden):m=vt.access(e,"fxshow",{}),o&&(m.hidden=!d),d?et(e).show():p.done(function(){et(e).hide()}),p.done(function(){var t;vt.remove(e,"fxshow");for(t in h)et.style(e,t,h[t])});for(r in h)a=D(d?m[r]:0,r,p),r in m||(m[r]=a.start,d&&(a.end=a.start,a.start="width"===r||"height"===r?1:0))}}function N(e,t){var n,r,i,o,a;for(n in e)if(r=et.camelCase(n),i=t[r],o=e[n],et.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),a=et.cssHooks[r],a&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function P(e,t,n){var r,i,o=0,a=nn.length,s=et.Deferred().always(function(){delete l.elem}),l=function(){if(i)return!1;for(var t=Kt||k(),n=Math.max(0,u.startTime+u.duration-t),r=n/u.duration||0,o=1-r,a=0,l=u.tweens.length;l>a;a++)u.tweens[a].run(o);return s.notifyWith(e,[u,o,n]),1>o&&l?n:(s.resolveWith(e,[u]),!1)},u=s.promise({elem:e,props:et.extend({},t),opts:et.extend(!0,{specialEasing:{}},n),originalProperties:t,originalOptions:n,startTime:Kt||k(),duration:n.duration,tweens:[],createTween:function(t,n){var r=et.Tween(e,u.opts,t,n,u.opts.specialEasing[t]||u.opts.easing);return u.tweens.push(r),r},stop:function(t){var n=0,r=t?u.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)u.tweens[n].run(1);return t?s.resolveWith(e,[u,t]):s.rejectWith(e,[u,t]),this}}),c=u.props;for(N(c,u.opts.specialEasing);a>o;o++)if(r=nn[o].call(u,e,c,u.opts))return r;return et.map(c,D,u),et.isFunction(u.opts.start)&&u.opts.start.call(e,u),et.fx.timer(et.extend(l,{elem:e,anim:u,queue:u.opts.queue})),u.progress(u.opts.progress).done(u.opts.done,u.opts.complete).fail(u.opts.fail).always(u.opts.always)}function I(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(dt)||[];if(et.isFunction(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function R(e,t,n,r){function i(s){var l;return o[s]=!0,et.each(e[s]||[],function(e,s){var u=s(t,n,r);return"string"!=typeof u||a||o[u]?a?!(l=u):void 0:(t.dataTypes.unshift(u),i(u),!1)}),l}var o={},a=e===xn;return i(t.dataTypes[0])||!o["*"]&&i("*")}function M(e,t){var n,r,i=et.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&et.extend(!0,e,r),e}function U(e,t,n){for(var r,i,o,a,s=e.contents,l=e.dataTypes;"*"===l[0];)l.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){l.unshift(i);break}if(l[0]in n)o=l[0];else{for(i in n){if(!l[0]||e.converters[i+" "+l[0]]){o=i;break}a||(a=i)}o=o||a}return o?(o!==l[0]&&l.unshift(o),n[o]):void 0}function H(e,t,n,r){var i,o,a,s,l,u={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)u[a.toLowerCase()]=e.converters[a];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!l&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),l=o,o=c.shift())if("*"===o)o=l;else if("*"!==l&&l!==o){if(a=u[l+" "+o]||u["* "+o],!a)for(i in u)if(s=i.split(" "),s[1]===o&&(a=u[l+" "+s[0]]||u["* "+s[0]])){a===!0?a=u[i]:u[i]!==!0&&(o=s[0],c.unshift(s[1]));break}if(a!==!0)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(p){return{state:"parsererror",error:a?p:"No conversion from "+l+" to "+o}}}return{state:"success",data:t}}function F(e,t,n,r){var i;if(et.isArray(t))et.each(t,function(t,i){n||Tn.test(e)?r(e,i):F(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==et.type(t))r(e,t);else for(i in t)F(e+"["+i+"]",t[i],n,r)}function B(e){return et.isWindow(e)?e:9===e.nodeType&&e.defaultView}var q=[],z=q.slice,V=q.concat,W=q.push,J=q.indexOf,Y={},Q=Y.toString,X=Y.hasOwnProperty,K={},G=t.document,Z="2.1.3",et=function(e,t){return new et.fn.init(e,t)},tt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,nt=/^-ms-/,rt=/-([\da-z])/gi,it=function(e,t){return t.toUpperCase()};et.fn=et.prototype={jquery:Z,constructor:et,selector:"",length:0,toArray:function(){return z.call(this)},get:function(e){return null!=e?0>e?this[e+this.length]:this[e]:z.call(this)},pushStack:function(e){var t=et.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return et.each(this,e,t)},map:function(e){return this.pushStack(et.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(z.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:W,sort:q.sort,splice:q.splice},et.extend=et.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,l=arguments.length,u=!1;for("boolean"==typeof a&&(u=a,a=arguments[s]||{},s++),"object"==typeof a||et.isFunction(a)||(a={}),s===l&&(a=this,s--);l>s;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],r=e[t],a!==r&&(u&&r&&(et.isPlainObject(r)||(i=et.isArray(r)))?(i?(i=!1,o=n&&et.isArray(n)?n:[]):o=n&&et.isPlainObject(n)?n:{},a[t]=et.extend(u,o,r)):void 0!==r&&(a[t]=r));return a},et.extend({expando:"jQuery"+(Z+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isFunction:function(e){return"function"===et.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!et.isArray(e)&&e-parseFloat(e)+1>=0},isPlainObject:function(e){return"object"!==et.type(e)||e.nodeType||et.isWindow(e)?!1:e.constructor&&!X.call(e.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?Y[Q.call(e)]||"object":typeof e},globalEval:function(e){var t,n=eval;e=et.trim(e),e&&(1===e.indexOf("use strict")?(t=G.createElement("script"),t.text=e,G.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(nt,"ms-").replace(rt,it)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var i,o=0,a=e.length,s=r(e);if(n){if(s)for(;a>o&&(i=t.apply(e[o],n),i!==!1);o++);else for(o in e)if(i=t.apply(e[o],n),i===!1)break}else if(s)for(;a>o&&(i=t.call(e[o],o,e[o]),i!==!1);o++);else for(o in e)if(i=t.call(e[o],o,e[o]),i===!1)break;return e},trim:function(e){return null==e?"":(e+"").replace(tt,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(r(Object(e))?et.merge(n,"string"==typeof e?[e]:e):W.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:J.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;n>r;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;a>o;o++)r=!t(e[o],o),r!==s&&i.push(e[o]);return i},map:function(e,t,n){var i,o=0,a=e.length,s=r(e),l=[];if(s)for(;a>o;o++)i=t(e[o],o,n),null!=i&&l.push(i);else for(o in e)i=t(e[o],o,n),null!=i&&l.push(i);return V.apply([],l)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),et.isFunction(e)?(r=z.call(arguments,2),i=function(){return e.apply(t||this,r.concat(z.call(arguments)))},i.guid=e.guid=e.guid||et.guid++,i):void 0},now:Date.now,support:K}),et.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){Y["[object "+t+"]"]=t.toLowerCase()});var ot=function(e){function t(e,t,n,r){var i,o,a,s,l,u,p,f,d,m;if((t?t.ownerDocument||t:H)!==D&&$(t),t=t||D,n=n||[],s=t.nodeType,"string"!=typeof e||!e||1!==s&&9!==s&&11!==s)return n;if(!r&&N){if(11!==s&&(i=vt.exec(e)))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&M(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return G.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&x.getElementsByClassName)return G.apply(n,t.getElementsByClassName(a)),n}if(x.qsa&&(!P||!P.test(e))){if(f=p=U,d=t,m=1!==s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(u=C(e),(p=t.getAttribute("id"))?f=p.replace(wt,"\\$&"):t.setAttribute("id",f),f="[id='"+f+"'] ",l=u.length;l--;)u[l]=f+h(u[l]);d=bt.test(e)&&c(t.parentNode)||t,m=u.join(",")}if(m)try{return G.apply(n,d.querySelectorAll(m)),n}catch(g){}finally{p||t.removeAttribute("id")}}}return S(e.replace(lt,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>A.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[U]=!0,e}function i(e){var t=D.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)A.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||J)-(~e.sourceIndex||J);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function u(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function c(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function p(){}function h(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function f(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=B++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,u=[F,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[U]||(t[U]={}),(s=l[r])&&s[0]===F&&s[1]===o)return u[2]=s[2];if(l[r]=u,u[2]=e(t,n,a))return!0}}}function d(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(e,n,r){for(var i=0,o=n.length;o>i;i++)t(e,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,u=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function y(e,t,n,i,o,a){return i&&!i[U]&&(i=y(i)),o&&!o[U]&&(o=y(o,a)),r(function(r,a,s,l){var u,c,p,h=[],f=[],d=a.length,y=r||m(t||"*",s.nodeType?[s]:s,[]),v=!e||!r&&t?y:g(y,h,e,s,l),b=n?o||(r?e:d||i)?[]:a:v;if(n&&n(v,b,s,l),i)for(u=g(b,f),i(u,[],s,l),c=u.length;c--;)(p=u[c])&&(b[f[c]]=!(v[f[c]]=p));if(r){if(o||e){if(o){for(u=[],c=b.length;c--;)(p=b[c])&&u.push(v[c]=p);o(null,b=[],u,l)}for(c=b.length;c--;)(p=b[c])&&(u=o?et(r,p):h[c])>-1&&(r[u]=!(a[u]=p))}}else b=g(b===a?b.splice(d,b.length):b),o?o(null,a,b,l):G.apply(a,b)})}function v(e){for(var t,n,r,i=e.length,o=A.relative[e[0].type],a=o||A.relative[" "],s=o?1:0,l=f(function(e){return e===t},a,!0),u=f(function(e){return et(t,e)>-1},a,!0),c=[function(e,n,r){var i=!o&&(r||n!==_)||((t=n).nodeType?l(e,n,r):u(e,n,r));return t=null,i}];i>s;s++)if(n=A.relative[e[s].type])c=[f(d(c),n)];else{if(n=A.filter[e[s].type].apply(null,e[s].matches),n[U]){for(r=++s;i>r&&!A.relative[e[r].type];r++);return y(s>1&&d(c),s>1&&h(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(lt,"$1"),n,r>s&&v(e.slice(s,r)),i>r&&v(e=e.slice(r)),i>r&&h(e))}c.push(n)}return d(c)}function b(e,n){var i=n.length>0,o=e.length>0,a=function(r,a,s,l,u){var c,p,h,f=0,d="0",m=r&&[],y=[],v=_,b=r||o&&A.find.TAG("*",u),w=F+=null==v?1:Math.random()||.1,x=b.length;for(u&&(_=a!==D&&a);d!==x&&null!=(c=b[d]);d++){if(o&&c){for(p=0;h=e[p++];)if(h(c,a,s)){l.push(c);break}u&&(F=w)}i&&((c=!h&&c)&&f--,r&&m.push(c))}if(f+=d,i&&d!==f){for(p=0;h=n[p++];)h(m,y,a,s);if(r){if(f>0)for(;d--;)m[d]||y[d]||(y[d]=X.call(l));y=g(y)}G.apply(l,y),u&&!r&&y.length>0&&f+n.length>1&&t.uniqueSort(l)}return u&&(F=w,_=v),m};return i?r(a):a}var w,x,A,E,j,C,T,S,_,O,k,$,D,L,N,P,I,R,M,U="sizzle"+1*new Date,H=e.document,F=0,B=0,q=n(),z=n(),V=n(),W=function(e,t){return e===t&&(k=!0),0},J=1<<31,Y={}.hasOwnProperty,Q=[],X=Q.pop,K=Q.push,G=Q.push,Z=Q.slice,et=function(e,t){for(var n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1},tt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",nt="[\\x20\\t\\r\\n\\f]",rt="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",it=rt.replace("w","w#"),ot="\\["+nt+"*("+rt+")(?:"+nt+"*([*^$|!~]?=)"+nt+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+it+"))|)"+nt+"*\\]",at=":("+rt+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ot+")*)|.*)\\)|)",st=new RegExp(nt+"+","g"),lt=new RegExp("^"+nt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+nt+"+$","g"),ut=new RegExp("^"+nt+"*,"+nt+"*"),ct=new RegExp("^"+nt+"*([>+~]|"+nt+")"+nt+"*"),pt=new RegExp("="+nt+"*([^\\]'\"]*?)"+nt+"*\\]","g"),ht=new RegExp(at),ft=new RegExp("^"+it+"$"),dt={ID:new RegExp("^#("+rt+")"),CLASS:new RegExp("^\\.("+rt+")"),TAG:new RegExp("^("+rt.replace("w","w*")+")"),ATTR:new RegExp("^"+ot),PSEUDO:new RegExp("^"+at),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+nt+"*(even|odd|(([+-]|)(\\d*)n|)"+nt+"*(?:([+-]|)"+nt+"*(\\d+)|))"+nt+"*\\)|)","i"),bool:new RegExp("^(?:"+tt+")$","i"),needsContext:new RegExp("^"+nt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+nt+"*((?:-\\d)?\\d*)"+nt+"*\\)|)(?=[^-]|$)","i")},mt=/^(?:input|select|textarea|button)$/i,gt=/^h\d$/i,yt=/^[^{]+\{\s*\[native \w/,vt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,bt=/[+~]/,wt=/'|\\/g,xt=new RegExp("\\\\([\\da-f]{1,6}"+nt+"?|("+nt+")|.)","ig"),At=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},Et=function(){$()};try{G.apply(Q=Z.call(H.childNodes),H.childNodes),Q[H.childNodes.length].nodeType}catch(jt){G={apply:Q.length?function(e,t){K.apply(e,Z.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}x=t.support={},j=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},$=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:H;return r!==D&&9===r.nodeType&&r.documentElement?(D=r,L=r.documentElement,n=r.defaultView,n&&n!==n.top&&(n.addEventListener?n.addEventListener("unload",Et,!1):n.attachEvent&&n.attachEvent("onunload",Et)),N=!j(r),x.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),x.getElementsByTagName=i(function(e){return e.appendChild(r.createComment("")),!e.getElementsByTagName("*").length}),x.getElementsByClassName=yt.test(r.getElementsByClassName),x.getById=i(function(e){return L.appendChild(e).id=U,!r.getElementsByName||!r.getElementsByName(U).length}),x.getById?(A.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&N){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},A.filter.ID=function(e){var t=e.replace(xt,At);return function(e){return e.getAttribute("id")===t}}):(delete A.find.ID,A.filter.ID=function(e){var t=e.replace(xt,At);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),A.find.TAG=x.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):x.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},A.find.CLASS=x.getElementsByClassName&&function(e,t){return N?t.getElementsByClassName(e):void 0},I=[],P=[],(x.qsa=yt.test(r.querySelectorAll))&&(i(function(e){L.appendChild(e).innerHTML="<a id='"+U+"'></a><select id='"+U+"-\f]' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&P.push("[*^$]="+nt+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||P.push("\\["+nt+"*(?:value|"+tt+")"),e.querySelectorAll("[id~="+U+"-]").length||P.push("~="),e.querySelectorAll(":checked").length||P.push(":checked"),e.querySelectorAll("a#"+U+"+*").length||P.push(".#.+[+~]")}),i(function(e){var t=r.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&P.push("name"+nt+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||P.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),P.push(",.*:")})),(x.matchesSelector=yt.test(R=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){x.disconnectedMatch=R.call(e,"div"),R.call(e,"[s!='']:x"),I.push("!=",at)}),P=P.length&&new RegExp(P.join("|")),I=I.length&&new RegExp(I.join("|")),t=yt.test(L.compareDocumentPosition),M=t||yt.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},W=t?function(e,t){if(e===t)return k=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!x.sortDetached&&t.compareDocumentPosition(e)===n?e===r||e.ownerDocument===H&&M(H,e)?-1:t===r||t.ownerDocument===H&&M(H,t)?1:O?et(O,e)-et(O,t):0:4&n?-1:1)}:function(e,t){if(e===t)return k=!0,0;var n,i=0,o=e.parentNode,s=t.parentNode,l=[e],u=[t];if(!o||!s)return e===r?-1:t===r?1:o?-1:s?1:O?et(O,e)-et(O,t):0;if(o===s)return a(e,t);for(n=e;n=n.parentNode;)l.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;l[i]===u[i];)i++;return i?a(l[i],u[i]):l[i]===H?-1:u[i]===H?1:0},r):D},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==D&&$(e),n=n.replace(pt,"='$1']"),!(!x.matchesSelector||!N||I&&I.test(n)||P&&P.test(n)))try{var r=R.call(e,n);if(r||x.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return t(n,D,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==D&&$(e),M(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==D&&$(e);var n=A.attrHandle[t.toLowerCase()],r=n&&Y.call(A.attrHandle,t.toLowerCase())?n(e,t,!N):void 0;return void 0!==r?r:x.attributes||!N?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,i=0;if(k=!x.detectDuplicates,O=!x.sortStable&&e.slice(0),e.sort(W),k){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return O=null,e},E=t.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=E(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=E(t);return n},A=t.selectors={cacheLength:50,createPseudo:r,match:dt,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xt,At),e[3]=(e[3]||e[4]||e[5]||"").replace(xt,At),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return dt.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&ht.test(n)&&(t=C(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(xt,At).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=q[e+" "];return t||(t=new RegExp("(^|"+nt+")"+e+"("+nt+"|$)"))&&q(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(i){var o=t.attr(i,e);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o.replace(st," ")+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,h,f,d,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(g){if(o){for(;m;){for(p=t;p=p[m];)if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;d=m="only"===e&&!d&&"nextSibling"}return!0}if(d=[a?g.firstChild:g.lastChild],a&&v){for(c=g[U]||(g[U]={}),u=c[e]||[],f=u[0]===F&&u[1],h=u[0]===F&&u[2],p=f&&g.childNodes[f];p=++f&&p&&p[m]||(h=f=0)||d.pop();)if(1===p.nodeType&&++h&&p===t){c[e]=[F,f,h];break}}else if(v&&(u=(t[U]||(t[U]={}))[e])&&u[0]===F)h=u[1];else for(;(p=++f&&p&&p[m]||(h=f=0)||d.pop())&&((s?p.nodeName.toLowerCase()!==y:1!==p.nodeType)||!++h||(v&&((p[U]||(p[U]={}))[e]=[F,h]),p!==t)););return h-=i,h===r||h%r===0&&h/r>=0}}},PSEUDO:function(e,n){var i,o=A.pseudos[e]||A.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return o[U]?o(n):o.length>1?(i=[e,e,"",n],A.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=et(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=T(e.replace(lt,"$1"));return i[U]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(xt,At),function(t){return(t.textContent||t.innerText||E(t)).indexOf(e)>-1}}),lang:r(function(e){return ft.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(xt,At).toLowerCase(),function(t){var n;do if(n=N?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===L},focus:function(e){return e===D.activeElement&&(!D.hasFocus||D.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();
+return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!A.pseudos.empty(e)},header:function(e){return gt.test(e.nodeName)},input:function(e){return mt.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:u(function(){return[0]}),last:u(function(e,t){return[t-1]}),eq:u(function(e,t,n){return[0>n?n+t:n]}),even:u(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:u(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:u(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:u(function(e,t,n){for(var r=0>n?n+t:n;++r<t;)e.push(r);return e})}},A.pseudos.nth=A.pseudos.eq;for(w in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})A.pseudos[w]=s(w);for(w in{submit:!0,reset:!0})A.pseudos[w]=l(w);return p.prototype=A.filters=A.pseudos,A.setFilters=new p,C=t.tokenize=function(e,n){var r,i,o,a,s,l,u,c=z[e+" "];if(c)return n?0:c.slice(0);for(s=e,l=[],u=A.preFilter;s;){(!r||(i=ut.exec(s)))&&(i&&(s=s.slice(i[0].length)||s),l.push(o=[])),r=!1,(i=ct.exec(s))&&(r=i.shift(),o.push({value:r,type:i[0].replace(lt," ")}),s=s.slice(r.length));for(a in A.filter)!(i=dt[a].exec(s))||u[a]&&!(i=u[a](i))||(r=i.shift(),o.push({value:r,type:a,matches:i}),s=s.slice(r.length));if(!r)break}return n?s.length:s?t.error(e):z(e,l).slice(0)},T=t.compile=function(e,t){var n,r=[],i=[],o=V[e+" "];if(!o){for(t||(t=C(e)),n=t.length;n--;)o=v(t[n]),o[U]?r.push(o):i.push(o);o=V(e,b(i,r)),o.selector=e}return o},S=t.select=function(e,t,n,r){var i,o,a,s,l,u="function"==typeof e&&e,p=!r&&C(e=u.selector||e);if(n=n||[],1===p.length){if(o=p[0]=p[0].slice(0),o.length>2&&"ID"===(a=o[0]).type&&x.getById&&9===t.nodeType&&N&&A.relative[o[1].type]){if(t=(A.find.ID(a.matches[0].replace(xt,At),t)||[])[0],!t)return n;u&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=dt.needsContext.test(e)?0:o.length;i--&&(a=o[i],!A.relative[s=a.type]);)if((l=A.find[s])&&(r=l(a.matches[0].replace(xt,At),bt.test(o[0].type)&&c(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&h(o),!e)return G.apply(n,r),n;break}}return(u||T(e,p))(r,t,!N,n,bt.test(e)&&c(t.parentNode)||t),n},x.sortStable=U.split("").sort(W).join("")===U,x.detectDuplicates=!!k,$(),x.sortDetached=i(function(e){return 1&e.compareDocumentPosition(D.createElement("div"))}),i(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),x.attributes&&i(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(tt,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(t);et.find=ot,et.expr=ot.selectors,et.expr[":"]=et.expr.pseudos,et.unique=ot.uniqueSort,et.text=ot.getText,et.isXMLDoc=ot.isXML,et.contains=ot.contains;var at=et.expr.match.needsContext,st=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,lt=/^.[^:#\[\.,]*$/;et.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?et.find.matchesSelector(r,e)?[r]:[]:et.find.matches(e,et.grep(t,function(e){return 1===e.nodeType}))},et.fn.extend({find:function(e){var t,n=this.length,r=[],i=this;if("string"!=typeof e)return this.pushStack(et(e).filter(function(){for(t=0;n>t;t++)if(et.contains(i[t],this))return!0}));for(t=0;n>t;t++)et.find(e,i[t],r);return r=this.pushStack(n>1?et.unique(r):r),r.selector=this.selector?this.selector+" "+e:e,r},filter:function(e){return this.pushStack(i(this,e||[],!1))},not:function(e){return this.pushStack(i(this,e||[],!0))},is:function(e){return!!i(this,"string"==typeof e&&at.test(e)?et(e):e||[],!1).length}});var ut,ct=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,pt=et.fn.init=function(e,t){var n,r;if(!e)return this;if("string"==typeof e){if(n="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:ct.exec(e),!n||!n[1]&&t)return!t||t.jquery?(t||ut).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof et?t[0]:t,et.merge(this,et.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:G,!0)),st.test(n[1])&&et.isPlainObject(t))for(n in t)et.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}return r=G.getElementById(n[2]),r&&r.parentNode&&(this.length=1,this[0]=r),this.context=G,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):et.isFunction(e)?"undefined"!=typeof ut.ready?ut.ready(e):e(et):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),et.makeArray(e,this))};pt.prototype=et.fn,ut=et(G);var ht=/^(?:parents|prev(?:Until|All))/,ft={children:!0,contents:!0,next:!0,prev:!0};et.extend({dir:function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&et(e).is(n))break;r.push(e)}return r},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),et.fn.extend({has:function(e){var t=et(e,this),n=t.length;return this.filter(function(){for(var e=0;n>e;e++)if(et.contains(this,t[e]))return!0})},closest:function(e,t){for(var n,r=0,i=this.length,o=[],a=at.test(e)||"string"!=typeof e?et(e,t||this.context):0;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&et.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?et.unique(o):o)},index:function(e){return e?"string"==typeof e?J.call(et(e),this[0]):J.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(et.unique(et.merge(this.get(),et(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),et.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return et.dir(e,"parentNode")},parentsUntil:function(e,t,n){return et.dir(e,"parentNode",n)},next:function(e){return o(e,"nextSibling")},prev:function(e){return o(e,"previousSibling")},nextAll:function(e){return et.dir(e,"nextSibling")},prevAll:function(e){return et.dir(e,"previousSibling")},nextUntil:function(e,t,n){return et.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return et.dir(e,"previousSibling",n)},siblings:function(e){return et.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return et.sibling(e.firstChild)},contents:function(e){return e.contentDocument||et.merge([],e.childNodes)}},function(e,t){et.fn[e]=function(n,r){var i=et.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=et.filter(r,i)),this.length>1&&(ft[e]||et.unique(i),ht.test(e)&&i.reverse()),this.pushStack(i)}});var dt=/\S+/g,mt={};et.Callbacks=function(e){e="string"==typeof e?mt[e]||a(e):et.extend({},e);var t,n,r,i,o,s,l=[],u=!e.once&&[],c=function(a){for(t=e.memory&&a,n=!0,s=i||0,i=0,o=l.length,r=!0;l&&o>s;s++)if(l[s].apply(a[0],a[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,l&&(u?u.length&&c(u.shift()):t?l=[]:p.disable())},p={add:function(){if(l){var n=l.length;!function a(t){et.each(t,function(t,n){var r=et.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&a(n)})}(arguments),r?o=l.length:t&&(i=n,c(t))}return this},remove:function(){return l&&et.each(arguments,function(e,t){for(var n;(n=et.inArray(t,l,n))>-1;)l.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?et.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=t=void 0,this},disabled:function(){return!l},lock:function(){return u=void 0,t||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||n&&!u||(t=t||[],t=[e,t.slice?t.slice():t],r?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!n}};return p},et.extend({Deferred:function(e){var t=[["resolve","done",et.Callbacks("once memory"),"resolved"],["reject","fail",et.Callbacks("once memory"),"rejected"],["notify","progress",et.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return et.Deferred(function(n){et.each(t,function(t,o){var a=et.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&et.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[o[0]+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?et.extend(e,r):r}},i={};return r.pipe=r.then,et.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,r,i=0,o=z.call(arguments),a=o.length,s=1!==a||e&&et.isFunction(e.promise)?a:0,l=1===s?e:et.Deferred(),u=function(e,n,r){return function(i){n[e]=this,r[e]=arguments.length>1?z.call(arguments):i,r===t?l.notifyWith(n,r):--s||l.resolveWith(n,r)}};if(a>1)for(t=new Array(a),n=new Array(a),r=new Array(a);a>i;i++)o[i]&&et.isFunction(o[i].promise)?o[i].promise().done(u(i,r,o)).fail(l.reject).progress(u(i,n,t)):--s;return s||l.resolveWith(r,o),l.promise()}});var gt;et.fn.ready=function(e){return et.ready.promise().done(e),this},et.extend({isReady:!1,readyWait:1,holdReady:function(e){e?et.readyWait++:et.ready(!0)},ready:function(e){(e===!0?--et.readyWait:et.isReady)||(et.isReady=!0,e!==!0&&--et.readyWait>0||(gt.resolveWith(G,[et]),et.fn.triggerHandler&&(et(G).triggerHandler("ready"),et(G).off("ready"))))}}),et.ready.promise=function(e){return gt||(gt=et.Deferred(),"complete"===G.readyState?setTimeout(et.ready):(G.addEventListener("DOMContentLoaded",s,!1),t.addEventListener("load",s,!1))),gt.promise(e)},et.ready.promise();var yt=et.access=function(e,t,n,r,i,o,a){var s=0,l=e.length,u=null==n;if("object"===et.type(n)){i=!0;for(s in n)et.access(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,et.isFunction(r)||(a=!0),u&&(a?(t.call(e,r),t=null):(u=t,t=function(e,t,n){return u.call(et(e),n)})),t))for(;l>s;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:u?t.call(e):l?t(e[0],n):o};et.acceptData=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType},l.uid=1,l.accepts=et.acceptData,l.prototype={key:function(e){if(!l.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=l.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,et.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(et.isEmptyObject(o))et.extend(this.cache[i],t);else for(r in t)o[r]=t[r];return o},get:function(e,t){var n=this.cache[this.key(e)];return void 0===t?n:n[t]},access:function(e,t,n){var r;return void 0===t||t&&"string"==typeof t&&void 0===n?(r=this.get(e,t),void 0!==r?r:this.get(e,et.camelCase(t))):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r,i,o=this.key(e),a=this.cache[o];if(void 0===t)this.cache[o]={};else{et.isArray(t)?r=t.concat(t.map(et.camelCase)):(i=et.camelCase(t),t in a?r=[t,i]:(r=i,r=r in a?[r]:r.match(dt)||[])),n=r.length;for(;n--;)delete a[r[n]]}},hasData:function(e){return!et.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){e[this.expando]&&delete this.cache[e[this.expando]]}};var vt=new l,bt=new l,wt=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,xt=/([A-Z])/g;et.extend({hasData:function(e){return bt.hasData(e)||vt.hasData(e)},data:function(e,t,n){return bt.access(e,t,n)},removeData:function(e,t){bt.remove(e,t)},_data:function(e,t,n){return vt.access(e,t,n)},_removeData:function(e,t){vt.remove(e,t)}}),et.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=bt.get(o),1===o.nodeType&&!vt.get(o,"hasDataAttrs"))){for(n=a.length;n--;)a[n]&&(r=a[n].name,0===r.indexOf("data-")&&(r=et.camelCase(r.slice(5)),u(o,r,i[r])));vt.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){bt.set(this,e)}):yt(this,function(t){var n,r=et.camelCase(e);if(o&&void 0===t){if(n=bt.get(o,e),void 0!==n)return n;if(n=bt.get(o,r),void 0!==n)return n;if(n=u(o,r,void 0),void 0!==n)return n}else this.each(function(){var n=bt.get(this,r);bt.set(this,r,t),-1!==e.indexOf("-")&&void 0!==n&&bt.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){bt.remove(this,e)})}}),et.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=vt.get(e,t),n&&(!r||et.isArray(n)?r=vt.access(e,t,et.makeArray(n)):r.push(n)),r||[]):void 0},dequeue:function(e,t){t=t||"fx";var n=et.queue(e,t),r=n.length,i=n.shift(),o=et._queueHooks(e,t),a=function(){et.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return vt.get(e,n)||vt.access(e,n,{empty:et.Callbacks("once memory").add(function(){vt.remove(e,[t+"queue",n])})})}}),et.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?et.queue(this[0],e):void 0===t?this:this.each(function(){var n=et.queue(this,e,t);et._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&et.dequeue(this,e)})},dequeue:function(e){return this.each(function(){et.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=et.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};for("string"!=typeof e&&(t=e,e=void 0),e=e||"fx";a--;)n=vt.get(o[a],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var At=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,Et=["Top","Right","Bottom","Left"],jt=function(e,t){return e=t||e,"none"===et.css(e,"display")||!et.contains(e.ownerDocument,e)},Ct=/^(?:checkbox|radio)$/i;!function(){var e=G.createDocumentFragment(),t=e.appendChild(G.createElement("div")),n=G.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),K.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="<textarea>x</textarea>",K.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var Tt="undefined";K.focusinBubbles="onfocusin"in t;var St=/^key/,_t=/^(?:mouse|pointer|contextmenu)|click/,Ot=/^(?:focusinfocus|focusoutblur)$/,kt=/^([^.]*)(?:\.(.+)|)$/;et.event={global:{},add:function(e,t,n,r,i){var o,a,s,l,u,c,p,h,f,d,m,g=vt.get(e);if(g)for(n.handler&&(o=n,n=o.handler,i=o.selector),n.guid||(n.guid=et.guid++),(l=g.events)||(l=g.events={}),(a=g.handle)||(a=g.handle=function(t){return typeof et!==Tt&&et.event.triggered!==t.type?et.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(dt)||[""],u=t.length;u--;)s=kt.exec(t[u])||[],f=m=s[1],d=(s[2]||"").split(".").sort(),f&&(p=et.event.special[f]||{},f=(i?p.delegateType:p.bindType)||f,p=et.event.special[f]||{},c=et.extend({type:f,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&et.expr.match.needsContext.test(i),namespace:d.join(".")},o),(h=l[f])||(h=l[f]=[],h.delegateCount=0,p.setup&&p.setup.call(e,r,d,a)!==!1||e.addEventListener&&e.addEventListener(f,a,!1)),p.add&&(p.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?h.splice(h.delegateCount++,0,c):h.push(c),et.event.global[f]=!0)},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,h,f,d,m,g=vt.hasData(e)&&vt.get(e);if(g&&(l=g.events)){for(t=(t||"").match(dt)||[""],u=t.length;u--;)if(s=kt.exec(t[u])||[],f=m=s[1],d=(s[2]||"").split(".").sort(),f){for(p=et.event.special[f]||{},f=(r?p.delegateType:p.bindType)||f,h=l[f]||[],s=s[2]&&new RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=h.length;o--;)c=h[o],!i&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(h.splice(o,1),c.selector&&h.delegateCount--,p.remove&&p.remove.call(e,c));a&&!h.length&&(p.teardown&&p.teardown.call(e,d,g.handle)!==!1||et.removeEvent(e,f,g.handle),delete l[f])}else for(f in l)et.event.remove(e,f+t[u],n,r,!0);et.isEmptyObject(l)&&(delete g.handle,vt.remove(e,"events"))}},trigger:function(e,n,r,i){var o,a,s,l,u,c,p,h=[r||G],f=X.call(e,"type")?e.type:e,d=X.call(e,"namespace")?e.namespace.split("."):[];if(a=s=r=r||G,3!==r.nodeType&&8!==r.nodeType&&!Ot.test(f+et.event.triggered)&&(f.indexOf(".")>=0&&(d=f.split("."),f=d.shift(),d.sort()),u=f.indexOf(":")<0&&"on"+f,e=e[et.expando]?e:new et.Event(f,"object"==typeof e&&e),e.isTrigger=i?2:3,e.namespace=d.join("."),e.namespace_re=e.namespace?new RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=r),n=null==n?[e]:et.makeArray(n,[e]),p=et.event.special[f]||{},i||!p.trigger||p.trigger.apply(r,n)!==!1)){if(!i&&!p.noBubble&&!et.isWindow(r)){for(l=p.delegateType||f,Ot.test(l+f)||(a=a.parentNode);a;a=a.parentNode)h.push(a),s=a;s===(r.ownerDocument||G)&&h.push(s.defaultView||s.parentWindow||t)}for(o=0;(a=h[o++])&&!e.isPropagationStopped();)e.type=o>1?l:p.bindType||f,c=(vt.get(a,"events")||{})[e.type]&&vt.get(a,"handle"),c&&c.apply(a,n),c=u&&a[u],c&&c.apply&&et.acceptData(a)&&(e.result=c.apply(a,n),e.result===!1&&e.preventDefault());return e.type=f,i||e.isDefaultPrevented()||p._default&&p._default.apply(h.pop(),n)!==!1||!et.acceptData(r)||u&&et.isFunction(r[f])&&!et.isWindow(r)&&(s=r[u],s&&(r[u]=null),et.event.triggered=f,r[f](),et.event.triggered=void 0,s&&(r[u]=s)),e.result}},dispatch:function(e){e=et.event.fix(e);var t,n,r,i,o,a=[],s=z.call(arguments),l=(vt.get(this,"events")||{})[e.type]||[],u=et.event.special[e.type]||{};if(s[0]=e,e.delegateTarget=this,!u.preDispatch||u.preDispatch.call(this,e)!==!1){for(a=et.event.handlers.call(this,e,l),t=0;(i=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=i.elem,n=0;(o=i.handlers[n++])&&!e.isImmediatePropagationStopped();)(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((et.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s),void 0!==r&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()));return u.postDispatch&&u.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,a=[],s=t.delegateCount,l=e.target;if(s&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!==this;l=l.parentNode||this)if(l.disabled!==!0||"click"!==e.type){for(r=[],n=0;s>n;n++)o=t[n],i=o.selector+" ",void 0===r[i]&&(r[i]=o.needsContext?et(i,this).index(l)>=0:et.find(i,this,null,[l]).length),r[i]&&r.push(o);r.length&&a.push({elem:l,handlers:r})}return s<t.length&&a.push({elem:this,handlers:t.slice(s)}),a},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,o=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||G,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||void 0===o||(e.which=1&o?1:2&o?3:4&o?2:0),e}},fix:function(e){if(e[et.expando])return e;var t,n,r,i=e.type,o=e,a=this.fixHooks[i];for(a||(this.fixHooks[i]=a=_t.test(i)?this.mouseHooks:St.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new et.Event(o),t=r.length;t--;)n=r[t],e[n]=o[n];return e.target||(e.target=G),3===e.target.nodeType&&(e.target=e.target.parentNode),a.filter?a.filter(e,o):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==h()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===h()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&et.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(e){return et.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=et.extend(new et.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?et.event.trigger(i,null,t):et.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},et.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},et.Event=function(e,t){return this instanceof et.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&e.returnValue===!1?c:p):this.type=e,t&&et.extend(this,t),this.timeStamp=e&&e.timeStamp||et.now(),void(this[et.expando]=!0)):new et.Event(e,t)},et.Event.prototype={isDefaultPrevented:p,isPropagationStopped:p,isImmediatePropagationStopped:p,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=c,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=c,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=c,e&&e.stopImmediatePropagation&&e.stopImmediatePropagation(),this.stopPropagation()}},et.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){et.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!et.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),K.focusinBubbles||et.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){et.event.simulate(t,e.target,et.event.fix(e),!0)};et.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=vt.access(r,t);i||r.addEventListener(e,n,!0),vt.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=vt.access(r,t)-1;i?vt.access(r,t,i):(r.removeEventListener(e,n,!0),vt.remove(r,t))}}}),et.fn.extend({on:function(e,t,n,r,i){var o,a;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=void 0);for(a in e)this.on(a,t,n,e[a],i);return this}if(null==n&&null==r?(r=t,n=t=void 0):null==r&&("string"==typeof t?(r=n,n=void 0):(r=n,n=t,t=void 0)),r===!1)r=p;else if(!r)return this;return 1===i&&(o=r,r=function(e){return et().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=et.guid++)),this.each(function(){et.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,et(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=void 0),n===!1&&(n=p),this.each(function(){et.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){et.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?et.event.trigger(e,t,n,!0):void 0}});var $t=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,Dt=/<([\w:]+)/,Lt=/<|&#?\w+;/,Nt=/<(?:script|style|link)/i,Pt=/checked\s*(?:[^=]|=\s*.checked.)/i,It=/^$|\/(?:java|ecma)script/i,Rt=/^true\/(.*)/,Mt=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Ut={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};Ut.optgroup=Ut.option,Ut.tbody=Ut.tfoot=Ut.colgroup=Ut.caption=Ut.thead,Ut.th=Ut.td,et.extend({clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),l=et.contains(e.ownerDocument,e);if(!(K.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||et.isXMLDoc(e)))for(a=v(s),o=v(e),r=0,i=o.length;i>r;r++)b(o[r],a[r]);if(t)if(n)for(o=o||v(e),a=a||v(s),r=0,i=o.length;i>r;r++)y(o[r],a[r]);else y(e,s);return a=v(s,"script"),a.length>0&&g(a,!l&&v(e,"script")),s},buildFragment:function(e,t,n,r){for(var i,o,a,s,l,u,c=t.createDocumentFragment(),p=[],h=0,f=e.length;f>h;h++)if(i=e[h],i||0===i)if("object"===et.type(i))et.merge(p,i.nodeType?[i]:i);else if(Lt.test(i)){for(o=o||c.appendChild(t.createElement("div")),a=(Dt.exec(i)||["",""])[1].toLowerCase(),s=Ut[a]||Ut._default,o.innerHTML=s[1]+i.replace($t,"<$1></$2>")+s[2],u=s[0];u--;)o=o.lastChild;et.merge(p,o.childNodes),o=c.firstChild,o.textContent=""}else p.push(t.createTextNode(i));for(c.textContent="",h=0;i=p[h++];)if((!r||-1===et.inArray(i,r))&&(l=et.contains(i.ownerDocument,i),o=v(c.appendChild(i),"script"),l&&g(o),n))for(u=0;i=o[u++];)It.test(i.type||"")&&n.push(i);return c},cleanData:function(e){for(var t,n,r,i,o=et.event.special,a=0;void 0!==(n=e[a]);a++){if(et.acceptData(n)&&(i=n[vt.expando],i&&(t=vt.cache[i]))){if(t.events)for(r in t.events)o[r]?et.event.remove(n,r):et.removeEvent(n,r,t.handle);vt.cache[i]&&delete vt.cache[i]}delete bt.cache[n[bt.expando]]}}}),et.fn.extend({text:function(e){return yt(this,function(e){return void 0===e?et.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=e)})},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=f(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=f(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=e?et.filter(e,this):this,i=0;null!=(n=r[i]);i++)t||1!==n.nodeType||et.cleanData(v(n)),n.parentNode&&(t&&et.contains(n.ownerDocument,n)&&g(v(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(et.cleanData(v(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return et.clone(this,e,t)})},html:function(e){return yt(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Nt.test(e)&&!Ut[(Dt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace($t,"<$1></$2>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(et.cleanData(v(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=arguments[0];return this.domManip(arguments,function(t){e=this.parentNode,et.cleanData(v(this)),e&&e.replaceChild(t,this)}),e&&(e.length||e.nodeType)?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t){e=V.apply([],e);var n,r,i,o,a,s,l=0,u=this.length,c=this,p=u-1,h=e[0],f=et.isFunction(h);if(f||u>1&&"string"==typeof h&&!K.checkClone&&Pt.test(h))return this.each(function(n){var r=c.eq(n);f&&(e[0]=h.call(this,n,r.html())),r.domManip(e,t)});if(u&&(n=et.buildFragment(e,this[0].ownerDocument,!1,this),r=n.firstChild,1===n.childNodes.length&&(n=r),r)){for(i=et.map(v(n,"script"),d),o=i.length;u>l;l++)a=n,l!==p&&(a=et.clone(a,!0,!0),o&&et.merge(i,v(a,"script"))),t.call(this[l],a,l);if(o)for(s=i[i.length-1].ownerDocument,et.map(i,m),l=0;o>l;l++)a=i[l],It.test(a.type||"")&&!vt.access(a,"globalEval")&&et.contains(s,a)&&(a.src?et._evalUrl&&et._evalUrl(a.src):et.globalEval(a.textContent.replace(Mt,"")))}return this}}),et.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){et.fn[e]=function(e){for(var n,r=[],i=et(e),o=i.length-1,a=0;o>=a;a++)n=a===o?this:this.clone(!0),et(i[a])[t](n),W.apply(r,n.get());return this.pushStack(r)}});var Ht,Ft={},Bt=/^margin/,qt=new RegExp("^("+At+")(?!px)[a-z%]+$","i"),zt=function(e){return e.ownerDocument.defaultView.opener?e.ownerDocument.defaultView.getComputedStyle(e,null):t.getComputedStyle(e,null)};!function(){function e(){a.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",a.innerHTML="",i.appendChild(o);var e=t.getComputedStyle(a,null);n="1%"!==e.top,r="4px"===e.width,i.removeChild(o)}var n,r,i=G.documentElement,o=G.createElement("div"),a=G.createElement("div");a.style&&(a.style.backgroundClip="content-box",a.cloneNode(!0).style.backgroundClip="",K.clearCloneStyle="content-box"===a.style.backgroundClip,o.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",o.appendChild(a),t.getComputedStyle&&et.extend(K,{pixelPosition:function(){return e(),n},boxSizingReliable:function(){return null==r&&e(),r},reliableMarginRight:function(){var e,n=a.appendChild(G.createElement("div"));return n.style.cssText=a.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",n.style.marginRight=n.style.width="0",a.style.width="1px",i.appendChild(o),e=!parseFloat(t.getComputedStyle(n,null).marginRight),i.removeChild(o),a.removeChild(n),e}}))}(),et.swap=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};var Vt=/^(none|table(?!-c[ea]).+)/,Wt=new RegExp("^("+At+")(.*)$","i"),Jt=new RegExp("^([+-])=("+At+")","i"),Yt={position:"absolute",visibility:"hidden",display:"block"},Qt={letterSpacing:"0",fontWeight:"400"},Xt=["Webkit","O","Moz","ms"];et.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=A(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=et.camelCase(t),l=e.style;return t=et.cssProps[s]||(et.cssProps[s]=j(l,s)),a=et.cssHooks[t]||et.cssHooks[s],void 0===n?a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t]:(o=typeof n,"string"===o&&(i=Jt.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(et.css(e,t)),o="number"),null!=n&&n===n&&("number"!==o||et.cssNumber[s]||(n+="px"),K.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(l[t]=n)),void 0)}},css:function(e,t,n,r){var i,o,a,s=et.camelCase(t);return t=et.cssProps[s]||(et.cssProps[s]=j(e.style,s)),a=et.cssHooks[t]||et.cssHooks[s],a&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=A(e,t,r)),"normal"===i&&t in Qt&&(i=Qt[t]),""===n||n?(o=parseFloat(i),n===!0||et.isNumeric(o)?o||0:i):i}}),et.each(["height","width"],function(e,t){et.cssHooks[t]={get:function(e,n,r){return n?Vt.test(et.css(e,"display"))&&0===e.offsetWidth?et.swap(e,Yt,function(){return S(e,t,r)}):S(e,t,r):void 0},set:function(e,n,r){var i=r&&zt(e);return C(e,n,r?T(e,t,r,"border-box"===et.css(e,"boxSizing",!1,i),i):0)}}}),et.cssHooks.marginRight=E(K.reliableMarginRight,function(e,t){return t?et.swap(e,{display:"inline-block"},A,[e,"marginRight"]):void 0}),et.each({margin:"",padding:"",border:"Width"},function(e,t){et.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];4>r;r++)i[e+Et[r]+t]=o[r]||o[r-2]||o[0];
+return i}},Bt.test(e)||(et.cssHooks[e+t].set=C)}),et.fn.extend({css:function(e,t){return yt(this,function(e,t,n){var r,i,o={},a=0;if(et.isArray(t)){for(r=zt(e),i=t.length;i>a;a++)o[t[a]]=et.css(e,t[a],!1,r);return o}return void 0!==n?et.style(e,t,n):et.css(e,t)},e,t,arguments.length>1)},show:function(){return _(this,!0)},hide:function(){return _(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){jt(this)?et(this).show():et(this).hide()})}}),et.Tween=O,O.prototype={constructor:O,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(et.cssNumber[n]?"":"px")},cur:function(){var e=O.propHooks[this.prop];return e&&e.get?e.get(this):O.propHooks._default.get(this)},run:function(e){var t,n=O.propHooks[this.prop];return this.pos=t=this.options.duration?et.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):O.propHooks._default.set(this),this}},O.prototype.init.prototype=O.prototype,O.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=et.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){et.fx.step[e.prop]?et.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[et.cssProps[e.prop]]||et.cssHooks[e.prop])?et.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},O.propHooks.scrollTop=O.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},et.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},et.fx=O.prototype.init,et.fx.step={};var Kt,Gt,Zt=/^(?:toggle|show|hide)$/,en=new RegExp("^(?:([+-])=|)("+At+")([a-z%]*)$","i"),tn=/queueHooks$/,nn=[L],rn={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=en.exec(t),o=i&&i[3]||(et.cssNumber[e]?"":"px"),a=(et.cssNumber[e]||"px"!==o&&+r)&&en.exec(et.css(n.elem,e)),s=1,l=20;if(a&&a[3]!==o){o=o||a[3],i=i||[],a=+r||1;do s=s||".5",a/=s,et.style(n.elem,e,a+o);while(s!==(s=n.cur()/r)&&1!==s&&--l)}return i&&(a=n.start=+a||+r||0,n.unit=o,n.end=i[1]?a+(i[1]+1)*i[2]:+i[2]),n}]};et.Animation=et.extend(P,{tweener:function(e,t){et.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");for(var n,r=0,i=e.length;i>r;r++)n=e[r],rn[n]=rn[n]||[],rn[n].unshift(t)},prefilter:function(e,t){t?nn.unshift(e):nn.push(e)}}),et.speed=function(e,t,n){var r=e&&"object"==typeof e?et.extend({},e):{complete:n||!n&&t||et.isFunction(e)&&e,duration:e,easing:n&&t||t&&!et.isFunction(t)&&t};return r.duration=et.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in et.fx.speeds?et.fx.speeds[r.duration]:et.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){et.isFunction(r.old)&&r.old.call(this),r.queue&&et.dequeue(this,r.queue)},r},et.fn.extend({fadeTo:function(e,t,n,r){return this.filter(jt).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=et.isEmptyObject(e),o=et.speed(t,n,r),a=function(){var t=P(this,et.extend({},e),o);(i||vt.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=et.timers,a=vt.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&tn.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));(t||!n)&&et.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=vt.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=et.timers,a=r?r.length:0;for(n.finish=!0,et.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),et.each(["toggle","show","hide"],function(e,t){var n=et.fn[t];et.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate($(t,!0),e,r,i)}}),et.each({slideDown:$("show"),slideUp:$("hide"),slideToggle:$("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){et.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),et.timers=[],et.fx.tick=function(){var e,t=0,n=et.timers;for(Kt=et.now();t<n.length;t++)e=n[t],e()||n[t]!==e||n.splice(t--,1);n.length||et.fx.stop(),Kt=void 0},et.fx.timer=function(e){et.timers.push(e),e()?et.fx.start():et.timers.pop()},et.fx.interval=13,et.fx.start=function(){Gt||(Gt=setInterval(et.fx.tick,et.fx.interval))},et.fx.stop=function(){clearInterval(Gt),Gt=null},et.fx.speeds={slow:600,fast:200,_default:400},et.fn.delay=function(e,t){return e=et.fx?et.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},function(){var e=G.createElement("input"),t=G.createElement("select"),n=t.appendChild(G.createElement("option"));e.type="checkbox",K.checkOn=""!==e.value,K.optSelected=n.selected,t.disabled=!0,K.optDisabled=!n.disabled,e=G.createElement("input"),e.value="t",e.type="radio",K.radioValue="t"===e.value}();var on,an,sn=et.expr.attrHandle;et.fn.extend({attr:function(e,t){return yt(this,et.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){et.removeAttr(this,e)})}}),et.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return typeof e.getAttribute===Tt?et.prop(e,t,n):(1===o&&et.isXMLDoc(e)||(t=t.toLowerCase(),r=et.attrHooks[t]||(et.expr.match.bool.test(t)?an:on)),void 0===n?r&&"get"in r&&null!==(i=r.get(e,t))?i:(i=et.find.attr(e,t),null==i?void 0:i):null!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):void et.removeAttr(e,t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(dt);if(o&&1===e.nodeType)for(;n=o[i++];)r=et.propFix[n]||n,et.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!K.radioValue&&"radio"===t&&et.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),an={set:function(e,t,n){return t===!1?et.removeAttr(e,n):e.setAttribute(n,n),n}},et.each(et.expr.match.bool.source.match(/\w+/g),function(e,t){var n=sn[t]||et.find.attr;sn[t]=function(e,t,r){var i,o;return r||(o=sn[t],sn[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,sn[t]=o),i}});var ln=/^(?:input|select|textarea|button)$/i;et.fn.extend({prop:function(e,t){return yt(this,et.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[et.propFix[e]||e]})}}),et.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,a=e.nodeType;if(e&&3!==a&&8!==a&&2!==a)return o=1!==a||!et.isXMLDoc(e),o&&(t=et.propFix[t]||t,i=et.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||ln.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),K.optSelected||(et.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),et.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){et.propFix[this.toLowerCase()]=this});var un=/[\t\r\n\f]/g;et.fn.extend({addClass:function(e){var t,n,r,i,o,a,s="string"==typeof e&&e,l=0,u=this.length;if(et.isFunction(e))return this.each(function(t){et(this).addClass(e.call(this,t,this.className))});if(s)for(t=(e||"").match(dt)||[];u>l;l++)if(n=this[l],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(un," "):" ")){for(o=0;i=t[o++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");a=et.trim(r),n.className!==a&&(n.className=a)}return this},removeClass:function(e){var t,n,r,i,o,a,s=0===arguments.length||"string"==typeof e&&e,l=0,u=this.length;if(et.isFunction(e))return this.each(function(t){et(this).removeClass(e.call(this,t,this.className))});if(s)for(t=(e||"").match(dt)||[];u>l;l++)if(n=this[l],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(un," "):"")){for(o=0;i=t[o++];)for(;r.indexOf(" "+i+" ")>=0;)r=r.replace(" "+i+" "," ");a=e?et.trim(r):"",n.className!==a&&(n.className=a)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):this.each(et.isFunction(e)?function(n){et(this).toggleClass(e.call(this,n,this.className,t),t)}:function(){if("string"===n)for(var t,r=0,i=et(this),o=e.match(dt)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else(n===Tt||"boolean"===n)&&(this.className&&vt.set(this,"__className__",this.className),this.className=this.className||e===!1?"":vt.get(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(un," ").indexOf(t)>=0)return!0;return!1}});var cn=/\r/g;et.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=et.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,et(this).val()):e,null==i?i="":"number"==typeof i?i+="":et.isArray(i)&&(i=et.map(i,function(e){return null==e?"":e+""})),t=et.valHooks[this.type]||et.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return t=et.valHooks[i.type]||et.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:(n=i.value,"string"==typeof n?n.replace(cn,""):null==n?"":n)}}}),et.extend({valHooks:{option:{get:function(e){var t=et.find.attr(e,"value");return null!=t?t:et.trim(et.text(e))}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(K.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&et.nodeName(n.parentNode,"optgroup"))){if(t=et(n).val(),o)return t;a.push(t)}return a},set:function(e,t){for(var n,r,i=e.options,o=et.makeArray(t),a=i.length;a--;)r=i[a],(r.selected=et.inArray(r.value,o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),et.each(["radio","checkbox"],function(){et.valHooks[this]={set:function(e,t){return et.isArray(t)?e.checked=et.inArray(et(e).val(),t)>=0:void 0}},K.checkOn||(et.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),et.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){et.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),et.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var pn=et.now(),hn=/\?/;et.parseJSON=function(e){return JSON.parse(e+"")},et.parseXML=function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=void 0}return(!t||t.getElementsByTagName("parsererror").length)&&et.error("Invalid XML: "+e),t};var fn=/#.*$/,dn=/([?&])_=[^&]*/,mn=/^(.*?):[ \t]*([^\r\n]*)$/gm,gn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,yn=/^(?:GET|HEAD)$/,vn=/^\/\//,bn=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,wn={},xn={},An="*/".concat("*"),En=t.location.href,jn=bn.exec(En.toLowerCase())||[];et.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:En,type:"GET",isLocal:gn.test(jn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":An,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":et.parseJSON,"text xml":et.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?M(M(e,et.ajaxSettings),t):M(et.ajaxSettings,e)},ajaxPrefilter:I(wn),ajaxTransport:I(xn),ajax:function(e,t){function n(e,t,n,a){var l,c,y,v,w,A=t;2!==b&&(b=2,s&&clearTimeout(s),r=void 0,o=a||"",x.readyState=e>0?4:0,l=e>=200&&300>e||304===e,n&&(v=U(p,x,n)),v=H(p,v,x,l),l?(p.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(et.lastModified[i]=w),w=x.getResponseHeader("etag"),w&&(et.etag[i]=w)),204===e||"HEAD"===p.type?A="nocontent":304===e?A="notmodified":(A=v.state,c=v.data,y=v.error,l=!y)):(y=A,(e||!A)&&(A="error",0>e&&(e=0))),x.status=e,x.statusText=(t||A)+"",l?d.resolveWith(h,[c,A,x]):d.rejectWith(h,[x,A,y]),x.statusCode(g),g=void 0,u&&f.trigger(l?"ajaxSuccess":"ajaxError",[x,p,l?c:y]),m.fireWith(h,[x,A]),u&&(f.trigger("ajaxComplete",[x,p]),--et.active||et.event.trigger("ajaxStop")))}"object"==typeof e&&(t=e,e=void 0),t=t||{};var r,i,o,a,s,l,u,c,p=et.ajaxSetup({},t),h=p.context||p,f=p.context&&(h.nodeType||h.jquery)?et(h):et.event,d=et.Deferred(),m=et.Callbacks("once memory"),g=p.statusCode||{},y={},v={},b=0,w="canceled",x={readyState:0,getResponseHeader:function(e){var t;if(2===b){if(!a)for(a={};t=mn.exec(o);)a[t[1].toLowerCase()]=t[2];t=a[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===b?o:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return b||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return b||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>b)for(t in e)g[t]=[g[t],e[t]];else x.always(e[x.status]);return this},abort:function(e){var t=e||w;return r&&r.abort(t),n(0,t),this}};if(d.promise(x).complete=m.add,x.success=x.done,x.error=x.fail,p.url=((e||p.url||En)+"").replace(fn,"").replace(vn,jn[1]+"//"),p.type=t.method||t.type||p.method||p.type,p.dataTypes=et.trim(p.dataType||"*").toLowerCase().match(dt)||[""],null==p.crossDomain&&(l=bn.exec(p.url.toLowerCase()),p.crossDomain=!(!l||l[1]===jn[1]&&l[2]===jn[2]&&(l[3]||("http:"===l[1]?"80":"443"))===(jn[3]||("http:"===jn[1]?"80":"443")))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=et.param(p.data,p.traditional)),R(wn,p,t,x),2===b)return x;u=et.event&&p.global,u&&0===et.active++&&et.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!yn.test(p.type),i=p.url,p.hasContent||(p.data&&(i=p.url+=(hn.test(i)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=dn.test(i)?i.replace(dn,"$1_="+pn++):i+(hn.test(i)?"&":"?")+"_="+pn++)),p.ifModified&&(et.lastModified[i]&&x.setRequestHeader("If-Modified-Since",et.lastModified[i]),et.etag[i]&&x.setRequestHeader("If-None-Match",et.etag[i])),(p.data&&p.hasContent&&p.contentType!==!1||t.contentType)&&x.setRequestHeader("Content-Type",p.contentType),x.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+An+"; q=0.01":""):p.accepts["*"]);for(c in p.headers)x.setRequestHeader(c,p.headers[c]);if(p.beforeSend&&(p.beforeSend.call(h,x,p)===!1||2===b))return x.abort();w="abort";for(c in{success:1,error:1,complete:1})x[c](p[c]);if(r=R(xn,p,t,x)){x.readyState=1,u&&f.trigger("ajaxSend",[x,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){x.abort("timeout")},p.timeout));try{b=1,r.send(y,n)}catch(A){if(!(2>b))throw A;n(-1,A)}}else n(-1,"No Transport");return x},getJSON:function(e,t,n){return et.get(e,t,n,"json")},getScript:function(e,t){return et.get(e,void 0,t,"script")}}),et.each(["get","post"],function(e,t){et[t]=function(e,n,r,i){return et.isFunction(n)&&(i=i||r,r=n,n=void 0),et.ajax({url:e,type:t,dataType:i,data:n,success:r})}}),et._evalUrl=function(e){return et.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},et.fn.extend({wrapAll:function(e){var t;return et.isFunction(e)?this.each(function(t){et(this).wrapAll(e.call(this,t))}):(this[0]&&(t=et(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return this.each(et.isFunction(e)?function(t){et(this).wrapInner(e.call(this,t))}:function(){var t=et(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=et.isFunction(e);return this.each(function(n){et(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){et.nodeName(this,"body")||et(this).replaceWith(this.childNodes)}).end()}}),et.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0},et.expr.filters.visible=function(e){return!et.expr.filters.hidden(e)};var Cn=/%20/g,Tn=/\[\]$/,Sn=/\r?\n/g,_n=/^(?:submit|button|image|reset|file)$/i,On=/^(?:input|select|textarea|keygen)/i;et.param=function(e,t){var n,r=[],i=function(e,t){t=et.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=et.ajaxSettings&&et.ajaxSettings.traditional),et.isArray(e)||e.jquery&&!et.isPlainObject(e))et.each(e,function(){i(this.name,this.value)});else for(n in e)F(n,e[n],t,i);return r.join("&").replace(Cn,"+")},et.fn.extend({serialize:function(){return et.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=et.prop(this,"elements");return e?et.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!et(this).is(":disabled")&&On.test(this.nodeName)&&!_n.test(e)&&(this.checked||!Ct.test(e))}).map(function(e,t){var n=et(this).val();return null==n?null:et.isArray(n)?et.map(n,function(e){return{name:t.name,value:e.replace(Sn,"\r\n")}}):{name:t.name,value:n.replace(Sn,"\r\n")}}).get()}}),et.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(e){}};var kn=0,$n={},Dn={0:200,1223:204},Ln=et.ajaxSettings.xhr();t.attachEvent&&t.attachEvent("onunload",function(){for(var e in $n)$n[e]()}),K.cors=!!Ln&&"withCredentials"in Ln,K.ajax=Ln=!!Ln,et.ajaxTransport(function(e){var t;return K.cors||Ln&&!e.crossDomain?{send:function(n,r){var i,o=e.xhr(),a=++kn;if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)o[i]=e.xhrFields[i];e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)o.setRequestHeader(i,n[i]);t=function(e){return function(){t&&(delete $n[a],t=o.onload=o.onerror=null,"abort"===e?o.abort():"error"===e?r(o.status,o.statusText):r(Dn[o.status]||o.status,o.statusText,"string"==typeof o.responseText?{text:o.responseText}:void 0,o.getAllResponseHeaders()))}},o.onload=t(),o.onerror=t("error"),t=$n[a]=t("abort");try{o.send(e.hasContent&&e.data||null)}catch(s){if(t)throw s}},abort:function(){t&&t()}}:void 0}),et.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return et.globalEval(e),e}}}),et.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),et.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=et("<script>").prop({async:!0,charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&i("error"===e.type?404:200,e.type)}),G.head.appendChild(t[0])},abort:function(){n&&n()}}}});var Nn=[],Pn=/(=)\?(?=&|$)|\?\?/;et.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Nn.pop()||et.expando+"_"+pn++;return this[e]=!0,e}}),et.ajaxPrefilter("json jsonp",function(e,n,r){var i,o,a,s=e.jsonp!==!1&&(Pn.test(e.url)?"url":"string"==typeof e.data&&!(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Pn.test(e.data)&&"data");return s||"jsonp"===e.dataTypes[0]?(i=e.jsonpCallback=et.isFunction(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,s?e[s]=e[s].replace(Pn,"$1"+i):e.jsonp!==!1&&(e.url+=(hn.test(e.url)?"&":"?")+e.jsonp+"="+i),e.converters["script json"]=function(){return a||et.error(i+" was not called"),a[0]},e.dataTypes[0]="json",o=t[i],t[i]=function(){a=arguments},r.always(function(){t[i]=o,e[i]&&(e.jsonpCallback=n.jsonpCallback,Nn.push(i)),a&&et.isFunction(o)&&o(a[0]),a=o=void 0}),"script"):void 0}),et.parseHTML=function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||G;var r=st.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=et.buildFragment([e],t,i),i&&i.length&&et(i).remove(),et.merge([],r.childNodes))};var In=et.fn.load;et.fn.load=function(e,t,n){if("string"!=typeof e&&In)return In.apply(this,arguments);var r,i,o,a=this,s=e.indexOf(" ");return s>=0&&(r=et.trim(e.slice(s)),e=e.slice(0,s)),et.isFunction(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&et.ajax({url:e,type:i,dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?et("<div>").append(et.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){a.each(n,o||[e.responseText,t,e])}),this},et.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){et.fn[t]=function(e){return this.on(t,e)}}),et.expr.filters.animated=function(e){return et.grep(et.timers,function(t){return e===t.elem}).length};var Rn=t.document.documentElement;et.offset={setOffset:function(e,t,n){var r,i,o,a,s,l,u,c=et.css(e,"position"),p=et(e),h={};"static"===c&&(e.style.position="relative"),s=p.offset(),o=et.css(e,"top"),l=et.css(e,"left"),u=("absolute"===c||"fixed"===c)&&(o+l).indexOf("auto")>-1,u?(r=p.position(),a=r.top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(l)||0),et.isFunction(t)&&(t=t.call(e,n,s)),null!=t.top&&(h.top=t.top-s.top+a),null!=t.left&&(h.left=t.left-s.left+i),"using"in t?t.using.call(e,h):p.css(h)}},et.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){et.offset.setOffset(this,e,t)});var t,n,r=this[0],i={top:0,left:0},o=r&&r.ownerDocument;if(o)return t=o.documentElement,et.contains(t,r)?(typeof r.getBoundingClientRect!==Tt&&(i=r.getBoundingClientRect()),n=B(o),{top:i.top+n.pageYOffset-t.clientTop,left:i.left+n.pageXOffset-t.clientLeft}):i},position:function(){if(this[0]){var e,t,n=this[0],r={top:0,left:0};return"fixed"===et.css(n,"position")?t=n.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),et.nodeName(e[0],"html")||(r=e.offset()),r.top+=et.css(e[0],"borderTopWidth",!0),r.left+=et.css(e[0],"borderLeftWidth",!0)),{top:t.top-r.top-et.css(n,"marginTop",!0),left:t.left-r.left-et.css(n,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||Rn;e&&!et.nodeName(e,"html")&&"static"===et.css(e,"position");)e=e.offsetParent;return e||Rn})}}),et.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r="pageYOffset"===n;et.fn[e]=function(i){return yt(this,function(e,i,o){var a=B(e);return void 0===o?a?a[n]:e[i]:void(a?a.scrollTo(r?t.pageXOffset:o,r?o:t.pageYOffset):e[i]=o)},e,i,arguments.length,null)}}),et.each(["top","left"],function(e,t){et.cssHooks[t]=E(K.pixelPosition,function(e,n){return n?(n=A(e,t),qt.test(n)?et(e).position()[t]+"px":n):void 0})}),et.each({Height:"height",Width:"width"},function(e,t){et.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){et.fn[r]=function(r,i){var o=arguments.length&&(n||"boolean"!=typeof r),a=n||(r===!0||i===!0?"margin":"border");return yt(this,function(t,n,r){var i;return et.isWindow(t)?t.document.documentElement["client"+e]:9===t.nodeType?(i=t.documentElement,Math.max(t.body["scroll"+e],i["scroll"+e],t.body["offset"+e],i["offset"+e],i["client"+e])):void 0===r?et.css(t,n,a):et.style(t,n,r,a)},t,o?r:void 0,o,null)}})}),et.fn.size=function(){return this.length},et.fn.andSelf=et.fn.addBack,"function"==typeof e&&e.amd&&e("jquery",[],function(){return et});var Mn=t.jQuery,Un=t.$;return et.noConflict=function(e){return t.$===et&&(t.$=Un),e&&t.jQuery===et&&(t.jQuery=Mn),et},typeof n===Tt&&(t.jQuery=t.$=et),et})},{}],19:[function(e,t){function n(e,t,n){var a=e?e.length:0;if(!a)return-1;if("number"==typeof n)n=0>n?o(a+n,0):n;else if(n){var s=i(e,t),l=e[s];return(t===t?t===l:l!==l)?s:-1}return r(e,t,n||0)}var r=e("../internal/baseIndexOf"),i=e("../internal/binaryIndex"),o=Math.max;t.exports=n},{"../internal/baseIndexOf":42,"../internal/binaryIndex":54}],20:[function(e,t){function n(e){if(s(e)&&!a(e)&&!(e instanceof r)){if(e instanceof i)return e;if(c.call(e,"__chain__")&&c.call(e,"__wrapped__"))return l(e)}return new i(e)}var r=e("../internal/LazyWrapper"),i=e("../internal/LodashWrapper"),o=e("../internal/baseLodash"),a=e("../lang/isArray"),s=e("../internal/isObjectLike"),l=e("../internal/wrapperClone"),u=Object.prototype,c=u.hasOwnProperty;n.prototype=o.prototype,t.exports=n},{"../internal/LazyWrapper":27,"../internal/LodashWrapper":28,"../internal/baseLodash":47,"../internal/isObjectLike":82,"../internal/wrapperClone":93,"../lang/isArray":96}],21:[function(e,t){var n=e("../internal/baseEach"),r=e("../internal/createFind"),i=r(n);t.exports=i},{"../internal/baseEach":36,"../internal/createFind":64}],22:[function(e,t){var n=e("../internal/arrayEach"),r=e("../internal/baseEach"),i=e("../internal/createForEach"),o=i(n,r);t.exports=o},{"../internal/arrayEach":30,"../internal/baseEach":36,"../internal/createForEach":65}],23:[function(e,t){function n(e,t,n){var s=a(e)?r:o;return t=i(t,n,3),s(e,t)}var r=e("../internal/arrayMap"),i=e("../internal/baseCallback"),o=e("../internal/baseMap"),a=e("../lang/isArray");t.exports=n},{"../internal/arrayMap":31,"../internal/baseCallback":32,"../internal/baseMap":48,"../lang/isArray":96}],24:[function(e,t){var n=e("../lang/isNative"),r=n(r=Date.now)&&r,i=r||function(){return(new Date).getTime()};t.exports=i},{"../lang/isNative":98}],25:[function(e,t){var n=e("../internal/createWrapper"),r=e("../internal/replaceHolders"),i=e("./restParam"),o=1,a=32,s=i(function(e,t,i){var l=o;if(i.length){var u=r(i,s.placeholder);l|=a}return n(e,l,t,i,u)});s.placeholder={},t.exports=s},{"../internal/createWrapper":68,"../internal/replaceHolders":88,"./restParam":26}],26:[function(e,t){function n(e,t){if("function"!=typeof e)throw new TypeError(r);return t=i("undefined"==typeof t?e.length-1:+t||0,0),function(){for(var n=arguments,r=-1,o=i(n.length-t,0),a=Array(o);++r<o;)a[r]=n[t+r];switch(t){case 0:return e.call(this,a);case 1:return e.call(this,n[0],a);case 2:return e.call(this,n[0],n[1],a)}var s=Array(t+1);for(r=-1;++r<t;)s[r]=n[r];return s[t]=a,e.apply(this,s)}}var r="Expected a function",i=Math.max;t.exports=n},{}],27:[function(e,t){function n(e){this.__wrapped__=e,this.__actions__=null,this.__dir__=1,this.__dropCount__=0,this.__filtered__=!1,this.__iteratees__=null,this.__takeCount__=o,this.__views__=null}var r=e("./baseCreate"),i=e("./baseLodash"),o=Number.POSITIVE_INFINITY;n.prototype=r(i.prototype),n.prototype.constructor=n,t.exports=n},{"./baseCreate":35,"./baseLodash":47}],28:[function(e,t){function n(e,t,n){this.__wrapped__=e,this.__actions__=n||[],this.__chain__=!!t}var r=e("./baseCreate"),i=e("./baseLodash");n.prototype=r(i.prototype),n.prototype.constructor=n,t.exports=n},{"./baseCreate":35,"./baseLodash":47}],29:[function(e,t){function n(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n<r;)t[n]=e[n];return t}t.exports=n},{}],30:[function(e,t){function n(e,t){for(var n=-1,r=e.length;++n<r&&t(e[n],n,e)!==!1;);return e}t.exports=n},{}],31:[function(e,t){function n(e,t){for(var n=-1,r=e.length,i=Array(r);++n<r;)i[n]=t(e[n],n,e);return i}t.exports=n},{}],32:[function(e,t){function n(e,t,n){var l=typeof e;return"function"==l?"undefined"==typeof t?e:a(e,t,n):null==e?s:"object"==l?r(e):"undefined"==typeof t?o(e+""):i(e+"",t)}var r=e("./baseMatches"),i=e("./baseMatchesProperty"),o=e("./baseProperty"),a=e("./bindCallback"),s=e("../utility/identity");t.exports=n},{"../utility/identity":109,"./baseMatches":49,"./baseMatchesProperty":50,"./baseProperty":51,"./bindCallback":56}],33:[function(e,t){function n(e,t,m,g,y,v,w){var x;if(m&&(x=y?m(e,g,y):m(e)),"undefined"!=typeof x)return x;if(!h(e))return e;var E=c(e);if(E){if(x=s(e),!t)return r(e,x)}else{var j=U.call(e),C=j==b;if(j!=A&&j!=d&&(!C||y))return R[j]?l(e,j,t):y?e:{};if(p(e))return y?e:{};if(x=u(C?{}:e),!t)return o(e,x,f(e))}v||(v=[]),w||(w=[]);for(var T=v.length;T--;)if(v[T]==e)return w[T];return v.push(e),w.push(x),(E?i:a)(e,function(r,i){x[i]=n(r,t,m,i,e,v,w)}),x}var r=e("./arrayCopy"),i=e("./arrayEach"),o=e("./baseCopy"),a=e("./baseForOwn"),s=e("./initCloneArray"),l=e("./initCloneByTag"),u=e("./initCloneObject"),c=e("../lang/isArray"),p=e("./isHostObject"),h=e("../lang/isObject"),f=e("../object/keys"),d="[object Arguments]",m="[object Array]",g="[object Boolean]",y="[object Date]",v="[object Error]",b="[object Function]",w="[object Map]",x="[object Number]",A="[object Object]",E="[object RegExp]",j="[object Set]",C="[object String]",T="[object WeakMap]",S="[object ArrayBuffer]",_="[object Float32Array]",O="[object Float64Array]",k="[object Int8Array]",$="[object Int16Array]",D="[object Int32Array]",L="[object Uint8Array]",N="[object Uint8ClampedArray]",P="[object Uint16Array]",I="[object Uint32Array]",R={};R[d]=R[m]=R[S]=R[g]=R[y]=R[_]=R[O]=R[k]=R[$]=R[D]=R[x]=R[A]=R[E]=R[C]=R[L]=R[N]=R[P]=R[I]=!0,R[v]=R[b]=R[w]=R[j]=R[T]=!1;var M=Object.prototype,U=M.toString;t.exports=n},{"../lang/isArray":96,"../lang/isObject":99,"../object/keys":104,"./arrayCopy":29,"./arrayEach":30,"./baseCopy":34,"./baseForOwn":41,"./initCloneArray":75,"./initCloneByTag":76,"./initCloneObject":77,"./isHostObject":78}],34:[function(e,t){function n(e,t,n){n||(n=t,t={});for(var r=-1,i=n.length;++r<i;){var o=n[r];t[o]=e[o]}return t}t.exports=n},{}],35:[function(e,t){(function(n){var r=e("../lang/isObject"),i=function(){function e(){}return function(t){if(r(t)){e.prototype=t;var i=new e;e.prototype=null}return i||n.Object()}}();t.exports=i}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../lang/isObject":99}],36:[function(e,t){var n=e("./baseForOwn"),r=e("./createBaseEach"),i=r(n);t.exports=i},{"./baseForOwn":41,"./createBaseEach":60}],37:[function(e,t){function n(e,t,n,r){var i;return n(e,function(e,n,o){return t(e,n,o)?(i=r?n:e,!1):void 0}),i}t.exports=n},{}],38:[function(e,t){function n(e,t,n){for(var r=e.length,i=n?r:-1;n?i--:++i<r;)if(t(e[i],i,e))return i;return-1}t.exports=n},{}],39:[function(e,t){var n=e("./createBaseFor"),r=n();t.exports=r},{"./createBaseFor":61}],40:[function(e,t){function n(e,t){return r(e,t,i)}var r=e("./baseFor"),i=e("../object/keysIn");t.exports=n},{"../object/keysIn":105,"./baseFor":39}],41:[function(e,t){function n(e,t){return r(e,t,i)}var r=e("./baseFor"),i=e("../object/keys");t.exports=n},{"../object/keys":104,"./baseFor":39}],42:[function(e,t){function n(e,t,n){if(t!==t)return r(e,n);for(var i=n-1,o=e.length;++i<o;)if(e[i]===t)return i;return-1}var r=e("./indexOfNaN");t.exports=n},{"./indexOfNaN":74}],43:[function(e,t){function n(e,t,i,o,a,s){if(e===t)return 0!==e||1/e==1/t;var l=typeof e,u=typeof t;return"function"!=l&&"object"!=l&&"function"!=u&&"object"!=u||null==e||null==t?e!==e&&t!==t:r(e,t,n,i,o,a,s)}var r=e("./baseIsEqualDeep");t.exports=n},{"./baseIsEqualDeep":44}],44:[function(e,t){function n(e,t,n,f,g,y,v){var b=a(e),w=a(t),x=c,A=c;b||(x=m.call(e),x==u?x=h:x!=h&&(b=l(e))),w||(A=m.call(t),A==u?A=h:A!=h&&(w=l(t)));var E=(x==h||g&&x==p)&&!s(e),j=(A==h||g&&A==p)&&!s(t),C=x==A;if(C&&!b&&!E)return i(e,t,x);if(g){if(!(C||E&&j))return!1}else{var T=E&&d.call(e,"__wrapped__"),S=j&&d.call(t,"__wrapped__");if(T||S)return n(T?e.value():e,S?t.value():t,f,g,y,v);
+if(!C)return!1}y||(y=[]),v||(v=[]);for(var _=y.length;_--;)if(y[_]==e)return v[_]==t;y.push(e),v.push(t);var O=(b?r:o)(e,t,n,f,g,y,v);return y.pop(),v.pop(),O}var r=e("./equalArrays"),i=e("./equalByTag"),o=e("./equalObjects"),a=e("../lang/isArray"),s=e("./isHostObject"),l=e("../lang/isTypedArray"),u="[object Arguments]",c="[object Array]",p="[object Function]",h="[object Object]",f=Object.prototype,d=f.hasOwnProperty,m=f.toString;t.exports=n},{"../lang/isArray":96,"../lang/isTypedArray":102,"./equalArrays":69,"./equalByTag":70,"./equalObjects":71,"./isHostObject":78}],45:[function(e,t){function n(e){return"function"==typeof e||!1}t.exports=n},{}],46:[function(e,t){function n(e,t,n,i,o){for(var a=-1,s=t.length,l=!o;++a<s;)if(l&&i[a]?n[a]!==e[t[a]]:!(t[a]in e))return!1;for(a=-1;++a<s;){var u=t[a],c=e[u],p=n[a];if(l&&i[a])var h="undefined"!=typeof c||u in e;else h=o?o(c,p,u):void 0,"undefined"==typeof h&&(h=r(p,c,o,!0));if(!h)return!1}return!0}var r=e("./baseIsEqual");t.exports=n},{"./baseIsEqual":43}],47:[function(e,t){function n(){}t.exports=n},{}],48:[function(e,t){function n(e,t){var n=[];return r(e,function(e,r,i){n.push(t(e,r,i))}),n}var r=e("./baseEach");t.exports=n},{"./baseEach":36}],49:[function(e,t){function n(e){var t=a(e),n=t.length;if(!n)return i(!0);if(1==n){var l=t[0],u=e[l];if(o(u))return function(e){return null!=e&&e[l]===u&&("undefined"!=typeof u||l in s(e))}}for(var c=Array(n),p=Array(n);n--;)u=e[t[n]],c[n]=u,p[n]=o(u);return function(e){return null!=e&&r(s(e),t,c,p)}}var r=e("./baseIsMatch"),i=e("../utility/constant"),o=e("./isStrictComparable"),a=e("../object/keys"),s=e("./toObject");t.exports=n},{"../object/keys":104,"../utility/constant":108,"./baseIsMatch":46,"./isStrictComparable":83,"./toObject":92}],50:[function(e,t){function n(e,t){return i(t)?function(n){return null!=n&&n[e]===t&&("undefined"!=typeof t||e in o(n))}:function(n){return null!=n&&r(t,n[e],null,!0)}}var r=e("./baseIsEqual"),i=e("./isStrictComparable"),o=e("./toObject");t.exports=n},{"./baseIsEqual":43,"./isStrictComparable":83,"./toObject":92}],51:[function(e,t){function n(e){return function(t){return null==t?void 0:t[e]}}t.exports=n},{}],52:[function(e,t){var n=e("../utility/identity"),r=e("./metaMap"),i=r?function(e,t){return r.set(e,t),e}:n;t.exports=i},{"../utility/identity":109,"./metaMap":85}],53:[function(e,t){function n(e){return"string"==typeof e?e:null==e?"":e+""}t.exports=n},{}],54:[function(e,t){function n(e,t,n){var o=0,s=e?e.length:o;if("number"==typeof t&&t===t&&a>=s){for(;s>o;){var l=o+s>>>1,u=e[l];(n?t>=u:t>u)?o=l+1:s=l}return s}return r(e,t,i,n)}var r=e("./binaryIndexBy"),i=e("../utility/identity"),o=Math.pow(2,32)-1,a=o>>>1;t.exports=n},{"../utility/identity":109,"./binaryIndexBy":55}],55:[function(e,t){function n(e,t,n,o){t=n(t);for(var s=0,l=e?e.length:0,u=t!==t,c="undefined"==typeof t;l>s;){var p=r((s+l)/2),h=n(e[p]),f=h===h;if(u)var d=f||o;else d=c?f&&(o||"undefined"!=typeof h):o?t>=h:t>h;d?s=p+1:l=p}return i(l,a)}var r=Math.floor,i=Math.min,o=Math.pow(2,32)-1,a=o-1;t.exports=n},{}],56:[function(e,t){function n(e,t,n){if("function"!=typeof e)return r;if("undefined"==typeof t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 3:return function(n,r,i){return e.call(t,n,r,i)};case 4:return function(n,r,i,o){return e.call(t,n,r,i,o)};case 5:return function(n,r,i,o,a){return e.call(t,n,r,i,o,a)}}return function(){return e.apply(t,arguments)}}var r=e("../utility/identity");t.exports=n},{"../utility/identity":109}],57:[function(e,t){(function(n){function r(e){return s.call(e,0)}var i=e("../utility/constant"),o=e("../lang/isNative"),a=o(a=n.ArrayBuffer)&&a,s=o(s=a&&new a(0).slice)&&s,l=Math.floor,u=o(u=n.Uint8Array)&&u,c=function(){try{var e=o(e=n.Float64Array)&&e,t=new e(new a(10),0,1)&&e}catch(r){}return t}(),p=c?c.BYTES_PER_ELEMENT:0;s||(r=a&&u?function(e){var t=e.byteLength,n=c?l(t/p):0,r=n*p,i=new a(t);if(n){var o=new c(i,0,n);o.set(new c(e,0,n))}return t!=r&&(o=new u(i,r),o.set(new u(e,r))),i}:i(null)),t.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../lang/isNative":98,"../utility/constant":108}],58:[function(e,t){function n(e,t,n){for(var i=n.length,o=-1,a=r(e.length-i,0),s=-1,l=t.length,u=Array(a+l);++s<l;)u[s]=t[s];for(;++o<i;)u[n[o]]=e[o];for(;a--;)u[s++]=e[o++];return u}var r=Math.max;t.exports=n},{}],59:[function(e,t){function n(e,t,n){for(var i=-1,o=n.length,a=-1,s=r(e.length-o,0),l=-1,u=t.length,c=Array(s+u);++a<s;)c[a]=e[a];for(var p=a;++l<u;)c[p+l]=t[l];for(;++i<o;)c[p+n[i]]=e[a++];return c}var r=Math.max;t.exports=n},{}],60:[function(e,t){function n(e,t){return function(n,o){var a=n?n.length:0;if(!r(a))return e(n,o);for(var s=t?a:-1,l=i(n);(t?s--:++s<a)&&o(l[s],s,l)!==!1;);return n}}var r=e("./isLength"),i=e("./toObject");t.exports=n},{"./isLength":81,"./toObject":92}],61:[function(e,t){function n(e){return function(t,n,i){for(var o=r(t),a=i(t),s=a.length,l=e?s:-1;e?l--:++l<s;){var u=a[l];if(n(o[u],u,o)===!1)break}return t}}var r=e("./toObject");t.exports=n},{"./toObject":92}],62:[function(e,t){(function(n){function r(e,t){function r(){var i=this&&this!==n&&this instanceof r?o:e;return i.apply(t,arguments)}var o=i(e);return r}var i=e("./createCtorWrapper");t.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./createCtorWrapper":63}],63:[function(e,t){function n(e){return function(){var t=r(e.prototype),n=e.apply(t,arguments);return i(n)?n:t}}var r=e("./baseCreate"),i=e("../lang/isObject");t.exports=n},{"../lang/isObject":99,"./baseCreate":35}],64:[function(e,t){function n(e,t){return function(n,s,l){if(s=r(s,l,3),a(n)){var u=o(n,s,t);return u>-1?n[u]:void 0}return i(n,s,e)}}var r=e("./baseCallback"),i=e("./baseFind"),o=e("./baseFindIndex"),a=e("../lang/isArray");t.exports=n},{"../lang/isArray":96,"./baseCallback":32,"./baseFind":37,"./baseFindIndex":38}],65:[function(e,t){function n(e,t){return function(n,o,a){return"function"==typeof o&&"undefined"==typeof a&&i(n)?e(n,o):t(n,r(o,a,3))}}var r=e("./bindCallback"),i=e("../lang/isArray");t.exports=n},{"../lang/isArray":96,"./bindCallback":56}],66:[function(e,t){(function(n){function r(e,t,x,A,E,j,C,T,S,_){function O(){for(var d=arguments.length,m=d,g=Array(d);m--;)g[m]=arguments[m];if(A&&(g=o(g,A,E)),j&&(g=a(g,j,C)),L||P){var b=O.placeholder,M=c(g,b);if(d-=M.length,_>d){var U=T?i(T):null,H=w(_-d,0),F=L?M:null,B=L?null:M,q=L?g:null,z=L?null:g;t|=L?y:v,t&=~(L?v:y),N||(t&=~(h|f));var V=[e,t,x,q,F,z,B,U,S,H],W=r.apply(void 0,V);return l(e)&&p(W,V),W.placeholder=b,W}}var J=$?x:this;D&&(e=J[R]),T&&(g=u(g,T)),k&&S<g.length&&(g.length=S);var Y=this&&this!==n&&this instanceof O?I||s(e):e;return Y.apply(J,g)}var k=t&b,$=t&h,D=t&f,L=t&m,N=t&d,P=t&g,I=!D&&s(e),R=e;return O}var i=e("./arrayCopy"),o=e("./composeArgs"),a=e("./composeArgsRight"),s=e("./createCtorWrapper"),l=e("./isLaziable"),u=e("./reorder"),c=e("./replaceHolders"),p=e("./setData"),h=1,f=2,d=4,m=8,g=16,y=32,v=64,b=128,w=Math.max;t.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./arrayCopy":29,"./composeArgs":58,"./composeArgsRight":59,"./createCtorWrapper":63,"./isLaziable":80,"./reorder":87,"./replaceHolders":88,"./setData":89}],67:[function(e,t){(function(n){function r(e,t,r,a){function s(){for(var t=-1,i=arguments.length,o=-1,c=a.length,p=Array(i+c);++o<c;)p[o]=a[o];for(;i--;)p[o++]=arguments[++t];var h=this&&this!==n&&this instanceof s?u:e;return h.apply(l?r:this,p)}var l=t&o,u=i(e);return s}var i=e("./createCtorWrapper"),o=1;t.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./createCtorWrapper":63}],68:[function(e,t){function n(e,t,n,g,y,v,b,w){var x=t&p;if(!x&&"function"!=typeof e)throw new TypeError(d);var A=g?g.length:0;if(A||(t&=~(h|f),g=y=null),A-=y?y.length:0,t&f){var E=g,j=y;g=y=null}var C=x?null:s(e),T=[e,t,n,g,y,E,j,v,b,w];if(C&&(l(T,C),t=T[1],w=T[9]),T[9]=null==w?x?0:e.length:m(w-A,0)||0,t==c)var S=i(T[0],T[2]);else S=t!=h&&t!=(c|h)||T[4].length?o.apply(void 0,T):a.apply(void 0,T);var _=C?r:u;return _(S,T)}var r=e("./baseSetData"),i=e("./createBindWrapper"),o=e("./createHybridWrapper"),a=e("./createPartialWrapper"),s=e("./getData"),l=e("./mergeData"),u=e("./setData"),c=1,p=2,h=32,f=64,d="Expected a function",m=Math.max;t.exports=n},{"./baseSetData":52,"./createBindWrapper":62,"./createHybridWrapper":66,"./createPartialWrapper":67,"./getData":72,"./mergeData":84,"./setData":89}],69:[function(e,t){function n(e,t,n,r,i,o,a){var s=-1,l=e.length,u=t.length,c=!0;if(l!=u&&!(i&&u>l))return!1;for(;c&&++s<l;){var p=e[s],h=t[s];if(c=void 0,r&&(c=i?r(h,p,s):r(p,h,s)),"undefined"==typeof c)if(i)for(var f=u;f--&&(h=t[f],!(c=p&&p===h||n(p,h,r,i,o,a))););else c=p&&p===h||n(p,h,r,i,o,a)}return!!c}t.exports=n},{}],70:[function(e,t){function n(e,t,n){switch(n){case r:case i:return+e==+t;case o:return e.name==t.name&&e.message==t.message;case a:return e!=+e?t!=+t:0==e?1/e==1/t:e==+t;case s:case l:return e==t+""}return!1}var r="[object Boolean]",i="[object Date]",o="[object Error]",a="[object Number]",s="[object RegExp]",l="[object String]";t.exports=n},{}],71:[function(e,t){function n(e,t,n,i,a,s,l){var u=r(e),c=u.length,p=r(t),h=p.length;if(c!=h&&!a)return!1;for(var f=a,d=-1;++d<c;){var m=u[d],g=a?m in t:o.call(t,m);if(g){var y=e[m],v=t[m];g=void 0,i&&(g=a?i(v,y,m):i(y,v,m)),"undefined"==typeof g&&(g=y&&y===v||n(y,v,i,a,s,l))}if(!g)return!1;f||(f="constructor"==m)}if(!f){var b=e.constructor,w=t.constructor;if(b!=w&&"constructor"in e&&"constructor"in t&&!("function"==typeof b&&b instanceof b&&"function"==typeof w&&w instanceof w))return!1}return!0}var r=e("../object/keys"),i=Object.prototype,o=i.hasOwnProperty;t.exports=n},{"../object/keys":104}],72:[function(e,t){var n=e("./metaMap"),r=e("../utility/noop"),i=n?function(e){return n.get(e)}:r;t.exports=i},{"../utility/noop":110,"./metaMap":85}],73:[function(e,t){var n=e("./baseProperty"),r=e("../utility/constant"),i=e("./realNames"),o=e("../support"),a=function(){return o.funcNames?"constant"==r.name?n("name"):function(e){for(var t=e.name,n=i[t],r=n?n.length:0;r--;){var o=n[r],a=o.func;if(null==a||a==e)return o.name}return t}:r("")}();t.exports=a},{"../support":107,"../utility/constant":108,"./baseProperty":51,"./realNames":86}],74:[function(e,t){function n(e,t,n){for(var r=e.length,i=t+(n?0:-1);n?i--:++i<r;){var o=e[i];if(o!==o)return i}return-1}t.exports=n},{}],75:[function(e,t){function n(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&i.call(e,"index")&&(n.index=e.index,n.input=e.input),n}var r=Object.prototype,i=r.hasOwnProperty;t.exports=n},{}],76:[function(e,t){(function(n){function r(e,t,n){var r=e.constructor;switch(t){case c:return i(e);case o:case a:return new r(+e);case p:case h:case f:case d:case m:case g:case y:case v:case b:r instanceof r&&(r=x[t]);var A=e.buffer;return new r(n?i(A):A,e.byteOffset,e.length);case s:case u:return new r(e);case l:var E=new r(e.source,w.exec(e));E.lastIndex=e.lastIndex}return E}var i=e("./bufferClone"),o="[object Boolean]",a="[object Date]",s="[object Number]",l="[object RegExp]",u="[object String]",c="[object ArrayBuffer]",p="[object Float32Array]",h="[object Float64Array]",f="[object Int8Array]",d="[object Int16Array]",m="[object Int32Array]",g="[object Uint8Array]",y="[object Uint8ClampedArray]",v="[object Uint16Array]",b="[object Uint32Array]",w=/\w*$/,x={};x[p]=n.Float32Array,x[h]=n.Float64Array,x[f]=n.Int8Array,x[d]=n.Int16Array,x[m]=n.Int32Array,x[g]=n.Uint8Array,x[y]=n.Uint8ClampedArray,x[v]=n.Uint16Array,x[b]=n.Uint32Array,t.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./bufferClone":57}],77:[function(e,t){function n(e){var t=e.constructor;return"function"==typeof t&&t instanceof t||(t=Object),new t}t.exports=n},{}],78:[function(e,t){var n=function(){try{Object({toString:0}+"")}catch(e){return function(){return!1}}return function(e){return"function"!=typeof e.toString&&"string"==typeof(e+"")}}();t.exports=n},{}],79:[function(e,t){function n(e,t){return e=+e,t=null==t?r:t,e>-1&&e%1==0&&t>e}var r=Math.pow(2,53)-1;t.exports=n},{}],80:[function(e,t){function n(e){var t=i(e);return!!t&&e===o[t]&&t in r.prototype}var r=e("./LazyWrapper"),i=e("./getFuncName"),o=e("../chain/lodash");t.exports=n},{"../chain/lodash":20,"./LazyWrapper":27,"./getFuncName":73}],81:[function(e,t){function n(e){return"number"==typeof e&&e>-1&&e%1==0&&r>=e}var r=Math.pow(2,53)-1;t.exports=n},{}],82:[function(e,t){function n(e){return!!e&&"object"==typeof e}t.exports=n},{}],83:[function(e,t){function n(e){return e===e&&(0===e?1/e>0:!r(e))}var r=e("../lang/isObject");t.exports=n},{"../lang/isObject":99}],84:[function(e,t){function n(e,t){var n=e[1],d=t[1],m=n|d,g=c>m,y=d==c&&n==u||d==c&&n==p&&e[7].length<=t[8]||d==(c|p)&&n==u;if(!g&&!y)return e;d&s&&(e[2]=t[2],m|=n&s?0:l);var v=t[3];if(v){var b=e[3];e[3]=b?i(b,v,t[4]):r(v),e[4]=b?a(e[3],h):r(t[4])}return v=t[5],v&&(b=e[5],e[5]=b?o(b,v,t[6]):r(v),e[6]=b?a(e[5],h):r(t[6])),v=t[7],v&&(e[7]=r(v)),d&c&&(e[8]=null==e[8]?t[8]:f(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=m,e}var r=e("./arrayCopy"),i=e("./composeArgs"),o=e("./composeArgsRight"),a=e("./replaceHolders"),s=1,l=4,u=8,c=128,p=256,h="__lodash_placeholder__",f=Math.min;t.exports=n},{"./arrayCopy":29,"./composeArgs":58,"./composeArgsRight":59,"./replaceHolders":88}],85:[function(e,t){(function(n){var r=e("../lang/isNative"),i=r(i=n.WeakMap)&&i,o=i&&new i;t.exports=o}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../lang/isNative":98}],86:[function(e,t){var n={};t.exports=n},{}],87:[function(e,t){function n(e,t){for(var n=e.length,a=o(t.length,n),s=r(e);a--;){var l=t[a];e[a]=i(l,n)?s[l]:void 0}return e}var r=e("./arrayCopy"),i=e("./isIndex"),o=Math.min;t.exports=n},{"./arrayCopy":29,"./isIndex":79}],88:[function(e,t){function n(e,t){for(var n=-1,i=e.length,o=-1,a=[];++n<i;)e[n]===t&&(e[n]=r,a[++o]=n);return a}var r="__lodash_placeholder__";t.exports=n},{}],89:[function(e,t){var n=e("./baseSetData"),r=e("../date/now"),i=150,o=16,a=function(){var e=0,t=0;return function(a,s){var l=r(),u=o-(l-t);if(t=l,u>0){if(++e>=i)return a}else e=0;return n(a,s)}}();t.exports=a},{"../date/now":24,"./baseSetData":52}],90:[function(e,t){function n(e){var t;if(!a(e)||p.call(e)!=l||o(e)||!c.call(e,"constructor")&&(t=e.constructor,"function"==typeof t&&!(t instanceof t))||!s.argsTag&&i(e))return!1;var n;return s.ownLast?(r(e,function(e,t,r){return n=c.call(r,t),!1}),n!==!1):(r(e,function(e,t){n=t}),"undefined"==typeof n||c.call(e,n))}var r=e("./baseForIn"),i=e("../lang/isArguments"),o=e("./isHostObject"),a=e("./isObjectLike"),s=e("../support"),l="[object Object]",u=Object.prototype,c=u.hasOwnProperty,p=u.toString;t.exports=n},{"../lang/isArguments":95,"../support":107,"./baseForIn":40,"./isHostObject":78,"./isObjectLike":82}],91:[function(e,t){function n(e){for(var t=l(e),n=t.length,c=n&&e.length,h=c&&a(c)&&(i(e)||u.nonEnumStrings&&s(e)||u.nonEnumArgs&&r(e)),f=-1,d=[];++f<n;){var m=t[f];(h&&o(m,c)||p.call(e,m))&&d.push(m)}return d}var r=e("../lang/isArguments"),i=e("../lang/isArray"),o=e("./isIndex"),a=e("./isLength"),s=e("../lang/isString"),l=e("../object/keysIn"),u=e("../support"),c=Object.prototype,p=c.hasOwnProperty;t.exports=n},{"../lang/isArguments":95,"../lang/isArray":96,"../lang/isString":101,"../object/keysIn":105,"../support":107,"./isIndex":79,"./isLength":81}],92:[function(e,t){function n(e){if(o.unindexedChars&&i(e)){for(var t=-1,n=e.length,a=Object(e);++t<n;)a[t]=e.charAt(t);return a}return r(e)?e:Object(e)}var r=e("../lang/isObject"),i=e("../lang/isString"),o=e("../support");t.exports=n},{"../lang/isObject":99,"../lang/isString":101,"../support":107}],93:[function(e,t){function n(e){return e instanceof r?e.clone():new i(e.__wrapped__,e.__chain__,o(e.__actions__))}var r=e("./LazyWrapper"),i=e("./LodashWrapper"),o=e("./arrayCopy");t.exports=n},{"./LazyWrapper":27,"./LodashWrapper":28,"./arrayCopy":29}],94:[function(e,t){function n(e,t,n){return t="function"==typeof t&&i(t,n,1),r(e,!0,t)}var r=e("../internal/baseClone"),i=e("../internal/bindCallback");t.exports=n},{"../internal/baseClone":33,"../internal/bindCallback":56}],95:[function(e,t){function n(e){var t=i(e)?e.length:void 0;return r(t)&&u.call(e)==a}var r=e("../internal/isLength"),i=e("../internal/isObjectLike"),o=e("../support"),a="[object Arguments]",s=Object.prototype,l=s.hasOwnProperty,u=s.toString,c=s.propertyIsEnumerable;o.argsTag||(n=function(e){var t=i(e)?e.length:void 0;return r(t)&&l.call(e,"callee")&&!c.call(e,"callee")}),t.exports=n},{"../internal/isLength":81,"../internal/isObjectLike":82,"../support":107}],96:[function(e,t){var n=e("../internal/isLength"),r=e("./isNative"),i=e("../internal/isObjectLike"),o="[object Array]",a=Object.prototype,s=a.toString,l=r(l=Array.isArray)&&l,u=l||function(e){return i(e)&&n(e.length)&&s.call(e)==o};t.exports=u},{"../internal/isLength":81,"../internal/isObjectLike":82,"./isNative":98}],97:[function(e,t){(function(n){var r=e("../internal/baseIsFunction"),i=e("./isNative"),o="[object Function]",a=Object.prototype,s=a.toString,l=i(l=n.Uint8Array)&&l,u=r(/x/)||l&&!r(l)?function(e){return s.call(e)==o}:r;t.exports=u}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"../internal/baseIsFunction":45,"./isNative":98}],98:[function(e,t){function n(e){return null==e?!1:c.call(e)==a?p.test(u.call(e)):o(e)&&(i(e)?p:s).test(e)}var r=e("../string/escapeRegExp"),i=e("../internal/isHostObject"),o=e("../internal/isObjectLike"),a="[object Function]",s=/^\[object .+?Constructor\]$/,l=Object.prototype,u=Function.prototype.toString,c=l.toString,p=RegExp("^"+r(c).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=n},{"../internal/isHostObject":78,"../internal/isObjectLike":82,"../string/escapeRegExp":106}],99:[function(e,t){function n(e){var t=typeof e;return"function"==t||!!e&&"object"==t}t.exports=n},{}],100:[function(e,t){var n=e("./isArguments"),r=e("./isNative"),i=e("../internal/shimIsPlainObject"),o=e("../support"),a="[object Object]",s=Object.prototype,l=s.toString,u=r(u=Object.getPrototypeOf)&&u,c=u?function(e){if(!e||l.call(e)!=a||!o.argsTag&&n(e))return!1;var t=e.valueOf,s=r(t)&&(s=u(t))&&u(s);return s?e==s||u(e)==s:i(e)}:i;t.exports=c},{"../internal/shimIsPlainObject":90,"../support":107,"./isArguments":95,"./isNative":98}],101:[function(e,t){function n(e){return"string"==typeof e||r(e)&&a.call(e)==i}var r=e("../internal/isObjectLike"),i="[object String]",o=Object.prototype,a=o.toString;t.exports=n},{"../internal/isObjectLike":82}],102:[function(e,t){function n(e){return i(e)&&r(e.length)&&!!_[k.call(e)]}var r=e("../internal/isLength"),i=e("../internal/isObjectLike"),o="[object Arguments]",a="[object Array]",s="[object Boolean]",l="[object Date]",u="[object Error]",c="[object Function]",p="[object Map]",h="[object Number]",f="[object Object]",d="[object RegExp]",m="[object Set]",g="[object String]",y="[object WeakMap]",v="[object ArrayBuffer]",b="[object Float32Array]",w="[object Float64Array]",x="[object Int8Array]",A="[object Int16Array]",E="[object Int32Array]",j="[object Uint8Array]",C="[object Uint8ClampedArray]",T="[object Uint16Array]",S="[object Uint32Array]",_={};_[b]=_[w]=_[x]=_[A]=_[E]=_[j]=_[C]=_[T]=_[S]=!0,_[o]=_[a]=_[v]=_[s]=_[l]=_[u]=_[c]=_[p]=_[h]=_[f]=_[d]=_[m]=_[g]=_[y]=!1;var O=Object.prototype,k=O.toString;t.exports=n},{"../internal/isLength":81,"../internal/isObjectLike":82}],103:[function(e,t){function n(e){return"undefined"==typeof e}t.exports=n},{}],104:[function(e,t){var n=e("../internal/isLength"),r=e("../lang/isNative"),i=e("../lang/isObject"),o=e("../internal/shimKeys"),a=e("../support"),s=r(s=Object.keys)&&s,l=s?function(e){if(e)var t=e.constructor,r=e.length;return"function"==typeof t&&t.prototype===e||("function"==typeof e?a.enumPrototypes:r&&n(r))?o(e):i(e)?s(e):[]}:o;t.exports=l},{"../internal/isLength":81,"../internal/shimKeys":91,"../lang/isNative":98,"../lang/isObject":99,"../support":107}],105:[function(e,t){function n(e){if(null==e)return[];u(e)||(e=Object(e));var t=e.length;t=t&&l(t)&&(o(e)||p.nonEnumStrings&&c(e)||p.nonEnumArgs&&i(e))&&t||0;for(var n=e.constructor,r=-1,h=a(n)&&n.prototype||E,f=h===e,d=Array(t),g=t>0,y=p.enumErrorProps&&(e===A||e instanceof Error),b=p.enumPrototypes&&a(e);++r<t;)d[r]=r+"";for(var _ in e)b&&"prototype"==_||y&&("message"==_||"name"==_)||g&&s(_,t)||"constructor"==_&&(f||!C.call(e,_))||d.push(_);if(p.nonEnumShadows&&e!==E){var O=e===j?w:e===A?m:T.call(e),k=S[O]||S[v];for(O==v&&(h=E),t=x.length;t--;){_=x[t];var $=k[_];f&&$||($?!C.call(e,_):e[_]===h[_])||d.push(_)}}return d}var r=e("../internal/arrayEach"),i=e("../lang/isArguments"),o=e("../lang/isArray"),a=e("../lang/isFunction"),s=e("../internal/isIndex"),l=e("../internal/isLength"),u=e("../lang/isObject"),c=e("../lang/isString"),p=e("../support"),h="[object Array]",f="[object Boolean]",d="[object Date]",m="[object Error]",g="[object Function]",y="[object Number]",v="[object Object]",b="[object RegExp]",w="[object String]",x=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],A=Error.prototype,E=Object.prototype,j=String.prototype,C=E.hasOwnProperty,T=E.toString,S={};S[h]=S[d]=S[y]={constructor:!0,toLocaleString:!0,toString:!0,valueOf:!0},S[f]=S[w]={constructor:!0,toString:!0,valueOf:!0},S[m]=S[g]=S[b]={constructor:!0,toString:!0},S[v]={constructor:!0},r(x,function(e){for(var t in S)if(C.call(S,t)){var n=S[t];n[e]=C.call(n,e)}}),t.exports=n},{"../internal/arrayEach":30,"../internal/isIndex":79,"../internal/isLength":81,"../lang/isArguments":95,"../lang/isArray":96,"../lang/isFunction":97,"../lang/isObject":99,"../lang/isString":101,"../support":107}],106:[function(e,t){function n(e){return e=r(e),e&&o.test(e)?e.replace(i,"\\$&"):e}var r=e("../internal/baseToString"),i=/[.*+?^${}()|[\]\/\\]/g,o=RegExp(i.source);t.exports=n},{"../internal/baseToString":53}],107:[function(e,t){(function(e){var n="[object Arguments]",r="[object Object]",i=Array.prototype,o=Error.prototype,a=Object.prototype,s=(s=e.window)&&s.document,l=a.toString,u=a.propertyIsEnumerable,c=i.splice,p={};!function(){var e=function(){this.x=1},t={0:1,length:1},i=[];e.prototype={valueOf:1,y:1};for(var a in new e)i.push(a);p.argsTag=l.call(arguments)==n,p.enumErrorProps=u.call(o,"message")||u.call(o,"name"),p.enumPrototypes=u.call(e,"prototype"),p.funcDecomp=/\bthis\b/.test(function(){return this}),p.funcNames="string"==typeof Function.name,p.nodeTag=l.call(s)!=r,p.nonEnumStrings=!u.call("x",0),p.nonEnumShadows=!/valueOf/.test(i),p.ownLast="x"!=i[0],p.spliceObjects=(c.call(t,0,1),!t[0]),p.unindexedChars="x"[0]+Object("x")[0]!="xx";try{p.dom=11===s.createDocumentFragment().nodeType}catch(h){p.dom=!1}try{p.nonEnumArgs=!u.call(arguments,1)}catch(h){p.nonEnumArgs=!0}}(0,0),t.exports=p}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],108:[function(e,t){function n(e){return function(){return e}}t.exports=n},{}],109:[function(e,t){function n(e){return e}t.exports=n},{}],110:[function(e,t){function n(){}t.exports=n},{}],111:[function(e,t){function n(){}function r(e){var t={}.toString.call(e);switch(t){case"[object File]":case"[object Blob]":case"[object FormData]":return!0;default:return!1}}function i(){if(g.XMLHttpRequest&&("file:"!=g.location.protocol||!g.ActiveXObject))return new XMLHttpRequest;try{return new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(e){}return!1}function o(e){return e===Object(e)}function a(e){if(!o(e))return e;var t=[];for(var n in e)null!=e[n]&&t.push(encodeURIComponent(n)+"="+encodeURIComponent(e[n]));return t.join("&")}function s(e){for(var t,n,r={},i=e.split("&"),o=0,a=i.length;a>o;++o)n=i[o],t=n.split("="),r[decodeURIComponent(t[0])]=decodeURIComponent(t[1]);return r}function l(e){var t,n,r,i,o=e.split(/\r?\n/),a={};o.pop();for(var s=0,l=o.length;l>s;++s)n=o[s],t=n.indexOf(":"),r=n.slice(0,t).toLowerCase(),i=y(n.slice(t+1)),a[r]=i;return a}function u(e){return e.split(/ *; */).shift()}function c(e){return m(e.split(/ *; */),function(e,t){var n=t.split(/ *= */),r=n.shift(),i=n.shift();return r&&i&&(e[r]=i),e},{})}function p(e,t){t=t||{},this.req=e,this.xhr=this.req.xhr,this.text="HEAD"!=this.req.method?this.xhr.responseText:null,this.setStatusProperties(this.xhr.status),this.header=this.headers=l(this.xhr.getAllResponseHeaders()),this.header["content-type"]=this.xhr.getResponseHeader("content-type"),this.setHeaderProperties(this.header),this.body="HEAD"!=this.req.method?this.parseBody(this.text):null}function h(e,t){var n=this;d.call(this),this._query=this._query||[],this.method=e,this.url=t,this.header={},this._header={},this.on("end",function(){var e=null,t=null;try{t=new p(n)}catch(r){e=new Error("Parser is unable to parse the response"),e.parse=!0,e.original=r}n.callback(e,t)})}function f(e,t){return"function"==typeof t?new h("GET",e).end(t):1==arguments.length?new h("GET",e):new h(e,t)}var d=e("emitter"),m=e("reduce"),g="undefined"==typeof window?this:window,y="".trim?function(e){return e.trim()}:function(e){return e.replace(/(^\s*|\s*$)/g,"")};f.serializeObject=a,f.parseString=s,f.types={html:"text/html",json:"application/json",xml:"application/xml",urlencoded:"application/x-www-form-urlencoded",form:"application/x-www-form-urlencoded","form-data":"application/x-www-form-urlencoded"},f.serialize={"application/x-www-form-urlencoded":a,"application/json":JSON.stringify},f.parse={"application/x-www-form-urlencoded":s,"application/json":JSON.parse},p.prototype.get=function(e){return this.header[e.toLowerCase()]},p.prototype.setHeaderProperties=function(){var e=this.header["content-type"]||"";this.type=u(e);var t=c(e);for(var n in t)this[n]=t[n]},p.prototype.parseBody=function(e){var t=f.parse[this.type];return t&&e&&e.length?t(e):null},p.prototype.setStatusProperties=function(e){var t=e/100|0;this.status=e,this.statusType=t,this.info=1==t,this.ok=2==t,this.clientError=4==t,this.serverError=5==t,this.error=4==t||5==t?this.toError():!1,this.accepted=202==e,this.noContent=204==e||1223==e,this.badRequest=400==e,this.unauthorized=401==e,this.notAcceptable=406==e,this.notFound=404==e,this.forbidden=403==e},p.prototype.toError=function(){var e=this.req,t=e.method,n=e.url,r="cannot "+t+" "+n+" ("+this.status+")",i=new Error(r);return i.status=this.status,i.method=t,i.url=n,i},f.Response=p,d(h.prototype),h.prototype.use=function(e){return e(this),this},h.prototype.timeout=function(e){return this._timeout=e,this},h.prototype.clearTimeout=function(){return this._timeout=0,clearTimeout(this._timer),this},h.prototype.abort=function(){return this.aborted?void 0:(this.aborted=!0,this.xhr.abort(),this.clearTimeout(),this.emit("abort"),this)},h.prototype.set=function(e,t){if(o(e)){for(var n in e)this.set(n,e[n]);return this}return this._header[e.toLowerCase()]=t,this.header[e]=t,this},h.prototype.unset=function(e){return delete this._header[e.toLowerCase()],delete this.header[e],this},h.prototype.getHeader=function(e){return this._header[e.toLowerCase()]},h.prototype.type=function(e){return this.set("Content-Type",f.types[e]||e),this},h.prototype.accept=function(e){return this.set("Accept",f.types[e]||e),this},h.prototype.auth=function(e,t){var n=btoa(e+":"+t);return this.set("Authorization","Basic "+n),this},h.prototype.query=function(e){return"string"!=typeof e&&(e=a(e)),e&&this._query.push(e),this},h.prototype.field=function(e,t){return this._formData||(this._formData=new FormData),this._formData.append(e,t),this},h.prototype.attach=function(e,t,n){return this._formData||(this._formData=new FormData),this._formData.append(e,t,n),this},h.prototype.send=function(e){var t=o(e),n=this.getHeader("Content-Type");if(t&&o(this._data))for(var r in e)this._data[r]=e[r];else"string"==typeof e?(n||this.type("form"),n=this.getHeader("Content-Type"),this._data="application/x-www-form-urlencoded"==n?this._data?this._data+"&"+e:e:(this._data||"")+e):this._data=e;return t?(n||this.type("json"),this):this},h.prototype.callback=function(e,t){var n=this._callback;return this.clearTimeout(),2==n.length?n(e,t):e?this.emit("error",e):void n(t)},h.prototype.crossDomainError=function(){var e=new Error("Origin is not allowed by Access-Control-Allow-Origin");e.crossDomain=!0,this.callback(e)},h.prototype.timeoutError=function(){var e=this._timeout,t=new Error("timeout of "+e+"ms exceeded");t.timeout=e,this.callback(t)},h.prototype.withCredentials=function(){return this._withCredentials=!0,this},h.prototype.end=function(e){var t=this,o=this.xhr=i(),a=this._query.join("&"),s=this._timeout,l=this._formData||this._data;if(this._callback=e||n,o.onreadystatechange=function(){return 4==o.readyState?0==o.status?t.aborted?t.timeoutError():t.crossDomainError():void t.emit("end"):void 0},o.upload&&(o.upload.onprogress=function(e){e.percent=e.loaded/e.total*100,t.emit("progress",e)}),s&&!this._timer&&(this._timer=setTimeout(function(){t.abort()},s)),a&&(a=f.serializeObject(a),this.url+=~this.url.indexOf("?")?"&"+a:"?"+a),o.open(this.method,this.url,!0),this._withCredentials&&(o.withCredentials=!0),"GET"!=this.method&&"HEAD"!=this.method&&"string"!=typeof l&&!r(l)){var u=f.serialize[this.getHeader("Content-Type")];u&&(l=u(l))}for(var c in this.header)null!=this.header[c]&&o.setRequestHeader(c,this.header[c]);return this.emit("request",this),o.send(l),this},f.Request=h,f.get=function(e,t,n){var r=f("GET",e);return"function"==typeof t&&(n=t,t=null),t&&r.query(t),n&&r.end(n),r},f.head=function(e,t,n){var r=f("HEAD",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},f.del=function(e,t){var n=f("DELETE",e);return t&&n.end(t),n},f.patch=function(e,t,n){var r=f("PATCH",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},f.post=function(e,t,n){var r=f("POST",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},f.put=function(e,t,n){var r=f("PUT",e);return"function"==typeof t&&(n=t,t=null),t&&r.send(t),n&&r.end(n),r},t.exports=f},{emitter:112,reduce:113}],112:[function(e,t){function n(e){return e?r(e):void 0}function r(e){for(var t in n.prototype)e[t]=n.prototype[t];return e}t.exports=n,n.prototype.on=n.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks[e]=this._callbacks[e]||[]).push(t),this},n.prototype.once=function(e,t){function n(){r.off(e,n),t.apply(this,arguments)}var r=this;return this._callbacks=this._callbacks||{},n.fn=t,this.on(e,n),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks[e];if(!n)return this;if(1==arguments.length)return delete this._callbacks[e],this;for(var r,i=0;i<n.length;i++)if(r=n[i],r===t||r.fn===t){n.splice(i,1);break}return this},n.prototype.emit=function(e){this._callbacks=this._callbacks||{};var t=[].slice.call(arguments,1),n=this._callbacks[e];if(n){n=n.slice(0);for(var r=0,i=n.length;i>r;++r)n[r].apply(this,t)}return this},n.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks[e]||[]},n.prototype.hasListeners=function(e){return!!this.listeners(e).length}},{}],113:[function(e,t){t.exports=function(e,t,n){for(var r=0,i=e.length,o=3==arguments.length?n:e[r++];i>r;)o=t.call(null,o,e[r],++r,e);return o}},{}]},{},[1])(1)}),window.SwaggerUi=Backbone.Router.extend({dom_id:"swagger_ui",options:null,api:null,headerView:null,mainView:null,initialize:function(e){e=e||{},e.dom_id&&(this.dom_id=e.dom_id,delete e.dom_id),e.supportedSubmitMethods||(e.supportedSubmitMethods=["get","put","post","delete","head","options","patch"]),"string"==typeof e.oauth2RedirectUrl&&(window.oAuthRedirectUrl=e.redirectUrl),$("#"+this.dom_id).length||$("body").append('<div id="'+this.dom_id+'"></div>'),this.options=e,marked.setOptions({gfm:!0});
+var t=this;this.options.success=function(){return t.render()},this.options.progress=function(e){return t.showMessage(e)},this.options.failure=function(e){return t.onLoadFailure(e)},this.headerView=new SwaggerUi.Views.HeaderView({el:$("#header")}),this.headerView.on("update-swagger-ui",function(e){return t.updateSwaggerUi(e)})},setOption:function(e,t){this.options[e]=t},getOption:function(e){return this.options[e]},updateSwaggerUi:function(e){this.options.url=e.url,this.load()},load:function(){this.mainView&&this.mainView.clear();var e=this.options.url;e&&0!==e.indexOf("http")&&(e=this.buildUrl(window.location.href.toString(),e)),this.options.url=e,this.headerView.update(e),this.api=new SwaggerClient(this.options)},collapseAll:function(){Docs.collapseEndpointListForResource("")},listAll:function(){Docs.collapseOperationsForResource("")},expandAll:function(){Docs.expandOperationsForResource("")},render:function(){switch(this.showMessage("Finished Loading Resource Information. Rendering Swagger UI..."),this.mainView=new SwaggerUi.Views.MainView({model:this.api,el:$("#"+this.dom_id),swaggerOptions:this.options,router:this}).render(),this.showMessage(),this.options.docExpansion){case"full":this.expandAll();break;case"list":this.listAll()}this.renderGFM(),this.options.onComplete&&this.options.onComplete(this.api,this),setTimeout(Docs.shebang.bind(this),100)},buildUrl:function(e,t){if(0===t.indexOf("/")){var n=e.split("/");return e=n[0]+"//"+n[2],e+t}var r=e.length;return e.indexOf("?")>-1&&(r=Math.min(r,e.indexOf("?"))),e.indexOf("#")>-1&&(r=Math.min(r,e.indexOf("#"))),e=e.substring(0,r),-1!==e.indexOf("/",e.length-1)?e+t:e+"/"+t},showMessage:function(e){void 0===e&&(e=""),$("#message-bar").removeClass("message-fail"),$("#message-bar").addClass("message-success"),$("#message-bar").html(e)},onLoadFailure:function(e){void 0===e&&(e=""),$("#message-bar").removeClass("message-success"),$("#message-bar").addClass("message-fail");var t=$("#message-bar").html(e);return this.options.onFailure&&this.options.onFailure(e),t},renderGFM:function(){$(".markdown").each(function(){$(this).html(marked($(this).html()))})}}),window.SwaggerUi.Views={},function(){function e(e){"console"in window&&"function"==typeof window.console.warn&&console.warn(e)}window.authorizations={add:function(){if(e("Using window.authorizations is deprecated. Please use SwaggerUi.api.clientAuthorizations.add()."),"undefined"==typeof window.swaggerUi)throw new TypeError("window.swaggerUi is not defined");window.swaggerUi instanceof SwaggerUi&&window.swaggerUi.api.clientAuthorizations.add.apply(window.swaggerUi.api.clientAuthorizations,arguments)}},window.ApiKeyAuthorization=function(){e("window.ApiKeyAuthorization is deprecated. Please use SwaggerClient.ApiKeyAuthorization."),SwaggerClient.ApiKeyAuthorization.apply(window,arguments)},window.PasswordAuthorization=function(){e("window.PasswordAuthorization is deprecated. Please use SwaggerClient.PasswordAuthorization."),SwaggerClient.PasswordAuthorization.apply(window,arguments)}}(),function(e,t){"function"==typeof define&&define.amd?define(["b"],function(n){return e.SwaggerUi=t(n)}):"object"==typeof exports?module.exports=t(require("b")):e.SwaggerUi=t(e.b)}(this,function(){return SwaggerUi}),SwaggerUi.Views.ApiKeyButton=Backbone.View.extend({events:{"click #apikey_button":"toggleApiKeyContainer","click #apply_api_key":"applyApiKey"},initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){var e=this.template();return $(this.el).html(e(this.model)),this},applyApiKey:function(){var e=new SwaggerClient.ApiKeyAuthorization(this.model.name,$("#input_apiKey_entry").val(),this.model["in"]);this.router.api.clientAuthorizations.add(this.model.name,e),this.router.load(),$("#apikey_container").show()},toggleApiKeyContainer:function(){if($("#apikey_container").length){var e=$("#apikey_container").first();e.is(":visible")?e.hide():($(".auth_container").hide(),e.show())}},template:function(){return Handlebars.templates.apikey_button_view}}),SwaggerUi.Views.BasicAuthButton=Backbone.View.extend({initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){var e=this.template();return $(this.el).html(e(this.model)),this},events:{"click #basic_auth_button":"togglePasswordContainer","click #apply_basic_auth":"applyPassword"},applyPassword:function(){var e=$(".input_username").val(),t=$(".input_password").val(),n=new SwaggerClient.PasswordAuthorization("basic",e,t);this.router.api.clientAuthorizations.add(this.model.type,n),this.router.load(),$("#basic_auth_container").hide()},togglePasswordContainer:function(){if($("#basic_auth_container").length){var e=$("#basic_auth_container").show();e.is(":visible")?e.slideUp():($(".auth_container").hide(),e.show())}},template:function(){return Handlebars.templates.basic_auth_button_view}}),SwaggerUi.Views.ContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return $(this.el).html(Handlebars.templates.content_type(this.model)),$("label[for=contentType]",$(this.el)).text("Response Content Type"),this}}),SwaggerUi.Views.HeaderView=Backbone.View.extend({events:{"click #show-pet-store-icon":"showPetStore","click #show-wordnik-dev-icon":"showWordnikDev","click #explore":"showCustom","keyup #input_baseUrl":"showCustomOnKeyup","keyup #input_apiKey":"showCustomOnKeyup"},initialize:function(){},showPetStore:function(){this.trigger("update-swagger-ui",{url:"http://petstore.swagger.io/v2/swagger.json"})},showWordnikDev:function(){this.trigger("update-swagger-ui",{url:"http://api.wordnik.com/v4/resources.json"})},showCustomOnKeyup:function(e){13===e.keyCode&&this.showCustom()},showCustom:function(e){e&&e.preventDefault(),this.trigger("update-swagger-ui",{url:$("#input_baseUrl").val(),apiKey:$("#input_apiKey").val()})},update:function(e,t,n){void 0===n&&(n=!1),$("#input_baseUrl").val(e),n&&this.trigger("update-swagger-ui",{url:e})}}),SwaggerUi.Views.MainView=Backbone.View.extend({apisSorter:{alpha:function(e,t){return e.name.localeCompare(t.name)}},operationsSorters:{alpha:function(e,t){return e.path.localeCompare(t.path)},method:function(e,t){return e.method.localeCompare(t.method)}},initialize:function(e){var t,n,r,i;if(e=e||{},this.router=e.router,e.swaggerOptions.apisSorter&&(t=e.swaggerOptions.apisSorter,n=_.isFunction(t)?t:this.apisSorter[t],_.isFunction(n)&&this.model.apisArray.sort(n)),e.swaggerOptions.operationsSorter&&(t=e.swaggerOptions.operationsSorter,n=_.isFunction(t)?t:this.operationsSorters[t],_.isFunction(n)))for(r in this.model.apisArray)this.model.apisArray[r].operationsArray.sort(n);this.model.auths=[];for(r in this.model.securityDefinitions)i=this.model.securityDefinitions[r],this.model.auths.push({name:r,type:i.type,value:i});"2.0"===this.model.swaggerVersion&&(this.model.validatorUrl="validatorUrl"in e.swaggerOptions?e.swaggerOptions.validatorUrl:this.model.url.indexOf("localhost")>0?null:"http://online.swagger.io/validator")},render:function(){if(this.model.securityDefinitions)for(var e in this.model.securityDefinitions){var t,n=this.model.securityDefinitions[e];"apiKey"===n.type&&0===$("#apikey_button").length&&(t=new SwaggerUi.Views.ApiKeyButton({model:n,router:this.router}).render().el,$(".auth_main_container").append(t)),"basicAuth"===n.type&&0===$("#basic_auth_button").length&&(t=new SwaggerUi.Views.BasicAuthButton({model:n,router:this.router}).render().el,$(".auth_main_container").append(t))}$(this.el).html(Handlebars.templates.main(this.model));for(var r={},i=0,o=0;o<this.model.apisArray.length;o++){for(var a=this.model.apisArray[o],s=a.name;"undefined"!=typeof r[s];)s=s+"_"+i,i+=1;a.id=s,r[s]=a,this.addResource(a,this.model.auths)}return $(".propWrap").hover(function(){$(".optionsWrapper",$(this)).show()},function(){$(".optionsWrapper",$(this)).hide()}),this},addResource:function(e,t){e.id=e.id.replace(/\s/g,"_");var n=new SwaggerUi.Views.ResourceView({model:e,router:this.router,tagName:"li",id:"resource_"+e.id,className:"resource",auths:t,swaggerOptions:this.options.swaggerOptions});$("#resources").append(n.render().el)},clear:function(){$(this.el).html("")}}),SwaggerUi.Views.OperationView=Backbone.View.extend({invocationUrl:null,events:{"submit .sandbox":"submitOperation","click .submit":"submitOperation","click .response_hider":"hideResponse","click .toggleOperation":"toggleOperationContent","mouseenter .api-ic":"mouseEnter","mouseout .api-ic":"mouseExit"},initialize:function(e){return e=e||{},this.router=e.router,this.auths=e.auths,this.parentId=this.model.parentId,this.nickname=this.model.nickname,this.model.encodedParentId=encodeURIComponent(this.parentId),this},mouseEnter:function(e){var t=$(this.el).find(".content"),n=e.pageX,r=e.pageY,i=$(window).scrollLeft(),o=$(window).scrollTop(),a=i+$(window).width(),s=o+$(window).height(),l=t.width(),u=t.height();n+l>a&&(n=a-l),i>n&&(n=i),r+u>s&&(r=s-u),o>r&&(r=o);var c={};c.top=r,c.left=n,t.css(c),$(e.currentTarget.parentNode).find("#api_information_panel").show()},mouseExit:function(e){$(e.currentTarget.parentNode).find("#api_information_panel").hide()},render:function(){var e,t,n,r,i,o,a,s,l,u,c,p,h,f,d,m,g,y,v,b,w,x,A,E,j,C,T,S,_,O,k,D,L,N,P,I,R,M;if(o=jQuery.inArray(this.model.method,this.model.supportedSubmitMethods())>=0,o||(this.model.isReadOnly=!0),this.model.description=this.model.description||this.model.notes,this.model.oauth=null,m=this.model.authorizations||this.model.security)if(Array.isArray(m))for(l=0,u=m.length;u>l;l++){n=m[l];for(s in n){t=n[s];for(e in this.auths)if(t=this.auths[e],"oauth2"===t.type){this.model.oauth={},this.model.oauth.scopes=[],A=t.value.scopes;for(a in A)R=A[a],D=n[s].indexOf(a),D>=0&&(y={scope:a,description:R},this.model.oauth.scopes.push(y))}}}else for(a in m)if(R=m[a],"oauth2"===a)for(null===this.model.oauth&&(this.model.oauth={}),void 0===this.model.oauth.scopes&&(this.model.oauth.scopes=[]),d=0,c=R.length;c>d;d++)y=R[d],this.model.oauth.scopes.push(y);if("undefined"!=typeof this.model.responses){this.model.responseMessages=[],E=this.model.responses;for(r in E)M=E[r],O=null,k=this.model.responses[r].schema,k&&k.$ref&&(O=k.$ref,0===O.indexOf("#/definitions/")&&(O=O.substring("#/definitions/".length))),this.model.responseMessages.push({code:r,message:M.description,responseModel:O})}if("undefined"==typeof this.model.responseMessages&&(this.model.responseMessages=[]),L=null,this.model.successResponse){P=this.model.successResponse;for(s in P)M=P[s],this.model.successCode=s,"object"==typeof M&&"function"==typeof M.createJSONSample&&(L={sampleJSON:JSON.stringify(M.createJSONSample(),void 0,2),isParam:!1,signature:M.getMockSignature()})}else this.model.responseClassSignature&&"string"!==this.model.responseClassSignature&&(L={sampleJSON:this.model.responseSampleJSON,isParam:!1,signature:this.model.responseClassSignature});for($(this.el).html(Handlebars.templates.operation(this.model)),L?(_=new SwaggerUi.Views.SignatureView({model:L,router:this.router,tagName:"div"}),$(".model-signature",$(this.el)).append(_.render().el)):(this.model.responseClassSignature="string",$(".model-signature",$(this.el)).html(this.model.type)),i={isParam:!1},i.consumes=this.model.consumes,i.produces=this.model.produces,j=this.model.parameters,g=0,p=j.length;p>g;g++)b=j[g],I=b.type||b.dataType||"","undefined"==typeof I&&(O=b.schema,O&&O.$ref&&(x=O.$ref,I=0===x.indexOf("#/definitions/")?x.substring("#/definitions/".length):x)),I&&"file"===I.toLowerCase()&&(i.consumes||(i.consumes="multipart/form-data")),b.type=I;for(S=new SwaggerUi.Views.ResponseContentTypeView({model:i,router:this.router}),$(".response-content-type",$(this.el)).append(S.render().el),C=this.model.parameters,v=0,h=C.length;h>v;v++)b=C[v],this.addParameter(b,i.consumes);for(T=this.model.responseMessages,w=0,f=T.length;f>w;w++)N=T[w],this.addStatusCode(N);return this},addParameter:function(e,t){e.consumes=t;var n=new SwaggerUi.Views.ParameterView({model:e,tagName:"tr",readOnly:this.model.isReadOnly});$(".operation-params",$(this.el)).append(n.render().el)},addStatusCode:function(e){var t=new SwaggerUi.Views.StatusCodeView({model:e,tagName:"tr",router:this.router});$(".operation-status",$(this.el)).append(t.render().el)},submitOperation:function(e){var t,n,r,i,o,a,s,l,u,c,p,h,f,d,m,g;if(null!==e&&e.preventDefault(),n=$(".sandbox",$(this.el)),t=!0,n.find("input.required").each(function(){$(this).removeClass("error"),""===jQuery.trim($(this).val())&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){$(e).focus()}}(this)}),t=!1)}),n.find("textarea.required").each(function(){$(this).removeClass("error"),""===jQuery.trim($(this).val())&&($(this).addClass("error"),$(this).wiggle({callback:function(e){return function(){return $(e).focus()}}(this)}),t=!1)}),t){for(u={},h={parent:this},r=!1,f=n.find("input"),i=0,o=f.length;o>i;i++)p=f[i],null!==p.value&&jQuery.trim(p.value).length>0&&(u[p.name]=p.value),"file"===p.type&&(u[p.name]=p.files[0],r=!0);for(d=n.find("textarea"),l=0,a=d.length;a>l;l++)p=d[l],null!==p.value&&jQuery.trim(p.value).length>0&&(u[p.name]=p.value);for(m=n.find("select"),c=0,s=m.length;s>c;c++)p=m[c],g=this.getSelectedValue(p),null!==g&&jQuery.trim(g).length>0&&(u[p.name]=g);return h.responseContentType=$("div select[name=responseContentType]",$(this.el)).val(),h.requestContentType=$("div select[name=parameterContentType]",$(this.el)).val(),$(".response_throbber",$(this.el)).show(),r?this.handleFileUpload(u,n):this.model["do"](u,h,this.showCompleteStatus,this.showErrorStatus,this)}},success:function(e,t){t.showCompleteStatus(e)},handleFileUpload:function(e,t){var n,r,i,o,a,s,l,u,c,p,h,f,d,m,g,y,v,b,w;for(y=t.serializeArray(),o=0,a=y.length;a>o;o++)h=y[o],null!==h.value&&jQuery.trim(h.value).length>0&&(e[h.name]=h.value);for(n=new FormData,g=0,v=this.model.parameters,c=0,s=v.length;s>c;c++)m=v[c],("form"===m.paramType||"formData"===m["in"])&&"file"!==m.type.toLowerCase()&&void 0!==e[m.name]&&n.append(m.name,e[m.name]);for(i={},b=this.model.parameters,p=0,l=b.length;l>p;p++)m=b[p],"header"===m.paramType&&(i[m.name]=e[m.name]);for(w=t.find('input[type~="file"]'),d=0,u=w.length;u>d;d++)r=w[d],"undefined"!=typeof r.files[0]&&(n.append($(r).attr("name"),r.files[0]),g+=1);return this.invocationUrl=this.model.supportHeaderParams()?(i=this.model.getHeaderParams(e),delete i["Content-Type"],this.model.urlify(e,!1)):this.model.urlify(e,!0),$(".request_url",$(this.el)).html("<pre></pre>"),$(".request_url pre",$(this.el)).text(this.invocationUrl),f={type:this.model.method,url:this.invocationUrl,headers:i,data:n,dataType:"json",contentType:!1,processData:!1,error:function(e){return function(t){return e.showErrorStatus(e.wrap(t),e)}}(this),success:function(e){return function(t){return e.showResponse(t,e)}}(this),complete:function(e){return function(t){return e.showCompleteStatus(e.wrap(t),e)}}(this)},window.authorizations&&window.authorizations.apply(f),jQuery.ajax(f),!1},wrap:function(e){var t,n,r,i,o,a,s;for(r={},n=e.getAllResponseHeaders().split("\r"),o=0,a=n.length;a>o;o++)i=n[o],t=i.match(/^([^:]*?):(.*)$/),t||(t=[]),t.shift(),void 0!==t[0]&&void 0!==t[1]&&(r[t[0].trim()]=t[1].trim());return s={},s.content={},s.content.data=e.responseText,s.headers=r,s.request={},s.request.url=this.invocationUrl,s.status=e.status,s},getSelectedValue:function(e){if(e.multiple){for(var t=[],n=0,r=e.options.length;r>n;n++){var i=e.options[n];i.selected&&t.push(i.value)}return t.length>0?t:null}return e.value},hideResponse:function(e){e&&e.preventDefault(),$(".response",$(this.el)).slideUp(),$(".response_hider",$(this.el)).fadeOut()},showResponse:function(e){var t=JSON.stringify(e,null," ").replace(/\n/g,"<br>");$(".response_body",$(this.el)).html(_.escape(t))},showErrorStatus:function(e,t){t.showStatus(e)},showCompleteStatus:function(e,t){t.showStatus(e)},formatXml:function(e){var t,n,r,i,o,a,s,l,u,c,p,h,f;for(p=/(>)(<)(\/*)/g,f=/[ ]*(.*)[ ]+\n/g,t=/(<.+>)(.+\n)/g,e=e.replace(p,"$1\n$2$3").replace(f,"$1\n").replace(t,"$1\n$2"),c=0,r="",l=e.split("\n"),i=0,a="other",h={"single->single":0,"single->closing":-1,"single->opening":0,"single->other":0,"closing->single":0,"closing->closing":-1,"closing->opening":0,"closing->other":0,"opening->single":1,"opening->closing":0,"opening->opening":1,"opening->other":1,"other->single":0,"other->closing":-1,"other->opening":0,"other->other":0},n=function(e){var t,n,o,s,l,u,c;u={single:Boolean(e.match(/<.+\/>/)),closing:Boolean(e.match(/<\/.+>/)),opening:Boolean(e.match(/<[^!?].*>/))},l=function(){var e;e=[];for(o in u)c=u[o],c&&e.push(o);return e}()[0],l=void 0===l?"other":l,t=a+"->"+l,a=l,s="",i+=h[t],s=function(){var e,t,r;for(r=[],n=e=0,t=i;t>=0?t>e:e>t;n=t>=0?++e:--e)r.push(" ");return r}().join(""),"opening->closing"===t?r=r.substr(0,r.length-1)+e+"\n":r+=s+e+"\n"},o=0,s=l.length;s>o;o++)u=l[o],n(u);return r},showStatus:function(e){var t,n;void 0===e.content?(n=e.data,t=e.url):(n=e.content.data,t=e.request.url);var r=e.headers,i=null;r&&(i=r["Content-Type"]||r["content-type"],i&&(i=i.split(";")[0].trim())),$(".response_body",$(this.el)).removeClass("json"),$(".response_body",$(this.el)).removeClass("xml");var o,a,s=function(e){var t=document.createElement("audio");return!(!t.canPlayType||!t.canPlayType(e).replace(/no/,""))};if(n)if("application/json"===i||/\+json$/.test(i)){var l=null;try{l=JSON.stringify(JSON.parse(n),null," ")}catch(u){l="can't parse JSON. Raw result:\n\n"+n}a=$("<code />").text(l),o=$('<pre class="json" />').append(a)}else if("application/xml"===i||/\+xml$/.test(i))a=$("<code />").text(this.formatXml(n)),o=$('<pre class="xml" />').append(a);else if("text/html"===i)a=$("<code />").html(_.escape(n)),o=$('<pre class="xml" />').append(a);else if(/^image\//.test(i))o=$("<img>").attr("src",t);else if(/^audio\//.test(i)&&s(i))o=$("<audio controls>").append($("<source>").attr("src",t).attr("type",i));else if(r["Content-Disposition"].test(/attachment/)||r["content-disposition"].test(/attachment/)||r["Content-Description"].test(/File Transfer/)||r["content-description"].test(/File Transfer/))if("Blob"in window){var c=i||"text/html",p=new Blob([n],{type:c}),h=document.createElement("a"),f=window.URL.createObjectURL(p),d=e.url.substr(e.url.lastIndexOf("/")+1),m=[c,d,f].join(":");h.setAttribute("href",f),h.setAttribute("download",m),h.innerText="Download "+d,o=$("<div/>").append(h)}else o=$('<pre class="json" />').append("Download headers detected but your browser does not support downloading binary via XHR (Blob).");else r.location||r.Location?window.location=e.url:(a=$("<code />").text(n),o=$('<pre class="json" />').append(a));else a=$("<code />").text("no content"),o=$('<pre class="json" />').append(a);var g=o;$(".request_url",$(this.el)).html("<pre></pre>"),$(".request_url pre",$(this.el)).text(t),$(".response_code",$(this.el)).html("<pre>"+e.status+"</pre>"),$(".response_body",$(this.el)).html(g),$(".response_headers",$(this.el)).html("<pre>"+_.escape(JSON.stringify(e.headers,null," ")).replace(/\n/g,"<br>")+"</pre>"),$(".response",$(this.el)).slideDown(),$(".response_hider",$(this.el)).show(),$(".response_throbber",$(this.el)).hide();var y=$(".response_body",$(this.el))[0],v=this.options.swaggerOptions;return v.highlightSizeThreshold&&e.data.length>v.highlightSizeThreshold?y:hljs.highlightBlock(y)},toggleOperationContent:function(){var e=$("#"+Docs.escapeResourceName(this.parentId+"_"+this.nickname+"_content"));e.is(":visible")?Docs.collapseOperation(e):Docs.expandOperation(e)}}),SwaggerUi.Views.ParameterContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return $(this.el).html(Handlebars.templates.parameter_content_type(this.model)),$("label[for=parameterContentType]",$(this.el)).text("Parameter content type:"),this}}),SwaggerUi.Views.ParameterView=Backbone.View.extend({initialize:function(){Handlebars.registerHelper("isArray",function(e,t){"array"===e.type.toLowerCase()||e.allowMultiple?t.fn(this):t.inverse(this)})},render:function(){var e=this.model.type||this.model.dataType;if("undefined"==typeof e){var t=this.model.schema;if(t&&t.$ref){var n=t.$ref;e=0===n.indexOf("#/definitions/")?n.substring("#/definitions/".length):n}}this.model.type=e,this.model.paramType=this.model["in"]||this.model.paramType,this.model.isBody="body"===this.model.paramType||"body"===this.model["in"],this.model.isFile=e&&"file"===e.toLowerCase(),this.model["default"]=this.model["default"]||this.model.defaultValue,this.model.allowableValues&&(this.model.isList=!0);var r=this.template();$(this.el).html(r(this.model));var i={sampleJSON:this.model.sampleJSON,isParam:!0,signature:this.model.signature};if(this.model.sampleJSON){var o=new SwaggerUi.Views.SignatureView({model:i,tagName:"div"});$(".model-signature",$(this.el)).append(o.render().el)}else $(".model-signature",$(this.el)).html(this.model.signature);var a=!1;this.model.isBody&&(a=!0);var s={isParam:a};if(s.consumes=this.model.consumes,a){var l=new SwaggerUi.Views.ParameterContentTypeView({model:s});$(".parameter-content-type",$(this.el)).append(l.render().el)}else{var u=new SwaggerUi.Views.ResponseContentTypeView({model:s});$(".response-content-type",$(this.el)).append(u.render().el)}return this},template:function(){return this.model.isList?Handlebars.templates.param_list:this.options.readOnly?this.model.required?Handlebars.templates.param_readonly_required:Handlebars.templates.param_readonly:this.model.required?Handlebars.templates.param_required:Handlebars.templates.param}}),SwaggerUi.Views.ResourceView=Backbone.View.extend({initialize:function(e){e=e||{},this.router=e.router,this.auths=e.auths,""===this.model.description&&(this.model.description=null),this.model.description&&(this.model.summary=this.model.description)},render:function(){var e={};$(this.el).html(Handlebars.templates.resource(this.model));for(var t=0;t<this.model.operationsArray.length;t++){for(var n=this.model.operationsArray[t],r=0,i=n.nickname;"undefined"!=typeof e[i];)i=i+"_"+r,r+=1;e[i]=n,n.nickname=i,n.parentId=this.model.id,this.addOperation(n)}return $(".toggleEndpointList",this.el).click(this.callDocs.bind(this,"toggleEndpointListForResource")),$(".collapseResource",this.el).click(this.callDocs.bind(this,"collapseOperationsForResource")),$(".expandResource",this.el).click(this.callDocs.bind(this,"expandOperationsForResource")),this},addOperation:function(e){e.number=this.number;var t=new SwaggerUi.Views.OperationView({model:e,router:this.router,tagName:"li",className:"endpoint",swaggerOptions:this.options.swaggerOptions,auths:this.auths});$(".endpoints",$(this.el)).append(t.render().el),this.number++},callDocs:function(e,t){t.preventDefault(),Docs[e](t.currentTarget.getAttribute("data-id"))}}),SwaggerUi.Views.ResponseContentTypeView=Backbone.View.extend({initialize:function(){},render:function(){return $(this.el).html(Handlebars.templates.response_content_type(this.model)),$("label[for=responseContentType]",$(this.el)).text("Response Content Type"),this}}),SwaggerUi.Views.SignatureView=Backbone.View.extend({events:{"click a.description-link":"switchToDescription","click a.snippet-link":"switchToSnippet","mousedown .snippet":"snippetToTextArea"},initialize:function(){},render:function(){return $(this.el).html(Handlebars.templates.signature(this.model)),this.switchToSnippet(),this.isParam=this.model.isParam,this.isParam&&$(".notice",$(this.el)).text("Click to set as parameter value"),this},switchToDescription:function(e){e&&e.preventDefault(),$(".snippet",$(this.el)).hide(),$(".description",$(this.el)).show(),$(".description-link",$(this.el)).addClass("selected"),$(".snippet-link",$(this.el)).removeClass("selected")},switchToSnippet:function(e){e&&e.preventDefault(),$(".description",$(this.el)).hide(),$(".snippet",$(this.el)).show(),$(".snippet-link",$(this.el)).addClass("selected"),$(".description-link",$(this.el)).removeClass("selected")},snippetToTextArea:function(e){if(this.isParam){e&&e.preventDefault();var t=$("textarea",$(this.el.parentNode.parentNode.parentNode));""===$.trim(t.val())&&t.val(this.model.sampleJSON)}}}),SwaggerUi.Views.StatusCodeView=Backbone.View.extend({initialize:function(e){this.options=e||{},this.router=this.options.router},render:function(){if($(this.el).html(Handlebars.templates.status_code(this.model)),this.router.api.models.hasOwnProperty(this.model.responseModel)){var e={sampleJSON:JSON.stringify(this.router.api.models[this.model.responseModel].createJSONSample(),null,2),isParam:!1,signature:this.router.api.models[this.model.responseModel].getMockSignature()},t=new SwaggerUi.Views.SignatureView({model:e,tagName:"div"});$(".model-signature",this.$el).append(t.render().el)}else $(".model-signature",this.$el).html("");return this}})}).call(this);
diff --git a/catalog-be/src/main/webapp/META-INF/MANIFEST.MF b/catalog-be/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..b00130fad6
--- /dev/null
+++ b/catalog-be/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Implementation-Title: catalog-be
+Implementation-Version: APPLICATION_VERSION
+Implementation-Vendor-Id: org.openecomp.sdc
+Built-By: esofer
+Build-Jdk: 1.7.0_45
+Created-By: Apache Maven 3.2.1
+Archiver-Version: Plexus Archiver
+
diff --git a/catalog-be/src/main/webapp/WEB-INF/jetty-web.xml b/catalog-be/src/main/webapp/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000000..2e287bcc90
--- /dev/null
+++ b/catalog-be/src/main/webapp/WEB-INF/jetty-web.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+ "-//Mort Bay Consulting//DTD Configure//EN"
+ "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Set name="contextPath">/</Set>
+</Configure>
diff --git a/catalog-be/src/main/webapp/WEB-INF/web.xml b/catalog-be/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..814a58a4fe
--- /dev/null
+++ b/catalog-be/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <servlet>
+ <servlet-name>jersey</servlet-name>
+ <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>jersey.config.server.provider.packages</param-name>
+ <param-value>com.wordnik.swagger.jaxrs.json, org.openecomp.sdc.be.servlets</param-value>
+ </init-param>
+ <init-param>
+ <param-name>jersey.config.server.provider.classnames</param-name>
+ <param-value>com.wordnik.swagger.jersey.listing.ApiListingResourceJSON;org.glassfish.jersey.media.multipart.MultiPartFeature,org.openecomp.sdc.be.filters.BeServletFilter, org.openecomp.sdc.be.filters.ComponentsAvailabilityFilter</param-value>
+ <!-- <param-value>com.wordnik.swagger.jersey.listing.ApiListingResourceJSON;org.glassfish.jersey.media.multipart.MultiPartFeature,org.openecomp.sdc.be.filters.BeServletFilter</param-value> -->
+ </init-param>
+
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+
+ <servlet-mapping>
+ <servlet-name>jersey</servlet-name>
+ <url-pattern>/sdc2/rest/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>EsGateway</servlet-name>
+ <servlet-class>org.openecomp.sdc.be.monitoring.EsGateway</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>EsGateway</servlet-name>
+ <url-pattern>/sdc2/esGateway/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>jerseyDistribution</servlet-name>
+ <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>jersey.config.server.provider.packages</param-name>
+ <param-value>org.openecomp.sdc.be.distribution.servlet, org.openecomp.sdc.be.externalapi.servlet</param-value>
+ </init-param>
+ <init-param>
+ <param-name>jersey.config.server.provider.classnames</param-name>
+<!-- <param-value>com.wordnik.swagger.jersey.listing.ApiListingResourceJSON;org.glassfish.jersey.media.multipart.MultiPartFeature,org.openecomp.sdc.be.filters.BeServletFilter,org.openecomp.sdc.be.filters.BasicAuthenticationFilter, org.openecomp.sdc.be.filters.ComponentsAvailabilityFilter</param-value> -->
+
+ <!-- Closed till the feature done and basic authentication defined with M Lando -->
+ <param-value>com.wordnik.swagger.jersey.listing.ApiListingResourceJSON;org.glassfish.jersey.media.multipart.MultiPartFeature,org.openecomp.sdc.be.filters.BeServletFilter,org.openecomp.sdc.be.filters.BasicAuthenticationFilter</param-value>
+ </init-param>
+
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>jerseyDistribution</servlet-name>
+ <url-pattern>/asdc/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>Jersey2Config</servlet-name>
+ <servlet-class>com.wordnik.swagger.jersey.config.JerseyJaxrsConfig</servlet-class>
+ <init-param>
+ <param-name>api.version</param-name>
+ <param-value>1.0.0</param-value>
+ </init-param>
+
+ <init-param>
+ <param-name>swagger.api.basepath</param-name>
+ <param-value>http://behost:8080/sdc2/rest</param-value>
+ </init-param>
+
+ <load-on-startup>2</load-on-startup>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <!-- ECOMP Portal -->
+ <servlet>
+ <servlet-name>ViewStatusMessages</servlet-name>
+ <servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ViewStatusMessages</servlet-name>
+ <url-pattern>/lbClassicStatus</url-pattern>
+ </servlet-mapping>
+
+ <filter>
+ <filter-name>GzipFilter</filter-name>
+ <filter-class>org.eclipse.jetty.servlets.GzipFilter</filter-class>
+ <async-supported>true</async-supported>
+ <init-param>
+ <param-name>methods</param-name>
+ <param-value>GET,POST</param-value>
+ </init-param>
+ <init-param>
+ <param-name>mimeTypes</param-name>
+ <param-value>text/html,text/plain,text/css,application/javascript,application/json</param-value>
+ </init-param>
+ </filter>
+ <filter-mapping>
+ <filter-name>GzipFilter</filter-name>
+ <url-pattern>/sdc2/rest/*</url-pattern>
+ </filter-mapping>
+
+
+ <context-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>classpath:application-context.xml</param-value>
+ </context-param>
+
+ <listener>
+ <listener-class>org.openecomp.sdc.be.listen.BEAppContextListener</listener-class>
+ </listener>
+
+ <listener>
+ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+ </listener>
+
+
+
+
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ </welcome-file-list>
+</web-app>
diff --git a/catalog-be/src/main/webapp/index.html b/catalog-be/src/main/webapp/index.html
new file mode 100644
index 0000000000..4b26987758
--- /dev/null
+++ b/catalog-be/src/main/webapp/index.html
@@ -0,0 +1,7 @@
+<html>
+
+<body>
+ <h1>Hello Catalog Server</h1>
+</body>
+
+</html> \ No newline at end of file
diff --git a/catalog-be/src/main/webapp/index.jsp b/catalog-be/src/main/webapp/index.jsp
new file mode 100644
index 0000000000..c38169bb95
--- /dev/null
+++ b/catalog-be/src/main/webapp/index.jsp
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h2>Hello World!</h2>
+</body>
+</html>
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/AuditingMockManager.java b/catalog-be/src/test/java/org/openecomp/sdc/AuditingMockManager.java
new file mode 100644
index 0000000000..272f0d6a1d
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/AuditingMockManager.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.util.EnumMap;
+
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuditingMockManager implements IAuditingManager {
+
+ private static Logger log = LoggerFactory.getLogger(AuditingMockManager.class.getName());
+
+ public AuditingMockManager(String string) {
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public void auditEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ AuditingActionEnum actionEnum = AuditingActionEnum.getActionByName((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION));
+ log.debug("call was made to auditEvent with event type {}", actionEnum.getName());
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java b/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java
new file mode 100644
index 0000000000..28b60559aa
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/ElementOperationMock.java
@@ -0,0 +1,266 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.Category;
+import org.openecomp.sdc.be.model.PropertyScope;
+import org.openecomp.sdc.be.model.Tag;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.CategoryData;
+
+import fj.data.Either;
+
+public class ElementOperationMock implements IElementOperation {
+
+ CategoryDefinition resourceCategory;
+ CategoryDefinition serviceCategory;
+ CategoryDefinition productCategory;
+
+ Category oldService;
+
+ public ElementOperationMock() {
+ resourceCategory = new CategoryDefinition();
+ resourceCategory.setName("Network Layer 2-3");
+ SubCategoryDefinition subCategoryDefinition = new SubCategoryDefinition();
+ subCategoryDefinition.setName("Router");
+ SubCategoryDefinition subCategoryDefinition1 = new SubCategoryDefinition();
+ subCategoryDefinition1.setName("Gateway");
+
+ resourceCategory.addSubCategory(subCategoryDefinition);
+ resourceCategory.addSubCategory(subCategoryDefinition1);
+
+ serviceCategory = new CategoryDefinition();
+ serviceCategory.setName("Mobility");
+ oldService = new Category();
+ oldService.setName("Mobility");
+
+ productCategory = new CategoryDefinition();
+ productCategory.setName("Network Layer 2-31");
+ SubCategoryDefinition subCategoryDefinition11 = new SubCategoryDefinition();
+ subCategoryDefinition11.setName("Router1");
+ GroupingDefinition group = new GroupingDefinition();
+ group.setName("group1");
+ subCategoryDefinition11.addGrouping(group);
+ productCategory.addSubCategory(subCategoryDefinition11);
+
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllResourceCategories() {
+
+ List<CategoryDefinition> categories = new ArrayList<CategoryDefinition>();
+ categories.add(resourceCategory);
+ return Either.left(categories);
+
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllServiceCategories() {
+
+ List<CategoryDefinition> categories = new ArrayList<CategoryDefinition>();
+ categories.add(serviceCategory);
+ return Either.left(categories);
+
+ }
+
+ /*
+ * @Override public Either<Category, ActionStatus> getCategory(String name) { if (name.equals(resourceCategory.getName())){ return Either.left(resourceCategory); } else { return Either.right(ActionStatus.CATEGORY_NOT_FOUND); } }
+ */
+
+ @Override
+ public Either<List<Tag>, ActionStatus> getAllTags() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<List<PropertyScope>, ActionStatus> getAllPropertyScopes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<List<ArtifactType>, ActionStatus> getAllArtifactTypes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Map<String, Object>, ActionStatus> getAllDeploymentArtifactTypes() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T extends GraphNode> Either<CategoryData, StorageOperationStatus> getCategoryData(String name, NodeTypeEnum type, Class<T> clazz) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Integer, ActionStatus> getDefaultHeatTimeout() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> createCategory(CategoryDefinition category, NodeTypeEnum nodeType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> deleteCategory(NodeTypeEnum nodeType, String categoryId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Boolean, ActionStatus> isCategoryUniqueForType(NodeTypeEnum nodeType, String normalizedName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> createSubCategory(String categoryId, SubCategoryDefinition subCategory, NodeTypeEnum nodeType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllCategories(NodeTypeEnum nodeType, boolean inTransaction) {
+
+ List<CategoryDefinition> categories = new ArrayList<CategoryDefinition>();
+ switch (nodeType) {
+ case ResourceNewCategory:
+ categories.add(resourceCategory);
+ break;
+ case ProductCategory:
+ categories.add(productCategory);
+ break;
+ case ServiceNewCategory:
+ categories.add(serviceCategory);
+ break;
+ default:
+ break;
+ }
+ return Either.left(categories);
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> getCategory(NodeTypeEnum nodeType, String categoryId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> getSubCategoryUniqueForType(NodeTypeEnum nodeType, String normalizedName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Boolean, ActionStatus> isSubCategoryUniqueForCategory(NodeTypeEnum nodeType, String subCategoryNormName, String parentCategoryId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> deleteSubCategory(NodeTypeEnum nodeType, String subCategoryId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<GroupingDefinition, ActionStatus> createGrouping(String subCategoryId, GroupingDefinition grouping, NodeTypeEnum nodeType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<GroupingDefinition, ActionStatus> deleteGrouping(NodeTypeEnum nodeType, String groupingId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> getSubCategory(NodeTypeEnum nodeType, String subCategoryId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Boolean, ActionStatus> isGroupingUniqueForSubCategory(NodeTypeEnum nodeType, String groupingNormName, String parentSubCategoryId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<GroupingDefinition, ActionStatus> getGroupingUniqueForType(NodeTypeEnum nodeType, String groupingNormalizedName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Map<String, String>, ActionStatus> getResourceTypesMap() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T extends GraphNode> Either<org.openecomp.sdc.be.resources.data.category.CategoryData, StorageOperationStatus> getNewCategoryData(String name, NodeTypeEnum type, Class<T> clazz) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllProductCategories() {
+ List<CategoryDefinition> categories = new ArrayList<CategoryDefinition>();
+ categories.add(productCategory);
+ return Either.left(categories);
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> createCategory(CategoryDefinition category, NodeTypeEnum nodeType, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> createSubCategory(String categoryId, SubCategoryDefinition subCategory, NodeTypeEnum nodeType, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/ErrorConfigurationTest.java b/catalog-be/src/test/java/org/openecomp/sdc/ErrorConfigurationTest.java
new file mode 100644
index 0000000000..5b86b9cc69
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/ErrorConfigurationTest.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.sdc.be.config.ErrorConfiguration;
+import org.openecomp.sdc.be.config.ErrorInfo;
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.FileChangeCallback;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ErrorConfigurationTest {
+ ConfigurationSource configurationSource = null;
+ private static Logger log = LoggerFactory.getLogger(ErrorConfigurationTest.class.getName());
+
+ @Before
+ public void setup() {
+
+ ExternalConfiguration.setAppName("catalog-be");
+ ExternalConfiguration.setConfigDir("src/test/resources/config");
+ ExternalConfiguration.listenForChanges();
+
+ configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), ExternalConfiguration.getConfigDir() + File.separator + ExternalConfiguration.getAppName());
+
+ }
+
+ @Test
+ public void testReadConfigurationFile() {
+
+ ConfigurationListener configurationListener = new ConfigurationListener(ErrorConfiguration.class, new FileChangeCallback() {
+
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+ log.debug("In reconfigure of {}", obj);
+ }
+
+ });
+
+ ErrorConfiguration testConfiguration = configurationSource.getAndWatchConfiguration(ErrorConfiguration.class, configurationListener);
+
+ assertTrue(testConfiguration != null);
+ ErrorInfo errorInfo = testConfiguration.getErrorInfo("USER_NOT_FOUND");
+ assertTrue(errorInfo != null);
+ log.debug("{}", testConfiguration);
+ log.debug("{}", errorInfo);
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java b/catalog-be/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java
new file mode 100644
index 0000000000..d19ae9c872
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.io.IOException;
+
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.FileChangeCallback;
+import org.openecomp.sdc.common.impl.ConfigFileChangeListener;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+
+public class TestExternalConfiguration<T extends Object> {
+
+ public static void main(String[] args) throws IOException {
+
+ ExternalConfiguration.setAppName("catalog-server");
+ ExternalConfiguration.setConfigDir("C:\\Users\\esofer\\workspaceLuna\\catalog-server\\src\\test\\resources\\config");
+ ExternalConfiguration.listenForChanges();
+
+ ConfigurationListener configurationListener = new ConfigurationListener(Configuration.class, new FileChangeCallback() {
+
+ @Override
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+
+ ConfigurationSource configurationSource1 = new FSConfigurationSource(new ConfigFileChangeListener(), ExternalConfiguration.getConfigDir());
+ configurationSource1.getAndWatchConfiguration(Configuration.class, configurationListener);
+
+ try {
+ Thread.currentThread().sleep(100 * 1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/ZipUtil.java b/catalog-be/src/test/java/org/openecomp/sdc/ZipUtil.java
new file mode 100644
index 0000000000..a651b77f9a
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/ZipUtil.java
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+
+public class ZipUtil {
+
+ public static void main(String[] args) {
+
+ String zipFileName = "/src/test/resources/config/config.zip";
+
+ zipFileName = "C:\\Git_work\\D2-SDnC\\catalog-be\\src\\test\\resources\\config\\config.zip";
+ zipFileName = "src/test/resources/config/config.zip";
+
+ Path path = Paths.get(zipFileName);
+
+ try {
+ byte[] zipAsBytes = Files.readAllBytes(path);
+ // encode to base
+
+ byte[] decodedMd5 = Base64.encodeBase64(zipAsBytes);
+ String decodedStr = new String(decodedMd5);
+
+ zipAsBytes = Base64.decodeBase64(decodedStr.getBytes());
+
+ // String str = new String(zipAsBytes);
+
+ // readZip(str.getBytes());
+ readZip(zipAsBytes);
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ private static Map<String, byte[]> readZip(byte[] zipAsBytes) {
+
+ Map<String, byte[]> fileNameToByteArray = new HashMap<String, byte[]>();
+
+ byte[] buffer = new byte[1024];
+ ZipInputStream zis = null;
+ try {
+
+ zis = new ZipInputStream(new ByteArrayInputStream(zipAsBytes));
+ // get the zipped file list entry
+ ZipEntry ze = zis.getNextEntry();
+
+ while (ze != null) {
+
+ String fileName = ze.getName();
+
+ if (false == ze.isDirectory()) {
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ os.write(buffer, 0, len);
+ }
+
+ // aClass.outputStreamMethod(os);
+ String aString = new String(os.toByteArray(), "UTF-8");
+
+ fileNameToByteArray.put(fileName, os.toByteArray());
+
+ } finally {
+ if (os != null) {
+ os.close();
+ }
+ }
+ }
+ ze = zis.getNextEntry();
+
+ }
+
+ zis.closeEntry();
+ zis.close();
+
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return null;
+ } finally {
+ if (zis != null) {
+ try {
+ zis.closeEntry();
+ zis.close();
+ } catch (IOException e) {
+ // TODO: add log
+ }
+
+ }
+ }
+
+ return fileNameToByteArray;
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/AuditingManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/AuditingManagerTest.java
new file mode 100644
index 0000000000..dff0e3c729
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/AuditingManagerTest.java
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import static org.mockito.Mockito.when;
+
+import java.util.EnumMap;
+import java.util.UUID;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.impl.AuditingDao;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.util.ThreadLocalsHolder;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+
+public class AuditingManagerTest extends BaseConfDependentTest{
+
+ @InjectMocks
+ private AuditingManager auditingManager = Mockito.spy(AuditingManager.class);
+ public static final AuditingDao auditingDao = Mockito.mock(AuditingDao.class);
+
+ public AuditingManagerTest() {
+ }
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(auditingDao.addRecord(Mockito.anyMap(), Mockito.anyString())).thenReturn(ActionStatus.OK);
+
+ }
+
+ @Test
+ public void testNormalizeEmptyStringValuesAndUuid() {
+ EnumMap<AuditingFieldsKeysEnum, Object> auditingFields = new EnumMap<>(AuditingFieldsKeysEnum.class);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_ACTION, "Create");
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_DESC, null);
+ auditingFields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT, " null ");
+
+ String randomUUID = UUID.randomUUID().toString();
+ ThreadLocalsHolder.setUuid(randomUUID);
+
+ auditingManager.auditEvent(auditingFields);
+ // Checking normalization
+ Assert.assertEquals(auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC), Constants.EMPTY_STRING);
+ Assert.assertEquals(auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT), Constants.EMPTY_STRING);
+ Assert.assertEquals(auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID), randomUUID);
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/BaseConfDependentTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/BaseConfDependentTest.java
new file mode 100644
index 0000000000..ad5471e852
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/BaseConfDependentTest.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import java.io.File;
+
+import org.junit.BeforeClass;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+
+public class BaseConfDependentTest {
+ protected static ConfigurationManager configurationManager;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ ExternalConfiguration.setAppName("catalog-be");
+ ExternalConfiguration.setConfigDir("src/test/resources/config");
+ ExternalConfiguration.listenForChanges();
+
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), ExternalConfiguration.getConfigDir() + File.separator + ExternalConfiguration.getAppName());
+
+ configurationManager = new ConfigurationManager(configurationSource);
+
+ configurationManager.getConfiguration().setTitanInMemoryGraph(true);
+
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ComponentBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ComponentBusinessLogicTest.java
new file mode 100644
index 0000000000..8f3234bc39
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ComponentBusinessLogicTest.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class ComponentBusinessLogicTest {
+ ComponentBusinessLogic businessLogic = new ComponentBusinessLogic() {
+
+ @Override
+ public Either<List<String>, ResponseFormat> deleteMarkedComponents() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ComponentInstanceBusinessLogic getComponentInstanceBL() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<List<ComponentInstance>, ResponseFormat> getComponentInstancesFilteredByPropertiesAndInputs(String componentId, ComponentTypeEnum componentTypeEnum, String userId, String searchText) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ };
+
+ @Test
+ public void testGetRequirementsAndCapabilities() {
+ // businessLogic.getRequirementsAndCapabilities(componentId,
+ // componentTypeEnum);
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/HealthCheckBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/HealthCheckBusinessLogicTest.java
new file mode 100644
index 0000000000..6af338ebcf
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/HealthCheckBusinessLogicTest.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.components.impl.HealthCheckBusinessLogic;
+import org.openecomp.sdc.common.api.HealthCheckInfo;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckComponent;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+public class HealthCheckBusinessLogicTest {
+
+ HealthCheckBusinessLogic healthCheckBusinessLogic = new HealthCheckBusinessLogic();
+
+ @Test
+ public void checkStausUpdated() {
+
+ boolean statusChanged = healthCheckBusinessLogic.anyStatusChanged(null, null);
+ assertFalse("check false", statusChanged);
+
+ List<HealthCheckInfo> checkInfosLeft = new ArrayList<HealthCheckInfo>();
+ List<HealthCheckInfo> checkInfosRight = new ArrayList<HealthCheckInfo>();
+
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, checkInfosRight);
+ assertFalse("check false", statusChanged);
+
+ HealthCheckInfo checkInfoTitanUp = new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.UP, null, null);
+ HealthCheckInfo checkInfoTitanDown = new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.DOWN, null, null);
+
+ HealthCheckInfo checkInfoEsUp = new HealthCheckInfo(HealthCheckComponent.ES, HealthCheckStatus.UP, null, null);
+ HealthCheckInfo checkInfoEsDown = new HealthCheckInfo(HealthCheckComponent.ES, HealthCheckStatus.DOWN, null, null);
+
+ /*
+ * HealthCheckInfo checkInfoUebUp = new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.UP, null, null); HealthCheckInfo checkInfoUebDown = new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.DOWN, null, null);
+ */
+
+ checkInfosLeft.add(checkInfoTitanUp);
+ checkInfosLeft.add(checkInfoEsUp);
+
+ checkInfosRight.add(checkInfoTitanUp);
+ checkInfosRight.add(checkInfoEsUp);
+
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, checkInfosRight);
+ assertFalse("check false", statusChanged);
+
+ checkInfosRight.remove(checkInfoTitanUp);
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, checkInfosRight);
+ assertTrue("check true", statusChanged);
+
+ checkInfosRight.add(checkInfoTitanDown);
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, checkInfosRight);
+ assertTrue("check true", statusChanged);
+
+ checkInfosRight.remove(checkInfoTitanDown);
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, checkInfosRight);
+ assertTrue("check true", statusChanged);
+
+ checkInfosRight.add(checkInfoTitanUp);
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, checkInfosRight);
+ assertFalse("check false", statusChanged);
+
+ statusChanged = healthCheckBusinessLogic.anyStatusChanged(checkInfosLeft, null);
+ assertTrue("check true", statusChanged);
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/PropertyBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/PropertyBusinessLogicTest.java
new file mode 100644
index 0000000000..aea29a6419
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/PropertyBusinessLogicTest.java
@@ -0,0 +1,189 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.components.impl.PropertyBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.EntryData;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+import junit.framework.Assert;
+
+public class PropertyBusinessLogicTest {
+
+ private static Logger log = LoggerFactory.getLogger(PropertyBusinessLogicTest.class.getName());
+ final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ final IPropertyOperation propertyOperation = Mockito.mock(IPropertyOperation.class);
+ final IResourceOperation resourceOperation = Mockito.mock(IResourceOperation.class);
+ WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ UserBusinessLogic mockUserAdmin = Mockito.mock(UserBusinessLogic.class);
+ WebApplicationContext webAppContext = Mockito.mock(WebApplicationContext.class);
+ PropertyBusinessLogic bl = new PropertyBusinessLogic();
+ User user = null;
+ Resource resourceResponse = null;
+ ResourceBusinessLogic blResource = new ResourceBusinessLogic();
+ PropertyBusinessLogic spy = null;
+ String resourceId = "resourceforproperty.0.1";
+
+ public PropertyBusinessLogicTest() {
+
+ }
+
+ @Before
+ public void setup() {
+
+ ExternalConfiguration.setAppName("catalog-be");
+
+ // Init Configuration
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ // User data and management
+ user = new User();
+ user.setUserId("jh003");
+ user.setFirstName("Jimmi");
+ user.setLastName("Hendrix");
+ user.setRole(Role.ADMIN.name());
+
+ Either<User, ActionStatus> eitherGetUser = Either.left(user);
+ when(mockUserAdmin.getUser("jh003", false)).thenReturn(eitherGetUser);
+
+ // Servlet Context attributes
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ when(servletContext.getAttribute(Constants.PROPERTY_OPERATION_MANAGER)).thenReturn(propertyOperation);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(servletContext.getAttribute(Constants.RESOURCE_OPERATION_MANAGER)).thenReturn(resourceOperation);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webAppContext);
+
+ // Resource Operation mock methods
+ // getCount
+ Either<Integer, StorageOperationStatus> eitherCount = Either.left(0);
+ when(resourceOperation.getNumberOfResourcesByName("MyResourceName".toLowerCase())).thenReturn(eitherCount);
+ Either<Integer, StorageOperationStatus> eitherCountExist = Either.left(1);
+ when(resourceOperation.getNumberOfResourcesByName("alreadyExist".toLowerCase())).thenReturn(eitherCountExist);
+ Either<Integer, StorageOperationStatus> eitherCountRoot = Either.left(1);
+ when(resourceOperation.getNumberOfResourcesByName("Root".toLowerCase())).thenReturn(eitherCountRoot);
+
+ Either<Resource, StorageOperationStatus> eitherGetResource = Either.left(createResourceObject(true));
+ when(resourceOperation.getResource(resourceId)).thenReturn(eitherGetResource);
+
+ // // createResource
+ // resourceResponse = createResourceObject(true);
+ // Either<Resource, StorageOperationStatus> eitherCreate =
+ // Either.left(resourceResponse);
+ // when(resourceOperation.createResource(Mockito.any(Resource.class))).thenReturn(eitherCreate);
+
+ // BL object
+ // bl = PropertyBusinessLogic.getInstance(servletContext);
+ // PropertyBusinessLogic spy = PowerMockito.spy(bl);
+ // when(spy, method(PropertyBusinessLogic.class, "getResource",
+ // String.class)).withArguments(resource).thenReturn(true);
+
+ }
+
+ private Resource createResourceObject(boolean afterCreate) {
+ Resource resource = new Resource();
+ resource.setName("MyResourceName");
+ resource.addCategory("Generic", "VoIP");
+ resource.setDescription("My short description");
+ List<String> tgs = new ArrayList<String>();
+ tgs.add("test");
+ resource.setTags(tgs);
+ List<String> template = new ArrayList<String>();
+ template.add("Root");
+ resource.setDerivedFrom(template);
+ resource.setVendorName("Motorola");
+ resource.setVendorRelease("1.0.0");
+ resource.setContactId("yavivi");
+ resource.setIcon("MyIcon.jpg");
+
+ if (afterCreate) {
+ resource.setName(resource.getName().toLowerCase());
+ resource.setVersion("0.1");
+ ;
+ resource.setUniqueId(resourceId);
+ resource.setCreatorUserId(user.getUserId());
+ resource.setCreatorFullName(user.getFirstName() + " " + user.getLastName());
+ }
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ log.debug(gson.toJson(resource));
+ return resource;
+ }
+
+ // @Test
+ public void testHappyScenario() {
+
+ String propertyName = "disk_size";
+ PropertyDefinition newPropertyDefinition = createPropertyObject(propertyName, resourceId);
+ Either<EntryData<String, PropertyDefinition>, ResponseFormat> either = bl.createProperty(resourceId, propertyName, newPropertyDefinition, user.getUserId());
+
+ if (either.isRight()) {
+ Assert.assertFalse(true);
+ }
+ Assert.assertEquals(newPropertyDefinition, either.left().value());
+ }
+
+ private PropertyDefinition createPropertyObject(String propertyName, String resourceId) {
+ PropertyDefinition pd = new PropertyDefinition();
+ List<PropertyConstraint> constraints = new ArrayList<PropertyConstraint>();
+ pd.setConstraints(null);
+ pd.setDefaultValue("100");
+ pd.setDescription("Size of thasdasdasdasde local disk, in Gigabytes (GB), available to applications running on the Compute node");
+ pd.setPassword(false);
+ pd.setRequired(true);
+ pd.setType("Integer");
+ pd.setUniqueId(resourceId + "." + propertyName);
+ return pd;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java
new file mode 100644
index 0000000000..eee5c4dd32
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java
@@ -0,0 +1,369 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.components.impl.ImportUtils;
+import org.openecomp.sdc.be.components.impl.ImportUtilsTest;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterOrEqualConstraint;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.exception.PolicyException;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+
+import fj.data.Either;
+
+public class ResourceImportManagerTest {
+
+ private static ConfigurationManager configurationManager;
+ static ResourceImportManager importManager;
+ static IAuditingManager auditingManager = Mockito.mock(IAuditingManager.class);
+ static ResponseFormatManager responseFormatManager = Mockito.mock(ResponseFormatManager.class);
+ static ResourceBusinessLogic resourceBusinessLogic = Mockito.mock(ResourceBusinessLogic.class);
+ static ResourceOperation resourceOperation = Mockito.mock(ResourceOperation.class);
+ static UserBusinessLogic userAdmin = Mockito.mock(UserBusinessLogic.class);
+ static Logger log = Mockito.spy(Logger.class);
+
+ @BeforeClass
+ public static void beforeClass() throws IOException {
+ importManager = new ResourceImportManager();
+ importManager.setAuditingManager(auditingManager);
+ when(resourceOperation.getLatestByToscaResourceName(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(Either.left(null));
+ importManager.setResponseFormatManager(responseFormatManager);
+ importManager.setResourceBusinessLogic(resourceBusinessLogic);
+ importManager.setResourceOperation(resourceOperation);
+ ResourceImportManager.setLog(log);
+
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+
+ Configuration configuration = new Configuration();
+ configuration.setTitanInMemoryGraph(true);
+ configurationManager.setConfiguration(configuration);
+ }
+
+ @Before
+ public void beforeTest() {
+ Mockito.reset(auditingManager, responseFormatManager, resourceBusinessLogic, userAdmin, log);
+ }
+
+ @Test
+ public void testBasicResourceCreation() throws IOException {
+ UploadResourceInfo resourceMD = createDummyResourceMD();
+
+ User user = new User();
+ user.setUserId(resourceMD.getContactId());
+ user.setRole("ADMIN");
+ user.setFirstName("Jhon");
+ user.setLastName("Doh");
+ Either<User, ActionStatus> eitherUser = Either.left(user);
+
+ when(userAdmin.getUser(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(eitherUser);
+
+ setResourceBusinessLogicMock();
+
+ String jsonContent = ImportUtilsTest.loadFileNameToJsonString("normative-types-new-blockStorage.yml");
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createResource = importManager.importNormativeResource(jsonContent, resourceMD, user, true, true);
+ assertTrue(createResource.isLeft());
+ Resource resource = createResource.left().value().left;
+
+ testSetConstantMetaData(resource);
+ testSetMetaDataFromJson(resource, resourceMD);
+
+ testSetDerivedFrom(resource);
+ testSetProperties(resource);
+
+ Mockito.verify(resourceBusinessLogic, Mockito.times(1)).propagateStateToCertified(Mockito.eq(user), Mockito.eq(resource), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.eq(false), Mockito.eq(true));
+ }
+
+ @Test
+ public void testResourceCreationFailed() throws IOException {
+ UploadResourceInfo resourceMD = createDummyResourceMD();
+ User user = new User();
+ user.setUserId(resourceMD.getContactId());
+ Either<User, ActionStatus> eitherUser = Either.left(user);
+ when(userAdmin.getUser(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(eitherUser);
+ ResponseFormat dummyResponseFormat = createGeneralErrorInfo();
+
+ when(responseFormatManager.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(dummyResponseFormat);
+ setResourceBusinessLogicMock();
+
+ String jsonContent = "this is an invalid yml!";
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createResource = importManager.importNormativeResource(jsonContent, resourceMD, user, true, true);
+ assertTrue(createResource.isRight());
+ ResponseFormat errorInfoFromTest = createResource.right().value();
+ assertTrue(errorInfoFromTest.getStatus().equals(dummyResponseFormat.getStatus()));
+ assertTrue(errorInfoFromTest.getMessageId().equals(dummyResponseFormat.getMessageId()));
+ assertTrue(errorInfoFromTest.getFormattedMessage().equals(dummyResponseFormat.getFormattedMessage()));
+ Mockito.verify(log).debug(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(Exception.class));
+ // Mockito.verify(log).error(Mockito.anyString(), Mockito.anyString(),
+ // Mockito.anyString());
+
+ Mockito.verify(resourceBusinessLogic, Mockito.times(0)).createOrUpdateResourceByImport(Mockito.any(Resource.class), Mockito.eq(user), Mockito.eq(true), Mockito.eq(false), Mockito.eq(true));
+
+ Mockito.verify(resourceBusinessLogic, Mockito.times(0)).propagateStateToCertified(Mockito.eq(user), Mockito.any(Resource.class), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.eq(false), Mockito.eq(true));
+
+ }
+
+ @Test
+ public void testResourceCreationWithCapabilities() throws IOException {
+ UploadResourceInfo resourceMD = createDummyResourceMD();
+ User user = new User();
+ user.setUserId(resourceMD.getContactId());
+ Either<User, ActionStatus> eitherUser = Either.left(user);
+
+ when(userAdmin.getUser(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(eitherUser);
+
+ setResourceBusinessLogicMock();
+
+ String jsonContent = ImportUtilsTest.loadFileNameToJsonString("normative-types-new-webServer.yml");
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createResource = importManager.importNormativeResource(jsonContent, resourceMD, user, true, true);
+ assertTrue(createResource.isLeft());
+ Resource resource = createResource.left().value().left;
+ testSetCapabilities(resource);
+
+ Mockito.verify(resourceBusinessLogic, Mockito.times(1)).propagateStateToCertified(Mockito.eq(user), Mockito.eq(resource), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.eq(false), Mockito.eq(true));
+ Mockito.verify(resourceBusinessLogic, Mockito.times(1)).createOrUpdateResourceByImport(resource, user, true, false, true);
+
+ }
+
+ @Test
+ public void testResourceCreationWithRequirments() throws IOException {
+ UploadResourceInfo resourceMD = createDummyResourceMD();
+ User user = new User();
+ user.setUserId(resourceMD.getContactId());
+ Either<User, ActionStatus> eitherUser = Either.left(user);
+
+ when(userAdmin.getUser(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(eitherUser);
+
+ setResourceBusinessLogicMock();
+
+ String jsonContent = ImportUtilsTest.loadFileNameToJsonString("normative-types-new-port.yml");
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createResource = importManager.importNormativeResource(jsonContent, resourceMD, user, true, true);
+ assertTrue(createResource.isLeft());
+ testSetRequirments(createResource.left().value().left);
+
+ }
+
+ private void setResourceBusinessLogicMock() {
+ when(resourceBusinessLogic.getUserAdmin()).thenReturn(userAdmin);
+ when(resourceBusinessLogic.createOrUpdateResourceByImport(Mockito.any(Resource.class), Mockito.any(User.class), Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyBoolean()))
+ .thenAnswer(new Answer<Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat>>() {
+ public Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return Either.left(new ImmutablePair<Resource, ActionStatus>((Resource) args[0], ActionStatus.CREATED));
+
+ }
+ });
+ when(resourceBusinessLogic.propagateStateToCertified(Mockito.any(User.class), Mockito.any(Resource.class), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.eq(false), Mockito.eq(true)))
+ .thenAnswer(new Answer<Either<Resource, ResponseFormat>>() {
+ public Either<Resource, ResponseFormat> answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return Either.left((Resource) args[1]);
+
+ }
+ });
+ when(resourceBusinessLogic.createResourceByDao(Mockito.any(Resource.class), Mockito.any(User.class), Mockito.any(AuditingActionEnum.class), Mockito.anyBoolean())).thenAnswer(new Answer<Either<Resource, ResponseFormat>>() {
+ public Either<Resource, ResponseFormat> answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return Either.left((Resource) args[0]);
+
+ }
+ });
+ when(resourceBusinessLogic.validateResourceBeforeCreate(Mockito.any(Resource.class), Mockito.any(User.class), Mockito.any(AuditingActionEnum.class), Mockito.eq(false))).thenAnswer(new Answer<Either<Resource, ResponseFormat>>() {
+ public Either<Resource, ResponseFormat> answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ return Either.left((Resource) args[0]);
+
+ }
+ });
+
+ Either<Boolean, ResponseFormat> either = Either.left(true);
+ when(resourceBusinessLogic.validatePropertiesDefaultValues(Mockito.any(Resource.class))).thenReturn(either);
+ }
+
+ public ResponseFormat createGeneralErrorInfo() {
+ ResponseFormat responseFormat = new ResponseFormat(500);
+ responseFormat.setPolicyException(new PolicyException("POL5000", "Error: Internal Server Error. Please try again later", null));
+ return responseFormat;
+ }
+
+ private UploadResourceInfo createDummyResourceMD() {
+ UploadResourceInfo resourceMD = new UploadResourceInfo();
+ resourceMD.setName("tosca.nodes.BlockStorage");
+ resourceMD.addSubCategory("Generic", "Infrastructure");
+ resourceMD.setContactId("ya107f");
+ resourceMD.setResourceIconPath("defaulticon");
+ resourceMD.setTags(Arrays.asList(new String[] { "BlockStorage" }));
+ resourceMD.setDescription("Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.");
+ return resourceMD;
+ }
+
+ private void testSetProperties(Resource resource) {
+ List<PropertyDefinition> propertiesList = resource.getProperties();
+
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+ for (PropertyDefinition propertyDefinition : propertiesList) {
+ properties.put(propertyDefinition.getName(), propertyDefinition);
+ }
+
+ assertTrue(properties.size() == 3);
+ assertTrue(properties.containsKey("size"));
+ PropertyDefinition propertyDefinition = properties.get("size");
+ assertTrue(propertyDefinition.getType().equals("scalar-unit.size"));
+ assertTrue(propertyDefinition.getConstraints().size() == 1);
+ PropertyConstraint propertyConstraint = propertyDefinition.getConstraints().get(0);
+ assertTrue(propertyConstraint instanceof GreaterOrEqualConstraint);
+
+ assertTrue(properties.containsKey("volume_id"));
+ propertyDefinition = properties.get("volume_id");
+ assertTrue(propertyDefinition.getType().equals("string"));
+ assertTrue(propertyDefinition.isRequired() == false);
+
+ assertTrue(properties.containsKey("snapshot_id"));
+ propertyDefinition = properties.get("snapshot_id");
+ assertTrue(propertyDefinition.getType().equals("string"));
+ assertTrue(propertyDefinition.isRequired() == false);
+
+ }
+
+ private void testSetCapabilities(Resource resource) {
+ Map<String, List<CapabilityDefinition>> capabilities = resource.getCapabilities();
+ assertTrue(capabilities.size() == 3);
+ assertTrue(capabilities.containsKey("tosca.capabilities.Endpoint"));
+ List<CapabilityDefinition> capabilityList = capabilities.get("tosca.capabilities.Endpoint");
+ CapabilityDefinition capability = capabilityList.get(0);
+ assertTrue(capability.getType().equals("tosca.capabilities.Endpoint"));
+ assertTrue(capability.getName().equals("data_endpoint"));
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.Endpoint.Admin"));
+ capabilityList = capabilities.get("tosca.capabilities.Endpoint.Admin");
+ capability = capabilityList.get(0);
+ assertTrue(capability.getType().equals("tosca.capabilities.Endpoint.Admin"));
+ assertTrue(capability.getName().equals("admin_endpoint"));
+
+ assertTrue(capabilities.containsKey("tosca.capabilities.Container"));
+ capabilityList = capabilities.get("tosca.capabilities.Container");
+ capability = capabilityList.get(0);
+ assertTrue(capability.getType().equals("tosca.capabilities.Container"));
+ assertTrue(capability.getName().equals("host"));
+
+ List<String> validSourceTypes = capability.getValidSourceTypes();
+ assertTrue(validSourceTypes.size() == 1);
+ assertTrue(validSourceTypes.get(0).equals("tosca.nodes.WebApplication"));
+
+ }
+
+ private void testSetRequirments(Resource resource) {
+ Map<String, List<RequirementDefinition>> requirements = resource.getRequirements();
+ assertTrue(requirements.size() == 2);
+
+ assertTrue(requirements.containsKey("tosca.capabilities.network.Linkable"));
+ List<RequirementDefinition> requirementList = requirements.get("tosca.capabilities.network.Linkable");
+ RequirementDefinition requirement = requirementList.get(0);
+ assertTrue(requirement.getCapability().equals("tosca.capabilities.network.Linkable"));
+ assertTrue(requirement.getRelationship().equals("tosca.relationships.network.LinksTo"));
+ assertTrue(requirement.getName().equals("link"));
+
+ assertTrue(requirements.containsKey("tosca.capabilities.network.Bindable"));
+ requirementList = requirements.get("tosca.capabilities.network.Bindable");
+ requirement = requirementList.get(0);
+ assertTrue(requirement.getCapability().equals("tosca.capabilities.network.Bindable"));
+ assertTrue(requirement.getRelationship().equals("tosca.relationships.network.BindsTo"));
+ assertTrue(requirement.getName().equals("binding"));
+
+ }
+
+ private void testSetDerivedFrom(Resource resource) {
+ assertTrue(resource.getDerivedFrom().size() == 1);
+ assertTrue(resource.getDerivedFrom().get(0).equals("tosca.nodes.Root"));
+
+ }
+
+ private void testSetMetaDataFromJson(Resource resource, UploadResourceInfo resourceMD) {
+
+ // assertTrue( resource.getCategory().equals(resourceMD.getCategory())
+ // );
+ assertTrue(resource.getDescription().equals(resourceMD.getDescription()));
+ assertTrue(resource.getIcon().equals(resourceMD.getResourceIconPath()));
+ assertTrue(resource.getName().equals(resourceMD.getName()));
+ assertTrue(resource.getContactId().equals(resourceMD.getContactId()));
+ assertTrue(resource.getCreatorUserId().equals(resourceMD.getContactId()));
+
+ // assertTrue( resource.isAbstract() ==
+ // Constants.ABSTRACT_CATEGORY.equals(resourceMD.getCategory()));
+
+ assertTrue(resourceMD.getTags().size() == resource.getTags().size());
+ for (String tag : resource.getTags()) {
+ assertTrue(resourceMD.getTags().contains(tag));
+ }
+
+ }
+
+ private void testSetConstantMetaData(Resource resource) {
+ assertTrue(resource.getVersion().equals(ImportUtils.Constants.FIRST_CERTIFIED_VERSION_VERSION));
+ assertTrue(resource.getLifecycleState() == ImportUtils.Constants.NORMATIVE_TYPE_LIFE_CYCLE);
+ assertTrue(resource.isHighestVersion() == ImportUtils.Constants.NORMATIVE_TYPE_HIGHEST_VERSION);
+ assertTrue(resource.getVendorName().equals(ImportUtils.Constants.VENDOR_NAME));
+ assertTrue(resource.getVendorRelease().equals(ImportUtils.Constants.VENDOR_RELEASE));
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceTestUtils.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceTestUtils.java
new file mode 100644
index 0000000000..9a8f1c5ad8
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceTestUtils.java
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+
+public class ResourceTestUtils {
+
+ public static Resource prepareResource(int resourceIndex) {
+ Resource r = new Resource();
+ r.setName("resource_" + resourceIndex);
+ r.setDescription("description");
+ r.setVendorName("vendor name");
+ r.setVendorRelease("vendor release");
+ r.setContactId("as123y");
+ r.addCategory("Generic", "Infrastructure");
+ List<String> arr = new ArrayList<String>();
+ arr.add("tosca.nodes.Root");
+ r.setDerivedFrom(arr);
+ List<String> arr1 = new ArrayList<String>();
+ arr1.add(r.getName());
+ r.setTags(arr1);
+ r.setIcon("borderElement");
+ return r;
+ }
+
+ public static Resource prepareResource(int resourceIndex, ResourceTypeEnum resourceType) {
+ Resource r = new Resource();
+ r.setName("resource_" + resourceIndex);
+ r.setToscaResourceName("resource_" + resourceIndex);
+ r.setDescription("description");
+ r.setVendorName("vendor name");
+ r.setVendorRelease("vendor release");
+ r.setContactId("as123y");
+ r.setResourceType(resourceType);
+ r.addCategory("Generic", "Infrastructure");
+ List<String> arr = new ArrayList<String>();
+ arr.add("tosca.nodes.Root");
+ r.setDerivedFrom(arr);
+ List<String> arr1 = new ArrayList<String>();
+ arr1.add(r.getName());
+ r.setTags(arr1);
+ r.setIcon("borderElement");
+ return r;
+ }
+
+ public static Service prepareService(int serviceIndex) {
+ Service service = new Service();
+ service.setName("service_" + serviceIndex);
+ service.setDescription("desc");
+ service.setIcon("icon-service-red1");
+ List<String> tags = new ArrayList<String>();
+ tags.add(service.getName());
+ service.setTags(tags);
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName("Mobility");
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ service.setCategories(categories);
+ service.setContactId("as123y");
+ service.setProjectCode("123456");
+
+ return service;
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ServiceBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ServiceBusinessLogicTest.java
new file mode 100644
index 0000000000..fd83af016a
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ServiceBusinessLogicTest.java
@@ -0,0 +1,931 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.ElementOperationMock;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.AuditCassandraDao;
+import org.openecomp.sdc.be.dao.impl.AuditingDao;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.impl.GraphLockOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionDeployEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionNotificationEvent;
+import org.openecomp.sdc.be.resources.data.auditing.ResourceAdminEvent;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class ServiceBusinessLogicTest {
+
+ private static Logger log = LoggerFactory.getLogger(ServiceBusinessLogicTest.class.getName());
+ private static final String SERVICE_CATEGORY = "Mobility";
+ final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ IAuditingManager iAuditingManager = null;
+ UserBusinessLogic mockUserAdmin = Mockito.mock(UserBusinessLogic.class);
+ final ServiceOperation serviceOperation = Mockito.mock(ServiceOperation.class);
+ WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ WebApplicationContext webAppContext = Mockito.mock(WebApplicationContext.class);
+ ServiceBusinessLogic bl = new ServiceBusinessLogic();
+ ResponseFormatManager responseManager = null;
+ IElementOperation mockElementDao;
+ AuditingManager auditingManager = Mockito.mock(AuditingManager.class);
+ ComponentsUtils componentsUtils = new ComponentsUtils();
+ AuditCassandraDao auditingDao = Mockito.mock(AuditCassandraDao.class);
+ ArtifactsBusinessLogic artifactBl = Mockito.mock(ArtifactsBusinessLogic.class);
+ GraphLockOperation graphLockOperation = Mockito.mock(GraphLockOperation.class);
+ TitanGenericDao mockTitanDao = Mockito.mock(TitanGenericDao.class);
+
+ CacheMangerOperation cacheManager = Mockito.mock(CacheMangerOperation.class);
+
+ User user = null;
+ Service serviceResponse = null;
+ private static final String CERTIFIED_VERSION = "1.0";
+ private static final String UNCERTIFIED_VERSION = "0.2";
+ private static final String COMPONNET_ID = "myUniqueId";
+ private static Map<AuditingFieldsKeysEnum, Object> FILTER_MAP_CERTIFIED_VERSION = new HashMap<AuditingFieldsKeysEnum, Object>();
+ private static Map<AuditingFieldsKeysEnum, Object> FILTER_MAP_UNCERTIFIED_VERSION_CURR = new HashMap<AuditingFieldsKeysEnum, Object>();
+ private static Map<AuditingFieldsKeysEnum, Object> FILTER_MAP_UNCERTIFIED_VERSION_PREV = new HashMap<AuditingFieldsKeysEnum, Object>();
+
+ public ServiceBusinessLogicTest() {
+
+ }
+
+ @Before
+ public void setup() {
+
+ ExternalConfiguration.setAppName("catalog-be");
+
+ // Init Configuration
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ // Elements
+ mockElementDao = new ElementOperationMock();
+
+ // User data and management
+ user = new User();
+ user.setUserId("jh0003");
+ user.setFirstName("Jimmi");
+ user.setLastName("Hendrix");
+ user.setRole(Role.ADMIN.name());
+
+ Either<User, ActionStatus> eitherGetUser = Either.left(user);
+ when(mockUserAdmin.getUser("jh0003", false)).thenReturn(eitherGetUser);
+
+ // Servlet Context attributes
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ when(servletContext.getAttribute(Constants.SERVICE_OPERATION_MANAGER)).thenReturn(serviceOperation);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webAppContext);
+ when(webAppContext.getBean(IElementOperation.class)).thenReturn(mockElementDao);
+ when(graphLockOperation.lockComponent(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Service))).thenReturn(StorageOperationStatus.OK);
+ when(graphLockOperation.lockComponentByName(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Service))).thenReturn(StorageOperationStatus.OK);
+
+ // artifact bussinesslogic
+ ArtifactDefinition artifactDef = new ArtifactDefinition();
+ when(artifactBl.createArtifactPlaceHolderInfo(Mockito.anyString(), Mockito.anyString(), Mockito.anyMap(), Mockito.any(User.class), Mockito.any(ArtifactGroupTypeEnum.class))).thenReturn(artifactDef);
+
+ // createService
+ serviceResponse = createServiceObject(true);
+ Either<Service, StorageOperationStatus> eitherCreate = Either.left(serviceResponse);
+ when(serviceOperation.createService(Mockito.any(Service.class))).thenReturn(eitherCreate);
+ Either<Boolean, StorageOperationStatus> eitherCount = Either.left(true);
+ when(serviceOperation.validateServiceNameExists("Service")).thenReturn(eitherCount);
+ Either<Boolean, StorageOperationStatus> eitherCountExist = Either.left(false);
+ when(serviceOperation.validateServiceNameExists("alreadyExist")).thenReturn(eitherCountExist);
+
+ when(serviceOperation.validateComponentNameExists("alreadyExist")).thenReturn(eitherCountExist);
+ when(serviceOperation.validateComponentNameExists("Service")).thenReturn(eitherCount);
+
+ bl = new ServiceBusinessLogic();
+ bl.setElementDao(mockElementDao);
+ bl.setUserAdmin(mockUserAdmin);
+ bl.setServiceOperation(serviceOperation);
+ bl.setArtifactBl(artifactBl);
+ bl.setGraphLockOperation(graphLockOperation);
+ bl.setTitanGenericDao(mockTitanDao);
+
+ componentsUtils.Init();
+ componentsUtils.setAuditingManager(auditingManager);
+ bl.setComponentsUtils(componentsUtils);
+ bl.setCassandraAuditingDao(auditingDao);
+ bl.setCacheManagerOperation(cacheManager);
+
+ mockAuditingDaoLogic();
+
+ responseManager = ResponseFormatManager.getInstance();
+
+ }
+
+ @Test
+ public void testGetComponentAuditRecordsCertifiedVersion() {
+ Either<List<Map<String, Object>>, ResponseFormat> componentAuditRecords = bl.getComponentAuditRecords(CERTIFIED_VERSION, COMPONNET_ID, user.getUserId());
+ assertTrue(componentAuditRecords.isLeft());
+ int size = componentAuditRecords.left().value().size();
+ assertTrue(size == 3);
+ }
+
+ @Test
+ public void testGetComponentAuditRecordsUnCertifiedVersion() {
+ Either<List<Map<String, Object>>, ResponseFormat> componentAuditRecords = bl.getComponentAuditRecords(UNCERTIFIED_VERSION, COMPONNET_ID, user.getUserId());
+ assertTrue(componentAuditRecords.isLeft());
+ int size = componentAuditRecords.left().value().size();
+ assertTrue(size == 1);
+ }
+
+ @Test
+ public void testHappyScenario() {
+ Service service = createServiceObject(false);
+ Either<Service, ResponseFormat> createResponse = bl.createService(service, user);
+
+ if (createResponse.isRight()) {
+ assertEquals(new Integer(200), createResponse.right().value().getStatus());
+ }
+ assertEqualsServiceObject(createServiceObject(true), createResponse.left().value());
+ }
+
+ private void assertEqualsServiceObject(Service origService, Service newService) {
+ assertEquals(origService.getContactId(), newService.getContactId());
+ assertEquals(origService.getCategories(), newService.getCategories());
+ assertEquals(origService.getCreatorUserId(), newService.getCreatorUserId());
+ assertEquals(origService.getCreatorFullName(), newService.getCreatorFullName());
+ assertEquals(origService.getDescription(), newService.getDescription());
+ assertEquals(origService.getIcon(), newService.getIcon());
+ assertEquals(origService.getLastUpdaterUserId(), newService.getLastUpdaterUserId());
+ assertEquals(origService.getLastUpdaterFullName(), newService.getLastUpdaterFullName());
+ assertEquals(origService.getName(), newService.getName());
+ assertEquals(origService.getName(), newService.getName());
+ assertEquals(origService.getUniqueId(), newService.getUniqueId());
+ assertEquals(origService.getVersion(), newService.getVersion());
+ assertEquals(origService.getArtifacts(), newService.getArtifacts());
+ assertEquals(origService.getCreationDate(), newService.getCreationDate());
+ assertEquals(origService.getLastUpdateDate(), newService.getLastUpdateDate());
+ assertEquals(origService.getLifecycleState(), newService.getLifecycleState());
+ assertEquals(origService.getTags(), newService.getTags());
+ }
+
+ private void assertResponse(Either<Service, ResponseFormat> createResponse, ActionStatus expectedStatus, String... variables) {
+ ResponseFormat expectedResponse = responseManager.getResponseFormat(expectedStatus, variables);
+ ResponseFormat actualResponse = createResponse.right().value();
+ assertEquals(expectedResponse.getStatus(), actualResponse.getStatus());
+ assertEquals("assert error description", expectedResponse.getFormattedMessage(), actualResponse.getFormattedMessage());
+ }
+
+ /* CREATE validations - start ***********************/
+ // Service name - start
+
+ @Test
+ public void testFailedServiceValidations() {
+ testServiceNameAlreadyExists();
+ testServiceNameEmpty();
+ // testServiceNameExceedsLimit();
+ testServiceNameWrongFormat();
+ testServiceDescriptionEmpty();
+ testServiceDescriptionMissing();
+ testServiceDescExceedsLimitCreate();
+ testServiceDescNotEnglish();
+ testServiceIconEmpty();
+ testServiceIconMissing();
+ testResourceIconInvalid();
+ testResourceIconExceedsLimit();
+ // testTagsExceedsLimitCreate();
+ // testTagsSingleExcessLimit();
+ testTagsNoServiceName();
+ testInvalidTag();
+ testServiceTagNotExist();
+ testServiceTagEmpty();
+ // 1610OS Support - Because of changes in the validation in the ui these tests will fail. need to fix them
+ //testContactIdTooLong();
+ //testContactIdWrongFormatCreate();
+ testResourceContactIdMissing();
+ testServiceCategoryExist();
+ testServiceBadCategoryCreate();
+ // 1610OS Support - Because of changes in the validation in the ui these tests will fail. need to fix them
+ //testInvalidProjectCode();
+ //testProjectCodeTooLong();
+ //testProjectCodeTooShort();
+ testMissingProjectCode();
+ }
+
+ private void testServiceNameAlreadyExists() {
+ String serviceName = "alreadyExist";
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ // 51 chars, the limit is 50
+ serviceExccedsNameLimit.setName(serviceName);
+ List<String> tgs = new ArrayList<String>();
+ tgs.add(serviceName);
+ serviceExccedsNameLimit.setTags(tgs);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_NAME_ALREADY_EXIST, ComponentTypeEnum.SERVICE.getValue(), serviceName);
+ }
+
+ private void testServiceNameEmpty() {
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ serviceExccedsNameLimit.setName(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testServiceNameExceedsLimit() {
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ // 51 chars, the limit is 50
+ String tooLongServiceName = "h1KSyJh9EspI8SPwAGu4VETfqWejeanuB1PCJBxdsafefegesse";
+ serviceExccedsNameLimit.setName(tooLongServiceName);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT, ComponentTypeEnum.SERVICE.getValue(), "" + ValidationUtils.COMPONENT_NAME_MAX_LENGTH);
+ }
+
+ private void testServiceNameWrongFormat() {
+ Service service = createServiceObject(false);
+ // contains :
+ String nameWrongFormat = "ljg\fd";
+ service.setName(nameWrongFormat);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(service, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_COMPONENT_NAME, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ // Service name - end
+ // Service description - start
+ private void testServiceDescriptionEmpty() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setDescription("");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_DESCRIPTION, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testServiceDescriptionMissing() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setDescription(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_DESCRIPTION, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testServiceDescExceedsLimitCreate() {
+ Service serviceExccedsDescLimit = createServiceObject(false);
+ // 1025 chars, the limit is 1024
+ String tooLongServiceDesc = "1GUODojQ0sGzKR4NP7e5j82ADQ3KHTVOaezL95qcbuaqDtjZhAQGQ3iFwKAy580K4WiiXs3u3zq7RzXcSASl5fm0RsWtCMOIDP"
+ + "AOf9Tf2xtXxPCuCIMCR5wOGnNTaFxgnJEHAGxilBhZDgeMNHmCN1rMK5B5IRJOnZxcpcL1NeG3APTCIMP1lNAxngYulDm9heFSBc8TfXAADq7703AvkJT0QPpGq2z2P"
+ + "tlikcAnIjmWgfC5Tm7UH462BAlTyHg4ExnPPL4AO8c92VrD7kZSgSqiy73cN3gLT8uigkKrUgXQFGVUFrXVyyQXYtVM6bLBeuCGQf4C2j8lkNg6M0J3PC0PzMRoinOxk"
+ + "Ae2teeCtVcIj4A1KQo3210j8q2v7qQU69Mabsa6DT9FgE4rcrbiFWrg0Zto4SXWD3o1eJA9o29lTg6kxtklH3TuZTmpi5KVp1NFhS1RpnqF83tzv4mZLKsx7Zh1fEgYvRFwx1"
+ + "ar3RolyDfNoZiGBGTMsZzz7RPFBf2hTnLmNqVGQnHKhhGj0Y5s8t2cbqbO2nmHiJb9uaUVrCGypgbAcJL3KPOBfAVW8PcpmNj4yVjI3L4x5zHjmGZbp9vKshEQODcrmcgsYAoKqe"
+ + "uu5u7jk8XVxEfQ0m5qL8UOErXPlJovSmKUmP5B5T0w299zIWDYCzSoNasHpHjOMDLAiDDeHbozUOn9t3Qou00e9POq4RMM0VnIx1H38nJoJZz2XH8CI5YMQe7oTagaxgQTF2aa0qaq2"
+ + "V6nJsfRGRklGjNhFFYP2cS4Xv2IJO9DSX6LTXOmENrGVJJvMOZcvnBaZPfoAHN0LU4i1SoepLzulIxnZBfkUWFJgZ5wQ0Bco2GC1HMqzW21rwy4XHRxXpXbmW8LVyoA1KbnmVmROycU4"
+ + "scTZ62IxIcIWCVeMjBIcTviXULbPUyqlfEPXWr8IMJtpAaELWgyquPClAREMDs2b9ztKmUeXlMccFES1XWbFTrhBHhmmDyVReEgCwfokrUFR13LTUK1k8I6OEHOs";
+
+ serviceExccedsDescLimit.setDescription(tooLongServiceDesc);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsDescLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT, ComponentTypeEnum.SERVICE.getValue(), "" + ValidationUtils.COMPONENT_DESCRIPTION_MAX_LENGTH);
+ }
+
+ private void testServiceDescNotEnglish() {
+ Service notEnglish = createServiceObject(false);
+ // Not english
+ String tooLongServiceDesc = "\uC2B5";
+ notEnglish.setDescription(tooLongServiceDesc);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(notEnglish, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_DESCRIPTION, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ // Service description - stop
+ // Service icon - start
+ private void testServiceIconEmpty() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setIcon("");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_ICON, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testServiceIconMissing() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setIcon(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_ICON, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testResourceIconInvalid() {
+ Service resourceExist = createServiceObject(false);
+ resourceExist.setIcon("kjk3453^&");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(resourceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_ICON, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testResourceIconExceedsLimit() {
+ Service resourceExist = createServiceObject(false);
+ resourceExist.setIcon("dsjfhskdfhskjdhfskjdhkjdhfkshdfksjsdkfhsdfsdfsdfsfsdfsf");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(resourceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT, ComponentTypeEnum.SERVICE.getValue(), "" + ValidationUtils.ICON_MAX_LENGTH);
+ }
+
+ // Service icon - stop
+ // Service tags - start
+ private void testTagsExceedsLimitCreate() {
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ String tag1 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjQ";
+ String tag2 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjW";
+ String tag3 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjE";
+ String tag4 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjb";
+ String tag5 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjr";
+ String tag6 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjf";
+ String tag7 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjg";
+ String tag8 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjd";
+ String tag9 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjf";
+ String tag10 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjg";
+ String tag11 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjh";
+ String tag12 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjj";
+ String tag13 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjk";
+ String tag14 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjs";
+ String tag15 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjz";
+ String tag16 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjx";
+ String tag17 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj2";
+ String tag18 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj3";
+ String tag19 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj4";
+ String tag20 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj5";
+ String tag21 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj0";
+
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ tagsList.add(tag2);
+ tagsList.add(tag3);
+ tagsList.add(tag4);
+ tagsList.add(tag5);
+ tagsList.add(tag6);
+ tagsList.add(tag7);
+ tagsList.add(tag8);
+ tagsList.add(tag9);
+ tagsList.add(tag10);
+ tagsList.add(tag11);
+ tagsList.add(tag12);
+ tagsList.add(tag13);
+ tagsList.add(tag14);
+ tagsList.add(tag15);
+ tagsList.add(tag16);
+ tagsList.add(tag17);
+ tagsList.add(tag18);
+ tagsList.add(tag19);
+ tagsList.add(tag20);
+ tagsList.add(tag21);
+ tagsList.add(serviceExccedsNameLimit.getName());
+
+ serviceExccedsNameLimit.setTags(tagsList);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT, "" + ValidationUtils.TAG_LIST_MAX_LENGTH);
+
+ }
+
+ private void testTagsSingleExcessLimit() {
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ String tag1 = "afzs2qLBb5X6tZhiunkcEwiFX1qRQY8YZl3y3Du5M5xeQY5Nq9a";
+ String tag2 = serviceExccedsNameLimit.getName();
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ tagsList.add(tag2);
+ serviceExccedsNameLimit.setTags(tagsList);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT, "" + ValidationUtils.TAG_MAX_LENGTH);
+
+ }
+
+ private void testTagsNoServiceName() {
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ String tag1 = "afzs2qLBb";
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ serviceExccedsNameLimit.setTags(tagsList);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME);
+
+ }
+
+ private void testInvalidTag() {
+ Service serviceExccedsNameLimit = createServiceObject(false);
+ String tag1 = "afzs2qLBb%#%";
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ serviceExccedsNameLimit.setTags(tagsList);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExccedsNameLimit, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_FIELD_FORMAT, new String[] { "Service", "tag" });
+
+ }
+
+ private void testServiceTagNotExist() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setTags(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_TAGS);
+ }
+
+ private void testServiceTagEmpty() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setTags(new ArrayList<String>());
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_TAGS);
+ }
+
+ // Service tags - stop
+ // Service contactId - start
+ private void testContactIdTooLong() {
+ Service serviceContactId = createServiceObject(false);
+ // 7 chars instead of 6
+ String contactIdTooLong = "yrt1234";
+ serviceContactId.setContactId(contactIdTooLong);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceContactId, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testContactIdWrongFormatCreate() {
+ Service serviceContactId = createServiceObject(false);
+ // 3 letters and 3 digits
+ String contactIdTooLong = "yrt134";
+ serviceContactId.setContactId(contactIdTooLong);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceContactId, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ private void testResourceContactIdMissing() {
+ Service resourceExist = createServiceObject(false);
+ resourceExist.setContactId(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(resourceExist, user);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_CONTACT, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ // Service contactId - stop
+ // Service category - start
+ private void testServiceCategoryExist() {
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setCategories(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ public void markDistributionAsDeployedTestAlreadyDeployed() {
+ String notifyAction = "DNotify";
+ String requestAction = "DRequest";
+ String resultAction = "DResult";
+ String did = "123456";
+
+ setupBeforeDeploy(notifyAction, requestAction, did);
+ List<DistributionDeployEvent> resultList = new ArrayList<DistributionDeployEvent>();
+ Map<String, Object> params = new HashMap<String, Object>();
+ DistributionDeployEvent event = new DistributionDeployEvent();
+
+ event.setAction(resultAction);
+ event.setDid(did);
+ event.setStatus("200");
+ // ESTimeBasedEvent deployEvent = new ESTimeBasedEvent();
+ // deployEvent.setFields(params);
+ resultList.add(event);
+ Either<List<DistributionDeployEvent>, ActionStatus> eventList = Either.left(resultList);
+
+ Mockito.when(auditingDao.getDistributionDeployByStatus(Mockito.anyString(), Mockito.eq(resultAction), Mockito.anyString())).thenReturn(eventList);
+
+ Either<Service, ResponseFormat> markDeployed = bl.markDistributionAsDeployed(did, did, user);
+ assertTrue(markDeployed.isLeft());
+
+ Mockito.verify(auditingDao, Mockito.times(0)).getDistributionRequest(did, requestAction);
+
+ }
+
+ @Test
+ public void markDistributionAsDeployedTestSuccess() {
+ String notifyAction = "DNotify";
+ String requestAction = "DRequest";
+ String did = "123456";
+
+ setupBeforeDeploy(notifyAction, requestAction, did);
+
+ Either<Service, ResponseFormat> markDeployed = bl.markDistributionAsDeployed(did, did, user);
+ assertTrue(markDeployed.isLeft());
+
+ }
+
+ @Test
+ public void markDistributionAsDeployedTestNotDistributed() {
+ String notifyAction = "DNotify";
+ String requestAction = "DRequest";
+ String did = "123456";
+
+ setupBeforeDeploy(notifyAction, requestAction, did);
+ List<ResourceAdminEvent> emptyList = new ArrayList<ResourceAdminEvent>();
+ Either<List<ResourceAdminEvent>, ActionStatus> emptyEventList = Either.left(emptyList);
+ Mockito.when(auditingDao.getDistributionRequest(Mockito.anyString(), Mockito.eq(requestAction))).thenReturn(emptyEventList);
+
+ Either<Service, StorageOperationStatus> notFound = Either.right(StorageOperationStatus.NOT_FOUND);
+ Mockito.when(serviceOperation.getService(did)).thenReturn(notFound);
+
+ Either<Service, ResponseFormat> markDeployed = bl.markDistributionAsDeployed(did, did, user);
+ assertTrue(markDeployed.isRight());
+ assertEquals(404, markDeployed.right().value().getStatus().intValue());
+
+ }
+
+ private void testServiceBadCategoryCreate() {
+
+ Service serviceExist = createServiceObject(false);
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName("koko");
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ serviceExist.setCategories(categories);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.SERVICE.getValue());
+ }
+
+ // Service category - stop
+ private void testInvalidProjectCode() {
+
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setProjectCode("koko");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.INVALID_PROJECT_CODE);
+ }
+
+ private void testProjectCodeTooLong() {
+
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setProjectCode("5555555555555");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.INVALID_PROJECT_CODE);
+ }
+
+ private void testProjectCodeTooShort() {
+
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setProjectCode("555");
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.INVALID_PROJECT_CODE);
+ }
+
+ private void testMissingProjectCode() {
+
+ Service serviceExist = createServiceObject(false);
+ serviceExist.setProjectCode(null);
+
+ Either<Service, ResponseFormat> createResponse = bl.createService(serviceExist, user);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_PROJECT_CODE);
+ }
+
+ @Test
+ public void testDeleteMarkedServicesNoServices() {
+ List<String> ids = new ArrayList<String>();
+ Either<List<String>, StorageOperationStatus> eitherNoResources = Either.left(ids);
+ when(serviceOperation.getAllComponentsMarkedForDeletion()).thenReturn(eitherNoResources);
+
+ Either<List<String>, ResponseFormat> deleteMarkedResources = bl.deleteMarkedComponents();
+ assertTrue(deleteMarkedResources.isLeft());
+ assertTrue(deleteMarkedResources.left().value().isEmpty());
+
+ Mockito.verify(artifactBl, Mockito.times(0)).deleteAllComponentArtifactsIfNotOnGraph(Mockito.anyList());
+
+ }
+
+ @Test
+ public void testDeleteMarkedServices() {
+ List<String> ids = new ArrayList<String>();
+ String resourceInUse = "123";
+ ids.add(resourceInUse);
+ String resourceFree = "456";
+ ids.add(resourceFree);
+ Either<List<String>, StorageOperationStatus> eitherNoResources = Either.left(ids);
+ when(serviceOperation.getAllComponentsMarkedForDeletion()).thenReturn(eitherNoResources);
+
+ Either<Boolean, StorageOperationStatus> resourceInUseResponse = Either.left(true);
+ Either<Boolean, StorageOperationStatus> resourceFreeResponse = Either.left(false);
+
+ List<ArtifactDefinition> artifacts = new ArrayList<ArtifactDefinition>();
+ Either<List<ArtifactDefinition>, StorageOperationStatus> getArtifactsResponse = Either.left(artifacts);
+ when(serviceOperation.getComponentArtifactsForDelete(resourceFree, NodeTypeEnum.Service, true)).thenReturn(getArtifactsResponse);
+
+ when(serviceOperation.isComponentInUse(resourceFree)).thenReturn(resourceFreeResponse);
+ when(serviceOperation.isComponentInUse(resourceInUse)).thenReturn(resourceInUseResponse);
+
+ Either<Component, StorageOperationStatus> eitherDelete = Either.left(new Resource());
+ when(serviceOperation.deleteComponent(resourceFree, true)).thenReturn(eitherDelete);
+
+ when(artifactBl.deleteAllComponentArtifactsIfNotOnGraph(artifacts)).thenReturn(StorageOperationStatus.OK);
+
+ Either<List<String>, ResponseFormat> deleteMarkedResources = bl.deleteMarkedComponents();
+ assertTrue(deleteMarkedResources.isLeft());
+ List<String> resourceIdList = deleteMarkedResources.left().value();
+ assertFalse(resourceIdList.isEmpty());
+ assertTrue(resourceIdList.contains(resourceFree));
+ assertFalse(resourceIdList.contains(resourceInUse));
+
+ Mockito.verify(artifactBl, Mockito.times(1)).deleteAllComponentArtifactsIfNotOnGraph(artifacts);
+ }
+
+ private Service createServiceObject(boolean afterCreate) {
+ Service service = new Service();
+ service.setName("Service");
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName(SERVICE_CATEGORY);
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ service.setCategories(categories);
+
+ service.setDescription("description");
+ List<String> tgs = new ArrayList<String>();
+ tgs.add(service.getName());
+ service.setTags(tgs);
+ // service.setVendorName("Motorola");
+ // service.setVendorRelease("1.0.0");
+ // service.setContactId("ya5467");
+ service.setIcon("MyIcon");
+ // service.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ service.setContactId("aa1234");
+ service.setProjectCode("12345");
+
+ if (afterCreate) {
+ service.setVersion("0.1");
+ service.setUniqueId(service.getName() + ":" + service.getVersion());
+ service.setCreatorUserId(user.getUserId());
+ service.setCreatorFullName(user.getFirstName() + " " + user.getLastName());
+ }
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ log.debug("{}", gson.toJson(service));
+ return service;
+ }
+
+ private void mockAuditingDaoLogic() {
+ FILTER_MAP_CERTIFIED_VERSION.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, COMPONNET_ID);
+ FILTER_MAP_UNCERTIFIED_VERSION_CURR.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, COMPONNET_ID);
+ FILTER_MAP_UNCERTIFIED_VERSION_PREV.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, COMPONNET_ID);
+
+ FILTER_MAP_UNCERTIFIED_VERSION_CURR.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, UNCERTIFIED_VERSION);
+ FILTER_MAP_UNCERTIFIED_VERSION_PREV.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION, UNCERTIFIED_VERSION);
+
+ final ResourceAdminEvent createResourceAudit = new ResourceAdminEvent();
+ createResourceAudit.setModifier("Carlos Santana(cs0008)");
+ createResourceAudit.setCurrState("NOT_CERTIFIED_CHECKOUT");
+ createResourceAudit.setCurrVersion("0.1");
+ createResourceAudit.setServiceInstanceId("82eddd99-0bd9-4742-ab0a-1bdb5e262a05");
+ createResourceAudit.setRequestId("3e65cea1-7403-4bc7-b461-e2544d83799f");
+ createResourceAudit.setDesc("OK");
+ createResourceAudit.setResourceType("Resource");
+ createResourceAudit.setStatus("201");
+ createResourceAudit.setPrevVersion("");
+ createResourceAudit.setAction("Create");
+ // fields.put("TIMESTAMP", "2015-11-22 09:19:12.977");
+ createResourceAudit.setPrevState("");
+ createResourceAudit.setResourceName("MyTestResource");
+ // createResourceAudit.setFields(fields);
+
+ final ResourceAdminEvent checkInResourceAudit = new ResourceAdminEvent();
+ checkInResourceAudit.setModifier("Carlos Santana(cs0008)");
+ checkInResourceAudit.setCurrState("NOT_CERTIFIED_CHECKIN");
+ checkInResourceAudit.setCurrVersion("0.1");
+ checkInResourceAudit.setServiceInstanceId("82eddd99-0bd9-4742-ab0a-1bdb5e262a05");
+ checkInResourceAudit.setRequestId("ffacbf5d-eeb1-43c6-a310-37fe7e1cc091");
+ checkInResourceAudit.setDesc("OK");
+ checkInResourceAudit.setComment("Stam");
+ checkInResourceAudit.setResourceType("Resource");
+ checkInResourceAudit.setStatus("200");
+ checkInResourceAudit.setPrevVersion("0.1");
+ checkInResourceAudit.setAction("Checkin");
+ // fields.put("TIMESTAMP", "2015-11-22 09:25:03.797");
+ checkInResourceAudit.setPrevState("NOT_CERTIFIED_CHECKOUT");
+ checkInResourceAudit.setResourceName("MyTestResource");
+
+ final ResourceAdminEvent checkOutResourceAudit = new ResourceAdminEvent();
+ checkOutResourceAudit.setModifier("Carlos Santana(cs0008)");
+ checkOutResourceAudit.setCurrState("NOT_CERTIFIED_CHECKOUT");
+ checkOutResourceAudit.setCurrVersion("0.2");
+ checkOutResourceAudit.setServiceInstanceId("82eddd99-0bd9-4742-ab0a-1bdb5e262a05");
+ checkOutResourceAudit.setRequestId("7add5078-4c16-4d74-9691-cc150e3c96b8");
+ checkOutResourceAudit.setDesc("OK");
+ checkOutResourceAudit.setComment("");
+ checkOutResourceAudit.setResourceType("Resource");
+ checkOutResourceAudit.setStatus("200");
+ checkOutResourceAudit.setPrevVersion("0.1");
+ checkOutResourceAudit.setAction("Checkout");
+ // fields.put("TIMESTAMP", "2015-11-22 09:39:41.024");
+ checkOutResourceAudit.setPrevState("NOT_CERTIFIED_CHECKIN");
+ checkOutResourceAudit.setResourceName("MyTestResource");
+ // checkOutResourceAudit.setFields(fields);
+
+ // Mockito.doAnswer(new Answer<Either<List<ESTimeBasedEvent>,
+ // ActionStatus> >() {
+ // public Either<List<ESTimeBasedEvent>, ActionStatus>
+ // answer(InvocationOnMock invocation) {
+ // final Either<List<ESTimeBasedEvent>, ActionStatus> either;
+ // final List<ESTimeBasedEvent> list;
+ // Object[] args = invocation.getArguments();
+ // Map<AuditingFieldsKeysEnum, Object> filterMap =
+ // (Map<AuditingFieldsKeysEnum, Object>) args[0];
+ // if( filterMap.equals(FILTER_MAP_CERTIFIED_VERSION) ){
+ // list = new
+ // ArrayList<ESTimeBasedEvent>(){{add(createResourceAudit);add(checkInResourceAudit);add(checkOutResourceAudit);}};
+ // either = Either.left(list);
+ //
+ // }
+ // else if( filterMap.equals(FILTER_MAP_UNCERTIFIED_VERSION_PREV) ){
+ // list = new ArrayList<ESTimeBasedEvent>();
+ // either = Either.left(list);
+ // }
+ // else if( filterMap.equals(FILTER_MAP_UNCERTIFIED_VERSION_CURR) ){
+ // list = new
+ // ArrayList<ESTimeBasedEvent>(){{/*add(createResourceAudit);add(checkInResourceAudit);*/add(checkOutResourceAudit);}};
+ // either = Either.left(list);
+ // }
+ // else{
+ // either = null;
+ // }
+ // return either;
+ // }
+ // }).when(auditingDao).getFilteredResourceAdminAuditingEvents(Mockito.anyMap());
+ //
+ //
+ List<ResourceAdminEvent> list = new ArrayList<ResourceAdminEvent>() {
+ {
+ add(createResourceAudit);
+ add(checkInResourceAudit);
+ add(checkOutResourceAudit);
+ }
+ };
+ Either<List<ResourceAdminEvent>, ActionStatus> result = Either.left(list);
+ Mockito.when(auditingDao.getByServiceInstanceId(Mockito.anyString())).thenReturn(result);
+
+ List<ResourceAdminEvent> listPrev = new ArrayList<ResourceAdminEvent>();
+ Either<List<ResourceAdminEvent>, ActionStatus> resultPrev = Either.left(listPrev);
+ Mockito.when(auditingDao.getAuditByServiceIdAndPrevVersion(Mockito.anyString(), Mockito.anyString())).thenReturn(resultPrev);
+
+ List<ResourceAdminEvent> listCurr = new ArrayList<ResourceAdminEvent>() {
+ {
+ add(checkOutResourceAudit);
+ }
+ };
+ Either<List<ResourceAdminEvent>, ActionStatus> resultCurr = Either.left(listCurr);
+ Mockito.when(auditingDao.getAuditByServiceIdAndCurrVersion(Mockito.anyString(), Mockito.anyString())).thenReturn(resultCurr);
+
+ }
+
+ private void setupBeforeDeploy(String notifyAction, String requestAction, String did) {
+
+ DistributionNotificationEvent notifyEvent = new DistributionNotificationEvent();
+ notifyEvent.setAction(notifyAction);
+ notifyEvent.setDid(did);
+ notifyEvent.setStatus("200");
+
+ ResourceAdminEvent requestEvent = new ResourceAdminEvent();
+ requestEvent.setAction(requestAction);
+ requestEvent.setDid(did);
+ requestEvent.setStatus("200");
+
+ ArrayList<DistributionNotificationEvent> arrayList = new ArrayList<DistributionNotificationEvent>();
+ List<DistributionNotificationEvent> notifyResults = arrayList;
+ notifyResults.add(notifyEvent);
+ Either<List<DistributionNotificationEvent>, ActionStatus> eitherNotify = Either.left(notifyResults);
+
+ Mockito.when(auditingDao.getDistributionNotify(Mockito.anyString(), Mockito.eq(notifyAction))).thenReturn(eitherNotify);
+
+ List<ResourceAdminEvent> requestResults = new ArrayList<ResourceAdminEvent>();
+ requestResults.add(requestEvent);
+ Either<List<ResourceAdminEvent>, ActionStatus> eitherRequest = Either.left(requestResults);
+ Mockito.when(auditingDao.getDistributionRequest(Mockito.anyString(), Mockito.eq(requestAction))).thenReturn(eitherRequest);
+
+ Either<Service, StorageOperationStatus> eitherService = Either.left(createServiceObject(true));
+ Mockito.when(serviceOperation.getService(Mockito.anyString())).thenReturn(eitherService);
+
+ List<DistributionDeployEvent> emptyList = new ArrayList<DistributionDeployEvent>();
+ Either<List<DistributionDeployEvent>, ActionStatus> emptyEventList = Either.left(emptyList);
+ Mockito.when(auditingDao.getDistributionDeployByStatus(Mockito.anyString(), Mockito.eq("DResult"), Mockito.anyString())).thenReturn(emptyEventList);
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineConfigTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineConfigTest.java
new file mode 100644
index 0000000000..7ad93226c7
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineConfigTest.java
@@ -0,0 +1,168 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngine;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.ComponentArtifactTypesConfig;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.CreateTopicConfig;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.DistributionStatusTopicConfig;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+
+public class DistributionEngineConfigTest {
+
+ @Before
+ public void setup() {
+ ExternalConfiguration.setAppName("catalog-be");
+ ExternalConfiguration.setConfigDir("src/test/resources/config");
+ ExternalConfiguration.listenForChanges();
+
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), ExternalConfiguration.getConfigDir() + File.separator + ExternalConfiguration.getAppName());
+
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+ }
+
+ @Test
+ public void validateMissingEnvironments() {
+
+ DistributionEngineConfiguration deConfiguration = new DistributionEngineConfiguration();
+
+ String uebPublicKey = "uebPublicKey";
+ String uebSecretKey = "uebSecretKey";
+
+ DistributionEngine distributionEngine = new DistributionEngine();
+
+ List<String> environments = new ArrayList<String>();
+ environments.add("PROD");
+ deConfiguration.setEnvironments(environments);
+
+ List<String> servers = new ArrayList<String>();
+ servers.add("server1:80");
+ servers.add("server2:8080");
+
+ CreateTopicConfig createTopic = new CreateTopicConfig();
+ createTopic.setPartitionCount(1);
+ createTopic.setReplicationCount(1);
+ deConfiguration.setCreateTopic(createTopic);
+
+ ComponentArtifactTypesConfig distribNotifResourceArtifactTypes = new ComponentArtifactTypesConfig();
+ deConfiguration.setDistribNotifResourceArtifactTypes(distribNotifResourceArtifactTypes);
+
+ ComponentArtifactTypesConfig distribNotifServiceArtifactTypes = new ComponentArtifactTypesConfig();
+ deConfiguration.setDistribNotifServiceArtifactTypes(distribNotifServiceArtifactTypes);
+
+ deConfiguration.setDistributionNotifTopicName("distributionNotifTopicName");
+ deConfiguration.setDistributionStatusTopicName("statusTopic");
+
+ DistributionStatusTopicConfig distributionStatusTopic = new DistributionStatusTopicConfig();
+ distributionStatusTopic.setConsumerGroup("asdc-group");
+ distributionStatusTopic.setConsumerId("asdc-id");
+ distributionStatusTopic.setFetchTimeSec(20);
+ distributionStatusTopic.setPollingIntervalSec(20);
+ deConfiguration.setDistributionStatusTopic(distributionStatusTopic);
+
+ deConfiguration.setUebServers(servers);
+ deConfiguration.setUebPublicKey(uebPublicKey);
+ deConfiguration.setUebSecretKey(uebSecretKey);
+ deConfiguration.setInitMaxIntervalSec(8);
+ deConfiguration.setInitRetryIntervalSec(3);
+
+ boolean isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setUebServers(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setUebServers(servers);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setEnvironments(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setEnvironments(environments);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setUebPublicKey(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setUebPublicKey(uebPublicKey);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setUebSecretKey(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setUebSecretKey(uebPublicKey);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setDistributionNotifTopicName(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setDistributionNotifTopicName(uebPublicKey);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setDistributionStatusTopicName(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setDistributionStatusTopicName(uebPublicKey);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setInitMaxIntervalSec(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setInitMaxIntervalSec(8);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ deConfiguration.setInitRetryIntervalSec(null);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertFalse("check empty configuration", isValid);
+
+ deConfiguration.setInitRetryIntervalSec(8);
+ isValid = distributionEngine.validateConfiguration(deConfiguration);
+ assertTrue("check empty configuration", isValid);
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineHealthCheckTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineHealthCheckTest.java
new file mode 100644
index 0000000000..b3a254f531
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineHealthCheckTest.java
@@ -0,0 +1,138 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.components.BaseConfDependentTest;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaErrorResponse;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaHandler;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineClusterHealth;
+import org.openecomp.sdc.be.components.distribution.engine.UebHealthCheckCall;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class DistributionEngineHealthCheckTest extends BaseConfDependentTest {
+
+ @Mock
+ private CambriaHandler cambriaHandler = Mockito.mock(CambriaHandler.class);
+
+ DistributionEngineClusterHealth distributionEngineClusterHealth = new DistributionEngineClusterHealth();
+
+ Gson gson = new Gson();
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ //
+ // @Test
+ // public void validateDownWhenEnvAreDown() {
+ //
+ // Map<String, AtomicBoolean> envNamePerStatus = new HashMap<>();
+ // envNamePerStatus.put("PROD1", new AtomicBoolean(false));
+ // envNamePerStatus.put("PROD2", new AtomicBoolean(false));
+ //
+ // distributionEngineClusterHealth.startHealthCheckTask(envNamePerStatus);
+ //
+ // HealthCheckInfo healthCheckInfo =
+ // distributionEngineClusterHealth.getHealthCheckInfo();
+ // assertEquals("verify down", HealthCheckStatus.DOWN,
+ // healthCheckInfo.getHealthCheckStatus());
+ // assertEquals("verify DE component", HealthCheckComponent.DE,
+ // healthCheckInfo.getHealthCheckComponent());
+ //
+ // }
+
+ @Test
+ public void validateUpWhenQuerySucceed() {
+
+ // Map<String, AtomicBoolean> envNamePerStatus = new HashMap<>();
+ // envNamePerStatus.put("PROD1", new AtomicBoolean(true));
+ // envNamePerStatus.put("PROD2", new AtomicBoolean(false));
+ //
+ // distributionEngineClusterHealth.startHealthCheckTask(envNamePerStatus,
+ // false);
+
+ CambriaErrorResponse cambriaOkResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, 200);
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.INTERNAL_SERVER_ERROR, 500);
+ CambriaErrorResponse cambriaNotErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.AUTHENTICATION_ERROR, 403);
+
+ List<String> uebServers = ConfigurationManager.getConfigurationManager().getDistributionEngineConfiguration().getUebServers();
+ if (uebServers.size() >= 2) {
+ when(cambriaHandler.getApiKey(Mockito.eq(uebServers.get(0)), Mockito.any(String.class))).thenReturn(cambriaOkResponse);
+ when(cambriaHandler.getApiKey(Mockito.eq(uebServers.get(1)), Mockito.any(String.class))).thenReturn(cambriaOkResponse);
+ }
+
+ UebHealthCheckCall healthCheckCall1 = new UebHealthCheckCall(uebServers.get(0), "publicKey");
+ healthCheckCall1.setCambriaHandler(cambriaHandler);
+ Boolean call1 = healthCheckCall1.call();
+ assertTrue("check response okay", call1);
+
+ UebHealthCheckCall healthCheckCall2 = new UebHealthCheckCall(uebServers.get(1), "publicKey");
+ healthCheckCall2.setCambriaHandler(cambriaHandler);
+
+ Boolean call2 = healthCheckCall2.call();
+ assertTrue("check response okay", call2);
+
+ if (uebServers.size() >= 2) {
+ when(cambriaHandler.getApiKey(Mockito.eq(uebServers.get(0)), Mockito.any(String.class))).thenReturn(cambriaErrorResponse);
+ when(cambriaHandler.getApiKey(Mockito.eq(uebServers.get(1)), Mockito.any(String.class))).thenReturn(cambriaOkResponse);
+ }
+ healthCheckCall1 = new UebHealthCheckCall(uebServers.get(0), "publicKey");
+ healthCheckCall1.setCambriaHandler(cambriaHandler);
+
+ call1 = healthCheckCall1.call();
+ assertFalse("check response okay", call1);
+
+ healthCheckCall2 = new UebHealthCheckCall(uebServers.get(1), "publicKey");
+ healthCheckCall2.setCambriaHandler(cambriaHandler);
+
+ call2 = healthCheckCall2.call();
+ assertTrue("check response okay", call2);
+
+ if (uebServers.size() >= 2) {
+ when(cambriaHandler.getApiKey(Mockito.eq(uebServers.get(0)), Mockito.any(String.class))).thenReturn(cambriaErrorResponse);
+ when(cambriaHandler.getApiKey(Mockito.eq(uebServers.get(1)), Mockito.any(String.class))).thenReturn(cambriaNotErrorResponse);
+ }
+ healthCheckCall1 = new UebHealthCheckCall(uebServers.get(0), "publicKey");
+ healthCheckCall1.setCambriaHandler(cambriaHandler);
+
+ call1 = healthCheckCall1.call();
+ assertFalse("check response okay", call1);
+
+ healthCheckCall2 = new UebHealthCheckCall(uebServers.get(1), "publicKey");
+ healthCheckCall2.setCambriaHandler(cambriaHandler);
+
+ call2 = healthCheckCall2.call();
+ assertTrue("check response okay", call2);
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTaskTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTaskTest.java
new file mode 100644
index 0000000000..de7b84fa82
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/distribution/engine/DistributionEngineInitTaskTest.java
@@ -0,0 +1,269 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.distribution.engine;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaErrorResponse;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaHandler;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineInitTask;
+import org.openecomp.sdc.be.components.distribution.engine.SubscriberTypeEnum;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.CreateTopicConfig;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+
+import fj.data.Either;
+
+public class DistributionEngineInitTaskTest {
+
+ @Mock
+ private ComponentsUtils componentsUtils = Mockito.mock(ComponentsUtils.class);
+
+ @Mock
+ private CambriaHandler cambriaHandler = Mockito.mock(CambriaHandler.class);
+
+ // public static final IAuditingDao iAuditingDao =
+ // Mockito.mock(AuditingDao.class);
+
+ @Before
+ public void setup() {
+ // ExternalConfiguration.setAppName("distribEngine1");
+ ExternalConfiguration.setAppName("catalog-be");
+ ExternalConfiguration.setConfigDir("src/test/resources/config");
+ ExternalConfiguration.listenForChanges();
+
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), ExternalConfiguration.getConfigDir() + File.separator + ExternalConfiguration.getAppName());
+
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+ }
+
+ @Test
+ public void checkIncrement() {
+
+ String envName = "PrOD";
+
+ DistributionEngineConfiguration deConfiguration = new DistributionEngineConfiguration();
+ int retry = 2;
+ int maxRetry = 40;
+ deConfiguration.setInitRetryIntervalSec(retry);
+ deConfiguration.setInitMaxIntervalSec(maxRetry);
+ DistributionEngineInitTask initTask = new DistributionEngineInitTask(0l, deConfiguration, envName, new AtomicBoolean(false), componentsUtils, null);
+
+ for (int i = 1; i < 5; i++) {
+ initTask.incrementRetryInterval();
+ assertEquals("check next retry interval", initTask.getCurrentRetryInterval(), retry * (long) Math.pow(2, i));
+ }
+
+ initTask.incrementRetryInterval();
+ assertEquals("check next retry interval reach max retry interval", initTask.getCurrentRetryInterval(), maxRetry);
+
+ }
+
+ @Test
+ public void testInitFlowScenarioSuccess() {
+
+ String notifTopic = "notif";
+ String statusTopic = "status";
+
+ List<String> uebServers = new ArrayList<>();
+ uebServers.add("server1");
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.NOT_FOUND);
+ Either<Set<String>, CambriaErrorResponse> right = Either.right(cambriaErrorResponse);
+ when(cambriaHandler.getTopics(Mockito.any(List.class))).thenReturn(right);
+
+ String envName = "PrOD";
+
+ DistributionEngineConfiguration deConfiguration = new DistributionEngineConfiguration();
+ deConfiguration.setUebServers(uebServers);
+ int retry = 2;
+ int maxRetry = 40;
+ deConfiguration.setInitRetryIntervalSec(retry);
+ deConfiguration.setInitMaxIntervalSec(maxRetry);
+ deConfiguration.setDistributionNotifTopicName(notifTopic);
+ deConfiguration.setDistributionStatusTopicName(statusTopic);
+ CreateTopicConfig createTopic = new CreateTopicConfig();
+ createTopic.setPartitionCount(1);
+ createTopic.setReplicationCount(1);
+ deConfiguration.setCreateTopic(createTopic);
+
+ cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OK);
+
+ String realNotifTopic = notifTopic + "-" + envName.toUpperCase();
+ String realStatusTopic = statusTopic + "-" + envName.toUpperCase();
+ when(cambriaHandler.createTopic(Mockito.any(Collection.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.eq(realNotifTopic), Mockito.eq(1), Mockito.eq(1))).thenReturn(cambriaErrorResponse);
+ when(cambriaHandler.createTopic(Mockito.any(Collection.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.eq(realStatusTopic), Mockito.eq(1), Mockito.eq(1))).thenReturn(cambriaErrorResponse);
+
+ cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OK);
+ when(cambriaHandler.registerToTopic(Mockito.any(Collection.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(SubscriberTypeEnum.class)))
+ .thenReturn(cambriaErrorResponse);
+
+ DistributionEngineInitTask initTask = new DistributionEngineInitTask(0l, deConfiguration, envName, new AtomicBoolean(false), componentsUtils, null);
+ initTask.setCambriaHandler(cambriaHandler);
+
+ boolean initFlow = initTask.initFlow();
+ assertTrue("check init flow succeed", initFlow);
+
+ }
+
+ @Test
+ public void testInitFlowScenarioSuccessTopicsAlreadyExists() {
+
+ String envName = "PrOD";
+ String notifTopic = "notif";
+ String statusTopic = "status";
+
+ String realNotifTopic = notifTopic + "-" + envName.toUpperCase();
+ String realStatusTopic = statusTopic + "-" + envName.toUpperCase();
+
+ Set<String> topics = new HashSet<String>();
+ topics.add(realNotifTopic);
+ topics.add(realStatusTopic);
+
+ List<String> uebServers = new ArrayList<>();
+ uebServers.add("server1");
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.NOT_FOUND);
+ Either<Set<String>, CambriaErrorResponse> left = Either.left(topics);
+
+ when(cambriaHandler.getTopics(Mockito.any(List.class))).thenReturn(left);
+
+ DistributionEngineConfiguration deConfiguration = new DistributionEngineConfiguration();
+ deConfiguration.setUebServers(uebServers);
+ int retry = 2;
+ int maxRetry = 40;
+ deConfiguration.setInitRetryIntervalSec(retry);
+ deConfiguration.setInitMaxIntervalSec(maxRetry);
+ deConfiguration.setDistributionNotifTopicName(notifTopic);
+ deConfiguration.setDistributionStatusTopicName(statusTopic);
+ CreateTopicConfig createTopic = new CreateTopicConfig();
+ createTopic.setPartitionCount(1);
+ createTopic.setReplicationCount(1);
+ deConfiguration.setCreateTopic(createTopic);
+
+ cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OK);
+ when(cambriaHandler.registerToTopic(Mockito.any(Collection.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(SubscriberTypeEnum.class)))
+ .thenReturn(cambriaErrorResponse);
+
+ DistributionEngineInitTask initTask = new DistributionEngineInitTask(0l, deConfiguration, envName, new AtomicBoolean(false), componentsUtils, null);
+ initTask.setCambriaHandler(cambriaHandler);
+
+ try {
+ boolean initFlow = initTask.initFlow();
+ assertTrue("check init flow succeed", initFlow);
+ } catch (Exception e) {
+ assertTrue("Should not throw exception", false);
+ }
+
+ }
+
+ @Test
+ public void testInitFlowScenarioFailToRegister() {
+
+ String notifTopic = "notif";
+ String statusTopic = "status";
+
+ List<String> uebServers = new ArrayList<>();
+ uebServers.add("server1");
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.NOT_FOUND);
+ Either<Set<String>, CambriaErrorResponse> right = Either.right(cambriaErrorResponse);
+ when(cambriaHandler.getTopics(Mockito.any(List.class))).thenReturn(right);
+
+ String envName = "PrOD";
+
+ DistributionEngineConfiguration deConfiguration = new DistributionEngineConfiguration();
+ deConfiguration.setUebServers(uebServers);
+ int retry = 2;
+ int maxRetry = 40;
+ deConfiguration.setInitRetryIntervalSec(retry);
+ deConfiguration.setInitMaxIntervalSec(maxRetry);
+ deConfiguration.setDistributionNotifTopicName(notifTopic);
+ deConfiguration.setDistributionStatusTopicName(statusTopic);
+ CreateTopicConfig createTopic = new CreateTopicConfig();
+ createTopic.setPartitionCount(1);
+ createTopic.setReplicationCount(1);
+ deConfiguration.setCreateTopic(createTopic);
+
+ cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.OK);
+
+ String realNotifTopic = notifTopic + "-" + envName.toUpperCase();
+ String realStatusTopic = statusTopic + "-" + envName.toUpperCase();
+ when(cambriaHandler.createTopic(Mockito.any(Collection.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.eq(realNotifTopic), Mockito.eq(1), Mockito.eq(1))).thenReturn(cambriaErrorResponse);
+ when(cambriaHandler.createTopic(Mockito.any(Collection.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.eq(realStatusTopic), Mockito.eq(1), Mockito.eq(1))).thenReturn(cambriaErrorResponse);
+
+ when(cambriaHandler.registerToTopic(Mockito.any(Collection.class), Mockito.eq(realNotifTopic), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(SubscriberTypeEnum.class)))
+ .thenReturn(new CambriaErrorResponse(CambriaOperationStatus.OK));
+
+ when(cambriaHandler.registerToTopic(Mockito.any(Collection.class), Mockito.eq(realStatusTopic), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(String.class), Mockito.any(SubscriberTypeEnum.class)))
+ .thenReturn(new CambriaErrorResponse(CambriaOperationStatus.CONNNECTION_ERROR));
+
+ DistributionEngineInitTask initTask = new DistributionEngineInitTask(0l, deConfiguration, envName, new AtomicBoolean(false), componentsUtils, null);
+ initTask.setCambriaHandler(cambriaHandler);
+
+ boolean initFlow = initTask.initFlow();
+ assertFalse("check init flow failed", initFlow);
+
+ }
+
+ @Test
+ public void testInitFlowScenario1GetTopicsFailed() {
+
+ List<String> uebServers = new ArrayList<>();
+ uebServers.add("server1");
+ CambriaErrorResponse cambriaErrorResponse = new CambriaErrorResponse(CambriaOperationStatus.CONNNECTION_ERROR);
+ Either<Set<String>, CambriaErrorResponse> right = Either.right(cambriaErrorResponse);
+ when(cambriaHandler.getTopics(Mockito.any(List.class))).thenReturn(right);
+
+ String envName = "PrOD";
+
+ DistributionEngineConfiguration deConfiguration = new DistributionEngineConfiguration();
+ deConfiguration.setUebServers(uebServers);
+ int retry = 2;
+ int maxRetry = 40;
+ deConfiguration.setInitRetryIntervalSec(retry);
+ deConfiguration.setInitMaxIntervalSec(maxRetry);
+ DistributionEngineInitTask initTask = new DistributionEngineInitTask(0l, deConfiguration, envName, new AtomicBoolean(false), componentsUtils, null);
+ initTask.setCambriaHandler(cambriaHandler);
+
+ boolean initFlow = initTask.initFlow();
+ assertFalse("check init flow failed", initFlow);
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ArtifactBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ArtifactBusinessLogicTest.java
new file mode 100644
index 0000000000..0905b8cd19
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ArtifactBusinessLogicTest.java
@@ -0,0 +1,234 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.IUserAdminOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ArtifactOperation;
+import org.openecomp.sdc.be.servlets.RepresentationUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+
+import fj.data.Either;
+
+public class ArtifactBusinessLogicTest {
+
+ static ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), "src/test/resources/config/catalog-be");
+ static ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ @InjectMocks
+ static ArtifactsBusinessLogic artifactBL = new ArtifactsBusinessLogic();
+
+ public static final ArtifactOperation artifactOperation = Mockito.mock(ArtifactOperation.class);
+ public static final ComponentsUtils componentsUtils = Mockito.mock(ComponentsUtils.class);
+ public static final IInterfaceLifecycleOperation lifecycleOperation = Mockito.mock(IInterfaceLifecycleOperation.class);
+ public static final IUserAdminOperation userOperation = Mockito.mock(IUserAdminOperation.class);
+ public static final IElementOperation elementOperation = Mockito.mock(IElementOperation.class);
+ // public static final InformationDeployedArtifactsBusinessLogic
+ // informationDeployedArtifactsBusinessLogic =
+ // Mockito.mock(InformationDeployedArtifactsBusinessLogic.class);
+
+ public static final Resource resource = Mockito.mock(Resource.class);
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ @BeforeClass
+ public static void setup() {
+
+ Either<ArtifactDefinition, StorageOperationStatus> NotFoundResult = Either.right(StorageOperationStatus.NOT_FOUND);
+ when(artifactOperation.getArtifactById(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(NotFoundResult);
+
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> NotFoundResult2 = Either.right(StorageOperationStatus.NOT_FOUND);
+ when(artifactOperation.getArtifacts(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Service), Mockito.anyBoolean())).thenReturn(NotFoundResult2);
+ when(artifactOperation.getArtifacts(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Resource), Mockito.anyBoolean())).thenReturn(NotFoundResult2);
+
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> notFoundInterfaces = Either.right(StorageOperationStatus.NOT_FOUND);
+ when(lifecycleOperation.getAllInterfacesOfResource(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(notFoundInterfaces);
+
+ User userJH = new User("John", "Doh", "jh0003", "jh0003@gmail.com", "ADMIN", System.currentTimeMillis());
+ Either<User, ActionStatus> getUserResult = Either.left(userJH);
+
+ when(userOperation.getUserData("jh0003", false)).thenReturn(getUserResult);
+
+ Either<List<ArtifactType>, ActionStatus> getType = Either.left(getAllTypes());
+ when(elementOperation.getAllArtifactTypes()).thenReturn(getType);
+
+ when(resource.getResourceType()).thenReturn(ResourceTypeEnum.VFC);
+ // when(informationDeployedArtifactsBusinessLogic.getAllDeployableArtifacts(Mockito.any(Resource.class))).thenReturn(new
+ // ArrayList<ArtifactDefinition>());
+ }
+
+ private static List<ArtifactType> getAllTypes() {
+ List<ArtifactType> artifactTypes = new ArrayList<ArtifactType>();
+ List<String> artifactTypesList = ConfigurationManager.getConfigurationManager().getConfiguration().getArtifactTypes();
+ for (String artifactType : artifactTypesList) {
+ ArtifactType artifactT = new ArtifactType();
+ artifactT.setName(artifactType);
+ artifactTypes.add(artifactT);
+ }
+ return artifactTypes;
+ }
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testValidJson() {
+ ArtifactDefinition ad = createArtifactDef();
+
+ String jsonArtifact = gson.toJson(ad);
+
+ ArtifactDefinition afterConvert = RepresentationUtils.convertJsonToArtifactDefinition(jsonArtifact, ArtifactDefinition.class);
+ assertEquals(ad, afterConvert);
+ }
+
+ private ArtifactDefinition createArtifactDef() {
+ ArtifactDefinition ad = new ArtifactDefinition();
+ ad.setArtifactName("artifact1.yaml");
+ ad.setArtifactLabel("label1");
+ ad.setDescription("description");
+ ad.setArtifactType(ArtifactTypeEnum.HEAT.getType());
+ ad.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+ ad.setCreationDate(System.currentTimeMillis());
+ ad.setMandatory(false);
+ ad.setTimeout(15);
+ return ad;
+ }
+
+ @Test
+ public void testInvalidStringGroupType() {
+ ArtifactDefinition ad = new ArtifactDefinition();
+ ad.setArtifactName("artifact1");
+ ad.setCreationDate(System.currentTimeMillis());
+ ad.setMandatory(false);
+ ad.setTimeout(15);
+
+ JsonElement jsonArtifact = gson.toJsonTree(ad);
+ jsonArtifact.getAsJsonObject().addProperty("artifactGroupType", "www");
+
+ ArtifactDefinition afterConvert = RepresentationUtils.convertJsonToArtifactDefinition(jsonArtifact.toString(), ArtifactDefinition.class);
+ assertNull(afterConvert);
+ }
+
+ @Test
+ public void testInvalidNumberGroupType() {
+ ArtifactDefinition ad = new ArtifactDefinition();
+ ad.setArtifactName("artifact1");
+ ad.setCreationDate(System.currentTimeMillis());
+ ad.setMandatory(false);
+ ad.setTimeout(15);
+
+ JsonElement jsonArtifact = gson.toJsonTree(ad);
+ jsonArtifact.getAsJsonObject().addProperty("artifactGroupType", 123);
+
+ ArtifactDefinition afterConvert = RepresentationUtils.convertJsonToArtifactDefinition(jsonArtifact.toString(), ArtifactDefinition.class);
+ assertNull(afterConvert);
+ }
+
+ @Test
+ public void testInvalidGroupTypeWithSpace() {
+ ArtifactDefinition ad = new ArtifactDefinition();
+ ad.setArtifactName("artifact1");
+ ad.setCreationDate(System.currentTimeMillis());
+ ad.setMandatory(false);
+ ad.setTimeout(15);
+
+ JsonElement jsonArtifact = gson.toJsonTree(ad);
+ jsonArtifact.getAsJsonObject().addProperty("artifactGroupType", " DEPLOYMENT");
+
+ ArtifactDefinition afterConvert = RepresentationUtils.convertJsonToArtifactDefinition(jsonArtifact.toString(), ArtifactDefinition.class);
+ assertNull(afterConvert);
+ }
+
+ @Test
+ public void testInvalidTimeoutWithSpace() {
+ ArtifactDefinition ad = new ArtifactDefinition();
+ ad.setArtifactName("artifact1");
+ ad.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+ ad.setCreationDate(System.currentTimeMillis());
+ ad.setMandatory(false);
+
+ JsonElement jsonArtifact = gson.toJsonTree(ad);
+ jsonArtifact.getAsJsonObject().addProperty("timeout", " 15");
+
+ ArtifactDefinition afterConvert = RepresentationUtils.convertJsonToArtifactDefinition(jsonArtifact.toString(), ArtifactDefinition.class);
+ assertNull(afterConvert);
+ }
+
+ // @Test
+ // public void convertAndValidateDeploymentArtifactNonHeatSuccess(){
+ // ArtifactDefinition createArtifactDef = createArtifactDef();
+ // createArtifactDef.setArtifactType(ArtifactTypeEnum.YANG_XML.getType());
+ //
+ // Either<ArtifactDefinition, ResponseFormat> validateResult = artifactBL
+ // .convertAndValidate(resource, "resourceId",
+ // gson.toJson(createArtifactDef), "jh0003", null, null, true,
+ // null, NodeTypeEnum.Resource);
+ //
+ // assertTrue(validateResult.isLeft());
+ // ArtifactDefinition validatedArtifact = validateResult.left().value();
+ //
+ // assertEquals(createArtifactDef.getArtifactGroupType(),
+ // validatedArtifact.getArtifactGroupType());
+ // assertEquals(new Integer(0), validatedArtifact.getTimeout());
+ // assertFalse(validatedArtifact.getMandatory());
+ // assertFalse(validatedArtifact.getServiceApi());
+ //
+ // }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManagerTest.java
new file mode 100644
index 0000000000..776fe8864e
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CapabilityTypeImportManagerTest.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager;
+import org.openecomp.sdc.be.components.impl.CommonImportManager;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.common.util.CapabilityTypeNameEnum;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class CapabilityTypeImportManagerTest {
+ @InjectMocks
+ private CapabilityTypeImportManager manager = new CapabilityTypeImportManager();
+ public static final CommonImportManager commonImportManager = Mockito.mock(CommonImportManager.class);
+ public static final CapabilityTypeOperation capabilityTypeOperation = Mockito.mock(CapabilityTypeOperation.class);
+ public static final ComponentsUtils componentsUtils = Mockito.mock(ComponentsUtils.class);
+
+ @BeforeClass
+ public static void beforeClass() {
+ when(capabilityTypeOperation.addCapabilityType(Mockito.any(CapabilityTypeDefinition.class))).thenAnswer(new Answer<Either<CapabilityTypeDefinition, StorageOperationStatus>>() {
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Either<CapabilityTypeDefinition, StorageOperationStatus> ans = Either.left((CapabilityTypeDefinition) args[0]);
+ return ans;
+ }
+
+ });
+ when(commonImportManager.createElementTypesFromYml(Mockito.anyString(), Mockito.any())).thenCallRealMethod();
+ }
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testCreateCapabilityTypes() throws IOException {
+ String ymlContent = getCapabilityTypesYml();
+ Either<List<CapabilityTypeDefinition>, ResponseFormat> createCapabilityTypes = manager.createCapabilityTypes(ymlContent);
+ assertTrue(createCapabilityTypes.isLeft());
+
+ List<CapabilityTypeDefinition> capabilityTypesList = createCapabilityTypes.left().value();
+ assertTrue(capabilityTypesList.size() == 14);
+ Map<String, CapabilityTypeDefinition> capibilityTypeMap = new HashMap<>();
+ for (CapabilityTypeDefinition capType : capabilityTypesList) {
+ capibilityTypeMap.put(capType.getType(), capType);
+ }
+ assertTrue(capabilityTypesList.size() == 14);
+
+ for (CapabilityTypeNameEnum curr : CapabilityTypeNameEnum.values()) {
+ assertTrue(capibilityTypeMap.containsKey(curr.getCapabilityName()));
+ }
+
+ }
+
+ private String getCapabilityTypesYml() throws IOException {
+ Path filePath = Paths.get("src/test/resources/types/capabilityTypes.yml");
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String ymlContent = new String(fileContent);
+ return ymlContent;
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CategoriesImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CategoriesImportManagerTest.java
new file mode 100644
index 0000000000..05fc6a2948
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CategoriesImportManagerTest.java
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.components.impl.CategoriesImportManager;
+import org.openecomp.sdc.be.components.impl.InterfaceLifecycleTypeImportManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+
+import fj.data.Either;
+
+public class CategoriesImportManagerTest {
+ @InjectMocks
+ static CategoriesImportManager importManager = new CategoriesImportManager();
+ public static final IElementOperation elementOperation = Mockito.mock(IElementOperation.class);
+ public static final ComponentsUtils componentsUtils = Mockito.mock(ComponentsUtils.class);
+
+ static Logger log = Mockito.spy(Logger.class);
+
+ private static SubCategoryDefinition subcategory;
+
+ @BeforeClass
+ public static void beforeClass() throws IOException {
+ InterfaceLifecycleTypeImportManager.setLog(log);
+
+ subcategory = new SubCategoryDefinition();
+ subcategory.setUniqueId("123");
+
+ when(elementOperation.createCategory(Mockito.any(CategoryDefinition.class), Mockito.any(NodeTypeEnum.class))).thenAnswer(new Answer<Either<CategoryDefinition, ActionStatus>>() {
+ public Either<CategoryDefinition, ActionStatus> answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Either<CategoryDefinition, ActionStatus> ans = Either.left((CategoryDefinition) args[0]);
+ return ans;
+ }
+
+ });
+ when(elementOperation.createSubCategory(Mockito.any(String.class), Mockito.any(SubCategoryDefinition.class), Mockito.any(NodeTypeEnum.class))).thenAnswer(new Answer<Either<SubCategoryDefinition, ActionStatus>>() {
+ public Either<SubCategoryDefinition, ActionStatus> answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ // subcategory.setName(((SubCategoryDefinition)args[0]).getName());
+ Either<SubCategoryDefinition, ActionStatus> ans = Either.left(subcategory);
+ return ans;
+ }
+
+ });
+
+ // when(Mockito.any(SubCategoryDefinition.class).getUniqueId()).thenReturn("123");
+ }
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void importCategoriesTest() throws IOException {
+ String ymlContent = getYmlContent();
+ Either<Map<String, List<CategoryDefinition>>, ResponseFormat> createCapabilityTypes = importManager.createCategories(ymlContent);
+ assertTrue(createCapabilityTypes.isLeft());
+
+ }
+
+ private String getYmlContent() throws IOException {
+ Path filePath = Paths.get("src/test/resources/types/categoryTypes.yml");
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String ymlContent = new String(fileContent);
+ return ymlContent;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogicTest.java
new file mode 100644
index 0000000000..135caf2c82
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CompositionBusinessLogicTest.java
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertTrue;
+
+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 org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Test;
+import org.openecomp.sdc.be.components.impl.CompositionBusinessLogic;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.unittests.utils.FactoryUtils;
+
+public class CompositionBusinessLogicTest {
+ CompositionBusinessLogic compBl = new CompositionBusinessLogic();
+
+ @Test
+ public void testBuildSpiralPatternPositioningForComponentInstances() {
+ int instancesNum = 10;
+ Resource createVF = FactoryUtils.createVF();
+ for (int i = 0; i < instancesNum; i++) {
+ FactoryUtils.addComponentInstanceToVF(createVF, FactoryUtils.createResourceInstance());
+ }
+ Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstances = compBl.buildSpiralPatternPositioningForComponentInstances(createVF);
+ assertTrue(componentInstances.size() == instancesNum);
+ // Verify Spiral Pattern
+ ImmutablePair<Double, Double> key;
+ key = new ImmutablePair<>(0D, 0D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(-1D, 0D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(-1D, 1D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(0D, 1D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(1D, 1D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(1D, 0D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(1D, -1D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(0D, -1D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(-1D, -1D);
+ assertTrue(componentInstances.containsKey(key));
+ key = new ImmutablePair<>(-2D, -1D);
+ assertTrue(componentInstances.containsKey(key));
+ }
+
+ @Test
+ public void testGetCpsConnectedToVFC() {
+ List<ComponentInstance> allComponentInstances = new ArrayList<>();
+ Resource createVF = FactoryUtils.createVF();
+ ComponentInstance vfc = populateVfWithVfcAndCps(allComponentInstances, createVF);
+
+ Map<ComponentInstance, List<ComponentInstance>> cpsConnectedToVFC = compBl.getCpsConnectedToVFC(allComponentInstances, createVF);
+ assertTrue(cpsConnectedToVFC.size() == 1);
+ assertTrue(cpsConnectedToVFC.containsKey(vfc));
+ Set<ComponentInstance> cps = cpsConnectedToVFC.get(vfc).stream().collect(Collectors.toSet());
+ assertTrue(cps.size() == 3);
+ cps.stream().forEach(e -> assertTrue(e.getOriginType() == OriginTypeEnum.CP));
+
+ }
+
+ @Test
+ public void testBuildCirclePatternForCps() {
+ List<ComponentInstance> allComponentInstances = new ArrayList<>();
+ Resource createVF = FactoryUtils.createVF();
+ ComponentInstance vfcInstance = populateVfWithVfcAndCps(allComponentInstances, createVF);
+ Map<ComponentInstance, List<ComponentInstance>> cpsConnectedToVFC = compBl.getCpsConnectedToVFC(allComponentInstances, createVF);
+
+ Map<ImmutablePair<Double, Double>, ComponentInstance> componentInstLocations = new HashMap<>();
+ componentInstLocations.put(new ImmutablePair<Double, Double>(0D, 0D), vfcInstance);
+ compBl.buildCirclePatternForCps(componentInstLocations, cpsConnectedToVFC);
+ assertTrue(componentInstLocations.size() == 4);
+
+ Set<ImmutablePair<Double, Double>> cpsLocations = componentInstLocations.entrySet().stream().filter(entry -> entry.getValue().getOriginType() == OriginTypeEnum.CP).map(e -> e.getKey()).collect(Collectors.toSet());
+ // Verify that all cps are located at different positions
+ assertTrue(cpsLocations.size() == 3);
+ Set<Double> distances = cpsLocations.stream().map(cpLocation -> Math.sqrt(Math.pow(cpLocation.left, 2) + Math.pow(cpLocation.right, 2))).collect(Collectors.toSet());
+ // Verify that all cps are at the same distance from center
+ assertTrue(distances.size() == 1);
+
+ }
+
+ /**
+ * Adds 4 instances to the vf.<br>
+ * vfc instance and 3 cps instances.<br>
+ * the cp instances are connected to the vfc instance.<br>
+ *
+ * @param allComponentInstances
+ * @param createVF
+ * @return vfc instance
+ */
+ private ComponentInstance populateVfWithVfcAndCps(List<ComponentInstance> allComponentInstances, Resource createVF) {
+ ComponentInstance vfc = FactoryUtils.createResourceInstance();
+ vfc.setOriginType(OriginTypeEnum.VFC);
+ FactoryUtils.addComponentInstanceToVF(createVF, vfc);
+ allComponentInstances.add(vfc);
+
+ connectCpToVfc(allComponentInstances, createVF, vfc);
+ connectCpToVfc(allComponentInstances, createVF, vfc);
+ connectCpToVfc(allComponentInstances, createVF, vfc);
+ return vfc;
+ }
+
+ private void connectCpToVfc(List<ComponentInstance> allComponentInstances, Resource createVF, ComponentInstance vfc) {
+ List<RequirementCapabilityRelDef> allRelations;
+ if (createVF.getComponentInstancesRelations() != null) {
+ allRelations = createVF.getComponentInstancesRelations();
+ } else {
+ allRelations = new ArrayList<>();
+ createVF.setComponentInstancesRelations(allRelations);
+ }
+ ComponentInstance cp1 = FactoryUtils.createResourceInstance();
+ cp1.setOriginType(OriginTypeEnum.CP);
+ addVfcCpRelation(vfc, cp1, allRelations);
+ FactoryUtils.addComponentInstanceToVF(createVF, cp1);
+ allComponentInstances.add(cp1);
+ }
+
+ private void addVfcCpRelation(ComponentInstance vfc, ComponentInstance cp, List<RequirementCapabilityRelDef> allRelations) {
+ RequirementCapabilityRelDef rel = new RequirementCapabilityRelDef();
+ rel.setToNode(vfc.getComponentUid());
+ rel.setFromNode(cp.getComponentUid());
+ allRelations.add(rel);
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CsarValidationUtilsTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CsarValidationUtilsTest.java
new file mode 100644
index 0000000000..84c8286f14
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/CsarValidationUtilsTest.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import java.util.Arrays;
+import org.junit.Assert;
+import org.junit.Test;
+import org.openecomp.sdc.be.components.impl.CsarValidationUtils;
+
+public class CsarValidationUtilsTest {
+ private String[] invalidExtensions = { null, ".bla", ".yaml", ".yml", ".txt", ".zip" };
+ private String[] validExtensions = { ".csar", ".cSAr", ".Csar", ".CSAR" };
+
+ @Test
+ public void testIsCsarPayloadName() {
+ Arrays.stream(invalidExtensions).forEach(e -> Assert.assertFalse(CsarValidationUtils.isCsarPayloadName(e)));
+ Arrays.stream(validExtensions).forEach(e -> Assert.assertTrue(CsarValidationUtils.isCsarPayloadName(e)));
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ImportUtilsTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ImportUtilsTest.java
new file mode 100644
index 0000000000..6dd19cce57
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ImportUtilsTest.java
@@ -0,0 +1,509 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Function;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.components.impl.ImportUtils;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaElementTypeEnum;
+import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.yaml.snakeyaml.Yaml;
+
+import fj.data.Either;
+
+public class ImportUtilsTest {
+ @Test
+ public void testStringTypeFindToscaElements() throws IOException {
+ Either<List<Object>, ResultStatusEnum> toscaElements = ImportUtils.findToscaElements((Map<String, Object>) loadJsonFromFile("normative-types-string-list-test.yml"), "stringTestTag", ToscaElementTypeEnum.STRING, new ArrayList<>());
+ assertTrue(toscaElements.isLeft());
+ List<Object> list = toscaElements.left().value();
+ assertTrue(list.size() == 4);
+ int count = 1;
+ for (Object element : list) {
+ assertTrue(element instanceof String);
+ String value = (String) element;
+ assertTrue(value.equals("stringVal" + count));
+ count++;
+ }
+ }
+
+ @Test
+ public void testBooleanTypeFindToscaElements() throws IOException {
+ Either<List<Object>, ResultStatusEnum> toscaElements = ImportUtils.findToscaElements((Map<String, Object>) loadJsonFromFile("normative-types-all-map-test.yml"), "required", ToscaElementTypeEnum.BOOLEAN, new ArrayList<>());
+ assertTrue(toscaElements.isLeft());
+ List<Object> list = toscaElements.left().value();
+ assertTrue(list.size() == 3);
+ int count = 1;
+ for (Object element : list) {
+ assertTrue(element instanceof Boolean);
+ Boolean value = (Boolean) element;
+ if (count == 1 || count == 3) {
+ assertFalse(value);
+ } else if (count == 2) {
+ assertTrue(value);
+ }
+
+ count++;
+ }
+ }
+
+ @Test
+ public void testListTypeFindToscaElements() throws IOException {
+ Either<List<Object>, ResultStatusEnum> toscaElements = ImportUtils.findToscaElements((Map<String, Object>) loadJsonFromFile("normative-types-string-list-test.yml"), "listTestTag", ToscaElementTypeEnum.LIST, new ArrayList<>());
+ assertTrue(toscaElements.isLeft());
+ List<Object> list = toscaElements.left().value();
+ assertTrue(list.size() == 3);
+ int count = 1;
+ for (Object element : list) {
+ assertTrue(element instanceof List);
+
+ if (count == 1) {
+ verifyListElement1(element);
+ } else if (count == 2) {
+ verifyListElement2(element);
+ }
+
+ else if (count == 3) {
+ verifyListElement3(element);
+ }
+ count++;
+ }
+ }
+
+ @Test
+ public void testAllTypeFindToscaElements() throws IOException {
+ Either<List<Object>, ResultStatusEnum> toscaElements = ImportUtils.findToscaElements((Map<String, Object>) loadJsonFromFile("normative-types-all-map-test.yml"), "allTestTag", ToscaElementTypeEnum.ALL, new ArrayList<>());
+ assertTrue(toscaElements.isLeft());
+ List<Object> list = toscaElements.left().value();
+ assertTrue(list.size() == 5);
+ int count = 1;
+ for (Object element : list) {
+ if (count == 1) {
+ assertTrue(element instanceof String);
+ assertTrue(element.equals("tosca.nodes.Root"));
+ } else if (count == 2) {
+ assertTrue(element instanceof Map);
+ Map<String, Object> mapElement = (Map<String, Object>) element;
+ assertTrue(mapElement.size() == 2);
+ Iterator<Entry<String, Object>> elementEntries = mapElement.entrySet().iterator();
+ Entry<String, Object> elementEntry = elementEntries.next();
+ assertTrue(elementEntry.getKey().equals("mapTestTag"));
+ assertTrue(elementEntry.getValue().equals("string"));
+
+ elementEntry = elementEntries.next();
+ assertTrue(elementEntry.getKey().equals("required"));
+ assertTrue(elementEntry.getValue() instanceof Boolean);
+ assertTrue((Boolean) elementEntry.getValue());
+ }
+
+ else if (count == 3) {
+ assertTrue(element instanceof String);
+ assertTrue(element.equals("1 MB"));
+ }
+
+ else if (count == 4) {
+ assertTrue(element instanceof List);
+ List<Object> listElement = (List<Object>) element;
+ assertTrue(listElement.size() == 2);
+
+ assertTrue(listElement.get(0) instanceof Map);
+ Map<String, Object> innerElement = (Map<String, Object>) listElement.get(0);
+ assertTrue(innerElement.size() == 1);
+ Entry<String, Object> innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("greater_or_equal"));
+ assertTrue(innerEntry.getValue().equals("1 MB"));
+
+ assertTrue(listElement.get(1) instanceof Map);
+ innerElement = (Map<String, Object>) listElement.get(1);
+ assertTrue(innerElement.size() == 1);
+ innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("stringTestTag"));
+ assertTrue(innerEntry.getValue().equals("stringVal3"));
+ } else if (count == 5) {
+ assertTrue(element instanceof Boolean);
+ assertFalse((Boolean) element);
+ }
+ count++;
+ }
+ }
+
+ @Test
+ public void testMapTypeFindToscaElements() throws IOException {
+ Either<List<Object>, ResultStatusEnum> toscaElements = ImportUtils.findToscaElements((Map<String, Object>) loadJsonFromFile("normative-types-all-map-test.yml"), "mapTestTag", ToscaElementTypeEnum.MAP, new ArrayList<>());
+ assertTrue(toscaElements.isLeft());
+ List<Object> list = toscaElements.left().value();
+ assertTrue(list.size() == 2);
+ int count = 1;
+ for (Object element : list) {
+ assertTrue(element instanceof Map);
+
+ if (count == 1) {
+ Map<String, Object> mapElement = (Map<String, Object>) element;
+ assertTrue(mapElement.size() == 2);
+ Iterator<Entry<String, Object>> iterator = mapElement.entrySet().iterator();
+ Entry<String, Object> inerElementEntry = iterator.next();
+ assertTrue(inerElementEntry.getKey().equals("stringTestTag"));
+ assertTrue(inerElementEntry.getValue().equals("stringVal1"));
+
+ inerElementEntry = iterator.next();
+ assertTrue(inerElementEntry.getKey().equals("listTestTag"));
+ assertTrue(inerElementEntry.getValue() instanceof List);
+ List<Object> innerValue = (List<Object>) inerElementEntry.getValue();
+
+ assertTrue(innerValue.size() == 3);
+
+ } else if (count == 2) {
+ Map<String, Object> mapElement = (Map<String, Object>) element;
+ assertTrue(mapElement.size() == 2);
+ Iterator<Entry<String, Object>> entryItr = mapElement.entrySet().iterator();
+ Entry<String, Object> inerElementEntry = entryItr.next();
+ assertTrue(inerElementEntry.getKey().equals("type"));
+ assertTrue(inerElementEntry.getValue().equals("tosca.capabilities.Attachment"));
+ inerElementEntry = entryItr.next();
+ assertTrue(inerElementEntry.getKey().equals("allTestTag"));
+ assertTrue(inerElementEntry.getValue() instanceof Boolean);
+ }
+
+ count++;
+ }
+ }
+
+ @Test
+ public void testCreateFullHeatParameterModuleWithString() {
+
+ testCreateFullHeatParameterModule("string", "default value");
+
+ }
+
+ @Test
+ public void testCreateFullHeatParameterModuleWithNumber() {
+
+ testCreateFullHeatParameterModule("number", "777");
+ testCreateFullHeatParameterModule("number", "777.23");
+
+ }
+
+ @Test
+ public void testCreateFullHeatParameterModuleWithBoolean() {
+
+ testCreateFullHeatParameterModule("boolean", "true");
+ testCreateFullHeatParameterModule("boolean", "on");
+ testCreateFullHeatParameterModule("boolean", "n");
+
+ }
+
+ @Test
+ public void testCreateFullHeatParameterModuleWithList() {
+
+ testCreateFullHeatParameterModule("comma_delimited_list", "[one, two]");
+
+ }
+
+ // @Test
+ // public void testCreateFullHeatParameterModuleWithInvalidType(){
+ //
+ // String name = "fullParameter";
+ // String description = "description_text";
+ //
+ // Map<String, Object> parametersMap = new HashMap<String, Object>();
+ // Map<String, Object> firstParam = createParameterMap("aaa", "aaa",
+ // name, description);
+ // parametersMap.put(ToscaTagNamesEnum.PARAMETERS.getElementName(),
+ // firstParam);
+ //
+ // Either<List<HeatParameterDefinition>,ResultStatusEnum> heatParameters =
+ // ImportUtils.getHeatParameters(parametersMap);
+ // assertTrue(heatParameters.isRight());
+ // assertEquals(ResultStatusEnum.INVALID_PROPERTY_TYPE,
+ // heatParameters.right().value());
+ //
+ // }
+
+ @Test
+ public void testCreateFullHeatParameterModuleWithMissingType() {
+
+ String name = "fullParameter";
+ String description = "description_text";
+
+ Map<String, Object> parametersMap = new HashMap<String, Object>();
+ Map<String, Object> firstParam = createParameterMap(null, "aaa", name, description);
+ parametersMap.put(ToscaTagNamesEnum.PARAMETERS.getElementName(), firstParam);
+
+ Either<List<HeatParameterDefinition>, ResultStatusEnum> heatParameters = ImportUtils.getHeatParameters(parametersMap, ArtifactTypeEnum.HEAT.getType());
+ assertTrue(heatParameters.isRight());
+ assertEquals(ResultStatusEnum.INVALID_PROPERTY_TYPE, heatParameters.right().value());
+
+ }
+
+ @Test
+ public void testCreateFullHeatParameterModuleWithMissingFields() {
+
+ String name = "fullParameter";
+
+ Map<String, Object> parametersMap = new HashMap<String, Object>();
+ String type = "number";
+ String defValue = "defvalue";
+ // default value cannot be empty in heat in case tag exists
+ Map<String, Object> firstParam = createParameterMap(type, defValue, name, null);
+ parametersMap.put(ToscaTagNamesEnum.PARAMETERS.getElementName(), firstParam);
+
+ Either<List<HeatParameterDefinition>, ResultStatusEnum> heatParameters = ImportUtils.getHeatParameters(parametersMap, ArtifactTypeEnum.HEAT.getType());
+ assertTrue(heatParameters.isLeft());
+ List<HeatParameterDefinition> parameterDefList = heatParameters.left().value();
+ assertFalse(parameterDefList.isEmpty());
+ HeatParameterDefinition parameterDefinition = parameterDefList.get(0);
+
+ assertParameter(parameterDefinition, name, type, null, defValue);
+
+ }
+
+ @Test
+ public void testGetAttributesFromYml() throws IOException {
+
+ Map<String, Object> toscaJson = (Map<String, Object>) loadJsonFromFile("importToscaWithAttribute.yml");
+ Either<Map<String, AttributeDefinition>, ResultStatusEnum> actualAttributes = ImportUtils.getAttributes(toscaJson);
+ assertTrue(actualAttributes.isLeft());
+ Map<String, Map<String, Object>> expectedAttributes = getElements(toscaJson, ToscaTagNamesEnum.ATTRIBUTES);
+ compareAttributes(expectedAttributes, actualAttributes.left().value());
+
+ }
+
+ @Test
+ public void testGetPropertiesFromYml() throws IOException {
+
+ Map<String, Object> toscaJson = (Map<String, Object>) loadJsonFromFile("importToscaProperties.yml");
+ Either<Map<String, PropertyDefinition>, ResultStatusEnum> actualProperties = ImportUtils.getProperties(toscaJson);
+ assertTrue(actualProperties.isLeft());
+ Map<String, Map<String, Object>> expectedProperties = getElements(toscaJson, ToscaTagNamesEnum.PROPERTIES);
+ compareProperties(expectedProperties, actualProperties.left().value());
+
+ }
+
+ private void compareAttributes(Map<String, Map<String, Object>> expected, Map<String, AttributeDefinition> actual) {
+
+ Map<String, Object> singleExpectedAttribute;
+ AttributeDefinition actualAttribute, expectedAttributeModel;
+ // attributes of resource
+ for (Map.Entry<String, Map<String, Object>> expectedAttribute : expected.entrySet()) {
+
+ singleExpectedAttribute = expectedAttribute.getValue();
+ assertNotNull(singleExpectedAttribute);
+ actualAttribute = actual.get(expectedAttribute.getKey());
+ assertNotNull(actualAttribute);
+ actualAttribute.setName(expectedAttribute.getKey().toString());
+ expectedAttributeModel = ImportUtils.createModuleAttribute(singleExpectedAttribute);
+ expectedAttributeModel.setName(expectedAttribute.getKey().toString());
+
+ assertEquals(expectedAttributeModel.getDefaultValue(), actualAttribute.getDefaultValue());
+ assertEquals(expectedAttributeModel.getDescription(), actualAttribute.getDescription());
+ assertEquals(expectedAttributeModel.getName(), actualAttribute.getName());
+ assertEquals(expectedAttributeModel.getStatus(), actualAttribute.getStatus());
+ assertEquals(expectedAttributeModel.getType(), actualAttribute.getType());
+
+ compareSchemas(expectedAttributeModel.getSchema(), actualAttribute.getSchema());
+
+ }
+
+ }
+
+ private void compareProperties(Map<String, Map<String, Object>> expected, Map<String, PropertyDefinition> actual) {
+
+ Map<String, Object> singleExpectedProperty;
+ PropertyDefinition actualProperty, expectedPropertyModel;
+ // attributes of resource
+ for (Map.Entry<String, Map<String, Object>> expectedProperty : expected.entrySet()) {
+
+ singleExpectedProperty = expectedProperty.getValue();
+ assertNotNull(singleExpectedProperty);
+ actualProperty = actual.get(expectedProperty.getKey());
+ assertNotNull(actualProperty);
+ actualProperty.setName(expectedProperty.getKey().toString());
+ expectedPropertyModel = ImportUtils.createModuleProperty(singleExpectedProperty);
+ expectedPropertyModel.setName(expectedProperty.getKey().toString());
+
+ assertEquals(expectedPropertyModel.getDefaultValue(), actualProperty.getDefaultValue());
+ assertEquals(expectedPropertyModel.getDescription(), actualProperty.getDescription());
+ assertEquals(expectedPropertyModel.getName(), actualProperty.getName());
+ assertEquals(expectedPropertyModel.getStatus(), actualProperty.getStatus());
+ assertEquals(expectedPropertyModel.getType(), actualProperty.getType());
+
+ compareSchemas(expectedPropertyModel.getSchema(), actualProperty.getSchema());
+
+ }
+
+ }
+
+ private void compareSchemas(SchemaDefinition expected, SchemaDefinition actual) {
+
+ if (expected == null && actual == null) {
+ return;
+ }
+ PropertyDataDefinition actualPropertySchema = actual.getProperty();
+ PropertyDataDefinition expectedPropertySchema = expected.getProperty();
+ assertNotNull(actualPropertySchema);
+ assertNotNull(expectedPropertySchema);
+ assertEquals(expectedPropertySchema.getDescription(), actualPropertySchema.getDescription());
+ assertEquals(expectedPropertySchema.getType(), actualPropertySchema.getType());
+
+ }
+
+ private <T> Map<String, T> getElements(Map<String, Object> toscaJson, ToscaTagNamesEnum elementType) {
+
+ Either<Map<String, T>, ResultStatusEnum> toscaExpectedElements = ImportUtils.findFirstToscaMapElement(toscaJson, elementType);
+ assertTrue(toscaExpectedElements.isLeft());
+
+ return toscaExpectedElements.left().value();
+
+ }
+
+ private void testCreateFullHeatParameterModule(String type, Object defaultVal) {
+
+ String name = "fullParameter";
+ String description = "description_text";
+
+ Map<String, Object> parametersMap = new HashMap<String, Object>();
+ Map<String, Object> firstParam = createParameterMap(type, defaultVal, name, description);
+ parametersMap.put(ToscaTagNamesEnum.PARAMETERS.getElementName(), firstParam);
+
+ Either<List<HeatParameterDefinition>, ResultStatusEnum> heatParameters = ImportUtils.getHeatParameters(parametersMap, ArtifactTypeEnum.HEAT.getType());
+ assertTrue(heatParameters.isLeft());
+ List<HeatParameterDefinition> parameterDefList = heatParameters.left().value();
+ assertFalse(parameterDefList.isEmpty());
+ HeatParameterDefinition parameterDefinition = parameterDefList.get(0);
+
+ assertParameter(parameterDefinition, name, type, description, defaultVal);
+
+ }
+
+ private Map<String, Object> createParameterMap(String type, Object defaultVal, String name, String description) {
+ Map<String, Object> firstParam = new HashMap<String, Object>();
+ Map<String, Object> valuesMap = new HashMap<String, Object>();
+
+ valuesMap.put(ToscaTagNamesEnum.TYPE.getElementName(), type);
+ valuesMap.put(ToscaTagNamesEnum.DESCRIPTION.getElementName(), description);
+ valuesMap.put(ToscaTagNamesEnum.DEFAULT_VALUE.getElementName(), defaultVal);
+
+ firstParam.put(name, valuesMap);
+ return firstParam;
+ }
+
+ private void assertParameter(HeatParameterDefinition parameterDefinition, String name, String type, String description, Object defaultVal) {
+ assertEquals(name, parameterDefinition.getName());
+ assertEquals(description, parameterDefinition.getDescription());
+ assertEquals(type, parameterDefinition.getType());
+ assertEquals(String.valueOf(defaultVal), parameterDefinition.getDefaultValue());
+ assertEquals(String.valueOf(defaultVal), parameterDefinition.getCurrentValue());
+ }
+
+ private void verifyListElement3(Object element) {
+ List<Object> listElement = (List<Object>) element;
+ assertTrue(listElement.size() == 2);
+
+ Map<String, String> innerElement = (Map<String, String>) listElement.get(0);
+ assertTrue(innerElement.size() == 1);
+ Entry<String, String> innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("testTag1"));
+ assertTrue(innerEntry.getValue().equals("1 MB"));
+
+ innerElement = (Map<String, String>) listElement.get(1);
+ assertTrue(innerElement.size() == 1);
+ innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("type"));
+ assertTrue(innerEntry.getValue().equals("stringVal2"));
+ }
+
+ private void verifyListElement2(Object element) {
+ List<Object> listElement = (List<Object>) element;
+ assertTrue(listElement.size() == 2);
+
+ Map<String, Object> innerElement = (Map<String, Object>) listElement.get(0);
+ assertTrue(innerElement.size() == 1);
+ Entry<String, Object> innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("testTag1"));
+ assertTrue(innerEntry.getValue().equals("1 MB"));
+
+ assertTrue(listElement.get(1) instanceof Map);
+ innerElement = (Map<String, Object>) listElement.get(1);
+ assertTrue(innerElement.size() == 1);
+ innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("listTestTag"));
+ assertTrue(innerEntry.getValue() instanceof List);
+ }
+
+ private void verifyListElement1(Object element) {
+ List<Object> listElement = (List<Object>) element;
+ assertTrue(listElement.size() == 3);
+
+ Map<String, String> innerElement = (Map<String, String>) listElement.get(0);
+ assertTrue(innerElement.size() == 1);
+ Entry<String, String> innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("listTestTag"));
+ assertTrue(innerEntry.getValue().equals("1 MB"));
+
+ innerElement = (Map<String, String>) listElement.get(1);
+ assertTrue(innerElement.size() == 1);
+ innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("listTestTag"));
+ assertTrue(innerEntry.getValue().equals("2 MB"));
+
+ innerElement = (Map<String, String>) listElement.get(2);
+ assertTrue(innerElement.size() == 1);
+ innerEntry = innerElement.entrySet().iterator().next();
+ assertTrue(innerEntry.getKey().equals("stringTestTag"));
+ assertTrue(innerEntry.getValue().equals("stringVal2"));
+ }
+
+ public static String loadFileNameToJsonString(String fileName) throws IOException {
+ String sourceDir = "src/test/resources/normativeTypes";
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(sourceDir, fileName);
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String content = new String(fileContent);
+ return content;
+ }
+
+ private static Object loadJsonFromFile(String fileName) throws IOException {
+ String content = loadFileNameToJsonString(fileName);
+ Object load = new Yaml().load(content);
+ return load;
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManagerTest.java
new file mode 100644
index 0000000000..6ba74ec133
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/InterfaceLifecycleTypeImportManagerTest.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.components.impl.CommonImportManager;
+import org.openecomp.sdc.be.components.impl.InterfaceLifecycleTypeImportManager;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+
+import fj.data.Either;
+
+public class InterfaceLifecycleTypeImportManagerTest {
+
+ @InjectMocks
+ private InterfaceLifecycleTypeImportManager importManager = new InterfaceLifecycleTypeImportManager();
+ public static final CommonImportManager commonImportManager = Mockito.mock(CommonImportManager.class);
+ public static final IInterfaceLifecycleOperation interfaceLifecycleOperation = Mockito.mock(IInterfaceLifecycleOperation.class);
+ public static final ComponentsUtils componentsUtils = Mockito.mock(ComponentsUtils.class);
+
+ static Logger log = Mockito.spy(Logger.class);
+
+ @BeforeClass
+ public static void beforeClass() throws IOException {
+ InterfaceLifecycleTypeImportManager.setLog(log);
+ when(interfaceLifecycleOperation.createInterfaceType(Mockito.any(InterfaceDefinition.class))).thenAnswer(new Answer<Either<InterfaceDefinition, StorageOperationStatus>>() {
+ public Either<InterfaceDefinition, StorageOperationStatus> answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Either<InterfaceDefinition, StorageOperationStatus> ans = Either.left((InterfaceDefinition) args[0]);
+ return ans;
+ }
+
+ });
+ when(commonImportManager.createElementTypesFromYml(Mockito.anyString(), Mockito.any())).thenCallRealMethod();
+ }
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void importLiecycleTest() throws IOException {
+ String ymlContent = getYmlContent();
+ Either<List<InterfaceDefinition>, ResponseFormat> createCapabilityTypes = importManager.createLifecycleTypes(ymlContent);
+ assertTrue(createCapabilityTypes.isLeft());
+
+ }
+
+ private String getYmlContent() throws IOException {
+ Path filePath = Paths.get("src/test/resources/types/interfaceLifecycleTypes.yml");
+ byte[] fileContent = Files.readAllBytes(filePath);
+ String ymlContent = new String(fileContent);
+ return ymlContent;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java
new file mode 100644
index 0000000000..5a503dc97c
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java
@@ -0,0 +1,1423 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.ElementOperationMock;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.auditing.impl.AuditingLogFormatUtil;
+import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.impl.CsarOperation;
+import org.openecomp.sdc.be.model.operations.impl.GraphLockOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class ResourceBusinessLogicTest {
+
+ private static Logger log = LoggerFactory.getLogger(ResourceBusinessLogicTest.class.getName());
+ public static final String RESOURCE_CATEGORY = "Network Layer 2-3/Router";
+ public static final String RESOURCE_CATEGORY1 = "Network Layer 2-3";
+ public static final String RESOURCE_SUBCATEGORY = "Router";
+
+ public static final String UPDATED_CATEGORY = "Network Layer 2-3/Gateway";
+ public static final String UPDATED_SUBCATEGORY = "Gateway";
+
+ public static final String RESOURCE_NAME = "My-Resource_Name with space";
+
+ final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ IAuditingManager iAuditingManager = null;
+ IElementOperation mockElementDao;
+ TitanGenericDao mockTitanDao = Mockito.mock(TitanGenericDao.class);
+ UserBusinessLogic mockUserAdmin = Mockito.mock(UserBusinessLogic.class);
+ final ResourceOperation resourceOperation = Mockito.mock(ResourceOperation.class);
+ final LifecycleBusinessLogic lifecycleBl = Mockito.mock(LifecycleBusinessLogic.class);
+ final ICapabilityTypeOperation capabilityTypeOperation = Mockito.mock(ICapabilityTypeOperation.class);
+ final IPropertyOperation propertyOperation = Mockito.mock(IPropertyOperation.class);
+ final ApplicationDataTypeCache applicationDataTypeCache = Mockito.mock(ApplicationDataTypeCache.class);
+ WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ WebApplicationContext webAppContext = Mockito.mock(WebApplicationContext.class);
+ AuditingLogFormatUtil auditingLogFormatter = Mockito.mock(AuditingLogFormatUtil.class);
+ @InjectMocks
+ ResourceBusinessLogic bl = new ResourceBusinessLogic();
+ ResponseFormatManager responseManager = null;
+ AuditingManager auditingManager = Mockito.mock(AuditingManager.class);
+ GraphLockOperation graphLockOperation = Mockito.mock(GraphLockOperation.class);
+ User user = null;
+ Resource resourceResponse = null;
+ ComponentsUtils componentsUtils = new ComponentsUtils();
+ ArtifactsBusinessLogic artifactManager = Mockito.mock(ArtifactsBusinessLogic.class);
+ CsarOperation csarOperation = Mockito.mock(CsarOperation.class);
+ Map<String, DataTypeDefinition> emptyDataTypes = new HashMap<String, DataTypeDefinition>();
+
+ CacheMangerOperation cacheManager = Mockito.mock(CacheMangerOperation.class);
+
+ public ResourceBusinessLogicTest() {
+
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ Mockito.reset(propertyOperation);
+
+ ExternalConfiguration.setAppName("catalog-be");
+
+ // Init Configuration
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ // Elements
+ mockElementDao = new ElementOperationMock();
+
+ // User data and management
+ user = new User();
+ user.setUserId("jh0003");
+ user.setFirstName("Jimmi");
+ user.setLastName("Hendrix");
+ user.setRole(Role.ADMIN.name());
+
+ Either<User, ActionStatus> eitherGetUser = Either.left(user);
+ when(mockUserAdmin.getUser("jh0003", false)).thenReturn(eitherGetUser);
+
+ // Servlet Context attributes
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ // when(servletContext.getAttribute(Constants.AUDITING_MANAGER)).thenReturn(iAuditingManager);
+ when(servletContext.getAttribute(Constants.RESOURCE_OPERATION_MANAGER)).thenReturn(resourceOperation);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webAppContext);
+ when(webAppContext.getBean(IElementOperation.class)).thenReturn(mockElementDao);
+
+ // Resource Operation mock methods
+ // getCount
+ /*
+ * Either<Integer, StorageOperationStatus> eitherCount = Either.left(0); when(resourceOperation.getNumberOfResourcesByName("MyResourceName")). thenReturn(eitherCount); Either<Integer, StorageOperationStatus> eitherCountExist = Either.left(1);
+ * when(resourceOperation.getNumberOfResourcesByName("alreadyExist")). thenReturn(eitherCountExist);
+ */
+ Either<Integer, StorageOperationStatus> eitherCountRoot = Either.left(1);
+ when(resourceOperation.getNumberOfResourcesByName("Root".toLowerCase())).thenReturn(eitherCountRoot);
+ Either<Boolean, StorageOperationStatus> eitherFalse = Either.left(false);
+ when(resourceOperation.validateResourceNameExists(ValidationUtils.normaliseComponentName("Root"), ResourceTypeEnum.VFC)).thenReturn(eitherFalse);
+
+ Either<Boolean, StorageOperationStatus> eitherCountExist = Either.left(false);
+ when(resourceOperation.validateResourceNameExists("alreadyExists", ResourceTypeEnum.VFC)).thenReturn(eitherCountExist);
+ Either<Boolean, StorageOperationStatus> eitherCount = Either.left(true);
+ when(resourceOperation.validateResourceNameExists(RESOURCE_NAME, ResourceTypeEnum.VFC)).thenReturn(eitherCount);
+
+ Either<Boolean, StorageOperationStatus> validateDerivedExists = Either.left(false);
+ when(resourceOperation.validateToscaResourceNameExists("Root")).thenReturn(validateDerivedExists);
+ Either<Boolean, StorageOperationStatus> validateDerivedNotExists = Either.left(true);
+ when(resourceOperation.validateToscaResourceNameExists(ValidationUtils.normaliseComponentName("kuku"))).thenReturn(validateDerivedNotExists);
+
+ when(graphLockOperation.lockComponent(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Resource))).thenReturn(StorageOperationStatus.OK);
+ when(graphLockOperation.lockComponentByName(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Resource))).thenReturn(StorageOperationStatus.OK);
+
+ ArtifactDefinition artifactDef = new ArtifactDefinition();
+ artifactDef.setUniqueId("123.123");
+ Either<ArtifactDefinition, StorageOperationStatus> returnEither = Either.left(artifactDef);
+ when(artifactManager.createArtifactPlaceHolderInfo(Mockito.anyString(), Mockito.anyString(), Mockito.anyMap(), Mockito.any(User.class), Mockito.any(ArtifactGroupTypeEnum.class))).thenReturn(artifactDef);
+
+ when(artifactManager.addHeatEnvArtifact(Mockito.any(ArtifactDefinition.class), Mockito.any(ArtifactDefinition.class), Mockito.anyString(), Mockito.any(NodeTypeEnum.class), Mockito.anyBoolean())).thenReturn(returnEither);
+
+ // createResource
+ resourceResponse = createResourceObject(true);
+ Either<Resource, StorageOperationStatus> eitherCreate = Either.left(resourceResponse);
+ Either<List<ResourceMetadataData>, StorageOperationStatus> eitherValidate = Either.left(null);
+ when(resourceOperation.createResource(Mockito.any(Resource.class), Mockito.anyBoolean())).thenReturn(eitherCreate);
+ when(resourceOperation.validateCsarUuidUniqueness(Mockito.anyString())).thenReturn(eitherValidate);
+ Map<String, DataTypeDefinition> emptyDataTypes = new HashMap<String, DataTypeDefinition>();
+ when(applicationDataTypeCache.getAll()).thenReturn(Either.left(emptyDataTypes));
+
+ // BL object
+ bl = new ResourceBusinessLogic();
+ bl.setElementDao(mockElementDao);
+ bl.setUserAdmin(mockUserAdmin);
+ bl.setResourceOperation(resourceOperation);
+ bl.setCapabilityTypeOperation(capabilityTypeOperation);
+ componentsUtils.Init();
+ componentsUtils.setAuditingManager(auditingManager);
+ bl.setComponentsUtils(componentsUtils);
+ bl.setLifecycleManager(lifecycleBl);
+ bl.setGraphLockOperation(graphLockOperation);
+ bl.setArtifactsManager(artifactManager);
+ bl.setPropertyOperation(propertyOperation);
+ bl.setTitanGenericDao(mockTitanDao);
+ bl.setApplicationDataTypeCache(applicationDataTypeCache);
+ bl.setCsarOperation(csarOperation);
+ bl.setCacheManagerOperation(cacheManager);
+
+ Resource resourceCsar = createResourceObjectCsar(true);
+ setCanWorkOnResource(resourceCsar);
+ Either<Resource, StorageOperationStatus> oldResourceRes = Either.left(resourceCsar);
+ when(resourceOperation.getResource(resourceCsar.getUniqueId())).thenReturn(oldResourceRes);
+ when(resourceOperation.getLatestResourceByCsarOrName(resourceCsar.getCsarUUID(), resourceCsar.getSystemName())).thenReturn(oldResourceRes);
+ when(resourceOperation.updateResource(Mockito.any(Resource.class), Mockito.anyBoolean())).thenReturn(oldResourceRes);
+ responseManager = ResponseFormatManager.getInstance();
+
+ }
+
+ private Resource createResourceObject(boolean afterCreate) {
+ Resource resource = new Resource();
+ resource.setName(RESOURCE_NAME);
+ resource.addCategory(RESOURCE_CATEGORY1, RESOURCE_SUBCATEGORY);
+ resource.setDescription("My short description");
+ List<String> tgs = new ArrayList<String>();
+ tgs.add("test");
+ tgs.add(resource.getName());
+ resource.setTags(tgs);
+ List<String> template = new ArrayList<String>();
+ template.add("Root");
+ resource.setDerivedFrom(template);
+ resource.setVendorName("Motorola");
+ resource.setVendorRelease("1.0.0");
+ resource.setContactId("ya5467");
+ resource.setIcon("MyIcon");
+
+ if (afterCreate) {
+ resource.setName(resource.getName());
+ resource.setVersion("0.1");
+ ;
+ resource.setUniqueId(resource.getName().toLowerCase() + ":" + resource.getVersion());
+ resource.setCreatorUserId(user.getUserId());
+ resource.setCreatorFullName(user.getFirstName() + " " + user.getLastName());
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ log.debug(gson.toJson(resource));
+ return resource;
+ }
+
+ private Resource createResourceObjectCsar(boolean afterCreate) {
+ Resource resource = new Resource();
+ resource.setName(RESOURCE_NAME);
+ resource.addCategory(RESOURCE_CATEGORY1, RESOURCE_SUBCATEGORY);
+ resource.setDescription("My short description");
+ List<String> tgs = new ArrayList<String>();
+ tgs.add("test");
+ tgs.add(resource.getName());
+ resource.setTags(tgs);
+ List<String> template = new ArrayList<String>();
+ template.add("Root");
+ resource.setDerivedFrom(template);
+ resource.setVendorName("Motorola");
+ resource.setVendorRelease("1.0.0");
+ resource.setContactId("ya5467");
+ resource.setIcon("MyIcon");
+ resource.setCsarUUID("valid_vf.csar");
+ resource.setCsarVersion("1");
+
+ if (afterCreate) {
+ resource.setName(resource.getName());
+ resource.setVersion("0.1");
+ ;
+ resource.setUniqueId(resource.getName().toLowerCase() + ":" + resource.getVersion());
+ resource.setCreatorUserId(user.getUserId());
+ resource.setCreatorFullName(user.getFirstName() + " " + user.getLastName());
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ log.debug(gson.toJson(resource));
+ return resource;
+ }
+
+ private Resource setCanWorkOnResource(Resource resource) {
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ resource.setLastUpdaterUserId(user.getUserId());
+ return resource;
+ }
+
+ @Test
+ public void testHappyScenario() {
+ Resource resource = createResourceObject(false);
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resource, user, null, null);
+
+ if (createResponse.isRight()) {
+ assertEquals(new Integer(200), createResponse.right().value().getStatus());
+ }
+ assertEquals(createResourceObject(true), createResponse.left().value());
+ }
+
+ @Test
+ public void testUpdateHappyScenario() {
+ Resource resource = createResourceObjectCsar(true);
+ setCanWorkOnResource(resource);
+ Either<Resource, ResponseFormat> updateResponse = bl.validateAndUpdateResourceFromCsar(resource, user, null, null, resource.getUniqueId());
+ if (updateResponse.isRight()) {
+ assertEquals(new Integer(200), updateResponse.right().value().getStatus());
+ }
+ assertEquals(resource.getUniqueId(), updateResponse.left().value().getUniqueId());
+ }
+
+ /* CREATE validations - start ***********************/
+ // Resource name - start
+
+ @Test
+ public void testFailedResourceValidations() {
+ testResourceNameExist();
+ testResourceNameEmpty();
+ // testResourceNameExceedsLimit();
+ testResourceNameWrongFormat();
+ testResourceDescExceedsLimitCreate();
+ testResourceDescNotEnglish();
+ testResourceDescriptionEmpty();
+ testResourceDescriptionMissing();
+ testResourceIconMissing();
+ testResourceIconInvalid();
+ testResourceIconExceedsLimit();
+ testResourceTagNotExist();
+ testResourceTagEmpty();
+ testTagsExceedsLimitCreate();
+ // testTagsSingleExceedsLimit();
+ testTagsNoServiceName();
+ testInvalidTag();
+ // 1610OS Support - Because of changes in the validation in the ui these tests will fail. need to fix them
+ //testContactIdTooLong();
+ //testContactIdWrongFormatCreate();
+ testResourceContactIdEmpty();
+ testResourceContactIdMissing();
+ testVendorNameExceedsLimit();
+ testVendorNameWrongFormatCreate();
+ testVendorReleaseWrongFormat();
+ testVendorReleaseExceedsLimitCreate();
+ testResourceVendorNameMissing();
+ testResourceVendorReleaseMissing();
+ testResourceCategoryExist();
+ testResourceBadCategoryCreate();
+ testHappyScenarioCostLicenseType();
+ testCostWrongFormatCreate();
+ testLicenseTypeWrongFormatCreate();
+ testResourceTemplateNotExist();
+ testResourceTemplateEmpty();
+ testResourceTemplateInvalid();
+ }
+
+ private void testResourceNameExist() {
+ String resourceName = "alreadyExists";
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setName(resourceName);
+ resourceExist.getTags().add(resourceName);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertResponse(createResponse, ActionStatus.COMPONENT_NAME_ALREADY_EXIST, ComponentTypeEnum.RESOURCE.getValue(), resourceName);
+ }
+
+ private void testResourceNameEmpty() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setName(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertResponse(createResponse, ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceNameExceedsLimit() {
+ Resource resourceExccedsNameLimit = createResourceObject(false);
+ // 51 chars, the limit is 50
+ String tooLongResourceName = "zCRCAWjqte0DtgcAAMmcJcXeNubeX1p1vOZNTShAHOYNAHvV3iK";
+ resourceExccedsNameLimit.setName(tooLongResourceName);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExccedsNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT, ComponentTypeEnum.RESOURCE.getValue(), "" + ValidationUtils.COMPONENT_NAME_MAX_LENGTH);
+ }
+
+ private void testResourceNameWrongFormat() {
+ Resource resource = createResourceObject(false);
+ // contains :
+ String nameWrongFormat = "ljg?fd";
+ resource.setName(nameWrongFormat);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resource, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ // Resource name - end
+ // Resource description - start
+ private void testResourceDescExceedsLimitCreate() {
+ Resource resourceExccedsDescLimit = createResourceObject(false);
+ // 1025 chars, the limit is 1024
+ String tooLongResourceDesc = "1GUODojQ0sGzKR4NP7e5j82ADQ3KHTVOaezL95qcbuaqDtjZhAQGQ3iFwKAy580K4WiiXs3u3zq7RzXcSASl5fm0RsWtCMOIDP"
+ + "AOf9Tf2xtXxPCuCIMCR5wOGnNTaFxgnJEHAGxilBhZDgeMNHmCN1rMK5B5IRJOnZxcpcL1NeG3APTCIMP1lNAxngYulDm9heFSBc8TfXAADq7703AvkJT0QPpGq2z2P"
+ + "tlikcAnIjmWgfC5Tm7UH462BAlTyHg4ExnPPL4AO8c92VrD7kZSgSqiy73cN3gLT8uigkKrUgXQFGVUFrXVyyQXYtVM6bLBeuCGQf4C2j8lkNg6M0J3PC0PzMRoinOxk"
+ + "Ae2teeCtVcIj4A1KQo3210j8q2v7qQU69Mabsa6DT9FgE4rcrbiFWrg0Zto4SXWD3o1eJA9o29lTg6kxtklH3TuZTmpi5KVp1NFhS1RpnqF83tzv4mZLKsx7Zh1fEgYvRFwx1"
+ + "ar3RolyDfNoZiGBGTMsZzz7RPFBf2hTnLmNqVGQnHKhhGj0Y5s8t2cbqbO2nmHiJb9uaUVrCGypgbAcJL3KPOBfAVW8PcpmNj4yVjI3L4x5zHjmGZbp9vKshEQODcrmcgsYAoKqe"
+ + "uu5u7jk8XVxEfQ0m5qL8UOErXPlJovSmKUmP5B5T0w299zIWDYCzSoNasHpHjOMDLAiDDeHbozUOn9t3Qou00e9POq4RMM0VnIx1H38nJoJZz2XH8CI5YMQe7oTagaxgQTF2aa0qaq2"
+ + "V6nJsfRGRklGjNhFFYP2cS4Xv2IJO9DSX6LTXOmENrGVJJvMOZcvnBaZPfoAHN0LU4i1SoepLzulIxnZBfkUWFJgZ5wQ0Bco2GC1HMqzW21rwy4XHRxXpXbmW8LVyoA1KbnmVmROycU4"
+ + "scTZ62IxIcIWCVeMjBIcTviXULbPUyqlfEPXWr8IMJtpAaELWgyquPClAREMDs2b9ztKmUeXlMccFES1XWbFTrhBHhmmDyVReEgCwfokrUFR13LTUK1k8I6OEHOs";
+
+ resourceExccedsDescLimit.setDescription(tooLongResourceDesc);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExccedsDescLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT, ComponentTypeEnum.RESOURCE.getValue(), "" + ValidationUtils.COMPONENT_DESCRIPTION_MAX_LENGTH);
+ }
+
+ private void testResourceDescNotEnglish() {
+ Resource notEnglish = createResourceObject(false);
+ // Not english
+ String notEnglishDesc = "\uC2B5";
+ notEnglish.setDescription(notEnglishDesc);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(notEnglish, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_DESCRIPTION, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceDescriptionEmpty() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setDescription("");
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_DESCRIPTION, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceDescriptionMissing() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setDescription(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_DESCRIPTION, ComponentTypeEnum.RESOURCE.getValue());
+ }
+ // Resource description - end
+ // Resource icon start
+
+ private void testResourceIconMissing() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setIcon(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_ICON, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceIconInvalid() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setIcon("kjk3453^&");
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_ICON, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceIconExceedsLimit() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setIcon("dsjfhskdfhskjdhfskjdhkjdhfkshdfksjsdkfhsdfsdfsdfsfsdfsf");
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_ICON_EXCEEDS_LIMIT, ComponentTypeEnum.RESOURCE.getValue(), "" + ValidationUtils.ICON_MAX_LENGTH);
+ }
+
+ // Resource icon end
+ // Resource tags - start
+ private void testResourceTagNotExist() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setTags(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_TAGS);
+ }
+
+ private void testResourceTagEmpty() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setTags(new ArrayList<String>());
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_TAGS);
+ }
+
+ private void testTagsExceedsLimitCreate() {
+ Resource resourceExccedsNameLimit = createResourceObject(false);
+ String tag1 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjQ";
+ String tag2 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjW";
+ String tag3 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjE";
+ String tag4 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjb";
+ String tag5 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjr";
+ String tag6 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjf";
+ String tag7 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjg";
+ String tag8 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjd";
+ String tag9 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjf";
+ String tag10 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjg";
+ String tag11 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjh";
+ String tag12 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjj";
+ String tag13 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjk";
+ String tag14 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjs";
+ String tag15 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjz";
+ String tag16 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjx";
+ String tag17 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj2";
+ String tag18 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj3";
+ String tag19 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj4";
+ String tag20 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj5";
+ String tag21 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj0";
+
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ tagsList.add(tag2);
+ tagsList.add(tag3);
+ tagsList.add(tag4);
+ tagsList.add(tag5);
+ tagsList.add(tag6);
+ tagsList.add(tag7);
+ tagsList.add(tag8);
+ tagsList.add(tag9);
+ tagsList.add(tag10);
+ tagsList.add(tag11);
+ tagsList.add(tag12);
+ tagsList.add(tag13);
+ tagsList.add(tag14);
+ tagsList.add(tag15);
+ tagsList.add(tag16);
+ tagsList.add(tag17);
+ tagsList.add(tag18);
+ tagsList.add(tag19);
+ tagsList.add(tag20);
+ tagsList.add(tag21);
+ tagsList.add(resourceExccedsNameLimit.getName());
+
+ resourceExccedsNameLimit.setTags(tagsList);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExccedsNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT, "" + ValidationUtils.TAG_LIST_MAX_LENGTH);
+
+ }
+
+ private void testTagsSingleExceedsLimit() {
+ Resource resourceExccedsNameLimit = createResourceObject(false);
+ String tag1 = "afzs2qLBb5X6tZhiunkcEwiFX1qRQY8YZl3y3Du5M5xeQY5Nq9afcFHDZ9HaURw43gH27nAUWM36bMbMylwTFSzzNV8NO4v4ripe6Q15Vc2nPOFI";
+ String tag2 = resourceExccedsNameLimit.getName();
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ tagsList.add(tag2);
+
+ resourceExccedsNameLimit.setTags(tagsList);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExccedsNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_SINGLE_TAG_EXCEED_LIMIT, "" + ValidationUtils.TAG_MAX_LENGTH);
+
+ }
+
+ private void testTagsNoServiceName() {
+ Resource serviceExccedsNameLimit = createResourceObject(false);
+ String tag1 = "afzs2qLBb";
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ serviceExccedsNameLimit.setTags(tagsList);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(serviceExccedsNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_TAGS_NO_COMP_NAME);
+
+ }
+
+ private void testInvalidTag() {
+ Resource serviceExccedsNameLimit = createResourceObject(false);
+ String tag1 = "afzs2qLBb%#%";
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ serviceExccedsNameLimit.setTags(tagsList);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(serviceExccedsNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_FIELD_FORMAT, new String[] { "Resource", "tag" });
+
+ }
+
+ // Resource tags - stop
+ // Resource contact info start
+ private void testContactIdTooLong() {
+ Resource resourceContactId = createResourceObject(false);
+ // 7 chars instead of 6
+ String contactIdTooLong = "yrt1234";
+ resourceContactId.setContactId(contactIdTooLong);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceContactId, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testContactIdWrongFormatCreate() {
+ Resource resourceContactId = createResourceObject(false);
+ // 3 letters and 3 digits
+ String contactIdTooLong = "yrt134";
+ resourceContactId.setContactId(contactIdTooLong);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceContactId, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceContactIdEmpty() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setContactId("");
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_CONTACT, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceContactIdMissing() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setContactId(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_CONTACT, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testVendorNameExceedsLimit() {
+ Resource resourceExccedsVendorNameLimit = createResourceObject(false);
+ String tooLongVendorName = "h1KSyJh9Eh1KSyJh9Eh1KSyJh9Eh1KSyJh9E";
+ resourceExccedsVendorNameLimit.setVendorName(tooLongVendorName);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExccedsVendorNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.VENDOR_NAME_EXCEEDS_LIMIT, "" + ValidationUtils.VENDOR_NAME_MAX_LENGTH);
+ }
+
+ private void testVendorNameWrongFormatCreate() {
+ Resource resource = createResourceObject(false);
+ // contains *
+ String nameWrongFormat = "ljg*fd";
+ resource.setVendorName(nameWrongFormat);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resource, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_VENDOR_NAME);
+ }
+
+ private void testVendorReleaseWrongFormat() {
+ Resource resource = createResourceObject(false);
+ // contains >
+ String nameWrongFormat = "1>2";
+ resource.setVendorRelease(nameWrongFormat);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resource, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_VENDOR_RELEASE);
+
+ }
+
+ private void testVendorReleaseExceedsLimitCreate() {
+ Resource resourceExccedsNameLimit = createResourceObject(false);
+ String tooLongVendorRelease = "h1KSyJh9Eh1KSyJh9Eh1KSyJh9Eh1KSyJh9E";
+ resourceExccedsNameLimit.setVendorRelease(tooLongVendorRelease);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExccedsNameLimit, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.VENDOR_RELEASE_EXCEEDS_LIMIT, "" + ValidationUtils.VENDOR_RELEASE_MAX_LENGTH);
+ }
+
+ private void testResourceVendorNameMissing() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setVendorName(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_VENDOR_NAME);
+ }
+
+ private void testResourceVendorReleaseMissing() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setVendorRelease(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_VENDOR_RELEASE);
+ }
+
+ // Resource vendor name/release stop
+ // Category start
+ private void testResourceCategoryExist() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setCategories(null);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ private void testResourceBadCategoryCreate() {
+
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setCategories(null);
+ resourceExist.addCategory("koko", "koko");
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ // Category stop
+ // Cost start
+ private void testHappyScenarioCostLicenseType() {
+ Resource createResourceObject = createResourceObject(false);
+ Resource createResourceObjectAfterCreate = createResourceObject(true);
+ // Adding cost and licenseType to basic mock
+ Either<Resource, StorageOperationStatus> eitherCreate = Either.left(createResourceObjectAfterCreate);
+ when(resourceOperation.createResource(Mockito.any(Resource.class), Mockito.anyBoolean())).thenReturn(eitherCreate);
+
+ String cost = "123.456";
+ String licenseType = "User";
+ createResourceObject.setCost(cost);
+ createResourceObject.setLicenseType(licenseType);
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(createResourceObject, user, null, null);
+
+ if (createResponse.isRight()) {
+ assertEquals(new Integer(200), createResponse.right().value().getStatus());
+ }
+ createResourceObjectAfterCreate.setCost(cost);
+ createResourceObjectAfterCreate.setLicenseType(licenseType);
+ assertEquals(createResourceObjectAfterCreate, createResponse.left().value());
+ }
+
+ private void testCostWrongFormatCreate() {
+ Resource resourceCost = createResourceObject(false);
+ // Comma instead of fullstop
+ String cost = "12356,464";
+ resourceCost.setCost(cost);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceCost, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_CONTENT);
+ }
+
+ // Cost stop
+ // License type start
+ private void testLicenseTypeWrongFormatCreate() {
+ Resource resourceLicenseType = createResourceObject(false);
+ // lowcase
+ String licenseType = "cpu";
+ resourceLicenseType.setLicenseType(licenseType);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceLicenseType, user, null, null);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_CONTENT);
+ }
+
+ // License type stop
+ // Derived from start
+ private void testResourceTemplateNotExist() {
+ Resource resourceExist = createResourceObject(false);
+ List<String> list = null;
+ resourceExist.setDerivedFrom(list);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_DERIVED_FROM_TEMPLATE);
+ }
+
+ private void testResourceTemplateEmpty() {
+ Resource resourceExist = createResourceObject(false);
+ resourceExist.setDerivedFrom(new ArrayList<String>());
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_DERIVED_FROM_TEMPLATE);
+ }
+
+ private void testResourceTemplateInvalid() {
+ Resource resourceExist = createResourceObject(false);
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("kuku");
+ resourceExist.setDerivedFrom(derivedFrom);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.PARENT_RESOURCE_NOT_FOUND);
+ }
+ // Derived from stop
+
+ private void assertResponse(Either<Resource, ResponseFormat> createResponse, ActionStatus expectedStatus, String... variables) {
+ ResponseFormat expectedResponse = responseManager.getResponseFormat(expectedStatus, variables);
+ ResponseFormat actualResponse = createResponse.right().value();
+ assertEquals(expectedResponse.getStatus(), actualResponse.getStatus());
+ assertEquals("assert error description", expectedResponse.getFormattedMessage(), actualResponse.getFormattedMessage());
+ }
+
+ // UPDATE tests - start
+ // Resource name
+ @Test
+ public void testResourceNameWrongFormat_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+ // contains *
+ String nameWrongFormat = "ljg*fd";
+ updatedResource.setName(nameWrongFormat);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue());
+
+ }
+
+ @Test
+ public void testResourceNameAfterCertify_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ String name = "ljg";
+ updatedResource.setName(name);
+ resource.setVersion("1.0");
+ ;
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.RESOURCE_NAME_CANNOT_BE_CHANGED);
+
+ }
+
+ @Test
+ @Ignore
+ public void testResourceNameExceedsLimit_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // 51 chars, the limit is 50
+ String tooLongResourceName = "zCRCAWjqte0DtgcAAMmcJcXeNubeX1p1vOZNTShAHOYNAHvV3iK";
+ updatedResource.setName(tooLongResourceName);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_NAME_EXCEEDS_LIMIT, ComponentTypeEnum.RESOURCE.getValue(), "" + ValidationUtils.COMPONENT_NAME_MAX_LENGTH);
+ }
+
+ @Test
+ public void testResourceNameAlreadyExist_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ String resourceName = "alreadyExists";
+ updatedResource.setName(resourceName);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_NAME_ALREADY_EXIST, ComponentTypeEnum.RESOURCE.getValue(), resourceName);
+ }
+
+ //
+
+ @Test
+ public void testResourceDescExceedsLimit_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // 1025 chars, the limit is 1024
+ String tooLongResourceDesc = "1GUODojQ0sGzKR4NP7e5j82ADQ3KHTVOaezL95qcbuaqDtjZhAQGQ3iFwKAy580K4WiiXs3u3zq7RzXcSASl5fm0RsWtCMOIDP"
+ + "AOf9Tf2xtXxPCuCIMCR5wOGnNTaFxgnJEHAGxilBhZDgeMNHmCN1rMK5B5IRJOnZxcpcL1NeG3APTCIMP1lNAxngYulDm9heFSBc8TfXAADq7703AvkJT0QPpGq2z2P"
+ + "tlikcAnIjmWgfC5Tm7UH462BAlTyHg4ExnPPL4AO8c92VrD7kZSgSqiy73cN3gLT8uigkKrUgXQFGVUFrXVyyQXYtVM6bLBeuCGQf4C2j8lkNg6M0J3PC0PzMRoinOxk"
+ + "Ae2teeCtVcIj4A1KQo3210j8q2v7qQU69Mabsa6DT9FgE4rcrbiFWrg0Zto4SXWD3o1eJA9o29lTg6kxtklH3TuZTmpi5KVp1NFhS1RpnqF83tzv4mZLKsx7Zh1fEgYvRFwx1"
+ + "ar3RolyDfNoZiGBGTMsZzz7RPFBf2hTnLmNqVGQnHKhhGj0Y5s8t2cbqbO2nmHiJb9uaUVrCGypgbAcJL3KPOBfAVW8PcpmNj4yVjI3L4x5zHjmGZbp9vKshEQODcrmcgsYAoKqe"
+ + "uu5u7jk8XVxEfQ0m5qL8UOErXPlJovSmKUmP5B5T0w299zIWDYCzSoNasHpHjOMDLAiDDeHbozUOn9t3Qou00e9POq4RMM0VnIx1H38nJoJZz2XH8CI5YMQe7oTagaxgQTF2aa0qaq2"
+ + "V6nJsfRGRklGjNhFFYP2cS4Xv2IJO9DSX6LTXOmENrGVJJvMOZcvnBaZPfoAHN0LU4i1SoepLzulIxnZBfkUWFJgZ5wQ0Bco2GC1HMqzW21rwy4XHRxXpXbmW8LVyoA1KbnmVmROycU4"
+ + "scTZ62IxIcIWCVeMjBIcTviXULbPUyqlfEPXWr8IMJtpAaELWgyquPClAREMDs2b9ztKmUeXlMccFES1XWbFTrhBHhmmDyVReEgCwfokrUFR13LTUK1k8I6OEHOs";
+ updatedResource.setDescription(tooLongResourceDesc);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_DESCRIPTION_EXCEEDS_LIMIT, ComponentTypeEnum.RESOURCE.getValue(), "" + ValidationUtils.COMPONENT_DESCRIPTION_MAX_LENGTH);
+
+ }
+
+ @Test
+ public void testIconWrongFormat_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // contains .
+ String icon = "icon.jpg";
+ updatedResource.setIcon(icon);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_ICON, ComponentTypeEnum.RESOURCE.getValue());
+
+ }
+
+ @Test
+ public void testIconAfterCertify_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // contains
+ String icon = "icon";
+ updatedResource.setIcon(icon);
+
+ resource.setVersion("1.0");
+ ;
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.RESOURCE_ICON_CANNOT_BE_CHANGED);
+
+ }
+
+ @Test
+ public void testTagsExceedsLimit_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ String tag1 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjQ";
+ String tag2 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjW";
+ String tag3 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjE";
+ String tag4 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjb";
+ String tag5 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjr";
+ String tag6 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjf";
+ String tag7 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjg";
+ String tag8 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjd";
+ String tag9 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjf";
+ String tag10 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjg";
+ String tag11 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjh";
+ String tag12 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjj";
+ String tag13 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjk";
+ String tag14 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjs";
+ String tag15 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjz";
+ String tag16 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBjx";
+ String tag17 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj2";
+ String tag18 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj3";
+ String tag19 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj4";
+ String tag20 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj5";
+ String tag21 = "I63llMSEF12FntTwpMt64JhopkjQZzv5KS7mBoRku42PYLrBj0";
+
+ List<String> tagsList = new ArrayList<String>();
+ tagsList.add(tag1);
+ tagsList.add(tag2);
+ tagsList.add(tag3);
+ tagsList.add(tag4);
+ tagsList.add(tag5);
+ tagsList.add(tag6);
+ tagsList.add(tag7);
+ tagsList.add(tag8);
+ tagsList.add(tag9);
+ tagsList.add(tag10);
+ tagsList.add(tag11);
+ tagsList.add(tag12);
+ tagsList.add(tag13);
+ tagsList.add(tag14);
+ tagsList.add(tag15);
+ tagsList.add(tag16);
+ tagsList.add(tag17);
+ tagsList.add(tag18);
+ tagsList.add(tag19);
+ tagsList.add(tag20);
+ tagsList.add(tag21);
+ tagsList.add(resource.getName());
+
+ updatedResource.setTags(tagsList);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_TAGS_EXCEED_LIMIT, "" + ValidationUtils.TAG_LIST_MAX_LENGTH);
+ }
+
+ @Test
+ public void testVendorNameWrongFormat_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // contains *
+ String nameWrongFormat = "ljg*fd";
+ updatedResource.setVendorName(nameWrongFormat);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.INVALID_VENDOR_NAME);
+
+ }
+
+ @Test
+ public void testVendorNameAfterCertify_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // contains *
+ String nameWrongFormat = "ljg*fd";
+ updatedResource.setVendorName(nameWrongFormat);
+ resource.setVersion("1.0");
+ ;
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED);
+
+ }
+
+ @Test
+ public void testVendorReleaseExceedsLimit_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ // 129 chars, the limit is 128
+ String tooLongVendorRelease = "h1KSyJh9EspI8SPwAGu4VETfqWejeanuB1PCJBxJmJncYnrW0lnsEFFVRIukRJkwlOVnZCy8p38tjhANeZq3BGMHIawWR6ICl8Wi9mikRYALWgvJug00JrlQ0iPVKPLxy";
+ updatedResource.setVendorRelease(tooLongVendorRelease);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resource.getUniqueId(), updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+ assertResponse(createResponse, ActionStatus.VENDOR_RELEASE_EXCEEDS_LIMIT, "" + ValidationUtils.VENDOR_RELEASE_MAX_LENGTH);
+ }
+
+ // 1610OS Support - Because of changes in the validation in the ui this test needs to be fixed
+// @Test
+// public void testContactIdWrongFormat_UPDATE() {
+// Resource resource = createResourceObject(true);
+// Resource updatedResource = createResourceObject(true);
+//
+// // this is in order to prevent failing with 403 earlier
+// Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+// // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+// when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+//
+// String resourceId = resource.getUniqueId();
+// // 3 letters and 3 digits
+// String contactIdTooLong = "yrt134";
+// updatedResource.setContactId(contactIdTooLong);
+// Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+// assertTrue(createResponse.isRight());
+//
+// assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CONTACT, ComponentTypeEnum.RESOURCE.getValue());
+// }
+
+ @Test
+ public void testResourceBadCategory_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ String resourceId = resource.getUniqueId();
+ String badCategory = "ddfds";
+ updatedResource.setCategories(null);
+ updatedResource.addCategory(badCategory, "fikt");
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue());
+ }
+
+ @Test
+ public void testResourceCategoryAfterCertify_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ String resourceId = resource.getUniqueId();
+ updatedResource.setCategories(null);
+ updatedResource.addCategory(RESOURCE_CATEGORY1, UPDATED_SUBCATEGORY);
+ resource.setVersion("1.0");
+ ;
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.RESOURCE_CATEGORY_CANNOT_BE_CHANGED);
+ }
+
+ // Derived from start
+ @Test
+ public void testResourceTemplateNotExist_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+ String resourceId = resource.getUniqueId();
+
+ List<String> list = null;
+ updatedResource.setDerivedFrom(list);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_DERIVED_FROM_TEMPLATE);
+ }
+
+ @Test
+ public void testResourceTemplateEmpty_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+ String resourceId = resource.getUniqueId();
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ updatedResource.setDerivedFrom(new ArrayList<String>());
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.MISSING_DERIVED_FROM_TEMPLATE);
+ }
+
+ @Test
+ public void testResourceTemplateInvalid_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+ String resourceId = resource.getUniqueId();
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("kuku");
+ updatedResource.setDerivedFrom(derivedFrom);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.PARENT_RESOURCE_NOT_FOUND);
+ }
+
+ @Test
+ public void testResourceTemplateCertify_UPDATE() {
+ Resource resource = createResourceObject(true);
+ Resource updatedResource = createResourceObject(true);
+ String resourceId = resource.getUniqueId();
+
+ // this is in order to prevent failing with 403 earlier
+ Either<Resource, StorageOperationStatus> eitherUpdate = Either.left(setCanWorkOnResource(resource));
+ // when(resourceOperation.getResource_tx(resource.getUniqueId(),false)).thenReturn(eitherUpdate);
+ when(resourceOperation.getResource(resource.getUniqueId(), false)).thenReturn(eitherUpdate);
+ resource.setVersion("1.0");
+ ;
+
+ ArrayList<String> derivedFrom = new ArrayList<String>();
+ derivedFrom.add("tosca.nodes.Root");
+ updatedResource.setDerivedFrom(derivedFrom);
+
+ Either<Resource, ResponseFormat> createResponse = bl.updateResourceMetadata(resourceId, updatedResource, null, user, false);
+ assertTrue(createResponse.isRight());
+
+ assertResponse(createResponse, ActionStatus.RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED);
+ }
+ // Derived from stop
+
+ @Test
+ public void createOrUpdateResourceAlreadyCheckout() {
+ Resource resourceExist = createResourceObject(false);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+
+ createResponse.left().value().setLastUpdaterUserId(user.getUserId());
+ assertTrue(createResponse.isLeft());
+
+ Either<Resource, StorageOperationStatus> getLatestResult = Either.left(createResponse.left().value());
+ when(resourceOperation.getLatestByName(resourceExist.getName(), true)).thenReturn(getLatestResult);
+ when(resourceOperation.overrideResource(Mockito.any(Resource.class), Mockito.any(Resource.class), Mockito.anyBoolean())).thenReturn(getLatestResult);
+
+ Resource resourceToUpdtae = createResourceObject(false);
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createOrUpdateResource = bl.createOrUpdateResourceByImport(resourceToUpdtae, user, false, false, false);
+ assertTrue(createOrUpdateResource.isLeft());
+
+ Mockito.verify(resourceOperation, Mockito.times(1)).overrideResource(Mockito.any(Resource.class), Mockito.any(Resource.class), Mockito.anyBoolean());
+ Mockito.verify(lifecycleBl, Mockito.times(0)).changeState(Mockito.anyString(), Mockito.eq(user), Mockito.eq(LifeCycleTransitionEnum.CHECKOUT), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.anyBoolean(), Mockito.anyBoolean());
+
+ }
+
+ @Test
+ public void createOrUpdateResourceCertified() {
+ Resource resourceExist = createResourceObject(false);
+
+ Either<Resource, ResponseFormat> createResponse = bl.createResource(resourceExist, user, null, null);
+
+ assertTrue(createResponse.isLeft());
+ Resource certifiedResource = createResponse.left().value();
+ certifiedResource.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ certifiedResource.setVersion("1.0");
+ ;
+
+ Either<Resource, StorageOperationStatus> getLatestResult = Either.left(certifiedResource);
+ when(resourceOperation.getLatestByName(resourceExist.getName(), true)).thenReturn(getLatestResult);
+ when(resourceOperation.overrideResource(Mockito.any(Resource.class), Mockito.any(Resource.class), Mockito.anyBoolean())).thenReturn(getLatestResult);
+
+ when(lifecycleBl.changeState(Mockito.anyString(), Mockito.eq(user), Mockito.eq(LifeCycleTransitionEnum.CHECKOUT), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.anyBoolean(), Mockito.anyBoolean())).thenReturn(createResponse);
+
+ Resource resourceToUpdtae = createResourceObject(false);
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createOrUpdateResource = bl.createOrUpdateResourceByImport(resourceToUpdtae, user, false, false, false);
+ assertTrue(createOrUpdateResource.isLeft());
+
+ Mockito.verify(resourceOperation, Mockito.times(1)).overrideResource(Mockito.any(Resource.class), Mockito.any(Resource.class), Mockito.anyBoolean());
+ Mockito.verify(lifecycleBl, Mockito.times(1)).changeState(Mockito.anyString(), Mockito.eq(user), Mockito.eq(LifeCycleTransitionEnum.CHECKOUT), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.anyBoolean(), Mockito.anyBoolean());
+
+ }
+
+ @Test
+ public void createOrUpdateResourceNotExist() {
+ Resource resourceToUpdtae = createResourceObject(false);
+
+ Either<Resource, StorageOperationStatus> getLatestResult = Either.right(StorageOperationStatus.NOT_FOUND);
+ when(resourceOperation.getLatestByName(resourceToUpdtae.getName(), true)).thenReturn(getLatestResult);
+
+ Either<Resource, StorageOperationStatus> getLatestToscaNameResult = Either.right(StorageOperationStatus.NOT_FOUND);
+ when(resourceOperation.getLatestByToscaResourceName(resourceToUpdtae.getToscaResourceName(), true)).thenReturn(getLatestResult);
+
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> createOrUpdateResource = bl.createOrUpdateResourceByImport(resourceToUpdtae, user, false, false, false);
+ assertTrue(createOrUpdateResource.isLeft());
+
+ Mockito.verify(resourceOperation, Mockito.times(0)).overrideResource(Mockito.any(Resource.class), Mockito.any(Resource.class), Mockito.anyBoolean());
+ Mockito.verify(lifecycleBl, Mockito.times(0)).changeState(Mockito.anyString(), Mockito.eq(user), Mockito.eq(LifeCycleTransitionEnum.CHECKOUT), Mockito.any(LifecycleChangeInfoWithAction.class), Mockito.anyBoolean(), Mockito.anyBoolean());
+
+ }
+
+ @Test
+ public void testValidatePropertiesDefaultValues_SuccessfullWithoutProperties() {
+ Resource basic = createResourceObject(true);
+
+ Either<Boolean, ResponseFormat> validatePropertiesDefaultValues = bl.validatePropertiesDefaultValues(basic);
+ assertTrue(validatePropertiesDefaultValues.isLeft());
+ }
+
+ @Test
+ public void testValidatePropertiesDefaultValues_SuccessfullWithProperties() {
+ Resource basic = createResourceObject(true);
+ PropertyDefinition property = new PropertyDefinition();
+ property.setName("myProperty");
+ property.setType(ToscaPropertyType.INTEGER.getType());
+ property.setDefaultValue("1");
+ List<PropertyDefinition> properties = new ArrayList<>();
+ properties.add(property);
+ basic.setProperties(properties);
+ when(propertyOperation.isPropertyTypeValid(property)).thenReturn(true);
+ when(propertyOperation.isPropertyDefaultValueValid(property, emptyDataTypes)).thenReturn(true);
+ Either<Boolean, ResponseFormat> validatePropertiesDefaultValues = bl.validatePropertiesDefaultValues(basic);
+ assertTrue(validatePropertiesDefaultValues.isLeft());
+ }
+
+ @Test
+ public void testValidatePropertiesDefaultValues_FailedWithProperties() {
+ Resource basic = createResourceObject(true);
+ PropertyDefinition property = new PropertyDefinition();
+ property.setName("myProperty");
+ property.setType(ToscaPropertyType.INTEGER.getType());
+ property.setDefaultValue("1.5");
+ List<PropertyDefinition> properties = new ArrayList<>();
+ properties.add(property);
+ basic.setProperties(properties);
+
+ when(propertyOperation.isPropertyDefaultValueValid(property, emptyDataTypes)).thenReturn(false);
+ Either<Boolean, ResponseFormat> validatePropertiesDefaultValues = bl.validatePropertiesDefaultValues(basic);
+ assertTrue(validatePropertiesDefaultValues.isRight());
+ }
+
+ @Test
+ public void testDeleteMarkedResourcesNoResources() {
+ List<String> ids = new ArrayList<String>();
+ Either<List<String>, StorageOperationStatus> eitherNoResources = Either.left(ids);
+ when(resourceOperation.getAllComponentsMarkedForDeletion()).thenReturn(eitherNoResources);
+
+ Either<List<String>, ResponseFormat> deleteMarkedResources = bl.deleteMarkedComponents();
+ assertTrue(deleteMarkedResources.isLeft());
+ assertTrue(deleteMarkedResources.left().value().isEmpty());
+
+ Mockito.verify(artifactManager, Mockito.times(0)).deleteAllComponentArtifactsIfNotOnGraph(Mockito.anyList());
+
+ }
+
+ @Test
+ public void testDeleteMarkedResources() {
+ List<String> ids = new ArrayList<String>();
+ String resourceInUse = "123";
+ ids.add(resourceInUse);
+ String resourceFree = "456";
+ ids.add(resourceFree);
+ Either<List<String>, StorageOperationStatus> eitherNoResources = Either.left(ids);
+ when(resourceOperation.getAllComponentsMarkedForDeletion()).thenReturn(eitherNoResources);
+
+ Either<Boolean, StorageOperationStatus> resourceInUseResponse = Either.left(true);
+ Either<Boolean, StorageOperationStatus> resourceFreeResponse = Either.left(false);
+
+ List<ArtifactDefinition> artifacts = new ArrayList<ArtifactDefinition>();
+ Either<List<ArtifactDefinition>, StorageOperationStatus> getArtifactsResponse = Either.left(artifacts);
+ when(resourceOperation.getComponentArtifactsForDelete(resourceFree, NodeTypeEnum.Resource, true)).thenReturn(getArtifactsResponse);
+
+ when(resourceOperation.isComponentInUse(resourceFree)).thenReturn(resourceFreeResponse);
+ when(resourceOperation.isComponentInUse(resourceInUse)).thenReturn(resourceInUseResponse);
+
+ Either<Component, StorageOperationStatus> eitherDelete = Either.left(new Resource());
+ when(resourceOperation.deleteComponent(resourceFree, true)).thenReturn(eitherDelete);
+
+ when(artifactManager.deleteAllComponentArtifactsIfNotOnGraph(artifacts)).thenReturn(StorageOperationStatus.OK);
+
+ Either<List<String>, ResponseFormat> deleteMarkedResources = bl.deleteMarkedComponents();
+ assertTrue(deleteMarkedResources.isLeft());
+ List<String> resourceIdList = deleteMarkedResources.left().value();
+ assertFalse(resourceIdList.isEmpty());
+ assertTrue(resourceIdList.contains(resourceFree));
+ assertFalse(resourceIdList.contains(resourceInUse));
+
+ Mockito.verify(artifactManager, Mockito.times(1)).deleteAllComponentArtifactsIfNotOnGraph(artifacts);
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceInstanceBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceInstanceBusinessLogicTest.java
new file mode 100644
index 0000000000..d1597886d8
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceInstanceBusinessLogicTest.java
@@ -0,0 +1,284 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class ResourceInstanceBusinessLogicTest {
+
+ private static final String RESOURCE_ID_WITH_HEAT_PARAMS = "MyResourceId";
+ private static final String RESOURCE_ID_NO_PAYLOAD = "NoHeatPayload";
+ private static final String RESOURCE_ID_NO_HEAT_PARAMS = "NoHeatParams";
+ private static final String RESOURCE_INSTANCE_ID = "MyResourceInstanceId";
+ private static final String SERVICE_ID = "MyServiceId";
+ private static final String HEAT_LABEL = "myHeat";
+ private static final String HEAT_ENV_LABEL = HEAT_LABEL + "Env";
+ private static final String USER_ID = "jh0003";
+ private static final long ARTIFACT_CREATION_TIME = System.currentTimeMillis();
+
+ static User adminUser = new User("John", "Doh", USER_ID, "", "ADMIN", null);
+
+ @InjectMocks
+ static ServiceComponentInstanceBusinessLogic bl = new ServiceComponentInstanceBusinessLogic();
+
+ public static final ArtifactsBusinessLogic artifactBusinessLogic = Mockito.mock(ArtifactsBusinessLogic.class);
+ public static final UserBusinessLogic userAdminManager = Mockito.mock(UserBusinessLogic.class);
+ public static final ServiceOperation serviceOperation = Mockito.mock(ServiceOperation.class);
+ public static final ComponentsUtils componentsUtils = Mockito.mock(ComponentsUtils.class);
+
+ static ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), "src/test/resources/config/catalog-be");
+ static ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+
+ // @BeforeClass
+ public static void setup() {
+
+ Map<String, Object> deploymentResourceArtifacts = ConfigurationManager.getConfigurationManager().getConfiguration().getDeploymentResourceInstanceArtifacts();
+ Map<String, Object> placeHolderData = (Map<String, Object>) deploymentResourceArtifacts.get(ComponentInstanceBusinessLogic.HEAT_ENV_NAME);
+
+ ArtifactDefinition heatArtifact = getHeatArtifactDefinition(USER_ID, RESOURCE_ID_WITH_HEAT_PARAMS, HEAT_LABEL, ARTIFACT_CREATION_TIME, false, true);
+ Map<String, ArtifactDefinition> artifacts = new HashMap<String, ArtifactDefinition>();
+ artifacts.put(HEAT_LABEL.toLowerCase(), heatArtifact);
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> eitherGetResourceArtifact = Either.left(artifacts);
+ Mockito.when(artifactBusinessLogic.getArtifacts(RESOURCE_ID_WITH_HEAT_PARAMS, NodeTypeEnum.Resource, true, ArtifactGroupTypeEnum.DEPLOYMENT)).thenReturn(eitherGetResourceArtifact);
+
+ ArtifactDefinition heatArtifactNoPayload = getHeatArtifactDefinition(USER_ID, RESOURCE_ID_NO_PAYLOAD, HEAT_LABEL, ARTIFACT_CREATION_TIME, true, false);
+ Map<String, ArtifactDefinition> artifactsNoPayload = new HashMap<String, ArtifactDefinition>();
+ artifactsNoPayload.put(HEAT_LABEL.toLowerCase(), heatArtifactNoPayload);
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> eitherGetResourceArtifactNoPayload = Either.left(artifactsNoPayload);
+ Mockito.when(artifactBusinessLogic.getArtifacts(RESOURCE_ID_NO_PAYLOAD, NodeTypeEnum.Resource, true, ArtifactGroupTypeEnum.DEPLOYMENT)).thenReturn(eitherGetResourceArtifactNoPayload);
+
+ ArtifactDefinition heatArtifactNoParams = getHeatArtifactDefinition(USER_ID, RESOURCE_ID_NO_HEAT_PARAMS, HEAT_LABEL, ARTIFACT_CREATION_TIME, false, false);
+ Map<String, ArtifactDefinition> artifactsNoParams = new HashMap<String, ArtifactDefinition>();
+ artifactsNoParams.put(HEAT_LABEL.toLowerCase(), heatArtifactNoParams);
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> eitherGetResourceArtifactNoParams = Either.left(artifactsNoParams);
+ Mockito.when(artifactBusinessLogic.getArtifacts(RESOURCE_ID_NO_HEAT_PARAMS, NodeTypeEnum.Resource, true, ArtifactGroupTypeEnum.DEPLOYMENT)).thenReturn(eitherGetResourceArtifactNoParams);
+
+ Either<ArtifactDefinition, ResponseFormat> eitherPlaceHolder = Either.left(getArtifactPlaceHolder(RESOURCE_INSTANCE_ID, HEAT_ENV_LABEL));
+ Mockito.when(artifactBusinessLogic.createArtifactPlaceHolderInfo(RESOURCE_INSTANCE_ID, HEAT_ENV_LABEL.toLowerCase(), placeHolderData, USER_ID, ArtifactGroupTypeEnum.DEPLOYMENT, false)).thenReturn(eitherPlaceHolder);
+
+ Mockito.when(artifactBusinessLogic.createArtifactAuditingFields(Mockito.any(ArtifactDefinition.class), Mockito.anyString(), Mockito.anyString())).thenReturn(new EnumMap<AuditingFieldsKeysEnum, Object>(AuditingFieldsKeysEnum.class));
+
+ Either<ArtifactDefinition, StorageOperationStatus> eitherArtifact = Either.left(getHeatArtifactDefinition(USER_ID, RESOURCE_INSTANCE_ID, HEAT_ENV_LABEL, ARTIFACT_CREATION_TIME, true, false));
+ Mockito.when(artifactBusinessLogic.addHeatEnvArtifact(Mockito.any(ArtifactDefinition.class), Mockito.any(ArtifactDefinition.class), Mockito.anyString(), Mockito.any(NodeTypeEnum.class), Mockito.anyBoolean())).thenReturn(eitherArtifact);
+
+ Either<User, ActionStatus> eitherUser = Either.left(adminUser);
+ Mockito.when(userAdminManager.getUser(USER_ID, false)).thenReturn(eitherUser);
+
+ Object lightService = new Service();
+ Either<Object, StorageOperationStatus> eitherLightService = Either.left(lightService);
+ Mockito.when(serviceOperation.getLightComponent(Mockito.anyString(), Mockito.anyBoolean())).thenReturn(eitherLightService);
+
+ Mockito.doNothing().when(componentsUtils).auditComponent(Mockito.any(ResponseFormat.class), Mockito.any(User.class), Mockito.any(Component.class), Mockito.anyString(), Mockito.anyString(), Mockito.any(AuditingActionEnum.class),
+ Mockito.any(ComponentTypeEnum.class), Mockito.any(EnumMap.class));
+
+ }
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ Mockito.reset(artifactBusinessLogic, serviceOperation, componentsUtils, userAdminManager);
+ setup();
+ }
+
+ @Test
+ public void testAddResourceInstanceArtifacts() throws Exception {
+ ComponentInstance resourceInstance = new ComponentInstance();
+ resourceInstance.setName(RESOURCE_INSTANCE_ID);
+ resourceInstance.setComponentUid(RESOURCE_ID_WITH_HEAT_PARAMS);
+ resourceInstance.setUniqueId(RESOURCE_INSTANCE_ID);
+ Service service = new Service();
+ service.setUniqueId(SERVICE_ID);
+
+ Either<ActionStatus, ResponseFormat> addArtifactsRes = bl.addComponentInstanceArtifacts(service, resourceInstance, USER_ID, false, null);
+ assertTrue(addArtifactsRes.isLeft());
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceInstance.getDeploymentArtifacts();
+ assertNotNull(deploymentArtifacts);
+ assertTrue(deploymentArtifacts.size() == 2);
+
+ ArtifactDefinition heatDefinition = deploymentArtifacts.get(HEAT_LABEL.toLowerCase());
+ assertNotNull(heatDefinition);
+ assertEquals(getHeatArtifactDefinition(USER_ID, RESOURCE_ID_WITH_HEAT_PARAMS, HEAT_LABEL, ARTIFACT_CREATION_TIME, false, true), heatDefinition);
+
+ ArtifactDefinition heatEnvDefinition = deploymentArtifacts.get(HEAT_ENV_LABEL.toLowerCase());
+ assertNotNull(heatEnvDefinition);
+
+ List<HeatParameterDefinition> heatParameters = heatDefinition.getHeatParameters();
+ assertNotNull(heatParameters);
+
+ List<HeatParameterDefinition> heatEnvParameters = heatEnvDefinition.getHeatParameters();
+ assertNotNull(heatEnvParameters);
+
+ assertEquals(heatParameters.size(), heatEnvParameters.size());
+
+ int index = 0;
+ for (HeatParameterDefinition heatEnvParameter : heatEnvParameters) {
+ HeatParameterDefinition heatParameterDefinition = heatParameters.get(index);
+ assertEquals(heatEnvParameter.getUniqueId(), heatParameterDefinition.getUniqueId());
+ assertEquals(heatEnvParameter.getType(), heatParameterDefinition.getType());
+ assertEquals(heatEnvParameter.getName(), heatParameterDefinition.getName());
+ assertEquals(heatEnvParameter.getDescription(), heatParameterDefinition.getDescription());
+ assertEquals(heatEnvParameter.getCurrentValue(), heatParameterDefinition.getCurrentValue());
+ // current of heat parameter should be the default for heat env
+ // parameter
+ assertEquals(heatEnvParameter.getDefaultValue(), heatParameterDefinition.getCurrentValue());
+
+ index++;
+ }
+ }
+
+ @Test
+ public void testAddResourceInstanceArtifactsNoParams() throws Exception {
+ ComponentInstance resourceInstance = new ComponentInstance();
+ resourceInstance.setName(RESOURCE_INSTANCE_ID);
+ resourceInstance.setComponentUid(RESOURCE_ID_NO_HEAT_PARAMS);
+ resourceInstance.setUniqueId(RESOURCE_INSTANCE_ID);
+ Service service = new Service();
+ service.setUniqueId(SERVICE_ID);
+
+ Either<ActionStatus, ResponseFormat> addArtifactsRes = bl.addComponentInstanceArtifacts(service, resourceInstance, USER_ID, false, null);
+ assertTrue(addArtifactsRes.isLeft());
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceInstance.getDeploymentArtifacts();
+ assertNotNull(deploymentArtifacts);
+ assertTrue(deploymentArtifacts.size() == 2);
+
+ ArtifactDefinition heatDefinition = deploymentArtifacts.get(HEAT_LABEL.toLowerCase());
+ assertNotNull(heatDefinition);
+ assertEquals(getHeatArtifactDefinition(USER_ID, RESOURCE_ID_NO_HEAT_PARAMS, HEAT_LABEL, ARTIFACT_CREATION_TIME, false, false), heatDefinition);
+
+ ArtifactDefinition heatEnvDefinition = deploymentArtifacts.get(HEAT_ENV_LABEL.toLowerCase());
+ assertNotNull(heatEnvDefinition);
+
+ List<HeatParameterDefinition> heatParameters = heatDefinition.getHeatParameters();
+ assertNull(heatParameters);
+
+ List<HeatParameterDefinition> heatEnvParameters = heatEnvDefinition.getHeatParameters();
+ assertNull(heatEnvParameters);
+
+ }
+
+ @Test
+ public void testAddResourceInstanceArtifactsNoArtifacts() throws Exception {
+ ComponentInstance resourceInstance = new ComponentInstance();
+ resourceInstance.setName(RESOURCE_INSTANCE_ID);
+ resourceInstance.setComponentUid(RESOURCE_ID_NO_PAYLOAD);
+ resourceInstance.setUniqueId(RESOURCE_INSTANCE_ID);
+ Service service = new Service();
+ service.setUniqueId(SERVICE_ID);
+
+ Either<ActionStatus, ResponseFormat> addArtifactsRes = bl.addComponentInstanceArtifacts(service, resourceInstance, USER_ID, false, null);
+ assertTrue(addArtifactsRes.isLeft());
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = resourceInstance.getDeploymentArtifacts();
+ assertNotNull(deploymentArtifacts);
+ assertTrue(deploymentArtifacts.size() == 0);
+
+ Mockito.verify(artifactBusinessLogic, Mockito.times(0)).addHeatEnvArtifact(Mockito.any(ArtifactDefinition.class), Mockito.any(ArtifactDefinition.class), Mockito.anyString(), Mockito.any(NodeTypeEnum.class), Mockito.anyBoolean());
+ }
+
+ private static ArtifactDefinition getHeatArtifactDefinition(String userId, String resourceId, String artifactName, long time, boolean placeholderOnly, boolean withHeatParams) {
+ ArtifactDefinition artifactInfo = new ArtifactDefinition();
+
+ artifactInfo.setArtifactName(artifactName + ".yml");
+ artifactInfo.setArtifactType("HEAT");
+ artifactInfo.setDescription("hdkfhskdfgh");
+ artifactInfo.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+
+ artifactInfo.setUserIdCreator(userId);
+ String fullName = "Jim H";
+ artifactInfo.setUpdaterFullName(fullName);
+ // long time = System.currentTimeMillis();
+ artifactInfo.setCreatorFullName(fullName);
+ artifactInfo.setCreationDate(time);
+ artifactInfo.setLastUpdateDate(time);
+ artifactInfo.setUserIdLastUpdater(userId);
+ artifactInfo.setArtifactLabel(HEAT_LABEL.toLowerCase());
+ artifactInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, artifactInfo.getArtifactLabel()));
+
+ if (!placeholderOnly) {
+ artifactInfo.setEsId(artifactInfo.getUniqueId());
+ artifactInfo.setArtifactChecksum("UEsDBAoAAAAIAAeLb0bDQz");
+
+ if (withHeatParams) {
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ HeatParameterDefinition heatParam = new HeatParameterDefinition();
+ heatParam.setCurrentValue("11");
+ heatParam.setDefaultValue("22");
+ heatParam.setDescription("desc");
+ heatParam.setName("myParam");
+ heatParam.setType("number");
+ heatParams.add(heatParam);
+ artifactInfo.setHeatParameters(heatParams);
+ }
+ }
+
+ return artifactInfo;
+ }
+
+ private static ArtifactDefinition getArtifactPlaceHolder(String resourceId, String logicalName) {
+ ArtifactDefinition artifact = new ArtifactDefinition();
+
+ artifact.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, logicalName.toLowerCase()));
+ artifact.setArtifactLabel(logicalName.toLowerCase());
+
+ return artifact;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransitionTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransitionTest.java
new file mode 100644
index 0000000000..48cd83a7c8
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationChangeTransitionTest.java
@@ -0,0 +1,169 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.CertificationChangeTransition;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class CertificationChangeTransitionTest extends LifecycleTestBase {
+
+ private CertificationChangeTransition certifyTransitionObj = null;
+ private CertificationChangeTransition certificationCancelObj = null;
+ private CertificationChangeTransition certificationFailObj = null;
+
+ private ComponentsUtils componentsUtils = new ComponentsUtils();
+ private String resourceName = "myResource";
+ private User owner = null;
+
+ protected ArtifactsBusinessLogic artifactsManager = Mockito.mock(ArtifactsBusinessLogic.class);
+
+ Resource resource;
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setup() {
+
+ super.setup();
+ componentsUtils.Init();
+ // checkout transition object
+ certifyTransitionObj = new CertificationChangeTransition(LifeCycleTransitionEnum.CERTIFY, componentsUtils, lcOperation);
+ certifyTransitionObj.setConfigurationManager(configurationManager);
+
+ certificationCancelObj = new CertificationChangeTransition(LifeCycleTransitionEnum.CERTIFY, componentsUtils, lcOperation);
+ certificationCancelObj.setConfigurationManager(configurationManager);
+
+ certificationFailObj = new CertificationChangeTransition(LifeCycleTransitionEnum.CERTIFY, componentsUtils, lcOperation);
+ certificationFailObj.setConfigurationManager(configurationManager);
+
+ owner = new User("cs0008", "Carlos", "Santana", "cs@sdc.com", "DESIGNER", null);
+
+ when(artifactsManager.deleteAllComponentArtifactsIfNotOnGraph(Mockito.anyList())).thenReturn(StorageOperationStatus.OK);
+ resource = createResourceObject(false);
+ }
+
+ @Test
+ public void testStateValidationSuccess() {
+
+ Either<Boolean, ResponseFormat> changeStateResult = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(changeStateResult.isLeft(), true);
+
+ }
+
+ @Test
+ public void testStateValidationFail() {
+
+ // checkout
+ Either<Boolean, ResponseFormat> validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ assertValidationStateErrorResponse(validateBeforeTransition);
+
+ // checkin
+ validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertValidationStateErrorResponse(validateBeforeTransition);
+
+ // rfc
+ validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertValidationStateErrorResponse(validateBeforeTransition);
+
+ // certified
+ validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFIED);
+ assertValidationStateErrorResponse(validateBeforeTransition);
+
+ }
+
+ @Test
+ public void testRolesFail() {
+ Either<Resource, ResponseFormat> changeStateResult;
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+ modifier.setRole(Role.DESIGNER.name());
+ Either<User, ResponseFormat> ownerResponse = certifyTransitionObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+
+ Either<Boolean, ResponseFormat> validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertResponse(changeStateResult, ActionStatus.RESTRICTED_OPERATION);
+
+ modifier.setRole(Role.TESTER.name());
+ validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testRolesSuccess() {
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<User, ResponseFormat> ownerResponse = certifyTransitionObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+
+ Either<Boolean, ResponseFormat> validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, owner, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(true, validateBeforeTransition.isLeft());
+
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+ modifier.setRole(Role.ADMIN.name());
+ validateBeforeTransition = certifyTransitionObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(true, validateBeforeTransition.isLeft());
+
+ }
+
+ private void assertValidationStateErrorResponse(Either<Boolean, ResponseFormat> validateBeforeTransition) {
+ assertEquals(validateBeforeTransition.isRight(), true);
+ ResponseFormat error = validateBeforeTransition.right().value();
+ Either<Resource, ResponseFormat> changeStateResult = Either.right(error);
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_NOT_READY_FOR_CERTIFICATION, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTest.java
new file mode 100644
index 0000000000..b45aa61de1
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CertificationRequestTest.java
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.HttpStatus;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.components.distribution.engine.ServiceDistributionArtifactsBuilder;
+import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.CertificationRequestTransition;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.tosca.ToscaError;
+import org.openecomp.sdc.be.tosca.ToscaExportHandler;
+import org.openecomp.sdc.be.tosca.ToscaRepresentation;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class CertificationRequestTest extends LifecycleTestBase {
+
+ private ComponentsUtils componentsUtils = new ComponentsUtils();
+
+ protected ServiceDistributionArtifactsBuilder serviceDistributionArtifactsBuilder = Mockito.mock(ServiceDistributionArtifactsBuilder.class);
+ protected ServiceBusinessLogic serviceBusinessLogic = Mockito.mock(ServiceBusinessLogic.class);
+ protected ResourceOperation resourceOperation = Mockito.mock(ResourceOperation.class);
+ protected CapabilityOperation capabilityOperation = Mockito.mock(CapabilityOperation.class);
+ protected ToscaExportHandler toscaExportUtils = Mockito.mock(ToscaExportHandler.class);
+ @InjectMocks
+ private CertificationRequestTransition rfcObj = new CertificationRequestTransition(componentsUtils, lcOperation, serviceDistributionArtifactsBuilder, serviceBusinessLogic, capabilityOperation, toscaExportUtils);
+
+ protected ToscaRepresentation toscaRepresentation = Mockito.mock(ToscaRepresentation.class);
+
+ @Before
+ public void setup() {
+ Mockito.reset(resourceOperation);
+ MockitoAnnotations.initMocks(this);
+ super.setup();
+
+ // checkout transition object
+ rfcObj.setLifeCycleOperation(lcOperation);
+ // checkoutObj.setAuditingManager(iAuditingManager);
+ rfcObj.setConfigurationManager(configurationManager);
+ componentsUtils.Init();
+
+ Either<ToscaRepresentation, ToscaError> either = Either.left(toscaRepresentation);
+ when(toscaExportUtils.exportComponent(Mockito.anyObject())).thenReturn(either);
+
+ }
+
+ @Test
+ public void testCheckoutStateValidation() {
+ Either<? extends Component, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ Either<User, ResponseFormat> ownerResponse = rfcObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ // changeStateResult = rfcObj.changeStateOperation(resource, user,
+ // owner, false);
+ changeStateResult = rfcObj.changeState(ComponentTypeEnum.RESOURCE, resource, serviceBusinessLogic, user, owner, false, false);
+ assertEquals(changeStateResult.isLeft(), true);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ // changeStateResult = rfcObj.changeStateOperation(resource, user,
+ // owner, false);
+ changeStateResult = rfcObj.changeState(ComponentTypeEnum.RESOURCE, resource, serviceBusinessLogic, user, owner, false, false);
+ assertEquals(changeStateResult.isLeft(), true);
+ }
+
+ @Test
+ public void testAlreadyRfc() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<User, ResponseFormat> ownerResponse = rfcObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ Either<Boolean, ResponseFormat> validateBeforeTransition = rfcObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_SENT_FOR_CERTIFICATION, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testCertificationInProgress() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<User, ResponseFormat> ownerResponse = rfcObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ Either<Boolean, ResponseFormat> validateBeforeTransition = rfcObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testAlreadyCertified() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ Either<User, ResponseFormat> ownerResponse = rfcObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ Either<Boolean, ResponseFormat> validateBeforeTransition = rfcObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFIED);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CERTIFIED, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testValidateAllResourceInstanceCertified_SuccessWithoutRI() {
+ Resource resource = new Resource();
+ Either<Boolean, ResponseFormat> validateAllResourceInstanceCertified = rfcObj.validateAllResourceInstanceCertified(resource);
+ assertTrue(validateAllResourceInstanceCertified.isLeft());
+ }
+
+ @Test
+ public void testValidateAllResourceInstanceCertified_SuccessWithCertifiedResources() {
+ Resource resource = new Resource();
+ List<ComponentInstance> riList = new ArrayList<ComponentInstance>();
+ ComponentInstance ri = new ComponentInstance();
+ ri.setComponentVersion("2.0");
+ riList.add(ri);
+ resource.setComponentInstances(riList);
+
+ Either<Boolean, ResponseFormat> validateAllResourceInstanceCertified = rfcObj.validateAllResourceInstanceCertified(resource);
+ assertTrue(validateAllResourceInstanceCertified.isLeft());
+ }
+
+ @Test
+ public void testValidateAllResourceInstanceCertified_FailWithUnCertifiedResourcesMinorVersion() {
+ Resource resource = createVFWithRI("0.3");
+
+ simulateCertifiedVersionExistForRI();
+
+ Either<Boolean, ResponseFormat> validateAllResourceInstanceCertified = rfcObj.validateAllResourceInstanceCertified(resource);
+
+ assertTrue(validateAllResourceInstanceCertified.isRight());
+ ResponseFormat responseFormat = validateAllResourceInstanceCertified.right().value();
+ assertTrue(responseFormat.getStatus() == HttpStatus.SC_FORBIDDEN);
+ assertTrue(responseFormat.getMessageId().equals("SVC4559"));
+
+ }
+
+ @Test
+ public void testValidateAllResourceInstanceCertified_FailWithUnCertifiedResourcesMajorVersion() {
+ Resource resource = createVFWithRI("1.3");
+
+ simulateCertifiedVersionExistForRI();
+
+ Either<Boolean, ResponseFormat> validateAllResourceInstanceCertified = rfcObj.validateAllResourceInstanceCertified(resource);
+
+ assertTrue(validateAllResourceInstanceCertified.isRight());
+ ResponseFormat responseFormat = validateAllResourceInstanceCertified.right().value();
+ assertTrue(responseFormat.getStatus() == HttpStatus.SC_FORBIDDEN);
+ assertTrue(responseFormat.getMessageId().equals("SVC4559"));
+
+ }
+
+ private void simulateCertifiedVersionExistForRI() {
+ Resource dummyResource = new Resource();
+ Either<List<Resource>, StorageOperationStatus> result = Either.left(new ArrayList<Resource>() {
+ {
+ add(dummyResource);
+ }
+ });
+ Mockito.when(resourceOperation.getResource(Mockito.anyString())).thenReturn(Either.left(dummyResource));
+ Mockito.when(resourceOperation.findLastCertifiedResourceByUUID(Mockito.any(Resource.class))).thenReturn(result);
+ }
+
+ private Resource createVFWithRI(String riVersion) {
+ Resource resource = new Resource();
+ List<ComponentInstance> riList = new ArrayList<ComponentInstance>();
+ ComponentInstance ri = new ComponentInstance();
+
+ ri.setComponentVersion(riVersion);
+ ri.setComponentUid("someUniqueId");
+ riList.add(ri);
+ resource.setComponentInstances(riList);
+ return resource;
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckinTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckinTest.java
new file mode 100644
index 0000000000..e0d942e2bc
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckinTest.java
@@ -0,0 +1,170 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.sdc.be.components.lifecycle.CheckinTransition;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class CheckinTest extends LifecycleTestBase {
+
+ private CheckinTransition checkinObj = null;
+ private ComponentsUtils componentsUtils = new ComponentsUtils();
+
+ @Before
+ public void setup() {
+
+ super.setup();
+
+ // checkout transition object
+ checkinObj = new CheckinTransition(componentsUtils, lcOperation);
+ checkinObj.setLifeCycleOperation(lcOperation);
+ checkinObj.setConfigurationManager(configurationManager);
+ componentsUtils.Init();
+ }
+
+ @Test
+ public void testSimpleCheckin() {
+ Either<Boolean, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Either<User, ResponseFormat> ownerResponse = checkinObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ changeStateResult = checkinObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(changeStateResult.isLeft(), true);
+
+ }
+
+ @Test
+ public void testSimpleServiceCheckin() {
+ Either<Boolean, ResponseFormat> changeStateResult;
+ Service service = createServiceObject(false);
+
+ service.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Either<User, ResponseFormat> ownerResponse = checkinObj.getComponentOwner(service, ComponentTypeEnum.SERVICE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ changeStateResult = checkinObj.validateBeforeTransition(service, ComponentTypeEnum.SERVICE, user, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(changeStateResult.isLeft(), true);
+
+ }
+
+ @Test
+ public void testCheckinTwiceValidation() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ Either<User, ResponseFormat> owner = checkinObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(owner.isLeft());
+ // changeStateResult = checkinObj.changeStateOperation(resource, user,
+ // owner.left().value());
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkinObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner.left().value(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testServiceCheckinTwiceValidation() {
+ Either<Service, ResponseFormat> changeStateResult;
+ Service service = createServiceObject(false);
+
+ service.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ Either<User, ResponseFormat> owner = checkinObj.getComponentOwner(service, ComponentTypeEnum.SERVICE);
+ assertTrue(owner.isLeft());
+
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkinObj.validateBeforeTransition(service, ComponentTypeEnum.SERVICE, user, owner.left().value(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+
+ assertServiceResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, service.getName(), ComponentTypeEnum.SERVICE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testCheckoutByAnotherUserValidation() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+ modifier.setRole(Role.DESIGNER.name());
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Either<User, ResponseFormat> ownerResponse = checkinObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ // changeStateResult = checkinObj.changeStateOperation(resource,
+ // modifier, owner);
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkinObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testServiceCheckoutByAnotherUserValidation() {
+ Either<Service, ResponseFormat> changeStateResult;
+ Service service = createServiceObject(false);
+
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+ modifier.setRole(Role.DESIGNER.name());
+
+ service.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Either<User, ResponseFormat> ownerResponse = checkinObj.getComponentOwner(service, ComponentTypeEnum.SERVICE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkinObj.validateBeforeTransition(service, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertServiceResponse(changeStateResult, ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, service.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTest.java
new file mode 100644
index 0000000000..4a81653e14
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/CheckoutTest.java
@@ -0,0 +1,188 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
+import org.openecomp.sdc.be.components.lifecycle.CheckoutTransition;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class CheckoutTest extends LifecycleTestBase {
+
+ private CheckoutTransition checkoutObj = null;
+ private ComponentsUtils componentsUtils = new ComponentsUtils();
+ @InjectMocks
+ ResourceBusinessLogic bl = new ResourceBusinessLogic();
+
+ @Before
+ public void setup() {
+
+ super.setup();
+
+ // checkout transition object
+ checkoutObj = new CheckoutTransition(componentsUtils, lcOperation);
+ checkoutObj.setLifeCycleOperation(lcOperation);
+ checkoutObj.setConfigurationManager(configurationManager);
+ componentsUtils.Init();
+
+ }
+
+ @Test
+ public void testCheckoutStateValidation() {
+ Either<? extends Component, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ Either<User, ResponseFormat> ownerResponse = checkoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ changeStateResult = checkoutObj.changeState(ComponentTypeEnum.RESOURCE, resource, bl, user, owner, false, false);
+ assertEquals(changeStateResult.isLeft(), true);
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ changeStateResult = checkoutObj.changeState(ComponentTypeEnum.RESOURCE, resource, bl, user, owner, false, false);
+ assertEquals(changeStateResult.isLeft(), true);
+
+ }
+
+ @Test
+ public void testAlreadyCheckout() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Either<User, ResponseFormat> ownerResponse = checkoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+
+ assertEquals(changeStateResult.isRight(), true);
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_IN_CHECKOUT_STATE, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testCertificationInProgress() {
+ Either<? extends Component, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<User, ResponseFormat> ownerResponse = checkoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ changeStateResult = checkoutObj.changeState(ComponentTypeEnum.RESOURCE, resource, bl, user, owner, false, false);
+
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_IN_CERT_IN_PROGRESS_STATE, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testReadyForCertification() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+
+ // if modifier = owner
+ Either<User, ResponseFormat> ownerResponse = checkoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ // changeStateResult = checkoutObj.changeStateOperation(resource, user,
+ // owner);
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(validateBeforeTransition.isLeft(), true);
+
+ // else
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+
+ // admin
+ modifier.setRole(Role.ADMIN.name());
+ // changeStateResult = checkoutObj.changeStateOperation(resource, user,
+ // owner);
+ validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(validateBeforeTransition.isLeft(), true);
+
+ // designer
+ modifier.setRole(Role.TESTER.name());
+ validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+
+ assertEquals(changeStateResult.isRight(), true);
+ assertResponse(changeStateResult, ActionStatus.RESTRICTED_OPERATION, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testRoles() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+ modifier.setRole(Role.DESIGNER.name());
+ Either<User, ResponseFormat> ownerResponse = checkoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ // changeStateResult = checkoutObj.changeStateOperation(resource,
+ // modifier, owner);
+ Either<Boolean, ResponseFormat> validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(validateBeforeTransition.isLeft(), true);
+
+ modifier.setRole(Role.TESTER.name());
+ // changeStateResult = checkoutObj.changeStateOperation(resource,
+ // modifier, owner);
+ validateBeforeTransition = checkoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertResponse(changeStateResult, ActionStatus.RESTRICTED_OPERATION);
+
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/LifecycleTestBase.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/LifecycleTestBase.java
new file mode 100644
index 0000000000..734a3d9bde
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/LifecycleTestBase.java
@@ -0,0 +1,215 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.AuditingMockManager;
+import org.openecomp.sdc.be.auditing.api.IAuditingManager;
+import org.openecomp.sdc.be.components.impl.ArtifactsBusinessLogic;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.LifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+import junit.framework.Assert;
+
+public class LifecycleTestBase {
+ private static Logger log = LoggerFactory.getLogger(LifecycleTestBase.class.getName());
+ @InjectMocks
+ protected final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ protected IAuditingManager iAuditingManager = null;
+ protected UserBusinessLogic mockUserAdmin = Mockito.mock(UserBusinessLogic.class);
+ protected WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ protected WebApplicationContext webAppContext = Mockito.mock(WebApplicationContext.class);
+ protected LifecycleOperation lcOperation = Mockito.mock(LifecycleOperation.class);
+ protected ArtifactsBusinessLogic artifactsManager = Mockito.mock(ArtifactsBusinessLogic.class);;
+ protected User user = null;
+ protected Resource resourceResponse;
+ protected Service serviceResponse;
+ protected ConfigurationManager configurationManager = null;
+ protected ResponseFormatManager responseManager = null;
+
+ public void setup() {
+
+ ExternalConfiguration.setAppName("catalog-be");
+
+ // Init Configuration
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+
+ // Auditing
+ iAuditingManager = new AuditingMockManager("lll");
+
+ // User data and management
+ user = new User();
+ user.setUserId("jh003");
+ user.setFirstName("Jimmi");
+ user.setLastName("Hendrix");
+ user.setRole(Role.ADMIN.name());
+
+ Either<User, ActionStatus> eitherGetUser = Either.left(user);
+ when(mockUserAdmin.getUser("jh003", false)).thenReturn(eitherGetUser);
+
+ // Servlet Context attributes
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ // when(servletContext.getAttribute(Constants.AUDITING_MANAGER)).thenReturn(iAuditingManager);
+ // when(servletContext.getAttribute(Constants.RESOURCE_OPERATION_MANAGER)).thenReturn(resourceOperation);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webAppContext);
+ when(webAppContext.getBean(LifecycleOperation.class)).thenReturn(lcOperation);
+ when(webAppContext.getBean(ArtifactsBusinessLogic.class)).thenReturn(artifactsManager);
+
+ // Resource Operation mock methods
+ // getCount
+
+ // createResource
+ resourceResponse = createResourceObject(true);
+ serviceResponse = createServiceObject(true);
+ Either<Resource, StorageOperationStatus> eitherCreate = Either.left(resourceResponse);
+ Either<? extends Component, StorageOperationStatus> eitherCreateComponent = Either.left(resourceResponse);
+ when((Either<Resource, StorageOperationStatus>) lcOperation.checkoutComponent(Mockito.eq(NodeTypeEnum.Resource), Mockito.any(Resource.class), Mockito.any(User.class), Mockito.any(User.class), Mockito.any(Boolean.class)))
+ .thenAnswer(createAnswer(eitherCreate));
+ when(lcOperation.checkoutComponent(Mockito.eq(NodeTypeEnum.Resource), Mockito.any(Resource.class), Mockito.any(User.class), Mockito.any(User.class), Mockito.any(Boolean.class))).thenAnswer(createAnswer(eitherCreateComponent));
+
+ when((Either<Resource, StorageOperationStatus>) lcOperation.checkinComponent(Mockito.eq(NodeTypeEnum.Resource), Mockito.any(Resource.class), Mockito.any(User.class), Mockito.any(User.class), Mockito.any(Boolean.class)))
+ .thenAnswer(createAnswer(eitherCreate));
+
+ when((Either<Resource, StorageOperationStatus>) lcOperation.requestCertificationComponent(Mockito.eq(NodeTypeEnum.Resource), Mockito.any(Resource.class), Mockito.any(User.class), Mockito.any(User.class), Mockito.any(Boolean.class)))
+ .thenAnswer(createAnswer(eitherCreate));
+ when(lcOperation.requestCertificationComponent(Mockito.eq(NodeTypeEnum.Resource), Mockito.any(Component.class), Mockito.any(User.class), Mockito.any(User.class), Mockito.any(Boolean.class))).thenAnswer(createAnswer(eitherCreateComponent));
+
+ Either<User, StorageOperationStatus> getOwnerResult = Either.left(user);
+ when(lcOperation.getComponentOwner(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Resource), Mockito.any(Boolean.class))).thenReturn(getOwnerResult);
+ when(lcOperation.getComponentOwner(Mockito.anyString(), Mockito.eq(NodeTypeEnum.Service), Mockito.any(Boolean.class))).thenReturn(getOwnerResult);
+
+ responseManager = ResponseFormatManager.getInstance();
+
+ }
+
+ public static <T> Answer<T> createAnswer(final T value) {
+ Answer<T> dummy = new Answer<T>() {
+ @Override
+ public T answer(InvocationOnMock invocation) throws Throwable {
+ return value;
+ }
+
+ };
+ return dummy;
+ }
+
+ protected Resource createResourceObject(boolean afterCreate) {
+ Resource resource = new Resource();
+ resource.setName("MyResourceName");
+ resource.addCategory("VoIP", "INfra");
+ resource.setDescription("My short description");
+ List<String> tgs = new ArrayList<String>();
+ tgs.add("test");
+ resource.setTags(tgs);
+ List<String> template = new ArrayList<String>();
+ template.add("Root");
+ resource.setDerivedFrom(template);
+ resource.setVendorName("Motorola");
+ resource.setVendorRelease("1.0.0");
+ resource.setContactId("yavivi");
+ resource.setIcon("MyIcon.jpg");
+
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ log.debug(gson.toJson(resource));
+ return resource;
+ }
+
+ protected Service createServiceObject(boolean b) {
+ Service service = new Service();
+ service.setName("MyServiceName");
+ service.addCategory("VoIP", null);
+ service.setDescription("My short description");
+ List<String> tgs = new ArrayList<String>();
+ tgs.add("test");
+ service.setTags(tgs);
+ List<String> template = new ArrayList<String>();
+ template.add("Root");
+ service.setContactId("aa0001");
+ service.setIcon("MyIcon.jpg");
+
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ log.debug(gson.toJson(service));
+ return service;
+ }
+
+ protected void assertResponse(Either<? extends Component, ResponseFormat> createResponse, ActionStatus expectedStatus, String... variables) {
+ Assert.assertTrue(createResponse.isRight());
+ ResponseFormat expectedResponse = responseManager.getResponseFormat(expectedStatus, variables);
+ ResponseFormat actualResponse = createResponse.right().value();
+ Assert.assertEquals(expectedResponse.getStatus(), actualResponse.getStatus());
+ Assert.assertEquals("assert error description", expectedResponse.getFormattedMessage(), actualResponse.getFormattedMessage());
+ }
+
+ protected void assertServiceResponse(Either<Service, ResponseFormat> createResponse, ActionStatus expectedStatus, String... variables) {
+ Assert.assertTrue(createResponse.isRight());
+ ResponseFormat expectedResponse = responseManager.getResponseFormat(expectedStatus, variables);
+ ResponseFormat actualResponse = createResponse.right().value();
+ Assert.assertEquals(expectedResponse.getStatus(), actualResponse.getStatus());
+ Assert.assertEquals("assert error description", expectedResponse.getFormattedMessage(), actualResponse.getFormattedMessage());
+ }
+
+ protected static ArtifactDefinition getArtifactPlaceHolder(String resourceId, String logicalName) {
+ ArtifactDefinition artifact = new ArtifactDefinition();
+
+ artifact.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, logicalName.toLowerCase()));
+ artifact.setArtifactLabel(logicalName.toLowerCase());
+
+ return artifact;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTest.java
new file mode 100644
index 0000000000..6cc0385667
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/lifecycle/UndoCheckoutTest.java
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.components.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.sdc.be.components.lifecycle.UndoCheckoutTransition;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+public class UndoCheckoutTest extends LifecycleTestBase {
+
+ private UndoCheckoutTransition undoCheckoutObj = null;
+ private ComponentsUtils componentsUtils = new ComponentsUtils();
+
+ @Before
+ public void setup() {
+
+ super.setup();
+
+ // checkout transition object
+ undoCheckoutObj = new UndoCheckoutTransition(componentsUtils, lcOperation);
+ undoCheckoutObj.setLifeCycleOperation(lcOperation);
+ undoCheckoutObj.setConfigurationManager(configurationManager);
+ componentsUtils.Init();
+
+ }
+
+ @Test
+ public void testResourceNotCheckedOutValidation() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ Either<User, ResponseFormat> ownerResponse = undoCheckoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+
+ Either<Boolean, ResponseFormat> validateBeforeTransition = undoCheckoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ validateBeforeTransition = undoCheckoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ validateBeforeTransition = undoCheckoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.CERTIFIED);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ resource.setLifecycleState(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ validateBeforeTransition = undoCheckoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, user, owner, LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_ALREADY_CHECKED_IN, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+
+ }
+
+ @Test
+ public void testDifferentResourceOwnerValidation() {
+ Either<Resource, ResponseFormat> changeStateResult;
+ Resource resource = createResourceObject(false);
+
+ resource.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ User modifier = new User();
+ modifier.setUserId("modifier");
+ modifier.setFirstName("Albert");
+ modifier.setLastName("Einstein");
+ modifier.setRole(Role.DESIGNER.name());
+
+ Either<User, ResponseFormat> ownerResponse = undoCheckoutObj.getComponentOwner(resource, ComponentTypeEnum.RESOURCE);
+ assertTrue(ownerResponse.isLeft());
+ User owner = ownerResponse.left().value();
+ Either<Boolean, ResponseFormat> validateBeforeTransition = undoCheckoutObj.validateBeforeTransition(resource, ComponentTypeEnum.RESOURCE, modifier, owner, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(validateBeforeTransition.isRight(), true);
+ changeStateResult = Either.right(validateBeforeTransition.right().value());
+ assertEquals(changeStateResult.isRight(), true);
+
+ assertResponse(changeStateResult, ActionStatus.COMPONENT_CHECKOUT_BY_ANOTHER_USER, resource.getName(), ComponentTypeEnum.RESOURCE.name().toLowerCase(), user.getFirstName(), user.getLastName(), user.getUserId());
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogicTest.java
new file mode 100644
index 0000000000..104cd14e7a
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/distribution/DistributionBusinessLogicTest.java
@@ -0,0 +1,192 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.components.BaseConfDependentTest;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaErrorResponse;
+import org.openecomp.sdc.be.components.distribution.engine.CambriaHandler;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngineInitTask;
+import org.openecomp.sdc.be.components.distribution.engine.SubscriberTypeEnum;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.distribution.AuditHandler;
+import org.openecomp.sdc.be.distribution.DistributionBusinessLogic;
+import org.openecomp.sdc.be.distribution.api.client.CambriaOperationStatus;
+import org.openecomp.sdc.be.distribution.api.client.RegistrationRequest;
+import org.openecomp.sdc.be.distribution.api.client.TopicRegistrationResponse;
+import org.openecomp.sdc.be.distribution.api.client.TopicUnregistrationResponse;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+
+public class DistributionBusinessLogicTest extends BaseConfDependentTest {
+
+ @InjectMocks
+ DistributionBusinessLogic distributionBusinessLogic = Mockito.spy(DistributionBusinessLogic.class);
+
+ CambriaHandler cambriaHandler = Mockito.mock(CambriaHandler.class);
+ AuditHandler auditHandler = Mockito.mock(AuditHandler.class);
+
+ @Test
+ public void testHandleRegistrationHappyScenario() {
+ CambriaErrorResponse okResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, HttpStatus.SC_OK);
+ Mockito.when(cambriaHandler.registerToTopic(Mockito.anyCollection(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(SubscriberTypeEnum.class))).thenReturn(okResponse);
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ RegistrationRequest registrationRequest = new RegistrationRequest("myPublicKey", "myEnv");
+ distributionBusinessLogic.handleRegistration(responseWrapper, registrationRequest, auditHandler);
+
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.PRODUCER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.CONSUMER);
+ Mockito.verify(cambriaHandler, Mockito.times(0)).unRegisterFromTopic(Mockito.anyCollection(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(SubscriberTypeEnum.class));
+
+ assertTrue(!responseWrapper.isEmpty());
+ Response response = responseWrapper.getInnerElement();
+ assertTrue(response.getStatus() == HttpStatus.SC_OK);
+
+ TopicRegistrationResponse okTopicResponse = (TopicRegistrationResponse) response.getEntity();
+
+ String expectedStatusTopicName = DistributionEngineInitTask.buildTopicName(configurationManager.getDistributionEngineConfiguration().getDistributionStatusTopicName(), registrationRequest.getDistrEnvName());
+ String actualStatusTopicName = okTopicResponse.getDistrStatusTopicName();
+ assertEquals(expectedStatusTopicName, actualStatusTopicName);
+
+ String expectedNotificationTopicName = DistributionEngineInitTask.buildTopicName(configurationManager.getDistributionEngineConfiguration().getDistributionNotifTopicName(), registrationRequest.getDistrEnvName());
+ String actualNotificationTopicName = okTopicResponse.getDistrNotificationTopicName();
+ assertEquals(expectedNotificationTopicName, actualNotificationTopicName);
+
+ }
+
+ @Test
+ public void testHandleRegistrationFailedScenario() {
+ CambriaErrorResponse okResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, HttpStatus.SC_OK);
+ CambriaErrorResponse errorResponse = new CambriaErrorResponse(CambriaOperationStatus.CONNNECTION_ERROR, HttpStatus.SC_SERVICE_UNAVAILABLE);
+ DistributionEngineConfiguration config = configurationManager.getDistributionEngineConfiguration();
+ RegistrationRequest registrationRequest = new RegistrationRequest("myPublicKey", "myEnv");
+ String expectedStatusTopicName = DistributionEngineInitTask.buildTopicName(config.getDistributionStatusTopicName(), registrationRequest.getDistrEnvName());
+ String expectedNotificationTopicName = DistributionEngineInitTask.buildTopicName(config.getDistributionNotifTopicName(), registrationRequest.getDistrEnvName());
+
+ Mockito.when(cambriaHandler.registerToTopic(config.getUebServers(), expectedStatusTopicName, config.getUebPublicKey(), config.getUebSecretKey(), registrationRequest.getApiPublicKey(), SubscriberTypeEnum.PRODUCER)).thenReturn(okResponse);
+ Mockito.when(cambriaHandler.registerToTopic(config.getUebServers(), expectedNotificationTopicName, config.getUebPublicKey(), config.getUebSecretKey(), registrationRequest.getApiPublicKey(), SubscriberTypeEnum.CONSUMER))
+ .thenReturn(errorResponse);
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+
+ distributionBusinessLogic.handleRegistration(responseWrapper, registrationRequest, auditHandler);
+
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.PRODUCER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.CONSUMER);
+ Mockito.verify(cambriaHandler, Mockito.times(1)).unRegisterFromTopic(config.getUebServers(), expectedStatusTopicName, config.getUebPublicKey(), config.getUebSecretKey(), registrationRequest.getApiPublicKey(), SubscriberTypeEnum.PRODUCER);
+
+ assertTrue(!responseWrapper.isEmpty());
+ Response response = responseWrapper.getInnerElement();
+ assertTrue(response.getStatus() == HttpStatus.SC_INTERNAL_SERVER_ERROR);
+
+ }
+
+ @Test
+ public void testHandleUnRegistrationHappyScenario() {
+ CambriaErrorResponse okResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, HttpStatus.SC_OK);
+
+ Mockito.when(cambriaHandler.unRegisterFromTopic(Mockito.anyCollection(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(SubscriberTypeEnum.class))).thenReturn(okResponse);
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ RegistrationRequest registrationRequest = new RegistrationRequest("myPublicKey", "myEnv");
+ distributionBusinessLogic.handleUnRegistration(responseWrapper, registrationRequest, auditHandler);
+
+ Mockito.verify(distributionBusinessLogic, Mockito.times(0)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.PRODUCER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(0)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.CONSUMER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).unRegisterDistributionClientFromTopic(registrationRequest, SubscriberTypeEnum.PRODUCER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).unRegisterDistributionClientFromTopic(registrationRequest, SubscriberTypeEnum.CONSUMER);
+
+ Mockito.verify(cambriaHandler, Mockito.times(2)).unRegisterFromTopic(Mockito.anyCollection(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.any(SubscriberTypeEnum.class));
+
+ assertTrue(!responseWrapper.isEmpty());
+ Response response = responseWrapper.getInnerElement();
+ assertTrue(response.getStatus() == HttpStatus.SC_OK);
+
+ TopicUnregistrationResponse okTopicUnregisterResponse = (TopicUnregistrationResponse) response.getEntity();
+
+ String expectedStatusTopicName = DistributionEngineInitTask.buildTopicName(configurationManager.getDistributionEngineConfiguration().getDistributionStatusTopicName(), registrationRequest.getDistrEnvName());
+ String actualStatusTopicName = okTopicUnregisterResponse.getDistrStatusTopicName();
+ assertEquals(expectedStatusTopicName, actualStatusTopicName);
+
+ String expectedNotificationTopicName = DistributionEngineInitTask.buildTopicName(configurationManager.getDistributionEngineConfiguration().getDistributionNotifTopicName(), registrationRequest.getDistrEnvName());
+ String actualNotificationTopicName = okTopicUnregisterResponse.getDistrNotificationTopicName();
+ assertEquals(expectedNotificationTopicName, actualNotificationTopicName);
+
+ assertEquals(okTopicUnregisterResponse.getNotificationUnregisterResult(), CambriaOperationStatus.OK);
+ assertEquals(okTopicUnregisterResponse.getStatusUnregisterResult(), CambriaOperationStatus.OK);
+
+ }
+
+ @Test
+ public void testHandleUnRegistrationFailedScenario() {
+ CambriaErrorResponse okResponse = new CambriaErrorResponse(CambriaOperationStatus.OK, HttpStatus.SC_OK);
+ CambriaErrorResponse errorResponse = new CambriaErrorResponse(CambriaOperationStatus.AUTHENTICATION_ERROR, HttpStatus.SC_INTERNAL_SERVER_ERROR);
+
+ Wrapper<Response> responseWrapper = new Wrapper<>();
+ RegistrationRequest registrationRequest = new RegistrationRequest("myPublicKey", "myEnv");
+ DistributionEngineConfiguration config = configurationManager.getDistributionEngineConfiguration();
+ String expectedStatusTopicName = DistributionEngineInitTask.buildTopicName(config.getDistributionStatusTopicName(), registrationRequest.getDistrEnvName());
+ String expectedNotificationTopicName = DistributionEngineInitTask.buildTopicName(config.getDistributionNotifTopicName(), registrationRequest.getDistrEnvName());
+ Mockito.when(cambriaHandler.unRegisterFromTopic(config.getUebServers(), expectedStatusTopicName, config.getUebPublicKey(), config.getUebSecretKey(), registrationRequest.getApiPublicKey(), SubscriberTypeEnum.PRODUCER)).thenReturn(okResponse);
+ Mockito.when(cambriaHandler.unRegisterFromTopic(config.getUebServers(), expectedNotificationTopicName, config.getUebPublicKey(), config.getUebSecretKey(), registrationRequest.getApiPublicKey(), SubscriberTypeEnum.CONSUMER))
+ .thenReturn(errorResponse);
+
+ distributionBusinessLogic.handleUnRegistration(responseWrapper, registrationRequest, auditHandler);
+
+ Mockito.verify(distributionBusinessLogic, Mockito.times(0)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.PRODUCER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(0)).registerDistributionClientToTopic(responseWrapper, registrationRequest, SubscriberTypeEnum.CONSUMER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).unRegisterDistributionClientFromTopic(registrationRequest, SubscriberTypeEnum.PRODUCER);
+ Mockito.verify(distributionBusinessLogic, Mockito.times(1)).unRegisterDistributionClientFromTopic(registrationRequest, SubscriberTypeEnum.CONSUMER);
+
+ assertTrue(!responseWrapper.isEmpty());
+ Response response = responseWrapper.getInnerElement();
+ assertTrue(response.getStatus() == HttpStatus.SC_INTERNAL_SERVER_ERROR);
+
+ TopicUnregistrationResponse okTopicUnregisterResponse = (TopicUnregistrationResponse) response.getEntity();
+
+ String actualStatusTopicName = okTopicUnregisterResponse.getDistrStatusTopicName();
+ assertEquals(expectedStatusTopicName, actualStatusTopicName);
+
+ String actualNotificationTopicName = okTopicUnregisterResponse.getDistrNotificationTopicName();
+ assertEquals(expectedNotificationTopicName, actualNotificationTopicName);
+
+ assertEquals(okTopicUnregisterResponse.getNotificationUnregisterResult(), CambriaOperationStatus.AUTHENTICATION_ERROR);
+ assertEquals(okTopicUnregisterResponse.getStatusUnregisterResult(), CambriaOperationStatus.OK);
+
+ }
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ Mockito.reset(cambriaHandler);
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/distribution/servlet/DistributionServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/distribution/servlet/DistributionServletTest.java
new file mode 100644
index 0000000000..d6274ad90e
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/distribution/servlet/DistributionServletTest.java
@@ -0,0 +1,159 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.distribution.servlet;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.components.distribution.engine.DistributionEngine;
+import org.openecomp.sdc.be.distribution.AuditHandler;
+import org.openecomp.sdc.be.distribution.DistributionBusinessLogic;
+import org.openecomp.sdc.be.distribution.api.client.RegistrationRequest;
+import org.openecomp.sdc.be.distribution.api.client.TopicRegistrationResponse;
+import org.openecomp.sdc.be.distribution.servlet.DistributionServlet;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class DistributionServletTest extends JerseyTest {
+
+ public static final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ public static final HttpSession session = Mockito.mock(HttpSession.class);
+ public static final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ public static final WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ public static final WebApplicationContext webApplicationContext = Mockito.mock(WebApplicationContext.class);
+ public static final ResponseFormat responseFormat = Mockito.mock(ResponseFormat.class);
+ public static final DistributionBusinessLogic distributionBusinessLogic = Mockito.mock(DistributionBusinessLogic.class);
+ public static final DistributionEngine distributionEngine = Mockito.mock(DistributionEngine.class);
+
+ public static final String ENV_NAME = "myEnv";
+ public static final String NOTIFICATION_TOPIC = ENV_NAME + "_Notification";
+ public static final String STATUS_TOPIC = ENV_NAME + "_Status";
+
+ @BeforeClass
+ public static void setup() {
+ ExternalConfiguration.setAppName("catalog-be");
+ when(request.getSession()).thenReturn(session);
+ when(request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER)).thenReturn("myApplicationInstanceID");
+
+ when(session.getServletContext()).thenReturn(servletContext);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext);
+ when(webApplicationContext.getBean(DistributionBusinessLogic.class)).thenReturn(distributionBusinessLogic);
+ when(distributionBusinessLogic.getDistributionEngine()).thenReturn(distributionEngine);
+ when(distributionEngine.isEnvironmentAvailable(ENV_NAME)).thenReturn(StorageOperationStatus.OK);
+ when(distributionEngine.isEnvironmentAvailable()).thenReturn(StorageOperationStatus.OK);
+
+ mockBusinessLogicResponse();
+
+ }
+
+ private static void mockBusinessLogicResponse() {
+ // Mock Register
+ Mockito.doAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Wrapper<Response> responseWrapper = (Wrapper<Response>) args[0];
+ TopicRegistrationResponse okTopicResponse = new TopicRegistrationResponse();
+ okTopicResponse.setDistrNotificationTopicName(NOTIFICATION_TOPIC);
+ okTopicResponse.setDistrStatusTopicName(STATUS_TOPIC);
+ responseWrapper.setInnerElement(Response.status(HttpStatus.SC_OK).entity(okTopicResponse).build());
+
+ return true;
+ }
+ }).when(distributionBusinessLogic).handleRegistration(Mockito.any(Wrapper.class), Mockito.any(RegistrationRequest.class), Mockito.any(AuditHandler.class));
+
+ // Mock Unregister
+ Mockito.doAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ Wrapper<Response> responseWrapper = (Wrapper<Response>) args[0];
+ TopicRegistrationResponse okTopicResponse = new TopicRegistrationResponse();
+ okTopicResponse.setDistrNotificationTopicName(NOTIFICATION_TOPIC);
+ okTopicResponse.setDistrStatusTopicName(STATUS_TOPIC);
+ responseWrapper.setInnerElement(Response.status(HttpStatus.SC_OK).entity(okTopicResponse).build());
+
+ return true;
+ }
+ }).when(distributionBusinessLogic).handleUnRegistration(Mockito.any(Wrapper.class), Mockito.any(RegistrationRequest.class), Mockito.any(AuditHandler.class));
+ }
+
+ @Test
+ public void registerSuccessTest() {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ RegistrationRequest registrationRequest = new RegistrationRequest("myPublicKey", ENV_NAME);
+ Response response = target().path("/v1/registerForDistribution").request(MediaType.APPLICATION_JSON).post(Entity.json(gson.toJson(registrationRequest)), Response.class);
+ assertTrue(response.getStatus() == HttpStatus.SC_OK);
+
+ }
+
+ @Test
+ public void unRegisterSuccessTest() {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ RegistrationRequest registrationRequest = new RegistrationRequest("myPublicKey", ENV_NAME);
+ Response response = target().path("/v1/unRegisterForDistribution").request(MediaType.APPLICATION_JSON).post(Entity.json(gson.toJson(registrationRequest)), Response.class);
+ assertTrue(response.getStatus() == HttpStatus.SC_OK);
+
+ }
+
+ @Override
+ protected Application configure() {
+
+ ResourceConfig resourceConfig = new ResourceConfig(DistributionServlet.class);
+ forceSet(TestProperties.CONTAINER_PORT, "0");
+ resourceConfig.register(new AbstractBinder() {
+
+ @Override
+ protected void configure() {
+ bind(request).to(HttpServletRequest.class);
+ }
+ });
+
+ return resourceConfig;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/ecomp/GenerateEcompErrorFileTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/ecomp/GenerateEcompErrorFileTest.java
new file mode 100644
index 0000000000..e4f46dd4dd
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/ecomp/GenerateEcompErrorFileTest.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.ecomp;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.common.config.EcompClassification;
+import org.openecomp.sdc.common.config.EcompErrorCode;
+import org.openecomp.sdc.common.config.EcompErrorEnum;
+import org.openecomp.sdc.common.config.generation.GenerateEcompErrorsCsv;
+
+import static org.junit.Assert.assertTrue;
+
+public class GenerateEcompErrorFileTest {
+
+ @Test
+ public void verifyNoDuplicatesInEcompErrorCodes() {
+
+ EcompErrorEnum[] ecompErrorEnums = EcompErrorEnum.values();
+
+ Map<EcompErrorCode, List<EcompClassification>> map = new HashMap<EcompErrorCode, List<EcompClassification>>();
+ for (EcompErrorEnum ecompErrorEnum : ecompErrorEnums) {
+
+ List<EcompClassification> list = map.get(ecompErrorEnum.getEcompErrorCode());
+ if (list == null) {
+ list = new ArrayList<>();
+
+ list.add(ecompErrorEnum.getClassification());
+
+ map.put(ecompErrorEnum.getEcompErrorCode(), list);
+ } else {
+ if (list.contains(ecompErrorEnum.getClassification())) {
+ assertTrue(ecompErrorEnum.getEcompErrorCode() + " already defined with ecomp classification " + ecompErrorEnum.getClassification(), false);
+ } else {
+ list.add(ecompErrorEnum.getClassification());
+ }
+
+ }
+
+ }
+
+ }
+
+ @Test
+ public void generateEcompErrorFileInTarget() {
+
+ GenerateEcompErrorsCsv ecompErrorsCsv = new GenerateEcompErrorsCsv();
+ boolean result = ecompErrorsCsv.generateEcompErrorsCsvFile("target", false);
+ assertTrue("check result from file generation", result);
+
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/AbstractValidationsServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/AbstractValidationsServletTest.java
new file mode 100644
index 0000000000..d41e95da9d
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/AbstractValidationsServletTest.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import org.apache.commons.codec.binary.Base64;
+import org.junit.Assert;
+import org.junit.Test;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.servlets.AbstractValidationsServlet;
+import org.openecomp.sdc.exception.ResponseFormat;
+
+import fj.data.Either;
+
+import static org.mockito.Mockito.mock;
+
+public class AbstractValidationsServletTest {
+ private static AbstractValidationsServlet servlet = mock(AbstractValidationsServlet.class);
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testGetScarFromPayload() {
+
+ String payloadName = "valid_vf.csar";
+ String rootPath = System.getProperty("user.dir");
+ Path path = null;
+ byte[] data = null;
+ String payloadData = null;
+ Either<Map<String, byte[]>, ResponseFormat> returnValue = null;
+ try {
+ path = Paths.get(rootPath + "/src/test/resources/valid_vf.csar");
+ data = Files.readAllBytes(path);
+ payloadData = Base64.encodeBase64String(data);
+ UploadResourceInfo resourceInfo = new UploadResourceInfo();
+ resourceInfo.setPayloadName(payloadName);
+ resourceInfo.setPayloadData(payloadData);
+ Method privateMethod = null;
+ privateMethod = AbstractValidationsServlet.class.getDeclaredMethod("getScarFromPayload", UploadResourceInfo.class);
+ privateMethod.setAccessible(true);
+ returnValue = (Either<Map<String, byte[]>, ResponseFormat>) privateMethod.invoke(servlet, resourceInfo);
+ } catch (IOException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ Assert.assertTrue(returnValue.isLeft());
+ Map<String, byte[]> csar = returnValue.left().value();
+ Assert.assertTrue(csar != null);
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ApplicationConfig.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ApplicationConfig.java
new file mode 100644
index 0000000000..2dda0f5d14
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ApplicationConfig.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+import org.glassfish.jersey.media.multipart.MultiPart;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.servlets.ResourceUploadServlet;
+
+public class ApplicationConfig extends Application {
+
+ public Set<Class<?>> getClasses() {
+ final Set<Class<?>> resources = new HashSet<Class<?>>();
+
+ // Add your resources.
+ resources.add(ResourceUploadServlet.class);
+ resources.add(MultiPart.class);
+ resources.add(FormDataContentDisposition.class);
+
+ final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+
+ ResourceConfig resourceConfig = ResourceConfig.forApplication(this);
+
+ resourceConfig.register(new AbstractBinder() {
+
+ @Override
+ protected void configure() {
+ // TODO Auto-generated method stub
+ bind(request).to(HttpServletRequest.class);
+ }
+ });
+
+ return resources;
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceServletTest.java
new file mode 100644
index 0000000000..5e1e4577e2
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceServletTest.java
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.ServletUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.servlets.ResourcesServlet;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class ResourceServletTest extends JerseyTest {
+ public static final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ public static final ResourceImportManager resourceImportManager = Mockito.mock(ResourceImportManager.class);
+ final static HttpSession session = Mockito.mock(HttpSession.class);
+ final static ServletContext servletContext = Mockito.mock(ServletContext.class);
+ final static WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ final static WebApplicationContext webApplicationContext = Mockito.mock(WebApplicationContext.class);
+ public static final ServletUtils servletUtils = Mockito.mock(ServletUtils.class);
+ public static final ComponentsUtils componentUtils = Mockito.mock(ComponentsUtils.class);
+ public static final UserBusinessLogic userAdmin = Mockito.mock(UserBusinessLogic.class);
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ @BeforeClass
+ public static void setup() {
+ ExternalConfiguration.setAppName("catalog-be");
+ when(request.getSession()).thenReturn(session);
+ when(session.getServletContext()).thenReturn(servletContext);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext);
+ when(webApplicationContext.getBean(ResourceImportManager.class)).thenReturn(resourceImportManager);
+ when(webApplicationContext.getBean(ServletUtils.class)).thenReturn(servletUtils);
+ when(servletUtils.getComponentsUtils()).thenReturn(componentUtils);
+ when(servletUtils.getUserAdmin()).thenReturn(userAdmin);
+ String userId = "jh0003";
+ User user = new User();
+ user.setUserId(userId);
+ user.setRole(Role.ADMIN.name());
+ Either<User, ActionStatus> eitherUser = Either.left(user);
+ when(userAdmin.getUser(userId, false)).thenReturn(eitherUser);
+ when(request.getHeader(Constants.USER_ID_HEADER)).thenReturn(userId);
+
+ ImmutablePair<Resource, ActionStatus> pair = new ImmutablePair<Resource, ActionStatus>(new Resource(), ActionStatus.OK);
+ Either<ImmutablePair<Resource, ActionStatus>, ResponseFormat> ret = Either.left(pair);
+ when(resourceImportManager.importUserDefinedResource(Mockito.anyString(), Mockito.any(UploadResourceInfo.class), Mockito.any(User.class), Mockito.anyBoolean(), Mockito.anyBoolean())).thenReturn(ret);
+
+ }
+
+ @Before
+ public void beforeTest() {
+ Mockito.reset(componentUtils);
+
+ Mockito.doAnswer(new Answer<ResponseFormat>() {
+ public ResponseFormat answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ ActionStatus action = (ActionStatus) args[0];
+ ResponseFormat resp = (action == ActionStatus.OK) ? new ResponseFormat(HttpStatus.CREATED.value()) : new ResponseFormat(HttpStatus.INTERNAL_SERVER_ERROR.value());
+ return resp;
+ }
+ }).when(componentUtils).getResponseFormat(Mockito.any(ActionStatus.class));
+
+ }
+
+ @Test
+ public void testHappyScenarioTest() {
+ UploadResourceInfo validJson = buildValidJson();
+ setMD5OnRequest(true, validJson);
+ Response response = target().path("/v1/catalog/resources").request(MediaType.APPLICATION_JSON).post(Entity.json(gson.toJson(validJson)), Response.class);
+ Mockito.verify(componentUtils, Mockito.times(1)).getResponseFormat(Mockito.any(ActionStatus.class));
+ Mockito.verify(componentUtils, Mockito.times(1)).getResponseFormat(ActionStatus.OK);
+ assertTrue(response.getStatus() == HttpStatus.CREATED.value());
+
+ }
+
+ @Test
+ public void testNonValidMd5Fail() {
+ UploadResourceInfo validJson = buildValidJson();
+
+ setMD5OnRequest(false, validJson);
+
+ Response response = target().path("/v1/catalog/resources").request(MediaType.APPLICATION_JSON).post(Entity.json(gson.toJson(validJson)), Response.class);
+ Mockito.verify(componentUtils, Mockito.times(1)).getResponseFormat(Mockito.any(ActionStatus.class));
+ Mockito.verify(componentUtils, Mockito.times(1)).getResponseFormat(ActionStatus.INVALID_RESOURCE_CHECKSUM);
+ assertTrue(response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR.value());
+
+ }
+
+ @Test
+ public void testNonValidPayloadNameFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+ mdJson.setPayloadName("myCompute.xml");
+
+ runAndVerifyActionStatusError(mdJson, ActionStatus.INVALID_TOSCA_FILE_EXTENSION);
+
+ }
+
+ @Test
+ public void testNullPayloadFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+ mdJson.setPayloadData(null);
+ runAndVerifyActionStatusError(mdJson, ActionStatus.INVALID_RESOURCE_PAYLOAD);
+
+ }
+
+ @Test
+ public void testNonYmlPayloadFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+ String payload = "{ json : { isNot : yaml } ";
+ encodeAndSetPayload(mdJson, payload);
+ runAndVerifyActionStatusError(mdJson, ActionStatus.INVALID_YAML_FILE);
+
+ }
+
+ @Test
+ public void testNonToscaPayloadFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+
+ String payload = "node_types: \r\n" + " org.openecomp.resource.importResource4test:\r\n" + " derived_from: tosca.nodes.Root\r\n" + " description: update update";
+ encodeAndSetPayload(mdJson, payload);
+ runAndVerifyActionStatusError(mdJson, ActionStatus.INVALID_TOSCA_TEMPLATE);
+
+ }
+
+ @Test
+ public void testServiceToscaPayloadFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+
+ String payload = "tosca_definitions_version: tosca_simple_yaml_1_0_0\r\n" + "node_types: \r\n" + " org.openecomp.resource.importResource4test:\r\n" + " derived_from: tosca.nodes.Root\r\n" + " topology_template: thisIsService\r\n"
+ + " description: update update";
+
+ encodeAndSetPayload(mdJson, payload);
+ runAndVerifyActionStatusError(mdJson, ActionStatus.NOT_RESOURCE_TOSCA_TEMPLATE);
+
+ }
+
+ @Test
+ public void testMultipleResourcesInPayloadFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+
+ String payload = "tosca_definitions_version: tosca_simple_yaml_1_0_0\r\n" + "node_types: \r\n" + " org.openecomp.resource.importResource4test2:\r\n" + " derived_from: tosca.nodes.Root\r\n"
+ + " org.openecomp.resource.importResource4test:\r\n" + " derived_from: tosca.nodes.Root\r\n" + " description: update update";
+
+ encodeAndSetPayload(mdJson, payload);
+ runAndVerifyActionStatusError(mdJson, ActionStatus.NOT_SINGLE_RESOURCE);
+
+ }
+
+ @Test
+ public void testNonValidNameSpaceInPayloadFail() {
+ UploadResourceInfo mdJson = buildValidJson();
+
+ String payload = "tosca_definitions_version: tosca_simple_yaml_1_0_0\r\n" + "node_types: \r\n" + " org.openecomp.resourceX.importResource4test:\r\n" + " derived_from: tosca.nodes.Root\r\n" + " description: update update";
+
+ encodeAndSetPayload(mdJson, payload);
+ runAndVerifyActionStatusError(mdJson, ActionStatus.INVALID_RESOURCE_NAMESPACE);
+
+ }
+
+ private void encodeAndSetPayload(UploadResourceInfo mdJson, String payload) {
+ Base64.encodeBase64(payload.getBytes());
+ mdJson.setPayloadData(payload);
+ }
+
+ private void runAndVerifyActionStatusError(UploadResourceInfo mdJson, ActionStatus invalidResourcePayload) {
+ setMD5OnRequest(true, mdJson);
+ Response response = target().path("/v1/catalog/resources").request(MediaType.APPLICATION_JSON).post(Entity.json(gson.toJson(mdJson)), Response.class);
+ Mockito.verify(componentUtils, Mockito.times(1)).getResponseFormat(Mockito.any(ActionStatus.class));
+ Mockito.verify(componentUtils, Mockito.times(1)).getResponseFormat(invalidResourcePayload);
+ assertTrue(response.getStatus() == HttpStatus.INTERNAL_SERVER_ERROR.value());
+ }
+
+ private void setMD5OnRequest(boolean isValid, UploadResourceInfo json) {
+ String md5 = (isValid) ? GeneralUtility.calculateMD5ByString(gson.toJson(json)) : "stam=";
+ when(request.getHeader(Constants.MD5_HEADER)).thenReturn(md5);
+
+ }
+
+ private UploadResourceInfo buildValidJson() {
+ UploadResourceInfo ret = new UploadResourceInfo();
+ ret.setName("MyCompute");
+ ret.setPayloadName("MyCompute.yml");
+ ret.addSubCategory("Application Layer 4+", "Application Servers");
+ ret.setDescription("ResourceDescription");
+ ret.setVendorName("VendorName");
+ ret.setVendorRelease("VendorRelease");
+ ret.setContactId("AT1234");
+ ret.setIcon("router");
+ ret.setTags(Arrays.asList(new String[] { "MyCompute" }));
+ ret.setPayloadData(
+ "dG9zY2FfZGVmaW5pdGlvbnNfdmVyc2lvbjogdG9zY2Ffc2ltcGxlX3lhbWxfMV8wXzANCm5vZGVfdHlwZXM6IA0KICBvcmcub3BlbmVjb21wLnJlc291cmNlLk15Q29tcHV0ZToNCiAgICBkZXJpdmVkX2Zyb206IHRvc2NhLm5vZGVzLlJvb3QNCiAgICBhdHRyaWJ1dGVzOg0KICAgICAgcHJpdmF0ZV9hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIHB1YmxpY19hZGRyZXNzOg0KICAgICAgICB0eXBlOiBzdHJpbmcNCiAgICAgIG5ldHdvcmtzOg0KICAgICAgICB0eXBlOiBtYXANCiAgICAgICAgZW50cnlfc2NoZW1hOg0KICAgICAgICAgIHR5cGU6IHRvc2NhLmRhdGF0eXBlcy5uZXR3b3JrLk5ldHdvcmtJbmZvDQogICAgICBwb3J0czoNCiAgICAgICAgdHlwZTogbWFwDQogICAgICAgIGVudHJ5X3NjaGVtYToNCiAgICAgICAgICB0eXBlOiB0b3NjYS5kYXRhdHlwZXMubmV0d29yay5Qb3J0SW5mbw0KICAgIHJlcXVpcmVtZW50czoNCiAgICAgIC0gbG9jYWxfc3RvcmFnZTogDQogICAgICAgICAgY2FwYWJpbGl0eTogdG9zY2EuY2FwYWJpbGl0aWVzLkF0dGFjaG1lbnQNCiAgICAgICAgICBub2RlOiB0b3NjYS5ub2Rlcy5CbG9ja1N0b3JhZ2UNCiAgICAgICAgICByZWxhdGlvbnNoaXA6IHRvc2NhLnJlbGF0aW9uc2hpcHMuQXR0YWNoZXNUbw0KICAgICAgICAgIG9jY3VycmVuY2VzOiBbMCwgVU5CT1VOREVEXSAgDQogICAgY2FwYWJpbGl0aWVzOg0KICAgICAgaG9zdDogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5Db250YWluZXINCiAgICAgICAgdmFsaWRfc291cmNlX3R5cGVzOiBbdG9zY2Eubm9kZXMuU29mdHdhcmVDb21wb25lbnRdIA0KICAgICAgZW5kcG9pbnQgOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuRW5kcG9pbnQuQWRtaW4gDQogICAgICBvczogDQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5PcGVyYXRpbmdTeXN0ZW0NCiAgICAgIHNjYWxhYmxlOg0KICAgICAgICB0eXBlOiB0b3NjYS5jYXBhYmlsaXRpZXMuU2NhbGFibGUNCiAgICAgIGJpbmRpbmc6DQogICAgICAgIHR5cGU6IHRvc2NhLmNhcGFiaWxpdGllcy5uZXR3b3JrLkJpbmRhYmxl");
+ return ret;
+ }
+
+ @Override
+ protected Application configure() {
+
+ ResourceConfig resourceConfig = new ResourceConfig(ResourcesServlet.class);
+ forceSet(TestProperties.CONTAINER_PORT, "0");
+ resourceConfig.register(new AbstractBinder() {
+
+ @Override
+ protected void configure() {
+ bind(request).to(HttpServletRequest.class);
+ }
+ });
+
+ return resourceConfig;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java
new file mode 100644
index 0000000000..252e9d8fbc
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java
@@ -0,0 +1,189 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataBodyPart;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.be.resources.api.IResourceUploader;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.servlets.ResourceUploadServlet;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+
+public class ResourceUploadServletTest extends JerseyTest {
+ private static Logger log = LoggerFactory.getLogger(ResourceUploadServletTest.class.getName());
+ final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ final HttpSession session = Mockito.mock(HttpSession.class);
+ final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ final WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ final WebApplicationContext webApplicationContext = Mockito.mock(WebApplicationContext.class);
+ final IResourceUploader iResourceUploader = Mockito.mock(IResourceUploader.class);
+ final AuditingManager iAuditingManager = Mockito.mock(AuditingManager.class);
+
+ Gson gson = new Gson();
+
+ public void zipDirectory() {
+
+ }
+
+ @Before
+ public void setup() {
+ ExternalConfiguration.setAppName("catalog-be");
+
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ // when(servletContext.getAttribute(Constants.AUDITING_MANAGER)).thenReturn(iAuditingManager);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext);
+ when(webApplicationContext.getBean(IResourceUploader.class)).thenReturn(iResourceUploader);
+ when(iResourceUploader.saveArtifact((ESArtifactData) anyObject(), eq(true))).thenReturn(ResourceUploadStatus.OK);
+ when(webApplicationContext.getBean(AuditingManager.class)).thenReturn(iAuditingManager);
+ }
+
+ @Override
+ protected Application configure() {
+
+ ResourceConfig resourceConfig = new ResourceConfig(ResourceUploadServlet.class);
+
+ resourceConfig.register(MultiPartFeature.class);
+ resourceConfig.register(new AbstractBinder() {
+
+ @Override
+ protected void configure() {
+ // The below code was cut-pasted to here from setup() because
+ // due to it now has
+ // to be executed during servlet initialization
+ bind(request).to(HttpServletRequest.class);
+ when(request.getSession()).thenReturn(session);
+ when(session.getServletContext()).thenReturn(servletContext);
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+ for (String mandatoryHeader : configurationManager.getConfiguration().getIdentificationHeaderFields()) {
+
+ when(request.getHeader(mandatoryHeader)).thenReturn(mandatoryHeader);
+
+ }
+
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ }
+ });
+
+ return resourceConfig;
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(MultiPartFeature.class);
+ }
+
+ public class DelegatingServletInputStream extends ServletInputStream {
+
+ private final InputStream sourceStream;
+
+ /**
+ * Create a DelegatingServletInputStream for the given source stream.
+ *
+ * @param sourceStream
+ * the source stream (never <code>null</code>)
+ */
+ public DelegatingServletInputStream(InputStream sourceStream) {
+ // Assert.notNull(sourceStream,
+ // "Source InputStream must not be null");
+ this.sourceStream = sourceStream;
+ }
+
+ /**
+ * Return the underlying source stream (never <code>null</code>).
+ */
+ public final InputStream getSourceStream() {
+ return this.sourceStream;
+ }
+
+ public int read() throws IOException {
+ return this.sourceStream.read();
+ }
+
+ public void close() throws IOException {
+ super.close();
+ this.sourceStream.close();
+ }
+
+ }
+
+ @Test
+ public void testMultipart() {
+ FileDataBodyPart filePart = new FileDataBodyPart("resourceZip", new File("src/test/resources/config/normative-types-root.zip"));
+
+ List<String> tags = new ArrayList<String>();
+ tags.add("tag1");
+ tags.add("tag2");
+ UploadResourceInfo resourceInfo = new UploadResourceInfo("payload", "normative-types-root.yml", "my_description", "category/mycategory", tags, null);
+
+ FormDataBodyPart metadataPart = new FormDataBodyPart("resourceMetadata", gson.toJson(resourceInfo), MediaType.APPLICATION_JSON_TYPE);
+ MultiPart multipartEntity = new FormDataMultiPart();
+ multipartEntity.bodyPart(filePart);
+ multipartEntity.bodyPart(metadataPart);
+
+ Response response = target().path("/v1/catalog/upload/" + ResourceUploadServlet.NORMATIVE_TYPE_RESOURCE).request(MediaType.APPLICATION_JSON).post(Entity.entity(multipartEntity, MediaType.MULTIPART_FORM_DATA), Response.class);
+ log.debug("{}", response);
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/TypesUploadServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/TypesUploadServletTest.java
new file mode 100644
index 0000000000..420cbcca6b
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/TypesUploadServletTest.java
@@ -0,0 +1,154 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.grizzly.http.util.HttpStatus;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+import org.glassfish.jersey.media.multipart.file.FileDataBodyPart;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.ServletUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.servlets.TypesUploadServlet;
+import org.openecomp.sdc.be.user.Role;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.web.context.WebApplicationContext;
+
+import fj.data.Either;
+
+public class TypesUploadServletTest extends JerseyTest {
+
+ public static final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ public static final HttpSession session = Mockito.mock(HttpSession.class);
+ public static final ServletContext servletContext = Mockito.mock(ServletContext.class);
+ public static final WebAppContextWrapper webAppContextWrapper = Mockito.mock(WebAppContextWrapper.class);
+ public static final WebApplicationContext webApplicationContext = Mockito.mock(WebApplicationContext.class);
+ public static final CapabilityTypeImportManager importManager = Mockito.mock(CapabilityTypeImportManager.class);
+ public static final ServletUtils servletUtils = Mockito.mock(ServletUtils.class);
+ public static final UserBusinessLogic userAdmin = Mockito.mock(UserBusinessLogic.class);
+ public static final ComponentsUtils componentUtils = Mockito.mock(ComponentsUtils.class);
+ public static final ResponseFormat responseFormat = Mockito.mock(ResponseFormat.class);
+
+ @BeforeClass
+ public static void setup() {
+ ExternalConfiguration.setAppName("catalog-be");
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext);
+ when(webApplicationContext.getBean(CapabilityTypeImportManager.class)).thenReturn(importManager);
+ when(webApplicationContext.getBean(ServletUtils.class)).thenReturn(servletUtils);
+ when(servletUtils.getComponentsUtils()).thenReturn(componentUtils);
+ when(servletUtils.getUserAdmin()).thenReturn(userAdmin);
+ String userId = "jh0003";
+ User user = new User();
+ user.setUserId(userId);
+ user.setRole(Role.ADMIN.name());
+ Either<User, ActionStatus> eitherUser = Either.left(user);
+ when(userAdmin.getUser(userId, false)).thenReturn(eitherUser);
+ when(request.getHeader(Constants.USER_ID_HEADER)).thenReturn(userId);
+ when(responseFormat.getStatus()).thenReturn(HttpStatus.CREATED_201.getStatusCode());
+ when(componentUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat);
+
+ }
+
+ @Test
+ public void creatingCapabilityTypeSuccessTest() {
+ List<CapabilityTypeDefinition> emptyList = new ArrayList<CapabilityTypeDefinition>();
+ Either<List<CapabilityTypeDefinition>, ResponseFormat> either = Either.left(emptyList);
+ when(importManager.createCapabilityTypes(Mockito.anyString())).thenReturn(either);
+ FileDataBodyPart filePart = new FileDataBodyPart("capabilityTypeZip", new File("src/test/resources/types/capabilityTypes.zip"));
+ MultiPart multipartEntity = new FormDataMultiPart();
+ multipartEntity.bodyPart(filePart);
+
+ Response response = target().path("/v1/catalog/uploadType/capability").request(MediaType.APPLICATION_JSON).post(Entity.entity(multipartEntity, MediaType.MULTIPART_FORM_DATA), Response.class);
+
+ assertTrue(response.getStatus() == HttpStatus.CREATED_201.getStatusCode());
+
+ }
+
+ @Override
+ protected void configureClient(ClientConfig config) {
+ config.register(MultiPartFeature.class);
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig resourceConfig = new ResourceConfig(TypesUploadServlet.class);
+
+ resourceConfig.register(MultiPartFeature.class);
+ resourceConfig.register(new AbstractBinder() {
+
+ @Override
+ protected void configure() {
+ // The below code was cut-pasted to here from setup() because
+ // due to it now has
+ // to be executed during servlet initialization
+ bind(request).to(HttpServletRequest.class);
+ when(request.getSession()).thenReturn(session);
+ when(session.getServletContext()).thenReturn(servletContext);
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ ConfigurationManager configurationManager = new ConfigurationManager(configurationSource);
+ for (String mandatoryHeader : configurationManager.getConfiguration().getIdentificationHeaderFields()) {
+
+ when(request.getHeader(mandatoryHeader)).thenReturn(mandatoryHeader);
+
+ }
+
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ }
+ });
+
+ return resourceConfig;
+ }
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/UserAdminServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/UserAdminServletTest.java
new file mode 100644
index 0000000000..ed69f104d2
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/UserAdminServletTest.java
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.servlets;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.openecomp.sdc.be.auditing.impl.AuditingManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.impl.WebAppContextWrapper;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.servlets.UserAdminServlet;
+import org.openecomp.sdc.be.user.UserBusinessLogic;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.UserRoleEnum;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+public class UserAdminServletTest extends JerseyTest {
+
+ final static HttpServletRequest request = mock(HttpServletRequest.class);
+ final static HttpSession session = mock(HttpSession.class);
+ final static ServletContext servletContext = mock(ServletContext.class);
+ final static WebAppContextWrapper webAppContextWrapper = mock(WebAppContextWrapper.class);
+ final static WebApplicationContext webApplicationContext = mock(WebApplicationContext.class);
+ final static UserBusinessLogic userAdminManager = spy(UserBusinessLogic.class);
+ final static AuditingManager auditingManager = mock(AuditingManager.class);
+ final static ComponentsUtils componentsUtils = mock(ComponentsUtils.class);
+ final static ResponseFormat okResponseFormat = mock(ResponseFormat.class);
+
+ final static String ADMIN_ATT_UID = "jh0003";
+ Gson gson = new Gson();
+
+ @BeforeClass
+ public static void setup() {
+ ExternalConfiguration.setAppName("catalog-be");
+
+ when(session.getServletContext()).thenReturn(servletContext);
+ when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)).thenReturn(webAppContextWrapper);
+ when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext);
+
+ when(webApplicationContext.getBean(UserBusinessLogic.class)).thenReturn(userAdminManager);
+ when(webApplicationContext.getBean(ComponentsUtils.class)).thenReturn(componentsUtils);
+ when(componentsUtils.getAuditingManager()).thenReturn(auditingManager);
+ when(componentsUtils.getResponseFormat(ActionStatus.OK)).thenReturn(okResponseFormat);
+ when(okResponseFormat.getStatus()).thenReturn(HttpStatus.OK.value());
+
+ }
+
+ @Before
+ public void beforeTest() {
+ reset(userAdminManager);
+ doReturn(buildEitherUser(ADMIN_ATT_UID, true)).when(userAdminManager).getUser(ADMIN_ATT_UID, false);
+
+ reset(request);
+ when(request.getSession()).thenReturn(session);
+ when(request.getHeader("USER_ID")).thenReturn(ADMIN_ATT_UID);
+ }
+
+ /*
+ * @Test public void deactivateUserSuccessfullyTest(){ String userToDeleteUserId = "admin1"; User adminUser = new User(); adminUser.setUserId(ADMIN_ATT_UID); Either<User, ActionStatus> eitherActiveUser = buildEitherUser(userToDeleteUserId, true);
+ * User userToDelete = eitherActiveUser.left().value(); doReturn(eitherActiveUser).when(userAdminManager).getUser( userToDeleteUserId);
+ *
+ * Either<User, ActionStatus> eitherInactiveUser = buildEitherUser(userToDeleteUserId, false); doReturn(eitherInactiveUser).when(userAdminManager).deActivateUser( adminUser, userToDelete.getUserId());
+ *
+ *
+ * Response response = target().path("/v1/user/"+userToDeleteUserId).request().delete(); assertTrue(response.getStatus() == HttpStatus.OK.value()); verify(userAdminManager, times(1)).deActivateUser(adminUser, userToDelete.getUserId()); }
+ *
+ *
+ * @Test public void forceDeleteUserSuccessfullyTest(){ String userToDeleteUserId = "admin1"; when(request.getHeader(User.FORCE_DELETE_HEADER_FLAG)).thenReturn(User. FORCE_DELETE_HEADER_FLAG);
+ *
+ * User adminUser = new User(); adminUser.setUserId(ADMIN_ATT_UID);
+ *
+ * Either<User, ActionStatus> eitherActiveUser = buildEitherUser(userToDeleteUserId, true); User userToDelete = eitherActiveUser.left().value(); doReturn(eitherActiveUser).when(userAdminManager).getUser( userToDeleteUserId);
+ *
+ * Either<User, ActionStatus> eitherUser = buildEitherUser(userToDeleteUserId, true); doReturn(eitherUser).when(userAdminManager).deleteUser(userToDelete. getUserId());
+ *
+ *
+ * Response response = target().path("/v1/user/"+userToDeleteUserId).request().delete(); assertTrue(response.getStatus() == HttpStatus.OK.value()); verify(userAdminManager, times(0)).deActivateUser(adminUser, userToDelete.getUserId());
+ * verify(userAdminManager, times(1)).deleteUser(userToDelete.getUserId()); }
+ */
+
+ @Override
+ protected Application configure() {
+
+ ResourceConfig resourceConfig = new ResourceConfig(UserAdminServlet.class);
+
+ resourceConfig.register(new AbstractBinder() {
+
+ @Override
+ protected void configure() {
+ bind(request).to(HttpServletRequest.class);
+ }
+ });
+
+ return resourceConfig;
+ }
+
+ private static Either<User, ActionStatus> buildEitherUser(String userId, boolean isActive) {
+ User user = new User();
+ user.setUserId(userId);
+ user.setRole(UserRoleEnum.ADMIN.getName());
+ if (!isActive) {
+ user.setStatus(UserStatusEnum.INACTIVE);
+ }
+ return Either.left(user);
+ }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/user/UserAdminManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/user/UserAdminManagerTest.java
new file mode 100644
index 0000000000..4b01b4fd61
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/user/UserAdminManagerTest.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.user;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.api.IUsersDAO;
+import org.openecomp.sdc.be.dao.impl.Neo4jUsersDAO;
+import org.openecomp.sdc.be.resources.data.UserData;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class UserAdminManagerTest {
+
+ final IUsersDAO usersDao = Mockito.mock(Neo4jUsersDAO.class);
+ Gson gson;
+
+ @Before
+ public void setup() {
+ Either<UserData, ActionStatus> eitherOk = Either.right(ActionStatus.OK);
+ gson = new GsonBuilder().setPrettyPrinting().create();
+ when(usersDao.getUserData(anyString())).thenReturn(eitherOk);
+ when(usersDao.saveUserData((UserData) anyObject())).thenReturn(ActionStatus.OK);
+ when(usersDao.updateUserData((UserData) anyObject())).thenReturn(ActionStatus.OK);
+ when(usersDao.deleteUserData(anyString())).thenReturn(ActionStatus.OK);
+ }
+
+ // @Test
+ // public void testCreateUser() {
+ // String json = "{\"firstName\": \"James\",\"lastName\":
+ // \"Brown\",\"userId\": \"jb1234u\",\"email\":
+ // \"jb1234u@sdc.com\",\"role\": \"ADMIN\"}";
+ // UserData user = gson.fromJson(json, UserData.class);
+ // Either<UserData,ActionStatus> either =
+ // UserAdminManager.getInstance().createUser(user);
+ // assertTrue(either.isRight());
+ // assertEquals(ActionStatus.OK, either.right().value());
+ // }
+ //
+ //
+ // @Test
+ // public void testCreateUserInvalidEmail() {
+ // String json = "{\"firstName\": \"James\",\"lastName\":
+ // \"Brown\",\"userId\": \"jb1234u\",\"email\": \"@sdc.com\",\"role\":
+ // \"ADMIN\"}";
+ // UserData user = gson.fromJson(json, UserData.class);
+ // Either<UserData,ActionStatus> either =
+ // UserAdminManager.getInstance().createUser(user);
+ // assertTrue(either.isRight());
+ // assertEquals(ActionStatus.INVALID_EMAIL_ADDRESS, either.right().value());
+ // }
+ //
+ // @Test
+ // public void testCreateUserInvalidRole() {
+ // String json = "{\"firstName\": \"James\",\"lastName\":
+ // \"Brown\",\"userId\": \"jb1234u\",\"email\":
+ // \"jb1234u@sdc.com\",\"role\": \"MIN\"}";
+ // UserData user = gson.fromJson(json, UserData.class);
+ // Either<UserData,ActionStatus> either =
+ // UserAdminManager.getInstance().createUser(user);
+ // assertTrue(either.isRight());
+ // assertEquals(ActionStatus.INVALID_EMAIL_ADDRESS, either.right().value());
+ // }
+
+}
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/common/transaction/mngr/SdncTransactionTest.java b/catalog-be/src/test/java/org/openecomp/sdc/common/transaction/mngr/SdncTransactionTest.java
new file mode 100644
index 0000000000..bf87033bfe
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/common/transaction/mngr/SdncTransactionTest.java
@@ -0,0 +1,437 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.transaction.mngr;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.dao.impl.ESCatalogDAO;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.common.transaction.api.IDBAction;
+import org.openecomp.sdc.common.transaction.api.RollbackHandler;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ActionTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBActionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.DBTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.ESActionTypeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.LogMessages;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.TransactionCodeEnum;
+import org.openecomp.sdc.common.transaction.api.TransactionUtils.TransactionStatusEnum;
+import org.openecomp.sdc.common.transaction.mngr.CommitManager;
+import org.openecomp.sdc.common.transaction.mngr.TransactionSdncImpl;
+import org.slf4j.Logger;
+
+import fj.data.Either;
+
+public class SdncTransactionTest {
+ private static ESCatalogDAO esCatalogDao = Mockito.mock(ESCatalogDAO.class);
+ private static TitanGenericDao titanGenericDao = Mockito.mock(TitanGenericDao.class);
+ private static Logger log = Mockito.spy(Logger.class);
+ private static int transactionId = 0;
+ private static ConfigurationManager configurationManager;
+
+ public enum TestAction {
+ TitanAction, Rollback, GeneralAction
+ }
+
+ public enum TestResponse {
+ TitanResponseSuccess, GeneralSuccess
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+ TransactionSdncImpl.setLog(log);
+ CommitManager.setLog(log);
+ RollbackHandler.setLog(log);
+ String appConfigDir = "src/test/resources/config/catalog-be";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+ }
+
+ @Before
+ public void beforeTest() {
+ reset(log);
+ reset(esCatalogDao);
+ reset(titanGenericDao);
+ }
+
+ @Test
+ public void testInvokeTitanAction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+
+ doBasicTitanAction(transactionId, tx, false, true);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.OPEN);
+ }
+
+ @Test
+ public void testInvokeESAction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+
+ doESAddArtifactAction(transactionId, tx, true, true);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.OPEN);
+ }
+
+ @Test
+ public void testfinishTransaction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doFinishTransaction(transactionId, tx, true);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+ }
+
+ @Test
+ public void testFinishOnClosedTransaction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doFinishTransaction(transactionId, tx, true);
+
+ TransactionCodeEnum finishTransaction = tx.finishTransaction();
+ assertTrue(finishTransaction == TransactionCodeEnum.TRANSACTION_CLOSED);
+ // verify(log).error(LogMessages.COMMIT_ON_CLOSED_TRANSACTION,
+ // transactionId, TransactionStatusEnum.CLOSED.name(),
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.COMMIT_ON_CLOSED_TRANSACTION, transactionId, TransactionStatusEnum.CLOSED.name(), TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+
+ }
+
+ @Test
+ public void testCallingLastActionTwice() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doBasicTitanAction(transactionId, tx, true, true);
+ Either<TestResponse, TransactionCodeEnum> doBasicTitanAction = doBasicTitanAction(transactionId, tx, true, false);
+ assertTrue(doBasicTitanAction.isRight());
+ assertTrue(tx.getStatus() != TransactionStatusEnum.OPEN);
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.DOUBLE_FINISH_FLAG_ACTION, transactionId, DBTypeEnum.TITAN.name(), TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ // verify(log).error( LogMessages.DOUBLE_FINISH_FLAG_ACTION,
+ // transactionId, DBTypeEnum.TITAN.name(), TransactionUtils.DUMMY_USER,
+ // ActionTypeEnum.ADD_ARTIFACT.name());
+ }
+
+ @Test
+ public void testActionOnClosedTransaction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doFinishTransaction(transactionId, tx, true);
+
+ Either<DBActionCodeEnum, TransactionCodeEnum> eitherESResult = tx.invokeESAction(false, ESActionTypeEnum.ADD_ARTIFACT, createDummyArtifactData());
+ assertTrue(eitherESResult.isRight());
+ assertTrue(eitherESResult.right().value() == TransactionCodeEnum.TRANSACTION_CLOSED);
+
+ Either<Object, TransactionCodeEnum> eitherTitanResult = tx.invokeTitanAction(false, createBasicAction(TestAction.TitanAction, TestResponse.TitanResponseSuccess));
+ assertTrue(eitherTitanResult.isRight());
+ assertTrue(eitherTitanResult.right().value() == TransactionCodeEnum.TRANSACTION_CLOSED);
+
+ Either<Object, TransactionCodeEnum> eitherGeneralDBAction = tx.invokeGeneralDBAction(true, DBTypeEnum.TITAN, createBasicAction(TestAction.TitanAction, TestResponse.TitanResponseSuccess),
+ createBasicAction(TestAction.Rollback, TestResponse.TitanResponseSuccess));
+ assertTrue(eitherGeneralDBAction.isRight());
+ assertTrue(eitherGeneralDBAction.right().value() == TransactionCodeEnum.TRANSACTION_CLOSED);
+
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+ // verify(log, times(3)).error(LogMessages.ACTION_ON_CLOSED_TRANSACTION,
+ // transactionId, TransactionUtils.DUMMY_USER,
+ // ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(3)).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.ACTION_ON_CLOSED_TRANSACTION, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ }
+
+ @Test
+ public void testBasicHappyScenario() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+
+ doBasicTitanAction(transactionId, tx, false, true);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.OPEN);
+
+ doESAddArtifactAction(transactionId, tx, true, true);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.OPEN);
+
+ doFinishTransaction(transactionId, tx, true);
+
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+
+ }
+
+ @Test
+ public void testRollbackSucceededOnAction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doESAddArtifactAction(transactionId, tx, false, true);
+
+ when(titanGenericDao.rollback()).thenReturn(TitanOperationStatus.OK);
+ String crushMessage = "DB Crush Simulation";
+ Either<TestResponse, TransactionCodeEnum> eitherTransactionResult = tx.invokeTitanAction(false, createCrushingAction(TestAction.TitanAction, crushMessage));
+
+ assertTrue(eitherTransactionResult.isRight());
+ assertTrue(eitherTransactionResult.right().value() == TransactionCodeEnum.ROLLBACK_SUCCESS);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+ // verify(log).error(LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION,
+ // DBTypeEnum.TITAN.name(), transactionId, crushMessage,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION, DBTypeEnum.TITAN.name(), transactionId, crushMessage, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log).info(LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ }
+
+ @Test
+ public void testRollbackFailedOnAction() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+
+ doESAddArtifactAction(transactionId, tx, false, true);
+
+ when(titanGenericDao.rollback()).thenReturn(TitanOperationStatus.NOT_CONNECTED);
+ String crushMessage = "DB Crush Simulation";
+ Either<TestResponse, TransactionCodeEnum> eitherTransactionResult = tx.invokeTitanAction(false, createCrushingAction(TestAction.TitanAction, crushMessage));
+
+ assertTrue(eitherTransactionResult.isRight());
+ assertTrue(tx.getStatus() == TransactionStatusEnum.FAILED_ROLLBACK);
+ assertTrue(eitherTransactionResult.right().value() == TransactionCodeEnum.ROLLBACK_FAILED);
+ // verify(log).error(LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION,
+ // DBTypeEnum.TITAN.name(), transactionId, crushMessage,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION, DBTypeEnum.TITAN.name(), transactionId, crushMessage, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ // verify(log, times(1)).error(LogMessages.ROLLBACK_FAILED_GENERAL,
+ // transactionId, TransactionUtils.DUMMY_USER,
+ // ActionTypeEnum.ADD_ARTIFACT.name());
+ // verify(log, times(1)).error(TransactionUtils.TRANSACTION_MARKER,
+ // LogMessages.ROLLBACK_FAILED_GENERAL, transactionId,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ }
+
+ @Test
+ public void testRollbackSucceededOnCommit() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doESAddArtifactAction(transactionId, tx, false, true);
+ doBasicTitanAction(transactionId, tx, true, true);
+
+ when(titanGenericDao.commit()).thenReturn(TitanOperationStatus.GENERAL_ERROR);
+ when(titanGenericDao.rollback()).thenReturn(TitanOperationStatus.OK);
+ // finishTransaction
+ TransactionCodeEnum transactionCode = tx.finishTransaction();
+ assertTrue(transactionCode == TransactionCodeEnum.ROLLBACK_SUCCESS);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log).info(LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ }
+
+ @Test
+ public void testRollbackFailedOnCommit() {
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ doESAddArtifactAction(transactionId, tx, false, true);
+ doBasicTitanAction(transactionId, tx, true, true);
+
+ when(titanGenericDao.commit()).thenReturn(TitanOperationStatus.GENERAL_ERROR);
+ when(titanGenericDao.rollback()).thenReturn(TitanOperationStatus.OK);
+ String esError = "No Connection to Es";
+ Mockito.doThrow(new RuntimeException(esError)).when(esCatalogDao).deleteArtifact(Mockito.anyString());
+ // finishTransaction
+ TransactionCodeEnum transactionCode = tx.finishTransaction();
+ assertTrue(transactionCode == TransactionCodeEnum.ROLLBACK_FAILED);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.FAILED_ROLLBACK);
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.ELASTIC_SEARCH.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(1)).debug(LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(1)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_NON_PERSISTENT_ACTION, DBTypeEnum.TITAN.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ // verify(log).error(LogMessages.ROLLBACK_FAILED_ON_DB_WITH_EXCEPTION,
+ // transactionId, DBTypeEnum.ELASTIC_SEARCH.name(), esError,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ // verify(log).error(TransactionUtils.TRANSACTION_MARKER,
+ // LogMessages.ROLLBACK_FAILED_ON_DB_WITH_EXCEPTION, transactionId,
+ // DBTypeEnum.ELASTIC_SEARCH.name(), esError,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ // verify(log, times(1)).error(LogMessages.ROLLBACK_FAILED_GENERAL,
+ // transactionId, TransactionUtils.DUMMY_USER,
+ // ActionTypeEnum.ADD_ARTIFACT.name());
+ // verify(log, times(1)).error(TransactionUtils.TRANSACTION_MARKER,
+ // LogMessages.ROLLBACK_FAILED_GENERAL, transactionId,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ }
+
+ @Test
+ public void testInvokeGeneralAction() {
+ when(titanGenericDao.rollback()).thenReturn(TitanOperationStatus.OK);
+ int transactionId = getNextTransactionId();
+ TransactionSdncImpl tx = new TransactionSdncImpl(transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT, esCatalogDao, titanGenericDao);
+ IDBAction generalAction = createBasicAction(TestAction.GeneralAction, TestResponse.GeneralSuccess);
+ IDBAction rollbackAction = createBasicAction(TestAction.Rollback, TestResponse.GeneralSuccess);
+ String crushMessage = "No DB Connection";
+ IDBAction crushingAction = createCrushingAction(TestAction.GeneralAction, crushMessage);
+
+ Either<TestResponse, TransactionCodeEnum> eitherResult = tx.invokeGeneralDBAction(false, DBTypeEnum.MYSTERY, generalAction, rollbackAction);
+ assertTrue(eitherResult.isLeft());
+ assertTrue(eitherResult.left().value() == TestResponse.GeneralSuccess);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.OPEN);
+ eitherResult = tx.invokeGeneralDBAction(false, DBTypeEnum.MYSTERY, crushingAction, rollbackAction);
+
+ assertTrue(eitherResult.isRight());
+ assertTrue(eitherResult.right().value() == TransactionCodeEnum.ROLLBACK_SUCCESS);
+ assertTrue(tx.getStatus() == TransactionStatusEnum.CLOSED);
+
+ // verify(log).error(LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION,
+ // DBTypeEnum.MYSTERY.name(), transactionId, crushMessage,
+ // TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.DB_ACTION_FAILED_WITH_EXCEPTION, DBTypeEnum.MYSTERY.name(), transactionId, crushMessage, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log, times(2)).debug(LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.MYSTERY.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log, times(2)).debug(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_PERSISTENT_ACTION, DBTypeEnum.MYSTERY.name(), transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ verify(log).info(LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).info(TransactionUtils.TRANSACTION_MARKER, LogMessages.ROLLBACK_SUCCEEDED_GENERAL, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+
+ }
+
+ private Either<TestResponse, TransactionCodeEnum> doBasicTitanAction(int transactionId, TransactionSdncImpl tx, boolean isLastAction, boolean isVerifyAction) {
+ // Add Titan Action
+ Either<TestResponse, TransactionCodeEnum> eitherTitanResult = tx.invokeTitanAction(isLastAction, createBasicAction(TestAction.TitanAction, TestResponse.TitanResponseSuccess));
+ if (isVerifyAction) {
+ // Check Titan Action
+ assertTrue(eitherTitanResult.isLeft());
+ assertTrue(eitherTitanResult.left().value() == TestResponse.TitanResponseSuccess);
+ verify(log).debug(TestAction.TitanAction.name());
+ verify(log).debug(LogMessages.INVOKE_ACTION, transactionId, DBTypeEnum.TITAN.name(), TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verifyNoErrorsInLog();
+ verifyNoInfoInLog();
+ }
+ return eitherTitanResult;
+ }
+
+ private TransactionCodeEnum doFinishTransaction(int transactionId, TransactionSdncImpl tx, boolean isVerifyAction) {
+ // Prerequisite finishTransaction
+ when(titanGenericDao.commit()).thenReturn(TitanOperationStatus.OK);
+ // finishTransaction
+ TransactionCodeEnum transactionCode = tx.finishTransaction();
+ if (isVerifyAction) {
+ // Check finishTransaction
+ verify(log).debug(LogMessages.COMMIT_ACTION_ALL_DB, transactionId, TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verify(log).debug(LogMessages.COMMIT_ACTION_SPECIFIC_DB, transactionId, DBTypeEnum.TITAN.name(), TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ assertTrue(transactionCode == TransactionCodeEnum.SUCCESS);
+ }
+ return transactionCode;
+ }
+
+ private void doESAddArtifactAction(int transactionId, TransactionSdncImpl tx, boolean isLastAction, boolean isVerifyAction) {
+ // Prerequisite ES Action
+ Either<ESArtifactData, ResourceUploadStatus> eitherBeforeAddArtifact = Either.right(ResourceUploadStatus.NOT_EXIST);
+ when(esCatalogDao.getArtifact(Mockito.anyString())).thenReturn(eitherBeforeAddArtifact);
+
+ // Add ES Action
+ Either<DBActionCodeEnum, TransactionCodeEnum> eitherEsAction = tx.invokeESAction(isLastAction, ESActionTypeEnum.ADD_ARTIFACT, createDummyArtifactData());
+
+ if (isVerifyAction) {
+ // Check Titan Action
+ assertTrue(eitherEsAction.isLeft());
+ assertTrue(eitherEsAction.left().value() == DBActionCodeEnum.SUCCESS);
+ verify(log).debug(LogMessages.INVOKE_ACTION, transactionId, DBTypeEnum.ELASTIC_SEARCH.name(), TransactionUtils.DUMMY_USER, ActionTypeEnum.ADD_ARTIFACT.name());
+ verifyNoErrorsInLog();
+ verifyNoInfoInLog();
+ }
+ }
+
+ private ESArtifactData createDummyArtifactData() {
+ String strData = "qweqwqweqw34e4wrwer";
+ String myNodeType = "MyNewNodeType";
+ ESArtifactData arData = new ESArtifactData("artifactNewMarina11", strData.getBytes());
+ return arData;
+ }
+
+ private void verifyNoErrorsInLog() {
+ verify(log, Mockito.times(0)).error(Mockito.anyString(), Mockito.any(Object[].class));
+ verify(log, Mockito.times(0)).error(Mockito.anyString());
+ }
+
+ private void verifyNoInfoInLog() {
+ verify(log, Mockito.times(0)).info(Mockito.anyString(), Mockito.any(Object[].class));
+ verify(log, Mockito.times(0)).info(Mockito.anyString());
+ }
+
+ private IDBAction createBasicAction(TestAction action, TestResponse resp) {
+ final TestAction finalAction = action;
+ final TestResponse finalResp = resp;
+ return new IDBAction() {
+ @Override
+ public TestResponse doAction() {
+ log.debug(finalAction.name());
+ return finalResp;
+ }
+ };
+ }
+
+ private IDBAction createCrushingAction(TestAction action, final String crushMessage) {
+ final TestAction finalAction = action;
+ return new IDBAction() {
+ @Override
+ public TestResponse doAction() {
+ log.debug(finalAction.name());
+ throw new RuntimeException(crushMessage);
+ }
+ };
+ }
+
+ public int getNextTransactionId() {
+ transactionId++;
+ return transactionId;
+ }
+}
diff --git a/catalog-be/src/test/resources/config/catalog-be/configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/configuration.yaml
new file mode 100644
index 0000000000..18d23707b1
--- /dev/null
+++ b/catalog-be/src/test/resources/config/catalog-be/configuration.yaml
@@ -0,0 +1,427 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+
+# catalog backend hostname
+beFqdn: localhost
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: /home/vagrant/catalog-be/config/catalog-be/titan.properties
+titanInMemoryGraph: true
+titanLockTimeout: 600
+titanReconnectIntervalInSeconds: 3
+titanHealthCheckReadTimeout: 1
+esReconnectIntervalInSeconds: 3
+uebHealthCheckReconnectIntervalInSeconds: 15
+uebHealthCheckReadTimeout: 4
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+neo4j:
+ host: neo4jhost
+ port: 7474
+ user: neo4j
+ password: "12345"
+
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # PLEASE NOTE: the timestamps are created in UTC/GMT timezone! This is needed so that timestamps will be correctly presented in Kibana.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+ - indexPrefix: monitoring_events
+ creationPeriod: month
+
+artifactTypes:
+ - CHEF
+ - PUPPET
+ - SHELL
+ - YANG
+ - YANG_XML
+ - HEAT
+ - BPEL
+ - DG_XML
+ - MURANO_PKG
+ - WORKFLOW
+ - NETWORK_CALL_FLOW
+ - TOSCA_TEMPLATE
+ - TOSCA_CSAR
+ - AAI_SERVICE_MODEL
+ - AAI_VF_MODEL
+ - AAI_VF_MODULE_MODEL
+ - AAI_VF_INSTANCE_MODEL
+ - OTHER
+
+licenseTypes:
+ - User
+ - Installation
+ - CPU
+
+#Deployment artifacts placeHolder
+resourceTypes: &allResourceTypes
+ - VFC
+ - CP
+ - VL
+ - VF
+
+# validForResourceTypes usage
+# validForResourceTypes:
+# - VF
+# - VL
+deploymentResourceArtifacts:
+# heat:
+# displayName: "Base HEAT Template"
+# type: HEAT
+# validForResourceTypes: *allResourceTypes
+# heatVol:
+# displayName: "Volume HEAT Template"
+# type: HEAT_VOL
+# validForResourceTypes: *allResourceTypes
+# heatNet:
+# displayName: "Network HEAT Template"
+# type: HEAT_NET
+# validForResourceTypes: *allResourceTypes
+
+deploymentResourceInstanceArtifacts:
+ heatEnv:
+ displayName: "HEAT ENV"
+ type: HEAT_ENV
+ description: "Auto-generated HEAT Environment deployment artifact"
+ fileExtension: "env"
+
+#tosca artifacts placeholders
+toscaArtifacts:
+ assetToscaTemplate:
+ artifactName: -template.yml
+ displayName: Tosca Template
+ type: TOSCA_TEMPLATE
+ description: TOSCA representation of the asset
+ assetToscaCsar:
+ artifactName: -csar.csar
+ displayName: Tosca Model
+ type: TOSCA_CSAR
+ description: TOSCA definition package of the asset
+
+#Informational artifacts placeHolder
+excludeResourceCategory:
+ - Generic
+informationalResourceArtifacts:
+ features:
+ displayName: Features
+ type: OTHER
+ capacity:
+ displayName: Capacity
+ type: OTHER
+ vendorTestResult:
+ displayName: Vendor Test Result
+ type: OTHER
+ testScripts:
+ displayName: Test Scripts
+ type: OTHER
+ cloudQuestionnaire:
+ displayName: Cloud Questionnaire (completed)
+ type: OTHER
+ HEATTemplateFromVendor:
+ displayName: HEAT Template from Vendor
+ type: HEAT
+ resourceSecurityTemplate:
+ displayName: Resource Security Template
+ type: OTHER
+
+excludeServiceCategory:
+
+informationalServiceArtifacts:
+ serviceArtifactPlan:
+ displayName: Service Artifact Plan
+ type: OTHER
+ summaryOfImpactsToECOMPElements:
+ displayName: Summary of impacts to ECOMP elements,OSSs, BSSs
+ type: OTHER
+ controlLoopFunctions:
+ displayName: Control Loop Functions
+ type: OTHER
+ dimensioningInfo:
+ displayName: Dimensioning Info
+ type: OTHER
+ affinityRules:
+ displayName: Affinity Rules
+ type: OTHER
+ operationalPolicies:
+ displayName: Operational Policies
+ type: OTHER
+ serviceSpecificPolicies:
+ displayName: Service-specific Policies
+ type: OTHER
+ engineeringRules:
+ displayName: Engineering Rules (ERD)
+ type: OTHER
+ distributionInstructions:
+ displayName: Distribution Instructions
+ type: OTHER
+ certificationTestResults:
+ displayName: TD Certification Test Results
+ type: OTHER
+ deploymentVotingRecord:
+ displayName: Deployment Voting Record
+ type: OTHER
+ serviceQuestionnaire:
+ displayName: Service Questionnaire
+ type: OTHER
+ serviceSecurityTemplate:
+ displayName: Service Security Template
+ type: OTHER
+
+serviceApiArtifacts:
+ configuration:
+ displayName: Configuration
+ type: OTHER
+ instantiation:
+ displayName: Instantiation
+ type: OTHER
+ monitoring:
+ displayName: Monitoring
+ type: OTHER
+ reporting:
+ displayName: Reporting
+ type: OTHER
+ logging:
+ displayName: Logging
+ type: OTHER
+ testing:
+ displayName: Testing
+ type: OTHER
+
+
+additionalInformationMaxNumberOfKeys: 50
+
+systemMonitoring:
+ enabled: false
+ isProxy: false
+ probeIntervalInSeconds: 15
+
+defaultHeatArtifactTimeoutMinutes: 60
+
+serviceDeploymentArtifacts:
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ AAI_SERVICE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_INSTANCE_MODEL:
+ acceptedTypes:
+ - xml
+ OTHER:
+ acceptedTypes:
+
+resourceDeploymentArtifacts:
+ HEAT:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_VOL:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NESTED:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_ARTIFACT:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+ HEAT_NET:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VF_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VENDOR_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ APPC_CONFIG:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ AAI_VF_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ OTHER:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+
+resourceInstanceDeploymentArtifacts:
+ HEAT_ENV:
+ acceptedTypes:
+ - env
+ VF_MODULES_METADATA:
+ acceptedTypes:
+ - json
+
+resourceInformationalDeployedArtifacts:
+
+
+requirementsToFulfillBeforeCert:
+ CP:
+ - tosca.capabilities.network.Bindable
+
+capabilitiesToConsumeBeforeCert:
+
+unLoggedUrls:
+ - /sdc2/rest/healthCheck
+
+cleanComponentsConfiguration:
+ cleanIntervalInMinutes: 1440
+ componentsToClean:
+ - Resource
+ - Service
+
+artifactsIndex: resources
+
+cassandraConfig:
+ cassandraHosts: ['localhost']
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: false
+ username: koko
+ password: bobo
+ ssl: false
+ truststorePath : /path/path
+ truststorePassword : 123123
+ keySpaces:
+ - { name: sdcaudit, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdcartifact, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+
+switchoverDetector:
+ gBeFqdn:
+ gFeFqdn:
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/update_network?user=root"
+ publishNetworkBody: '{"note":"publish network"}'
+ groups:
+ beSet: { changePriorityUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"name":"AIO-BE.ecomp.idns.cip","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip","no_ad_redirection":false,"v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_be","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_be"],"failover_policy":["FAILALL"]},"comment":"AIO BE G-fqdn","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"comment":"AIO G-fqdn","name":"AIO-FE.ecomp.idns.cip","v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_fe","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_fe"],"failover_policy":["FAILALL"]},"no_ad_redirection":false,"intended_app_proto":"DNS","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip"}'}
+
+
+heatEnvArtifactHeader:
+ ""
+heatEnvArtifactFooter:
+ ""
+
+onboarding:
+ protocol: http
+ host: localhost
+ port: 8080
+ downloadCsarUri: "/onboarding-api/v1.0/vendor-software-products/packages"
+
+applicationL1Cache:
+ datatypes:
+ enabled: true
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: false
+ catalogL1Cache:
+ enabled: true
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 60
+ waitOnShutDownInMinutes: 30
+ numberOfCacheWorkers: 4
+
diff --git a/catalog-be/src/test/resources/config/catalog-be/distribution-engine-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/distribution-engine-configuration.yaml
new file mode 100644
index 0000000000..a64ea7f364
--- /dev/null
+++ b/catalog-be/src/test/resources/config/catalog-be/distribution-engine-configuration.yaml
@@ -0,0 +1,43 @@
+uebServers:
+ - uebsb91kcdc.it.att.com:3904
+ - uebsb92kcdc.it.att.com:3904
+# - uebsb93kcdc.it.att.com:3904
+
+uebPublicKey: 8F3MDAtMSBwwpSMy
+
+uebSecretKey: gzFmsTxSCtO5RQfAccM6PqqX
+
+distributionNotifTopicName: ASDC-DISTR-NOTIF-TOPIC
+distributionStatusTopicName: ASDC-DISTR-STATUS-TOPIC
+
+initRetryIntervalSec: 5
+initMaxIntervalSec: 60
+
+distribNotifServiceArtifactTypes:
+ info:
+ - MURANO-PKG
+
+distribNotifResourceArtifactTypes:
+ lifecycle:
+ - HEAT
+ - DG-XML
+
+environments:
+ - PROD
+
+distributionStatusTopic:
+ pollingIntervalSec: 60
+ fetchTimeSec: 15
+ consumerGroup: asdc
+ consumerId: asdc-id
+
+distributionNotificationTopic:
+ minThreadPoolSize: 0
+ maxThreadPoolSize: 10
+ maxWaitingAfterSendingSeconds: 5
+
+createTopic:
+ partitionCount: 1
+ replicationCount: 1
+
+startDistributionEngine: true \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/catalog-be/ecomp-error-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..9d7cd74a2b
--- /dev/null
+++ b/catalog-be/src/test/resources/config/catalog-be/ecomp-error-configuration.yaml
@@ -0,0 +1,383 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+## Range of BE codes - 3010-7999
+
+errors:
+
+ BeRestApiGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4000,
+ severity: ERROR,
+ description: "Unexpected error during BE REST API execution",
+ alarmSeverity: CRITICAL
+ }
+
+ BeHealthCheckError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3010,
+ severity: ERROR,
+ description: "Error during BE Health Check",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInitializationError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4019,
+ severity: ERROR,
+ description: "Catalog-BE was not initialized properly",
+ alarmSeverity: CRITICAL
+ }
+
+ BeResourceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3011,
+ severity: ERROR,
+ description: "Mandatory resource %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeServiceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3012,
+ severity: ERROR,
+ description: "Mandatory service %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedAddingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3013,
+ severity: ERROR,
+ description: "Failed to add resource instance of resource %s to service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeIncorrectServiceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3014,
+ severity: ERROR,
+ description: "Service %s is not valid",
+ alarmSeverity: MAJOR
+ }
+
+ BeRepositoryDeleteError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3015,
+ severity: ERROR,
+ description: "Failed to delete object %s from repository",
+ alarmSeverity: CRITICAL
+ }
+
+ BeRepositoryQueryError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3016,
+ severity: ERROR,
+ description: "Failed to fetch from repository %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeInvalidConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3017,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. Value configured is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4001,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3019,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebObjectNotFoundError: {
+ type: DATA_ERROR,
+ code: ASDC_4005,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Data not found: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3021,
+ severity: ERROR,
+ description: "Error occured in Distribution Engine. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebAuthenticationError: {
+ type: AUTHENTICATION_PROBLEM,
+ code: ASDC_4003,
+ severity: ERROR,
+ description: "Authentication problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebUnkownHostError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4002,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Cannot reach host %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineInvalidArtifactType: {
+ type: DATA_ERROR,
+ code: ASDC_4006,
+ severity: WARN,
+ description: "The artifact type %s does not appear in the list of valid artifacts %s",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4008,
+ severity: WARN,
+ description: "The type %s of %s is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidValueError: {
+ type: DATA_ERROR,
+ code: ASDC_3028,
+ severity: WARN,
+ description: "The value %s of %s from type %s is invalid",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeletingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3029,
+ severity: ERROR,
+ description: "Failed to delete resource instance %s from service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3030,
+ severity: FATAL,
+ description: "Configuration parameter %s is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeConfigurationInvalidListSizeError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3031,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. At least %s values shall be configured",
+ alarmSeverity: MAJOR
+ }
+
+ ErrorConfigFileFormat: {
+ type: CONFIG_ERROR,
+ code: ASDC_3032,
+ severity: ERROR,
+ description: "Error element not found in YAML name: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingArtifactInformationError: {
+ type: DATA_ERROR,
+ code: ASDC_4010,
+ severity: ERROR,
+ description: "Artifact uploaded has missing information. Missing %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4011,
+ severity: ERROR,
+ description: "Artifact %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactPayloadInvalid: {
+ type: DATA_ERROR,
+ code: ASDC_4012,
+ severity: ERROR,
+ description: "Payload of artifact uploaded is invalid (invalid MD5 or encryption)",
+ alarmSeverity: MAJOR
+ }
+
+ BeUserMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4009,
+ severity: ERROR,
+ description: "User %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactInformationInvalidError: {
+ type: DATA_ERROR,
+ code: ASDC_4013,
+ severity: ERROR,
+ description: "Input for artifact metadata is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeFailedAddingCapabilityTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4015,
+ severity: ERROR,
+ description: "Failed adding capability type",
+ alarmSeverity: CRITICAL
+ }
+
+ BeCapabilityTypeMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4016,
+ severity: ERROR,
+ description: "Capability Type %s not found",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInterfaceMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4020,
+ severity: ERROR,
+ description: "Interface %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeDaoSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4014,
+ severity: ERROR,
+ description: "Operation towards database failed",
+ alarmSeverity: CRITICAL
+ }
+
+ BeSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4017,
+ severity: ERROR,
+ description: "Unexpected error during operation",
+ alarmSeverity: CRITICAL
+ }
+
+ BeFailedLockObjectError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4007,
+ severity: WARN,
+ description: "Failed to lock object for update",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInvalidJsonInput: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4018,
+ severity: ERROR,
+ description: "Failed to convert json input to object",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4021,
+ severity: ERROR,
+ description: "Distribution %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeHealthCheckRecovery: {
+ type: RECOVERY,
+ code: ASDC_4022,
+ severity: INFO,
+ description: "BE Health Check Recovery",
+ alarmSeverity: INFORMATIONAL
+ }
+ BeFailedCreateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6000,
+ severity: ERROR,
+ description: "Failed to create node %s on graph. status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeFailedUpdateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6001,
+ severity: ERROR,
+ description: "Failed to update node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeleteNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6002,
+ severity: ERROR,
+ description: "Failed to delete node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedRetrieveNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6003,
+ severity: ERROR,
+ description: "Failed to retrieve node %s from graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeExecuteRollbackError: {
+ type: DATA_ERROR,
+ code: ASDC_6004,
+ severity: ERROR,
+ description: "Going to execute rollback on graph.",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindParentError: {
+ type: DATA_ERROR,
+ code: ASDC_6005,
+ severity: ERROR,
+ description: "Failed to find parent node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAllNodesError: {
+ type: DATA_ERROR,
+ code: ASDC_6006,
+ severity: ERROR,
+ description: "Failed to fetch all nodes with type %s of parent node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6007,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6008,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeComponentCleanerSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_6009,
+ severity: ERROR,
+ description: "Error occured in Component Cleaner Task. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml
new file mode 100644
index 0000000000..89b44d94d6
--- /dev/null
+++ b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml
@@ -0,0 +1,1583 @@
+# Errors
+errors:
+ OK: {
+ code: 200,
+ message: "OK"
+ }
+ CREATED: {
+ code: 201,
+ message: "OK"
+ }
+ NO_CONTENT: {
+ code: 204,
+ message: "No Content"
+ }
+#--------POL4050-----------------------------
+ NOT_ALLOWED: {
+ code: 405,
+ message: "Error: Method not allowed.",
+ messageId: "POL4050"
+ }
+#--------POL5000-----------------------------
+ GENERAL_ERROR: {
+ code: 500,
+ message: "Error: Internal Server Error. Please try again later.",
+ messageId: "POL5000"
+ }
+#---------POL5001------------------------------
+ MISSING_X_ECOMP_INSTANCE_ID: {
+ code: 400 ,
+ message: "Error: Missing 'X-ECOMP-InstanceID' HTTP header.",
+ messageId: "POL5001"
+ }
+#---------POL5002------------------------------
+ AUTH_REQUIRED: {
+ code: 401 ,
+ message: "Error: Authentication is required to use the API.",
+ messageId: "POL5002"
+ }
+#---------POL5003------------------------------
+ AUTH_FAILED: {
+ code: 403 ,
+ message: "Error: Not authorized to use the API.",
+ messageId: "POL5003"
+ }
+#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }
+#---------SVC4002-----------------------------
+ MISSING_INFORMATION: {
+ code: 403,
+ message: "Error: Missing information.",
+ messageId: "SVC4002"
+ }
+#---------SVC4003------------------------------
+# %1 - Users's USER_ID
+ USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: User '%1' was not found.",
+ messageId: "SVC4003"
+ }
+#---------SVC4004-----------------------------
+# %1 - Users's email address
+ INVALID_EMAIL_ADDRESS: {
+ code: 400,
+ message: "Error: Invalid email address '%1'.",
+ messageId: "SVC4004"
+ }
+#---------SVC4005------------------------------
+# %1 - role
+ INVALID_ROLE: {
+ code: 400,
+ message: "Error: Invalid role '%1'.",
+ messageId: "SVC4005"
+ }
+#---------SVC4006------------------------------
+# %1 - Users's USER_ID
+ USER_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: User with '%1' ID already exists.",
+ messageId: "SVC4006"
+ }
+#---------SVC4007------------------------------
+ DELETE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator can only be deleted by another administrator.",
+ messageId: "SVC4007"
+ }
+#---------SVC4008-----------------------------
+# %1 - Users's userId
+ INVALID_USER_ID: {
+ code: 400,
+ message: "Error: Invalid userId '%1'.",
+ messageId: "SVC4008"
+ }
+#---------SVC4049------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_CONTACT: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 contact id.",
+ messageId: "SVC4049"
+ }
+#---------SVC4050-----------------------------
+# %1 - Service/Resource/Additional parameter
+# %2 - service/resource/label name
+ COMPONENT_NAME_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: %1 with name '%2' already exists.",
+ messageId: "SVC4050"
+ }
+#---------SVC4051------------------------------
+# %1 - resource/service
+ COMPONENT_MISSING_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 category.",
+ messageId: "SVC4051"
+ }
+
+#---------SVC4052------------------------------
+ COMPONENT_MISSING_TAGS: {
+ code: 400,
+ message: "Error: Invalid Content. At least one tag has to be specified.",
+ messageId: "SVC4052"
+ }
+
+#---------SVC4053------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 description.",
+ messageId: "SVC4053"
+ }
+#---------SVC4054------------------------------
+# %1 - resource/service
+ COMPONENT_INVALID_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 category.",
+ messageId: "SVC4054"
+ }
+#---------SVC4055------------------------------
+ MISSING_VENDOR_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor name.",
+ messageId: "SVC4055"
+ }
+#---------SVC4056------------------------------
+ MISSING_VENDOR_RELEASE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor release.",
+ messageId: "SVC4056"
+ }
+
+#---------SVC4057------------------------------
+ MISSING_DERIVED_FROM_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing derived from template specification.",
+ messageId: "SVC4057"
+ }
+
+#---------SVC4058------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 icon.",
+ messageId: "SVC4058"
+ }
+#---------SVC4059------------------------------
+# %1 - service/resource
+ COMPONENT_INVALID_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 icon.",
+ messageId: "SVC4059"
+ }
+#---------SVC4060------------------------------
+ PARENT_RESOURCE_NOT_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Derived from resource template was not found.",
+ messageId: "SVC4060"
+ }
+#---------SVC4061------------------------------
+ MULTIPLE_PARENT_RESOURCE_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Multiple derived from resource template is not allowed.",
+ messageId: "SVC4061"
+ }
+
+#---------SVC4062------------------------------
+# %1 - service/resource
+ MISSING_COMPONENT_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 name.",
+ messageId: "SVC4062"
+ }
+#---------SVC4063------------------------------
+ #%1  -  resource/service name
+ RESOURCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource was not found.",
+ messageId: "SVC4063"
+ }
+
+#---------SVC4064------------------------------
+# %1 - Service/Resource
+ COMPONENT_INVALID_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description contains non-english characters.",
+ messageId: "SVC4064"
+ }
+#---------SVC4065------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name length
+ COMPONENT_DESCRIPTION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description exceeds limit of %2 characters.",
+ messageId: "SVC4065"
+ }
+#---------SVC4066------------------------------
+# %1 - max length
+ COMPONENT_TAGS_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Tags overall length exceeds limit of %1 characters.",
+ messageId: "SVC4066"
+ }
+#---------SVC4067------------------------------
+# %1 - max length
+ VENDOR_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor name exceeds limit of %1 characters.",
+ messageId: "SVC4067"
+ }
+#---------SVC4068------------------------------
+# %1 - max length
+ VENDOR_RELEASE_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor release exceeds limit of %1 characters.",
+ messageId: "SVC4068"
+ }
+
+#---------SVC4069------------------------------
+# %1 - Service/Resource/Product
+ COMPONENT_INVALID_CONTACT_ID: {
+ code: 400,
+ message: "Error: Invalid Content. %1 contact id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9",
+ messageId: "SVC4069"
+ }
+#---------SVC4070------------------------------
+# %1 - Service/Resource
+ INVALID_COMPONENT_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. %1 name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4070"
+ }
+
+#---------SVC4071------------------------------
+ INVALID_VENDOR_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4071"
+ }
+#---------SVC4072------------------------------
+ INVALID_VENDOR_RELEASE: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor release is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4072"
+ }
+#---------SVC4073------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name
+ COMPONENT_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 name exceeds limit of %2 characters.",
+ messageId: "SVC4073"
+ }
+#---------SVC4080------------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CHECKOUT_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for modification by %3 %4(%5).",
+ messageId: "SVC4080"
+ }
+#---------SVC4081-----------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CERT_IN_PROGRESS_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for certification by %3 %4(%5).",
+ messageId: "SVC4081"
+ }
+
+#-----------SVC4082---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_SENT_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is sent for certification by %3 %4(%5).",
+ messageId: "SVC4082"
+ }
+#-----------SVC4083---------------------------
+ COMPONENT_VERSION_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Version of this %1 was already promoted.",
+ messageId: "SVC4083"
+ }
+#-----------SVC4084---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CHECKED_IN: {
+ code: 409,
+ message: "Error: The current version of '%1' %2 was already checked-in by %3 %4(%5).",
+ messageId: "SVC4084"
+ }
+#-----------SVC4085---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_CHECKOUT_BY_ANOTHER_USER: {
+ code: 403,
+ message: "Error: %1 %2 has already been checked out by %3 %4(%5).",
+ messageId: "SVC4085"
+ }
+#-----------SVC4086---------------------------
+# %1  - resource/service name
+# %2  - resource/service
+ COMPONENT_IN_USE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is in use by another user.",
+ messageId: "SVC4086"
+ }
+#-----------SVC4087---------------------------
+# %1 - component name
+# %2 - resource/service/product
+ COMPONENT_HAS_NEWER_VERSION: {
+ code: 409,
+ message: "Error: Checking out of the requested version of the '%1' %2 is not allowed as a newer version exists.",
+ messageId: "SVC4087"
+ }
+#-----------SVC4088---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CERTIFIED: {
+ code: 403,
+ message: "Error: Requested %1 %2 has already been certified by %3 %4(%5).",
+ messageId: "SVC4088"
+ }
+#-----------SVC4089---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+ COMPONENT_NOT_READY_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification.",
+ messageId: "SVC4089"
+ }
+#-----------SVC4100---------------------------
+#%1 - property name
+ PROPERTY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' property was not found.",
+ messageId: "SVC4100"
+ }
+#-----------SVC4101---------------------------
+#%1 - property name
+ PROPERTY_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Property with '%1' name already exists.",
+ messageId: "SVC4101"
+ }
+
+#-----------SVC4102---------------------------
+# %1 - capability type name
+ CAPABILITY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Capability Type with name '%1' already exists.",
+ messageId: "SVC4102"
+ }
+#-----------SVC4114---------------------------
+ AUTH_FAILED_INVALIDE_HEADER: {
+ code: 400,
+ message: "Error: Invalid Authorization header.",
+ messageId: "SVC4114"
+ }
+#-----------SVC4115---------------------------
+# %1 - capability type name
+ MISSING_CAPABILITY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Capability Type '%1'.",
+ messageId: "SVC4115"
+ }
+ RESOURCE_INSTANCE_BAD_REQUEST: {
+ code: 400,
+ message: "Error: Invalid Content.",
+ messageId: "SVC4116"
+ }
+#-----------SVC4117---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_MATCH_NOT_FOUND: {
+ code: 404,
+ message: "Error: Match not found between resource instance '%1' and resource instance '%2' for requirement '%3'.",
+ messageId: "SVC4117"
+ }
+#-----------SVC4118---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Resource instances '%1' and '%2' are already associated with requirement '%3'.",
+ messageId: "SVC4118"
+ }
+#-----------SVC4119---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_RELATION_NOT_FOUND: {
+ code: 404,
+ message: "Error: No relation found between resource instances '%1' and '%2' for requirement '%3'.",
+ messageId: "SVC4119"
+ }
+#-----------SVC4120---------------------------
+# %1 - User's USER_ID
+ USER_INACTIVE: {
+ code: 404,
+ message: "Error: User %1 was not found.",
+ messageId: "SVC4120"
+ }
+#-----------SVC4121---------------------------
+# %1 - User's USER_ID
+ USER_HAS_ACTIVE_ELEMENTS: {
+ code: 403,
+ message: "Error: User with %1 ID can not be deleted since it has active elements(resources/services/artifacts).",
+ messageId: "SVC4121"
+ }
+#-----------SVC4122---------------------------
+# %1 - artifact type
+ ARTIFACT_TYPE_NOT_SUPPORTED: {
+ code: 400,
+ message: "Error: Invalid artifact type '%1'.",
+ messageId: "SVC4122"
+ }
+#-----------SVC4123---------------------------
+ ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Artifact logical name cannot be changed.",
+ messageId: "SVC4123"
+ }
+#-----------SVC4124---------------------------
+ MISSING_ARTIFACT_TYPE: {
+ code: 400,
+ message: "Error: Missing artifact type.",
+ messageId: "SVC4124"
+ }
+#-----------SVC4125---------------------------
+# %1-artifact name
+ ARTIFACT_EXIST: {
+ code: 400,
+ message: "Error: Artifact '%1' already exists.",
+ messageId: "SVC4125"
+ }
+#---------SVC4126------------------------------
+# %1 - resource/service/product/...
+# %2 - field (tag, vendor name...)
+ INVALID_FIELD_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 format.",
+ messageId: "SVC4126"
+ }
+#-----------SVC4127---------------------------
+ ARTIFACT_INVALID_MD5: {
+ code: 400,
+ message: "Error: Invalid artifact checksum.",
+ messageId: "SVC4127"
+ }
+#-----------SVC4128---------------------------
+ MISSING_ARTIFACT_NAME: {
+ code: 400,
+ message: "Error: Invalid content. Missing artifact name.",
+ messageId: "SVC4128"
+ }
+#-----------SVC4129---------------------------
+ MISSING_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing PROJECT_CODE number.",
+ messageId: "SVC4129"
+ }
+#-----------SVC4130---------------------------
+ INVALID_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. PROJECT_CODE number must be numeric from 5 up to 10 digits.",
+ messageId: "SVC4130"
+ }
+#-----------SVC4131---------------------------
+# %1-resource/service
+# %2-srtifact/artifacts
+# %3-semicolomn separated list of artifact
+ COMPONENT_MISSING_MANDATORY_ARTIFACTS: {
+ code: 403,
+ message: "Error: Missing mandatory informational %1 %2: [%3].",
+ messageId: "SVC4131"
+ }
+#-----------SVC4132---------------------------
+# %1 - lifecycle type name
+ LIFECYCLE_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Lifecycle Type with name '%1' already exists.",
+ messageId: "SVC4132"
+ }
+#-----------SVC4133---------------------------
+# %1 - service version
+# %2 - service name
+ SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION: {
+ code: 403,
+ message: "Error: Version %1 of '%2' service is not available for distribution.",
+ messageId: "SVC4133"
+ }
+#-----------SVC4134---------------------------
+ MISSING_LIFECYCLE_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing interface life-cycle type.",
+ messageId: "SVC4134"
+ }
+#---------SVC4135------------------------------
+ SERVICE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service category cannot be changed once the service is certified.",
+ messageId: "SVC4135"
+ }
+#---------SVC4136------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE: {
+ code: 500,
+ message: "Error: Requested distribution environment '%1' is not available.",
+ messageId: "SVC4136"
+ }
+#---------SVC4137------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_FOUND: {
+ code: 400,
+ message: "Error: Requested distribution environment '%1' was not found.",
+ messageId: "SVC4137"
+ }
+#---------SVC4138------------------------------
+ DISTRIBUTION_ENVIRONMENT_INVALID: {
+ code: 400,
+ message: "Error: Invalid distribution environment.",
+ messageId: "SVC4138"
+ }
+#---------SVC4139------------------------------
+# %1 - service name
+ DISTRIBUTION_ARTIFACT_NOT_FOUND: {
+ code: 409,
+ message: "Error: Service '%1' cannot be distributed due to missing deployment artifacts.",
+ messageId: "SVC4139"
+ }
+#---------SVC4200------------------------------
+# %1 - Service/Resource
+# %2 - max icon name length
+ COMPONENT_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 icon name exceeds limit of %2 characters.",
+ messageId: "SVC4200"
+ }
+#---------SVC4300------------------------------
+ RESTRICTED_ACCESS: {
+ code: 403,
+ message: "Error: Restricted access.",
+ messageId: "SVC4300"
+ }
+#---------SVC4301------------------------------
+ RESTRICTED_OPERATION: {
+ code: 409,
+ message: "Error: Restricted operation.",
+ messageId: "SVC4301"
+ }
+#---------SVC4500------------------------------
+ MISSING_BODY: {
+ code: 400 ,
+ message: "Error: Missing request body.",
+ messageId: "SVC4500"
+ }
+#---------SVC4501------------------------------
+ MISSING_PUBLIC_KEY: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'apiPublicKey'." ,
+ messageId: "SVC4501"
+ }
+#---------SVC4502------------------------------
+ DISTRIBUTION_ENV_DOES_NOT_EXIST: {
+ code: 400 ,
+ message: "Error: Invalid Body : Missing mandatory parameter 'distrEnvName'." ,
+ messageId: "SVC4502"
+ }
+#-----------SVC4503---------------------------
+# %1 - service name
+ SERVICE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' service was not found.",
+ messageId: "SVC4503"
+ }
+
+#---------SVC4504------------------------------
+# %1 - Service/Resource
+# %2 - service/resource version
+ COMPONENT_VERSION_NOT_FOUND: {
+ code: 404,
+ message: "Error: %1 version %2 was not found.",
+ messageId: "SVC4504"
+ }
+#-----------SVC4505---------------------------
+ #%1-artifact name
+
+ ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Artifact '%1' was not found.",
+ messageId: "SVC4505"
+ }
+#---------SVC4506------------------------------
+ MISSING_ENV_NAME: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'distrEnvName'.",
+ messageId: "SVC4506"
+ }
+#---------SVC4507------------------------------
+ COMPONENT_INVALID_TAGS_NO_COMP_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. One of the tags should be the component name.",
+ messageId: "SVC4507"
+ }
+
+#---------SVC4508------------------------------
+ SERVICE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service name cannot be changed once the service is certified.",
+ messageId: "SVC4508"
+ }
+
+#---------SVC4509------------------------------
+ SERVICE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the service is certified.",
+ messageId: "SVC4509"
+ }
+#---------SVC4510------------------------------
+# %1 - icon name max length
+ SERVICE_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Icon name exceeds limit of %1 characters.",
+ messageId: "SVC4510"
+ }
+#---------SVC4511------------------------------
+ DISTRIBUTION_REQUESTED_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested distribution was not found.",
+ messageId: "SVC4511"
+ }
+#---------SVC4512------------------------------
+# %1 - Distribution ID
+ DISTRIBUTION_REQUESTED_FAILED: {
+ code: 403,
+ message: "Error: Requested distribution '%1' failed.",
+ messageId: "SVC4512"
+ }
+#---------SVC4513------------------------------
+ RESOURCE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource category cannot be changed once the resource is certified.",
+ messageId: "SVC4513"
+ }
+#---------SVC4514------------------------------
+ RESOURCE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource name cannot be changed once the resource is certified.",
+ messageId: "SVC4514"
+ }
+#---------SVC4515------------------------------
+ RESOURCE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the resource is certified.",
+ messageId: "SVC4515"
+ }
+#---------SVC4516------------------------------
+ RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Vendor name cannot be changed once the resource is certified.",
+ messageId: "SVC4516"
+ }
+#---------SVC4517------------------------------
+ RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Derived from resource template cannot be changed once the resource is certified.",
+ messageId: "SVC4517"
+ }
+#---------SVC4518------------------------------
+# %1 - max length
+ COMPONENT_SINGLE_TAG_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Single tag exceeds limit of %1 characters.",
+ messageId: "SVC4518"
+ }
+#---------SVC4519------------------------------
+ INVALID_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: mismatch in data-type occurred for property %1. data type is %2 and default value found is %3.",
+ messageId: "SVC4519"
+ }
+#---------SVC4520------------------------------
+# %1 - service or resource
+ ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED: {
+ code: 409,
+ message: "Error: Maximal number of additional %1 parameters was reached.",
+ messageId: "SVC4520"
+ }
+#---------SVC4521------------------------------
+ ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED: {
+ code: 400,
+ message: "Error: Invalid Content. The Additional information label and value cannot be empty.",
+ messageId: "SVC4521"
+ }
+#---------SVC4522------------------------------
+# %1 - label/value
+# %2 - Maximal length of %1
+ ADDITIONAL_INFORMATION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Additional information %1 exceeds limit of %2 characters.",
+ messageId: "SVC4522"
+ }
+#---------SVC4523------------------------------
+ ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information label is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4523"
+ }
+#---------SVC4524------------------------------
+ ADDITIONAL_INFORMATION_NOT_FOUND: {
+ code: 409,
+ message: "Error: Requested additional information was not found.",
+ messageId: "SVC4524"
+ }
+#---------SVC4525------------------------------
+ ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information contains non-english characters.',
+ messageId: "SVC4525"
+ }
+#---------SVC4526------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found.",
+ messageId: "SVC4526"
+ }
+#---------SVC4527------------------------------
+ ASDC_VERSION_NOT_FOUND: {
+ code: 500,
+ message: 'Error: ASDC version cannot be displayed.',
+ messageId: "SVC4527"
+ }
+#---------SVC4528------------------------------
+# %1-artifact url/artifact label/artifact description/VNF Service Indicator
+ MISSING_DATA: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1.",
+ messageId: "SVC4528"
+ }
+#---------SVC4529------------------------------
+# %1-artifact url/artifact label/artifact description/artifact name
+# %2 - Maximal length of %1
+ EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 exceeds limit of %2 characters.",
+ messageId: "SVC4529"
+ }
+#---------SVC4530------------------------------
+ ARTIFACT_INVALID_TIMEOUT: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact Timeout should be set to valid positive non-zero number of minutes.",
+ messageId: "SVC4530"
+ }
+#---------SVC4531------------------------------
+ SERVICE_IS_VNF_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: VNF Indicator cannot be updated for certified service.",
+ messageId: "SVC4531"
+ }
+ #---------SVC4532------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found on the service '%2.",
+ messageId: "SVC4532"
+ }
+ #---------SVC4533------------------------------
+ # %1 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"/"YANG_XML"
+ WRONG_ARTIFACT_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for %1 artifact type.",
+ messageId: "SVC4533"
+ }
+
+#---------SVC4534------------------------------
+# %1 - "HEAT"/"HEAT_ENV"
+ INVALID_YAML: {
+ code: 400,
+ message: "Error: Uploaded YAML file for %1 artifact is invalid.",
+ messageId: "SVC4534"
+ }
+
+#---------SVC4535------------------------------
+# %1 - "HEAT"
+ INVALID_DEPLOYMENT_ARTIFACT_HEAT: {
+ code: 400,
+ message: "Error: Invalid %1 artifact.",
+ messageId: "SVC4535"
+ }
+#---------SVC4536------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"
+# %4 - "HEAT"/"HEAT_ENV"/"MURANO_PKG
+ DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact of %3 type .Please delete or update an existing %4 artifact.",
+ messageId: "SVC4536"
+ }
+
+#---------SVC4537------------------------------
+ MISSING_HEAT: {
+ code: 400,
+ message: "Error: Missing HEAT artifact. HEAT_ENV artifact cannot be uploaded without corresponding HEAT template.",
+ messageId: "SVC4537"
+ }
+#---------SVC4538------------------------------
+ MISMATCH_HEAT_VS_HEAT_ENV: {
+ code: 400,
+ message: "Error: Invalid artifact content. Parameter's set in HEAT_ENV '%1' artifact doesn't match the parameters in HEAT '%2' artifact.",
+ messageId: "SVC4538"
+ }
+#---------SVC4539------------------------------
+ INVALID_RESOURCE_PAYLOAD: {
+ code: 400,
+ message: "Error: Invalid resource payload.",
+ messageId: "SVC4539"
+ }
+#---------SVC4540------------------------------
+ INVALID_TOSCA_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for TOSCA template.",
+ messageId: "SVC4540"
+ }
+#---------SVC4541------------------------------
+ INVALID_YAML_FILE: {
+ code: 400,
+ message: "Error: Invalid YAML file.",
+ messageId: "SVC4541"
+ }
+#---------SVC4542------------------------------
+ INVALID_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid TOSCA template.",
+ messageId: "SVC4542"
+ }
+#---------SVC4543------------------------------
+ NOT_RESOURCE_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Imported Service TOSCA template.",
+ messageId: "SVC4543"
+ }
+#---------SVC4544------------------------------
+ NOT_SINGLE_RESOURCE: {
+ code: 400,
+ message: "Error: Imported TOSCA template should contain one resource definition.",
+ messageId: "SVC4544"
+ }
+#---------SVC4545------------------------------
+ INVALID_RESOURCE_NAMESPACE: {
+ code: 400,
+ message: "Error: Invalid resource namespace.",
+ messageId: "SVC4545"
+ }
+#---------SVC4546------------------------------
+ RESOURCE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: Imported resource already exists in ASDC Catalog.",
+ messageId: "SVC4546"
+ }
+#---------SVC4549------------------------------
+ INVALID_RESOURCE_CHECKSUM: {
+ code: 400,
+ message: "Error: Invalid resource checksum.",
+ messageId: "SVC4549"
+ }
+#---------SVC4550------------------------------
+ #%1  -  Consumer salt
+ INVALID_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 length.",
+ messageId: "SVC4550"
+ }
+ #---------SVC4551------------------------------
+ #%1  -  ECOMP User name
+ ECOMP_USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: ECOMP User '%1' was not found.",
+ messageId: "SVC4551"
+ }
+#---------SVC4552------------------------------
+ CONSUMER_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: ECOMP User already exists.",
+ messageId: "SVC4552"
+ }
+#---------SVC4553-----------------------------
+ #%1  -  Consumer name / Consumer password/ Consumer salt
+ INVALID_CONTENT_PARAM: {
+ code: 400,
+ message: "Error: %1 is invalid.",
+ messageId: "SVC4553"
+ }
+ #---------SVC4554------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested artifact doesn't belong to specified %1.",
+ messageId: "SVC4554"
+ }
+#---------SVC4554------------------------------
+# %1 - "Service name"
+ SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND: {
+ code: 403,
+ message: "Error: Requested '%1' service is not ready for certification. Service has to have at least one deployment artifact.",
+ messageId: "SVC4554"
+ }
+#---------SVC4555------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name length.",
+ messageId: "SVC4555"
+ }
+#---------SVC4556------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name format.",
+ messageId: "SVC4556"
+ }
+#---------SVC4557------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category name"
+ COMPONENT_CATEGORY_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: %1 category name '%2' already exists.",
+ messageId: "SVC4557"
+ }
+#---------SVC4558------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ VALIDATED_RESOURCE_NOT_FOUND: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource.",
+ messageId: "SVC4558"
+ }
+#---------SVC4559------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_ALREADY_VALIDATED_RESOURCE: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use already available validated resource version.",
+ messageId: "SVC4559"
+ }
+#---------SVC4560------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_LIST_VALIDATED_RESOURCES: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use one of available validated resource versions.",
+ messageId: "SVC4560"
+ }
+#---------SVC4561------------------------------
+# %1 - "resource"/"product"
+# %2 - "category"
+# %2 - "category name"
+ COMPONENT_CATEGORY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested %1 %2 '%3' was not found.",
+ messageId: "SVC4561"
+ }
+#---------SVC4562------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %2 - "category name"
+ COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY: {
+ code: 409,
+ message: "Error: %1 sub-category '%2' already exists under '%3' category.",
+ messageId: "SVC4562"
+ }
+#---------SVC4563------------------------------
+# %1 - "Product"
+# %2 - "grouping name"
+# %2 - "sub-category name"
+ COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY: {
+ code: 409,
+ message: "Error: %1 grouping '%2' already exists under '%3' sub-category.",
+ messageId: "SVC4563"
+ }
+#---------SVC4564------------------------------
+# %1 - product name
+ PRODUCT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' product was not found.",
+ messageId: "SVC4564"
+ }
+#---------SVC4565------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+# %3 - parameter name
+ INVALID_HEAT_PARAMETER_VALUE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Invalid %2 value set for '%3' parameter.",
+ messageId: "SVC4565"
+ }
+#---------SVC4566------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+ INVALID_HEAT_PARAMETER_TYPE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Unsupported '%2' parameter type.",
+ messageId: "SVC4566"
+ }
+#---------SVC4567------------------------------
+# %1 - "YANG_XML"
+ INVALID_XML: {
+ code: 400,
+ message: "Error: Uploaded XML file for %1 artifact is invalid.",
+ messageId: "SVC4567"
+ }
+#---------SVC4567------------------------------
+# %1 - "User Name and userId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: User cannot be deleted. User '%1' has %2 projects.",
+ messageId: "SVC4567"
+ }
+#---------SVC4568------------------------------
+# %1 - "User Name and userId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: Role cannot be changed. User '%1' has %2 projects.",
+ messageId: "SVC4568"
+ }
+#---------SVC4570------------------------------
+ UPDATE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator is not allowed to change his/her role.",
+ messageId: "SVC4570"
+ }
+#---------SVC4571------------------------------
+ SERVICE_CANNOT_CONTAIN_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Sub category cannot be defined for service",
+ messageId: "SVC4571"
+ }
+#---------SVC4572------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_TOO_MUCH_CATEGORIES: {
+ code: 400,
+ message: "Error: %1 must have only 1 category",
+ messageId: "SVC4572"
+ }
+#---------SVC4574------------------------------
+ RESOURCE_TOO_MUCH_SUBCATEGORIES: {
+ code: 400,
+ message: "Error: Resource must have only 1 sub category",
+ messageId: "SVC4574"
+ }
+#---------SVC4575------------------------------
+ COMPONENT_MISSING_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Missing sub category",
+ messageId: "SVC4575"
+ }
+ #---------SVC4576------------------------------
+# %1 - "component type"
+ UNSUPPORTED_ERROR: {
+ code: 400,
+ message: "Error : Requested component type %1 is unsupported.",
+ messageId: "SVC4576"
+ }
+ #---------SVC4577------------------------------
+# %1 - "resource type"
+ RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES: {
+ code: 409,
+ message: "Error : Resource of type %1 cannot contain resource instances.",
+ messageId: "SVC4577"
+ }
+#---------SVC4578------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "artifact name"
+ DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact named '%3'.",
+ messageId: "SVC4578"
+ }
+#---------SVC4579------------------------------
+# %1 - "Category"/"Sub-Category"/"Group"
+# %2 - category/sub-category/grouping name.
+ INVALID_GROUP_ASSOCIATION: {
+ code: 400,
+ message: "Error: Invalid group association. %1 '%2' was not found.",
+ messageId: "SVC4579"
+ }
+#---------SVC4580------------------------------
+ EMPTY_PRODUCT_CONTACTS_LIST: {
+ code: 400,
+ message: "Error: Invalid content. At least one Product Contact has to be specified.",
+ messageId: "SVC4580"
+ }
+#---------SVC4581------------------------------
+# %1 - userId
+ INVALID_PRODUCT_CONTACT: {
+ code: 400,
+ message: "Error: Invalid content. User '%1' cannot be set as Product Contact.",
+ messageId: "SVC4581"
+ }
+#---------SVC4582------------------------------
+# %1 - Product
+# %2 - "abbreviated"/"full"
+ MISSING_ONE_OF_COMPONENT_NAMES: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1 %2 name.",
+ messageId: "SVC4582"
+ }
+#---------SVC4583------------------------------
+# %1 - "Icon"
+# %2 - "resource"/"service"/"product"
+ COMPONENT_PARAMETER_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: %1 cannot be changed once the %2 is certified.",
+ messageId: "SVC4583"
+ }
+#---------SVC4584------------------------------
+# %1 - service/VF name
+# %2 - "service" /"VF"
+# %3 - resource instance origin type
+# %4 - resource instance name
+# %5 - requirement/capability
+# %6 - requirement/capability name
+# %7 - "fulfilled" (for req)/"consumed (for cap)"
+ REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification. %3 '%4' has to have %5 '%6' %7.",
+ messageId: "SVC4584"
+ }
+#---------SVC4585------------------------------
+ INVALID_OCCURRENCES: {
+ code: 400,
+ message: "Error: Invalid occurrences format.",
+ messageId: "SVC4585"
+ }
+#---------SVC4586------------------------------
+#---------SVC4586------------------------------
+ INVALID_SERVICE_API_URL: {
+ code: 400,
+ message: 'Error: Invalid Service API URL. Please check whether your URL has a valid domain extension and does not contain the following characters - #?&@%+;,=$<>~^`\[]{}|"*!',
+ messageId: "SVC4586"
+ }
+#---------SVC4587------------------------------
+# %1 - Data type name
+ DATA_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Data type %1 already exists.',
+ messageId: "SVC4587"
+ }
+#---------SVC4588------------------------------
+# %1 - Data type name
+ DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM: {
+ code: 400,
+ message: 'Error: Invalid Data type %1. Data type must have either a valid derived from declaration or at least one valid property',
+ messageId: "SVC4588"
+ }
+#---------SVC4589------------------------------
+# %1 - Data type name
+ DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Data type %1. 'properties' parameter cannot be empty if provided.",
+ messageId: "SVC4589"
+ }
+#---------SVC4590------------------------------
+# %1 - Property type name
+# %2 - Property name
+ INVALID_PROPERTY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Property type %1 in property %2.",
+ messageId: "SVC4590"
+ }
+#---------SVC4591------------------------------
+# %1 - Property inner type
+# %2 - Property name
+ INVALID_PROPERTY_INNER_TYPE: {
+ code: 400,
+ message: "Error: Invalid property inner type %1, in property %2",
+ messageId: "SVC4591"
+ }
+#---------SVC4592------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+ COMPONENT_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found.",
+ messageId: "SVC4592"
+ }
+#---------SVC4593------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+# %3 - "resource/"service"/"product"
+# %4 - container name
+ COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found on the %3 '%4'.",
+ messageId: "SVC4593"
+ }
+#---------SVC4594------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+ IMPORT_DUPLICATE_REQ_CAP_NAME: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains more than one %1 named '%2'.",
+ messageId: "SVC4594"
+ }
+#---------SVC4595------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+#%3 - parent containing the requirement
+ IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains %1 '%2' that is already defined by derived template %3.",
+ messageId: "SVC4595"
+ }
+#---------SVC4596------------------------------
+# %1 - Data type name
+ DATA_TYPE_DERIVED_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The ancestor data type %1 cannot be found in the system.",
+ messageId: "SVC4596"
+ }
+#---------SVC4597------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties named %2 which are already defined in one of its ancestors.",
+ messageId: "SVC4597"
+ }
+#---------SVC4598------------------------------
+# %1 - Data type name
+ DATA_TYPE_DUPLICATE_PROPERTY: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains duplicate property.",
+ messageId: "SVC4598"
+ }
+#---------SVC4599------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties %2 which their type is this data type.",
+ messageId: "SVC4599"
+ }
+#---------SVC4600------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_HAVE_PROPERTIES: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 cannot have properties since it is of type scalar",
+ messageId: "SVC4600"
+ }
+#---------SVC4601------------------------------
+ NOT_TOPOLOGY_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file %1 cannot be modeled to VF as it does not contain 'topology_template.",
+ messageId: "SVC4601"
+ }
+#---------SVC4602--------------------------------
+# %1 - yaml file name
+# %2 - node_template label
+# %3 - node_template type
+ INVALID_NODE_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file '%1' contains node_template '%2' of type '%3' that does not represent existing VFC/CP/VL",
+ messageId: "SVC4602"
+ }
+#---------SVC4603------------------------------
+# %1 - component type
+# %2 - component name
+# %3 - state
+ ILLEGAL_COMPONENT_STATE: {
+ code: 403,
+ message: "Error: Component instance of %1 can not be created because the component '%2' is in an illegal state %3.",
+ messageId: "SVC4603"
+ }
+#---------SVC4604------------------------------
+# %1 - csar file name
+ CSAR_INVALID: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. 'TOSCA-Metadata/Tosca.meta' file must be provided.",
+ messageId: "SVC4604"
+ }
+#---------SVC4605------------------------------
+# %1 - csar file name
+ CSAR_INVALID_FORMAT: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. Invalid 'TOSCA-Metadata/Tosca.meta' file format.",
+ messageId: "SVC4605"
+ }
+#---------SVC4606------------------------------
+# %1 - property name
+# %2 - property type
+# %3 - property innerType
+# %4 - default value is
+ INVALID_COMPLEX_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: Invalid default value of property %1. Data type is %2 with inner type %3 and default value found is %4.",
+ messageId: "SVC4606"
+ }
+#---------SVC4607------------------------------
+# %1 - csar file name
+ CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is not found.",
+ messageId: "SVC4607"
+ }
+#---------SVC4608------------------------------
+# %1 - artifact name
+# %2 - component type
+# %3 - actual component type
+ MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE: {
+ code: 400,
+ message: "Error: Artifact %1 is only compatible with component of type %2, but component type is %3.",
+ messageId: "SVC4608"
+ }
+
+#---------SVC4609------------------------------
+# %1 - "INVALID_JSON"
+ INVALID_JSON: {
+ code: 400,
+ message: "Error: Uploaded JSON file for %1 artifact is invalid.",
+ messageId: "SVC4609"
+ }
+#---------SVC4610------------------------------
+# %1 - csar file name
+# %2 - missing file name
+ YAML_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error - TOSCA CSAR %1 is invalid. TOSCA-Metadata/Tosca.meta refers to file %2 that is not provided.",
+ messageId: "SVC4610"
+ }
+#---------SVC4611------------------------------
+# %1 - group name
+ GROUP_MEMBER_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Group %1 member list was provided but does not have values",
+ messageId: "SVC4611"
+ }
+#---------SVC4612------------------------------
+# %1 - group name
+ GROUP_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Group type %1 already exists.',
+ messageId: "SVC4612"
+ }
+#---------SVC4613------------------------------
+# %1 - group name
+# %2 - VF name(component name)
+# %3 - actual component type [VF]
+ GROUP_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Group with name '%1' already exists in %2 %3.",
+ messageId: "SVC4613"
+ }
+#---------SVC4614------------------------------
+# %1 - group type
+ GROUP_TYPE_IS_INVALID: {
+ code: 400,
+ message: "Error: Invalid content. Group type %1 does not exist",
+ messageId: "SVC4614"
+ }
+#---------SVC4615------------------------------
+# %1 - group name
+ GROUP_MISSING_GROUP_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Group Type for group '%1'",
+ messageId: "SVC4615"
+ }
+#---------SVC4616------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - VF name
+# %4 - component type [VF ]
+ GROUP_INVALID_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of %3 %4.",
+ messageId: "SVC4616"
+ }
+#---------SVC4617------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - group type
+ GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of allowed members of group type %3.",
+ messageId: "SVC4617"
+ }
+#---------SVC4618------------------------------
+# %1 - missing file name
+# %2 - csar file name
+ ARTIFACT_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 is defined in CSAR %2 manifest but is not provided",
+ messageId: "SVC4618"
+ }
+#---------SVC4619------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - existing artifact type
+ ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 already exists in type %3.",
+ messageId: "SVC4619"
+ }
+#---------SVC4620------------------------------
+ FAILED_RETRIVE_ARTIFACTS_TYPES: {
+ code: 400,
+ message: "Error: Failed to retrieve list of suported artifact types.",
+ messageId: "SVC4620"
+ }
+#---------SVC4621------------------------------
+# %1 - artifact name
+# %2 - master
+ ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 already exists in master %2 .",
+ messageId: "SVC4621"
+ }
+#---------SVC4622------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - master name
+# %4 - master type
+ ARTIFACT_NOT_VALID_IN_MASTER: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 can not be exists under master %3 in type %4.",
+ messageId: "SVC4622"
+ }
+#---------SVC4623------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - env name
+# %4 - existing env
+ ARTIFACT_NOT_VALID_ENV: {
+ code: 400,
+ message: "Error: Artifact %1 in type %2 with env %3 already exists with another env %4",
+ messageId: "SVC4623"
+ }
+#---------SVC4624------------------------------
+# %1 - groups names
+# %2 - VF name
+# %3 - component type [VF ]
+ GROUP_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The groups '%1' cannot be found under %2 %3.",
+ messageId: "SVC4624"
+ }
+#---------SVC4625------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_ASSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already associated to group '%1'.",
+ messageId: "SVC4625"
+ }
+#---------SVC4626------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_DISSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already dissociated from group '%1'.",
+ messageId: "SVC4626"
+ }
+#---------SVC4627------------------------------
+# %1 - property name
+# %2 - group name
+# %3 - group type name
+ GROUP_PROPERTY_NOT_FOUND: {
+ code: 400,
+ message: "Error: property %1 listed in group %2 is not exist in group type %3.",
+ messageId: "SVC4627"
+ }
+#---------SVC4628------------------------------
+# %1 - csarUUID
+# %2 - VF name
+ VSP_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: The VSP with UUID %1 was already imported for VF %2. Please select another or update the existing VF.",
+ messageId: "SVC4628"
+ }
+#---------SVC4629------------------------------
+# %1 - VF name
+ MISSING_CSAR_UUID: {
+ code: 400,
+ message: "Error: The Csar UUID or payload name is missing for VF %1.",
+ messageId: "SVC4629"
+ }
+#---------SVC4630------------------------------
+# %1 - VF name
+# %2 - new csarUUID
+# %3 - old csarUUID
+ RESOURCE_LINKED_TO_DIFFERENT_VSP: {
+ code: 400,
+ message: "Error: Resource %1 cannot be updated using CsarUUID %2 since the resource is linked to a different VSP with csarUUID %3.",
+ messageId: "SVC4630"
+ }
+#---------SVC4631------------------------------
+# %1 - policy name
+ POLICY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Policy type %1 already exists.",
+ messageId: "SVC4631"
+ }
+#---------SVC4632------------------------------
+# %1 - target name
+# %2 - policy type name
+ TARGETS_NON_VALID: {
+ code: 400,
+ message: "Error: target %1 listed in policy type %2 is not a group or resource.",
+ messageId: "SVC4632"
+ }
+#---------SVC4633------------------------------
+# %1 - policy name
+ TARGETS_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Policy %1 target list was provided but does not have values",
+ messageId: "SVC4633"
+ }
+#---------SVC4634------------------------------
+ DATA_TYPE_CANNOT_BE_EMPTY: {
+ code: 500,
+ message: "Error: Data types are empty. Please import the data types.",
+ messageId: "SVC4634"
+ }
+#---------SVC4635------------------------------
+# %1 - csar uuid
+ RESOURCE_FROM_CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: resource from csar uuid %1 not found",
+ messageId: "SVC4635"
+ }
+#---------SVC4636------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST: {
+ code: 400,
+ message: 'Error: Data type %1 cannot be upgraded. The new data type does not contain old properties or the type of one of the properties has been changed.',
+ messageId: "SVC4636"
+ } \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/catalog-be/neo4j-errors-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/neo4j-errors-configuration.yaml
new file mode 100644
index 0000000000..7a0d6dbfd4
--- /dev/null
+++ b/catalog-be/src/test/resources/config/catalog-be/neo4j-errors-configuration.yaml
@@ -0,0 +1,60 @@
+# Errors
+errors:
+ Neo_ClientError_General_ReadOnly: "This is a read only database, writing or modifying the database is not allowed."
+ Neo_ClientError_LegacyIndex_NoSuchIndex: "The request (directly or indirectly) referred to a index that does not exist."
+ Neo_ClientError_Request_Invalid: "The client provided an invalid Request."
+ Neo_ClientError_Request_InvalidFormat: "The client provided a request that was missing required fields, or had values that are not allowed."
+ Neo_ClientError_Schema_ConstraintAlreadyExists: "Unable to perform operation because it would clash with a pre-existing constraint."
+ Neo_ClientError_Schema_ConstraintVerificationFailure: "Unable to create constraint because data that exists in the database violates it."
+ Neo_ClientError_Schema_ConstraintViolation: "A constraint imposed by the database was violated."
+ Neo_ClientError_Schema_IllegalTokenName: "A token name, such as a label, relationship type or property key, used is not valid. Tokens cannot be empty strings and cannot be null."
+ Neo_ClientError_Schema_IndexAlreadyExists: "Unable to perform operation because it would clash with a pre-existing index."
+ Neo_ClientError_Schema_IndexBelongsToConstraint: "A requested operation can not be performed on the specified index because the index is part of a constraint. If you want to drop the index, for instance, you must drop the constraint."
+ Neo_ClientError_Schema_IndexLimitReached: "The maximum number of index entries supported has been reached, no more entities can be indexed."
+ Neo_ClientError_Schema_LabelLimitReached: "The maximum number of labels supported has been reached, no more labels can be created."
+ Neo_ClientError_Schema_NoSuchConstraint: "The request (directly or indirectly) referred to a constraint that does not exist."
+ Neo_ClientError_Schema_NoSuchIndex: "The request (directly or indirectly) referred to an index that does not exist."
+ Neo_ClientError_Security_AuthenticationFailed: "The client provided an incorrect username and/or password."
+ Neo_ClientError_Security_AuthenticationRateLimit: "The client has provided incorrect authentication details too many times in a row."
+ Neo_ClientError_Security_AuthorizationFailed: "The client does not have privileges to perform the operation requested."
+ Neo_ClientError_Statement_ArithmeticError: "Invalid use of arithmetic, such as dividing by zero."
+ Neo_ClientError_Statement_ConstraintViolation: "A constraint imposed by the statement is violated by the data in the database."
+ Neo_ClientError_Statement_EntityNotFound: "The statement is directly referring to an entity that does not exist."
+ Neo_ClientError_Statement_InvalidArguments: "The statement is attempting to perform operations using invalid arguments"
+ Neo_ClientError_Statement_InvalidSemantics: "The statement is syntactically valid, but expresses something that the database cannot do."
+ Neo_ClientError_Statement_InvalidSyntax: "The statement contains invalid or unsupported syntax."
+ Neo_ClientError_Statement_InvalidType: "The statement is attempting to perform operations on values with types that are not supported by the operation."
+ Neo_ClientError_Statement_NoSuchLabel: "The statement is referring to a label that does not exist."
+ Neo_ClientError_Statement_NoSuchProperty: "The statement is referring to a property that does not exist."
+ Neo_ClientError_Statement_ParameterMissing: "The statement is referring to a parameter that was not provided in the Request."
+ Neo_ClientError_Transaction_ConcurrentRequest: "There were concurrent requests accessing the same transaction, which is not allowed."
+ Neo_ClientError_Transaction_EventHandlerThrewException: "A transaction event handler threw an exception. The transaction will be rolled back."
+ Neo_ClientError_Transaction_HookFailed: "Transaction hook failure."
+ Neo_ClientError_Transaction_InvalidType: "The transaction is of the wrong type to service the Request_ For instance, a transaction that has had schema modifications performed in it cannot be used to subsequently perform data operations, and vice versa."
+ Neo_ClientError_Transaction_MarkedAsFailed: "Transaction was marked as both successful and failed. Failure takes precedence and so this transaction was rolled back although it may have looked like it was going to be committed"
+ Neo_ClientError_Transaction_UnknownId: "The request referred to a transaction that does not exist."
+ Neo_ClientError_Transaction_ValidationFailed: "Transaction changes did not pass validation checks"
+ Neo_DatabaseError_General_CorruptSchemaRule: "A malformed schema rule was encountered. Please contact your support representative."
+ Neo_DatabaseError_General_FailedIndex: "The request (directly or indirectly) referred to an index that is in a failed state. The index needs to be dropped and recreated manually."
+ Neo_DatabaseError_General_UnknownFailure: "An unknown failure occurred."
+ Neo_DatabaseError_Schema_ConstraintCreationFailure: "Creating a requested constraint failed."
+ Neo_DatabaseError_Schema_ConstraintDropFailure: "The database failed to drop a requested constraint."
+ Neo_DatabaseError_Schema_IndexCreationFailure: "Failed to create an index."
+ Neo_DatabaseError_Schema_IndexDropFailure: "The database failed to drop a requested index."
+ Neo_DatabaseError_Schema_NoSuchLabel: "The request accessed a label that did not exist."
+ Neo_DatabaseError_Schema_NoSuchPropertyKey: "The request accessed a property that does not exist."
+ Neo_DatabaseError_Schema_NoSuchRelationshipType: "The request accessed a relationship type that does not exist."
+ Neo_DatabaseError_Schema_NoSuchSchemaRule: "The request referred to a schema rule that does not exist."
+ Neo_DatabaseError_Statement_ExecutionFailure: "The database was unable to execute the Statement."
+ Neo_DatabaseError_Transaction_CouldNotBegin: "The database was unable to start the Transaction."
+ Neo_DatabaseError_Transaction_CouldNotCommit: "The database was unable to commit the Transaction."
+ Neo_DatabaseError_Transaction_CouldNotRollback: "The database was unable to roll back the Transaction."
+ Neo_DatabaseError_Transaction_CouldNotWriteToLog: "The database was unable to write transaction to log."
+ Neo_DatabaseError_Transaction_ReleaseLocksFailed: "The transaction was unable to release one or more of its locks."
+ Neo_TransientError_General_DatabaseUnavailable: "The database is not currently available to serve your request, refer to the database logs for more details. Retrying your request at a later time may succeed."
+ Neo_TransientError_Network_UnknownFailure: "An unknown network failure occurred, a retry may resolve the issue."
+ Neo_TransientError_Schema_ModifiedConcurrently: "The database schema was modified while this transaction was running, the transaction should be retried."
+ Neo_TransientError_Security_ModifiedConcurrently: "The user was modified concurrently to this Request."
+ Neo_TransientError_Statement_ExternalResourceFailure: "The external resource is not available"
+ Neo_TransientError_Transaction_AcquireLockTimeout: "The transaction was unable to acquire a lock, for instance due to a timeout or the transaction thread being interrupted."
+ Neo_TransientError_Transaction_DeadlockDetected: "This transaction, and at least one more transaction, has acquired locks in a way that it will wait indefinitely, and the database has aborted it. Retrying this transaction will most likely be successful." \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/catalog-be/users-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/users-configuration.yaml
new file mode 100644
index 0000000000..a6c23653f0
--- /dev/null
+++ b/catalog-be/src/test/resources/config/catalog-be/users-configuration.yaml
@@ -0,0 +1,2 @@
+userCredentials:
+ ci: 2a1f887d607d4515d4066fe0f5452a50:0a0dc557c3bf594b1a48030e3e99227580168b21f44e285c69740b8d5b13e33b \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/configuration1.yaml b/catalog-be/src/test/resources/config/configuration1.yaml
new file mode 100644
index 0000000000..12ab2c777f
--- /dev/null
+++ b/catalog-be/src/test/resources/config/configuration1.yaml
@@ -0,0 +1,17 @@
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 5
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/elasticsearch.yml b/catalog-be/src/test/resources/config/elasticsearch.yml
new file mode 100644
index 0000000000..a6a2c1b950
--- /dev/null
+++ b/catalog-be/src/test/resources/config/elasticsearch.yml
@@ -0,0 +1,387 @@
+
+cluster.name: elasticsearch_1_5_2
+
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+discovery.zen.ping.unicast.hosts: elasticsearch_host
+
+
+
+##################### Elasticsearch Configuration Example #####################
+
+# This file contains an overview of various configuration settings,
+# targeted at operations staff. Application developers should
+# consult the guide at <http://elasticsearch.org/guide>.
+#
+# The installation procedure is covered at
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
+#
+# Elasticsearch comes with reasonable defaults for most settings,
+# so you can try it out without bothering with configuration.
+#
+# Most of the time, these defaults are just fine for running a production
+# cluster. If you're fine-tuning your cluster, or wondering about the
+# effect of certain configuration option, please _do ask_ on the
+# mailing list or IRC channel [http://elasticsearch.org/community].
+
+# Any element in the configuration can be replaced with environment variables
+# by placing them in ${...} notation. For example:
+#
+# node.rack: ${RACK_ENV_VAR}
+
+# For information on supported formats and syntax for the config file, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
+
+
+################################### Cluster ###################################
+
+# Cluster name identifies your cluster for auto-discovery. If you're running
+# multiple clusters on the same network, make sure you're using unique names.
+#
+# cluster.name: elasticsearch
+
+
+#################################### Node #####################################
+
+# Node names are generated dynamically on startup, so you're relieved
+# from configuring them manually. You can tie this node to a specific name:
+#
+# node.name: "Franz Kafka"
+
+# Every node can be configured to allow or deny being eligible as the master,
+# and to allow or deny to store the data.
+#
+# Allow this node to be eligible as a master node (enabled by default):
+#
+# node.master: true
+#
+# Allow this node to store data (enabled by default):
+#
+# node.data: true
+
+# You can exploit these settings to design advanced cluster topologies.
+#
+# 1. You want this node to never become a master node, only to hold data.
+# This will be the "workhorse" of your cluster.
+#
+# node.master: false
+# node.data: true
+#
+# 2. You want this node to only serve as a master: to not store any data and
+# to have free resources. This will be the "coordinator" of your cluster.
+#
+# node.master: true
+# node.data: false
+#
+# 3. You want this node to be neither master nor data node, but
+# to act as a "search load balancer" (fetching data from nodes,
+# aggregating results, etc.)
+#
+# node.master: false
+# node.data: false
+
+# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
+# Node Info API [http://localhost:9200/_nodes] or GUI tools
+# such as <http://www.elasticsearch.org/overview/marvel/>,
+# <http://github.com/karmi/elasticsearch-paramedic>,
+# <http://github.com/lukas-vlcek/bigdesk> and
+# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
+
+# A node can have generic attributes associated with it, which can later be used
+# for customized shard allocation filtering, or allocation awareness. An attribute
+# is a simple key value pair, similar to node.key: value, here is an example:
+#
+# node.rack: rack314
+
+# By default, multiple nodes are allowed to start from the same installation location
+# to disable it, set the following:
+# node.max_local_storage_nodes: 1
+
+
+#################################### Index ####################################
+
+# You can set a number of options (such as shard/replica options, mapping
+# or analyzer definitions, translog settings, ...) for indices globally,
+# in this file.
+#
+# Note, that it makes more sense to configure index settings specifically for
+# a certain index, either when creating it or by using the index templates API.
+#
+# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
+# for more information.
+
+# Set the number of shards (splits) of an index (5 by default):
+#
+# index.number_of_shards: 5
+
+# Set the number of replicas (additional copies) of an index (1 by default):
+#
+# index.number_of_replicas: 1
+
+# Note, that for development on a local machine, with small indices, it usually
+# makes sense to "disable" the distributed features:
+#
+index.number_of_shards: 1
+index.number_of_replicas: 0
+
+# These settings directly affect the performance of index and search operations
+# in your cluster. Assuming you have enough machines to hold shards and
+# replicas, the rule of thumb is:
+#
+# 1. Having more *shards* enhances the _indexing_ performance and allows to
+# _distribute_ a big index across machines.
+# 2. Having more *replicas* enhances the _search_ performance and improves the
+# cluster _availability_.
+#
+# The "number_of_shards" is a one-time setting for an index.
+#
+# The "number_of_replicas" can be increased or decreased anytime,
+# by using the Index Update Settings API.
+#
+# Elasticsearch takes care about load balancing, relocating, gathering the
+# results from nodes, etc. Experiment with different settings to fine-tune
+# your setup.
+
+# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
+# the index status.
+
+
+#################################### Paths ####################################
+
+# Path to directory containing configuration (this file and logging.yml):
+#
+path.conf: /src/test/resources
+
+# Path to directory where to store index data allocated for this node.
+#
+path.data: target/esdata
+#
+# Can optionally include more than one location, causing data to be striped across
+# the locations (a la RAID 0) on a file level, favouring locations with most free
+# space on creation. For example:
+#
+# path.data: /path/to/data1,/path/to/data2
+
+# Path to temporary files:
+#
+path.work: /target/eswork
+
+# Path to log files:
+#
+path.logs: /target/eslogs
+
+# Path to where plugins are installed:
+#
+# path.plugins: /path/to/plugins
+
+
+#################################### Plugin ###################################
+
+# If a plugin listed here is not installed for current node, the node will not start.
+#
+# plugin.mandatory: mapper-attachments,lang-groovy
+
+
+################################### Memory ####################################
+
+# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
+# it _never_ swaps.
+#
+# Set this property to true to lock the memory:
+#
+# bootstrap.mlockall: true
+
+# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
+# to the same value, and that the machine has enough memory to allocate
+# for Elasticsearch, leaving enough memory for the operating system itself.
+#
+# You should also make sure that the Elasticsearch process is allowed to lock
+# the memory, eg. by using `ulimit -l unlimited`.
+
+
+############################## Network And HTTP ###############################
+
+# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
+# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
+# communication. (the range means that if the port is busy, it will automatically
+# try the next port).
+
+# Set the bind address specifically (IPv4 or IPv6):
+#
+# network.bind_host: 192.168.0.1
+
+# Set the address other nodes will use to communicate with this node. If not
+# set, it is automatically derived. It must point to an actual IP address.
+#
+# network.publish_host: 192.168.0.1
+
+# Set both 'bind_host' and 'publish_host':
+#
+# network.host: 192.168.0.1
+
+# Set a custom port for the node to node communication (9300 by default):
+#
+# transport.tcp.port: 9300
+
+# Enable compression for all communication between nodes (disabled by default):
+#
+# transport.tcp.compress: true
+
+# Set a custom port to listen for HTTP traffic:
+#
+# http.port: 9200
+
+# Set a custom allowed content length:
+#
+# http.max_content_length: 100mb
+
+# Disable HTTP completely:
+#
+# http.enabled: false
+
+
+################################### Gateway ###################################
+
+# The gateway allows for persisting the cluster state between full cluster
+# restarts. Every change to the state (such as adding an index) will be stored
+# in the gateway, and when the cluster starts up for the first time,
+# it will read its state from the gateway.
+
+# There are several types of gateway implementations. For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
+
+# The default gateway type is the "local" gateway (recommended):
+#
+# gateway.type: local
+
+# Settings below control how and when to start the initial recovery process on
+# a full cluster restart (to reuse as much local data as possible when using shared
+# gateway).
+
+# Allow recovery process after N nodes in a cluster are up:
+#
+gateway.recover_after_nodes: 1
+
+# Set the timeout to initiate the recovery process, once the N nodes
+# from previous setting are up (accepts time value):
+#
+# gateway.recover_after_time: 5m
+
+# Set how many nodes are expected in this cluster. Once these N nodes
+# are up (and recover_after_nodes is met), begin recovery process immediately
+# (without waiting for recover_after_time to expire):
+#
+gateway.expected_nodes: 1
+
+
+############################# Recovery Throttling #############################
+
+# These settings allow to control the process of shards allocation between
+# nodes during initial recovery, replica allocation, rebalancing,
+# or when adding and removing nodes.
+
+# Set the number of concurrent recoveries happening on a node:
+#
+# 1. During the initial recovery
+#
+# cluster.routing.allocation.node_initial_primaries_recoveries: 4
+#
+# 2. During adding/removing nodes, rebalancing, etc
+#
+# cluster.routing.allocation.node_concurrent_recoveries: 2
+
+# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
+#
+# indices.recovery.max_bytes_per_sec: 20mb
+
+# Set to limit the number of open concurrent streams when
+# recovering a shard from a peer:
+#
+# indices.recovery.concurrent_streams: 5
+
+
+################################## Discovery ##################################
+
+# Discovery infrastructure ensures nodes can be found within a cluster
+# and master node is elected. Multicast discovery is the default.
+
+# Set to ensure a node sees N other master eligible nodes to be considered
+# operational within the cluster. Its recommended to set it to a higher value
+# than 1 when running more than 2 nodes in the cluster.
+#
+# discovery.zen.minimum_master_nodes: 1
+
+# Set the time to wait for ping responses from other nodes when discovering.
+# Set this option to a higher value on a slow or congested network
+# to minimize discovery failures:
+#
+# discovery.zen.ping.timeout: 3s
+
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
+
+# Unicast discovery allows to explicitly control which nodes will be used
+# to discover the cluster. It can be used when multicast is not present,
+# or to restrict the cluster communication-wise.
+#
+# 1. Disable multicast discovery (enabled by default):
+#
+# discovery.zen.ping.multicast.enabled: false
+#
+# 2. Configure an initial list of master nodes in the cluster
+# to perform discovery when new nodes (master or data) are started:
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
+
+# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
+#
+# You have to install the cloud-aws plugin for enabling the EC2 discovery.
+#
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
+#
+# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
+# for a step-by-step tutorial.
+
+# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
+#
+# You have to install the cloud-gce plugin for enabling the GCE discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
+
+# Azure discovery allows to use Azure API in order to perform discovery.
+#
+# You have to install the cloud-azure plugin for enabling the Azure discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
+
+################################## Slow Log ##################################
+
+# Shard level query and fetch threshold logging.
+
+#index.search.slowlog.threshold.query.warn: 10s
+#index.search.slowlog.threshold.query.info: 5s
+#index.search.slowlog.threshold.query.debug: 2s
+#index.search.slowlog.threshold.query.trace: 500ms
+
+#index.search.slowlog.threshold.fetch.warn: 1s
+#index.search.slowlog.threshold.fetch.info: 800ms
+#index.search.slowlog.threshold.fetch.debug: 500ms
+#index.search.slowlog.threshold.fetch.trace: 200ms
+
+#index.indexing.slowlog.threshold.index.warn: 10s
+#index.indexing.slowlog.threshold.index.info: 5s
+#index.indexing.slowlog.threshold.index.debug: 2s
+#index.indexing.slowlog.threshold.index.trace: 500ms
+
+################################## GC Logging ################################
+
+#monitor.jvm.gc.young.warn: 1000ms
+#monitor.jvm.gc.young.info: 700ms
+#monitor.jvm.gc.young.debug: 400ms
+
+#monitor.jvm.gc.old.warn: 10s
+#monitor.jvm.gc.old.info: 5s
+#monitor.jvm.gc.old.debug: 2s
+
diff --git a/catalog-be/src/test/resources/config/elasticsearch.yml.bak b/catalog-be/src/test/resources/config/elasticsearch.yml.bak
new file mode 100644
index 0000000000..98c6864bf2
--- /dev/null
+++ b/catalog-be/src/test/resources/config/elasticsearch.yml.bak
@@ -0,0 +1,387 @@
+
+cluster.name: elasticsearch_pavel
+
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+discovery.zen.ping.unicast.hosts: elasticsearch_host
+
+
+
+##################### Elasticsearch Configuration Example #####################
+
+# This file contains an overview of various configuration settings,
+# targeted at operations staff. Application developers should
+# consult the guide at <http://elasticsearch.org/guide>.
+#
+# The installation procedure is covered at
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
+#
+# Elasticsearch comes with reasonable defaults for most settings,
+# so you can try it out without bothering with configuration.
+#
+# Most of the time, these defaults are just fine for running a production
+# cluster. If you're fine-tuning your cluster, or wondering about the
+# effect of certain configuration option, please _do ask_ on the
+# mailing list or IRC channel [http://elasticsearch.org/community].
+
+# Any element in the configuration can be replaced with environment variables
+# by placing them in ${...} notation. For example:
+#
+# node.rack: ${RACK_ENV_VAR}
+
+# For information on supported formats and syntax for the config file, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
+
+
+################################### Cluster ###################################
+
+# Cluster name identifies your cluster for auto-discovery. If you're running
+# multiple clusters on the same network, make sure you're using unique names.
+#
+# cluster.name: elasticsearch
+
+
+#################################### Node #####################################
+
+# Node names are generated dynamically on startup, so you're relieved
+# from configuring them manually. You can tie this node to a specific name:
+#
+# node.name: "Franz Kafka"
+
+# Every node can be configured to allow or deny being eligible as the master,
+# and to allow or deny to store the data.
+#
+# Allow this node to be eligible as a master node (enabled by default):
+#
+# node.master: true
+#
+# Allow this node to store data (enabled by default):
+#
+# node.data: true
+
+# You can exploit these settings to design advanced cluster topologies.
+#
+# 1. You want this node to never become a master node, only to hold data.
+# This will be the "workhorse" of your cluster.
+#
+# node.master: false
+# node.data: true
+#
+# 2. You want this node to only serve as a master: to not store any data and
+# to have free resources. This will be the "coordinator" of your cluster.
+#
+# node.master: true
+# node.data: false
+#
+# 3. You want this node to be neither master nor data node, but
+# to act as a "search load balancer" (fetching data from nodes,
+# aggregating results, etc.)
+#
+# node.master: false
+# node.data: false
+
+# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
+# Node Info API [http://localhost:9200/_nodes] or GUI tools
+# such as <http://www.elasticsearch.org/overview/marvel/>,
+# <http://github.com/karmi/elasticsearch-paramedic>,
+# <http://github.com/lukas-vlcek/bigdesk> and
+# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
+
+# A node can have generic attributes associated with it, which can later be used
+# for customized shard allocation filtering, or allocation awareness. An attribute
+# is a simple key value pair, similar to node.key: value, here is an example:
+#
+# node.rack: rack314
+
+# By default, multiple nodes are allowed to start from the same installation location
+# to disable it, set the following:
+# node.max_local_storage_nodes: 1
+
+
+#################################### Index ####################################
+
+# You can set a number of options (such as shard/replica options, mapping
+# or analyzer definitions, translog settings, ...) for indices globally,
+# in this file.
+#
+# Note, that it makes more sense to configure index settings specifically for
+# a certain index, either when creating it or by using the index templates API.
+#
+# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
+# for more information.
+
+# Set the number of shards (splits) of an index (5 by default):
+#
+# index.number_of_shards: 5
+
+# Set the number of replicas (additional copies) of an index (1 by default):
+#
+# index.number_of_replicas: 1
+
+# Note, that for development on a local machine, with small indices, it usually
+# makes sense to "disable" the distributed features:
+#
+index.number_of_shards: 1
+index.number_of_replicas: 0
+
+# These settings directly affect the performance of index and search operations
+# in your cluster. Assuming you have enough machines to hold shards and
+# replicas, the rule of thumb is:
+#
+# 1. Having more *shards* enhances the _indexing_ performance and allows to
+# _distribute_ a big index across machines.
+# 2. Having more *replicas* enhances the _search_ performance and improves the
+# cluster _availability_.
+#
+# The "number_of_shards" is a one-time setting for an index.
+#
+# The "number_of_replicas" can be increased or decreased anytime,
+# by using the Index Update Settings API.
+#
+# Elasticsearch takes care about load balancing, relocating, gathering the
+# results from nodes, etc. Experiment with different settings to fine-tune
+# your setup.
+
+# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
+# the index status.
+
+
+#################################### Paths ####################################
+
+# Path to directory containing configuration (this file and logging.yml):
+#
+path.conf: /src/test/resources
+
+# Path to directory where to store index data allocated for this node.
+#
+path.data: target/esdata
+#
+# Can optionally include more than one location, causing data to be striped across
+# the locations (a la RAID 0) on a file level, favouring locations with most free
+# space on creation. For example:
+#
+# path.data: /path/to/data1,/path/to/data2
+
+# Path to temporary files:
+#
+path.work: /target/eswork
+
+# Path to log files:
+#
+path.logs: /target/eslogs
+
+# Path to where plugins are installed:
+#
+# path.plugins: /path/to/plugins
+
+
+#################################### Plugin ###################################
+
+# If a plugin listed here is not installed for current node, the node will not start.
+#
+# plugin.mandatory: mapper-attachments,lang-groovy
+
+
+################################### Memory ####################################
+
+# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
+# it _never_ swaps.
+#
+# Set this property to true to lock the memory:
+#
+# bootstrap.mlockall: true
+
+# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
+# to the same value, and that the machine has enough memory to allocate
+# for Elasticsearch, leaving enough memory for the operating system itself.
+#
+# You should also make sure that the Elasticsearch process is allowed to lock
+# the memory, eg. by using `ulimit -l unlimited`.
+
+
+############################## Network And HTTP ###############################
+
+# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
+# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
+# communication. (the range means that if the port is busy, it will automatically
+# try the next port).
+
+# Set the bind address specifically (IPv4 or IPv6):
+#
+# network.bind_host: 192.168.0.1
+
+# Set the address other nodes will use to communicate with this node. If not
+# set, it is automatically derived. It must point to an actual IP address.
+#
+# network.publish_host: 192.168.0.1
+
+# Set both 'bind_host' and 'publish_host':
+#
+# network.host: 192.168.0.1
+
+# Set a custom port for the node to node communication (9300 by default):
+#
+# transport.tcp.port: 9300
+
+# Enable compression for all communication between nodes (disabled by default):
+#
+# transport.tcp.compress: true
+
+# Set a custom port to listen for HTTP traffic:
+#
+# http.port: 9200
+
+# Set a custom allowed content length:
+#
+# http.max_content_length: 100mb
+
+# Disable HTTP completely:
+#
+# http.enabled: false
+
+
+################################### Gateway ###################################
+
+# The gateway allows for persisting the cluster state between full cluster
+# restarts. Every change to the state (such as adding an index) will be stored
+# in the gateway, and when the cluster starts up for the first time,
+# it will read its state from the gateway.
+
+# There are several types of gateway implementations. For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
+
+# The default gateway type is the "local" gateway (recommended):
+#
+# gateway.type: local
+
+# Settings below control how and when to start the initial recovery process on
+# a full cluster restart (to reuse as much local data as possible when using shared
+# gateway).
+
+# Allow recovery process after N nodes in a cluster are up:
+#
+gateway.recover_after_nodes: 1
+
+# Set the timeout to initiate the recovery process, once the N nodes
+# from previous setting are up (accepts time value):
+#
+# gateway.recover_after_time: 5m
+
+# Set how many nodes are expected in this cluster. Once these N nodes
+# are up (and recover_after_nodes is met), begin recovery process immediately
+# (without waiting for recover_after_time to expire):
+#
+gateway.expected_nodes: 1
+
+
+############################# Recovery Throttling #############################
+
+# These settings allow to control the process of shards allocation between
+# nodes during initial recovery, replica allocation, rebalancing,
+# or when adding and removing nodes.
+
+# Set the number of concurrent recoveries happening on a node:
+#
+# 1. During the initial recovery
+#
+# cluster.routing.allocation.node_initial_primaries_recoveries: 4
+#
+# 2. During adding/removing nodes, rebalancing, etc
+#
+# cluster.routing.allocation.node_concurrent_recoveries: 2
+
+# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
+#
+# indices.recovery.max_bytes_per_sec: 20mb
+
+# Set to limit the number of open concurrent streams when
+# recovering a shard from a peer:
+#
+# indices.recovery.concurrent_streams: 5
+
+
+################################## Discovery ##################################
+
+# Discovery infrastructure ensures nodes can be found within a cluster
+# and master node is elected. Multicast discovery is the default.
+
+# Set to ensure a node sees N other master eligible nodes to be considered
+# operational within the cluster. Its recommended to set it to a higher value
+# than 1 when running more than 2 nodes in the cluster.
+#
+# discovery.zen.minimum_master_nodes: 1
+
+# Set the time to wait for ping responses from other nodes when discovering.
+# Set this option to a higher value on a slow or congested network
+# to minimize discovery failures:
+#
+# discovery.zen.ping.timeout: 3s
+
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
+
+# Unicast discovery allows to explicitly control which nodes will be used
+# to discover the cluster. It can be used when multicast is not present,
+# or to restrict the cluster communication-wise.
+#
+# 1. Disable multicast discovery (enabled by default):
+#
+# discovery.zen.ping.multicast.enabled: false
+#
+# 2. Configure an initial list of master nodes in the cluster
+# to perform discovery when new nodes (master or data) are started:
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
+
+# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
+#
+# You have to install the cloud-aws plugin for enabling the EC2 discovery.
+#
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
+#
+# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
+# for a step-by-step tutorial.
+
+# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
+#
+# You have to install the cloud-gce plugin for enabling the GCE discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
+
+# Azure discovery allows to use Azure API in order to perform discovery.
+#
+# You have to install the cloud-azure plugin for enabling the Azure discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
+
+################################## Slow Log ##################################
+
+# Shard level query and fetch threshold logging.
+
+#index.search.slowlog.threshold.query.warn: 10s
+#index.search.slowlog.threshold.query.info: 5s
+#index.search.slowlog.threshold.query.debug: 2s
+#index.search.slowlog.threshold.query.trace: 500ms
+
+#index.search.slowlog.threshold.fetch.warn: 1s
+#index.search.slowlog.threshold.fetch.info: 800ms
+#index.search.slowlog.threshold.fetch.debug: 500ms
+#index.search.slowlog.threshold.fetch.trace: 200ms
+
+#index.indexing.slowlog.threshold.index.warn: 10s
+#index.indexing.slowlog.threshold.index.info: 5s
+#index.indexing.slowlog.threshold.index.debug: 2s
+#index.indexing.slowlog.threshold.index.trace: 500ms
+
+################################## GC Logging ################################
+
+#monitor.jvm.gc.young.warn: 1000ms
+#monitor.jvm.gc.young.info: 700ms
+#monitor.jvm.gc.young.debug: 400ms
+
+#monitor.jvm.gc.old.warn: 10s
+#monitor.jvm.gc.old.info: 5s
+#monitor.jvm.gc.old.debug: 2s
+
diff --git a/catalog-be/src/test/resources/config/mysql-type-empty-nodes.zip b/catalog-be/src/test/resources/config/mysql-type-empty-nodes.zip
new file mode 100644
index 0000000000..d317bccd1e
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type-empty-nodes.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/mysql-type-no-nodes.zip b/catalog-be/src/test/resources/config/mysql-type-no-nodes.zip
new file mode 100644
index 0000000000..09999faed5
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type-no-nodes.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/mysql-type-no-version.zip b/catalog-be/src/test/resources/config/mysql-type-no-version.zip
new file mode 100644
index 0000000000..fa1319f311
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type-no-version.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/mysql-type-only-yaml.zip b/catalog-be/src/test/resources/config/mysql-type-only-yaml.zip
new file mode 100644
index 0000000000..b4b1946940
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type-only-yaml.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/mysql-type-with-scripts.zip b/catalog-be/src/test/resources/config/mysql-type-with-scripts.zip
new file mode 100644
index 0000000000..d689b668a1
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type-with-scripts.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/mysql-type.yml b/catalog-be/src/test/resources/config/mysql-type.yml
new file mode 100644
index 0000000000..f1985a0bdc
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type.yml
@@ -0,0 +1,82 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: MySQL RDBMS installation on a specific mounted volume path.
+template_name: mysql-type
+template_version: 1.1.1-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Mysql:
+ derived_from: tosca.nodes.Database
+ description: >
+ A node to install MySQL v5.5 database with data
+ on a specific attached volume.
+ capabilities:
+ host:
+ type: alien.capabilities.MysqlDatabase
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/mysql.png
+ properties:
+ db_port:
+ type: integer
+ default: 3306
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: true
+ default: wordpress
+ description: The logical name of the database.
+ db_user:
+ type: string
+ default: pass
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ default: pass
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ bind_address:
+ type: boolean
+ default: true
+ required: false
+ description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces.
+ storage_path:
+ type: string
+ default: /mountedStorage
+ constraints:
+ - valid_values: [ "/mountedStorage", "/var/mysql" ]
+ interfaces:
+ Standard:
+ create: scripts/install_mysql.sh
+ start:
+ inputs:
+ VOLUME_HOME: { get_property: [SELF, storage_path] }
+ PORT: { get_property: [SELF, db_port] }
+ DB_NAME: { get_property: [SELF, db_name] }
+ DB_USER: { get_property: [SELF, db_user] }
+ DB_PASSWORD: { get_property: [SELF, db_password] }
+ BIND_ADRESS: { get_property: [SELF, bind_address] }
+ implementation: scripts/start_mysql.sh
+ fastconnect.cloudify.extensions:
+ start_detection:
+ inputs:
+ PORT: { get_property: [SELF, db_port] }
+ implementation: scripts/mysql_start_detection.groovy
+ artifacts:
+ - scripts: scripts
+ type: tosca.artifacts.File
+
+capability_types:
+ alien.capabilities.MysqlDatabase:
+ derived_from: tosca.capabilities.Container
+
+artifact_types:
+ tosca.artifacts.GroovyScript:
+ description: A groovy script (.groovy file)
+ file_ext: [groovy]
diff --git a/catalog-be/src/test/resources/config/mysql-type.zip b/catalog-be/src/test/resources/config/mysql-type.zip
new file mode 100644
index 0000000000..b4b1946940
--- /dev/null
+++ b/catalog-be/src/test/resources/config/mysql-type.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/normative-types-root.zip b/catalog-be/src/test/resources/config/normative-types-root.zip
new file mode 100644
index 0000000000..b0c39962d1
--- /dev/null
+++ b/catalog-be/src/test/resources/config/normative-types-root.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/config/sample.yaml b/catalog-be/src/test/resources/config/sample.yaml
new file mode 100644
index 0000000000..12ab2c777f
--- /dev/null
+++ b/catalog-be/src/test/resources/config/sample.yaml
@@ -0,0 +1,17 @@
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 5
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd \ No newline at end of file
diff --git a/catalog-be/src/test/resources/config/sampleNoProtocol.yaml b/catalog-be/src/test/resources/config/sampleNoProtocol.yaml
new file mode 100644
index 0000000000..6197232aa4
--- /dev/null
+++ b/catalog-be/src/test/resources/config/sampleNoProtocol.yaml
@@ -0,0 +1,17 @@
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 5
+
+# Protocols
+#protocols:
+# - http
+# - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd \ No newline at end of file
diff --git a/catalog-be/src/test/resources/elasticsearch.yml b/catalog-be/src/test/resources/elasticsearch.yml
new file mode 100644
index 0000000000..eba942dc31
--- /dev/null
+++ b/catalog-be/src/test/resources/elasticsearch.yml
@@ -0,0 +1,391 @@
+
+elasticSearch.local: true
+elasticSearch.transportclient: false
+cluster.name: elasticsearch_1_5_2222
+
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+discovery.zen.ping.unicast.hosts: 1.2.3.4
+transport.client.initial_nodes:
+ - 1.2.3.4:9300
+
+#plugin.types: "DeleteByQueryPlugin"
+
+##################### Elasticsearch Configuration Example #####################
+
+# This file contains an overview of various configuration settings,
+# targeted at operations staff. Application developers should
+# consult the guide at <http://elasticsearch.org/guide>.
+#
+# The installation procedure is covered at
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
+#
+# Elasticsearch comes with reasonable defaults for most settings,
+# so you can try it out without bothering with configuration.
+#
+# Most of the time, these defaults are just fine for running a production
+# cluster. If you're fine-tuning your cluster, or wondering about the
+# effect of certain configuration option, please _do ask_ on the
+# mailing list or IRC channel [http://elasticsearch.org/community].
+
+# Any element in the configuration can be replaced with environment variables
+# by placing them in ${...} notation. For example:
+#
+# node.rack: ${RACK_ENV_VAR}
+
+# For information on supported formats and syntax for the config file, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
+
+
+################################### Cluster ###################################
+
+# Cluster name identifies your cluster for auto-discovery. If you're running
+# multiple clusters on the same network, make sure you're using unique names.
+#
+# cluster.name: elasticsearch
+
+
+#################################### Node #####################################
+
+# Node names are generated dynamically on startup, so you're relieved
+# from configuring them manually. You can tie this node to a specific name:
+#
+# node.name: "Franz Kafka"
+
+# Every node can be configured to allow or deny being eligible as the master,
+# and to allow or deny to store the data.
+#
+# Allow this node to be eligible as a master node (enabled by default):
+#
+# node.master: true
+#
+# Allow this node to store data (enabled by default):
+#
+# node.data: true
+
+# You can exploit these settings to design advanced cluster topologies.
+#
+# 1. You want this node to never become a master node, only to hold data.
+# This will be the "workhorse" of your cluster.
+#
+# node.master: false
+# node.data: true
+#
+# 2. You want this node to only serve as a master: to not store any data and
+# to have free resources. This will be the "coordinator" of your cluster.
+#
+# node.master: true
+# node.data: false
+#
+# 3. You want this node to be neither master nor data node, but
+# to act as a "search load balancer" (fetching data from nodes,
+# aggregating results, etc.)
+#
+# node.master: false
+# node.data: false
+
+# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
+# Node Info API [http://localhost:9200/_nodes] or GUI tools
+# such as <http://www.elasticsearch.org/overview/marvel/>,
+# <http://github.com/karmi/elasticsearch-paramedic>,
+# <http://github.com/lukas-vlcek/bigdesk> and
+# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
+
+# A node can have generic attributes associated with it, which can later be used
+# for customized shard allocation filtering, or allocation awareness. An attribute
+# is a simple key value pair, similar to node.key: value, here is an example:
+#
+# node.rack: rack314
+
+# By default, multiple nodes are allowed to start from the same installation location
+# to disable it, set the following:
+# node.max_local_storage_nodes: 1
+
+
+#################################### Index ####################################
+
+# You can set a number of options (such as shard/replica options, mapping
+# or analyzer definitions, translog settings, ...) for indices globally,
+# in this file.
+#
+# Note, that it makes more sense to configure index settings specifically for
+# a certain index, either when creating it or by using the index templates API.
+#
+# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
+# for more information.
+
+# Set the number of shards (splits) of an index (5 by default):
+#
+# index.number_of_shards: 5
+
+# Set the number of replicas (additional copies) of an index (1 by default):
+#
+# index.number_of_replicas: 1
+
+# Note, that for development on a local machine, with small indices, it usually
+# makes sense to "disable" the distributed features:
+#
+index.number_of_shards: 1
+index.number_of_replicas: 0
+
+# These settings directly affect the performance of index and search operations
+# in your cluster. Assuming you have enough machines to hold shards and
+# replicas, the rule of thumb is:
+#
+# 1. Having more *shards* enhances the _indexing_ performance and allows to
+# _distribute_ a big index across machines.
+# 2. Having more *replicas* enhances the _search_ performance and improves the
+# cluster _availability_.
+#
+# The "number_of_shards" is a one-time setting for an index.
+#
+# The "number_of_replicas" can be increased or decreased anytime,
+# by using the Index Update Settings API.
+#
+# Elasticsearch takes care about load balancing, relocating, gathering the
+# results from nodes, etc. Experiment with different settings to fine-tune
+# your setup.
+
+# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
+# the index status.
+
+
+#################################### Paths ####################################
+path.home: /src/test/resources
+# Path to directory containing configuration (this file and logging.yml):
+#
+path.conf: /src/test/resources
+
+# Path to directory where to store index data allocated for this node.
+#
+path.data: target/esdata
+#
+# Can optionally include more than one location, causing data to be striped across
+# the locations (a la RAID 0) on a file level, favouring locations with most free
+# space on creation. For example:
+#
+# path.data: /path/to/data1,/path/to/data2
+
+# Path to temporary files:
+#
+path.work: /target/eswork
+
+# Path to log files:
+#
+path.logs: /target/eslogs
+
+# Path to where plugins are installed:
+#
+# path.plugins: /path/to/plugins
+
+
+#################################### Plugin ###################################
+
+# If a plugin listed here is not installed for current node, the node will not start.
+#
+# plugin.mandatory: mapper-attachments,lang-groovy
+
+
+################################### Memory ####################################
+
+# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
+# it _never_ swaps.
+#
+# Set this property to true to lock the memory:
+#
+# bootstrap.mlockall: true
+
+# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
+# to the same value, and that the machine has enough memory to allocate
+# for Elasticsearch, leaving enough memory for the operating system itself.
+#
+# You should also make sure that the Elasticsearch process is allowed to lock
+# the memory, eg. by using `ulimit -l unlimited`.
+
+
+############################## Network And HTTP ###############################
+
+# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
+# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
+# communication. (the range means that if the port is busy, it will automatically
+# try the next port).
+
+# Set the bind address specifically (IPv4 or IPv6):
+#
+# network.bind_host: 192.168.0.1
+
+# Set the address other nodes will use to communicate with this node. If not
+# set, it is automatically derived. It must point to an actual IP address.
+#
+# network.publish_host: 192.168.0.1
+
+# Set both 'bind_host' and 'publish_host':
+#
+# network.host: 192.168.0.1
+
+# Set a custom port for the node to node communication (9300 by default):
+#
+# transport.tcp.port: 9300
+
+# Enable compression for all communication between nodes (disabled by default):
+#
+# transport.tcp.compress: true
+
+# Set a custom port to listen for HTTP traffic:
+#
+# http.port: 9200
+
+# Set a custom allowed content length:
+#
+# http.max_content_length: 100mb
+
+# Disable HTTP completely:
+#
+# http.enabled: false
+
+
+################################### Gateway ###################################
+
+# The gateway allows for persisting the cluster state between full cluster
+# restarts. Every change to the state (such as adding an index) will be stored
+# in the gateway, and when the cluster starts up for the first time,
+# it will read its state from the gateway.
+
+# There are several types of gateway implementations. For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
+
+# The default gateway type is the "local" gateway (recommended):
+#
+# gateway.type: local
+
+# Settings below control how and when to start the initial recovery process on
+# a full cluster restart (to reuse as much local data as possible when using shared
+# gateway).
+
+# Allow recovery process after N nodes in a cluster are up:
+#
+gateway.recover_after_nodes: 1
+
+# Set the timeout to initiate the recovery process, once the N nodes
+# from previous setting are up (accepts time value):
+#
+# gateway.recover_after_time: 5m
+
+# Set how many nodes are expected in this cluster. Once these N nodes
+# are up (and recover_after_nodes is met), begin recovery process immediately
+# (without waiting for recover_after_time to expire):
+#
+gateway.expected_nodes: 1
+
+
+############################# Recovery Throttling #############################
+
+# These settings allow to control the process of shards allocation between
+# nodes during initial recovery, replica allocation, rebalancing,
+# or when adding and removing nodes.
+
+# Set the number of concurrent recoveries happening on a node:
+#
+# 1. During the initial recovery
+#
+# cluster.routing.allocation.node_initial_primaries_recoveries: 4
+#
+# 2. During adding/removing nodes, rebalancing, etc
+#
+# cluster.routing.allocation.node_concurrent_recoveries: 2
+
+# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
+#
+# indices.recovery.max_bytes_per_sec: 20mb
+
+# Set to limit the number of open concurrent streams when
+# recovering a shard from a peer:
+#
+# indices.recovery.concurrent_streams: 5
+
+
+################################## Discovery ##################################
+
+# Discovery infrastructure ensures nodes can be found within a cluster
+# and master node is elected. Multicast discovery is the default.
+
+# Set to ensure a node sees N other master eligible nodes to be considered
+# operational within the cluster. Its recommended to set it to a higher value
+# than 1 when running more than 2 nodes in the cluster.
+#
+# discovery.zen.minimum_master_nodes: 1
+
+# Set the time to wait for ping responses from other nodes when discovering.
+# Set this option to a higher value on a slow or congested network
+# to minimize discovery failures:
+#
+# discovery.zen.ping.timeout: 3s
+
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
+
+# Unicast discovery allows to explicitly control which nodes will be used
+# to discover the cluster. It can be used when multicast is not present,
+# or to restrict the cluster communication-wise.
+#
+# 1. Disable multicast discovery (enabled by default):
+#
+# discovery.zen.ping.multicast.enabled: false
+#
+# 2. Configure an initial list of master nodes in the cluster
+# to perform discovery when new nodes (master or data) are started:
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
+
+# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
+#
+# You have to install the cloud-aws plugin for enabling the EC2 discovery.
+#
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
+#
+# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
+# for a step-by-step tutorial.
+
+# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
+#
+# You have to install the cloud-gce plugin for enabling the GCE discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
+
+# Azure discovery allows to use Azure API in order to perform discovery.
+#
+# You have to install the cloud-azure plugin for enabling the Azure discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
+
+################################## Slow Log ##################################
+
+# Shard level query and fetch threshold logging.
+
+#index.search.slowlog.threshold.query.warn: 10s
+#index.search.slowlog.threshold.query.info: 5s
+#index.search.slowlog.threshold.query.debug: 2s
+#index.search.slowlog.threshold.query.trace: 500ms
+
+#index.search.slowlog.threshold.fetch.warn: 1s
+#index.search.slowlog.threshold.fetch.info: 800ms
+#index.search.slowlog.threshold.fetch.debug: 500ms
+#index.search.slowlog.threshold.fetch.trace: 200ms
+
+#index.indexing.slowlog.threshold.index.warn: 10s
+#index.indexing.slowlog.threshold.index.info: 5s
+#index.indexing.slowlog.threshold.index.debug: 2s
+#index.indexing.slowlog.threshold.index.trace: 500ms
+
+################################## GC Logging ################################
+
+#monitor.jvm.gc.young.warn: 1000ms
+#monitor.jvm.gc.young.info: 700ms
+#monitor.jvm.gc.young.debug: 400ms
+
+#monitor.jvm.gc.old.warn: 10s
+#monitor.jvm.gc.old.info: 5s
+#monitor.jvm.gc.old.debug: 2s
+
diff --git a/catalog-be/src/test/resources/logback-test.xml b/catalog-be/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..d2b9bff23f
--- /dev/null
+++ b/catalog-be/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+<!-- only one line, shut up logback ! -->
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>
+ %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+ </Pattern>
+ </encoder>
+ </appender>
+ <root level="OFF">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration> \ No newline at end of file
diff --git a/catalog-be/src/test/resources/mock_vf.csar b/catalog-be/src/test/resources/mock_vf.csar
new file mode 100644
index 0000000000..4b37f44c73
--- /dev/null
+++ b/catalog-be/src/test/resources/mock_vf.csar
Binary files differ
diff --git a/catalog-be/src/test/resources/normativeTypes/importToscaProperties.yml b/catalog-be/src/test/resources/normativeTypes/importToscaProperties.yml
new file mode 100644
index 0000000000..f856603397
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/importToscaProperties.yml
@@ -0,0 +1,452 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vl.LinkTest:
+ derived_from: tosca.nodes.Root
+ properties:
+ string_prop01:
+ type: map
+ description : another description
+ default: {keyA : val1 , keyB : val2}
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop02:
+ type: map
+ description : another description
+ default: {keyA : "val1" , keyB : "val2"}
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop03:
+ type: map
+ description : another description
+ default: {"keyA" : "val1" , keyB : val2}
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop04:
+ type: map
+ description : another description
+ default: {"keyA" : 10 , keyB : <b>true</b> }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop05:
+ type: map
+ description : another description
+ default: {"keyA" : , keyB : "Big" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop06:
+ type: map
+ description : another description
+ default: {"keyA" : aaaA , keyB : null }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop07:
+ type: map
+ description : another description
+ default: {"keyA" : NULL , keyB : Null }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop08:
+ type: map
+ description : another description
+ default: {"keyA" : "" , keyB : "abcd" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop09:
+ type: map
+ description : another description
+ default: {"keyA" : " " , keyB : "abcd" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop10:
+ type: map
+ description : another description
+ default: {"keyA" : " aaaa" , keyB : " bbbb" }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop11:
+ type: map
+ description : another description
+ default: {"keyA" : "aaaa " , keyB : "bbbb " }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop12:
+ type: map
+ description : another description
+ default: {"keyA" : " aaaa " , keyB : " bbbb ccccc " }
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop13:
+ type: map
+ description : another description
+ default:
+ keyA : "aaaa"
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop14:
+ type: map
+ description : another description
+ default:
+ keyA : " aaaa "
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop15:
+ type: map
+ description : another description
+ default:
+ keyA : AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop16:
+ type: map
+ description : another description
+ default:
+ keyA : AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop17:
+ type: map
+ description : another description
+ default:
+ keyA : AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop18:
+ type: map
+ description : another description
+ default:
+ keyA : <b>AbcD</b>
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop19:
+ type: map
+ description : another description
+ default:
+ keyA : <b>AbcD
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop20:
+ type: map
+ description : another description
+ default:
+ keyA : aaaa
+ keya : aaaa
+ Keya : Aaaa
+ KEYA : nnnn
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop21:
+ type: map
+ description : another description
+ default:
+ keyA : NULL
+ keyB : null
+ keyC : Null
+ entry_schema:
+ description: This is my property
+ type: string
+ string_prop22:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: string
+ integer_prop01:
+ type: map
+ description : another description
+ default: {keyA : 1 , keyB : 1000}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop02:
+ type: map
+ description : another description
+ default: {keyA : Null , keyB : NULL ,keyC : null }
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop03:
+ type: map
+ description : another description
+ default: {keyA : , keyB : -600}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop03:
+ type: map
+ description : another description
+ default: {keyA : 800 , keyB : -600}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop04:
+ type: map
+ description : another description
+ default: {keyA : , keyB : -600}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop05:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyB : 0 }
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop06:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyB : 00}
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop07:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyB : 100 }
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop08:
+ type: map
+ description : another description
+ default:
+ keyA : 100
+ keyB : 200
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop09:
+ type: map
+ description : another description
+ default:
+ keyA : 100
+ keyB : 200
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop10:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyA : Null
+ keyB : 1111
+ keyB : 2222
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop11:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyB : Null
+ keyC : NULL
+ keyD :
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop12:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: integer
+ integer_prop13:
+ type: map
+ description : another description
+ default: {keyA : 100 , keyA : 200}
+ entry_schema:
+ description: This is my property
+ type: integer
+ boolean_prop01:
+ type: map
+ description : another description
+ default: {keyA : true , keyB : false , keyC : false }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop02:
+ type: map
+ description : another description
+ default: {keyA : TRUE , keyB : FALSE , keyC : False }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop03:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyB : Null
+ keyC : NULL
+ keyD :
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop04:
+ type: map
+ description : another description
+ default: {keyA : Null , keyB : NULL ,keyC : null ,keyD : }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop05:
+ type: map
+ description : another description
+ default: {keyA : true , keyB : false , keyC : false }
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop06:
+ type: map
+ description : another description
+ default:
+ keyA : true
+ keyB : true
+ keyC : false
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop07:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop08:
+ type: map
+ description : another description
+ default:
+ keyA : false
+ keyA : true
+ keyB : true
+ keyB : false
+ entry_schema:
+ description: This is my property
+ type: boolean
+ boolean_prop09:
+ type: map
+ description : another description
+ default: {keyA : true,keyA : false,keyB : false,keyB : true}
+ entry_schema:
+ description: This is my property
+ type: boolean
+ float_prop01:
+ type: map
+ description : another description
+ default: {keyA : 1.20 , keyB : 3.56f , keyC : 33}
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop02:
+ type: map
+ description : another description
+ default: {keyA : 0.00, keyB : 0.0 , keyC : 0 }
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop03:
+ type: map
+ description : another description
+ default: {keyA : null, keyB : Null , keyC : NULL , keyD : }
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop04:
+ type: map
+ description : another description
+ default: {keyA : 1.20 , keyB : 3.56f , keyC : 33 }
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop05:
+ type: map
+ description : another description
+ default:
+ keyA : 33
+ keyB : 1.2000
+ keyC : 3.607f
+ keyD : 0
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop06:
+ type: map
+ description : another description
+ default:
+ keyA : 33
+ keyB : 1.2000
+ keyC : 3.607f
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop07:
+ type: map
+ description : another description
+ default:
+ keyA : null
+ keyB : Null
+ keyC : NULL
+ keyD :
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop08:
+ type: map
+ description : another description
+ default:
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop09:
+ type: map
+ description : another description
+ default:
+ keyA : 3.5
+ keyA : 0.01
+ keyB : 3.6
+ keyB :
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop10:
+ type: map
+ description : another description
+ default: {keyA : 0.0002}
+ entry_schema:
+ description: This is my property
+ type: float
+ float_prop11:
+ type: map
+ description : another description
+ default: {keyA : 0.000 , keyA : 003.56f, keyB : 33}
+ entry_schema:
+ description: This is my property
+ type: float
+ capabilities:
+ link:
+ type: tosca.capabilities.network.Linkable \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/importToscaWithAttribute.yml b/catalog-be/src/test/resources/normativeTypes/importToscaWithAttribute.yml
new file mode 100644
index 0000000000..5a51e9e198
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/importToscaWithAttribute.yml
@@ -0,0 +1,41 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.MyComputeTest:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ status: supported
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ myAttr:
+ type: list
+ description: this is my description
+ entry_schema:
+ type: string
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-all-map-test.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-all-map-test.yml
new file mode 100644
index 0000000000..4cf82d8b8f
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-all-map-test.yml
@@ -0,0 +1,30 @@
+tosca.nodes.BlockStorage:
+ allTestTag: tosca.nodes.Root
+ properties:
+ mapTestTag:
+ stringTestTag: stringVal1
+ listTestTag:
+ - allTestTag: 1 MB
+ - listTestTag: 2 MB
+ - stringTestTag: stringVal2
+ volume_id:
+ type: string
+ required: false
+ allTestTag:
+ - greater_or_equal: 1 MB
+ - stringTestTag: stringVal3
+ allTestTag:
+ mapTestTag: string
+ required: true
+ snapshot_id:
+ stringTestTag: stringVal4
+ required: false
+ listTestTag:
+ - testTag1: 1 MB
+ - mapTestTag:
+ - testTag1: 1 MB
+ - type: stringVal2
+ capabilities:
+ mapTestTag:
+ type: tosca.capabilities.Attachment
+ allTestTag: false \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-DBMS.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-DBMS.yml
new file mode 100644
index 0000000000..28919d38e4
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-DBMS.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.DBMS:
+ derived_from: tosca.nodes.SoftwareComponent
+ properties:
+ root_password:
+ type: string
+ required: false
+ description: the optional root password for the DBMS service
+ port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [ tosca.nodes.Database ]
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-Root.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-Root.yml
new file mode 100644
index 0000000000..e9b1de9518
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-Root.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Root:
+ description: The TOSCA Node Type all other TOSCA base Node Types derive from
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ state:
+ type: string
+ capabilities:
+ feature:
+ type: tosca.capabilities.Node
+ requirements:
+ - dependency :
+ capability: tosca.capabilities.Node
+ node: tosca.nodes.Root
+ relationship: tosca.relationships.DependsOn
+ occurrences: [ 0, UNBOUNDED ]
+ interfaces:
+ Standard:
+ type: tosca.interfaces.node.lifecycle.Standard
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-blockStorage.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-blockStorage.yml
new file mode 100644
index 0000000000..a82965215f
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-blockStorage.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.BlockStorage:
+ derived_from: tosca.nodes.Root
+ properties:
+ size:
+ type: scalar-unit.size
+ constraints:
+ - greater_or_equal: 1 MB
+ volume_id:
+ type: string
+ required: false
+ snapshot_id:
+ type: string
+ required: false
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-compute.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-compute.yml
new file mode 100644
index 0000000000..00b07fb908
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-compute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Compute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-database.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-database.yml
new file mode 100644
index 0000000000..5166150c73
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-database.yml
@@ -0,0 +1,27 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.Database:
+ derived_from: tosca.nodes.Root
+ properties:
+ name:
+ type: string
+ description: the logical name of the database
+ port:
+ type: integer
+ description: the port the underlying database service will listen to for data
+ user:
+ type: string
+ description: the optional user account name for DB administration
+ required: false
+ password:
+ type: string
+ description: the optional password for the DB user account
+ required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.DBMS
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ database_endpoint:
+ type: tosca.capabilities.Endpoint.Database
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-port.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-port.yml
new file mode 100644
index 0000000000..2d1540b27b
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-port.yml
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.network.Port:
+ derived_from: tosca.nodes.Root
+ properties:
+ ip_address:
+ type: string
+ required: false
+ order:
+ type: integer
+ required: true
+ default: 0
+ constraints:
+ - greater_or_equal: 0
+ is_default:
+ type: boolean
+ required: false
+ default: false
+ ip_range_start:
+ type: string
+ required: false
+ ip_range_end:
+ type: string
+ required: false
+ requirements:
+ - link:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - binding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-softwareComponent.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-softwareComponent.yml
new file mode 100644
index 0000000000..9beb93a75a
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-softwareComponent.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.SoftwareComponent:
+ derived_from: tosca.nodes.Root
+ properties:
+ # domain-specific software component version
+ component_version:
+ type: version
+ required: false
+ #admin_credential:
+ # type: tosca.datatypes.Credential
+ # required: false
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ node: tosca.nodes.Compute
+ relationship: tosca.relationships.HostedOn
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-new-webServer.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-new-webServer.yml
new file mode 100644
index 0000000000..7c957b5247
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-new-webServer.yml
@@ -0,0 +1,11 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ tosca.nodes.WebServer:
+ derived_from: tosca.nodes.SoftwareComponent
+ capabilities:
+ # Private, layer 4 endpoints
+ data_endpoint: tosca.capabilities.Endpoint
+ admin_endpoint: tosca.capabilities.Endpoint.Admin
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [ tosca.nodes.WebApplication ] \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/normative-types-string-list-test.yml b/catalog-be/src/test/resources/normativeTypes/normative-types-string-list-test.yml
new file mode 100644
index 0000000000..0a292b3418
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/normative-types-string-list-test.yml
@@ -0,0 +1,29 @@
+tosca.nodes.BlockStorage:
+ derived_from: tosca.nodes.Root
+ properties:
+ listTestTag:
+ stringTestTag: stringVal1
+ listTestTag:
+ - listTestTag: 1 MB
+ - listTestTag: 2 MB
+ - stringTestTag: stringVal2
+ volume_id:
+ type: string
+ required: false
+ stringTestTag:
+ - greater_or_equal: 1 MB
+ - stringTestTag: stringVal3
+ stringTestTag:
+ listTestTag: string
+ required: false
+ snapshot_id:
+ stringTestTag: stringVal4
+ required: false
+ listTestTag:
+ - testTag1: 1 MB
+ - listTestTag:
+ - testTag1: 1 MB
+ - type: stringVal2
+ capabilities:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_duplicateNode.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_duplicateNode.yml
new file mode 100644
index 0000000000..be16f471fc
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_duplicateNode.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+ node_templates:
+ mongo_server:
+ type: tosca.nodes.Database
+ mongo_server:
+ type: tosca.nodes.Compute
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_empty.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_empty.yml
new file mode 100644
index 0000000000..43ad624300
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_empty.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_inputs.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_inputs.yml
new file mode 100644
index 0000000000..fbc64e05fe
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_inputs.yml
@@ -0,0 +1,103 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+ inputs:
+ vf_module_id:
+ hidden: false
+ immutable: false
+ type: string
+ description: Unique ID for this VF Module instance
+ default: dummy
+ cmd_fw_srv_grp_id:
+ hidden: false
+ immutable: false
+ type: string
+ description: uuid of the server group
+ default: be97d566-35c1-4bc6-a9dd-a5f193cba314
+ nimbus_hsl_interface_type:
+ hidden: false
+ immutable: false
+ type: string
+ description: service_interface_type for ServiceInstance
+ default: other
+ cmd_fw_names:
+ hidden: false
+ immutable: false
+ type: list
+ description: Comma Delimited List of Names for ServiceInstance VMs
+ default:
+ - ZRDM2FCMD01CMD001
+ - ' ZRDM2FCMD01CMD002'
+ - ' ZRDM2FCMD01CMD003'
+ - ' ZRDM2FCMD01CMD004'
+ entry_schema:
+ type: string
+ cmd_fw_shc_max_retries:
+ hidden: false
+ immutable: false
+ type: integer
+ description: max_retries for the ServiceHealthCheck
+ default: 2
+ cmd_fw_shc_url_path:
+ hidden: false
+ immutable: false
+ type: string
+ description: url_path for the ServiceHealthCheck
+ default: local-ip
+ node_templates:
+ nodejs:
+ type: tosca.nodes.WebServer
+ requirements:
+ - host:
+ node: app_server
+ mongo_db:
+ type: tosca.nodes.Database
+ requirements:
+ - host: mongo_dbms
+ mongo_dbms:
+ type: tosca.nodes.DBMS
+ properties:
+ root_password:
+ get_input: nimbus_hsl_interface_type
+ # fixed_ips:
+ # - ip_address:
+ # get_input: app2_int_ota_apps_ip_1
+ # subnet_id:
+ # get_input: pmaa_dpu_subnet_id
+ # - ip_address:
+ # get_input: app2_int_ota_apps_ip_3
+ # subnet_id:
+ # get_input: pmaa_dpu_subnet_id
+ # name:
+ # get_input:
+ # - mnsoamvfw_names
+ # - get_input: index
+ # metadata:
+ # pmaa.sb_nic:
+ # address:
+ # get_input: pmaa_dpu_fixed_ip
+ # cidr:
+ # get_input: pmaa_dpu_cidr
+ # gateway:
+ # get_input: pmaa_dpu_gateway
+ #port:
+ # get_input: cmd_fw_shc_max_retries
+ requirements:
+ - host: mongo_server
+ app_server:
+ type: tosca.nodes.Compute
+ mongo_server:
+ type: tosca.nodes.Compute
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_nodeEmpty.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_nodeEmpty.yml
new file mode 100644
index 0000000000..3f8f3d15f8
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_nodeEmpty.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_nodeVF.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_nodeVF.yml
new file mode 100644
index 0000000000..4f1966315a
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_nodeVF.yml
@@ -0,0 +1,38 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+ node_templates:
+ my_node:
+ type: org.openecomp.resource.vf.Resource19
+ nodejs:
+ type: tosca.nodes.WebServer
+ requirements:
+ - host:
+ node: app_server
+ mongo_db:
+ type: tosca.nodes.Database
+ requirements:
+ - host: mongo_dbms
+ mongo_dbms:
+ type: tosca.nodes.DBMS
+ requirements:
+ - host: mongo_server
+ app_server:
+ type: tosca.nodes.Compute
+ mongo_server:
+ type: tosca.nodes.Compute
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_notValidNode.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_notValidNode.yml
new file mode 100644
index 0000000000..a4534fc03d
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_notValidNode.yml
@@ -0,0 +1,36 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+ node_templates:
+ nodejs:
+ type: tosca.nodes.KUKU
+ requirements:
+ - host:
+ node: app_server
+ mongo_db:
+ type: tosca.nodes.Database
+ requirements:
+ - host: mongo_dbms
+ mongo_dbms:
+ type: tosca.nodes.DBMS
+ requirements:
+ - host: mongo_server
+ app_server:
+ type: tosca.nodes.Compute
+ mongo_server:
+ type: tosca.nodes.Compute
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_notValidRelationNode.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_notValidRelationNode.yml
new file mode 100644
index 0000000000..f606769387
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_notValidRelationNode.yml
@@ -0,0 +1,36 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+ node_templates:
+ nodejs:
+ type: tosca.nodes.WebServer
+ requirements:
+ - host:
+ node: KUKU
+ mongo_db:
+ type: tosca.nodes.Database
+ requirements:
+ - host: mongo_dbms
+ mongo_dbms:
+ type: tosca.nodes.DBMS
+ requirements:
+ - host: mongo_server
+ app_server:
+ type: tosca.nodes.Compute
+ mongo_server:
+ type: tosca.nodes.Compute
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/normativeTypes/topology_template_sample.yml b/catalog-be/src/test/resources/normativeTypes/topology_template_sample.yml
new file mode 100644
index 0000000000..4b685a4497
--- /dev/null
+++ b/catalog-be/src/test/resources/normativeTypes/topology_template_sample.yml
@@ -0,0 +1,36 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This TOSCA simple profile deploys nodejs, mongodb, each on a separate server
+ with monitoring enabled for nodejs server where a sample nodejs application is running.
+
+topology_template:
+ node_templates:
+ nodejs:
+ type: tosca.nodes.WebServer
+ requirements:
+ - host:
+ node: app_server
+ mongo_db:
+ type: tosca.nodes.Database
+ requirements:
+ - host: mongo_dbms
+ mongo_dbms:
+ type: tosca.nodes.DBMS
+ requirements:
+ - host: mongo_server
+ app_server:
+ type: tosca.nodes.Compute
+ mongo_server:
+ type: tosca.nodes.Compute
+
+
+
+ outputs:
+ nodejs_url:
+ description: URL for the nodejs server, http://<IP>:3000
+ value: { get_attribute: [ app_server, private_address ] }
+ mongodb_url:
+ description: URL for the mongodb server.
+ value: { get_attribute: [ mongo_server, private_address ] }
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/capabilityTypes.yml b/catalog-be/src/test/resources/types/capabilityTypes.yml
new file mode 100644
index 0000000000..58d661b17e
--- /dev/null
+++ b/catalog-be/src/test/resources/types/capabilityTypes.yml
@@ -0,0 +1,148 @@
+tosca.capabilities.Root:
+ description: The TOSCA root Capability Type all other TOSCA base Capability Types derive from
+tosca.capabilities.Attachment:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Node:
+ derived_from: tosca.capabilities.Root
+tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ cpu_frequency:
+ type: scalar-unit.frequency
+ required: false
+ constraints:
+ - greater_or_equal: 0.1 GHz
+ disk_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+ mem_size:
+ type: scalar-unit.size
+ required: false
+ constraints:
+ - greater_or_equal: 0 MB
+tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: PortDef
+ required: false
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ port_name:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ default: PRIVATE
+ initiator:
+ type: string
+ default: source
+ constraints:
+ - valid_values: [ source, target, peer ]
+ ports:
+ type: map
+ required: false
+ constraints:
+ - min_length: 1
+ entry_schema:
+ type: PortSpec
+ attributes:
+ ip_address:
+ type: string
+tosca.capabilities.DatabaseEndpoint:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.Endpoint.Public:
+ derived_from: tosca.capabilities.Endpoint
+ properties:
+ # Change the default network_name to use the first public network found
+ network_name: PUBLIC
+ floating:
+ description: >
+ indicates that the public address should be allocated from a pool of floating IPs that are associated with the network.
+ type: boolean
+ default: false
+ status: experimental
+ dns_name:
+ description: The optional name to register with DNS
+ type: string
+ required: false
+ status: experimental
+tosca.capabilities.Endpoint.Admin:
+ derived_from: tosca.capabilities.Endpoint
+ # Change Endpoint secure indicator to true from its default of false
+ properties:
+ secure: true
+tosca.capabilities.Endpoint.Database:
+ derived_from: tosca.capabilities.Endpoint
+tosca.capabilities.OperatingSystem:
+ derived_from: tosca.capabilities.Root
+ properties:
+ architecture:
+ type: string
+ required: false
+ type:
+ type: string
+ required: false
+ distribution:
+ type: string
+ required: false
+ version:
+ type: version
+ required: false
+tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_instances:
+ type: integer
+ default: 1
+ max_instances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+tosca.capabilities.network.Bindable:
+ derived_from: tosca.capabilities.Node
+
+
+tosca.capabilities.Container.Docker:
+ derived_from: tosca.capabilities.Container
+ properties:
+ version:
+ type: list
+ required: false
+ entry_schema: version
+ publish_all:
+ type: boolean
+ default: false
+ required: false
+ publish_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ expose_ports:
+ type: list
+ entry_schema: PortSpec
+ required: false
+ volumes:
+ type: list
+ entry_schema: string
+ required: false
+tosca.capabilities.network.Linkable:
+ derived_from: tosca.capabilities.Root
+
+
diff --git a/catalog-be/src/test/resources/types/capabilityTypes.zip b/catalog-be/src/test/resources/types/capabilityTypes.zip
new file mode 100644
index 0000000000..4f945a7d1f
--- /dev/null
+++ b/catalog-be/src/test/resources/types/capabilityTypes.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/types/categoryTypes.yml b/catalog-be/src/test/resources/types/categoryTypes.yml
new file mode 100644
index 0000000000..c853f9a52c
--- /dev/null
+++ b/catalog-be/src/test/resources/types/categoryTypes.yml
@@ -0,0 +1,74 @@
+services:
+ Mobility:
+ name: "Mobility"
+ icons: ['mobility']
+ Network_L1_3:
+ name: "Network L1-3"
+ icons: ['network_l_1-3']
+ Network_L4:
+ name: "Network L4"
+ icons: ['network_l_4']
+ VoIP_Call_Control:
+ name: "VoIP Call Control"
+ icons: ['call_controll']
+resources:
+ NetworkLayer23:
+ name: "Network Layer 2-3"
+ subcategories:
+ Router:
+ name: "Router"
+ icons: ['router']
+ Gateway:
+ name: "Gateway"
+ icons: ['gateway']
+ WAN_Connectors:
+ name: "WAN Connectors"
+ icons: ['connector']
+ LAN_Connectors:
+ name: "LAN Connectors"
+ icons: ['connector']
+ NetworkLayer4:
+ name: "Network Layer 4+"
+ subcategories:
+ Common_Network_Resources:
+ name: "Common Network Resources"
+ icons: ['network', 'loadBalancer']
+ ApplicationLayer4:
+ name: "Application Layer 4+"
+ subcategories:
+ Border_Elements:
+ name: "Border Elements"
+ icons: ['borderElement']
+ Application_Servers:
+ name: "Application Servers"
+ icons: ['applicationServer', 'server']
+ Web_Server:
+ name: "Web Server"
+ icons: ['applicationServer', 'server']
+ Call_Control:
+ name: "Call Control"
+ icons: ['call_controll']
+ Media_Servers:
+ name: "Media Servers"
+ icons: ['applicationServer', 'server']
+ Load_Balancer:
+ name: "Load Balancer"
+ icons: ['loadBalancer']
+ Database:
+ name: "Database"
+ icons: ['database']
+ Generic:
+ name: "Generic"
+ subcategories:
+ Infrastructure:
+ name: "Infrastructure"
+ icons: ['objectStorage', 'compute']
+ Abstract:
+ name: "Abstract"
+ icons: ['objectStorage', 'compute']
+ Network_Elements:
+ name: "Network Elements"
+ icons: ['port', 'network', 'router']
+ Database:
+ name: "Database"
+ icons: ['database']
diff --git a/catalog-be/src/test/resources/types/categoryTypes.zip b/catalog-be/src/test/resources/types/categoryTypes.zip
new file mode 100644
index 0000000000..2e08a6b43c
--- /dev/null
+++ b/catalog-be/src/test/resources/types/categoryTypes.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeDeriveFromIntegerWithProperty.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeDeriveFromIntegerWithProperty.yml
new file mode 100644
index 0000000000..7040f8717a
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeDeriveFromIntegerWithProperty.yml
@@ -0,0 +1,8 @@
+integer:
+
+mytypes.phonenumber:
+ derived_from: integer
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeDerivedFromRootNoProperties.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeDerivedFromRootNoProperties.yml
new file mode 100644
index 0000000000..ce72cf52c4
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeDerivedFromRootNoProperties.yml
@@ -0,0 +1,4 @@
+# define a new datatype that derives from existing type and extends it
+mytypes.phonenumber.extended:
+ derived_from: tosca.datatypes.Root
+ description: custom phone number type that extends the basic phonenumber type
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeDuplicateProperty.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeDuplicateProperty.yml
new file mode 100644
index 0000000000..32ed904882
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeDuplicateProperty.yml
@@ -0,0 +1,11 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode1:
+ type: integer
+ number:
+ type: integer
+ areacode1:
+ type: integer
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeForGroup.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeForGroup.yml
new file mode 100644
index 0000000000..e811a1ff30
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeForGroup.yml
@@ -0,0 +1,40 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+string:
+ derived_from: tosca.datatypes.Root
+
+boolean:
+ derived_from: tosca.datatypes.Root
+
+float:
+ derived_from: tosca.datatypes.Root
+
+list:
+ derived_from: tosca.datatypes.Root
+
+map:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false
+ token_type:
+ type: string
+ default: password
+ token:
+ type: string
+ keys:
+ type: map
+ required: false
+ entry_schema:
+ type: string
+ user:
+ type: string
+ required: false \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part1.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part1.yml
new file mode 100644
index 0000000000..b8cdc3bf62
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part1.yml
@@ -0,0 +1,15 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol1:
+ type: string
+ required: false
+ protocol2:
+ type: string
+ required: false
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part2.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part2.yml
new file mode 100644
index 0000000000..5a5be60ee3
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdatePropertyRemoved_part2.yml
@@ -0,0 +1,12 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol1:
+ type: string
+ required: false \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part1.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part1.yml
new file mode 100644
index 0000000000..f7aaff2236
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part1.yml
@@ -0,0 +1,19 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential2:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol2:
+ type: string
+ required: false
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part2.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part2.yml
new file mode 100644
index 0000000000..e1897d1f72
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentDerivedDataType_part2.yml
@@ -0,0 +1,19 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential2:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol2:
+ type: string
+ required: false
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Credential2
+ properties:
+ protocol:
+ type: string
+ required: false \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part1.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part1.yml
new file mode 100644
index 0000000000..d5784d1e61
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part1.yml
@@ -0,0 +1,20 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+map:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ keys:
+ type: map
+ required: false
+ entry_schema:
+ type: integer
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part2.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part2.yml
new file mode 100644
index 0000000000..4a5539dcaa
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentEntryType_part2.yml
@@ -0,0 +1,20 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+map:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ keys:
+ type: map
+ required: false
+ entry_schema:
+ type: string
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part1.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part1.yml
new file mode 100644
index 0000000000..70e4976b2f
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part1.yml
@@ -0,0 +1,22 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential2:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol2:
+ type: string
+ required: false
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: integer
+ required: false
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part2.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part2.yml
new file mode 100644
index 0000000000..71e26a7320
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithDifferentPropertyType_part2.yml
@@ -0,0 +1,19 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential2:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol2:
+ type: string
+ required: false
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part1.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part1.yml
new file mode 100644
index 0000000000..71e26a7320
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part1.yml
@@ -0,0 +1,19 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential2:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol2:
+ type: string
+ required: false
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part2.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part2.yml
new file mode 100644
index 0000000000..b4945fcd7f
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeUpdateWithExistingPropertyNameInAncestor_part2.yml
@@ -0,0 +1,25 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+string:
+ derived_from: tosca.datatypes.Root
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential2:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol2:
+ type: string
+ required: false
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Credential2
+ properties:
+ protocol:
+ type: integer
+ required: false
+ protocol2:
+ type: integer
+ required: false
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypeWithPropertyTypeThisDataType.yml b/catalog-be/src/test/resources/types/datatypes/dataTypeWithPropertyTypeThisDataType.yml
new file mode 100644
index 0000000000..c142b1d2c4
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypeWithPropertyTypeThisDataType.yml
@@ -0,0 +1,9 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: mytypes.phonenumber
diff --git a/catalog-be/src/test/resources/types/datatypes/dataTypes.yml b/catalog-be/src/test/resources/types/datatypes/dataTypes.yml
new file mode 100644
index 0000000000..188c85bef3
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/dataTypes.yml
@@ -0,0 +1,129 @@
+tosca.datatypes.Root:
+ description: The TOSCA root Data Type all other TOSCA base Data Types derive from
+
+integer:
+ derived_from: tosca.datatypes.Root
+
+string:
+ derived_from: tosca.datatypes.Root
+
+boolean:
+ derived_from: tosca.datatypes.Root
+
+float:
+ derived_from: tosca.datatypes.Root
+
+list:
+ derived_from: tosca.datatypes.Root
+
+map:
+ derived_from: tosca.datatypes.Root
+
+tosca.datatypes.Credential:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false
+ token_type:
+ type: string
+ default: password
+ token:
+ type: string
+ keys:
+ type: map
+ required: false
+ entry_schema:
+ type: string
+ user:
+ type: string
+ required: false
+
+tosca.datatypes.TimeInterval:
+ derived_from: tosca.datatypes.Root
+ properties:
+ start_time:
+ type: timestamp
+ required: true
+ end_time:
+ type: timestamp
+ required: true
+
+tosca.datatypes.network.NetworkInfo:
+ derived_from: tosca.datatypes.Root
+ properties:
+ network_name:
+ type: string
+ network_id:
+ type: string
+ addresses:
+ type: list
+ entry_schema:
+ type: string
+
+tosca.datatypes.network.PortInfo:
+ derived_from: tosca.datatypes.Root
+ properties:
+ port_name:
+ type: string
+ port_id:
+ type: string
+ network_id:
+ type: string
+ mac_address:
+ type: string
+ addresses:
+ type: list
+ entry_schema:
+ type: string
+
+tosca.datatypes.network.PortDef:
+ derived_from: integer
+ constraints:
+ - in_range: [ 1, 65535 ]
+
+tosca.datatypes.network.PortSpec:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: true
+ default: tcp
+ constraints:
+ - valid_values: [ udp, tcp, igmp ]
+ target:
+ type: tosca.datatypes.network.PortDef
+ target_range:
+ type: range
+ constraints:
+ - in_range: [ 1, 65535 ]
+ source:
+ type: tosca.datatypes.network.PortDef
+ source_range:
+ type: range
+ constraints:
+ - in_range: [ 1, 65535 ]
+
+tosca.datatypes.complexEntryTypeMapList:
+ derived_from: tosca.datatypes.Root
+ properties:
+ protocol:
+ type: string
+ required: false
+ token_type:
+ type: string
+ default: password
+ token:
+ type: string
+ keys:
+ type: map
+ required: false
+ entry_schema:
+ type: tosca.datatypes.Credential
+ addresses:
+ type: list
+ entry_schema:
+ type: tosca.datatypes.Credential
+ user:
+ type: string
+ required: false
diff --git a/catalog-be/src/test/resources/types/datatypes/derived3levelDataType.yml b/catalog-be/src/test/resources/types/datatypes/derived3levelDataType.yml
new file mode 100644
index 0000000000..f185167b80
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/derived3levelDataType.yml
@@ -0,0 +1,37 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: integer
+
+# define a new datatype that derives from existing type and extends it
+mytypes.phonenumber.extended:
+ derived_from: mytypes.phonenumber
+ description: custom phone number type that extends the basic phonenumber type
+ properties:
+ phone_description:
+ type: string
+ constraints:
+ - max_length: 128
+
+
+
+
+mytypes.phonenumber.extended.extended:
+ derived_from: mytypes.phonenumber.extended
+ description: custom phone number type that extends the basic phonenumber type
+ properties:
+ email:
+ type: string
+ constraints:
+ - max_length: 128
+ complex1:
+ type: mytypes.phonenumber.extended
+ constraints:
+ - max_length: 128
+
+ \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/datatypes/derivedDataType.yml b/catalog-be/src/test/resources/types/datatypes/derivedDataType.yml
new file mode 100644
index 0000000000..1c4ca88ff9
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/derivedDataType.yml
@@ -0,0 +1,19 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: integer
+
+# define a new datatype that derives from existing type and extends it
+mytypes.phonenumber.extended:
+ derived_from: mytypes.phonenumber
+ description: custom phone number type that extends the basic phonenumber type
+ properties:
+ phone_description:
+ type: string
+ constraints:
+ - max_length: 128
diff --git a/catalog-be/src/test/resources/types/datatypes/derivedDataTypeNoProperties.yml b/catalog-be/src/test/resources/types/datatypes/derivedDataTypeNoProperties.yml
new file mode 100644
index 0000000000..2e6b35978e
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/derivedDataTypeNoProperties.yml
@@ -0,0 +1,15 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: integer
+
+# define a new datatype that derives from existing type and extends it
+mytypes.phonenumber.extended:
+ derived_from: mytypes.phonenumber
+ description: custom phone number type that extends the basic phonenumber type
+
diff --git a/catalog-be/src/test/resources/types/datatypes/derivedInvalidDataType.yml b/catalog-be/src/test/resources/types/datatypes/derivedInvalidDataType.yml
new file mode 100644
index 0000000000..d76cc5f219
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/derivedInvalidDataType.yml
@@ -0,0 +1,15 @@
+mytypes.phonenumber2:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: integer
+
+# define a new datatype that derives from existing type and extends it
+mytypes.phonenumber.extended:
+ derived_from: mytypes.phonenumber3
+ description: custom phone number type that extends the basic phonenumber type
+
diff --git a/catalog-be/src/test/resources/types/datatypes/emptyDataType.yml b/catalog-be/src/test/resources/types/datatypes/emptyDataType.yml
new file mode 100644
index 0000000000..6a0318eca4
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/emptyDataType.yml
@@ -0,0 +1,4 @@
+mytypes.phonenumber.empty:
+ description: my phone number datatype
+ properties:
+
diff --git a/catalog-be/src/test/resources/types/datatypes/emptyDataTypeNoPropertiesTag.yml b/catalog-be/src/test/resources/types/datatypes/emptyDataTypeNoPropertiesTag.yml
new file mode 100644
index 0000000000..933c14f778
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/emptyDataTypeNoPropertiesTag.yml
@@ -0,0 +1,3 @@
+mytypes.phonenumber.empty:
+ description: my phone number datatype
+
diff --git a/catalog-be/src/test/resources/types/datatypes/exitingPropertyAtAncestor.yml b/catalog-be/src/test/resources/types/datatypes/exitingPropertyAtAncestor.yml
new file mode 100644
index 0000000000..0e46cc2a98
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/exitingPropertyAtAncestor.yml
@@ -0,0 +1,19 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: integer
+
+# define a new datatype that derives from existing type and extends it
+mytypes.phonenumber.extended:
+ derived_from: mytypes.phonenumber
+ description: custom phone number type that extends the basic phonenumber type
+ properties:
+ areacode:
+ type: integer
+
+
diff --git a/catalog-be/src/test/resources/types/datatypes/invalidDataType.yml b/catalog-be/src/test/resources/types/datatypes/invalidDataType.yml
new file mode 100644
index 0000000000..d5ad5a93ba
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/invalidDataType.yml
@@ -0,0 +1 @@
+mytypes.phonenumber.empty
diff --git a/catalog-be/src/test/resources/types/datatypes/oneDataType.yml b/catalog-be/src/test/resources/types/datatypes/oneDataType.yml
new file mode 100644
index 0000000000..114236fb3e
--- /dev/null
+++ b/catalog-be/src/test/resources/types/datatypes/oneDataType.yml
@@ -0,0 +1,9 @@
+mytypes.phonenumber:
+ description: my phone number datatype
+ properties:
+ countrycode:
+ type: integer
+ areacode:
+ type: integer
+ number:
+ type: integer
diff --git a/catalog-be/src/test/resources/types/interfaceLifecycleTypes.yml b/catalog-be/src/test/resources/types/interfaceLifecycleTypes.yml
new file mode 100644
index 0000000000..1b67118934
--- /dev/null
+++ b/catalog-be/src/test/resources/types/interfaceLifecycleTypes.yml
@@ -0,0 +1,11 @@
+tosca.interfaces.node.lifecycle.Standard:
+ create:
+ description: Standard lifecycle create operation.
+ configure:
+ description: Standard lifecycle configure operation.
+ start:
+ description: Standard lifecycle start operation.
+ stop:
+ description: Standard lifecycle stop operation.
+ delete:
+ description: Standard lifecycle delete operation. \ No newline at end of file
diff --git a/catalog-be/src/test/resources/types/interfaceLifecycleTypes.zip b/catalog-be/src/test/resources/types/interfaceLifecycleTypes.zip
new file mode 100644
index 0000000000..9bcf93ab7d
--- /dev/null
+++ b/catalog-be/src/test/resources/types/interfaceLifecycleTypes.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/valid_vf.csar b/catalog-be/src/test/resources/valid_vf.csar
new file mode 100644
index 0000000000..01bf159071
--- /dev/null
+++ b/catalog-be/src/test/resources/valid_vf.csar
Binary files differ
diff --git a/catalog-be/tarball.xml b/catalog-be/tarball.xml
new file mode 100644
index 0000000000..bedd27660c
--- /dev/null
+++ b/catalog-be/tarball.xml
@@ -0,0 +1,54 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar</format>
+ </formats>
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.artifactId}-${project.version}.war</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>${project.artifactId}-${project.version}.war</destName>
+ </file>
+ <file>
+ <source>src/main/resources/elasticsearch.yml</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>elasticsearch.yml</destName>
+ </file>
+ <!--file>
+ <source>src/main/resources/elasticsearch.properties</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>elasticsearch.properties</destName>
+ </file-->
+ <file>
+ <source>src/main/resources/jetty-ssl.xml</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>jetty-ssl.xml</destName>
+ </file>
+ <!--<file>-->
+ <!--<source>${project.build.directory}/ecompErrorCodes.csv</source>-->
+ <!--<outputDirectory>config</outputDirectory>-->
+ <!--<destName>ecompErrorCodes.csv</destName>-->
+ <!--</file>-->
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <directory>src/main/resources/config</directory>
+ <outputDirectory>config</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/scripts</directory>
+ <outputDirectory>scripts</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/keystore</directory>
+ <outputDirectory>keystore</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/import</directory>
+ <outputDirectory>import</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/catalog-dao/.gitignore b/catalog-dao/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/catalog-dao/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/catalog-dao/pom.xml b/catalog-dao/pom.xml
new file mode 100644
index 0000000000..e5767efeed
--- /dev/null
+++ b/catalog-dao/pom.xml
@@ -0,0 +1,335 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-dao</artifactId>
+
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <properties>
+ <aspectj.version>1.7.4</aspectj.version>
+ <hibernate-validator.version>5.3.4.Final</hibernate-validator.version>
+ <groovy.version>2.3.5</groovy.version>
+ <lucene.version>4.10.2</lucene.version>
+ <mockito.version>1.9.0</mockito.version>
+ <regex.version>3.0.3</regex.version>
+ </properties>
+
+
+ <dependencies>
+ <!-- Common of SD&C -->
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>common-be</artifactId>
+ <version>${common-be.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.elasticsearch</groupId>
+ <artifactId>elasticsearch</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.elasticsearch.plugin</groupId>
+ <artifactId>shield</artifactId>
+ <version>${elastic-search.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+
+ <!-- Elasticsearch delete by query was extracted to plugin: adding this
+ plugin requires updating elasticsearch.yml with: plugin.types: "DeleteByQueryPlugin"
+ and installing the plugin in the server -->
+ <!-- <dependency> -->
+ <!-- <groupId>org.elasticsearch.plugin</groupId> -->
+ <!-- <artifactId>delete-by-query</artifactId> -->
+ <!-- <scope>provided</scope> -->
+ <!-- </dependency> -->
+
+
+ <!-- SPRING -->
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <version>${aspectj.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ <version>${aspectj.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <scope>provided</scope>
+
+ </dependency>
+ <dependency>
+ <groupId>org.apache.lucene</groupId>
+ <artifactId>lucene-regex</artifactId>
+ <version>${regex.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>${mockito.version}</version><!--$NO-MVN-MAN-VER$ -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version><!--$NO-MVN-MAN-VER$ -->
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- TEST -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </exclusion>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <!--<artifactId>asdc-titan-cassandra</artifactId> -->
+ <version>${titan.version}</version>
+ <!--<version>1.0.0-snapshot</version> -->
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- TITAN END -->
+
+ <!-- CASSANDRA -->
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-core</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-mapping</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- CASSANDRA END -->
+
+
+ </dependencies>
+
+
+
+ <build>
+
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings
+ only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>fr.fastconnect</groupId>
+ <artifactId>plantuml-maven-plugin</artifactId>
+ <versionRange>[1.0.0,)</versionRange>
+ <goals>
+ <goal>plant</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+
+
+ <profiles>
+ <profile>
+ <id>Fortify</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.fortify.ps.maven.plugin</groupId>
+ <artifactId>sca-maven-plugin</artifactId>
+ <version>4.30</version>
+ <configuration>
+ <source>1.8</source>
+ <buildId>${project.parent.artifactId}</buildId>
+ <toplevelArtifactId>${project.parent.artifactId}</toplevelArtifactId>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/Account.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/Account.java
new file mode 100644
index 0000000000..6ff717df92
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/Account.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao;
+
+//import com.datastax.driver.mapping.annotations.Column;
+//import com.datastax.driver.mapping.annotations.Frozen;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import com.google.common.base.Objects;
+
+@Table(keyspace = "dstest", name = "accounts")
+public class Account {
+
+ @PartitionKey
+ private String email;
+ private String name;
+ // @Column (name = "addr")
+ // @Frozen
+ // private Address address;
+
+ public Account() {
+ }
+
+ // public Account(String name, String email, Address address) {
+ public Account(String name, String email) {
+ this.name = name;
+ this.email = email;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+ //
+ // public Address getAddress() {
+ // return address;
+ // }
+ //
+ // public void setAddress(Address address) {
+ // this.address = address;
+ // }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Account) {
+ Account that = (Account) other;
+ return Objects.equal(this.name, that.name) && Objects.equal(this.email, that.email);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name, email);
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java
new file mode 100644
index 0000000000..9ab528ca1d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+public enum ActionStatus {
+ OK, CREATED, NO_CONTENT, GENERAL_ERROR, NOT_ALLOWED, MISSING_INFORMATION, RESTRICTED_OPERATION, RESTRICTED_ACCESS, INVALID_CONTENT,
+ // User related
+ USER_ALREADY_EXIST, USER_INACTIVE, USER_NOT_FOUND, USER_HAS_ACTIVE_ELEMENTS, INVALID_EMAIL_ADDRESS, INVALID_ROLE, DELETE_USER_ADMIN_CONFLICT, UPDATE_USER_ADMIN_CONFLICT, CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS, CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS, INVALID_USER_ID,
+ // CapabilityType related
+ CAPABILITY_TYPE_ALREADY_EXIST, MISSING_CAPABILITY_TYPE, REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION, IMPORT_DUPLICATE_REQ_CAP_NAME, IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED,
+ // Resource related
+ RESOURCE_NOT_FOUND, MISSING_DERIVED_FROM_TEMPLATE, PARENT_RESOURCE_NOT_FOUND, INVALID_DEFAULT_VALUE, INVALID_COMPLEX_DEFAULT_VALUE, MULTIPLE_PARENT_RESOURCE_FOUND, INVALID_RESOURCE_PAYLOAD, INVALID_TOSCA_FILE_EXTENSION, INVALID_YAML_FILE, INVALID_TOSCA_TEMPLATE, NOT_RESOURCE_TOSCA_TEMPLATE, NOT_SINGLE_RESOURCE, INVALID_RESOURCE_NAMESPACE, RESOURCE_ALREADY_EXISTS, INVALID_RESOURCE_CHECKSUM, RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES, NO_ASSETS_FOUND,
+ // Component name related
+ COMPONENT_NAME_ALREADY_EXIST, COMPONENT_NAME_EXCEEDS_LIMIT, MISSING_COMPONENT_NAME, INVALID_COMPONENT_NAME,
+ // Component description related
+ COMPONENT_INVALID_DESCRIPTION, COMPONENT_MISSING_DESCRIPTION, COMPONENT_DESCRIPTION_EXCEEDS_LIMIT,
+ // Component icon related
+ COMPONENT_MISSING_ICON, COMPONENT_INVALID_ICON, COMPONENT_ICON_EXCEEDS_LIMIT,
+ // Tag related
+ COMPONENT_MISSING_TAGS, COMPONENT_TAGS_EXCEED_LIMIT, COMPONENT_SINGLE_TAG_EXCEED_LIMIT, INVALID_FIELD_FORMAT, COMPONENT_INVALID_TAGS_NO_COMP_NAME,
+ // user contact related
+ COMPONENT_MISSING_CONTACT, COMPONENT_INVALID_CONTACT,
+ // Vendor related
+ VENDOR_NAME_EXCEEDS_LIMIT, VENDOR_RELEASE_EXCEEDS_LIMIT, INVALID_VENDOR_NAME, INVALID_VENDOR_RELEASE, MISSING_VENDOR_NAME, MISSING_VENDOR_RELEASE,
+ // Category related
+ COMPONENT_MISSING_CATEGORY, COMPONENT_INVALID_CATEGORY, COMPONENT_ELEMENT_INVALID_NAME_FORMAT, COMPONENT_ELEMENT_INVALID_NAME_LENGTH, COMPONENT_CATEGORY_ALREADY_EXISTS, COMPONENT_CATEGORY_NOT_FOUND, COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY, COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY, COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY,
+ // Service API URL
+ INVALID_SERVICE_API_URL,
+ // Property related
+ PROPERTY_ALREADY_EXIST, PROPERTY_NAME_ALREADY_EXISTS, PROPERTY_NOT_FOUND, INVALID_PROPERTY, INVALID_PROPERTY_TYPE, INVALID_PROPERTY_INNER_TYPE,
+ // Attribute related
+ ATTRIBUTE_ALREADY_EXIST, ATTRIBUTE_NOT_FOUND,
+ // State related
+ COMPONENT_IN_CHECKOUT_STATE, ILLEGAL_COMPONENT_STATE, COMPONENT_IN_CERT_IN_PROGRESS_STATE, COMPONENT_SENT_FOR_CERTIFICATION, COMPONENT_VERSION_ALREADY_EXIST, COMPONENT_ALREADY_CHECKED_IN, COMPONENT_CHECKOUT_BY_ANOTHER_USER, COMPONENT_IN_USE, COMPONENT_HAS_NEWER_VERSION, COMPONENT_ALREADY_CERTIFIED, COMPONENT_NOT_READY_FOR_CERTIFICATION, COMPONENT_ARTIFACT_NOT_FOUND, COMPONENT_INSTANCE_NOT_FOUND, COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER, SERVICE_NOT_FOUND, SERVICE_CATEGORY_CANNOT_BE_CHANGED, SERVICE_NAME_CANNOT_BE_CHANGED, SERVICE_ICON_CANNOT_BE_CHANGED, COMPONENT_TOO_MUCH_CATEGORIES, SERVICE_CANNOT_CONTAIN_SUBCATEGORY, RESOURCE_CATEGORY_CANNOT_BE_CHANGED, RESOURCE_NAME_CANNOT_BE_CHANGED, RESOURCE_ICON_CANNOT_BE_CHANGED, RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED, RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED, RESOURCE_TOO_MUCH_SUBCATEGORIES, SERVICE_ICON_EXCEEDS_LIMIT, RESOURCE_INSTANCE_NOT_FOUND, RESOURCE_INSTANCE_BAD_REQUEST, RESOURCE_INSTANCE_MATCH_NOT_FOUND, RESOURCE_INSTANCE_ALREADY_EXIST, RESOURCE_INSTANCE_RELATION_NOT_FOUND, COMPONENT_MISSING_SUBCATEGORY, ARTIFACT_TYPE_NOT_SUPPORTED, MISSING_ARTIFACT_TYPE, ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED, ARTIFACT_EXIST, ARTIFACT_NOT_FOUND, ARTIFACT_INVALID_MD5, MISSING_ARTIFACT_NAME, MISSING_PROJECT_CODE, INVALID_PROJECT_CODE, COMPONENT_MISSING_MANDATORY_ARTIFACTS, LIFECYCLE_TYPE_ALREADY_EXIST, SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION, MISSING_LIFECYCLE_TYPE,
+ // Distribution
+ MISSING_X_ECOMP_INSTANCE_ID, MISSING_PUBLIC_KEY, MISSING_ENV_NAME, DISTRIBUTION_ENV_DOES_NOT_EXIST, MISSING_BODY, ECOMP_RESEND_WITH_BASIC_AUTHENTICATION_CREDENTIALS, ECOMP_COMPONENT_NOT_AUTHORIZED, METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT, REGISTRATION_FAILED, DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE, DISTRIBUTION_ENVIRONMENT_NOT_FOUND, DISTRIBUTION_ENVIRONMENT_INVALID, DISTRIBUTION_ARTIFACT_NOT_FOUND, DISTRIBUTION_REQUESTED_NOT_FOUND, DISTRIBUTION_REQUESTED_FAILED, DISTRIBUTION_NOT_FOUND, ADDITIONAL_INFORMATION_ALREADY_EXISTS, COMPONENT_VERSION_NOT_FOUND, ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED, ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED, ADDITIONAL_INFORMATION_EXCEEDS_LIMIT, ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS, ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS, ADDITIONAL_INFORMATION_NOT_FOUND, SDC_VERSION_NOT_FOUND, MISSING_DATA, EXCEEDS_LIMIT, UNSUPPORTED_ERROR, ARTIFACT_INVALID_TIMEOUT, SERVICE_IS_VNF_CANNOT_BE_CHANGED, RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE, WRONG_ARTIFACT_FILE_EXTENSION, INVALID_YAML, INVALID_XML, INVALID_JSON, INVALID_DEPLOYMENT_ARTIFACT_HEAT, INVALID_HEAT_PARAMETER_TYPE, INVALID_HEAT_PARAMETER_VALUE, DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS, DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS, MISSING_HEAT, MISMATCH_HEAT_VS_HEAT_ENV, CORRUPTED_FORMAT, MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE, INVALID_PARAMS_IN_HEAT_ENV_FILE,
+ // auth
+ AUTH_FAILED_INVALIDE_HEADER, AUTH_FAILED, AUTH_REQUIRED, CONSUMER_ALREADY_EXISTS, INVALID_LENGTH, ECOMP_USER_NOT_FOUND, INVALID_CONTENT_PARAM, INVALID_FILTER_KEY, SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND, VALIDATED_RESOURCE_NOT_FOUND, FOUND_ALREADY_VALIDATED_RESOURCE, FOUND_LIST_VALIDATED_RESOURCES,
+
+ // product
+ PRODUCT_NOT_FOUND, INVALID_GROUP_ASSOCIATION, EMPTY_PRODUCT_CONTACTS_LIST, INVALID_PRODUCT_CONTACT, MISSING_ONE_OF_COMPONENT_NAMES, COMPONENT_PARAMETER_CANNOT_BE_CHANGED,
+
+ // occurrences
+ EMPTY_OCCURRENCES_LIST, INVALID_OCCURRENCES, NOT_TOPOLOGY_TOSCA_TEMPLATE, INVALID_NODE_TEMPLATE,
+
+ // Data type related
+ DATA_TYPE_ALREADY_EXIST, DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, DATA_TYPE_DERIVED_IS_MISSING, DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, DATA_TYPE_DUPLICATE_PROPERTY, DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, DATA_TYPE_CANNOT_HAVE_PROPERTIES, DATA_TYPE_CANNOT_BE_EMPTY, DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST,
+
+ // Policy Type related
+ TARGETS_EMPTY, TARGETS_NON_VALID, POLICY_TYPE_ALREADY_EXIST,
+
+ // Group Type related
+ GROUP_MEMBER_EMPTY, GROUP_TYPE_ALREADY_EXIST,
+
+ // CSAR
+ MISSING_CSAR_UUID, CSAR_INVALID, CSAR_INVALID_FORMAT, CSAR_NOT_FOUND, YAML_NOT_FOUND_IN_CSAR, VSP_ALREADY_EXISTS, RESOURCE_LINKED_TO_DIFFERENT_VSP, RESOURCE_FROM_CSAR_NOT_FOUND, AAI_ARTIFACT_GENERATION_FAILED,
+
+ // Group
+ GROUP_ALREADY_EXIST, GROUP_TYPE_IS_INVALID, GROUP_MISSING_GROUP_TYPE, GROUP_INVALID_COMPONENT_INSTANCE, GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE, GROUP_IS_MISSING, GROUP_ARTIFACT_ALREADY_ASSOCIATED, GROUP_ARTIFACT_ALREADY_DISSOCIATED, GROUP_PROPERTY_NOT_FOUND, INVALID_VF_MODULE_NAME, INVALID_VF_MODULE_NAME_MODIFICATION, INVALID_VF_MODULE_TYPE,
+
+ ARTIFACT_NOT_FOUND_IN_CSAR, ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR, FAILED_RETRIVE_ARTIFACTS_TYPES, ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR, ARTIFACT_NOT_VALID_IN_MASTER, ARTIFACT_NOT_VALID_ENV,
+
+ // cache
+ CONVERT_COMPONENT_ERROR, COMPONENT_NOT_FOUND,
+
+ // Inputs
+ INPUT_IS_NOT_CHILD_OF_COMPONENT
+
+ ;
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/BasicDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/BasicDao.java
new file mode 100644
index 0000000000..c61956313d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/BasicDao.java
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.impl.Neo4jResourceDAO;
+import org.openecomp.sdc.be.dao.neo4j.BatchBuilder;
+import org.openecomp.sdc.be.dao.neo4j.GraphNeighbourTable;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jClient;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jGraphBatchBuilder;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public abstract class BasicDao implements IBasicDAO {
+
+ Neo4jGraphBatchBuilder graphBatchBuilder = new Neo4jGraphBatchBuilder();
+
+ Neo4jClient neo4jClient;
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jResourceDAO.class.getName());
+
+ public <T extends GraphNode> Either<T, Neo4jOperationStatus> create(GraphNeighbourTable graphNeighbourTable,
+ Class<T> clazz, NodeTypeEnum nodeType) {
+
+ if (graphNeighbourTable != null) {
+
+ Either<BatchBuilder, Neo4jOperationStatus> bbResult = graphBatchBuilder
+ .buildBatchBuilderFromTable(graphNeighbourTable);
+
+ if (bbResult.isLeft()) {
+
+ BatchBuilder batchBuilder = bbResult.left().value();
+ // Neo4jOperationStatus neo4jOperationStatus =
+ // neo4jClient.execute(batchBuilder);
+ Either<List<List<GraphElement>>, Neo4jOperationStatus> executeResult = neo4jClient
+ .execute(batchBuilder);
+
+ if (executeResult.isRight()) {
+ return Either.right(executeResult.right().value());
+ }
+
+ T result = null;
+ List<List<GraphElement>> listOfResults = executeResult.left().value();
+ if (listOfResults != null) {
+ for (List<GraphElement> listOfElements : listOfResults) {
+ if (listOfElements != null && false == listOfElements.isEmpty()) {
+ for (GraphElement element : listOfElements) {
+ logger.debug("element {} was returned after running batch operation {}", element, batchBuilder);
+ if (element instanceof GraphNode) {
+ GraphNode neo4jNode = (GraphNode) element;
+ if (NodeTypeEnum.getByName(neo4jNode.getLabel()) == nodeType) {
+ result = clazz.cast(neo4jNode);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return Either.left(result);
+
+ } else {
+ return Either.right(bbResult.right().value());
+ }
+
+ } else {
+ logger.error("The table sent in order to create resource is empty.");
+ return Either.right(Neo4jOperationStatus.BAD_REQUEST);
+ }
+
+ }
+
+ @Override
+ public <T extends GraphNode> Either<T, Neo4jOperationStatus> getNodeData(String uniqueid, Class<T> clazz,
+ NodeTypeEnum nodeTypeEnum) {
+
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueid);
+
+ return getNodeData(filter, clazz, nodeTypeEnum);
+
+ }
+
+ @Override
+ public <T extends GraphNode> Either<T, Neo4jOperationStatus> getNodeData(String keyName, String uniqueid,
+ Class<T> clazz, NodeTypeEnum nodeTypeEnum) {
+
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch(keyName, uniqueid);
+
+ return getNodeData(filter, clazz, nodeTypeEnum);
+
+ }
+
+ private <T extends GraphNode> Either<T, Neo4jOperationStatus> getNodeData(MatchFilter filter, Class<T> clazz,
+ NodeTypeEnum nodeTypeEnum) {
+
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ nodeTypeEnum.getName(), filter);
+
+ if (status.isRight()) {
+ return Either.right(status.right().value());
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null || value.isEmpty()) {
+ return Either.right(Neo4jOperationStatus.NOT_FOUND);
+ } else {
+ return Either.left(clazz.cast(value.get(0)));
+ }
+ }
+ }
+
+ @Override
+ public <T extends GraphNode> Either<List<T>, Neo4jOperationStatus> getNodesData(
+ Map<String, Object> propertiesToMatch, Class<T> clazz, NodeTypeEnum nodeTypeEnum) {
+
+ MatchFilter filter = new MatchFilter();
+ if (propertiesToMatch != null) {
+ for (Entry<String, Object> property : propertiesToMatch.entrySet()) {
+ filter.addToMatch(property.getKey(), property.getValue());
+ }
+ }
+
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ nodeTypeEnum.getName(), filter);
+
+ if (status.isRight()) {
+ return Either.right(status.right().value());
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null || value.isEmpty()) {
+ return Either.right(Neo4jOperationStatus.NOT_FOUND);
+ } else {
+ List<T> list = new ArrayList<T>();
+ for (GraphElement element : value) {
+ list.add(clazz.cast(element));
+ }
+ return Either.left(list);
+ }
+ }
+ }
+
+ public Neo4jClient getNeo4jClient() {
+ return neo4jClient;
+ }
+
+ public void setNeo4jClient(Neo4jClient neo4jClient) {
+ this.neo4jClient = neo4jClient;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericIdDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericIdDAO.java
new file mode 100644
index 0000000000..ae2b236de1
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericIdDAO.java
@@ -0,0 +1,171 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.get.MultiGetItemResponse;
+import org.elasticsearch.action.get.MultiGetResponse;
+import org.elasticsearch.client.Client;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.be.dao.utils.Exceptions;
+import org.openecomp.sdc.exception.IndexingServiceException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public abstract class ESGenericIdDAO implements IGenericIdDAO {
+
+ private static Logger log = LoggerFactory.getLogger(ESGenericIdDAO.class.getName());
+
+ @Resource(name = "elasticsearch-client")
+ private ElasticSearchClient esClient;
+
+ private ObjectMapper jsonMapper = new ObjectMapper();
+ private final Map<String, String> typesToIndices = new HashMap<String, String>();
+
+ public Client getClient() {
+ return this.esClient.getClient();
+ }
+
+ public ElasticSearchClient getEsClient() {
+ return esClient;
+ }
+
+ public ObjectMapper getJsonMapper() {
+ return jsonMapper;
+ }
+
+ public void setJsonMapper(ObjectMapper jsonMapper) {
+ this.jsonMapper = jsonMapper;
+ }
+
+ public void addToIndicesMap(String type, String index) {
+ typesToIndices.put(type, index);
+ }
+
+ public String getIndexForType(String type) {
+ return typesToIndices.get(type);
+ }
+
+ @Override
+ public <T> T findById(String typeName, String id, Class<T> clazz) {
+
+ String indexName = getIndexForType(typeName);
+ GetResponse response = getClient().prepareGet(indexName, typeName, id).execute().actionGet();
+
+ if (response == null || !response.isExists()) {
+ log.debug("Nothing found in index <{}>, type <{}>, for Id <{}>.", indexName, typeName, id);
+ return null;
+ }
+
+ log.debug("Found one in index <{}>, type <{}>, for Id <{}>.", indexName, typeName, id);
+
+ T ret = null;
+ try {
+ ret = (T) jsonMapper.readValue(response.getSourceAsString(), clazz);
+ } catch (IOException e) {
+ Exceptions.convertToRuntimeEx(e);
+ }
+ return ret;
+ }
+
+ @Override
+ public <T> List<T> findByIds(String typeName, Class<T> clazz, String... ids) {
+ String indexName = getIndexForType(typeName);
+ MultiGetResponse response = getClient().prepareMultiGet().add(indexName, typeName, ids).execute().actionGet();
+
+ if (response == null || response.getResponses() == null || response.getResponses().length == 0) {
+ log.debug("Nothing found in index <{}>, type <{}>, for Ids <{}>.", indexName, typeName,
+ Arrays.toString(ids));
+ return null;
+ }
+
+ List<T> result = new ArrayList<>();
+ for (MultiGetItemResponse getItemResponse : response.getResponses()) {
+ if (getItemResponse.getResponse().isExists()) {
+ T val = null;
+ try {
+ val = jsonMapper.readValue(getItemResponse.getResponse().getSourceAsString(), clazz);
+ result.add(val);
+ } catch (IOException e) {
+ Exceptions.convertToRuntimeEx(e);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ protected void saveResourceData(String typeName, Object data, String id) throws JsonProcessingException {
+ String indexName = getIndexForType(typeName);
+
+ log.debug("ESGenericIdDAO saveResourceData resource indexName: {} typeName is: {}", indexName, typeName);
+
+ String json = getJsonMapper().writeValueAsString(data);
+ log.debug("ESGenericIdDAO saveResourceData resource id is: {}", id);
+ try {
+ getClient().prepareIndex(indexName, typeName, id).setSource(json).setRefresh(true).execute().actionGet();
+ } catch (Exception e) {
+ log.error("failed to write data with id {} to elasticsearch type {}. error: {}", id, typeName,
+ e.getMessage());
+ throw e;
+ }
+ }
+
+ @Override
+ public void delete(String typeName, String id) {
+ assertIdNotNullFor(id, "delete");
+ String indexName = getIndexForType(typeName);
+ getClient().prepareDelete(indexName, typeName, id).setRefresh(true).execute().actionGet();
+ }
+
+ public void deleteIndex(String indexName) {
+ DeleteIndexResponse actionGet = getClient().admin().indices().delete(new DeleteIndexRequest(indexName))
+ .actionGet();
+ if (!actionGet.isAcknowledged()) {
+ log.error("failed to delete index {}", indexName);
+ }
+ }
+
+ private void assertIdNotNullFor(String id, String operation) {
+ if (id == null || id.trim().isEmpty()) {
+ log.error("Null or empty Id is not allowed for operation <" + operation + ">.");
+ throw new IndexingServiceException("Null or empty Id is not allowed for operation <" + operation + ">.");
+ }
+ }
+
+ public static String indexTypeFromClass(Class<?> clazz) {
+ return clazz.getSimpleName().toLowerCase();
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericSearchDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericSearchDAO.java
new file mode 100644
index 0000000000..c24325aefb
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ESGenericSearchDAO.java
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.be.dao.utils.Exceptions;
+
+/**
+ * Elastic search dao that manages search operations.
+ *
+ * @author luc boutier
+ */
+public class ESGenericSearchDAO extends ESGenericIdDAO implements IGenericSearchDAO {
+
+ private static final int MAX_SEARCH_SIZE = 1000;
+
+ @Resource(name = "elasticsearch-client")
+ private ElasticSearchClient esClient;
+
+ @Override
+ public long count(String indexName, String typeName, QueryBuilder query) {
+
+ SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(indexName).setTypes(typeName)
+ .setSize(0);
+ if (query != null) {
+ searchRequestBuilder.setQuery(query);
+ }
+
+ SearchResponse response = searchRequestBuilder.execute().actionGet();
+ if (!somethingFound(response)) {
+ return 0;
+ } else {
+ return response.getHits().getTotalHits();
+ }
+ }
+
+ /**
+ * Convert a SearchResponse into a list of objects (json deserialization.)
+ *
+ * @param searchResponse
+ * The actual search response from elastic-search.
+ * @param clazz
+ * The type of objects to de-serialize.
+ * @return A list of instances that contains de-serialized data.
+ */
+ public <T> List<T> toGetListOfData(SearchResponse searchResponse, Class<T> clazz) {
+ // return null if no data has been found in elastic search.
+ if (!somethingFound(searchResponse)) {
+ return null;
+ }
+
+ List<T> result = new ArrayList<>();
+
+ for (int i = 0; i < searchResponse.getHits().getHits().length; i++) {
+ try {
+ result.add(getJsonMapper().readValue(searchResponse.getHits().getAt(i).getSourceAsString(), clazz));
+ } catch (IOException e) {
+ Exceptions.convertToRuntimeEx(e);
+ }
+ }
+
+ return result;
+ }
+
+ public <T> List<T> doCustomFind(Class<T> clazz, String indexName, String typeName, QueryBuilder query,
+ SortBuilder sortBuilder) {
+
+ List<T> result = new ArrayList<T>();
+ SearchRequestBuilder searchRequestBuilder = getClient().prepareSearch(indexName).setTypes(typeName)
+ .setSize(MAX_SEARCH_SIZE);
+ if (query != null) {
+ searchRequestBuilder.setQuery(query);
+ }
+ if (sortBuilder != null) {
+ searchRequestBuilder.addSort(sortBuilder);
+ }
+ SearchResponse response = searchRequestBuilder.execute().actionGet();
+ if (!somethingFound(response)) {
+ return null;
+ } else {
+ for (int i = 0; i < response.getHits().getHits().length; i++) {
+ String hit = response.getHits().getAt(i).sourceAsString();
+
+ T val = null;
+ try {
+ val = getJsonMapper().readValue(hit, clazz);
+ result.add(val);
+ } catch (IOException e) {
+ Exceptions.convertToRuntimeEx(e);
+ }
+ }
+ return result;
+ }
+ }
+
+ private boolean somethingFound(final SearchResponse searchResponse) {
+ if (searchResponse == null || searchResponse.getHits() == null || searchResponse.getHits().getHits() == null
+ || searchResponse.getHits().getHits().length == 0) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IBasicDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IBasicDAO.java
new file mode 100644
index 0000000000..3a2bfb34e8
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IBasicDAO.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphNeighbourTable;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import fj.data.Either;
+
+public interface IBasicDAO {
+
+ /**
+ * add the content of the graph neighbour table to the graph.
+ *
+ * @param graphNeighbourTable
+ * @param clazz
+ * - the type of the object to be returned
+ * @param nodeType
+ * - label of the node
+ * @return Neo4jNode implementation
+ */
+ public <T extends GraphNode> Either<T, Neo4jOperationStatus> create(GraphNeighbourTable graphNeighbourTable,
+ Class<T> clazz, NodeTypeEnum nodeType);
+
+ /**
+ * return the node data by unique id.
+ *
+ * @param id
+ * - unique id of the node
+ * @param clazz
+ * @param nodeType
+ * @return
+ */
+ public <T extends GraphNode> Either<T, Neo4jOperationStatus> getNodeData(String id, Class<T> clazz,
+ NodeTypeEnum nodeType);
+
+ public <T extends GraphNode> Either<T, Neo4jOperationStatus> getNodeData(String keyName, String id, Class<T> clazz,
+ NodeTypeEnum nodeType);
+
+ public <T extends GraphNode> Either<List<T>, Neo4jOperationStatus> getNodesData(
+ Map<String, Object> propertiesToMatch, Class<T> clazz, NodeTypeEnum nodeTypeEnum);
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ICatalogDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ICatalogDAO.java
new file mode 100644
index 0000000000..b59e956f9c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ICatalogDAO.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.exception.ResourceDAOException;
+
+import fj.data.Either;
+
+public interface ICatalogDAO {
+
+ public static final String TOSCA_ELEMENT_INDEX = "toscaelement";
+ public static final String RESOURCES_INDEX = "resources";
+ public final static String REF_NAME_FIELD = "refName";
+ public final static String REF_VERSION_FIELD = "refVersion";
+ public final static String ARTIFACT_NAME_FIELD = "artifactName";
+
+ void addToIndicesMap(String typeName, String indexName);
+
+ /**
+ * Save an artifact in the DAO layer.
+ *
+ * @param imageData
+ */
+ void writeArtifact(ESArtifactData artifactData) throws ResourceDAOException;
+
+ /**
+ * Get an artifact as a byte array based on the artifact id.
+ *
+ * @param id
+ * The id of the artifact to read.
+ * @param id2
+ * @return The artifact as a byte array.
+ */
+ Either<ESArtifactData, ResourceUploadStatus> getArtifact(String id);
+
+ Either<List<ESArtifactData>, ResourceUploadStatus> getArtifacts(String[] ids);
+
+ /**
+ * Delete the given image.
+ *
+ * @param id
+ * Id of the image to delete.
+ */
+ void deleteArtifact(String id);
+
+ /**
+ * delete all artifacts
+ */
+ void deleteAllArtifacts();
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IElementDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IElementDAO.java
new file mode 100644
index 0000000000..f43bd2303f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IElementDAO.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+
+import fj.data.Either;
+
+public interface IElementDAO {
+
+ public Either<List<GraphElement>, ActionStatus> getAllCategories();
+
+ public Either<List<GraphElement>, ActionStatus> getAllTags();
+
+ public Either<GraphElement, ActionStatus> getCategory(String name);
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IEsHealthCheckDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IEsHealthCheckDao.java
new file mode 100644
index 0000000000..e1ab8a7ba9
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IEsHealthCheckDao.java
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+
+public interface IEsHealthCheckDao {
+
+ public HealthCheckStatus getClusterHealthStatus();
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericIdDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericIdDAO.java
new file mode 100644
index 0000000000..2bc11dfcf4
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericIdDAO.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.util.List;
+
+/**
+ * A DAO that allows accessing data by Id or / and multiple Ids.
+ *
+ * @author Igor Ngouagna
+ */
+public interface IGenericIdDAO {
+
+ /**
+ * Find an instance from the given class.
+ *
+ * @param clazz
+ * The class of the object to find.
+ * @param id
+ * The id of the object.
+ * @return The object that has the given id or null if no object matching
+ * the request is found.
+ */
+ <T> T findById(String typeName, String id, Class<T> clazz);
+
+ /**
+ * Find instances by id
+ *
+ * @param clazz
+ * The class for which to find an instance.
+ * @param ids
+ * array of id of the data to find.
+ * @return List of Objects that has the given ids or empty list if no object
+ * matching the request is found.
+ */
+ <T> List<T> findByIds(String typeName, Class<T> clazz, String... ids);
+
+ /**
+ * Delete an instance from the given class.
+ *
+ * @param clazz
+ * The class of the object to delete.
+ * @param id
+ * The id of the object to delete.
+ */
+ void delete(String typeName, String id);
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericSearchDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericSearchDAO.java
new file mode 100644
index 0000000000..1e0cdace2c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IGenericSearchDAO.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import org.elasticsearch.index.query.QueryBuilder;
+
+/**
+ * A Dao that supports search and/or filter based queries.
+ *
+ * @author luc boutier
+ */
+public interface IGenericSearchDAO extends IGenericIdDAO {
+
+ /**
+ * Get the index in which a class belongs.
+ *
+ * @param clazz
+ * The class for which to get the index.
+ * @return The name of the index in which the class lies.
+ */
+ String getIndexForType(String type);
+
+ long count(String indexName, String typeName, QueryBuilder query);
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IPropertyDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IPropertyDAO.java
new file mode 100644
index 0000000000..17bded535c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IPropertyDAO.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+public interface IPropertyDAO extends IBasicDAO {
+
+ // Either<PropertyData, Neo4jOperationStatus>
+ // createPropertyData(GraphNeighbourTable graphNeighbourTable);
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IResourceDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IResourceDAO.java
new file mode 100644
index 0000000000..47bbcbcb11
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IResourceDAO.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.Neo4jClient;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+
+import fj.data.Either;
+
+public interface IResourceDAO extends IBasicDAO {
+
+ Either<ResourceMetadataData, Neo4jOperationStatus> getResourceData(String id);
+
+ // Either<ResourceData, Neo4jOperationStatus>
+ // createResourceData(GraphNeighbourTable graphNeighbourTable);
+
+ /**
+ * the method retrieves all the resources according to the supplied
+ * properties, if none or null is supplied all the resources will be
+ * returned.
+ *
+ * @param propertiesToMatch
+ * a map of properties to match.
+ * @return
+ */
+ Either<List<ResourceMetadataData>, Neo4jOperationStatus> getAllResourcesData(Map<String, Object> propertiesToMatch);
+
+ // ActionStatus updateUserData(UserData userData);
+ //
+ // ActionStatus deleteUserData(String id);
+
+ void setNeo4jClient(Neo4jClient client);
+
+ Either<Integer, Neo4jOperationStatus> getNumberOfResourcesByName(String name);
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IUsersDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IUsersDAO.java
new file mode 100644
index 0000000000..441e8a9010
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/IUsersDAO.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+import org.openecomp.sdc.be.resources.data.UserData;
+
+import fj.data.Either;
+
+public interface IUsersDAO {
+
+ Either<UserData, ActionStatus> getUserData(String id);
+
+ ActionStatus saveUserData(UserData userData);
+
+ ActionStatus updateUserData(UserData userData);
+
+ ActionStatus deleteUserData(String id);
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ResourceUploadStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ResourceUploadStatus.java
new file mode 100644
index 0000000000..88ef319657
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ResourceUploadStatus.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.api;
+
+public enum ResourceUploadStatus {
+ OK, ALREADY_EXIST, NOT_EXIST, ERROR, COMPONENT_NOT_EXIST, SERVICE_NOT_EXIST
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ArtifactCassandraDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ArtifactCassandraDao.java
new file mode 100644
index 0000000000..36d9b60eb5
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ArtifactCassandraDao.java
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.MappingManager;
+
+import fj.data.Either;
+
+@Component("artifact-cassandra-dao")
+public class ArtifactCassandraDao extends CassandraDao {
+
+ private static Logger logger = LoggerFactory.getLogger(ArtifactCassandraDao.class.getName());
+
+ public ArtifactCassandraDao() {
+ super();
+
+ }
+
+ @PostConstruct
+ public void init() {
+ String keyspace = AuditingTypesConstants.ARTIFACT_KEYSPACE;
+ if (client.isConnected()) {
+ Either<ImmutablePair<Session, MappingManager>, CassandraOperationStatus> result = client.connect(keyspace);
+ if (result.isLeft()) {
+ session = result.left().value().left;
+ manager = result.left().value().right;
+ logger.info("** ArtifactCassandraDao created");
+ } else {
+ logger.info("** ArtifactCassandraDao failed");
+ throw new RuntimeException("Artifact keyspace [" + keyspace + "] failed to connect with error : "
+ + result.right().value());
+ }
+ } else {
+ logger.info("** Cassandra client isn't connected");
+ logger.info("** ArtifactCassandraDao created, but not connected");
+ }
+ }
+
+ public CassandraOperationStatus saveArtifact(ESArtifactData artifact) {
+ return client.save(artifact, ESArtifactData.class, manager);
+ }
+
+ public Either<ESArtifactData, CassandraOperationStatus> getArtifact(String artifactId) {
+ return client.getById(artifactId, ESArtifactData.class, manager);
+ }
+
+ public CassandraOperationStatus deleteArtifact(String artifactId) {
+ return client.delete(artifactId, ESArtifactData.class, manager);
+ }
+
+ /**
+ * ---------for use in JUnit only--------------- the method deletes all the
+ * tables in the audit keyspace
+ *
+ * @return the status of the last failed operation or ok if all the deletes
+ * were successful
+ */
+ public CassandraOperationStatus deleteAllArtifacts() {
+ logger.info("cleaning all artifacts.");
+ String query = "truncate sdcArtifact.resources;";
+ try {
+ session.execute(query);
+ } catch (Exception e) {
+ logger.debug("Failed to clean artifacts", e);
+ return CassandraOperationStatus.GENERAL_ERROR;
+ }
+ logger.info("cleaning all artifacts finished succsesfully.");
+ return CassandraOperationStatus.OK;
+ }
+
+ /**
+ * the method checks if the given table is empty in the artifact keyspace
+ *
+ * @param tableName
+ * the name of the table we want to check
+ * @return true if the table is empty
+ */
+ public Either<Boolean, CassandraOperationStatus> isTableEmpty(String tableName) {
+ return super.isTableEmpty(tableName);
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditAccessor.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditAccessor.java
new file mode 100644
index 0000000000..c9262909bf
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditAccessor.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import org.openecomp.sdc.be.resources.data.auditing.DistributionDeployEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionNotificationEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionStatusEvent;
+import org.openecomp.sdc.be.resources.data.auditing.ResourceAdminEvent;
+
+import com.datastax.driver.mapping.Result;
+import com.datastax.driver.mapping.annotations.Accessor;
+import com.datastax.driver.mapping.annotations.Param;
+import com.datastax.driver.mapping.annotations.Query;
+
+@Accessor
+public interface AuditAccessor {
+
+ // ***** distributionstatusevent table
+ @Query("SELECT * FROM sdcAudit.distributionstatusevent WHERE DID = :did AND ACTION = 'DStatus' ALLOW FILTERING")
+ Result<DistributionStatusEvent> getListOfDistributionStatuses(@Param("did") String did);
+
+ // ***** resourceadminevent table
+ @Query("SELECT * FROM sdcAudit.resourceadminevent WHERE SERVICE_INSTANCE_ID = :serviceInstanceId AND ACTION = 'DRequest' ALLOW FILTERING")
+ Result<ResourceAdminEvent> getServiceDistributionStatus(@Param("serviceInstanceId") String serviceInstanceId);
+
+ @Query("SELECT * FROM sdcAudit.resourceadminevent WHERE SERVICE_INSTANCE_ID = :serviceInstanceId ")
+ Result<ResourceAdminEvent> getByServiceInstanceId(@Param("serviceInstanceId") String serviceInstanceId);
+
+ @Query("SELECT * FROM sdcAudit.resourceadminevent WHERE SERVICE_INSTANCE_ID = :serviceInstanceId AND PREV_VERSION = :prevVersion ALLOW FILTERING")
+ Result<ResourceAdminEvent> getAuditByServiceIdAndPrevVersion(@Param("serviceInstanceId") String serviceInstanceId,
+ @Param("prevVersion") String prevVersion);
+
+ @Query("SELECT * FROM sdcAudit.resourceadminevent WHERE DID = :did AND ACTION = :action ALLOW FILTERING")
+ Result<ResourceAdminEvent> getDistributionRequest(@Param("did") String did, @Param("action") String action);
+
+ @Query("SELECT * FROM sdcAudit.resourceadminevent WHERE SERVICE_INSTANCE_ID = :serviceInstanceId AND CURR_VERSION = :currVersion ALLOW FILTERING")
+ Result<ResourceAdminEvent> getAuditByServiceIdAndCurrVersion(@Param("serviceInstanceId") String serviceInstanceId,
+ @Param("currVersion") String currVersion);
+
+ // ***** distributiondeployevent table
+ @Query("SELECT * FROM sdcAudit.distributiondeployevent WHERE SERVICE_INSTANCE_ID = :serviceInstanceId AND ACTION = 'DResult' ALLOW FILTERING")
+ Result<DistributionDeployEvent> getServiceDistributionDeploy(@Param("serviceInstanceId") String serviceInstanceId);
+
+ @Query("SELECT * FROM sdcAudit.distributiondeployevent WHERE DID = :did AND ACTION = :action AND STATUS = :status ALLOW FILTERING")
+ Result<DistributionDeployEvent> getDistributionDeployByStatus(@Param("did") String did,
+ @Param("action") String action, @Param("status") String status);
+
+ // ***** distributionnotificationevent table
+ @Query("SELECT * FROM sdcAudit.distributionnotificationevent WHERE SERVICE_INSTANCE_ID = :serviceInstanceId AND ACTION = 'DNotify' ALLOW FILTERING")
+ Result<DistributionNotificationEvent> getServiceDistributionNotify(
+ @Param("serviceInstanceId") String serviceInstanceId);
+
+ @Query("SELECT * FROM sdcAudit.distributionnotificationevent WHERE DID = :did AND ACTION = :action ALLOW FILTERING")
+ Result<DistributionNotificationEvent> getDistributionNotify(@Param("did") String did,
+ @Param("action") String action);
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditCassandraDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditCassandraDao.java
new file mode 100644
index 0000000000..73b55b909c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/AuditCassandraDao.java
@@ -0,0 +1,383 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.schema.Table;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingGenericEvent;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionDeployEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionNotificationEvent;
+import org.openecomp.sdc.be.resources.data.auditing.DistributionStatusEvent;
+import org.openecomp.sdc.be.resources.data.auditing.ResourceAdminEvent;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.MappingManager;
+import com.datastax.driver.mapping.Result;
+
+import fj.data.Either;
+
+@Component("audit-cassandra-dao")
+public class AuditCassandraDao extends CassandraDao {
+
+ private AuditAccessor auditAccessor;
+
+ private static Logger logger = LoggerFactory.getLogger(AuditCassandraDao.class.getName());
+
+ public AuditCassandraDao() {
+ super();
+ }
+
+ @PostConstruct
+ public void init() {
+ String keyspace = AuditingTypesConstants.AUDIT_KEYSPACE;
+ if (client.isConnected()) {
+ Either<ImmutablePair<Session, MappingManager>, CassandraOperationStatus> result = client.connect(keyspace);
+ if (result.isLeft()) {
+ session = result.left().value().left;
+ manager = result.left().value().right;
+ auditAccessor = manager.createAccessor(AuditAccessor.class);
+ logger.info("** AuditCassandraDao created");
+ } else {
+ logger.info("** AuditCassandraDao failed");
+ throw new RuntimeException(
+ "Audit keyspace [" + keyspace + "] failed to connect with error : " + result.right().value());
+ }
+ } else {
+ logger.info("** Cassandra client isn't connected");
+ logger.info("** AuditCassandraDao created, but not connected");
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends AuditingGenericEvent> CassandraOperationStatus saveRecord(T entity) {
+ return client.save(entity, (Class<T>) entity.getClass(), manager);
+ }
+
+ /**
+ *
+ * @param did
+ * @return
+ */
+ public Either<List<DistributionStatusEvent>, ActionStatus> getListOfDistributionStatuses(String did) {
+ List<DistributionStatusEvent> remainingElements = new ArrayList<DistributionStatusEvent>();
+
+ try {
+ Result<DistributionStatusEvent> events = auditAccessor.getListOfDistributionStatuses(did);
+ if (events == null) {
+ logger.debug("not found distribution statuses for did {}", did);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get DistributionStatuses List");
+
+ logger.debug("failed to get distribution statuses for ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public Either<List<DistributionDeployEvent>, ActionStatus> getDistributionDeployByStatus(String did, String action,
+ String status) {
+ List<DistributionDeployEvent> remainingElements = new ArrayList<DistributionDeployEvent>();
+
+ try {
+ Result<DistributionDeployEvent> events = auditAccessor.getDistributionDeployByStatus(did, action, status);
+ if (events == null) {
+ logger.debug("not found distribution statuses for did {}", did);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("get Distribution Deploy By Status");
+
+ logger.debug("failed to get distribution deploy by status for ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public Either<List<ResourceAdminEvent>, ActionStatus> getDistributionRequest(String did, String action) {
+ List<ResourceAdminEvent> remainingElements = new ArrayList<ResourceAdminEvent>();
+
+ try {
+ Result<ResourceAdminEvent> events = auditAccessor.getDistributionRequest(did, action);
+ if (events == null) {
+ logger.debug("not found distribution requests for did {}", did);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("get Distribution request");
+
+ logger.debug("failed to get distribution request for ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public Either<List<DistributionNotificationEvent>, ActionStatus> getDistributionNotify(String did, String action) {
+ List<DistributionNotificationEvent> remainingElements = new ArrayList<DistributionNotificationEvent>();
+
+ try {
+ Result<DistributionNotificationEvent> events = auditAccessor.getDistributionNotify(did, action);
+ if (events == null) {
+ logger.debug("not found distribution notify for did {}", did);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("get Distribution notify");
+
+ logger.debug("failed to get distribution notify for ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public Either<List<ResourceAdminEvent>, ActionStatus> getByServiceInstanceId(String serviceInstanceId) {
+ List<ResourceAdminEvent> remainingElements = new ArrayList<ResourceAdminEvent>();
+
+ try {
+ Result<ResourceAdminEvent> events = auditAccessor.getByServiceInstanceId(serviceInstanceId);
+ if (events == null) {
+ logger.debug("not found audit records for serviceInstanceId {}", serviceInstanceId);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("get Distribution notify");
+
+ logger.debug("failed to get distribution notify for ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ /**
+ *
+ * @param serviceInstanceId
+ * @return
+ */
+ public Either<List<? extends AuditingGenericEvent>, ActionStatus> getServiceDistributionStatusesList(
+ String serviceInstanceId) {
+
+ List<AuditingGenericEvent> resList = new ArrayList<>();
+
+ try {
+ Result<ResourceAdminEvent> resourceAdminEvents = auditAccessor
+ .getServiceDistributionStatus(serviceInstanceId);
+
+ if (resourceAdminEvents != null) {
+ resourceAdminEvents.all().forEach(event -> {
+ event.fillFields();
+ resList.add(event);
+ logger.debug(event.toString());
+ });
+ }
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Service DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Service DistributionStatuses List");
+ logger.debug("failed to get distribution statuses for action {}",
+ AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST.getName(), e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ try {
+ Result<DistributionDeployEvent> distDeployEvents = auditAccessor
+ .getServiceDistributionDeploy(serviceInstanceId);
+ if (distDeployEvents != null) {
+ distDeployEvents.all().forEach(event -> {
+ event.fillFields();
+ resList.add(event);
+ logger.debug(event.toString());
+ });
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Service DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Service DistributionStatuses List");
+ logger.debug("failed to get distribution statuses for action {}",
+ AuditingActionEnum.DISTRIBUTION_DEPLOY.getName(), e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ try {
+ Result<DistributionNotificationEvent> distNotifEvent = auditAccessor
+ .getServiceDistributionNotify(serviceInstanceId);
+ if (distNotifEvent != null) {
+ distNotifEvent.all().forEach(event -> {
+ event.fillFields();
+ resList.add(event);
+ logger.debug(event.toString());
+ });
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Service DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Service DistributionStatuses List");
+ logger.debug("failed to get distribution statuses for action {}",
+ AuditingActionEnum.DISTRIBUTION_NOTIFY.getName(), e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ return Either.left(resList);
+ }
+
+ public Either<List<ResourceAdminEvent>, ActionStatus> getAuditByServiceIdAndPrevVersion(String serviceInstanceId,
+ String prevVersion) {
+ List<ResourceAdminEvent> remainingElements = new ArrayList<ResourceAdminEvent>();
+
+ try {
+ Result<ResourceAdminEvent> events = auditAccessor.getAuditByServiceIdAndPrevVersion(serviceInstanceId,
+ prevVersion);
+ if (events == null) {
+ logger.debug("not found audit records for serviceInstanceId {} andprevVersion {}", serviceInstanceId,
+ prevVersion);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "get Audit By ServiceId And PrevVersion");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("get Audit By ServiceId And PrevVersion");
+
+ logger.debug("failed to getAuditByServiceIdAndPrevVersion ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public Either<List<ResourceAdminEvent>, ActionStatus> getAuditByServiceIdAndCurrVersion(String serviceInstanceId,
+ String currVersion) {
+ List<ResourceAdminEvent> remainingElements = new ArrayList<ResourceAdminEvent>();
+
+ try {
+ Result<ResourceAdminEvent> events = auditAccessor.getAuditByServiceIdAndCurrVersion(serviceInstanceId,
+ currVersion);
+ if (events == null) {
+ logger.debug("not found audit records for serviceInstanceId {} andprevVersion {}", serviceInstanceId,
+ currVersion);
+ return Either.left(remainingElements);
+ }
+ events.all().forEach(event -> {
+ event.fillFields();
+ remainingElements.add(event);
+ logger.debug(event.toString());
+ });
+ return Either.left(remainingElements);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "get Audit By ServiceId And CurrVersion");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("get Audit By ServiceId And CurrVersion");
+
+ logger.debug("failed to getAuditByServiceIdAndPrevVersion ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ /**
+ * the method checks if the given table is empty in the audit keyspace
+ *
+ * @param tableName
+ * the name of the table we want to check
+ * @return true if the table is empty
+ */
+ public Either<Boolean, CassandraOperationStatus> isTableEmpty(String tableName) {
+ return super.isTableEmpty(tableName);
+ }
+
+ /**
+ * ---------for use in JUnit only--------------- the method deletes all the
+ * tables in the audit keyspace
+ *
+ * @return the status of the last failed operation or ok if all the deletes
+ * were successful
+ */
+ public CassandraOperationStatus deleteAllAudit() {
+ logger.info("cleaning all audit tables.");
+ String query = "truncate " + AuditingTypesConstants.AUDIT_KEYSPACE + ".";
+ try {
+ for (Table table : Table.values()) {
+ if (table.getTableDescription().getKeyspace().equals(AuditingTypesConstants.AUDIT_KEYSPACE)) {
+ logger.debug("clean Audit table:{}", table.getTableDescription().getTableName());
+ session.execute(query + table.getTableDescription().getTableName() + ";");
+ logger.debug("clean Audit table:{} was succsesfull", table.getTableDescription().getTableName());
+ }
+ }
+ } catch (Exception e) {
+ logger.error("Failed to clean Audit", e);
+ return CassandraOperationStatus.GENERAL_ERROR;
+ }
+ logger.info("clean all audit finished succsesfully.");
+ return CassandraOperationStatus.OK;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraClient.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraClient.java
new file mode 100644
index 0000000000..10b9b0578c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraClient.java
@@ -0,0 +1,207 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import java.util.List;
+
+import javax.annotation.PreDestroy;
+
+import com.datastax.driver.core.policies.*;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.Mapper;
+import com.datastax.driver.mapping.MappingManager;
+
+import fj.data.Either;
+
+@Component("cassandra-client")
+public class CassandraClient {
+ private static Logger logger = LoggerFactory.getLogger(CassandraClient.class.getName());
+
+ private Cluster cluster;
+ private boolean isConnected;
+
+ public CassandraClient() {
+ super();
+ isConnected = false;
+ List<String> cassandraHosts = null;
+ try {
+ cassandraHosts = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getCassandraHosts();
+ Long reconnectTimeout = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getCassandraConfig().getReconnectTimeout();
+
+ logger.debug("creating cluster to hosts:{} with reconnect timeout:{}", cassandraHosts, reconnectTimeout);
+ Cluster.Builder clusterBuilder = Cluster.builder()
+ .withReconnectionPolicy(new ConstantReconnectionPolicy(reconnectTimeout))
+ .withRetryPolicy(DefaultRetryPolicy.INSTANCE);
+
+ cassandraHosts.forEach(host -> clusterBuilder.addContactPoint(host));
+ enableAuthentication(clusterBuilder);
+ enableSsl(clusterBuilder);
+ setLocalDc(clusterBuilder);
+
+ cluster = clusterBuilder.build();
+ isConnected = true;
+ } catch (Exception e) {
+ logger.info("** CassandraClient isn't connected to {}", cassandraHosts);
+ }
+
+ logger.info("** CassandraClient created");
+ }
+
+ private void setLocalDc(Cluster.Builder clusterBuilder) {
+ String localDataCenter = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getLocalDataCenter();
+ if (localDataCenter != null) {
+ logger.info("localDatacenter was provided, setting Cassndra clint to use datacenter: {} as local.",
+ localDataCenter);
+ LoadBalancingPolicy tokenAwarePolicy = new TokenAwarePolicy(
+ DCAwareRoundRobinPolicy.builder().withLocalDc(localDataCenter).build());
+ clusterBuilder.withLoadBalancingPolicy(tokenAwarePolicy);
+ } else {
+ logger.info(
+ "localDatacenter was provided, the driver will use the datacenter of the first contact point that was reached at initialization");
+ }
+ }
+
+ private void enableSsl(Cluster.Builder clusterBuilder) {
+ boolean ssl = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig().isSsl();
+ if (ssl) {
+ String truststorePath = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getCassandraConfig().getTruststorePath();
+ String truststorePassword = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getCassandraConfig().getTruststorePassword();
+ if (truststorePath == null || truststorePassword == null) {
+ logger.error("ssl is enabled but truststorePath or truststorePassword were not supplied.");
+ } else {
+ System.setProperty("javax.net.ssl.trustStore", truststorePath);
+ System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);
+ clusterBuilder.withSSL();
+ }
+
+ }
+ }
+
+ private void enableAuthentication(Cluster.Builder clusterBuilder) {
+ boolean authenticate = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .isAuthenticate();
+ if (authenticate) {
+ String username = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getUsername();
+ String password = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getPassword();
+ if (username == null || password == null) {
+ logger.error("authentication is enabled but username or password were not supplied.");
+ } else {
+ clusterBuilder.withCredentials(username, password);
+ }
+
+ }
+ }
+
+ /**
+ *
+ * @param keyspace
+ * - key space to connect
+ * @return
+ */
+ public Either<ImmutablePair<Session, MappingManager>, CassandraOperationStatus> connect(String keyspace) {
+ if (cluster != null) {
+ try {
+ Session session = cluster.connect(keyspace);
+ if (session != null) {
+ MappingManager manager = new MappingManager(session);
+ return Either.left(new ImmutablePair<Session, MappingManager>(session, manager));
+ } else {
+ return Either.right(CassandraOperationStatus.KEYSPACE_NOT_CONNECTED);
+ }
+ } catch (Throwable e) {
+ logger.debug("Failed to connect to keyspace [{}], error :", keyspace, e);
+ return Either.right(CassandraOperationStatus.KEYSPACE_NOT_CONNECTED);
+ }
+ }
+ return Either.right(CassandraOperationStatus.CLUSTER_NOT_CONNECTED);
+ }
+
+ public <T> CassandraOperationStatus save(T entity, Class<T> clazz, MappingManager manager) {
+ if (!isConnected) {
+ return CassandraOperationStatus.CLUSTER_NOT_CONNECTED;
+ }
+ try {
+ Mapper<T> mapper = manager.mapper(clazz);
+ mapper.save(entity);
+ } catch (Exception e) {
+ logger.debug("Failed to save entity [{}], error :", entity, e);
+ return CassandraOperationStatus.GENERAL_ERROR;
+ }
+ return CassandraOperationStatus.OK;
+ }
+
+ public <T> Either<T, CassandraOperationStatus> getById(String id, Class<T> clazz, MappingManager manager) {
+ if (!isConnected) {
+ return Either.right(CassandraOperationStatus.CLUSTER_NOT_CONNECTED);
+ }
+ try {
+ Mapper<T> mapper = manager.mapper(clazz);
+ T result = mapper.get(id);
+ if (result == null) {
+ return Either.right(CassandraOperationStatus.NOT_FOUND);
+ }
+ return Either.left(result);
+ } catch (Exception e) {
+ logger.debug("Failed to get by Id [{}], error :", id, e);
+ return Either.right(CassandraOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+ public <T> CassandraOperationStatus delete(String id, Class<T> clazz, MappingManager manager) {
+ if (!isConnected) {
+ return CassandraOperationStatus.CLUSTER_NOT_CONNECTED;
+ }
+ try {
+ Mapper<T> mapper = manager.mapper(clazz);
+ mapper.delete(id);
+ } catch (Exception e) {
+ logger.debug("Failed to delete by id [{}], error :", id, e);
+ return CassandraOperationStatus.GENERAL_ERROR;
+ }
+ return CassandraOperationStatus.OK;
+ }
+
+ public boolean isConnected() {
+ return isConnected;
+ }
+
+ @PreDestroy
+ public void closeClient() {
+ if (isConnected) {
+ cluster.close();
+ }
+ logger.info("** CassandraClient cluster closed");
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraDao.java
new file mode 100644
index 0000000000..1d2eb15354
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraDao.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Statement;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import fj.data.Either;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.MappingManager;
+
+public abstract class CassandraDao {
+
+ private static Logger logger = LoggerFactory.getLogger(CassandraDao.class.getName());
+
+ protected Session session;
+ protected MappingManager manager;
+
+ @Autowired
+ protected CassandraClient client;
+
+ /**
+ * the method checks if the given table is empty under the keyspace the
+ * session was opened to.
+ *
+ * @param tableName
+ * the name of the table we want to check
+ * @return returns true if the table was empty
+ */
+ protected Either<Boolean, CassandraOperationStatus> isTableEmpty(String tableName) {
+
+ Statement select = QueryBuilder.select().countAll().from(tableName).limit(10);
+ try {
+ ResultSet res = session.execute(select);
+ return Either.left((res.one().getLong("count") != 0 ? false : true));
+
+ } catch (Exception e) {
+ logger.debug("Failed check if table is empty", e);
+ return Either.right(CassandraOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraOperationStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraOperationStatus.java
new file mode 100644
index 0000000000..52b71c780b
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraOperationStatus.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+public enum CassandraOperationStatus {
+ OK, CLUSTER_NOT_CONNECTED, KEYSPACE_NOT_CONNECTED, GENERAL_ERROR, NOT_FOUND
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCacheAccessor.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCacheAccessor.java
new file mode 100644
index 0000000000..828072a4e6
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCacheAccessor.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.resources.data.ComponentCacheData;
+
+import com.datastax.driver.mapping.Result;
+import com.datastax.driver.mapping.annotations.Accessor;
+import com.datastax.driver.mapping.annotations.Param;
+import com.datastax.driver.mapping.annotations.Query;
+
+@Accessor
+public interface ComponentCacheAccessor {
+
+ @Query("SELECT * FROM sdccomponent.componentcache WHERE id IN :ids ALLOW FILTERING")
+ Result<ComponentCacheData> getComponents(@Param("ids") List<String> ids);
+
+ @Query("SELECT * FROM sdccomponent.componentcache WHERE id = :id ALLOW FILTERING")
+ Result<ComponentCacheData> getComponent(@Param("id") String id);
+
+ @Query("SELECT id,modification_time,type FROM sdccomponent.componentcache ALLOW FILTERING")
+ Result<ComponentCacheData> getAllComponentIdTimeAndType();
+
+ // @Query("SELECT * FROM sdcartifact.resources LIMIT 2000")
+ // Result<ESArtifactData> getListOfResources();
+
+ // Result<ESArtifactData> getListOfResources(List<String> dids);
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCassandraDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCassandraDao.java
new file mode 100644
index 0000000000..546dff855e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ComponentCassandraDao.java
@@ -0,0 +1,293 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.resources.data.ComponentCacheData;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.MappingManager;
+import com.datastax.driver.mapping.Result;
+
+import fj.data.Either;
+
+@Component("component-cassandra-dao")
+public class ComponentCassandraDao extends CassandraDao {
+
+ private static Logger logger = LoggerFactory.getLogger(ComponentCassandraDao.class.getName());
+
+ public static Integer DEFAULT_FETCH_SIZE = 500;
+
+ private ComponentCacheAccessor componentCacheAccessor;
+
+ public ComponentCassandraDao() {
+ super();
+
+ }
+
+ @PostConstruct
+ public void init() {
+ String keyspace = AuditingTypesConstants.COMPONENT_KEYSPACE;
+ if (client.isConnected()) {
+ Either<ImmutablePair<Session, MappingManager>, CassandraOperationStatus> result = client.connect(keyspace);
+ if (result.isLeft()) {
+ session = result.left().value().left;
+ manager = result.left().value().right;
+ componentCacheAccessor = manager.createAccessor(ComponentCacheAccessor.class);
+ logger.info("** ComponentCassandraDao created");
+ } else {
+ logger.info("** ComponentCassandraDao failed");
+ throw new RuntimeException("Artifact keyspace [" + keyspace + "] failed to connect with error : "
+ + result.right().value());
+ }
+ } else {
+ logger.info("** Cassandra client isn't connected");
+ logger.info("** ComponentCassandraDao created, but not connected");
+ }
+ }
+
+ /**
+ *
+ * @param ids
+ * - list of components unique id
+ * @return
+ */
+ public Either<List<ComponentCacheData>, ActionStatus> getComponents(List<String> ids) {
+
+ List<ComponentCacheData> components = new ArrayList<ComponentCacheData>();
+ if (ids == null || true == ids.isEmpty()) {
+ return Either.left(components);
+ }
+
+ try {
+
+ Result<ComponentCacheData> events = componentCacheAccessor.getComponents(ids);
+ if (events == null) {
+ logger.trace("not found component for ids list of in size {}", ids.size());
+ return Either.left(components);
+ }
+ events.all().forEach(event -> {
+ components.add(event);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Fetch component uid = {} isDirty = {}", event.getId(), event.getIsDirty());
+ }
+ });
+
+ logger.debug("Number of components to fetch was {}. Actually, {} components fetched", ids.size(),
+ components.size());
+
+ return Either.left(components);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("GetComponentsFromCache");
+
+ logger.debug("failed to get components from cache", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public Either<List<ComponentCacheData>, ActionStatus> getAllComponentIdTimeAndType() {
+ try {
+ List<ComponentCacheData> components = new ArrayList<ComponentCacheData>();
+ Result<ComponentCacheData> events = componentCacheAccessor.getAllComponentIdTimeAndType();
+ if (events == null) {
+ logger.trace("no component found ");
+ return Either.left(components);
+ }
+ events.all().forEach(event -> {
+ components.add(event);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Fetch component uid = {} isDirty = {}", event.getId(), event.getIsDirty());
+ }
+ });
+
+ logger.debug("Number of components fetched was {}.", components.size());
+
+ return Either.left(components);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("GetComponentsFromCache");
+
+ logger.debug("failed to get components from cache", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ /**
+ *
+ * @param id
+ * - component unique id
+ * @return
+ */
+ public Either<ComponentCacheData, ActionStatus> getComponent(String id) {
+
+ if (id == null) {
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+
+ try {
+
+ Result<ComponentCacheData> events = componentCacheAccessor.getComponent(id);
+ if (events == null) {
+ logger.trace("not found component for id {}", id);
+ return Either.right(ActionStatus.RESOURCE_NOT_FOUND);
+ }
+
+ ComponentCacheData componentCacheData = events.one();
+ if (componentCacheData != null) {
+ logger.debug("Component with id {} was found. isDirty={}.", componentCacheData.getId(),
+ componentCacheData.getIsDirty());
+ } else {
+ return Either.right(ActionStatus.RESOURCE_NOT_FOUND);
+ }
+ return Either.left(componentCacheData);
+
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("GetComponentFromCache");
+
+ logger.trace("Failed to get component from cache", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public CassandraOperationStatus saveComponent(ComponentCacheData componentCacheData) {
+ return client.save(componentCacheData, ComponentCacheData.class, manager);
+ }
+
+ /**
+ * ---------for use in JUnit only--------------- the method deletes all the
+ * tables in the audit keyspace
+ *
+ * @return the status of the last failed operation or ok if all the deletes
+ * were successful
+ */
+ // public CassandraOperationStatus
+ // deleteAllPartialComponents(ComponentTypeEnum componentTypeEnum) {
+ // logger.info("cleaning all partial components of " + componentTypeEnum);
+ //
+ // String tableName = getTableName(componentTypeEnum);
+ // if (tableName == null) {
+ // BeEcompErrorManager.getInstance().logInvalidInputError("DeletePartialComponentData",
+ // "input type not found " + componentTypeEnum, ErrorSeverity.INFO);
+ // return CassandraOperationStatus.NOT_FOUND;
+ // }
+ // String query = "truncate " + AuditingTypesConstants.COMPONENT_KEYSPACE +
+ // "." + tableName;
+ // try {
+ // session.execute(query);
+ // } catch (Exception e) {
+ // logger.debug("Failed to clean partial components", e);
+ // return CassandraOperationStatus.GENERAL_ERROR;
+ // }
+ // logger.info("cleaning all partial components finished succsesfully.");
+ // return CassandraOperationStatus.OK;
+ // }
+
+ /**
+ * the method checks if the given table is empty in the artifact keyspace
+ *
+ * @param tableName
+ * the name of the table we want to check
+ * @return true if the table is empty
+ */
+ public Either<Boolean, CassandraOperationStatus> isTableEmpty(String tableName) {
+ return super.isTableEmpty(tableName);
+ }
+
+ /**
+ *
+ * @param idToTimestampMap
+ * - list of components unique id
+ * @return
+ */
+ public Either<ImmutablePair<List<ComponentCacheData>, Set<String>>, ActionStatus> getComponents(
+ Map<String, Long> idToTimestampMap) {
+
+ List<ComponentCacheData> components = new ArrayList<ComponentCacheData>();
+ Set<String> notFetchedFromCache = new HashSet<>();
+ ImmutablePair<List<ComponentCacheData>, Set<String>> result = new ImmutablePair<List<ComponentCacheData>, Set<String>>(
+ components, notFetchedFromCache);
+
+ if (idToTimestampMap == null || true == idToTimestampMap.isEmpty()) {
+ return Either.left(result);
+ }
+
+ try {
+
+ Set<String> keySet = idToTimestampMap.keySet();
+ List<String> ids = new ArrayList<>();
+ ids.addAll(keySet);
+ Result<ComponentCacheData> events = componentCacheAccessor.getComponents(ids);
+ if (events == null) {
+ logger.trace("not found component for ids list of in size {}", ids.size());
+ notFetchedFromCache.addAll(idToTimestampMap.keySet());
+ return Either.left(result);
+ }
+ events.all().forEach(event -> {
+ long timeFromCache = event.getModificationTime().getTime();
+ long timeRequested = idToTimestampMap.get(event.getId());
+ if (timeFromCache == timeRequested) {
+ logger.trace("Fetch component uid = {} from cache", event.getId());
+ components.add(event);
+ } else {
+ logger.trace(
+ "Fetch and ignore component uid = {} from cache. Time requested is {} while timestamp in cache is {}",
+ event.getId(), timeRequested, timeFromCache);
+ }
+ });
+
+ logger.debug("Number of components to fetch was {}. Actually, {} components fetched", ids.size(),
+ components.size());
+ List<String> foundComponents = components.stream().map(p -> p.getId()).collect(Collectors.toList());
+ // fetch all ids which was not found in cache/found in cache and not
+ // updated.
+ Set<String> notFoundComponents = idToTimestampMap.keySet().stream()
+ .filter(p -> false == foundComponents.contains(p)).collect(Collectors.toSet());
+
+ notFetchedFromCache.addAll(notFoundComponents);
+
+ return Either.left(result);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("GetComponentsFromCache");
+
+ logger.debug("failed to get components from cache", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+
+ public CassandraOperationStatus deleteComponent(String id) {
+ return client.delete(id, ComponentCacheData.class, manager);
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/ITableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/ITableDescription.java
new file mode 100644
index 0000000000..010c3ee8fc
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/ITableDescription.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+import com.datastax.driver.core.DataType;
+
+public interface ITableDescription {
+
+ public static final String TIMESTAMP_FIELD = "timestamp1";
+ public static final String TIMEBASED_UUID_FIELD = "timebaseduuid";
+ public static final String ID_FIELD = "id";
+
+ public List<ImmutablePair<String, DataType>> primaryKeys();
+
+ public List<ImmutablePair<String, DataType>> clusteringKeys();
+
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription();
+
+ public String getKeyspace();
+
+ public String getTableName();
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/SdcSchemaBuilder.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/SdcSchemaBuilder.java
new file mode 100644
index 0000000000..0f54a25c14
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/SdcSchemaBuilder.java
@@ -0,0 +1,425 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema;
+
+import com.datastax.driver.core.*;
+import com.datastax.driver.core.schemabuilder.Alter;
+import com.datastax.driver.core.schemabuilder.Create;
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import com.datastax.driver.core.schemabuilder.SchemaStatement;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.*;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLContext;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class SdcSchemaBuilder {
+
+ /**
+ * creat key space statment for SimpleStrategy
+ */
+ final static String CREATE_KEYSPACE_SIMPLE_STRATEGY = "CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class':'SimpleStrategy', %s};";
+ /**
+ * creat key space statment for NetworkTopologyStrategy
+ */
+ final static String CREATE_KEYSPACE_NETWORK_TOPOLOGY_STRATEGY = "CREATE KEYSPACE IF NOT EXISTS %s WITH replication = {'class':'NetworkTopologyStrategy', %s};";
+
+ private static Logger log = LoggerFactory.getLogger(SdcSchemaBuilder.class.getName());
+
+ /**
+ * the method creates all keyspaces, tables and indexes in case they do not
+ * already exist. the method can be run multiple times. the method uses the
+ * internal enums and external configuration for its operation
+ *
+ * @return true if the create operation was successful
+ */
+ public static boolean createSchema() {
+ Cluster cluster = null;
+ Session session = null;
+ try {
+ log.info("creating Schema for Cassandra.");
+ cluster = createCluster();
+ if (cluster == null) {
+ return false;
+ }
+ session = cluster.connect();
+ List<KeyspaceMetadata> keyspacesMetadateFromCassandra = cluster.getMetadata().getKeyspaces();
+ if (keyspacesMetadateFromCassandra == null) {
+ log.debug("filed to retrive a list of keyspaces from cassndra");
+ return false;
+ }
+ log.debug("retrived Cassndra metadata.");
+ Map<String, Map<String, List<String>>> cassndraMetadata = parseKeyspaceMetadata(
+ keyspacesMetadateFromCassandra);
+ Map<String, List<ITableDescription>> schemeData = getSchemeData();
+ log.info("creating Keyspaces.");
+ for (String keyspace : schemeData.keySet()) {
+ if (!createKeyspace(keyspace, cassndraMetadata, session)) {
+ return false;
+ }
+ Map<String, List<String>> keyspaceMetadate = cassndraMetadata.get(keyspace);
+ createTables(schemeData.get(keyspace), keyspaceMetadate, session);
+
+ }
+ return true;
+ } catch (Exception e) {
+ log.info("createSchema failed with exception.", e);
+ } finally {
+ if (session != null) {
+ session.close();
+ }
+ if (cluster != null) {
+ cluster.close();
+ }
+
+ }
+
+ return false;
+ }
+
+ public static boolean deleteSchema() {
+ Cluster cluster = null;
+ Session session = null;
+ try {
+ log.info("delete Data from Cassandra.");
+ cluster = createCluster();
+ if (cluster == null) {
+ return false;
+ }
+ session = cluster.connect();
+ List<KeyspaceMetadata> keyspacesMetadateFromCassandra = cluster.getMetadata().getKeyspaces();
+ if (keyspacesMetadateFromCassandra == null) {
+ log.debug("filed to retrive a list of keyspaces from cassndra");
+ return false;
+ }
+ log.debug("retrived Cassndra metadata.");
+ Map<String, Map<String, List<String>>> cassndraMetadata = parseKeyspaceMetadata(
+ keyspacesMetadateFromCassandra);
+ cassndraMetadata.forEach((k, v) -> {
+ if (AuditingTypesConstants.TITAN_KEYSPACE.equals(k)) {
+
+ // session.execute("")
+ } else if (AuditingTypesConstants.ARTIFACT_KEYSPACE.equals(k)) {
+
+ } else if (AuditingTypesConstants.AUDIT_KEYSPACE.equals(k)) {
+
+ }
+ });
+
+ System.out.println(cassndraMetadata);
+ return true;
+ } catch (Exception e) {
+ log.info("deleteSchema failed with exception.", e);
+ } finally {
+ if (session != null) {
+ session.close();
+ }
+ if (cluster != null) {
+ cluster.close();
+ }
+
+ }
+
+ return false;
+ }
+
+ /**
+ * the method creates the cluster object using the supplied cassandra nodes
+ * in the configuration
+ *
+ * @return cluster object our null in case of an invalid configuration
+ */
+ private static Cluster createCluster() {
+ List<String> nodes = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getCassandraHosts();
+ if (nodes == null) {
+ log.info("no nodes were supplied in configuration.");
+ return null;
+ }
+ log.info("connecting to node:{}.", nodes);
+ Cluster.Builder clusterBuilder = Cluster.builder();
+ nodes.forEach(host -> clusterBuilder.addContactPoint(host));
+
+ clusterBuilder.withMaxSchemaAgreementWaitSeconds(60);
+
+ boolean authenticate = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .isAuthenticate();
+ if (authenticate) {
+ String username = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getUsername();
+ String password = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig()
+ .getPassword();
+ if (username == null || password == null) {
+ log.info("authentication is enabled but username or password were not supplied.");
+ return null;
+ }
+ clusterBuilder.withCredentials(username, password);
+ }
+ boolean ssl = ConfigurationManager.getConfigurationManager().getConfiguration().getCassandraConfig().isSsl();
+ if (ssl) {
+ String truststorePath = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getCassandraConfig().getTruststorePath();
+ String truststorePassword = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getCassandraConfig().getTruststorePassword();
+ if (truststorePath == null || truststorePassword == null) {
+ log.info("ssl is enabled but truststorePath or truststorePassword were not supplied.");
+ return null;
+ }
+ System.setProperty("javax.net.ssl.trustStore", truststorePath);
+ System.setProperty("javax.net.ssl.trustStorePassword", truststorePassword);
+ clusterBuilder.withSSL();
+ }
+ return clusterBuilder.build();
+ }
+
+ /**
+ * the method prcess the metadata retrieved from the cassandra for the
+ * creation of a map conting the names of keyspaces tabls and indexes
+ * already defined in the cassandra keyspacename -> tablename -> list of
+ * indexes info
+ *
+ * @param keyspacesMetadata
+ * cassndra mmetadata
+ * @return a map of maps of lists holding parsed info
+ */
+ private static Map<String, Map<String, List<String>>> parseKeyspaceMetadata(
+ List<KeyspaceMetadata> keyspacesMetadata) {
+ Map<String, Map<String, List<String>>> cassndraMetadata = keyspacesMetadata.stream()
+ .collect(Collectors.toMap(keyspaceMetadata -> keyspaceMetadata.getName(),
+ keyspaceMetadata -> keyspaceMetadata.getTables().stream()
+ .collect(Collectors.toMap(tableMetadata -> tableMetadata.getName(),
+ tableMetadata -> tableMetadata.getIndexes().stream()
+ .map(indexMetadata -> indexMetadata.getName())
+ .collect(Collectors.toList())))));
+ return cassndraMetadata;
+ }
+
+ /**
+ * the method builds an index name according to a defined logic
+ * <table>
+ * _<column>_idx
+ *
+ * @param table
+ * table name
+ * @param column
+ * column name
+ * @return string name of the index
+ */
+ private static String createIndexName(String table, String column) {
+ return new StringBuilder().append(table).append("_").append(column).append("_idx").toString();
+ }
+
+ /**
+ * the method creats all the tables and indexes thet do not already exist
+ *
+ * @param iTableDescriptions
+ * a list of table description we want to create
+ * @param keyspaceMetadate
+ * the current tables that exist in the cassandra under this
+ * keyspace
+ * @param session
+ * the session object used for the execution of the query.
+ */
+ private static void createTables(List<ITableDescription> iTableDescriptions,
+ Map<String, List<String>> keyspaceMetadate, Session session) {
+
+ for (ITableDescription tableDescription : iTableDescriptions) {
+ String tableName = tableDescription.getTableName().toLowerCase();
+ Map<String, ImmutablePair<DataType, Boolean>> columnDescription = tableDescription.getColumnDescription();
+ log.info("creating tables:{}.", tableName);
+ if (keyspaceMetadate == null || !keyspaceMetadate.keySet().contains(tableName)) {
+ Create create = SchemaBuilder.createTable(tableDescription.getKeyspace(),
+ tableDescription.getTableName());
+ for (ImmutablePair<String, DataType> key : tableDescription.primaryKeys()) {
+ create.addPartitionKey(key.getLeft(), key.getRight());
+ }
+ if (tableDescription.clusteringKeys() != null) {
+ for (ImmutablePair<String, DataType> key : tableDescription.clusteringKeys()) {
+ create.addClusteringColumn(key.getLeft(), key.getRight());
+ }
+ }
+
+ for (String columnName : columnDescription.keySet()) {
+ create.addColumn(columnName, columnDescription.get(columnName).getLeft());
+ }
+ log.trace("exacuting :{}", create.toString());
+ ResultSet result = session.execute(create);
+ log.info("table:{} created succsesfully.", tableName);
+ } else {
+ log.info("table:{} already exists skiping.", tableName);
+ }
+ List<String> indexNames = (keyspaceMetadate != null ? keyspaceMetadate.get(tableName) : new ArrayList<>());
+ log.info("table:{} creating indexes.", tableName);
+ for (String columnName : columnDescription.keySet()) {
+ String indexName = createIndexName(tableName, columnName).toLowerCase();
+ if (columnDescription.get(columnName).getRight()) {
+ if (!indexNames.contains(indexName)) {
+ SchemaStatement creatIndex = SchemaBuilder.createIndex(indexName)
+ .onTable(tableDescription.getKeyspace(), tableName).andColumn(columnName);
+ log.info("executing :{}", creatIndex.toString());
+ session.execute(creatIndex);
+ log.info("index:{} created succsesfully.", indexName);
+ } else {
+ log.info("index:{} already exists skiping.", indexName);
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * the method create the keyspace in case it does not already exists the
+ * method uses configurtion to select the needed replication strategy
+ *
+ * @param keyspace
+ * name of the keyspace we want to create
+ * @param cassndraMetadata
+ * cassndra metadata
+ * @param session
+ * the session object used for the execution of the query.
+ * @return true in case the operation was successful
+ */
+ private static boolean createKeyspace(String keyspace, Map<String, Map<String, List<String>>> cassndraMetadata,
+ Session session) {
+ List<Configuration.CassandrConfig.KeyspaceConfig> keyspaceConfigList = ConfigurationManager
+ .getConfigurationManager().getConfiguration().getCassandraConfig().getKeySpaces();
+ log.info("creating keyspace:{}.", keyspace);
+ if (!cassndraMetadata.keySet().contains(keyspace)) {
+ Optional<Configuration.CassandrConfig.KeyspaceConfig> keyspaceConfig = keyspaceConfigList.stream()
+ .filter(keyspaceInfo -> keyspace.equalsIgnoreCase(keyspaceInfo.getName())).findFirst();
+ if (keyspaceConfig.isPresent()) {
+ Configuration.CassandrConfig.KeyspaceConfig keyspaceInfo = keyspaceConfig.get();
+ String createKeyspaceQuery = createKeyspaceQuereyString(keyspace, keyspaceInfo);
+ if (createKeyspaceQuery != null) {
+ log.trace("exacuting: {}", createKeyspaceQuery);
+ session.execute(createKeyspaceQuery);
+ log.info("keyspace:{} created.", keyspace);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ log.info(
+ "keyspace:{} not present in configuration, no info on replications is available. operation failed.",
+ keyspace);
+ return false;
+ }
+ } else {
+ log.info("keyspace:{} already exists skipping.", keyspace);
+ return true;
+ }
+ }
+
+ /**
+ * the method retries the schem info from the enums describing the tables
+ *
+ * @return a map of keyspaces to there table info
+ */
+ private static Map<String, List<ITableDescription>> getSchemeData() {
+ Map<String, List<ITableDescription>> tablesByKeyspace = new HashMap<String, List<ITableDescription>>();
+ Table[] tables = Table.values();
+ for (Table table : tables) {
+ String keyspace = table.getTableDescription().getKeyspace().toLowerCase();
+ List<ITableDescription> list = tablesByKeyspace.get(keyspace);
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ list.add(table.getTableDescription());
+ tablesByKeyspace.put(keyspace, list);
+ }
+ return tablesByKeyspace;
+ }
+
+ /**
+ * the methoed creates the query string for the given keyspace the methoed
+ * valides the given data according the the requirments of the replication
+ * strategy SimpleStrategy: "CREATE KEYSPACE IF NOT EXISTS
+ * <keyspaceName></keyspaceName> WITH replication =
+ * {'class':'SimpleStrategy', 'replication_factor':2};" SimpleStrategy:
+ * "CREATE KEYSPACE IF NOT EXISTS <keyspaceName></keyspaceName> WITH
+ * replication = {'class':'NetworkTopologyStrategy', 'dc1' : 2 ,dc2 : 2 };"
+ *
+ * @param keyspace
+ * name of the keyspace we want to create
+ * @param keyspaceInfo
+ * configuration info regurding the replication of the keyspace
+ * @return a querey string for the creation of the keyspace
+ */
+ private static String createKeyspaceQuereyString(String keyspace,
+ Configuration.CassandrConfig.KeyspaceConfig keyspaceInfo) {
+ String query = null;
+ if (ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY.getName()
+ .equalsIgnoreCase(keyspaceInfo.getReplicationStrategy())) {
+ List<String> dcList = keyspaceInfo.getReplicationInfo();
+ if (dcList.size() % 2 != 0) {
+ log.error("the supplied replication info is in valid expected dc1,2,dc2,2 etc received:{}", dcList);
+ return query;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < dcList.size(); i = i + 2) {
+ sb.append("'").append(dcList.get(i)).append("'").append(" : ").append(dcList.get(i + 1));
+ if (i + 2 < dcList.size()) {
+ sb.append(",");
+ }
+ }
+
+ query = String.format(CREATE_KEYSPACE_NETWORK_TOPOLOGY_STRATEGY, keyspace, sb.toString());
+ } else if (ReplicationStrategy.SIMPLE_STRATEGY.getName()
+ .equalsIgnoreCase(keyspaceInfo.getReplicationStrategy())) {
+ List<String> dcList = keyspaceInfo.getReplicationInfo();
+ if (dcList.size() != 1) {
+ log.error("the supplied replication info is in valid expected <number> etc received:{}", dcList);
+ return query;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append("'replication_factor'").append(" : ").append(dcList.get(0));
+ query = String.format(CREATE_KEYSPACE_SIMPLE_STRATEGY, keyspace, sb.toString());
+
+ } else {
+ log.error("the suplied replication Strategy is in valide expacted {}/{} etc recived:{}",
+ ReplicationStrategy.NETWORK_TOPOLOGY_STRATEGY.getName(),
+ ReplicationStrategy.SIMPLE_STRATEGY.getName(), keyspaceInfo.getReplicationStrategy());
+ }
+ return query;
+ }
+
+ public enum ReplicationStrategy {
+ NETWORK_TOPOLOGY_STRATEGY("NetworkTopologyStrategy"), SIMPLE_STRATEGY("SimpleStrategy");
+
+ public String name;
+
+ private ReplicationStrategy(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/Table.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/Table.java
new file mode 100644
index 0000000000..6de40a7117
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/Table.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema;
+
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.ArtifactTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.AuthEventTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.CategoryEventTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.ComponentCacheTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.ConsumerEventTableDefinition;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribDeployEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribDownloadEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribEngineEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribNotifEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribStatusEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.ExternalApiEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.GetCatHierEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.GetUebClusterEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.GetUsersListEventTableDesc;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.ResAdminEventTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.UserAccessEventTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.UserAdminEventTableDescription;
+
+public enum Table {
+
+ ARTIFACT(new ArtifactTableDescription()),
+ USER_ADMIN_EVENT(new UserAdminEventTableDescription()),
+ USER_ACCESS_EVENT(new UserAccessEventTableDescription()),
+ RESOURCE_ADMIN_EVENT(new ResAdminEventTableDescription()),
+ DISTRIBUTION_DOWNLOAD_EVENT(new DistribDownloadEventTableDesc()),
+ DISTRIBUTION_ENGINE_EVENT(new DistribEngineEventTableDesc()),
+ DISTRIBUTION_NOTIFICATION_EVENT(new DistribNotifEventTableDesc()),
+ DISTRIBUTION_STATUS_EVENT(new DistribStatusEventTableDesc()),
+ DISTRIBUTION_DEPLOY_EVENT(new DistribDeployEventTableDesc()),
+ DISTRIBUTION_GET_UEB_CLUSTER_EVENT(new GetUebClusterEventTableDesc()),
+ AUTH_EVENT(new AuthEventTableDescription()),
+ CONSUMER_EVENT(new ConsumerEventTableDefinition()),
+ CATEGORY_EVENT(new CategoryEventTableDescription()),
+ GET_USERS_LIST_EVENT(new GetUsersListEventTableDesc()),
+ GET_CATEGORY_HIERARCHY_EVENT(new GetCatHierEventTableDesc()),
+ EXTERNAL_API_EVENT(new ExternalApiEventTableDesc()),
+ COMPONENT_CACHE(new ComponentCacheTableDescription());
+
+ ITableDescription tableDescription;
+
+ Table(ITableDescription tableDescription) {
+ this.tableDescription = tableDescription;
+ }
+
+ public ITableDescription getTableDescription() {
+ return tableDescription;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ArtifactTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ArtifactTableDescription.java
new file mode 100644
index 0000000000..2e26bd2675
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ArtifactTableDescription.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.*;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class ArtifactTableDescription implements ITableDescription {
+
+ private static final String keyspaceType = "artifact";
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(ID_FIELD, DataType.varchar()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (ArtifactFieldsDescription field : ArtifactFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.ARTIFACT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return "resources";
+ }
+
+ enum ArtifactFieldsDescription {
+ DATA("data", DataType.blob(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ ArtifactFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ return null;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/AuthEventTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/AuthEventTableDescription.java
new file mode 100644
index 0000000000..ef7be5deb7
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/AuthEventTableDescription.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.UserAdminEventTableDescription.UAEFieldsDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.mapping.annotations.Column;
+
+public class AuthEventTableDescription implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (AEFieldsDescription field : AEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.AUTH_EVENT_TYPE;
+ }
+
+ enum AEFieldsDescription {
+
+ URL("url", DataType.varchar(), false), REQUEST_ID("request_id", DataType.varchar(), true), USER("user",
+ DataType.varchar(), false), AUTH_STATUS("auth_status", DataType.varchar(), false), REALM("realm",
+ DataType.varchar(), false), ACTION("action", DataType.varchar(), true), STATUS("status",
+ DataType.varchar(), false), DESC("description", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ AEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/CategoryEventTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/CategoryEventTableDescription.java
new file mode 100644
index 0000000000..efefac57b6
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/CategoryEventTableDescription.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.mapping.annotations.Column;
+
+public class CategoryEventTableDescription implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (CEFieldsDescription field : CEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.CATEGORY_EVENT_TYPE;
+ }
+
+ enum CEFieldsDescription {
+
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESC("description",
+ DataType.varchar(), false), CATEGORY_NAME("category_Name", DataType.varchar(),
+ false), SUB_CATEGORY_NAME("sub_Category_Name", DataType.varchar(), false), GROUPING_NAME(
+ "grouping_name", DataType.varchar(), false), MODIFIER("modifier", DataType.varchar(),
+ false), REQUEST_ID("request_id", DataType.varchar(), false), RESOURCE_TYPE(
+ "resource_type", DataType.varchar(), false), SERVICE_INSTANCE_ID(
+ "service_instance_id", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ CEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ComponentCacheTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ComponentCacheTableDescription.java
new file mode 100644
index 0000000000..6396da5c32
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ComponentCacheTableDescription.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.*;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class ComponentCacheTableDescription implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(ID_FIELD, DataType.varchar()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (PartialComponentFieldsDescription field : PartialComponentFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.COMPONENT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return "componentcache";
+ }
+
+ enum PartialComponentFieldsDescription {
+ DATA("data", DataType.blob(), false), MODIFICATION_TIME("modification_time", DataType.timestamp(), false), TYPE(
+ "type", DataType.varchar(), false), IS_DIRTY("is_dirty", DataType.cboolean(),
+ false), IS_ZIPPED("is_zipped", DataType.cboolean(), false),;
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ PartialComponentFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ return null;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ConsumerEventTableDefinition.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ConsumerEventTableDefinition.java
new file mode 100644
index 0000000000..554a7a7a51
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ConsumerEventTableDefinition.java
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.GetUebClusterEventTableDesc.DEEFieldsDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class ConsumerEventTableDefinition implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DEEFieldsDescription field : DEEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.CONSUMER_EVENT_TYPE;
+ }
+
+ enum DEEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(),
+ false), ECOMP_USER("ecomp_user", DataType.varchar(), false), MODIFIER("modifier", DataType.varchar(),
+ false), REQUEST_ID("request_id", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DEEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDeployEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDeployEventTableDesc.java
new file mode 100644
index 0000000000..5fc1dc8bd4
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDeployEventTableDesc.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribDownloadEventTableDesc.DSEFieldsDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class DistribDeployEventTableDesc implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DSEFieldsDescription field : DSEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE;
+ }
+
+ enum DSEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), true), DESCRIPTION(
+ "description", DataType.varchar(), false), REQUEST_ID("request_id", DataType.varchar(),
+ false), SERVICE_INST_ID("service_instance_id", DataType.varchar(), true), MODIFIER("modifier",
+ DataType.varchar(), false), CURR_VERSION("curr_version", DataType.varchar(),
+ false), DID("did", DataType.varchar(), true), RESOURCE_NAME("resource_name",
+ DataType.varchar(),
+ false), RESOURCE_TYPE("resource_type", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DSEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDownloadEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDownloadEventTableDesc.java
new file mode 100644
index 0000000000..0ed4f55a7b
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribDownloadEventTableDesc.java
@@ -0,0 +1,100 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class DistribDownloadEventTableDesc implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DSEFieldsDescription field : DSEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.DISTRIBUTION_DOWNLOAD_EVENT_TYPE;
+ }
+
+ enum DSEFieldsDescription {
+ REQUEST_ID("request_Id", DataType.varchar(), false), SERVICE_INST_ID("service_Instance_Id", DataType.varchar(),
+ false), ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(),
+ false), DESCRIPTION("description", DataType.varchar(), false), CONSUMER_ID("consumer_Id",
+ DataType.varchar(), false), RESOURCE_URL("resource_URL", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DSEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribEngineEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribEngineEventTableDesc.java
new file mode 100644
index 0000000000..5ce5bffb7c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribEngineEventTableDesc.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class DistribEngineEventTableDesc implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DEEFieldsDescription field : DEEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE;
+ }
+
+ enum DEEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(),
+ false), CONSUMER_ID("consumer_id", DataType.varchar(), false), REQUEST_ID("request_id",
+ DataType.varchar(), true), SERVICE_INST_ID("service_instance_id", DataType.varchar(),
+ false), ROLE("role", DataType.varchar(), false), D_ENV("d_env", DataType.varchar(),
+ false), API_KEY("api_key", DataType.varchar(), false), DSTATUS_TOPIC(
+ "dstatus_topic", DataType.varchar(),
+ false), DNOTIF_TOPIC("dnotif_topic", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DEEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribNotifEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribNotifEventTableDesc.java
new file mode 100644
index 0000000000..96fab66701
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribNotifEventTableDesc.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class DistribNotifEventTableDesc implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DNEFieldsDescription field : DNEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE;
+ }
+
+ enum DNEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(),
+ false), REQUEST_ID("request_id", DataType.varchar(), false), SERVICE_INST_ID("service_instance_id",
+ DataType.varchar(), true), TOPIC_NAME("topic_name", DataType.varchar(), false), MODIFIER(
+ "modifier", DataType.varchar(), false), CURR_STATE("curr_state", DataType.varchar(),
+ false), CURR_VERSION("curr_version", DataType.varchar(), false), DID("did",
+ DataType.varchar(), true), RESOURCE_NAME("resource_name",
+ DataType.varchar(), false), RESOURCE_TYPE("resource_type",
+ DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DNEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribStatusEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribStatusEventTableDesc.java
new file mode 100644
index 0000000000..bc564e3743
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/DistribStatusEventTableDesc.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class DistribStatusEventTableDesc implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DSEFieldsDescription field : DSEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE;
+ }
+
+ enum DSEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(),
+ false), DID("did", DataType.varchar(), true), CONSUMER_ID("consumer_id", DataType.varchar(),
+ false), REQUEST_ID("request_id", DataType.varchar(), false), RESOURCE_URL("resoure_URL",
+ DataType.varchar(), false), SERVICE_INST_ID("service_instance_id", DataType.varchar(),
+ false), TOPIC_NAME("topic_name", DataType.varchar(),
+ false), STATUS_TIME("status_time", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DSEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ExternalApiEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ExternalApiEventTableDesc.java
new file mode 100644
index 0000000000..a67c9ef4ef
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ExternalApiEventTableDesc.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class ExternalApiEventTableDesc implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (EGAEFieldsDescription field : EGAEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE;
+ }
+
+ enum EGAEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(), false), CONSUMER_ID("consumer_id", DataType.varchar(),
+ false), RESOURCE_URL("resource_URL", DataType.varchar(), false), RESOURCE_NAME("resource_name",
+ DataType.varchar(),
+ false), RESOURCE_TYPE("resource_type", DataType.varchar(), false), SERVICE_INST_ID(
+ "service_instance_id", DataType.varchar(),
+ true), MODIFIER("modifier", DataType.varchar(), false), PREV_ARTIFACT_UUID(
+ "prev_artifact_uuid", DataType.varchar(), false), CURR_ARTIFACT_UUID(
+ "curr_artifact_uuid", DataType.varchar(), false), ARTIFACT_DATA(
+ "artifact_data", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ EGAEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetCatHierEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetCatHierEventTableDesc.java
new file mode 100644
index 0000000000..2d1a5c3c75
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetCatHierEventTableDesc.java
@@ -0,0 +1,99 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.GetUsersListEventTableDesc.DEEFieldsDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class GetCatHierEventTableDesc implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DEEFieldsDescription field : DEEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.GET_CATEGORY_HIERARCHY_EVENT_TYPE;
+ }
+
+ enum DEEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(), false), DETAILS("details", DataType.varchar(), false), REQUEST_ID(
+ "request_id", DataType.varchar(), false), MODIFIER("modifier", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DEEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUebClusterEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUebClusterEventTableDesc.java
new file mode 100644
index 0000000000..8ef1f01186
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUebClusterEventTableDesc.java
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.DistribEngineEventTableDesc.DEEFieldsDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class GetUebClusterEventTableDesc implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DEEFieldsDescription field : DEEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE;
+ }
+
+ enum DEEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(),
+ false), CONSUMER_ID("consumer_Id", DataType.varchar(), false), REQUEST_ID("request_Id",
+ DataType.varchar(), false), SERVICE_INST_ID("service_Instance_Id", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DEEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUsersListEventTableDesc.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUsersListEventTableDesc.java
new file mode 100644
index 0000000000..5d94bd3f4d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/GetUsersListEventTableDesc.java
@@ -0,0 +1,99 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.GetUebClusterEventTableDesc.DEEFieldsDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class GetUsersListEventTableDesc implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (DEEFieldsDescription field : DEEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.GET_USERS_LIST_EVENT_TYPE;
+ }
+
+ enum DEEFieldsDescription {
+ ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION(
+ "description", DataType.varchar(), false), DETAILS("details", DataType.varchar(), false), REQUEST_ID(
+ "request_id", DataType.varchar(), false), MODIFIER("modifier", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ DEEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ResAdminEventTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ResAdminEventTableDescription.java
new file mode 100644
index 0000000000..a6f0ba905d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ResAdminEventTableDescription.java
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.mapping.annotations.Column;
+
+public class ResAdminEventTableDescription implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (AEFieldsDescription field : AEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE;
+ }
+
+ enum AEFieldsDescription {
+ REQUEST_ID("request_id", DataType.varchar(), false), SERVICE_INST_ID("service_instance_id", DataType.varchar(),
+ true), INVARIANT_UUID("invariant_UUID", DataType.varchar(), true), ACTION("action", DataType.varchar(),
+ true), STATUS("status", DataType.varchar(), false), DESCRIPTION("description",
+ DataType.varchar(),
+ false), RESOURCE_TYPE("resource_type", DataType.varchar(), false), PREV_VERSION(
+ "prev_version", DataType.varchar(),
+ true), PREV_STATE("prev_state", DataType.varchar(), true), CURR_STATE(
+ "curr_state", DataType.varchar(), false), RESOURCE_NAME("resource_name",
+ DataType.varchar(), false), CURR_VERSION("curr_version",
+ DataType.varchar(), true), MODIFIER("modifier",
+ DataType.varchar(),
+ false), PREV_ARTIFACT_UUID("prev_artifact_UUID",
+ DataType.varchar(),
+ false), CURR__ARTIFACT_UUID(
+ "curr_artifact_UUID",
+ DataType.varchar(),
+ false), ARTIFACT_DATA(
+ "artifact_data",
+ DataType.varchar(),
+ false), DID("did",
+ DataType.varchar(),
+ true), DPREV_STATUS(
+ "dprev_status",
+ DataType.varchar(),
+ false), DCURR_STATUS(
+ "dcurr_status",
+ DataType.varchar(),
+ false), TOSCA_NODE_TYPE(
+ "tosca_node_type",
+ DataType.varchar(),
+ false), COMMENT(
+ "comment",
+ DataType.varchar(),
+ false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ AEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAccessEventTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAccessEventTableDescription.java
new file mode 100644
index 0000000000..7f482ae3ac
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAccessEventTableDescription.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.mapping.annotations.Column;
+
+public class UserAccessEventTableDescription implements ITableDescription {
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (UAEFieldsDescription field : UAEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.USER_ACCESS_EVENT_TYPE;
+ }
+
+ enum UAEFieldsDescription {
+ REQUEST_ID("request_Id", DataType.varchar(), false), USER("USER", DataType.varchar(), false), ACTION("action",
+ DataType.varchar(), true), STATUS("status", DataType.varchar(), false), DESCRIPTION("description",
+ DataType.varchar(),
+ false), SERVICE_INSTANCE_ID("service_instance_id", DataType.varchar(), false);
+
+ @Column
+ private String modifier;
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ UAEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAdminEventTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAdminEventTableDescription.java
new file mode 100644
index 0000000000..dd8df08cdd
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/UserAdminEventTableDescription.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.cassandra.schema.tables;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+import com.datastax.driver.core.DataType;
+
+public class UserAdminEventTableDescription implements ITableDescription {
+
+ @Override
+ public List<ImmutablePair<String, DataType>> primaryKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMEBASED_UUID_FIELD, DataType.timeuuid()));
+ return keys;
+ }
+
+ @Override
+ public List<ImmutablePair<String, DataType>> clusteringKeys() {
+ List<ImmutablePair<String, DataType>> keys = new ArrayList<>();
+ keys.add(new ImmutablePair<String, DataType>(TIMESTAMP_FIELD, DataType.timestamp()));
+ return keys;
+ }
+
+ @Override
+ public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+ Map<String, ImmutablePair<DataType, Boolean>> columns = new HashMap<>();
+
+ for (UAEFieldsDescription field : UAEFieldsDescription.values()) {
+ columns.put(field.getName(), new ImmutablePair<DataType, Boolean>(field.type, field.indexed));
+ }
+
+ return columns;
+ }
+
+ @Override
+ public String getKeyspace() {
+ return AuditingTypesConstants.AUDIT_KEYSPACE;
+ }
+
+ @Override
+ public String getTableName() {
+ return AuditingTypesConstants.USER_ADMIN_EVENT_TYPE;
+ }
+
+ enum UAEFieldsDescription {
+ REQUEST_ID("request_id", DataType.varchar(), false), SERVICE_INST_ID("service_instance_id", DataType.varchar(),
+ false), ACTION("action", DataType.varchar(), true), STATUS("status", DataType.varchar(),
+ false), DESCRIPTION("description", DataType.varchar(), false), MODIFIER("modifier",
+ DataType.varchar(), false), USER_BEFORE("user_before", DataType.varchar(),
+ false), USER_AFTER("user_after", DataType.varchar(), false);
+
+ private String name;
+ private DataType type;
+ private boolean indexed;
+
+ UAEFieldsDescription(String name, DataType type, boolean indexed) {
+ this.name = name;
+ this.type = type;
+ this.indexed = indexed;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataType getType() {
+ return type;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/es/ElasticSearchClient.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/es/ElasticSearchClient.java
new file mode 100644
index 0000000000..1c3ba1107f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/es/ElasticSearchClient.java
@@ -0,0 +1,226 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.es;
+
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.apache.commons.lang.SystemUtils;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequestBuilder;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.client.transport.TransportClient;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.node.Node;
+import org.elasticsearch.node.NodeBuilder;
+import org.elasticsearch.shield.ShieldPlugin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * Prepare the node to work with elastic search.
+ *
+ * @author luc boutier
+ */
+@Component("elasticsearch-client")
+public class ElasticSearchClient {
+
+ private static Logger log = LoggerFactory.getLogger(ElasticSearchClient.class.getName());
+
+ private Node node;
+ private boolean isLocal;
+ private String clusterName;
+ private Client client;
+
+ String serverHost;
+ String serverPort;
+
+ ArrayList<String> nodes = new ArrayList<String>();
+
+ private boolean isTransportClient;
+
+ @PostConstruct
+ public void initialize() throws URISyntaxException {
+
+ URL url = null;
+ Settings settings = null;
+ URL systemResourceElasticsearchPath = ClassLoader.getSystemResource("elasticsearch.yml");
+
+ if (systemResourceElasticsearchPath != null) {
+ log.debug("try to create URI for {}", systemResourceElasticsearchPath.toString());
+ Path classpathConfig = Paths.get(systemResourceElasticsearchPath.toURI());
+ settings = Settings.settingsBuilder().loadFromPath(classpathConfig).build();
+ }
+ String configHome = System.getProperty("config.home");
+ if (configHome != null && false == configHome.isEmpty()) {
+ try {
+ if (SystemUtils.IS_OS_WINDOWS) {
+ url = new URL("file:///" + configHome + "/elasticsearch.yml");
+ } else {
+ url = new URL("file:" + configHome + "/elasticsearch.yml");
+ }
+
+ log.debug("URL {}", url);
+ settings = Settings.settingsBuilder().loadFromPath(Paths.get(url.toURI())).build();
+ } catch (MalformedURLException | URISyntaxException e1) {
+ log.error("Failed to create URL in order to load elasticsearch yml");
+ System.err.println("Failed to create URL in order to load elasticsearch yml from " + configHome);
+ }
+ }
+ if (settings == null) {
+ log.error("Failed to find settings of elasticsearch yml");
+ System.err.println("Failed to create URL in order to load elasticsearch yml from " + configHome);
+ }
+ if (isTransportClient()) {
+ log.info("******* ElasticSearchClient type is Transport Client *****");
+ TransportClient transportClient = TransportClient.builder().addPlugin(ShieldPlugin.class).settings(settings)
+ .build();
+
+ String[] nodesArray = transportClient.settings().getAsArray("transport.client.initial_nodes");
+ for (String host : nodesArray) {
+ int port = 9300;
+
+ // or parse it from the host string...
+ String[] splitHost = host.split(":", 2);
+ if (splitHost.length == 2) {
+ host = splitHost[0];
+ port = Integer.parseInt(splitHost[1]);
+ }
+
+ transportClient.addTransportAddress(new InetSocketTransportAddress(new InetSocketAddress(host, port)));
+
+ }
+ this.client = transportClient;
+ serverHost = Arrays.toString(nodesArray);
+
+ } else {
+ log.info("******* ElasticSearchClient type is Node Client *****");
+ this.node = NodeBuilder.nodeBuilder().settings(settings).client(!isLocal).clusterName(this.clusterName)
+ .local(isLocal).node();
+ this.client = node.client();
+
+ serverHost = this.client.settings().get("discovery.zen.ping.unicast.hosts");
+ if (serverHost == null) {
+ serverHost = "['localhost:9200']";
+ }
+
+ }
+
+ serverPort = this.client.settings().get("http.port");
+ if (serverPort == null) {
+ serverPort = "9200";
+ }
+
+ log.info("Initialized ElasticSearch client for cluster <{}> with nodes: {}", this.clusterName, serverHost);
+ }
+
+ @PreDestroy
+ public void close() {
+ if (client != null) {
+ client.close();
+ }
+ if (node != null) {
+ node.close();
+ }
+ log.info("Closed ElasticSearch client for cluster <" + this.clusterName + ">");
+ }
+
+ /**
+ * Get the elastic search client.
+ *
+ * @return The elastic search client.
+ */
+ public Client getClient() {
+ return this.client;
+ }
+
+ public String getServerHost() {
+ return serverHost;
+ }
+
+ public String getServerPort() {
+ return serverPort;
+ }
+
+ /**
+ * Wait for green status for the given indices.
+ *
+ * @param indices
+ * The indices to wait for.
+ * @return A {@link ClusterHealthResponse} that contains the cluster health
+ * after waiting maximum 5 minutes for green status.
+ */
+ public ClusterHealthResponse waitForGreenStatus(String... indices) {
+ ClusterHealthAction healthAction = ClusterHealthAction.INSTANCE;
+
+ ClusterHealthRequestBuilder builder = healthAction.newRequestBuilder(this.client.admin().cluster());
+ builder.setIndices(indices);
+ builder.setWaitForGreenStatus();
+ builder.setTimeout(TimeValue.timeValueSeconds(30));
+ ClusterHealthResponse response = builder.execute().actionGet();
+ log.debug("getStatus : {}", response.getStatus());
+ log.debug("getActivePrimaryShards : {}", response.getActivePrimaryShards());
+ log.debug("getActiveShards : {}", response.getActiveShards());
+ log.debug("getInitializingShards : {}", response.getInitializingShards());
+ log.debug("getNumberOfDataNodes : {}", response.getNumberOfDataNodes());
+ log.debug("getNumberOfNodes : {}", response.getNumberOfNodes());
+ log.debug("getRelocatingShards : {}", response.getRelocatingShards());
+ log.debug("getUnassignedShards : {}", response.getUnassignedShards());
+ log.debug("getAllValidationFailures : {}", response.getAllValidationFailures());
+ return response;
+ }
+
+ @Value("#{elasticsearchConfig['cluster.name']}")
+ public void setClusterName(final String clusterName) {
+ this.clusterName = clusterName;
+ }
+
+ @Value("#{elasticsearchConfig['elasticSearch.local']}")
+ public void setLocal(final String strIsLocal) {
+ if (strIsLocal != null && !strIsLocal.isEmpty())
+ this.isLocal = Boolean.parseBoolean(strIsLocal);
+ }
+
+ public boolean isTransportClient() {
+ return isTransportClient;
+ }
+
+ @Value("#{elasticsearchConfig['elasticSearch.transportclient']}")
+ public void setTransportClient(final String strIsTransportclient) {
+ if (strIsTransportclient != null && !strIsTransportclient.isEmpty())
+ this.isTransportClient = Boolean.parseBoolean(strIsTransportclient);
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java
new file mode 100644
index 0000000000..bf93c23afc
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java
@@ -0,0 +1,264 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.AdditionalInfoParameterData;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.be.resources.data.AttributeValueData;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.ConsumerData;
+import org.openecomp.sdc.be.resources.data.DataTypeData;
+import org.openecomp.sdc.be.resources.data.GraphNodeLock;
+import org.openecomp.sdc.be.resources.data.GroupData;
+import org.openecomp.sdc.be.resources.data.GroupTypeData;
+import org.openecomp.sdc.be.resources.data.HeatParameterData;
+import org.openecomp.sdc.be.resources.data.HeatParameterValueData;
+import org.openecomp.sdc.be.resources.data.InputValueData;
+import org.openecomp.sdc.be.resources.data.InputsData;
+import org.openecomp.sdc.be.resources.data.InterfaceData;
+import org.openecomp.sdc.be.resources.data.OperationData;
+import org.openecomp.sdc.be.resources.data.PolicyTypeData;
+import org.openecomp.sdc.be.resources.data.ProductMetadataData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.RelationshipInstData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.resources.data.RequirementImplData;
+import org.openecomp.sdc.be.resources.data.ResourceCategoryData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceCategoryData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.UserFunctionalMenuData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.GroupingData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+
+public class GraphElementFactory {
+
+ public static <T extends GraphNode> T createElement(String label, GraphElementTypeEnum type,
+ Map<String, Object> properties, Class<T> clazz) {
+ T element = null;
+
+ if (type.equals(GraphElementTypeEnum.Node)) {
+ element = createNode(label, properties, clazz);
+ }
+ return element;
+ }
+
+ public static GraphNode createElement(String label, GraphElementTypeEnum type, Map<String, Object> properties) {
+ GraphNode element = null;
+
+ if (type.equals(GraphElementTypeEnum.Node)) {
+ element = createNode(label, properties);
+ }
+ return element;
+ }
+
+ public static GraphRelation createRelation(String type, Map<String, Object> properties, GraphNode from,
+ GraphNode to) {
+ GraphRelation element = new GraphRelation(type);
+ RelationEndPoint endPOintFrom = new RelationEndPoint(NodeTypeEnum.getByName(from.getLabel()),
+ from.getUniqueIdKey(), from.getUniqueId());
+ RelationEndPoint endPOintTo = new RelationEndPoint(NodeTypeEnum.getByName(to.getLabel()), to.getUniqueIdKey(),
+ to.getUniqueId());
+ element.setFrom(endPOintFrom);
+ element.setTo(endPOintTo);
+ element.addPropertis(properties);
+ return element;
+ }
+
+ private static GraphNode createNode(String label, Map<String, Object> properties) {
+ GraphNode element = null;
+ NodeTypeEnum type = NodeTypeEnum.getByName(label);
+ if (type != null) {
+ switch (type) {
+ case User:
+ element = new UserData(properties);
+ break;
+ case ResourceCategory:
+ element = new ResourceCategoryData(properties);
+ break;
+ case ServiceCategory:
+ element = new ServiceCategoryData(properties);
+ break;
+ case Tag:
+ element = new TagData(properties);
+ break;
+ case Service:
+ element = new ServiceMetadataData(properties);
+ break;
+ case Resource:
+ element = new ResourceMetadataData(properties);
+ break;
+ case Property:
+ element = new PropertyData(properties);
+ break;
+ case HeatParameter:
+ element = new HeatParameterData(properties);
+ break;
+ case HeatParameterValue:
+ element = new HeatParameterValueData(properties);
+ break;
+ }
+ }
+ return element;
+ }
+
+ private static <T extends GraphNode> T createNode(String label, Map<String, Object> properties, Class<T> clazz) {
+ T element = null;
+ NodeTypeEnum type = NodeTypeEnum.getByName(label);
+ if (type != null) {
+ switch (type) {
+ case User:
+ element = clazz.cast(new UserData(properties));
+ break;
+ case ResourceCategory:
+ element = clazz.cast(new ResourceCategoryData(properties));
+ break;
+ case ServiceCategory:
+ element = clazz.cast(new ServiceCategoryData(properties));
+ break;
+ case ResourceNewCategory:
+ case ServiceNewCategory:
+ case ProductCategory:
+ element = clazz.cast(new CategoryData(properties));
+ break;
+ case ResourceSubcategory:
+ case ProductSubcategory:
+ element = clazz.cast(new SubCategoryData(properties));
+ break;
+ case ProductGrouping:
+ element = clazz.cast(new GroupingData(properties));
+ break;
+ case Tag:
+ element = clazz.cast(new TagData(properties));
+ break;
+ case Service:
+ element = clazz.cast(new ServiceMetadataData(properties));
+ break;
+ case Product:
+ element = clazz.cast(new ProductMetadataData(properties));
+ break;
+ case Resource:
+ element = clazz.cast(new ResourceMetadataData(properties));
+ break;
+ case Attribute:
+ element = clazz.cast(new AttributeData(properties));
+ break;
+ case Property:
+ element = clazz.cast(new PropertyData(properties));
+ break;
+ case CapabilityType:
+ element = clazz.cast(new CapabilityTypeData(properties));
+ break;
+ case Requirement:
+ element = clazz.cast(new RequirementData(properties));
+ break;
+ case RequirementImpl:
+ element = clazz.cast(new RequirementImplData(properties));
+ break;
+ case Capability:
+ element = clazz.cast(new CapabilityData(properties));
+ break;
+ case CapabilityInst:
+ element = clazz.cast(new CapabilityInstData(properties));
+ break;
+ case PropertyValue:
+ element = clazz.cast(new PropertyValueData(properties));
+ break;
+ case AttributeValue:
+ element = clazz.cast(new AttributeValueData(properties));
+ break;
+ case InputValue:
+ element = clazz.cast(new InputValueData(properties));
+ break;
+ case RelationshipType:
+ break;
+ case LockNode:
+ element = clazz.cast(new GraphNodeLock(properties));
+ break;
+ case ArtifactRef:
+ element = clazz.cast(new ArtifactData(properties));
+ break;
+ case Interface:
+ element = clazz.cast(new InterfaceData(properties));
+ break;
+ case InterfaceOperation:
+ element = clazz.cast(new OperationData(properties));
+ break;
+ case Input:
+ element = clazz.cast(new InputsData(properties));
+ break;
+ case ResourceInstance:
+ element = clazz.cast(new ComponentInstanceData(properties));
+ break;
+ case RelationshipInst:
+ element = clazz.cast(new RelationshipInstData(properties));
+ break;
+ case AdditionalInfoParameters:
+ element = clazz.cast(new AdditionalInfoParameterData(properties));
+ break;
+ case ConsumerCredentials:
+ element = clazz.cast(new ConsumerData(properties));
+ break;
+ case HeatParameter:
+ element = clazz.cast(new HeatParameterData(properties));
+ break;
+ case HeatParameterValue:
+ element = clazz.cast(new HeatParameterValueData(properties));
+ break;
+ case DataType:
+ element = clazz.cast(new DataTypeData(properties));
+ break;
+ case Group:
+ element = clazz.cast(new GroupData(properties));
+ break;
+ case GroupType:
+ element = clazz.cast(new GroupTypeData(properties));
+ break;
+ case UserFunctionalMenu:
+ element = clazz.cast(new UserFunctionalMenuData(properties));
+ break;
+ case PolicyType:
+ element = clazz.cast(new PolicyTypeData(properties));
+ break;
+ default:
+ break;
+ }
+
+ }
+ return element;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/ActionEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/ActionEnum.java
new file mode 100644
index 0000000000..13ef58ddf5
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/ActionEnum.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+public enum ActionEnum {
+ Create, Update, Delete, NoAction
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/AdditionalInformationEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/AdditionalInformationEnum.java
new file mode 100644
index 0000000000..ea59ef81ac
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/AdditionalInformationEnum.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+public enum AdditionalInformationEnum {
+ Label, Value, None;
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphEdge.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphEdge.java
new file mode 100644
index 0000000000..4555f9f9ea
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphEdge.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+
+public class GraphEdge {
+
+ private GraphEdgeLabels edgeType;
+
+ private Map<String, Object> properties;
+
+ public GraphEdge() {
+ super();
+ }
+
+ public GraphEdge(GraphEdgeLabels edgeType, Map<String, Object> properties) {
+ super();
+ this.edgeType = edgeType;
+ this.properties = properties;
+ }
+
+ public GraphEdgeLabels getEdgeType() {
+ return edgeType;
+ }
+
+ public void setEdgeType(GraphEdgeLabels edgeType) {
+ this.edgeType = edgeType;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((edgeType == null) ? 0 : edgeType.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ GraphEdge other = (GraphEdge) obj;
+ if (edgeType != other.edgeType)
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "GraphEdge [edgeType=" + edgeType + ", properties=" + properties + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElement.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElement.java
new file mode 100644
index 0000000000..660e28c674
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElement.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+import java.util.Map;
+
+public abstract class GraphElement {
+
+ private GraphElementTypeEnum elementType;
+ private ActionEnum action;
+
+ protected GraphElement(GraphElementTypeEnum elementType) {
+ this.elementType = elementType;
+ this.action = ActionEnum.NoAction;
+ }
+
+ public GraphElementTypeEnum getElementType() {
+ return elementType;
+ }
+
+ public ActionEnum getAction() {
+ return action;
+ }
+
+ public void setAction(ActionEnum action) {
+ this.action = action;
+ }
+
+ public void setElementType(GraphElementTypeEnum elementType) {
+ this.elementType = elementType;
+ }
+
+ public abstract Map<String, Object> toGraphMap();
+
+ @Override
+ public String toString() {
+ return "GraphElement [elementType=" + elementType + ", action=" + action + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElementTypeEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElementTypeEnum.java
new file mode 100644
index 0000000000..0d39b3e39e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphElementTypeEnum.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+public enum GraphElementTypeEnum {
+ Node, Relationship
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphNode.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphNode.java
new file mode 100644
index 0000000000..5da11cb738
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphNode.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.Gson;
+
+public abstract class GraphNode extends GraphElement {
+
+ private static final Gson gson = new Gson();
+
+ private NodeTypeEnum label;
+
+ protected Gson getGson() {
+ return gson;
+ }
+
+ protected GraphNode(NodeTypeEnum label) {
+ super(GraphElementTypeEnum.Node);
+
+ this.label = label;
+ }
+
+ public String getLabel() {
+ return label.getName();
+ }
+
+ public ImmutablePair<String, Object> getKeyValueId() {
+ ImmutablePair<String, Object> keyValue = new ImmutablePair<String, Object>(getUniqueIdKey(), getUniqueId());
+ return keyValue;
+ }
+
+ protected void addIfExists(Map<String, Object> map, GraphPropertiesDictionary property, Object value) {
+ if (value != null) {
+ if (value instanceof List || value instanceof Map) {
+ value = getGson().toJson(value);
+ }
+ map.put(property.getProperty(), value);
+ }
+ }
+
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ public abstract Object getUniqueId();
+
+ @Override
+ public String toString() {
+ return "GraphNode [label=" + label + ", parent: " + super.toString() + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphRelation.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphRelation.java
new file mode 100644
index 0000000000..4dfd1544d4
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/GraphRelation.java
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class GraphRelation extends GraphElement {
+
+ private RelationEndPoint from;
+ private RelationEndPoint to;
+ private String type;
+ private Map<String, Object> properties;
+
+ public GraphRelation() {
+ super(GraphElementTypeEnum.Relationship);
+ properties = new HashMap<String, Object>();
+ }
+
+ public GraphRelation(String type) {
+ super(GraphElementTypeEnum.Relationship);
+ properties = new HashMap<String, Object>();
+ setType(type);
+ }
+
+ public RelationEndPoint getFrom() {
+ return from;
+ }
+
+ public void setFrom(RelationEndPoint from) {
+ this.from = from;
+ }
+
+ public RelationEndPoint getTo() {
+ return to;
+ }
+
+ public void setTo(RelationEndPoint to) {
+ this.to = to;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void addProperty(String property, Object value) {
+ if (property != null && !property.isEmpty() && value != null) {
+ properties.put(property, value);
+ }
+ }
+
+ public void addPropertis(Map<String, Object> props) {
+ properties.putAll(props);
+ }
+
+ public void overwritePropertis(Map<String, Object> props) {
+ properties = props;
+ }
+
+ public Object getProperty(String property) {
+ return properties.get(property);
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ return properties;
+ }
+
+ @Override
+ public String toString() {
+ return "GraphRelation [from=" + from + ", to=" + to + ", type=" + type + ", properties=" + properties + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((from == null) ? 0 : from.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + ((to == null) ? 0 : to.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/RelationEndPoint.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/RelationEndPoint.java
new file mode 100644
index 0000000000..9d72717b8e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/datatype/RelationEndPoint.java
@@ -0,0 +1,100 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.graph.datatype;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class RelationEndPoint {
+ NodeTypeEnum label;
+ String idName;
+ Object idValue;
+
+ public RelationEndPoint(NodeTypeEnum label, String idName, Object idValue) {
+ super();
+ this.label = label;
+ this.idName = idName;
+ this.idValue = idValue;
+ }
+
+ public NodeTypeEnum getLabel() {
+ return label;
+ }
+
+ public void setLabel(NodeTypeEnum label) {
+ this.label = label;
+ }
+
+ public String getIdName() {
+ return idName;
+ }
+
+ public void setIdName(String idName) {
+ this.idName = idName;
+ }
+
+ public Object getIdValue() {
+ return idValue;
+ }
+
+ public void setIdValue(Object idValue) {
+ this.idValue = idValue;
+ }
+
+ @Override
+ public String toString() {
+ return "RelationEndPoint [label=" + label + ", idName=" + idName + ", idValue=" + idValue + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((idName == null) ? 0 : idName.hashCode());
+ result = prime * result + ((idValue == null) ? 0 : idValue.hashCode());
+ result = prime * result + ((label == null) ? 0 : label.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RelationEndPoint other = (RelationEndPoint) obj;
+ if (idName == null) {
+ if (other.idName != null)
+ return false;
+ } else if (!idName.equals(other.idName))
+ return false;
+ if (idValue == null) {
+ if (other.idValue != null)
+ return false;
+ } else if (!idValue.equals(other.idValue))
+ return false;
+ if (label != other.label)
+ return false;
+ return true;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/AuditingDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/AuditingDao.java
new file mode 100644
index 0000000000..2cf479ec64
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/AuditingDao.java
@@ -0,0 +1,264 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.PostConstruct;
+
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingGenericEvent;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.openecomp.sdc.be.resources.data.auditing.ResourceAdminEvent;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("auditingDao")
+public class AuditingDao extends ESTimeBasedDao {
+
+ private static final String SERVICE_INSTANCE_ID_FIELD = AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID
+ .getDisplayName(); // "serviceInstanceId";
+ private static final String STATUS_FIELD = AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(); // "status";
+ private static final String ACTION_FIELD = AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(); // "action";
+ private static final String DISTRIBUTION_ID_FIELD = AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID.getDisplayName(); // "distributionId";
+ private static Logger log = LoggerFactory.getLogger(AuditingDao.class.getName());
+ public static final String AUDITING_INDEX = "auditingevents";
+
+ @PostConstruct
+ private void init() {
+ AuditingActionEnum[] values = AuditingActionEnum.values();
+ for (AuditingActionEnum value : values) {
+ typesToClasses.put(value.getAuditingEsType(), ESTimeBasedEvent.class);
+ }
+ }
+
+ @Override
+ public String getIndexPrefix() {
+ return AUDITING_INDEX;
+ }
+
+ public ActionStatus addRecord(Map<AuditingFieldsKeysEnum, Object> params, String type) {
+
+ // TODO rhalili - remove? check debugEnabled?
+ Map<String, Object> displayFields = new HashMap<String, Object>();
+ StringBuilder sb = new StringBuilder();
+ for (Entry<AuditingFieldsKeysEnum, Object> entry : params.entrySet()) {
+ displayFields.put(entry.getKey().getDisplayName(), entry.getValue());
+ sb.append(entry.getKey().getDisplayName()).append(" = ").append(entry.getValue()).append(",");
+ }
+
+ // Persisiting
+ // String type = clazz.getSimpleName().toLowerCase();
+ AuditingGenericEvent auditingGenericEvent = new AuditingGenericEvent();
+ populateCommonFields(params, auditingGenericEvent);
+ auditingGenericEvent.getFields().putAll(displayFields);
+
+ log.debug("Auditing: Persisting object of type {}, fields: {}", type, sb.toString());
+
+ return write(type, auditingGenericEvent);
+ }
+
+ public Either<List<ESTimeBasedEvent>, ActionStatus> getListOfDistributionStatuses(String did) {
+
+ QueryBuilder componentNameMatch = QueryBuilders.matchQuery(DISTRIBUTION_ID_FIELD, did);
+ QueryBuilder componentVersionMatch = QueryBuilders.matchQuery(ACTION_FIELD,
+ AuditingActionEnum.DISTRIBUTION_STATUS.getName());
+ QueryBuilder remainingElementQueryBuilder = QueryBuilders.boolQuery().must(componentNameMatch)
+ .must(componentVersionMatch);
+ List<ESTimeBasedEvent> remainingElements = null;
+ try {
+ remainingElements = customFindEvent(AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE,
+ remainingElementQueryBuilder, null);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get DistributionStatuses List");
+
+ log.debug("failed to get distribution statuses for ", e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ if (remainingElements != null && !remainingElements.isEmpty()) {
+ return Either.left(remainingElements);
+ } else {
+ log.debug("not found distribution statuses for did {}", did);
+ remainingElements = new ArrayList<ESTimeBasedEvent>();
+ return Either.left(remainingElements);
+ }
+
+ }
+
+ public Either<List<ESTimeBasedEvent>, ActionStatus> getServiceDistributionStatusesList(String serviceInstanceId) {
+
+ List<ESTimeBasedEvent> resList = new ArrayList<ESTimeBasedEvent>();
+ QueryBuilder componentNameMatch = QueryBuilders.matchQuery(SERVICE_INSTANCE_ID_FIELD, serviceInstanceId);
+ QueryBuilder componentVersionMatch = QueryBuilders.matchQuery(ACTION_FIELD,
+ AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST.getName());
+ QueryBuilder remainingElementQueryBuilder = QueryBuilders.boolQuery().must(componentNameMatch)
+ .must(componentVersionMatch);
+ List<ESTimeBasedEvent> remainingElements = null;
+ try {
+ remainingElements = customFindEvent(AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE,
+ remainingElementQueryBuilder, null);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Service DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Service DistributionStatuses List");
+ log.debug("failed to get distribution statuses for action {}",
+ AuditingActionEnum.DISTRIBUTION_STATE_CHANGE_REQUEST.getName(), e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ if (remainingElements != null && !remainingElements.isEmpty()) {
+ resList.addAll(remainingElements);
+ }
+
+ componentVersionMatch = QueryBuilders.matchQuery(ACTION_FIELD,
+ AuditingActionEnum.DISTRIBUTION_DEPLOY.getName());
+ remainingElementQueryBuilder = QueryBuilders.boolQuery().must(componentNameMatch).must(componentVersionMatch);
+ List<ESTimeBasedEvent> dResultElements = null;
+ try {
+ dResultElements = customFindEvent(AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE,
+ remainingElementQueryBuilder, null);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Service DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Service DistributionStatuses List");
+ log.debug("failed to get distribution statuses for action {}",
+ AuditingActionEnum.DISTRIBUTION_DEPLOY.getName(), e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ if (dResultElements != null && !dResultElements.isEmpty()) {
+ resList.addAll(dResultElements);
+ }
+
+ componentVersionMatch = QueryBuilders.matchQuery(ACTION_FIELD,
+ AuditingActionEnum.DISTRIBUTION_NOTIFY.getName());
+ remainingElementQueryBuilder = QueryBuilders.boolQuery().must(componentNameMatch).must(componentVersionMatch);
+ List<ESTimeBasedEvent> dNotifyElements = null;
+ try {
+ dNotifyElements = customFindEvent(AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE,
+ remainingElementQueryBuilder, null);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Service DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Service DistributionStatuses List");
+ log.debug("failed to get distribution statuses for action {}",
+ AuditingActionEnum.DISTRIBUTION_NOTIFY.getName(), e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ if (remainingElements != null && !remainingElements.isEmpty()) {
+ resList.addAll(dNotifyElements);
+ }
+
+ return Either.left(resList);
+
+ }
+
+ public Either<List<ESTimeBasedEvent>, ActionStatus> getFilteredResourceAdminAuditingEvents(
+ Map<AuditingFieldsKeysEnum, Object> filterMap) {
+
+ Iterator<Entry<AuditingFieldsKeysEnum, Object>> filterItr = filterMap.entrySet().iterator();
+ BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
+ while (filterItr.hasNext()) {
+ Entry<AuditingFieldsKeysEnum, Object> curr = filterItr.next();
+ boolQuery = boolQuery.must(QueryBuilders.termQuery(curr.getKey().getDisplayName(), curr.getValue()));
+ }
+
+ try {
+ List<ESTimeBasedEvent> customFindEvent = customFindEvent(
+ ResourceAdminEvent.class.getSimpleName().toLowerCase(), boolQuery, null);
+ return Either.left(customFindEvent);
+ } catch (Exception e) {
+ log.debug("Failed to query AuditRecords in es");
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ }
+
+ public Either<List<ESTimeBasedEvent>, ActionStatus> getListOfDistributionByAction(String did, String actionType,
+ String requestedStatus, Class<? extends AuditingGenericEvent> clazz) {
+
+ QueryBuilder distributionIdMatch = QueryBuilders.matchQuery(DISTRIBUTION_ID_FIELD, did);
+ QueryBuilder distributionActionMatch = QueryBuilders.matchQuery(ACTION_FIELD, actionType);
+ QueryBuilder remainingElementQueryBuilder;
+
+ if (requestedStatus != null && !requestedStatus.isEmpty()) {
+ QueryBuilder statusMatch = QueryBuilders.matchQuery(STATUS_FIELD, requestedStatus);
+ remainingElementQueryBuilder = QueryBuilders.boolQuery().must(distributionIdMatch)
+ .must(distributionActionMatch).must(statusMatch);
+ } else {
+ remainingElementQueryBuilder = QueryBuilders.boolQuery().must(distributionIdMatch)
+ .must(distributionActionMatch);
+ }
+
+ List<ESTimeBasedEvent> remainingElements = null;
+ try {
+ remainingElements = customFindEvent(clazz.getSimpleName().toLowerCase(), remainingElementQueryBuilder,
+ null);
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get DistributionStatuses List");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get DistributionStatuses List");
+ log.debug("failed to get distribution statuses for action {}", actionType, e);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ return Either.left(remainingElements);
+
+ }
+
+ private void populateCommonFields(Map<AuditingFieldsKeysEnum, Object> params,
+ AuditingGenericEvent timeBasedIndexedData) {
+ String dateStr = (String) params.get(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP);
+ if (dateStr != null) {
+ timeBasedIndexedData.setTimestamp(dateStr);
+ }
+ timeBasedIndexedData.setAction((String) params.get(AuditingFieldsKeysEnum.AUDIT_ACTION));
+ Object statusObj = params.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ // For BC. status was Integer and is String
+ if (statusObj != null) {
+ timeBasedIndexedData.setStatus(String.valueOf(statusObj));
+ } else {
+ timeBasedIndexedData.setStatus(null);
+ }
+ // timeBasedIndexedData.setStatus((String)params.get(AuditingFieldsKeysEnum.AUDIT_STATUS));
+ timeBasedIndexedData.setDesc((String) params.get(AuditingFieldsKeysEnum.AUDIT_DESC));
+ timeBasedIndexedData
+ .setServiceInstanceId((String) params.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID));
+ timeBasedIndexedData.setRequestId((String) params.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID));
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESCatalogDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESCatalogDAO.java
new file mode 100644
index 0000000000..87cd8a5f3e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESCatalogDAO.java
@@ -0,0 +1,214 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
+import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
+import org.elasticsearch.common.unit.TimeValue;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ESGenericSearchDAO;
+import org.openecomp.sdc.be.dao.api.ICatalogDAO;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.exception.ResourceDAOException;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import fj.data.Either;
+
+@Component("resource-dao")
+public class ESCatalogDAO extends ESGenericSearchDAO implements ICatalogDAO {
+
+ private static Logger log = LoggerFactory.getLogger(ESCatalogDAO.class.getName());
+ private static Logger healthCheckLogger = LoggerFactory.getLogger("elasticsearch.healthcheck");
+
+ ///// HealthCheck/////////
+ private static final String ES_HEALTH_CHECK_STR = "elasticsearchHealthCheck";
+
+ private ScheduledExecutorService healthCheckScheduler = Executors
+ .newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "ES-Health-Check-Thread");
+ }
+ });
+
+ private class HealthCheckScheduledTask implements Runnable {
+ @Override
+ public void run() {
+ healthCheckLogger.trace("Executing ELASTICSEARCH Health Check Task - Start");
+
+ HealthCheckStatus healthStatus = null;
+ try {
+ healthStatus = isInitCompleted() ? checkHealth() : HealthCheckStatus.DOWN;
+ } catch (Exception e) {
+ log.error("Error while trying to connect to elasticsearch. host: " + getEsClient().getServerHost()
+ + " port: " + getEsClient().getServerPort(), e.getMessage());
+ log.trace("Error while trying to connect to elasticsearch", e);
+ healthStatus = HealthCheckStatus.DOWN;
+ }
+ healthCheckLogger.trace("Executed ELASTICSEARCH Health Check Task - Status = {}", healthStatus);
+ if (healthStatus != lastHealthState) {
+ log.trace("ELASTICSEARCH Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
+ lastHealthState = healthStatus;
+ logAlarm();
+ }
+ }
+ }
+
+ private HealthCheckScheduledTask healthCheckScheduledTask = new HealthCheckScheduledTask();
+ private volatile HealthCheckStatus lastHealthState = HealthCheckStatus.DOWN;
+
+ /**
+ * Get ES cluster status string rep
+ *
+ * @return "GREEN", "YELLOW" or "RED"
+ */
+ private HealthCheckStatus checkHealth() {
+ if (!isInitCompleted()) {
+ return HealthCheckStatus.DOWN;
+ }
+ ClusterHealthRequest healthRequest = new ClusterHealthRequest("_all");
+ healthRequest.masterNodeTimeout(TimeValue.timeValueSeconds(2));
+ ClusterHealthStatus status = getClient().admin().cluster().health(healthRequest).actionGet().getStatus();
+ healthCheckLogger.debug("ES cluster health status is {}", status);
+ if (status == null || status.equals(ClusterHealthStatus.RED)) {
+ return HealthCheckStatus.DOWN;
+ }
+ return HealthCheckStatus.UP;
+ }
+
+ private void logAlarm() {
+ if (lastHealthState == HealthCheckStatus.UP) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckRecovery,
+ ES_HEALTH_CHECK_STR);
+ BeEcompErrorManager.getInstance().logBeHealthCheckElasticSearchRecovery(ES_HEALTH_CHECK_STR);
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckError, ES_HEALTH_CHECK_STR);
+ BeEcompErrorManager.getInstance().logBeHealthCheckElasticSearchError(ES_HEALTH_CHECK_STR);
+ }
+ }
+
+ @PostConstruct
+ public void initCompleted() {
+ long interval = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getEsReconnectIntervalInSeconds(5);
+ this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, interval, TimeUnit.SECONDS);
+ initCompleted = true;
+ }
+
+ // Index Checking Variables
+ private boolean initCompleted = false;
+
+ @Override
+ public void writeArtifact(ESArtifactData artifactData) throws ResourceDAOException {
+ try {
+ saveResourceData(artifactData);
+ } catch (Exception e) {
+ throw new ResourceDAOException("Error to save ArtifactData with " + artifactData.getId());
+ }
+ }
+
+ @Override
+ public Either<ESArtifactData, ResourceUploadStatus> getArtifact(String id) {
+ ESArtifactData resData = null;
+
+ try {
+ resData = findById(getTypeFromClass(ESArtifactData.class), id, ESArtifactData.class);
+ } catch (Exception e) {
+ resData = null;
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Get Artifact from database");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Get Artifact from database");
+ log.debug("ESCatalogDAO:getArtifact failed with exception ", e);
+ return Either.right(ResourceUploadStatus.ERROR);
+ }
+
+ if (resData != null) {
+ return Either.left(resData);
+ } else {
+ return Either.right(ResourceUploadStatus.NOT_EXIST);
+ }
+ }
+
+ private <T> String getTypeFromClass(Class<T> clazz) {
+
+ return clazz.getSimpleName().toLowerCase();
+ }
+
+ @Override
+ public void deleteArtifact(String id) {
+ delete(getTypeFromClass(ESArtifactData.class), id);
+ }
+
+ @Override
+ public Either<List<ESArtifactData>, ResourceUploadStatus> getArtifacts(String[] ids) {
+ List<ESArtifactData> resData = null;
+ try {
+ resData = findByIds(getTypeFromClass(ESArtifactData.class), ESArtifactData.class, ids);
+ } catch (Exception e) {
+ resData = null;
+ return Either.right(ResourceUploadStatus.ERROR);
+ }
+
+ if (resData != null && !resData.isEmpty()) {
+ return Either.left(resData);
+ } else {
+ return Either.right(ResourceUploadStatus.NOT_EXIST);
+ }
+ }
+
+ private void saveResourceData(ESArtifactData data) throws JsonProcessingException {
+ String typeName = getTypeFromClass(data.getClass());
+ saveResourceData(typeName, data, data.getId());
+ }
+
+ @Override
+ public void deleteAllArtifacts() {
+ String typeName = getTypeFromClass(ESArtifactData.class);
+ String indexName = getIndexForType(typeName);
+ deleteIndex(indexName);
+
+ }
+
+ public boolean isInitCompleted() {
+ return initCompleted;
+ }
+
+ public HealthCheckStatus getHealth() {
+ return lastHealthState;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESTimeBasedDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESTimeBasedDao.java
new file mode 100644
index 0000000000..b7666392aa
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/ESTimeBasedDao.java
@@ -0,0 +1,305 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.search.sort.SortBuilder;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.ElasticSearchConfig.IndicesTimeFrequencyEntry;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public abstract class ESTimeBasedDao {
+ private static final String SCORE_SCRIPT = "_score * ((doc.containsKey('alienScore') && !doc['alienScore'].empty) ? doc['alienScore'].value : 1)";
+ private static final int MAX_SEARCH_SIZE = 1000;
+ private static Logger log = LoggerFactory.getLogger(ESTimeBasedDao.class.getName());
+
+ private Gson gson;
+
+ private Map<String, String> indexPrefix2CreationPeriod;
+
+ private ConfigurationManager configurationManager;
+
+ protected ESTimeBasedDao() {
+ gson = new GsonBuilder().setPrettyPrinting().create();
+ configurationManager = ConfigurationManager.getConfigurationManager();
+ setIndexPrefix2CreationPeriod();
+ }
+
+ public void setConfigurationManager(ConfigurationManager configurationManager) {
+ this.configurationManager = configurationManager;
+ }
+
+ @Resource(name = "elasticsearch-client")
+ private ElasticSearchClient esClient;
+
+ protected final Map<String, Class<?>> typesToClasses = new HashMap<String, Class<?>>();
+
+ public abstract String getIndexPrefix();
+
+ public ActionStatus write(String typeName, ESTimeBasedEvent data) {
+
+ String indexPrefix = getIndexPrefix();
+ String indexSuffix = getIndexSuffix(indexPrefix, data);
+ StringBuilder sb = new StringBuilder();
+ sb.append(indexPrefix);
+ if (indexSuffix != null) {
+ sb.append("-").append(indexSuffix);
+ }
+
+ ActionStatus res = ActionStatus.OK;
+ JSONObject json = new JSONObject(data.getFields());
+ try {
+ IndexResponse actionGet = esClient.getClient().prepareIndex(sb.toString(), typeName)
+ .setSource(json.toString()).setRefresh(true).execute().actionGet(TimeValue.timeValueSeconds(15));
+
+ if (actionGet.isCreated()) {
+ log.debug("Created record {}", data.toString());
+ // typesToClasses.put(typeName, data.getClass());
+ } else {
+ log.error("Record {} couldn't be created", data.toString());
+ res = ActionStatus.GENERAL_ERROR;
+ }
+ } catch (Exception e) {
+ log.error("Couldn't serialize object of type " + typeName + " , error:", e);
+ res = ActionStatus.GENERAL_ERROR;
+ }
+ return res;
+ }
+
+ public ActionStatus write(ESTimeBasedEvent data) {
+
+ String indexPrefix = getIndexPrefix();
+ String indexSuffix = getIndexSuffix(indexPrefix, data);
+ StringBuilder sb = new StringBuilder();
+ sb.append(indexPrefix);
+ if (indexSuffix != null) {
+ sb.append("-").append(indexSuffix);
+ }
+
+ String typeName = data.getClass().getSimpleName().toLowerCase();
+ ActionStatus res = ActionStatus.OK;
+ String json = gson.toJson(data);
+ try {
+ IndexResponse actionGet = esClient.getClient().prepareIndex(sb.toString(), typeName).setSource(json)
+ .setRefresh(true).execute().actionGet(TimeValue.timeValueSeconds(15));
+
+ if (actionGet.isCreated()) {
+ log.debug("Created record {}", data.toString());
+ // typesToClasses.put(typeName, data.getClass());
+ } else {
+ log.error("Record {} couldn't be created", data.toString());
+ res = ActionStatus.GENERAL_ERROR;
+ }
+ } catch (Exception e) {
+ log.debug("Couldn't serialize object of type {}", typeName);
+ res = ActionStatus.GENERAL_ERROR;
+ }
+ return res;
+ }
+
+ private void setIndexPrefix2CreationPeriod() {
+ indexPrefix2CreationPeriod = new HashMap<String, String>();
+ List<IndicesTimeFrequencyEntry> indicesTimeFrequencyEntries = configurationManager.getConfiguration()
+ .getElasticSearch().getIndicesTimeFrequency();
+ for (IndicesTimeFrequencyEntry entry : indicesTimeFrequencyEntries) {
+ indexPrefix2CreationPeriod.put(entry.getIndexPrefix(), entry.getCreationPeriod());
+
+ }
+ }
+
+ private String getIndexSuffix(String indexPrefix, ESTimeBasedEvent data) {
+ String indexSuffix = indexPrefix2CreationPeriod.get(indexPrefix);
+ String res = null;
+ if (indexSuffix != null) {
+ if (indexSuffix.equalsIgnoreCase(Constants.YEAR)) {
+ res = data.calculateYearIndexSuffix();
+ } else if (indexSuffix.equalsIgnoreCase(Constants.MONTH)) {
+ res = data.calculateMonthIndexSuffix();
+ } else if (indexSuffix.equalsIgnoreCase(Constants.DAY)) {
+ res = data.calculateDayIndexSuffix();
+ } else if (indexSuffix.equalsIgnoreCase(Constants.HOUR)) {
+ res = data.calculateHourIndexSuffix();
+ } else if (indexSuffix.equalsIgnoreCase(Constants.MINUTE)) {
+ res = data.calculateMinuteIndexSuffix();
+ } else if (indexSuffix.equalsIgnoreCase(Constants.NONE)) {
+ // do nothing - no time-based behaviour. I wanted to ensure
+ // proper syntax, that's why this clause is needed.
+ }
+ } else {
+ // Default behaviour - time-based with month period
+ res = data.calculateMonthIndexSuffix();
+ }
+ return res;
+ }
+
+ public <T> long count(Class<T> clazz, QueryBuilder query) {
+ String indexName = getIndexPrefix() + "*";
+ String typeName = clazz.getSimpleName().toLowerCase();
+ SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(indexName).setTypes(typeName)
+ .setSize(0);
+ if (query != null) {
+ searchRequestBuilder.setQuery(query);
+ }
+
+ SearchResponse response = searchRequestBuilder.execute().actionGet();
+ return response.getHits().getTotalHits();
+ }
+
+ private <T> List<T> doCustomFind(Class<T> clazz, QueryBuilder query, SortBuilder sortBuilder, int size) {
+ String indexName = getIndexPrefix() + "*";
+ String typeName = clazz.getSimpleName().toLowerCase();
+ SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(indexName).setTypes(typeName)
+ .setSize(size);
+ if (query != null) {
+ searchRequestBuilder.setQuery(query);
+ }
+ if (sortBuilder != null) {
+ searchRequestBuilder.addSort(sortBuilder);
+ }
+ SearchResponse response = searchRequestBuilder.execute().actionGet();
+ if (!somethingFound(response)) {
+ return null;
+ } else {
+ List<T> hits = new ArrayList<T>();
+ for (int i = 0; i < response.getHits().getHits().length; i++) {
+ String hit = response.getHits().getAt(i).sourceAsString();
+
+ hits.add((T) gson.fromJson(hit, clazz));
+
+ }
+ return hits;
+ }
+ }
+
+ private List<ESTimeBasedEvent> doCustomFindForEvent(String typeName, QueryBuilder query, SortBuilder sortBuilder,
+ int size) {
+ String indexName = getIndexPrefix() + "*";
+ // String typeName = clazz.getSimpleName().toLowerCase();
+ SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(indexName).setTypes(typeName)
+ .setSize(size);
+ if (query != null) {
+ searchRequestBuilder.setQuery(query);
+ }
+ if (sortBuilder != null) {
+ searchRequestBuilder.addSort(sortBuilder);
+ }
+ SearchResponse response = searchRequestBuilder.execute().actionGet();
+ if (!somethingFound(response)) {
+ return null;
+ } else {
+ List<ESTimeBasedEvent> hits = new ArrayList<ESTimeBasedEvent>();
+ for (int i = 0; i < response.getHits().getHits().length; i++) {
+ String hit = response.getHits().getAt(i).sourceAsString();
+
+ ESTimeBasedEvent event;
+ try {
+ event = ESTimeBasedEvent.createEventFromJson(hit);
+ hits.add(event);
+ } catch (JSONException e) {
+ log.warn("failed to parse hit from audit index. error: {}", e.getMessage());
+ log.debug("failed to parse hit from audit. hit = {} {}", hit, e);
+ }
+ }
+ return hits;
+ }
+ }
+
+ public List<ESTimeBasedEvent> customFindEvent(String typeName, QueryBuilder query, SortBuilder sortBuilder)
+ throws JSONException {
+ List<ESTimeBasedEvent> results = doCustomFindForEvent(typeName, query, sortBuilder, MAX_SEARCH_SIZE);
+ if (results == null) {
+ results = new ArrayList<ESTimeBasedEvent>();
+ }
+ return results;
+ }
+
+ public <T> T customFind(Class<T> clazz, QueryBuilder query) {
+ return customFind(clazz, query, null);
+ }
+
+ public <T> T customFind(Class<T> clazz, QueryBuilder query, SortBuilder sortBuilder) {
+ List<T> results = doCustomFind(clazz, query, sortBuilder, 1);
+ if (results == null || results.isEmpty()) {
+ return null;
+ } else {
+ return results.iterator().next();
+ }
+ }
+
+ public <T> List<T> customFindAll(Class<T> clazz, QueryBuilder query) {
+ return customFindAll(clazz, query, null);
+ }
+
+ public <T> List<T> customFindAll(Class<T> clazz, QueryBuilder query, SortBuilder sortBuilder) {
+ return doCustomFind(clazz, query, sortBuilder, Integer.MAX_VALUE);
+ }
+
+ private boolean somethingFound(final SearchResponse searchResponse) {
+ if (searchResponse == null || searchResponse.getHits() == null || searchResponse.getHits().getHits() == null
+ || searchResponse.getHits().getHits().length == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public String getEsHost() {
+ String host = null;
+ if (this.esClient != null) {
+ host = this.esClient.getServerHost();
+ } else {
+ log.error("esClient is unavilable could not get host.");
+ }
+ return host;
+ }
+
+ public String getEsPort() {
+ String port = null;
+ if (this.esClient != null) {
+ port = this.esClient.getServerPort();
+ } else {
+ log.error("esClient is unavilable could not get port.");
+ }
+
+ return port;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/EsHealthCheckDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/EsHealthCheckDao.java
new file mode 100644
index 0000000000..04d985bc83
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/EsHealthCheckDao.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.dao.api.IEsHealthCheckDao;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component("esHealthCheckDao")
+public class EsHealthCheckDao implements IEsHealthCheckDao {
+
+ private static Logger logger = LoggerFactory.getLogger(EsHealthCheckDao.class.getName());
+
+ @Resource(name = "elasticsearch-client")
+ private ElasticSearchClient esClient;
+
+ @Resource
+ private ESCatalogDAO esCatalogDao;
+
+ public EsHealthCheckDao() {
+ }
+
+ public HealthCheckStatus getClusterHealthStatus() {
+ return this.esCatalogDao.getHealth();
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/MonitoringDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/MonitoringDao.java
new file mode 100644
index 0000000000..1c16c155a5
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/MonitoringDao.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.common.monitoring.MonitoringEvent;
+import org.springframework.stereotype.Component;
+
+@Component("monitoringDao")
+public class MonitoringDao extends ESTimeBasedDao {
+
+ public static final String MONITORING_INDEX = "monitoring_events";
+
+ @Override
+ public String getIndexPrefix() {
+ return MONITORING_INDEX;
+ }
+
+ public ActionStatus addRecord(MonitoringEvent monitoringEvent) {
+ return write(monitoringEvent);
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jElementDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jElementDAO.java
new file mode 100644
index 0000000000..d6b8d2eed8
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jElementDAO.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import java.util.List;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.api.IElementDAO;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jClient;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+//@Component("elements-dao")
+public class Neo4jElementDAO implements IElementDAO {
+
+ // @Resource
+ Neo4jClient neo4jClient;
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jElementDAO.class.getName());
+
+ @Override
+ public Either<List<GraphElement>, ActionStatus> getAllCategories() {
+ MatchFilter filter = new MatchFilter();
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ NodeTypeEnum.ResourceCategory.getName(), filter);
+ if (status.isRight()) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null || value.isEmpty()) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ return Either.left(value);
+ }
+ }
+ }
+
+ @Override
+ public Either<List<GraphElement>, ActionStatus> getAllTags() {
+ MatchFilter filter = new MatchFilter();
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ NodeTypeEnum.Tag.getName(), filter);
+ if (status.isRight()) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ return Either.left(value);
+ }
+ }
+ }
+
+ @Override
+ public Either<GraphElement, ActionStatus> getCategory(String name) {
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch(GraphPropertiesDictionary.NAME.getProperty(), name);
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ NodeTypeEnum.ResourceCategory.getName(), filter);
+ if (status.isRight()) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ return Either.left(value.get(0));
+ }
+ }
+
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param neo4jClient
+ */
+ public void setNeo4jClient(Neo4jClient neo4jClient) {
+ this.neo4jClient = neo4jClient;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jPropertyDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jPropertyDAO.java
new file mode 100644
index 0000000000..c3a7c50803
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jPropertyDAO.java
@@ -0,0 +1,99 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.dao.api.BasicDao;
+import org.openecomp.sdc.be.dao.api.IPropertyDAO;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jClient;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jGraphBatchBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+//@Component("neo4j-property-dao")
+public class Neo4jPropertyDAO extends BasicDao implements IPropertyDAO {
+
+ // @Resource
+ Neo4jClient neo4jClient;
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jPropertyDAO.class.getName());
+
+ Neo4jGraphBatchBuilder graphBatchBuilder = new Neo4jGraphBatchBuilder();
+
+ public Neo4jPropertyDAO() {
+
+ }
+
+ @PostConstruct
+ public void init() {
+ setNeo4jClient(neo4jClient);
+ }
+
+ // @Override
+ // public Either<PropertyData, Neo4jOperationStatus> createPropertyData(
+ // GraphNeighbourTable graphNeighbourTable) {
+ //
+ // Either<BatchBuilder, Neo4jOperationStatus> bbResult = graphBatchBuilder
+ // .buildBatchBuilderFromTable(graphNeighbourTable);
+ //
+ // if (bbResult.isLeft()) {
+ //
+ // BatchBuilder batchBuilder = bbResult.left().value();
+ // //Neo4jOperationStatus neo4jOperationStatus =
+ // neo4jClient.execute(batchBuilder);
+ // Either<List<List<Neo4jElement>>, Neo4jOperationStatus> executeResult =
+ // neo4jClient.execute(batchBuilder);
+ //
+ // if (executeResult.isRight()) {
+ // return Either.right(executeResult.right().value());
+ // }
+ //
+ // PropertyData propertyData = null;
+ // List<List<Neo4jElement>> listOfResults = executeResult.left().value();
+ // if (listOfResults != null) {
+ // for (List<Neo4jElement> listOfElements : listOfResults) {
+ // if (listOfElements != null && false == listOfElements.isEmpty()) {
+ // for (Neo4jElement element : listOfElements) {
+ // logger.debug("element {} was returned after running batch operation {}", element, batchBuilder);
+ // if (element instanceof Neo4jNode) {
+ // Neo4jNode neo4jNode = (Neo4jNode) element;
+ // if (NodeTypeEnum.getByName(neo4jNode.getLabel()) ==
+ // NodeTypeEnum.Property) {
+ // propertyData = (PropertyData)neo4jNode;
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ //
+ // return Either.left(propertyData);
+ //
+ // } else {
+ // return Either.right(bbResult.right().value());
+ // }
+ //
+ // }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jResourceDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jResourceDAO.java
new file mode 100644
index 0000000000..1647a656f3
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jResourceDAO.java
@@ -0,0 +1,242 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.openecomp.sdc.be.dao.api.BasicDao;
+import org.openecomp.sdc.be.dao.api.IResourceDAO;
+import org.openecomp.sdc.be.dao.graph.datatype.ActionEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jClient;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jGraphBatchBuilder;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
+import org.openecomp.sdc.be.dao.neo4j.filters.RecursiveFilter;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+//@Component("neo4j-resource-dao")
+public class Neo4jResourceDAO extends BasicDao implements IResourceDAO {
+
+ // @Resource
+ Neo4jClient neo4jClient;
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jResourceDAO.class.getName());
+
+ Neo4jGraphBatchBuilder graphBatchBuilder = new Neo4jGraphBatchBuilder();
+
+ public Neo4jResourceDAO() {
+
+ }
+
+ @PostConstruct
+ public void init() {
+ super.setNeo4jClient(neo4jClient);
+ }
+
+ // public Either<ResourceData, Neo4jOperationStatus> createResourceData(
+ // GraphNeighbourTable graphNeighbourTable) {
+ //
+ // if (graphNeighbourTable != null) {
+ //
+ // String resourceId = findResourceDataIdFromNodes(graphNeighbourTable
+ // .getNodes());
+ // if (resourceId == null || resourceId.isEmpty()) {
+ // logger.error("Cannot find resource id in the graph table");
+ // return Either.right(Neo4jOperationStatus.BAD_REQUEST);
+ // }
+ //
+ // Either<BatchBuilder, Neo4jOperationStatus> bbResult = graphBatchBuilder
+ // .buildBatchBuilderFromTable(graphNeighbourTable);
+ //
+ // if (bbResult.isLeft()) {
+ //
+ // BatchBuilder batchBuilder = bbResult.left().value();
+ // //Neo4jOperationStatus neo4jOperationStatus =
+ // neo4jClient.execute(batchBuilder);
+ // Either<List<List<Neo4jElement>>, Neo4jOperationStatus> executeResult =
+ // neo4jClient.execute(batchBuilder);
+ //
+ // if (executeResult.isRight()) {
+ // return Either.right(executeResult.right().value());
+ // }
+ //
+ // ResourceData resourceData = null;
+ // List<List<Neo4jElement>> listOfResults = executeResult.left().value();
+ // if (listOfResults != null) {
+ // for (List<Neo4jElement> listOfElements : listOfResults) {
+ // if (listOfElements != null && false == listOfElements.isEmpty()) {
+ // for (Neo4jElement element : listOfElements) {
+ // logger.debug("element {} was returned after running batch operation {}", element, batchBuilder);
+ // if (element instanceof Neo4jNode) {
+ // Neo4jNode neo4jNode = (Neo4jNode) element;
+ // if (NodeTypeEnum.getByName(neo4jNode.getLabel()) ==
+ // NodeTypeEnum.Resource) {
+ // resourceData = (ResourceData)neo4jNode;
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ //
+ // return Either.left(resourceData);
+ //
+ // } else {
+ // return Either.right(bbResult.right().value());
+ // }
+ //
+ // } else {
+ // logger.error("The table sent in order to create resource is empty.");
+ // return Either.right(Neo4jOperationStatus.BAD_REQUEST);
+ // }
+ // }
+
+ private String findResourceDataIdFromNodes(List<GraphNode> nodes) {
+
+ if (nodes != null) {
+
+ for (GraphNode neo4jNode : nodes) {
+ String label = neo4jNode.getLabel();
+ if (label.equals(NodeTypeEnum.Resource.getName())) {
+ return neo4jNode.getUniqueId().toString();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private GraphRelation addStateRelation(RelationEndPoint from, RelationEndPoint to, GraphEdgeLabels edgeLabel,
+ String value) {
+
+ GraphRelation relationState = new GraphRelation();
+ relationState.setFrom(from);
+ relationState.setTo(to);
+ relationState.setType(edgeLabel.name());
+ relationState.setAction(ActionEnum.Create);
+ return relationState;
+ }
+
+ // private ActionStatus convertNeo4jOperationStatusToActionStatus(
+ // Neo4jOperationStatus value) {
+ //
+ // if (value == null) {
+ // return ActionStatus.GENERAL_ERROR;
+ // }
+ //
+ // switch (value) {
+ // case NOT_FOUND:
+ // return ActionStatus.RESOURCE_NOT_FOUND;
+ // case ERROR:
+ // return ActionStatus.GENERAL_ERROR;
+ // case NOT_SUPPORTED:
+ // return ActionStatus.INVALID_CONTENT;
+ // case WRONG_INPUT:
+ // return ActionStatus.INVALID_CONTENT;
+ // case OK:
+ // return ActionStatus.OK;
+ // default:
+ // return ActionStatus.GENERAL_ERROR;
+ // }
+ //
+ // }
+
+ @Override
+ public Either<ResourceMetadataData, Neo4jOperationStatus> getResourceData(String id) {
+
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), id);
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ NodeTypeEnum.Resource.getName(), filter);
+
+ if (status.isRight()) {
+ return Either.right(status.right().value());
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null || value.isEmpty()) {
+ return Either.right(Neo4jOperationStatus.NOT_FOUND);
+ } else {
+ return Either.left((ResourceMetadataData) value.get(0));
+ }
+ }
+ }
+
+ @Override
+ public Either<Integer, Neo4jOperationStatus> getNumberOfResourcesByName(String name) {
+
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch(GraphPropertiesDictionary.NAME.getProperty(), name);
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ NodeTypeEnum.Resource.getName(), filter);
+
+ if (status.isRight() || (status.left().value() == null)) {
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ } else {
+ List<GraphElement> value = status.left().value();
+ return Either.left(value.size());
+ }
+ }
+
+ @Override
+ public void setNeo4jClient(Neo4jClient client) {
+ this.neo4jClient = client;
+ super.setNeo4jClient(client);
+ }
+
+ @Override
+ public Either<List<ResourceMetadataData>, Neo4jOperationStatus> getAllResourcesData(
+ Map<String, Object> propertiesToMatch) {
+
+ RecursiveFilter filter = new RecursiveFilter(NodeTypeEnum.Resource);
+ // filter.addRelationType("typeof").addRelationType("belong").setProperties(propertiesToMatch);
+
+ Either<List<List<GraphElement>>, Neo4jOperationStatus> ret = neo4jClient.executeGet(filter);
+ if (ret.isRight()) {
+ return Either.right(ret.right().value());
+ }
+ List<List<GraphElement>> listOfListOfNeo4jElement = ret.left().value();
+
+ for (List<GraphElement> row : listOfListOfNeo4jElement) {
+
+ for (GraphElement elem : row) {
+
+ }
+ }
+ return Either.right(null);
+
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jUsersDAO.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jUsersDAO.java
new file mode 100644
index 0000000000..25a77f1ff1
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/impl/Neo4jUsersDAO.java
@@ -0,0 +1,169 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.api.IUsersDAO;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jClient;
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
+import org.openecomp.sdc.be.dao.neo4j.filters.UpdateFilter;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+//@Component("users-dao")
+public class Neo4jUsersDAO implements IUsersDAO {
+
+ // @Resource
+ Neo4jClient neo4jClient;
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jUsersDAO.class.getName());
+
+ public Neo4jUsersDAO() {
+
+ }
+
+ @PostConstruct
+ public void init() {
+ }
+
+ private void createIndexesAndConstraints() {
+ Either<Map<String, List<String>>, Neo4jOperationStatus> statusInd = neo4jClient
+ .getIndexes(NodeTypeEnum.User.getName());
+ if (statusInd.isRight()) {
+ logger.error("Failed to get indexes from Neo4j graph");
+ throw new RuntimeException("Failed to initialize Neo4jUsersDAO - Failed to get indexes from Neo4j graph");
+ }
+ Map<String, List<String>> indexes = statusInd.left().value();
+ if (indexes == null || indexes.isEmpty()) {
+ logger.info("Define users indexes in Neo4j");
+ List<String> propertyNames = new ArrayList<String>();
+ propertyNames.add("firstName");
+ propertyNames.add("lastName");
+ propertyNames.add("email");
+ propertyNames.add("role");
+ logger.info("Start create Users indexes in Neo4jGraph");
+ Neo4jOperationStatus createIndexStatus = neo4jClient.createIndex(NodeTypeEnum.User.getName(),
+ propertyNames);
+ if (createIndexStatus.equals(Neo4jOperationStatus.OK)) {
+ logger.info("Users indexes created in Neo4j");
+ List<String> propertyUnique = new ArrayList<String>();
+ propertyUnique.add("userId");
+
+ logger.info("Start create Users constraints in Neo4jGraph");
+ Neo4jOperationStatus createUniquenessStatus = neo4jClient
+ .createUniquenessConstraints(NodeTypeEnum.User.getName(), propertyUnique);
+ if (createUniquenessStatus.equals(Neo4jOperationStatus.OK)) {
+ logger.info("Users constraints creatyed in Neo4j");
+ } else {
+ logger.error("Failed to create constraints in Neo4j graph [" + createUniquenessStatus + "]");
+ throw new RuntimeException(
+ "Failed to initialize Neo4jUsersDAO - Failed to create constraints in Neo4j graph");
+ }
+ } else {
+ logger.error("Failed to create indexes in Neo4j graph [" + createIndexStatus + "]");
+ throw new RuntimeException(
+ "Failed to initialize Neo4jUsersDAO - Failed to create indexes in Neo4j graph");
+ }
+ } else {
+ logger.info("Users indexes already defined in Neo4j");
+ }
+ }
+
+ @Override
+ public Either<UserData, ActionStatus> getUserData(String id) {
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch("userId", id);
+ Either<List<GraphElement>, Neo4jOperationStatus> status = neo4jClient.getByFilter(GraphElementTypeEnum.Node,
+ NodeTypeEnum.User.getName(), filter);
+ if (status.isRight()) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ } else {
+ List<GraphElement> value = status.left().value();
+ if (value == null || value.isEmpty()) {
+ return Either.right(ActionStatus.USER_NOT_FOUND);
+ } else {
+ return Either.left((UserData) value.get(0));
+ }
+ }
+ }
+
+ @Override
+ public ActionStatus saveUserData(UserData userData) {
+ Neo4jOperationStatus status = neo4jClient.createElement(userData);
+ if (status.equals(Neo4jOperationStatus.OK)) {
+ return ActionStatus.OK;
+ } else {
+ return ActionStatus.GENERAL_ERROR;
+ }
+ }
+
+ @Override
+ public ActionStatus updateUserData(UserData userData) {
+ UpdateFilter filter = new UpdateFilter();
+ filter.addToMatch("userId", userData.getUserId());
+ filter.setToUpdate(userData.toGraphMap());
+ Neo4jOperationStatus status = neo4jClient.updateElement(GraphElementTypeEnum.Node, NodeTypeEnum.User.getName(),
+ filter);
+ if (status.equals(Neo4jOperationStatus.OK)) {
+ return ActionStatus.OK;
+ } else {
+ return ActionStatus.GENERAL_ERROR;
+ }
+ }
+
+ @Override
+ public ActionStatus deleteUserData(String id) {
+ MatchFilter filter = new MatchFilter();
+ filter.addToMatch("userId", id);
+ Neo4jOperationStatus status = neo4jClient.deleteElement(GraphElementTypeEnum.Node, NodeTypeEnum.User.getName(),
+ filter);
+ if (status.equals(Neo4jOperationStatus.OK)) {
+ return ActionStatus.OK;
+ } else {
+ return ActionStatus.GENERAL_ERROR;
+ }
+ }
+
+ public Neo4jClient getNeo4jClient() {
+ return neo4jClient;
+ }
+
+ public void setNeo4jClient(Neo4jClient neo4jClient) {
+ this.neo4jClient = neo4jClient;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchFacet.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchFacet.java
new file mode 100644
index 0000000000..24db9b6912
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchFacet.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.model;
+
+import java.io.Serializable;
+
+/**
+ * A facet informations.
+ *
+ */
+@SuppressWarnings("PMD.UnusedPrivateField")
+public class FacetedSearchFacet implements Serializable {
+ public FacetedSearchFacet(String facetValue, int count) {
+ this.count = count;
+ this.facetValue = facetValue;
+ }
+
+ public String getFacetValue() {
+ return facetValue;
+ }
+
+ public void setFacetValue(String facetValue) {
+ this.facetValue = facetValue;
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public void setCount(long count) {
+ this.count = count;
+ }
+
+ private static final long serialVersionUID = 1L;
+ private String facetValue;
+ private long count;
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchResult.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchResult.java
new file mode 100644
index 0000000000..3e14ee7d6c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FacetedSearchResult.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Contains results for a search query.
+ *
+ */
+
+@SuppressWarnings("PMD.UnusedPrivateField")
+public class FacetedSearchResult extends GetMultipleDataResult {
+ private static final long serialVersionUID = 1L;
+
+ private Map<String, FacetedSearchFacet[]> facets;
+
+ /**
+ * Argument constructor.
+ *
+ * @param from
+ * The start index of the returned elements.
+ * @param to
+ * The end index of the returned elements.
+ * @param queryDuration
+ * The duration of the query.
+ * @param totalResults
+ * The total results for this query.
+ * @param types
+ * The types of data found.
+ * @param data
+ * The found data.
+ * @param hashMap
+ * The facets if any for the query.
+ */
+ public FacetedSearchResult(final int from, final int to, final long queryDuration, final long totalResults,
+ final String[] types, final Object[] data, final HashMap<String, FacetedSearchFacet[]> hashMap) {
+ super(types, data, queryDuration, totalResults, from, to);
+ this.facets = hashMap;
+ }
+
+ public Map<String, FacetedSearchFacet[]> getFacets() {
+ return facets;
+ }
+
+ public void setFacets(Map<String, FacetedSearchFacet[]> facets) {
+ this.facets = facets;
+ }
+
+ public FacetedSearchResult() {
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FetchContext.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FetchContext.java
new file mode 100644
index 0000000000..a02c38cf0c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/FetchContext.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.model;
+
+public final class FetchContext {
+
+ public static final String QUICK_SEARCH = "quick_search";
+ public static final String TAG_SUGGESTION = "tag_suggestion";
+ public static final String COMPONENT_SUMMARY = "component_summary";
+ public static final String DEPLOYMENT = "deployment";
+
+ private FetchContext() {
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/GetMultipleDataResult.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/GetMultipleDataResult.java
new file mode 100644
index 0000000000..e1e0593a04
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/model/GetMultipleDataResult.java
@@ -0,0 +1,113 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.model;
+
+import java.io.Serializable;
+
+/**
+ * Result for a multiple data query.
+ *
+ * @author luc boutier
+ */
+@SuppressWarnings("PMD.UnusedPrivateField")
+public class GetMultipleDataResult<T> implements Serializable {
+ public String[] getTypes() {
+ return types;
+ }
+
+ public T[] getData() {
+ return data;
+ }
+
+ public void setTypes(String[] types) {
+ this.types = types.clone();
+ }
+
+ public void setData(T[] data) {
+ this.data = data.clone();
+ }
+
+ public void setQueryDuration(long queryDuration) {
+ this.queryDuration = queryDuration;
+ }
+
+ public void setTotalResults(long totalResults) {
+ this.totalResults = totalResults;
+ }
+
+ public void setFrom(int from) {
+ this.from = from;
+ }
+
+ public void setTo(int to) {
+ this.to = to;
+ }
+
+ public long getQueryDuration() {
+ return queryDuration;
+ }
+
+ public long getTotalResults() {
+ return totalResults;
+ }
+
+ public int getFrom() {
+ return from;
+ }
+
+ public int getTo() {
+ return to;
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private String[] types;
+ private T[] data;
+ private long queryDuration;
+ private long totalResults;
+ private int from;
+ private int to;
+
+ /**
+ * Construct an object only with data and types
+ *
+ * @param types
+ * @param data
+ */
+ public GetMultipleDataResult(String[] types, T[] data) {
+ this.types = types.clone();
+ this.data = data.clone();
+ }
+
+ public GetMultipleDataResult(String[] types, Object[] data, long queryDuration, long totalResults, int from,
+ int to) {
+
+ this.types = types.clone();
+ this.data = (T[]) data.clone();
+ this.queryDuration = queryDuration;
+ this.totalResults = totalResults;
+ this.from = from;
+ this.to = to;
+ }
+
+ public GetMultipleDataResult() {
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/BatchBuilder.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/BatchBuilder.java
new file mode 100644
index 0000000000..0177d0bd70
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/BatchBuilder.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+
+public class BatchBuilder {
+ // private Map<String, List<Neo4jNode>> nodes;
+ // private List<Neo4jRelation> relations;
+ //
+ private List<GraphElement> elements;
+
+ // TODO add filter
+
+ protected BatchBuilder() {
+ // nodes = new HashMap<String, List<Neo4jNode>>();
+ // relations = new ArrayList<Neo4jRelation>();
+ elements = new ArrayList<GraphElement>();
+ }
+
+ public static BatchBuilder getBuilder() {
+ return new BatchBuilder();
+ }
+
+ public BatchBuilder add(GraphElement element) {
+ elements.add(element);
+ return this;
+ }
+
+ public List<GraphElement> getElements() {
+ return elements;
+ }
+
+ // public BatchBuilder add( Neo4jNode element ){
+ // String label = element.getLabel();
+ // List<Neo4jNode> list = nodes.get(label);
+ // if ( list == null ){
+ // list = new ArrayList<Neo4jNode>();
+ // }
+ // list.add(element);
+ // nodes.put(label, list);
+
+ // return this;
+ // }
+ // public BatchBuilder add( Neo4jRelation relation ){
+ // relations.add(relation);
+ // return this;
+ // }
+ //
+ // public Map<String, List<Neo4jNode>> getNodes() {
+ // return nodes;
+ // }
+ //
+ // public List<Neo4jRelation> getRelations() {
+ // return relations;
+ // }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTemplates.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTemplates.java
new file mode 100644
index 0000000000..b0b2cc20bb
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTemplates.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+public interface CypherTemplates {
+
+ public static final String CypherUrlTemplate = "http://$host$:$port$/db/data/transaction/commit";
+ public static final String batchTemplate = "http://$host$:$port$/db/data/batch";
+ public static final String getAllIndexsTemplate = "http://$host$:$port$/db/data/schema/index";
+
+ public static final String CypherCreateNodeTemplate = "{\n\"statements\" : [ {\n \"statement\" : \"CREATE (n:$label$ { props } ) RETURN n\",\n \"parameters\" : { \n \"props\" : $props$ \n } } ] }";
+
+ public static final String CypherMatchTemplate = "{\"statements\": [{\"statement\": \"MATCH (n:$label$ {$filter$}) RETURN ($type$) \" }]}";
+
+ public static final String CypherUpdateTemplate = "{\"statements\": [{\"statement\": \"MATCH (n:$label$ {$filter$}) SET n += {props} RETURN ($type$) \",\"parameters\" : {\"props\" : {$props$}}}]}";
+ public static final String CypherDeleteNodeTemplate = "{\"statements\": [{\"statement\": \"MATCH ( n:$label$ {$filter$} ) DELETE n \" }]}";
+
+ public static final String BatchTemplate = "{ \"statements\" : [ $statementList$ ] }";
+
+ public static final String RegularStatementTemplate = "{ \"statement\" : $statement$ }";
+
+ public static final String CreateSingleNodeTemplate = " \"CREATE (n:$label$ { props } ) RETURN n, labels(n)\", \"parameters\" : { \"props\" : $props$ }";
+
+ public static final String CreateRelationTemplate = "\"MATCH (a:$labelFrom$),(b:$labelTo$) WHERE a.$idNameFrom$ = '$idValueFrom$' AND b.$idNameTo$ = '$idvalueTo$' CREATE (a)-[r:$type$ { props } ]->(b) RETURN a, labels(a), b, labels(b), r, type(r)\", \"parameters\": {\"props\": $props$ } ";
+
+ public static final String CreateRelationTemplateNoProps = "\"MATCH (a:$labelFrom$),(b:$labelTo$) WHERE a.$idNameFrom$ = '$idValueFrom$' AND b.$idNameTo$ = '$idvalueTo$' CREATE (a)-[r:$type$ ]->(b) RETURN a,labels(a), b, labels(b), r, type(r)\"";
+
+ public static final String UpdateNodeStatementTemplate = "\"MATCH (n:$label$ {$filter$}) SET n += {props} \",\"parameters\" : {\"props\" : $props$}";
+
+ public static final String GetNodeRecursiveTemplate = "\"MATCH (m:$label$ {$filter$} )-[f$typesList$]->l RETURN m, labels(m), l, labels(l),f, type(f)\"";
+
+ public static final String GetByRelationNodeRecursiveTemplate = "\"MATCH (n:$labelNode$ ($propsNode$} )-[r:$type$ {$propsRel$}]->(m:$labelSrc$)-[f$typesList$]->l RETURN n, labels(n), r, type(r), m, labels(m), l, labels(l),f, type(f)\"";
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTranslator.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTranslator.java
new file mode 100644
index 0000000000..e1409b3bb1
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/CypherTranslator.java
@@ -0,0 +1,251 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
+import org.openecomp.sdc.be.dao.neo4j.filters.RecursiveByRelationFilter;
+import org.openecomp.sdc.be.dao.neo4j.filters.RecursiveFilter;
+import org.openecomp.sdc.be.dao.utils.DaoUtils;
+
+public class CypherTranslator {
+
+ public String translate(BatchBuilder builder) {
+ String json = null;
+ StringBuilder statementList = new StringBuilder();
+
+ List<GraphElement> elements = builder.getElements();
+ int statementCounter = 0;
+ for (GraphElement element : elements) {
+ String singleStatementBody = null;
+ switch (element.getElementType()) {
+ case Node:
+ singleStatementBody = prepareNodeStatement(element);
+ break;
+ case Relationship:
+ singleStatementBody = prepareRelationStatement(element);
+ break;
+ }
+ if (singleStatementBody != null && !singleStatementBody.isEmpty()) {
+
+ String singleStatement = CypherTemplates.RegularStatementTemplate.replace("$statement$",
+ singleStatementBody);
+
+ statementList.append(singleStatement);
+ }
+ ++statementCounter;
+ if (statementCounter < elements.size() && singleStatementBody != null) {
+ statementList.append(",");
+ }
+
+ }
+ json = CypherTemplates.BatchTemplate.replace("$statementList$", statementList.toString());
+ return json;
+ }
+
+ private String prepareNodeStatement(GraphElement element) {
+ if (element instanceof GraphNode) {
+ GraphNode node = (GraphNode) element;
+
+ switch (node.getAction()) {
+ case Create:
+ return createNodeStatement(node);
+ case Update:
+ return updateNodeStatement(node);
+ case Delete:
+ // TODO
+ break;
+ default:
+ break;
+ }
+ }
+ return null;
+ }
+
+ private String updateNodeStatement(GraphNode node) {
+ String singleStatement = CypherTemplates.UpdateNodeStatementTemplate.replace("$label$", node.getLabel());
+ String filter = prepareKeyValueFilter(node);
+
+ singleStatement = singleStatement.replace("$filter$", filter);
+
+ singleStatement = singleStatement.replace("$props$", DaoUtils.convertToJson(node.toGraphMap()));
+
+ return singleStatement;
+ }
+
+ private String createNodeStatement(GraphNode node) {
+ String singleStatement = CypherTemplates.CreateSingleNodeTemplate.replace("$label$", node.getLabel());
+
+ singleStatement = singleStatement.replace("$props$", DaoUtils.convertToJson(node.toGraphMap()));
+ return singleStatement;
+ }
+
+ private String prepareRelationStatement(GraphElement element) {
+ if (element instanceof GraphRelation) {
+
+ GraphRelation relation = (GraphRelation) element;
+
+ switch (relation.getAction()) {
+ case Create:
+ return createRelationStatement(relation);
+ case Update:
+ return updateRelationStatement(relation);
+ case Delete:
+ // TODO
+ break;
+ default:
+ break;
+ }
+ }
+ return null;
+ }
+
+ private String createRelationStatement(GraphRelation relation) {
+ RelationEndPoint from = relation.getFrom();
+ String singleStatement;
+
+ Map<String, Object> props = relation.toGraphMap();
+ if (props == null || props.isEmpty()) {
+ singleStatement = CypherTemplates.CreateRelationTemplateNoProps.replace("$labelFrom$",
+ from.getLabel().getName());
+ } else {
+ singleStatement = CypherTemplates.CreateRelationTemplate.replace("$labelFrom$", from.getLabel().getName());
+ singleStatement = singleStatement.replace("$props$", DaoUtils.convertToJson(props));
+ }
+
+ singleStatement = singleStatement.replace("$idNameFrom$", from.getIdName());
+ singleStatement = singleStatement.replace("$idValueFrom$", from.getIdValue().toString());
+
+ RelationEndPoint to = relation.getTo();
+ singleStatement = singleStatement.replace("$labelTo$", to.getLabel().getName());
+ singleStatement = singleStatement.replace("$idNameTo$", to.getIdName());
+ singleStatement = singleStatement.replace("$idvalueTo$", to.getIdValue().toString());
+
+ singleStatement = singleStatement.replace("$type$", relation.getType());
+ return singleStatement;
+ }
+
+ private String updateRelationStatement(GraphRelation relation) {
+ // TODO
+ return null;
+ }
+
+ private String prepareKeyValueFilter(GraphNode node) {
+ StringBuilder sb = new StringBuilder();
+
+ ImmutablePair<String, Object> keyValueId = node.getKeyValueId();
+
+ sb.append(keyValueId.getKey()).append(":");
+ if (keyValueId.getValue() instanceof String) {
+ sb.append("'");
+ }
+ sb.append(keyValueId.getValue());
+
+ if (keyValueId.getValue() instanceof String) {
+ sb.append("'");
+ }
+
+ return sb.toString();
+ }
+
+ public String translateGet(RecursiveFilter filter) {
+ String requestJson = null;
+ String statement;
+
+ if (filter instanceof RecursiveByRelationFilter) {
+ RecursiveByRelationFilter byRelationFilter = (RecursiveByRelationFilter) filter;
+
+ statement = CypherTemplates.GetByRelationNodeRecursiveTemplate.replace("$labelNode$",
+ byRelationFilter.getNode().getLabel());
+ String keyValueId = prepareKeyValueFilter(byRelationFilter.getNode());
+
+ statement = statement.replace("$propsNode$", keyValueId);
+
+ statement = statement.replace("$type$", byRelationFilter.getRelationType());
+
+ String relationProps = prepareFilterBody(filter);
+ statement = statement.replace("$propsRel$", relationProps);
+ statement = statement.replace("$labelSrc$", filter.getNodeType().getName());
+
+ } else {
+
+ statement = CypherTemplates.GetNodeRecursiveTemplate.replace("$label$", filter.getNodeType().getName());
+
+ // replace filter
+ if (filter.getProperties().isEmpty()) {
+ // get all records by label
+ statement = statement.replace("{$filter$}", "");
+ } else {
+ String filterStr = prepareFilterBody(filter);
+ statement = statement.replace("$filter$", filterStr);
+ }
+ }
+
+ if (filter.getChildRelationTypes() == null || filter.getChildRelationTypes().isEmpty()) {
+ statement = statement.replace("$typesList$", "");
+
+ } else {
+ StringBuilder typesList = new StringBuilder();
+ int count = 0;
+ for (String type : filter.getChildRelationTypes()) {
+ typesList.append(":").append(type);
+ ++count;
+ if (count < filter.getChildRelationTypes().size()) {
+ typesList.append("|");
+ }
+ }
+ statement = statement.replace("$typesList$", typesList.toString());
+ }
+ String singleStatement = CypherTemplates.RegularStatementTemplate.replace("$statement$", statement);
+ requestJson = CypherTemplates.BatchTemplate.replace("$statementList$", singleStatement);
+
+ return requestJson;
+ }
+
+ public static String prepareFilterBody(MatchFilter filter) {
+ StringBuilder sb = new StringBuilder();
+ int count = 0;
+ int size = filter.getProperties().entrySet().size();
+ for (Map.Entry<String, Object> entry : filter.getProperties().entrySet()) {
+ sb.append(entry.getKey()).append(":");
+ if (entry.getValue() instanceof String) {
+ sb.append("'");
+ }
+ sb.append(entry.getValue());
+ if (entry.getValue() instanceof String) {
+ sb.append("'");
+ }
+ ++count;
+ if (count < size) {
+ sb.append(",");
+ }
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgeLabels.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgeLabels.java
new file mode 100644
index 0000000000..917bcbb986
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgeLabels.java
@@ -0,0 +1,101 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum GraphEdgeLabels {
+
+ // field name
+ //
+ STATE("STATE"), LAST_STATE("LAST_STATE"), CREATOR("CREATOR"), LAST_MODIFIER("LAST_MODIFIER"), ATTRIBUTE(
+ "EDGE_ATTRIBUTE"), PROPERTY("EDGE_PROPERTY"), CATEGORY("CATEGORY"), DERIVED_FROM(
+ "DERIVED_FROM"), REQUIREMENT("REQUIREMENT"), CAPABILITY_TYPE("CAPABILITY_TYPE"), RELATIONSHIP_TYPE(
+ "RELATIONSHIP_TYPE"), CAPABILITY("CAPABILITY"), INSTANCE_OF("INSTANCE_OF"), INTERFACE(
+ "INTERFACE"), INTERFACE_OPERATION("INTERFACE_OPERATION"), ARTIFACT_REF(
+ "ARTIFACT_REF"), INPUTS("INPUTS"), REQUIREMENT_IMPL(
+ "REQUIREMENT_IMPL"), NODE_IMPL("NODE_IMPL"), IMPLEMENTATION_OF(
+ "IMPLEMENTATION_OF"), ATTRIBUTE_VALUE(
+ "ATTRIBUTE_VALUE"), INPUT_VALUE(
+ "INPUT_VALUE"), PROPERTY_VALUE(
+ "PROPERTY_VALUE"), CAPABILITY_INST(
+ "CAPABILITY_INST"), TYPE_OF(
+ "TYPE_OF"), RESOURCE_INST(
+ "RESOURCE_INST"), RELATIONSHIP_INST(
+ "RELATIONSHIP_INST"), CAPABILITY_NODE(
+ "CAPABILITY_NODE"), LAST_DISTRIBUTION_STATE_MODIFAIER(
+ "LAST_DISTRIBUTION_STATE_MODIFAIER"), ATTRIBUTE_IMPL(
+ "ATTRIBUTE_IMPL"), INPUT_IMPL(
+ "INPUT_IMPL"), PROPERTY_IMPL(
+ "PROPERTY_IMPL"), ADDITIONAL_INFORMATION(
+ "ADDITIONAL_INFORMATION"), HEAT_PARAMETER(
+ "HEAT_PARAMETER"), SUB_CATEGORY(
+ "SUB_CATEGORY"), GROUPING(
+ "GROUPING"), CATEGORIZED_TO(
+ "CATEGORIZED_TO"), GENERATED_FROM(
+ "GENERATED_FROM"), PARAMETER_VALUE(
+ "PARAMETER_VALUE"), PARAMETER_IMPL(
+ "PARAMETER_IMPL"),
+ // VF additions
+ CALCULATED_REQUIREMENT("CALCULATED_REQUIREMENT"), CALCULATED_CAPABILITY(
+ "CALCULATED_CAPABILITY"), RELATIONSHIP_ORIGIN("RELATIONSHIP_ORIGIN"), CAPABILITY_ORIGIN(
+ "CAPABILITY_ORIGIN"), CALCULATED_REQUIREMENT_FULLFILLED(
+ "CALCULATED_REQUIREMENT_FULLFILLED"), CALCULATED_CAPABILITY_FULLFILLED(
+ "CALCULATED_CAPABILITY_FULLFILLED"),
+ // Group
+ GROUP("GROUP"), GROUP_ARTIFACT_REF("GROUP_ARTIFACT_REF"), GROUP_MEMBER("GROUP_MEMBER"), INPUT(
+ "EDGE_INPUT"), GET_INPUT("GET_INPUT"),;
+
+ private String property;
+
+ GraphEdgeLabels(String property) {
+ this.property = property;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public static List<String> getAllProperties() {
+
+ List<String> arrayList = new ArrayList<String>();
+
+ for (GraphEdgeLabels graphProperty : GraphEdgeLabels.values()) {
+ arrayList.add(graphProperty.getProperty());
+ }
+
+ return arrayList;
+ }
+
+ public static GraphEdgeLabels getByName(String property) {
+ for (GraphEdgeLabels inst : GraphEdgeLabels.values()) {
+ if (inst.getProperty().equals(property)) {
+ return inst;
+ }
+ }
+ return null;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgePropertiesDictionary.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgePropertiesDictionary.java
new file mode 100644
index 0000000000..18c9ecb6f7
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphEdgePropertiesDictionary.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public enum GraphEdgePropertiesDictionary {
+
+ // field name class type
+ // stored in graph
+ STATE("state", String.class), NAME("name", String.class), GROUP_TYPE("groupType", String.class), SOURCE("source",
+ String.class), OWNER_ID("ownerId", String.class), REQUIRED_OCCURRENCES("requiredOccurrences",
+ String.class), LEFT_OCCURRENCES("leftOccurrences",
+ String.class), GET_INPUT_INDEX("get_input_index", String.class);
+
+ private String property;
+ private Class clazz;
+
+ GraphEdgePropertiesDictionary(String property, Class clazz) {
+ this.property = property;
+ this.clazz = clazz;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public Class getClazz() {
+ return clazz;
+ }
+
+ public void setClazz(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ public static List<String> getAllProperties() {
+
+ List<String> arrayList = new ArrayList<String>();
+
+ for (GraphEdgePropertiesDictionary graphProperty : GraphEdgePropertiesDictionary.values()) {
+ arrayList.add(graphProperty.getProperty());
+ }
+
+ return arrayList;
+ }
+
+ public static GraphEdgePropertiesDictionary getByName(String property) {
+ for (GraphEdgePropertiesDictionary inst : GraphEdgePropertiesDictionary.values()) {
+ if (inst.getProperty().equals(property)) {
+ return inst;
+ }
+ }
+ return null;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphNeighbourTable.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphNeighbourTable.java
new file mode 100644
index 0000000000..ccfb57b145
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphNeighbourTable.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+
+public class GraphNeighbourTable {
+
+ List<GraphNode> nodes = new ArrayList<GraphNode>();
+
+ List<NodeRelation> directedEdges = new ArrayList<NodeRelation>();
+
+ public List<GraphNode> getNodes() {
+ return nodes;
+ }
+
+ public void setNodes(List<GraphNode> nodes) {
+ this.nodes = nodes;
+ }
+
+ public List<NodeRelation> getDirectedEdges() {
+ return directedEdges;
+ }
+
+ public void setDirectedEdges(List<NodeRelation> directedEdges) {
+ this.directedEdges = directedEdges;
+ }
+
+ public int addNode(GraphNode node) {
+ this.nodes.add(node);
+ return this.nodes.size() - 1;
+ }
+
+ public void addEdge(NodeRelation directedEdge) {
+ this.directedEdges.add(directedEdge);
+ }
+
+ @Override
+ public String toString() {
+ return "GraphNeighbourTable [nodes=" + nodes + ", directedEdges=" + directedEdges + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphPropertiesDictionary.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphPropertiesDictionary.java
new file mode 100644
index 0000000000..a042ab510e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/GraphPropertiesDictionary.java
@@ -0,0 +1,212 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public enum GraphPropertiesDictionary {
+// field name class type unique indexed
+// stored in graph index
+ // Common
+ LABEL ("nodeLabel", String.class, false, true),
+ HEALTH_CHECK ("healthcheckis", String.class, true, true), //yavivi
+ // Resource
+ NAME ("name", String.class, false, true),
+ TOSCA_RESOURCE_NAME ("toscaResourceName", String.class, false, true),
+ CATEGORY_NAME ("categoryName", String.class, false, true), // ?
+ VERSION ("version", String.class, false, true),
+ CREATION_DATE ("creationDate", Long.class, false, false),
+ LAST_UPDATE_DATE ("modificationDate", Long.class, false, false),
+ IS_HIGHEST_VERSION ("highestVersion", Boolean.class, false, true),
+ IS_ABSTRACT ("abstract", Boolean.class, false, true),
+ DESCRIPTION ("description", String.class, false, false),
+ UNIQUE_ID ("uid", String.class, true, true),
+ STATE ("state", String.class, false, true),
+ TYPE ("type", String.class, false, true),
+ REQUIRED ("required", Boolean.class, false, false),
+ DEFAULT_VALUE ("defaultValue", String.class, false, false),
+ CONSTRAINTS ("constraints", String.class, false, false),
+ CONTACT_ID ("contactId", String.class, false, false),
+ VENDOR_NAME ("vendorName", String.class, false, false),
+ VENDOR_RELEASE ("vendorRelease", String.class, false, false),
+ ICON ("icon", String.class, false, false),
+ TAGS ("tags", String.class, false, false),
+ UUID ("uuid", String.class, false, true),
+ COST ("cost", String.class, false, false),
+ LICENSE_TYPE ("licenseType", String.class, false, false),
+ NORMALIZED_NAME ("normalizedName", String.class, false, true),
+ SYSTEM_NAME ("systemName", String.class, false, true),
+ IS_DELETED ("deleted", Boolean.class, false, true),
+ RESOURCE_TYPE ("resourceType", String.class, false, true),
+ ENTRY_SCHEMA ("entry_schema", String.class, false, false),
+ CSAR_UUID ("csarUuid", String.class, false, true),
+ CSAR_VERSION ("csarVersion", String.class, false, true),
+ IMPORTED_TOSCA_CHECKSUM ("importedToscaChecksum", String.class, false, true),
+ GENERATED ("generated", Boolean.class, false, false),
+ // User
+ USER_ID ("userId", String.class, true, true),
+ EMAIL ("email", String.class, false, false),
+ FIRST_NAME ("firstName", String.class, false, false),
+ LAST_NAME ("lastName", String.class, false, false),
+ ROLE ("role", String.class, false, true),
+ USER_STATUS ("status", String.class, false, true),
+ VALID_SOURCE_TYPES ("validSourceTypes", String.class, false, false),
+ NODE ("node", String.class, false, false),
+ VALUE ("value", String.class, false, false),
+ HIDDEN ("Hidden", Boolean.class, false, false),
+ PROPERTIES ("properties", String.class, false, false),
+ POSITION_X ("positionX", String.class, false, false),
+ POSITION_Y ("positionY", String.class, false, false),
+ RELATIONSHIP_TYPE ("relationshipType", String.class, false, false),
+ ARTIFACT_TYPE ("artifactType", String.class, false, true),
+ ARTIFACT_REF ("artifactRef", String.class, false, false),
+ ARTIFACT_REPOSITORY ("artifactRepository", String.class, false, false),
+ ARTIFACT_CHECKSUM ("artifactChecksum", String.class, false, false),
+ CREATOR ("creator", String.class, false, false),
+ ATT_CREATOR ("attCreator", String.class, false, false),
+ LAST_UPDATER ("lastUpdater", String.class, false, false),
+ CREATOR_FULL_NAME ("creatorFullName", String.class, false, false),
+ UPDATER_FULL_NAME ("updaterFullName", String.class, false, false),
+ ES_ID ("esId", String.class, false, false),
+ ARTIFACT_LABEL ("artifactLabel", String.class, false, true),
+ ARTIFACT_DISPLAY_NAME("artifactDisplayName", String.class, false, true),
+ INSTANCE_COUNTER ("instanceCounter", Integer.class, false, false),
+ PROJECT_CODE ("projectCode", String.class, false, false),
+ DISTRIBUTION_STATUS ("distributionStatus", String.class, false, false),
+ IS_VNF ("isVNF", Boolean.class, false, false),
+ LAST_LOGIN_TIME ("lastLoginTime", Long.class, false, true),
+ ATTRIBUTE_COUNTER ("attributeCounter", Integer.class, false, false),
+ INPUT_COUNTER ("inputCounter", Integer.class, false, false),
+ PROPERTY_COUNTER ("propertyCounter", Integer.class, false, false),
+ API_URL ("apiUrl", String.class, false, false),
+ SERVICE_API ("serviceApi", Boolean.class, false, true),
+ ADDITIONAL_INFO_PARAMS ("additionalInfo", String.class, false, false),
+ ADDITIONAL_INFO_ID_TO_KEY ("idToKey", String.class, false, false),
+ ARTIFACT_GROUP_TYPE ("artifactGroupType", String.class, false, true),
+ ARTIFACT_TIMEOUT ("timeout", Integer.class, false, false),
+ IS_ACTIVE ("isActive", Boolean.class, false, true),
+ PROPERTY_VALUE_RULES ("propertyValueRules", String.class, false, false),
+ //authantication
+ CONSUMER_NAME ("consumerName", String.class, true, true),
+ CONSUMER_PASSWORD ("consumerPassword", String.class, false, false),
+ CONSUMER_SALT ("consumerSalt", String.class, false, false),
+ CONSUMER_LAST_AUTHENTICATION_TIME ("consumerLastAuthenticationTime", Long.class, false, false),
+ CONSUMER_DETAILS_LAST_UPDATED_TIME ("consumerDetailsLastupdatedtime", Long.class, false, false),
+ LAST_MODIFIER_USER_ID("lastModfierUserId", String.class, false, false),
+ ARTIFACT_VERSION ("artifactVersion", String.class, false, false),
+ ARTIFACT_UUID ("artifactUUID", String.class, false, false),
+ PAYLOAD_UPDATE_DATE ("payloadUpdateDate", Long.class, false, false),
+ HEAT_PARAMS_UPDATE_DATE ("heatParamsUpdateDate",Long.class, false, false),
+ //product
+ FULL_NAME ("fullName", String.class, false, true),
+ //was changed as part of migration from 1602 to 1602 ( in 1602 was defined as unique. it's problem to reconfigure the index )
+ CONSTANT_UUID ("constantUuidNew", String.class, false, true),
+ CONTACTS ("contacts", String.class, false, false),
+ //categorys
+ ICONS ("icons", String.class, false, false),
+ //relation
+ CAPABILITY_OWNER_ID ("capOwnerId", String.class, false, false),
+ REQUIREMENT_OWNER_ID ("reqOwnerId", String.class, false, false),
+ CAPABILITY_ID ("capabiltyId", String.class, false, false),
+ REQUIREMENT_ID ("requirementId", String.class, false, false),
+ PROPERTY_ID ("propertyId", String.class, false, false),
+ PROPERTY_NAME ("propertyName", String.class, false, false),
+ //component instance
+ ORIGIN_TYPE ("originType", String.class, false, false),
+ //requirement & capabilty
+ MIN_OCCURRENCES ("minOccurrences", String.class, false, false),
+ MAX_OCCURRENCES ("maxOccurrences", String.class, false, false),
+ //Data type
+ DERIVED_FROM ("derivedFrom", String.class, false, false),
+ MEMBERS ("members", String.class, false, false),
+ TARGETS ("targets ", String.class, false, false),
+ METADATA ("metadata", String.class, false, false),
+ INVARIANT_UUID ("invariantUuid", String.class, false, true),
+ IS_BASE ("isBase", Boolean.class, false, true),
+ GROUP_UUID ("groupUuid", String.class, false, true),
+ STATUS ("status", String.class, false, false),
+ FUNCTIONAL_MENU ("functionalMenu", String.class, false, false),
+ REQUIRED_ARTIFACTS ("requiredArtifacts", String.class, false, false),
+ ;
+
+
+
+ private String property;
+ private Class clazz;
+ private boolean unique;
+ private boolean indexed;
+
+
+ GraphPropertiesDictionary(String property,Class clazz, boolean unique,boolean indexed) {
+ this.property = property;
+ this.clazz = clazz;
+ this.unique = unique;
+ this.indexed = indexed;
+ }
+
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public Class getClazz() {
+ return clazz;
+ }
+ public void setClazz(Class clazz) {
+ this.clazz = clazz;
+ }
+
+ public boolean isUnique() {
+ return unique;
+ }
+ public void setUnique(boolean unique) {
+ this.unique = unique;
+ }
+
+ public boolean isIndexed() {
+ return indexed;
+ }
+
+
+ public void setIndexed(boolean indexed) {
+ this.indexed = indexed;
+ }
+
+
+ public static List<String> getAllProperties() {
+
+ List<String> arrayList = new ArrayList<String>();
+
+ for (GraphPropertiesDictionary graphProperty : GraphPropertiesDictionary
+ .values()) {
+ arrayList.add(graphProperty.getProperty());
+ }
+
+ return arrayList;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jClient.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jClient.java
new file mode 100644
index 0000000000..8bab8d8a8f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jClient.java
@@ -0,0 +1,1004 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpResponseException;
+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.protocol.HttpClientContext;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.filters.MatchFilter;
+import org.openecomp.sdc.be.dao.neo4j.filters.RecursiveFilter;
+import org.openecomp.sdc.be.dao.neo4j.filters.UpdateFilter;
+import org.openecomp.sdc.be.dao.utils.DaoUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+//@Component("neo4j-client")
+public class Neo4jClient {
+ private CloseableHttpClient httpClient;
+ private JSONParser jsonParser;
+
+ private CypherTranslator cypherTranslator;
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jClient.class.getName());
+
+ private static final String getServiceRoot = "http://$host$:$port$/db/data/";
+ // Error's Classification templates
+ private static final String ClientError = "ClientError";
+ private static final String DatabaseError = "DatabaseError";
+ private static final String TransientError = "TransientError";
+
+ // Error's Category templates
+ private static final String General = "General";
+ private static final String LegacyIndex = "LegacyIndex";
+ private static final String Request = "Request";
+ private static final String Schema = "Schema";
+ private static final String Security = "Security";
+ private static final String Statement = "Statement";
+ private static final String Transaction = "Transaction";
+
+ // Error's Title templates
+ private static final String EntityNotFound = "EntityNotFound";
+ private static final String ConstraintViolation = "ConstraintViolation";
+
+ @PostConstruct
+ public void init() {
+
+ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+ connectionManager.setMaxTotal(100);
+ connectionManager.setDefaultMaxPerRoute(20);
+ connectionManager.setValidateAfterInactivity(15000);
+ this.httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
+ jsonParser = new JSONParser();
+ cypherTranslator = new CypherTranslator();
+
+ }
+
+ @PreDestroy
+ public void shutdown() {
+ try {
+ httpClient.close();
+ logger.debug("Http client to Neo4j Graph closed");
+ } catch (Exception e) {
+ logger.info("Failed to close http client", e);
+ }
+ }
+
+ /**
+ *
+ * @param builder
+ * @return
+ */
+ public Either<List<List<GraphElement>>, Neo4jOperationStatus> execute(BatchBuilder builder) {
+
+ String json = cypherTranslator.translate(builder);
+ logger.debug("Try to execute cypher request [ {} ]", json);
+
+ Either<String, Neo4jOperationStatus> result = sendPostCypher(json);
+ if (result.isRight()) {
+ return Either.right(result.right().value());
+ }
+ List<List<GraphElement>> batchResult;
+ try {
+ batchResult = parseResult(result.left().value(), false);
+ } catch (ParseException e) {
+ logger.error("Failed to parse batchresponse", e);
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+
+ return Either.left(batchResult);
+ }
+
+ public Either<List<List<GraphElement>>, Neo4jOperationStatus> executeGet(RecursiveFilter filter) {
+ String json = cypherTranslator.translateGet(filter);
+ logger.debug("Try to execute cypher request [ {} ]", json);
+
+ Either<String, Neo4jOperationStatus> result = sendPostCypher(json);
+ if (result.isRight()) {
+ return Either.right(result.right().value());
+ }
+ List<List<GraphElement>> batchResult;
+ try {
+ batchResult = parseResult(result.left().value(), true);
+ } catch (ParseException e) {
+ logger.error("Failed to parse batchresponse", e);
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+
+ return Either.left(batchResult);
+
+ }
+
+ /**
+ *
+ * @param element
+ * @param ip
+ * @param user
+ * @param password
+ * @return
+ */
+ public Neo4jOperationStatus createElement(GraphElement element) {
+ Neo4jOperationStatus result = Neo4jOperationStatus.OK;
+ switch (element.getElementType()) {
+ case Node:
+ Either<String, Neo4jOperationStatus> status = createNode(element);
+ if (status.isRight()) {
+ result = status.right().value();
+ }
+ break;
+ case Relationship:
+ // TODO
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ public Either<GraphElement, Neo4jOperationStatus> createSingleElement(GraphElement element) {
+ switch (element.getElementType()) {
+ case Node:
+ Either<String, Neo4jOperationStatus> status = createNode(element);
+ if (status.isRight()) {
+ return Either.right(status.right().value());
+ }
+ // parse response
+ String response = status.left().value();
+ try {
+ List<GraphElement> listElements = parseGetResponse(element.getElementType(),
+ ((GraphNode) element).getLabel(), response);
+ if (listElements == null || listElements.isEmpty()) {
+ return Either.right(Neo4jOperationStatus.NOT_FOUND);
+ } else {
+ return Either.left(listElements.get(0));
+ }
+ } catch (Exception e) {
+ logger.error("Failed to parse fetched data from graph", e);
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+ case Relationship:
+ // TODO
+ break;
+
+ default:
+ break;
+ }
+
+ return Either.right(Neo4jOperationStatus.NOT_SUPPORTED);
+ }
+
+ /**
+ *
+ * @param type
+ * @param label
+ * @param filter
+ * @param ip
+ * @param user
+ * @param password
+ * @return
+ */
+ public Either<List<GraphElement>, Neo4jOperationStatus> getByFilter(GraphElementTypeEnum type, String label,
+ MatchFilter filter) {
+
+ List<GraphElement> result = null;
+
+ String requestJson;
+ // replace return type
+ if (type.equals(GraphElementTypeEnum.Node)) {
+ requestJson = CypherTemplates.CypherMatchTemplate.replace("$type$", "n");
+ } else {
+ requestJson = CypherTemplates.CypherMatchTemplate.replace("$type$", "r");
+ }
+ // replace label
+ if (label != null && !label.isEmpty()) {
+ requestJson = requestJson.replace("$label$", label);
+ } else {
+ requestJson = requestJson.replace("$label$", "");
+ }
+
+ // replace filter
+ if (filter.getProperties().isEmpty()) {
+ // get all records by label
+ requestJson = requestJson.replace("{$filter$}", "");
+ } else {
+ String filterStr = CypherTranslator.prepareFilterBody(filter);
+ requestJson = requestJson.replace("$filter$", filterStr);
+ }
+ logger.debug("Try to perform request [ {} ]", requestJson);
+
+ Either<String, Neo4jOperationStatus> status = sendPostCypher(requestJson);
+ if (status.isRight()) {
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+ // parse response
+ String response = status.left().value();
+ try {
+ result = parseGetResponse(type, label, response);
+ } catch (Exception e) {
+ logger.error("Failed to parse fetched data from graph", e);
+ Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+
+ return Either.left(result);
+ }
+
+ /**
+ *
+ * @param type
+ * @param label
+ * @param toMatch
+ * @param toUpdate
+ * @param ip
+ * @param user
+ * @param password
+ * @return
+ */
+ public Neo4jOperationStatus updateElement(GraphElementTypeEnum type, String label, UpdateFilter toUpdate) {
+
+ String requestJson;
+ // replace return type
+ if (type.equals(GraphElementTypeEnum.Node)) {
+ requestJson = CypherTemplates.CypherUpdateTemplate.replace("$type$", "n");
+ } else {
+ requestJson = CypherTemplates.CypherUpdateTemplate.replace("$type$", "r");
+ }
+ // replace label
+ if (label != null && !label.isEmpty()) {
+ requestJson = requestJson.replace("$label$", label);
+ } else {
+ requestJson = requestJson.replace("$label$", "");
+ }
+
+ // replace filter
+ if (toUpdate.getProperties().isEmpty()) {
+ // get all records by label
+ requestJson = requestJson.replace("{$filter$}", "");
+ } else {
+ String filterStr = CypherTranslator.prepareFilterBody(toUpdate);
+ requestJson = requestJson.replace("$filter$", filterStr);
+ }
+ String props = preparePropertiesInStatement(toUpdate.getToUpdate());
+ requestJson = requestJson.replace("$props$", props);
+
+ logger.debug("Try to perform request [ {} ]", requestJson);
+
+ Either<String, Neo4jOperationStatus> result = sendPostCypher(requestJson);
+ if (result.isRight()) {
+ return Neo4jOperationStatus.GENERAL_ERROR;
+ }
+ return Neo4jOperationStatus.OK;
+ }
+
+ /**
+ *
+ * @param type
+ * @param label
+ * @param response
+ * @return
+ * @throws ParseException
+ */
+
+ private List<GraphElement> parseGetResponse(GraphElementTypeEnum type, String label, String response)
+ throws ParseException {
+ List<GraphElement> result = new ArrayList<GraphElement>();
+ JSONObject responseData = (JSONObject) jsonParser.parse(response);
+ JSONArray results = (JSONArray) responseData.get("results");
+ Iterator<JSONObject> iteratorResults = results.iterator();
+ while (iteratorResults.hasNext()) {
+ JSONObject elementResult = iteratorResults.next();
+ // JSONArray data = (JSONArray) elementResult.get("row");
+ JSONArray data = (JSONArray) elementResult.get("data");
+
+ Iterator<JSONObject> iterator = data.iterator();
+ JSONObject element;
+ while (iterator.hasNext()) {
+ element = (JSONObject) iterator.next();
+ JSONArray row = (JSONArray) element.get("row");
+
+ Iterator<JSONObject> iteratorRow = row.iterator();
+ while (iteratorRow.hasNext()) {
+ JSONObject rowElement = iteratorRow.next();
+
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) rowElement.entrySet()) {
+ // props.put(entry.getKey(),
+ // rowElement.get(entry.getValue()));
+ props.put(entry.getKey(), entry.getValue());
+ }
+ GraphElement newElement = GraphElementFactory.createElement(label, type, props);
+ result.add(newElement);
+ }
+ }
+ }
+ return result;
+ }
+
+ private List<List<GraphElement>> parseResult(String response, boolean storeRelationNode) throws ParseException {
+
+ List<List<GraphElement>> batchList = new ArrayList<List<GraphElement>>();
+
+ JSONObject responseData = (JSONObject) jsonParser.parse(response);
+ JSONArray results = (JSONArray) responseData.get("results");
+ Iterator<JSONObject> iteratorResults = results.iterator();
+
+ while (iteratorResults.hasNext()) {
+ JSONObject elementResult = iteratorResults.next();
+ JSONArray data = (JSONArray) elementResult.get("data");
+ JSONArray columns = (JSONArray) elementResult.get("columns");
+ Iterator<JSONObject> iteratorData = data.iterator();
+ List<GraphElement> singleDataList = new ArrayList<GraphElement>();
+ while (iteratorData.hasNext()) {
+
+ JSONObject singleData = iteratorData.next();
+ JSONArray row = (JSONArray) singleData.get("row");
+ if (columns.size() == 2) {
+ // node
+ JSONArray labelArray = (JSONArray) row.get(1);
+ JSONObject node = (JSONObject) row.get(0);
+
+ Map<String, Object> props = jsonObjectToMap(node);
+ // get only first label on node. Now single label supported
+ GraphElement newElement = GraphElementFactory.createElement((String) labelArray.get(0),
+ GraphElementTypeEnum.Node, props);
+ singleDataList.add(newElement);
+ }
+ if (columns.size() == 10) {
+ // relation
+ JSONObject startNode = (JSONObject) row.get(0);
+ JSONArray startNodeArray = (JSONArray) row.get(1);
+
+ JSONObject relationFromStart = (JSONObject) row.get(2);
+ String relationFromStartType = (String) row.get(3);
+
+ JSONObject nodeFrom = (JSONObject) row.get(4);
+ JSONArray labelFromArray = (JSONArray) row.get(5);
+
+ JSONObject nodeTo = (JSONObject) row.get(6);
+ JSONArray labelToArray = (JSONArray) row.get(7);
+
+ JSONObject relation = (JSONObject) row.get(8);
+ String type = (String) row.get(9);
+
+ Map<String, Object> propsStartNode = jsonObjectToMap(startNode);
+ Map<String, Object> propsRelationStartNode = jsonObjectToMap(relationFromStart);
+
+ Map<String, Object> propsFrom = jsonObjectToMap(nodeFrom);
+ Map<String, Object> propsTo = jsonObjectToMap(nodeTo);
+ Map<String, Object> propsRelation = jsonObjectToMap(relation);
+
+ GraphNode startN = (GraphNode) GraphElementFactory.createElement((String) startNodeArray.get(0),
+ GraphElementTypeEnum.Node, propsStartNode);
+
+ GraphNode from = (GraphNode) GraphElementFactory.createElement((String) labelFromArray.get(0),
+ GraphElementTypeEnum.Node, propsFrom);
+ GraphNode to = (GraphNode) GraphElementFactory.createElement((String) labelToArray.get(0),
+ GraphElementTypeEnum.Node, propsTo);
+
+ singleDataList.add(startN);
+
+ GraphElement relationFromStartNode = GraphElementFactory.createRelation(type,
+ propsRelationStartNode, startN, from);
+ singleDataList.add(relationFromStartNode);
+
+ singleDataList.add(from);
+ singleDataList.add(to);
+ // get only first type on relationship. Now single type
+ // supported
+ GraphElement newElement = GraphElementFactory.createRelation(type, propsRelation, from, to);
+ singleDataList.add(newElement);
+ }
+ if (columns.size() == 8) {
+
+ }
+ }
+ batchList.add(singleDataList);
+ }
+ return batchList;
+ }
+
+ private Map<String, Object> jsonObjectToMap(JSONObject node) {
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ for (Map.Entry<String, Object> entry : (Set<Map.Entry<String, Object>>) node.entrySet()) {
+ props.put(entry.getKey(), entry.getValue());
+ }
+ return props;
+ }
+
+ private String preparePropertiesInStatement(Map<String, Object> properties) {
+ StringBuilder sb = new StringBuilder();
+ int count = 0;
+ int size = properties.entrySet().size();
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ sb.append("\"").append(entry.getKey()).append("\"").append(":");
+ if (entry.getValue() instanceof String) {
+ sb.append("\"");
+ }
+ sb.append(entry.getValue());
+ if (entry.getValue() instanceof String) {
+ sb.append("\"");
+ }
+ ++count;
+ if (count < size) {
+ sb.append(",");
+ }
+ }
+ return sb.toString();
+ }
+
+ private Either<String, Neo4jOperationStatus> createNode(GraphElement element) {
+ Either<String, Neo4jOperationStatus> status;
+ if (element instanceof GraphNode) {
+ GraphNode node = (GraphNode) element;
+ String json = prepareCreateNodeBody(node);
+
+ logger.debug("Try to save Node [ {} ]", json);
+
+ status = sendPostCypher(json);
+
+ return status;
+
+ } else {
+ return Either.right(Neo4jOperationStatus.WRONG_INPUT);
+ }
+ }
+
+ private Either<String, Neo4jOperationStatus> sendPostCypher(String json) {
+ Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration().getNeo4j();
+ String host = (String) neo4jParams.get("host");
+ Integer port = (Integer) neo4jParams.get("port");
+ String user = (String) neo4jParams.get("user");
+ String password = (String) neo4jParams.get("password");
+
+ String uri = CypherTemplates.CypherUrlTemplate.replace("$host$", host);
+ uri = uri.replace("$port$", port.toString());
+
+ HttpClientContext context = creatClientContext(host, user, password);
+ CloseableHttpResponse response = null;
+
+ HttpPost post = new HttpPost(uri);
+ try {
+ StringEntity input = new StringEntity(json);
+ input.setContentType("application/json");
+ post.setEntity(input);
+
+ response = httpClient.execute(post, context);
+
+ int status = response.getStatusLine().getStatusCode();
+ String responseString;
+ responseString = new BasicResponseHandler().handleResponse(response);
+ logger.debug("response [ {} ]", responseString);
+
+ if (status == 200 || status == 201) {
+ logger.debug("cypher request [ {} ]", json);
+ Neo4jOperationStatus responseStatus = checkResponse(responseString);
+ if (Neo4jOperationStatus.OK.equals(responseStatus)) {
+ return Either.left(responseString);
+ } else {
+ return Either.right(responseStatus);
+ }
+ } else {
+ logger.debug("cypher request [ {} ] was failed: [ {} ]", json, responseString);
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+
+ } catch (HttpResponseException e) {
+ logger.debug("failed to perform cypher request [ {} ], {}", json, e);
+ if (e.getStatusCode() == 401) {
+ return Either.right(Neo4jOperationStatus.NOT_AUTHORIZED);
+ } else {
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ }
+ } catch (ClientProtocolException e) {
+ logger.debug("failed to perform cypher request [ {} ] {}", json, e);
+ return Either.right(Neo4jOperationStatus.HTTP_PROTOCOL_ERROR);
+ } catch (IOException e) {
+ logger.debug("failed to perform cypher request [ {} ] {}", json, e);
+ return Either.right(Neo4jOperationStatus.NOT_CONNECTED);
+ } finally {
+ releaseResource(response);
+ }
+ }
+
+ private Neo4jOperationStatus checkResponse(String responseString) {
+ try {
+ JSONObject response = (JSONObject) jsonParser.parse(responseString);
+ JSONArray errors = (JSONArray) response.get("errors");
+ if (errors.size() == 0) {
+ return Neo4jOperationStatus.OK;
+ } else {
+ Iterator<JSONObject> iterator = errors.iterator();
+ JSONObject error;
+ while (iterator.hasNext()) {
+ error = (JSONObject) iterator.next();
+ String code = (String) error.get("code");
+ String message = (String) error.get("message");
+
+ Neo4jOperationStatus neoError = mapToNeoError(code, message);
+ return neoError;
+ }
+ return Neo4jOperationStatus.GENERAL_ERROR;
+ }
+ } catch (ParseException e) {
+ logger.error("Failed to parse response", e);
+ return Neo4jOperationStatus.GENERAL_ERROR;
+ }
+ }
+
+ private Neo4jOperationStatus mapToNeoError(String code, String message) {
+ Neo4jOperationStatus error;
+
+ String[] errorCode = code.split("\\.");
+ if (errorCode.length < 4) {
+ error = Neo4jOperationStatus.GENERAL_ERROR;
+ } else {
+ // by Classification
+ switch (errorCode[1]) {
+ case ClientError:
+ // by Category
+ switch (errorCode[2]) {
+ case General:
+ error = Neo4jOperationStatus.DB_READ_ONLY;
+ break;
+ case LegacyIndex:
+ error = Neo4jOperationStatus.LEGACY_INDEX_ERROR;
+ break;
+ case Request:
+ error = Neo4jOperationStatus.BAD_REQUEST;
+ break;
+ case Schema:
+ if (errorCode[3].equals(ConstraintViolation)) {
+ error = Neo4jOperationStatus.ENTITY_ALREADY_EXIST;
+ } else {
+ error = Neo4jOperationStatus.SCHEMA_ERROR;
+ }
+ break;
+ case Security:
+ error = Neo4jOperationStatus.NOT_AUTHORIZED;
+ break;
+ case Statement:
+ // by Title
+ if (errorCode[3].equals(EntityNotFound)) {
+ error = Neo4jOperationStatus.NOT_FOUND;
+ } else {
+ if (errorCode[3].equals(ConstraintViolation)) {
+ error = Neo4jOperationStatus.ENTITY_ALREADY_EXIST;
+ } else {
+ error = Neo4jOperationStatus.BAD_REQUEST;
+ }
+ }
+ break;
+ case Transaction:
+ error = Neo4jOperationStatus.TRANSACTION_ERROR;
+ break;
+ default:
+ error = Neo4jOperationStatus.GENERAL_ERROR;
+ break;
+ }
+ break;
+ case DatabaseError:
+ // by Category
+ switch (errorCode[2]) {
+ case General:
+ error = Neo4jOperationStatus.GENERAL_ERROR;
+ break;
+ case Schema:
+ error = Neo4jOperationStatus.SCHEMA_ERROR;
+ break;
+ case Statement:
+ error = Neo4jOperationStatus.EXECUTION_FAILED;
+ break;
+ case Transaction:
+ error = Neo4jOperationStatus.TRANSACTION_ERROR;
+ break;
+ default:
+ error = Neo4jOperationStatus.GENERAL_ERROR;
+ break;
+ }
+ break;
+ case TransientError:
+ error = Neo4jOperationStatus.DB_NOT_AVAILABLE;
+ break;
+ default:
+ error = Neo4jOperationStatus.GENERAL_ERROR;
+ break;
+ }
+ error.setOriginError(code).setMessage(message);
+ String errorFromCfg = code.replace(".", "_");
+ String helpMessage = ConfigurationManager.getConfigurationManager().getNeo4jErrorsConfiguration()
+ .getErrorMessage(errorFromCfg);
+ if (helpMessage != null && !helpMessage.isEmpty()) {
+ error.setHelpErrorMsg(helpMessage);
+ }
+ }
+ return error;
+ }
+
+ private String prepareCreateNodeBody(GraphNode node) {
+
+ String body = CypherTemplates.CypherCreateNodeTemplate.replace("$label$", node.getLabel());
+
+ body = body.replace("$props$", DaoUtils.convertToJson(node.toGraphMap()));
+
+ return body;
+ }
+
+ /**
+ * the method returns all the indexes for the given label if no label is
+ * supplied ( null or "") all indexes will be returned
+ *
+ * @param label
+ * the name of the label
+ * @param ip
+ * @param user
+ * @param password
+ * @return a map of labels and there properties
+ */
+ public Either<Map<String, List<String>>, Neo4jOperationStatus> getIndexes(String label) {
+ Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration().getNeo4j();
+ String host = (String) neo4jParams.get("host");
+ Integer port = (Integer) neo4jParams.get("port");
+ String user = (String) neo4jParams.get("user");
+ String password = (String) neo4jParams.get("password");
+
+ String uri = null;
+ if (label == null || "".equals(label)) {
+ uri = CypherTemplates.getAllIndexsTemplate.replace("$host$", host);
+ } else {
+ uri = CypherTemplates.getAllIndexsTemplate.replace("$host$", host) + "/" + label;
+ }
+ uri = uri.replace("$port$", port.toString());
+
+ HttpClientContext context = creatClientContext(host, user, password);
+ CloseableHttpResponse response = null;
+
+ HttpGet get = new HttpGet(uri);
+ get.setHeader("Content-Type", "application/json");
+ get.setHeader("Accept", "application/json; charset=UTF-8");
+
+ try {
+
+ response = httpClient.execute(get, context);
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ logger.error("failed to get indexes requeste returned " + statusCode);
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ } else {
+ Map<String, List<String>> labels = getLeablesFromJson(response);
+ return Either.left(labels);
+ }
+ } catch (Exception e) {
+ logger.debug("failed to get indexes ", e);
+ return Either.right(Neo4jOperationStatus.GENERAL_ERROR);
+ } finally {
+ releaseResource(response);
+
+ }
+
+ }
+
+ private Map<String, List<String>> getLeablesFromJson(CloseableHttpResponse response)
+ throws HttpResponseException, IOException, ParseException {
+ Map<String, List<String>> labels = new HashMap<>();
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONArray results = (JSONArray) jsonParser.parse(responseString);
+ Iterator<JSONObject> iteratorResults = results.iterator();
+ while (iteratorResults.hasNext()) {
+ JSONObject elementResult = iteratorResults.next();
+ String label = (String) elementResult.get("label");
+ List<String> props = labels.get(label);
+ if (props == null) {
+ props = new ArrayList<>();
+ labels.put(label, props);
+ }
+ JSONArray properties = (JSONArray) elementResult.get("property_keys");
+ Iterator<String> iterator = properties.iterator();
+ while (iterator.hasNext()) {
+ props.add(iterator.next());
+ }
+ }
+ return labels;
+ }
+
+ public Neo4jOperationStatus createIndex(String label, List<String> propertyNames) {
+
+ Neo4jOperationStatus result = Neo4jOperationStatus.OK;
+ if (propertyNames != null && !propertyNames.isEmpty()) {
+
+ Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getNeo4j();
+ String host = (String) neo4jParams.get("host");
+ Integer port = (Integer) neo4jParams.get("port");
+ String user = (String) neo4jParams.get("user");
+ String password = (String) neo4jParams.get("password");
+
+ String uri = CypherTemplates.batchTemplate.replace("$host$", host);
+ uri = uri.replace("$port$", port.toString());
+
+ String opertionUri = "/schema/index/" + label;
+
+ HttpClientContext context = creatClientContext(host, user, password);
+
+ CloseableHttpResponse response = null;
+
+ HttpPost post = new HttpPost(uri);
+
+ String json = createBatchJson(HttpMethod.POST, opertionUri, propertyNames);
+
+ try {
+ StringEntity input = new StringEntity(json);
+ input.setContentType("application/json");
+ post.setEntity(input);
+ response = httpClient.execute(post, context);
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ logger.error("failed to create index for label [" + label + "] with properties:" + propertyNames
+ + " requeste returned " + statusCode);
+ result = Neo4jOperationStatus.GENERAL_ERROR;
+ } else {
+ logger.debug("index for label [ {} ] with properties {} created", label, propertyNames);
+ }
+ } catch (Exception e) {
+ logger.debug("failed to create index for label [ {} ] with properties {}", label, propertyNames);
+ result = Neo4jOperationStatus.GENERAL_ERROR;
+ } finally {
+
+ releaseResource(response);
+
+ }
+
+ }
+
+ else {
+ logger.debug("no index was created for label: {}, the received propertyNames list: {} is invalid", label, propertyNames);
+ return Neo4jOperationStatus.WRONG_INPUT;
+ }
+
+ return result;
+ }
+
+ public Neo4jOperationStatus createUniquenessConstraints(String label, List<String> propertyNames) {
+ Neo4jOperationStatus result = Neo4jOperationStatus.OK;
+ if (propertyNames != null && !propertyNames.isEmpty()) {
+
+ Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getNeo4j();
+ String host = (String) neo4jParams.get("host");
+ Integer port = (Integer) neo4jParams.get("port");
+ String user = (String) neo4jParams.get("user");
+ String password = (String) neo4jParams.get("password");
+
+ String uri = CypherTemplates.batchTemplate.replace("$host$", host);
+ uri = uri.replace("$port$", port.toString());
+
+ String opertionUri = "/schema/constraint/" + label + "/uniqueness/";
+
+ HttpClientContext context = creatClientContext(host, user, password);
+
+ CloseableHttpResponse response = null;
+
+ HttpPost post = new HttpPost(uri);
+
+ String json = createBatchJson(HttpMethod.POST, opertionUri, propertyNames);
+
+ try {
+ StringEntity input = new StringEntity(json);
+ input.setContentType("application/json");
+ post.setEntity(input);
+ response = httpClient.execute(post, context);
+
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ logger.error("failed to create uniqueness constraint for label [" + label + "] on properties:"
+ + propertyNames + ". request returned " + statusCode);
+ result = Neo4jOperationStatus.GENERAL_ERROR;
+ } else {
+ logger.debug("uniqueness constraint for label [ {} ] on properties {} created", label, propertyNames);
+ }
+ } catch (Exception e) {
+ logger.error("failed to create uniqueness constraint [ {} ] with properties {}, error: {}",label, propertyNames, e);
+ result = Neo4jOperationStatus.GENERAL_ERROR;
+ } finally {
+ releaseResource(response);
+ }
+
+ }
+
+ else {
+ logger.debug("no index was created for label: {} the received propertyNames list: {} is invalid", label, propertyNames);
+ return Neo4jOperationStatus.WRONG_INPUT;
+ }
+
+ return result;
+ }
+
+ public Neo4jOperationStatus deleteElement(GraphElementTypeEnum type, String label, MatchFilter filter) {
+
+ String requestJson;
+ // replace return type
+ if (type.equals(GraphElementTypeEnum.Node)) {
+ logger.debug("removing node label: {}", label);
+ requestJson = createDeleteNodeStatment(label, filter);
+
+ } else {
+ logger.error(" delete on type {} is not yet supported", type);
+ throw new RuntimeException(" delete on type " + type + " is not yet supported");
+ }
+
+ logger.debug("Try to perform request [ {} ]", requestJson);
+
+ Either<String, Neo4jOperationStatus> status = sendPostCypher(requestJson);
+ if (status.isRight()) {
+ logger.error(" delete request failed with {}", status.right());
+ return Neo4jOperationStatus.GENERAL_ERROR;
+ } else {
+ return Neo4jOperationStatus.OK;
+ }
+ }
+
+ public String getNeo4jVersion() throws Exception {
+ Map<String, Object> neo4jParams = ConfigurationManager.getConfigurationManager().getConfiguration().getNeo4j();
+ String host = (String) neo4jParams.get("host");
+ Integer port = (Integer) neo4jParams.get("port");
+ String user = (String) neo4jParams.get("user");
+ String password = (String) neo4jParams.get("password");
+
+ String uri = getServiceRoot.replace("$host$", host).replace("$port$", port.toString());
+
+ HttpClientContext context = creatClientContext(host, user, password);
+ CloseableHttpResponse response = null;
+ String result = null;
+
+ HttpGet get = new HttpGet(uri);
+ get.setHeader("Content-Type", "application/json");
+ get.setHeader("Accept", "application/json; charset=UTF-8");
+
+ try {
+ response = httpClient.execute(get, context);
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != 200) {
+ throw new Exception("Couldn't get Neo4j service root, HTTP status " + statusCode);
+ } else {
+ // Parse response
+ String responseString = new BasicResponseHandler().handleResponse(response);
+ JSONObject responseData = (JSONObject) jsonParser.parse(responseString);
+ Object obj = responseData.get("neo4j_version");
+ if (obj != null) {
+ result = (String) obj;
+ }
+ return result;
+ }
+ } finally {
+ releaseResource(response);
+ }
+ }
+
+ private String createDeleteNodeStatment(String label, MatchFilter filter) {
+ String requestJson;
+ requestJson = CypherTemplates.CypherDeleteNodeTemplate;
+
+ if (label != null && !label.isEmpty()) {
+ requestJson = requestJson.replace("$label$", label);
+ } else {
+ requestJson = requestJson.replace("$label$", "");
+ }
+
+ // replace filter
+ if (filter.getProperties().isEmpty()) {
+ // get all records by label
+ requestJson = requestJson.replace("{$filter$}", "");
+ } else {
+ String filterStr = CypherTranslator.prepareFilterBody(filter);
+ requestJson = requestJson.replace("$filter$", filterStr);
+ }
+ return requestJson;
+ }
+
+ /*
+ * removed do to fortify scan CredentialsProvider cp = new
+ * BasicCredentialsProvider(); cp.setCredentials(AuthScope.ANY, new
+ * UsernamePasswordCredentials(user, password)); AuthCache authCache = new
+ * BasicAuthCache(); BasicScheme basicAuth = new BasicScheme();
+ * authCache.put(new HttpHost(ip, 7474, "http"), basicAuth);
+ * context.setAuthCache(authCache); context.setCredentialsProvider(cp);
+ *
+ */
+ private HttpClientContext creatClientContext(String ip, String user, String password) {
+ HttpClientContext context = HttpClientContext.create();
+
+ return context;
+ }
+
+ private void releaseResource(CloseableHttpResponse response) {
+ if (response != null) {
+ try {
+ HttpEntity entity = response.getEntity();
+ EntityUtils.consume(entity);
+ response.close();
+ } catch (Exception e) {
+ logger.error("failed to close connection exception", e);
+ }
+ }
+ }
+
+ private String createBatchJson(HttpMethod method, String opertionUri, List<String> propertyNames) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ ");
+ for (int i = 0; i < propertyNames.size(); i++) {
+ sb.append("{ \"method\" : \"" + method + "\" , \"to\" : \"" + opertionUri
+ + "\" , \"body\" : { \"property_keys\" : [ \"" + propertyNames.get(i) + "\" ] } }");
+ if (i + 1 < propertyNames.size()) {
+ sb.append(",");
+ }
+ }
+ sb.append(" ]");
+ String json = sb.toString();
+ return json;
+ }
+
+ enum HttpMethod {
+ GET, PUT, POST, DELETE
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jEdge.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jEdge.java
new file mode 100644
index 0000000000..e8278a9a3e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jEdge.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.ActionEnum;
+
+public class Neo4jEdge {
+
+ private GraphEdgeLabels edgeType;
+ private Map<String, Object> properties;
+ private ActionEnum action;
+
+ public Neo4jEdge(GraphEdgeLabels edgeType, Map<String, Object> properties, ActionEnum actionEnum) {
+ super();
+ this.edgeType = edgeType;
+ this.properties = properties;
+ this.action = actionEnum;
+ }
+
+ public GraphEdgeLabels getEdgeType() {
+ return edgeType;
+ }
+
+ public void setEdgeType(GraphEdgeLabels edgeType) {
+ this.edgeType = edgeType;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.properties = properties;
+ }
+
+ public ActionEnum getAction() {
+ return action;
+ }
+
+ public void setAction(ActionEnum action) {
+ this.action = action;
+ }
+
+ @Override
+ public String toString() {
+ return "Neo4jEdge [edgeType=" + edgeType + ", properties=" + properties + ", action=" + action + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jGraphBatchBuilder.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jGraphBatchBuilder.java
new file mode 100644
index 0000000000..831fb001be
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jGraphBatchBuilder.java
@@ -0,0 +1,194 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.be.dao.graph.datatype.ActionEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElement;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class Neo4jGraphBatchBuilder {
+
+ private static Logger logger = LoggerFactory.getLogger(Neo4jGraphBatchBuilder.class.getName());
+
+ public Either<BatchBuilder, Neo4jOperationStatus> buildBatchBuilderFromTable(
+ GraphNeighbourTable graphNeighbourTable) {
+
+ logger.debug("The table sent in order to build BatchBuilder is {}", graphNeighbourTable);
+
+ List<GraphNode> nodes = graphNeighbourTable.getNodes();
+ if (nodes != null && nodes.size() > 0) {
+ // String resourceId = findResourceDataIdFromNodes(nodes);
+ // if (resourceId == null || resourceId.isEmpty()) {
+ // logger.error("Cannot find resource id in the graph table");
+ // return Either.right(ActionStatus.INVALID_CONTENT);
+ // }
+ List<NodeRelation> directedEdges = graphNeighbourTable.getDirectedEdges();
+
+ List<RelationEndPoint> relationEndPoints = new ArrayList<RelationEndPoint>(nodes.size());
+ Set<Integer> nodesInRelations = findDistinctNodesIndex(directedEdges);
+
+ buildRelationEndPoints(nodes, nodesInRelations, relationEndPoints);
+
+ BatchBuilder batchBuilder = BatchBuilder.getBuilder();
+
+ for (GraphElement neo4jElement : nodes) {
+ if (neo4jElement.getAction() != ActionEnum.Delete) {
+ logger.debug("Goint to add node {} to batch builder.", neo4jElement);
+ batchBuilder.add(neo4jElement);
+ }
+ }
+
+ if (directedEdges != null) {
+ for (NodeRelation nodeRelation : directedEdges) {
+ GraphRelation relation = buildNeo4jRelation(relationEndPoints, nodeRelation);
+ logger.debug("Goint to add relation {} to catch builder.", relation);
+ batchBuilder.add(relation);
+ }
+ }
+
+ for (GraphElement neo4jElement : nodes) {
+ if (neo4jElement.getAction() == ActionEnum.Delete) {
+ logger.debug("Goint to add node {} to batch builder.", neo4jElement);
+ batchBuilder.add(neo4jElement);
+ }
+ }
+
+ return Either.left(batchBuilder);
+
+ } else {
+ logger.error("No node was sent in order to create the resource.");
+ return Either.right(Neo4jOperationStatus.BAD_REQUEST);
+ }
+ }
+
+ private Pair<String, String> getUniqueIdKeyValue(GraphNode neo4jNode) {
+
+ // String label = neo4jNode.getLabel();
+ // NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getByName(label);
+ //
+ return Pair.createPair(neo4jNode.getUniqueIdKey(), neo4jNode.getUniqueId().toString());
+ }
+
+ private Set<Integer> findDistinctNodesIndex(List<NodeRelation> directedEdges) {
+
+ HashSet<Integer> nodesIndex = new HashSet<Integer>();
+
+ if (directedEdges != null) {
+ for (NodeRelation nodeRelation : directedEdges) {
+ nodesIndex.add(nodeRelation.getFromIndex());
+ nodesIndex.add(nodeRelation.getToIndex());
+ }
+ }
+
+ return nodesIndex;
+ }
+
+ private String findResourceDataIdFromNodes(List<GraphNode> nodes) {
+
+ if (nodes != null) {
+
+ for (GraphNode neo4jNode : nodes) {
+ String label = neo4jNode.getLabel();
+ if (label.equals(NodeTypeEnum.Resource.getName())) {
+ return neo4jNode.getUniqueId().toString();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private GraphRelation buildNeo4jRelation(List<RelationEndPoint> relationEndPoints, NodeRelation nodeRelation) {
+ GraphRelation relation = new GraphRelation();
+ int fromIndex = nodeRelation.getFromIndex();
+ int toIndex = nodeRelation.getToIndex();
+ Neo4jEdge neo4jEdge = nodeRelation.getEdge();
+ relation.setFrom(relationEndPoints.get(fromIndex));
+ relation.setTo(relationEndPoints.get(toIndex));
+ relation.setType(neo4jEdge.getEdgeType().getProperty());
+
+ // TODO: fix it after change
+ Map<String, Object> edgeProps = neo4jEdge.getProperties();
+ if (edgeProps != null && false == edgeProps.isEmpty()) {
+ relation.addPropertis(edgeProps);
+ }
+
+ relation.setAction(neo4jEdge.getAction());
+ return relation;
+ }
+
+ private void buildRelationEndPoints(List<GraphNode> nodes, Set<Integer> nodesInRelations,
+ List<RelationEndPoint> relationEndPoints) {
+
+ if (nodesInRelations != null) {
+ for (Integer nodeIndex : nodesInRelations) {
+
+ GraphElement neo4jElement = nodes.get(nodeIndex);
+ GraphNode neo4jNode = (GraphNode) neo4jElement;
+ String label = neo4jNode.getLabel();
+ Pair<String, String> uniqueKeyValue = getUniqueIdKeyValue(neo4jNode);
+
+ RelationEndPoint endPoint = new RelationEndPoint(NodeTypeEnum.getByName(label), uniqueKeyValue.getKey(),
+ uniqueKeyValue.getValue());
+ relationEndPoints.add(nodeIndex, endPoint);
+
+ }
+ }
+
+ }
+
+ public static class Pair<K, V> {
+
+ private final K key;
+ private final V value;
+
+ public static <K, V> Pair<K, V> createPair(K key, V value) {
+ return new Pair<K, V>(key, value);
+ }
+
+ public Pair(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jOperationStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jOperationStatus.java
new file mode 100644
index 0000000000..154449b521
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/Neo4jOperationStatus.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+public enum Neo4jOperationStatus {
+
+ OK, NOT_CONNECTED, NOT_AUTHORIZED, HTTP_PROTOCOL_ERROR, DB_NOT_AVAILABLE, DB_READ_ONLY, BAD_REQUEST, LEGACY_INDEX_ERROR, SCHEMA_ERROR, TRANSACTION_ERROR, EXECUTION_FAILED, ENTITY_ALREADY_EXIST,
+
+ WRONG_INPUT, GENERAL_ERROR, NOT_SUPPORTED, NOT_FOUND;
+
+ private String originError;
+ private String message;
+ private String helpErrorMsg;
+
+ private static final String NA = "NA";
+
+ Neo4jOperationStatus() {
+ originError = NA;
+ message = NA;
+ helpErrorMsg = NA;
+ }
+
+ public Neo4jOperationStatus setOriginError(String originError) {
+ this.originError = originError;
+ return this;
+ }
+
+ public Neo4jOperationStatus setMessage(String message) {
+ if (message != null && !message.isEmpty()) {
+ this.message = message;
+ }
+ return this;
+ }
+
+ public Neo4jOperationStatus setHelpErrorMsg(String helpErrorMsg) {
+ this.helpErrorMsg = helpErrorMsg;
+ return this;
+ }
+
+ public String getOriginError() {
+ return originError;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getHelpErrorMsg() {
+ return helpErrorMsg;
+ }
+
+ public String printError() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[").append(toString()).append("-").append(originError).append("-").append(helpErrorMsg).append("-")
+ .append(message).append("]");
+ return sb.toString();
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/NodeRelation.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/NodeRelation.java
new file mode 100644
index 0000000000..c1402f402f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/NodeRelation.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j;
+
+public class NodeRelation {
+
+ private int fromIndex;
+ private int toIndex;
+ private Neo4jEdge edge;
+
+ public NodeRelation(int fromIndex, int toIndex, Neo4jEdge edge) {
+ super();
+ this.fromIndex = fromIndex;
+ this.toIndex = toIndex;
+ this.edge = edge;
+ }
+
+ public int getFromIndex() {
+ return fromIndex;
+ }
+
+ public void setFromIndex(int fromIndex) {
+ this.fromIndex = fromIndex;
+ }
+
+ public int getToIndex() {
+ return toIndex;
+ }
+
+ public void setToIndex(int toIndex) {
+ this.toIndex = toIndex;
+ }
+
+ public Neo4jEdge getEdge() {
+ return edge;
+ }
+
+ public void setEdge(Neo4jEdge edge) {
+ this.edge = edge;
+ }
+
+ @Override
+ public String toString() {
+ return "NodeRelation [fromIndex=" + fromIndex + ", toIndex=" + toIndex + ", edge=" + edge + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/MatchFilter.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/MatchFilter.java
new file mode 100644
index 0000000000..10e93c1f56
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/MatchFilter.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j.filters;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MatchFilter {
+ private Map<String, Object> toMatchProperties;
+
+ public MatchFilter() {
+ toMatchProperties = new HashMap<String, Object>();
+ }
+
+ public MatchFilter(Map<String, Object> toMatchProperties) {
+ super();
+ this.toMatchProperties = toMatchProperties;
+ }
+
+ public Map<String, Object> getProperties() {
+ return toMatchProperties;
+ }
+
+ public void setProperties(Map<String, Object> properties) {
+ this.toMatchProperties = properties;
+ }
+
+ public MatchFilter addToMatch(String propName, Object value) {
+ toMatchProperties.put(propName, value);
+ return this;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveByRelationFilter.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveByRelationFilter.java
new file mode 100644
index 0000000000..698077d45b
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveByRelationFilter.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j.filters;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class RecursiveByRelationFilter extends RecursiveFilter {
+
+ private GraphNode node;
+ private String relationType;
+
+ public RecursiveByRelationFilter() {
+ super();
+ }
+
+ public RecursiveByRelationFilter(NodeTypeEnum nodeType, GraphNode node) {
+ super(nodeType);
+ this.node = node;
+ }
+
+ public RecursiveByRelationFilter(NodeTypeEnum nodeType) {
+ super(nodeType);
+ }
+
+ public RecursiveByRelationFilter(NodeTypeEnum nodeType, GraphNode node, String relationType) {
+ super(nodeType);
+ this.node = node;
+ this.relationType = relationType;
+ }
+
+ public RecursiveByRelationFilter addNode(GraphNode node) {
+ this.node = node;
+ return this;
+ }
+
+ public RecursiveByRelationFilter addRelation(String relationType) {
+ this.relationType = relationType;
+ return this;
+ }
+
+ public GraphNode getNode() {
+ return node;
+ }
+
+ public void setNode(GraphNode node) {
+ this.node = node;
+ }
+
+ public String getRelationType() {
+ return relationType;
+ }
+
+ public void setRelationType(String relationType) {
+ this.relationType = relationType;
+ }
+
+ @Override
+ public String toString() {
+ return "RecursiveByRelationFilter [node=" + node + ", relationType=" + relationType + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveFilter.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveFilter.java
new file mode 100644
index 0000000000..fa78539f8f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/RecursiveFilter.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j.filters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class RecursiveFilter extends MatchFilter {
+
+ private List<String> childRelationTypes;
+ NodeTypeEnum nodeType;
+
+ public RecursiveFilter() {
+ childRelationTypes = new ArrayList<String>();
+ }
+
+ public RecursiveFilter(NodeTypeEnum nodeType) {
+ childRelationTypes = new ArrayList<String>();
+ this.nodeType = nodeType;
+ }
+
+ public RecursiveFilter addChildRelationType(String type) {
+ childRelationTypes.add(type);
+ return this;
+ }
+
+ public List<String> getChildRelationTypes() {
+ return childRelationTypes;
+ }
+
+ public void setChildRelationTypes(List<String> childRelationTypes) {
+ this.childRelationTypes = childRelationTypes;
+ }
+
+ public NodeTypeEnum getNodeType() {
+ return nodeType;
+ }
+
+ public void setNodeType(NodeTypeEnum nodeType) {
+ this.nodeType = nodeType;
+ }
+
+ @Override
+ public String toString() {
+ return "RecursiveFilter [childRelationTypes=" + childRelationTypes + ", nodeType=" + nodeType + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/UpdateFilter.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/UpdateFilter.java
new file mode 100644
index 0000000000..3abfdeb70e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/neo4j/filters/UpdateFilter.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.neo4j.filters;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class UpdateFilter extends MatchFilter {
+
+ private Map<String, Object> toUpdate;
+
+ public UpdateFilter(Map<String, Object> toUpdate) {
+ super();
+ this.toUpdate = toUpdate;
+ }
+
+ public UpdateFilter() {
+ super();
+ toUpdate = new HashMap<String, Object>();
+ }
+
+ public UpdateFilter(Map<String, Object> toMatch, Map<String, Object> toUpdate) {
+ super(toMatch);
+ this.toUpdate = toUpdate;
+ }
+
+ public Map<String, Object> getToUpdate() {
+ return toUpdate;
+ }
+
+ public void setToUpdate(Map<String, Object> toUpdate) {
+ this.toUpdate = toUpdate;
+ }
+
+ public void addToUpdate(String property, Object value) {
+ toUpdate.put(property, value);
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/HttpRestClient.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/HttpRestClient.java
new file mode 100644
index 0000000000..fb09131b42
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/HttpRestClient.java
@@ -0,0 +1,403 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.rest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+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.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+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.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.util.EntityUtils;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.common.rest.api.RestResponse;
+import org.openecomp.sdc.common.rest.api.RestResponseAsByteArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpRestClient {
+
+ public final static int DEFAULT_CONNECTION_POOL_SIZE = 10;
+
+ public final static int DEFAULT_READ_TIMEOUT_IN_SEC = 30;
+
+ public final static int DEFAULT_CONNECT_TIMEOUT = 30;
+
+ public final static int DEFAULT_SOCKET_TIMEOUT = 30;
+
+ private CloseableHttpClient httpClient = null;
+
+ private PoolingHttpClientConnectionManager cm = null;
+
+ private static Logger logger = LoggerFactory.getLogger(HttpRestClient.class.getName());
+
+ private RestResponse errorRestResponse = new RestResponse("internal server error", null, 500);
+ private RestResponseAsByteArray errorRestResponseAsByteArray = new RestResponseAsByteArray(
+ "internal server error".getBytes(), null, 500);
+
+ boolean isInitialized = false;
+
+ // public static void main(String[] argv) {
+ // try {
+ // RestClientService restClientService =
+ // RestClientServiceFactory.createHttpRestClientService(new
+ // RestConfigurationInfo());
+ //
+ // String uriCreateCell =
+ // "http://172.20.37.245:9082/topology/management/cell/update";
+ // String jsonStr = " { \"cellName\" : \"mycell118\" }";
+ // String jsonUpdateStr =
+ // " { \"cellName\" : \"mycell118\", \"hostName\" : \"myhost333\" }";
+ //
+ //
+ // // jsonStr = " <note>dfd</note>";
+ //
+ // Properties headers = new Properties();
+ // headers.put("Content-type", "application/json");
+ // headers.put("Accept", "*/*");
+ //
+ // // RestResponse restResponse = restClientService.doPOST(uriCreateCell,
+ // headers, jsonStr);
+ //
+ // RestResponse restResponse = restClientService.doPUT(uriCreateCell,
+ // headers, jsonUpdateStr);
+ //
+ // System.out.println(restResponse);
+ //
+ //
+ // } catch (RestClientServiceExeption e) {
+ // // TODO Auto-generated catch block
+ // e.printStackTrace();
+ // }
+ //
+ // }
+
+ public HttpRestClient() {
+ super();
+ isInitialized = init(new RestConfigurationInfo());
+ }
+
+ public HttpRestClient(RestConfigurationInfo restConfigurationInfo) {
+ super();
+ isInitialized = init(restConfigurationInfo);
+ }
+
+ public boolean init(RestConfigurationInfo restConfigurationInfo) {
+
+ logger.debug("create HttpRestClient: restConfigurationInfo={}", restConfigurationInfo);
+ boolean result = false;
+ // try {
+ createHttpClient(restConfigurationInfo);
+ result = true;
+ // } catch (KeyManagementException e) {
+ // String msg = "Failed creating client config for rest. "
+ // + e.getMessage();
+ // logger.debug(msg, e);
+ // //TODO: log. error
+ // } catch (NoSuchAlgorithmException e) {
+ // String msg = "Failed creating client config for rest. "
+ // + e.getMessage();
+ // logger.debug(msg, e);
+ // //TODO: log. error
+ // }
+
+ logger.debug("Finish creating HttpRestClient. Result is {}", result);
+ return result;
+ }
+
+ public void destroy() {
+
+ try {
+ httpClient.close();
+ logger.debug("Http client closed");
+ } catch (Exception e) {
+ logger.trace("Failed to close http client", e);
+ }
+
+ }
+
+ private void createHttpClient(RestConfigurationInfo restConfigurationInfo) {
+ // throws KeyManagementException, NoSuchAlgorithmException {
+
+ PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
+
+ int connPoolSize = getConnectionPoolSize(restConfigurationInfo);
+ connManager.setMaxTotal(connPoolSize);
+ connManager.setDefaultMaxPerRoute(10);
+ connManager.setValidateAfterInactivity(15000);
+
+ // Create common default configuration
+ int socketTimeout = getTimeout(restConfigurationInfo.getSocketTimeoutInSec(), DEFAULT_SOCKET_TIMEOUT);
+
+ int connectTimeoutInSec = getTimeout(restConfigurationInfo.getConnectTimeoutInSec(), DEFAULT_CONNECT_TIMEOUT);
+
+ int readTimeOut = getTimeout(restConfigurationInfo.getReadTimeoutInSec(), DEFAULT_READ_TIMEOUT_IN_SEC);
+
+ RequestConfig clientConfig = RequestConfig.custom().setConnectTimeout(connectTimeoutInSec)
+ .setSocketTimeout(socketTimeout).setConnectionRequestTimeout(readTimeOut).build();
+
+ this.cm = connManager;
+
+ this.httpClient = HttpClients.custom().setDefaultRequestConfig(clientConfig).setConnectionManager(connManager)
+ .build();
+
+ }
+
+ private int getConnectionPoolSize(RestConfigurationInfo restConfigurationInfo) {
+ Integer connPoolSizeObj = restConfigurationInfo.getConnectionPoolSize();
+ int connPoolSize = DEFAULT_CONNECTION_POOL_SIZE;
+ if (connPoolSizeObj != null) {
+ connPoolSize = connPoolSizeObj.intValue();
+ if (connPoolSize <= 0) {
+ connPoolSize = DEFAULT_CONNECTION_POOL_SIZE;
+ }
+ }
+ return connPoolSize;
+ }
+
+ private int getTimeout(Integer value, Integer defaultValue) {
+
+ int defaultTimeout = defaultValue != null ? defaultValue.intValue() * 1000 : 0;
+
+ int timeout = defaultTimeout;
+
+ if (value != null) {
+ timeout = defaultValue.intValue() * 1000;
+ if (timeout <= 0) {
+ timeout = defaultTimeout;
+ }
+ }
+
+ return timeout;
+ }
+
+ /**
+ * Executes RS-GET to perform FIND.
+ *
+ * @param headerParameterKey
+ * String
+ * @param headerParameterValue
+ * String
+ * @return String
+ */
+ public RestResponse doGET(String uri, Properties headers) {
+
+ logger.debug("Before executing uri {}. headers = {}", uri, headers);
+
+ HttpGet httpGet = new HttpGet(uri);
+
+ RestResponse response = execute(httpGet, headers);
+
+ return response;
+ }
+
+ public RestResponse doPUT(String uri, Properties headers, String body) {
+
+ logger.debug("Before executing uri {}. headers = {}. body = ", uri, headers, body);
+
+ HttpPut httpPut = new HttpPut(uri);
+ StringEntity data = new StringEntity(body, ContentType.APPLICATION_JSON);
+ httpPut.setEntity(data);
+ RestResponse response = execute(httpPut, headers);
+
+ return response;
+ }
+
+ public RestResponse doPOST(String uri, Properties headers, String body) {
+
+ logger.debug("Before executing uri {}. headers = {}. body = {}", uri, headers, body);
+
+ HttpPost httpPost = new HttpPost(uri);
+ StringEntity data = new StringEntity(body, ContentType.APPLICATION_JSON);
+ httpPost.setEntity(data);
+ RestResponse response = execute(httpPost, headers);
+
+ return response;
+ }
+
+ public RestResponseAsByteArray doGetAsByteArray(String uri, Properties headers) {
+
+ logger.debug("Before executing uri {}. headers = {}.", uri, headers);
+
+ HttpGet httpGet = new HttpGet(uri);
+
+ RestResponseAsByteArray response = executeAndReturnByteArray(httpGet, headers);
+
+ return response;
+ }
+
+ private void addHeadersToRequest(HttpRequestBase httpRequestBase, Properties headers) {
+
+ if (headers != null) {
+ for (Entry<Object, Object> entry : headers.entrySet()) {
+ httpRequestBase.addHeader(entry.getKey().toString(), entry.getValue().toString());
+ }
+ }
+
+ }
+
+ private RestResponse execute(HttpRequestBase httpRequestBase, Properties headers) {
+
+ RestResponse restResponse = null;
+
+ CloseableHttpResponse httpResponse = null;
+
+ try {
+
+ addHeadersToRequest(httpRequestBase, headers);
+
+ httpResponse = this.httpClient.execute(httpRequestBase);
+
+ restResponse = buildRestResponseFromResult(httpResponse);
+
+ logger.debug("After executing uri {}. response = {}.", httpRequestBase.getURI().toString(), restResponse);
+ } catch (Exception exception) {
+ httpRequestBase.abort();
+
+ String description = "Failed executing http request " + httpRequestBase.getURI().toString() + "("
+ + httpRequestBase.getMethod() + ")";
+ BeEcompErrorManager.getInstance().logInternalFlowError("ExecuteRestRequest", description,
+ ErrorSeverity.ERROR);
+ restResponse = errorRestResponse;
+ } finally {
+ // ensure the connection gets released to the manager
+ releaseResource(httpResponse);
+ }
+
+ return restResponse;
+ }
+
+ private RestResponse buildRestResponseFromResult(CloseableHttpResponse httpResponse) throws IOException {
+
+ int statusCode = httpResponse.getStatusLine().getStatusCode();
+ String statusDesc = httpResponse.getStatusLine().getReasonPhrase();
+
+ HttpEntity entity = httpResponse.getEntity();
+ String response = null;
+ if (entity != null) {
+ response = EntityUtils.toString(entity);
+ }
+
+ RestResponse restResponse = new RestResponse(response, statusDesc, statusCode);
+
+ return restResponse;
+ }
+
+ private RestResponseAsByteArray buildRestResponseByteArrayFromResult(CloseableHttpResponse httpResponse)
+ throws IOException {
+
+ int statusCode = httpResponse.getStatusLine().getStatusCode();
+ String statusDesc = httpResponse.getStatusLine().getReasonPhrase();
+
+ HttpEntity entity = httpResponse.getEntity();
+
+ byte[] response = null;
+ if (entity != null) {
+ InputStream content = entity.getContent();
+ if (content != null) {
+ response = IOUtils.toByteArray(content);
+ }
+ }
+
+ RestResponseAsByteArray restResponse = new RestResponseAsByteArray(response, statusDesc, statusCode);
+
+ return restResponse;
+ }
+
+ private RestResponseAsByteArray executeAndReturnByteArray(HttpRequestBase httpRequestBase, Properties headers) {
+
+ RestResponseAsByteArray restResponse = null;
+
+ CloseableHttpResponse httpResponse = null;
+
+ try {
+
+ addHeadersToRequest(httpRequestBase, headers);
+
+ httpResponse = this.httpClient.execute(httpRequestBase);
+
+ restResponse = buildRestResponseByteArrayFromResult(httpResponse);
+
+ if (restResponse != null) {
+ logger.debug("After executing uri {}. Response: {}.", httpRequestBase.getURI().toString(), restResponse.toPrettyString());
+ }
+
+ } catch (Exception exception) {
+ httpRequestBase.abort();
+ String description = "Failed executing http request " + httpRequestBase.getURI().toString() + "("
+ + httpRequestBase.getMethod() + ")";
+ BeEcompErrorManager.getInstance().logInternalFlowError("ExecuteRestRequest", description,
+ ErrorSeverity.ERROR);
+ logger.debug(description, exception);
+ restResponse = errorRestResponseAsByteArray;
+ } finally {
+ // ensure the connection gets released to the manager
+ releaseResource(httpResponse);
+ }
+
+ return restResponse;
+ }
+
+ /**
+ * This method print the JSON response from the REST Server
+ *
+ * @param response
+ * the JSON response from the REST server
+ * @param method
+ * name of method
+ */
+ private void logResponse(String response, String method) {
+ logger.trace(method + " response = " + response);
+ }
+
+ private void releaseResource(CloseableHttpResponse response) {
+ if (response != null) {
+ try {
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ EntityUtils.consume(entity);
+ }
+ response.close();
+ } catch (Exception e) {
+ logger.error("failed to close connection exception", e);
+ }
+ }
+ }
+
+ public boolean isInitialized() {
+ return isInitialized;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/RestConfigurationInfo.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/RestConfigurationInfo.java
new file mode 100644
index 0000000000..1e45ba6ae3
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/rest/RestConfigurationInfo.java
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.rest;
+
+public class RestConfigurationInfo {
+
+ private Integer readTimeoutInSec = null;
+
+ private Boolean ignoreCertificate = null;
+
+ private Integer connectionPoolSize = null;
+
+ private Integer connectTimeoutInSec = null;
+
+ private Integer socketTimeoutInSec = null;
+
+ /**
+ * @return the readTimeoutInSec
+ */
+ public Integer getReadTimeoutInSec() {
+ return readTimeoutInSec;
+ }
+
+ /**
+ * @param readTimeoutInSec
+ * the readTimeoutInSec to set
+ */
+ public void setReadTimeoutInSec(Integer readTimeoutInSec) {
+ this.readTimeoutInSec = readTimeoutInSec;
+ }
+
+ /**
+ * @return the ignoreCertificate
+ */
+ public Boolean getIgnoreCertificate() {
+ return ignoreCertificate;
+ }
+
+ /**
+ * @param ignoreCertificate
+ * the ignoreCertificate to set
+ */
+ public void setIgnoreCertificate(Boolean ignoreCertificate) {
+ this.ignoreCertificate = ignoreCertificate;
+ }
+
+ /**
+ * @return the connectionPoolSize
+ */
+ public Integer getConnectionPoolSize() {
+ return connectionPoolSize;
+ }
+
+ /**
+ * @param connectionPoolSize
+ * the connectionPoolSize to set
+ */
+ public void setConnectionPoolSize(Integer connectionPoolSize) {
+ this.connectionPoolSize = connectionPoolSize;
+ }
+
+ /**
+ * @return the connectTimeoutInSec
+ */
+ public Integer getConnectTimeoutInSec() {
+ return connectTimeoutInSec;
+ }
+
+ /**
+ * @param connectTimeoutInSec
+ * the connectTimeoutInSec to set
+ */
+ public void setConnectTimeoutInSec(Integer connectTimeoutInSec) {
+ this.connectTimeoutInSec = connectTimeoutInSec;
+ }
+
+ public Integer getSocketTimeoutInSec() {
+ return socketTimeoutInSec;
+ }
+
+ public void setSocketTimeoutInSec(Integer socketTimeoutInSec) {
+ this.socketTimeoutInSec = socketTimeoutInSec;
+ }
+
+ @Override
+ public String toString() {
+ return "RestConfigurationInfo [readTimeoutInSec=" + readTimeoutInSec + ", ignoreCertificate="
+ + ignoreCertificate + ", connectionPoolSize=" + connectionPoolSize + ", connectTimeoutInSec="
+ + connectTimeoutInSec + ", socketTimeoutInSec=" + socketTimeoutInSec + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/QueryType.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/QueryType.java
new file mode 100644
index 0000000000..689ed32a8a
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/QueryType.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.titan;
+
+/**
+ * Created by mlando on 9/21/2016.
+ */
+public enum QueryType {
+ HAS, HAS_NOT
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGenericDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGenericDao.java
new file mode 100644
index 0000000000..535146d692
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGenericDao.java
@@ -0,0 +1,1848 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.titan;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.GraphNodeLock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.PropertyKey;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanGraphQuery;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.TitanVertexQuery;
+import com.thinkaurelius.titan.graphdb.query.TitanPredicate;
+
+import fj.data.Either;
+
+@Component("titan-generic-dao")
+public class TitanGenericDao {
+
+ @Resource
+ TitanGraphClient titanClient;
+
+ private static Logger logger = LoggerFactory.getLogger(TitanGenericDao.class.getName());
+ private static final String LOCK_NODE_PREFIX = "lock_";
+
+ public TitanGenericDao() {
+ logger.info("** TitanGenericDao created");
+ }
+
+ public TitanOperationStatus commit() {
+ logger.debug("doing commit.");
+ return titanClient.commit();
+ }
+
+ public TitanOperationStatus rollback() {
+ return titanClient.rollback();
+ }
+
+ public Either<TitanGraph, TitanOperationStatus> getGraph() {
+ return titanClient.getGraph();
+ }
+
+ // For healthCheck
+ public boolean isGraphOpen() {
+ return titanClient.getHealth();
+ }
+
+ /**
+ *
+ * @param node
+ * @param clazz
+ * @return
+ */
+ public <T extends GraphNode> Either<T, TitanOperationStatus> createNode(T node, Class<T> clazz) {
+ logger.debug("try to create node for ID [{}]", node.getKeyValueId());
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ T newNode;
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ Vertex vertex = tGraph.addVertex();
+
+ vertex.property(GraphPropertiesDictionary.LABEL.getProperty(), node.getLabel());
+
+ Map<String, Object> properties = node.toGraphMap();
+ if (properties != null) {
+ setProperties(vertex, properties);
+ }
+ Map<String, Object> newProps = getProperties(vertex);
+ newNode = GraphElementFactory.createElement(node.getLabel(), GraphElementTypeEnum.Node, newProps, clazz);
+ logger.debug("created node for props : {}", newProps);
+ logger.debug("Node was created for ID [{}]", node.getKeyValueId());
+ return Either.left(newNode);
+
+ } catch (Exception e) {
+ logger.debug("Failed to create Node for ID [{}]", node.getKeyValueId(), e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ logger.debug("Failed to create Node for ID [{}] {}", node.getKeyValueId(), graph.right().value());
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public Either<TitanVertex, TitanOperationStatus> createNode(GraphNode node) {
+ logger.debug("try to create node for ID [{}]", node.getKeyValueId());
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ TitanVertex vertex = tGraph.addVertex();
+
+ vertex.property(GraphPropertiesDictionary.LABEL.getProperty(), node.getLabel());
+
+ Map<String, Object> properties = node.toGraphMap();
+ if (properties != null) {
+ setProperties(vertex, properties);
+ }
+ logger.debug("Node was created for ID [{}]", node.getKeyValueId());
+ return Either.left(vertex);
+
+ } catch (Exception e) {
+ logger.debug("Failed to create Node for ID [{}]", node.getKeyValueId(), e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ logger.debug("Failed to create Node for ID [{}] {}", node.getKeyValueId(), graph.right().value());
+ return Either.right(graph.right().value());
+ }
+ }
+
+ /**
+ *
+ * @param relation
+ * @return
+ */
+ public Either<GraphRelation, TitanOperationStatus> createRelation(GraphRelation relation) {
+ logger.debug("try to create relation from [{}] to [{}] ", relation.getFrom(), relation.getTo());
+
+ RelationEndPoint from = relation.getFrom();
+ RelationEndPoint to = relation.getTo();
+ ImmutablePair<String, Object> fromKeyId = new ImmutablePair<String, Object>(from.getIdName(), from.getIdValue());
+ ImmutablePair<String, Object> toKeyId = new ImmutablePair<String, Object>(to.getIdName(), to.getIdValue());
+
+ return createEdge(relation.getType(), fromKeyId, toKeyId, from.getLabel().getName(), to.getLabel().getName(), relation.toGraphMap());
+
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> createEdge(String type, ImmutablePair<String, Object> from, ImmutablePair<String, Object> to, String fromLabel, String toLabel, Map<String, Object> properties) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+
+ if (graph.isLeft()) {
+ try {
+ Either<Vertex, TitanOperationStatus> fromV = getVertexByPropertyAndLabel(from.getKey(), from.getValue(), fromLabel);
+ if (fromV.isRight()) {
+ TitanOperationStatus error = fromV.right().value();
+ if (TitanOperationStatus.NOT_FOUND.equals(error)) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ } else {
+ return Either.right(error);
+ }
+ }
+ Either<Vertex, TitanOperationStatus> toV = getVertexByPropertyAndLabel(to.getKey(), to.getValue(), toLabel);
+ if (toV.isRight()) {
+ TitanOperationStatus error = toV.right().value();
+ if (TitanOperationStatus.NOT_FOUND.equals(error)) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ } else {
+ return Either.right(error);
+ }
+ }
+
+ Vertex fromVertex = fromV.left().value();
+ Vertex toVertex = toV.left().value();
+ Edge edge = fromVertex.addEdge(type, toVertex);
+
+ if (properties != null) {
+
+ setProperties(edge, properties);
+ }
+
+ Vertex vertexOut = edge.outVertex();
+ Vertex vertexIn = edge.inVertex();
+
+ GraphNode nodeOut = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(vertexOut), GraphNode.class);
+ GraphNode nodeIn = GraphElementFactory.createElement(toLabel, GraphElementTypeEnum.Node, getProperties(vertexIn), GraphNode.class);
+
+ GraphRelation newRelation = GraphElementFactory.createRelation(edge.label(), getProperties(edge), nodeOut, nodeIn);
+
+ return Either.left(newRelation);
+ } catch (Exception e) {
+ logger.debug("Failed to create edge from [{}] to [{}]", from, to, e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ logger.debug("Failed to create edge from [{}] to [{}] {}", from, to, graph.right().value());
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public TitanOperationStatus createEdge(TitanVertex vertexOut, TitanVertex vertexIn, GraphEdgeLabels type, Map<String, Object> properties) {
+ try {
+ Edge edge = vertexOut.addEdge(type.getProperty(), vertexIn);
+
+ if (properties != null) {
+
+ setProperties(edge, properties);
+ }
+ } catch (Exception e) {
+ logger.debug("Failed to create edge from [{}] to [{}]", vertexOut, vertexIn, e);
+ return TitanGraphClient.handleTitanException(e);
+ }
+ return TitanOperationStatus.OK;
+
+ }
+
+ public TitanOperationStatus createEdge(TitanVertex vertexOut, GraphNode to, GraphEdgeLabels type, Map<String, Object> properties) {
+
+ TitanVertex vertexIn;
+ Either<Vertex, TitanOperationStatus> toV = getVertexByPropertyAndLabel(to.getUniqueIdKey(), to.getUniqueId(), to.getLabel());
+ if (toV.isRight()) {
+ TitanOperationStatus error = toV.right().value();
+ if (TitanOperationStatus.NOT_FOUND.equals(error)) {
+ return TitanOperationStatus.INVALID_ID;
+ } else {
+ return error;
+ }
+ }
+ vertexIn = (TitanVertex) toV.left().value();
+ return createEdge(vertexOut, vertexIn, type, properties);
+ }
+
+ /**
+ *
+ * @param from
+ * @param to
+ * @param label
+ * @param properties
+ * @return
+ */
+ public Either<GraphRelation, TitanOperationStatus> createRelation(GraphNode from, GraphNode to, GraphEdgeLabels label, Map<String, Object> properties) {
+ logger.debug("try to create relation from [{}] to [{}]", from.getKeyValueId(), to.getKeyValueId());
+ return createEdge(label.getProperty(), from.getKeyValueId(), to.getKeyValueId(), from.getLabel(), to.getLabel(), properties);
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> replaceRelationLabel(GraphNode from, GraphNode to, GraphEdgeLabels label, GraphEdgeLabels newLabel) {
+
+ logger.debug("try to replace relation {} to {} from [{}] to [{}]", label.name(), newLabel.name(), from.getKeyValueId(), to.getKeyValueId());
+ Either<GraphRelation, TitanOperationStatus> getRelationResult = getRelation(from, to, label);
+ if (getRelationResult.isRight()) {
+ return getRelationResult;
+ }
+
+ GraphRelation origRelation = getRelationResult.left().value();
+ Either<GraphRelation, TitanOperationStatus> createRelationResult = createRelation(from, to, newLabel, origRelation.toGraphMap());
+ if (createRelationResult.isRight()) {
+ return createRelationResult;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> deleteRelationResult = deleteRelation(origRelation);
+ if (deleteRelationResult.isRight()) {
+ return deleteRelationResult;
+ }
+ return Either.left(createRelationResult.left().value());
+ }
+
+ /**
+ *
+ * @param keyName
+ * @param keyValue
+ * @param clazz
+ * @return
+ */
+ public <T extends GraphNode> Either<T, TitanOperationStatus> getNode(String keyName, Object keyValue, Class<T> clazz) {
+
+ logger.debug("Try to get node for key [{}] with value [{}] ", keyName, keyValue);
+
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = getVertexByProperty(keyName, keyValue);
+
+ if (vertexByProperty.isLeft()) {
+ try {
+ Vertex vertex = vertexByProperty.left().value();
+ Map<String, Object> properties = getProperties(vertex);
+ T node = GraphElementFactory.createElement((String) properties.get(GraphPropertiesDictionary.LABEL.getProperty()), GraphElementTypeEnum.Node, properties, clazz);
+ return Either.left(node);
+ } catch (Exception e) {
+ logger.debug("Failed to get node for key [{}] with value [{}] ", keyName, keyValue, e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ logger.debug("Failed to get node for key [{}] with value [{}] ", keyName, keyValue, vertexByProperty.right().value());
+ return Either.right(vertexByProperty.right().value());
+ }
+ }
+
+ /**
+ *
+ * @param from
+ * @param to
+ * @param label
+ * @return
+ */
+ public Either<GraphRelation, TitanOperationStatus> getRelation(GraphNode from, GraphNode to, GraphEdgeLabels label) {
+ logger.debug("try to get relation from [{}] to [{}]", from.getKeyValueId(), to.getKeyValueId());
+
+ ImmutablePair<String, Object> keyValueIdFrom = from.getKeyValueId();
+ ImmutablePair<String, Object> keyValueIdTo = to.getKeyValueId();
+
+ Either<Edge, TitanOperationStatus> edge = getEdgeByVerticies(keyValueIdFrom.getKey(), keyValueIdFrom.getValue(), keyValueIdTo.getKey(), keyValueIdTo.getValue(), label.getProperty());
+
+ if (edge.isLeft()) {
+ try {
+ Map<String, Object> properties = getProperties(edge.left().value());
+ GraphRelation relation = GraphElementFactory.createRelation(label.getProperty(), properties, from, to);
+ return Either.left(relation);
+ } catch (Exception e) {
+ logger.debug("Failed to get get relation from [{}] to [{}]", from.getKeyValueId(), to.getKeyValueId(), e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ logger.debug("Failed to get get relation from [{}] to [{}] {}", from.getKeyValueId(), to.getKeyValueId(), edge.right().value());
+ return Either.right(edge.right().value());
+ }
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> deleteIncomingRelationByCriteria(GraphNode to, GraphEdgeLabels label, Map<String, Object> props) {
+
+ Either<Edge, TitanOperationStatus> edgeByCriteria = getIncomingEdgeByCriteria(to, label, props);
+ if (edgeByCriteria.isLeft()) {
+ Either<TitanGraph, TitanOperationStatus> graph = getGraph();
+ if (graph.isLeft()) {
+ Edge edge = edgeByCriteria.left().value();
+ logger.debug("delete edge {} to {} ", label.getProperty(), to.getUniqueId());
+ edge.remove();
+ Map<String, Object> properties = getProperties(edge);
+ Vertex fromVertex = edge.outVertex();
+ String fromLabel = fromVertex.value(GraphPropertiesDictionary.LABEL.getProperty());
+ GraphNode nodeFrom = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(fromVertex), GraphNode.class);
+ GraphRelation relation = GraphElementFactory.createRelation(label.getProperty(), properties, nodeFrom, to);
+ return Either.left(relation);
+ } else {
+ logger.debug("failed to get graph");
+ return Either.right(graph.right().value());
+ }
+
+ } else {
+ logger.debug("failed to find edge {} to {}", label.getProperty(), to.getUniqueId());
+ return Either.right(edgeByCriteria.right().value());
+ }
+
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> getIncomingRelationByCriteria(GraphNode to, GraphEdgeLabels label, Map<String, Object> props) {
+
+ Either<Edge, TitanOperationStatus> edgeByCriteria = getIncomingEdgeByCriteria(to, label, props);
+ if (edgeByCriteria.isLeft()) {
+ Either<TitanGraph, TitanOperationStatus> graph = getGraph();
+ if (graph.isLeft()) {
+ Edge edge = edgeByCriteria.left().value();
+ Map<String, Object> properties = getProperties(edge);
+ Vertex fromVertex = edge.outVertex();
+ String fromLabel = fromVertex.value(GraphPropertiesDictionary.LABEL.getProperty());
+ GraphNode nodeFrom = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(fromVertex), GraphNode.class);
+ GraphRelation relation = GraphElementFactory.createRelation(label.getProperty(), properties, nodeFrom, to);
+ return Either.left(relation);
+ } else {
+ logger.debug("failed to get graph");
+ return Either.right(graph.right().value());
+ }
+
+ } else {
+ logger.debug("failed to find edge {} to {}", label.getProperty(), to.getUniqueId());
+ return Either.right(edgeByCriteria.right().value());
+ }
+
+ }
+
+ public Either<Edge, TitanOperationStatus> getIncomingEdgeByCriteria(GraphNode to, GraphEdgeLabels label, Map<String, Object> props) {
+
+ ImmutablePair<String, Object> keyValueIdTo = to.getKeyValueId();
+
+ Either<TitanVertex, TitanOperationStatus> vertexFrom = getVertexByProperty(keyValueIdTo.getKey(), keyValueIdTo.getValue());
+ if (vertexFrom.isRight()) {
+ return Either.right(vertexFrom.right().value());
+ }
+ Vertex vertex = vertexFrom.left().value();
+ TitanVertex titanVertex = (TitanVertex) vertex;
+ TitanVertexQuery<?> query = titanVertex.query();
+ query = query.labels(label.getProperty());
+
+ if (props != null && !props.isEmpty()) {
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+ Edge matchingEdge = null;
+ Iterable<TitanEdge> edges = query.edges();
+ if (edges == null) {
+ logger.debug("No edges in graph for criteria");
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ Iterator<TitanEdge> eIter = edges.iterator();
+ if (eIter.hasNext()) {
+ TitanEdge edge = eIter.next();
+ matchingEdge = edge;
+ }
+
+ if (matchingEdge == null) {
+ logger.debug("No edges in graph for criteria");
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ return Either.left(matchingEdge);
+ }
+
+ private Either<Edge, TitanOperationStatus> getEdgeByVerticies(String keyNameFrom, Object keyValueFrom, String keyNameTo, Object keyValueTo, String label) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+
+ if (graph.isLeft()) {
+ try {
+ Either<TitanVertex, TitanOperationStatus> vertexFrom = getVertexByProperty(keyNameFrom, keyValueFrom);
+ if (vertexFrom.isRight()) {
+ return Either.right(vertexFrom.right().value());
+ }
+ Iterable<TitanEdge> edges = ((TitanVertex) vertexFrom.left().value()).query().labels(label).edges();
+ Iterator<TitanEdge> eIter = edges.iterator();
+ while (eIter.hasNext()) {
+ Edge edge = eIter.next();
+ Vertex vertexIn = edge.inVertex();
+ if (vertexIn.value(keyNameTo) != null && vertexIn.value(keyNameTo).equals(keyValueTo) && label.equals(edge.label())) {
+ return Either.left(edge);
+ }
+ }
+ logger.debug("No relation in graph from [{}={}] to [{}={}]", keyNameFrom, keyValueFrom, keyNameTo, keyValueTo);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ } catch (Exception e) {
+ logger.debug("Failed to get get relation from [{}={}] to [{}={}]", keyNameFrom, keyValueFrom, keyNameTo, keyValueTo, e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public Either<List<Edge>, TitanOperationStatus> getEdgesForNode(GraphNode node, Direction requestedDirection) {
+
+ Either<List<Edge>, TitanOperationStatus> result;
+
+ ImmutablePair<String, Object> keyValueId = node.getKeyValueId();
+ Either<TitanVertex, TitanOperationStatus> eitherVertex = getVertexByProperty(keyValueId.getKey(), keyValueId.getValue());
+
+ if (eitherVertex.isLeft()) {
+ List<Edge> edges = prepareEdgesList(eitherVertex.left().value(), requestedDirection);
+
+ result = Either.left(edges);
+ } else {
+ result = Either.right(eitherVertex.right().value());
+ }
+ return result;
+ }
+
+ private List<Edge> prepareEdgesList(Vertex vertex, Direction requestedDirection) {
+ List<Edge> edges = new ArrayList<>();
+ Iterator<TitanEdge> edgesItr = ((TitanVertex) vertex).query().edges().iterator();
+ while (edgesItr.hasNext()) {
+ Edge edge = edgesItr.next();
+ Direction currEdgeDirection = getEdgeDirection(vertex, edge);
+ if (currEdgeDirection == requestedDirection || requestedDirection == Direction.BOTH) {
+ edges.add(edge);
+ }
+
+ }
+ return edges;
+ }
+
+ private Direction getEdgeDirection(Vertex vertex, Edge edge) {
+ Direction result;
+ Vertex vertexOut = edge.outVertex();
+ if (vertexOut.equals(vertex)) {
+ result = Direction.OUT;
+ } else {
+ result = Direction.IN;
+
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param from
+ * @param to
+ * @param label
+ * @param properties
+ * @return
+ */
+ public Either<GraphRelation, TitanOperationStatus> updateRelation(GraphNode from, GraphNode to, GraphEdgeLabels label, Map<String, Object> properties) {
+ logger.debug("try to update relation from [{}] to [{}]", from.getKeyValueId(), to.getKeyValueId());
+ return updateEdge(label.getProperty(), from.getKeyValueId(), to.getKeyValueId(), from.getLabel(), to.getLabel(), properties);
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> updateEdge(String type, ImmutablePair<String, Object> from, ImmutablePair<String, Object> to, String fromLabel, String toLabel, Map<String, Object> properties) {
+
+ Either<Edge, TitanOperationStatus> edgeS = getEdgeByVerticies(from.getKey(), from.getValue(), to.getKey(), to.getValue(), type);
+ if (edgeS.isLeft()) {
+
+ try {
+ Edge edge = edgeS.left().value();
+ if (properties != null) {
+ setProperties(edge, properties);
+ }
+
+ Vertex vertexOut = edge.outVertex();
+ Vertex vertexIn = edge.inVertex();
+
+ GraphNode nodeOut = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(vertexOut), GraphNode.class);
+ GraphNode nodeIn = GraphElementFactory.createElement(toLabel, GraphElementTypeEnum.Node, getProperties(vertexIn), GraphNode.class);
+
+ GraphRelation newRelation = GraphElementFactory.createRelation(edge.label(), getProperties(edge), nodeOut, nodeIn);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Relation was updated from [ {} ] to [ {} ]", from, to);
+ }
+ return Either.left(newRelation);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to update relation from [ {} ] [ {} ] {}", from, to, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to update relation from [ {} ] [ {} ] {}", from, to, edgeS.right().value());
+ }
+ return Either.right(edgeS.right().value());
+ }
+ }
+
+ /**
+ *
+ * @param relation
+ * @return
+ */
+ public Either<GraphRelation, TitanOperationStatus> updateRelation(GraphRelation relation) {
+ logger.debug("try to update relation from [{}] to [{}]", relation.getFrom(), relation.getTo());
+ RelationEndPoint from = relation.getFrom();
+ RelationEndPoint to = relation.getTo();
+ ImmutablePair<String, Object> fromKeyId = new ImmutablePair<String, Object>(from.getIdName(), from.getIdValue());
+ ImmutablePair<String, Object> toKeyId = new ImmutablePair<String, Object>(to.getIdName(), to.getIdValue());
+
+ return updateEdge(relation.getType(), fromKeyId, toKeyId, from.getLabel().getName(), to.getLabel().getName(), relation.toGraphMap());
+
+ }
+
+ private Either<Vertex, TitanOperationStatus> getVertexByPropertyAndLabel(String name, Object value, String label) {
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertecies = tGraph.query().has(name, value).has(GraphPropertiesDictionary.LABEL.getProperty(), label).vertices();
+
+ java.util.Iterator<TitanVertex> iterator = vertecies.iterator();
+ if (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ return Either.left(vertex);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("No vertex in graph for key = {} and value = {} label = {}", name, value, label);
+ }
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to get vertex in graph for key = {} and value = {} label = {}", name, value, label);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("No vertex in graph for key = {} and value = {} label = {}. error : {}", name, value, label, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public Either<TitanVertex, TitanOperationStatus> getVertexByProperty(String name, Object value) {
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (value == null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("No vertex in graph for key = {} and value = {}", name, value);
+ }
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertecies = tGraph.query().has(name, value).vertices();
+
+ java.util.Iterator<TitanVertex> iterator = vertecies.iterator();
+ if (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ return Either.left(vertex);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("No vertex in graph for key ={} and value = {}", name, value);
+ }
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to get vertex in graph for key = {} and value = ", name, value);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("No vertex in graph for key = {} and value = {} error : {}", name, value, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public <T extends GraphNode> Either<List<T>, TitanOperationStatus> getByCriteria(NodeTypeEnum type, Map<String, Object> hasProps, Map<String, Object> hasNotProps, Class<T> clazz) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ TitanGraphQuery<? extends TitanGraphQuery> query = tGraph.query();
+ query = query.has(GraphPropertiesDictionary.LABEL.getProperty(), type.getName());
+
+ if (hasProps != null && !hasProps.isEmpty()) {
+ for (Map.Entry<String, Object> entry : hasProps.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+ if (hasNotProps != null && !hasNotProps.isEmpty()) {
+ for (Map.Entry<String, Object> entry : hasNotProps.entrySet()) {
+ query = query.hasNot(entry.getKey(), entry.getValue());
+ }
+ }
+ Iterable<TitanVertex> vertices = query.vertices();
+ if (vertices == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<T> result = new ArrayList<T>();
+
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> newProp = getProperties(vertex);
+
+ T element = GraphElementFactory.createElement(type.getName(), GraphElementTypeEnum.Node, newProp, clazz);
+ result.add(element);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Number of fetced nodes in graph for criteria : from type = {} and properties has = {}, properties hasNot = {} is {}", type, hasProps, hasNotProps, result.size());
+ }
+ if (result.size() == 0) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(result);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type = {}", type, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type ={} error : {}", type, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public <T extends GraphNode> Either<List<T>, TitanOperationStatus> getByCriteria(NodeTypeEnum type, Class<T> clazz, List<ImmutableTriple<QueryType, String, Object>> props) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ TitanGraphQuery<? extends TitanGraphQuery> query = tGraph.query();
+ query = query.has(GraphPropertiesDictionary.LABEL.getProperty(), type.getName());
+ for (ImmutableTriple<QueryType, String, Object> prop : props) {
+ if (QueryType.HAS.equals(prop.getLeft())) {
+ query = query.has(prop.getMiddle(), prop.getRight());
+ } else {
+ query = query.hasNot(prop.getMiddle(), prop.getRight());
+ }
+ }
+ Iterable<TitanVertex> vertices = query.vertices();
+ if (vertices == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<T> result = new ArrayList<T>();
+
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> newProp = getProperties(vertex);
+
+ T element = GraphElementFactory.createElement(type.getName(), GraphElementTypeEnum.Node, newProp, clazz);
+ result.add(element);
+ }
+ if (result.size() == 0) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(result);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type = {}", type, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type ={} error : {}", type, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ }
+
+ private boolean vertexLeftNotContainsAllRightProps(Map<String, Object> leftProps, Map<String, Object> rightProps) {
+
+ if (rightProps != null) {
+
+ for (Entry<String, Object> entry : rightProps.entrySet()) {
+ String key = entry.getKey();
+ Object leftValue = leftProps.get(key);
+ Object rightValue = entry.getValue();
+
+ if (leftValue == null) {
+ if (rightValue == null) {
+ return false;
+ } else {
+ continue;
+ }
+ }
+
+ if (true == leftValue.equals(rightValue)) {
+ logger.trace("The value of key {} is differnet between properties. {} vs {}", key, leftValue, rightValue);
+ return false;
+ }
+ }
+
+ }
+
+ return true;
+
+ }
+
+ public <T extends GraphNode> Either<List<T>, TitanOperationStatus> getByCriteria(NodeTypeEnum type, Map<String, Object> props, Class<T> clazz) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ TitanGraphQuery<? extends TitanGraphQuery> query = tGraph.query();
+ query = query.has(GraphPropertiesDictionary.LABEL.getProperty(), type.getName());
+
+ if (props != null && !props.isEmpty()) {
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+ Iterable<TitanVertex> vertices = query.vertices();
+ if (vertices == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<T> result = new ArrayList<T>();
+
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> newProp = getProperties(vertex);
+
+ T element = GraphElementFactory.createElement(type.getName(), GraphElementTypeEnum.Node, newProp, clazz);
+ result.add(element);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Number of fetced nodes in graph for criteria : from type = {} and properties = {} is {}", type, props, result.size());
+ }
+ if (result.size() == 0) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(result);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type = {} and properties = {}", type, props, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type ={} and properties = {} error : {}", type, props, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ }
+
+ // public <T extends GraphNode> Either<List<T>, TitanOperationStatus>
+ // getByCriteria_tx(NodeTypeEnum type, Map<String, Object> props, Class<T>
+ // clazz) {
+ // Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ // TitanTransaction tx=null;
+ // if (graph.isLeft()) {
+ // try {
+ // TitanGraph tGraph = graph.left().value();
+ // tx = tGraph.newTransaction();
+ //
+ // TitanGraphQuery<? extends TitanGraphQuery> query = tx.query();
+ // query = query.has(GraphPropertiesDictionary.LABEL.getProperty(),
+ // type.getName());
+ //
+ // if (props != null && !props.isEmpty()) {
+ // for (Map.Entry<String, Object> entry : props.entrySet()) {
+ // query = query.has(entry.getKey(), entry.getValue());
+ // }
+ // }
+ // Iterable<TitanVertex> vertices = query.vertices();
+ // if (vertices == null) {
+ // tx.commit();
+ // return Either.right(TitanOperationStatus.NOT_FOUND);
+ // }
+ //
+ //
+ // Iterator<TitanVertex> iterator = vertices.iterator();
+ // List<T> result = new ArrayList<T>();
+ //
+ // while (iterator.hasNext()) {
+ // Vertex vertex = iterator.next();
+ //
+ // Map<String, Object> newProp = getProperties(vertex);
+ //
+ // T element = GraphElementFactory.createElement(type.getName(),
+ // GraphElementTypeEnum.Node, newProp, clazz);
+ // result.add(element);
+ // }
+ // if (logger.isDebugEnabled()) {
+ // logger.debug("Number of fetced nodes in graph for criteria : from type ="
+ // + type + " and properties = " + props + " is " + result.size());
+ // }
+ // tx.commit();
+ // if (result.size() == 0) {
+ // return Either.right(TitanOperationStatus.NOT_FOUND);
+ // }
+ //
+ // return Either.left(result);
+ // } catch (Exception e) {
+ // if (tx != null)
+ // try {
+ // tx.commit();
+ // } catch (Exception e1) {
+ // logger.debug("failed to commit after get", e);
+ // }
+ // if (logger.isDebugEnabled()) {
+ // logger.debug("Failed get by criteria for type = {} and properties = {}. {}", type, props, e);
+ // }
+ // return Either.right(TitanGraphClient.handleTitanException(e));
+ // }
+ //
+ // } else {
+ // if (logger.isDebugEnabled()) {
+ // logger.debug("Failed get by criteria for type = {} and properties {}. error : {}", type, props, graph.right().value());
+ // }
+ // return Either.right(graph.right().value());
+ // }
+ // }
+
+ private boolean vertexLeftContainsRightProps(Map<String, Object> leftProps, Map<String, Object> rightProps) {
+
+ if (rightProps != null) {
+
+ for (Entry<String, Object> entry : rightProps.entrySet()) {
+ String key = entry.getKey();
+ Object leftValue = leftProps.get(key);
+ Object rightValue = entry.getValue();
+ if (leftValue == null) {
+ if (rightValue == null) {
+ continue;
+ } else {
+ logger.debug("The key {} cannot be found in the properties {}", key, leftProps);
+ return false;
+ }
+ }
+
+ if (false == leftValue.equals(rightValue)) {
+ logger.trace("The value of key {} is differnet between properties. {} vs {} ", key, leftValue, rightValue);
+ return false;
+ }
+ }
+
+ }
+
+ return true;
+ }
+
+ public <T extends GraphNode> Either<List<T>, TitanOperationStatus> getByCriteriaWithPradicat(NodeTypeEnum type, Map<String, Entry<TitanPredicate, Object>> props, Class<T> clazz) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ try {
+ TitanGraph tGraph = graph.left().value();
+
+ TitanGraphQuery<? extends TitanGraphQuery> query = tGraph.query();
+ query = query.has(GraphPropertiesDictionary.LABEL.getProperty(), type.getName());
+
+ if (props != null && !props.isEmpty()) {
+ TitanPredicate predicate = null;
+ Object object = null;
+ for (Map.Entry<String, Entry<TitanPredicate, Object>> entry : props.entrySet()) {
+ predicate = entry.getValue().getKey();
+ object = entry.getValue().getValue();
+ query = query.has(entry.getKey(), predicate, object);
+ }
+ }
+ Iterable<TitanVertex> vertices = query.vertices();
+ if (vertices == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<T> result = new ArrayList<T>();
+
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> newProp = getProperties(vertex);
+ T element = GraphElementFactory.createElement(type.getName(), GraphElementTypeEnum.Node, newProp, clazz);
+ result.add(element);
+ }
+ if (result.size() == 0) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("No nodes in graph for criteria : from type = {} and properties = {}", type, props);
+ }
+ return Either.left(result);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type = {} amd properties = {}. error: {}", type, props, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed get by criteria for type = {} and properties = {}. error : {}", type, props, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ }
+
+ public <T extends GraphNode> Either<List<T>, TitanOperationStatus> getAll(NodeTypeEnum type, Class<T> clazz) {
+ return getByCriteria(type, null, clazz);
+ }
+
+ /**
+ *
+ * @param node
+ * @param clazz
+ * @return
+ */
+ public <T extends GraphNode> Either<T, TitanOperationStatus> updateNode(GraphNode node, Class<T> clazz) {
+ logger.debug("Try to update node for {}", node.getKeyValueId());
+
+ ImmutablePair<String, Object> keyValueId = node.getKeyValueId();
+ Either<Vertex, TitanOperationStatus> vertexByProperty = getVertexByPropertyAndLabel(keyValueId.getKey(), keyValueId.getValue(), node.getLabel());
+
+ if (vertexByProperty.isLeft()) {
+ try {
+ Vertex vertex = vertexByProperty.left().value();
+
+ Map<String, Object> mapProps = node.toGraphMap();
+
+ for (Map.Entry<String, Object> entry : mapProps.entrySet()) {
+ if (!entry.getKey().equals(node.getUniqueIdKey())) {
+ vertex.property(entry.getKey(), entry.getValue());
+ }
+ }
+
+ Either<Vertex, TitanOperationStatus> vertexByPropertyAndLabel = getVertexByPropertyAndLabel(keyValueId.getKey(), keyValueId.getValue(), node.getLabel());
+ if (vertexByPropertyAndLabel.isRight()) {
+ return Either.right(vertexByPropertyAndLabel.right().value());
+ } else {
+ Map<String, Object> newProp = getProperties(vertexByPropertyAndLabel.left().value());
+ T updateNode = GraphElementFactory.createElement(node.getLabel(), GraphElementTypeEnum.Node, newProp, clazz);
+ return Either.left(updateNode);
+ }
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to update node for {}", node.getKeyValueId(), e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to update node for {} error :{}", node.getKeyValueId(), vertexByProperty.right().value());
+ }
+ return Either.right(vertexByProperty.right().value());
+ }
+
+ }
+
+ public TitanOperationStatus updateVertex(GraphNode node, TitanVertex vertex) {
+ logger.debug("Try to update node for {}", node.getKeyValueId());
+ try {
+
+ Map<String, Object> mapProps = node.toGraphMap();
+
+ for (Map.Entry<String, Object> entry : mapProps.entrySet()) {
+ if (!entry.getKey().equals(node.getUniqueIdKey())) {
+ vertex.property(entry.getKey(), entry.getValue());
+ }
+ }
+
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to update node for {}", node.getKeyValueId(), e);
+ }
+ return TitanGraphClient.handleTitanException(e);
+ }
+ return TitanOperationStatus.OK;
+
+ }
+
+ /**
+ *
+ * @param node
+ * @param clazz
+ * @return
+ */
+ public <T extends GraphNode> Either<T, TitanOperationStatus> deleteNode(GraphNode node, Class<T> clazz) {
+ logger.debug("Try to delete node for {}", node.getKeyValueId());
+ ImmutablePair<String, Object> keyValueId = node.getKeyValueId();
+ return deleteNode(keyValueId.getKey(), keyValueId.getValue(), clazz);
+ }
+
+ /**
+ *
+ * @param keyName
+ * @param keyValue
+ * @param clazz
+ * @return
+ */
+ public <T extends GraphNode> Either<T, TitanOperationStatus> deleteNode(String keyName, Object keyValue, Class<T> clazz) {
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = getVertexByProperty(keyName, keyValue);
+
+ if (vertexByProperty.isLeft()) {
+ try {
+ Vertex vertex = vertexByProperty.left().value();
+
+ Map<String, Object> properties = getProperties(vertex);
+ if (properties != null) {
+ String label = (String) properties.get(GraphPropertiesDictionary.LABEL.getProperty());
+
+ T node = GraphElementFactory.createElement(label, GraphElementTypeEnum.Node, properties, clazz);
+ if (node != null) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ TitanGraph tGraph = graph.left().value();
+ vertex.remove();
+ } else {
+ return Either.right(graph.right().value());
+ }
+ return Either.left(node);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to delete node for {} = {}. Missing label property on node", keyName, keyValue);
+ }
+ return Either.right(TitanOperationStatus.MISSING_NODE_LABEL);
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to delete node for {} = {}. Missing label property on node", keyName, keyValue);
+ }
+ return Either.right(TitanOperationStatus.MISSING_NODE_LABEL);
+ }
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to delete node for {} = {}. error: {}", keyName, keyValue, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+
+ } else {
+ return Either.right(vertexByProperty.right().value());
+ }
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> deleteRelation(GraphRelation relation) {
+ logger.debug("try to delete relation from [{}] to [{}]", relation.getFrom(), relation.getTo());
+ RelationEndPoint from = relation.getFrom();
+ RelationEndPoint to = relation.getTo();
+ ImmutablePair<String, Object> fromKeyId = new ImmutablePair<String, Object>(from.getIdName(), from.getIdValue());
+ ImmutablePair<String, Object> toKeyId = new ImmutablePair<String, Object>(to.getIdName(), to.getIdValue());
+
+ return deleteEdge(relation.getType(), fromKeyId, toKeyId, from.getLabel().getName(), to.getLabel().getName());
+
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> deleteRelation(GraphNode from, GraphNode to, GraphEdgeLabels label) {
+ logger.debug("try to delete relation from [{}] to [{}]", from.getKeyValueId(), to.getKeyValueId());
+ return deleteEdge(label.getProperty(), from.getKeyValueId(), to.getKeyValueId(), from.getLabel(), to.getLabel());
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> deleteEdge(String type, ImmutablePair<String, Object> fromKeyId, ImmutablePair<String, Object> toKeyId, String fromLabel, String toLabel) {
+ Either<Edge, TitanOperationStatus> edgeS = getEdgeByVerticies(fromKeyId.getKey(), fromKeyId.getValue(), toKeyId.getKey(), toKeyId.getValue(), type);
+ if (edgeS.isLeft()) {
+ try {
+ Edge edge = edgeS.left().value();
+
+ Vertex vertexOut = edge.outVertex();
+ Vertex vertexIn = edge.inVertex();
+
+ GraphNode nodeOut = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(vertexOut), GraphNode.class);
+ GraphNode nodeIn = GraphElementFactory.createElement(toLabel, GraphElementTypeEnum.Node, getProperties(vertexIn), GraphNode.class);
+
+ GraphRelation newRelation = GraphElementFactory.createRelation(edge.label(), getProperties(edge), nodeOut, nodeIn);
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+
+ if (graph.isLeft()) {
+ edge.remove();
+ ;
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to delete relation {} from {} to {}. error: {}", type, fromKeyId, toKeyId, graph.right().value());
+ }
+ return Either.right(graph.right().value());
+ }
+ return Either.left(newRelation);
+ } catch (Exception e) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to delete relation {} from {} to {}. error: {}", type, fromKeyId, toKeyId, e);
+ }
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to delete relation {} from {} to {}. error: {}", type, fromKeyId, toKeyId, edgeS.right().value());
+ }
+ return Either.right(edgeS.right().value());
+ }
+ }
+
+ public void setTitanGraphClient(TitanGraphClient titanGraphClient) {
+ this.titanClient = titanGraphClient;
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> deleteIncomingRelation(GraphRelation relation) {
+
+ RelationEndPoint to = relation.getTo();
+ ImmutablePair<String, Object> toKeyId = new ImmutablePair<String, Object>(to.getIdName(), to.getIdValue());
+
+ return deleteIncomingEdge(relation.getType(), toKeyId);
+
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> deleteIncomingEdge(String type, ImmutablePair<String, Object> toKeyId) {
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+
+ if (graph.isLeft()) {
+ Either<TitanVertex, TitanOperationStatus> rootVertexResult = getVertexByProperty(toKeyId.getKey(), toKeyId.getValue());
+ if (rootVertexResult.isLeft()) {
+ Vertex rootVertex = rootVertexResult.left().value();
+ Iterator<Edge> edgesIterator = rootVertex.edges(Direction.IN, type);
+ if (edgesIterator != null) {
+
+ Edge edge = null;
+
+ if (edgesIterator.hasNext()) {
+ edge = edgesIterator.next();
+ if (edgesIterator.hasNext()) {
+ return Either.right(TitanOperationStatus.MULTIPLE_EDGES_WITH_SAME_LABEL);
+ }
+ } else {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ logger.debug("Find the tail vertex of the edge of type {} to vertex {}", type, toKeyId);
+ Vertex vertexOut = edge.outVertex();
+ String fromLabel = vertexOut.value(GraphPropertiesDictionary.LABEL.getProperty());
+ String toLabel = rootVertex.value(GraphPropertiesDictionary.LABEL.getProperty());
+ logger.debug("The label of the outgoing vertex is {}", fromLabel);
+ GraphNode nodeOut = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(vertexOut), GraphNode.class);
+
+ GraphNode nodeIn = GraphElementFactory.createElement(toLabel, GraphElementTypeEnum.Node, getProperties(rootVertex), GraphNode.class);
+
+ GraphRelation newRelation = GraphElementFactory.createRelation(edge.label(), getProperties(edge), nodeOut, nodeIn);
+
+ edge.remove();
+
+ return Either.left(newRelation);
+
+ } else {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ } else {
+ return Either.right(graph.right().value());
+ }
+
+ } else {
+ return Either.right(graph.right().value());
+ }
+
+ }
+
+ public Either<GraphRelation, TitanOperationStatus> deleteOutgoingRelation(GraphRelation relation) {
+
+ RelationEndPoint from = relation.getFrom();
+ ImmutablePair<String, Object> fromKeyId = new ImmutablePair<String, Object>(from.getIdName(), from.getIdValue());
+
+ return deleteOutgoingEdge(relation.getType(), fromKeyId);
+
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> deleteOutgoingEdge(String type, ImmutablePair<String, Object> toKeyId) {
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+
+ if (graph.isLeft()) {
+ Either<TitanVertex, TitanOperationStatus> rootVertexResult = getVertexByProperty(toKeyId.getKey(), toKeyId.getValue());
+ if (rootVertexResult.isLeft()) {
+ Vertex rootVertex = rootVertexResult.left().value();
+ Iterator<Edge> edgesIterator = rootVertex.edges(Direction.OUT, type);
+ if (edgesIterator != null) {
+
+ Edge edge = null;
+
+ if (edgesIterator.hasNext()) {
+ edge = edgesIterator.next();
+ if (edgesIterator.hasNext()) {
+ return Either.right(TitanOperationStatus.MULTIPLE_EDGES_WITH_SAME_LABEL);
+ }
+ } else {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ logger.debug("Find the tail vertex of the edge of type {} to vertex ", type, toKeyId);
+ Vertex vertexIn = edge.inVertex();
+ String toLabel = vertexIn.value(GraphPropertiesDictionary.LABEL.getProperty());
+ String fromLabel = rootVertex.value(GraphPropertiesDictionary.LABEL.getProperty());
+ logger.debug("The label of the tail vertex is {}", toLabel);
+ GraphNode nodeFrom = GraphElementFactory.createElement(fromLabel, GraphElementTypeEnum.Node, getProperties(rootVertex), GraphNode.class);
+
+ GraphNode nodeTo = GraphElementFactory.createElement(toLabel, GraphElementTypeEnum.Node, getProperties(vertexIn), GraphNode.class);
+
+ GraphRelation newRelation = GraphElementFactory.createRelation(edge.label(), getProperties(edge), nodeFrom, nodeTo);
+
+ edge.remove();
+
+ return Either.left(newRelation);
+
+ } else {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ } else {
+ return Either.right(graph.right().value());
+ }
+
+ } else {
+ return Either.right(graph.right().value());
+ }
+ }
+
+ /**
+ *
+ * @param id
+ * @return
+ */
+
+ public TitanOperationStatus lockElement(String id, NodeTypeEnum type) {
+
+ StringBuffer lockId = new StringBuffer(LOCK_NODE_PREFIX);
+ lockId.append(type.getName()).append("_").append(id);
+ return lockNode(lockId.toString());
+ }
+
+ public TitanOperationStatus lockElement(GraphNode node) {
+
+ StringBuffer lockId = createLockElementId(node);
+
+ return lockNode(lockId.toString());
+ }
+
+ private TitanOperationStatus lockNode(String lockId) {
+ TitanOperationStatus status = TitanOperationStatus.OK;
+
+ GraphNodeLock lockNode = new GraphNodeLock(lockId);
+
+ Either<GraphNodeLock, TitanOperationStatus> lockNodeNew = createNode(lockNode, GraphNodeLock.class);
+ if (lockNodeNew.isLeft()) {
+ logger.debug("before commit, Lock node created for {}", lockId);
+ return titanClient.commit();
+ } else {
+ Either<TitanGraph, TitanOperationStatus> graph = titanClient.getGraph();
+ if (graph.isLeft()) {
+ TitanGraph tGraph = graph.left().value();
+ Either<TitanVertex, TitanOperationStatus> vertex = getVertexByProperty(lockNode.getUniqueIdKey(), lockNode.getUniqueId());
+ if (vertex.isLeft()) {
+ status = relockNode(lockNode, lockNodeNew, tGraph, vertex);
+ } else {
+ status = vertex.right().value();
+ }
+ } else {
+ status = graph.right().value();
+ }
+ }
+ return status;
+ }
+
+ private TitanOperationStatus relockNode(GraphNodeLock lockNode, Either<GraphNodeLock, TitanOperationStatus> lockNodeNew, TitanGraph tGraph, Either<TitanVertex, TitanOperationStatus> vertex) {
+ TitanOperationStatus status = TitanOperationStatus.OK;
+ Long time = vertex.left().value().value(GraphPropertiesDictionary.CREATION_DATE.getProperty());
+ Long lockTimeout = ConfigurationManager.getConfigurationManager().getConfiguration().getTitanLockTimeout();
+ if (time + lockTimeout * 1000 < System.currentTimeMillis()) {
+ logger.debug("Found not released lock node with id {}", lockNode.getUniqueId());
+ vertex.left().value().remove();
+ lockNodeNew = createNode(lockNode, GraphNodeLock.class);
+ if (lockNodeNew.isLeft()) {
+ logger.debug("Lock node created for {}", lockNode.getUniqueIdKey());
+ return titanClient.commit();
+ } else {
+ logger.debug("Failed Lock node for {} . Commit transacton for deleted previous vertex .", lockNode.getUniqueIdKey());
+ titanClient.commit();
+ status = checkLockError(lockNode.getUniqueIdKey(), lockNodeNew);
+ }
+ } else {
+ logger.debug("Failed Lock node for {} rollback transacton", lockNode.getUniqueIdKey());
+ titanClient.rollback();
+ status = checkLockError(lockNode.getUniqueIdKey(), lockNodeNew);
+ }
+ return status;
+ }
+
+ public <T extends GraphNode> Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> getChildrenNodes(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz, boolean withEdges) {
+
+ List<ImmutablePair<T, GraphEdge>> immutablePairs = new ArrayList<ImmutablePair<T, GraphEdge>>();
+
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanClient.getGraph();
+ if (graphRes.isRight()) {
+ logger.error("Failed to retrieve graph. status is {}", graphRes);
+ return Either.right(graphRes.right().value());
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = titanGraph.query().has(key, uniqueId).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ Vertex rootVertex = vertices.iterator().next();
+
+ Iterator<Edge> edgesCreatorIterator = rootVertex.edges(Direction.OUT, edgeType.getProperty());
+ if (edgesCreatorIterator != null) {
+ while (edgesCreatorIterator.hasNext()) {
+ Edge edge = edgesCreatorIterator.next();
+ GraphEdge graphEdge = null;
+
+ if (withEdges) {
+ Map<String, Object> edgeProps = getProperties(edge);
+ GraphEdgeLabels edgeTypeFromGraph = GraphEdgeLabels.getByName(edge.label());
+ graphEdge = new GraphEdge(edgeTypeFromGraph, edgeProps);
+ }
+
+ Vertex outgoingVertex = edge.inVertex();
+ Map<String, Object> properties = getProperties(outgoingVertex);
+ T data = GraphElementFactory.createElement(nodeTypeEnum.getName(), GraphElementTypeEnum.Node, properties, clazz);
+
+ ImmutablePair<T, GraphEdge> immutablePair = new ImmutablePair<T, GraphEdge>(clazz.cast(data), graphEdge);
+ immutablePairs.add(immutablePair);
+ }
+ }
+
+ if (true == immutablePairs.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(immutablePairs);
+
+ }
+
+ public Either<List<ImmutablePair<TitanVertex, Edge>>, TitanOperationStatus> getChildrenVertecies(String key, String uniqueId, GraphEdgeLabels edgeType) {
+
+ List<ImmutablePair<TitanVertex, Edge>> immutablePairs = new ArrayList<ImmutablePair<TitanVertex, Edge>>();
+
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanClient.getGraph();
+ if (graphRes.isRight()) {
+ logger.error("Failed to retrieve graph. status is {}", graphRes);
+ return Either.right(graphRes.right().value());
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = titanGraph.query().has(key, uniqueId).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ Vertex rootVertex = vertices.iterator().next();
+
+ Iterator<Edge> edgesCreatorIterator = rootVertex.edges(Direction.OUT, edgeType.getProperty());
+ if (edgesCreatorIterator != null) {
+ while (edgesCreatorIterator.hasNext()) {
+ Edge edge = edgesCreatorIterator.next();
+ TitanVertex vertex = (TitanVertex) edge.inVertex();
+
+ ImmutablePair<TitanVertex, Edge> immutablePair = new ImmutablePair<TitanVertex, Edge>(vertex, edge);
+ immutablePairs.add(immutablePair);
+ }
+ }
+ if (true == immutablePairs.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(immutablePairs);
+
+ }
+
+ public <T extends GraphNode> Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> getChildrenNodes(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz) {
+ return this.getChildrenNodes(key, uniqueId, edgeType, nodeTypeEnum, clazz, true);
+ }
+
+ private TitanOperationStatus checkLockError(String lockId, Either<GraphNodeLock, TitanOperationStatus> lockNodeNew) {
+ TitanOperationStatus status;
+ TitanOperationStatus error = lockNodeNew.right().value();
+ logger.debug("Failed to Lock node for {} error = {}", lockId, error);
+ if (error.equals(TitanOperationStatus.TITAN_SCHEMA_VIOLATION) || error.equals(TitanOperationStatus.ILLEGAL_ARGUMENT)) {
+ status = TitanOperationStatus.ALREADY_LOCKED;
+ } else {
+ status = error;
+ }
+ return status;
+ }
+
+ /**
+ *
+ * @param node
+ * @return
+ */
+ public TitanOperationStatus releaseElement(GraphNode node) {
+ StringBuffer lockId = createLockElementId(node);
+
+ return unlockNode(lockId);
+ }
+
+ private TitanOperationStatus unlockNode(StringBuffer lockId) {
+ GraphNodeLock lockNode = new GraphNodeLock(lockId.toString());
+
+ Either<GraphNodeLock, TitanOperationStatus> lockNodeNew = deleteNode(lockNode, GraphNodeLock.class);
+ if (lockNodeNew.isLeft()) {
+ logger.debug("Lock node released for lock id = {}", lockId);
+ return titanClient.commit();
+ } else {
+ titanClient.rollback();
+ TitanOperationStatus error = lockNodeNew.right().value();
+ logger.debug("Failed to Release node for lock id {} error = {}", lockId, error);
+ return error;
+ }
+ }
+
+ public TitanOperationStatus releaseElement(String id, NodeTypeEnum type) {
+ StringBuffer lockId = new StringBuffer(LOCK_NODE_PREFIX);
+ lockId.append(type.getName()).append("_").append(id);
+ return unlockNode(lockId);
+ }
+
+ private StringBuffer createLockElementId(GraphNode node) {
+ StringBuffer lockId = new StringBuffer(LOCK_NODE_PREFIX);
+ lockId.append(node.getLabel()).append("_").append(node.getUniqueId());
+ return lockId;
+ }
+
+ public <T extends GraphNode> Either<ImmutablePair<T, GraphEdge>, TitanOperationStatus> getChild(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz) {
+
+ Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> childrenNodes = getChildrenNodes(key, uniqueId, edgeType, nodeTypeEnum, clazz);
+
+ if (childrenNodes.isRight()) {
+ return Either.right(childrenNodes.right().value());
+ }
+
+ List<ImmutablePair<T, GraphEdge>> value = childrenNodes.left().value();
+
+ if (value.size() > 1) {
+ return Either.right(TitanOperationStatus.MULTIPLE_CHILDS_WITH_SAME_EDGE);
+ }
+
+ return Either.left(value.get(0));
+
+ }
+
+ public ImmutablePair<TitanVertex, Edge> getChildVertex(TitanVertex vertex, GraphEdgeLabels edgeType) {
+
+ ImmutablePair<TitanVertex, Edge> pair = null;
+ Iterator<Edge> edges = vertex.edges(Direction.OUT, edgeType.getProperty());
+ if (edges.hasNext()) {
+ // get only first edge
+ Edge edge = edges.next();
+ pair = new ImmutablePair<TitanVertex, Edge>((TitanVertex) edge.inVertex(), edge);
+ }
+ return pair;
+ }
+
+ public <T extends GraphNode> Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> getParentNodes(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz) {
+
+ List<ImmutablePair<T, GraphEdge>> immutablePairs = new ArrayList<ImmutablePair<T, GraphEdge>>();
+
+ T data = null;
+ GraphEdge graphEdge = null;
+
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanClient.getGraph();
+ if (graphRes.isRight()) {
+ logger.error("Failed to retrieve graph. status is {}", graphRes);
+ return Either.right(graphRes.right().value());
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = titanGraph.query().has(key, uniqueId).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ Vertex rootVertex = vertices.iterator().next();
+
+ Iterator<Edge> edgesCreatorIterator = rootVertex.edges(Direction.IN, edgeType.name());
+ if (edgesCreatorIterator != null) {
+ while (edgesCreatorIterator.hasNext()) {
+ Edge edge = edgesCreatorIterator.next();
+ Map<String, Object> edgeProps = getProperties(edge);
+ GraphEdgeLabels edgeTypeFromGraph = GraphEdgeLabels.getByName(edge.label());
+ graphEdge = new GraphEdge(edgeTypeFromGraph, edgeProps);
+
+ Vertex outgoingVertex = edge.outVertex();
+ Map<String, Object> properties = getProperties(outgoingVertex);
+ data = GraphElementFactory.createElement(nodeTypeEnum.getName(), GraphElementTypeEnum.Node, properties, clazz);
+
+ ImmutablePair<T, GraphEdge> immutablePair = new ImmutablePair<T, GraphEdge>(clazz.cast(data), graphEdge);
+ immutablePairs.add(immutablePair);
+ }
+ }
+
+ if (true == immutablePairs.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(immutablePairs);
+
+ }
+
+ public <T extends GraphNode> Either<ImmutablePair<T, GraphEdge>, TitanOperationStatus> getParentNode(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz) {
+
+ Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> parentNodesRes = this.getParentNodes(key, uniqueId, edgeType, nodeTypeEnum, clazz);
+
+ if (parentNodesRes.isRight()) {
+ logger.debug("failed to get edge key:{} uniqueId:{} edgeType {} nodeTypeEnum: {}, reason:{}", key, uniqueId, edgeType, nodeTypeEnum, parentNodesRes.right().value());
+ return Either.right(parentNodesRes.right().value());
+ }
+
+ List<ImmutablePair<T, GraphEdge>> value = parentNodesRes.left().value();
+
+ if (value.size() > 1) {
+ return Either.right(TitanOperationStatus.MULTIPLE_CHILDS_WITH_SAME_EDGE);
+ }
+
+ return Either.left(value.get(0));
+ }
+
+ public <T extends GraphNode> Either<ImmutablePair<T, GraphEdge>, TitanOperationStatus> getChildByEdgeCriteria(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz, Map<String, Object> edgeProperties) {
+
+ Either<Edge, TitanOperationStatus> outgoingEdgeByCriteria = getOutgoingEdgeByCriteria(key, uniqueId, edgeType, edgeProperties);
+ if (outgoingEdgeByCriteria.isRight()) {
+ TitanOperationStatus status = outgoingEdgeByCriteria.right().value();
+ logger.debug("Cannot find outgoing edge from vertex {} with label {} and properties {}", uniqueId, edgeType, edgeProperties);
+ return Either.right(status);
+ }
+
+ Edge edge = outgoingEdgeByCriteria.left().value();
+ Map<String, Object> edgeProps = getProperties(edge);
+ GraphEdgeLabels edgeTypeFromGraph = GraphEdgeLabels.getByName(edge.label());
+ GraphEdge graphEdge = new GraphEdge(edgeTypeFromGraph, edgeProps);
+
+ Vertex outgoingVertex = edge.inVertex();
+ Map<String, Object> properties = getProperties(outgoingVertex);
+ T data = GraphElementFactory.createElement(nodeTypeEnum.getName(), GraphElementTypeEnum.Node, properties, clazz);
+
+ ImmutablePair<T, GraphEdge> immutablePair = new ImmutablePair<T, GraphEdge>(clazz.cast(data), graphEdge);
+
+ return Either.left(immutablePair);
+ }
+
+ public Either<ImmutablePair<TitanVertex, Edge>, TitanOperationStatus> getChildByEdgeCriteria(TitanVertex vertex, GraphEdgeLabels edgeType, Map<String, Object> edgeProperties) {
+
+ Either<Edge, TitanOperationStatus> outgoingEdgeByCriteria = getOutgoingEdgeByCriteria(vertex, edgeType, edgeProperties);
+ if (outgoingEdgeByCriteria.isRight()) {
+ TitanOperationStatus status = outgoingEdgeByCriteria.right().value();
+ logger.debug("Cannot find outgoing edge from vertex {} with label {} and properties {}", vertex, edgeType, edgeProperties);
+ return Either.right(status);
+ }
+ Edge edge = outgoingEdgeByCriteria.left().value();
+
+ TitanVertex outgoingVertex = (TitanVertex) edge.inVertex();
+
+ ImmutablePair<TitanVertex, Edge> immutablePair = new ImmutablePair<TitanVertex, Edge>(outgoingVertex, edge);
+
+ return Either.left(immutablePair);
+ }
+
+ public Either<Edge, TitanOperationStatus> getOutgoingEdgeByCriteria(String key, String value, GraphEdgeLabels label, Map<String, Object> props) {
+
+ Either<TitanVertex, TitanOperationStatus> vertexFrom = getVertexByProperty(key, value);
+ if (vertexFrom.isRight()) {
+ TitanOperationStatus status = vertexFrom.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ return Either.right(status);
+ }
+
+ return getOutgoingEdgeByCriteria(vertexFrom.left().value(), label, props);
+ }
+
+ public Either<Edge, TitanOperationStatus> getOutgoingEdgeByCriteria(TitanVertex vertex, GraphEdgeLabels label, Map<String, Object> props) {
+
+ TitanVertexQuery<?> query = vertex.query();
+ query = query.direction(Direction.OUT).labels(label.getProperty());
+
+ if (props != null && !props.isEmpty()) {
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+ Edge matchingEdge = null;
+ Iterable<TitanEdge> edges = query.edges();
+ if (edges == null) {
+ logger.debug("No edges in graph for criteria");
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ Iterator<TitanEdge> eIter = edges.iterator();
+ if (eIter.hasNext()) {
+ Edge edge = eIter.next();
+ matchingEdge = edge;
+ }
+
+ if (matchingEdge == null) {
+ logger.debug("No edges in graph for criteria");
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ return Either.left(matchingEdge);
+ }
+
+ public <T extends GraphNode> Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> deleteChildrenNodes(String key, String uniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz) {
+
+ List<ImmutablePair<T, GraphEdge>> result = new ArrayList<ImmutablePair<T, GraphEdge>>();
+
+ Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> childrenNodesRes = getChildrenNodes(key, uniqueId, edgeType, nodeTypeEnum, clazz);
+
+ if (childrenNodesRes.isRight()) {
+ TitanOperationStatus status = childrenNodesRes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<T, GraphEdge>> list = childrenNodesRes.left().value();
+ for (ImmutablePair<T, GraphEdge> pair : list) {
+ T node = pair.getKey();
+ Either<T, TitanOperationStatus> deleteNodeRes = this.deleteNode(node, clazz);
+ if (deleteNodeRes.isRight()) {
+ TitanOperationStatus status = deleteNodeRes.right().value();
+ logger.error("Failed to delete node {} . status is {}", node, status);
+ return Either.right(status);
+ }
+ ImmutablePair<T, GraphEdge> deletedPair = new ImmutablePair<T, GraphEdge>(node, pair.getValue());
+ result.add(deletedPair);
+ }
+
+ return Either.left(result);
+
+ }
+
+ public void setProperties(Element element, Map<String, Object> properties) {
+
+ if (properties != null && false == properties.isEmpty()) {
+
+ Object[] propertyKeyValues = new Object[properties.size() * 2];
+ int i = 0;
+ for (Entry<String, Object> entry : properties.entrySet()) {
+ propertyKeyValues[i++] = entry.getKey();
+ propertyKeyValues[i++] = entry.getValue();
+ }
+
+ ElementHelper.attachProperties(element, propertyKeyValues);
+
+ }
+
+ }
+
+ public Map<String, Object> getProperties(Element element) {
+
+ Map<String, Object> result = new HashMap<String, Object>();
+
+ if (element != null && element.keys() != null && element.keys().size() > 0) {
+ Map<String, Property> propertyMap = ElementHelper.propertyMap(element, element.keys().toArray(new String[element.keys().size()]));
+
+ for (Entry<String, Property> entry : propertyMap.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue().value();
+
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+
+ public Object getProperty(TitanVertex vertex, String key) {
+ PropertyKey propertyKey = titanClient.getGraph().left().value().getPropertyKey(key);
+ Object value = vertex.valueOrNull(propertyKey);
+ return value;
+ }
+
+ public Object getProperty(Edge edge, String key) {
+ Object value = null;
+ Property<Object> property = edge.property(key);
+ if (property != null) {
+ return property.orElse(null);
+ }
+ return value;
+ }
+
+ public <T extends GraphNode> Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> getChildrenByEdgeCriteria(Vertex vertex, String vertexUniqueId, GraphEdgeLabels edgeType, NodeTypeEnum nodeTypeEnum, Class<T> clazz,
+ Map<String, Object> edgeProperties) {
+
+ List<ImmutablePair<T, GraphEdge>> result = new ArrayList<>();
+
+ Either<List<Edge>, TitanOperationStatus> outgoingEdgeByCriteria = getOutgoingEdgesByCriteria(vertex, edgeType, edgeProperties);
+ if (outgoingEdgeByCriteria.isRight()) {
+ TitanOperationStatus status = outgoingEdgeByCriteria.right().value();
+ logger.debug("Cannot find outgoing edge from vertex {} with label {} and properties {}", vertexUniqueId, edgeType, edgeProperties);
+ return Either.right(status);
+ }
+
+ List<Edge> edges = outgoingEdgeByCriteria.left().value();
+ if (edges != null) {
+ for (Edge edge : edges) {
+ Map<String, Object> edgeProps = getProperties(edge);
+ GraphEdgeLabels edgeTypeFromGraph = GraphEdgeLabels.getByName(edge.label());
+ GraphEdge graphEdge = new GraphEdge(edgeTypeFromGraph, edgeProps);
+
+ Vertex outgoingVertex = edge.inVertex();
+ Map<String, Object> properties = getProperties(outgoingVertex);
+ T data = GraphElementFactory.createElement(nodeTypeEnum.getName(), GraphElementTypeEnum.Node, properties, clazz);
+
+ ImmutablePair<T, GraphEdge> immutablePair = new ImmutablePair<T, GraphEdge>(clazz.cast(data), graphEdge);
+ result.add(immutablePair);
+ }
+ }
+
+ return Either.left(result);
+ }
+
+ public Either<List<Edge>, TitanOperationStatus> getOutgoingEdgesByCriteria(Vertex vertexFrom, GraphEdgeLabels label, Map<String, Object> props) {
+
+ List<Edge> edgesResult = new ArrayList<>();
+
+ TitanVertex titanVertex = (TitanVertex) vertexFrom;
+ TitanVertexQuery<?> query = titanVertex.query();
+
+ query = query.direction(Direction.OUT).labels(label.getProperty());
+
+ if (props != null && !props.isEmpty()) {
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+
+ Iterable<TitanEdge> edges = query.edges();
+ Iterator<TitanEdge> eIter = edges.iterator();
+ if (edges == null || false == eIter.hasNext()) {
+ logger.debug("No edges found in graph for criteria (label = {} properties={})", label.getProperty(), props);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ while (eIter.hasNext()) {
+ Edge edge = eIter.next();
+ edgesResult.add(edge);
+ }
+
+ if (edgesResult.isEmpty()) {
+ logger.debug("No edges found in graph for criteria (label = {} properties={})", label.getProperty(), props);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ return Either.left(edgesResult);
+
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGraphClient.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGraphClient.java
new file mode 100644
index 0000000000..68a05a91bf
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanGraphClient.java
@@ -0,0 +1,872 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.titan;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.graph.datatype.ActionEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.CategoryData;
+import org.openecomp.sdc.be.resources.data.ResourceCategoryData;
+import org.openecomp.sdc.be.resources.data.ServiceCategoryData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.InvalidElementException;
+import com.thinkaurelius.titan.core.InvalidIDException;
+import com.thinkaurelius.titan.core.PropertyKey;
+import com.thinkaurelius.titan.core.QueryException;
+import com.thinkaurelius.titan.core.SchemaViolationException;
+import com.thinkaurelius.titan.core.TitanConfigurationException;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanGraphQuery;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.TitanVertexProperty;
+import com.thinkaurelius.titan.core.schema.ConsistencyModifier;
+import com.thinkaurelius.titan.core.schema.TitanGraphIndex;
+import com.thinkaurelius.titan.core.schema.TitanManagement;
+import com.thinkaurelius.titan.core.util.TitanCleanup;
+import com.thinkaurelius.titan.diskstorage.ResourceUnavailableException;
+import com.thinkaurelius.titan.diskstorage.locking.PermanentLockingException;
+import com.thinkaurelius.titan.graphdb.database.idassigner.IDPoolExhaustedException;
+
+
+import fj.data.Either;
+
+@Component("titan-client")
+public class TitanGraphClient {
+
+ private static Logger logger = LoggerFactory.getLogger(TitanGraphClient.class.getName());
+ private static Logger healthLogger = LoggerFactory.getLogger("titan.healthcheck");
+
+ private static final String HEALTH_CHECK = GraphPropertiesDictionary.HEALTH_CHECK.getProperty();
+ private static final String OK = "GOOD";
+
+ private class HealthCheckTask implements Callable<Vertex> {
+ @Override
+ public Vertex call() {
+
+ TitanVertex v = (TitanVertex) graph.query().has(HEALTH_CHECK, OK).vertices().iterator().next();
+ TitanVertexProperty<String> property = v.property("healthcheck", OK + "_" + System.currentTimeMillis());
+ healthLogger.trace("Health Check Node Found...{}", v.property(HEALTH_CHECK));
+ graph.tx().commit();
+
+ // Vertex v = graph.getVertices(HEALTH_CHECK, OK).iterator().next();
+ // v.setProperty("healthcheck", OK + "_" +
+ // System.currentTimeMillis());
+ // graph.commit();
+ // healthLogger.trace("Health Check Node
+ // Found..."+v.getProperty(HEALTH_CHECK) );
+ return v;
+ }
+ }
+
+ private class HealthCheckScheduledTask implements Runnable {
+ @Override
+ public void run() {
+ healthLogger.trace("Executing TITAN Health Check Task - Start");
+ boolean healthStatus = isGraphOpen();
+ healthLogger.trace("Executing TITAN Health Check Task - Status = {}", healthStatus);
+ if (healthStatus != lastHealthState) {
+ logger.trace("TITAN Health State Changed to {}. Issuing alarm / recovery alarm...", healthStatus);
+ lastHealthState = healthStatus;
+ logAlarm();
+ }
+ }
+ }
+
+ private class ReconnectTask implements Runnable {
+ @Override
+ public void run() {
+ logger.trace("Trying to reconnect to Titan...");
+ if (graph == null) {
+ createGraph(titanCfgFile, true);
+ }
+ }
+ }
+
+ private TitanGraph graph;
+
+ // Health Check Variables
+
+ /**
+ * This executor will execute the health check task on a callable task that
+ * can be executed with a timeout.
+ */
+ ExecutorService healthCheckExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "Titan-Health-Check-Thread");
+ }
+ });
+ private long healthCheckReadTimeout = 2;
+ HealthCheckTask healthCallableTask = new HealthCheckTask();
+ HealthCheckScheduledTask healthCheckScheduledTask = new HealthCheckScheduledTask();
+ boolean lastHealthState = false;
+
+ // Reconnection variables
+ private ScheduledExecutorService reconnectScheduler = null;
+ private ScheduledExecutorService healthCheckScheduler = null;
+ private Runnable reconnectTask = null;
+ private long reconnectInterval = 3;
+ @SuppressWarnings("rawtypes")
+ private Future reconnectFuture;
+
+ private String titanCfgFile = null;
+
+ public TitanGraphClient() {
+ super();
+
+ // Initialize a single threaded scheduler for health-check
+ this.healthCheckScheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "Titan-Health-Check-Task");
+ }
+ });
+
+ healthCheckReadTimeout = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getTitanHealthCheckReadTimeout(2);
+ reconnectInterval = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getTitanReconnectIntervalInSeconds(3);
+
+ logger.info("** TitanGraphClient created");
+ }
+
+ @PostConstruct
+ public TitanOperationStatus createGraph() {
+
+ logger.info("** createGraph started **");
+
+ if (ConfigurationManager.getConfigurationManager().getConfiguration().getTitanInMemoryGraph()) {
+ BaseConfiguration conf = new BaseConfiguration();
+ conf.setProperty("storage.backend", "inmemory");
+ graph = TitanFactory.open(conf);
+
+ createIndexesAndDefaults();
+
+ logger.info("** in memory graph created");
+ return TitanOperationStatus.OK;
+ } else {
+ this.titanCfgFile = ConfigurationManager.getConfigurationManager().getConfiguration().getTitanCfgFile();
+ if (titanCfgFile == null || titanCfgFile.isEmpty()) {
+ titanCfgFile = "config/titan.properties";
+ }
+
+ // yavivi
+ // In case connection failed on init time, schedule a reconnect task
+ // in the BG
+ TitanOperationStatus status = createGraph(titanCfgFile);
+ logger.debug("Create Titan graph status {}", status);
+ if (status != TitanOperationStatus.OK) {
+ this.startReconnectTask();
+ }
+
+ return status;
+ }
+ }
+
+ private void startHealthCheckTask() {
+ this.healthCheckScheduler.scheduleAtFixedRate(healthCheckScheduledTask, 0, reconnectInterval, TimeUnit.SECONDS);
+ }
+
+ /**
+ * This method will be invoked ONLY on init time in case Titan storage is
+ * down.
+ */
+ private void startReconnectTask() {
+ this.reconnectTask = new ReconnectTask();
+ // Initialize a single threaded scheduler
+ this.reconnectScheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "Titan-Reconnect-Task");
+ }
+ });
+
+ logger.info("Scheduling reconnect task {} with interval of {} seconds", reconnectTask, reconnectInterval);
+ reconnectFuture = this.reconnectScheduler.scheduleAtFixedRate(this.reconnectTask, 0, this.reconnectInterval,
+ TimeUnit.SECONDS);
+ }
+
+ public void cleanupGraph() {
+ if (graph != null) {
+ // graph.shutdown();
+ graph.close();
+ TitanCleanup.clear(graph);
+ }
+ }
+
+ public TitanOperationStatus createGraph(String titanCfgFile) {
+ logger.info("** createGraph with " + titanCfgFile + " started");
+ return createGraph(titanCfgFile, true);
+ }
+
+ public TitanOperationStatus createGraph(String titanCfgFile, boolean initializeGraph) {
+ logger.info("** createGraph with " + titanCfgFile + " and initializeGraph=" + initializeGraph + " started");
+ try {
+ logger.info("createGraph : try to load file " + titanCfgFile);
+ graph = TitanFactory.open(titanCfgFile);
+ if (graph == null) {
+ return TitanOperationStatus.NOT_CREATED;
+ }
+
+ } catch (Exception e) {
+ this.graph = null;
+ logger.info("createGraph : failed to open Titan graph with configuration file: " + titanCfgFile);
+ logger.trace("createGraph : failed to open Titan graph. ", e);
+ return TitanOperationStatus.NOT_CONNECTED;
+ }
+ if (true == initializeGraph) {
+ createIndexesAndDefaults();
+ }
+ logger.info("** Titan graph created ");
+
+ // Do some post creation actions
+ this.onGraphOpened();
+
+ return TitanOperationStatus.OK;
+ }
+
+ private void onGraphOpened() {
+ // if a reconnect task is running, cancel it.
+ if (this.reconnectFuture != null) {
+ logger.info("** Cancelling Titan reconnect task");
+ reconnectFuture.cancel(true);
+ }
+
+ // create health-check node
+ if (!graph.query().has(HEALTH_CHECK, OK).vertices().iterator().hasNext()) {
+ logger.trace("Healthcheck Singleton node does not exist, Creating healthcheck node...");
+ Vertex healthCheckNode = graph.addVertex();
+ healthCheckNode.property(HEALTH_CHECK, OK);
+ logger.trace("Healthcheck node created successfully. ID={}", healthCheckNode.property(T.id.getAccessor()));
+ graph.tx().commit();
+ } else {
+ logger.trace("Skipping Healthcheck Singleton node creation. Already exist...");
+ }
+ this.startHealthCheckTask();
+ }
+
+ private void createIndexesAndDefaults() {
+ createVertexIndixes();
+ createEdgeIndixes();
+ createDefaultUsers();
+ // createCategories();
+ }
+
+ public Either<TitanGraph, TitanOperationStatus> getGraph() {
+ if (graph != null) {
+ return Either.left(graph);
+ } else {
+ return Either.right(TitanOperationStatus.NOT_CREATED);
+ }
+ }
+
+ private TitanOperationStatus createVertexIndixes() {
+ logger.info("** createVertexIndixes started");
+ if (graph != null) {
+ // TitanManagement graphMgt = graph.getManagementSystem();
+ TitanManagement graphMgt = graph.openManagement();
+ TitanGraphIndex index = null;
+ for (GraphPropertiesDictionary prop : GraphPropertiesDictionary.values()) {
+ PropertyKey propKey = null;
+ if (!graphMgt.containsPropertyKey(prop.getProperty())) {
+ Class<?> clazz = prop.getClazz();
+ if (!ArrayList.class.getName().equals(clazz.getName())
+ && !HashMap.class.getName().equals(clazz.getName())) {
+ propKey = graphMgt.makePropertyKey(prop.getProperty()).dataType(prop.getClazz()).make();
+ }
+ } else {
+ propKey = graphMgt.getPropertyKey(prop.getProperty());
+ }
+ if (prop.isIndexed()) {
+ if (!graphMgt.containsGraphIndex(prop.getProperty())) {
+ if (prop.isUnique()) {
+ index = graphMgt.buildIndex(prop.getProperty(), Vertex.class).addKey(propKey).unique()
+ .buildCompositeIndex();
+
+ graphMgt.setConsistency(propKey, ConsistencyModifier.LOCK); // Ensures
+ // only
+ // one
+ // name
+ // per
+ // vertex
+ graphMgt.setConsistency(index, ConsistencyModifier.LOCK); // Ensures
+ // name
+ // uniqueness
+ // in
+ // the
+ // graph
+
+ } else {
+ graphMgt.buildIndex(prop.getProperty(), Vertex.class).addKey(propKey).buildCompositeIndex();
+ }
+ }
+ }
+ }
+ graphMgt.commit();
+ logger.info("** createVertexIndixes ended");
+ return TitanOperationStatus.OK;
+ }
+ logger.info("** createVertexIndixes ended, no Indixes were created.");
+ return TitanOperationStatus.NOT_CREATED;
+ }
+
+ private TitanOperationStatus createEdgeIndixes() {
+ logger.info("** createEdgeIndixes started");
+ if (graph != null) {
+ // TitanManagement graphMgt = graph.getManagementSystem();
+ TitanManagement graphMgt = graph.openManagement();
+ for (GraphEdgePropertiesDictionary prop : GraphEdgePropertiesDictionary.values()) {
+ if (!graphMgt.containsGraphIndex(prop.getProperty())) {
+ PropertyKey propKey = graphMgt.makePropertyKey(prop.getProperty()).dataType(prop.getClazz()).make();
+ graphMgt.buildIndex(prop.getProperty(), Edge.class).addKey(propKey).buildCompositeIndex();
+
+ }
+ }
+ graphMgt.commit();
+ logger.info("** createEdgeIndixes ended");
+ return TitanOperationStatus.OK;
+ }
+ logger.info("** createEdgeIndixes ended, no Indixes were created.");
+ return TitanOperationStatus.NOT_CREATED;
+ }
+
+ public TitanOperationStatus commit() {
+ if (graph != null) {
+ try {
+ graph.tx().commit();
+ return TitanOperationStatus.OK;
+ } catch (Exception e) {
+ return handleTitanException(e);
+ }
+ } else {
+ return TitanOperationStatus.NOT_CREATED;
+ }
+ }
+
+ public TitanOperationStatus rollback() {
+ if (graph != null) {
+ try {
+ // graph.rollback();
+ graph.tx().rollback();
+ return TitanOperationStatus.OK;
+ } catch (Exception e) {
+ return handleTitanException(e);
+ }
+ } else {
+ return TitanOperationStatus.NOT_CREATED;
+ }
+ }
+
+ public static TitanOperationStatus handleTitanException(Exception e) {
+ if (e instanceof TitanConfigurationException) {
+ return TitanOperationStatus.TITAN_CONFIGURATION;
+ }
+ if (e instanceof SchemaViolationException) {
+ return TitanOperationStatus.TITAN_SCHEMA_VIOLATION;
+ }
+ if (e instanceof PermanentLockingException) {
+ return TitanOperationStatus.TITAN_SCHEMA_VIOLATION;
+ }
+ if (e instanceof IDPoolExhaustedException) {
+ return TitanOperationStatus.GENERAL_ERROR;
+ }
+ if (e instanceof InvalidElementException) {
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+ if (e instanceof InvalidIDException) {
+ return TitanOperationStatus.INVALID_ID;
+ }
+ if (e instanceof QueryException) {
+ return TitanOperationStatus.INVALID_QUERY;
+ }
+ if (e instanceof ResourceUnavailableException) {
+ return TitanOperationStatus.RESOURCE_UNAVAILABLE;
+ }
+ if (e instanceof IllegalArgumentException) {
+ // TODO check the error message??
+ return TitanOperationStatus.ILLEGAL_ARGUMENT;
+ }
+
+ return TitanOperationStatus.GENERAL_ERROR;
+ }
+
+ public boolean getHealth() {
+ return this.lastHealthState;
+ }
+
+ private boolean isGraphOpen() {
+ healthLogger.trace("Invoking Titan health check ...");
+ Vertex v = null;
+ if (graph != null) {
+ try {
+ Future<Vertex> future = healthCheckExecutor.submit(healthCallableTask);
+ v = future.get(this.healthCheckReadTimeout, TimeUnit.SECONDS);
+ healthLogger.trace("Health Check Node Found... " + v.property(HEALTH_CHECK));
+ graph.tx().commit();
+ } catch (Exception e) {
+ String message = e.getMessage();
+ if (message == null) {
+ message = e.getClass().getName();
+ }
+ logger.error("Titan Health Check Failed. " + message);
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private TitanOperationStatus createCategories() {
+ logger.info("** createCategories started");
+ if (graph != null) {
+ try {
+ List<CategoryData> categoryDataList = getDefaultResourceCategoryList();
+ generateGraphCategories(categoryDataList, NodeTypeEnum.ResourceCategory.getName());
+ } catch (Exception e) {
+ logger.error("createCategories : failed to load categories ", e);
+ rollback();
+ return TitanOperationStatus.GENERAL_ERROR;
+ } finally {
+ logger.info("** createCategories ended");
+ commit();
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private void generateGraphCategories(List<CategoryData> categoryDataList, String label) {
+ for (CategoryData categoryData : categoryDataList) {
+ Map<String, Object> properties = categoryData.toGraphMap();
+ properties.put(GraphPropertiesDictionary.LABEL.getProperty(), label);
+
+ // This is temporary for old mechnism of categories - they don;t
+ // have the norm name
+ properties.remove(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty());
+
+ if (isVertexExist(properties)) {
+ return;
+ }
+
+ Vertex vertex = graph.addVertex();
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ vertex.property(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ private List<CategoryData> getDefaultResourceCategoryList() {
+ Map<String, List<String>> categories = new HashMap<String, List<String>>();
+ categories.put("Network Layer 2-3", Arrays.asList("Router", "Gateway", "WAN Connectors", "LAN Connectors"));
+ categories.put("Network Layer 4+", Arrays.asList("Common Network Resources"));
+ categories.put("Application Layer 4+", Arrays.asList("Border Elements", "Application Servers", "Web Server",
+ "Call Control", "Media Servers", "Load Balancer", "Database"));
+ categories.put("Generic", Arrays.asList("Infrastructure", "Abstract", "Network Elements", "Database"));
+
+ List<CategoryData> categoryDataList = new ArrayList<CategoryData>();
+ CategoryData categoryData = null;
+ for (Map.Entry<String, List<String>> entryList : categories.entrySet()) {
+ for (String val : entryList.getValue()) {
+ categoryData = new ResourceCategoryData(entryList.getKey(), val);
+ categoryData.setAction(ActionEnum.Create);
+ categoryData.setElementType(GraphElementTypeEnum.Node);
+ // categoryData.setCategoryName();
+ // categoryData.setName();
+ categoryDataList.add(categoryData);
+ }
+ }
+ return categoryDataList;
+ }
+
+ private List<CategoryData> getDefaultServiceCategoryList() {
+ List<String> categories = new ArrayList<>();
+ categories.add("Mobility");
+ categories.add("Network L1-3");
+ categories.add("Network L4");
+ categories.add("VoIP Call Control");
+
+ List<CategoryData> categoryDataList = new ArrayList<CategoryData>();
+ CategoryData categoryData = null;
+ for (String category : categories) {
+ categoryData = new ServiceCategoryData(category);
+ categoryData.setAction(ActionEnum.Create);
+ categoryData.setElementType(GraphElementTypeEnum.Node);
+ // categoryData.setCategoryName(entryList.getKey());
+ // categoryData.setName(val);
+ categoryDataList.add(categoryData);
+ }
+ return categoryDataList;
+ }
+
+ private void createDefaultUsers() {
+ if (graph != null) {
+ List<UserData> users = createUserList();
+ for (UserData user : users) {
+ String propertyName = GraphPropertiesDictionary.USER_ID.getProperty();
+ String propertyValue = user.getUserId();
+ Vertex vertex = null;
+ Map<String, Object> properties = null;
+ if (!isVertexExist(propertyName, propertyValue)) {
+ vertex = graph.addVertex();
+ vertex.property(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.User.getName());
+ properties = user.toGraphMap();
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ vertex.property(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ graph.tx().commit();
+ }
+ }
+
+ private List<UserData> createUserList() {
+ LinkedList<UserData> users = new LinkedList<UserData>();
+ users.add(getDefaultUserAdmin1());
+ users.add(getDefaultUserAdmin2());
+ users.add(getDefaultUserAdmin3());
+ users.add(getDefaultUserDesigner1());
+ users.add(getDefaultUserDesigner2());
+ users.add(getDefaultUserDesigner3());
+ users.add(getDefaultUserTester1());
+ users.add(getDefaultUserTester2());
+ users.add(getDefaultUserTester3());
+ users.add(getDefaultUserGovernor1());
+ users.add(getDefaultUserOps1());
+ users.add(getDefaultUserProductManager1());
+ users.add(getDefaultUserProductManager2());
+ users.add(getDefaultUserProductStrategist1());
+ users.add(getDefaultUserProductStrategist2());
+ users.add(getDefaultUserProductStrategist3());
+ return users;
+ }
+
+ private boolean isVertexExist(String propertyName, String propertyValue) {
+ // Iterable<Vertex> vertecies = graph.getVertices(propertyName,
+ // propertyValue);
+ // java.util.Iterator<Vertex> iterator = vertecies.iterator();
+
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertecies = graph.query().has(HEALTH_CHECK, OK).vertices();
+
+ java.util.Iterator<TitanVertex> iterator = vertecies.iterator();
+ if (iterator.hasNext()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isVertexExist(Map<String, Object> properties) {
+ TitanGraphQuery query = graph.query();
+
+ if (properties != null && !properties.isEmpty()) {
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ query = query.has(entry.getKey(), entry.getValue());
+ }
+ }
+ Iterable<Vertex> vertecies = query.vertices();
+ java.util.Iterator<Vertex> iterator = vertecies.iterator();
+ if (iterator.hasNext()) {
+ return true;
+ }
+ return false;
+ }
+
+ private UserData getDefaultUserAdmin1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("jh0003");
+ userData.setEmail("admin@sdc.com");
+ userData.setFirstName("Jimmy");
+ userData.setLastName("Hendrix");
+ userData.setRole("ADMIN");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserAdmin2() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("tr0001");
+ userData.setEmail("admin@sdc.com");
+ userData.setFirstName("Todd");
+ userData.setLastName("Rundgren");
+ userData.setRole("ADMIN");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserAdmin3() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("ah0002");
+ userData.setEmail("admin@sdc.com");
+ userData.setFirstName("Alex");
+ userData.setLastName("Harvey");
+ userData.setRole("ADMIN");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserDesigner1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("cs0008");
+ userData.setEmail("designer@sdc.com");
+ userData.setFirstName("Carlos");
+ userData.setLastName("Santana");
+ userData.setRole("DESIGNER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserDesigner2() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("me0009");
+ userData.setEmail("designer@sdc.com");
+ userData.setFirstName("Melissa");
+ userData.setLastName("Etheridge");
+ userData.setRole("DESIGNER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserDesigner3() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("af0006");
+ userData.setEmail("designer@sdc.com");
+ userData.setFirstName("Aretha");
+ userData.setLastName("Franklin");
+ userData.setRole("DESIGNER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserTester1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("jm0007");
+ userData.setEmail("tester@sdc.com");
+ userData.setFirstName("Joni");
+ userData.setLastName("Mitchell");
+ userData.setRole("TESTER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserTester2() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("kb0004");
+ userData.setEmail("tester@sdc.com");
+ userData.setFirstName("Kate");
+ userData.setLastName("Bush");
+ userData.setRole("TESTER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserTester3() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("jt0005");
+ userData.setEmail("tester@sdc.com");
+ userData.setFirstName("James");
+ userData.setLastName("Taylor");
+ userData.setRole("TESTER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserOps1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("op0001");
+ userData.setEmail("ops@sdc.com");
+ userData.setFirstName("Steve");
+ userData.setLastName("Regev");
+ userData.setRole("OPS");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserGovernor1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("gv0001");
+ userData.setEmail("governor@sdc.com");
+ userData.setFirstName("David");
+ userData.setLastName("Shadmi");
+ userData.setRole("GOVERNOR");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserProductManager1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("pm0001");
+ userData.setEmail("pm1@sdc.com");
+ userData.setFirstName("Teddy");
+ userData.setLastName("Isashar");
+ userData.setRole("PRODUCT_MANAGER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserProductManager2() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("pm0002");
+ userData.setEmail("pm2@sdc.com");
+ userData.setFirstName("Sarah");
+ userData.setLastName("Bettens");
+ userData.setRole("PRODUCT_MANAGER");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserProductStrategist1() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("ps0001");
+ userData.setEmail("ps1@sdc.com");
+ userData.setFirstName("Eden");
+ userData.setLastName("Rozin");
+ userData.setRole("PRODUCT_STRATEGIST");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserProductStrategist2() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("ps0002");
+ userData.setEmail("ps2@sdc.com");
+ userData.setFirstName("Ella");
+ userData.setLastName("Kvetny");
+ userData.setRole("PRODUCT_STRATEGIST");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ private UserData getDefaultUserProductStrategist3() {
+ UserData userData = new UserData();
+ userData.setAction(ActionEnum.Create);
+ userData.setElementType(GraphElementTypeEnum.Node);
+ userData.setUserId("ps0003");
+ userData.setEmail("ps3@sdc.com");
+ userData.setFirstName("Geva");
+ userData.setLastName("Alon");
+ userData.setRole("PRODUCT_STRATEGIST");
+ userData.setStatus(UserStatusEnum.ACTIVE.name());
+ userData.setLastLoginTime(0L);
+ return userData;
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+ TitanGraphClient client = new TitanGraphClient();
+ client.createGraph();
+
+ while (true) {
+ boolean health = client.isGraphOpen();
+ System.err.println("health=" + health);
+ Thread.sleep(2000);
+ }
+
+ }
+
+ private static final String TITAN_HEALTH_CHECK_STR = "titanHealthCheck";
+
+ private void logAlarm() {
+ if (lastHealthState == true) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckRecovery,
+ TITAN_HEALTH_CHECK_STR);
+ BeEcompErrorManager.getInstance().logBeHealthCheckTitanRecovery(TITAN_HEALTH_CHECK_STR);
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeHealthCheckError,
+ TITAN_HEALTH_CHECK_STR);
+ BeEcompErrorManager.getInstance().logBeHealthCheckTitanError(TITAN_HEALTH_CHECK_STR);
+ }
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanOperationStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanOperationStatus.java
new file mode 100644
index 0000000000..dc9ed02fca
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/titan/TitanOperationStatus.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.titan;
+
+public enum TitanOperationStatus {
+ OK, NOT_CONNECTED, NOT_CREATED, INDEX_CANNOT_BE_CHANGED, NOT_FOUND, MISSING_UNIQUE_ID, MISSING_NODE_LABEL, MULTIPLE_EDGES_WITH_SAME_LABEL, CANNOT_DELETE_NON_LEAF_NODE, MULTIPLE_NODES_WITH_SAME_ID, GRAPH_IS_NOT_AVAILABLE, TITAN_CONFIGURATION, TITAN_SCHEMA_VIOLATION, INVALID_ELEMENT, INVALID_QUERY, INVALID_ID, RESOURCE_UNAVAILABLE, ILLEGAL_ARGUMENT, ALREADY_LOCKED, ALREADY_EXIST, MULTIPLE_CHILDS_WITH_SAME_EDGE, GENERAL_ERROR, MATCH_NOT_FOUND, INVALID_TYPE, PROPERTY_NAME_ALREADY_EXISTS, INVALID_PROPERTY,
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/CollectionUtils.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/CollectionUtils.java
new file mode 100644
index 0000000000..7177e4c9fa
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/CollectionUtils.java
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public final class CollectionUtils {
+ private CollectionUtils() {
+ }
+
+ /**
+ * Add the content of the 'source' Set to the 'target' set and return the
+ * union set.
+ *
+ * If 'source' is null then a new set is created and returned. If 'target'
+ * is null then no content is added to the 'source' Set or newly created
+ * set.
+ *
+ * @param source
+ * The Set to merge in the target Set.
+ * @param target
+ * The Set in which the source set will be merged (through
+ * addAll).
+ * @return The target Set with addition of source Set elements, or a new Set
+ * (including content of source set) if target was null.
+ */
+ public static <T> Set<T> merge(Set<T> source, Set<T> target) {
+ Set<T> merged = new HashSet<T>();
+ if (target != null) {
+ merged.addAll(target);
+ }
+ if (source != null) {
+ merged.addAll(source);
+ }
+ return merged.isEmpty() ? null : merged;
+ }
+
+ /**
+ * <p>
+ * Add the content of the 'source' Map to the 'target' set and return the
+ * union Map.
+ * </p>
+ * <p>
+ * If 'source' is null then a new Map is created and returned. If 'target'
+ * is null then no content is added to the 'source' Map or newly created
+ * Map.
+ * </p>
+ *
+ * @param source
+ * The Map to merge in the target Map.
+ * @param target
+ * The Map in which the source Map will be merged (through
+ * addAll).
+ * @param override
+ * If an key from the source map already exists in the target
+ * map, should it override (true) or not (false) the value.
+ * @return The target Map with addition of source Map elements, or a new Map
+ * (including content of source set) if target was null.
+ */
+ public static <T, V> Map<T, V> merge(Map<T, ? extends V> source, Map<T, V> target, boolean override) {
+ if (target == null) {
+ target = new HashMap();
+ }
+
+ if (source != null) {
+ for (Entry<T, ? extends V> entry : source.entrySet()) {
+ if (override || !target.containsKey(entry.getKey())) {
+ target.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ return target.isEmpty() ? null : target;
+ }
+
+ /**
+ * Merge two lists, the merge is performed based on the contains method so
+ * elements presents both in source and target are not added twice to the
+ * list.
+ *
+ * @param source
+ * The source list.
+ * @param target
+ * The target list.
+ * @return A list that represents the merged collections.
+ */
+ public static <T> List<T> merge(List<T> source, List<T> target) {
+ List<T> merged = target == null ? new ArrayList<T>() : target;
+
+ if (source == null) {
+ return merged;
+ }
+
+ for (T t : source) {
+ if (!merged.contains(t)) {
+ merged.add(t);
+ }
+ }
+
+ return merged;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Constants.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Constants.java
new file mode 100644
index 0000000000..4487e51a55
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Constants.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+/**
+ * Holds constants to use in ALIEN.
+ */
+public final class Constants {
+ public static final int DEFAULT_ES_SEARCH_SIZE = 50;
+ public static final int MAX_ES_SEARCH_SIZE = 100;
+
+ public static final String ALIEN_INTERNAL_TAG = "icon";
+ public static final String DEFAULT_CAPABILITY_FIELD_NAME = "defaultCapabilities";
+
+ public static final String GROUP_NAME_ALL_USERS = "ALL_USERS";
+ public static final String GRAPH_EMPTY_VALUE = "__NANANA__";
+
+ private Constants() {
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/DaoUtils.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/DaoUtils.java
new file mode 100644
index 0000000000..b21d1e9630
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/DaoUtils.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import com.google.gson.Gson;
+
+/**
+ *
+ * @author Evgenia Alberstein
+ *
+ * Utility class for convertation to/from JSON string
+ */
+public class DaoUtils {
+
+ /**
+ * Convert from Object to Json string
+ *
+ * @param object
+ * @return json string
+ */
+ public static String convertToJson(Object object) {
+ if (object == null) {
+ throw new RuntimeException("The object cannot be NULL!!!");
+ }
+ Gson gson = new Gson(); // Or use new GsonBuilder().create();
+ return gson.toJson(object); // serializes target to Json
+ }
+
+ /**
+ * Convert from Json string to object
+ *
+ * @param clazz
+ * @param json
+ * @return object
+ */
+ public static <T> T convertFromJson(Class<T> clazz, String json) {
+ Gson gson = new Gson(); // Or use new GsonBuilder().create();
+ return gson.fromJson(json, clazz); // deserializes json into target2
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ElasticSearchUtil.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ElasticSearchUtil.java
new file mode 100644
index 0000000000..f20b523a4d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ElasticSearchUtil.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import org.elasticsearch.action.search.SearchResponse;
+
+/**
+ * Utility class to work with elastic search responses.
+ *
+ */
+public final class ElasticSearchUtil {
+ private ElasticSearchUtil() {
+ }
+
+ /**
+ * Checks if a search response from elastic search contains results or not.
+ *
+ * @param searchResponse
+ * The ES search response object.
+ * @return True if the response does not contain any result, false if the
+ * response does contains results.
+ */
+ public static boolean isResponseEmpty(SearchResponse searchResponse) {
+ if (searchResponse == null || searchResponse.getHits() == null || searchResponse.getHits().getHits() == null
+ || searchResponse.getHits().getHits().length == 0) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Exceptions.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Exceptions.java
new file mode 100644
index 0000000000..fd0a6754ab
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/Exceptions.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+public final class Exceptions {
+ private Exceptions() {
+ }
+
+ public static RuntimeException convertToRuntimeEx(Throwable t) {
+ return Exceptions.<RuntimeException>convertToRTException(t);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Throwable> T convertToRTException(Throwable t) throws T {
+ throw (T) t;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageQuality.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageQuality.java
new file mode 100644
index 0000000000..4eb691a4b7
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageQuality.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+/**
+ * Available qualities for images in Alien 4 Cloud.
+ *
+ * @author luc boutier
+ */
+public enum ImageQuality {
+ QUALITY_16(16), QUALITY_32(32), QUALITY_64(64), QUALITY_128(128), QUALITY_512(512), QUALITY_BEST(-1);
+
+ private final int size;
+
+ private ImageQuality(int size) {
+ this.size = size;
+ }
+
+ public int getSize() {
+ return size;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageResizeUtil.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageResizeUtil.java
new file mode 100644
index 0000000000..4db8c72e5a
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/ImageResizeUtil.java
@@ -0,0 +1,142 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+
+/**
+ * Utility to resize images.
+ *
+ * @author luc boutier
+ */
+public final class ImageResizeUtil {
+ private ImageResizeUtil() {
+ }
+
+ /**
+ * Resize an image with default quality settings.
+ *
+ * @param originalImage
+ * The image to resize.
+ * @param width
+ * The target width.
+ * @param height
+ * The target height.
+ * @param preserveDimensions
+ * Flag to know if we should preserve original image dimensions.
+ * @return The resized image.
+ */
+ public static BufferedImage resizeImage(final BufferedImage originalImage, final int width, final int height,
+ final boolean preserveDimensions) {
+ return resizeImage(originalImage, width, height, preserveDimensions, false);
+ }
+
+ /**
+ * <p>
+ * Resize an image with high quality settings.
+ * </p>
+ * <ul>
+ * <li>g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ * RenderingHints.VALUE_INTERPOLATION_BILINEAR);</li>
+ * <li>g.setRenderingHint(RenderingHints.KEY_RENDERING,
+ * RenderingHints.VALUE_RENDER_QUALITY);</li>
+ * <li>g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ * RenderingHints.VALUE_ANTIALIAS_ON);</li>
+ * </ul>
+ *
+ * @param originalImage
+ * The image to resize.
+ * @param width
+ * The target width.
+ * @param height
+ * The target height.
+ * @param preserveDimensions
+ * Flag to know if we should preserve original image dimensions.
+ * @return The resized image.
+ */
+ public static BufferedImage resizeImageWithHint(BufferedImage originalImage, final int width, final int height,
+ final boolean preserveDimensions) {
+ return resizeImage(originalImage, width, height, preserveDimensions, true);
+ }
+
+ private static BufferedImage resizeImage(BufferedImage originalImage, final int width, final int height,
+ final boolean preserveDimensions, final boolean enableHighQuality) {
+ int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
+
+ int targetWidth = width;
+ int targetHeight = height;
+
+ if (preserveDimensions) {
+ int[] targetDimentions = computeDimensions(width, height, originalImage.getWidth(),
+ originalImage.getHeight());
+ targetWidth = targetDimentions[0];
+ targetHeight = targetDimentions[1];
+ }
+
+ BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, type);
+
+ Graphics2D g = resizedImage.createGraphics();
+ if (enableHighQuality) {
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+
+ g.drawImage(originalImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH), 0, 0, null);
+ g.dispose();
+
+ return resizedImage;
+ }
+
+ /**
+ * Compute target width and height based on requested width and height but
+ * making sure the original dimensions of the image will be preserved.
+ *
+ * @param width
+ * The ideal (and max) target width.
+ * @param height
+ * The ideal (and max) target height.
+ * @param originalWidth
+ * The original width.
+ * @param originalHeight
+ * The original height.
+ * @return An array of int that contains the ideal width and height to
+ * preserve dimensions.
+ */
+ public static int[] computeDimensions(final int width, final int height, final int originalWidth,
+ final int originalHeight) {
+ int targetWidth = width;
+ int targetHeight = height;
+
+ float targetDimensions = Float.valueOf(width).floatValue() / Float.valueOf(height).floatValue();
+ float sourceDimensions = Float.valueOf(originalWidth).floatValue() / Float.valueOf(originalHeight).floatValue();
+ if (targetDimensions > sourceDimensions) {
+ targetWidth = Float.valueOf(width * sourceDimensions / targetDimensions).intValue();
+ } else {
+ targetHeight = Float.valueOf(height * targetDimensions / sourceDimensions).intValue();
+ }
+
+ return new int[] { targetWidth, targetHeight };
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/JsonUtil.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/JsonUtil.java
new file mode 100644
index 0000000000..00b6d5e894
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/JsonUtil.java
@@ -0,0 +1,209 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+/**
+ * Simple utility for JSon processing.
+ */
+public final class JsonUtil {
+ private static ObjectMapper getOneObjectMapper() {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
+ return mapper;
+ }
+
+ private JsonUtil() {
+ }
+
+ // /**
+ // * Parse a {@link RestResponse} by using the specified dataType as the
+ // expected data object's class.
+ // *
+ // * @param responseAsString
+ // * The {@link RestResponse} as a JSon String
+ // * @param dataType
+ // * The type of the data object.
+ // * @return The parsed {@link RestResponse} object matching the given JSon.
+ // * @throws JsonParseException
+ // * In case of a JSon parsing issue.
+ // * @throws JsonMappingException
+ // * In case of a JSon parsing issue.
+ // * @throws IOException
+ // * In case of an IO error.
+ // */
+ // public static <T> RestResponse<T> read(String responseAsString, Class<T>
+ // dataType) throws IOException {
+ // ObjectMapper mapper = getOneObjectMapper();
+ // JavaType restResponseType =
+ // mapper.getTypeFactory().constructParametricType(RestResponse.class,
+ // dataType);
+ // return mapper.readValue(responseAsString, restResponseType);
+ // }
+
+ /**
+ * Deserialize json text to object
+ *
+ * @param objectText
+ * @param objectClass
+ * @return
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ */
+ public static <T> T readObject(String objectText, Class<T> objectClass) throws IOException {
+ return getOneObjectMapper().readValue(objectText, objectClass);
+ }
+
+ /**
+ * Deserialize json stream to object
+ *
+ * @param jsonStream
+ * @param objectClass
+ * @return
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ */
+ public static <T> T readObject(InputStream jsonStream, Class<T> objectClass) throws IOException {
+ return getOneObjectMapper().readValue(jsonStream, objectClass);
+ }
+
+ /**
+ * Deserialize json text to object
+ *
+ * @param objectText
+ * @return
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ */
+ public static <T> T readObject(String objectText) throws IOException {
+ TypeReference<T> typeRef = new TypeReference<T>() {
+ };
+ return getOneObjectMapper().readValue(objectText, typeRef);
+ }
+
+ // /**
+ // * Parse a {@link RestResponse} without being interested in parameterized
+ // type
+ // *
+ // * @param responseAsString
+ // * @return
+ // * @throws JsonParseException
+ // * @throws JsonMappingException
+ // * @throws IOException
+ // */
+ // public static RestResponse<?> read(String responseAsString) throws
+ // IOException {
+ // return getOneObjectMapper().readValue(responseAsString,
+ // RestResponse.class);
+ // }
+
+ // /**
+ // * Serialize the given object in a JSon String.
+ // *
+ // * @param obj
+ // * The object to serialize.
+ // * @return The JSon serialization of the given object.
+ // * @throws JsonProcessingException
+ // * In case of a failure in serialization.
+ // */
+ // public static String toString(Object obj) throws JsonProcessingException
+ // {
+ // return getOneObjectMapper().writeValueAsString(obj);
+ // }
+
+ /**
+ * Deserialize the given json string to a map
+ *
+ * @param json
+ * json text
+ * @return map object
+ * @throws IOException
+ */
+ public static Map<String, Object> toMap(String json) throws IOException {
+ ObjectMapper mapper = getOneObjectMapper();
+ JavaType mapStringObjectType = mapper.getTypeFactory().constructParametricType(HashMap.class, String.class,
+ Object.class);
+ return mapper.readValue(json, mapStringObjectType);
+ }
+
+ /**
+ * Deserialize the given json string to a map
+ *
+ * @param json
+ * @param keyTypeClass
+ * @param valueTypeClass
+ * @return
+ * @throws IOException
+ */
+ public static <K, V> Map<K, V> toMap(String json, Class<K> keyTypeClass, Class<V> valueTypeClass)
+ throws IOException {
+ ObjectMapper mapper = getOneObjectMapper();
+ JavaType mapStringObjectType = mapper.getTypeFactory().constructParametricType(HashMap.class, keyTypeClass,
+ valueTypeClass);
+ return mapper.readValue(json, mapStringObjectType);
+ }
+
+ public static <V> V[] toArray(String json, Class<V> valueTypeClass) throws IOException {
+ ObjectMapper mapper = getOneObjectMapper();
+ JavaType arrayStringObjectType = mapper.getTypeFactory().constructArrayType(valueTypeClass);
+ return mapper.readValue(json, arrayStringObjectType);
+ }
+
+ /**
+ * Deserialize the given json string to a list
+ *
+ * @param json
+ * json text
+ * @return list object
+ * @throws IOException
+ */
+ public static <T> List<T> toList(String json, Class<T> clazz) throws IOException {
+ ObjectMapper mapper = getOneObjectMapper();
+ JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, clazz);
+ return mapper.readValue(json, type);
+ }
+
+ public static <T> List<T> toList(String json, Class<T> elementClass, Class<?> elementGenericClass)
+ throws IOException {
+ ObjectMapper mapper = getOneObjectMapper();
+ JavaType elementType = mapper.getTypeFactory().constructParametricType(elementClass, elementGenericClass);
+ JavaType listType = mapper.getTypeFactory().constructCollectionType(List.class, elementType);
+ return mapper.readValue(json, listType);
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapEntry.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapEntry.java
new file mode 100644
index 0000000000..13561f60a0
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapEntry.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+/**
+ * A map/set entry as a key/value object to be serialized as is.
+ *
+ * @author luc boutier
+ */
+public class MapEntry<T, V> {
+ private T key;
+ private V value;
+
+ public MapEntry() {
+ }
+
+ public MapEntry(T key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public T getKey() {
+ return key;
+ }
+
+ public void setKey(T key) {
+ this.key = key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public void setValue(V value) {
+ this.value = value;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapUtil.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapUtil.java
new file mode 100644
index 0000000000..84802ce293
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapUtil.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class to ease map manipulation.
+ */
+public final class MapUtil {
+ private MapUtil() {
+ }
+
+ /**
+ * Try to get a value following a path in the map. For example :
+ * MapUtil.get(map, "a.b.c") correspond to: map.get(a).get(b).get(c)
+ *
+ * @param map
+ * the map to search for path
+ * @param path
+ * keys in the map separated by '.'
+ */
+ public static Object get(Map<String, ? extends Object> map, String path) {
+ String[] tokens = path.split("\\.");
+ if (tokens.length == 0) {
+ return null;
+ } else {
+ Object value = map;
+ for (String token : tokens) {
+ if (!(value instanceof Map)) {
+ return null;
+ } else {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> nested = (Map<String, Object>) value;
+ if (nested.containsKey(token)) {
+ value = nested.get(token);
+ } else {
+ return null;
+ }
+ }
+ }
+ return value;
+ }
+ }
+
+ /**
+ * Create a new hash map and fills it from the given keys and values
+ * (keys[index] -> values[index].
+ *
+ * @param keys
+ * The array of keys.
+ * @param values
+ * The array of values.
+ * @return A map that contains for each key element in the keys array a
+ * value from the values array at the same index.
+ */
+ public static <K, V> Map<K, V> newHashMap(K[] keys, V[] values) {
+ Map<K, V> map = new HashMap<K, V>();
+ if (keys == null || values == null || keys.length != values.length) {
+ throw new IllegalArgumentException("keys and values must be non-null and have the same size.");
+ }
+ for (int i = 0; i < keys.length; i++) {
+ map.put(keys[i], values[i]);
+ }
+ return map;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/TypeMap.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/TypeMap.java
new file mode 100644
index 0000000000..1f7e692598
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/TypeMap.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TypeMap {
+ private Map<Class<? extends Object>, Map<String, Object>> cacheMap = new HashMap<Class<? extends Object>, Map<String, Object>>();
+
+ private Map<String, Object> getMap(Class<? extends Object> clazz) {
+ Map<String, Object> map = cacheMap.get(clazz);
+ if (map == null) {
+ cacheMap.put(clazz, new HashMap<String, Object>());
+ }
+ return cacheMap.get(clazz);
+ }
+
+ /**
+ * put an object (value) in it's type map using the given key.
+ *
+ * @param key
+ * The key inside the type map.
+ * @param value
+ * The object to insert (based on it's type and the given key).
+ */
+ public void put(String key, Object value) {
+ getMap(value.getClass()).put(key, value);
+ }
+
+ /**
+ * Get the cached object based on it's type and key.
+ *
+ * @param clazz
+ * The object's type.
+ * @param key
+ * The object key.
+ * @return The object that match the given type and key or null if none
+ * matches.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(Class<T> clazz, String key) {
+ return (T) (cacheMap.get(clazz) == null ? null : cacheMap.get(clazz).get(key));
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/UserStatusEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/UserStatusEnum.java
new file mode 100644
index 0000000000..dbe9b84a5e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/UserStatusEnum.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.dao.utils;
+
+import org.openecomp.sdc.common.util.MethodActivationStatusEnum;
+
+import fj.data.Either;
+
+public enum UserStatusEnum {
+ ACTIVE, INACTIVE;
+
+ public static Either<UserStatusEnum, MethodActivationStatusEnum> findByName(String name) {
+ Either<UserStatusEnum, MethodActivationStatusEnum> result = Either.right(MethodActivationStatusEnum.NOT_FOUND);
+ for (UserStatusEnum status : UserStatusEnum.values()) {
+ if (status.name().equals(name)) {
+ result = Either.left(status);
+ break;
+ }
+ }
+ return result;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/ArtifactDataEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/ArtifactDataEnum.java
new file mode 100644
index 0000000000..39c023dc00
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/ArtifactDataEnum.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.api;
+
+public enum ArtifactDataEnum {
+ SERVICE_ARTIFACT, COMPONENT_ARTIFACT
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/IResourceUploader.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/IResourceUploader.java
new file mode 100644
index 0000000000..9eea2962e1
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/api/IResourceUploader.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.data.ServiceArtifactsDataCollection;
+import org.openecomp.sdc.be.resources.exception.ResourceDAOException;
+
+import fj.data.Either;
+
+/**
+ * DAO to manage image upload and retrieval.
+ *
+ * @author luc boutier
+ */
+public interface IResourceUploader {
+
+ /**
+ * Save an artifact in the DAO layer.
+ *
+ * @param imageData
+ */
+ ResourceUploadStatus saveArtifact(ESArtifactData artifactData, boolean isReload);
+
+ /**
+ * Save an artifact in the DAO layer.
+ *
+ * @param imageData
+ */
+ ResourceUploadStatus updateArtifact(ESArtifactData artifactData);
+
+ /**
+ * Get an artifact as a byte array based on the artifact id.
+ *
+ * @param id
+ * The id of the artifact to read.
+ * @return The artifact as a byte array.
+ */
+ Either<ESArtifactData, ResourceUploadStatus> getArtifact(String id);
+
+ /**
+ * Delete the given image.
+ *
+ * @param id
+ * Id of the image to delete.
+ */
+ void deleteArtifact(String id);
+
+ /**
+ * delete all artifacts
+ */
+ public void deleteAllArtifacts();
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AdditionalInfoParameterData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AdditionalInfoParameterData.java
new file mode 100644
index 0000000000..a5a6e6df76
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AdditionalInfoParameterData.java
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.AdditionalInfoParameterDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class AdditionalInfoParameterData extends GraphNode {
+
+ AdditionalInfoParameterDataDefinition additionalInfoParameterDataDefinition;
+
+ private Map<String, String> parameters;
+
+ private Map<String, String> idToKey;
+
+ public AdditionalInfoParameterData() {
+ super(NodeTypeEnum.AdditionalInfoParameters);
+ additionalInfoParameterDataDefinition = new AdditionalInfoParameterDataDefinition();
+ }
+
+ public AdditionalInfoParameterData(AdditionalInfoParameterDataDefinition additionalInfoParameterDataDefinition,
+ Map<String, String> parameters, Map<String, String> idToKey) {
+ super(NodeTypeEnum.AdditionalInfoParameters);
+ this.additionalInfoParameterDataDefinition = additionalInfoParameterDataDefinition;
+ this.parameters = parameters;
+ this.idToKey = idToKey;
+ }
+
+ public AdditionalInfoParameterData(Map<String, Object> properties) {
+
+ this();
+
+ additionalInfoParameterDataDefinition
+ .setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ additionalInfoParameterDataDefinition
+ .setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ additionalInfoParameterDataDefinition
+ .setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ additionalInfoParameterDataDefinition.setLastCreatedCounter(
+ (Integer) properties.get(GraphPropertiesDictionary.PROPERTY_COUNTER.getProperty()));
+ Type mapType = new TypeToken<HashMap<String, String>>() {
+ }.getType();
+ HashMap<String, String> prametersfromJson = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.ADDITIONAL_INFO_PARAMS.getProperty()), mapType);
+
+ this.setParameters(prametersfromJson);
+
+ // this.setParameters((HashMap<String, String>) properties
+ // .get(GraphPropertiesDictionary.ADDITIONAL_INFO_PARAMS
+ // .getProperty()));
+
+ HashMap<String, String> idToKeyfromJson = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY.getProperty()), mapType);
+
+ this.setIdToKey(idToKeyfromJson);
+ // this.setIdToKey((HashMap<String, String>) properties
+ // .get(GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY
+ // .getProperty()));
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, additionalInfoParameterDataDefinition.getUniqueId());
+
+ // String parametersToJson = getGson().toJson(getParameters());
+
+ // addIfExists(map, GraphPropertiesDictionary.ADDITIONAL_INFO_PARAMS,
+ // parametersToJson);
+ addIfExists(map, GraphPropertiesDictionary.ADDITIONAL_INFO_PARAMS, getParameters());
+
+ // String idToKeyToJson = getGson().toJson(getIdToKey());
+ // addIfExists(map, GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY,
+ // idToKeyToJson);
+ addIfExists(map, GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY, getIdToKey());
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE,
+ additionalInfoParameterDataDefinition.getCreationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE,
+ additionalInfoParameterDataDefinition.getModificationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.PROPERTY_COUNTER,
+ additionalInfoParameterDataDefinition.getLastCreatedCounter());
+
+ return map;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return additionalInfoParameterDataDefinition.getUniqueId();
+ }
+
+ public AdditionalInfoParameterDataDefinition getAdditionalInfoParameterDataDefinition() {
+ return additionalInfoParameterDataDefinition;
+ }
+
+ public void setAdditionalInfoParameterDataDefinition(
+ AdditionalInfoParameterDataDefinition additionalInfoParameterDataDefinition) {
+ this.additionalInfoParameterDataDefinition = additionalInfoParameterDataDefinition;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Map<String, String> parameters) {
+ this.parameters = parameters;
+ }
+
+ public Map<String, String> getIdToKey() {
+ return idToKey;
+ }
+
+ public void setIdToKey(Map<String, String> idToKey) {
+ this.idToKey = idToKey;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyData [parameters= " + parameters + " idToKey= " + idToKey
+ + ", additionalInfoParameterDataDefinition=" + additionalInfoParameterDataDefinition + "]";
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ArtifactData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ArtifactData.java
new file mode 100644
index 0000000000..e4da028c60
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ArtifactData.java
@@ -0,0 +1,183 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class ArtifactData extends GraphNode {
+
+ private ArtifactDataDefinition artifactDataDefinition;
+
+ public ArtifactData() {
+ super(NodeTypeEnum.ArtifactRef);
+ artifactDataDefinition = new ArtifactDataDefinition();
+ }
+
+ public ArtifactData(ArtifactDataDefinition artifactDataDefinition) {
+ super(NodeTypeEnum.ArtifactRef);
+ this.artifactDataDefinition = artifactDataDefinition;
+
+ }
+
+ public ArtifactData(Map<String, Object> properties) {
+ this();
+ artifactDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ artifactDataDefinition
+ .setArtifactType((String) properties.get(GraphPropertiesDictionary.ARTIFACT_TYPE.getProperty()));
+ artifactDataDefinition
+ .setArtifactRef((String) properties.get(GraphPropertiesDictionary.ARTIFACT_REF.getProperty()));
+ artifactDataDefinition.setArtifactName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ artifactDataDefinition.setArtifactRepository(
+ (String) properties.get(GraphPropertiesDictionary.ARTIFACT_REPOSITORY.getProperty()));
+ artifactDataDefinition.setArtifactChecksum(
+ (String) properties.get(GraphPropertiesDictionary.ARTIFACT_CHECKSUM.getProperty()));
+ artifactDataDefinition
+ .setArtifactCreator((String) properties.get(GraphPropertiesDictionary.CREATOR.getProperty()));
+ artifactDataDefinition
+ .setUserIdCreator((String) properties.get(GraphPropertiesDictionary.ATT_CREATOR.getProperty()));
+ artifactDataDefinition
+ .setUserIdLastUpdater((String) properties.get(GraphPropertiesDictionary.LAST_UPDATER.getProperty()));
+ artifactDataDefinition
+ .setCreatorFullName((String) properties.get(GraphPropertiesDictionary.CREATOR_FULL_NAME.getProperty()));
+ artifactDataDefinition
+ .setUpdaterFullName((String) properties.get(GraphPropertiesDictionary.UPDATER_FULL_NAME.getProperty()));
+ artifactDataDefinition
+ .setCreationDate((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+ artifactDataDefinition
+ .setLastUpdateDate((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+ artifactDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ artifactDataDefinition.setEsId((String) properties.get(GraphPropertiesDictionary.ES_ID.getProperty()));
+ artifactDataDefinition
+ .setArtifactLabel((String) properties.get(GraphPropertiesDictionary.ARTIFACT_LABEL.getProperty()));
+ artifactDataDefinition
+ .setMandatory((Boolean) properties.get(GraphPropertiesDictionary.IS_ABSTRACT.getProperty()));
+ artifactDataDefinition.setArtifactChecksum(
+ (String) properties.get(GraphPropertiesDictionary.ARTIFACT_CHECKSUM.getProperty()));
+ artifactDataDefinition.setArtifactDisplayName(
+ (String) properties.get(GraphPropertiesDictionary.ARTIFACT_DISPLAY_NAME.getProperty()));
+ artifactDataDefinition.setApiUrl((String) properties.get(GraphPropertiesDictionary.API_URL.getProperty()));
+ artifactDataDefinition
+ .setServiceApi((Boolean) properties.get(GraphPropertiesDictionary.SERVICE_API.getProperty()));
+ artifactDataDefinition
+ .setArtifactVersion((String) properties.get(GraphPropertiesDictionary.ARTIFACT_VERSION.getProperty()));
+ artifactDataDefinition
+ .setArtifactUUID((String) properties.get(GraphPropertiesDictionary.ARTIFACT_UUID.getProperty()));
+ artifactDataDefinition.setPayloadUpdateDate(
+ (Long) properties.get(GraphPropertiesDictionary.PAYLOAD_UPDATE_DATE.getProperty()));
+ artifactDataDefinition.setHeatParamsUpdateDate(
+ (Long) properties.get(GraphPropertiesDictionary.HEAT_PARAMS_UPDATE_DATE.getProperty()));
+ artifactDataDefinition.setGenerated((Boolean) properties.get(GraphPropertiesDictionary.GENERATED.getProperty()));
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> requiredArtifactsfromJson = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.REQUIRED_ARTIFACTS.getProperty()), listType);
+ artifactDataDefinition.setRequiredArtifacts(requiredArtifactsfromJson);
+
+ String groupType = (String) properties.get(GraphPropertiesDictionary.ARTIFACT_GROUP_TYPE.getProperty());
+ if (groupType != null && !groupType.isEmpty()) {
+
+ artifactDataDefinition.setArtifactGroupType(ArtifactGroupTypeEnum.findType(groupType));
+ }
+
+ Integer timeout = (Integer) properties.get(GraphPropertiesDictionary.ARTIFACT_TIMEOUT.getProperty());
+ if (timeout != null) {
+
+ artifactDataDefinition.setTimeout(timeout);
+ }
+
+ }
+
+ public ArtifactDataDefinition getArtifactDataDefinition() {
+ return artifactDataDefinition;
+ }
+
+ public void setArtifactDataDefinition(ArtifactDataDefinition artifactDataDefinition) {
+ this.artifactDataDefinition = artifactDataDefinition;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return artifactDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, artifactDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_TYPE, artifactDataDefinition.getArtifactType());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_REF, artifactDataDefinition.getArtifactRef());
+ addIfExists(map, GraphPropertiesDictionary.NAME, artifactDataDefinition.getArtifactName());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_REPOSITORY, artifactDataDefinition.getArtifactRepository());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_CHECKSUM, artifactDataDefinition.getArtifactChecksum());
+ addIfExists(map, GraphPropertiesDictionary.CREATOR, artifactDataDefinition.getArtifactCreator());
+ addIfExists(map, GraphPropertiesDictionary.ATT_CREATOR, artifactDataDefinition.getUserIdCreator());
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATER, artifactDataDefinition.getUserIdLastUpdater());
+ addIfExists(map, GraphPropertiesDictionary.CREATOR_FULL_NAME, artifactDataDefinition.getCreatorFullName());
+ addIfExists(map, GraphPropertiesDictionary.UPDATER_FULL_NAME, artifactDataDefinition.getUpdaterFullName());
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, artifactDataDefinition.getCreationDate());
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, artifactDataDefinition.getLastUpdateDate());
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, artifactDataDefinition.getDescription());
+ addIfExists(map, GraphPropertiesDictionary.ES_ID, artifactDataDefinition.getEsId());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_LABEL, artifactDataDefinition.getArtifactLabel());
+ addIfExists(map, GraphPropertiesDictionary.IS_ABSTRACT, artifactDataDefinition.getMandatory());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_CHECKSUM, artifactDataDefinition.getArtifactChecksum());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_DISPLAY_NAME,
+ artifactDataDefinition.getArtifactDisplayName());
+ addIfExists(map, GraphPropertiesDictionary.API_URL, artifactDataDefinition.getApiUrl());
+ addIfExists(map, GraphPropertiesDictionary.SERVICE_API, artifactDataDefinition.getServiceApi());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_TIMEOUT, artifactDataDefinition.getTimeout());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_VERSION, artifactDataDefinition.getArtifactVersion());
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_UUID, artifactDataDefinition.getArtifactUUID());
+ addIfExists(map, GraphPropertiesDictionary.PAYLOAD_UPDATE_DATE, artifactDataDefinition.getPayloadUpdateDate());
+ addIfExists(map, GraphPropertiesDictionary.HEAT_PARAMS_UPDATE_DATE,
+ artifactDataDefinition.getHeatParamsUpdateDate());
+ addIfExists(map, GraphPropertiesDictionary.REQUIRED_ARTIFACTS, artifactDataDefinition.getRequiredArtifacts());
+ addIfExists(map, GraphPropertiesDictionary.GENERATED, artifactDataDefinition.getGenerated());
+
+ String groupType = null;
+ ArtifactGroupTypeEnum groupTypeEnum = artifactDataDefinition.getArtifactGroupType();
+ if (groupTypeEnum != null) {
+ groupType = groupTypeEnum.getType();
+ }
+ addIfExists(map, GraphPropertiesDictionary.ARTIFACT_GROUP_TYPE, groupType);
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtifactData [artifactDataDefinition=" + artifactDataDefinition + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeData.java
new file mode 100644
index 0000000000..16a8c8be60
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeData.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class AttributeData extends GraphNode {
+ AttributeDataDefinition attributeDataDefinition;
+
+ public AttributeData() {
+ super(NodeTypeEnum.Attribute);
+ attributeDataDefinition = new AttributeDataDefinition();
+ }
+
+ public AttributeData(AttributeDataDefinition attributeDataDefinition) {
+ super(NodeTypeEnum.Attribute);
+ this.attributeDataDefinition = attributeDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "AttributeData [attributeDataDefinition=" + attributeDataDefinition + "]";
+ }
+
+ @Override
+ public String getUniqueId() {
+ return attributeDataDefinition.getUniqueId();
+ }
+
+ public AttributeDataDefinition getAttributeDataDefinition() {
+ return attributeDataDefinition;
+ }
+
+ public void setAttributeDataDefinition(AttributeDataDefinition attributeDataDefinition) {
+ this.attributeDataDefinition = attributeDataDefinition;
+ }
+
+ public AttributeData(Map<String, Object> properties) {
+
+ this();
+
+ attributeDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ attributeDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ attributeDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ String defaultValue = (String) properties.get(GraphPropertiesDictionary.DEFAULT_VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(defaultValue)) {
+ attributeDataDefinition.setDefaultValue(null);
+ } else {
+ attributeDataDefinition.setDefaultValue(defaultValue);
+ }
+
+ attributeDataDefinition.setStatus((String) properties.get(GraphPropertiesDictionary.STATUS.getProperty()));
+
+ attributeDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ attributeDataDefinition.setValue((String) properties.get(GraphPropertiesDictionary.VALUE.getProperty()));
+
+ Type schemaType = new TypeToken<SchemaDefinition>() {
+ }.getType();
+ SchemaDefinition schema = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.ENTRY_SCHEMA.getProperty()), schemaType);
+ attributeDataDefinition.setSchema(schema);
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, attributeDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, attributeDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, attributeDataDefinition.getDescription());
+
+ String defaultValue = attributeDataDefinition.getDefaultValue();
+ if (defaultValue == null) {
+ defaultValue = Constants.GRAPH_EMPTY_VALUE;
+ }
+
+ addIfExists(map, GraphPropertiesDictionary.DEFAULT_VALUE, defaultValue);
+
+ addIfExists(map, GraphPropertiesDictionary.STATUS, attributeDataDefinition.getStatus());
+
+ addIfExists(map, GraphPropertiesDictionary.NAME, attributeDataDefinition.getName());
+
+ addIfExists(map, GraphPropertiesDictionary.VALUE, attributeDataDefinition.getValue());
+
+ SchemaDefinition entrySchema = attributeDataDefinition.getSchema();
+ if (entrySchema != null) {
+ String entrySchemaStr = getGson().toJson(entrySchema);
+ addIfExists(map, GraphPropertiesDictionary.ENTRY_SCHEMA, entrySchemaStr);
+ }
+
+ return map;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeValueData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeValueData.java
new file mode 100644
index 0000000000..7c8ce1dbb6
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/AttributeValueData.java
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class AttributeValueData extends GraphNode {
+ private String uniqueId;
+
+ private String value;
+
+ private String type;
+
+ private Boolean hidden;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ public AttributeValueData() {
+ super(NodeTypeEnum.AttributeValue);
+ }
+
+ public AttributeValueData(Map<String, Object> properties) {
+ this();
+
+ this.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ String updatedValue = (String) properties.get(GraphPropertiesDictionary.VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(updatedValue)) {
+ this.setValue(null);
+ } else {
+ this.setValue(updatedValue);
+ }
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ this.setHidden((Boolean) properties.get(GraphPropertiesDictionary.HIDDEN.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, type);
+
+ addIfExists(map, GraphPropertiesDictionary.HIDDEN, hidden);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ String updatedValue = Objects.isNull(value) ? Constants.GRAPH_EMPTY_VALUE : value;
+ addIfExists(map, GraphPropertiesDictionary.VALUE, updatedValue);
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "AttributeValueData [uniqueId=" + uniqueId + ", hidden=" + hidden + ", type=" + type + ", creationTime="
+ + creationTime + ", value=" + value + ", modificationTime=" + modificationTime + "]";
+ }
+
+ public Boolean isHidden() {
+ return hidden;
+ }
+
+ public void setHidden(Boolean hidden) {
+ this.hidden = hidden;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityData.java
new file mode 100644
index 0000000000..2582475c5c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityData.java
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class CapabilityData extends GraphNode {
+ public final static String MIN_OCCURRENCES = "1"; // occurrences
+ public final static String MAX_OCCURRENCES = "UNBOUNDED";
+
+ public CapabilityData() {
+ super(NodeTypeEnum.Capability);
+
+ }
+
+ public CapabilityData(Map<String, Object> properties) {
+ this();
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> validSourceTypesfromJson = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.VALID_SOURCE_TYPES.getProperty()), listType);
+
+ this.setValidSourceTypes(validSourceTypesfromJson);
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ this.setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ this.setMinOccurrences((String) properties.get(GraphPropertiesDictionary.MIN_OCCURRENCES.getProperty()));
+ this.setMaxOccurrences((String) properties.get(GraphPropertiesDictionary.MAX_OCCURRENCES.getProperty()));
+ }
+
+ private String uniqueId;
+
+ private String description;
+
+ /** Identifies the type of the capability. */
+ private String type;
+
+ private List<String> validSourceTypes;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private String minOccurrences = MIN_OCCURRENCES;
+ private String maxOccurrences = MAX_OCCURRENCES;
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List<String> getValidSourceTypes() {
+ return validSourceTypes;
+ }
+
+ public void setValidSourceTypes(List<String> validSourceTypes) {
+ this.validSourceTypes = validSourceTypes;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public String getMinOccurrences() {
+ return minOccurrences;
+ }
+
+ public void setMinOccurrences(String minOccurrences) {
+ if (minOccurrences != null) {
+ this.minOccurrences = minOccurrences;
+ }
+ }
+
+ public String getMaxOccurrences() {
+ return maxOccurrences;
+ }
+
+ public void setMaxOccurrences(String maxOccurrences) {
+ if (maxOccurrences != null) {
+ this.maxOccurrences = maxOccurrences;
+ }
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ // String validSourceTypesToJson = getGson().toJson(validSourceTypes);
+
+ // addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES,
+ // validSourceTypesToJson);
+ // addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES,
+ // validSourceTypes);
+
+ addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES, validSourceTypes);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, description);
+ addIfExists(map, GraphPropertiesDictionary.MIN_OCCURRENCES, minOccurrences);
+ addIfExists(map, GraphPropertiesDictionary.MAX_OCCURRENCES, maxOccurrences);
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabilityData [uniqueId=" + uniqueId + ", description=" + description + ", type=" + type
+ + ", validSourceTypes=" + validSourceTypes + ", creationTime=" + creationTime + ", modificationTime="
+ + modificationTime + ", minOccurrences=" + minOccurrences + ", maxOccurrences=" + maxOccurrences + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityInstData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityInstData.java
new file mode 100644
index 0000000000..4c9bf6767e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityInstData.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class CapabilityInstData extends GraphNode {
+
+ public CapabilityInstData() {
+ super(NodeTypeEnum.CapabilityInst);
+ }
+
+ public CapabilityInstData(Map<String, Object> properties) {
+ this();
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> propertiesfromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.PROPERTIES.getProperty()), listType);
+
+ this.setProperties(propertiesfromJson);
+
+ // this.setProperties((ArrayList<String>) properties
+ // .get(GraphPropertiesDictionary.PROPERTIES.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+ }
+
+ private String uniqueId;
+
+ private List<String> properties;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public List<String> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<String> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ // String propertiesToJson = getGson().toJson(properties);
+
+ // addIfExists(map, GraphPropertiesDictionary.PROPERTIES,
+ // propertiesToJson);
+
+ addIfExists(map, GraphPropertiesDictionary.PROPERTIES, properties);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabilityInstData [uniqueId=" + uniqueId + ", properties=" + properties + ", creationTime="
+ + creationTime + ", modificationTime=" + modificationTime + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityTypeData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityTypeData.java
new file mode 100644
index 0000000000..81e8ce9174
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CapabilityTypeData.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.CapabilityTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class CapabilityTypeData extends GraphNode {
+
+ CapabilityTypeDataDefinition capabilityTypeDataDefinition;
+
+ // private List<String> constraints;
+
+ public CapabilityTypeData() {
+ super(NodeTypeEnum.CapabilityType);
+ capabilityTypeDataDefinition = new CapabilityTypeDataDefinition();
+ }
+
+ public CapabilityTypeData(CapabilityTypeDataDefinition capabilityTypeDataDefinition) {
+ super(NodeTypeEnum.CapabilityType);
+ this.capabilityTypeDataDefinition = capabilityTypeDataDefinition;
+ // this.constraints = constraints;
+ }
+
+ public CapabilityTypeData(Map<String, Object> properties) {
+
+ this();
+
+ capabilityTypeDataDefinition
+ .setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ capabilityTypeDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ capabilityTypeDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> validSourceTypesfromJson = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.VALID_SOURCE_TYPES.getProperty()), listType);
+
+ capabilityTypeDataDefinition.setValidSourceTypes(validSourceTypesfromJson);
+
+ // capabilityTypeDataDefinition.setValidSourceTypes((List<String>)
+ // properties.get(GraphPropertiesDictionary.VALID_SOURCE_TYPES
+ // .getProperty()));
+
+ capabilityTypeDataDefinition
+ .setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ capabilityTypeDataDefinition
+ .setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ // capabilityTypeDataDefinition.setVersion(version);
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, capabilityTypeDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, capabilityTypeDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, capabilityTypeDataDefinition.getDescription());
+
+ // String validSourceTypesToJson =
+ // getGson().toJson(capabilityTypeDataDefinition.getValidSourceTypes());
+
+ // addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES,
+ // validSourceTypesToJson);
+
+ addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES,
+ capabilityTypeDataDefinition.getValidSourceTypes());
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, capabilityTypeDataDefinition.getCreationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE,
+ capabilityTypeDataDefinition.getModificationTime());
+
+ return map;
+ }
+
+ public CapabilityTypeDataDefinition getCapabilityTypeDataDefinition() {
+ return capabilityTypeDataDefinition;
+ }
+
+ public void setCapabilityTypeDataDefinition(CapabilityTypeDataDefinition capabilityTypeDataDefinition) {
+ this.capabilityTypeDataDefinition = capabilityTypeDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabilityTypeData [capabilityTypeDataDefinition=" + capabilityTypeDataDefinition + "]";
+ }
+
+ @Override
+ public String getUniqueId() {
+ return this.capabilityTypeDataDefinition.getUniqueId();
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CategoryData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CategoryData.java
new file mode 100644
index 0000000000..4cfde8b0fa
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/CategoryData.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public abstract class CategoryData extends GraphNode {
+
+ private String name;
+ private String normalizedName;
+ private String uniqueId;
+
+ protected abstract void createUniqueId();
+
+ protected CategoryData(NodeTypeEnum label) {
+ super(label);
+ }
+
+ protected CategoryData(String name, String normalizedName, NodeTypeEnum label) {
+ super(label);
+ this.name = name;
+ this.normalizedName = normalizedName;
+ }
+
+ protected CategoryData(Map<String, Object> properties, NodeTypeEnum label) {
+ super(label);
+ setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+ addIfExists(map, GraphPropertiesDictionary.NAME, name);
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+ addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, normalizedName);
+ return map;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNormalizedName() {
+ return normalizedName;
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ this.normalizedName = normalizedName;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public String toString() {
+ return "CategoryData [name=" + name + ", normalizedName=" + normalizedName + "uniqueId=" + uniqueId + "]";
+ }
+
+ /*
+ * @Override public int hashCode() { final int prime = 31; int result = 1;
+ * result = prime * result + ((name == null) ? 0 : name.hashCode()); result
+ * = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); return
+ * result; }
+ *
+ * @Override public boolean equals(Object obj) { if (this == obj) return
+ * true; if (obj == null) return false; if (getClass() != obj.getClass())
+ * return false; CategoryData other = (CategoryData) obj; if (name == null)
+ * { if (other.name != null) return false; } else if
+ * (!name.equals(other.name)) return false; if (uniqueId == null) { if
+ * (other.uniqueId != null) return false; } else if
+ * (!uniqueId.equals(other.uniqueId)) return false; return true; }
+ */
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return uniqueId;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentCacheData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentCacheData.java
new file mode 100644
index 0000000000..727740002c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentCacheData.java
@@ -0,0 +1,153 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.nio.ByteBuffer;
+import java.util.Date;
+
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = "sdccomponent", name = "componentcache")
+public class ComponentCacheData {
+ public final static String RRESOURCE_ID_FIELD = "resourceId";
+
+ public final static String SERVICE_NAME_FIELD = "serviceName";
+ public final static String SERVICE_VERSION_FIELD = "serviceVersion";
+ public final static String ARTIFACT_NAME_FIELD = "artifactName";
+
+ public static String delim = ":";
+
+ @PartitionKey
+ @Column(name = "id")
+ private String id;
+
+ @Column
+ private ByteBuffer data;
+
+ @Column(name = "modification_time")
+ private Date modificationTime;
+
+ @Column
+ private String type;
+
+ @Column(name = "is_dirty")
+ private boolean isDirty;
+
+ @Column(name = "is_zipped")
+ private boolean isZipped;
+
+ public ComponentCacheData() {
+
+ }
+
+ public ComponentCacheData(String id, byte[] data, Date modificationTime, String type, boolean isDirty,
+ boolean isZipped) {
+ super();
+ this.id = id;
+ if (data != null) {
+ this.data = ByteBuffer.wrap(data.clone());
+ }
+ this.modificationTime = modificationTime;
+ this.type = type;
+ this.isDirty = isDirty;
+ this.isZipped = isZipped;
+ }
+
+ public ComponentCacheData(String id) {
+
+ this.id = id;
+ }
+
+ public ComponentCacheData(String artifactId, byte[] data) {
+ super();
+ this.id = artifactId;
+ if (data != null) {
+ this.data = ByteBuffer.wrap(data.clone());
+ // this.data = data.clone();
+ }
+ }
+
+ public byte[] getDataAsArray() {
+ if (data != null) {
+ return data.array();
+ }
+ return null;
+ }
+
+ public void setDataAsArray(byte[] data) {
+ if (data != null) {
+ this.data = ByteBuffer.wrap(data.clone());
+ }
+ }
+
+ public ByteBuffer getData() {
+ return data;
+ }
+
+ public void setData(ByteBuffer data) {
+ if (data != null) {
+ this.data = data.duplicate();
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Date getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Date modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public boolean getIsDirty() {
+ return isDirty;
+ }
+
+ public void setIsDirty(boolean isDirty) {
+ this.isDirty = isDirty;
+ }
+
+ public boolean getIsZipped() {
+ return isZipped;
+ }
+
+ public void setIsZipped(boolean isZipped) {
+ this.isZipped = isZipped;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentInstanceData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentInstanceData.java
new file mode 100644
index 0000000000..c61b99a805
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentInstanceData.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+
+public class ComponentInstanceData extends GraphNode {
+
+ ComponentInstanceDataDefinition componentInstDataDefinition;
+
+ public ComponentInstanceData() {
+ super(NodeTypeEnum.ResourceInstance);
+ this.componentInstDataDefinition = new ComponentInstanceDataDefinition();
+ }
+
+ public ComponentInstanceData(ComponentInstanceDataDefinition componentInstDataDefinition) {
+ super(NodeTypeEnum.ResourceInstance);
+ this.componentInstDataDefinition = componentInstDataDefinition;
+ }
+
+ public ComponentInstanceData(Map<String, Object> properties) {
+
+ this();
+
+ componentInstDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ componentInstDataDefinition.setComponentUid((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+ componentInstDataDefinition.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+ componentInstDataDefinition.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+ componentInstDataDefinition.setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ componentInstDataDefinition.setPosX((String) properties.get(GraphPropertiesDictionary.POSITION_X.getProperty()));
+ componentInstDataDefinition.setPosY((String) properties.get(GraphPropertiesDictionary.POSITION_Y.getProperty()));
+ componentInstDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ componentInstDataDefinition.setPropertyValueCounter((Integer) properties.get(GraphPropertiesDictionary.PROPERTY_COUNTER.getProperty()));
+ componentInstDataDefinition.setAttributeValueCounter((Integer) properties.get(GraphPropertiesDictionary.ATTRIBUTE_COUNTER.getProperty()));
+ componentInstDataDefinition.setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
+ componentInstDataDefinition.setOriginType(OriginTypeEnum.findByValue((String) properties.get(GraphPropertiesDictionary.ORIGIN_TYPE.getProperty())));
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, componentInstDataDefinition.getComponentUid());
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, componentInstDataDefinition.getCreationTime());
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, componentInstDataDefinition.getModificationTime());
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, componentInstDataDefinition.getDescription());
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, componentInstDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.POSITION_X, componentInstDataDefinition.getPosX());
+ addIfExists(map, GraphPropertiesDictionary.POSITION_Y, componentInstDataDefinition.getPosY());
+ addIfExists(map, GraphPropertiesDictionary.NAME, componentInstDataDefinition.getName());
+ addIfExists(map, GraphPropertiesDictionary.PROPERTY_COUNTER, componentInstDataDefinition.getPropertyValueCounter());
+ addIfExists(map, GraphPropertiesDictionary.ATTRIBUTE_COUNTER, componentInstDataDefinition.getAttributeValueCounter());
+ addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, componentInstDataDefinition.getNormalizedName());
+ if (componentInstDataDefinition.getOriginType() != null) {
+ addIfExists(map, GraphPropertiesDictionary.ORIGIN_TYPE, componentInstDataDefinition.getOriginType().getValue());
+ }
+
+ return map;
+ }
+
+ @Override
+ public String getUniqueId() {
+ return componentInstDataDefinition.getUniqueId();
+ }
+
+ public String getName() {
+ return componentInstDataDefinition.getName();
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ public ComponentInstanceDataDefinition getComponentInstDataDefinition() {
+ return componentInstDataDefinition;
+ }
+
+ public void setComponentInstDataDefinition(ComponentInstanceDataDefinition componentInstDataDefinition) {
+ this.componentInstDataDefinition = componentInstDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentInstanceData [componentInstDataDefinition=" + componentInstDataDefinition + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentMetadataData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentMetadataData.java
new file mode 100644
index 0000000000..e39dfe226f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ComponentMetadataData.java
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public abstract class ComponentMetadataData extends GraphNode {
+
+ protected ComponentMetadataDataDefinition metadataDataDefinition;
+ protected Integer componentInstanceCounter;
+
+ public ComponentMetadataData(NodeTypeEnum label, ComponentMetadataDataDefinition metadataDataDefinition) {
+ super(label);
+ this.metadataDataDefinition = metadataDataDefinition;
+ this.componentInstanceCounter = 0;
+ }
+
+ public ComponentMetadataData(NodeTypeEnum label, ComponentMetadataDataDefinition metadataDataDefinition,
+ Map<String, Object> properties) {
+ this(label, metadataDataDefinition);
+ metadataDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ metadataDataDefinition
+ .setCreationDate((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+ metadataDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ metadataDataDefinition.setIcon((String) properties.get(GraphPropertiesDictionary.ICON.getProperty()));
+ metadataDataDefinition.setHighestVersion(
+ (Boolean) properties.get(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty()));
+ metadataDataDefinition
+ .setLastUpdateDate((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+ metadataDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ metadataDataDefinition.setState((String) properties.get(GraphPropertiesDictionary.STATE.getProperty()));
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> tagsFromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.TAGS.getProperty()), listType);
+ metadataDataDefinition.setTags(tagsFromJson);
+ metadataDataDefinition.setVersion((String) properties.get(GraphPropertiesDictionary.VERSION.getProperty()));
+ metadataDataDefinition
+ .setContactId((String) properties.get(GraphPropertiesDictionary.CONTACT_ID.getProperty()));
+ metadataDataDefinition.setUUID((String) properties.get(GraphPropertiesDictionary.UUID.getProperty()));
+ metadataDataDefinition
+ .setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
+ metadataDataDefinition
+ .setSystemName((String) properties.get(GraphPropertiesDictionary.SYSTEM_NAME.getProperty()));
+ metadataDataDefinition
+ .setIsDeleted((Boolean) properties.get(GraphPropertiesDictionary.IS_DELETED.getProperty()));
+ metadataDataDefinition.setProjectCode((String) properties.get(GraphPropertiesDictionary.PROJECT_CODE.getProperty()));
+ metadataDataDefinition.setCsarUUID((String) properties.get(GraphPropertiesDictionary.CSAR_UUID.getProperty()));
+ metadataDataDefinition
+ .setCsarVersion((String) properties.get(GraphPropertiesDictionary.CSAR_VERSION.getProperty()));
+ metadataDataDefinition.setImportedToscaChecksum(
+ (String) properties.get(GraphPropertiesDictionary.IMPORTED_TOSCA_CHECKSUM.getProperty()));
+ metadataDataDefinition
+ .setInvariantUUID((String) properties.get(GraphPropertiesDictionary.INVARIANT_UUID.getProperty()));
+ componentInstanceCounter = (Integer) properties.get(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty());
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, metadataDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.VERSION, metadataDataDefinition.getVersion());
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, metadataDataDefinition.getCreationDate());
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, metadataDataDefinition.getDescription());
+ addIfExists(map, GraphPropertiesDictionary.ICON, metadataDataDefinition.getIcon());
+ addIfExists(map, GraphPropertiesDictionary.IS_HIGHEST_VERSION, metadataDataDefinition.isHighestVersion());
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, metadataDataDefinition.getLastUpdateDate());
+ addIfExists(map, GraphPropertiesDictionary.STATE, metadataDataDefinition.getState());
+ addIfExists(map, GraphPropertiesDictionary.TAGS, metadataDataDefinition.getTags());
+ addIfExists(map, GraphPropertiesDictionary.CONTACT_ID, metadataDataDefinition.getContactId());
+ addIfExists(map, GraphPropertiesDictionary.NAME, metadataDataDefinition.getName());
+ addIfExists(map, GraphPropertiesDictionary.UUID, metadataDataDefinition.getUUID());
+ addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, metadataDataDefinition.getNormalizedName());
+ addIfExists(map, GraphPropertiesDictionary.SYSTEM_NAME, metadataDataDefinition.getSystemName());
+ addIfExists(map, GraphPropertiesDictionary.IS_DELETED, metadataDataDefinition.isDeleted());
+ addIfExists(map, GraphPropertiesDictionary.INSTANCE_COUNTER, componentInstanceCounter);
+ addIfExists(map, GraphPropertiesDictionary.PROJECT_CODE, metadataDataDefinition.getProjectCode());
+ addIfExists(map, GraphPropertiesDictionary.CSAR_UUID, metadataDataDefinition.getCsarUUID());
+ addIfExists(map, GraphPropertiesDictionary.CSAR_VERSION, metadataDataDefinition.getCsarVersion());
+ addIfExists(map, GraphPropertiesDictionary.IMPORTED_TOSCA_CHECKSUM,
+ metadataDataDefinition.getImportedToscaChecksum());
+ addIfExists(map, GraphPropertiesDictionary.INVARIANT_UUID, metadataDataDefinition.getInvariantUUID());
+ return map;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return metadataDataDefinition.getUniqueId();
+ }
+
+ public ComponentMetadataDataDefinition getMetadataDataDefinition() {
+ return metadataDataDefinition;
+ }
+
+ public void setMetadataDataDefinition(ComponentMetadataDataDefinition metadataDataDefinition) {
+ this.metadataDataDefinition = metadataDataDefinition;
+ }
+
+ public Integer getComponentInstanceCounter() {
+ return componentInstanceCounter;
+ }
+
+ public void setComponentInstanceCounter(Integer componentInstanceCounter) {
+ this.componentInstanceCounter = componentInstanceCounter;
+ }
+
+ public Integer increaseAndGetComponentInstanceCounter() {
+ return ++componentInstanceCounter;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentMetadataData [metadataDataDefinition=" + metadataDataDefinition + ", componentInstanceCounter="
+ + componentInstanceCounter + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ConsumerData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ConsumerData.java
new file mode 100644
index 0000000000..1016930e2a
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ConsumerData.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class ConsumerData extends GraphNode {
+
+ private ConsumerDataDefinition consumerDataDefinition;
+
+ public ConsumerData() {
+ super(NodeTypeEnum.ConsumerCredentials);
+ consumerDataDefinition = new ConsumerDataDefinition();
+ }
+
+ public ConsumerData(ConsumerDataDefinition consumerDataDefinition) {
+ super(NodeTypeEnum.ConsumerCredentials);
+ this.consumerDataDefinition = consumerDataDefinition;
+
+ }
+
+ public ConsumerData(Map<String, Object> properties) {
+ super(NodeTypeEnum.ConsumerCredentials);
+ consumerDataDefinition = new ConsumerDataDefinition();
+ consumerDataDefinition.setConsumerDetailsLastupdatedtime((Long) properties.get(GraphPropertiesDictionary.CONSUMER_DETAILS_LAST_UPDATED_TIME.getProperty()));
+ consumerDataDefinition.setConsumerLastAuthenticationTime((Long) properties.get(GraphPropertiesDictionary.CONSUMER_LAST_AUTHENTICATION_TIME.getProperty()));
+ consumerDataDefinition.setConsumerName((String) properties.get(GraphPropertiesDictionary.CONSUMER_NAME.getProperty()));
+ consumerDataDefinition.setConsumerPassword((String) properties.get(GraphPropertiesDictionary.CONSUMER_PASSWORD.getProperty()));
+ consumerDataDefinition.setConsumerSalt((String) properties.get(GraphPropertiesDictionary.CONSUMER_SALT.getProperty()));
+ consumerDataDefinition.setLastModfierAtuid((String) properties.get(GraphPropertiesDictionary.LAST_MODIFIER_USER_ID.getProperty()));
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.CONSUMER_NAME.getProperty();
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return consumerDataDefinition.getConsumerName();
+ }
+
+ public ConsumerDataDefinition getConsumerDataDefinition() {
+ return consumerDataDefinition;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+ addIfExists(map, GraphPropertiesDictionary.CONSUMER_NAME, this.consumerDataDefinition.getConsumerName());
+ addIfExists(map, GraphPropertiesDictionary.CONSUMER_PASSWORD,
+ this.consumerDataDefinition.getConsumerPassword());
+ addIfExists(map, GraphPropertiesDictionary.CONSUMER_SALT, this.consumerDataDefinition.getConsumerSalt());
+ addIfExists(map, GraphPropertiesDictionary.CONSUMER_LAST_AUTHENTICATION_TIME,
+ this.consumerDataDefinition.getConsumerLastAuthenticationTime());
+ addIfExists(map, GraphPropertiesDictionary.CONSUMER_DETAILS_LAST_UPDATED_TIME,
+ this.consumerDataDefinition.getConsumerDetailsLastupdatedtime());
+ addIfExists(map, GraphPropertiesDictionary.LAST_MODIFIER_USER_ID,
+ this.consumerDataDefinition.getLastModfierAtuid());
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "ConsumerData [consumerDataDefinition=" + consumerDataDefinition + "]";
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/DataTypeData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/DataTypeData.java
new file mode 100644
index 0000000000..2f745243c6
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/DataTypeData.java
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class DataTypeData extends GraphNode {
+
+ DataTypeDataDefinition dataTypeDataDefinition;
+
+ public DataTypeData() {
+ super(NodeTypeEnum.DataType);
+ dataTypeDataDefinition = new DataTypeDataDefinition();
+ }
+
+ public DataTypeData(DataTypeDataDefinition dataTypeDataDefinition) {
+ super(NodeTypeEnum.DataType);
+ this.dataTypeDataDefinition = dataTypeDataDefinition;
+ // this.constraints = constraints;
+ }
+
+ public DataTypeData(Map<String, Object> properties) {
+
+ this();
+
+ dataTypeDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ dataTypeDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ dataTypeDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ dataTypeDataDefinition
+ .setDerivedFromName((String) properties.get(GraphPropertiesDictionary.DERIVED_FROM.getProperty()));
+
+ dataTypeDataDefinition
+ .setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ dataTypeDataDefinition
+ .setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, dataTypeDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.NAME, dataTypeDataDefinition.getName());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, dataTypeDataDefinition.getDescription());
+
+ addIfExists(map, GraphPropertiesDictionary.DERIVED_FROM, dataTypeDataDefinition.getDerivedFromName());
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, dataTypeDataDefinition.getCreationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, dataTypeDataDefinition.getModificationTime());
+
+ return map;
+ }
+
+ public DataTypeDataDefinition getDataTypeDataDefinition() {
+ return dataTypeDataDefinition;
+ }
+
+ public void setDataTypeDataDefinition(DataTypeDataDefinition dataTypeDataDefinition) {
+ this.dataTypeDataDefinition = dataTypeDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "DataTypeData [dataTypeDataDefinition=" + dataTypeDataDefinition + "]";
+ }
+
+ @Override
+ public String getUniqueId() {
+ return this.dataTypeDataDefinition.getUniqueId();
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ESArtifactData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ESArtifactData.java
new file mode 100644
index 0000000000..56101d031f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ESArtifactData.java
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.nio.ByteBuffer;
+
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = "sdcArtifact", name = "resources")
+public class ESArtifactData {
+ public final static String RRESOURCE_ID_FIELD = "resourceId";
+
+ public final static String SERVICE_NAME_FIELD = "serviceName";
+ public final static String SERVICE_VERSION_FIELD = "serviceVersion";
+ public final static String ARTIFACT_NAME_FIELD = "artifactName";
+
+ public static String delim = ":";
+
+ @PartitionKey
+ @Column(name = "id")
+ private String id;
+
+ /*
+ * Base64 encoded Artifact file data
+ */
+
+ @Column
+ private ByteBuffer data;
+
+ // private byte[] data;
+
+ public ESArtifactData() {
+
+ }
+
+ public ESArtifactData(String id) {
+
+ this.id = id;
+ }
+
+ public ESArtifactData(String artifactId, byte[] data) {
+ super();
+ this.id = artifactId;
+ if (data != null) {
+ this.data = ByteBuffer.wrap(data.clone());
+ // this.data = data.clone();
+ }
+
+ }
+
+ public byte[] getDataAsArray() {
+ // return data;
+ if (data != null) {
+ return data.array();
+ }
+ return null;
+ }
+
+ public void setDataAsArray(byte[] data) {
+ if (data != null) {
+ // this.data = data.clone();
+ this.data = ByteBuffer.wrap(data.clone());
+ }
+ }
+
+ public ByteBuffer getData() {
+ // return data;
+ return data;
+ }
+
+ public void setData(ByteBuffer data) {
+ if (data != null) {
+ // this.data = data.clone();
+ this.data = data.duplicate();
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/EntryData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/EntryData.java
new file mode 100644
index 0000000000..a3229bf88a
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/EntryData.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Map;
+
+public class EntryData<K, V> implements Map.Entry<K, V> {
+
+ private final K key;
+ private V value;
+
+ public EntryData(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public K getKey() {
+ return key;
+ }
+
+ @Override
+ public V getValue() {
+ return value;
+ }
+
+ @Override
+ public V setValue(V value) {
+ V old = this.value;
+ this.value = value;
+ return old;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GraphNodeLock.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GraphNodeLock.java
new file mode 100644
index 0000000000..200521c054
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GraphNodeLock.java
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class GraphNodeLock extends GraphNode {
+
+ private String uniqueId;
+ private Long time;
+
+ public GraphNodeLock() {
+ super(NodeTypeEnum.LockNode);
+ time = System.currentTimeMillis();
+ }
+
+ public GraphNodeLock(String uniqueId) {
+ this();
+ this.uniqueId = uniqueId;
+ }
+
+ public GraphNodeLock(Map<String, Object> properties) {
+ super(NodeTypeEnum.LockNode);
+
+ setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ setTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return uniqueId;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, time);
+ return map;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getTime() {
+ return time;
+ }
+
+ public void setTime(Long time) {
+ this.time = time;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupData.java
new file mode 100644
index 0000000000..e354e95b27
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupData.java
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class GroupData extends GraphNode {
+
+ GroupDataDefinition groupDataDefinition;
+
+ public GroupData() {
+ super(NodeTypeEnum.Group);
+ groupDataDefinition = new GroupDataDefinition();
+ }
+
+ public GroupData(GroupDataDefinition groupDataDefinition) {
+ super(NodeTypeEnum.Group);
+ this.groupDataDefinition = groupDataDefinition;
+ }
+
+ public GroupData(Map<String, Object> properties) {
+
+ super(NodeTypeEnum.Group);
+
+ groupDataDefinition = new GroupDataDefinition();
+
+ groupDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ groupDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ groupDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+ groupDataDefinition.setVersion((String) properties.get(GraphPropertiesDictionary.VERSION.getProperty()));
+ groupDataDefinition
+ .setInvariantUUID((String) properties.get(GraphPropertiesDictionary.INVARIANT_UUID.getProperty()));
+ groupDataDefinition.setGroupUUID((String) properties.get(GraphPropertiesDictionary.GROUP_UUID.getProperty()));
+ groupDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ groupDataDefinition.setPropertyValueCounter(
+ (Integer) properties.get(GraphPropertiesDictionary.PROPERTY_COUNTER.getProperty()));
+
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return groupDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+ addIfExists(map, GraphPropertiesDictionary.NAME, groupDataDefinition.getName());
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, groupDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.TYPE, groupDataDefinition.getType());
+ addIfExists(map, GraphPropertiesDictionary.VERSION, groupDataDefinition.getVersion());
+ addIfExists(map, GraphPropertiesDictionary.INVARIANT_UUID, groupDataDefinition.getInvariantUUID());
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, groupDataDefinition.getDescription());
+ addIfExists(map, GraphPropertiesDictionary.PROPERTY_COUNTER, groupDataDefinition.getPropertyValueCounter());
+ addIfExists(map, GraphPropertiesDictionary.GROUP_UUID, groupDataDefinition.getGroupUUID());
+
+ return map;
+ }
+
+ public GroupDataDefinition getGroupDataDefinition() {
+ return groupDataDefinition;
+ }
+
+ public void setGroupDataDefinition(GroupDataDefinition groupDataDefinition) {
+ this.groupDataDefinition = groupDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupData [ " + groupDataDefinition.toString() + "]";
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupTypeData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupTypeData.java
new file mode 100644
index 0000000000..87f4fcf283
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/GroupTypeData.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.GroupTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class GroupTypeData extends GraphNode {
+
+ private static Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ private static Type mapType = new TypeToken<HashMap<String, String>>() {
+ }.getType();
+
+ GroupTypeDataDefinition groupTypeDataDefinition;
+
+ public GroupTypeData() {
+ super(NodeTypeEnum.GroupType);
+ groupTypeDataDefinition = new GroupTypeDataDefinition();
+ }
+
+ public GroupTypeData(GroupTypeDataDefinition groupTypeDataDefinition) {
+ super(NodeTypeEnum.GroupType);
+ this.groupTypeDataDefinition = groupTypeDataDefinition;
+ }
+
+ public GroupTypeData(Map<String, Object> properties) {
+
+ this();
+
+ groupTypeDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ groupTypeDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ groupTypeDataDefinition.setVersion((String) properties.get(GraphPropertiesDictionary.VERSION.getProperty()));
+
+ groupTypeDataDefinition.setHighestVersion(
+ (boolean) properties.get(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty()));
+
+ groupTypeDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ List<String> members = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.MEMBERS.getProperty()), listType);
+ groupTypeDataDefinition.setMembers(members);
+
+ HashMap<String, String> metatdata = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.METADATA.getProperty()), mapType);
+ groupTypeDataDefinition.setMetadata(metatdata);
+
+ groupTypeDataDefinition
+ .setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ groupTypeDataDefinition
+ .setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, groupTypeDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, groupTypeDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.VERSION, groupTypeDataDefinition.getVersion());
+
+ addIfExists(map, GraphPropertiesDictionary.IS_HIGHEST_VERSION, groupTypeDataDefinition.isHighestVersion());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, groupTypeDataDefinition.getDescription());
+
+ addIfExists(map, GraphPropertiesDictionary.METADATA, groupTypeDataDefinition.getMetadata());
+
+ addIfExists(map, GraphPropertiesDictionary.MEMBERS, groupTypeDataDefinition.getMembers());
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, groupTypeDataDefinition.getCreationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, groupTypeDataDefinition.getModificationTime());
+
+ return map;
+ }
+
+ public GroupTypeDataDefinition getGroupTypeDataDefinition() {
+ return groupTypeDataDefinition;
+ }
+
+ public void setGroupTypeDataDefinition(GroupTypeDataDefinition groupTypeDataDefinition) {
+ this.groupTypeDataDefinition = groupTypeDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupTypeData [groupTypeDataDefinition=" + groupTypeDataDefinition + "]";
+ }
+
+ @Override
+ public String getUniqueId() {
+ return this.groupTypeDataDefinition.getUniqueId();
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterData.java
new file mode 100644
index 0000000000..7115a45bf3
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterData.java
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.elements.HeatParameterDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class HeatParameterData extends GraphNode {
+
+ private HeatParameterDataDefinition heatDataDefinition;
+
+ public HeatParameterData() {
+ super(NodeTypeEnum.HeatParameter);
+ heatDataDefinition = new HeatParameterDataDefinition();
+ }
+
+ public HeatParameterData(HeatParameterDataDefinition heatDataDef) {
+ super(NodeTypeEnum.HeatParameter);
+ this.heatDataDefinition = heatDataDef;
+ }
+
+ public HeatParameterData(Map<String, Object> properties) {
+ this();
+
+ heatDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ heatDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ String type = (String) properties.get(GraphPropertiesDictionary.TYPE.getProperty());
+ heatDataDefinition.setType(type);
+
+ String description = (String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(description)) {
+ heatDataDefinition.setDescription(null);
+ } else {
+ heatDataDefinition.setDescription(description);
+ }
+
+ String defaultValue = (String) properties.get(GraphPropertiesDictionary.DEFAULT_VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(defaultValue)) {
+ heatDataDefinition.setDefaultValue(null);
+ } else {
+ heatDataDefinition.setDefaultValue(getValue(type, defaultValue));
+ }
+
+ String value = (String) properties.get(GraphPropertiesDictionary.VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(value)) {
+ heatDataDefinition.setCurrentValue(null);
+ } else {
+ heatDataDefinition.setCurrentValue(getValue(type, value));
+ }
+
+ }
+
+ private String getValue(String type, String value) {
+ if (Constants.GRAPH_EMPTY_VALUE.equals(value)) {
+ return value;
+ }
+ if ("number".equals(type)) {
+ return new BigDecimal(value).toPlainString();
+ }
+ return value;
+ }
+
+ public HeatParameterDataDefinition getHeatDataDefinition() {
+ return heatDataDefinition;
+ }
+
+ public void setHeatDataDefinition(HeatParameterDataDefinition heatDataDefinition) {
+ this.heatDataDefinition = heatDataDefinition;
+ }
+
+ public String getName() {
+ return heatDataDefinition.getName();
+ }
+
+ public void setName(String name) {
+ heatDataDefinition.setName(name);
+ }
+
+ public String getType() {
+ return heatDataDefinition.getType();
+ }
+
+ public void setType(String type) {
+ heatDataDefinition.setType(type);
+ }
+
+ public String getDescription() {
+ return heatDataDefinition.getDescription();
+ }
+
+ public void setDescription(String description) {
+ heatDataDefinition.setDescription(description);
+ }
+
+ public String getCurrentValue() {
+ return heatDataDefinition.getCurrentValue();
+ }
+
+ public void setCurrentValue(String currentValue) {
+ heatDataDefinition.setCurrentValue(currentValue);
+ }
+
+ public String getDefaultValue() {
+ return heatDataDefinition.getDefaultValue();
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ heatDataDefinition.setDefaultValue(defaultValue);
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return heatDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.NAME, getName());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, getType());
+
+ String description = getDescription();
+ if (description == null) {
+ description = Constants.GRAPH_EMPTY_VALUE;
+ }
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, description);
+
+ String defaultVal = getDefaultValue();
+ if (defaultVal == null) {
+ defaultVal = Constants.GRAPH_EMPTY_VALUE;
+ }
+ addIfExists(map, GraphPropertiesDictionary.DEFAULT_VALUE, getValue(getType(), defaultVal));
+
+ String currentVal = getCurrentValue();
+ if (currentVal == null) {
+ currentVal = Constants.GRAPH_EMPTY_VALUE;
+ }
+
+ addIfExists(map, GraphPropertiesDictionary.VALUE, getValue(getType(), currentVal));
+ return map;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterValueData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterValueData.java
new file mode 100644
index 0000000000..ecabe22b80
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/HeatParameterValueData.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class HeatParameterValueData extends GraphNode {
+
+ public HeatParameterValueData() {
+ super(NodeTypeEnum.HeatParameterValue);
+ }
+
+ public HeatParameterValueData(Map<String, Object> properties) {
+ this();
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ String value = (String) properties.get(GraphPropertiesDictionary.VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(value)) {
+ this.setValue(null);
+ } else {
+ this.setValue(value);
+ }
+
+ }
+
+ private String uniqueId;
+
+ private String value;
+
+ @Override
+ public Object getUniqueId() {
+ return uniqueId;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ String updatedValue = value;
+ if (updatedValue == null) {
+ updatedValue = Constants.GRAPH_EMPTY_VALUE;
+ }
+ addIfExists(map, GraphPropertiesDictionary.VALUE, updatedValue);
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "HeatParameterValueData [uniqueId=" + uniqueId + ", value=" + value + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputValueData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputValueData.java
new file mode 100644
index 0000000000..1ed3ef1135
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputValueData.java
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class InputValueData extends GraphNode {
+ private String uniqueId;
+
+ private String value;
+
+ private String type;
+
+ private Boolean hidden;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ public InputValueData() {
+ super(NodeTypeEnum.InputValue);
+ }
+
+ public InputValueData(Map<String, Object> properties) {
+ this();
+
+ this.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ String updatedValue = (String) properties.get(GraphPropertiesDictionary.VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(updatedValue)) {
+ this.setValue(null);
+ } else {
+ this.setValue(updatedValue);
+ }
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ this.setHidden((Boolean) properties.get(GraphPropertiesDictionary.HIDDEN.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, type);
+
+ addIfExists(map, GraphPropertiesDictionary.HIDDEN, hidden);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ String updatedValue = Objects.isNull(value) ? Constants.GRAPH_EMPTY_VALUE : value;
+ addIfExists(map, GraphPropertiesDictionary.VALUE, updatedValue);
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "InputValueData [uniqueId=" + uniqueId + ", hidden=" + hidden + ", type=" + type + ", creationTime="
+ + creationTime + ", value=" + value + ", modificationTime=" + modificationTime + "]";
+ }
+
+ public Boolean isHidden() {
+ return hidden;
+ }
+
+ public void setHidden(Boolean hidden) {
+ this.hidden = hidden;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputsData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputsData.java
new file mode 100644
index 0000000000..3bb71d5bed
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InputsData.java
@@ -0,0 +1,149 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class InputsData extends GraphNode {
+
+ PropertyDataDefinition propertyDataDefinition;
+
+ private List<String> constraints;
+
+ public InputsData() {
+ super(NodeTypeEnum.Input);
+ propertyDataDefinition = new PropertyDataDefinition();
+ }
+
+ public InputsData(PropertyDataDefinition propertyDataDefinition, List<String> constraints) {
+ super(NodeTypeEnum.Input);
+ this.propertyDataDefinition = propertyDataDefinition;
+ this.constraints = constraints;
+ }
+
+ public InputsData(Map<String, Object> properties) {
+
+ this();
+
+ propertyDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ propertyDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ propertyDataDefinition.setRequired((Boolean) properties.get(GraphPropertiesDictionary.REQUIRED.getProperty()));
+
+ String defaultValue = (String) properties.get(GraphPropertiesDictionary.DEFAULT_VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(defaultValue)) {
+ propertyDataDefinition.setDefaultValue(null);
+ } else {
+ propertyDataDefinition.setDefaultValue(defaultValue);
+ }
+
+ propertyDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> constraintsfromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.CONSTRAINTS.getProperty()), listType);
+ setConstraints(constraintsfromJson);
+ // setConstraints((List<String>)
+ // properties.get(GraphPropertiesDictionary.CONSTRAINTS.getProperty()));
+
+ Type schemaType = new TypeToken<SchemaDefinition>() {
+ }.getType();
+ SchemaDefinition schema = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.ENTRY_SCHEMA.getProperty()), schemaType);
+ propertyDataDefinition.setSchema(schema);
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, propertyDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, propertyDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, propertyDataDefinition.getDescription());
+
+ String defaultValue = propertyDataDefinition.getDefaultValue();
+ if (defaultValue == null) {
+ defaultValue = Constants.GRAPH_EMPTY_VALUE;
+ }
+ addIfExists(map, GraphPropertiesDictionary.DEFAULT_VALUE, defaultValue);
+
+ addIfExists(map, GraphPropertiesDictionary.REQUIRED, propertyDataDefinition.isRequired());
+
+ addIfExists(map, GraphPropertiesDictionary.CONSTRAINTS, getConstraints());
+
+ SchemaDefinition entrySchema = propertyDataDefinition.getSchema();
+ if (entrySchema != null) {
+ String entrySchemaStr = getGson().toJson(entrySchema);
+ addIfExists(map, GraphPropertiesDictionary.ENTRY_SCHEMA, entrySchemaStr);
+ }
+ // String constraintsAsJson = getGson().toJson(getConstraints());
+ // addIfExists(map, GraphPropertiesDictionary.CONSTRAINTS,
+ // constraintsAsJson);
+
+ return map;
+ }
+
+ public List<String> getConstraints() {
+ return constraints;
+ }
+
+ public void setConstraints(List<String> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return propertyDataDefinition.getUniqueId();
+ }
+
+ public PropertyDataDefinition getPropertyDataDefinition() {
+ return propertyDataDefinition;
+ }
+
+ public void setPropertyDataDefinition(PropertyDataDefinition propertyDataDefinition) {
+ this.propertyDataDefinition = propertyDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyData [propertyDataDefinition=" + propertyDataDefinition + ", constraints=" + constraints + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InterfaceData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InterfaceData.java
new file mode 100644
index 0000000000..4ad07d70eb
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/InterfaceData.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.InterfaceDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class InterfaceData extends GraphNode {
+
+ private InterfaceDataDefinition interfaceDataDefinition;
+
+ public InterfaceData() {
+ super(NodeTypeEnum.Interface);
+ interfaceDataDefinition = new InterfaceDataDefinition();
+
+ }
+
+ public InterfaceData(InterfaceData p) {
+ super(NodeTypeEnum.Interface);
+ interfaceDataDefinition = p.getInterfaceDataDefinition();
+
+ }
+
+ public InterfaceData(InterfaceDataDefinition interfaceDataDefinition) {
+ super(NodeTypeEnum.Interface);
+ this.interfaceDataDefinition = interfaceDataDefinition;
+
+ }
+
+ public InterfaceData(Map<String, Object> properties) {
+ this();
+ interfaceDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ interfaceDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+ interfaceDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ interfaceDataDefinition
+ .setCreationDate((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+ interfaceDataDefinition
+ .setLastUpdateDate((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+ }
+
+ public InterfaceDataDefinition getInterfaceDataDefinition() {
+ return interfaceDataDefinition;
+ }
+
+ public void setInterfaceDataDefinition(InterfaceDataDefinition interfaceDataDefinition) {
+ this.interfaceDataDefinition = interfaceDataDefinition;
+ }
+
+ @Override
+ public String getUniqueId() {
+ return interfaceDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, interfaceDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.TYPE, interfaceDataDefinition.getType());
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, interfaceDataDefinition.getCreationDate());
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, interfaceDataDefinition.getLastUpdateDate());
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, interfaceDataDefinition.getDescription());
+
+ return map;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/OperationData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/OperationData.java
new file mode 100644
index 0000000000..d87cfe8853
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/OperationData.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class OperationData extends GraphNode {
+
+ OperationDataDefinition operationDataDefinition;
+
+ protected OperationData() {
+ super(NodeTypeEnum.InterfaceOperation);
+ operationDataDefinition = new OperationDataDefinition();
+ }
+
+ public OperationData(OperationDataDefinition operationDataDefinition) {
+ super(NodeTypeEnum.InterfaceOperation);
+ this.operationDataDefinition = operationDataDefinition;
+
+ }
+
+ public OperationData(OperationData operationData) {
+ super(NodeTypeEnum.InterfaceOperation);
+ this.operationDataDefinition = operationData.getOperationDataDefinition();
+
+ }
+
+ public OperationData(Map<String, Object> properties) {
+ this();
+ operationDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ operationDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+ operationDataDefinition
+ .setCreationDate((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+ operationDataDefinition
+ .setLastUpdateDate((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+ }
+
+ public OperationDataDefinition getOperationDataDefinition() {
+ return operationDataDefinition;
+ }
+
+ public void setOperationDataDefinition(OperationDataDefinition operationDataDefinition) {
+ this.operationDataDefinition = operationDataDefinition;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return operationDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, operationDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, operationDataDefinition.getCreationDate());
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, operationDataDefinition.getLastUpdateDate());
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, operationDataDefinition.getDescription());
+
+ return map;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PolicyTypeData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PolicyTypeData.java
new file mode 100644
index 0000000000..62f2ad5e12
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PolicyTypeData.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.PolicyTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class PolicyTypeData extends GraphNode {
+
+ private PolicyTypeDataDefinition policyTypeDataDefinition;
+ private static final Type mapType = new TypeToken<HashMap<String, String>>() {
+ }.getType();
+ private static final Type listType = new TypeToken<List<String>>() {
+ }.getType();
+
+ public PolicyTypeData() {
+ super(NodeTypeEnum.PolicyType);
+ policyTypeDataDefinition = new PolicyTypeDataDefinition();
+ }
+
+ public PolicyTypeData(PolicyTypeDataDefinition policyTypeDataDefinition) {
+ super(NodeTypeEnum.PolicyType);
+ this.policyTypeDataDefinition = policyTypeDataDefinition;
+ }
+
+ public PolicyTypeData(Map<String, Object> properties) {
+
+ this();
+
+ policyTypeDataDefinition
+ .setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ HashMap<String, String> metatdata = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.METADATA.getProperty()), mapType);
+ policyTypeDataDefinition.setMetadata(metatdata);
+
+ List<String> members = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.TARGETS.getProperty()), listType);
+ policyTypeDataDefinition.setTargets(members);
+
+ policyTypeDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ policyTypeDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ policyTypeDataDefinition.setHighestVersion(
+ (boolean) properties.get(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty()));
+
+ policyTypeDataDefinition.setVersion((String) properties.get(GraphPropertiesDictionary.VERSION.getProperty()));
+
+ policyTypeDataDefinition
+ .setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ policyTypeDataDefinition
+ .setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, policyTypeDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, policyTypeDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.VERSION, policyTypeDataDefinition.getVersion());
+
+ addIfExists(map, GraphPropertiesDictionary.IS_HIGHEST_VERSION, policyTypeDataDefinition.isHighestVersion());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, policyTypeDataDefinition.getDescription());
+
+ addIfExists(map, GraphPropertiesDictionary.METADATA, policyTypeDataDefinition.getMetadata());
+
+ addIfExists(map, GraphPropertiesDictionary.TARGETS, policyTypeDataDefinition.getTargets());
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, policyTypeDataDefinition.getCreationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, policyTypeDataDefinition.getModificationTime());
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "PolicyTypeData [policyTypeDataDefinition=" + policyTypeDataDefinition + "]";
+ }
+
+ @Override
+ public String getUniqueId() {
+ return this.policyTypeDataDefinition.getUniqueId();
+ }
+
+ public PolicyTypeDataDefinition getPolicyTypeDataDefinition() {
+ return policyTypeDataDefinition;
+ }
+
+ public void setPolicyTypeDataDefinition(PolicyTypeDataDefinition policyTypeDataDefinition) {
+ this.policyTypeDataDefinition = policyTypeDataDefinition;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ProductMetadataData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ProductMetadataData.java
new file mode 100644
index 0000000000..9bf3ce2a03
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ProductMetadataData.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.ProductMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class ProductMetadataData extends ComponentMetadataData {
+
+ public ProductMetadataData() {
+ super(NodeTypeEnum.Product, new ProductMetadataDataDefinition());
+ }
+
+ public ProductMetadataData(ProductMetadataDataDefinition metadataDataDefinition) {
+ super(NodeTypeEnum.Product, metadataDataDefinition);
+ }
+
+ public ProductMetadataData(Map<String, Object> properties) {
+ super(NodeTypeEnum.Product, new ProductMetadataDataDefinition(), properties);
+ ((ProductMetadataDataDefinition) metadataDataDefinition)
+ .setFullName((String) properties.get(GraphPropertiesDictionary.FULL_NAME.getProperty()));
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> contactsfromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.CONTACTS.getProperty()), listType);
+ ((ProductMetadataDataDefinition) metadataDataDefinition).setContacts(contactsfromJson);
+ ((ProductMetadataDataDefinition) metadataDataDefinition)
+ .setIsActive((Boolean) properties.get(GraphPropertiesDictionary.IS_ACTIVE.getProperty()));
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> graphMap = super.toGraphMap();
+ addIfExists(graphMap, GraphPropertiesDictionary.FULL_NAME,
+ ((ProductMetadataDataDefinition) metadataDataDefinition).getFullName());
+ addIfExists(graphMap, GraphPropertiesDictionary.CONTACTS,
+ ((ProductMetadataDataDefinition) metadataDataDefinition).getContacts());
+ addIfExists(graphMap, GraphPropertiesDictionary.IS_ACTIVE,
+ ((ProductMetadataDataDefinition) metadataDataDefinition).getIsActive());
+ return graphMap;
+ }
+
+ @Override
+ public String toString() {
+ return "ProductMetadataData [metadataDataDefinition=" + metadataDataDefinition + "]";
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyData.java
new file mode 100644
index 0000000000..a72ec9465b
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyData.java
@@ -0,0 +1,149 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class PropertyData extends GraphNode {
+
+ PropertyDataDefinition propertyDataDefinition;
+
+ private List<String> constraints;
+
+ public PropertyData() {
+ super(NodeTypeEnum.Property);
+ propertyDataDefinition = new PropertyDataDefinition();
+ }
+
+ public PropertyData(PropertyDataDefinition propertyDataDefinition, List<String> constraints) {
+ super(NodeTypeEnum.Property);
+ this.propertyDataDefinition = propertyDataDefinition;
+ this.constraints = constraints;
+ }
+
+ public PropertyData(Map<String, Object> properties) {
+
+ this();
+
+ propertyDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ propertyDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ propertyDataDefinition.setRequired((Boolean) properties.get(GraphPropertiesDictionary.REQUIRED.getProperty()));
+
+ String defaultValue = (String) properties.get(GraphPropertiesDictionary.DEFAULT_VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(defaultValue)) {
+ propertyDataDefinition.setDefaultValue(null);
+ } else {
+ propertyDataDefinition.setDefaultValue(defaultValue);
+ }
+
+ propertyDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> constraintsfromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.CONSTRAINTS.getProperty()), listType);
+ setConstraints(constraintsfromJson);
+ // setConstraints((List<String>)
+ // properties.get(GraphPropertiesDictionary.CONSTRAINTS.getProperty()));
+
+ Type schemaType = new TypeToken<SchemaDefinition>() {
+ }.getType();
+ SchemaDefinition schema = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.ENTRY_SCHEMA.getProperty()), schemaType);
+ propertyDataDefinition.setSchema(schema);
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, propertyDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, propertyDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, propertyDataDefinition.getDescription());
+
+ String defaultValue = propertyDataDefinition.getDefaultValue();
+ if (defaultValue == null) {
+ defaultValue = Constants.GRAPH_EMPTY_VALUE;
+ }
+ addIfExists(map, GraphPropertiesDictionary.DEFAULT_VALUE, defaultValue);
+
+ addIfExists(map, GraphPropertiesDictionary.REQUIRED, propertyDataDefinition.isRequired());
+
+ addIfExists(map, GraphPropertiesDictionary.CONSTRAINTS, getConstraints());
+
+ SchemaDefinition entrySchema = propertyDataDefinition.getSchema();
+ if (entrySchema != null) {
+ String entrySchemaStr = getGson().toJson(entrySchema);
+ addIfExists(map, GraphPropertiesDictionary.ENTRY_SCHEMA, entrySchemaStr);
+ }
+ // String constraintsAsJson = getGson().toJson(getConstraints());
+ // addIfExists(map, GraphPropertiesDictionary.CONSTRAINTS,
+ // constraintsAsJson);
+
+ return map;
+ }
+
+ public List<String> getConstraints() {
+ return constraints;
+ }
+
+ public void setConstraints(List<String> constraints) {
+ this.constraints = constraints;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return propertyDataDefinition.getUniqueId();
+ }
+
+ public PropertyDataDefinition getPropertyDataDefinition() {
+ return propertyDataDefinition;
+ }
+
+ public void setPropertyDataDefinition(PropertyDataDefinition propertyDataDefinition) {
+ this.propertyDataDefinition = propertyDataDefinition;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyData [propertyDataDefinition=" + propertyDataDefinition + ", constraints=" + constraints + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyValueData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyValueData.java
new file mode 100644
index 0000000000..7577e57a7e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/PropertyValueData.java
@@ -0,0 +1,158 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.Constants;
+import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class PropertyValueData extends GraphNode {
+
+ public PropertyValueData() {
+ super(NodeTypeEnum.PropertyValue);
+ }
+
+ public PropertyValueData(Map<String, Object> properties) {
+ this();
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ String updatedValue = (String) properties.get(GraphPropertiesDictionary.VALUE.getProperty());
+ if (Constants.GRAPH_EMPTY_VALUE.equals(updatedValue)) {
+ this.setValue(null);
+ } else {
+ this.setValue(updatedValue);
+ }
+
+ this.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ Type mapType = new TypeToken<List<PropertyRule>>() {
+ }.getType();
+ List<PropertyRule> propertyRules = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.PROPERTY_VALUE_RULES.getProperty()), mapType);
+ this.setRules(propertyRules);
+
+ }
+
+ private String uniqueId;
+
+ private String value;
+
+ private String type;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private List<PropertyRule> rules;
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public List<PropertyRule> getRules() {
+ return rules;
+ }
+
+ public void setRules(List<PropertyRule> rules) {
+ this.rules = rules;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ String updatedValue = value;
+ if (updatedValue == null) {
+ updatedValue = Constants.GRAPH_EMPTY_VALUE;
+ }
+ addIfExists(map, GraphPropertiesDictionary.VALUE, updatedValue);
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, type);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.PROPERTY_VALUE_RULES, rules);
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyValueData [uniqueId=" + uniqueId + ", value=" + value + ", type=" + type + ", creationTime="
+ + creationTime + ", modificationTime=" + modificationTime + ", rules=" + rules + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipInstData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipInstData.java
new file mode 100644
index 0000000000..7fc9a601c9
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipInstData.java
@@ -0,0 +1,164 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class RelationshipInstData extends GraphNode {
+
+ private String type;
+
+ private String uniqueId;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private String capabilityOwnerId;
+ private String requirementOwnerId;
+ private String capabiltyId;
+ private String requirementId;
+
+ public RelationshipInstData() {
+ super(NodeTypeEnum.RelationshipInst);
+ }
+
+ public RelationshipInstData(Map<String, Object> properties) {
+
+ this();
+
+ this.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ this.setCapabilityOwnerId((String) properties.get(GraphPropertiesDictionary.CAPABILITY_OWNER_ID.getProperty()));
+ this.setRequirementOwnerId(
+ (String) properties.get(GraphPropertiesDictionary.REQUIREMENT_OWNER_ID.getProperty()));
+ this.setRequirementId((String) properties.get(GraphPropertiesDictionary.REQUIREMENT_ID.getProperty()));
+ this.setCapabiltyId((String) properties.get(GraphPropertiesDictionary.CAPABILITY_ID.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, type);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+ addIfExists(map, GraphPropertiesDictionary.CAPABILITY_OWNER_ID, capabilityOwnerId);
+ addIfExists(map, GraphPropertiesDictionary.REQUIREMENT_OWNER_ID, requirementOwnerId);
+
+ addIfExists(map, GraphPropertiesDictionary.REQUIREMENT_ID, requirementId);
+
+ addIfExists(map, GraphPropertiesDictionary.CAPABILITY_ID, capabiltyId);
+
+ return map;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getCapabilityOwnerId() {
+ return capabilityOwnerId;
+ }
+
+ public void setCapabilityOwnerId(String capabilityOwnerId) {
+ this.capabilityOwnerId = capabilityOwnerId;
+ }
+
+ public String getRequirementOwnerId() {
+ return requirementOwnerId;
+ }
+
+ public void setRequirementOwnerId(String requirementOwnerId) {
+ this.requirementOwnerId = requirementOwnerId;
+ }
+
+ public String getCapabiltyId() {
+ return capabiltyId;
+ }
+
+ public void setCapabiltyId(String capabiltyId) {
+ this.capabiltyId = capabiltyId;
+ }
+
+ public String getRequirementId() {
+ return requirementId;
+ }
+
+ public void setRequirementId(String requirementId) {
+ this.requirementId = requirementId;
+ }
+
+ @Override
+ public String toString() {
+ return "RelationshipInstData [type=" + type + ", uniqueId=" + uniqueId + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + ", capabilityOwnerId=" + capabilityOwnerId
+ + ", requirementOwnerId=" + requirementOwnerId + ", capabiltyId=" + capabiltyId + ", requirementId="
+ + requirementId + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipTypeData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipTypeData.java
new file mode 100644
index 0000000000..7ceecf7e21
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RelationshipTypeData.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.RelationshipTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class RelationshipTypeData extends GraphNode {
+
+ RelationshipTypeDataDefinition relationshipTypeDataDefinition;
+
+ public RelationshipTypeData() {
+ super(NodeTypeEnum.RelationshipType);
+ relationshipTypeDataDefinition = new RelationshipTypeDataDefinition();
+ }
+
+ public RelationshipTypeData(RelationshipTypeDataDefinition relationshipTypeDataDefinition) {
+ super(NodeTypeEnum.RelationshipType);
+ this.relationshipTypeDataDefinition = relationshipTypeDataDefinition;
+ }
+
+ public RelationshipTypeData(Map<String, Object> properties) {
+
+ this();
+
+ relationshipTypeDataDefinition
+ .setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ relationshipTypeDataDefinition.setType((String) properties.get(GraphPropertiesDictionary.TYPE.getProperty()));
+
+ relationshipTypeDataDefinition
+ .setDescription((String) properties.get(GraphPropertiesDictionary.DESCRIPTION.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> validSourceTypesfromJson = getGson().fromJson(
+ (String) properties.get(GraphPropertiesDictionary.VALID_SOURCE_TYPES.getProperty()), listType);
+
+ relationshipTypeDataDefinition.setValidSourceTypes(validSourceTypesfromJson);
+
+ // relationshipTypeDataDefinition.setValidSourceTypes((List<String>)
+ // properties.get(GraphPropertiesDictionary.VALID_SOURCE_TYPES
+ // .getProperty()));
+
+ relationshipTypeDataDefinition
+ .setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ relationshipTypeDataDefinition
+ .setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ // capabilityTypeDataDefinition.setVersion(version);
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, relationshipTypeDataDefinition.getUniqueId());
+
+ addIfExists(map, GraphPropertiesDictionary.TYPE, relationshipTypeDataDefinition.getType());
+
+ addIfExists(map, GraphPropertiesDictionary.DESCRIPTION, relationshipTypeDataDefinition.getDescription());
+
+ // String validSourceTypesToJson =
+ // getGson().toJson(relationshipTypeDataDefinition.getValidSourceTypes());
+
+ // addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES,
+ // validSourceTypesToJson);
+
+ addIfExists(map, GraphPropertiesDictionary.VALID_SOURCE_TYPES,
+ relationshipTypeDataDefinition.getValidSourceTypes());
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, relationshipTypeDataDefinition.getCreationTime());
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE,
+ relationshipTypeDataDefinition.getModificationTime());
+
+ return map;
+ }
+
+ public RelationshipTypeDataDefinition getRelationshipTypeDataDefinition() {
+ return relationshipTypeDataDefinition;
+ }
+
+ public void setRelationshipTypeDataDefinition(RelationshipTypeDataDefinition relationshipTypeDataDefinition) {
+ this.relationshipTypeDataDefinition = relationshipTypeDataDefinition;
+ }
+
+ @Override
+ public String getUniqueId() {
+ return this.relationshipTypeDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public String toString() {
+ return "RelationshipTypeData [relationshipTypeDataDefinition=" + relationshipTypeDataDefinition + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementData.java
new file mode 100644
index 0000000000..2b7e2d22b7
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementData.java
@@ -0,0 +1,158 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class RequirementData extends GraphNode {
+ public final static String MIN_OCCURRENCES = "1";
+ public final static String MAX_OCCURRENCES = "UNBOUNDED";
+ public final static String MAX_DEFAULT_OCCURRENCES = "1";
+
+ private String uniqueId;
+
+ private String node;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private String relationshipType;
+ private String minOccurrences = MIN_OCCURRENCES;
+ private String maxOccurrences = MAX_DEFAULT_OCCURRENCES;
+
+ public RequirementData() {
+ super(NodeTypeEnum.Requirement);
+
+ }
+
+ public RequirementData(Map<String, Object> properties) {
+
+ this();
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ this.setNode((String) properties.get(GraphPropertiesDictionary.NODE.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ this.setRelationshipType((String) properties.get(GraphPropertiesDictionary.RELATIONSHIP_TYPE.getProperty()));
+ this.setMinOccurrences((String) properties.get(GraphPropertiesDictionary.MIN_OCCURRENCES.getProperty()));
+ this.setMaxOccurrences((String) properties.get(GraphPropertiesDictionary.MAX_OCCURRENCES.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ addIfExists(map, GraphPropertiesDictionary.NODE, node);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.RELATIONSHIP_TYPE, relationshipType);
+ addIfExists(map, GraphPropertiesDictionary.MIN_OCCURRENCES, minOccurrences);
+ addIfExists(map, GraphPropertiesDictionary.MAX_OCCURRENCES, maxOccurrences);
+
+ return map;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getRelationshipType() {
+ return relationshipType;
+ }
+
+ public void setRelationshipType(String relationshipType) {
+ this.relationshipType = relationshipType;
+ }
+
+ public String getMinOccurrences() {
+ return minOccurrences;
+ }
+
+ public void setMinOccurrences(String minOccurrences) {
+ if (minOccurrences != null) {
+ this.minOccurrences = minOccurrences;
+ }
+ }
+
+ public String getMaxOccurrences() {
+ return maxOccurrences;
+ }
+
+ public void setMaxOccurrences(String maxOccurrences) {
+ if (maxOccurrences != null) {
+ this.maxOccurrences = maxOccurrences;
+ }
+ }
+
+ @Override
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementData [uniqueId=" + uniqueId + ", node=" + node + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + ", relationshipType=" + relationshipType
+ + ", minOccurrences=" + minOccurrences + ", maxOccurrences=" + maxOccurrences + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementImplData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementImplData.java
new file mode 100644
index 0000000000..1224f873d2
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/RequirementImplData.java
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class RequirementImplData extends GraphNode {
+
+ private String name;
+
+ private String uniqueId;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private String posX;
+
+ private String posY;
+
+ public RequirementImplData() {
+ super(NodeTypeEnum.RequirementImpl);
+ }
+
+ public RequirementImplData(Map<String, Object> properties) {
+
+ this();
+
+ this.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ this.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+
+ this.setCreationTime((Long) properties.get(GraphPropertiesDictionary.CREATION_DATE.getProperty()));
+
+ this.setModificationTime((Long) properties.get(GraphPropertiesDictionary.LAST_UPDATE_DATE.getProperty()));
+
+ setPosX((String) properties.get(GraphPropertiesDictionary.POSITION_X.getProperty()));
+
+ setPosY((String) properties.get(GraphPropertiesDictionary.POSITION_Y.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+
+ addIfExists(map, GraphPropertiesDictionary.NAME, name);
+
+ addIfExists(map, GraphPropertiesDictionary.CREATION_DATE, creationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.LAST_UPDATE_DATE, modificationTime);
+
+ addIfExists(map, GraphPropertiesDictionary.POSITION_X, posX);
+
+ addIfExists(map, GraphPropertiesDictionary.POSITION_Y, posY);
+
+ return map;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPosX() {
+ return posX;
+ }
+
+ public void setPosX(String posX) {
+ this.posX = posX;
+ }
+
+ public String getPosY() {
+ return posY;
+ }
+
+ public void setPosY(String posY) {
+ this.posY = posY;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementImplData [name=" + name + ", uniqueId=" + uniqueId + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + ", posX=" + posX + ", posY=" + posY + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceCategoryData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceCategoryData.java
new file mode 100644
index 0000000000..012780c5a7
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceCategoryData.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class ResourceCategoryData extends CategoryData {
+
+ private String categoryName;
+
+ public ResourceCategoryData() {
+ super(NodeTypeEnum.ResourceCategory);
+ }
+
+ public ResourceCategoryData(String categoryName, String name) {
+ super(name, "", NodeTypeEnum.ResourceCategory);
+ this.categoryName = categoryName;
+ createUniqueId();
+ }
+
+ public ResourceCategoryData(Map<String, Object> properties) {
+ super(properties, NodeTypeEnum.ResourceCategory);
+ setCategoryName((String) properties.get(GraphPropertiesDictionary.CATEGORY_NAME.getProperty()));
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+
+ public void setCategoryName(String categoryName) {
+ this.categoryName = categoryName;
+ }
+
+ @Override
+ protected void createUniqueId() {
+ setUniqueId(getLabel() + "." + this.categoryName + "." + getName());
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceCategoryData [categoryName=" + categoryName + "]" + super.toString();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> graphMap = super.toGraphMap();
+ addIfExists(graphMap, GraphPropertiesDictionary.CATEGORY_NAME, categoryName);
+ return graphMap;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceMetadataData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceMetadataData.java
new file mode 100644
index 0000000000..9df3d7cfac
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ResourceMetadataData.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+
+public class ResourceMetadataData extends ComponentMetadataData {
+
+ public ResourceMetadataData() {
+ super(NodeTypeEnum.Resource, new ResourceMetadataDataDefinition());
+ }
+
+ public ResourceMetadataData(ComponentMetadataDataDefinition metadataDataDefinition) {
+ super(NodeTypeEnum.Resource, metadataDataDefinition);
+ }
+
+ public ResourceMetadataData(Map<String, Object> properties) {
+ super(NodeTypeEnum.Resource, new ResourceMetadataDataDefinition(), properties);
+ ((ResourceMetadataDataDefinition) metadataDataDefinition)
+ .setVendorName((String) properties.get(GraphPropertiesDictionary.VENDOR_NAME.getProperty()));
+ ((ResourceMetadataDataDefinition) metadataDataDefinition)
+ .setVendorRelease((String) properties.get(GraphPropertiesDictionary.VENDOR_RELEASE.getProperty()));
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).setResourceType(ResourceTypeEnum
+ .valueOf((String) properties.get(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty())));
+ ((ResourceMetadataDataDefinition) metadataDataDefinition)
+ .setAbstract((Boolean) properties.get(GraphPropertiesDictionary.IS_ABSTRACT.getProperty()));
+ ((ResourceMetadataDataDefinition) metadataDataDefinition)
+ .setCost((String) properties.get(GraphPropertiesDictionary.COST.getProperty()));
+ ((ResourceMetadataDataDefinition) metadataDataDefinition)
+ .setLicenseType((String) properties.get(GraphPropertiesDictionary.LICENSE_TYPE.getProperty()));
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).setToscaResourceName(
+ (String) properties.get(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty()));
+
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> graphMap = super.toGraphMap();
+ addIfExists(graphMap, GraphPropertiesDictionary.VENDOR_NAME,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).getVendorName());
+ addIfExists(graphMap, GraphPropertiesDictionary.VENDOR_RELEASE,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).getVendorRelease());
+ addIfExists(graphMap, GraphPropertiesDictionary.RESOURCE_TYPE,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).getResourceType().name());
+ addIfExists(graphMap, GraphPropertiesDictionary.IS_ABSTRACT,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).isAbstract());
+ addIfExists(graphMap, GraphPropertiesDictionary.COST,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).getCost());
+ addIfExists(graphMap, GraphPropertiesDictionary.LICENSE_TYPE,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).getLicenseType());
+ addIfExists(graphMap, GraphPropertiesDictionary.TOSCA_RESOURCE_NAME,
+ ((ResourceMetadataDataDefinition) metadataDataDefinition).getToscaResourceName());
+ return graphMap;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceArtifactsDataCollection.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceArtifactsDataCollection.java
new file mode 100644
index 0000000000..6d01268434
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceArtifactsDataCollection.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.List;
+import java.util.Map;
+
+public class ServiceArtifactsDataCollection {
+
+ private Map<String, List<ESArtifactData>> serviceArtifactDataMap;
+
+ public Map<String, List<ESArtifactData>> getServiceArtifactDataMap() {
+ return serviceArtifactDataMap;
+ }
+
+ public void setServiceArtifactDataMap(Map<String, List<ESArtifactData>> serviceArtifactDataMap) {
+ this.serviceArtifactDataMap = serviceArtifactDataMap;
+ }
+
+ public List<ESArtifactData> getNodeTemplateArtifacts(String nodeTemplateName) {
+ if (serviceArtifactDataMap != null && serviceArtifactDataMap.containsKey(nodeTemplateName))
+ return serviceArtifactDataMap.get(nodeTemplateName);
+ else
+ return null;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceCategoryData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceCategoryData.java
new file mode 100644
index 0000000000..666e2e51b6
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceCategoryData.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class ServiceCategoryData extends CategoryData {
+
+ public ServiceCategoryData() {
+ super(NodeTypeEnum.ServiceCategory);
+ }
+
+ public ServiceCategoryData(String categoryName) {
+ super(categoryName, "", NodeTypeEnum.ServiceCategory);
+ createUniqueId();
+ }
+
+ public ServiceCategoryData(Map<String, Object> properties) {
+ super(properties, NodeTypeEnum.ServiceCategory);
+ }
+
+ @Override
+ protected void createUniqueId() {
+ setUniqueId(getLabel() + "." + getName());
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceMetadataData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceMetadataData.java
new file mode 100644
index 0000000000..12f011d72f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ServiceMetadataData.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class ServiceMetadataData extends ComponentMetadataData {
+
+ public ServiceMetadataData() {
+ super(NodeTypeEnum.Service, new ServiceMetadataDataDefinition());
+ }
+
+ public ServiceMetadataData(ServiceMetadataDataDefinition serviceMetadataDataDefinition) {
+ super(NodeTypeEnum.Service, serviceMetadataDataDefinition);
+ }
+
+ public ServiceMetadataData(Map<String, Object> properties) {
+ super(NodeTypeEnum.Service, new ServiceMetadataDataDefinition(), properties);
+ ((ServiceMetadataDataDefinition) metadataDataDefinition)
+ .setProjectCode((String) properties.get(GraphPropertiesDictionary.PROJECT_CODE.getProperty()));
+ ((ServiceMetadataDataDefinition) metadataDataDefinition).setDistributionStatus(
+ (String) properties.get(GraphPropertiesDictionary.DISTRIBUTION_STATUS.getProperty()));
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> graphMap = super.toGraphMap();
+ addIfExists(graphMap, GraphPropertiesDictionary.PROJECT_CODE,
+ ((ServiceMetadataDataDefinition) metadataDataDefinition).getProjectCode());
+ addIfExists(graphMap, GraphPropertiesDictionary.DISTRIBUTION_STATUS,
+ ((ServiceMetadataDataDefinition) metadataDataDefinition).getDistributionStatus());
+ return graphMap;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/TagData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/TagData.java
new file mode 100644
index 0000000000..de2412d7ed
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/TagData.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class TagData extends GraphNode {
+
+ private String name;
+
+ protected TagData(NodeTypeEnum label) {
+ super(label);
+ }
+
+ public TagData(String name) {
+ super(NodeTypeEnum.Tag);
+ this.name = name;
+ }
+
+ public TagData() {
+ super(NodeTypeEnum.Tag);
+ }
+
+ public TagData(Map<String, Object> properties) {
+ super(NodeTypeEnum.Tag);
+ setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+ addIfExists(map, GraphPropertiesDictionary.NAME, name);
+ return map;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Tag [Name=" + name + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ TagData other = (TagData) obj;
+ if (name == null) {
+ if (other.getClass() != null) {
+ return false;
+ }
+ } else if (!name.equals(other.getName())) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.NAME.getProperty();
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return name;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UniqueIdData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UniqueIdData.java
new file mode 100644
index 0000000000..30f9a66d79
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UniqueIdData.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class UniqueIdData extends GraphNode {
+
+ private String uniqueId;
+
+ public UniqueIdData(NodeTypeEnum label, String uniqueId) {
+ super(label);
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return uniqueId;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ return null;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserData.java
new file mode 100644
index 0000000000..d09caacabe
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserData.java
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.DaoUtils;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class UserData extends GraphNode {
+
+ private String firstName;
+
+ private String lastName;
+
+ private String userId;
+
+ private String email;
+
+ private String role;
+
+ private String status;
+
+ private Long lastLoginTime;
+
+ public UserData(String firstName, String lastName, String userId, String email, String role, String status,
+ Long lastLoginTime) {
+ super(NodeTypeEnum.User);
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.userId = userId;
+ this.email = email;
+ this.role = role;
+ this.status = status;
+ this.lastLoginTime = lastLoginTime;
+ }
+
+ public UserData() {
+ super(NodeTypeEnum.User);
+ }
+
+ public UserData(Map<String, Object> properties) {
+ super(NodeTypeEnum.User);
+
+ setFirstName((String) properties.get("firstName"));
+ setLastName((String) properties.get("lastName"));
+ setUserId((String) properties.get("userId"));
+ setEmail((String) properties.get("email"));
+ setRole((String) properties.get("role"));
+ setStatus((String) properties.get("status"));
+ setLastLoginTime((Long) properties.get("lastLoginTime"));
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ // right name?
+ public void setLastLoginTime() {
+ Date d = new Date();
+ this.lastLoginTime = new Long(d.getTime()); // this is in milli-seconds
+ // divide by 1000 to get
+ // secs?
+ }
+
+ public void setLastLoginTime(Long time) {
+ this.lastLoginTime = time;
+ }
+
+ public Long getLastLoginTime() {
+ return this.lastLoginTime;
+ }
+
+ @Override
+ public String toString() {
+ return "UserData [firstName=" + firstName + ", lastName=" + lastName + ", userId=" + userId + ", email=" + email
+ + ", role=" + role + ", last Login time=" + lastLoginTime + ", parent: " + super.toString() + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((userId == null) ? 0 : userId.hashCode());
+ result = prime * result + ((email == null) ? 0 : email.hashCode());
+ result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
+ result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
+ result = prime * result + ((role == null) ? 0 : role.hashCode());
+ result = prime * result + ((lastLoginTime == null) ? 0 : lastLoginTime.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ UserData other = (UserData) obj;
+ if (userId == null) {
+ if (other.userId != null)
+ return false;
+ } else if (!userId.equals(other.userId))
+ return false;
+ if (email == null) {
+ if (other.email != null)
+ return false;
+ } else if (!email.equals(other.email))
+ return false;
+ if (firstName == null) {
+ if (other.firstName != null)
+ return false;
+ } else if (!firstName.equals(other.firstName))
+ return false;
+ if (lastName == null) {
+ if (other.lastName != null)
+ return false;
+ } else if (!lastName.equals(other.lastName))
+ return false;
+ if (role == null) {
+ if (other.role != null)
+ return false;
+ } else if (!role.equals(other.role))
+ return false;
+ if (lastLoginTime == null) {
+ if (other.lastLoginTime != null)
+ return false;
+ } else if (!lastLoginTime.equals(other.lastLoginTime))
+ return false;
+ return true;
+ }
+
+ public String toJson() {
+ return DaoUtils.convertToJson(toGraphMap());
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+ addIfExists(map, GraphPropertiesDictionary.USER_ID, userId);
+ addIfExists(map, GraphPropertiesDictionary.EMAIL, email);
+ addIfExists(map, GraphPropertiesDictionary.FIRST_NAME, firstName);
+ addIfExists(map, GraphPropertiesDictionary.LAST_NAME, lastName);
+ addIfExists(map, GraphPropertiesDictionary.ROLE, role);
+ addIfExists(map, GraphPropertiesDictionary.USER_STATUS, status);
+ addIfExists(map, GraphPropertiesDictionary.LAST_LOGIN_TIME, lastLoginTime);
+ return map;
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.USER_ID.getProperty();
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return userId;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserFunctionalMenuData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserFunctionalMenuData.java
new file mode 100644
index 0000000000..2a31f4bb68
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/UserFunctionalMenuData.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.utils.DaoUtils;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class UserFunctionalMenuData extends GraphNode {
+
+ private String uniqueId;
+
+ private String functionalMenu;
+
+ public UserFunctionalMenuData(String functionalMenu, String uniqueId) {
+ super(NodeTypeEnum.UserFunctionalMenu);
+ this.functionalMenu = functionalMenu;
+ this.uniqueId = uniqueId;
+ }
+
+ public UserFunctionalMenuData() {
+ super(NodeTypeEnum.UserFunctionalMenu);
+ }
+
+ public UserFunctionalMenuData(Map<String, Object> properties) {
+ super(NodeTypeEnum.UserFunctionalMenu);
+
+ setFunctionalMenu((String) properties.get(GraphPropertiesDictionary.FUNCTIONAL_MENU.getProperty()));
+ setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ }
+
+ public String getFunctionalMenu() {
+ return functionalMenu;
+ }
+
+ public void setFunctionalMenu(String functionalMenu) {
+ this.functionalMenu = functionalMenu;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public String toString() {
+ return "UserFunctionalMenuData [uniqueId=" + uniqueId + ", functionalMenu=" + functionalMenu + "]";
+ }
+
+ public String toJson() {
+ return DaoUtils.convertToJson(toGraphMap());
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId);
+ addIfExists(map, GraphPropertiesDictionary.FUNCTIONAL_MENU, functionalMenu);
+
+ return map;
+ }
+
+ @Override
+ public String getUniqueIdKey() {
+ return GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return uniqueId;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditRecordFactory.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditRecordFactory.java
new file mode 100644
index 0000000000..9e9836049f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditRecordFactory.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.util.EnumMap;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+public final class AuditRecordFactory {
+ public static AuditingGenericEvent createAuditRecord(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ AuditingActionEnum actionEnum = AuditingActionEnum
+ .getActionByName((String) auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION));
+ String tableName = actionEnum.getAuditingEsType();
+ AuditingGenericEvent event = null;
+ switch (tableName) {
+ case AuditingTypesConstants.USER_ADMIN_EVENT_TYPE:
+ event = new UserAdminEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.AUTH_EVENT_TYPE:
+ event = new AuthEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.CATEGORY_EVENT_TYPE:
+ event = new CategoryEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE:
+ event = new ResourceAdminEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.USER_ACCESS_EVENT_TYPE:
+ event = new UserAccessEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE:
+ event = new DistributionStatusEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_DOWNLOAD_EVENT_TYPE:
+ event = new DistributionDownloadEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE:
+ event = new DistributionEngineEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE:
+ event = new DistributionNotificationEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE:
+ event = new DistributionDeployEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE:
+ event = new AuditingGetUebClusterEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.CONSUMER_EVENT_TYPE:
+ event = new ConsumerEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.GET_USERS_LIST_EVENT_TYPE:
+ event = new GetUsersListEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.GET_CATEGORY_HIERARCHY_EVENT_TYPE:
+ event = new GetCategoryHierarchyEvent(auditingFields);
+ break;
+ case AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE:
+ event = new ExternalApiEvent(auditingFields);
+ break;
+ }
+
+ return event;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingActionEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingActionEnum.java
new file mode 100644
index 0000000000..8b0bd58d9e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingActionEnum.java
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public enum AuditingActionEnum {
+
+ // User admininstration
+ ADD_USER("AddUser", AuditingTypesConstants.USER_ADMIN_EVENT_TYPE),
+ UPDATE_USER("UpdateUser", AuditingTypesConstants.USER_ADMIN_EVENT_TYPE),
+ DELETE_USER("DeleteUser", AuditingTypesConstants.USER_ADMIN_EVENT_TYPE),
+ USER_ACCESS("Access", AuditingTypesConstants.USER_ACCESS_EVENT_TYPE),
+ GET_USERS_LIST("GetUsersList", AuditingTypesConstants.GET_USERS_LIST_EVENT_TYPE),
+
+ // Resource/service administration
+ CREATE_RESOURCE("Create", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ IMPORT_RESOURCE("ResourceImport", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ CHECKOUT_RESOURCE("Checkout", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ UNDO_CHECKOUT_RESOURCE("UndoCheckout", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ CHECKIN_RESOURCE("Checkin", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ CERTIFICATION_REQUEST_RESOURCE("CertificationRequest", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ START_CERTIFICATION_RESOURCE("CertificationStart", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ FAIL_CERTIFICATION_RESOURCE("CertificationFailure", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ CANCEL_CERTIFICATION_RESOURCE("CertificationCancel", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ CERTIFICATION_SUCCESS_RESOURCE("CertificationSuccess", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ UPDATE_RESOURCE_METADATA("UpdateResourceMetadata", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ UPDATE_SERVICE_METADATA("UpdateServiceMetadata", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ ARTIFACT_UPLOAD("ArtifactUpload", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ ARTIFACT_UPLOAD_BY_API("ArtifactUploadByAPI", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+ ARTIFACT_UPDATE_BY_API("ArtifactUpdateByAPI", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+ ARTIFACT_DELETE_BY_API("ArtifactDeleteByAPI", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+ ARTIFACT_PAYLOAD_UPDATE("ArtifactPayloadUpdate", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ ARTIFACT_METADATA_UPDATE("ArtifactMetadataUpdate", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ ARTIFACT_DELETE("ArtifactDelete", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ ARTIFACT_DOWNLOAD("ArtifactDownload", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ DOWNLOAD_ARTIFACT("DownloadArtifact",AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+
+ // Distribution
+ DISTRIBUTION_ARTIFACT_DOWNLOAD("DArtifactDownload", AuditingTypesConstants.DISTRIBUTION_DOWNLOAD_EVENT_TYPE),
+ DISTRIBUTION_STATE_CHANGE_REQUEST("DRequest", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ DISTRIBUTION_STATE_CHANGE_APPROV("DApprove", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ DISTRIBUTION_STATE_CHANGE_REJECT("DReject", AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE),
+ CREATE_DISTRIBUTION_TOPIC("CreateDistributionTopic", AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE),
+ ADD_KEY_TO_TOPIC_ACL("AddKeyToTopicACL", AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE),
+ REMOVE_KEY_FROM_TOPIC_ACL("RemoveKeyFromTopicACL", AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE),
+ DISTRIBUTION_REGISTER("DRegister", AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE),
+ DISTRIBUTION_UN_REGISTER("DUnRegister", AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE),
+ DISTRIBUTION_NOTIFY("DNotify", AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE),
+ DISTRIBUTION_STATUS("DStatus", AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE),
+ DISTRIBUTION_DEPLOY("DResult",AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE),
+ GET_UEB_CLUSTER("GetUebCluster", AuditingTypesConstants.DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE),
+ GET_VALID_ARTIFACT_TYPES("GetValidArtifactTypes", AuditingTypesConstants.DISTRIBUTION_GET_VALID_ARTIFACT_TYPES_EVENT_TYPE),
+ // ....
+ AUTH_REQUEST("HttpAuthentication", AuditingTypesConstants.AUTH_EVENT_TYPE),
+ ADD_ECOMP_USER_CREDENTIALS("AddECOMPUserCredentials", AuditingTypesConstants.CONSUMER_EVENT_TYPE),
+ GET_ECOMP_USER_CREDENTIALS("GetECOMPUserCredentials", AuditingTypesConstants.CONSUMER_EVENT_TYPE),
+ DELETE_ECOMP_USER_CREDENTIALS("DeleteECOMPUserCredentials", AuditingTypesConstants.CONSUMER_EVENT_TYPE),
+ UPDATE_ECOMP_USER_CREDENTIALS("UpdateECOMPUserCredentials", AuditingTypesConstants.CONSUMER_EVENT_TYPE),
+ // Category
+ ADD_CATEGORY("AddCategory", AuditingTypesConstants.CATEGORY_EVENT_TYPE),
+ ADD_SUB_CATEGORY("AddSubCategory", AuditingTypesConstants.CATEGORY_EVENT_TYPE),
+ ADD_GROUPING("AddGrouping", AuditingTypesConstants.CATEGORY_EVENT_TYPE),
+ GET_CATEGORY_HIERARCHY("GetCategoryHierarchy", AuditingTypesConstants.GET_CATEGORY_HIERARCHY_EVENT_TYPE),
+
+ // Assets
+
+ GET_ASSET_LIST("GetAssetList", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+ GET_FILTERED_ASSET_LIST("GetFilteredAssetList", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+ GET_ASSET_METADATA("GetAssetMetadata", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE),
+ GET_TOSCA_MODEL("GetToscaModel", AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE);
+
+ private String name;
+ // private Class<? extends AuditingGenericEvent> auditingEsType;
+ private String auditingEsType;
+
+ private static Logger log = LoggerFactory.getLogger(AuditingActionEnum.class.getName());
+
+ // AuditingActionEnum(String name, Class<? extends AuditingGenericEvent>
+ // auditingEsType){
+ // this.name = name;
+ // this.auditingEsType = auditingEsType;
+ // }
+
+ AuditingActionEnum(String name, String auditingEsType) {
+ this.name = name;
+ this.auditingEsType = auditingEsType;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ // public Class<? extends AuditingGenericEvent> getAuditingEsType(){
+ // return auditingEsType;
+ // }
+
+ public String getAuditingEsType() {
+ return auditingEsType;
+ }
+
+ public static AuditingActionEnum getActionByName(String name) {
+ AuditingActionEnum res = null;
+ AuditingActionEnum[] values = values();
+ for (AuditingActionEnum value : values) {
+ if (value.getName().equals(name)) {
+ res = value;
+ break;
+ }
+ }
+ if (res == null) {
+ log.debug("No auditing action is mapped to name {}", name);
+ }
+ return res;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGenericEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGenericEvent.java
new file mode 100644
index 0000000000..9e220bf8da
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGenericEvent.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+
+public class AuditingGenericEvent extends ESTimeBasedEvent {
+ protected String requestId;
+ protected String serviceInstanceId;
+ protected String action;
+ protected String status;
+
+ protected String desc;
+
+ // protected Map<String, Object> fields = new HashMap<String, Object>();
+
+ public AuditingGenericEvent() {
+ super();
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+ // public Map<String, Object> getFields() {
+ // return fields;
+ // }
+ //
+ // public void setFields(Map<String, Object> fields) {
+ // this.fields = fields;
+ // }
+
+ public void fillFields() {
+
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGetUebClusterEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGetUebClusterEvent.java
new file mode 100644
index 0000000000..bd8c032390
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingGetUebClusterEvent.java
@@ -0,0 +1,191 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE)
+public class AuditingGetUebClusterEvent extends AuditingGenericEvent {
+ private static String DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TEMPLATE = "action=\"%s\" "
+ + " consumerId=\"%s\" statusTime=\"%s\" status=\"%s\" status_desc=\"%s\" ";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "consumer_id")
+ private String consumerId;
+
+ public AuditingGetUebClusterEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public AuditingGetUebClusterEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ } else {
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID);
+ if (value != null) {
+ setConsumerId((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID.getDisplayName(), getConsumerId());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ @Override
+ public String toString() {
+ return "AuditingGetUebClusterEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1
+ + ", requestId=" + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action
+ + ", status=" + status + ", desc=" + desc + ", consumerId=" + consumerId + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingTypesConstants.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingTypesConstants.java
new file mode 100644
index 0000000000..01774ca63f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuditingTypesConstants.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+public interface AuditingTypesConstants {
+
+ public static final String ARTIFACT_KEYSPACE = "sdcartifact";
+ public static final String AUDIT_KEYSPACE = "sdcaudit";
+ public static final String COMPONENT_KEYSPACE = "sdccomponent";
+ public static final String TITAN_KEYSPACE = "titan";
+
+ public static final String USER_ADMIN_EVENT_TYPE = "useradminevent";
+ public static final String USER_ACCESS_EVENT_TYPE = "useraccessevent";
+ public static final String RESOURCE_ADMIN_EVENT_TYPE = "resourceadminevent";
+ public static final String DISTRIBUTION_DOWNLOAD_EVENT_TYPE = "distributiondownloadevent";
+
+ public static final String DISTRIBUTION_ENGINE_EVENT_TYPE = "distributionengineevent";
+ public static final String DISTRIBUTION_NOTIFICATION_EVENT_TYPE = "distributionnotificationevent";
+ public static final String DISTRIBUTION_STATUS_EVENT_TYPE = "distributionstatusevent";
+ public static final String DISTRIBUTION_DEPLOY_EVENT_TYPE = "distributiondeployevent";
+ public static final String DISTRIBUTION_GET_UEB_CLUSTER_EVENT_TYPE = "auditinggetuebclusterevent";
+ public static final String DISTRIBUTION_GET_VALID_ARTIFACT_TYPES_EVENT_TYPE = "auditinggetvalidartifacttypesevent";
+
+ public static final String AUTH_EVENT_TYPE = "authevent";
+ public static final String CONSUMER_EVENT_TYPE = "consumerevent";
+ public static final String CATEGORY_EVENT_TYPE = "categoryevent";
+ public static final String GET_USERS_LIST_EVENT_TYPE = "getuserslistevent";
+ public static final String GET_CATEGORY_HIERARCHY_EVENT_TYPE = "getcategoryhierarchyevent";
+ public static final String EXTERNAL_API_EVENT_TYPE = "externalapievent";
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuthEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuthEvent.java
new file mode 100644
index 0000000000..99a42e852e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/AuthEvent.java
@@ -0,0 +1,209 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = "sdcAudit", name = AuditingTypesConstants.AUTH_EVENT_TYPE)
+public class AuthEvent extends AuditingGenericEvent {
+
+ private static String AUTH_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "URL=\"%s\" USER=\"%s\" AUTH_STATUS=\"%s\" " + "REALM=\"%s\" status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column
+ private String url;
+ @Column
+ private String user;
+
+ @Column(name = "auth_status")
+ private String authStatus;
+
+ @Column
+ private String realm;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ public AuthEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getAuthStatus() {
+ return authStatus;
+ }
+
+ public void setAuthStatus(String authStatus) {
+ this.authStatus = authStatus;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = realm;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public AuthEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_AUTH_URL);
+ if (value != null) {
+ setUrl((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_AUTH_USER);
+ if (value != null) {
+ setUser((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_AUTH_STATUS);
+ if (value != null) {
+ setAuthStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_AUTH_REALM);
+ if (value != null) {
+ setRealm((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_URL.getDisplayName(), getUrl());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_USER.getDisplayName(), getUser());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_STATUS.getDisplayName(), getAuthStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_AUTH_REALM.getDisplayName(), getRealm());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/CategoryEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/CategoryEvent.java
new file mode 100644
index 0000000000..a970c84847
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/CategoryEvent.java
@@ -0,0 +1,240 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.CATEGORY_EVENT_TYPE)
+public class CategoryEvent extends AuditingGenericEvent {
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column
+ String action;
+ @Column
+ String status;
+ @Column(name = "description")
+ String desc;
+
+ @Column(name = "category_name")
+ String categoryName;
+
+ @Column(name = "sub_category_name")
+ String subCategoryName;
+
+ @Column(name = "grouping_name")
+ String groupingName;
+
+ @Column
+ String modifier;
+
+ @Column(name = "service_instance_id")
+ String serviceInstanceId;
+
+ @Column(name = "resource_type")
+ String resourceType;
+
+ @Column(name = "request_id")
+ String requestId;
+
+ public CategoryEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public CategoryEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_CATEGORY_NAME);
+ if (value != null) {
+ setCategoryName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SUB_CATEGORY_NAME);
+ if (value != null) {
+ setSubCategoryName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_GROUPING_NAME);
+ if (value != null) {
+ setGroupingName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE);
+ if (value != null) {
+ setResourceType((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_CATEGORY_NAME.getDisplayName(), getCategoryName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SUB_CATEGORY_NAME.getDisplayName(), getSubCategoryName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_GROUPING_NAME.getDisplayName(), getGroupingName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE.getDisplayName(), getResourceType());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+
+ public void setCategoryName(String categoryName) {
+ this.categoryName = categoryName;
+ }
+
+ public String getSubCategoryName() {
+ return subCategoryName;
+ }
+
+ public void setSubCategoryName(String subCategoryName) {
+ this.subCategoryName = subCategoryName;
+ }
+
+ public String getGroupingName() {
+ return groupingName;
+ }
+
+ public void setGroupingName(String groupingName) {
+ this.groupingName = groupingName;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ConsumerEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ConsumerEvent.java
new file mode 100644
index 0000000000..6c5bc3140d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ConsumerEvent.java
@@ -0,0 +1,184 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.CONSUMER_EVENT_TYPE)
+public class ConsumerEvent extends AuditingGenericEvent {
+ private static String CONSUMER_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "modifier=\"%s\" ecompUser=\"%s\" status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column
+ private String modifier;
+
+ @Column(name = "ecomp_user")
+ private String ecompUser;
+
+ public ConsumerEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public ConsumerEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ECOMP_USER);
+ if (value != null) {
+ setEcompUser((String) value);
+ }
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ECOMP_USER.getDisplayName(), getEcompUser());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getEcompUser() {
+ return ecompUser;
+ }
+
+ public void setEcompUser(String ecompUser) {
+ this.ecompUser = ecompUser;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ @Override
+ public String toString() {
+ return "ConsumerEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", action=" + action + ", status=" + status + ", desc=" + desc + ", modifier=" + modifier
+ + ", ecompUser=" + ecompUser + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDeployEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDeployEvent.java
new file mode 100644
index 0000000000..6ee320575c
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDeployEvent.java
@@ -0,0 +1,253 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.DISTRIBUTION_DEPLOY_EVENT_TYPE)
+public class DistributionDeployEvent extends AuditingGenericEvent {
+
+ private static String DISTRIBUTION_DEPLOY_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "resourceName=\"%s\" resourceType=\"%s\" currVersion=\"%s\" "
+ + "modifierName=\"%s\" modifierUid=\"%s\" did=\"%s\" " + "status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "resource_name")
+ private String resourceName;
+
+ @Column(name = "resource_type")
+ private String resourceType;
+
+ @Column(name = "curr_version")
+ private String currVersion;
+
+ @Column
+ private String modifier;
+
+ @Column
+ private String did;
+
+ public DistributionDeployEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public DistributionDeployEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID);
+ if (value != null) {
+ setDid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION);
+ if (value != null) {
+ setCurrVersion((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME);
+ if (value != null) {
+ setResourceName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE);
+ if (value != null) {
+ setResourceType((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID.getDisplayName(), getDid());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION.getDisplayName(), getCurrVersion());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME.getDisplayName(), getResourceName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE.getDisplayName(), getResourceType());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getCurrVersion() {
+ return currVersion;
+ }
+
+ public void setCurrVersion(String currVersion) {
+ this.currVersion = currVersion;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getDid() {
+ return did;
+ }
+
+ public void setDid(String did) {
+ this.did = did;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionDeployEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action + ", status=" + status
+ + ", desc=" + desc + ", resourceName=" + resourceName + ", resourceType=" + resourceType
+ + ", currVersion=" + currVersion + ", modifier=" + modifier + ", did=" + did + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDownloadEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDownloadEvent.java
new file mode 100644
index 0000000000..4b9fc0318f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionDownloadEvent.java
@@ -0,0 +1,205 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.DISTRIBUTION_DOWNLOAD_EVENT_TYPE)
+public class DistributionDownloadEvent extends AuditingGenericEvent {
+
+ private static String DISTRIBUTION_DOWNLOAD_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "consumerId=\"%s\" resourceUrl=\"%s\" status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "consumer_id")
+ private String consumerId;
+
+ @Column(name = "resource_url")
+ private String resourceUrl;
+
+ public DistributionDownloadEvent() {
+ super();
+
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public DistributionDownloadEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID);
+ if (value != null) {
+ setConsumerId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL);
+ if (value != null) {
+ setResourceUrl((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID.getDisplayName(), getConsumerId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL.getDisplayName(), getResourceUrl());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ public String getResourceUrl() {
+ return resourceUrl;
+ }
+
+ public void setResourceUrl(String resourceUrl) {
+ this.resourceUrl = resourceUrl;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionDownloadEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1
+ + ", requestId=" + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action
+ + ", status=" + status + ", desc=" + desc + ", consumerId=" + consumerId + ", resourceUrl="
+ + resourceUrl + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionEngineEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionEngineEvent.java
new file mode 100644
index 0000000000..b125e7a712
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionEngineEvent.java
@@ -0,0 +1,273 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.DISTRIBUTION_ENGINE_EVENT_TYPE)
+public class DistributionEngineEvent extends AuditingGenericEvent {
+
+ private static String DISTRIBUTION_ENGINE_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "environmentName=\"%s\" topicName=\"%s\" role=\"%s\" apiKey=\"%s\" " + "status=\"%s\" ";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "consumer_id")
+ private String consumerId;
+
+ @Column(name = "DSTATUS_TOPIC")
+ private String dstatusTopic;
+
+ @Column(name = "DNOTIF_TOPIC")
+ private String dnotifTopic;
+
+ @Column(name = "d_env")
+ private String environmentName;
+
+ @Column
+ private String role;
+
+ @Column(name = "api_key")
+ private String apiKey;
+
+ public DistributionEngineEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public DistributionEngineEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ } else {
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID);
+ if (value != null) {
+ setConsumerId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME);
+ if (value != null) {
+ setDstatusTopic((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME);
+ if (value != null) {
+ setDnotifTopic((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY);
+ if (value != null) {
+ setApiKey((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME);
+ if (value != null) {
+ setEnvironmentName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ROLE);
+ if (value != null) {
+ setRole((String) value);
+ }
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID.getDisplayName(), getConsumerId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_API_KEY.getDisplayName(), getApiKey());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ENVRIONMENT_NAME.getDisplayName(), getEnvironmentName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ROLE.getDisplayName(), getRole());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME.getDisplayName(), getDstatusTopic());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME.getDisplayName(),
+ getDnotifTopic());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getDstatusTopic() {
+ return dstatusTopic;
+ }
+
+ public void setDstatusTopic(String dstatusTopic) {
+ this.dstatusTopic = dstatusTopic;
+ }
+
+ public String getDnotifTopic() {
+ return dnotifTopic;
+ }
+
+ public void setDnotifTopic(String dnotifTopic) {
+ this.dnotifTopic = dnotifTopic;
+ }
+
+ public String getEnvironmentName() {
+ return environmentName;
+ }
+
+ public void setEnvironmentName(String environmentName) {
+ this.environmentName = environmentName;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getApiKey() {
+ return apiKey;
+ }
+
+ public void setApiKey(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionEngineEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action + ", status=" + status
+ + ", desc=" + desc + ", consumerId=" + consumerId + ", dstatusTopic=" + dstatusTopic + ", dnotifTopic="
+ + dnotifTopic + ", environmentName=" + environmentName + ", role=" + role + ", apiKey=" + apiKey + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionNotificationEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionNotificationEvent.java
new file mode 100644
index 0000000000..6420f08fb5
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionNotificationEvent.java
@@ -0,0 +1,286 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.DISTRIBUTION_NOTIFICATION_EVENT_TYPE)
+public class DistributionNotificationEvent extends AuditingGenericEvent {
+
+ private static String DISTRIBUTION_NOTIFICATION_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "resourceName=\"%s\" resourceType=\"%s\" currVersion=\"%s\" " + "modifierName=\"%s\" modifierUid=\"%s\" "
+ + "currState=\"%s\" distributionId=\"%s\" " + "topicName=\"%s\" status=\"%s\" description=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "resource_name")
+ private String resourceName;
+
+ @Column(name = "resource_type")
+ private String resourceType;
+
+ @Column(name = "curr_version")
+ private String currVersion;
+
+ @Column
+ private String modifier;
+
+ @Column(name = "curr_state")
+ private String currState;
+
+ @Column(name = "topic_name")
+ private String topicName;
+
+ @Column
+ private String did;
+
+ public DistributionNotificationEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public DistributionNotificationEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID);
+ if (value != null) {
+ setDid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE);
+ if (value != null) {
+ setCurrState((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION);
+ if (value != null) {
+ setCurrVersion((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME);
+ if (value != null) {
+ setResourceName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE);
+ if (value != null) {
+ setResourceType((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME);
+ if (value != null) {
+ setTopicName((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID.getDisplayName(), getDid());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE.getDisplayName(), getCurrState());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION.getDisplayName(), getCurrVersion());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME.getDisplayName(), getResourceName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE.getDisplayName(), getResourceType());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME.getDisplayName(), getTopicName());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getCurrVersion() {
+ return currVersion;
+ }
+
+ public void setCurrVersion(String currVersion) {
+ this.currVersion = currVersion;
+ }
+
+ public String getCurrState() {
+ return currState;
+ }
+
+ public void setCurrState(String currState) {
+ this.currState = currState;
+ }
+
+ public String getTopicName() {
+ return topicName;
+ }
+
+ public void setTopicName(String topicName) {
+ this.topicName = topicName;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getDid() {
+ return did;
+ }
+
+ public void setDid(String did) {
+ this.did = did;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionNotificationEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1
+ + ", requestId=" + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action
+ + ", status=" + status + ", desc=" + desc + ", resourceName=" + resourceName + ", resourceType="
+ + resourceType + ", currVersion=" + currVersion + ", modifier=" + modifier + ", currState=" + currState
+ + ", topicName=" + topicName + ", did=" + did + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionStatusEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionStatusEvent.java
new file mode 100644
index 0000000000..6565b824ac
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/DistributionStatusEvent.java
@@ -0,0 +1,254 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.DISTRIBUTION_STATUS_EVENT_TYPE)
+public class DistributionStatusEvent extends AuditingGenericEvent {
+
+ private static String DISTRIBUTION_STATUS_NOTIFICATION_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "did=\"%s\" consumerId=\"%s\" topicName=\"%s\" resoureURL=\"%s\" statusTime=\"%s\" status=\"%s\" ";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column
+ private String did;
+
+ @Column(name = "consumer_id")
+ private String consumerId;
+
+ @Column(name = "topic_name")
+ private String topicName;
+
+ @Column(name = "resoure_url")
+ private String resoureURL;
+
+ @Column(name = "status_time")
+ private String statusTime;
+
+ public DistributionStatusEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+
+ }
+
+ public DistributionStatusEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID);
+ if (value != null) {
+ setDid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID);
+ if (value != null) {
+ setConsumerId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME);
+ if (value != null) {
+ setTopicName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL);
+ if (value != null) {
+ setResoureURL((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME);
+ if (value != null) {
+ setStatusTime((String) value);
+ }
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID.getDisplayName(), getConsumerId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID.getDisplayName(), getDid());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME.getDisplayName(), getStatusTime());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME.getDisplayName(), getTopicName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL.getDisplayName(), getResoureURL());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+
+ }
+
+ public String getDid() {
+ return did;
+ }
+
+ public void setDid(String did) {
+ this.did = did;
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ public String getTopicName() {
+ return topicName;
+ }
+
+ public void setTopicName(String topicName) {
+ this.topicName = topicName;
+ }
+
+ public String getResoureURL() {
+ return resoureURL;
+ }
+
+ public void setResoureURL(String resoureURL) {
+ this.resoureURL = resoureURL;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp) {
+ this.timestamp1 = timestamp;
+ }
+
+ public String getStatusTime() {
+ return statusTime;
+ }
+
+ public void setStatusTime(String statusTime) {
+ this.statusTime = statusTime;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionStatusEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action + ", status=" + status
+ + ", desc=" + desc + ", did=" + did + ", consumerId=" + consumerId + ", topicName=" + topicName
+ + ", resoureURL=" + resoureURL + ", statusTime=" + statusTime + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ExternalApiEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ExternalApiEvent.java
new file mode 100644
index 0000000000..027c57c5c9
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ExternalApiEvent.java
@@ -0,0 +1,279 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.EXTERNAL_API_EVENT_TYPE)
+public class ExternalApiEvent extends AuditingGenericEvent {
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn()
+ protected Date timestamp1;
+
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column(name = "consumer_id")
+ private String consumerId;
+
+ @Column(name = "resource_url")
+ private String resourceURL;
+
+ @Column(name = "resource_name")
+ private String resourceName;
+
+ @Column(name = "resource_type")
+ private String resourceType;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+
+ @Column(name = "modifier")
+ private String modifier;
+
+ @Column(name = "prev_artifact_uuid")
+ private String prevArtifactUuid;
+
+ @Column(name = "curr_artifact_uuid")
+ private String currArtifactUuid;
+
+ @Column(name = "artifact_data")
+ private String artifactData;
+
+ public ExternalApiEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public ExternalApiEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID);
+ if (value != null) {
+ setConsumerId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL);
+ if (value != null) {
+ setResourceURL((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME);
+ if (value != null) {
+ setResourceName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE);
+ if (value != null) {
+ setResourceType((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID);
+ if (value != null) {
+ setPrevArtifactUuid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID);
+ if (value != null) {
+ setCurrArtifactUuid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA);
+ if (value != null) {
+ setArtifactData((String) value);
+ }
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID.getDisplayName(), getConsumerId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL.getDisplayName(), getResourceURL());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME.getDisplayName(), getResourceName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE.getDisplayName(), getResourceType());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID.getDisplayName(), getPrevArtifactUuid());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID.getDisplayName(), getCurrArtifactUuid());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA.getDisplayName(), getArtifactData());
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ public String getResourceURL() {
+ return resourceURL;
+ }
+
+ public void setResourceURL(String resourceURL) {
+ this.resourceURL = resourceURL;
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getPrevArtifactUuid() {
+ return prevArtifactUuid;
+ }
+
+ public void setPrevArtifactUuid(String prevArtifactUuid) {
+ this.prevArtifactUuid = prevArtifactUuid;
+ }
+
+ public String getCurrArtifactUuid() {
+ return currArtifactUuid;
+ }
+
+ public void setCurrArtifactUuid(String currArtifactUuid) {
+ this.currArtifactUuid = currArtifactUuid;
+ }
+
+ public String getArtifactData() {
+ return artifactData;
+ }
+
+ public void setArtifactData(String artifactData) {
+ this.artifactData = artifactData;
+ }
+
+ @Override
+ public String toString() {
+ return "ExternalApiEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", action=" + action
+ + ", status=" + status + ", desc=" + desc + ", consumerId=" + consumerId + ", resourceURL="
+ + resourceURL + ", resourceName=" + resourceName + ", resourceType=" + resourceType
+ + ", serviceInstanceId=" + serviceInstanceId + ", modifier=" + modifier + ", prevArtifactUuid="
+ + prevArtifactUuid + ", currArtifactUuid=" + currArtifactUuid + ", artifactData=" + artifactData + "]";
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetCategoryHierarchyEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetCategoryHierarchyEvent.java
new file mode 100644
index 0000000000..0b86651b8a
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetCategoryHierarchyEvent.java
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.GET_CATEGORY_HIERARCHY_EVENT_TYPE)
+public class GetCategoryHierarchyEvent extends AuditingGenericEvent {
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn()
+ // @Column(name="timestamp")
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column
+ private String modifier;
+
+ @Column
+ private String details;
+
+ public GetCategoryHierarchyEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public GetCategoryHierarchyEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DETAILS);
+ if (value != null) {
+ setDetails((String) value);
+ }
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DETAILS.getDisplayName(), getDetails());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+ @Override
+ public String toString() {
+ return "GetCategoryHierarchyEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1
+ + ", requestId=" + requestId + ", action=" + action + ", status=" + status + ", desc=" + desc
+ + ", modifier=" + modifier + ", details=" + details + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetUsersListEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetUsersListEvent.java
new file mode 100644
index 0000000000..728a0430b0
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/GetUsersListEvent.java
@@ -0,0 +1,186 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.GET_USERS_LIST_EVENT_TYPE)
+public class GetUsersListEvent extends AuditingGenericEvent {
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "request_id")
+ protected String requestId;
+ @Column
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column
+ private String modifier;
+
+ @Column
+ private String details;
+
+ public GetUsersListEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public GetUsersListEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_USER_DETAILS);
+ if (value != null) {
+ setDetails((String) value);
+ } else {
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DETAILS);
+ if (value != null) {
+ setDetails((String) value);
+ }
+ }
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_USER_DETAILS.getDisplayName(), getDetails());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+
+ public void setDetails(String details) {
+ this.details = details;
+ }
+
+ @Override
+ public String toString() {
+ return "GetUsersListEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", action=" + action + ", status=" + status + ", desc=" + desc + ", modifier=" + modifier
+ + ", details=" + details + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ResourceAdminEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ResourceAdminEvent.java
new file mode 100644
index 0000000000..21a84751da
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/ResourceAdminEvent.java
@@ -0,0 +1,437 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.RESOURCE_ADMIN_EVENT_TYPE)
+public class ResourceAdminEvent extends AuditingGenericEvent {
+
+ private static String RESOURCE_ADMIN_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "resourceName=\"%s\" resourceType=\"%s\" prevVersion=\"%s\" currVersion=\"%s\" "
+ + "modifierName=\"%s\" modifierUid=\"%s\" " + "prevState=\"%s\" currState=\"%s\" "
+ + "checkinComment=\"%s\" prevArtifactUuid=\"%s\" currArtifactUuid=\"%s\" " + "artifactData=\"%s\" "
+ + "status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column
+ protected String action;
+
+ @Column(name = "resource_type")
+ protected String resourceType;
+
+ @Column(name = "prev_version")
+ protected String prevVersion;
+
+ @Column(name = "prev_state")
+ protected String prevState;
+
+ @Column(name = "curr_state")
+ protected String currState;
+
+ @Column(name = "resource_name")
+ private String resourceName;
+
+ @Column(name = "curr_version")
+ private String currVersion;
+
+ @Column(name = "request_id")
+ protected String requestId;
+
+ @Column(name = "service_instance_id")
+ protected String serviceInstanceId;
+
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column
+ protected String modifier;
+
+ @Column(name = "prev_artifact_UUID")
+ protected String prevArtifactUUID;
+
+ @Column(name = "curr_artifact_UUID")
+ protected String currArtifactUUID;
+
+ @Column(name = "artifact_data")
+ protected String artifactData;
+
+ @Column
+ protected String did;
+
+ @Column(name = "dprev_status")
+ protected String dprevStatus;
+
+ @Column(name = "dcurr_status")
+ protected String dcurrStatus;
+
+ @Column(name = "tosca_node_type")
+ protected String toscaNodeType;
+
+ @Column
+ protected String comment;
+
+ @Column(name = "invariant_UUID")
+ protected String invariantUUID;
+
+ public ResourceAdminEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public ResourceAdminEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE);
+ if (value != null) {
+ setResourceType((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION);
+ if (value != null) {
+ setPrevVersion((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE);
+ if (value != null) {
+ setPrevState((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME);
+ if (value != null) {
+ setResourceName((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION);
+ if (value != null) {
+ setCurrVersion((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE);
+ if (value != null) {
+ setCurrState((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID);
+ if (value != null) {
+ setPrevArtifactUUID((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID);
+ if (value != null) {
+ setCurrArtifactUUID((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA);
+ if (value != null) {
+ setArtifactData((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT);
+ if (value != null) {
+ setComment((String) value);
+ }
+
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS);
+ if (value != null) {
+ setDcurrStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS);
+ if (value != null) {
+ setDprevStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID);
+ if (value != null) {
+ setDid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE);
+ if (value != null) {
+ setToscaNodeType((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID);
+ if (value != null) {
+ setInvariantUUID((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE.getDisplayName(), getResourceType());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION.getDisplayName(), getPrevVersion());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE.getDisplayName(), getPrevState());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME.getDisplayName(), getResourceName());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION.getDisplayName(), getCurrVersion());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE.getDisplayName(), getCurrState());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_PREV_ARTIFACT_UUID.getDisplayName(), getPrevArtifactUUID());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_CURR_ARTIFACT_UUID.getDisplayName(), getCurrArtifactUUID());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ARTIFACT_DATA.getDisplayName(), getArtifactData());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_COMMENT.getDisplayName(), getComment());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID.getDisplayName(), getDid());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DCURR_STATUS.getDisplayName(), getDcurrStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_DPREV_STATUS.getDisplayName(), getDprevStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TOSCA_NODE_TYPE.getDisplayName(), getToscaNodeType());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_INVARIANT_UUID.getDisplayName(), getInvariantUUID());
+
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public void setResourceName(String resourceName) {
+ this.resourceName = resourceName;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public String getPrevVersion() {
+ return prevVersion;
+ }
+
+ public void setPrevVersion(String prevVersion) {
+ this.prevVersion = prevVersion;
+ }
+
+ public String getCurrVersion() {
+ return currVersion;
+ }
+
+ public void setCurrVersion(String currVersion) {
+ this.currVersion = currVersion;
+ }
+
+ public String getPrevState() {
+ return prevState;
+ }
+
+ public void setPrevState(String prevState) {
+ this.prevState = prevState;
+ }
+
+ public String getCurrState() {
+ return currState;
+ }
+
+ public void setCurrState(String currState) {
+ this.currState = currState;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp1) {
+ this.timestamp1 = timestamp1;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getPrevArtifactUUID() {
+ return prevArtifactUUID;
+ }
+
+ public void setPrevArtifactUUID(String prevArtifactUUID) {
+ this.prevArtifactUUID = prevArtifactUUID;
+ }
+
+ public String getCurrArtifactUUID() {
+ return currArtifactUUID;
+ }
+
+ public void setCurrArtifactUUID(String currArtifactUUID) {
+ this.currArtifactUUID = currArtifactUUID;
+ }
+
+ public String getArtifactData() {
+ return artifactData;
+ }
+
+ public void setArtifactData(String artifactData) {
+ this.artifactData = artifactData;
+ }
+
+ public String getDid() {
+ return did;
+ }
+
+ public void setDid(String did) {
+ this.did = did;
+ }
+
+ public String getDprevStatus() {
+ return dprevStatus;
+ }
+
+ public void setDprevStatus(String dprevStatus) {
+ this.dprevStatus = dprevStatus;
+ }
+
+ public String getDcurrStatus() {
+ return dcurrStatus;
+ }
+
+ public void setDcurrStatus(String dcurrStatus) {
+ this.dcurrStatus = dcurrStatus;
+ }
+
+ public String getToscaNodeType() {
+ return toscaNodeType;
+ }
+
+ public void setToscaNodeType(String toscaNodeType) {
+ this.toscaNodeType = toscaNodeType;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceAdminEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", action="
+ + action + ", resourceType=" + resourceType + ", prevVersion=" + prevVersion + ", prevState="
+ + prevState + ", currState=" + currState + ", resourceName=" + resourceName + ", currVersion="
+ + currVersion + ", requestId=" + requestId + ", serviceInstanceId=" + serviceInstanceId + ", status="
+ + status + ", desc=" + desc + ", modifier=" + modifier + ", prevArtifactUUID=" + prevArtifactUUID
+ + ", currArtifactUUID=" + currArtifactUUID + ", artifactData=" + artifactData + ", invariantUUID="
+ + invariantUUID + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAccessEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAccessEvent.java
new file mode 100644
index 0000000000..a1ae1e2df1
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAccessEvent.java
@@ -0,0 +1,179 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.USER_ACCESS_EVENT_TYPE)
+public class UserAccessEvent extends AuditingGenericEvent {
+ private static String USER_ACCESS_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" "
+ + "userUid=\"%s\" userName=\"%s\" status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "REQUEST_ID")
+ protected String requestId;
+
+ @Column(name = "USER")
+ private String userUid;
+
+ @Column
+ private String status;
+
+ @Column(name = "DESCRIPTION")
+ private String desc;
+
+ @Column
+ private String action;
+
+ @Column(name = "service_instance_id")
+ private String serviceInstanceId;
+
+ public UserAccessEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+ }
+
+ public UserAccessEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_USER_UID);
+ if (value != null) {
+ setUserUid((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_USER_UID.getDisplayName(), getUserUid());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getUserUid() {
+ return userUid;
+ }
+
+ public void setUserUid(String userUid) {
+ this.userUid = userUid;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp) {
+ this.timestamp1 = timestamp;
+ }
+
+ @Override
+ public String toString() {
+ return "UserAccessEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", userUid=" + userUid + ", status=" + status + ", desc=" + desc + ", action=" + action
+ + ", serviceInstanceId=" + serviceInstanceId + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAdminEvent.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAdminEvent.java
new file mode 100644
index 0000000000..72d7d2ff6d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/auditing/UserAdminEvent.java
@@ -0,0 +1,224 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.auditing;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.EnumMap;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+
+@Table(keyspace = AuditingTypesConstants.AUDIT_KEYSPACE, name = AuditingTypesConstants.USER_ADMIN_EVENT_TYPE)
+public class UserAdminEvent extends AuditingGenericEvent {
+
+ private static String USER_ADMIN_EVENT_TEMPLATE = "action=\"%s\" timestamp=\"%s\" modifierName=\"%s\" modifierUid=\"%s\" "
+ + "userUid=\"%s\" userName=\"%s\" userEmail=\"%s\" userRole=\"%s\" "
+ + "userBeforeUid=\"%s\" userBeforeName=\"%s\" userBeforeEmail=\"%s\" userBeforeRole=\"%s\" "
+ + "userAfterUid=\"%s\" userAfterName=\"%s\" userAfterEmail=\"%s\" userAfterRole=\"%s\" "
+ + "status=\"%s\" desc=\"%s\"";
+
+ @PartitionKey
+ protected UUID timebaseduuid;
+
+ @ClusteringColumn
+ protected Date timestamp1;
+
+ @Column(name = "REQUEST_ID")
+ protected String requestId;
+
+ @Column(name = "SERVICE_INSTANCE_ID")
+ protected String serviceInstanceId;
+
+ @Column(name = "ACTION")
+ protected String action;
+ @Column
+ protected String status;
+
+ @Column(name = "description")
+ protected String desc;
+
+ @Column
+ private String modifier;
+
+ @Column(name = "user_before")
+ private String userBefore;
+
+ @Column(name = "user_after")
+ private String userAfter;
+
+ public UserAdminEvent() {
+ super();
+ timestamp1 = new Date();
+ timebaseduuid = UUIDs.timeBased();
+
+ }
+
+ public UserAdminEvent(EnumMap<AuditingFieldsKeysEnum, Object> auditingFields) {
+ this();
+ Object value;
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID);
+ if (value != null) {
+ setRequestId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_STATUS);
+ if (value != null) {
+ setStatus((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID);
+ if (value != null) {
+ setModifier((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_ACTION);
+ if (value != null) {
+ setAction((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID);
+ if (value != null) {
+ setServiceInstanceId((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_DESC);
+ if (value != null) {
+ setDesc((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_USER_BEFORE);
+ if (value != null) {
+ setUserBefore((String) value);
+ }
+ value = auditingFields.get(AuditingFieldsKeysEnum.AUDIT_USER_AFTER);
+ if (value != null) {
+ setUserAfter((String) value);
+ }
+
+ }
+
+ @Override
+ public void fillFields() {
+ fields.put(AuditingFieldsKeysEnum.AUDIT_REQUEST_ID.getDisplayName(), getRequestId());
+
+ fields.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID.getDisplayName(), getServiceInstanceId());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_ACTION.getDisplayName(), getAction());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_STATUS.getDisplayName(), getStatus());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_DESC.getDisplayName(), getDesc());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_USER_BEFORE.getDisplayName(), getUserBefore());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_USER_AFTER.getDisplayName(), getUserAfter());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID.getDisplayName(), getModifier());
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), simpleDateFormat.format(timestamp1));
+ }
+
+ public String getModifier() {
+ return modifier;
+ }
+
+ public void setModifier(String modifier) {
+ this.modifier = modifier;
+ }
+
+ public String getUserBefore() {
+ return userBefore;
+ }
+
+ public void setUserBefore(String userBeforeName) {
+ this.userBefore = userBeforeName;
+ }
+
+ public String getUserAfter() {
+ return userAfter;
+ }
+
+ public void setUserAfter(String userAfterName) {
+ this.userAfter = userAfterName;
+ }
+
+ public String getRequestId() {
+ return requestId;
+ }
+
+ public void setRequestId(String requestId) {
+ this.requestId = requestId;
+ }
+
+ public String getServiceInstanceId() {
+ return serviceInstanceId;
+ }
+
+ public void setServiceInstanceId(String serviceInstanceId) {
+ this.serviceInstanceId = serviceInstanceId;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public UUID getTimebaseduuid() {
+ return timebaseduuid;
+ }
+
+ public void setTimebaseduuid(UUID timebaseduuid) {
+ this.timebaseduuid = timebaseduuid;
+ }
+
+ public Date getTimestamp1() {
+ return timestamp1;
+ }
+
+ public void setTimestamp1(Date timestamp) {
+ this.timestamp1 = timestamp;
+ }
+
+ @Override
+ public String toString() {
+ return "UserAdminEvent [timebaseduuid=" + timebaseduuid + ", timestamp1=" + timestamp1 + ", requestId="
+ + requestId + ", serviceInstanceId=" + serviceInstanceId + ", action=" + action + ", status=" + status
+ + ", desc=" + desc + ", modifierUid=" + modifier + ", userBefore=" + userBefore + ", userAfter="
+ + userAfter + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/CategoryData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/CategoryData.java
new file mode 100644
index 0000000000..d42eb78ed3
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/CategoryData.java
@@ -0,0 +1,90 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.category;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.category.CategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class CategoryData extends GraphNode {
+ private CategoryDataDefinition categoryDataDefinition;
+
+ public CategoryData(NodeTypeEnum label) {
+ super(label);
+ categoryDataDefinition = new CategoryDataDefinition();
+ }
+
+ public CategoryData(NodeTypeEnum label, CategoryDataDefinition categoryDataDefinition) {
+ super(label);
+ this.categoryDataDefinition = categoryDataDefinition;
+ }
+
+ public CategoryData(Map<String, Object> properties) {
+ this(NodeTypeEnum.getByName((String) properties.get(GraphPropertiesDictionary.LABEL.getProperty())));
+
+ categoryDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ categoryDataDefinition
+ .setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
+ categoryDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> iconsfromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.ICONS.getProperty()), listType);
+ categoryDataDefinition.setIcons(iconsfromJson);
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return categoryDataDefinition.getUniqueId();
+ }
+
+ public CategoryDataDefinition getCategoryDataDefinition() {
+ return categoryDataDefinition;
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, categoryDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.NAME, categoryDataDefinition.getName());
+ addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, categoryDataDefinition.getNormalizedName());
+ // String icons=getGson().toJson(categoryDataDefinition.getIcons());
+ // addIfExists(map, GraphPropertiesDictionary.ICONS, icons);
+ addIfExists(map, GraphPropertiesDictionary.ICONS, categoryDataDefinition.getIcons());
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return "CategoryData [categoryDataDefinition=" + categoryDataDefinition + "]";
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/GroupingData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/GroupingData.java
new file mode 100644
index 0000000000..ca40ae986b
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/GroupingData.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.category;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.category.GroupingDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public class GroupingData extends GraphNode {
+
+ private GroupingDataDefinition groupingDataDefinition;
+
+ public GroupingData(NodeTypeEnum label) {
+ super(label);
+ groupingDataDefinition = new GroupingDataDefinition();
+ }
+
+ public GroupingData(NodeTypeEnum label, GroupingDataDefinition groupingDataDefinition) {
+ super(label);
+ this.groupingDataDefinition = groupingDataDefinition;
+ }
+
+ public GroupingData(Map<String, Object> properties) {
+ this(NodeTypeEnum.getByName((String) properties.get(GraphPropertiesDictionary.LABEL.getProperty())));
+
+ groupingDataDefinition.setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ groupingDataDefinition
+ .setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
+ groupingDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+ }
+
+ public GroupingDataDefinition getGroupingDataDefinition() {
+ return groupingDataDefinition;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return groupingDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, groupingDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.NAME, groupingDataDefinition.getName());
+ addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, groupingDataDefinition.getNormalizedName());
+ return map;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/SubCategoryData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/SubCategoryData.java
new file mode 100644
index 0000000000..1112b4514d
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/category/SubCategoryData.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.data.category;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.category.SubCategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.google.gson.reflect.TypeToken;
+
+public class SubCategoryData extends GraphNode {
+
+ private SubCategoryDataDefinition subCategoryDataDefinition;
+
+ public SubCategoryData(NodeTypeEnum label) {
+ super(label);
+ subCategoryDataDefinition = new SubCategoryDataDefinition();
+ }
+
+ public SubCategoryData(NodeTypeEnum label, SubCategoryDataDefinition subCategoryDataDefinition) {
+ super(label);
+ this.subCategoryDataDefinition = subCategoryDataDefinition;
+ }
+
+ public SubCategoryData(Map<String, Object> properties) {
+ this(NodeTypeEnum.getByName((String) properties.get(GraphPropertiesDictionary.LABEL.getProperty())));
+
+ subCategoryDataDefinition
+ .setUniqueId((String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()));
+ subCategoryDataDefinition
+ .setNormalizedName((String) properties.get(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty()));
+ subCategoryDataDefinition.setName((String) properties.get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ Type listType = new TypeToken<List<String>>() {
+ }.getType();
+ List<String> iconsfromJson = getGson()
+ .fromJson((String) properties.get(GraphPropertiesDictionary.ICONS.getProperty()), listType);
+ subCategoryDataDefinition.setIcons(iconsfromJson);
+ }
+
+ public SubCategoryDataDefinition getSubCategoryDataDefinition() {
+ return subCategoryDataDefinition;
+ }
+
+ @Override
+ public Object getUniqueId() {
+ return subCategoryDataDefinition.getUniqueId();
+ }
+
+ @Override
+ public Map<String, Object> toGraphMap() {
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, subCategoryDataDefinition.getUniqueId());
+ addIfExists(map, GraphPropertiesDictionary.NAME, subCategoryDataDefinition.getName());
+ addIfExists(map, GraphPropertiesDictionary.NORMALIZED_NAME, subCategoryDataDefinition.getNormalizedName());
+ // String icons=getGson().toJson(subCategoryDataDefinition.getIcons());
+ // addIfExists(map, GraphPropertiesDictionary.ICONS, icons);
+ addIfExists(map, GraphPropertiesDictionary.ICONS, subCategoryDataDefinition.getIcons());
+ return map;
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/DeleteDeployedException.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/DeleteDeployedException.java
new file mode 100644
index 0000000000..f0d320114f
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/DeleteDeployedException.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.exception;
+
+import org.openecomp.sdc.exception.TechnicalException;
+
+/**
+ * Thrown when a deployed environment / topology is about to be deleted
+ *
+ * @author mourouvi
+ *
+ */
+public class DeleteDeployedException extends TechnicalException {
+
+ private static final long serialVersionUID = -5192834855057177252L;
+
+ public DeleteDeployedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DeleteDeployedException(String message) {
+ super(message);
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/InitializationException.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/InitializationException.java
new file mode 100644
index 0000000000..9c4337554e
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/InitializationException.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.exception;
+
+import org.openecomp.sdc.exception.TechnicalException;
+
+/**
+ * Exception to be thrown in case of an error that prevents alien to startup.
+ */
+public class InitializationException extends TechnicalException {
+ public InitializationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/ResourceDAOException.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/ResourceDAOException.java
new file mode 100644
index 0000000000..cae5c47699
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/exception/ResourceDAOException.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.exception;
+
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.exception.TechnicalException;
+
+public class ResourceDAOException extends TechnicalException {
+
+ private static final long serialVersionUID = 171917520842336653L;
+
+ private ResourceUploadStatus status;
+
+ public ResourceDAOException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ResourceDAOException(String message) {
+ super(message);
+ }
+
+ public ResourceDAOException(ResourceUploadStatus status, String message, Throwable cause) {
+ super(message, cause);
+ this.status = status;
+ }
+
+ public ResourceDAOException(ResourceUploadStatus status, String message) {
+ super(message);
+ this.status = status;
+ }
+
+ public ResourceUploadStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ResourceUploadStatus status) {
+ this.status = status;
+ }
+
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/impl/ResourceUploader.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/impl/ResourceUploader.java
new file mode 100644
index 0000000000..1570ff1ed1
--- /dev/null
+++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/impl/ResourceUploader.java
@@ -0,0 +1,206 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources.impl;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ICatalogDAO;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.resources.api.IResourceUploader;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.be.resources.exception.ResourceDAOException;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("resource-upload")
+public class ResourceUploader implements IResourceUploader {
+
+ private static final String DEFAULT_ARTIFACT_INDEX_NAME = "resources";
+
+ @Resource
+ private ICatalogDAO resourceDAO;
+ private static Logger log = LoggerFactory.getLogger(ResourceUploader.class.getName());
+
+ @PostConstruct
+ public void init() {
+ ConfigurationManager configMgr = ConfigurationManager.getConfigurationManager();
+ String artifactsIndex = null;
+ artifactsIndex = configMgr.getConfiguration().getArtifactsIndex();
+ if (artifactsIndex == null || artifactsIndex.isEmpty()) {
+ artifactsIndex = DEFAULT_ARTIFACT_INDEX_NAME;
+ }
+ resourceDAO.addToIndicesMap(ESArtifactData.class.getSimpleName().toLowerCase(), artifactsIndex);
+ }
+
+ public ResourceUploader() {
+ super();
+ }
+
+ public ResourceUploader(ICatalogDAO resourcetDAO) {
+ super();
+ this.resourceDAO = resourcetDAO;
+ }
+
+ public ICatalogDAO getResourceDAO() {
+ return resourceDAO;
+ }
+
+ public void setResourceDAO(ICatalogDAO resourceDAO) {
+ this.resourceDAO = resourceDAO;
+ }
+
+ @Override
+ public ResourceUploadStatus saveArtifact(ESArtifactData artifactData, boolean isReload) {
+ ResourceUploadStatus status = ResourceUploadStatus.OK;
+ if (resourceDAO == null) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeInitializationError,
+ "Save Artifact - internal object not initialized");
+ BeEcompErrorManager.getInstance()
+ .logBeInitializationError("Save Artifact - internal object not initialized");
+ log.debug("update artifact failed - resourceDAO is null");
+ return ResourceUploadStatus.ERROR;
+ }
+
+ Either<ESArtifactData, ResourceUploadStatus> getArtifactStatus = getArtifact(artifactData.getId());
+ if (getArtifactStatus.isLeft()) {
+ status = ResourceUploadStatus.ALREADY_EXIST;
+ log.debug("ResourceUploadStatus:saveArtifact artifact with id {} already exist.", artifactData.getId());
+ if (isReload) {
+ status = updateArtifact(artifactData, getArtifactStatus.left().value());
+ }
+ } else {
+ try {
+
+ resourceDAO.writeArtifact(artifactData);
+ status = ResourceUploadStatus.OK;
+
+ } catch (ResourceDAOException e) {
+ status = ResourceUploadStatus.ERROR;
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError,
+ "Save Artifact to database");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Save Artifact to database");
+ log.debug("ResourceUploadStatus:saveArtifact failed with exception ", e);
+ }
+
+ }
+
+ return status;
+ }
+
+ @Override
+ public ResourceUploadStatus updateArtifact(ESArtifactData artifactUpdateData) {
+ ResourceUploadStatus status = ResourceUploadStatus.OK;
+ if (resourceDAO == null)
+ return ResourceUploadStatus.ERROR;
+
+ Either<ESArtifactData, ResourceUploadStatus> getArtifactStatus = getArtifact(artifactUpdateData.getId());
+ if (getArtifactStatus.isRight()) {
+ status = getArtifactStatus.right().value();
+ log.debug("ResourceUploadStatus:updateArtifactt artifact with id {}", artifactUpdateData.getId()
+ + " not exist.");
+ }
+ if (getArtifactStatus.isLeft()) {
+ status = updateArtifact(artifactUpdateData, getArtifactStatus.left().value());
+ }
+
+ return status;
+ }
+
+ /*
+ * @Override public ResourceUploadStatus
+ * updateServiceArtifact(ServiceArtifactData artifactUpdateData) {
+ * ResourceUploadStatus status = ResourceUploadStatus.OK; if(resourceDAO ==
+ * null) return ResourceUploadStatus.ERROR; Either<ServiceArtifactData,
+ * ResourceUploadStatus> getServiceArtifactStatus =
+ * getServiceArtifact(artifactUpdateData.getId());
+ *
+ * if(getServiceArtifactStatus.isRight()){
+ * log.debug("ResourceUploadStatus:updateArtifactt artifact with id {} not exist.",
+ * artifactUpdateData.getId()); status =
+ * getServiceArtifactStatus.right().value(); }
+ * if(getServiceArtifactStatus.isLeft()){ status =
+ * updateServiceArtifact(artifactUpdateData,
+ * getServiceArtifactStatus.left().value()); }
+ *
+ * return status; }
+ *
+ */
+
+ @Override
+ public Either<ESArtifactData, ResourceUploadStatus> getArtifact(String id) {
+ if (resourceDAO == null)
+ return Either.right(ResourceUploadStatus.ERROR);
+
+ return resourceDAO.getArtifact(id);
+ }
+
+ /*
+ * @Override public Either<ServiceArtifactData, ResourceUploadStatus>
+ * getServiceArtifact(String id) { if(resourceDAO == null) return
+ * Either.right(ResourceUploadStatus.ERROR);
+ *
+ * return resourceDAO.getServiceArtifact(id); }
+ */
+ @Override
+ public void deleteArtifact(String id) {
+ if (resourceDAO != null) {
+ resourceDAO.deleteArtifact(id);
+ }
+
+ }
+
+ private ResourceUploadStatus updateArtifact(ESArtifactData artifactUpdateData, ESArtifactData existData) {
+ ResourceUploadStatus status;
+
+ updateData(artifactUpdateData, existData);
+
+ try {
+ resourceDAO.writeArtifact(artifactUpdateData);
+ status = ResourceUploadStatus.OK;
+
+ } catch (ResourceDAOException e) {
+ status = ResourceUploadStatus.ERROR;
+ log.debug("ResourceUploadStatus:updateArtifact failed with exception ", e);
+ }
+ return status;
+ }
+
+ private void updateData(ESArtifactData artifactUpdateData, ESArtifactData existData) {
+
+ if (artifactUpdateData.getData() == null) {
+ artifactUpdateData.setData(existData.getData());
+ }
+
+ }
+
+ @Override
+ public void deleteAllArtifacts() {
+ resourceDAO.deleteAllArtifacts();
+ }
+
+}
diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ArtifactDaoTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ArtifactDaoTest.java
new file mode 100644
index 0000000000..aa9d30cb12
--- /dev/null
+++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ArtifactDaoTest.java
@@ -0,0 +1,577 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import javax.annotation.Resource;
+
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.IGenericSearchDAO;
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.be.resources.api.IResourceUploader;
+import org.openecomp.sdc.be.resources.data.ESArtifactData;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
+import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
+ DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class }) // ,
+ // CassandraUnitTestExecutionListener.class})
+// @EmbeddedCassandra(host ="localhost", port=9042)
+public class ArtifactDaoTest {
+ private static final String TEST_IMAGES_DIRECTORY = "src/test/resources/images";
+
+ @Resource
+ ElasticSearchClient esclient;
+
+ /*
+ * @Resource(name = "artifact-dao") private IArtifactDAO artifactDAO;
+ */
+
+ @Resource(name = "resource-upload")
+ private IResourceUploader daoUploader;
+ ESArtifactData arData;
+
+ @Resource(name = "resource-dao")
+ private IGenericSearchDAO resourceDAO;
+
+ private String nodeType = "NodeType1";
+ private String nodeTypeVersion = "1.0.0";
+
+ private String nodeType2 = "NodeType2";
+ private String nodeTypeVersion2 = "1.0.1";
+
+ private String nodeType3 = "NodeType3";
+ private String nodeNypeVersion3 = "1.1.1";
+
+ private String topologyId = "topology";
+ private String topologyTemplateName = "topologyTemplate";
+ private String topologyTemplateVersion = "1.1.1";
+
+ private String nodeTypeTemplate1 = "NodeTypeTemplate1";
+ private String nodeTypeTemplate2 = "NodeTypeTemplate2";
+ private String nodeTypeTemplate3 = "NodeTypeTemplate3";
+
+ private static ConfigurationManager configurationManager;
+
+ @Before
+ public void before() {
+ // try {
+ // clearIndex(ICatalogDAO.RESOURCES_INDEX, ArtifactData.class);
+ // clearIndex(ICatalogDAO.RESOURCES_INDEX, ServiceArtifactData.class);
+ // } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ // e.printStackTrace();
+ // }
+
+ }
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ ExternalConfiguration.setAppName("catalog-dao");
+ String appConfigDir = "src/test/resources/config/catalog-dao";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+ }
+
+ // @Before
+ // public void createSchema(){
+ // SdcSchemaBuilder.createSchema();
+ // }
+ //
+
+ @Test
+ public void testSaveNewArtifact() {
+ // daoUploader = new ArtifactUploader(artifactDAO);
+ if (daoUploader == null) {
+ assertTrue(false);
+ }
+ String strData = "qweqwqweqw34e4wrwer";
+
+ String myNodeType = "MyNewNodeType";
+
+ ESArtifactData arData = new ESArtifactData("artifactNewMarina11", strData.getBytes());
+
+ ResourceUploadStatus status = daoUploader.saveArtifact(arData, true);
+
+ assertEquals(status, ResourceUploadStatus.OK);
+
+ daoUploader.deleteArtifact(arData.getId());
+
+ }
+
+ /*
+ * @Test public void testSaveNewImage(){
+ *
+ * Path iconPath = Paths.get(TEST_IMAGES_DIRECTORY, "apache.png");
+ *
+ * ImageData imageData = new ImageData(); try {
+ * imageData.setData(Files.readAllBytes(iconPath));
+ * imageData.setComponentName("ComponentMarina");
+ * imageData.setComponentVersion("v.1.0");
+ * imageData.setArtifactName("apache.png");
+ * imageData.setResourceCreator("Marina");
+ * imageData.setResourceLastUpdater("Marina"); ResourceUploadStatus status =
+ * daoUploader.saveImage(imageData, true); assertEquals(status,
+ * ResourceUploadStatus.OK); } catch (IOException e) { // TODO
+ * Auto-generated catch block e.printStackTrace(); }
+ *
+ *
+ * }
+ */
+
+ // @Test
+ // public void testGetArtifactsList() {
+ // //daoUploader = new ArtifactUploader(artifactDAO);
+ // if(daoUploader==null){
+ // assertTrue(false);
+ // }
+ // String myNodeType = "MyListNodeType";
+ //
+ //
+ //
+ // //resourceDAO.save(indexedNodeType);
+ //
+ // String strData = "qweqwqweqw34e4wrwer";
+ // ESArtifactData arData1 = new ESArtifactData("artifactNewMarina_1",
+ // strData.getBytes());
+ //
+ //
+ // ResourceUploadStatus status = daoUploader.saveArtifact(arData1, true);
+ // assertEquals(status, ResourceUploadStatus.OK);
+ //
+ // ESArtifactData arData2 = new ESArtifactData("artifactNewMarina_2",
+ // strData.getBytes());
+ //
+ //
+ // status = daoUploader.saveArtifact(arData2, true);
+ // assertEquals(status, ResourceUploadStatus.OK);
+ //
+ // ESArtifactData arData3 = new ESArtifactData("artifactNewMarina_3",
+ // strData.getBytes());
+ //
+ //
+ // status = daoUploader.saveArtifact(arData3, true);
+ // assertEquals(status, ResourceUploadStatus.OK);
+ //
+ //
+ //
+ // Either<List<ESArtifactData>, ResourceUploadStatus> arrArray =
+ // daoUploader.getArtifacts(myNodeType, nodeTypeVersion);
+ // assertTrue(arrArray.isLeft());
+ //
+ // assertEquals(3, arrArray.left().value().size());
+ //
+ // daoUploader.deleteArtifact(arData1.getId());
+ // daoUploader.deleteArtifact(arData2.getId());
+ // daoUploader.deleteArtifact(arData3.getId());
+ //
+ // //resourceDAO.delete(IndexedNodeType.class, indexedNodeType.getId());
+ //
+ // }
+ //
+
+ /*
+ * @Test public void testGetSeviceArtifactsList() {
+ *
+ * if(daoUploader==null){ assertTrue(false); } String strData =
+ * "qweqwqweqw34e4wrwer";
+ *
+ * ServiceArtifactData serviceArData = new
+ * ServiceArtifactData("serviceArData", topologyTemplateName,
+ * topologyTemplateVersion, nodeTypeTemplate1, nodeType, nodeTypeVersion,
+ * "YANG", strData.getBytes(), strData.getBytes(), "Marina", null);
+ * //serviceArData.setRefArtifactId(arData.getId()); ResourceUploadStatus
+ * status = daoUploader.saveServiceArtifact(serviceArData, true);
+ *
+ * ServiceArtifactData serviceArData1 = new
+ * ServiceArtifactData("serviceArData1", topologyTemplateName,
+ * topologyTemplateVersion, nodeTypeTemplate2, nodeType2, nodeTypeVersion2,
+ * "YANG", strData.getBytes(), strData.getBytes(), "Marina", null);
+ * //serviceArData1.setRefArtifactId(arData4.getId()); status =
+ * daoUploader.saveServiceArtifact(serviceArData1, true);
+ * ServiceArtifactData getServiceData =
+ * daoUploader.getServiceArtifact(serviceArData.getId()).left().value();
+ *
+ * List<ServiceArtifactData> arrArray =
+ * daoUploader.getServiceArtifacts(topologyTemplateName,
+ * topologyTemplateVersion).left().value();
+ *
+ * assertEquals(2, arrArray.size());
+ *
+ * daoUploader.deleteArtifact(serviceArData.getId());
+ * daoUploader.deleteArtifact(serviceArData1.getId());
+ *
+ *
+ * }
+ */
+
+ @Test
+ public void testGetArtifact() {
+
+ String myNodeType = "MyNodeType";
+
+ // resourceDAO.save(indexedNodeType);
+ ESArtifactData arData = getArtifactData(myNodeType, nodeTypeVersion);
+
+ ESArtifactData getData = null;
+ Either<ESArtifactData, ResourceUploadStatus> getArtifactStatus = daoUploader
+ .getArtifact(myNodeType + "- dassasd" + ":" + nodeTypeVersion + ":updatedArtifact");
+ if (getArtifactStatus.isRight()) {
+ daoUploader.saveArtifact(arData, true);
+ getArtifactStatus = daoUploader.getArtifact(arData.getId());
+ }
+ assertNotNull(getArtifactStatus.left().value());
+
+ }
+
+ /*
+ * @Test public void testGetSeviceArtifact() {
+ *
+ * ServiceArtifactData servArData = getServiceArtifactData();
+ *
+ * Either<ServiceArtifactData, ResourceUploadStatus>
+ * getServiceArtifactStatus =
+ * daoUploader.getServiceArtifact("MyService:v.1.1:updatedServiceArtifact");
+ * if (!getServiceArtifactStatus.isLeft()){
+ * daoUploader.saveServiceArtifact(servArData, true);
+ * getServiceArtifactStatus =
+ * daoUploader.getServiceArtifact(servArData.getId()); }
+ *
+ * assertNotNull(getServiceArtifactStatus.left().value());
+ *
+ * daoUploader.deleteArtifact(getServiceArtifactStatus.left().value().getId(
+ * ));
+ *
+ *
+ * }
+ */
+
+ /*
+ * @Test public void testGetSeviceArtifactsCollection() {
+ *
+ * prepareTopolgyService(); prepareTestTopolgyService();
+ * Either<ServiceArtifactsDataCollection, ResourceUploadStatus>
+ * getServiceArtifactsCollectionStatus =
+ * daoUploader.getServiceArtifactsCollection(topologyTemplateName,
+ * topologyTemplateVersion); ServiceArtifactsDataCollection serviceAtrifacts
+ * = getServiceArtifactsCollectionStatus.left().value();
+ *
+ * Map<String, List<ArtifactData>> map =
+ * serviceAtrifacts.getServiceArtifactDataMap();
+ *
+ * List<ArtifactData> list = map.get(nodeType); assertNotNull(list);
+ * assertEquals(2, list.size());
+ *
+ *
+ * list = map.get(nodeTypeTemplate1 ); assertNotNull(list); assertEquals(1,
+ * list.size());
+ *
+ * list = map.get(nodeTypeTemplate2 ); assertNotNull(list); assertEquals(1,
+ * list.size());
+ *
+ *
+ * }
+ */
+
+ @Test
+ public void testUpdateArtifact() {
+ // daoUploader = new ArtifactUploader(artifactDAO);
+ if (daoUploader == null) {
+ assertTrue(false);
+ }
+ ResourceUploadStatus status = ResourceUploadStatus.OK;
+
+ String myNodeType = "MyUpdatedNodeType";
+
+ // resourceDAO.save(indexedNodeType);
+
+ ESArtifactData arData = getArtifactData(myNodeType, nodeTypeVersion);
+ Either<ESArtifactData, ResourceUploadStatus> getArtifactStatus = daoUploader.getArtifact(arData.getId());
+
+ if (!getArtifactStatus.isLeft())
+ status = daoUploader.saveArtifact(arData, false);
+
+ String payload1 = "new payloadjfdsgh";
+ arData.setDataAsArray(payload1.getBytes());
+
+ status = daoUploader.updateArtifact(arData);
+
+ assertEquals(status, ResourceUploadStatus.OK);
+ // resourceDAO.delete(IndexedNodeType.class, indexedNodeType.getId());
+
+ }
+
+ private ESArtifactData getArtifactData(String componentName, String componentVersion) {
+ String strData = "qweqwqweqw34e4wrwer";
+ ESArtifactData arData = new ESArtifactData("updatedArtifact", strData.getBytes());
+
+ return arData;
+ }
+
+ /*
+ * private ServiceArtifactData getServiceArtifactData(){ String strData =
+ * "qweqwqweqw34e4wrwer"; ServiceArtifactData arData = new
+ * ServiceArtifactData("updatedServiceArtifact", "MyService", "v.1.1",
+ * "MyComponentTemplate", "MyComponent", "v.1.1", "YANG",
+ * strData.getBytes(), strData.getBytes(), "Marina", null);
+ *
+ * return arData; }
+ */
+
+ /*
+ * private void prepareTopolgyService(){
+ *
+ * List<String> listCap = new ArrayList<String>(); listCap.add("very_evil");
+ * List<String> listCap1 = new ArrayList<String>(); listCap.add("evil");
+ * try{ // Initialize test data IndexedNodeType indexedNodeType = new
+ * IndexedNodeType(); CSARDependency dep = new CSARDependency();
+ * dep.setName(nodeType); dep.setVersion(nodeTypeVersion);
+ * indexedNodeType.setElementId(nodeType);
+ * indexedNodeType.setArchiveName(nodeType);
+ * indexedNodeType.setArchiveVersion(nodeTypeVersion);
+ * indexedNodeType.setCreationDate(new Date());
+ * indexedNodeType.setLastUpdateDate(new Date());
+ * indexedNodeType.setDefaultCapabilities(listCap);
+ * resourceDAO.save(indexedNodeType);
+ *
+ *
+ * IndexedNodeType indexedNodeType1 = new IndexedNodeType();
+ * indexedNodeType1.setElementId(nodeType2);
+ * indexedNodeType1.setArchiveName(nodeType2);
+ * indexedNodeType1.setArchiveVersion(nodeTypeVersion2); CSARDependency dep1
+ * = new CSARDependency(); dep1.setName(nodeType2);
+ * dep1.setVersion(nodeTypeVersion2); indexedNodeType1.setCreationDate(new
+ * Date()); indexedNodeType1.setLastUpdateDate(new Date());
+ * indexedNodeType1.setDefaultCapabilities(listCap1);
+ * resourceDAO.save(indexedNodeType1);
+ *
+ *
+ * indexedNodeType.setElementId(nodeType3);
+ * indexedNodeType.setArchiveName(nodeType3);
+ * indexedNodeType.setArchiveVersion(nodeNypeVersion3); CSARDependency dep2
+ * = new CSARDependency(); dep2.setName(nodeType3);
+ * dep2.setVersion(nodeNypeVersion3); indexedNodeType.setCreationDate(new
+ * Date()); indexedNodeType.setLastUpdateDate(new Date());
+ * indexedNodeType.setDefaultCapabilities(null);
+ * resourceDAO.save(indexedNodeType); String osgiliath100Id =
+ * indexedNodeType.getId();
+ *
+ * Topology topology = new Topology(); topology.setId(topologyId);
+ * Set<CSARDependency> dependencies = new HashSet<CSARDependency>();
+ * dependencies.add(dep); dependencies.add(dep2); dependencies.add(dep1);
+ * topology.setDependencies(dependencies); Map<String, NodeTemplate>
+ * nodeTemplates = new HashMap <String, NodeTemplate>();
+ *
+ * NodeTemplate template1 = new NodeTemplate(nodeType, null, null, null,
+ * null, null, null); template1.setName(nodeTypeTemplate1);
+ * nodeTemplates.put(nodeTypeTemplate1, template1 );
+ *
+ * NodeTemplate template2 = new NodeTemplate(nodeType2, null, null, null,
+ * null, null, null); template2.setName(nodeTypeTemplate2 );
+ * nodeTemplates.put(nodeTypeTemplate2, template2 );
+ *
+ * NodeTemplate template3 = new NodeTemplate(nodeType, null, null, null,
+ * null, null, null); template3.setName(nodeTypeTemplate3 );
+ * nodeTemplates.put(nodeTypeTemplate3, template3);
+ *
+ * topology.setNodeTemplates(nodeTemplates); resourceDAO.save(topology);
+ *
+ * TopologyTemplate topologyTemplate = new TopologyTemplate();
+ * topologyTemplate.setId(topologyTemplateName);
+ * topologyTemplate.setName(topologyTemplateName);
+ * topologyTemplate.setTopologyId(topology.getId());
+ * topologyTemplate.setDescription("my topology template");
+ * resourceDAO.save(topologyTemplate);
+ *
+ * String strData = "qweqwqweqw34e4wrwer"; ArtifactData arData = new
+ * ArtifactData("artifact1", nodeType, nodeTypeVersion, "YANG",
+ * strData.getBytes(), strData.getBytes(), "Marina"); ArtifactData arData1 =
+ * new ArtifactData("artifact2", nodeType, nodeTypeVersion, "YANG",
+ * strData.getBytes(), strData.getBytes(), "Marina"); ResourceUploadStatus
+ * status = daoUploader.saveArtifact(arData, true); status =
+ * daoUploader.saveArtifact(arData1, true);
+ *
+ * ArtifactData arData3 = new ArtifactData("artifact1", nodeType2,
+ * nodeTypeVersion2, "YANG", strData.getBytes(), strData.getBytes(),
+ * "Marina"); status = daoUploader.saveArtifact(arData3, true);
+ *
+ * ArtifactData arData4 = new ArtifactData("artifact2", nodeType2,
+ * nodeTypeVersion2, "YANG", strData.getBytes(), strData.getBytes(),
+ * "Marina"); status = daoUploader.saveArtifact(arData4, true);
+ *
+ * ServiceArtifactData serviceArData = new
+ * ServiceArtifactData("serviceArData", topologyTemplateName,
+ * topologyTemplateVersion, nodeTypeTemplate1, nodeType, nodeTypeVersion,
+ * "YANG", strData.getBytes(), strData.getBytes(), "Marina",
+ * arData.getId());
+ *
+ * status = daoUploader.saveServiceArtifact(serviceArData, true);
+ *
+ * ServiceArtifactData serviceArData1 = new
+ * ServiceArtifactData("serviceArData1", topologyTemplateName,
+ * topologyTemplateVersion, nodeTypeTemplate2, nodeType2, nodeTypeVersion2,
+ * "YANG", strData.getBytes(), strData.getBytes(), "Marina",
+ * arData4.getId());
+ *
+ * status = daoUploader.saveServiceArtifact(serviceArData1, true);
+ *
+ *
+ * } catch (Exception e) { // TODO Auto-generated catch block
+ * e.printStackTrace(); }
+ *
+ * }
+ *
+ * private void prepareTestTopolgyService(){
+ *
+ * List<String> listCap = new ArrayList<String>();
+ * listCap.add("very_evil test"); List<String> listCap1 = new
+ * ArrayList<String>(); listCap.add("evil test"); try{ // Initialize test
+ * data IndexedNodeType indexedNodeType = new IndexedNodeType();
+ * CSARDependency dep = new CSARDependency(); dep.setName(nodeType +
+ * " test"); dep.setVersion(nodeTypeVersion);
+ * indexedNodeType.setElementId(nodeType + " test");
+ * indexedNodeType.setArchiveName(nodeType + " test");
+ * indexedNodeType.setArchiveVersion(nodeTypeVersion);
+ * indexedNodeType.setCreationDate(new Date());
+ * indexedNodeType.setLastUpdateDate(new Date());
+ * indexedNodeType.setDefaultCapabilities(listCap);
+ * resourceDAO.save(indexedNodeType);
+ *
+ *
+ * IndexedNodeType indexedNodeType1 = new IndexedNodeType();
+ * indexedNodeType1.setElementId(nodeType2 + " test");
+ * indexedNodeType1.setArchiveName(nodeType2 + " test");
+ * indexedNodeType1.setArchiveVersion(nodeTypeVersion2); CSARDependency dep1
+ * = new CSARDependency(); dep1.setName(nodeType2 + " test");
+ * dep1.setVersion(nodeTypeVersion2); indexedNodeType1.setCreationDate(new
+ * Date()); indexedNodeType1.setLastUpdateDate(new Date());
+ * indexedNodeType1.setDefaultCapabilities(listCap1);
+ * resourceDAO.save(indexedNodeType1);
+ *
+ *
+ * indexedNodeType.setElementId(nodeType3 + " test");
+ * indexedNodeType.setArchiveName(nodeType3 + " test");
+ * indexedNodeType.setArchiveVersion(nodeNypeVersion3); CSARDependency dep2
+ * = new CSARDependency(); dep2.setName(nodeType3 + " test");
+ * dep2.setVersion(nodeNypeVersion3); indexedNodeType.setCreationDate(new
+ * Date()); indexedNodeType.setLastUpdateDate(new Date());
+ * indexedNodeType.setDefaultCapabilities(null);
+ * resourceDAO.save(indexedNodeType); String osgiliath100Id =
+ * indexedNodeType.getId();
+ *
+ * Topology topology = new Topology(); topology.setId(topologyId + " test");
+ * Set<CSARDependency> dependencies = new HashSet<CSARDependency>();
+ * dependencies.add(dep); dependencies.add(dep2); dependencies.add(dep1);
+ * topology.setDependencies(dependencies); Map<String, NodeTemplate>
+ * nodeTemplates = new HashMap <String, NodeTemplate>();
+ *
+ * NodeTemplate template1 = new NodeTemplate(nodeType + " test", null, null,
+ * null, null, null, null); template1.setName(nodeTypeTemplate1 + " test");
+ * nodeTemplates.put(nodeTypeTemplate1 + " test", template1 );
+ *
+ * NodeTemplate template2 = new NodeTemplate(nodeType2 + " test", null,
+ * null, null, null, null, null); template2.setName(nodeTypeTemplate2 +
+ * " test" ); nodeTemplates.put(nodeTypeTemplate2 + " test", template2 );
+ *
+ * NodeTemplate template3 = new NodeTemplate(nodeType, null, null, null,
+ * null, null, null); template3.setName(nodeTypeTemplate3 + " test" );
+ * nodeTemplates.put(nodeTypeTemplate3 + " test", template3);
+ *
+ * topology.setNodeTemplates(nodeTemplates); resourceDAO.save(topology);
+ *
+ * TopologyTemplate topologyTemplate = new TopologyTemplate();
+ * topologyTemplate.setId(topologyTemplateName + " test");
+ * topologyTemplate.setName(topologyTemplateName + " test");
+ * topologyTemplate.setTopologyId(topology.getId());
+ * topologyTemplate.setDescription("my topology template");
+ * resourceDAO.save(topologyTemplate);
+ *
+ * String strData = "qweqwqweqw34e4wrwer"; ArtifactData arData = new
+ * ArtifactData("artifact1 test", nodeType + " test", nodeTypeVersion,
+ * "YANG", strData.getBytes(), strData.getBytes(), "Marina"); ArtifactData
+ * arData1 = new ArtifactData("artifact2 test", nodeType + " test",
+ * nodeTypeVersion, "YANG", strData.getBytes(), strData.getBytes(),
+ * "Marina"); ResourceUploadStatus status = daoUploader.saveArtifact(arData,
+ * true); status = daoUploader.saveArtifact(arData1, true);
+ *
+ * ArtifactData arData3 = new ArtifactData("artifact1 test", nodeType2 +
+ * " test", nodeTypeVersion2, "YANG", strData.getBytes(),
+ * strData.getBytes(), "Marina"); status = daoUploader.saveArtifact(arData3,
+ * true);
+ *
+ * ArtifactData arData4 = new ArtifactData("artifact2 test", nodeType2 +
+ * " test", nodeTypeVersion2, "YANG", strData.getBytes(),
+ * strData.getBytes(), "Marina"); status = daoUploader.saveArtifact(arData4,
+ * true);
+ *
+ * ServiceArtifactData serviceArData = new
+ * ServiceArtifactData("serviceArData test" , topologyTemplateName +
+ * " test", topologyTemplateVersion, nodeTypeTemplate1 + " test", nodeType +
+ * " test", nodeTypeVersion, "YANG", strData.getBytes(), strData.getBytes(),
+ * "Marina", arData.getId());
+ *
+ * status = daoUploader.saveServiceArtifact(serviceArData, true);
+ *
+ * ServiceArtifactData serviceArData1 = new
+ * ServiceArtifactData("serviceArData1 test", topologyTemplateName +
+ * " test", topologyTemplateVersion, nodeTypeTemplate2 + " test", nodeType2
+ * + " test", nodeTypeVersion2, "YANG", strData.getBytes(),
+ * strData.getBytes(), "Marina", arData4.getId());
+ *
+ * status = daoUploader.saveServiceArtifact(serviceArData1, true);
+ *
+ *
+ * } catch (Exception e) { // TODO Auto-generated catch block
+ * e.printStackTrace(); }
+ *
+ * }
+ */
+
+ private void clearIndex(String indexName, Class<?> clazz) throws InterruptedException {
+
+ DeleteIndexResponse actionGet = esclient.getClient().admin().indices().delete(new DeleteIndexRequest(indexName))
+ .actionGet();
+ assertTrue(actionGet.isAcknowledged());
+ }
+
+}
diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/AuditingDaoTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/AuditingDaoTest.java
new file mode 100644
index 0000000000..06d26f79f9
--- /dev/null
+++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/AuditingDaoTest.java
@@ -0,0 +1,463 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources;
+
+import fj.data.Either;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.index.query.MatchAllQueryBuilder;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.SearchHits;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.ElasticSearchConfig.IndicesTimeFrequencyEntry;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.es.ElasticSearchClient;
+import org.openecomp.sdc.be.dao.impl.AuditingDao;
+import org.openecomp.sdc.be.resources.data.auditing.*;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.datastructure.AuditingFieldsKeysEnum;
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
+import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static org.junit.Assert.*;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
+ DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class })
+public class AuditingDaoTest {
+ private static Logger log = LoggerFactory.getLogger(AuditingDaoTest.class.getName());
+ @Resource(name = "elasticsearch-client")
+ private ElasticSearchClient esclient;
+
+ @Resource(name = "auditingDao")
+ private AuditingDao auditingDao;
+
+ private static ConfigurationManager configurationManager;
+ // private static Map<AuditingFieldsKeysEnum, String> auditField2esField;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ ExternalConfiguration.setAppName("catalog-dao");
+ String appConfigDir = "src/test/resources/config/catalog-dao";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+ // initAudit2EsMap();
+ }
+
+ @After
+ public void tearDown() {
+ deleteOldIndexes();
+ }
+
+ @Before
+ public void setup() {
+ auditingDao.setConfigurationManager(configurationManager);
+ deleteOldIndexes();
+ }
+
+ private void deleteOldIndexes() {
+ DeleteIndexResponse deleteResponse = esclient.getClient().admin().indices()
+ .prepareDelete(auditingDao.getIndexPrefix() + "*").execute().actionGet();
+ if (!deleteResponse.isAcknowledged()) {
+ log.debug("Couldn't delete old auditing indexes!");
+ assertTrue(false);
+ }
+ }
+
+ // @Test
+ public void testAddUpdateAdminEventMinute() {
+
+ String timestamp = "2015-06-23 13:34:53.123";
+
+ String creationPeriod = Constants.MINUTE;
+ String expectedIndexName = auditingDao.getIndexPrefix() + "-2015-06-23-13-34";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName).execute().actionGet()
+ .isExists());
+ Map<AuditingFieldsKeysEnum, Object> params = getUserAdminEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAdminEvent.class);
+ params = getUserAccessEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAccessEvent.class);
+ params = getResourceAdminEventParams(timestamp, "addResource");
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, ResourceAdminEvent.class);
+ }
+
+ // @Test
+ public void testAddUpdateAdminEventYearly() {
+
+ String timestamp = "2016-06-23 13:34:53.123";
+ String creationPeriod = Constants.YEAR;
+ String expectedIndexName = auditingDao.getIndexPrefix() + "-2016";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName).execute().actionGet()
+ .isExists());
+ Map<AuditingFieldsKeysEnum, Object> params = getUserAdminEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAdminEvent.class);
+ params = getUserAccessEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAccessEvent.class);
+ params = getResourceAdminEventParams(timestamp, "addResource");
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, ResourceAdminEvent.class);
+ }
+
+ @Test
+ public void testGetDistributionStatusEvent() {
+
+ String timestamp1 = "2016-06-23 13:34:53.123";
+ String creationPeriod = Constants.MONTH;
+ String expectedIndexName1 = auditingDao.getIndexPrefix() + "-2016-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName1).execute().actionGet()
+ .isExists());
+ Map<AuditingFieldsKeysEnum, Object> params = getDistributionStatusEventParams(timestamp1);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName1, DistributionStatusEvent.class);
+ String timestamp2 = "2015-06-23 13:34:53.123";
+
+ String expectedIndexName2 = auditingDao.getIndexPrefix() + "-2015-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName2).execute().actionGet()
+ .isExists());
+ Map<AuditingFieldsKeysEnum, Object> params2 = getDistributionStatusEventParams(timestamp2);
+ testCreationPeriodScenario(params2, creationPeriod, expectedIndexName2, DistributionStatusEvent.class);
+ Either<List<ESTimeBasedEvent>, ActionStatus> status = auditingDao.getListOfDistributionStatuses("123-456");
+ assertEquals(2, status.left().value().size());
+ }
+
+ @Test
+ public void testGetCountAdminEventMonthly() {
+
+ String timestamp1 = "2016-06-23 13:34:53.123";
+ String timestamp2 = "2015-06-23 13:34:53.123";
+ String creationPeriod = Constants.MONTH;
+ String expectedIndexName1 = auditingDao.getIndexPrefix() + "-2016-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName1).execute().actionGet()
+ .isExists());
+ String expectedIndexName2 = auditingDao.getIndexPrefix() + "-2015-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName2).execute().actionGet()
+ .isExists());
+
+ Map<AuditingFieldsKeysEnum, Object> params1 = getUserAdminEventParams(timestamp1);
+ testCreationPeriodScenario(params1, creationPeriod, expectedIndexName1, UserAdminEvent.class);
+ Map<AuditingFieldsKeysEnum, Object> params2 = getUserAdminEventParams(timestamp2);
+ testCreationPeriodScenario(params2, creationPeriod, expectedIndexName2, UserAdminEvent.class);
+
+ long count = auditingDao.count(UserAdminEvent.class, new MatchAllQueryBuilder());
+ log.debug("Testing auditing count {}", count);
+ assertEquals(2, count);
+ }
+
+ @Test
+ public void testServiceDistributionStatuses() {
+
+ String timestamp = "2016-06-23 13:34:53.123";
+ String creationPeriod = Constants.MONTH;
+ String expectedIndexName = auditingDao.getIndexPrefix() + "-2016-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName).execute().actionGet()
+ .isExists());
+ Map<AuditingFieldsKeysEnum, Object> params = getUserAdminEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAdminEvent.class);
+ params = getUserAccessEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAccessEvent.class);
+ params = getResourceAdminEventParams(timestamp, "DRequest");
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, ResourceAdminEvent.class);
+ params = getDistributionNotificationEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, DistributionNotificationEvent.class);
+ Either<List<ESTimeBasedEvent>, ActionStatus> status = auditingDao
+ .getServiceDistributionStatusesList("SeviceId");
+ log.debug("Testing auditing count {}", status);
+ }
+
+ @Test
+ public void testAddUpdateAdminEventMonthly() {
+
+ String timestamp = "2016-06-23 13:34:53.123";
+ String creationPeriod = Constants.MONTH;
+ String expectedIndexName = auditingDao.getIndexPrefix() + "-2016-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName).execute().actionGet()
+ .isExists());
+ Map<AuditingFieldsKeysEnum, Object> params = getUserAdminEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAdminEvent.class);
+ params = getUserAccessEventParams(timestamp);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, UserAccessEvent.class);
+ params = getResourceAdminEventParams(timestamp, "addResource");
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, ResourceAdminEvent.class);
+ }
+
+ private SearchResponse testCreationPeriodScenario(Map<AuditingFieldsKeysEnum, Object> params, String creationPeriod,
+ String expectedIndexName, Class<? extends AuditingGenericEvent> clazz) {
+
+ String typeName = clazz.getSimpleName().toLowerCase();
+ log.debug("Testing auditing type {}", typeName);
+ setCreationPeriod(creationPeriod);
+ ActionStatus saveUserAdminEvent = auditingDao.addRecord(params, typeName);
+ assertEquals(ActionStatus.OK, saveUserAdminEvent);
+ assertTrue(esclient.getClient().admin().indices().prepareExists(expectedIndexName).execute().actionGet()
+ .isExists());
+ MatchAllQueryBuilder matchAllQueryBuilder = new MatchAllQueryBuilder();
+
+ SearchResponse searchResponse = esclient.getClient().prepareSearch(expectedIndexName).setTypes(typeName)
+ .setQuery(matchAllQueryBuilder).execute().actionGet();
+
+ SearchHits hits = searchResponse.getHits();
+ assertEquals(1, hits.getTotalHits());
+ log.debug("Checking that all expected fields are properly persisted");
+ validateHitValues(params, hits.getAt(0));
+ log.debug("testCreationPeriodScenario successful");
+ return searchResponse;
+ }
+
+ private void validateHitValues(Map<AuditingFieldsKeysEnum, Object> params, SearchHit searchHit) {
+ Map<String, Object> source = searchHit.getSource();
+ log.debug("Hit source is {}", searchHit.sourceAsString());
+ for (Entry<AuditingFieldsKeysEnum, Object> paramsEntry : params.entrySet()) {
+ AuditingFieldsKeysEnum key = paramsEntry.getKey();
+ log.debug("Testing auditing field {}", key.name());
+ Object value = paramsEntry.getValue();
+ // assertEquals(value, source.get(auditField2esField.get(key)));
+ assertEquals(value, source.get(key.getDisplayName()));
+ }
+ }
+
+ private void setCreationPeriod(String creationPeriod) {
+ Configuration configuration = configurationManager.getConfiguration();
+ List<IndicesTimeFrequencyEntry> indicesTimeFrequencyEntries = new ArrayList<>();
+ IndicesTimeFrequencyEntry indicesTimeFrequencyEntry = new IndicesTimeFrequencyEntry();
+ indicesTimeFrequencyEntry.setIndexPrefix("auditingevents");
+ indicesTimeFrequencyEntry.setCreationPeriod(creationPeriod);
+ configuration.getElasticSearch().setIndicesTimeFrequency(indicesTimeFrequencyEntries);
+ }
+
+ private Map<AuditingFieldsKeysEnum, Object> getUserAdminEventParams(String timestamp) {
+
+ Map<AuditingFieldsKeysEnum, Object> params = new HashMap<AuditingFieldsKeysEnum, Object>();
+ String action = "updateUser";
+ String modifierName = "moshe moshe";
+ String modifierUid = "mosheUid";
+ String userUid = "mosheUid";
+ String userBeforeName = "moshe moshe";
+ String userBeforeEmail = "moshe@moshe1.com";
+ String userBeforeRole = "TESTER";
+ String userAfterName = "moshe moshe";
+ String userAfterEmail = "moshe@moshe2.com";
+ String userAfterRole = "TESTER";
+ String userStatus = "200";
+ String userDesc = "OK";
+
+ params.put(AuditingFieldsKeysEnum.AUDIT_ACTION, action);
+ params.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifierName + '(' + modifierUid + ')');
+ params.put(AuditingFieldsKeysEnum.AUDIT_USER_UID, userUid);
+ params.put(AuditingFieldsKeysEnum.AUDIT_USER_BEFORE,
+ userUid + ", " + userBeforeName + ", " + userBeforeEmail + ", " + userBeforeRole);
+ params.put(AuditingFieldsKeysEnum.AUDIT_USER_AFTER,
+ userUid + ", " + userAfterName + ", " + userAfterEmail + ", " + userAfterRole);
+ params.put(AuditingFieldsKeysEnum.AUDIT_STATUS, userStatus);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DESC, userDesc);
+ params.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, timestamp);
+
+ return params;
+ }
+
+ private Map<AuditingFieldsKeysEnum, Object> getUserAccessEventParams(String timestamp) {
+
+ Map<AuditingFieldsKeysEnum, Object> params = new HashMap<AuditingFieldsKeysEnum, Object>();
+ String action = "userAccess";
+ String userUid = "mosheUid";
+ String userName = "moshe moshe";
+ String userStatus = "200";
+ String userDesc = "OK";
+
+ params.put(AuditingFieldsKeysEnum.AUDIT_ACTION, action);
+ params.put(AuditingFieldsKeysEnum.AUDIT_USER_UID, userName + '(' + userUid + ')');
+ params.put(AuditingFieldsKeysEnum.AUDIT_STATUS, userStatus);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DESC, userDesc);
+ params.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, timestamp);
+
+ return params;
+ }
+
+ private Map<AuditingFieldsKeysEnum, Object> getResourceAdminEventParams(String timestamp, String action) {
+
+ Map<AuditingFieldsKeysEnum, Object> params = new HashMap<AuditingFieldsKeysEnum, Object>();
+
+ String modifierName = "moshe moshe";
+ String modifierUid = "mosheUid";
+ String resourceName = "Centos";
+ String resourceType = "Resource";
+ String currState = "READY_FOR_CERTIFICATION";
+ String prevState = "CHECKED_OUT";
+ String currVersion = "1.1.4";
+ String prevVersion = "1.1.3";
+ String status = "200";
+ String desc = "OK";
+ String distributionId = "123-456";
+ String serviceId = "SeviceId";
+
+ params.put(AuditingFieldsKeysEnum.AUDIT_ACTION, action);
+ params.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifierName);
+ params.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifierUid);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceName);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, resourceType);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, currState);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_STATE, prevState);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, currVersion);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_PREV_VERSION, prevVersion);
+ params.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DESC, desc);
+ params.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, timestamp);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+ params.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, serviceId);
+
+ return params;
+ }
+
+ private Map<AuditingFieldsKeysEnum, Object> getDistributionStatusEventParams(String timestamp) {
+
+ Map<AuditingFieldsKeysEnum, Object> params = new HashMap<AuditingFieldsKeysEnum, Object>();
+ String action = "DStatus";
+ String modifierName = "moshe moshe";
+ String modifierUid = "mosheUid";
+ String topicName = "Centos";
+ String serviceId = "SeviceId";
+ String resourceUrl = "resourceUrl";
+ String distributionId = "123-456";
+
+ String status = "200";
+ String desc = "OK";
+
+ params.put(AuditingFieldsKeysEnum.AUDIT_DESC, desc);
+ params.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, timestamp);
+ params.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ params.put(AuditingFieldsKeysEnum.AUDIT_ACTION, action);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_CONSUMER_ID, modifierUid);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, topicName);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_RESOURCE_URL, resourceUrl);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_STATUS_TIME, timestamp);
+ params.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, serviceId);
+
+ return params;
+ }
+
+ // @Test
+ public void getListOfDistributionByActionTest() {
+
+ String timestamp = "2016-06-23 13:34:53.123";
+ String distributionId = "123-456";
+
+ String creationPeriod = Constants.MONTH;
+ String expectedIndexName = auditingDao.getIndexPrefix() + "-2016-06";
+ assertTrue(!esclient.getClient().admin().indices().prepareExists(expectedIndexName).execute().actionGet()
+ .isExists());
+
+ // Client client = esclient.getClient();
+ // final CreateIndexRequestBuilder createIndexRequestBuilder =
+ // client.admin().indices().prepareCreate(expectedIndexName);
+ // final XContentBuilder mappingBuilder =
+ // jsonBuilder().startObject().startObject("resourceadminevent")
+ // .startObject("_ttl").field("enabled", "true").field("default",
+ // "1s").endObject().endObject()
+ // .endObject();
+ // System.out.println(mappingBuilder.string());
+ // createIndexRequestBuilder.addMapping(documentType, mappingBuilder);
+ //
+ // // MAPPING DONE
+ // createIndexRequestBuilder.execute().actionGet();
+ //
+ //
+
+ Map<AuditingFieldsKeysEnum, Object> params = getResourceAdminEventParams(timestamp, "DRequest");
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, ResourceAdminEvent.class);
+ params = getDistributionNotificationEventParams(timestamp);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, distributionId);
+
+ testCreationPeriodScenario(params, creationPeriod, expectedIndexName, DistributionNotificationEvent.class);
+
+ Either<List<ESTimeBasedEvent>, ActionStatus> distributionByAction = auditingDao
+ .getListOfDistributionByAction(distributionId, "DRequest", "200", ResourceAdminEvent.class);
+ assertTrue(distributionByAction.isLeft());
+ assertFalse(distributionByAction.left().value().isEmpty());
+
+ distributionByAction = auditingDao.getListOfDistributionByAction(distributionId, "DNotify", "200",
+ DistributionNotificationEvent.class);
+ assertTrue(distributionByAction.isLeft());
+ assertFalse(distributionByAction.left().value().isEmpty());
+
+ }
+
+ private Map<AuditingFieldsKeysEnum, Object> getDistributionNotificationEventParams(String timestamp) {
+
+ Map<AuditingFieldsKeysEnum, Object> params = new HashMap<AuditingFieldsKeysEnum, Object>();
+
+ String action = "DNotify";
+ String modifierName = "moshe moshe";
+ String modifierUid = "mosheUid";
+ String resourceName = "Centos";
+ String resourceType = "Resource";
+
+ String currVersion = "1.1.4";
+ String currState = "READY_FOR_CERTIFICATION";
+ String status = "200";
+ String desc = "OK";
+ String did = "1027";
+ String topicName = "Centos";
+ String serviceId = "SeviceId";
+ String requestId = "12364";
+
+ params.put(AuditingFieldsKeysEnum.AUDIT_ACTION, action);
+ params.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, requestId);
+ params.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_UID, modifierUid);
+ params.put(AuditingFieldsKeysEnum.AUDIT_MODIFIER_NAME, modifierName);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_NAME, resourceName);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_TYPE, resourceType);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_STATE, currState);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_TOPIC_NAME, topicName);
+ params.put(AuditingFieldsKeysEnum.AUDIT_RESOURCE_CURR_VERSION, currVersion);
+ params.put(AuditingFieldsKeysEnum.AUDIT_STATUS, status);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DESC, desc);
+ params.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP, timestamp);
+ params.put(AuditingFieldsKeysEnum.AUDIT_DISTRIBUTION_ID, did);
+ params.put(AuditingFieldsKeysEnum.AUDIT_SERVICE_INSTANCE_ID, serviceId);
+ return params;
+ }
+
+}
diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/CassandraTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/CassandraTest.java
new file mode 100644
index 0000000000..caaf0702a5
--- /dev/null
+++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/CassandraTest.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.Mapper;
+import com.datastax.driver.mapping.MappingManager;
+
+import org.openecomp.sdc.be.dao.Account;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CassandraTest {
+ private static Logger log = LoggerFactory.getLogger(CassandraTest.class.getName());
+ private Cluster cluster;
+
+ // #\@Test
+ public void testCrud() {
+ String node = "mtanjv9sdcg44";
+
+ cluster = Cluster.builder().addContactPoint(node).build();
+
+ // Query
+ String query = "CREATE KEYSPACE IF NOT EXISTS dstest WITH replication "
+ + "= {'class':'SimpleStrategy', 'replication_factor':1};";
+
+ String queryTable = "CREATE TABLE IF NOT EXISTS accounts(email varchar PRIMARY KEY, name varchar);";
+
+ Session session = cluster.connect();
+ // Executing the query
+ session.execute(query);
+ // //using the KeySpace
+ session.execute("USE dstest");
+ session.execute(queryTable);
+
+ Mapper<Account> mapper = new MappingManager(session).mapper(Account.class);
+ Account account = new Account("John Doe", "jd@example.com");
+ // Class<? extends Account> class1 = account.getClass();
+ // Class class2 = Account.class;
+ mapper.save(account);
+
+ Account whose = mapper.get("jd@example.com");
+ log.debug("Account name: {}", whose.getName());
+
+ account.setName("Samanta Smit");
+ mapper.save(account);
+ whose = mapper.get("jd@example.com");
+ log.debug("Account name: {}", whose.getName());
+
+ mapper.delete(account);
+ whose = mapper.get("jd@example.com");
+
+ cluster.close();
+ }
+}
diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ESUsersDAOTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ESUsersDAOTest.java
new file mode 100644
index 0000000000..a99acbe2b7
--- /dev/null
+++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/ESUsersDAOTest.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources;
+
+//@RunWith(SpringJUnit4ClassRunner.class)
+//@ContextConfiguration("classpath:application-context-test.xml")
+//@TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class})
+public class ESUsersDAOTest {
+
+ // @Resource
+ // ElasticSearchClient esclient;
+ //
+ //// @Resource(name = "users-dao")
+ // private IUsersDAO usersDao;
+
+ // @Test
+ public void testNewUserStub() {
+
+ }
+
+ // @Test
+ public void testNewUser() {
+ // if( usersDao == null ){
+ // assertTrue(false);
+ // }
+ //
+ // String id = "yhufksd57834601";
+ // UserData userData = new UserData("Myname", "Mylastname", id, "email",
+ // "Tester");
+ //
+ // ActionStatus saveUserData = usersDao.saveUserData(userData);
+ // assertEquals(saveUserData, ActionStatus.OK);
+ //
+ // Either<UserData, ActionStatus> statusFromEs =
+ // usersDao.getUserData(id);
+ // assertTrue(statusFromEs.isLeft() );
+ // UserData fromEs = statusFromEs.left().value();
+ // assertNotNull(fromEs);
+ // assertEquals(userData, fromEs);
+ //
+ //
+ // usersDao.deleteUserData(id);
+
+ }
+
+}
diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/TitanGenericDaoTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/TitanGenericDaoTest.java
new file mode 100644
index 0000000000..a4110f5be3
--- /dev/null
+++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/TitanGenericDaoTest.java
@@ -0,0 +1,721 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.resources;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.configuration.BaseConfiguration;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.AdditionalInfoParameterData;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.GraphNodeLock;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.UserRoleEnum;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
+import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
+import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
+
+import com.google.gson.Gson;
+import com.thinkaurelius.titan.core.PropertyKey;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.attribute.Text;
+import com.thinkaurelius.titan.core.schema.TitanManagement;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
+ DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class })
+public class TitanGenericDaoTest {
+ private static Logger log = LoggerFactory.getLogger(TitanGenericDaoTest.class.getName());
+ private static ConfigurationManager configurationManager;
+
+ @Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ ExternalConfiguration.setAppName("catalog-dao");
+ String appConfigDir = "src/test/resources/config/catalog-dao";
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+ configurationManager.getConfiguration()
+ .setTitanCfgFile("../catalog-be/src/main/resources/config/titan.properties");
+ configurationManager.getConfiguration().setTitanInMemoryGraph(true);
+ }
+
+ // @Test
+ public void testcheckEdgeProps() {
+ TitanGraph graph = titanDao.getGraph().left().value();
+ TitanVertex v1 = graph.addVertex();
+ v1.property("prop1", 123);
+ TitanVertex v2 = graph.addVertex();
+ v2.property("prop1", 456);
+ TitanEdge addEdge = v1.addEdge("label11", v2);
+ addEdge.property("edgeProp", "my prop edge");
+ graph.tx().commit();
+
+ Either<TitanVertex, TitanOperationStatus> v11 = titanDao.getVertexByProperty("prop1", 123);
+ Iterator<Edge> edges = v11.left().value().edges(Direction.OUT, "label11");
+ Edge edge = edges.next();
+ // String value = (String)edge.value("edgeProp");
+ String value = (String) titanDao.getProperty(edge, "edgeProp");
+ log.debug(value);
+
+ }
+
+ @Test
+ public void testCrudNode() {
+
+ String id = "user12345abc";
+ UserData userData = new UserData("Myname123", "Mylastname", id, "email123", "Tester",
+ UserStatusEnum.ACTIVE.name(), null);
+
+ Either<UserData, TitanOperationStatus> newNode = titanDao.createNode(userData, UserData.class);
+
+ assertTrue(newNode.isLeft());
+
+ log.debug("{}", newNode.left().value());
+
+ titanDao.commit();
+
+ ImmutablePair<String, Object> keyValueId = userData.getKeyValueId();
+ Either<UserData, TitanOperationStatus> node = titanDao.getNode(keyValueId.getKey(), keyValueId.getValue(),
+ UserData.class);
+ titanDao.commit();
+ assertTrue(node.isLeft());
+ log.debug("{}", node.left().value());
+
+ userData.setRole("Designer");
+ node = titanDao.updateNode(userData, UserData.class);
+ assertTrue(node.isLeft());
+ log.debug("{}", node.left().value());
+ assertEquals(null, "Designer", node.left().value().getRole());
+ titanDao.commit();
+
+ node = titanDao.deleteNode(userData, UserData.class);
+ assertTrue(node.isLeft());
+ log.debug("{}", node.left().value());
+ titanDao.commit();
+
+ node = titanDao.getNode(keyValueId.getKey(), keyValueId.getValue(), UserData.class);
+ assertTrue(node.isRight());
+ log.debug("{}", node.right().value());
+
+ }
+
+ @Test
+ public void testGetByCategoryAndAll() {
+
+ // create 2 nodes
+ String id = "user12345abc";
+ UserData userData1 = new UserData("Myname123", "Mylastname", id, "email123", "Tester",
+ UserStatusEnum.ACTIVE.name(), null);
+
+ Either<UserData, TitanOperationStatus> node1 = titanDao.createNode(userData1, UserData.class);
+ assertTrue(node1.isLeft());
+ log.debug("{}", node1.left().value());
+
+ id = "userdfkoer45abc";
+ UserData userData2 = new UserData("Mynadyhme123", "Mylasghtname", id, "emaighdl123", "Designer",
+ UserStatusEnum.ACTIVE.name(), null);
+ Either<UserData, TitanOperationStatus> node2 = titanDao.createNode(userData2, UserData.class);
+ assertTrue(node2.isLeft());
+ log.debug("{}", node2.left().value());
+
+ titanDao.commit();
+
+ ImmutablePair<String, Object> keyValueId1 = userData1.getKeyValueId();
+ // get first node
+ Either<UserData, TitanOperationStatus> node = titanDao.getNode(keyValueId1.getKey(), keyValueId1.getValue(),
+ UserData.class);
+ assertTrue(node.isLeft());
+ log.debug("{}", node.left().value());
+ titanDao.commit();
+
+ // get all must be 2 + 1 default user = 3
+ Either<List<UserData>, TitanOperationStatus> all = titanDao.getAll(NodeTypeEnum.User, UserData.class);
+ assertTrue(all.isLeft());
+ assertTrue(all.left().value().size() > 0);
+
+ log.debug("{}", all.left().value());
+
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(keyValueId1.getKey(), keyValueId1.getValue());
+
+ // get by criteria. must be 1
+ Either<List<UserData>, TitanOperationStatus> byCriteria = titanDao.getByCriteria(NodeTypeEnum.User, props,
+ UserData.class);
+ assertTrue(byCriteria.isLeft());
+ assertEquals(1, byCriteria.left().value().size());
+
+ log.debug("{}", byCriteria.left().value());
+
+ // delete all nodes
+ node = titanDao.deleteNode(userData1, UserData.class);
+ assertTrue(node.isLeft());
+ node = titanDao.deleteNode(userData2, UserData.class);
+ assertTrue(node.isLeft());
+ }
+
+ @Test
+ public void testGetEdgesForNode() {
+ String id = "user12345abc";
+ UserData userData = new UserData("Myname123", "Mylastname", id, "email123", UserRoleEnum.ADMIN.name(),
+ UserStatusEnum.ACTIVE.name(), null);
+ titanDao.createNode(userData, UserData.class);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setName("resourceForLock");
+ resourceData.getMetadataDataDefinition().setVersion("0.1");
+ resourceData.getMetadataDataDefinition().setState("newState");
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceData.getMetadataDataDefinition().getName() + "."
+ + resourceData.getMetadataDataDefinition().getVersion());
+
+ titanDao.createNode(resourceData, ResourceMetadataData.class);
+ titanDao.createRelation(userData, resourceData, GraphEdgeLabels.LAST_MODIFIER, null);
+ titanDao.commit();
+
+ Either<List<Edge>, TitanOperationStatus> eitherEdges = titanDao.getEdgesForNode(userData, Direction.OUT);
+ assertTrue(eitherEdges.isLeft());
+ assertTrue(eitherEdges.left().value().size() == 1);
+
+ eitherEdges = titanDao.getEdgesForNode(userData, Direction.IN);
+ assertTrue(eitherEdges.isLeft());
+ assertTrue(eitherEdges.left().value().size() == 0);
+
+ eitherEdges = titanDao.getEdgesForNode(resourceData, Direction.OUT);
+ assertTrue(eitherEdges.isLeft());
+ assertTrue(eitherEdges.left().value().size() == 0);
+
+ eitherEdges = titanDao.getEdgesForNode(resourceData, Direction.IN);
+ assertTrue(eitherEdges.isLeft());
+ assertTrue(eitherEdges.left().value().size() == 1);
+
+ eitherEdges = titanDao.getEdgesForNode(resourceData, Direction.BOTH);
+ assertTrue(eitherEdges.isLeft());
+ assertTrue(eitherEdges.left().value().size() == 1);
+
+ eitherEdges = titanDao.getEdgesForNode(userData, Direction.BOTH);
+ assertTrue(eitherEdges.isLeft());
+ assertTrue(eitherEdges.left().value().size() == 1);
+
+ titanDao.deleteNode(userData, UserData.class);
+ titanDao.deleteNode(resourceData, ResourceMetadataData.class);
+ titanDao.commit();
+ }
+
+ @Test
+ public void testLockElement() {
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+
+ resourceData.getMetadataDataDefinition().setName("resourceForLock");
+ resourceData.getMetadataDataDefinition().setVersion("0.1");
+ resourceData.getMetadataDataDefinition().setState("newState");
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceData.getMetadataDataDefinition().getName() + "."
+ + resourceData.getMetadataDataDefinition().getVersion());
+
+ Either<ResourceMetadataData, TitanOperationStatus> resource1 = titanDao.createNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(resource1.isLeft());
+ titanDao.commit();
+ String lockId = "lock_" + resourceData.getLabel() + "_" + resource1.left().value().getUniqueId();
+
+ Either<GraphNodeLock, TitanOperationStatus> nodeLock = titanDao
+ .getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, nodeLock.right().value());
+
+ TitanOperationStatus status = titanDao.lockElement(resourceData);
+ assertEquals(TitanOperationStatus.OK, status);
+
+ nodeLock = titanDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isLeft());
+ assertEquals(lockId, nodeLock.left().value().getUniqueId());
+
+ titanDao.commit();
+
+ status = titanDao.lockElement(resourceData);
+ assertEquals(TitanOperationStatus.ALREADY_LOCKED, status);
+
+ status = titanDao.releaseElement(resourceData);
+ assertEquals(TitanOperationStatus.OK, status);
+
+ nodeLock = titanDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, nodeLock.right().value());
+ titanDao.deleteNode(resourceData, ResourceMetadataData.class);
+ titanDao.commit();
+
+ }
+
+ @Test
+ public void testReLockElement() throws InterruptedException {
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+
+ resourceData.getMetadataDataDefinition().setName("resourceForReLock");
+ resourceData.getMetadataDataDefinition().setVersion("0.1");
+ resourceData.getMetadataDataDefinition().setState("newState");
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceData.getMetadataDataDefinition().getName() + "."
+ + resourceData.getMetadataDataDefinition().getVersion());
+
+ Either<ResourceMetadataData, TitanOperationStatus> resource1 = titanDao.createNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(resource1.isLeft());
+ titanDao.commit();
+ String lockId = "lock_" + resourceData.getLabel() + "_" + resource1.left().value().getUniqueId();
+
+ Either<GraphNodeLock, TitanOperationStatus> nodeLock = titanDao
+ .getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, nodeLock.right().value());
+
+ // lock
+ TitanOperationStatus status = titanDao.lockElement(resourceData);
+ assertEquals(TitanOperationStatus.OK, status);
+
+ nodeLock = titanDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isLeft());
+ assertEquals(lockId, nodeLock.left().value().getUniqueId());
+ long time1 = nodeLock.left().value().getTime();
+
+ titanDao.commit();
+
+ // timeout
+ configurationManager.getConfiguration().setTitanLockTimeout(2L);
+ Thread.sleep(5001);
+
+ // relock
+ status = titanDao.lockElement(resourceData);
+ assertEquals(TitanOperationStatus.OK, status);
+
+ nodeLock = titanDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isLeft());
+ assertEquals(lockId, nodeLock.left().value().getUniqueId());
+
+ long time2 = nodeLock.left().value().getTime();
+
+ assertTrue(time2 > time1);
+
+ status = titanDao.releaseElement(resourceData);
+ assertEquals(TitanOperationStatus.OK, status);
+
+ nodeLock = titanDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), lockId, GraphNodeLock.class);
+ assertTrue(nodeLock.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, nodeLock.right().value());
+
+ titanDao.deleteNode(resourceData, ResourceMetadataData.class);
+ titanDao.commit();
+
+ }
+
+ @Test
+ public void testBoolean() {
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+
+ resourceData.getMetadataDataDefinition().setName("resourceForLock");
+ resourceData.getMetadataDataDefinition().setVersion("0.1");
+ resourceData.getMetadataDataDefinition().setState("NOT_CERTIFIED_CHECKOUT");
+ resourceData.getMetadataDataDefinition().setHighestVersion(true);
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceData.getMetadataDataDefinition().getName() + "."
+ + resourceData.getMetadataDataDefinition().getVersion());
+
+ Either<ResourceMetadataData, TitanOperationStatus> resource1 = titanDao.createNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(resource1.isLeft());
+
+ resourceData = new ResourceMetadataData();
+
+ resourceData.getMetadataDataDefinition().setName("resourceForLock");
+ resourceData.getMetadataDataDefinition().setVersion("0.2");
+ resourceData.getMetadataDataDefinition().setState("NOT_CERTIFIED_CHECKOUT");
+ resourceData.getMetadataDataDefinition().setHighestVersion(false);
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceData.getMetadataDataDefinition().getName() + "."
+ + resourceData.getMetadataDataDefinition().getVersion());
+
+ Either<ResourceMetadataData, TitanOperationStatus> resource2 = titanDao.createNode(resourceData,
+ ResourceMetadataData.class);
+ titanDao.commit();
+
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), "NOT_CERTIFIED_CHECKOUT");
+ props.put("name", "resourceForLock");
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), false);
+
+ // get by criteria. must be 1
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isLeft());
+
+ titanDao.deleteNode(resource1.left().value(), ResourceMetadataData.class);
+
+ titanDao.deleteNode(resource2.left().value(), ResourceMetadataData.class);
+ titanDao.commit();
+ }
+
+ // @Test
+ public void testStringSearch() {
+ TitanGraph graph;
+
+ BaseConfiguration conf = new BaseConfiguration();
+ conf.setProperty("storage.backend", "inmemory");
+ graph = TitanFactory.open(conf);
+
+ // TitanManagement graphMgt = graph.getManagementSystem();
+ TitanManagement graphMgt = graph.openManagement();
+ PropertyKey propKey = graphMgt.makePropertyKey("string1").dataType(String.class).make();
+ graphMgt.buildIndex("string1", Vertex.class).addKey(propKey).unique().buildCompositeIndex();
+
+ propKey = graphMgt.makePropertyKey("string2").dataType(String.class).make();
+
+ // graphMgt.buildIndex("string2", Vertex.class).addKey(propKey,
+ // Mapping.TEXT.getParameter()).buildMixedIndex("search");
+ graphMgt.buildIndex("string2", Vertex.class).addKey(propKey).unique().buildCompositeIndex();
+ graphMgt.commit();
+
+ // TitanVertex v = graph.addVertex();
+ // v.addProperty("string1", "My new String 1");
+ // v.addProperty("string2", "String11");
+ // graph.commit();
+ //
+ // v = graph.addVertex();
+ // v.addProperty("string1", "my new string 1");
+ // v.addProperty("string2", "string11");
+ // graph.commit();
+ //
+ // System.out.println("First index search - case");
+ //
+ // Iterable<Vertex> vertices = graph.getVertices("string1", "My new
+ // String 1");
+ // Iterator<Vertex> iter = vertices.iterator();
+ // while ( iter.hasNext() ){
+ // Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ // }
+ // System.out.println("First index search non case");
+ //
+ // vertices = graph.getVertices("string1", "my new string 1");
+ // iter = vertices.iterator();
+ // while ( iter.hasNext() ){
+ // Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ // }
+ // System.out.println("Second index search case");
+ //
+ // vertices = graph.getVertices("string2", "String11");
+ // iter = vertices.iterator();
+ // while ( iter.hasNext() ){
+ // Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ // }
+ // System.out.println("second index search non case");
+ //
+ // vertices = graph.getVertices("string2", "string11");
+ // iter = vertices.iterator();
+ // while ( iter.hasNext() ){
+ // Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ // }
+ // System.out.println("Query index search case");
+ // vertices = graph.query().has("string1", "My new String
+ // 1").vertices();
+ // iter = vertices.iterator();
+ // while ( iter.hasNext() ){
+ // Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ // }
+ // System.out.println("Query index search non case");
+ // vertices = graph.query().has("string1", "my new string
+ // 1").vertices();
+ // iter = vertices.iterator();
+ // while ( iter.hasNext() ){
+ // Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ // }
+
+ log.debug("**** predicat index search non case");
+ Iterable<TitanVertex> vertices = graph.query().has("string1", Text.REGEX, "my new string 1").vertices();
+ Iterator<TitanVertex> iter = vertices.iterator();
+ while (iter.hasNext()) {
+ Vertex ver = iter.next();
+ // System.out.println(com.tinkerpop.blueprints.util.ElementHelper.getProperties(ver));
+ log.debug("{}", titanDao.getProperties(ver));
+ }
+
+ }
+
+ @Test
+ public void testDuplicateResultDueToTitanBug() {
+
+ // TitanGraph titanGraph = titanDao.getGraph().left().value();
+ // TitanManagement managementSystem = titanGraph.getManagementSystem();
+
+ // GraphPropertiesDictionary[] properties = {
+ // GraphPropertiesDictionary.IS_ABSTRACT,
+ // GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY,
+ // GraphPropertiesDictionary.POSITION_X,
+ // GraphPropertiesDictionary.ARTIFACT_TIMEOUT };
+ //
+ // for (GraphPropertiesDictionary property : properties) {
+ // if (false ==
+ // managementSystem.containsGraphIndex(property.getProperty())) {
+ // PropertyKey propKey1 =
+ // managementSystem.makePropertyKey(property.getProperty()).dataType(property.getClazz()).make();
+ // managementSystem.buildIndex(property.getProperty(),
+ // Vertex.class).addKey(propKey1).unique().buildCompositeIndex();
+ // }
+ // }
+
+ // managementSystem.commit();
+
+ ResourceMetadataData resourceData1 = new ResourceMetadataData();
+ resourceData1.getMetadataDataDefinition().setUniqueId("A");
+ ((ResourceMetadataDataDefinition) resourceData1.getMetadataDataDefinition()).setAbstract(true);
+ resourceData1.getMetadataDataDefinition().setName("aaaa");
+
+ Either<ResourceMetadataData, TitanOperationStatus> newNode1 = titanDao.createNode(resourceData1,
+ ResourceMetadataData.class);
+ assertTrue(newNode1.isLeft());
+ log.debug("{}", newNode1.left().value());
+ // titanDao.commit();
+
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), true);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isLeft());
+ assertEquals("check one result returned", 1, byCriteria.left().value().size());
+ // titanDao.commit();
+
+ ResourceMetadataData resourceToUpdate = new ResourceMetadataData();
+ ((ResourceMetadataDataDefinition) resourceToUpdate.getMetadataDataDefinition()).setAbstract(false);
+ resourceToUpdate.getMetadataDataDefinition().setUniqueId("A");
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceToUpdate,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+ // titanDao.commit();
+
+ byCriteria = titanDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria.right().value());
+
+ AdditionalInfoParameterData infoParameterData = new AdditionalInfoParameterData();
+ infoParameterData.getAdditionalInfoParameterDataDefinition().setUniqueId("123");
+ Map<String, String> idToKey = new HashMap<>();
+ idToKey.put("key1", "value1");
+ infoParameterData.setIdToKey(idToKey);
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> newNode2 = titanDao.createNode(infoParameterData,
+ AdditionalInfoParameterData.class);
+ assertTrue(newNode2.isLeft());
+ log.debug("{}", newNode2.left().value());
+ // titanDao.commit();
+
+ Map<String, String> idToKey2 = new HashMap<>();
+ idToKey2.put("key1", "value2");
+
+ Map<String, Object> props2 = new HashMap<>();
+ props2.put(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), "123");
+ Gson gson = new Gson();
+ props2.put(GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY.getProperty(), gson.toJson(idToKey2));
+ // props2.put(GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY.getProperty(),
+ // idToKey2);
+
+ Either<List<AdditionalInfoParameterData>, TitanOperationStatus> byCriteria2 = titanDao
+ .getByCriteria(NodeTypeEnum.AdditionalInfoParameters, props2, AdditionalInfoParameterData.class);
+ assertTrue(byCriteria2.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria2.right().value());
+
+ infoParameterData.setIdToKey(idToKey2);
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> updateNode2 = titanDao.updateNode(infoParameterData,
+ AdditionalInfoParameterData.class);
+ assertTrue(updateNode2.isLeft());
+ // titanDao.commit();
+
+ props2.put(GraphPropertiesDictionary.ADDITIONAL_INFO_ID_TO_KEY.getProperty(), idToKey);
+ byCriteria2 = titanDao.getByCriteria(NodeTypeEnum.AdditionalInfoParameters, props2,
+ AdditionalInfoParameterData.class);
+ assertTrue(byCriteria2.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria2.right().value());
+
+ ComponentInstanceData resourceInstanceData = new ComponentInstanceData();
+ resourceInstanceData.getComponentInstDataDefinition().setUniqueId("ri123");
+ resourceInstanceData.getComponentInstDataDefinition().setPosX("22");
+ resourceInstanceData.getComponentInstDataDefinition().setName("myresource_1");
+
+ Either<ComponentInstanceData, TitanOperationStatus> newNode3 = titanDao.createNode(resourceInstanceData,
+ ComponentInstanceData.class);
+ assertTrue(newNode3.isLeft());
+ log.debug("{}", newNode3.left().value());
+ // titanDao.commit();
+
+ resourceInstanceData.getComponentInstDataDefinition().setPosX("50");
+ Either<ComponentInstanceData, TitanOperationStatus> updateNode3 = titanDao.updateNode(resourceInstanceData,
+ ComponentInstanceData.class);
+ assertTrue(updateNode3.isLeft());
+ // titanDao.commit();
+
+ resourceInstanceData.getComponentInstDataDefinition().setName("myresource_2");
+ updateNode3 = titanDao.updateNode(resourceInstanceData, ComponentInstanceData.class);
+ assertTrue(updateNode3.isLeft());
+ // titanDao.commit();
+
+ Map<String, Object> props3 = new HashMap<>();
+ props3.put("positionX", "22");
+ Either<List<ComponentInstanceData>, TitanOperationStatus> byCriteria3 = titanDao
+ .getByCriteria(NodeTypeEnum.ResourceInstance, props3, ComponentInstanceData.class);
+ assertTrue(byCriteria3.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria3.right().value());
+
+ props3.put("positionX", "50");
+ byCriteria3 = titanDao.getByCriteria(NodeTypeEnum.ResourceInstance, props3, ComponentInstanceData.class);
+ assertTrue(byCriteria3.isLeft());
+
+ /////////////////////////// check integer ////////////////////////
+
+ ArtifactData artifactData = new ArtifactData();
+ artifactData.getArtifactDataDefinition().setUniqueId("ad234");
+ artifactData.getArtifactDataDefinition().setTimeout(100);
+
+ Either<ArtifactData, TitanOperationStatus> newNode4 = titanDao.createNode(artifactData, ArtifactData.class);
+ assertTrue(newNode4.isLeft());
+ log.debug("{}", newNode4.left().value());
+ // titanDao.commit();
+
+ artifactData.getArtifactDataDefinition().setTimeout(50);
+ Either<ArtifactData, TitanOperationStatus> updateNode4 = titanDao.updateNode(artifactData, ArtifactData.class);
+ assertTrue(updateNode4.isLeft());
+ // titanDao.commit();
+
+ Map<String, Object> props4 = new HashMap<>();
+ props4.put("timeout", 100);
+ Either<List<ArtifactData>, TitanOperationStatus> byCriteria4 = titanDao.getByCriteria(NodeTypeEnum.ArtifactRef,
+ props4, ArtifactData.class);
+ assertTrue(byCriteria4.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria4.right().value());
+
+ props4.put("timeout", 50);
+ byCriteria4 = titanDao.getByCriteria(NodeTypeEnum.ArtifactRef, props4, ArtifactData.class);
+ assertTrue(byCriteria4.isLeft());
+
+ titanDao.rollback();
+ }
+
+ @Test
+ public void testDuplicateResultUSeHasNotQueryDueToTitanBug() {
+
+ String name = "bbbb";
+
+ ResourceMetadataData resourceData1 = new ResourceMetadataData();
+ resourceData1.getMetadataDataDefinition().setUniqueId("A");
+ ((ResourceMetadataDataDefinition) resourceData1.getMetadataDataDefinition()).setAbstract(true);
+ resourceData1.getMetadataDataDefinition().setName(name);
+
+ Either<ResourceMetadataData, TitanOperationStatus> newNode1 = titanDao.createNode(resourceData1,
+ ResourceMetadataData.class);
+ assertTrue(newNode1.isLeft());
+ log.debug("{}", newNode1.left().value());
+ // titanDao.commit();
+
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), true);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanDao
+ .getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isLeft());
+ assertEquals("check one result returned", 1, byCriteria.left().value().size());
+ // titanDao.commit();
+
+ ResourceMetadataData resourceToUpdate = new ResourceMetadataData();
+ ((ResourceMetadataDataDefinition) resourceToUpdate.getMetadataDataDefinition()).setAbstract(false);
+ resourceToUpdate.getMetadataDataDefinition().setUniqueId("A");
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceToUpdate,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+ // titanDao.commit();
+
+ // no result where isAbstract = true
+ byCriteria = titanDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria.right().value());
+
+ // one result where isAbstract != true
+ byCriteria = titanDao.getByCriteria(NodeTypeEnum.Resource, null, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isLeft());
+ assertEquals("check one result returned", 1, byCriteria.left().value().size());
+
+ props.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), false);
+ byCriteria = titanDao.getByCriteria(NodeTypeEnum.Resource, null, props, ResourceMetadataData.class);
+ assertTrue(byCriteria.isRight());
+ assertEquals("check one result returned due to titan bug", TitanOperationStatus.NOT_FOUND,
+ byCriteria.right().value());
+
+ titanDao.rollback();
+
+ }
+
+}
diff --git a/catalog-dao/src/test/resources/application-context-test.xml b/catalog-dao/src/test/resources/application-context-test.xml
new file mode 100644
index 0000000000..468dab3763
--- /dev/null
+++ b/catalog-dao/src/test/resources/application-context-test.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
+
+ <util:properties id="elasticsearchConfig" location="classpath:elasticsearch.yml" />
+
+
+ <context:component-scan
+ base-package="
+ org.openecomp.sdc.be.dao.impl,
+ org.openecomp.sdc.be.dao.es,
+ org.openecomp.sdc.be.dao.neo4j,
+ org.openecomp.sdc.be.dao.titan,
+ org.openecomp.sdc.be.resources.impl
+ ">
+
+ </context:component-scan>
+
+</beans>
diff --git a/catalog-dao/src/test/resources/cassandra.yaml b/catalog-dao/src/test/resources/cassandra.yaml
new file mode 100644
index 0000000000..39f987152a
--- /dev/null
+++ b/catalog-dao/src/test/resources/cassandra.yaml
@@ -0,0 +1,801 @@
+# Cassandra storage config YAML
+
+# NOTE:
+# See http://wiki.apache.org/cassandra/StorageConfiguration for
+# full explanations of configuration directives
+# /NOTE
+
+# The name of the cluster. This is mainly used to prevent machines in
+# one logical cluster from joining another.
+cluster_name: 'Test Cluster'
+
+# This defines the number of tokens randomly assigned to this node on the ring
+# The more tokens, relative to other nodes, the larger the proportion of data
+# that this node will store. You probably want all nodes to have the same number
+# of tokens assuming they have equal hardware capability.
+#
+# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
+# and will use the initial_token as described below.
+#
+# Specifying initial_token will override this setting on the node's initial start,
+# on subsequent starts, this setting will apply even if initial token is set.
+#
+# If you already have a cluster with 1 token per node, and wish to migrate to
+# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
+num_tokens: 256
+
+# initial_token allows you to specify tokens manually. While you can use # it with
+# vnodes (num_tokens > 1, above) -- in which case you should provide a
+# comma-separated list -- it's primarily used when adding nodes # to legacy clusters
+# that do not have vnodes enabled.
+# initial_token:
+
+# See http://wiki.apache.org/cassandra/HintedHandoff
+# May either be "true" or "false" to enable globally, or contain a list
+# of data centers to enable per-datacenter.
+# hinted_handoff_enabled: DC1,DC2
+hinted_handoff_enabled: true
+# this defines the maximum amount of time a dead host will have hints
+# generated. After it has been dead this long, new hints for it will not be
+# created until it has been seen alive and gone down again.
+max_hint_window_in_ms: 10800000 # 3 hours
+# Maximum throttle in KBs per second, per delivery thread. This will be
+# reduced proportionally to the number of nodes in the cluster. (If there
+# are two nodes in the cluster, each delivery thread will use the maximum
+# rate; if there are three, each will throttle to half of the maximum,
+# since we expect two nodes to be delivering hints simultaneously.)
+hinted_handoff_throttle_in_kb: 1024
+# Number of threads with which to deliver hints;
+# Consider increasing this number when you have multi-dc deployments, since
+# cross-dc handoff tends to be slower
+max_hints_delivery_threads: 2
+
+# Maximum throttle in KBs per second, total. This will be
+# reduced proportionally to the number of nodes in the cluster.
+batchlog_replay_throttle_in_kb: 1024
+
+# Authentication backend, implementing IAuthenticator; used to identify users
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
+# PasswordAuthenticator}.
+#
+# - AllowAllAuthenticator performs no checks - set it to disable authentication.
+# - PasswordAuthenticator relies on username/password pairs to authenticate
+# users. It keeps usernames and hashed passwords in system_auth.credentials table.
+# Please increase system_auth keyspace replication factor if you use this authenticator.
+authenticator: AllowAllAuthenticator
+
+# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
+# CassandraAuthorizer}.
+#
+# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
+# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
+# increase system_auth keyspace replication factor if you use this authorizer.
+authorizer: AllowAllAuthorizer
+
+# Validity period for permissions cache (fetching permissions can be an
+# expensive operation depending on the authorizer, CassandraAuthorizer is
+# one example). Defaults to 2000, set to 0 to disable.
+# Will be disabled automatically for AllowAllAuthorizer.
+permissions_validity_in_ms: 2000
+
+# Refresh interval for permissions cache (if enabled).
+# After this interval, cache entries become eligible for refresh. Upon next
+# access, an async reload is scheduled and the old value returned until it
+# completes. If permissions_validity_in_ms is non-zero, then this must be
+# also.
+# Defaults to the same value as permissions_validity_in_ms.
+# permissions_update_interval_in_ms: 1000
+
+# The partitioner is responsible for distributing groups of rows (by
+# partition key) across nodes in the cluster. You should leave this
+# alone for new clusters. The partitioner can NOT be changed without
+# reloading all data, so when upgrading you should set this to the
+# same partitioner you were already using.
+#
+# Besides Murmur3Partitioner, partitioners included for backwards
+# compatibility include RandomPartitioner, ByteOrderedPartitioner, and
+# OrderPreservingPartitioner.
+#
+partitioner: org.apache.cassandra.dht.Murmur3Partitioner
+
+# Directories where Cassandra should store data on disk. Cassandra
+# will spread data evenly across them, subject to the granularity of
+# the configured compaction strategy.
+# If not set, the default directory is $CASSANDRA_HOME/data/data.
+# data_file_directories:
+# - /var/lib/cassandra/data
+
+# commit log. when running on magnetic HDD, this should be a
+# separate spindle than the data directories.
+# If not set, the default directory is $CASSANDRA_HOME/data/commitlog.
+# commitlog_directory: /var/lib/cassandra/commitlog
+
+# policy for data disk failures:
+# die: shut down gossip and Thrift and kill the JVM for any fs errors or
+# single-sstable errors, so the node can be replaced.
+# stop_paranoid: shut down gossip and Thrift even for single-sstable errors.
+# stop: shut down gossip and Thrift, leaving the node effectively dead, but
+# can still be inspected via JMX.
+# best_effort: stop using the failed disk and respond to requests based on
+# remaining available sstables. This means you WILL see obsolete
+# data at CL.ONE!
+# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
+disk_failure_policy: stop
+
+# policy for commit disk failures:
+# die: shut down gossip and Thrift and kill the JVM, so the node can be replaced.
+# stop: shut down gossip and Thrift, leaving the node effectively dead, but
+# can still be inspected via JMX.
+# stop_commit: shutdown the commit log, letting writes collect but
+# continuing to service reads, as in pre-2.0.5 Cassandra
+# ignore: ignore fatal errors and let the batches fail
+commit_failure_policy: stop
+
+# Maximum size of the key cache in memory.
+#
+# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
+# minimum, sometimes more. The key cache is fairly tiny for the amount of
+# time it saves, so it's worthwhile to use it at large numbers.
+# The row cache saves even more time, but must contain the entire row,
+# so it is extremely space-intensive. It's best to only use the
+# row cache if you have hot rows or static rows.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
+key_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# save the key cache. Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 14400 or 4 hours.
+key_cache_save_period: 14400
+
+# Number of keys from the key cache to save
+# Disabled by default, meaning all keys are going to be saved
+# key_cache_keys_to_save: 100
+
+# Maximum size of the row cache in memory.
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is 0, to disable row caching.
+row_cache_size_in_mb: 0
+
+# Duration in seconds after which Cassandra should
+# save the row cache. Caches are saved to saved_caches_directory as specified
+# in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 0 to disable saving the row cache.
+row_cache_save_period: 0
+
+# Number of keys from the row cache to save
+# Disabled by default, meaning all keys are going to be saved
+# row_cache_keys_to_save: 100
+
+# Maximum size of the counter cache in memory.
+#
+# Counter cache helps to reduce counter locks' contention for hot counter cells.
+# In case of RF = 1 a counter cache hit will cause Cassandra to skip the read before
+# write entirely. With RF > 1 a counter cache hit will still help to reduce the duration
+# of the lock hold, helping with hot counter cell updates, but will not allow skipping
+# the read entirely. Only the local (clock, count) tuple of a counter cell is kept
+# in memory, not the whole counter, so it's relatively cheap.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(2.5% of Heap (in MB), 50MB)). Set to 0 to disable counter cache.
+# NOTE: if you perform counter deletes and rely on low gcgs, you should disable the counter cache.
+counter_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# save the counter cache (keys only). Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Default is 7200 or 2 hours.
+counter_cache_save_period: 7200
+
+# Number of keys from the counter cache to save
+# Disabled by default, meaning all keys are going to be saved
+# counter_cache_keys_to_save: 100
+
+# The off-heap memory allocator. Affects storage engine metadata as
+# well as caches. Experiments show that JEMAlloc saves some memory
+# than the native GCC allocator (i.e., JEMalloc is more
+# fragmentation-resistant).
+#
+# Supported values are: NativeAllocator, JEMallocAllocator
+#
+# If you intend to use JEMallocAllocator you have to install JEMalloc as library and
+# modify cassandra-env.sh as directed in the file.
+#
+# Defaults to NativeAllocator
+# memory_allocator: NativeAllocator
+
+# saved caches
+# If not set, the default directory is $CASSANDRA_HOME/data/saved_caches.
+# saved_caches_directory: /var/lib/cassandra/saved_caches
+
+# commitlog_sync may be either "periodic" or "batch."
+#
+# When in batch mode, Cassandra won't ack writes until the commit log
+# has been fsynced to disk. It will wait
+# commitlog_sync_batch_window_in_ms milliseconds between fsyncs.
+# This window should be kept short because the writer threads will
+# be unable to do extra work while waiting. (You may need to increase
+# concurrent_writes for the same reason.)
+#
+# commitlog_sync: batch
+# commitlog_sync_batch_window_in_ms: 2
+#
+# the other option is "periodic" where writes may be acked immediately
+# and the CommitLog is simply synced every commitlog_sync_period_in_ms
+# milliseconds.
+commitlog_sync: periodic
+commitlog_sync_period_in_ms: 10000
+
+# The size of the individual commitlog file segments. A commitlog
+# segment may be archived, deleted, or recycled once all the data
+# in it (potentially from each columnfamily in the system) has been
+# flushed to sstables.
+#
+# The default size is 32, which is almost always fine, but if you are
+# archiving commitlog segments (see commitlog_archiving.properties),
+# then you probably want a finer granularity of archiving; 8 or 16 MB
+# is reasonable.
+commitlog_segment_size_in_mb: 32
+
+# any class that implements the SeedProvider interface and has a
+# constructor that takes a Map<String, String> of parameters will do.
+seed_provider:
+ # Addresses of hosts that are deemed contact points.
+ # Cassandra nodes use this list of hosts to find each other and learn
+ # the topology of the ring. You must change this if you are running
+ # multiple nodes!
+ - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+ parameters:
+ # seeds is actually a comma-delimited list of addresses.
+ # Ex: "<ip1>,<ip2>,<ip3>"
+ - seeds: "127.0.0.1"
+
+# For workloads with more data than can fit in memory, Cassandra's
+# bottleneck will be reads that need to fetch data from
+# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
+# order to allow the operations to enqueue low enough in the stack
+# that the OS and drives can reorder them. Same applies to
+# "concurrent_counter_writes", since counter writes read the current
+# values before incrementing and writing them back.
+#
+# On the other hand, since writes are almost never IO bound, the ideal
+# number of "concurrent_writes" is dependent on the number of cores in
+# your system; (8 * number_of_cores) is a good rule of thumb.
+concurrent_reads: 32
+concurrent_writes: 32
+concurrent_counter_writes: 32
+
+# Total memory to use for sstable-reading buffers. Defaults to
+# the smaller of 1/4 of heap or 512MB.
+# file_cache_size_in_mb: 512
+
+# Total permitted memory to use for memtables. Cassandra will stop
+# accepting writes when the limit is exceeded until a flush completes,
+# and will trigger a flush based on memtable_cleanup_threshold
+# If omitted, Cassandra will set both to 1/4 the size of the heap.
+# memtable_heap_space_in_mb: 2048
+# memtable_offheap_space_in_mb: 2048
+
+# Ratio of occupied non-flushing memtable size to total permitted size
+# that will trigger a flush of the largest memtable. Lager mct will
+# mean larger flushes and hence less compaction, but also less concurrent
+# flush activity which can make it difficult to keep your disks fed
+# under heavy write load.
+#
+# memtable_cleanup_threshold defaults to 1 / (memtable_flush_writers + 1)
+# memtable_cleanup_threshold: 0.11
+
+# Specify the way Cassandra allocates and manages memtable memory.
+# Options are:
+# heap_buffers: on heap nio buffers
+# offheap_buffers: off heap (direct) nio buffers
+# offheap_objects: native memory, eliminating nio buffer heap overhead
+memtable_allocation_type: heap_buffers
+
+# Total space to use for commitlogs. Since commitlog segments are
+# mmapped, and hence use up address space, the default size is 32
+# on 32-bit JVMs, and 8192 on 64-bit JVMs.
+#
+# If space gets above this value (it will round up to the next nearest
+# segment multiple), Cassandra will flush every dirty CF in the oldest
+# segment and remove it. So a small total commitlog space will tend
+# to cause more flush activity on less-active columnfamilies.
+# commitlog_total_space_in_mb: 8192
+
+# This sets the amount of memtable flush writer threads. These will
+# be blocked by disk io, and each one will hold a memtable in memory
+# while blocked.
+#
+# memtable_flush_writers defaults to the smaller of (number of disks,
+# number of cores), with a minimum of 2 and a maximum of 8.
+#
+# If your data directories are backed by SSD, you should increase this
+# to the number of cores.
+#memtable_flush_writers: 8
+
+# A fixed memory pool size in MB for for SSTable index summaries. If left
+# empty, this will default to 5% of the heap size. If the memory usage of
+# all index summaries exceeds this limit, SSTables with low read rates will
+# shrink their index summaries in order to meet this limit. However, this
+# is a best-effort process. In extreme conditions Cassandra may need to use
+# more than this amount of memory.
+index_summary_capacity_in_mb:
+
+# How frequently index summaries should be resampled. This is done
+# periodically to redistribute memory from the fixed-size pool to sstables
+# proportional their recent read rates. Setting to -1 will disable this
+# process, leaving existing index summaries at their current sampling level.
+index_summary_resize_interval_in_minutes: 60
+
+# Whether to, when doing sequential writing, fsync() at intervals in
+# order to force the operating system to flush the dirty
+# buffers. Enable this to avoid sudden dirty buffer flushing from
+# impacting read latencies. Almost always a good idea on SSDs; not
+# necessarily on platters.
+trickle_fsync: false
+trickle_fsync_interval_in_kb: 10240
+
+# TCP port, for commands and data
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+storage_port: 7000
+
+# SSL port, for encrypted communication. Unused unless enabled in
+# encryption_options
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+ssl_storage_port: 7001
+
+# Address or interface to bind to and tell other Cassandra nodes to connect to.
+# You _must_ change this if you want multiple nodes to be able to communicate!
+#
+# Set listen_address OR listen_interface, not both. Interfaces must correspond
+# to a single address, IP aliasing is not supported.
+#
+# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
+# will always do the Right Thing _if_ the node is properly configured
+# (hostname, name resolution, etc), and the Right Thing is to use the
+# address associated with the hostname (it might not be).
+#
+# Setting listen_address to 0.0.0.0 is always wrong.
+#
+# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
+# you can specify which should be chosen using listen_interface_prefer_ipv6. If false the first ipv4
+# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
+# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
+listen_address: localhost
+# listen_interface: eth0
+# listen_interface_prefer_ipv6: false
+
+# Address to broadcast to other Cassandra nodes
+# Leaving this blank will set it to the same value as listen_address
+# broadcast_address: 1.2.3.4
+
+# Internode authentication backend, implementing IInternodeAuthenticator;
+# used to allow/disallow connections from peer nodes.
+# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
+
+# Whether to start the native transport server.
+# Please note that the address on which the native transport is bound is the
+# same as the rpc_address. The port however is different and specified below.
+start_native_transport: true
+# port for the CQL native transport to listen for clients on
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+native_transport_port: 9042
+# The maximum threads for handling requests when the native transport is used.
+# This is similar to rpc_max_threads though the default differs slightly (and
+# there is no native_transport_min_threads, idle threads will always be stopped
+# after 30 seconds).
+# native_transport_max_threads: 128
+#
+# The maximum size of allowed frame. Frame (requests) larger than this will
+# be rejected as invalid. The default is 256MB.
+# native_transport_max_frame_size_in_mb: 256
+
+# The maximum number of concurrent client connections.
+# The default is -1, which means unlimited.
+# native_transport_max_concurrent_connections: -1
+
+# The maximum number of concurrent client connections per source ip.
+# The default is -1, which means unlimited.
+# native_transport_max_concurrent_connections_per_ip: -1
+
+# Whether to start the thrift rpc server.
+start_rpc: true
+
+# The address or interface to bind the Thrift RPC service and native transport
+# server to.
+#
+# Set rpc_address OR rpc_interface, not both. Interfaces must correspond
+# to a single address, IP aliasing is not supported.
+#
+# Leaving rpc_address blank has the same effect as on listen_address
+# (i.e. it will be based on the configured hostname of the node).
+#
+# Note that unlike listen_address, you can specify 0.0.0.0, but you must also
+# set broadcast_rpc_address to a value other than 0.0.0.0.
+#
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+#
+# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
+# you can specify which should be chosen using rpc_interface_prefer_ipv6. If false the first ipv4
+# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
+# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
+rpc_address: 0.0.0.0
+# rpc_interface: eth1
+# rpc_interface_prefer_ipv6: false
+
+# port for Thrift to listen for clients on
+rpc_port: 9160
+
+# RPC address to broadcast to drivers and other Cassandra nodes. This cannot
+# be set to 0.0.0.0. If left blank, this will be set to the value of
+# rpc_address. If rpc_address is set to 0.0.0.0, broadcast_rpc_address must
+# be set.
+broadcast_rpc_address: 127.0.0.1
+
+
+
+
+
+
+# enable or disable keepalive on rpc/native connections
+rpc_keepalive: true
+
+# Cassandra provides two out-of-the-box options for the RPC Server:
+#
+# sync -> One thread per thrift connection. For a very large number of clients, memory
+# will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size
+# per thread, and that will correspond to your use of virtual memory (but physical memory
+# may be limited depending on use of stack space).
+#
+# hsha -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
+# asynchronously using a small number of threads that does not vary with the amount
+# of thrift clients (and thus scales well to many clients). The rpc requests are still
+# synchronous (one thread per active request). If hsha is selected then it is essential
+# that rpc_max_threads is changed from the default value of unlimited.
+#
+# The default is sync because on Windows hsha is about 30% slower. On Linux,
+# sync/hsha performance is about the same, with hsha of course using less memory.
+#
+# Alternatively, can provide your own RPC server by providing the fully-qualified class name
+# of an o.a.c.t.TServerFactory that can create an instance of it.
+rpc_server_type: sync
+
+# Uncomment rpc_min|max_thread to set request pool size limits.
+#
+# Regardless of your choice of RPC server (see above), the number of maximum requests in the
+# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
+# RPC server, it also dictates the number of clients that can be connected at all).
+#
+# The default is unlimited and thus provides no protection against clients overwhelming the server. You are
+# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
+# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
+#
+# rpc_min_threads: 16
+# rpc_max_threads: 2048
+
+# uncomment to set socket buffer sizes on rpc connections
+# rpc_send_buff_size_in_bytes:
+# rpc_recv_buff_size_in_bytes:
+
+# Uncomment to set socket buffer size for internode communication
+# Note that when setting this, the buffer size is limited by net.core.wmem_max
+# and when not setting it it is defined by net.ipv4.tcp_wmem
+# See:
+# /proc/sys/net/core/wmem_max
+# /proc/sys/net/core/rmem_max
+# /proc/sys/net/ipv4/tcp_wmem
+# /proc/sys/net/ipv4/tcp_wmem
+# and: man tcp
+# internode_send_buff_size_in_bytes:
+# internode_recv_buff_size_in_bytes:
+
+# Frame size for thrift (maximum message length).
+thrift_framed_transport_size_in_mb: 15
+
+# Set to true to have Cassandra create a hard link to each sstable
+# flushed or streamed locally in a backups/ subdirectory of the
+# keyspace data. Removing these links is the operator's
+# responsibility.
+incremental_backups: false
+
+# Whether or not to take a snapshot before each compaction. Be
+# careful using this option, since Cassandra won't clean up the
+# snapshots for you. Mostly useful if you're paranoid when there
+# is a data format change.
+snapshot_before_compaction: false
+
+# Whether or not a snapshot is taken of the data before keyspace truncation
+# or dropping of column families. The STRONGLY advised default of true
+# should be used to provide data safety. If you set this flag to false, you will
+# lose data on truncation or drop.
+auto_snapshot: true
+
+# When executing a scan, within or across a partition, we need to keep the
+# tombstones seen in memory so we can return them to the coordinator, which
+# will use them to make sure other replicas also know about the deleted rows.
+# With workloads that generate a lot of tombstones, this can cause performance
+# problems and even exaust the server heap.
+# (http://www.datastax.com/dev/blog/cassandra-anti-patterns-queues-and-queue-like-datasets)
+# Adjust the thresholds here if you understand the dangers and want to
+# scan more tombstones anyway. These thresholds may also be adjusted at runtime
+# using the StorageService mbean.
+tombstone_warn_threshold: 1000
+tombstone_failure_threshold: 100000
+
+# Granularity of the collation index of rows within a partition.
+# Increase if your rows are large, or if you have a very large
+# number of rows per partition. The competing goals are these:
+# 1) a smaller granularity means more index entries are generated
+# and looking up rows withing the partition by collation column
+# is faster
+# 2) but, Cassandra will keep the collation index in memory for hot
+# rows (as part of the key cache), so a larger granularity means
+# you can cache more hot rows
+column_index_size_in_kb: 64
+
+
+# Log WARN on any batch size exceeding this value. 5kb per batch by default.
+# Caution should be taken on increasing the size of this threshold as it can lead to node instability.
+batch_size_warn_threshold_in_kb: 5
+
+# Number of simultaneous compactions to allow, NOT including
+# validation "compactions" for anti-entropy repair. Simultaneous
+# compactions can help preserve read performance in a mixed read/write
+# workload, by mitigating the tendency of small sstables to accumulate
+# during a single long running compactions. The default is usually
+# fine and if you experience problems with compaction running too
+# slowly or too fast, you should look at
+# compaction_throughput_mb_per_sec first.
+#
+# concurrent_compactors defaults to the smaller of (number of disks,
+# number of cores), with a minimum of 2 and a maximum of 8.
+#
+# If your data directories are backed by SSD, you should increase this
+# to the number of cores.
+#concurrent_compactors: 1
+
+# Throttles compaction to the given total throughput across the entire
+# system. The faster you insert data, the faster you need to compact in
+# order to keep the sstable count down, but in general, setting this to
+# 16 to 32 times the rate you are inserting data is more than sufficient.
+# Setting this to 0 disables throttling. Note that this account for all types
+# of compaction, including validation compaction.
+compaction_throughput_mb_per_sec: 16
+
+# When compacting, the replacement sstable(s) can be opened before they
+# are completely written, and used in place of the prior sstables for
+# any range that has been written. This helps to smoothly transfer reads
+# between the sstables, reducing page cache churn and keeping hot rows hot
+sstable_preemptive_open_interval_in_mb: 50
+
+# Throttles all outbound streaming file transfers on this node to the
+# given total throughput in Mbps. This is necessary because Cassandra does
+# mostly sequential IO when streaming data during bootstrap or repair, which
+# can lead to saturating the network connection and degrading rpc performance.
+# When unset, the default is 200 Mbps or 25 MB/s.
+# stream_throughput_outbound_megabits_per_sec: 200
+
+# Throttles all streaming file transfer between the datacenters,
+# this setting allows users to throttle inter dc stream throughput in addition
+# to throttling all network stream traffic as configured with
+# stream_throughput_outbound_megabits_per_sec
+# inter_dc_stream_throughput_outbound_megabits_per_sec:
+
+# How long the coordinator should wait for read operations to complete
+read_request_timeout_in_ms: 5000
+# How long the coordinator should wait for seq or index scans to complete
+range_request_timeout_in_ms: 10000
+# How long the coordinator should wait for writes to complete
+write_request_timeout_in_ms: 2000
+# How long the coordinator should wait for counter writes to complete
+counter_write_request_timeout_in_ms: 5000
+# How long a coordinator should continue to retry a CAS operation
+# that contends with other proposals for the same row
+cas_contention_timeout_in_ms: 1000
+# How long the coordinator should wait for truncates to complete
+# (This can be much longer, because unless auto_snapshot is disabled
+# we need to flush first so we can snapshot before removing the data.)
+truncate_request_timeout_in_ms: 60000
+# The default timeout for other, miscellaneous operations
+request_timeout_in_ms: 10000
+
+# Enable operation timeout information exchange between nodes to accurately
+# measure request timeouts. If disabled, replicas will assume that requests
+# were forwarded to them instantly by the coordinator, which means that
+# under overload conditions we will waste that much extra time processing
+# already-timed-out requests.
+#
+# Warning: before enabling this property make sure to ntp is installed
+# and the times are synchronized between the nodes.
+cross_node_timeout: false
+
+# Enable socket timeout for streaming operation.
+# When a timeout occurs during streaming, streaming is retried from the start
+# of the current file. This _can_ involve re-streaming an important amount of
+# data, so you should avoid setting the value too low.
+# Default value is 0, which never timeout streams.
+# streaming_socket_timeout_in_ms: 0
+
+# phi value that must be reached for a host to be marked down.
+# most users should never need to adjust this.
+# phi_convict_threshold: 8
+
+# endpoint_snitch -- Set this to a class that implements
+# IEndpointSnitch. The snitch has two functions:
+# - it teaches Cassandra enough about your network topology to route
+# requests efficiently
+# - it allows Cassandra to spread replicas around your cluster to avoid
+# correlated failures. It does this by grouping machines into
+# "datacenters" and "racks." Cassandra will do its best not to have
+# more than one replica on the same "rack" (which may not actually
+# be a physical location)
+#
+# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
+# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
+# ARE PLACED.
+#
+# Out of the box, Cassandra provides
+# - SimpleSnitch:
+# Treats Strategy order as proximity. This can improve cache
+# locality when disabling read repair. Only appropriate for
+# single-datacenter deployments.
+# - GossipingPropertyFileSnitch
+# This should be your go-to snitch for production use. The rack
+# and datacenter for the local node are defined in
+# cassandra-rackdc.properties and propagated to other nodes via
+# gossip. If cassandra-topology.properties exists, it is used as a
+# fallback, allowing migration from the PropertyFileSnitch.
+# - PropertyFileSnitch:
+# Proximity is determined by rack and data center, which are
+# explicitly configured in cassandra-topology.properties.
+# - Ec2Snitch:
+# Appropriate for EC2 deployments in a single Region. Loads Region
+# and Availability Zone information from the EC2 API. The Region is
+# treated as the datacenter, and the Availability Zone as the rack.
+# Only private IPs are used, so this will not work across multiple
+# Regions.
+# - Ec2MultiRegionSnitch:
+# Uses public IPs as broadcast_address to allow cross-region
+# connectivity. (Thus, you should set seed addresses to the public
+# IP as well.) You will need to open the storage_port or
+# ssl_storage_port on the public IP firewall. (For intra-Region
+# traffic, Cassandra will switch to the private IP after
+# establishing a connection.)
+# - RackInferringSnitch:
+# Proximity is determined by rack and data center, which are
+# assumed to correspond to the 3rd and 2nd octet of each node's IP
+# address, respectively. Unless this happens to match your
+# deployment conventions, this is best used as an example of
+# writing a custom Snitch class and is provided in that spirit.
+#
+# You can use a custom Snitch by setting this to the full class name
+# of the snitch, which will be assumed to be on your classpath.
+endpoint_snitch: SimpleSnitch
+
+# controls how often to perform the more expensive part of host score
+# calculation
+dynamic_snitch_update_interval_in_ms: 100
+# controls how often to reset all host scores, allowing a bad host to
+# possibly recover
+dynamic_snitch_reset_interval_in_ms: 600000
+# if set greater than zero and read_repair_chance is < 1.0, this will allow
+# 'pinning' of replicas to hosts in order to increase cache capacity.
+# The badness threshold will control how much worse the pinned host has to be
+# before the dynamic snitch will prefer other replicas over it. This is
+# expressed as a double which represents a percentage. Thus, a value of
+# 0.2 means Cassandra would continue to prefer the static snitch values
+# until the pinned host was 20% worse than the fastest.
+dynamic_snitch_badness_threshold: 0.1
+
+# request_scheduler -- Set this to a class that implements
+# RequestScheduler, which will schedule incoming client requests
+# according to the specific policy. This is useful for multi-tenancy
+# with a single Cassandra cluster.
+# NOTE: This is specifically for requests from the client and does
+# not affect inter node communication.
+# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
+# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
+# client requests to a node with a separate queue for each
+# request_scheduler_id. The scheduler is further customized by
+# request_scheduler_options as described below.
+request_scheduler: org.apache.cassandra.scheduler.NoScheduler
+
+# Scheduler Options vary based on the type of scheduler
+# NoScheduler - Has no options
+# RoundRobin
+# - throttle_limit -- The throttle_limit is the number of in-flight
+# requests per client. Requests beyond
+# that limit are queued up until
+# running requests can complete.
+# The value of 80 here is twice the number of
+# concurrent_reads + concurrent_writes.
+# - default_weight -- default_weight is optional and allows for
+# overriding the default which is 1.
+# - weights -- Weights are optional and will default to 1 or the
+# overridden default_weight. The weight translates into how
+# many requests are handled during each turn of the
+# RoundRobin, based on the scheduler id.
+#
+# request_scheduler_options:
+# throttle_limit: 80
+# default_weight: 5
+# weights:
+# Keyspace1: 1
+# Keyspace2: 5
+
+# request_scheduler_id -- An identifier based on which to perform
+# the request scheduling. Currently the only valid option is keyspace.
+# request_scheduler_id: keyspace
+
+# Enable or disable inter-node encryption
+# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
+# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
+# suite for authentication, key exchange and encryption of the actual data transfers.
+# Use the DHE/ECDHE ciphers if running in FIPS 140 compliant mode.
+# NOTE: No custom encryption options are enabled at the moment
+# The available internode options are : all, none, dc, rack
+#
+# If set to dc cassandra will encrypt the traffic between the DCs
+# If set to rack cassandra will encrypt the traffic between the racks
+#
+# The passwords used in these options must match the passwords used when generating
+# the keystore and truststore. For instructions on generating these files, see:
+# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
+#
+server_encryption_options:
+ internode_encryption: none
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ truststore: conf/.truststore
+ truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
+ # require_client_auth: false
+
+# enable or disable client/server encryption.
+client_encryption_options:
+ enabled: false
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ # require_client_auth: false
+ # Set trustore and truststore_password if require_client_auth is true
+ # truststore: conf/.truststore
+ # truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
+
+# internode_compression controls whether traffic between nodes is
+# compressed.
+# can be: all - all traffic is compressed
+# dc - traffic between different datacenters is compressed
+# none - nothing is compressed.
+internode_compression: all
+
+# Enable or disable tcp_nodelay for inter-dc communication.
+# Disabling it will result in larger (but fewer) network packets being sent,
+# reducing overhead from the TCP protocol itself, at the cost of increasing
+# latency if you block for cross-datacenter responses.
+inter_dc_tcp_nodelay: false
diff --git a/catalog-dao/src/test/resources/config/catalog-dao/configuration.yaml b/catalog-dao/src/test/resources/config/catalog-dao/configuration.yaml
new file mode 100644
index 0000000000..0b43e3b8c6
--- /dev/null
+++ b/catalog-dao/src/test/resources/config/catalog-dao/configuration.yaml
@@ -0,0 +1,120 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+# catalog backend hostname
+beFqdn: 172.20.43.124:8080
+
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: src/main/resources/config/titan.properties
+titanInMemoryGraph: true
+titanLockTimeout: 30
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+#Neoj4
+neo4j:
+ host: neo4jhost
+ port: 7474
+ user: neo4j
+ password: "12345"
+ maxHttpConnection: 100
+ maxHttpPerRoute: 20
+
+cassandraConfig:
+ cassandraHosts: ['mtanjv9sdcg44']
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: false
+ username: koko
+ password: bobo
+ ssl: false
+ truststorePath : /path/path
+ truststorePassword : 123123
+ keySpaces:
+ - { name: sdcaudit, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+ - { name: sdcartifact, replicationStrategy: SimpleStrategy, replicationInfo: ['1']}
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+
+switchoverDetector:
+ gBeFqdn: AIO-BE.ecomp.idns.cip
+ gFeFqdn: AIO-FE.ecomp.idns.cip
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/update_network?user=root"
+ publishNetworkBody: '{"note":"publish network"}'
+ groups:
+ beSet: { changePriorityUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"name":"AIO-BE.ecomp.idns.cip","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-BE.ecomp.idns.cip","no_ad_redirection":false,"v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_be","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_be"],"failover_policy":["FAILALL"]},"comment":"AIO BE G-fqdn","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "http://cora.web/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip?user=root",
+ changePriorityBody: '{"comment":"AIO G-fqdn","name":"AIO-FE.ecomp.idns.cip","v4groups":{"failover_groups":["/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_mg_fe","/crt/CipDomain.ECOMP-ASDC-DEVST/config/groups/group_bs_fe"],"failover_policy":["FAILALL"]},"no_ad_redirection":false,"intended_app_proto":"DNS","uri":"/crt/CipDomain.ECOMP-ASDC-DEVST/config/sites/AIO-FE.ecomp.idns.cip"}'}
+
+applicationL1Cache:
+ datatypes:
+ enabled: true
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: true
+ catalogL1Cache:
+ enabled: true
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 60
+ waitOnShutDownInMinutes: 30
+ numberOfCacheWorkers: 4
+toscaValidators:
+ stringMaxLength: 1024 \ No newline at end of file
diff --git a/catalog-dao/src/test/resources/config/catalog-dao/ecomp-error-configuration.yaml b/catalog-dao/src/test/resources/config/catalog-dao/ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..9d7cd74a2b
--- /dev/null
+++ b/catalog-dao/src/test/resources/config/catalog-dao/ecomp-error-configuration.yaml
@@ -0,0 +1,383 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+## Range of BE codes - 3010-7999
+
+errors:
+
+ BeRestApiGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4000,
+ severity: ERROR,
+ description: "Unexpected error during BE REST API execution",
+ alarmSeverity: CRITICAL
+ }
+
+ BeHealthCheckError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3010,
+ severity: ERROR,
+ description: "Error during BE Health Check",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInitializationError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4019,
+ severity: ERROR,
+ description: "Catalog-BE was not initialized properly",
+ alarmSeverity: CRITICAL
+ }
+
+ BeResourceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3011,
+ severity: ERROR,
+ description: "Mandatory resource %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeServiceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3012,
+ severity: ERROR,
+ description: "Mandatory service %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedAddingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3013,
+ severity: ERROR,
+ description: "Failed to add resource instance of resource %s to service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeIncorrectServiceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3014,
+ severity: ERROR,
+ description: "Service %s is not valid",
+ alarmSeverity: MAJOR
+ }
+
+ BeRepositoryDeleteError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3015,
+ severity: ERROR,
+ description: "Failed to delete object %s from repository",
+ alarmSeverity: CRITICAL
+ }
+
+ BeRepositoryQueryError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3016,
+ severity: ERROR,
+ description: "Failed to fetch from repository %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeInvalidConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3017,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. Value configured is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4001,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3019,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebObjectNotFoundError: {
+ type: DATA_ERROR,
+ code: ASDC_4005,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Data not found: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3021,
+ severity: ERROR,
+ description: "Error occured in Distribution Engine. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebAuthenticationError: {
+ type: AUTHENTICATION_PROBLEM,
+ code: ASDC_4003,
+ severity: ERROR,
+ description: "Authentication problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebUnkownHostError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4002,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Cannot reach host %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineInvalidArtifactType: {
+ type: DATA_ERROR,
+ code: ASDC_4006,
+ severity: WARN,
+ description: "The artifact type %s does not appear in the list of valid artifacts %s",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4008,
+ severity: WARN,
+ description: "The type %s of %s is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidValueError: {
+ type: DATA_ERROR,
+ code: ASDC_3028,
+ severity: WARN,
+ description: "The value %s of %s from type %s is invalid",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeletingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3029,
+ severity: ERROR,
+ description: "Failed to delete resource instance %s from service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3030,
+ severity: FATAL,
+ description: "Configuration parameter %s is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeConfigurationInvalidListSizeError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3031,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. At least %s values shall be configured",
+ alarmSeverity: MAJOR
+ }
+
+ ErrorConfigFileFormat: {
+ type: CONFIG_ERROR,
+ code: ASDC_3032,
+ severity: ERROR,
+ description: "Error element not found in YAML name: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingArtifactInformationError: {
+ type: DATA_ERROR,
+ code: ASDC_4010,
+ severity: ERROR,
+ description: "Artifact uploaded has missing information. Missing %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4011,
+ severity: ERROR,
+ description: "Artifact %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactPayloadInvalid: {
+ type: DATA_ERROR,
+ code: ASDC_4012,
+ severity: ERROR,
+ description: "Payload of artifact uploaded is invalid (invalid MD5 or encryption)",
+ alarmSeverity: MAJOR
+ }
+
+ BeUserMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4009,
+ severity: ERROR,
+ description: "User %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactInformationInvalidError: {
+ type: DATA_ERROR,
+ code: ASDC_4013,
+ severity: ERROR,
+ description: "Input for artifact metadata is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeFailedAddingCapabilityTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4015,
+ severity: ERROR,
+ description: "Failed adding capability type",
+ alarmSeverity: CRITICAL
+ }
+
+ BeCapabilityTypeMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4016,
+ severity: ERROR,
+ description: "Capability Type %s not found",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInterfaceMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4020,
+ severity: ERROR,
+ description: "Interface %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeDaoSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4014,
+ severity: ERROR,
+ description: "Operation towards database failed",
+ alarmSeverity: CRITICAL
+ }
+
+ BeSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4017,
+ severity: ERROR,
+ description: "Unexpected error during operation",
+ alarmSeverity: CRITICAL
+ }
+
+ BeFailedLockObjectError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4007,
+ severity: WARN,
+ description: "Failed to lock object for update",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInvalidJsonInput: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4018,
+ severity: ERROR,
+ description: "Failed to convert json input to object",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4021,
+ severity: ERROR,
+ description: "Distribution %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeHealthCheckRecovery: {
+ type: RECOVERY,
+ code: ASDC_4022,
+ severity: INFO,
+ description: "BE Health Check Recovery",
+ alarmSeverity: INFORMATIONAL
+ }
+ BeFailedCreateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6000,
+ severity: ERROR,
+ description: "Failed to create node %s on graph. status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeFailedUpdateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6001,
+ severity: ERROR,
+ description: "Failed to update node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeleteNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6002,
+ severity: ERROR,
+ description: "Failed to delete node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedRetrieveNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6003,
+ severity: ERROR,
+ description: "Failed to retrieve node %s from graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeExecuteRollbackError: {
+ type: DATA_ERROR,
+ code: ASDC_6004,
+ severity: ERROR,
+ description: "Going to execute rollback on graph.",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindParentError: {
+ type: DATA_ERROR,
+ code: ASDC_6005,
+ severity: ERROR,
+ description: "Failed to find parent node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAllNodesError: {
+ type: DATA_ERROR,
+ code: ASDC_6006,
+ severity: ERROR,
+ description: "Failed to fetch all nodes with type %s of parent node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6007,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6008,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeComponentCleanerSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_6009,
+ severity: ERROR,
+ description: "Error occured in Component Cleaner Task. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+ \ No newline at end of file
diff --git a/catalog-dao/src/test/resources/elasticsearch.yml b/catalog-dao/src/test/resources/elasticsearch.yml
new file mode 100644
index 0000000000..e1808ad7cc
--- /dev/null
+++ b/catalog-dao/src/test/resources/elasticsearch.yml
@@ -0,0 +1,392 @@
+
+elasticSearch.local: true
+elasticSearch.transportclient: false
+cluster.name: elasticsearch_1_5_2222
+
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+discovery.zen.ping.unicast.hosts: 1.2.3.4
+transport.client.initial_nodes:
+ - 1.2.3.4:9300
+
+
+#plugin.types: "DeleteByQueryPlugin"
+
+##################### Elasticsearch Configuration Example #####################
+
+# This file contains an overview of various configuration settings,
+# targeted at operations staff. Application developers should
+# consult the guide at <http://elasticsearch.org/guide>.
+#
+# The installation procedure is covered at
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html>.
+#
+# Elasticsearch comes with reasonable defaults for most settings,
+# so you can try it out without bothering with configuration.
+#
+# Most of the time, these defaults are just fine for running a production
+# cluster. If you're fine-tuning your cluster, or wondering about the
+# effect of certain configuration option, please _do ask_ on the
+# mailing list or IRC channel [http://elasticsearch.org/community].
+
+# Any element in the configuration can be replaced with environment variables
+# by placing them in ${...} notation. For example:
+#
+# node.rack: ${RACK_ENV_VAR}
+
+# For information on supported formats and syntax for the config file, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html>
+
+
+################################### Cluster ###################################
+
+# Cluster name identifies your cluster for auto-discovery. If you're running
+# multiple clusters on the same network, make sure you're using unique names.
+#
+# cluster.name: elasticsearch
+
+
+#################################### Node #####################################
+
+# Node names are generated dynamically on startup, so you're relieved
+# from configuring them manually. You can tie this node to a specific name:
+#
+# node.name: "Franz Kafka"
+
+# Every node can be configured to allow or deny being eligible as the master,
+# and to allow or deny to store the data.
+#
+# Allow this node to be eligible as a master node (enabled by default):
+#
+# node.master: true
+#
+# Allow this node to store data (enabled by default):
+#
+# node.data: true
+
+# You can exploit these settings to design advanced cluster topologies.
+#
+# 1. You want this node to never become a master node, only to hold data.
+# This will be the "workhorse" of your cluster.
+#
+# node.master: false
+# node.data: true
+#
+# 2. You want this node to only serve as a master: to not store any data and
+# to have free resources. This will be the "coordinator" of your cluster.
+#
+# node.master: true
+# node.data: false
+#
+# 3. You want this node to be neither master nor data node, but
+# to act as a "search load balancer" (fetching data from nodes,
+# aggregating results, etc.)
+#
+# node.master: false
+# node.data: false
+
+# Use the Cluster Health API [http://localhost:9200/_cluster/health], the
+# Node Info API [http://localhost:9200/_nodes] or GUI tools
+# such as <http://www.elasticsearch.org/overview/marvel/>,
+# <http://github.com/karmi/elasticsearch-paramedic>,
+# <http://github.com/lukas-vlcek/bigdesk> and
+# <http://mobz.github.com/elasticsearch-head> to inspect the cluster state.
+
+# A node can have generic attributes associated with it, which can later be used
+# for customized shard allocation filtering, or allocation awareness. An attribute
+# is a simple key value pair, similar to node.key: value, here is an example:
+#
+# node.rack: rack314
+
+# By default, multiple nodes are allowed to start from the same installation location
+# to disable it, set the following:
+# node.max_local_storage_nodes: 1
+
+
+#################################### Index ####################################
+
+# You can set a number of options (such as shard/replica options, mapping
+# or analyzer definitions, translog settings, ...) for indices globally,
+# in this file.
+#
+# Note, that it makes more sense to configure index settings specifically for
+# a certain index, either when creating it or by using the index templates API.
+#
+# See <http://elasticsearch.org/guide/en/elasticsearch/reference/current/index-modules.html> and
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
+# for more information.
+
+# Set the number of shards (splits) of an index (5 by default):
+#
+# index.number_of_shards: 5
+
+# Set the number of replicas (additional copies) of an index (1 by default):
+#
+# index.number_of_replicas: 1
+
+# Note, that for development on a local machine, with small indices, it usually
+# makes sense to "disable" the distributed features:
+#
+index.number_of_shards: 1
+index.number_of_replicas: 0
+
+# These settings directly affect the performance of index and search operations
+# in your cluster. Assuming you have enough machines to hold shards and
+# replicas, the rule of thumb is:
+#
+# 1. Having more *shards* enhances the _indexing_ performance and allows to
+# _distribute_ a big index across machines.
+# 2. Having more *replicas* enhances the _search_ performance and improves the
+# cluster _availability_.
+#
+# The "number_of_shards" is a one-time setting for an index.
+#
+# The "number_of_replicas" can be increased or decreased anytime,
+# by using the Index Update Settings API.
+#
+# Elasticsearch takes care about load balancing, relocating, gathering the
+# results from nodes, etc. Experiment with different settings to fine-tune
+# your setup.
+
+# Use the Index Status API (<http://localhost:9200/A/_status>) to inspect
+# the index status.
+
+
+#################################### Paths ####################################
+path.home: /src/test/resources
+# Path to directory containing configuration (this file and logging.yml):
+#
+path.conf: /src/test/resources
+
+# Path to directory where to store index data allocated for this node.
+#
+path.data: target/esdata
+#
+# Can optionally include more than one location, causing data to be striped across
+# the locations (a la RAID 0) on a file level, favouring locations with most free
+# space on creation. For example:
+#
+# path.data: /path/to/data1,/path/to/data2
+
+# Path to temporary files:
+#
+path.work: /target/eswork
+
+# Path to log files:
+#
+path.logs: /target/eslogs
+
+# Path to where plugins are installed:
+#
+# path.plugins: /path/to/plugins
+
+
+#################################### Plugin ###################################
+
+# If a plugin listed here is not installed for current node, the node will not start.
+#
+# plugin.mandatory: mapper-attachments,lang-groovy
+
+
+################################### Memory ####################################
+
+# Elasticsearch performs poorly when JVM starts swapping: you should ensure that
+# it _never_ swaps.
+#
+# Set this property to true to lock the memory:
+#
+# bootstrap.mlockall: true
+
+# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set
+# to the same value, and that the machine has enough memory to allocate
+# for Elasticsearch, leaving enough memory for the operating system itself.
+#
+# You should also make sure that the Elasticsearch process is allowed to lock
+# the memory, eg. by using `ulimit -l unlimited`.
+
+
+############################## Network And HTTP ###############################
+
+# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens
+# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node
+# communication. (the range means that if the port is busy, it will automatically
+# try the next port).
+
+# Set the bind address specifically (IPv4 or IPv6):
+#
+# network.bind_host: 192.168.0.1
+
+# Set the address other nodes will use to communicate with this node. If not
+# set, it is automatically derived. It must point to an actual IP address.
+#
+# network.publish_host: 192.168.0.1
+
+# Set both 'bind_host' and 'publish_host':
+#
+# network.host: 192.168.0.1
+
+# Set a custom port for the node to node communication (9300 by default):
+#
+# transport.tcp.port: 9300
+
+# Enable compression for all communication between nodes (disabled by default):
+#
+# transport.tcp.compress: true
+
+# Set a custom port to listen for HTTP traffic:
+#
+# http.port: 9200
+
+# Set a custom allowed content length:
+#
+# http.max_content_length: 100mb
+
+# Disable HTTP completely:
+#
+# http.enabled: false
+
+
+################################### Gateway ###################################
+
+# The gateway allows for persisting the cluster state between full cluster
+# restarts. Every change to the state (such as adding an index) will be stored
+# in the gateway, and when the cluster starts up for the first time,
+# it will read its state from the gateway.
+
+# There are several types of gateway implementations. For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-gateway.html>.
+
+# The default gateway type is the "local" gateway (recommended):
+#
+# gateway.type: local
+
+# Settings below control how and when to start the initial recovery process on
+# a full cluster restart (to reuse as much local data as possible when using shared
+# gateway).
+
+# Allow recovery process after N nodes in a cluster are up:
+#
+gateway.recover_after_nodes: 1
+
+# Set the timeout to initiate the recovery process, once the N nodes
+# from previous setting are up (accepts time value):
+#
+# gateway.recover_after_time: 5m
+
+# Set how many nodes are expected in this cluster. Once these N nodes
+# are up (and recover_after_nodes is met), begin recovery process immediately
+# (without waiting for recover_after_time to expire):
+#
+gateway.expected_nodes: 1
+
+
+############################# Recovery Throttling #############################
+
+# These settings allow to control the process of shards allocation between
+# nodes during initial recovery, replica allocation, rebalancing,
+# or when adding and removing nodes.
+
+# Set the number of concurrent recoveries happening on a node:
+#
+# 1. During the initial recovery
+#
+# cluster.routing.allocation.node_initial_primaries_recoveries: 4
+#
+# 2. During adding/removing nodes, rebalancing, etc
+#
+# cluster.routing.allocation.node_concurrent_recoveries: 2
+
+# Set to throttle throughput when recovering (eg. 100mb, by default 20mb):
+#
+# indices.recovery.max_bytes_per_sec: 20mb
+
+# Set to limit the number of open concurrent streams when
+# recovering a shard from a peer:
+#
+# indices.recovery.concurrent_streams: 5
+
+
+################################## Discovery ##################################
+
+# Discovery infrastructure ensures nodes can be found within a cluster
+# and master node is elected. Multicast discovery is the default.
+
+# Set to ensure a node sees N other master eligible nodes to be considered
+# operational within the cluster. Its recommended to set it to a higher value
+# than 1 when running more than 2 nodes in the cluster.
+#
+# discovery.zen.minimum_master_nodes: 1
+
+# Set the time to wait for ping responses from other nodes when discovering.
+# Set this option to a higher value on a slow or congested network
+# to minimize discovery failures:
+#
+# discovery.zen.ping.timeout: 3s
+
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-zen.html>
+
+# Unicast discovery allows to explicitly control which nodes will be used
+# to discover the cluster. It can be used when multicast is not present,
+# or to restrict the cluster communication-wise.
+#
+# 1. Disable multicast discovery (enabled by default):
+#
+# discovery.zen.ping.multicast.enabled: false
+#
+# 2. Configure an initial list of master nodes in the cluster
+# to perform discovery when new nodes (master or data) are started:
+#
+# discovery.zen.ping.unicast.hosts: ["host1", "host2:port"]
+
+# EC2 discovery allows to use AWS EC2 API in order to perform discovery.
+#
+# You have to install the cloud-aws plugin for enabling the EC2 discovery.
+#
+# For more information, see
+# <http://elasticsearch.org/guide/en/elasticsearch/reference/current/modules-discovery-ec2.html>
+#
+# See <http://elasticsearch.org/tutorials/elasticsearch-on-ec2/>
+# for a step-by-step tutorial.
+
+# GCE discovery allows to use Google Compute Engine API in order to perform discovery.
+#
+# You have to install the cloud-gce plugin for enabling the GCE discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-gce>.
+
+# Azure discovery allows to use Azure API in order to perform discovery.
+#
+# You have to install the cloud-azure plugin for enabling the Azure discovery.
+#
+# For more information, see <https://github.com/elasticsearch/elasticsearch-cloud-azure>.
+
+################################## Slow Log ##################################
+
+# Shard level query and fetch threshold logging.
+
+#index.search.slowlog.threshold.query.warn: 10s
+#index.search.slowlog.threshold.query.info: 5s
+#index.search.slowlog.threshold.query.debug: 2s
+#index.search.slowlog.threshold.query.trace: 500ms
+
+#index.search.slowlog.threshold.fetch.warn: 1s
+#index.search.slowlog.threshold.fetch.info: 800ms
+#index.search.slowlog.threshold.fetch.debug: 500ms
+#index.search.slowlog.threshold.fetch.trace: 200ms
+
+#index.indexing.slowlog.threshold.index.warn: 10s
+#index.indexing.slowlog.threshold.index.info: 5s
+#index.indexing.slowlog.threshold.index.debug: 2s
+#index.indexing.slowlog.threshold.index.trace: 500ms
+
+################################## GC Logging ################################
+
+#monitor.jvm.gc.young.warn: 1000ms
+#monitor.jvm.gc.young.info: 700ms
+#monitor.jvm.gc.young.debug: 400ms
+
+#monitor.jvm.gc.old.warn: 10s
+#monitor.jvm.gc.old.info: 5s
+#monitor.jvm.gc.old.debug: 2s
+
diff --git a/catalog-dao/src/test/resources/images/apache.png b/catalog-dao/src/test/resources/images/apache.png
new file mode 100644
index 0000000000..8e9f402d90
--- /dev/null
+++ b/catalog-dao/src/test/resources/images/apache.png
Binary files differ
diff --git a/catalog-dao/src/test/resources/log4j.properties b/catalog-dao/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..c18c3daa0e
--- /dev/null
+++ b/catalog-dao/src/test/resources/log4j.properties
@@ -0,0 +1,8 @@
+# Root logger option
+log4j.rootLogger=info, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
diff --git a/catalog-dao/src/test/resources/logback-test.xml b/catalog-dao/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..4b7eb955c7
--- /dev/null
+++ b/catalog-dao/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+<!-- only one line, shut up logback ! -->
+<configuration >
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>
+ %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+ </Pattern>
+ </encoder>
+ </appender>
+ <root level="OFF">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration>
diff --git a/catalog-fe/.gitignore b/catalog-fe/.gitignore
new file mode 100644
index 0000000000..d21b91d9b5
--- /dev/null
+++ b/catalog-fe/.gitignore
@@ -0,0 +1,5 @@
+/target
+/target
+/target
+/target
+/build/
diff --git a/catalog-fe/pom.xml b/catalog-fe/pom.xml
new file mode 100644
index 0000000000..cb41d46b6c
--- /dev/null
+++ b/catalog-fe/pom.xml
@@ -0,0 +1,441 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>catalog-fe</artifactId>
+ <packaging>war</packaging>
+
+
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${security-utils.version}</version>
+ <scope>compile</scope> </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ </dependency>
+
+ <!-- File changes listener -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.janino</groupId>
+ <artifactId>janino</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-multipart</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-moxy</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>javax.mail-api</artifactId>
+ <version>1.5.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-jackson</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>4.3.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <!-- http client END -->
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- JSON and YAML Parsing -->
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Jetty Proxy -->
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-proxy</artifactId>
+ <version>${jetty.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlets</artifactId>
+ <version>${jetty.servlets.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Proxy servlet -->
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Aspects -->
+ <dependency>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-aspects</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Inserted for ECOMP Portal Integration -->
+ <dependency>
+ <groupId>org.openecomp.ecompsdkos</groupId>
+ <artifactId>ecompFW</artifactId>
+ <version>${ecomp.version}</version>
+ <scope>compile</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>com.att.nsa</groupId>
+ <artifactId>cambriaClient</artifactId>
+ </exclusion>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <!-- TEST -->
+ <dependency>
+ <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+ <artifactId>jersey-test-framework-provider-bundle</artifactId>
+ <type>pom</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+
+
+
+ <build>
+
+ <finalName>${project.artifactId}-${project.version}</finalName>
+
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <archive>
+ <manifest>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ </archive>
+
+ <webResources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+ <includeScope>compile</includeScope>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-installed</id>
+ <phase>install</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${project.version}</version>
+ <type>${project.packaging}</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.parent.basedir}/sdc-os-chef/sdc-frontend/</outputDirectory>
+ </configuration>
+ </execution>
+
+ </executions>
+ </plugin>
+
+
+
+ <plugin>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>9.0.6.v20130930</version>
+ <configuration>
+ <contextPath>/</contextPath>
+ <webApp>
+ <contextPath>/</contextPath>
+ <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+ </webApp>
+ <war>${project.build.directory}/${project.build.finalName}.war</war>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ </plugin>
+
+ <plugin>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-maven-plugin</artifactId>
+ <version>${jcabi.plugin.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>ajc</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- ============================================= -->
+ <!-- Create the TAR file -->
+ <!-- ============================================= -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <finalName>${project.artifactId}-${full.release.version}${build.type}</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptor>${project.basedir}/tarball.xml</descriptor>
+ </configuration>
+
+ <executions>
+ <execution>
+ <id>assembly-tar-file</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+
+
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings
+ only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-maven-plugin</artifactId>
+ <versionRange>[0.0,)</versionRange>
+ <goals>
+ <goal>ajc</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <execute />
+ </action>
+ </pluginExecution>
+
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+
+ <!-- jacbi (log injection) -->
+
+ </plugins>
+ </pluginManagement>
+
+ </build>
+
+
+
+
+
+
+</project>
diff --git a/catalog-fe/readMe.txt b/catalog-fe/readMe.txt
new file mode 100644
index 0000000000..36bb65e213
--- /dev/null
+++ b/catalog-fe/readMe.txt
@@ -0,0 +1,14 @@
+Running Jetty
+
+1. in Arguments Tab add to VM Arguments:
+
+-Dconfig.home=C:\Git_work\D2-SDnC\catalog-fe\src\test\resources\config
+-Dlog.home=C:\Git_work\D2-SDnC\catalog-fe\target\log\
+-Dlogback.configurationFile=C:\Git_work\D2-SDnC\catalog-fe\src\main\resources\config\logback.xml
+
+2. In Run Configuration make sure to run jetty version 8.x or newer
+
+3. in order to test try:
+http://localhost:8080/sdc1/rest/configmgr/get
+or
+http://localhost:8080/sdc1/proxy \ No newline at end of file
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/Constants.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/Constants.java
new file mode 100644
index 0000000000..e157efcda9
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/Constants.java
@@ -0,0 +1,15 @@
+package org.openecomp.sdc.fe;
+
+public class Constants {
+
+ public static String HTTP_IV_USER = "HTTP_IV_USER";
+ public static String USER_ID = "USER_ID";
+ public static String HTTP_CSP_FIRSTNAME = "HTTP_CSP_FIRSTNAME";
+ public static String HTTP_CSP_LASTNAME = "HTTP_CSP_LASTNAME";
+ public static String HTTP_IV_REMOTE_ADDRESS = "HTTP_IV_REMOTE_ADDRESS";
+ public static String HTTP_CSP_WSTYPE = "HTTP_CSP_WSTYPE";
+ public static String HTTP_CSP_EMAIL = "HTTP_CSP_EMAIL";
+
+ public static final String WEBSEAL_USER_ID_HEADER = "csp-attuid";
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/client/BackendClient.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/client/BackendClient.java
new file mode 100644
index 0000000000..93e17144c0
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/client/BackendClient.java
@@ -0,0 +1,179 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.client;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.ResponseInfo;
+import org.openecomp.sdc.common.api.ResponseInfo.ResponseStatusEnum;
+import org.openecomp.sdc.fe.impl.Audit;
+import org.openecomp.sdc.fe.impl.HttpRequestInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BackendClient {
+
+ private static Logger log = LoggerFactory.getLogger(BackendClient.class.getName());
+
+ private HostnameVerifier hostnameVerifier = null;
+
+ private CloseableHttpClient backendHttpClient;
+ private String backendHost;
+ private String backendContext;
+
+ public BackendClient(String protocol, String backendHost, String backendContext) {
+
+ this.backendContext = backendContext;
+ hostnameVerifier = new HostnameVerifier() {
+
+ public boolean verify(String hostname, SSLSession session) {
+
+ return true;
+ }
+ };
+
+ if (protocol == null || protocol.isEmpty() || protocol.equals(Constants.HTTP)) {
+ backendHttpClient = HttpClients.createDefault();
+ this.backendHost = Constants.HTTP + "://" + backendHost;
+ } else {
+ // NULL can be returned in case of error
+ backendHttpClient = getSslClient();
+ this.backendHost = Constants.HTTPS + "://" + backendHost;
+ }
+
+ }
+
+ public HostnameVerifier getHostnameVerifier() {
+ return hostnameVerifier;
+ }
+
+ private CloseableHttpClient getSslClient() {
+
+ CloseableHttpClient httpClient = null;
+ try {
+
+ // SSLContextBuilder is not thread safe
+ SSLContextBuilder builder = new SSLContextBuilder();
+ builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
+ SSLContext sslContext = builder.build();
+
+ httpClient = HttpClientBuilder.create().setSSLHostnameVerifier(hostnameVerifier).setSslcontext(sslContext)
+ .build();
+
+ } catch (Exception e) {
+ log.error("Failed to create https client", e);
+ return null;
+ }
+
+ return httpClient;
+
+ }
+
+ public ResponseInfo forwardRequestToBackend(HttpRequestInfo requestInfo, List<String> requiredHeaders,
+ AsyncResponse asyncResponse) {
+
+ ResponseInfo responseInfo = null;
+ log.debug("forwardRequestToBackend");
+ if (backendHttpClient == null) {
+ responseInfo = new ResponseInfo(ResponseStatusEnum.INTERNAL_ERROR, "Failed to create https client");
+ Audit.error(log, requestInfo, HttpStatus.SC_INTERNAL_SERVER_ERROR);
+ asyncResponse.resume(
+ Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(responseInfo.toString()).build());
+ return responseInfo;
+ }
+
+ CloseableHttpResponse response = null;
+ int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
+ HttpPost httpPost = new HttpPost(backendHost + backendContext);
+ try {
+
+ log.debug("Executing request {}", httpPost.getRequestLine());
+ httpPost.setEntity(new InputStreamEntity(requestInfo.getRequestData()));
+ boolean allHeadersAreSet = copyHeadersToRequest(requiredHeaders, requestInfo, httpPost);
+ if (!allHeadersAreSet) {
+ responseInfo = new ResponseInfo(ResponseStatusEnum.MISSING_HEADERS, "Required headers are missing");
+ asyncResponse
+ .resume(Response.status(HttpStatus.SC_BAD_REQUEST).entity(responseInfo.toString()).build());
+ Audit.error(log, requestInfo, HttpStatus.SC_BAD_REQUEST);
+ } else {
+ response = backendHttpClient.execute(httpPost);
+ status = response.getStatusLine().getStatusCode();
+ asyncResponse.resume(Response.status(status).entity(response.getEntity()).build());
+ }
+ Audit.info(log, requestInfo, status);
+
+ } catch (IOException e) {
+ log.error("connection with backend failed with exception", e);
+ responseInfo = new ResponseInfo(ResponseStatusEnum.INTERNAL_ERROR, e.getMessage());
+ Audit.error(log, requestInfo, HttpStatus.SC_INTERNAL_SERVER_ERROR);
+ asyncResponse.resume(
+ Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(responseInfo.toString()).build());
+ } finally {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ backendHttpClient.close();
+ } catch (IOException e) {
+ log.error("failed to close httpClient: " + e.getMessage());
+ }
+
+ }
+
+ return responseInfo;
+
+ }
+
+ private boolean copyHeadersToRequest(List<String> requiredHeaders, HttpRequestInfo requestInfo, HttpPost httpPost) {
+ boolean allHeadersAreSet = false;
+ Map<String, String> originalHeaders = requestInfo.getHeaders();
+ for (String headerName : requiredHeaders) {
+ String headerValue = originalHeaders.get(headerName);
+ if (headerValue != null) {
+ httpPost.setHeader(headerName, headerValue);
+ } else {
+ log.error("missing required header " + headerName);
+ return allHeadersAreSet;
+ }
+ }
+ allHeadersAreSet = true;
+ return allHeadersAreSet;
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/Audit.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/Audit.java
new file mode 100644
index 0000000000..449d8a932d
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/Audit.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.impl;
+
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.openecomp.sdc.common.api.Constants;
+import org.slf4j.Logger;
+
+public class Audit {
+
+ private Audit() {
+ }
+
+ public static void error(Logger log, HttpRequestInfo requestInfo, int status) {
+ String errorMsg = "Internal Error";
+ if (requestInfo != null && requestInfo.getHeaders() != null) {
+ Map<String, String> requestHeaders = requestInfo.getHeaders();
+ errorMsg = String.format(Constants.ERROR_LOG_FORMAT, requestHeaders.get(Constants.USER_ID_HEADER),
+ requestHeaders.get(Constants.FIRST_NAME_HEADER) + " "
+ + requestHeaders.get(Constants.LAST_NAME_HEADER),
+ requestHeaders.get(Constants.ORIGIN_HEADER), requestHeaders.get(Constants.ACCESS_HEADER),
+ requestInfo.getRequestURL(), status);
+ }
+ log.error(errorMsg);
+ }
+
+ public static void error(Logger log, HttpServletRequest request, int status) {
+ String errorMsg = "Internal Error";
+ if (request != null) {
+
+ errorMsg = String.format(Constants.ERROR_LOG_FORMAT, request.getHeader(Constants.USER_ID_HEADER),
+ request.getHeader(Constants.FIRST_NAME_HEADER) + " "
+ + request.getHeader(Constants.LAST_NAME_HEADER),
+ request.getHeader(Constants.ORIGIN_HEADER), request.getHeader(Constants.ACCESS_HEADER),
+ request.getRequestURL(), status);
+ }
+ log.error(errorMsg);
+ }
+
+ public static void info(Logger log, HttpRequestInfo requestInfo, int status) {
+ String errorMsg = "Internal Error";
+ if (requestInfo != null && requestInfo.getHeaders() != null) {
+ Map<String, String> requestHeaders = requestInfo.getHeaders();
+ errorMsg = String.format(Constants.ERROR_LOG_FORMAT, requestHeaders.get(Constants.USER_ID_HEADER),
+ requestHeaders.get(Constants.FIRST_NAME_HEADER) + " "
+ + requestHeaders.get(Constants.LAST_NAME_HEADER),
+ requestHeaders.get(Constants.ORIGIN_HEADER), requestHeaders.get(Constants.ACCESS_HEADER),
+ requestInfo.getRequestURL(), status);
+ }
+ log.info(errorMsg);
+ }
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/CrudOperation.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/CrudOperation.java
new file mode 100644
index 0000000000..27fc02c00a
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/CrudOperation.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.impl;
+
+public enum CrudOperation {
+
+ CREATE, RETRIEVE, UPDATE, DELETE
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HttpRequestInfo.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HttpRequestInfo.java
new file mode 100644
index 0000000000..6a2a41fe1d
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/HttpRequestInfo.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class HttpRequestInfo {
+
+ public HttpRequestInfo(HttpServletRequest request, Map<String, String> headersMap, String data) {
+ headers = headersMap;
+ requestURL = request.getRequestURI();
+ requestData = new ByteArrayInputStream(data.getBytes());
+ originServletContext = request.getContextPath();
+ }
+
+ private Map<String, String> headers;
+ private String requestURL;
+ private InputStream requestData;
+ private String originServletContext;
+
+ public Map<String, String> getHeaders() {
+ return headers;
+ }
+
+ public void setHeaders(Map<String, String> headers) {
+ this.headers = headers;
+ }
+
+ public String getRequestURL() {
+ return requestURL;
+ }
+
+ public void setRequestURL(String requestURL) {
+ this.requestURL = requestURL;
+ }
+
+ public InputStream getRequestData() {
+ return requestData;
+ }
+
+ public void setRequestData(InputStream requestData) {
+ this.requestData = requestData;
+ }
+
+ public String getOriginServletContext() {
+ return originServletContext;
+ }
+
+ public void setOriginServletContext(String originServletContext) {
+ this.originServletContext = originServletContext;
+ }
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/ImportMetadata.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/ImportMetadata.java
new file mode 100644
index 0000000000..0d0aa7bd4c
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/impl/ImportMetadata.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.impl;
+
+public class ImportMetadata {
+
+ private String name;
+ private long size;
+ private String mime;
+ private String creator;
+ private String md5Checksum;
+
+ public ImportMetadata(String name, long size, String mime, String creator, String md5Checksum) {
+ super();
+ this.name = name;
+ this.size = size;
+ this.mime = mime;
+ this.creator = creator;
+ this.md5Checksum = md5Checksum;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public String getMime() {
+ return mime;
+ }
+
+ public void setMime(String mime) {
+ this.mime = mime;
+ }
+
+ public String getCreator() {
+ return creator;
+ }
+
+ public void setCreator(String creator) {
+ this.creator = creator;
+ }
+
+ public String getMd5Checksum() {
+ return md5Checksum;
+ }
+
+ public void setMd5Checksum(String md5Checksum) {
+ this.md5Checksum = md5Checksum;
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/FEAppContextListener.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/FEAppContextListener.java
new file mode 100644
index 0000000000..b132c46866
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/FEAppContextListener.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.listen;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.listener.AppContextListener;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.openecomp.sdc.fe.monitoring.FeMonitoringService;
+import org.openecomp.sdc.fe.servlets.HealthCheckService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FEAppContextListener extends AppContextListener implements ServletContextListener {
+
+ private static Logger log = LoggerFactory.getLogger(FEAppContextListener.class.getName());
+
+ public void contextInitialized(ServletContextEvent context) {
+
+ super.contextInitialized(context);
+
+ ConfigurationManager configurationManager = new ConfigurationManager(
+ ExternalConfiguration.getConfigurationSource());
+ log.debug("loading configuration from configDir:{} appName:{}", ExternalConfiguration.getConfigDir(),
+ ExternalConfiguration.getAppName());
+ context.getServletContext().setAttribute(Constants.CONFIGURATION_MANAGER_ATTR, configurationManager);
+
+ // Health Check service
+ HealthCheckService hcs = new HealthCheckService(context.getServletContext());
+ hcs.start(configurationManager.getConfiguration().getHealthCheckIntervalInSeconds(5));
+ context.getServletContext().setAttribute(Constants.HEALTH_CHECK_SERVICE_ATTR, hcs);
+
+ // Monitoring service
+ FeMonitoringService fms = new FeMonitoringService(context.getServletContext());
+ fms.start(configurationManager.getConfiguration().getSystemMonitoring().getProbeIntervalInSeconds(15));
+
+ if (configurationManager.getConfiguration() == null) {
+ log.debug("ERROR: configuration was not properly loaded");
+ return;
+ }
+
+ ExecutorService executorPool = Executors
+ .newFixedThreadPool(configurationManager.getConfiguration().getThreadpoolSize());
+ context.getServletContext().setAttribute(Constants.THREAD_EXECUTOR_ATTR, executorPool);
+
+ log.debug("After executing {}", this.getClass());
+ }
+
+ public void contextDestroyed(ServletContextEvent context) {
+
+ ExecutorService executorPool = (ExecutorService) context.getServletContext()
+ .getAttribute(Constants.THREAD_EXECUTOR_ATTR);
+ if (executorPool != null) {
+ executorPool.shutdown();
+ }
+
+ super.contextDestroyed(context);
+
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/MyObjectMapperProvider.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/MyObjectMapperProvider.java
new file mode 100644
index 0000000000..f891ba4e3f
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/listen/MyObjectMapperProvider.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.listen;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+@Provider
+public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {
+ final ObjectMapper defaultObjectMapper;
+
+ public MyObjectMapperProvider() {
+ defaultObjectMapper = createDefaultMapper();
+ }
+
+ @Override
+ public ObjectMapper getContext(Class<?> type) {
+ return defaultObjectMapper;
+ }
+
+ private static ObjectMapper createDefaultMapper() {
+ final ObjectMapper result = new ObjectMapper();
+ result.configure(SerializationFeature.INDENT_OUTPUT, true);
+
+ return result;
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigMgrServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigMgrServlet.java
new file mode 100644
index 0000000000..7792225742
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigMgrServlet.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.rest.api.RestConfigurationInfo;
+import org.openecomp.sdc.common.servlets.BasicServlet;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Path("/configmgr")
+public class ConfigMgrServlet extends BasicServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ConfigMgrServlet.class.getName());
+
+ @GET
+ @Path("/get")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String getConfig(@Context final HttpServletRequest request, @QueryParam("type") String type) {
+
+ String result = null;
+
+ ServletContext context = request.getSession().getServletContext();
+
+ ConfigurationManager configurationManager = (ConfigurationManager) context
+ .getAttribute(Constants.CONFIGURATION_MANAGER_ATTR);
+
+ if (type == null || type.equals("configuration")) {
+
+ Configuration configuration = configurationManager.getConfiguration();
+ if (configuration == null) {
+ log.warn("Configuration of type " + Configuration.class + " was not found");
+ } else {
+ log.info("The value returned from getConfig is " + configuration);
+
+ result = gson.toJson(configuration);
+
+ }
+ } else if (type.equals("rest")) {
+
+ RestConfigurationInfo configuration = configurationManager.getRestClientConfiguration();
+ if (configuration == null) {
+ log.warn("Configuration of type " + RestConfigurationInfo.class + " was not found");
+ } else {
+ log.info("The value returned from getConfig is " + configuration);
+
+ result = gson.toJson(configuration);
+
+ }
+
+ }
+ return result;
+
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigServlet.java
new file mode 100644
index 0000000000..92bb9e8743
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/ConfigServlet.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.container.TimeoutHandler;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.servlets.BasicServlet;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Path("/config")
+public class ConfigServlet extends BasicServlet {
+
+ private static Logger log = LoggerFactory.getLogger(ConfigServlet.class.getName());
+
+ @GET
+ @Path("/get")
+ @Produces(MediaType.APPLICATION_JSON)
+ public String getConfig(@Context final HttpServletRequest request) {
+
+ String result = null;
+
+ ServletContext context = request.getSession().getServletContext();
+
+ ConfigurationSource configurationSource = (ConfigurationSource) context
+ .getAttribute(Constants.CONFIGURATION_SOURCE_ATTR);
+ if (configurationSource != null) {
+ Configuration configuration = configurationSource.getAndWatchConfiguration(Configuration.class, null);
+
+ if (configuration == null) {
+ log.warn("Configuration of type " + Configuration.class + " was not found");
+ }
+ log.debug("{}", configuration);
+ if (log.isInfoEnabled()) {
+ log.info("Info level ENABLED...");
+ }
+ log.info("The value returned from getConfig is " + configuration);
+
+ result = gson.toJson(configuration);
+
+ } else {
+ log.warn("Source Configuration object was not initialized in the context.");
+ }
+
+ return result;
+
+ }
+
+ @GET
+ @Path("/asyncget")
+ public void asyncGet(@Suspended final AsyncResponse asyncResponse) {
+
+ asyncResponse.setTimeoutHandler(new TimeoutHandler() {
+
+ @Override
+ public void handleTimeout(AsyncResponse asyncResponse) {
+ asyncResponse.resume(
+ Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("Operation time out.").build());
+ }
+ });
+ asyncResponse.setTimeout(3, TimeUnit.SECONDS);
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ String result = veryExpensiveOperation();
+ asyncResponse.resume(result);
+ }
+
+ private String veryExpensiveOperation() {
+
+ return "veryExpensiveOperation SUCCESS";
+
+ }
+ }).start();
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeHealthCheckServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeHealthCheckServlet.java
new file mode 100644
index 0000000000..da5b321339
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeHealthCheckServlet.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.servlets.BasicServlet;
+
+import com.jcabi.aspects.Loggable;
+
+@Loggable(prepend = true, value = Loggable.TRACE, trim = false)
+@Path("/healthCheck")
+public class FeHealthCheckServlet extends BasicServlet {
+
+ // private static Logger log =
+ // LoggerFactory.getLogger(FeHealthCheckServlet.class.getName());
+
+ @GET
+ public Response getFEandBeHealthCheck(@Context final HttpServletRequest request) {
+ ServletContext context = request.getSession().getServletContext();
+ HealthCheckService hcs = ((HealthCheckService) context.getAttribute(Constants.HEALTH_CHECK_SERVICE_ATTR));
+ return hcs.getFeHealth();
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeProxyServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeProxyServlet.java
new file mode 100644
index 0000000000..1cd28deb7a
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/FeProxyServlet.java
@@ -0,0 +1,214 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import java.net.URI;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.Response;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.openecomp.sdc.fe.config.FeEcompErrorManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
+public class FeProxyServlet extends SSLProxyServlet {
+ private static final long serialVersionUID = 1L;
+ private static final String URL = "%s://%s:%s%s";
+ private static Logger log = LoggerFactory.getLogger(FeProxyServlet.class.getName());
+ private static Cache<String, MdcData> mdcDataCache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS).build();
+
+ @Override
+ public URI rewriteURI(HttpServletRequest request) {
+ try {
+ logFeRequest(request);
+ } catch (Exception e) {
+ FeEcompErrorManager.getInstance().processEcompError(EcompErrorName.FeHttpLoggingError, "FE Request");
+ FeEcompErrorManager.getInstance().logFeHttpLoggingError("FE Request");
+ log.error("Unexpected FE request logging error :", e);
+ }
+ String originalUrl = request.getRequestURL().toString();
+ String redirectedUrl = getModifiedUrl(request);
+
+ log.debug("FeProxyServlet Redirecting request from: {} , to: {}", originalUrl, redirectedUrl);
+
+ return URI.create(redirectedUrl);
+ }
+
+ @Override
+ protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse) {
+ try {
+ logFeResponse(request, proxyResponse);
+ } catch (Exception e) {
+ FeEcompErrorManager.getInstance().processEcompError(EcompErrorName.FeHttpLoggingError, "FE Response");
+ FeEcompErrorManager.getInstance().logFeHttpLoggingError("FE Response");
+ log.error("Unexpected FE response logging error :", e);
+ }
+ super.onResponseSuccess(request, response, proxyResponse);
+ }
+
+ private void logFeRequest(HttpServletRequest httpRequest) {
+
+ MDC.clear();
+
+ Long transactionStartTime = System.currentTimeMillis();
+ // UUID - In FE, we are supposed to get the below header from UI.
+ // We do not generate it if it's missing - BE does.
+ String uuid = httpRequest.getHeader(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ String serviceInstanceID = httpRequest.getHeader(Constants.X_ECOMP_SERVICE_ID_HEADER);
+
+ if (uuid != null && uuid.length() > 0) {
+ // User Id for logging
+ String userId = httpRequest.getHeader(Constants.USER_ID_HEADER);
+
+ String remoteAddr = httpRequest.getRemoteAddr();
+ String localAddr = httpRequest.getLocalAddr();
+
+ mdcDataCache.put(uuid, new MdcData(serviceInstanceID, userId, remoteAddr, localAddr, transactionStartTime));
+
+ updateMdc(uuid, serviceInstanceID, userId, remoteAddr, localAddr, null);
+ }
+ inHttpRequest(httpRequest);
+ }
+
+ private void logFeResponse(HttpServletRequest request, Response proxyResponse) {
+ String uuid = request.getHeader(Constants.X_ECOMP_REQUEST_ID_HEADER);
+ String transactionRoundTime = null;
+
+ if (uuid != null) {
+ MdcData mdcData = mdcDataCache.getIfPresent(uuid);
+ if (mdcData != null) {
+ Long transactionStartTime = mdcData.getTransactionStartTime();
+ if (transactionStartTime != null) {// should'n ever be null, but
+ // just to be defensive
+ transactionRoundTime = Long.toString(System.currentTimeMillis() - transactionStartTime);
+ }
+ updateMdc(uuid, mdcData.getServiceInstanceID(), mdcData.getUserId(), mdcData.getRemoteAddr(), mdcData.getLocalAddr(), transactionRoundTime);
+ }
+ }
+ outHttpResponse(proxyResponse);
+
+ MDC.clear();
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void inHttpRequest(HttpServletRequest httpRequest) {
+ log.info("{} {} {}", httpRequest.getMethod(), httpRequest.getRequestURI(), httpRequest.getProtocol());
+ }
+
+ // Extracted for purpose of clear method name, for logback %M parameter
+ private void outHttpResponse(Response proxyResponse) {
+ log.info("SC=\"{}\"", proxyResponse.getStatus());
+ }
+
+ private void updateMdc(String uuid, String serviceInstanceID, String userId, String remoteAddr, String localAddr, String transactionStartTime) {
+ MDC.put("uuid", uuid);
+ MDC.put("serviceInstanceID", serviceInstanceID);
+ MDC.put("userId", userId);
+ MDC.put("remoteAddr", remoteAddr);
+ MDC.put("localAddr", localAddr);
+ MDC.put("timer", transactionStartTime);
+ }
+
+ private class MdcData {
+ private String serviceInstanceID;
+ private String userId;
+ private String remoteAddr;
+ private String localAddr;
+ private Long transactionStartTime;
+
+ public MdcData(String serviceInstanceID, String userId, String remoteAddr, String localAddr, Long transactionStartTime) {
+ super();
+ this.serviceInstanceID = serviceInstanceID;
+ this.userId = userId;
+ this.remoteAddr = remoteAddr;
+ this.localAddr = localAddr;
+ this.transactionStartTime = transactionStartTime;
+ }
+
+ public Long getTransactionStartTime() {
+ return transactionStartTime;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public String getRemoteAddr() {
+ return remoteAddr;
+ }
+
+ public String getLocalAddr() {
+ return localAddr;
+ }
+
+ public String getServiceInstanceID() {
+ return serviceInstanceID;
+ }
+ }
+
+ public String getModifiedUrl(HttpServletRequest request) {
+
+ Configuration config = getConfiguration(request);
+ if (config == null) {
+ log.error("failed to retrive configuration.");
+ }
+ String scheme = config.getBeProtocol();
+ String uri = request.getRequestURI().toString();
+ StringBuilder url = new StringBuilder();
+ url.append(scheme).append("://").append(config.getBeHost());
+ url.append(":");
+ if (config.getBeProtocol().equals(BE_PROTOCOL.HTTP.getProtocolName())) {
+ url.append(config.getBeHttpPort());
+ } else {
+ url.append(config.getBeSslPort());
+ }
+ url.append(uri);
+ String queryString = request.getQueryString(); // d=789
+ if (queryString != null) {
+ url.append("?").append(queryString);
+ }
+
+ String redirectedUrl = url.toString();
+ String onboardingForwardContext = config.getOnboardingForwardContext();
+ if (onboardingForwardContext == null || onboardingForwardContext.isEmpty()) {
+ onboardingForwardContext = "/onboarding-api";
+ }
+ redirectedUrl = redirectedUrl.replace("/sdc1/feProxy/onboarding-api", onboardingForwardContext);
+ redirectedUrl = redirectedUrl.replace("/sdc1/feProxy", "/sdc2");
+ return redirectedUrl;
+
+ }
+
+ private Configuration getConfiguration(HttpServletRequest request) {
+ Configuration config = ((ConfigurationManager) request.getSession().getServletContext().getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).getConfiguration();
+ return config;
+ }
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/HealthCheckService.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/HealthCheckService.java
new file mode 100644
index 0000000000..338e8d4c01
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/HealthCheckService.java
@@ -0,0 +1,219 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Response;
+
+import org.apache.http.client.config.RequestConfig;
+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.util.EntityUtils;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.api.HealthCheckInfo;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckComponent;
+import org.openecomp.sdc.common.api.HealthCheckInfo.HealthCheckStatus;
+import org.openecomp.sdc.common.api.HealthCheckWrapper;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.openecomp.sdc.fe.config.FeEcompErrorManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+public class HealthCheckService {
+
+ private class HealthStatus {
+ public String body;
+ public int statusCode;
+
+ public HealthStatus(int code, String body) {
+ this.body = body;
+ this.statusCode = code;
+ }
+ }
+
+ private static final String URL = "%s://%s:%s/sdc2/rest/healthCheck";
+ private static Logger healthLogger = LoggerFactory.getLogger("asdc.fe.healthcheck");
+ private static Logger log = LoggerFactory.getLogger(HealthCheckService.class.getName());
+
+ private HealthStatus lastHealthStatus = new HealthStatus(500, "{}");
+
+ private class HealthCheckScheduledTask implements Runnable {
+ @Override
+ public void run() {
+ healthLogger.trace("Executing FE Health Check Task - Start");
+ HealthStatus currentHealth = checkHealth();
+ int currentHealthStatus = currentHealth.statusCode;
+ healthLogger.trace("Executing FE Health Check Task - Status = {}", currentHealthStatus);
+
+ // In case health status was changed, issue alarm/recovery
+ if (currentHealthStatus != lastHealthStatus.statusCode) {
+ log.trace("FE Health State Changed to {}. Issuing alarm / recovery alarm...", currentHealthStatus);
+ logFeAlarm(currentHealthStatus);
+ }
+
+ // Anyway, update latest response
+ lastHealthStatus = currentHealth;
+ }
+ }
+
+ /**
+ * This executor will execute the health check task.
+ */
+ ScheduledExecutorService healthCheckExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "FE-Health-Check-Thread");
+ }
+ });
+ private ServletContext context;
+
+ public HealthCheckService(ServletContext context) {
+ this.context = context;
+ }
+
+ public void start(int interval) {
+ this.healthCheckExecutor.scheduleAtFixedRate(new HealthCheckScheduledTask(), 0, interval, TimeUnit.SECONDS);
+ }
+
+ /**
+ * To be used by the HealthCheckServlet
+ *
+ * @return
+ */
+ public Response getFeHealth() {
+ return this.buildResponse(lastHealthStatus.statusCode, lastHealthStatus.body);
+ }
+
+ private HealthStatus checkHealth() {
+ CloseableHttpClient httpClient = null;
+ try {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ Configuration config = ((ConfigurationManager) context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR))
+ .getConfiguration();
+ String redirectedUrl = String.format(URL, config.getBeProtocol(), config.getBeHost(),
+ config.getBeHttpPort());
+ httpClient = getHttpClient(config);
+ HttpGet httpGet = new HttpGet(redirectedUrl);
+ CloseableHttpResponse beResponse;
+ int beStatus;
+ String feAggHealthCheck;
+ try {
+ beResponse = httpClient.execute(httpGet);
+ beStatus = beResponse.getStatusLine().getStatusCode();
+ String beJsonResponse = EntityUtils.toString(beResponse.getEntity());
+ feAggHealthCheck = getFeHealthCheckInfos(gson, beJsonResponse);
+ } catch (Exception e) {
+ log.error("Health Check error when trying to connect to BE", e);
+ String beDowneResponse = gson.toJson(getBeDownCheckInfos());
+ return new HealthStatus(500, beDowneResponse);
+ }
+ return new HealthStatus(beStatus, feAggHealthCheck);
+ } catch (Exception e) {
+ FeEcompErrorManager.getInstance().processEcompError(EcompErrorName.FeHealthCheckGeneralError, "Unexpected FE Health check error");
+ FeEcompErrorManager.getInstance().logFeHealthCheckGeneralError("Unexpected FE Health check error");
+ log.error("Unexpected FE health check error {}", e.getMessage());
+ return new HealthStatus(500, e.getMessage());
+ } finally {
+ if (httpClient != null) {
+ try {
+ httpClient.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private Response buildResponse(int status, String jsonResponse) {
+ healthLogger.trace("FE and BE health check status: {}", jsonResponse);
+ return Response.status(status).entity(jsonResponse).build();
+ }
+
+ private void logFeAlarm(int lastFeStatus) {
+
+ switch (lastFeStatus) {
+ case 200:
+ FeEcompErrorManager.getInstance().processEcompError(EcompErrorName.FeHealthCheckRecovery,
+ "FE Health Recovered");
+ FeEcompErrorManager.getInstance().logFeHealthCheckRecovery("FE Health Recovered");
+ break;
+ case 500:
+ FeEcompErrorManager.getInstance().processEcompError(EcompErrorName.FeHealthCheckConnectionError,
+ "Connection with ASDC-BE is probably down");
+ FeEcompErrorManager.getInstance().logFeHealthCheckError("Connection with ASDC-BE is probably down");
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ private String getFeHealthCheckInfos(Gson gson, String responseString) {
+ Type wrapperType = new TypeToken<HealthCheckWrapper>() {
+ }.getType();
+ HealthCheckWrapper healthCheckWrapper = gson.fromJson(responseString, wrapperType);
+ String appVersion = ExternalConfiguration.getAppVersion();
+ String description = "OK";
+ healthCheckWrapper.getComponentsInfo()
+ .add(new HealthCheckInfo(HealthCheckComponent.FE, HealthCheckStatus.UP, appVersion, description));
+ return gson.toJson(healthCheckWrapper);
+ }
+
+ private HealthCheckWrapper getBeDownCheckInfos() {
+ List<HealthCheckInfo> healthCheckInfos = new ArrayList<HealthCheckInfo>();
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.FE, HealthCheckStatus.UP,
+ ExternalConfiguration.getAppVersion(), "OK"));
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.BE, HealthCheckStatus.DOWN, null, null));
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.ES, HealthCheckStatus.UNKNOWN, null, null));
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.TITAN, HealthCheckStatus.UNKNOWN, null, null));
+ healthCheckInfos.add(new HealthCheckInfo(HealthCheckComponent.DE, HealthCheckStatus.UNKNOWN, null, null));
+ HealthCheckWrapper hcWrapper = new HealthCheckWrapper(healthCheckInfos, "UNKNOWN", "UNKNOWN");
+ return hcWrapper;
+ }
+
+ private CloseableHttpClient getHttpClient(Configuration config) {
+ int timeout = 3000;
+ int socketTimeout = config.getHealthCheckSocketTimeoutInMs(5000);
+ RequestConfig.Builder requestBuilder = RequestConfig.custom();
+ requestBuilder.setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(socketTimeout);
+
+ HttpClientBuilder builder = HttpClientBuilder.create();
+ builder.setDefaultRequestConfig(requestBuilder.build());
+ return builder.build();
+ }
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/KibanaServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/KibanaServlet.java
new file mode 100644
index 0000000000..64784b3265
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/KibanaServlet.java
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import java.net.URI;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class KibanaServlet extends ProxyServlet {
+ private static final long serialVersionUID = 1L;
+ private static Logger log = LoggerFactory.getLogger(KibanaServlet.class.getName());
+
+ @Override
+ public URI rewriteURI(HttpServletRequest request) {
+
+ String originalUrl = request.getRequestURI();
+
+ String redirectedUrl = getModifiedUrl(request);
+
+ log.debug("KibanaServlet Redirecting request from: {} , to: {}", originalUrl, redirectedUrl);
+
+ return URI.create(redirectedUrl);
+ }
+
+ @Override
+ public void customizeProxyRequest(Request proxyRequest, HttpServletRequest request) {
+ super.customizeProxyRequest(proxyRequest, request);
+
+ }
+
+ @Override
+ protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse) {
+
+ super.onResponseSuccess(request, response, proxyResponse);
+ }
+
+ public String getModifiedUrl(HttpServletRequest request) {
+ Configuration config = getConfiguration(request);
+ if (config == null) {
+ log.error("failed to retrive configuration.");
+ }
+ // String scheme = request.getScheme();
+ String contextPath = request.getContextPath(); // /mywebapp
+ String servletPath = request.getServletPath(); // /servlet/MyServlet
+ String pathInfo = request.getPathInfo(); // /a/b;c=123
+ String queryString = request.getQueryString(); // d=789
+
+ StringBuilder url = new StringBuilder();
+ url.append(config.getKibanaProtocol()).append("://").append(config.getKibanaHost());
+ url.append(":").append(config.getKibanaPort());
+ url.append(contextPath).append(servletPath);
+
+ if (pathInfo != null) {
+ url.append(pathInfo);
+ }
+ if (queryString != null) {
+ url.append("?").append(queryString);
+ }
+
+ String redirectedUrl = url.toString().replace("/sdc1/kibanaProxy/", "/");
+ return redirectedUrl;
+
+ }
+
+ private Configuration getConfiguration(HttpServletRequest request) {
+ Configuration config = ((ConfigurationManager) request.getSession().getServletContext()
+ .getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).getConfiguration();
+ return config;
+ }
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/PortalServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/PortalServlet.java
new file mode 100644
index 0000000000..4eba2e5eb5
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/PortalServlet.java
@@ -0,0 +1,279 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+
+import org.openecomp.portalsdk.core.onboarding.crossapi.ECOMPSSO;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.impl.MutableHttpServletRequest;
+import org.openecomp.sdc.fe.Constants;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.openecomp.sdc.fe.config.FeEcompErrorManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Root resource (exposed at "/" path)
+ */
+@Path("/")
+public class PortalServlet extends HttpServlet {
+
+ private static Logger log = LoggerFactory.getLogger(PortalServlet.class.getName());
+ private static final long serialVersionUID = 1L;
+ public static final String MISSING_HEADERS_MSG = "Missing Headers In Request";
+ public static final String AUTHORIZATION_ERROR_MSG = "Autherization error";
+ public static final String NEW_LINE = System.getProperty("line.separator");
+
+ /**
+ * Entry point from ECOMP portal
+ */
+ @GET
+ @Path("/portal")
+ public void doGet(@Context final HttpServletRequest request, @Context final HttpServletResponse response) {
+ try {
+ addRequestHeadersUsingWebseal(request, response);
+ } catch (Exception e) {
+ FeEcompErrorManager.getInstance().processEcompError(EcompErrorName.FePortalServletError, "Portal Servlet");
+ FeEcompErrorManager.getInstance().logFePortalServletError("Portal Servlet");
+ log.error("Error during getting portal page", e);
+ }
+ }
+
+ /**
+ * Building new HTTP request and setting headers for the request The request
+ * will dispatch to index.html
+ *
+ * @param request
+ * @param response
+ * @throws ServletException
+ * @throws IOException
+ */
+ private void addRequestHeadersUsingWebseal(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
+
+ response.setContentType("text/html");
+
+ // Create new request object to dispatch
+ MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
+
+ // Get configuration object (reads data from configuration.yaml)
+ Configuration configuration = getConfiguration(request);
+
+ // Check if we got header from webseal
+ String userId = request.getHeader(Constants.WEBSEAL_USER_ID_HEADER);
+ if (null == userId) {
+ // Authentication via ecomp portal
+ try {
+ String valdiateECOMPSSO = ECOMPSSO.valdiateECOMPSSO(request);
+ String userIdFromCookie = ECOMPSSO.getUserIdFromCookie(request);
+ if (valdiateECOMPSSO == null || ("").equals(userIdFromCookie)) {
+ // This is probably a webseal request, so missing header in request should be printed.
+ response.sendError(HttpServletResponse.SC_USE_PROXY, MISSING_HEADERS_MSG);
+ }
+ userId = userIdFromCookie;
+ } catch (Exception e) {
+ response.sendError(HttpServletResponse.SC_USE_PROXY, AUTHORIZATION_ERROR_MSG);
+ }
+ }
+
+ // Replace webseal header with open source header
+ mutableRequest.putHeader(Constants.USER_ID, userId);
+
+ // Getting identification headers from configuration.yaml
+ // (identificationHeaderFields) and setting them to new request
+ // mutableRequest
+ List<List<String>> identificationHeaderFields = configuration.getIdentificationHeaderFields();
+ for (List<String> possibleHeadersToRecieve : identificationHeaderFields) {
+ String allowedHeaderToPass = possibleHeadersToRecieve.get(0);
+ setNewHeader(possibleHeadersToRecieve, allowedHeaderToPass, request, mutableRequest);
+ }
+
+ // Getting optional headers from configuration.yaml
+ // (optionalHeaderFields) and setting them to new request mutableRequest
+ List<List<String>> optionalHeaderFields = configuration.getOptionalHeaderFields();
+ for (List<String> possibleHeadersToRecieve : optionalHeaderFields) {
+ String allowedHeaderToPass = possibleHeadersToRecieve.get(0);
+ setNewHeader(possibleHeadersToRecieve, allowedHeaderToPass, request, mutableRequest);
+ }
+
+ // Print headers from original request for debug purposes
+ printHeaders(request);
+
+ // In case using webseal, validate all mandatory headers (identificationHeaderFields) are included in the new request (mutableRequest).
+ // Via ecomp portal do not need to check the headers.
+ boolean allHeadersExist = true;
+ if (null != request.getHeader(Constants.WEBSEAL_USER_ID_HEADER)) {
+ allHeadersExist = checkHeaders(mutableRequest);
+ }
+
+ if (allHeadersExist) {
+ addCookies(response, mutableRequest, getMandatoryHeaders(request));
+ addCookies(response, mutableRequest, getOptionalHeaders(request));
+ RequestDispatcher rd = request.getRequestDispatcher("index.html");
+ rd.forward(mutableRequest, response);
+ } else {
+ response.sendError(HttpServletResponse.SC_USE_PROXY, MISSING_HEADERS_MSG);
+ }
+ }
+
+ /**
+ * Print all request headers to the log
+ *
+ * @param request
+ */
+ private void printHeaders(HttpServletRequest request) {
+
+ if (log.isDebugEnabled()) {
+ StringBuilder builder = new StringBuilder();
+ String sessionId = "";
+ if (request.getSession() != null) {
+ String id = request.getSession().getId();
+ if (id != null) {
+ sessionId = id;
+ }
+ }
+
+ builder.append("Receiving request with headers:" + NEW_LINE);
+ log.debug("{}", request.getHeaderNames());
+ @SuppressWarnings("unchecked")
+ Enumeration<String> headerNames = request.getHeaderNames();
+ if (headerNames != null) {
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ String headerValue = request.getHeader(headerName);
+ builder.append("session " + sessionId + " header: name = " + headerName + ", value = " + headerValue + NEW_LINE);
+ }
+ }
+
+ log.debug(builder.toString());
+ }
+
+ }
+
+ /**
+ * Add cookies (that where set in the new request headers) in the response
+ *
+ * @param response
+ * @param request
+ * @param headers
+ */
+ private void addCookies(HttpServletResponse response, HttpServletRequest request, String[] headers) {
+ for (int i = 0; i < headers.length; i++) {
+ String currHeader = headers[i];
+ String headerValue = request.getHeader(currHeader);
+ if (headerValue != null) {
+ response.addCookie(new Cookie(currHeader, headerValue));
+ }
+ }
+ }
+
+ /**
+ * Get mandatory headers (identificationHeaderFields) String array, and
+ * checks that each header exists in the new request
+ *
+ * @param request
+ * @return boolean
+ */
+ private boolean checkHeaders(HttpServletRequest request) {
+ String[] mandatoryHeaders = getMandatoryHeaders(request);
+
+ boolean allHeadersExist = true;
+ for (int i = 0; i < mandatoryHeaders.length; i++) {
+ String headerValue = request.getHeader(mandatoryHeaders[i]);
+ if (headerValue == null) {
+ allHeadersExist = false;
+ break;
+ }
+ }
+ return allHeadersExist;
+ }
+
+ /**
+ * Get mandatory headers (identificationHeaderFields) from
+ * configuration.yaml file and return String[]
+ *
+ * @param request
+ * @return String[]
+ */
+ private String[] getMandatoryHeaders(HttpServletRequest request) {
+ Configuration configuration = getConfiguration(request);
+ List<List<String>> identificationHeaderFields = configuration.getIdentificationHeaderFields();
+ String[] mandatoryHeaders = new String[identificationHeaderFields.size()];
+ for (int i = 0; i < identificationHeaderFields.size(); i++) {
+ mandatoryHeaders[i] = identificationHeaderFields.get(i).get(0);
+ }
+ return mandatoryHeaders;
+ }
+
+ /**
+ * Get optional headers (optionalHeaderFields) from configuration.yaml file
+ * and return String[]
+ *
+ * @param request
+ * @return String[]
+ */
+ private String[] getOptionalHeaders(HttpServletRequest request) {
+ Configuration configuration = getConfiguration(request);
+ List<List<String>> optionalHeaderFields = configuration.getOptionalHeaderFields();
+ String[] optionalHeaders = new String[optionalHeaderFields.size()];
+ for (int i = 0; i < optionalHeaderFields.size(); i++) {
+ optionalHeaders[i] = optionalHeaderFields.get(i).get(0);
+ }
+ return optionalHeaders;
+ }
+
+ /**
+ * Return Configuration object to read from configuration.yaml
+ *
+ * @param request
+ * @return Configuration
+ */
+ private Configuration getConfiguration(HttpServletRequest request) {
+ ConfigurationManager configManager = (ConfigurationManager) request.getSession().getServletContext().getAttribute(org.openecomp.sdc.common.api.Constants.CONFIGURATION_MANAGER_ATTR);
+ return configManager.getConfiguration();
+ }
+
+ private boolean setNewHeader(List<String> possibleOldHeaders, String newHeaderToSet, HttpServletRequest oldRequest, MutableHttpServletRequest newRequest) {
+ boolean newHeaderIsSet = false;
+ for (int i = 0; i < possibleOldHeaders.size() && !newHeaderIsSet; i++) {
+ String headerValue = oldRequest.getHeader(possibleOldHeaders.get(i));
+ if (headerValue != null) {
+ newRequest.putHeader(newHeaderToSet, headerValue);
+ newHeaderIsSet = true;
+ }
+ }
+ return newHeaderIsSet;
+ }
+
+}
diff --git a/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java
new file mode 100644
index 0000000000..c3ba279c8c
--- /dev/null
+++ b/catalog-fe/src/main/java/org/openecomp/sdc/fe/servlets/SSLProxyServlet.java
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.servlets;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class SSLProxyServlet extends ProxyServlet {
+
+ private static final long serialVersionUID = 1L;
+ private static Logger log = LoggerFactory.getLogger(SSLProxyServlet.class.getName());
+
+ public enum BE_PROTOCOL {
+ HTTP("http"), SSL("ssl");
+ private String protocolName;
+
+ public String getProtocolName() {
+ return protocolName;
+ }
+
+ BE_PROTOCOL(String protocolName) {
+ this.protocolName = protocolName;
+ }
+ };
+
+ @Override
+ public void customizeProxyRequest(Request proxyRequest, HttpServletRequest request) {
+ super.customizeProxyRequest(proxyRequest, request);
+ // Add Missing Headers to proxy request
+ @SuppressWarnings("unchecked")
+ Enumeration<String> headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ if (!proxyRequest.getHeaders().containsKey(headerName)) {
+ String headerVal = request.getHeader(headerName);
+ log.debug("Adding missing header to request, header name: {} , header value: {}", headerName,
+ headerVal);
+ proxyRequest.header(headerName, headerVal);
+ }
+ }
+ proxyRequest.getHeaders().remove(HttpHeader.HOST);
+
+ }
+
+ @Override
+ protected HttpClient createHttpClient() throws ServletException {
+ Configuration config = ((ConfigurationManager) getServletConfig().getServletContext()
+ .getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).getConfiguration();
+ boolean isSecureClient = !config.getBeProtocol().equals(BE_PROTOCOL.HTTP.getProtocolName());
+ HttpClient client = (isSecureClient) ? getSecureHttpClient() : super.createHttpClient();
+ setTimeout(600000);
+ client.setIdleTimeout(600000);
+ client.setStopTimeout(600000);
+
+ return client;
+ }
+
+ private HttpClient getSecureHttpClient() throws ServletException {
+ // Instantiate and configure the SslContextFactory
+ SslContextFactory sslContextFactory = new SslContextFactory(true);
+
+ // Instantiate HttpClient with the SslContextFactory
+ HttpClient httpClient = new HttpClient(sslContextFactory);
+
+ // Configure HttpClient, for example:
+ httpClient.setFollowRedirects(false);
+
+ // Start HttpClient
+ try {
+ httpClient.start();
+
+ return httpClient;
+ } catch (Exception x) {
+ throw new ServletException(x);
+ }
+ }
+}
diff --git a/catalog-fe/src/main/resources/application-context.xml b/catalog-fe/src/main/resources/application-context.xml
new file mode 100644
index 0000000000..a5ec92697a
--- /dev/null
+++ b/catalog-fe/src/main/resources/application-context.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
+
+ <context:annotation-config />
+ <aop:aspectj-autoproxy proxy-target-class="true" />
+
+ <context:component-scan
+ base-package="org.openecomp.sdnc.catalog.backend.dao,
+ org.elasticsearch.mapping,
+ org.openecomp.sdnc.catalog.backend.artifacts">
+
+ </context:component-scan>
+
+
+
+ <!-- <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /> -->
+</beans>
diff --git a/catalog-fe/src/main/resources/config/configuration.yaml b/catalog-fe/src/main/resources/config/configuration.yaml
new file mode 100644
index 0000000000..6be93b330d
--- /dev/null
+++ b/catalog-fe/src/main/resources/config/configuration.yaml
@@ -0,0 +1,78 @@
+# Needed for logging purposes. To be populated by DevOps - currently dummy
+feFqdn: asdcFe.att.com
+
+# catalog backend hostname
+beHost: localhost
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc2/rest/v1/catalog/upload/resources
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+# threadpool size for handling requests
+threadpoolSize: 50
+
+# request processing timeout (seconds)
+requestTimeout: 10
+
+healthCheckSocketTimeoutInMs: 5000
+
+healthCheckIntervalInSeconds: 5
+
+identificationHeaderFields:
+ -
+ - &HTTP_IV_USER HTTP_IV_USER
+ - &iv-user iv-user
+ -
+ - &USER_ID USER_ID
+ - &user-id user-id
+ -
+ - &HTTP_CSP_ATTUID HTTP_CSP_ATTUID
+ - &csp-attuid csp-attuid
+ -
+ - &HTTP_CSP_WSTYPE HTTP_CSP_WSTYPE
+ - &csp-wstype csp-wstype
+
+optionalHeaderFields:
+ -
+ - &HTTP_CSP_FIRSTNAME HTTP_CSP_FIRSTNAME
+ - &csp-firstname csp-firstname
+ -
+ - &HTTP_CSP_LASTNAME HTTP_CSP_LASTNAME
+ - &csp-lastname csp-lastname
+ -
+ - &HTTP_IV_REMOTE_ADDRESS HTTP_IV_REMOTE_ADDRESS
+ - &iv-remote-address iv-remote-address
+ -
+ - &HTTP_CSP_EMAIL HTTP_CSP_EMAIL
+ - &csp-email csp-email
+
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 17
+
+# Protocols
+protocols:
+ - http
+ - https
+
+
+systemMonitoring:
+ enabled: false
+ isProxy: true
+ probeIntervalInSeconds: 15
+
+kibanaHost: localhost
+kibanaPort: 5601
+kibanaProtocol: http
diff --git a/catalog-fe/src/main/resources/config/ecomp-error-configuration.yaml b/catalog-fe/src/main/resources/config/ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..8982b2424f
--- /dev/null
+++ b/catalog-fe/src/main/resources/config/ecomp-error-configuration.yaml
@@ -0,0 +1,48 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+# Range of FE codes - 8000-9999
+
+
+errors:
+ FeHealthCheckConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_8000,
+ severity: ERROR,
+ description: "Connection error during FE Health Check",
+ alarmSeverity: CRITICAL
+ }
+ FeHttpLoggingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_8001,
+ severity: ERROR,
+ description: "Error when logging FE HTTP request/response",
+ alarmSeverity: MINOR
+ }
+ FePortalServletError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_8002,
+ severity: ERROR,
+ description: "Error when trying to access FE Portal page",
+ alarmSeverity: MAJOR
+ }
+ FeHealthCheckGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_8004,
+ severity: ERROR,
+ description: "General error during FE Health Check",
+ alarmSeverity: CRITICAL
+ }
+ FeHealthCheckRecovery: {
+ type: RECOVERY,
+ code: ASDC_8005,
+ severity: INFO,
+ description: "BE Health Check Recovery",
+ alarmSeverity: INFORMATIONAL
+ } \ No newline at end of file
diff --git a/catalog-fe/src/main/resources/config/logback.xml b/catalog-fe/src/main/resources/config/logback.xml
new file mode 100644
index 0000000000..fd2e13ca43
--- /dev/null
+++ b/catalog-fe/src/main/resources/config/logback.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="5 seconds">
+
+ <property scope="system" name="ECOMP-component-name" value="ASDC" />
+ <property scope="system" name="ECOMP-subcomponent-name" value="ASDC-FE" />
+ <property scope="context" name="enable-all-log" value="false" />
+ <property file="${config.home}/catalog-fe/configuration.yaml" />
+ <!-- value used by pattern field list (| - is inter-field separator, || - unavailable or not applicable field value) (m - mandatory, o- optional)-->
+ <!--timestamp(m)| requestID(m)| serviceInstanceID(o)| threadID(m)| physicalServerName(o)| serviceName(m)| userID(m)| logLevel(m)| severity(o)| serverIpAddress(m)| serverName(m)| clientIpAddress(o)| className(m)| timer(o)| detailedMessage(o)-->
+ <property name="default-log-pattern"
+ value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{uuid}|%X{serviceInstanceID}|%thread||${ECOMP-subcomponent-name}|%X{userId}|%level|%X{alarmSeverity}|%X{localAddr}|${feFqdn}|%X{remoteAddr}|%logger{35}|%X{timer}|ActivityType=&lt;%M&gt;, Desc=&lt;%msg&gt;%n" />
+
+ <!-- All log -->
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender name="ALL_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log
+ </file>
+
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ALL_ROLLING" />
+ </appender>
+
+ </then>
+ </if>
+
+ <!-- Error log -->
+ <appender name="ERROR_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log
+ </file>
+
+ <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Debug log -->
+ <appender name="DEBUG_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log
+ </file>
+
+ <!-- accept DEBUG and TRACE level -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
+ <expression>
+ e.level.toInt() &lt;= DEBUG.toInt()
+ </expression>
+ </evaluator>
+ <OnMismatch>DENY</OnMismatch>
+ <OnMatch>NEUTRAL</OnMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Asynchronicity Configurations -->
+ <appender name="ASYNC_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="DEBUG_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ERROR_ROLLING" />
+ </appender>
+
+ <root level="INFO">
+ <appender-ref ref="ASYNC_ERROR" />
+ <appender-ref ref="ASYNC_DEBUG" />
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender-ref ref="ALL_ROLLING" />
+ </then>
+ </if>
+ </root>
+
+ <logger name="org.openecomp.sdc" level="INFO" />
+</configuration> \ No newline at end of file
diff --git a/catalog-fe/src/main/resources/config/rest-configuration-info.yaml b/catalog-fe/src/main/resources/config/rest-configuration-info.yaml
new file mode 100644
index 0000000000..083adcf7c7
--- /dev/null
+++ b/catalog-fe/src/main/resources/config/rest-configuration-info.yaml
@@ -0,0 +1,12 @@
+# rest read timeout - means no timeout
+readTimeoutInSec: 0
+
+# whether to ignore certificate
+ignoreCertificate: false
+
+# the connection pool size
+connectionPoolSize: 10
+
+# create connection timeout
+connectTimeoutInSec: 10
+
diff --git a/catalog-fe/src/main/resources/jetty-ipaccess.xml b/catalog-fe/src/main/resources/jetty-ipaccess.xml
new file mode 100644
index 0000000000..97db551f02
--- /dev/null
+++ b/catalog-fe/src/main/resources/jetty-ipaccess.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<!-- =============================================================== -->
+<!-- Mixin the Statistics Handler -->
+<!-- =============================================================== -->
+
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+ <Get id="oldhandler" name="handler"/>
+
+ <Set name="handler">
+ <New id="IPAccessHandler" class="org.eclipse.jetty.server.handler.IPAccessHandler">
+ <Set name="handler"><Ref refid="oldhandler"/></Set>
+ <Set name="white">
+ <Array type="String">
+ <Item>0-255.0-255.0-255.0-255</Item>
+ </Array>
+ </Set>
+ <Set name="black">
+ <Array type="String">
+ <Item>127.0.0.1/blacklisted</Item>
+ <Item>127.0.0.2/black.html</Item>
+ </Array>
+ </Set>
+ <Set name="whiteListByPath">false</Set>
+ </New>
+ </Set>
+</Configure>
diff --git a/catalog-fe/src/main/resources/portal.properties b/catalog-fe/src/main/resources/portal.properties
new file mode 100644
index 0000000000..880c4fe9e9
--- /dev/null
+++ b/catalog-fe/src/main/resources/portal.properties
@@ -0,0 +1,28 @@
+# Portal REST URL, ends "/auxapi"
+ecomp_rest_url = https://portal.api.simpledemo.openecomp.org/ecompportal/auxapi
+
+# Java implementation of interface
+portal.api.impl.class = org.openecomp.sdc.be.ecomp.EcompIntImpl
+
+# CSP-SSO URL
+ecomp_redirect_url = http://portal.api.simpledemo.openecomp.org:8989/ECOMPPORTAL/login.htm
+
+# Cookie set by CSP-SSO
+csp_cookie_name = attESSec
+# CSP setting, most use PROD; DEV also recognized
+csp_gate_keeper_prod_key = PROD
+
+# Comma-separated list of UEB server names
+ueb_url_list =
+# UEB topic where Portal listens
+ecomp_portal_inbox_name = ECOMP-PORTAL-INBOX-TEST
+# UEB key generated while on-boarding
+ueb_app_key = app_key_here
+# UEB secret generated while on-boarding
+ueb_app_secret = app_secret_here
+# UEB topic where App listens
+ueb_app_mailbox_name = app_topic_name_here
+# Consumer group name; most Apps should use {UUID}
+ueb_app_consumer_group_name = {UUID}
+
+decryption_key = AGLDdG4D04BKm2IxIWEr8o== \ No newline at end of file
diff --git a/catalog-fe/src/main/resources/scripts/install.sh b/catalog-fe/src/main/resources/scripts/install.sh
new file mode 100644
index 0000000000..bed9411795
--- /dev/null
+++ b/catalog-fe/src/main/resources/scripts/install.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+export JETTY_BASE=/home/jetty/base
+
+COMP=$1
+
+function usage() {
+ echo "$0 <fe | be>"
+}
+
+function exitOnError() {
+ if [ $1 -ne 0 ]
+ then
+ echo "Failed running task $2"
+ exit 2
+ fi
+}
+
+if [ $# -ne 1 ]
+then
+ usage
+ exit 1
+
+fi
+
+/opt/app/sdc/catalog-${COMP}/scripts/installJettyBase.sh
+exitOnError $? "installJettyBase"
+
+cd ${JETTY_BASE}
+exitOnError $? "move_to_base_dir"
+
+mkdir -p scripts
+
+cp /opt/app/sdc/catalog-${COMP}/scripts/* scripts
+exitOnError $? "copy_scripts_from_rpm"
+
+cp /opt/app/sdc/catalog-${COMP}/ext/jetty-ipaccess.xml etc
+exitOnError $? "override_jetty-ipaccess_module."
+
+cp /opt/app/sdc/catalog-${COMP}/catalog-${COMP}-*.war webapps
+exitOnError $? "copy_war"
+
+cp /opt/app/sdc/catalog-${COMP}/scripts/startJetty.sh .
+exitOnError $? "copy_startJetty"
+
+cp /opt/app/sdc/catalog-${COMP}/scripts/jvm.properties .
+exitOnError $? "copy_jvm_properties"
+
+./scripts/updateSslParams.sh ${JETTY_BASE}
+exitOnError $? "updateSslParams_script"
+
+#ONLY FOR BE
+#cp /opt/app/sdc/config/catalog-${COMP}/elasticsearch.yml config
+#exitOnError $? "copy_elasticsearch_yaml_to_config"
+
+mkdir -p ${JETTY_BASE}/config/catalog-${COMP}
+cp -r /opt/app/sdc/config/catalog-${COMP}/*.xml ${JETTY_BASE}/config/catalog-${COMP}
+exitOnError $? "copy_xml_files_to_config"
+
+cp -r /opt/app/sdc/config/catalog-${COMP}/*.yaml ${JETTY_BASE}/config/catalog-${COMP}
+exitOnError $? "copy_yaml_files_to_config"
diff --git a/catalog-fe/src/main/resources/scripts/installJettyBase.sh b/catalog-fe/src/main/resources/scripts/installJettyBase.sh
new file mode 100644
index 0000000000..0f8ac7e109
--- /dev/null
+++ b/catalog-fe/src/main/resources/scripts/installJettyBase.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+export JETTY_HOME=/home/jetty/jetty-distribution-9.2.7.v20150116
+export JETTY_BASE=/home/jetty/base
+
+mkdir -p ${JETTY_BASE}
+mkdir -p ${JETTY_BASE}/config
+
+cd ${JETTY_BASE}
+
+java -jar $JETTY_HOME/start.jar --add-to-start=deploy
+java -jar $JETTY_HOME/start.jar --add-to-startd=http,https,logging,ipaccess
+
+cd -
diff --git a/catalog-fe/src/main/resources/scripts/jvm.properties b/catalog-fe/src/main/resources/scripts/jvm.properties
new file mode 100644
index 0000000000..52b5134f34
--- /dev/null
+++ b/catalog-fe/src/main/resources/scripts/jvm.properties
@@ -0,0 +1,5 @@
+-XX:MaxPermSize=256m
+-Xmx1500m
+-Dconfig.home=${JETTY_BASE}/config
+-Dlog.home=${JETTY_BASE}/logs
+-Dlogback.configurationFile=${JETTY_BASE}/config/catalog-fe/logback.xml \ No newline at end of file
diff --git a/catalog-fe/src/main/resources/scripts/startJetty.sh b/catalog-fe/src/main/resources/scripts/startJetty.sh
new file mode 100644
index 0000000000..074d91d6f7
--- /dev/null
+++ b/catalog-fe/src/main/resources/scripts/startJetty.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+export JETTY_HOME=/home/jetty/jetty-distribution-9.2.7.v20150116
+export JETTY_BASE=/home/jetty/base
+
+eval "jvmargs=`sed '/^#/d' jvm.properties | paste -s -d"#"`"
+jvmargs=`echo $jvmargs | sed 's/#/ /g'`
+echo $jvmargs
+
+java -jar $JETTY_HOME/start.jar $jvmargs $@ \ No newline at end of file
diff --git a/catalog-fe/src/main/resources/scripts/updateSslParams.sh b/catalog-fe/src/main/resources/scripts/updateSslParams.sh
new file mode 100644
index 0000000000..d9e955e0f5
--- /dev/null
+++ b/catalog-fe/src/main/resources/scripts/updateSslParams.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+function usage() {
+ echo "$0 <working dir>"
+}
+
+function exitOnError() {
+ if [ $1 -ne 0 ]
+ then
+ echo "Failed running task $2"
+ exit 2
+ fi
+}
+
+if [ $# -ne 1 ]
+then
+ usage
+ if [ ${#OLDPWD} -ne 0 ]
+ then
+ cd -
+ fi
+ exit 1
+
+fi
+
+WORK_DIR=$1
+
+cd $WORK_DIR
+
+sed -i 's/\(^https.port=\)\(.*\)/\1443/g' start.d/https.ini
+exitOnError $? "update_port_in_https_ini"
+
+cd -
diff --git a/catalog-fe/src/main/webapp/META-INF/MANIFEST.MF b/catalog-fe/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..c47899c09d
--- /dev/null
+++ b/catalog-fe/src/main/webapp/META-INF/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Implementation-Title: catalog-fe
+Implementation-Version: APPLICATION_VERSION
+Implementation-Vendor-Id: org.openecomp.sdc
+Built-By: esofer
+Build-Jdk: 1.7.0_45
+Created-By: Apache Maven 3.2.1
+Archiver-Version: Plexus Archiver
+
diff --git a/catalog-fe/src/main/webapp/WEB-INF/jetty-web.xml b/catalog-fe/src/main/webapp/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000000..adb07b0ec8
--- /dev/null
+++ b/catalog-fe/src/main/webapp/WEB-INF/jetty-web.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+ "-//Mort Bay Consulting//DTD Configure//EN"
+ "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Set name="contextPath">/sdc1</Set>
+</Configure>
diff --git a/catalog-fe/src/main/webapp/WEB-INF/web.xml b/catalog-fe/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..f6d709ef58
--- /dev/null
+++ b/catalog-fe/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <servlet>
+ <servlet-name>jersey</servlet-name>
+ <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>jersey.config.server.provider.packages</param-name>
+ <param-value>org.openecomp.sdc.fe.servlets</param-value>
+ </init-param>
+
+ <init-param>
+ <param-name>jersey.config.server.provider.classnames</param-name>
+ <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
+ </init-param>
+ <init-param>
+ <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>jersey</servlet-name>
+ <url-pattern>/rest/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ViewStatusMessages</servlet-name>
+ <servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ViewStatusMessages</servlet-name>
+ <url-pattern>/lbClassicStatus</url-pattern>
+ </servlet-mapping>
+
+ <!-- Proxy Server Only used for tests to simulate webSeal ## Start ## -->
+<!-- <servlet>
+ <servlet-name>ProxyAdmin1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletAdmin1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyAdmin1</servlet-name>
+ <url-pattern>/proxy-admin1</url-pattern>
+ </servlet-mapping>
+ <servlet>
+ <servlet-name>ProxyAdmin2</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletAdmin2</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyAdmin2</servlet-name>
+ <url-pattern>/proxy-admin2</url-pattern>
+ </servlet-mapping>
+ <servlet>
+ <servlet-name>ProxyAdmin3</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletAdmin3</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyAdmin3</servlet-name>
+ <url-pattern>/proxy-admin3</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyDesigner1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletDesigner1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyDesigner1</servlet-name>
+ <url-pattern>/proxy-designer1</url-pattern>
+ </servlet-mapping>
+ <servlet>
+ <servlet-name>ProxyDesigner2</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletDesigner2</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyDesigner2</servlet-name>
+ <url-pattern>/proxy-designer2</url-pattern>
+ </servlet-mapping>
+ <servlet>
+ <servlet-name>ProxyDesigner3</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletDesigner3</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyDesigner3</servlet-name>
+ <url-pattern>/proxy-designer3</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyTester1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletTester1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyTester1</servlet-name>
+ <url-pattern>/proxy-tester1</url-pattern>
+ </servlet-mapping>
+ <servlet>
+ <servlet-name>ProxyTester2</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletTester2</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyTester2</servlet-name>
+ <url-pattern>/proxy-tester2</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyTester3</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletTester3</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyTester3</servlet-name>
+ <url-pattern>/proxy-tester3</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>ProxyGovernor1</servlet-name>
+ <url-pattern>/proxy-governor1</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyGovernor1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletGovernor1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyOps1</servlet-name>
+ <url-pattern>/proxy-ops1</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyOps1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletOps1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>ProxyPs1</servlet-name>
+ <url-pattern>/proxy-ps1</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyPs1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletProductStrategist1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>ProxyPm1</servlet-name>
+ <url-pattern>/proxy-pm1</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>ProxyPm1</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.websealmock.WebSealSimulatorServletProductManger1</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet> -->
+
+ <!-- Proxy Server Only used for tests to simulate webSeal ## End ## -->
+
+ <!-- Fe Proxy Servlet -->
+ <servlet>
+ <servlet-name>FeProxy</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.FeProxyServlet</servlet-class>
+
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+
+
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>FeProxy</servlet-name>
+ <url-pattern>/feProxy/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>Portal</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.PortalServlet</servlet-class>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Portal</servlet-name>
+ <url-pattern>/portal</url-pattern>
+ </servlet-mapping>
+
+ <!-- Kibana proxy -->
+ <servlet>
+ <servlet-name>KibanaProxy</servlet-name>
+ <servlet-class>org.openecomp.sdc.fe.servlets.KibanaServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>KibanaProxy</servlet-name>
+ <url-pattern>/kibanaProxy/*</url-pattern>
+ </servlet-mapping>
+
+
+
+ <filter>
+ <filter-name>cross-origin-att</filter-name>
+ <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
+ <async-supported>true</async-supported>
+ <init-param>
+ <param-name>allowedOrigins</param-name>
+ <param-value>*</param-value>
+ </init-param>
+ <init-param>
+ <param-name>allowedMethods</param-name>
+ <param-value>GET,POST,OPTIONS,DELETE,PUT,HEAD</param-value>
+ </init-param>
+ <init-param>
+ <param-name>allowedHeaders</param-name>
+ <param-value>HTTP_CSP_FIRSTNAME, HTTP_CSP_LASTNAME, USER_ID,
+ HTTP_CSP_EMAIL, X-ECOMP-RequestID, origin, content-type, accept, authorization, Content-MD5,X-ECOMP-ServiceID</param-value>
+ </init-param>
+ <init-param>
+ <param-name>allowCredential</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ </filter>
+ <filter-mapping>
+ <filter-name>cross-origin-att</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+<!--
+ <filter>
+ <filter-name>GzipFilter</filter-name>
+ <filter-class>org.eclipse.jetty.servlets.GzipFilter</filter-class>
+ <async-supported>true</async-supported>
+ <init-param>
+ <param-name></param-name>
+ <param-value>text/html,text/plain,text/css,application/javascript,application/json</param-value>
+ </init-param>
+ <init-param>
+ <param-name>methods</param-name>
+ <param-value>get,post</param-value>
+ </init-param>
+ </filter>
+
+
+ <filter-mapping>
+ <filter-name>GzipFilter</filter-name>
+ <url-pattern>/sdc2/*</url-pattern>
+ </filter-mapping>
+-->
+
+ <listener>
+ <listener-class>org.openecomp.sdc.fe.listen.FEAppContextListener</listener-class>
+ </listener>
+
+ <welcome-file-list>
+ <welcome-file>index.html</welcome-file>
+ </welcome-file-list>
+</web-app>
diff --git a/catalog-fe/src/test/SpecRunner.html b/catalog-fe/src/test/SpecRunner.html
new file mode 100644
index 0000000000..d2617c5b5c
--- /dev/null
+++ b/catalog-fe/src/test/SpecRunner.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Jasmine Spec Runner v2.0.0</title>
+
+ <link rel="shortcut icon" type="image/png" href="jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine_favicon.png">
+ <link rel="stylesheet" type="text/css" href="jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.css">
+
+ <script type="text/javascript" src="jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js"></script>
+ <script type="text/javascript" src="jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine-html.js"></script>
+ <script type="text/javascript" src="jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js"></script>
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
+ <!--script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script-->
+ <script type="text/javascript" src="testScripts/jasmine-fixture.js"></script>
+
+ <link href="../main/webapp/scripts/style.css" rel="stylesheet" />
+ <!-- include source files here... -->
+
+ <!-- JSZIP -->
+ <script type="text/javascript" src="../main/webapp/scripts/jszip.min.js"></script>
+ <!-- MD5 -->
+ <script type="text/javascript" src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js"></script>
+
+ <script type="text/javascript" src="../main/webapp/scripts/domHandler.js"></script>
+ <script type="text/javascript" src="../main/webapp/scripts/fileHandler.js"></script>
+ <script type="text/javascript" src="../main/webapp/scripts/jsHandler.js"></script>
+ <script type="text/javascript" src="testScripts/filesContents.js"></script>
+ <!-- include spec files here... -->
+ <script type="text/javascript" src="spec/codeSpec.js"></script>
+
+ </head>
+
+ <body>
+ </body>
+</html>
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/MIT.LICENSE b/catalog-fe/src/test/jasmine-standalone-2.0.0/MIT.LICENSE
new file mode 100644
index 0000000000..7c435baaec
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/MIT.LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2011 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/SpecRunner.html b/catalog-fe/src/test/jasmine-standalone-2.0.0/SpecRunner.html
new file mode 100644
index 0000000000..a0e39f4b0c
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/SpecRunner.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Jasmine Spec Runner v2.0.0</title>
+
+ <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png">
+ <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css">
+
+ <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
+ <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
+ <script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>
+
+ <!-- include source files here... -->
+ <script type="text/javascript" src="src/Player.js"></script>
+ <script type="text/javascript" src="src/Song.js"></script>
+
+ <!-- include spec files here... -->
+ <script type="text/javascript" src="spec/SpecHelper.js"></script>
+ <script type="text/javascript" src="spec/PlayerSpec.js"></script>
+
+</head>
+
+<body>
+</body>
+</html>
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js
new file mode 100644
index 0000000000..b1686af19b
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js
@@ -0,0 +1,201 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
+
+ If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
+
+ The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
+
+ [jasmine-gem]: http://github.com/pivotal/jasmine-gem
+ */
+
+(function() {
+
+ /**
+ * ## Require &amp; Instantiate
+ *
+ * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
+ */
+ window.jasmine = jasmineRequire.core(jasmineRequire);
+
+ /**
+ * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
+ */
+ jasmineRequire.html(jasmine);
+
+ /**
+ * Create the Jasmine environment. This is used to run all specs in a project.
+ */
+ var env = jasmine.getEnv();
+
+ /**
+ * ## The Global Interface
+ *
+ * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
+ */
+ var jasmineInterface = {
+ describe: function(description, specDefinitions) {
+ return env.describe(description, specDefinitions);
+ },
+
+ xdescribe: function(description, specDefinitions) {
+ return env.xdescribe(description, specDefinitions);
+ },
+
+ it: function(desc, func) {
+ return env.it(desc, func);
+ },
+
+ xit: function(desc, func) {
+ return env.xit(desc, func);
+ },
+
+ beforeEach: function(beforeEachFunction) {
+ return env.beforeEach(beforeEachFunction);
+ },
+
+ afterEach: function(afterEachFunction) {
+ return env.afterEach(afterEachFunction);
+ },
+
+ expect: function(actual) {
+ return env.expect(actual);
+ },
+
+ pending: function() {
+ return env.pending();
+ },
+
+ spyOn: function(obj, methodName) {
+ return env.spyOn(obj, methodName);
+ },
+
+ jsApiReporter: new jasmine.JsApiReporter({
+ timer: new jasmine.Timer()
+ })
+ };
+
+ /**
+ * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
+ */
+ if (typeof window == "undefined" && typeof exports == "object") {
+ extend(exports, jasmineInterface);
+ } else {
+ extend(window, jasmineInterface);
+ }
+
+ /**
+ * Expose the interface for adding custom equality testers.
+ */
+ jasmine.addCustomEqualityTester = function(tester) {
+ env.addCustomEqualityTester(tester);
+ };
+
+ /**
+ * Expose the interface for adding custom expectation matchers
+ */
+ jasmine.addMatchers = function(matchers) {
+ return env.addMatchers(matchers);
+ };
+
+ /**
+ * Expose the mock interface for the JavaScript timeout functions
+ */
+ jasmine.clock = function() {
+ return env.clock;
+ };
+
+ /**
+ * ## Runner Parameters
+ *
+ * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
+ */
+
+ var queryString = new jasmine.QueryString({
+ getWindowLocation: function() { return window.location; }
+ });
+
+ var catchingExceptions = queryString.getParam("catch");
+ env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
+
+ /**
+ * ## Reporters
+ * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
+ */
+ var htmlReporter = new jasmine.HtmlReporter({
+ env: env,
+ onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); },
+ getContainer: function() { return document.body; },
+ createElement: function() { return document.createElement.apply(document, arguments); },
+ createTextNode: function() { return document.createTextNode.apply(document, arguments); },
+ timer: new jasmine.Timer()
+ });
+
+ /**
+ * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
+ */
+ env.addReporter(jasmineInterface.jsApiReporter);
+ env.addReporter(htmlReporter);
+
+ /**
+ * Filter which specs will be run by matching the start of the full name against the `spec` query param.
+ */
+ var specFilter = new jasmine.HtmlSpecFilter({
+ filterString: function() { return queryString.getParam("spec"); }
+ });
+
+ env.specFilter = function(spec) {
+ return specFilter.matches(spec.getFullName());
+ };
+
+ /**
+ * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
+ */
+ window.setTimeout = window.setTimeout;
+ window.setInterval = window.setInterval;
+ window.clearTimeout = window.clearTimeout;
+ window.clearInterval = window.clearInterval;
+
+ /**
+ * ## Execution
+ *
+ * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
+ */
+ var currentWindowOnload = window.onload;
+
+ window.onload = function() {
+ if (currentWindowOnload) {
+ currentWindowOnload();
+ }
+ htmlReporter.initialize();
+ env.execute();
+ };
+
+ /**
+ * Helper function for readability above.
+ */
+ function extend(destination, source) {
+ for (var property in source) destination[property] = source[property];
+ return destination;
+ }
+
+}());
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/console.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/console.js
new file mode 100644
index 0000000000..4e31320575
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/console.js
@@ -0,0 +1,180 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*
+Copyright (c) 2008-2013 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+function getJasmineRequireObj() {
+ if (typeof module !== "undefined" && module.exports) {
+ return exports;
+ } else {
+ window.jasmineRequire = window.jasmineRequire || {};
+ return window.jasmineRequire;
+ }
+}
+
+getJasmineRequireObj().console = function(jRequire, j$) {
+ j$.ConsoleReporter = jRequire.ConsoleReporter();
+};
+
+getJasmineRequireObj().ConsoleReporter = function() {
+
+ var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ function ConsoleReporter(options) {
+ var print = options.print,
+ showColors = options.showColors || false,
+ onComplete = options.onComplete || function() {},
+ timer = options.timer || noopTimer,
+ specCount,
+ failureCount,
+ failedSpecs = [],
+ pendingCount,
+ ansi = {
+ green: '\x1B[32m',
+ red: '\x1B[31m',
+ yellow: '\x1B[33m',
+ none: '\x1B[0m'
+ };
+
+ this.jasmineStarted = function() {
+ specCount = 0;
+ failureCount = 0;
+ pendingCount = 0;
+ print("Started");
+ printNewline();
+ timer.start();
+ };
+
+ this.jasmineDone = function() {
+ printNewline();
+ for (var i = 0; i < failedSpecs.length; i++) {
+ specFailureDetails(failedSpecs[i]);
+ }
+
+ printNewline();
+ var specCounts = specCount + " " + plural("spec", specCount) + ", " +
+ failureCount + " " + plural("failure", failureCount);
+
+ if (pendingCount) {
+ specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount);
+ }
+
+ print(specCounts);
+
+ printNewline();
+ var seconds = timer.elapsed() / 1000;
+ print("Finished in " + seconds + " " + plural("second", seconds));
+
+ printNewline();
+
+ onComplete(failureCount === 0);
+ };
+
+ this.specDone = function(result) {
+ specCount++;
+
+ if (result.status == "pending") {
+ pendingCount++;
+ print(colored("yellow", "*"));
+ return;
+ }
+
+ if (result.status == "passed") {
+ print(colored("green", '.'));
+ return;
+ }
+
+ if (result.status == "failed") {
+ failureCount++;
+ failedSpecs.push(result);
+ print(colored("red", 'F'));
+ }
+ };
+
+ return this;
+
+ function printNewline() {
+ print("\n");
+ }
+
+ function colored(color, str) {
+ return showColors ? (ansi[color] + str + ansi.none) : str;
+ }
+
+ function plural(str, count) {
+ return count == 1 ? str : str + "s";
+ }
+
+ function repeat(thing, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(thing);
+ }
+ return arr;
+ }
+
+ function indent(str, spaces) {
+ var lines = (str || '').split("\n");
+ var newArr = [];
+ for (var i = 0; i < lines.length; i++) {
+ newArr.push(repeat(" ", spaces).join("") + lines[i]);
+ }
+ return newArr.join("\n");
+ }
+
+ function specFailureDetails(result) {
+ printNewline();
+ print(result.fullName);
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var failedExpectation = result.failedExpectations[i];
+ printNewline();
+ print(indent(failedExpectation.stack, 2));
+ }
+
+ printNewline();
+ }
+ }
+
+ return ConsoleReporter;
+};
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine-html.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine-html.js
new file mode 100644
index 0000000000..f7469e97b7
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine-html.js
@@ -0,0 +1,379 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*
+Copyright (c) 2008-2013 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+jasmineRequire.html = function(j$) {
+ j$.ResultsNode = jasmineRequire.ResultsNode();
+ j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
+ j$.QueryString = jasmineRequire.QueryString();
+ j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
+};
+
+jasmineRequire.HtmlReporter = function(j$) {
+
+ var noopTimer = {
+ start: function() {},
+ elapsed: function() { return 0; }
+ };
+
+ function HtmlReporter(options) {
+ var env = options.env || {},
+ getContainer = options.getContainer,
+ createElement = options.createElement,
+ createTextNode = options.createTextNode,
+ onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
+ timer = options.timer || noopTimer,
+ results = [],
+ specsExecuted = 0,
+ failureCount = 0,
+ pendingSpecCount = 0,
+ htmlReporterMain,
+ symbols;
+
+ this.initialize = function() {
+ htmlReporterMain = createDom("div", {className: "html-reporter"},
+ createDom("div", {className: "banner"},
+ createDom("span", {className: "title"}, "Jasmine"),
+ createDom("span", {className: "version"}, j$.version)
+ ),
+ createDom("ul", {className: "symbol-summary"}),
+ createDom("div", {className: "alert"}),
+ createDom("div", {className: "results"},
+ createDom("div", {className: "failures"})
+ )
+ );
+ getContainer().appendChild(htmlReporterMain);
+
+ symbols = find(".symbol-summary");
+ };
+
+ var totalSpecsDefined;
+ this.jasmineStarted = function(options) {
+ totalSpecsDefined = options.totalSpecsDefined || 0;
+ timer.start();
+ };
+
+ var summary = createDom("div", {className: "summary"});
+
+ var topResults = new j$.ResultsNode({}, "", null),
+ currentParent = topResults;
+
+ this.suiteStarted = function(result) {
+ currentParent.addChild(result, "suite");
+ currentParent = currentParent.last();
+ };
+
+ this.suiteDone = function(result) {
+ if (currentParent == topResults) {
+ return;
+ }
+
+ currentParent = currentParent.parent;
+ };
+
+ this.specStarted = function(result) {
+ currentParent.addChild(result, "spec");
+ };
+
+ var failures = [];
+ this.specDone = function(result) {
+ if (result.status != "disabled") {
+ specsExecuted++;
+ }
+
+ symbols.appendChild(createDom("li", {
+ className: result.status,
+ id: "spec_" + result.id,
+ title: result.fullName
+ }
+ ));
+
+ if (result.status == "failed") {
+ failureCount++;
+
+ var failure =
+ createDom("div", {className: "spec-detail failed"},
+ createDom("div", {className: "description"},
+ createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName)
+ ),
+ createDom("div", {className: "messages"})
+ );
+ var messages = failure.childNodes[1];
+
+ for (var i = 0; i < result.failedExpectations.length; i++) {
+ var expectation = result.failedExpectations[i];
+ messages.appendChild(createDom("div", {className: "result-message"}, expectation.message));
+ messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack));
+ }
+
+ failures.push(failure);
+ }
+
+ if (result.status == "pending") {
+ pendingSpecCount++;
+ }
+ };
+
+ this.jasmineDone = function() {
+ var banner = find(".banner");
+ banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s"));
+
+ var alert = find(".alert");
+
+ alert.appendChild(createDom("span", { className: "exceptions" },
+ createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"),
+ createDom("input", {
+ className: "raise",
+ id: "raise-exceptions",
+ type: "checkbox"
+ })
+ ));
+ var checkbox = find("input");
+
+ checkbox.checked = !env.catchingExceptions();
+ checkbox.onclick = onRaiseExceptionsClick;
+
+ if (specsExecuted < totalSpecsDefined) {
+ var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all";
+ alert.appendChild(
+ createDom("span", {className: "bar skipped"},
+ createDom("a", {href: "?", title: "Run all specs"}, skippedMessage)
+ )
+ );
+ }
+ var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount);
+ if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); }
+
+ var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed");
+ alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage));
+
+ var results = find(".results");
+ results.appendChild(summary);
+
+ summaryList(topResults, summary);
+
+ function summaryList(resultsTree, domParent) {
+ var specListNode;
+ for (var i = 0; i < resultsTree.children.length; i++) {
+ var resultNode = resultsTree.children[i];
+ if (resultNode.type == "suite") {
+ var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id},
+ createDom("li", {className: "suite-detail"},
+ createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description)
+ )
+ );
+
+ summaryList(resultNode, suiteListNode);
+ domParent.appendChild(suiteListNode);
+ }
+ if (resultNode.type == "spec") {
+ if (domParent.getAttribute("class") != "specs") {
+ specListNode = createDom("ul", {className: "specs"});
+ domParent.appendChild(specListNode);
+ }
+ specListNode.appendChild(
+ createDom("li", {
+ className: resultNode.result.status,
+ id: "spec-" + resultNode.result.id
+ },
+ createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description)
+ )
+ );
+ }
+ }
+ }
+
+ if (failures.length) {
+ alert.appendChild(
+ createDom('span', {className: "menu bar spec-list"},
+ createDom("span", {}, "Spec List | "),
+ createDom('a', {className: "failures-menu", href: "#"}, "Failures")));
+ alert.appendChild(
+ createDom('span', {className: "menu bar failure-list"},
+ createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"),
+ createDom("span", {}, " | Failures ")));
+
+ find(".failures-menu").onclick = function() {
+ setMenuModeTo('failure-list');
+ };
+ find(".spec-list-menu").onclick = function() {
+ setMenuModeTo('spec-list');
+ };
+
+ setMenuModeTo('failure-list');
+
+ var failureNode = find(".failures");
+ for (var i = 0; i < failures.length; i++) {
+ failureNode.appendChild(failures[i]);
+ }
+ }
+ };
+
+ return this;
+
+ function find(selector) {
+ return getContainer().querySelector(selector);
+ }
+
+ function createDom(type, attrs, childrenVarArgs) {
+ var el = createElement(type);
+
+ for (var i = 2; i < arguments.length; i++) {
+ var child = arguments[i];
+
+ if (typeof child === 'string') {
+ el.appendChild(createTextNode(child));
+ } else {
+ if (child) {
+ el.appendChild(child);
+ }
+ }
+ }
+
+ for (var attr in attrs) {
+ if (attr == "className") {
+ el[attr] = attrs[attr];
+ } else {
+ el.setAttribute(attr, attrs[attr]);
+ }
+ }
+
+ return el;
+ }
+
+ function pluralize(singular, count) {
+ var word = (count == 1 ? singular : singular + "s");
+
+ return "" + count + " " + word;
+ }
+
+ function specHref(result) {
+ return "?spec=" + encodeURIComponent(result.fullName);
+ }
+
+ function setMenuModeTo(mode) {
+ htmlReporterMain.setAttribute("class", "html-reporter " + mode);
+ }
+ }
+
+ return HtmlReporter;
+};
+
+jasmineRequire.HtmlSpecFilter = function() {
+ function HtmlSpecFilter(options) {
+ var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ var filterPattern = new RegExp(filterString);
+
+ this.matches = function(specName) {
+ return filterPattern.test(specName);
+ };
+ }
+
+ return HtmlSpecFilter;
+};
+
+jasmineRequire.ResultsNode = function() {
+ function ResultsNode(result, type, parent) {
+ this.result = result;
+ this.type = type;
+ this.parent = parent;
+
+ this.children = [];
+
+ this.addChild = function(result, type) {
+ this.children.push(new ResultsNode(result, type, this));
+ };
+
+ this.last = function() {
+ return this.children[this.children.length - 1];
+ };
+ }
+
+ return ResultsNode;
+};
+
+jasmineRequire.QueryString = function() {
+ function QueryString(options) {
+
+ this.setParam = function(key, value) {
+ var paramMap = queryStringToParamMap();
+ paramMap[key] = value;
+ options.getWindowLocation().search = toQueryString(paramMap);
+ };
+
+ this.getParam = function(key) {
+ return queryStringToParamMap()[key];
+ };
+
+ return this;
+
+ function toQueryString(paramMap) {
+ var qStrPairs = [];
+ for (var prop in paramMap) {
+ qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop]));
+ }
+ return "?" + qStrPairs.join('&');
+ }
+
+ function queryStringToParamMap() {
+ var paramStr = options.getWindowLocation().search.substring(1),
+ params = [],
+ paramMap = {};
+
+ if (paramStr.length > 0) {
+ params = paramStr.split('&');
+ for (var i = 0; i < params.length; i++) {
+ var p = params[i].split('=');
+ var value = decodeURIComponent(p[1]);
+ if (value === "true" || value === "false") {
+ value = JSON.parse(value);
+ }
+ paramMap[decodeURIComponent(p[0])] = value;
+ }
+ }
+
+ return paramMap;
+ }
+
+ }
+
+ return QueryString;
+};
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.css b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.css
new file mode 100644
index 0000000000..f4d35b6e78
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.css
@@ -0,0 +1,55 @@
+body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
+
+.html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+.html-reporter a { text-decoration: none; }
+.html-reporter a:hover { text-decoration: underline; }
+.html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; }
+.html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
+.html-reporter .banner .version { margin-left: 14px; }
+.html-reporter #jasmine_content { position: fixed; right: 100%; }
+.html-reporter .version { color: #aaaaaa; }
+.html-reporter .banner { margin-top: 14px; }
+.html-reporter .duration { color: #aaaaaa; float: right; }
+.html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+.html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
+.html-reporter .symbol-summary li.passed { font-size: 14px; }
+.html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; }
+.html-reporter .symbol-summary li.failed { line-height: 9px; }
+.html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
+.html-reporter .symbol-summary li.disabled { font-size: 14px; }
+.html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
+.html-reporter .symbol-summary li.pending { line-height: 17px; }
+.html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
+.html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
+.html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+.html-reporter .bar.failed { background-color: #b03911; }
+.html-reporter .bar.passed { background-color: #a6b779; }
+.html-reporter .bar.skipped { background-color: #bababa; }
+.html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; }
+.html-reporter .bar.menu a { color: #333333; }
+.html-reporter .bar a { color: white; }
+.html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; }
+.html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; }
+.html-reporter .running-alert { background-color: #666666; }
+.html-reporter .results { margin-top: 14px; }
+.html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+.html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+.html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+.html-reporter.showDetails .summary { display: none; }
+.html-reporter.showDetails #details { display: block; }
+.html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+.html-reporter .summary { margin-top: 14px; }
+.html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
+.html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
+.html-reporter .summary li.passed a { color: #5e7d00; }
+.html-reporter .summary li.failed a { color: #b03911; }
+.html-reporter .summary li.pending a { color: #ba9d37; }
+.html-reporter .description + .suite { margin-top: 0; }
+.html-reporter .suite { margin-top: 14px; }
+.html-reporter .suite a { color: #333333; }
+.html-reporter .failures .spec-detail { margin-bottom: 28px; }
+.html-reporter .failures .spec-detail .description { background-color: #b03911; }
+.html-reporter .failures .spec-detail .description a { color: white; }
+.html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; }
+.html-reporter .result-message span.result { display: block; }
+.html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js
new file mode 100644
index 0000000000..a1390a20c5
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js
@@ -0,0 +1,2422 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/*
+Copyright (c) 2008-2013 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+function getJasmineRequireObj() {
+ if (typeof module !== "undefined" && module.exports) {
+ return exports;
+ } else {
+ window.jasmineRequire = window.jasmineRequire || {};
+ return window.jasmineRequire;
+ }
+}
+
+getJasmineRequireObj().core = function(jRequire) {
+ var j$ = {};
+
+ jRequire.base(j$);
+ j$.util = jRequire.util();
+ j$.Any = jRequire.Any();
+ j$.CallTracker = jRequire.CallTracker();
+ j$.Clock = jRequire.Clock();
+ j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
+ j$.Env = jRequire.Env(j$);
+ j$.ExceptionFormatter = jRequire.ExceptionFormatter();
+ j$.Expectation = jRequire.Expectation();
+ j$.buildExpectationResult = jRequire.buildExpectationResult();
+ j$.JsApiReporter = jRequire.JsApiReporter();
+ j$.matchersUtil = jRequire.matchersUtil(j$);
+ j$.ObjectContaining = jRequire.ObjectContaining(j$);
+ j$.pp = jRequire.pp(j$);
+ j$.QueueRunner = jRequire.QueueRunner();
+ j$.ReportDispatcher = jRequire.ReportDispatcher();
+ j$.Spec = jRequire.Spec(j$);
+ j$.SpyStrategy = jRequire.SpyStrategy();
+ j$.Suite = jRequire.Suite();
+ j$.Timer = jRequire.Timer();
+ j$.version = jRequire.version();
+
+ j$.matchers = jRequire.requireMatchers(jRequire, j$);
+
+ return j$;
+};
+
+getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
+ var availableMatchers = [
+ "toBe",
+ "toBeCloseTo",
+ "toBeDefined",
+ "toBeFalsy",
+ "toBeGreaterThan",
+ "toBeLessThan",
+ "toBeNaN",
+ "toBeNull",
+ "toBeTruthy",
+ "toBeUndefined",
+ "toContain",
+ "toEqual",
+ "toHaveBeenCalled",
+ "toHaveBeenCalledWith",
+ "toMatch",
+ "toThrow",
+ "toThrowError"
+ ],
+ matchers = {};
+
+ for (var i = 0; i < availableMatchers.length; i++) {
+ var name = availableMatchers[i];
+ matchers[name] = jRequire[name](j$);
+ }
+
+ return matchers;
+};
+
+getJasmineRequireObj().base = function(j$) {
+ j$.unimplementedMethod_ = function() {
+ throw new Error("unimplemented method");
+ };
+
+ j$.MAX_PRETTY_PRINT_DEPTH = 40;
+ j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+ j$.getGlobal = (function() {
+ var jasmineGlobal = eval.call(null, "this");
+ return function() {
+ return jasmineGlobal;
+ };
+ })();
+
+ j$.getEnv = function(options) {
+ var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
+ //jasmine. singletons in here (setTimeout blah blah).
+ return env;
+ };
+
+ j$.isArray_ = function(value) {
+ return j$.isA_("Array", value);
+ };
+
+ j$.isString_ = function(value) {
+ return j$.isA_("String", value);
+ };
+
+ j$.isNumber_ = function(value) {
+ return j$.isA_("Number", value);
+ };
+
+ j$.isA_ = function(typeName, value) {
+ return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+ };
+
+ j$.isDomNode = function(obj) {
+ return obj.nodeType > 0;
+ };
+
+ j$.any = function(clazz) {
+ return new j$.Any(clazz);
+ };
+
+ j$.objectContaining = function(sample) {
+ return new j$.ObjectContaining(sample);
+ };
+
+ j$.createSpy = function(name, originalFn) {
+
+ var spyStrategy = new j$.SpyStrategy({
+ name: name,
+ fn: originalFn,
+ getSpy: function() { return spy; }
+ }),
+ callTracker = new j$.CallTracker(),
+ spy = function() {
+ callTracker.track({
+ object: this,
+ args: Array.prototype.slice.apply(arguments)
+ });
+ return spyStrategy.exec.apply(this, arguments);
+ };
+
+ for (var prop in originalFn) {
+ if (prop === 'and' || prop === 'calls') {
+ throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon");
+ }
+
+ spy[prop] = originalFn[prop];
+ }
+
+ spy.and = spyStrategy;
+ spy.calls = callTracker;
+
+ return spy;
+ };
+
+ j$.isSpy = function(putativeSpy) {
+ if (!putativeSpy) {
+ return false;
+ }
+ return putativeSpy.and instanceof j$.SpyStrategy &&
+ putativeSpy.calls instanceof j$.CallTracker;
+ };
+
+ j$.createSpyObj = function(baseName, methodNames) {
+ if (!j$.isArray_(methodNames) || methodNames.length === 0) {
+ throw "createSpyObj requires a non-empty array of method names to create spies for";
+ }
+ var obj = {};
+ for (var i = 0; i < methodNames.length; i++) {
+ obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
+ }
+ return obj;
+ };
+};
+
+getJasmineRequireObj().util = function() {
+
+ var util = {};
+
+ util.inherit = function(childClass, parentClass) {
+ var Subclass = function() {
+ };
+ Subclass.prototype = parentClass.prototype;
+ childClass.prototype = new Subclass();
+ };
+
+ util.htmlEscape = function(str) {
+ if (!str) {
+ return str;
+ }
+ return str.replace(/&/g, '&amp;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;');
+ };
+
+ util.argsToArray = function(args) {
+ var arrayOfArgs = [];
+ for (var i = 0; i < args.length; i++) {
+ arrayOfArgs.push(args[i]);
+ }
+ return arrayOfArgs;
+ };
+
+ util.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ return util;
+};
+
+getJasmineRequireObj().Spec = function(j$) {
+ function Spec(attrs) {
+ this.expectationFactory = attrs.expectationFactory;
+ this.resultCallback = attrs.resultCallback || function() {};
+ this.id = attrs.id;
+ this.description = attrs.description || '';
+ this.fn = attrs.fn;
+ this.beforeFns = attrs.beforeFns || function() { return []; };
+ this.afterFns = attrs.afterFns || function() { return []; };
+ this.onStart = attrs.onStart || function() {};
+ this.exceptionFormatter = attrs.exceptionFormatter || function() {};
+ this.getSpecName = attrs.getSpecName || function() { return ''; };
+ this.expectationResultFactory = attrs.expectationResultFactory || function() { };
+ this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
+ this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
+
+ this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout};
+
+ if (!this.fn) {
+ this.pend();
+ }
+
+ this.result = {
+ id: this.id,
+ description: this.description,
+ fullName: this.getFullName(),
+ failedExpectations: []
+ };
+ }
+
+ Spec.prototype.addExpectationResult = function(passed, data) {
+ if (passed) {
+ return;
+ }
+ this.result.failedExpectations.push(this.expectationResultFactory(data));
+ };
+
+ Spec.prototype.expect = function(actual) {
+ return this.expectationFactory(actual, this);
+ };
+
+ Spec.prototype.execute = function(onComplete) {
+ var self = this,
+ timeout;
+
+ this.onStart(this);
+
+ if (this.markedPending || this.disabled) {
+ complete();
+ return;
+ }
+
+ function timeoutable(fn) {
+ return function(done) {
+ timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() {
+ onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'));
+ done();
+ }, j$.DEFAULT_TIMEOUT_INTERVAL]]);
+
+ var callDone = function() {
+ clearTimeoutable();
+ done();
+ };
+
+ fn.call(this, callDone); //TODO: do we care about more than 1 arg?
+ };
+ }
+
+ function clearTimeoutable() {
+ Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]);
+ timeout = void 0;
+ }
+
+ var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()),
+ allTimeoutableFns = [];
+ for (var i = 0; i < allFns.length; i++) {
+ var fn = allFns[i];
+ allTimeoutableFns.push(fn.length > 0 ? timeoutable(fn) : fn);
+ }
+
+ this.queueRunnerFactory({
+ fns: allTimeoutableFns,
+ onException: onException,
+ onComplete: complete
+ });
+
+ function onException(e) {
+ clearTimeoutable();
+ if (Spec.isPendingSpecException(e)) {
+ self.pend();
+ return;
+ }
+
+ self.addExpectationResult(false, {
+ matcherName: "",
+ passed: false,
+ expected: "",
+ actual: "",
+ error: e
+ });
+ }
+
+ function complete() {
+ self.result.status = self.status();
+ self.resultCallback(self.result);
+
+ if (onComplete) {
+ onComplete();
+ }
+ }
+ };
+
+ Spec.prototype.disable = function() {
+ this.disabled = true;
+ };
+
+ Spec.prototype.pend = function() {
+ this.markedPending = true;
+ };
+
+ Spec.prototype.status = function() {
+ if (this.disabled) {
+ return 'disabled';
+ }
+
+ if (this.markedPending) {
+ return 'pending';
+ }
+
+ if (this.result.failedExpectations.length > 0) {
+ return 'failed';
+ } else {
+ return 'passed';
+ }
+ };
+
+ Spec.prototype.getFullName = function() {
+ return this.getSpecName(this);
+ };
+
+ Spec.pendingSpecExceptionMessage = "=> marked Pending";
+
+ Spec.isPendingSpecException = function(e) {
+ return e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1;
+ };
+
+ return Spec;
+};
+
+if (typeof window == void 0 && typeof exports == "object") {
+ exports.Spec = jasmineRequire.Spec;
+}
+
+getJasmineRequireObj().Env = function(j$) {
+ function Env(options) {
+ options = options || {};
+
+ var self = this;
+ var global = options.global || j$.getGlobal();
+
+ var totalSpecsDefined = 0;
+
+ var catchExceptions = true;
+
+ var realSetTimeout = j$.getGlobal().setTimeout;
+ var realClearTimeout = j$.getGlobal().clearTimeout;
+ this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler());
+
+ var runnableLookupTable = {};
+
+ var spies = [];
+
+ var currentSpec = null;
+ var currentSuite = null;
+
+ var reporter = new j$.ReportDispatcher([
+ "jasmineStarted",
+ "jasmineDone",
+ "suiteStarted",
+ "suiteDone",
+ "specStarted",
+ "specDone"
+ ]);
+
+ this.specFilter = function() {
+ return true;
+ };
+
+ var equalityTesters = [];
+
+ var customEqualityTesters = [];
+ this.addCustomEqualityTester = function(tester) {
+ customEqualityTesters.push(tester);
+ };
+
+ j$.Expectation.addCoreMatchers(j$.matchers);
+
+ var nextSpecId = 0;
+ var getNextSpecId = function() {
+ return 'spec' + nextSpecId++;
+ };
+
+ var nextSuiteId = 0;
+ var getNextSuiteId = function() {
+ return 'suite' + nextSuiteId++;
+ };
+
+ var expectationFactory = function(actual, spec) {
+ return j$.Expectation.Factory({
+ util: j$.matchersUtil,
+ customEqualityTesters: customEqualityTesters,
+ actual: actual,
+ addExpectationResult: addExpectationResult
+ });
+
+ function addExpectationResult(passed, result) {
+ return spec.addExpectationResult(passed, result);
+ }
+ };
+
+ var specStarted = function(spec) {
+ currentSpec = spec;
+ reporter.specStarted(spec.result);
+ };
+
+ var beforeFns = function(suite) {
+ return function() {
+ var befores = [];
+ while(suite) {
+ befores = befores.concat(suite.beforeFns);
+ suite = suite.parentSuite;
+ }
+ return befores.reverse();
+ };
+ };
+
+ var afterFns = function(suite) {
+ return function() {
+ var afters = [];
+ while(suite) {
+ afters = afters.concat(suite.afterFns);
+ suite = suite.parentSuite;
+ }
+ return afters;
+ };
+ };
+
+ var getSpecName = function(spec, suite) {
+ return suite.getFullName() + ' ' + spec.description;
+ };
+
+ // TODO: we may just be able to pass in the fn instead of wrapping here
+ var buildExpectationResult = j$.buildExpectationResult,
+ exceptionFormatter = new j$.ExceptionFormatter(),
+ expectationResultFactory = function(attrs) {
+ attrs.messageFormatter = exceptionFormatter.message;
+ attrs.stackFormatter = exceptionFormatter.stack;
+
+ return buildExpectationResult(attrs);
+ };
+
+ // TODO: fix this naming, and here's where the value comes in
+ this.catchExceptions = function(value) {
+ catchExceptions = !!value;
+ return catchExceptions;
+ };
+
+ this.catchingExceptions = function() {
+ return catchExceptions;
+ };
+
+ var maximumSpecCallbackDepth = 20;
+ var currentSpecCallbackDepth = 0;
+
+ function clearStack(fn) {
+ currentSpecCallbackDepth++;
+ if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) {
+ currentSpecCallbackDepth = 0;
+ realSetTimeout(fn, 0);
+ } else {
+ fn();
+ }
+ }
+
+ var catchException = function(e) {
+ return j$.Spec.isPendingSpecException(e) || catchExceptions;
+ };
+
+ var queueRunnerFactory = function(options) {
+ options.catchException = catchException;
+ options.clearStack = options.clearStack || clearStack;
+
+ new j$.QueueRunner(options).execute();
+ };
+
+ var topSuite = new j$.Suite({
+ env: this,
+ id: getNextSuiteId(),
+ description: 'Jasmine__TopLevel__Suite',
+ queueRunner: queueRunnerFactory,
+ resultCallback: function() {} // TODO - hook this up
+ });
+ runnableLookupTable[topSuite.id] = topSuite;
+ currentSuite = topSuite;
+
+ this.topSuite = function() {
+ return topSuite;
+ };
+
+ this.execute = function(runnablesToRun) {
+ runnablesToRun = runnablesToRun || [topSuite.id];
+
+ var allFns = [];
+ for(var i = 0; i < runnablesToRun.length; i++) {
+ var runnable = runnableLookupTable[runnablesToRun[i]];
+ allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable));
+ }
+
+ reporter.jasmineStarted({
+ totalSpecsDefined: totalSpecsDefined
+ });
+
+ queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone});
+ };
+
+ this.addReporter = function(reporterToAdd) {
+ reporter.addReporter(reporterToAdd);
+ };
+
+ this.addMatchers = function(matchersToAdd) {
+ j$.Expectation.addMatchers(matchersToAdd);
+ };
+
+ this.spyOn = function(obj, methodName) {
+ if (j$.util.isUndefined(obj)) {
+ throw new Error("spyOn could not find an object to spy upon for " + methodName + "()");
+ }
+
+ if (j$.util.isUndefined(obj[methodName])) {
+ throw new Error(methodName + '() method does not exist');
+ }
+
+ if (obj[methodName] && j$.isSpy(obj[methodName])) {
+ //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
+ throw new Error(methodName + ' has already been spied upon');
+ }
+
+ var spy = j$.createSpy(methodName, obj[methodName]);
+
+ spies.push({
+ spy: spy,
+ baseObj: obj,
+ methodName: methodName,
+ originalValue: obj[methodName]
+ });
+
+ obj[methodName] = spy;
+
+ return spy;
+ };
+
+ var suiteFactory = function(description) {
+ var suite = new j$.Suite({
+ env: self,
+ id: getNextSuiteId(),
+ description: description,
+ parentSuite: currentSuite,
+ queueRunner: queueRunnerFactory,
+ onStart: suiteStarted,
+ resultCallback: function(attrs) {
+ reporter.suiteDone(attrs);
+ }
+ });
+
+ runnableLookupTable[suite.id] = suite;
+ return suite;
+ };
+
+ this.describe = function(description, specDefinitions) {
+ var suite = suiteFactory(description);
+
+ var parentSuite = currentSuite;
+ parentSuite.addChild(suite);
+ currentSuite = suite;
+
+ var declarationError = null;
+ try {
+ specDefinitions.call(suite);
+ } catch (e) {
+ declarationError = e;
+ }
+
+ if (declarationError) {
+ this.it("encountered a declaration exception", function() {
+ throw declarationError;
+ });
+ }
+
+ currentSuite = parentSuite;
+
+ return suite;
+ };
+
+ this.xdescribe = function(description, specDefinitions) {
+ var suite = this.describe(description, specDefinitions);
+ suite.disable();
+ return suite;
+ };
+
+ var specFactory = function(description, fn, suite) {
+ totalSpecsDefined++;
+
+ var spec = new j$.Spec({
+ id: getNextSpecId(),
+ beforeFns: beforeFns(suite),
+ afterFns: afterFns(suite),
+ expectationFactory: expectationFactory,
+ exceptionFormatter: exceptionFormatter,
+ resultCallback: specResultCallback,
+ getSpecName: function(spec) {
+ return getSpecName(spec, suite);
+ },
+ onStart: specStarted,
+ description: description,
+ expectationResultFactory: expectationResultFactory,
+ queueRunnerFactory: queueRunnerFactory,
+ fn: fn,
+ timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}
+ });
+
+ runnableLookupTable[spec.id] = spec;
+
+ if (!self.specFilter(spec)) {
+ spec.disable();
+ }
+
+ return spec;
+
+ function removeAllSpies() {
+ for (var i = 0; i < spies.length; i++) {
+ var spyEntry = spies[i];
+ spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue;
+ }
+ spies = [];
+ }
+
+ function specResultCallback(result) {
+ removeAllSpies();
+ j$.Expectation.resetMatchers();
+ customEqualityTesters = [];
+ currentSpec = null;
+ reporter.specDone(result);
+ }
+ };
+
+ var suiteStarted = function(suite) {
+ reporter.suiteStarted(suite.result);
+ };
+
+ this.it = function(description, fn) {
+ var spec = specFactory(description, fn, currentSuite);
+ currentSuite.addChild(spec);
+ return spec;
+ };
+
+ this.xit = function(description, fn) {
+ var spec = this.it(description, fn);
+ spec.pend();
+ return spec;
+ };
+
+ this.expect = function(actual) {
+ return currentSpec.expect(actual);
+ };
+
+ this.beforeEach = function(beforeEachFunction) {
+ currentSuite.beforeEach(beforeEachFunction);
+ };
+
+ this.afterEach = function(afterEachFunction) {
+ currentSuite.afterEach(afterEachFunction);
+ };
+
+ this.pending = function() {
+ throw j$.Spec.pendingSpecExceptionMessage;
+ };
+ }
+
+ return Env;
+};
+
+getJasmineRequireObj().JsApiReporter = function() {
+
+ var noopTimer = {
+ start: function(){},
+ elapsed: function(){ return 0; }
+ };
+
+ function JsApiReporter(options) {
+ var timer = options.timer || noopTimer,
+ status = "loaded";
+
+ this.started = false;
+ this.finished = false;
+
+ this.jasmineStarted = function() {
+ this.started = true;
+ status = 'started';
+ timer.start();
+ };
+
+ var executionTime;
+
+ this.jasmineDone = function() {
+ this.finished = true;
+ executionTime = timer.elapsed();
+ status = 'done';
+ };
+
+ this.status = function() {
+ return status;
+ };
+
+ var suites = {};
+
+ this.suiteStarted = function(result) {
+ storeSuite(result);
+ };
+
+ this.suiteDone = function(result) {
+ storeSuite(result);
+ };
+
+ function storeSuite(result) {
+ suites[result.id] = result;
+ }
+
+ this.suites = function() {
+ return suites;
+ };
+
+ var specs = [];
+ this.specStarted = function(result) { };
+
+ this.specDone = function(result) {
+ specs.push(result);
+ };
+
+ this.specResults = function(index, length) {
+ return specs.slice(index, index + length);
+ };
+
+ this.specs = function() {
+ return specs;
+ };
+
+ this.executionTime = function() {
+ return executionTime;
+ };
+
+ }
+
+ return JsApiReporter;
+};
+
+getJasmineRequireObj().Any = function() {
+
+ function Any(expectedObject) {
+ this.expectedObject = expectedObject;
+ }
+
+ Any.prototype.jasmineMatches = function(other) {
+ if (this.expectedObject == String) {
+ return typeof other == 'string' || other instanceof String;
+ }
+
+ if (this.expectedObject == Number) {
+ return typeof other == 'number' || other instanceof Number;
+ }
+
+ if (this.expectedObject == Function) {
+ return typeof other == 'function' || other instanceof Function;
+ }
+
+ if (this.expectedObject == Object) {
+ return typeof other == 'object';
+ }
+
+ if (this.expectedObject == Boolean) {
+ return typeof other == 'boolean';
+ }
+
+ return other instanceof this.expectedObject;
+ };
+
+ Any.prototype.jasmineToString = function() {
+ return '<jasmine.any(' + this.expectedClass + ')>';
+ };
+
+ return Any;
+};
+
+getJasmineRequireObj().CallTracker = function() {
+
+ function CallTracker() {
+ var calls = [];
+
+ this.track = function(context) {
+ calls.push(context);
+ };
+
+ this.any = function() {
+ return !!calls.length;
+ };
+
+ this.count = function() {
+ return calls.length;
+ };
+
+ this.argsFor = function(index) {
+ var call = calls[index];
+ return call ? call.args : [];
+ };
+
+ this.all = function() {
+ return calls;
+ };
+
+ this.allArgs = function() {
+ var callArgs = [];
+ for(var i = 0; i < calls.length; i++){
+ callArgs.push(calls[i].args);
+ }
+
+ return callArgs;
+ };
+
+ this.first = function() {
+ return calls[0];
+ };
+
+ this.mostRecent = function() {
+ return calls[calls.length - 1];
+ };
+
+ this.reset = function() {
+ calls = [];
+ };
+ }
+
+ return CallTracker;
+};
+
+getJasmineRequireObj().Clock = function() {
+ function Clock(global, delayedFunctionScheduler) {
+ var self = this,
+ realTimingFunctions = {
+ setTimeout: global.setTimeout,
+ clearTimeout: global.clearTimeout,
+ setInterval: global.setInterval,
+ clearInterval: global.clearInterval
+ },
+ fakeTimingFunctions = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setInterval: setInterval,
+ clearInterval: clearInterval
+ },
+ installed = false,
+ timer;
+
+ self.install = function() {
+ replace(global, fakeTimingFunctions);
+ timer = fakeTimingFunctions;
+ installed = true;
+ };
+
+ self.uninstall = function() {
+ delayedFunctionScheduler.reset();
+ replace(global, realTimingFunctions);
+ timer = realTimingFunctions;
+ installed = false;
+ };
+
+ self.setTimeout = function(fn, delay, params) {
+ if (legacyIE()) {
+ if (arguments.length > 2) {
+ throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill");
+ }
+ return timer.setTimeout(fn, delay);
+ }
+ return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
+ };
+
+ self.setInterval = function(fn, delay, params) {
+ if (legacyIE()) {
+ if (arguments.length > 2) {
+ throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill");
+ }
+ return timer.setInterval(fn, delay);
+ }
+ return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
+ };
+
+ self.clearTimeout = function(id) {
+ return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
+ };
+
+ self.clearInterval = function(id) {
+ return Function.prototype.call.apply(timer.clearInterval, [global, id]);
+ };
+
+ self.tick = function(millis) {
+ if (installed) {
+ delayedFunctionScheduler.tick(millis);
+ } else {
+ throw new Error("Mock clock is not installed, use jasmine.clock().install()");
+ }
+ };
+
+ return self;
+
+ function legacyIE() {
+ //if these methods are polyfilled, apply will be present
+ return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
+ }
+
+ function replace(dest, source) {
+ for (var prop in source) {
+ dest[prop] = source[prop];
+ }
+ }
+
+ function setTimeout(fn, delay) {
+ return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
+ }
+
+ function clearTimeout(id) {
+ return delayedFunctionScheduler.removeFunctionWithId(id);
+ }
+
+ function setInterval(fn, interval) {
+ return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
+ }
+
+ function clearInterval(id) {
+ return delayedFunctionScheduler.removeFunctionWithId(id);
+ }
+
+ function argSlice(argsObj, n) {
+ return Array.prototype.slice.call(argsObj, 2);
+ }
+ }
+
+ return Clock;
+};
+
+getJasmineRequireObj().DelayedFunctionScheduler = function() {
+ function DelayedFunctionScheduler() {
+ var self = this;
+ var scheduledLookup = [];
+ var scheduledFunctions = {};
+ var currentTime = 0;
+ var delayedFnCount = 0;
+
+ self.tick = function(millis) {
+ millis = millis || 0;
+ var endTime = currentTime + millis;
+
+ runScheduledFunctions(endTime);
+ currentTime = endTime;
+ };
+
+ self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
+ var f;
+ if (typeof(funcToCall) === 'string') {
+ /* jshint evil: true */
+ f = function() { return eval(funcToCall); };
+ /* jshint evil: false */
+ } else {
+ f = funcToCall;
+ }
+
+ millis = millis || 0;
+ timeoutKey = timeoutKey || ++delayedFnCount;
+ runAtMillis = runAtMillis || (currentTime + millis);
+
+ var funcToSchedule = {
+ runAtMillis: runAtMillis,
+ funcToCall: f,
+ recurring: recurring,
+ params: params,
+ timeoutKey: timeoutKey,
+ millis: millis
+ };
+
+ if (runAtMillis in scheduledFunctions) {
+ scheduledFunctions[runAtMillis].push(funcToSchedule);
+ } else {
+ scheduledFunctions[runAtMillis] = [funcToSchedule];
+ scheduledLookup.push(runAtMillis);
+ scheduledLookup.sort(function (a, b) {
+ return a - b;
+ });
+ }
+
+ return timeoutKey;
+ };
+
+ self.removeFunctionWithId = function(timeoutKey) {
+ for (var runAtMillis in scheduledFunctions) {
+ var funcs = scheduledFunctions[runAtMillis];
+ var i = indexOfFirstToPass(funcs, function (func) {
+ return func.timeoutKey === timeoutKey;
+ });
+
+ if (i > -1) {
+ if (funcs.length === 1) {
+ delete scheduledFunctions[runAtMillis];
+ deleteFromLookup(runAtMillis);
+ } else {
+ funcs.splice(i, 1);
+ }
+
+ // intervals get rescheduled when executed, so there's never more
+ // than a single scheduled function with a given timeoutKey
+ break;
+ }
+ }
+ };
+
+ self.reset = function() {
+ currentTime = 0;
+ scheduledLookup = [];
+ scheduledFunctions = {};
+ delayedFnCount = 0;
+ };
+
+ return self;
+
+ function indexOfFirstToPass(array, testFn) {
+ var index = -1;
+
+ for (var i = 0; i < array.length; ++i) {
+ if (testFn(array[i])) {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ function deleteFromLookup(key) {
+ var value = Number(key);
+ var i = indexOfFirstToPass(scheduledLookup, function (millis) {
+ return millis === value;
+ });
+
+ if (i > -1) {
+ scheduledLookup.splice(i, 1);
+ }
+ }
+
+ function reschedule(scheduledFn) {
+ self.scheduleFunction(scheduledFn.funcToCall,
+ scheduledFn.millis,
+ scheduledFn.params,
+ true,
+ scheduledFn.timeoutKey,
+ scheduledFn.runAtMillis + scheduledFn.millis);
+ }
+
+ function runScheduledFunctions(endTime) {
+ if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
+ return;
+ }
+
+ do {
+ currentTime = scheduledLookup.shift();
+
+ var funcsToRun = scheduledFunctions[currentTime];
+ delete scheduledFunctions[currentTime];
+
+ for (var i = 0; i < funcsToRun.length; ++i) {
+ var funcToRun = funcsToRun[i];
+ funcToRun.funcToCall.apply(null, funcToRun.params || []);
+
+ if (funcToRun.recurring) {
+ reschedule(funcToRun);
+ }
+ }
+ } while (scheduledLookup.length > 0 &&
+ // checking first if we're out of time prevents setTimeout(0)
+ // scheduled in a funcToRun from forcing an extra iteration
+ currentTime !== endTime &&
+ scheduledLookup[0] <= endTime);
+ }
+ }
+
+ return DelayedFunctionScheduler;
+};
+
+getJasmineRequireObj().ExceptionFormatter = function() {
+ function ExceptionFormatter() {
+ this.message = function(error) {
+ var message = error.name +
+ ': ' +
+ error.message;
+
+ if (error.fileName || error.sourceURL) {
+ message += " in " + (error.fileName || error.sourceURL);
+ }
+
+ if (error.line || error.lineNumber) {
+ message += " (line " + (error.line || error.lineNumber) + ")";
+ }
+
+ return message;
+ };
+
+ this.stack = function(error) {
+ return error ? error.stack : null;
+ };
+ }
+
+ return ExceptionFormatter;
+};
+
+getJasmineRequireObj().Expectation = function() {
+
+ var matchers = {};
+
+ function Expectation(options) {
+ this.util = options.util || { buildFailureMessage: function() {} };
+ this.customEqualityTesters = options.customEqualityTesters || [];
+ this.actual = options.actual;
+ this.addExpectationResult = options.addExpectationResult || function(){};
+ this.isNot = options.isNot;
+
+ for (var matcherName in matchers) {
+ this[matcherName] = matchers[matcherName];
+ }
+ }
+
+ Expectation.prototype.wrapCompare = function(name, matcherFactory) {
+ return function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ expected = args.slice(0),
+ message = "";
+
+ args.unshift(this.actual);
+
+ var matcher = matcherFactory(this.util, this.customEqualityTesters),
+ matcherCompare = matcher.compare;
+
+ function defaultNegativeCompare() {
+ var result = matcher.compare.apply(null, args);
+ result.pass = !result.pass;
+ return result;
+ }
+
+ if (this.isNot) {
+ matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
+ }
+
+ var result = matcherCompare.apply(null, args);
+
+ if (!result.pass) {
+ if (!result.message) {
+ args.unshift(this.isNot);
+ args.unshift(name);
+ message = this.util.buildFailureMessage.apply(null, args);
+ } else {
+ message = result.message;
+ }
+ }
+
+ if (expected.length == 1) {
+ expected = expected[0];
+ }
+
+ // TODO: how many of these params are needed?
+ this.addExpectationResult(
+ result.pass,
+ {
+ matcherName: name,
+ passed: result.pass,
+ message: message,
+ actual: this.actual,
+ expected: expected // TODO: this may need to be arrayified/sliced
+ }
+ );
+ };
+ };
+
+ Expectation.addCoreMatchers = function(matchers) {
+ var prototype = Expectation.prototype;
+ for (var matcherName in matchers) {
+ var matcher = matchers[matcherName];
+ prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
+ }
+ };
+
+ Expectation.addMatchers = function(matchersToAdd) {
+ for (var name in matchersToAdd) {
+ var matcher = matchersToAdd[name];
+ matchers[name] = Expectation.prototype.wrapCompare(name, matcher);
+ }
+ };
+
+ Expectation.resetMatchers = function() {
+ for (var name in matchers) {
+ delete matchers[name];
+ }
+ };
+
+ Expectation.Factory = function(options) {
+ options = options || {};
+
+ var expect = new Expectation(options);
+
+ // TODO: this would be nice as its own Object - NegativeExpectation
+ // TODO: copy instead of mutate options
+ options.isNot = true;
+ expect.not = new Expectation(options);
+
+ return expect;
+ };
+
+ return Expectation;
+};
+
+//TODO: expectation result may make more sense as a presentation of an expectation.
+getJasmineRequireObj().buildExpectationResult = function() {
+ function buildExpectationResult(options) {
+ var messageFormatter = options.messageFormatter || function() {},
+ stackFormatter = options.stackFormatter || function() {};
+
+ return {
+ matcherName: options.matcherName,
+ expected: options.expected,
+ actual: options.actual,
+ message: message(),
+ stack: stack(),
+ passed: options.passed
+ };
+
+ function message() {
+ if (options.passed) {
+ return "Passed.";
+ } else if (options.message) {
+ return options.message;
+ } else if (options.error) {
+ return messageFormatter(options.error);
+ }
+ return "";
+ }
+
+ function stack() {
+ if (options.passed) {
+ return "";
+ }
+
+ var error = options.error;
+ if (!error) {
+ try {
+ throw new Error(message());
+ } catch (e) {
+ error = e;
+ }
+ }
+ return stackFormatter(error);
+ }
+ }
+
+ return buildExpectationResult;
+};
+
+getJasmineRequireObj().ObjectContaining = function(j$) {
+
+ function ObjectContaining(sample) {
+ this.sample = sample;
+ }
+
+ ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+ if (typeof(this.sample) !== "object") { throw new Error("You must provide an object to objectContaining, not '"+this.sample+"'."); }
+
+ mismatchKeys = mismatchKeys || [];
+ mismatchValues = mismatchValues || [];
+
+ var hasKey = function(obj, keyName) {
+ return obj !== null && !j$.util.isUndefined(obj[keyName]);
+ };
+
+ for (var property in this.sample) {
+ if (!hasKey(other, property) && hasKey(this.sample, property)) {
+ mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+ }
+ else if (!j$.matchersUtil.equals(this.sample[property], other[property])) {
+ mismatchValues.push("'" + property + "' was '" + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + "' in actual, but was '" + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in expected.");
+ }
+ }
+
+ return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+ };
+
+ ObjectContaining.prototype.jasmineToString = function() {
+ return "<jasmine.objectContaining(" + j$.pp(this.sample) + ")>";
+ };
+
+ return ObjectContaining;
+};
+
+getJasmineRequireObj().pp = function(j$) {
+
+ function PrettyPrinter() {
+ this.ppNestLevel_ = 0;
+ }
+
+ PrettyPrinter.prototype.format = function(value) {
+ this.ppNestLevel_++;
+ try {
+ if (j$.util.isUndefined(value)) {
+ this.emitScalar('undefined');
+ } else if (value === null) {
+ this.emitScalar('null');
+ } else if (value === j$.getGlobal()) {
+ this.emitScalar('<global>');
+ } else if (value.jasmineToString) {
+ this.emitScalar(value.jasmineToString());
+ } else if (typeof value === 'string') {
+ this.emitString(value);
+ } else if (j$.isSpy(value)) {
+ this.emitScalar("spy on " + value.and.identity());
+ } else if (value instanceof RegExp) {
+ this.emitScalar(value.toString());
+ } else if (typeof value === 'function') {
+ this.emitScalar('Function');
+ } else if (typeof value.nodeType === 'number') {
+ this.emitScalar('HTMLNode');
+ } else if (value instanceof Date) {
+ this.emitScalar('Date(' + value + ')');
+ } else if (value.__Jasmine_been_here_before__) {
+ this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>');
+ } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
+ value.__Jasmine_been_here_before__ = true;
+ if (j$.isArray_(value)) {
+ this.emitArray(value);
+ } else {
+ this.emitObject(value);
+ }
+ delete value.__Jasmine_been_here_before__;
+ } else {
+ this.emitScalar(value.toString());
+ }
+ } finally {
+ this.ppNestLevel_--;
+ }
+ };
+
+ PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+ for (var property in obj) {
+ if (!obj.hasOwnProperty(property)) { continue; }
+ if (property == '__Jasmine_been_here_before__') { continue; }
+ fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
+ obj.__lookupGetter__(property) !== null) : false);
+ }
+ };
+
+ PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
+ PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
+
+ function StringPrettyPrinter() {
+ PrettyPrinter.call(this);
+
+ this.string = '';
+ }
+
+ j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
+
+ StringPrettyPrinter.prototype.emitScalar = function(value) {
+ this.append(value);
+ };
+
+ StringPrettyPrinter.prototype.emitString = function(value) {
+ this.append("'" + value + "'");
+ };
+
+ StringPrettyPrinter.prototype.emitArray = function(array) {
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Array");
+ return;
+ }
+
+ this.append('[ ');
+ for (var i = 0; i < array.length; i++) {
+ if (i > 0) {
+ this.append(', ');
+ }
+ this.format(array[i]);
+ }
+ this.append(' ]');
+ };
+
+ StringPrettyPrinter.prototype.emitObject = function(obj) {
+ if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+ this.append("Object");
+ return;
+ }
+
+ var self = this;
+ this.append('{ ');
+ var first = true;
+
+ this.iterateObject(obj, function(property, isGetter) {
+ if (first) {
+ first = false;
+ } else {
+ self.append(', ');
+ }
+
+ self.append(property);
+ self.append(' : ');
+ if (isGetter) {
+ self.append('<getter>');
+ } else {
+ self.format(obj[property]);
+ }
+ });
+
+ this.append(' }');
+ };
+
+ StringPrettyPrinter.prototype.append = function(value) {
+ this.string += value;
+ };
+
+ return function(value) {
+ var stringPrettyPrinter = new StringPrettyPrinter();
+ stringPrettyPrinter.format(value);
+ return stringPrettyPrinter.string;
+ };
+};
+
+getJasmineRequireObj().QueueRunner = function() {
+
+ function QueueRunner(attrs) {
+ this.fns = attrs.fns || [];
+ this.onComplete = attrs.onComplete || function() {};
+ this.clearStack = attrs.clearStack || function(fn) {fn();};
+ this.onException = attrs.onException || function() {};
+ this.catchException = attrs.catchException || function() { return true; };
+ this.userContext = {};
+ }
+
+ QueueRunner.prototype.execute = function() {
+ this.run(this.fns, 0);
+ };
+
+ QueueRunner.prototype.run = function(fns, recursiveIndex) {
+ var length = fns.length,
+ self = this,
+ iterativeIndex;
+
+ for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
+ var fn = fns[iterativeIndex];
+ if (fn.length > 0) {
+ return attemptAsync(fn);
+ } else {
+ attemptSync(fn);
+ }
+ }
+
+ var runnerDone = iterativeIndex >= length;
+
+ if (runnerDone) {
+ this.clearStack(this.onComplete);
+ }
+
+ function attemptSync(fn) {
+ try {
+ fn.call(self.userContext);
+ } catch (e) {
+ handleException(e);
+ }
+ }
+
+ function attemptAsync(fn) {
+ var next = function () { self.run(fns, iterativeIndex + 1); };
+
+ try {
+ fn.call(self.userContext, next);
+ } catch (e) {
+ handleException(e);
+ next();
+ }
+ }
+
+ function handleException(e) {
+ self.onException(e);
+ if (!self.catchException(e)) {
+ //TODO: set a var when we catch an exception and
+ //use a finally block to close the loop in a nice way..
+ throw e;
+ }
+ }
+ };
+
+ return QueueRunner;
+};
+
+getJasmineRequireObj().ReportDispatcher = function() {
+ function ReportDispatcher(methods) {
+
+ var dispatchedMethods = methods || [];
+
+ for (var i = 0; i < dispatchedMethods.length; i++) {
+ var method = dispatchedMethods[i];
+ this[method] = (function(m) {
+ return function() {
+ dispatch(m, arguments);
+ };
+ }(method));
+ }
+
+ var reporters = [];
+
+ this.addReporter = function(reporter) {
+ reporters.push(reporter);
+ };
+
+ return this;
+
+ function dispatch(method, args) {
+ for (var i = 0; i < reporters.length; i++) {
+ var reporter = reporters[i];
+ if (reporter[method]) {
+ reporter[method].apply(reporter, args);
+ }
+ }
+ }
+ }
+
+ return ReportDispatcher;
+};
+
+
+getJasmineRequireObj().SpyStrategy = function() {
+
+ function SpyStrategy(options) {
+ options = options || {};
+
+ var identity = options.name || "unknown",
+ originalFn = options.fn || function() {},
+ getSpy = options.getSpy || function() {},
+ plan = function() {};
+
+ this.identity = function() {
+ return identity;
+ };
+
+ this.exec = function() {
+ return plan.apply(this, arguments);
+ };
+
+ this.callThrough = function() {
+ plan = originalFn;
+ return getSpy();
+ };
+
+ this.returnValue = function(value) {
+ plan = function() {
+ return value;
+ };
+ return getSpy();
+ };
+
+ this.throwError = function(something) {
+ var error = (something instanceof Error) ? something : new Error(something);
+ plan = function() {
+ throw error;
+ };
+ return getSpy();
+ };
+
+ this.callFake = function(fn) {
+ plan = fn;
+ return getSpy();
+ };
+
+ this.stub = function(fn) {
+ plan = function() {};
+ return getSpy();
+ };
+ }
+
+ return SpyStrategy;
+};
+
+getJasmineRequireObj().Suite = function() {
+ function Suite(attrs) {
+ this.env = attrs.env;
+ this.id = attrs.id;
+ this.parentSuite = attrs.parentSuite;
+ this.description = attrs.description;
+ this.onStart = attrs.onStart || function() {};
+ this.resultCallback = attrs.resultCallback || function() {};
+ this.clearStack = attrs.clearStack || function(fn) {fn();};
+
+ this.beforeFns = [];
+ this.afterFns = [];
+ this.queueRunner = attrs.queueRunner || function() {};
+ this.disabled = false;
+
+ this.children = [];
+
+ this.result = {
+ id: this.id,
+ status: this.disabled ? 'disabled' : '',
+ description: this.description,
+ fullName: this.getFullName()
+ };
+ }
+
+ Suite.prototype.getFullName = function() {
+ var fullName = this.description;
+ for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+ if (parentSuite.parentSuite) {
+ fullName = parentSuite.description + ' ' + fullName;
+ }
+ }
+ return fullName;
+ };
+
+ Suite.prototype.disable = function() {
+ this.disabled = true;
+ };
+
+ Suite.prototype.beforeEach = function(fn) {
+ this.beforeFns.unshift(fn);
+ };
+
+ Suite.prototype.afterEach = function(fn) {
+ this.afterFns.unshift(fn);
+ };
+
+ Suite.prototype.addChild = function(child) {
+ this.children.push(child);
+ };
+
+ Suite.prototype.execute = function(onComplete) {
+ var self = this;
+ if (this.disabled) {
+ complete();
+ return;
+ }
+
+ var allFns = [];
+
+ for (var i = 0; i < this.children.length; i++) {
+ allFns.push(wrapChildAsAsync(this.children[i]));
+ }
+
+ this.onStart(this);
+
+ this.queueRunner({
+ fns: allFns,
+ onComplete: complete
+ });
+
+ function complete() {
+ self.resultCallback(self.result);
+
+ if (onComplete) {
+ onComplete();
+ }
+ }
+
+ function wrapChildAsAsync(child) {
+ return function(done) { child.execute(done); };
+ }
+ };
+
+ return Suite;
+};
+
+if (typeof window == void 0 && typeof exports == "object") {
+ exports.Suite = jasmineRequire.Suite;
+}
+
+getJasmineRequireObj().Timer = function() {
+ function Timer(options) {
+ options = options || {};
+
+ var now = options.now || function() { return new Date().getTime(); },
+ startTime;
+
+ this.start = function() {
+ startTime = now();
+ };
+
+ this.elapsed = function() {
+ return now() - startTime;
+ };
+ }
+
+ return Timer;
+};
+
+getJasmineRequireObj().matchersUtil = function(j$) {
+ // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
+
+ return {
+ equals: function(a, b, customTesters) {
+ customTesters = customTesters || [];
+
+ return eq(a, b, [], [], customTesters);
+ },
+
+ contains: function(haystack, needle, customTesters) {
+ customTesters = customTesters || [];
+
+ if (Object.prototype.toString.apply(haystack) === "[object Array]") {
+ for (var i = 0; i < haystack.length; i++) {
+ if (eq(haystack[i], needle, [], [], customTesters)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return haystack.indexOf(needle) >= 0;
+ },
+
+ buildFailureMessage: function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ matcherName = args[0],
+ isNot = args[1],
+ actual = args[2],
+ expected = args.slice(3),
+ englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+
+ var message = "Expected " +
+ j$.pp(actual) +
+ (isNot ? " not " : " ") +
+ englishyPredicate;
+
+ if (expected.length > 0) {
+ for (var i = 0; i < expected.length; i++) {
+ if (i > 0) {
+ message += ",";
+ }
+ message += " " + j$.pp(expected[i]);
+ }
+ }
+
+ return message + ".";
+ }
+ };
+
+ // Equality function lovingly adapted from isEqual in
+ // [Underscore](http://underscorejs.org)
+ function eq(a, b, aStack, bStack, customTesters) {
+ var result = true;
+
+ for (var i = 0; i < customTesters.length; i++) {
+ var customTesterResult = customTesters[i](a, b);
+ if (!j$.util.isUndefined(customTesterResult)) {
+ return customTesterResult;
+ }
+ }
+
+ if (a instanceof j$.Any) {
+ result = a.jasmineMatches(b);
+ if (result) {
+ return true;
+ }
+ }
+
+ if (b instanceof j$.Any) {
+ result = b.jasmineMatches(a);
+ if (result) {
+ return true;
+ }
+ }
+
+ if (b instanceof j$.ObjectContaining) {
+ result = b.jasmineMatches(a);
+ if (result) {
+ return true;
+ }
+ }
+
+ if (a instanceof Error && b instanceof Error) {
+ return a.message == b.message;
+ }
+
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+ if (a === b) { return a !== 0 || 1 / a == 1 / b; }
+ // A strict comparison is necessary because `null == undefined`.
+ if (a === null || b === null) { return a === b; }
+ var className = Object.prototype.toString.call(a);
+ if (className != Object.prototype.toString.call(b)) { return false; }
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return a == String(b);
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') { return false; }
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] == a) { return bStack[length] == b; }
+ }
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+ var size = 0;
+ // Recursively compare objects and arrays.
+ if (className == '[object Array]') {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ size = a.length;
+ result = size == b.length;
+ if (result) {
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (size--) {
+ if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; }
+ }
+ }
+ } else {
+ // Objects with different constructors are not equivalent, but `Object`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
+ isFunction(bCtor) && (bCtor instanceof bCtor))) {
+ return false;
+ }
+ // Deep compare objects.
+ for (var key in a) {
+ if (has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (has(b, key) && !(size--)) { break; }
+ }
+ result = !size;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+
+ return result;
+
+ function has(obj, key) {
+ return obj.hasOwnProperty(key);
+ }
+
+ function isFunction(obj) {
+ return typeof obj === 'function';
+ }
+ }
+};
+
+getJasmineRequireObj().toBe = function() {
+ function toBe() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual === expected
+ };
+ }
+ };
+ }
+
+ return toBe;
+};
+
+getJasmineRequireObj().toBeCloseTo = function() {
+
+ function toBeCloseTo() {
+ return {
+ compare: function(actual, expected, precision) {
+ if (precision !== 0) {
+ precision = precision || 2;
+ }
+
+ return {
+ pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
+ };
+ }
+ };
+ }
+
+ return toBeCloseTo;
+};
+
+getJasmineRequireObj().toBeDefined = function() {
+ function toBeDefined() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: (void 0 !== actual)
+ };
+ }
+ };
+ }
+
+ return toBeDefined;
+};
+
+getJasmineRequireObj().toBeFalsy = function() {
+ function toBeFalsy() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: !!!actual
+ };
+ }
+ };
+ }
+
+ return toBeFalsy;
+};
+
+getJasmineRequireObj().toBeGreaterThan = function() {
+
+ function toBeGreaterThan() {
+ return {
+ compare: function(actual, expected) {
+ return {
+ pass: actual > expected
+ };
+ }
+ };
+ }
+
+ return toBeGreaterThan;
+};
+
+
+getJasmineRequireObj().toBeLessThan = function() {
+ function toBeLessThan() {
+ return {
+
+ compare: function(actual, expected) {
+ return {
+ pass: actual < expected
+ };
+ }
+ };
+ }
+
+ return toBeLessThan;
+};
+getJasmineRequireObj().toBeNaN = function(j$) {
+
+ function toBeNaN() {
+ return {
+ compare: function(actual) {
+ var result = {
+ pass: (actual !== actual)
+ };
+
+ if (result.pass) {
+ result.message = "Expected actual not to be NaN.";
+ } else {
+ result.message = "Expected " + j$.pp(actual) + " to be NaN.";
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toBeNaN;
+};
+
+getJasmineRequireObj().toBeNull = function() {
+
+ function toBeNull() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: actual === null
+ };
+ }
+ };
+ }
+
+ return toBeNull;
+};
+
+getJasmineRequireObj().toBeTruthy = function() {
+
+ function toBeTruthy() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: !!actual
+ };
+ }
+ };
+ }
+
+ return toBeTruthy;
+};
+
+getJasmineRequireObj().toBeUndefined = function() {
+
+ function toBeUndefined() {
+ return {
+ compare: function(actual) {
+ return {
+ pass: void 0 === actual
+ };
+ }
+ };
+ }
+
+ return toBeUndefined;
+};
+
+getJasmineRequireObj().toContain = function() {
+ function toContain(util, customEqualityTesters) {
+ customEqualityTesters = customEqualityTesters || [];
+
+ return {
+ compare: function(actual, expected) {
+
+ return {
+ pass: util.contains(actual, expected, customEqualityTesters)
+ };
+ }
+ };
+ }
+
+ return toContain;
+};
+
+getJasmineRequireObj().toEqual = function() {
+
+ function toEqual(util, customEqualityTesters) {
+ customEqualityTesters = customEqualityTesters || [];
+
+ return {
+ compare: function(actual, expected) {
+ var result = {
+ pass: false
+ };
+
+ result.pass = util.equals(actual, expected, customEqualityTesters);
+
+ return result;
+ }
+ };
+ }
+
+ return toEqual;
+};
+
+getJasmineRequireObj().toHaveBeenCalled = function(j$) {
+
+ function toHaveBeenCalled() {
+ return {
+ compare: function(actual) {
+ var result = {};
+
+ if (!j$.isSpy(actual)) {
+ throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
+ }
+
+ if (arguments.length > 1) {
+ throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+ }
+
+ result.pass = actual.calls.any();
+
+ result.message = result.pass ?
+ "Expected spy " + actual.and.identity() + " not to have been called." :
+ "Expected spy " + actual.and.identity() + " to have been called.";
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalled;
+};
+
+getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
+
+ function toHaveBeenCalledWith(util) {
+ return {
+ compare: function() {
+ var args = Array.prototype.slice.call(arguments, 0),
+ actual = args[0],
+ expectedArgs = args.slice(1),
+ result = { pass: false };
+
+ if (!j$.isSpy(actual)) {
+ throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
+ }
+
+ if (!actual.calls.any()) {
+ result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called.";
+ return result;
+ }
+
+ if (util.contains(actual.calls.allArgs(), expectedArgs)) {
+ result.pass = true;
+ result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was.";
+ } else {
+ result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + ".";
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toHaveBeenCalledWith;
+};
+
+getJasmineRequireObj().toMatch = function() {
+
+ function toMatch() {
+ return {
+ compare: function(actual, expected) {
+ var regexp = new RegExp(expected);
+
+ return {
+ pass: regexp.test(actual)
+ };
+ }
+ };
+ }
+
+ return toMatch;
+};
+
+getJasmineRequireObj().toThrow = function(j$) {
+
+ function toThrow(util) {
+ return {
+ compare: function(actual, expected) {
+ var result = { pass: false },
+ threw = false,
+ thrown;
+
+ if (typeof actual != "function") {
+ throw new Error("Actual is not a Function");
+ }
+
+ try {
+ actual();
+ } catch (e) {
+ threw = true;
+ thrown = e;
+ }
+
+ if (!threw) {
+ result.message = "Expected function to throw an exception.";
+ return result;
+ }
+
+ if (arguments.length == 1) {
+ result.pass = true;
+ result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + ".";
+
+ return result;
+ }
+
+ if (util.equals(thrown, expected)) {
+ result.pass = true;
+ result.message = "Expected function not to throw " + j$.pp(expected) + ".";
+ } else {
+ result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + ".";
+ }
+
+ return result;
+ }
+ };
+ }
+
+ return toThrow;
+};
+
+getJasmineRequireObj().toThrowError = function(j$) {
+ function toThrowError (util) {
+ return {
+ compare: function(actual) {
+ var threw = false,
+ thrown,
+ errorType,
+ message,
+ regexp,
+ name,
+ constructorName;
+
+ if (typeof actual != "function") {
+ throw new Error("Actual is not a Function");
+ }
+
+ extractExpectedParams.apply(null, arguments);
+
+ try {
+ actual();
+ } catch (e) {
+ threw = true;
+ thrown = e;
+ }
+
+ if (!threw) {
+ return fail("Expected function to throw an Error.");
+ }
+
+ if (!(thrown instanceof Error)) {
+ return fail("Expected function to throw an Error, but it threw " + thrown + ".");
+ }
+
+ if (arguments.length == 1) {
+ return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + ".");
+ }
+
+ if (errorType) {
+ name = fnNameFor(errorType);
+ constructorName = fnNameFor(thrown.constructor);
+ }
+
+ if (errorType && message) {
+ if (thrown.constructor == errorType && util.equals(thrown.message, message)) {
+ return pass("Expected function not to throw " + name + " with message \"" + message + "\".");
+ } else {
+ return fail("Expected function to throw " + name + " with message \"" + message +
+ "\", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
+ }
+ }
+
+ if (errorType && regexp) {
+ if (thrown.constructor == errorType && regexp.test(thrown.message)) {
+ return pass("Expected function not to throw " + name + " with message matching " + regexp + ".");
+ } else {
+ return fail("Expected function to throw " + name + " with message matching " + regexp +
+ ", but it threw " + constructorName + " with message \"" + thrown.message + "\".");
+ }
+ }
+
+ if (errorType) {
+ if (thrown.constructor == errorType) {
+ return pass("Expected function not to throw " + name + ".");
+ } else {
+ return fail("Expected function to throw " + name + ", but it threw " + constructorName + ".");
+ }
+ }
+
+ if (message) {
+ if (thrown.message == message) {
+ return pass("Expected function not to throw an exception with message " + j$.pp(message) + ".");
+ } else {
+ return fail("Expected function to throw an exception with message " + j$.pp(message) +
+ ", but it threw an exception with message " + j$.pp(thrown.message) + ".");
+ }
+ }
+
+ if (regexp) {
+ if (regexp.test(thrown.message)) {
+ return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + ".");
+ } else {
+ return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) +
+ ", but it threw an exception with message " + j$.pp(thrown.message) + ".");
+ }
+ }
+
+ function fnNameFor(func) {
+ return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
+ }
+
+ function pass(notMessage) {
+ return {
+ pass: true,
+ message: notMessage
+ };
+ }
+
+ function fail(message) {
+ return {
+ pass: false,
+ message: message
+ };
+ }
+
+ function extractExpectedParams() {
+ if (arguments.length == 1) {
+ return;
+ }
+
+ if (arguments.length == 2) {
+ var expected = arguments[1];
+
+ if (expected instanceof RegExp) {
+ regexp = expected;
+ } else if (typeof expected == "string") {
+ message = expected;
+ } else if (checkForAnErrorType(expected)) {
+ errorType = expected;
+ }
+
+ if (!(errorType || message || regexp)) {
+ throw new Error("Expected is not an Error, string, or RegExp.");
+ }
+ } else {
+ if (checkForAnErrorType(arguments[1])) {
+ errorType = arguments[1];
+ } else {
+ throw new Error("Expected error type is not an Error.");
+ }
+
+ if (arguments[2] instanceof RegExp) {
+ regexp = arguments[2];
+ } else if (typeof arguments[2] == "string") {
+ message = arguments[2];
+ } else {
+ throw new Error("Expected error message is not a string or RegExp.");
+ }
+ }
+ }
+
+ function checkForAnErrorType(type) {
+ if (typeof type !== "function") {
+ return false;
+ }
+
+ var Surrogate = function() {};
+ Surrogate.prototype = type.prototype;
+ return (new Surrogate()) instanceof Error;
+ }
+ }
+ };
+ }
+
+ return toThrowError;
+};
+
+getJasmineRequireObj().version = function() {
+ return "2.0.0";
+};
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine_favicon.png b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine_favicon.png
new file mode 100644
index 0000000000..3562e278f1
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine_favicon.png
Binary files differ
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/spec/PlayerSpec.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/spec/PlayerSpec.js
new file mode 100644
index 0000000000..976c0de51a
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/spec/PlayerSpec.js
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+describe("Player", function() {
+ var player;
+ var song;
+
+ beforeEach(function() {
+ player = new Player();
+ song = new Song();
+ });
+
+ it("should be able to play a Song", function() {
+ player.play(song);
+ expect(player.currentlyPlayingSong).toEqual(song);
+
+ //demonstrates use of custom matcher
+ expect(player).toBePlaying(song);
+ });
+
+ describe("when song has been paused", function() {
+ beforeEach(function() {
+ player.play(song);
+ player.pause();
+ });
+
+ it("should indicate that the song is currently paused", function() {
+ expect(player.isPlaying).toBeFalsy();
+
+ // demonstrates use of 'not' with a custom matcher
+ expect(player).not.toBePlaying(song);
+ });
+
+ it("should be possible to resume", function() {
+ player.resume();
+ expect(player.isPlaying).toBeTruthy();
+ expect(player.currentlyPlayingSong).toEqual(song);
+ });
+ });
+
+ // demonstrates use of spies to intercept and test method calls
+ it("tells the current song if the user has made it a favorite", function() {
+ spyOn(song, 'persistFavoriteStatus');
+
+ player.play(song);
+ player.makeFavorite();
+
+ expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
+ });
+
+ //demonstrates use of expected exceptions
+ describe("#resume", function() {
+ it("should throw an exception if song is already playing", function() {
+ player.play(song);
+
+ expect(function() {
+ player.resume();
+ }).toThrowError("song is already playing");
+ });
+ });
+});
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/spec/SpecHelper.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/spec/SpecHelper.js
new file mode 100644
index 0000000000..b6b66da78a
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/spec/SpecHelper.js
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+beforeEach(function () {
+ jasmine.addMatchers({
+ toBePlaying: function () {
+ return {
+ compare: function (actual, expected) {
+ var player = actual;
+
+ return {
+ pass: player.currentlyPlayingSong === expected && player.isPlaying
+ }
+ }
+ };
+ }
+ });
+});
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/src/Player.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/src/Player.js
new file mode 100644
index 0000000000..20be99a3ba
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/src/Player.js
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+function Player() {
+}
+Player.prototype.play = function(song) {
+ this.currentlyPlayingSong = song;
+ this.isPlaying = true;
+};
+
+Player.prototype.pause = function() {
+ this.isPlaying = false;
+};
+
+Player.prototype.resume = function() {
+ if (this.isPlaying) {
+ throw new Error("song is already playing");
+ }
+
+ this.isPlaying = true;
+};
+
+Player.prototype.makeFavorite = function() {
+ this.currentlyPlayingSong.persistFavoriteStatus(true);
+};
diff --git a/catalog-fe/src/test/jasmine-standalone-2.0.0/src/Song.js b/catalog-fe/src/test/jasmine-standalone-2.0.0/src/Song.js
new file mode 100644
index 0000000000..c16d77e378
--- /dev/null
+++ b/catalog-fe/src/test/jasmine-standalone-2.0.0/src/Song.js
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+function Song() {
+}
+
+Song.prototype.persistFavoriteStatus = function(value) {
+ // something complicated
+ throw new Error("not yet implemented");
+};
diff --git a/catalog-fe/src/test/java/org/openecomp/sdc/ApplicationConfig.java b/catalog-fe/src/test/java/org/openecomp/sdc/ApplicationConfig.java
new file mode 100644
index 0000000000..6297156eb6
--- /dev/null
+++ b/catalog-fe/src/test/java/org/openecomp/sdc/ApplicationConfig.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
+
+public class ApplicationConfig extends Application {
+
+ public Set<Class<?>> getClasses() {
+ final Set<Class<?>> resources = new HashSet<Class<?>>();
+
+ // Add additional features such as support for Multipart.
+ resources.add(MultiPartFeature.class);
+
+ return resources;
+ }
+
+}
diff --git a/catalog-fe/src/test/java/org/openecomp/sdc/ContentDisposiotionDelegator.java b/catalog-fe/src/test/java/org/openecomp/sdc/ContentDisposiotionDelegator.java
new file mode 100644
index 0000000000..9b873879e5
--- /dev/null
+++ b/catalog-fe/src/test/java/org/openecomp/sdc/ContentDisposiotionDelegator.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.util.Date;
+
+import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
+
+public class ContentDisposiotionDelegator extends FormDataContentDisposition {
+
+ public ContentDisposiotionDelegator(String type, String name, String fileName, Date creationDate,
+ Date modificationDate, Date readDate, long size) {
+ super(type, name, fileName, creationDate, modificationDate, readDate, size);
+ // TODO Auto-generated constructor stub
+ }
+
+}
diff --git a/catalog-fe/src/test/java/org/openecomp/sdc/Main.java b/catalog-fe/src/test/java/org/openecomp/sdc/Main.java
new file mode 100644
index 0000000000..0737b414fd
--- /dev/null
+++ b/catalog-fe/src/test/java/org/openecomp/sdc/Main.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.util.component.Container.Listener;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.openecomp.sdc.fe.listen.FEAppContextListener;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // The port that we should run on can be set into an environment
+ // variable
+ // Look for that variable and default to 8080 if it isn't there.
+ String webPort = System.getenv("PORT");
+ if (webPort == null || webPort.isEmpty()) {
+ webPort = "8080";
+ }
+ // String webPort = "9998";
+
+ final Server server = new Server(Integer.valueOf(webPort));
+ server.addEventListener((Listener) new FEAppContextListener());
+ final WebAppContext root = new WebAppContext();
+
+ root.setContextPath("/sdc1");
+ // Parent loader priority is a class loader setting that Jetty accepts.
+ // By default Jetty will behave like most web containers in that it will
+ // allow your application to replace non-server libraries that are part
+ // of the
+ // container. Setting parent loader priority to true changes this
+ // behavior.
+ // Read more here:
+ // http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
+ root.setParentLoaderPriority(true);
+
+ final String webappDirLocation = "src/main/webapp/";
+ root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
+ root.setResourceBase(webappDirLocation);
+
+ server.setHandler(root);
+
+ server.start();
+ server.join();
+ }
+}
diff --git a/catalog-fe/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java b/catalog-fe/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java
new file mode 100644
index 0000000000..ecbc055a81
--- /dev/null
+++ b/catalog-fe/src/test/java/org/openecomp/sdc/TestExternalConfiguration.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc;
+
+import java.io.IOException;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.FileChangeCallback;
+import org.openecomp.sdc.common.impl.ConfigFileChangeListener;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.fe.config.Configuration;
+
+public class TestExternalConfiguration<T extends Object> {
+
+ public static void main(String[] args) throws IOException {
+
+ ExternalConfiguration.setAppName("catalog-server");
+ ExternalConfiguration
+ .setConfigDir("C:\\Users\\esofer\\workspaceLuna\\catalog-server\\src\\test\\resources\\config");
+ ExternalConfiguration.listenForChanges();
+
+ ConfigurationListener configurationListener = new ConfigurationListener(Configuration.class,
+ new FileChangeCallback() {
+
+ @Override
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+
+ ConfigurationSource configurationSource1 = new FSConfigurationSource(new ConfigFileChangeListener(),
+ ExternalConfiguration.getConfigDir());
+ configurationSource1.getAndWatchConfiguration(Configuration.class, configurationListener);
+
+ try {
+ Thread.currentThread().sleep(100 * 1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/catalog-fe/src/test/java/org/openecomp/sdc/servlets/FeProxyServletTest.java b/catalog-fe/src/test/java/org/openecomp/sdc/servlets/FeProxyServletTest.java
new file mode 100644
index 0000000000..4c4eb1ca2a
--- /dev/null
+++ b/catalog-fe/src/test/java/org/openecomp/sdc/servlets/FeProxyServletTest.java
@@ -0,0 +1,140 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.servlets;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.http.HttpFields;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.openecomp.sdc.fe.servlets.FeProxyServlet;
+
+public class FeProxyServletTest {
+ /*
+ * Example Url Mappings:
+ * http://localhost:8080/sdc1/feProxy/rest/services/MichaelTest2/0.0.1/csar
+ * --> http://localhost:8090/sdc2/rest/services/MichaelTest2/0.0.1/csar
+ * http://localhost:8080/sdc1/feProxy/dummy/not/working -->
+ * http://localhost:8090/sdc2/dummy/not/working
+ */
+ FeProxyServlet feProxy = new FeProxyServlet();
+ final static HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class);
+ final static HttpSession httpSession = Mockito.mock(HttpSession.class);
+ final static ServletContext servletContext = Mockito.mock(ServletContext.class);
+ final static ConfigurationManager configurationManager = Mockito.mock(ConfigurationManager.class);
+ final static Configuration configuration = Mockito.mock(Configuration.class);
+ final static Request proxyRequest = Mockito.spy(Request.class);
+ final static HttpFields httpFields = Mockito.mock(HttpFields.class);
+
+ final static String BE_PROTOCOL = "http";
+ final static String BE_HOST = "172.20.43.124";
+ final static int BE_PORT = 8090;
+ final static String HEADER_1 = "Header1";
+ final static String HEADER_2 = "Header2";
+ final static String HEADER_3 = "Header3";
+ final static String HEADER_1_VAL = "Header1_Val";
+ final static String HEADER_2_VAL = "Header2_Val";
+ final static String HEADER_3_VAL = "Header3_Val";
+ final static String REQUEST_ID_VAL = "4867495a-5ed7-49e4-8be2-cc8d66fdd52b";
+
+ @BeforeClass
+ public static void beforeClass() {
+ when(servletRequest.getSession()).thenReturn(httpSession);
+ when(httpSession.getServletContext()).thenReturn(servletContext);
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ when(configurationManager.getConfiguration()).thenReturn(configuration);
+ when(configuration.getBeProtocol()).thenReturn(BE_PROTOCOL);
+ when(configuration.getBeHost()).thenReturn(BE_HOST);
+ when(configuration.getBeHttpPort()).thenReturn(BE_PORT);
+
+ List<String> strList = new ArrayList<String>();
+ strList.add(HEADER_1);
+ strList.add(HEADER_2);
+ strList.add(HEADER_3);
+
+ when(servletRequest.getHeaderNames()).thenReturn(Collections.enumeration(strList));
+ when(servletRequest.getHeader(HEADER_1)).thenReturn(HEADER_1_VAL);
+ when(servletRequest.getHeader(HEADER_2)).thenReturn(HEADER_2_VAL);
+ when(servletRequest.getHeader(HEADER_3)).thenReturn(HEADER_3_VAL);
+ when(servletRequest.getHeader(Constants.X_ECOMP_REQUEST_ID_HEADER)).thenReturn(REQUEST_ID_VAL);
+
+ when(proxyRequest.getHeaders()).thenReturn(httpFields);
+ when(httpFields.containsKey(HEADER_1)).thenReturn(true);
+ when(httpFields.containsKey(HEADER_2)).thenReturn(true);
+ when(httpFields.containsKey(HEADER_3)).thenReturn(false);
+
+ }
+
+ @Test
+ public void testRewriteURI_APIRequest() {
+ when(servletRequest.getRequestURI()).thenReturn("/sdc1/feProxy/rest/dummyBeAPI");
+ String requestResourceUrl = "http://localhost:8080/sdc1/feProxy/rest/dummyBeAPI";
+ String expectedChangedUrl = BE_PROTOCOL + "://" + BE_HOST + ":" + BE_PORT + "/sdc2/rest/dummyBeAPI";
+ when(servletRequest.getRequestURL()).thenReturn(new StringBuffer(requestResourceUrl));
+
+ when(servletRequest.getContextPath()).thenReturn("/sdc1");
+ when(servletRequest.getServletPath()).thenReturn("/feProxy/rest/dummyBeAPI");
+
+ URI rewriteURI = feProxy.rewriteURI(servletRequest);
+
+ assertTrue(rewriteURI.toString().equals(expectedChangedUrl));
+ }
+
+ @Test
+ public void testRewriteURIWithQureyParam_APIRequest() {
+ when(servletRequest.getRequestURI()).thenReturn("/sdc1/feProxy/rest/gg%20g?subtype=VF");
+ String requestResourceUrl = "http://localhost:8080/sdc1/feProxy/rest/gg%20g?subtype=VF";
+ String expectedChangedUrl = BE_PROTOCOL + "://" + BE_HOST + ":" + BE_PORT + "/sdc2/rest/gg%20g?subtype=VF";
+ when(servletRequest.getRequestURL()).thenReturn(new StringBuffer(requestResourceUrl));
+
+ when(servletRequest.getContextPath()).thenReturn("/sdc1");
+ when(servletRequest.getServletPath()).thenReturn("/feProxy/rest/gg%20g?subtype=VF");
+
+ URI rewriteURI = feProxy.rewriteURI(servletRequest);
+
+ assertTrue(rewriteURI.toString().equals(expectedChangedUrl));
+ }
+
+ @Test
+ public void testCustomizeProxyRequest() {
+ feProxy.customizeProxyRequest(proxyRequest, servletRequest);
+ verify(proxyRequest).header(HEADER_3, HEADER_3_VAL);
+ verify(proxyRequest, times(1)).header(Mockito.anyString(), Mockito.anyString());
+
+ }
+}
diff --git a/catalog-fe/src/test/java/org/openecomp/sdc/servlets/PortalServletTest.java b/catalog-fe/src/test/java/org/openecomp/sdc/servlets/PortalServletTest.java
new file mode 100644
index 0000000000..f2ff870a91
--- /dev/null
+++ b/catalog-fe/src/test/java/org/openecomp/sdc/servlets/PortalServletTest.java
@@ -0,0 +1,159 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.servlets;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.openecomp.sdc.fe.servlets.PortalServlet;
+
+public class PortalServletTest extends JerseyTest {
+
+ final static HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ final static HttpSession httpSession = Mockito.mock(HttpSession.class);
+ final static ServletContext servletContext = Mockito.mock(ServletContext.class);
+ final static ConfigurationManager configurationManager = Mockito.mock(ConfigurationManager.class);
+ final static Configuration configuration = Mockito.mock(Configuration.class);
+ final static HttpServletResponse response = Mockito.spy(HttpServletResponse.class);
+ final static RequestDispatcher rd = Mockito.spy(RequestDispatcher.class);
+
+ @SuppressWarnings("serial")
+ @BeforeClass
+ public static void setUpTests() {
+ when(request.getRequestDispatcher(Mockito.anyString())).thenReturn(rd);
+ when(request.getSession()).thenReturn(httpSession);
+ when(httpSession.getServletContext()).thenReturn(servletContext);
+ when(servletContext.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).thenReturn(configurationManager);
+ when(configurationManager.getConfiguration()).thenReturn(configuration);
+ List<List<String>> mandatoryHeaders = new ArrayList<List<String>>();
+ mandatoryHeaders.add(new ArrayList<String>() {
+ {
+ add("HTTP_IV_USER");
+ add("iv-user");
+ }
+ });
+ mandatoryHeaders.add(new ArrayList<String>() {
+ {
+ add("HTTP_CSP_ATTUID");
+ add("csp-attuid");
+ }
+ });
+ mandatoryHeaders.add(new ArrayList<String>() {
+ {
+ add("USER_ID");
+ add("csp-userId");
+ }
+ });
+ mandatoryHeaders.add(new ArrayList<String>() {
+ {
+ add("HTTP_CSP_WSTYPE");
+ add("csp-wstype csp-wstype");
+ }
+ });
+
+ List<List<String>> optionalHeaders = new ArrayList<List<String>>();
+ optionalHeaders.add(new ArrayList<String>() {
+ {
+ add("HTTP_CSP_FIRSTNAME");
+ add("csp-firstname");
+ }
+ });
+ optionalHeaders.add(new ArrayList<String>() {
+ {
+ add("HTTP_CSP_LASTNAME");
+ add("csp-lastname");
+ }
+ });
+ optionalHeaders.add(new ArrayList<String>() {
+ {
+ add("HTTP_IV_REMOTE_ADDRESS");
+ add("iv-remote-address");
+ }
+ });
+
+ when(configuration.getIdentificationHeaderFields()).thenReturn(mandatoryHeaders);
+ when(configuration.getOptionalHeaderFields()).thenReturn(optionalHeaders);
+
+ }
+
+ @Test
+ public void testMissingHeadersRequest() throws IOException {
+ when(request.getHeader(Mockito.anyString())).thenReturn(null);
+ target().path("/portal").request().get();
+ Mockito.verify(response, times(1)).sendError(HttpServletResponse.SC_USE_PROXY, PortalServlet.MISSING_HEADERS_MSG);
+ Mockito.reset(response, rd);
+ }
+
+ @Test
+ public void testSuccesfulRequest() throws IOException, ServletException {
+ Mockito.doAnswer(new Answer<Object>() {
+ public Object answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ String headerName = (String) args[0];
+ return headerName;
+ }
+ }).when(request).getHeader(Mockito.anyString());
+ target().path("/portal").request().get();
+ verify(rd).forward(Mockito.any(ServletRequest.class), Mockito.any(ServletResponse.class));
+ Mockito.reset(response, rd);
+ }
+
+ @Override
+ protected Application configure() {
+ ResourceConfig resourceConfig = new ResourceConfig(PortalServlet.class);
+
+ resourceConfig.register(new AbstractBinder() {
+ @Override
+ protected void configure() {
+ bind(request).to(HttpServletRequest.class);
+ bind(response).to(HttpServletResponse.class);
+ }
+ });
+
+ return resourceConfig;
+ }
+}
diff --git a/catalog-fe/src/test/resources/CI/ReadMe.txt b/catalog-fe/src/test/resources/CI/ReadMe.txt
new file mode 100644
index 0000000000..c12020240d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/ReadMe.txt
@@ -0,0 +1,5 @@
+In Order to Run the CI:
+
+1. Configure ip and port for catalog-fe in env.sh file located in CI\tests\env
+2. run script runTests.sh with the env file as parameter: ./runTests.sh env/env.sh
+ \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/originalResources/apache-type.yml b/catalog-fe/src/test/resources/CI/originalResources/apache-type.yml
new file mode 100644
index 0000000000..360b1a2d60
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/apache-type.yml
@@ -0,0 +1,50 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+description: TOSCA simple profile with Apache.
+template_name: apache-type
+template_version: 2.0.0-SNAPSHOT
+template_author: FastConnect
+
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-webServer:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ alien.nodes.Apache:
+ derived_from: tosca.nodes.WebServer
+ description: >
+ The TOSCA Apache Node Type represents an apache component
+ that can be managed and run by a TOSCA Compute Node Type.
+ capabilities:
+ host:
+ type: alien.capabilities.ApacheContainer
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
+ tags:
+ icon: /images/apache.png
+ properties:
+ version:
+ type: version
+ default: 2.4
+ constraints:
+ - equal: 2.4
+ port:
+ type: integer
+ description: Port for the Apache server
+ default: 80
+ constraints:
+ - greater_or_equal: 1
+ document_root:
+ type: string
+ default: "/var/www"
+ interfaces:
+ Standard:
+ create:
+ inputs:
+ PORT: { get_property: [SELF, port] }
+ DOC_ROOT: { get_property: [SELF, document_root] }
+ implementation: scripts/install_apache.sh
+ start: scripts/start_apache.sh
+
+capability_types:
+ alien.capabilities.ApacheContainer:
+ derived_from: tosca.capabilities.Container
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/apache.png b/catalog-fe/src/test/resources/CI/originalResources/images/apache.png
new file mode 100644
index 0000000000..8e9f402d90
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/apache.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/compute.png b/catalog-fe/src/test/resources/CI/originalResources/images/compute.png
new file mode 100644
index 0000000000..7d5297eed3
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/compute.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/loadbalancer.png b/catalog-fe/src/test/resources/CI/originalResources/images/loadbalancer.png
new file mode 100644
index 0000000000..62f0fa8aae
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/loadbalancer.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/network.png b/catalog-fe/src/test/resources/CI/originalResources/images/network.png
new file mode 100644
index 0000000000..c8bf18f31a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/network.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/objectstore.png b/catalog-fe/src/test/resources/CI/originalResources/images/objectstore.png
new file mode 100644
index 0000000000..2b2063c4f7
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/objectstore.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/relational_db.png b/catalog-fe/src/test/resources/CI/originalResources/images/relational_db.png
new file mode 100644
index 0000000000..a7a632effd
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/relational_db.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/root.png b/catalog-fe/src/test/resources/CI/originalResources/images/root.png
new file mode 100644
index 0000000000..170f1c3c27
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/root.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/router.png b/catalog-fe/src/test/resources/CI/originalResources/images/router.png
new file mode 100644
index 0000000000..379cdceec0
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/router.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/software.png b/catalog-fe/src/test/resources/CI/originalResources/images/software.png
new file mode 100644
index 0000000000..dc9c53245d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/software.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/images/volume.png b/catalog-fe/src/test/resources/CI/originalResources/images/volume.png
new file mode 100644
index 0000000000..16fa17bd70
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/images/volume.png
Binary files differ
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-DBMS.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-DBMS.yml
new file mode 100644
index 0000000000..4a924672fc
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-DBMS.yml
@@ -0,0 +1,36 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-DBMS
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-softwareComponent:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.DBMS:
+ abstract: true
+ derived_from: tosca.nodes.SoftwareComponent
+ description: >
+ The TOSCA DBMS node represents a typical relational, SQL Database Management System software component or service.
+ tags:
+ icon: /images/relational_db.png
+ properties:
+ dbms_root_password:
+ type: string
+ required: false
+ description: the root password for the DBMS service.
+ dbms_port:
+ type: integer
+ required: false
+ description: the port the DBMS service will listen to for data and requests
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ properties:
+ valid_node_types: [tosca.nodes.Database]
+ endpoint:
+ type: tosca.capabilities.DatabaseEndpoint
+
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-blockStorage.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-blockStorage.yml
new file mode 100644
index 0000000000..df942b9f4f
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-blockStorage.yml
@@ -0,0 +1,40 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-blockStorage
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.BlockStorage:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA BlockStorage node currently represents a server-local block storage device (i.e., not shared)
+ offering evenly sized blocks of data from which raw storage volumes can be created.
+ tags:
+ icon: /images/volume.png
+ properties:
+ size:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: The requested storage size in MegaBytes (MB).
+ volume_id:
+ type: string
+ required: false
+ description: ID of an existing volume (that is in the accessible scope of the requesting application).
+ snapshot_id:
+ type: string
+ required: false
+ description: Some identifier that represents an existing snapshot that should be used when creating the block storage (volume).
+ attributes:
+ volume_id:
+ type: string
+ description: ID provided by the orchestrator for newly created volumes.
+ requirements:
+ attachment:
+ type: tosca.capabilities.Attachment
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-compute.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-compute.yml
new file mode 100644
index 0000000000..3a972a81e0
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-compute.yml
@@ -0,0 +1,77 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-compute
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ # Infrastructure components
+ tosca.nodes.Compute:
+ derived_from: tosca.nodes.Root
+ description: >
+ Represents a real or virtual machine or ‘server’. Informations specified on the Compute
+ node will be used to find the machine that fits the given requirements in the cloud
+ available machines. If no sizing informations are specified the cloud’s provider default
+ machine will be used. It is strongly recommended to specify the required cpus and memory
+ at least.
+ tags:
+ icon: /images/compute.png
+ properties:
+ num_cpus:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: Number of (actual or virtual) CPUs associated with the Compute node.
+ mem_size:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: Size of memory, in Megabytes (MB), available to applications running on the Compute node.
+ disk_size:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.
+ os_arch:
+ type: string
+ constraints:
+ - valid_values: ["x86_32", "x86_64"]
+ description: The host Operating System (OS) architecture.
+ os_type:
+ type: string
+ constraints:
+ - valid_values: ["linux", "aix", "mac os", "windows"]
+ description: The host Operating System (OS) type.
+ os_distribution:
+ type: string
+ required: false
+ description: The host Operating System (OS) distribution.
+ os_version:
+ type: string
+ required: false
+ description: The host Operating System version.
+ attributes:
+ ip_address:
+ type: string
+ description: >
+ The primary IP address assigned by the cloud provider that applications may use to access the Compute node.
+ Note: This is used by the platform provider to convey the primary address used to access the compute node. Future working drafts will address implementations that support floating or multiple IP addresses.
+ requirements:
+ network:
+ type: tosca.capabilities.Connectivity
+ lower_bound: 0
+ upper_bound: unbounded
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ properties:
+ valid_node_types: [tosca.nodes.SoftwareComponent]
+ attach: tosca.capabilities.Attachment
+ scalable: tosca.capabilities.Scalable
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-database.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-database.yml
new file mode 100644
index 0000000000..38bf5d11db
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-database.yml
@@ -0,0 +1,41 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-database
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-DBMS:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.Database:
+ derived_from: tosca.nodes.Root
+ description: >
+ Base type for the schema and content associated with a DBMS.
+ The TOSCA Database node represents a logical database that can be managed and hosted by a TOSCA DBMS node.
+ tags:
+ icon: /images/relational_db.png
+ properties:
+ db_user:
+ type: string
+ required: false
+ description: The special user account used for database administration.
+ db_password:
+ type: string
+ required: false
+ description: The password associated with the user account provided in the ‘db_user’ property.
+ db_port:
+ type: integer
+ required: false
+ description: The port on which the underlying database service will listen to data.
+ db_name:
+ type: string
+ required: false
+ description: The logical name of the database.
+ requirements:
+ - host: tosca.nodes.DBMS
+ relationship_type: tosca.relationships.HostedOn
+ capabilities:
+ database_endpoint: tosca.capabilities.DatabaseEndpoint
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-network.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-network.yml
new file mode 100644
index 0000000000..bb860f82be
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-network.yml
@@ -0,0 +1,39 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-network
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.Network:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA Network node represents a simple, logical network service.
+ properties:
+ ip_version:
+ type: integer
+ required: false
+ default: 4
+ constraints:
+ - valid_values: [ 4, 6 ]
+ cidr:
+ type: string
+ required: false
+ gateway_ip:
+ type: string
+ required: false
+ network_name:
+ type: string
+ required: false
+ network_id:
+ type: string
+ required: false
+ capabilities:
+ connection:
+ type: tosca.capabilities.Connectivity
+ tags:
+ icon: /images/network.png
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-objectStorage.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-objectStorage.yml
new file mode 100644
index 0000000000..a56fad5363
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-objectStorage.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-objectStorage
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.ObjectStorage:
+ abstract: true
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA ObjectStorage node represents storage that provides the ability to store data as objects (or BLOBs of data)
+ without consideration for the underlying filesystem or devices.
+ tags:
+ icon: /images/objectstore.png
+ properties:
+ store_name:
+ type: string
+ description: The logical name of the object store (or container).
+ store_size:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 0
+ description: The requested initial storage size in Gigabytes.
+ store_maxsize:
+ type: integer
+ required: false
+ constraints:
+ - greater_than: 0
+ description: The requested maximum storage size in Gigabytes.
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-root.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-root.yml
new file mode 100644
index 0000000000..7f4c16c260
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-root.yml
@@ -0,0 +1,168 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-root
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+
+node_types:
+ tosca.nodes.Root:
+ abstract: true
+ description: >
+ This is the default (root) TOSCA Node Type that all other TOSCA nodes should extends.
+ This allows all TOSCA nodes to have a consistent set of features for modeling and management
+ (e.g, consistent definitions for requirements, capabilities, and lifecycle interfaces).
+ tags:
+ icon: /images/root.png
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ requirements:
+ dependency:
+ type: tosca.capabilities.Root
+ lower_bound: 0
+ upper_bound: unbounded
+ capabilities:
+ root:
+ type: tosca.capabilities.Root
+ interfaces:
+ tosca.interfaces.node.lifecycle.Standard:
+ description: >
+ This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.
+ create:
+ description: Standard lifecycle create operation.
+ configure:
+ description: Standard lifecycle configure operation (pre-start).
+ start:
+ description: Standard lifecycle start operation.
+ post_start:
+ description: Standard lifecycle post-configure operation (post-start)
+ stop:
+ description: Standard lifecycle stop operation.
+ delete:
+ description: Standard lifecycle delete operation.
+
+
+capability_types:
+ tosca.capabilities.Root:
+ description: This is the default (root) TOSCA Capability Type definition that all other TOSCA Capability Types derive from.
+ tosca.capabilities.Container:
+ derived_from: tosca.capabilities.Root
+ properties:
+ valid_node_types:
+ type: string
+ required: true
+ description: >
+ A list of one or more names of Node Types that are supported as containees that declare the Container type as a Capability.
+ tosca.capabilities.Endpoint:
+ derived_from: tosca.capabilities.Root
+ properties:
+ protocol:
+ type: string
+ default: tcp
+ port:
+ type: integer
+ required: false
+ constraints:
+ - greater_or_equal: 1
+ - less_or_equal: 65535
+ secure:
+ type: boolean
+ default: false
+ url_path:
+ type: string
+ required: false
+ tosca.capabilities.DatabaseEndpoint:
+ derived_from: tosca.capabilities.Endpoint
+ description: This is the default TOSCA type that should be used or extended to define a specialized database endpoint capability.
+ tosca.capabilities.Attachment:
+ derived_from: tosca.capabilities.Root
+ description: This is the default TOSCA type that should be used or extended to define a network endpoint capability.
+ tosca.capabilities.Scalable:
+ derived_from: tosca.capabilities.Root
+ properties:
+ min_intances:
+ type: integer
+ default: 1
+ max_intances:
+ type: integer
+ default: 1
+ default_instances:
+ type: integer
+ default: 1
+ tosca.capabilities.Connectivity:
+ derived_from: tosca.capabilities.Root
+
+relationship_types:
+ tosca.relationships.Root:
+ abstract: true
+ description: This is the default (root) TOSCA Relationship Type definition that all other TOSCA Relationship Types derive from.
+ valid_targets: [ tosca.capabilities.Root ]
+ attributes:
+ tosca_id:
+ type: string
+ tosca_name:
+ type: string
+ interfaces:
+ tosca.interfaces.relationship.Configure:
+ description: >
+ The lifecycle interfaces define the essential, normative operations that each TOSCA Relationship Types may support.
+ pre_configure_source:
+ description: Operation to pre-configure the source endpoint.
+ pre_configure_target:
+ description: Operation to pre-configure the target endpoint.
+ post_configure_source:
+ description: Operation to post-configure the source endpoint.
+ post_configure_target:
+ description: Operation to post-configure the target endpoint.
+ add_target:
+ description: Operation to notify the source node of a target node being added via a relationship.
+ add_source:
+ description: Operation to notify the target node of a source node which is now available via a relationship.
+ remove_target:
+ description: Operation to notify the source node of a target node being removed from a relationship.
+ remove_source:
+ description: Operation to notify the target node of a source node being removed from a relationship.
+ target_changed:
+ description: Operation to notify source some property or attribute of the target.
+ source_changed:
+ description: Operation to notify target some property or attribute of the source.
+ tosca.relationships.DependsOn:
+ derived_from: tosca.relationships.Root
+ description: >
+ A generic depends on relationship.
+ tosca.relationships.HostedOn:
+ derived_from: tosca.relationships.DependsOn
+ description: Relationship to use to describe that the source is hosted (installed/ deployed) on the target node.
+ valid_targets: [ tosca.capabilities.Container ]
+ tosca.relationships.ConnectsTo:
+ derived_from: tosca.relationships.DependsOn
+ valid_targets: [ tosca.capabilities.Endpoint ]
+ tosca.relationships.AttachTo:
+ derived_from: tosca.relationships.Root
+ valid_targets: [ tosca.capabilities.Attachment ]
+ properties:
+ location:
+ type: string
+ constraints:
+ - min_length: 1
+ device:
+ type: string
+ required: false
+ tosca.relationships.Network:
+ derived_from: tosca.relationships.Root
+ valid_sources: [ tosca.capabilities.Connectivity ]
+ valid_targets: [ tosca.capabilities.Connectivity ]
+
+artifact_types:
+ tosca.artifacts.Root:
+ description: The TOSCA Artifact Type all other TOSCA Artifact Types derive from.
+ tosca.artifacts.File:
+ derived_from: tosca.artifacts.Root
+ description: This artifact type is used when an artifact definition needs to have its associated file simply treated as a file and no special handling/handlers are invoked.
+ tosca.artifacts.ShellScript:
+ description: A shell script (.sh file)
+ file_ext: [ sh ]
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-softwareComponent.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-softwareComponent.yml
new file mode 100644
index 0000000000..04e04af640
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-softwareComponent.yml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-softwareComponent
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-compute:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+
+ tosca.nodes.SoftwareComponent:
+ abstract: true
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA SoftwareComponent Node Type represents a generic software component
+ that can be managed and run by a TOSCA Compute Node Type.
+ requirements:
+ - host: tosca.nodes.Compute
+ type: tosca.relationships.HostedOn
+ tags:
+ icon: /images/software.png
+
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-webApplication.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-webApplication.yml
new file mode 100644
index 0000000000..ded008ebdf
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-webApplication.yml
@@ -0,0 +1,21 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-webApplication
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-webServer:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.WebApplication:
+ derived_from: tosca.nodes.Root
+ description: >
+ The TOSCA WebApplication node represents a software application that can be managed and run by a TOSCA WebServer node. Specific types of web applications such as Java, etc. could be derived from this type.
+ tags:
+ icon: /images/network.png
+ requirements:
+ - host: tosca.nodes.WebServer
+ type: tosca.relationships.HostedOn
diff --git a/catalog-fe/src/test/resources/CI/originalResources/normative-types-webServer.yml b/catalog-fe/src/test/resources/CI/originalResources/normative-types-webServer.yml
new file mode 100644
index 0000000000..1c2e4b2ea5
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/normative-types-webServer.yml
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03
+
+template_name: tosca-normative-types-webServer
+template_author: TOSCA TC
+template_version: 1.0.0.wd03-SNAPSHOT
+
+description: Contains the normative types definition.
+imports:
+ - "tosca-normative-types-root:1.0.0.wd03-SNAPSHOT"
+ - "tosca-normative-types-softwareComponent:1.0.0.wd03-SNAPSHOT"
+
+node_types:
+ tosca.nodes.WebServer:
+ abstract: true
+ derived_from: tosca.nodes.SoftwareComponent
+ description: >
+ The TOSCA WebServer Node Type represents an abstract software component or service that is capable of hosting and providing management operations for one or more WebApplication nodes
+ capabilities:
+ app_endpoint: tosca.capabilities.Endpoint
+ secure_endpoint: tosca.capabilities.Endpoint
+ host:
+ type: tosca.capabilities.Container
+ properties:
+ valid_node_types: [ tosca.nodes.WebApplication ]
diff --git a/catalog-fe/src/test/resources/CI/originalResources/scripts/apache_start_detection.groovy b/catalog-fe/src/test/resources/CI/originalResources/scripts/apache_start_detection.groovy
new file mode 100644
index 0000000000..93d732e0cd
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/scripts/apache_start_detection.groovy
@@ -0,0 +1,6 @@
+import org.cloudifysource.dsl.utils.ServiceUtils
+
+println "apache_start_detection.groovy: port http=${PORT} ..."
+def isPortOccupied = ServiceUtils.isPortOccupied(Integer.parseInt(PORT))
+println "apache_start_detection.groovy: isPortOccupied http=${PORT} ... ${isPortOccupied}"
+return isPortOccupied
diff --git a/catalog-fe/src/test/resources/CI/originalResources/scripts/install_apache.sh b/catalog-fe/src/test/resources/CI/originalResources/scripts/install_apache.sh
new file mode 100644
index 0000000000..a77f9a13f8
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/scripts/install_apache.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+echo "Using apt-get. Installing apache2 on one of the following : Debian, Ubuntu, Mint"
+LOCK="/tmp/lockaptget"
+DEFAULT_PORT=80
+
+while true; do
+ if mkdir "${LOCK}" &>/dev/null; then
+ echo "Apache take the lock"
+ break;
+ fi
+ echo "Waiting the end of one of our recipes..."
+ sleep 0.5
+done
+
+while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
+ echo "Waiting for other software managers to finish..."
+ sleep 0.5
+done
+
+sudo rm -f /var/lib/dpkg/lock
+sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})
+sudo apt-get install -y -q apache2 || exit ${1}
+rm -rf "${LOCK}"
+
+sudo /etc/init.d/apache2 stop
+if [ ! -d $DOC_ROOT ]; then
+ eval "sudo mkdir -p $DOC_ROOT"
+fi
+eval "sudo chown -R www-data:www-data $DOC_ROOT"
+
+if [[ ("$PORT" == "$DEFAULT_PORT") ]]; then
+ echo "Use default port for Apache : $DEFAULT_PORT"
+else
+ echo "Replacing port $DEFAULT_PORT with $PORT..."
+ sudo sed -i -e "s/$DEFAULT_PORT/$PORT/g" /etc/apache2/ports.conf || exit ${1}
+fi
+
+echo "Change config of apache2"
+if sudo test -f "/etc/apache2/sites-available/default"; then
+ echo "Change the DocumentRoot of apache2 on Ubuntu < 14.04"
+ sudo sed -i -e "s#DocumentRoot /var/www#DocumentRoot $DOC_ROOT#g" /etc/apache2/sites-available/default
+fi
+if sudo test -f "/etc/apache2/sites-available/000-default.conf"; then
+ echo "Change the DocumentRoot of Apache2 on Ubuntu >= 14.04"
+ sudo sed -i -e "s#DocumentRoot /var/www/html#DocumentRoot $DOC_ROOT#g" /etc/apache2/sites-available/000-default.conf
+fi
+
+sudo bash -c "echo ServerName localhost >> /etc/apache2/apache2.conf"
+
+echo "Start apache2 whith new conf"
+sudo /etc/init.d/apache2 start
+echo "End of $0"
diff --git a/catalog-fe/src/test/resources/CI/originalResources/scripts/start_apache.sh b/catalog-fe/src/test/resources/CI/originalResources/scripts/start_apache.sh
new file mode 100644
index 0000000000..478c56edf5
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/originalResources/scripts/start_apache.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+service="apache2"
+
+if (( $(ps -ef | grep -v grep | grep $service | wc -l) > 0 ))
+then
+ sudo /etc/init.d/$service restart
+else
+ sudo /etc/init.d/$service start
+fi
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/body.txt b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/body.txt
new file mode 100644
index 0000000000..681335d644
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-root.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"root.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/root.png"}],"payloadData":"UEsDBAoAAAAIAFxxbEbb+EOB8AUAABsZAAAYAAAAbm9ybWF0aXZlLXR5cGVzLXJvb3QueW1stVlLb9s4EL77V/CYALWSosgefFjASLvoKVnUuS0CgRbHFlGK1JKUXe+v3yGpB2VLtuUiySE2OW8OZ75hrDIZTRlsuOSWK2nSHWiDHxbE+i3Di1JAeqCFSD+nj/i7Z49fZjMLuE4tpJIWUBPPpdIFtXwHc3sowcy1UrajpJXNlV6Qt9fV85K8PXc7rc7PySP+Og3z1cvy79X317fZjIHJNC+tp3hW0lIuDbE5kFYf8fpI50cym0nFIPXrixkJBiZuzSQ/0Cy3RghdG6tpZtEBXYFf6mn70y8R8pZzQ3hQikpoJSy5c97d1968oGDyhsqQhFpChSAKiXW97fUSk6tKMAK/LEhmklg2Mqi9/9PjsIrkFN2jJMOz4QYZLTFgidqQDVBbaSTaKE0KJBdcbgmVjBRU0i0USFuruINk+ykWER24Z9fwb8W1ZzFISEu65gL3Ab85iYJvIDtkAgiXFvSGZmDugwOWbs2i1sMzF7QHXqB68+Dik5RyGyJtrebrykJLHPKLs+Y78Ye4IHgivGZqiHyOjZPF1jdkDEoMMsjscMwYMiH20SdES4UHATpdq0qyBXlsl6uy7JYr6T8A89uxrEabbnPsOs1dXHvxSbp1n7xJexLJyuLJUB3FbzB12xQbOMOQBxDyGozBAHIqPkX3SqHTNOSJT+w4OQt6IAbDorRNWmWZxrSMDuvIqsboyJrA0WmKZCm54VvM8UniGqZOIrkrNcyNpdred9L99ymSPcOQnaUyNp0sznHNh611O8HcyFpVTjNWlUO2YpmAaecTOGJZs1mbw4fjCnuS3YvTqnqxmj634kNN7crVcHk9oneNQLvs3WhVJMOm1X0EdGOfY2Cp4zh/UUvtIhFf9R0VnKX9dhNf+15Ba6sVu9RzlngGxld6JTH8rsZjmrhSaNxi23Hqm0lxs76NwAg1/vp4FxsKBplwVC7srfveRkdOoyiOxOybZKXC4vH7IcMVqzIlLoSqTg6UnpUNp9InddXVsy3ogQhvqDAQ1xPX7nnUJvBnTra+AulU6RQ5qUAcEm8LrIzR3h9PT1+e6n0DWa88BXvWSgmg8tSN2JxKi7SkNr86WxrmgUh/pZauqYGJB9SQX3dDw1WzLcap0cwaSGUw4zBBA7DBzwhbQmfBrDIlZNhU+H+4zmo7CdSau9Y5lnNLa2mWu9Y+Nes+yiEJdq/0zwk+rDIq6FrA79+bgssUVVKZnZaZ41vQZl2TzAX9dTtzvYACzA0ShguwhAxBBsZtSmBmGkRAJDkvj7tPvDcF51/sSD8iwdf1pBOOk67UdA5s9FvAkkT+GXOavH84hr4Cf8bBdcd3jM3OwE8YnCCaO3U1+gQsBePhHcSjiPzSFmSlRlU6G0c/ry0GwwvvMGMHz5yNgbu9+GNKwnneqiRwDylxKPNGV/pQ87wvfTWTnDlVM+oNZWySbKks3xxi0x3gckCINkr8whr8FMxc1d5xiru9tO3pnxLCSH+szuuPDdrnHHMUS4lUe9Syo9yX/rO24OiqdtNCPS0cQQHzleeSFR8RlAlWBDlpllO5BTbJjFqjUQU0rfPgunhbNp1hna3RJOgZb9JZu31ZZ9DRAYR+p/rqXyvMqxzvhKet7dzosAXE9jyrn0FwYJAn8R4S+x1vMLDrzWjtPrWlV6AxYIipAoxyNOsackX5i1cm99rJnccYQgB7cPYLdQB2T3yf7eXZ9T20m3beRxyv0Yh5U7e5fo0VDdweNSJg3etNaJPgGu0dkK7RxCm6FCrz0i+MJMNjFA5KDp0KkFscaiLcuOPZOeARSs/wkNN39yUA7xujE9LsTIa0aLSOz5WJFbPNKMYToY09xqXN+pknEaiBzbImDSjzGFX2dkfeOTplf/FzQ0ffqBFQ3BCFOQkX/Gy0z0ESKrvdCA1LANa9XHOLMoxROAO6u73h7m3K/U8Ba6cfu1l4fPAb7qlZqmZkRAGSuUftB/8BtPGPHFzu1E9gQ/6uchBi5T0YiPESJzzcJ2GJ3CUm92rDI5v7lOLA504aN95n/wNQSwMECgAAAAAAXHFsRgAAAAAAAAAAAAAAAAcAAABpbWFnZXMvUEsDBAoAAAAAAFxxbEYZnyLT1xQAANcUAAAPAAAAaW1hZ2VzL3Jvb3QucG5niVBORw0KGgoAAAANSUhEUgAAAeQAAAIACAYAAABTkx/wAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAbvQAAG70BaPBCXwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAABRUSURBVHic7d1viGX3XcfxTyZNq5ZqqWIobUZpoCUUXYqC6AMpaIQ+sC3aqaw2ba0gxQeaIDhW9kECq3EEMYUiBfsnrX8WvEUEKULqoxQqglqmUgpCBaeWNlJDVaJtYjo+mHs3s7uzM/fPOfd8z/m9XjBPdu+f7/L73fvec4Z77h3Hx8dhHC5dvXZPkp88vHL5o0PPwvkuXb324iS/fnjl8qNDz9KlS1evvS/J7x9eufzs0LNwvktXr/1ikr85vHL5S0PPwnJ2hh6AldyZ5EPzFxpFzWM8S/KWoWfpwVuSzOb/Roqav0d8KCfvGYyEII/PTkS5rFMxfvPQs/TozRHlsk7F2Pv7yFiwcRLlghqJ8YIoFyTG42bRxkuUC2ksxguiXIgYj5+FGzdRLqDRGC+IcgFiPA0Wb/xEeUCNx3hBlAckxtNhAadBlAcgxjcQ5QGI8bRYxOlYRPndQw/SAjE+kyhv0fy1LsYTYiGnZSfJh0W5X2J8LlHegvlr/MPxHj4pFnN6RLlHYrwUUe6RGE+XBZ0mUe6BGK9ElHsgxtNmUadLlDskxmsR5Q6J8fRZ2GkT5Q6I8UZEuQNi3AaLO32ivAEx7oQob0CM22GB27CI8ruGHmRMxLhToryG+WtWjBthkduxk+QjorwcMe6FKK9g/lr9SLxPN8NCt0WUlyDGvRLlJYhxmyx2e0T5HGK8FaJ8DjFulwVvkyifQYy3SpTPIMZts+jtEuVTxHgQonyKGGPh2ybKEeOBiXLEmBMWn6ajLMYlNB1lMWbBBiB5IcrvHHqQbRLjUpqM8vw1J8YksQl4wU6Sj7YSZTEuqakoz19rH433YeZsBE5rIspiXFoTURZjzmIzcLNJR1mMR2HSURZjbseG4CyTjLIYj8okoyzGnMem4HYmFWUxHqVJRVmMuYiNwXkmEWUxHrVJRFmMWYbNwUVGHWUxnoRRR1mMWZYNwjIWUX5g6EFWIcaTMsooz18zYsxSbBKWtZPk8bFEWYwnaVRRnr9WHo/3WZZko7CKUURZjCdtFFEWY9Zhs7Cq0lEW4yaUjrIYsy4bhnWUjLIYN6VklMWYTdg0rKtUlMW4SaWiLMZsysZhEyWiLMZNKxFlMaYLNg+bGjTKYkwGjrIY0xUbiC4sovyObT6pGHPKIFGe7/nH472UDthEdGUnyce2FWUx5gxbjfJ8r38s3kfpiI1El7YSZTHmHFuJshjTB5uJrvUaZTFmCb1GWYzpiw1FH3qJshizgl6iLMb0yaaiL51GWYxZQ6dRFmP6ZmPRp06iLMZsoJMoizHbYHPRt42iLMZ0YKMoizHbYoOxDYso/8IqdxJjOrRWlOd7VozZCpuMbdlJ8vFloyzG9GClKM/36sfjfZItsdHYpqWOlMWYHi0VZUfGDMFmY9vuzDlRFmO24Nwon4rxnVudiuYJMkM4M8pizBadGWUxZkiCzFBuiLIYM4AboizGDO1FQw9A0xZRfnGSt0aM2b5FlP8yyR9FjBmQIDO0O5N8ZOghaNqb4z+DFOCUNQAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABLxp6AFbylSQ/OvQQLO2/hx6gB7+U5GVDD8HSvjL0ACzvjuPj46FnAIDmOWUNAAUIMgAUIMgAUIAgA0ABggwABfT+safdvf1vT3LPqZ9XJbmr7+cFgA08l+TLSb60+DmaHfxvn0/Y+ceedvf2X57kZ5JcTvKGJN/d6RMAwDD+I8lnk1xL8hdHs4Ovd/ngnQR5fhT800l+Psmbkrx44wcFgLqeTfLXSf4syV91cfS8UZB39/bvyMmVew6SvGLTYQBghJ5Osp/kw0ezg7WjunaQd/f2fyDJB5P82LpPDgAT8pkk7z2aHfzTOndeOci7e/svTfJwkgfjWtgAcNr/JXksycNHs4NnVrnjSkHe3dt/ZZJPJXn9SuMBQFs+n+T+o9nB0l/wsfTnkHf39r8/yacjxgBwkdcn+fS8nUtZKsi7e/uvy0mM711vLgBozr05ifLrlrnxhUHe3du/lOTJJK/ecDAAaM2rkzw5b+m5zv0d8u7e/vcm+VySu7ubDQCa81SSHzyaHfz77W5w2yPk+WeMH48YA8Cm7k7y+LytZzrvlPWv5uSqWwDA5t6Uk7ae6cxT1vNz3X+X5CX9zQUAzflmkh85mh0c3vwXtwR5fl3qf0hy33ZmA4CmfCHJD918/euzTln/csQYAPpyX05ae4MbjpB39/bvSvIv8REnAOjTvyV5zdHs4LnFH9x8hPxAxBgA+vbqnDT3uutHyLt7+zs5Oa/92u3PBQDN+eck9x3NDr6V3HiE/LMRYwDYltfmpL1Jbgzyr21/FgBo2vX23nF8fJzdvf2XJ/lakjuHmwkAmvN8ku85mh18fXGE/BMRYwDYtjtz0uDrp6zvH24WAGja/YkgA8DQ7k+SO+5522+8JskXBx4GAFp2706SNw49BQA07o07Se4ZegoAaNw9Ozn50mQAYDh3CzIADE+QAaAAQQaAAgQZAAq4eyfJy4aeAgAa97Kdi28DAPRNkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAKEGQAKECQAaAAQQaAAgQZAAoQZAAoQJABoABBBoACBBkAChBkAChAkAGgAEEGgAIEGQAK2Eny/NBDAEDjnt9J8rWhpwCAxn1tJ8lTQ08BAI17SpABYHiCDAAFCDIAFPDUTpKvDj0FADTuqztJPj/0FADQuM/vJHkyybNDTwIAjXo2yZM7R7ODZ5J8ZuhpAKBRnzmaHTyzuHTmpwYdBQDa9ankhWtZPzHgIADQsieSF4L8j0meHm4WAGjS0zlp8EmQj2YH30ryiSEnAoAGfWLe4Bu+fvH34pufAGBbns9Je5OcCvLR7OCLSWZDTAQADZrN25vkxiPkJHl0y8MAQKtuaO4NQT6aHXwuySe3Og4AtOeT8+Zed/MRcpL8dpLj7cwDAM05zklrb3BLkI9mB3+b5P3bmAgAGvT+eWtvcNYRcpL8ZpLDfucBgOYc5qSxt7jj+Pjss9O7e/v3Jfn7JN/R31wA0Iz/SfLDR7ODL5z1l7c7Qs78Dg/1NRUANOah28U4OecIeWF3b/9PkvxC11MBQEP+9Gh28I7zbnDbI+RT3pXk8U7GAYD2PJ6Tlp7rwiAfzQ6eT/KeJB/YfCYAaMoHkrxn3tJzXXjK+rTdvf3fSfK+DQYDgFY8ejQ7+K1lb7xSkJNkd2//wZxc7uvbVhwMAFrwjSTvO5odPLbKnVYOcpLs7u3fm+QPk/zUyncGgOl6IsmvnP7SiGWtFeSF3b39tyd5LMkr134QABi/ryR58Gh28OfrPsBGQU6S3b3978zJ75UfSPKqjR4MAMbly0n+OCe/L/6vTR5o4yAv7O7t7yT58SSXk7wtySs6eWAAqOXpJJ9Ici3Jk0ezg2918aCdBfm03b39u3Ly++U3JLnnpp/v6vwJAaB7/5nkSzf9fDbJE0ezg+e6frJegnye3b39lya5a6tPCgCree5odvDMNp9w60EGAG61zKUzAYCeCTIAFCDIAFCAIANAAYIMAAW8aOgBLnLp6rUPJnn50HOwtAcOr1zu/PN5Xbl09drHk7x46DlYyrOHVy6/c+ghbufS1Wt35eQKTYzD1w+vXH7v0EOcp3yQk7w1yd1DD8HS3p2kbJCTvD3JS4YegqV8M0nZICe5M8nPDT0ES3sqSekgO2UNAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAUIMgAUIMgAUIAgA0ABggwABQgyABQgyABQgCADQAGCDAAFCDIAFCDIAFCAIANAAYIMAAWMIci/O/QALO2xwyuXvzH0EBd4dOgBWFrptZrv9ceGnoOllW9J+SAfXrn8WJKHhp6DCz12eOVy+XU6vHL5kSSPDD0HF3pkvlalzfe8KNf30LwlpZUPciLKIzCKGC8cXrn8cES5skfmazQKolzeKGKcjCTIiSgXNqoYL4hyWaOK8YIolzWaGCcjCnIiygWNMsYLolzOKGO8IMrljCrGyciCnIhyIaOO8YIolzHqGC+Ichmji3EywiAnolzAJGK8IMqDm0SMF0R5cKOMcTLSICeiPKBJxXhBlAczqRgviPJgRhvjZMRBTkR5AJOM8YIob90kY7wgyls36hgnIw9yIspbNOkYL4jy1kw6xguivDWjj3EygSAnorwFTcR4QZR710SMF0S5d5OIcTKRICei3KOmYrwgyr1pKsYLotybycQ4mVCQE1HuQZMxXhDlzjUZ4wVR7tykYpxMLMiJKHeo6RgviHJnmo7xgih3ZnIxTiYY5ESUOyDGp4jyxsT4FFHe2CRjnEw0yIkob0CMzyDKaxPjM4jy2iYb42TCQU5EeQ1ifA5RXpkYn0OUVzbpGCcTD3IiyisQ4yWI8tLEeAmivLTJxzhpIMiJKC9BjFcgyhcS4xWI8oWaiHHSSJATUT6HGK9BlG9LjNcgyrfVTIyThoKciPIZxHgDonwLMd6AKN+iqRgnjQU5EeVTxLgDonydGHdAlK9rLsZJg0FORDli3ClRFuMuiXKbMU4aDXLSdJTFuAcNR1mMe9BwlJuNcdJwkJMmoyzGPWowymLcowaj3HSMk8aDnDQVZTHegoaiLMZb0FCUm49xIshJmoiyGG9RA1EW4y1qIMpiPCfIcxOOshgPYMJRFuMBTDjKYnyKIJ8ywSiL8YAmGGUxHtAEoyzGNxHkm0woymJcwISiLMYFTCjKYnwGQT7DBKIsxoVMIMpiXMgEoizGtyHItzHiKItxQSOOshgXNOIoi/E5BPkcI4yyGBc2wiiLcWEjjLIYX0CQLzCiKIvxCIwoymI8AiOKshgvQZCXMIIoi/GIjCDKYjwiI4iyGC9JkJdUOMpiPEKFoyzGI1Q4ymK8AkFeQcEoi/GIFYyyGI9YwSiL8YoEeUWFoizGE1AoymI8AYWiLMZrEOQ1FIiyGE9IgSiL8YQUiLIYr0mQ1zRglMV4ggaMshhP0IBRFuMNCPIGBoiyGE/YAFEW4wkbIMpivCFB3tAWoyzGDdhilMW4AVuMshh3QJA7sIUoi3FDthBlMW7IFqIsxh0R5I70GGUxblCPURbjBvUYZTHukCB3qIcoi3HDeoiyGDeshyiLcccEuWMdRlmM6TLKYkyXURbjHghyDzqIshhzXQdRFmOu6yDKYtwTQe7JBlEWY26xQZTFmFtsEGUx7pEg92iNKIsxt7VGlMWY21ojymLcM0Hu2QpRFmMutEKUxZgLrRBlMd4CQd6CJaIsxixtiSiLMUtbIspivCWCvCXnRFmMWdk5URZjVnZOlMV4iwR5i86IshiztjOiLMas7Ywoi/GW3XF8fDz0DM25dPXag0m+T4zpwqWr1x5OrgcaNnLp6rU/SPKvYrx9/w/CusV4t1Y9iAAAAABJRU5ErkJgglBLAQIUAAoAAAAIAFxxbEbb+EOB8AUAABsZAAAYAAAAAAAAAAAAAAAAAAAAAABub3JtYXRpdmUtdHlwZXMtcm9vdC55bWxQSwECFAAKAAAAAABccWxGAAAAAAAAAAAAAAAABwAAAAAAAAAAABAAAAAmBgAAaW1hZ2VzL1BLAQIUAAoAAAAAAFxxbEYZnyLT1xQAANcUAAAPAAAAAAAAAAAAAAAAAEsGAABpbWFnZXMvcm9vdC5wbmdQSwUGAAAAAAMAAwC4AAAATxsAAAAA"} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/command b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/commandTrial b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/contentMD5.txt
new file mode 100644
index 0000000000..83e75b2b56
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/contentMD5.txt
@@ -0,0 +1 @@
+YmEzYWEwZWNjYjk3N2EyZDBhYTc5M2M0NjkyM2Y5OTE= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/headers b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/results b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/toExec b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/01_NormativeTypeCI-root/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/body.txt b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/body.txt
new file mode 100644
index 0000000000..d197f9e4ec
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-compute.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"compute.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/compute.png"}],"payloadData":"UEsDBAoAAAAIAJhdbEYhgTkIBgQAAFULAAAbAAAAbm9ybWF0aXZlLXR5cGVzLWNvbXB1dGUueW1szVbbbhs3EH3XVwzcFxuwFKcpgkIPAVwBTfMQ24jcpyBYULvcFZFdcsuLlM1TPiP9vXxJz3AvohwlSNsUqGTYa3J2zjmc4cx443KRFbJUWnlltMt20jo8LMnHLaeatpZZJ5o6e5xd4bsvrp7MZl5iXXiZadHIwXiujW2EVzs5910r3Tw3TRu8PBiL4LfGLun+dr26pvvVYWeCfby4wpdB5uub67v1b7f3s1khXW5V66PFymgvlHbkt5ImSIqQdJCymIG6sd4tZ0RzOjtN0Rrjlycwz2YzbQqZRSv28AO90KUVztuQ+2AlsTijpfYOu9H5gt9wi1Wvml8i8LEAK7LSmmZ5ZPYKyINJIu5ZXCJ6JVsrHXsnQVaKmoylnbI+4LER+VZpyUufPnx00uL0Pn34c8EUe32IJLlW5qpUsiCj41kNxAYEZkF7Vde0kRQczLwhHF4RbUcIvxUeq74/7QpaNOj8EZSVTWSnet95bUIxeBY7oWqxqScvDsxKAJJT75Wu8E5CU+AsD1QnX9DjqLVmp3CEHFYRaj8AjORS9oDwpKDaW6OrugNLRAgci15ZD9FFgEFAQXkbQACSG9kY2438PdUSkV7E/72o3HLYUTnH6JFqRCXdoyG9F62u4j7YttJ6JSd7HZqMMcb/KWbpEgfgZSXttDoSWlIpaiendcBBD5LdJy44m6lCTnhpM8QHjK6mzaNkugnNBodnSjoXecycQxJd0Orud4h3zuQKrgqcpt+meRIzZDEeuWxQC97L767km4Ssgcwy+jBdctK9lJXYdB5X/vzlLxeXSdIh2KJta5UPCWaD1px1x7fgSF2h3Nv/gTymV5sccWJCUeZzNcl8/q9lGpcJm28figRzNaTwV7XsRK2KDL8DMpxen737+Wn25MezS4pPT386e3Na3j3obI3zdIvrAbYgue4cSj+d364viCkpL2NVTZhGbt+Jaa10eMdEhYp/UEAAwU97FDyzd/+QOrNKKCNqILgJ8d2vU/9SIv0d9BQuYTG20v+UwADSwwrf8zgUPtVmoijQwT4rfQ9onGx+/GHo1qLS2o5e3NHgjeuVqjSq1aY79IpDn4jt6uhaNKLj/hCvS56ziy/dDv7cGDRuYKOR4Cd2xQGIxxTuWgmW4QTcyWF/4DoSHTtqApqnoES/hjhH7I19ywdbWFGin8aeNjqJwxf32UFMlOdCy3MNlbXpQ4Ki3qA5KtgmR4WuO0sCHbv11JekZ9iHsenHk1y0YqNqxY0Mw4zWuJpqp3w3Wddmj5K2MUEXaU0Dr8Ny0PFB9kNB6nNE5cT6NgY87iVF+PM+y5/+zidDG71Ox621Kf0es8ZqHNvGG4/kxThxEvo6bvHJDbawiMX3pPV62Jz9BVBLAwQKAAAAAACYXWxGAAAAAAAAAAAAAAAABwAAAGltYWdlcy9QSwMECgAAAAAAmF1sRqaI/3ih7wAAoe8AABIAAABpbWFnZXMvY29tcHV0ZS5wbmeJUE5HDQoaCgAAAA1JSERSAAACAAAAAgAIBgAAAPR41PoAAAAEc0JJVAgICAh8CGSIAAAACXBIWXMAABrWAAAa1gErf24pAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAACN0RVh0VGl0bGUAQ1BVIChDZW50cmFsIFByb2Nlc3NpbmcgVW5pdClSoUzSAAAAC3RFWHRBdXRob3IAaXZha8gLBJwAAABtdEVYdERlc2NyaXB0aW9uAGNoaXAsIGNsaXAgYXJ0LCBjbGlwYXJ0LCBjb21wdXRlciwgY3B1LCBlbGVjdHJvbmljcywgbWljcm9jb250cm9sbGVyLCBtaWNyb3Byb2Nlc3NvciwgcHJvY2Vzc29yLCCRJqL9AAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMTAtMTItMTBUMTA6NDU6NDaDD8peAAAAWHRFWHRTb3VyY2UAaHR0cDovL29wZW5jbGlwYXJ0Lm9yZy9kZXRhaWwvMTAwMjY3L2NwdS0oY2VudHJhbC1wcm9jZXNzaW5nLXVuaXQpLWJ5LWl2YWstMTAwMjY3Lm/wGAAAAEl0RVh0Q29weXJpZ2h0AFB1YmxpYyBEb21haW4gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluL1nD/soAACAASURBVHic7L13vGRHde/7q+6Tz+TRjCYIzYw0I2mUNUpIRkJIIODKNtmIZD44XuBhv2sbf5xtHK79Id0LmGBjzAWDDfcZjE0QIIQkBBqBhEBCYaTJSZPzyd296/2xU+VdVXt3n9C15jOnd6i1VlX17vquWjsRSinaLYQQ0nYnQYLMQqGd+AEGCRIkiEKIy/hz3Tuv7iWEvBIAAUC1/3OTFAClJP4kJp1Yj9WnIPk+AhLbyspTVhcmu6kuAEpqJFL6ZXyxPjJdqvFF4n2UrVNSllBCKWi2Hi/RuCyllEbx/6hFadSKaGuqRRvjDTp+bILu2bw3gCFIkCCzSkJAO7vEOgBI4P9/AbzSzUP64ZgEIOyigy4RVy10NUUKdQ27tbqFJpUFuABF958NdMAGKEWBV+zXR0/2W6wHSdeyjh56UG0nRNFPYhCq6UdlH5uCUEUQW/hfEYwSjR+bvi/0q9dTt0mjQ7Nlavap9gfGr52e4ruyPI7VxwUT7NNMj6rrItSBPR4pQEGp2hc7WaCxHrueTRKyz2SSEFEaNSMaNVu0Odmik6OTdOTwCH3mu1tTH0HmiHQyiLIKAK5759U9hJAvAni1m/X0I8C/SKeMnlbXsusI3+FOUlrX4+SQrx4AeJ2N6uRxrPwaO38cG/V8j+Myuj6/AaehY3Ydx0qJ0412QabLfybQSuyjXfYZH8Zgzde2wY+fL4N9jQ8Kim3f/Mu7P6D9HhXSrqCgMAC47p1X1wkh/wbgdW6W048A/yKdMnpa3QB/WTfAv7xegL+sOzPgD/bca5V288X22q/ch8FUJX4KTJh8UErf/a2//M4H3a1m+pV0lDEASOD/OQB3ullNPwL8i3TK6Gl1A/xl3QD/8noB/rJugH8l9iv3MYPhnxaJWtGd3/7re75kYVVrrGwgUDPtJIT8AQL8C/WMugH+sm6Av1bHSXc2wJ+U1C2QAP8A/yLblfspD38AILV67TMv/sNbb0T+K1H9h2I9N5KIbdVFMQYAAJY7WQvwt9Ypo6fVDfCXdQP8y+t1+jj21Q3wr8xuvhjgb2vfw8dAT1/9y9e86ar5iI+MWvLfJhjgxDcI6CnYbwwQVixciAtXrMyrBY9Bs0D2nziB7UcOyzsC/J31JN0Af5XD3K+HnpNugH91ugH+ldnNFwP8be2X8HFW72DffABjjBfCeGOXIZThakUIIa6nBEoFAOcuXYpXbbraxZ+zfO/ZZ+QAIMDfWU/SDfBXOcz9eug56Qb4V6cb4F+Z3XwxwN/WfikfFGhONnuQs5ZqPnW14gIE1yCgKACodjpfhQT4O+tJugH+Koe5Xw89J90A/+p0A/wrs5svBvjb2i/lI1GLmlEdQB089FW3JRJhO1FsdwoCSmUAOi4B/s56km6Av8ph7tdDz0k3wL863QD/yuzmiwH+tvZL+WDUkgyAKgBQ/ddZ8woC/AOATucGAvyd9STdAH+Vw9yvh56TboB/dboB/pXZzRcD/G3tl/IhqLUarV7ELNZBX358vRwQeAUBfqcAfAdNXwnwd9aTdAP8VQ5zvx56TroB/tXpBvhXZjdfDPC3tV/Kh0ItakY9UAcAKfiJsB4ZrHNBQJG4ZwAC/K11yuhpdQP8Zd0A//J6Af6yboB/JfYr9zGH4A8ArUarD0AvZPBHyX+CHPqpFasgoCgL4JYB6DT8BQnwL9aTdAP8VQ5zvx56TroB/tXpBvhXZjdfDPC3tV/Kh0Gt1Yj6APRBBj/7vwU+EICwnHpwygBoz/EnDxbI9wf4W+uU0dPqBvjLugH+5fUC/GXdAP9K7FfuYw7CHwCiZhYApJkA9n96gWD6WUf+wCDVQ4NSsToa7U4BBPhb65TR0+oG+Mu6Af7l9QL8Zd0A/0rsV+5jjsIfAKJWqx9AP/KZPvuZgj7NALRUJiAfgVYXAxafAgjwt9Ypo6fVDfCXdQP8y+sF+Mu6Af6V2K/cxxyGPwBETdqP/BRACv/0PzvLZy2ztwyy+1mvJF3XBQHmAIDEGYDpgr+VBPgH+Bc7zP166DnpBvhXpxvgX5ndfDHA39Z+KR8OalEr6gcwAB78LQBN6AMAdpky+3XllFJ4CiDAv1injJ5WN8Bf1g3wL68X4C/rBvhXYr9yH10AfwoKGkXpKQAV/JsK66b/2aw/EXGdE2UAkL5ZiIDMrCcBshLgH+Bf7DD366HnpBvgX51ugH9ldvPFAH9b+6V8OMIfAKKIDoAPAFL4p7N/1rr4Pz3/n/53ehaAFAAIrxWcmQFAgH+Af7HD3K+HnpNugH91ugH+ldnNFwP8be2X8uEBfwCgEe1DfAqgiTgASK/0F480FvrsBYKqDABhdLQyu94FAAT4i7oB/iqHuV8PPSfdAP/qdAP8K7ObLwb429ov5cMT/kAWAPQjBr/pvL/q+QA1yFkAZQZAdSHg7HobYIB/gH+xw9yvh56TboB/dboB/pXZzRcD/G3tl/JRAv7JxvQuAPb+ftY6C3/2NkHVswBSUQYBopTLAHQyPAjwD/Avdpj79dBz0g3wr043wL8yu/ligL+t/VI+ysIfAKU0fQiQ7rw/C/86ZPirsgCpM2MgYAoAUsP6vZ2SAP8A/2KHuV8PPSfdAP/qdAP8K7ObLwb429ov5aMC+CfSizwASI82Efwp/F2fBmi8KLDU2wCnUwL8O6Qb4K/VcdIN8A/wD/AvtF25n5kPf1CaPfaXtc4GAE2o4W8Cv3Hmn4o5AKCoST8Aol3pmAT4d0g3wF+r46Qb4B/gH+BfaLtyP7MA/kmp9HXAqaTg7wE/868p/otBgNNR63YNgM+gWbEE+HdIN8Bfq+OkG+Af4B/gX2i7cj+zBv4AYg73IJ/194A/55/Cn709UHfxn06UGYGiRwETZlm5uZMS4N8h3QB/rY6TboB/gH+Af6Htyv3MLvgDOeTZ1wGLs/6i8/5sIGB9BDu9DTBeDPAP8K9GDwjwr0QvwF/WDfCvxH7lPgL8VQpsAMA+4McG+CL02c90WVuhogf91OAzaFYsAf4d0g3w1+o46Qb4B/gH+BfartzP7IQ/QIgK+Lr/lcz8Uym6z58oFmeWBPhXoxvgr9Vx0g3wD/AP8C+0Xbmf2Qp/AMR8gZ8t+L2OYKtTAAH+xXpa3U7Cv4ODX4C/nZ6VboC/rBvgL9nNFwP8be2X8tEB+ANID/wU9qpPHfgB+eh1CgYKnwNgGoSe3L8PH/jWCW1VygyaqYxOTjrrBfg76Ab4a3WcdAP8A/wD/AttV+5ntsM/FhbstrN9Feidj2ZjAEAKXgc8NjWFsakppevpGDSNugH+sm6Av1bHSXcuw9/nOHbQk3QD/CW7+WKAv639Uj46C3/ADvKmmb33kVx8EaCNBPg760m6Af4qh7lfDz0n3QB/Lz2lboB/ZXbzxQB/W/ulfHQe/iDxRYAs6KUizKcqMNDpFUoR4IuNBvg760m6Af4qh7lfDz0n3QB/Lz2lboB/ZXbzxQB/W/ulfEwD/AGwx6MIeBXo1Zrmfdpy5TIAAf7OepJugL/KYe7XQ89JN8DfS0+pG+Bfmd18McDf1n4pH9MF/3g/C3qVWMHcR/wDgAB/Zz1JN8Bf5TD366HnpBvg76Wn1A3wr8xuvhjgb2u/lI/phD+gOy4rBb1O/E4BBPg760m6Af4qh7lfDz0n3QB/Lz2lboB/ZXbzxQB/W/ulfEw3/AGQfLAsOkptMgFOR7p7BiDA31lP0g3wVznM/XroOekG+HvpKXUD/Cuzmy8G+HO25zD8NaICvQ38TXaU4hYABPg760m6Af4qh7lfDz0n3QB/Lz2lboB/ZXbzxQB/G9ulfcwk+PM/LB/Qe4v9KYAAf2c9STfAX+Uw9+uh56Qb4O+lp9QN8K/Mbr4Y4G9ju7SPmQR/oOxxWipgsMsABPg760m6Af4qh7lfDz0n3QB/Lz2lboB/ZXbzxQB/G9ulfcww+FNQEPgMnpx46zu9DTD2FOBvIwH+hQ5zvx56TroB/l56St0A/8rs5osB/ja2S/uYgfAHUO3xWiBEGKyt3waoWNXp2Jiy1jPqzoZBM8Bf5TD366HnpBvg76Wn1A3wr8xuvhjgb2O7tI+ZCn/AZkDS7S99tFtfAxDgX6wn6Qb4qxzmfj30nHQD/L30lLoB/pXZzRcD/G1sl/Yxk+EPj7GwQrG6BiDAv1hP0g3wVznM/XroOekG+HvpKXUD/Cuzmy8G+NvYLu1jhsMfQLXHrqMUZgAC/Iv1JN0Af5XD3K+HnpNugL+XnlI3wL8yu/ligL+N7dI+ZgP8Adcj2KastT1jAEAKXgdschXg76gb4K/Vc9IN8PfSU+oG+FdmN18M8LexXdrHLIE/AJS/CcBfyr0NMMA/wL/YYe7XQ89JN8DfS0+pG+Bfmd18McDfxnZpH7MJ/oDH4FidVPYyoHxzgL+TboC/Vs9JN8DfS0+pG+Bfmd18McDfxnZpH7MN/nD8uVViJpeiAKDu4irA31E3wF+r56Qb4O+lp9QN8K/Mbr4Y4G9ju7SPWQh/AJ4DbDVSfI5flAD/AP9ih7lfDz0n3QB/Lz2lboB/ZXbzxQB/G9ulfcxW+Of2XY5s4lheK6VeBpRvDvB30g3w1+o56Qb4e+kpdQP8K7ObLwb429gu7WO2w199XItb25IlsA8AAvwD/Isd5n499Jx0A/y99JS6Af6V2c0XA/xtbJf2MdvhDxTda98W8KdidxdAgH+Af7HD3K+HnpNugL+XnlI3wL8yu/ligL+N7dI+5gD8Y2kr443i/DKgVAL8HXUD/LV6TroB/l56St0A/8rs5osB/ja2S/uYM/CH6Rhve2RQ9DIg5f4Af0fdAH+tnpNugL+XnlI3wL8yu/ligL+N7dI+5hL8AZsjvW2BgPODgAL8HXUD/LV6TroB/l56St0A/8rs5osB/ja2S/uYY/AHgJJPAiyl7HQXQIC/o26Av1bPSTfA30tPqRvgX5ndfDHA38Z2aR9zEf6xiBw2/QKIZtlLdAEAEfcH+DvqBvhr9Zx0A/y99JS6Af6V2c0XA/xtbJf2MXfhzx7zvke/96/G6hRAgL+jboC/Vs9JN8DfS0+pG+Bfmd18McDfxnZpH3MZ/qj2sHeVwlMAAf6OugH+Wj0n3QB/Lz2lboB/ZXbzxQB/G9ulfcxx+AMQB2PTL6HyWKHHtJMUvQ44wJ/XDfDX6jnpBvh76Sl1Hb6uhf2DWDw0pNU7OjqC0akpbtu5ixaDGJ5ksv/0KTSjiNvWW69j1fwFeQUF5dGpKRwbG+W2nTU8jKHePm3dT4yP4czkZIC/he1K/QT4l7c/jSkAYwAAU9W6CP7XrlmLcxYv1mr/bP8+bD96xM6Z6Jfx94e3vxyHD52Uyg0O9eNMNInP/HAzp3fLhgtxw7rzcPTIKbRawiDb14Ndoyfwraef5H0G+JfXmw3HsYNeqnvR8rNx1lgPnnhil7T/ta+9Cd/cvgVPHTrIbX/jlVdj185DeHDzU5LO7S/ZhH+feBKnJsa57Qv6B/DiVefj23c/KunceMPFGBsGvraFP25fsOY8rF+8FF/44v2SzsaN5+Lksvl4eO+eAP8C25X6CfCvxL7lVM42S+AkRQGAOgPQRfAHgA3Lz8ZXP/MATp8Zl/ZdcvEarLhqmXMAoJrB99d68JY3vVcqe/tLNuEX7/w5TJya4PTGTo7jzPFR/Pa7Po4jR05xOlddeT7e+u47eJ+O8B8eGMCa5Suy9YmpKew8dAAAsGb5Cgz192f7jp46hSOnmeDFA/6rli7FwuF5fJ01utsO7Eej2ZR82eimcuE556LG9MnWA/vQbLWwZN4CnL1oSbZ9fGoCuw8fMvoq9KnZPNw/gOctW57pjk9OYveR2Ne5y87m+/jMKRw9fUppp6q0/+bNT+Nzn/+uVGZsbBI7z5zA4ZEz3PbP7f8unn12P+699zFJ55JL1mh97n/uGD7+ia9L27c8sw+LVi3EtqQPUvnGllEs6hvAp/75W5LOL73uZlx6x8XWg/SGlatRq+VD246DB9BoNbFweB5WLMoD/clGA7uYgKdKMC+eNx+Lh+cp4C/LgRNHMS5kXqwkwL+UbS8fPk2u7mWAzobcMwBdBv9UNj+0BcePn1Huu+mqZXYOWb8OX9W3735UOVvajB/Z+/Q4yC4+dx3+7q2/ma1vP/gcfvPv3wcA+O1feC0uX3d+tu+z3/0WPvvdbybOGL8OM/833XI7br/yWqvib/ng3+DQyeOlZv4feNs70NuT/wTufP97cHJkBLdceiV+82W/mG3fsm8P/t9/+khbjuONz1uDv3rjr2brz+zfg//xqY8CAN75316Jy9acl+37/Pe+g8/ff3exX0/4m/Q+89nvKLdvwbP2zlQ+BVEFEgDwNJ4psGo/8v7VG38Vw/0D2fqvfvR9OHzqBG688BK842WvyLbvPHQAv/VPH0msVzvzf/mm6/HGm26zKvsH//KP+NnuHW4OAvxL2fby4d2t1PSL1e2rJGqo5G2A8a65C/8qpfILnmx8lkj7l9Erk/Yvozfb0v5l9Kbrgj9fmY7jv6x0Iu3fKdsB/m3y4d1kqssAtHnkiKX02wDjXQH+1n5nEfydNWcx/MvolIP/9J3z99ErIwH+mdH2SYB/KdtePsrAf5ql1NsA410B/tZ+Ow3/EkrTdcFfGWkL/H19+h7Hvn4D/GeHBPh72fb2MTvg39Y0v0mKLwIM8C8tZQe/xYvnYenyhTh85jS3feHgEBb0D+DZrfvRaDRL1hIS/J/auwvv+of/ne2eYC5E+tBX/z2/QI3EFwGyura+WPnX++7G1x/ebKV+fITvCx/4/+6nP4Yac6frmfExAMB9T/wUT+/bk+mMT05oTJY/jrfs3Y3f+eePZtvHpyaz5Y/e9RUM9fEXASr9dgD+v/5rL8P208dx6Ax/Hcyt6y/Alqf34u7vyNeocD4tfb3kxZuw+JxFePLwAW77ZStWYVHvAD7xD/KFg67yp//6Ke4iwBPJhY0PbnkSOw4+l22faHhceGcSZsy/5/Ef4/Hd263Udhw6UFwowL+UbS8fFc38LZK0Nr8eL8KYAgBiGlW7Df4feP+v4x3v/CjGx/MB+pKL1+Cd7/gFPH5K/wN1GfwIIVi/fpW0/dYXXYnbfvFqfOL793PbX7rxEty8fgN+9/c+iSNH+avDzznnLDuneUXz+iYyOjGBp/fuVhbfffggpyfq2vjiNxE8d/wYnjt+zEnPyq9m97P79yq3nxg5gxOj6gs+C306HsejkxPYwgQbrOwRroRX+q0Y/jfcsBHz5w9K21/72pvwre3P4KnD/G2Av/TCm7Br5yGsX79S0lm9ainIjkNKX6tXLcXb//sd0vb0NsBx4TbAl1x8GdYvXqocLDduPBcnEck7NLL1wH55IwVOjY7g1OhIR9L+B08ex8GTx9tim98V4N8WH5Wm/Z2AVOnUVRsALDxnQYB/ojveaOC8NatQq/HKg8P9WLJ8AaaOqUHiOvP/yf49+H/+4jVxJQW9p5iZSSp7TxzHj3bvwuvf9RLGZy67TDDlK5rX10UqhL+PnpWuj95sOI4d9CRdjd6Ww4eweGgI5JKFkpsvP/04jgoP5wGAf3vsURAANUEHAL7z3A6MNial7acnJ/CdAztQu3SRtO+hM89h9Jg88/7+7h14dP8+pZ9ncArHD8u36FpLuM/f2XZpHwH+uZQ5F1hSCKViOoIQAOR5zz+n53nPXy39ersN/qzeDz78EJqTeap9ybrFuOy1l+h1Pb7WMqcLwkN+KtCbDcexg56k6/Mb6PBxrJTwhL9C25X6CfCvzH6Rj+337/j6jgd2PQmgCaABYCr5PwlgIvk/nvyfYD4nkjJTiV4jsdEE0AIQJf8p8x+Ugb42AzCwsN/+VcCzYdAsCX9n3QB/rY6TboB/gH+Af6HtqvwQEPTW6wCAiFI0W61qfQT4q8T21yKWIw66StEGALWePN896wfNAH+Vw9yvh56TboC/l55SN8C/Mrv5YoA/AcHPX3sDXnHdC7BqydJs+092bMX/94P78NOd20r7CPDXybSdAdDfBtjTX68Bc2DQDPBXOcz9eug56Qb4e+kpdbsM/pTSAP8C21X5uXzteXj7y17BwR8ArjpvA/7odW9GX/LEzAD/in3EzwFqRwRgZVOfAeitm18FPBsGzYrgv2BgAP09vdIVyL31OpbNm4+RyQlMNJoB/gYdJ90A/wB/2gYwA9MC/3qtht6a/pErjShCS3hjYn9Pj9ydjO3JVovro7JteelV12n3zRsYxPUXXIzvPaV+THOhBPi72lb9kkjBfi/RBwA1A11mw6BZ4cz/jksvx+Wrz8E333cvGsgvAly79Cz8zm0vwdef+Bl+sGObrGjjc4bAf6ivD5eslG9BTOXwyBnsPn6M012/bBkWDw5pfe06fhxHmJfHsLobVp2DdWfLt46lRR7ZugXHGd16rY4XX3G1tn4A8N2fPSqds0x9XrZyFfp7hMM98dVotfDYc/ytYcvmzcOaxfxsiJWT42PYfuyodn+Av79UCf+Ll6/AQG/yvXPwB1pRhMeEWwKXDg1jzeIl0MnpiXFs033vmpn/lStX45LhZXjuOfmunKs3bcA3tz4t1eO3fu6FaDWAY8fkuxuWLRvCpx/djGOjo5wfb6HA0uz1zGpZUrDfZNu+aJfCP7nw3s9bOeFGRMJQhdQ1IWuXwT+Vt7/jIxgb42+KePyxHfjAB7+Mi27f4OYQ5QbNdsz8Fw4M4vzaQvzHfzwo7XvDnbdg5YIF2HOcv2/5unPXYlnPkPKtbre+6ApgyZIsABD93nzJFXj9Tbdqq/vuT38sCwAICHpqNfzuK1+vbx+AB7c8gZFWPmCyPl+4bj0+/uGvSjqbNq3HS267XAoA1ixeijs2XoKP/P1/4dQp/va3NWuW47qXXqoNAHTH8eLBIbz8oou19d969Age2buH23bL+g1YaRh8H967B9uOHZH9BvgDAG5adx4+/fG7MDHR4LZfeME5+MVX3SCBd/XChXj5hRvxL5+7B7t3H+b2LVw4jDve+HPqAKAg7f+1r/1Q+ZbFj//PX8Ho8UPAcf414Hsf24mxqUHsPSk/k+G6a/MXGXXiav9O2O5a+PtLJRkBUwZADgC6FP4AsHffUUQR/82NT0zh8JGTuMjN5YyDfyoHnjuOb9z1sLT9yJFTIEN17D1xgtv+qQePoN4i+N4DP5N01qxZjkXJWxLLnOLy1RX1oogq2/bEE7vwo0eewRMH84c5ERAcGN6Nnyx8HPfe9zj38CcgftXydS+91L6+yab+nh7sfeI5/M+//YJU5C/+7M1Yvma+pLdy/gL0jVP86q/9L0nnV3/lZVh4+Vmy3wrhv3b5Cpx71nKtJqv3053bsqcpusr8wSFcufb84oIA9hw9jF3CA4lM8u27H8XICP80xx//eCueemYPnjzI29kztB2PLfwZHvqh/PbPFSsW4443/pzsoMQ5/+0fuwdL6nWICfgd93wLQ5dcjPNe8ypJZ3Cwupl/KlONhr4cgKmmeb/JdnHR7oZ/wZDe1syA/kmAImm6GP5VykyFv0kefkT9ytcDKB6EZwL8TbJn7xHs2XtE2r4fwONV+BU2NRstCUYA8Cd/9lmQGkEj4k9h3Fe7DwCk7BMATE01ZL8Vz/xfeMkVeO0NL7Sy9e7PfgLP7PcLAFYuXoJ3v/JOq7L/vvl+pwBAJYcOn8S3vvljafs+AHI4a5CSF/zdeN5aLOnrU5tefz7ounXS9r7eHbJjVxHUH3jqZ7h6/YXKohGN8NAzT3nbNhftbvgnUjWBrO1pAwBSs3sTYLw7wN/ap4e/T77r93H+ytWF5UYnJvCLf/UHrMPc7zRIOb/th3+VUuY4BoCJCfVz55soesdD59P+VUsnLvjrhH2fGfnSoUGcNTCg3NecN4yJ4WFpe9SqO/vhRFHN+5/8KW657EpcuW49t32q2cSXH/qe9O4NF9v6ogH+AMQUQEd/kcUZgAD/SqTjg+00w386ZLbCv6JKOBYP8K/Svm86/sGdu7FAvDg1kf6+fszbuVPavuwsvywLAG2/TDYa+KN/+WR8ymfZ2XFRSvHTXdtwWvEIaBfb6qIB/qkwv0Tdef0y5/uJqQb6DAAhxjcBxpYD/K19zrK0f9tlGoKh9pgO8PeVboc/ABxdMw9bx8dwYpyH7CUrVqJ3mKJPEQDMG+71c2ZRzZ2HD2DnYYu3D3rYzosG+FtK23+ldqcAVPu7DP5f/Lc/VA5Y9Z46vrvtGa3egoFB9NT1XTkyOYmG5nGb03Wfvyi/8esvR21JP364mx+Mbll/AfqnCP7uvf+3Ml+VS4GvG27YiF94xfPx5cf5e5zXL1uG685dg7/663/FiRMjGtPVwb+npw6Q+PGrrNSSp4Q0GupjpN3w/7cH7sG/b77fSm980v71ueJvaetz+3HnB//SSrfRNJwWsbzP/9JL1+Itv3wbvvQz/ntft2QJrj93LT76sa9i+3YFCD3hv2HDKrz4tiul7S/9nVfh3j3bcPDAfs72bbfchtMnGnjqafmOAwLHN33GFbUoEh7yU6kPW9vTGIwbTgHoq9Vt8P/CIw976REQvOHq6zB1XP2msiVL5uNr257Es4cVr31l4P/FB76LRcPzCv01Ws3Mr2NFAQBXXHEe/u5v3ybtvuzSddh25hi21U9xOpddtQ5rFizGosXyecp161bgsVOKC7V8D3YfPUant7eubNuqlUux8tyl+N7YPk7v/HPOwfM3XoQ//9M3YUIA28KF89QVsqhjrV5Db6/8s/ubv34rhtYuwDe28BdbveHKq9E7RvGWX36fpFM3BJYq8Zn5TzWbaDRblQ5SqkA6ohFGJ+SLI90Ms4v5yl/82Zul50MsXboA6y9Yje9N8LcBnrdyFZ6/8SIMDvbj1Ck+8Bvo5y/WswXN7pMnsOacxbjhDddI+x7YtwP7Tp2UYPHtZ7egp1YDlst+Nh86iRGHYKtq+C8enoeXgK06QgAAIABJREFUX/18XLP+QqSPi9l39Ai++vCDeEbzim1XH4mCs8w6+AMQLwIwFXSxamWQ/TEyzwEg1/7G1av7hnv3SApdBv+y90e/5arr8St3yoM3ALz5TbdieNNSKQCYjpl/jRAM9/VrizVaLUymM69EZ6C3Nx6kNH4nm00+u8EUGezrx2B29bOse2Z8LB+0k92L5/G3yYk+T46O5HARTA719YF9thWrG1GKsakpTq+nVseA5twsAUEzijDB3hpl0e1LhobwykuuEI1lsuXwIWwWsiy3X3ARVi+UX5ubyubdO/HMkcPa/WydO3n866TTT/gb6u2F6o7mtNzoFA/SnloNAz29YsFM0u99Njzet8i2r49PvvPdOGfpMuW+d/3jh7BNeLaCj4/ugT+w66E9d2+9Z9tPUfw2QPGtgOzbANM3AqZvAkz/U/BvBLR7GyAh8imAAP/26gHTl/anND4d4aIz0VDchmahBwDjU5MYn5os1mV2n9A8VbDIF4Ac8CZdZnMzamFkSk67lzmOT4yN49MPP+Sk9+1nt8h+O3TOf7bDHwDGCu5vF6UZRRiZYn4Hiip3M/wvft4aLfwB4OVXX4+PfO3LpXx0E/wBlPmVlf51Wp8C6Dr4+4AY5QbN8Gz/CvQKTc6A49hBT9IN8BcMs4sV+wjwl2TdcsXjuxk5fwX/OPEAfwsp9zsjmmUrMZxEzPNmM2LQDPBXOcz9eug56Qb4e+kpdQP8KzLMLgb429iuzIdVNQL87cTqx1YK9DopPAUwIwbNAH+Vw9yvh56TboC/l56k63sc++oG+Je23RYfGlOV+Gjz1f7iXSqipPsD/B2E0Cp+cSobBAW1M11GrH9N8WwYNGcI/HvqdWy6ar20fcWKxVhzru4Z64UOc78eek66Af5eepJugH+FhtnFAP8i25X4SdQe22l+6+lPtm8N8Hf3UeGvzk20AQCB5tLZ2TBozhD4A0D/QC/e8563SNtvvuky3HGH/h3cBoe5Xw89J90Afy89STfAv0LD7GKAf5HtSvwwas8dP6Z9L8CZ8THc9egPvW3bq8wp+MPzh1fJr9V0EaDz2wDjIgH+rHxv27Mgk/KRsuPYEfzXzx7D4ZEzCi2tw9yviwT4O+lpdQP8rSTA385+ZT46BP9U3vOF/4Mr1q3H9RdszMbOvUcO4zuPP4JJl7suAvwBKFPtRLNcuegDAPHhBLNh0Jxh8AeApw4ewNSo/NCOQ6dP40e7d7k4zP26SIC/k55WN8DfSgL87exX5qPD8E/lsZ3buNMBIe1fsQ9/cfo16y8ChP3bAOMicxf+L9xwAc5byj5+kx80f7RrJ5466PH8bFsJ8LfSKdSdDcexr26Af2nbbfExB+Ff2keAPy/FA2xRRsD7V1z8HIDZMGi2eea/csFCfORvvoyTp+S3Yl29aT3W33a+ewVsJcDfSqdQdzYcx766Af6lbbfFR4B/KdtePmYb/AG0OctvFMMpANuxf27DP5UdOw/i+HH5fP3KlUuwHm0KAAL8rXQKdWfDceyrG+Bf2nZbfAT4l7Lt5WNWwl/MJxul8kjB7SJAqUh3wL+MvPiijehrEPwAD3HbL1h+Nn5p0zW4f9tWHDx9SlYM8LfSKdQN8K9ETycB/nb2K/Mxi+D/Wz//GgwPDGa2v/bIg/jZrh2V+mAKe8l0wx8Uut8j0e6pUEwZAPfXAc9R+DtDmJF1i5fiR/fKt82cOTKC3jMU8/v7Ib0zL8DfSqdQN8C/Ej2dBPjb2a/MxyyCPwDccNElWDycv8DrkW1b8DPYBQBdA/9pFj3kDSPytA+anYa/56BJCMHkZAP/+0Nfkfb96OFn8d3v/lTlMPfr5IxdDPAP8K9GTycB/nb2K/Mxy+BfRroQ/p1NSzNimuUr9037oDmL4O/hMPfroeekG+DvpSfpBvhXaJhdDPAvsl2Jn3DOv1LbVj6EXZasaEuQYHwUsLyhe+H/uc++G8PDA1yZTVetx5/+8Ruq8Rngb6VTqBvgX4meUmiAv639ynwE+Fdm39rH9Kb9bX6tRPj0Fqu3Acaeuhf+R0dHcIY2pC+2RShONCZwZmKinM8AfyudQt0A/0r0lELbmP4N8HeyXYmfAP9KbVv50N8FOG2nAKweBDTtg+Y0p/2/s+VpfAdPY6rZ4rbvPXEcn/j+/eV8zhL410gt6RvCmCKSvvR9c+XZEsJ2wn/XOrt5H2v0023SgyzlugIkL6aoJys1Nh5W9n1aP76Q1E8k/q/tN0k/raNQXtE+aZ3RU9er2C5AkoGLpgWU+pI97XGt12fHR7G++ZpFfQ31o9lWub+1bbLpa5UkmymNMN4Yw3hjBGONEYw3RtFoTap1Ugnwr8y+tY85fsGfSgofBNTt8C/rs6+3B29+063KfZs2rcczODEj4N9fH8SSgWVYMrgci/rPQn99EH31AfT19KOv3o866dF8n+KAC8WAqd7O1ULaJYJLKGQKnHRHrrJe6uNMffwxiNAAKt+nqpeir9Itkjv3evFVcqhXkI5LK2pirHEGR0cP4MDp3ThwZjdOjh+Jd3YJ/DsRXFj7mUb4kxgerj9MInx6iSEDQGoB/uV9PnZwPy66bYPKIcYAHN8/5uiMXSwH/+WDq7B24UU4a3AFhnrm88XZNkVAhFauqYG13IXy9yjVmZ2larbz1lTfkzmI4FVk/0os6+oFQMo2aOqlP44Zu5pAwghr5Yu6ef9WwY3Cv1wdh3oFsZZ6rQfz+xdjfv9irFtyMQBgvDGKg2d247lTu7D92BPaLEGAf8V+ZvbMv60/NrfnAAT4O/t8ZM9ulcPcr5MzdtEP/v31AayZdwHWLrgI83oXZLujVktZ3h4WSiRJs+7MqmsQwRVRwVq5R/JPNNvL1qtdQURSK68gQluvxL9dEMEs6Y4/hX9jcJP4D8LLYO8w1i25GOuWXIxrnncrnjz4EJ44+CMuEOgE/F+04QIM9fZq939/5w6cHGcmLgH+ZW0T4bNjYn4UsH7NoDT34L9ywULMHxjADwQfw3392LBsOY6MjPA/CLPD3K9bRZlFd/jXSA0XLrwSGxZcmpzPJmg1m3nRmTrjzCsn+Vd/5URRDbcgQqpFOKWh3K4+LpKyuu/A3O3cCr/PoV6zTuQ29NX7semcW3DpyufjiQM/xJMHf4jJ1oRC10IcZ/7nDizExz78X8r9L3/ZNZg30JePdwH+5W0XH8JEs1xa7DIAFi71A6qdzFT4A/HbAC9btRqf7/kqppC/73rVokV46/U34BtPPoEHd263cZj7dasoX2dHnXk9C7BpyQuwsG8paETRos2sEAVVXFAm27CaccIVFhXPOE1r2mNkek5pcEsG/8X1kv0rvOvrBcA2G1EYRHCrQs8Zjgv1HtWxQaAaWe2vi3CoV6eEyG0iyTZKKXpr/bhq9c24dOX1+MHOb2D7sSfc7Huk/Scnp/DAA2o/l16yBtiQP97XJJu3PJk/ChjAwZPH7StjYV+vNovgD/UI1CkpDgC6HP6pvPq1f4WRET4Cf/jhZ/Anf/oZ3Hjn1TYOc78uUhL+a4cvwEXzr0Sd1LkZP5AcxMnBmt/fTbORXDkoS2u6mV0bYAHd920P0VKnNNoWRKhVpe9AUa+s5sqq2darc0FEUitlXQUX9vVK/NsFEcyS7rel8G88XhP/pYUkxxhFfAUWqQGgIATorfXjRetfjYHeITx58Ed29qb5nP+Hv/alttpXq80u+ANw4ZCppNcBWP3bAOcg/AFgaqopbYsiioZwa6DGYe7XRUrC//zhjVjVvxaIIM36aRQhohSgFBEFQKMsIBBvtQMM45thsC4Li1JBhNpz5l/DS7W+dxChqZd1EMNvVwK78NhS9ITuJ+txSkO4eY9b1MNaU+NpP6WhO2JUO1ThhjLagNQTgn9CCCilqNVqqNVqIITEn7UaCEm3UVAK3LDmZRjoGcaP992rrGkm7YS/owT4z1yp9m2ADnwrc60A0Fn4l5Jpgv/KwXNxwbzLEUURRqfOYKA2CJBkph9RtKIIrVYLNGqhRaM4IIioaEY/KFs3x2JQNsJCF0QoPeghqtlmCiJy97awIPp6SSX1QYRcTVUQIRdlbnJXGVHUWYao9SkN05rCvzGIKIhOjMGcpK6rl/yFFQURpnrphx9dbfWBHKnlAQCp1VCv1dHT04ue3h7U63XU6nXUSB0gBDVCcMWKGzHQM4gHd92lBlKAf6W2rXyUsa17WIY7qZzJ5h0ABPhbOcz9eug56TLFFvedhasW3QAaxRv7awOYaI6hh/SCRhRR1EKz2UKz2UCr2USz2YyDARqfdzT2k6lNWlhYVFptRuVYq28ek4tnnCZjprQ148LoX7VD2wOq5gobimb9utVss+mUhmaPzfdiGhuUYFRtUx5e5t+vNuDSBVjW9dJGVoX1Kg4wSZwBAFCvxbCv13vQ19+Hvt5+9Pb1oaenB/WeXtRqddB6PCyvW3QJyBqC7+/+Om8wwL9S21Y+Stp25kOFYn8XALcjwN/CYe7XQ89JlylWI3Vcs/SFqJGe+N59SkEA9KAXk1Pj8emARgtTjSk0GlOYmppCs9FAq9VCFEWgNEoMike1ARbWTZS/e0MIYNxkmtkV+fd694ZlcONeL8sgwuQ5cSU9pl8z43Spl4FdBfVqx3GhqJexsEeAaTCl/fYt/XP64uFFCOr1eMbf29eHvr5+DAwMoX9gIP7f3496vQe1qAfNqIHeeh/WLb4Yzx57DIdH9sVGKoL/kiXz8Zfv+WX82Z9/ltv+Cz9/PV7+smvxn9uLL0QM8HeQotmFet23LCduzwHA9MG/1L36ZXSTUWze8ACmphrS/sGBPpXD3K+TM3bRHf4AsHbeBRisDyOKWvyBSylqtI6JxhimJicwNdHAxMQEJicn0Gg00Gw2ELUipFcfW3WYHR10VeX3WM3sSvg3T+I05ouh4GFUo+ETDcsrhVYMUYbzt2jnzDUOknawu7gJQDnDQveV7f98g/uvgYLUaqjV6ujp6UF/fz8GBgYxOTSBwcYwWq0mWq0m+vr6QdHCQP+8+JQBanjBmjvwn0/9E1qRxbVImTcztaYQ4Zw1Z0nbFy2dh8EF/WhFZv0A/wp9qKWyqaxTANCt8B+dmsIn/vm3teXuffYZ1mHu18kZu+gH/zqp44KFl0nFKI0PsVarhajRwuT4JEbGRjA1PomJifEkAGgiasUXAtq88U0JsMoOS72n4lmyWixDGktxs2Rf55I1VKqXgb3Jj58Fs5b/GFHk1TFJYG3XuoQpwE1OA9Rqcfo/nfUPTUygMTWFVqOBVmMYUwOTGB6chyhqoZacCljQvwRXrHoBHt13v1WNbWDzyYd+gNGjo9L2B3fuxOF77yltX1Dwki6Gf6VifQqgW+EPAF974nF8zeb222mEPwCsm78RA8nsP5X4vH6EKGqh1WpiamoKU5OTmBqfwOjYKCYnJtGYmkK93oOFCxciolFyKiAJBLK7BGgSSFDQSPhMy0U0Lyf9R3J6IbkQsS3BQtwxbTOtd1m2QHvda5U63lOWUnyXxswQ+yxZUeBDCEBqNfT09GJiYgz9/QOYmpxEs9lAszGFVtTEPLIQjZ6p+KLAeh21WgyPjcuvxWPP/QCtSL5TiZVwzr8iHxXDv8RzAFxOCyjFEAAQ/dsAZwP8S9yXO1vf6rdBMfsntRpokyBKrvpvNBqYSs79T07EGYBarYZ3/c7v4bzz17vVvYSkWQYaRaDsJ42yYEH8BAWi5FO5HyRbByJQSuLbGylAkWxnApnYTrqcBytxvdjghvELMcgBAMrUPwmGsnJ5EJbZZXxGlObtSfoBQHaLphR0seUVwVUURVz9s/WsbMTUiV+HZn/c/rRvaN4GyvhQfCf5babCp/idR1HeRqZvc72kDlEEPqjk2xC3NQ5EOTuCXton0naq3t5uSX/38R0Ak2j09qLRaKDVii/QpVGEWq2Ger0nvjugpxdRTwRaj7+Pvno/zlt6MbYeeVzro91wDvAv4WMaA9vCDECAv41S+jF98J/ftwiDvcPZM/0zkCUDXtSKsiv+G1MNTE3GFwG2mk38yjve1VH4A3k/k3o93pB+etkCuPOv4pFLmDXhuyJ8IaZ7+bicMIVtfLBng1kfRPzOuWoJ6x3xIT9syMpHvlnrIy+uP48vtVPhQ1nvgu8rO/6ZP+lyNvjSdBdTFmlQl5elNA5OoiToSgOMKGplgUdEoyyAiShNbquNA5yo1UoCJRrfgivYobSFKAL279uH9773bzE5MYFWsxXrUaCnXke9pwc9vX3o748vCOyNWlnQBwAbl1+jDQAC/CvyMbPS/pWEDYa3AUJ+G2CAv0Ip/Zg++APAWQMrFWUJ0qM2ohFaNIpnFcnMotVs4aZbbsPV11znVvcZJDJMREi7Q1OCqMlHsi7EEdmCPqAohibbDvH35AV/U73FdjIdqIM9u871kC38lT7s+tLkIw9+gRzrYDI4YLYxwzGzzM/8CUgNqKMOSoF6PQ0mehM7FIxZtR3K1STPHDFuVq9ejfe9/4P40z/+Q5w4cRIUNJn519Hb14/+gUFMTk5ioNFAX9TKAg4CYNm81VgytBzHxw6DFR84Lx4aQt+QrDfU14ezhufh5PgYmmlmKcC/Ch8mABBhvw/gchAIor3XnzCnAFzdBvjb6TnpGuAPAMsGV0p7AWYQpHGqNWol/2mEVtTCggULnKo+k0SCiXKGzsKfpAt6aJJ8nTA+sl8hIcxyvp7a5HwwmoVgJrJNFfw5H1mT43bm9Y4vKrOGPzGAmakIX+98nbOpA7PJB/Mn7wNNXwo+COMjO5UBBrDpMuVBnZ3KQVJOB23RTgrt1A7NbYIyNqlgJ/VKoYB/fqph7brz8IEPfgjLz16OZrOBycmJ5P84piYnsqxd+htOWwMAa5dsBCu+cH7rNdfjjZuulXZfd+4avPMFN2NFMmYE+Ffgg5bjFrxpGYvdRYAB/gql9GP64Q/EAUDKQDlzk58bT8/ZRq1WfPDVSh0/0yKzKeXPrutmz1Y+xHYzhULKv9qUP1dWAnW2JGUS2GVGJbOptMOkAFLdZWefjY0bL8ahg/ei1Yof2NVoxP+bjQZaTPqfzSAsm7eKseUP52NHT+P3f++TUpF//9L3seyshcDZAf6V+Mg3TdsgXMnLgFIJ8LfTc9K1gP9w7wIM9qRX/+dDOCEAyWYlBOLRTGmUvBp49ogNTNqf8nf34ZTyF+rg7cMh5c+2SQKzwgfhFe3gzwYwzB++XxU2C3xUkfKXQS1nEnI7zKxfZ5NyNVGm/KkQNLD1Th/MlV2/02ohivJt6cWirJw1vCqvn4sIxZutFp47IL+579SpUZw6PQp69kAp+/ZqXQF/UUxgsIGGE5TMjwIO8FcopR8zA/4AMNgzJI7aeXmSJWullCZI/NKR2SJVwaTM+X3WB1sHow8XMBOuFbwPZl38up3aIZZn+5IFP9c8RTulbIJl9oL5Y/Yhfnd8vd1AzUObm3lzy6IdAehc9gAC8NnsgX7Wz2cSBJuJvVYUAQRI7/YAcxdG1onJBYf1ZNtg7zCG+xdgZPIUrKUiyFVtv6vg7wyT6sQUAFhXKsDfTs9J1xL+ANBT6xMLKoQiOw8JIDkfMCsCACMEs12uMJmOlL+DD6Hd3il/CMe6ZTukvlT40AcCuQ/C//H3IbS78yl/wU6FKf/8I29DfIqO5m3LggAItnlZNrzKPgAI8G+LbaMPte0ZGAAYLhBkpRvg/4Lz1mPN0qXa/Y/u3YMthw4q973qxptx1boNWt27Hv0hHtrypFBBuZwpcOit9aqVAIDEr//l2swchGSGBwAh5V/OB9tsnQ+2L6v0oZ71a/pSgL3Jh2vKn13Wg5qHNm+Hzx60I+UvBjDpswhYYYMI3fMJFg8tx87jTyv38caKi5SSAH9r25aTwrYECaUCgG6APwCsXrQI//T+r+L0qTFp31VXnY91L1yrcQZsWHkObtx4qdb2Y7u2STqyGXOdswyAVCzfQGlihx0cAdTKXYHaVml3yp9dV4JZ6UOEl8KHCM12pPxdfAjt9k75i4GUb8pfLK/97ngfZVL+ulv8ZkrKX7QTMef4hTAAJumr9xv3W5goLwH+brb1Q3DbB2evtwHGO7sD/mk9n3lmH44fPyPtXrJ0PtZhrVbPxxe/qdhQb131QqJYjzvuElPsAToTTwG0J+UvQDA37AaoXE1ab1vKX2ynKRBIlF3b4dSXLil/sd2Kdqi/rxIpfyEQUM3C1furT/lbwV8RwESKi/yy44wQbpkV5VjAigXkli9fhG9/82+U+/r7e/GZR39Yyr5arUvhH4srLSoLDLwyAN0G/47oecIfBOip9VpVIDulmK6DzrhTAN4XiwE80ARCmVP+nFV7aBpBLKx36LSCLuBhfbB96QxmU2ZBbKdtQFHgY7pS/mo7ppS/mF2Ql20DmPjNnLLw45hMFul6IFYsIfehB+4DU0N7CfD3sy0P15UBvkjKvQ64C+A/0y74U+nJAYAsVFqJt8ykDMDMTPlb+CiEvWvKX/6tVJbyF9up8GELZvfvSwwEZJs+KX92WQI1RBCLdnxT/omu76y/wGb2el8i1B86oQCIPgPgzPIA/9I+LG0zP4d2g59AqJXTKYAAfzs9L13OjD38AaCWaMR9rNLNhlSk33/aLzMhAJgpKX/WB4vXWZvyd/EhttvQl6wPwv/RBhjuKX9AD2oZmmooM/Bk97M2DaBmigk++Vm/M/zZoIVpKxvAiBf5qcYxKagnmslAgH9bbBt9lLftChAv4FhnAAL87fS8dDkzbvAHknYy4En/cyMwI/HgQnLdaRTXlD+7zoHZAJOQ8ucr4gRmj5Q/60PZN4XwtwWxuN8+5c+Dms0kiHbYTEKBnRIpf9Fe+lKvpHeSBcVvNY3pk13SKYAA/7bYNvpwtu00BhPFsvcgbpr+qV8H3KXw/9Q//Q8MDfFX2F555fn4g9//pWmFf7yppixDwBbPBxlCCNJnmE9nBiD+jhgIJus6+DN7JWgSxqYK/nlQJPggRA1m4gBmQpj65ZXLICj5ENrt6CNV1IKZ8ZH3ZVwHpsszH0xtnHyosx0s/AlnMwtIRB/JSnyrOwNYbpkBdbZM85yWkPIX4U+5ZQHauVlulk655USXW6bcclJUCmCy2hTBn7nPvyVcBMi++S9vqLxJd7uvjQT4V+DDxzY7ILhoVSBurwPuUvifHB/HsmXz5Qi8TjBZjzA6OaXVPXzqBHYcfE7r9tToqFZXV0dR9I/zTfufao+w6QgAnFL+ybrN+eOqUv7iujmbYPZRPuUv+BDb7etDbKfwU60k5W/00b0pfzaYUWcaqPIuAFCm/4pAM43wv/n89VgwMKhVfGj3LhwZke+osrFtI7MK/sXiExxYi/EUQIB/LN/e8hS+veUpTDWb3Pa9J47jo9+/z6j7mXu+ic/c803JF+/eH/7xjKumKMMriHcApHs7HQC4XizG7rVO+TOFbXyoYe+a8u+ED+FIsfWRb9b6UAYXgCLgkW2y68p6F8LfFsTCfm72bA/ttqT8Wd0KApgoiiBNOJTCU4fImywsVDvzP2/RUnz8g/+l3PeiWy7HwqUD+gCgG+FvnzauPBDQBgDcT7SL4W9nwlK3DfAHVO3O+196Ylg2OMXbOxkAeN8yBmihKUFUMVsW14U4IlvQBxTF0GTbIQbO8gzdwoep3kw786KavlT44L4FW/grfdj1pc6H6ir/fNkF1AyIFTalWbhh5p06k+GsBrV21p/ZsbMp2pEzAMQyIHAbx9qR9m+1IjzwwBPKfWvOXY75S5d52zbJrIQ/yrFHMuUolb4NMC4e4G/y5axbAH9AbLvJpnykduo5AJXcMqaBfW7CDPvMqhBc6IONAjCb6i22kwU/51qEdHE72pHyt/YhttvBh+pFPqoZc7bmMGPm9rM2NYDtfMqf2S8sqwIY5SkARqq4eLdT5/w7YXu2wn+6xRwABPhXI22EPwCQ7BoAXf+ngIjfC5BeVAXQtj8KeDal/Nl13ey5rI+Q8hdnz/EfbbrcOuUv7OdAbZdJKIK/bcqf9WkbwIh20gCACH3nPcAKYgP/17/gVvT15Ii457FH8dzxo5X4FypTUn2Ww78chEodEJW8DTArPkfhv3zefAz39+MBwddgby/WLV2KY6OjOD0xodR93rLlWDJ/gco9AGD/saM4elrx9i5L+Ou2sXsp4pcBiWcDgPaeAuiKF/mw9sv4cEj5sz51sGfXCa9oB3/GsD7gUdgs8OH6VD87UPPLehDzdlTZA86OFtTF2YPioES0wwce7G2ATC0AxN+N8+yds2Kn+/oX3IrhgYFs/em9e6oPALod/vZSTeQnSOm3AQJzG/4AcMuGC3DZqtX4Qs/XMIVGtn31osV42/U34q6nn8TmnTuUvu68+TbcfuW1WtufuOs/8eXN31PWUTan3sHeTifskDaJx227TgHMqJS/UAejDxOYTfUWfTDrwma3dojl2b5kgwuueYp2StmE6Un5s8t2M2YxRS6ny7nZcxFgM/MytL1T/kqb4n63axbEUwDx7a2E11f8vovGxDKBQ+US4A9A/ml2UqyeA2CSuQ7/VO/ON/wtRkcnuc2PPPIs/uI9nzP6chZH+ANADTWQgq8hPpCZgSfVrTgAIEQNrCxESWCTwYSBD+FgQnjAFV1AqAAayU0BKh8M/Enqgz1GSpzvz9qdNZzxkXuX4E/EduQVdLumQOxLpqKSD7ZHRB8C/InWB3v88d8XTc45UcjQpsk/Fpr87JmBKrec7ocC/lkBDbTt4J/f56+YpbPp/9QOTatPGeDHbRDhL/aF2C+RKl2nEduSnYL/4GAf3v/eX5O2v/i2K/Ga17wgrUwpmSvw9zRZWcDg/TbAuEB3wB8ARkYnpCvqW60I4xNTWh1n8YA/AOYagHwQ1+oIg16VAUBXpPyFOlTpg222Tcq/Sh8zJeXPLqtm12VS/iy0GU/OKX8xgMnrKNoxZQ+PEeg0AAAgAElEQVTE/XxQEkWqUwD+46bPBX9TzQb6mjkiImq+MDH1M9Fs4pzzlkv75i8axvCiQbR2Fdsp8mHYWYl0Dv4UqBDoBiFQtMD7FEA3wb/tOga9wjorYc/P6Eg2owGXSgT44KGMzMwX+YiBgMKHCM2C0wpeKX8XH0K7vVP+YiDVgZQ/66NMyr8oXa4GcVZAA3y7Wb8Z2gUpf9YO2wbWjmW/6N4G6CO+V/vf+b73ePn5zMMPYWpEnhj9eO8evO+eu93qovGh2VmJdBj+JmaUDQwK9U0XAZpfB9xF8LfWVRSz0iwBf4DpF5E8AACaXSMQF0tGq2RAKpsBMEKQqZMrTLyfuMfazNWkdfmCOQcfQrsL0/Fg6sStC8e0ZTuc+lI695/rEP6Pczt0wYbrc/DVAC0zuy6GthP8HQMYp2sWNH3BXwOQdDaN7ZnGQXHM8IW/q3Ti9MKchD/koa96b3pxPgXgC/8yQQMwW+FvoVsS/gDA3QaotBuvUMoOfLGUCQCmJ+XPWbWHpvidmsDcodMK3K9ec1qB7UtnMDun/C0CigIf7U/5i3Zkm+oMgMKOAtRyvTIvJYMScb9gR/CvT7fb06gs/F91w03o68nfLnj/Ez/FwRPHy/vxkLkK/3jRiU+VBghOpwC6Gf59fT3o7ZW7q7enrjHj32AX+ANp/xDlvnz2wAx2oAkBqHcAEFL+OfzlbIKDD6HdNul41ocVmA3tcOpLKZswW1L+ia7LrF+oV6FN65S/OoAR26B7EFD8LA+gaECtYub/llteyt0GuP3Ac1IAEOBfwk85+8yg7y9urwPuUviPNxr47Od/X6t2z9Ytgplcd3RiAicML76YmOLPlbnCH4jvAlCXjQvzQxN/1LkGACHlz/uQ2mkKBBLl0j7EdhshnesQ/o82wHBP+QN6UMvQVEOZgT+7n7VpADVTTPDJw9cZ/sKMneZOygcwgh1WNwsAjOMfVS5Pd9p/0eAQmlGvtH2wpxdnDc/D6YlxTCmfc2DvI9lZicwC+LtC0Lq8KQBgrHUe/t7XClQMfwD46hOP46tPPG7U0el+7Btfwce+8ZVCPZWujT8A8SkAaZ/OFskOPkKIUwDgerEYu86B2QCTkPLnK+IEZlNmQWynwoeybwrhr5jpJn+qSvnzoJZtqkEs29Gfm+frXRTAqIMJRzvavkgyAMJFgPF3L/+mS3OqYvgDwBs2XYPeBsF/4Fvc9itWn4N33XwL/uWRH2LbkSOlfMwp+NvDygeKROExk8IMQLfD30bHSbdq+Cf/iiQ+AAkzy4o/bfva+/wxoIdm6ZR/vqANKAphz/uYEyl/Fx9iuw196ZPyZ5fLzJjZspIdJ1DPjJS/2me+XPQugEqkAHQ7Dx/AUF9/tj42OZGomRXHxyfxrnf9o7T9rm8+jLPPXgysH1BoiVXrEvhDg5J8s80A7QlMUwBAUWcfjOIiAf72ela6BXo1Iry2oaAP2dlWUQbAKeWfrNucP3ZN+bNQ5PCqDTbcfZRP+Qs+xHb7+hDbKfy8ip8ZYA4wbC7869aUf2azZAAj94XZDmUuApQCZcN4YT1+WoDudz/1UYVasWIUUezff0zafubMOE6cOIP5MAcA3QT/RLwBXtaW+XXAswj+npVlFmcn/NllkizzwQCSAy+bu3F2TAGA68yRqwtfSAuTuZbyZ32I34OVD6Zi5VP+sk12XVnvQvjbgljYz82e7aFdJuXP2eEugmV0KwhgnO1QHj+q0yhRpKNF9oP2F091q2sLSlet6+AP+HJPsGJdkBBCk4PO6zkAepUAf1s9K11Lvfw5ADwACKA+8JhZiC4AsDl/3NaUf7IuxBHZgjagKIQ93w6uL4V1a/jPypS/wqbQzqKUf77sAmp5xswuc7PwSlL+pqBl5qT8RTtRFCl+51ALzVSLJcC/bba1fort2wCokihBlEpeBgQE+LvoWek66MWxGhE3JgMfyYMBUKmcKgCo5PyxS8pfLC9AkSupDTYKwGyqt9hOFvyca/4Yt2lHO1L+1j7Edlv7sE/5Z2sOM2ZuP2tTAKV6WQQ1D19dIKCFP9sG1iYTJHtfs+Bqh6m3dA2AOEbykYOdBPi3zbbSj63tcmgvpV3qXQB5wdkHfxfdX7rqapwzuAAtRVquv68H39+7A5t37VT6+rXrX4Ahqn5WwNBgH7705GPYdvSIpKeuunqH/o1+hPtIF+Muj6cN3HsEHGHP7rVO+TOF7QMKhQ9mvTgd7+cjpPzF2XP8RzsL72DKv9COZcqf9WkbwFj1BWvHIuUvtkEMAChlI4nku1cCRjN4BPi3zbbSj4Ntf4qVl9IZgLkO/7Tom3/5fTh+XL6f/6abLsWLfvl6rd7Y2ARe//r3Kk2/+U23YsG1wkszHOEv7VMUYwdF8bhMMwBd8SIf8bjx9VE65S//bqYr5W/y4XqLnx2o+WXnGTPlaqKYPYugVtRbuSzaschIsPV27JeiaxaiKMqzc2WZ3mH4Dw3149vf/Btl8b6+Xnzx8R/b+egC+CdSGdBdbZquASg00C3w9/VVlZ6xzgSQX+hDIIz0ycEZb2AHy1qt1pZbxrxT/kId9JAuALOp3qIPZl3Y7NyO8il/waaLD7HdWh9iIMC30w3UPLSV99qzgM3szKSUP7NfWHa6ZsEmgGF12XqzdijNyvPfUbaU/JXHBWlLh+EPAJ/c/APjENjKvpcAfwD8oNZhmV1vAwzwV+rw34WufLqdH0Br9Vq+twD27N6ZkvIX113hP5tS/qwPHezZdWW9C76v9qT8hf0cqOUZM7vMqBjh3+mUP98vgh2PlD9bL9UzAORftT+gi9XK2W5ZPMMgwJ+TaYsAvAKAAP/O6NnAH4ifA+BWj3xgq9XqWpjM+pS/UAdvHw4pfzWk/dqh89ENKX/+in+FHS2oFfVWLot2TNkDcb9g06JfbOEPyABlgxudZCXS42Sa4F/aR/fBnxUbgpjKOBNIFQAQ4VPY2V3wt9adJvgDQM30lRH+2EzHkvS7qNdrPOAcYKKDPbuuhI3gQ53yV/hwAbNwzLQl5S+WZ8HMgp9rXnE7O5HyZ31UnvJX2hGAzmUPIABfhrZ3yl9pU7DDgpq1UyKA8T51QCloZPec/FyocdXeSoC/l5+StsVEH7urnOVi6cjbAIHZD//z1q3AyZOjXHpuaKgfq1Yu0XA33lir1bBu3Qrs3HmQ279o4TCWnbUQk8pq2MMfiPuWJBCRdrMHZzo5oMgGOcI+RdATJrP2RT6m4EJaF45hy3Y49aVlyr/Qh6IdumCj8yl/wU6FKf/8owMBjAhtQLLjMutn7ehT6KRwHPUlhgqaf/fW38RQX/7Uvo/f9RU8vXe3p4cA/4qFoIIAwXSev5K3AQKzH/4A8OEPvR1DQ33ctssuW4ff/q1XKkzluoODffjwh94ulbn99qvxutfdZNQtqmO+uSYVI6q+ozQf/JPPeq2WHUqEgQlh6pIHFmKQQYQZMBHAzFoB/32aoJmbkYAWrzv4KAH/7BdG5Haq2pH2O2NVBrMl/FkfJDPM+1B+X2y9VT5U8M8CQgH+FBzkkl0xqBTwp9kyDzkeeDLwOYhS1g4DYirDn6bLqR1FACPCn3LLgp1sOS+b22H7RQN/5uI9X/gDQCS8Kc9rDHQQHZgvXHUuLjon/z9vYLByH8nOSmT2wr8QkMKoXZ1YPQeg2+H/5MEDODo6gqbwhq7jY6O4b9uz2HvypFb30X17QCblI2XfqZO4b/tWHB8b0+qa6sjtqtUUB2MOSUIgzJDyz/giwPak/LP15I85EFD4EMHcjpS/iw+h3X7peLmd3il/sbwhoKgq5V+U5tbOmOd4yt98vt+c8hf7RZUByI87WjCmuo2ZVmn/khLgXyDtje+M0ra3AQJzA/4A8NTBA3jq4AE0IzkAuHfbs0bdR/ftxeTIlLR938kTuK9AV1dHeTdHNFmBGai5r4RS4SJAFUzEQECAIGPUHlBEqAu/3raUv9hOUyCQKJf2IbbbCOlch/B/zAEGV2/x+yqR8lfMnkU73H7WpgHU7LIK2t7wZwGbFakogBHsFL5amK032z6hDar2i68CVgphGuwpAf6eftrfbR0TYwAQ4O9iqk26FvAnRJUBEHXjFUppkl6OJTsFwNTD/yp/1gqs4e90Ll6aoVfnQwdm3Z0ELIidwWzKLIjtVPhQ9k2BD9er/NllPaj10DYDTwFYxk4hYJk/pgDGLoBQ1Ju1o+0L06zfoy+SndpXAQunohgV59EnwN/TT3u6zfT1+YPFQip/GyAQ4F+ZrgX8s09Cs61KQ4Q/h0qTYYPUiBoeBtiz6yxssnVlIGDhoxD2ril/3qazD6HdNil/1geHdFv4m3yI7TbY9En5s8ulZ8zWKf9EVwFNM7TbkfJX94Xapxn+WjsW/cIFAMyxwT4gSN5tTyYr+JcEXYC/mwcLaUsgUOnbAIEAfxt/VrqW8Afi5wBEtAWmM/Jy4qyB8vuyZwhoIW0zO+UBpZypKtbN2QRLCGZFRZtFKX/BR7Je2ofYTkNfsj4I/0cbYLin/AE9qGVoqqHMwJ/dz9o0gJopJvjUz5it4M8ClrVZRQDjaoett1AXqd4FfSFeA0CSC0BNkgfk5nIB/p5+2gT/xL7qCzZ9kS5ljWKCvLPRuQr/K1efg5dffAl66nx3LRueh5dtvBhrlyzV6j5/7Trcuv4CafvaJUvxsosuxlnD8wrrKO9WFdBFGfF2StM/QHw0x7P/tIgeJiwEiRWYVfBnfYhX+bPZhBhwrA+i9JEt68DM/KREaKp8sKdFpKv8mXaWuspf6EsV/DMfAvwJ5HYUw58iBxK7rIA/1cCfivCn+X4J/lQJ6nw5h5wJ/twV/6wdmi+ndcmAn65zbZChnf8MRDtMX7Dwpxo7Wb8ogpZC+Od9IQdCFFHRcwA8h9kAf08/7YR/xv/pkcoCgLkKfwDYsGw5hk8TEPENnQ1geWsAKxYs0PrbuHwFek/KP+jaRITn9c7HosFBScckYp3j9wBwVOBKZ8n+hMBp6h+UgtRqEjQ5CDL2eEhDAWYBmsyC9bl4kgMt95EDUr79Lqd8GR9st4np+NQHD2l1gMHURoa/wQfXl6wPsZ2CDyL6SFZyOPGBnwQnysMpB15qR4Y2D1C+LGOWBzW3nOhyy4IdpKCmjJ0M9+oAhgta+ABCebujsi+ySEIN7YJTB8r0P9sGZb/kAUxar/QiQELyugPp1f8k860UzfgR4O/pp+3wh/iducKQCJ9OYnqPrPUpgLkM/7TYH/7xpzE+wV/N//TTe/APn7zLqDcxMYU/+uP/I+2+777H8ZX/3Gyso2xSLkByPPB7td8JyQ7uer2elTWl43VP9SN5AQbSMrDS2kk+RDArbPIQzJuWzZ6zAIMBc7pm8sG0I91ofKpf5gOMTcLBP+0ACcw2Ppg/eR8w7Vb4yGqhON8vznRFONFsOUO8cqbLwSlZ5wHKQxss8CRQM/AFFMuCHWY5862yyQBfStUL0BYDmNyUJoABhH7J6y3aoWkblP0iBEJMACEGMDbP0s+lmFAB/p5+OgH/WDwBWl7Kvw64C+DvLD56HvAHktf5smAFACqbS2dZ+aBE4/P/3OxUnD1DBjMEoKkAlaspbApQFtc74kPoTVsf+WatDyX4IYBfbKfCh7Leog+h3e15qh8/02XLFqe5ZZu6lD9TlAe+qQ0MtPlgwrEvWDuUx0wVD/Yx9ylvk4KqXwZkOc6K40SAv6efzsFfNbKbvuxKgwWrBwFpCwT4V6PnCX9lP7KgoTSdSCKbKdZIdgDWDDBxfw1uvqAPKIqhmfsT2uYLf1O9xXYyNNbBnl3nesgW/kofdn1p8lHFi3wqf/Ut5WrCzZ6leucFLAAq2uFT/qId5kMGsVW/iG0QfTr2r7Jf8v262wCLhxYqrJWD/xcfuAe9Pb3Z+v5jRxXqAf5O9lW2vWBYjRgCAOkl8/zeAP9q9ErAHyj4HpJdlHXDHIC1Wl2yYQN75UxVsW4NZiUE1dBk182QLm6Hzodx1s+1U7Dp4kNst9aHGAjw35cbqItnzNz+zI4FYDNd3UxX9Gm2KbWBrVunAhhWl603a6eSvuDtiI8CZiW/LiAO6Nm6sL/tKmb+X3jguwXqAf5O9rW2p43/fhmAAH97vTJfbRH848Uas01VPu089uiLD8b4IkAD/E1g1sKeX9fNnq18ZHYUYDbZVAQbPKTt2qELMJRgnjMpf2F/wUyXXWZUjMCzTfmzPm0DGLUdtl8EOx1P+RvsZL6LrgFgDwLGJ2FsFklJ0NnA/9U33sy9P+B7Tz6OXYcOlPMx5+APwB4TRLPsYwuA8UFA6msAug3+qe7NN12KkZFxaf/GjecazBH09NTx4tuuVO7fsGEVDkMd7dvAH0ByF4CwQ9dcSpEeiYSQ+PqBzI4hEEgWqjoXz0KT9aF74p6TD4eUP9smCcwKH4RXtIM/G8Awf/h+Vdgs8FFFyl+GE7+sB7HGJuVq4pjyV0OzOCiR7UiAdeyXqlL+rE+5X4RAiGlDi3kUcHYoZbd4cMYVYjHWdQD+APDqG16Isxctztb3HjlsHQB0EfwdkV2RZiJurwPuUvjvOHYUt955vVb1yYPP6XVPHsMrfu0Wra+te3ZpdW3qWGPvR1Mo8INaPNCky9lTAG1T/llRMRBIyviCWTg+bFL+8faioMUSzCz4ueYp2jkNKX/Wh3/KXw0ctR0B6Fz2AALwZWh7p/yVNgU7LKhZOyUCGO9TB0K/yM9L0PWFKRCiaOmeA6Adyhzo1SH4V+5jrsIf8s9dXaQ9Yn0KoFvhD8Qv9Hl0314rPVH3Bzt3WOmodF19Cd9YvsgNVPm5w/QugKy0cnaqABR4aLLrhefiTT6yogowm4ILaV04Xi3bIYFZ4UMfCOQ+CP/H34fQ7s6n/AU7Fab8849yAYyqL6QARoA2OybPhJS/aFP5MiABJKrHAgMFw0uAv+xnmuFfIL7gt9azygB0M/xd9Ap1K4Y/AOYUgKBgnC3ER2Z6CkCCdGbCApouYG5Hyr8AzNYv8qnIh/FOArGdAuxNPjrxIh/eDg/fdqT8jQGMdVAi7hdArIX2zEn5i32hfRkQAPiOxdMA/7HJCYxOTGTrDcPFjVof3QB/X8xXIFkAQGTK15LtfpYD/K10CnUt9PivSKMgHpCUolaryWAmQm2UgUBSxgTNdqT8XXzAAGYW/IwBm3Z6p/zF8tpAgPdRJuVflOZWgzgroAG+ANi8OOyhPb0pfzP8y6X81QGMORAS+zSi8rsApN+aTlTj9TTAHwB+4+/fV85HN8AfQEFU19bwwHgKIMDfTq9Qt03wB4AaqWdF5WAgtZNvpDQfsOOHCM3QlH++WeFDCAQSZdd2mOEvgNkl5S+2W9EONfxLpPyFQEAFH/X+6lP+VvBnAZsV4aHtfc2CtR1FG9j2CW3QBy36vnDOzlD1KQBKqd+YOk3wL+2ja+BvLUSzrCtjJfq7ANS55WIJ8LfSKdS1qg5R6hNwPFIOhADyDEBStt0p/yp96MBsnfJn1kul/MV22gYUBT6mK+WvtmNK+VsAlvljCmDKp/xNfTEzUv6sfykQSuwYTwEwYoQzY9tXysL/F667EcPMbYAPPv0E9hw5VOwjwL9jUvpRwJwE+FvpFOpawh9gYKTJ1mQhAqFZKpEmPwdCahm1eEjnC7rZafUpf96msw+2LwQfhPkjuM582ILZ/RY/MRCQbfqk/NllNeRMwPFN+Se6Cmiaod2OlL9HAKO0Y5/yt+8XMfixDYTiZdO7ALjfCiMUlN8yzfAHgNffdBt3G+ChE8e5ACDAPxEvaFYjpR4FLJYO8C/WKdR1gD+QXMkvd0jyRzRGOe34IsAciixeZ23K38WH2G7erfZOAsL/0QYY7il/QA9qGThF8OH2szYNoGaKKeHUzpS/dwBjbUfRBrZ92gDCvi98Uv5smBGfomOeA+A8DJPSoOvrqeuv/aLAVLOJiJZzEuCfS0n6qwZ6a6kmAxDgb6VTqOsI/8J6AMiPzqQczQer/CJAPfxDyp/3oQ54eB/KvimEvy2Ixf32Kf8iUKkBWmCn4pS/jx22X7zaYNkXRf3L2WH7hbUjlFUFMOw1ABRIhngC6KBcoVBQ/Obzb8Lo8THl/sWL5+GLTzyKvSdPlPKh2Fi5zAb4J+LyxVZ6EJQPALoI/rdfdS2et3R5tv6THVvxkx1bC31Z+fWAPwAQ5ml+HHlZUUXrlKJWIxKwdICyScfn+4S6CuvW8J8rKX+hnUUp/3xZhBMPCp8ZM1tWsuME6tma8jfYqSDlL9nxCITyawDUg4L3xdkFktbt2LHTeNub368s8/b/fgdwXr+Vvd2HD2J0In966pnx8QB/lbQvriu0XO4UQBfBHwBeeMmVuGb9hdn6VLPJBwAdhr+6/7lppyTxDyM+cmv1mgJYyV8WmoUz4KpT/oKPZL20D7GdvNtqUv5GH92b8s9sVhHAuNph6y3UpWxfVJHyF9vQKrhfvh1iPOfvKX/8L58s9tHt8AegGaxVG0nBfmfxzwB0GfwNmy12Ar21nnjGnUgriuKLfUrAHwBzIZ+hEvGVf9lgEz8JkOYPAmKhqZgt55aFdasZsALMTj7EC+YsfTDdUT7lL9tk15X1LoS/LYiF/SzQSoJKBdBCO8bUdrkAxtkO5ZHS/qf68TarSvmLAYzuZUDpWwBNuPbJDrQD/lY+AvxTIcJnx8QvAOhK+Bt0C8wSEPzFG9+Gq8+/INv2+fvvxufuv9uiOnr48/uJtE+5IQsECAiplToXn+0TjwdhvXMpf94mu84h3Rb+Sh9iwKOwWeDDBCr71DYDHMmOx4xZCWcTqPllPUD1NlV29H1h2y9iX4g+Hfu3IGiR+yXz4tQXrJ0okglDAKjZTjXLdhLg72i/DbYrOqPjZcX9FEAXw//M+BhOjJzJtk9MTVrB31eK4A/EdwEQebNGaP49EMLdQWANZiUENdBUzJZF+0ofyXppH5luvsD1k60Psd0OPtiAwi21TTXQ0MCJtVkE2EyXtaOHZpFNqQ1s3RwCGLt+sbDD1pu1U0FftCPlL7YhilqduN4vwN/Vfvu6q+jb1u0vfZS4ZQC6GP4A8Hdf+lcrHSe/LrratuWg4oKBZIFSKgxqFKAU9Tp/CkA3e85MmeDPQpCxCYSUP+A4S7RO+Qv7C0ClAmgh8CxT/qxP2wDGqi9YO3M05c9sBkClBwEVXTvkwyUT/Pt6e7Bx47l4+uk93PYVK5bg7LMXYzfUdwhY+QjwVzloh1jBx3Sen4hr3Qx/Wx0nvy66ujqmD2xU9FkWEJCYTFQ40Gq1mhL+hFnPAgqSbY23E4LS8CeyDxHMvI+8Dmy9CRP1SPV2gX9mmIU/yfpShL/Rhwr+NPvDA48iB0QCh2xZAX+aLdMMGlQBPM4OUxcmBpSAx74umgU1pZSDXBZSZssy8EX4U8Fm1hc0t8O1lWrgT2lF8GcDY6YNKvhTxg7XL4xNTV/w/SLDP++XeJ8YAEhv/SP8cSvtLBDjzJ8Ci5fMx5/9yRulXXf8t2tx+0s2FdrX+gjwVzkALQOLkmKXAegy+Ov0br18E1YtOSvTeXzXdjy+a7ufX2117OEPaAYCNu0MmgcCia10PKnX0/cIKMDMlOdqJRwLbKqcXRc2m+8kSNaNYFYFF4wBORiRbU5Xyp9dtp0lOj0HXwCcelkB2LwIZ6fIJlNMsCnYKZjp2vVLRXa8+0IVtJj7ojAoMX6fpicBludEEfwB4L6tz+LMsRFp99Yjh3HX00/ixLg5AxDgb+1A932ovmhSsN9LigOAAP9Mbrt8E65mbgP8HCAFAJ2Ef7xbfA6A1IkA4oEmnWkByYFHiABmMRAQamWCJgtmFvyiTWldCGJYHyxoRR+MAdeUP+uD8H+UPtT1FuE/81L+7LIeVIIdRWo7/6g+5c/3i2Cn4yl/g502pvzFvqBMAJD+NuRAnypXTcOFDfwB4NF9ezB6TIb8vpMnsHnnDoOHAH8HB1kb1CO2ape3EGhaow8ASDquBvibdKz0rarjDn9AHBiIcjF9SjjJbkOMj4V6rcYUlaHJmtE9cY8pClv468BcfB9+tT46epV/8scf1LYgFu0oAMva0YJaD237oERjh623oV/UABXboLBpYYf1KfeLCGpTAGOCv33/svVWvQzIyCKuwzVFLOFfRgL8rR3wfWUGT1VBgFJMAUAtwN+sY6XbRvgDybsAjGVJHv9laYB4e/wgIEiwt02Vs+vCZhnMnUj5C8FBJ1L+rI8yKX/XWWKmy2UPGDsSqPXQNEPbM+XP2nHqF9GnCf6WKX8wfVrUBqasFtSGZdusiqre6bIUAAiBMft9yKCSB4MAf0f7nYT/NIv+dcDweB3wHIf/niOHuddbHj19alrhHydp0lv5uGmw0RBNgoFarc6DWTkD9gCzcYaun+lzoC3yIZY3zMhZH4T/439aQQg2vK9s5wBaBCczqNllFbSd4O8YwDhds6DtC7WdaUn5s32hs6lpg08wx/rXvQ6YZIE8GGdmCfB3tD/z4e8xK9dLpW8DjD/mJvwB4B++9V/2uh2APwDUTFcDs5xNnv7HHn41Dv7iDN0SzEbYm8Hc8Rf5EE7LPqAo8NH+lL9oxwQ8U8pfA1ipXpmXkkGJuF+wo+2LmZHyZ/2rQC3D3/GUiqZfqBQAxK/yTouxR5+IEy4o9YT/SzdejDNHRrAZP+S2bzx7Be64/Erct20rjo+N6n0E+KscmL6PSqHuIqZZvtPbAOOPuQt/J12rppSHPwDtbYCcUBH9sdRq9Qz+BDkU2dvvSBYI8PAnRfBnbvFLFU1gTn2wM/TMhwB/pjZ6H8QG/nk7wdRBsqnxEZ9R4aFN02UBOPFysj9RyCGXfz+SnTRsS+1Q1iZjh6Z2kO1PIZPZYVIN5ivbLeFPDdmDpBMkUGf94gP/pG4KEHO3KSr7RQa1CH8qLWe9n9U7t5O3T+yX+FZKuX3scSG1geZllXcBOMKpzLNZMHQAACAASURBVMx//eJlGN17Rto+eWISSzGA4b4+vY8Af5UD8/dR7qlPpZQreRtg/BHgb9L7/P134+s/3pzp7jt62Nqf1jeJA4AUiNyMNZX0uCMkG0TjyUT6NsD2p/wr8ZHp5gu64IL1Qfg/AvzV7VD6EAIec2pbhqYSoMiBw+1nbaoAKy2LPlloij7NNhnmCnbYNjBl2TYU9otsRz/zVrSBbZ/QBqaYdV+USvmzNkum/MV+kTMAuWTHYHY+IJd4jZSCPwCcPDmCD/yvL0vbv3PPT7BhwyrgvP4Af3sH5u8jFlt4EsflQin9NsD4I8C/SO/pfbv1up7wB/hZblomHhuEg05cR/IgIMbenEv5M5XX+WBtan1I8C8AsQtUsiJuoFKDWLZTCFjmjzqY4OvCLzvamY6Uv3O/8G1gfYqgNttx6xe23trnABhnigWQqRByAf7WDmzg70bsik2Vehtg/BHgX6Rn1LVqhtRxmdS4fWIwkPzhBux8EIwvAtTPyPN1HsTiugTaZF0LZosZOeuDQ7ot/E0+MlNiwJOU4Y1m+9Qp6WyTACceFN6zRBYkrB0nUFvM+m1ssm1g7agAq+gLtc+CAEZnx7tfRDg7gFrXp+z3KfWL/XGRnSpgnwOA+DhlQSI9GbBI2gm5NtnvFvgDSAeiCuMAwbqhpd5vA4w/AvyL9Iy6JeEPIL8GQAA0AG7QSw8BFnTxbYAaaFrMyLPNnrcReqf8AUU2QYa9uO7mo3tT/plNDajKpPwL7bD1FupSti86n/I3wV/fL+ldALoJv9MrfwP81fZnCvzLSemgwettgPFHgH+RnlG3Avib7McP/RH30XwTpajX6pldFuHeT/UznIs3+mDaVj7lL9tk15X1LoS/LYiF/SzQSoJKDVDGk8qOYmaqBrUclLDL3il/1g7lh8S5+iIf235RHheJzYjqrwFgj65Y+N849+uYZvhfu+Ei9PX0ZutbD+zD4ZMnCkwG+CukNOh14vw2wPgjwL9Iz6hbEfwBxBkA5YwgRVtMNgrKwDI+QEktBZ84Q080TfCflSl/hU2hnUUp/3zZBdQyKNhlDj6VpPxFUPPLeoDqbXJ2CvvCtl9kO/bwd0/5y/2Seammfw19oWq/ri+ilu45AIQ9os2YKcGgwcF+vPIVNyj3XbBhNR5rHbGy//uveSMWz5ufrb/vy/+Gbz36I235roW/OqVTBvrWuvYBQIC/tZ5Rt0L4x7tq4gatAqXgBrH4IkD7GXlu3gB/ZUBh4SPTzRe4VrT9qX72Kf9sjQFFIXzY/axNFWClZRFOemgW2ZTawNbNIYCx6xcLO2y9WTsV9MVsSfmLNlUZAEII1Of+hW1E3uQqz5w8jBtefZVy3xSA07snyjlQSNfC316IZrmU2J0CCPC31jPqVgx/gAkeVe1Pnv2f3iMt6tWSUwAh5W9K+cd/1PABDxUWaKqyWlDLoFKBohB4lil/1qcMp5DytwU136e2/cKDQnm7Y+QOEgqAVASgB7ZvS2uidlSxdDv8xbmOvxl3KQ4AAvyt9Yy6bYA/kLwLQFRS6THpf5ocsLVarYMpf94mu84h3Rb+pll/bkq2WeDD9al+dqDml/Xw4e2osgecHS2oFfVWLot2LDISbL0d+6WSlH9hv4ig5vuiCNTup1TEegvt8zgujO8C4DbL253GaYME+Fs7KAX/6RbzKYAAf2s9o26b4A8gOS8oRGuMUFAQUuPThwks6/Va7pkFs+le+mSdB60GzKZZf6arsOnig/lj9iEGAnw73UDNQ1t5LzgLlczO7E352/VLsZ12pvzNdmxBbNEGRfuL+sLFZhS1oBKC5Jgt9+S4Qgnwt3ZQDfzdorZKv3zz64DhWLcAf0luuexKXLj6eVq9B7c8iSd273TyxUoN+W2AaSAggpRSmp1DpBTZSFer1Wd8yp/1oYM9u66stwT/mZfyZ5f1oBLsdDjlz/eLYKfjKX+DnQ6n/I02BUgoU/6CjUhzCoDdWlW6X/YR4G/poBr4m0UazqsWYwYgwL9Yz6hLgE3nXYAXX3G1VvfQyZN8AODU5SSZxXK0yxYJzTdRhe1ajUOiBHtOpfCpfiKkE6sWAYaNj25I+bOg4sKM1I4W1Ip6K5dFO6bsgbhfsGnRL1Wl/Fmfcr+IoFa0wdAX6n6x6F9lv2jqrewLfSYhokwGQPwdtlEC/K0dVA3/Tny9SjG9Dti+UgH+znpldXLg1jUFOATGpfkRDrVaLSnKl/U+32+a9Sfrull4J1L+Pk/1k0GhBo7ajgB0LnsAAfgytL1T/kqbgh0W1Kwdp34R7PieOhD6hT+NYmgDU9a2LwqDEsvsQVFflDmN4H4RIFtePYjcuPFSvO7nXpRlIymAx3dtwxcfuBc/3bE1wN/eQdXwn1ap7GVA+WqAf6fgDyQwlnS5/HacCgByKCWr9VotgzkLTdaCbqbPgRYu8DfPyNXZBAsfpsyCEGx0PuUv2Kkw5Z9/dCCAEaDNDoMh5Z8fT7Ypf24/U0i6CDAR9lZAPYLkPauWnIW/eMPbpIsGr1l/ES5fux6vf++f48z4WJGZ0hLgrxF5rq2jAbEo4yTlXgcc4O+sV1aH80sg/ai1iRvKFonL1Op1xn0OzTQLwM/0iRLmhHOpAbMl/MXXECd/OB+E8UEYH1m9VT5U8KfZH37gpmAGcwHUCvjTbJmHEw8qGfgiqKhgM7Ynw5+my6kdCdRUghPllgU72XJeNrfD9osG/pSiGvhnHrnvIrcp9IWyX/g+ZX3K8E/6Fyo7/z97bx7vyVXWCX+rfne/vSek0521QxISkkCCgbAEAwRwxZEPOCIq6sjg6PuKjjKvOh+XcUdEX3WUZcwI4os6KAIBEiAYIBvZyL52utNJd9L7mu7bt+/2O+8ftT3nnOdsVafu0rceSP/qVp36Pud869TzPeepTe4XOi8KF4z4Z4dI5kLtF5VPcjyhDwDkTJk9XHDbvv/KVxtfHzw0MIA3XKY889+yiLYDu0TFH42VvNHu9b8G2Il/8H5NTRV/gN4EaK5PGYyVN4mVXwOcp7f6RUn5q+Wt2YQ4Kf86s0RtAEFxaDDRhD/HYQVUwVzglL/MSwCONy8qF7JQy1zwnLr4bSfl78eFzkv2D5sBsFyRpdicrV+zzrwRwPo1axWwlqwTfxN+y8phtkYfA6r+7MR/IcQfIOKsKrCyt5KwhUD+DoH5fqtfSMqf/MNmFmDwoQw2Yt8YJm2nmBahpsucaNcWfyokZRGzUDX5kI/z07e03rR9Shu49juFmsGxYtI2UMzYKX8rL3IbNBzDAKb4GqAg2RXWpM5RYDQIRJE17t0f/j0Je9bweGMTOwnEXw5c9az2QQ8fAHTiH7xfUzOJPwBIXwNkytCAIMo0a7Yu+xpgCWSc9VMxl8cZzR7xs958yPigmL4+Qu/yp8tmoQ4TKk2IDeIU5dvyhgGEEUcZQPDi5DvTrcEFbUcwLzIX1GeYUIfx0lq/yPftm0Rf6tjKr8Vm5mat22fn5loR0enZmWyhJf08KcQ/M5eCmGd2DS3sEkAn/sH7Tc3OYGLK/O7sWcfJaRN/AEiVtD5XOXqtE6g6dfU1wHZS/t7ib/NRQqmZBR2zTsqfLvMB2SY4dVP++b6sUM93yp/ngvdpF38jTkspf5l/P34XY8pfxRHcJYC8UNG/9Wv6QtpO7fbHHsZbLr+S2ZLZHY8/YtxW1+jxaMNOIvFX/bQ8nZQt+GNA1Z+d+Ps04yM3fB4fueHzQfuwfo1VSMvN0gMBbAeWg2uapmg95Q8um6CLvfp3UGYBaso/b2vOg0soXOIjbaeYFqEmxRSfsmgGiz8VEoppEKomKX8nDq23Uhet3oFcLOaUvz8vquC7+wUEcxNg0dNr6tGdTz6KB7ZtweWbzpfWCyFwy6MPYvPzO+oBG6wTfzd+tSgA2w0elSWG5UbmNwDoxD94P3bfyOIPgFwCkPdVnw4kEpuZEOj1evyMgoi5PNOPnfK3+6CYRh+a+PsKsbKdBu6GQsULBfHE4VhnphFT/hYcygsNf/Hu8q8aaEudc9kDnhe7UEe/pKIIg89b/VxccNmD8muAeXaveJNnZbZAom+b6/fxgb/7G1y48Sz5PQDbtmL7vj0WrHDrxN+NXy1mf7T8ZmereX8MqFrZif9iEX8AhvcAUBNIEpEFEghkPTzreOyLgDxm5OUaRYi9xd/mo4RSMwt5GRm03MYF0mo5JCDTAF/uqQkFLavhNBJqeZkTTV6cTq6Uv85L6SWIX/u7E/g2cO1vM+VP2wABw4uA1EEAb1yJwuPmnTuweeeOYmV068TfjV8tttkQf/P6GFBhnfi792P3bUn8syKp9Bc3nEySFAL6/cFJmkR4q586EAC8LyuQf8J8+Kf8y7/UIFts4cSHbqeYilDyy6o4yYJjFCoGU2sDxTQIVf00twcOrbdSl6ZczH/K3yb+dXlRBV/mQuOtciXhmF4ElFkCUjOnkLDbO/HnHDi5bIpfLWp+QnIAYekfh9k+BpTKf3biX0f8V42PY3RoyFj+6ORxHJ+a0vf1pds2K1Cm7ULpd71eryxH0z2xPuQz/yn/7B9jkJ3HlL8Tp0HKnxONYC4UoaJdo/23+smYsVL+dQZCdl7kYB0j5e/DhX0A4DD6hctO/H0dLKT4m2J9sJjXMa+nADrxd+/H7psA733z9+Pal5k/BvS/vvYlfOHu22qJP0BeBGR7UYjawfPzKXsVcOXQJPb0b0nSfcXfNuuvoHRMh48YH/KJ/ulbIdWEnyXGSPlL4qPjkB9diL14Udug+gzkl+VF5V8+hi6hDr+kotZbaZ+3UPtmElQudBxattxX8C8Cor1fCKHFG2mw0om/r4OFFf/MYs7qg8o7bwLsxN+9H7tvwGGoK/4A8psATb4TGr21GXbaq/YN+pCP5fG7rJyn+Bt9qAMBeUARJtTy8tJM+ZPtynL0AQzdl9ab4kThwoYz3yl/1Wd4yl9vj+w/ZACjZQAC7xLrxN/bwWIQfwQE/ZCBghdonM8Bd+IfvA9bNFT8kSBJTS9sJHl0IZ9IRaAq3gPQpfyV7ZJQU9FQMFtI+VOfpSe2jnYuZF4UnHlP+Vtw5jnlb8VUBCE05e/ixZTy1zilB1mx7KbfwEDRiT/nYJGIPwBz5A+d/Qdb888Bd+IfvE9dX5pfZbYtTcORBRlp5lyszC1NUln8JagG4m+b9VdQOqbDR4yUvz0g+8+YqVCRmgSm/HnR5AXflj1QtyuYHrzESvlTnzovqlAzbbBwwfPiwS/Li6HeLBftpvzVNvT7fSLyotwuRL/yYwjPWjyqoXHvv+aNGOzzE4vh4UH8/b134rnDhzrxt+BXi24/NR8DjDI4YAcAa89Z24n/EhB/AOgl2cMaytxbw6TBqLDiEoA9HQ9NiOcj5R/rQz61P30rZQ+gCL4u2rVT/iymgkNFheIE8aLg1L10oPAiX0axtIGU9eXCOSjxzB64uIhyGYGW1XipFnx44W4CzM55aTisldGspsYd3H8UP/MTH2a3/fx/+QEkmwY78bfgV4ttNqQ0m3o4lYUdAIyvG3NLUif+wfvU9aX5tczU5cFAZoKm0CGyEkIgTXu6MAfM+qn4J/I/9S8rYKFT/gpOxJR/9dNsAMNxIfOi4HQpfwMvcpC2pfyl7aSQu1/ImMZLB6Te0k2Arq8A1tq4OG0Zi3+U2XwdYwcAK04bt3xjFp34xxL/QFPFH4DyJkB9MFDtIAezJEnQG0hri7875e8z07f7mI8P+QTPEoVUk+CUvyzUVEBVnOWV8qc+dfGf75Q/3y/0OsqYdVP+Khf6q4DJMhtrhLzciT/nIFSUg/GrxXk7AI2Vhx0ADK0Y8v4UcLaqE/+2jRN/uT7cxnxdHmwSyOdYmqaMSOeoStQhE3qJg3pv9VMHArKPJin/OrNETUgojibUZtG0i/bCpvxdQmXEaSXlz3Pq4ndxf8hH56IuL9ZPAEMefIdsm297x2uvwYrR0fLvWx55ENv27NLKLXvxDz9k0Q4yOwDoDfR4B534B+/zV1/6N/z1DZ+T9yWLs3Pul36YxB8oMgDm7VW57FXAWbGsk1ZfA4yQ8if/zFvK3yPIStsppkWo6TIn2rXFnwpJWUQWqtr3LHjjMG2g7VPawLXfKdQMjhWTtoFixk75W3mR26DheAxg3FkVDy5geBNgYpr9V9iLR/oze8frrsH6NevKv7fv26sNAJa9+GfmOnSJo4zPoVfnfwBMA4ChHveFGWZVJ/4um+3PIenz4u9jNvEHAO5jQJICK1YJrMi/Bugn/vojfqrwl7W1zPTtPhYq5c/j2FL+HgJL/uFFI9/mJRTqdgXHyMXiSPlT/02Eug4vvv2iDhexUv5yH3G9Crh9G+j1sHHDOuzcdVBav3r1OFavGsdxTEfx04l/YQs3dGNT/b3B1Pop4GxVJ/4+1uglP54ze27PyvKgJOTZDQD0eikv1K7r/Zr4JyjfU1ZmCRhMgw8hdPEXxbIm/qIMwmpwFkJoQV5Iy1WAFoIsU5yCq4o6FOJQzRJJiCeBXRWqsjYu8RdUfMoK5+0XUllBKl1wIfskmIRfVagEg1nxYhgICZlDmRcYuVDFv2RGxVE5FYLhVO4XWhuEXLZqgyLUDi6qZSLUHKdCWaZtYHgpj2e1Qu9rjgEA7X9t2CmnrsKfffh92vp3vuNq/NAPvTqKj078K2OifIzZvpexGYBEm+6p6J34+1jb4g9kz/LLPqR8OpSNUsdN055V7CsxbzPln1WKD4i6aLICChIU6XaKyQmstqz6pEKh+vRI7VIhIW2VYp8ygPHjpQYOrTdtn9IGUsybi+DsDG0DxazBBfXfjBcFx6NfuHnx4ILhxXQPgIAjJAi4SnjZ3c9uw9H9E9r6Zw8ewC1bn8ILJ0544RyfmsIEKTs7N5tXkx6gFoxy2xJ+tRjBzwJeu+EHAL3iO7HMtk78vWw+xF/zQ5ZFAmW90K4hpr2e36w/314tK96VlH9VxCX+vkKsbp/v1K6CY0xPl174NqhCTANJIA7lhYagOI/4+fMr4VBeKI6HUEe/pKKIQIwP+XDZAyMXWvsNWRWNi+o9ANXNs4n8N12mlYsgJAICt2x9Csf3H9e2bd2/D/3HH/PGeu9f/QmL34m/ZnnKdP6NHQCkxdtlFOvE38/mS/yBPANgLZ9A/WJwkZ4sxnnN3+qXkGXSEhm03BYWSGWhqD1LpEJCcRoJtbzMiSYvTqQNFMeTC96nfQBjxKnNSyShpkLMDCBcbeDa7xJtqV+YMBXR5tL81H/QPQsKFyov3MeA1AE5bQPVoboqIolZS/rZiX9jiz5I4F8FzFxY7sTfz+ZT/It9EvIr759Avp4q75umSZyUf7lvtVAn5V/+ZQmOFQ4jKhRTEUp+WRUnWXCMQsVgam2gmAahqp/m9sCh9Vbq0pSL+U/5W4S6Ni8KTowBDMMFxbT2C4LT71dr1VBM7y2o9mxmbYn/D131OoyPjJY+7njsYTy7d088B9SE0o4W8KvFFv3Ms/GXAFL51vJO/P2ME/8rzrsAZ596mnGfR7Zvw9bdO2uJPwDta4BJ+R+5HGC4ptjr9QiOmvIHVLGPm/LP/jEGWWlmakjt1hAqTiicONbUtmeqXhWfRZXyt3EqY8ZK+dcZCNl5kQNzjJR/EBda+xlMz37BPgVQlnEFv7Ag0ubM/13ffa30GOCegwfbGQAsZfHP4GwHrdVLA4YBQJU07sTfz0wz/zdccjmufdkrjPv97U1fwtO7d9UTfyTVTYDcMUjIxSUmgKXl1wBDU/7VgjRsoH/7POIXGJBrv7/ehMMGYZtQy8tmATVjcjhmLnx5UblQfQbyS4WK1i1yyl/HkTE1Lmj7Yqf8PXBoWW4gxLVB5kXFMXBhGLBrJ3lSXgyoZSbx/+4XX4CjayZwJ+6Ryr/41BfhzRe/FHc9sw1HJidr+41qS1/8UVPjowwMDG/8S9SwH1SNTvwD960p/tn/ufcAsAclO1nKACWQ9lJ/8c9HEvojfgnUR/wS5hE/ATkgq+IkymVBgqNBnDScPJAWOIIEWUEwhYJTeGXFSbjFX1AcSD4loaJCQQVf44UXJ1W0ZV6qelMc2SflReZC5aXKMFeYQuHC+bgj2U/iVOFCmLggOBIXVWUNol2ypfULiqPyAuK/4kL2KfOicEq4qJbLI6r1C8ovxwV/D4C2Kl9fTwdsM//LN56JsWM67tBUgpesOBWrRkZq+YxuQmlHC/jVYmQ/BM6gAK3O/AszPAaItBN/P2si/nX3kR61S1UAkvonAUgXemCgyACQfxZ/yl/ZTgOpJBowCgXFZHHoTJfuq7SB+ixrz9YxkAuKI+TQ06X8oW/XeHFz4eLFyYXWfgYnoF9Qn9y3AIS6gjVh20hK0Qro2w8dOorf/4N/1NbfcOM9OOec04BNg14+nt27G8dIpuDopP5kQW1Tjn90kyiK7EeFSyx/xTepOxkGAKn9Y0AFjLaqE/+2Tc0YSBkAx2yABjAASNJ0flP++T/1hVpeNouPjGNPbduEmqk3u6ziUPFRcRhRCeSlmfj78qKKk8yFt1CH8qvwImUPqqYG9wu67MepXJYbCHFcyLz4caHW3fUiILt5xIDqj1as4OrXP/nxthzI7WgBv1qM7IeBE8pD28R8VCUhv8EqZMoA2IE68V8U4g/kM3vBVEL5UwihpQt7veoGQlMWwH2Xv5xdCBNqJcgygsPj6KnVapNbtDnRdIk2Jz5sYHfMdP14iYRTmwubUPvPmCVOrcfTX6j9MwkWTFqW4rBCXXeA6IlDectX9GlBYklSP+Wf14D+0Yqpk4wWHJxs4l8HKZriGB4DNN0bwLvuxL99M90roD4FoO4FoBxfivzkKeJLdROg5EUW/yWW8qfLZqFScJjUdvUTP+Uv86LgCDkktJ/yt+AwvPhxIfuP8lY/JfDbUv7SdlLI3S9kTGP/ovWmOAYuQge23D0A1ASAJFA4OvH3w68WI/th4BgftkDObWusPvwAwJRL7sR/UYk/AKRqKDCU0+4sFsBALy2PtM/z/DFT/nTZLyD7z5ipUBFPFY6XUMg+66f81e2KECv78QKqtoHB9MBhhYpg+qb8qU9d8AJnzCwvhnobuOBm+nodZUyuf9Gy3ECI58IymDNwQX2yOOQ9AIWZ4p2AezDQib8ffrUY2Q8D12pbAsx4E6C+kinXiX/r5npKIE166Is5ds9iPxp4aAox7fVQtiog5e96q5+fUEAJyJ7BsSqg49AgoYlGjsMKqIJpnT0rOLQNFCeIF9WnXaiMOAov+hMPhjaQsjoXPKcufn2zB21wofNCymq8VAvNePHEoW0w8NJ3ZABMJkAv6JW1kAt42MjIEN587eXstnPPPR2P4YDiV3hj1zKhtKMF/Goxsh8GTvPhdcc9Kd1su2SGDACafw2wE38AwEe+8nn87U1fMu4zPTNr9+nJo/TMv7OCWQdM0978pfxNwdEpTnahpstkF6s4GTGdQi0LVe17FrxxLAIriZ25/S4ugrMzhjZET/lbeZHbIHHl0waCYeYlEKdhv5C4UG8CTBKCkzjuAyD+ZGXzth3Hj+Dt//mNxu0Tm3fJPgKwg00o7WgBv1qM7IeBWywz/8JMA4CEWSKrOvH3temZWUzPmkXe6tPFh+FTwMVAIE0S5QY9IQWkXpou4pS/imMTPEZgKY5LYMk/UhsUTD+hULcrOEYubDNdBtPFBW1HMC8yF9SnLtT+M2YXL837hSzEruyBJNoUx9Iv5D7ixwX1yWPKPoUQxquwPpYA6FOhEcairH3lsUe9ynXi74ft8pE0OdgFRE0zfQug/tcAO/GX961xaHz3S7QPAUnT9yzoCIFE6XciP7HSXsqk/KGJfZ2UP13mA7JfcNSEhOLQAMEGYf9MAic+LCZtA8UJEirVZ3iauxkvqsjJgsdlD+gyyyk9nkpdTFxw7a/Dhc4LKUtxCBd1eNH6pQ8ObUO5i6lf5HhCf2InRCPqzvxDrBN/P+zIPqJfeGYHAKIv6n0NsBN/ed8Wxb8sS/7iC9EsQHbSFn/10l5gyj/D4AOiLjiu4Chtp5iKUPLL/uLkwtREhbTVJFTzlvKn7VPaQIp5cxGcnaFtoJg1uKD+OV6cmGrQFlwbeC7cvHhwQeoi1ZviNOSC/Q6AZMKwTCxJkI/yW7FO/P2wvX2E3QMQ1bzfA9CJf+C+LYs/gOo7AMrMnyIWpj4FkCQJegPkTYBO8XcIcd1AWhWHS6h4odBx2kz5B+MYubC3oQ4XLn7jpPxb4Hde+oXChQnHIto6F4aBLfHJY5r5ld4BUGby9HVOE+4idexkEf9WfDCQrbYlgnm9B6AT/8B9yT6//LZ34trLzB8Duu7rX8YX7r691qChfAcAI9RUvVWBKDplL01L8ZcyjAuY8qdlNRyXkBiW7ULsEifSBorjFJW6QmXBqc2Lsm+EgZD93Qn+/cIl2l59jZalOIzAanX35kXHZPmlbaA4nv2iP6c/0eMXFqrKJJTriOYr/t9z8SVYOzZm3P6tpzZj1wtHOAfkKLZg9Bi0hC2varEtkS4HOJ8C6MQ/cN95mPlL++krAchBrvJBRwpAkqZKumf5pvxLTINQNUn5+6e2FX6NbfDnIjg7Q9tAMb25UH0qmLV4UXBiDGB8uGB5UXAizPopjva+jtwSKSxl5zJfsh0LmfmftXINPvz7n2G3vfWtr8DK9SP6AIBy3oap53sL2PKqQD8Jj4PmQq9eJ5a8WJ8C6MQ/cN+6h6rmfubHgeTUv9QZyWKv16uf8i+Xle004DYUKl4oiCcOh5mZ+goFLxoqFwE4Qg4Dy/1DPmy/0HgJ4YLnxZ1VUdvPYAb0CxcX1D/HLx0gz/XlDIDvq38F5Mt3MS1E/AFgrt/H/Q9sZbddcsk5GF9/iuoAIsRBqNFj0BK2vCrcT5IkiWnw16YZnwLoxD9w3wW4jSMBuVdTX5DWCeiBtld888mR8qf7+Au1Hhzpsjk4EhyX9pnMjAAAIABJREFUkBgCclEvvo7+mBKOkwtfXnScYMFztkHln3JReonDr4ULrv12LnwzCQYcl8Ca2sDyomPW5jegX3BvASwsUR7r1UyganQkCxX/Gg4g2nRA+09L2PKqKH5cahJNbdgBQIrE/jXATvzlfRfoHs6UPK2ZgLSDq09xaQDVI0a93gD7IR8ukJV/hQRHup1icgKrLaviZBZNF6bWBlq3gAGMHy8eOLTeFCcCF5zgaFxQHHo8KaY3F6pPsxD786JgRhjAhPU1hSuKU3tgK3NhwnE/BVCVTZKqHRCIHoc68Xdjy6vq+0mSJGmVC4O5XwTkv6UT/3m2JKH+k/Lf4r8sOCTZrxCAHNLyrwHmf4cEWVqWBtyggKwLFScUTsGjgZTuGxSQbTNdBxcUR8in8HJP+Ve8yMExNOXv4iUoU0XrTXEC+oWLC+qf45d9RXOOafoQkBSnBPmP+Ixpnfi7seVVDf24dcQ0rXNfB7ZY2NcAO/GX911A8c/qYH8MMMmv60t3gWTTBqQpI/40kOb/1BdqedkcHGUc9j3tFIcJyLpQ8KLJB3aPWaIaTAJ4aSb+vryo4iRzQfm3CnUovwovXPagTr+gy36cKgJLcSxcyLz4ceHC1I4nxfHghc0A5Fk6IWC9RFBgVJ8Ir2dFG/7bO34MI4PD5fp//NZN2Lrr+frAlQOIJhX0wM9+WvDBQMbwwz16bypaBx6GHqEKfWJY34m/uu8Ci39Zj9ABYN4NkiTJZxx6QC6SBTQIZ8uyIMnLeVkVpwiABY4gQVYQTFKXKkCIKnkhBWRhEaeqLqzgCSUgU6GgXGi8KJjVnxZeKsIpjuyT8iJzofIiFHFSxV8oywUXkjiVOIRfhguhcMH1i/JIk7roAxhRYupcQMNReQHxX3Gh9DWJFwWHcEF9yrxwODwXOi9C46X0pHJa0SIdi/JoCKDvuAcgXzCWaWr0OHz3JS/HG192RfnfKStXeWGMjQ3jox/5RW39933vlXj3j70BpIfEN9qfWsKWV7XYliqIt3bA/S4BdOIv7xuwz6e+8VX86x3fMu53eOJYWAUKE46nAKSMIY3QWTDKMgCm2ZAummUxKsRcWbqdBlJJNBRM68xIwaHtoDgWUSk9sXVUhULFscwShXz6t5/yt+AwvPhxIfuPkuambdB4cXPh4oXj14hD601xAvpFIy4ojsaL6lOg3+e+7FmZ71MBdYxy2MQOT05ibO2otn5gZBCzA6j1bRQvo8eiJWx5VSQ/AtygLvRAJ4Zlq7kvAXTiL+8buM/BY0dx6NixuGO4PJBYb/rTLJFmHr20JwdSgmsWJ9+AbAuOBkwh1YQJjkRIqgK64GjLKg4VHx1HE6dAXpqJv0OoCOZiSPmXqxr2C7rsx6kisBSn7mDOwAVfd4VfejwpZo0BjPYlQMmKfUT0LEAs8QeAT99zF6YnprX19+/Yjj+/+aY4TlRT+2YL2PKqSH4qmBgHNBjDkAEwfwyo8tSJf5v7GY0EkoR7WEMJDFVAlDusngFgBIcGWUZwsmUoOEqQlbIHqIKiJk5m0ZQFVMG0zp4VHCoqFMdTqHhx0rnwwlF4YWeJDnHy5cI5KPHMHri48M8kWDBpWY2XaiGMFxenBhzKm4UX7nxxDWBMOH3DTYB6DKHHTChFwwJOTPFfEKM8toQtr4rkh8C0mdnhLH/vgOC/BeDU907829zPaELufPoAgOMngUD2/HD1ZEA2ADAGR0mcqID6B1JOnOiyWagUHEacqp95GMBI4iSf+l3K386vU2BJIXe/sPHigROxX9ThQsK0tKG4B0DO5+ZP+LQgEp34u7HlVZH8qDB+h7bRHf+chT0FgE78297PaELvfHK7zM4S5rWhaZpKwTF3UTsgczM6HocRWILpO6OjAbn+oETdrgixqw0GLjRMDxxWqAimb8qf+tTFKZBflhdDvQ1cmPuFLJquGbMksBTHyoVlMGfggvo08qsMkJvzUm1wvwcgsWb/QwSqE383trwqkh8GJuApgKgW9B6ATvzb3c9ogu98aZK9CTBRGUrK3fKFMuRBIAtgSZpYhAJKQPYLjpqQUBzaBi045jisgCqY1tmzgkPbQHEYgTELqOqzXmrXnxeVC1XkZMHjOHXx207K348LnRdSluKw/bIuL544tA0UJ5CXsP4l46j3ANheD9vk1bFtiv/6Vasw19Nv9FsxMoyz1q7DvmNHcWJmppkTymVsYyDbFP/MggUjisI4PwZUeevEv85+57xoPU5ZudpY/rkDe7H3yGEzoDB3vuxFQJWzhPxX/FsFQhmjygCUTqzBUdqe/y2JCvHBLRMdsoqTC1MLpGURW0AmgqcFZNssUd1XX5bqTduntIFrv4uL4OwMbQPFrMEF9R/Gi9wGDSfGAMaHC1IXWesMA6E2uaCYtA0ML8Z7AAKCketSQdsz/x+54rswNJviX/AVaf3LNp6Jn3/9NfjknXdg89499R0o539UYyDbF3+E37jhZ07M5p8D7sTfut/br7oab3J8Dvj6e+7gNwp759Ou1CTVgjwYKOHKQNPr9cIDaVUcLqHShNggTk6BJf/woiHXRROfwNS2tQ2xuKDtCOZF5oL6DBOnMF5a6xcsjtIGimPpF3IfUXEMA1vik8e08Fubl2qDisO/6EdIy1wJX2tb/CGAyeNT+Nn3/rW26fov3ok1a1YALzF/KtgHP/tpoREM5HyIv4Bo89UOVnNeAujEv939jCbcna96MYhcE22dyE76hOyTZQBMATkgONZObQfO+n0waRsoDhOQzald1add/I04tXlRRc4seL78LoeUv8yLJw5tA8UJ5KU2FxQnP570HgD3pFA4tqulReguYVa0QQgcPqy/3+TEiWkcP34CY6g5AKAcxjYGcr7EHwBN2/ioiK1MkApZLwF04t/ufkYTfp3P+RRAVTCDJUGrfArAEByl4Jn/LQdHKEFYDqS+6VwXphZIKaYxIBPB0wKyTSg8cGi9lbpo9Q7kgsOxYtI2UMy6gzkVsxYvCo73AMbGiwcXLC8KTiQu/B/9VNpg4aVog/1VwNVMkQ6GfEJPHfG/88nHMDI4VP596NhRmwOIUAchpsaAFrDlVZH8WGCojwVKALADgASA/XPAnfg33s9oASdSyjkuVlVqUP6vgE2SJP+SIJ09NxMqXijKEjVmpnUyCXJg9MGhAZmyHucRP16onJkEzxk7J05moQ7jhe0XGi/1uOBE08iF1n4Gk+XCMLAlPjkuqH+OX/Nb/Wy8VBt0XuR+YboHIJHu9dFu+60qn5eQtwh5s6f94f/5lF9BhYvoRo9LS9jyqkh+LDCaD/M1ANdsv5H6GN4DYHumzA7YiX8DCzyR5AwAPxjQsgRCoHgVcK2ZUe3Utk2o3cGRFyciKhTHKSp1hUr2z4l2OC8q/5SL0ksQv/Z3J/BccO13iTYnmhomLUtxXAKrcWHjRcdk+xptA8Vp3C9s/Bpw6PEkOLY3Abpv7tOjQF3x9zalHW3gZz8t+GAgF0T8gZCbAKMmC7yfAvBx3Yl/A6txIlXibqoLMxPIrfga4HJK+Zd/heDQeit1acqFHYcZCFGxo5jeXKg+FcxavCg4MQYwDBcUU8ORsjeGgVBQv5C5CHrbozQQNPNi4qJPiUUl+qZH/rj1ZRJQOqAtGG1HS/jZTws+GMgFE394SmQLZngKgHnH7DIW/9Vj4xgm18LU/Y6dOI7jU1NhzqnVPJHsnCeSuAOQXgjU6/XYdGaT1K4Th5mZ+goFLxpKwA3BETLj7b/VT8bksgc8L3ZxqjMQsvMi98UYKf8gLrT2OwZCjn7h4oL65/gN+ZCPc2Cr4jDnizkDkBfIP+cN2CeNCyn+IyNDuOWWDxt3/dRd3/bCz35aaAQDuZDiD8Cd3mnJzK8CVlbY7GQWfwD4ube+Da9/6cuM262P8nlY3c6XGj/ZoK8RQg4ISZkByGpAl83BEbzg0OBJcZiAHBYcVRyPWaIaOIho8D5tqV3Vpy+OjRdVnGgbSM0DuNBxZExt1k/b5y3UvpkElQsdRxJYimPhQuZFxbGJv4NfejwpTowBjMYFrZfOixA+bwJUTehrEnZ1PKN9k7GP3PrNxvjZTwuNYCAXXPwB1yWA1gYH7ksAy1z8m9o/3/4N3Hj/3fxGAew5fKg2dqK+BCjRORVK2SIIpWnCz4xcApv/Y5vFmISKw9REpcQk25XloJSsVZx0HKneFCcKFzYcx0yXYnrP+lVxMnPhz4uCOV8DGIojZW8MGaagfiHzMp8pf70N+gAg+44Hud/L/i5gcoBbMNqWlvCznxZ8MJCLQvz9Lbpq2QcAnfg3tt2HDmL3oYP6hggnkv7JhqT8NyHNLCb/lTiJ6muAXBCSRANuoaqKyDg0kNJ9gwKyTShUHMssUchst5/yt+BYebGLk7/4e2Iq/TA05e/ixXfGrLWB4gT0i0ZcUByNF9Vn3cGyDaf6GJCPUZ6y/4T5dqAYpnDSBn7204IPBnIxiX+Ew1Zrf/OLgDrxl/aMapFOJPlFQHwd1cFA4bZ6CsB/xkyFioQ4fpYYOGN2B0cdRxMniqnsxwuo2gYG0wOHFSqC2STl7xI8I78sL4Z6s1zUyyRwOLSsb/ZA5sWPC77uCr/0eFLMhgMYa78w8MJlVcyvAi4soQ1wB+pYRvtnS/jZTws+GMjFJP4AiqyOejBbvywwkPlWckqWrwFmm5eP+C+Gu/1Nxh4m7mnAJL8hUFTby/cAlHWCLCT537w4mUVTFlAF0zp7VnCoqFAcT6HixUkXCi8chRd2lugQJ18unIMSz+yBiwv/TIIFk5bVeKkWwnhxcWrAobxZeNHr1aBfmPilbaA4rjYA1scAqx2U33xRviAY0ZT2RDfKbUvY8qpIfiwwwT6C7rpz7B1gYY8BohP/Rhb5RErpY4CcJdl1fiFHG0AAaY+8B6BYb1g2C1W20iZO1U/dgGwTJ8ssUchMdyn/spgiTuY2SNtJIXe/kDE5TrU2UJyI/cLKBcXReFG5sA9siWulj5hxqP+SU2YAIISocsTFvonQyrViCi9t4Gc/LfhgIBel+AOxPgccjBHnc8Cd+BvtPW94K15z4SXG7Z+54xu4+eH7a2FLiRutronWSQWE/C0AuASPEVhwwdEmFKWXoOBoxFEDhvBsA6k3gdYxPXBYoSKYvil/6lMXp8AZM8uLod4GLriZvl5HGZObMUsCS3GsXFgGcwYuqE8jv1L2hmDGGMCE4kgDQVqXqt7sPQDMqsAC9Yyery3hZz8t+GBpjOTHAlPfBxfA/QvXteafA+7E32rrVqzEGaecaty+crT+l7FMV2r0LqisSbLsgf/b68yiKQtoVcgWHNnATkWF4jACYxZQ1addqIw4Ci9xUv48py5+l8OHfDhOnTi0DRSH5cLMS1j/MuDQNlAc9nxRuZB96vcAVLFIYB5NaVMb+NlPCz4YyMUt/tEyAMFm+hxwYvmTbnDaySD+N953F+57erNxn807nwt3FMH446KuK4KbHLyqDACUgGyb0WQrg8RfOdHZWWKN1C4XkM04TBto+5Q2cO13cRE8S6RtoJiSOPlxQf2H8SK3QcPxGMC4syoeXFAcKrAUZ764oJi0Dd68yPtq/BoHQhkK9xggtSSxPwUIiObvlKFtasOUmNAGtrwqkh8LTKt8tWjODMByF38AeGT7NjyyfVs4oLD2mWYm6D0AueUnfvbGPzW4QXoXQPk1QG6WaBAnp8CSf0KDIxdYNYGlOEZRsc3uGEzFP4dTtiOYF5kL6tNXnOrwYhXYhlxwM2apDRTH0i/kPqLi8KJJffKYik+KWZuXakMoDpfyd7VBugTAxCLTK4GjGT1nW8LPflrwwUAuCfEXQMCoLTEs17J6nwNeRuJf29o8kQrYQvC5Mknmn9aA/pX2elUwo3Vlg3DgrN8Hk4oKxeECqSROdYWKx9HaQHGcvKgiZxY874GQJBpyXaxtCOLCE5OWpTikDc158cShbSh3MfULMy+1uaA49HhSHPZ8UblQ2y/3C9NTAAnkT7QJtlRDU9rVBn7204IPBnLJiD9cWZ1g80YzDQA68W9ibZ5I5CQqPwecVB8IlYaHSZXm194EWHwXQAtCsmgahYoUMgpVWcQWkJlAWoFahMIDh9abtk9pAynmzQWHY8WkbaCYNbig/pvxouBYU9u+vHhwwfKi4ETiwv8RP6UN3rzI+2r8WgZCPKd6Ct88ORSG9TWM1qcNU+JCG9jyqkh+LDCxxN/TTJ2gkXKZPgdc62uA+b7B+7D7duLPYmc/2QL3zaaiOepgQAghBcMkSRWhqByEz0zrZBLU4OjGoQGZMhznET9eqJyZBM8Zu1uc9OVgfpW+F/pWP44LTjSNXGjtZzBZLvh+QX1yXFD/Vn6DeKk26LzY+4UXvwov6gDZ/SIg3UhtAVgmbxaATvz9sKP5UHc3j/LqqpPXfv7vAVjG4v/DV12NS84617j9pgfvxd1PPdHuicScROwAQN0mgARCyQwk5B4ALgj7zmJsszsFh4oKxXGKSl2hsuDQNlAcVxvIvjovpRcPHPvAytUGrv1R0twOcTIKrMaFjRcdk+1rtA0Up3G/qMGvcl6zGSbj+aK2X+8XEhfKJYDQ1LBwF9F26MTfDzuaD2b3hfkWoO97AJax+APABRvOxFUXXGzc/vCzTxtPpC/ecwfufPIx477P7tvtroDhJDK/ByApy5epfwkv/xYAqTMfzDxmRmrdCkyDUNWf3Xng0HordeHbwNTbwIUdhxkI0QBPMb25sIlTXV4UnBgDGIYLimntFxQn9qyf4BgxaRssvPhyQf379jXqU8DyNcAkyc7lechWvvXil2KMfP5ctW9t2YxDx48bt7/xwpdg9cgodSPZHU9vxd6jLwRXVzK92mxbYmFH82HefV6e+1et8dcAsyInr/j7mqljbN29E1t372wCbMTn3wOgt01N/yPJvwVgEzxrajtgRmMMjgE4Qm59+2/1kzG57AHPi12c6gyE7LzI/SJGyj+IC639DGZAv3BxQf1z/LaZ8td5UXH8eNGzKrJP86uAhWF9TVM4onbuinX46F99kd32fd97JVYMj1gHABesOw0f/X+vZ7e98Q0vw5pVo80GAEy1l7j4+6YAfJ4ACFK2Rl8DzIp04t+a0eDCWKp1Gq5hglwOqAYC1ceAcg9MQA4LjiqOxyxRbV+B6SFUzcSfEWIqKrRuxjQ3qXkAFzqOjKnN+mn7AtLcnGjSZV8cSWApjqUNMi8qjk38HfzS40lxXG2oqhjeLwyY5qwKU+8ALgq/6qe+g8wlJrRejE1NTePWWx9ht116yTnAOcZ7xwEAs7Nzxv3POfs0jF6+1l4/mzHVXvLiDyyyFwF53kfSiX+LpoojY/I9AHLbqsCSyLP/3IoXAZmESg/sjKjkfzcKpCqmUZx0HKneFCcwDes7gNC4oDg0wFNM71m/6lPBrMWLghlhAMNxQTGt/YLi1O4XMhdBOLSuFMfVBgMXGqZHX3Px0ieDAGrlB70049ZZjNalDWsRmsM+GcS/1ePhMMObAC13lxVFOvFvz2jgs5h1lJaU/6iwEAJI0vxrgFxAzgtxQlH+xQZkW3CUceypbbnlXcqfERWNFzcXLl6CZsy03hQnoF+4uKD+OX7NKX/VZ4MBjBGnLi+kLMOF19cAjZZhGaOCwlN0axGaw+7EvzT1oS9vMzwGGPg54E784xkNMA6zj9OSPNAUAb1UNABAmiaa+LsDom9wVHEYUaGYXCBVlpuJPxNwqajQukkBWeaCilNoapfnRa230j5vofbNJKhc6DiSwFIcCxcyL35c8HVX+KXHk+LEGMAE4fBZlTb6BX0MMHv5j3zZV429Ap6hjdatDVPP7Raw5VWR/Fhg5lX8wy4BRFOzoK8BZhs68W/NAk+ilDQs0RYYeAEkaVYgVTMARQGyzAVHNpg5Zrp0mRMqXpxq4ngMYMLTuf6CI3HqmT1wceGfSbBg0rIUhxWnwAFMKA7lzcKLLqBN+peBX0UYbdmDMC78eJFwhNq+fNmY/jeZehew3MboRvluCVteFcmPBWa+Z/6Nv99Q00wfA2KnlstV/L/16IPYtmeX8UR6bMczzZ3UOInUDEBS/peUK0SeBUhQXEfMNmVfAySuleDsF0htwVHFscwShdzq9lP+FhxP0baKiopj4cKKqYmTuQ3SdlLIxovvjFlrA8UxcOH/fYQa4q/xonLhkY1Scdg+be9rfrzA2S8kHOMlgAQEkRUMASAR+spQMVu3biV+73ffg9/+nU9J69/2g1fh+773lfiXx8nnyyk/uY2ODuPDH3ovPvD/XCft/+ZrL8c73nE1btj6qF9FmGqfjOJvsRijAitG/c8BLxPxB4C7n3ocd29+PF7nU405iXxMGgAoKYCE/Feur6amSHvVx4DKKigBudYshuKo7SLBke7HB8qyRA3xdwgVwfRN+VOfujgFzhJZXgz1NnDBzfT1OsqYnODQstxAiOfCMpgzcEF9GvsaPZ4UM8YAJghHaYNUl+ZcGM8XAIJ+DCjUBOR7gUkbQ2y6P4czztY/Yb5m3QqMrBzGXDFIUftxbidmp3HmptO0/VeuHsf46lHM+tznwFT7pBZ//0sAPo8CepvXJYDlLP4Aap9IvtjZTzi+3mcS6acMPkz9kySBT3BkgxkVFeIoLCWr+rQLlRGHtgFEcFxtIGW5gGxO58qCQzEX94d8dC6a8+KJQ9tg4UXHq8eFFUc5F2zZA50L2ae5j5gx9fNFxizuATA9CVBtI40T+TINB0o7Q+xvbvkmjh/Qn/O/detT2PHVadIQ3sd1d9yG6Ylpbf3dz27Dwa982V0BptontfhDyujM67WA8M8Bd+IfFTv7qYefqo8BWnyUf+SBpXgMMDQg10nthuEwwZEVO1LvEl4PyEHpXBOmlzj5cUH91+LXKE6ONhAMMy+BOJQLijNfXFBM2gaK4zGA8RN/X16U7RIvpIYGLlxPAQh1QUA59QUg5M+BRzd67FrClldF8mOBWUjxz62uajXKCFgzAJ34t9TJc+zspz6+dh2wnPmLKholgBTVcqteBGSa0eTbvIRC3a7gGIOnbXbHYCr+OZyyHVRUCKa/qNjEKTC1a+HFLCRKvaX268t+nMplJRzLTFfuI35cUJ88puKTYs7XAIbgyNkbyksIF3z/MvYLpV7SsmT5p4BFEVftQa4Tfz/saD4cu3vhu3WrlcyAMQPQiX+LJ1Kkk8hwr6bmSh0oCMivAtaDUF47oiSC7CunMH2FSsapndqVRIPguNoAe0A2c2ERHEk05LqYuODaX4cLnRdSluKwQhU4gKl298OhbSh3MfULMy9B/cKEo5zHeibFxoXafr1/8VwYBiV0O8sLDBkA2jCy1nDneGs3lFPeW8KWV0XyY4FZFOKPcogX25yYfp8D7sQ/Knb20xzf+gXJJJE8iDwgFuuyzwHLdWFTuzXS3FxADkr5l7vLwdE0o6PLvtkDKyZtA8WswQX1bxUqEyYVJ7pvjAGMDxekLrIOGQZCDbjwf8RPaYM3L/K+Gr+BA0SuX8i82NrAc9GnFUpkTrIYR/5uydaNj2PkhB5bxoaGceqKFTh0/Dhm+3PG/deOjWEW+seExgaH8KIVK3FkchLTc7PVBqZJy0n8AXD6NS/3ApimkMwD5m7rxN+Nnf3EwWdfBKQMCpIkgWD4SZIsA1AGIRJYq0Cpi5OAHsyEtJwXIMv0JURU8I3X+4tjoAiVLJqmgCwqHCg4omxo7ocEZOJTEhWyLy9UQuJC50XGLPkVFabKhSr+Ij8WEqcSL2Rfw3KFo3Oqc6H3C7kNLYp/yYUgvFT9AkwbeF44LkyzdNIvWV4UHEu/kHkhOI5+USw3exNgYc0C4ntfezV+6qrXautfc955+JVr34KNq1db9//JV70GP/f6a7T1V5x1Nn7l2rfg3FNOqVYKrVh1bjc1C8yiEn9APWSm6/pNrvez5Rt/DKhC78TfhZ39xMNPXW9spjMIIjJA9ibAcn1RsyqSVUEt/7v60UVFDfB0vyre0kAZgEMDM8Whx4prg7qvMqNTA7IZxyB2Gi98G7j227ngRVPDpGUpDiMqWt29edExWX5pGyhO437hyQXFUc5hdTBl7yNq+/V+4ewjlVN5O8XkyubLUgYgt+wzwFygo/1RRAuFB/a/gF/7wHXa+n/97G140amrgTX2/Scnp/H+X/pf2vobv3IP1q9fW31MSG+qdOwamQVm0Yk/AMeFm9ayASYFSTvxj4ud/cTFp7xnnwrnHyYVAhVX+RQzTXskIBnEX50xQ53RVAFRXtZx1CAvzXTLfeWZrhqcBRPkyxm1Ijj2O7j1gMzOEm0zXTpLJO2ny5VomASvZEsWKgZHzgBUx6LghROnarlqPzvTJVzIvKg45Hia+kXBL9cGhQuuX6iYZVuFikOOrWlQSDmFuqxzIXFKcQqWiE9+gFhWXumLlFPKC+VfkHpUthBviJudm8POXQe19UeOTODICxPO/fv9Pp5//oC2/ujRSRw6dDT7Q2ibK86bmgVmcYp/i/dtOMz0JkDv6nTi78bOfuLjyzcBqoMBIJGUXz5W5U2ATIAH1OCYb8+XrTMjIbe0/bf66QG3LMrM2MkuWvvJrppQkdUSFy5eqsGE3AfqP+7I8+LkQms/g8lywfcLFxfUP8ev+a1+Nl6qDWF9RMXx46XktIV+wXKBTDxVK7YnifptAN4W6MuyjWw5i39uIQct2gH2fhMgX4tO/F3Y2U8L+MI+O8h8JtkggM4081/6MSBjkHWKip9QNRN/T4GFGpCpUJCWO3Fcs0Q7F1z77VzwounC5HBMohI75a9jqjh1+4WNXxsOrZeOGWVg69MvfDAdAxghzPcACOOWyDZvjgp3kRxaYBa1+AvM8+t/Kqs9AOjE342d/QisX70Wb3vla3H+hjOydULg/m1b8JX778YLx90pNRO2dg9AIv+RJCpfVU9L8hcB1ZnRlH8ZxYnBoQJLcSy3HA7TAAAgAElEQVSBtMmMji6rQVYK8BTTmwvVp4JZixcFM8IAhuOCYnLiRGpY4bgEluGF4yIIh9aV4rjaYOBCw/Toa25e1DYo25l+6ewX5auA5cxdop3HFmsSIx3Qsa0T//q7Eqt9xIO/Bpht7MTfhZ39CKxbsRIf/4VfxUDak4pcds55+MErX4Of+Z8frN6tHYANOB4DVHYqYkyxT6p8YaxL+UdO7Sr9KzTl7+IlaMZM601xWC5ss34zF9Q/x6855a/6rDuAqdHXnLyQsm1wQZa5wZbN/EoFWiugNneRHFpglor4R3gPQK0nBIIzAJ34u7Gzn2zhrZe/UhP/wtatWIkrz38J7tr8eBB2YdYXAeVPAJhuXkuUVwFry0o7aHCkZflAWVU2ysyIBlJatxgDGIPgGQVH4YXLHuiCJC9zQkGX/ThVRIXiWLiQefHjwoWpHU+KE2MAE4TDZ1Xa7BdWTCmTofZpvQ19yyWAIvbKA389ktW6B4Acg9NOW4OvfeUP2WLDw4O47tu3WaHGxkaM+w8NDeLT37mLqXVNs8AsFfEHEKJvTR4F1CxoANCJvxs7+6nwi7S/yV58+hl+AwCmynoGIAFHFhWcYr9e2muQ2oUiTgE4EWZ0dhx/wfGf3aniZOaiNmbgAKZ5mtsTJ6hfyLzEunRAZ+ztcOGHKeGw22kfIfVWlm280HsAfF77K5twF7HsVtTwQ1//aj2c3P7nt/7dWqVO/Nm1sVSOw0lMXr0vAXTi78bOflrAN5xE7HsAErWcvE9ZLH8PACc4svhUC1pQg00ochyv4MgHZCcODaQUM0RUVBwLF1ZMpW8tVMpfawPFMXBhfktjTS4ojsaLzIXMi84Fca30ERtOXV4Qv19oOEy/oJjc54ATfVYf7U5/Wv/YxkB24s+vzidz834roFcGoBN/N3b2o+O7ruk5r/lZTiLntwDyPpWg+jpY8Zs9Bogq8KhtIMGxKmaa0VQVbTIzYoWKYC6GlH+5qrZQ68t+nCqiQnEsXMi8+HFBfRr5pceTYjYcwFj7hYEXqQ1SXZpzwfULa1+jZQmO3gbVp9AyAIQZrV4ms2/lC3fi74fdGN+2a32ta6SS5hcBleid+Luwsx8e/6Fnn7bu/sj2bU5seVW1Ur0EoN/6p64oApDIvwUgUAUhUQVkEuRFgaPMjGxCJUhgk3CyCKfjiGJZD8iq+AtluairGpBFuZzjEMzSNzPzFkr7pTawIicknxwXKo7KS+FYFo3Kpy5UBEcTKpkLVfA4LiQcUfmv2io0XsoSQrC8VPxTLsjx5HAkTiH3C4oj5GWpDZRTIxeEX40LvV8QajVMAlX1oWLZKP5Vvyxqxg1yqZEaSWt48wie9DjENgayE39nCVPq3ra9sVm/BdCJvxs7+zHj3/zwfTh2YpLdtn3fHjzy7DYrtrxKXsk9BZAA5adDs51EGdApbpomJCDLQiGLk1AEnxEKNSAXAVcoOIV7AWNwVANyiYMKhy6zgxbSGFFV3JBJqOpWBfiyIjIXilDpvPBclNuLqmq8yIJXcUEwNV6g8SLxS9pPhbqst7Qsi1PZLyReOC5oN2MGMBSTVE5tv7ysD+boMZWFujyMloGQkHHUvmbkpay8LtSU35IXc78wD4SU844OGhiL9lZAejxjGwPZib+7SAtvfPQC9PsccCf+LHb2Y8efOHECP/1XH8QbL7sC563fUK5/8Jmt+PaTj/L7e55EqTp+S+SF4qpSNiiQvw5YfA1QDo6ogl6+XMVbeXYnB/i8hopQuHDUQKpi+qb8qU9dnFxCoWCqx1VwbZCFRKu31H592Y9TuayEY+VCFhUfLqhPI7/UJ8WszUu1IRiH1lvipTkXxn6h1Is9X2jdtDaoPg3ni/JIcCUMtsGBvN4aQtX+HdMYyCh+HBBLXfxz81E+V0YgWD1NnwOmb6EIsk78dTsxM40b77srCFtexfthvwZY3PBJuBSQgx0gvwq49GEQJ5dQSUKhiIPUJNPsrtjTMyBzMzpXwPW9YbANLnReSFmK01CoOE6dOLQNFCeQl6B+YcJRztmwWb/s09xHzJgSp3Q7ywspq3Ch90VfXujngKuG0tmhOpDXzJJBkHxa7B8+8Fs4fc064/b3f/wv8PiOZ1lseZXdj5c5IE4S8Y94V2eYRfsaYFa8E/8Y2PIqsx+/94LL/xbp2SRNSeCRg5D5rvCw4MgFRD045jis4FSFjJi0DRRTS0mDbLfMdC1C5bxbnooT3TfGAMaHC1IXWm8JZ764oJi0Dd68yPtq/BoHQjZelO0SL6SGgVzU6WtavfN9ba8CLiyRBvdAIvIFAXINUDHaDocN9QYwOGCSBibuMJCd+IcUDfYTTS3NA4BO/Fns7KcF/Bonkf4UAHdi5k8AKDODNEn1IOQ9081rpwRHF055bGggJZj+olJXnGTB0dpQQTmEpDkXnOBIXFAcy0xXExWFF040qU8e08JvbV6qDdH4DeIisF8o9XJiam2Q6+3LhfUuf+4uX1GtF0yRyglpS0xjIDvxDymaFzZnAEJfBhFkjb8GWBbvxL8xtrzK7cfvPQBVoBHl9iR7D0BN8TemiJUZDZvadYiTHhxJK5w4hpkuxbS0gWu/S7Q50dQwaVmKw4pKXV48cWgbKE4gL7W5oDjK+apnUmxcqO3X+4Wzj1RO5e0UkyurcEH9B50vGo6Q+q08007KMgoJmkkhgPqNbQxkJ/4hRcmxru+xkTX6GiDQiX8sbHmVnx/nJYCETA0Uf2UGAFACuyMg5xj24KgGZCbIWgIiXdaCLA3wFLNGGpb610UjhBcFx1uobLx4cMHyouBE4sL/rX5KGyy8+A7mqH/fvkZ98ryobVD7or7s3S8oF0pdKq8Eh/YF23dBhPLrMlr/2MZAduIfUlQp3MJjAD7W/HPAnfg3xpZX+fuxvwiIyReWs53iHgDZW5y3+tWcGTEzdl9xMgdkWSjkgGwRH6U/hb7Vj6+X2n6b+KjtZzBZLnjRpD45Lqh/jl/zW/1svFQbdF5U/mvwq/CiZ1Vkn0EvOaqgzFyY2k8x63BBDyAA/0BJ90vk4xrbGEjOz4f+0y/glFWry78/fsPncbftteeOqp604h9mUQcKtb4GmBXoxD8GtrwqzI9x0JgkVTAxQKb0bmKrUOU14wKyx4zGT5xocCy9eOAYZrqk8qGpbZdoc6KpYTrFyUNUaBtYXnRMdsZM20Bx2DaEcUGqGIBD26rW2zbrV9uv9wsnF6TCRkylDXx7Kv+xBssVF6TinPlMFKnveTCTn3NPOx3r11ZPE6wcHbOB1PLhbW3iB+xq9MO/0sVlifIbbLUyAJ34x8GWV4X7Ue8BSMh/0sp8gcaWtJd/DdAVkPP62oOjGpDDAiJd1oIsDfAU03t2p/pUMKtNAUKl4MQYwDBcUEwNR8reGAZCGhc1Zv0Eh8dUBNbCiy8XGqZHX3PzoraB7xc6LzJm/XcnEBwLF8VTANpbPpPEP0ucEPyWLYofB8RJL/4LaKabAO2fA+7EvzG2vKqeH+0wKa9vSABIUZW+2ylJ+VkiK040OEIOsoaA7Ay4zIzdV5yip3aVvhQj5R/EhdZ+x0CIFdBAcbLwa075qz7tAxi/PqLi1OVF9rlkUv5qvzBmAEzrm5bV7Qt33oYVY6NGyP0vHMlXNfOj4vKbG/poEz9gV5efZJHdA8BWphP/ONjyqvp+rH1GuwVA5IEo+y32jZfCZAIpwYyd8tdxZExtRkfb5y3UvpkElQsdRxIVimPhQuZFxbGJv4NfejwpTowBjMYFrZfOizmrwtQ7kAuuX1gxlRm7+RG/GOeLAYf5GqDpPJdrK+3Blve1f/zWTZwjZRXnV7ZvPnw/Vo+vKP/edeiAEzfUhwOgPfyAXQP9hB68RpcBvC8BdOIfB1teVd+Pz5OaWWBJJOfFXmma8jM6UtfQNGy4UDtmuhTTe3anBmGKqfp0YDLipLdH9h91AENxjKlth8AyvOgCGogjCaWQ6mhtQxQufHlR26Bsl/qIghMygKG8mPpaABfci4Cc9wXkVWhlCsm49o1bH/ny54Jw6/hYEPyAXb39uAN6K4fX/3PAnfg3xpZXNfPDvwYY+nsAymAqpAJJ/sWg8ODIB2QnDg2kFNNDnOqkdq2YSj8KTfm7ePGdMWttoDgsF7ZZf00uKI7Gi+qT8qJzQVz7C15tXhC/X2g4TL+gmIH9wud8UcW+mvznF/WKFbY3/sQyBi5KfHRALDvxh3Y45+1ygPMSwEkv/jSgtowf04/XJaPy3SF6UKsyAHmNLAGZC7isUIEIXkhKulyuFmyCZxQc2gaK6S3U9TIJHI4kKhTHwoXMix8XfN0VfunxpJgNBzDWfmHghcuqxOKC6xfWviZlMginWhtUnw3OFwNmNdggDVuot8MApKPTVcxKi/3Be96HU1auKv++7mtfxHc2P+lwG+aDAWgPP2DXYD9+9wAwb4ZqZtYMQCf+8fBj+9G+BChZot81LOTFJE2r2nAi5xEcwwOyv+BwwVHDYYMwFQrVp++M0YJJy2q8VAuhQuU7Y5ZwKG8WXvR6qVwQHAsXVhzlHLVlD3QuZJ/cjNnFi8Qp3U5xlLJ8H7H1SxsvMo79nhgzF+olgGxbWDBt+l2Zd1/zFqwYHTVu/9y3b8Hew4ecOBduPFN6DHDN2ApLabn/1DLH7otW/BfQ7J8D7sQ/Cn50P8Lx6uiEBK/qjb9SHdIkIcHTFhz5gOycDXmKtlucGFGpQP0wNXEyt0HaTgrZeHEOYCgOrTfFMXDh/30Ef34XMuVP/YfxAme/4HkJ55cbQLi4aHy+FDiUKM1s2yKZAP7Dq6+WhFu1Wx99yGsAEOa2Ydscuy9q8Q/fxaawQepr/hxwJ/5R8KP7yWGM9wBYdpPfM55W9VKDULmTQ6hQBTbflD/1qQfkwNQuxaugjAHZndrWl3nxUXEUUaE4Vi7oDNWPC+rTyK+UvSGYtXmpNgTjSANBWpfmXBj7hVIv2n5CEzsQ4gctDtGuKmbhRcFhBsjqempJQq7/kzZwf9WOwbzr1q0Tf9RU3OYW/B4Ak3Xir+NH90Ng/AcAxeN+1c5JkmT3ACjiQF3ESfmr+/oF3HZS/hWAbZaoYdKyFKehUHGcOnFoGygOy4WZlyYzZnosfLMHOheyT3MfMWNKnNLtLC+krMKF3hd9eVFxmAEMy4ujX1gzAC1aC24//c2bsGKkupSwZefzjNuGjh27LwXxz8zw5L1lo8W8yzf+GFDmrRN/FT+6HwWG+16zEKJ6G2C5mQRp8mdSvC4425ENiHpwzFbWniWywVEu2+bd3BKm2hcE1wYbFzZePLigOFRUKM58cUExaRu8eZH39Rc8x4yZbpd4ITVskwvaPu8Bct3zpdiuPgaYSG//9Xj4N/weAOEuUse+8O1bHW4bOnbsvnTEH81v3Khptb8FUBXsxF/Fj+6HgbFlAIqAk+S9KlEPkRB5BgBKEPYXbf9AGh6QueCoBdIKyiEkYQGZ44ITHKkNFMcy09VEReGFE03qk8dUfFLM2rxUG0JxuJS/qw2+/UJrq4aj9wtCEyva/KDFlwvHAIbiSLwQTLUNNTIA4XvE2rk+bif+3qaqa8j1f6cyNxoAdOKv40f3Y4Bxvwcgu8lPiidCVPVKEj6YcYHUIE56cMzLeuHYg6MkcFRIFKEIEW0vTFqW4rBCVZcXTxzahnIXvd4uXmpzQXGUc1PPpNi4UNuv9wtnH6mcyttZXkhZhQvqnx3AGPlVcZjjyfISdr5wbwIEyKu94fvEmIcxrqLELQdEJ/661bwHoHFHmPevARpNROp8DG612C5+dD8WGDv18stCsgAkgyVlBkANyEyQtQREuqwFWTY4ymVjpbmdmGo/EFwbeC7cvHhwwfKi4ETiwv8OeaUN3rzI+4YInplfhTeJF1sbGvYLyoVSF+Kiwol2vhhwCAO82be79rYV7MQ/pGgNP9ZdGolp7acC5vVrgEYTkTofg1sttosf3Y8FRkBYLgGYHw8UqGYPaZoahco5M/KcsbvFSV/mA7JFfJS+E/pWP14o1PZbuNDa75glsgLqL07Uv5XfIF6qDTov9n7hxa/Ci55VkX3GeYmUAYfWm2IGcqHz4uaC50XGoe8BqCaFCYQQUC8PSIkAqQGO4Cy4VdVK9R3+qh2eOOqNa/JRy9rED9g1vvhj0d0DEP1rgEYTETqGAbdabBc/uh8LTOHD9SGgKqhkQUkNXGmSWAOpWZzCAjIbHJkBBPlRgqdZKKKkuZ3i5CEqtA0sLzomO2OmbaA4FlHx5YJU0Y9f5bzUMym2Wb/afr1fOPsIqTAn2l4Cq/ivPYDReNFxog1slUsAxWkuryUmiHYIuGMzA6TGLes7/ANwbT4WFX7Arq2IPxbupY/z8jVAo4kIHcOAWy22ix/djwWG+vB5DFAIIU0TpPdIpikTHHMvloBIl7UgawiO/mlYmzgZhMqEqfYBwbVB9u89gGG4oJjaLJHWm+LEnvUTHCMmbYOFF18uqH8vLigm7UMSL2ob+H6h8yJj1r+RkuAE9Qvb+cJzoeJUdZYDbTngJy/wIuRJ1hdz/AamfJS45YDoxL8Vq/uIoGStfw3QaCJS52Nwq8V28aP7scCoPuyvAi6MvDhEyOvTJFWCkCXgMjN2X3GqE5CtM0al38RI+esB2SY+avsds0RWNALFycJvmyl/m1CF8aK2QfYZpV9oOEy9KeY8cEHcKduVvia1oboEIKf8RfnobgXM28zclL6SKd+Jf0jRGn5CdnFra+JVyo0h1cowAEiifA3QaCJS52Nwq8V28aP7scBwPpx3ApfbSVCEqIIKfQ+AEthip/x1HN/gCCUgU9FQ6+2bSXDjSAGZ4ljaIPOi4tjE38EvFRWKE2MAo3FB66XzYs6qMPUO5MLNqYKpzNjNj/gp/Yn49+fFgWMdIPueLzIONeZUlv0pNjU75SzUiX9I0Rp+wneJIe7B1trXAI0mInU+BrdabBc/uh8LjMlHyD0jWaCTIg2SJNWCkH0WY5vRKcHRe3an+lQwBaHGhcmIEy0bHpADBzAUx5ja1uvt4oXjIghHEkoh1dHahihc+PKitkHZzvRL735BKmfmheC0zAWLk/v2fweAvdzM3LS1aCf+IUVr+AncRUBwGtv0ff9eAmH4FkCzrwEaTUTqfAxutdgufnQ/Fhibj5n+NLu+eOlPAmSBRVbG0tI0ywaxQdY6o7GLU/TUrtJnQlP+WhsUzKAZM603xWG5sM36zVxQ/xy/5pS/6pPyonNBXHsLXn1eSNk2uCDLUr0pZmC/0PuImwviTtmu9DUjL+pbAKnpr/Qm3ks/WfpfqJukMi67bOMZGBowzQ2BJ3bvxsQUc5khwIfVHLufdOIPQHmmY94s+tcAjSYidAwDbrXYLn50PxYYl4/JmQnDFnLTHx0MKHnENE0NM1M5kNFAahVqg+CFBUcYA7LvjJku80FWx5FEheJYuJB58eOCr7vCrxS79VmimxdenDgunPyq55aAud6BXHD9woqpzNjbTPnrmAqOdYDsy4uMw/2dn7jk76T8KJBJLsrr/0Lf5hu3rtl0Ib7x1fvZba+44nzsGz9qHAB04l/TxwI9BhD1a4BGExE6hgG3WmwXP7ofC4yPj6nZSfRFH6njaYDsXgH9cJZfAzQGZH/B8Q+OahCWA2K91K4Fk5alOKw4BQ5gQnEobxZeXELhOxDicNg0N8VpzIUfLxIO3U5xlLK+/cKPFwMO5Y3lJZwLF6eScesAZBHZLyBPz06RRhLogLh17NgkPvqxL7Pbfv6//ABwFh9zOvGv74PO1ebTon0N0GgiQscw4FaL7eJH92OBCfExNXsco4PMCzsSGmCq1KEUdEiZ+U75WzE1cfILpHpAbjZj1tpAcQxcmJ8Fr8kFxdF4UbkwCKy2rAqVDacuL3D2C56XcH65AYSLC65f6H3Ekwul/ZQZPntjPsNp+7LsXYI0rWb7RUbPdq/AweN7dVyjx3jWiX9DH/7SH3WQEOVrgEYTLXU+KgAt40f3Y4EJ9TE5M8EPAEoz95WUzQCQWjgDcuDMSAmO5araQq0v8+Kj4iiiQnGsXNAZqh8X1CePo4gKxYwxgAnCUdog1aU5F1y/0LigmLQswdHboPq0izbXR1yYvil/jTe1r1mEm24vsnaFJUXqHwlEkZw1QO088oyMaSoY0Trxj+HDquutZQYafw3QaKKlzqcGqRbxo/uxwNTxcXzmGNZhPbutfFFQks8ckgRJWgWWJE01wTOnc+sGRzUIywHRnNqtAGyzRA2TlqV1ayhU3IzZiUPbYOGFE4o6XFhxlHPRlj3QuZB9mvuIGVPilG6nOEpZvo+ofdGXFwMm5Y3lJaRf2LiQzxOTCdEvU/1JkqDX6yFNi/9S9Hppfh6nIC2TbBcZACy0+F9wxlkY6lUSs2P/XrxwXLl3yVHF5SH+qPsq4FY+BpSgaQZAtNT56AnVMn50PxaYuj52H30WZ65+MbOluKJEXhaVZB8HLgcCrlmilzgxolKBeguVM3WuHndR1dvaBoJBikuYwTiUC4ozX1xQTNoGIy8ucfIVPMeMWUptU15IDdvkgrbPI3vAtkHiVJA6qlz449hS/qr1+9lTAGnaQ9pLM9EfGMDAwAB6AwPlQCBBMZiv/AFZRvDQ5P6q/i2by8cfvud9OH3tuvLv3/3Hv8O/P/AdCtAI37FzQNEafgJ3cfkwKHnr9wSYhL7+AEC01PlUEWgRP7ofC0wTHzsOb2HX51IPoAomvV4PaS8LIAMDg5lvgSpACVTHjg3IAoIRf1EuM+KU46miLcplgqMtE5wcXQhB6iiKKktBVmgBOd9HwiFtKHA0Lki9KQ7lguIQ/yZxqnwy/AohC5XGS8WpzEvZaIkLVagqXuR6a/wqXKi8lDiUU6VfFMeX9guVF65fVLzAwgVpv9bXSkcMp6I617Q+onAqqopImEq/kM4XQhDHi489cf9tSJLsuv9AbwCDQ0MYHh7G4NAwhoaGsoFAfg4LAenJMQFg9wvbi5YEeNVt5coxvOcnr9XWX3XVRXjllRdG8eHafTmJPwA1A9C28Jf4oZ+Us5uI0DEMuNViu/jR/Vhgmvo4eHwPjk8bvsyVZI/6ZYI/gIGBQQwNDWFwaAhIgCNHjlQ10AJyJRSV4OV1JkFVEhwSENVZVCX4dBZZBWR+lmiajVHRkDF1waeiIuTArs10LTNKDaeimRVqyqnES4lQCoXKCzeAqQS0whQURzkWEIX4KDiSgFaNoIMSXqhtXJQ1Zzit+oWKWVRaKlu0gYq/1kfUQUu1rOFUDSS8MPwygzlR8uI5gCHHheL42tOP34dd259Cr9dDLxf/kZFRDA+PYHhkBENDIxgcGsLAwCCSNBskqD6eO/I0SKtr29jYML7ve1+prb/0knNw0UVnNffh2H3ZiT8Ai+SGvPQn+EmCeBkA0fDAWXCrxXbxo/uxwETxIeQsQPnIX5IgSdJy5j84OIyh4WEMDY3kqcQUH/ubv8T0zIwUkPXgiFKoskUqGpACaykVUkCmosEE5BJTFyoOR5o9q+JEpoHWAQxpBxUKFbOa6XoMhIg4aLNEtQ0SL1Bmz2WtNMEpBVbilAgoFbnKscwp1GWdC1XkIC3LPvnZc1l5fTBH+1rJBTOAKXkx8Ev6mtovRLlv0X61PepAqDpGruv9Ki9cv5AGMJ62b9d2bH7oTvTyVH92rg5heGQEo2NjGB0dxcjICAYHh/IsXg9JkkqPBk7OTOCpfQ8F+WVNAB+75Zu47rZbtE23bXkKH/zqjdh5+HAjfPvmMO5CsBv7CdzF20dWLHTSHSVLEOcmQNHwwFlwq8V28aP7scDEEn8A2H7oSbzktCu0zb38JqLBweE8lTiCkdFRDAz1MDoyhp3P78A/fOI6vOvH31POgAT6yP5fiE2xXpSfKc2W+3LgzYO16JOg3S8Cdr8Ktv3MRxWAi+UioArpv2pdjpO3W8IsRUWgXwHLbRKQ2lGJc7EPwSr8lwJTYEPGLrbn7Si20/Yjbz9Q+UdfoI8Kh/JXFOxLs+dCQBXxLUWH/uqDEnVgwF5GUQYQBZZUb3XQVpYnv16DCbXesvgWfNB9tQEVOQ7F/tV+tIwgdVFFX7BY/LIi6kLBlfpKWUjiUzomAIBqBn/0yH70etmlueJcHRsfw8jICEZHxzE6No6h4REMDA6i1+tJwl8sP7jzDsz2Z9HI8qodmZzE8RMntM2TMzM4pN7EZ7Hf/fTfYWiwkphndu92uG8QFwN2XYTib/poq4/Nz9cAjSYaHjgLbrXYLn50PxaYmOIPANsPP4UDx3fjlLHTy3VJCiBJkKY9DAwMYGQ4m0kcPLIbq8bXIBU9CACPPvoQfv0D70e/P4d+X1h7Uuispq61cqzdTg2rF6AulfNFZnUrtFBfOo9vMftD8Vm2JEnQS3tI8+v8xUB9fGwcY+PjGB4awsjICIaHhzEwMIhefnNgJvxZfSZnJvDE3vuaVaiF/vbo9m3e+MtZ/D2tlRcFNRsAdOLvhR3VBwNx745v4Hte8mMAsoAiRHYJoNfrYWBoEIMzQzjRn8Apq9fjaHIkL5diamoS04PT6M/NYa4/hwT6dUWrY++taoG4xzTW4ESHceN6vaFNSD/mAovVPPiNdAS8QReCsZiD4OL13GmSIO31MDA4iOGhYQyPjGJ8fByjo2N5+n8MvTTFwOAgBgYGpNR/OfvfdWez2X9LZJ67fgMGe70Sf9ehAzg2Ocm478Q/0KINBOpfAujE3ws7qg8DxHOHt2D30e04feXZ+Zokv5s4my0cnzuGtStehKneJASyJwMGBgdx4sQwpqenMTc7Wz6CJAe5oLOL/NRvq0+QNYlu0wBdZwBQd2BjLBnYBmvpOu2xGUd705jacGA4X+IR3LOFtqBYUj7T30t7GBwaLGf/Y2PZAGBkNLsJcGBwAKLfz77QQl8LiOzNf0/s49/bH1bP+Pah//QL8mOAn1YeA0Qn/hu/CdMAACAASURBVKX5vuvZz7yx6r0KuBN/L+yoPhwQ9+z4d7ztpT8DAGSGkGJq9jjGBschEoFeOpBnBvK7jKdOYGZ6GnP9uWwAICw1rSGufes+zpwg4zq8DkmivPPAywS7KJdoPtL3woi8f1BfVI+fd4zSUx61cgjO/f37ULWq7nET9s1scVupagAw0BvIMgDDwxgeyQYBoyOj2eN/+Y1/vTRFf3YWSf4IL5Cl/r++9bOYqzv7N1Tvl699M17YN4Gf/oR8WeGNF74EP/WDb8Hf3n4Lnjt0qJ5PyX0n/sSaDgBq7U8HABTAPADoxN8LO6oPZ5wT2HN0B+7ZcTNeedabspUJMDs3g7m5WQwPjaLf6yPJ3wUwkD9iNDU9hbnZWczNzWY3kBVZgNDKibjpUd5LnXywJekeNnVm9hceOC6Q6vOvMQcnnFDXOT71j6nwE39tYBcwTCjHGP51pJOssLbpAxpuFbuPw3ppD0maYnBoCIP5IGAof/Z/cGCwfO4/e/sfMDc7g97wAObELL6x7QuYMD0G7DJL9aYmZvB3131VW3/77Y/hskvORRpw2cu8uRP/xWBhlwA68ffCjuoj4ER6cOdtWD2yDhec+nIkSYJDx/dh9ci67M73Xj+fSfQwODiImeERjM7NZQOA/hzA3e1cuG8illqgDD67Qj2SUsJxfHz920uGDk6EBdOdgQkYKkhF+f2MmUeyc9/bJaf6gTNn+IqzY3DnOSjUi2Vrqvey+PPdN7TZZAmyV3KnaS+7X2egh8HBIfQGBjHQG9DEvzhWc3OzeGT/Pdg3scu7bpI5qjd5Ygq33Pqwtn7Llp3Y+vQu4CzHAKAT/2DcgO/vLtDHgDrx98KO6qPGiXTbti9hxdBqYA44Zey0LA2fZkE1eytgDwP9IQzm1/1F/gRA4cwUe02BMqyZTIbBX4X5tewmn+knmCbUOWa6oLgErBTdkHGDXdUsOEr9mFmsh3dLndz72XcThvXeAPpmth7uPkFXSwMj1V+N81Io/qt3dmSX6tI0vxkwz9L10h6Q6jf8Tc2dwC3P3oDdEzvslTBXLrjudfHf95d/gjStpOTY5GQn/ovM/AYAnfh7YUf1UfNE7Ys+bt7yr7j6nB/IXhoClM/kowf0RB9CCPQH+vlz43PVs96edXG1rwydumLYYBm//nP9sA3mwpI+NpiJeg1OgmCLAVr9oBOQO7Cem17DgxoDO/lZe799jGu9abLV23MmbxgssMMQIcryQohs6pdUjwMWX/7LhLMSfwHg4ORe3LrjBkzMxE/7Z5vjiT8AHJ44Fg//ZBf/ejcBtvIxIBm4E38v7Kg+Gp6oJ2Yn8fWt/4rL1r8aV2z8bvQGeiS4Zu8AEAP5S3jy6/5hF7N1saTG92XztMvs2aNOgYMTM7Rb5OTZc93BiZZ28EYJ16aagxNX27QmBEdPbZ+gwYnnVmHKmoR3K8+N4YOTJKkeCQSQXV9nXvSz5eCjuPv5mzEn5mw1M9s8i39U/JNd/OfXqhdIwJUB6MTfCzuqj4gn0sN77sSeiR14w3lvzy4LoJplCZLkobMSd10CArfwKmXGq3UTGhFL792X/uBELuCXLViSg5Ng2HkenFjjg2UNc1lIQOCZw0/i0b334uDkXu9aeDhWNnfiH91HIK5j/t/aEwLmAUAn/l7YUX20cCLtPfY8/u3hj+HCF12Oyza+BiuH11Zo/VBETdFsf7q3GFfXnaIt5sGJMl9e5oOTYDcn0eBEqCsUm+lPY/O+B/Ho3ntxfOYYW8bbasSUtWtW4M//7H1s+bPPPg2f2/xAI3xvWybin5sq0vPyCk1+ACCQdOLvxo7qo60TSQCzYhaP7bkXj+/9Ds5b91JctvG1OHV8A5LU/9bT+TXhmkzx+/j/6d7SDU5CvUqraw9OmpzDS3RwMtufwf6JXdhz9HnsPfY8dr+wHdP9qRAwmwvLZr7Avz70HQykPXbbg1v2YO8LRxvhe9nyEv86ct/ex4AERHxd6MS/Fn4jH6oICoGtBx7F1gOPYsXwamxctQkbV2/CxlXnYmxoZT0frVhS/D9sH/8/G1g3ODGVEgU3SfFdCVHeYFrKnfTRoEI4q+1VueIvUeHmeKV4KjjSp4mLfWn5Akd64VW+TD8eJK2T95PqjeLDTbZ6ZDiz/VnMzE1hem6q/J2aPYEDE3twYGI3+tI7IRY2pmw/eLBV/KbYjf0sNvHPrG6IavSNgOYfA/KxTvxr4Tfy4djt2NQRbN73ADbvy9J548OrMDa4EqOD4xgdHMfI4DgGe0PllJANjEygq+Y1JHASCbAHe+i4in+6XcU0Bv1yH2Z/tX2qPyFtlbC0fSziJLfbwIWgJTjxMgiadLD1Npj2oayhT9rM1N91rE1WcdySae2Pi10tLqG41aY4e+BfsH49xgYHjdu37t+Ho8zXBn2w5aInh/gHtCP6lwLjfA7YZkv1JDJgR/WxQOLP+Tk2dQTHpo60hh9QuJbN0yjd7Cdi12Z9RD51hHxitmKd+PvhR/WzwOIvIPDWl7wU3775UXb7xRefhcnVM3jyBPNp4OUs/mGPAdZ9ZFCrqToAKIDjZACW6klkwI7qYxGJ/6LB78S/E39/B534e+BG9eGJPz09g7/8q8+zZX7ix9+E4ZetDsbm/ATZYhZ/s9H0vo/oBw8MTELfPAOwVE8iA3ZUH534N8YO9tGJv+6jE38Wu1pcQnFrkYh/G9iN/SwB8Y/6LcAc0qeQaQDQLAOwVE8iA3ZUH534N8YO9tGJv+6jE38Wu1pcQnGrE/8o+EE+2p35NzHvoUSSJEn8mwCX6klkwI7qoxP/xtjBPjrx13104s9iV4vzE7dWj4/jbVe9Dq+/5OXo9bLH7nYdPIDr77wV9zz1hN9rnzvxj4If5KMd8V9UHwOq56QT/1r4jXx04h8d3+mnE3/eRyf+Tnzq549+6udwyTmbpPXnbzgDr7/kZfiNT34Mdzz+SDCu6qORdeLfGNfbR3NZr4UQ7xJAJ/618Bv56MQ/Or7TTyf+vI9O/J341M95p2/UxJ/a97/ytcG4qo9G1gB/aGgQP/WeN2vrX3bZJrzmNRd34s/awnwMKM4AoBP/WviNfHTiHx3f6acTf95HJ/5OfNXPpvUbrLuec9r6IFzOR21riD842MM73/l6bf1FF52FKy5/cUA1lov4t/reXyt080sAnfjXwm/koxP/6PhOP5348z468XfiR/WzyMUfAD757TswNzmjrb9v+zP48Ne+gsOTkx7VWD7inxdZkLeyDyT8Cwj8MgBL9SQyYEf1sQjEf1GdRDWwg3104q/76MSfxa4WFyZuzZFX/3LW74f3k8Ug/gBw6PgEpif0AcDx6RnsPXrUoxqLKG7Nj/gvkPw3uQSwCE6imNhRfXTi3xg72Ecn/rqPTvxZ7Gpx4eLW/VufwszcnBHm3qeecOK6fARZ2/je1VhEcWu+xF+3pi/98R5O1HsR0CI5iWJhR/XRiX9j7GAfnfjrPtrST9GJvw++y8+RiWP40t23s9uOTh6Xt3Xib9upHR/zLv7WmwBbyw+EvwdgEZ1EMbCj+ujEvzF2sI9O/HUfbYp/m7ZMxL+wv/j8Z/Clu+/ANZdejoH8PQA7D+7HTffdgxMz00bcEB9O68S/MW6wD6ZIgzcBzuPXABfhSdQEO6qPTvwbYwf76MRf99GJP4tdLS6uuLVl53PYsvM5b9w6PiwAreC/7+prMDSX4F0fv1daf9WmTfj5H/xBfPquu7B1395mfk4i8c8tJO0fLSPg/7jfIj6J6mBH9dGJf2PsYB+d+Os+OvFnsavFJRS3lqj4A8BgkuKT//smbf1DD2zDfXc/hYG0kp1FFbcWTvwXz02AGy49XR8ULNWTyIAd1Ucn/o2xg3104q/76MSfxa4Wl1DcWsLiDwHMzMziK1+5V9u09eldeOSRZ5v5ORnFP7MFGQJoYr/ytJXyuqV6Ehmwo/roxL8xdrCPTvx1H534s9jV4hKKW0tc/P2LLqK4tfDij/xp/Hm/EVC7B2BweKBytFRPIgN2VB+d+DfG5nyMDg1jdGjIiD85M43JqanGflRcHxsdHMTwwKBx+9TsDI4XN2/VwPe14cEBjFrrMYvj00w9PGyo18P48LBR/Gfn+jg6daIWtmSd+HvhRvXRiX9j3GAf8btfux8DGhofyjIAS/UkMmBH9dGJf2Nsk493X/Nm/OjVbzKW/9y3b8VHb/x8Yz9kg7e94YILcUa6AlNT+ktORkaH8MzUEXztycdq4/uYgMBrN70YF42fiokJXYiHhgaxF8fxhYcfrIV/0frT8YYzz8eRIxPs9oF1w7jujttqYZfWib8XblQfi0T85wN/iYq/6Qa/kKxA8OBAGwAMjAykS/YkMmBH9dGJf2PsWj4aWMy0/2/+9qewZctObf0lLz0H7/m1H2iMbzPajj/9s8/irrue0MqcffZp+NU/flcDH8An//4mfP4L32a3f/oL/702duGgE383blQfnfg3xg32MT+hrbFpA4A0TctRxJI6iQzYUX20KP7XXHo5fv0d7y5XTUydwDs/+NsAgJ94w1vw49e8pdw2MzeLt/3Bb3jDX3PJ5fiNd/64d/nv+R8f8C67nMQ/0HFkOEndWrGFvOY/kPZwzQUXGnc9MHEMDzy3Q1p30frTccaatbwjAE/v34+n9+9zVus1m16M8WH+stPsXB/ffOpJifMNq1fjkg0bjYfhwMQx3L9ju9Mvqapls/8xufCMszDYMz3ZXdnUzDS27Hw+GF8zw64jw0P44B//DLvtrLNOw78/92QjfHPxJSz++tw9dDZf6xFBrbf0BrNnNOZD/KP6WMLi36q1id+J/7zgn+ziDwADvRQXr3wR/vTPPqtte/sPvwZXvOQsdgDwqnM34es3P6/ts3HDGHAq/AYA552HP/qdf9LWv+qVF+Lt73gdvrlZFqyNq9fg2osuxof/7LN4fucBadsZG0/BD/3k1X4DgMgx5U9/9v/C2hUrneW279uDn/jQ77U28//Cww9ghNyjQv1sfu4Qnjt8qBE+X3wJi39mC/MxIK0WaZp04u+P38jHEhOiGNid+IfCnfziX9jExAn2ssaK/UdxyamrccqRw9L6J7+xF7tHx7D2B/RLHgMDKdDf7V1Fzu/WrTtx511PYOs+eRDxzOhW3L7qO3j44W04fly+IfX88zfih3C122HbaX+n+/bS/s8eqAZFi+py5eIVf/Df5Gv/qQB9ANBL/F8O5Gud+Afj1izacKd2sU968Y9sy0n8bXbqJHDZ5BAuGzpN3jAFYEpgzXnnafuMjU5gx57Njfzu3/8C9u9/QVt/EMCz2FUfeIHFv5EFxa1O/ANskWQAYg8ATkLxHxkcwmsuuqR8XO3o5CS+/eSjmJmbrY374LYt+LW//3j591x/Li8qcNMD9+ChZ7ZWu7uCtrL5oWe24jf/v+tCquON7b/bAp+okbrdyy7bhB079klPAqxYMYJLLz0njoPcXOJ/8UVn4tFHn8GxY9WTAMPDg7j85boYGn0Y+tGmTafj1FNXaeL3qlear9HzDuKch+e/6FS84fzzjduPbNqkrevP7QH2NHYd3zrxj4If5GPxi/+CHXd9AGDIRdSyk1D8X3fxpfjlt70T4yOj0vpDE8fwJ5/9NB4kQh2Ce3jiGA5PHFOKZoX3HD6EPT7XzQz4h44dxb1b+BtvFtXd/otc/AHgA7/6Dtzx7cewe3d1PM45ez1+6f0/jFu3boniw2fm/77//P24556n8Ohj1ZvV1q5dgV//tf+Ie559xu3DMoj8kXe+Hk8++RxuuPEeaf2H/uS9eP7oYcNeqoN45/uBiQlsO3DAuD05pJ8bAz3+McYFtZbF/5c/9pflR4Vs+NOznhMVeeeAop34h/oJlN3EsGwqYzTmHoBIGYCTUPwB4EevfpMm/gCwdnwFfuR1b7QPAJbqSVQDO9hHXuz2xx+2Dnae3qM/gjdf4v/Irp3Yd+wYJqfl9wAcmjyO6x95CLuOHGnsw0f8n9yzG8emTuDQpCxyk9MzuP7hB7H36FG7D4v4P3f4EL7w0IPYfuigtu2LDz+Eyb7HC4Yiij8AbNl3AN+YNPvdtG2btm7tmhoi16a1PfMXwLY95ssS3Ut+WvARd+bfZOId72uASRJhAHCSiv8pK1fh/A1nGLe/4sUXYGRwqPqUpyeuXnQRnUQ1sIN9kGJPPLcdTzzn+QiVy0fUbiew/dBBbD90EFPKpZ5jU1Nes24fH+QPoz1/5DCeP3IYx6bkfjY1N4u7HfVwXT46ODGBuye2Yf+xY9q2e7c/g3TAER4iiz8AnP39V+D0K8/FFx6qXm4kIPDGC1+Cyzaegbs36wOA2TNHgJFmfq9+3SV4z0+/GR+79VvS+pdu2IC3XPRS/NZvfwrbd7ifMpgP8W8Nf6nGraUl/r4SHv0+AWYA0NDJSSr+ANDr2YNfggS9Xg9QXxS3VE+iGtjBPpZA2n8+rs91N/wBG844FR/96C/q6zesw8HZSfR3j+VuMh/jZ6/DKeecju9aeVzbZ2RkAPt2+dVlrt9n/a5ZswKnnLYKK54cl9afunENNr14A37v99+DSSUzMTo6hLl+v1rRiX8U/CAfS038M2vjjn/nfnEvAZzE4j8/d/svopOoBnawj078dR/LVPxPzMzgj75+A7/xaeqm8nH9Qw/i+ofqvfKY2l/e/O/S36523LdjO+5bgOf85xV/qcatRS7+i+0mT13s614CWAbif2Rigk/v53bk+AROTJPngpfqSVQDO9jHUhR/ERdf87FMxd+FXS224KOtuNWJfxT8IB9LWPwT92x9fr4G6FER3ZaB+APAiZlp3PrYQ3jLy69ky970wL1V+m+pnkQ1sIN9WIq98bIr8NqLLzVuv2vzY7jpAf1b4y7cUOPE//Izz8KFLzoNDwzdhxOoHr87ZXwc//GK78ITe3bjoZ36W+m8fATU/ZING3Hpho3Y9tnNeAHVo3rjQ0P40VdciS379+E727OnA0LF/+y16/Da816MT955ALuV5+h+5BXfhUMnJnHzZvLSnE78nbj65ubi30tT453jQvQx25d9JEmCXmqe2wkhstjFVM3kS0AAQmCWXvJw+RLEl4ctB/HPNnnLbrQnAABmAICEyQrYbJmIf2Gf+PoNOHj0KN788ldgZGgYAHB08ji+dv89uP6eO7xwXT48dgosvjTEHwDOO30jrrn0cuP2g0df4AcAbc/8AWxctRqHnz6M2ek5afPUxDSOPnMEp69f7T0AaDLzX79yFSafn8DUhJyNmp2ew8EtB3HmOWvwHTxba+a/ZmwMycEZTBzUr6s/9+AuXPa683Fz8X6dTvyduPrmODP//3rtW7Bji/6ig1PWrcL6jWvx21+Uv5j58jPOxLtf9Wo88cQOHJ+U3144NjqMoVNH8LFvfZN1+f43XYtDz7+AE8pNp+PjIzhn03r81vWfk32deRZ+/FWvxrZn9uDQIfmJlDRJsOqMVfjot77h0dTlIv5YoNcANX0PwDITfyB73v+TN9+IT958Yy1ck4/LN50vffDHshNm5mbx3//hb719XHTm2XjX1deW62bmZvGH//IPAIDXXXyZltH4H//0CS9s6sOzYDs2D+Jf2N9ed6P2qdydOw/gE5+8Sf8aoI+PmnX/p3/+JnYq76E/cmQCH/3Yl/Grf/yuRmn/G268Bw89vE1b/8EPfab6GmAn/k5cfXPctP///Ysf0YpcddVF+NF3XYMjO+SXOD1/Yi/uG9yCP/7g/8Hzz8v95vzzN+J//OlPWV3/3h/8I7Zv3yutO/fc9Xj/L/0wjjyn+JrKfP3dJ76G++6T340xODiAT3zmv1l9ActM/DNbHG8ChO9TAMtQ/Jvi2nysHl+By87xe4vbjOeLPAof61aswmsuuoTd/8xTXiRtC7XlJP7RfbSvn605mA/xP+tFp+FTv/qbXrtdf9ft+PPP/XMQfvYn344bfvdPpfd9/OgHfwe7Dx3Af3j11fiVt1ffHtiy63n87F/88byLv8nuuusJ9rsGDwP4HL7ZrA6KPfPMHvzKf/24tv4RPIbPN/C1DMU/hvwndVCYdL/HTYCd+AfjRvERYIvKRyf+uo/2D09r1s387bj65vkR/5PBlqX417MoGQNN7BPXPQCd+AfjRvERYIvKRyf+uo9lFNC9bamKv9NtJ/6+tszFv81LAEZs/RKArSKd+Afj+vrYvm8PPnP7N7zwiw8F+fp47sA+fOa2Cnu22F8Aj+94VtrmY534N/CxjAK6t3XibwJYFDYf1Vjm4m+78s5t8HkSwMv8nwLoxD8YN8THtj279Hd5B1bL5GP7vj3431//Mov90DNbpS8N1vXBFFz0Vkf8v+et34VXXKF/mW7DhnVuHxE5uea7L8M5Z5+mrV+3bmVj7CuvvBBDQ4ONcbzMIv6Hjh3FX3/p37xgnt5lePqiZtz6+I3XY2igCo9H8+8uPPzM0/jrL362XH+YeWVyiB+j1dx9w4Z1uOiis/Dwzuek9WtHx3DOKafgzruexLFj/39759ZryXGW4a/33uNtBxgnEljy2I4TBIjIKAqWQ3wTpIiTuEDiEsEd4k/wA5C4JfkPhAsunMCFhRIR4kQCRYqdBBScMTOOk4w9TpzYk3hO+9BczOrV1VVfdX3VVd17HZ5XWlq9V1W/b69e3fupru6uvpOxGPEFefTRX5Jnn/2NaNZ3/vv7cvNm+iFm+w5/EfFbAItdEKj0AKg3fHp/Av+a8C/1z86Y/JXn31G/ee17cv/UH0u513d/8P1oWY6mwP/qj9+Wpz55Ra7IFbX8/34yHBd+Lvhff+cn8tGP/ap8/GMfUstfvfnWZO+bt27Jrz1xRT7+xG8/+KAdLno3vkAVJY78f37ntvxzZu9UzD+WEdMX/vMl9fNrb92Qa2+GD6SamhMxMPl/+tPheBnPPfub8md//nxwG+AnnnxK/vL3npd/+OwLcuPN4YOerhgar5987rfk6aeHDc6nP/yY/PXf/MnwNsBW5BNPfVj+6lPPy+f/6SvyyreuDea5dDR8WiHwr6LJjQf3ysFGRJo//ts/eObSI5fcp24MBPzTvsUZewr/pXaiXej2b30yz6F2xu7ybe32n+t/Sqb/pz7y6/KB44fUOqdnZ/LSa1cHnz32K5flmcf1hquIyLu3b8vLzvDGA/h/5KPyy8fHkaxzeem17w2W/bHLl+WZK4msN964+P8pFfyTGUbvn11/91uv/uvVr4jI2ep14rzuOa87InJ39d5Nd697InJ/9ToRkdPV63zleb5aou6lXgPQnwIA/tm+xRnAv6pvMgf4x0KAf8I3LF4G/iIi//X6tZGaod6+dUvevnUrWU/7Dt94/Xo6wJnNknXh/1Mq+CczcrxLH8I35jwi7Xz/gxmAf7ZvcQbwr+qbzAH+sRDgn/ANi5eDf23v4oy5/m/tC/wlOgT/7NcCKA8DkgPgn+9bnAH8q/omc4B/LAT4J3zDYuBvrw78dVV9FoBZYQOgHQ4EBPzTvsUZwL+qbzIH+MdCgH/CNywG/vbqwD+q8BTAHHcFBD7hQEBOJeCf9i3OAP5VfZM5wD8WAvwTvmEx8LdXB/6FmuV0QNAAaNv2UAT4W3yLM4B/Vd9kDvCPhQD/hG9YDPzt1YH/pkq7CPAA+Kd9izOAf1XfZA7wj4UA/4RvWAz87dWBv0n5dwFU6REIbgNsm7ZeVwPwL/bPzpgT/pu+E8VygH8sBPgnfMNi4G+vDvw3XdOeBmgR8C/2z84A/sDfHgL8E75hMfC3Vwf+mRmlB96T5tcuAixvAAD/Yv/sDOAP/O0hwD/hGxYDf3t14J+dkYfv2B0C2XcOxAcCmirgX+yfnQH8gb89BPgnfMNi4G+vDvyrZJTJzHB9IKCpAv7F/tkZwB/420OAf8I3LAb+9urAv0LGokMCq3cBTLIH/sX+2RnAH/jbQ4B/wjcsBv726sC/JCMyFLBVk+etcwoA+Bf7Z2cAf+BvDwH+Cd+wGPjbqwP/ooxWZAS7c4wIuFZ5DwDwL/bPzgD+wN8eAvwTvmEx8LdXB/5FGf1Hc3T9Jz21awDsCwL8i/2zM4A/8LeHAP+Eb1gM/O3VgX9RhvuRnbozPwzIOg4A8C/2z84A/sDfHgL8E75hMfC3Vwf+RRnhR43U6QXI8pg2DgDwL/bPzgD+wN8eAvwTvmEx8LdXB/5FGXPv/xnKvwgQ+Bf7Z2cAf+BvDwH+Cd+wGPjbqwP/ooyId5N/7F/lmoG8iwCBf7F/dgbwB/72EOCf8A2Lgb+9OvAvyhj1Hm0C1Do9EMh+ESDwL/bPzgD+wN8eAvwTvmEx8LdXB/5FGSnvuYb/Sch2CgD4F/tnZwB/4G8PAf4J37AY+NurA/+iDJv3hTQB0qcAgH+xf3YG8Af+9hDgn/ANi4G/vTrwL8oo8441CqrdCjjeAAD+xf7ZGcAf+NtDgH/CNywG/vbqwL8oI8O7yRl/J2IxZab4KQDgX+yfnQH8gb89BPgnfMNi4G+vDvyLMube/ytJ7wEA/sX+2RnAH/jbQ4B/wjcsBv726sC/KGOSdxP9Y6qJRWEDoI3fBgj8Z8oA/sDfHgL8E75hMfC3Vwf+RRnz7v+WawKyZB4ICPjPlAH8gb89BPgnfMNi4G+vDvyLMsq8fe4ucleAMg5A+CwA4D9TBvAH/vYQ4J/wDYuBv7068C/KKPXelIcB+c8CAP4zZQB/4G8PAf4J37AY+NurA/+ijDreGzMOwHpBgP9MGcAf+NtDgH/CNywG/vbqwL8oo5L3BQ0EGB8HAPjPlAH8gb89BPgnfMNi4G+vDvyLMmruNvnjAKSeHWCS2gAA/jNlAH/gbw8B/gnfsBj426sD/6KMyrvNiN2snQPaBX/lgcC/2DsrYwt2IjUH+MdCgH/CNywG/vbqwL8oozr8TYY1ewjWynscsEXAv9g7K2MLdiI1B/jHQoB/wjcsBv726sC/KGMu+OedAsjpxbEOlgAADSVJREFU/h/1NY8DYBLwL/bOytiCnUjNAf6xEOCf8A2Lgb+9OvAvypjxyL8Z957tNEC9HgDgX+ydlbEFO5GaA/xjIcA/4RsWA397deBflDFnt38rUy4C7FT5aYDNhAYA8C/2zsrYgp1IzQH+sRDgn/ANi4G/vTrwL8qYG/51NOmugPJTAMC/2DsrYwt2IjUH+MdCgH/CNywG/vbqwL8oYzH4NxszEJC9BwD4F3tnZWzBTqTmAP9YCPBP+IbFwN9eHfgXZSx55H9BIwEdeX83Ym0AAP9i76yMLdiJ1BzgHwsB/gnfsBj426tvAPwrbBZ7Af9yzf80wIGAf7F3Vgbw1zOAv+rdTwL/RfyBf1VvU8buwX8KxIvnyT8FAPyLvbMygL+eAfxV734S+C/iD/yrepsydg/+VjVSaQjgTnkNAOBf7J2VAfz1DOCveveTwH8Rf+Bf1duUsaPwn+kSgKSt/RQA8C/2zsoA/noG8Fe9+0ngv4g/8K/qbcrYUfiLSO2HAZll6wEA/sXeWRnAX88A/qp3Pwn8F/EH/lW9TRm7DP8LlNIAaIafAf9i76wM4K9nAH/Vu58E/ov4A/+q3qaMvYC/ekA/+82B46cAgH+xd1YG8NczgL/q3U8C/0X8gX9Vb1PGXsBfSoYCLlLQAGi6z4B/sXdWBvDXM4C/6t1PAv9F/IF/VW9Txr7AP1/VGgtdA6AZfAb8i72zMoC/ngH8Ve9+Evgv4g/8q3qbMvYM/k3P4EV7ArIHAgL+lTOAv54B/FXvfhL4L+IP/Kt6mzL2DP4iYsF+rEblpwGOGAL/yhnAX88A/qp3Pwn8F/EH/lW9TRn7CP8HWvoagEYkowEA/CtnAH89A/ir3v0k8F/EH/hX9TZl7C/8xzRlfADzPP7DgESURsFGwn9SWwH4F+UA/1gI8E/4hsXA314d+BdlbAX8A2ZbIN5Eps1KDgQE/CtnAH89A/ir3v0k8F/EH/hX9TZl7D38JXUbYAru8zwNEPhXzgD+egbwV737SeC/iD/wr+ptygD+nSxH81nd+xZFewCAf+UM4K9nAH/Vu58E/ov4A/+q3qYM4L/WBKpX6RVQGwDAv3IG8NczgL/q3U8C/0X8gX9Vb1MG8B9mNHP/09KlXfA3rYsB+Ff1NmUA/9Af+Jv8q2YA/7g38J/FP5qxjfDv/2+Vdu9nz297GmBKwL+qtykD+If+wN/kXzUD+Bd5T8oB/vsO/+pDAfcLdN6+LCLnZgfgX9XblAH8Q3/gb/KvmgH8i7wn5QD/XYS/SNNc+MOAGhGRf/u7L//j6b3TvxCR28m5gX9Vb1MG8A/9gb/Jv2oG8C/ynpQD/HcT/hcotbv/S3//71+8/dPbfyStvBmdE/hX9TZlAP/QH/ib/KtmAP8i70k5wB/4uxaVFD3f/9XPff2Vt6/++DPtefvtknjgXykD+If+wN/kXzUD+Bd5T8oB/jsP/2aY2k5cgux53AaAP3P7zc+/cuO7L776p+en5y9OiQD+lTKAf+gP/E3+VTOAf5H3pBzgv/PwFxF3JMCAw85n/vRUra83OJTh6EKN/3rvxq2zH778o3958nefuHx4dPicNQH4V8oA/qE/8Df5V80A/kXek3KA/17A//z0/N4bX/vhl++8c+e91RKce68zETl13rvXifd+5r26+VvnNZDfABAZNgAORKQ5u392cP3rr3/18d95/J2HPnDpM5K4VRD4V8oA/qE/8Df5V80A/kXek3KA/17A/+ze2fuvvXjthXevv/eW9PDv3l2Yn8o4/N0Gggv/rAaA3wNw4LwO3/jGD/7ng09+8NuPfOiRP2ya5jj6ZXME/GfxHs0B/rEQ4J/wDYuBv30W4F+UsWPwP3n/5KevfvHqC7+4+f7PpAe0f+QfawD4jQAX/sUNAHf6wHk/uPGdN390dHz0H48+fvn3m4PmcvBlcwT8Z/EezQH+sRDgn/ANi4G/fRbgX5SxY/C/++7dG//7wtUv3H3v3vsyPOp34e82AsYaAG4PwJk3r9urECxN7Oh/fdS/eh2tXpdE5KHV61hEHhaR4+ageUREHpZWHm4O5OG2lePmoDmWVo5F5JI0cqnzaJrOszmUrnHR9Nnr8RD6z/rrI5xHJg6GTehmcj5rlD/UedzZ3MEYusjB8AxNmB8ZwKHpFsC1DNyD8kba4fcYLKeS1CjL3Nds15Nr72G6uhq88jB1vT6b9Y7kPc0y/D1GfLzlCMuddaL6uMs0/N2VcvezxOAbzvccfOpNNZHf06nWiKz2vvQ8yrY6XOjBZ62zrUm/hzfaihh+MrLdDNJi61tdOmdGZX0HdRt/oboPW2VjVZdF+Z6JzNjSj++o7rY4kq0tZ+vt1NHv4f17Vre74ZyJ9aFK2+dG96mRxHB70Tc20342muQX6us+tlV6Xv3/xfW/r8g88W1p5P9VZ9zq85zcPvnFtS+9/rWz+2enqwKt278D/ImI3F+97onI3dXrjjPdve45dd3rAk5F7w1oj8JvMVA78lq3Utrzdt1Cac/kVEQO2/O26znwvdwWTlenEWVQoh3Urn4vhNBy4v/I9kvjqd/t718MGOvWd4/u/aN8rQ9i/VmsAeB3F2gLqi1w1xDoeg66vzvIaxld2blTZ9M28E1bHoR2UexnaF/kMlY7BZDz0hoBape//5nfAPA7oGOtFO0WhSMZwr87jeDC3/3Ch6L3ACjdhGjPxG+O0AOxL+ymYg0A/4A6dmuf1giIneuPXuWg9QB0jQAN/rGuCv/I320AaF/6cOXhNxJiPQDsBPstfn+0yWL7RLlKNQBiF/dp9/jHTgmMnRoQEZGjtm3b1YVV7tG/P62dp+i6+H34a0f+nc+R43Og1NUaAOxc84r1izSxXaC5ta/bmNZVr/Wqay/tSn9LL4B4n7Ui8WsAtIU7UBayW9DujgH3lkHfx53XbSj489S8DmBfN7B9EL/t/orfHvnapm1iagPAv9dfOyUw1gsQKHYKQCQ8DeA2Aror+E+9d/9IXiTee6D1FoydBlhK27Qh7bP4nbZf/Ia7K37bcVmuAXDv9+9eWm/AlLsDRGT8IsBYK6W7Yt9tBHRH8Bb4H8mwxyCnAcBGtTnit7hYsf63T/xmm6+5fyMfyP41dv65/hPlpfUGxK4HcLP86dFTAO4MLvz9BsAYuN1WTdf1310v0DUCtMbDRfYCsJMuK9b3smJ9b7b4fS5WSzUAumn/ANs9t++P+jfWA5Aa/lc9FXAkIuJcCKgtpHYq4Gz1efduOfLvjv797v9apwDYceYT63Y+sW6XE+t6ObGux6WdAuh6ALS7AbReAK0RYLkGoBV5wP2x2wAHlaUHusgQ/mNfrmsAHDlfxtoAEJlnI2LDrCPW43xi3S4j1vN8Yt2Oy3IhYKwXYKwRoB39R5W6C8D/2z8dcBaZz+8tcC/8i90BsIu3AW7zsm+SWI/1xLpcRqznetq1dTl2HYDbADiX4fn+sQcAaXcDuN7xUwAiwWmArhfAP19xLuOKXfSnXfmvNQBEeZfI32go1k89sS6XEeu5nliXdbT0NQDdu98A8G8JtIwNkD0q4KAHINII8GfUGgGx8w7dArlPFrQe/Y/9EGzs84r1O59Yt8uI9TyfWLfl0noBtGcC+LcFug2B3JEB+/C2bUXsdwFon8V6A/zuf/eK/zH4W87/s+EtI9bzcmJdLyfW9Xxi3eYpdjeANuR+bOj92IBA068BMJwKEBkOCuR/iQPnCxw4791tgyn478r5f5HtX/5tF+t/s8Xvs6xY38vKH1PHV+w8/djpAP+q/9iQwNrRf/wagBFZGwF+4IEMGwAa+FPn/rd9g9325d8H8Rttn/jNLlas/zoauxjQf/eP8nPA72asu/9FIg2AyLgAWmumuxvAPfJ3v4QG/tSRPxf/XbxY57srftvtF7/hdijVA+CWxcYF0K4L0F6xC/9GTwNEewASgwNpchsDbq9B97kG/l0++q8p1gXyxTaxv+K33x3FegHGrgvQpmMX4rveg6N/kcQpgJGeAL+F4UO99d4bGQ4l7AKfI//lxDpFS4jtDGliuxhq7JbAbtqHu9Y7YIK/psZrEOiVho0AEf3IXQP72BF/zlE/Gw66aLENok0W2+d2KtYI6N5jr7Hx/lX4+0f/IsYGgIi5EdC9W6a1d80fIYvYXhDqxf6wHfIBHOsJGIN9bJ7eNAJ6cwNARG0EiKShboE+R/4I1Rf7D0LLyLKvpWBr7Q0QZVp7f/DHCOSzGgDrmeK9AbFpK/T5h4WsYltBCJXqIv6PWO4KcKet74H3GPxFJjYARJK9AdrfHPEjdPFiH0NoMxU7HRCbHr3dLwV/kYIGwNpAbwiI2K7q55/RfonfGyGExjXWEPD/VgFugb9IhQbA2ijeEBAZ/8cPFBBCqEz8H91e5VwbMDqPFfydqjUABqbjjYF1terBCCGEUF3VZNVU4I7Olwv+TrM0AAYBtsYAQgghDoz2Vdkgngp9V7M3AKLBNAwQQgghk2oA39f/A/SLT+C1GaRAAAAAAElFTkSuQmCCUEsBAhQACgAAAAgAmF1sRiGBOQgGBAAAVQsAABsAAAAAAAAAAAAAAAAAAAAAAG5vcm1hdGl2ZS10eXBlcy1jb21wdXRlLnltbFBLAQIUAAoAAAAAAJhdbEYAAAAAAAAAAAAAAAAHAAAAAAAAAAAAEAAAAD8EAABpbWFnZXMvUEsBAhQACgAAAAAAmF1sRqaI/3ih7wAAoe8AABIAAAAAAAAAAAAAAAAAZAQAAGltYWdlcy9jb21wdXRlLnBuZ1BLBQYAAAAAAwADAL4AAAA19AAAAAA="} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/command b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/commandTrial b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/contentMD5.txt
new file mode 100644
index 0000000000..c1d5e6e533
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/contentMD5.txt
@@ -0,0 +1 @@
+ZjcyYzUxNzM5YzJlNzk0YTlmNzM5Zjc5ODE2NDQwMWI= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/headers b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/results b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/toExec b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/02_NormativeTypeCI-compute/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/body.txt b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/body.txt
new file mode 100644
index 0000000000..55b79ccd58
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-blockStorage.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"volume.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/volume.png"}],"payloadData":"UEsDBAoAAAAIALZdbEbQalrCZgIAAHkFAAAgAAAAbm9ybWF0aXZlLXR5cGVzLWJsb2NrU3RvcmFnZS55bWytk01v2zAMhu/+FcROKbC4KXbzYUDbHbZD12HJ3VBk2iZmS55IJ8t+/SjZdZKiGHYYjAACxY/npd6IZ2vKCmtyJOQdlwcMrIcCJF0x9UOH5cn0XXlXbvQ7VpsPWSaocSNYOtPjnLx2PvRG6IBrOQ3I633n7Y+t+GAaPFeYUVofCtg9bx/vYfd4vllm3+Ub/eKk9fbr/bft5+ddllXINtAgKePROzHkGKRFWOZCmgtnPXmm/D4IFxnAGt69zRm8l+KNme+yzPkKy5QVO6TyPMY4f7gQF+9AxwbtWZV18H1xlftdB8wpFxo+phDATiVMy7jsCbEU7BgCOulOEHAIyHpmMMAYdFlrTTcdpDUDz2UVHsgirCjH/L02EeDWBKxu5mm+rpXTNYAHdNqX6TdWUw/WS6iMGIgS4NiSbSGY49L74Lux1wVb42CvcAH12ao8dRbTcDHPIBv13VKvRXw7VeWDa9L1EPyAQQiX9Ijwcob0hgWQE2wwLNGAP0dSGQXUpmNc4jqJJagVhM8t4ls3CS6U0hpl2Sx3V08QVx87I6uORWbkUQB4wsY8nEQFr54ebvK5xSSnpOo1snLQrPFvxFcAXz7Fnes68RexxGeZ2sNKuQWII0f0uLEWmWnfKZ7VBcYyOdPHSjMMHVkTGy+w7MzArZf/hLv1ikaVupBqwgAJ8tKZF0JeRk9Jehq7KrpmZF31sUU3+SemRiHXLl5Na5h1GFHW/Shny/zrI7xetprvoPwKckpTfbAtRgPpXKj15/Cof4rZ2S+GnyjmDfV44TUlM7aNodcg0//fmsHsqaNo9/x+Sc7+AFBLAwQKAAAAAAC2XWxGAAAAAAAAAAAAAAAABwAAAGltYWdlcy9QSwMECgAAAAAAtl1sRhDCxB4DQQAAA0EAABEAAABpbWFnZXMvdm9sdW1lLnBuZ4lQTkcNChoKAAAADUlIRFIAAAI7AAABdwgGAAAAQ6SK0QAAAARzQklUCAgICHwIZIgAAAAJcEhZcwAAGtYAABrWASt/bikAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAF3RFWHRUaXRsZQBVU0IgSEQgKEV4dGVybmFsKYc4zzAAAAAOdEVYdEF1dGhvcgBteXN0aWNh3NYIKgAAACx0RVh0RGVzY3JpcHRpb24AQSBVU0IgSEQgKEV4dGVybmFsKS4uLmVuam95ISA6LSnV7P/rAAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMDgtMDMtMDVUMTM6MDE6MDdI5Wy9AAAAR3RFWHRTb3VyY2UAaHR0cDovL29wZW5jbGlwYXJ0Lm9yZy9kZXRhaWwvMTU0NjMvdXNiLWhkLShleHRlcm5hbCktYnktbXlzdGljYQNSLGUAAABJdEVYdENvcHlyaWdodABQdWJsaWMgRG9tYWluIGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL3B1YmxpY2RvbWFpbi9Zw/7KAAAgAElEQVR4nO3deZRU9Z3//+enll5ZROMSg+w7ssgiiyiKKIsgIihLFBeymczEmEwWs7pETSZ/fLPMmfObJN/f70wyM4mgIuIGLoAKyC4a4zKKggaku2mW7qa61vv743Z1N011d3V3Vd17q16Pczjdtz59q97dB7pevO/nfj7GsixERArBvn1vng18H/gKcAh4H3gPeN8Y3gfz/ujRoyqcrFFEMs8o7IhIvnvjjX3lxvAtMN8Feqb6GmMADMAJ7BB02h9jeH/UqFG1ualYRDJJYUdE8tYbb+wrwu7i/NgYzm8IMyk1CzttjSe7Qc3/PD9q1MXRjBUtIhkXcLoAEZFMe+ONfT7gFuB+oF8Gn/rChj9XNnvsemBdBl9DRDLM53QBIiKZ9MYb+24A3gT+k8wGndbclIPXEJEu0GUsEckLe/e+cZUx5hFgUqrxNC9TdWb8hDGcf/HFF4fTLlZEckqXsUTE0/bufWMC8DBwjUMl9ATmAE+29UVvv/33HsAiYB6wH9gB7Bg5csSBrFcoUuDU2RERT9q7941hwM+xAwQAxmSlc9PuuDFYYHYDzwO7gDqgHvgcMM4YxoGZAZSmOLcC2AFmh/2RnSNGDK9utRAR6TCFHRHxlL173+gD/Ay4DfA3H3Mw7HTx3DPGPwB2GEMyAO0dPnx4fasvICJtUtgREU/Ys2fvuWB+aAx3AcWpviaPwk6LMWJg3oTG8LMDeGf48GGJVl9URBop7IiIq+3Zs7cH8B3g22C6tZFn8jnspBqvNYbdNIYfs2PYsKEHWy1CpIAp7IiIK+3Zs7cE+DpwL/bcF8CgsNPm+GfATpouge0cOnTosVYLEykQCjsi4ip79uz1A3dgz8vpffqowk664w1jFvBBs8nPO4C9Q4cO0W3yUlAUdkTEFXbv3mOAm4wxDwJDUn+Vwk66422MRY2hxfwf8+6QIYM1/0fylsKOiDhu9+49s7DXyhnXVmBR2El/PP0xAFODfcv8DhougQ0ePPjTlAWLeJDCjog4ZvfuPVOAR4DpyccUdlKNdXy8g2En1fhhmk1+BnYOHjzoxBlFiHiAwo6I5Nzu3XtGAQ8B81uOKeykGuv4eAbCTstxC3i/2do/O4B9gwYN0vwfcT2FHRHJmV27dg8Ac78xLKeVjYgVdlKNdXw8C2En1VgE2Nd8ArQxvDdw4EC9sYirKOyISNbt2rX7AuAnwJfBBDsbWBR20h/PUdhJNX6CZvN/gB0DBw48lOoskVxR2BGRrNm1a/dZwPeAu4Ey+9HOBxaFnfTHHQw7qcb/QbPJz8CuAQMGnEz1lSLZoLAjIhm3c+fuMmP4JvB94KzTRxV2zhxLPZ5HYafluAW8y+kToN8cMKB/JNXZIl2lsCMiGbNz564gmC8DPzGGC1J/lcLOmWOpx/M47KQaDxvDG5y+/9f/9u/fX29S0mUKOyLSZTt37vIBy4AHwAyA1t7UQGEn1Vjq8QILO6nGjoPZRbMd4Pv163c41TOItEVhR0S6ZOfOXfOxbyMfZT/S6hsXyXGFnZZjqccVdlKOfcrpqz/v6tevb02qM0WSFHZEpFN27tw1HXvV46mnjyjsdPzc1OMKO+2NAZiEMc3n/7ADeLNv377R1s6QwqOwIyIdsmPHrnHAQ8YwO/VXKOx0/NzU4wo77Y3Z4ynG6iE5/8fsMIYdffr0+d/WnkHyn8KOiKRlx46dQ4AHwdxEm7lCYafj56YeV9hpb8web/OvTNP4MWAnzTpAffr0OdLWmZI/FHZEpE07duzsDfwMuB0IpPPm0964wk7LsdTjCjvtjdnjaYadVA4aY5pf/tp90UUX1bb1bOJNCjsiktL27TvPMYZ7gW8AJU0jCjuZPzf1uMJOe2P2eBfCTsu/Mwng7zTr/hhj3urdu3esrVcQ91PYEZHTbN++sxvwbeA7xtDjzK9Q2Mn8uanHFXbaG7PHMxh2Uo2HgL00C0C9e/f+sM2TxHUUdkQEgO3bdxQDd4H5IXAudP7Np71xhZ2WY6nHFXbaG7PHsxx2znjMsqyjNJv/Y1nWjj59+lS2+UTiKIUdkQK3ffsOP7ACuA/o07U3pvTGFXZajqUeV9hpb8wedyDspDr+mGbhB9jTt2/fujafXHJGYUekgG3fvmMR8HNgWNOjCjsdHVfYUdhJcRwH3qYp/OwA3u7Xr5/m/zhAYUekAL3++o6ZwCPGMOHMUYWdjo4r7CjspHl8CtjTLPzs6N+//0dtFiAZobAjUkBef337JOBhMDMgG29M6Y0r7LQcSz2usNPemD3uobBzxrFlWVXYwWcnDV2ggQMHVrVZlHSYwo5IAXj99e0jsPevusF+pP03l/bGFXYyeW7qcYWd9sbscY+HnVRjH9Fi/s+gQYNOtVmotElhRySPbdu2vZ8x3A/cAviaRhR2MjmusKOw09njNL82Ror5P4MHD463Wbw0UtgRyUPbtm0/H/gR8FVjKDrzKxR2MjmusKOw09njLpxbR4v5P0OGDPk45TchCjsi+WTbttd7At8F8y2gHDr/5tK5c9MbV9hpOZZ6XGGnvTF7vEDDTqrjyubhx7KsncOGDTt6xjdQgBR2RPLAtm2vlwL/BPwAODsTby6dOze9cYWdlmOpxxV22huzxxV22nzuDzl9/s/e4cOHhygwCjsiHrZ16+sBY1gJ/BS4sGlEYefMsabaMj2usKOw09njHISdlscx4C1On//z9xEjRiTIYwo7Ih60devrBlgKPGAMg878CoWdM8eaasv0uMKOwk5njx0IO6mOa4HdzS+BjRw58iB5RGFHxGO2bt02F8zDwBjI3ptL585Nb1xhp+VY6nGFnfbG7HGFnaw895GW839GjRp1DI9S2BHxiK1bt00DHgGm5eLNpXPnpjeusNNyLPW4wk57Y/a4wk72n7vh8//l9Pk/b4wePboeD1DYEXG5LVu2jTGGh4G5TY8q7HT0XIWd1sY6Nq6w495Aks3nbmUsCrzJ6fN/3h0zZozr5v8o7Ii41JYt2wYBDwBLjWn5Tqew09FzFXZaG+vYuMKOewNJNp+7A19bA+xqCD9/Hjt27Nu4QMDpAkTkdFu2bL0QzE+BlejfqIh4S3fgqoY/3bCXxHCcfpGKuMSWLVvPxl4n55+AUofLERHpqkmtDezZs6cYuGjcuHEf5KIQXcYScdhrr20tB75lDN8FetqP5uaygi5jZWZcl7F0Gauzx3l4Gavl8YqxY8f+ufnYnj17zgPWAFOBCsuytgJbgS3A7vHjx4fJMIUdEYe89trWIuCr2HtYnd/ZNyeFnfTOVdhpbaxj4wo7rgkROX3uLj7Xy5Zl/QfQA1hoWdbVQHErXxu2LGs3TeFn64QJEyroIoUdkRx77bUtPjC3APcD/ZKPK+wkX1Nh58yxjo8r7KQ3rrDjief6gGbhx7Ksty+99NIOhReFHZEceu21LTcAD4EZ0XJMYSf5mgo7Z451fFxhJ71xhR1PPtcJYBt28NkCbJ80aVIdbVDYEcmBV1/dMsPYa+U0TNjryhvX6eMKO+mdq7DT2ljHxhV2XPfGn5PndvlzxYF9NIWfrZMnTz5tuwuFHZEsevXVLROwVz2embk3rtPHFXbSO1dhp7Wxjo0r7Lj3jT+bz+3B5/oU+OrkyZOfBfAhIhn36qtbhr366pbHgJ3ATKfrEREpML2Bb2/bts0H6uyIZNSrr77WB7gPzArA33xMnZ2mcXV2Wo6lHldnp70xe1ydnew/t4efqwqYo0UFRTLglVdeOxf4oTHcRbNbKkVExDH74/H44oMH/7FEYUekC1555bUewHeAb2MvjS4iIs57saqq+p6TJ2v+CJytsCPSCa+88moJmG8A9wLnOF2PiIg0+vVHHx18zrKsl4FzgVcUdkQ64JVXXg0AtwM/w54AJyIi7hAGvrp//4EvAM/RdBPWYYUdkTRs3vyqAW4yhgeBIU7XIyIipzkUDkdWfPrpobuB+S3HFHZE2rF586uzgYeAcU7XIiIiZ9hWUVH1k5qa2t8DA1KMq7Mj0prNm1+dCjwMTHe6FhERSen//fjjT7YlEol1QGkrX6OwI9LS5s2vjMLev6plK1RERNwhZlnWd/fvPzAC+EM7X6vLWCJJmza9MgB4wBiWodXFRUTcqioUqv/m4cOffQcYn8bXq7MjsmnTKxcAPwG+DAQdLkdERFq378iRyl/X1Z36N+DsNM9R2JHCtWnTK72A7wHfBMocLkdERNr22Mcff/J+PB7/v6TffQ8tX77kuMKOFJxNmzaXAXeD+R5wltP1iIhImxLxeOIXBw58Ms6yrB928NzDAAo7UjA2btwcBL5sDD8BLnC6HhERadfJurpT9x05Unk30LcT5yvsSGHYuHGzD1gO3E/qNRhERMR93j9ypOK/a2tPPULKDZYNYJ358OkUdiT/bdy4eT72goCjnK5FRETS9sKBA59WxeOx+7v4PIdAYUfy1MaNm6aDeQSY4nQtIiKSvlgs/vuDBz+dBFyTgadTZ0fyz8svbxoHPGwMs5yuRUREOiRUW1v370eOVK4kczePKOxI/nj55U1DgAeBm7Av5IqIiHd8cuRI5Yt1dae+TWZ/h+sylnjfyy9v6g38DLgD8DtcjoiIdJBlWa9/8smhaCwWuyMLT6/OjnjXyy9v/ByYe4GvAyVO1yMiIh0Xi8XWfPLJPy5NJKwvZOklFHbEe156aWM34NvG8B2gh9P1iIhIp0Rramofr6w8ugisbG3TE1m+fMlRUNgRj3jppY3FwF3AD4FzHS5HREQ6r7KiovKN2tpTS7P8OoeTnyjsiKu99NJGP3Ab9rycPg6XIyIiXWBZ1tuffHIoGItFM3FbeXsUdsT9Xnrp5UVgfg4Mc7oWERHpmmg0+uqnnx4ea1lW9xy95KHkJwo74jovvvjyNdhr5UxwuhYREemyRE1N7WuVlUevyPHrqrMj7vPiiy9PAh4BrnK6FhERyYiTFRVVB2tra3MddEBhR9zkxRdfHgn8HLjB6VpERCQzLMs68Omnh8ui0ejFDpWgy1jivBdeeKmfMeZ+4BbA53Q9IiKSGdFo9O1Dhz4bGo8nnMwZ6uyIc1544aXzgR8DXwGKHC5HREQyqKam9p3KyqqRnT3fGLCsjJSisCO598ILL/UEvgt8Cyh3uBwREcmsUGVlVXVNTd1wpwtpoLAjufPCCy+VAv8MfB842+FyREQkwxIJq/Lw4c+6RSKRbG37AHSo6xMFKpMHCjuSNRs2vBgwxqwEfgpc6HQ9IiKSeZFI9NNDhz7rnUjEmz1qgMxci+qkI8uXL2ksQGFHMm7DhhcNsBR4ABjkcDkiIpIltbV1hysrq3pnaI5NJh1ufqCwIxm1YcOL1wEPAWOcrkVERLImWlVVHaqpqfm8C4MONLvtHBR2JEM2bHhxGvaCgNOcrkVERLInkbBqDh8+Uh4Oh3s4XUsb1NmRzFm//sWxxvAQMNfpWkREJLui0eixw4eP9IrF4u1/sbMUdqTr1q9/YRDwIJgl2DPRREQkj9XVnaqprKzqZbn0ulULuowlnbd+/QsXYt9dtRL9/RERKQSJ6upj8RMnaro7fIdVR6izIx23fv0LZwM/AP4JKHW4HBERyQHLssJHjlQUh0L1XtvSR2FH0vf88y+UA/cYw78APZ2uR0REciMajYU+++xIaSwWc7qUztBlLGnf889vKAK+CubHwHlO1yMiIrlz6lQoUlFRWZpIJJwupTPiQEXzBxR25DTPP7/BB9wK3A/0dbgcERHJLevYseMcP36iyBvzkFOqWL58yWkpTWFHGj3//IaFwM+BEU7XIiIiuWVZVqyioipw6tQpp0vpqkMtH1DYEZ57bsMMY3gEuNTpWkREJPdisVj0s88qgpFI1OlSMuFwywcUdgrYc8+tnwg8DGam07WIiIgzQqH6REVFZTDb83M6sGN5VynsCDz33Prh2JerbnS6FhERcc6JEyeprj7mtdvK26OwU8iee259H+A+YAXgd7YaERFximVZiaqqo76amjqnS8kGzdkpRM8+u/5cY/gR8DWg2Ol6RETEOfF4PHHkSIUvHI44XUq2qLNTSJ599vkewHfAfBvo5nQ9IiLirHA4zJEjVb543JMLBaZLYacQPPvs8yXAN4B7gXMcLkdERFygpqaW6upjJBLeXUAnTbqMlc+effb5AHAH9kadvR0uR0REXMCyLKu6+pg5ebLG6VJyIQEcafmgwk4eeOaZ5w1wkzE8CAxxuh4REXGHeDxBZWWlCYXqnS6lDYYM7qZetXz5kjOu0SnseNwzzzw3G8zDwCVO1yIiIu4RiUSoqKjCoxt5NuhwEDrjEhYo7HjWM888NxV4BLjC6VpERMRd6upOUVlZheXhDa466YzJyaCw4znPPPPcKOAhYL7TtYiIiPvYG3meJIOXhrxEYcfLnn76uQHAA8awDMi31S5FRKSLEokEFRVVhEIhp0txki5jedHTTz/7eTA/Ab4EBJ2uR0RE3CcajXLkSCXRaF5s5NkV6ux4ydNPP9sL+B7wTaDM4XJERMSlQqEQlZVHicfjTpfiBgo7XvD008+WAXdjB52zHC5HRERc7MSJkxw7dtzpMtxEl7HcbN26Z4PAV4zhx8AFTtcjIiLuZVkWVVVHqa11fiNPY8BFN32ps+NG69Y94wOzHHgA6O90PSIi4m6xWIyKiioikbzdyLOzLOCzVAMKOw5at+6Z67FvI7/Y6VpERMT96uvDVFZWEo8nnC7FjaqXL1+SMgEq7Dhg3bpnpmMvCDjF6VpERMQbampqOXr0qJsuGblNyvk6oLCTU0899cw44GFjmOV0LSIi4g2WZVFdfYyamloFnbalnK8DCjs58dRTTw8F8yCwGHujDxERkXbF43EqKqoIh8NOl+IFCjtOeOqpp3sD9wG3A35HixEREU8JhyNUVFQQi2n9nDTpMlYurV379OeM4V7g60CJ0/WIiIi31NbWcfRoNZalicgdoM5OLqxd+3R34NvAd4DuDpcjIiIeVF19nJMnT+bwFQ15smmowk42rV27rhjMXcAPgXOdrkdERLwnkUhQWVnFqVMFvZFnV+gyVjasXbvOD9yGPS/nImerERERr0pu5BmLxZwupQVPdX3U2cmkJ59cZ4xhEfAgMMzpekRExLtOnQpRVXWURELzc7pIYSdTnnxy3TXAw8AEp2sRERFvO3HiJMePH9f6OV13bPnyJfWtDSrspOnJJ9dNwl71+CqnaxEREW+zLIvKyqOcOnXK6VLyRatdHVDYadeTTz41EngIzAKnaxEREe+zN/KsJBKJOl2K23Ys7wqFnc5Ys+apfsZwP3AL4HO6HhER8b76+noqKqpIJLRQYIa1eicWKOycYc2ap84Hfgx8BShyuBwREckTJ0/WUF19zOky8pU6O+lYs+apnsD3gLuBcofLERGRPGFv5FlNTU2d06XkM4WdtqxZs7YU+GcwPwB6OV2PiIjkD3sjz0pt5Jl9CjupPPHE2oAxfAn4CXCh0/WIiEh+CYfDVFRUEY9rfk4OaM5Oc088sdYAS7EXBBzocDkiIpKHamvrqK6uJpHIj1udPECdnaQnnlh7HfAQMMbpWkREJD9VVx+jpqYWD22zkA8Udp544snLwTwCXOZ0LSIikp+SG3mGQq0u5CvZcXLZspvbnP2d12Hn8cefHGsMDwNznK5FRETyVyQSpbLSjRt5FoQ2uzqQp2Hn8cefHAw8ACzB3rJVREQkK06dOkVVVTWWpY08HfJxe1+QV2Hn8cef/ALwU+BO8ux7ExER9zl+/DjHj5/s4rMYNL+n0/YBd7T3RXkRCB5/fM3ZDevk/BNQ6nQ9IiKS3xKJBFVV1YRC2sjTQRuBG5Ytu7ndtOnpsPPYY2vKjeEe4F+Ank7XIyIi+c/eyLOKaDQXG3kWRtenExuSPgqsWLbs5kg6X+zJsPPYY2uKgK8BPwLOc7gcEREpEKFQPVVVVcTjmp+TKymC0G+Ae5YuvSnteOSpsPPYY2t8wK3A/UBfh8sREZECcvJkDceOeWMjz050SrzAAr6/dOlNv+roiZ4JO4899sRCMD8HRjhdi4iIFA7Lsjh6tJq6Os3PaVtWL7lFgTuXLLnpvzpzsuvDzurVT1zdsFbOpU7XIiIihSUej1NZWUU4nNbUEGlVl4JQLbBoyZLFGzr7BK4NO6tXPzEReBiY6XQtIiJSeMLhCEePHtVGns46Aly3ZMni3V15EteFndWrnxgO/By40elaRESkMNXV1XHs2AktFOisD4BZN9+8eH9Xn8g1YWf16sf7gLkfewKy3+l6RESk8FiWxfHjJ6irq8vHCb5esguYe/PNiyoz8WSOh51Vqx4/zxh+iH0rebHT9YiISGFKJBIcPVpNOBx2upRC9zyw+OabF7W5uWdHOBZ2Vq16vAf2YoD3AN2cqkNERCQajXL0aDWxmObnOOxPwMqbb16U0R1Vcx52Vq16vAR7W4cfAOfk+vVFRESaO3UqxPHjx7F03cppv7jpphvvzcYT5yzsrFr1WADMHcDPgC/k6nVFRERac+JEDbW1NU6XUegSwLduuunG32XrBbIedh599DED3GwMDwKDs/16IiIi7UkkEhw/foL6es3PcVgYuGXx4hsfy+aLZDXsPProY7Ox18q5JJuvIyIikq5YLMaxY8c0P8d5J4AFixcv3JztF8pK2Hn00cemAo8AV2Tj+UVERDojHA5r/Rx3+AcwZ/HihW/l4sUyGnYeffSx0cBDwLxMPq+IiEhX1dbWUVtbq/VzcqiVDUnfAWYvXrzwYK7qyEjY+etfVw8E7jfGLAN8mXhOERGRTLAsixMnTlJfX9+Js7O6uWXqV8zPHcuTtgLzFy1aWJ3LF+1S2PnrX1d/HvgJ8CUgmJGKREREMiQej3P8+AlisajTpQisBZYtWnRDKNcv3Kmw89e/ru4FfB/4Z6AsoxWJiIhkQCQS5eTJk8Tjmp/jAr8Hvr5o0Q2OzArvUNj5619XlwF3A98DzspKRSIiIl0UCoWoqakF8v6ykBf87MYbFzzgZAFphZ2//GVVEPiKMebHwAXZLUlERKTzampqCYU6Mz9HMiwOfO3GGxf80elC2gw7f/nLKh/wReB+oH9OKhIREemERCLByZMniUYzuq1Snsj5ROsQsGThwgXrcvmirWk17PzlL6uux76N/OLclSMiItJxsViMEydOkkhofo4LHAXmL1y4YJvThSSdEXb+8pdVV2IvCDg559WIiIh0UDgcpra2TkHHHQ4AsxcuvP5dpwtprjHs/M//PDoeeNgYc62D9YiIiKStrq6uk+vnSBa8CcxZuPD6Q04X0lLgf/7n0aHAg8Bi7It6IiIirmZZFjU1tUSjWj/HJTYCN9xww/UnnS4klQDw/wFTnC5EREQkHfF4nJqaWuJxbeTpEquAW2+4YX7E6UJa40N3WYmIiEfYCwXWaH6Oe/wGWOrmoAN2Z+dj4AJjdAVLRETcKxSqJxTK+U4DkpoF/GDBgvn/6nQh6fAB+1MNGGMa/4iIiDjFsixqazUROdfaePuPAisWLJjniaADdmcnZdhprnnosbTmtoiI5EgikaCurk7zc9yjFli0YMG8DU4X0hEB4KOOnGAHHwDTGHwUgEREJNNisRh1daf0HuMeFcDc66+ft9vpQjoqrc5Oa5LdHnV9REQkk8LhCPX19Xn1vuLxDUk/AGZff/28D50upDO6FHaaU+AREZFMOHUq1Lh+jjFG7yvO2wVcd/3111U4XUhn+YBPgYzcMqbJzCIi0lmWZVFXd6oDCwXqPScH1gNXzp/v3aAD4Fu+fEkCey+LjFDgERGRjorH49TWaiKyy/wJmDd//nV1ThfSVcm9sfYDgzPxhMkJzIlE221HXfYSERGAaDSq28rd55fz5s39gdNFZErzsJMxxhh8Pvs6a/Mwk7yF3edLdn+SHy0SiYSXJ26JiEgnhMMRolFXL76bIQZ7HT7XSwDfmjdv7u+cLiSTshJ24PS1eZK3qrf1tX6/n+Tt7JZlaSlwEZE8ZlkW9fVhXbZylzBw67x5c1c7XUimZS3sdFay8+P3+7AsGjo+Cj4iIvkikUgQCuXXbeV54ASwYN68OZudLiQbXBd2mkt2fIzxN3R7rDMujYmIiHfEYnHC4bB+j7vLIWD2ddfNecvpQrLF1WGnOTv4+BoWZbIvc9mToPUPRkTEC6LRaAduK5cceQc76Bx0upBs8gEsX77kJHDU4VrSluz4BINBgsFgQ/dHt7yLiLiRZVlEImEFHffZCky77rrZeR10oCHsNHB9dyeVpuATUPAREXEZy7IIhzUR2YXWAjPnzp1d7XQhudA87HRoQ1A3MsYQCPgpKgpSVBQkEAgo+IiIOCQeT2h+jgsZw++BRXPnzg45XUuuBJp97snOTmuSc3wCAX/jHJ94PKF/dCIiORCLxXXZyp3umzNn1v1OF5FreRt2mkte6vL7A4BFPJ7QLe0iIlkSjUaJx/Pr96vHdywHiAN3zZkz6w9OF+KEggg7zSUvdYG/YR2feLM7u0REpLMsyyIajTb+Ps2DgJAvQsCSOXNmrXO6EKcUXNhpzg4+9o8guY5PPB7X6s0iIh2UDDrpThVQEMqZamDenDnXbnO6ECc1DzsHgViLxwpGyzk+9qUuBR8RkfYkEglisRha98x1DgCzZ8++9l2nC3Fa491Yy5bdHMcOPAWv6a6uYkpKSigqKsLv97V/oohIgYnH4w1BR1zmTWCqgo6t5Tt4wV3Kak9ycnNRUVGz4ON3uiwREcfFYup+u9Qm4IrZs6895HQhbtHykpXCThuMgUDA3zDBual1q8WyRKSQ2Jf645pz406rgBWzZl0TdroQN1HY6QL7dnY7+Nit3DiJhH4BiEj+siyLWEz/wXOp3wL3zJp1jdptLSjsZEgy+BhjiMfjjX+0iKGI5Atvr09myOMJ1Bbwg1mzrvlXpwtxK4WdLGgKPvZy6falLq3eLCLeZQedfPac+xQAAB8qSURBVP0d5ukgFAVWXnvtzD87XYibKexkWfNLXYmEfalLHR8R8RJdnnetWmDRtdfO3OB0IW532t1Yy5bdfAw44VAtec/v91NcXExZWRklJSUEg0FtVCoirmWvMq+U41IVwJUKOulJtXiMujs5YAefIsrLyyktVfAREfdRB9p9Gt4nPgSmXnPN1bsdLsczFHZcIBl8unUro7S0lKKiInw+LWIoIs6wLEtBx712YQedD50uxEtSbQ2hsOOg5ByfoqKixnV8tHCXiIgA64HF11xzda3ThXiNwo6L+f0+/P5iiotpFnxiCj4iIhnioQ1J/wR8aebMGVGnC/EihR2P8Pl8FBcXUVxc3Cz4RInHFXxEpOs68qbvoYCQL34J3Dtz5gz91DtJYceDmoJPEYmERTSa7PhoVVMR6RjdGOFqCeCemTNn/NbpQrwuVdg5AMQB7XbpAc2Dj2UliMXiRKNR7dclIu3y+XwNHRo1DFwoDNx69dVXrXa6kHxwxi0/y5bdHAU+daAW6SKfz0dRkX07e/fu3SgtLSEQSJVnRaSwGd3x6W4ngNkKOpnT2jvhfqBvLguRzEoGn6Ki4oaN+6KNl7tEpHAZkww66ua41CFgztVXX/Wm04Xkk9aivebt5BFjTEPHp4wePXpQWlqqjo9IAfL5fOrouNu7wJQZMxR0Mq2tzo7koWTwsef42JObo9GoOj4iec7v92GM0V1U7rUVmD9jxpXVTheSjxR2Clgy+BQVFQGnBx+tniqSH+zLVn5005WrPQUsnTHjypDTheSr1sLORzmtQhxnB58gRUVBLAtisRjRaIRoVDu0i3iVz+fD7/frjit3+z3w9RkzrtQttFmkzo6cIXXwiRKNRhV8RDzC7/e7bH6OQYHrDPddddX0+50uohCk/JewbNnNlUBNjmsRFzLGEAwGKS8v46yzetKtWznFxUVaiEzExYLBAD6fl5ZKK7jfJ3HgKwo6udPWLTkfAaNzVYh4QzAYbLizC6LRKJGIOj4ibmH/5ySgicjuFgKWXnnl9KecLqSQtBV29qOwI20IBoMEg0GMMUSjMSKRCNFoVBuVijjA5/MRDGpJCZerBuZdeeUV25wupNC0F3ZE0hIMBhp/0cZicSKRCJFIRB0fkRwIBAL4/QEKbU6MMxuSdnru0QFg9pVXXvFuZuuRdCjsSMYlg095eRmxWIxIJEokElHHRyQLgsFgszuu2lcoO5a7LAi9CcyZPv2KQ7mtR5IUdiSrAoFA4wRnO/hEiES0UalIVyXXyfL5Cm5yr9dsAm6YPv3yE04XUsgUdiRnAoEAgUCA8nKj4CPSBT6fj+LiIjJ9O3ehdH1yaBWwYvr0y8NOF1Lo2go7H2P/K9J/GyTjksGnrAwSiQThcIRwOKzgI9KOQCBAUVEQUDBxud8C90yffrmu37tAqytOLV16Uxj4Rw5rkQLl9/spKyulV6+z6NXrLMrLy7RRqUgKxcVFjUFHXMsCfnDFFdPuvuIKBR23aO8dZT/QOxeFiEAy+JRRVlZGPG7f1RUOR7RRqRQ0YwzFxSX4/T7d4ehuUWDlFVdM+7PThcjp0gk7V+SiEJGW/H4/paWllJWVNVzqChMO22v5iBQKn89HSUmJVi13v1pg8eWXT1vvdCFypvbCjjYEFVfw+XyUlpZSWlqKZVkKPlIQAoEAxcUlFNr6OR5UAcy9/PLLdjtdiKSWTmdHxFVODz4JwuEo4XCYSCTidGkiGWPPzynSJGT3+xCYdfnll33odCHSOoUd8TQ7+JRQWlpCIpFomOOj4CPeZYyhpKSEQMBLG3kWrF3AdZdfflmF04VI2xR2JG80BZ/ml7rCRCLaqFS8wefzUVZWqvk53rAeWDxt2mW1Thci7Wv11nOApUtv+gw4laNaRDIm+b/js87qybnnnkPPnj0oLi7Wm4i4lr3gZhk+X5u/lrtAf/cz6M/A/GnTpiroeEQ6i5l8BIzMdiEi2ZIMPiUl9kTP5AKG4bA2KhV3KCoqavj7KR7wS+DeadOm6peHh6QTdvajsCN5wg4+xZSUlGBZFtFohPr6MPX1YQUfyTljDKWlJS5YRDOz207kqQRwz2WXTfmt04VIx6UbdkTyjr1QWzHFxcX06EHj5OZwOKwd2iXrfD5DaWkpfr8mIrfkwj26wsCtl102ZbXThUjnKOyI0BR8SkqKARovddXXK/hI5vn9fkpKirM4P0cy6ARww9SpUzY5XYh0nsKOSArJjk/PnnbHJxQKU19fj2Up+EjXBIMBiouLM/68LuyGZElOL7kdAuZMnTrlzVy9oGSHwo5IO4qKiigqKqZnzx4Nc3zqqa8PE48r+EjHFBcXuWB+Tj7LaBB6F5g1derkg5l6QnFOundjiQjJ4FNEjx6GaDRKfX09oVA98Xjc6dLExZKXSTU/xzO2AvOnTp1c7XQhkhntXjBeuvSmEHA4B7WIeEpRUZAePbpz/vnncu65n6Nbt276X7ucIbmRp+bneMZTwMwpUxR08km6v5k/Aj6fzUJEvCwYDBIMBjGme0PHJ0woVE8sFnO6NHFQIOCnqKhIi1l6xx+Au6ZMmaxWbZ5JN+zsB6ZmsxCRfGEHnyJ69OhOLBYjFKpvCD7aob2QBIMBgsGg02VI+u6fPHnSfU4XIdnRkbAjIh0UCATo3r0b3bt3Jx5PBp8Q0ag6PvnKGHtuly5beUYc+PrkyZN+73Qhkj0KOyI50hR8uhGPJwiFQoRC9dqhPY8YYygqCiroeEcIWDp58qVPOV2IZJfCjogD/H5/s+ATJxSqp74+RDis4ONVPp+PoiJdtvKQamD+pEmXbnW6EMk+hR0Rh/n9frp1Oz34hEL1hMNhp0uTNAUCAd1W7i0HgVmTJl36rtOFSG6kG3YOAfWAtuUVySI7+JTTrVs3LMu+1HXqVEjBx8WCQQUdj3kTmDNp0qWHnC5EcietC8tLl95kAR9ntxQRac7n81FeXs65536OCy+8kLPP7kVpaYluY3YJYwzBYOC0+TlWYezX4GWbgCsmTZqooFNgOrIC2n5gWLYKEZHWJYNPeXk5iUSC+vowp06FqK+vJ4f7BEmDZNCRM7l4j67VwK2XXjpRbdIC1NGwIyIOs4NPGeXlZViW1Xipy96o1J3vMvnE5/Ph9+tuK4/5LXDPpZdO1IZ2BUphR8TDjDGUlZVRWlraEHzsdXzq68MKPlng9/t0W3kXOND1sYB7J06c8Mucvqq4jsKOSJ4wxlBaWkJpaQmJRIJwOEIoFCISCZNIKPh0ld/vd2S+lIsvC7ldFPjSxIkT/uR0IeI8hR2RPGSMoaSkmOLiIgDq68OEw/afREKd/I4wxuD3+zQxvJHBA/PEaoHFEydOWO90IeIOHQk7H2WtChHJquLiIoqLi7CsbkQiEerrI0QiESxLwactyaAjnlIBXDdhwvhdThci7pH2v+IlSxbXApVZrEVEcqCoqIju3cs555xe9OjRg5KSEs1DScHnM/h86uZ0jOM/rw+BqQo60lJHf8PpUpZIHgkGA5SXl9Gr11mUlZU6XY5r+HwmZ5etNJE8Y3ZjB50PnS5E3EdhR0QAu+Mj9oRg8Zz1wJUTJoyrcLoQcSeFHREB7PVjCv1yloKOJ/0ZmD9+/LhapwsR91LYEZFGgUBhrgrc1ZCjS1GO+VfgtvHjx0WdLkTcraO/2RR2RPJYSUkxkUihvW9YuGBirXRMArhn3LhLfut0IeIN6uyISKNAIEBpaUm7X2dP4M1BQVmnjowHhYFlCjrSER3t7HwKRADNZBTJUyUlxfj9PurqQrQMA8YYioqCBINBAOLxOPF4gkTC/ugdFpZlaaFA7zkB3DBu3CWbnC5EvKVDnZ0lSxYngANZqkVEXCIYDNKzZ3e6dStv2IKilPLyMrp1K28MOmBPag4GAxQVFVFaWkJRUZBAwO/q9Wksy9IcG286BFwxbtzYTU4XIt7TmdmI+4HBmS5ERNzH7/c33qGVTkCw7+gCy7LPSSQSWJZFIuGOgGFZCXV0vOldYPYll4zVf7alUzoTdrRgk4ikxRjT8McOOskNSZ0IPtoTrGsc3JB0GzDvkkvGVjvy6pIXOrOoxj3AEuxFnPTbQ0TSZsyZf7I9SdjuLMVd0VmSDnsKuFpBR7rKdOUXwKpVj/cBbgfuMMb0a3xSA81v5Ux93PRYR45P7z7n+pjT2t+pWuGnjzd9D5k+bvkzam88/WP7sbbGW37fTh5n8+9ae8ctf2btjWfyuOXPJPnvuLMfc3VOOs9hf6+tbdfQ9l1gZ/5cEiQSicbHO/KxM+d05dzmtbf8vZzq52V/Ds3DYmtjTQ9bGRizj1N/3vmx079nC8viD8BdY8eOiSPSRV0KO0mrVj1ujDFXAyuBhcZQrLCjsJOLY4UdWz6FneYf7e/T4PP5Gj9PN+w07+Yo7DQ+moEx+zjLYef+MWPG3IdIhmQk7DS3evUTZxvDF8GsBMaAwk42jhV2kp831dj6cdNjCjveCjvJj35/oGGidDphxyIWizU7VtjxUNiJA18fM2b07xHJoIyHneYee2zNeGClMSwDc1bjiyrsdPlYYSf5eVONrR83Paawk99hByAejzV2hfIz7ECqYNL+16U+z0VhJwQsHTNm9FOIZFhWd/1bvHjh7sWLF34duBC4FdhI83+ZIiIZkkgkiEYjZwQF8YRqYKaCjmRLVjs7qTz++JMDjeEO4HYwXwB1djpzrM5O8vOmGls/bnpMnZ387OzE47HGW8ud7M505dzMdHZSj7m8s3MQmD169Kh3EMmSrHZ2Ulm06IYPb7zxhh8DfYHrgCeAQtt5UEQywiIWixKP64Ydj3oLmKKgI9mW885OKmvWrD0XWIE9qXm4OjvtH6uzk/y8qcbWj5seU2cnfzo7lmURjUbO+Jmos+OZzs4my+KG0aNHnUAky1wRdppbs+apKcawElgCphso7KQ6VthJft5UY+vHTY8p7Hgz7Ph8/obbz30YY99WHo1GT/tZuCGwdOXcAgs7q4FbR426OIxIDrgu7CQ9+eRT5WCWAHcaw2X2owo7CjsKO/brFFbYafbRsizLNP8ZuCmwdOXcAgo7vwO+NWrUxVqBX3LGtWGnubVr1w0D7gSzwhjObxpR2FHYaaqx9eOmxxR2PB92Gj+6MbB05TkKJOz84OKLL/4lIjmW8wnKnbFgwfx3FyyY/z2gN7AQeBp78SkREXG/GHCbgo44xROdnVSeeurpC8HcZgx3AoPsR9XZUWcn1XHTY+rsqLOTi3M78xzZ7Oy0NZaDzk4tsPjii0euR8Qhng07za1b98x0YCWYxcZQ2jSisKOw0/hZxo8VdvI77GTiObwQdtoay0DYqbAsrrv44pG7EHGQJy5jtWf+/Os2z59/3Qrg88BdwE6HSxIRKXQfApcp6Igb5EVnJ5Wnn352FJiVxnALcI79qDo76uxk7lidHXV2MnlunnV2dgNzR44cUYGIC+RFZyeVefPmvjVv3pxvAV8AlgAbAN3qKFJA8vU/cy63AbhSQUfcJG87O6k888zzfYy9L9cdQF91dlId24+ps6POTj50dpIf1dnJWWfnz2CtHDFihLYAElfJ285OKtddN/vg3Lmz7wf6A9cCjwJawVNEpOv+FbhNQUfcqKA6O6k899z6s4FbsBctHKPOjv2YOjvq7Kiz49y5HuvsWJbFPSNGDP8NIi5V8GGnueee2zDe2PtyLQd6KuykHlfYsY8VdhR2snWuh8JOBKxbhw8fvgoRFyuoy1jtmTPn2t2zZ1/7dexb2G8FNtH8N4KIiCSdAGYr6IgXqLPTjvXrXxgI3AncDlyozo46O8ljdXbU2cnWuR7o7ByyLOYMHz7sTUQ8QJ2ddsyadc2Hs2Zd8yOgDzAPWANoAp6IFKp3gakKOuIl6ux0woYNL54HrDDG3AkMbz6mzo46O62Nq7Ojzk4edHa2AfOHDRt6FBEPUdjpohdeeGkKsBJ74cJuCjsKO62NK+wo7Hg87KyzLJYMGzY0hIjH6DJWF11zzdXbrrnm6i9hT2peCWx1uCQRkS4yLR/4A7BQQUe8Sp2dLHjxxZeHASuNYQWY85KPq7OT+WN1dmzq7LivO5OLzk5bY13r7NjHDZ/fP3TokPsQ8TB1drJg5swZ786cOeO7QG/gRuAZIO5sVSIiHRIHvqqgI/lAnZ0ceemljRcaw+1g7gAGgTo7mThWZ8emzo77ujMe7+yEwFo2ZMiQtYjkAXV2cuTqq686NGPGVQ8DQ4ArgT8Duv4tIm5TDcxU0JF8os6OgzZu3NQTzDLsic0T1Nnp+LE6OzZ1drLfnenMc3mws3PQspg9ZMjgdxDJIwo7LrFx4+bRxrASzC3A2aCwk86xwo5NYUdhp+VYJ8LOW8CcwYMH/wORPKPLWC5x1VXT37zyyul3AxcCS4EXgISzVYlIgdgMXK6gI/lKnR0X27z5lb7YE5pvB/qqs3PmsTo7NnV2st+d6cq5Lu/srLYsbh08eFAYkTylzo6LTZ9+xYHp0y+/DxgAXAs8CugXkohkyu+ApQo6ku/U2fGYV1557WzgFmNYCYxWZ6epxtaPmx5TZ0edHXV2Gj+/d9CgQb9ApAAo7HjYq6++NgHMSmAZ0FNhR2FHYUdhJ42wE7MsVg4aNPBPiBQIXcbysMsvn7br8ssvuwt7X64V2JMMlV5FpDV1wHwFHSk06uzkmdde2zoIuNMYbgMuVGen8bOMH6uzo85Ouue6pLNTCdbcgQMH7kKkwKizk2emTZv6wbRpU38I9AHmAWuAqLNViRQmF/1n8kNgqoKOFCp1dgrAli3bzjOGFcBKMMNAnR11dtTZycW5nT0nw52d3cDcAQMGVCBSoBR2CszWra9PBVYaw81AN4Wdzh8r7BRG2GntubJ5Tms/I/tz6EDY2QAsGjBgQC0iBUyXsQrM1KmTt06dOnkl9qTmLwHbHC5JRLLjv4B5Cjoi6uwIsG3b9uHGXrfnVuA8dXbSO1ZnR52dbJ3T2s/I/hzS6Oz8yrL4/oAB/fULXgSFHWnm9de3B4F5YFYaw2zAr7DT+rHCjnfCTsuPeRx2LLDu6d+//28QkUYKO5LS9u07vgDcBuZOYKDCjsKOwo7rw07EsljRv3+/RxGR0yjsSJu2b99pgOnGvsy1CChV2LGPFXYUdrJ1Tms/I/tzSBF2TgI39OvXbyMicgaFHUnbjh07ewLLjTErgfHJxxV20htX2FHYyVLYOQzM6dev3z5EJCWFHemUnTt3jQFWAl80xpzdfExhJ/W4wo7CThbCzruWZc3u16/fAUSkVbr1XDpl4sQJ+yZOnPBN4ELsjUhfAO3LJZJD24BpCjoi7VNnRzJm167dfYE7jDF3YG9XAaizk41jdXYKvrOzDljSt2/fECLSLnV2JGMmTBh/YMKE8fcB/YFZwCog4mhRIvnnj8BCBR2R9KmzI1m1e/eec4wxt2DP7xkF6uxk4lidnYLt7DzQp0+fnyEiHaKwIzmzZ8/eicBKY8wyoEfycYWdjh8r7BRc2IkD3+jTp89/ICIdprAjObd37xtlwGLgTmC6wk7HjxV2CirshCzLWtanT5+1iEinKOyIo/bufWOQMeZO4DbsO7sUdtI4VtgpmLBTDVx/0UUXbUFEOk1hR1zhjTf2+YE52Je55gGB5JjCzpnHCjsFEXYOArMvuuiidxCRLlHYEdfZt+/N84EV2Je5hinsnHmssOPNsJPucwBvWZY156KLLvoHItJlCjviavv2vXmZsbenuBkoB4Ud+3UUdvI47GwGFvTu3fsEIpIRWmdHXG3MmNFbRo8edSdwAfBl7FVjRRyT5f8gPgbMUtARySx1dsRz3nrrbyOwL3GtMIZz1dnJfocleugf1L6ykZLRYykeNiJnr9vWR6c6O515jjQ7O/9mjLm7d+/eCUQkoxR2xLPeeutvQWOYD2Yl9orNfoWdzIeOqj/8O1V//H+g4bh82hV8/hf/B+P3K+x08GvbOPfeiy666BeISFYo7Ehe+Nvf3v4CcLsx3AlmQPJxhZ2uhY4Tz67j8H0/pKWei5Zw3r/c69mwk/zogrATA77Up0+f/0REskZhR/LK22+/bcBcib09xY3GUGqPKOy09THVY4lwmA/mzSR+/Bip9P3vxwn2H5Dx1y2gsFNnjFncp0+f5xGRrNIEZckrI0eOtEaOHLFx5MgRt2AvUvgNYLfDZXnSyReebzXoAJx4/NEcVpN3KoGrFHREckNhR/LWyJEjjo8YMeLfR4wYMQEYC/wOe0VaScPJZ9e1Pf78M43zeJzg4a70fmBq3759dzpdiEihUNiRgjBixPB9I0YM/yZ2t2cZ8CLg2XfLXIgc+KjN8cSpOuJVVTmqJm/swQ46HzhdiEghUdiRgjJ8+PDw8OHD/zp8+LBrgAHAA8BBh8tyHSsaJVZV2e7XRQ9rgd8O2ABM79ev3xGnCxEpNAo7UrCGDx/28bBhw34G9AdmA6uBiLNVuUP85AmsRPvLvcSPH89BNd5nWdZ/AfP69+9f63QtIoUo0P6XiOS3YcOGJYD1wPr33nvvHOBW7EULRzlamIP8vc7GBINY0WibXxc477wcVeRpvwK+379/f102FXGIwo5IM0OHDj0K/Br49XvvvT8R+xb2ZUAPRwvLMePzETz/AiKfftLm1wUu+HyOKvIkC7hnwIABv3G6EJFCp8tYIq0YOnTIziFDhnwN+DxwG/CKwyXlVNGgIW2OB849F/9ZvXJUjedEgGUDBw5U0BFxAYUdkXYMGTLk1JAhg/80ZMjg6cAQ4BfAYYfLyrpeN97U5njPBYtyVInnnARmDxw4UAsRibiEwo5IBwwePPh/Bw8efC9wEXA9sBaIOVtVdpRPmkpRn74px0wgQA+FnVQOA1cMGjRoo9OFiEgThR2RThg8eFB80KBB6wYNGnQD0Bv4PvCew2VlljFc+MAvMMUlZwyd+90fEfjcuQ4U5WrvAVMGDRq0z+lCROR02htLJIM+/PDDy4CVYG42hvKmEe/tjZX8WPvaZo786mGihw/h79GTXivupNcXb8v666Zzbi73t2rn3NeNMfMGDx58FBFxHYUdkSz48MP93Y1hCfbdXJO9HHYsywLLov6D9ynq2x8CgfTOycTrtnOuS8LOOmDpkCFDTiEirqSwI5Jl+/fvHwFmpTHcCjRc+/FY2MnQOXkYdv5ojPnakCFD4oiIaynsiOTIRx99FMSe1LwSzCxjms+ZU9jxYNh5YOjQoT9DRFxPYUfEAR999HFvY7gduAMYoLDjqbATN8Z8Y9iwYf+BiHiCwo6Igz7++GMDXNlwmWsR0HDrk8KOS8NOCFg2fPjwtYiIZyjsiLjEgQMHzgKWY1/mGqew47qwU22MuX748OFbEBFPUdgRcaEDBw6ONYaVwBeBXgo7joedg8DsESNGvIOIeI7CjoiLHTx4sARY2HCZawZgQGEnqXlgaXmcwbDzljFmzogRI/6BiHiSwo6IR3zyySf9gDuB240xFzUfU9g58zhDYWczsGDkyJEnEBHPUtgR8ZhPPvnEZ4y5BnvBwgVAkcLOmccZCDuPGWNuGTlyZBgR8TSFHREP+/TTTz8H3GJZ1krgYoWdjIWdfwPuvvjiixOIiOcp7IjkiU8++eTShtCz1LKsHqCw097HVsZ+OGrUqEcQkbyhsCOSZw4ePFhmWdZNwErLsi4HhZ3WPrZ4LGaM+dKoUaP+ExHJKwo7Inns448/HowdelYAn1fYaTXs1AGLR48e/TwikncUdkQKwEcffRQA5jRc5rrOsqwAKOw0fF5pjLlu9OjRO1P97ETE+xR2RArM/v37L2jo9Ky0LGtI8vECDTv7gVljxoz5oNUfmIh4nsKOSAH74IMPpmHfwn6TZVnlUFBhZw8wd+zYsUfa+TGJiMcp7IgIH3zwQXfLspZid3smQd6HnReMMTeOHTu2Nq0fkIh4msKOiJzm/fffH4kdem4FPuf2sNPyYxph57+BOy655JJoB34sIuJhCjsiktJ7771XBMxvmNQ8y7IsH3g+7PzKGPP9Sy65RL/4RAqIwo6ItOvdd9/tbVnW7cCdlmX1Tz7uobBjGWO+PW7cuF938kcgIh6msCMiaXvnnXeMZVlXYU9qvtGyrBJwfdiJACvGjx//aBe/fRHxKIUdEemUv//972dZlvVF7Pk9l4Arw85JY8zC8ePHv5yRb1pEPElhR0S67G9/+9sl2KFnOdDLJWHnMDBnwoQJ+zL3nYqIFynsiEjGvPXWWyXAwoZJzTMsy7InzOQ+7LxnjJk9YcKEjzP8LYqIBynsiEhWvPnmm/0ty7oDuN2yrIsgZ2HndWDexIkTj2blGxMRz1HYEZGs2rdvn8+yrGuxL3NdDxRlMew8bYxZMnHixFPZ+45ExGsUdkQkZ/bu3fs54NaGy1wjIaNh5/8aY7566aWXxrP9fYiItyjsiIgj9uzZMwm727MU6N7FsPPgpEmTfpqj0kXEYxR2RMRRu3fvLgNutizrTuDyDoadOPCNyZMn/0cuaxYRb1HYERHX2LVr15CG0HObZVkXQJthp94Ys2zy5MlPOlCqiHiIwo6IuM7OnTsDlmXNxb7MNRcItAg7IeCaKVOmbHGuShHxCoUdEXG17du3X4Dd6bkTGNLwO+v/TJky5dvOViYiXqGwIyKesW3btn8DzgNenTJlyu+crkdEvOH/By2Fw2SgKwxaAAAAAElFTkSuQmCCUEsBAhQACgAAAAgAtl1sRtBqWsJmAgAAeQUAACAAAAAAAAAAAAAAAAAAAAAAAG5vcm1hdGl2ZS10eXBlcy1ibG9ja1N0b3JhZ2UueW1sUEsBAhQACgAAAAAAtl1sRgAAAAAAAAAAAAAAAAcAAAAAAAAAAAAQAAAApAIAAGltYWdlcy9QSwECFAAKAAAAAAC2XWxGEMLEHgNBAAADQQAAEQAAAAAAAAAAAAAAAADJAgAAaW1hZ2VzL3ZvbHVtZS5wbmdQSwUGAAAAAAMAAwDCAAAA+0MAAAAA"} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/command b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/commandTrial b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/contentMD5.txt
new file mode 100644
index 0000000000..697f377b62
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/contentMD5.txt
@@ -0,0 +1 @@
+NzM2NDVmNDI4ODM3MWYxNWRmNDcwNGJhZmE0NjdiMmQ= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/headers b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/results b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/toExec b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/03_NormativeTypeCI-blockStorage/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/body.txt b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/body.txt
new file mode 100644
index 0000000000..b5337685d5
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-objectStorage.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"objectstore.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/objectstore.png"}],"payloadData":"UEsDBAoAAAAIAM5dbEayGABb7AEAAHoEAAAhAAAAbm9ybWF0aXZlLXR5cGVzLW9iamVjdFN0b3JhZ2UueW1stVI9b9tADN31K4hMzWDFQTcNBRIPzRDURe1doCVKZiHdqTzKqfrryzvLdoykRZfilsPjx3uPpPpQYVlTw46VvQvlgSTYpwBNocD90FE5Yd+V9+XS3ku9/JhlSoajUumwpzl54bz0qHyghU4DhYXffadKN+oFW7qU4Kh7LwVs15vVA2xXl8iZ/D5f2otUi82Xh6+bp/U2y2oKlfCgKWPlnSK7ALonOBNDIoaLoTwzA140FBnAAm7eFyrea/EO502WOV9TmbJih1SeRyzk69fuYhAAd0EFK7WByEgJqkmMpy4b8X1xVf/NSOeUV74+JQhga7aOA7rigVgLQoNQIKcBwozrHhUG8Qe2dmkouOOOdTLOlERQoyJggONaAnzwAo/P60dDmhS8nblf2BY0KlR2ENZOMEqDxtJj39EZ1E3sWmi4ozAF2x9YsKYDV2YsdVFsQzH34yo6u+PehIa7mT5KygfXphwTPpAo07kmxY/XNSOQlltYRHguezO9OLXOt1xhB7E2Oouaj5zzHKLv6ng+JLf5FWHgX28I2Sm1JGdU6MfIQnUBDXaBzngcl63f0sOlRby6VsiuW0ovpZViV8Dyz/Jjd7KR1pBO2IycVhy1GQifucXdpKdJn6T3+PO/qrcLc/+o3KRwP/Z/U/4bUEsDBAoAAAAAAM5dbEYAAAAAAAAAAAAAAAAHAAAAaW1hZ2VzL1BLAwQKAAAAAADOXWxGvFW0wAIRAAACEQAAFgAAAGltYWdlcy9vYmplY3RzdG9yZS5wbmeJUE5HDQoaCgAAAA1JSERSAAAB9QAAAgAIBgAAAJv/9SYAAAAEc0JJVAgICAh8CGSIAAAACXBIWXMAABwuAAAcLgFphtdgAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAEH9JREFUeJzt3X+M5PVdx/H37O31KhQpP9zr0oQ7JS1dqwemEBOFcsZUY6LxV7xU4x9G0kgTkJpavURS27q2W0FT5Y+ehzahJCWc1grG1NRftBXjD6KyaA+KLTdgOu7yQ45CuONud/ynRuwhd7M7M5+Z1z4e/8/n+/qD5Zn9zNxOp9/vF7B1rd678/uq6p7WOybI4tzelcXWI2AjZloPAACGQ9QBIMTs4fn5t7cewVR7dKHX+/vWIwComq2qO1uPYKrdXlWiDjABXL8DQAhRB4AQog4AIUQdAEKIOgCEEHUACCHqABBC1AEghKgDQAhRB4AQog4AIUQdAEKIOgCEmG09ADbqt2546/vf+Oqnrm+9Y1I8fOzCW95962c/1HoH0I6oM7V2dNa++ZIdT5/fesekePT4ebtabwDacv0OACFEHQBCiDoAhBB1AAgh6gAQwqffAYZg1779l1fV1a13MNXu7B5aenIzB4g6wHBcXVU3tx7BVPtcVW0q6q7fASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANAiNnWA2Cj+lUnTvRn+q13TIp+1VrrDUBbos7Uuv537ru2qq5tvWNS7Gk9AGjO9TsAhBB1AAgh6gAQQtQBIISoA0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEKIOACFEHQBCzLYeANNo9d6d51fV3UM88tNze1c+OMTzgC1I1GFjtlfVlUM87+EhngVsUa7fASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhfKEL8FxVfbH1iAnyVOsBsFGiDlvc3N6Vv62qPa13AJvn+h0AQog6AIQQdQAI4T112Ji1qlod4nlHh3gWsEWJOmzA3N6VJ6vq4tY7AF5qtqoWWo9gqvkNE2BCzC70eg+1HgEAbJ4PygFACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCzrQfApPj3N7xhx+zJk51BX7f7yJEXq99fH8UmgEGIOnzNieeeWzlRde6gr/vC/PwPfGvVn41iE8AgRhL1Pft3nVVV147ibMbu4PJS93jrETAFDlbVHa1HMNW+utkDRvWb+jlVdfOIzma87qgqUYfT6B5aOl5+VmjMB+UAIISoA0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEL6lDf7H2tqV27Zv3zboy46trT0+ijkAgxJ1+JqF1dVHWm8A2AzX7wAQQtQBIISoA0AIUQeAED4oB8DUOzw/v62q3tF6R1XdvtDrvdDq4aIOQILtVfXR1iOq6o+rqlnUXb8DQAhRB4AQog4AIUQdAEL4oBwD2f0T+79rvdO/sOWG2bXtD3z5k4vdlhsAJpGoM5B+p97fqc7VLTeszZ68oapua7kBYBK5fgeAEKIOACFEHQBCiDoAhBB1AAjh0+8AE+bwTfOLVXVV6x0j8C8Li713tR6RTNQBJs+3VdU1rUcwfVy/A0AIUQeAEKIOACFEHQBC+KAcbAHX7Vx9fVVd0npHYy8eWJn7u9YjYJREHbaGH6uqm1uPaGy1qi5uPQJGyfU7AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEL3QBIMFaVd3dekRVHWv5cFEHYOot9HonqupHWu9ozfU7AIQQdQAIIeoAEELUASCED8oxmE4drn69uuWE/nqttnw+wKQSdQbSvWvp51tvAODluX4HgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIMdt6AIzbnv27rqyqH269YwPuWl7qPth6BDC5RJ2t6PKq+sXWIzZguapEHfh/uX4HgBCiDgAhRB0AQnhPHWDyfL6qjrUeMQJfaD0gnagDTJiFxd5vtt7AdHL9DgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEr14FgJe47Jcu/sn1mc6Fg75uZq3/1w/c/Ni/jmLTmRJ1AHiJ/kznFzpVewZ+3bbODVXVNOqu3wEghKgDQAhRB4AQog4AIUQdAEKIOgCE8E/aYGu4varuaT2isbXWA2DURB22gAMrc89W1bOtdwCj5fodAEKIOgCEGNX1+wtV9XsjOpvxOt56AABnZiRRX17qPltV14/ibADg5fmgHAD8X/9UVUcHfVF/vXoj2DIQUQeAl1he6l7XesNG+aAcAIQQdQAI4fodYALs2b/rgqp6Z+sdA1pZXure1noE/0vUASbDhVV1U+sRA1quKlGfIK7fASCEqANACFEHgBCiDgAhRB0AQvj0+5it3rvzr6rqNa13DOiLc3tXfrr1iCH6ZFX9Q+sRG9BtPQCYbKI+fm+uqnNbj9jKlpe6T1fV0613AAyb63cACCHqABDC9TvQxK59+++oqt0jOv57u4eWXhzR2TCxRB1o5bKqeuOIznYLyZbkP3wACCHqABBC1AEghKgDQAhRB4AQog4AIUQdAEKIOgCEmIg/PnP4pvlrqupHG894cGGx9/uNNwDAhk1E1KvqO6rqxsYb7q4qUQdgarl+B4AQog4AIUQdAEKIOgCEEHUACCHqABBC1AEghKgDQAhRB4AQog4AISblz8QSZNe+/duq6qda75gSx7qHlv6g9Qggg6gzCtur6rbWI6bEalVt1ah/d1VtG8XB3UNLx0ZxLkw6UQea6B5a+mrrDZDGe+oAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAI4Y/PADB2u/bt//Ea0V8UnAIvdA8t/ckoDhZ1gMmwWlXvaT1iQE9u4rUfq6odwxoyZVarStQBUi0vdf+rqm5tvYPp5j11AAgh6gAQQtQBIISoA0AIUQeAED79Pn6PV9XR1iMG1Gs9AIDTE/Uxm9u7ckXrDQBkmpSo/2NVfbjxhn9r/HwA2JSJiPrCYu++qrqv9Q4AmGY+KAcAIUQdAEKIOgCEEHUACCHqABBC1AEghKgDQIiJ+HfqQFuH5+evr633/4N/Xuj1Ptt6BAzTVvshBl7eLVW1o/WIMfvtqhJ1orh+B4AQog4AIUQdAEKIOgCE8EE5gEBPfG7uZ9fX632jOHtmpt73TW9d/dgozmZzRP3r7Nm/a2dVXdN6x4j86fJS9/nWI4Ax6NfZnerMjebs/tkjOZdNE/VTXVZVH289YkQurSpRBwjlPXUACCHqABBC1AEghKgDQAhRB4AQog4AIUQdAEKIOgCEEHUACCHqABBC1AEghKgDQAhRB4AQog4AIUQdAEKIOgCEEHUACCHqABBC1AEgxGzrAQAMX3995njN9I+O7GwmkqgDBJr7nv88WFUHW+9gvFy/A0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQwvepMwonq2qx9Ygp8XzrAdDIUlVtaz2ikZH93Is6Q9c9tCTqwCvqHlr6UOsNiVy/A0AIUQeAEKIOACFEHQBCiDoAhBB1AAgh6gAQQtQBIISoA0AIUQeAEP5MLMAr+Jv9l9xZVa9qvWOIVq9a+tI7W49gNEQd4JX9YFXtaD1iiB5rPYDRcf0OACFEHQBCiDoAhPCe+qn+oqpe23rEiBxvPYCJtbOqOq1HjJmfB+KI+tdZXuquV9Wx1jtgnBZ6vaOtNwCb5/odAEKIOgCEcP3OVLnsl7/l0n5n7YERHH10eam7cwTnAi9xeH7+I1V145gfe/tCr/czY35mE35TB4AQog4AIUQdAEKIOgCEEHUACCHqABBC1AEghKgDQAhRB4AQog4AIUQdAEKIOgCEEHUACCHqABBC1AEghKgDQIjZ1gNg0l23c/VXqupNjWccPLAy9/nGG4AJJ+pwenur6urGGz5TVaIOYa7buXpeVb1qWOeJOgC08/GqetuwDvOeOgCE8Jv6hDp80/zbqurdrXecoXctLPYeaj0CYKsT9cn1+qr6/tYjztBrx/Wg5bMefeTyZ3a/btjnbtsx0x/2mQDjJupMlf6v9ter6pnWOwAmkffUASCEqANACFEHgBCiDgAhRB0AQvj0OwDj9MGqOjDmZx4d8/OaEXUAxmah11utqtXWO1K5fgeAEKIOACEm6vr9oYsueke/3/+1Bo8+uNDrvbfBcwFgaCYq6v2qs6tq59gf3Ol849ifCQBD5vodAEKIOgCEEHUACDFR76kDwBbzl1W1MqzDRB0AGjmwMveRYZ7n+h0AQog6AIQQdQAIIeoAEMIH5QBe2ZurqtN6xBCdbD2A0RF14BSX7Nt/7ol+5ztb7xhUv7/26ON/+BuPDPPMq5a+9Ngwz4NREnXgFC9W/9KZTt3TesegZjrbbqmqm1rvgFa8pw4AIUQdAEKIOgCE8J46nN5Hq5q/v3x/4+cDU0DU4TQOrMz9UesNAGfC9TsAhBB1AAgh6gAQQtQBIISoA0AIUQeAEP5JGwBVVXXFz12x/cXznvjEoK/r9+vggx/u/vkoNjEYUQegqqqeuuipbee8UD806Os6nfrMKPYwOFEHgCF56L2v+/Z+f9slg75upt//j0sXv7Lpvxwp6gAwJP21mWurs37joK9br7qrqt6+2ef7oBwAhJio39QXer1bj+zefWDczz1y5MjJhXE/9PSOVtXDrUecoRdaDwBgwqJe/f7a7qq1cT9297gfeAYWFnufqqpPtd4BwPRw/Q4AIUQdAEKIOgCEEHUACCHqABBisj79zpbzxL1zr3m+Tgz9v8Onznnm+be8pX9i2OcCTDJRp6l+dT59Vr3qymGf+w3P7txXVfcM+1xIdsFXLlg7cf4TG/hilvXHh7+GjRB1AKqq6v7fvf9E1eBf6MLk8J46AITwmzoADEmn03+6X9Ud9HX9qieG8XxRB4AhedNi7wNV9YFWz3f9DgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACFEHgBCiDgAhRB0AQog6AIQQdQAIIeoAEELUASCEqANACN+nDpxi++xsd+3E2nta7xhYp3N/6wnQkqgDp/jyJ359papubb0DGMx/A5vJ5xlYL38CAAAAAElFTkSuQmCCUEsBAhQACgAAAAgAzl1sRrIYAFvsAQAAegQAACEAAAAAAAAAAAAAAAAAAAAAAG5vcm1hdGl2ZS10eXBlcy1vYmplY3RTdG9yYWdlLnltbFBLAQIUAAoAAAAAAM5dbEYAAAAAAAAAAAAAAAAHAAAAAAAAAAAAEAAAACsCAABpbWFnZXMvUEsBAhQACgAAAAAAzl1sRrxVtMACEQAAAhEAABYAAAAAAAAAAAAAAAAAUAIAAGltYWdlcy9vYmplY3RzdG9yZS5wbmdQSwUGAAAAAAMAAwDIAAAAhhMAAAAA"} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/command b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/commandTrial b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/contentMD5.txt
new file mode 100644
index 0000000000..1a7037769f
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/contentMD5.txt
@@ -0,0 +1 @@
+ZDdmYjFmODg4ODZlOGRkMDE0NWNhNGYwYjRhNmExN2Q= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/headers b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/results b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/toExec b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/04_NormativeTypeCI-objectStorage/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/body.txt b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/body.txt
new file mode 100644
index 0000000000..a6b6faae8d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-softwareComponent.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"software.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/software.png"}],"payloadData":"UEsDBAoAAAAIAOJdbEYqXfgFaQEAAPQCAAAlAAAAbm9ybWF0aXZlLXR5cGVzLXNvZnR3YXJlQ29tcG9uZW50LnltbH1SS2/CMAy+51dY3NuBduth0tQLJ5hG75XXGBqJJJ3jbuLfzw0F9kBTLpHzPezPkZg6bC3tXXDiYkjtB3HSSwWSn5Lzw5HaE/pju2qXej7t8tEYIa2jUBvQ0wwuQmSP4j6okNNAqUhxL5/IVEc/xEBBbjQcpY9cQbPd1c/Q1LeXawOrcqlnsit2m+eX3XrbGGMpdewGyYg6BkEXEkhPcDWHbA63oUqjQ0SWVBmAAhb3m+UYpbrjufiP1Olko9B9ngnRUpuBlVGVLFFOxVTufkcz9QaAb0kYO9FEeaRcssTqZ9s9R1/90HjVjmfIt1Cecgmg0UzO6f7xgo3yodHGgGlgSlpLgHCgoGYdXPYG3XVxZ03pUaDDAG8EHgMeyAIGCzxq6aQKZ8P6nMrNpsx8pvfRMfnJrZoVC+hjkp9zzfQZAXmfFwSTfpLpo/ZuSOVauWS3IUMFD1dZ101RPDivLaaHyzzlEA7GfAFQSwMECgAAAAAA4l1sRgAAAAAAAAAAAAAAAAcAAABpbWFnZXMvUEsDBAoAAAAAAOJdbEacjiETb9gAAG/YAAATAAAAaW1hZ2VzL3NvZnR3YXJlLnBuZ4lQTkcNChoKAAAADUlIRFIAAAGHAAACAAgGAAAAaUOggwAAAARzQklUCAgICHwIZIgAAAAJcEhZcwAAGzQAABs0AbsOJE0AAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAKXRFWHRUaXRsZQBTb2Z0d2FyZSBDYXJ0b24gQm94IFdlYiAyLjAgKHJlbWl4KV/asksAAAAVdEVYdEF1dGhvcgBwYWxvbWFpcm9uaXF1ZZDwO/kAAADTdEVYdERlc2NyaXB0aW9uAFNvZnR3YXJlIENhcnRvbiBCb3ggV2ViIDIuMCAocmVtaXhlZCBpbGx1c3RyYXRpb24gZnJvbSB0aGUgZXhjZWxsZW50IHdvcmsgb2YgImxlYW5kcm9zY2lvbGFAZ21haWwuY29tIikgLSBCb8OudGUgZW4gY2FydG9uIGR1IGxvZ2ljaWVsIFdlYiAyLjAgLSBTb2Z0d2FyZSBLYXJ0b24gV2ViIDIuMCAtIENhcnRvbmUgY29uIHNvZnR3YXJlIFdlYiAyLjAZE/0rAAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMTAtMDYtMTBUMTk6NTU6MTIMWx2BAAAAYHRFWHRTb3VyY2UAaHR0cDovL29wZW5jbGlwYXJ0Lm9yZy9kZXRhaWwvNjU2Mjkvc29mdHdhcmUtY2FydG9uLWJveC13ZWItMi4wLShyZW1peCktYnktcGFsb21haXJvbmlxdWWxIioQAAAASXRFWHRDb3B5cmlnaHQAUHVibGljIERvbWFpbiBodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9wdWJsaWNkb21haW4vWcP+ygAAIABJREFUeJzsvXmcFNW5Pv5Ur9PrDLMwrGoAEQQFkUVABK9xize5Ji6RX8I1akxIFJerJgrfRKNJcM1ighKXEGOIkShuaIyJxhgWBRSDGBVFgdlgmJnunq2XqlPn90d19/RSa3d1d1VPPZ8P2lNVZ6nqrvc57/O+5xyGUgoLFixYqGZMv+HHNgDBjH+1AAI5x1L/AgBTC+AIgH8B2LLnvlUtleh3JcFY5GDBggWzYfr/3eGFYOBrkTb2TDDj76HPDAIA/AAYAJkGT+Yzk3v8IIaIol3fuzEmLHKwYMFCRTH9+jucYFALoA5DRj3H8GcdDwBwJItnGDBG3Ngzagkh83MeOWR+/gzAZghEcVjh9kwLixwsWLCgG6Zdf7sNgvFOGftMQ1/L5PydlG9qhmxxIYY8hbKRQ+bnfWCwGcDWPfeuOoIqgkUOFixYkMS0636Ukm/qMkb3dUmjLkYAAWRYVuQYVCbvOCN8Ni45KJdh0p/3AtgKgSi6YXJY5GDBwjDBtOtucyaNep3Iv5RxH5HxuQ6AE/lGEGmjPgRVn/PJAQCYcpJDcXXJk0MKPICPIBDFtj33rgrBhLDIwYIFE2LatbcxEHT4lHEfASZt5EdgyODXYcjg+wo16gBEjKAGA5r8XAJyUNcnzd5DUeSQQxTMfyAQxVt77l0ZgUlgkYMFCwbAtGtv9WDIqOf8n0n9XZdxPAjAliyuxkgVNeIXr78k5FB4/+T6VDlyyJSoCID3IRDF9j33ruyDgWGRgwULOmPaNT+0Jw166l8dgHoIun3m37UZ17gyqig0OKrCCJadHLL+ZqT6VFBcQOxzBYLS6skh8zgB8B4Eotix596VAzAYLHKwYEEG0675IQMhyFqPIUMu9k8w/sLnQIUyZ0pFDsLfedKP9rqGJzmkL5IiTQ7AbghEsXPPvSujMAAscrAwrDBtxQ9qMGTMMw1+PcAIfzN5ht8OMxgpVfVrqCv3s0UO4p+LJ4fMzyyAdwFsA/DOnntXxlAhWORgwbSYtuL/2QGMSEo49QAaMGT4R2T8nXnMkywu/QIbN61SfmQv1F9sXSUlB8CU6azlJIfMzwkAu8BgG4B399yzMo4ywiIHC4bBtKv/XwCCQa/P+Jdp8If+MaiHEJRlitLR8z6XNa1S2djL1VWQobLIQX2dFScH4bNQfxzAOwC2Acy/99xzC4sSwyIHCyXBtKtXuSAY9tS/TGOfknByicCRUYWGF9Lw5JB9zhiZMxY5KH4uaaxHfT/yn3sMwE4AbwHYveeeWziUABY5WFDEtKtW2iDINymD3oghQ59p/FOj/EYw8KFURgqoNDmor0vsuEUO4vVY5CD+Wf65D2KIKPbsuecWAp1gkcMwxLTvrvQBaAKTNuxixj7T6I8AYDOMkcqrv2hyyK6rqoOjlX/uw4wctNdVOCn3Y4goPiiWKCxyMDmmffcWFwQD3gRkGftG4TOTeSxl7N0ASjuCVay/pEaq8HotcrDIIf1Z9FmJl6msxyZWvg/ZRKHZ0FvkYCBM+87NDFKyDBjBwDNZxj5l4BswRAaBjCrEjV1Bxlvsc1nJQb6NrBemSIOX9bnaySF9kYHIAShzIkBxdZWeHLL/zvu9aP69RwDsALAdwF61RGGRQwkx7Ts3+5Bt2JuQbeSbMj6n/p9cEsGIo6miyKG4uspJDoZ77kYlB6CQ77EE5KCuT8aL9ajrR/HkkPk5DIEktgPYJ0cUFjmoxLTv3OxAtkHPNfZif3tEKxNgQiMlk4Gj+YWxyEFbncOKHArvn1yfLHLI/dwDMNvBYPueu2/+LKdfw5MckvJNLdQZ+NTftUDOz7c4GM1IFVeXRQ4inw0XHK0UOWT9zUj1qarlvCLIodS/d+G5dEHwJnbsufvmA0CVkMO079zsgfrRfEqrd4hWVj6U2kjl1yV2nfFGU5V/WSxykP6sQzrr8CSH9EWGIgdKwROeRDlCWI7jCOEITbBsbyIRf7zSBjIP075zsx3IC8IqGXxfRTpbXjAY+nLVfLZgQS2s31aVgVLKE0JiHCEJjuM4jhCe4zgb4XkbIbyL5/kaSqmXUupFzveapJ4XDEEOxy6/5ftOhl7CAEdByNbRU74xKqwXshhQ5AyghsNPRjUM/duS/Lasr1ERlFJCCBfjOD7BEY7jOEI5wjGEEHGjn/c88+KGQM5vIfk1JAxBDgQYSygz0cnQTxxCHr4FCxWCIS2UoY29heJBKSUcIVGOI3HB6HOUIwQ8IXbCp42+j1LUQfx7Fjf6NJcgKCNBELmIG4IcAAwACLCUOYkDPnAxtN4GNFe6UxYs6Igh403BZOjflrGvBkiMKZJGf1Aw+oTjOI7nOMKQPKNPPcj9vpm8+EyqEbHfhvTvJa9vogSR6z0YhhwGUx8oMDVOmYQd2O5i6GwMbYU4PGHIgawFqDLqqkdpFkwGSiknjPS5OMcRliMc5Uja6Dt5nvckjX4NhK1e86oQqTbbwFOAMmCyCULWIGgjDfnyxpCVIHgOmXARYG6UMvvdDOVtwISK9MrCcILBpRtrlFAOUEo5jiNRjnAxliOsEMgVjD4vjPRTRj8AYee/bOSnhwtHQWnO96f6t0QhShDajL52eckwnoPU/qnHxClDbcAOF0NPYICasvbKQnGorD0zuLE3KUzKUYLR5/o5QuIsx7EcN2T0CSUunqceyvN+KmQ+1maXHkpBZfJPZP9+siXD3F7k1qA80pd/3qWUlwzrOWSC4YE5McoccTJ0nwOYVrZeVRQmfQtLB8vY6wYj/rYK6xPPU5YQboAlJMaljD4hIIQ4CCEunsLD87wfgBfZ65BlNpzbdM7vSONIXZwgCpV3KiUvGYYcBpUvQRNLmSYOeNfN0M8xeexuQRpGNAYGN/aGfGRGRGkeFM/ziWQgN8ZxXILlCC+kbPJ2whMXpdSbNPoeyBl9ILN/RRtNwU4XXt4k8hLp/eOviCHIweN0xKKsus2MKDAzRpl+B/C2k6Enl7hrFtTD2MbegiHA83ycI2SACLo+O2T0iZ0Q3sVT3svz1A8gCDCyRp8ZMnIKRl+RwESMpqj3IFWbmeQlNQQRByq/hAQA4EvTJx1V73Xj8Z0foD+eUFPEzwEnE8p85GJowAaMKXUfhyHUZOBYxt6IqIDXw/N8TBjpc1GOE5Zi4AgBR4iDFyZneXlKA2AQAOCX6HXuIdmUS0qzCEIMRWryppKX9Iw/GIcceMr3zD1qNKaMbMDvd76Pt1sOqypHgePilGGTaa8nA7CXtqemg/IIXjrnvgywtBujIznS7+c4LspyJMERjnCEMBwhdp7wNZTnPbyQuSNi9IcCucg/oZ/RVFU+67dmJnmpJPGHGqeDGVNf6x5VG3A3B/01TQG/uzHgc9X7vDUMgz7AIOTAERoCgGCNC1efehK2H+zQ4kU4k2mvLS6Gxu3ApNL2tqKwpBsLuoDn+RjHkT6WcFGO4xJC9g4Hjid2ntCa9Egf8IGBV6SKIXln6JjySFuzPCKfcpnjPZhJXtI9/mBjbLaRQb9jdF3Q3VzrdzcF/e5Gv8/dGPC6R/i87hFej7vW63HXeWvcHpfTKVV3e6j3CGAQcuAp7c78uxAvAsD4BGVgA3a6GDqNkd9LwSiwjH0xyHo7LS8EAAjPRzmO9HGEiyYXXSPs0IzcGp7nvckcfS/E35Hkb43JPCIrj6h48mIEoeF3bVx5qRzxh6CnxjmmLuhsrgu4Rwb97qaAz90Q8LkbfF7XCJ/HXecV/gU8bpeNUXgSKsDxfAIwCDmwhO/KPVagFwEemB2jTLeToR87gBN176yFYQDjEU3S6PdyHImyhIsnl2EAR4gzuQyDn1IaYMBkG/2h26iQvKNd8tC3/VyolJdKHH9wOezMqNpgzai6gLM5GHA3Bf2uRr/PXe/3uuv9Xle9Txjl13pqXC6HvaxyOUuIcchhkGXzyCGFAr2IBpYyDRyw283Q8Yyw0qsFC8ZB0lgQQgY4kjT6HBfnCOFZjgMhvIPwvDtl9CEYfLFJoCUy+jmSh+zoN48CzCQv6RZ/YBgGDX6vc3Rd0NVcG3CNDPpdjQGfu8HvdTX4fe4Rfo+rzutx1Xo9bp/b5TTW8GMIKc/BMJv9vPqPf1CHTX4ZJa1eRBKDDuA/TmGdJiOCSn/O0EzER4AaPucFB4c+m3XDn7z6Ze5R82fNexpn/U0I6eMI6WM5YdE1VsjeYQghmUY/CGHtsMx2xZ6ZaBtSx5jc58BIX6t8LE80kS3PiBzLOUNFDqvtEyQIYqh9Tfcq0afMaxnA53bZUwa/Oeh3NiZH+Q0Br6ve53WN8Hlddd4ad9BT47LbbEa1+arxn7bDu7942oIZhvAcACDBEepwyT/YAr0ILwfMJpT52MVQjw0YV3xvLQwDiMV9KCGkn+VIL0e4AZZLafocQ3jqIDyp4XnqSxp9D1PoSF+zJp99rRBTzRipK6tkJpKX9Ik/2G02prk26BxVG3A11/pdTUG/syngzzL4I3weV63X46pxOoZVFiTH88ZJZQUAlvA8VKSiFhqLoMCxccqQZNrrLBjo3qsaxpHv5QL+PCeM9Hs5jgywwoxcLjkj10F44uF5mpJ3agC4IexGmFF7/oiW5mWU5LUtAn0lj+LLl01e0uVeR/g8jtEjgq7mYMA5MigY/caAz1XvHzL4dV6PK1DjduoQu61KEJ6PAQYykAmV5JBCgV6EPZn22uZi6IAdmFxQZ8sG41hWg0BzdhdHSEQI5HIDLMfFWY6Q5IxcB+F5D08hb/TVavqUMmIEoeI+RI4lVWxNmryo95DRP2TWp9EQ5/wOtWUvFR1/qHE6mVF1AWdzbcDVXBtwNQX8zsaA35kc5TvrfV5XnWD0nQ67gjZtQRGEp8YiB47nOQCSubdiKNSLADA2mfb6touhU5hhsQe1IYlGs7GH8AdPhFF+mCXcIJut6TsJ4Wt4SgOZRp+RMvpM+j+ibUkcE4cIQUh4D1IwsLxUFEHkwcYwTFPQ7xhVF3Q1B/3OppSWH/A56/1eZ73P60yO8p1et8swdmo4gCMkChiIHFhC1C2uJIICvQjwwMkxyoSS25POKLR9C2kUZOxTEDZF5yMc4cIsRwZZjouxGSmbPM97eNBAUtN3AxgpYoJER/rJsbhEXxWtZlGkUUl5SYYgpMoXda8BT41jTF3QMbI24GwO+l2NAb+zMeBzNgS8znqfzznC73GO8HpdQW+NQ4+cfAv6g/DUYOSQTJ8qFEV4ESNYyozggD1uho5igMZi+lFlkDDq2tZXShr9EEu4MCcY/SibXE8/bfQpDSZH+kmjn19NVq/Sn9TPMhWhABUzYmVheHmpwGuz4HI4mKSk42iuDTiTOr6zwe9zNgR8zhHCKN9Z5/M43Q6HJeuYHITnBwEDkQPhaVHkkEKhXgQFpscoE3MAO5Jpr9U4qhE39gWuryTsj8uHOcKFWI4bSMs7SaNPhF2zgpRSPwA3GIzMt8MFGLI8K593QJshpEhynRHlpTRBFGz0xeIPjI1Bg9/raK4NOptrA46moN/RFBCMfYMg7TjqfV5nnc/r9Ne4HdX4MlgQh+HIgSNC+pQeKMKLqOGAORxl9rkZ6rABR+vVpxKhKBlHCslN0Xs4jkRYwvWxrJC9wxLORgjvJDz10iFN3wWlkf7QEYYymfKOSHBSHkVntJhIXioo/uBzu2yj6oLOkbUBx8ig3yGM8v0OYZTvddT7fKlRvqMacvIt6A/C0wHAQORAqJA+pScK9SIATIxThk+mvZ4EjYHyIlASY59CclP0bpYjYZbj+rlk9k7S6DsIz/uyRvpAExip7J3CR+qi8o5Wo5tdSTnlJXV9lZCXCok/OOwMMzIYdDTXpQ2+ozHodzQKso6j3u911vt9jhE+j7PG6bRkHQtFgVDeWOTAJYMgeqMIL8KWTHs95GJoyA5MLUX/UISxTyHD6IdYjusXUjY5jiUkOdLnfZTng1RYUtkNMCORHO1niw0ifdNdk8/NcpEdrZdTXkreq6K8pL6vCvGHET6vvbk24BiZknWCfkdjwOdoCPgcDX5f0uB7HUFvjSXrWCgbCE/7AQORQ0rnKhWK8CJGJSgzyga842LosYz4loQlAaWUFYw+FxLkHRJPDKVsughPfZTna6mQipuSdwSJJ1u7ya05M6CcgoImn3tt4csNl1leEr1WRF7K6Erh8Ycap5Nprgs4BB1/yOg3BHyORr/PUe/3Oer9Xkedz+Nw2u2WzbdgOBCeNxo50JKSA1CUFwEemBWjTK+TobscwEnF9IOnlOU40sURrifBkn6W42Is4VLZOy6e8j6ep3UQllXONvpDRkuXjJQcu6+xfHGB4CLlJeG4srwkBdXxBxvDoDHodzTXBuwjkwa/UTD69ga/z5HU8h0j/F6Hz+2yZB0LpobxyIEKrkw5UIQXEWQpcxIH/MfF0AYb0Jx5kudpgiOki+W4EMtx/QlB3iEcR2wcIW6epz6e0hEQVth0QSifriOZ9a1PGmKe0cy9dsh7UAzDllBeGmo7/als8Yegp8aWlHXsIwWDb28KBhyNAZ9d0PJ9jnq/117r9ThsVkq+hWECjucjgIHIgefLRw5AcV4EBY6PUyYRDoX+PTg4GON5PsBTWgfB6CssrSxttJKLhikYXQ2Sh6LVL6u8VJb4g8thZ0YGA/aRtQH7yNqgY2TQb28UtHx7Q0AY6df7vfZ6v9fhclhSvgULueCIwTwHntLeSrRbhBfh4nl6LEeIWApu0SmX8uX1T7lULS9VIP7AMAzq/V57c23ANrI2YG8KBuxNmQY/4LM3+H32er/XEfDUWLKOBQtFwHCeA+H5ipADULgXwdgYJn/0K19EyZAqLDlcTnmp5PEHr9vFNNcG7M11QXtT0G8fGQzYG4M+e2PAnxrl2+oDXscIn9dm5eRbsFAeDMQSvYCByIGniFS6D1q9CBtj4wGIyCMAijCkJZaXShp/cNhtaErJOkG/rSkYsDcG/bbGQMro++wNAZ+93u+ze4y7GZYFC8MWkWjUWJ4DT2m40n0AtHkRjI1RWiywnPJSSeMPI3xe28jagK0pafSF4K3f1hDw2xsDPltDwG+v9/vstV6PFbu1YMGkoACe2PZu5DfXGIgcKGio0n3IhBovgmEYAmGbRynvQQpa5KWSxR9qXE6muTZgawoG7CODAVtTbcDWGPDbktKOrSHgszX4ffb6gM9m5eRbsFD9YDlCep/4NQcYiBwIbyxyAJS9iKSsNBQA1SYvlST+YLfZaEPAJ4zygwFbkzDCZxqDfrtg+JNGP+C3+9wuy+BbsGAhDZYQkvpsGHLgKe2qdB+kIOVFMDaGz7u4RPGHoKcGaYNfG7A1CdKOMNLPMPp1Pq/N0nUsWLBQCDjCp6Vyw5ADA3RXug9yEPMibIymrMk8gnA5HExTrZ8ZGQwmjb7f1hjwM2nDLxh/piHgs1k5+RYsWCg1EhmbrhmGHBx225FK90ENMr2IrZ8cTA37c3PybSNrA4wwwg8wjUG/rSnoZ5qCAVtjUDD2DQG/LeipsQy+BQsWDAOOEDb1maFUbQy19PjH669TM0kibx7oiDkcTltj0M80Bvy2hqCPqff7bNYe5xYsWDAjWnrCh5bMOXk0YCDPAQDiHKEep3nkk6Prax1zpk421DO0YMGChULBkqHtmg01xE0Qkh/gNTBsjHwOqQULFiyYCcSo5MAS3lTkYGdMpIFZsGDBggI43rjkoDTj2FAwU3zEggULFpSQuZCoociB481FDg5rMTgLFixUEQjPx1KfDRVM5TL0LjPA2gGmunDw4EG0tbWB4ziwLIuamhocf/zxqKur01QPy7JwOp0l6qWFSuK9997Dtm3b8Pbbb2Pv3r3gk0q41+vF3Llzcdppp2HBggXweDwV7mlh4HhqUHLgTUYOdnulu2ChSGzfvh3r16/Hjh070NnZKXrN2LFjsXz5cixdulSxvieffBKJRALLli3Tu6sWKoj3338f9957L/71r39JXvPxxx9j/fr1aGpqws0334z/+Z//KWMP9QHh+Wjqs8HIgYptnGNYOO2GUuUsaEBXVxdWr16N5557TvHatrY2RCLKK8o/8MAD+NnPfoYbb7xRjy5a0AmdnZ14/fXXsWfPHni9XtTV1WH+/PmYMWOGqvI///nP8cADD0DtnLAjR47ghhtuwFNPPYU1a9YgGAwW0/2ywrDkkKl3mQEMAEoprKQlcyEUCuGSSy7B/v37VZcZOXKk5DlKKe644w78/ve/BwAMDg4W20ULOuG5557DD37wA9HvZM6cObjhhhswe/Zs0bKEEKxcuRJPP/10QW1v27YN3/zmN/HYY4+ZRmbiDEsOlEaVrzIWOELgdBjqMVqQQSwWwze/+U1NxABIkwPLsrjpppuwadOm9LFo1HQ/Y1Nh//79+Oyzz3Do0CF0dHTg0KFD6c+LFi3CD3/4QwDAs88+K+vF7dixA7t27ZIkh1WrVhVMDCm88847WL58OX7729/CbgIZmufpQOqzoawa4XnTDbliCZY6rUXxTIMnn3wS//73vzWXa25uzjs2ODiI7373u9i8eXPW8VjMVA6w6XDbbbflPfMUpk+fDgBoaWnBrbfeqljXrFmzRI8/99xzeOqppwrvZAa2bNmC3//+97jssst0qa+U4DJssKHIgeOp6cghzrIIwBwu43AHz/N47LHHCiqb6zn09PTgiiuuwHvvvZd3rSUrAe3t7di+fTveeust7N27FyzLprPAOI7D5MmTsXDhQixYsACTJk3SVLfX65U8l/Labr/9dgwMDEheBwAOhyNNJpnYv38/fvCDH2jqkxLuv/9+fOlLX0JDQ4Ou9eoNw3oOPKX9le6DVsRZU03NGNbYvXs3Dh48qLmc2+1GbW1t+u/u7m5ccskl+Oyzz0SvH66ew8GDB/HAAw/gzTffRGtrq+y1LS0tePXVVwEAp556Ku6++27ZuE4m5PT7WCyGDz74AP/4xz8U65k2bRrcbnfe8Z/97Ge6E3xfXx/uu+8+/PSnP9W1Xr3B8cSY5EB42lfpPmhFgmMV9+k0Eiil+PTTT/Huu+/iyJEjiEajGBwcBKUURx99NCZOnIhjjz0WTU1Nle6q7ti3b5/iNQ6HA3PnzsWUKVMwODiIgYEB1NTUZF3T0tIiSQxAdcUcKKVgWRYul0v2ug0bNuDHP/5xQUZ18+bNOO+883D//fdj/vz5itfLkUM0GsVvfvMbVe2edNJJecf27duHl19+Wbacy+XCueeei4svvhif+9zn8Nlnn+Hvf/87fve738lmND3zzDO4+eabDZ29RPihAbqhyIFSM5IDMTwxhEIh/OlPf8KOHTvw7rvvore3V/Z6hmGwYMECXHLJJfj85z9fNRO65Aw6APh8Pqxbt05Sh04hlyxyYTZyCIfD2LlzZzq429HRkf536NAh3H333fjSl74kWjYUCuGWW27B3//+96L6EAqFsGLFCrz00kuKHoQcOezfvx/vvvuuqjbFyGHt2rXpiW1icDgcePjhh7Fw4cL0sZEjR2LevHmYPXs2brrpJkmCZFkWf/vb33DBBReo6l8lwGcM0A1FDoTy8lbLgGCHtlw1HEKhEB555BE8/vjjmkZ0lFJs2bIFW7ZswaRJk7BmzRpMnDixhD0tD9rb22XPn3/++YrEAMhr3oAxyEFLivUzzzyDn/zkJ5LnxXT5VBvLly/H22+/XVAfcxEOh/G9730Pv/vd72SvkyOH7m71G0rmkkM0GsWLL74oeT3DMLjrrruyiCETZ599Ntrb22Wf5aZNmwxNDhw/ZIMNRQ48pcozjQwGjjMmOaxfvx533XVX0drpJ598gvPPPx933303zj33XJ16VxmwLCt7ftGiRarqMYrn0NHRgY8//hgdHR1ob2/P+n9HRwdeeeUVjBs3TrGevXv3Sp7zer045phjRM898cQTuhFDCps3b0Zra6tsv5XIWQyNjY2glKbJo7m5GWPGjMm6Ztu2bUgkpBdpWLhwoeKs56VLl+Khhx7CkSPiG1tu3boV4XBY85Is5QLP88b0HChFuNJ90ApCeMPFHO6//37cf//9utUXjUZx3XXXob6+HvPmzdOt3nJDaYZrZtBZDkoTmspFDrfeeitee+01yfM7d+5URQ4fffSR5Llp06bBJrKzYWdnJ+655x51HdWILVu24Ktf/arkeSVyzsRRRx2FO++8E3PmzAHDMOjq6sKDDz6IUCiUd+0///lP2bq+/OUvK7ZXU1ODK664AnfeeafoeUIIdu/ejdNOO03dDZQZcY4Y1nPoqXQftIJQ42xBQSnF7bffjscff1z3ugkhuOqqq/Dss8+qMjhGhBI5qDU6xZLDgQMH0N7enjXKT4366+rq8Kc//UlVP5RiKDt27MD5558vew3P8/j4448lz0+bNk30+E9+8hP09ZUmRLh582ZZclDrOYwaNQobN27MGqU3NjbiBz/4AYiIHCxHDl6vF2eddZaqdufOnSt7/sMPPzQsObCEpNUbQ5EDhfk8B7ngVbnxhz/8oSTEkEI4HMatt96KRx99tGRtlBJK35XatXMcDgfsdruogQGAgYEBPPPMM3mzd7/1rW/hi1/8Iq699lrs2bNHtCzDMAiFQhgxYoRsHziOU0zL3blzp+K9tLS0yJKZGDkMDAzgr3/9q2y9TqcTX/jCF7BgwQKMHTsWhBAMDAxgw4YNeP3112XL7tq1S/a8WhK/+uqrJeWb3NnK0WhUNv122rRpqpfAmDRpEhiGkfw9yXlqlUY0wRqTHEDRVekuaAWlUGdRSozu7m78/Oc/L3k7//znP/H222/j5JNPLnlbekMspz0TckaSEILDhw+nDb4cOSQSCdx00015x3t6BMd4yZIlkuRAKcW2bdvwhS98QbavBw4ckGw/hX379qGnpwf19fWS1ygZKrFg9LZt28Bx0vN7xo0bhwceeADHH3983rmzzjoLv/jFL/DrX/9asnxXl7wZUOM5eDweTYFfJaL4IOcsAAAgAElEQVRVKzkCQv/GjBmDtrY20fNGJoePOjqNKSvZbYzpyEH1cLPEuOuuuxRTVPXC/fffX/BM40pCaeT33nvvIRQK5QV4f/SjH+Gll17Cww8/XFT7KfJZsmSJrHHcunWrIjmombMBCNLS2WefLXlezlB5PB5MmDAh77jcstUMw+Cee+4RJYYUvv3tb2P9+vWiuj8geEWRSETSIKsZwZ988smaUrBbWlpkz6shh76+vvTgQY7A9u3bB57nRWM5lQTH8/w/P/zUmAvvNfg86vPQDAIjMMOHH36IZ555RvX1NpsNU6dOxfjx45FIJBCPx7F3717JDItcbNu2TZX0YQSkMlQ6OjoU0xylgoiRSESXVTVTmWMnnngi6urqEA6Lq6hbt25VrOvTTz9V1ebOnTsLJocpU6aILhb3xhtvSJY544wzMGfOHNk+eTwenHjiibIaf1dXV1HkoDVxQokcnE5nOjMsc8G/zJiR2sxAlmXR29truIwlliOk709r0ibNUORwzdILB//x+uum2pvZCD195ZVXVDkw8+bNw7Jly3DKKafk/TBjsRieeOIJrF27VtGI8jyPN954w/CbmZx//vnYu3evbHqiGsRiMV3IIeU52Gw2nHbaaXj++edFrzt48KBiOqdactixY4fseaVMpVx0dHRIbooEQHW688SJE2XJoaenR3JujZrv4qijjlLVjxSU9uv405/+pDpRQG17hiOHHJ3SUOQAAAnC0xqH3Qg2VyUq7zvIpTMCQvBtxYoV+O53vyvpytbU1OCyyy7DrFmzcPHFFyvq2a+//rqu5BCNRvHWW29hy5YtWcanubkZp5xyCubMmYNAIKCpzq6urqKJARDIoZDc+lxkxjQWL14sSQ6A4J1ddNFFkufVykoffPABBgYG4PP58s7FYjEcOHBAsqxYvGH06NHYs2cPurq60NbWhtbWVrS2tqY/L168WFW/xo4dK3teLu6ghhy0/laUfu96IxwO4+ijjy5rm0pgCZ8VSDIcObAc4WtMtP9mpb2czs5OvP/++7LX3Hffffjv//5vVfXNmDEDy5cvx5o1a2Sv+/DDD1X3UQqUUvz5z3/Gs88+i127dklOUkuthX/OOefge9/7nqJhSUGvDVai0aim3Hq5elI47bTTYLPZJDOotm7dKksOSmmsKRBCsGvXLpx66ql55z755BPZDC6pNFaGYdDU1ISmpibMnDlTVT8y8dZbb8nORAYgmyarhqj9fr+mPpU761BKUqwkOEKyyMFYEREACUKMkxuqArYKC0s7duyQlZTmz5+vmhhSuPrqq0VHmploaWkpKha/b98+LF26FCtXrsT27dsVZy8TQvDiiy/irLPOwi9/+UtVL7MeBh0ojecwYsQInHDCCZLXbt26VfL5dnV1aUo+kJKW5CQll8uFyZMnq25DCYQQbNq0Ceeffz6+9rWv4Z133pG9Xm5lWzXfq1bPQS77qhQwIjmwhGS9hIbzHDie5wCYZqU3W4XZIZUeKYXTTz9dc51OpxPz58+XXUwtFovh8OHDGDVqlKa6eZ7Hgw8+iDVr1hQk+cTjcfzqV7/CZ599hvvuu092d61Keg5OpxPNzc0YPXo0Ro0ahVGjRuWt5bN48WLJjYe6u7uxd+9eHHfccXnn1MYbUti+fbvo8UKC0VoxODiIDRs24He/+53iMt6ZiMelt5Mvheeg91a/NpsNjY2N6e9/9OjR6X+jRo3Cscceq2t7eoAjfNYLaThyyNW9jA57hTeQVhqB5K4foxaLFi3KI4cxY8Zg8uTJOPbYY3HsscdqHp0BwsxaPdJgN23ahMHBQfz617+WXE66VJ6DzWaDw+GQJbef/exnisHZJUuWyC5zsnXrVlFyUBtvSGH37t1gWTYvtVNuTSUpSUkturu7sW7dOjzxxBOKwV4xyHkOSpMQAWhOE9XynjAMg/r6+ixjn2v8R40aBYfJtg/meN7ongMtPoJYRtgZW0XJQUleOHToUEH1nn766di/fz8mT56MyZMnY9KkSYpSkxLWrVun6/yI1157DatXr5bcDlIrOTAMg8bGxvTLnvr/ggULMG7cODz55JMYPXo0mpub8dOf/lT2XtSsr3TCCSegoaFBMjts69atoltLavUc4vE4du/enTdxUc5zkFqJVQnhcBgPPfQQHn/88aLWmJLzHADBe5CLS2iViaQWF0xh2rRpWLVqVfr7V9rfwozgCJ/10A1IDry5yKHCiVVKL+CWLVvwjW98Q3O9Y8aMwapVqwrsVT5efvllrF69Wrf6Unj88cexZMkS0SyZXFnJ4/HIPq+zzjpLNhCfaVz1WHyPYRgsWrQIzz77rOj57du3gxCSJ+9IkYPf70d/v/hmijt27MjqfygUkp3XotVziEQieOSRR/DYY4/psouaEjl4PB5dyUEpc2hgYEBxzSSzg+OzycFwAencDhodTltlPQeljVH+8Y9/YNOmTWXqjTja2tpwww03lCwj5OabbxaV1y6//HL84Q9/wN///ne89957+P3vfy9bj5aRrpJXotZALlmyRPLcwMCAaExCSlY655xzJJcIyV1nSc5rcDgconKWGKLRKH75y19i8eLFePDBB3XbXlMNOchBKzmMGzdONsbS0tKi2Cezw/CeA+GpqTbgddory69qAsI33HADtmzZgmuvvVZzAFkPPPLII6pfrJS0o3a2NgAcOXIE69atw/XXX591fMaMGVl/67kPg17Ldp966qmy+vmWLVuyNiCKx+OSmxZNmTIFra2tePPNN/POvf3221lLNsiRw+TJk1UtPRGLxXDFFVdIBrzFYLPZcOaZZ+KLX/wirr76atm65aAUlFbKfsuF3W7H+PHjsX//ftHzhBB8+umnmDp1qqr6brnlFsRiMTQ3N6O5uTkdh2hubsbIkSMNGY8gPJ/1ozVcD3M7aHQ4Krw+ipqZoIQQ/PnPf8bzzz+PCy64AIsXL8bcuXMLCihrRU9PD/785z8rXtfc3IwLL7wQF110EcaNG4f+/n7s27cP69atU+X5rF+/HsuXL5c12nru4KYXOdTV1WHGjBmSqZ1bt27FihUr0n/v379f0gObOHEi+vr6RMmhr68PH330Udq4aZ0ZnQtCCJYvX66aGDweDy666CJcdtllGD9+vOJy30qDCSWiLyQ19ZhjjpEkB0AgWDXk0Nvbi6efflrye3I4HHjzzTcNN0Oa8Eb3HCjVxy8tI7Rsyag3Zs2aBa/Xq8qdj8fj+OMf/4g//vGPsNvtmD59OubPn4/58+dj1qxZuqV+ZmLdunWKo8AJEyZgw4YNWS+L3+/HjBkz8Itf/AITJkxQ3LwoHA7j6aefxte//nXJa/TcpEfPupYsWSJJDu+++y6i0Wi6Pblg9IQJE2RXnt2xY4cqclATjH7yySexefNmxevcbjcuu+wyXHnllVlrJSmNnIv1HAqZ8awUd3j44Yfx1a9+VdGrevPNN2Ul1Dlz5hiOGID8gbnhYg6E501HDnG2ctm3LperoI1DCCH497//jbVr1+LSSy/FSSedhIsuugj33nsv3njjDQwMDBTdt4GBAfzhD3+Qvaa+vh6PPvqo7MtyzTXX4Pvf/75iexs3bpQ9b2RykALLslmT2KTiDR6PB2PGjMHMmTMlCSJVD6VUNo1ViRyi0Sh+9atfyV5js9nwla98Ba+++ipuvPHGvEX0lGSfYj0HrbISoDwnqK2tDRs2bFCs57e//a3seaNut8vl2F4DkgMVT7cwMGIsW9EFlvRY44jjOOzatQtr167F5ZdfjlmzZmHFihWKC7fJYcuWLYrywZo1azB+/HjFui699FLF4Pv7778v256SQdcSTNUzfjF16lQ0NTVJns9cpVXKczjmmGPAMAxcLpfkkhapoLTcBj92u10xGL1x40bZmNC4cePw/PPP4+6775aMcRVLDkqeQyETLBcuXKiY0vrrX/9allhffPFF2U2W7Ha77Cq5lQThqbHJgae0NHsPlhDxhPZRip74/Oc/X/SkpVwQQvCXv/wFS5cuxXnnnSeZbikHJT36+OOPV1zeOQWXy4UrrrhC9hpCiCyZORwOWTlDScrIhJ7xC4ZhZBesU0MOmSuYSi1XfeTIERw4cEBWUpo0aZIi8Snt5PbDH/4QU6ZMkb1GyXhLpeSmoNRHLd9lCgzD4Gtf+5rsNUeOHMGFF16YtxMepRRPPvkkbrjhBtnyc+fORUNDg+a+lQO80T0HU5JDAS6snmAYBjfeeGPJ6v/oo49w44034oYbbtBk9JTIQWl/41wsXbpUUavdtm2b7Hk57yEWi6lOt9UrlTUFOWnpgw8+SG+MI0UOmZvyyO1lsGPHjqKC0YlEQjTgncJxxx2H//qv/5KtA1D2HIrdDa7QlNoLL7xQlYd59dVX4/TTT8c3vvENXH/99Vi4cCFWrVqlGAhXIp9KgstRbQxIDijPdmY6IsGVd7lfMSxatEh0Nq2eeO6553DhhRdKbn+Yid7eXtmVW202m+YFAb1eL0455RTZaz755BPZ80ovvtqUW6V6tI5cFy5cKJlnTynFm2++iUOHDkkavUxymDlzpuQM3p07dxYVjN6/f7/sAGHSpEmy5VM4fPiw7Pmenh5ZotYz5pOJQCCgSqallKKlpQWbN2/GCy+8ILvHRQrnnHMOzjnnnIL6VQ7kxnsNSA5U+0IsFQZHjLEc1M0334z58+eXtI2PPvoIK1asUBwh7dy5U/blnjVrlmIMQQxKGSVKRkevuIOe8QtAMEqZ8xlysXXrVtlMpUxZye12583xSGH79u1FramklKigdndAJWmKECK7blipyAEAli1bpvsWnvX19bj99tt1rVNv8DxvbM+BUiq+sayBwRrAcwCEYNcjjzyCK6+8UpcVNaWwe/du/PznP5e9RmnD9kKIAVBeA0dpBKdXrKAUxklOWtqyZYskOTAMk/dcpDysgwcPSu4FYbPZZPd+BpTJ4eOPP5Y9DwjxBDUxLDlpSc+YTy6OO+44xdiBVtx+++2or6/XtU69wfF8lqRvOHIAYDpy4Mq8i5Qc3G43vv/972PDhg2S2yzqgYcfflg2K0MpoBgMBgtqV8lzCIfDstKQXllGpSAHuaD0wYMHJfduHjNmTF5/5OIOUh7d5z73OcX7UprP895776XjI1L46U9/qujhAfLkoGe2mBi+/e1v65IFyDAMVqxYYWg5KYXceK/hyMGMngNnwP2JZsyYgRdeeAE33XST4miwEPA8L7tWkRI5FDo7W83WinKjW71GnHoHpAFh+Yvm5mbJ81JSTGa8IYWZM2eqWgIjE2omvymluQ4ODuKuu+4SPUcIwR133KFqrgBQOc8hhdWrV6sKrkuhoaEB69atw7XXXlt0X8oBwhucHOw2m/zu9gYEocYjB0BI//z2t7+N559/Hv/6179w66234tRTT9VtXZdXX31Vcl5BqchBzUYvcven14jTZrPJLtsci8UKmqUr5z1IjfjFyKGmpkbzFp5qyKGxsVF2TgYAPPXUU1i1alX6WfI8jx07duCKK67QtGR7JT0HQHh/HnroIdx1112aPd158+bhhRdeEN2e1ajgCMmK9xpu+Qy3wy6fw2ZA8DxPgQrvF6qA0aNHY9myZVi2bBn6+vqwdetW7NixA9u3b8eHH35Y0Iqp8XgcL7/8suhex0rkoHWnrhSUJtUBkB0x65kC6fV6JfP1KaXo6+vTvEzCkiVLVI+sUxAjB0AwUFomMaqdKzNlyhTFhRGffPJJPP/88xg1ahQGBgZUZfPk4s0338Tll18ueq4cnkMKF1xwARYtWoSNGzfijTfewDvvvJOXkOFyuTB37lwsWrQIp512miF3elMCx/NZmaKGI4fJTSNMRw6o6Pxo7QgEAjj77LPTMzX7+/vx9ttvp8ni3XffVU0WUuSgFBBXymOXQrHkoGcKqt/vl82oiUQimslh4cKFcDgcmhaOk4otadl/gGEY1fLjtGnT8K9//Uvxumg0Khn8BoT4mMPhkJQBt2zZkrWuVCbK4TlkYuTIkVi+fDmWL1+OwcFBHDhwAISQ9Hty7LHHlmRtsnIiwWV7DoaTlc4/9+xoMRvXVwLUbB3Ogd/vx+LFi3HjjTdiw4YNeOWVV0QNvhikVrFU2nZRbvVLOSiRg8vlkpWV9ExBVUrbLGR7TJ/Ph9mzZ2sqI+U5zJo1S3Xc4eijj1btzX39619XJe8p4cc//rHsumDxeBz//Oc/Rc+V03MQa3vq1KmYPn06TjzxRJx44ommJwYAaA/1GpscACBBeLMZW7P1VxbHHHMMVq9ejTvuuEMxO6W9vV3Uyxg7dqxsObkRpRyUyEHKUKagp+eg5BUobeEqBbmU1lz4/X7JtOCamhrJ+Q650LL8yqhRo3Ddddepvl4Ml156Kb785S8r3usrr7wiejzze/R4PJgwYQIWLFiAr3zlK7jqqquwdOnSovo33EAppX/e/m9jy0oAwBLCux0lTNTXGYYONhSBpUuX4qmnnhLdjSwFlmXR2dmZt8CaEjkcOHCgoD61trbKnleaoaun56CUt37gwIG8gGQ4HFYklSVLluDOO+9U1QeldOW5c+fKphynoHXP6EsvvRTPPPMMPvjgA03lAODiiy/GypUrAQgrodpsNkkZc+vWraJbpY4dOxabNm3C6NGj81Z8taAdCUL4/icfyBrkGpIcEoTnAZiHHKqVHSDsqyxHDoBgsHPJQUlWGhwcRGdnp+bJcC+99JLseSVy0FOOUJKV1qxZg9mzZ2PixIn4y1/+gsceewxNTU148MEHZctNmjQJY8eOVbVMyec+9znZ8/PmzcMDDzygWI/WhRvtdjvuvfderFixQnbmdiYYhsH//d//4Tvf+U76WH19PS666CJEo1GMGjUKo0ePzvonRcBut1txcT8L6sGKTNYyJDmwhOcAaEvSriCMxA3t7e3Yvn073nrrLbz//vvYuHFjUamrZ555Ju655x7Za8QCikrkAAAvvPCC4kqrmWhvb1ccBSuNgPUMZCp5AJ2dnTjvvPPgdDrTC82p3WZy8eLF+OMf/6h4nZLnMGvWLFUB7kJW9T3uuOPwwgsv4Be/+AV++9vfyqbuLliwADfddBNOOOGEvHM/+clPNLdtQV8kbW4WDEkOHJ/fUSPDVqlt4CBIF9u3b0//yx1tvvPOO5qyVnIxduxYMAwDuZi7WA64x+PBuHHjZGWgRx99FMuWLZOdL5CJTZs2yfbD7/djwYIFsnWU03NIIXMFUiVZLIUlS5aoIgc1MZYTTzxRcqc5QNh/oVBpJjUj/9xzz8Xf/vY3dHV1obu7G0eOHEEgEEind6qNfVioDFiSv0CcUclB+04dFYS9QtzQ2tqKM844Q/aaN954oyhyaGlpkTXIgPSEtjPPPBPr1q2TLNfZ2YmNGzfikksuUexHJBJRNJZnnHGGItHo6TkoxVXE0NfXh97eXsVJVfPnz4fL5VLc90DNEinz5s2TJQet8QYxpLJ2LJgTLCF5a6gbMlvJdORgqww7qBnxSaUCqoWalFMpQ6dmPZm1a9cqznngOA5XXXWV4qhbzfaLenoO8+fPLyilU00swePxKJK63W5XtZxI5jpLtbW1mDJlCpYsWYKlS5fi+uuvx5VXXqncaQtVDY7weeRgSM+BEKpuUX2DwK7z8r5aMHXqVNnNVz788EO0t7erigGIQWn1TIZhJMkhtSy33OzY1tZWfPnLX8ZDDz0kqsdTSrFq1SrZewQEolSTAqqn5+B2u7FkyRLFIHkuWltbVcUeFi9ejM2bN0ueHz9+vKp5DPPmzcMrr7yC0aNHV0U+vgX9wfH55GBMz4Hy2vf4qyAq5TkAyoFESiluu+22guretWsXXn75ZdlrTjjhBEmDyzCMqv1yOzo6cPHFF+PRRx/F9u3b0dPTgyNHjmDt2rU444wz8PTTTyvWcf3116sKvOs9eaqQ1TbVxh0y11lyu904+uijccopp6Rz+W+++WZV9TidTkyYMMEiBguS4AjJG5Ab03PgaemmN5YATputYuRw8skn49FHH5W95rXXXsNzzz2naQniwcFB3HHHHYrXKa1aecEFF2D9+vWKy3FEo1GsXr06/bdSEDwTU6ZMwRe/+EVV1+q97MLpp5+Oo48+WtO8DTVLgABCsDm1PpHR9wKwYG5wJF/KN6TnkLtdndHhtFfuMS5atEjViPCOO+5QnY9+4MABXHDBBdi9e7fitUrkMH36dPzv//6vqnYzoZYYbDYbVq1apXrnLr09B4/Hg0ceeQRHHXWU4rWTJk3CmjVrcM0116iu//jjj7eIwULJQfh8tcaQ5MBTaipycFQw5uDxeGSXeU4hHA7jvPPOw7333itpANva2rB+/Xp85StfUbWjV3Nzs6rF2m644QaMHz9e8bpCcN1112naGlXv7T0BYSLayy+/jJUrV2L27Nnp2bw1NTWYM2cOli9fjkcffRQvvfSSKpnNgoVygxMhB6PKSvLrPRsQhOcrFpg+99xzFWMDgJBvv3btWmzcuBHTp09HXV0d6urqkEgkFPcoFsPXvvY1Vdd5PB6sXr0ay5YtU+0RqMGZZ56ZNdtWbV/koGVtpUy4XC5cfvnlkktMW7BgBMQ5jnT3DcQ7e/tjhyP9sY5wb6w9FIm1dIdfveiM7EGmIcmhP8Eq7yFoMMQSLHw17oq0ffbZZytOOMtEZ2cnXnvttaLaHDdunKbZzaeccgq+973v4Z577ilo74hcLFy4EPfee6/iwoC5cLvdsNlsCAaD6SUaxowZk/5cKg/HgoVyYDCe4I70DcQ6e/vihyN9sY5wb7ytJxJt6QnHD3SFY0d6+1NZSbmjtDzjYUhy+P2O91/d3xNZ/vWTj6+onq8F8QqSg8PhwHXXXYcbb7yxbG2uXLkSbre2+73yyisxdepUXH/99Yr7DMvhq1/9Km6//XbFPSOksHv3bsXAtAULRkRkMMYe6euPdUb644cifbH2UCTeHorEBOPfE4sMxsRWl8ggAibzQyZBmCNbCcDgG/tacaCnF1edehKa/MZPwYuzbEV3g/vSl76Ehx56CHv37i15WwsXLsRZZ51VUNlTTz0Vzz33HFasWKG4oF8uRo0ahWuuuQYXX3xxQW2nYBGDBSOCp5SGBgYTR3r744cj/fGOcG+8IxyJtfZE4i3d4diBrlA8mmAz3e60cc9woHONfs4xSTNlGnIYAIADoV786K9bceX8EzFjjPy+tZVGjM2bQ1JW2Gw23Hfffbj44otLutHJjBkz8Mtf/rKoOsaMGYOnnnoKO3fuxHPPPYe//OUvshvjTJo0CcuWLcPFF1+sevMaCxaMBo7wtLt/IN7Z2x8/FO6NH4r0xdtDkXhbTyR+sDsUP9gdinPCXjYSgTlG0uhTqrg6tAJp5JMDY8RNzCYsv2U2gPTmtwyA/542EV8+YZJmjblcGICNHjduTMU79+KLL+Laa68tSd0LFizA2rVrddkFLBOpgPiRI0cwMDCAaDQKlmUxdepUzJ49W/UCdxYsVBJxluO7+oRR/+FIb7wj3BtvD0USrT2R+IEjoXhHuDfOUzGFR5QMRI7lkUPWtTmmUaFOJvf4Pf0bHsya6m9ozyEFCuCF9/dhX3cY31kwA363ulU8ywmOk16uuJw477zz0NnZiTvvvFN2CeVC6r3nnntUr6CqBS6XS9PuZxYsVAID8QTp7O2Pd0b64ocivYmOUG+8LRRJtHSHYwe7QvEjff1czugwx0Az2aN3eSFaZKRPGRmCyPUetMpLppGVRJPN/3OoG7e+vBVXnToTExq0bdxearCk+AwcvXDZZZdh+vTpuPbaa2XXNVKDxYsX4+qrr8ZJJ52kU+8sWDAmIoNRtrO3P3E40pc4FO6Nd4R646094URLdzh+oCuU6I3GhNEWIz0qVxF4FCMIMUMuAVGC0ENeMg055O8ek0TPYAyr/74dl8yagjOOVZ6VWi6IbKRUUcyZMwebNm3C+vXrsWHDBnR0dKguO2LECMyfPx/f+ta3dFnO2YKFSoOnlPb0D7KdvX0p459o64nE23oiiZbuUOJgdygRTbBiLzFVmWeipOlDgTpUlC+2/dzjWf0xTczBAwnvIROnHD0a35g7HW5H5XcUbeuP0XlTJlU85iAGQgg2b96M9957D5988gk++eQTRKNROJ1OuFwu+Hw+TJ06FTNnzsTMmTNVLQVhwYKRwBGedvUJo/7Dkb5EeyiSaA/1JtqSI/+W7hDL8XymsVOp86eO57zaMt6DcFquXib/GKPUfi50jz9c07/hwaz1cgxJDgAwYfktBCqW9xhb68fVp56EUUFfGXoljYO9g3TB8ZMNSQ4WLJgdcY7jOyP9icORXvZQuDfREepNtIUiidaecOJgVyhxKNzLJoO9WQaNyTXEmoxm7rGCCUKMHPKP60MQYumtUvVmEsS3+zc8+FHmSaPKSgAQBaBo8dsi/fjRK9twxbwTMHt8cxm6JQ6qw6xfCxaGKwbiCXI40sseDguST3u4l20fknzYrr4BsVxxGckkdQEFkxkIVg4KlEHeSXfCSPEH08QcACHuoModiLEc1mzehbOnHIOLZx6HSmyvQFX/dixYGH4ID0S5w7197OFwb6Ij3Mt2hCJsa3c4PfLvjcZEJ3cBTMqYl1OTlyhPaY4/wEh4DwyEizOvNmr8IdUf05GDJvz1w/34rDuC7yyciTpPmZeyoGoSFSxYqD5QStHVN8AejvSxh8O9bEe4l20LRRJtPWG2pTvMtnSH2GiC5Rn1kkl2yiVl5N4sRUOY9B6yW1Od8pl7XDVBiDQlShBGSW+tbnIAgL1HQrjt5a34zsIZOG5kWdfBt1wHC1UJjvD0SG8fdzjSxx4K9ybaQxG2rSfCpox/a0+ITc7szQaTbbSSdqqw0a9myUWTvKRxpJ1jwcUJYkjegeKo0QjykqnIoeA9HSKxOO5+bQcumjkZ50z5nJ59koTlMlgwK+IsRw9H+tjDEUHuaQ/1ske0TZ8AACAASURBVO2hCNfaHU4c7A6xh8O9HE9z7XxhkCAIJckDGW9YASmbqVoMHX+oUPtpmIocCvIcUuApxZO7PsInXWFcMe8EeJylvVWGkXbtLFioJPpjcT456ucE4y+M/Fu7Q1xLd4jt7htI5ffToThpGoUbIkqZXO9BU3lpeclM8YfkaUPLS6R/w9q81VyrlhxSeLvlMFrDfVixaBbG1vr1qFIUDKMwBrFgoUQID0TJoXCEOyTo/Vx7T4Rt7QlzrYLez6Vn9maDiv5gKZI2pqCRukh9+QQhIy9pIQgxlDP+kFfYZPKSbKYSMAzIAQAO9w3i9r9uwzfmTsP8Y8boVW0W7AZdENCCuSEEe/u5Q+E+7lA4wrWHerm2njDb1h3mWnvCbGt3iIsKy8Wni6gUORmaTxDZhli+HvWGVBtBKLdV2viDcvvSx4oor5e8VFD8wXTkoOs+0glC8NC23fi4K4z/b9YU3fd9tsjBQiHgCKGdvf0kKflw7T0Rrj2UGvmHuLaeMJFZxnnoWNaIkOYShKQhEzFJTNJ6Zp5VpekrtSXSdjnjD2Iwk7xUyns1HTno5jlk4h8fH8SBngi+e+pJaPDqt+mLrRKTKywYHnGWo4fCvdyhcC9pD0XY9p4waQtFuLZuwfgfivRy2asUpK1yLuSNQ56VzzsgWV6eIGTLS6Gc8YcCyhtKXjJC/MEihxQ+7Y7gtpe34tsLTsT0UY261Km3J2LBHOiPxflD4V7SEYoII/9QhLQJej/X0h3iuvsGJCZ3pcGAyZR3RIxD5rXqCCJ5TD1BiN5cZeMPYjCwvGTq+INFDpnojyfws9ffxvnTJ+GL0yeqU2ll4LA8h6pEaGCQ7whFuEOhXtIeipD2UJhr64lwrd0h0tIdIn3CzN7kS5j3E1CpyYOh6glCDEVLDmWLP4jAUPKSvIyjb1uSx/SKP6gubzpy0DXmIAZKKZ5572Ps6w7jW/NPhM9V+BaUTpvNIgeTgacUXb39pCMcIR0p498T5tq6w6S1O0Rae8JcLMGKvXRqJRdNL7KovKPVEOTJI3rFH1S2r9RX48pLZoo/SLRfsLyUEDtvZHIoqeeQid3tR3Dby1tx1akn4Zj6YEF1OO2VXzbcQjZYQmhnpI/vCEVIRyhC2kMR0tYdFmSfnhBp74kQNmsfDuFFUsHyEkYzzxBLl88zmrmGpIjRYxHxB9HWDC0vpQmiYII2UPxBDOWQl2JiJS1ySKJrIIqf/O1NfH32VCyeOF5zebvlOJQdMZalh0K9pCMU4ZOjftKW/NfaHSaHw7181jLO2V+RyMslvEjyI7qcGpQJQoPRtOQlDfJSkUazlPEH7e2LHyubvGQ6Wams5AAAHM/jd9vfxyddYSybfTxcGr0BluPgdBj5kZoLfdE4PRSOkI5QhG/rCaeMP9/WEyYtXSG+pz81szdvmJwL4UVQHhFmVaJAEOpHj5a8pFpe0kgQYj0ug9FWKm8aeYkBQCm1yEE1Nn/ahgM9vbh60UkY6feqLheNJyxy0IBQ/yDfHgrz7T3pkT/f1hPmW7tDpLU7TPqiMUHmEV4EmdGjonEYgiJBDL1Iqlx+3Y1m1cpLqglCAgptpeWlAstXlbykhSAAE3oOJQ9Iy6El3Icf/XUbvnnKCThp7EhVZWIsi8IiFtUHnlIcEfR+vj0U4dt7wnx7T5i0Csafb+sJ88lgb/IHm/dqDL20wnBSv9Gj4psor9PKN526tkrlpeIIIh9Vnd6qvX3xY+lOFHmvkr9r05FDxTyHFAYTLH71xjv4wvET8JUTj1XcRCjO5q1dVbVgCcHhcC/f3hMRCCDD6Ld1h/mOcG6wN40SvkiKb7LmF9nE8pKElKaatMwUfxgqZU55SZB3oCgvlaqvFjkUAgrgxf98ik+7I1i+YAaCNS7JaxMsp8I7NAdiCZZ2hCI0Jfu09YRoW0+Yb+8Ok9aeMO1MB3vTt5v/45TRWfOuVTBaCmvSa385zCsv6XCv5og/qC5vXHlJC0GINCVKEGLegxTUyksWORSDDw5347aXt+K7p87EpMY60WsSnHk8h75ojLaHIrRDGOnTtpAw4m/rCdPW7hDf0z+Q+gHl/ZCZ/B+seCMKOqtYARUEUa3ykihBiMhLUp3VeK/FeTXDXF4qzoM0Xnqr6cihojEHMYSiMdz56lv46klTcObko/POs5yojFIR9PQP0PaesDDa7wkLn7vDfGt3iLb2hGl/NMaL/ARzR4SiyH9pRUY08iixy6+/5CE/opMtr2mkLtKwceMPQ/0pWfsSBFEqeUkLQYihxL9rxU4U2r41z0EPEJ7ij29/gE+OhHH5vOlwO4bSXTlxjV13JIO9tC1p9JMkQJOaP23vDvMxls0son6kXnJ5RKm8aeQl8bbyn59oQ1r7KmISdLjXYSMvlcFoK5U3tLxkLs/h07Wr4xOW30IAGHLq8faDHWgN9+HqRSdhdNAHAOB4fciBJQSHQpEs49/aHaZtPSHa1h2mHaEI5QgBkyt5CD8OdZKFZnkk+1oJ70H8hswlL2khCDFY6a3FGFJrc6B0eVW/tXyC0PBbS/+uzUUOSQwAxs0Obe/tx4/+uhWXzzsBc48aBUJ45UIAYgkWScMPwfCH0qP/tu4QOiN9fM6evYDIF05BmSyC0PzjKE7ykCCIMo/IFIxDYQQhAiu9NWlqJcorPkD17Zsr/qDcvvSxIsoXMUDIAmUAxpTkMAgDkwMAxDmCB7e8i31dx2D+hHEAgN5oDO09YdreHUJrd5i294QgGH5h9N/TN4D0FyuT2qbimBSKG9GUZfSrVF61vKSFINT1VeFe5V1+pfLFjdRF7kb7byVv9GttDqSmfX3KG3JzIFOSgyHjDmJ45aP9eO39vfTQo0/y/bE4oGFEqM1oZl8r4T2oLi9xTKoukfZLJi9pIQgx6GA0c6+15CXA2hwoVaX8b0XDAKHymwOJkoPRd6gxDTkAQHf/IJLEACjajqzvV+xahfKZxXO8faqtvHBt3u9YQ/u511Lp8prvVb5fVNW9ZtUh376iOcpVVhTaz6+vqGcNmqttyTxr9ffK5B5QVT6/b7l1FPW7Bs1/1vm/NVno8F5kFs951srUJXP/ufZZ/lmpYMli7tUih1KDYZjciQ4KL4fiV17cy6W5vIhxEDVuoiXVE4Q4Snyv+j/rnBp1JT3Z8toIQgxFP2uRp1m237UEQeg3QFCGDMGqqlfn70r2eatp35TkYLi5DnJgbDa1s+DyvzBVMwyky0t4D+rblxs9qiQI1W1pvldV3oP69pX6n12fyLVDz1rViE7989PUV3kDrVxepBJNXo1o+7r/VkQ9NfXlc0mryN8KHapAB6OvynvIOC3XlsgAQf29is5zMDo5mMpzsNlsvMoXNgMVk5d0kEfyLlE/ohle8pKE0VQrqash2CJGj3lFq1leokW/FzTfqGsqL1e4QvKSKT0HU5EDwzA8UMiITj833FTxB8n2VR0rsLyekofwrFXIS1JNF/WsLXlJU3kd3ovM4nrGH7S3L36s4AGCRQ6lhs1mk88TyEf+iyTtWpbhx5WJYS0vCceVR4RDbRfeVn5pS15SLS8ZKv5QcPmKyksY+PNvRPeQNjo5mCvmMJR6pzCiUjhumPiDCMwtLzEidWg0mnkXaHP5S06wVSMvqSYI1eXF5aUCy1eNvCTqNQDGJwdzeQ5MVta9FoJIomLxBzEYWF4ydfxheMlLho4/oNrlJTX3apFDOcDYbGI/3LzLxIrKltBUXhFmkpdKfK/6P+thLC+ZKf4wVEqn9vUpr1peYoTTcm2p9iAtcigHGCZvvm6pjKaq8gaWl3S417LFH5IVKl1rWHlJ7b1KjX41eTWVjD+oLm9ceUkLQYg0pcJbz79X05KD2WIOdrEXoWrlJXPHH8TqMJO8JEoQIvKSFDTea0XjDyL1mUpeKu53Xdr4g+gcB8D45GA2z0FyeXGTy0uaRo+5KG38QY/yhpKXinrWho4/DPWnZO2XWV7SQhCK5RXaKrC84gDBtJ6D2cjBAVADpHzmFpeUl4ofqVf8Xk0jL4m3Jdk/U8cfzC4vlXPgJVG+bPKSRQ5lwpDnUFl5SQtBqCov09Zwl5e0EIQYrPRWLeXz6tMkL2khiALK6xp/yCtcInnJtORgqpgDANhsNkhozwCKlJeMHH9Q1b6p5CWNRjMXVnpr2eQlc8UflNuXPlZEeclOmJYczOY5YChhSVpeMnn8ASaXl8r+rIuIP4j1p4rkJTp0TBxFDoYKKa+jV2MOeckKSJcL2dmspoo/qGvfuPKSFoKQKK+6rWSFStda8pJEq2aKPxRQ3lDykhJBWJ5DucDYRB5pZeMPIjWZSF4yV/xBrK7qkZfyvC9Txx/EYGB5qWTxB9OSg/liDnnz4EoUfxCHaeQlk8UfdGm/YvKSeoIoibwDxcFQaX/XJpOXytBW1r1ZslK5kD9JGqhuecnU8Qd17RtXXtLUV2PHH1S2r3StceUlJlWlQnmZtkoSfxBdkRUwODl8unY1C4CtdD+0QGx9pTTMLS/pII/kXSI3osk9NZzkJWtzIC3l8+qzNgeSuFoMpvUcAJN5D0nPQXb0mHdGpBqxqmVLaCqfWZOJ4g+S7as6VmB5PSUPa3MgE8lLOrwXmcX1jD9ob1/8GAVMHHMATBZ3yI85ZEJPeYmm/ldB7TITw1pe0iwZVCz+IFZ6GMlLJos/SJTXVV4yNTmYy3MYylYqlbykg9HMLV616a1idWmRlxiROjQazbwLtLj8VZveKori5CXVBKG6vLi8VGB5w8pLlqxULuQEpKWNg7njD2IwsLxk6vjD8JKXDB1/QDXKS5bnUC6IyEqyo8e8M/mHlEc08hhO8lKJ71X/Zz2M5SUzxR+GSunUvj7lVctLjHBatC1Tk4OpYg6ik+BEUc3prSIoTl7S4V7LFn9IVqh0bdXKS5q8mkrGH1SXN668pIUgRJoCA0tWKh8YhuHFDksWGE7ykrnjD2J1mEleEiUIDfKSxnutaPxBpD5TyUvF/a61xR9M7TmYihxsNhsncWo4yUuaRo+5KG38QY/yhpKXinrWho4/DPWnZO2XWV7SQhCK5RXaUlveIodygbHZiLYSppKXih+pV/xeTSMvibcl2T9Txx/MLi+Vc+AlUb5gecnU5GCqmINNXFZKwajykhaCUFVepq3hLi9pIQgxmCn+IHbATPKSFoIooLyu8Ye8wirlJVOTg9k8BzlyAMopLxk5/qCqfVPJSxqNZi6s9NayyUvmij8oty99TE15KyBdLtgYsd+4GlibAxXVvrU5kFR/qkheokPHxFHkYKiQ8jp6NZWRlyzPoVxQmcoq8YWZKv6grn3jyktaCEKivOq2khUqXWvJSxKtmin+UED5islLicGnHpLsuRnIwVQxB0Z8zW7RSyXPVDb+IFKTieQlc8UfxOqqHnkpz/sydfxBDAaWl1QRhORy3YA5yMFUnoNNPTkACqPHvDOqylenvGSy+IMu7VdMXrI2BzKTvFRMW5KSEmCRg+5gbKqnSMugmuUlU8cf1LVvXHlJU1+NHX9Q2b7StcaVl5hUlQrlZdpS9B4kg9GARQ66gwHs2otIwNzykg7ySN4lYt6DVH3DSV6yNgfSUj6vvmG7OZDpPQdTxRzAMI5CSuUfMqa8ZKr4g2T7qo4VWF5PycPaHMhE8pIO70VmcT3jD5KwPIdygmEYrZ6DDKzNgYpq37jykmbJoGLxB7HSw0heMln8QaK8pPdges/BVOQAQMPKrNnFJM8UJy/pYDRzi1dteqtYXVrkJUakDo1GM++C9DGd4w/i5fOar1p5STVBqC4vLi8VWL4s8pLlOZQb8luFykLaOJg7/iAGA8tLpo4/DC95ydDxBxhdXjK952CumAOQuxuc5uL5h4wZf9CnfEXlpRLfq/7PehjLS2aKPwyV0ql9fcrneQ+mJwfTeQ5FkoMEqjm9VQTFyUs63GvZ4g/JCpWurVp5SZNXU8n4g+ryxpWXcp+1ucnh07WrCRRuwmgoMOaQVYXkmeEkL5k7/iBWh5nkJVGC0CAvabzXisYfROozlbxU6O/a9DEHwGTeQxExh0wMJ3lJ0+gxF6WNP+hR3lDyUlHP2tDxh6H+lKz9MstLWghCsbzIMXN7DkmYKu6gg+cgA1PJS8WP1Ct+r6aRl8TbkuyfqeMPZpeXyjnwkihPgSohB1N5DjrGHIwqL2khCFXlZdoa7vKSFoIQg5niD2IHzCQvaSGIAsrrGn8AQC1ZqdzQSVZKoXzykpHjD6raN5W8pNFo5sJKby2bvGSu+INy+0PHLM+h3CitrJSCtTlQUe1bmwNJ9aeK5CU6dEwcRQ6GCimvo1dTfPmq8BzMFXPQP5VVokJTxR/UtW9ceUkLQUiUV91WskKlay15SaJVM8UfCiivm7xkeQ7lRmnmOWgzmmWMP4jUZCJ5yVzxB7G6qkdeyvO+TB1/EIPR5CWLHMoNXbZ0EIfs6DHvjKry1SkvmSz+oEv7xpGXJJ91SeQdKA6GSvu7Npm8lHm8KmQlU5FDiTwHGVSzvGTq+IO69o0rL6nrK5WRd9Qdy6wr51q94g8q21e61rjyEpOqUqF8JqrCczBXzMFm40pZveQZc8tLOsgjeZeo18SHl7xkbQ6kpXxefVWzOZDlOZQbNoYhJW7CNPKSqeIPku2rOlZgeT0lD2tzIBPJSzq8F5nFC4o/VIXnYCpyYGy2UpODBKzNgYpq37jykmbJwDjxh0Laz6vENPKSyeIPludQbtgYhi9DM6WSl3QwmrnFqza9VawuLfISI1KHRqOZd0H6mM7xB/Hyec1XrbykmiBUlxeXlwosX5C8VBWeg9liDqrF22Kbyj9kbQ4k3n4p5SVTxx+Gl7xk6PgDyi0vWZ5DuaHz8hlKkB095p1RVb58Oq328hWVl0p8r/o/62EsL5kp/jBUSqf2VZavCs/BVORQnuUzlFDN6a0iKE5e0uFeyxZ/SFaodG3VykuavJpKxh9Ul6+MvMQNPv2wbGzUCFZMDcxFDuWf6KDJaFatvGTu+INYHcNJXtJ4rxWNP4jUZyp5iYGKDdTMQg7mijkwTCWe63CSl4oyWqWNP+hR3lDyktpnLV7eyPGHof6UrP0yy0taCEI23gCYhxzM5jnYK92HIZhKXip+pF7xezWNvCTelmT/TB1/MLu8VIrBSNV4DhY5qGxa8kxl5SUtBKGqvExbw11e0kIQYjBT/EHsgJnkJS0EUUB52fiDRQ6VAMMwjko2n3/I2hxIvH1TyUsajWYuTBV/EIN55CVzxB+qRlYyVcwBQAUW31OCtTlQUe1bmwNJ9aeK5CWZ7DXp40UMRtSU19Gryb62ajwH85FDZdNZJX4wpoo/qGvfuPKSFoKQKK+6rWSFStda8pJEq2aKPxRQXlReqg7P4dO1q3kA0Ur3QwvKPBFODJqMZhnjDyI1mUheMlf8Qayu6pGX8rwvU8cfxFBKealqPAfAfHGHSncBKGf8QXX7xpSXTBZ/0KV948hLks+6JPIOyhl/EIFB5KXq8BySMJW0ZIxZ0lKoZnnJ1PEHde1Xibxk7PiDyvaVrjWuvGR5DpWCAWSlFKpVXtJBHsm7RL0mPrzkJbWbA6n/rgwSfxCFoeUlWvR7kSQIixwqBYPISimYRl4yVfxBsn1Vxwosr6fkYW0OZCJ5SYf3IrM4rSpZyVzkYGhZKQVrc6Ci2jeuvCQcV5SXMtouvK380kXFHwppP68S08hLFYw/VBU5mCvmYCzPASidvKSD0cwtXrXprWJ1aZGXGJE6NBrNvAvSx4ZzeqsoipOXVBOE6vL6xh8sWalSMFDMIRPSxsHc8QcxGFheMnX8YXjJS4aOP6AYeamqPAdTkYOBZSXZ0WPeGVXly6fTai9fUXmpxPdazemthbSfV4kW0jJT/GGoVOHtW55DpWBAWUkB1ZzeKoLi5CUd7tVKb81uvmTykiavZhilt1YVOVgxB/2gyWhWrbxk7viDWB3DSV7SeK/VnN6KQt4LS1aqFGzGlZVSGE7yUlFGa3ilt4rWWIpnLV7eyPGHof6UrP0yyksWOVQKDCP2GzMDTCUvmSn+IFHeNPKSeFuS/TN1/MHs8pKaZ1VVspKpyMFms3GV7oMKGFVeMlP8Qaw+M8lLwyn+IHbATPKSnvGHqvIczBVzsNlIpfugEuWTl4wcf1DVvqnkpeEUfxCDeeSlysQfLM+hUrAxDF/pPhQHa3Ogotq3NgeS6k8VyUsy2WvSx40Sf6gqz8FU5MDYbGaKOUj84EwVf1DXvnHlJTPFH8Tbr1J5yeTxB6nyFjnkItZxEPHOdj2qkoXNfAFpo8YfRGoykbxkrviDWF3VIy/leV+mjj+IQct7UVWyki4xh9733kbHC08g0d2pR3WSMPAMaTmUL/6gun1jyksmiz/o0r5x5CXJZ10SeQfljD+IoETykuU55MLuC4ByLDpf2Qg+ofh8CgZj8Flw2lDN8pKp4w/q2q8SecnY8QeV7StdWx55iY9ufIRVqnzYkYP36Imw13jgHj0eR159QY8qRcEwjJmebSaqVV4yU/xBrC4zyUvW5kBayufVV3J5SdWo2EwGTBdyqBl9FByBWjQtOQ82lxuhnZv1qDYPJiYHwETykqniD5LtqzpWYHk9JQ9rcyATyUvZnnH2e6EYbwDMRQ66xBwYux12XxDcQB8aF5+Lwf17MXhwnx5VZ7fDMHbdK604rM2BimrfuPKScFxRXspou/C28ksXFX8opP28SkwjL+kUf6g6cohCleSpDO/RExFt+RSMw4m6WQvQ9c+/gPL6TkuoAnIolbxk4PiDCMwtL2WPHhXbymvOsPGHzAOayhtXXipn/CGqpkLTkMOna1dT6OQ9eMdPTHsLNrcHZLAffFz34HQ1xKSljYO54w9iMLC8ZOr4w/CSl8wRf0ioKWgackii6LgD5Qm4gV7YHE6woW7Y3W4AKEnmUhWQA2Ci+IM+5SsqL5ko/iBa43CSl8wUfxgqJaDqAtKADp4D29OFrn+9grrZixB+ZwtsrhoAAB+Pg+vvLbqDmTDpXAeVqOb0VhEUJy+ZKf6QrFDp2qqVl0wTf1BdPl9eqkpyKNpzcNY3gYv0INF1CDZ3Ddi+CADBc4i1H0S07UDRnUzBoPtIF4JSxR/yKzGyvGTu+INYHcNJXjJT/EGkPl3lpaoLSAM6kANjs8E3cSoiu7ejbtYC9P1nFwDBc6gZcxR6d28vupPptqqHHIDhJS8ZOP6gR/kqkpeMHH8Y6k/J2i9QXrI8Byk4gnVw+AKwe/1w1o4AIHgODn8QNncNEl2H9WimymWlFEwlL5kp/iBR3jTyknhbks/P1PEHs8lLVUkOumQrxQ+3gUSFbK7amfNgc9eks5VqZ8xD+N039WimmmSlFIwqL5kp/iBWn5nkpeEUfxA7YCZ5SaotS1aSQt2sBYgfaUe05VPYXDWom3kKuIE+AICrYST4RBxsb7jodqpMVkqhfPKSkeMPqto3lbw0nOIPYjCPvFR8/KEqPQddyME9cgwCU2akPYTg9NkY3P8xYu0HAADeoyZi4JP3i25neMhKKVibAxXVvrU5kFR/qkhekslekz5eiviD5TnIYcTcxeAiof+fvTcNjuNKrwXPvTeXKlQVgAIKxE4UKJKSQIKAqG7taqrdrV7c6h63PfJ7YzvieTw9tt/8mB8zv+dRnDcTbyZmJsIx8SaewxOexT8cYTPssd3h163etbRaUkuthSIkUSKbIrFvhaWAqsrMe+/8qMxkFlAFFKqwVBbuiUAAyMq891YRzJPfOd/3XeRn7oBoGjqffBbzP/wHOOuriPQPIT99t+45mjRyACr+wYbKf6hu/saVl8LkP5Sfv0nlpRD4D00ZOezbPtJUN9D51Few9PMfAVKiJX0GLekzmHvx70A1Dfm5qbpbatDmjhwa1X8oM1KI5KVw+Q/lxmoeeWlb9BVq/yGIpiSHfdoN7i4279xES/oM9NYk1tx01o4nvgxhFXD3r/8c0rFhLc7WNU8TRw4eDs9/qHr+xpSXQuY/7Mv8jSMvVfysD0TewWH6D2VQhbykZKVKyN29hfkX/x756TvofPJZrL73JkQhD6obOPHsb4EaxZYahQVFDrWhmeWlUPsP1c3fJPJSY/sPVc6/27m1yUsqcqgIKSEFx/xPvgui62gbewTLr/8EAGB29aL7N38XVDfAoi11TXNMDOlmlZfC5D+UG+s4yUsN6z+UxdHLS01JDvviOegdXQCKT/aZN36G1nMXYWeWkJ+5AwCIdPdj6I/+G8SG769rniasc6iE0MhLofIfKs5f1bEar99PyaPqzYFIhakbx3+oZf7GlZeUrFQJRkcXiKah77f/FTY/+xT52Ul0Xvo6ll79ISTn906s8+ZOKOW7n9XMUJsD1TV/48pL5W7mIfEfapl/2yChkZcqEERTRg77Qg56MgXCNAAEXc/+FpZe/QH0tiRa0mew8qvX9mMKAAAl5DiRw0HJSw3sP5RBuOWlcr5KU/gPwQN7ur5x5aV6/AdFDpXgNd8rzE8j0t0Ps7sPq++8jvaLT2Dz1x/DzizuxzTHMXKofHMIt/9QDmGSl46T/1BuvMaVl47Gf2hKcti3Oof4mXPITd4GAER6BrHyzmtw1lfR+dSzWHz5+/syByXl/iaaHqHxH/bn+iOVlyqsteo/u0P/rI+xvNRI/oPyHHaCmepGfnYSIp+DkeoGpMTiy99HpG8IelsH1iferXsOQulxJIcKaOb01jKoT14Kk//gDrjbuU0rL4XGfwigKSOHfSOH9Q/fRduFz2P12lsw2jtBNB0sGsPa9V+h4/HfwOr7b4Bv1jfdMcpW2oqD8h+2D9LI8lK4/YdyYxwneSlM/kOZ8XaUlxQ57ITNOzcR7RtCbuo2hGNDb+9A7L4HsfLOLyBsC+2fexpLv/hRXXMckzqHSjhO8lKY/Icarm8ieamR/Yd76zmw+d2/taYkh33zHNrGH0PmVz9HgQdd4gAAIABJREFU2+jnIW0bRmc3nOwq2kY/h6VXXkT89Aioptc1ByEkbJ/vISBU8lKY/IcK1zervBRq/+Eo5SUJwKpmwFDdvG79+b/LA6ivG56LaH8afGMdRqobLNoCI9WNwtw0Wkc/B3t1GdlPriP5+S/UNYcih4aVl8LkP5QbL0zyUrUEUfnfKjz+Q7kDjSYvWbn/7y+r8kLDePPav7bdn/8CMm+9DBCC6MAwCvPTIJShbfwxLL32o9KCuBpACGH7tNQw4/DkpUb2H6qaP1Ty0nHyH8ohPPJSKUFUJSkBx5wcjM4TIFRDYWEWems7qG6Ab2YRPz0CCIG5H/w9pKidIBQ57AS1OVBd86vNgSqtp4nkpR2y1yof3+3voqnJYV98h42bH0JyjuTnn8bKW68AAOJnz2Pj1scgmo74/aMghMDJrtczDTm+nVlLUOFDCJX/UN38jSsvNbH/UBUaVl46ZP+hqclhXyIHe20Fy6//BFq8FXoyhdzkr5F4YAzZGx8AAGL3PYjoydOgrL6Hf0UOPhrVfygzUojkpXD5D+XGah55aVv01ZD+Q1UFcMAxJof4fQ9ifeJdbP76Btofehyr774BGonC6DyB7KcTMFM9yE/fAYsl6prnmKezbsXh+Q9Vz9+Y8lLI/Id9mb9x5KWKn/V+yjsl5x5iemuuuiUdY3LQWtvRev4iFn72HyGsAqInTyH76QTaP/cUVt56BaKQhxQOnPWVuuY5xoVwe0Azy0uh9h+qm79J5KXG9h+qnH+3c6WsKo0VCCc51Ow5bN0Tuv3ikwCA+R//IxIPjmP9w/fAojHETj2Aue9fBd/IIj8zWddilay0Dc0qL4XJfyg31nGSlxrWfyiL/ZWXVORQDtbCbEl6KjUjaL/4OJzsOlbe/jkS949ifeIdtD30OKTjwMmuwVqar2uxSlYqi9DIS6HyHyrOX9WxGq/fT8lDbQ50CPKS8hzKQW/vwOr7bwIAhJWHsC20nv8c9NZ2bN65CWpGsPnZpwCA7q8/DxaNwV7L1LVYFTnsBWpzoLrmb1x5qdzNPCT+Qy3zbxukkeQlla1UDtlPPkBhYQb2yhKk7WDplRdBGEPiwXFE+4ew9OoPELvvAay9/ya0RBu6v/Y7iPQO1rVY5TlUxEHJSw3sP5RBuOWlcr5KU/gPwQN7ur5x5SXveFOTQ82ew9q1txBLn8Xiyy+CRiIozE1j/aP3ED9zDvZqBm1jjyB74wMU5mfA85swT/Sh7cIjdS1WyUo7ovLNIdz+QzmESV46Tv5DufEaV16q339oanKoXVbqOIH8zF1osTiyn1xH7PSDWH79p7CWF9A29ih4bhNaog1SSqy8vT/bhSpZaVeExn/Yn+uPVF6qsNYqntF3vP7exPVdvx3HWF46KP9BeQ7lEOnph7Oxho4nv4zVd9/A+ofvov3iE5j/4T/APNELe2UZ7RefgJNdw8atj+pOYwUUOdSOZk5vLYP65KUw+Q/ugLud27Ty0lH7DypyKIf46XOwlhbAIi1of/hJ8NwmCKWI9Axg6ZUX0f7wk1h9702c+PJ/AlHII/PLV+peLFWyUjU4KP9h+yCNLC+F238oN8ZxkpfC4j80deRQs+fAYnFE+04iP/0Z4mfOwejogrAK6HzqWViZRRTmp/1zO5/8MrKfXIe1NFfXYlXkUDWOk7wUJv+hhuubSF5qZP/h3nr2Mr+qc6iE1guPYPmNlwAALJbA+kfvA1Ki+9lvI/PWK4gN34+Vt19F4sHxoifhnlsrFDnUi1DJS2HyHypc36zyUqj9h/2Ul5o6cqiLHMyuHujtndi4+SFiw/eD5zaQeetVaK3tSD39NSz94kfQ4q3ITd1G6gtfh7O2suv/iJ2gZKU9oVHlpTD5D+XGC5O8VC1BVP63Co//UO7AQctLynPYCR2PPYPMW68iP/0ZOh79InJTt2EtzqFl6DRiQ2dgr61g9b03QXWj6D84ds1zEVLuHqawAw5PXmpk/6Gq+UMlLx0n/6EcGkVeampyqHs/BxaNofXcQ9i49RFy058h9fRXsfjy9wEpkXzkEoRlQeRzyN74AEaqG1Q3ap6LUFrfdnIKLtTmQHXNrzYHqrSeJpKXdsheu3e8qclhX7qytp57GEaqGzy7BvNEH8zuPqxeewsgBCe+/C3w/CYyb70CyZ265qGEKHLYOyr8cYfKf6hu/saVl5rYf6gKDSsv1ek/KHLYFYQgfv8FOOtrxR3hHrmE9Q/fgZNdA4vG0PUb3wTfWMfatbfqm4ZSJSvVhkb1H8qMFCJ5KVz+Q7mxmkde2hZ9HYr/oMihGmjRGHh+EyvvvAaqG+h49ItYeuVFAECkZwDJR5/Byju/gMhXnf21DZQQsftZChVweP5D1fM3prwUMv9hX+ZvHHmp4me9k7xT9Vzlzq3Df2jqbKV92UMaAFrSZ2B29WDj1sewM0toSZ8BYRo2bn4EAGi78Aii/Wls3L5R8xyqt9JBoJnlpVD7D9XN3yTyUmP7DxWvb946h1t//u8sAPUZAR4IQefTX0PHY1/E4ivfBwB0PvksMr98GcIqRl+pZ34TzIzWPIXqylo3mlVeCpP/UG6s4yQvNaz/UBY7y0tNHTkA+ygtmV09aDl5H7REO9Y/fBcsFkfrhc9j+fWfAQCoYaJl+GzN4xNKFTvUj9DIS6HyHyrOX9WxGq8/Qnmpkf2HWuavTV5qas8B2EdycNZXAQCdj/8GVt97A3xzA60jD8HOLBbrHzKLdY1PCAnrZxwCqM2B6pq/ceWlcjfzvchLR+g/1DL/tkEOUl5qenLYF9+B5zZx96//A5Ze/QFoJIr2i09i6bUfAgA6v/A1LL70fSz8+J/A6zCkFTnsGw5KXmpg/6EMwi0vlfNVmsJ/CB7Y0/WHKy85+X/4v6pOkAnrjWtfIgdhWwCAteu/wvLrP0X87HmIQh6bd27CSKaQeHAM1tI8Mq//tOY5CCFsP9aqAGCnm0O4/YdyCJO8dJz8h3LjNa68VEoQe3rKPdbkoLe2Q4u3ghomVt97A5t3bqLz6a9h+Rc/gXRstI09CrOrB/m5qXqmoar53r4iNP7D/lx/pPJShbVW8Yy+4/X3Jq7v+u04xvJSNf5D1WY0cMzJAQCi/Wl0PvlsMWPppe+BmREk7h9F5pcvg1CK1BefAzUjdc2hyOEw0MzprWVQn7wUJv/BHXC3c5tWXtpP/6FqvwEILznsW61DZCCNwuJsMUo40YfFV15E64VHkJu+g8LCLIxkCr3f+v265lDksO84KP9h+yCNLC+F238oN8ZxkpeOwn84FuRQc+TAcxtYn3jH/8uN9g8hd+cmAKDri9+AtTCD/NRnxfbdrxSb8dVbyKYK4Q4Ex0leCpP/UMP1TSQvNbL/cEwih5rJoTA7hcVXXsTiS98DUOzQSo0I7MwSqGHixFe+jeU3X4KR6obZ3Y/Va7+se7GqEO4wESp5KUz+Q4Xrm1VeCrX/UEleUuSwE+y1DABg/eP3sf7RewCA9otPYOPXHwMAjM5utAwOY33iHbcZ37t+LUStULLSgaFR5aUw+Q/lxmsaeSlwauV/q/D4D+UO7OXf6liQQ82eg9baDgCguoGlV39Y3OQnfQaFuSm/ZUbb+GNY++BtQAh0PPpFLLrN+GqFkpUOFIcnLzWy/1DV/KGSl6q9aVaYP1T+QzkchLykspV2gpnqAQCcePbbiJ85h9X33gBQ3Ft65Z3XARRbZrSeu4jMW6+iJX0GVNdhLc3XvFgVORwF1OZAdc2vNgeqtJ4wy0vHInKomRy0RJubmirR+fRXoSdTAIrGtJ1ZBN9YBwAkzj2E3N1bsFeW0fnEs5C89j17lOdw4Kj89HjkN82tlzetvNTE/kNVaFh5KXCmIofdEO1PozA/DUIp2h963D/efvFJZN56FQBAKEP7557C2gdvgcXiME/01jyfkpUOBY3qP5QZKUTyUhP5D7uMWWm8xpGXtkVfe45qjgU51FXnEL9/FPlZt+o58FRvnuiFsPKw3WZ78dMjsJYX/DYbtULJSoeGw/Mfqp6/MeWlkPkP+zJ/48hLFT/rHaKy6ucqd647/7Egh7oih5bBU3A21svGue0PP4Wl137kvxYbvh8bn0zUMx2oihyOGM0sL4Xaf6hu/iaRlxrAf1DksCsIQWzoNDZufbztJaOjCywaw8qvXgMAtKRPY23iV3VOpyKHQ0Szykth8h/KjXWc5KVG9R8UOVSDxMg4Vt59veSYvbIEYeWRfOQSVq/9Ermp29AS7YCUsBbnap5LkcOhIzTyUqj8h4rzV3WsxuuPUF5qZP+hlvmPCTnU3VtJS7Qj0t2HzV/f2x96/sf/hDt/9e/hrK+i84kvYeHH3wXfzCI6MIzc9J2a51Lk0ChQmwPVNX/jykvlbuZ7kZeO0H+oZf5tg1RLWqrOoVq0P/wkMm//HKKQR2F+GtbiHCR3MP+jf0R04BTMnn7M/vPfIDd1G4W5yZrnUZ7DkeCg5KUG9h/KINzyUjlfpSn8h+CBPV1fn7yk9nOoFiwaQ8vQfZh78e/8FFYA4JtZzP/oH5F6+qsQhTyspXnkZ2onB0JI1bsvKewrKt8cwu0/lEOY5KXj5D+UG++o5KVjISvt234ObeOPwdlYR+7urXvHxh4Fz29g87Ob6Pnm74G1xCEdu+Y5KKW1V9Ap1IvQ+A/7c/2RyksV1lrFM/qO19+buL7rt+OYyUvHQlbat/0cqG7gxJd/C4QWd/NksQQ6Hn0G3V/9Hay9/ya0eAK93/p9dD71bM1zEEpV5NBwaOb01jKoT14Kk//gDrjbuU0rL+30WTe/rHTrz/+dA6C+yrQAzK4edD79FRBNQ/v4YwAh0Ns6kBgZx9oHb0NvSyJ+drTm8amSlY4aB+U/bB+kkeWlcPsP5cY4TvLSfvgPx0JWAvZRWgKAxANjGPz9/wqJkYfuHRt5CBs3P/S7tdYK1T6jIXCc5KUw+Q81XN9E8tLh+g+KHGoFi7SU3MgJZUiMXMT6xLt1jatSWRsZoZKXwuQ/VLi+WeWlUPgPx4Yc9s132Amx+x5A9uZHdY2hyKFh0KjyUpj8h3LjNY28FDi18r9VePyHrQeOhSENHEDkUA5UN2B0pJCfraPOgZAwf87NhsOTlxrZf6hq/lDJS9XeNCvMHyr/oRyqWX/zG9IuDoUcAKDl5H3IfvR+zdcTVQUXAqjNgeqaX20OVGk9jSQvHRtZ6UDJQQoByR0AgN7RhezND/3f9wpCCNvPtSnUjcpPj0d+09x6edPKS03sP1SFw5aXeOEf/+893cDCTA4H6jkQSrH8i58AAPS2DkjHrqf5Xpg/52ZFo/oPZUYKkbzURP7DLmNWGq9x5KXS6GvPKZdhvmkduKyUn53E2sQ7/tahhYWZmsdSylJD4vD8h6rnb0x5KWT+w77MH2J5qdzxPZnRAKDt9YIGwoGTQ+dTX8Hsd/8aK4YJACjM104OKmMpTJAEkmx9BiaoSm2ocK4EKXsrrvJ6CUlI8KFSYqdbU7m1use2XUggtz3vH/J7lVu5p2RMKYEd/vvs8F4roPQjKHOuJF6wuPPH7F5/QJ/fvbn9n+p5r3syowEVOeyISM8Ako9cAs8XP1dnfbXmsVQhXMOiWeWlMPkP5cY6TvLSYfgPe44cwnzHOpQ6h7axR9GSPgMA0Frbax5HRQ4NjdDIS6HyHyrOX9WxGq8/Qnmpkf0H5TkcDLp+45uI3z+KSM9AzWNQRQ4hhNocqK75m7d6Okz+gzfInmUl5TlUAaob6HrmG3WNoWSlhkdl7Xa78Ezk9tCi0vUN7D+UQZn3un1+kNI7+Q4LKP9ed9DJy3gi2/2HXT7rkjF2fv8h8R+CB3ZYxU7vVUUOjQolK4UClZ8ew+0/lEOY5KXj5D+UG28/5KVjRQ6H4jnsF5SsFBqExn/Yn+uPVF6qsNZqH7Qb2H+o5urDlZeOFTmoyEHhEKGqp8ucUi56KD9u8/oP5ec/8L+VPWcvHZ9spVOd7TRMt1vlOYQKe7ppNq28pNJbt44VJnlp6/HjU+fwyMmejq88kD7qZVQNFTmEDsdJXgqT/1DD9U0kL9XuP+xZVgpttpKQcuU/HTuLD+eWcSezdtTL2RWqfUazIFTV0ztlr5RBmUwhVT19QNlLO2dl7bbWMnNvO/dUd8q4ODxoXBjqNzPZzQd2Xup2hJYcJLCsUYo/fWIML3z/NVicH/WSdoSKHEKJyjeS+tJbtw+yt5TPvRBEOeztphnu9NZyY+z8/ne965cliMr/VoeQ3toea6EXhweNsfSAOTrYZ44M9kZaoxH/ifS7b10zq5zPR2jJAcAyAPS2xvCfXXwA/+8vrx/1enaEIofQYsenx62o5omu9FjNBFFm7rIEsccb0c5P6jvPX5Yg6rsR1nsjrfRZ73b9ztEXKvwN7JFga/usdcbIucFeY3x40Bgb6jPPD/aZ/Z1Jfad35gix5+zO0JIDI2TR+/mZ04P4YGYRb0/W3FL7wKEM6WZDZXmpLoIojwO+aW5FqOWlaj/rqqOyHc4tHbGm63eXlwZTSf3i8KA+nh4wzw/2mWf7ug1DY3t62uRC7Dm7M7Tk0BmLLAV//88fPY9bS6vI5PbsuxwKVJ1DqFH56TE8/kN18zeuvNTE/sM9JKIR+lB6QB9PDxijQ/3muYFeIxlvqXuzMIeL7F6vCS05/Ld/8C+sn/z0Z2BuQmvM0PFfPn4B/8tPfwm5SxL0UYBQygGoHeHCi0b1H8qMFCJ5qYn8h0prqjQ1oxQP9vcY40XT2Dg/2Gekuzr0g5CguThG5AAAFucySjX/k3ywuwO/+eAw/nni1lEuqywoIYocwo/D8x+qnr8x5aWm8h+qnH83eak32aZdHB7Ux4YHjPODfcaD/T1GxNjRKtg32Jyv7/WaUJODzYWI6qU33G+PnsHE7BJ+vVz73gsHAUJp44UzCvuEZpaXQu0/VDf/AchLLaZBxoYGjPHhAX30ZL9x/mSf0dUaP7KHQ4eL40YOXGDL0zijBH/6xBj+zfd/joLTOOmtlJR/ulQIHZpVXgqT/1BurCOTlyghONN7Qn9o+KQ+lu43Rk/266dOpHTaQD0cbM5X9npNqMnBEcIBoG89fiLRgj94eAR/+ca1I1hVeajIoakQGnkpVP5DxfkbS17qak2wi6cG9bGhAX10qF8fGeg1YqbROExQBrbD91wpHHJykE6l15461Y9rM4t4807t+z7vJ4gqdDgGOFR5KTT+Q9XzN6C8ZOoauTDUr42nB40LQ/36+ZN9em+yLXTeYd6296yzh5wchLXT63/4yDncXFrB0saee07tOxQ5NB0OSl7arvU3jP9QBk0kLxFCcOpESntoeFC7kO7XR0/262d6T+gaC3+N0spG7pjJSlzYO70e1TX8yeMX8D/9+E2II05vJYSE/y9MYSsqy0vh9h/KIUzyUlUEkYy3sIeGB9lYelC/cLIYFSSikaZ8iLs1t3i8yIFLuWuP8jNdSXzz3H34xw8+PYwlVQQhJHShqEJVCI3/sD/XH6m8VGGt20hrGwyNYWSgTx8fHtDG0gP66Ml+baAzeSz+TzpCyPfvTB2vOgcuRFXl0N86fx+uzy7i072T575BRQ7HDc2c3loG9clL++4/nEx1sIunBrWx9IA2OtSv39/Xw/bacqJZYNmOLPzT/7PnzX5CTg6yKjOBEoI/eWIM/+Z7P0fOruhhHzgopRBCHNn8CgeGg/Iftg8SLnnpUPyH1miEjA8PauPpQe1CekA7f7JPS8Za1MOYi4Lj1HTTCTc5yOrIAQBSsSj+1efP4c9fe+8gl7QjlCfd1DhO8lJ5gtguL5VFPf6DRikeGOjRxocH2djQgDY61K+lu1JM/deqDOs4koMQck+dBh8d6sW1mUX8/NdTB7WkHaHI4TgiVPJSw/kPfcl29tCpQTaWHmAXhga0Bwd6WEQ/nJYTzQLLqW2zm1CTA5d7IwcA+IOHH8QnCxnMZ/fc3rxuqLbdTY9GlZdC4T/ETJNcSA+w8fQgGxse0EZP9rNUa1z9p6kTlsNr0tJDTQ5Syj078BFdw588MYb/8YevH3p6q2rbfSxwePJSuPyHElBCcKavm10sppKyC0P97FRPF1P/R/YfDj+G5CBqIAcAONXZhm+PnsHfvX9jv5e0I5SsdJxxvDcHOtGWoA8Nn6Rj6QE2lh5gI4N9rNFbTjQLLIfvWCxcCaEmBynlnvuFePjGyDCuzy7io/nl/VzSjlCy0rFBhRtpqPyH6uYvcyxi6Bg92c/Ghz2voJ/2JNvUH/8RweF8x2LhSmhIcqim1YSUEqIOciCE4I8fv4D/7ns/x4ZV02dX05wKxwaN6j+UGal2eYkQglPdKfbQ8Ekylh5kF9L97GxfN2XqQahhYHO+5xoHoIHIYSshVLObG6W0rqq2ZEsEf/jIefwfr75TzzBVQ+mpxw5Nl97aEY/R8eFB4kYF9PzJftqsLSeaBQ7fuQddJRwpORBCiJRSut8rnnflyhVy+fJlWeb3unf0+dxgN75w3wBevjlZ71C7QslKCkWEQ14ydA3nBnvp2PAgHU8PsgvpATrQmVREEDI4XIQncvCiBJcQSojhypUrZf/4th6/cuUKiabPZvZjPb//8IO4sZDB7NqeM2P3BCUrHUuERl462dVBL546ScbSg/RCeoA+MNBDdXYs2g81NWzOq2oztBWHTg5biAHA9hv/5cuX8bu/+7vk+eef33GsGU53dJMdx4Gm7f4WDcbwp0+M4X/4wetwDrC9hSKHY4uGk5faWqJkbHiQjKcH6Vh6gF5ID5D2WIv6A21CcCFq2rPgMMmhhBQ8aejKlStkZGSk5I/y6tWrCBJDV1cXWVhYkBMTE+TSpUvwfu65/9yOkcPGxgY0TUMsFtt1cUPJVvzO2Fn8zTsf1fDWqgNVspJCCQ5HXtIYw4MDPXQ8PUjGhgfJhfQATXd1qi1Gjgkczmuq+D0scvD/Cr0o4fLlywBQQgzezT+IGzdukEwmAwCkt7cXN27cAADy3HPPYWZufkdDmjEGxhiklFU9tX/1gTQ+mFnA9dml6t/ZHqD+Mx5rHJS85B6/N0h/RzsdHz6JseEBMj48SEcG+oipN0zuicIhw2nQyGGbTwAAIyMjxI0YZFdXFwGKJNDb24uFhQWSyWRkMlk0vgqFAunp6ZEAEI/HSTablQAQjUYRNY1d33QqlcLMzAwSiURVi/3OY8X01myhJoN/5/EVORx3VJaXaiSIWMTEWHqAjKUHyfjwSTI2PEA7E/H9X7lCaOFwUZOZepDkQIBSP8GLErq6usilS5dw48YNmslkkEwmSTKZxOzsLInH44jH4+TMmTN49dVXyejoKACQ27dv+2NubGzIN998E+l0urCr2gogkUjAtm3our7rotujJr7z2Cj+7KW3a3nPO0JlKymgDv+BUYozfSfo+PBJjA8PYiw9SE71dBGVIq2wExwhGkpWIlvkI1y9epV4slEikSAAkEwmiUcIc3NzpKenh2SzxY4YH330EUmlUvB+7+vrI3NzcwCAVColM5kMyWazMtISkzqjO/7vuPXpp+ju7a2KHABgrK8LXzo7hJ98cqeqeotqQQmphssUjiW2+w/d7a1kfHgQ48ODZCw9iNGhfhI1jKNbokIowbmoqc3QfpODHy1cv36dPP/887h69SomJiZIb2+vLxtdu3aNnD59GhsbGz4hMMZoPp9HJFIsqJmfnyepVAoAsL6+ToB7hu7q6qq3cY60ORc6o2Xz7bwb+8T168hkMhg5fx7RaLSqN/Ivxu/Hx/PLmFrNloxVDwilHA1UeKhwZNgWPUQNHaNDgxgrFphhbHiA9LS3HdHyFJoJjhDrtVy3XzeqEglpZGSEjIyMYGJiggBAb28vmZmZISMjI4jH4ySRSJC7d+/Svr4+ZDIZEolESFtbG1lfXyeZTIYkk0kkEgkIIQhQWj3d1taG1dVi7ZsQQjpCcAA7JmMTQjAzNYWR8+erfkM6o/iTxy/g3/7wddhcgBBSN0FQQtQ2cAqghOBUTxcZHx6UY8MnyUPDgzjT1w1Gd46AFRRqgcP5kUUO22oUgtECABQKBTo0NES6urpw48YNGo1GSTKZJPPz86S3t5dks1kihCCEEJJMJkkulyOJRALedz2wucfm5qYvD+VyORkxo7tuZME0DYVcDj968UV88ctfRjxenWE30J7A82Nn8ddvfwgQUjdBEEoPt0e4QkOgMxHHWFEewvjwSVxIDyAeMQElMSocAmzOa+pBVxc5BNteuHULuHLlCgWK0UIymSTxeJzMzc2RWCxGstksSSaT1DRNous62djYoJlMhui6TnK5HNV1ncTjcTiOQ/P5PDRNIysrK6SlpQUAYFkWAQDDMCQAFAoFcCEq9ionhEAIAV3XsbmxgWvXruHc6ChM06zaf/jy2SF8MLOI92cW/TGB2mQm13NQaGKYuoaRwT74pvHwIAY6k0e9LIVjjEMlh61Vzp6cdPXqVT9aGBwcpHNzcwQASaVSxDAMks/nKQAqhCDZbJa2tbURx3GopmkEAGWMEdu2iRCCMsZIPp8nkUgEnHPPK/CftDjn0jRNcCErtlR1fQkYhgHHtkEZwwfvv4/unp6qyQEA/ovHRnH5e69hJZcv7l9V/Az2TBAqW6n5MNydwlh60CeCBwd6oamWEwoNhILl1NSDriZy2EoKIyMjZGJigmQyGTIzM0M6OjpINBollmVRxhgxTZOurq5SVx6iUkqSz+eZpmnENE3qOA7lnFPbtimllBiGQWzbJoZhkEKhQEzTxNraWkkIruu6BAAuxa7kYJombNsGpRSf3b6N9vZ2rK2tVW1OJ0wDf/ToefzZS29Deu+/BplJlaSGG+2xFowVU0gxPjyIC+kBtMdajnpZCgo7ouAcHjn45vPWuoWFhQUyODhIstks9SQky7JooVAgtm1TTdNoPB6n2WyW6bpOOeeUMUYsy2JCCCqEoKZpEiEEdW+khBDK2nj2AAAgAElEQVRCHMchpmkCABzH8TKXpBBCil0iB845TNOE4zhgjEEIgTdffx3p4eGqyQEAzvem8Oz9afzg49ulH8YeCIIQokKHkEDXGB4c6C2JCtInUke9LAWFPSObL9S0tUHV5LBVSvKM566uLpJIJMitW7eIW8FMo9EoicfjlDFGC4UCtW2b6rrO8vk8lVJSxhhDUUZi+Xye6rpOLctisViMCCGo4ziMUkoopSQSiRDHcYjjOHCPAQAcx5FSSik1VCxl9shB03UIIXxyWM5kkM/nMTo2VrU5DQC/M3YGH80v4U5m/R4h7CGCUOTQuBhIJTGePomx4UE8dGoQI4N9MKpo2qig0Oi4ObdwcORQzmO4fPmyn5GUTCbJxsYGBUDy+TxdX1+njDGSyWRYNBql+XyeEUKYaZqUc84AMCEENQyDWZbFAFBKqVYoFChjjLrwvAdIKWmwu6oQgkgpJWNMWqgcOTDGwDmHoevgnINSCse2oWsa3n7rLQwNDyMSiVTVuRUANErxx4+P4b9/8ReweDFJai8SEyFEidENgEQ0ggvpART7Dw1iPD2IjsTuzRkVFMIGISTeuz25/4Z0JePZ640UzEiCSwxtbW20UCjQbDbLCCE0n88zXdcZAMY5Z4QQ5pGCZVksQBCMEMIIIVRKyRzHobquE8dxCGOMwJWzHKckOUlCyoqRg3fD9iIGRilsKYtehJT47NYttLa27il66G2N4V9efAB/9cvrJXN4BBH8vHZak8LhgFGK+/t7fGlofHgQp7q7VJ8rhWOBvG1LAAezn8PWaOHq1aukq6urhBiy2SzN5/PUtm1aKBQoAOY4DgXAtOJjOfO+KKWaZVka51zTdZ05jsOklP45juMwxhiVUlLOue89cM4JIYRomibddXl32F03gNbcyGFrh9YbH3+ME93d0AYHEYlEqv7QLt03gA9mFvH23VmQYNRQxQ3Hk7oUDga9yTaXBIpRwfmT/Yga1WemKSg0EyzHEdZ3/6qmp9GK5LBbZs3DDz+M+fl532Nw91ygAFg0GqW5XM6/4TPGNNu2NUqpBkDTdV2jlGqEEE0IoUkpNU3TPLlJQ9GPoFJK6jgOJS4AQLib8VBKpUsQO7ZPFUJA1zRIL2IovjcQAJxzvPryy/jd3/u96j8xF3/4yDn8emkVmVze+7wAoIR8ykUI6ol1/9BiGhgdGiiJCk60tR71shQUGgaWw2vuylCWHIJyUlBK8gzol156iXZ0dJDR0VHCOSe5XI5OT08zVjQMGABmWZZGCGGMMc2dRwegOY6je8RAKdU453rgHP9627YZpZQSQrzooYSw3JuwoEDFIjgPXiprNBotNrURAlJKOJxjI5tFR0cHFhcXq9oUyEPM0PGdx0fxv/3sLQhRnpjLSUiKHGoDJQSne0/4UcH48CBO954AU7UjCgoVUXCcmmWKipFD8KYWbInx3HPPkZGREXR1dRHTNIllWWR1dZXG43FKCKFeBBCNRplt25oQQgegaZqmCyF0QojuEoIuhNAopTohRJNS6m7UwFxQFCOPksjBA+cchBABKnckByklkp2dYJSCMQYQAs45pJTgjgMJ4MXvfQ9n778f0Wh0T7u1PXCiA197II3/OHEL95KXdpaYVCFcdehqTZS0nBgd6kes2HJCQUGhStgO3/XhuRK2kcPWlhhAsfIZKLbEuHXrlp+Z1NbWRvP5PI3FYlRKSQHQQqFADcPQKKWapmk6pVQTQui2bRuMMZ0QYriEoEspdSmlTxiEEE9qYkIIpmkaRbFojuLeNqOE3GtDISmwIzMSQmAYBp5+5hl8+sknWMpkIKQEZHF/FSkEOOd451e/wheeeWZP0QMA/Nb5M/hwdhm3M2s+oe4kMane+9sR0XWcH+r3M4fGhgfR19F+1MtSUAg9rP0kh60I1jOcOnUKr732Gunp6SH5fJ6apkkLhQJtbW2lm5ubrJhdWvQXotGoZtu27jiOrmmaDsAghBgADCGEIaXUARgAdCGEwRjTpJQ6Y4wJITT3O0UxcvAIAoB/05WyeHBHcjAMA2tra3jy6acxNj6O//3P/gwEgCfECc6haRosy8Kvb97E2QcegLGHnvmMEvzxExdw5fuvocCLclW5bUm9iOK4y0qEEJzqTgUqjU/igYEeJQ8pKBwAbL5P5BCMGjx4UcONGzdIMpkkiUTCa6tNvMyktbU1VigUmKZpmhBC03Vdy+VyuqZpGudcl1IalFJTCGEIIUxKqQ7ABGAwxnTOueFFEZxzjVLK3OhBY4wRKSV110YCa5UAJCG7b8ZuGAbW19eRSCTwjW9+E3fv3Cm5iLhS009/9CPcd+bMnj/EE/EW/MHnRvCXb1zzx/O+b5WYjpuslIzHvF3L3JYTg2htqT4zTEFBoXbYDq95v+OKkUMgddWPGubn5wkA4vZLIkIIksvlaCwWowCoruusUChojuNohBDd9RsMIYSJYuRgappm2rZtMsYMxpiBYvRgCCEMQohnTPvmtBCCoSgpldxVhRCglArI3cmBMYZcLod4PI7RCxfAbRtvvfWW37WVUgrHcbC6uoqlhQV0pFLwOsFWi8fTffhgZhFv3pmFhCzxIIB7ElMzRw6GpuHBwV6fDB46dRKDqY6jXpaCwrGFw/eBHCp5DV1dXcSLGjY2NohlWcGogXr1DChmGHk3dU3XdY1z7t/8CSEmIcR0HCdCKTUppQbn3ARgSClNSqlOKdW91FYAmlcQF8xY8jwHV74RdHduAABEo1EsLy+js7MTPX19xZu1m9oqOPeL6/72b/4Gf/yv/3VNEtAffG4EN5dWsJjNVSx2aybP4WRXp2sYF32CkYE+6JoqAldQaBQ4Qux/5BCEV9OAQNSQzWZpoVBguq6zzc1NZhiGVigUtJaWFs2yLE0I4ZnNXmRgSikjhBATQMSNJkwppYl73oPuehEaAOYSA/MMaTeTiBBCpHvzlZTsbcOUQqGAvv5+PPXkk3jnnXf8zq22Xayl28zloDGGjY2NPVVOA0BU1/Cdxy7gf/3pL+GI0vRijyxoSNs5t7VEMZoewENucdlYegDJuGo5oaDQyLA5r6k6GgiQQ6U23JcuXYLbVA8DAwOYnJwkLS0ttFAokGg0Stx220xKyQzDYI7jaEII3fUV/KgBRY/BpJRGOOcRSqlJCDFd0jAB6FJKw12TJqXUKKXM9Rso3CppSqm3VkkplbIoO1UF0zSxvr4O0zTxxNNP49oHH4AS4mcsAUV/Ire5ieXlZZjp9J72fQCA06l2fGPkFL57/WYxGwqlacF+H5AGbqOhMYYH+nv8VNKx4UEMn0g1tSSmoNCMcDgv1HqtBpQ3oj0kEgmSy+XIRx99RFOpFKXFHFUSjUbpysoKSyQSNJ/PM8dx/OpnN+tIF0LonrQkpTQ9aYlSGnEJwXSNatOVlAxCiA6XHIQQvqQkpaQ0sMeuVzDtnl/9G9Y035z+yle+gr+/ehVAsWcTZQyapqFQKODnr7yC3v7+PZMDAHxj5BQmZpfw6WIGkKWZSqwBI4f+jnaMBXYuO3eyD5Ea3reCgkJjweEiV+u1Wrk2Gd4+DV5dQzweRywWI5FIhAAgCwsL1HEcZpomzeVyzO2eyjjnPjE4jmO4WUo6AINSasCNHtyvCICIGy2YQggDxSpq3V1XsCcTCWYrBWodpNvltfo3rGnY3NxEIpHAAyMjaGtrA+CSA6W+Ob22uooPP/gAI6Oje9r3ASj6Ct95bBT/9ge/wIZl+5550JT2fj7s6CEWMXFhaKAkKuhqTRzqGhQUFA4HjqiDHCq98Pzzz+Mv/uIvAADRaJT09fVBCOFnKLlN8ahhGF4XVUYp1dyiNw2AZzCbAExPWvJ8BymlJyd5X4ZXGOdlLblykict+VXSgZurrOXm2tLSgqWlJXR2duKrv/mb+PTGjeJOce5TvWdOZ7NZ2LaNSCSyZ0mlMxbF7z88gv/zF+8XDxAJgGwrnN6pD1O9YJTijNdy4tRJjKcHcV/viaYyxRUUFCrD4Xyz1ms1YLvfAAA/+9nPCADE43GysLBA8vk8SaVSRAhBTNMkq6urxCMGuBlKbktuTQih67quCSEMTy6C6z/AlZg8mQnFCMI77ldOwzWk4RbBASBuxOCvV/qoqiFqCaSUsG0bD4yMwDRNvP3OOzB0HXCPe/j5yy/jC1/84p4rpwHg8yd7cH12Ea/9erp4gACUsVJC2Mcb9Ym21mL2kEsEo0P9iJrVF/QpKCg0FzgX9ZFDEJcvX8aVK1fIpUuX8PDDD+PatWt++mo2myW5XI7quk7djXuo4zjMMAyfIIQQGiFEcxxHd/0AnVLqmc0GpdSraQhGDF5Kqy8rIdDmG0VioFsiB3kvehDY6yZrkUgEKysr6OrqQqqrC4xSaIyBBzKXAOC9d9/F6NgYDMOoyX/4lxcfwKeLK5hfL/4bbXtqr3E/6qih43ywI2l6ED3Jtj2vT0FBoXnhCLFR67XbyMGriAaAW7duEQDwJCW3PTdljFHOubdfA7VtmzHGmKZpzHEcjTHmN9zzSAH3it28qGHrcSMQNfjk4NU6ACBCiK0V0oAbQAB7S2kFiv7DxsYGEq2t+PZv/zb++bvfhcQ9WckzkN/71a/wxWefrYkcIpqG7zw2iv/5x2+CCwkCIggB9ZZbkslUgSAIIbivpytABCdxtr9btZxQUFDYEQ4X2Vqv9ckhKCkBxSyla9eukZ6eHpLJZEgqlSJtbW2wLIsUCgUCV+5xN+fxdnrT4O7J4NU4uLJSsObBiygMQoghpfSJwYs0pJQaKW6p6UlL3nxAgAQ8U1pIiVpygHRdRzabRSwWw7nRUXz/n/8ZDuclkQNjDJ/evImT6TROnz27Z3MaANIdbfjW+dP4h2ufgDHGIUGB7U36PImpMxG/Zxi7jejiqiOpgoLCHuGIfSAH4F6TvUuXLgEAlpeXiWmaWyUlYpom9Rrvobj/s18lTdz224wxJqXU3G6rvpcQkJg8CUl3NwHSvZYbWzKVqPtF4G7pEKiSds0H1BQ5AEVzenl5GR0dHXjiqafws5/9rGQrUsoYCIA3XnsN/YODNZEDAHztgTQm5pbw/p0ZSQgpcgORMDQNI4N9/p7G48OD6O9M1jSHgoKCQhC2w9drvVbbKmNMTEz4W4AODQ0hlUohHo/j9u3bpK2tDRsbG5QxRhhjRAhBNU2jbv+jIDl40YOXkup9L8lGcucPkoL/mttCw+/I6qWyCiH8Thp+G40qW2iUA3Gb7jmOg8efegofffwxHMfxn+ip6wdYloU3XnutZnOaEII/evQ8/n3Bwuf6uzCWHsSF9AAeGOiB1oC1DwoKCuGHI0Tt5FDphdnZWZJIJJDJZIgQghiGQRzHoZqmEc655zn41dHeJj9wZSWvQ+supKC5ZOJ/d18LykpeppInsPu71LltNGpKZw0iGo0ik8mgq6sLv/GlL2Hi+nUwxuA4DhhjEG4b7rfefBOPPPYYTNNEcWvsvSEZjeC//vKT+rmh/rrWq6CgoFANLMdZq/XaEkfTM6MffvjhkpN0XSe6rhMAYIwRSinRdZ1qmkYNwyCMMeo4jk8M3p4MgRs+877jXtdVL6Jg3pe7RahPDF6dA0oNat+LcL807EONgFccN5RO46tf/3qx5xLnxZ3hXD9ASIlCPg/LqrmX1baeSwoKCgoHBZvz1Vqv9cnh8uXL216MxWIEAO7evUsAwHEcalkWsW2bUEpJoVCgblsL6m3rKaUskZm8Gz6llAUjB484gl8oTV/16xxwz5D2/Advb2kvm6nW9+9D13WsrxcjMOpuKWrbdpEcCAF322vYto1YLFYzQXBR85auCgoKCntCwbZrJgcNKGm255vRp0+fRjabRSQSIZFIBJqmEdM0sbm56XkN3p4Ovint/uyRAvW2+yTFzqr+Pg0IRAFeEV0gZbWEAFw5icp7G/1sM54Dr9UFT15KJpN4+POfx2effQbGGKRlgQsB5rbWME0T2Wx2TzvGeeAqclBQUDgkrG7mVmq9tmyi/Ntvv425uTmSyWSIaZpkY2OD5PN5UigU/Kghn89TL3LY+uXJQYQQSilllNLgjd73ErZUQPtE4H0Ff3eb7gXJggohqJSSyHoc6eCHQSls2wbnHO3JJLht+43yBOd+NMEYq7ndhRT7Q2QKCgoKu2FmMbNCtqDaa8uSQzKZ9IvfAKC9vR2JRAKMMRKJREApJZFIhJim6TXE8+UlD568FCQEFD0JFrzBexGDdDf1wT3z2XsjXvrq1mM+WUjs3w3XS23t6e2FbhiglBZbegsB6prUe61mDkJIFTkoKCgcPISU+GR6Zv2FF14gL7zwgt95u1qS8MnB68R648YNAgDpdNo/aXNzk+RyOWJZFuGcU9M04TgOcRzHb2vhkYRHDLgXOZREAAHfwJOSgoRA3Wu8bUGJFyHArXNwJSSfNAAQuc9SDWMMsXgcjz3xBAYGByFR3JbUy2CqCw26h4OCgkJzwbIdeb4jYV2/fp1cv36dXLlyZRtJ7HS9Tw7PP/+8nJiYIABw7tw5AEAmkyHr6+skl8uVDOJJS0W1qHg396IHzrlflwDvyT4gN7k8EowCvGOAe/MPEAANHPfej3+Od57YZ3IwDANra2sYvu8+fOnZZxExTXDOwVzZyV1ITWMrblBQUDgM5G1HfqG/QwBgyWSSAqBBkgB2JgifHII9lX784x+T27dvo7u7GwDQ2toKTdOIV/zmRQ2cc0II8QrTSiSfICnAJQC4kcAWk5kIIfzXg18eAZBiIZp3DIHXIIQ4kHbXXmO+eCKBr37968XIQdPqjhzI/tgjCgoKCjvCchwxPT2txWIxapomnZ6eJufOnSPXr18nALAbQZR4DiMjIxIAenp6/DtYR0fHtoscx/EH9fZ4CMIlAD8yQEA2Cj7xByKJbcQA+FlIwagi+B3eWPsdOQBFaalQKEAIgfMXLqArlfKzleoBUdygoKBwCLAdh7e3t7NUKsU0TaOWZbHr16/TIEHsJDGVkENXVxcBitXRsViMzM3NlZwcrHFwC+H8gb2OqQHZx/MJECANX4oKSkOuweu34/bIwJvXixKCb+YgooWtiMViWFpaAiEEX/vGN/xspnqgIgcFBYXDgOVwvrS0pK+trWmFQoEZhkFjsRgNEsSVK1cqbhFd4jm89NJLAIo1DgDQ3d2N5eVlAEA+nydbm855shJjDF704JEEvddOOhgpIBA5bHsdW6Qk4B7ZoDSigBCCcM6J4zjEtu0DSw8lhCCXy+H8hQs4OTRUt+dAD2ylCgoKCvdgOw7XdV3TdV1zHIe5HS1oT08PmZ6erj5bKdg649NPPwUAuLu9kdbWVv+CSCQCoEgMWwcLEsMW0xjAPRmIEOKRR7D99k7rlO71/jjufBBCgPODqzqORCJYXS0WGX7xS1+qew8FpshBQUHhEGBz7gDQOecsFosxx3FYa2srWV9fJ5lMhgSjB2C7tLTNc3j77bfR09Mju7q6/Fhjbe1e76Z8Pl92IYwxUErvpRQRIiml8L77E7rnbK1q3hraBLSwYnPrgJzkkcJhkANwjyBSXV341re/XddYtLbO4goKCgpVQwiJ5bXsvNelglJKW1payObmJkkmk6Snp2fXG1FJa9Guri6SyWT8faO947lczvcXIpEICCFwHGebUCWE8CQmeN9loM32Vi/Bgys3ed+DW4EGIxTpeQ9bCWJzcxOWZdXUzqIaaJqGbDaL1tZWXy6rVVZSkYOCgsJ+gwuBO/OL2bvzizfnFpd+vnD3zt9tzE/fdffaoZZlEcdxSKFQII7jENM04UpLFU3QEnJ46aWX8Nxzz2F+fh4AkM/npScjAUAul0M0GoVlWaBu5k6wdbUQwr95ejdu98k/GAn45wcJwSMDbJGQAse3RRoeSTiOg8nJSZw6dWpPH+heEI/Hsbi4iK6urrrGUVt7Kigo1AsuBO7Mzm/cnV+4PTs//8bCndvfy6+uzFFKLc65xRizAIAxJgkpzZGMxWLScRz09fXJTCZTcY4Scrh06RLefvttFAoF9PT0AAAsyyoZOJ/PgzEGzjnR3Lx/Tz7SNE3K4p1ebkkv9ceQ96rZJO5t2CPLkIF/SZAgPFLYGkGsrq5ibm7Or804KNQbobAaIw4FBYXjC4dz3Jmd35icm7s7Mzv37vxnv/5pYSO7RCm1hBAWpdSSUgpCCPe+pJTC+yKECM656OjoEIuLi3JjY0N2dnbuOKdPDhMTExIA6e3txZe+9CV5/fp1/6RoNCodx4FhGBKApJRKSqmwLEsyxqQQwruBSwDBiEFSSqW7aY7HAMV9n90oI0AEJRGDhzJksQ0eUdy9exdtbW0IRjv7Ca9ra3d3d+2yElORg4KCws6wHQd3ZuY2J2fnpmdnZz6Yu/PZa3Y+tyqltAghlhDCYoz5xOB9t23bZozZQghH13VH13WHc84dxxGO44i1tTWZSCSkYRjy+vXr8ty5c/Ly5cvyhRde8O/fHnxyGBkZIRMTEzh79qy8fv06mZycRCqV8k+MRCLSKwCzLAtuG28ppZSutyAD6ac+CXiRBALbegZf934OHt+6yCACcwBASRTBOccnn3yC0dHR+v91KsBrrVErlKykoKCwFZZt487M7ObUzNzc9Mz0h3N3br/FbTvrEYEbGViMsYIQwgLg/W4JISxCiE0ptRzHsR3HsQHYAGxKqeM4jqNpGs9ms0IIITKZjDx37pwvKVV60A2mmpKrV6+STCZDk8kk6erqonfv3mXuJj2Mc65HIhFd13Xdtm1TCBGRUkYARCmlLUKIFkppDEAcQIwQEgMQl1LGAMQJIS1SyhZCSIsQIkoIiRBCDCGESQgxZHHbUH8PapR2bfXTYr12GW77Dti2Ddu24TgObNuGZVk4deoU+vsPbivO9fV1MMbQ0tKy52ullBgcHDyAVSkoKIQFBcuWd2Zm8pPTM3Mz09OfzE/eeZ87zgYhxJJSWlLKgvczIcQCUABge0ThEQGl1AJgE0JszrlDCLEdx3EMw3Acx3EYY3xtbY1TSjljjKfTaQFABKOGoOoThL/Zz+XLlzExMUEuXbokFxYWyO3bt5FKpaSX4x+JRKRpmnJzc9P3F4QQnkQki733pKSUejqXHx0EvhA8HowWSOW9oCX8Wji5PRe36HeURBS3bt1CR0cHthbt7Rfi8TiWlpZqIgdCSLGJn7tPhIKCQvMjXyjIz6Zn8lMzMwszk1Ofzk9PTkghNl0isAghQRIokYq87wBsxpjtOI7NObe934UQjhDCMQzD4Zw70WiUb25ucsYYJ4TwWCwmLMsSlYihEjQAuHz5sgRARkZG5I0bN2gymfRPKBQKsqOjQ9q2jc3NTf+4EMLzGiQhRBBCBADBOZduLYP3JQCUkEVxkzgRdNGD8lKJDIV7WUp+RpOU0vMwiOddeIV1XouLDz/8EBcvXqz7H7UcCCElktte4Xow+7giBQWFRkIun5d3pmfyk9MzCzPTUzfnJ+9+LKXMByIDi1LqE4GU0gJQQggoSkMWpdQmhNhCCAfFKMERQjiapjmWZTmUUm6appPL5TjnXDDGuJRS5HI5kUqlBGNMoHgf9n0GoESaL/tUXhI5AMDZs2dlIpHAtWvX/JNWVlYgpURbW5sEIPP5vH8jZ4z50YN7Y/cW4rnk3o1eSCl9onB/l5RSgYDP4JKNFyWAcw43KnFfJr5JTSmVhBDikYKbRQVKKVZWVjA5OYmBgYH9/DffF9i2DdM0j3oZCgoK+4TNXE5+NjWdm5qZWZy6O3lrcXb6UymETwYeEcD1CjwvIfjdPdcWQviegSsR+dEBpZRLKR0vKjBNk4tiIRk3TVNwzoXjOKKtrU06jiMmJydlMpmUN27ckJ7XEIwYdvJ3NU9vunLlChkZGSl5cXp6GslkUm5sbCASicjNzU2Ypil1XZeUUlkoFKRt28EMJilclvDkJQQihyBxuOsqyWDC9jYZ2BI9SJcciJfptLXoLkgUn3zyCTo7Ow9MXqoV9TbvU1BQOFpkNzflnanp3OT09OL05OTN5bnZm95NPiATFVypaNuX+7odiBA8UnB0XffJQNM07jiOQynluq5z10MQ7e3tXEopOOdCSimWlpZkKpUS6+vrMp1Oi+npadnX1ydnZ2dlJpPBM888IwHghRdekC+88AKAnYkBALTg688//7y8cuUK6e3tRaFQkN7TbSwWk5qmScdxJOdcRiIRadu2NE3TJwAPUkqpaRoH4ElNMkgS2CI3eREEsM2f8CIO4hKL33QvEDl475F4BOHt7+xlL01MTODhhx+u6w9hv1H3bnIKCgqHivXsRjEymJ5emJmavLU4O/Nr7+Yf9A28KCH4OyHE4pzbrnxkuwVqtpTSoZTaQgiHc+5omuYAcKSUjm3bnFLKCSHevZRzzoVlWYIQIgqFgnQcR8RiMQlA3HfffXJ6elqm02kJAJlMRvb19Ulv47YXXnhhW63ZbvAiB//ApUuXcOPGDZw+fRrZbNZ/YX19HbquS9M0pec3oCgnCVfeEQCEG/b4hOD+zkWxKs73JtzXfSkqGFnIYrvvndJZJaXUL4ijlELTNEgp/e8eWWQymYaTlw66F5SCgkJ9WF1fl3empjenpmcWpifv3lpZXLi99YYP1zze+lWOEIK+Aefc1jTNJwFN0xxd13mhUOCUUu44DgcgbNsWnHORSCQE51xks1mZSqVEJpORjDG5sLAgAci+vj55/fp1CQB9fX0SAP72b/+2xGyulhCC8HdX8zrzjYyMbEtnTaVSLJvNapqmaZubm4ZhGLrjOCal1JRuOqumaVEpZcxNWY1JN4XV/YpJKWOU0hb3nKiUMiqljBBCTACG+6UB0AEwKaVG7u037e9V7fVpCvRt8lpoEM45LMvy01u91FYAePzxxw+sOK4WNBJZKSgcd2TW1uTdIhnMT969c3N9eemOEML2ogC4N33v94B0tI0M4MpEQRNZ0zRHCOEQQrjnHXhkQDW3R0cAACAASURBVCnlQgjBOS8hg/X1dZ8MEomEnJ2dlclksoQMPHMZKI0OgNoIIYjg1pvAllqHjY0Nmkql6Orqqtbe3s42Nze1XC5nGIahE0IMSqkphIgQQqIAokFi8GoehBBxxliLSxYeaUSllFFCSASARw4misSgEUJ8cpDFmgePHIDAFqHed845hBCwbduvfdhKEu3t7QeWvbRXcM4xNDR01MtQUDi2WF5ZFXempjanpmfmpifvfrqeWZ7aKxm4NQUltQaHRQZb01DrJYJy8CukA5HDtowlz3jOZrOIRqPSMAypaZrI5XLSM5/hms6ec+5+FwB8SUlKKbwPBts9B/9nz4Pw4HkLqLD/g9fsz63YJowxaJrm7y8thMDy8jKmpqYOtDiuWhzEtqYKCgqVsbicEXempzemp2fmpu589ml2dWWmnDzkEsM2I9ltaOcTgZTSiw4cKaVPBpxz7kpG3LIsnwxs2xZSSmFZlk8GAERra6v0yCCfz8uNjQ2ZTqcrykRXr171ieAA+KAE/r7MQJEgRkZGSFdXF3nppZfo0NAQtSyLJpNJmkwm2fr6uuY4js4510mxujlimqbpOE5UCBGhlLZ40QOlNLZFXooFZKcWSmkUgFdl7UUPOgBdSqkDYIQQBoASQqgQgrrmdDDa8aMezjmklLBtm3jV0lu/pJR44oknjjyNNJ/P+7vtKSgo7D/ml5bF3anp7PTM9NzUZ599srG+NreVDIK/ByMClyDsLfUGtpTS8X6uJTLwDGSPDAqFggCKBBCsPwBqM5D3G9rWA4GMJdnd3S09UzqTyaC1tVUuLy9L0zQlAOkZJlJK7kYPfs4tAP8raE4D8Bx4P7rwXvcK6tyfg1uHlhTESTdjCYFoglLq1zgEDergvg+2beP69etHLi+pyEFBYf8gAcwvLoq7U9Prk1PTc9N3P7uRy2YXthjI2+oMtmQb+b2JvOiAF3dSKyEDz0Dea2TgpZdubGzAiwy2SkTBqMCrOztKaEBp51Nvu1AAyGaz0m3AJymlcnJyEq6kJHVdF5RS4UlKjuMITdOElJK7FXo+ORBCHACO+7tXHMe9TKat9Q8ozVgKtvUO6mwlmwIFOrxKz4vYKi9JKbG0tISZmRn09vYe+IdbCYocFBRqh5QSswuL/O7UVHZ6emZm6u5nN/IbG0u7ZRK5BGEjIBV5Tepc2cghxQZ2DiHE9ohACFFCBlLKfSeDIwoOdgRxn8J9ecaTlraa0kIIls1mNV3XNc65bhiGLqU0vKwlAFEhRAtjLOo4TkzTtGDWUowQEiOExIQQcTdbqQVFYzoCIEIIMQAYUkqDEKKhSFzbspZwL3sJ2CKLeeY055xwzrFVXvJMagBHKi+tr6/jwQcfPJK5FRTCBiElZubm+d3p6fXp6ZmZ6Tuf3SjkNpe3SkGBn7fJRISQEjIghPhVyIwxJ1B05lciB6Wi3WQijwyA7TJRtRXJjQYN8Bdc0oFpZmZGzszMkEceeURms1lpmiYsy5LRaFR4BnUkEvH7JDHGBGOMA+CUUocXk/l9CQlFGYnjXgThncuFENw1q4V7nOFebyWBe9KSv62dR2huxOC/7EUQXrW0VzkthPClJsuy8OGHH2J8fPxQPuStUJGDgkJlCCEwPTfvFMlgemb67p0bhc3NlWCRGQIRgRCixED2ehWRQBtrFGUimxBSkQw0TeOFQoG7tVt8Kxlgi4Hs1RkEDeRge4qrV68CKJJDiDjBh+85bC2GA4DnnntOzs/PE6CYsRSLxaRbDCeEEMK2bWGaptdwTzDGvEykoOfgbPk9SAyOKytxlGYvcZTWNwhShBeCBfc+9eUloOg9eAVwAEpIwSMNTdOwuLh45PKSgoJCccvL6dk5587U1Nr01PT0zOTdT6x8bnVL2qhPAJzzEoJwjWPbfd0mxX5FNgDbtu2t7Sgc27a5rutOOTLwiIBzLnK5XElqaTky8LKJrl696n0PLRlsxTZD2sPIyIhcX19HNpuVXV1d8u7duzKVSkkAwVYawnEc32gOEoOn23HOPRLwS8MBOEKIkqgicH2wLxN1f6a415zPIwbqEZr7XW6JJnxjGoDfJjsYSXz88cfo7Oysa9vPWtAMfzgKCrWCc467M7PO5PT02tTU1NTs5OQNblv/f3vvttzGlWQN5z5UFUCQoiCLEt1t9bgVbo0/2XMK37TUHZ/9R8zFvICeR9Jr9Cvocm4nYuyYiL5TzEWHGP5kt22RKPAASRCJY9XeO/O/qJ2bG0VQlmzKOrhWBAIACZFASapVK1fmylGtWyiYyP5kH88ehNfAsWFc+u4hE98nSRKiKNI0tZPJBL2BvJQMhBA4Go1eurX0TTCQzxqBHPjEe/v2bbp79y5cv349lJl4t4MxJgTtcQysMYa01miMQZ+ptEwdWACwvifYCiGsUspA1boaG9fc3cRmNBMDgq8kHb/V0KlEdLywiHc7kJQy+BDL4jXi8tK//Mu/vKrj26DBrx7WWtjZ3TO9fv+o38vz3d7ON+jsAhmwKmBCYA8hIoGSiAwPn7FnIKJICi4POees9MttfKupM8Zgu912586dOxMyAHj3L/JOVQ63bt2iL7/8UgyHQ7py5Qrt7OxAt9ulsixJCEGTyYRarRYppdDHxrrKs3FOa+2cc7zo2jJBMDF4snCiyiU/oSCg8hwQqtLSQhBfTT2IunoAgJDYyh5E3Xvg8pJSCgaDAezv78Ply5df6YFu0ODXgtIY6PV3zU5/97Dfz3v7vd43zpop1eYK4BTfgPz6S+8RLCWD2C/gUpFSihWCQ0TUWjsiwna7jZPJhJRSYRL5ZcgA4O31DX4OFsih3tK6sbEBn332Gfz1r3+Fzc1Nms/nlCQJaq2x1WqhtRadc84Yo6SUvGTCwnFZKVYLhglCKWV921j8moWZCCGEIyJJVdtrXFYiIQT5jKX67EPouuKpafItrfE9EwOriK+//hq63e4vVl6K2nEbNHjrUZQl7PR3y16/f9jP+7293va3vOVsmW8QqYGC/QEeOEO/D5l8WJ0/Z5hlZEC+bd7HWSMTgnMOJ5MJXbhwAY+Ojsi3m9KjR4+o2+3Shx9+SF9++SVsbGxQQwanI85VWtrSurGxIQaDgazvlD537pwqyzJxziXGmCzLslQIkSFiC3xbK0R5S3wPfp80InaklKGlVQjRIh/Eh4gpAKRCiERUO6U1VGpCQaUm6q2twN1KfI+IIlYJvsU17JuuZy9dvHgR/vmf//kXOehHR0dQ353RoMHbgnlRwHbeL3v93We7/V5vP8+/JcTZkjLRQksp1tZeCh9WB37OgKIJ5JgMRDSFzOUijMDeZ50M3nvvPeK9BkwGv2Q20duOBeUQm7oxhsMhra6u0mw2o263i5PJRBpjyDkXWliNMTwU54iItxXxAAkPwVmqMkmsvypIfGkp7lpybHB7vyH2HMTxWwxvcll5KfgOiBh2TMflJR6OY+LY39+Hg4MDuHTp0is+5I1yaPB2YTYvYDvPy15/d7ib93YO+vnfKVp5GZeI6iayWJwzCMttfHfRiRhrJgOuJjARJEkSZg3KslxKBqurq3R0dLSgDJgMvvjiC7hz5w41yuDFcYIIYHHeQdy9e1e+//774sqVK3I8Hsv19XXpnAsR3nHWkpQy46RWVg5QDbp1hBArSqkVRFwlohVRDcXxIBy/tgUAmfAx3oiYCiESqEgsqAcRpbXGN29MCz75IqLw9xzrHWI0lqkHKSXcvHkTkiR5NUfbYzwew8cff/xKf0eDBj8V09kMHuX9Is/zYT/Ptx/v731PiMEori+zgePy0IIygIoEFiKs/dfC6kueQi7L0qVpanngLFYHL6IMAADexHyitxknDGm+8uayEkC1V3owGNBkMqH5fA5SSl7fGbqWwEdoCCG4DshbjhJENFprE42nG995YBAxgWMlwQSgySe4ehLg3xG8B69yQhkpfu8AoUuJmCCEEGH2gb2Hev6StRa+/vpr+Kd/+qdXetDZD2nQ4E3AZDqFH3r5PO/3h/083366v/cDES0Np4PKPGZlUDAZeI+AX3tCGSilFiIpZrMZkwECgGu32wtTyPP5PJBBp9Mhay3GygDgJBnEC26I3s320l8SS7uVuPPHt7QCeDXhg/gQjq/WXVEUEit3GIUQzss/p5SyfqNR6FQCH3XrS0t8b6H6x6T9Y+3NaSuEkN6YZm8h/G6efxDV0JysEwQzhhCCuJQEUHkTSiki387K5EA+j2lvbw82NzdhY2PjlRxwgIYcGrxejCYTetTLi17ef7qb9x49Pdh/BLVQOoomkWsm8gl14L8e/m/HJWX2DuKQOojIQAiB1trnksH6+nogA4Dl285YGTQC4exwghzYd+Dn9+7dg1u3btHGxgZ6Y5p2dnZISknnz5+n6XQaRssR0fFQHP/DEEJYrXW4kogJge/ZewA/CwEVoSis4jac71ZycLKExH5DmJYWVbIrsNcAUWqr71iieueS9x8IEYVSCra2tuBPf/pTGKA7a7CCadDgl8DhaEzbeV7kef9J3tv54dnjwQ4sIYO4owhg+epLrwQCGaBfblMnA+nXXdbJABF/Nhn8mmYNXieWnv0iY5ru3r0L4E+wW1tb8A//8A9w8eJFOjg4IGMMWWtJa43OOddqtaQxxvFEolcP1peYmCxCSYmvNHwtUgshEjoemFNCCCYICQDc1opQTUdjZEDL+K3XPw6/f+a8uM017mhiJVGWJXz99dfw6aefnuWxDmiUQ4NXiWdHR7Tdy+e9PH/S7/V+OHz6pAenkAHUTGRWCBTFWIMnAu4siuMokiQJ2UQxGfgqwguRwWQygeftQW7I4PVgmSFdfWOxa+mEMQ3HraWqbkxbazOlVKaU4mU+YY2oEGLFOcdm9AoAdPz3VwBgRfiWVvDmNBzvl2ZjOqEqqTUsAvJlJeFJgtXFQnurfx5unNrKNzali6IIy4L+9V//FS5evHjmBx2g2SHd4Ozw9Nkz2s77s4oMdr4fDYe7ULvql7V1l3VjmdVAtMuAYymWDp5JKRf2GcQmMhvIrVaLOLnUWotsIAMAvK7Vlw1eHKfWTfxfzImeS85aGo/HiIhiPB5LPwyHWZYhIrqVlRVrrVVsRCmljLVW+39kbFxpIkr8lYqOSk5sSFtPAAqqkhK3tbq47OXBRMaJrYEQsEpnDYqivhQoLi8hIiRJEp5vbW3BzZs3X0l5ibOeGjR4WTweDmmnl0/z/u7jvLfz/fjZcB+WlIBi38B7BHGbqYl9A6pWXYaNZ0opzigKQXXW2qAOrLWhvdRaG8jAORdirC9duhR2Giwjg2bw7M3GqcqBvx/tlg4DcZPJRHY6HQFeOZw7d05Np1NtrU3m83m6urqa8J4H35battYuqAff6toBgBWqWltDW6sQoiWEaGG1ejQFgIyqPQ/xCtHQ2uoVA6uIhfZWROQ4jaAeAGBhMI6H46K9D4Ifb25uwieffHLmB/69996Ddrt95j+3wbuHwZOntJ3n037eH+Q7299PRkcH8BwyiBWCqA2h+Q6iUkT7DLhZBHExtfRllMFpOw0AGmXwtuK55MClJW5rvXXrFvzlL3+RRVHItbU1oZQKE9NJkmillHbOJeBLQUwOiNhyzrWllC1EXGGCIKIwPS2lbPvvtcHPPPjyUph7gGoZ0LKp6TA57VUFl5gAFgkidCXx57bWLpSYotkHUZYlWGvh3/7t3+C999470wO/vr4Oa2trZ/ozG7z9CCsve/1pvtsf7O7sfD8ZHQ3gFDKI20zrX3fOhfbSOhmwkcxBdT+VDB48eADdbpcaMnj38KPkAAALcw+sHlZXV8XOzo7qdrtyMpno9fV1pZTSo9EoSdM00Von1trMOZcppVpwvPWtzSoh9h+klG3nXMd/nQkig5PeQwpVGepErAYRKeER+w/+M7L/sEAQ7D8YY+oKQjBRaK3h5s2bZ1oG6nQ60O12z+znNXg7QQCwdzDAnTyf9Pu7B/2d7e9nk/ETqE7kBVQn81IIsTB4Vh9Ei8kAvHfADSDgVQErA04tTdPUxvsMmBCcc3j+/Hkcj8cLZLC2tkZ7e3vUkMGvA88tptd9h3v37sEnn3wC77//Pu3t7YnNzU1aX1+nsiwxTVMxnU6x3W67NE3lbDZjr8B57yH4CVDVQLXwm5qISPvnBREpIlJSSuUfs/cQMpW80RxKR6xw/NfkcWMDcXQGL6Fe6FryLwoDcuQH47jkxI/LsoSHDx+e6WpPXlfa4NcFIoLdgwPcyfuTPM8Pdnu9v8+nkyH4JTXxCZ/8ukuIoin4OVVtpAuegZQyTCE7506QQbvdtkVROCJCInKz2Yy3N2KSJGE/S6fToYODgx9dcNN4Bu82fsxzCK8DOBnGx/ulDw4O1NramtRa6/l8rrMsS5xzJ9QD+w98i0tM/BiiOA0hRAt8eYmIMu8/pIiYSim1LzGF0hJV09QcqxGUA5NH5EcsVQ9cXirLUsQdTKwgPvvsM7hw4cKZHHilVLOF7lcAJIL+3r7r5f1J3s/3d3e2vyuL4rB+1U+1pFL/PFYO8etCieh5ykBGO5DryiDLsgUyaJRBgzpeihz4cb2tdT6fSymlStNUA4AqikJnWZYURZFmWRb8B0RsWWvbXGaCmkFNRCtxUitE3oMnlqWtrRB5D2IxdylubRWeEJb6D9ytZIwRcfZSTBJnXV5q2lnfPThEyPf2XC/vj/O8v7/b2/7eluWpZPBjJnJcKoJonwFUbaYWq9C6U8mA1/c2ZNDgZfFCPZpcq7979664ffs2XL9+nf7rv/5LdLtdmkwmVJYldbtdTJIEj46ORJIkCFWqKodqSaiu7K3vllDOOa2U0lBdLWlYDNdbCNnzj8NMg3/MU9Iyeo/8L5m/Ho/X19NQudoUOmN9eYmYNOK2VufcmZaXnHM/+2c0eP1wzkFvd8/t5P1xv5/v7fV2vrPGhC1nvnV7YbuZiLadxWUjbx4vTCBzSykskoHTWlsiCrMGROSMMUhE6IPpQnJBTAZNmajBi+KlG/jv3r0Lt2/fpq2tLRoOh3DlyhUaj8c0n8+pLEvUWgutNSqlsNVqhegLPzhjuLtJVZffCgCUc46H2jQi1ttU66UiCVECKyuBKqalmpqGaCI6JpD4axExBIJgElRKEfp9ED6cj4hIICLkeQ6bm5s/20xuyOHthLUWert7difPPRnk3zlrxj4eZiGpNFYHsGT7GXtu1lr23gIRYJRc+jJkMJvN6OLFi9iQQYOfixcih/gsynMPn3/+OQAAra2tIQBAu90WiEhFUaDWGufzOe+UlqwekiRRftcrn/RZLZSIqP0VlhRCxKTABrX0BrWUUgqKpqJrBnV9O5xc9i++bk5zqSnKYFqI1ogH5B48eEA3btwQP6e8hD4IsMGbDWMt7PR3bS/PR3ne293P8++dtWHLWdQ9VBJRWHfJ5SI4DqUL6sBaG0pEfhWmcc5ZpZQlIqu1DtsRX5YMmtWXDc4KL6UcxHFaq7h+/TptbGyEOs1sNqP19XUcjUYiSRKnteZpZsnqoSgKK6VUUkoDAEpKycpBaa0VIgYy4A4lROQOpTDoBl5FMClECoFP+I7/0fsZh4XkVoDjMlN0H9RDVF4Kr0VEcs4JrTUURQHffPMNffzxxz95a0+jHN5MlMbATt43vX5/1M/z3b289wM6NxWLcwUnyMAPmJ0Iq4vJAHwshRDiRFCdqHatO621i9tLGzJo8LrwwuSwUJyH47RWgCpSYzKZULvdpjRN0RgjZrMZrq2tibIsUSnltNYWAKRzTiGi8rsegkJwzikAUEopiYgKqwjvutEck4IEP+AGUZ5S7D34tyuq/2cSEXEZQXAbLPHzunpQSvGN+DmXl86fP/+TDnzzn/TNQFGWsJ33Td7vH/V7+e5ev/cDIc7izqDYJ/C3ok4GTBZxqQieQwY8hVwnA+dcIIKGDBq8TryUcqiVlwD8Cfnq1av017/+lTqdDrXbbUREYYzB+XwufGlJwHF5SaVpygpAQU1BVOdmySpiYbYBFj0HCYszDvwe68Y0n/il9yak8CtDuWwUEUYglZggKFoMBHC8WY7LSz8lZbX5T/t6MJsXsJPnptfvH/bzvH/Qzx8R0RyiltGYDGpqIJCCHzIz/Bj8ghsAMMYYGyeXnkYGxpilZCCEwNFo1JBBg9eKF21lPf4D4jitddncQ6fTEevr6/Lw8DBc+XOsBlZb31IASJMkSZ1zGR1PTS/MQNBiO2tbCNFyzq1IKXliuuV/ViaqzKWUiBI//6DZ1KZqilrWbqHNlQURl6XweLWoiGYfyForanMPZIyB3/3ud+LatWsvXV46OjriRUoNXiGms3m1/zjPD3f7eX7Q7++QHy6jJe2ltcljLhktDKiBn0Dm5FKsoquXkgGXhubzuVumDEaj0YIyaFpLG7wpeOlupUg9LHx9OBxSURQEANButynLMnz69KlYX18X4FNVkyQR8/lcdjodMZvNpBBC+alO6ZyTAKD9ZHTwGXw9SHpFwWoBIBpm828hlJb81xdKTD6Zlb8db5WT3pcg/zpeFETe+I7LS8TGNKuH7e1tunz5slhfX3/Z4/hSr2/wYphMp7Ddy4tev3/Y7+X9J4P9bXSuvug+zBmI2i4DvsFxlxGH1RmlVIikiO+VUs4YY2W1w8QJIVy73Xaz2cx1Oh0kImy3240yaPBW4aWVA8CPZy6Nx2O5vr4uB4OB6nQ6cjabaa31QjBfWZZpmqapECIzxrSSJMmomoQOA3L1mw/ua4vjxNaMiOq5S4lXEsvmJuL01hNtsXBMOJK7lJxzfC+stRQpB1GWJVlrIcsycePGDfky5aXDw8NXkvb6a8N4MoFHvbzI+/1n/byXDweDHV/nP6EKoiv/BRO5riAgmjyG2uCZb8l28eAZkwIiotbaEVFQBisrK+ScW6oMvvzyS9jY2KBGGTR4E/GTFhXUzWkAgMFgEFaJ1s1prTUCgCvLUiqlWG4LABDWWuk7m4RzTsrqf55KkkQ656QQQiKiVEpxCSh0KZGfeIZqp7Twsw4hEqPuJUDV5srGc7xrmucluKyEsTHtv0+y2gFBUkqhlCKtNSEiTKdT+vbbb+HatWvNirdXjKPxmLZ7ednL82He6/WePR7045N6TAAQZRJBpAT43j8OEdZMCGLJchsuFSmlnA+tO0EGk8mEeNuZ7zCiR48eUbfbpQ8//DCQASuD//7v/26UQYM3Fj95i03dnL5+/foJc3o+n9P6+joioiAiobW2Simw1oqiKGSSJJaIFJOCUiqUkvzJmu/rJ+9w1o/KRWGNILejRgRBAIBeTfCaTiaKYGx7cpH+Y8VRG8Rk4Y1pZGXB4XyPHj1ym5ub4ty5cz+5vbXBSfiVl0Wv33+22+vtPHvyeHeZD0DRXAE/5zZTOG4rDdPHTAjgI6zJD6DVyYCIAiEkSfKjZMCrLxsyaPC24yeVlcIffgFzmnOXhBCqKAq9urq61KBGxEwIERYEcXmJiFpKqTYitqnKWOKyU4uIWmxQ18tLQogEqpWiCRFp7ojyZjXnLwWTOiYHgONgPvAmNe994HC+OJCPb1mWiT/96U9JTVQtRVNWWo7hs0PazvMiz/Onea+3czR8un8aGcQlIn6OPqk0vodo/SUsWW7j/QOe4l8oEzE4l6hOBrz6sikTNXjX8LP2X76IOc25S8PhUHS7Xbe6ugqz2UxANfMghBBSay2klOHqHY6H3GSsFricxK/jNtNl8KYyAABJKRGq1aMElWLQUOUu8Q4Igqo0JRGRS2ZcuiIpJQghJA/GSb9eNDamfXlJ/P3vf7cfffTRjx7XFyGQXwOeDIe03cvneb//NN/Z2RkfPjuAWlspRINlUDOR676BUip0EfnXWP/zAhlQlfHlhJ9CVkotkIFzDsuyXEoGy5QBk8EXX3wBd+7coUYZNHgX8LOUA8Dp6mFtbU20221xcHAgx+PxQqw3+PbWoih0rB6guuJPhRBBPbA6iJVE3ZgGvy3O/5kFBUFReyscJ7hye+uCeoDjeYqQx8QKgltbiSgsBooMalGWJZRlKZxz8Mc//rH1Y+Wl8XgMH3/88c869m8jHj99So96+azf7z/Nt7d3/JYzJgMjakttqNZeWlcGUCmAhdRS/7WFsLq6MkA83nT2U5UBAMCdO3fC44YJGrxL+FnKAWC5evBhX3D//n24cuUKzWYzSpIEy7IUSZJwtIUAAMHtrUmSCGMMm8tSay2cc1JrHYxn9gAAQiwGAIQhN37Mbwu8IkAiQqhaV1k1IFTEQOA3yEURG+xzIP8O5xwrmxDIRz6gzznH09NCay0QUfztb38rbt682XqeOvg1KAcCgMHjJ7iT5/O8nz/tbe9sz8ajJ3DsCxioxVbDsVEc7zWI21ANm8++1BQMZA6qM8ZwGrBTSlkmgSRJQoy1cw7n8/lzlQEAwIMHD05VBkREt2/ffh2HtkGDV46frRzCDxKL+6a3trbE+++/L7rdroiH4zg+YzqdagBQZVlqREzSNE0QMdFaJ865TGudImLmfYagIrxCCP5D5E8s7JuuKYh498PCgNxz/AcO9uPEV0FE5JwTzjmKV4t61cC7IIQxRl69ejX76KOP0tOO12w2gz/84Q9ncuzfFBAA7A8GuJP3Z3nef9LfebQ9m0yG4MPnoLYDGSK/IDr5F95MDl6B8HsNoKYMlFJWCGFiMljWXrpMGXQ6HbLWYqwMABbJAKBRBg1+vfjZymEZtra26Pr167C1tQUAAEVR0HA4FO12m2azGQ+fOQCAbrcLR0dHQkoJxhi+QpfOOZEkifC+BFhrhTiOtRDCdyOB39lAHuA7k1g1AABidXbH6pxfPfbEgJ4YYtWg2HPwngcIITh2g+TxoqAQrcFdTF5hSKUUfvfdd3j58mW9tra2tL31p0RuvGkgomr/cT+f9nr5k/7OznYxmz6DiAggIgNa0lHEr+FSER1vOgs7DfzN1vOJEzsplAAAFYFJREFUYjIAANdutwMZWGsXlAGTASuD9fX1QAYAJwfP/OdrlEGDXy3OjByi8hLdvXtX3Lp1i+7du8cEQRcuXMDV1dUQTfH06VMhpRTz+VwkSeLm87nodDqmKArRarVCJxAiCt8+KpIkCRdvTAxwfEFHUkpiAvDfQwBAb0g7qtaKIhE5IYSDaKmQn2BT/nWhvOR/F5vgAHCcvcRzDhzlzSUwRFTOOfm3v/1tfOPGjXPLSkhntU3ulwQSwd7+AW7n+bSf5493e73t+Wx6CM8hA68Q6sF1C68TtQU37BfUyUBK6ay1P4kMJpMJxJEUy6aQwz+kRiA0aHB2ZSWAk+b07du34d69e2I4HMputyvi6en5fC4xSmX1ba56MpmE8hJExjK3ugohsrjc5MtIC6tEiSiY06JqaY2npzVV7a0Jl5eE3x8hqoE75Qf0OC48HCfmP1ycnI53TQs/OS2NMdIYo69du7Z29erVVv1YERFcuXLlzI79qwAiQn9vH3fyfJL388e7O71HxXw25hM+nEIGdRM5NpMhaiuNuopMPZuIzeNYHcQm8o+ViQAAfiyfqCkTNWhwOs6UHABOEgQAQH3+4R//8R/F//7v/6oPPvhAIKKazWYaAFSn01Hj8ThBxKTdbmutdVKW5QJBKKUCUcR+BBOFc64FVddTBgCZlDJBxAwqvyEFP/vgSUILIeLNc1xWCtlOFE1RAwCXr8BaC4hIWO2dBr93WvjOJVWWpTLGKERM/vznP7+3urp6Qir89re/faOMaecc7z+e5nk+2M172+V8zmRwQh3I2u7jOimcRgZa66VTyC9CBq1Wi3gP8suSAUBDCA0avCheCTkAHEvzenvrd999FwxqqAxp6U/QCgCU1lobY3TVXJIkUkoNi9lJgSSEEJm1NhADEWVUtbqmRJRJKVNPDAu5S+CNaURk9RDvjpBcVoLj7KXw0bwHgkQUhuKcc+QH40RZltIrCF0UhbLWJufOnWvfuHHjYp0INjc3oersfT2wzkHe33U7/f4k7/UO9vLetjVmCjUDGZb4BrSkzZQzjWIy0FovHTzj1NKXIYPhcEgffvghNmTQoMGrx5mTA8CiegB4/vzDbDaTw+FQra6uynPnzgUVEU9RxwThr/oz8CTBKiJWFEwUTA5ElCqlEkRc6F5iYvCzFmEVaawc/GfhoTvCaiCP6Hg7HDFJcFmpKAppjFFlWSZlWWprbfp/rl+/ePX3v+/Ex+nChQuwsrJy5sf/NBhroVetvJzkeT7Y7+fb1pgp+XwheAEyELVAO6yG0BbiKKBGBhxU15BBgwZvD17JZStFl9rx/9WvvvoKPv/8c7p//z4URUGbm5vUbrdxNBqJbrdL/X5fnDt3jvdnhh3NUkoqy5Ljs8lfbZOUkhCRu5NQCEFxlxJUHVEoqinZ1D93UJ3AEiLiFlcbxWso8Mmt4ji9FXy+U/gs/LuEECilFM45klIKH8onnXNKSmm11gki4v/7+uvB5UuXWp1OJ5SXrLVnf/AjlMbATr9ve3l/nOe9wUHe33HOToUQYacxRJ1D8b0QoqgThvQ7DLi9VFQx1oEMnHOhTMRkYK11AOCUUs5aG2YNrLV4/vx5HI/H5JwLZHDp0iV88OABAMBCjDWTQRNj3aDBL4NXohzCD49KTKweAADYf9jb2xObm5uy3W6L2WwmAUAdHR0pKaXyNXo1mUwSrbXmEpNSKjHGpEqpBcMaIiXBKkJrnRBRFimGVFRx3iF3ib0HJgeoCEKCX00KUZS3H4ojPwRHRER+ahoRMSwEYs+hKAptjEnLssyMMdmFCxfO3bhx47d8pdteWYH3Llw4s+NdlCXs5H3by/Nx3usdHOzu7iC6WUQGBqq00rD68sdMZCYD8N4B+j0G4FVBTAY+rdTGay9ZHTjnAhnEyqBZcNOgwZuJV1rwJlqM9uad0/fu3YPhcEibm5uwurqK4/FYzudzMsbguXPnFn5Gp9MBYwwBAFlrSWtNWmsyxpBSKnxdCMHPEQC4bBHUAlV98gYRUyllIAguLXnFwAY1E4SIS0v8maDiPfSfj/w0NasJ6RUEl8a4JdM9efLE/fDo0ervP/xwHQDA/UzlMC8K2Mlz28v7o16vtz/Y280JcS6OZwhOhNWRnzyGKKeIn1PVRho8AyIyfiKZ9yGfIIN2u22LonBUJdW62WyGSikHAJgkCXG5qNPp0MHBAa2trZGfoG8W3DRo8AbjlSqH8EvEyfylW7duwV/+8peFFtf5fC5brZYAfwU/Ho+DgjDGaKWUllJqREyIKLS8SikTAEidcykApPw8vnkyOOE5kE9tjTqWFHjVUF38VoGAETnEV68IAOTjGMI6UWstlGWpi6LQZVmmxpisLMuWN89Xvvji//u401nRUin4zfvvv/BxnM3nsN3LTd7vj3q93sHjvd08PtnHPoCMtp1BNIkMi8ohHkZbWG4DpygDKaUriuKEb+CcwyzLFsigUQYNGry9+EXIgX8XP1gWscEEsazE1Gq1pNZat9ttNZlMEimlTtNUIWKCiKHMxAQgpUxiouDnvoyUQrWONJSVEHGhtOSJgSM1FkL4vKdBQgiy1rJqQEQEay0hIvmOJWmM0fP5PDXGpMaYtrW2ba1tXbx4cePGjRt/QAD43QcfnHrAprMZbPdy0+vno7zXO3iyv9+H4yU2hhZ3IYdsIn/FPz/NRI5LRVDbdMZtpc8jA+ccKqVcQwYNGry7+CX7KE8E9F2/fp22trbg2rVrxBvkAADiEtPKygodHR0pnivwMRuEiJSmKQEAKaVISumMMaiUQkR0SinnO2ZSf6KzVCW0Gk8UxjmXyGrXBHctaaUUE4OC4xjvBeUAfvq6am6qFv8AAGqt0RgDSimRJIlARJ1UrmzmO3qMEMIMBgOzvb29/v5vP9iAiDTHkyns5JUyyHd2Dp48HuwDEcdNhPuoTLRADgBQOudCRAV4E1nUJpB9ea1OBs7HWYdZAyJyxhgkIvTBdOicQwBYIIOmTNSgwbuHX1I5VL9QHAf08QT1shJTu90Wq6urodVVCKE6nY6aTqeq3W4rIlI8D8GlJimlLssyUUolUkrNikFrvTAV7ZxLhRCab4i4UFZCxNDOCsd7HUT0GQirvQ/oz3SIiORPooCIVBSFdM6poij0bDbLnHNZWZZta23bOZdJKVf++Of/+4XWCvN+/2g3z/eHjwePobqatwDAbaIFVGqh8GRQIGJQBohYROF0JVSxE4W1NqiDuK20nlx6mjJwzgUyGI1GdPHixaV7kBtl0KDBu4nXRg6MeAbi4cOHCymuXGLiQbl2uy3H47Hy7aAKAJRzjmcUtLV2wZNggrDWJkIILaUM8RlMDNZa3vGgVCU7tJ93iOccFspKiMjrSUlr7cqyZAXhhBBUFIUwxgjnnJjP54kxJpnNZpm1tuWcaxljMkRMvW+i/M8m8MY5VXMHBVQEURDRXEpZYLX2kkmiBIACEQ0vuKmXi9Anl9ankBsyaNCgwY/hFx/PjU4eC+PCg8GAdnd34dq1awQAsLq6KtrtdnhNWZYghKDV1VUYj8fQarU4gZWMMTrLMpRSEvjyTlEUqLXm6Gbr/QgLANo5Z1U1RBHUA/gZByJSWGUq8bR0UA58EvQb4UgIQc45lNUuCKSqpRV95xR4ctFElHBbLRFlSqmMjrOdJJel/PuzXDJipQBeOTBheKLg1ZkGEY1zjskgTCELIayU0mmtXdxeWi8TzWazBTKYz+c0mUyaMlGDBr9i/OLKYeGXRyUmgOMMpocPH4rd3V1x4cIFsba2JsqylN1uV2ZZJhBRGWOktVayguCTujFGW2t1mqaKVUSSJOGxc05HJ2XlPQaFiAudSlUDjgptrEKE9aUAAEE1MDwhOC4xOefQ5y0JPymt5vO59sZ06o10ba1VVE1fA3jVAL6TCCr/IBCCEKJwzsW7lA0iWg6sE36vQZIkQR0wGcSqoFEGDRo0eBG8EeTAOK2LaX9/X5RlKdvttlhfX5c+nkIKIVSr1ZKTyUQJIVSaplIppYlIWWs1+HIT3zvndOW7aiaBQBJJkkjnXBiC4x5WJgiAaggOYJEchN8TIYTAoihQSonWWiIiKooCjDHSrw/VniAST2QKEZUfqEPnHFK129h4L8EwIQghSmutUUqVngRCmShJEmuMcdZa25BBgwYNzgqvL/UNlq8Y9YNydO/evZDkevnyZdjf38dWqyUODw/BGANra2vg9zuoLMsoyzKcTqfKOUcAgK1WC4uiUACAZVmqNE0dIro0TS35mHBWEEwMqqoXSSmltNZKVg9Y5SqBcy4MwjFB+DZWjtFwQgiEKrIDtNZkrRVKKSGlFOxnGGOYiAT/LPDDemwkCyGMlNJQtUrTtNvtsigKl2WZEUK4LMvsZDJBRHRFUbiYDGazGQkhcDQaNWWiBg0a/CS8VuUQ3oRYjNkAqEpMW1tbAgDgtFmILMtEURQSAJS1VrZaLWmtZb+A90SoTqcjoUqAVWmahh0SvswktdbBZ2Ajmm9KKVhWVnLOgdYa/XQ2eiHh0jRFYwzNZjNSSqFXD4CIMJlMFBEJ/x6hKAoAACrLksmB5wuMlNKOx2NLRLbValmqJrzdaDSyiIjdbtcBAA4GAzTGYKMMGjRocJZ4I8gBYHmJiVtd651Mw+FQdDod8eGHH4peryfX1tZkkiSi3W5LY4wcjUYqyzIJFQEEstBayyRJ5GQy0fzcGKP8bINUSp3oUoo9B5+rJBCRlFKklCLfqRQIQmvtnKsWlBVFQcYYgMo4B2MMOefEdDqVzjnCah8E+XgQa4zBSrRY51WDg8pgN0SE8/ncJUniJpMJbWxs4NbWFm1ubmJMBl9++SVsbGxQQwYNGjT4OXhjyAFg+aKgBw8eiFu3bsHW1pb4/PPPYTAYSPYh4nbX1dVVWRSFTJJElGUprbUyyzLpS01SCKG8jaC01hIqr4G3vSlWIV4tSF8Gks45obWGunKw1oKP8OYpafInXucznlxZliSEwMlkAkopmk6nPAPBnxen0ykIIdBa67TWTghhp9MppWnqAMDO53NqtVpuOp3S4eGhe//99/Hp06d04cIFEkJgQwYNGjR4FXijyAFg+bKgWEEAAPDSICaJixcvyjRNxeHhYVjR2el0pNZatFotaYwJJIGIMkmShXutdSADvjnnxMrKCjjnhLVWyGp1KABUrazGGPABgFAJC6Q0TZ1vs0WsosSd3/eASZIQT4BLKXE8HtNoNKIkSQgA0FrrjDGQpqk7OjrCVqvlwEePj0YjMsbgp59+ivfv34eHDx8SE0JDBg0aNHgVeOPIAWC5ggCofAiAKvKbCQIAoD40l2WZGI1GotPpSG571VoLa610zsksy6RSSjjnpHNOpmkqZbWTQc5mM8kGspRSWGtFmqZgrV2Yy9BaEyIS30clIvS7HbDT6aBzjpgIVldX0afJ4mQyAa8qsCgKOn/+PEopUSmFP/zwAznn8Pe//z3+z//8D3W7Xfr3f/93BADY2tqiO3fuhL+0hgwaNGjwKvBGkgPAcgXx4MED8cknnxCb1Z9//vmCimi322IwGAhOd82yTCRJIuJyU5IkYjgcylarJZVSIiYNYwwv6xGIKI0xIssyMMYIAIAsy8L7S5KEiqKAJEkI/UY4pRTy/Ww2g6IocG1tDVutFs3nc7TW0pMnT0gphe12m8qypKIoSEqJ586do8FggEdHR/T06VP69NNP8dq1a/TVV18BESH/3jt37iA0aNCgwSvGG0sOAMtNaoBjBQEAEM9EAFST1ePxWA6HQ9Fut0Wr1RKj0UhkWSY6nY6cTqciSRLBaoLJQSklyrIUSinBj9vtNkwmE9lqtU68t/l8Dn5fAQEApGlK3L3kd0qTD+ILj8uypM3NTXzy5AkcHh7i2toatVot3NjYoB9++AE6nQ5evXqV/vM//5P8ZztROmqUQoMGDX4JvNHkAHBSQQCcJAnuZgIAiAfnOp2O+OCDD+DJkydiNptJJok0TUWSJKJOFKurqzCfz8V8PhdaawEAwETBv3tlZQWyLKPhcCjSNKXpdAp+zmKBFKy1tLKyQsYYMsZQlmVYFAUVRUF7e3sgpcQLFy7QaDSizc1NGg6H5NNpaWtrK5jL/hiAPwZv9l9WgwYN3hnI1/0GfgzRJHI4SfJJc2tri27dukWDwYC63S5eu3Yt7CHudDq4urqKz549w2+++YaGwyFeunTJIaIDvynOzwq4drttjTEWEe3KyopttVqW4XOYjDHGGmPs4eGhPTo6skop45yzflrZOOestZZf4wDAPXv2zEkpHQC4w8NDHA6H+MEHH7hPPvnE/sd//IfrdDqYZRlubGxgt9vFOjHwZ+Zj8Jr+Cho0aPArxBuvHGKcpiK4mwkAQssrwHFX097eXuhy6nQ6Yjgcit/85jfAHU5sYAMApGkqAACSJBEAANPpNKiG9fV1AADY29sT7XabAAD8ABsAAHQ6HVpdXSVjDBVFQfP5nAAAZrMZdbtdmkwmdPnyZbp06RICAIxGIwKoQgfrasF/3kYtNGjQ4LXgjVcOMfhEGbdu+pMpsYq4fv06DQYDGgwGNBqNaDgc0s2bN3Fzc5NYTaRpio8fP0a+mpdSuo2NDZemKQKAG41GyJHWRBRu/DU/seyIyF24cMEhYlAkjx8/dlJKNxwOsdVqoXPOXblyxe3t7WGn08GdnR0cjUY0Go3C+1xGDP7jNsTQoEGD14K3SjnEqJvVjGWmNQDAcDgMRMjm9d7ennj69GlQGoPBQAAAcFT4cDgMP+Py5ctweHgoWA34vCfodrvhTcxmMwIA4HmGy5cv07fffgvsKQBAiCQfDAZ07949YMO59jHezr+UBg0avDN4a8mBcVpHEyMmCR6iAwB4+PCh+Oyzz4DbYOM/s7+/H8jhg+fseOYuI8bly5cJAGAZIXz11Vdw/fr18EZPUwsv9KEbNGjQ4BXjrScHgOUqgkni9u3b4WvsSwAcexPc5QRwrCgAKlWxublJe3t74qOPPgKA6qQPAPDRRx8FAqi/l6tXrxLAsZ8AUKkEgJA4C3fv3q2TAkBDDA0aNHiD8E6QA+O0UhPA8klrgEU1AQCBLK5du0Zra2vi/v37AFARB5/479+/D5999hnw9xisEHwG1AmVwGjUQoMGDd50vFPkAHDc0QSw2NUEsEgQsaIAOKkq+PH169eJCYRP/HUMBgPyuyfCL4xVgv99C11I/v29Wwe/QYMG7wzeOXIAWCQIgJMkwXhRsnhRbG1t0e3btwMh+J+78Mub9tQGDRq8DXgnySHG85QEwEkD+3ngE7/Pdjr1wC3xExq10KBBg7cK7zw5xHhRRREjMrap/twP4J36QxpCaNCgwduK/x8nB8alzCUh4QAAAABJRU5ErkJgglBLAQIUAAoAAAAIAOJdbEYqXfgFaQEAAPQCAAAlAAAAAAAAAAAAAAAAAAAAAABub3JtYXRpdmUtdHlwZXMtc29mdHdhcmVDb21wb25lbnQueW1sUEsBAhQACgAAAAAA4l1sRgAAAAAAAAAAAAAAAAcAAAAAAAAAAAAQAAAArAEAAGltYWdlcy9QSwECFAAKAAAAAADiXWxGnI4hE2/YAABv2AAAEwAAAAAAAAAAAAAAAADRAQAAaW1hZ2VzL3NvZnR3YXJlLnBuZ1BLBQYAAAAAAwADAMkAAABx2gAAAAA="} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/command b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/commandTrial b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/contentMD5.txt
new file mode 100644
index 0000000000..59613c85d8
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/contentMD5.txt
@@ -0,0 +1 @@
+NzEyYjhjMDY4ZGNiMGM4MGM3OTY2NzFjY2Y4OTNjN2E= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/headers b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/results b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/toExec b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/05_NormativeTypeCI-softwareComponent/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/body.txt b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/body.txt
new file mode 100644
index 0000000000..0ca83ed662
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-webServer.zip","isEncoded":true,"isCompressed":true,"artifactList":[],"payloadData":"UEsDBAoAAAAIAApebEb/fQgElQEAAJMDAAAdAAAAbm9ybWF0aXZlLXR5cGVzLXdlYlNlcnZlci55bWyVUk1r4zAQvftXDL3bpOzNh0IIhT21BRv2UIpQrHEjsDViNEnov+9YtRO3ZBfWutiaeR/zPEKps8Zh74MXTyGZE3LSlxokl5If44Dmw46DuTcbPWe3+VUUgnpvBU2wI87NZSAerfgTlvIRMZVn3DfIynhtt0c5ENfQPje7LbS7a+UifF9t9EwyZfO0fWl+P7dF4TB17KPkjh0FsT4kkAPCRRSyKFyHqQo1TyypLgBKuLttkomkvqF59y9Qol7OlnFHqhAw/IWhCOTQZMjkIXNV012q/izZTAUAu0/CthONko+YrxyyCjrTM431N2zzU33uX0X0kK8AWk3oK+uLIDwpCbRqChgjY1KCBDZcPMAyHXSLABBDUrTvNOaDFfAJOhvtfkCgHg6UxId3JXEQmU7eTV+jDfYdxwyPyDYvGPRKpZwT40iqob62MQ6+y3XIE2bzmd8P+iu/0ssxxWgwuEg+yJLJuq96nItzf8LuyPhfkGmWRQ7yTt1EzTuou7206uA65drt9Jzs4J1Z7QG8/tyD9fhvxSdQSwECFAAKAAAACAAKXmxG/30IBJUBAACTAwAAHQAAAAAAAAAAAAAAAAAAAAAAbm9ybWF0aXZlLXR5cGVzLXdlYlNlcnZlci55bWxQSwUGAAAAAAEAAQBLAAAA0AEAAAAA"} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/command b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/commandTrial b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/contentMD5.txt
new file mode 100644
index 0000000000..da3c1fb22c
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/contentMD5.txt
@@ -0,0 +1 @@
+YmZkOGVhZjgxMmRjMjNmODgxMzQ3N2NkOGMzNmViMGI= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/headers b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/results b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/toExec b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/toExec
new file mode 100644
index 0000000000..b74e582498
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/06_NormativeTypeCI-webServer/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.122:8080/sdc1/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "USER_ID: USER_ID" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/body.txt b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/body.txt
new file mode 100644
index 0000000000..15f24240fb
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-webApplication.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"network.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/network.png"}],"payloadData":"UEsDBAoAAAAIACBebEYn4Wr3gwEAAB8DAAAiAAAAbm9ybWF0aXZlLXR5cGVzLXdlYkFwcGxpY2F0aW9uLnltbH1QO2/bMBDe9SsOmSvGQTcNAQwvQYc4qAx0FC7kySIqkSzvZMP/viSt+BGkBRfi7rvvJZ41doZ666xY77g7UOT0aUDKiu0URupOOI3dU7dK72hW36tKKM1RqHM40QKunY8Tij1QLadAXB/pfR3CaDVm7usNzjL42MBu227WsNtcNxf1J7VKL2vV7ev6rX3Z7qrKEOtogxTExjtB6xhkILgoQ1GGayJVpQQ+CjcVQA0PXzuN3kvzhebD/45SvJZisvyvy8p5Q10BZ/XCovKM1a+7avIWkumYyE3XRz81d+ifyd4CuWnguYwAdqmAc5X3rJCPIVKIxOSEAYF9L0eMBHgDkwEFNDp4J5jQ4Z4MoDMQ5zQ6pasL+Tlu4VUAbSBte6uXzn0PqZFbZgae9QDI8AMP+A1ItALt59FkqSUu5LjJg+XCo0oowT03Szyrc9hHOyVj/OhIjj7+VsHtyz7Sn9lGmnLAj4saBs/SfC787H7BQFH7wEQaz44HG1i9pGsyW1f9BVBLAwQKAAAAAAAgXmxGAAAAAAAAAAAAAAAABwAAAGltYWdlcy9QSwMECgAAAAAAIF5sRgSle3HbbwIA228CABIAAABpbWFnZXMvbmV0d29yay5wbmeJUE5HDQoaCgAAAA1JSERSAAAB+QAAAgAIBgAAAIHDFagAAAAEc0JJVAgICAh8CGSIAAAACXBIWXMAAKHMAAChzAHSZr+kAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAABN0RVh0QXV0aG9yAHdhcnN6YXdpYW5rYQy+S5cAAAEjdEVYdERlc2NyaXB0aW9uACJJbnRlcm5ldCIgaWNvbiBmcm9tIDxhIGhyZWY9Imh0dHA6Ly90YW5nby5mcmVlZGVza3RvcC5vcmcvVGFuZ29fRGVza3RvcF9Qcm9qZWN0Ij4gVGFuZ28gUHJvamVjdCA8L2E+IFxuPGJyPjxicj5cblNpbmNlIHZlcnNpb24gMC44LjkwIFRhbmdvIFByb2plY3QgaWNvbnMgYXJlIFB1YmxpYyBEb21haW46IDxhIGhyZWY9Imh0dHA6Ly90YW5nby5mcmVlZGVza3RvcC5vcmcvRnJlcXVlbnRseV9Bc2tlZF9RdWVzdGlvbnMjVGVybXNfb2ZfVXNlLjNGIj4gVGFuZ28gUHJvamVjdCBGQVEgPC9hPhStYigAAAAhdEVYdENyZWF0aW9uIFRpbWUAMjAxMC0wMy0yOVQwOTowMToyODRTQT0AAABWdEVYdFNvdXJjZQBodHRwOi8vb3BlbmNsaXBhcnQub3JnL2RldGFpbC8zNTM4OS90YW5nby1hcHBsaWNhdGlvbnMtaW50ZXJuZXQtYnktd2Fyc3phd2lhbmtho1yxagAAAEl0RVh0Q29weXJpZ2h0AFB1YmxpYyBEb21haW4gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluL1nD/soAACAASURBVHic7L13mCZJXt/5yXxdvVX1lnfdXV3t/Xi/Nbuzw84OMCwgsxx7QpxOQivgJPoOiTvQSSeBhKSTDoF0Kh0cCwKtFh4EEiCDFjfL7rIz70zvTM+0995Vd3n31mszQ39kRmZEZL5leqq7enre3/NUpYuMjIzMNz7x/YVJSwhBwxrWsAffhocPtgHdQI+xVNezQFr5yxjb6v4M4AIV/6+srFfq7C8Ak/7fRNwynx8p3LNMaFjDGrYqsxqQb1jD1t+Ghw92ANv8v+3K+iDQiwfw1LolcHVWwoP+OHAduAxc8peXgcv5/MjC+iWvYQ376FgD8g1r2H2y4eGDW4ADwFZ0kG8DOtYvZetiE4TQVysAx/P5kdvrmbCGNexhsgbkG9awNbbh4YM54FHgMeXvUaBtPdP1IbJx4DhwTPk7lc+PFNc1VQ1r2IfQGpBvWMM+gA0PHxwCnkMH+lbAWsdkPYzmABfQwX8onx+5s66paljDHnBrQL5hDVuhDQ8fTOBB/EX/7+N4beYNWz+7ALyp/J3O50cahVrDGuZbA/INa1gd893uLxBC/QWgdV0T1bDlbAp4ixD67zTc/A37KFsD8g1rmG/DwwezwMvAtwMv4bWjJ9YzTQ37wFYFDgNfB/4AeDOfH6mub5Ia1rD7Zw3IN+wjbcPDB3cBr/l/LwNN65qght1rmwdeB34f+P18fuTGOqenYQ27p9aAfMM+UjY8fLAZD+YS7DvWNUENW287gQ984I2Gym/Yw2YNyDfsobfh4YObgc/iQf0lGmq9YfE2D3wFD/i/m8+PjK9zehrWsA9sDcg37KG04eGDG4HvAT4HfIyHfEibbdukUgmSyQTJVJJkKkki6S2TySTJZIKE3OevJ5MJBOBUa9Rqjras1mo4tSpO1aFadXBqNWr+8Vqthus+9OWGA/wJ8Jt4wJ9a5/Q0rGF3ZQ3IN+yhseHhg/14iv1zeMPb7PVN0QezdDpJW2cbnd2dtLS10Nyapbm1mWxzlmxzE9mWLJlsE03NTaQzaVLpFMl0iriftPo7F8q/cD1YxJzvhRNCUKtUqZYrVEplSotlSqUSxcUSpUKRovK3MDvPzPgUi4slajVnTfNlHayK147/W8B/yudHZtY5PQ1r2IqtAfmGfahtePhgDx7Yvxf4JB+i3vCpVIKW1izdfT109nXS1dNFR3c77V3ttHXkyLY2k0yp09ULDcBxgA6O1fldy/0i3KHFJbSw2pnKOTKc0M4xKxLCFVRKJYoLiyzMzDM7Pcfc9ByzUzPMTkwzMzlLqVjCcdzYtD6gVgH+EA/4/zmfH5lf5/Q0rGFLWgPyDfvQ2fDwQRv4NuDzwHcDyfVN0dKWSiXp6mln45ZNDAz207exl67eTlpyLaSbmsAyIG38JlWYB3itA3szqCAmLoXMEVWvH465lojZb1Q+jIpDXY+B61IulijOF5ienGZidJLxW3cYv3mH+dmFDwP8i8BvAL+Uz4+8vd6JaVjD4qwB+YZ9aMyfQvYH/L/N65yciFmWRWuumU1bNrBhaAP9G/voHeimo6eTTNbr61dXHWt75YqI2aevrETZm79xLdZ6sK/rwhcx+8K4YlX9EvHGh4XS4iJzEzNMjk0xeXuc8VtjTIyOU1ws8YDaCeCXgS812u8b9iBZA/INe6BtePhgCk+tfx74Vh6gdvZ0OsmmLRvYtmcrQ9s3MzDYT2tHDssykijqwHwZ4JsK3mSqurJa2Md5Dlbuwr8L0AeHRUycRiVEq3iE13Idh8LsPGM3xrh97SY3L91g8s7Eg6b4S8Dv4Kn7r61zWhrWsAbkG/Zg2vDwwd14YP+fgb51Tg4AubZmtu7awrbdW9m0bSP9G/pIZzOxYeM6r6mr0TZ0PWwMO+se0/YvB3vDhV/Xa7Csqq/v3l/OfS+UE+IqBGpyTNAHcfk7KsUiE7fGuH1tlNErN7lzfZRSqcIDYufx1P0XGx/Sadh6WQPyDXtgbHj4oAV8J/C38CasWVdrb29lz+O72b5nK4NbN9LZ04WdTEAcJCMbccdi3OYxP79Y4McpczO+OgGXg72ICajCPw7mcS721YBevS8T5LHxLgF6tdLg1GrMjE9y58Ydbl64yo0L1x4E6FeB/wL8i3x+5M31TkzDPlrWgHzD1t2Ghw82AX8JD+571isdiUSCLTsHOfDEXrbv20b/xj7sRHxn/eivJgq9OkejAK4T6fK92yNHlGP1YV+tOVQqVcqVKuVylaamNO1tLfWbB1T1ry2EAeKVgT68t3sDevV4rVpl/OYdbly4xtUzl5gam1zvMf5vAz+LN/b+Qz+2sGEPvjUg37B1M3/429/w/3rXIw3tHa3sf3Ivux/ZxZadm2nJmR+Zi4NbvEV0egR6argl4l1S3S8Ne9dxKVeqlMpVKpUa5XIlgHm54u2TbdiqWs9kUvT1dtDX00HKH7YXcesbDf/1QK/fwipBb4RRL7sa0KvpFUo8hZk5Rq/e4PrZK1y/cJVSscw62SXgXwK/ks+PFNYrEQ17+K0B+Ybdd/Pb2/8mXnt79n5e27YtBrdu5PHnHmXnfk+tW3aCSqVKoVimVKpQqVTZsrlfmyIvVrkvA36h/4sNG2BoBereFS7lcs0HdoWSBHe5SskHueM4MfBfKoEGwC3obG+lt7eTro5WwNLd+gbJTdCrUQltW4V71OthdsiLCxPGolQsVgB6LS+U85xqjcnbY9y4cI3LJ84xNT7NOtg08AvASD4/cns9EtCwh9sakG/YfbPh4YMfB/534Lu4j73kLcticMsAjz7/GNt2byHT2sJisUKxWKJYqlAsVRBu2ENbAHt2bGKgvysaWR11HgVfzFGhh413TXtQWlgoMj2zwHyhSKXsAbxSqxlhjWvHxrdUCP2A2Rs+nU7S291BX287mUxaDy7i7re++94EvXq9MOzagj5OzWvhlGOu6zJ7Z5zLpy9x6fhZZiZnuc9WAX4d+Ll8fuTE/b54wx5ea0C+YffchocPvgj8NPAt9+ualmWxYbCPp4efYP/ju5kp17g1OhkbNu43kEonee7JPSQS8XWR5RhrtmFHghvQK5WrTM8sMD07z/RMgZrj1E2bjKRac6k6jresudQch2rNwXEFrhAIV+DKdeGr12Bb7vO8GxZgWxZYlrdtgW15+yzLprs7x+ZNvQz0dZBKJLAtSFgWtu3ltXbncaBXVL92GysEfWyFZQWu+6XUvHleUHlwXaZvj3P51EUunTjL3PR9ndROAL8L/GQD9g1bC2tAvmH3zIaHDz4H/EO82enuuVkWDGzs5amPPcGBp/bSt6EPLLh4ZZQboxN64Dj4Gr+FwY097Ni2QTle/9r1oK9pY2W/47jMzC0wNbPA9MwCRaVtuOa4lCo1768cLiXUazWXmuPisdUDtLD8L/BYy3+JJ3IbgbL1/kngebtd7b5TqSRDg31sHeonl2sJ6GzbFrbl/SVsSNgWyYRNMmGRtC1SCa8CEYAc5ZpGc8CagV6rQNQDfXi/cc0PruMwNTrGlVMXuHzyHPOz96353MWbOven8vmRs/frog17+KwB+YatuQ0PH3wSD+7feT+u15RN88zHn+TZF59k45aNWHaIufMXR7l1Z6L+yUuocMuyeOaJ3TRn03XPqffrETEB5heKTM/MMzW7wOTkHHOLFYo+xMsK0KWKl+ax0YO5ZUmsY4Tx9wWHwgtroWPSbt6DcMP9oUci9AZ4dQFBZ0eOLUP9DG7sJZmw64ISP3zCtkgkLFK2TULCP2mTTtrYlp5ny4Fer1StHvR6hUFV/eE9CyUdAG7NZez6Tc6/f4rLpy56/R/uvTnArwH/MJ8fuXQ/Ltiwh8sakG/Ymtnw8MFHgH8A/Dnu8addLcti687NDL/yHPuf2ENTNvqJ+HOXbnHrdryLPmLC3PR2dHXkeGz/tuix+l70wCqVGpPTc9y8M8P1W5NMzxUpFCsUShWqNVc7ST0vALklfLBb+kEZxjyJMNNVD4JlPgkj7eaIMq2CIqHu74i4w4X3mdtNG7rZMtRPR0cu9AyY5yzhmveAb5FOJgLwpxK+lwLjfBP0IkhRfDjlunFegeDcZUAfOh0EpYUCl09d4PzhE/erw14N+FXgp/P5kev344INezisAfmGfWAbHj64B/gpvC/B3dMOda2tWZ7/5DM8Pfw4A4MDsWEEgvOXRrl5ewkFH39iYMlkgp7uNnq7O+jqaGGpOosEguMIbk/Mce3WJJeuj3PrzgylcrWu2ldNVegq1C3LwvJhHwQ0zkOY7eL6LVmWikrjIKE6jz3frwG4AVgVZU+wivBhnmvJMrS5j02b+kgnk6sCvXncApIJi3TSJpNM0JROkEr64L8L0Nc7pt2ZAfp6HoHgvoXLxI3bXDhymsunzlOt1LjHVgG+APyTfH5k9F5frGEffmtAvmF3bcPDB7vwlPsPcw+/BGdZFrsPbGf4W55jz2M7SWe8qWQdx2WxVKZUrFAslSkWKyyWyhSLZSpVvbBdyVueTCTo6Wqjt6edzo6c1xEtODcag+MKJmcKXL05ycVr41y6Pk6tWlvRteR9WX4bumVZ2r4gEit0zscIel3BSzKq18DSVH2sGb3q49q+hWu2aYtYV7jry/69uwbZtXMzQgi/gmB2yDPHx6+sImBZkEnaNKUTZFIJmlK2l3cK7ePUeJBmjGvotQQl7BLpUisUynplscS1Mxc5994JJsw+IGtvBeCfAv88nx95YL/a07D1twbkG7ZqGx4+mAT+Op5677xX10mmErzwyaf5+Csv0L/Jm75+cnqeG6MTFAplKpVwulIBEZUbHqz/jidsX7H3tNPdkQva8yNnCKg5grnFCtduTXLx2hiXro2xWCxFwsWcqpnszW6C3fLvQeN30KPOaI/3ffXhtrZQLNxTH/ZC26W670MG+qD1g3qVAWXYoVAgKuCVl58i25T2wrlh+NAjwF2DXjsuIJ20aEp5Sj+TTpCUfTIM0C/dzq/kjqbmo+ky0+SHCM91BRM3bnH60FGun79yr2fYuwr8RD4/8pv38iIN+/BaA/INW5UNDx98Dfg5YO+9ukZLa5ZPfvuLPP/Jp2nraEMIwfjEDNduTrBQKC557krf5lxLlsENPfT1tmPbYacxNRYhoFR1uH57mvNXxrhyfZyJ6bkl6gyi7mag0AnhLmEvwS57y/u7lLZ5GZcah36peu3ucW58E/ba/fjwMrIihJjPddc/Ma69u6e7g+ef2edDUATqXoYJhvTJOAiP3y3o1fNSSYvmdJJsJkE2lQBLUehqHJGOeOb1lOsSU1GIpMnobIhgfnyas4ePc+HoGWrVe+rKfxP40Xx+5N17eZGGffisAfmGrciGhw/uxYP7a/fqGn0bunnlMy/xxHOPkM42IVyX0bFprt+coFha/fSjEQVtWfT1dDC4sZtca3PsOdWaS6nqUqy6VKoub75zmtPn6vdzWvrXIwK42+iq3d/Q4G6C3UJR+Ep4AYra17vayW2Zrno9CVTQm0VA7Ix0KiQD0IsQsH4gIeDpJ3cz0OdNJOQBXuBKGPpglOAPxuz7iVoV6GMgrbrZEV5+NKXtAPqphB3bzFDXba9cd8ke+0ZlQM1bAZQXClw8epqz7xxncZmK6gcwAXwR+DuN9vqGSWtAvmFL2vDwwU48t/xf5x60u1sW7Ni9lU995hPseXQndjKJ67jcvD3JjVsTlKvVMPBdvqqZdIpNG7rZONBNKql/cEaAB/WKQ6nq4riyoBa8cegUZy/cXNW1ArhKuFuGMrcsLKGDnXpgV9W/3CbcFkQnolG3g8rAEvkmUKAY7AzlsjxugksGcxXftsCbJe+Vl57GsiGYcMcP7AK4+MAXgeveFYSu/A8IelNZqx4EgFTCpjmTpDmTIJtOaMc0Na9CXjkW57Y3KxemmleVf61a4fqpi5z+5hGm712v/AXg/8abPa/RXv8RtwbkPxxm1VnK9ejIKd2Ecczcjpj/2dcfBv4REDO/6wczy7J45Mk9vPrdLzO0YxAsi1rN4cboJDduTlB1PNdm3T7ty4ArmUjQ3t7Cxv5uerpych6WwDywe3B3ldyQhf6fvnWC85dvrfraJtwlqC1FtWuKnRDsWni5Hu4OXfco7flCwj5IgZZAo0/aMrdjKtEQgEGzsqmeFZALYPvWAfbtHgqBLTznv4Cg857Xfg8CN2yv94+7iGDf3YHe7CVfv7NdwoaWphQtTUmaUrZyLAb0BsRX7rbX0yP3u47D2OVrHH/jMOO3xmKfyxrYFeDH8vmR37lXF2jYg28NyD9YpgLbitlWl+b6cmY+aKEsBZ4TVgDCd83/EvDxVcS/IrMs2PvITl777CsM7RhCcuvqjTGuXh/DUeaQX7EJyGYztLe10J5rpr2thZbmTCRYueqyWPH+3DrvvSsEX33jGJeuruJbIQID5FbYmU7ZhwJ3eQxffQcPWoI9Buoqx8PhdsEuX7nryl7dXBL2KqDU3Yqq9/InDKFB3g/28ouP0dySAaEMu/PDSAUvw8t2fekNCLch6JUfB2mU/QbM9eaDuLBh2vVx+h7wW7NJMskEWgVDy6P483XQm00gqkdDD++6LrcvXuXEG+8wudI5HVZv/wn4G/n8SJ1aa8MeZmtAfn3NqvNHzLq6NNdXaiJmPVj+7u++kfrX//o//Vi1Wvs/hRCZcH7ztXlHdu3bxnd89hW27dmmdRS7Mz7DKa3de/nrWZbN5o09dLS30JZrJpWKb0mQYC8uAXZpruvy+tePcPXG2ApS4KcDsGwD7mAAX68EyJvXO97JSWv084V/EVXtq3FEJrqLvBYruxMVaKqaDdCvzIKn5qOEnXChq6OVF57bH7wzEnbqO+S57P02elfWLKV6D9836dp3fFkfr8ZNlWwc05S4DvqlOuIlbJvWpiStTUkyUuFr+bTEsLpVqHlt3nzXYfTCZY5/49175cafBX4c+KV8fqRR6H+ErAH5+28W3oQxS4G9HtzjYM8S+6C+glfXxfd//z95dnR06v9zXXc/qAWzUvC64q6Av33XZl777KfZdWBHpLd3sVTh3SPnqTlLKXj9mslkksf3b6G9zfz2u2c1V7BYdiiUwzb2ldiRE5f45vvnl0yDGptlWdgWdZW7OgY+ovC9CIJ4wrCESl0Q7BdG+7yZj4H7Xkut+lLo+SCMFfWoax4kVOP+hjYUTr4Tj+3fxsYNPcFZUqEHU+EGMA/jC9vtlc54vhvAxVf8wlVc+PGgXwq6QShDRcc1BZjnpxI2bc0pctkUtj/3rgnxuGF1WsUiTIHWn8FMr0Agai43z1/k5Bvv3qsv4f0p8Nfy+ZFz9yLyhj141oD8/bF6YF+Jgl9Oxa9E0dcDvfgP/+HrLV/84h/9X+Vy5YeEELamPoRRCPnQl7DXCv4YG9q2kc98z6vsfmxX2AEN1AZkRu9McWdilkKhSLnebGHKHWZSKZ54ZButLfpn6IXwhrstlF3K1ZW7/DXntIBTZ6/x9ntnqdXqxxGqd4/Ctj8u2zZBH6fwg20vJr2TndmbXnkB1J71huseopCX97PU26Gy3MwHuQgBqEJeBTcgBIlEgk+99CS2bYXHlPcnBLhH67DXPZHOeAJZQTB64QfgVysffupEeD9Lqet41a+Aus75lgXNmSRtzSmy6bDz5mrc9tFwRMLJMK7jcP30BU69eZi5mbl6j/BurYT3bYmfyedH7vkUfQ1bX2tA/t7ZSsG+Wlf93brshbn+1/7az3765s2Jn3Fdd7PpltdmNFMLS82F73/K1FD43b2d/Nnve41Hntrnfao1Zqx2xCyoVh0KhRKFRe9voVBioViiVvM+BNKSzfDEgR00NaWC06qOYLHisFhe3h2/0ld9dm6RP3nzKGMTipLyz9XVewhwO4Ct909T+KjA927W9uOKuOJVN7zaWU/NKiU/9Q530azWZ7wzbkbZEnU8HqFiD6V0pPIHDG3qYd+erd45/olBhzs/kAR4sI2I74znr7tueL5QwrvCfFfrK+ilj4kVna9VFIQgmbBpy6bINSdJ2HYkLjO8ktKYZpGlz3WcGlePneX4G+9QLq5+GOkydgT4fD4/cnitI27Yg2MNyK+9qWBfK8jHgX01gJcmAH7nd77R8eu//pV/XCpVPms+f1OhhwWQul934cvtVCrBq9/9Mp/41o+RadI7vkUSay1/C/JoqVylsFgil8sG86EXK547vryE4r5bk/f2/olLHD56AVeIiDKXngnbcNWrFQBAqQgoCt+ntm1Jl7xSAbD0yoBeCRDetrDqwz0G8vinRtGuboX00dWnCisDiko7/QvP7qctl1VgLc8hcLmHeRuqV+9PATv6uxZ2ygvb7V3Xf/cUN7uuoPVrB3dngreOio49PxbI0NyUoC2bpqUpWddtr//GYjrhGfHGnVtZLHLm7fc4/94p3LvpnFrfHOBfAH8/nx+5ZwP4G7Z+1oD82pkE+geF/ErUvLkety1Ne8A//uO/+OK5czf/X9d1B0zl7i1NlRQWuHHAF8LrMPb0C4/x7X/+Fdq7201+KKmLT2JE6Nd5JYWAQtlhobK6tva6psEsPr6JqTm++o1jzMwXYtW7Cm4V/BListMdKMpe2bb9fFHBrvbAR9kHejZahEq9nrPEbLuP/txFZE1V9UsD0yNRrrWZjz23P3CxS4gJPy4ZWwBsPxFqr3rhun74EG5BOEXtu76HIOiBL+c1MNK0JKiVYxHQKxWHuu37Jszxxt93tKbINaW8h1EnfvMaS6U1bv/8xBTHvvY2o5dvmA/yg9pp4Pvy+ZEjax1xw9bXGpD/4GbCfSWQV/dTJwwrWJrr9UxcuzaW+smf/Lc/MTOz8Hl5Tmy7ZATw9eEOsGX7Jj7zvd/K0PbNfkJ0XIqY5NUFUlzCAceFhbJDoewgxN25MOJs2Tffv1bNdXjn/fOcPHs1gHk4JC7GfU8Ib9untwwDQgG9WmlQFbuxrrXHh/HIcN4yvB8tf+MyOwb0ajHgBuQhojaFEl5u7989xOCm3kCNq8eD6W9BOyfsTa9UCmSvexEO1QvVu95+L4fYaaCPVELiQR0gNjaMep9LnF8H4rYF7S1p2pvTJGyi4ZU4Zbxqnob76qXPu/c7l65x7GtvMTe1pu31FeDvAj/b6IH/8FgD8ndv9eC+HORX0rP+btV8rH3hC7+386tfPfIvq9XaPogv1KSZil4HfHi8ra2F1z77Co8+fQA7YQep0RSLkkjzLVtJwquOYL7kUKy4MSfU8QoY2/XfbrHkphqhx2OL0TuTfOOtUxQWSx607bDN3fYle5x73lT4Fha2LRCGajdVfjAzntKpLnDjq2kz71tts697//otS4B468b7EYRRwOPDJmHbvDT8mNf3AglrHXxqb3kgUPdB57tA2evvmQScq1QIXOHFb7bnu9ITEIG58U7HKP2VhIkofS28ek1/hwVt2TQdLSlvKl3lnLp5HOMxibuu3OfUqlw5eppT+feolMOPNa2BfQX4S41x9Q+HNSC/ejOBvhTcVwr55UC/1HJJ+7Ef+4Xvv3lz4seFEE2xLsNgn8AEvAynAj+RSPDSt77Ai688T1M2bHePU+yKZvISrBS8dc2CUlWwUHIo1+JjvZcWpFV+5U1A0E4OVKo13n7vLJeujGoKXXPRexFove6xwBIWXj+t6Cx48kZty1D5Qcr0CoGQ50klb4W0UDvbLdfn0QS59h7IPa56zFtz/QMb+rs5sHdLALHguApbRZGq8A4VuwS/7gEQSsc7V8bjT3+rvpOuCN344fXqw1zWZ7RjQq+Y3K3bPu7aLU1JOloyNKXtGGir+V9fzUcqAMrvtrSwyOn8u1w+fo41LM+n8IbaNWbL+5BbA/KrM5t4sK9E1S8H+TjQE7NOzHpk++tfP9r9pS/98T8uFEovqfvjOtp5y7jCTFfvQ9s28l2f+zb6NvRKeeuFVxJwe3yG/t4O5QJhiLCgDU8oFqtMzSwwMzuPg82GjQNUHSWNdSD1QeFf960XIRg9ACtXsvDwKeDajXHeeu8MlXJN63Sn96yX54fHArj711Fd9X6NIoT7Mm58/DBhfoggjJrmlWSG3sVBqeChPEINlh6EnnliN+1tLV4c/rnymPDPCWe+CwGuKXj5wZsA/EplwFft0iWvznMfxOe/p2p7fQDeZSBZ3y2uQDxybHm3fVwnvGwmSXcuTSaVMDwncWkJ98Wq+eD0MI7J6zd5//U3mZta0/H1v4z3dbvCWkbasPtnDcivzFTVvpSKX85lv9qx8itR8ZFi/J//89/65PHjl/6h47hdoEA1sJjCzHAlhkoJMpkkr373yzz5/KPYSX2MsAqcxWKFw0cv8NRjO2jWVL7/zxKUSjWmZxeYmllgemaBYrnC9FyRS7emaW1t5c++9nz8TS2x94NbeO8a0zWASuiHaSmVyuQPn+HW6KQ2VM4OwBx134PqtjfC+QmwFNjLdMg6ldoGH/ao1/Ml2PSfz0p+4cqn4RVVar4numJtzmZ44dl9GuACsCmKPWxbD8GoKnMJtRDa8tq+Sg8qCz7MzUpAzDFtStxYiOvt83EKe0WVBC28Gq96rrotaM4k6c5lSCfV+fLj0rKyNKjrtWqV84eOcPadY2vZC/88Xqe8xmdsP4TWgPzyZrM84NcS8vUU/LKAn5lZSP70T3/pb46Pz/7FpW4orl3RP6K5QQH2HNjOq3/mW+jo7ghoISB0D/vnW1hcuDLK6J0perrb2bdj0DumpHB6tsCRExeDAn16ocilG9NMz3sjdzLpNH/1+16NpHc5l/Nam97pTfj7goM6aIELl2/x7rGLOI6jqHof6NqEOAqwbSu4TnDcu2AAcAsv/zS3vgZ5pRKi5VFIeXX3imAvVOgoY+QgcKFL27FtI0ODvRHVKbdDZe7HJrf9C2kz3Em4+xUC2clO4OK6aOpfwlyq/nCuewP0rp8qEd57nBpWARqnyGUWrNRtH1cB0NLhh2/JpuhuTZNK2spvS6k0ttZDUQAAIABJREFUqPsiFRVVzavp9ALOjU1w5CtvMjk6Hv+gV29V4G/n8yM/t1YRNuz+WAPy9c1U7ybU48C+VpBfCu6xkD906HT3F7/4R/9PqVR+ot4jNXv1RveFLvqW1izf9mdeZs+ju7Bs72voS70pVcfh3ffO4/gdoJ56bAfN/lh54af0yPFLzMwVmFsoc2l0munZovYxEiEEf/lzn6KluanudSK8X20NwMgcuRW2f5vTx6J5LEJFHSbIAhYWSrx1+AwTk3P13ffIbV3ZW2o4LCU9oeKXlQvLUq/ryXS1Td/MJbWtXr3faL6Eq+a0tqa6Fa53zRdfeISk79kJKof+RjghjoQTwbultr0LCDvqQeRLdJEe9xGYK++PPNcA/crc9uoxvfKr3ldYWQjPXzJeowIQrQhAazZJd2uGVNLWIL/a6wRPzQ/jOg5Xj5ziZP4wteqaTWz3m8BfbbjvPzzWgHy8LQf31bjs1xLysYD/tV97/bFvfOP4P3Ucp8e8EfPxhs87piDz1x9/eh8vfdswLblmJaRqVrDTg7+gVKly+OiFoBDv7W5nz85NnuoXMD2zwFvvn+fSzRkmZxe1Alpe2xWCl4cfZdfWDaRSiRUBfLUiP+5tVzu4WT7VVXWstocH0BX46yIIIwScPn+DE2euIvw4dNgbat7SO9rJbXkts21eTW+QVvQXRAuw5F0bIZQgAZRiVCx4z6m3u51H9m0NnqGMQ4aV74FU2+HxUO3rHfEMaKugF0LzCshtsze+N/Vt2IYvINhXv+197dz2GswhkjexsFbWc9kU3bkMyYSlPw9RL/111Lzx3Iozsxz7k7e4ffXmsu/BCu0E8Ofy+ZELaxVhw+6dNSAfNRPmcX/LKfq1gPyK1PzP/MxvfvbChZs/KgTxn2FDL6DN/Woh1dqa5Tu+59Ns9T8Bq7raAzmuJECN1XVd3jp8NijgEJ6azzZlcIXgv3zlCCcvjAaKS1XvQRurX9C7rqApkyLXmqWttZnWlibaWrPkWpvJ5bLkWrNkm9L1bndVpmWuOvTMVO4iDBhR80FcXsDZuQL5d06zsFgKYF1vohxQOuX5abDUbdRjyrWUWohWCYm7QdXq/NyFciCsB4ZAC7DhA+exA9vobM8pytZw1cuwIoRRVMnrx1wlDgFBj/sozMPtYKpbP7yrqPcA9PJbC8q9LQfxqOKOVnjqgTvuXK0CEFPZkPstCzpb0nTkMsGjW62aD/Yr13Rdwc1TZzn2tUNrpepngf8pnx/5r2sRWcPunTUgH1qcWl9LRX83kK8Hd2tyci79cz/3H358amr+21dyc6Y7z1uG27v2buXT3/1JWlpbtALfnPvchDvK9qHD53BcJyiA+rrb2TTYz/mrE/z+V9/XYG5CHhEW0mrBXs+SiQS51mz419JMrrWJrZv7aMqsrAJgWUqbtdG+bRGC3Yo7LtNmyVwK1bwFHD5+kUtXR7Wx8Zob34C7HG6ndbJTwK/mvxVIfC3pAeQ1rsdwHwjVX7AZB3lv4RrvTiad4vmn93qVQBHCp76i93aGbfPqtjlHvQSzDnr1ndHfI/8rdQLtWBzog/fMT08sKOsqfhXW9SoMBrTV9bjrxqh5CeWEbdPTliGXTS2TNuW8Or9xNS2FqWkO/8HXmRlbk2/XC+CngX+Qz4+s/fzSDVsTa0Des5UAfjnoLwf7pSAv9xNzDHP97bdPD/zWb33tH5XLlV2RG/FL/LjnahZKACKR4hOvPMMTzx4gmUjUVX4WRJS9sMJjAO8du0SpXEEAM/Mlro7O8urLT/L1t04yPjWnfL1OKdCl0lCA7xpKfzX2+P5tfPJjB5YNJ+8ngKYwAK90LAzc4gr0BRjj2JW8sODwsYtcvnbHb2cPwWx+b16Du1oZILyACnrteHBBI51xNbElrN6HfTS4KZWCrZv72DzYF1WmEpYKwAW6210CTn/+SqUPwiluA4grgF4C9K4SXn7IJqgAEFYi4yG+Orf9vVDzYTzh8WwqQa7JJmlDNpsBWeleqZpXn4N/fadW40z+MBffO7H0i7Fy+zLw/fn8yPRaRdiwtbMG5L1iMsEHA/zddMKrt4+YMHIf//7ff/XZt9469fdc181pN1Gn/dp8vup2V08H2596hKZs2suEZIJMKkkmkyKTSdOea6Ylm4kqdwl3AVILW8Dxs1cZn5znyugMU3NFhID2tmamZxaUgl1CXQH5Ctz3y5uXjmTC5i9/7hVamvUP5Kj3rcM8RsETVgC83VJdCw3wXsVAmZxGxiEEh49f4sq1O2Ch97T3Iw3a3k24m8Pp1DQqyj+EvOdrEea9aLmi5ENMzsV2yIw55vod7p57cjfpTCoCMxk0bIPXoaV+XjZ4H9RwKrTR3wG5JHhPMN4TEAbovcqirvTVMOr96RBmeVhr5+rxBPv8nDTBHQdoud8VgsVCibm5BebmCszPF6hWa3S3NzO0oZOuzhxtbS00ZZuWVPMyvjAtRroEjF25zvt/9KdUiqWYt2LVdhH48/n8yLG1iKxha2cfdcgvBfE48NdT/Mup+DjgL6Xsidv+V//qdz538eKtz1v1iL4Ck8/7kaf389izB7h2awL1FdAKfKC7M8emgW6vI5xmVgg24Smlb7xzniNnriuuUbW9NQQ4SgEdtq/qc5GHBbi1CtB79uQj23jphXg1r8IcQkUd7BMiclw9D0GkvV5ZBJWA9497Sl79KE30e/PeGd7CGE6ngF1NIxi9/80EEKYvSL+RdUa1L/b5i3BHuLBgY38327YMREDoKhDTAe4FUMetR4bWKdPgRkDvRs9ZyWQ4QThXQt8Hvet90156F+oq4jpqezlYyzxcsoKgxOO4LvPzBebmCgHUHccNjqtm2xYbenL0dbaQTqfI5VrI5VpIZ9J1r2/ek1qREQLKhQJH/vgNxq+uyQdvinjT4f7HtYisYWtjH2XIrwTq97IT3lKQ10BfqdQSP/uzv/UjY2MznwFdta8U9/IxpzNpXvmul9i2Y5DL1+4wO78YBBBGWPlu2LbNQF8H/b0d2LYtOeiFAaqOy1xJ8M33z3Hu8migKGSBG6ovpXOVK2JAbw6TUtWYqPvNc9OSSZu/8j++4k3IY5xi9qSPqHZjHSwN/DrgQ4Bq7n3gvROXuHz1jq/e63+wpp5ql+PrUVQ+RJ99AHFLAl95OCiVAMO0bFHkuwSBWiykUgkG+roY6OskFczWFqMQA+Uuj4fAVtW+0FS6d+Fw1js3qDQGs9wp7ereNgjc+IlylCF48ut26rS3cu57x7+OCXFTWdeDtczD5c7V9wtqVYfZuQJzs/PMzhVYWFisW4GNA70AmptSbBnooDmTAgTpTJrWXDO5XCupVDKi5peCPH4eXTx6irPf+CZrwAMB/EQ+P/IzHzSihq2NfVQhvxK4Lwf9OFW/UkW/EshbAOPjM80///P/5e/MzRWeUVUc6AW+tx2uxz3WDZsHeOU7P0Fra5Zq1eHU+euaqgCjcFNMCEinkuzfs5mEHX5wY6EsKFW9EvrE2Wv+8DEV2rJAD5WaOTZeDae77/3zXHAU6K/EnnpsB594fn8kf3SAG/vQjwv0Xu2hmhchlFFOEjKs4P3jl7hyYwzbrx2Yih7Qetp7Sy8yE+76ZDgE1w6WQTpCr4N8gto7EpN1wjigQivX2syGgS66O9qwbFOJhs8OwndI3R9XCXDNMD6Q1UpA4JYnHvQCfIi74cQ5QuAExxTQxyl9V2/D1+873m3/QdR8zalx9eodpmfmKBSKOkijHFcOxUMewBLQ393Khp5WbCvsHJvJpGlubaYpnSLdlCaZSOjeAwzAK+meHp/i/S//CcXZNfmy3f8P/Eg+P+KsRWQNu3v7KEJ+KZDfrZpfaSe8pdYjf2fPXu/+tV97/adKpcr2qJKrD3jV5ON94vlHeWb4MWzvCymMjc9we3w6Eg7CwkDb75c3Tzy6DduyKNdgoVRTRSAXL9/m8PEL8eOXhcAVltb+3tnRwtCGHi5dv8PUTCGoDAQTmywH+iW8GKlkgh/4C58OhtrpsLZi9qF0xBNKKAX+WgVLjUko+zw7cuIyV67f0cAegNkHd9xHbSxE2ElPrWTEtM/LcfvBtZWekV4lRUT7asSBXnn4lm3R09XGhv4uWpqbFGVqwE1RrZpajVG/quve7Pim9rjXO9IJ5bxQ9bsidLULQ7W7rhumyT/mueujoHdcPa545R2uo6RDzTOzAqCdKwSTU3OcO3+NcrmigVbP/+gzCR+XDnqhHySTSrBzcyd9na04rqDquNQcl1rNc/knEgnSaa+PTSaTJpVOkUqnpM8nUjmplMscff1Nxi5eqZ+olduXgc/l8yMLaxFZw+7OPmqQtwhBvhLArwT6Syl6E/Yrhnw+f3Lbl7986Cer1Vq3quC1TljBLdW3VDrFt7z2IkPbN2n7z5y/QbVWUxSJt19YQulQpxdYtm3z6P5tLJQdqjWtKoAQ8O7RC1y+dkdvf8eK6S0vOLB7iMcf3YbtNUwzNVPg0pVRLly5zcxcwZj0RPiVg9Up+mcf38nHn9unKGMDzoZC15S8ctA7LpRj+smad0AIHNflG2+fYna+oPWmD1z1WGB714gbIy//5HM3Z8GzLHld3bODHwdCv7dlc0pAOp1koK+T3p4OwyUfwswEvQ49H+p+OBPmErze0xd6j3sJdf8Erb1dUfrBsDrZ1o4J+rB5RwiBE1QcjEpi8C4alYZl1boKc7VCE60g1GoO5y9c5/YdZahaUCE2nojOcQByzWm62rJ0tDXRmcvS3Zal0//rkstclq72LK3Z6JBRATiOS7XmUKk6VGreerXqUK251FwXxwXHFSxWHGYKFSbnK0zNlZhdrHDxvZOce+udFTeRLWHvA9/Z+Gzt+tlHCfIq4E2A11tfK0Uft6wL+f/2395+8s03T/6EECKrwjy+XbY+5Dt7OnjlMx+nrV12xPfCzhcWuXJ9zNslS2WfBvJt0BW8V3DNF6s8cmCHoTwsqo7DO++f48btKd1VqygoWcC3NKcZfmYfvT0dSvyAFQJjfGqeC1dGuXD5NnPzixrotbnK3aU/RZtKJfn8971KtikVhot0tJPw9DbqVgLk0qwYyKzz43Acb1KgianZAOyqq14qeF2Vq3BXge/PUqClOabCh1Tuqv9BDVPnNy4g19bMQG8HXR25IBLVxe4to6BXK38ScMG2hDv6uwDKrHbKMf1rdOqkOH4FABF2wAvgHu185wpZGQgrDvrQzPC9CSoNInTzr1TNE9yvXpGR+ycmZzh77irlcjU2z2X+tmbT7Nzcze6hbnZt7mb7pk56O5rpyGW9+ew/oMl0aZVsw0PmuvooBYBKzWVqvsTonRnef+M9rly6xc2bE4yPz3KXvLgOfCafHzn+gW+qYau2jxLkE8Sr+KUAv1rgxwF+VZD/9V//yqsnT175ISARdKwiBIFnuls2znbt384LLz1FIpX0zwgDXr05xrz/UZig7RYQwgr3KAVcteZy/fYsdjLJx5/fr12/WKqQf/cM07ML4PeE15SbotS2DfXzzOM7w/nOZSRaYerj1vIKzFu3J/namyeYnitEQC8Lr6Vs04Zunnl8BzuG+v3ObLonxDLWhaqcwYdsqIjDCoGSo77Kdx3BW4fPMDY5p/ekt4jdRi6JgzvBcbWFRnPhqwo/rn+GwHhBvLyyLZue7jYG+jpoVoZiBSGWAb0GOAljeb4BcA/a3jH1c7H4Kj1U7iHETdBLeOug11W72s/D7NCpdbyTFQAF9M4q1LxayZH5Io9XqjXOnb/OnTtTQV7LfOnvavVh3sWuzd3sGupmY482CnZNTf7+YjuwCjmCRa0gyd+WOhLBC1spFKmUy5TLVW7dmuTmzQkuXLjJsWOXmJ1d8RT2c8D/kM+P/NE9u+mGxdpHBfIq4FV4342aN49ZdcKtGvK/9Ev/7XNXrtz+XlX5xRXk9Vz1lmVhJ2yef+kpdu3b5ofQ5Wit5nD24s2w0FJMfRVkAV0oVrl6e4ZqzaW3u53nntzlK0aYnivw1rtnKZWrQQFuDocTwuuw98wTOxnc2K17AQQILH/mNAgKRhEcDlyfbx0+y9HTV3FdV1MmS4Hetj3VbFsWuVyWR/cMcWDvZlqam0LXvKLoJUQjLnglt+XAQcMrjut6gB+fmNWBbofQNr8zHyp7L/YA+LYO8tClLwJVH+llbzxnSz3oZ08mk6K/t4O+nnaSCTvqtUGHnPo8NAAGz06FXLiuAlJtc5fxB+reB3d03Hz0PVKHWi4F+gjgDZiZbn3Nhe/qadQUfB3VLvePjU9z5txVXMdl60AHu4Z8mG/uZufmLtpa9Hkb7qVFZpZ0w0qTWlGOQl9X+0JAMul13FucW6C4sBj8/ryly4ULtzhy5AJHj15ibm5Z4NeAH8znR3713udCw6R9FCBvAn01sL9b9/1KFL22/ou/+F//0o0bE9+ltcVGQB/vsve2oSXXwkuvvkB3X5d+TFkfn5xjbHLG21ALd/9fUIwLGJsuMDZdCHo0bxro4vEDXuVh9M4Mh49fpFZztELZVFb9fR08+8Quspm0og4VisqCM1gPJ5zx9oU9h6/fmuQrbxxldn4xBH0ACf09tiywbRtbqmHbawtPJhJs39LPo3u3sGlDVwTmqrL3mRrmoaXmpYSuD/h3zzI2OYOtPDtt+JzW0c5U7saScBlCPeb5izBNkYlw/JVEwqazvZXurjY62lowLYBZ3LZW2TIqYAqQTeCrrnlV+WpwN9bNiW7CMPGgD9vUjbkXJMSV9VChRl3UbmQ7WtGRlZw4Ne+6DsnKIrs3tfHkng1s39RJKmnOKXH/TM4poc4toXrTwgqy4tEQQjkW5kUimQRC71ylWGJ+agbHcb2x/EIE0Hccl8uXRzl27DInT15mbm6xXhIF8L/m8yP/+j5lyUfeHnbIx4F8pbCvB/iloB+n6uNUfLB0XWH/4i/+1x8YHZ36VhPsJgA8M9W8d7y3v4tPvPoCTc3ZMJQF1UqNYqlMsVylVKpQLFV0RaasyEKt5rjcGJtjoVgJC2oBW4f62LdzkEvXxjh57ppX+AUuWL3dz07aPLZ3CzvkxCnq9YJr6fu1ioZMk6XsA8rVGt94+xQnzl7TCy1XaPEnJGBtS1P0Erq2bdHZ3soje4bYvWMjmXQyBL4ihy0/ZTr4Q6A6rsvbh88yNjHjVSQsVbmbbfHeg4tz36vnBG55w3sTehkstBEAlp4my7bobGsJwG4nbC3T1fwNV1XVSgAyNe9jAY8J+xCKZoc7hF8hM4bMqaA3lb7pvpewlhWCiEIFUDrZecc897OjqH/PCySnvPXOdQw1r96rmh+duQw7BlrZOZBjqLeZTGSiqPUxFe7qUghvMKjeFh+v5CX4E8kElmUpYbxlrVJldnySaqXq5aHjIoSL4whN5V+9eoeTJ69w+vQ15udjgf+38/mRf3a/8+ijaA8z5C3i2+HXCvZ3q+iDZa3mJL7whd/74bGxmZfqg90s7E0FbzG4dSMfe/lpsCyKxQqlcoVSqeK70ZXvRgRu8Shw5dSohVKVG2NzgUIPpyiF3ds30tnRSv7ds56qUQoArzD01jvaWnjm8Z20tmSN6wgtHbLbnA78EETaq+mHlwXu5etjvP7GUeYXSkHhJZWLbSlg96eStW0by/I6Oqgw7shl+cwrT9OWy2r5qyp3mW2qyg8Bf46x8enQLa9B3Iqo9gDuHvH94zGKHisG7OEzt6wwjfKe29ta6O7M0dnR6o2PFmHlxPyZC2WnVqlCaOHjgC8i2+E5Orx1SKqzIGpfnAv2hUq9Hui9SmR0NjwT9OaIjsDjE9N506sACE31y3QKAemkxVBvCzsGcuwYaKWzdW2+gLiWpoLd8e/dXHrQjsLdVPJ2wsa2ExHvhjzPqdWYvDNFueZgVSsIx9UAL4Sn7OX29etjHD9+mZMnr+A42ndsfjqfH/n765VnHxV7mCGfYHWQX8vOeKaij8C+XK4mv/CF3/sbU1PzL+gFfDzU41z1lmWxa982nnjuEcrVGjdHJzWoB49WrxfohCcspMdnFpmYKRjtpwIhvCrAgT1DTE3Pc2N0KlBSXuEbFtQW8K0vP0lTOrWkgg/AYukK3iIERBDeMioC/nqxXOWr+eOcOX/TV2EeGeyE4aa3VODLCgD0drXzHa88Rdafu19T8hYEDQWWeixM/1vvnuHOWAh4CXBPjes96zXYK5UMkOkM3e5xbvtw7DyaC7+ttZnurhxdnbnQTSyMhCoVJ3VF7o14VbRnFK/aNZVvwN1xXUbvTDE2McveXZtJp5Ia9MP3JjwnfI8IoKNVAKT6D2Adtq0DuhI1QaZWADTAeWkFX/nLyXNcQW97E9v6W9gx0MpgdzMJ2/wRPRgm80gFuhvM8qfM9uffl+zvEdcmL4T/viXsoFIWl5+OK1gsFJmfK1AqVUjUKgjHUdrqhbHuLefmCnzzm2dM2P9cPj/yY+uYhQ+9PayQV8G9FNTXqjPeSqFvAXaxWE7/8i9/+X+bnl54MvxoSag4gbqwV9cff/YAex7ZwWKxzK07Uyse0xoKaq8ArTkutybmKRSrEfdkUAgLwaN7t3D87DVqjhu4XiXsZcE8tLGXJx/ZZtQjrKAgD/+rQAkVurBCwst91ZpDIpkIoaLFITh/eZTXv3GMQqGs5KMBdgXutm2xaaCbb3/5SdLphNEuHwJUgP65WUSgqgG+lj/O9OxCBPB6D3of8IYr3xxehzyXqLKH8BgWtDRn6enK0dPVRjqd1HNErdiZ8NaC6QfDSlW4P4C58iwCuCvvhxp2Ymqe6zfHKZc9d25TJsW+3UMkkrbmptfVstKD3gC9DKe2wct3L+4zsnqzkTcrnjp/vQc9EHjjxIUPw0wywWBvM9v6mtna10prU5IH3YJ7ctHB7ueHBL8rIOF7dmQRYdsYeR1W6qN9IPR8LZXK1KoOrnCZn1ukuFjCrlbAdXAcWWkI2+3N5fz8Iu+9d55Tp65K2P8i8L/k8yMPJYzW2x5GyFtAkqUhfz8742mAn59fbPqVX/mDvzU/v3hAV+9+YW7r21qbbBDG60E/uGUDhcUSt8emiX2OgZoLEOVvh2EXS1VuTc77M2T5hXygusJCf/OmHprSac5cuGG4W5X2VlfwyY89Eri+RSDTdShrYFcsVPChC/WbR89z4sx1dgwN8Oj+Ibo72ry0+ZHK+AqlCn/89SNcvHbHG7tuYSj3cLlj6wCvfvwxEglbU+lxit0yj4tw/52JGd5850wE8HLe+QD2S7jyQ5d91JsDBHFnmzL0drfR09lGU1MqSIvw02I+fkvmdZC5xOS3vicEffgM1HBqpSto5/ePzxdKXL8xznyhGILDh3BrS5bdOzcFbbyBOx4Ml7viKTDAYoJe60ymVAo8aHtviKPAXQhPrSO8SXJcV2BbsKWvhb0b29jS1xyMbvgwmKqqZX8C4dZ31wPY/hS3Mu8StqX9zuPBbrTJ12qUihXCbwQIFhaKLMwXSNSqiFotAnZV3cv2ewn7Q4dOc+nSKMCXgL/SmAZ37e1hhHw9wK9U3d+Nml/JsDprenq+5d/9uz/6sYWF4m4V7tECvr7LPpVK8eIrz9Ld18nCQok7EzPREh50MkEkjAAmZxb9T8KqblgMxSbYtmWA7UP9vPHOaQqL5aBAxShcu7tyvPDUnuAC4dA4PQnq/vB6KG313gx4X80fZ2p6ISzAhGBwoJvHDmxjszIcTygEO33hJl976wSlcjXWTf/o3iFeen5fCFIF2jIJaru8nAFQPS5PsAT86aGTTM3OK+D2O9ApPest9Zgv1U1XfgB7O1TuCdtmw0AnvV1ttDQ3KU/O8yYIQm+MvBUjy9VTwrxS9mlh5XugPBj5XniHve1rN8aZnl0g15KltTXL3Pwik9PzBsBDl7grBJ2duaATpgn6cPy8Am9vRzByIlTqGFAPO3vKuNQP0wQVgeC4S197E7s25Ng50EL6Aek0txqL61znStir+80OeEAymdTAbdveuxan4M1JiWqOS7FY0ke2+OEKhRLzswvYlTI4TsRVH+e+l8Pwbt6c5NCh08zPL/428Bfy+ZGYWYQadrf2sEHeZm0hv2ZqfmpqvuWLX/zDv724WN6qKju5DmjrcbDPNmd58ZXnaGtvZW5+kfGpWa2UNrmumhAhABzX5fbkAoulqsb+cMgTQcm+Y9tGNm/oZmp2gcPHLmpwD9tLvULgmSd20dfdFrShe9ezQuCjxI/qWQhVvRCCk+du8O7Ri1SdmlKY6EOAutpzPPHINnZu3YCttJdawPxiide/cZRrNyYC5WzbFs8/tZtnH98ZZpHlQTJc91akMlZVvCXTT+iut4DxqVny755VFLoCeKKAD932enu73tve279lcy+DG7qjD1O51xDsIsxvheBx0I+A3qgEhM9FwloPd/7SLeYXigFkAzc5pgoMlbUQgv6+TgY39oQqfgWgV+HttcmH4V2U+I0w+nA5aGlKsL2/hZ0DOdqaH3xXfD2Tv4FSpcbUbJGpuUWm5kpMzS1ScwTJhE3StkgmbBJJm6Rtk0jYJBNepTGTSdHakg28Wgm/uTCZsOso+NCrtrhYQp+nQq9ElIoVpqfmSFRKGuhNd70KeU/Vu94Hs05d5cyZa/+5VnO+J58fqa13Xj8s9jBBPs5NvxzY11LRy30RFT8/v5j91V/9g/+jUCjt1Ny2fvu77s6NqnjLglx7jhc/9RxN2Qxz8wUmpuYC2AR3H10NCmwLr5AulqvcmSpQk9+sBq0Qlz9qy4K9Owfp62lHACfPXuP2+Ew4/7hRELQ0N/HSC/sVYPjt8Io699KjpEgW9n6wwmKZN989E0yPq43p1VRJWNhlm9IMP7OXvTs3RYh24ux13vzmaRzH5eXhAzyyd8iANkaHu3BDB7+er5aR12+8c5rpmQWl0oau1IkC3hxeJ5fyq3WtzU08vn9reK2g5kEIb8NlLzNSBlNz2zQN7MqGrOBvsJeoAAAgAElEQVRJNS+08N4zP3n2GpVqLWwfVyt9IhyRoU0v60N8985NtLY2+++R2gavNAGhxhV6EOT7oO7XvzanK9CEDUM9LWzrb6a/vYkPg03Nl5kpVJgvVpkv1lgoVrh2a4pLV+8wNVcM/grFinaeqLMSef4Ccm2tbNsxiGVZZNNJunMZ+jqzbOvL0ZlLk8umyaYTQX46rqBYLOHUHKPjYvj7dPxnXSpVmJqcxS4XsRzHcNebbfQe4NWKwOxsgWPHLv327dtT35vPj7hm8hu2enuYIL9SwK+lol9WzReLlfSv/MqX/9b8fHEf4I/bjoe7VKQ66C06u9t54eWnyWTSTM8seJ291DvXXMnx7YoCmJorMjNfjCm8w4LeFQLbsti3ezNdHa0AVKsOb7xzxneB6i48T8EJ9u8eYmhjj+H+jVHtGIDxl6Vyjd97/V0KgTswLNTVj9LEFSzbNvfxna8+HcRtWRJWFvOFRaZnC2zd1Bu0Y5sA16DtH4zA3RIx+7yQE1NzvHX4bNCpL9r+Hqr1+OludeDbltes0NraZKTEy7GQ51ag4ONMqWNpmR9R9rp0D132angReoGOn7qidJzT28xle3gAXL9SmEwkGBrspa2tNYC2CXptWB3huRGwK+9g3L6+jgxbeloY7M4+sL3iHdflyq0Zzl2b5MrtWZxUM2NzZYoVJ/CgILxw7757impVF7ZqE5W3jbFRH/ICaO/IsXXbJoIGH/9l6W1ror0lTSph05pNkcumcCtFmlMJutubaW5KKR36wt+lE3htXCqVGhPjMyRKi4harY6r3hxuFx6v1VyuXbvzH48du/S9jc54H9w+vH4r3aQgW65dfCV/9cbKr7p9vlKppr74xT/8kfn54j7VNR+3DijrYYHf09fFs594klQqydT0PHPzi1oHIUsrw6IFmoU31GhiukChWPXil2DHL+AFQTt5Mplg/+7NtOfkGHeL2+MzyClVbYugwEUIbCxSiRSDA91geTceVBwsFZdhQUKwx8L2r93UlOLVlx7jjW+eYWJqDpfwYzG2Dw0JQhmNbclKia250PEfgADaW1tob20JFbsVLoOF8YW5IIlaGCvoaS9BKwP0drXR25Vjakbvaa8+X7VznY0c2idd9qHStyyLDX2d5HJZT6VHvAle04a8Pzu+FV7LiyCEL/llO748LagUmZUGoRz0XUHVai24P+89sEjgtYPbfsYLAbaMyYaujhybNvZg23ZwHaM25r87ljefPYDwhhX6D15Jl58D8l78eDqyKTb3ZNnc00xTyo7Nj/WyUrnGhRtTnLs2wblrk5y7NsnFm9NUaw5tuRYeeWQHiUQxuEfVQzMxMRMBfJxF3wJvT723Y3ZmnmtXbjG0dVNQWRQCxmaKVGouPW1NTC+UOXPhJgvzi8GPuq+zhS0bO9iyoYPmTMrwtHm/x0QyQVd3O+NjLlZ1AWLb4usvQTA42Ps9/f2d/x74i3jT4TbsLu1hUfJJ4lX63Sj6NVHzrusm/s2/+f0fnpyce1aqdxXotm0r67ItPnT3WpZF/8Y+nnrhUZLJBBNTcywUisENB/BUIaAT31dXgrHpAsWy/zuRKgErUO/SFZtMJtm/e5CWbJNWMBx6/zzFUjlU8OjqadvmfnZuHZDRBykMwRJeI3Qka4Ij2Oe6guNnr3Lk1FXPPah2LArc9aFqdIVgx1A/n3nlKS0eVe3KfEIo4NMAvoSqF3FKHwX43mJqZoG3Dp8N2jjVYXu6SteXZqUgk07xxIFtJBJhNcKoG2l9BrQb1m4++rs21Z4wjoReHfmOYOwXlMpVLl29TbXq+O5yqbTDgl4CI5lMsHljj+eeD8Khr4vwfDlbnWxrD+J3RWz7vIXFYHeWLT3ND0w7e7XmcOzCHc5cmeD8tUnOX5/i2p3ZoLlCVdidnW0c2LcdO2FpeSx/FwLB+++foVAoRa7jhRHRfTEr9dQ8QHd3B4NDGyLXzWXTpN0SM9PzkZPkuT3tzQwNtLO5v51sUzLoACnb6mtVh/E7U1BYAKdWd0hdXLu9XHZ0tP7Gj/7oZ38QaHyT/i7tYYB8XGe7tQL+ajvj2YAthEj+23/7h39lbGzmRbVndz0VH9fDftPQAI89c4BE0mZyco6FQkkHOijb5gFv03EFdyYXqNS8USkqVCV0ZaGbSSfZt2uQpkxag6TjukxMzikFgDwvrCT0dLWRSiWDykMQvyIjNOBDpMAINId/jamZBf707dNMzc5H3PTedtj2u21zH6996qnwxpVryeGDUaiLYL/p/wjDWKGy8hOmhbf0cw69f57pmXkF3KaKj3HhK8PtbMtiz85NQTNJqK/lf0HMpVdkWnFvVKzCip+/z3w2RmWwVnMZHZtmano+MpRSFvKdHTk2DnR56j0G7q6yboI+HFan7Bdh+HTSZnNPlqHuZlLJ1ebE2tuNsTkOnbzBoRM3eO/sKKWy6VpXV7yt3t5O9u3dSvgmhvkIXp5Mz8xx8sTFWCXunxF9ljHXWgryAD09nWzc3B/sF0IwNTHD/PQ0Wzd2+p7DKOQhTG9Xe5bBvnY29baRbUoG70Gt5jJxZwp3bhbh1DRXfdAcZ7jrTVf+0FDfF3/oh77rR4GZOlnRsCXsYYC8quJXA/i17JSnQf9LX/rjv3jr1uSnTMBL9a665VXlLvcPbR/kwBN7sCyL6dmFcO5n6f5WgUU4plq1Ws1lbKbgj3/XlZq3ESqujvYWtm3uI5VKBQc1nChwtowCQv/Bh6d48Ydgqg92tEJHCBHcn+sIDp+4zPHT3qQZpoL3XPWwdbCP1z71pKZuw3XF3YwCcBGuB3equOLlmG4LtApCAHz9VO9ZzSxw6L1zmnpPqBU64zlL4EtXdXdXjj3bN2JakM7gPvTmgphiPNaE+hDQn4m3qr8nqrpUw8v3ZmGxxI1bk5TLVby2eUEqmWTThh5aWppAEHxOVh1a53tkQ9Uve8xLj4Af3tUqBoKWpiRD3VkGOjJYcS/9fbJSpcaRs7d5+8QNDp28wY2x2eCYmafR/YKurjYefXQXen+HMH/k7+HkiYtMz8zFOWX8OEXkWqLOilkBMKPs6+tmYGMvApibmefqlZsIIWhuSrF9UxfJhB28EHGQV/d3tjWxY1MXG3py3nN0BONjU9Smp6AO0JcecifYsWPDz//gD37n3wOm4nOjYfXsww55qeKXAvu9UvSx+37jN/7ke65fH3tNdc9Hl9486tF2eYude7ewe/8OsC1m5wre15ysKFDquujxXIbj04VgEgwRyGqUbcikU2ze1EN7rpngIn4BIF3CZqNeBOqxxy0t7MUrt+nv7aClORMLdjWs+jrK+Mcm5vj6oVNMzy5obnvpOdi6qY/XPvVEJB+ISYsJ7Yir3oC/FkaE6zrww+fxztELzMwseOBWn7sC+IRZsbM81/YTB7aSSiZjK22RXX5FJTgoazWSGsZzCfLU2Kir4JVjgdcn2I8PaQ/sd8ZmGJ+co7Ojlf6+DizL6zEQwjx8D13/ZE+Zo3gADDe9L/1dAV2tKQa7s3S2pFgvuzo6w6GTNzl08gZHzvnNFVoIA4B14GsBzz6zj+bmbMRFrlaqao7LW28dkwdjq3HBFeN+n0tBPnoaAP0DPbS2tXDp/DXU6bHT6SQ7B7tI+xMg1YO8fkzQ0pRm5+YuBro92E+MTVGdnIpMmBMdUhfbXi/27dvys5///Hf8U2AyJjsaVsc+7JCvp+JXo+bXzG3/27/9p99x8eKtzy4HePllNLNn/c69W9m1bztYMDe/yKz/fWZ1cha5orU1h7spVx0mZhbDAsQ3qUoFHnwG+jro6+kI1W6MxRYsQWllKQWF/K+ofj/c9OwCb7xzBoAtg73s2b6JVCpsPxXybKOg0oACOI7LxPQ8AssoWCwymSTtueawrVqJx/LTpVeSwvwCYeRrGEYFu8xxy9JzxawQzMwWeOfoeYKhc3Z9wKvD6LZv6ae/p11JlxW4TdS+b6bV17PCCCHMcl8JGQVJnIo3m3hMKAWueSWsCnrhRxz0xkcHvfqNeMuy6G1LM9jVRDadqHuX98qKpSpHzt/mmydvcujkTUYn5oN57r2b1hbRtToVrP6+Lvbv2xrkIUZ+STVfqzq89fax4MR6pfRK1fxKIA8C27bNj8gggFQywY5NnWTTSaNyXqeSoVRamjMpdg1109vRzOT4NKWx8WAcvTqUTv2SnQS93kaP+/jjO37qB37g238BmKiTJQ0z7MHorXJ3ZhHtTW/+me705fbFHa/Xu17b9/rr7z138eLon1cBb7rnTde9Ol5+685BdvqAn18oMjtXCBW7ApkARJYBLmCxXGV6rhRABFQoe4uO9lY29ndqoI0zgewpLxW+r5sjMtdfMSnkr58+fxPb8j5zeeX6ONdvTbJnx0a2be73PBp44AzAjqpQwwgTiQT9PR1B2jSGyZs0PR7KzcSCXLmGruoN8Gu3q4YXWtwW0NnRQk9nmz+nPd6MYn4k6hfnVMC355rp7+3wO/N5GamBHv9ZWLI3uXF/saYcFfhxCrkaOabvU2qEants6L6IhEkmbGTKXT//hLCCSpGN/11yyx8tgXIv/geQLAtStk1/V4b+jgypxNJ3uNZWrtZ4+/gNvvLuZY6eu03F7/jpOCJ4bkJ513RKGjtiPCm2ZbF928bgoBpEXzfuu45XZgUHVx3KdaJD0y087+D5G1Ns39BBaza9grhCWyxVOXLuNj0dzeze3E25XKU6OenNaFinl33YVq/ttw8fPvd329ubb3/2sy/9FjC77MUb9qGGvAn2OCgvBf2lzltJhSD4e/fdc7uOHLnwA7ZtWfUAX7893mJwywb2PLITy4LFxRKzc4Xg4yY62L0CIGRfCJyFUoXZeR/wKnZ8OdrUlGbTQDfNzZl6JQvq2B1ZDAVt3JY3g5teiw+BLJQ0Sh7cuj3F3EI45M9FUKs5nDhznUtXxziwZ4gN/Z1IAMmLqfWFoA+Af/FQnROobanUzWOyMmDbYVqFFRawoABcKbtlvmmVATO8Gk5R9xawc9sA7x696EEdyx8aR9gZTwF8ImGzfUu/X9nRr2Zu22oCAqTKdSUBZgksn6GKD6kcVb5rlStVnoWVjUikwUOxvIqgBbawcCXchRUMh7Tww+DD3x+3KWxoTibob8/QnUvd1znkBXDq8hhfe/cq+WPXWCxXPbC7bvA+2zZer39LqTT7tjJ4erZxYw/ZbCZsWfHzzVK8blbMNZa6jvzZroq6/olm/5rlzHFcLt6YYuvGDtpalptcSEmUvzoxs8j0XJHN/e0kW9txZyb94XX1htTFVgAyr7/+3j9rb2+9/elPP/XHQHToQcM0+7BCXsLWhPhSUF8K7ssBv+7fhQu3Br7+9aM/AqRMoMe5500lP7CpjwNP7MG2LBaL5WCsdVjoewpCQkfteCczYm6xwsJiWZveVQZKJmz6etvp6sgph0JVF8BQr0nECvOgM5sCW1N0ILy01xyH85dHvfC2he2GYYUQLBZLHHr/HN1dOR7bu4X2tpagEiELLlXZmOVY3Gdg5VPUvxxHJL8COKogtwhAFoH7ssAPgWdZFh25Fnq6cv4seOiAt8Nx8bZlMTjQTXNTOigITb7FXc88oldx5CJafAd5qDw8bcy8Gr8Cdlmd8LgcPny10hSeJ0/1AngeISvoJOnKVPjHc9kkfW0Z2rL3tyiaW6xy8XaB41cm+fKfvIfreP0A5O8u+M0F+eKN4l+2QlV/N9mmTP2wJq0/QD1Hj3P1Jy8Ff1cILt+aZrCvne725vrXrmOOK7gyOvPf2XvvIEuS+77zk1XPtve+x7ud9QZYbBOGFAmQggTyqBODohQXlISTTiBvKJ1EBXWKu5B4RnGK4ynE27tAKERIWkkEJRICSZHYFbgHEASIAbAeu7NmZne8b9/Pm6rK+yMrq7Lq1et+b6Znpnuxv4meqkpfWfnymz+Tv6Q3l6I31UOuWUS6rWL7ZHF9cB3+nd/5k18fGxv4uUceOfQS8MGhNpuQda8bcIvUCQB3AvztwF20uY/8LS8XBr7yle/+kpSybyuAV+Hmn2B8apQHHzuGZVlUaw1W10u+V7RworFiXKAp7rWEoFCuU6k1sCzlm1r/pWyLsZF+jhycYWy43y+DoHz8/ETKjeB8OOH5na7vk9OLYAEiBFy4skij4WDr97HMdwjrXFkt8LVvv8Erb5yl1mj6+VVZllGmLjfoG63b1u0SW3/0oM0Jf+Y7KBAO30WETdgiTAT9dHDvpP+drKA8/R31oSA9PVlmp4bD/tb5I31ttLHNuwV9FvluwkgTke3EyjQXHbF6EZG2BH1kSJgSx4XZPuPb6bSWgOG+FMdmejk02XvXAL7heJy5XuKrr97g91+8xuuXNvCERTaTQVi6/SLSdrP92raiPYlNH6/fVPZicQlNUvJEkX2n9baJ3yrVZmWZoVLC5Zsb3FwtdVZ9QpJyzWGx6nGjaeFKAn18qJf3gq110auKr9Ua+z//+T94+saN1f0dv9oPKO1WTv5WufitRPyb5Y2E12qNzG//9jd+sdFoTobAHork4wAf18sPjw7x8BPHsVO28ve8XkyYLEUwfE2Les19rxdr1BtOAJ6aevJZpsaHyGbT6AK0BzItDtSe61p+HSJ2Ne992a7KJ1DiXl+Mr8MF1GpNLl9dwbKEcljm6SJCbi6yeJCSC5cWuXR1maMHZzl2aDbwjqY/iCRkdALAkUJvg295hUB1HI9PYJTMfg2ejczqOeTydXsIvokhrvcjB/t7Qm4+sLD3v4VlMT0xxNz0qO/iWEkATB14iANaldHa3s3eWzdGCNRCyxLYgoixm/JQhr8LQ4TqF4NbB4ODx2T41QdX40kYeYLeiXGmKv1APsX4QOaueaXzpOTGWo0LSxWurlRxfKMuETRZMDTYy82lBuFhRf741Jw9Sr2gfpeyI5F9Uli5VKVYrNDfrzhg/VtEtIrskyipTE2WZUUs4julzcpMzqAbDdeXizQdl9mJga7rVWXZFMmwvFzi0KCt3GZv6hEvGlYqVT/0j/7Rv/m1f/Ev/u7fAJZurRHvf9qNIL8VuHcL/u3i2gK+lNL+4he/9tfL5erhdkZ1SQCvw/sH+3n0yQdIpW0a9Qara4VgctF63CiXpHXjAbSyUazRdDy/fJU2ZdtMjA0y0Jc3kEr/kEOduvZsuylTgghmdCll0KZoPhEFTT/83MUbviGV8PXIKtwL3keiNQueVFytZymd36nTlziyf1q9l98BelKOKuujgyGujyeWNJjIwiaHiwWifRskNRYKIuJUR/VHBEwhqEXnOzA/ySu+fYV2gDMy1Mf++QnyuYxRnlm2bpSpQfeBHjOtjL20ik1bgmxKkLFDS/6tSEpouB5NR9JwJU0HQmMLvXDQH19G6gtuTaD3248R3pOxGe/PkM/cHXBfLze5tFzh8nKVWtM/WMW3DZCICIAPDfaxuLyOsFT79dHClhB4/ngNpSwxWOwSJa/fWKa/f08kLFw0sqmCPZvL0NvbQy6XIZtVf5lsinKpxoULV5NBfrP2bRLX6Wstr1douh57J4cSxtrWpWQyaZyhIV6/ep37xvMgvYgnvK1OsltfL/3U5z73z89//vN/5x8C1U0r+wGl3QjyWxnUdSO63wz02z7/x//4jZ9eXt74cDvr+c0Avrevh8eeepB0OqUOcvDdRpqiePUcgrsJ+p6UrBdruK4XHL4hhGB4qJfRof7g8BsV4f/MZAjspt47oIBDl/78HKKkJGyTSkvw2zUBVQdvFCosrmxExcEa2I33kL4xE4LAGlsIGOrvJZNJB1NDYAfgx8d3C+gHkRQcSaFbqUEpuhgIsEmXFeBU2LZ4bdGwoIDAary/Pxf4tO/pyXBwzyRDA70J+Yxn0VJitFYjXvgKY9uGbMoim+KWDmQRIswPqo+rTY9y3cX1ZAS7W46+843tgIirX50ul7YY7U/Tcxe2wbme5MpKlQuLFQrVZuAlT3PtGqCD//0zC4YGesOFnj9uhQwXbNpfg7DAklsZ4G2+CLi5uMahg/NYllASkBgIBms2IRgfG6a3L09fXw99vXnslK3q9bl+x/W4cP4qi4urxDfJtSu5I/DucuGi5qM19k0PqYVlp9n9hD35LO7UJK9fusLxiZ7A+ZEJ7JudZHfz5trf+dzn/vm5z3/+7/y/qFnlAzJoN+6TT7P1/vikvxSh+1uL5H3xW3rIe/bZ7338jTfO/zXbDgFc38evcTF+JpvhyU88Rj6XxXFdlpY3FIhZBqAb4A4h+INQK9diVU28frre3izjo4PKiYpK1pbCnznE+NlISMuzbI03+F0f8FXIy2+cY6NYDVfiMjzAwjyxyjxpLkgj1V76Jx461FKPjDcgPpO0mVniEuSkfPEFgtmPIpJN93u4qDExOe7PHiTlSp1CqcL0+LAS20d7LlJ/JDyhffE4IaAnY5FLt2hwt41qPtg33XB/vP7e4X559U7hyXSQti1GelP0ZO88uNebHpeWK1xaVoereIave4/YWENGzpt3PeUd8uvfegXH9ZQBnjFGXc1J+mPY85RPAC8+b0pzWMXuYkkfuP8A4+PDLX4HkIbXO/XY4jBHXwuFMu++e4larWHUGGlEy+/XSNWm3WZ4wt76SFGtb9uTTXNgdjjYTqmSRSePljnGKGdtvUTp0iWOTvT636sz8b3P2TeHhvr/wjPP/MofJr3ODzLtNsM7zVGLLv5SQBa1ODCBWxjldWSB/+qr7x48der8f2MeLNPe8U2Uy7dTNo899RD5XBZPSlZWCwGHHTG2I+Told9zX98uJYVSTXHjljrIZHZ6hNnJETLplLFAIOA8g6v/F+gahV48iEi82WkQK0vEygz+qURCCBZXNihXai2Hr4Q2A61hWl+tpQXjIwNt26RErZJiperXSXBt92cZbY6/WFL5QZlGcv1giVBIHylTA3FsYYAQ9PXmmJkcCXzUBz1n5jMguuWdYoNZx+XSguEem/wdBHhAceJ9aYZ7U9hW7D2DvvV/D0A6ZTExkGFuJHvHAb5Uc3jrSpFvv7PC+aUKjusDhjAMLcNRqsZ/8HFF8A62bdHX1+OPRzCThFtZw17eWlgiNn28ubjWPoeIPEUWeQIFihcv3uDUqfcCgE+oYtP643G3O350/kqtybmra8ZhPB22wafhoT5y09OcvlYwXN2G4vpkV7hKvO+6Xnp5ef2Zn/3Z//XQ1jX9YNFuE9d3CvA2CtgzhIAeB3F7k/wtYv6bN9cGv/71134BSCWBuja0M0X2Jlf/8IfuZ2CgF4DVtSKuJ42jRgER6uOFr8jWk5REslGuB2e9Dw6oLVo6ryUgZYlgP7jiAAhdhwq2lJ+Zovd4ROgpXYmhI/vnVZA6H/vyUrBwsPy06gjSEK30TjphgfCMLUtSTahjIwPBfBBIhFGe5N47f52zlxYRQvAzn/4IwldNiFjaJDL3wUshoy5h42n9/0Q8LAg3oTkU6+rK4/0Yzavbq7n68PubchazHWYZQsBA1iJ1lx3FZFMWY/0ZNioOtaZH4BjGf2/bFgzkU/TdBc59rdTg8kqNlVIj4ILNb6a37PlBkcWSPywJe1tJZ4YGen0Pk8kie70wTqVtBOBU6y3t6uBnBhAzvGvNZYbo3z8CKuU6p89cpFSqdNRPm5e8deZu99GDAvrz19Y5MDtsDOD2pQTv59PY+DDXGw6Xlm8yPZhr0cNvcZLdyPp68Us//uO/8tRXv/pPP9DP+/R+A/kU0APkjLTdbo1rAXvHcVNf+tI3P9dsOiMatEOnNkRAPfRkF4Ydf/gIY+PDCKFOV2s0HZ/TCLl28xpONKr8YqmO50ksS3kWG+7PMpRPkbI708E6nqTpguNKml6rdbAhZSbwPhY8imByRISHthhBAFy9sYrjOD6nbhrZab2oWrwIibK2l2G87xuIVNpmoC885rZRb3L+8hLvXrjBin/imfBnn3cvXOPYwbkogEaAnKhq1ARLYegNzXyxffLRuOS+Dazi/YlYhFlbADqaL1wshGlUhcLoWFO8bwtBf87CvkfyN0vAcG+KSt2lUHWDhWlfzqY3a3fCrN0yeVKyVGhwbbXGpetr1BrNQNUjJQwO9JLNZghARX8AYyGixoUP7P74swBXwuBgH+LKoqpMGONEgOWLrkeG+3ng/gPU602+/b1TNJvG9uw2WJYUPDU5GgVyofX+rVb2+rd67doSFy5cw/O8dlW1b8Q2pto0rx9ZrNS5dHOD+cnBLWtJKnB6dpzLjSbLxTWG8qmYB7xWS/tY3MOu634e+KtbV/6DQbsJ5DcDdwvoQwF8Erfeditcm7Ii4b/1W1//mWKxcizuwS6Zo4/GHTiyl9k9UyCUu9pqrbEpwCtRbsgpFysNXFcyNtTD2FAPQ325mNObrSllCVIWkFb5mq6k1lSW1LpjdRcHltQa+g2ON8Lt65WBhHrD4frN1eBISk+Dt0Z7S+lDgw4V4QSLVIZMQsDocD9IyfXFdc5dusmVG6tK/+n7MteSDongjXcuc/jADLZIRjyR9CCT04QLAGEsDkIpg4zn0emNvhFGJeGCIGZ1r9MmtSMIF5HFglrwSWxL0J+1OhAV33nqydqkUxaNpkdP1m67ANoOclzJzY06N9brSt8uJTeX19nYKOF6+lRCj+HhAQ4fnA2/qVTjyxMgZHTnhPrOenyr7zU82Gdw8LFvLgSHD82yb880Ekk2m+HxR47wwkvvtOrmw1xEV5nqcXi4n5y/syK+2jYXnnogNhpNTp+5yNpaYcu+SlrcdkpdZzNXIAm0Vqhi2xYzY/2R8E7rmd87zYX3mtj1EnmbTjzhmfE//0M/dOI73/720/+im1d6v9Ju0slr4IUoKOeBCRTImwZ55hnzJqhv9twC9n/0Ry89eeXK0k+Y4A0YwGy14egF0/NTHDiyBwTUag0KxaoPHEkAb/6p8su1Jpm0zf0HJziyZ5SRgXzXAJ9EaVtxhDZVLW8AACAASURBVEN5m2zaCrlKYXaA+Z4GOOuPIMLr5WvLftqYDt5PHHk3wns/MnguV2v84dde4dsvvcPVG6v+1j2zDSJoQ7VW59yFm8FoMAdGO5ay0zShFCXsiSBPrA8i5Qb5/LYGZcZ08Ea+eJ+a7dPxKUswsEMAXlPaFvTm7hzA15seF5erfP9SgaurNZxAtaP16tq+Q4WtrhZo1B2C3g9WbiI6hoz2CuP/nnyWTDoTLrD9/u/pyfLhD93Hwf0zwW9PAGOjQzz4wIGWdm/VHdPTY5E0wvzwsXIqlRovv/I26/4OnNuizRq2Dd8wqYiltTJLa+VbLnDPwXlW7B4aXmiEl+wgR9+b3D3/98LCiSdv66XeJ7QbQV7/ZVDgPkYI7nHQTrKWF0RB3jTEi9Rx6tSFuVdeefezJmfdysXTIqYXAkbHh7nvwcMIS9BsuoGLU71fuhXgowDjOB5zE/08eHCC/p7MHelQ24K+jGAwZ2PbRCZD9D2toG7el8u10JFPkC8Ee8ucZNGGhtG/YFFTqdFoNqPgZ1z17kDhB5w6fSmwzo3PlW2B3FwQiOQkGPF6kYJeoMTKMdYqRhWipQwT9kUkPLxGytN1COjLWpHy389UqjmcW6zw1tUSK8VGuH0Nwn4y+56wz27cjJ1Aan5fEX4BNQ4No1E/ydCQsZXOEsxMj7Lw4QcYHuw3vl14nZ0Z58ih+Uh9SaSDbdticnwkOU3L4gM2Nko4zQSPrZuOBbHJ05aZW5Le7rC7tlxkrVDbourkCGEJ9hycZ9XO43psCfDmSXYofPjSwsKJ8dt8hV1PuwXkRexvEJhFiedNAN9MDy9oBXzzz9xeJ1ZXiz3PPfe9X/I8LwshGKt7Ak4vSUw/MNjHg4/fh21buK5kJWEvfMiZigD8wwNM4ND8KONDvXe0UzXZFgzmrIildmQijU1A4eQoWF4tREE9stgx1BvG+2lOLEB4YtekMAwpgH8tVWqcv7wYeZf4dCE2iWtJZy4AEq7BtyMEiJY6jAzCiA/7VUTyiVi+OPb3ZsSO4uDvFJXrLmdvVji3WKVYddt+N7MfjaUUAri5vKF01iKe1k8R72yiY3yoX/3e0imbB4/v58H7D5DyPfNFQNifUQRw6MAs87MTbd4q+uEmJkawDYOKxM9qNL5crm25cLhV6iz/LdSSsFi5vLhBsdxqqNhJNVbKZnLfHKVsb9utdHGXuAbNAf9xYeHE3T+reAfRbgP5NOrDTZBsNJcE4vFtc0nhkXvPk/Zv/dbX/laj4UwlnSYXt6A3Of1sNs3DHzpO2nfosrpWwPNcA7sMgDf9r6PiMimLfTPDpFN3/9Pk04L+nLLSj4NNKG4nwhVlM+lQ5BgsYmKLBIyCwqThqk0kXNuEBX7w/TadOn1pU90gRj1JIH5LFLx/qyg46KuEesL5W8TCRSRe58v4nuvez1RtelxcrnFhqUq1EQK0/mDBmkgvsITRv+aYBDzXZXFpPfgG4feO9mF0IaZGh4U6hnlosI+PfOg4M1OjQd7EbxUUAPcf38+4fwxya20hzUyPYmRL/HGYIeVy9wbiIvive+o0W7fFSwkXbqxTqTVvqZ50Os3Q3AyVdL5lS53Jzbdx6/sjwP/RZZPfV7RbQB6Uzv0A0I/6/nHxe5K43gT1pLj4M4D87d/+40+vrZUeM8XPSVx8yJUr8LdtiwceO0a+twcBrK0XaTadYJoIQI0ouGtxcD6TYs/U4C15LdsuSlmC/owV2S+sJ7pgYjXQMpdNY2k//SYHT1RkH3Dzca4+slAy1RZRFYYC99hCQ0ChVOXClSW/lWab7wIZYyEpskU4EUkpQuAihJLgfS3oSb9/Ab7ueFxdq3NxqUrF16WHvzeIAnwUAIf6e8CKL7BU7PXF1cBANGJX4uc3j7EN4Nu/DA718aHHjqrjmP2Mlo4XoS8LIwj8Mh99+Aj79k63tZnJZ7OMDA9E69XtENGrJhPk43m6GeSi5WazRF1QrMGbFeF5knPX1qg3nVuoCLL5HH1zs1RFOlFcn7g3P6RfXlg48dO3VPH7gHYDyAsU934Axckncd/trOc1954knjcXCR7quEL5yivv7n333Ws/YwJ4ov489gxw4MhexibUar1QqlCtNYJ4EwBbuBIhyGZsZicHgsnqXpLS1Vv+BBe+L4Q/ZD3J5bLpIDDC0Yp44tizTm+UFUzlUVbLv4brsEDk7+dbXk22PO5kTmubRsTSbLJ4CBYlmN/XX8yZ6WKvlcjR68VTKhxX7ydqupKbGw0uL9co193gNxDD28hwCTlf9d/M9Cjjo4NRiYmfoVZrsGqMB2EkCIYShi1ImDJYfBrVRkkovfC589ei7ULp248d2cNHFx5iYmK45b2nNRffWmRCoODKlZs4rrtJos3G91YDp7sVwnYNQ9f1OHt1jaZzayfDDgz3k5maoiGsuDV9J9l/Y2HhxOwtVbzLaaeDvA0cBqYIwVqDuAneZniSW9p4Wgu1k6MB1PG3dZdK1ezzz7/834NMQRTAo9x89N6yLCYmR9l/eC8IqPqW9JoCwy0NagaLqgFxaqTv7nGgHVDKUi5T4yBlAjMo9URc395qSW8AuXmfEGa1C0dPzLFFF4KZyWRjpi3JBJY4qG+VLWHya+XW9ZMB5m3qEUYCSyhR/fuJHE+yXGxyeaVGqe5uuroKOXj/mVifCTiwZ5LBgV7/NxRVnVyPG+CxaXXh9wy+n7H8EuH4W10t8O3vnuKibwcStMz47j09OR59+AgffuIYA/09Qc0z02MqTbtB45MnJadPX+TsuavtWrxtdDdGWHzJ1HRczl1dw20RrW/SGj9KSsHE3CTNgVGangj2yHdII8AzCwsn3l8/rA5oJ++TzwJHUVaSejGiDSiE8Rc3rmt31X8O0CQE/sAo4zd/82t/pVZrzCqHN35FEUAPn0P9POTyWe5/7D4sS9BoOqwF1uYxnTvms5oipISxwTyZ9M6zDUnbyn1qral+SBEnHYCQykjJti2kdCMObgLHN6D2KQvjWd8Lwv32+h6ZmDZYlgnCQ2skpFIW0+ND2L6eXp3VDrY/OdsQTsLGZCzizyIWZoJxDJjjE3XMPbfha9x8FtE0wdwU8xEu1fa098tM5HmSjapLsebg+e4OYwfZhf4XpLHPWwBSoE/bk0IoRzF6bFgWRw7O8sbbFynFxNqFUpVypUZPPqccOwXfQhUu0P4Q9GmCxsEt/k04NgVOw+HtM5e5dOUmniexU6mgtLBe39GO/2FHhgf4yIcf4Or1JZaX1unpyfljIvTYZO7HFwIaDZc3Tr3Hxkbptvrc77pb2i/fprQOQzuJhFrD4fzVNQ7MjYSSk47bK5g7OMeZUpne0nq3v5MfBf4e8GvdZdvdtFNBvh8F8Br52jmtafdscvp6HNRQnLtpSR/kf+65Fx6/dm35U7Ztx0Txcd17FPQty+KRJx8kk03heh4rK4UAhBQgiIAbiAC/36pM2mZ4IH+n+vG2KZuCpqu8gkWchEjYKFZYWimEHu2MBU0E8PVqxgRzYzJVLm0hnbZJpzJk0nbwl03bZDM2uUyaXCYV/OWzKbLZFD2ZNOm7dDZ5W7otRH6/wHmUPKm2wxVrLp4XLmrCz6/ulEdFhe6hu+HQ62Lc+5u6qjy2bTExNki5UvWlQALXr+jatWUOHZwjvqoQxkLCwGTM1pn1Li2tceqtC1RMN7Yido1R0FYL5mbGmZ0ZSzi1LurOtVSu8vrr71GrtbdCj+fpHBgjhbTPE1t4dFZeSyd2RJVak4vX19k3PdS6ao42qaU96VyWmYN7uHbaoa/WtQ+B/31h4cTXTp58+tVuM+5W2okgPwYcQX1fLdNJAnkTzC0jnWU8A1RQAG+K7jHLuXjx5tCLL77zOVMf3k5Ur5598EZw/JGjDAz1gYTllQ1czwv2woeTgHowSgCgXG0qH887mARKP1z2uXnX9VhdL7GyVqTecHxXs8YiBgPgRXifzaTI++Dck0v7fxn68ml68xny2VSkjz+g3UuVukeh5uBqF8qGJCSCBx2AlDkJxDO0Gy0CWF4rsLfZJKW5boE6JjYpn7/Q0MfRSn8FIITk0pVF6o2mmgP0WJfKX4bryVaOPGhhZImiSm3zvivL65x6+zyus8kpqW3ydorzrem2jdW/5VIL5TpXFgvMdeL+NkbDY4MUC1NUrrrkG5358vcpA/zmwsKJx0+efPoHwr/9TgP5CeCYf+87RE3k2tsBvQmtDaCIGnNJYn5N4nd+509+0XG8AdvWwK459XZW9epvdu80c3unEcB6oax80mulMqYUoJWLL1UbDA/kSN0rR+RdUNoGt+pwc7nIRrGC63lILxSrW0IZDmbTKXX1AT3nA3o+m94V7/kBbQ95yBbOXZPwUVGrNBKg0Ed2yUaxwtp6mbWNIkcOzpHJpMM8piQoLDwoUHqSazdW2Ts/kchoRuuNiupNKdPczDhLyxtt3zXg/QNpQcjZtgM8k/m9ePE6Z89f3eR8+lulzkroKNWtcPgd0mqhSsq2mIq5v92iOUhgft8UpysVGisOGaexVTaT7gP+L+AXumrsLqWdBPJjwP3+vQb4rYA+SWTvAmsokNfubVuAXf/9u3/3/J9dXy89almqKJODBxOco2L2voFejj9yFATU6k2K5aqvXzIWBX5V0coFtYZDo+kyPdr5wL7XlBYeUrpMjvSSTaeUKD2jReqpzSRuH9APGOVSFkXcAIxNEXwAoH5aA1NBQKFQYW2jxPpG2T+3wMP1JMurBWamjH3mWuyuA8zzAXy6cXOVudlxhBEfytLZFLV09OT4EOm0Tb3uROI2y2NW05pG9YPnSd565zw3bqzECtgeDnsbi9pGMj6+0a7FtTI9uTQDvTnaNjjhXYQl2HdwnjPVBnZpFdvrymr/cwsLJ547efLpP+gm026kncJejQCPkLzVLcntbHzLnM5XBq4CVTYBdh3w4ovv7D19+vLPtwK7IC6iN8O1Hj5lW3ieZGW1GNUBiGiFfmYQ6kjWcq3ByECeXGYnrbE2p4HeLPfvH+fA7DCzE/2MD/cw0Jsll/kA4D+gKKVswUDOJp+JHonbfpiEv78bi2tsbJQNq2n1m1tdK8Zg3Jeq+bqAuIW9EOC6LotL6uz2qISOSFr92wxKNBYPlmUxMzUWSvKEAulqrdkyubR5NUQsQaPR5OVX34kCfId0b0wyk+vctCUi8bYtXV4sRE/165ByuQxz+2co5bT7lK7oCwsLJ6a6rnSX0U5AmWHgCcK1WpyLN/XoVuweP14Ci0CJ0KhOUxzsAUSlUks/++wL/4PnybQS07dy8UAL2AsBxx89Rn/kbHgXS1iGHl6EP+xgAlKtLJTrCAkTw3fHZe12ktQKVp8+APcPqB315mx6fS2ZlOo0uYbr0XAkDcfz/0Af76uZ61w2Q63eaOHaHMdhY6OkjoTtQnR89doyE+Oh3UuUIUxiD7XkwX8USmR/4dKNIKzZdPnGN18hnUkz0N/D4GA/w4N99A/0Br/1dnr5QqnCq98/Q60WFS/fMtPdRUZTYrKtxnfbQK7rcfHmOgdnh7ueWEZGBykWxqlca9JT7+pAnHHgXwGf7qrCXUb3GuR7gA/77fBoBXhtJ6O5d2JXgTKqu+qnje9DawF3HfZv/s1Xf6ZWa+zTh8xEErUR2YNgcmaC+f0zAJQqNSrVemTlH1SSMFCLlYbagmML+nuz7Xtlx1LMuleG4cHdB8D/AcVICEinBOmUjTnsPYkC+6ZH3fGoNT0G+rKsF5K3kC2vFhgc7PMLJboPz6zPuK/WG7z59kXuO7IHYYm2hn+bWa4P9Pcw0NfDeiEKIPV6g+uVGtdurIJvjDcw0MvQUD9Dg70MDvaTzaSCUm8urvHGW+dwXffWUL1NnvZFbVXJNsnzY7qJTkuNp6vUmlxfKTHd0fG00dC5PZOcKZVprDW71c//2YWFEz9/8uTTz3STaTfRvQR5GwXwWUJQh/DL6Z+wKaaPA/4qcJ3YfncjXeLzd7/71t5LlxZ/plUcD1HAisbne7I89MRxBALHUSfL6XQiXpt/o29rDYd608G2BL35zD11XXurJAR4Xvh6IcaHS/9w3v0A+D+gzckSkEtb5IwtkPOjM3zo6CTrpRrrxRqrhSqrG1VWChUKhQrNhmEx7+eJi8PjtLZR5PW3z/PA0b0IS08lIUAsLa1RqakFuCclngee9MJnV5LOpiNlJlXneh5r60XW1sNtXflclsHBPlIpm8tXFukG/rYJgreH7hKHv7RWpi+f6frkTduy2HdwnnffrJMqr2El+7FvR//M188vbp1099G9BPlHUKL6JO7d5OLbWdZfQ4no4++QxL0H5HnSeu65F09IKVOhKD6Zm48+w6MfeYhMNgNIVlYL/olXBpgZf2bFrutRrNQDxw8DPbuRi1ckCLdFBW+oTaURyrLZT6fJZLhama/3p/vWD+jWKZO2mRjubVFpVWtNCtUmTWlRrDmUqk4bj2e+IlzobXFQLJR5/a3z3H98H5YI+YFSqcqpty/guh6ulOGpZlIZ/HmexN3aN7qutQUEq9W6scd+c6v7rSO7I0Fnxnf3ZDGxSaWXb25weH60s0O6jHLy+Syze6e5ds5hoNJ+R0QCjQC/DvxcN5l2C90rw7sDwH5aDezEFld9fw7FwW8K6EnPv/EbX/nz5XL1PkjmMNvp5I/cf4gR/yzojUKFWr3ZthIzTErY0McsCsXb705RvSLpw7eUSkcf/sWf/T8/rSfDPOG9fo7nu9dv+QHtRMrn0kwO9zA3kuO+mT4+dHCITz4+z088dZiHD00xPtzbXmwkoFCs8Pqp8ziuGyzGz5y90rpQiM8BnTRuGxaqnRaxk4zvus+9eTkCgeN6XLq5vqWiIYlGxocZGBulku3ptol/aWHhxJ/rNtNuoHvByY8Aj6O+U5x7h6i43hTZ6zXbO6j9753o3yPPr79+bvzMmSt/LTk6mXsHGB4b4vDxAwihLGPXN0qhNW+LUiBaRrFSx3U9/3QqFZfdgS5su6EtTnZFCv/DBRy+n8+4iwgDpFo8iKR4o+S7wfGbLmplQph5EwxOEb5L8JXF7U6LH1AnNNibZfDgBA8cVGe615su15cLXLlZ4OKNDS7djJ5lXixWeO2Nszx0/36WVwpsFLow1Nryg26lPY7FbxcLfSvlbJGn0yLvlFSiXG1yc6XE1Ghfl+UJ5vZNcaZcoek4pN2u9POfX1g4cf/Jk0937UZvJ9PdBnkLWCA0tGsnpk8S10vgLaBA1OK+Y/rSl775S54n8+2Og0yidNrm8acewrItpJQsLm/EAMjXQAvzSd1U601qdSc8d94Pt3exYxj1IWLGd8aPTwoQngH0gYsxU2Cv7wxg9/2Sg9+Xnq+VDMAyBH7XldSaDrW6S73RpNZwqDVdavUm1YZDve5QrTsqvOHguB6PP3iQsZF+tNQ1AtyGrjEO0N08bxrX7mqkyafaM6IfUGeUTdvsmx5m3/QwH/XDVgpVLt1Y5+L1dS5cW+fyYoEr11a4cm3ptqRG9+RTtQG6bQHlbgu7w7S4Vqb3FvTzlp1i78E53nurSX95DUt2rJ+fB/4JcKLLpu5outsgfxwYohXQ9ZBK4ux1/FsoJze3wgaLf/WvnvvhQqH8pDpUZjMBu//kp3ng8eP09OntcgWajtOR+9Wm41Eo1yNHV2oy9w7vSpIxbJetccRAVOvoowskpf8s15qUqw3KtSb1huMDuA/S/rXe8IG76eA4nhL1Q4DWSi2QPDOlbZu+/l4ahlHznfgCWnohE7h4c1Eh/JsA4P24pO0hH9Dt0+hAntGBPI8emQbAcT0W1yq8fW6Qs1fXOHtllZWN0DVqgoyPW0a9WwZlleL28XarErYJ0dt5/+m6HCLNMfXz3Sxi8r09jE+PsXKlSX81+SjqNvQLCwsnvnjy5NPf6SbTTqa7CfL9KGO7uFje9E9vhpvM1Slgic7nwMjv9OzZawNvvHG+69XZ+OQoew7MAcqAZqNYSQTtOEkJ68Va23jb2r2cvNaxgwxELZv9+BzXo1p3qNaalGtNKnUf0KsNipUG1VoT15PKyMk3fFJ/XvCsDf1uVV8/NTGEtQv63JPq9LwP6M5SyraYGetjZqyPH/XDVjYqnDq7yBvvLXLq/CKFUvvfb4TM8egv2G4b6nYIJ91CXbzfdkkWlH5+45bO+JiYGWN9rUDVdbrxb2+hzp5/9OTJp7uS9e9Uupsg/xSQpr0evp24/izKyK6btkbGzTPP/NEvOI471I2Y3LIEDz/5IMKy8FyPxeUNxaV2oGtdL9VwXC+yTS6TtpX+sC/XxWvsPFLGdDLg2JuOS73pBqLxWsOhWnOo1hWoNx03AG4F5F5478m7MpfNaneoO5w+MDi8dzQ62MMnHtvHJx7bh5SSc9fWef3dG7x25ibvXFzCa3S1JeuOU8u+/rbpdrCFfYdUrja4uVpicqQvFrN5q4VlMb9vmnffaZB2G6Rcp23aGB0H/kfgV2+pwTuM7hbI7wf20Jke3rwuAme4jV0AX/zi1x5dXt74CRPgk8W60bD7Hj7CgO94Y3FlA8f16IQZLFcbgR4+n0sz1JdjsC9LLpMOnOoE56HvQiqU69xYKQWArrYb+Zy35sJlCOKa7vYEkrJtpsaH2DM7xvz06J2T0XdLxkIxME702/UByO8MEkJwcHaYg7PD/PQP30e94fDmuUVeOX2dl9+5zoVr60HazczskkO3AKZNYzdr9K1m3B20uFqmJ5ehP9+dfr6nv5exsSHWFl0GK+tbZwjpHywsnHjm5MmnL3RV4Q6kuwHyFoqLbyeObwfwZeClhPS0eW6hZtOxXnjhnV/qtsFDwwMcOn4QUNa45XINsYWxnpTKGYYE5iYGGOrPkU7ZBrCHWmxPSuxdivIb5bry3GeKzmPoFAerlucta2n91NKLDgRp6OLxn23LZnpCAfvM1AgZ2w4B9R5MgHEdfbt1RmBQ+D6ZpJuupFJ3sW1ByhKkbbErnT9pymZSPHZshseOzfDf/hSsFCq88vZ1Xn77Gt978wqrm6jmtqRdBc7b1Vi/nC6L0/r5lhMttyhnan6SjUKJmpMn1+j4dNkc8H8CP9N5C3cm3Q2QPwIM0h7MoRXIXeC7qJPkoq6m2lMLMvz6r3/5M7Va47BlWQnce+uUK6VECIvHf+hhbNum0XRZWtkw4qMcuC6hN5+hL59RZ1Zbyoe90t3LQFYmjWfPk7t20qs3fJGX3ugesZZP+AhbAP5WGWRrUOTZtiymJ4aZnxlldmqEVMpuMQgMkuvPYdyL2P29ZPhdb+cIHG6H6o5HtekhG/4w8d8qZSmj07QtSNsWmdTuBP/RgR4++eRBPvnkQaSEs1dXeenta7z09jVeP3ODWrNjsfCt0XZZ2G+W4R7o38N0yaoIx/W4dCPUzyeVFwJJGGPbNnPzk1w465B2Gt2cVvcXFxZOfOLkyaf/pNMMO5HuBsg/TgjmnYrpT6Nc1sYF5FtChL45e/Za73vvXf3vIpFbcJwAh48fYGRCOb25sbSG58lw+5tRQzptM9iXZaA3S8q2aDRdxcn7HLuHxAo2nAnfujzk5HcjSSkpVRrEu70dEMvIs9xah9jB95Eow6mp8SHmZ8aYnRwmlbJDrtlIdzvwEX6tBG68Q9uMW6lzN6tyNDWcpPEhqTtQa3rBs3pXQdqGTMoK/nIpqyPV2E4gIeDQ3AiH5kb4S598gEbT5fX3bvLS21d58a1rvHdlJbLgjORNDm5Jcect7O8gbXPV5VqDpbWycnzUBfUPDzI4XKDsdm1t/88XFk48fvLk0zvLKKMLutMgfxAYZXOuPQ7wJeANI03HwG4+f+ELz37Wdb3h+J54NbmIyDOosN6+Hu5/9D4ANgplarWGYU0vEVj092QY7M/Rkw117I7r0XRcf2udbo4CeoEGAy2iEjiuuysd4pSrzUBMr0HcBGIzPAgjAdyNryqEoCeXUZ7vTAv74Iq/0IL+vjzz02PM+MAe4cxjtL5R5uqNVe4/Ot9WRG5eWyJvEWi7XQTo9EJAPq38ue9m8qSk6YSqlHbzu47zPInjSsp1L+Ih0bYgm7LIpi2yKYt8xiKXtnf8AiiTtnnivhmeuG+Gv/UXYGm9wjdfvcA3XrrA6+/d7HyBf1dxeYdto9uCbq6WGejNku3yqO7ZPZO8U6hQdxpkmx2rWB4BPgv8yy6buWNItNtbvE30l1HH+UkUiHdy/WPgkm4fSlxvoxYktvHX9vnZZ7+390tf+uaXbdtKW5Y6E9q21Z9lWf6zwLZtLEv4cTY/8mcXmNkzg+d5XLh8U2GyJchlbIb68wz2ZbEtC0uE++gRUK05gMQyjqs1j64VCDV5+88pWzAy0LXbxXtO15eLXF4s4HoSGd/2Zmx/c43tb9FtceGzEIKZqRH2zo6TzaQN3XoMgGWUq8aI02AaCUQldl2P3/6Dk0xODLHw+FFymXQQFxYQvRcYSe7ic9qCnvTu5+ABSnWXUs1TS7tg0WfcB26NQ0APwgDpb5n0UAfFqLyh/UdP1qY3Y9Obs+nLpUjvoj2Hq4Uq33rtIt94+Tyvnr4R2PAkrYHDG0lCEv930ZoxaTZXxbRKV+I3yXXEw5LlcdKIb1+HeSs3b3MCLpnl9eYzHJgZRppOJow0cYdduhfXlte5cv4ag5X1bpzkLAGHT558uiuH+DuF7iQnvweYJgRv2FpMf4kQ4OOUxLHH530J8Oyz3/u7INPx88/1hBI+Kw5eSpjfP8P0nhkQsLRSQErJYF+OoRjXDhJPCiWWENBoOMFBNSHnrsTzSnTh5/HvlG9mieN6rQYkO5wK5UaCi1e/2xN09EFCIyidSjE7PcLc1GggZm8pcxPqlMm2LYvBgR6uL67x7Ndf4aNPHGNibLDziu4S5VLq7/1AUkIlaatZ0iSfxPXpcSAAD4QIFweg/AgUqw4bFSc48yCTsunL2fTnbPpzKfpydkfOqu4F8fP2cwAAIABJREFUjQzk+amPH+OnPn6MjXKdP33tIt945QIvvXWVphP22+3qrlvTsX3b6O6prj5MV642WC1WGR7obkvy8OgQq0trVNwmfbWOvdeOA/8z8MtdVbZD6E5y8p8BjrI1927efxnl1c6kNF1w8U8//btPvfji6S+kUibXbnLxIrjX13Q6xU/+3E8wMNRPs9mkUioz2JclZduGG1J1Y/k+6xGK66g21EE1QgifkyfCzUe4e0IOvy+fprfL7SD3kmoNhzd8cWPSVrl4eBDvb7HLZFLMTY0qxzTCCrG/DecefzY597hhnbkFTU9oQsLJl89w7tJN9d2Ah47vU+L7WL57wclbFvSmoZODtnYLVRoexZprHFjkfzuf+ws4+BiHH6qAZDQNvrpGP3uKww+vMhD5e/7VAvryKWzpkMJjfnKAgR1+IFSp2uDb37/EN165wAunrlBvusb4bs/Jq0sSx9pKMhaZxMm31JNYnkwMT+Lkg/AtOPmWeknm5OPl2Zbg8J5RUimrY04eoFaq8O7pi/RVi6Q7P3u+Cdx/8uTT73aaYafQneIh0sBhOjOyA9X7l2gFeIz4RK7dfK7VGtarr773K0CEA1DPkiTOHiQPPHaMsbEBcmmbstcg059TYKEL8DkDIdXkYvkoU6krHbW2pFfcusAK+E0ZeUlzLVpruPTm23fgTqMbq2V/wg0nb/M+rqfX196eLLNTI4yNDraCcwfUMXefkHB4sDdI4CF57c0LLC5tsPCho2Qz6WCPetICIq4F6MT4Lvzqm6dP29CbuWW1/46lSiN0NyxR7xux3YjMuzKIC8MSrsZDnBs18zpNl41ihUKxzEahyup6kabj4nqSgd4M8xODzE8Nsm96iLmJgR0lRevLZ/jxjxzixz9yiFrd4eQbl/njl89z8vXLkdMut6S7qse/9+R6kqtLRfZOD3aVL9fXw8jIAGvLHoNuE9EZo5sG/hmKed1VdKc4+fuA/4rNufb49feAGwllaZ38lpz8P/gH//KvXL689I8249zN8Gw2zdFje/jUn/soqUyKWrVOuVrzdejCENFrnbq/NU5Ao+nSaLqBzl1YSoQf6OItWnT0Yby6DvXnyHVpPHIvyHE9vn/mBk7M4U0SB6/19P29eWYmRxga7I1w451y7kAEBJKAM+ACDBGiycnfWFrn+T993QBTlbMnn+MzP/Y46ZStQF7eHe5dCKV7f7+I500q111KdS/CoWvuXIO8NjqLcuuEHLlsx9n7935ax/UolasUS1U2ilU2imUq1XrEk2JU0hTaiLiuRFgwMzbAvukhDu8Z5djeUfLZTnfq3j2qN1y+9+YVvvHyef70+5coVWNcp4xxrLQw0kbS6IpKtrmJZI8twszAFs67TQPii3oZv5PxcF1M8osklbd3apCBnmyknOR+CStzmk1OnzpHul6hp97FSYTwqZMnn36+mwz3mu7UdPMAne+J94CbJAO8Tpc0piJMwJkzV3KXLy/+op5Wkzl3FWbbFvv2TbJnzyRHjh/ATtt4rkep7FtcCoItcNLn3D2pwFyLCas1xxDJS59bDO+RAk+qCUVIZTXtaZE9AsdxubpYZP/MEN2cincvaHGtjOPJVs7dvPr3w4N9zEwO09uTawHtzSgR8DvIk9hzfuahwb6Aiwel40WKYNtdp/XcDuk22hb0Z9T1/UZNV1KqK3lpMJfGOtZcuEUmab0ISAIZoukuXVlibaNMuVJTxp2uxJXh+QYtuGAAiBnvOh4Xrq1x9uoqz79wFiTsnRni+L5x7j84zqG5kR1xvkQ2Y/PxR/fy8Uf30nQ8Xnr7Kn/88nm++epFCsbxubdFXXP/2ywu2Ibiri4V6J0f7eqbpdJpJqZGuX7NI+PUu3F5+78Buwrk7wQnnwX+HuHpcZ1Y1X8V5b42iTqxrLf/9t/+f/7WzZtrvxK1oNecu7Kwz2TS7N07wd69U2QyKfoHernv4aMIS1AsVqjXm6HePdCtx/XpaiuZ63qKe2+nfxcisMLXYY2mS7XepFp3cD0PS1jMTw4wNzGw3d9g26jedDl19iaOG+PiJRFOXnqSgYEeZSnfliOTbfWvnr8Aaw2DIwdmyGbTAWIGXD1Rzj14Ntr/u899j1JVLd6khL6eHJ/55BPKG6GRNs55A+E58Ua8cYnkCfIl5MmloO99KJ4H1aerZQfHAFq9aVIa33JLjl5/e8zvHx0LxVKVt85cotGMnYdgcupS4rmmhMlTByD5ZyZ4rhpnrhcuEMyyXM8jm7Y5tm+Mhw5O8tDhSWbGd9bvs+m4/MkrF/iDb53hxbevRBc3bRbVcU7eT2rctFlkJZUnN7Gw75iTN54S6uiGkwfJSH+e2fGBjjl5AOl5nHnrHG6lykC1K8P5z5w8+fQfdpPhXtKd4OSPAhmiVvUm1x7n8D3g/CblSeMvMezVV9/ruXFj7W+2GuyqH7Rl2ezZM8m+fZNkMimEAM/zmNs/CxY0Gk0q1XpgVCeRhNa/ivvTivh6w1NieksEuhzt9Ebp5LUTHOWmtNZoUqk2qTaaSKkOvrGEwLJUPdeWiowP9XS95/NukATOXVnFcbUBlMkVycifJyWr66VNjfASDfM2OX3O8ySz06MhwG/SUHOAmWA6PNRHsRLuiV14/KgS09M6oFr07Mazjm+H1ElRQihwfz+K5zUV6y6OF+9JErj56PKrdQ734yWR76mKUk99vTmOHd7Dm+9cxPUcgu11GKAgZRhuqASIlBeChYyNaaSkUm/y0tvXeOHNq7hSMtqf58HDUzx6ZIpHDk8ycI8PmUqnbH7swwf5sQ8f5PpykT/89hm+8qenubm6mdh5ZyjsO21FpzsHNClL+3xXahdhWUzPjHPh/DUaqQyZzo3w/peFhRNfOXny6XvfoR3QnZh+DuIbENOZuP4cynKxHbUFdx32+c//57/qut6wbUdF9ZZlMz8/wZ49E+RyaSxLKA5cCManRukd6ENK2ChUFLcgpHJMgkB4voEdQmG8J/GE2rohUc+WEHgCpJBYwlLiYKBQq1OuNqk1HN+S2gD2YFJRon/X8zh9cYXjB8Z3lDEQwLWlIoVKQ/WnL6uPgDt6cjQmysQwnV5G74NFlAwWVGY5mUyaxx48EG1UDE0Tf2UGOA8P9nLx6jIAhw9MMzU+FCliE9zumJLKSFkwmNu+o2M9ufMc5ZTrHjV/y1xcbWP+UCPhsV9zuxV8kCEIU2OjryfH8aN7eOPti7i+iDXEd2nkjZWrh5lAnYMQtCUcz4kkYXG9wvPfO8tXv/MeUrrsnx3hkcNTPHHfLA8emggWjfeCpsf6+Rs/9Tif/cnHeOHNK/zBN0/zzdcuRrbkdUZ3cBFwl4q+vlz0Xd62/lDaNaF/eIC+5TWqntsNyD8K/DRqN9iOpzsB8vvp7qS5dmJ6k+LzRRD29a+/2reysvE3hVAAqcDdYm5unL17J8hm01iWrbZyeR6WJUinU8ztn0MC1UqdZtMBIRBedCL1EP5qRXHp1ZqjTqMTAk8IsKQfL6g2mxRKdUqVBlJIbKHUBEIo+bIgnFg8QEjl7NaTylXj6YvL3LdvfMfo50uVBteWChFA19y8aQwVBf12YN4O/BPCjPvHHthPznSUQ+vPV5Js6a5peLAPUMZ2Tzx4sCVv12TUpevWFevnfNoXz2/Dp5RArQlNTxnt7ZQtd8WaS7UZXZQlKsXjCA7o3gsAOZojuAZZjUCJ4ujvP7qH19++4AO9bB1XRnsiiwsZH6PRunV98XJ0Hk/CmUsrvHV+iS9+9Q2yaZuHDk8pL3fHZzkwM3xPnBpZQvCRB+b5yAPzrJdqPHfyXf7gW6c5d7XdhqVdTG0Qu1Jrsl6sMdTf3balmblJ3i3VqKezZJsd2zr86sLCid/dDdz8doN8HzBG9LdqXuMAXwUubFFmu8W+BOQzz3z1b3ieHLRtBe4zM2MGuFu4rgRcpLQCS/mZPVNkc1mkJykUK7gSf9ubbzznT0CBw33/R1+uKje3nqXiHBfKlTob5Rr1huvr/oWfRs0Slt9aT+uNZVim8HvEswSFSoMzl1c4umf0njvzKFYanLm0kqAnD7lvzd23cPQyxinFZ23Cawv3Z3D2s1MjzM+MhZOumVW2iuWTSAIjw314UrLwxFHSaTtBTBwuHuILhsTnTeoTQhnX9WyTobYrodIMx0ylCRkbsql7p9+XQKHqUndM5Azjwu8pgzB1lZG4CLjq8aVTmsgecOf+F/A58b7ePA8c28f33zqP29AcvSQqrtflRsMiDfPv42NYj3FibdXt1VRvurz41lVefOsq8j+9yMhAno8+vIcffmwfjxyduicGfEN9OX7uUw/yc596kDfPLfL73zzN8y+cpRK3zr8TdJdc27ajGyslBnqzCaeGtrZJNzObzzE80s/6skum2UB0tvx/APhZ4D/cXovvPG234d2DwF+hcxe2bwG/30G5icZ2X/7yt0afeeaPXrJtqz8K7qErWyFCwzvLEmQyaZ76Mx8ik0lTKlcpFCuBYVzUcY3aFqfF7YVynabjYglBo+mxUa4FFq6WJbB9cbz+s4PT6PxtfJZfRyC6t9RVG+lZqr6R/hwH50bumeh+vVjjvSurvmtafMtlGV6lYXRnhgUGeZ3r49uFpVMpPvnxh8ll0y0grykAZh1hyt9jzy+/cZbHHzrYGm/cJxnTdfKsw2wLhnNqD/x2UN2BmkvwouYiwxJKWnC3Pbq6nqRY82i4GkyNhVqAyZqrNrnrmFGe1PYdRrgByl4kXZgmarSp0pVKVV49dY5m08WTXrCFzjXGpzaoC8aZaxjpBWGeYYwXNTLVDp20kZ/jqrLaSSH0Q39vho89spdPPLaPx4/NkL6HYphq3eFrL57lP3/zNN9/94ZuIvGbFjRI+O0FC6/WpEF8QtGtYcFCLqmOxNa0KU9GwsaHe5kYiR1gY7bZYDL0Q6Pe4Mxb58nXy90cR3sa5SCn42Pt7gXZ//gf/+PtLO8jwF7jWWxxfQ241kG5WscvzL9f/dV/+8u5XPrjx4/vZXx8MBB1a44d1IdUi0t1yMn+w3sZmRjBkx7Lq0UiVqciWqGmRtOlUK6zUa5zfaXE0lqZms89WIjQgt6fjTUnrk+v080RftqgBh8xDDf41JsuyxsVenLpu76Hfnmjwtkrq5HDYaLGdUTCwAB2PQEbXHzAzQUTfHiNGvFF4x57cD+jw/3tG9qGpW7huP0005PqVEEzLiLeTwBx4xL5PmYa/ZyxYaRne0TpUkK5CQ1j2hCxGwmo00wlthB3XDwsJZQbHsWap868198rns7k1gm/aTx9ZLI1JlqdP7DBMyZjsy6zrHQ6RSpl+66oCdRh0gvHpBcsSHSYDEDEC8Z3KLHzjHHrEVVPqefO9N31psO7l1d4/oWzfPkbb3Hh2jq2JZga7cO+y4v4dMriyJ4xPvOxo3zyyYPkMikuLxao1aNbx3aGsvD2qFp3GOrPb32MsRFtp2ycZpNizSXn1DrthzHg7Gc/++nv33Jj7wJtNyd/AuWzXrK1C1sJ/Abt98dH2kmMk//N3/z/Zr/3vbdfHh8fzpvb5AJO2j98Jgy3yGQzfOxTHyGTzbCxUaJYqhocPD7nHt365noe715eZbVQAUnIgQfcucG9C4Fl+5y6ydlrTl0Yz0YatRWPFte406N9zE8NGifh3RlqNF0u39xgeaMaAfQIuBtcu55AzW10wSlycY4+Ft5y2lyMk58cG2bhiaNAOP9HwCG+IDNX9FolQgjkSSyHGbcZ0HfynE/DUHZ79O+OB+WGApUkaUErqZdTInxxRwzzqk1JpeEGQAcxrj14DoEyHmdawJtcOcQd45hgGwdofwwaHL7nqYhKrcF3Xnon2Jnham5dxhziGJy5lCG3r7fdRbbjBePT83eXhOO16bpm98e+RvjQwon6j/lcmqcenOdHHt/Hkw/M3TOHWI7r8SevXuA3n3udU2cXgwa2vkeMZEI683k7OHlCJiG5DjNMtoT192bZM2V4wkvi5GNtbTYanHnzPNl6hXyjktCiRDoLHDt58umON9rfbdru0TVOZy5sAeooJzidUJxBmFhdLf6ToaH+vOcpX9m2bfmDwkIdJStQh89YSKmOsdx/eA/pdAbHcSkUKz4Y6M0aoUW9KwX1RpNyrcHyeoWl9UoAzngSYRNMRpaPGHqVjwfCUrp9JIErXKSv6/fAs9SkHaQHsERg4CekKuP6SomVQo2pESV+2m4RvudJrq+UuL5cVCdiaW6lDcBrPXycow/FpwZHb8SZKK37Lf4nUYfXtFjTb0HtJiTZEhnLY/zQt8LGeBoJWBIGcsrAbjuo2oSqP00IvxJT2hAsXDDS+CENFxquJG1B2hakrFtfdEiUc5uGI6k7ppjdjw/6zRTXy1jfhxy9LjOQ1sTC43Wb87pMipCtaXLZDNlMikq10SoZ8mS0bhmWEbRFj9UALozx7ne8llB1fFTsJlSpNfnai+f42otnyWVSfOTBeT7x2D5+6KH5u+p5L2Vb/OgTB/jRJw7w2pkb/LtnX+NPv3+RLV+xzeL5NpNuKxXKaodTb77z/kxnMoyMDrKyLMk2a52eUncQ+HngC7fY1DtO28nJZ4F/Cm259vj1AvCvuyjfBnqA49evrx58+unf/YIQ5MyDZtpx87Ztkcvn+JFPf5R0Js3KWoFSuWZwz6GIv1JrUK42gx/5hRvrSKm4fdso2+TQW65twoQQvnhVub3V9wGXb3LzVijetwRYtsXEUA9To323vae+VndYLVZZXCtTb7ghp+WFwJ7I0RsifJPb0VxWIqeecG3h+v3rYw8eZO/seNBODcZJwH0ro9YESvPaTs/ejnu3BIzmlQHc7ZInodRQ1vOJnLvYahGS3BMWkLKVCkGL9OPvFC6yFFfc9CRNNwR2CAES2odFuHYD8EPATdLJq0SKGTcXhv4kYXLyxpgzJQFm+FtnLnPtxkqLHl3r5xXnbujavXbOcCTSd5zjGvk918MNyo2dhpL0JXRfJCSQsQD9nEnZPHn/HD/8+D5+6OE99N2DA6wuXF/n3z/3fZ47+S4Nx438/kKSieHhoky2hict3mId0lLebXDyEshlUhyaG0Eb7yS2I1aH02hw+q3zpOtVejt3d/suipvvdt/iXaHt5OTH6MyFrU6z2mX5U6hDb7K/93vf+qTjODkhLGxbFa8mCCu4Ko7e9Tl8OHhsP3Y6RaPpUChUEJYIuGYJVOohuGvQXS1UcVy1H16KcGLSHLoCRpCWP4EJ32peEilbegTnnruo1QqessDXXJvqFIOb94h4XPMcj+srJW6slMhn0/T3ZhnozTLQm9lyn27T8ag3HdaLNVaLVWo1x5+EZTDQI2LTDgE+mLjbcfQRjsif0GQyFz85NsS+2fEWQG/33I5MAE+M0I/Gs9TfahNA1clTFoxtk/694SqA96ShQoi1X7dLh7dtWOyFXcB1JPUgUTRNCNKxCTmWVLYJ12Etk64J8BgAb96bZcWe9bhEhtdgGk9qg/83ONDL1esrwfgK2mfcB+mlUbcRptl8HYYxdtv1V1e0BVvbaLp867WLfOu1i6RTFo/fN8uPPL6Pjz+6l/6eu3OS3r7pIf6nv/4JPvdff4j/8Pwp/tPX3qRQuQtW+XeAag2H9VKNof7OnRelMmlGx4ZYWpTkmjVsryObusPAT6LOX9lxtN0gL+hcXN/pBs48cAgYAGS5XJNnz17/jOdJbNv/UUoLy1LPnhcCv2Upz7q5XIa5/bNICcurBVwpsfw9WNVag0q16Z8up8BdSnA9j+X1avBGCrBFMPl4UgQTswZ/4QmwJNKflb3gB61QX++m0UDveQLL8nWLAuVMJ9DJ41vjGzpaFU2l3qRSb7K4VlbAY1ukUhYpX2phWxau51H3D9Hx/IYEExd60lPArUFeP7eAfBuANy3vw3DDsClihEcYHyh41bUnl+1KTB8HZRMA/E8UeTYzynYovkUdAPmU4uC3Q/9eaijxfEQc739ncz0ShBN7X90+jIAW0hZsYVkmmXgVuSeSLQL08Xtzq1wL+BoZQmM3H+yDMmQ0PigvVq7/LaQXti3ccqnOKQhE7v5YDEDbGLNIWtJIKZEiZkOg0wfvEvbR3RBBNx2P77xxme+8fplf+/ff5hOP7uPPf+wojx+buSv78EcHe/jFv/hh/tqff5Tf+5N3+OJXX+fGSunOV7wFddv3i2vq2PBuumx8coSV5XWqmZ5uzpz/+/wAgLzWx+tvsJUTnK04eQFMA7N+egeQzzzz1U/Vao1RZZ1qGSCiwLkV+C0OP3AYK2VTqzcolmoIIanUHcq1pjqD2herB5OolKysV3A9D+VFzz9sxgdBSwoFfP7bWqi6PSSWfzCNBYb+XYWZQB/s8/Y5dmmFqyBVo7IVCHT2Gvjx3emKcPL3pEvDcVv0xpEHEQJ7yMlEwT3C0RMF+HDPfJzbj4eFgB7n6JGC/t4c/X15+vvyDPT3MNjfQzqdagWVTZ7N9+oItGUInAF4w6Z74uM0mFUe7G6XXA8KDWi6IffeEdDr9zBmuXaTnfkuSYnizGgrOIehMv4djLDI7gmdL7bw0KDe0l49FoM00fIj31tCo+lQqtT8RaUh7pdeIBGyU7baMx+MwbAsIBiLOl7SyvWbYy18J38Me5E32JK2azHQaLo8/8JZ/uiFs8yM9fPnPnqETy8cYTK+TewOUE8uzV/+8Qf52R+7n+dfOMe/ffY1zlxavuP1bhc1mi7rpRrDXbgittMpxsaHWLy5hmvZnXLzCwsLJ546efLp79xyY+8Q3Utx/comZfWirPRzKMZXY6L39tuXf9Z1vQBQohy8DKrWwN/Tm2f+wLzi4lcKlKp1KjXlR15bsavJU82KEsWZrhaqoejPXwhEdIsiOjFosb0nlajd83vA8sCz1cLAQwN96JdZi+jxQFr+YkKAsCTCRwDhzxYK5KVqs6kv7gLo2nHyetJuAXsZ5eI1mIf6dYIwE9wty6K/L0dfTwjqfb15tc0xBtjmBN/Ba+B3V0SvHsTLmN49XnAXs64eTWM92+PgpuHCRj1sU9LCIgB6WsMx49hELUHYT5Hg+LO+l/H7EJRD4I2BfgTEw1LjoK3HWvDOMlxkBuXEpAFmvqbjcunKIleur+A4bmAIGtqDGMfL+ga2Jhdvbp9Dhtb6+mUCjj3G9YdxoapA+r/N/v5eZbjbMrgMMj5OJpNmoL8neMe11QJulwsGTdeWi/zL33uZL/z+K3z4/lk+89GjfPSRvXd8D75tW/zEU4f4iacO8cKbV3jmK6/x3VNX7midt0Pmb2NptcxQb24r45YIjU2MsLK0QS2d60Y3//eBv9BVQ+8CbSfI99GduH49oQwBTKCkAgIF8DpcfP7zv79QqVQPamM76YOJBntdhRble57i4oVtUSjXOXt1VeUR/rnw+CJ3n0XSVvaL6xXlvjbiax6kED4Iam5QieYl2oJeWV17CCxPKtAnfHsLDfQyOPNGaHWAn07r4bVOXveYMMT1wgiDkPtrSyaoJoE8IXirBUsbYE+41xNtOp1icmyIvt4cfb158tpoKAHQiT8ngJFs87zl+xHLu0l4HGTjz2kLxnvVFrXbpUoTinWC7xQH9bg+PsK5x9LH3yf+ool9ldCHMqHfzXsZS9fC6RodqUXwxONl+FbxbxqkM9P6iRzX48r1FS5fXabpOMZxsoZkiPhCM2yjycUHaTCAXoM70W16GPkBUukUY4O9DA70MTjYz9BgH6mUxfmLN/j+G2eTerqFmk2HPfOTTIwPI6Vko1Dmte+/S7WmLCZuheP3pOS7p67w3TeuMNiX5cefOsRPfuyY77v9ztKH75/jw/fPcebSCv/22df4o++dxXV3pM0ZAA3HZc0/wEbTZv0tASudYnRskKVFj3yj2qml/U8tLJw4dPLk0+/dZpO3lbbTGc4CyjjOhBuxyfW/EO1rG5gBBgkt8PWfB8jf+I3n/mGz6cyDvzL3xeThtCgj4dlsisc+9jgugvOXF6lUGr5jmtCBDT43L3wluONKri0VACMNBPks3XotQ/cvgaMboZcOYT6T0w7K0/E60SajLjIBRwA7nNiCuKQ/M04DuuqtqHjeM0EcQ/y+OcBL4KH79jI9oc6RT2u3b0ngswmAxwE/sR8MareuCUfD1iL5QBUSe86nYKrv9g3spIRCXTm4McuPt7+bcN0PQm7SXVKCEMHYiEbJlqRBubEyW4A9aIMM8sSBWhqFRPJIM41RrlGhKyVXb6zw9pnLrKyV8AKpnTEGg+1sxEA7+hd3ghOCvRrrXtCmEPT7+vJMjA+zb88U9x3Zw/Gje5mdGWdkqJ+enpxv5xPaAKysFtp9gQgtLq4xOjrob/fLMD09RrFYoVq9/XPhaw2HN88t8uU/fovvvHEZKWF+coDMdrlfbEOjgz38mScO8JmPKb8Wpy8t37KE4k5Tre4yMtjTlW4+l82wvLyOkJK0u9kZagEJIPPZz376K7fWyjtD27mF7peBY2y+dU7fN4FfMfJmgEn/GiJkeC/+9b/+L0eff/7l3zOd29h2dJucDlc+610OPXiYj/zIkzQdh1NvXwzcygbb0vQWNiGwLBDC4sZykfVSLYiz7ej2N1W+4Z42ydFNcM683v4mInW1bpczHfAYV/y9+Zq7NxcluoOCRcPmpCc7gTEh0yqulzLcC9wC7F4y4O+dm+DA/EQrB70JoCeF3cpzIogbHRJ/1iuAJCDVYYM5GMl3Jd1LJE/CWhX8g9paFhJm2Jbh8XcIKAbYfrIWII8kbWOtHvRtzOgMAiTW3z2I06AZxBtAqgHUKDswgCM2vjzJ9cU1Ll1ZolZvhmJ46YvhfT283g5niuzjYYFLW3ObnJTB2fJSSuX8xh+/c9PjTP7/5L15mGXHVSf4i/vey31fKysrK2uXSrWoSltZKcuS5V0G3Cy2wRjafIIZZgZNYxrP97kxDQP012wNNG6muwcEbdoYMCB7bLe4OPB1AAAgAElEQVQXDJZl2fIia9/lKlWVasvKzMr9Zb7t3pg/YjsRN+579y1ZEnTUl/XujThxIm7cuPE750TEifEh9Pd2IZPNinn+CJq/mCKIrO9G1fuxp76H8+fnPW8C+rtSoS2Xw4mbD6Kzs0MKGhG+d+o8zpy95BVwuf2fX6BTQpVdLDrasnjTzQKAjx3Y5svZ8nB5cR3/+e8eweceelGPIbKKVuW5e+X2Q5WagE1xfn4GVjkc2DbSi2GpzZs+HyvVYnP+9EWsLK5iYGNJHy1eI2wC2Pnwwx99zSxcaKUmfxcAes5ftd8CgK/I+04I83wGCRo8AP5Hf/TpDxUKpYPqxSvt2Gix0nTOIywtrWF5eR1v/+G3oKOzHRcuXcHGRkFr2Fqbp9o2GMqVEOfm13QlFahSGpXXRQrmeUoXfhkZwJk1jBMg8vQj3besQdignaXNqz9u/pTp0lpTAAXSJl5pPrE9ykgG+N7uTly3f4d+uGoAH3veWoDuoXcDBUZFwgCv9zt679PeGQPGuoGB+qbvvKEcAgsbwoudD7zTAro3DeS54EQm3XL73biXdGgzfYmAPXfp7X6paGx6ezj3rrbnHJcXVvD8S+cwd2UF5UroaN72Wo+YVq/6eASdRvPDp8VrnsIt7okbD2otnX4j6jkiq/HsMDY2hOXldWxsFPwEJIRRhPmFZUyMDyOTyYAxYGioHz3dXbhyZUXvgkkK9fTJShjhe+eu4HNffwlf+tYpFEsVTI71oatj65zt9HS24c4bd+Oum/dg9so6Xpld2bKyGgmbxQqG+jvrOgCsrS2LxSurCHiEbJTKqV0OwMY999z9YKP1bHVoJci/DWKbm29ccn/zAB6EmMdXvgdjwK7u77//obFHHnnxtzhHYLyvyUxEgygUipifX0GhUMLRmw/h0I3XoVIJcfrsZY3LShNWYE+vL13Jo1CsCAC2tGbic94kWfE6mREApxq4EzSomztzSUZvd2CODbpqsIQN7LQBrcENBvQtUNf5bRMnuEejl/eMMRw7tFusjnfq6gPrtIBOM3jB3uFBQRxIAHH319HmMwyY6AG6W+B/ZKMMLG7adU8Eek9dkugTg2yEGOAz8U51d3L7lCKl8Tz5/VBg13hN+46PXqUpOtjXjz19CrNzSwLcIxvAletkvWjOMdOr+flIRph8pj509Tztu5xzRODYM70NI8P95pm5XEdDBYgq2MsYw7axIcwtLKNYrG3SrZRDLC6tYWLbiD5ro6e7E6OjA7iyuCqOvW5xWM0X8chzF/BXX34Gp84vYmKkF6ODW7cyf6ivE++Y2Y+brpvE6YtLmFtKvXBtS0PExW6r1IIOB7LZLDbyG9isiH3zKcPh++77wn+65567XxOublsJ8ndDgDaQPL6q6xWIw2k6kQDs9P63f/uv/9fNzeKMAvQo4hJUBdSGYYTl5TzW1jbAuTClv/M9b0NPXzcuzi5ifV28HHOIjNKxjTZfqXCcn1uVdAqcXfCOa+y6HsqErkE+YYR2B2NZL68lyImLafSOxuH/k0AeOZqPNTjHtRzvfDxszeravZPyvHY/wMMTlwjsLk2Veze42nyaQIE+GwCTfUBHk8tQOYDVgpiDV4XEJN4qQG/d10jjQNU5+VrdyQv6qg9QOvoeuP3rAjznhIfzS0umwH/yzKycCnJ8MBBhM6bJ050deoun5O3QWVq94geI/fZgOH50H4IgY/q3rrMca7hrDI+HIAiwbXwQFy8tolKpvd2qUCxjbT2PbeNDUG8115bFxLYR5Dc2U1kFGgkR5zh9YQn/34Mv4LEXLmKwrxNTY/1btu9++0gvfvDO67BvxxBePLuAlfXm1x80GwqlCoYHuurIwZDNBVhaWkcmitJup+sC8Pw999z9dGO1bG1o5b4LdUpctd8AwizfLn8r8i8k19b9wsIKX1xce2+lEiIMI4RhJA6NqESoVEKsrW1ibm4JGxsFvRBs9/6dGN0+hjCMcHlh2dZyoRbnGWmeA5hfyYt5N9+gogcKaM3CNhsSj3B0HpHD2moWkYFH54noFiATFzsK0/On5izFMZqeP+fITOpCturRr24ep75RxDE61I+JMbGSNwngE8GZp6CpEtw8agdDI0JCLgPs6Gt+BX3EhXl+rVS7PciPAVUXcN00AsAUTCmxla6iLbDlWqCzyuM2HU2zAd4stOROusrhjVf8yZ+pnwPanGjcEbfvVV8EyDcK+3uiwgFtK04FXQ4WMBy6dhrtbTlSSft5efwBE0N7extO3HRQW7VqhYWFFTz3/BkrLpvN4OiRfRga7PPmqecbqRUee/ESPvj7X8T7fvlv8T++/hIqW7g6/s237MX9v/Wj+PAHbsdwf2ftDFsYKmGE5fX6hKie3h50dLSh0FaXo4yfrquQLQytnpPvR3VzvQL5MoS5vqoGDyD6+Z//ozcvL6//JEDBFQhD4Z5WrU41C9YY7n73W9E/MoDZy4tYWc0DMOfEa0O7VssZEAGvzK7Ij0ie+25s+9rkrvVztTqfPKW5VuZ7Z8bUKy2zqsl0MHQ1JDVwKTr3OooMjdHICY2jtVA6PUjqe3uwbcvlcOzQbmSCoCpA1QJ97maqQpMGsL2NyEw+uiYCEHvfJ/uaX0FfDoH5vFhgl6SJ19LoXXo3zb3nsGZ1UsX73hMFZpMeBzyoPiLpqDBh9SHFh3Nj9ob9p/Nw4My5eds8by2ic4QAnY8I4OAWeLtgzkE1efE7OjyAW268BsND/dY3ZSsCop50AVmt0NaWw9jIAPL5TWykWDW/tpYHY8DggA3qm5sFLC9fHe9yS2ubePCxM/jMQy+Cc459U8M13WQ3EoKA4dCeMfzImw4hl83gudPzKKewemxFKJVDDPX5hQ3/2xaLqZdXN5ENK8ik20636777vvCJe+65u1737S0PrdTkS4hr7fRPHRPLIBYnVNXg1f2FC1feLzT4UGvw+XwBi4vrKJUq0NqF/BubGMHUvim9Wtd8/A6QRUpS51hY2UA5DA1dxB3tgWrk4i+ke8lJGtWYadlRZA9k6s/N49WmyerguBZvryoOI7oqmWr6Hp5JmjuxRsTqGwGHDkwhl800D/DOdRohwA1WHvfX5UF49bQBk71iLr6ZsFkGZvPigBnvMyMhnlend6qr7y2gVL8OvQZpArAUbDUvQpMYRwEQtf+gQNLiaSpoCaQgwOwAPAVzapo3a0VkvHad7LO8UcsZkGvL4YbrD+DmG69FR0c7aX9uXdNxot7Q29uFEzdfh1tvOYTh4f6a9KdevoALF+etuO7uq6/tzi/l8Yd//S183wc/jv/0N9/G/PLWzKN3deTwsz90Ez73ez+O97z5cMtP1kwTiqUK1uv0x9832IdcNotCW13v5mfqKmSLQitbuIza5nrI6zakMNV/+MN/MlYslt8ShsJUXy5XsL6+iUJBvCB1WhQdFE7ceQsYY5ibX0a5VLG1EDkAyRg9IMwtrmvt1zLBOwMHdRJDBQAX6FXe0AeeMQBOAFkF3KHY9hOGcfCuCdoU8F2hoJq5nvvN/JxzTE2OYHCgJxk4SDxIvBsHJ67acFpVCKiVzqU5n9wPdADbe9H0XORyAZjbcEC2HqAn8b42dNNooIBM+SWCNM3r8PbxcYUETvORQqgQILJxEicIjJCgIdVcO/3LBXhVlHU+gp4ag/W9AXHHNpGsy84do7jztusxMT6kV9LF+rD1jmrPxVcLg4O9OHHTQQH2Q9XB/pXz9onbPT31zBm3NqxvlvCx//EE3vWLn8Cv3/cgTl9Me8RIfWGorxP/5gNvwKd++8fwlhN7t6SMamFhJfV58QDEduvh0X6UMzmEQWpLx7+cmbn36p0dnBBa6fGuiNq+65n8bYPjzc65ZgDw6KMvvS8MwwxjDGEYxlaemsFJDA6dne3Yf2QfOICLs1fIQCKWXnMAjCuXsqLIpfUCCqWKdjUbReKY14hDnv8uTosTlWOIInFgfACOSPqi16fHqXlheTa8aAQm3eKKAUhPGwQAI/fUAx71ZEc93UHdy5ayPOIBiNl3ueeSDMa6DbUmQ82cagAVv4P9PZjcNoTx0QHCw8+/FsCnBn3PfSw4z+jbN0/bZbRb7IFvJnAOzG8ILR6A1y2t7yx4eOKh6ujh46ZZFRCphtZtBxrFbRoqAMRYkmsLvN1+Q/5AaDgpywVM/b2S/qX2sdug7rumc/aqjzqCdaQED5PW092Jw9dNY6C/18zhU0FE933a/6uvqE8VZP7BgV7cctNBLC6t4uSp87hyJe48Z/vEiHXf1dkOFrC6fOW3t7dhYmIUmUyAkyfPNVV1QByQ85mHXsBnH3oBrz82jZ94x/U4fs1E03zdMDXej9/5P9+GZ07N4T/8xTfw2IuXWsK3VsvlN0soFCvoqGMxzsBQPy7PLqKY60BXOle3YwDeBeBvUxeyBaGVIF9Get/1gDDZ04krC+gfeOCJTKFQ+gAgpfjIPw8itHlxGM3RW46graMdy8t5FIol6bqWyOQOwAMcs1fWhaMNcCCQB8uoD13uQQr0sbBcHkbDRRq4dkWrgZ3LOUEC2mYlvnCdy2RLMAAsIJKRAnGzHEACPPFjTwAf4LHtedR/gDuHa9rMHn7NQK4GP/GbzWQwMT6IHduG0aWOukwC+Crx9cYlCgFIpvHe07oxYKJXHDTTTKhEwOW8mIcHksHbB/Qg8TqvqZ7m46apfDRB5CHvDhzySCNxT98HVxSGqd0fbFrDwwC6oqM0nNJ43ikVEIwAYNYAcCC2ba4awMcW4GkaWPTKEQ5jDLfefBDZXMYI4K6ZAyReP2sCRNQJ/JR8cKAXhw7uxte+8aSVEAQBJrYNW/kYY+ju6sTaeg1tkwsPfZOTYxgeGYByzF0olHDesQ40GjgHvvb4WXzt8bM4snccP/nO63HHDbv02NqqcHjvGP70l38Qf/eVZ/F7f/lN5De3/njbhZUN7Bjt9Sd63nUml0VffzdWlyJ0FjfI11Y1/AxeZZBvpbl+A+lW16s/1w2T+u4jAOEv/dJ9d4ZhNF2phIkAD5iPG+C4/sRhACBz8YKtGWCM1yoOjvXNEtY2SrFBxTIfahOhM4g4A4+1Sj6yV9bb89zULG4ftuGbA3fN6WEUmTl2D221OfkwkivuuZ+3+u3r6cJ1B6Zw+4mDOLBnuwB4+XbUS6IvbCsA3u0YiQDu6USaVv4GDJjqax7gCxXgwpo4aCZWhyrP5Yt362+1ZUI+03dlnMNHaKVUS/UAtUNv0UkN2AvQHh4WwMu/iFzrNNh11t+szhMHeHdtS3wKzV5dT+MBUY/t24aQzWWswt12tNtKXNRSoHmN9KRw6vTFWKcdGx20V+TL9J6edOamo0f3Y3R00BL2p6cnMDTkX6HfTHj61GV86A//Hu/58F/jgUdPt5w/Y8CPvOkQPvXbP4o3HN/lpWmw6b1hNV+oe/Hf4FAfOGMoZVM71HjLzMy9u+qtWytDK0F+Fum20anf7TX4/S9pF75wzjExNY7xHdtQKlWwtLymTXbmI6amaBE5u7Du7KWFpSnQhTsx4Kdg7tE2FJiqbUDavSa9d4QDepqWu9AuPpcfadCvtnAvdECduvmkaUHAMLltGK+74QBuvn4fJsYGhZ/uKqCjLpKAvyGAd659+VxiLx95nw2A6YHmndysl4BL6+KoWK8Q4qlnNUCvBea12j0RPCkf+f2obaKGhnwbpH4U6DjsurigDfdeiwf29waSn37OineYAPBmfp5bAG/81nOPFcDe+rpzxxh5Fu70E3vtgPZl31IYMWFzs4iLl+KeTicnR2NxHEB3V+3tWtlcFpmM39x8YP80ulLwaCScubSMD/3HL+Gnfu1TeOKl2ZbzHx/qwUd/8W785v/xFgz0bs0zAKK/XFnZrCtPd2832nI5FHOp68XwKm+nayXIn4fR0pNW2FOgTwT5mZl7xwD8QNqCOQduecPNAGO4PLckBgcYjUGboYkGXihXsLS2EVtcF3Ey50f8V9NV9lQo0EBMBibfAjm9OC/FwrfYvnnO9eK7KOKIQpuH8B3glEkEAQ32bt6Qo6ujHQf37cDtJ67DNXsn0d3dUTcI0TR10Qjo+/J6khMB3aojF3vfdw007+RmuSBM9BQQ0zw7pfXWkWR0n7Ua2CfVw20Tf/k8DtoufxpPQZKT74rQKOuBAXMep6F8QWjdPh/RxbS2pm65VtbfnPudi7Seni4MDPSCw/F0Jx+avi/64Ol2R8XfVa1w6vTF2Bx7V2dH4p74NJp8Z4cruRr+mUyAa6/djewWbIdT4amTl3HPb3wav/AHX8TLF1q/QO/u2w7gM7/743jHzP6W81Zhea2gF21We486jTEMDvehksnWswDvp2Zm7t3a04KqhFaC/AXEwb2auX6yCq+fgJizTxXa23O45vprwDm3TPV0sY6W3DnAeYS5xTzCyNAoDZquotcObVygDz2DUKQOylD3Jp4KApY27QN6B/RDysPJR7X4MIoQhUTzDx0rgGsNkNdHDu7C9m1DltaeFty9oO0D6RQAnxr0UwoBnTlg90DzTm4WNsRf6mdIiKf01YSAmmDvAWSrTSiNy5e+Hwd8vbxVugvasP9s5xbkCTg1/1MhwZ4mcIVs17Im5tjFdegCPBGgzS4XYHpqzBJkTPtxc/qcrK95Lrv169Lqq5AWCiVcdLbJAcDk9rgWr0JPd+0T0zo6PfNPpB4dHW04cGDXlnm0U+HBx87gvb/0SfzafV9tuQvbgd4O/NbPvRUf/dA7MTbUele8YRRhNV+fJ77+wV4whnq0+e0A3lFv3VoVWm2uryC9ub4ayP9oPQUfO3EUHV2dWFxeR6FYMoMG1JYarj9iDgHA80t5ormrAYOY+yI7TZnPY9vmEsDTayIn2opt9vdchzV4u/mlQKDm7ENVLk/wqMfFivnuzvh8e73gXi/gNQP6aYSA3nahwTezBZdzYHZdaPF115vEewHdE58a7AkDC8xBAJzQW+APzx83oKtAEBSQY7Qw6aQwTv70YTEO/xioVgF4IyxHnu/SNvHbArdwvLJjYhjK+uC2h6lr5Fgq4A+eflhPePn0Ra0tqsAYw/btIwk5hOKyf/9UVb4dHcmLTFRp/f092LWr2lDbQPC0RxRxfPrBF/CuD/0l/vCvv4W1jda6sL3j+C58+nfeh3e/6VDLhZal1fpM9rn2HHp6ulHK1rXI5711FdLC0EqQ5wAuIf28/DZ4VvfPzNy7B8BN9RR8/LZjAIDZy4vQc/GIDzRqEFiW2+aoid0MIs6AQgYVe4EcXQwE79y+AWB7rjDJTG9p7mQACwng6/3y1MzpAfyqAoiMFwNhDVAhiYngngB+rQR4CpBJNAAw2AlM9YvFdo2GkIsFdpaLWlJwUl1dgE1qtyQwTwJ7qw7cpqMgrrPS/OT9+N6XBXzuNTd8TVnmmwL8vODwMXScCARSCCf9OLbwLorP0ccX6dkCAucc27cNI5PNalfHnJTpNrhuq9QoXh/cF4plnLswZ8VlMwGmd44Tl7r+8ndMjmHnzuRjYuPmel/gGN82jOHhgVT1bUaYAYSjmf/2ucfx/b/wF/jzzz+BUrl1Xu16Otvwb++5E/f90r/Azm21HQ2lDRuFMop0e7bvo3euh0b6ENW3AO9dMzP3bt0Cgyqh1e6G6Lx80ny8+sshvsIeAN5TT4Hbp8YxMb0dxVIZi0vrRJuIzLUzMFxeXHcGDWeBjw+U6SK7SPCnAGtr68o8T1YJc26DsscEH3Jbg6cr5V0wt4Dep/V76Gg52WwWYyMDXpCpBTScXCQCP6HzgRKaiHMBX92OdUknN4m9pXaoRMD5FXGSnAvS1Z47DaDT+KQ8sXRux8Xal8vtn3DA3eXvgrsGWhMZEwhUmQ4wGyHAWMkimAQ7jy0QRNzlS4CdfD9ex08egLc8T8r6CVO9I4zAqRu4dW3aPh3M8diFP7x85iKiKEJvbxd279qOm2+6FnfdeSMO7N+Zqpy9eyYxPj7sTevoaE9d3917JtHe1oIjFlOG1XwRf/CX38S7PvQJfOZrL6ASRmRMbY73zddN4v7f+lF84PuO65P8GgqkHkur9fqz70Yum63HZN8L4O11FdKisBUgX2v7HI332ZHqMmvc+PobwOSCO9XhORkM7Gvht3hxZZNI/tDzfFwCoLXojmgKdA5cDzaRKcMWChzNOSSafjWw5wrcIzs/98eZOXnJMyR/zhQBBf2p7cPaY0oMVJyBXgULZJKAzhePhPhGAd655gDGu4Wjm2ZCqQK8sgwUQ7tstyx1kRroCX0M7FO2N21zOkiqS/fQB3A1XWXzMkDOY2VTAQBE+HXTKH8L8C0QVeV6NH/CML663tbKI+u7jNMZgBdCeE9PFwb6HY+M5BnUs6t9860x0ydD7eBAD+56ww247XVHcGD/FAYH+1KdZ075Hbx2GoOeBXqddE6+Rl0zmQz27Ztq3s1jreA05uyVdfzKHz+AH/vI3+Chx8/qtUTuYuF6gb+9LYt//eMz+MSv/wj2T/mFoHrC8noBPG0lOADGMDDUi3Imh4ilhtFXxWTfapA/h/q20e2gmWdm7j0A4FjawoIgwP5De8E5x+y8vbqTSvZmsQ3H5St5j5kdBNzNXLbQ2HmMXmvuBGBtYAfMSXA2uNN73yr6+AK7KOb61t5yl7zNzmhBNg0Hx5ScD9SanGewB5y4BHCvCnAO8Fu8PLS14uDEcQDbuoGRJj2BbpSBsyvCB30t8K71DL74pDRfvkSwhwtW0O9E11Fe051+WmOngEvft+IBAtROmhEOSF5SAQ4qaHDDUwMxAXrCmwq9rtto2820B+BdE3/EsUstuOPSwkGFEW76e1J/9gdO/q8dKN228WG0t/vWEHOrb1UPDIcP7bFW3AcBQ1tb0tpkP9Pe3m5Mbh9LU2DLw8lzi/j53/sCfvbffxbPnJpDJeKohJG2QjYK/If2jOFv/v178XPvPpHgBz/dWwvDCGsJC/D8HDj6BsQx23Vo898/M3PvVT+YoNUg/wpqm+vp7w4nf12m+p27t6N/ZAhLy2soFsuxdAHaygGOGBhmF9f1fDtdrcu50Qqss60J0EeUPjYgebRsj5ncAltnkLPM61I7ifOyj5BN9GfvlKsW5UWcY2yoH+3tOXvAdtsOZiCsCe4ODyvNiVcXtcA8bdxEDzDcJMCvloBzq2Iuvip4I7k+Sc8Yo6+S5mtPq02dCEtjdkDU4kPeIRx+FvDyeDytt4+GgrnR/gnY67xUQOEacK/dN4WDB3bg4P4pXLN/Ctfu34GO9rY6AV708/HRQUxuH7WtC7TOMYBPC9ueUDNvKunByZGcIZPJ4OjhfehoFyb3jvbai7583CZ3jKHb6xu/ibaooyKPvnARP/Xrn8Jv//nXsbZR1OAeRrw68JPpGjdkMwF+9oduxn/7lR/E2KDfnJfm6ZbW6jPZd3S2o709h2Iu9QK8bgDvrKuQFoStMNcrvtW2z6k/F+TrMmccveUIAGB2zr9H0wxConMsrRZQKFYccHfAMQb0xnudAnQK1qGTzz4QBnHw1fk9GrgD6GHIbSGAU2GAAL27XU4JJMo7nlPG9NSYHujUdiJ5WxW8kTJdXcTAj+SBE99I3Lae5v3QL24CF1ZsL2eNAD2N5zXo07SvAU6HV6xd40AKiwe3yuTc0fLhmOUlof3tcOFQhwK85BGbJoApiAOWbwpTjvzHgV1TY5jeMY5dO8ewe+c4du/chltuOIBsNrC/BeeXAvz46CDuvO0oTtx0LTJSm3MB3mj0RsBIGvjTYn9aWOSeq0ZCW1sOR47sQzabRUdnW3qW6p1CrOrft3cKwatw+puuDgf+7oHn8L5f/lt848lXUNFjWGR2B4XiTwO/FgaSgf/YgQn8zW++FzdeW8vPmj/kN0v2QsFY28Ybu7+/BxELUMmk3vF91U32rX7TZYitdPWY6zMAMDNz70EAh9MWlMkE2H94HyphiKXlPNTA5AYK2LOL67JzKM0A1rGy2jyutXgF1B53s+4f1Soc0KXAT1fsWyvpCaC7v96jYp0/9XFUyAfiq2dXZztGhvocLc7MzyogaATcfUBeC/AaBfiJ3uYB/nJebJNThaStB/mx45PoG2hPl8YCcEKvwVhfc62h8oQ/lcECbNUfCL+IlKHjqbZuxUMKw/a9KZcICeR4WOuZuCizq6sDtxy/Bowxbe3yafUU3Pv7u827UXy5FN4o4HNTF9PQ1XRo+jJrkFBpuQofXovGVCtG2NXVgcOH9qC7y3T+FKys0N7Rhl3TKYCQp+ddP51YAP2v/+MX8Wt//ACW1jaNBi/BPYw4AXY7LQn4B3s78ScfeRd+4h3XN1Tr5Tq1+V5psq9jlf07Z2bubf2G/yqhlQfUqHAeYkEdHecB/6l07QCuA/A06pRwpvfuQO/wAObml8GpmyqO2PJqzjmiCseV5Q1x4hsHoogBgahKEHFE8qCYiIvoUIo/QSRPmQu4PrJUPwSHPqiGRSCH0ohDbAKYA2/AxGE0qo7iIBquF78xBjB1aI06pIabdTL6l1wD8QNqYKXTRhA/O3eMiiaSdeKyjKSPlDsXPrqq4CcvUsfXiGMQAD/QxGYUzsUWudWiKSN2WIy65vAePgN44lVdybvS9VbtDbt7WulA7IAaQB7fSPjG2oi8AO+74PFrCnKuJceK02l0eLY/biU4xOIdAcIGfcPfwkZ5P9DfjRuv34tvPfpSDOC3jQ/iwN4d6O/tIovvVFk+DV5Mu3FAWyS8ITXwpqBLR4YU4oXNjwN9fT2JHvHSChDDI4PIteVQKpVRqYgTPs2vuK6UK6iEIaIo7dM0EDjwpW+dxLefPY+f/7Fb8cYb98jxi8sTN9UHIU/shJ2maKEO/5Lkv/j+23Bo7xj+7z9+AJueqdyksLpeSDT5+0J7Rxva23Mo8SjtyXSdAL4fwF+lLqTJsBUgfxbArYiDOuAH/pvRAMgfuVmY6hc8Rzf6wsJyHmEUCVBkstbDRsUAACAASURBVGIE6AW6q8GWaeAHZJw8bY4z6BPiAAnUAYuBeRABEeMCCBRtCPAA5HS5CFx2XHr6XEDBndRBgTGj3xzznETnfJMKWDKZDKYnR81AL+PVwKVPsHMzu3Fw4hyaaoJBVYEgBcBv7wX6mwD4iIsFdhslwTAG3g4QJwK9qpsTjxp8AD+QJ4G9StRAb0XbwAtSbhzoeez92HPUtllfRlnvwBIGPNYABdAmj7EQxYCd/uq8xJTOxeEthw/uwhNPnQIYMDE+hGv2TaK3p0sAduTMuctniLhdFwvwSec0ggzHxmYJGxsF5DcL2NgoYkP+ZrIZjA73Y3R4AF3d8Y6XFlRTCQeknrUIWZoV3bx61fr6uq22U0XrfqHaM4xQrlRQKpUxe2ke66ut9WoHiPnwX/l/H8CXrz+FD75vBiP9ct0A4+Z4UnkstwX8BOgp8HNwvPXEPuydHMIv/P4X8MrllRS14ChVQmwWy+iUiyX9Qpg1AKN/oAdzl8uoZHLIhqkEivfiKoI8a2rxiT+8EcCvAFJwrv07OzNz7x8CeCZtAZlMgH/16z+Hrt4efOfxF8lHwczASQZJBuC50/NYWi0gCJj5YwyMMQQBELAAAQOCDAOT1xnGwBRdwLSWrmhZIDqU5qM6IWPyCFkmOyalI52VMd0pGQQ/APoYRyNIMA3kpsOT56uhzXMu8u2aGsNNR/dCoRttKwZogYP2Ybd31BIAKMB78zYB8JN9QF8TJ8lVIuDMsjhNTvG02oHEkZ/qtNXoG0iL0YAMNPSHWykWiMtbUCDTtBaAc4s2DsYmj21a5zZwah6GzjKLR+Y6koVEgPblrn1VyDidn4vpstPnLmOgrxu9PV0AxHRbRGjjW+8gD5wRtMVyBRv5AtY2NrGeF+Cd3yhgY6OAjc0iNjeLck1GfCykw2NnZxtGhgcwOtKPkaF+tLXnpBBj5vuttqXCB3kfdruRPLq9PesjVDxtVyTkcd5BXOCK18fUw8fb5OHgWF5cxYXzl1GpGAcytB9a7ec0K3evPONBT2cbfvaHb8bd0me90eAhNHZGx0EX+JW104yh+c0yPvJf/hFfe/yMVbe4MCXuh/u7MD7Uo587VnuSj3OgVCji5Evn0F4uoDudNl8EMPLwwx9dr0nZgrAVmvx3IVrD1d595voAwLZDh3a979lnz6QuYNf+negb7Mfs3KLnRZFS5XuvRBGW1gpyQAAYFx8/AlejZ0AEaZqX5h8ZLUBQelJjXGv9TNq8mYxnARPxsgWYfEiq5SutXP+p+1AQcy61c646LEjnFc8U0+YdqFDTEjTs3jmuhnVQVVObgj3ATJvTvkgG92ppqeNJHGPAZG9zAF8KgdPL5hx4WpjXHJ9So0c1enLhpul8QFXNXufn5p4TScydt+Xe9jeNyWHzosXoaE55w4AMSVMJFEyo1m4AyQyUhoabeBdsJL+I1Hd6x5gGn4jy1zwowAHLK+u4dHkJl+YWMb+wimKppAUAbx+vMoTQsLlZxCvnL+OV85cBDvT2dGFEAv7QUJ9e9GflT6FEpdPy068bSMOuPjqbcmCwDz293bh0cQ6LV5ZTckkf1jdL+J3//nX84yMv4xfeN4OJ4R4B7rI2RkES966pnsl7FdHRnsXv/qu34c8++xj+6/3fRVSjwVfzRQHyVkjO09bRho6ONpQ4B9KBfDuAuwB8Jg1xs2ErQH4NQiu/HunM9cGePdt/oB6QP3LzYXAAC4ur9uiZEBZXNuXgxKTEzxAgDvQsENoAogiMBUJ7B9fz7wCT3sUi8IgJs7o0v+uBO4LocIEEaVU/JgaZQGnuEjUCFthArublVedl8poDwjbgaPH6P7shqEYKAEMDPRhUTkKAmOlXDbbUKhBPj8dXA3fCtmq+WgC/ow/obcJZVzEETi8JTd6tm2q61PPuGmTT0cNJc/P50m0aHm+/hDazTfU8Tk/BWgMvDICQl+Wa3VVeF/ipBhrTPGG0P4nFAOfaOx4VCijAW2UoLZ+7dTJCw8ZGCZfmBKjPzi1hc7NkL14lAN/KsLa+gbX1Dbx85hICxnD9kb2YmEj2R889V9XpqofasE/ecRp+qYQNwS+TyWByahv6B/tw8dwsCsVSQumpWHrDYy9cxE//xqfxU99/A/7FHdciCJhtqk8Afg6ugZ4C/wfeeRzXTo/il//rP1Y9lKasTPaJfghkvXXFGXr7uzFfKKGcySGXzmT/DvwTBnkA+AaA40jW3jXwVyphMD09djAt42w2g73X7UW5VMHK6oaD7xyWTisHvoXlDZsq4ogCG+gZABbJuAAIwMXZ4cyAeKA0dckn4gZMAzmtrzubNCUGgJiHV4JlBLngjmmgB5NCBNXuZf31tbIUwNXik2UcRdff14XX3XBAD5BSaTdavKJXGqOLQoh/iKnAXV4kAX8t0A8kwPc0A/AVocG7AE/LTQT6pHgCqEn0QLwZ04K9BmuV7r6HJNBXGnhSPnlla+YOTwvEHcCHAWPKx81PgZ3G0fqre/VaYmXI8yFomvotliuYn1/CpbklzM4tY2UtL7R833ZUBfCtDB52Eed44qmT4AC2bxtO/C4sFimqVQ/w1qIRP+naojaVoOju7sK+a3Zh/vIi5uautLytC6UK/vPffQcPPHoaH/yxWzE9MVDFVE8X6xngV/VlTLjE/bN/+4P48B99GS+dXUgsdzVfrAnyNPQP9GD+8hJK2fZ6QP6qhMyv/uqvbgXfFRjHNhSDmPu7sLA6EoZ88uGHn0GZHhKQEKb3TuHmN96CufllLC+vxzRW8soBCFP9qfNLOs0KCnhhgNzWhbm/1rQ0Kx8hsFCQxb4aOqDFB0jycasBUQEwd7ZIyVFWXBONR+bfNjaIO153CB36MAuDWIxUnrYjMYTFQj3g7qYnAb8vnjFgqkmAL1TEHHwSwNPAyIWvw/qEKVYtnlw0mh5bWOfGUyAmRPY1t64piLuMOegKeTof7wgFRDCgfdVyH+vWwwHsCIbI8CZaO80DYG5hBS+fvYSnnz2Nx595GWfPzWFhcR2FYlnPyYcS0M0+eiEsVA1pADLeVInh8uVF9HR3oifmcIZXLavV4NvKZ6oVGANYEGCpVaZ7T6UWljfwhW9+Dz2dbdi/c1j2S7nrAm7fIVM7dLyUaT2d7Xj7zH7MLeVx8tyitwqVMMJQn2cHQ4IQE2QzWFteRykCOsupTrUbuO++L3zynnvuTpY0WhS2SpM/B+H9bidqmOtXVvJjQcBw8OBOfPe7L9VkfODoAYBLUz1cYI6HK8ubiDgnsGWbo6OIixXskdTGwRBwsWgnAAOH0PaFpi40/VABYwA9UFlz7OpPWwK4NtFzqfXrPOAAC/Q8q7ACwDLLKwsAIFeTkjQVfNvmrt07ieNHdoMxY3WwtHUVyVWCjHb4Wd26AXDX90nA78QzBuzsB7rTC9KxUJAafJgC4FXZaTR6wElLiicXbh+10uHPz8mrEZHCSgU47Uca3tvGMSC266Hy+UCfChFuXkrq46+2rNl79slgqwGe6y1ase1vEBry08+fwbMvvGLSI2FlMz7rjcZuA3yTENZAds6Bx586iWNHgG3bEnyq63eUXICa+qhVv5pV5LVKQvzF1kkSRRHOv3Kp9RYTJ1TCCP/P330Hz5+ex73vPYH2nIAv5pjuITV4S+NXg5u8zwYB/s0H7sD2kV786Wcei5VVroTYLFTQ2ZEeInt6u3ClWEY504ZcGJ++8IR3AHg+dQENhq10e/QNiGat5gwnyOc3R4KA4dCh3amY7j6wC8VSBWtrxgRfrWstLFdfCKGAPtG7Fnc81hFvW8ornfaC53Ga4/OAF8Yc3zj+6SPPGfQhoefK+Q33/EXg4Ljl+D4cOyzalGpEep+wGvxVO3C1+llQqjQ91njywENTLd0qLyGeMWD6KgN8PXUlP+Y6IT5Nu/hoFCwqb4RGw+Zm8RgBYAtkyb2tWZuB3mg9NsAbDVqBJIhGBN3vLYtRZCoeEXq3LuCQhz4p8OXmeRA326t8p85c0gAfcehvI+bmlnNyCFQywFeB1cSUdOmEknM8+fRJzF6+op+pdqbkErh1UeMJUpRVTYDghKgaK8pi9uI8ip45eZthiwIHHnj0NH7h97+I83OrYuwMI1RC6QhMjsnaSY50nFNxHOooRzs/cfcx/O8/cou3qNV8wRTqVsPzXD29wnpTyqU2P14Vk/1WgvzXUcN3/cZGsadcDjsZE5q8uzrVDf0DvRjZPoYriyt2syd0pEoYYXk9YYGFk8e4SORyMJIaAvGhTcE4BsKW97soBuJe17XcFQIIbcRR8Xi606fNRZLWEUJyuSzumjmC3VPjAOwtMlygvAB7bj5UPWCre5iBOQ4+pvk47ISkdKqZWXROfMCAXf1AVxMAv1kRi+zqBXgaagK653l8ghMhTwR7L437LiLnHoZOL2JTdOo9w7xXtWHVAlDCgwoE1E2tETJs4I2gBAQpiBARQgF3RDpZxImYQoQELR9w9098d5cuL+K7T5403iIVsFvfgCt8JwB8nWDTLDapOfrZy35zcHLBtUtuVmigNOSnKk1SWF1dx5WUZnruufLT1X7AM5eW8cE/+CK+9ex54xkvJJ4/wwiV0Lj/VmmVMA78P3zXIfz8j90as4aubSQvzvOFzu4OBAFDOb2L29uvhve7rQT5JyFW2mut3f1bXl4fDgKxB7yjow379vlOnjVh/+F9CIJAO8DxAgfpIstrdRwfCBAt3oCyDeaOL3ru/KUEe98A5XVpqwQLqx7OWduEZ29PF952xzGMjvT7B3MdpwZkMtBCDdYqD9cDPg1JoJUGxH15VQgYMD0AdDYD8GXgzJI4aKbZUBXo5YX3uT3PRqJrgr1rSdHXhKcCWkVEgVmkk7lK2O5p3flJ888IBEYw4EQQMNq7KxxEWrjghtbAOgC1l13UT/crLt3g6jqbei0ur+Oh7zyHSiUy7qXJd6h96nPzvEpIN++pBR3BDbzqrZ0mNfrLc4uJdOkAuxbsmc7RDGDXkgy4U0i5VMaFc5dqMW2iPtVDfrOEf/dnD+IvvviUPNjGgLjW2HWcSdN+8kn83bddgw+9/3ZzPj0HypUIxRJZJ5ZQX9UuLAjQ3d2BiAUIg0yaR1Bb6bY0bCXIRwAeRhVz/fr65rBwRiP+Dh/eVZXh3uv2olgqYy3vX9jgDrhLaz666p+/5YPeMd9r3/MEXLmUErV274J9aJvWTXrk3Dsn0NHz4EMjjfqtBxEmxgfxtjuOobu7w5hAiWangNsM9oBakxTT6iOjIaq2SgtSFh/fu/HkVXPwnU2sENkoSxN9C8f1JNC22gLJabXayCskKS1YpdFfTg7SUeBOpDcObtJJHnfBHeVF7wVbe6omvvrdsQ4RWlcjN65o7eeB7GNUuDD15MhvFPHgw0+hVCKHSZHvkh7zTLX7OuR53YatFwNsjgro5/QhWvbaiWq5qz1PbW23pliQKiSVU9gs4uTJswgrruOJqxs4Bz75D8/g1+97ECv5AjHVc6mxm/HXmOqVls8t4L/r5j348AfusI6sXd9MNbeuQ0+vUMzr0Oa33GS/1UcRPYQEc30U8UyhUBowHuEYjh/fn2iyz2YzmNqzA8srtpOgap1VHx1Y58fMOSQAR8S06NHYY3Pw8IO9q8XHtPbIOoQmDJ1pAZcXF4c0qLhr9k7ijTOHkclm4mAAe8A2A64xzyoiy9Qp20wBDgVuH6hVA/dqYAmIVfTNmOjzZbGKnj5zq0I14cQH1jSNJ6SRbFYiBXCjETs8FHg6fBRgA46Znbw7lU+Z2KHSQUEUZO5c8TZg7Dexkzl6V0iwQN88E+2L8jBocOmd7oFvPI2NjaJlhndPe6SCctgIwNcKvOptYh4fHY+kRj+fcFpmytGJAyiXxXqkhYVlzM8voVIOSe44HypQ1F7oV7sCHMDaWh6nTp5FJcVuqK0NptJPvHQJ/9cf/j1OXViUpnp1Aic90S6ygV8rTwb4Z47uxEfuuRNtOaGJr9dpsu/uFSvyy+kPrHl7XQU0ELbCrS0N3QC+ArGK33Jpu7KS7z9zZvYgAP0hc87xsY/9Pb773RdjjKb3TeEnP/gv8dLJc7iytGY/hHPBwJDfLOGJl2YtIubkiG+/i/NT7moDaXEAM25shatax8UtTJqhVSs8TRoD9J57Jv+zV84z7bdeiz2Wu1tRn1tvOIADe7breOZukVPxdMm4Wnmq6AEAZoUqJ/Hi3vWnZ4OfdV9POgd29AP9TXiyy5eEL/qtAHg30P6StJ0uMa1KuglUy+NxoUiCqPFtYNNzdzCnQA9XgFMcTLzir8vl/nTKTwsJKl0JIFJTjwhPY3lQFgcpQEcS+MMI//iNp3D58pIWOOyFfmbKzFoEqNuAtqQT6QNg7qEjbU0J3f7sveUWpUXAIb736w/vxejoYGKb0zZdXd3ApdkFFApFbBZKKBRKiOT8hjYRM4aBgV6MDA9gcKgPmUxGtofPgpfiPVbLwzkWF1diK+lN2/jifG1l9083+DDJzy8+uGSzGfz0u27AHcd3OfvkYY296t78mvGPMYYnX7qE37jvqyiWyziwc0SM/c53FquHrPvpl86hVCxhcD31eoz9Dz/80ZNpiesNW7VPXoUygJsgjpTVbQsAi4trY4VCqRcwW84AYGCgBw8//GyM0Yk7bsLk7h14+Wx8q4Y7iALA5cU8VuiiOw/Iq7J9gZH/OHfy0sFa7n/iVn+zB2gOpzPTBAZDy20+1rWT3pbL4k2vPypcfpKW5bJ+RIyRPOLgH28ORraPyedS926eZsBdXkz0AoNNHDazXgJeuUoAr4IlAFZJs+5rpNNBlboYVolCO6bv0r7gnnjaf0QUhTMyB+8M7G59bP5UAKBme7tMTsoDIKeFFFhw3Y/d/v3wI8/j/MUFy+88J2ATSTOCAX7/i/cBfD0hLXnd3Y4Dc3NL6O3tQldXhxVP2x8ANjYLeOLJl7C8so7NzSLK5Uri8xYKRVy5soJLF+extp5HxDna29sQyL26VevpA+YEksuzC7h4/nJNPk2FJtmEEcd3n7+IpbUCDu0ZlzuDVZ+TwiFALFuOsAMRPz7cg0N7x/HwU68gmwnQ3pZNWTmGcqmMzY0icmEFQS0/DSI8ds89dz/Z6DPXClsN8gDQC+B2ea3haH5+ZTIMQ0uHYwzo7+/B88+fxfKybZZ/07veCGRzuHR5KaaBW4OpvDk7u4ISdVQeG3ipVhwPzP4PQPxzoXd0wNJjdAzoVKo7AEN3QotX7Fd01v6+brztjccxMthLGDDDkzNdpgZ0JQBofkZrF3SKzOQFc2g8+7RjbZES/Md6gBHXX0gdYV1q8FtriEoOzWruNL95b+reSJWuVq6C3Vc4aXdq4ifxHoHRLdvNp8BV0VF+VBO3BAcVx8ngqevkMevL+yeeeRkvnroAziM9AEeSQJv5udkf3zQa8AY41MxQTehQ1xyX55bQ19uFzk6/hFsqlfH4ky+h5DsitUodOIBCoYSlxVVcurSA9fUNcM7R1qYAP95f/Ixs6wjnHOfPzWJh7kqVTAkVepXC6YtLePrULI7sHUd7W073S9qXANMX9VQWAf6RgS4cPbAN33rmnN6TnyYEDFhZXkfAo7Te7+bvuefuzzb0oGnqs1WMSfgiAHn0ivjjnGdKpXI3AKjV9eovCBje+MbjFoPuni4Mbx/D0jIx01cR1CthhDXXN3EjHY70dDUgmS1sZo49aT+9vTpf0ZstcnqeUW314GYeXtNJvop2Ytsg7n7Tjejt7jSmShgw4HC2QamB1d1GJSVaFRdFRrrVz0sAA9zMx7pNRGnc4deXPtwFjDYB8GuvMsADpj+kfWZvu6n3QsCSzpWrU9qoxqziLU2Yq/fOrTqpcqzV95YmrQY2867dYyLNNZ1L56bO3Cx6o/WK9T/SfzTOyPwvvXwRTz1/RnwjZHGpmov3rZ6n7bjVYNI69up9Rnjy6ZNYcLaecQBhGOGpp0+isFnHXLCngpxzLC+v4dSpc3js0efw4gunxRy+s1CO0ysPnyiKcPrlc1i8krSeoN4qVs9R1e5Q14vgOHV+Eb/2J1/FxflVPe+u99XrRXcJq+7lvP7u7UP4xfe/Hr1d6ecUO7vawVhdW+luq+fJ6g1bPSevwr8D8P2QY8TGRrHzlVfmrlFSuzajqA87jPCRj/yp1uYPXn8AP/wz78Yzz5/G2nrBa56n2tLCygZePHslrkF5TPauVcAhtwhciwFjDBl5aILaesEChozMo+fiVbkBMwfUqPkfmQcwcWBmbl7PsTPg0IGdeN0NB+RBDep5mNHCmaknnZun/EDoYZJteloX0nCMXOvArR83OpY+2ClOlGs0rBaBc6uvLsC7oZpWb8W5ZnoHoWzN2whb4LCmdTgXNhmV37S1rcVz5yVorZvwMfEmkVdJ98UbvgT4Vbqk1UfLcltQOH/xCr7y9ackmBst3hoXFB8Lh5z2IYGkJPZPwyvekey+1cR8vFWOnYNDrOc5emQvhofEltcoEgB/5cqq+xTVy/Hjs+FAEhlj6O7pRMACWzAlD00tMKViCcViWfNJ/s6rC1+xNkgaNxI+bD+/eIVi/YMD/T0d+NBP3IbJ0T7HE549fsbGU5jxeyVfxJ9++hEsq8XcVpnmYdT12ZMXsLlZxGB+Eaz2YMUhjp6t06lCunA1NHkA+AREmwUA2OZmsVcAXUCAkGmtPpMJ8IY3HNWZd+7biUolxNr6pt2ZEtpObJ2rMReV+ImnD5xzsorTaOyVyKPVR65GL/84XXFPtHmt7Qv67WODOHHDAV2uWbVsrvXgCKnVkafkIKaqSOVVz2G0QP0xOSus9eCswEP+mTJMq3LrwqT3tQPbmwX4V1mD9wUFaL720OnkQr0L9V6UlqscFZl3qOi5PjtdPTwFWKh+APvdqxJcM6VI56Ru9Bokr31tW4g85ZP+QlfhRxzm7Hf1P+dYWFzDVx9+BpUw7tSJfgv23nf7yvcumvmqW9q1EpipaAPqKwCAF148awH8VtQh4hxraxtYWVnH6so6VlfWsLKyhtXVdf23pn/zEuCRKEj8Uwgr6wX85scewumLS1pLV1uSK5Ym76y6l9p+JeQY7O3AT/3AjXJu3gSe0Cu7usVUTEptngG4tekHTQhXC+SfhXCOEwAIisVydxAojdYP9G94w1Hk5DaG7dPbsbKaN6BRA+hXqni5q7ujEhGSx+JFiLjZbxlxWGBubZcLa3nKMx6b3P3znZ0d5Pnt53E1NDXAKhO8AnUKutphCdX+lAAA42xF0VLTvh7Qnebwgbui6WkTW+USjCY1w5oC+AbzX42QFuyVBk63ukGBtJ5zhhSsiGYd2UCqeGo+8l2D0Li/WtBw+ob2lqembeQL5nI1OzSdY37n9pY9a8pAafQgK+FlnvV8Af/wtSdRLFeMgBsScOdm1X219m6oQzQ8DiTeNhyiiOPJZ07imWdO4dKsPeedtoxaIk8jOZsJzde7RbkJyfpGCb/78W/ge+euEGA3DnGoqb4SGuVNpZXKEUYGuvGetxyJL1z2BLWwsvIaMNlfLZAHgL+A1OZLpXI3dYLDWGDNzQcBQ09PJ2655SDa2rIYnhjB8qrtg94FenVXqoS2l6I0oYbEnZYJ5xxhRc7zUO2czttHHCG3BYCYBzwZT3l0dOQSNXhQ7ZwMzlqLgwFqOv8KkIFa8YIzYMt7xACGziPDgAdInAydOeHsJsW34Q35sjTRN5b9qgdfe9A0vSiNenvT6VwLaC4P13Md9RanyhSAaoDaWGzonLv4F+krrt91fC7efgZzbZvkDbDTxUtcO6oBoS2WKvjyg08gv1Gw5t/FGhazpTbeqE28j7QpDRfrp+RJ6XT4ijjmEvbQN1iZxrM3JDS16MtschyuRbdRKOP3//KbeOH0vARv5ZdEafTm/A/lJldZU8uVEGHEcWDnCN76un01K9Ih5/DLmdT75f9ZgPzfA5grlyvtnPOsWWwHOZ/NYkB/113HsW3HONo7OrC8sl79JcrEVUuLb6XJPg0neS0Hqoryp6y18sg+aCayNXsK9vYUAEd7W5utMSmw5iBmWhucab0U0NO1D1Gk6mtM+xrYiUaneIObgV3zU5pjQgu1Z4Fd/WLFaSOhUAHObpGjm60MPuEnUgIZSBtGBiCp0xmAaPFcCbXK8mIsKXRLGhXCLBO8SlPnMBDtPdICIuEdReZeplOvcvTX8mMvO40GevXcgH6OiHM88I2nsbi8boM7Afuk9nSv/DTcjajrndWKScrYbPdMD2QtEH5q5GkJqL5Gv9diqYKPfvLbePrk5SoaPTXVE1/3Euhnrp/GsQMTFl/3cTPZLNrbcgiDABFLBbM3z8zc24RLsORwNUE+BPBXhUKpm5rlqwH91NQobn3DcWwWSnpuyAbTeE+Krap3QyMfZNK44TJidrwY2MmJSCGZg49CS3NXHuwsc76MDyOx71VrcHAGbziAoAFaDeoaKQyYc8NDgYU18JPHsxZPES1UaYVJANyWAXYPADXOHUoMxXDrPNldraDbWyKd3qMLOO+DvD+4ayS49V7pHLURuIzQB4sWMS0d+td9z7AFNp3XzKdTzV/koYKIyWvFkfjvPP49nLuwAH0mgxR+eZ0vWQtQ9YYWAHLacuqITpHYQPEt49ccozQCmkitkt7CtilXQvyX+x/B4y9ecjT6yAZ+stI+iiKUKqH2svjO26/FjvH+quVobT6d97tOADc0/XCecDVBHgA+ubFRbKfaehAEoKZ7F+hvmzmElZU8krqKtfCBA6t1uiH0Mq0d7Q3vetst+KF33orbbjmIfbsn0NvdadVNAX6lYp+QFMmtHRExD7mn13VIkKerlNXArjQzBRoAEPEIei4UttleDwAe8FBpMSuBLFMt4DKLwsSFK29lA2DXgPhtJJQjAfCVVL4kXtuBy0Y1c+4Q0x863QFt2fBK+LLm7tV7kZq1C/AGjMkqdvmOobV28g6dFSKWUgAAIABJREFU8w0iahkAj83Fu+s8jJAIO50KMlz0nxdOncfTz52x59xJx0n81poY4FuCDbzqbYsDJ/9vfUhVTqsr0yQ/HrtIovCYD2VSGEb40888ikeeuxBffBf6NfpyRa67CiMwxvCeNx9BX3eyN6/u+hbfAcDr0xLWE5o4EqShsPS1rz0VnjhxkERxBEGAKIr0NjTOgSAAOGfYtX0Yj56cjzHioIozBwNDxCPkN8t2ok5NCoLYypJI5qGVN12d7RgdGQBjDCNDfTh0zU4wxrCxWcTl+SVcnl/G7NwyFhZX9eIiMcCpLXYMEeMItJMe0RsZAoABHe05RJwLqYwJsJXeGPVvxIzHNLHFyjw5p8/JRSbxY55GpXHGxKNCMLP4MRajE85y5GjPGLJMAHxbqoOY4iGUAF9+dc++aDqIJiSDNjdx+p5z6ahIvjtJJACY674WE6YsHjrK1oa4XS6lM/SELikO9JpMBSmBAIj7V9CCh6C/NLeEr3/rOYSRobMrWaMdnSs/DXcjUnFNikqPRX7KxHo3AnIJeWq1SN0Mm6S+WsJJmtAz2I/1peXEOkURx3///JMolio4cXgKDJysG2JgzNwzMHAeoi0rBzXG0dGew3vecgQf+9xj0vGa3Zc7u+oG+RkA/6GeZ0wTrirIz8zcm5ueHh+89dbr9HywCDbQq7RsNoNsNoud4704N7sIB71VVjlIcqxtlBLn9Nw81JW7yytltBWmto9IYm75eu/qbMee6W3YOz0BMCAMQyxcWcXs3DIuzS3i7Pl5MzhL7UbtwRf7NEOABejsaIMY5JWQQYUXc83BHT/1kGAOLRBwkkMDfcQI2JBnYIJfMtBDlAfRphlwTA8wdDTYsyIOnFkBiq/22RdNBhfMY3GA0eAlitJ5dGoaiSQTKQ9YdJykmcwk3r0GCAeXl6mP6o/UQkS1dpB7LURoxz7cmo5YXd3Elx98AmHYgFkmCYsbBMqkbPH4lAXUllHSsNjyjDx20aKKpBlvW1RUXbk50NndBXCOteWVxLycc3zyy0+jVA4xc/1OvU9e+f3U55Bw4W2xEkaWH/vRwW583xsO4v5/fCZWhVxbDtlMIKwEQRaZqOag9s/CXH/t2bOXs88/f1ab5JW5Xl0Dxgtee2c7Ih7hut2j6O1S8xrJcv1avthU50mX1081NTkWS7WmEuR1NpPBtvFBHD+yB3e/+SYcO7zby50uSOIRR64thyiCnLOnC6nMIjw9z06uI8vMaufVh4CQlfi6Do7png5mlmMSAk6MAzv7WcMnynEuPNltpvIE+doN1tSJblPuB3j5n54ekcBqzOf2u4koHTHBK55qvlz8xvez0/PizaI6ImhwaPO9We9h+pWqP/UfrxfcqbK56ZelUgVf+urjKBSrH9mZHnzTh2bB18ckFc8Eoqp5U+FWS56oRhktoGtQAGuGVRJdV18vunp7qubkAD711efwyLMXPCfWRZYvlFI5JKZ8MZ+/f2oYtx/fFWfNGDr1vHyqQXHXzMy9fWkI6wlXG+SPAsDnP/8dPQ8PQAI9LNAPAoa29hx4xJHNMJw4PEXY+F/p6kYpgcTzeVgSeHqp3ZcjYAEmJ4YoCtoDPDeZuPN7643XGCtAQnU6OnJae1bJFphrEIc1aFuL8mAG/Fg9dB1TAL0FWjZI7Rhg6G3wRDkOsU0uX9/xza+pILDdLFRT7RNrKw/Au0IUhy2YqTluM9ftCHMUmK3V8PKXgL8N3HaZVDsHqJBIeClwV/Qc0qe8LFswBOccX/n6U1haWUv4dqyPMEXr+sljKTV5eggaAfMqlIn5U6Bjs1BO31/zoTlGWy+W1C69d3AA7QnnBNBw/wPP4vTFJbFeiqyZCsnZ8yW5wt5yOx5xvO7ITlw7PRrjqcqtBKlNm0fSEqYNV3tO/igAPPLIi1hYWMHISD+CQAwgylxPDePZbBZRFIGBYf/UEJ4+OYvLi2q/PIeZYxbZaq6sTxO4qcLIUB/6+7qteRnlw5S6Mu3v7UIum9FZCQv4jOqk5gBjeOudx/C3n30YK2sbpgqESVdnuzaT60RtQk9htucAZw6Vyk9M88p0zzmDKYrOvVMTv81nvJs1daLcxVXh0e6fYuDkP0u4A4ygJCO0edwnLIEIAI7QRefowYXgpvoaJww4qQA3JcSsC7RcX5m07tTcr8p28xIKXfYjj38Pr1yIr6fxtl89CY0gRxV5Ih6fsoBUMkp1ilQlJRAl501X/7RlV6PLteVQLqUxvXHrJ55apZR65LcEBv3DQ1iaW0C5mDzIVMIIH//CE/jffuQE+rvbyRHgIjAmLFNqXl6km3H1bTMHsLS2ictXzOFqHR1Cgw+D1AuUjgL4Rl2PVyO8KiAfRRG+9KVH8P73v0UDKOeIAX02m0GhVNE+12eO7sSnvvo8YWeQcKNQRkXO+elYirYWGPqCBb04cnAXjh3Zg0B65ANgn88uAV/5N9Zz22by2ixgI/PiFKy5XCjX1pbDO950E+7//MMol8W8DRUiujra7VoqwNVATcswIC0GbDWn5NSjhUA/2MEw0dvgRngAs+vAUtwl9Gs+xMBd3yeDt7j20FimfJmaICyoK7XrjHMnn7xmDg8D8LDrpvJyc80dvt68Tvn0Wb738kU8+dxpq03sy9oIyT1XyTR2RGNgUJ351gByg6EOhm7fbFVt2tvbsHvvFObnFnFlfrH1z1gt1JAMuUPDGMPAyBCW5uZRKSfPja9vlPDxzz+Oe951E3LZjF58p5AjikKEUSTHcDGoMiYE7oAxfN/tB/Hnn3sMZXkQUHu7mGaO6gP5loZXxVwPAF/+8qMoFEowW+aMuT4IGLLZDFgm0KjEAEyM9GLv5JCXcb6RiVzPONPensObbj+GY0f2Ep1YkTvDjmdA4fDEcdEJpGBI/kQHChgwMtiNt77hGDJBgGwmQCYTICuve3s6EEitPZB55W5DydM8SEwbowO9pbE5dU0LMly0g5rn7Wpj2DnQeDeazwMLGw1nf1WCAUXbNG9AkoI3aXegeYCPATu385lMxJQPe56d+73iuXvzKV/69P7vwVzPzi/joW89m7ot0ybEqlKbSx2MG+HWIlhLfjBCssUQWlvmskIum8X0rkkEmQzGt42ibyD9VHJzT9JAbtm+QcAwMDKMIMlxh2R9aWEN9z/wLNwT6dSJdaVyaEz5yv+J3G7X3dmG1x3dqVlmc1lkAnEYUEqg/6cL8jMz9w4D2K7u8/kC7r//IWdvvFrNyJBrE/PxjDFxqo0E+luPTOm5fBU4gI3NUiyu3jA20o/ve+stmNg2pDlY358DpCbdHhAZV9q+AXF9ApxSzxmTKzllHGPYvXMMJ27YLwWdAEz+dnW2ibyMGY2QS/5Q7EhZkOVToFePkGAOrgdslNaXy3DsGQwa9ma3uAnM5mvTvVZCTXAXiK6itEAE594WAuoFeI/QBkrvQjIA2OAPb34PLycv/RZcgVGlr69v4MsPPo5Qu1N06+KppCe0FNKqFBePT1lyKp71Sw/NPncKeaFebt7Yiclx5NrMYrLtO8bR3d3VWNkJedKyitHxhHgAmWwGAyND0iqbXMJzL8/hq4+elnPx9sE15UooHZyR80ZC46H06L5tGB3sloo+Q3u7NNmzVCB/eGbm3sbNop5wNTX52IKCz33um1hYWHH81wtAz+ayUHuIzXYyhv6edhzdN064iBeVLyRo8ty+8XcIhuuuncZb7jyOzs52HU8HRu7hoNKVad3WsMWFEloABfQShLn65db9jUf2Yc/0OALGxB8Yuro6NJgHip8WDqCFBEbqxmTF1L56Rn6bAXqVlzFg33AGuQb3wi8XgPOrEhQ5tTy89kJqcCd9hi7AM6ZwTvIoGllGMwDv8FZ0Fo8YDZz80Ak+AIfDi6Yr+nIlxJe++jg2C+lWTya+bx6/8YEXd4mrDO61UmqRpspZZwduhmezz9js8xTJ3DaHGIN27NyO9o42K76eOl2NwMGRzeXQPzwAuv6LBoU/33ruAk5dXEaQCbQve7X4Tjst0w7M1GI84Qvyjhv36PG+TbZJGKSC2z4A0y14VB2uJsjHzBClUgUf//g/6L2ICuiVuV7MXyMG9DcdnESHc+RfvlBuqC+1t+dw5+uP4PjhvWqSGYB/IHQTGOMS1DWki/8lmAZqkhtkkQazr+FcM8bxxluPYHioVwg9AUN3Rxu4dI6jFhtoDZ6bXxXLmKoHlx+gzMNVGYZHXeBCBvW9wxl05hoTONeKwLllG/DgAOWrOSwYEJYAlhbcYcBdkSpwTZyj540IWx6AV/eEjoK39euWT+vmM/9bV/5rDuCBrz+FK0trdkPGLh0pwxNa+u6rMIsLDvUYxVtEmYJNrFaNyCrxi/ryO2F9LU8IBFWQCTA1PYlsLmGpV6LAUuslVa9L9eDP1dbRjr7BPrR3tKOrpxu9A70YHBnAyPgwRraNYHBkAH0DvXjwqXNYXC8hk8uKbXXSXK+2N4dyqx1dhR9F4sS6w3uFMtre2OK7loVXFeQB4KGHnsapUxctoGdMLLqLIg7tnECb8oGOtixuvm5S81DzJG7w92vTpUaH+/GON9+M7duG/UMYt4czgMylg2rUEqth5t6h60w0eK4xWjQ8xW2lbQNoawvw9juPoauzDZmAoaurXeYVA6QGbSIYqPV+ir/4VQvwmBYs7DKpRycfyHgGfA7sHMigv6MxgM+XgDNLkTlER4GmA5yuhr/VoO+COgV2LeDUAnd37p0CKAFRG2DFRV0Ab4E299ORwZf+r+rqp7Hb2SsgWPxN/LMvvoIz5y4ntq8bEt8nj9/4aGMpHDVoWxiqyCnueJGuEsnPWU9oOH9tuSsWNvKbZkqGlJ3NZbFjejJx3rsl76JG5/EmWx1c3HR0daJ/eAA9fT3o7OpEri2HIGOPa+Uwwue/+T0USiGyuSzC2F55Lv3ZR4g4OaMkinDToR3o7mxDR8eru/juVQd5zjn+7M++SPzVQ89H67llZZaW14wBR/aOY6BH7Nly5+NrBQbg2gNTeNMdx9DV2W6NYtZABtigDWVRMCvWtStEF9CJUKAAWdEFGv3ltZmpl7P2DL3dnbjrtiPIZAJ0drRbgO5q40oAMMIFRXIlHOh5BE2n0EiXzQQyMU6bxAaE0R6G8d7Guk2hAry8FCHkCgS5ZdaOA34C6Fvp6QPNY7BczZETUFfP6wC7pksJ7j7tnYJ3HOBVPQyND+BVYbUA3vRlux46miSr+tH8lJrHruzrZ54/CyvE8RexSnhCOvBvJHgYJOFwPfhcF1FreKaQF5ovO4GK9uX8+qadJvtPe0cbJqcm9FjYSGWql14HPzqw+OhifV3ek/S1jSK++J1TiDiQa8uiUolQJg5yQmKyj4h2nwkC3Hp02szJ/08A8oeSEp599gy+/e0XNNBnyT5E42bQBvpMhmHmqHCQs0FPqKvxgba15XD7rYdx7PBes5UtRmsAWhrAjaUBHtM7jAAA/SvhmqtfaK1foWpABQBVHhEYJrcN48Tx/ejqyAl+EqgtDGcwLnoZ12nqwfR3xux7tYdeCQsqi+ZjsRHpve0Bpoca23VZiYCXr4SohPbCM/MN+gHfB/oU+A341/6z8hhOMVC3gJ1UgNaXgjtofhgwTtTeNShX0dAlQ186rwbwhtq8O9rHeUKaz0xPaFReQqrznr8wj5U1soKyUSD3UPlok/LH4nkdtHWhdHra5gG5xoBWJbGpZ0xBml/PJ2JkZ3cnJraP11dmA6Ea9/zaepVUf7uaT9pOn11Yx7efOw8OAfTqsBqqwUchAXopBOzcNoDpyWG05TKIWACeSvLB/jREacNVAfmZmXvHAXRVo/nYx76EUPoFzmTpojsF9GQ1uozbu2MI20d65fa52iPL8GAf3v7Gm7B9YphE09GRzFuDLGxjQKDj9TS60cKVEAKqrUPb5zVwQ5nRuWNWF1Vgzi/Aceianeju6oBaSGesCqpNKHBTJkaj12Wpewa9z97k5to6oe9VZg50ZIH9o1kD/nUEzoGXr0QoVAjYIA72BtxtwDfx5s+n0af6UyDr8tOXVCCwy9DgTgBI8/M8k3j2Wto77DwKWEH4Vk1PMPUTgKZ1gS+NUyih+e3fWJ1keObFV5AUKN+an2hSujee10ivyaAx0iqPwT1XtXny2iTNhmrMq6TVqpOYl7ep6F1vfy9Gx0fiCRZ9ExWoETZW17A0N48oDGuXleLFP/vyPOaXNhABYEEAbs3DKw3enaOPcOLwTnT3iBNJU4L8rjREacPV0uR31SK4cGEBX/rSI2CMIZMJoE9QM5ZtS1NWc/S3H5vGJllZTwckKw7A6289rH0JA2RQUbgo1WkBoJyULa+50nzVinlONHC6GI5q54QGRvvXJxy51zB56Xa4uLBgjzaMEVM8OMF6OV9PTPdSfiIauxRoODTmi6cTZWQzDNeM5Ro+NvbsUoi1UmSAhQIPqKYdB2RLW4cN/Ppd1/Fn33IH1LkF6hawe8BdAx/i9U+vvdvWBC/A07o0CfDUFO90/9j3QIdFn5meA1hd28C5iwsgiZ7LRPjz5qsGfKnH/VpgnBKHU5VXJxhtGc8GQbHubLKjlIollMuVqm03ODSAgcHq566nKjJ2kUhhhVKxhCuz8ygVfJ7uagM7DRHn+MbTr0h/9iFyuSw4uNlOF3GpzRuXt2EkTqu77ca96C7mEdgnsyWFgZmZe5tvNBmuFsjvTkP0iU98BZubRb1oQ28/SwJ6AOPDPbj5OrX9PvmlZXNZtKt9ndwetITp3YC70ZYVGBqhQt7apnsNmiB0TP/TQoFGWnuPvM2XmtF1ZS0LgCmL63xQQhG1QOjHk3TqnpjuOYjGDvUsZjMeYxzXjGbR0eBK+ktrERY2DPpZc9U1AN8H+hT46zHVe032pHwK6tWAnTvPkQTuFDgVB0t7j4GwoWke4OHwdvLpTAlppH5xGpv3cy++Ymn1iaEGSX1jd1ySSGafBsJSwlwVJK1bIGkEkKsILmkZNkPli83LaRq7D3DrvYxuG0Vnd1ejMkjVOqWhisIISwuLyK+soXoj6q84MX1heQMvnJ1HUa6wV+7MQ734jmynCyO9KO/4gQls72urwjsWUmFmmvCa0eQBYG1tA5/85IN6hb1lqtdAawM9OPDuNx/G6GC35uP7FnuluYQmUcGB0ThZrtoeF4CayQF7UR0FdRCAJSvXJagqPpofbLO9OrJVYLdR34lsoIl1fa26cJPN5eEMjHp7ncrHNake6hkY9gzn0NvRWDdZ2ohwfjnUAKSA0gJ7VbwP8OECtKlqK/5igE6sBTqZADutF0h9Nci64K7B2APIFIxlAVTDbwrgKVhTPhY/zSwG/u4vIbUG83K5ghdPXdD3Huyllao+tjo3dYNmPYQ84TYpvq46pGDiJCaRVDcvp6xF0sPVyJyWen3NcVfJnXcvw9DIoLechOJThRhdtQ7GOfKr61heWJSu0xW952Gdj6VcqmB9zSwyfPylWazmi4iiCBEXO8G42itPT6iTcQxALsNw222HUz4ZgBaa7F9TmjwAfPaz35SObZgFuGAGjBXQK/N1R1sWP/0DN2gTui/0dkuQl9qwenAN8HLPu/FWJ9NhbzNTGnmgNXQgtgiPG/CkQK/rDm6A2t07LwqyzPZGsyeL+FSgNOT5pNTgAD7hxaFN/rpsiTzKWjDRH2C0pzFvN/kSx8tX1FyYAVJzDwOwVQCfPqoP+OOaOqy/qpo9LZdmSwB2rbXTccEBd5WuuMW1d1fLp0CraEw6qDUhJcCb/B5BgTv5TKnxNFI/QqVpTp25hKI6nKTGyFwtOR34eyKrCA/JwJm69HRc6wSqVKXVie26u9Ub6gDzpJDPG5C3v1VyzYHOrg50OifBVRVimq1YQigVSlicW0C5FN+RZeRXU3hYCbGyvIbNzQLCiphyLJdDfPeFC9rVLecc2VzGcYrD5RZDjkyGAUGAoaFeTE2Npa3qP09NHhBnyT/y4izR4pOBXgEXY8A10yN4y4m9NjPSUXqUJi95cGIZENHKzM0sYGWyAON+loAziGYu+QTW3nMm4+i0g1OGeQxLYFBAH0As+lNl0Xl5ZfaX1SHCgL21zhIEVJsQwYVzk1+twh/qymDnYGMHw5dCjpfmKwiJSVwFe8taMuBrsKOg72C4L3Dnn58mDuicRFocHGDXIJiguaunsszuCtw1iCoA9pjnKcBT8K0D4LniBVJeAsDTOpHaWlc+Mz2H2Buf1L6xDEmBx2/qAe00/FIxSlHP2llT1jIFKsf6btWmTFduM1Sme9jpYRhic7NgU3I3k7gcGPZo8/XUKPEB6pMIojDE8sISOT3Pnz+KIiwvrYHLQyDyeaPNvzK7gtmFdW2O5wByuQwiblbZg3NkM4G4DgJEEcfhw7vSVjM1Ya3wmtPke/t78fzpBVxaWJPgZAO91uIhTcvcaPXvftN1mBjpBRB/bX29XQbIJXhqbZ05GjnUnzF1mzrYi+dUHQ0fZ8ufqgCn1gGYfOpZwABuFsAxTWcyWqvlAXuuXRekLkk+2Rr0+Fol1lC+qtG62hn2jjYG8BEHXpyroFThGpgsMCNFVQN8H+hT4I+Bfx1/Fg/3H0HzWsBO6xB7TsAWElQWBbjw0NE2oACvqOhrahTgYdfFhihD5M7D29sFOWbnlox3O2cwT76Kh0Qqb6Z4QT6ypPLi8cm6ZCrYqEWUgkl98JQu+J6zicw1Q97yfudjJa66e7rQJk9lSyrHH52uUv8/ee/VZEmSpYd9HnFValFadHWJ1t3T0yN2djHYNVA9AAaYLfeB7zSakQ+E4V/wH4wZn/hAmtHwAMIAWwA0cLngggB3e3cGC/bs9HSPaFUtSlfqzJtXxuGDq3M83ENkZdVUDT0t8ka4Hz9+3D3CPz/HVd0rI7yJsLe9i/msvImaDi+wt3OAYu4nyo1HU8xmc9hv/aef3sPM7VlvgL6Ta9AvCuSdzIUVSqEoCGfOrOLChUadnRdHkzeb7V+rJTRu/ew6jkcT/OmPP/faLwN6cKA3AGk15V4nx3/7X35PHphiOgErS4vgpnFu8s8sCEICrwVp3xkw6cOsWTdmexWjs35WI2eA62mDe6bRWzksIz5k4Gg4CQXy23kLoiDkrHvHiDyDXq7wxvneiQ+d+fTRFMOJ28+OgasBQUiwN5LFAZ/FF8AfAf/Wfy49y1eCehLYwejNgx1zT4G7A9sm2rvreIRpcJonAHgB3hSn42kFPO39R7/8yldeytW0zRQJD/odTdgkCFPMT+BiQlWwfKodhxj5SRKkJ5STuaPDoX+/Ai6hFWh9c70l94YuKXQ6N0VRYHdr14zRu48PALC/cxg9kvbo0Fst9g7H+Pzutpts54C+myHv5iCCm4xXAIBSmM8LvPXWy01ydL0JURP3LDT5ywD6tVTGrZ3dwGg8wVcP9vDRZw+rgV7faqA2wPbK1U38/d9/zfHLMoU8U1hdWfDj8Az6LA8bX5wcBxKT73y65IGVzXCXp8L50+fECoFIB8DjuQd6Phxgx9Q9gDPhKZgdz5fyMVlg+fCNbsxL7TsvwKvne+h2TobwX+3MsHOsX3QtmjSaS7Ava/dGIg+sTP0WwO94lTsBqauk0QckLp06YLeAyMDdk6TBvbn2HoAwBQBfAmY/DHBygA9/eVPNeer74XCM218/ROhEA0+hX5K4iipOU8G7OXBJi0wdj3oJGzBx/tXcnjdTvQ+W4fbpeHiMYTg2H2VFWFpZ0oePVaXVQNhm+amPOZ/Nsbu1h6Lw/gd7h5iM47uoTiZTTKd6w4/ZfI4v7+3hYDgWk+0AbWUtCrn1LfIMs9kc586tY2NjpU7Q6yfOYuCeBchfb0O8vLGGudm84F//5Sc4Gk6ECdyZ1g3gapBSDugB4I/+kzdx7eKaO+BlcdBHt5v7OIDX4J3Wy8CaADHebf5pq4G5d4AsLQuuQ2LyI3koL4OjVy7Mm88DTd/JyoAb0LvpKRlHkY/n6Ml3NIAgXJH7eK9tdrHcP9kr8fBwjnv7+uXn4/7KIFgZ7OOAHwV9SOAPl8IJusQVJFziFyRb7hgEwM5N52lwZyDJ43Dt3dJy0HUAz9JglpBKAK8LjwJ8wNfKG/Iwvx9/8jUKKmK4m7gruySQRrEyBtFVHhHuJ0WFFGIl+FVie0PaJ3ENSuoUeMpAIuDOV/cwHo3kex2Jr5TCWot181S6aSVdIzebzrC/sw9A7+I3Oo6tqfffxtGR1uanszmICJ98veWOmS3YrPo5A/j5nKDcaXYFXn/9pTqxVszx7E/snjuQ7y/5jfGOxzP8qz//lQdBpwFzYGVauLnv5Bn+uz/6Ljp5jkxpLZ5r/9wcbsfmuQlfWMpZp8CDul/fLkDXWRc8ONsw/euXzGXQpnOXPudj/4ey2I6II2TP5Bm5DgLxPJhDdWxXwm5Qb2g2FzNcXD3ZlrX7owK3t2YaQJQHQ0WAWRXozcw1gJ8E/Ujjn+oApGfTR1k5zxio1wM706TB+Li4PF5Eew+0fBvK03KWDCvn0wB4H+IVtgiP+Zzwy0++aY/gyfAUjLfn3RxgT0eLb9adacCoJrwy/Ta9ChYWDU75N3DFvMA3X901Ju4A4gO+q6uryPPyqp142s0kSuWnqZuMJ9jZ2sXw8Lg24nQ6x2Qyw2ymzfy7B8fYPTxmQO+PndVb3mo/ynID+HNcvLjh9rSvcDea5yDtngXIX2xD3LPnuZuC/uTrbfz01/ejQA/7y4E+U8iVwvVL6/jDv/M6MqWwsrIIviRNgq8FSrlWHkSuM+A7BRJo+ZntfDkan23POws2DR8ml9VZ8z5AUkZCANAQ8SB4si5DyXRPPqI9oU4BC50MN8+22qjBudGU8MnDKch0IkBaIqUs2BB7Zg2TA/sY4EdAPwD+Ugeg4RXj4aUog7oEdp8n23BJ2T2fKLiTp4VL18vF0/Z0EpSfGsALOv8s7gn44qv7OA40HQF2FPqVXRIco4+EieNeAAAgAElEQVRUCi/xpgqedcLUuhPwqwwv5SoIrS6TdoKcIhXJ8Bj1dDrHN1/ddZPVKCSyzxlaafMlUWo9agOidLNJeQw+5Y6ORpiySXlfP9gVy+f4gTV2Fzyrydvwq1fP1SVzobFAFe5ZgPzZNsRK9PD0K/8nf/Up9o/GUou39Mqe6maAHoDKNRz+g99/DTcur2PRnuJm40Nq7nzNvYVIpRQyc4iLLaQMEADrrAPW7G79nNldeRkdrU/Toj+3CniLgZyIZ59F3smyIC+f7Zwo3m1xSelwlo9MAbfO6yNt27rZnPCrBxOzXMTWlwZ7/oErC3KaxAMmuwk1fJOVEvBz8BcdgIZ/Iq7T8oOOA5yoDKAJSa3dyRR2DMrg7sGW0Vtq3qEgB+kMxE4H4K1nHcCT8NTPHwfL5loDfAoz22BbmnsjH0oEVnce6vi2oz0ZUUMWqQw+Cc+GgePxBHe/viffIXPHyVfXV5FlEfgp9+lO0TV8yRp04mazOcbjKeZzTbyzf4zDYzk273bBM8APZc31+qjay5drobEVdqbcswD5xuMKnU6O6bwIGgJ9fu8f//tfSqBmGr1SygF9bsEyy5BnGf6bP/wuAAI4iCIAaTCQVJ4/N7t7zdrvhGe1dmciD58NMGs/udGN0+7dMjzDn0waUGxTHQ/01lnLhtuUB5KOa/BKwVrRS+HXN3tY7LV/DYj0TPrxrBDfhEJCG7EtuWLaKgd8WNDn5nUPy2Hap3HFRLTAHDPFh8Aeau1JcLd5Y10NEPNLaO9c4+eyaDoK6JsDvLSeBADvuxOyIwBga3sfDx7tijJL30UcJR/qI6Q6EBVA3DiZNpFjAjRJj6oCLUk5/AnYJSNGo9WVYz1b54bDYzy4+xBV5aeyDCtrtZPPysybBZx6DyFWNyBgNi/cx3Pn4Z4bgy8K/V0XTLunTGE+n2M20+Pyy8sDrKxUntv2wozJN+6NdDo5JlOzS5r4vgm37+7iP3x8102uk2Z6/ZwrBZUDmcqQKT2z/sqZFfz+O5cCM7kFfHJgybVxbwL3CMs7CByo9SMDamFKDybghTQ6c9LkXuoEWDm8uT41Rh8OQzgZKR5+djnH2ZWTjcN/sTXF/mjuMuGW5PlicchIys9F8PVqTfAe8FOgXwb+mF7e5uJDAGlNnYnaGNij4M6tFDYe4yNoOZ19CsC70gSPBgBPMbogLaHp61C++U0d1pWcAJHgLoqTbVvpJgD5fGjxjXNWSdiMy6lQBT3iuq4KABzsHeDxwy32PpUpVzfWXBsb53maeWzq2nCzu9ppt703xGQ6M0vq9GS7Yu6BnqD0yXRCm6/E8RcG5BsL2hv0MGFrE0Og/9OffIbt/WMP9PBAnylAZQYIM69xq1zhb71zGWdX+gKsXXzY5XH88BerIcuT4DhAe15ekxb769v0HY0FdmXOmDcy2PhcYw86EVY2G+7kRNDBcP5s0h7Fwxd6GV4+03hlo3D392d4fKTriVg/QiM8Q0mbJ1IO1BTjE5qva0GfxaHwocUlHgP+Ll0L6hXA7iSPAHsTcC9r79zC4QugBN42TgjwFR0AL2vAKwbw4Gnqp9Fois9u3yu/DCxSVfOYBLsmbWqKfwUQ1wrUNNFKr7qeAvevECYR1IYdpW/qXcv0m7rdrV3s7ezBvYfiDsjzHEsry60SrKuCxsxOUk4Rx5feEQGPd4eYzwnkZtr7E+kK06boyXf6On9+3SuTZffbZ67vr6y4xsc6/jidzfHP/90vAHgN3v1mbMY9FDIG9FDAD17dwKCbmXFpMIuATkCBgTAAYaq3YaYzYUHdbXDDaeyzBWur6TrADzbkQTghUC6t8x2PcIwejs6v/7d8bZ48H+7yDHj1/OBEG94cjgt8szN13wZn4ZfOGc2eNc5eCnKAZvNRBs+ypu2B01+lDkDDS/AQV2gjiMkGIVcI7jaBRuAOC7YJ7T0B8LYT4DsEQEzzFjypBcCHHQUCfvXZHczthCpWEo0AXgSyfMRow9AKgE88JH1S7XqMd7V89X7taeuEahxYGa2NrHW86uISgMcPHrPtY42/+wi1Nl/J/skwOMbxychIEllN3n4zW3tDPwGP/JK6oihAikBUmMl3+jfPc6ytLUUSAnBKmvzJbLXtXOPeSHdRj08QkejdEHlg+/rBHv7iw6/w++9eg9WANW4TMmM0LqD9ioxg13At9XP8zq1N/OWvt8paMZ94JwAY4Gqw9ZvNZ/jgw88wm87FRDoF4PvvvYqFQc9EY5MElQe6slWAgyWBwzK3AhCcdZyNrftwy0fTkQt12Kt0WV4/00P/BBvezArCZw/HKMhJYiQ26fI1c0Y+r+krgJQ23RuhxPgvdPlQ5CPj8xGq2/ZUnpp94LG0dew40sjvnUqycYllJ8F7cB48nRBwfZS0eT5JIzoA7QGeiPCLT74OiiB2F3FVFRZ9bN6qVwLWE6FZhKi6l3Hy9E4BxFJdnIZvfU2wDK+mLn8nBL25zMbZjWjHoNvrYmFpUWym00iuqjyeWsfAsksznM/l+fCj8QTD4wkG/Y5T4Li5k0hhPp8JC+76+jJ2dw9j7H/7NPnOgjcfV2n0//d//AKPdo7EmDW/rLatN8PxGvqFtT7efmnVmcMzV9ByXbvV5znYK4eeCh//8iscHR273Y8mk6m+n83Q73UMB0AF1gB54lyoaSv2xzV7fmANfEckFe4sETBdHhLyX1jtYmOpfd+OAHz+aIKJmU3Kj651xWa3x4XXMnlXgMy2uiGYWgwPx91dTKq/vJSxqw0PuPSFts7aL26OB5eW84MvBwriWtDlmnid9l4H8D6dJwd4y8A+fnXnMQ4Pj8uw3qAxjZFUgnAkcgxfG3YxHE1jDKY0x2Reav2rQTemxVfybSNgnUukdZo4ebB/YHhGUJ7McrqTvEvJOE2lD+na59qZ6+0PAdv7R363u7mZiGeWzpHS4/J68p0el19dTU6+e/7H5H/4w3+0hhbWgs2VBbmsIgL0SumC/eN//ysQkR+PdyDvx+gB5cz51r16cRlXNhdgd7mz4Kd/7Yx5Eto15/3V1w+wvXuALMuQZZq//V3o9/ywAZipHfFnuY6dHIDrNAMgZ36w+eU73gk+dtzfdCLMOPnSIMPVjZOth7+3O8Xe8dylxeDHNRS2DMHDzH+7t77bHMfKGoCgYEskL5Q7AC5KQwD3ScSGBBKgzviDpBQC3FmMNLjHTPNlsJWz5428vrg1r1DrjtEwnp62GuAJUvaVpQG63eAzboDzT8VMHxMi9CljZhNmaaI6dDkpIp4oXrNIbTovTyJCk/7GbDrDyJxUx0319n9v0MeAHUN7mh2MKMdT6yTptfChQrp7OBL72c/5unmlZ9jzcfnFxQHyPArFL4Qm36onsry0gIvnN91z+MHyMev72wf45//uF9ovs0DPZttnEugdoAP4zvU1rCx22Bi27RxYEFX+12nEwO7eIW5//QC5YuBu9sbPMmBxsefM9nbyHQRPrmtLrd1p3gLouW5vZbWAHp6Yxy0E5FDVptjJgZtnB6LD09Ttjwrc252WjOHe8KHH4MmapZzEAMiE8Ql5YCZ8u7YPXsv32nDw3XnScgeg5cV5lZLgnYMIsFvCstaeGHMPwd3GYeBeAmNI8BZ0ArwtjWdieZ8U4MV/IqyvL+M//4NvI8sUJ2oN8NWPDVvYOrCqA+Qa0jbtf63EJUCLkSQ6KtXsyrTtejLNaEMlqwmvBNHh3mFIKe7l2HzdC1ZRpqfcQyhbH2QCRVEgIMB8XmB4PGG73ZkJeFQASjlwt7vfFUWB1dXouPzzr8mjZU+kv7iA8+c30OlwrcF/eXzsWymFf/pvPsbtu7v+EBk7Yc3SZF7LBzwQ5nmG37m1iW4nA4iZ6V0nwgOnXuOuMJ1M8ctffyU090wpk4ZCnmVYHPQkEAe/PozkrH1lT7NjpnauwYMBP+sgcNOAD/e//MS762cH6J1gHH46J3zxaKQ1cJaG4GTRR8EhI3FiC8wMWT3gKf9NW1O/R3IJpgH4RzC60oXxohq/MMFLcG4O7BzcwfJaDe612rulc/cQZemeiKfZHuCdVYI874sXNvH7v/sWfBezpqAjD5UgFfGsBuEm4Cg7ZZUuJlxKiOpEnwxnmhdsK56pco75Pw0RDg8OSxqvq2cCBosDdPtpK2NdNTy5OwFDMhPvIu/r4XDiTPR+H/sCyOSGOPN5gdlsjuXlhVgK3R/+8B+tniAzwj1tkG91ruDAmC0uXfQdGFtwmeIgqABSmBUFfvS//QSzOTstzoImA3qvcXrAXBrk+M6NdX/gjIVPcaocjOmb8Itff4XZvNAaf+a1+AwG6DOF/qDv5WMavTiOVjETuwNoErLpbXIDDR6+efUyhx0A+6ucDADhwloXawvlvaLrHBHw2aMxZoX3UEq+zO6MequU+9juh/cBXJBBL2u9kGZmDsblvxDx60z1oVZur/gfa+8DUE8Bu8wSo+Q8moC7jePyyWiDToDYIdDSMf6wacLLeVKAt2E3X76E7757qxYEYkCcwtCQLgnwKfpG/JtwSEdvDYRNBEoEPZkMlPB/Oq6NrPP5HMPDIewLEpN0dX31FGSv4UClmxO5Xq+DXq8jNXn3wRCGo4nYGMceUkMKwlRvt7ldWEh2cJ7YZP+0Qb7VQuxevwci4NzZdbl5vwA0DZKzokCmFO483Mf/+q//xk24c2vVMwn08/kcxbwAKQ8259f6eO3yittr3pn7TTo23S++uo/Do2NkGUqavDXXK6WwOOhZcZnJ3ssvft3lJ/mByeaW1jFN3Z/AR3reQCQtTUdOi18ZdHBp7WTj8Hd2Jzgaz301KI3G/EhdNteO3XjAt+XtlHzmB4RtoQV9EuDsAdrHqeoEVP9JkC2BeQ2oUygDWXCNaP1oCe6WB4vvaN09LFeRTslqEAK86AD4tJsCvCX/1lvXcfN64jgKAfDCO0VeEdqMd1WEVHte4pMC5DhhRXjb/NfzE4FtULXOhVp1yOoUTfXW/3D/UJCE94vLS96Km0ywbWabv19NwzudHKsrGtrs0bJhXY4mMw3iZltbv4wuExq81epLc168q9wSr4l7rkA+M5MPlAIuXzrn7sOJdRow9DK7LFP40x9/jv/np18FwMk0ehOvKAiz6QzFvHAgc+viEs6v6UkfXHMG6XQfPNrBvftbAtQ5uOtnPQlv0O/GAZ5r3lZTd+Dnx9aFBs/H9p2mbk6SY5YC8HA37q//d3OF62f9vv1t3N5wjof7dn2rHXOHT8xAipVBe9kSJDcGr3jbZAbdFQczG2RZMPT0e8IzkI4B8pNcJeiPgzq8WA6Qq7T2JwX3EIxdZ4IDvH0SnQBZTtYPnCcra+L55eERgCcQ5rMCW9sHiLkYLFQDbRzkYthKIVEYLYWXT9jGtwHtSoFaMah3jeU9zb5Bgwjx8tK+w6OhO7zG1bV82bCy3sA6ncrTKZSrZBcyJOR5hrXVBYcV8yKeKFGB4/EUBRFmbPJdQeRM9Xr2feGW4SWA/mQ7ljH3XIG8PX6QCNjcWMHiwsABmp2JbkEbYJq3Av7nf/kBbt/d9R2BwFxv97ZXSmFe6F6UNbV86+VVrAw6cKBFwNb2Hj742Wf47PM7sKfbWRO97Vz4ToTeM39h0PMT/4yAFnY1sPPlbRHTvdXCA83fXppOv1SiI+HCyZWTggb4Tt4e4cczwpdbY8fVcVAaoG3PwgE6+WV8VsdXrqHzwKQokMWtp7OgJDNktX95EQPKk11yEl45DesIMl0naxNgbwPuxjMEdw7GCNOElcljSmz8vQrgJYCz8ATAg4Cf/vxz7O0fIXRS8UshbvRReFa3080AvopLHAQj9HU8m0SpkCSmxVeCcS1St0G4GtoGWjxVhsYjExGODo7cuxUjXVpZKh1D2zxnTSnL4N3EZZnC2uqCmYBq4tn3h7yX/Z1MZ3rZXEEoirmeiQ+4vevlLPt56ujZk5lhudxPyqDGtRIwzzNR3C+9dC7Qyr1mS/Dm60wpzOYFfvRPfoyj4YRp/SHQ+44BETmTSa6Ab99YQydTePB4Bx/87BP86tNvcHw8ckDuZtSntPksw6DXdSCrQSoEbBIavfGWY+mQ6/O9ad92CNgOd6EW78qKcG61i6X+ycbhv3g8wqzQaGKMGjoNglkCZwHPd1A4CIpxB5tHMtq95ccQkCxziVgeZCW5kPUkVynP9nI05PIjzPwsizFgF0ANz6cW3F22y+Bep717UCYXx9MweU4B4B9v7ePnv/yyXH6iTIPmP9l+UvQ2RtYGvgR8NoqYJmoH2vymIuF2mXmSSOmyS/ifMJXKyGFn5vDgMCDn9UWAUlhZTx1D21ZCqnxsw1YpYHV1MbLUjVskZSs1mWqNfebOmZ+7dfN84p3GoSKlyT/3IN/SXO9BiQhYXV7C5saqBzgFb64mNhnPgO/O/gj/4z/7CexSLnFCXaYEH6txExGm0zl6OXBpReHzz+9hPJk64PbaO8SSOZWxZ6WwMOj6vfOZnE6jZyZ2t/Uu67RoOrl1bWh+d6AuTPh2e12nQ2PQzXBxLdorrHXf7IxxPCniHRFmWdApesuHdc6iQD4+7yS4Xx5RIKPOiYPFYF1dlQbuLsuyEuwZAMcAHR7QJY6ngT0Ed4BlrwrcbRx3z4CYyUGGDqV4EfN8jDfn2xLg50WBP//xRyJfVlb2JO8ijSchERDzpRLHMn2qHU+mneJfQ1gZHndpTCnzq8Sf2jxWlWpTqZpHewKsxPh45M5td+9OEHF5dQVLq8txno3r7MkcBRleXV1At5N7D5ZkzKwP6G3YCzEmTyhAganemu4LdLtRpey3DeTtUTFeW3/pyjl0OrkDMHdyHEiDqjGlaxM68MmX2/in/9dHfk97Q83N6H5s30/Wo4Jw4/I6/uC9a8bfa+yadybXxltTvdXizXi819S9Rp+BHBgrptGbYNcR4WfO6zCKAqy7TIDr+ICQKcJLmwM3DNDG7RzNsHU484KlPhwmu1XoS6nx+ESCgEyYsmEMJklZgFECPUONnhJ/nriKiiQvKokhNHXiPCOy6GzEtfZG4G7jCHBvoL2zeC4WSx+Mt28c2wM8APz0w8+xG5jpTwXgCTHfwKMZwFe19HHSdE8g1rmoBTfG8/SA6MlBuYm/zwPF/VslUh18dFCe00HinrC+uYn+YnRZ2clkSdLVx1teHqDX60C+HP7bkDtFejczWjyffEeEqKl+Pi9SbfZvG8jnuvF34AV0uzmuXDrHwE8DGgHuOFmtZRvwzoA/++sv8OOPvhFaO8DA3VkA2DCAAfzf+9ZVvHZtUxx8I3a2gx+Tz+w6/EyPx5fG4m0nw41fM5M8PFDzo2atfyZkDEz3Ni/QnQkwv/OrfSyc4Hz40bTA1ztjoalziwGYv8DsoDOgtXty3wAZuSzQgdjMfNY7IGilXV8mptH2SUltNQTkEji3pYt1AbimHgF1XQ5NgJ28Jm7bhTBeCtxtulwzN+DexjwfBXBnvUiEs0bs8fY+PgrM9E8V4EtcmwS2NdOfVrpt0yyXQSXLeJGVbmI8nlDU5hGSHYd4wOHBUZmCvYsAQArYPH8WvV7P0bZzqYKrJgvd4mKvYnmbKfsiaB3st1wQZvO53+2OH1pjTPTcZJ9wv10gr8c75FtPpHD2zCqWlhbYuDS5LV0tIIKNmSul8I//5Gf45uGeMNnb8XivzVvw9x2ILFP4ez98FefWF70WbzoPDtxL4/JAv98TJnVhWjf+dlY93wjHa+IIZJEb4SiPh65joNyfDlvsZTi30t5MXxDh9tYYxN4z0afkD64TZoNIhCtmaldgs/JL6r8BGfJl5PbFUS5Yp2euZhp6sz+J9CiDP8Rnq3PEwDkO7GCyMZ4uiCrM8rE4Hog9GIcdg6ATYOXkacDKzOhLPOMAP59rM33BUL0S4CMu6l8F8FTNtRLQkh2MkH9Dwoo0y/5pwG2NUSePdHJ+DbT40+g4TKdTjEfj+ogK2LxwFjlbVnd6ZZt29t1YXOxjaXEQJFCu43TyhJndu96ukydiwO5N9fP5vDQUZtxvz+x6rYVnfjwbEAD50pVzQvsmIq9V23F3BsrzOeF/+uP/F0ejiecPq9nD8ckYT7sNbr+b44/+0zexvjxwE+tylYFPtlNgy+eUXj4HJrscDjC6u2L5YqDO8+nG8g2qKx6m4HfGM3JbmkwBVzZPtlzum+0JJtPCmRFE2cOArJPXf2nK/dcg4ZMOhFAsnIOeTYvAgNP8sh4NAWLP+1Bbr9LYowAeAfI6QI+NsVuNXXQ6eFqW50nB3Ra1LR8G8C52lXnepsEANQ7w5Q6D9fubj77A7p4309cCfNBORWGyqlFuAvAVYFwbEmdQyamqA1GTfEBWLpxKlsmylD7xTkycaRvgrnYkfpoytd5+lr0sX97BBGnr7ub5c8iyqknETXMQ0sXj5XmG9fVlLC0NyqT+w3Ee/JvhPAtY07zV4rUmb9fH8yV0RWQPfON+ezR5DVpmpbhdRsaWVC0uDPDySxfQ6XTMeL0GAmemNyDNNfbdgxH+l3/1U1gTMd94xmnSzA8W6JXC8kIPf/h33sDSoCd2uLPgmrvtbPW4fL/X8bxDoIxq9NwSwTsCDFx52djug9xWztFcWOuh32lfndtHM+wdzxwvJzfDWYvjDtTZpjfaRwttNUJF5MAZ5EUmJ7A06YNsITFnzckWuKz5O/4hNMH4cpwIkEcB3YtTNsXD4Xwc2E8Z3GPau0uPCW2bfw7wzirg0owAPMvv1s4+PvzFbVElssSDu5YAXw1OzQG+FoRjfGo9axE28EoAbgu5Gkc6bX4No7XB85Sp3rqjg0ONgoB8dwN+BKDTzbFx/my5jUi6+rpL+Q8WetjYXDGT4FJSSW+/nBf+JTWPBRXSXA8ITd6b7uehIcW65x7kGwuYZQpZx0+8A+CVRGMC3lhfxpuvXcO5sxtsIp0yQA8zAc+P0ysFfPbNNv7lX/zaaPmAXYbmtWb4MMABr1LA+soA/+APXkO/23Edh3DWvZ2E1+93xZi63o8eTrv3QG79vHYOdp9BhmnTPjPdBx0JpYDlfo7N5fZm+sm8wP29STxQBbd8fT5Twe1xtgjXwCfeWFefZHmSOYJWztS3ZnydqLQRVIFzmysUKw7oCECdmH9zYPdL4RqAu81jDNwR1959p8DTyA4Bo3F84gAP0rPp/+LHH3v/0wD4aHjMoykgVQN8vBORJozJXCtJvOcR4V0uoErsqcWpRKcixTfh78sv1mVJuZpSqSs0Aop5gePhMIBRStwD3V4P62fPhDbCE/ddQpflGdbWl7C8Yk8oRaQi4vcxxcPKX8yNBj83x85SfAmdvSKT7357QN7OYHeaLxC9z/MMly+dwVtv3MDmxqrWvEkeGMPHzlWm8OcffImf/voeHLgzczc309s9DviBNufWF/H3fvgKup28PKveaNhZnqHXZafawQtdZTmQGr00v3swDSbgiY6E3oTn0kb7YRsCcGd74s5Ddh0QeP5AaIL3PVYVfG5+1YOm4JYIDmQsBkBwwwGOs0V6tlGOC7B0DIQFODe5KH7xwNJfAtQFyIbaiMuuy73kw8qkBO4MWEvgztNvoL2XzfMhTdhp0H4ffvwFdszJYW0BPkbLHyvBrSqsRYMeA78qBpWsI4GVgFnyb4tEFfQnArUTImEDsI57xwNC38O9A4iXFexdo3KV9RcHUHlotm+at5DOP/cHPWxurqLX61aTGoHCL8B/H2VZCje73i6nk0vovOleAz8Ah1/GnWwtNHNPG+Sn9STaZZlexqYbPm/+lQ2nsqocFhZ7+PY7t/DGGy9jMOgGYOkvC8j/7M8+xr3HB3BgzrR3bqZ3s9ozOy6ucOnMMv6z799AnmWwM/j98jmFfq8b7HQnNW2uvZc1ehLgHdvCF5ZP5Lqw3kP3BLvabR9OMZz48+HtDefke5XkykkDuEUfRs0qSncHSDwrDvlUnoHPgsrIHIAuf06idgWaJ/+C9GMdCdcRqAD2UGsPrXlOsz4huIPROjltufA2k4jFidF4AXx/Svtt7Zwc4CkWUAXwFOUsfSKoXSVDIpkGnlWSBP41HYcqBpW840VX8omJXl++Eb4NtPhUqTSF2FiE0fEIs+m0hofP63g4QjGfh0HpBKoEJEBlGVbXl7G6tgSVhZEoeI48mspSkM/8P5E/ha4gvS10eAqdvQc8wLMqaYyhKZfcFf+U3LgpIRFARWE0d2JgbUDFwgRBgywAKOD82XWcO7OGu3cf4979xwA4UFqTtz7Q5h//Hz/Df/9f/QCL/S4yEAqlHNjogX5yCquCMmZkgJTCy5fW8be/fQ1/9fOvtWwW6JUej5/NZq7CZjPdc5vNCswLX5kLgy5eunzOITy3VHCAtfkN/aVTWBrkWFtoX4XjaeH2pTeGEDFubovCp6Rc4+/uFcwe9F5++OIzuWAvvDHn276B6yOYD4P34K0ApDzI2rS52Zn9PJFL8uAabjpYcIkBjwsJZXbAG+MHqWEHfpIlibgp7Z2nIeYd2Dpg9G+8egVf33lUyl8qj6W8lj2fGcDHwS9NGEu/tgPRGEtSEJ1yJ+g0nJTfk0RrWC5V3gStza+d3YTocLKK4fU/PAy3Uz75198b9LCyuohM+RVdoq5KL14a9GPTiSxZAYKa+9M2i/ncaOze4my4YD4v3DP5pU6J8dTm7rkA+SzTBa21GQ2gRBIA3S9TlfUErwydTOHlaxdw6cImvvz6Hvb2jpgp3k/E2zsa4Z/8m5/jv/7772mtnQhkgL6AATAinX6h4xP8WPGrL21iOp3hg0/ui+V1x8djfPLZHV1BKlzjrsxaeQDQM/B1B4V1XByQe0QXWXdg6MOzDLh4wtPl7u5OoLVpn5DboEb2dRjg6wmRYuMHj+hBq6hcftyHYzpMGqf1jddkLW/DwQE52aiaVrkZADLJJ3Uc8JqRCcpWwO6868Gdx0uBO4/PyywE+KpOQAjwIMLF8/+/v8QAACAASURBVJtYXVnE/sGwNq+lPJc9f6MAX1Wrle9QLf9ojqsZ12F4bechkSY1KeMI31ZafL1/u6EJvZf96sY6kClRr2Eui/kMk+NRLb/4o/dXSmF5dRGDQd8kVFPgorEpm+otz1QnwM+N0UA/nemtbZUCsoxQFBmyDIBBoCzLnNneuCcG+adtrm8I8hq9i/lcjl8jAD/yphG/ZM4RYWHQxZuvX8ebr1/DQr/nd8Rj1+27u/jf/+LXbHxc/4qlaUZT1yTG2GwEeuvmebxz87zY/Y7PvM/dfACIeQJ2Nr7fHIeNwQurBV/iB0ar1W2b/wurfeR+3Kaxe3wwxXg6F+YBxS5W6OaHRLrOn2nuvLPgjfUejm0nyfmYj8Kuo7cgxHvAysfUFK5zALhxczqlC0w2uCSCy1Px3AnzPg8NTey2DMgPQsXM8mKGu81ryTQPUWZaDoIzzzt5fNnyNOB4Wz8vv7OUAHj9lauiVJ4lwKdcFWU8LAE78d5AM2lqOg5VKVfHqusBtHUnilQRrapCGyRH5duCChweHjL/oB7Me3J8OJTleYKsdXtdbJxd9wAfZVZqCeJ5YMIp9uxjl3no76zQdERu2Zz+dJVTbIONcV58kOdmdTveYovH7QRnwUQpX7huiJfgNWLtubG+gvfevYVrV88jzzOncVsN+4Nf3ce//Y9feBB1YAsH5m7SG6TZXyng269exK0rmwzQvVbvN8nxM/BztpFOOFYvjp1VdjY9/4NDUjurfXUhx/Kg/eEzo2mBrcMpHIQqnl/nHdwrdqJcYJpy9yQ6YGDe7pfNzk+2FXZpnrMYUPBRwX0QT+rKIO7BvAzocVAXwM4Q1wGvKxrbmUiDOy8TDu7E5bUxiAM3PLjb9Bxflg5Pg3cCGD2C9G6+fAEds1+3ewWeEcDHcbRlI0/p9CWLyF0yn/V+JwFBighbliVa0pX5rJQ51GKraBv4p7T4Sl8CjvYPxXceq/snMtUrheXVJaxvrsoDZkSa4btVBul4uiTbRNFeeVq7LBykj0jPczPRnM17Aii2891zD/K1AlotXikFmsfAQjlTMulH6+tAWe4Fr8fzsyzDlUtn8O47t3Bmc9XsWGfX1QN/+bOv8R8+vuNBNQb01k95sLW033/rMq6cX42fLa/8Gnp9n7l7MC2dr5V3lnhlXwg5Ic92ArqZwrmV9mZ6IuD+7lgXpO0w6BDxkkoLCglPB9CK3QMAcQ/ytMoH+0qMIAXnw5451PInJY6oDS8EV1x7R8CzBOZgorJLUFvtmSQZeLq2YWYywfIItH0yhMm19QG4l5bGucYqrb07XvBpW3oKaDrdDm5cuxhtdGVtx+uzHgArmv8IkrUC4LDhrqOvCZRpV8Rm5V6W5wSynNi151odo6pSGyRXET6bTDEqmeL9mzUZjTCfzdolYB8VsLq+ggW3D34FYJfxm/3GTfVAoPiEYbD4YZQ8ZAbcs5IVWKnshQT5Sk1eg67PZEFzATIa4PzMbgMTcOZzYXLmGrijQr/XwSs3L+P1V1/C4sJAnFz3b//6C/z880cmHb77nd/+1m5BCw7GBsR/9+0rOL+xJCsrgz973oI7y6PX3OWyOO5v88fD7XV+rceXVzR2jw8mmMzsVri2IyEXwoVc/VgT+R6nL14A5tU3IF5q/4yHpA9uBJImMID/BqBYBdcl0A4vDrxJQPdAmdTWjWxNgb00W94Ql5bEuQJNg7sDeJtCkJ5om8nBu48TtQp4E/5rr1ypAdX2AB+paUlyCgCfRJU4cYVEab5pvGsLsOUeQDm/iVIrR6305+9bSpTIba1ro8XH6vJw/8DzIFnUw4NAi28qmAJW11fRH/R8JPnBBMwo8hx5LPHQ7WTYirkN2pi1F6DSMG+WZcjzzGyIU8pc48nrKfcbBnm7w50y5voCXI8noSGaOC6uhUMPRkIjNf+siX91ZQlvvfEyrl45h6XFgQPdP/nLT/H5nV3fuQi0du3HgJ6F51mG33vnKjaWF8SQgDXj286Cr0zPV5jtDYIL4Ae/NN+1hQ4Weu3N9MeTArvDmTTHw1hA4OUI723muVbPNXTfzyK4cXurjPN3VZwVb1g5pZ3tjAfWISAC31nPAa7pZNhhBEdygks+sj8B6GVN3X3j3Dpg5fTRnKs1yUc0d66RS5N7AO4BbTQ9ggNvHweOl6ThZUNYW1nChXPr4C4JcxT1rQ/jJBEUi7bHaS8Zp1KGyF3DNKr4x/wrghqzae7ac6iOQRVPJ2Fe5jAeHmM+nQmQtLSTUVzLr3QKWNuwAB+L518GMVRSAnAq/Q95KPkIEJBnYBq6VRwBRcSGcq1WrzlMp1FrxYutyctCUMJUwXt1UByQjIZtCZXHDDt7W0FqlRzAz5/bwBuvX8O7b93E9WsXsbmxgv/zJ5/h7uPDWqD3B8v45W+dTobf+9ZVrCz25W54HNyVn4Qnj7z1gG8n22UI18rr8E6msLnc3kxfEPBwfyKsAc7cbocpbFFyoHeF7wuZDx8pHmbLPlLmAJtF7zpLsKgCMGC3IO5CGMApsp0AKoFr47/QZC8ucVsCdCRAHZBsnB94OiG/uNYeau4c3EsT5io7Aqw8yUoD1naVOxYxGnv/2q2rLF/lO/7YFlyFz2kAfApQq+RrkkZcwAh9Im9Vidb2AOIQU5XXmL9nl0iwLnu1cta7KhZH+4eehvzvyuZGDYOy59rGKnr9HkRhxAowyYO/MFSqC/6sV2F5T4dr8NZbfYKp3uzN4oRt3/M8w3Q6i2nxwIsM8ryHY7X5Yl64F5Cb3UFwyEOegecFgj221Gn6YBox00atX97JsLG+jJdeOo/XXnkJv7h7iP3hpGIc3ujUrCMAI3+/m+MHb1/BQr/jAFqYY9j4ixNdSRm9v59wp1g+Npe7wdhPM7d1MMHMdp6IMDwes3zZsme9Uea0tkxwMx/JBejy9rAlCl6ZtFy482PgzdJxwB6gJd8/XwCvpTcFUsLr1BXyCa4omLOOhJM34KujhlaAkHdZaweTywEv5xmAuy3SKLgjRutN70J7Z5+V37iH8Wbyg4Arl89gcaH/mwP4po6qY1HiqTadQMBUPqNpVzCP5bt1ntsk2CJGKrdx2nialb6RwOHhkTm2VVIPFhewsr5WyZU7rcHzXUBJ3lPEu5Y3l5vEM9/YSw9B28necO2/xjvTrhl8yPPMTQScTJJ73rzIIA9YcLfgVcznEqgJDkg54AFAZo5GU4ZZDCydI6vF8o4B6xQoIM9zfPzNIcbTgpnNjXyZYmnzSXoGjpXCYr+DH7x9FYuDrj+8xlQu3w7XTvQLz4v3VoLytdjLsdhvb6Yfjuc4GOl5DlQUuHN3C9/ceYQHj3Z1Q26R18E9LzZyhenAlmv9LKpi5F71ZmUdoigoiIPo9yWAE+SAK0TuNib76qV0EswRS44C+RiotwJ2AdYk+SbB3TeoJXAP2iCXExcU0d7Ns+g8EJdf+ymlcOvG5XhFPRWAD7yaYGeKUZpY3jXE56p8xvyrIakiNBAsloWULJVyP2stvh6TnSuKws2it9+PZbK4sozFlaXaBNY21gzAs4+mRMfrn9HIDwbJsmcuC7T4TpYxy61tw/WJqRr8CXmeCU1+NJokqwUvwGY4SQF9Jj2YTqczpx8qo625DVTATMjKrGvXTRAy0gVJyITJGbCAr2OLjoLQ+nXYZFbgw6/28O3r6+jmukKcDcBpsuQfhHargf77b17B3/z6PkaTmdgVL+OgxzoXCkCvm6HfydHJ9Yz8TqaXWID0We/dvH1frCgIjw8nUACmswJ3729hNJ5CZQp7e4c4Ph7j0sVN9HpdEGkLh8N9I6QzH9lAV17m4+D+ruJQBi0X7guMgg9JdybY5jk2EgOwgIUjI0VVH0mtaxJXyiXTD5nVN75hGYSNmsQsSsWJ0ofgLmVK05GI4zoZxuPWjYv4+S9voygKxqh0W/I8CcDXMU8BfFs5GgF8VedB0Ld7AQW4pMVIRW4b0IpVqs7itPE0K2kr8j08OMDi8pL0N3WwsraGYjbHaHgc4a6wtrmKfl8PaZbal1I9hveRsATo82e9kZv2chOsDYGzyGa+eaQ5iWHYoiCMx5U71w6rApu4p63J78Y8xfgw0+ano5FomPxpQB5sNbZ64FQMMVVQIZETfWAn4jHrsnCjyRwffb2PeWEQy3UaTJpZ5g6nsZnx+VFY6HfwnTcuYnmhK8bllQJmsxkKs3XvoJdhc7mHK5sLuLQ+wOZyF6sLHSz1c/S7ehe/Tq7Q6/iOSxu3dThFUQCT6RR37z/GdDbz8wIyhclkii+/fqh3B/SZLJeVLfRoebHJjhaIiVs9PJ0333tG4rsTpmXSwM3R3ZAK0zux7/AJLilpavwe7nvnl2VS0thDuUyMkkkeBlCtVg1fVCaGicfjpM34IXAL7Z2XM3wcG8GXJdkqcen1+z29JTOvM3lb8kyBhZclHrkR+KbiVMqRpol6BUJWpRHzrwiqdkEBxAGzeZq8TT2RQCfNSG1GyxFm0ynGozGLLMF3dXMd3X44N0lhbXONmegrEmYfnnhrSi9k5N7R+Oc8U+6xa/dkybKg7fcX5oXR5DXd8XHt5PnHdQR17mmDfFRAvoscH+Mej8YecABw1ZI3+LYRUgp6zBhMM3Zmb9mjcmo8INK0cTxYKwzHM/zyzj6I8RWaP6s0b+j2FoJ+t4N3X7uoJ+MxYM2UQi8Drm4u4OL6AKsLHXROcLhMnTsazzGczDEaT/Dg4Q6oILPzHrMsZFpLvv9wG3fub4EKEnm1jteHvyO4oRKB2wR5eLz9ZdvROn8DbEqVP0kFP3MeYEArkAgeBlv+VZjsGeZFrxioiza5BbDHZsvbxqcS3F25BPSiiChIDy4tcPnCYrXAwtK0NK/eksvpqoDGw9NTBvgUmAavYfjUHGir09Y/ZeJq/mWCcr5JPlYKUhlQJUWFX6rkuF88zUq+deVKwPBAbo4jfhWwdmYDebdjPJUx0ffkd+HeW8J8Psd0MsN4NMFwOMLhwRCj40lcAJEeJcvDPut5VoRObvdC0e0kX47tFUSY81ns9rVFnRY/ff/9H+1VETRxT9tcvxXz5OYKDpjj4xFsxcE0/ooIlGXG4k4AMoc2ea70PsCA2Y8lormbfwF2GYDy2r7dRMe6g+MZPr13gFcvrbihAws+AEBmzKUo7BQ0DZoK+mCbfjfHO7fO4xe3H+N4PMXSQg83L69jbXlw0rJs5IgIu0dTDIcjPN4+0HnLFDJrZc0AFGan5AzICuDgcIjj0RhXLp7BwC45sZ2pyMgEAHk4jU0bEKDk6tLFCT4VZSN4MBG8+CgBs9wLHsTka9HONSJl2nIVo1hzWLYQSJ7OT4ARCxNlEZQNiZBAhmCoJGysGJDzeML0z+Mwms2NFWysr2B79wBRF8iRIDllgE/XTi3A16ZTX//P1ExfRZ/oWMTeu1aJp8Jbx2seYXw8xGy+htwdK6s/ciJ9FjsVBZZXV7C3vYOFxQGggNFwhPl8bg6AKdxBMFRQNO+DhR766DreSTlTnQ3zm5s997vdzJnnAfLgDrjhYgDAfO60+6Ojur34sV1H0MQ9bZDfAzAL07Hm61CbH5uZ39rsrtzxf07DNOvmLWxkCrBb+Qt4V/JGl7HXud2kO+YnwMiQ7xxNcPvhEW5cWPIgJ0DPAD0FR7GageNuJ8NbN87hcDjG2fWlE5nd2zqlFJYHGe7cG2rZoJdwFAbQC5ABd6NBG//ZbI7bXz/E2TOrOLOx6kuCfCfKv/8qGAdnSGzWyxMvLEKJh2vsSZC5jgL30I8kPjACv/RYfqvGMvjwm4F+jDYOzCIkBPYSr1THIAB39i/WQaAaWpcWSTlC7T0G8LYTcOvGZWx/8CuU3PME8BUA0yidtKD16SfAtgHHknCxbMTLom03oaZDkHzDuV9FHTfwi/qTL/qdB4/MhGECFYW3fAWxj4+OcXx0XFGxFWUjAJsS5R+mKZ+zLEO3kyM3yqezJBsKPpqZ6UF4ZFmGyWRWNaPeuic21QNP2Vz//vs/IgS9Eb9lrDTVa03+2LTlutUnA85+AhhgC9maRLy/NQrDgIqP4/bUMY7YFmzeBA9fSQz8Hx+M8c3WsaAVQ9jW9A1fubYDAwD9Xo5zG88G4K1bW+zhvdcuYm154Jf0KeXmEnjTva8Hu8Pfw0d7+PKbh5jP5gD4vAiTZ2UOlqGwWwTnX2oklQdva3L2ZWkR26ejqzkyTCDKkODNBvq3cuZ8eEF2FBCw5uy5+V2a4EmEu6hieCEy1l7iJdtpMXQAnk073ADXJpHLO08/RusjceCOmec5f1uuluba1XPo9VifndM6j7J71gAfk+ckAJ9KP5V2NdyWCdrDc5Jz2i/Voa1LvDE614WXKqWW4Xw2w2w609q5AfkTucpoZdD2P2XQjxVcJ9e7qiq7HwrghnHF/ihGeydznOzBQaP5dFFLeFv3tMfkgaA3wgFQP3ttfjIcB+PB0I0+M5OXekbwYZa/Nq8HWqS9CQDfPXPgATnYUgp4sDvCg92RS59nwVoGMtYxsHJ08kzMqn+WrmeGCy6cWZZArxjQK79Rj+2cZBkwHI7w2e17xpxErvMDAGS3r3XZYg1igNcCeUwc3st1wM/AxsMfeX5k0uVgGKI0yffAC5W+SiAeAWAHpRFAF+w5sAt/llYE2CW4MkAFD68Gdw/a+p8rTUHr6wemzBFJw8ex9cKrUi//uXHtoqjXGkg8dYBPttx1WFIX2ASAWX21cTGxKfpA5TDjEZfndMAvVYfxT6qinhv4taU9OdeUnMEHLOgi944mfAYGg55pQ2FOPbW7nGburBJkZjtzACA9Dp/Y3S50p6LJP21zPRD0RsLxeO4/Oo5rzADMdqZkzn/XGmVm1q+Tsr0VozKqMg++wQzgQY2vibfkijMgQGXA3Z1jZErh3Frf0XHzs41P0NqsXiJ4sgI7TXfryibm8wKPd4d6HJ7lU5vzTTlkgCq8JWI+L/DlNw9xZmMV59y2pmQt8qxtsYVAmEznGI2nGI0nGI3073A8xfHxBKPRGK/cvIxzZ9YdgNh6cP0xBuD+nmQyAn7JjdsLUC25Zs1Hk/aSEoSxBjvWXDc3ybvQMr+gwecySXof0Rcna84b0AkTvrm/eeMyfvXpN0Hunh3AV4NvnEtzbK9/CU5mpq/g2wDg23L2LJuAdlPPKllS4U16XlXpNf0oW/jH+JL8bfLm5Ephsd91yhLgzfWAPSdEt1GZyoD5HEQK+/vhiXpJdyqa/G8I5AEE4/GAPo2oKAqoPHfjuvrEMb4BjXEEX7D6UQO9sAR40BcyiCeCnXJX6hgI8Fe4u3MMAnC+AuiJ/El3z4NTCnj12hnM5oTdg2MgIxSF7ygp8hs62Gc3K5SAx9t7ODw6xqWLZ/SazskU48nM/XpAn2JeFCgKQkH6rOQ5uy8Kwr0HOzh3dp0jiQPmUHdWKvgOWUdadKqJXQyIntSlGslSOHuINQyx6MnJVyGInSK4g7y/AO4SXVl753EIwNJCHxcvbOLe/W1431g+U0G/IYCvSisQti3wVb92ifpu4VLyVPJJfQw1nYYqstPX4mu7QE/Rlb4mfx+AfuwlWlrsoWustbrtVE4JAlMgybapRDg6OsZ0OkdD98KAfMRcz5+VA9LZrEAxm7lZlZzUFpTFbII112tPHUZmd2DL3Ken7Mx8nq4h8uDO5gn4lN2TAozZnnB+beD8ONBb0/jz5DKl8Mb1s/j5Zw9wMJxoKwigrSKm3CzYu36RBXoAx6MJPvn8LoqiEKBdkAHyQqOu6CjBdxSsdWBr5wDjyQy9TsVrZxtzB9yQpnLlMd5+bsIaUFEO8YaytnlOeqQgoT2wOwoXIHhHwT0VJ0LfUnv3dAHAsw7Fd999BX+2+1Mcj+LrfOMAHzToLyDAx6SofeeqAD4QMFZkzWG1OqQxELdhUBkerZwnTC9VmCFxqtS0f7fb8ZUTrYPwi+N5IfS7HfS6Hb3m3TSUup3zCqltSLXySaA5YW+vsRYPvAgT74xzvZHY+njtr8OKosBsbDfJ8+unbaETSVNInmXio3NL3awzjZIHc4L3YJS2MxC8Fy6ekp6P9sdujN7Ruc7E8wXw1uWZwq0rm279JgAxIcTWjX9ZuenJ15EK6s/SukJk4/sh6AOE+w+2IbrH5L817y27WO5yIwPk4voJZvVXbACewjRYEhR48nF1MWgQsPWy87RZfhxvOf/ApwH3IMbcEcaTclliDtqVY++u7EM6mS/Ly8oA6LHIv/17b6MT6bDFgM02r41AN8KsGnjjXBqlVQXApXSaCs2DagnSlFXgSPFgzzIROckzVYrcLwWczZOJpZLOZlUB1LiKqJlS6Pf5eysBvPRyBM9KKaws9qCU0hvgZDDb1rJtysHmP5lNcQ4OjjCbNdbigRdx4p0Edg4u3o0jOwB50FDm3dWtfZ4rP1QLAMS1cgSmAO9degFsmNnG1qbFZfYgp8O2DycloH9O8d255cUezm0sMXDX/hLUbcEx0A+A38byGwIpR+MLwsZnhULA/Qc7HqzYeL4G8fKkO7spjq23Ek6DXyp4ToN3DMRR4iv/XKMU8nDZCzsVLtsyPUiQBksPLn4I7tQI3H2YlNXL59OD4evvbWo8jqcJea+uLOH3fudNNh+F55G7AB6aYqUrq1RYGprCdjrq6kBWkMaFroahMkFb2IrnvT34NQbiZujcIDxaQU+YXqowEw16yVf7u71ABF3knn+g7Hl5oWcOl1HodHKx8Q1vWznog4CdncT+Emn3wmjy9/1tWYsHpMleH1BQriRyLb55BtBh6+hLH6GO4IDHrdBS1nTvTfBeIGbz5aZ+11uQaeweTfFgr3ZDg+fKXb+05k4+8j1NBvLm2R2uYP8ccEstn3eCnBWA0QCyeA+Hxzg4HDqrC8MV8T1ZVPfwypBYBT4BaCeU9iR4x7RzoKyhNwV1BHJZwVJauwf3ENjBwJ2DrI8TA3f7LQhwR0ib0PJZHnna9jnkfe7sGr737Ve90CX3FAA+Gq85wKfiNSAW/tWy1QB8IGQMI1NQlRLLs2yU8XIApcl+W7R4ABgsdBmtBPDIWyRct5NjaaEHlSl089yDeyZXLYWbvR0OxxgPa7ewDd2DthFi7lmA/Jf2pqzJS0KllN7SkDmrnQG6PvxWtdrlmTUxK7H+3UTWeG3iuYbQgrkDJhYvUD69FDpQjNkrYH84FRr98+66nRwvnV/1yztMRrgZ3ueVZ9aCd9lkX+ZRqlgfDwr3Hmwz8zkAvk+9dSEQRsBWDugQ/CL96quyE8AuL0p8SCAF6hbYRdchzI8D9rRJHtG4nokH7DIAO9C24G7TsGna9MilWtLe7T34MysbWwZXr57HW69fR9k9JYAvAWBTxiG21jTprjzKoVU4UgtPUoh2cFbKe6MoFX61JVmfYCmcEv4N+TYRghL+yc6I9u91O8izCtjjHyIvbPPSry0PnHLU6+bmnHgltHnbTlrQL4oC+4cjzBLzVyrc7bYRYu5ZgPxte8Nn1ofO4sLh3iFKR41pCg3NogHQp7YBHsw9d+U8SPn4jpj/colIPgtJhdbq+Y2az5Z8Ltz5zSUnvzTVs7xxE73zDxixsiqN4Zt/3M+6B4/2II6NZvgswBRgwM0Zm2gMu4Wm3hC8ZVa4ph0B8xDQg3RS2nobYA9N8g7cWdw24M7j63AJ7smx95LlwPN33HkngAivvnIFN16+GJRoUGClMo9WhIxbG6kBSIVhTwjwjVA8hUlNXArIUy8v50+J8jgp4CJdG9Wdhyp/ivg1FKbO1URdWGCm+gDA41L556WFLgbdrhtj71pTfeY1dncoTeb3IdnZG4KowGzU6tTYo/ff/9GjNhFS7lmA/B0AU6CsIYdan1LA0KwhtI0Vj6L9lG/ICOZIVqebO6AQmMQxIgZWUdM9AyxlrQVhDH23tsjMPy+A63ZyrC72S2Z4qbGb+2DYQmjpyu8+oHjpKB4W8FTAdDrD4619CZbmXlQNBRcYQFowdGbnMji3uWQ6EREEoIOBsQfUsraeBnYwvkJrB+dBLn4rcLc0jj5umrfPXhbZzKVM/463Kwcd6Vtv38TFCxsuL4JZ4J4E4FPwXJXmaQJ8dUeiBuADIUu8kvzTwJgC+LgADcqP+1V0LOKRSpXUMF6VfyoDaUCOuSzL0ItNuLP3IV/2nGUKa8sL7pCvDAq9XK/lskOemVUmlR/uPB5PMTTg7ieVN3K32xBXuacO8u+//6MCwFd1dBY8jnb39IfiNH72cite7nptYu7O8zVnolvTPf9ERU/BmnkDT3cbfAJlo0MgN7DUfxYrEU/XnV1fiEwqNMCs+K52BsSdFYakdm5uSg0Z2eCI+R7AvQdbktBCYgC0QkOvyVO0/1bhOK4ntX6hXddp6h7U64A9qbWTBRcq8W4M7jZNKwMrS17OTbV3ScdxUnYolAK+997rWF9f8ZlNlHvc8zkAeBcWZ1Qbp5pA3NS9z23Srkyuxq8d4LbkXfKvy3vbXNZHtXW5MOiyFzwmD3/JSDyvrwzQMRucZZkeg+/2cr86CdY8DyiVuV1Et3YONYOiwGzSaJc76263Ia5yz0KTB4AvPKDEl89ZNzwcguwyg5r6JgI6mT8/jmsl5QbfoY58JklRwqMgWhicK4Vu51kV4+m5zdUFaZaHzHtoyhf+QeGGJnkebr8XxcMAbG3vYzKZ+2qIgCWxJ49eHmhSwNz0CtXz6J8PDkWoBHUHrCIZC6ZxYBdae9AOPSm4R03zsP5M3oT2HprnQ4uB5Z3nGX73+29iaTF+2mIKZESnPBGJQo/wKcIgFVaXVlzGtKsFuCbYlUqjQpumOpqGGY2RVdRIIlK5TW0lU+rliD42KVDvFIIJdyGgJ/kS+r0Olhf7BtwzB+Z64p3dGhzaRO9m1CvsHgwxm+vjP2k6b7t87otWGaxwzwqdbsdgN9TwlFKYTueYjsf6pVX83Y2/lBZfHR1rs+/JyQAAIABJREFUjLxfGFFFNT5lGir/7JGKnPTWV2u0C/0cL6Lr9zoY9Dpe02aT7JwB3k6Bt05JAPPgrn3DDoGJIuJw9HvwcJvBlCxrge/WnwGi9uJdgdh9zR/nSTLNMpjDcfeAXgZ1JzoDdQ7GTYDdg7YF64bgDgvSKNHbZy+fyUeQnoxn6dj3RLF0SfDudTv43d95C72eHMZKteFJMLFlJeImwCnCohxGKVJBVwtuqcgpPIr4lsLK0avpuV8E4Cvpaz0r/JO8KRoYq60065OUu42aKj3t3+t19dayoqDDr9b+8A8AOLO2iDzLHKDbYeZuJ/Mz6jOpzU9nM+weDB3fWfW58TF3u22ElHtmmnzokdo0hogwPDpy9/qXdw8EJCDLTcVx+3GswoP6dI9sRj5BgpR7cRUfODDPhnCh92KCPKBPyAubtLBWlCwQb9L3XtG6LIN/2a5y7+GOvncAw0CUAyNs2bN18AIs/XPsSmrzJVKWKiMUgB40xlwevumOkF3IykJiIGb5MGBuDO4lkDa54m1WAtzrtXfr62UGfLmEvJcWBvjB999Enmcyf8FLUAnwpXiJp6rPvYooml6cWVVsihBQ9IHKYXWi1aSdiFLDl2I/AUU8ctPiTLpUvNQLckqJV2nxKUUSAFaXB0YZgjvFM88UOnmGTp6JZXL2PlMZHu0cggrP9gQz679oGyHlnpkmn8D0qDveH/rGK2h8vK9++/NggpenQHzmneVJMohgx/L5s+TngIs1mv3ui2eqt67fzeFm2Qt7e1wjl88S6GMzGj2wx5v5g8NjHB755Ych2DkAZECrAgB2zwKi5R+COHEADzT7iNQctIWmngB1D8RSYw9uPdBGtXb4vAVxufwyjikPxsOWo6svW66R9OLaO0vfypTibfK8vraE73z7tXInkDxN1EXBJ/FUA5AhURWWnBbAV8nQDuPSjKmOJoWPlaVa5VnFO1ph0ZQa5Ki9q9Hi8yxDr9sJ0ojc+5fXxFM4s7roZsznbHfQboeZ6oMd7w6GIxyPJiwNwrTdpDvgBdTkb/OHmJmeu+HhUH5cdimc8eCb0xARurnyFWT+KxMhrH7PU3o6Td2lweISSU2ecbJL+F5Ep1987wKlveRS5niJhgH4R51Hwbv3tx0qVgFsOWZwUc2ViFfJ38X3GnrUysAicVBPmeJdW5IAdqm1S3AlQ5g2yyfAPaK9y7gmxRrtnVganoREOVjgIwIunN/A229elwUMn+doBZTqJvFUxSJCFCVneY+Fpd/hMJ3m8jVLg9VDkn85tJK+1tMGVUnUmE2ZKkXcRMBWhSpd+81v9P259SV0OpnX1GE3vdFWULvxDZgWT0R4tL1fEnM2+v+hub7KHe7syg+IWHUEDRiUWUYH39DZSXjkPU14+IKY54jGLyhjFgHj95s6L/40XL8bGWqoyE7rz4vgZurzuLxT9+DRThBOvPIMoMGBWgy8bVKNrjC+5R0AeRTM4YEMQoYyqAe3LGstgZ3zOCVwT5rmbVrmn6N05Rykwfi7uCBGr2lefukCXrlxOdKwBi6KBYmnBJNUXHuXKYWXr13ChfObLL0IM0omwfiVKeJxEtQ1CbT93urKJizgGNlpLZmLkaU5t25ZauPaOlUABv1eQMfuKZDf/C4OOlhZGnhtXemzUvRSuQy9LtvtDnZ9PPB459BMtiPBv+Ua+f333//Rdj1ZM/es1n7dBzACEJ9yy5xSwO7jXV1G5phZcmPiBGc0J69VdvIMo8kc9qhXpug7Fd3ycBFNmGugVArfEoGmDl9kTR4I+i8U+FmaiIbf+LMMUTZ4HE+meLx1gDObKx4cy1EcgDiwsf8dYXBfIU4jmZNe6dYs3rBFgCBCGDbQokkKIlBATKEMXBuP8BD0IW3J7M5k5LJFzPOct0gPwGuvXsNwNMadu4ntuE8T4EkS2bu1tWW89so1LCz23bHJOzv7UUb170mZiKIPqe5AJI7zbPD+PmMzfbRDkCZvTtVEkKqKr2Hf7+vNa3R9iRc40UXTc4jOb6w4M7xuANm5Jgrodjpm11C7YFthPJ5iZ/+oLBMRJsNWu6LebkNc556JJv/++z8iQH3UlP5gZw/z2Uy08GFjYh4BAjo5X68lPyvf9qcbWwrjBg1U4OXci6zFA8C8KOqJkq4xzKejGlC+9+AxuNZs4VxouYmr0kQfuRrxifwJ00H50b9DhJK2DgQy8Dxa8Az4Oa2dQh4+xVrNHRA8JH3ENM+sJR7DrByeX5mOZBsafj+G5t23b+HM5lryfaiCId7OX750Fq+/di0ZzuMS9OZPr716De+9+xoWFvsAtDXpzTevY3lpISVO0pEvnHgcKUwSx+LppON4tpG2rIq+1tMG1eS8FBytuChZmvOTtCOpEvT++jAaEqGl20CbP7O2iIV+t3zwjNJafJYp9Jkmr835hHuP9iRj+31NZ5jNWq2R/6QNcZ17Zru4KIUPAXyvjo5I74g2PjpEZ30duo8EMHVe+yk/f7ubZaJRC3VR6yPbdbO63pETiJRX8hkbRqLjhrPtX1A3n1P09Q+/ndJzKee+YeIA09Q9fLSHnb2PsbK8gLXVJaytLGFpeUEIJc3m5XsEfq1dIkqSE5MrGhxp6WJAIP0iJRsARsibmIeoy5TmzlpcYv94MpxbtfZu4nL+pXS9OR9K4bvvvYa/+slHODgcimylAD4sj1s3r+K62T53NJ7iyy/vBbgu454/t4Fbt66i1+36N8W0IXmW4623b+FvfvprjCeTeIKBiwF8XPRKNE0DfA37kyWZKl3m19L6laKuT0kkWs+5SXkmXCfP0O3qFUQhkEe+GACEQb+Lc+vLZnmwcpOQ7bRiPS6vZ9bbeJlS2Nob4ng8iYo5ORpVGWdi7metqGvcMwR51Urw/e19LK2vgZvrAQbYpIGeYGY3KiXC4J6Nr0BqxQDcaERmYoUYB+D34CwIpBQKIswLemFN9najBu7KAB8gSmg69l0D/UQU+Ebe7hIPYDSaYDgc4d6DbcznBJUprKwsYnV1Ceury1hbXUanm0uN3CcrexcNPqjG31wNmCey5CLG2qgnBXYX8iTgHsYX4BIHbg6msbF3KZujEnR5nuF7330df/XjjzAaTSKlGgF40g3rW29ex4Xzm87/5o3LGI3GuH9/uxR3MOjj1VdewsbmqklfdlqsTL1uF++8cwu7ewfOT5Y1z4MmePRoB4e2kxLImc5NmoaH1YLnk5jpmyFzIt1ESN2HdIJOSyN3Ii0+IhQDf6UUrp5f9UfEGhK9+ki3/gpg4/Ea9mfzAg+2gmEfJt+k/elzLybI53n2YVHYDy1o5APtm4iwv72HSzde0njMARpsU1rGppsrTOfeoyBCzpZwEfx4vQZ22XkAE4FMP4DgpgXAaf4B/WxeIM9ezLXy01m1uT45UdEAag3sgVPEsLjKzeYFtrYP8OjxPuZFgYIISwsDrK2tYH19CefPraPX65X4Uq1cMivSq1lzFG9fyoCbbAcjwC75poBaenDycGhA8o11OJiGzaSvtBIQy2UNwJctBl7Cfq+H733ndfz4rz/G1B3uFLQJ7KbX7eBb776C9dXlUppvvH5dj4WasXWlFK5eOY/r1y9BZVkU4N3kRfNvsNDHxUFfyClkJvm8sbmGjz76FONoJ4WXZtQ7HqeJJn0KZvo4fc17X+pAxJnFajDJ+Slr8QrAYNCVaYnvgPPUNX7xzCoGvS6UIiiyWrwHdzs82+909OY3Zmv1+492MZ8X/uXnnAvCZHjcVvxTBflntsh7ZWWxnSa/tRN8YPEGxvggz/2it7AhkFpJEJHdCoAgEmk7GjCzMEF0LF40dzAcx8ePQaWGOwRPVy4UPNtwioBNwJezsFdRSscncHB4jC+/vo8P/uZTfPCzT73sJ7kif0IuSl/8TeDj6m6Mmnx58ffJpx1Lx5eCTMuWv/fwIATwugvLXIzHs4p0McjnxKYbpmfr3oMliXehZDmw8vD8szzb8ltaXsB7777qJ0UF74S9WVwc4PvffzMK8NrQpvDOOzextLSAlZUlfPe7b+DmzSuVAM8LRLYxfNgitEL4sE4nxxuv30Cnw3QkkjepViHun45T1bpQLVGiXBsm0FaeElW7QmjuGmjx/X63vEW5TTwsOAKWF3o4u77IdrCzY/EZcqUtScqsie92c3cozdFoip19v7NdWT7C5KjVpLtDnOJGOMAzBPl/8S/+h0dK4YF9rluqsfdYm+Dsx1eIxkXfFJ4CHfNRFwEacfMc/6jtkySXjSjxeIkvpE4bfl7deDLDmB2Ry0HK+QGivIAyCNu4HNGoCMMDxuAAxMBNlrwDRHsV7P7ugx3s7B5WgvGTXKFcKUCHjSOLAALUA5BrA+zhZDoHPAlwtx0pDu4cfH18/7aL+EF6HqzJxZP1x9IIQZPXIStXm+2NjVW88/ZNRPdWIGBjYwXf/94bWLBatnuXeDuqx9a/897r+O53Xsfy0gKTQdJKi0Ya4F3ZBGXEefT6Pbz2+su6k8KF5nngLvLd8IB4GCdLUFDqkZI0kj4pVSIuRf1jXNLJNqBOC9DIuSNlfcUxbjwPhDwDXrq44SbaZVnGNr+xO93BdQB63VzPugfwzYNtwUtkoSCgIEzbLZ/7uZ6ofnruGW/Xpj7kTzGzvfUbHh5jarYCFOVXsIpijWu3wybf2YaL8+Ufaixt+Mao5M++ft/A6/vhuNWsyefG7R9ZLV4CS8Ebcw4cQUvEwYtBlShAD2SQcSMftLQk8PoxnEVHQafzi199yVLmvwHziiv9x4CJXSGgW4BIgToLLndqCKXyd3nnAOqybHmT4OvqiL307l2uBPf4NyFpbd7k98TzZuuPOH/eCSAnoUgPRLhwfhOvvnLV+5mbSxfP4L13X0O30ymlywHe3ud5xvhClJGIa2VzdOyZyepfd/79Q/BcXFrArVvX3OmMSUcVoRVhBDbpS8nzyrNMITdbrNorC+4zsw2rPd/cbt7ipxxVyBzJUqpDEI1Ulalmnon4cVouWyfP0YkeGsYL28t55fya3tzGAjtYeZsytuWYKYVeJ4fKgEc7hxiNplJ+IR9hPp5oU35zd6qmeuAZjskDQJapD4novyDi4+/xl60oCgz399EdDLzZhUhPkCOY8XRzA3KVIxxp0Mr5uniCG+QnE52I3ExK1/jasX/2QdjJebq50gFH4xmKgozZ8cVxe4dj3zg7gGDAYsGMoVrYYAJBIw/4K2wgOA92X+5UoeTBaTiA3nu4g62dQ6yvLTPwjTRFbdqQWgIGFtUkUY7J/PIyDQjCHInyYA2XbL9IPHPuMj5knhyvEFCDeg/TYWlUzbDn+bX3L1+7iNFogq++eQAQcPPGFdy4fknIJ0FY5qPUOakE+PC3DPD2FRVlFdSDLfe19RVcu34ZX35xx6fBXYMXRbcxHlzkPGEqbTNNjjqVlIvt8inbWJ1AIb53X2ax9rjilY5/syUOSeoKkpC22Ye8sNBgLN7U9dryAGfWlnRpKXKHcmVZMPHOPHc7WssfTaa4+2hX8BJYb+afTdutjwdedJDvdPIPp9O45luefKdn2K+dPwc9SY5QQMEsiHBAX4CQm3jdPMN0XjgNxJ8t719ku2EOQU+q0MkqF+6OrTXxiTzYu3jw8QtSOBzPsLogT9x6nl1BhO2DY9dQpTUwr8GJ7y70C/y9l45fROhL33EBbea3fJgcjrTU+Ght/m/94G1Tf/46sUuBbT15NEZMFooEhGyqgF2T+MKWZZkGd86Hy1CmJxG3XA8JGerAuIL3q69cw2QyxZmza7h4/kwFz6CT0hrgWfoJgEeYTsCXgjycPbuB8XiCe3cfiXIK68dguWthlFn668DENX8MwEMsd6uAykmp4I7fyoOmdFfYTkwmlpD73gtrzTLlQAUrm1IWo08lF/0wW3ysCVL+rSgo9PsdEep+g5ezk2d4+dK6AXBj6QC5ilDKbICjGZvtbDuAAr64s2WAPNqqufvxb3hmPfBsQZ76/e6Hk4kHed+QsxeMldPOo21cfeOW3lHIrmEns22t0eitdm57WdN5Aatpe03dpGfik1IGgJQDb7trkZtVb2WEfdntGnrbETDxARwMpy8UyD/eHWIynbPy9to8b5xtY2B790R6boQPluZj7U+IAjSI0TsWKEBOo+DxWUR4BDderEPw4OEOtrf3sb6+bBimG41Twv5KbklQjwQ+CbDH+DYBd30fhCbAPUXLwZDLkAZMD6oyTS+bUsDbb9+s5xnwk2lUAXy9iZ7LlQL4sIxsGpcvX8DoeFzeQU/5Wdl2/3O4FgRubJeDsesUsLaR7wsC0pqlKyZEnG0buRcrO6X8RGXFwqBMuplyofqT0vuyg7SSINsKJgUl5GnqqHSTeI67hYWe0cb5i8Leb8bv5Yub6HU7UDB2WVdPMDPnfZ1ZTb/f6eDeoz0M7alypQ8BTosHgPFB65n1H9aTtHPPdEz+7/7dH/wCwCxWYWEjRETYvvtQflBmaCP1gXfzTANGYfiRI/ZjzZY/S1eMLVtgg+fBwzy4+XSPxrPomvPn1d15uO+tHbZBE/lkjbsoH/uRIyg/f/GPKqVVe1ofyCfVWZqC4v4hr5//8ktHw+nDK+XCPMjLdjrK3GL04FQ2rrlKHBh/ivB08sHykXUU8oWNy6QQcrEOlHy/GS2qaMvvi+vUifqRgMq/V+9PjN6/WyL9kCcr3xC0fWfUl5eUO5A5KS8XIpST5QH+Gwnls2O5WZ7pSVxuXBduAheU0jO22di5neiVZ8rM6GZj7MZkb8fa7ZgxlL/3oBQLY2P0Ro5c6XF9J58L93IpM4vc0muZMnTyHHmeufFqX14JF2sIQmpK+Ce8WM0B0Fr20lIvwke8WACAs2tL2Fxd8OfDB5PrssxMwDMT72ydTuZz3Hu851/kqIDmvSgI48NWIP/1++//aLdNhCbumYL8P/yHfzhWCp8CthGR4eEs7IO9A0yO+Yl08Q/dfmzdjhlrsvxCEHNfvXwWDR77LTfG0t/6zQvg4V5rs8xvxM3scAaR1og5MLgeOsFMDGUA4DV6/qyjeS2/AFwBOQBj9nr/2bGGlb0H8g3g/Fm5w6Rl/B4+3sWjrX393OgicXHO4VXFR8jM+VWAuigXBiApYA/B3b6spY6o+Eaag7sD1wgtSrQIZCl3lt03yuSRdP6b5GnKdliWi+MZpivy7MvN+ZPzYX71AC+eI3ShjLZ0Dg+PNPC5SVseGJUBdTv5zQI3nyRnaezYvJ0wpy/TYYAHZXkhOQnP+YHPHs8cbw/uBvQNLwd2DPidXLYzkmfIc3++ug0TLvygW7s4Aw7w3W6OlZVFSc/rjdH2u7kx0/t6svUiJiuaDkCugNwcTPPl3S3/fciXUHsxLX52PMZ87lcwNXAftCFu6p71YeiUZeonvkEwnpGGhYhQFAX2Hu9oTztGZBp3QH60RLqn2zFAL0DZPrOG0fvB05vaKlzDEjTGYA0BBz0i7B9PcTxpVaG/EUcEvHXjHM6uLzJAJxQFMZC0DTNrjFnjLsItDQOL8lp31mAXHrhsGC9rcFrEAIckXyMPn2lfdzXrCIikSumVQD2Skr3h79FJgN3lm73DjjdsMxZ0vJicEHzg5bAyuPINypYBmKB1+WH1wr8tcDrWFgpg9PkFJF9OY+vLyfcsAN6HROiEcE7TO9ofAkTI4AHaaeoW1EFlQOez35nWn/N4mdemeXyvqcvZ4M6SEF6Z7FTkWaZPVlNKau4qY9ot7zRkDPx93mxnRjGeDvB97QYu8nGlaGPRmcvzDGuri3rjmhgf/wIBAG5e2UQnl3nhs+eVAX29esGXxTcPtnE8nvKPIyKgfW8I44MhWrq/aBuhiXvmIN/pdN53DwG4x/x2HmzpMSCg/GHbRpIBfTfPHKFr6FwzaOP4Rop3OBxYgaRWWBjgMo2Wlcc3CDrs4V7rmZTP1BWFLoksU7h1dRM3r2zoANvwO+AyZm/WsJMpbD604cofvBwl4PAyspq+ALjgovCURg4alox7GPfg0S4ePtprDdzWlUQJgLxKQ+flId+vOKifFNj5O+/qBIFWbGQXHQ9RjiR4gOUVQdq2A4eQ1t37Opd0vpx8HF8ONt+ibiMA7PjaPLFytk9PBeBFRyMC8GbynDKDtRmA/YNDA5YB+FnwhNagwUze3kRvzebw5mNmAbDAa9dve5AOLgfm5UtZs3PoZzsOSmv6HNBtp4Jr74I/M2uDdQjCToHNI3/rG7vEB2trM8sU1tYWGX/2IYBYSvru0tkVrK8MnMXEWip4OfCOj83zzt4QW7tHgewkHkMse15A/pnOrgeAlZXF97e29hBbRqcv6b9976F9AkFPiLDbzBbE9qwnAimFbp7hGHMQyNO6D1m5JXWuDVUEseWtqTcFMumYKXkEM2HPTFQpCJQBBXQPHVA4Hs8wmswx6D1/29wS6VPn3MoAABfPLGNxoYuff/YIs5E147OGVIAca6DJ1xksSEGCowcJru0KaIKzIpg0CwaMXNtHQbq8Pbrpe+tvOgYf/eI2/uCH3zqd8mpIQAnKdGeCSsxJ3ITNEk+LcYnx4EAYkzFIutzBrqAvgSSjFO1qCMYUxAkAO5DDAWuKPsLzWQA8FJARQFY7Ne2GMhEP9g7MWLucya7HyAlK6VYi4xPnlZ/oZViK5/+PvTcNsuy4zsS+zPeq3qu1930F0GigsREEAZKCuEqUxJHE0UgajcYTsj0mNbYVYcozY4dl2Z4JKazx/JEU4WjLy4yosENDK0SPZzSUBO4UKRIFkiCIjQ2giW40et+qq7uqa3/v5vGPPOfkyXvvq6rX3UA3bWfH67o3b243b+b5zjl58mS8MR2lETZQiisZDdskhqymvnVpV1E0ZjZVUoh0skQXNQ0zO2n3kVM6EY33mBb4yAl5vq812isP2OqD2uCcw7p1I2j4JNhV8hnud6g1gH3b1xtjOgby+GXid/KA7MmSdN2iwCvHz2HjuhEhcqYaGS+JQMoY6hPklwB8t58Maw1vuyT/u7/7nxwFMKURRopIcSn+6uUpFMudBCJgAMlmcZrMA02fQGnVCZzAy5ZLMN9Pnps2SRor3RPFQTc48HZ36dpCCAGWGMjf8eEWHj+0AxvG2/p+UXWf3kvu7Y8YnEOwfVftP+nbTIKkYL67AXXYvo7aBLv9jnG9Ylwn6S5OTuPS5LXs2Y3+7E2dVG+l9HK7M4Ay71deplB6YZigPN4QV/m/RCBzpqo0bsv9Xs5Tajvq0uv7prR27sC2r9SGOjBeST1fBniStq5UJpk5rm2Dibs5gJf1cbuNCgoREZAXFhYRiqBrvNaJSsNDwd9KxXbt1zsxtEMmvWfl+STJJ2kceVk9fq5UbspTNqhLUrcuJej7AHatX55XjNbse2Vts4aEXtXgKwbqFR13J4yPD5vT4EyG0jgCImAf3Ls5Sum2bYC+nzPvajUVR09cRKdTYHCgUa2rRwjLy1he6qz8fnl4bmLi8Fti2PV2S/K0f/928t4/A9DP1EnuSTqMnGGnU2D22jTWbd0c0/BPOE9CKoOQrCMVlBG5ysCTMlDaKx/LSJK8kJ94HaX+qI4Tad4Z0u50210AwTtg/UjrjjxjXpYXHHPeTglYvBpoNvDowR04fuYKjp1hd8IGzOI9/xhhg3leATbte/mWdeBXAjOk9AlsQlZO/IUe5cWII6+dxId+dP2a+qWXFL5inlWy1EnqMb58Qz3ibVwOaHn2cioYwKLSvbQ9L8gSQcqzVdKTbXO5nRlw5/1qAT5rTwV8OVVGp0uAXQfw5Xrr8pv2rxXgo+TOh1gJsWBRWdZ+RXq9PjMb1fCAro9LKxzr81VyNAy2bo+T2VhDO5KTLptP3tBVLu1TeS91nCPJpY+YeRFJXjJF/yGxxWR8iQQilQoJBDifhAEuX8qhAJBH/kwYNqG/FOlRCAQq+8JeZW6OjrUxONiwH78mXxoMe7avw9hISxmz8mfQeLnm38Ur13H+8jW0WmUHO6n8eim+761zb4mqHrgN6noA5L17pigiyGukcvUuuwYIVy9OYd2WzQCDagRmAKwqCo6iOohVY82Gx3K3gKiSLKA7HlQyWn1wIM/lxpmbmAPLLHiHgMj5xuwEcqzHcnHybxwbfDv7cU2BiDKLTwV6M3lBBOeAe3ZvxMbxYTz/g/OYWyggUn0ZiLP1YwO4Vsq3z+rygtI3z6V8Q6D0HUxZHJGXFzkPAmHyyjT++pmX8cD9e7Fpw/gN9Nca01URufS8fEPVZ4Rq3A0Au8lVn7dPcO8JwHX1lehrnfReLr9/9fxa0lBtWjL/1ac1ZREDuAFFmecKCp7nj4/5vHOYuT6nkiAgEiHBES/kOVuk+NvgdEznFOB7LF3b/fOQMiWNqw5DBdu68pxOQWlI7APHQhCzBUIvwTQvLnPGOScMjmcWBfqMy/W8xIm0ZCrlOKUHzN54AsEzrVltXhFGhtvxGFkikzD/4Hbcjo+0sHf7+viNGFOcXDtKfgzkx3223OniyPHzAIChVrN+4vYISzP/Hwf5VmtwYm5uARbQRXLvtS6/76GDAGS9SMYmK254MBMRgnNoDXgsdQojpVPmvQ7MqYrjG30uZSH+ortaMOfJhCDEgUHK2cZCxkYG2OjvzglytC+cOO+pTyfLfs45bBhv4/2P7sPLxy7i5MXp2A8Uj3qVLXeZIWQvoJa0MIcLmb4tG3rl0pz8ZB5bUE/tTnXnwHbp8lVcunwV27ZuxAP37cWG9WM31H+ZpL/CvK4Ceh5b97wEu/H/mjpWAncL7BnYSpKbAPeUvj9wt/XcqPSerlO5vctcDeCr2oDELJp2U2Tg1cmWBJbQ7eK4c5LeYbnTwfLiEuSUMknp4EA+ObsRCV9K0bV5y0AYIE8SZtJSJgDiO/OnokEkIKj+0haa+lKVE5S0mw1n+l7KJ2kv02Ync1tlhAjZlNqv9DajFUn7oc8CJcneRxocCnW5VXolQrs9iOGJU7hqAAAgAElEQVSRln6L7IXs2OH/BxoNHLpra/Tu59Jyi3f6Jbhfk2tzz9L8kWPn0Ol0QQCGWuZsejMh6qR4AFia7dvobmL1JDcWbgfIhx/90Qe/+8UvfneZCINAItZgtXmMS/FTFy+jKAo0Gg09A14Iu6jMo6Fc5IwHBxogdGIZVh3PGgAxyBMCQHAIIXKUAUk9n6R6pwOHeJCIv3rRImwZb73d/bhiEM4c+idJ71aaB0H7jUkYBhoe77xvB7ZsGMH3jp7HIk86K4lH4E8Andbx40+keti9+CadrrczgZCyhTlPdDgnEvw4m9CRqaiShYuXpnDx0hS2b9uIQ/ftxfp1oz06aw39uWIk9U5HdZdVQKxkWaPUXgb3CrCXCrD3deCbqi6Da95g/T4VqTyPXzldD0DW9yq3gdL7Uv4+tFJ5Nc+lfGVwRbMn80EB3amEH6eU0Uk74PrMnFrPi995UQZInJUOCU4BPlMZS13mPsfnXHQXjKI8WepjxH3deST3QgnsRRVvhSaJTwJQbEW0p4kv1yArQMWGUIBqAAjQZ0JPrRBFQFTpQ/LFceK8Y5ocsjE1ODiAsbGh9ILZ4M8+rF7fv38rhloDuZpev5FL6nnHhnf8vU6dn8KlK9cBxGPMB5viUN2O42zAp8tOt193tj+YmDh8efVkNxZuiyT/m7/59xa/9KXnngfoPVZyT2ozhhQeRAvzi5i/No2xTRsjgCBatMf1cMBbiR6RE2s2HIoiqpudA4IHXHDxLyL4BDa8IHAa4nIdW81Tfpytd05V9XCJIRlpNzHcuh1dmYdOt8Cb56fR8A77d65nlRuEHiFbj2egh0tqNccJCXES7No6jg3jQ/jOkTO4cGW2ZHiHXIqHkdaF0ErakIA/AjJ0v3zk5qHbFANk+16yvJcfYPJk4KmsQG24cHEKFy5OYcf2TTh0cC/WjY9kz1fEeOp5s0ZAz+/WAupZjjUAe8x+i8G9ru4STS2DsNZE9XXUgbttexmQbZ5ymgrA23rs/UoAzxPEJ1RTFb01cXesKvQqPBPgI72ZvT6XrOp5DjmfpHcpVyR5AeUykCcJP1aStAJWTU9JrS+gqm202k9SCT0LVuUvdZA+yvtL8htNgwg8onZX4Ebsx7isyXQbYNrCanxZ+uSGO/6CGi/r9+Ya3pv5ThgdFYDPRq35oyMSALBv+wZsXj+s4C39LMsnCeDl28X4hcUOXn3jvJY3NDhQqcd0NkoxWJ5bqPb9yuEtU9UDtwfkAYC8dxMhCMjLT9Ti+bo8ETB57hJGN27I1FdyEh3xQBIpPDhCq+kx1y3S+hclST+wCi4KmnEAiFEJKXin4ZgGo3xQhzSgHLaub/fdAd2C0GzU8eD9hyIQLkxex8mL0+h2o7Ha9OwiHrh7KwabPl+XA5QZkmFPygXwc+5DR8BwewAffGw/Xj1xGS++flFB3UrryZGOyuQgCkowCDwXDFOgBMWkgTAExg4AZnwEywwQW/Zz2hVc1ms4d/4Kzl+4gp07NuPQwb0YGy15yKoJtU9otTSGLPcommoeUumCSjflouoISQ7OWUG3HNztK5BJpFi6Rund1lsBZFMOmf+y9BVGYmWAByhtvXWUJEyX5oRK+HLPEmdS3cctqQvzCyzJC5Oc5HOropds4lddJX8kgJWlPxXASxJ/duVs+0wgA/glFX6AMbCz0rdSOkZvfeb0rArpv7QWD+Tr8EINXa4BZcmfuFCh8SGYfgfSciugWgDneI47h8FWE42GIVLlQamX8XrDWBt379qY1ttLmhXVtJhukmcvvHYa3W7Q8obapbPpwWMyH3R6vXB1tvxVVgv/rwT5MDDQ/Mbi4vI/slJ8FfBT/KWTZ7HvoftyTpCyT86Sa+QeBwcamF3oorz2HmCN7WTQpjICySGyJcYhbsqHt3EgDA00sH6kP4O7mfkOLlxdwOjQQNQCDDYwUHv+ce/QLQKmZhYweW0eV68voCgEQCPwXb2+iO8cOYOH79mGdaOt1D+iq9IIUo6eskeO+zLWd+iuLdi2cRTffPEkpmYWY38oSFsgNhI4St/UEIz07arSOWO7Sv61zIBhFtRt7hoCEXD23CTOnZ/Erp2bcf+9e5OEkCVcU1QWuxLzviKom5sysNfVuyapvS5/LbibPDXgbvOvVTXfS3q39xk4a1w5DVVo+eqaglUA3hwnaiU8S/QF4e2edjgHT7kcuriwgGQxn9Z0LbA7gyIK8F7lRmOEhwygVbKWfAKipv/y5jmew+YblUaOMBEpE6+rS3qmcSzAgwA0ROBiBiYwZ6AKBe1nB3Jp50siM04BPFFWKNOUgT3F7XiBrfMcOcBHOjAy3Ib3jk/Hsx9U/qTv3Bpo4KF7ticnRC7qa9XYDmS+EakzHAA4fuoSpqbntPecY6M706vlazsWQcDCtTn0Gb7Zb4Z+wm2T5B999MBfPfPMK0sAtcoqewH3+OHiIJu6eAWdxUUMDrVjPGQdmaVq5jo9S/UDbAgjEmCcsWwNKoZ3SC5yJV4c5kQQF2t60m15EeCZ44TD9g39S/FXri+hCITp+WXMzHew3OlgaWER60bbGBkaUD/Q4h6yGwKWlwssLnex1OliYbGLmfnF+F4gnWi274gIC0sB33nlLA7u3YR929dlQJ/W52P/KmOjBAuJUDJx2jA+hL/x5EF899WzeOWNy2kN3qy7w9SfSfsC/BageYscLGBn+VdhBkiYgb4/AYiAM2cncfbcJHbv3IL77t2LkZFe37JawaqAXpOoDtTTZX/ArmlWBOrUjiq4m1prmIFS0RUA1hrXxAisTXq3eVaV3i14m3dKaXJmQI3mM3W4WJE73SanMMRg6SyaObO8B2B0dBTj4yOYvT4HdTOLBOwiOQpYOIKq8uFES6ikKan5+ZWTMV1iTASn9fQ6E0iMxzSka6EPNlqXHrVQY2Uv7aBklCdMSDJ2tc+EpiKjr9V1eO490x9CE2QJBGbrnXOEdnMAAwONSC+cB7mAUCRJW4cI99ND92xHe7BZktgTkMu1HOYD/j7XZxfx6okLdhCiNTjASyh2XJuBVZqXxdIylpeWK99mhXBmYuLw0X4y9BtuG8j/83/+q7Mf/vA/fpoIP1aW4EMIcC6txwAO3W6BqxcnsXXf7gjuHgguDnaR6uVbEOK2usEBj8XlENfsqbRXHkBwDg2SgYdoOc/b6SLQI64f88BOEnxU2zU9sHm8P5CfXyowt9TVgUcgNLzHwlIHC0vxGF5Xnb+x08oEsETMLAhaz1JH3riEqZkFPHLPNviGy9bgmZQlER48kUPs9+QPOqZpNjze8+Bu7Nw8hq8/fxLzC8sVQFfQ5wV2aWcV+COhKYgqDneUyYNxnKPMQOl+JcSt78msT0+fvYQz5y5jz+6tuO/AbgwPt1cE8dqSeoFwzQ3VRNRVt2ZgN3G2PeXcNwPu5nEtuFclalNXCeDrys8Be+0A37PMrE8pTSoSK3oBfpbQXZKGZRudgG0EC6v2Tde7927HqZPnsLSwqMAenyVQUUtul4MN4CoAj/yPlpVu8mdAoiOu9MT2aWJeUv+KoZ3ivIA2gIbkd6xyFwHA0NmkljdSOiNrUtPz/CeRwi0TEOttkLi7TkyPGOKBHEZGWmwP4UA+MJ2WJV396ACAA3s2YtO6YX7bqsRe1tg4Q+Oee/UkQqEjDAAw1M63zuVj2467eLVwrW9V/ef6zdBvuJ3WYtRo+C8URfixsso+DhKw0Ryv3yDg8unz2LJ3NxiLk6SOCLohOHifpPxW02NhqQCFuJ0lWuADUVoHfCAEn1TvcNEBRpLWIyXwlIzyFOiJsHHDEBq+POVWDpdnFhOXSqwqdx4DAwNYZA4wW8eTziIlNwxsiWjbv+pXXyYUA+iZi9O4dn0RTzywEyNDg+qmF9yGzNoeSX0omhJrpAPnsGfbOvzihw7ha997EyfOXdM9rnVr9rDAT1yudVULYVLSO8k58xJnDfLiNzdpaiGyv0BEOHn6Ik6fuYy9u7fi4IHdGBpqadtMwvr8K0TUgXptHtwksHP73ipwt+3rxUxYIlgGYlu/LaUn82DbQakcqSK7t2XKGJamKHhng1jV7HBGajYArwK9AVYBOFG9Nxoee/ftxKkTZ9DpdHXt13LqItFb5iB7LlUbqV/y2TIEjyWt9jRVAR6l9LYvK1b8Mu9kjR3yjqnszEre3GuPGgGLKPWvxoFA3qV7s3WO4JIqnhmAwEZTreYABgcHQBRFNKXJghFOtLGELRtGcNeOjWn9vYfELn+F+XLO48XXTmP6+gK0l/i/uB6fjfB0WzOBF/tX1b/lIH87N3aHkZGhLySAsmpYQ/zN9eVT5xH46D5VE+vUTgRDjLMGmg0zaK262AJlqjvmM/dcR+7GNQHL1j63zS13A67OLpvyUpvaQ61YNv+K2l9AEUJKRwFFKT0RIRTJIK4wa+TTs4v4yrMn8Oa5a4mjdXbwO2OY4jLio8EQvKH2AD76Iwfw/kf3otHwCZCDAXOYPjXSevxm6XuVre3BRnZCFGC/C2cWRvCWBYruf988dQFf/tr38OL3j2NhYSlxH8JAlX/mRpOaf9Lo9MzQCaLsZ5qSMUWpRFOXKUf7J93qGBbApXK+0jtBxrydS4qxOUOmbS/VV5nL0jZbh5Roy5c+0v6UvjPgUqqvDuClfy0og8S1LOnY9tI6J3JdLMEh3+LmRFvo0hxJ4BDnRrPhsWffDgwMNKCuXh27gHXpRLny4TDlE968SettOfwXrnq4jANyN63ln3PmeFp2J2tc4jqktpZPwlP3rsbFrbqFdfZ5KsPG2/ank/Dkr09p5VkjpRP3uiMjrfwQGbh0rcfgRr/0j/A6fPkQHQV6n1zr2m9w9sJVHD992U4mAPF0u0H1fbK6FE9FgYX+/NV3AHylnww3Elz/qs5bGpof+tA/PuG92y2DqdFIPprTdTyz2HuPD/ztj2J0w/rkY1k+IP+1g8Z5YOr6MjpFYB/NPn5o8zwNQOjpUc5Bz4R2Pk1yHbgAhltN/Mj9W/p62TOT87g0vZg4TWX745/JyWk9f9jiasZHGiqnQFAihpZ5CTXXgQg7No3hiQd2ojXQVCYIMIRbCW68CFpHziQB0ebh6vUFfP6ZY7gwNZsxK8n63jIwIYsrsvSBmZUIuEWpnKIISb0fCF1Zm3uLgm947NuzDQfu3oV2KzewpJorue01q1aabwrgJqZEd0qPe0ntJq9JoBBdLouymnLJvTZ9CWxt6SVwt+9FtelK5StDk+fJ2rRKmgzgVSpP0B8xOt9WJTgv0j4r/JJL60xSzvNKyQuLSzh3+rxpB6kKX+iTBGcmfqZxAHi5AKU686CGdj1HWgWzKmnFtwSZZJYOSKQy5ZSnKTPeOaNqNItB8pTTh2TAi0SbIv0K8N5j3foRFg7ibp0QQqQ9BYEQdIv0ux/Yg/HRVvyGqSsZyE2fO1bjM02/Pr+IrzzzGrpFYZjG2NaxkTY2jQ/ru6TBZhnOFLd0bRYXjp7p+T1qwtcmJg5/uJ8MNxJu9+Zu8t59kYg+3ktlL4MjhDj4r5y5gJEN63XLm+yZl8kpHu7Eir492MDyfAGCQ3ABnrxuC4n74OOKVoCDDwTiU4hku4kP7PCGywUB8A5b1vW3Fl8EwqWZRfWSx2+og7JLhOVOt35aW8AvEcdkBCPP833sMknLgH/m0gwuXZ3Dex+K6+uyviYrkdn+elZbEqzK0/FAj2r+DWNt/J2PPIinXzqN7xw5q8sG+TY70zZus5XalHEg0ZhA3yNpUCBzTInPWxlCEXDizfM4efoi9u/dhnvu3oXWoPixzv7UhlVBvXRBNQn6AXb9v5L31oJ7ud4chKmUrxdY17W3DPqGoSi3gVJ6iRPAFgguAyZjYwXgVXslgx5O1ZxidOtKYCHTwMcCMTzUwo6dW3Hh/CXDMABWO6bYnj3Xlqg9gDYUqV7zhlx3sujvFZiUal47x6LTGdLHxO9B/LJKR1nvbp3okO3zmnj5zg0g82Ef6bjQLaeGdyABXoeCEw0Pt1TR4plKe2lX3O8M5wiH7tqK9WNts8TIFvP5qydmy9TzzAtvMMBrj2nfjbQtU29HYZX5Bt2QVf1brqoHbj/IB+fc54sifDyuvfOKt1mHd86rdzkiwqVT57DnofugvuYBdjHLqitALcQJQKvpE6cYgODFNzMhMhYBAV7X4B2PTmEe7HY5hDiofAC2b6jZdrVCmJxZRLcb4BxQMMFI630OiwtL8ShYVzNjDYBn0UwdVXJHAkUbZ31CW89084sBX3n2Ddy3bzMeu28HGt4xoCeEFwc6MoNIgB+I1rAhUb+Gd3jfO/bi7p0b8LlnjuHytbkE0qZN5RPtAhOdnHgYNbZ5X8v4vZ1aqFAEHD9xHm+euoj9e7fjnrt2YXAwnz6rtaeGjqCWYFSS9Afs+WUOginqLQB381DaY+G+zKDm7a5K5rbdtIY0tQDvXJpnjHg+YpZxbuNy6V6l+vRuVkVvHUdJhWLPMjo2gq1hMyYvXUmMg+UJFPANs2DvkaR0i1IVquBqY01vc7JMQDBMgY6BtE7OWM55CCHE0/GIkDyN6vwXRsAwDkjW9jylM0aACAnUYftE9sPHCqKa3WNoaJCP9CaEIgJz8AEITJvJYdeWddi/Y0PqEuFquKFWes+dFgHPvnIKM7PiY95qvqIxdGuwiWyMKx1G+QJEwML0/w/ydYG2b9/4lTNnLneJqLmaNE/kcOXCZSwvLKA1NGw4SzUPA0E84kG3wDWbHt0iROM5ThvggBDXdTSPIxRsvGe1BBboPRyGh5oYbffXdZemoxTvzOEw1pp3bmGJGRuqBfryei1HKrBnYM8TOLAmu2xnIKAvkv2RNy7hzMVpfPCx/dg4PlQBesBI+c4wQ4T8kB4mfzu3jOHv/8w7MPHSaXzzpVM6F/Ts+OwXJfjCgH4RkuW9uL/Ntt4h5b/Z0G8RRRFw/MQ5nDx1Afv37cDd+3dgYKB+LKwK6uZZpR01wM7RWcZ6cK8Bao2+NeBu7+vAXcss11Mr5ZO55pxZHlNaTRrFSR2KTkRldlNrJHVWTXnHIKCe7FiQgGG+Oa2AhAB6tMZ31XQs+q9bN4aiKDB9dZqlR6fSKEweJ6W61H7HaZxP3umqknw+Muzj8pix38hK3AqALKFrOn4pCkDDp7Q657hCmX9yYh2BMkdicYcO0h55zuiY2YpHVHPfEnTbnNDYoeHBSIOZ/ngfrfXjyYCEAg5jI008cu/2eJ48JXsLUg4u2U0ICyUM1/HTk3jz7JVSJ6XeG24PmH61KF9CfM5WLC5iebGvrXNnJyYOv9xPhhsNt/1ElU9/+r+5BuBbCajS5I3rrkGBKQRCt1tg8vR55R4teMUSzKBiItUeaGTAZgEjBHN6WkgqY7mX9Pa3o08pfmG5wPX5Ts9z2YtAWFhYUuO5bhEQQvoVRTK2U0O7bG06X7uW61h2iO+hhntkwJbTBcLUzCL+7K9fw4vHLsZG8+SyBEji5Axm59KkyX7OwTc83vfoPnz8Y49h19bxWuv7ZMwYP5wSEfM8S0dJE9GV3QM3+bvR0C0Cjr1xFl/96+fxg2OnsdztZswHmZvsn6ncSjzxtavaC2RlxtRp7ENBT55IoVquzhFhj0z7pLRyHiBrQ0rPtdj8+o6mPmkLauox6aRvsm9v5r/O5QzgUxsAqHMYAXgxsANc5sUujVVR4YvRWcyfTiITyZyBwaUxrdp2x3PAGyM9zhPjgc2bN2BsbCQ/d90Zwy8XVdVynRvneWZISkZtanCXG9e58s+ln6SRc+092BjQSX3epOMz42GM7cQgz8lZ8MaOyZfr8HpefNn4rdz+ZMAn7+O1f5oNj6H2QDqnnvs/2V/F8zXedf9uDDQaqc5GfIdmw6HRiDZd2u8uteXa9QU898rJNFjNeJW4kaFWNs6Uia1gfLyYn7qOPsPn+81wo+F2S/JAFNa+QBTeF4J4JorA5b1HWZoPATh/7CS2H7iL3T9GFXMgwHEe0XYFAnwAWgM+cYkBfGwsqSSftsfFdfnAgy04ggt8DVIjnB0bh1d4nWq4dG1RlxMIslwQ38k5YGlpWQ3IhMiI0XhZpicz2Kg2zjBKwqyA0i4BQK3cLTMTrfKBZ146jTfPXcOPP3EXRoYGkY6lTSpKcVEp6/fg7S8qzZNoQghb1w/jP/jpd+C7r57Dl759HAuLXViVvYCW2g3YtXh5pwwsLeDdxsDVdzsFXj92FidOXsDd+3Zg377taDYbWZpSFhOxmrSeclG6LJVlCVWerZfUHh/X5KthLrIc5fpLoG3u3jLp3TQ7qb2F+XSArlMDZv0dJi6OZdkr7227+HmmLpdhnXgHLUsMVazTGmEAAEK7NYiF+YWkogfy9Xlpvz7jxpjz521SokivykGfy3+GaAQTKf1lQyBi6TfVIcfowqV97iohg7Ua+oz3xJsy5DV0PzyfLqdCFKr74UX6JgLa7UE0XDydJGjfyXeLdPrhA9swPtJK3e4JIA/ykVbosi2l7+7g0Ol28c3nj5ldOWmMyUhoNBzag03FHENoNQ9sFNGNgPzboqoH7gyQh/f+qaIo/nud2MSDg+rX5i+fu4TO/ALcyFAcAIGi60JRDZnZKeA82PRY7ga1EhfjNzlONujQZMAPwqnHa++i6mjTujaGBht9vd/FawtqBQpE2qAECg5z80s66CqaORPq1uRz6YiYuRFiSLrNUOLE2rUn4BPh9MVpfPrzL+PDj+/HvXs2KdCrns7ZOmNcPLwnTX4gudtxcHji0E4c3LMJn/3GURx541JsqwC6ah6g8XVSfPJT/zYAfM8qqPZxp9PF0WOnceLkedy1fyf27d2GRqM0TnqAOj+qVLwisFf/VIA9L1fuK6XcNLhzqkreCiNRC9x5/b3TUaVdSdNUMrAzwOmF0DsqMQOOvb2x1E6lZTKR4FmQiPTElk8pEWQpKzEHAk3Jrz0gywWCwxHwbRugutXykp2Dy1zW1qzoVQSCmE7mreKdhiAMCs9pUoEq1k8gpGnP38Osw8cuoezwGWI1vTBFZJ97ij5LkBiA4GO6ABF+XNybrvJDbJcnB/JxOXXftnHs3rqOu94QU29e0jJeBNX0PPPiCczOLcEMLOSTjFSKN5HIZlZpzBcLS1ha6EtV3wHw5X4y3Ey43VvoNHzgA//wuHO4O22Z86jbVidxj37w3dh53z2s6pF9lEb9o3/jxFpY6mJ6vsNqG1YZWTWac3Ae1ThJzwTlsXs2Yd/WHkeW1oSZ+Q6eO37FTOoYRH0IABcuTOnWuRRc9bIiEVFFos+2qMDcm78W8DPQN0AvoHvf/s348Xftx8BAUye2XfcHoGkJeT1JrSzGdbG+7x+7hD/7xmuYvr5Y2WanSw0hJPV8CCj0+q3aNkcr3PUfBgeauHv/TuzZsw2NRlX86imtlypfFdj5ZiVwt2Cb13MT4K71mvZb4lcBd9PGHsBdlt5tWVnfWP/zrJ5XydeBXdSm9Xcn6++WKbDgilx6V4lawbp6ihlXoQAS6zXiPoBrU9cwMz1rhfbYLlOvZQi0WqnL5JP3rwPyRB/yaPvNsvgSzcj6ufStBPSJxI2snefp6+uSj6ENIdjlmESL1EEWpyvYFokIGBxsYmioFbUDIWn7Io0JGB0axHsf3qvbmu1Yjn0WgZ4o6Pcn1ri8cvx8VNMbtUXqI+kUwvYt42g1m1lcmitmbHO+mdOTuHb+St2X6RX+cmLi8M/2k+Fmwh0hyQMAEf0p4H7TSvOybc5K8yLhnzt+EjsO3h0/dHAIJWM569MeBLQGm6C55Xh0LBvXiac8gLfJBYeCy0mGdtGTnnOEhnPYuak/Vf35q0mKV4AHlLvvdAosLXdivGHPneHaqQemJTV9DrJZnEjsNQCfrN4twCMD3iPHL+HUhWv4mR89iF1bxuL4FgmE5BxqS4a4bXBpgjBRFbfCD96zFXfv2oA/f/oH+M6RM8lJjpHgo8GdGOTF/bGBgIJCBdDuxLDc6eK110/hjZPnI9jv3srLT0AFuuuIs1zVAr486g3sMUkJbE3CtwLc43U1rQVuS5QpT1KTbq0An9DQSsxiKJqAVeZgiovJDajaMrhMyeXNOFdre5+eW8CWP7qOrBWkOhLjkABLD6DLRHWq+qovoz3lcXprylGBTngRvo59ztuGlddx6Rs5AhG7GXeAaj0s4CvbBKhhsY870tWgWPQfju1wRNpnozsHYKg9GLWnFCVzzw6xvAOaAwN4/NBuDDS8jhNvNBX5y8X2+qi2wcUrM3jh6BmIdtECve21ZsMngNee7AXwMWr+at+q+j/tN8PNhDsK5Inwm84FyNp8o+GQcYCBdABNnruEpdk5tEZH4ocMiAc0yPoPz0dR8TsAgwMNLHUKOETgJke6/uWFUeC/hHgCk+6XB7Bt/TAGaqSyFd4J56fmIxDC6fY8JTYEzM8vpi0lq2pV0rZB+6cO7GWCyjKAWtMbBsraOVjAFwlarq/OLOJffe4lvPfh3Xj/o/si+8RETlz8ysEVQljTIT7R6AJIB/t4RJ/Qv/TjD+CdB7fjM18+ggtXZnNmRLh40Qggxd/xwTRxeamD146exIk3z+Guu3Zi966tZg23khy3Etir+XuBew1Ym4g1gXuZoSiBtsnVW3ovvWOZeXAMmIzRKK+/SxCA1/V3IAEr4tiVtXjZRudZPZ18WKQ8sUwHq6LPriWDRjEDADEEdKl+0TAgtUcaneKtZB/drmqy9JoJVG1DS08zTW22xi797lSt3nAJqIWOqNYCMLuZnAH89OpWNQ/NG2lyCOKrnncY8Va6wPXG5dG4E4qIVE0fTxCNKP+u+3dhuD3Ixo4ytrivnY/aBqcWTxCT/cWlZXzz+ePsRpwA2Q1ggZwvh4fM3ngqjcPSfAEI3fm+reqXAPy7fjLcbLhj1PUA8L73/fpr3vv7yqr5XF2fvOE9/P7Hsev+e9FwgPO+oqq3qnbvgMVOwPYv9dMAACAASURBVNXZJVXFJ4tLyecyD3eOLU3l+n2HtmL35pE1v8/k9CK+e/wKD0orXcTnDsCly1extLikVKo8V+smNZAmqrWMFiIuQBmjcic4BKR1ePtcQZ2yuLIjne2bxvC3Png/No0PGaYBWdlSrlXd5e1I6QIRljsFvvCtY/jSt99ApyjMzoC0eyAQoSgIb8t6/FrDKk2pe9xuD+Luu3Zh964teSrqkWcNwB6TUX0ZVM79doC7abPNa/iPOkagLp28f1K9IwKmkZoFHO3+9wgwaf3dIW2xqgPwCMoJjIUBQKmOlEfuk0OZXBMHXJm8ioXZ+TS3Fej5P2FW4KrlS7mlMlcL1PMmB3zb16Hum5UEB3lOTGD0GoneCMMmanqlQyKklWhCJR2A8fEhkz55uXv4wDbs3LzOtlKFoqg1iOCdOBJEDUQAvvyt13B+8hpCEVX+Qk9IHHRIIQB2blkXj/3WQdlbTQ8QZk5fxrXzV1f/MCn8u4mJw3+rnww3G277FjobQqDPyNpLtoZs1bhmS935YycBROlQ1nBVEhVgUcMzoNUUq3ZJR6oGTvfJEI0CaTlN77CzT6v6s1PzDFIwZcdBRrwNTrbOZVvkKP0K89M0Rb61ruy/XkGxdG/rIAKXRQhFKG3tqwK8bNM7c2ka/+v//Syefe1cRrR0i5HPmay0BsoqU/nLxNY7h8FmAz/7vvvwX/37P4p929ZnzEZiDt4mgzsbaJXfKknrwuLiEt48eU4GofxJefgiMWWJkMkv1Zn6CFSq1zB11fwxpS0v0u5E1AyNA9Wmr6lXQZtMXmkLsrYoUEg9tg5NZ6QtC/AA0v/xuQV4QXEL8DFxEj0VVGsAXsoTNsJzvOcikgo+ivHejHV5Li20288yP++wfupRShPdb+f+40VoKW9N89mvwdvyrM/8JNB4Ltsl3/Wu6j8/81fPZVb91Zd82ItA5ZCXZWiBtqf8l/sy99mfhK0Dezdj99b1cMaWqoHocjpuuZMtgRznooV8w3u8fOwcLk7NRH8IKvClb5UGJNAaaGJAdsbIeJTrMiMEAIEwP9X3qXOf6TfDzYY7Rl3P4U+J6J/IOemimhdwB6IPe1HbX7kwicWZWbTHx/hkuqQOduyOtoGoKmrw6XRDgw3MLnUBpG1z0Xzex61fsr2O4gR1vPdt92ZeFlhjKALhHKvqiRIQMo2Bc4T5+eQFDyKtcLAcfEXbUibMsF6noMRduXGRmpllL3ufy66VsbEq+5zhWiq6+LOvvYrvH7uIn/vg/Vg/2jb6PNn2EtsFQG0kZFudUUQiqmEdKATs2jKG//JXfgR/9d0T+Ldfew3z3U7ktoniEZBvM8aXQ//V1+dot1sViasqb+eAnpLm/ZCXs5LUzrnrhpKMk6w9KabMWNjxZwoxQJ/yU7mtti5bD5XrT3VHokxGCk/bqsDEPXNwozJ+YgKsi9kYb0HdSNIG3FXFrg+q/u5TOSI+xrTWJW5ZVS9H3Nr65FqXvWw7AZM29Z0r0SOSuZWmnslDtiSIMEreJGdCpYydXAtvxP2s0nd6YySnONAtcd4DoLgrSqzpxXNpQGSOWFgHPKk2VcoNnrBj4zju27MJ+iW8vCNbAfBWOzCIx5wNEBHOXbqGI8fOxZ1RntQWqwjRWj+YUzcBwtgIW9Url4lq0IFL6M4tYXmpU5OoZ1gA8Nl+MtyKcEdJ8hMTh48Q0ZF6Cb76t9stcOnUWQQKSdKjZDUOjUv5hlpNlWit5F8YaZcoglHUGkR11v4+LOqJgPNT81juBrVcV62BWJETYW5+sXqynJHW5Vc5nU6ke82X/mpckUv3ckKdVYEXgUE9O9kuqrjESY61ute6uayjp67g9z49gb9+4WRiZGCMh6RDjMgvXL0QZ9lTrN7GvMeHH78b//QffAgP3bMNBQFFiGfOvxUYT338+i+pPrRbg0lSt9Iq0JfELqBpl2KqZcSUVu0a00h5Nym5VyRyYQQM08lt0roEYGw9tl0lgM+Of4Woac1Z4brFM4EukNTiklMwUY3mYoZcI6WaphzgxS2qjm8r0RsGIC0XQqXeJAlz+UYql7/qDAf20KxccrcSsUr52g7kErzNp5K313LjYV0ladpK0Sptw9TnVJoutzFzvuPyMrM4I+1XT9pDeu6AjWNDePhA3IoaHd1I23kXFGskmnKAmdYLLC52MPHiG9rnKr0bTWKKi30+rL7qdRSnQc1jMz2/IYO7pyYmDvct+t9suNMkeQD4DFH47WjNmRvhWcO7EKK1/enXjmPXoXujEwQ43W9JEKk+xXkQGt6hxQZ44vfWgZJ/egAU0gRGCBgabGLb+rV5uRMieeryXFTJM41SSZ6JiyPeH0+8F7UkcfQs31Yk95SIYiSi5p7yE+T0RCmzJp6t0WfMTxXorWRfhIBuAXz2G0fx3Kvn8EsfeRA7t4wbZziRIFZt5cxzn7h577l+57Bp3TB+/Zffg29//wz+5Isv48r0Qv7+tyXceO02Z9scfLEWab1Scw+Gpx+pPd3b8tO46dWWPE/KbMelrVYYjfTc1JXlM2SVI6O6neyuNAPMVprnZyI1S3qWqMnVAXxJAlctQQncE6fKyqqY3pfU+zAgIq72nL6DWaoSQVvinMmPPDj7XuWgbY/9Zppp/2hQho8bQI7QEIne8XOXH05DXAex1BzfXYyhc2ZU78F0mlsly+SBYrcEEgdk6VuKlC/v7J1Dq9XEY/fvwkCzoRoE68JQmDsZM4m5i9vnnn7hODqdAmKAKZqewiNK8eBvxEzkyBAfhkOU91d5rEtcIMxemal+l5XD266qB+5MkP9TIvx2BPQE7OIBT9T23kdp89rkVcxcuoJ12zYDMMACB+t3HiCNGxpsYnG5MF7vktMc3Tan+R22bRjCKtgLQKQQYKlT4OK1BZVedPKTGOjE38jIEK5ejQMlFU/ZBLYPM8MZS3hJFXWqVhewT5MvDs5QeibArc8qgF8CeLEZCHn8yYvT+N1PT+DDj+3HR3/kXjSb9pDO+H/QiZKeBOIlEuEEXDKScgDe/eAuvOPe7fjLp4/iqaePYblb9idwK8KtZR1WLI2AVmuwCqQ1GW8E2KtxJUawVPhbBu7lOnuBO3qMaxdBRU54S2CYA3wCSwZvAQGXTOJ0jbwG4J3VEggzQIZhSNWy9knAwbyAYxpTcciTpPvUxsRwwMRnuwdg6lWkTx1r65bW15GnbByIT3rFyVSnaOHkIgK80zKIAV6TcL2EKkCLNT2RZwGGZzrTcrGdgDL2Sc0v/eiawOOHdmGoNZBeRNQnBBX0lE4Sxe16TMeePXIGV6bnlJnwiKr56FSHEDyy00WDcxgfbadBWZpAaS7IWwOL03PodvqiRfMA/qKfDLcq3HEgPzFx+OiTT37yJSJ6RL3RhTgY81Pq4scuCuDs0TcwtnVztqZeBnqHBNqtQQb9xBHwug3UoYXsWnEE7Ni4uhSfgBU4PTmPbiFgzT9lokXFSBgdG8G16Vl0u92cW+cBJdx/mbyXx2JOJCmLt9vqLOirkwlK8WXAt/77M8t7A/D2VLsQCF/89nE899p5/PJPPIT792+GkKEE98zk8P46z0sD0WNeJBryrQpirn6wgZ//0AP4wDv34zNf/j6+9f2+zmx+S8Ka2IIeidrtwQqI1ia/RcCelW3Btqa8vsEddevuJnUpb9ZOqosnJd6ydi32Hc6lsjUNIAiVwNXm4VItwCcJOd9zr4DPkzZJ2rlE72Vft2EepB1W6wBX9ngn7yP1pa5Q3/ml+NT2VGaKz/sSpduMpFDKkxgpkdwNI6bgifR9eN1bAR/S1gTQ9pAbZ+pP++EpHuPNaO6dMAm8fY7ze+/xjnu3Y3ykbdrqpDHq1c4BkUZzdNwa73D0xCW8fuoSRHfiPSEUvEWSP1pkPJKXznZrAK2Bphp16/yoTkiNm700XX64WviLiYnD8/1muhXhjgN5Dn8C4JFoZZ/U9mq0YcDfe+DMsTdx9xOPYLDd1n3xSTKPEJOdPQ+HoVYTcwsdBXpCBBz1Zc+qdu+AnRt7b5sTYioATwBOXppFQRTHIxMIOYrSqfoxDsP168Zw6fIURCrpUUN+W5LspV4LCnZ3gj5TcBfQRu72Fvn+eFBvgC9CsoMoS/iXpubwP/7JM3jPg7vxSx95EMPtASXEsq/eM2cPXkMLIMBHyS1EpwcaLxqWTeuG8Z/+whP4yLvvwac//xJOnOtr60pfYU0gfkOJ45p8PQ0pw2/lUVZhP8BuctWWacG9mu8GwD1LT3leylIlphRG6wVEEIJde4cBYAFjpx7uEvAngJf0FuAFcJ1KhiWARxIck1SuTYrPM8BO/SDjOaZJ687pcWpD2bMdbJwpL7vnNBn7R4DZSl/pV+k4oVFSnzXwAyX1OljKl74XECWYn5xnYcA9BLGy4D4jyFZ1EIAGg76eNSJaAJbmHYBD+7dg+6bRjNnQTYrme2ibWTBzjnB+cgbPvXqKVf7gXVeRKStEkIAcOesQEI2ex4ZbqavLY1MHbhrAYWkZC9f7xuu31QGODXcqyP8xgN8hQkO2wUUQTBK8/bu0uIzLb57GjoMHIsCTAENgqdBK93ECD7cauD7fSX7qHQ+2wNbgPuYbbjcxPjxQ28gM4Bksl7sBk9OLABDX44W4CLArRx0J2tDIEJrXBrC8tMzvuHLHiGqsTLKtxJ6YjpJUD+havAXw+HP5mjvnKavl6+IqKnzeljjx8mm88PoF/PJPPIwfeWg3O8VJoC1qSngXDwJyvLxCDoVK9JaviVcHdm/EP/nEB/H0S6fwr7/6Cq5dX1xlON2C0CeQrxQGW4MZIK8O6ilVNX4t4F4P7PqsFtzrwDrdUJ60R/oSuCMxpnlbrNRvQZVUwgUMwCKBi2cAS8BZBXh1cGMkbAveMkfTPRTAyxI9UHVxC+TlWRwSQMmAXRkZ4vfKNQ5Wa5D+T30gb6iajRLNIJNWyonr6jFtJsmbbyRlJ7U9Eh3RPneGvqS1+yiFAzB0JC6VuKiB5cKj3U1kSmSFTub4vp3rsW/HBogdhgNYsyfmucY+g+ISgpx3PzO7iG9+7zj3eTxnxCPS9BBSWaK6995FAu2BUfZVHwA+QjhpLSqTCcDc5Ew2jtcQLgH4834y3MpwR4L8xMThs08++cmnAHxMpPVcgk/r897HE+ZOvXIM2++9h3fDMYhQ3DonwBFd38ZnDe/RGvBYXC7YAQ4BIS4FwLm4dc6j5954YqoUmLUV47aL1xZQ8KCOA5WlBBe3+OlkidmjNL9hDOfPTWrZvYBe6uQ7xMmrT3sDu4kLBuStmh5IYA1CVYLnQU92n31UFdQCvOSdmVvC//Zvn8U3XziJ//BnHsWm9bE/I8FhD4ORWsdlEwCFq263S1oZsZd0eN8j+/D4/bvwF08fxRe+dQyd7hp82t9CsL6RMDjY1LVLG3qBet0zqnlQBfbeZdSq5PVibeCeyiSbNas3azelkuvBvQRwzqiw9T+RYmOe3KhNJDHRnMX0AvD5uryUH8ux6+9l9bxK9MxVWJCW09gSQEvRhgnwZj1e8pl0VppPGowcoM3rZ/XYB3UkI9PAcCYryeszLlRU8laN78y9AJ+c7sZ/EjPAP+9Qc7pcpH2BAOe5nhA1ByEAWzaM4JGDO9Cw7gkRKxANjxrfWW4KhOVOF1//7jF0i8AqeDG2YzW9E2bRIbBPe2LBbnS4pVphocfiEj2FkG4pYHayb6v6/2Ni4nBfe+1uZbgjQZ7DvwTwMUD2dxPE5W1c6zEL6vCYungFs5NTGN28Uba9Aw4oeI98cFCXtbLmO9JuYmG5ANiiGz4omHg4FIGwa3MV5HWwU/macGFqAUVIkrqV5AsZm5S4d3KEVquFdruFufkkkVopoUz/k5q+Sv0TZ11emzdcea/7EFV01hlO7P+SZX0wzoaIgZ//Sj9k6/WB8OKxC/iNP/gSfvHDD+Cn3nsAeiCI+KcGEJiA67G/qAP6pPKHj0dC/uKHH8AHHt2P/+srR/DsK2f7HWdvaSh/u1YrWtbXCwI14GiflBmD0sVKwB6f1IB7GXA5zoJ7pZwacLcl9AJ3+6y8BOBkYoDBjvMp6CIHWlXVw4CWcZgDQPdclwFe1LkK5gbgayV6kz+p3o2q28lW0ARODkCz2US308nAPTEELqtP2pXZrSC1V3qnBt9tV2fBMg1KKxwq3zlOxWS0mAzvXEZHKqCuuwiSml5AG3q6nAF9QNX3QDK6Gx1p4YkHd6PJzm2ylyXz1gLs2uhIY77xvWOYX1xisGZJnhkCUc+Ly3KIyp6NfcdGhng8OD3lzzuHAtx4WMGBsDQ9j85y33j9h/1muJXhTgb5pwCcBbALANK6vPxlMDEf4szRN3Df5o388dkS1kegd+KnXo3sHFrNBhrOoRvY13oQri8OZu+B3Zvy/fErATwBOH81erlzslYEQPfwOuH+weuI0Im1fsM4ZufmKwTVVS4MobQE0xL6kNoj4JAA3TyjHJT1WU28dYwja/nlQ25Egi9MWmEAEAiLS1388VMv4hvPv4lf/fnHcdeO9Qz0ABXxGwlhsg50SL9wPdAjAFs2DOPXfvEJ/PgTd+P//MJLOHWhb8OYmwq1mF2ToN1uZZIs0Avw30JgL13kQG3i+wT3rKoe4G7ro+yhi3PGApuVfEuStHLLKulaCRmKBSsCPKerW38X5sJn5VgVfkorBScAj3/H143i2tQ1ZVDKqn1JqwyAMrk5M5BeqnKJ+tg0rqSPLXMCgI+DJZbgE5aqcRoXwC5nYi7xJ68qe7GRSu8Sreudav48q/0Dy2PZunyI54m895E9aA9GKBLGTL6LaCDA9DJ98tjOb798CleuzcU2Eylt90QokJzeONUIuEhzETA8NIiBRpTuZQcACAgumCN2kTqRgNnLfdOVr09MHP5Bv5luZbijnOHYMDFxuADwR3IvQFN2/pLWiAlnf/AGOovL0Q0rGYkUBqhgtooBGB1qankCbtG1bMC64UGMDiU+SIxRrKRLJt/CUhdXry9lUqwenyrXPCmKwBIyv1NzYADDI8NVpzjyK9IvOa4J8WfTiUMcaUMRNH/5ONeg6XvUGXJHOrnL2xAZLwo60QO3T8+HNxJ+wf1aEOHY2av4jf/pS/jjz72I5W7BB1ZEYmcdjDiXiGPZsYaT44UBVonGdAf3bsI//cSH8Pd/9p1YN9K6pWOSVvitmogvWq2BEiNVyqrPKDFn+ixeKHNpaq+WJWXYstOF/pMyIQxBapvNR6QpTB5SJsICisyTvF3V+iStfGOyqlkwA8xl5MZ18qsB+DJzgBzgNQen8/xAgFccyyQLfSkzaQ0EtNW5jX2mzIFDqz2IVmswOYuRtDx2G+L+1afxnpzOJNeyTp+XHMhkTm9gfslJjTqyMc8c2CmNOMpxyQ1tQ9zYluoou7aNbUrvUXGIo2WzAaLUaeZwo+HxxIN7MDbcMuUk17wxLZJTIPnbiFb4r75xASfPTaVvId+BKIuTb2qdDTnvsG60rd/FO9Lvk/o451rDchfz030b3P2LfjPc6nAnS/IA8CkA/y14Llqu0bJX3kP9wF8+eRrb770LQFS7E6KlfBAnCGJ9TwQX4p5555cZlMDr89EVrqjqRaIK8s2ZQZA4gBDI4fzUPAqSQZY4euFm5dzr7PxrrsA5woYN45ienkuqtRWClZ7kPpfwyRBnWROL6QMlgluW7u2hEeoG126TY2qeS/ZUkewzj4KhVAYzXv/ma6/iGy+cwq/9wuN49OD26HqS0lbH9IKRhVfLfO9YJRhVcARC8I7Vg/H5+x/di3cd2oGnvvk6vvSd47Vn0K/eyyt+gBt62G4N5qlqvjVVLhTK031NFWuW2jmOkN/YIin/z15mY6/M4NSp7Osl/RjLwhPPh5TJSru69m6kbCvxk86rXBIXMEbpXuclP8gk+ixPUs+ntXlunbHGl+15ZUYAAEbHRjE9dU3zuiwPsmt5b92+W5K+y+r79MS0QSJdeRQyw8S2RonJgkrwYrWf1PaJbmQnzrnE1PFXjCfYqTYv9aXUk/bDp7n9jgPbsWXDsLZXAFZFdvkwYqav7+dw6uJVvHzsfARv8I4oF5ddvY9LrU7X5kWDKgwkod2M2+ZIjAL5m8ocsts2idca5q9Mr4k2mzAF4N/0k+GtCI3f+q3fut1t6Bk+8Ymfnv7Upz73JIADNr48MZQzh8Pi9Vnsuv/ebCLo9CCzjmZUbyCHhU43I7rOAe+9bxs2jbfNyWoJDDNDFB7Yr56+hsmZRS1fNUwyI5yVqJIBkVw7F40J5+cXM4mu/BMjtwjYpl3mOsgzJCDOToEK5i8kTxX4y+p6ZQTIgja0XWWveJm7XJMm8LPZ+SV89dk3cX5yFg/ctQXtVlO/jZXU7Jck+UCK/zwZkY+JgYbHobu24N0P7sbUzALOX1mjR8k1zeP+WARJvWPHFgwPtWqf2RuqeXorgL1S9grgXs23Orjbdq4E7hIiYWeCigTCApYykcWhVQ7AsYzydrY6a/UywGf3VqKX+kw5ntNo2QzW3t67uLTkXaJFzjk0mw10iy7ARsKZBF6Wyp3PJGg4JOnXeU2b8iQ3taltTt/D/qQ92lYr1aIkCSvQJqYkP4QnzkIHm15Sa8enpZJSWQBwYM9m3LN7E5cXv673Ds2m0c5JGzPjRYdrMwv4xvPH+BQ5GXDCCCWykCiC0Q5xuvVjQ/FYW4nLvOcBEIc7Un4gTJ64yLZgaw7/YmLi8G1xgGPDnS7JA9EA76dsRJQwxcIekK10QMDVy1cxde4CNu7eAfGcFiXDeDpRCIGPkOW9mt5hpN3AtTkGKuGIA7Bny4gaoyUATcCp3CwD4tkr86AAFI6UcJFy8AACVEJxwjEyZynTYN36cVy9OoOiRvKUsKqUBAPW+ixnAkTC1yMXKfeGVz64Rp7XrdFX70WtH3KjvUBZGdHOIn6Trzx7HM+8fAp/9ycext/8wH1oNCLnnq3B89t5fu8gQC+HTzhigxt+aZ7yWzeM4Nd+8QkcPTmJz3z5yBrW6/sD8LXmHh5qYf36sRogrgd14O0Bdn3Old0suGtsmYGgPK3neSD7rXW9XT6dgA7AUpgAsEh3LgP47GXMvFNAR5WZgJZfWn93Th1YRSZA5i3nySRxKccCvPwljI3x2nwJSLO0WpdlUFK5+X31++VCT/l7SCYyVvayhRWqlYjxRuvnjCRP/K2cizTR+1ywAJJDHKU93KeO1JIeAdi9bRyH7toClXq4LvGlX+ICkZg7wvxCB998/hhCEdf+PRtNRy0ff3cjqEXBDlFLCwdyDgPeY6g9GLHAxXeGrMPzmHbGTwecw9zUNLqdbqWPVwn/st8Mb0X4YQD5zwK4CGCbjRSLe1HVNxpegf7E80ewYdcOTsmW2hTi6WjOOrpxcCEOoOF2E7MLnYgX3mHL+jbaAw0GdFRBkBEn8Ay4vtjBzHy0uuSxBaFFTgcq9K+CPaXJKQZ66zesx+VLV3p2SGIwmShbYlxiACxjIpNPlhhUMyD3YuUuanqR/NFberfPil4SfknKj0yBYQC4vfOLHfzRn38Pn3vmdXzibz6G9zy0G1Wgl5dlb1Y8eVXVz3FKOZkkejjct28z/ruPfwDfe+08PvuN13Duct9bYWq/w1qCcw7333cXGt5XGLKszF6gXkpaxyj0C+6U/5flzWJuANxteWVwBxJgWBAGSm5pFewh/0FUyt6Aku6VRwJamWMCnLBpnFG3yz0SKFmAz+aqFCV1cLPkudch57K0AwNNtNstdJaXK+CeXycUV6CXv4apSN2Sw30V+O03I40gbqNskyOTBEg+A8TK3oI4KLmQJQZNndMsOIhinQAzL2P5mzeN4NH7dpr38jrWkrrevHiawugWhKdfOI6l5UIt6QF2YMZMfgFk1vSRAQAgztPIYWy0rWcaKKMIh+AQwZ7rJxBciL7wb8DD3TMTE4eP9JvprQh3PMhPTBzuPPnkJ/93AL9Rfmb92Fugv3j6PGYuT2J8ixxRGLm9dHCKdXcbJcOR9gBm5paj5B0I+7aMqsFe8goHwICiqspBOHtlDkUIhsi4ZEEP2UbHE5WPRJSDaXRy8zwcXzeMK1PXsLzczSUU5MRViakBeZmQ+oDbR5qOkmeq8r0xTAS/Ww7MVqtBK0j2CeArwF8G+BpUOzd5Hb/zR1/HO+7djl/9uXdh3471up0uSfSJeKhbXKYIQlDsvnqYvO+6fwcePbgdz75yFn/xzR/gYs2Z0P0A+FrCvr07MDo2lH8/1IN6rL+MrHXAXgXn7L4G2PW5MHa1eXPmMS9u7eBeVs2TqTPOhwTuBrl4xpJK0AmsY367f94eQSvgnVukU3pm0sBoD8THvFB3u/7usrpysAVKe/nL4C8ADsLI6DBmrnV6SPBpCUDbnGkn6utMnVYf7PexAB+lV8sE1EjvnEA93TklJ5wuCjAC9jI39UCaEOme/E8OWD/axmOHdqHpRReXGHDZV5NL8qSdSSB8++U3MT27wBI2a+48gOAS00gEuAjOYKc30a0tj7uGw+jQYNTicmNFcHD8/pH/I7XMX5pZwPJihzUfa6YMd4QUD/wQgDyHPwDwX6CmvWWgl32WJ19+DQ98+El44oHDgypKAQQiD+dJNL1oenZ1u9iF98C+raORU6Rcgk8ca5LiCcDZyXldOgDFQZUkAlYVisFdyCets+OG5/nGTetx7uwlE527srRjjUyEMB3Z8wrY5+r8Mrhna/dENaBvdigQKQPUc62eSpL+CgBv3+mF1y/g13//KfzUew7gV/7GIxgbbvFXjEQxOrBIQJ+MjxLxKDMGQog8gHc/uBvvOrQT3/r+Gfzl0z/A5LW3xrX0+PgI9uzZ2hPQY8tWAPXshmrj+wH2UnHIYuvylhp+o+Au1yrBilRukFG2YCnomUKShJnWyRXkRG2mgJjAWwFTGYp6gJe16SS1W4A3YM5p9T0olu1NU6T1Ks0PDqDdl6MQfwAAIABJREFUbqOzvMwMgF1nNrQAAJzXN7f1ElI6fbYS5pRAXOKIQUxXrOWR4bdEwUiUaArIqTpfjZBdTORJ1PdcLjPanrfLjQ238fiDuzHQ8NpkB6ffQoLMT2UiXATjF14/g/OTM7E6nr/i1S44HjOFMPui0TOH8CBK/KPDragxQNTskkj8BMCFxPzooTmE+cvT2sY1YvwUbqMb23K4ow3vJHziEz8986lPfe5+AA/XPZf1ZTtYZqdmsOe+u9FgP+EpJGR1lO4JwGDT4/p8B855/PQTe+CdTyCFOnBMgDrx6kV0ukWCGJ0l0kbofSLoTieRJCWeYYODA5idncfycie2AQl4BXCtpJ20CjkzYp/J9jni7Xt5OuTvGigD9IBkIR+37tk8PdbkRdoPOfCvBvD5twWOnZnCF545hmazgXv3bELDM+efKGEVFJJspZKjfaZSkXfYvW0cH3xsPzaMDeHMxRksLve99lbT8Pin0fB4+KF70WyarZhVZK7elm7KoJxf3iSwpyKyzL2YyhsFdwlxy2NyswpV01op3ardrbSbS76aJrsXpprLpWoaIBnX9QR4zec0fRng43UO8FKWAje3t9lsqMrecz7dCuqS8Z2orb1sU+PCKttImTvQrXaVH/ck36e2cLlchj1rPUtjvwPkY6FUfv5N5NtBmHHvMDI0gHc/uAeDAw3TX9Vtd87FbXXS57L97sTZK/j+sXOpfiPEWAEmLflIv2dEF855bBgfVoYpX+6k9IZC20Dozi7j6rmpDFvWEH5/YuLwF/rK8RaGHxZJHgB+D8Df6/Uw8HFHYozX7XZx6sgPcM97HtMtNh7QtTZ1fQtxfUlouCjNrxsZQLNhAJ4/uGwTA6AgRgCuzS5hdiF6tvIA+AA63vuNOMAQiRukDSCkLR2JI3eSgYBNmzbg1Oy50puSriVFZiINVLIEVdqshNgwKFTKJxy4Q7K2N9J97XXZ8E6eWeaiJMmHAASEtXLDWZhb7OCPPvs9fP6Z1/Hxjz2GJw7tjNtmxCWlgARcSU2PNan6mw2P9z+6D+99aDe+8cIpfOGZ1zE9t7R6w1Z5l7vv2Y1We6CWqVkTqJeerQbsmmYt4F6X/1aDeym9YyJMjBxe2TAkicshzg2BHQUc+U8Lg3xLyDOp0CVVsxfgLgG8oJeNj9U5rcsyF3BGPa8ACr1PIImcGeBymwMNtFqDWF7u5AyHSvWGwTBt0faWwurAkyqPclDsIIEzlXIpvV8mKHD+7HAal76vMEJR9c1d7xAdirGhXWugiccf2I3WYJOZkjQuHOTTkfalF09kHC5NXcfzR88AEOdmjtffY73Z+jtreyK9J30vx3nHhqPPAgrJ0U6U3HNGVJg0IofZyWnuo8TYrSKgLAE4vNqXeTuD63Pf320NTz75ya8C+PBKaewWlfZQGx/8lZ9Ho92uOERwzMU6z/HMYXYpYOv4EH7uvfshaui0fS0Bu5Vij5y6imdevQgZqPlkLa3TxUYmosX3/DhNcP5z9vQFXJ9NauQy0Y1xwjtXQd2CfZkhyEBf0xugztLkfZGc4qxufS9b/lQTsVpYA9f86MHt+PjHHsOebesAWDsDs6RCuZbCvqu2ia+BPO1yp8DXv/cmvvjt45idX161PSVYBgBs3rweh+6/i59XMvTOXQvqfHezwJ6KyfOXPgxlbchRvV9wt+WLg5RcCo9gkbaCJRD1Zs5Ivgi2VpJOwGyvJQ9gt7ZhhfS5Zztn2pPKiDMtAXgELntULBdlGAHlSEBFgdmZ6yVGIjEIMPXaeyC1Q3kZeV47YXLgsnH2e8TxLk9JJWNJU6Z3Ob2oagJtmmbD4/EHdmNkaFA5CUpdIbyYxjkAA02v4212bgl/9Z2jWOwUvFOHkiMudciF5BSNKF4H/kvJaZoDsHnDWHzLQCjYiZeUEwoCmbiCAorFDs4dOYWiEGdpUt6Kmsg/nJg4/A96Pbwd4YdJkgeiNL8iyFvL98WFRZw7+gZ2P3wort+43Hc9Aezmltd1ADTgsX3jcAKyymAmiFOHeAgD4ezkXHS+AONXmye3urGtcP1I4E6UuFoyhzEA2LxlI2Zm5hEo31JXmbwO2jYL+L2APYszhnjp0BlOVwP6FfU+1avrs7hQB4PcH2UitQZG4IWjF/APX/8cPvreA/j3fuphjA4Pxnbwi+mxkkgGeVq43R4jXDvEqUakPIPNBj7y7nvwvkf34WvPncCXv/MG5hZXB3sJg4ODOHBgbwXsKiBcuukL1PXixoBdGMAsyhRSXx5uCNwBJCbXHjTCjyM/bL+TUzCLTHgqIEm1SVJVxMgexfJUO7YiQ5AAXoG+AvDg+VleLkggnMDdxjsuA1ha7qoNQmISTFkweQ34lwE+Ewhs0A+QCwtk4uIQL51Mh0g74IwRnhweJ3TC5WvyUn5D5jrA534CvuHw2P27MDbMZzWU+ijGQeOkqcIsLS0XeObFE+gUgdffeZ0dYKt5ltrJOvGhvP9iCnjnMDLcQoOPEZe8kRTErXOyli/v7shhbnJGy5JjcVeR5gnA79c9uJ3hhw3knwLwKoBDKyUiYveq5HH8+SPY/sC9aDSacSAEBngHPhqRrbDJwfkIylvXtdVhiwXBOomPCLg8vciGX0KnonqIDAFjAYAnM0s1TsBdZ6KR7pnpaDYxNj6KqavTGRTKEMvXleITIcHEbqp6gb0Qa3synV2GyA+pkYmf2yjUSe9appHkV/xea0H1UnCI3gufmngdX3/+JP7uTzyEjz55LxreIfDhGFJ2HdBHULf+7yNIxDMHCCHq69AebOAn33sA73/nPnz12TfwV8+dwMLSamv2Dgfv3Ytmw1cAkauvu4RF4p7AXkLR1YC9UtaKwB4TUJY2T7hmcDcFS3RyaGMASiYGEtimEhLgkgO7HgXS4aMJVMv7z+uuFUylWqnOMBFO/hoQttcKziVAz9T4zjHDj0p5naVlPZmuDO4Vy3tpt3lX+87pQaIfOaNkQ9X4juQaApjxm6W98C6jYQnsI720dkCiZRc7p3cc3IH1Y20dF7bNVnKv8GYe6HYJEy+dwPziMkRTEtPy0o5jtT7P2QCxhOclOEKc8XzSnHfA6FAbhABQ4OnO5YrrZAP28EB3uYO5K7MK8F77Io3fGqD/y4mJw6/WdP5tDXes7/q6MDFxuC9OKYSA69eu4/xrx1l9Y4FJvLEhAyMiYN3IYAK5YNJS9dcpAq7OLkH80ResMuoWIV4TMreugdi3fCD15a7+5gP7eOc8Ut6GzesAuMyXfNnHfeaLXn3cBy071kNIfv9NWZTu7XNrFS9xXSItqxu4Hv5pf9kyVgH4Gw1k/s0uLOEPP/sc/vPfewrPvXbOSErJQEh9bjuov264sv97cbRivZnF6+HWAH76Rw/it//jH8NPvucetAYaPdu2c+cWrFs/mlSh/CNDFKXtEpnUoIaBk58waaS5NF6LsE+yelJhRIkBBFJ7st7MypR0iXmDbY/UaCoS5k8YgpwxSWBCJEQ+HUpEcMmRiUjycsiT1OPMFjd9E5dzI4JeJWYm4idpxgT8YGA2jEEZ1C3YO7u0YD3O8T2shzjx2R6BQzR11q+8+GwX47P8bxRGGuIL3tvnPvN6p37ra35Spy1b/OY3rOc8mQel9ulcssZy5udMuY/cux2b1g/DeY+Gy9usdTv+2bJdHBvffvlNTF+f17mXDBXzZRdAtKXmGxELT6b/xkaG4jeQb84Svm5tVuYvcSELk9dBrD2VPJ4tL60hYyn8bl3k7Q4/bJI8APwrAP8MwNa1JA4h4PVvPY9t99wFDDZ1cAJ8ShGIvSHFyUQOGB2KKqZoVR6pXQTCJAULoZueW4rnx8sgZLHAwcEVBN3Hqxwrg4uISSIBAIhcYlW6976BDRvHcenS1VwKK+HnqkZ4msYlIkxJRa/XEMZHCHxihJIGo7pGb6V3KS8LtfPi1oWzl2fwO3/0dbzzvh34j372ndi1ddxUypIhoNK7bO/Ro27ZDWJU3YmnxJTfw2G4PYCPvf9+fOhdd+FL3zmOb75wCp1OoW0YHhnCvr07qu++ipSexVOeiioXmfzcu1wzDjSK8gTlsskkLJfXr+Ru6/eGKMq6dvKIJtJGlCJV8nBAspBOjlIIyZd8KrMszVdd4FbTMOFHKZ3EObNG71I9vlyuvlNaSshV+DFdZ3lZgSct3+VLCMKcyus54VVcsi+AabPUoR2WBTMHjeSpzJpVefP7CitGJq+LQi/TB07jkHYX8fuGANy/fyu2bjQndzLtqzSP5F6k6VjOc6/EU+XiufCB80fGzBtVe2RaKBnhIWp6CgX7+CbNhsfw8GD06qnflvhdHZwTIAcfnQtQp8Ds1Cy89wboyXzXWmn+uxMTh7+OOzD80IH8xMThxSef/OQfAPjtteaZvT6Hc6/8ALve8UAcviTelSg6yQF7SwrA2NAAq9rjiFRgD1aVnRxGTM0sxfPjQXCeh2qRb6HxRioSYlPofVJBxgiZ2MnVLQCs37AOlyevoegEnSwyvpQc6+xFNkkpk+5EKktMgOz3j/f14C6MDgHZ9rqy9bxd3qgEIRq9PtQtYgKeP3oeL75+AR967C78nY88iM3rR0wDcqBnE90E7j72RdxxwUBvXOWK6n9suIWf/+Ah/Ni77sZfPXcCT790CsudAgcP7o3OjsqAVxNWBXVzU3lKPcq+GWDnxJTfZonLzMtq4G4TOu90LOr8kPPIGdwj0Cmyab54SepeVmBOQFaDQ8qrqaSshNROkxswT0UDTrzq5dvj6gAeBqCTRiAH/1AUCEVhJNEc3F2W10qrOcNg/+YMbF0w5TCjQCQAF/tENTYSF+KSlWqCnPnm7CBGhRzxOsdMwIG9m7Bz61g2rrRfSkEYDO10Ar79/dO4ODmjZ73rXwZx3VVElI2dWD8vwTo+xMpF9f3oSMuMgPgy2p+U+kAYO+cI80aK996hKJIL4OiPJTGNBuTvSCke+CEEeQ7/M4D/GsDQWjO8/txL2HrfPRhoD0JGVTQSIR4gUVIYY29IcTLwWcNGyrWqyxCAKzNRkvfOwRVCMAgFiDWDPnKfihO5W1sFvoQjeh9DmsCbNm/A+bOXs9iK+pXvhfO2YN7Tuh72L5ghqAf3+N7VtXfbP6uFnilKD24G80NB+Oqzb+Dr33sTP/7E3fjbP/YANo4PJWM7JCM7mL+RSACZYxUfpY3yljzAYd1oCz/3gfvxk++5B6+fv45TV7uYXez0frXqRS2op8s84saAPSbqCe49pXaTr4zZVNP+UjskSdznHRsvKvKomgdExWUZ2uw0Ov4uOl+Q/MgbpM4ZAwXgJNGX1e1wqRwFcWh2ftYL4E31wpyUAd6CNxw6y8toqLo3PRfnXXXAnvKbvnHpuXwY58qrrtVvAAakaKSWAC/+MYbE3qRTGpLAnsmDblMVpzd7d6zH3u3r8+9i6k5jV0DWpfY54MXXzuHUhasYaDo0uH1RnELS7jCBjK5qKT9xTiR+MxSazQaGh1og9lEPfg+hs1F657/g7YTLBeauXs++R2KyyNxn0vybAP417tDwQwnyExOHJ5988pOfAvCfrTXPwtwCzr7yOvY++iAA4pONSD3SRYmNMD4yaKTYOCAKQi69QkANuDK7GLdcuDgIHPGhNHDsvjYY951MAOTcRkDXwKxRTAL7fGKPrxvD5ctXsWitvEsEO1fT5wTaArukscCcQN+ANnPOle1za5XebyLcivK6RcAXvnUMX/3uG/iJ99yDX/jQA1g32oIAPbkkjYDtM/QoWzkEBahY6ot/btl3PzbaxvvesQGBCMfPX8dLJ67yiYQl8F3hBfsB9fjnJoCdM1B+W21JmenoA9zzl3AqSHvLREla7yBbzQiypZXzM6oKK0CIEiUAZc4UOBMvoHUoeEu0NF6lSZNXGfCqg5vYTDOPS6DOr1EBewoBRacLXXtGHbjb+gXk8rK1/ZlkbzgD/SqWWzDfxCWvmSLEkPRD3H7Ec581lQLsJMZsCewRWDAiYPumcdy9e5Oq/VXQkLQGSJP5ZWyrg8OrJy7i9dOTSVp2QPAuWtGbPfDiwMwxY+LYQ514vVPfJ7wMNz7aVkPAiMuiAZBeivO44HIJwDxb1Nv2p79pX33J0v53JyYOpzW7Oyz8UII8h/8BwK8CaK81w4nnX8auQwfQaLWiNTwY4H00eHPOYXx4wKiiEScCG66pxMojPgTgyvVFFAyoUasYR6kHVCcvVvyAGHYlKugLAXej8oNECWVOk3zzlk04dfJsjCabwQC1xoghVU7sM+nd3MNK5Hyfts4lxkb20lqV/Z0eOt2Ap55+HV/+zhv46HsP4Oc+eD/Ghlvxu7FaUNfpkaxpM8kfZUv95GCn3RqEc0ADDvfuGMOBHWM4e2UeL705hVOX53oAen7VE9T1WfXpzQJ7Xsbq4F7F73pwT2r5JPYqRDunB8ww18uMrtkepz5NTaU+90vveL5ZyRCUmAVd95a6XQJpBeMScyDmVwlQYbQAOcDb9XfNr+WnOdtZ7pjjUiV9D3BXOmB2EJSkfGmDXmc0IE8o31KlVWGsnAB57GdqiGCQgzkBUbXO1vTyHCzxb94wioN7N/P3cPzcfCNK7Uo9klT1b5ydxKtvXNQtr3GXkoeTtXDhvp2AudhjxGU06y5c2ubg0B5soD04EA2uAXOOCGsIBOxFMANAnS7mr83p95F+Lq/Np+9BcM6d3rhx7A9xB4cfWpCfmDh8/sknP/m/APhHa82zOL+I00dew753PqJqq+j8gLk6RxgfHlTPdoyP8N6h04mDRSV6BoOp66yuB1B4JiwFTx4ZVIEBwf0/5L1pkCTJdSb2eUTemVVZd9/HzHT3HDgGN4hDJMgVBRKkKFtKJjPJ9odky6XJaKKRS8kk00omGn9oSRl3TQdILqElbU1LiYe4iwWJiwBIAsQAg2swmHswMz093T19VnfXlVdkRoS7frg/9+cRkVlZ1Vd1j5dFRYaHX+Hh8b73nj9/rq3lAUf87Ny8leTdx21pJmMKmq0GavU6+r2+Iap8ft2x7YqVwUHcxRFYK/cxZ9Ty3LFFzrkM3PI6sM93r4dRnOIvn3gFX/7O6/jER07iP/zow2jUyh7Qo0CKp99aFkEO6K0UYyVUhUOLDRxcaGCtO8Rzb6zj9OVNpLTaoAiAeRgD6kAW2DPge4uA3cXtDtxtHgvSGmnsrou0SRMlJKkXbi6ci+BOA0YSlfNLT0k1SLp3RdXrPO7KgitLQUtfbZUWePNz55MAnuehupI4tlbiAJiV9hgmgpVFddh2K+TiXW8XfIE0HE3Jdk6egN8sJbMaSpLufe7A9q80rEIggHarjkeOL/vtyPIbjiRBTyuYL0gpvHl1E8+9dsVsGavTSZOX7GMCqYxUr2kPl6qFYL7qFfQUKQREoDDTqrv3Y9QJCsqtiVf6WfW8voQQAv1rHSuwuDHG+8rVS31XqYS/+bnP/dP8/NweCvcsyJvwWwB+EUBzu4QUzv7gRRx69BRKtZr9qO0ABzBbLztvaGytO6DnebkP+f4wRW+gd4pTGsH1Wl76MCydIgDQ5QgB64IRIM6YiI2yc7+W6HAKKxSW9y3i9dd6Nq+ngjPXWcDnz1gE7BShQP4AxoC74uBOwdSbozF7F/yjUYJPf/Vl/NW3TuNnPnIKP/ORU6hVS9sCPVEvT11PkoQFImUlJQhgvlXFj719H953chEvntvAS29uYBhntHtTg7pOXIDz2wK7X1YxuCs/kVdGVmMzEdyhySoHawJ9Wu9OceRf3khGmQHjvhMHGs7DHGD63M7tO8S3AG8iApbGAqxtnvNdb5dVcrCGA3gOskUMAP0GBFSagm+Uk3WE49T2WeZgXDwyP3wGxn9BMEwndRuNS1igJ4ndAr797nUu60mS3p3dcKaKRx/ch5C1w40oYb8dAnXu9Q8QuLrWxdMvX3Dvl16boTNBIBAovQKKmEFrn2GItgDsmn1FgC+AZrVq3ZLTmCKJXW/MQ2VJ0B4HKorR2+zZd6p3smTvwNOm2DRnf+EXPvGvizt/74R7YoOaceEf/sNP9P7wD784C+Cj0+ZJkxTlcglzB/e7SPP2yqUAH3x4xams2ZI5QM/v0rp5pRRWNwZ46c01KCJnBJIWbIUlotai3XwtdjoADmzpk3CMtGMo+FEqlVAqh9ja6nlr8XNgbN09wpVj1+zDK9vbjCYD7pSW7/2+o1D4CYjJt+9QiBOJl964hr/+7hkoBTx4aF47sRHCa2G+tZ55GIIwQDkM+W2X1vwuhwEOLjTwtqNzaFRK2OgN82CPYlB3//mPWwPsLi5Tj7kYP99eDO7UDrumGrAAZ1Xpwhm+cSKq50k1MfYt2Y0EBZFbhiaE04xl43IA7NVF7fLzcXW6QNbdrnueSQAvjLQLpZDEidtUJiCfDWz9uu0b057A3eebz9g17haIhHvGMUdgmRaugnZnv4844yDse+LPBAG0mzU89uA+9p1k+pX3HzvDnDe2+vjOC+eNoAP7fdgvSgAlbbhh9x3x7CdILyEY42nolwgE2q2GG+vEPCo7WgFjc0BDV0Ghe2UDo8HIaUdtsR6ba+8DQKNR++9/8zd/4Wn3pezNcK9L8gDw2wB+CcDMtBneeOZFHHjkJCqtpuNwoZ3gcKMypcCcuSgrgZMnvOtbEdIUEIG0BndatWUGJ5NYBPPCBijLwdtxLWAHvZVGTOBwoswH2Z5rIwhKOHfukgVzLzCC7zkzIWYBbgBbsBf+s2cl912DceHw58+0XQHjE+y0TeO+xM5ghD/+8nP43DdfwX/0o4/gJz/wEEqlYKKxnb/hDREdJ3FYUYMRL0D79H7s2BwePTKHN1Y7eO7MGlY3o1wrtwV14JYBe+72duBelI+1RdANgxC+xbxO4anmDfNrxDlL8rN7pwvvhTvLeVsnk5ptPi+TeR9KWD/61CYflBwTQNfE7BUCPDK/A8caEhiDMR6A79glq+r3JXn/2rWEEY/CoNzHZYYknflWslbaJbpAaaBV84otqWu3anj42LLTQCjzT9Ae8WbxqQKck1nX851epAFeOu1mAABszTuUWaompdtRDgKAtBoHYbSsNJdP3dCsV01TBFSq3Fij9fUSIF2pMEyYHCQYbA389ywA7mvBjSvLxJz+rd/6R39iHktAP8aeNL67pzzeFYUnn/zkDQD/+07yxKMEZ773A8bJ6TDXqFhQI2mWpNs01V6nyNtbmirc6Ax12pR7rQPzLme8w1Ga1HjEUwppKvVB0jZLnyjJPNtp6/7EeJiTlC+VaLQaOP6g3hKXPPClUiFJpfZGZ36nnpc7CakkpEyNRzzm0S6Vpp3mkNJTzauC45aEooK9QxUfhgzt5M9xOUUHsNUd4o++8Cx+5Z9/AV/5zutIUulJWZ6EyKSwLAgRachKQg5A9O8H983g537kKH72A0dwbKUJOz/Km8r729zMeqGD102sb1hZtqtNXgJlVzZnCJVXJqhEysteDXhbbHqSEF09tmeU1XG5eAPugJOiCLQtGTWxjmHIdDaIwRKZgt2LIC9npJK3eWzZrDksDcC9IBaAugV9J637jILwxk0YMG9u1rNc4HmpC7lEzyX5INB1ZD3eeQe8PJ4mIOPlLu8hL/DLMfnmZmt49IFlhKF7niAQRmMDqzkQQljvfIK1KRrG+M7zb9p15475cX1J/J6BZJA3vUDArkTK9j+g21guBajXynYo+QZy7j2BM3JKoHd1w44DT4PBGLKsdqLdbv7m29/+AK3L8UboXgv3gyQPaFe3vwxgbtoMF185g0OPnsLMvmXAcJQzzLI+65+eiFgoBGKp942/vjVAkko9CBXMYJXWi5e2/tRcpzQERltvC0d7lJ7v5RKJDsoOUABWuvcgVylUq2U8cOIIzpy5gGGkt0Z1RDkjcamMGkpprtZJ9n6aXCgYxjtIeuvDLeMy8mG9E+FfffYH+Muvv4K//7FH8aPvPmYNpoCMFE+SpJECYCQgQWpr4Yx4WHKd2xCj/fN17J87hPXeCC+cW8fpS1uIU2kTFT2q/54YaGYS28vdSu3EDXtl2TusDBdI0lJwXuNsH5hxbC3soeA5quFME/Wt2RpYz6mytenUYEaQmXtJXzq35QPeewMDBDFJzT+eWbPpA7P8j9VFYAg48OfTC968vK2XynBMCAc1vja++FszLBJplOxvMjh24xKgbVqd4Z0iH/CGaZubreGhI4tuK1jhXjixXextgmCaFFnDUYzvvHAeozjRz2++n8B4mdRr3wEEMC659bhRoKp0LcKkS6HgvivNOM40a3bsuPXwbgwpwzCSkbNSQNyLEA2G7D076d8yB8JpDoQQCEPxyu/8zq98ml4Je3RtvrPHwj0vyQPAk09+cgM73P1HSoXXvvWUWWKhB2y5FDKAV7DuWtk5DPX71JJ8pKXgVHp+4PVvymek6NT4fqd0qZPSUwWTh/zMS1aOKVMxP/SmfH0ohKUSHnrwKGr1uv5AlJHCmY/8xLTR+rc3fuipPXo7RVL7e/KdC9lbE0C2KOk2WfZkuLHZxx/8xffx3/4fX8ITz5yzRIUItQUHZYVFJmU44OASIMDSMWkPAphrVvCRR1fwn33sQXzk0RUszlQnSusTJXYQM+ekaZvEMnTFUjvAJX4/H5TTi5DkrvxaQdKQEuTURDEUEJaxEKZeRyozjTDRigymlB8P6juTVbB7FhxNA0UmDxFwqpfeCS/Mgir/vx3AGzSm+jyJNnDjxpuXD4QniQsBu8dCIIzk76WBk7KZtJ6bkyftAEnO5jdpErhdgPU/b67Jr/z8bB2nji2jFAbWrkA/u9MMFGm3hGljKiWeeukComFin8n1oV8WIFAu6SMQwjJMXn8Dxhe9sn1fq1ZQKZcYE0RMnbJxxFSRwASl0F3dtPW6cWFaVdCfALC01P6t/fvnlSs1B/Z7KtwvkjygVfa/AmBx2gw3Ll/D9dfPYvnEg1BC6a2MzCZpAAAgAElEQVQIyf86nFRrjdcAKAlUSiFGSYx+lGoAF3rABcI4wgkCz/uSm5sPAKVdpdIgc6NCGCkCjpoSKOSQUbFcOp8KBI4/eBjnz17CxkbHEmed3UlVlrijWGLPj9JsIjH59viCts2ygyJuayhq39X1Hn7/00/hz//mJfz0h07gY+95ANVKqGcgBVkjO1CwBRkpAwKg5UrCiBlG3gfMu9Bgpt9pJQzwyJE5PHx4Dte3Irz85gbOXNlCnMhCad1GZV6qylxkdQKTpHYvf1ab4CVzTAKgibwGZDKaMvPrJF4xSdv2mXKMgRVhXSc6Y23hy41WhGIklr4Ja9FNzBYDabDcnMmy4CNovt5X02ct7y11D1icqYCsvZ0amxgCJtlbFXSGYWC/wdrHn1lk+zAT7BtjDrq4RonGLUnClsaZSoQC5mbreODQvF8Xa4Qet4y78tgsIE0lvv/yBXT7Q7NzHMzSON1C2jkOZpozDIBqSUApqb8Ood2FB1IgNTvFucUUeiwEQYBWowpylmOXyJn3RAyooD4xWojhRg9JnDIJnvUP3KfL+7hUCl/81Kd+7bP02guOPRfuG5B/8slPdj784V/+X7BDif70t5/G/NHDKFUqCM3cNnfxmlXb0xx1KRBI0tQQRTOgDeUTUi/8QBAYy136iLUmRxIxA+M4oQdoCpH5mJW9EIVjyMlQAgJHjh9EcGEVq1dv6Nu0HDBH3otDURqxwxRjk41JupO23O1wY7OP/+evnsOnv/Yyfvy9D+DjP3ICc60aAjiGTWM7UVIUUAuQhtmqAK2zUU+9qtMszdbw0cf24YOnlnH68hZeubBpvOndbmC3d8fkVbk4OxqVZjypGyDg/JXTkA5IyhcWxJQQdicxj24S4FGn5J7YqbedJbb/xRCAEzNB1vrEBGRgzOZxMeyCf9cG4L218ExaJckYwtf+uHl6XajPCOSB3c0xG4YCQLVaQq1SQrVSQhAIJIm214lTiSRNkSQp4kS69yScNzsoIBSGtgmhmTACe6XQnmng+ME5ZHuG4BMghiFDmUzZUik8++plbHUjvf+ANOpv4r2YAZ0QQBgKVEIBQEKmjl5qZzXSME3Kqu9p6epMo2qcVzlaaN+dcu/daoyUAJIU3Rtd/xtkhnZ8FQiPP3hw6bcajSp/UpH5LYDMB3eXw30D8ib8DvS6+UemzdDr9nHx+Zdw9L2P24GSXV6moHJz9eVSYDhjPQg0PRR2DCkAQkptbmmIuOOADffKiJD1DEaMAjWQRXPpPktY3censLJ/CUEQ4uLFK1lsGXMxOWRHbD7rDhB92uF/t3nibdrZH8T4/DdexV996zQ+9I7D+KkPncSp49r3gnaRrAmHMsyb90I9sZ1J8XBSiJVYLZ+gDYseOTKHU4fbuLEV4ZU3N/H6lS2MkhxqbwvsOtk4cJ8ktZuc+SphNVO0r6dtuwN3BQ3uVAZJ/Qp67lDoSHvteo2QgfWjJbGctro5WN46Pk3C03ngzsDeSu+gb5OBMMgmwE3BeJoEBvBQeWC3GgEWz+uzWgW4cgQEZltVrMy3MNusGmAPPQl7XEhSiY1OhI3OABudATr9ofVep4yM4gQB4+imWcfRA3OsL/kLN3HMcj+bTEHhhdNXsbbVtyskrARvNrYJAkBJB6DVkskpBYRxZ5uq7Bp5x1wI6GWp1WoZSkpIRevhqe+F3VnOvF7NWEChf6PjTb3RgwjD9Gip3owdk6ZUCp/41Kf+8ZfZE2cBHtiDVvb3Fcg/+eQn4w9/+Jd/DcAXdpLvjWdewr6TD2p1fUZq99X17jqVCkvtGi5c71mCVwT2AKwBlpM3tKW95YRhJHg4msVHD49wYA5LCLJBCGBpZR6lSgnnz160ywC9z3R75B4bpof07RB9QqV7ihfOBte4NE3xjWfO4RvPnMPP/MS78LF3H8WxlRlkl/gIxdxpekUZ0YYDPhM7aJ0wN6AMACzO1PChx2p436llvHFFS/ermwO/6B0Bu73r3VSZlDlwV5kUQjBRSlmGVIE8vTnJTafxgddhNReIeKcRuKsMyPhOcyyomw/Ks4+A0xywUl1RPJ4kO85AcBKfla5FRiI3R1Y9T0acxA/ZpXUM3AMBzM3UsbLQxPJ8E9Xy7sh1KQywNNfA0lwDgAb9zW6E1bUurtzoWtevCrpL2606juxru3eReQecz7KUhTuBgsJLr1/FtfWOR89EBoTp+xChQtn0D9EqDcVuZ0jtRVRrumhTmkDpveLtVjbM4Q1J//adulSQUYzBVt8xUCKvICq4l77//Q//Ont8kTmQ+b1nwn0F8gDw5JOf/OKHP/zLXwTw09PmSeIEZ773DIIff5txgqOJnSfRG+JEHqDiRKJeLaHdKGOjN2KcnyYIyqC8/U6IfjmOwFzTJX0uwkk8IDrmpPusZX4R4FOZ7fYMHjpxFG+cuYAk8ZnLHMbfBOiPK3P7onaC5Lf727l5ruKlc2u4vJVgZa6OD5xaxqNH2lY60+uBjRWzIDe4cC/Z8xCGDHFlFCgj5ZdLAicPtXHiYBtrnQg/vLCJM5e3MEqcke/ugd2lzqnks+DOpCUmwmtwF+77oekr9zzIvFoCOndD8w1MG0bq4cAmd1mJyeDOU0SmogJiTpI6tcvpA/Le6twcvYA/HVekcocH8J7TG6Y50MvDhE1/ZF8bx/bPoVIOcatDKQyw2G5gsd3Ag4cXceHqBi5c3USSSrRbNRxeadu0431YGDBmxI0mrF56/SpW17u6v0hqB4BAAdLYKZghXQrdlIEkptiAupXaTVrN1wlj6yHQaFQQhnozKSHcmnrrVkwIRyOtml+hd70Dxk3a8UE03Kns6b0CrVbt//3VX/37r2I8sGfj9oyYct+BvAm/BuAnsYPnu3z6HJJeD6pdc1K7NL6aDZvL5+vjVMcvtuvoRgmSlBwsOC93FuxBgw0MoJWVfDjYc+meAJ+y7QbwG80GTpw8jjOvn8doNN7F8ragz9q+03AT8vwOSrn9YbsWJHEMQGF1o4/PfvccvvZ8Ge87uYx3PbCAcimw70UpxSR8OAmfJFgrVjBJ1nGQjBBxCQpYmKnhQ49U8b6TSzhzpYNXL2zg2iabu7c/dg7sLqtiKdwPJ9EZQsyYElIBA7DOnBzmuvlzRf/JUIy4IEVMsE7nJHLXPW6uX5npADeqBPsPCN+vvRIWkCmOS3HW0hp+W3mLrZqfu80FATmY8R1Xz7M5fOHm8JfaTZw8uohGrYw7EarlEA8dXsTxA/O4sdVHAOH2V+DPiOx7Nz3G51SkwotnruLaelePcwbmmu8i+wsD7oEAIO1Olp5RXSAgpIIy6nVhUJ7m4ytlgXq17PuzB7w6uVkHaRFGnQFG0cgDcAJ482CWCaceCAKx8fM//5H/bTgcBgBkvV4fB+xgcXefYJlwXyyhy4Ynn/zkDwH87k7yKKWwdfWadQBjN6lRxGX6TnISrW9HIASW5+r2jY51FmL+YBgGc9L3GPNgoux8EP+4lLs0d2iJk7Oep2yKlVuplXHy4eOoN6besM+WxY/iyJsPRcXu9NgLdcRx4t6dAjr9GF999hJ+7/Mv46vPXcbWIGZzrLAEzxIYOllJwkkUsPnyxlee5AGgHAY4dbCNn/nAUfzcjxzDw4fnUC0FsKsqWKfQuOG95MYfjTs2zqgv7DimcU1MiIFDKsSSPuEKND+EKwakmeBJCxmSTODgTn1k+5L6mf/OpVOWo3Xz9oKXbsCbQCGvjqc1+/q3P/euGRQH8LSEztcGCLTqFbzr1AE8fmr/HQN4HsIwwMp8C4tzTTTrZc/BjR2jrL3+0j39nC+/sYobZhc3u1SQLQMkI8VKWaBaEdBecZnrXfa+PBTNXAeBQqtRY+/UDDP+vj3GzIREonu9AyDzbWXO2d8rK7P/5wc+cGJrOByKKIqCfr+f2/+o4Ngz4X6V5AHgNwD8A+xgSZ2SKXrrm6jPtfOW9SBip9X4caJAyy1atRJmamV0Ii0pC5MWQEayh90wokiVbw2ediDdu0vl6mBijmFUEYYhHjpxFOfOXkRnqzd1J3r9UxBXKPHbiu9c2Atsc5ykDMNci6JRgu++soqnXr2GR47M4QMPL2O5XTPvWDgplVngkXRChnh2TlHoPIFgGxmxdJSR8s+3qvjgw8t4/6llXFrr4eyVDs6tdpk6X2Wb62LHqOQV/AtKFdAzAIAQVmqnMQg29mncBqDvS5hNQUy80PDu1OYc+fngUiDpGV46+j4MO2WzCditm4lpUhp8LPgzpkCnY+VnrP4F+2d/e0wAMWZ8/h1WPR8EwNF9czhxZIFJk3cvCAG0GlVUyiVtoMfdepvBSTH0XpUB+LXNvtOKKM0cWfBVwkrv5OHTmaMYFbygUc77T4KWngbG42i9WkEpDMyW32QESv1PhqxuypToZ+/aljX0s050zJNwiZ6r7Mvl4PQv/uJP/lkcx0JKGSilVL/fV/1+P6zVami1WuT1DpmzJe93O9yXkjwAfOtbv7OhlPr1neQJwxDd9U2MBkOrmrfSu3ExS/GknieBZWW+jpJx0cQlvyLJ3pfu4UlURFzHSfe+ExReHlwdKJbwRRDg+AOH0Zpp5DPuMqgxx02JxPdKyDxXPIptvxY9dqoUXjy/jn/15VfxZ18/g7NXO770wtXGVpXopJ2c1MHSWTDh95kUFgjg0GITH35sP/7TH30QH3vnARxbaSEMgm2ldhomdtRZ5tcf5477FIawUz5WAODmeRUzNDQNIOlLPx+nlW7g0LNSH1AOPy+7kZHeSTK3fckZYlYrSe+2TqrX/OYSfdYa30mo7D6cdEsAf+LwIk4eXdwTAM9DpRxiYbaOWqWUk+i50x4A+OHZa9joDJjUziR4w+BUywLVkt8PzueA6zvXz/S+hGUWBLTDska1AqfR4uOfMYCsO4UAkt4QUW9or7Pfl4sTNg4ATpw48L/W62XZ7/eDXq8XbG1tidFoFERRFPR6PdHpdMZJ9Xsm3HeSvKBJNEBcvPi9f3n48Ad+CcBj0+QNwwBpKrG5eh1zB/fDOHo0BniwoKukMi5H3aAKhMCBhQbevNYFiefc+JdbIRfP2wNZ6R6Am7vXGaeX8IHcHD7dPXL0IF794RtIk8QX0Wzmmx+jk7B8rPRfmPhmW7LDsIO2ZZMmXJJnCYoUzmevdnD2agdLszW8+6ElPHpkDpVSAKv75AIrXQrhCoWfzk7jZ+9ZDYCwAycQAkeXWzi81ESSKpxf1W25tNZHqlyd7uQusk+Sta63c6KA2aTJQa9gz6YgrErYtZ0ems3PA1aGJKLuHJbwpXYur26X7yDH9gwBg20PZ6z878QSEh4rmNtazhBQWgJ4Xo7xusYBPgyAR46v4ODy1Ptq3fEghMBMs4ryKERvMDKxbsBJpfDK2VVsdszmLuaWAOz8einU69/19CfsWnll0ri5dwa+Svnz+Uzb1WpWoeV9yeia0QCwMU6xQiioRKJzbcuWn93Mi49B/h01GpWv/fzPv/e7g8EgCIJAmSNQSiFNU5mmaSB1CNrtNvdjDzju9K6He3qr2WzgAA9AbG1dxMGDj78WBKV/ME3+v/f33gOltLW9EAJhrerU9iApR587gxiXbvR53SiFAYQA+sNEx7H/NPgz7fWv2T+PXiOTV4hcWSbaz5QrV4cgCFCrlrGx0cmnyxU68e5tD3ej1t1+mUIAS8sL48stKLg/TPD65S18//Q1bPRGaNbKmGlUqEQQ7+bUxVnUsv8YkObTadB1ZmMk7wYCmGtVcXzfDB4+PIeZWgWpVOhGsWVOcqr8TATBayDMDmSGmJLHOA18Ll6AlpJx6c3N7zrnMoKVpWkmgSWvg1u+i8CfIx83j+5J4Myy3c2xO5ezJHV7Ervw3bdya3kuvcs0dXPOpp5SKPCOE/uxf7E1dqzspVAKtWvcJE3tO5MKePXcdWx2I0fbOK0SQBjo1R9kfGlXlHBu0aqK4Ascigye3b1mo4JSGFpBiwJ3XuZvlKTr6d/oYNgf2bTZkNvTQ5cVf/jDJ/+b/ftnNyhemrkLKaU0QK9MPNI0VfV6naucfPXTXQz3DchnAZ6OixefPnv48PseE0I8Oil/GAb4sR973Pp7j3p9VFsNIAjZXLwD+63+CFfWBjmaW6+UEI2k9TLlwN1Ha55tPNjrH9sBfi7ORfsZ2a9qrYokTjAY8C1O82EvAv9eDc1mHfML7UIw58FK9iydlAqrGwM8+8YaXr24aefTS2HgjQcCclJz62sn7bprB+ZWc0ShaGwYUFuY0YB/8mAbjVoJcaIs01q8vazN7paTaQS2cQA8YKWWCnIIZeIROCbAgnPAy+dqVQLnDHCz6yJGIFcOlRE4pzc5JiAH5o4J4E5xgoznuyAQUGniOb4JAuDxUwewNNecPEj2WCiF+kXQ3hivnb+OrZ6hHYTZbL1dORR6+jI71pXDQMW3Yc6As8oAfrVSQqXMDRIdA6rTa42Vt1xUKSRRjM61LXOp7DkL6tn7c3ONP/74x9/2FXOtNK47YE/TVHKAl1IijmPVaDQ4uO+JzWruC3X9OICnY2vr0q+124d/AhN2qSuVQvPC9CYtaSqxcXkVc4cPMi7RcHRKO5RQdqA6YgYA++frOLeaIElpyQdgLXhJhcoAe5wq36TcVp3PkiOv0td3CfQVQ4v9B1fQ6/YxHI4wLhThlVO3s7tjmIq3UlhaWfT63VPTF3RkLsq8+GsbA3zlBxfw1ecu4eHDbTz+wCIOLDRAhklQxBiSsxkF4zVUv2dyFwrY+44vE2ZwmJFEDIFRfZKP81olxMOH5nDyYBv9KMG51Q7OrXZwozP02krPIKigQIAGvdmWxhrUkRRHG6hZFSmgJX32Uejxqr+vIDuc7PcAxs3SM2UY2pyIScmEa49lPFic/e23yRbhlUX3hCfFu7QO4I/tn8Niu4F7MdSrZUip8Pzpa+gNhmYZKO0kB8hA77KpN/HSNkykMhc0LIRgS+mUHavCsqT29YCM5kqlEI1aFVLRroxgqZVhzCSgjDMckBSv0L1G1vTInbmKnt8rlcKLP/dzj/+hlDIQQiillBR62z8phAjSNJVBEARSSpUkiSiVSkEcxzKO4yCOYzkajYJjx47tCYAH7gNJfjuAByCuXfth/8CBx68HQelnx5WjlMJHP/oOY2CnpfnRcISwVEJYrYGceRDIR6MUV9b6TMISFnwDIVAth+j0RnadvP3v0Ruf+ORo2c1I+LqAQrh1A12g0WhgY31zXLdMHSbC+n3NAGgKUatVceDgyjapiiLGLBFTJN1HeP7sGl65uAmlFOZnnHTvmD0GSFnVUuEgya5+FvnBqVx0uRRgcbaOEwdncWxlBtVSgGiUIopTJskLo2Zn1yRNM/Bz15rqW/U7y+NU875ETmeuWs+fM1I+REbih61DEDhTPvJGx9MFPL2OD1l5nlQv/DghAJm4nddmm1W87aF9ue/6XgqVcoj1Th+9PvO5YcZAILR6Xr8+JnxwoSCrFrfRWgBWLB3xcK16FTThz3dFtPmMlpWXrRQQbfQRGW0Dl9JtazJxdH7kkf3/84kTy+eNESqp5D0VPf+dJImSUmI0Gqnr16+LXq+ntra2sLy8vCeA/p4G+WkAno5Ll55+/vDh931ECHG8qCylgPe85yRCszSDpPqo20NttsXU9nrpUpJKXLjey9FQIqDlkhZX+sMkJ1EUg7PIXHvP6V8X/MjHZbFV5OMAlMz2jL1uH7crTEXS8hzKrW/IjoKaeJkN+w+uoFbkhyCj9RhbjBp3T8cOhgnOXOng6dPXsdYZoV4pYbZp5u4ZKNvgjRmntqf0gknBRQwnz8Ml7ko5xHK7jhOH2jiy3EKrXoaEwjBO7RSCD9pUjfCvRXZu3c3P0z2RS6MTZNX2dn7f/jaMREB5/e1ds4yBTQe2LSwM0+IxBpyRcNdhAbiTKl/KFIHQ9jrvevjArl3T7qWwNNfElRsdvcLIjKNAaCM7AGws+oDsA79yaQFPiCJVvVJAs1ZBqRQyQBcM2HU5/Msib6XpUKvp/TJdKAJ4pYCZmdoXf+qnHvs3BOwAQKp6fs0AXiqlsLW1hSiKEEWRiuMY6+vr6oknnlDvfve7t6Ectz/csyNuJwBPx9bWpV9ptw9/G0ChV5itrT7K5ZJV1+uXmOL6+YtYeuCYHadSKZTDAI7zFKT5BOmylQLmZ2oYjBL0ImfFLgAr3fNlw4ByqnxTJJ0K1fk6CyhHVqVvb1M5Y9T6gFYzdzo99HoDsCy3LGw3ykVhorv+beTCuBaVSiXMzbc9sWUSmE+8z+4Uze0nqcJL59fx0rk1LMxU8c4HFvHokTnUKiWmExdOF2nGaABDFI1+kqyMtdqT+9QnjoG74YUFcCpXKYV2o4KZehknD7WRSoVrm31cXRvg6mYfw1FqpgFMwYFuiVXrm1NgvgMF8lTneoHGfmAbR9NVzE89DBMSOE6E1rz7eV3JpOoNsnUIYeuAIDU+LANBnIBPWBTLA8eggBgIbYx48sgimrUK7ocQBgFOHFnCi6evGDsKpceKNJPQ1j+9UcXTyxSkqqdhaKaVzEB3mhUA0FNGlUoJBlvdWIWux9JS5e5p9Ym0anpebtH3ZKe9hECpJNZ/9Ecf+pRSSig9DxsYYA+UUtJcKyFEIKWUQodgMBgoIUSQJIkUQgSj0UgqpYJLly4JYfT9t+dNTBfuyXXyuwF4AOKllz5zNkmGvzmu3E6nbyV4d1YYdHrYvLKqJXwjzZMhCmNTLRPgNEcK++YaBWuRYThQxa4pH/MgxsqiUcI92VHg9bpyi8pmZbFylFI4dOQAwsAtRsoetzMU1bcXj3FhYWnOgFbeI5zt+4J3yRO5d8HeJU/F37uRXm50hvjqc5fwqS+8jC987zwuXNdOjujrsAAFJmEKx8RxdTXAzozD5HmJOHt1GEArBwEOzDfx7hNL+Ph7juLHHz+Mtx1dwFK7BmHmaAU9G9WjzHPBMRLWD55ya+j9/uJsiImjCGIelON16HmUu8yVwRQcrA7WiRTH+87889T+MASVXZfCEHOtOg6tzOJ+CvsXZ9CeqRkDu8DrA+qALINkxxKLtyMuMy7DQKBRc6tM3DviTJUbD/wd9W90kSapK9eOb0w8Hzs2/3vLy62elFIopYI0TYWU0p6TJBFSSmHOQRzHotvtBmmaBsPhEEmSBP1+X4xGo2B9fV1UKhVjl3l352fuOUl+twBPxwsv/PnvPf74f/4fCxG8M1u2Bnk3J8/V9muXriJs1FFpNqGkVtmXwxCjhM9LWrKh6RS02u7AQh0XrnWtkR6pPumf5UhNPkuAssZ6JgmdshI+KxI2h2AEcYyUDwDlcgn7Dizj0oUrVKDXN0Ugd1dH7h4JQggsLM4XSgku+DcnpVVjEqkxFwpAoiRefnMdL7+5jvlmFW8/voBHj86jUdXLjXIb4wgaYQ4RSdKnsSnsPRKB+N7v+ixNXmEYHBqnAgoz9TJmG2U8dHAGSQLc6AxwfWuIta0Ig1Giy2P7viul3ZXagSlc8wILtA5i3Wg3DSbuRTmNgFDC/15YTorzuAE4QgFWJ6fRzh6gaPwzZsiUEZYCHDvQzqW8H8LDx1bwzA/fNIKPAWKS4gHTScruG+9phEw6aZPx8QUN8Oal2T3nAbcWnu4BYPpUDLsRhsbpjW6CG/NFZwrNZuU7H/nIg98ggGeSuwQgjHQfKKWkUspK+HEcK6WUSJJEKKVEFEUiTVNhAD+o1WoiiiKqbyKVuF3hngL5mwV4AGIw2JCdzpVfmZ09+BVknr/TGXjW9XSWUv9eff089j9yEghDQCmUSwLD2BAq4egygSuRgVqlhIWZGq53IqJD9h4HfAfKRKR0eVad7y4mAr7pKw/wqV3erlIZ0C9X2RKVYt2WdzluxL6VwH9uflbPGRb0xnaf9DhA9+5lLlRBBE+71h3i6y9cxtdfuIyjyy2cOtzGiQNtVMqBwawMeDOiJ6AJsrTjFz6O5pgFord66sqOZU3DLaiWQ2D/fAP75hqAAHqDGDe2ItzYirDVj20lfO7fMhkBiPSb+6YHlA++UP53qAHBqeYd8DJmwVXm7oOr8vmkgAEju4sKYzmYNE9sBIFVs1q555bLTRtmmlUsL8xg9caW7cpACKR2e2RHBgn49Zhxgo1ToztJvFapIAgFZGopoaNdkqWFcFOfUEjiFP31rilHWOaAs3h6nPrnMAz673//kd+TUgoAwpwDsy7eAjo/E9CHYSgGg4FQSgWj0UhJKYPBYCCHw6HY2toSzWZTRFFk23Q3gP6eAflbAfB0vPjip194//v/0e+XStX/mtfR7Q6YFF9wjoe49sZ5LD14HApOZU+GIJbI6EgQqykAzM/W0B8mes2xIFKC3Uv3Nm1ewtflbiflm5YzOinTbcbfuPE5Jfjb5Nvcv1eCArCwNJ9jsIrS6R/F6XKx24B6YR7kGb1zqx2cXe3gb4KLOLY8g1OH23hw/yxKoQ+oHniD5s0J/QzkKgUrGFslgPahT6DoBCwBwcGUEXAFoFUro1Ur49i+FpJEYaM3xFpniI3eCKM4hfNfLlh2LaHbZnmDiDQNBLGgVhhJMJOUVgEwqd+Cu/AhgdrNlR+C1Z9V4bOpfQgAB5fuDYc3uw3HDy3iyvUtEtotH2npjGDD3s7JOwt8w76BNETlUgnlsl7ObF8/H9eCaC0NC0XT8Ojf6Bjf9EzgEtlzXqI/cKD1R/v3z6wbKZ7m44VSigO+BBCkaUpnBSBIkkSGYSiSJEGpVEKapqhUKmJ9fV2EYRiEej2ha/5dAPp7AuRvJcDTcfr0V3774Yc/8dNCBA9RPb1elFPVZ8+dG+sotVqYWV5AJQy8wZQFewBO0hIK+xfqOLfaRZpyV7WO66VM0wO+TVSo0qefY6V8lx2A9s61qzAl+BdUuW240wzBTtrWajVQq1Xz+SZ8w9sDenHkNKCeb4c+panEmSubeP3KJkphgOMrGvCP7xZH7j0AACAASURBVJtx1uvZzBliSOvwLQVnrkY94IdAAKO+pUaQalRQareGv1wSWGrXsdxuQEFilCh0+iN0BjE6/REGo5SVAY/wC+GkfP+h9WdOzbXPAWOt70R1hubkcwBs+R0ff6SKd1wArffn4M7zlcMAK3P35pr4aUOtUkKzUUOvH4G0OcJuFUzjRP/2ttvmdAy670IRoF4tgxuC6qXxPCW/cuMz2ugjMdNAth1ZjRNnOKj9tdJLH/rQsS9npHjvzMBepGkqlFIiTVOQCt9Y2Aej0UhKKYMoimS9XhfdbhcLCwtidXX1jkvvPOx5kL8dAA9ArK+fHW5uXvilubmjnwNQBoBeb6ABmKnrOcBT3PUz51Bu1FEKA5C1py+RF4N9GATYP9/Ahetdejo38AFPFemef3vAh43zAd/GEBdLaQtAH9Drsj06X/hGdhC2Y1insEe5q1/HuGCea3GMFF/YZjXucjpA19VuownIIHwR4MeJxGuXNvHapQ2UwwAP7J/FqUNtHF2Zsepm4ky5gx1rh+9pAMx9GucCEBJgU+GwYMtilCAFv74T0C8lUC0HqMzWsDSrF8CkSqE3SNCLYvSiGP0ohlTa1oVaC6d/8CRwB8YurSLpXZlxzzkU0JBkpQlS+DsGhZ6XjMjcPVevEMCBxZZt5/0cVhZaON2NfKmZqeQ5neKMkz2bNPV6RY+tFIbhcpyASyug2PtQkIgHQ0SdgaeGd9+KiyPwpzrDUPTf977Dv2PSZtXxWaC3anrj7S5IkkQppYTZoU6MRiORJAmGw6EYDoei3+8Lhl823Glpfk+D/O0CeDpefvkvn3/ve//Lf1apNP8HAOj1hh6o5+fmHehfeuUMgoNHPEl9HNibp9EGJVU9P7+2FQFC+eTDjkvnoYyXB0wJ+KYMc8cifZGUr+vQaVOZelSyaBTeUpJ1M+N8CgbhttUNoFKtojnTLATS4qhilJ/Uim1B3btQRZf5NrAhOUokXrmwgVcubKBSCvHQgVmcPNTG4aWmBipDRu1uIQaQpXKLmayExgk8h1s3OW/bQrYmlAq2eF2qw0WBkhCYa1bQbjp7kWiUojdMMIgS9Iex2SzKlek6V+g5fcXd68JyKArKbDbDuGBrmOiA26YQ9gHNd2UYBsYYCFbUYruOt0JYWWjh9PnrSFPtr14TWPfuNZ2ieXXhGADAStf1WgVhEFg1fRbg7ZK5zOCXiUR/rcekdQJ4O7rGhiNH5v5weblxA2yePWtYR4CfpqmAUdMzoCeDO0+aV0oFURTJUqlE+qIcsbqTQL9nQf52Azwdzz77J//ive/9Lz4WBKUP9fvRhDl5Py7pdNF99XWo+f26vWPAXj8LADgjkMWZGiJaPw/AN4jzpftx6nxdovvnAb6J50R0OykfgNs7egK6jxuVtxT8pwl3x1DVhsWlOQ8wdRiP8tu1dtL3Pp6RKJbWc23KtZPqdGmGcYIXz6/hxXNrqFdKOHFwFicOtbF/vuE09ACsgR6MgZ4SGX7Ln493Uryl9hBZ5pbAkelUfeAFA16gXi2hUS0BZlVakir0hzEGoxSDYYLY7AZoJUprvGeqMAYEpMK3VJjusV5k7Iyt332qfhxX15dKAZo17mv9/g31ahmtRhVbvQiJBEJuSQ8YlM4SJGXHVbXsz8NzFb0GeH+e3RYJoL/W9TarKQpF8/CtVuWbjz++/9uT1PNGk8uvrZqerOlpWV0cxyJNUxHHsRiNRmI4HIooioTQ7nALG3ingH7PgvwOw65BPkkidfnys//40KH3fClJ0nYUxSiVAqO2zy+l49J9vHkDl6/1ceDkg06SsQSVjXCSXBjYHlho4MK1HqI4ZWC9c8CncrOAD/hSPuXgUr7/U0CmE7wwbiPWTxqpd5wBuM0hDEPMzs06iJ0SyCnsFtAn3c9K6+Paw4Hd/s+U0x/FeO7sDTz3xg00amUN+Afb2DdX92i1Mzoz/5lEDwJHywEwpztM4rLX0AU6i+ksk8pYAgG35Eroef12qYJ2ywG5drubIE4lklgi5vYmXjtd4FI4/yG8pzRsCzEQWcsu6Gebbdwfjm+mDSsLLXT6EZQEEiUQCtdP2hiOmDeA3qFS2jNotVLWaZBX0esldsIDcsobbfSRDGMv3j8XW9iXSuL6Bz5w6I+yDm+41XzBmebjrTRvpHcrzcdxTMvqZBAEwWg0kq1Wy+MLs+FOAP2eBPldSPE3Fc6f/9alublj/6TZXPrdXm+AVqsxRl3vq/IbjSoufe0HmFmYQ3Nx3vGeRPwMBSWCYlXv0AZAh5aaOL/aQZwo+xREdOlj2A7webm671gaDvo2XrDyfUmfPEtNHaZE9mlG8F5nBPgzzC20zcc5If0U320uxW5B3Zwmg3qmzFxZfgRd9aIYz565gWdfv4FWvYTj+2ZwZGUGBxeaxgWscWbj1SrYmQ02kFDjLNkVjIW+t6mOySZgNQXkZsh+QUKDr2M4mMMbATRqIRq1kmuJ0K6ok1QhTlIkUiFJJFIpYeV8WlIIrcbnHZJzFMSkfgtc9EsptJvVgrdx/4a5mbp9Z5IBvbJcofLfldKrk6qVMrRDJCA7gj06ZplDBSWBeDDCsBvl5uEnGdkBgBCQp04t/l+1WikiaZ0s6rNSe9FZSinSNIU5W+c4Zm28iOPYHqPRSMCYrkzqu9sN9HsO5O+Umj57PPfcn37+/e//hR/f2Oj+J41GbfJSOivdK8zPtfDq176Bd/zsx1GqVgCQFakVc/xd2wxho/mjQ0stvHmtq/1AwxCRDFALUwjjZScAPmAlqwzjQFeGdtqy6J6UcjuBffqw3ZDNFHrbdVY3EQQEarUK6o06Gs06WjPNqUCcwmQwdxHbAzq7GgPqQF5at7+mBHY/zsV2BjGee2MNz72xhjAQOLTYwNF9szi63MJso6wBMjPfqstx7kgBMILtmIEMW5A3lHJitUtjmkZpaN7drNpnmgYdyqUQ5RLQrJZcPqGnqZJUIjVn+s7z4576IrtEULePYoQAZt9iIF+rlnJAawcBmyMnDUgYCFSrZUApH5Tp5TqRhI1BPUhlmmKw3s02YWzgEv7CQuPzx461z9DSuOwcvDJOcBRzhsON7aSUksCdn5MksQcH+rvt7Q7YYyB/twCejtOn//o33v3uRz66f//Cfmddnwd4bnk/N9fClStreP0b38apH//39FpeADQgufqRop2zHL1P86HFJs5f6xqO1lHb/Lp7JzWMlfABTFLr26IyoK+JXTHDOQ5Mbmr07hTVb/enwtoThgFqjZoG9UYNtXoNQTDZA3Th40wD5gURuwf1TB2F5e4M2IvKV9Dz4OdWu3jzeg/fCrRx3NHlGRxeamLfQgOhp6bXmivdJjKuKwB+Yk4tE2zaI/KvP/DyuCV53PsZz2ONCM03xZmKUiBQCkOQ4RwVq6RCqhxDr/uCqZ4ZeJC4KQQQCnHf+KmfNlQrBkqEXgtfCvRWs0WjNgwCk175U/Xg3IGJ5wwCBCS0oR03sssb2/kMom1jtXTmXe9a+SJJ7jTnDl3PNNI8mAGeDIJASCkRhiHMOnkRxzEqlYqIogiNRkOUSiV6lInhdkrzewrktwm3HeTX18/2t7Yu/49SHv+XUqogq67nantiANpt7c1q7c1LuPz8SzjwzsdAHzsAC9pEvrKgDSiUywEOLTZw4XrPEtSsdX6RhK/jM4Bv7nEpnzpvO9DXhGx6Wf62gP+4cBtF/Uq1jHqjjroB9mq1kqtyOhD3b0yTZxygTyq+CNTtr0nAPo5RmBLY85oAoskKG70RtvpreOn8GiqlEAcWGzi81MTBxZaTnPk/AnFD2En+Z1gP+o5YMp9wO8ymkQ9yqmJLICBmICx4vMEFuyzO1YwgFCjZ9PqONaZnDcnS5ka1dNOLP+61EAiBSjnEKI4RhkAqed/qYC3pzVp4hudMVi8YZHbg6vXwabxzfx5BgOixx5b+byEgs2p6AvGdCt0kwWeleS7JG6ZgqoJvF9DvGZCfQoqfqpibPT796U8+8fGPf+xr1671fmKyul4DfaVSRq1WQRSNcP6ZF9BcWsDswf05tbkv3cOnVko7ldi/0MCVGz2QOt/1TTHgm6xjAN+kzALGGNU+MF6S3wnwj0s9Xc47H/YdWtE7yAGO4Ex8AFXwa2ySCVHTAXq+PTsAdfNzd8CuE40Dd5tfaGgO6KYA4jTFhWtdXLzeQyBWMdes4uBSEwcXmlhq16wDHvowrHU9ACeFMalMOTndyePKLMuiPGAg7Za8Ca9Mzmi4wNe7wzQra4Tnf1acWRCGsdBxgRBovEWs6rOhWStDpjGUdIBuVyeY19moV/RrVY45ojFWDPAuIu6PMOqPdtQmkvIPH57988XF+nWS4IUQ3hmAoe2eVb13LjrSNM2p680B+o3tSd5tFGH2CMjfQjX91FWOO0ajEVZWwj/tdErHut3hQ5OX0unfs7MNRNEISim8/sS38bZP/Psot1rIqs19ydk3IgK0u8/luQaurvc9ArU7wNcX3tw7UKjap7tSyvxwG9uru5fjdzqibxdT0JxpYG5u1qLbVO0ak2h83unBHNgG0AsKyKWYVM8tAPZ8CboMBVjDOa2Sd4C60R+h8+YIr17YQLkUYN98AwfnG9i30ECjUrJjnexYuIGqLpcjLvm4Y2vXWXDDnTux8ZfLIXdWJn0e3MHjBLEPyk8h4BgIKLub41stNGoldHpA0egTUKhUywiE2diIEyoFuDX1fF7fSERKQSUpBpv9XbWr3a4+efLk/FO0TI6keK6qR8E8PLe656CePfODG+ExkN8usGF766X5vT4abxX474gZOHPm9YuPP37wz4JAbKQpLZ9Tdj7eAb0+Zmac68phNMTrX38SMh4ZbtVJV0q5rUR1DIzayt1rNytYnK25vDRPT6UoZMrwt4yFVzZLa364P8YUmDTk8S6HM0XH2DAuw7YZd13ibo4gDLDvwD6/P8ccPE3+NutN6mPlv1Oezz5Ttlx6f7zUgrbl6rV1FtTDynXjKD+GXH1OhcrHD1h+lWu/315v/Nny3LhKEoVL17t4+vVr+KunzuNvn72I58/ewMUbPePCVn+gZo/OnNrbd0zD4imt8G8FDn2RdT6nJXZ/SoDqoPu5a8oDds60MOOu/C0TgkBk+sKN1mqlzJgfxf77v4pohEoV+uv93LQI4BjA7JlCtRqef8c7lj/D5+Gzc/LmoN/e/DwdjCnwrOy3C8qpS6c+brWx3l2X5LeR4qcq4lYfX/7yl1c/+MEPdh5+eOmPnn320n+Vpqo8yRNeq+V7ttq8vo4zT3wbD37so9pgS7GhO8G5DQyhXJipIkn1xh0mi/ssHM1CdumcKcFWxi2SOeHXUaZE1stcXV8Ex94LGYfX2761nQL97SGYKwdWEJbCIpKST6y2TTE2FPPkKn81ptAsOeQgOq7YvKydbQcrNV+0vZEtRWUGkcZvYyQHuHFGWiprbAfzDSh/kxsAW/0ROoMRzl3dghAC9WoJC60aFmarmGtWMdOoGHCmcaBFP/PlwDmqUfYuAuGtaLH+6k0C3+qeuakFvN/mEdiZanUt8ZgKU3bpLeDKtijoFUL5AVUplxCGgWfzkwN4y1H60ZAK/XXaH376YJi37mOPLf5RGIqUA3zW2C47N08q+kkhCAKRpimYFI9xqntMR8R2ShinDntBkr9ZUL7l5X/2s5/t9nq9wfJy480DB2b+HZfaySkOl+rDMEAtY01748JlvPnd7zMpygQmcVmZTCEHBstzNbRqJSelwRFlT/rLlJGT8q2E5ot5XFKzxzbr5NWYY6pEhYmnCdsVuPNjZrZll8HlD+SPCY9QmJ7eTUY94N5H5l1l3wn7owaM0wiA1cVl7fx4yNcP/mzmX1Zi52W51hW0m40xr69Y3dnnhM3n4oejFFfWe3j5/Dq+88Mr+NqzF/CD167hzOVNrHWGSFOzO5kQgNAucEmNbqV/0AYl7CEMGFv3tvAJCAf1LHhnismfSYo0T/FWVdcnie9wCKBli6EfaX+qXDRPoJTCYJeGdkpBPfBA+09arfJWgQQ/FReWkfC9I45jyIJ5+az63hjeTRM8DLqV0vxdleR38CC3kwkoLP/ChQvXjhw5cuDkyfnvrK/3Dq+tRR9WStoNbLJz9a1WHVHkG4VcefUMyo0GDrzjMZDKkub+GM0rlO4hgAMLTVy43tXb0yomwzBC5cpRTALJz+PrFP5H5bkOBXNru8MwKVfuBeykitsgEJVKJazsX56YZhsmnlJNjp1Qhir4xS/HZrX3x9RdwHFxWlrcxuLSiqT2onKUUszvvI4T5h+/Vgr2fVpGA7DqVa0NUNZLIy1DT6XCWneIjd4Q51cFgkCgVS9jrlnBbL2C2VYF9XIIMuKzcCtg2wU6mfbT8jty8SzYdwOonMoXonD7m0xPUAk05/zWC0mSeuOmVApRLgU5usJGVH5gsevh1gAx82hXFKzxXua8slL/0oEDzTPjJPKMGt5K+OPOSZJAae924M5wtgNx5RH1HYdbMpDuGsjv0Nhu2+Ju8siFl19++fqhQ4f2pWmq3v72xX/7rW9dPjgYpMeL1PVK6aV0169v5sp585kXUG40sPjQcd1QLtVPAHzDEeDgQhNvXu9iSO5vTeIs4Jus/gdkbmcZ13Ggbw3vbiG4TjtKC6u8DbRy5cAKRBAwyXS6UICfO8izQzBn6cYBOjAB1DOFKz8JxpU6LbDzihWBO43JjO5eAVZFT+DObjNwF2zcu/vaVa5TygMK/UGMaJjgqugjDAQqYYDZZgWzjQpajTIa1RJCESDzeeSkdLKSd/czPuqFYYQV7B703KqeMwS8nHiHquX7JSR2jlprN8ulMDeP7n13KnfDanyi7gCjwYiB9/TtaLXKL548OfcNNu8OAHxO3puXZ0AP45uezkJOIflsJ81jPGc4MRjL/5umgnd9Tn5CuO3S+qTjS1/60o2PfexjUvsplsnDD8/+wbPPXv/vpJSzRUvq+FK6bHjj20+hVK+jfWAfYbduUAbwdZwP+EEA6ywnTqRvoc8AX5NKVra7DZaoEPSpRDueMsOKW/DfrnAnZJ/2fBv1Zt2HuF1WXCSbjEs0VRU7BnSdqQjUvUtVkLaw3B0AO/+hNLArC8oO9C2TacFdMBfPsNb4Cm4OnW+ZrHJecBTcjniu/jiVWOtE2OgOEQYauOvVEuoVvZFNrRKiXi2hWg7Z1q++RzzBRX3Q5yjsM+SHP3d1y/Po7XzfiiFJ9Jx8EDiAz0rY7t0iE+9ujwYjDLvDXbWhXA6vPfro/Gc4wBeAuSD7o+1ANDNP7y2dK/htAX+Kpk5FUW+Ftf1dAfkppfhti9nhsaPyvv/97w83Nzf7tVqtmiSJqtfF+qFDtT84c6b7q1LKoGgpXbvdLAR5JSVe//o3ceo/+Ak059sQhtLxjTgsJ5tZP6+grVYPLTXx5moHqfRFbW8NvjmNk/JdKh/0qXp/H2beDM55UL5MAXs8CCGwuLxQiLgToHVyobvhFex72Dmge/WMA3V7sR2oZ8oc1y/jwN3UoEejk9ollF0mRXvGa09lQCigfZkHwknnFtz1NaTevtXhu2IfChfzFVNJud96+ZXAcJQgjlN0+gT8AoEQqJZD1KshapUSauUQlUqIajl00rkpSikzf2+keFMraDme1ymZZozegpJ8og3PEAiBcjm0dilAwdAaN/SVQjKKEW0NdtWGIBCjEyfa/18YingaXOQS/TgPd9ziXttjTTfHzozxgO2p5G2Vcfaihcjdkt5z4fXXX7+WpqlMkiRN0zRdWiq90m4Hf8aN7sgDXppKNJvj3Z+mcYLX/vbriHoDcIM4LQlZoQQZaykbymGAQ0st6w6UcnBjMQrmDjOG8o33lEvk5W3ONKfsVnjPkD1cJXsnKKXQ77O+Z385KzXeZ5MOjHlULuFmDwuN1K78QYX4fWzuUdmsbqXA2jzecI4bAmYN8Lym83Hj1QH/GTJ9YVtN49rrH2qbq8j1of8d0Puid0DsA9XvGBjF5r/Zg2SkRj7g4yRFdzDC2tYAV9Z6eHN1C2cubeD81S1cXetioztAbzAy29UqT9rnneTH+RJ9msq33Lz8ZmcAEQhUyiXkFtLRO8vQBk/KVwrJKEG0OWD5xp1V0VkdOtT8d7Oz5RtUQ3Y5XNb4LmtNP61RHje+S9MUo9EIaZqK4XCINE1FFEVI01T0+/2pltlhG2y6WSO8Ow7yd9iz3U2V89nPfvZqHMcySRIZx7FKkkQePVr+61oNf+2s7KXn6rbVqo2tLBlEeO1vvo5RNHJEc1rAVwrVcoijKzMoh0EGaPKAPxXoZwjx7Fx7yu6fHCYxACpb6R0MG2sb24L1tk3jQDvm+fIwO4ZJYOCT7zNWT7ZtY0BdFdbFy2f1Mvy7GWDPjy1Xrmu/8q5hkzngd73F0V5lrvnzOSCndsE+K+wYZ7W7+xRvvzm9I10virHRHeL6xgBX1rq4sLqFyzc6uLbRx0YnQqc/Qj+K9Ra2iYRMJSsbpJID8Nabl+90B6iW3SY1RIBcX8Nc+4hN7yBJUkSbfbZHwM7q37ev/pX9++unsyBO8+o0z27qJCZAFV3TfDwzsAMZ3/HzNO0yO9HdCszbddiLkjxw51T5E8M3v/nN3pUrV7biOFZxHEtzqKNH8cdhqH6QnZenNfPBhHWyw60tvPa3TyCJkxzRnQT4RIhLocCRpRYqZbMshdNB5QB/KtC3demKypUKanV/zf/tCJ4cPQ4oJzEGuzyiXoRRNCq8N207+F/uudT4IwOJ7NkZ6BY9Jvvht8HhXhGo23oy5duWMFDfKbC75zXtMfaaBHl+fwHKLJ+W1ColbT7YOnU8jQ9eKY1dyUCZ6vc7iXWGfVL+YLDPLG1yxep074mKSFKJ0ShBP4rRHYyw1RtisxthrTPAja0+bmwOcGOzh81ehE5viO5ghMEwRneH7lfv9dA305SuP9lYHNfX5jpNUww2ip3duMDeX+Y8N1f53uHDzaeNFK5MfGFh3PkNYwTIfW1uLf2kwOfls97vmLQ/6aEo3FYm4G6B/O0G8VvRBgFAfPOb37yaJElqDpmmqUzTJDl0KP5dIeQbvgc8/T4bjfHSPAAM1tZw+mvfQJomNo7o0DjA59Q1DAWOLDVRq4SWkPKCPKK/Q9CfmZspgK+7H9Qt+ttY3yiMH1vvBOCeHsgz/b5jQPdbOA2oc9yzLWPAXlRvFtjtWMq+ByoH7D61m9qay+/XCxaXH4wuFeXgkrtLx/vEle+uXVnZ58iVBaXtBVifcsCybbX974NNkqaIE4k4SRGNEqyuTb8V6r0e0lSiP3CGcg7g2avlfcn6T6YS0eYASjrGqgjIi84A0GiUX3vwwZmvmXilmHGdAW8vjjSuxARkz1y6nyTF0zr5NE0VACRJ4p1Ho5ECgMFgcEuw6mZU9ncU5JmqfqrkE46bzT91GZ/5zGfWe71eHMexGo1G0hxKytFgcXHw20qlN7Kq+1qtsu3WpL3Va3jjiW9DmiUbyid9YwGf4gKh96Jv1mlPZl964oVNC/pKAfVmA+VyOQ9AU3bYXg/drS6SON0BcAPZnvD+vL6fAsjB06gdAvruQL0I2B0Ijwd2B6u8nOxz6HhJ486T2gGS2u22CFmCzUFcKZB4z/vUT8ffh2sbTwPvGnZ+3NcA+GXRb9v7HHDo3bJ2sUfxGBB6r+udAdJtnEvdL6HbjwC4scQBnfcXdaJ9L1JisNnX0x45AC94Z+B9DlQqwZUTJ2a/aO4LwPmlz+RVmTMxAWON7uSU7y7rJIf7tAcc6E8It12NvxfU9bcLyG82vwCAKIrU888/f4MkeaOyT+M4lkIM1pvNzm+lqRxwT3hKKdTr2+8nvXnxMs5+67uOeDJiT8EHBR1DfwIKB+ebmG1UMgR8Z6CfBf6Z9mwhuhcC1pQdvVeCUgqjeJSF6jGgXcwI8Ycf1yd+2vFgviNAvwlQ9xiPDLC7sTAdsCPzHBxbqbUujdf7fJCyaw7aWbW8awNkBpApnhfP9n0HwOZ4eZmuDRZTGLjngMX1iEvP2sbfF+URQmC0C09t92LY2OyxfmD0i82ve+CtAKUkoq0B0jgtAPLsOV9nqSQ2T5yY/YswRGzSKp6HSezZOXoerzJ5vfM2Ujy2k+L7/T6YW9ubxqfdSvN3DOQnGNxNlf025J16WuDzn//8WhRFkkvyw+FQjkYjGYZbZ8vlzX8mpUq52r5UChGG23fv+rkLuPC9p30iywjGZMAHIBT2zdcx36oWEFvKOBn0s8DfmG0i4G0fh2Tb3C74Nu96KFfKqNVqExs86Xn4O8iCeDGQ58EcmTKmBvRsn2dA3Ws7y5NtTxbYXZnTAjtvBGu1YXBJaleuATq/NLWYSXzl+TJnEp/XUfxh4BgYU5Z9COazhMZ4DtBZXcgwC8o+Sx5o3H0eBy8OlAZAs17BI8eXJ9rm3C8hSVJc3+janrWvRDIG0BvLygB8hGSkpysVS0OBdWvmrCAEhsePtz5bLos+Z6wI2LkDmyIpnq5JHa+MkZ1ixnasTFV0Nr8L182z+2qCRuC2Su887AVJPhv2EiMAALh48WL8wgsvrJHxHQP7NIoiJcTVZ5Ta+P2s2r5SmW5f6Wun38DlZ551hIYR2CzgE6mS0HSNPp7l2RqW52oMIPLE3JWZB31OXgUEWjOt7Rs+ASi3SzIhy20NrdmZHFBnj6JIv7+KQTz7HMXlE1Gy0DgdoANeG6D8enneIlAvAnY+VsYBO8YBu3JPQADnPb+SHhg7YFWsAljGwDEARISlqwxgfeYaYcHBA2/XJuoLL21m3t1Lw/uKt5WXrVwb3Htz5R9cmsHDR5dRKYVI0/tfXb+6tuWkZopUmXdCfW/6c9jRAF8M6j7Ym1h7TwjIQ4caX2g2S2sUV3QmgFVTzMePAXBujFcE4ABgpXk6x3GspJQiiqIiy/ppwi0H/rsN8rcSlHebb6q8n/vc59b7/X46HA7V0c0JiQAAIABJREFUcDiUcRyrKIrUaDSSw+FQJsmZL6dp599kl9SV7OYMk8OVl1/D9Rdf8oi+T0xRSNEpSgJoN6vYP9/QD6TgyvKIq1+mGe05IGu2Z3c/vNQUxy6y3OwBIdCcaY0B6mLQHtPcCUwCA01WQhGY7wTQi0Ddvc/tQd2DJgbqLHkhsPNxlwf2vNSueKX0RF6DXW/YuXLKK006543M1mOvqTxbn/4tvfGLqfJxKZD3pTRpHYNA7eD9R8+k48qlAI8eX8HhlTbbHEeYndnuzyClwtXrm9578rQibGxSfw57EUZR7L2bLEADvCzvnlpern11fr5ykZbF6Xbo38xozraxSJLnkju3sh+XhzMBWbV9mqYijrV/fTqTur7X62VJx03j1m5U9nfS491eAPTd5hPr6+vpc889t3nq1KmZ0WikjCEeqe0RRZEcDn/wr8vldzaCoP4JWlK3k3DhuZegpMTy298G2uADAKB0I4q92CnXakBvzRkEuHyjZ4koZVGCjTmlJXZbQqZ3wjBAo9lAr9sr6J5bwF4WIee0YZeVN1oNbxoiLzFsF8ZnUGMvtkk/phG5WO/VqcJ7hczImJvK+5ErsaBM5XAyW4OUEIH5VGjcKgGlJPSecLDrpxXM6+PvkFY+CSpfp3V5lR2k2mMeoJS+S1irvdKarWeF/lYENZjWbgudT0kFEZh6BWy7+OY4CsrkM88uXJvs5jumvoVWA6eOLdnd1rg/+zhJrce9+y3c2OjoTWngeDhi+DzmlAC+G2E0cEvtsqBO8f7Z/V5crD65b1/tFQ7mRRK8AXwL6FkXttkzz1e0Np7u83NWgh8Oh1b6p2MKozsgT81uhjIWhjsiye+Q+7jbgD42/N3f/d1Wr9dLoiiSURSpKIrkYDBQg8GAzunVq3/3u6NR969Jmp/WSpPCxRd+iMtPP4uMtrLg43ECE31kJF00qyEOLTcRBoLl5WlgPkhf0s9K+83Z2YIW2pZk/m7D6BwX1O6OFknxBZJ29ij8c3RrvEReAKa67zPSic1YUDt/R5SH9zR7R1lJHdT6cdK6LZ+X6ze2SGL3hHNKzCKt1GukcQW35l0pxdTkZrU8ez4qhEt8vEEEtIqlsxIixXlSuQMXCyDUl7wum89/R7Yu9q6sVMqkeAHgwUPzeNuDK6iUyDWubgInJPejNK8UcOXaBuufzHsG61slEXUixFEM/orozD6F3G86z89XvrdvX/V56TabAUnv5rfF7CJJnqnsFbeeN/ltmkweBQB8BzqTdyypo3XxRuN7KzDppsPdVtcDtwac70hdg8FAPfPMM53hcCijKEoN2EsD9nIwGMh+v5eeOfOFfx5FnSfIr/1Ow9VXT+PN7z6lBybyIOLBEKf0LFmtHOLwUgthQBIUJ3CO2E8CfqM+yB+FYQI4MnK88964NaFUKaNSq00E6rGgPabhPnCqwsMCTlGR2b6fBtCV35wsk2YBLVOPAz42Ery63RvcHtjpht84a0hHdTCCr0tmrmk5k8PAAZYJYCDLgV7RN+XAVtlvjD+/S1NUBiSsit80wt2XrGxeBn0fSqFWKePdDx/E4ZV2Btndb6NfRSpZPfdJuLy6hmgYs/71vwPXd9qKPh7G/hhlabOMtw/WCu12+dkDB2o/MFVbUM4AuBWqWHleuuyZdpxTbL6eSewwZ5U9M0kdUkoxGmntBEn9zLo++9LvCujvBWc4dyrPLenUp59+ur+6ujo0QC8Hg0Gqwb0v+/2+6vf7stvdSp999k//6XDY/e4O2uqFtTfO4dyT34VMU/fBoACcKc4DfE2QKuUAR1ZaKJfDsUBCoajsUTRmJ6hxIDiRjrlE2//tsOgpQmt2vCHh2McZA9w5aZwTuVwZPpCPA/NpAB289xigTwvqOVxmb2S3wO6AGF67fEM61la4tFB+fJYJsOPbGue5MeuAxLXRgjQYSGcAxQJ1ph+z9wEH7n5aYP9CCx9422HMNGuwk16CEQ1hYs32uQL3lzTf7Ue4tLpu+kNCKQO0zJZCKQWZSgy2IiSj1PYdv6/YeKD47P1Wq/TSwYO1p9g7Erl3ls+rOOhzjWqB6t6by1duvt66wR1jmKcAp64fjUaQUorhUNPM4XCoCOwnhF1h2k7n5e8kyN8NYL/leZRSeOqpp7q9Xk/2+305GAyUOct+v5/2ej3Z6/XkcNiLn3/+z38jjgfP7qAdXti8cBFvfP1bSJPEEhwfNTKAoIzVPbSLUakUyoHA0aUmauWSD5hZwCkA/uE4kJ/YQdscN1HIbv8ggEarOR1YTwPc/Mi+km2AfCyYTwHoNwPq8OpwEXlgzzauKI6NPzijNx3l5plsu7y164pJ+OzdegDLnp7y8PIUSyMZGFMZJp19JgYoUrn0DHU8ZsTlp/L0OQgE3vbgCt5+Yj9KYaD3nxcO1IW3dy17k0JXle5Cq7fXQiolzpxftf5AbF8C7j1A2yMNNgdI46QQvN01y+e/ErRa4WuHDtW+DRqFJhhpWmUkdsXScRC3Zy7xF9yzB+BZz0MZVb25Vtn5eJp7Z6p6DIfDqVzjsnBLVfQ83HaQ36GXO+DOAPtNSfhbW1vq9OnTg8FgIIfDoez3+5JAv9frSecUoRu/+OK//fUkiX64g2fxQvfqVbz+tW8gMSohH1x8gusn0IcEIAKBw8tNzDUrDAx8YPFpuf54RtEwBzw3HXIoOcVxk6HebEIEwY4Auwi4cwA+BsQ5kE8D5rpbpgf0nYO6AU8O6oq3oahxBY33cvA2Ug1EPHmcOWeW1FkmSsJKgDZPBriVQt4RjnS/HegDirVTZYBIsWdT3n0FVxT/Plz+Vr2CD7/zGA4vtw2YCyOt63ottkOr6M1dJ9WDlmXd0q/pjofzF69jOByxMcrGqxkTacI92cEAq3knJq2bcsnnV0qhXg/OHjpUf1JnscECMwUWb1XurB5FaQBnXKeUUtn18SztWCM7PjdPlvQk7WeBvtPpkOrmTgmqY8NemJOncCeZgZuu48KFC/H6+npCwN7r9WS325XZPIPBxuCll/7if0qS4Zkd1OuFwY01nP7bJzAcRDl0cPSeSaVFoA9guV3HgcUmgkB4oOIBv0mepKmdY+LF3AYM3j5si86Tj+bMeCl+GsDeEYDnwJU/BvvL1F0I6BgH6NODugfs2U7Nof10wG7ba2tSRjpXFjgB2HSOLjugViYtLGNAUpYj9tQBCrw8ehb7lHnAybSB5/fS8r7iDAH8Zzx+YA4feddxtBoVb/rdAbiw6npS0Wttvb7nltRpa/tObxcasj0Qbmx0cX1tyy1XlP7SRSkVkiTRu8ml3ty4z7DBMV3u2tGxajW4cPhw/QlAS99srl2xshQH8cxvAmufQ4ADfB5H8+zGoh4Zib4Q8NM0VVn/9Rmju6IuvBOYlQt7AeTvGlDfbB2XLl2KCeCjKFLj8vR617qvvPL5f5Kmozd30AYvjDY3ceZv/w6DTmciyjpMyIKXvteslXF0ZQY1Pk8PR+woTxxF+cLHhLvKAGwTtMFddXIjpwHtbQCcwjRAPg7Mc4CeeXeFbbWXxaDOITr3EOMenEAx8xz5PPDzEKArpaVzBfiGdO6+LsnM5ypXH2yRiscwgGAALpU3FcCZCUl9p+BAn/cNpZe8n6ntOq5eLeFD7zyOd548oJfBZebZAQ3eQWYOHnDSO5897UcjPPfqJfz/7L1ZkB3Hee/5z6o6a69ooIHGvnED98WkJNsSqcW+kmVL45B0FXLYcb2F7Rfb1xEzo7B9J8YRll7maV5mPI65MXFnwmM7JFlxLdnSWLq2JVGmSIgiRYIEiJUCsQPdjV7OVlvmPORSX2VVnVPndKPRIJnkwanKrbKqTtcv/19+mfXKqUtYWk3em34nhIWlVbz51jUC9eS3pa9nFEborfRSr4vNgr2/iq/VnKt79tS/C4hkIjz5KdP7LPoHCnpGE2zPet1pIPVr4GsTPoBEtVMzfRRFgjrfkRfTDHr8bRjsbyfkNwruw4ShykRRhJs3b8YK8H3Dysrl5ZMnv/H5OA5+MmSbTAhbbZz95++iNb9QCJPcQB/GQr6uds/sOKbHaybJBojfC2jhnE+5UJKtt6xDIBe/WVsbRN5/uSplAMgxCOblgW6A1A/qKXgXxeeA3T6XfmDXpVIe5Ik6h4IsBSygH74qjy5P8snyGsQE+roDkOOEp7e56WgImBfckmNDb9OOgIa7qvPgrhl8+Km7MTs9BkCr9ETBg1lz35kVzyjgGa7cWMHLb1xCuyfnUx8/cwXLrTsD9Nfml3Hu/DXzwh16veR95Ah7IXorXaPgOU/NVc90rsgtU+P7QLXKru3eXf8uY4gNfSXLaT0G1FqpAwnwc7ah8jML+OrYiVLXkUS1mylzGux5ip6sWQ8hBCvpdKfDLefa7YD8qPC91flHutBDzINny8sXl48f/69/HEW900O2zwQeBHjzO89i+eIl6+EtQwY8Fjz0c44BmJ2qY9dMEw5DJl/Q8wcQcP1xPUyHoBSwGUN9vFka0GXA3RfgsLmZ7BTCPOfEi4BOQZm99Ta89QHsim1A54DdbhQ9l9RRk2PpMxLkLXKk9QAS0y4nL5IXAmlgQ7cjgXI6jsy/J2PqXKRNx+k8yAKdQCc5FtCsV/H044fw+H27lHNd4lSXdrKT+ZmCemIulBXph0kUc7x25jJOvXVdeaDLdkac49WTl3D+8iLpHG2+cOnqIt66dMO8NMg43PHk3gbdEL2Wb11bdQ/UqVFHvaTTpZ3gBGo1dmXPnvp3HAcRvR5EbecGa9pcBvQ6iuQXQs2Xp3H6WDkOeEIQZa8c7wBkFD2CIBDqk1pBr2QYKv8wHvYbDfkNg++QZW51flOm1bq++tprX/1PYdh9fch6TBCc463njmLxzFlkHt728yKHPhQyY40K9m+fQK3ikh46RxSEhQAbzPVSCL7loTHWhMOc3EOXaWERvG2A50M827HKhfkQQM9A3b7v/aBOAG13dnIbOBDs2XJ0XF7XkTzMdT6ulrXXTlIJ3mVZdRR9XC4yU7NAHKw4uKkBuk4NIXo/BGlfCjQw+e7auxUffd892D4zDsaYNMMzlhqHT8bfE7gnql2Z7NVf/PJqFy8cO4/ri6vmGghAKWAJ+7euLOKVU5fQ88P8H/FtCkIInL90A5evLZLOWfp3zoWA3/Lht30DbHOeqe88RQ+zXa87F3fvrn+PMcRcUXUQ3PPgbY/Ba+DTJXBJfmN+11ltuOvxeep4B6Q97G2HuyAIhOV0N0xYF0c7O2z0FLrNlH8jjpGbv9td7Bw79qU/C4L2y3npZcOll4/h6ivHjLqRIefhbwcLUp7HsHd2XL7JDkDgB8mDs6i4/aE8KMX0oTE7qMJMaIyPZThXBOlCaOfCW2RbU9DMLMiLYD4A6IX3Ne9gWThnoJ5pdN6NzDu2fUxkwQ5trtXHU5Dm6Xw0ThCTvLkmRC0agENOD9X3BBxpL3n9MAdPgM6TO0aBr+/BeLOGn3vqbjx5/x5UPa3ekYy/G9hr8IM42ingawc7ddXOXVzA0dfPo9sLMsCTsOTG+rCy2sVLJy7g2sLK4B/1BoSVVgevn76Ia/PL6TF4wcmbBjl6q121TG1auWf+dvS9Vr8dTu5Xs+n8ZOfO2nOA4ArwpGNY5LxnPsKOy1P2dqeAzounY+8qznQIKPALQI8oioQGvP5utVr0z/e2g34j167fbOG2djp8f9V/9dW//eJDD/37/6FWm3jPkHWbcOPUWcTdLnb91GOA65kjJy5AFAikWfrBBen4pM33jaqLc+dXR21OMX5JQqkLOTBTf9B7lQoqtWrfzsoaqh+QRQza7F+uzEUUOXGpnPZ9z8krcuJ0Qr+yueWEedurI5LxaNOnENx4m0vQyjQZRwEh266tkULIn4JQ/8rnr6qDA8Ixh4der57mAWCWxheQ775xlXGHCYb7Ds7iiSN7UPGcZM35lDkeSFtGiXOdojxN9sMIP37jEhZX2nCUqndU2zmUqhIAd+Q2F/JcwyjGyZ9cw5UbK9g7twVblS/ARgY/CPHW5QUsLbfUPbM6W1x14GK1TG0QgUKbjsNn1bzq2BDAj425Z7dvr7wkuWvUtulBCiFsOV9G3mcc6kxhMoWOzLUXImecPs9srxW8Bj19j7zlWW//cSQ/8nJh2Px9w0ZBfrOp+FtiFhk2hGE3fOWVv/5fHn74s39Yr09/YNR6Fi9cRtjzsfe9T8KpVaFf1GGC4bp6aYfxApaPTpOVAeONCnZvbSLodNC9RSbEUr/egkxlb1xzfHyNoC6Ru/9uuXrWAegypQTUTdR6gV0d2URzgDnQw5EC3Hid6+e3AMA4AxxhgC6gXxrDEuMAV3BW0z1lEf2SGPnLFUyChzkE6Ejn0dDXr7ORnVpgeqyOpx8/hLmtE4nZXR5OBu1kZ4z0zMQl6h3J3xYDri+28PIbFxFGsTwOk/VxR0jQCwHuMAl6LtOZjIbjyMu7uNLBwnIXY40q9u3cgrmtE7f8vfRxzHHl+k1cvbGUWthIOzCmp8mpd8GHMfIAD8D8HmyVbYxEAMbH3ZPbtnmvanAjAbsNZG0lEHn5DM3Tkfr4GU960i4N7wzU7UVxFODpWvYa9oKa64MgECsrK/3eIX9bwjtZyQ8TblknIo7D+Mc//pv/9eGH/33QbG79yJDHMWH1xgLe/M6z2PfeJ1GZsl4sI9IPJL0al8OkaZKrNfL1M5zxGAd3TuPqYguLK5vLA7gMSJnDUB9rlFfxBdnW1pUuK+GLegoDgJ6bZUioZ5LKK32R106uwM4YBH0rHVcwc5RAYVqhC+h/BXlLnaYph4ALQAgGRt4+p2ENoUBPVTySPLJ1DFy/dQ4MggEPH57DUw/sRcWTZgBqnk++0mo+yZcaoDdHfe30FZy5cEOZ9PXYvASkw5kBuqPhrqpgAjIfl8MBQq2pv9rxcfzsVZy9MI89O6awbcs4xpu1dVMnccyxtNLG4nIby6sdo8ABECCnIR2HEXotH3EkneVstQ6AxPECNS8wPu4c37rVO54nwweMxReZ6vOgb5cz9ecdQ7Y9BXnYwNcmeup8p1Q8ADkeX2Lq3IaHdyrk5dNlk+QXIhavvvq3/8eDD36mNz6+/ReHOE4qdFfbOP0vz2LP4w9jct8emEeSox9iQj1cHPXgEkr1J+CII7luMxgwt3UcY/UKLt9YIUtyph9ymzHUG02lKG9F7aPI9yL4Dq6gGOj96i2qs4Tazy2vH/45ZUh++TtKzOoMEri6UykgwJQal3Bn6m2vul0MHEKqXyHN/ioawnQM1HHUPgQgHAEBB8afmUKfyWNxAcxMNPCRp+5KqfcU0M2YOl3Axpr7rgGtyN/u+njh2E9wc6WrZqnI4ECDGxkzvYa7o86LESWvp/6rZqMXRDh7YQHnLi6i4jmYmmhgy2QD0xMNTDRrpV9hyzlHEEZYbfdwc7mNlVY3BXB5f1XnTVjmeSEQdgMEnSBlbqfgTsM8WdnPWHDUzZqcdF/dssU9PQzcFWQL09PtSXvN0zxWWmoePI1X1ysFd2quB5I58npbK/l2u725ZDw2DvKbCqqbMQghxLFjX/ovR4780s3p6f2/ihHPh8cx3vrhy9h2cxFzDz0I5roAhFQX5mEmyLijAFNqCwAiy0Q/MVbDoeoMLl5fRteXY3CywXlHZ7mbGx0a400MrcNH7hD0KVgS5DJHv+takDCw/lGhrlpUpPKLOgICapycKnYFdkco+7VS5rpjwKWK547S8AzSIuAIqe4BMCEgOINw9HViJo98Tz0DGIfgDMyB6igwcDA4HHBchsfu3YWffmg/PM9BahydqGoDd50ADXSSRqB67uI8Xj55EXHE5fHIODsYk+DWZnomQS64GhJQ8Ncw5zxR9FxIKwAj6UII+KHAjcUW5m+24DlAxWWoVjx4ngPPdc13tSIf62EYIggi9MIIcWwpdbmR7Bu4WwqeC/RaPURBZPIlIBSpbQ14/buxxuHjLVvcH42POxfKes4TE3uqAM+pgMSpUzLwp7Dv11PImO011KnjnQ36MAwN4KMoSq0SulnCZlbym61jsCH5T5z4+tcOHXrmxvbtD/w+Y6wyRHkTGAMWz12Av9zC3vc8jmqzQYBOx+OVxzAYuFrAJAyDTH2ViouDu7ZgfqmDG0sd87DIP4XsJmlZ3921BM/zUG82UB9rwvW8EaFdstAgcPYtOgjmBYmlOgwloG6i1gnsmWiuIC5Nz2BIwK5gJYsQ1Q4BV5vhFcwhoMzwUu1qCZ8odJlgzPJknB+QY/xgwJbpMXz0ffdg57ZJAnMytk7WmU/G3HNUPinb6QV4/tWf4Or8iplmx5GY4mUHQ0pyhzEIpjo1XJnwwcA0yPWxhO6waEdYpkz58rwZk3kcB3DVEEUYSYc92r6K58L1nMytM3+zNthVnMkjAC7k8F0cxfBXexnzfB7sKeCFUfVG0Qezs5XnGw02ryGc89HtMtxOmp5R6oVmekX5jGVA7+eZ5HUfQR8/bzxew11Bn5MxeQDSVL+8vFyk4od9Go0sOfLCRkJ+FLW9KUE8ZH4MUUYAwLlz33m+11tZ3Lv3Pf+j47iTgwplA4PrMvSWlnHuX7+PPU8+iskd28mDC6BzfF0HiGTnVL54oQDQ26bHMDlWw+X5VXR6ozjl5anIcueTF1zPQa3RRL3ZQKVK+kMj2+nX/rcl7KfrKMcrC/TcLMNBHaCXq4zKzztusiOUwgbTfywKxALSS9txIGe3M7hqDJoRJglVB3OYMrXDjMlrT3oBBi7UeD2FPmfKyQ2A4+A9D+zFTz9ywCxqoynuGC96+a895m687Cnc1c7p89fx4vG3EEUcjsMSUzwSs3zyrXsmwih9baYHk54FDoTsrCizvXRyY3qwXlpCVLtcRw5tmLUEoDsGAq7joFLxwADwOH3PTEdIpO+dhmWyrfIKgbAXIuj4ZjW6NGhtZ7p8wAsBuC5rb9vmPlepoKWBWsBm3idNK3U73Qa/AMCJqqcgz7y5jnYIBo3Ha9gzxszUOWqmV6b64j+y8mFdAQ9svJIfFnq6zKYD8QjHGCr/5csvner1lv/TXXd9+I9dt7pziLKQil092OIYl154Cb1778aOew8DSMyT5uMy+KFAzAVE3/deS/PggZ1bcHO1i2uLLTP2dmtDcgzHdVBrNFBvNlCtVXPzrP/R+ynr/iXLJQ+qrJ/6HwbqMr6/Ws8rK/p3JqzjCT3uLiBVPFfmdOaACw5HMDBHwUqrdugxb60qyWHV2HUyJk8UP9QiulzAcRwwAezfuQU//957sG16LG1qBwyskzF3DfZEuZt0tcEAtLoBnn3pDK7cWIbjyI4AV0pdmuLlGXADdN1gJjsDQsBRlo1YSNM9E4mHPbWueY5Ijq2HE5TK1/dOt90BUK14cF1mFHn6ttD7IjLxGtTydyE/vZaPyE9PjwNgwT15sxwdg6eA9zwszM66Rx0HPQvMeYF2OmhnIAVtC/p07p0x19MxdgvuKcc72XSuy2Tgrs30apsLIXieV30YhkIIgZypc7cd8MDtM9e/LUA85DGGbtPi4tlrr722/D8dOfJL/321OnZf6YYJmAeRHkdcOnMWwdISdj3+MGoNCUdtwndUk6JgkDpPfoNbJuoYb1Zwdb6F1Y4/zGmNFGqNOhpjTVRrNXMoYZ54JStZ1z+hkpWVBjnJU5i1X2djBKib3QIBMgTY02UEBHcAh8vxZcghIcG5UugwIGdkDF9AQAhHjsHr8XZo9arH5GXgSiQ7JgfDRLOKf/fee3DfgR3JErR0VbqUak8WsKG/n4ylC8Dxc1fxwrHziOJYApyL1Li6Azmmrh3pAHle2ukOXB5CK/nEm176KxgHPMg0PbTBtMoHoJ1mdduEEKh4Hiqeq2bIJOny2tr3WZAv+ptIFHkcxfBbPngcp8zxOt02z2dhnwC+VsOlrVvdlwERca5G/BVz8wjPZeVUrYN82+Z6A2Z9ukStU9jboBfybsCsqkehb4M+jmNTJzXZExVvps5ZpnrdrrJh2PzaalEqsCHyjhTI++SLPs6A9PUoc0cfw/Pq1Qcf/NTvNRpbfrrsda/VKvA815geNfSdahW7HnsIE7Mz5kEHBrR7IVaWW/C7w78Gc6Xdw9WFFqIBVoBRg+M62LZz+zrVu06/934ALFu4b5HRgA5gRKir+KHLZs9FQMBx5FQy+ZFgknHyjrmOtDQ5DCYvS23LvLoO5kB9yw4D02kOQ8Vz8LOPHMQHHjuISsUlZnfVIDPGnlb0JBnmtbBqHwxYbffwr0dP49KNJdMmBv23hJw4Zo6tne502xlpM3R+mSWTxnQjhNVRIW2tVSvqzXj2nUjfn9TzXaTjhBDQQymBMs9nXtMrkIIs3c+a7aWiHxtjZ6annRNCiBhpuHMhx9opzPmgD+dcL3ery6f2VR6dL6ZxsSR1LISI9b6uj3POoyiKdNk4jnkYhjo9DoIgiuOYR1EU93q9OAzDOAxD3ul0It/3Y9/3+crKStRut+MLFy5EQRDESIBd5sOHzG/KDAP5zeB4N6rqHqbMRh5j3ctEUS985ZW//t/uv/+Xb0xO7vpkqYqFMl/qh4R+4kURrrz4MnoH92PHfXeBqQdWxWEIlQftUGcA6YE/1qji+mIbNwtfoTksXJMGNJqNbHGTPCK0BxZbS2cg+2Ad+jilLABloV5UzwC1bnbLgT1VLZfj5In6FgCX8+clv6TtWpv2ZR5myslxejUPHgAEGYdnhoG4Z98sPv6zRzAz2cwodxDY0vF1OuZO4Q5VLwC8euoynvvxOUSx8pxnSr07MEMJ2mMeTHvHq30Qb3kwOMrsIKT3nDT1x3LxH6aVNZPnmnRA6Kp/8uo4DHBdD7WK9EYQ5PYJfcqp30RyT8xvxAY8V+Z58rdvw1yeD93mGdgDApwLMTXlHBsfZ+c1xGV1KcCnBLzON8DjXp+Mri81rh7HccYCoOqLYXnm0zx6Wyv4KIpMB0RM8xEjAAAgAElEQVTHUYe7PM96xpjodrs8CIJhps4NrdxHLANgc0Beh7VA8raDeJ3L6HLm/IQQ4vXXv/qlgwefPr9jxwO/w5hT71cZ58KoC4CoGAX8lbcuIlhaxq7HHkB1bAye4yCOyfSPop9Twdk4DsPctnFsmazj6kJrRMc8GpIG1JuNbIPWwuCRw6ht6JOxFMyT9L5AN1F9lHpucl6nJAfsmdO3y1GoCGgiCgVRs7ys9qRnSHnJ6x+8XBhHdgK4WjnGUWvlyjVxBGYmx/BLH3gA9+3fnpjmlVUqDXpiEqN/CyrBhvtKq4dvPXcCF68vGSsChzCOdHphG4cxA0n5NyWHvTiXgHYc2YFxHGGWrmXQnvVqnyfe9QCMNz2dNifbC3iei1rFk+P7XF07NeZvwF14SxN40+84lN7ztlmewj2BuA31tNmeMdGbmXFeqtexoE3vSAO+X6Ce9JTsKdWv1TsAexoddaTTY/icW/Pfyfi64JwLJeKFdYyMN32eqV5t8wFe9XYYFtRUwY8cbjnkhRCCzC29VZBcL6huljK6HCPbAIA33/zuC6urVy4dPPj0f/S8WqFDXhxzA3S9LKZ5LaZ6EAatFi7+4EfYcf/dGNu5Uz1k6WFLnI11ZrWqh/07p7Ha9nFtsY0wikuebn6oVKtwXQcpuo1u+U+HNXcUSlZQGuRJnqwxrgjo/eotAXWzW07p9wO7nV+vRidTlCc9k+PtQjgQTK5dK5QHnf7tyZe3MOXMBgU8Cb9K1cVHnrobTz9xCJ7nGPN62nkuHZc4sFnwNx0DGf/SiQv43o/OSPXuyEVpoJakFaoRejEbqK4LFzDWAul0pzouZN47Y8q/UDA4TCTnw2QexhgBu4yDkG1zXWWad9VR6SIzDNkfin1PzPUX6vbI77AbIOgGuWCX9Wfhnjbdw4y/u65Y3LrVecl10aVAB4E22TaOdFa6gbPdMaDj8LJdKcDTDkXe1DkIIdU+8ZannQpuj8PTbWvqnIjjmCuzvgjDUPi+LzqdTr8/7FFAvWaw03C7vOuBjQH+rTzOqMfKu3lDYWt+/tSlVuva/3zkyCd+t16feiL3INrEZ8YK00pebwshcO34KUzfXEJjehs6vaCgiSWaTIpNNGsYb9SwsNzBwlJHLhAy9JkCjWYdmVuzbj9/YM2VDVLWAwrmj6yNAnSVVqa+Qqhn68jMLMgDe6a65NyEYGAOl5BnGpaAfOUsU05jejoaUyvFqZfIqEVzuPKcf+Tenfjk0w9geqJBTPNpz3j6naxql5jn9TeF+83VDr7xvddx8dpNM+ZvPOe1JUHNfRPGo56lTPOOkuEc0pnOTJfTK9kpoHOh2qOsCJL5Ih2nLAjVqouK50EvDaxnHhTd1jy403sqIOTiNqs9xGGcUfYU5Ml+Oi4x28v70miwn0xPO69DzUSDAieQOMOpRphxctW6DMwppEE6ADTAGse2HO5S3vIa7qSMaZdW8UTVpxzvlMe8sSCo8XthT51bWVnhQmTuyrBgH6VM6XDLHe8AbBbnuzu1HIrqYYw59933i780Pb3/UySvCePjDTQatVy4px6E6juEAzG3G6w60ho8JKSbEkYc1xdbWGkP59THHIZtO7Yl/gRDdhBKh75/Amv5+xgS5JmkQQKhKEsemIsyZzsGpcA+EDBJJ1M71bnasqQAyZh2pCOOoQrYLnOMs92ubZP4zM89jHv2zZrfa/L7JdtgKec1Y0BUMHfM70gNHwA4+tpP8J2jpxBxDgdJu4wjoKpIbztMQz/dXkaPYeKzjni6CcbxjpyH7rBUKx5qVS/pjICci2k5kr8HcukZ7N9bAmwexui1euDEQVZDXG8ncVng67F3FR9PTODY+Di7pKGuVTVlNk2j27Zqz4tHgYOd2k6lqe1YdSJi5WBnnPbodqyCjgzDMFK7kVLqse/7cRRFcRRFPAzDuNvtxkEQxL7vx61WK+52u3G73Y4vXboU+r4/sgPdKGVIx6lU2Cxj8hupiEc51noeb9hyOX/KKkIIfuLE17++d+97zu/a9fjvOY6bej9lEIQYG6sXwt0241eFwIUXX8LEwf1obN8OpqXKsIGlm1rxGHbPTmDLZB3XF9vk7Xb9L0NzvKkebv2AdjtCuiHFf3IDGlwK5iS9bGdhkFLPSc6a4e1M5cGeqlW9Tc4seKOc0QRncNVSt0KNtbvQ4/dSEY9VPfziz96PZ37qLniOtJNn1pwHDJCh45n6ZTH1xjcL7owBC8sd/Nd/eUWqd0goczVxXW/rxW70T5ArNe8I7Ygn58PTdehlq5Kxd7ninYCIGZgjVT5D8opZaZqX0wornoN6rQLXcSRcRbIgkDbnS3t+cs1Tf0GpW0S2OEfQ9hH61LkuAbjcF7lpOXCH46CzZQt+VK1iWcNcHoanlDxR6dSEnxr/Vg3NBT/NkyfqVRtN50IHXU6rel1Ar1ZH6kx5+VMVT831vu9zMhbPFfh5t9vlOXPji8JtU/gbAnkhhh6XXy8QjwrhtZRdyzHLlk/VdeHCC6+url79s7vu+sjvVyqNfTohCKQjneMkcM864yXKyHUdNBo1LJ/9CVrX5jF59yHl9DZkkwrOrlmr4MDOabS6AW4stdHzi9d5bo430RxrpJyPbmUYrm9cMnOOoipVYJhOw8COQgmom/QspEcBezqLUGPzymGNM4DJt8IJtbSrEEjNlwcD3vvQfnz6ww9jaqxG4EyUsDZvEysPNc9ryANIdQw4F/j+y+fwL0dPIo7kvH1He8FDvacegH79rZzGpykuTfO6E6GXsgVDYqZXqpzOo4cAGJNvo5MOh6o9CvQVz0Gt6sFzpW2fc7VWHtPXUp23HoiXiIJeR0Be6Jz7ACDyIzn3Pef1p/3AnuzTsXigUhE3tmzBy46DgDqpI1GYgnMuGGM24FMqX3UIBjnk0fpTnQCq3DXUczoBZiEbfTwkHQoN8ZTZPgxDruGuM+o61Fg8fa1sv2VsbxvU7bAh5noA/Uz2d4r5fK1l16N83zorlWbt/vs/8R+azW1mPv2WLRNKzSMX7sm2TOt2fVy6NA8A4MxBY+8eTO3aIZ3fsnc1J658WO0GuHGzDT9Iw7453sTYRHNNdd/S0A98w1TQt9goQFdpuUVLQj0TvbZyAkjmtTvq9cYKrAxMzvXW5m2H4d59s/jszz2Kw3u3KpgSc3ZqDF7/lkk8kAK/rfrPXriBr33nVdxYbClLVpJHH58p9c+ctGneSf29KOsBmJkDr6Fu8sH6G0M6HWCoeC7qNQ+e6yZt10DXgWX/ylLJjGUALwCImCNo9xAFydg7gBTU0/s27NNwBwQaDZydmsIbIj0VLuXERuPoN1X5QqQ85O258ymQkzLGPG/BPrbT7bnwZDtloieme86tefFhGMZRFPFutxsFQcC1qX51dTXudDpxq9WKL1++HNHzH+EzUlkxJLQ3A+Q3E4hvZwdg3ToBhw49877t2+//NcacRrNZw7ZtU/oe5AI+SZPfb755xVgBAIDVG5i6+zDqE6nRgPzACncKw2rHx/zNNvwwRnOsYQF+bR2J4cN6/D3YaneEY5UFekGWfKjnZR6g1s1u+XIi/Q8APTZPgckU9OX+gd1b8JkPP4JH7t5lQE2hmoE7kvFvAOk0BUb9yFludfH333kFx05etsbcydi5Q03/SafDAQG96ueaRXAM7GHK92u3blfF81CvVVKzAwDaIVFnSAHPoI0cJtjWLf0sj7oB/HaQC3c7fxr26X1inu9NTopXajVxA0Sxg4Bdb5PvDMxJ/tRCODo+x1xvA98eh48TsZ0oe3ubJ2PxBuwU8HEcx0EQxHEcx1EUacjzXq8XhWFoAK8gH7Xb7fjGjRvR6upqWUivW0dgWMADGzgmL4Qx2Zd5ahedyJ1Ydj3rKFX/uXPfeX5h4ezZu+768G87DrsbgFLixXBPTPoMW7ZM4Nq1m0mjel0sHXsNjbk5TOzfA8d1S55Bn9MhV2OiWcVEs4ZQAN2YWdPuNqYTOjgUQXPIcoXJgyocAugmX16do0K9f9k8sNMy+pWy8hWyMHE7d0ziMx95BE89uM+oY+qkxlLwYzmmeBvuGo4MMef41x++gW8/9waiKJZL6qqBc33NpNO6WtrZmL/1IjYCXLWJK1M5A8wrZSGStecdrvwJHGupWqaXjRZwHBeNWsUsRavflCfUnDtpktfnIhKoM2bm48tWqCtr/a2JiKPXkp7zqZQU7O24fLDr/UpFXJ2exquOIwIhL1oe1FOKnihwQdKTDV2YJ29+o0CHpeqtToLQZYXILF2rf4DUK14DPWWW1x8FdTM/XhdTK99xPWUujmOuFr0Rvu+LVqtV8AeS+ykT1lK2b9gwJQ9gkJq/083o62WKXzeTvuO47j33/MLH7733kU+MjzccG+56eU7z8FQPTM45Tp68kPvyGcdzMXn3XahvmUK5fku5UG/W5XQ5IbDaCbDU6sEPS86xL9uMdf+pl6iwbKfHzlOQtRjoRYXKQn348oPAbsc5ZEnanVsn8emfexgfePxwygM/BXeGVKdUA09DX/+WHVUOpNzrZ6/gK99+GfM3V4k6J+qbKne97SSdDPkHxBKzvIa5kx4y0I59Jt2ykjFAmeWrqFQcU1a1OHXOyTnIiydN8SovKZNECK3vEHYC+B2/lBNo1lRvdwQEABGPjYnXx8bEBSQq0l60hk6HM9+Wok855BWo+9jap2CnCj+maf2UfJ65Xi9xq831WsVHUcSpiqdL2FIzfbfbjTudTnzjxo2w1WqNqs5HVvXqegwdNot3PVD8BCzzCO/39Fxr+fWqo2xd64YizmP+xhtf/wfHWTr9xBM//1uAN0PX2Lbhrrdd18PWrVO4cWMpW2cUY+nESVQnJzBx+CAqjcaa21lv1tBo1qCly8RYFRNjVXR6IZZavQGr57Ehr9g6kn4QPMsU7lNkJKCXbVcunIvqEOmtIrAXlVP5BQe2TDXx6Z97GD//3nvkWLSBdYF5OxfuJN6C+/xSC1/6p5dw7NQl42RqVnxT+bngEuZMrWan5Liei865siZw/X54WYOcPy8hLFe0Ewa2jvod0pfQ1KoVaZZXr7qVyl29gEY1Xcgv5VMn9J8AGIhnvUpn+hwY1Eo9ArEfwW/3Mq+XTd2Fvko+2ddK3vPE8tSUeNnzREupd6PKkcDaAMtW8jSQslyrbrXNGWOFgFd1UaBTBzudN6XkSZvovHguhBDERK97Beb1sfo7CAKt6jlV8b1ej2uv+iAIeMErZVOXs8+nTFhL2UzYUMgLkfKyBzYHwMvUs14AH6auojpFn7TcYx0//uzJBx7Y/b+Pj9/7i0Dz0SK4U6U/N7cFi4sriAteOhOsrGLh5VfR2LEd4/v2wK2MPre+UvGQlSAMzVoFzVoFfhhhadVHq0uUijnzEX/7A4uttTMgcjfzcxZkGAnoBZGFUFfxBR2DdLEcsBd1KKzjTYzV8KkPP4yP/+z9qFU8pXqRqGAk677T8Wz1fwJ3g/Y03IMwxv/3/dfxzX97HXEs57xzJAvXMEcAQvsCMEAI00S9Br1+oxyEmgKnYM7VW+FSr5Qlc+uYmlLHGIMrgKqCu+tK1W/W41dvnWNqPpzkupxwp07KqHdhgK+X/NVvo1OXPYzRa3URBcOvKJkoeP2d3LB6nZ+dmBCnABFzDsFkY1Mgp8AHEqUOMj0OyFfySCtzocENnYFMb1PbKVWPZEGbjJKnHQEL6Fx3CGANBVAFr+uiS9l2u11O3xm/vLysF7+5VSBf64MnE263kh90QusB382uwoetk/VJKwxnz55afOKJiX8UYuYcsPVjAGsUOeA5jpxONzc3Yzzti0L32nX4N25gbP9ejM3tANjwc+vDIITn2eP8yanVKi52zDSxNa5judXDctvfoPfY5wXRdze/xEDKD6hsFKAPqK8IznqrsFNQHuwA0Kx7+OTTD+GTzzyIZr1qYK3pbXut28rdIb9L803gDgAvHX8Lf/PNF7Gw3DYL2ggmAarXnQdXc9uhlDpjZi48BMi74ZVJn8s2cM5VG1gK/vLlMsysZOc50lO+Xq2YF0PJOhWihaoCenU7DXFybmpsXpDzN2v8605XJOe8B90gdV36hTwlbzpxap8x0Z2YiF+u17FIIa7BCGTUvI7nJJ5b+TPme/pR9ae2QVbGox0BouIpuE3HwTqmbWkwcWEYcq7G4q3larWq55xzY6bX0+yIiufI/8PIXPYBnzLBvt4jhQ2HPFHzd5oZfZi6bmWdI4UTJ06sPvnkkzOe1zopRHAljnd8FKgeNuOaOfPn5+ZmcP36EsKweD47IB9mq2++he7Fy5i6+zCqW6aTZWytwHIuie9HaDTy89NqXMYwM9HAlok6Vjo+Vto+grLj9rmVj140qaJkJQPBS9IKsxQkFAKZZCgF9aJ6hgM7IFD1XHz8/ffjUx9+BJNmCicAkPF32zyP9D4AYqZPOgU6XJ1fwf/ztefx2pnLZiydM7nQjuNIs7hcsEY7v9E16IUxv+vp+TQfF9okr9qpP1xZHZS+rlY8NOoVZZ1QWBbcmOR1J8Aod2gzvzDWCKno5T/ySjIwIWHPGDPr1QcdH4HymteXYZhnf/60OSZqNX5hYiI+4TgI9DtdlCk9D+pm31L2uWAH6SjY0NZpjDEN+xhIT63TH2K2j0kbUgvfaPZTYa/fUKet9XYbdadBCMHJOLxZular+FarxRcWFsqo+PVS8+vGhdup5NdDhQ9zITaDCl9L6Ge275cPANDtdvmFCxfahw8fnmIsblcqV77K+ZYjcTz1IcacJpBnunewb992nD17uVQDozDCwvGTqI+PYfrew2D1BuLMHN6cZvMYQRihklHz+afHwDDVrGOqWYcfRlhp+1jtBEM98Pq2Z7gKBkUUp/fNugagD2hXmucFUM8k2e3Ogh1CzuL4+ffeh8/+u0cxMzlmjaHnwD1nDB4g0Ce/SR38IMJX/9uP8c3vvwauX8TkQL7URitUteiOHnPXY+tcWaqYkMPaDlNwZ4yMwcOMzTM1bq9aD0cIMMdR4+16jjsjU9D0S2gU2AUdakh+b0anq2uZUvQseU2UEAJRL4RvLUerwyAlX/Q3IeTUuM74ePRKvS7mJfMSsOtAzfXqptvqnHYEUmPrpB5jpqdpWrnrAJ2orO6kP5BR/zmmer2kbWZ6nu4EUNWuTPRcO90phW86CWo8XmhnO9/3OVm+dlAo0wkoXY8Y5cFGwoZ616cOzNh6LgxzSxeZ2YDPhhxr27Zt1V/91V896Lquk8x08JphuPVpzuv3572tDmA4ceI8lpZag25pJkxsncbkoQOIq3XwAY6hjXoVjUYVg/tN6aB/vVwItLoBVtp+ZnGd0pUMn9g//8CifTKsWfkPA3UVP5RaT5dxHIZnnrgLn/vY49gxM5EGdxHczdzyHLgTL3Ya/u3HZ/FXX3sBi8vtxPtdm9iZHnOX9VBverOwjao7HcfMOvmZthkvfCjVXkWt6qUWygFAOiTkHNXfj/5J63PViJebiU+CDrqOyA8RtHqIiLWKThccNhBTvajV+LmJCf4GY9JjXdVbWsWDAB9EHWOAqT4nzah0JCo+1uk5+7mL4gjpwGf86wz5yVx4zjmPoijWC96oFezM+vRhGMbKJB/5vs97vV7c6XTidrsdz8/PRyOuUV/0KV3XnQz52w3BjYL4RnYWBn4+8YlPzN13331bdCfLcRyHMcaiaGx/GE5+CHAnzYOIyYe370d46aVThU54g8Lk7BaMHzwAUakipl7A6kFVq3loNGtgNHINIQhjrHR8rHbWY+w+p/xQVQ7IXArmJH0A0M3ewHr7QD1bSWG5etXDR95zDz7xzIOY2zqZneOufnnam50p0mngpXxBcsbbdXjr6iL+81f+Da+fuSzzqcV1pJmewNwBICzQOxLkqZfLqOl86QVuZKP0oj1gDJ7L0KhV0ahXjSNdosxVZ0Ak56L+dNLnQNOg/hCtTo0OjAFxEMFf6SIKY1NHWrGzVP5+wX68uy5fHh+PflytimUAnDEGCnQQsBSpeJInBW0an5Nmj72nFDqJKwV4CnkCd5NHRwhlqtfT5+zpcr7vxxTwQRDEvV4v7vV6XKv45eXleHFxMbavkfVZzw4A7SytGdC3DfKAAf27AN/Az/T0dOX3fu/37nZd12UqAHAYY0wIpxoEU++J4/ojSZJ8WF25sojTpy+Wua25gTFgcvtWTB3cD16tIYo4hBBoNmuo1m7BqJEAhBDo9EKsdgN0euFI5vzCykfKWqZcP5hnE8qpdJU2KtQzSQIzU0384vsfwEd/+j5MqDH3PLjTeeZ5cNfquwjuV+dX8Nf/eBTfe/F0AktGVLuKM3PcWfqNdkB6XzvRJZ0AfVymlqiVQw71qod6rZp6G5w8tn6xjHTAg+48IInX55LE6X2Y8zfXQp8oA3gQw1/tIvSjtF+CzkL2R1Dycb0enxwfj85AAUQDnsI8R8WnVD7S8DFqnJZRcC0EPlXwVh3a5J6n6AcCXteZt6Id/dZvl9Mr2ynAxxr6vV7PqPjFxcV4YWEh4uRlOmv4DN0RWA/I327vemBIXaTCoJ/4ejzNy/wZ3b4e0ohhaWkpPHHixOKjjz46C5iOFrSgr1ZbP4ii8KzvTzwjhLNVP6T27NmGmzdXMT+/PNJxhQCWry1g5foipua2YetdB+CZZXL7XMY1XGHGgLFGBWONCrgCfqvjo+OHfRYNKRn6AXOYCvoWWwPQc7PYUC+qp7hDcGDXDP67Dz6Epx+/Sy7LmgP3rJk+DXc63l4E9/mlNv72H3+Ib/3gODiXL7Rherzb0XPf5RvsmCDe8WCA9c53Pe7Npcu9nBbHQbzpBRzmoF6RU9+oOV5Pf5MdBnkNhNBz5xkYuHwTHfRUN5lBn5PQ25Ae+bq87ADIc4+DCMFqFyFdSpp0HKCunX7eSzjn3LaC4Lp8fmIietnzeJtLfz4NeDMOTgFPwGLGtEGgT7ZzoU/zkHHyDPgtZa/7Biadkyl0Og1IzZ2PSRsGAl73FoyZQI2/B0EQB0HA1bi76PV6vNPp8Ha7zVdXV1XzUn8Io34GhXUHPIDbq+QBA5mNVsd3nAJfz8/ExIT3h3/4h/fVarWKrebVLWEAc31//NEwrD8GMI8xhjjmePHFk2i1umVubd/AGMP0zlnMHNpHYJ+bc83HMkH91LkQaHcDtLoBur2wCKVrP1Cp6rIZsvwdBeh57SiqJ1+t68AY8Ni9e/DLH3oIj9yzO+WvkZitCeTXAPeV1S7+5ps/xNe/cwxhLM3VcszcWrEupeLTcYAdl+580HH8Rr2CRr2KesUzL6gxbYal3vX5MXqu6jw1jBXMM8qd5lfbPIzgr/QQ+sliTxTq/ZX84L8LxoTfaETHm83ovLqhRrWTG1ykHmlHIBUPa9qcBW8Dfq3KdR4C9ZimW4qdqnwN+JSC12Z5XZeCeorxAIYah88z06+srMQLCwuDzPTrotj73Ic1h80I+XcCxG9V+1A278c+9rHt73//+3crxpvxeXVLGBT0OXfHut2xJ+O4chgA8/0QL7xwAr7fbxW68oExhonZacwc2IfazHRyFmXDGn++MRdo9wJ0uiG6flg49S/3QKWP3QfkqZ1BFZYEutkdBurZiIrr4pmfOoxPfvAh7JvbkoG7PYZuwz3lyIn+cG91fHzpn17EV779Mnw/zEBcw5qCHiw9Lm/SjNOeSDnjAcyY4hv1Kmo1D67V+Ug6LUp9F8GdXAutuPU5mc4BFOAZ1Li9zBD7IfzVBO7muCy5XjpQ6NtxfQKv1eKzY2PBG4yJUIPdMs8DZEyemuktRZ9rwkcaRCmljoLlbamCJ+nGbE/BLiyzfBnA6zyDTPTaTK87AEEQxK1WK6KAV1Pm1stMP3QnYL0AD2wCyANDg34zAnwztQll8jmOw/7oj/7o3rm5uYm0gk8pegP9KKpu73YbT3Huzq6stPHCCydGdsQrCs2pccwc2IOxHbPaPrquYdBPXUDO2e/4cgx/8Bz84goLRHHfMpk8hVnzgN6vgBiqYzA5VsfHfvZ+fPz992N6opECWu4cdwJ3DWagHNx7QYiv/NNL+H+/8UO0uz21tj1xmDMdBgJ67WiHROEDSNbGJyqeQaBaraBRk6b4atUz8E0c8QiYmYS2qVPngc6rzoVprW6NxQs1Vk+tBwAgBKJOgN5qD3EUAanOQnJRUp2FHOjTfHnB8+LLY2PBMc/jLSiw628Ncw1trdR1HhDAULDb8NadBqLUbfBnnO6oqrehTqCfGoO3t0cBvFCOd0Xj8MpMH9ve9Ddv3ow7nQ5dHvdWwP6dA3nAgH4zwXIztWXYD8rk27t3b/33f//3H6hUKi4I3PW2Vvbq9jDGGOv1aod6vepj8/OtsR/+8I11Bz0A1Bo1zOzfg4nd28EGLJc7/M+3fIEo5uj6ITo9qfJj21N/gBIu3Za+xUYAemFysdoXAHZvn8Inn3kIH3rqbrO4CwPS09kIrDNj8Dlwp9CiIYxi/P2/vIL/+2s/wOJS25Q3r3hVCjrtSJcAmHrUa095reJd10GjWkG9VkW9VoHjqrpNe0mbTadCtkuvgJcMPxBFnwKysCwVRLmTawDOEbR66LXk+vL0WiWWANqm9MUqo+SlpYIvNxr+K7VafF030FLuBvgg6lzF6T/ilNokHYFCBV+0bXcActS8reQp/KkDXl/Ag5jlKeAV3Ps62hUBfnV1la+srAxrpl+3jsB6Ah7YfJB/F94bDPpPfOITcx/+8If3606WI59cqfF5vZ1An1Xa7dr9V692jzz//AkvXMuqc32CV/EwvXcnpvbshFuvZTPkwKN0KBK3NIMV/CBCN4jg+xF6QQ70+9UxDMhTUSWOUQboJkpkUh+6exc++cyDeOqBfSmYKXGaUeKJOTuBE0DgXjDHHQDimOOb338N/9dXn8O1hZVUW3XdKdCzxCs+q/DlMTzPQaNWRa3qol6toFrxgJQXvDTdmwce30oAACAASURBVPL0XCzFTcFP4Z6cv1Lp5nwT5U7nzItIesr7bZ8siJOA3VbsybUaTskzJvxaLXi90fDPMca4SqNQB9k26RT+Kg8n3zSOKnsg51Wz9raGto4nit6odZWPjs0Xwl5BPqbpRQpeK/c8wHPOxaBx+FarxRcXF6NYzvVd78+Gmul12DSQB7BW0L9T4F3mg7J5GWPsT/7kT+7fvXv3FBTcLcBnxul12Th2xi5fDh747ndPHl6vMfq84DgME7MzmNozh/rMNMqsj7/mlez61KxDGHH0/BA9P0IviBBG/To7/UBekJ6XuTBbeagDwI6ZcTzz5N340JP3YOfsJNTNhTEzAxm4m3Qk0ILK12+OOwAIAfzz8yfwn//u+7hw9Wa6RaSdRpmrurRapoq+VqmgXnNRr1VQr1ZQcR0IbQVQjUvM7RqiyXml5stTmIMpL34CXhWfdBpUmmp+ynwvBKJuCL/VRdiLgJQFALABToGPzDXVFye9T8DNK5XgdKPRO+66SI27628L5qacqicXMkq56yl21FRPQa7VfUbJU7N8P1O9peiNWgfyne6okh8G8EIIUdbRrtVq8aWlpTgMww03z5Nr/PaGPIA80L8L71sAd/rZvXt3/fOf//zD9Xq9qm5BZlxebTOAKno4jDFcv94a+4d/ePWuIKjc2+/erkeoNWqY2jOHiZ2zcOr1tVc4FGj7h5hz9IIIPV+O5wdBjNhMvylb9wgwT0UXdW8EmrUKfubRQ/jgU/fggUNzxCwOFMG9aJ15YPAcdx2+/9IZ/J9feRZn3rqRbj+BO42kY+2e56BW8aSzXK2CWq2SWuBGA9ZhkKBHAsu0c14C1iLgQ6SdBSWARdqULohJX1+LMILf6smlZ7kAkAwnwLpmNvDTUKfHhrme1r5w3eh8o9F73fPiVQpwvU2Bz3Kc7ogqtwGfUvgU8EiDWx8ztQY8iHrXZci4ewryGtg6rgj2xFxfCvBkudqYMVboSR+rV8hSM/3S0lK8hvfEb0rAA7gjIP/up/8H61H2ySefnPqd3/mdh13XpQBXz8a0oif3yOQJw5D/3d/9c/vyZednarXJe4ru73oFx2EY3zaNqd07Ud9aTt2vFeLpasrVFXOOIIzhhxHCMJbwj6JkYZ6+1fQDer5CT5VTQHr03j344JN3470P7VeLu2gFC+TBPb2ADVJwT8zaLJWWaYEAnnv5DP7L3/8Ax+m7D/qAvVJxUfE8VCsOqlUP9UoFrh5719Pb6DFZ2rpQrNoTh0DZ9gS29HzM9VB5kvp1h4KUF0DY8eGvdJWXfNb8b8fBuo7QxyHXMA/6Kl54XnihXu++5rrRsoI1AAjZ506NuetjpKBN0lNAF0LA2k/lgWVm1gFZ8NPV7TTsTboGtWXCTyl3CnQKeJ2moG7G4sn4u1nJTgjBbU/6IAhizjkvGodfWVnhq6urG72q3S0HPIDNB3kA74J+SECvV7lf/uVf3vnJT37yXqriLahT2Fvz6uX3888/f+ErX/muMzv70C/UahOHCm/yOoZqvYapXdsxsXMWbqMxQg3r/TdA1HRu1QJhxBGE0sQfxTHCiCOKYsQxzynbD+gqxUrcv2sGH3zyLjz9xN2YmWySMfR8uOcBdBS4+0GEbz77Gv72mz/EhauLpPnSdO25jvx4DjzHgee5qFY8eC6pTABmvrkCNASSeewUplb7zTx5ek6qvY46Lyd1PjINanlaOHohG3o9EpM+DyIEq1q188Sq4VgLAw2APfSxMwo/qUPHO054sVbrvFqpRDctsOeqd/WdGZMXypPeBjojpnsNfLWtp9jlme+5EMKkCWt8Pk/d22Z7XcaCvA19ToKwAU+d7IoAr+Be6GgXBIF4u0yXywubEvIA3gX9CJBej/J/8Ad/cNdTTz21zwJ4ah49iU996w7AtWvXVv/yL//y+Py8t2fnzkc+Xq2O7S+4zesaGGNoTo1jYm4bxma3wKmtgzkfQC5eRd/U/mULk+VOGEvg6+9IfcecI+YcPM6vc3qijvc/fhc+9NTdOLh7q1nClSrWFNyJg5wN92HmuAPA4nIbX/32S/jad15Fu+srgCuguy4814HrstzzTe8KsyOhm4CeEdUugZ4FuekUMGqRSI+NM8g0OY6vzzMP7jKviDjCtgQ7XXI2VWdJ2KtbMBD4AOC60eVqtfWK54XzBOoZsMtjmptCAW9AQqFvA96Ct22uB6yxd9oxIJDXIbXiHQF5bO0XvZwmF/LKgx7aVJ8HeD0GXwbwYRjyDXC0u+0qHsDmhTyAd0FfEszrWAccx3G+8IUvPHLo0KFZdQsc6z446kHDctL1NoQQeO65587/xV/8xbktW+4/Mjf30C9UKs09+Xd6/YPjMDSnJjC+Y6sEfjXHO3/IIPrslSsoslH9ChRmEhL2XMB1HDx4906896GDePDwTriuhkwC6KxqlxADS/LYEARLww7mOx3eurKIb3zvGH7w47OIosQxO3WmmfOwe0ginUaKaECD0SlwCdj1vHoDegX2jGon5w/dEVAdAECPxbNknD+K4bd8hK0uAmWO10MG+trmvbUxrczzzPQ6j84Hkg/qOOHVWq3zY8/zr6m/J5HUxzSYdbwBPlXxpjaVX+3ac+LNtq3qqYq31T39zovTAcQZj+TPBbvatsffzbeOj6JIUPM8Ve5lAB9F0dve0c4OmxryAGzQ30onPNzCum9Xm8qUz+RpNpvun//5nz+2b9++bVrJs0R2mGl26vYw+g2kFf/Kykr3r/7qr17/5je/Ob9nz1MPbt9+5EO12sRd2MDgOAxjWyYxvn0rxmanwSrV3HxD/SX0A1epusqaArKJU+N13HdgDkcO7cCRg3No1CsE6EgADhs2yOTrn5ZAyw6vnrqIb3z3GI6dumTauF5gT20JDUIFVtL5kOPlgpjf81R7AnwTb84R6el+oVLsZjU6AVhwT3wD0vvm+pWEvUxL5ROOE1yoVNqveZ5/XdYtx9qVUhekvhT09aUi+6ltCml1vMxUOvVNF8exF75JdQbQZyweaRN9phPQD+xaued90zXobdP8IMBrRzsK+OXl5TgIgrc14AHccZC/U+B6O489bPnc/PV63f3CF77w6OHDh3eo25CCOt1WIbNwDtl3Ll68uPjlL3/59Le+9a352dn79u/c+egHx8a2PopyHnPrFhyHYXzLFMa2z6AxMwWnZiv8/L+H4f9KykJ8QAYBuI6DA7tncOTQHO4/NIdd26eImXl4uFMzfl4daWWZhDCK8dzLZ/GN776Ki9duWi1fX7DblWnVrttPoQ9Qpzt9Pml1jxTsVR6BxBS/2kPQDYiyZta2vZ9sA+Vgn6SBXH8Rua5/ulJpve44wQoFu6w3BfpUGnLAro5hgKz39TYx0RugU7DTPGpTb1MHuoy5nm5rk721D2qK1zRX28bpDnK8nVvfsRBC2I52QggeBAEnwC8NeN/3R1nw5o4DPIDND3kAtwP02ODjrfWYw5btl9+kVatV54tf/OJj99577y51Gxx6P1h2Dr0pywoc8y5evHjzy1/+8qlvfOMbNyYnd83s3fueZyYmdr6XMWft9vQhA2MM9fE6mlun0ZyZRnVyDHDcPiVE393S5fomy53piQaOHJrDfQfncO+B7ahXK31VdwJvK41Rc7yMLAN+GlbbPfy3HxzHt587juXVbh+w512fcmAH8uFOlzRkup0MOab7ZDqg+rWRc0/iHAGEbR9h24ff7iEOIwvk+XDX5nzGbDN9klfHpafhpfMl+7zrOJ3jlcrqccbigDEmBgA+ZaIn7TMg19+Mpc31BOwGMjb0KdhVNLUE2GqXesYD1tS5om+t2nXop951OgU6/Vbq3Sj5OI5Fnhf9Ox3wAO4MyAMJWDbogzvgOMOWLZM/k8fzPOeLX/ziIw8++KB2xkuN0VOAs7SaB0nPTL9bWFhYfe6559760pe+dGF+ftXZv/+nf2Z6ev8HXLcyhdsUPM9FY3pSQX+ixDz8If52+sEQ8h3mh3Zvw5FDO3DfwTns3DaZgTSggKWgbpvUM3AneZAD9zzw03DlxjK++b1jePZHp+GHcnGXvmrd7PYDezo9F+xJQiZ/7ng70mZ62gkAA0QQIWj5CFZ7CLo+hBApszsFM3WcyzfTZ9OKYJ8FOwNj0RJjrVc9b/k0Y4i1QqfgpuZ5AnwD8T5KXufRafa0uhTkLbDD2qcw18m2l70N8Yyi199UwSPtVCeA7Pg7lJK34E7N8ka9c7KSXRRFot8Y/DsN8MAdBHlgw0CPTVz/MGXL5i3KZ+IZY+yP//iP7//ABz5Ap9fp70IzvU4vAD4YYw7nXJw6derqt771rTe//vV/nN+586ce37bt7mcqleZu3OZQH2ugOTOFxvQEahNNsFrOWP4g6BUE13GwY+s4DuzeivsOzuHufdtRq3oA6NvdCuDeB/zZlecsuBekmdYLgddOX8I/ff91vPzGeaSWPsk7R5ETNwDsMq082O2OA2NpM72t2hHFCDs+wnYAv9VFFMYZiKfhnixeQ8Fvwzu9DzCW1JW6vrmwDy8Dy684zsp513WE+lMRGuq6bUS16/IZhU/hbU2lS0FfH1+b41nakz6j8rVJ3e4AUJO9Vu55ZnzLPG86AnQMXn9rqFsq3kyR0+Z58s1zxuG5Ns/rhW4459z3/bjb7Yp3AS/DHQV5ALcS9LhF9d5JYO+Xhk996lM7f/M3f/PJWq1W08DWAFefzLK4tI68NJqn2+0Gp0+fvnr06NErzz13olqp7HlyfHz7Y7fDlG8HxhhqjRoaU+OoTY2jPjkGt14f8LY8+bdVrXjYtW0Ku7ZPYff2aezeMY2d26aMJ7wBg4Y4KDg0lPukOfaYutzRAO8Hfh0uXV/Csy+ewvdfOo3FpXbueaR37eeGDfZsmXyw07zFYLerNXAVAsIPEXZ8BB0fYSdAREzwacWOPvEJsKm5PTHTJ6rahj1QpOxFD2i/ASydcJzeolbtynFVaKgDUqkTmJt0Zil6llXyGa96+q23Sby9rK2+qhr0qaVraZzuLCBZ7MaA3OoI5I7H631ikjfAt9W7Arqw4J5ysNMKXpvn7ZXsoijirVaLv5MBD+DOgzyA9QY91rGu9ap7mHJl8/bLVzrtyJEj43/2Z3/2vu3bt8+AQJoodnsuPZg1Dc/ezgO/EEJcunRp8dix4/OnT9/cef16/MDqanTb1T0N1VoFtfGmVPqTY6iONTA+0cSu2Sns2j6NXbNT2L1jGrNbxlNg1tBgVJEzJNsASe+fpq9kAnANmf7gB+RY+w9+fBbfe/EUzl24Qc4sD+p2vDBf6wb2TFo2nkccYVdObfNbAcKubxalScNbq/Y0oIvN9BrSRaoeJE3fhyzsJej8S8Dy68DNs4yJWMPcBrr66VPYCxpHYJ2COyOqPQf4GRM9Veu67RrYWrmTq2xUuQa6yp+COd2HBHtGzdvfyuSeitNQR4F6F0II8u73jIlejcGLPMC3222j4JeXl2P7HDfqczsBD+DOhDyA9QI91qGOzQr2QXn6peelmbhms+l+8YtffPyJJ564274PFvQz8Xo7B+wg6bYTHwPAut2oduHC4vSlS8s7rl9frty4sYRr15Zw8+YqNup3zBgwNtbA1NQYpqbGMDk5hj17tuHAgTkcODCHrVsn4bgOmOOY7xQk5BmpE6PbFjiQTcsocrptyvaHexRz/PjEW/jei6fw8om31KuCy0Bd7Qs71lbfenMQvAeodi7AgwhhN0DY8xF0AmmCD0K1Pnz6nCm8KaQHwz1tvrdVfR7s6b5uAxC3gdYJzudfA7rLGuSO4zA9nk6UvDm24ziZsXjb8U6lmZO2OgAG+BTWGuiMpUz31PSuL7o9Dq9VfGb8nTEmbHN9Acx1KDTRK0UPGqcBn6fe9bZ2sKMe9Jxz3uv14jiOhV7JLgxD3mq1+MrKStzr9WzHQVj7b1vAA7hzIQ8YMmwGuI9a3zDlyuQdlKdfelFaXjwAsM985jO7fuM3fuPJqampSQpu9cmAmuVPw4P6dqx9VvQtBFzfF9O+L2biGGNxzHHjxhKuX1/CjRvL8P0QcRzL1eKiGGEYm/0wjBFFEdmOEYYRKhUPk5NNTEw0MTk5hqkpua1BnkC9qczsyQOemnTth79WkY7jgLkMDnOked0yF5vysqCBO1OwLoJ76ljITwOAsxdu4NkXT+G5l8+i1ekiFQZAPZ1S1CEoAjvdyOkQCAERxYi0yb0rTe5hL0DU961+SdBgTuDuqOtuj68Xm+8p+O172md6HBeie57zxWNxvHDWcRITPIF5Cvb2+LtKZzme9RmlTsbrhRWn22avU6/Bzuk+CNw1hKx89AUzmQVyYL2YhnYCbLhr5Z7TCUiZ7fV4vJ5Kp1a4M7AX1vg751z0er2UB30cx5wuVbu8vBz7vj9oHjwGpN/RgAdwZ0MeMFR4F+y3Hux58ZiYmPD+9E//9KEPfOADDzmO4+ZAOTXtLifNgN1KszsDLC8v56j5vpgKQ0xxjnoKmAUKTLVn4IplWXgn6fnLl6bLJun5dTOWhn0mbiS4J6p9cbmNZ390Gs++eBKXry/BhH5QV1+Fat3a7Q92VVHMEQcRIj9EFESI/UBu+xGiIJTr9a/Dc0jfE3kdk20gC3c6vt5f1dPyAGNMcN67FMfLJ6Po+huA39aQ1lDO+dhmeFZGwdvj8Bru1gtpCuEOpD3ubeCrbTpnXojkZTWZdeupSZ50EDLAt4BuL4yTAbwQcpxdp4VhaMBvO9jp8XftYKfh3m63uTbRDwH4Wwb7zQJ4AHc+5IE0QHI+6JN2KwE9arkyeQfl6ZdelDZMvB2Hp556aurzn//8+/bt27fbBj3d7gd2vZ1THjTOStfJLI5RCwI+GUWYjGPU8uA+aNESOjZrw53mT6enyxbB3V4trajuVLpZu12V0/lVOlh6jrsfRDh67ByeffEUXj9zWcJTAFmgk7ghoA4AIuZKgUeIYw4eRuARRxxF4FGMOIrBgwiRAvt6QbxM0NAeBPesys+/DxqOnHcvx/HyG0Fw9Q0huislwG6PwxvY03nvRLEbgNvAz4N7TlwK5kBiogdR7jpOiGScXvk0UHVPVbkg20bJa2Az4mFvA11vU/UOQKv0FPD12Lyt3oMgENo0rxifGn+P45h3u9243W7HYRjy1dVV3ul0xBpXssMaym4qwANvE8gDuaCHtf8u2NcX7Hlx+O3f/u39n/70px/fvn37rA1r+x4Vpdvw1t92Z0BnyCsbx6iFoZgIQzEhBKtm4Jl5kOv04eCezqfT8+seCu45ddP2USvD8nIbR4++gaNHT+AHL7yBXi8AjCqVwwPQQwRgaj9Z414IAcEFwIXZFoJDzX5W+wIi5uAxB5cvCsFmfXRo0NPr2G+KXJ6qZ4yB897lKFo60etdOh7HrWVtcnddlzmOA9d1+0GeAYn5vsCTPtMR6Ad4DWLHcRiFcvLnkr/KXRHcNYw0qAFkFsfJU/Lqk1HwND+FuwK6WcHOUvNCK3dbvdve81EUCW2e1+Pv1MEuCAK+tLRke9ADawD2sOU3G+ABvH0gr0MO7Ef94BaXKZN3UJ5+6UVpa43Pywd7+7d+67f2f/azn31ibm5uB83bD/wDwJ2Xx56Dz3L2WRyLahiK8SjCGOeoph/2aXjaD/r1hvvoQwQJqDgXOH78PF544QSef/4ETp58K+WM9m5Irm/eOL2+vjmqXnDeveT7N9/odC68HkXLS67rMtd1jQq3wG7G08t85LHyYe+6LkBM9GSfQUGWmu9z4J9Ko3FQY+y0Q6DVOmPGJA+dR6cRhZ9ytMtT+MgxyVsKXo+1p9Io0EXiXIcy6j0IAjHAwW49FfrAspsR8MDbEPLAmkCPW1ymTN5BefqlF6WtR7wdNygPALBf//Vf3/e5z33uid27d+9iOY53sMCs8sDORzKZN93ZeRz9BE8qNcV0XiHgRREaUSQacSzqAHP0w5/C1oY7VXt5cO+31OngIYLi15Pqz8LCMo4efQPPP38CP/zhSayudvBuGBzoOH3SUUqurxBxKwiWT/v+wulW660znHd7nudlAK3iygLdONnlKXqWmOqpcs8AXn2n0nR+xjLmeSABuUmz44qUvMhOmUtNsdNe9RTsGu46vw13De68NJGMsxtVH0URp0Grd9t7Xqt3tYytWFpain3f30iTfKbsZgU88DaFPDAU6FEy37tgHy0PALBnnnlm5td+7dceeOyxx+5rNBpj6uHkCOnoQ6fbpe6dDnrbzmN9w44riNf7ThyjGseiHseizjkqFN795kQPWu3MBrkdl9eRoHXHcYxjx97E0aNSrZ89exnvhtGC7oSpexFHUft8r7dwutW6eKrVunTVdR3ueR6jgLb3Fej7QV6b8qEALgAJ7Bz1TgFvOgEqv6DfugOg6wLSznW2umc5ql57yOsOgR2Xp9xBgKbBrDsDVryBtq43D+gK5FDj7zGFu5CmfBDlbpzrqHoPw1DQ6XEF5vlRAD5s/lTZzQx44G0MeaAv6FEQvx5gX698/dKL0tYj3o4bJU/RPprNpvu7v/u7hz/60Y8+eODAgQOO47gg4EUW3HkQz51HT+CdKlPQaciofSHgcI56HIsa56gKIYcDBsN77XB3HIarV2/i6NETeOGFN/CjH51Ct+vj3bD2EMf+Yrd781Srdfnk4uKpM5z7PcdxmOu6wvM8R4NXq3XXdel+LtA9zzNj8grkGtYZE75W5NrEDyTw1/EKuIyCW4M+ZxtAdvw9D/D2tq3kSXxqOVvdCSB5uO4E2NAvAjvn3FbzRrnHcZzsWKb5orH3MAyp9zwvYZ6/pcDf7HDX4W0NeSD1gAfeBXtZaK8F5GX2AYA9+OCD47/yK79y+PHHHz904MCB/dVqtQ5koZzzDSsOSF6ck0lj2SGA1O/C0U9VK10IuJyjyrnwhEBFCLg23PPM+Ml+8fj+wsIyzp69jDNnLuH0afm5cmUB74a1BsGDoHO511t6c3X16k8WF8+da7WuLSNRw6xarRrlXalUDOQrlUpGmVcqlTz1zlzXNWP0Ntht9U7zAlnYA0ipe52HAl9tM2ZNjbNM97mOeMQUb/IoRQ7GUtPnNAxS+yTdwJ2CnaYRbqfgrsfZiXoXWrX7vs85mSevTfO2eife89zyngfWGeCD8t8pgAfeAZDXgYJjwAfrmG9QnlHT1yO+TNx67helAZDvr//c5z63+0Mf+tDhI0eOHN62bdt2lr9qHvS23QFAAvyBnQIL/LllrE4EA+BwDg+AxzlcAK5OpnCnZn4hBC5dmseZM5cN1M+cuYTlZXt9+HfDKEGIuOf7q+c7ncU3V1Yuvzk/f+p8GHaoCUTY34wxVCoV5rouFNgZUfYa4nQ8PrVPTPgpyFNlDwXuoji1bUCuLQG6I8AsZU9BXmSi199FaRTcFP4a2naeHDN9auxd51WKnev6CNgTSU/grsfZ9aI2Gu4a6to8b4+9l1TvGJC+ZuDfSYAH3kGQB/qCHgXx74J9/WA+1PbevXvrH/zgB2cffPDB2YMHD87u3LlzdnZ2dvv4+PgUVeHWdp6aN9t9wG+gnwN23SnI7QAIAVcIOABzfT9gb755lZ05c9k5d+4yzpy5jDffvALfD/FuWHvgPOqEYeeq77cud7s3rywvXzi/uHjushCcPogBZMCeFydc12We50GrdQ1yG+o58bmQVwBOjc0T9S6oWd+OV9sAEnWvtzWobdjTeijQ7X0NZNIRNZBW6aJfnOoAGHirTgE45ykVT8CuyxjBrpW8nhKn4R5FkVBm+YxpPggCocfec9R7GVAPSh8q350Gdx3eUZAH0lDAu2BfL7CXBfiw0M/E7d27t/b000/PPvzww9sPHz68fdeuXTtmZ2dnJyYmppE/pS4P2n3BboMcAHq9XrfT6bTb7XZ7dXW1vby83F5aWmovLCy0r1692jp69Oj1b3/72/NxHGNycvfWycndO5vNrbvq9cld1er4Ls+rzQLGevBu6BOk2Ote8/3WlV5v+UqnM395efniFWp211lztoeGfaVS0aZ6A3d7n5rp7Xhrm9ngJuPyVMnreANwIB/qumOgwU3N9kAa6FqZ22k2vIvUO4U9VfScc+E4jpkCp9M12GmHgIDdqHwShAV2rk3zetU6appXL5wxU/z6fNaa3jffnQp44B0IeR36qPqNAPsoacPEjxq3lv2yaaNu29+puJ07d9be9773bZ2Zmak1Go1Ko9Hw6vV6pV6ve/V6vVKr1bxqtVqp1WperVarVCqVSrVa9SoyeHEci5WVlfbNmzfbN27caF+5cqX91ltvtc+dO9c6efJkVy2RqY+Z16a+cZ5X86an989NTMztqtUmZ6vV5oznNbZ6Xm3GdatTVpl3QBA8jsOlKOothmF3MQjai73e0vWVlSuXl5cvXOc8og92jLA9VBxjTHiel4K9rfAJzJnruoKo+lSaAnGqM8CIWZ4qfiABe57Jnprmabxuty5rKXyTj5raCfBhAd6Mz3POuYa5BrnuEBAVb8pQqtM4G+5RFGnP+hTcwzAU3W43pnAvYZpHQfwtAf6dDHjgHQx5IAN6kO13wb520I+SDyXSy+a/Fd9l04badt2qOzExN9Nsbttar0/N1GrjWyuVsZlKpb7VdavTjuONMeZUcAcFIXjAedSKIn8pirqLQdBZ8P3VxV5vaaHdnl9sta4tKZDbD6AiePdLG1nF22kU9nplOz1+T2CegrxW95CATpnyLRM+A2CnAQTsVOVrYFN1r/NSqNM0W9Hb+bhatU6nU7O6NucTmGvgp+JUHjoOL/JUOwBoL3oKd/mCqIjbcPd9H77v83a7HUdRVBbK/dLWlH6nw12HdzTkdVijqu+XPkraMPGjxg2bZz3SRt3uFzdKnlG+y6aVSR8mzexXKo1qo7FlrFqdGKtWx8YrlcZYpdIY87zamOvWxly3Oua6lTHGnCpjjscYc9W3B8hvxhwPYB5jzEU2CCFErT+vQwAADEJJREFUDPBICBEJIWIheASISAiuPiISIg7iOGzHcdCOIr8dRX47DLvtKOq0fb/V8v3Vdq+33ImiHnVEyHvIDIobFvB0e1TYp1SwMuMbpW4rehVnq/1c6NsQ12P5dpxtjtfbRWb6vDF5sm3mvttp1Dyvx9YBZDzt6bi76iDAgrtW7qYTkKfaNdiVcx2n4+5BEIgcuOv7MQy0+6WVVfDqMrx9wPj/t3d224nrMBRWpoG+/8sOnQLNXDRKHdeW9eckgPZaZ1naEszMufAXOQEC8rOEUz1V19QkvtbT9ICiJu3T1CnPc+XWJHVpTeo168MwDG9v57c/f05vAAD3++ftfv/k/a7rWq3No1anAN7KtbEG9tMwDDCOI5zP5yGZ3FPo41S/wB5muKefhcce+Jnk8YdpVmBvTfJpD0D9u+rz4/sc4ujhJJ/W0mk9zdMn6PE++jzpL/faEfYU3PHraBHu80fkNGCv+WrgPxPcUQH5TMRUDxX/yGCXgrwH6CWxh2dZtT3WmJPXPE5N01cSd7Pgwr3kcQFfq3WBPQAAPomPR/kp6OEb7JD8N2SAxxiyqX4iJvkV4NOn7lOQ147v8yl9+Hkob5nQswfwIAV6Bnh88G550A4frsM8v9d+v9+/hmGYSnD/+Pj4+vv37/12u+G/yQr2ms8G/jMCHiAgX1QCeoAAO7fmEXt4mlXb4xmXconHqfUUtZF4TPTS6b4b7HFiP5/Pf8ZxRLivnrwHgBT4C7yT2grQySS/XDAAAE7uKcDTb8hb4I1/N2qix3vn+D5ZvgJ6PsGnOd5nH4Zh9aT8MAyAcC8dyeNH4a7Xa+1p+ZLXHfjPCndUQJ5QYaqHLH9WsG8Bek29th4F7lqYS/OWL+3RqrV5bDHRS2JX2APANI7jcD6fl4fzAGD5MZtxHBeQVyb41RF9egFATPK/HrYbx7Hoz5P5NMe/JvwkXmrJcftUWfEnYwE/547+9XqdrtfrMr0j3C+Xy/3j4+Nrvt+O//9aUC55rsB/drijAvIMKY7wJT63l/NaaU/r9bVe71jqaXqo1cvjxpq85nFqll6UZKOwwL3keQA+jbUXANV1mL/V7nQ6DefzGcZxHAB+vnBnjiG9EABYH9nje0ACbnyP9IG7cRwhg3lxcsf+1E+Bnh/n4/H6/X7HKX+Z1tP8er1CUvv6/PycEOwz3AHvtc9P3FvhXvJM/qsAHiAgzxbjCF/iW3o1PVTuUWvFmjrl1VYL1LnwtsDdC+xesPcUtZFwod8CvhT+GvCbYI/r29vbcDqd4P39HR/ISyf39AJgBW+A9fSewj6NswfxBgLqX6UagjyFOIIc4Af41+sVMsAvQL/dvr/LAKf2+SNxMP+CnAfIuR7bfyW4owLyQmVTPSTxI4G9F8x7AF0CeY9V4nF8KubkNY/ypT0acTYOy0QvyXtM9C6wB/j+6tpxHGEcx+H9/R2/7nbCab80tafgzr/wpvaxOfzz0gfsILsgQIiXAI/QxqN5BPrsrwA/DMN0uVymy+Wy/MBM8m+ugTfPNwP+K8IdFZBXinm/vuQ9Mti1EH8EyGu8Wr2Va8FOAVsKc0m/dJOQwL3kawFP1TjwLnlW6P/y0s/Yz0f7cDqdBuzBe/kA6/vttYkd/RLYEeroJf7yPnPPBABwu92WiR1fg5P6/X6f/v37tzw9P4OTC/RW3g34rwx4gIC8WZX79eDo9cy5NW2sqdfWI8G9Bfw85uTcHsrn1nuI2kgk0G8BPve0sRX2rV7RhcD8AzjLU/rJT9wuEz8A4DfyLX9uNsmvvLyOk3rq45SO8TRN+CtwgA/TJffVS0BNYy3MtT3ka18d7qiAvINm0ANsB3YLvDXQloBbA/QtIS+ttTxurMm1vrZPIs7GIYF7rUbltTjNt4K9Za3W8ONuybfq4XfgLx+pSx/EQw/g5776NE0wJZq/uOZrmib4+v41uFWt8vfyAHwr1/YE3AkF5B1lhL2mh8o9apJYU6c8TQ+1etSssSaveZwaR9TrrZuDBPIcjwN1Tnw02EtrLU9S94I6VdsE+AH3sgLyHSSAvaZHknNr3rGH11o1wPaAuwbmXmBvAd0KfA9RGwoX+i3gWwCfxlqwU7UtIF+qeYGdE/cGfJ6TPQF3WgH5jjLA3pJ71Fqxpi71eq7cmqSex5JaKa95lC/t8RBnA+HCveRJco94D9h71npcBGj7pL2i9wu48xSQ30AV2PfMuTWPWFOvrUeBuxToGuiXconHqVl6a5JsGBK4l3wt4KkaBepSXQJ0Tg8X4JweCbhbdW/4Wy4GyAuAgLtMAfmNxfjoHZVrezV94NRLeZoezSqtcWIK2BLQc3son1vfQtSGIoG+F/D3gL3naoF8yZP29oY/Ow+46xSQ30mNL9Whco+aNtbUKU/Tw1m1Pdp6HnvkNY/ypT0e4mwgFriXPC/A1+Ijwd4b8iXPGncFfsDdpoD8ziK+LpfKuTXpayTv4+HV1h5Q58JbCnRP0Es8Ts2jHyXdKKh+C/Q1gE/zR4O9R6035GuxGvABdj8F5A8ixX37WqyBfivu5XFXDcz3gnsv0FM+t95brc3EAveSx4E6J/aAfS/oe9Z6g1/bt+QBd38F5A8o4ig/jXvAXAJqKcgtkNes0pq2Lq2VconHqUl6LOJuHhK4l3xLro33hn2PC4AeYOfEzVrAvZ8C8gdW5Si/Fh8B6JKp/Ihw5wB9a9BTfqvm9RrNJkG9RgL9PQCfxlbYS3u9LgSskG/VtVBf4gD7NgrIP4iY07137OFpejSrtNbyNLGkJvUon1vvrdZmIoF7ydcCnqpRMG7VLdD3WL0hX/I845jad1BA/sHEuHcviTV1qVdbe0J9a7hLQN/q5/itmqZPI+7mIZ3qrRO9FPC1+Oiw96hpIc/pXfIA+34KyD+whMf5rbiXV1t7QN0C995Tu3aqp/xWraXWay2bg2aq9wC+F+DT+Kiw7zXlu033Aff9FZB/AhWme8jW3lM9p7bF6uVpYk0u8Shf2tNLGrBTtdb0nns9AJ/GWth7Q7/HBUCA/UkVkH8yEcD3AnqPKd0T6lvD/egTfC/oSzYOK9xLniS3wL8H7K3Q3xvyv7wA+3EVkH9iGYAv9TQ9llVaa3mcOue13FziUT63voe0U7034PN8qym+5Gmh730h4OIF2B9DAfkXkfBIX+ppejSrR01Sp2JJTdKj8aU9PcTZSI400R8Z9h6rR+1XPcD+eArIv6Ay4OPqCXlNb2m19rT6rHGr1uqnPMpv1Sy9HEk3DSnca34PwNdq3rDfGvputQD7Yysg/+ISAJ9T075G+n6t17Q8z9gj1/rc+tayTPU9Jnqv6Z5bPwLsTbUA+/MoIB9aqfCxvNra60h+K7h7T+0asHvDXtrnKQvYazUP4HsBPo09Ye8NfXVPgP05FZAPVVWZ8nF9Fbh7gl7iUX6r5tFfk3TDaPVbJnpL7h17T++SXi7cV70B9ddQQD7EFuPhPc9V29PyrLGkVsolHqfGqW+tox3X57kF/lvD3nUNsL+eAvIhtQRH+5zV2tvypL1eNQ+PU9P0eYm7iUiP62v+XoBP44eAfUA9FJAPuahxtE+t1p6WJ6nnsaXW6qc8a42S5QJAs1n0PK4veUc9si957tAPqIdyBeRDXZRAH6DPdK/xrLFHLvE4NUnPVvI+ri/5VuBLp/tDwj6gHmopIB/aTIJpX1preZ6xR17zKL9Vs/RaJNlAeh3Xl7xnOrJf4oB6SKqAfGg3FaCfxt5w73lUz8klHqfGqXu9zrJJeB7Xl3zPfM8j+19eAD3koYB86FBigl/jaWJJrZRbPU5N0rOFuBuKFe4lb2/Ap7HIC6CHeikgHzq8Gvf3W541ltRKucSjfG5d26vVIx7X57kF/uJ6AD20pQLyoYdVY+qvxVyAW0Bv9Tg1Tp2j2nt4bQy9j+tL3h7359N48QLoob0VkA89lSpTPzeW1DR5zaP8Vk3T11OcDeUoE70b4APmoaMqIB96GTEvACQ1Ti7xKJ9bt/ZLJN08vOBe8qXA1wA+YB56OAXkQyEgLwA88ppH+a0ap763LFO953F97lX7A+KhZ1NAPhRiKLsIANh2gn+U43rrk/VUTTvRxyQeemkF5EMhRzEuBrQ+t+71mpY8j+pbddb9+AB4KPRbAflQaEcVLgpWZc5beP1dOsj0EF5AOxSyKyAfCj2RGhcNXRVQDoWOp/+0nvYAkgcC3gAAAABJRU5ErkJgglBLAQIUAAoAAAAIACBebEYn4Wr3gwEAAB8DAAAiAAAAAAAAAAAAAAAAAAAAAABub3JtYXRpdmUtdHlwZXMtd2ViQXBwbGljYXRpb24ueW1sUEsBAhQACgAAAAAAIF5sRgAAAAAAAAAAAAAAAAcAAAAAAAAAAAAQAAAAwwEAAGltYWdlcy9QSwECFAAKAAAAAAAgXmxGBKV7cdtvAgDbbwIAEgAAAAAAAAAAAAAAAADoAQAAaW1hZ2VzL25ldHdvcmsucG5nUEsFBgAAAAADAAMAxQAAAPNxAgAAAA=="} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/command b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/commandTrial b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/contentMD5.txt
new file mode 100644
index 0000000000..24d4e9ac79
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/contentMD5.txt
@@ -0,0 +1 @@
+MDBkNmI2OWIxYzhkMDg3ODgzZGFjYTk3NWEzYzAyMDY= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/headers b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/results b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/toExec b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/07_NormativeTypeCI-webApplication/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/body.txt b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/body.txt
new file mode 100644
index 0000000000..e61be1905d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-DBMS.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"relational_db.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/relational_db.png"}],"payloadData":"UEsDBAoAAAAIAEFebEb5Akzo4gEAAHYEAAAYAAAAbm9ybWF0aXZlLXR5cGVzLURCTVMueW1slVNNb9swDL37VxA9L26K3XwY0KUFeljXDc5tGATGYhIBtuSJTIL8+1GqnThNN2z2xaD4PvRIS+AGjaW1805c8Gz2FFk/KpB8xK7rWzJH7FpzZ+b6Huz8Y1EIaR2FjMeOhuaZD7FDcXuaybEnnj18fq7PnbiTbYgVLF/qxT0sF+eTk+ZdOdc3Kczqr/ff6qeXZVFY4ia6XnLHInhB5xlkS3DSg6wH53uUhfoOUbgqAGZw876/GIJU72je/A3EYS0HjLQIquDJ/5Gh8MGSyaDkIrOVqcZlCibVAHDFErERjTDuKJcsRVWzZh1DV13A6rfSQ/8kn0+5BLDUeF6DTlqQ8BCpj8QKY8CUmGuw1aKOQKHYfoD6+xd4QMEVMsEzetxQp+1QH1lnBePNoRn1IURginvXUJmFBTdcDRZckwzduk5p+PasY+yq7P0md/Ux9BTF0QllVx2bNBjTI/MhRDueQJ5yBRqYG+DpifRr5yLZCtbYMp3qF7GkbUmkMJLCWq2nao7n4g6Dh7Q+b6WdF9pQ/H/tRHYlBwfXttA6DdfrnLMlq/EDepupiYUzZ4M9rlzrpkFtA1/5e12WaXc5/DAT09eZp2ePrbNmsrLw42Jjh7X4OUDI2z5oHP/gYIQ+DpCi+A1QSwMECgAAAAAAQV5sRgAAAAAAAAAAAAAAAAcAAABpbWFnZXMvUEsDBAoAAAAAAEFebEaIQQwqjcoAAI3KAAAYAAAAaW1hZ2VzL3JlbGF0aW9uYWxfZGIucG5niVBORw0KGgoAAAANSUhEUgAAAdAAAAIACAYAAAAyiz2yAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAiZAAAImQBG3pPcAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAVdEVYdFRpdGxlAGRhdGFiYXNlIHN5bWJvbE6hJLoAAAANdEVYdEF1dGhvcgByZzEwMjTI8fmmAAAALXRFWHREZXNjcmlwdGlvbgBkYXRhYmFzZSBzeW1ib2wgaW4gbWV0YWxsaWMgc3R5bGXJwWuNAAAAIXRFWHRDcmVhdGlvbiBUaW1lADIwMTAtMTEtMDhUMjI6MDg6NDOCDKX8AAAARHRFWHRTb3VyY2UAaHR0cDovL29wZW5jbGlwYXJ0Lm9yZy9kZXRhaWwvOTQ3MjMvZGF0YWJhc2Utc3ltYm9sLWJ5LXJnMTAyNMR2K2UAAABJdEVYdENvcHlyaWdodABQdWJsaWMgRG9tYWluIGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL3B1YmxpY2RvbWFpbi9Zw/7KAAAgAElEQVR4nOy9eZAbV37n+c1M3EAVUCeLZPFSkZQokhKlEk9JLalFuSVZVNvdttRD2+twzGzM7kZsbOzujP3HTsTOxh4Ru7M7Extr72G7Qz221U25O7rbaqlbUlMXJVIkJZ7iKR6iSFQVi1fdVShcuX8kMpEJJFBAFpCZAL6fCEQVHl5mvspEvU/+3pWCLMsghBiJx+MigDbdq72C34MAPAUvb4VppdIBIF3wSqm/y7JsSJdlpIHCNMP7tCxjDpCnAEwBmAQwJcuYAuTJXNqULGNSl2fqvvvuy9bmzBLSPAgUKGlW4vG4AKADQK/JqwdAFKWFGHagyBVT+H+rvC1MqySPMc38PQDIM8gJV5FtXriyjAkAtwH5FoBbAG7JMnK/y2Pr169nJUOaEgqUNBTxeDwMcyGavbqRj+KaglqIUydFC3nyaRXmSQO4A0WmOrEq72GQLW5t2PDADAhpEChQ4hri8bgPwAoAKwGsMnktBRByrIAOUizF4v/bysRZe7kaD2su1/J5lLTc+1kAI4D8DYBvZBnfAOpLvi7LuLFp08YkCHEBFCixjXg83obSclwFoA+A6FgBXchC4rTSLFs6T/nt6izOSvNkAdwE5EK5qu+vP/TQ5ikQYgMUKKkp8Xg8BmADgAdzP9ciL8gOB4vWUDglzuqabktvVwdxltyuMI8sy2PQxIrLgHxelnEOwPktWx4eL9wjIVahQIkl4vH4EuQlqf7cAKWZlVjASrNs5XmMaXXs89Qf1UIeJc2KXIvPn2meEQDnc69zAM7Lsnzu0UcfGQUhVUKBkpLkRrGuhFGS6k9GkzWiXuK0ebCQviQW8ihp1UegFYuzcEeFaWOyLGtSBXBOlnEekK8/9tggK0liCgVKAADxeDwA4GEAgwAeA/AQgAfg8ukcjQzFmU+zS5wWzucMgAuyjNOA/AWAYwBObd36WKKwhKT1oEBbkHg87ociyMeQF+ZGNNmUD7fiphG0Zmn1EqeV/kwHxWmalnufBnAWwBcAjskyvgDk09u3b5svLD1pbijQJic3NeQh5EU5CGATlFVviI1QnKXSrInTyT5jk7SULOMMgGO6SPX0jh3bOeWmiaFAm4hcn+WDAB5HXpabQVk6ipumnpilWRGnlUE+TSrOctchBeBLaFEqDgLyuV27drLSbRIo0AZGJ8ync6+noCxRR1wAxVl6u4WbZRtPnBWez9sAPgbwESB/BODc44/vYiXcoFCgDUY8Hn8QwDOgMF2Lmyr1SvJYaZY1S7Mi1xqNoHVUrou8WbkNyB8D+EiW8eGTTz5+rugPIa6FAnU5OWE+jbwwe50sDykNxWn23jzN7eJ0MMq/JctqhIqPvvWtJyhUF0OBuox4PL4awAtQokwKswFwkzitNMtWn0dJozgrvw6LiOBvQWny/RCQf/PUU9+6BuIaKFCHyT13chuAPQBehjJCljQArSDOhaXofnHWK4I3S7Oh6fsMgDcB+VeyjKPPPPMUn9PqIBSoA+QeyfUcFGm+BEaZDUWD9K2Z5Cm9HcVZ+XWoVwS/cJ6itFuA/JYs41cAfvvtbz/NR8HZDAVqE/F4vB+KLF+G0jwbcLZEpFoaXZw1qLArzFM/cdYrgq8kj4vEaZYnAcgfyjLeBPDWs88+Ey/cA6k9FGidyE0xeRT5ptlHnC0RsYqb+9bK51HSKM7ans/q8yhp9boOJc7nCQBvAvgVgOO7d3+bFX0doEBrTDwefwDAfwLgj6AsxE4alNYTp71ybeCpJyZ5Sm9Xr+tQxc3KdQCvA/i755579gJIzaBAa0A8Hu8C8AMo4tzmcHHIImEzolke8+0oztLbWbkONkT5RwH8HYB9v/M7u+8WZSJVQYFaJLfG7IsA/jT30+dsichioTjN8phvV4tmWfM8+XJVmsfJKL/6PEqaXeIsc86TAH4N4D/KMn79/PPPcc1eC1CgVRKPx7dBiTR/AKDL4eKQGmBNnM0fDS2cZ9HRUJV58uUye186T/nt6nUdGijKvwtgH4C/e/753zlatCEpCQVaAfF4fAWAP4YizgccLg6pAWxGzKc50IxY5n2+nKXSKE5dSu2j/AsA/k6W8Q8vvvidG0U7IwYo0DLE4/HHAfzXAH4PgORwcUgNYDNiPo3irPw61CuCr+Q6ONQ8ngHwSwD/4cUXnz9YtHMCgAItIh6PewD8ARRxckBQk0Bx5tPsEme9IvjSecpvR3FWm0dLOwrgP8gyfvbSSy+kiw7YwlCgOeLxeAzAfwrgvwSwwuHikBrRjH1rleSpVxNhM4izXjciC+exdj7N0hzqgrgB4P+SZfzNnj0vjhcVoAVpeYHG4/EBAP8VgD8DEHG4OKRGUJyl0hpv6knpPOW3ozgrz1Pl+ZwG8BqA/3PPnt+9UlSgFqJlBRqPx58E8N9AWSVIdLg4pEa4qcI2S7Mizno1EVKcleZR0uyK4M3SXCLOwrQsIL8J4N+//PJLnxQVsAVoOYHG4/GnAPyvALY7XRZSO9xUYZuluUmctauwnZNrM0b5dsq1Dt/9IwD+4rvf3fNxUWGamJYRaDwe3wBFnHucLgupHYyGKs3j/mjILK3Rb1bsvA4u+V7/CpD/4vd+7+XzRX9AE9L0Ao3H430A/gcA/xycitI0UJxm783T3C7OZozym0Gci4jgMwB+CMj//e///ndvFv1BTUTTCjQej0cA/GsA/y2AsMPFITXCTeJcRAVTRR4lzU0VtnmefNkrzVMvcdYrgrfzOjTJ93oGwP8B4N9973vfnUYT0nQCzc3j/BcA/i2AJc6WhtSKJq1gDHncHumY51H2X+p96Tzlt6M4q81jTLMrgq8wz6gs498C+Nvvf//3mmoeaVMJNB6Pvwyln5PL7TUJrVDBUJw1r7BdJc5m7DOuPg8AyBdkGX/xB3/w+2+iSWgKgcbj8Q4A/w+AV50uC6kNjV7BuD3SMUtrhT7jhfOYb+cmcVo5n2ZpNoqz8P0bsoz//A//8HtjaHAaXqDxePx3oEzqXeZ0WcjiafQKhuJ0ZYVdQR7z7ew65+Z58mU3e186T/ntnJSr7v0wgD/7wz/83ntoYBpWoPF4PAjg3wH4LwAIDheHLBI3VzBur9QrqcTZjJhPs3IdGu1mpbo8+TSbbxplAP83gH/9yivfnyvM0Qg0pEDj8fhWAH8P4H6ny0IWh5srGIqzdB63RzpmabU4nxRnPq2G3+uLAP7k1Vf/4PPC3G6noQSaG2H73wH4NwA8DheHLAI2I5rlMd+uFhW2eZ58uSrN4/ZKvZI8jXazYpbWhDeNaVmW/ycA//MPfvCHDTNSt2EEGo/H1wF4HcBWp8tCrMFmxHyaFbkyGjLLo6TV62aF4qw0j5JWg/P5OSD/0Q9+8Mqlwi3dSEMINB6P7wLwFoAOp8tCqofNiPm0WlTYFGc+rV7idDLKN0uzch0a+Hs9BuClf/bPXjlUuBe34XqBxuPxlwD8I4Cg02Uh1UFx5tPsEqfbIx2ztFqcz4XzmG9HcZberhbncxHf6zkAr+zd++pbhXt0E64WaDwe/1MAfwv2dzYUbhpBa5bWJBVMwft8uarLY0xrRnE6OfWk8jzGNH6vAQBpAP9i795X/2Ph3t2CawUaj8f/HMqqQqRBoDhLpbFvzTyN4nRKnFbOp43iLOQv9u599X8rl8EpXCfQeDwuAPjfoTzsmjQAbqqwzdIarYKhOPNpdlbYrTAFqMHEqeffA/hXe/e+6iphuUqguWkqrwH4Y6fLQhbGTRW2WZqbKhi3Rzql8+TTrJzP6vMoae4XZ/XXqtFuVhbOY76dtWtVEf8A4M/27n3VNdNc3Na3+ENQnq7HTZV6JXmcrGDcHg2ZpTX6zYqd16FVv9c2i1PljwFkAfzpYnZSS1wTgcbj8X8J4P91uhykNBRnpXncL04r59MsjeIsnWble23lfFafR0mr13Wo5Hwukv9s795X/79a7tAqrhBoPB5/DMCnAPxOl4UU4yZxOlnBOFlhm+fJl73SPPUSp/sjHXePZKY4q2IewBN79776RT12Xg2OCzQej3cCOA5glaMFIUWwgjF7b57mpDitnM/q8uTT3FRhu12cVs5n+Tylt6vXdajV97oOfAPg0b17X71X7wOVw1GB5kbcvg3gBccKQYpohQrGyQrbLI3iLL2dlevQjH3G1edR0ppMnHp+A+B3nRyZ6/Qgon8DytM1tEIF43ZxWjmfpfOU385NFTbFaZan9HZWroOd32ubeAGKQ/5HJw4OOBiBxuPxZwDsByA6UgCi0egVjJMVNsWZT6vXdWjGPuPyeUpv5yZxOt39lyMLYPfeva9+6MTBnYxA/xdQno7S6BVM84rTvTcr1edR0qxcq1ZtHi+fR0lz8rvvEnGqiFBcstOJgzsSgcbj8ScAfGL7gQkAd1cwbo+GrFYwFKfZe/O0ZozyzdIozpry5N69r35q90GdikD/3KHjtjRurmAoztJ5nKywq8+jpFGcjfHdt3rOXcifQ5kKaSu2R6DxeHwDgLMABFsP3MI0xqTw5qpg3Bzlm6XV6zo02s2KWVoz3jQ2kThVZAAb9+599bydB3UiAv1XoDzrDpsR82luEqc7oqF8mpXrYOV8too4nfxeWzmfTSBOFQGKW/65rQe182TF4/GlAK4B8Nl20BaDzYj5NDsrGIrTLI/5drU4n+Z58uWqLo8xjeJsWJIAVu/d++qIXQe0OwL9l6A86wLFmU+zq4JxssKuLk8+rRbnc+E85ttRnKW3q8X5bGFxqvigOObf2nVAuwX6jM3Ha3rYt1YqjeLUp9klTif7jCvPY0zj97qpeAY2CtS2Jtx4PO4FMAEgaMsBmxyKs1Rac/atVZLHyvk0S6M4a/u9tnI+KU7LzAGI7t37asqOg9kZgT4MynPRuKnCNktrtAqG4syn2Vlht8IUIIrTEYJQXGPLk1rsFOh6G4/VdLipwjZLc1MF4/ZIp5I89TrnleRxulJ38xSget2sLJzHfDtr16HpWY8mFGiPjcdqGpyv1Mtv56YKxu3RkFlao9+s2HkdWvV7TXFWjW2usVOg3TYeq+GhOM3em6e5XZxWzqdZGsVZOs3K99rK+aw+j5JWr+tQyflsQWxzjZ0Cjdp4rIbFTeJ0soKhOPNpbqqwa3UdWvV7vXAe8+0ozqqwzTV2CnTCxmM1HK1Qwbg90jHPo+y/1PvSecpvR3FWm8eYVovzWT5P6e3qdR1q9b0m9rnGToHesfFYDUMrVDAUpzsr7Fpdh2bsM64+j5JGcTqLIAgQBOGuXcezTaAzMzMD4XDYrsO5nkavYNwe6ZiltUKf8cJ5zLdzkzitnE+zNCfFaef3mhhpb2/batexbBPo/Pz8jCpQQRBa9kvQ6BUMxenOCnvhPObb1eJ8mufJl6vSPPW6WTFLq9e1svNmhRgRBOUZJT6f15ZFFAAbBTo7Oxvv6OjQ5NlqEnVzBeP2St1qBdPoUX71eZQ0K9eh0W5WqsuTT2u0m8ZWqiNrgSAICIWCt+w6nm0ClWU5k0wm4ff7tTT1jqGZvyRurmAoztJ53B7pmKXV4nxSnPm0en2vrUb5xIiZP/x+PwRByNpVBlsXk5+fnzcIVKUZo1E2I5rlMd+uVhVM64mzdue8kuvgZJRfOk/57dz03bd6s0KKUeVZSCBg78O+bBXozMwM2traTP/4ZpAomxHzaXZWMIyGzPKYb0dxmqVZ/15bOZ8U5+IQRQGAuS/a29tsLYutAs1kMpidnUWp0biNKlGKM59mZwVDcZrlMd+uFufTPE++XNXlMaZZuQ7N+r0mpRFFseRnkUgYkiTZek7tfh4opqenSwoUaCyJshkxn2ZXBeP2SMcsrRbnc+E85ttRnKW3q8X5pDjto5w8ASAWa7epJHlsF2gqlUIikUAwWPrJZm6XaKuK08kKhuIslcd6he0mcbbq95pUhiiKZQedhsMheL1eu4tlv0ABYGpqqqxAAXdK1EoF42SFXT5P6e3cVMG4XZxWzqdZGsVZ2++1lfNJcboTURQhioLJtcjjRPQJOCTQZDJZckSuipumuDSjOJ2sYNzeRFhJHoqzdJ5W/V5TnLVHkqScC0qfx0DAj0Ag4Mi5dkSggBKFlhMooK1riGzWtmk9BtxUYZulNVoF43ZxWjmf1edR0uyssBtdnE7erNTuWpFqEAQhJ0/1GpWWaFdXp51FM+CYQOfn5zE1NYW2toWHHYuiCFmWbftSNpc461vBuL1SN0uz62alkjxOR0NuHslMcbYmgiDA41HVVP58dnZ2IBh0JvoEHBQoAExOTsLn8y0YiQL2NOm6KRqqJI+TFYzbxenMzYqS5myFTXGqafW6DpWcT2INSZIgSRLMzns+GlUIh0Po6HD2MdOOChQAxsbG0NPTkztp5amXRClOs/fmaRSnWR4lze3idPJ7beV8Vp9HSaM4GxOPxwNJEk2uTTFerwe9vT31L9QCOC7QbDaLsbExdHV1lVyeSY/aL1qLJl1WMGbvzdNaVZxOVtgUZ6V5lDQr16FW32tiHUEQ4PV6SwwWKk4TBAFLlvTmZOvsdXBcoIAyN3Rqagrt7ZUPRVb7Ra0MMGqFCsbJCtssrdFGMrtdnK3aZ2yWRnE2Lh6PBx6Pp+x5LWy67enpQiDgd8W1cIVAAWB2dhY+nw+BQACA8kVdKCJVR2pls9mKTmYrVDAUpzsrbIrTLE/p7axcBzu/12RxiKKoizorJxIJIxptd831cI1AAWB8fByxWEyTaKUsFI02egXjZIVdK3FaOZ+l85TfjuKsPI+V82mWRnGSSvH5vJAkRT3q+S1eOKe46TYSiaC3t9umUlaGqwQKABMTE5BlecGVigoxi0YbvYKhOGt1Pt0p12aM8s3S6nUd7LxZIYtHkiT4fD6tr7Oa0xyLRdHd3em6a+M6gQLKIgvZbBaRSKTqbc2iUTdVMIyGFspjTHNThW2W5iZx1utmpbo8+bRGu2l0W+XcLIiiqEWd1Zxjte+zq6sTHR0xV14fVwoUUJ4dms1mqxpYpG8OUKNRo0gBpyoYirN0HrdHOmZptTifFGc+zW03K2TxCIIAn8+nWxSheEBQuaZbQRDQ29uNtrbqAym7cK1AASCRSABAyYdwq5SqYPIilZHJpAvyAIutYNwe6VRS0bMZ0SxP6e0a/WbFLK0ZbxopTudQxOmF1+vLpVTXXAsAoggsWbIE4XDI1dfJ1QIFlCX/stksotGoQaKlKhhjHgCQIQjKcOlsNotMJlOwjZKnXJqbKmyzNDsrGEZDZnnMt6M4zdKsf6+tnE+K016UleV8UCNJ5VSXXwy+EEmSsHTpEkeX6KsU1wsUUOaJTkxMIBKJaCsWlZOpWcUAqI/FEZHJZJDJZLHYisIszUlx1rOCoTjN8phvV4vzaZ4nX67q8hjTrFyHZv1ek9rg9XoRCPghCCLMzn1h020pqfr9fvT19TjybE8rNIRAASCdTmNiYgKhUMgwV7QQs4qpMCpVRapEpOqoXYrTTRV2dXnyabU4nwvnMd+O4iy9XS3OJ8XpLtQ+Tr/fB0FY3KpAgiCgszOGzs4OAI1zzRpGoCqzs7NIpVIIh8MQRRGAsRJQOqXz71XM0kRR0ok0g2zWTKQom9aMFQzFWSqP9QrbTeJs1e81qQ2iKMLv9+empChpslx5lKl9msvv8/mwZEkPAgF/bl+Nc90aTqCAEo1OTk4iGAzB5zOG+gtVTOqvyoVXpSvC41FGg+Wbd/Of67dr1ArG7ZFOJXmsnE+zNIqztt9rK+eT4mw8JElCIBAw1Llm38NqiEbb0NPTbTIatzFoSIGqJBJzSKdTCASCEITCflEgL0jBpGIojlQVkYqQpCzS6TSyWbnhKxiKM59mZ4XdClOAKM7WQOnfDOT6JWXT72kpSkWlkiShr68XoZCyYE6jXruGFiigRKOzszMIBAK5RYmBhSpxJc1YURgjUuXpALIMZDLpXPOulrtgv/k0uyoYt0c6pfPk06xU2NXnUdLcVqm7eQpQvc75wnnMt7N2HchiEQQBfr8fgUCg6PmcxVIs2hrlBBuJhNHX16stetPINLxAAeUfKJFIwOPxaEtFqRWF/hE55aNSwZBHeWwaIEkeSJIH2WwGmUwG6bR+Goy9FYzboyGzNEZDpdNaIcpfOI/5dhSnM/h83lwzrU9Lq9Vp9vl86O7uRCQSzu238a9fUwhUJZ1WokWv12tY/aKySsfs83weZeSuBI9H1qLSTEY25KlPNOR+cVqpsM3SKM7SaVbEaeV8Vp9HSavXdajkfJLFIUkiAoEgAgE/JEnSzm/582yMMotXGMq/93gkdHV1Ihptr2C/jUVTCVQlnU4jnU7D6/VBkkQtXZaBfDepsUlCn0dJkwveK9uoUam6MINyrIzuS0FxLlac7o903D2SmeIklRAIBBAM5qPN4vNbXpILIQgCOjo60NXVoTXXNts1bEqBqqTTKWSzojZdBbmm2uIKpjhNlvVLB5ptJ0AQRHi9Pni9Sl9pOq008+pl2mjitFJhV5cnn+amCtvt4rRyPsvnKb1dva5Drb7XxDrqM5cDgQBE0XzKn3UU4QqCsvxqb2+PIaJtRppaoIDyz5fJpCHLikhzqQV58mmFUapZnvz7fJraVwooMk2l0rllA2W0sjjdHulUJsXqz2fleYxpFGf5NFI9fr8fwaAiTeNKQXLVUeVCUWk4HEJvb09uOb/mv4ZNL1AVWZaRzSoDgJS7Iu2TEl+iwibehd7n0/IylZFOZ7QmZeXL1Ph9a5XkoThL57FyPs3SnBSnnTeEpDrUEbSqNPWjXet1an0+P3p7uxGJhNGMTbWlaBmB6slms4ZVjIorFKHEeyXN+D6XavjC5CsKj0fKDWiSkUqlDTJtNHG6PdKppKJvxj7j8nlKb2clj503K6RyBEHI9WkGEQwGtDnxlZ3XwqjSuKhBuSg1HA6js7PD9U9NqRctKVAg/8VSv2jKMn7G+aCyXP59fl/lhJs/ltfrgderzFVVpsSkcyOH045FQ2Zp7q3UlTQrcm20m5Xq8uTTrFwHt0X5pDI8Ho8WZarrg6vUekCQ8TmdIqLRNnR2drRMU20pWlagegRBgMejrImrRIbGz82+G2qavjnX+MjSQpkaI1clMpUgy37IsqzJNJlMGr6MFGc+rRYVNsWZT3NblE/KUxhlqlP1Kpt2sngkSUIsFkV3d1fTjqqtFgpUhyiKEAQBmUxW6y9dTHNuObkqWYRcPmXlI6/Xi2AwiEwmg1QqhVQqhXQ6XbTf/Pt8uSrN42SFXX0eJc0ucTp5s1I6T/ntmiHKJ6Xx+bwIBkMIBoPaYusqheey+vVkK2u69Xq96Ozs0J6UYnbsVoUCNUGSxNwQb7koKjWXor6CMY84lc/1Ys1vl9uz7viStnAzICOZTCGdTmkje900ytYszU3REMVZaR4lzc4onxQjSRKCwQCCwRBCoaD2/GP1nFZ7HhezSHsgEEBnZwfa29uq7FNtHSjQMgiCAEmSkM1mtUedlat8y0echTLVb6PsJ7+NPurNP3NP/QdSZaqKtXi/FGcziLMZo3xiRG11UvsyjQu21/78LdT36fF4EYmEEI22F/WrkmIo0ApQRCrkBJaPSIsrnYUjTmOUquYp3q6UgJXmXh+8Xh/CYaUM6XQKyWRKa/bNl80ZcdaribCSit7JUbZmaRQn0RMI+HPCVF7GhQaKz2PxEnm1eOyXselWkgQEAkG0t7cjHA4X1D2kHBRolSiLzAtQO9BVmS4ccRZXxoVf1OJKSNlX8eAk44Yejzd35yroItS8VJXnm1KcrTogq14RPMVZHlEUEQgEEArlhamuiKbc5Ko32IVSLP80k1rh9/vR1hZGJBLWNRWTaqBAF4HSLyBAEGStiVdJV3PoKzR932h+H0ZJFuYpfEKM8fil9qMMSPIhFFI+y2azWnSqiDWJbO75bG4WZ71G0JrnyZerujzGNIqzNVFlqZ9Wkp/iAZif69pgFpWWGhAkSRLC4RDa2trg8XgYbS4SCrRGqOtKKk28aoQplOjjVNMK3xcKGCiMOEsJtzCPshi+kCubCL/fD7/fr+07k8kilUpqUer8vCpV6xW2WVozirNeEXz5PEpavSJ4irNyRFHU+i3Vnz6fH8bzIxd8J6qLKks9iNoKgiAgGAwgEgnD7w9QmjWEAq0xgqAXmTroSNCNYgPUf4R8XrOIU0k3PilGRnVRqf55qMZ9S5IISQogEAhq+TOZDJLJvFTT6SRSqfSClbpZmp0VNsVplsd8u/rJtTlRBvD5dU2xqiwBvSTz/7+lz0v1ixdYR5Ik+Hw+BIN+BAJBSrNOUKB1RPmHUf6plOhOiUiVBZ2LI87CyFFNKxblwlFp8Z2wcd/GNGU7ZQh9EKFQUEsz61NVR/8aF51wZzRUC7may9YsT/ntqrsRsX6zsnAe8+1aVZxerxeBgF9rpVGE6YfP58s1hQL582p2fq1Rbd9nuQFFykBHET6fD6FQMLfIgkBx1hkK1CbUf0RFOpmcLJFb0cO4WAOgRo+A9ai0eLv8fgvlaxSpPk3pU/Vpfap62St9qkrEqo9c1WZsI+4SZ+VSbD1xVnI+Gw2Px4NAwJ/rm/Tn+ikVWebXxTaTZOHfXX5JvMU2vVa3vQyPR9KkqfwdlKadUKAOokR3yopHqkwB9bmlah5AkWLpqLQwcjWToj69MILV79f84eLFeQRBWSXF51Om0+jLoi5LmB+0lB/AZLbubzOI00qzbPV5lDSK0xxJEhEIBDU56gf0qKNMjU2uRmHmWey6sfVDlhVpqpGyKKoDGZ0uWWtCgbqEvEwzOUFJEEXBICbAXJT6SrRwsftS26lylWWhoIJQp+UY+3LNo1aYpumXJVTS8hnNRgQrglV+qqODdXtesKI3Fx7FWWq7RhanOiAmEMgP4lFHvypTuRTy3QulJFnTUqHwe2Ms82Kj0nzzrNKsrO6DOA0F6kKUJQTTyGQU6QmCAFEUIUlKE01hxKlsA4yxE18AACAASURBVJhX/kq6Ua6CaWVsTC+MUo3RrL6sglA+Tf9eGe7vN6xyol+BSXk6Tf5JNcpLfZ8yPBKucH6reqzic1CdOKuTojvFWasI3g7UtaAVQXi1GzAlzfje7/cXlbOw6bXEUbC4qLK+Tbcq6j5EUYLX64HPp8zvpjTdCQXqctSRvNlsBqlUfiEHRaiSNjHbvPm28B+u1KIP6jb645oLt7yEFxJwYQRcWGalr8rj8eSm3BjLr69EBEF5BJ2+uTj/u/o+pfs9bZirmz8uxVkqbTGo8lOjJp/PVyDJvCwLnyqSL3PxknalFx8oPffRjahlU8qr3GAqC6J4KMsGggJtMBSZZrUntgDKqF6PR9IJNS8HfXRXXIkLBdIsjhyV/cNCWiXvS0vW7H3hPkRRMG3WKlxTWH2vRLJ5qeZfaa0ZOS/iTIl5sfaLc+Fm2fqKUxRFiKKoE6Iffr/PIEdVjKooixcfN66+k/+sfgsMLExhVFmtgKtvupXlDLJZWWuW9fv1LTGUZqNBgTY46qjeZDKdE4wAUZRy8zwlbfDEQpGj8T2K0tR9l0szb7rN78tsZHFx36z+bystWbMIeSEhezzKA82VrllVrsXS1ZdBfbSd/mcmk9FuYvKvLDKZtPa5+tQcde3kbDbfmgAoDydQ3+fz5Fe0Uh9goG++V0WmvCRIkgBByKepeQTBmL8wj7pPs5fxMyE35Uov+sLo0HzxACtSrOXiAaX2sdim22pRrqPSry+KYu7mQt0vaXQo0CZDnbepe4wo1KfKiKIIj8cDSZKgLrBQXVNtXq7lm2qBSiVp8hcY9rdQJGt+DkpvY9aEbSZkFUlSHnyu/k1q5affR/6mQS/jfB7jjUfxPvTbF6bptzV7pJRZ/1/xdIxCARrz6puyC+WYTytFdVFYLec+OkEpAcsycjdWGSh/j5i7iVXHLjDCbEYo0CYn34eaLWieErQIVe2HqqRZ1iytesHJOhkA+aZmoeh9Pn/+/WKi0HwZS29jRdK1waysxYO0KpFIuUhusU2VbutbrNeDpMuhDmZT96NE+YIWxZPWgAJtQfRSTSaTmJubgywrfYrqIB794A6g2qZaACgtSbNtcntCoUDMy18uoiyXXn7/5lGo8mQd4/FKCdd8/2YSrObzSnAyMlts02s9mm5rN6BI1vrJMxklulRlKQiAuqoYaU0oUKKhCnV+Pgl9U6w6QEkvV7VZamEBLhwRAgv3fxa/L5ROcUSpF1vxgKlCCVcShRrFWkqybmCxfX31GECz+P7M+qF899XR2hnD0psKgrZoAfsviQoFSsoiy1mkUkrlou8TEwTkxOqFxyPp5uopa3Aq+QqXHaxOkrlcKF1hVdOUW8n+aslCQl4oGrYWlZZvunVX02s56tP3KRieQKSKMpvNQhDEEn3WhJSGAiWWkGU5N/1D6QfSDyoqbAZW5ZpfTq38aFx1P+ZRY6FwFypn6Sh0oeh4oabihY5vR9NtfSkv4GqjzMUOKKoUdYqXOjUpnc5oza/q9dUP5HL3NSBuhgIlNUedZ6n2reoFq0StHm1KiV6wXq8nJ7l8JbqwNKuLQisfQFQ6QiyfZ6HmXOsRcCUDiqofEFTbuY/1RrlxU5aAVOfxqoJUIkp1UI9SVn0TLD1Jag0FSmxFXSlIwThvUJ1c7vF4NcGqq9cogvXl+qHMhbqYAUWlItVqRuxaiVKLy+r2qLQ8i+37lOUsEon53BrJSnNrJpPWlng0F6QxqiTELihQ4hqUOax5wZrNbRRFAZLk0eZnKtGsVLLZuHhAUekBRJX3U1Yy4MhJFtv3WbsBRbIsa9Gi8tO47KK6+EQ6nYH6mD/lmPkTalzswlUnmrQ4FChpKJQVgJIAzAWr/12WZW2eq/LTA0lSFuhWolxJmwurLjChF3P5/s1ysq18gFBl/bjW5oIasd70KsvQLeifjwbVn/qlEZWlEjPIZpUbIWU1pXyZ9eUv/J2CJI0GBUqaGv2yeoAaSZX+XZ8milIu4pVMX/rpPWqaOpFeXRJPmTOI3ChPwbBEXvnf1feCtryffv6usuxfFrKc/73wvX4Bjfzncq7PMP+kG+V3dUnCNNJpddlCZYlCWVYfMWe2EpP5ikuFqzMR0oxQoISUQBGIMmilcDBUoWxLf66Pjhf63Pz3aiI34++lPy8W4EKfE0IK4TIahBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBALUKCEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWIACJYQQQixAgRJCCCEWoEAJIYQQC1CghBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBALUKCEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWIACJYQQQixAgRJCCCEWoEAJIYQQC9gm0Lt370bsOhYhhBBSb2wT6P79+/ecOnXKrsMRQghpMa5fv46DBw8+ZNfxbBNoIBCY/PDDD/HOO+8gnU7bdVhCCCEtwNGjR/HLX/4SPp8vZdcxPXYdKBKJ3AOACxcu4M6dO/jud7+LaDRq1+EJIYQ0IfPz8/jNb36Dq1evQhAEdHR0jNt1bNsi0L6+vmEAEAQBt2/fxt///d/j6tWrdh2eEEJIk3Hnzh38wz/8A65cuQJA8UtfX989u45vm0A3bNhwPRAIAFD+yPn5efziF7/AoUOH7CoCIYSQJuH8+fP48Y9/jPHxfMDZ29uLSCQyb1cZbBOoKIpyf38/AEWgKocOHcJPf/pTTE1N2VUUQgghDUo6ncb+/fvx9ttvI5UydneuXr3a1rLYOg9U/eMEQYAo5g997do1/PCHPwRH6RJCCCnFjRs38Nprr+HEiRNamj4gW7Vqla3lsVWgGzZsQCSiTAcVBMHwhyeTSbz77rt44403MDExYWexCCGEuJhkMonf/va3+MlPfoKxsTEtXe+QJUuWYOnSpbaWy1aBSpKEwcHB/MHF4sOr0ejx48ftLBohhBAXUsoJenkCwM6dO+0sFgAHlvLbtGkTwuEwAOUESJJUlCeZTOK9997D66+/brjbIIQQ0hrMz8/j17/+Nfbt22faKqlvxVy2bJnt/Z+AAwL1eDx47LHH8gUQRdNIFFBWlfjbv/1bHD16FNls1q4iEkIIcZDLly/jr//6r0uOi5EkyRCB7tq1y66iGbBtIQU9mzZtwueff465uTkA0KJQM0mmUins378fJ0+exHPPPYeBgQFby0oIIcQe7t69i/379+PSpUsl8xS2Wvb392PlypWQZbnexSvCEYF6PB5s3boVBw4c0NIkSYIsyyVPwp07d/CTn/wEAwMDeO6559Db22tXcQkhhNSRubk5fPzxxzh27FjZ1kZJkiCKosEVTkWfgEMCBYDNmzfj7NmzuHdPWTRCEAR4vV6kUqmydxJXrlzB1atXMTg4iGeeeQahUMiuIhNCCKkh2WwWR48exYEDB5BIJMrmNevuW7NmDfr7+x2JPgEHBerxeLBnzx688cYb2okTBAE+nw/JZLLsCZFlGV988QXOnDmDb33rW9i+fTs8Hsf+FEIIIVVy4cIF/Pa3v9WCqHJIkgSPx2OITmOxGJ5//vl6FnFBHH2gdjQaxYsvvlg0HNnv95ccWKQnkUjgvffew1/91V/h/Pnz9SomIYSQGjEyMoIf/ehHeOONNyqSp8fjKQqQfD4fXn75ZajLwzqF42HbihUr8OSTT+KTTz7R0gRBgN/vRzKZRCaTWXAf9+7dw759+7Bs2TI8/fTTeOCBB4qkTAghxDlGR0fx8ccf4+zZsxU3ufp8Pq3PE8jP/XzxxRfR1dXlWNOtiuMCBYBHH30Ut2/fxsWLFwEoTbR6iVb6/NDh4WH8+Mc/Rl9fH55++mls3LiRIiWEEAcZGRnBRx99hPPnz1csPLU7T22J1G+3a9curFmzxnF5Ai4RKADs3r0bY2NjuHXrliHd7/dDEISiRYPLcfPmTezbtw9LlizBM888g02bNlGkhBBiI/F4HB9++CEuXLhQ1XaCICAQCEAQhCJJ3n///di2bZsr5Am4SKDqoKJ9+/ZhdnbW8JnP54MkSZifn6/qxI2OjmLfvn3o6enBM888g4cffriivlVCCCHWuH79Oj744AN89dVXVW8rSZLWr6mv6wVBQE9PD5577rmalbMWuEagABCJRPDqq6/in/7pnzA2NqY15QKKYCVJQiKRqKhfVM/t27fxj//4j/jggw/w9NNP45FHHuGoXUIIqSFff/013n//fe3h1tXi9/vh9XoBoChQWrZsGfbs2eO6ettdpQHQ3t6OV155Bb/61a8wPDxs+EwURYTDYSSTyQXnDJlx584d/OxnP8M777yDHTt2YMeOHWhvb69V0QkhpKXIZDI4deoUDh48iHg8bmkfkiQhGAwWLZCgNuGuXbsWzz//vGEwkVtwnUABIBAI4Hvf+x7ee+89XLp0ydB/Kcsy/H4/PB4PZmdnLa2ROz09jf379+PDDz/E5s2b8cQTTziyEDEhhDQiExMT+Oyzz3DkyBFMT09b3o/f7zdtslUZHBzEE088UfJzp3GlQAHlruTFF1/EJ598Ynh4qv7ztrY2JBIJzM/PWzpGJpPByZMncfLkSfT39+OJJ55g8y4hhJTg66+/xqeffoovv/xyUQ/4UFsTCxdHUBEEAU899RQeeughV4pTxfWm+Na3voVoNGpYN1ftGxUEAaFQCF6vF3Nzc1X3jeqJx+PYt28f3nrrLWzfvh27du1CNBqtxZ9ACCENSzqdxokTJ/Dpp59iaGho0fvz+/2GJVgLR9v6fD585zvfcc1UlXK4XqAA8PDDD6O9vR3vvPOO6ZxQr9cLr9eL+fl5zM3NLeqkT09P4/3338eHH36ITZs24bHHHsP999/P0buEkJbi9u3bOHr0KI4cOVI0M8IKXq8X4XC47INDQqEQ9uzZ0zAPC2kIgQLAfffdh7179+K9997D6Oho0eeCICAYDCIQCGB2dtbSICM92WwWp0+fxunTpxGJRPDoo49icHAQS5cuXdR+CSHErczOzuLUqZM4duwYrl+/XpN9SpKESCQCn8+nidNsjueKFSuwe/duRCIR10eeKg0jUADo6OjAK6+8gmPHjuHo0aPIZDJFCySIooi2tjYEg0HMzMwgmUwu+rjT09M4cOAADhw4gKVLl2JwcBCPPPIIIpHIovdNCCFOkslkcPHiRRw7dgznz59fVFeYHrWfMxgMAigeBKRK1Ov1YteuXdi8ebNpPjfTUAIFlJO+detWDAwM4N1338Xdu3dN83k8HsRiMSSTSUxNTdXsSzEyMoK33noLv/71r7F+/XoMDg5iw4YNHHhECGko4vE4jh8/hpMnT9akiVZFHZuiDzBKSXH58uXYvXs32traGkqcKg1b63d1dWHv3r04cuSI4SGs+sUXAKXD2u/3Y3Z2FjMzM4saOaYnm83iwoULuHDhAoLBIB566CE8/PAWrF69issGEkJcyfj4GE6ePInjx4/j9u3bNd9/MBhEW1sbJEkCgKK+TjXq9Hg82LFjBx5++GEtXyPSsAIFlIuxY8cODAwM4L333sPY2FjJfJFIBOFwGHNzc5ienq5ZRAooT1M/cuQIjhw5gkgkggcffBAbN27CffetgShKNTsOIYRUy507t3H27FmcPXvW8mIHC6GKU11JqFygsmzZMuzevRvt7e0NK06VhhaoSm9vL/bu3YvDhw/j5MmTyGazRYsvqNNewuEwwuEwEokEpqamqlqkvhKmp6dx9OhRHD16FIFAABs2bMDGjRuxbt06SFJTnG5CiMsZHh7GuXPncPbs2aIHdNQKtT5tb283fWpKIV6vF1u3bsWWLVtMBxE1Ik1To0uShMcffxybNm3CwYMHcfXqVcPn+qZddcRuKBTSRGp1MYZyJBIJnDhxAidOnIDP58P69euxceNGrF+/Hj6fv+bHI4S0JrIs4/r16zh37hzOnTtXsjWuFoiiiPb2dkQiEYM4zZpq1fwDAwPYuXNnQ42wrYSmEahKLBbDSy+9hJs3b+LQoUNF6+kWooo0mUxicnKypp3pepLJJM6cOYMzZ87A4/Fg7dq12LBhA9auXYe2tra6HJMQ0ryk02l88801nDt3HufPn1vUknqV4PF4EI1Gtfqq1FxOPStXrsTOnTvR3d1dUf5Go+kEqtLX14fvf//7+Oabb3Do0KGSo3VVfD4fenp6kE6nMTExgZmZmbpd7HQ6rQ1AAoAlS5Zg7dq1WLt2HVauXAmPh/2mhJBi7ty5g8uXL+Hy5Su4du3rmndBmeHz+RCLxbRRteXmcqosWbIEO3fuxLJly5pOmnqaVqAqq1evxurVq3Hx4kUcPnx4wbs0r9eLnp4edHd3Y3p6um7Nu3pGR0cxOjqKgwcPwuv1YvXq1Vi7dh3Wrh1AZ2dXXY9NCHEviUQCV69+jStXLuPy5cuYmJiw5biiKCISiSAajcLvV7qbzCLIQol2dnZi+/btWLNmjbZNM9P0AlV54IEHcP/99+PUqVM4fvw45ubmABRPe1FR2/mj0ag2l3RycrJm02BKkUqlcOnSJVy6dAkAEIt1YO3aAQwMDGDNmvvg8/nqenxCiHPIsoyhoSFcuXIFV65cwdDQUN3rHD3BYFBrplXrxUqOH4lE8Nhjj2HDhg0Aml+cKi0jUECR4iOPPILNmzfjxIkTOHPmTEV9nuojd3p6ejA9PY2JiYm69ZUWMj4+hi+++AJffPEFRFFEf38/Vq5chZUrV2LFihXasHFCSOORyWQwMjKCoaE44vE4rl69uuhlSKtF7duMxWKG5fYKn8upon8fDofx0EMPYfPmzRWNxG02WkqgKl6vF9u3b8fWrVtx8eJFnD59Gnfu3NE+LxWVCoKAtrY2tLe3I51OY3x8HBMTE6YL3NeDbDaL69eva2tUCoKAvr4+rFy5EitXrkR//wrDUw4IIe5iYmICw8NDiMfjGBoaws2bN2s6J71S1Lqso6OjqG+zEnp7e7F582asW7dOE2oriVOlJQWqIopibtGDjRgeHsbx48dx48YNZLPZIokWvvd6vejt7UVvby9mZmYwOTlZ0yUDK0GWZYyMjGBkZARHjhwBAHR3d2PFCiU6XblyJUf4EuIQqVQKN2/exPDwEIaGlFe9R8ouRCgUQjQaRUdHh/ZUFKD0OrV6RFHEypUrsWXLFvT19bWsNPW0tED1LF++HMuXL8f09DSOHz+Or776qqKF6NVVjtQ+g5mZGUxMTGByctK2yFTPnTt3cOfOHZw4cRwAEI1GsWLFCixf3o++vj709vby0WyE1IGxsTEMDQ1heHgYw8NDuHXrlq39l6WIRCKIxWKIRqPwer0G8ZUToCpRv9+PdevWYcuWLQiHwwtu10pQoAW0tbXhqaeewq5du3DmzBmcPXsWU1NTAIqjULOmXlWmgPJooImJCYyPj9sy3NyMiYkJTExM4MyZMwCUu8ienh709S1FX18flixZgp6eHi45SEgVJJPJXOvPsCZNdWCi0+ibZ6PRqPagi2ojxra2Njz44IN48MEHtbEWFKcRCrQEHo8HW7ZswZYtW3D16lWcOXMGo6OjVX2BwuEwIpEI+vv7MTs7i/HxcYyPj9d9Wkw5stmsNm3m1CklTZIkdHf3oK+vT5Nqd3c3I1VCcty9excjI8MYHh7GyMgw7ty54yqZiKKoNc3GYjFD82y5KLiwqVa9wd60aRNWr15d72I3PBRoBaxZswZr1qzB7OwcLl68gKtXr2J8fBxAZVEpAG0N3v7+fiQSCYyNjWkLNjhNJpPB6OhNjI7eNEi1t7cXPT096OzsQkdHh3ZHC/BpM6Q5mZ+fx/j4GMbGxnD37j2Mjt7EyMiIoze9pVBHz3Z2dhqkWW5ZPbP3gNLVs2rVKtx//wMIhzkQsVIo0CoIBoNaVHrv3j1cvHgR33zzTdXDztXlA/v7+5HJZDA5Oak1tbrlH1UdXj8yMmJIlyQJsVgsJ9ROTayxWAcCgYBDpSWkcrLZLCYmlNagsbEx3euebdPTrCAIAtrb2xGLxRCLxQz9kVai4UAggP7+fqxfvx6dnZ3qUWpY4uaHArVIR0cHduzYge3bd2BoKI4rV65geHgY6XS64qgUUITU2dmJrq4uCIKQuwMe14TqxECkcmQyGdy9e9d0acRgMKjJVC/WaLQdgsDmYGIvMzMzGB8fy0WU41pkOTEx4arm13KEw2GtWTYajRZFmaWW1Cu1zJ7X68WSJUswMDCApUuXgY8uXhwUaA1QR/Cm0xlcu/Y1rl69inv37lm+K+zr68PSpUshCAKmpqa0vtPJyUlX/+PPzc1hbm6uaAF/dVUnRawxxGId2l10MMjmImKdVCqJ8fGJ3P/IWC6qVH53auDeYvD5fOjs7ERnp9K6oy5sAFTXl6lHFEV0dnZi1apVWLVqNSSJN7O1ggKtIZIkYWBAWXYvkZjH5cuXMDIygvHx8YqiUrM0dTnBVatW5ZqeJjSZTk1NuWKY/EJks1ntJuDaNeNnykLVeaGqr/b2qPZUe9LaZLNZTE1Nac2uSguNIsrZWefHECwGdaH2WCyGzs7OokUNrN4wq4OKli5dioGBtfD51BXLGHLWEgq0Tvj9fmzcuBEPPrgR6XQKN27cwPDwMO7du6c1y5Zr2jVDkiR0dXWhu7sbgiAgm81qSwu6rQ+1UpLJJG7dGsWtW6NFn/l8PgSDIQSDQe0VCgURCCi/qz+DwaA2VJ80BplMBnNzc0gk5jA3l8j9NHvNIpFIuLrlpVL000vyLTBBgyiriTIL33u9XsRiMSxbtgzLli2Dx+PNNdFSmvWCtY4NeDwerFmzBqtXrwEg4/bt27hxI467d+8UDUCqpv9UvcuMxWIQBAGCIGBubk6LUsfHxzE1NdWwlU8ymUQymcTExPiCeSXJk5NpwCDXQCCg/QwElHS/nwvy14NkMqkTYsIgR/V3VYyp1MKLlDQ6anOsGl2qfZiqJGuxkk8wGERXVxeWL1+Orq4uAAL7NW2EAnUA5XFpPQBkzMzMIh6/gVu3bmF6etr0H6oaqQaDQYTDYSxbtgyCIGijfNWRhuPj4xWtsNRoZDJpTE9PYXp6asG8giAiGAxoUlV+VyTr9wcQDCo/JUmCKIraT+UlVdVq0EjIsoxMJoNsVkY2m0E2m0Emk0U2m0EiMZ+TYl6Oxp8JR9Z0dQvqCNmuLmXKV2dnJ8LhcNGAn0r2Uy7KFEURoVAIPT09WL68H8FgAJSmc1CgDhMKhbB+/f1Yv3490ukMRkaGcfPmTUxNTVkegasXrMfjQWdnJ7q7uwFAG+k7OTmpvSYmJkrKuxmR5SxmZ2ctT1kQBKFArhIkSTT9qYq3UMLK58V59PsWBBGCAJ3QsrmXKrb8+2w2q8lOTVdkaMyvpGUMeTOZDDKZTMtc/8WiNpWqI2NjsRja2togimJNmmLNjtfW1obe3iVYsmRJbhAQjekGKFAXIUkS+vtXoL+/H7IMzM7O4ObNmxgbG8fMzLThDr+aqLTwc7VZs6+vD0D+H1g/H9XpJQjdjCzLrpteROpDJBIx9Fmq8y8Lo8paLqwuSRKCwSBisQ4sWdKLYDDEvkyXQoG6mFAohDVr7oPycHcZc3MJjI6OYnx8DLOzs4tqMisUriiKWtMTAK1PVb+erzo/dXp6uiFG/xJSKfpVffJzmGPa4usADH2XC1FNlKk2y0ajMfT29iIQ8EOVJZtm3Q0F2kAEAgGsWqU8TBtQlh27desWJibGMTs7i2w2azkqLfVeXYJw+fLlmlQBZZL69PS0Np1G/dlKTcGksVDnI0ejUcPP9vZ2BINBACga4FPNkngLNcWqI+cFQUAgEEA0GkV3dw/8fr/2OWksKNAGxu/3o7+/H/39/QBkzM8ncffuXYyPjyGRSNQtStQ/wk0drKT+88uyrD3STRWr+ng3p5+FSJofdaqIvo9SfYXDYU1igFGSqvgWkmC1qPvy+XyIRqPo6urOzclUI0xKs5GhQJsIn8+HpUuXan2bAHLymsDs7CySyWRNotJy2+vv8tV86lNd1Anxk5OTWgSr/lRfbBom5ZAkCZFIRLuB0/+uLMDRbpBgtaNgC6kkqtQ38cqyDI9HmVLV1taulUfNS5oLCrTJUZuogPzdsDr6dm5uVhsMY8c/tyRJWj+rvlLRR7Bzc3OaVNUmYfX3qakpzMzMULJNiiiKaGtrQ3t7O9ra2rSXKsi2tjaEQqGSA3iqfVB0qfeV5FdHL0uShEAgqJVb/VyflzQvFGgLolZGQL6iUQWVSMwhk8loUyvMWMwI4HL5BUFAKBRCOBzGkiVLDGLVl0UfvZqtXjM7O6v9bLSVmZoNv9+PUChU9FKfSBQKhQxyVGVVKMRqBvDUmmw2i3Q6DUEQ4Pf7EQ5HtP8fCrK1oUAJACASadOtwwkAMmZnZzEzM4P5+flcpCpDkiRH1qhVpav2canz7gBjFFsY2WazWSQSCU2qqlj1r7m5OczPz2srH83Pz2N+fp5TeHJIkgS/3w+/3w+fzwe/349AIKDd7ITDYVNJhkIh06eHAOaDdWopRytRZiqVQiqVgiwrf7PP59P+DuNiBZQmUaBASUnUSAHISxUAkskUZmenkUjMI51OQRWrz5dfIq9WUWk5KtlG32dWiXD1aQCQSqU0sepfqnALfyaTSd0CBsZXfqWf4s/UJsHCPLKcv2kpXpDB/L1xMQbBkEcVoV6GpX56vV74fD5tgYBSzaaF7+uxmEAl+SsdBaveEGazMiRJOSfBYBixmE/LCW7maQAAIABJREFUl9uC00hIWShQUjVerxfRaAzRaF6ssqxUorOzM0gkEoaI1ev1wuv1LrDXxVNraatNdoFAoGSfbbVSLpe/0n1VIq1S/YJW86tlqLX0yuVfDOl0WmtFkGUZoijlRBlCMBjWFiZgVEkWAwVKaoYgCLnmvDDUaFUVbCaTwfx8AqlUCul0JlepCVrk6vf7KxKcHZGtU9Si7PWWnJ2UK0sikdAEmckoNwGiKMLjUaJmr9cHr9dvEKQqTUJqBQVKbEEUJQSDYQSDMvJ1oqwTrIxkMpGrEDO5bUR4vR4tCqxH3+tipVWvAVVWy1NvarGYwEL58486S+SaxLO5Gy5VkF6IogifT1n037hqj3vOFWl+KFDiCpTm0kAuElXS8k2JwMzMHNLpNDKZNLLZjCZeQVD6/TwepalY37+nUmvJOSk19dh2NqXWIipNpVLaYC21D1JZxD4LpbVCyLVIeOHxeHLN1RL8/qCuGRugIImboEBJQyAIQq4v1VMkWPX3VCqDVGoWU1MzkGUZ6XQqNxAHEAS9bD3w+by5Zj6PJl6v11uTKLfeUakZdjXFqoNw1OZT/cAqtXleHfyklEO5uSkcnKWmezygIEnDQoGSpkQQBHg8+YFL+mg2nc7k+mETKOyrzWZlyHIm97uQEy8AiBBFQRvN6vFIkCQPPB6PJmGv12sYGVs4erbUazHoH0eWj+qKX/p8qVQaqVQSyWQK6XQKqVS6IK8MWc7mBhAp50DpQ1TOQeHgG/V3RZIiJEnkSFbSElCghOhQmw6V3wG9YLNZOdc/m8H8fNIwtUc/GllBNomUF/o8v798JCYbfhcEURfdFUduxt9Lf15qcE3+c/0rn5cQkocCJcRFGAWritKQw+YSEUJKYb5WGyGEEELKQoESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBALUKCEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWIACJYQQQixAgRJCCCEWoEAJIYQQC1CghBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBALUKCEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWMA2gZ49e3aVXccihBDSmszPz3vsOpZtAj1z5syud999F+l02q5DEkIIaSGOHz+Ow4cPP2zX8WwTaCgUGjt//jz27duH8fFxuw5LCCGkyUkmk3jrrbfwySefoK2tbdKu49om0Egkcg8A7ty5g9dffx1Xr16169CEEEKalLt37+L111/H5cuXAQAdHR0Tdh3bNoGuWrXqGwAQBAHz8/P4xS9+gUOHDkGWZbuKQAghpIm4cOECXn/9dYyNjQEAJEnCsmXL7tl1fNsEet99941GIhHloKJy2M8++ww///nPkUgk7CoGIYSQBiebzeKDDz7A22+/jVQqpaUvX74cfr/ftoE2tk5jWblyJQAlClX5+uuv8aMf/Qhff/21nUUhhBDSgIyNjeEnP/kJjh8/XvTZ6tWrbS2LrQIdGBjIH1jMH3pqago//elP8Zvf/Abz8/N2FokQQkgDIMsyjh49itdeew3Dw8Nauj4gs1ugts2XARSBdnZ2YmxsDIIgQBAEQx/ol19+iWvXruE73/kO1q5da2fRCCGEuJQ7d+7g7bffxsjIiCFdL8+BgQF0dHTYOq7G9pWItm/frv0uSZLhBABKNPqzn/0Mb731Fubm5uwuHiGEEJeQzWZx6NAhvPbaa2XlCQA7d+60s2gAHBDo+vXr0dnZqb2XJMk035kzZ/A3f/M3+Oqrr+wqGiGEEJcwOjqKH/3oRzhw4AAymYzhM0EQIIqiJtH169eju7vb9jI6shauPgoVBKGkRGdmZvCzn/0Mv/zlLzE1NWVX8QghhDhEOp3Gxx9/jNdeew2jo6OmefRjaARBwK5du+wqngFb+0BV1q1bhyNHjmhzd0RRhCzLyGazpvnPnTuHS5cuYefOndi5cyd8Pp+dxSWEEGIDZ8+exfvvv4/JydKLCXk8HkM/5wMPPIDOzk5H1hRwRKCCIGD79u1455138gXxeJBOp0tKNJVK4cCBAzh58iS+/e1v46GHHrKruIQQQurI0NAQ3nvvPcTj8bL59ONmZFmGIAjYsWOHHUU0xRGBAsD999+PCxcu4JtvvskXZgGJAsDk5CR++ctf4vPPP8fzzz+P/v5+O4pLCCGkxkxOTmL//v04c+bMgnklSTI03QLAo48+avvIWz2OPg/0hRdeQEdHh/ZeEAT4fL6SfaJ6hoaG8MMf/hA///nPMTFh29KHhBBCFkkymcSHH36Iv/zLv6xInl6vFx6PMd5buXIlnnzyyXoVsSIci0ABwOfzYc+ePXjjjTeQTCYBKGG5z+dDKpWq6NFnX375JS5cuIDHH38cu3btQiAQqHexCSGEWECWZZw8eRLvv/9+xQNDfT5f0ZSVaDSK3/3d3y1KtxtHBQoAHR0deOGFF/Dmm29qabIsw+v1QhAEwzqHpUilUvjoo49w+PBhbaBRMBisZ7EJIYRUSDabxenTp/Hxxx/jzp07FW2jtkjqF9wRBAEejwcvv/wyAoGA4w8jcVyggLL80q5du3Do0CFDuipRNTpdiEQigQ8//BCHDh3C9u3b8cQTTyAcDtejyIQQQhYgk8ngxIkTOHDgAO7dq/whKYIgGFoT9aJ84YUX0N3d7bg8AZcIFAC2bt2KO3fuFC2coEq0mjVy5+fnceDAARw+fBjbtm3Dk08+iba2tloXmRBCiAnpdBrHjh3DgQMHMD4+XtW2oihq8iyU5M6dOzEwMOAKeQIuEigAPPfcc5ieni5assnj8UCSJCQSibIjdAtJJpP49NNPceTIEWzduhVPPfUU2tvba11sQgghULrTjhw5gk8++aTsXM5SeL1ebZ6/XpKCIGDt2rWGRXjcgKsE6vF48L3vfQ/vvPMOrl69qs3zAZS7klAohGQyWXGTrkoqlcKhQ4dw9OhRDA4O4oknnkBvb289/gRCCGk55ufncfjwYXzyySeYnp6uens16pQkCbIsF0WYGzduxLPPPlur4tYMVwkUUCT60ksv4aOPPsLp06cNn6nt4l6vF7Ozs1WH8el0GkeOHMHRo0exbt067Nq1Cxs2bHB8JBchhDQit27dwqFDh3Ds2LGqAxsVr9erDfosjDplWcbOnTuxbds2U7E6jesECign7plnnkE0GsWnn35qEJwsy/B4PGhra8Pc3FxFo3QLkWUZX331Fb766it0dXVh165d2LZtG0fuEkLIAsiyjLNnz+LgwYO4dOmS5f0IgoBQKKTN7yyUoyRJ+M53voP169cvqrz1xJUCVRkcHERbWxvee+89w8lVm3bD4TCSySTm5uYs35ncvXsXv/rVr/Duu+9qzbt9fX21+hMIIaQpmJubw+HDh3Ho0KGqRtSa4fV6EQqFIIqi6biWQCCAl156CcuWLXNd1KnH1QIFlCX/wuEw3n77bdMmAr/fD6/Xi7m5OctNCIAy4Oizzz7DZ599hrVr1+KJJ57Agw8+WLR0FCGEtBIjIyM4ePAgjh8/vqg6FshHnX6/3zC3Uy/JaDSK7373u4jFYq6WJ9AAAgWA/v5+vPLKK3jzzTdNR3aJoohIJIJ0Oo3Z2dmKVjAqx+XLl3H58mXEYjE89thjGBwcdORZc4QQ4gTJZBJffvklPv/8c1y5cqUm+wwGgwgGg4bF4Avp6+vDnj17GmZFuYYQKAB0dnbij/7oj3DgwAGcO3fONI/X60UsFkMikcDs7GxVU17MGB8fx/79+7F//36sXr0ag4OP4eGHH2qYi0sIIZUiyzIuX76M48eP4csvv1x0tKni9/sRiUS0x1aaiVMURWzZsgU7d+4sikjdTMMIFFDWRNy9ezfWr1+P999/H9PT06YjaIPBIAKBAGZnZxfVP6rn2rVruHbtGt5885+wceNGDA4OYt26dQA4gpcQ0riMjo7i+PHjOHHiRE0fzOH1ehGJRODz+UzFqYqyo6MDzz33nDa1sFHkCTSYQFVWrVqFP/mTP8FHH32EixcvmuYRBAGRSAShUAjT09NIJBI1OXYqlcLJkydx8uRJRKNRPPLIIxgcHERPT09N9k8IIfVmZmYGp06dwvHjxxd8Bme1SJKEtra2BdeqFQRBizrV6LTRaEiBAko0qg5x/uCDDzA7OwsAhsUXAOVixmIxpFIpTE1N1axZAgAmJibw0Ucf4aOPPsKKFSuwZcsWbNy4EdFotGbHIISQWpBMJnHhwkWcOnUSFy9eRCaTqen+BUFAW1tb0frjZnM7Y7EYnn32WW3GQyPKE2hggaqsWbMGf/zHf4yPP/5YW0e3UKLqI9K6urqQSqUwMzODubm5mpbjxo0buHHjBt566y309/dj48ZN2LjxQXR2dtX0OIQQUimJRALnz1/A2bNn8NVXXy16gKUZkiQhEokgHA5r9W6p8Sf6qFNddaiRaXiBAsqcoeeffx7r1q3DgQMHyi4l5fP54Pf7kclkMDU1ZWlFo3LIsqzJ9J13foO+vj5s3LgRGzduRG/vkpodhxBCzJiZmcG5c+dw9uxZXL16teaRporX69UiTrUOLVeXxmIxfPvb38bSpUsXzNsoNIVAVQYGBrBmzRqcOnUKx44dMzzBxaxpt6OjA9FoFNPT05ienl70qF0zbt68iZs3b+L9999HV1eXJtNly5bX/FiEkNZkcnJSk+Y333xTl7pMxe/3IxqNIhgMFi1wo6IfSRsMBvHYY49h06ZNDTXCthKaSqCAIsbBwUFs2rQJx44dw+nTp8s2W6h9pKpIJycn63bHdvfuXRw4cAAHDhxANBrDhg0PYN26dVi9eg283qa7FISQOjI6OopLl77C+fPnEY/H6y6mcDiMaDRqWASh3DG9Xi8efvhhPPLII/B6va5cy3axNG2t7ff78fjjj+PRRx/FZ599hgsXLiw4Iqy9vR3t7e2YmZnB5ORkTQccFTIxMY7Dhw/j8OHD8Hg8WLVqFdauXYu1a9eht5cjegkhRmZnZ3HlyhVcuaIs9DI1NVX3Y6oDg2KxWMUSFEURGzZswLZt2xYcidvoNK1AVUKhEJ599lkMDg7i0KFDuHbtWtn86hemvb0dyWQSU1NTmJqaqmuTSDqdzv1jXMG7776L9vZ2DAysxdq1A7jvvvsQCHCRe0JajWw2i6GhOC5fVqQ5PDxsm4wCgQCi0Sja2tq0NLNj65tkRVHEfffdhx07dmjbNbM8gRYQqEpHRwdeeukljI6O4uDBgxgeHgZQ3Deqx+fzobu7G93d3ZiZmcHExETNR++aMTk5iRMnjuPEieMQBBHLly/DwMAABgbWYvly9p0S0qyMj4/jypXLuHLlCr7++mvDOI56I0kSotEootGotvgBUHpErZ7+/n7s2rUL3d3dTdlUW4qWEajKkiVL8P3vfx83btzAiRMncOPGDdNpL/r3+qg0lUphcnISExMTdRkSXogsZxGPxxGPx/Hxxx/D7/djxYqVWLlSeS1duhSSJNW9HISQ2pLNZnH79m0MDcW1//HFPuWkWtQFZ2KxGCKRCAAUCbBw4I/+/fLlyzE4OIjly5e3jDT1tJxAVVQBjY+P4/jx47h8+XJFQvR6veju7kZPTw9mZmYwPj6O6elp27488/PzuHz5Ei5fVp7D5/F4sGzZMqxcuRIrVqzA8uX98Pl8tpSFEFI509PTGBoawtBQHENDQxgZGbH0PONa4Pf70dHRgVgsBo/Ho0mz1BNS9Hg8HgwMDGDLli2IxWIAmr+pthQtK1CVjo4O7N69G08++SROnz6Ns2fPYmZmZsGoFAAikQja2tq0OaWTk5PatnaRTqdx/fp1XL9+HYDyxV+yZAlWrFiBFStWoL9/RdHKIISQ+pLJZHDz5k0MDw/lpDlk+iQpO/F6vYhGo4jFYgiFQlU3tUYiEWzYsAGbN28uub5tq9HyAlUJBALYtm0btm3bhgsXLuDUqVO4e/duRduqc0o7OzuRzWYxNTWFiYkJTE1N2f4Fk2VZm3v6+eefA1CeZLN8+XL09S3F0qV96Onp1Z4CTwhZPBMT4xgeHsHw8BCGh4cxOjpat+lw1eD3+xGLxYqkqQYEC81MkGUZPT3/P3vv/iTHdd15fs+5mVnvfgBoEI0GKZKgHhalMClrLI8YEzue3Zj1/DC/TAR+2h82YiM29l/YnzYY612PHevx2I6xd+y1wxMez85MUGNL1suUTEoiLXJNPfgagiJIggIJdANooLu6q+uRmfeesz/czOqq6jfQDYLE/UQUKquyKiurANSnzrnnnjuHz3/+88XiGZ57XZwl4Vt0AiLCL/zCL+Azn/kMlpaW8Morr+D9998f+0dXsl1UWsp0dnZ2TKbr6+tHWsm7GysrK1hZWcHrr78OwFfLnThxAqdOzePUqVM4deoUTpyYgzFh8fBAYC+yLMO1a1exuLhYXK4Me3HfDdRqteF3UK1WG37vHER6zIz7778fv/iLv4iTJ0/uuobnvUwQ6C7Mz89jfn4e6+vreP311/Hee+8d6D/KaGSqqlhfX0e73cba2tqH+utURHD9+nVcv34dr7326vBc5+bmcOrUKdx33zxOnboPx48f37FCORC4V7h58yaWlpawtOSFeePGjbtOJI1GA8eOHcPs7Oxw7uV+znEyCm00GnjooYfw+c9/HvV6/ShP+WNBEOg+aLVa+PKXv4x/+A+/jCtXLuPChQu4fPky8jzfV1QK+F90MzMzmJ2dBYCxyPROTI3Zi3LM5urVqwBeAeCLBU6cmMOxY7OYnT02/DEwPT2DKAqVv4GPD6qKTqeDlZUVrK6uYnV1FSsrK7h27eodnUqyX5gZU1NTmJmZwbFjx4aFg7utu7nT7SRJcPr0aXzyk5/E/PxphN/M+ycI9IAsLCxgYWEBzjm89957eOedd7C8vHyg9CwRDQfzAYxNjVlbW/vQKvMmsdbi6tUlXL26tGXf1NRUkSY6htnZGczM+O1Wq/khnGkgsD8GgwFWV1fRbq9gdbU9lGW7vXpXjFnuRrPZHFbOtlqtoQhvpZinzDg9/PDD+MQnPhGmwt0iQaC3CDPj7NmzePjhs8iyFG+//TZ+/vOfo91u7zsqLUmSBHNzc8MV2Xu9HtbW1tButz/UsdPdWF9fx/r6Oi5dujR2fxRFw0h7Zma2EKsXbJheE7gTOOewvt7G6mob7XZ7KMjV1VUMBoMP+/T2TaVSGaZly+kmALZIc7tioJ3um52dxQMPPICzZ88ijpMQbd4mQaCHQJIkePTRR/HZzz6KTmcd77zzDj744IOx8dL9SpWI0Gg00Gw2cebMmeHYqf8SaO+6VNvdgLUWN27cwI0bN7bsq9frRUXgbCHZGUxPz2BqagpEoYApcDA2Njpot9sjF/9/5MOofj8MoigaDpPMzs5umWqy2w/p3STaaDSwsLCAs2fPotFoBmkeIkGgh0yr1cLjjz+Oxx57HDduLOPSpUu4fv36vsS3nVSZeVhRR0Rj6d719fUj79N7mPR6PfR6vWEbxZLR8ZzpaV9yPzU1jXq9jlqthkql8iGdceDDxFqLfr+Pfr83HN4oZbm21r4jncCOkiRJhkM5s7OzmJqaGu67nTmWRIR6vY4TJ07gwQcfxOzssSDNIyII9Ag5ceIEjh8/AUDR6/Vw6dIlXLt2DWtraxCRbYW5V6Q6me4tix9Gv2COchWZo0BEhl+M28HMqNVqE5c6qtXq8Ha1Wl5X7/DZBw5ClmXo9/sYDAaFHPsYDPrD7VKY/X7/rqkFOAzKlnllOnYywryVKHOykXur1cLJkyfxwAMPoFarAaAgziMmCPQOUa/X8ZnP+PmlzjlcubKIpaVFrK6u3tYXxWh1bynawWAwltr6qKa0SkQE3W4X3W53z8cSESqVyphUa7UqqlUvV3+fv65UqmHu6yGQpgP0+4OhFAeD/ti23+fleLcX6hwWURQNU7GTY5i7zcvcq2J28jWmp6dx6tQ8Tp8ue2IHad5JgkA/BIwxRau9M1D1jQ6uXLmMmzdXMBhsndJy0KKkWq2Ger2OhYUFEBGcc1hbWxuOo94tU2eOAlXFYDAoikVW93x8kiRDsfrr6jBtbEwEYxjMDGYDYwyYeXjt7yMwm2KbAdz9314iAhEHEYFz49uqbnifvxY4Z5GmKfr9AdJ0MCbFwWDwkf5xdhiUi02U45fHjh0bW87rVitlt6NarWJmZgYLCwsjqdkgzQ+LINC7AP+fbhaqPnq8fPkD3Ly5gm5341B+sRtjhsuyERGICNbasbHUD7tb0odFlmXIsuzQ+pQS0YhkTSHgna+9iDcfz0wjsiYANCa8SemV+zZl5ybucxOSvDciwKMiSZJhazw/Zj9dFMHRoaRiJ2Fm1Ov1oh3nGVSr1aE0Ax8+QaB3GdVqFY888gjOngUAX4F7/foy2u1V9Hq9sfTPQaLSyf2+ScK4VAGMjaeWY6ofpdL/DxtVhXMuiOojzmhUWaZgZ2ZmUKvVto0qD9q8YLfXrdVqmJ6exokTJzA9PY3NCDNI824jCPQup9WaQqs1BUChCnS7XVy/fr0QW/9QI8aywcP09PRQqkSENE2HLQjLMdVOp3NXdmgJBA5Ko9HA1NQUpqenh+OW09PTMMbsO6q8VZgZSZKg1ZrCyZMn0Wg0giw/QgSBfsRoNBp48MEHh7d7vR6Wl5exvr6Gfr8/9iv3oFHpTrcrlQpOnTqF+fn5sWg1z/OhTMspNWWDhY9TBWXgo08Z1ZUp1/K61WoNRQmMF/gcNKrc7+omlUoFrdYUjh8/PrbUYOg7/dEjCPQjTq1WwwMPPIAyQk3TFMvL19HpdJBl2ZGOaSZJMlxcvPzPz+yrWvv9/lCmo20KO51OSG8GjoRy6a5ybmUpzOnpacRxvKMkb0eKu1E+L0kqaDYbOHFiDpVKJUSYHyOCQD9mVCoVLCycGd5WVWxsbGBtbQ3dbhdZlu4ame43Kt2Jcn+9Xkej0cD8/PxQquXzer0eNjY2hlNTut0uNjY2hpder3fPV3YGxmHm4QL2rVZruN1sNofbo6uQjI5NHuXCz6PFQyKCKIpRq9WG0e3o/5UQYX78CAK9Byi/ZEa/VLrdDayvr6PX6yHPLYh0KLqjpJxQXpb5j461jn7BjIq10+kM5VqmjLvdbpDsx4SyCUApnclLs9lEvV7f0jx9J0nuN5W60+29Hl8WijEzKpXq8DzLf79BlPcOQaD3KI1GE43GqFSBbtcLajAYwDkLItp2lYbbrQDe6/FlBWQ5PaC8b/QiImPdbEYvvV5veD16CanjO0MURWg0GsMsRL1eH7uU3aNG7wOwrRBvtYDnsFKxzrmiZSChWq2g0Wii2WwN510GWd7bBIEGhjQaDTQaDfjvGf9lk2UZut0u+v0+rM0homAmRFH0oX55lCm9ZrO5bRS73X15no8Jtd/vD99bmqbI83w4L7S8nabp8HaWZR/7qNdHVRVUKhUkSbLjddlwoowORy9xHAPYfqxxJ0l+mJ+rqsJaC2stVBXGRKhUEtTrjZHWkKFZQWArQaCBXYnjeNjkvZSqKiDi0Ot10e8PioXFBcYYJEky/LV/mFHpYRyjFMCxY8f2JdzRsdvyMirZcruUa3kpmxmU42LbXTYbHoxvT972X+gGURTBGDPSBYn3tT15X/kZlCKclONuFam7pU1vV4aHkXbd6xhll6qyCT1zVESVdRgTbRNVBmMGdicINHBL+AiwhUajhbIC2KPo9fpI0802b0SMOI4Qx/GwH+jdwK1IPo7j4Y8EYHvJHlTKOz1+p337SXXuJ+rb6fElhzVt46gevx1ekj574FwZURpUKuXCA43i2ECIKgO3y93zbRb42FA2cAcwTAeX34N57iM2a3M4J/Ct7whRFBeRUDw8znaCO+zI9jAi4aPiVs7loyC528Fai8FggDRNi0hdQQQYEyFJYsRxZbiIwLgggywDh08QaOCO4qPQUpK6RbBpmiHL8mK8VUAEMBskSTxMPd6JauH9ctTC3unxH6bEduN2BFymWDfHpG3xb4CG/waiKAaRQa1Wx2SLu9HtQOBOEAQauKswxqBaNQCqW6YOWCuwtldURrrhWCERCtEyoihCFEXDQhc/cZ0OVXIHff6HzZ2OMkenm/hVXPoYDPw4sbU5rHVF6lihSsPG+nEcoRRgFCWIomR4PH99gDcdCNwBgkADHzl8NGK2CLbEi3aAbtcv2Sbihesf4+VmDA+LcsoinSiKEMcJ4jhGpeILbMqK0tvhTqSRb1eSOyEiyLKsGFcsi6dsIUILa/00D+dsUfiE4geN/3yJeKI4hxFFPPb+7qYfG4HAQQgCDXzs8dGNT/uOTtFxDnDOArBQTTGaUh59bFlNW0ZMzGVRDw/XC/USLoVBE1Wwk7fN2PPK63J7tOKWiIp1OseressVX0aXLHPOQXW75c4EIlpU9cowenfODpdB88fW4r1vpkZHBTh+/+R95Xso7g1SDNwDBIEGAntQjsGNjtWWMiubM0yO5U4+dvK+7fdvv71TtDae2txue+f920nRb9PE/kAgsBN3rBpDVcN/yUAgEAgcKXeyoO6OCJRCPicQCAQCd4g75ZwjF2iQZyAQCATuNHfCPUcq0CDPQCAQCHxYHLWDjqSIKIgzEAgEAncDpY/0CAZHDz0C3Umed0unlEAgEAh8rNnWQUcR2B2qQHc5wRCRBgKBQOBOcUckemgC3eHEqLhAg0QDgUAgcMSM5DqH/hnlMCV6KALdRZ6ec+fCoGggEAgEjhwCgHPnaPKusccckkRvW6D7kec5hDHQQCAQCBw9qkrngDsi0dsS6H7luby8TKFaxKdGAAAgAElEQVQTUSAQCATuBMvLy3dEorcs0IPIs9PpBHkGAoFA4I7Q6XToTkj0lgS6lzzPTchzMBiECDQQCAQCR46q0mAwoFGJnjsiiR5WFe5Y5AmMyzNNU5Ig0EAgEAgcMapKaZqOSRTAnpHorXBggW5j6l0jzzRNyZ45QwhFRIFAIBC4A9gzZ7ZIdK9I9Fai0AMJdDd57hR52jNnaD5NQwo3EAgEAkeOqtJ84Z6DRqIHlei+BbrrgXeJPOfTlLIsI5Eg0EAgEAgcLaqgLMt2lOg2hUVjHESi+xLorkVDO1TbjspzMBiwIgg0EAgEAkeLQjEYDHhUouW+HSR6y0VFt1pEVBx8U57ljsFgQOkDD4zJ087MkIoEgQYCgUDgaFElOzNDoxJNH3iABoPB0EFDiWJnie6HPQW6e9HQ5p2jqdsz/T5lWUZ5npOdmaHmYMASeuEGAoFA4IhRVWoWgVue55RlGZ3p98dSueVjRx2GWxgPvfVpLLsUDXU6Hc7znNJWi5uDAWe1Gn/7m9/8y2vXlv/nW369QCAQCAR2YXV17X/97ne/+2+yWo2bgwGnrRbneU6dTof3WVR0IHYV6M7R5+5FQ2PyzGpcyzL+3vd+1KvWKo/c6okGAoFAILAbSSX+9De/+YP1WpZxlo1LdNeioh1SuXtFoQeJQP2BnnwS586NFw2NyrPb7XLaavGMtZRlNc5rGQ8Gkcnj1NRqtX90gNcLBAKBQGDfVCuVf5THqRkMIpPXvERnrKW01eJut8ujEgVGiorOAXjyyfIwt1+Fu6N533hj7H4/MPtJlEVDzh2jGWspTVPOaxnX8pxdJedf+PSna3EUf3G/JxYIBAKBwEEwxjzyL/75Pz/lKjnX8pzzWsZpmvKMteTcseH0FuCTGC0qArDFbSW7RaHbCnTH1O2O8z37w4rbtNlnL88a1/KcsygyURaZf/DYY/cTITnIhxEIBAKBwEF4+BOf+GSURSaLIuMlWuM0TTlt9rmszLVn+nvND91XKvdARUST8z0nxz3tzAxNW0t5rcbVLOM8STixCdsoM7Ozs3MH/ygCgUAgENg/9Xr9pI0yk9iE8yTxLqrVeNpaKitzd0zlHvC1tgh0t+hz9M7R1G2322XnjlFZNNRwjvJKhSvWch5lxhhjGtV6EGggEAgEjpRKXDlpjDF5lJmKtZxXKtxwjsqiIueOUTkeum0q9wBR6L4j0N1St845ajb77JyjvJZxnuecp6nJo8hEuTHOWhPF5tgtfBaBQCAQCOybKDYnnLUmyo3Jo8jkaWryYjx01FW7pnL3yV4C3XGezGTq1hap23LcM7EJO2vZxY4Ns8nyPD3AeQUCgUAgcGDyPO8bZuNix85aTmzCo+OhdptU7paD7NLmb5Qxge40ULpd9DlZdZtlftzT1WqUWMsusWyMMYb91frGxsYtfh6BQCAQCOyLbr+/Nuoel1hOrGVXq1E125zaMlqVu98odNKRu0Wg20af6YixffSZU5qmLE1HIkJ5mhqXJOxTt8Y4y8ZaNu2Vtc5tfCaBQCAQCOxJt9Ndt5aNs2ycNX4YMUk4T1MjIiRNR2masp3JKc9zAjbXDx070D6i0H2NgY5GnwDG5nw2BwN2zSaNVt1upm5zQ5RHTHl0/s2fXb+1jyMQCAQCgf3x/geXrzLlEVEeGc7HUrllVa5rNqksKNqcG+qLY8fa/O3BUKDbpm+LeZ+jjEafaVk4lGWc577qdjR164wxjtk4ZvP6W2900zR7/9Y/lkAgEAgEdkZEun/zvb+9XHrHTaRyK9Zynlc4z3xBUdrs82gUOnqsndYNHXXlThEolQfYbexz2lpyzSY1nCOpObI24djGLM6xs9awtcY4NmwpYiLTXl9747A+qEAgEAgERtnY2HjTWUtsKfLuscZZa8Q5jm3M1iYsNUcN58g1m95h+xsLPWgjhSe33DM59rld9Oliy1EUcRRFbJgNszPMzpCj6IPLl1+/vY8nEAgEAoHtuX7jxhvkKCq9Y5hN6SMXe0dNRqGTY6Fbj/rkjq/HwE7p282+gGX0CQCjlbfO7Rx9WstGxLAwG0dkHCF66qm//Pvc2rXb/IwCgUAgEBhDVfOvf+NbzzhC5IiMsHeQLyjaIQp1TRqtyAV8oexYc4VzW3vkls7cLgIdW7V7c/FR33WojD6ttVRW3rpto0/ro09HhhxFBBetrq/LxYsXv3O4H1sgEAgE7nUWl5aef/PC2x2Ci8hRxI6KKNRuiUKdtVxW5Pp5oflwXijwSQCb7f1GXmL/nYi2Fg9tdh1y1vpr5yhPEq5KlVSENqNP66NPx8aRi5jJMLMhIvOf/+qvnhGR7FA+sUAgEAgEAP3Wd77zN0TeNcxkHLlIXBmFbo6FqghVpUp5knDpsdJpZXei0QPv1plo1yKikrH5MXNzKIuHannOKkIusRy7mCMXsUjEkTHsc9BkyJEREiMOEZFEVz5YHPz80vsv3PLHFAgEAoHACFevX3/txz9++TqRROIQCYkhR8YHb85Exng3uYhjF7NLLKsI1fKcy2IizG22a98yJ/RgRUTnJuZ+jqRvc188JM6RczWqivg0buxYIsfGOBYRFjHsyBk1yuQoIqJComS+/u1vf09V9bA+vEAgEAjcu3zvB8/97ahjyFGkRtmR89lQEe+myLGLHYsIVUXIuVrhMkc2H0/jjs8J3T4OnRSon74y8djRkNZZSwAwXjzkba4ak4hhMYaFnTFqmImMMBkiMcRkhBC98cb59pXFxf96mB9gIBAIBO49VlZWL33v+z94XwgRFa4RJsNExqh3kRjDIoZVY1IRiq0dKyYCNt0GjDsPGHPivlv5AdgMZcvq2+MAnGuSc45UhLQqpJUKRS7iKBKOjLARYSPMQs6IkGFWViVDRIZEjBKZb3z76edEVG7vowsEAoHAvcx3n332WS3cQkRG1TtHhIyQM0U5LkdGvKNcxFqpeHeJFPU8zcJt49W4e702b53C8uSWBz1YXE9W345eNFFyzrGq+iiUhUWZmZVVvDxV1EsUiH7601euPf/DHz5zm59dIBAIBO5RXn711RefefYH7xF86rZ0jBaBmyh7F4lh1cJRiW7x12g1LrDpvHGeHLtFRLQlAj03Mf8T+CTSYvwTAIZVS65Go9W34hyXKVw1wkYNG/G/AoiEVZWJYJTIkKpRUvMf/tN/fvndi+9dOLyPMxAIBAL3AktXr17+4z/9s79T0sIpZIhgvGuERcgYUTbqnVSmcIe+KqpxnasNZ5UARZvabRbbPrfNfNBtU7hj8z8/uXn/aI5Yaj4KTaRCqgmpxqQqpJGSEfGFRCzM4tO3SmQiY+okapSIi1Cb/82//bffb7fb7UP7VAOBQCDwsabX7/f+8I/++LvWChGRd4qoUVXWMo0rWkSfflhRI/WO0phUE0qk4qPPmttS4wNg6L7dmsuPCnTbB9h+fxh5Hi+upTA16oCqUBwLxbFSOf6paghGSZVZWVhFmVSZjEkeWJg/x8w1/ytBTafbdX/0J3/2vTzP7e19pIFAIBD4uKOq+u//w3/8wdL16wOiQpqqUWuq+YASM6myinr3KLN3kaFyHDSOleJYSFUIdX/MshK3dByw6b5t2LOZ/Bh+ULWYIzM9jRYAFT8AK4kQAIgIqSqpRlSOfxoDAgyxYVJSztOsJyr52fsX/pdmvX5SlZhUzYV33+l85atf+/Etfp6BQCAQuEf422e///pLP/nJMqkaVeIkjhsP33/mf4AqsyorKbNhAgwZAyrHQVUj8jU6hbMS7zAVoRYATE8XrzCHspBoLyYE+uSuD7bWkrjxAiIA8OL0F6gSADIK0iKVq6oMMCkTL99ceZ6ZZxfum/sfTxybflSJSaH83WeevfzSj3/y3kE+yEAgEAjcO7z9zrtX/9NXvnJBoazE1GrVFx5cmP+fVNV1OhuLyuRdo+rdI8JGQQAII55S7ymMFRI5X0i0+xk8OXaLd9k30YFofF8R+Xp5FiIFYmikZAqRavELAApiUYaCBrnd6A3SV4goPjE789/df+rkfxORiQCl/+fP/t2bL7/62uI+PsdAIBAI3EO8c/Hizd/9gz98RVWIlejU8WOPLZyc+xfM3Lx+c/X7yjzmGmNACkNQ7ySNlIAYQJFBLSRan3yhEddtmcry5PhD95XCBQBbVOGOsilOwA/ManE7gppy20DZbyu8WG+srr6ggAOARq32qQfvP/1r1UptyjmH3//D//vN7z/3/OX9nlcgEAgEPt688trry7/1r/71q/1+X6IornzizPyvzky1/gEA7vUHb/TTwU0ftBWuYSXAAEDhoshvq5JqvOmtEYeVbOe6ndhToHabyaTO1TbTtyMm3zzB4vbItkKJi1C6P0g7vX7/fPmcOIpmPnH6vl89MTv7IAj07/7iLy597evfeD/0+gsEAoF7m+f/7ofXfu8P/uCCdRbNev3YQwun/9tqpXIaAFTVXVtZebHwDLFiU6KbQ4rjXsJ45lSKln6Tr7ud+yYZE+iTt/MuAfgQeduXoUlVLy3ffN456ZS3iSg6MTv92YcWTv9StVKt/dXXv3H9z//iP3zgnAseDQQCgXuQv/7mt5b+9M///QdMxpw+OffpM6dO/ooxXCv3d3q9Vwdp1hl7EgMAb+uinR21P56cuL3vFO4hMHbiubWDpRvLT6uqG72/kiTTnzh96rG5Y7Pz33vu+fYf/NEfX86yLLT8CwQCgXsEEcGf/7//cem/fPVry1PN5szD95/+wlSjsTD6mDTLFpeWb7y0zdNvS5IH4VAFSpZ2iBYFEGzZt7ExuH6zvfbiluMQ8fGZ6YWHzpz+5Bvnz9v/63d/b7Hb6wWJBgKBwMecPM/1D//4T5Z+8NzzG/efuu8Tp0+eeMQYk4w+xjrXef/q1e+pw1YvCBTb3A3s5qhbY88UblSpbPuCzKwAQMxKNH5Sk7cBL08hKAAlIiUlBQTLq+0LG73eu9u9RhLH1U+cnn+ovbLS+D9+47euvv/B5bAQdyAQCHxMuXHjpv3N3/6dq2+/fcE8dOb02Ua9Nj35GFG1V65df95aSQEBKZUO0sIxALCrk4hIqXBY6bJJtnPfkxO39x2BRnE8PJgx/aE8N08o92IkUsCCHCmIlEiUhJTgLyBSQSlSKAG6dHX5pSzPV3d67dmp1mwl5rnf+u3f7nzj23+zIRKC0UAgEPg48dzf/bD3v/3vv34z7Xdn7zt+7CQzb+cnvXbj5kuDfrpKhUMAeKfQpmdIvHtApOQKJxV+Iso3vVU4rHQaMO66vdh1Hmhlh+gTAHrlCYyYHPAhsqNCnnAKB/UzQf2FIAoRBakKSEBQC7gr166/KCI7RphxFMXzcyeOv/DDv4t//V/+5vq169fdTo8NBAKBwEeDtbV1+e3f+/21r/31X+v83PGTtUqlutNjV9fW31rrbCwKkYJQOEQVIt4tI66Bg3cQeSeNpm9HM6e9nV4M2zjwyfH9+x8DXQaiKFI2Rpl5eBmeEJESWUURSjvyvwiUWQikQioQUVESIhIiiP+doEJQGeT5xuVr1190u0gUAOrVaoXFTf2r3/nX9ltPP52phiLdQCAQ+Cjy9z/6cfbrv/Ebg257dWp2qtXYurzmJhu93pVrN29eUIIQ1LtDC5cQiSh5x5AKwbvHy9NHqSgcNZrOHXUZG6NRFCmW93/+B2jlVxx1bW38AJk3ORdGJ7JKzofPIiSAUyERUhKlQp5KoqJCpKKgoUj7/cHqB0vX/j/r3GCvE2/Wa5W/f/HF+P/8zd/Mlm/cCBYNBAKBjwjdbld/53d/L/vm178eTTXqdebtp52UtDudS5evLr9GoKE4FeQdIqVIC8coiZB49wgJkRROsmOu4mxi7HPott0M+uTYrVGB7iihJEnUGKM3ARhjtGuMErPSgJWINc9Z85w0J1JLpMwi5EiIWIh5+OaIVEhUlFSI2amSENQpqSMip1DXT9POpcXFH+W57e72gQJ+NXCXZcnv//7v46mv/BcXotFAIBC4u/nbZ55x//I3f0s7a+0kisyeWdAbq+13ry7feFuhoihc4QNL7xBmp4VbiHQYpBGzd5AjYRax5B2V56R57t1FA1Zi1q4xY45LkmQ3mWyOl263d25uTgG/oHZ0uaaY8x0ZTBQp0hQA4FO5pBkPlOCLgcglAhWxRjRiFmURERImERWIf+MkpOJEVZjIKbGDQBQqpORA6nLrej9fXPrpA/P3PVZJktZeH7Bhpv/6+mvmzfPn5Z/9s1+jL33pS3dsHlAgEAgE9uatCxf0K1/5iqaDgdmHN6Gqeu3GzQvtzsYiiBypOlJyCgiURJkcQZ2ICgNOiYUUTn2zXL8kNYsQWCypsmMBsYAyBaAZQRNUVQ0prF9N00TRphwv1xQVoNVqaenESQzGJ53S+fOP0qOPAr1ej0QEabpIjUaDACCLIq6KkMQxxYMBDdhxAfmKKWcIYkgMF0uaGSYxUGZiMkIascIokWFmo6qGAAMGE4iVYABmAEYVWO90V2q1ylQcRTsOKo9/4EIXLlygH77wgtbrdZw+fXqYUici7LY9ejsQuJsY/Te53ba/2m575/2bh6F97h9/7Hb7tz/PvfbvdJ77e587v/dbf5/bnftB3vPd8T7Hn7f/97H339dBuXjxIv7kT/9UfvyjH7Hbc7UTj4i4xevLP1vf6N0AUQ7AApyBkBMoA2tGilxVc2bOFchIkQtrzsSZKuVEYkU5VxLLji2RdUTi2BhLzA5EkiOXJM9Fk0QSVcmiSGuqYoxBr/eBNptNrVQqaDQaeOopADg/dp6Rqur4wO1TCpwbe5NLlYoezzKK2rGiOgBvGEU0MgBLpFBVNkZURFSdkBhRFlFlx1RGouyUxZGQU6glZqcijuBDclV1THCqakEwopp9sHT9/OmTJz7datSP7/cvLEtT+uuvfQ3fefpp/bVf+zX6pV/6JRhj9vv0QCAQCNwm77zzDr761a/p6uoK4SALlziXX756/a1Bmq2BYEHqoGqJ4ETVgeCKSNP5oUC1pHDK6ljZZzdZRQr3kLCA3XBIUYteBFBVBRQi3mkJUDpuqVLRrenZp8aiUFXd5jETVCoVtQAlSaJ5vqJRVNMsWwNzsjkOyqQZ+c69AAsiFaiIERaw+DfC4lRhFWzB4lTIAWqV2ULFQsmyslWoAdiQqlGQURV75er1t4/NTm/Mzc7cT0T7/osYDAb01a9+FX/z9NP47//pP8UXv/hFRNGebzkQCAQCt8hbb72Fr3/jG1i5eRM4YFu9fppuLF5fvphntgeCA8iRqlWwVdXSEf7CbKFqVeHA6hRsAbXM6kTZUSFPx+JTt5YEjlRBmigpiSpVUcwmWUMU1dWYFU2ShgK7T+MsmbSJAqCnAPzj0QeNjIO2o0hrRSFR0jfKpq95FAkDQqoCQOBUoJGIEWeUnbI6FXZK4n85gC1DrIIsA1aVrZJasFoojJIaKAygBr4NPa+01652e/3u6ZNzD1WSuIYDMOj38bWvfQ1Pf+c7+Ce/+qv4lV/5FVQqlYMcIhAIBAI7oKp47bXX8PTTT2NlZeVWnq8322tXb6y2rwLIfOQJL0jAKqklhVVWqwpLypYJVgHLUCvKtixIhbJjVkfCzrE4P/ZpBUwCQJRIckBia1X7Ne1WVavGaDuKtLRCOf5Z8tTIqY6e9/bh2FNPYe7cuWEhEfA2lipn9HiWqYtjNUXFkjF9zaJIDDMzkUBVCBCoOlVxUONExCnEQdkSOIKqZfK/HlTUAmoVZH0/BcoVagjk5QliVTCgTGAaZFn351cWL5w8Pnt6dmpqbttz34VBv49vfetbePbZZ/HEE0/gS1/6EmZmZg56mEAgEAgAsNbi5ZdfxjPPPIPV1R2bye1KmuXp0vLyB4M021DAEsgqfFaSgBzQ4j61BMqpcEYRfVowWy62CT6lqyKOhJ1h42CcIzIORKKAgEhEVbMokgR9NaaqxhiN4ljjOFafvn0bQGWzgOipp7Y9953ymYoi9K5WqwoAtgzFl5ex1mxqbWNDu0miyUgaN+fUCxQQaCSIREDqWIxTFgclq6wGSlbV/3JQUA6GgZIBiBlq1OfLvTwJzPBrvDFAqkrXbqxc2ej1u/NzJ85Exhw4JzsYDPDMM8/g2Wefxec+9zk88cQTeOSRRw56mEAgELgnWV1dxYsvvogXX3wRg8Ge0/Z3Ps7a+sr1ldWrqpoBsAzkCrUM5ALkqrBEyAHkfh/lAHIwcihyH32SBXx0SkqWWB2JcWLEORKBLSJQQBSQWCrD9G2XWSsbG7pWr2tleRmo+nrVSqWipfsKtu+Xu9Mb25LGrdU0SVPN81xNFKkxRuMkEe4SR3EqKbMwmTICFYI4FXVQ49SoVVEDVcNkjKpYMHJVGAWM/2BQVuXmABggBkp5ggEqF0olANjo9tfeS68M5udOLDTr9T2numyHquL111/H66+/jvvuuw9PPPEEvvjFL4b0biAQCEygqvjZz36GF154AW+++SZuZ959bm2+tHxjqdcfdApJWngPlJnIoURRShPIVdVSIVOBfw4IOQSWyVhRsYBaGLXs2IGcI0NOwVJGoDnnUhER7Vc0brAY54ZOS5JEo1pt7I1tH3t69ozeWq2WDgYDwttvY+lMkcZtx2qqRp0xGsd9SaOIDLPwgCSjgTCRAxKGikOkDqqO1TgUIvWDwWTUfygGPuI0IGJV9dGnlyeDQNCRGmv/JxEAawWXr15/f6bVOjZ3bOakuY1S22vXruEv//Iv8c1vfhO//Mu/jC9/+cuYmztwljgQCAQ+VvR6PfzoRz/CD3/4w1sa35xkfWNj/eqNlWsikg0jTIKFYihKgDIQclYvUfJyzUGUQXXkcchZYUXVQtWygYUzTlScGjhY40DsgMyJqiRaFaqqpCJSQSpqaj592441rhbp27ffBio+fbvXe4kAP4A7MpXFp2+felTnzr2B5eVlmkzjGrOia1FNm2Uat280ilNJIxZDkSOAoVYIGItCocoKMmyQQ8GqMKAy4oRhCAPEKkRlY331rhy9AABB/RwoVaDd6aysd3ududnpuZmp1sxu/RT3Ik1TPP/883j++efxqU99Ck888QQ+85nPYPuFAQKBQODjyQcffIAXXngBL7/8MmzRaOB2GKTp4PrNleXeIOsC6obyVNjCA2MXBXIizQiUQSgjQkaQzTTuSIqXDXJRX3QEg4noUwQ+O+pyzaWSiWhe8cVDGxu6Vqlo3awoEAMYT9/68c9HtYhDh0LVIvzeJQJ9EsC5sXuiy5c1KToymChSY63GSSKx69OAmaMoGolCKw4AEwpxqjJg2KgYJRg/XQU5A6wiDCKGgJXAgLCCuGxxoKR+pTewfw+bvwvK2FRFnF67efPayvp6++SxY3OtRr15y3/TBRcuXMCFCxfQarXw+OOP4/HHv4CFhdO3e9hAIBC4K+l2u3j11Vfw4x//GJcvXz6UY+bW2uWV1Zvr3e46VB3gxyzLtC0AC0WGMvKE5CDy4gQyBfJSnCTIQchUJGciP04KtQRYo2odGQt1FkoWiKwoOQBONB1GnwNrtRqnokltmL6N49inby9fVmwZwntyx/e2awr3KQDnJloYbTZVaOtas7kZhQ4iiSSlMgplEvKRqLKquuJD4mLcsxjj1IlrMPlW+X4lUVJilaJNEBX1SYVEC3EWfygBCrDmudUr164v1qvV5skTx45Xk+S2BzQ7nQ6ee+45PPfcczh16hS+8IUv4LHHHsP09Ja1XgOBQOAjhbUW58+fx09/+lO89dYFiBzOSpEiIjfX1tor7fU19WlXh2Kupq+2HRnDHJWnF2cOlUxAOSllIGRElHnRUg5oPnqt0BwKa7SY/mLEEZEDkVPAkUaj0ad0q3YYfVbabUW1iqVKRRtF9FlW3+42/gmMCHT7NO5TinPjXYmiyzVN5kaKiUai0DSKyAxYmAeSMxdjoWACrGrEBLUagVU2xzlB8HIkkADEUgpTfM8EBUjJD1gTwSdttXh66U//KC9YUlVobzDoXLq82JtqNafnjs0eiw6pFdHVq1fxrW99C9/+9rdx9uxZPP74F/D5z38OSZIcxuEDgUDgyFFVXLx4Ea+88jJee+01pEWP80M5NoC19U5nebW9Is7lCjhVOB9xigM4B3Qz+hyKs5CjIiNQCiAjohQqGUAZRDIhn8otxkgzLVO+qhYR5XBqVY0jslZ84OZEVWIRIalKWi3GPkeiz2Hx0MTcTwDAU8PuQ1vSt8A+iogmo9A0fZuGc0Ino9C+0SxOJY4SFxFRnhERtJjHqRYxSEV9ulZBBsSAEEDsLVmMd5YiF9+BHlCwv1JlLYpxR0Uq6m+TghCx+p0K0rWNjXZno7txfHZmdnaqNbXDKucHRlXxzjvvFO2q/gqf+9zn8Nhjj+ORR86G8dJAIHBXcu3aNbzyyst4+eWXsTaxNOVh0O33+9dvrqymWT4oOwkB6hiwSihSq1LIk+2YOAkZiRejQDMAGYlkIEqxGX1mIM18wZHkBj6NC6JcvUSt5mpFjQPIKcjFiTqn6nKbSdwfH/ustNsaV6vDuZ/V6vS+o09gd4FuG4VWKhXF5RqSuVQBCGo1GGsp7icSJ32SKCKTZZQxC5siClVlS0QQZQJs5Ctti+kpRQTqlMBCABGJkHoJEakWXi38KAxlBQGqQ5GSYBiJQpWG26KAiKour6zeXGmvrc1OT03PTLVahxWRAkCe53j5Zf+Pstls4rOf/SweffRRPPzww2AOPXgDgcCHx40bN3D+/Bt47bXXsLi4eCSvsdHr91faa2u9waAPnwp0RZrWAXDqxzuLS1kE5JvnUBFJenkW0SaQkaoXJ1EpUr/fka/Gha/GNUUEaomsiDhEZCNVByInqpLnIomISJJIjFS0XxOTOK3UalK1Vsroc0vrvm2iz0nGBLq1sXxxHGwfhc4DyFdWdKPVkprboEFSk2QwoCyKKIoilw2IDKfERGRUqbRt9JwAACAASURBVMh5ExBRYT/WsrqWQCQEYf/6LIJCoiBVKLOqQv0NVVLyISjDzz2VTWkSIAoSQCIGiQ9VSZwTubHaXrm5urY2M9VqHZuemorjw22Ou7GxgZdeegkvvfQSqtUqPv3pz+DRRz+LT33qU4ii+DBfKhAIBLagqrhy5QrefPNNnD9/HtevXz+q19H1jW7vZnttLcvyDASBwoFGok6fsrWEomuQ+nFP4mJaiiAnRa6kmRapWRoTJqXkU7epFykykC840nJ6i8ISRblR6xsqqDoFrIg4JxWXVNVl1kqSZTKoViV2G7pRbUhlZUWTRuNA0adOTH7dnzyeelRx7o2JKPQyMDdHcRyra8daaVYE/T76SULJIKJMUoqTyDHFxBlRTvDFQdDiurhNBIUUdUQKOCEUa8XRpkSH0gRDoVAhaNG0QQH2Yi1ECUAALSfORv5+8SuYAyJQWV1fb6+ur3emmo3GsZnpqeoRDGIOBgO8+uorePXVVxDHMR555JN49NHP4tOf/jSq1QO18w0EAoEdERG89957OH/+TfzsZ28eSXq2xInIWqfTXWmvd6xzOXyE6actAo4gRfoUFuRb7pWVt6Bi7FMpp6La1s/z9JEmBBmg2UTk6SNRJxmYijSuZESUlRIVhQUi3+IPsLHCSaKO1bkssxLnFelXSar9vlQqFcmLeZ+An12yNfocTl3Zlb0EWrT0exJP4dy2FbnzAPJ8RdtRS5vW6mQqN48ixxGRISKb+25CNDKvUy0AAwIcAYZAIDiBFBJlUQWrD1hJ1euTlLjYhp/jgrKFIFQEEPK/hkQB8fVJGjHK2xAAhgBZ3+h21je63Ua9Vj8+Mz1Vr1aPpA1Rnud4883zePPN8zDG4MEHHypk+hm0WrfUSCkQCNzD5HmOd955G2+++Sbeeust9Pv9I309a61bWe9stNc7HfGluqU0pUzVciFRgGzRNciCfKODYdWtT98OOwwRIYNQIU4fXRJpCkEmhUjhiogUlAPOV97azbmgWlT2ArBRrM6qOnHqYmu3pG7bUaSVYtWVnStvnyzf9q7NFLYIdKc07mQUCnhzL505g+NZpuh0ZKPVQhN9DJKEksGA8iSBGTDZKHPKTCYiYiLKc/98ApXn53sLqQOR8dYWVWWFgBUCMBRgLsY6VQuliioXQvQSlU1BymQ0Ct/1KEKx389F9fd3e/2Nbm/Qq1aS6vGZ6almvVa9nYYMu+Gcw7vvvoN3330HwF9jfn4ejzzyCM6efQQPPPAAzD5Waw8EAvceN27cKL473sW7716EtfmRv2aaZfnK2npnfaPbVd38rgXgFCq+UMgL1K+iQm4s6tRiJZWiBR+K9ny+UYJkqoVEyY99QiQXpSLylJwEqRLlRJQBrnzc2HEUyOMYVlScE3Ui4iKbSF5lFxep2yr6slGtSqXTkWHT+O3mfe4QfU6mb4FizseWO7eKw98+d47OAVheXqZOp0ODwYDSNKW5uTnOsowGgxanzT4305TzWo2TwcBkUWSiKDLZYBBFUWSYKIJqbIkiyhEzUaRGEvJtIGKoJoCJAU2gSJQ1gXICaDLcz0gASqCIAfj7gdgfQ/1xQBGAWIGICBEUEfwqLxG8SEcuZHwOedhWkI0x0Uyr2ZxuNRtJHN+xRUSTJMGDDz6Is2cfwSOPnMWxY+U64r60GMBYD8rN7b32b25vf58/xtbtvfaPv/bm9l77xx+73f47+z53PvfbfZ/bnftB3sfof8fttv3Vdts77988DO1z//hjt9u//XnutX+n89zf+9z5vd/6+9zu3A/yng/zfQ4GfVy8eBEXL17EO++8c6Sp2VFEVDe63f7qxka33x8MMAxKvDh94KGb4hxeyPpICMXqKGXUWU5b8cLD5iXz80DVp2+HcizStEVVri8scnm5X4GcHGeiajVGHqlaEOWiaq21LqlW/bW1LqtWXdzvy0alIpWNmlSrHUmSRJeXl6WyXfS5Q/HQvgUK7C7Rf7y8TABQSrQ7N8fzaUrdbpedO0bNZp97vdjktYyTQWTyODVxFBmTGZNzGjFRRESRJYqoFJ9qPJQoEHtpYihSQBMwF4LFpkyBGOAE0Bi8+XwCIhHERIgAiggSKSgCISKFUWhUhLvDpdMAMtDNNUjLS61Sqc5MtRqtRqPGfDRR6U7MzMzg7NmzOHv2LB566GFUKpUg0CDQse0g0I+PQEUEly9/gHffvYh3330XS0uL2Ok7+ijop2nW7mz0Op1uT9SnaYcXhZQFQigjT4KDqiOQVYKDwhcMlQ0TFJa5bBJfSFPKBvHiZbkZURbjoFL2wi0rcnNfnbvZ4g+0GXlGqlZVrajaWCrWJc7l1ro4r7isal3cT6Rez93GRk2MWdFGoyFLlYo2lpellCcAfN8vW7ZveQK7jIHumMqF7w+4XEgU2EzlzgPS7a5wO2ppM+kLkhqqyICogjxNgQSI0pisycHFizMVzXCZFerrtCiCKqSYqqIgMqqAkqgoVIkhChYiElUV8ikFB/W/gsT/GoqJ/F8yQZ2AhBQCkBNIRERuGJUSCpFK8Q9kPCLtp2mvv5wOrt24yVOtZmO61azXKpU70jmh3W7jJz/5CX7yk5+AiHDmzBk8/PDDOHPmfiwsLIQGDoHARxhVxfLyMi5d+jkuXryIn//858iy7I6eg3NO1ja6/bXORjf1L67YLuIkdX6UzHf4gZKDny5iRdWRsoWqEyLL0FwBS6RW1ctumL71EadvzTcsBJJMfUVupr7mNFO4zf1EmVrkiJBTEWnGQC4j8oxc7FylkGel4qrGOk0SSVxf2lFVKyPynEzdzk3U94yykzyBXSJQYPcodLtUrj1zho53OpznOaWtFjcHA85qNU76fZNXKhynqcmjyHBKkTW5mYxE2VKEIgU7jEYjH50CSAAfgcowSkWCYcoWCYH9NmsMUESCWKAxEUUAIkBiAhktUroE8pFomcYlnYhIi0W9fTclBoGhPiqtVOJ4ptlqTDUbdfMhDlqeODGHM2cWcPr0aSwsnNmygkyIQPf7Pnc+99t9nyEC3W3/vRWBdrtdLC5ewZUr/rK4uHjHhVnS7fXTtc5Gr9Pv9VTUS3Os+LIc76SJiLOMQuGKha4dCMU4pxadhmBV1TIoVy5SuEJFBFk2Tygi0aJwiIfRqJTRaA47EnUWj5dI7WTkGbnYSUVtbK3LKxUXp6lktZpL+uPjnjdbLSmrbm8ndVty0LE9BcabK4wt+XK5htYZSKfTYddu68bMjDT7vqiomqZIo4jiLKM8jlxkYth8MxK1RFBA/YTQzaYI5FQdqLitAvZ/0YSyoGj0l5I6GkaiGguV1WEag+AI5FQpKvY7gUZ+PyKglOdoREoOvlq3XOC73KYste5aupJdv7m61mzUa61Gvdas16rMfEdTvDduLOPGjWW88sorAPwY6vz8PBYWFrCwsID5+dNoNBp38pQCgQB8j9lr165hcdGL8sqVK3dsDHMn0izLO93eoN3Z6NncWlA5SwEC/5tNyE+JKAsxfRp3NOLcHPu04r9zLZR8f1vyFbEoVkkRqOVymTLSYu4mjYyBeoEqxHcUkrLXrW+QoFRU6g6rbX06eEyecewsOxdnVtIkkUqayiBJpFrIMyq6DbVaLVkrGiaMpm4B7KtpwnbsKtDd0rhPPfqonnvDL3e2eSLv4/LlBzBXdCkatNu81mxqs9+XQa2GapYhTRLEWYYcQORijKZzbeSn3ML39QMRqUXZKL641lKaWqRkNwewBVrMQ0Lx6wjOj4HCifqiIkAdEZwIHBEcAFM8p6jQHYlC/RipQ1lc5HP8BgpWKvr5klKn23WdbrdHTNys1qrNZr3WrNUrxtz5nn5ZluHSpUu4dOnS8L7p6WksLPgo9fTpBZw8eRKH2IgpEAgAWF1dLWS5hMXFK7h27RpE5MM+LfQHab7R6w063W4/szYffocyxCdCtBjfhBTfcZupW4JAdSzqBGCL70xLvs+tZfbjnTqsti3ne/rbGIkieUygE1KlzSIjC+QRfIs+LRbZlkjtZORp2bnYWpcniatY6wZJInG/L2Wrvmq1KkmS6OXLNa1U3h+T59zcnD716KOKp7af87lb9AnskcIdPmifqVwAGC0q8pW5A242m5wWlbnVLOM0ikzFWh5N5xpjDBNFeY6iyMhGrkjtoigKwmhqF4h9Kpdj6EgF7shzAI1F4VO4UqR1CVGRto2IEAkQkSKCF2lEfjKqUVLji43KSl1lVTLkF/02RH4FGSqb4vvPpCw8IoJyo1avNJv1WrNer0Z30dwUYwzuu+8+nD59GvPzp3H69GlMTU0jpHBDCjekcPeXws2yFIuLi1haWsLS0iIWFxePfB7mflFV9NM063R7g42Nbj93tqiaHcvY+WmARTCiSkJETlWFSMvHO/JNEhwpOd0MUCyK9nwML00AlkDFtlqwFx9TWTBUCNRPbykKgXw/282pLZuS1aLoSIHcqFrVyBcJxX5OqXPOjaZt0yiSUXlWKhXZ2NiQUp6jRUOAz5zeTuq25FYFCuwg0dHx0E2Jjk9vmZSoyYyxkR8TdZYNEUVMeUREkSumvZArKnYJkSul6qNLv60jYiWMSHRTvlJOaRF/LCGJCIiIyIyKtEjhRgo1pGSUYIiKFK4fBx1Od0EZnSoYXqrktwupKhhE1KhVKs1Go9aq1ytRdPeFf41GA/Pz85ifP11c7kMcJ0GgQaD3vEBFBDdu3MDSkhfm4uIiVlZWcDehqtrtD7JOtzvodnsDK+qgqiAvy2GECVUoFdvDDJ7fJjhVCJXXCqekjvzYpy2LNDfF6VvmsbKPMosolDflN6y8VSBnHbk9soB2IWCfsi3TtMZPSzHDaDO2qmpNJE5UbWRj5xIfeU7Kc3K6Slk0dJBxz/Iz3etz35dAgV2iUGBPiZbTWyYlOlpYVErUMBubU8RsDRcFRmx95EhEkYXGBBcRopjIDQUqExEqyaY0h/f76NRHnUAMQkRCkRcpGSJEIl6oBDWq8FNd/OLf49NdiipdUrBSuaZpIdWRYiNQIdERmdarSdJsNGqNWrVSSZI7Nsf0IBARTpw4gfn5edx33ynMzs5idnYWjUYjCPSA7zMIdLf9d5dAnXNYW1vDzZs3i+hyCdeuXYW1FncbTkR6vX7W6fUG3V5/4ERlTJq+IMhHnUVxEEaazJCqKE3O6xxtjODHPIlgFeRU1TGXaVp1rGyV/Xin+iIg3w1IdZi2HcqUNyPK0fsB5KrGKmyuMHaYslUM07WiakUiF8VqnYgblWdZMDQpTzNRcbuHPIFbiD6BAxQRbTMeqij/NRZFRVtKgS9fRjm9Jcs6hI0WNppAs9/HoFZDNU2R5xXkSEFVVslEiUiL3n2q5BvGK6AAKZETApRAQiQCSyIEISJHvhpXqFg+R7wsh2OgBFiBxPCCjAiwoohB6m8LRQq1XtRqVCkCqVVfaOSFWs4bJeUyvSsEJtKxqFSluA/EqmBCkd5lEFS5N0hdr5+mIKLYsKnXa5VGrVat16rJ3ZLqLcvrl5eXAbw2vD+OY8zOHsOxY7OYnT2GmZlZzM7OYGZmFnEcmuUH7n5UFZ1OB6urbbTbq1hd9ZeVlZvY2NjAfoOKO42qaj9Nba83SLv9fjpI00xBOiZN3pSmatEvHCoq5NubDqNNdWXXNp+mVT/2SeRI1Sl8yraUpy9H8eOdpOTARfs8H3EOq26VYZm4WOdzM6LkInWLYmxUC1GywlIk+egyZ6rGla35dBjpkhUVJyJOquryzAryiouxVZ4+8mzsGHkCOBR5AgeIQIdP2DESfRLnilZ/+0nnTltLWVbjWi3jwSAyrpJzlEXGJZYjY4zNfUrXGGPyHJFhZ8qULlkU6V1EBFcKMQYQiSvGPH3DhFhoGIH6ayIfZUKisYi0TO3SsOGCUfEyJVARjapRkKHNjkVbprz4SJSG0ahP/fr7FSMyHY1MAfL7iapJEjfrtUq9VqvUq5V4pyKuu5FGozmMVGdmZorrWUxNtbCZsAgR6EHfR4hAby0C7ff7aLdLSbbRbrexurqKdrsN38r17ifNctsb9LNub5D2+v3M+XnvitElHEciTUUpTRIiFR2LOrVobzoxNaVI4yrgCL5gyItTnSo5Yp++9SuqsB1N1W5GnGx9dKpj6VtWWKWimEjVsilkCxTFQcaaYp9GsP9/e+ey5EZyZO3jHplAFUkN2zSm3dhvvdFKz6P36ffR82g1m9nLpGmOmqwCkOH+L9w9LnkBUBdeG27GRmbcUCSz8fEc9wiEZZsl5XHElHPOpdI255yOg0y7KafDKHd3U3542Mlu9yAfhkGvsW0B4G9/+4tunXX7FHgCrwNQ4MkQfeTpp5/o3eMjH4/3fLo/8v3pxKfdjsfjMZ2GIY3TyHNLNzGnabK9o1yLjAbKDlO/BzASiYORBmQdCTS0MCUi65clSEkp+RYXO7WIdAA8F6pu7zaqlJRYSROUGN0rTIFGwRGBVmGqICIiNRuYoCBrQXpzf7d7e3e/f/vmbvet2r2Xgpnx/v1PDtWf8P59qNaf/FtpbgA99/u4AXT79zFNR/z66wcH5a8FmL/++utX21/5kphylk8PD8ePnx6PHx8eDlPOZq+aylS4A6f1fg2ailIYFPYtZZCWV1LKSiqhNgkw1Umaybf5kdrpQgpMDJqUNC/AyRz50DjM3aAJnZBcZQIGXgerqho0k81JqpPoMKnqNAxm084t29Nwkqi0HY9HeRhHGR2esVWlLRh6DjyBLwBQ4HUg2h22cDyyvHtHu8fHdNrtOE8Tj8OQ8jRxSikNU0oTn0yBZk6tGuVMfp0LTMVhCs91CklcW84z8p8B0pkiVaIEwaCeGwVhUDUlSgSzd2O7C1ECKUNLrtRyowBrX3jEVLa/EKtB0xSrgvqio1Clca+kIBpTSm/f3O/f7O92d3f7cb/7cmf0fq4YhhHv3/8H/vjH/yzK9f37n/D+/ftuq80NoNi8/tEBqqr48OEDPnz4FR8+fMD//u//4p//+if+78MHHA4HfM+Rc5aHw+H06eFw+vjwcDgcjycDoiqUqjUL2Ba+vihIAT+NzbbRCylZXpMg6gVBpFFtGwVDlEGaoWRbVMjynXZqENy6pQzPdZLyBMZEXjQ0V5zWZlaucv1WlFJcFIcqdNBMVhyUNLeqMyXJqjoNMuZpyDnnnNMwyGmachoGGY9HOd7dZf7tN93tdt0hCV8ansAzAQpcgijwVz9o4RqI/jRN1G5zOZ32nPcnHhyiwyml2OqSJ06tGmXmlGkaCBhYOHW2boEphqpAZSCp9wRUBSpSVKxV5VJyS7eDKAhDANNUaVi7xAqdWbx2itEcpqZMTYE2KpXg7RpbYwhk3wCn3b1CKTHz/d1+d7/f7+7v9uPdfj+mL3yQw+eMYRix3+/x5u0b/OHdH/Du3Vvc3d3j/t5+2fUd7u7uwc2W2xtAv32AAsDhcMDDwwMeHx/89REPDw/4+PEj/v3v/8PHjx/x+Pj4XSrJrTgcj9PD4XB6eDicHg6Px+NpmgyYpOXrGmFgbO/Jc5rwStqiLiPX2UKz+ZoxNOBU+GEy9nXKWYlcbQZQHZgOT4JmVZ6U4suxHYTMbbHQFIrTCoW43isqNBu7VliyQXSYRCS3qjMNkmOLyjQaPKfJLNtxrPnO/X4vvw6DXg/P7Zwn8Dx4Ai8AKHBZieKvf1/dJ3o4/D/6r/96oH/7sX9RoRt50bmlm3c7TseU8jhxqNE2N5qZE080MOeU7XjAVODpMM0ZAzMl8gPlBVIVaQNSNYgmAINalW6CQ9TsXU1i+0mThp3rECVQUttPyhQWb82HdjCFRr60z5kibF04NBe50u66ADW+qHy/G8f7u7vRoTp8yW+S+ZqRUsJuv8fd3R3evHmDP7x7V2B7d3ePu7s73N/fYb+/x25nxU43gLbXLweoiBQgGgwf8fj4iMfHT/j3v3/Dp0+f8PDwgMPxYFWtL/js+R5CRPThcDg9PB5O/noUESFAdQHM8mslt0nif1izvZyR0yQxW7YozQJNgooVBNnpQgbWCk8vEMqqyAydlMgKhaAZVmWbyfd5qmom5h6crjgZ7NW4OoloTgkFmg1Ec1KdRFKWQackkttcZ6jOdBok73JOx6OsWbaR70zpXzqOo/7hD3+Q9pAEYL7P8/WVZ8SLAAo8XYkCFaLTfz1021ymn05dXvRtznRwFbpzazcKjEKNppR5mmhIzIk5J8ls21FYUrF0HZxEuQPpXJEqmbokKfnTRAbVBKJBgUREA6mkUKVFfVquNBFRgJW1fmWaK9MepupFRjOYUmP1UqtUAVOm1AJ0DlQFheVLQKjU8f7ubne/34/7/e6HUqnPCSLCOI7Y7fbY7/e4c0X79u1bvHv7tgB3GAYwM1JKYE7+Pa3xR/fjAlREIZIhIshZME0nU4mPB3z8+Bs+fvyIh0+f8PD4gMPhgOPhgNPp9E2cuvM143g65YfHw+nheDg9PByOh+Nxgj0arSW7CUxF5DI9txnKEqrFmi0FRAbNUJzolKb6bgTUwxAsP5pV1Q5FICsY0lZtEme1L77OUJ2I7V7VFSgbJM3KXSrOCs40KXRi0RyWLQtb/jRJFkk5h/LMSYrqjEKhYZDj8SjJ93d+TEn7fOeo/TaVdXgCn095RrwYoMBLIPo0S3c37TjvJk7HY8rjyHlKKfEpiQwc+0ZzdpAyJ5owCHNiyikKjZg4EeVBxBUlfB+oUCIg8qfJIGpK0/KkYvAkJBUaQAZRavOjaicYgZCg6qcWrcHUFCq6faN1L2kD1rBuiyKlWohEULDa599FoNrbmEodh2G42+3G/X437Hf2azcO39zhDt9qEJH/YjBTgStzAjODEyOV64QUEE4JiRNSSkiDXQ/DYPfJx3OzjtvSqgYyEdsbn3Oe/ZqQs9irZORJGgBmZMmQLBCJX7msJyL2SS0uhn5wVfgaIap6PB7z4Xg6PR4P+fB4Oj2ejqdOXUJwFTCLLUtaC4BaxWlWbg9KgyeaCttQmFiHpp9l6ycKkZY8Z6M2M7HnQM2mdXi6MmU2kKpbuez7QMtXlmlWTZOo5CgQEk2ZRbIOmFgMnCnZQQgiQ2aeJMuY05BzOp0k73Y5HQc5Dkd5jmULfFl4Aq8EUOAyRNvDFoD6XaJrEN2ydPP9PZ0OhxQgHU4p5TFzOnGSIXNKKU0Tp8RTMpBafpQz+XUeOFMyRZoHYk6SEYc0JIFY3hMweKokU6ZIbu86RCl1qlQpEc3yoR1MiX1vaYEpGSjd7iUGKasSkwNzYfW6lTtXpPaq5NpiqVDJIbpoqyoVAJiI97vdeLffDQWs4zh86cPxb3GLbylO05QPx1M+HI/T4XiYHh9Pp+N0muAfyEVdEi3tWELX1itMVfvfdUVxttbtzJp1KAqUXGnWfGcPzai6rdBs855a2jq1aQqU2dqsoGhSYoMo4PlQg6kpSkwqkhVpYtFsRUFpEhG/tjxnSpKzDHlwtclTkjxKTqck05gLOMf9PqeHBz1n2W7lOwE/2xY4u88TeB14Aq8IUOD5EAWA1tJtt7q0ajTnTPenPZ92R45jAPM08TiOPJ1OaRgGjiKjqNbNzIkzJ+GcAqTiirSAVDgpR95TEolBUszOTdXelUFNubaq1LeyaCJgsPNy3eJ1iHJsczFlymbzKveFR8ogYrL22alG7UlHoUo3LF47g58DqCAQaVOUZBW/pCjKFZhBlWFVDABoN4zD3d1u2O92o6nVMQ3DkG5UvcWPFKYqA5TH6fF4PB0ej1NWiXNjVQCswNIhWqpkFVH00wG0FP4UJdpatMWW7ZVnpzIJKkokUDWQNgVBZN+NnEFUoCm+XQUBT2XLdwKTxoEJqrlVm8SW+6w2LZvqVM3KOpFyBjCRaFau4NSkE2vKAU6WlCVJjjxnSrVIaJomGcYxn04nScMgcRzfeNzJw3iQlJK2qnO5RcUsWwD4mvAEXhmgwCZEgfJh/Vf661/t6hpLN//xj3R4qGpU3mWKYwDzqdq6p+MxjePIkgdOaUqSE58DabF2hQ2UJMngaJW3xeIlSirq9i6lVpUSaYqj/+zwed/mItqANWCqyQBZDqpnz5myqiYlYlI/Z5dQrdyiTpVrDnVp7QZQMStCMqCCyji/D0BiCVX07b39G3/Fu2EYdrtx2I1j2o3jMI5D2o1j+lZOUrrFLeahqnqaJjmepnw8nfLpdJoO02k6Hk7Z9lvasHM2rAKYw7LMKW3klbJQau7Rq88lMP1aoUJEncoEEN+YkpUgpFoPf7ecZuztzHDLNvKaAUpiyhrn22p7NF8UFLna9O0qNl5ztWjZC4kMxlZEJDms2nPg5JQl5yFzmuR0OsnY2rXjKHEcH/+WtKjO+3tJ/9pWncDcsgWA819L9prwBD4DQIFrIIoXqdH87h2djrVSN09TAek4jZzHifOU0jBkjkKjAKmkxJwtR2ogNRVpVbvZFKpQYpJUCo5Ik4pvZbH8qAHSbV6xYqOkc0XawrRRpmbjNupTkZTg1bh2opGPqcVHqkykrGC6CqhN9S4pSBtVannUBrgKWuRS1TFuedjWAsaWWgUABvFub1B1uKZxHNJuGNPX+Hq3W/y+QgFMpykfp1MOUPqv6XScskMP2FaVsYxZrKSmAXuIRu6yqEtTgM29Ff2ElbtSTbsOTIKoKgmISjGQV9J2JwiRQpRm21JA0inNDprUgNTsWlYHLNQVpsEWiskgSTkKg0Q5M2sWkcyaclTVsrCDU7LYB60EOKNAaJqSJK+uPQ0nKeD0fZ0P4yjjbifpt9/0c6lO4PXhCXwmgJbFn2HpAmfUaFOpm3OmausaSPfTxNMVIB2ScFtsVEDq+VEmSSyUhCSFvUskDlZKpeiIYLAUP5GIJInnUNWKiJqvP4MrXM+FkrLlTMFKSD7eDqGP83ZDnSpZUdFMlRa7l0DtdZ83JbZ/CIOWtm/NnW5BdQbIolabv8cFWGeKtfydJ2LeG80j8wAAIABJREFU7cdhN4xpGFIah4GHIaUhDTwMiW/q9RaXQlV1yllOU5ZpmsSup3yapgLJptZZAXvwi6JcBaVfr6hKAXAJlv4x2tmxZuuSug1b8plQaLVi2+tebapCiKrK9G9QyTDC2vnfGmoU2ezhOHrPtqXEXFUvGAImJgesalYOqCJHUZCoZlebOWxaVs7CmkU5u13bg9OBOWWWS+AchqMcGnCGXZtS0rbC9pLqBL6OZTuPzwpQ4Bo12p9cBJxXo1Gpm6eJFrbuac9yn2k/TTzFiUYzkIa1KzJwFBulJCyZEzOlTDmxcBKWsHqZ2eBJyIMqM8WB8275SgGuW7ZUVavtF7W8p+c+UwEnvOjIcp8JRHaiEWlYvaymRk2VuvJcAlUZSl6R21m+dsKRgrxoqbN3UQ9paKBaFefM/t1Sq0AHVkKjWGsfAy1cgV652kCiYUxpTCkNw8jjkNIwDDwOiYc0pBtkf+xQVUw5yzTlPOUpIJnt9ZRPOefplKVRkUDkHwF0kOxVo3WD1JjXgdLeeqEqSXsbdtEmRaHO1KUB208KUog6dAskFQqKPOYCmKJqalNdfZqa9ZOD3KJ1RWmvqCB1aGaHpkRe02xaU5ysmsWAmasl6xW5JKJIk6lNziwiEsBkyUlTFtHMSXIOYHpFbVi1C3AOgwxHAyc/JI3q2tauTasVttuqE7h8spA/U58VcJ8doMDzLV1gqUYBwPaNZgpbd3oWSDObXSs8pFQVqSSueVJJLMyhSsPabVRpfMl2sWrXYUoFosXmbbe5UMBTEojZ50XFbVIFcyhZ9ercs0A1UBa1asByJRoq1QCr5ehAO93Its80ytSB2djEMLW6Dla2T43e8nW4Ut+Hrr/YxYCahA7AduMoEYYUkB04caKUOKWUrEIsJR4SxzWdefZu8QVCRDSLaM4iWbLkLJpztq/VyFmmLDrlUz5NOU85SwM+oFGQ1kzW3tuqZRxc9WkPyehXcgm4Bkoiz2HaG/jeS6g2yhNEvp3E5sBBqtYnUKjbrmHTaqcqUapwV4HptqwQURY7ns8A6DlPJc5QERAbRDUOQShK1exZUYn85TY0vd8UqYTaLFatq01hkao2U2bOUhVnFmYWEcmc0pPBOQyDhl2bUtK3b98KALyG6gQ+PzyBLwRQ4EqI/vIL/vr3p6nRsHVLfjS/oziEIfaP3onQHKSSM1vV7mTVuzlzZk6DCGfmlERYmFMS5k6VCrOUewOqqdLG4hVJxKYgW5iCfFxAU4nJxzMzq81jlapKlYi55EyVW7CinK8LBorly7aN1C1dVf9yb/LcqatU1Thztzki0KHaKNVi/zZHClZlWkEL2CcUzcEKoBYuqbN9Blf21zkwF2PU23kNsv2z5HOYiBIzD2nglAyqKaU0MFNKydo4ETERExEzExMRMd/I66GqEFVVERV7UQej5Jy1wjCLg1Inh6TtYEQLO6D5wOvh6Acx9AqyHV9fCWtj1P+11hT0RJ+rR3KlWdo7CzasVjUYug3bKcsKS39Pu46cpf0b0b+4GgKiOA1IAHH7V/37ONGeW1tB6blMKd/ZaWqTmByOnEVEvHo224EKlMOehYq00CQlUbFq286iJZEGlJk1VWC62swswiI5mw2XJ39NKck0TZKGIU/TJJySzMH5yKyxnzMOQ0ipzXOu2bUXVOdf/qL45Zfu8dx4Zr8I2L4YQMsbvoIaBf6MLZBGfnQOUrnP1BcbTZzH0UCaB5Yhs8jA8zxpSsKtKtWkTP5qsKTErBzqtL+vcAxlqkxccqYaeVB1xalNztTUKcf+UMudslru0yp6G6D6VhmzfEkZnTIlVlKi5Zd8s3/aMIEpoGocLIVIdW9p2VO6CtY1xVqsYGb7N3zTbn/nDjrfarPMq/aAbZ+TJThjTGMXW7O931ngzuf7s8pEzMQGVibYNXGAlpmJGvD64QdERfuWk34o/sBCjbftMa72Wpf9XP5/qWsrv7FP+GhEvYnB2gy2V1URg6GIGAi1vqqIdG2SVWyHh/2BxEFDtPjQWgDRf0d1YkCxn78GVS1jVgHpOc2+P5RjUZMiwNx67RVltV87UPrWk6IW/Z5AEn+8BktxW9Yh2n6lmFXEKqBFacKVZliyQqYwA5jVlo1vVKFMpCJaD0MoVbetfWsFQlJymqISSjMgG9AUISnqsrvXTJlEk722ajNs2ja/yTwJT0mmFOA8yWkYuuKgUJwtOM/nOe8V+G98D6qzjS8OUOBKiM7O0gXWbV0AOAtSr9htQRrFRnonFPtIc8485oFzlyc1e1caVapRxWvJ0SSUkyhzq0TV7+cwLTauqlmztvWFlaJQqI5BN7ZudWEDoxUhzYCqRYW2QFVmcqu22L4glEpfV5sFqkQwsFOxfwtk697STq1ugrU54KGD6Ey5FkVrT8cCsLCfYtXW3QQtoVln9mwtlapdV4VrXXV+GTKH7vr626Fnxl2b3T13Yt4Sbts/yep0a5eurQEhYH/NcbuhLPtrzz3Gz3dGXRZFGipxBZCA/VuCujVMSS7hWSts10C5piprztJAW/OWriwbWNp8t2AJWuxYgoiqEqgDpqoKrQNTiDSLwbCFZdnfWfOamqn7SjKKoqEyZg2a1twoTxKJvZvMIpJSppwl1CYzS7VpLb+ZpiSnNElKSWIfJz2yRnFQB06vrD0HTgC4bNd2uc7NZ/dLwxP4SgAFzkIUWFGjwAtB2ihSkTd02h05TxPfyR3l3WT2bh5ZdkJh70rOPMjAE09JJPHgajRzTkkSq/m7SVg4aTJrV01tzmFa71s16RW8TMyiLESJVblXp2BlYg2btz2IYQFUb2utXvWiIigzMam2KtVB2kKV1GGLZs+ogVQBZigpOCAXedUGrAbSUoV7Fq4olm5VrkADWACRbwU2IQsUEMbYDdj24+tA6rt1/mxeuke3ZsQaEHWx9iwuUfTCebMBt3NTtiG7qSx9onZNAcN+zQVEO1W6CdAejmVs6VdXkgbXJjdpa1yEZN+GAGWTrySIim189nuNb0AxWBJEXVm2sIRfk29HERU1SJJoozTDko285wyYFX4iQsyZbCFpVaZY4UUWJiFRiUraVrW2kGyhycpZHJqZsrCwSNJMmSVzluSqc8oszFkGGfLEpjLDpuUj6ykZONNxkEd+1OSHIDB/0msVJ3AenABwreq0p+PrgOyrAbT8AFepUWyCtOZHDaQ/+/W5HGkUG4kInXY7VhHaTRPL3R21eVJVpapKPWeaq8Urkri1eJMwi1UdJWXhFqZFibKyVedaAZI2FbcxnltVqsptYVGz7YUXQFVibkBKsLmq6usQE/wsXYeqb52hFaiGaiVVZbYcpudSG7XaKVQu99BiBV8BV7NbVwCLbaUaFq12/T5jNiauZ8/VCnSteUOtrt/34OSN9tVouy+BdTG3+R/3wv/DLTBlo317sc6ebZdYgWEzvlWttAZGHxu5zzpmqSyBJSB9zgVIelFR00ba269ljt+bahSrqBO3hAs4W1jadShKV5KAurr0vZ1iOU0NuFJTPNQAk0jqtpRaMGTrmMoU0gLBqMylYvGyUKs4haSFJllyM7P9R1qLNqCZUs1tckpFbRKRtvlNfnzU4zAIMet4PAozaxQHnctx7vd7/R9UcAIbeU5gDs6159IavzLAvjpAgSeo0ZUtL8DTQdpW7cY+0nx/T5JrnrTYuyuqNHKlIgNLyjzINkxDmYoyp4CoKCsLi3BitsKfClgvImJlt3fZcqf1XrUo1VWgegGS5UE5TjiqVbtgtn2jDlKCsjmyrlSV/eAFUFOkRFaSU9Wq2uG9HVgtl1m+29QrcxvVWvKqa8rVn4YWsABaBVuUZelbB62jtQFpGdM9VwHPyMPW7lX4rjyT8fM14dN01r4qSGth1TNCG3u1xkJwBpAuK9EVxRkT6/uU/CPmMCwjOoiSui27BUZraQ42aEDY9BVAxs8+V5INJIuaLArSwKhlTAfKmaoUWyOKfAA1G7YqS9sh0oCTSCBRJORn2BJExfOfjdJcA6YYKDOFajWp2N0TUxbRBpAkdi/ZAGnwzELCM6W5Ck1mYc9pltzmhtqkR9aS30xJ47zalJIuq2qfBk5gc2vKyjPZPmVfH17fBEAjXgLS1tYFKkiBdWs3QNoeyNDau0tVOrHu95RzZskDD0NmFaEWpkPKLJdgymLALNYvs7ByZ/WKsHpVblGZDlqlAHAFKpQ5LF9Vq3OBq04vTipwhRIRK9v2FT97dw5V9n2hzTWDDKquVrko1DlY/b7kV92OLXAF9cr1ImDt734G2XWYxtrxmVs94g3gds/VErxAhecqTBfP6xyaAMC83v45I3KBa+0rw1tftoFme78KwmbuEoje6TYu1XEzMHbQtMEFjj5sG5BNm//lagtJGxt5SgfjHJQEETXLFuSwdOUJIQ0bFuLfmNLC0s+mNVhaBW8PSR9PJCph55oli6okHZhm01IZ29q5IsosrTXLQpJJqhVLZLA8A03La/bQJGadpiTsuU06HPQ0DCtqs9q07QEIW+CcW7WAgRNYsWu/M3BGfFMAjXhKfhQAtgqNgHMgtX2kcSBD5EnlnYH0dDpxzvcU1buRKxUROmfxikN1TZlqEk7CLCKsRk5WFia3fFWZDY7MqtLbvqFG1VUmO/wahdpbvjZGVZl7kFoetFWsRm9qoOpVuGwq03OqZsu2ahXUghWhWLnYtTafI49ZYBuQLEq1r8TdAizQQhYAFqDtiomqNdzlRDvg+pqzwqFOFrZA7XKYxUJefz6bmNvEq/EKRUSdvboeKyqzWbDNodbtHisgbFRpA0S7nW9Nofm2kx6MsUYBIXARkGUtU5JurWoLSZGqLkFQEmgoyg6U9tdeVSVBS86SSKEipl5JA5YiBsoOlnatAU7xNstlup0blmynMG0MEUnYs0QqvR3LQiIGWbdktShPEcokzAZNcliuKU1m1nMWLTNrm9vkh6QpPeg4jvLR1WbkN+MAhNjH+VRw/i0erm88z3kuvkmAAhchCrQfVL/8Avy9r9gFltYu/vxnTA/99hcAeIoqvROhtoJ3J3uKfaU73VHAVGTgYVjCVJLwoAOpCEsAtVOnAVG7LoVI0gBxBajUtYVaZYOn97GCroQqhf3LavnNqlDtWlWtoKiBaSjWgCMr24GF5Z6owrWBKQKwXrFLSjyHabGDA4S1veYxA5SlyKmDpTooaaMAqbdie/iivG+5ocv5zvVnWJfQfZVYs3P9HZft87xoc0/+IbaAYDt3pRBIfWxRi9ZY7FyDol93a1Q49kfnNe0GPIevltOBWmgGJIUqVElIi/VK0KIoG2gaqGeq0q+FoOQ2LBnVLsJSCFogSSSk4nDUAkcNC3YGzLKGgzIKgMKiJVecncrMLMQsE03KK9CcJtawZ9OU5EhHjX2bRz5oW0n7yKxPUZsA0G1Hub9X/LdtRwHWrdq/AcCV+znrE/xtguqbBWjE9bYuVguNgPP7SAFgbu+uqdKSK3VVOrd4VYTafGkLU9WRwuZVFYoCJAOosEpiHZTW1KkocxLlud2ropySkkPW2kxJOjx7teq2Lxl/W6gqawDRr8P+RQErsalIZaiS2cl+YlGA1TaauGL1QiWHZ1GtBaZL5aqwsuQ5YHsFC3TtFJC1J6WFpmpsqyxzsAQuwD6nU7j16Zop1Qo/drhuPotzwLbtnzPWlOey0Md+kjKFVFyF0hyUNnJh8RLEIev3CyA2hUFAoyRjDa1qteQhpSrQTkGuAtJgOleSDslWTYb1KvD1TFFqgBJkNq7nK92CdQj6mLBhSUQrLOO6gSWJkJBGlWwPSctZEhU4CpFIzqRU4Fht2cyWy1xVmRMpsW054aYQiMhgmXMSopPOoRl5TWLWuUUbarPNbS7VZm/TAnFy0PY+TuB5BULAtwvOiG8eoBFPUqRnio2Ay/YuAOQ//pGmU69K87t3JLlW8LaFR3citAZTVaV5znQc1XKpkngclOSCOgVAa0C149mVVarlq1bJywmJVGUGUleFLVShrEZEVlaGOhAtj0pNTpVatQp7duq9KhXFCpDxMGBaYEzseVEt1b9u0S4Aq4RGwWIVssAMtEBRsyV3SapWBlVVbZmHMq4Ba78FZqWYqIHjMrdZFWtpKXCex2sp0XXlGZCLkJWxXYERtYArjb0tu1CfPSxDLdrZCQE/GxeqUbq+auGuwlFIQWV7SQfIto3sdB8tkAwFCVeurjobRamtioTNq/dEGjlLkJ/lY3lLbZSlFSL1sFRaqEuWjKzswCyWLNs1kHUNmAD0nMpkZjlNpMzZ92WSznOaRKRr0HyMay8IKpW0KWns3Sxqcxw1/WupNoHzNi3wvBwn8O2DM+K7AWjEU0EahzEA5/Okrb0LXFKlmeTdO6oWr1CfLzWYqvQ2b8B0HE2lqo40iLAMQiLCOgzUW71KJXeqSnoGqOpqlNtr2zITti9VlWqgLCB1m9auDZSGI6VWibZqFQrqwYoAmec9K2hX4EpgVOWqoAVgV4A6bzNYsXmHDWgB2I+narD1hhaqgPl/7P29wu3H2K0B2JaaW70+oLuP22ZsQ9Zriopi+IWdnwBmOc+zW1XOFAmFddtV2ZbxHVzj/apKnI3xfoMiKVUbuQEjXPmeh2O0zQFpXnwoSSiosXsdkujB6LCUULcFlPZU9gqzKE+QVhvWYNrau+RgLRasQdLW47BmRSi7MvXrLWASi2QijVzmRNRZszRNyszCE+vEXJTm6cTKTU6z2LPM2kHTlSYzF4uWHZpXqc0VmxbY2sf5Y4Iz4rsDaMTTQIqzBzIAS3v3Z/TbYID1XGlr8c5hWmxeuSO9E4oCJN0L5WyKNKzeuTpVHSlyp6oDzYGqapavwdIt3+YaSUlcmaYEKio1gdagmpprZSVWZrV/vDNK/tSsWA7wFbBabhWWkKLWCi5Q5B6usGfPxoCJWzCqdkBVV6Ngn9MCFVrylx1oAaBCFavAtY6SD40cKTs0e/hGtPlTLzzy8c23nXbP3jYsP7MC3bBtZQnH0rfIdcIgCABCTY4zxpcxK0CMfgnozsFo7xftKHCE2o6TOTDrXBHLqUZuEoB2kBSpc1sw2r+ttOYqPd8JcnCGwoSpViWxPrNmA5C5ue5gmaGhLrNfM4sgkwp7/nR2ne16AUyiSSOXSXTSucoMa5aY1apnWaMQiB5ZmZf2bA/NatGey23WbShLmxa46gCE5vlaj+8NnBHfLUAjXgOkwIYq3cyVZgL+hKfCtC1AErHagrnVO45xXXOn6uAMu3cOVKj6fSLPoVJSO4IQSUmUOSkoCpOSKgkrQ83mXYMqoKS2ebUozt4CVkpgUrOBKRRrgBbKbrkWmM7hWiA6s4UNkA5QFAVrsOQWsmC3XVvQOtg62AKmar34qCkYKnYxgA686MfA17C1uPbxDII9LBfPZp83PV92e70CrSM2ComWec24lbaSVjqbl2aQbEFo7zoHJVALfChOH6pQhCBUYwUjAFQ4is8lB2bMF5EK2JmqRIHiHJJxbzZwAWMoSljuMUM04BgWbChSyaLErkI3YAnKarlLG0PCkgnKZOBktqIf8uuJsnJmAZn92gKztWWJSNtcJhFpqzJbazag2RYCPQWawD/QVtICy9wmcMGmBX434Iz47gEa8WSQNpW7wLYqBZa5UmAG0z/9CYt86QWYigjdyR3JnXRWr+6tzwC6o2r3KrVADYU6hiotEFWqRUnCqok6lapKmkxJVqgmSiqsoVShxKKMVFQpeX6VNsBaFCtcjbLGtatW1PsKVxDYt6icASzmQEXYr9yNQVGOzfvF33sDXGrXaaELFCDHMxPwtSWq6pxD1i7rdTuvjQLptSg83VKmVPOVGxFwm8/rXd0VoEpVo1W5Vgja2gG8HobxQ+sMiOW9CKY8xX+2BoxolGP5za8AcxWQEIWQWbYNJMv7tfeuOgvAXVFugDLsWCUK9UgOXBHK0EwsRFkDlkSilL1St1GXRFkzcyn6qbA0SJ6INBRmC0wiKrYsRQEQs9KBtbdmWR/50QD6FGiOo+If69AE1nObwIbaXFbUlmdmK753cEb8MABt40uoUqAWHgHAXJm2OVMAWAWqFyGFOpVmr6mK0ByoO92RzIDaWr5R4Wt9S5XaQjVyqclVa6tUzf71IiXPqbZqteRPDaxVsSqIG0B2qrXeE2sUEKnbsh1gDXSKTcjC3qe0oUIUC6iiALF8U0oLyAJdhysXoDZALONN2TZrli9uiWemU5idGi10XK/OnUXzM9qktTNu51HgVm/8HZs8Z7dOc0ZtgbPOD41vVabAIFkKlAJwdQ0V84gXUPS+bmyAUAJmm3CEwpViC0hI7bcdK7GOb18JNemANTC6Cq2KUkses1GVkbMEsjKzIJOuKEtF5C4bWK6pSyLSqJRtLdkWmHxiPa4AM1RmOg7CbPlMYta2CGgOTACY5zRbpQkAfUHQTW0+NX5IgEY8GaQbRUfAM2HqyjRgCgDXq1MDamv3bilUVaU2hyriNrArUVOpZv1WiA40h2qo0xTXLKwwwNVCJaVtsJb8I4l47jVU6zpciRtYtgAtCrbkWtFYuAbedi0GgB6qKLD1PGnbhxa6nqMFevU5vw8AxzMxg1wDzh5+pQq3A2cH4GdHY97qvHFehTs7aL5CddYeAJz19feeY2zaClANfr7FJaDY9bmybcAWICwWK0EjNxkKsgVoaV+0LSCpOcMACFOKINoEpeUircCHSJQAJTHFmG2sK9MelkRTVZphxRIpNXYsM2ubwwxLdk1htrZsAPNalQkABZozpQm8FJqLoqDuWVqLHxGcET80QNt4iSoFng5ToNq8QC1AAoC51Yv3gOSXATUg2hYlLVWqKdNxUAdshepCqarBMXKqmKnVAtZiAxtYkyppgr83M9RVZKpwDeUK9IAthUzNdpFrIGtDq5otucoKW7SqFqgVtUBU6jI68Pr8YsUyryjS+tzMFSPHWC7/WX3+5vOeGmeUqSlJh6nMPuTmYPTGqjjFioECdKW9jBMvLrI5UsZUtWhKNUAHBNCKavS1r4FjrF0LeCogAS8qKtawKLLDOPKSRGrWK2kBZWu/AjpXlZGzBGXNDsNWWbawJCI9+T0RLdTlvOgngPpsYKak+ADMrVkAWCsEAlp79npoAje1eS5+NwCNeKkqBS7nS7dgCizVacAU6NVp3D8FqCJCe91TFCUZRHuVqjrSqEo6g2oo1db+VTWwdmrVt9O4Wi1gLfeqJGoqFt7WwVVBRbnCxs/Vq6lfpTQDLABgBaiAFfMwGlUKoMIWKMCN9hhToAu04LWXOqYtHOr6CmRtfrkMxTuPC8DUDdBG0IUPLqwANRRh02LQs84Kvjoh4Nv3NSC0txLtxvhaAUQAmAPQ9nT6NhZfswVmLVRqFKSP7wDpKtJULWkoSaIZJLNBL1NW9m0rNs7a4KA0yPn2kUZVTmbvdjbsqYFnwJJO3k4nnavLAswj64EOFYxPACYAzFXmVj4TMJUJXIYmsMxrAje1eW387gDaxueweIE5TIG1PaaAwRRYV6fW3tu9/+Ftc6ACQAA1ipIuqVTpoFqVqjT3qkK18reCdmhAG2A12DpI1+CqSqFck7olexawSsCAJWRtLBCFRK2SBZawNagCbcEQSvVswLj020U3JvaEJjSKcQ2m3D4zscVl/gwtlOqLYllP1AO0FgSVkXWP5wKapkxzXb1CsB3TAXU5pva7zTuDol0vlCMCegs4YkIF3zogrb0qSc9rLiFJpBP53AaU1t6DMSpiI2fZ2rCtsiQijdzlWXXpRT8BTACYAzOlpP8Hy11uARNYqkygh2bs1QRwFprAzaJ9SfyuARpxBUiBJyhTYGnzAk9Qp16IBPR2L1CLkey6V6h4Z1ZwLUoyqOKNfVKdg6rqjkYR0l0PVVWlYZAKQewQYNUGpBWi18NVVSmpw9Ch2VrDUCUMBq0Ap6rSACBsYpuTirXKCsJg103Fa1Nk1BYX9cf3teBNCDXYbFnZKBCabWUhO+hvOa42vcy23YrVLSyhClH+W8YU6DXjunVciRJgUO1ACMxgCDgQra0tLvKq3Mmt3mIVZweY2atTmZcL7GCN2lmtAU4A2WGIp0ByBspqv7ICR5Q1Jvb+BpZH0hPPlOUGLPEJYK+QbYt+8BtwTmECBtA5MNcKgIBtlQlca88+HZrA7xucETeAzuJzwhTYtnp/RlWsfe50HajRt5ZDBYBWpQL2ydZCVe+kjCtQVSn2b8zZVKuqhJ3PbxTr3AoGHIAbyjX6VwALAGRbbOq4ULF2vwXaAaZEHbYO1QBuUba2iIG1AW+s3f5dt7lKVaWU5s9CWslnLkF51deabdm8V1Tirn8Ly1yVkgJFZxqIcp9PXRYPNVW3U815Vhg2StHHELVQtP4tMNoaVT0CQPZinfIzrAASALaVZMB8br32ihJH4DQH5UxVxp8JH+07GFpYAkBYsS0sAaBVlwCwlcMElgozgAn0ucw42AC4zpoFbtD8XHED6Jl4HkxxVQES8HR1ClwGKgDEmb3RFirV7rehOleqALCmVoE9dqodWAEgFGtrBdf2Cldrc+WqShgDoj14W8imUJta7ds5aAc0EG1hWBRoQgqANdCN9QK8cV/gC6AdW+ZjsEvM22uk2RpdvLCAqMQcrFM0o7Fjl2OrYp369gAc5hAEWhB2Ywv0oEA2ULZFRxOqsgOwBkZEO5HbtBWO1terSJxizanMn0MyfsZqvVJt70BJChywpipj7DllCVRYAsBCXfoZswBwLTCBp6lM4MmFQMANmi+KG0CvjJfAFLDvLAV6mAJPBypwPoeap4n+E8A5lQoAFarvLK/aQFVE6A0AkTsD4Uyt3gGQBqyAg8ut4D2ATbgulGu1hXVQ2qGBIIA1yJb3WwFtgG8OW8Bt4ubvycYOM4gCc/jalKow01xFNvPWqmr7tpair2XltgCdautaQVEDxzl4c5MHDYU6h2C7rsFwat+nALGQNrkdAAAIfUlEQVQfZ1BsQbwGRgBYg2OsRUR69Llzu7VXkuuQPABorVfAQEcHVuaDPqKCEghVacryU4ztYJkU+K3AEvDc5Ya6/CeAdEUOE3geMIFZPhO4QfMzxw2gz4hnw3R2+hHwfKD+jN7yBbZV6iWoRtvc/o2cKrAO1laxxpiwgnFXIRl9c7jW9oCotYWCBVqYGmQBYK5k7XWoazpMh5UxsItuXPQPpW/ooKfbgOzGDDOJuQbTc+0LhboV03rz1paWefsMekCbF10BZb02pTqh76cpcqYViG3/NBs3ByNQlaO19XAEDF6nUwtm72/t1RkkSzuz4tHWDes11qyKcgOUs5wlUG3Y0n8FLM+py3OWLHAdMIGzpwJ1f8dbcYPm0+MG0BfGlTAFrlCnwLbdCzwdqMAMqr51BliHqr32RUqAqVXgGrAKAW8wV60xbidCwD103yjaBqTFGhYh7BvwdTAd7Xo0tdqC1l69v4HtruufwbRtGyrYhjWAYlyxcRFdV6jPGSM/g4U7Z+umCj2tz7fxp8XcqR0z9QrTrnsgtmrR7gNop24cM9u4AsdTtXMbCOLgYxurNfqKYjywAg84tgCcqUngE5j5alAC1XKdF/kAK7BstpQAFZbA84AJXLBlgWerTOAGzZfGDaCvHM9Wp8BFoAKXFCrwZKheUKr2ulSrQA/WsILbSuDom8M1lGudOwNso2CBNZBWJbve38MUMFXbvl8/zhSuXTf9DtQWwMASinY/Ytm2EhugfY1YgHHe18Vp0TZXnMe4ntp29utjnwf1CPiEWrT+HpodGAG0ynG1v1GQm4BslOQcktFfIVmtV2AFlI2qbNuvVZbAeVhGe4x9ksIEtoAJ3FTmF48bQD9jvEidrlT2AtcDFagqFX8GpofnQRU4B9ZMwHs8Fa5AD9BrANvO2cm+qsy7DSg2sAWAfay1n8FwZW5rJ0cEgCPa9+znbrXt5t0ov4cXRAXaPI7NmA0V2kTAJKIFYB1/XMylZl6rFA+lbR2K5T0f6++hLdiZ/1zXABJoleR1kAQ+lLYtUALPgOX9vRore3UJXA9M4GzFLHBTmV89bgD9gvEaQAWuV6jA06EKLHOqwHVgreNctQIIO7jt6+EKrAEW2IIssAbadr22LWALVOD2770NVGAPVaV98+cXEF6bu77GMj6rAj3Xz0vYdnMOPUgPpf9Qx8+B6VHmPfZQDyi2c9v3mIMx+tfgCCwB2a43hyRgtqu1/bbouwaUwHqRz3NgCVyrMG/A/F7iBtCvFE+AKfAEyxd4LlSB1v4F1it/gXWw4k8o+VXgPFztureF2/7/aK4XkLXLhZJtx67C1jvmcFtTuBFhKZf+UIt36/PncS0oLwF3K9aAuDruDFg75fkY49t1zTLdet+5cg0g1r4eivM5c+UIrMMxTuip/b7WzG6d969CchwV/wC2QAmsV8QC2zYs8DRYAq9jyZaBtw/yrxI3gH4j8SpAvVKlAk+DaqtWgR6swPPhClwALIAtyAJVyUZcgm3EFkTfLNZ5M7uvg85B7xxUt36Gl8Y1MJ3DbnX+p9pWx3/q7tdAuPYzXIJixMcGYmtwtPZtQALASyEJzEDZqErg6bAEnqwugRswv7u4AfQbjdcBKla3zgDrUAWeAlZgrlh/XvRvwxXYBiywDdk6L67f1b4zsAWWwAXmsHvXz3nnY1bmAU+H6Ju1gVfOPwe/eXxaabsKnvM5AZ7falsLwq11WyDWOc2aH9q+dTAC23AEtgEJnIdka70CvaKM/nb8NaAENtTl9paSiBswv/O4AfQ7iScCFbgCqsBl+xdYQhVYghU4r1p/xnm4AluABc5BFgD+s2k7B1u7f9f/3O+3ATmfC/T28lpcVp/vVlvPrbkWc2jU+G292eMcSFub9Jr3msPQxv62CU+gQhEwMALn4bi2RgtI4ApIXlCTwBKUwGVVCTTK8hVhCdyA+b3EDaDfcbwqVDfs34hrFStwCa7AGmCBpTUMLCELLEELNGoWKC9z4AK9so2YQ7dfcz0WII54399uwflLBc/h92F93Bx8fd86QIcZ+NaUIuBABIKHWFONwBKMwBKOQG+1AktAAi+DJLAEJXC1DQvcYPm7iRtAf6B4BlCBs1AFXhOswDpcgRXA+sscsj8vxta4FrbATN1GnAFvN7cB7hzG89iC85eKOeTm8c/meg1+3VoLENabLcheC0VgqRyBFo7AJUAC65AEPhsogSfCErgB80eKG0B/8HgmVIEXghXYhiuwDVjgWsgC50ALnIdtxBp029gC8DyWavVP6wOfOOSq+MflIfNB2xZwH2sAbGMLhhFrUAS2wRhz1tbagiOwDUhgHZLA5wclcIPljx43gP5O4/OBFWjhCjwPsMB5yALboAW2wPnn7mUNuhE/n13nfFyC8teKS7Bbi4DZ/5wZM9zf27obQGzXWYtzYATOwxG4ApDAtZAEbqC8xRPiBtBblHgBVMsSl4f8Yp9jG0VM87gEWeAyaCPOAbeN89D88+btOSB/S1GAB6ywbgm/iHMQbOMSECMugRHYhmNEX8QDfE5Ilsm3D81beNwAeouL8QpgBa6Ca8QvHWSB1wFtG9dCt41rAfy9x7UAbOMaGLZxNRiBp8Ix4sUfbDdQ3uJS3AB6ixfFK8EVeBJgI35ZgBa4DNuIp0J3K54D428hngq9rbgEw4i/zRueB8aIV/nZb5C8xUviBtBbfLZ4Rbh2yz5/6i/28te/r65xLXjPxWtB+XPFtbA7FwsQlo6/+Nq/vGT5V/9AukHyFp8rbgC9xVeLzwTY7i1ef8lflpd/XwfyWrwGpM/FJtzW4i8OvF/axl9WBr44PuuHzA2Qt/hacQPoLb7p+AKQ3Xzrr/S+32p8lQ+KGxxv8S3HDaC3+CHiK4L2Fs+IGxhv8SPE/wfDsYY70uA6IwAAAABJRU5ErkJgglBLAQIUAAoAAAAIAEFebEb5Akzo4gEAAHYEAAAYAAAAAAAAAAAAAAAAAAAAAABub3JtYXRpdmUtdHlwZXMtREJNUy55bWxQSwECFAAKAAAAAABBXmxGAAAAAAAAAAAAAAAABwAAAAAAAAAAABAAAAAYAgAAaW1hZ2VzL1BLAQIUAAoAAAAAAEFebEaIQQwqjcoAAI3KAAAYAAAAAAAAAAAAAAAAAD0CAABpbWFnZXMvcmVsYXRpb25hbF9kYi5wbmdQSwUGAAAAAAMAAwDBAAAAAM0AAAAA"} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/command b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/commandTrial b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/contentMD5.txt
new file mode 100644
index 0000000000..4ca5998e41
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/contentMD5.txt
@@ -0,0 +1 @@
+YmM1YmYzZDUxYmY4OTg1ZGUwOWQ2Y2Y4YTc3NWUzODg= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/headers b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/results b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/toExec b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/08_NormativeTypeCI-DBMS/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/body.txt b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/body.txt
new file mode 100644
index 0000000000..7adbb52d21
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-database.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"relational_db.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/relational_db.png"}],"payloadData":"UEsDBAoAAAAIAFdebEaKXpA6QwIAAJsFAAAcAAAAbm9ybWF0aXZlLXR5cGVzLWRhdGFiYXNlLnltbK1Uy27bMBC86ysWuUtx0JsOBRKnQC6ti9p3YkWuLQISyZK0Dd/yGe3v5Uu6pB62G6OHItbF4u7OzA7HjjZIFIq22uiorQniQD7wlxpiLgXdu47ECftOPIgFP0e1+FQUkfgcIwmDPY3NpbG+x6gPVMaTo1AqjNhgoHM37mNrfQ2b1Xr5CJvluTLzPlQLfhJLuf72+H39stoUhaIgvXYxdyytiahNgNgSzJyQOeG8S1WwdutjqAuAEu5ua/TWxvoG592/hp6fvq5vDxXGKhK5K9Hm8Sqdhep5dCOdA+v0jKfE1tu+vur7wYrGloulP+cjgCdGyKvC1vrsQJAt9QhoFEh2hkwEDMFKzbYqOOrYAkJSXI0QGx4aLmCSBIkZPDlPgecDD3R2pyV2MN0hU2EEiQYagh4N7hg8cbY2JJ7mxEMjKnNlxIEw4i7UI7WWaZd73fN4uPfEV8/bYSdUUzmzy13OW0c+apqnVCP2gfz0Cnn/GkL0epxJH08/99qTqmGLHYduOr+yMe0eHLE5HSRMQCntni3jF5UtnRdG1XOSmASHNM1aHNt7tF59lJ4J7921pdu9EsnWHLTisja5+Pb6a/Tm7fX3ZNzpUirn/2+ZmjOyI/8fOhkMrIFjq+UoznCMuxNvfbaNxRy0JN6g66Bj/4i12ly/EJb/Nj7IvymqCRPsNiub5AyUI1Sfsj2xljm51z+9lNwL/iGdodVODAqH3stCqF5y/lcmz0l02OhOX6V3lCLIKGfZ/Qnnsnn+d/gyNhV/AFBLAwQKAAAAAABXXmxGAAAAAAAAAAAAAAAABwAAAGltYWdlcy9QSwMECgAAAAAAV15sRohBDCqNygAAjcoAABgAAABpbWFnZXMvcmVsYXRpb25hbF9kYi5wbmeJUE5HDQoaCgAAAA1JSERSAAAB0AAAAgAIBgAAADKLPbIAAAAEc0JJVAgICAh8CGSIAAAACXBIWXMAACJkAAAiZAEbek9wAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAABV0RVh0VGl0bGUAZGF0YWJhc2Ugc3ltYm9sTqEkugAAAA10RVh0QXV0aG9yAHJnMTAyNMjx+aYAAAAtdEVYdERlc2NyaXB0aW9uAGRhdGFiYXNlIHN5bWJvbCBpbiBtZXRhbGxpYyBzdHlsZcnBa40AAAAhdEVYdENyZWF0aW9uIFRpbWUAMjAxMC0xMS0wOFQyMjowODo0M4IMpfwAAABEdEVYdFNvdXJjZQBodHRwOi8vb3BlbmNsaXBhcnQub3JnL2RldGFpbC85NDcyMy9kYXRhYmFzZS1zeW1ib2wtYnktcmcxMDI0xHYrZQAAAEl0RVh0Q29weXJpZ2h0AFB1YmxpYyBEb21haW4gaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluL1nD/soAACAASURBVHic7L15kBtXfuf5zUzcQBVQJ4tk8VKRlCiSEqUST0ktqUW5JVlU29221EPb63DMbMzuRmxs7O6M/cdOxM7GHhG7szsTG2vvYbtDPbbVTbk7uttqqVtSUxclUiQlnuIpHqJIVBWLV91VKFy5fyQykQkkUEAWkJkAvp8IRBUeXma+ykS9T/7elYIsyyCEGInH4yKANt2rvYLfgwA8BS9vhWml0gEgXfBKqb/LsmxIl2WkgcI0w/u0LGMOkKcATAGYBDAly5gC5Mlc2pQsY1KXZ+q+++7L1ubMEtI8CBQoaVbi8bgAoANAr8mrB0AUpYUYdqDIFVP4f6u8LUyrJI8xzfw9AMgzyAlXkW1euLKMCQC3AfkWgFsAbskycr/LY+vXr2clQ5oSCpQ0FPF4PAxzIZq9upGP4pqCWohTJ0ULefJpFeZJA7gDRaY6sSrvYZAtbm3Y8MAMCGkQKFDiGuLxuA/ACgArAawyeS0FEHKsgA5SLMXi/9vKxFl7uRoPay7X8nmUtNz7WQAjgPwNgG9kGd8A6ku+Lsu4sWnTxiQIcQEUKLGNeDzehtJyXAWgD4DoWAFdyELitNIsWzpP+e3qLM5K82QB3ATkQrmq768/9NDmKRBiAxQoqSnxeDwGYAOAB3M/1yIvyA4Hi9ZQOCXO6ppuS29XB3GW3K4wjyzLY9DEisuAfF6WcQ7A+S1bHh4v3CMhVqFAiSXi8fgS5CWp/twApZmVWMBKs2zleYxpdezz1B/VQh4lzYpci8+faZ4RAOdzr3MAzsuyfO7RRx8ZBSFVQoGSkuRGsa6EUZLqT0aTNaJe4rR5sJC+JBbyKGnVR6AVi7NwR4VpY7Isa1IFcE6WcR6Qrz/22CArSWIKBUoAAPF4PADgYQCDAB4D8BCAB+Dy6RyNDMWZT7NLnBbO5wyAC7KM04D8BYBjAE5t3fpYorCEpPWgQFuQeDzuhyLIx5AX5kY02ZQPt+KmEbRmafUSp5X+TAfFaZqWe58GcBbAFwCOyTK+AOTT27dvmy8sPWluKNAmJzc15CHkRTkIYBOUVW+IjVCcpdKsidPJPmOTtJQs4wyAY7pI9fSOHds55aaJoUCbiFyf5YMAHkdelptBWTqKm6aemKVZEaeVQT5NKs5y1yEF4EtoUSoOAvK5Xbt2stJtEijQBkYnzKdzr6egLFFHXADFWXq7hZtlG0+cFZ7P2wA+BvARIH8E4Nzjj+9iJdygUKANRjwefxDAM6AwXYubKvVK8lhpljVLsyLXGo2gdVSui7xZuQ3IHwP4SJbx4ZNPPn6u6A8hroUCdTk5YT6NvDB7nSwPKQ3FafbePM3t4nQwyr8ly2qEio++9a0nKFQXQ4G6jHg8vhrAC1CiTAqzAXCTOK00y1afR0mjOCu/DouI4G9BafL9EJB/89RT37oG4hooUIfJPXdyG4A9AF6GMkKWNACtIM6Fpeh+cdYrgjdLs6Hp+wyANwH5V7KMo8888xSf0+ogFKgD5B7J9RwUab4ERpkNRYP0rZnkKb0dxVn5dahXBL9wnqK0W4D8lizjVwB+++1vP81HwdkMBWoT8Xi8H4osX4bSPBtwtkSkWhpdnDWosCvMUz9x1iuCrySPi8RplicByB/KMt4E8Nazzz4TL9wDqT0UaJ3ITTF5FPmm2UecLRGxipv71srnUdIoztqez+rzKGn1ug4lzucJAG8C+BWA47t3f5sVfR2gQGtMPB5/AMB/AuCPoCzEThqU1hOnvXJt4KknJnlKb1ev61DFzcp1AK8D+Lvnnnv2AkjNoEBrQDwe7wLwAyji3OZwccgiYTOiWR7z7SjO0ttZuQ42RPlHAfwdgH2/8zu77xZlIlVBgVokt8bsiwD+NPfT52yJyGKhOM3ymG9Xi2ZZ8zz5clWax8kov/o8Sppd4ixzzpMAfg3gP8oyfv38889xzV4LUKBVEo/Ht0GJNH8AoMvh4pAaYE2czR8NLZxn0dFQlXny5TJ7XzpP+e3qdR0aKMq/C2AfgL97/vnfOVq0ISkJBVoB8Xh8BYA/hiLOBxwuDqkBbEbMpznQjFjmfb6cpdIoTl1K7aP8CwD+TpbxDy+++J0bRTsjBijQMsTj8ccB/NcAfg+A5HBxSA1gM2I+jeKs/DrUK4Kv5Do41DyeAfBLAP/hxRefP1i0cwKAAi0iHo97APwBFHFyQFCTQHHm0+wSZ70i+NJ5ym9HcVabR0s7CuA/yDJ+9tJLL6SLDtjCUKA54vF4DMB/CuC/BLDC4eKQGtGMfWuV5KlXE2EziLNeNyIL57F2Ps3SHOqCuAHg/5Jl/M2ePS+OFxWgBWl5gcbj8QEA/xWAPwMQcbg4pEZQnKXSGm/qSek85bejOCvPU+X5nAbwGoD/c8+e371SVKAWomUFGo/HnwTw30BZJUh0uDikRripwjZLsyLOejURUpyV5lHS7IrgzdJcIs7CtCwgvwng37/88kufFBWwBWg5gcbj8acA/K8AtjtdFlI73FRhm6W5SZy1q7Cdk2szRvl2yrUO3/0jAP7iu9/d83FRYZqYlhFoPB7fAEWce5wuC6kdjIYqzeP+aMgsrdFvVuy8Di75Xv8KkP/i937v5fNFf0AT0vQCjcfjfQD+BwD/HJyK0jRQnGbvzdPcLs5mjPKbQZyLiOAzAH4IyP/97//+d28W/UFNRNMKNB6PRwD8awD/LYCww8UhNcJN4lxEBVNFHiXNTRW2eZ582SvNUy9x1iuCt/M6NMn3egbA/wHg333ve9+dRhPSdALNzeP8FwD+LYAlzpaG1IomrWAMedwe6ZjnUfZf6n3pPOW3ozirzWNMsyuCrzDPqCzj3wL42+9///eaah5pUwk0Ho+/DKWfk8vtNQmtUMFQnDWvsF0lzmbsM64+DwDIF2QZf/EHf/D7b6JJaAqBxuPxDgD/D4BXnS4LqQ2NXsG4PdIxS2uFPuOF85hv5yZxWjmfZmk2irPw/RuyjP/8D//we2NocBpeoPF4/HegTOpd5nRZyOJp9AqG4nRlhV1BHvPt7Drn5nnyZTd7XzpP+e2clKvu/TCAP/vDP/zee2hgGlag8Xg8CODfAfgvAAgOF4csEjdXMG6v1CupxNmMmE+zch0a7Walujz5NJtvGmUA/zeAf/3KK9+fK8zRCDSkQOPx+FYAfw/gfqfLQhaHmysYirN0HrdHOmZptTifFGc+rYbf64sA/uTVV//g88LcbqehBJobYfvfAfg3ADwOF4csAjYjmuUx364WFbZ5nny5Ks3j9kq9kjyNdrNiltaEN41pWZb/JwD/8w9+8IcNM1K3YQQaj8fXAXgdwFany0KswWbEfJoVuTIaMsujpNXrZoXirDSPklaD8/k5IP/RD37wyqXCLd1IQwg0Ho/vAvAWgA6ny0Kqh82I+bRaVNgUZz6tXuJ0Mso3S7NyHRr4ez0G4KV/9s9eOVS4F7fheoHG4/GXAPwjgKDTZSHVQXHm0+wSp9sjHbO0WpzPhfOYb0dxlt6uFudzEd/rOQCv7N376luFe3QTrhZoPB7/UwB/C/Z3NhRuGkFrltYkFUzB+3y5qstjTGtGcTo59aTyPMY0fq8BAGkA/2Lv3lf/Y+He3YJrBRqPx/8cyqpCpEGgOEulsW/NPI3idEqcVs6njeIs5C/27n31fyuXwSlcJ9B4PC4A+N+hPOyaNABuqrDN0hqtgqE482l2VtitMAWowcSp598D+Fd7977qKmG5SqC5aSqvAfhjp8tCFsZNFbZZmpsqGLdHOqXz5NOsnM/q8yhp7hdn9deq0W5WFs5jvp21a1UR/wDgz/bufdU101zc1rf4Q1CersdNlXoleZysYNweDZmlNfrNip3XoVW/1zaLU+WPAWQB/OlidlJLXBOBxuPxfwng/3W6HKQ0FGeledwvTivn0yyN4iydZuV7beV8Vp9HSavXdajkfC6S/2zv3lf/v1ru0CquEGg8Hn8MwKcA/E6XhRTjJnE6WcE4WWGb58mXvdI89RKn+yMdd49kpjirYh7AE3v3vvpFPXZeDY4LNB6PdwI4DmCVowUhRbCCMXtvnuakOK2cz+ry5NPcVGG7XZxWzmf5PKW3q9d1qNX3ug58A+DRvXtfvVfvA5XDUYHmRty+DeAFxwpBimiFCsbJCtssjeIsvZ2V69CMfcbV51HSmkycen4D4HedHJnr9CCifwPK0zW0QgXjdnFaOZ+l85Tfzk0VNsVplqf0dlaug53fa5t4AYpD/kcnDg44GIHG4/FnAOwHIDpSAKLR6BWMkxU2xZlPq9d1aMY+4/J5Sm/nJnE63f2XIwtg9969r37oxMGdjED/F1CejtLoFUzzitO9NyvV51HSrFyrVm0eL59HSXPyu+8ScaqIUFyy04mDOxKBxuPxJwB8YvuBCQB3VzBuj4asVjAUp9l787RmjPLN0ijOmvLk3r2vfmr3QZ2KQP/coeO2NG6uYCjO0nmcrLCrz6OkUZyN8d23es5dyJ9DmQppK7ZHoPF4fAOAswAEWw/cwjTGpPDmqmDcHOWbpdXrOjTazYpZWjPeNDaROFVkABv37n31vJ0HdSIC/VegPOsOmxHzaW4SpzuioXyaletg5Xy2ijid/F5bOZ9NIE4VAYpb/rmtB7XzZMXj8aUArgHw2XbQFoPNiPk0OysYitMsj/l2tTif5nny5aoujzGN4mxYkgBW79376ohdB7Q7Av2XoDzrAsWZT7OrgnGywq4uTz6tFudz4Tzm21GcpberxflsYXGq+KA45t/adUC7BfqMzcdreti3ViqN4tSn2SVOJ/uMK89jTOP3uql4BjYK1LYm3Hg87gUwASBoywGbHIqzVFpz9q1VksfK+TRLozhr+722cj4pTsvMAYju3ftqyo6D2RmBPgzKc9G4qcI2S2u0CobizKfZWWG3whQgitMRglBcY8uTWuwU6Hobj9V0uKnCNktzUwXj9kinkjz1OueV5HG6UnfzFKB63awsnMd8O2vXoelZjyYUaI+Nx2oanK/Uy2/npgrG7dGQWVqj36zYeR1a9XtNcVaNba6xU6DdNh6r4aE4zd6bp7ldnFbOp1kaxVk6zcr32sr5rD6Pklav61DJ+WxBbHONnQKN2nishsVN4nSygqE482luqrBrdR1a9Xu9cB7z7SjOqrDNNXYKdMLGYzUcrVDBuD3SMc+j7L/U+9J5ym9HcVabx5hWi/NZPk/p7ep1HWr1vSb2ucZOgd6x8VgNQytUMBSnOyvsWl2HZuwzrj6PkkZxOosgCBAE4a5dx7NNoDMzMwPhcNiuw7meRq9g3B7pmKW1Qp/xwnnMt3OTOK2cT7M0J8Vp5/eaGGlvb9tq17FsE+j8/PyMKlBBEFr2S9DoFQzF6c4Ke+E85tvV4nya58mXq9I89bpZMUur17Wy82aFGBEE5RklPp/XlkUUABsFOjs7G+/o6NDk2WoSdXMF4/ZK3WoF0+hRfvV5lDQr16HRblaqy5NPa7SbxlaqI2uBIAgIhYK37DqebQKVZTmTTCbh9/u1NPWOoZm/JG6uYCjO0nncHumYpdXifFKc+bR6fa+tRvnEiJk//H4/BEHI2lUGWxeTn5+fNwhUpRmjUTYjmuUx365WFUzribN257yS6+BklF86T/nt3PTdt3qzQopR5VlIIGDvw75sFejMzAza2tpM//hmkCibEfNpdlYwjIbM8phvR3GapVn/Xls5nxTn4hBFAYC5L9rb22wti60CzWQymJ2dRanRuI0qUYozn2ZnBUNxmuUx364W59M8T75c1eUxplm5Ds36vSalEUWx5GeRSBiSJNl6Tu1+Hiimp6dLChRoLImyGTGfZlcF4/ZIxyytFudz4Tzm21GcpberxfmkOO2jnDwBIBZrt6kkeWwXaCqVQiKRQDBY+slmbpdoq4rTyQqG4iyVx3qF7SZxtur3mlSGKIplB52GwyF4vV67i2W/QAFgamqqrEABd0rUSgXjZIVdPk/p7dxUwbhdnFbOp1kaxVnb77WV80lxuhNRFCGKgsm1yONE9Ak4JNBkMllyRK6Km6a4NKM4naxg3N5EWEkeirN0nlb9XlOctUeSpJwLSp/HQMCPQCDgyLl2RKCAEoWWEyigrWuIbNa2aT0G3FRhm6U1WgXjdnFaOZ/V51HS7KywG12cTt6s1O5akWoQBCEnT/UalZZoV1ennUUz4JhA5+fnMTU1hba2hYcdi6IIWZZt+1I2lzjrW8G4vVI3S7PrZqWSPE5HQ24eyUxxtiaCIMDjUdVU/nx2dnYgGHQm+gQcFCgATE5OwufzLRiJAvY06bopGqokj5MVjNvF6czNipLmbIVNcapp9boOlZxPYg1JkiBJEszOez4aVQiHQ+jocPYx044KFADGxsbQ09OTO2nlqZdEKU6z9+ZpFKdZHiXN7eJ08ntt5XxWn0dJozgbE4/HA0kSTa5NMV6vB729PfUv1AI4LtBsNouxsTF0dXWVXJ5Jj9ovWosmXVYwZu/N01pVnE5W2BRnpXmUNCvXoVbfa2IdQRDg9XpLDBYqThMEAUuW9OZk6+x1cFyggDI3dGpqCu3tlQ9FVvtFrQwwaoUKxskK2yyt0UYyu12crdpnbJZGcTYuHo8HHo+n7HktbLrt6elCIOB3xbVwhUABYHZ2Fj6fD4FAAIDyRV0oIlVHamWz2YpOZitUMBSnOytsitMsT+ntrFwHO7/XZHGIoqiLOisnEgkjGm13zfVwjUABYHx8HLFYTJNopSwUjTZ6BeNkhV0rcVo5n6XzlN+O4qw8j5XzaZZGcZJK8fm8kCRFPer5LV44p7jpNhKJoLe326ZSVoarBAoAExMTkGV5wZWKCjGLRhu9gqE4a3U+3SnXZozyzdLqdR3svFkhi0eSJPh8Pq2vs5rTHItF0d3d6bpr4zqBAsoiC9lsFpFIpOptzaJRN1UwjIYWymNMc1OFbZbmJnHW62alujz5tEa7aXRb5dwsiKKoRZ3VnGO177OrqxMdHTFXXh9XChRQnh2azWarGlikbw5Qo1GjSAGnKhiKs3Qet0c6Zmm1OJ8UZz7NbTcrZPEIggCfz6dbFKF4QFC5pltBENDb2422tuoDKbtwrUABIJFIAEDJh3CrlKpg8iKVkcmkC/IAi61g3B7pVFLRsxnRLE/p7Rr9ZsUsrRlvGilO51DE6YXX68ulVNdcCwCiCCxZsgThcMjV18nVAgWUJf+y2Syi0ahBoqUqGGMeAJAhCMpw6Ww2i0wmU7CNkqdcmpsqbLM0OysYRkNmecy3ozjN0qx/r62cT4rTXpSV5XxQI0nlVJdfDL4QSZKwdOkSR5foqxTXCxRQ5olOTEwgEoloKxaVk6lZxQCoj8URkclkkMlksdiKwizNSXHWs4KhOM3ymG9Xi/NpnidfruryGNOsXIdm/V6T2uD1ehEI+CEIIszOfWHTbSmp+v1+9PX1OPJsTys0hEABIJ1OY2JiAqFQyDBXtBCziqkwKlVFqkSk6qhditNNFXZ1efJptTifC+cx347iLL1dLc4nxeku1D5Ov98HQVjcqkCCIKCzM4bOzg4AjXPNGkagKrOzs0ilUgiHwxBFEYCxElA6pfPvVczSRFHSiTSDbNZMpCib1owVDMVZKo/1CttN4mzV7zWpDaIowu/356akKGmyXHmUqX2ay+/z+bBkSQ8CAX9uX41z3RpOoIASjU5OTiIYDMHnM4b6C1VM6q/KhVelK8LjUUaD5Zt385/rt2vUCsbtkU4leaycT7M0irO232sr55PibDwkSUIgEDDUuWbfw2qIRtvQ09NtMhq3MWhIgaokEnNIp1MIBIIQhMJ+USAvSMGkYiiOVBWRipCkLNLpNLJZueErGIozn2Znhd0KU4AoztZA6d8M5PolZdPvaSlKRaWSJKGvrxehkLJgTqNeu4YWKKBEo7OzMwgEArlFiYGFKnElzVhRGCNS5ekAsgxkMulc866Wu2C/+TS7Khi3Rzql8+TTrFTY1edR0txWqbt5ClC9zvnCecy3s3YdyGIRBAF+vx+BQKDo+ZzFUizaGuUEG4mE0dfXqy1608g0vEAB5R8okUjA4/FoS0WpFYX+ETnlo1LBkEd5bBogSR5IkgfZbAaZTAbptH4ajL0VjNujIbM0RkOl01ohyl84j/l2FKcz+HzeXDOtT0ur1Wn2+Xzo7u5EJBLO7bfxr19TCFQlnVaiRa/Xa1j9orJKx+zzfB5l5K4Ej0fWotJMRjbkqU805H5xWqmwzdIoztJpVsRp5XxWn0dJq9d1qOR8ksUhSSICgSACAT8kSdLOb/nzbIwyi1cYyr/3eCR0dXUiGm2vYL+NRVMJVCWdTiOdTsPr9UGSRC1dloF8N6mxSUKfR0mTC94r26hRqbowg3KsjO5LQXEuVpzuj3TcPZKZ4iSVEAgEEAzmo83i81tekgshCAI6OjrQ1dWhNdc22zVsSoGqpNMpZLOiNl0Fuaba4gqmOE2W9UsHmm0nQBBEeL0+eL1KX2k6rTTz6mXaaOK0UmFXlyef5qYK2+3itHI+y+cpvV29rkOtvtfEOuozlwOBAETRfMqfdRThCoKy/Gpvb48hom1GmlqggPLPl8mkIcuKSHOpBXnyaYVRqlme/Pt8mtpXCigyTaXSuWUDZbSyON0e6VQmxerPZ+V5jGkUZ/k0Uj1+vx/BoCJN40pBctVR5UJRaTgcQm9vT245v+a/hk0vUBVZlpHNKgOAlLsi7ZMSX6LCJt6F3ufT8jKVkU5ntCZl5cvU+H1rleShOEvnsXI+zdKcFKedN4SkOtQRtKo09aNd63VqfT4/enu7EYmE0YxNtaVoGYHqyWazhlWMiisUocR7Jc34Ppdq+MLkKwqPR8oNaJKRSqUNMm00cbo90qmkom/GPuPyeUpvZyWPnTcrpHIEQcj1aQYRDAa0OfGVndfCqNK4qEG5KDUcDqOzs8P1T02pFy0pUCD/xVK/aMoyfsb5oLJc/n1+X+WEmz+W1+uB16vMVVWmxKRzI4fTjkVDZmnurdSVNCtybbSblery5NOsXAe3RfmkMjwejxZlquuDq9R6QJDxOZ0iotE2dHZ2tExTbSlaVqB6BEGAx6OsiatEhsbPzb4bapq+Odf4yNJCmRojVyUylSDLfsiyrMk0mUwavowUZz6tFhU2xZlPc1uUT8pTGGWqU/Uqm3ayeCRJQiwWRXd3V9OOqq0WClSHKIoQBAGZTFbrL11Mc245uSpZhFw+ZeUjr9eLYDCITCaDVCqFVCqFdDpdtN/8+3y5Ks3jZIVdfR4lzS5xOnmzUjpP+e2aIconpfH5vAgGQwgGg9pi6yqF57L69WQra7r1er3o7OzQnpRiduxWhQI1QZLE3BBvuSgqNZeivoIxjziVz/VizW+X27Pu+JK2cDMgI5lMIZ1OaSN73TTK1izNTdEQxVlpHiXNziifFCNJEoLBAILBEEKhoPb8Y/WcVnseF7NIeyAQQGdnB9rb26rsU20dKNAyCIIASZKQzWa1R52Vq3zLR5yFMtVvo+wnv40+6s0/c0/9B1Jlqoq1eL8UZzOIsxmjfGJEbXVS+zKNC7bX/vwt1Pfp8XgRiYQQjbYX9auSYijQClBEKuQElo9IiyudhSNOY5Sq5inerpSAleZeH7xeH8JhpQzpdArJZEpr9s2XzRlx1quJsJKK3slRtmZpFCfREwj4c8JUXsaFBorPY/ESebV47Jex6VaSBAQCQbS3tyMcDhfUPaQcFGiVKIvMC1A70FWZLhxxFlfGhV/U4kpI2Vfx4CTjhh6PN3fnKugi1LxUleebUpytOiCrXhE8xVkeURQRCAQQCuWFqa6IptzkqjfYhVIs/zSTWuH3+9HWFkYkEtY1FZNqoEAXgdIvIEAQZK2JV0lXc+grNH3faH4fRkkW5il8Qozx+KX2owxI8iEUUj7LZrNadKqINYls7vlsbhZnvUbQmufJl6u6PMY0irM1UWWpn1aSn+IBmJ/r2mAWlZYaECRJEsLhENra2uDxeBhtLhIKtEao60oqTbxqhCmU6ONU0wrfFwoYKIw4Swm3MI+yGL6QK5sIv98Pv9+v7TuTySKVSmpR6vy8KlXrFbZZWjOKs14RfPk8Slq9IniKs3JEUdT6LdWfPp8fxvMjF3wnqosqSz2I2gqCICAYDCASCcPvD1CaNYQCrTGCoBeZOuhI0I1iA9R/hHxes4hTSTc+KUZGdVGp/nmoxn1LkghJCiAQCGr5M5kMksm8VNPpJFKp9IKVulmanRU2xWmWx3y7+sm1OVEG8Pl1TbGqLAG9JPP/v6XPS/WLF1hHkiT4fD4Eg34EAkFKs05QoHVE+YdR/qmU6E6JSJUFnYsjzsLIUU0rFuXCUWnxnbBx38Y0ZTtlCH0QoVBQSzPrU1VH/xoXnXBnNFQLuZrL1ixP+e2quxGxfrOycB7z7VpVnF6vF4GAX2ulUYTph8/nyzWFAvnzanZ+rVFt32e5AUXKQEcRPp8PoVAwt8iCQHHWGQrUJtR/REU6mZwskVvRw7hYA6BGj4D1qLR4u/x+C+VrFKk+TelT9Wl9qnrZK32qSsSqj1zVZmwj7hJn5VJsPXFWcj4bDY/Hg0DAn+ub9Of6KRVZ5tfFNpNk4d9dfkm8xTa9Vre9DI9H0qSp/B2Upp1QoA6iRHfKikeqTAH1uaVqHkCRYumotDByNZOiPr0wgtXv1/zh4sV5BEFZJcXnU6bT6MuiLkuYH7SUH8Bktu5vM4jTSrNs9XmUNIrTHEkSEQgENTnqB/Soo0yNTa5GYeZZ7Lqx9UOWFWmqkbIoqgMZnS5Za0KBuoS8TDM5QUkQRcEgJsBclPpKtHCx+1LbqXKVZaGgglCn5Rj7cs2jVpim6ZclVNLyGc1GBCuCVX6qo4N1e16wojcXHsVZartGFqc6ICYQyA/iUUe/KlO5FPLdC6UkWdNSofB7YyzzYqPSfPOs0qys7oM4DQXqQpQlBNPIZBTpCYIAURQhSUoTTWHEqWwDjLETXwAAIABJREFUmFf+SrpRroJpZWxML4xSjdGsvqyCUD5N/14Z7u83rHKiX4FJeTpN/kk1ykt9nzI8Eq5wfqt6rOJzUJ04q5OiO8VZqwjeDtS1oBVBeLUbMCXN+N7v9xeVs7DptcRRsLiosr5NtyrqPkRRgtfrgc+nzO+mNN0JBepy1JG82WwGqVR+IQdFqJI2Mdu8+bbwH67Uog/qNvrjmgu3vIQXEnBhBFxYZqWvyuPx5KbcGMuvr0QEQXkEnb65OP+7+j6l+z1tmKubPy7FWSptMajyU6Mmn89XIMm8LAufKpIvc/GSdqUXHyg999GNqGVTyqvcYCoLongoywaCAm0wFJlmtSe2AMqoXo9H0gk1Lwd9dFdciQsF0iyOHJX9w0JaJe9LS9bsfeE+RFEwbdYqXFNYfa9Esnmp5l9prRk5L+JMiXmx9otz4WbZ+opTFEWIoqgToh9+v88gR1WMqiiLFx83rr6T/6x+CwwsTGFUWa2Aq2+6leUMsllZa5b1+/UtMZRmo0GBNjjqqN5kMp0TjABRlHLzPCVt8MRCkaPxPYrS1H2XSzNvus3vy2xkcXHfrP5vKy1Zswh5ISF7PMoDzZWuWVWuxdLVl0F9tJ3+ZyaT0W5i8q8sMpm09rn61Bx17eRsNt+aACgPJ1Df5/PkV7RSH2Cgb75XRaa8JEiSAEHIp6l5BMGYvzCPuk+zl/EzITflSi/6wujQfPEAK1Ks5eIBpfax2KbbalGuo9KvL4pi7uZC3S9pdCjQJkOdt6l7jCjUp8qIogiPxwNJkqAusFBdU21eruWbaoFKJWnyFxj2t1Aka34OSm9j1oRtJmQVSVIefK7+TWrlp99H/qZBL+N8HuONR/E+9NsXpum3NXuklFn/X/F0jEIBGvPqm7IL5ZhPK0V1UVgt5z46QSkByzJyN1YZKH+PmLuJVccuMMJsRijQJiffh5otaJ4StAhV7YeqpFnWLK16wck6GQD5pmah6H0+f/79YqLQfBlLb2NF0rXBrKzFg7QqkUi5SG6xTZVu61us14Oky6EOZlP3o0T5ghbFk9aAAm1B9FJNJpOYm5uDLCt9iuogHv3gDqDaploAKC1Js21ye0KhQMzLXy6iLJdefv/mUajyZB3j8UoJ13z/ZhKs5vNKcDIyW2zTaz2abms3oEjW+skzGSW6VGUpCIC6qhhpTShQoqEKdX4+CX1TrDpASS9XtVlqYQEuHBECC/d/Fr8vlE5xRKkXW/GAqUIJVxKFGsVaSrJuYLF9ffUYQLP4/sz6oXz31dHaGcPSmwqCtmgB+y+JCgVKyiLLWaRSSuWi7xMTBOTE6oXHI+nm6ilrcCr5CpcdrE6SuVwoXWFV05Rbyf5qyUJCXigathaVlm+6dVfTaznq0/cpGJ5ApIoym81CEMQSfdaElIYCJZaQZTk3/UPpB9IPKipsBlblml9OrfxoXHU/5lFjoXAXKmfpKHSh6HihpuKFjm9H0219KS/gaqPMxQ4oqhR1ipc6NSmdzmjNr+r11Q/kcvc1IG6GAiU1R51nqfat6gWrRK0ebUqJXrBerycnuXwlurA0q4tCKx9AVDpCLJ9noeZc6xFwJQOKqh8QVNu5j/VGuXFTloBU5/GqglQiSnVQj1JWfRMsPUlqDQVKbEVdKUjBOG9QnVzu8Xg1waqr1yiC9eX6ocyFupgBRaUi1WpG7FqJUovL6vaotDyL7fuU5SwSifncGslKc2smk9aWeDQXpDGqJMQuKFDiGpQ5rHnBms1tFEUBkuTR5mcq0axUstm4eEBR6QFElfdTVjLgyEkW2/dZuwFFsixr0aLy07jsorr4RDqdgfqYP+WY+RNqXOzCVSeatDgUKGkolBWAkgDMBav/XZZlbZ6r8tMDSVIW6FaiXEmbC6suMKEXc/n+zXKyrXyAUGX9uNbmghqx3vQqy9At6J+PBtWf+qURlaUSM8hmlRshZTWlfJn15S/8nYIkjQYFSpoa/bJ6gBpJlf5dnyaKUi7ilUxf+uk9apo6kV5dEk+ZM4jcKE/BsERe+d/V94K2vJ9+/q6y7F8Wspz/vfC9fgGN/Odyrs8w/6Qb5Xd1ScI00ml12UJliUJZVh8xZ7YSk/mKS4WrMxHSjFCghJRAEYgyaKVwMFShbEt/ro+OF/rc/PdqIjfj76U/LxbgQp8TQgrhMhqEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWIACJYQQQixAgRJCCCEWoEAJIYQQC1CghBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBALUKCEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWIACJYQQQixAgRJCCCEWoEAJIYQQC1CghBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBAL2CbQu3fvRuw6FiGEEFJvbBPo/v3795w6dcquwxFCCGkxrl+/joMHDz5k1/FsE2ggEJj88MMP8c477yCdTtt1WEIIIS3A0aNH8ctf/hI+ny9l1zE9dh0oEoncA4ALFy7gzp07+O53v4toNGrX4QkhhDQh8/Pz+M1vfoOrV69CEAR0dHSM23Vs2yLQvr6+YQAQBAG3b9/G3//93+Pq1at2HZ4QQkiTcefOHfzDP/wDrly5AkDxS19f3z27jm+bQDds2HA9EAgAUP7I+fl5/OIXv8ChQ4fsKgIhhJAm4fz58/jxj3+M8fF8wNnb24tIJDJvVxlsE6goinJ/fz8ARaAqhw4dwk9/+lNMTU3ZVRRCCCENSjqdxv79+/H2228jlTJ2d65evdrWstg6D1T94wRBgCjmD33t2jX88Ic/BEfpEkIIKcWNGzfw2muv4cSJE1qaPiBbtWqVreWxVaAbNmxAJKJMBxUEwfCHJ5NJvPvuu3jjjTcwMTFhZ7EIIYS4mGQyid/+9rf4yU9+grGxMS1d75AlS5Zg6dKltpbLVoFKkoTBwcH8wcXiw6vR6PHjx+0sGiGEEBdSygl6eQLAzp077SwWAAeW8tu0aRPC4TAA5QRIklSUJ5lM4r333sPrr79uuNsghBDSGszPz+PXv/419u3bZ9oqqW/FXLZsme39n4ADAvV4PHjsscfyBRBF00gUUFaV+Nu//VscPXoU2WzWriISQghxkMuXL+Ov//qvS46LkSTJEIHu2rXLrqIZsG0hBT2bNm3C559/jrm5OQDQolAzSaZSKezfvx8nT57Ec889h4GBAVvLSgghxB7u3r2L/fv349KlSyXzFLZa9vf3Y+XKlZBlud7FK8IRgXo8HmzduhUHDhzQ0iRJgizLJU/CnTt38JOf/AQDAwN47rnn0Nvba1dxCSGE1JG5uTl8/PHHOHbsWNnWRkmSIIqiwRVORZ+AQwIFgM2bN+Ps2bO4d09ZNEIQBHi9XqRSqbJ3EleuXMHVq1cxODiIZ555BqFQyK4iE0IIqSHZbBZHjx7FgQMHkEgkyuY16+5bs2YN+vv7HYk+AQcF6vF4sGfPHrzxxhvaiRMEAT6fD8lksuwJkWUZX3zxBc6cOYNvfetb2L59Ozwex/4UQgghVXLhwgX89re/1YKockiSBI/HY4hOY7EYnn/++XoWcUEcfaB2NBrFiy++WDQc2e/3lxxYpCeRSOC9997DX/3VX+H8+fP1KiYhhJAaMTIygh/96Ed44403KpKnx+MpCpB8Ph9efvllqMvDOoXjYduKFSvw5JNP4pNPPtHSBEGA3+9HMplEJpNZcB/37t3Dvn37sGzZMjz99NN44IEHiqRMCCHEOUZHR/Hxxx/j7NmzFTe5+nw+rc8TyM/9fPHFF9HV1eVY062K4wIFgEcffRS3b9/GxYsXAShNtHqJVvr80OHhYfz4xz9GX18fnn76aWzcuJEiJYQQBxkZGcFHH32E8+fPVyw8tTtPbYnUb7dr1y6sWbPGcXkCLhEoAOzevRtjY2O4deuWId3v90MQhKJFg8tx8+ZN7Nu3D0uWLMEzzzyDTZs2UaSEEGIj8XgcH374IS5cuFDVdoIgIBAIQBCEIknef//92LZtmyvkCbhIoOqgon379mF2dtbwmc/ngyRJmJ+fr+rEjY6OYt++fejp6cEzzzyDhx9+uKK+VUIIIda4fv06PvjgA3z11VdVbytJktavqa/rBUFAT08PnnvuuZqVsxa4RqAAEIlE8Oqrr+Kf/umfMDY2pjXlAopgJUlCIpGoqF9Uz+3bt/GP//iP+OCDD/D000/jkUce4ahdQgipIV9//TXef/997eHW1eL3++H1egGgKFBatmwZ9uzZ47p6212lAdDe3o5XXnkFv/rVrzA8PGz4TBRFhMNhJJPJBecMmXHnzh387Gc/wzvvvIMdO3Zgx44daG9vr1XRCSGkpchkMjh16hQOHjyIeDxuaR+SJCEYDBYtkKA24a5duxbPP/+8YTCRW3CdQAEgEAjge9/7Ht577z1cunTJ0H8pyzL8fj88Hg9mZ2ctrZE7PT2N/fv348MPP8TmzZvxxBNPOLIQMSGENCITExP47LPPcOTIEUxPT1vej9/vN22yVRkcHMQTTzxR8nOncaVAAeWu5MUXX8Qnn3xieHiq/vO2tjYkEgnMz89bOkYmk8HJkydx8uRJ9Pf344knnmDzLiGElODrr7/Gp59+ii+//HJRD/hQWxMLF0dQEQQBTz31FB566CFXilPF9ab41re+hWg0alg3V+0bFQQBoVAIXq8Xc3NzVfeN6onH49i3bx/eeustbN++Hbt27UI0Gq3Fn0AIIQ1LOp3GiRMn8Omnn2JoaGjR+/P7/YYlWAtH2/p8PnznO99xzVSVcrheoADw8MMPo729He+8847pnFCv1wuv14v5+XnMzc0t6qRPT0/j/fffx4cffohNmzbhsccew/3338/Ru4SQluL27ds4evQojhw5UjQzwgperxfhcLjsg0NCoRD27NnTMA8LaQiBAsB9992HvXv34r333sPo6GjR54IgIBgMIhAIYHZ21tIgIz3ZbBanT5/G6dOnEYlE8Oijj2JwcBBLly5d1H4JIcStzM7O4tSpkzh27BiuX79ek31KkoRIJAKfz6eJ02yO54oVK7B7925EIhHXR54qDSNQAOjo6MArr7yCY8eO4ejRo8hkMkULJIiiiLa2NgSDQczMzCCZTC76uNPT0zhw4AAOHDiApUuXYnBwEI888ggikcii900IIU6SyWRw8eJFHDt2DOfPn19UV5getZ8zGAwCKB4EpErU6/Vi165d2Lx5s2k+N9NQAgWUk75161YMDAzg3Xffxd27d03zeTwexGIxJJNJTE1N1exLMTIygrfeegu//vWvsX79egwODmLDhg0ceEQIaSji8TiOHz+GkydP1qSJVkUdm6IPMEpJcfny5di9ezfa2toaSpwqDVvrd3V1Ye/evThy5IjhIaz6xRcApcPa7/djdnYWMzMzixo5piebzeLChQu4cOECgsEgHnroITz88BasXr2KywYSQlzJ+PgYTp48iePHj+P27ds1338wGERbWxskSQKAor5ONer0eDzYsWMHHn74YS1fI9KwAgWUi7Fjxw4MDAzgvffew9jYWMl8kUgE4XAYc3NzmJ6erllECihPUz9y5AiOHDmCSCSCBx98EBs3bsJ9962BKEo1Ow4hhFTLnTu3cfbsWZw9e9byYgcLoYpTXUmoXKCybNky7N69G+3t7Q0rTpWGFqhKb28v9u7di8OHD+PkyZPIZrNFiy+o017C4TDC4TASiQSmpqaqWqS+Eqanp3H06FEcPXoUgUAAGzZswMaNG7Fu3TpIUlOcbkKIyxkeHsa5c+dw9uzZogd01Aq1Pm1vbzd9akohXq8XW7duxZYtW0wHETUiTVOjS5KExx9/HJs2bcLBgwdx9epVw+f6pl11xG4oFNJEanUxhnIkEgmcOHECJ06cgM/nw/r167Fx40asX78ePp+/5scjhLQmsizj+vXrOHfuHM6dO1eyNa4WiKKI9vZ2RCIRgzjNmmrV/AMDA9i5c2dDjbCthKYRqEosFsNLL72Emzdv4tChQ0Xr6RaiijSZTGJycrKmnel6kskkzpw5gzNnzsDj8WDt2rXYsGED1q5dh7a2trockxDSvKTTaXzzzTWcO3ce58+fW9SSepXg8XgQjUa1+qrUXE49K1euxM6dO9Hd3V1R/kaj6QSq0tfXh+9///v45ptvcOjQoZKjdVV8Ph96enqQTqcxMTGBmZmZul3sdDqtDUACgCVLlmDt2rVYu3YdVq5cCY+H/aaEkGLu3LmDy5cv4fLlK7h27euad0GZ4fP5EIvFtFG15eZyqixZsgQ7d+7EsmXLmk6aeppWoCqrV6/G6tWrcfHiRRw+fHjBuzSv14uenh50d3djenq6bs27ekZHRzE6OoqDBw/C6/Vi9erVWLt2HdauHUBnZ1ddj00IcS+JRAJXr36NK1cu4/Lly5iYmLDluKIoIhKJIBqNwu9XupvMIshCiXZ2dmL79u1Ys2aNtk0z0/QCVXnggQdw//3349SpUzh+/Djm5uYAFE97UVHb+aPRqDaXdHJysmbTYEqRSqVw6dIlXLp0CQAQi3Vg7doBDAwMYM2a++Dz+ep6fEKIc8iyjKGhIVy5cgVXrlzB0NBQ3escPcFgUGumVevFSo4fiUTw2GOPYcOGDQCaX5wqLSNQQJHiI488gs2bN+PEiRM4c+ZMRX2e6iN3enp6MD09jYmJibr1lRYyPj6GL774Al988QVEUUR/fz9WrlyFlStXYsWKFdqwcUJI45HJZDAyMoKhoTji8TiuXr266GVIq0Xt24zFYobl9gqfy6mifx8Oh/HQQw9h8+bNFY3EbTZaSqAqXq8X27dvx9atW3Hx4kWcPn0ad+7c0T4vFZUKgoC2tja0t7cjnU5jfHwcExMTpgvc14NsNovr169ra1QKgoC+vj6sXLkSK1euRH//CsNTDggh7mJiYgLDw0OIx+MYGhrCzZs3azonvVLUuqyjo6Oob7MSent7sXnzZqxbt04TaiuJU6UlBaoiimJu0YONGB4exvHjx3Hjxg1ks9kiiRa+93q96O3tRW9vL2ZmZjA5OVnTJQMrQZZljIyMYGRkBEeOHAEAdHd3Y8UKJTpduXIlR/gS4hCpVAo3b97E8PAQhoaUV71Hyi5EKBRCNBpFR0eH9lQUoPQ6tXpEUcTKlSuxZcsW9PX1taw09bS0QPUsX74cy5cvx/T0NI4fP46vvvqqooXo1VWO1D6DmZkZTExMYHJy0rbIVM+dO3dw584dnDhxHAAQjUaxYsUKLF/ej76+PvT29vLRbITUgbGxMQwNDWF4eBjDw0O4deuWrf2XpYhEIojFYohGo/B6vQbxlROgKlG/349169Zhy5YtCIfDC27XSlCgBbS1teGpp57Crl27cObMGZw9exZTU1MAiqNQs6ZeVaaA8migiYkJjI+P2zLc3IyJiQlMTEzgzJkzAJS7yJ6eHvT1LUVfXx+WLFmCnp4eLjlISBUkk8lc68+wJk11YKLT6Jtno9Go9qCLaiPGtrY2PPjgg3jwwQe1sRYUpxEKtAQejwdbtmzBli1bcPXqVZw5cwajo6NVfYHC4TAikQj6+/sxOzuL8fFxjI+P131aTDmy2aw2bebUKSVNkiR0d/egr69Pk2p3dzcjVUJy3L17FyMjwxgeHsbIyDDu3LnjKpmIoqg1zcZiMUPzbLkouLCpVr3B3rRpE1avXl3vYjc8FGgFrFmzBmvWrMHs7BwuXryAq1evYnx8HEBlUSkAbQ3e/v5+JBIJjI2NaQs2OE0mk8Ho6E2Mjt40SLW3txc9PT3o7OxCR0eHdkcL8GkzpDmZn5/H+PgYxsbGcPfuPYyO3sTIyIijN72lUEfPdnZ2GqRZblk9s/eA0tWzatUq3H//AwiHORCxUijQKggGg1pUeu/ePVy8eBHffPNN1cPO1eUD+/v7kclkMDk5qTW1uuUfVR1ePzIyYkiXJAmxWCwn1E5NrLFYBwKBgEOlJaRystksJiaU1qCxsTHd655t09OsIAgC2tvbEYvFEIvFDP2RVqLhQCCA/v5+rF+/Hp2dnepRalji5ocCtUhHRwd27NiB7dt3YGgojitXrmB4eBjpdLriqBRQhNTZ2Ymuri4IgpC7Ax7XhOrEQKRyZDIZ3L1713RpxGAwqMlUL9ZotB2CwOZgYi8zMzMYHx/LRZTjWmQ5MTHhqubXcoTDYa1ZNhqNFkWZpZbUK7XMntfrxZIlSzAwMIClS5eBjy5eHBRoDVBH8KbTGVy79jWuXr2Ke/fuWb4r7Ovrw9KlSyEIAqamprS+08nJSVf/48/NzWFubq5oAX91VSdFrDHEYh3aXXQwyOYiYp1UKonx8Ync/8hYLqpUfndq4N5i8Pl86OzsRGen0rqjLmwAVNeXqUcURXR2dmLVqlVYtWo1JIk3s7WCAq0hkiRhYEBZdi+RmMfly5cwMjKC8fHxiqJSszR1OcFVq1blmp4mNJlOTU25Ypj8QmSzWe0m4No142fKQtV5oaqv9vao9lR70tpks1lMTU1pza5KC40iytlZ58cQLAZ1ofZYLIbOzs6iRQ2s3jCrg4qWLl2KgYG18PnUFcsYctYSCrRO+P1+bNy4EQ8+uBHpdAo3btzA8PAw7t27pzXLlmvaNUOSJHR1daG7uxuCICCbzWpLC7qtD7VSkskkbt0axa1bo0Wf+Xw+BIMhBINB7RUKBREIKL+rP4PBoDZUnzQGmUwGc3NzSCTmMDeXyP00e80ikUi4uuWlUvTTS/ItMEGDKKuJMgvfe71exGIxLFu2DMuWLYPH48010VKa9YK1jg14PB6sWbMGq1evASDj9u3buHEjjrt37xQNQKqm/1S9y4zFYhAEAYIgYG5uTotSx8fHMTU11bCVTzKZRDKZxMTE+IJ5JcmTk2nAINdAIKD9DASUdL+fC/LXg2QyqRNiwiBH9XdVjKnUwouUNDpqc6waXap9mKoka7GSTzAYRFdXF5YvX46uri4AAvs1bYQCdQDlcWk9AGTMzMwiHr+BW7duYXp62vQfqhqpBoNBhMNhLFu2DIIgaKN81ZGG4+PjFa2w1GhkMmlMT09henpqwbyCICIYDGhSVX5XJOv3BxAMKj8lSYIoitpP5SVV1WrQSMiyjEwmg2xWRjabQTabQSaTRTabQSIxn5NiXo7GnwlH1nR1C+oI2a4uZcpXZ2cnwuFw0YCfSvZTLsoURRGhUAg9PT1YvrwfwWAAlKZzUKAOEwqFsH79/Vi/fj3S6QxGRoZx8+ZNTE1NWR6Bqxesx+NBZ2cnuru7AUAb6Ts5Oam9JiYmSsq7GZHlLGZnZy1PWRAEoUCuEiRJNP2pirdQwsrnxXn0+xYEEYIAndCyuZcqtvz7bDaryU5NV2RozK+kZQx5M5kMMplMy1z/xaI2laojY2OxGNra2iCKYk2aYs2O19bWht7eJViyZEluEBCN6QYoUBchSRL6+1egv78fsgzMzs7g5s2bGBsbx8zMtOEOv5qotPBztVmzr68PQP4fWD8f1eklCN2MLMuum15E6kMkEjH0WarzLwujylourC5JEoLBIGKxDixZ0otgMMS+TJdCgbqYUCiENWvug/JwdxlzcwmMjo5ifHwMs7Ozi2oyKxSuKIpa0xMArU9Vv56vOj91enq6IUb/ElIp+lV98nOYY9ri6wAMfZcLUU2UqTbLRqMx9Pb2IhDwQ5Ulm2bdDQXaQAQCAaxapTxMG1CWHbt16xYmJsYxOzuLbDZrOSot9V5dgnD58uWaVAFlkvr09LQ2nUb92UpNwaSxUOcjR6NRw8/29nYEg0EAKBrgU82SeAs1xaoj5wVBQCAQQDQaRXd3D/x+v/Y5aSwo0AbG7/ejv78f/f39AGTMzydx9+5djI+PIZFI1C1K1D/CTR2spP7zy7KsPdJNFav6eDenn4VImh91qoi+j1J9hcNhTWKAUZKq+BaSYLWo+/L5fIhGo+jq6s7NyVQjTEqzkaFAmwifz4elS5dqfZsAcvKawOzsLJLJZE2i0nLb6+/y1XzqU13UCfGTk5NaBKv+VF9sGiblkCQJkUhEu4HT/64swNFukGC1o2ALqSSq1DfxyrIMj0eZUtXW1q6VR81LmgsKtMlRm6iA/N2wOvp2bm5WGwxjxz+3JElaP6u+UtFHsHNzc5pU1SZh9fepqSnMzMxQsk2KKIpoa2tDe3s72tratJcqyLa2NoRCoZIDeKp9UHSp95XkV0cvS5KEQCColVv9XJ+XNC8UaAuiVkZAvqJRBZVIzCGTyWhTK8xYzAjgcvkFQUAoFEI4HMaSJUsMYtWXRR+9mq1eMzs7q/1stJWZmg2/349QKFT0Up9IFAqFDHJUZVUoxGoG8NSabDaLdDoNQRDg9/sRDke0/x8KsrWhQAkAIBJp063DCQAyZmdnMTMzg/n5+VykKkOSJEfWqFWlq/ZxqfPuAGMUWxjZZrNZJBIJTaqqWPWvubk5zM/Paysfzc/PY35+nlN4ckiSBL/fD7/fD5/PB7/fj0AgoN3shMNhU0mGQiHTp4cA5oN1ailHK1FmKpVCKpWCLCt/s8/n0/4O42IFlCZRoEBJSdRIAchLFQCSyRRmZ6eRSMwjnU5BFavPl18ir1ZRaTkq2UbfZ1aJcPVpAJBKpTSx6l+qcAt/JpNJ3QIGxld+pZ/iz9QmwcI8spy/aSlekMH8vXExBsGQRxWhXoalfnq9Xvh8Pm2BgFLNpoXv67GYQCX5Kx0Fq94QZrMyJEk5J8FgGLGYT8sJbuZpAAAgAElEQVSX24LTSEhZKFBSNV6vF9FoDNFoXqyyrFSis7MzSCQShojV6/XC6/UusNfFU2tpq012gUCgZJ9ttVIul7/SfVUirVL9glbzq2WotfTK5V8M6XRaa0WQZRmiKOVEGUIwGNYWJmBUSRYDBUpqhiAIuea8MNRoVRVsJpPB/HwCqVQK6XQmV6kJWuTq9/srEpwdka1T1KLs9ZacnZQrSyKR0ASZySg3AaIowuNRomav1wev128QpCpNQmoFBUpsQRQlBINhBIMy8nWirBOsjGQykasQM7ltRHi9Hi0KrEff62KlVa8BVVbLU29qsZjAQvnzjzpL5JrEs7kbLlWQXoiiCJ9PWfTfuGqPe84VaX4oUOIKlObSQC4SVdLyTYnAzMwc0uk0Mpk0stmMJl5BUPr9PB6lqVjfv6dSa8k5KTX12HY2pdYiKk2lUtpgLbUPUlnEPgultULItUh44fF4cs3VEvz+oK4ZG6AgiZugQElDIAhCri/VUyRY9fdUKoNUahZTUzOQZRnpdCo3EAcQBL1sPfD5vLlmPo8mXq/XW5Mot95RqRl2NcWqg3DU5lP9wCq1eV4d/KSUQ7m5KRycpaZ7PKAgScNCgZKmRBAEeDz5gUv6aDadzuT6YRMo7KvNZmXIcib3u5ATLwCIEEVBG83q8UiQJA88Ho8mYa/XaxgZWzh6ttRrMegfR5aP6opf+nypVBqpVBLJZArpdAqpVLogrwxZzuYGECnnQOlDVM5B4eAb9XdFkiIkSeRIVtISUKCE6FCbDpXfAb1gs1k51z+bwfx80jC1Rz8aWUE2iZQX+jy/v3wkJht+FwRRF90VR27G30t/XmpwTf5z/SuflxCShwIlxEUYBauK0pDD5hIRQkphvlYbIYQQQspCgRJCCCEWoEAJIYQQC1CghBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYgAIlhBBCLECBEkIIIRagQAkhhBALUKCEEEKIBShQQgghxAIUKCGEEGIBCpQQQgixAAVKCCGEWIACJYQQQixAgRJCCCEWoEAJIYQQC1CghBBCiAUoUEIIIcQCFCghhBBiAQqUEEIIsQAFSgghhFiAAiWEEEIsQIESQgghFqBACSGEEAtQoIQQQogFKFBCCCHEAhQoIYQQYgEKlBBCCLEABUoIIYRYwDaBnj17dpVdxyKEENKazM/Pe+w6lm0CPXPmzK53330X6XTarkMSQghpIY4fP47Dhw8/bNfxbBNoKBQaO3/+PPbt24fx8XG7DksIIaTJSSaTeOutt/DJJ5+gra1t0q7j2ibQSCRyDwDu3LmD119/HVevXrXr0IQQQpqUu3fv4vXXX8fly5cBAB0dHRN2Hds2ga5ateobABAEAfPz8/jFL36BQ4cOQZZlu4pACCGkibhw4QJef/11jI2NAQAkScKyZcvu2XV82wR63333jUYiEeWgonLYzz77DD//+c+RSCTsKgYhhJAGJ5vN4oMPPsDbb7+NVCqlpS9fvhx+v9+2gTa2TmNZuXIlACUKVfn666/xox/9CF9//bWdRSGEENKAjI2N4Sc/+QmOHz9e9Nnq1attLYutAh0YGMgfWMwfempqCj/96U/xm9/8BvPz83YWiRBCSAMgyzKOHj2K1157DcPDw1q6PiCzW6C2zZcBFIF2dnZibGwMgiBAEARDH+iXX36Ja9eu4Tvf+Q7Wrl1rZ9EIIYS4lDt37uDtt9/GyMiIIV0vz4GBAXR0dNg6rsb2lYi2b9+u/S5JkuEEAEo0+rOf/QxvvfUW5ubm7C4eIYQQl5DNZnHo0CG89tprZeUJADt37rSzaAAcEOj69evR2dmpvZckyTTfmTNn8Dd/8zf46quv7CoaIYQQlzA6Ooof/ehHOHDgADKZjOEzQRAgiqIm0fXr16O7u9v2MjqyFq4+ChUEoaREZ2Zm8LOf/Qy//OUvMTU1ZVfxCCGEOEQ6ncbHH3+M1157DaOjo6Z59GNoBEHArl277CqeAVv7QFXWrVuHI0eOaHN3RFGELMvIZrOm+c+dO4dLly5h586d2LlzJ3w+n53FJYQQYgNnz57F+++/j8nJ0osJeTweQz/nAw88gM7OTkfWFHBEoIIgYPv27XjnnXfyBfF4kE6nS0o0lUrhwIEDOHnyJL797W/joYcesqu4hBBC6sjQ0BDee+89xOPxsvn042ZkWYYgCNixY4cdRTTFEYECwP33348LFy7gm2++yRdmAYkCwOTkJH75y1/i888/x/PPP4/+/n47iksIIaTGTE5OYv/+/Thz5syCeSVJMjTdAsCjjz5q+8hbPY4+D/SFF15AR0eH9l4QBPh8vpJ9onqGhobwwx/+ED//+c8xMWHb0oeEEEIWSTKZxIcffoi//Mu/rEieXq8XHo8x3lu5ciWefPLJehWxIhyLQAHA5/Nhz549eOONN5BMJgEoYbnP50Mqlaro0WdffvklLly4gMcffxy7du1CIBCod7EJIYRYQJZlnDx5Eu+//37FA0N9Pl/RlJVoNIrf/d3fLUq3G0cFCgAdHR144YUX8Oabb2ppsizD6/VCEATDOoelSKVS+Oijj3D48GFtoFEwGKxnsQkhhFRINpvF6dOn8fHHH+POnTsVbaO2SOoX3BEEAR6PBy+//DICgYDjDyNxXKCAsvzSrl27cOjQIUO6KlE1Ol2IRCKBDz/8EIcOHcL27dvxxBNPIBwO16PIhBBCFiCTyeDEiRM4cOAA7t2r/CEpgiAYWhP1onzhhRfQ3d3tuDwBlwgUALZu3Yo7d+4ULZygSrSaNXLn5+dx4MABHD58GNu2bcOTTz6Jtra2WheZEEKICel0GseOHcOBAwcwPj5e1baiKGryLJTkzp07MTAw4Ap5Ai4SKAA899xzmJ6eLlqyyePxQJIkJBKJsiN0C0kmk/j0009x5MgRbN26FU899RTa29trXWxCCCFQutOOHDmCTz75pOxczlJ4vV5tnr9ekoIgYO3atYZFeNyAqwTq8Xjwve99D++88w6uXr2qzfMBlLuSUCiEZDJZcZOuSiqVwqFDh3D06FEMDg7iiSeeQG9vbz3+BEIIaTnm5+dx+PBhfPLJJ5ienq56ezXqlCQJsiwXRZgbN27Es88+W6vi1gxXCRRQJPrSSy/ho48+wunTpw2fqe3iXq8Xs7OzVYfx6XQaR44cwdGjR7Fu3Trs2rULGzZscHwkFyGENCK3bt3CoUOHcOzYsaoDGxWv16sN+iyMOmVZxs6dO7Ft2zZTsTqN6wQKKCfumWeeQTQaxaeffmoQnCzL8Hg8aGtrw9zcXEWjdAuRZRlfffUVvvrqK3R1dWHXrl3Ytm0bR+4SQsgCyLKMs2fP4uDBg7h06ZLl/QiCgFAopM3vLJSjJEn4zne+g/Xr1y+qvPXElQJVGRwcRFtbG9577z3DyVWbdsPhMJLJJObm5izfmdy9exe/+tWv8O6772rNu319fbX6EwghpCmYm5vD4cOHcejQoapG1Jrh9XoRCoUgiqLpuJZAIICXXnoJy5Ytc13UqcfVAgWUJf/C4TDefvtt0yYCv98Pr9eLubk5y00IgDLg6LPPPsNnn32GtWvX4oknnsCDDz5YtHQUIYS0EiMjIzh48CCOHz++qDoWyEedfr/fMLdTL8loNIrvfve7iMVirpYn0AACBYD+/n688sorePPNN01HdomiiEgkgnQ6jdnZ2YpWMCrH5cuXcfnyZcRiMTz22GMYHBx05FlzhBDiBMlkEl9++SU+//xzXLlypSb7DAaDCAaDhsXgC+nr68OePXsaZkW5hhAoAHR2duKP/uiPcODAAZw7d840j9frRSwWQyKRwOzsbFVTXswYHx/H/v37sX//fqxevRqDg4/h4YcfapiLSwghlSLLMi5fvozjx4/hyy+/XHS0qeL3+xGJRLTHVpqJUxRFbNmyBTt37iyKSN1MwwgUUNZE3L17N9avX4/3338f09PTpiNog8EgAoEAZmdnF9U/qufatWu4du0a3nzzn7Bx40YMDg5i3bp1ADiClxDSuIyOjuL48eM4ceJETR/M4fV6EYlE4PP5TMWpirKjowPPPfecNrWwUeQJNJhAVVatWoU/+ZM/wUcffYSLFy+a5hEEAZFIBKFQCNPT00gkEjU5diqVwsmTJ3Hy5ElEo1E88sgjGBwcRE9PT032Twgh9WZmZganTp3C8ePHF3wGZ7VIkoS2trYF16oVBEGLOtXotNFoSIECSjSqDnH+4IMPMDs7CwCGxRcA5WLGYjGkUilMTU3VrFkCACYmJvDRRx/ho48+wooVK7BlyxZs3LgR0Wi0ZscghJBakEwmceHCRZw6dRIXL15EJpOp6f4FQUBbW1vR+uNmcztjsRieffZZbcZDI8oTaGCBqqxZswZ//Md/jI8//lhbR7dQouoj0rq6upBKpTAzM4O5ubmaluPGjRu4ceMG3nrrLfT392Pjxk3YuPFBdHZ21fQ4hBBSKYlEAufPX8DZs2fw1VdfLXqApRmSJCESiSAcDmv1bqnxJ/qoU111qJFpeIECypyh559/HuvWrcOBAwfKLiXl8/ng9/uRyWQwNTVlaUWjcsiyrMn0nXd+g76+PmzcuBEbN25Eb++Smh2HEELMmJmZwblz53D27FlcvXq15pGmitfr1SJOtQ4tV5fGYjF8+9vfxtKlSxfM2yg0hUBVBgYGsGbNGpw6dQrHjh0zPMHFrGm3o6MD0WgU09PTmJ6eXvSoXTNu3ryJmzdv4v3330dXV5cm02XLltf8WISQ1mRyclKT5jfffFOXukzF7/cjGo0iGAwWLXCjoh9JGwwG8dhjj2HTpk0NNcK2EppKoIAixsHBQWzatAnHjh3D6dOnyzZbqH2kqkgnJyfrdsd29+5dHDhwAAcOHEA0GsOGDQ9g3bp1WL16DbzeprsUhJA6Mjo6ikuXvsL58+cRj8frLqZwOIxoNGpYBKHcMb1eLx5++GE88sgj8Hq9rlzLdrE0ba3t9/vx+OOP49FHH8Vnn32GCxcuLDgirL29He3t7ZiZmcHk5GRNBxwVMjExjsOHD+Pw4cPweDxYtWoV1q5di7Vr16G3lyN6CSFGZmdnceXKFVy5oiz0MjU1VfdjqgODYrFYxRIURREbNmzAtm3bFhyJ2+g0rUBVQqEQnn32WQwODuLQoUO4du1a2fzqF6a9vR3JZBJTU1OYmpqqa5NIOp3O/WNcwbvvvov29nYMDKzF2rUDuO+++xAIcJF7QlqNbDaLoaE4Ll9WpDk8PGybjAKBAKLRKNra2rQ0s2Prm2RFUcR9992HHTt2aNs1szyBFhCoSkdHB1566SWMjo7i4MGDGB4eBlDcN6rH5/Ohu7sb3d3dmJmZwcTERM1H75oxOTmJEyeO48SJ4xAEEcuXL8PAwAAGBtZi+XL2nRLSrIyPj+PKlcu4cuUKvv76a8M4jnojSRKi0Sii0ai2+AFQekStnv7+fuzatQvd3d1N2VRbipYRqMqSJUvw/e9/Hzdu3MCJEydw48YN02kv+vf6qDSVSmFychITExN1GRJeiCxnEY/HEY/H8fHHH8Pv92PFipVYuVJ5LV26FJIk1b0chJDaks1mcfv2bQwNxbX/8cU+5aRa1AVnYrEYIpEIABQJsHDgj/798uXLMTg4iOXLl7eMNPW0nEBVVAGNj4/j+PHjuHz5ckVC9Hq96O7uRk9PD2ZmZjA+Po7p6Wnbvjzz8/O4fPkSLl9WnsPn8XiwbNkyrFy5EitWrMDy5f3w+Xy2lIUQUjnT09MYGhrC0FAcQ0NDGBkZsfQ841rg9/vR0dGBWCwGj8ejSbPUE1L0eDweDAwMYMuWLYjFYgCav6m2FC0rUJWOjg7s3r0bTz75JE6fPo2zZ89iZmZmwagUACKRCNra2rQ5pZOTk9q2dpFOp3H9+nVcv34dgPLFX7JkCVasWIEVK1agv39F0coghJD6kslkcPPmTQwPD+WkOWT6JCk78Xq9iEajiMViCIVCVTe1RiIRbNiwAZs3by65vm2r0fICVQkEAti2bRu2bduGCxcu4NSpU7h7925F26pzSjs7O5HNZjE1NYWJiQlMTU3Z/gWTZVmbe/r5558DUJ5ks3z5cvT1LcXSpX3o6enVngJPCFk8ExPjGB4ewfDwEIaHhzE6Olq36XDV4Pf7EYvFiqSpBgQLzUyQZRk9Pf8/e+/+JMd13Xl+z7mZWe9+AGgQjQYpkqAeFqUwKWssjxgTO57dmPX8ML9MBH7aHzZiIzb2X9ifNhjrXY8d6/HYjrF37LXDEx7PzkxQY0vWy5RMSiItck09+BqCIkiCAgl0A2igu7qr65GZ956zP9zM6qrqN9ANgsT9RBQqq7IqK6sA1KfOueeeO4fPf/7zxeIZnntdnCXhW3QCIsIv/MIv4DOf+QyWlpbwyiuv4P333x/7R1eyXVRaynR2dnZMpuvr60daybsbKysrWFlZweuvvw7AV8udOHECp07N49SpUzh16hROnJiDMWHx8EBgL7Isw7VrV7G4uFhcrgx7cd8N1Gq14XdQrVYbfu8cRHrMjPvvvx+/+Iu/iJMnT+66hue9TBDoLszPz2N+fh7r6+t4/fXX8d577x3oP8poZKqqWF9fR7vdxtra2of661REcP36dVy/fh2vvfbq8Fzn5uZw6tQp3HffPE6dug/Hjx/fsUI5ELhXuHnzJpaWlrC05IV548aNu04kjUYDx44dw+zs7HDu5X7OcTIKbTQaeOihh/D5z38e9Xr9KE/5Y0EQ6D5otVr48pe/jH/4D7+MK1cu48KFC7h8+TLyPN9XVAr4X3QzMzOYnZ0FgLHI9E5MjdmLcszm6tWrAF4B4IsFTpyYw7Fjs5idPTb8MTA9PYMoCpW/gY8PqopOp4OVlRWsrq5idXUVKysruHbt6h2dSrJfmBlTU1OYmZnBsWPHhoWDu627udPtJElw+vRpfPKTn8T8/GmE38z7Jwj0gCwsLGBhYQHOObz33nt45513sLy8fKD0LBENB/MBjE2NWVtb+9Aq8yax1uLq1SVcvbq0Zd/U1FSRJjqG2dkZzMz47Var+SGcaSCwPwaDAVZXV9Fur2B1tT2UZbu9eleMWe5Gs9kcVs62Wq2hCG+lmKfMOD388MP4xCc+EabC3SJBoLcIM+Ps2bN4+OGzyLIUb7/9Nn7+85+j3W7vOyotSZIEc3NzwxXZe70e1tbW0G63P9Sx091YX1/H+vo6Ll26NHZ/FEXDSHtmZrYQqxdsmF4TuBM457C+3sbqahvtdnsoyNXVVQwGgw/79PZNpVIZpmXL6SYAtkhzu2Kgne6bnZ3FAw88gLNnzyKOkxBt3iZBoIdAkiR49NFH8dnPPopOZx3vvPMOPvjgg7Hx0v1KlYjQaDTQbDZx5syZ4dip/xJo77pU292AtRY3btzAjRs3tuyr1+tFReBsIdkZTE/PYGpqCkShgClwMDY2Omi32yMX/3/kw6h+PwyiKBoOk8zOzm6ZarLbD+ndJNpoNLCwsICzZ8+i0WgGaR4iQaCHTKvVwuOPP47HHnscN24s49KlS7h+/fq+xLedVJl5WFFHRGPp3vX19SPv03uY9Ho99Hq9YRvFktHxnOlpX3I/NTWNer2OWq2GSqXyIZ1x4MPEWot+v49+vzcc3ihlubbWviOdwI6SJEmGQzmzs7OYmpoa7rudOZZEhHq9jhMnTuDBBx/E7OyxIM0jIgj0CDlx4gSOHz8BQNHr9XDp0iVcu3YNa2trEJFthblXpDqZ7i2LH0a/YI5yFZmjQESGX4zbwcyo1WoTlzqq1erwdrVaXlfv8NkHDkKWZej3+xgMBoUc+xgM+sPtUpj9fv+uqQU4DMqWeWU6djLCvJUoc7KRe6vVwsmTJ/HAAw+gVqsBoCDOIyYI9A5Rr9fxmc/4+aXOOVy5soilpUWsrq7e1hfFaHVvKdrBYDCW2vqoprRKRATdbhfdbnfPxxIRKpXKmFRrtSqqVS9Xf5+/rlSqYe7rIZCmA/T7g6EUB4P+2Lbf5+V4txfqHBZRFA1TsZNjmLvNy9yrYnbyNaanp3Hq1DxOny57Ygdp3kmCQD8EjDFFq70zUPWNDq5cuYybN1cwGGyd0nLQoqRarYZ6vY6FhQUQEZxzWFtbG46j3i1TZ44CVcVgMCiKRVb3fHySJEOx+uvqMG1sTARjGMwMZgNjDJh5eO3vIzCbYpsB3P3fXiICEQcRgXPj26pueJ+/FjhnkaYp+v0B0nQwJsXBYPCR/nF2GJSLTZTjl8eOHRtbzutWK2W3o1qtYmZmBgsLCyOp2SDND4sg0LsA/59uFqo+erx8+QPcvLmCbnfjUH6xG2OGy7IREYgI1tqxsdQPu1vSh0WWZciy7ND6lBLRiGRNIeCdr72INx/PTCOyJgA0JrxJ6ZX7NmXnJu5zE5K8NyLAoyJJkmFrPD9mP10UwdGhpGInYWbU6/WiHecZVKvVoTQDHz5BoHcZ1WoVjzzyCM6eBQBfgXv9+jLa7VX0er2x9M9BotLJ/b5JwrhUAYyNp5Zjqh+l0v8PG1WFcy6I6iPOaFRZpmBnZmZQq9W2jSoP2rxgt9et1WqYnp7GiRMnMD09jc0IM0jzbiMI9C6n1ZpCqzUFQKEKdLtdXL9+vRBb/1AjxrLBw/T09FCqRIQ0TYctCMsx1U6nc1d2aAkEDkqj0cDU1BSmp6eH45bT09Mwxuw7qrxVmBlJkqDVmsLJkyfRaDSCLD9CBIF+xGg0GnjwwQeHt3u9HpaXl7G+voZ+vz/2K/egUelOtyuVCk6dOoX5+fmxaDXP86FMyyk1ZYOFj1MFZeCjTxnVlSnX8rrVag1FCYwX+Bw0qtzv6iaVSgWt1hSOHz8+ttRg6Dv90SMI9CNOrVbDAw88gDJCTdMUy8vX0el0kGXZkY5pJkkyXFy8/M/P7Kta+/3+UKajbQo7nU5IbwaOhHLprnJuZSnM6elpxHG8oyRvR4q7UT4vSSpoNhs4cWIOlUolRJgfI4JAP2ZUKhUsLJwZ3lZVbGxsYG1tDd1uF1mW7hqZ7jcq3Ylyf71eR6PRwPz8/FCq5fN6vR42NjaGU1O63S42NjaGl16vd89XdgbGYebhAvatVmu43Ww2h9ujq5CMjk0e5cLPo8VDIoIoilGr1YbR7ej/lRBhfvwIAr0HKL9kRr9Uut0NrK+vo9frIc8tiHQouqOknFBelvmPjrWOfsGMirXT6QzlWqaMu91ukOzHhLIJQCmdyUuz2US9Xt/SPH0nSe43lbrT7b0eXxaKMTMqlerwPMt/v0GU9w5BoPcojUYTjcaoVIFu1wtqMBjAOQsi2naVhtutAN7r8WUFZDk9oLxv9CIiY91sRi+9Xm94PXoJqeM7QxRFaDQawyxEvV4fu5Tdo0bvA7CtEG+1gOewUrHOuaJlIKFaraDRaKLZbA3nXQZZ3tsEgQaGNBoNNBoN+O8Z/2WTZRm63S76/T6szSGiYCZEUfShfnmUKb1ms7ltFLvdfXmejwm13+8P31uapsjzfDgvtLydpunwdpZlH/uo10dVFVQqFSRJsuN12XCijA5HL3EcA9h+rHEnSX6Yn6uqwloLay1UFcZEqFQS1OuNkdaQoVlBYCtBoIFdieN42OS9lKoqIOLQ63XR7w+KhcUFxhgkSTL8tX+YUelhHKMUwLFjx/Yl3NGx2/IyKtlyu5RreSmbGZTjYttdNhsejG9P3vZf6AZRFMEYM9IFife1PXlf+RmUIpyU424VqbulTW9XhoeRdt3rGGWXqrIJPXNURJV1GBNtE1UGYwZ2Jwg0cEv4CLCFRqOFsgLYo+j1+kjTzTZvRIw4jhDH8bAf6N3ArUg+juPhjwRge8keVMo7PX6nfftJde4n6tvp8SWHNW3jqB6/HV6SPnvgXBlRGlQq5cIDjeLYQIgqA7fL3fNtFvjYUDZwBzBMB5ffg3nuIzZrczgn8K3vCFEUF5FQPDzOdoI77Mj2MCLho+JWzuWjILnbwVqLwWCANE2LSF1BBBgTIUlixHFluIjAuCCDLAOHTxBo4I7io9BSkrpFsGmaIcvyYrxVQAQwGyRJPEw93olq4f1y1MLe6fEfpsR243YEXKZYN8ekbfFvgIb/BqIoBpFBrVbHZIu70e1A4E4QBBq4qzDGoFo1AKpbpg5YK7C2V1RGuuFYIREK0TKiKEIURcNCFz9xnQ5Vcgd9/ofNnY4yR6eb+FVc+hgM/DixtTmsdUXqWKFKw8b6cRyhFGAUJYiiZHg8f32ANx0I3AGCQAMfOXw0YrYItsSLdoBu1y/ZJuKF6x/j5WYMD4tyyiKdKIoQxwniOEal4gtsyorS2+FOpJFvV5I7ISLIsqwYVyyLp2whQgtr/TQP52xR+ITiB43/fIl4ojiHEUU89v7uph8bgcBBCAINfOzx0Y1P+45O0XEOcM4CsFBNMZpSHn1sWU1bRkzMZVEPD9cL9RIuhUETVbCTt83Y88rrcnu04paIinU6x6t6yxVfRpcsc85BdbvlzgQiWlT1yjB6d84Ol0Hzx9bivW+mRkcFOH7/5H3leyjuDVIM3AMEgQYCe1COwY2O1ZYyK5szTI7lTj528r7t92+/vVO0Np7a3G575/3bSdFv08T+QCCwE3esGkNVw3/JQCAQCBwpd7Kg7o4IlEI+JxAIBAJ3iDvlnCMXaJBnIBAIBO40d8I9RyrQIM9AIBAIfFgctYOOpIgoiDMQCAQCdwOlj/QIBkcPPQLdSZ53S6eUQCAQCHys2dZBRxHYHapAdznBEJEGAoFA4E5xRyR6aALd4cSouECDRAOBQCBwxIzkOof+GeUwJXooAt1Fnp5z58KgaCAQCASOHAKAc+do8q6xxxySRG9boPuR5zmEMdBAIBAIHD2qSueAOyLR2xLofuW5vLxMoVrEp0YAACAASURBVBNRIBAIBO4Ey8vLd0SityzQg8iz0+kEeQYCgUDgjtDpdOhOSPSWBLqXPM9NyHMwGIQINBAIBAJHjqrSYDCgUYmeOyKJHlYV7ljkCYzLM01TkiDQQCAQCBwxqkppmo5JFMCekeitcGCBbmPqXSPPNE3JnjlDCEVEgUAgELgD2DNntkh0r0j0VqLQAwl0N3nuFHnaM2doPk1DCjcQCAQCR46q0nzhnoNGogeV6L4FuuuBd4k859OUsiwjkSDQQCAQCBwtqqAsy3aU6DaFRWMcRKL7EuiuRUM7VNuOynMwGLAiCDQQCAQCR4tCMRgMeFSi5b4dJHrLRUW3WkRUHHxTnuWOwWBA6QMPjMnTzsyQigSBBgKBQOBoUSU7M0OjEk0feIAGg8HQQUOJYmeJ7oc9Bbp70dDmnaOp2zP9PmVZRnmek52ZoeZgwBJ64QYCgUDgiFFVahaBW57nlGUZnen3x1K55WNHHYZbGA+99WksuxQNdTodzvOc0laLm4MBZ7Uaf/ub3/zLa9eW/+dbfr1AIBAIBHZhdXXtf/3ud7/7b7JajZuDAaetFud5Tp1Oh/dZVHQgdhXoztHn7kVDY/LMalzLMv7e937Uq9Yqj9zqiQYCgUAgsBtJJf70N7/5g/ValnGWjUt016KiHVK5e0WhB4lA/YGefBLnzo0XDY3Ks9vtctpq8Yy1lGU1zmsZDwaRyePU1Gq1f3SA1wsEAoFAYN9UK5V/lMepGQwik9e8RGespbTV4m63y6MSBUaKis4BePLJ8jC3X4W7o3nfeGPsfj8w+0mURUPOHaMZaylNU85rGdfynF0l51/49KdrcRR/cb8nFggEAoHAQTDGPPIv/vk/P+UqOdfynPNaxmma8oy15Nyx4fQW4JMYLSoCsMVtJbtFodsKdMfU7Y7zPfvDitu02WcvzxrX8pyzKDJRFpl/8Nhj9xMhOciHEQgEAoHAQXj4E5/4ZJRFJosi4yVa4zRNOW32uazMtWf6e80P3Vcq90BFRJPzPSfHPe3MDE1bS3mtxtUs4zxJOLEJ2ygzs7Ozcwf/KAKBQCAQ2D/1ev2kjTKT2ITzJPEuqtV42loqK3N3TOUe8LW2CHS36HP0ztHUbbfbZeeOUVk01HCO8kqFK9ZyHmXGGGMa1XoQaCAQCASOlEpcOWmMMXmUmYq1nFcq3HCOyqIi545ROR66bSr3AFHoviPQ3VK3zjlqNvvsnKO8lnGe55ynqcmjyES5Mc5aE8Xm2C18FoFAIBAI7JsoNiectSbKjcmjyORpavJiPHTUVbumcvfJXgLdcZ7MZOrWFqnbctwzsQk7a9nFjg2zyfI8PcB5BQKBQCBwYPI87xtm42LHzlpObMKj46F2m1TuloPs0uZvlDGB7jRQul30OVl1m2V+3NPVapRYyy6xbIwxhv3V+sbGxi1+HoFAIBAI7Ituv7826h6XWE6sZVerUTXbnNoyWpW73yh00pG7RaDbRp/piLF99JlTmqYsTUciQnmaGpck7FO3xjjLxlo27ZW1zm18JoFAIBAI7Em30123lo2zbJw1fhgxSThPUyMiJE1HaZqynckpz3MCNtcPHTvQPqLQfY2BjkafAMbmfDYHA3bNJo1W3W6mbnNDlEdMeXT+zZ9dv7WPIxAIBAKB/fH+B5evMuURUR4ZzsdSuWVVrms2qSwo2pwb6otjx9r87cFQoNumb4t5n6OMRp9pWTiUZZznvup2NHXrjDGO2Thm8/pbb3TTNHv/1j+WQCAQCAR2RkS6f/O9v71cesdNpHIr1nKeVzjPfEFR2uzzaBQ6eqyd1g0ddeVOESiVB9ht7HPaWnLNJjWcI6k5sjbh2MYszrGz1rC1xjg2bCliItNeX3vjsD6oQCAQCARG2djYeNNZS2wp8u6xxllrxDmObczWJiw1Rw3nyDWb3mH7Gws9aCOFJ7fcMzn2uV306WLLURRxFEVsmA2zM8zOkKPog8uXX7+9jycQCAQCge25fuPGG+QoKr1jmE3pIxd7R01GoZNjoVuP+uSOr8fATunbzb6AZfQJAKOVt87tHH1ay0bEsDAbR2QcIXrqqb/8+9zatdv8jAKBQCAQGENV869/41vPOELkiIywd5AvKNohCnVNGq3IBXyh7FhzhXNbe+SWztwuAh1btXtz8VHfdaiMPq21VFbeum2jT+ujT0eGHEUEF62ur8vFixe/c7gfWyAQCATudRaXlp5/88LbHYKLyFHEjooo1G6JQp21XFbk+nmh+XBeKPBJAJvt/UZeYv+diLYWD212HXLW+mvnKE8SrkqVVIQ2o0/ro0/HxpGLmMkwsyEi85//6q+eEZHsUD6xQCAQCAQA/dZ3vvM3RN41zGQcuUhcGYVujoWqCFWlSnmScOmx0mlld6LRA+/WmWjXIqKSsfkxc3Moi4dqec4qQi6xHLuYIxexSMSRMexz0GTIkRESIw4RkURXPlgc/PzS+y/c8scUCAQCgcAIV69ff+3HP375OpFE4hAJiSFHxgdvzkTGeDe5iGMXs0ssqwjV8pzLYiLMbbZr3zIn9GBFROcm5n6OpG9zXzwkzpFzNaqK+DRu7Fgix8Y4FhEWMezIGTXK5CgiokKiZL7+7W9/T1X1sD68QCAQCNy7fO8Hz/3tqGPIUaRG2ZHz2VAR76bIsYsdiwhVRci5WuEyRzYfT+OOzwndPg6dFKifvjLx2NGQ1llLADBePORtrhqTiGExhoWdMWqYiYwwGSIxxGSEEL3xxvn2lcXF/3qYH2AgEAgE7j1WVlYvfe/7P3hfCBEVrhEmw0TGqHeRGMMihlVjUhGKrR0rJgI23QaMOw8Yc+K+W/kB2Axly+rb4wCca5JzjlSEtCqklQpFLuIoEo6MsBFhI8xCzoiQYVZWJUNEhkSMEplvfPvp50RUbu+jCwQCgcC9zHefffZZLdxCREbVO0eEjJAzRTkuR0a8o1zEWql4d4kU9TzNwm3j1bh7vTZvncLy5JYHPVhcT1bfjl40UXLOsar6KJSFRZmZlVW8PFXUSxSIfvrTV649/8MfPnObn10gEAgE7lFefvXVF5959gfvEXzqtnSMFoGbKHsXiWHVwlGJbvHXaDUusOm8cZ4cu0VEtCUCPTcx/xP4JNJi/BMAhlVLrkaj1bfiHJcpXDXCRg0b8b8CiIRVlYlglMiQqlFS8x/+039++d2L7104vI8zEAgEAvcCS1evXv7jP/2zv1PSwilkiGC8a4RFyBhRNuqdVKZwh74qqnGdqw1nlQBFm9ptFts+t8180G1TuGPzPz+5ef9ojlhqPgpNpEKqCanGpCqkkZIR8YVELMzi07dKZCJj6iRqlIiLUJv/zb/9t99vt9vtQ/tUA4FAIPCxptfv9/7wj/74u9YKEZF3iqhRVdYyjStaRJ9+WFEj9Y7SmFQTSqTio8+a21LjA2Dovt2ay48KdNsH2H5/GHkeL66lMDXqgKpQHAvFsVI5/qlqCEZJlVlZWEWZVJmMSR5YmD/HzDX/K0FNp9t1f/Qnf/a9PM/t7X2kgUAgEPi4o6r67//Df/zB0vXrA6JCmqpRa6r5gBIzqbKKevcos3eRoXIcNI6V4lhIVQh1f8yyErd0HLDpvm3Ys5n8GH5QtZgjMz2NFgAVPwAriRAAiAipKqlGVI5/GgMCDLFhUlLO06wnKvnZ+xf+l2a9flKVmFTNhXff6Xzlq1/78S1+noFAIBC4R/jbZ7//+ks/+ckyqRpV4iSOGw/ff+Z/gCqzKisps2ECDBkDKsdBVSPyNTqFsxLvMBWhFgBMTxevMIeykGgvJgT65K4PttaSuPECIgDw4vQXqBIAMgrSIpWrqgwwKRMv31x5nplnF+6b+x9PHJt+VIlJofzdZ569/NKPf/LeQT7IQCAQCNw7vP3Ou1f/01e+ckGhrMTUatUXHlyY/59U1XU6G4vK5F2j6t0jwkZBAAgjnlLvKYwVEjlfSLT7GTw5dot32TfRgWh8XxH5enkWIgViaKRkCpFq8QsACmJRhoIGud3oDdJXiCg+MTvz391/6uR/E5GJAKX/58/+3Zsvv/ra4j4+x0AgEAjcQ7xz8eLN3/2DP3xFVYiV6NTxY48tnJz7F8zcvH5z9fvKPOYaY0AKQ1DvJI2UgBhAkUEtJFqffKER122ZyvLk+EP3lcIFAFtU4Y6yKU7AD8xqcTuCmnLbQNlvK7xYb6yuvqCAA4BGrfapB+8//WvVSm3KOYff/8P/+83vP/f85f2eVyAQCAQ+3rzy2uvLv/Wv/vWr/X5foiiufOLM/K/OTLX+AQDu9Qdv9NPBTR+0Fa5hJcAAQOGiyG+rkmq86a0Rh5Vs57qd2FOgdpvJpM7VNtO3IybfPMHi9si2QomLULo/SDu9fv98+Zw4imY+cfq+Xz0xO/sgCPTv/uIvLn3t6994P/T6CwQCgXub5//uh9d+7w/+4IJ1Fs16/dhDC6f/22qlchoAVNVdW1l5sfAMsWJToptDiuNewnjmVIqWfpOvu537JhkT6JO38y4B+BB525ehSVUvLd983jnplLeJKDoxO/3ZhxZO/1K1Uq391de/cf3P/+I/fOCcCx4NBAKBe5C//ua3lv70z//9B0zGnD459+kzp07+ijFcK/d3er1XB2nWGXsSAwBv66KdHbU/npy4ve8U7iEwduK5tYOlG8tPq6obvb+SJNOfOH3qsbljs/Pfe+759h/80R9fzrIstPwLBAKBewQRwZ//v/9x6b989WvLU83mzMP3n/7CVKOxMPqYNMsWl5ZvvLTN029LkgfhUAVKlnaIFgUQbNm3sTG4frO99uKW4xDx8ZnphYfOnP7kG+fP2//rd39vsdvrBYkGAoHAx5w8z/UP//hPln7w3PMb95+67xOnT554xBiTjD7GOtd5/+rV76nDVi8IFNvcDezmqFtjzxRuVKls+4LMrABAzEo0flKTtwEvTyEoACUiJSUFBMur7Qsbvd67271GEsfVT5yef6i9stL4P37jt66+/8HlsBB3IBAIfEy5ceOm/c3f/p2rb799wTx05vTZRr02PfkYUbVXrl1/3lpJAQEplQ7SwjEAsKuTiEipcFjpskm2c9+TE7f3HYFGcTw8mDH9oTw3Tyj3YiRSwIIcKYiUSJSElOAvIFJBKVIoAbp0dfmlLM9Xd3rt2anWbCXmud/67d/ufOPbf7MhEoLRQCAQ+Djx3N/9sPe//e+/fjPtd2fvO37sJDNv5ye9duPmS4N+ukqFQwB4p9CmZ0i8e0Ck5AonFX4iyje9VTisdBow7rq92HUeaGWH6BMAeuUJjJgc8CGyo0KecAoH9TNB/YUgChEFqQpIQFALuCvXrr8oIjtGmHEUxfNzJ46/8MO/i3/9X/7m+rXr191Ojw0EAoHAR4O1tXX57d/7/bWv/fVf6/zc8ZO1SqW602NX19bfWutsLAqRglA4RBUi3i0jroGDdxB5J42mb0czp72dXgzbOPDJ8f37HwNdBqIoUjZGmXl4GZ4QkRJZRRFKO/K/CJRZCKRCKhBRURIiEiKI/52gQlAZ5PnG5WvXX3S7SBQA6tVqhcVN/avf+df2W08/namGIt1AIBD4KPL3P/px9uu/8RuDbnt1anaq1di6vOYmG73elWs3b15QghDUu0MLlxCJKHnHkArBu8fL00epKBw1ms4ddRkbo1EUKZb3f/4HaOVXHHVtbfwAmTc5F0YnskrOh88iJIBTIRFSEqVCnkqiokKkoqChSPv9weoHS9f+P+vcYK8Tb9Zrlb9/8cX4//zN38yWb9wIFg0EAoGPCN1uV3/nd38v++bXvx5NNep15u2nnZS0O51Ll68uv0agoTgV5B0ipUgLxyiJkHj3CAmRFE6yY67ibGLsc+i23Qz65NitUYHuKKEkSdQYozcBGGO0a4wSs9KAlYg1z1nznDQnUkukzCLkSIhYiHn45ohUSFSUVIjZqZIQ1CmpIyKnUNdP086lxcUf5bnt7vaBAn41cJdlye///u/jqa/8Fxei0UAgELi7+dtnnnH/8jd/Sztr7SSKzJ5Z0Bur7XevLt94W6GiKFzhA0vvEGanhVuIdBikEbN3kCNhFrHkHZXnpHnu3UUDVmLWrjFjjkuSZDeZbI6Xbrd3bm5OAb+gdnS5ppjzHRlMFCnSFADgU7mkGQ+U4IuByCUCFbFGNGIWZREREiYRFYh/4ySk4kRVmMgpsYNAFCqk5EDqcut6P19c+ukD8/c9VkmS1l4fsGGm//r6a+bN8+fln/2zX6MvfelLd2weUCAQCAT25q0LF/QrX/mKpoOB2Yc3oap67cbNC+3OxiKIHKk6UnIKCJREmRxBnYgKA06JhRROfbNcvyQ1ixBYLKmyYwGxgDIFoBlBE1RVDSmsX03TRNGmHC/XFBWg1Wpp6cRJDMYnndL584/So48CvV6PRARpukiNRoMAIIsiroqQxDHFgwEN2HEB+YopZwhiSAwXS5oZJjFQZmIyQhqxwiiRYWajqoYAAwYTiJVgAGYARhVY73RXarXKVBxFOw4qj3/gQhcuXKAfvvCC1ut1nD59ephSJyLstj16OxC4mxj9N7ndtr/abnvn/ZuHoX3uH3/sdvu3P8+99u90nvt7nzu/91t/n9ud+0He893xPseft//3sfff10G5ePEi/uRP/1R+/KMfsdtztROPiLjF68s/W9/o3QBRDsACnIGQEygDa0aKXFVzZs4VyEiRC2vOxJkq5URiRTlXEsuOLZF1ROLYGEvMDkSSI5ckz0WTRBJVyaJIa6pijEGv94E2m02tVCpoNBp46ikAOD92npGq6vjA7VMKnBt7k0uVih7PMorasaI6AG8YRTQyAEukUFU2RlREVJ2QGFEWUWXHVEai7JTFkZBTqCVmpyKO4ENyVXVMcKpqQTCimn2wdP386ZMnPt1q1I/v9y8sS1P66699Dd95+mn9tV/7NfqlX/olGGP2+/RAIBAI3CbvvPMOvvrVr+nq6grhIAuXOJdfvnr9rUGarYFgQeqgaongRNWB4IpI0/mhQLWkcMrqWNlnN1lFCveQsIDdcEhRi14EUFUFFCLeaQlQOm6pUtGt6dmnxqJQVd3mMRNUKhW1ACVJonm+olFU0yxbA3OyOQ7KpBn5zr0ACyIVqIgRFrD4N8LiVGEVbMHiVMgBapXZQsVCybKyVagB2JCqUZBRFXvl6vW3j81Ob8zNztxPRPv+ixgMBvTVr34Vf/P00/jv/+k/xRe/+EVE0Z5vORAIBAK3yFtvvYWvf+MbWLl5EzhgW71+mm4sXl++mGe2B4IDyJGqVbBV1dIR/sJsoWpV4cDqFGwBtczqRNlRIU/H4lO3lgSOVEGaKCmJKlVRzCZZQxTV1ZgVTZKGArtP4yyZtIkCoKcA/OPRB42Mg7ajSGtFIVHSN8qmr3kUCQNCqgJA4FSgkYgRZ5SdsjoVdkrifzmALUOsgiwDVpWtklqwWiiMkhooDKAGvg09r7TXrnZ7/e7pk3MPVZK4hgMw6Pfxta99DU9/5zv4J7/6q/iVX/kVVCqVgxwiEAgEAjugqnjttdfw9NNPY2Vl5Vaerzfba1dvrLavAsh85AkvSMAqqSWFVVarCkvKlglWActQK8q2LEiFsmNWR8LOsTg/9mkFTAJAlEhyQGJrVfs17VZVq8ZoO4q0tEI5/lny1Mipjp739uHYU09h7ty5YSER8DaWKmf0eJapi2M1RcWSMX3NokgMMzORQFUIEKg6VXFQ40TEKcRB2RI4gqpl8r8eVNQCahVkfT8FyhVqCOTlCWJVMKBMYBpkWffnVxYvnDw+e3p2ampu23PfhUG/j29961t49tln8cQTT+BLX/oSZmZmDnqYQCAQCACw1uLll1/GM888g9XVHZvJ7Uqa5enS8vIHgzTbUMASyCp8VpKAHNDiPrUEyqlwRhF9WjBbLrYJPqWrIo6EnWHjYJwjMg5EooCASERVsyiSBH01pqrGGI3iWOM4Vp++fRtAZbOA6Kmntj33nfKZiiL0rlarCgC2DMWXl7HWbGptY0O7SaLJSBo359QLFBBoJIhEQOpYjFMWByWrrAZKVtX/clBQDoaBkgGIGWrU58u9PAnM8Gu8MUCqStdurFzZ6PW783MnzkTGHDgnOxgM8Mwzz+DZZ5/F5z73OTzxxBN45JFHDnqYQCAQuCdZXV3Fiy++iBdffBGDwZ7T9nc+ztr6yvWV1auqmgGwDOQKtQzkAuSqsETIAeR+H+UAcjByKHIffZIFfHRKSpZYHYlxYsQ5EoEtIlBAFJBYKsP0bZdZKxsbulava2V5Gaj6etVKpaKl+wq275e70xvbksat1TRJU83zXE0UqTFG4yQR7hJHcSopszCZMgIVgjgVdVDj1KhVUQNVw2SMqlgwclUYBYz/YFBW5eYAGCAGSnmCASoXSiUA2Oj2195Lrwzm504sNOv1Pae6bIeq4vXXX8frr7+O++67D0888QS++MUvhvRuIBAITKCq+NnPfoYXXngBb775Jm5n3n1ubb60fGOp1x90CklaeA+UmcihRFFKE8hV1VIhU4F/Dgg5BJbJWFGxgFoYtezYgZwjQ07BUkagOedSERHtVzRusBjnhk5LkkSjWm3sjW0fe3r2jN5arZYOBgPC229j6UyRxm3HaqpGnTEax31Jo4gMs/CAJKOBMJEDEoaKQ6QOqo7VOBQi9YPBZNR/KAY+4jQgYlX10aeXJ4NA0JEaa/8nEQBrBZevXn9/ptU6Nnds5qS5jVLba9eu4S//8i/xzW9+E7/8y7+ML3/5y5ibO3CWOBAIBD5W9Ho9/OhHP8IPf/jDWxrfnGR9Y2P96o2VayKSDSNMgoViKEqAMhByVi9R8nLNQZRBdeRxyFlhRdVC1bKBhTNOVJwaOFjjQOyAzImqJFoVqqqkIlJBKmpqPn3bjjWuFunbt98GKj59u9d7iQA/gDsylcWnb596VOfOvYHl5WWaTOMas6JrUU2bZRq3bzSKU0kjFkORI4ChVggYi0KhygoybJBDwaowoDLihGEIA8QqRGVjffWuHL0AAEH9HChVoN3prKx3e5252em5manWzG79FPciTVM8//zzeP755/GpT30KTzzxBD7zmc9g+4UBAoFA4OPJBx98gBdeeAEvv/wybNFo4HYYpOng+s2V5d4g6wLqhvJU2MIDYxcFciLNCJRBKCNCRpDNNO5IipcNclFfdASDiehTBD476nLNpZKJaF7xxUMbG7pWqWjdrCgQAxhP3/rxz0e1iEOHQtUi/N4lAn0SwLmxe6LLlzUpOjKYKFJjrcZJIrHr04CZoygaiUIrDgATCnGqMmDYqBglGD9dBTkDrCIMIoaAlcCAsIK4bHGgpH6lN7B/D5u/C8rYVEWcXrt589rK+nr75LFjc61GvXnLf9MFFy5cwIULF9BqtfD444/j8ce/gIWF07d72EAgELgr6Xa7ePXVV/DjH/8Yly9fPpRj5tba5ZXVm+vd7jpUHeDHLMu0LQALRYYy8oTkIPLiBDIF8lKcJMhByFQkZyI/Tgq1BFijah0ZC3UWShaIrCg5AE40HUafA2u1GqeiSW2Yvo3j2KdvL19WbBnCe3LH97ZrCvcpAOcmWhhtNlVo61qzuRmFDiKJJKUyCmUS8pGosqq64kPiYtyzGOPUiWsw+Vb5fiVRUmKVok0QFfVJhUQLcRZ/KAEKsOa51SvXri/Wq9XmyRPHjleT5LYHNDudDp577jk899xzOHXqFL7whS/gsccew/T0lrVeA4FA4COFtRbnz5/HT3/6U7z11gWIHM5KkSIiN9fW2ivt9TX1aVeHYq6mr7YdGcMclacXZw6VTEA5KWUgZESUedFSDmg+eq3QHAprtJj+YsQRkQORU8CRRqPRp3Srdhh9VtptRbWKpUpFG0X0WVbf7jb+CYwIdPs07lOKc+NdiaLLNU3mRoqJRqLQNIrIDFiYB5IzF2OhYAKsasQEtRqBVTbHOUHwciSQAMRSClN8zwQFSMkPWBPBJ221eHrpT/8oL1hSVWhvMOhcurzYm2o1p+eOzR6LDqkV0dWrV/Gtb30L3/72t3H27Fk8/vgX8PnPfw5JkhzG4QOBQODIUVVcvHgRr7zyMl577TWkRY/zQzk2gLX1Tmd5tb0izuUKOFU4H3GKAzgHdDP6HIqzkKMiI1AKICOiFCoZQBlEMiGfyi3GSDMtU76qFhHlcGpVjSOyVnzg5kRVYhEhqUpaLcY+R6LPYfHQxNxPAMBTw+5DW9K3wD6KiCaj0DR9m4ZzQiej0L7RLE4ljhIXEVGeERG0mMepFjFIRX26VkEGxIAQQOwtWYx3liIX34EeULC/UmUtinFHRSrqb5OCELH6nQrStY2Ndmeju3F8dmZ2dqo1tcMq5wdGVfHOO+8U7ar+Cp/73Ofw2GOP45FHzobx0kAgcFdy7do1vPLKy3j55ZexNrE05WHQ7ff712+urKZZPig7CQHqGLBKKFKrUsiT7Zg4CRmJF6NAMwAZiWQgSrEZfWYgzXzBkeQGPo0Loly9RK3makWNA8gpyMWJOqfqcptJ3B8f+6y02xpXq8O5n9Xq9L6jT2B3gW4bhVYqFcXlGpK5VAEIajUYaynuJxInfZIoIpNllDELmyIKVWVLRBBlAmzkK22L6SlFBOqUwEIAEYmQegkRqRZeLfwoDGUFAapDkZJgGIlClYbbooCIqi6vrN5caa+tzU5PTc9MtVqHFZECQJ7nePll/4+y2Wzis5/9LB599FE8/PDDYA49eAOBwIfHjRs3cP78G3jttdewuLh4JK+x0ev3V9pra73BoA+fCnRFmtYBcOrHO4tLWQTkm+dQEUl6eRbRJpCRqhcnUSlSv9+Rr8aFr8Y1RQRqiayIOERkI1UHIieqkuciiYhIkkiMVLRfE5M4rdRqUrVWyuhzS+u+baLPScYEurWxfHEcbB+FzgPIV1Z0o9WSmtugQVKTZDCgLIooiiKXDYgMp8REZFSptG30nAAAIABJREFUyHkTEFFhP9ayupZAJARh//osgkKiIFUos6pC/Q1VUvIhKMPPPZVNaRIgChJAIgaJD1VJnBO5sdpeubm6tjYz1Wodm56aiuPDbY67sbGBl156CS+99BKq1So+/enP4NFHP4tPfepTiKL4MF8qEAgEtqCquHLlCt58802cP38e169fP6rX0fWNbu9me20ty/IMBIHCgUaiTp+ytYSia5D6cU/iYlqKICdFrqSZFqlZGhMmpeRTt6kXKTKQLzjScnqLwhJFuVHrGyqoOgWsiDgnFZdU1WXWSpJlMqhWJXYbulFtSGVlRZNG40DRp05Mft2fPJ56VHHujYko9DIwN0dxHKtrx1ppVgT9PvpJQskgokxSipPIMcXEGVFO8MVB0OK6uE0EhRR1RAo4IRRrxdGmRIfSBEOhUCFo0bRBAfZiLUQJQAAtJ85G/n7xK5gDIlBZXV9vr66vd6aajcaxmemp6hEMYg4GA7z66it49dVXEMcxHnnkk3j00c/i05/+NKrVA7XzDQQCgR0REbz33ns4f/5N/Oxnbx5JerbEichap9Ndaa93rHM5fITppy0CjiBF+hQW5FvulZW3oGLsUymnotrWz/P0kSYEGaDZROTpI1EnGZiKNK5kRJSVEhWFBSLf4g+wscJJoo7VuSyzEucV6VdJqv2+VCoVyYt5n4CfXbI1+hxOXdmVvQRatPR7Ek/h3LYVufMA8nxF21FLm9bqZCo3jyLHEZEhIpv7bkI0Mq9TLQADAhwBhkAgOIEUEmVRBasPWEnV65OUuNiGn+OCsoUgVAQQ8r+GRAHx9UkaMcrbEACGAFnf6HbWN7rdRr1WPz4zPVWvVo+kDVGe53jzzfN4883zMMbgwQcfKmT6GbRat9RIKRAI3MPkeY533nkbb775Jt566y30+/0jfT1rrVtZ72y01zsd8aW6pTSlTNVyIVGAbNE1yIJ8o4Nh1a1P3w47DBEhg1AhTh9dEmkKQSaFSOGKiBSUA85X3trNuaBaVPYCsFGszqo6cepia7ekbttRpJVi1ZWdK2+fLN/2rs0Utgh0pzTuZBQKeHMvnTmD41mm6HRko9VCE30MkoSSwYDyJIEZMNkoc8pMJiJiIspz/3wClefnewupA5Hx1hZVZYWAFQIwFGAuxjpVC6WKKhdC9BKVTUHKZDQK3/UoQrHfz0X193d7/Y1ub9CrVpLq8ZnpqWa9Vr2dhgy74ZzDu+++g3fffQfAX2N+fh6PPPIIzp59BA888ADMPlZrDwQC9x43btwovjvexbvvXoS1+ZG/Zppl+craemd9o9tV3fyuBeAUKr5QyAvUr6JCbizq1GIllaIFH4r2fL5RgmSqhUTJj31CJBelIvKUnASpEuVElAGufNzYcRTI4xhWVJwTdSLiIptIXmUXF6nbKvqyUa1KpdORYdP47eZ97hB9TqZvgWLOx5Y7t4rD3z53js4BWF5epk6nQ4PBgNI0pbm5Oc6yjAaDFqfNPjfTlPNajZPBwGRRZKIoMtlgEEVRZJgogmpsiSLKETNRpEYS8m0gYqgmgIkBTaBIlDWBcgJoMtzPSABKoIgB+PuB2B9D/XFAEYBYgYgIERQR/CovEbxIRy5kfA552FaQjTHRTKvZnG41G0kc37FFRJMkwYMPPoizZx/BI4+cxbFj5TrivrQYwFgPys3tvfZvbm9/nz/G1u299o+/9ub2XvvHH7vd/jv7Pnc+99t9n9ud+0Hex+h/x+22/dV22zvv3zwM7XP/+GO327/9ee61f6fz3N/73Pm93/r73O7cD/KeD/N9DgZ9XLx4ERcvXsQ777xzpKnZUURUN7rd/urGRrffHwwwDEq8OH3goZviHF7I+kgIxeooZdRZTlvxwsPmJfPzQNWnb4dyLNK0RVWuLyxyeblfgZwcZ6JqNUYeqVoQ5aJqrbUuqVb9tbUuq1Zd3O/LRqUilY2aVKsdSZJEl5eXpbJd9LlD8dC+BQrsLtF/vLxMAFBKtDs3x/NpSt1ul507Rs1mn3u92OS1jJNBZPI4NXEUGZMZk3MaMVFERJEliqgUn2o8lCgQe2liKFJAEzAXgsWmTIEY4ATQGLz5fAIiEcREiACKCBIpKAIhIoVRaFSEu8Ol0wAy0M01SMtLrVKpzky1Gq1Go8Z8NFHpTszMzODs2bM4e/YsHnroYVQqlSDQINCx7SDQj49ARQSXL3+Ad9+9iHfffRdLS4vY6Tv6KOinadbubPQ6nW5P1KdphxeFlAVCKCNPgoOqI5BVgoPCFwyVDRMUlrlsEl9IU8oG8eJluRlRFuOgUvbCLStyc1+du9niD7QZeUaqVlWtqNpYKtYlzuXWujivuKxqXdxPpF7P3cZGTYxZ0UajIUuVijaWl6WUJwB83y9btm95AruMge6YyoXvD7hcSBTYTOXOA9LtrnA7amkz6QuSGqrIgKiCPE2BBIjSmKzJwcWLMxXNcJkV6uu0KIIqpJiqoiAyqoCSqChUiSEKFiISVRXyKQUH9b+CxP8aion8XzJBnYCEFAKQE0hERG4YlRIKkUrxD2Q8Iu2naa+/nA6u3bjJU61mY7rVrNcqlTvSOaHdbuMnP/kJfvKTn4CIcObMGTz88MM4c+Z+LCwshAYOgcBHGFXF8vIyLl36OS5evIif//znyLLsjp6Dc07WNrr9tc5GN/Uvrtgu4iR1fpTMd/iBkoOfLmJF1ZGyhaoTIsvQXAFLpFbVy26YvvURp2/NNywEkkx9RW6mvuY0U7jN/USZWuSIkFMRacZALiPyjFzsXKWQZ6XiqsY6TRJJXF/aUVUrI/KcTN3OTdT3jLKTPIFdIlBg9yh0u1SuPXOGjnc6nOc5pa0WNwcDzmo1Tvp9k1cqHKepyaPIcEqRNbmZjETZUoQiBTuMRiMfnQJIAB+ByjBKRYJhyhYJgf02awxQRIJYoDERRQAiQGICGS1SugTykWiZxiWdiEiLRb19NyUGgaE+Kq1U4nim2WpMNRt18yEOWp44MYczZxZw+vRpLCyc2bKCTIhA9/s+dz73232fIQLdbf+9FYF2u10sLl7BlSv+sri4eMeFWdLt9dO1zkav0+/1VNRLc6z4shzvpImIs4xC4YqFrh0IxTinFp2GYFXVMihXLlK4QkUEWTZPKCLRonCIh9GolNFoDjsSdRaPl0jtZOQZudhJRW1srcsrFRenqWS1mkv64+OeN1stKatubyd1W3LQsT0FxpsrjC35crmG1hlIp9Nh127rxsyMNPu+qKiapkijiOIsozyOXGRi2HwzErVEUED9hNDNpgjkVB2ouK0C9n/RhLKgaPSXkjoaRqIaC5XVYRqD4AjkVCkq9juBRn4/IqCU52hESg6+Wrdc4Lvcpiy17lq6kl2/ubrWbNRrrUa91qzXqsx8R1O8N24s48aNZbzyyisA/Bjq/Pw8FhYWsLCwgPn502g0GnfylAKBAHyP2WvXrmFx0YvyypUrd2wMcyfSLMs73d6g3dno2dxaUDlLAQL/m03IT4koCzF9Gnc04twc+7Tiv3MtlHx/W/IVsShWSRGo5XKZMtJi7iaNjIF6gSrEdxSSstetb5CgVFTqDqttfTp4TJ5x7Cw7F2dW0iSRSprKIEmkWsgzKroNtVotWSsaJoymbgHsq2nCduwq0N3SuE89+qiee8Mvd7Z5Iu/j8uUHMFd0KRq027zWbGqz35dBrYZqliFNEsRZhhxA5GKMpnNt5Kfcwvf1AxGpRdkovrjWUppapGQ3B7AFWsxDQvHrCM6PgcKJ+qIiQB0RnAgcERwAUzynqNAdiUL9GKlDWVzkc/wGClYq+vmSUqfbdZ1ut0dM3KzWqs1mvdas1SvG3PmeflmW4dKlS7h06dLwvunpaSws+Cj19OkFnDx5EofYiCkQCABYXV0tZLmExcUruHbtGkTkwz4t9AdpvtHrDTrdbj+zNh9+hzLEJ0K0GN+EFN9xm6lbgkB1LOoEYIvvTEu+z61l9uOdOqy2Led7+tsYiSJ5TKATUqXNIiML5BF8iz4tFtmWSO1k5GnZudhalyeJq1jrBkkicb8vZau+arUqSZLo5cs1rVTeH5Pn3NycPvXoo4qntp/zuVv0CeyRwh0+aJ+pXAAYLSrylbkDbjabnBaVudUs4zSKTMVaHk3nGmMME0V5jqLIyEauSO2iKArCaGoXiH0ql2PoSAXuyHMAjUXhU7hSpHUJUZG2jYgQCRCRIoIXaUR+MqpRUuOLjcpKXWVVMuQX/TZEfgUZKpvi+8+kLDwignKjVq80m/Vas16vRnfR3BRjDO677z6cPn0a8/Oncfr0aUxNTSOkcEMKN6Rw95fCzbIUi4uLWFpawtLSIhYXF498HuZ+UVX00zTrdHuDjY1uP3e2qJody9j5aYBFMKJKQkROVYVIy8c78k0SHCk53QxQLIr2fAwvTQCWQMW2WrAXH1NZMFQI1E9vKQqBfD/bzaktm5LVouhIgdyoWtXIFwnFfk6pc86Npm3TKJJReVYqFdnY2JBSnqNFQ4DPnN5O6rbkVgUK7CDR0fHQTYmOT2+ZlKjJjLGRHxN1lg0RRUx5RESRK6a9kCsqdgmRK6Xqo0u/rSNiJYxIdFO+Uk5pEX8sIYkIiIjIjIq0SOFGCjWkZJRgiIoUrh8HHU53QRmdKhhequS3C6kqGETUqFUqzUaj1qrXK1F094V/jUYD8/PzmJ8/XVzuQxwnQaBBoPe8QEUEN27cwNKSF+bi4iJWVlZwN6Gq2u0Psk63O+h2ewMr6qCqIC/LYYQJVSgV28MMnt8mOFUIldcKp6SO/NinLYs0N8XpW+axso8yiyiUN+U3rLxVIGcduT2ygHYhYJ+yLdO0xk9LMcNoM7aqak0kTlRtZGPnEh95TspzcrpKWTR0kHHP8jPd63Pfl0CBXaJQYE+JltNbJiU6WlhUStQwG5tTxGwNFwVGbH3kSESRhcYEFxGimMgNBSoTESrJpjSH9/vo1EedQAxCREKRFykZIkQiXqgENarwU1384t/j012KKl1SsFK5pmkh1ZFiI1Ah0RGZ1qtJ0mw0ao1atVJJkjs2x/QgEBFOnDiB+fl53HffKczOzmJ2dhaNRiMI9IDvMwh0t/13l0Cdc1hbW8PNmzeL6HIJ165dhbUWdxtORHq9ftbp9QbdXn/gRGVMmr4gyEedRXEQRprMkKooTc7rHG2M4Mc8iWAV5FTVMZdpWnWsbJX9eKf6IiDfDUh1mLYdypQ3I8rR+wHkqsYqbK4wdpiyVQzTtaJqRSIXxWqdiBuVZ1kwNClPM1Fxu4c8gVuIPoEDFBFtMx6qKP81FkVFW0qBL19GOb0lyzqEjRY2mkCz38egVkM1TZHnFeRIQVVWyUSJSIvefarkG8YroAApkRMClEBCJAJLIgQhIke+GleoWD5HvCyHY6AEWIHE8IKMCLCiiEHqbwtFCrVe1GpUKQKpVV9o5IVazhsl5TK9KwQm0rGoVKW4D8SqYEKR3mUQVLk3SF2vn6YgotiwqddrlUatVq3Xqsndkuoty+uXl5cBvDa8P45jzM4ew7Fjs5idPYaZmVnMzs5gZmYWcRya5QfuflQVnU4Hq6tttNurWF31l5WVm9jY2MB+g4o7japqP01trzdIu/1+OkjTTEE6Jk3elKZq0S8cKirk25sOo011Zdc2n6ZVP/ZJ5EjVKXzKtpSnL0fx452k5MBF+zwfcQ6rbpVhmbhY53MzouQidYtibFQLUbLCUiT56DJnqsaVrfl0GOmSFRUnIk6q6vLMCvKKi7FVnj7ybOwYeQI4FHkCB4hAh0/YMRJ9EueKVn/7SedOW0tZVuNaLePBIDKuknOURcYlliNjjM19StcYY/IckWFnypQuWRTpXUQEVwoxBhCJK8Y8fcOEWGgYgfprIh9lQqKxiLRM7dKw4YJR8TIlUBGNqlGQoc2ORVumvPhIlIbRqE/9+vsVIzIdjUwB8vuJqkkSN+u1Sr1Wq9SrlXinIq67kUajOYxUZ2ZmiutZTE21sJmwCBHoQd9HiEBvLQLt9/tot0tJttFut7G6uop2uw3fyvXuJ81y2xv0s25vkPb6/cz5ee+K0SUcRyJNRSlNEiIVHYs6tWhvOjE1pUjjKuAIvmDIi1OdKjlin771K6qwHU3VbkacbH10qmPpW1ZYpaKYSNWyKWQLFMVBxppin0aw/39757LkRnJk7eMemUAVSQ3bNKbd2G+90UrPo/fp99HzaDWb2cukaY6arAKQ4f4v3D0ueQFQF14bbsZGZtxQJLPx8Rz3CIRlmyXlccSUc86l0jbnnI6DTLspp8Mod3dTfnjYyW73IB+GQa+xbQHgb3/7i26ddfsUeAKvA1DgyRB95Omnn+jd4yMfj/d8uj/y/enEp92Ox+MxnYYhjdPIc0s3Madpsr2jXIuMBsoOU78HMBKJg5EGZB0JNLQwJSLrlyVISSn5Fhc7tYh0ADwXqm7vNqqUlFhJE5QY3StMgUbBEYFWYaogIiI1G5igIGtBenN/t3t7d79/++Zu963avZeCmfH+/U8O1Z/w/n2o1p/8W2luAD33+7gBdPv3MU1H/PrrBwflrwWYv/7661fbX/mSmHKWTw8Px4+fHo8fHx4OU85mr5rKVLgDp/V+DZqKUhgU9i1lkJZXUspKKqE2CTDVSZrJt/mR2ulCCkwMmpQ0L8DJHPnQOMzdoAmdkFxlAgZeB6uqGjSTzUmqk+gwqeo0DGbTzi3b03CSqLQdj0d5GEcZHZ6xVaUtGHoOPIEvAFDgdSDaHbZwPLK8e0e7x8d02u04TxOPw5DyNHFKKQ1TShOfTIFmTq0a5Ux+nQtMxWEKz3UKSVxbzjPynwHSmSJVogTBoJ4bBWFQNSVKBLN3Y7sLUQIpQ0uu1HKjAGtfeMRUtr8Qq0HTFKuC+qKjUKVxr6QgGlNKb9/c79/s73Z3d/txv/tyZ/R+rhiGEe/f/wf++Mf/LMr1/fuf8P79+26rzQ2g2Lz+0QGqqvjw4QM+fPgVHz58wP/+7//in//6J/7vwwccDgd8z5FzlofD4fTp4XD6+PBwOByPJwOiKpSqNQvYFr6+KEgBP43NttELKVlekyDqBUGkUW0bBUOUQZqhZFtUyPKddmoQ3LqlDM91kvIExkReNDRXnNZmVq5y/VaUUlwUhyp00ExWHJQ0t6ozJcmqOg0y5mnIOeec0zDIaZpyGgYZj0c53t1l/u033e123SEJXxqewDMBClyCKPBXP2jhGoj+NE3UbnM5nfac9yceHKLDKaXY6pInTq0aZeaUaRoIGFg4dbZugSmGqkBlIKn3BFQFKlJUrFXlUnJLt4MoCEMA01RpWLvECp1ZvHaK0RympkxNgTYqleDtGltjCGTfAKfdvUIpMfP93X53v9/v7u/2491+P6YvfJDD54xhGLHf7/Hm7Rv84d0f8O7dW9zd3eP+3n7Z9R3u7u7BzZbbG0C/fYACwOFwwMPDAx4fH/z1EQ8PD/j48SP+/e//w8ePH/H4+PhdKsmtOByP08PhcHp4OJweDo/H42maDJik5esaYWBs78lzmvBK2qIuI9fZQrP5mjE04FT4YTL2dcpZiVxtBlAdmA5PgmZVnpTiy7EdhMxtsdAUitMKhbjeKyo0G7tWWLJBdJhEJLeqMw2SY4vKNBo8p8ks23Gs+c79fi+/DoNeD8/tnCfwPHgCLwAocFmJ4q9/X90nejj8P/qv/3qgf/uxf1GhG3nRuaWbdztOx5TyOHGo0TY3mpkTTzQw55TteMBU4OkwzRkDMyXyA+UFUhVpA1I1iCYAg1qVboJD1OxdTWL7SZOGnesQJVBS20/KFBZvzYd2MIVGvrTPmSJsXTg0F7nS7roANb6ofL8bx/u7u9GhOnzJb5L5mpFSwm6/x93dHd68eYM/vHtXYHt3d4+7uzvc399hv7/HbmfFTjeAttcvB6iIFCAaDB/x+PiIx8dP+Pe/f8OnT5/w8PCAw/FgVa0v+Oz5HkJE9OFwOD08Hk7+ehQRIUB1AczyayW3SeJ/WLO9nJHTJDFbtijNAk2CihUE2elCBtYKTy8QyqrIDJ2UyAqFoBlWZZvJ93mqaibmHpyuOBns1bg6iWhOCQWaDURzUp1EUpZBpySS21xnqM50GiTvck7Ho6xZtpHvTOlfOo6j/uEPf5D2kARgvs/z9ZVnxIsACjxdiQIVotN/PXTbXKafTl1e9G3OdHAVunNrNwqMQo2mlHmaaEjMiTknyWzbUVhSsXQdnES5A+lckSqZuiQp+dNEBtUEokGBREQDqaRQpUV9Wq40EVGAlbV+ZZor0x6m6kVGM5hSY/VSq1QBU6bUAnQOVAWF5UtAqNTx/u5ud7/fj/v97odSqc8JIsI4jtjt9tjv97hzRfv27Vu8e/u2AHcYBjAzUkpgTv49rfFH9+MCVEQhkiEiyFkwTSdTiY8HfPz4Gz5+/IiHT5/w8PiAw+GA4+GA0+n0TZy68zXjeDrlh8fD6eF4OD08HI6H43GCPRqtJbsJTEXkMj23GcoSqsWaLQVEBs1QnOiUpvpuBNTDECw/mlXVDkUgKxjSVm0SZ7Uvvs5QnYjtXtUVKBskzcpdKs4KzjQpdGLRHJYtC1v+NEkWSTmH8sxJiuqMQqFhkOPxKMn3d35MSft856j9NpV1eAKfT3lGvBigwEsg+jRLdzftOO8mTsdjyuPIeUop8SmJDBz7RnN2kDInmjAIc2LKKQqNmDgR5UHEFSV8H6hQIiDyp8kgakrT8qRi8CQkFRpABlFq86NqJxiBkKDqpxatwdQUKrp9o3UvaQPWsG6LIqVaiERQsNrn30Wg2tuYSh2HYbjb7cb9fjfsd/ZrNw7f3OEO32oQkf9iMFOBK3MCM4MTI5XrhBQQTgmJE1JKSINdD8Ng98nHc7OO29KqBjIR2xufc579mpCz2Ktk5EkaAGZkyZAsEIlfuawnIvZJLS6GfnBV+Bohqno8HvPheDo9Hg/58Hg6PZ6Op05dQnAVMIstS1oLgFrFaVZuD0qDJ5oK21CYWIemn2XrJwqRljxnozYzsedAzaZ1eLoyZTaQqlu57PtAy1eWaVZNk6jkKBASTZlFsg6YWAycKdlBCCJDZp4ky5jTkHM6nSTvdjkdBzkOR3mOZQt8WXgCrwRQ4DJE28MWgPpdomsQ3bJ08/09nQ6HFCAdTinlMXM6cZIhc0opTROnxFMykFp+lDP5dR44UzJFmgdiTpIRhzQkgVjeEzB4qiRTpkhu7zpEKXWqVCkRzfKhHUyJfW9pgSkZKN3uJQYpqxKTA3Nh9bqVO1ek9qrk2mKpUMkhumirKhUAmIj3u914t98NBazjOHzpw/FvcYtvKU7TlA/HUz4cj9PheJgeH0+n43Sa4B/IRV0SLe1YQtfWK0xV+991RXG21u3MmnUoCpRcadZ8Zw/NqLqt0GzznlraOrVpCpTZ2qygaFJigyjg+VCDqSlKTCqSFWli0WxFQWkSEb+2PGdKkrMMeXC1yVOSPEpOpyTTmAs4x/0+p4cHPWfZbuU7AT/bFji7zxN4HXgCrwhQ4PkQBYDW0m23urRqNOdM96c9n3ZHjmMA8zTxOI48nU5pGAaOIqOo1s3MiTMn4ZwCpOKKtIBUOClH3lMSiUFSzM5N1d6VQU25tqrUt7JoImCw83Ld4nWIcmxzMWXKZvMq94VHyiBisvbZqUbtSUehSjcsXjuDnwOoIBBpU5RkFb+kKMoVmEGVYVUMAGg3jMPd3W7Y73ajqdUxDcOQblS9xY8UpioDlMfp8Xg8HR6PU1aJc2NVAKzA0iFaqmQVUfTTAbQU/hQl2lq0xZbtlWenMgkqSiRQNZA2BUFk342cQVSgKb5dBQFPZct3ApPGgQmquVWbxJb7rDYtm+pUzco6kXIGMJFoVq7g1KQTa8oBTpaUJUmOPGdKtUhomiYZxjGfTidJwyBxHN943MnDeJCUkraqc7lFxSxbAPia8AReGaDAJkSB8mH9V/rrX+3qGks3//GPdHioalTeZYpjAPOp2rqn4zGN48iSB05pSpITnwNpsXaFDZQkyeBolbfF4iVKKur2LqVWlRJpiqP/7PB53+Yi2oA1YKrJAFkOqmfPmbKqJiViUj9nl1Ct3KJOlWsOdWntBlAxK0IyoILKOL8PQGIJVfTtvf0bf8W7YRh2u3HYjWPajeMwjkPajWP6Vk5SusUt5qGqepomOZ6mfDyd8ul0mg7TaToeTtn2W9qwczasApjDsswpbeSVslBq7tGrzyUw/VqhQkSdygQQ35iSlSCkWg9/t5xm7O3McMs28poBSmLKGufbans0XxQUudr07So2XnO1aNkLiQzGVkQkOazac+DklCXnIXOa5HQ6ydjateMocRwf/5a0qM77e0n/2ladwNyyBYDzX0v2mvAEPgNAgWsgihep0fzuHZ2OtVI3T1MB6TiNnMeJ85TSMGSOQqMAqaTEnC1HaiA1FWlVu9kUqlBiklQKjkiTim9lsfyoAdJtXrFio6RzRdrCtFGmZuM26lORlODVuHaikY+pxUeqTKSsYLoKqE31LilIG1VqedQGuApa5FLVMW552NYCxpZaBQAG8W5vUHW4pnEc0m4Y09f4erdb/L5CAUynKR+nUw5Q+q/pdJyyQw/YVpWxjFmspKYBe4hG7rKoS1OAzb0V/YSVu1JNuw5MgqgqCYhKMZBX0nYnCJFClGbbUkDSKc0OmtSA1OxaVgcs1BWmwRaKySBJOQqDRDkzaxaRzJpyVNWysINTstgHrQQ4o0BompIkr649DScp4PR9nQ/jKONuJ+m33/RzqU7g9eEJfCaAlsWfYekCZ9RoU6mbc6Zq6xpI99PE0xUgHZJwW2xUQOr5USZJLJSEJIW9SyQOVkql6IhgsBQ/kYgkiedQ1YqImq8/gytcz4WSsuVMwUpIPt4OoY/zdkOdKllR0UyVFruXQO11nzcltn8Ig5a2b82dbkF1BsiiVpu/xwVYZ4q1/J0nYt4bzSPzAAAgAElEQVTtx2E3jGkYUhqHgYchpSENPAyJb+r1FpdCVXXKWU5TlmmaxK6nfJqmAsmm1lkBe/CLolwFpV+vqEoBcAmW/jHa2bFm65K6DVvymVBotWLb615tqkKIqsr0b1DJMMLa+d8aahTZ7OE4es+2pcRcVS8YAiYmB6xqVg6oIkdRkKhmV5s5bFpWzsKaRTm7XduD04E5ZZZL4ByGoxwacIZdm1LStsL2kuoEvo5lO4/PClDgGjXan1wEnFejUambp4kWtu5pz3KfaT9NPMWJRjOQhrUrMnAUG6UkLJkTM6VMObFwEpawepnZ4EnIgyozxYHzbvlKAa5btlRVq+0Xtbyn5z5TASe86MhynwlEdqIRaVi9rKZGTZW68lwCVRlKXpHbWb52wpGCvGips3dRD2looFoV58z+3VKrQAdWQqNYax8DLVyBXrnaQKJhTGlMKQ3DyOOQ0jAMPA6JhzSkG2R/7FBVTDnLNOU85Skgme31lE855+mUpVGRQOQfAXSQ7FWjdYPUmNeB0t56oSpJext20SZFoc7UpQHbTwpSiDp0CyQVCoo85gKYompqU119mpr1k4PconVFaa+oIHVoZoemRF7TbFpTnKyaxYCZqyXrFbkkokiTqU3OLCISwGTJSVMW0cxJcg5gekVtWLULcA6DDEcDJz8kjera1q5NqxW226oTuHyykD9TnxVwnx2gwPMtXWCpRgHA9o1mClt3ehZIM5tdKzykVBWpJK55UkkszKFKw9ptVGl8yXaxatdhSgWixeZtt7lQwFMSiNnnRcVtUgVzKFn16tyzQDVQFrVqwHIlGirVAKvl6EA73ci2zzTK1IHZ2MQwtboOVrZPjd7ydbhS34euv9jFgJqEDsB24ygRhhSQHThxopQ4pZSsQiwlHhLHNZ159m7xBUJENItoziJZsuQsmnO2r9XIWaYsOuVTPk05TzlLAz6gUZDWTNbe26plHFz1aQ/J6FdyCbgGSiLPYdob+N5LqDbKE0S+ncTmwEGq1idQqNuuYdNqpypRqnBXgem2rBBRFjuezwDoOU8lzlAREBtENQ5BKErV7FlRifzlNjS93xSphNosVq2rTWGRqjZTZs5SFWcWZhYRyZzSk8E5DIOGXZtS0rdv3woAvIbqBD4/PIEvBFDgSoj+8gv++venqdGwdUt+NL+jOIQh9o/eidAcpJIzW9XuZNW7OXNmToMIZ+aURFiYUxLmTpUKs5R7A6qp0sbiFUnEpiBbmIJ8XEBTicnHMzOrzWOVqkqViLnkTJVbsKKcrwsGiuXLto3ULV1V/3Jv8typq1TVOHO3OSLQodoo1WL/NkcKVmVaQQvYJxTNwQqgFi6ps30GV/bXOTAXY9TbeQ2y/bPkc5iIEjMPaeCUDKoppTQwU0rJ2jgRMRETETMTExEx38jroaoQVVURFXtRB6PknLXCMIuDUieHpO1gRAs7oPnA6+HoBzH0CrIdX18Ja2PU/7XWFPREn6tHcqVZ2jsLNqxWNRi6DdspywpLf0+7jpyl/RvRv7gaAqI4DUgAcftX/fs40Z5bW0HpuUwp39lpapOYHI6cRUS8ejbbgQqUw56FirTQJCVRsWrbzqIlkQaUmTVVYLrazCzCIjmbDZcnf00pyTRNkoYhT9MknJLMwfnIrLGfMw5DSKnNc67ZtRdU51/+ovjll+7x3HhmvwjYvhhAyxu+ghoF/owtkEZ+dA5Suc/UFxtNnMfRQJoHliGzyMDzPGlKwq0q1aRM/mqwpMSsHOq0v69wDGWqTFxyphp5UHXFqU3O1NQpx/5Qy52yWu7TKnoboPpWGbN8SRmdMiVWUqLll3yzf9owgSmgahwshUh1b2nZU7oK1jXFWqxgZvs3fNNuf+cOOt9qs8yr9oBtn5MlOGNMYxdbs73fWeDO5/uzykTMxAZWJtg1cYCWmYka8PrhB0RF+5aTfij+wEKNt+0xrvZal/1c/n+payu/sU/4aES9icHaDLZXVRGDoYgYCLW+qoh0bZJVbIeH/YHEQUO0+NBaANF/R3ViQLGfvwZVLWNWAek5zb4/lGNRkyLA3HrtFWW1XztQ+taTohb9nkASf7wGS3Fb1iHafqWYVcQqoEVpwpVmWLJCpjADmNWWjW9UoUykIloPQyhVt619awVCUnKaohJKMyAb0BQhKeqyu9dMmUSTvbZqM2zaNr/JPAlPSaYU4DzJaRi64qBQnC04z+c57xX4b3wPqrONLw5Q4EqIzs7SBdZtXQA4C1Kv2G1BGsVGeicU+0hzzjzmgXOXJzV7VxpVqlHFa8nRJJSTKHOrRNXv5zAtNq6qWbO29YWVolCojkE3tm51YQOjFSHNgKpFhbZAVWZyq7bYviCUSl9XmwWqRDCwU7F/C2Tr3tJOrW6CtTngoYPoTLkWRWtPxwKwsJ9i1dbdBC2hWWf2bC2Vql1XhWtddX4ZMofu+vrboWfGXZvdPXdi3hJu2z/J6nRrl66tASFgf81xu6Es+2vPPcbPd0ZdFkUaKnEFkID9W4K6NUxJLuFZK2zXQLmmKmvO0kBb85auLBtY2ny3YAla7FiCiKoSqAOmqgqtA1OINIvBsIVl2d9Z85qaqftKMoqioTJmDZrW3ChPEom9m8wiklKmnCXUJjNLtWktv5mmJKc0SUpJYh8nPbJGcVAHTq+sPQdOALhs13a5zs1n90vDE/hKAAXOQhRYUaPAC0HaKFKRN3TaHTlPE9/JHeXdZPZuHll2QmHvSs48yMATT0kk8eBqNHNOSRKr+btJWDhpMmtXTW3OYVrvWzXpFbxMzKIsRIlVuVenYGViDZu3PYhhAVRva61e9aIiKDMxqbYq1UHaQpXUYYtmz6iBVAFmKCk4IBd51QasBtJShXsWriiWblWuQANYAJFvBTYhCxQQxtgN2Pbj60Dqu3X+bF66R7dmxBoQdbH2LC5R9MJ5swG3c1O2IbupLH2idk0Bw37NBUQ7VboJ0B6OZWzpV1eSBtcmN2lrXIRk34YAZZOvJIiKbXz2e41vQDFYEkRdWbawhF+Tb0cRFTVIkmijNMOSjbznDJgVfiJCzJlsIWlVpljhRRYmIVGJStpWtbaQbKHJylkcmpmysLBI0kyZJXOW5KpzyizMWQYZ8sSmMsOm5SPrKRk403GQR37U5IcgMH/SaxUncB6cAHCt6rSn4+uA7KsBtPwAV6lRbIK05kcNpD/79bkcaRQbiQiddjtWEdpNE8vdHbV5UlWlqko9Z5qrxSuSuLV4kzCLVR0lZeEWpkWJsrJV51oBkjYVtzGeW1Wqym1hUbPthRdAVWJuQEqwuarq6xAT/Cxdh6pvnaEVqIZqJVVlthym51IbtdopVC730GIFXwFXs1tXAIttpRoWrXb9PmM2Jq5nz9UKdK15Q62u3/fg5I321Wi7L4F1Mbf5H/fC/8MtMGWjfXuxzp5tl1iBYTO+Va20BkYfG7nPOmapLIElIH3OBUh6UVHTRtrbr2WO35tqFKuoE7eECzhbWNp1KEpXkoC6uvS9nWI5TQ24UlM81ACTSOq2lFowZOuYyhTSAsGozKVi8bJQqziFpIUmWXIzs/1HWos2oJlSzW1ySkVtEpG2+U1+fNTjMAgx63g8CjNrFAedy3Hu93v9H1RwAht5TmAOzrXn0hq/MsC+OkCBJ6jRlS0vwNNB2lbtxj7SfH9PkmuetNi7K6o0cqUiA0vKPMg2TEOZijKngKgoKwuLcGK2wp8KWC8iYmW3d9lyp/VetSjVVaB6AZLlQTlOOKpVu2C2faMOUoKyObKuVJX94AVQU6REVpJT1ara4b0dWC2XWb7b1CtzG9Va8qprytWfhhawAFoFW5Rl6VsHraO1AWkZ0z1XAc/Iw9buVfiuPJPx8zXh03TWvipIa2HVM0Ibe7XGQnAGkC4r0RXFGRPr+5T8I+YwLCM6iJK6LbsFRmtpDjZoQNj0FUDGzz5Xkg0ki5osCtLAqGVMB8qZqhRbI4p8ADUbtipL2yHSgJNIIFEk5GfYEkTF85+N0lwDphgoM4VqNanY3RNTFtEGkCR2L9kAafDMQsIzpbkKTWZhz2mW3OaG2qRH1pLfTEnjvNqUki6rap8GTmBza8rKM9k+ZV8fXt8EQCNeAtLW1gUqSIF1azdA2h7I0Nq7S1U6se73lHNmyQMPQ2YVoRamQ8osl2DKYsAs1i+zsHJn9YqwelVuUZkOWqUAcAUqlDksX1Wrc4GrTi9OKnCFEhEr2/YVP3t3DlX2faHNNYMMqq5WuSjUOVj9vuRX3Y4tcAX1yvUiYO3vfgbZdZjG2vGZWz3iDeB2z9USvECF5ypMF8/rHJoAwLze/jkjcoFr7SvDW1+2gWZ7vwrCZu4SiN7pNi7VcTMwdtC0wQWOPmwbkE2b/+VqC0kbG3lKB+MclAQRNcsW5LB05QkhDRsW4t+Y0sLSz6Y1WFoFbw9JH08kKmHnmiWLqiQdmGbTUhnb2rkiyiytNctCkkmqFUtksDwDTctr9tAkZp2mJOy5TToc9DQMK2qz2rTtAQhb4JxbtYCBE1ixa78zcEZ8UwCNeEp+FAC2Co2AcyC1faRxIEPkSeWdgfR0OnHO9xTVu5ErFRE6Z/GKQ3VNmWoSTsIsIqxGTlYWJrd8VZkNjsyq0tu+oUbVVSY7/BqF2lu+NkZVmXuQWh60VaxGb2qg6lW4bCrTc6pmy7ZqFdSCFaFYudi1Np8jj1lgG5AsSrWvxN0CLNBCFgAWoO2Kiao13OVEO+D6mrPCoU4WtkDtcpjFQl5/PpuY28Sr8QpFRJ29uh4rKrNZsM2h1u0eKyBsVGkDRLudb02h+baTHoyxRgEhcBGQZS1Tkm6tagtJkaouQVASaCjKDpT2115VJUFLzpJIoSKmXkkDliIGyg6Wdq0BTvE2y2W6nRuWbKcwbQwRSdizRCq9HctCIgZZt2S1KE8RyiTMBk1yWK4pTWbWcxYtM2ub2+SHpCk96DiO8tHVZuQ34wCE2Mf5VHD+LR6ubzzPeS6+SYACFyEKtB9Uv/wC/L2v2AWW1i7+/GdMD/32FwB4iiq9E6G2gncne4p9pTvdUcBUZOBhWMJUkvCgA6kISwC1U6cBUbsuhUjSAHEFqNS1hVplg6f3sYKuhCqF/ctq+c2qUO1aVa2gqIFpKNaAIyvbgYXlnqjCtYEpArBesUtKPIdpsYMDhLW95jEDlKXIqYOlOihpowCpt2J7+KK8b7mhy/nO9WdYl9B9lVizc/0dl+3zvGhzT/4htoBgO3elEEh9bFGL1ljsXIOiX3drVDj2R+c17QY8h6+W04FaaAYkhSpUSUiL9UrQoigbaBqoZ6rSr4Wg5DYsGdUuwlIIWiBJJKTicNQCRw0LdgbMsoaDMgqAwqIlV5ydyswsxCwTTcor0Jwm1rBn05TkSEeNfZtHPmhbSfvIrE9RmwDQbUe5v1f8t21HAdat2r8BwJX7OesT/G2C6psFaMT1ti5WC42A8/tIAWBu766p0pIrdVU6t3hVhNp8aQtT1ZHC5lUVigIkA6iwSmIdlNbUqShzEuW53auinJKSQ9baTEk6PHu16rYvGX9bqCprANGvw/5FASuxqUhlqJLZyX5iUYDVNpq4YvVCJYdnUa0FpkvlqrCy5DlgewULdO0UkLUnpYWmamyrLHOwBC7APqdTuPXpminVCj92uG4+i3PAtu2fM9aU57LQx36SMoVUXIXSHJQ2cmHxEsQh6/cLIDaFQUCjJGMNrWq15CGlKtBOQa4C0mA6V5IOyVZNhvUq8PVMUWqAEmQ2rucr3YJ1CPqYsGFJRCss47qBJYmQkEaVbA9Jy1kSFTgKkUjOpFTgWG3ZzJbLXFWZEymxbTnhphCIyGCZcxKik86hGXlNYta5RRtqs81tLtVmb9MCcXLQ9j5O4HkFQsC3C86Ibx6gEU9SpGeKjYDL9i4A5D/+kaZTr0rzu3ckuVbwtoVHdyK0BlNVpXnOdBzVcqmSeByU5II6BUBrQLXj2ZVVquWrVsnLCYlUZQZSV4UtVKGsRkRWVoY6EC2PSk1OlVq1Cnt26r0qFcUKkPEwYFpgTOx5US3Vv27RLgCrhEbBYhWywAy0QFGzJXdJqlYGVVVtmYcyrgFrvwVmpZiogeMyt1kVa2kpcJ7HaynRdeUZkIuQlbFdgRG1gCuNvS27UJ89LEMt2tkJAT8bF6pRur5q4a7CUUhBZXtJB8i2jex0Hy2QDAUJV66uOhtFqa2KhM2r90QaOUuQn+VjeUttlKUVIvWwVFqoS5aMrOzALJYs2zWQdQ2YAPScymRmOU2kzNn3ZZLOc5pEpGvQfIxrLwgqlbQpaezdLGpzHDX9a6k2gfM2LfC8HCfw7YMz4rsBaMRTQRqHMQDn86StvQtcUqWZ5N07qhavUJ8vNZiq9DZvwHQcTaWqjjSIsAxCIsI6DNRbvUold6pKegao6mqU22vbMhO2L1WVaqAsIHWb1q4NlIYjpVaJtmoVCurBigCZ5z0raFfgSmBU5aqgBWBXgDpvM1ixeYcNaAHYj6dqsPWGFqqA+X/s/b3C7cfYrQHYlppbvT6gu4/bZmxD1muKimL4hZ2fAGY5z7NbVc4UCYV121XZlvEdXOP9qkqcjfF+gyIpVRu5ASNc+Z6HY7TNAWlefChJKKixex2S6MHosJRQtwWU9lT2CrMoT5BWG9Zg2tq75GAtFqxB0tbjsGZFKLsy9estYBKLZCKNXOZE1FmzNE3KzMIT68RclObpxMpNTrPYs8zaQdOVJjMXi5YdmlepzRWbFtjax/ljgjPiuwNoxNNAirMHMgBLe/dn9NtggPVcaWvxzmFabF65I70TigIk3QvlbIo0rN65OlUdKXKnqgPNgapqlq/B0i3f5hpJSVyZpgQqKjWB1qCammtlJVZmtX+8M0r+1KxYDvAVsFpuFZaQotYKLlDkHq6wZ8/GgIlbMKp2QFVXo2Cf0wIVWvKXHWgBoEIVq8C1jpIPjRwpOzR7+Ea0+VMvPPLxzbedds/eNiw/swLdsG1lCcfSt8h1wiAIAEJNjjPGlzErQIx+CejOwWjvF+0ocITajpM5MOtcEcupRm4SgHaQFKlzWzDav6205io93wlycIbChKlWJbE+s2YDkLm57mCZoaEus18ziyCTCnv+dHad7XoBTKJJI5dJdNK5ygxrlpjVqmdZoxCIHlmZl/ZsD81q0Z7LbdZtKEubFrjqAITm+VqP7w2cEd8tQCNeA6TAhirdzJVmAv6Ep8K0LUASsdqCudU7jnFdc6fq4Ay7dw5UqPp9Is+hUlI7ghBJSZQ5KSgKk5IqCStDzeZdgyqgpLZ5tSjO3gJWSmBSs4EpFGuAFspuuRaYzuFaIDqzhQ2QDlAUBWuw5BayYLddW9A62DrYAqZqvfioKRgqdjGADrzox8DXsLW49vEMgj0sF89mnzc9X3Z7vQKtIzYKiZZ5zbiVtpJWOpuXZpBsQWjvOgclUAt8KE4fqlCEIFRjBSMAVDiKzyUHZswXkQrYmapEgeIcknFvNnABYyhKWO4xQzTgGBZsKFLJosSuQjdgCcpquUsbQ8KSCcpk4GS2oh/y64mycmYBmf3aArO1ZYlI21wmEWmrMltrNqDZFgI9BZrAP9BW0gLL3CZwwaYFfjfgjPjuARrxZJA2lbvAtioFlrlSYAbTP/0Ji3zpBZiKCN3JHcmddFav7q3PALqjavcqtUANhTqGKi0QVapFScKqiTqVqkqaTElWqCZKKqyhVKHEooxUVCl5fpU2wFoUK1yNssa1q1bU+wpXENi3qJwBLOZARdiv3I1BUY7N+8XfewNcatdpoQsUIMczE/C1JarqnEPWLut1O6+NAum1KDzdUqZU85UbEXCbz+td3RWgSlWjVblWCNraAbwehvFD6wyI5b0IpjzFf7YGjGiUY/nNrwBzFZAQhZBZtg0ky/u19646C8BdUW6AMuxYJQr1SA5cEcrQTCxEWQOWRKKUvVK3UZdEWTNzKfqpsDRInog0FGYLTCIqtixFARCz0oG1t2ZZH/nRAPoUaI6j4h/r0ATWc5vAhtpcVtSWZ2YrvndwRvwwAG3jS6hSoBYeAcBcmbY5UwBYBaoXIYU6lWavqYrQHKg73ZHMgNpavlHha31LldpCNXKpyVVrq1TN/vUiJc+ptmq15E8NrFWxKogbQHaqtd4TaxQQqduyHWANdIpNyMLep7ShQhQLqKIAsXxTSgvIAl2HKxegNkAs403ZNmuWL26JZ6ZTmJ0aLXRcr86dRfMz2qS1M27nUeBWb/wdmzxnt05zRm2Bs84PjW9VpsAgWQqUAnB1DRXziBdQ9L5ubIBQAmabcITClWILSEjttx0rsY5vXwk16YA1MLoKrYpSSx6zUZWRswSyMrMgk64oS0XkLhtYrqlLItKolG0t2RaYfGI9rgAzVGY6DsJs+Uxi1rYIaA5MAJjnNFulCQB9QdBNbT41fkiARjwZpBtFR8AzYerKNGAKANerUwNqa/duKVRVpTaHKuI2sCtRU6lm/VaIDjSHaqjTFNcsrDDA1UIlpW2wlvwjiXjuNVTrOlyJG1i2AC0KtuRa0Vi4Bt52LQaAHqoosPU8aduHFrqeowV69Tm/DwDHMzGDXAPOHn6lCrcDZwfgZ0dj3uq8cV6FOztovkJ11h4AnPX1955jbNoKUA1+vsUloNj1ubJtwBYgLBYrQSM3GQqyBWhpX7QtIKk5wwAIU4og2gSl5SKtwIdIlAAlMcWYbawr0x6WRFNVmmHFEik1diwza5vDDEt2TWG2tmwA81qVCQAFmjOlCbwUmouioO5ZWosfEZwRPzRA23iJKgWeDlOg2rxALUACgLnVi/eA5JcBNSDaFiUtVaop03FQB2yF6kKpqsExcqqYqdUC1mIDG1iTKmmCvzcz1FVkqnAN5Qr0gC2FTM12kWsga0Ormi25ygpbtKoWqBW1QFTqMjrw+vxixTKvKNL63MwVI8dYLv9Zff7m854aZ5SpKUmHqcw+5OZg9MaqOMWKgQJ0pb2MEy8usjlSxlS1aEo1QAcE0Ipq9LWvgWOsXQt4KiABLyoq1rAossM48pJEatYraQFla78COleVkbMEZc0Ow1ZZtrAkIj35PREt1OW86CeA+mxgpqT4AMytWQBYKwQCWnv2emgCN7V5Ln43AI14qSoFLudLt2AKLNVpwBTo1WncPwWoIkJ73VMUJRlEe5WqOtKoSjqDaijV1v5VNbB2atW307haLWAt96okaioW3tbBVUFFucLGz9WrqV+lNAMsAGAFqIAV8zAaVQqgwhYowI32GFOgC7TgtZc6pi0c6voKZG1+uQzFO48LwNQN0EbQhQ8urAA1FGHTYtCzzgq+OiHg2/c1ILS3Eu3G+FoBRACYA9D2dPo2Fl+zBWYtVGoUpI/vAOkq0lQtaShJohkks0EvU1b2bSs2ztrgoDTI+faRRlVOZu92NuypgWfAkk7eTiedq8sCzCPrgQ4VjE8AJgDMVeZWPhMwlQlchiawzGsCN7V5bfzuANrG57B4gTlMgbU9poDBFFhXp9be273/4W1zoAJAADWKki6pVOmgWpWqNPeqQrXyt4J2aEAbYDXYOkjX4KpKoVyTuiV7FrBKwIAlZG0sEIVErZIFlrA1qAJtwRBK9WzAuPTbRTcm9oQmNIpxDabcPjOxxWX+DC2U6otiWU/UA7QWBJWRdY/nApqmTHNdvUKwHdMBdTmm9rvNO4OiXS+UIwJ6CzhiQgXfOiCtvSpJz2suIUmkE/ncBpTW3oMxKmIjZ9nasK2yJCKN3OVZdelFPwFMAJgDM6Wk/wfLXW4BE1iqTKCHZuzVBHAWmsDNon1J/K4BGnEFSIEnKFNgafMCT1CnXogE9HYvUIuR7LpXqHhnVnAtSjKo4o19Up2DquqORhHSXQ9VVaVhkApB7BBg1QakFaLXw1VVKanD0KHZWsNQJQwGrQCnqtIAIGxim5OKtcoKwmDXTcVrU2TUFhf1x/e14E0INdhsWdkoEJptZSE76G85rja9zLbditUtLKEKUf5bxhToNeO6dVyJEmBQ7UAIzGAIOBCtrS0u8qrcya3eYhVnB5jZq1OZlwvsYI3aWa0BTgDZYYinQHIGymq/sgJHlDUm9v4GlkfSE8+U5QYs8Qlgr5Bti37wG3BOYQIG0Dkw1wqAgG2VCVxrzz4dmsDvG5wRN4DO4nPCFNi2en9GVax97nQdqNG3lkMFgFalAvbJ1kJV76SMK1BVKfZvzNlUq6qEnc9vFOvcCgYcgBvKNfpXAAsAZFts6rhQsXa/BdoBpkQdtg7VAG5RtraIgbUBb6zd/l23uUpVpZTmz0JayWcuQXnV15pt2bxXVOKufwvLXJWSAkVnGohyn09dFg81VbdTzXlWGDZK0ccQtVC0/i0w2hpVPQJA9mKd8jOsABIAtpVkwHxuvfaKEkfgNAflTFXGnwkf7TsYWlgCQFixLSwBoFWXALCVwwSWCjOACfS5zDjYALjOmgVu0PxccQPomXgeTHFVARLwdHUKXAYqAMSZvdEWKtXut6E6V6oAsKZWgT12qh1YASAUa2sF1/YKV2tz5apKGAOiPXhbyKZQm1rt2zloBzQQbWFYFGhCCoA10I31ArxxX+ALoB1b5mOwS8zba6TZGl28sICoxBysUzSjsWOXY6tinfr2ABzmEARaEHZjC/SgQDZQtkVHE6qyA7AGRkQ7kdu0FY7W16tInGLNqcyfQzJ+xmq9Um3vQEkKHLCmKmPsOWUJVFgCwEJd+hmzAHAtMIGnqUzgyYVAwA2aL4obQK+Ml8AUsO8sBXqYAk8HKnA+h5qnif4TwDmVCgAVqu8sr9pAVUToDQCROwPhTK3eAZAGrICDy63gPYBNuC6Ua7WFdVDaoYEggDXIlvdbAW2Abw5bwG3i5u/Jxg4ziAJz+NqUqjDTXEU289aqavu2lqKvZeW2AJ1q61pBUQPHOXhzkwcNhTqHYLuuwXBq36cAsZA2uR0AAAh9SURBVB9nUGxBvAZGAFiDY6xFRHr0uXO7tVeS65A8AGitV8BARwdW5oM+ooISCFVpyvJTjO1gmRT4rcAS8Nzlhrr8J4B0RQ4TeB4wgVk+E7hB8zPHDaDPiGfDdHb6EfB8oP6M3vIFtlXqJahG29z+jZwqsA7WVrHGmLCCcVchGX1zuNb2gKi1hYIFWpgaZAFgrmTtdahrOkyHlTGwi25c9A+lb+igp9uA7MYMM4m5BtNz7QuFuhXTevPWlpZ5+wx6QJsXXQFlvTalOqHvpylyphWIbf80GzcHI1CVo7X1cAQMXqdTC2bvb+3VGSRLO7Pi0dYN6zXWrIpyA5SznCVQbdjSfwUsz6nLc5YscB0wgbOnAnV/x1txg+bT4wbQF8aVMAWuUKfAtt0LPB2owAyqvnUGWIeqvfZFSoCpVeAasAoBbzBXrTFuJ0LAPXTfKNoGpMUaFiHsG/B1MB3tejS12oLWXr2/ge2u65/BtG0bKtiGNYBiXLFxEV1XqM8ZIz+DhTtn66YKPa3Pt/GnxdypHTP1CtOueyC2atHuA2inbhwz27gCx1O1cxsI4uBjG6s1+opiPLACDzi2AJypSeATmPlqUALVcp0X+QArsGy2lAAVlsDzgAlcsGWBZ6tM4AbNl8YNoK8cz1anwEWgApcUKvBkqF5Qqva6VKtAD9awgttK4OibwzWUa507A2yjYIE1kFYlu97fwxQwVdu+Xz/OFK5dN/0O1BbAwBKKdj9i2bYSG6B9jViAcd7XxWnRNlecx7ie2nb262OfB/UI+IRatP4emh0YAbTKcbW/UZCbgGyU5ByS0V8hWa1XYAWUjaps269VlsB5WEZ7jH2SwgS2gAncVOYXjxtAP2O8SJ2uVPYC1wMVqCoVfwamh+dBFTgH1kzAezwVrkAP0GsA287Zyb6qzLsNKDawBYB9rLWfwXBlbmsnRwSAI9r37Odute3m3Si/hxdEBdo8js2YDRXaRMAkogVgHX9czKVmXqsUD6VtHYrlPR/r76Et2Jn/XNcAEmiV5HWQBD6Uti1QAs+A5f29Git7dQlcD0zgbMUscFOZXz1uAP2C8RpABa5XqMDToQosc6rAdWCt41y1Agg7uO3r4QqsARbYgiywBtp2vbYtYAtU4PbvvQ1UYA9VpX3z5xcQXpu7vsYyPqsCPdfPS9h2cw49SA+l/1DHz4HpUeY99lAPKLZz2/eYgzH61+AILAHZrjeHJGC2q7X9tui7BpTAepHPc2AJXKswb8D8XuIG0K8UT4Ap8ATLF3guVIHW/gXWK3+BdbDiTyj5VeA8XO26t4Xb/v9orheQtcuFkm3HrsLWO+ZwW1O4EWEpl/5Qi3fr8+dxLSgvAXcr1oC4Ou4MWDvl+Rjj23XNMt1637lyDSDWvh6K8zlz5QiswzFO6Kn9vtbMbp33r0JyHBX/ALZACaxXxALbNizwNFgCr2PJloG3D/KvEjeAfiPxKkC9UqUCT4Nqq1aBHqzA8+EKXAAsgC3IAlXJRlyCbcQWRN8s1nkzu6+DzkHvHFS3foaXxjUwncNudf6n2lbHf+ru10C49jNcgmLExwZia3C09m1AAsBLIQnMQNmoSuDpsASerC6BGzC/u7gB9BuN1wEqVrfOAOtQBZ4CVmCuWH9e9G/DFdgGLLAN2Tovrt/VvjOwBZbABeawe9fPeedjVuYBT4fom7WBV84/B795fFppuwqe8zkBnt9qWwvCrXVbINY5zZof2r51MALbcAS2AQmch2RrvQK9ooz+dvw1oAQ21OX2lpKIGzC/87gB9DuJJwIVuAKqwGX7F1hCFViCFTivWn/GebgCW4AFzkEWAP6zaTsHW7t/1//c77cBOZ8L9PbyWlxWn+9WW8+tuRZzaNT4bb3Z4xxIW5v0mveaw9DG/rYJT6BCETAwAufhuLZGC0jgCkheUJPAEpTAZVUJNMryFWEJ3ID5vcQNoN9xvCpUN+zfiGsVK3AJrsAaYIGlNQwsIQssQQs0ahYoL3PgAr2yjZhDt19zPRYgjnjf327B+UsFz+H3YX3cHHx93zpAhxn41pQi4EAEgodYU43AEozAEo5Ab7UCS0ACL4MksAQlcLUNC9xg+buJG0B/oHgGUIGzUAVeE6zAOlyBFcD6yxyyPy/G1rgWtsBM3UacAW83twHuHMbz2ILzl4o55Obxz+Z6DX7dWgsQ1pstyF4LRWCpHIEWjsAlQALrkAQ+GyiBJ8ISuAHzR4obQH/weCZUgReCFdiGK7ANWOBayALnQAuch23EGnTb2ALwPJZq9U/rA5845Kr4x+Uh80HbFnAfawBsYwuGEWtQBLbBGHPW1tqCI7ANSGAdksDnByVwg+WPHjeA/k7j84EVaOEKPA+wwHnIAtugBbbA+efuZQ26ET+fXed8XILy14pLsFuLgNn/nBkz3N/buhtAbNdZi3NgBM7DEbgCkMC1kARuoLzFE+IG0FuUeAFUyxKXh/xin2MbRUzzuARZ4DJoI84Bt43z0Pzz5u05IH9LUYAHrLBuCb+IcxBs4xIQIy6BEdiGY0RfxAN8TkiWybcPzVt43AB6i4vxCmAFroJrxC8dZIHXAW0b10K3jWsB/L3HtQBs4xoYtnE1GIGnwjHixR9sN1De4lLcAHqLF8UrwRV4EmAjflmAFrgM24inQncrngPjbyGeCr2tuATDiL/NG54HxohX+dlvkLzFS+IG0Ft8tnhFuHbLPn/qL/by17+vrnEteM/Fa0H5c8W1sDsXCxCWjr/42r+8ZPlX/0C6QfIWnytuAL3FV4vPBNjuLV5/yV+Wl39fB/JavAakz8Um3NbiLw68X9rGX1YGvjg+64fMDZC3+FpxA+gtvun4ApDdfOuv9L7fanyVD4obHG/xLccNoLf4IeIrgvYWz4gbGG/xI8T/B8OxhjvS4DojAAAAAElFTkSuQmCCUEsBAhQACgAAAAgAV15sRopekDpDAgAAmwUAABwAAAAAAAAAAAAAAAAAAAAAAG5vcm1hdGl2ZS10eXBlcy1kYXRhYmFzZS55bWxQSwECFAAKAAAAAABXXmxGAAAAAAAAAAAAAAAABwAAAAAAAAAAABAAAAB9AgAAaW1hZ2VzL1BLAQIUAAoAAAAAAFdebEaIQQwqjcoAAI3KAAAYAAAAAAAAAAAAAAAAAKICAABpbWFnZXMvcmVsYXRpb25hbF9kYi5wbmdQSwUGAAAAAAMAAwDFAAAAZc0AAAAA"} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/command b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/commandTrial b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/contentMD5.txt
new file mode 100644
index 0000000000..29d1bec2a1
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/contentMD5.txt
@@ -0,0 +1 @@
+OGNiY2Y2ODIxZDI4MjNhYWM5NzM1OWFiMjFiNmY2Zjg= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/headers b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/results b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/toExec b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/09_NormativeTypeCI-database/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/body.txt b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/body.txt
new file mode 100644
index 0000000000..8cae64ff16
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/body.txt
@@ -0,0 +1 @@
+{"payloadName":"normative-types-network.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"network.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/network.png"}],"payloadData":"UEsDBAoAAAAIAGxebEaVjiOukgEAAM8DAAAbAAAAbm9ybWF0aXZlLXR5cGVzLW5ldHdvcmsueW1spVLBatwwEL37K4acY2dDQg8+BMJeekpKd2+hCFWadYbakjqa9bJ/X1kW9qYsFFILjHh68+bpacRHo5XFAzkS8i6qETmmTQuSjyINoUd11kOv7tUmrZPdPFSVYMK1oHJ6wEKunedBC41YyzlgrB3KyfOvlayP8u65hf3rbvsM++16srS9bzZpTU3q3cvzt93X131VWYyGKUhmbL0TTS6CvCMsLSG3hPUqTZWse5bYVgA13Fy3yN5Le6XnTVU5b1Fl1qSQy5sJi83LfK8JhtSRk5xVB/ZD+4H2PWkXyoX9pwwB7JP7OYciB1MVMAbGiE4iaJjTv4Xed2R0DyVQiMgjGWyyVGAfkIVmn9NHYcmzIJDjaYGcYIe8oIy/j8RoWzjoPuKCpxj1sZcWHhfIpOkQTsFLXFWnZEfdk1Xpf0wO4A0eb+EL/CgUQ5b/NpFkyHX/8NClqTjps6LwqfKS1Dye/yNA9hPlRgf9k3q6fJSUn0MjVx5lnpnLmmZbyCPJObNFd+vzmmmO7mjQHca7YrQJydMfUEsDBAoAAAAAAGxebEYAAAAAAAAAAAAAAAAHAAAAaW1hZ2VzL1BLAwQKAAAAAABsXmxGBKV7cdtvAgDbbwIAEgAAAGltYWdlcy9uZXR3b3JrLnBuZ4lQTkcNChoKAAAADUlIRFIAAAH5AAACAAgGAAAAgcMVqAAAAARzQklUCAgICHwIZIgAAAAJcEhZcwAAocwAAKHMAdJmv6QAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAE3RFWHRBdXRob3IAd2Fyc3phd2lhbmthDL5LlwAAASN0RVh0RGVzY3JpcHRpb24AIkludGVybmV0IiBpY29uIGZyb20gPGEgaHJlZj0iaHR0cDovL3RhbmdvLmZyZWVkZXNrdG9wLm9yZy9UYW5nb19EZXNrdG9wX1Byb2plY3QiPiBUYW5nbyBQcm9qZWN0IDwvYT4gXG48YnI+PGJyPlxuU2luY2UgdmVyc2lvbiAwLjguOTAgVGFuZ28gUHJvamVjdCBpY29ucyBhcmUgUHVibGljIERvbWFpbjogPGEgaHJlZj0iaHR0cDovL3RhbmdvLmZyZWVkZXNrdG9wLm9yZy9GcmVxdWVudGx5X0Fza2VkX1F1ZXN0aW9ucyNUZXJtc19vZl9Vc2UuM0YiPiBUYW5nbyBQcm9qZWN0IEZBUSA8L2E+FK1iKAAAACF0RVh0Q3JlYXRpb24gVGltZQAyMDEwLTAzLTI5VDA5OjAxOjI4NFNBPQAAAFZ0RVh0U291cmNlAGh0dHA6Ly9vcGVuY2xpcGFydC5vcmcvZGV0YWlsLzM1Mzg5L3RhbmdvLWFwcGxpY2F0aW9ucy1pbnRlcm5ldC1ieS13YXJzemF3aWFua2GjXLFqAAAASXRFWHRDb3B5cmlnaHQAUHVibGljIERvbWFpbiBodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9wdWJsaWNkb21haW4vWcP+ygAAIABJREFUeJzsvXeYJkle3/nJfF29VfWWd91dXe39eL81u7PDzg4wLCCzHHtCnE5CK+Ak+g6JO9BJJ4GEpJMOgXQqHRwLAq0WHgQSIIMWN8vusjPvTO9Mz7T33lV3effWazNDf2RGZkRkvmV6qrt6et7f81Sli4yMjMw3PvH9hUlLCEHDGtawB9+Ghw+2Ad1Aj7FU17NAWvnLGNvq/gzgAhX/r6ysV+rsLwCT/t9E3DKfHyncs0xoWMMatiqzGpBvWMPW34aHD3YA2/y/7cr6INCLB/DUuiVwdVbCg/44cB24DFzyl5eBy/n8yML6Ja9hDfvoWAPyDWvYfbLh4YNbgAPAVnSQbwM61i9l62IThNBXKwDH8/mR2+uZsIY17GGyBuQb1rA1tuHhgzngUeAx5e9RoG090/UhsnHgOHBM+TuVz48U1zVVDWvYh9AakG9Ywz6ADQ8fHAKeQwf6VsBax2Q9jOYAF9DBfyifH7mzrqlqWMMecGtAvmENW6ENDx9M4EH8Rf/v43ht5g1bP7sAvKn8nc7nRxqFWsMa5lsD8g1rWB3z3e4vEEL9BaB1XRPVsOVsCniLEPrvNNz8DfsoWwPyDWuYb8PDB7PAy8C3Ay/htaMn1jNNDfvAVgUOA18H/gB4M58fqa5vkhrWsPtnDcg37CNtw8MHdwGv+X8vA03rmqCG3WubB14Hfh/4/Xx+5MY6p6dhDbun1oB8wz5SNjx8sBkP5hLsO9Y1QQ1bbzuBD3zgjYbKb9jDZg3IN+yht+Hhg5uBz+JB/SUaar1h8TYPfAUP+L+bz4+Mr3N6GtawD2wNyDfsobTh4YMbge8BPgd8jId8SJtt26RSCZLJBMlUkmQqSSLpLZPJJMlkgoTc568nkwkE4FRr1GqOtqzWaji1Kk7VoVp1cGo1av7xWq2G6z705YYD/Anwm3jAn1rn9DSsYXdlDcg37KGx4eGD/XiK/XN4w9vs9U3RB7N0OklbZxud3Z20tLXQ3JqlubWZbHOWbHMT2ZYsmWwTTc1NpDNpUukUyXSKuJ+0+jsXyr9wPVjEnO+FE0JQq1SplitUSmVKi2VKpRLFxRKlQpGi8rcwO8/M+BSLiyVqNWdN82UdrIrXjv9bwH/K50dm1jk9DWvYiq0B+YZ9qG14+GAPHti/F/gkH6Le8KlUgpbWLN19PXT2ddLV00VHdzvtXe20deTItjaTTKnT1QsNwHGADo7V+V3L/SLcocUltLDamco5MpzQzjErEsIVVEoliguLLMzMMzs9x9z0HLNTM8xOTDMzOUupWMJx3Ni0PqBWAf4QD/j/OZ8fmV/n9DSsYUtaA/IN+9DZ8PBBG/g24PPAdwPJ9U3R0pZKJenqaWfjlk0MDPbTt7GXrt5OWnItpJuawDIgbfwmVZgHeK0DezOoICYuhcwRVa8fjrmWiNlvVD6MikNdj4HrUi6WKM4XmJ6cZmJ0kvFbdxi/eYf52YUPA/yLwG8Av5TPj7y93olpWMPirAH5hn1ozJ9C9gf8v83rnJyIWZZFa66ZTVs2sGFoA/0b++gd6Kajp5NM1uvrV1cda3vliojZp6+sRNmbv3Et1nqwr+vCFzH7wrhiVf0S8caHhdLiInMTM0yOTTF5e5zxW2NMjI5TXCzxgNoJ4JeBLzXa7xv2IFkD8g17oG14+GAKT61/HvhWHqB29nQ6yaYtG9i2ZytD2zczMNhPa0cOyzKSKOrAfBngmwreZKq6slrYx3kOVu7CvwvQB4dFTJxGJUSreITXch2Hwuw8YzfGuH3tJjcv3WDyzsSDpvhLwO/gqfuvrXNaGtawBuQb9mDa8PDB3Xhg/5+BvnVODgC5tma27trCtt1b2bRtI/0b+khnM7Fh4zqvqavRNnQ9bAw76x7T9i8He8OFX9drsKyqr+/eX859L5QT4ioEanJM0Adx+TsqxSITt8a4fW2U0Ss3uXN9lFKpwgNi5/HU/RcbH9Jp2HpZA/INe2BsePigBXwn8LfwJqxZV2tvb2XP47vZvmcrg1s30tnThZ1MQBwkIxtxx2Lc5jE/v1jgxylzM746AZeDvYgJqMI/DuZxLvbVgF69LxPksfEuAXq10uDUasyMT3Lnxh1uXrjKjQvXHgToV4H/AvyLfH7kzfVOTMM+WtaAfMPW3YaHDzYBfwkP7nvWKx2JRIItOwc58MRetu/bRv/GPuxEfGf96K8mCr06R6MArhPp8r3bI0eUY/VhX605VCpVypUq5XKVpqY07W0t9ZsHVPWvLYQB4pWBPry3ewN69XitWmX85h1uXLjG1TOXmBqbXO8x/m8DP4s39v5DP7awYQ++NSDfsHUzf/jb3/D/etcjDe0drex/ci+7H9nFlp2bacmZH5mLg1u8RXR6BHpquCXiXVLdLw1713EpV6qUylUqlRrlciWAebni7ZNt2Kpaz2RS9PV20NfTQcofthdx6xsN//VAr9/CKkFvhFEvuxrQq+kVSjyFmTlGr97g+tkrXL9wlVKxzDrZJeBfAr+Sz48U1isRDXv4rQH5ht1389vb/yZee3v2fl7bti0Gt27k8eceZed+T61bdoJKpUqhWKZUqlCpVNmyuV+bIi9WuS8DfqH/iw0bYGgF6t4VLuVyzQd2hZIEd7lKyQe54zgx8F8qgQbALehsb6W3t5OujlbA0t36BslN0KtRCW1bhXvU62F2yIsLE8aiVCxWAHotL5TznGqNydtj3LhwjcsnzjE1Ps062DTwC8BIPj9yez0S0LCH2xqQb9h9s+Hhgx8H/nfgu7iPveQty2JwywCPPv8Y23ZvIdPawmKxQrFYoliqUCxVEG7YQ1sAe3ZsYqC/KxpZHXUeBV/MUaGHjXdNe1BaWCgyPbPAfKFIpewBvFKrGWGNa8fGt1QI/YDZGz6dTtLb3UFfbzuZTFoPLuLut7773gS9er0w7NqCPk7Na+GUY67rMntnnMunL3Hp+FlmJme5z1YBfh34uXx+5MT9vnjDHl5rQL5h99yGhw++CPw08C3365qWZbFhsI+nh59g/+O7mSnXuDU6GRs27jeQSid57sk9JBLxdZHlGGu2YUeCG9ArlatMzywwPTvP9EyBmuPUTZuMpFpzqTqOt6y51ByHas3BcQWuEAhX4Mp14avXYFvu87wbFmBbFliWt22BbXn7LMumuzvH5k29DPR1kEoksC1IWBa27eW1dudxoFdUv3YbKwR9bIVlBa77pdS8eV5QeXBdpm+Pc/nURS6dOMvc9H2d1E4Avwv8ZAP2DVsLa0C+YffMhocPPgf8Q7zZ6e65WRYMbOzlqY89wYGn9tK3oQ8suHhllBujE3rgOPgav4XBjT3s2LZBOV7/2vWgr2ljZb/juMzMLTA1s8D0zAJFpW245riUKjXvrxwuJdRrNZea4+Kx1QO0sPwv8FjLf4knchuBsvX+SeB5u13tvlOpJEODfWwd6ieXawnobNsWtuX9JWxI2BbJhE0yYZG0LVIJrwIRgBzlmkZzwJqBXqtA1AN9eL9xzQ+u4zA1OsaVUxe4fPIc87P3rfncxZs696fy+ZGz9+uiDXv4rAH5hq25DQ8ffBIP7t95P67XlE3zzMef5NkXn2Tjlo1Ydoi58xdHuXVnov7JS6hwy7J45ondNGfTdc+p9+sRMQHmF4pMz8wzNbvA5OQcc4sVij7EywrQpYqX5rHRg7llSaxjhPH3BYfCC2uhY9Ju3oNww/2hRyL0Bnh1AUFnR44tQ/0MbuwlmbDrghI/fMK2SCQsUrZNQsI/aZNO2tiWnmfLgV6vVK0e9HqFQVX94T0LJR0Abs1l7PpNzr9/isunLnr9H+69OcCvAf8wnx+5dD8u2LCHyxqQb9ia2fDwwUeAfwD8Oe7xp10ty2Lrzs0Mv/Ic+5/YQ1M2+on4c5ducet2vIs+YsLc9HZ0deR4bP+26LH6XvTAKpUak9Nz3Lwzw/Vbk0zPFSkUKxRKFao1VztJPS8AuSV8sFv6QRnGPIkw01UPgmU+CSPt5ogyrYIioe7viLjDhfeZ200butky1E9HRy70DJjnLOGa94BvkU4mAvCnEr6XAuN8E/QiSFF8OOW6cV6B4NxlQB86HQSlhQKXT13g/OET96vDXg34VeCn8/mR6/fjgg17OKwB+YZ9YBsePrgH+Cm8L8Hd0w51ra1Znv/kMzw9/DgDgwOxYQSC85dGuXl7CQUff2JgyWSCnu42ers76OpoYak6iwSC4whuT8xx7dYkl66Pc+vODKVyta7aV01V6CrULcvC8mEfBDTOQ5jt4votWZaKSuMgoTqPPd+vAbgBWBVlT7CK8GGea8kytLmPTZv6SCeTqwK9edwCkgmLdNImk0zQlE6QSvrgvwvQ1zum3ZkB+noegeC+hcvEjdtcOHKay6fOU63UuMdWAb4A/JN8fmT0Xl+sYR9+a0C+YXdtw8MHu/CU+w9zD78EZ1kWuw9sZ/hbnmPPYztJZ7ypZB3HZbFUplSsUCyVKRYrLJbKFItlKlW9sF3JW55MJOjpaqO3p53OjpzXES04NxqD4womZwpcvTnJxWvjXLo+Tq1aW9G15H1Zfhu6ZVnaviASK3TOxwh6XcFLMqrXwNJUfawZverj2r6Fa7Zpi1hXuOvL/r27Btm1czNCCL+CYHbIM8fHr6wiYFmQSdo0pRNkUgmaUraXdwrt49R4kGaMa+i1BCXsEulSKxTKemWxxLUzFzn33gkmzD4ga28F4J8C/zyfH3lgv9rTsPW3BuQbtmobHj6YBP46nnrvvFfXSaYSvPDJp/n4Ky/Qv8mbvn5yep4boxMUCmUqlXC6UgERlRserP+OJ2xfsfe0092RC9rzI2cIqDmCucUK125NcvHaGJeujbFYLEXCxZyqmezNboLd8u9B43fQo85oj/d99eG2tlAs3FMf9kLbpbrvQwb6oPWDepUBZdihUCAq4JWXnyLblPbCuWH40CPAXYNeOy4gnbRoSnlKP5NOkJR9MgzQL93Or+SOpuaj6TLT5IcIz3UFEzducfrQUa6fv3KvZ9i7CvxEPj/ym/fyIg378FoD8g1blQ0PH3wN+Dlg7726Rktrlk9++4s8/8mnaetoQwjB+MQM125OsFAoLnnuSt/mXEuWwQ099PW2Y9thpzE1FiGgVHW4fnua81fGuHJ9nInpuSXqDKLuZqDQCeEuYS/BLnvL+7uUtnkZlxqHfql67e5xbnwT9tr9+PAysiKEmM911z8xrr27p7uD55/Z50NQBOpehgmG9Mk4CI/fLejV81JJi+Z0kmwmQTaVAEtR6GockY545vWU6xJTUYikyehsiGB+fJqzh49z4egZatV76sp/E/jRfH7k3Xt5kYZ9+KwB+YatyIaHD+7Fg/tr9+oafRu6eeUzL/HEc4+QzjYhXJfRsWmu35ygWFr99KMRBW1Z9PV0MLixm1xrc+w51ZpLqepSrLpUqi5vvnOa0+fq93Na+tcjArjb6Krd39DgboLdQlH4SngBitrXu9rJbZmuej0JVNCbRUDsjHQqJAPQixCwfiAh4OkndzPQ500k5AFe4EoY+mCU4A/G7PuJWhXoYyCtutkRXn40pe0A+qmEHdvMUNdtr1x3yR77RmVAzVsBlBcKXDx6mrPvHGdxmYrqBzABfBH4O432+oZJa0C+YUva8PDBTjy3/F/nHrS7Wxbs2L2VT33mE+x5dCd2MonruNy8PcmNWxOUq9Uw8F2+qpl0ik0butk40E0qqX9wRoAH9YpDqeriuLKgFrxx6BRnL9xc1bUCuEq4W4YytywsoYOdemBX1b/cJtwWRCeiUbeDysAS+SZQoBjsDOWyPG6CSwZzFd+2wJsl75WXnsayIZhwxw/sArj4wBeB694VhK78Dwh6U1mrHgSAVMKmOZOkOZMgm05oxzQ1r0JeORbntjcrF6aaV5V/rVrh+qmLnP7mEabvXa/8BeD/xps9r9Fe/xG3BuQ/HGbVWcr16Mgp3YRxzNyOmP/Z1x8G/hEQM7/rBzPLsnjkyT28+t0vM7RjECyLWs3hxugkN25OUHU812bdPu3LgCuZSNDe3sLG/m56unJyHpbAPLB7cHeV3JCF/p++dYLzl2+t+tom3CWoLUW1a4qdEOxaeLke7g5d9yjt+ULCPkiBlkCjT9oyt2Mq0RCAQbOyqZ4VkAtg+9YB9u0eCoEtPOe/gKDzntd+DwI3bK/3j7uIYN/dgd7sJV+/s13ChpamFC1NSZpStnIsBvQGxFfuttfTI/e7jsPY5Wscf+Mw47fGYp/LGtgV4Mfy+ZHfuVcXaNiDbw3IP1imAtuK2VaX5vpyZj5ooSwFnhNWAMJ3zf8S8PFVxL8isyzY+8hOXvvsKwztGEJy6+qNMa5eH8NR5pBfsQnIZjO0t7XQnmumva2FluZMJFi56rJY8f7cOu+9KwRffeMYl66u4lshAgPkVtiZTtmHAnd5DF99Bw9agj0G6irHw+F2wS5fuevKXt1cEvYqoNTdiqr38icMoUHeD/byi4/R3JIBoQy788NIBS/Dy3Z96Q0ItyHolR8HaZT9Bsz15oO4sGHa9XH6HvBbs0kyyQRaBUPLo/jzddCbTSCqR0MP77outy9e5cQb7zC50jkdVm//Cfgb+fxInVprwx5ma0B+fc2q80fMuro011dqImY9WP7u776R+tf/+j/9WLVa+z+FEJlwfvO1eUd27dvGd3z2Fbbt2aZ1FLszPsMprd17+etZls3mjT10tLfQlmsmlYpvSZBgLy4Bdmmu6/L6149w9cbYClLgpwOwbAPuYABfrwTIm9c73slJa/TzhX8RVe2rcUQmuou8Fiu7ExVoqpoN0K/Mgqfmo4SdcKGro5UXntsfvDMSduo75Lns/TZ6V9YspXoP3zfp2nd8WR+vxk2VbBzTlLgO+qU64iVsm9amJK1NSTJS4Wv5tMSwulWoeW3efNdh9MJljn/j3Xvlxp8Ffhz4pXx+pFHof4SsAfn7bxbehDFLgb0e3ONgzxL7oL6CV9fF93//P3l2dHTq/3Nddz+oBbNS8LriroC/fddmXvvsp9l1YEekt3exVOHdI+epOUspeP2ayWSSx/dvob3N/Pa7ZzVXsFh2KJTDNvaV2JETl/jm++eXTIMam2VZ2BZ1lbs6Bj6i8L0IgnjCsIRKXRDsF0b7vJmPgfteS636Uuj5IIwV9ahrHiRU4/6GNhROvhOP7d/Gxg09wVlSoQdT4QYwD+ML2+2Vzni+G8DFV/zCVVz48aBfCrpBKENFxzUFmOenEjZtzSly2RS2P/euCfG4YXVaxSJMgdafwUyvQCBqLjfPX+TkG+/eqy/h/Snw1/L5kXP3IvKGPXjWgPz9sXpgX4mCX07Fr0TR1wO9+A//4estX/ziH/1f5XLlh4QQtqY+hFEI+dCXsNcK/hgb2raRz3zPq+x+bFfYAQ3UBmRG70xxZ2KWQqFIud5sYcodZlIpnnhkG60t+mfohfCGuy2UXcrVlbv8Nee0gFNnr/H2e2ep1erHEap3j8K2Py7bNkEfp/CDbS8mvZOd2ZteeQHUnvWG6x6ikJf3s9TbobLczAe5CAGoQl4FNyAEiUSCT730JLZthceU9ycEuEfrsNc9kc54AllBMHrhB+BXKx9+6kR4P0up63jVr4C6zvmWBc2ZJG3NKbLpsPPmatz20XBEwskwruNw/fQFTr15mLmZuXqP8G6thPdtiZ/J50fu+RR9DVtfa0D+3tlKwb5aV/3duuyFuf7X/trPfvrmzYmfcV13s+mW12Y0UwtLzYXvf8rUUPjdvZ382e97jUee2ud9qjVmrHbELKhWHQqFEoVF72+hUGKhWKJW8z4E0pLN8MSBHTQ1pYLTqo5gseKwWF7eHb/SV312bpE/efMoYxOKkvLP1dV7CHA7gK33T1P4qMD3btb244q44lU3vNpZT80qJT/1DnfRrNZnvDNuRtkSdTweoWIPpXSk8gcMbeph356t3jn+iUGHOz+QBHiwjYjvjOevu254vlDCu8J8V+sr6KWPiRWdr1UUhCCZsGnLpsg1J0nYdiQuM7yS0phmkaXPdZwaV4+d5fgb71Aurn4Y6TJ2BPh8Pj9yeK0jbtiDYw3Ir72pYF8ryMeBfTWAlyYAfud3vtHx67/+lX9cKlU+az5/U6GHBZC6X3fhy+1UKsGr3/0yn/jWj5Fp0ju+RRJrLX8L8mipXKWwWCKXywbzoRcrnju+vITivluT9/b+iUscPnoBV4iIMpeeCdtw1asVAECpCCgK36e2bUmXvFIBsPTKgF4JEN62sOrDPQby+KdG0a5uhfTR1acKKwOKSjv9C8/upy2XVWAtzyFwuYd5G6pX708BO/q7FnbKC9vtXdd/9xQ3u66g9WsHd2eCt46Kjj0/FsjQ3JSgLZumpSlZ122v/8ZiOuEZ8cadW1kscubt9zj/3incu+mcWt8c4F8Afz+fH7lnA/gbtn7WgPzamQT6B4X8StS8uR63LU17wD/+47/44rlzN/9f13UHTOXuLU2VFBa4ccAXwusw9vQLj/Htf/4V2rvbTX4oqYtPYkTo13klhYBC2WGhsrq29rqmwSw+vompOb76jWPMzBdi1bsKbhX8EuKy0x0oyl7Ztv18UcGu9sBH2Qd6NlqESr2es8Rsu4/+3EVkTVX1SwPTI1GutZmPPbc/cLFLiAk/LhlbAGw/EWqveuG6fvgQbkE4Re27vocg6IEv5zUw0rQkqJVjEdArFYe67fsmzPHG33e0psg1pbyHUSd+8xpLpTVu//zEFMe+9jajl2+YD/KD2mng+/L5kSNrHXHD1tcakP/gZsJ9JZBX91MnDCtYmuv1TFy7Npb6yZ/8tz8xM7PweXlObLtkBPD14Q6wZfsmPvO938rQ9s1+QnRcipjk1QVSXMIBx4WFskOh7CDE3bkw4mzZN9+/Vs11eOf985w8ezWAeTgkLsZ9Twhv26e3DANCAb1aaVAVu7GutceH8chw3jK8Hy1/4zI7BvRqMeAG5CGiNoUSXm7v3z3E4KbeQI2rx4Ppb0E7J+xNr1QKZK97EQ7VC9W73n4vh9hpoI9UQuJBHSA2Nox6n0ucXwfitgXtLWnam9MkbKLhlThlvGqehvvqpc+79zuXrnHsa28xN7Wm7fUV4O8CP9vogf/wWAPyd2/14L4c5FfSs/5u1XysfeELv7fzq1898i+r1do+iC/UpJmKXgd8eLytrYXXPvsKjz59ADthB6nRFIuSSPMtW0nCq45gvuRQrLgxJ9TxChjb9d9useSmGqHHY4vRO5N8461TFBZLHrTtsM3d9iV7nHveVPgWFrYtEIZqN1V+MDOe0qkucOOraTPvW22zr3v/+i1LgHjrxvsRhFHA48MmYdu8NPyY1/cCCWsdfGpveSBQ90Hnu0DZ6++ZBJyrVAhc4cVvtue70hMQgbnxTsco/ZWEiSh9Lbx6TX+HBW3ZNB0tKW8qXeWcunkc4zGJu67c59SqXDl6mlP596iUw481rYF9BfhLjXH1D4c1IL96M4G+FNxXCvnlQL/Uckn7sR/7he+/eXPix4UQTbEuw2CfwAS8DKcCP5FI8NK3vsCLrzxPUzZsd49T7Ipm8hKsFLx1zYJSVbBQcijX4mO9lxakVX7lTUDQTg5UqjXefu8sl66Magpdc9F7EWi97rHAEhZeP63oLHjyRm3LUPlByvQKgZDnSSVvhbRQO9st1+fRBLn2Hsg9rnrMW3P9Axv6uzmwd0sAseC4CltFkarwDhW7BL/uARBKxztXxuNPf6u+k64I3fjh9erDXNZntGNCr5jcrds+7totTUk6WjI0pe0YaKv5X1/NRyoAyu+2tLDI6fy7XD5+jjUsz6fwhto1Zsv7kFsD8qszm3iwr0TVLwf5ONATs07MemT7618/2v2lL/3xPy4USi+p++M62nnLuMJMV+9D2zbyXZ/7Nvo29Ep564VXEnB7fIb+3g7lAmGIsKANTygWq0zNLDAzO4+DzYaNA1QdJY11IPVB4V/3rRchGD0AK1ey8PAp4NqNcd567wyVck3rdKf3rJfnh8cCuPvXUV31fo0ihPsybnz8MGF+iCCMmuaVZIbexUGp4KE8Qg2WHoSeeWI37W0tXhz+ufKY8M8JZ74LAa4pePnBmwD8SmXAV+3SJa/Ocx/E57+nant9AN5lIFnfLa5APHJsebd9XCe8bCZJdy5NJpUwPCdxaQn3xar54PQwjsnrN3n/9TeZm1rT8fW/jPd1u8JaRtqw+2cNyK/MVNW+lIpfzmW/2rHyK1HxkWL8n//z3/rk8eOX/qHjuF2gQDWwmMLMcCWGSgkymSSvfvfLPPn8o9hJfYywCpzFYoXDRy/w1GM7aNZUvv/PEpRKNaZnF5iaWWB6ZoFiucL0XJFLt6ZpbW3lz772fPxNLbH3g1t47xrTNYBK6IdpKZXK5A+f4dbopDZUzg7AHHXfg+q2N8L5CbAU2Mt0yDqV2gYf9qjX8yXY9J/PSn7hyqfhFVVqvie6Ym3OZnjh2X0a4AKwKYo9bFsPwagqcwm1ENry2r5KDyoLPszNSkDMMW1K3FiI6+3zcQp7RZUELbwar3quui1oziTpzmVIJ9X58uPSsrI0qOu1apXzh45w9p1ja9kL/zxep7zGZ2w/hNaA/PJmszzg1xLy9RT8soCfmVlI/vRPf+lvjo/P/sWlbiiuXdE/orlBAfYc2M6rf+Zb6OjuCGghIHQP++dbWFy4MsronSl6utvZt2PQO6akcHq2wJETF4MCfXqhyKUb00zPeyN3Muk0f/X7Xo2kdzmX81qb3ulN+PuCgzpogQuXb/HusYs4jqOoeh/o2oQ4CrBtK7hOcNy7YABwCy//NLe+BnmlEqLlUUh5dfeKYC9U6Chj5CBwoUvbsW0jQ4O9EdUpt0Nl7scmt/0LaTPcSbj7FQLZyU7g4rpo6l/CXKr+cK57A/SunyoR3nucGlYBGqfIZRas1G0fVwHQ0uGHb8mm6G5Nk0raym9LqTS21kNRAAAgAElEQVSo+yIVFVXNq+n0As6NTXDkK28yOToe/6BXb1Xgb+fzIz+3VhE27P5YA/L1zVTvJtTjwL5WkF8K7rGQP3TodPcXv/hH/0+pVH6i3iM1e/VG94Uu+pbWLN/2Z15mz6O7sGzva+hLvSlVx+Hd987j+B2gnnpsB83+WHnhp/TI8UvMzBWYWyhzaXSa6dmi9jESIQR/+XOfoqW5qe51IrxfbQ3AyBy5FbZ/m9PHonksQkUdJsgCFhZKvHX4DBOTc/Xd98htXdlbajgsJT2h4peVC8tSr+vJdLVN38wlta1evd9ovoSr5rS2proVrnfNF194hKTv2Qkqh/5GOCGOhBPBu6W2vQsIO+pB5Et0kR73EZgr74881wD9ytz26jG98qveV1hZCM9fMl6jAhCtCEBrNkl3a4ZU0tYgv9rrBE/ND+M6DlePnOJk/jC16ppNbPebwF9tuO8/PNaAfLwtB/fVuOzXEvKxgP+1X3v9sW984/g/dRynx7wR8/GGzzumIPPXH396Hy992zAtuWYlpGpWsNODv6BUqXL46IWgEO/tbmfPzk2e6hcwPbPAW++f59LNGSZnF7UCWl7bFYKXhx9l19YNpFKJFQF8tSI/7m1XO7hZPtVVday2hwfQFfjrIggjBJw+f4MTZ64i/Dh02Btq3tI72slteS2zbV5Nb5BW9BdEC7DkXRshlCABlGJULHjPqbe7nUf2bQ2eoYxDhpXvgVTb4fFQ7esd8Qxoq6AXQvMKyG2zN7439W3Yhi8g2Fe/7X3t3PYazCGSN7GwVtZz2RTduQzJhKU/D1Ev/XXUvPHcijOzHPuTt7h99eay78EK7QTw5/L5kQtrFWHD7p01IB81E+Zxf8sp+rWA/IrU/M/8zG9+9sKFmz8qBPGfYUMvoM39aiHV2prlO77n02z1PwGrutoDOa4kQI3VdV3eOnw2KOAQnprPNmVwheC/fOUIJy+MBopLVe9BG6tf0LuuoCmTIteapa21mdaWJtpas+Ram8nlsuRas2Sb0vVud1WmZa469MxU7iIMGFHzQVxewNm5Avl3TrOwWApgXW+iHFA65flpsNRt1GPKtZRaiFYJibtB1er83IVyIKwHhkALsOED57ED2+hszynK1nDVy7AihFFUyevHXCUOAUGP+yjMw+1gqls/vKuo9wD08lsLyr0tB/Go4o5WeOqBO+5crQIQU9mQ+y0LOlvSdOQywaNbrZoP9ivXdF3BzVNnOfa1Q2ul6meB/ymfH/mvaxFZw+6dNSAfWpxaX0tFfzeQrwd3a3JyLv1zP/cffnxqav7bV3JzpjvPW4bbu/Zu5dPf/UlaWlu0At+c+9yEO8r2ocPncFwnKID6utvZNNjP+asT/P5X39dgbkIeERbSasFez5KJBLnWbPjX0kyutYmtm/toyqysAmBZSpu10b5tEYLdijsu02bJXArVvAUcPn6RS1dHtbHxmhvfgLscbqd1slPAr+a/FUh8LekB5DWux3AfCNVfsBkHeW/hGu9OJp3i+af3epVAEcKnvqL3doZt8+q2OUe9BLMOevWd0d8j/yt1Au1YHOiD98xPTywo6yp+Fdb1KgwGtNX1uOvGqHkJ5YRt09OWIZdNLZM25bw6v3E1LYWpaQ7/wdeZGVuTb9cL4KeBf5DPj6z9/NINWxNrQN6zlQB+OegvB/ulIC/3E3MMc/3tt08P/NZvfe0flcuVXZEb8Uv8uOdqFkoAIpHiE688wxPPHiCZSNRVfhZElL2wwmMA7x27RKlcQQAz8yWujs7y6stP8vW3TjI+Nad8vU4p0KXSUIDvGkp/Nfb4/m188mMHlg0n7yeApjAAr3QsDNziCvQFGOPYlbyw4PCxi1y+dsdvZw/BbH5vXoO7WhkgvIAKeu14cEEjnXE1sSWs3od9NLgplYKtm/vYPNgXVaYSlgrABbrbXQJOf/5KpQ/CKW4DiCuAXgL0rhJefsgmqAAQViLjIb46t/29UPNhPOHxbCpBrskmaUM2mwFZ6V6pmlefg399p1bjTP4wF987sfSLsXL7MvD9+fzI9FpF2LC1swbkvWIywQcD/N10wqu3j5gwch///t9/9dm33jr191zXzWk3Uaf92ny+6nZXTwfbn3qEpmzay4RkgkwqSSaTIpNJ055rpiWbiSp3CXcBUgtbwPGzVxmfnOfK6AxTc0WEgPa2ZqZnFpSCXUJdAfkK3PfLm5eOZMLmL3/uFVqa9Q/kqPetwzxGwRNWALzdUl0LDfBexUCZnEbGIQSHj1/iyrU7YKH3tPcjDdreTbibw+nUNCrKP4S852sR5r1ouaLkQ0zOxXbIjDnm+h3unntyN+lMKgIzGTRsg9ehpX5eNngf1HAqtNHfAbkkeE8w3hMQBui9yqKu9NUw6v3pEGZ5WGvn6vEE+/ycNMEdB2i53xWCxUKJubkF5uYKzM8XqFZrdLc3M7Shk67OHG1tLTRlm5ZU8zK+MC1GugSMXbnO+3/0p1SKpZi3YtV2Efjz+fzIsbWIrGFrZx91yC8F8Tjw11P8y6n4OOAvpeyJ2/5X/+p3Pnfx4q3PW/WIvgKTz/uRp/fz2LMHuHZrAvUV0Ap8oLszx6aBbq8jnGZWCDbhKaVvvHOeI2euK65Rtb01BDhKAR22r+pzkYcFuLUK0Hv25CPbeOmFeDWvwhxCRR3sEyJyXD0PQaS9XlkElYD3j3tKXv0oTfR7894Z3sIYTqeAXU0jGL3/zQQQpi9Iv5F1RrUv9vmLcEe4sGBjfzfbtgxEQOgqENMB7gVQx61HhtYp0+BGQO9Gz1nJZDhBOFdC3we9633TXnoX6iriOmp7OVjLPFyygqDE47gu8/MF5uYKAdQdxw2Oq2bbFht6cvR1tpBOp8jlWsjlWkhn0nWvb96TWpERAsqFAkf++A3Gr67JB2+KeNPh/se1iKxha2MfZcivBOr3shPeUpDXQF+p1BI/+7O/9SNjYzOfAV21rxT38jGnM2le+a6X2LZjkMvX7jA7vxgEEEZY+W7Yts1AXwf9vR3Yti056IUBqo7LXEnwzffPce7yaKAoZIEbqi+lc5UrYkBvDpNS1Zio+81z05JJm7/yP77iTchjnGL2pI+odmMdLA38OuBDgGrufeC9E5e4fPWOr97rf7CmnmqX4+tRVD5En30AcUsCX3k4KJUAw7RsUeS7BIFaLKRSCQb6uhjo6yQVzNYWoxAD5S6Ph8BW1b7QVLp34XDWOzeoNAaz3Cnt6t42CNz4iXKUIXjy63bqtLdy7nvHv44JcVNZ14O1zMPlztX3C2pVh9m5AnOz88zOFVhYWKxbgY0DvQCam1JsGeigOZMCBOlMmtZcM7lcK6lUMqLml4I8fh5dPHqKs9/4JmvAAwH8RD4/8jMfNKKGrY19VCG/ErgvB/04Vb9SRb8SyFsA4+MzzT//8//l78zNFZ5RVRzoBb63Ha7HPdYNmwd45Ts/QWtrlmrV4dT565qqAKNwU0wISKeS7N+zmYQdfnBjoSwoVb0S+sTZa/7wMRXaskAPlZo5Nl4Np7vv/fNccBTor8SeemwHn3h+fyR/dIAb+9CPC/Re7aGaFyGUUU4SMqzg/eOXuHJjDNuvHZiKHtB62ntLLzIT7vpkOATXDpZBOkKvg3yC2jsSk3XCOKBCK9fazIaBLro72rBsU4mGzw7Cd0jdH1cJcM0wPpDVSkDglice9AJ8iLvhxDlC4ATHFNDHKX1Xb8PX7zvebf9B1HzNqXH16h2mZ+YoFIo6SKMcVw7FQx7AEtDf3cqGnlZsK+wcm8mkaW5tpimdIt2UJplI6N4DDMAr6Z4en+L9L/8Jxdk1+bLd/w/8SD4/4qxFZA27e/soQn4pkN+tml9pJ7yl1iN/Z89e7/61X3v9p0qlyvaokqsPeNXk433i+Ud5ZvgxbO8LKYyNz3B7fDoSDsLCQNvvlzdPPLoN27Io12ChVFNFIBcv3+bw8Qvx45eFwBWW1v7e2dHC0IYeLl2/w9RMIagMBBObLAf6JbwYqWSCH/gLnw6G2umwtmL2oXTEE0ooBf5aBUuNSSj7PDty4jJXrt/RwB6A2Qd33EdtLETYSU+tZMS0z8tx+8G1lZ6RXiVFRPtqxIFeefiWbdHT1caG/i5ampsUZWrATVGtmlqNUb+q697s+Kb2uNc70gnlvFD1uyJ0tQtDtbuuG6bJP+a566Ogd1w9rnjlHa6jpEPNM7MCoJ0rBJNTc5w7f41yuaKBVs//6DMJH5cOeqEfJJNKsHNzJ32drTiuoOq41ByXWs1z+ScSCdJpr49NJpMmlU6RSqekzydSOamUyxx9/U3GLl6pn6iV25eBz+XzIwtrEVnD7s4+apC3CEG+EsCvBPpLKXoT9iuGfD5/ctuXv3zoJ6vVWreq4LVOWMEt1bdUOsW3vPYiQ9s3afvPnL9BtVZTFIm3X1hC6VCnF1i2bfPo/m0slB2qNa0qgBDw7tELXL52R29/x4rpLS84sHuIxx/dhu01TDM1U+DSlVEuXLnNzFzBmPRE+JWD1Sn6Zx/fycef26coYwPOhkLXlLxy0DsulGP6yZp3QAgc1+Ubb59idr6g9aYPXPVYYHvXiBsjL//kczdnwbMseV3ds4MfB0K/t2VzSkA6nWSgr5Peng7DJR/CzAS9Dj0f6n44E+YSvN7TF3qPewl1/wStvV1R+sGwOtnWjgn6sHlHCIETVByMSmLwLhqVhmXVugpztUITrSDUag7nL1zn9h1lqFpQITaeiM5xAHLNabrasnS0NdGZy9LdlqXT/+uSy1yWrvYsrdnokFEBOI5LteZQqTpUat56tepQrbnUXBfHBccVLFYcZgoVJucrTM2VmF2scPG9k5x7650VN5EtYe8D39n4bO362UcJ8irgTYDXW18rRR+3rAv5//bf3n7yzTdP/oQQIqvCPL5dtj7kO3s6eOUzH6etXXbE98LOFxa5cn3M2yVLZZ8G8m3QFbxXcM0XqzxyYIehPCyqjsM775/jxu0p3VWrKChZwLc0pxl+Zh+9PR1K/IAVAmN8ap4LV0a5cPk2c/OLGui1ucrdpT9Fm0ol+fz3vUq2KRWGi3S0k/D0NupWAuTSrBjIrPPjcBxvUqCJqdkA7KqrXip4XZWrcFeB789SoKU5psKHVO6q/0ENU+c3LiDX1sxAbwddHbkgEtXF7i2joFcrfxJwwbaEO/q7AMqsdsox/Wt06qQ4fgUAEXbAC+Ae7XznClkZCCsO+tDM8L0JKg0idPOvVM0T3K9ekZH7JyZnOHvuKuVyNTbPZf62ZtPs3NzN7qFudm3uZvumTno7munIZb357D+gyXRplWzDQ+a6+igFgErNZWq+xOidGd5/4z2uXLrFzZsTjI/Pcpe8uA58Jp8fOf6Bb6phq7aPEuQTxKv4pQC/WuDHAX5VkP/1X//KqydPXvkhIBF0rCIEgWe6WzbOdu3fzgsvPUUilfTPCANevTnGvP9RmKDtFhDCCvcoBVy15nL99ix2MsnHn9+vXb9YqpB/9wzTswvg94TXlJui1LYN9fPM4zvD+c5lJFph6uPW8grMW7cn+dqbJ5ieK0RALwuvpWzThm6eeXwHO4b6/c5suifEMtaFqpzBh2yoiMMKgZKjvsp3HcFbh88wNjmn96S3iN1GLomDO8FxtYVGc+GrCj+uf4bAeEG8vLItm57uNgb6OmhWhmIFIZYBvQY4CWN5vgFwD9reMfVzsfgqPVTuIcRN0Et466DXVbvaz8Ps0Kl1vJMVAAX0zirUvFrJkfkij1eqNc6dv86dO1NBXst86e9q9WHexa7N3ewa6mZjjzYKdk1N/v5iO7AKOYJFrSDJ35Y6EsELWykUqZTLlMtVbt2a5ObNCS5cuMmxY5eYnV3xFPZzwP+Qz4/80T276YbF2kcF8irgVXjfjZo3j1l1wq0a8r/0S//tc1eu3P5eVfnFFeT1XPWWZWEnbJ5/6Sl27dvmh9DlaK3mcPbizbDQUkx9FWQBXShWuXp7hmrNpbe7neee3OUrRpieK/DWu2cplatBAW4OhxPC67D3zBM7GdzYrXsBBAgsf+Y0CApGERwOXJ9vHT7L0dNXcV1XUyZLgd62PdVsWxa5XJZH9wxxYO9mWpqbQte8ouglRCMueCW35cBBwyuO63qAH5+Y1YFuh9A2vzMfKnsv9gD4tg7y0KUvAlUf6WVvPGdLPehnTyaTor+3g76edpIJO+q1QYec+jw0AAbPToVcuK4CUm1zl/EH6t4Hd3TcfPQ9UodaLgX6COANmJlufc2F7+pp1BR8HdUu94+NT3Pm3FVcx2XrQAe7hnyYb+5m5+Yu2lr0eRvupUVmlnTDSpNaUY5CX1f7QkAy6XXcW5xboLiwGPz+vKXLhQu3OHLkAkePXmJublng14AfzOdHfvXe50LDpH0UIG8CfTWwv1v3/UoUvbb+i7/4X//SjRsT36W1xUZAH++y97ahJdfCS6++QHdfl35MWR+fnGNscsbbUAt3/19QjAsYmy4wNl0IejRvGuji8QNe5WH0zgyHj1+kVnO0QtlUVv19HTz7xC6ymbSiDhWKyoIzWA8nnPH2hT2Hr9+a5CtvHGV2fjEEfQAJ/T22LLBtG1uqYdtrC08mEmzf0s+je7ewaUNXBOaqsveZGuahpealhK4P+HfPMjY5g608O234nNbRzlTuxpJwGUI95vmLME2RiXD8lUTCprO9le6uNjraWjAtgFnctlbZMipgCpBN4KuueVX5anA31s2JbsIw8aAP29SNuRckxJX1UKFGXdRuZDta0ZGVnDg177oOycoiuze18eSeDWzf1Ekqac4pcf9Mzimhzi2hetPCCrLi0RBCORbmRSKZBELvXKVYYn5qBsdxvbH8QgTQdxyXy5dHOXbsMidPXmZubrFeEgXwv+bzI//6PmXJR94edsjHgXylsK8H+KWgH6fq41R8sHRdYf/iL/7XHxgdnfpWE+wmADwz1bx3vLe/i0+8+gJNzdkwlAXVSo1iqUyxXKVUqlAsVXRFpqzIQq3muNwYm2OhWAkLagFbh/rYt3OQS9fGOHnumlf4BS5Yvd3PTto8tncLO+TEKer1gmvp+7WKhkyTpewDytUa33j7FCfOXtMLLVdo8SckYG1LU/QSurZt0dneyiN7hti9YyOZdDIEviKHLT9lOvhDoDquy9uHzzI2MeNVJCxVuZtt8d6Di3Pfq+cEbnnDexN6GSy0EQCWnibLtuhsawnAbidsLdPV/A1XVdVKADI172MBjwn7EIpmhzuEXyEzhsypoDeVvum+l7CWFYKIQgVQOtl5xzz3s6Oof88LJKe89c51DDWv3quaH525DDsGWtk5kGOot5lMZKKo9TEV7upSCG8wqN4WH6/kJfgTyQSWZSlhvGWtUmV2fJJqperloeMihIvjCE3lX716h5Mnr3D69DXm52OB/7fz+ZF/dr/z6KNoDzPkLeLb4dcK9ner6INlreYkvvCF3/vhsbGZl+qD3SzsTQVvMbh1Ix97+WmwLIrFCqVyhVKp4rvRle9GBG7xKHDl1KiFUpUbY3OBQg+nKIXd2zfS2dFK/t2znqpRCgCvMPTWO9paeObxnbS2ZI3rCC0dstucDvwQRNqr6YeXBe7l62O8/sZR5hdKQeEllYttKWD3p5K1bRvL8jo6qDDuyGX5zCtP05bLavmrKneZbarKDwF/jrHx6dAtr0Hciqj2AO4e8f3jMYoeKwbs4TO3rDCN8p7b21ro7szR2dHqjY8WYeXE/JkLZadWqUJo4eOALyLb4Tk6vHVIqrMgal+cC/aFSr0e6L1KZHQ2PBP05oiOwOMT03nTqwAITfXLdAoB6aTFUG8LOwZy7BhopbN1bb6AuJamgt3x791cetCOwt1U8nbCxrYTEe+GPM+p1Zi8M0W55mBVKwjH1QAvhKfs5fb162McP36Zkyev4Djad2x+Op8f+fvrlWcfFXuYIZ9gdZBfy854pqKPwL5cria/8IXf+xtTU/Mv6AV8PNTjXPWWZbFr3zaeeO4RytUaN0cnNagHj1avF+iEJyykx2cWmZgpGO2nAiG8KsCBPUNMTc9zY3QqUFJe4RsW1BbwrS8/SVM6taSCD8Bi6QreIgREEN4yKgL+erFc5av545w5f9NXYR4Z7IThprdU4MsKAPR2tfMdrzxF1p+7X1PyFgQNBZZ6LEz/W++e4c5YCHgJcE+N6z3rNdgrlQyQ6Qzd7nFu+3DsPJoLv621me6uHF2dudBNLIyEKhUndUXujXhVtGcUr9o1lW/A3XFdRu9MMTYxy95dm0mnkhr0w/cmPCd8jwigo1UApPoPYB22rQO6EjVBplYANMB5aQVf+cvJc1xBb3sT2/pb2DHQymB3Mwnb/BE9GCbzSAW6G8zyp8z259+X7O8R1yYvhP++JeygUhaXn44rWCwUmZ8rUCpVSNQqCMdR2uqFse4t5+YKfPObZ0zY/1w+P/Jj65iFD709rJBXwb0U1NeqM95KoW8BdrFYTv/yL3/5f5ueXngy/GhJqDiBurBX1x9/9gB7HtnBYrHMrTtTKx7TGgpqrwCtOS63JuYpFKsR92RQCAvBo3u3cPzsNWqOG7heJexlwTy0sZcnH9lm1COsoCAP/6tACRW6sELCy33VmkMimQihosUhOH95lNe/cYxCoazkowF2Be62bbFpoJtvf/lJ0umE0S4fAlSA/rlZRKCqAb6WP8707EIE8HoPeh/whivfHF6HPJeosofwGBa0NGfp6crR09VGOp3Uc0St2Jnw1oLpB8NKVbg/gLnyLAK4K++HGnZiap7rN8cplz13blMmxb7dQySStuam19Wy0oPeAL0Mp7bBy3cv7jOyerORNyueOn+9Bz0QeOPEhQ/DTDLBYG8z2/qa2drXSmtTkgfdgnty0cHu54cEvysg4Xt2ZBFh2xh5HVbqo30g9HwtlcrUqg6ucJmfW6S4WMKuVsB1cBxZaQjb7c3l/Pwi7713nlOnrkrY/yLwv+TzIw8ljNbbHkbIW0CSpSF/PzvjaYCfn19s+pVf+YO/NT+/eEBX735hbuvbWptsEMbrQT+4ZQOFxRK3x6aJfY6BmgsQ5W+HYRdLVW5NzvszZPmFfKC6wkJ/86YemtJpzly4YbhblfZWV/DJjz0SuL5FINN1KGtgVyxU8KEL9ZtHz3PizHV2DA3w6P4hujvavLT5kcr4CqUKf/z1I1y8dscbu25hKPdwuWPrAK9+/DESCVtT6XGK3TKPi3D/nYkZ3nznTATwct75APZLuPJDl33UmwMEcWebMvR2t9HT2UZTUypIi/DTYj5+S+Z1kLnE5Le+JwR9+AzUcGqlK2jn94/PF0pcvzHOfKEYgsOHcGtLlt07NwVtvIE7HgyXu+IpMMBigl7rTKZUCjxoe2+Io8BdCE+tI7xJclxXYFuwpa+FvRvb2NLXHIxu+DCYqqplfwLh1nfXA9j+FLcy7xK2pf3O48FutMnXapSKFcJvBAgWFooszBdI1KqIWi0CdlXdy/Z7CftDh05z6dIowJeAv9KYBnft7WGEfD3Ar1Td342aX8mwOmt6er7l3/27P/qxhYXibhXu0QK+vss+lUrx4ivP0t3XycJCiTsTM9ESHnQyQSSMACZnFv1PwqpuWAzFJti2ZYDtQ/288c5pCovloEDFKFy7u3K88NSe4ALh0Dg9Cer+8HoobfXeDHhfzR9nanohLMCEYHCgm8cObGOzMhxPKAQ7feEmX3vrBKVyNdZN/+jeIV56fl8IUgXaMglqu7ycAVA9Lk+wBPzpoZNMzc4r4PY70Ck96y31mC/VTVd+AHs7VO4J22bDQCe9XW20NDcpT87zJghCb4y8FSPL1VPCvFL2aWHle6A8GPleeIe97Ws3xpmeXSDXkqW1Ncvc/CKT0/MGwEOXuCsEnZ25oBOmCfpw/LwCb29HMHIiVOoYUA87e8q41A/TBBWB4LhLX3sTuzbk2DnQQvoB6TS3GovrXOdK2Kv7zQ54QDKZ1MBt2967FqfgzUmJao5LsVjSR7b44QqFEvOzC9iVMjhOxFUf576Xw/Bu3pzk0KHTzM8v/jbwF/L5kZhZhBp2t/awQd5mbSG/Zmp+amq+5Ytf/MO/vbhY3qoqO7kOaOtxsM82Z3nxledoa29lbn6R8alZrZQ2ua6aECEAHNfl9uQCi6Wqxv5wyBNByb5j20Y2b+hmanaBw8cuanAP20u9QuCZJ3bR190WtKF717NC4KPEj+pZCFW9EIKT527w7tGLVJ2aUpjoQ4C62nM88cg2dm7dgK20l1rA/GKJ179xlGs3JgLlbNsWzz+1m2cf3xlmkeVBMlz3VqQyVlW8JdNP6K63gPGpWfLvnlUUugJ4ooAP3fZ6e7ve297bv2VzL4MbuqMPU7nXEOwizG+F4HHQj4DeqASEz0XCWg93/tIt5heKAWQDNzmmCgyVtRCC/r5OBjf2hCp+BaBX4e21yYfhXZT4jTD6cDloaUqwvb+FnQM52poffFd8PZO/gVKlxtRskam5RabmSkzNLVJzBMmETdK2SCZsEkmbpG2TSNgkE16lMZNJ0dqSDbxaCb+5MJmw6yj40Ku2uFhCn6dCr0SUihWmp+ZIVEoa6E13vQp5T9W73gezTl3lzJlr/7lWc74nnx+prXdePyz2MEE+zk2/HNjXUtHLfREVPz+/mP3VX/2D/6NQKO3U3LZ++7vuzo2qeMuCXHuOFz/1HE3ZDHPzBSam5gLYBHcfXQ0KbAuvkC6Wq9yZKlCT36wGrRCXP2rLgr07B+nraUcAJ89e4/b4TDj/uFEQtDQ38dIL+xVg+O3wijr30qOkSBb2frDCYpk33z0TTI+rjenVVElY2GWb0gw/s5e9OzdFiHbi7HXe/OZpHMfl5eEDPLJ3yIA2Roe7cEMHv56vlpHXb7xzmumZBaXShq7UiQLeHF4nl/Krda3NTTy+f2t4raDmQQhvw2UvM1IGU3PbNA3syoas4G+wl6gAACAASURBVEk1L7Tw3jM/efYalWotbB9XK30iHJGhTS/rQ3z3zk20tjb775HaBq80AaHGFXoQ5Pug7te/Nqcr0IQNQz0tbOtvpr+9iQ+DTc2XmSlUmC9WmS/WWChWuHZriktX7zA1Vwz+CsWKdp6osxJ5/gJyba1s2zGIZVlk00m6cxn6OrNs68vRmUuTy6bJphNBfjquoFgs4dQco+Ni+Pt0/GddKlWYmpzFLhexHMdw15tt9B7g1YrA7GyBY8cu/fbt21Pfm8+PuGbyG7Z6e5ggv1LAr6WiX1bNF4uV9K/8ypf/1vx8cR/gj9uOh7tUpDroLTq723nh5afJZNJMzyx4nb3UO9dcyfHtigKYmisyM1+MKbzDgt4VAtuy2Ld7M10drQBUqw5vvHPGd4HqLjxPwQn27x5iaGOP4f6NUe0YgPGXpXKN33v9XQqBOzAs1NWP0sQVLNs29/Gdrz4dxG1ZElYW84VFpmcLbN3UG7RjmwDXoO0fjMDdEjH7vJATU3O8dfhs0Kkv2v4eqvX46W514NuW16zQ2tpkpMTLsZDnVqDg40ypY2mZH1H2unQPXfZqeBF6gY6fuqJ0nNPbzGV7eABcv1KYTCQYGuylra01gLYJem1YHeG5EbAr72Dcvr6ODFt6Whjszj6wveId1+XKrRnOXZvkyu1ZnFQzY3NlihUn8KAgvHDvvnuKalUXtmoTlbeNsVEf8gJo78ixddsmggYf/2XpbWuivSVNKmHTmk2Ry6ZwK0WaUwm625tpbkopHfrC36UTeG1cKpUaE+MzJEqLiFqtjqveHG4XHq/VXK5du/Mfjx279L2Nzngf3D68fivdpCBbrl18JX/1xsqvun2+UqmmvvjFP/yR+fniPtU1H7cOKOthgd/T18Wzn3iSVCrJ1PQ8c/OLWgchSyvDogWahTfUaGK6QKFY9eKXYMcv4AVBO3kymWD/7s205+QYd4vb4zPIKVVti6DARQhsLFKJFIMD3WB5Nx5UHCwVl2FBQrDHwvav3dSU4tWXHuONb55hYmoOl/BjMbYPDQlCGY1tyUqJrbnQ8R+AANpbW2hvbQkVuxUug4XxhbkgiVoYK+hpL0ErA/R2tdHblWNqRu9prz5ftXOdjRzaJ132odK3LIsNfZ3kcllPpUe8CV7Thrw/O74VXsuLIIQv+WU7vjwtqBSZlQahHPRdQdVqLbg/7z2wSOC1g9t+xgsBtozJhq6OHJs29mDbdnAdozbmvzuWN589gPCGFfoPXkmXnwPyXvx4OrIpNvdk2dzTTFPKjs2P9bJSucaFG1OcuzbBuWuTnLs2ycWb01RrDm25Fh55ZAeJRDG4R9VDMzExEwF8nEXfAm9Pvbdjdmaea1duMbR1U1BZFALGZopUai49bU1ML5Q5c+EmC/OLwY+6r7OFLRs72LKhg+ZMyvC0eb/HRDJBV3c742MuVnUBYtvi6y9BMDjY+z39/Z3/HviLeNPhNuwu7WFR8kniVfrdKPo1UfOu6yb+zb/5/R+enJx7Vqp3Fei2bSvrsi0+dPdalkX/xj6eeuFRkskEE1NzLBSKwQ0H8FQhoBPfV1eCsekCxbL/O5EqAStQ79IVm0wm2b97kJZsk1YwHHr/PMVSOVTw6Opp2+Z+dm4dkNEHKQzBEl4jdCRrgiPY57qC42evcuTUVc89qHYsCtz1oWp0hWDHUD+feeUpLR5V7cp8Qijg0wC+hKoXcUofBfjeYmpmgbcOnw3aONVhe7pK15dmpSCTTvHEgW0kEmE1wqgbaX0GtBvWbj76uzbVnjCOhF4d+Y5g7BeUylUuXb1Nter47nKptMOCXgIjmUyweWOP554PwqGvi/B8OVudbGsP4ndFbPu8hcVgd5YtPc0PTDt7teZw7MIdzlyZ4Py1Sc5fn+LandmguUJV2J2dbRzYtx07YWl5LH8XAsH775+hUChFruOFEdF9MSv11DxAd3cHg0MbItfNZdOk3RIz0/ORk+S5Pe3NDA20s7m/nWxTMugAKdvqa1WH8TtTUFgAp1Z3SF1cu71cdnS0/saP/uhnfxBofJP+Lu1hgHxcZ7u1Av5qO+PZgC2ESP7bf/uHf2VsbOZFtWd3PRUf18N+09AAjz1zgETSZnJyjoVCSQc6KNvmAW/TcQV3Jheo1LxRKSpUJXRloZtJJ9m3a5CmTFqDpOO6TEzOKQWAPC+sJPR0tZFKJYPKQxC/IiM04EOkwAg0h3+NqZkF/vTt00zNzkfc9N522Pa7bXMfr33qqfDGlWvJ4YNRqItgv+n/CMNYobLyE6aFt/RzDr1/numZeQXcpoqPceErw+1sy2LPzk1BM0mor+V/QcylV2RacW9UrMKKn7/PfDZGZbBWcxkdm2Zqej4ylFIW8p0dOTYOdHnqPQburrJugj4cVqfsF2H4dNJmc0+Woe5mUsnV5sTa242xOQ6dvMGhEzd47+wopbLpWldXvK3e3k727d1K+CaG+QhenkzPzHHyxMVYJe6fEX2WMddaCvIAPT2dbNzcH+wXQjA1McP89DRbN3b6nsMo5CFMb1d7lsG+djb1tpFtSgbvQa3mMnFnCnduFuHUNFd90BxnuOtNV/7QUN8Xf+iHvutHgZk6WdGwJexhgLyq4lcD+LXslKdB/0tf+uO/eOvW5KdMwEv1rrrlVeUu9w9tH+TAE3uwLIvp2YVw7mfp/laBRTimWrVazWVspuCPf9eVmrcRKq6O9ha2be4jlUoFBzWcKHC2jAJC/8GHp3jxh2CqD3a0QkcIEdyf6wgOn7jM8dPepBmmgvdc9bB1sI/XPvWkpm7DdcXdjAJwEa4Hd6q44uWYbgu0CkIAfP1U71nNLHDovXOaek+oFTrjOUvgS1d1d1eOPds3YlqQzuA+9OaCmGI81oT6ENCfibeqvyequlTDy/dmYbHEjVuTlMtVvLZ5QSqZZNOGHlpamkAQfE5WHVrne2RD1S97zEuPgB/e1SoGgpamJEPdWQY6MlhxL/19slKlxpGzt3n7xA0OnbzBjbHZ4JiZp9H9gq6uNh59dBd6f4cwf+Tv4eSJi0zPzMU5Zfw4ReRaos6KWQEwo+zr62ZgYy8CmJuZ5+qVmwghaG5KsX1TF8mEHbwQcZBX93e2NbFjUxcbenLec3QE42NT1KanoA7Qlx5yJ9ixY8PP/+APfuffA6bic6Nh9ezDDnmp4pcC+71S9LH7fuM3/uR7rl8fe011z0eX3jzq0XZ5i517t7B7/w6wLWbnCt7XnKwoUOq66PFchuPThWASDBHIapRtyKRTbN7UQ3uumeAifgEgXcJmo14E6rHHLS3sxSu36e/toKU5Ewt2Naz6Osr4xybm+PqhU0zPLmhue+k52Lqpj9c+9UQkH4hJiwntiKvegL8WRoTrOvDD5/HO0QvMzCx44FafuwL4hFmxszzX9hMHtpJKJmMrbZFdfkUlOChrNZIaxnMJ8tTYqKvglWOB1yfYjw9pD+x3xmYYn5yjs6OV/r4OLMvrMRDCPHwPXf9kT5mjeAAMN70v/V0BXa0pBruzdLakWC+7OjrDoZM3OXTyBkfO+c0VWggDgHXgawHPPrOP5uZsxEWuVqpqjstbbx2TB2OrccEV436fS0E+ehoA/QM9tLa1cOn8NdTpsdPpJDsHu0j7EyDVg7x+TNDSlGbn5i4Guj3YT4xNUZ2cikyYEx1SF9teL/bt2/Kzn//8d/xTYDImOxpWxz7skK+n4lej5tfMbf/bv/2n33Hx4q3PLgd4+WU0s2f9zr1b2bVvO1gwN7/IrP99ZnVyFrmitTWHuylXHSZmFsMCxDepSgUefAb6Oujr6QjVbozFFixBaWUpBYX8r6h+P9z07AJvvHMGgC2DvezZvolUKmw/FfJso6DSgAI4jsvE9DwCyyhYLDKZJO255rCtWonH8tOlV5LC/AJh5GsYRgW7zHHL0nPFrBDMzBZ45+h5gqFzdn3Aq8Potm/pp7+nXUmXFbhN1L5vptXXs8IIIcxyXwkZBUmcijebeEwoBa55JawKeuFHHPTGRwe9+o14y7LobUsz2NVENp2oe5f3yoqlKkfO3+abJ29y6ORNRifmg3nuvZvWFtG1OhWs/r4u9u/bGuQhRn5JNV+rOrz19rHgxHql9ErV/EogDwLbts2PyCCAVDLBjk2dZNNJo3Jep5KhVFqaMyl2DXXT29HM5Pg0pbHxYBy9OpRO/ZKdBL3eRo/7+OM7fuoHfuDbfwGYqJMlDTPsweitcndmEe1Nb/6Z7vTl9sUdr9e7Xtv3+uvvPXfx4uifVwFvuudN1706Xn7rzkF2+oCfXygyO1cIFbsCmQBElgEuYLFcZXquFEAEVCh7i472Vjb2d2qgjTOB7CkvFb6vmyMy118xKeSvnz5/E9vyPnN55fo4129NsmfHRrZt7vc8GnjgDMCOqlDDCBOJBP09HUHaNIbJmzQ9HsrNxIJcuYau6g3wa7erhhda3BbQ2dFCT2ebP6c93oxifiTqF+dUwLfnmunv7fA783kZqYEe/1lYsje5cX+xphwV+HEKuRo5pu9TaoRqe2zovoiESSZsZMpdP/+EsIJKkY3/XXLLHy2Bci/+B5AsC1K2TX9Xhv6ODKnE0ne41lau1nj7+A2+8u5ljp67TcXv+Ok4InhuQnnXdEoaO2I8KbZlsX3bxuCgGkRfN+67jldmBQdXHcp1okPTLTzv4PkbU2zf0EFrNr2CuEJbLFU5cu42PR3N7N7cTblcpTo56c1oWKeXfdhWr+23Dx8+93fb25tvf/azL/0WMLvsxRv2oYa8CfY4KC8F/aXOW0mFIPh7991zu44cufADtm1Z9QBfvz3eYnDLBvY8shPLgsXFErNzheDjJjrYvQIgZF8InIVShdl5H/Aqdnw52tSUZtNAN83NmXolC+rYHVkMBW3cljeDm16LD4EslDRKHty6PcXcQjjkz0VQqzmcOHOdS1fHOLBniA39nUgAyYup9YWgD4B/8VCdE6htqdTNY7IyYNthWoUVFrCgAFwpu2W+aZUBM7waTlH3FrBz2wDvHr3oQR3LHxpH2BlPAXwiYbN9S79f2dGvZm7bagICpMp1JQFmCSyfoYoPqRxVvmuVK1WehZWNSKTBQ7G8iqAFtrBwJdyFFQyHtPDD4MPfH7cpbGhOJuhvz9CdS93XOeQFcOryGF979yr5Y9dYLFc9sLtu8D7bNl6vf0upNPu2Mnh6tnFjD9lsJmxZ8fPNUrxuVsw1lrqO/Nmuirr+iWb/muXMcVwu3phi68YO2lqWm1xISZS/OjGzyPRckc397SRb23FnJv3hdfWG1MVWADKvv/7eP2tvb7396U8/9cdAdOhBwzT7sEJewtaE+FJQXwruywG/7t+FC7cGvv71oz8CpEygx7nnTSU/sKmPA0/swbYsFovlYKx1WOh7CkJCR+14JzNibrHCwmJZm95VBkombPp62+nqyCmHQlUXwFCvScQK86AzmwJbU3QgvLTXHIfzl0e98LaF7YZhhRAsFkscev8c3V05Htu7hfa2lqASIQsuVdmY5VjcZ2DlU9S/HEckvwI4qiC3CEAWgfuywA+BZ1kWHbkWerpy/ix46IC3w3HxtmUxONBNc1M6KAhNvsVdzzyiV3HkIlp8B3moPDxtzLwavwJ2WZ3wuBw+fLXSFJ4nT/UCeB4hK+gk6cpU+Mdz2SR9bRnasve3KJpbrHLxdoHjVyb58p+8h+t4/QDk7y74zQX54o3iX7ZCVX832aZM/bAmrT9APUePc/UnLwV/Vwgu35pmsK+d7vbm+teuY44ruDI689/Ze+8gS5L7vvOTVc+2977Hu531BlhsE4YUCZCCBPKoE4OiFBeUhJNOIG8onUQFdYq7kHhGcYrjKcTbu0AoREhaSQQlEgJJkdgVuAcQBIgBsB67s2Zmd7xv38+bqsr7IyursurV635vpmeme7G/iZ6qSl9Z+fKbP5O/pDeXojfVQ65ZRLqtYvtkcX1wHf6d3/mTXx8bG/i5Rx459BLwwaE2m5B1rxtwi9QJAHcC/O3AXbS5j/wtLxcGvvKV7/6SlLJvK4BX4eafYHxqlAcfO4ZlWVRrDVbXS75XtHCisWJcoCnutYSgUK5TqTWwLOWbWv+lbIuxkX6OHJxhbLjfL4OgfPz8RMqN4Hw44fmdru+T04tgASIEXLiySKPhYOv3scx3COtcWS3wtW+/wStvnKXWaPr5VVmWUaYuN+gbrdvW7RJbf/SgzQl/5jsoEA7fRYRN2CJMBP10cO+k/52soDz9HfWhID09WWanhsP+1vkjfW20sc27BX0W+W7CSBOR7cTKNBcdsXoRkbYEfWRImBLHhdk+49vptJaA4b4Ux2Z6OTTZe9cAvuF4nLle4quv3uD3X7zG65c28IRFNpNBWLr9ItJ2s/3atqI9iU0fr99U9mJxCU1S8kSRfaf1tonfKtVmZZmhUsLlmxvcXC11Vn1CknLNYbHqcaNp4UoCfXyol/eCrXXRq4qv1Rr7P//5P3j6xo3V/R2/2g8o7VZO/la5+K1E/JvljYTXao3Mb//2N36x0WhOhsAeiuTjAB/Xyw+PDvHwE8exU7by97xeTJgsRTB8TYt6zX2vF2vUG04Anpp68lmmxofIZtPoArQHMi0O1J7rWn4dInY1733ZrsonUOJeX4yvwwXUak0uX13BsoRyWObpIkJuLrJ4kJILlxa5dHWZowdnOXZoNvCOpj+IJGR0AsCRQm+Db3mFQHUcj09glMx+DZ6NzOo55PJ1ewi+iSGu9yMH+3tCbj6wsPe/hWUxPTHE3PSo7+JYSQBMHXiIA1qV0drezd5bN0YI1ELLEtiCiLGb8lCGvwtDhOoXg1sHg4PHZPjVB1fjSRh5gt6JcaYq/UA+xfhA5q55pfOk5MZajQtLFa6uVHF8oy4RNFkwNNjLzaUG4WFF/vjUnD1KvaB+l7IjkX1SWLlUpVis0N+vOGD9W0S0iuyTKKlMTZZlRSziO6XNykzOoBsN15eLNB2X2YmBrutVZdkUybC8XOLQoK3cZm/qES8aVipVP/SP/tG/+bV/8S/+7t8Alm6tEe9/2o0gvxW4dwv+7eLaAr6U0v7iF7/218vl6uF2RnVJAK/D+wf7efTJB0ilbRr1BqtrhWBy0XrcKJekdeMBtLJRrNF0PL98lTZl20yMDTLQlzeQSv+QQ5269my7KVOCCGZ0KWXQpmg+EQVNP/zcxRu+IZXw9cgq3AveR6I1C55UXK1nKZ3fqdOXOLJ/Wr2X3wF6Uo4q66ODIa6PJ5Y0mMjCJoeLBaJ9GyQ1Fgoi4lRH9UcETCGoRec7MD/JK759hXaAMzLUx/75CfK5jFGeWbZulKlB94EeM62MvbSKTVuCbEqQsUNL/q1ISmi4Hk1H0nAlTQdCYwu9cNAfX0bqC25NoPfbjxHek7EZ78+Qz9wdcF8vN7m0XOHycpVa0z9YxbcNkIgIgA8N9rG4vI6wVPv10cKWEHj+eA2lLDFY7BIlr99Ypr9/TyQsXDSyqYI9m8vQ29tDLpchm1V/mWyKcqnGhQtXk0F+s/ZtEtfpay2vV2i6HnsnhxLG2talZDJpnKEhXr96nfvG8yC9iCe8rU6yW18v/dTnPvfPz3/+83/nHwLVTSv7AaXdCPJbGdR1I7rfDPTbPv/H//iNn15e3vhwO+v5zQC+t6+Hx556kHQ6pQ5y8N1GmqJ49RyCuwn6npSsF2u4rhccviGEYHiol9Gh/uDwGxXh/8xkCOym3juggEOX/vwcoqQkbJNKS/DbNQFVB28UKiyubETFwRrYjfeQvjETgsAaWwgY6u8lk0kHU0NgB+DHx3cL6AeRFBxJoVupQSm6GAiwSZcV4FTYtnht0bCggMBqvL8/F/i07+nJcHDPJEMDvQn5jGfRUmK0ViNe+Apj24ZsyiKb4pYOZBEizA+qj6tNj3LdxfVkBLtbjr7zje2AiKtfnS6XthjtT9NzF7bBuZ7kykqVC4sVCtVm4CVPc+0aoIP//TMLhgZ6w4WeP26FDBds2l+DsMCSWxngbb4IuLm4xqGD81iWUBKQGAgGazYhGB8bprcvT19fD329eeyUrer1uX7H9bhw/iqLi6vEN8m1K7kj8O5y4aLmozX2TQ+phWWn2f2EPfks7tQkr1+6wvGJnsD5kQnsm51kd/Pm2t/53Of++bnPf/7v/L+oWeUDMmg37pNPs/X++KS/FKH7W4vkffFbesh79tnvffyNN87/NdsOAVzfx69xMX4mm+HJTzxGPpfFcV2WljcUiFkGoBvgDiH4g1Ar12JVTbx+ut7eLOOjg8qJikrWlsKfOcT42UhIy7NsjTf4XR/wVcjLb5xjo1gNV+IyPMDCPLHKPGkuSCPVXvonHjrUUo+MNyA+k7SZWeIS5KR88QWC2Y8ikk33e7ioMTE57s8eJOVKnUKpwvT4sBLbR3suUn8kPKF98TghoCdjkUu3aHC3jWo+2DfdcH+8/t7hfnn1TuHJdJC2LUZ6U/Rk7zy415sel5YrXFpWh6t4hq97j9hYQ0bOm3c95R3y6996Bcf1lAGeMUZdzUn6Y9jzlE8ALz5vSnNYxe5iSR+4/wDj48MtfgeQhtc79djiMEdfC4Uy7757iVqtYdQYaUTL79dI1abdZnjC3vpIUa1v25NNc2B2ONhOqZJFJ4+WOcYoZ229ROnSJY5O9PrfqzPxvc/ZN4eG+v/CM8/8yh8mvc4PMu02wzvNUYsu/lJAFrU4MIFbGOV1ZIH/6qvvHjx16vx/Yx4s097xTZTLt1M2jz31EPlcFk9KVlYLAYcdMbYj5OiV33Nf3y4lhVJNceOWOshkdnqE2ckRMumUsUAg4DyDq/8X6BqFXjyISLzZaRArS8TKDP6pREIIFlc2KFdqLYevhDYDrWFaX62lBeMjA23bpEStkmKl6tdJcG33Zxltjr9YUvlBmUZy/WCJUEgfKVMDcWxhgBD09eaYmRwJfNQHPWfmMyC65Z1ig1nH5dKC4R6b/B0EeEBx4n1phntT2FbsPYO+9X8PQDplMTGQYW4ke8cBvlRzeOtKkW+/s8L5pQqO6wOGMAwtw1Gqxn/wcUXwDrZt0dfX449HMJOEW1nDXt5aWCI2fby5uNY+h4g8RRZ5AgWKFy/e4NSp9wKAT6hi0/rjcbc7fnT+Sq3JuatrxmE8HbbBp+GhPnLT05y+VjBc3Ybi+mRXuEq877peenl5/Zmf/dn/9dDWNf1g0W4T13cK8DYK2DOEgB4HcXuT/C1i/ps31wa//vXXfgFIJYG6NrQzRfYmV//wh+5nYKAXgNW1Iq4njaNGARHq44WvyNaTlESyUa4HZ70PDqgtWjqvJSBliWA/uOIACF2HCraUn5mi93hE6CldiaEj++dVkDof+/JSsHCw/LTqCNIQrfROOmGB8IwtS1JNqGMjA8F8EEiEUZ7k3jt/nbOXFhFC8DOf/gjCV02IWNokMvfBSyGjLmHjaf3/RDwsCDehORTr6srj/RjNq9urufrw+5tyFrMdZhlCwEDWInWXHcVkUxZj/Rk2Kg61pkfgGMZ/b9sWDORT9N0Fzn2t1ODySo2VUiPggs1vprfs+UGRxZI/LAl7W0lnhgZ6fQ+TySJ7vTBOpW0E4FTrLe3q4GcGEDO8a81lhujfPwIq5Tqnz1ykVKp01E+bl7x15m730YMC+vPX1jkwO2wM4PalBO/n09j4MNcbDpeWbzI9mGvRw29xkt3I+nrxSz/+47/y1Fe/+k8/0M/79H4D+RTQA+SMtN1ujWsBe8dxU1/60jc/12w6Ixq0Q6c2REA99GQXhh1/+Ahj48MIoU5XazQdn9MIuXbzGk40qvxiqY7nSSxLeRYb7s8ylE+RsjvTwTqepOmC40qaXqt1sCFlJvA+FjyKYHJEhIe2GEEAXL2xiuM4PqduGtlpvahavAiJsraXYbzvG4hU2magLzzmtlFvcv7yEu9euMGKf+KZ8Gefdy9c49jBuSiARoCcqGrUBEth6A3NfLF98tG45L4NrOL9iViEWVsAOpovXCyEaVSFwuhYU7xvC0F/zsK+R/I3S8Bwb4pK3aVQdYOFaV/Opjdrd8Ks3TJ5UrJUaHBttcal62vUGs1A1SMlDA70ks1mCEBFfwBjIaLGhQ/s/vizAFfC4GAf4sqiqkwY40SA5YuuR4b7eeD+A9TrTb79vVM0m8b27DZYlhQ8NTkaBXKh9f6tVvb6t3rt2hIXLlzD87x2VbVvxDam2jSvH1ms1Ll0c4P5ycEta0kqcHp2nMuNJsvFNYbyqZgHvFZL+1jcw67rfh74q1tX/oNBuwnkNwN3C+hDAXwSt952K1ybsiLhv/VbX/+ZYrFyLO7BLpmjj8YdOLKX2T1TIJS72mqtsSnAK1FuyCkXKw1cVzI21MPYUA9DfbmY05utKWUJUhaQVvmarqTWVJbUumN1FweW1Br6DY43wu3rlYGEesPh+s3V4EhKT4O3RntL6UODDhXhBItUhkxCwOhwP0jJ9cV1zl26yZUbq0r/6fsy15IOieCNdy5z+MAMtkhGPJH0IJPThAsAYSwOQimDjOfR6Y2+EUYl4YIgZnWv0ya1IwgXkcWCWvBJbEvQn7U6EBXfeerJ2qRTFo2mR0/WbrsA2g5yXMnNjTo31utK3y4lN5fX2dgo4Xr6VEKP4eEBDh+cDb+pVOPLEyBkdOeE+s56fKvvNTzYZ3DwsW8uBIcPzbJvzzQSSTab4fFHjvDCS++06ubDXERXmepxeLifnL+zIr7aNheeeiA2Gk1On7nI2lphy75KWtx2Sl1nM1cgCbRWqGLbFjNj/ZHwTuuZ3zvNhfea2PUSeZtOPOGZ8T//Qz904jvf/vbT/6KbV3q/0m7SyWvghSgo54EJFMibBnnmGfMmqG/23AL2f/RHLz155crST5jgDRjAbLXh6AXT81McOLIHBNRqDQrFqg8cSQBv/qnyy7UmmbTN/QcnOLJnlJGBfNcAn0RpW3GENlUtbwAAIABJREFUQ3mbbNoKuUphdoD5ngY4648gwuvla8t+2pgO3k8ceTfCez8yeC5Xa/zh117h2y+9w9Ubq/7WPbMNImhDtVbn3IWbwWgwB0Y7lrLTNKEUJeyJIE+sDyLlBvn8tgZlxnTwRr54n5rt0/EpSzCwQwBeU9oW9ObuHMDXmx4Xl6t8/1KBq6s1nEC1o/Xq2r5Dha2uFmjUHYLeD1ZuIjqGjPYK4/+efJZMOhMusP3+7+nJ8uEP3cfB/TPBb08AY6NDPPjAgZZ2b9Ud09NjkTTC/PCxciqVGi+/8jbr/g6c26LNGrYN3zCpiKW1Mktr5VsucM/BeVbsHhpeaISX7CBH35vcPf/3wsKJJ2/rpd4ntBtBXv9lUOA+RgjucdBOspYXREHeNMSL1HHq1IW5V15597MmZ93KxdMiphcCRseHue/BwwhL0Gy6gYtTvV+6FeCjAOM4HnMT/Tx4cIL+nswd6VDbgr6MYDBnY9tEJkP0Pa2gbt6Xy7XQkU+QLwR7y5xk0YaG0b9gUVOp0Wg2o+BnXPXuQOEHnDp9KbDOjc+VbYHcXBCI5CQY8XqRgl6gxMox1ipGFaKlDBP2RSQ8vEbK03UI6MtakfLfz1SqOZxbrPDW1RIrxUa4fQ3CfjL7nrDPbtyMnUBqfl8RfgE1Dg2jUT/J0JCxlc4SzEyPsvDhBxge7De+XXidnRnnyKH5SH1JpINt22JyfCQ5TcviAzY2SjjNBI+tm44FscnTlplbkt7usLu2XGStUNui6uQIYQn2HJxn1c7jemwJ8OZJdih8+NLCwonx23yFXU+7BeRF7G8QmEWJ500A30wPL2gFfPPP3F4nVleLPc89971f8jwvCyEYq3sCTi9JTD8w2MeDj9+HbVu4rmQlYS98yJmKAPzDA0zg0Pwo40O9d7RTNdkWDOasiKV2ZCKNTUDh5ChYXi1EQT2y2DHUG8b7aU4sQHhi16QwDCmAfy1Vapy/vBh5l/h0ITaJa0lnLgASrsG3IwSIljqMDMKID/tVRPKJWL449vdmxI7i4O8UlesuZ29WOLdYpVh12343sx+NpRQCuLm8oXTWIp7WTxHvbKJjfKhf/d7SKZsHj+/nwfsPkPI980VA2J9RBHDowCzzsxNt3ir64SYmRrANg4rEz2o0vlyubblwuFXqLP8t1JKwWLm8uEGx3Gqo2Ek1Vspmct8cpWxv2610cZe4Bs0B/3Fh4cTdP6t4B9FuA/k06sNNkGw0lwTi8W1zSeGRe8+T9m/91tf+VqPhTCWdJhe3oDc5/Ww2zcMfOk7ad+iyulbA81wDuwyAN/2vo+IyKYt9M8OkU3f/0+TTgv6cstKPg00obifCFWUz6VDkGCxiYosEjILCpOGqTSRc24QFfvD9Np06fWlT3SBGPUkgfksUvH+rKDjoq4R6wvlbxMJFJF7ny/ie697PVG16XFyucWGpSrURArT+YMGaSC+whNG/5pgEPNdlcWk9+Abh9472YXQhpkaHhTqGeWiwj4986DgzU6NB3sRvFRQA9x/fz7h/DHJrbSHNTI9iZEv8cZgh5XL3BuIi+K976jRbt8VLCRdurFOpNW+pnnQ6zdDcDJV0vmVLncnNt3Hr+yPA/9Flk99XtFtAHpTO/QDQj/r+cfF7krjeBPWkuPgzgPzt3/7jT6+tlR4zxc9JXHzIlSvwt22LBx47Rr63BwGsrRdpNp1gmghAjSi4a3FwPpNiz9TgLXkt2y5KWYL+jBXZL6wnumBiNdAyl01jaT/9JgdPVGQfcPNxrj6yUDLVFlEVhgL32EJDQKFU5cKVJb+VZpvvAhljISmyRTgRSSlC4CKEkuB9LehJv38Bvu54XF2rc3GpSsXXpYe/N4gCfBQAh/p7wIovsFTs9cXVwEA0Ylfi5zePsQ3g278MDvXxoceOquOY/YyWjhehLwsjCPwyH334CPv2Tre1mclns4wMD0Tr1e0Q0asmE+TjeboZ5KLlZrNEXVCswZsV4XmSc9fWqDedW6gIsvkcfXOzVEU6UVyfuDc/pF9eWDjx07dU8fuAdgPICxT3fgDFySdx3+2s5zX3niSeNxcJHuq4QvnKK+/ufffdaz9jAnii/jz2DHDgyF7GJtRqvVCqUK01gngTAFu4EiHIZmxmJweCyepektLVW/4EF74vhD9kPcnlsukgMMLRinji2LNOb5QVTOVRVsu/huuwQOTv51teTbY87mROa5tGxNJssngIFiWY39dfzJnpYq+VyNHrxVMqHFfvJ2q6kpsbDS4v1yjX3eA3EMPbyHAJOV/138z0KOOjg1GJiZ+hVmuwaowHYSQIhhKGLUiYMlh8GtVGSSi98Lnz16LtQunbjx3Zw0cXHmJiYrjlvac1F99aZEKg4MqVmziuu0mizcb3VgOnuxXCdg1D1/U4e3WNpnNrJ8MODPeTmZqiIay4NX0n2X9jYeHE7C1VvMtpp4O8DRwGpgjBWoO4Cd5meJJb2nhaC7WTowHU8bd1l0rV7PPPv/zfg0xBFMCj3Hz03rIsJiZH2X94Lwio+pb0mgLDLQ1qBouqAXFqpO/ucaAdUMpSLlPjIGUCMyj1RFzf3mpJbwC5eZ8QZrULR0/MsUUXgpnJZGOmLckEljiob5UtYfJr5db1kwHmbeoRRgJLKFH9+4kcT7JcbHJ5pUap7m66ugo5eP+ZWJ8JOLBnksGBXv83FFWdXI8b4LFpdeH3DL6fsfwS4fhbXS3w7e+e4qJvBxK0zPjuPT05Hn34CB9+4hgD/T1BzTPTYypNu0Hjkyclp09f5Oy5q+1avG10N0ZYfMnUdFzOXV3DbRGtb9IaP0pKwcTcJM2BUZqeCPbId0gjwDMLCyfeXz+sDmgn75PPAkdRVpJ6MaINKITxFzeua3fVfw7QJAT+wCjjN3/za3+lVmvMKoc3fkURQA+fQ/085PJZ7n/sPixL0Gg6rAXW5jGdO+azmiKkhLHBPJn0zrMNSdvKfWqtqX5IEScdgJDKSMm2LaR0Iw5uAsc3oPYpC+NZ3wvC/fb6HpmYNliWCcJDaySkUhbT40PYvp5endUOtj852xBOwsZkLOLPIhZmgnEMmOMTdcw9t+Fr3HwW0TTB3BTzES7V9rT3y0zkeZKNqkux5uD57g5jB9mF/heksc9bAFKgT9uTQihHMXpsWBZHDs7yxtsXKcXE2oVSlXKlRk8+pxw7Bd9CFS7Q/hD0aYLGwS3+TTg2BU7D4e0zl7l05SaeJ7FTqaC0sF7f0Y7/YUeGB/jIhx/g6vUllpfW6enJ+WMi9Nhk7scXAhoNlzdOvcfGRum2+tzvulvaL9+mtA5DO4mEWsPh/NU1DsyNhJKTjtsrmDs4x5lSmd7Sere/kx8F/h7wa91l2920U0G+HwXwGvnaOa1p92xy+noc1FCcu2lJH+R/7rkXHr92bflTtm3HRPFx3XsU9C3L4pEnHySTTeF6HisrhQCEFCCIgBuIAL/fqkzaZnggf6f68bYpm4Kmq7yCRZyESNgoVlhaKYQe7YwFTQTw9WrGBHNjMlUubSGdtkmnMmTSdvCXTdtkMza5TJpcJhX85bMpstkUPZk06bt0Nnlbui1Efr/AeZQ8qbbDFWsunhcuasLPr+6UR0WF7qG74dDrYtz7m7qqPLZtMTE2SLlS9aVAAtev6Nq1ZQ4dnCO+qhDGQsLAZMzWmfUuLa1x6q0LVEw3tiJ2jVHQVgvmZsaZnRlLOLUu6s61VK7y+uvvUau1t0KP5+kcGCOFtM8TW3h0Vl5LJ3ZElVqTi9fX2Tc91LpqjjappT3pXJaZg3u4dtqhr9a1D4H/fWHhxNdOnnz61W4z7lbaiSA/BhxBfV8t00kCeRPMLSOdZTwDVFAAb4ruMcu5ePHm0IsvvvM5Ux/eTlSvnn3wRnD8kaMMDPWBhOWVDVzPC/bCh5OAejBKAKBcbSofzzuYBEo/XPa5edf1WF0vsbJWpN5wfFezxiIGA+BFeJ/NpMj74NyTS/t/GfryaXrzGfLZVKSPP6DdS5W6R6Hm4GoXyoYkJIIHHYCUOQnEM7QbLQJYXiuwt9kkpblugTomNimfv9DQx9FKfwUghOTSlUXqjaaaA/RYl8pfhuvJVo48aGFkiaJKbfO+K8vrnHr7PK6zySmpbfJ2ivOt6baN1b/lUgvlOlcWC8x14v42RsNjgxQLU1SuuuQbnfny9ykD/ObCwonHT558+gfCv/1OA/kJ4Jh/7ztETeTa2wG9Ca0NoIgac0lifk3id37nT37RcbwB29bArjn1dlb16m927zRze6cRwHqhrHzSa6UyphSglYsvVRsMD+RI3StH5F1Q2ga36nBzuchGsYLreUgvFKtbQhkOZtMpdfUBPecDej6b3hXv+QFtD3nIFs5dk/BRUas0EqDQR3bJRrHC2nqZtY0iRw7OkcmkwzymJCgsPChQepJrN1bZOz+RyGhG642K6k0p09zMOEvLG23fNeD9A2lByNm2AzyT+b148Tpnz1/d5Hz6W6XOSugo1a1w+B3SaqFKyraYirm/3aI5SGB+3xSnKxUaKw4Zp7FVNpPuA/4v4Be6auwupZ0E8mPA/f69BvitgD5JZO8CayiQ1+5tW4Bd//27f/f8n11fLz1qWaook4MHE5yjYva+gV6OP3IUBNTqTYrlqq9fMhYFflXRygW1hkOj6TI92vnAvteUFh5SukyO9JJNp5QoPaNF6qnNJG4f0A8Y5VIWRdwAjE0RfACgfloDU0FAoVBhbaPE+kbZP7fAw/Uky6sFZqaMfeZa7K4DzPMBfLpxc5W52XGEER/K0tkUtXT05PgQ6bRNve5E4jbLY1bTmkb1g+dJ3nrnPDdurMQK2B4OexuL2kYyPr7RrsW1Mj25NAO9Odo2OOFdhCXYd3CeM9UGdmkV2+vKav9zCwsnnjt58uk/6CbTbqSdwl6NAI+QvNUtye1sfMuczlcGrgJVNgF2HfDii+/sPX368s+3ArsgLqI3w7UePmVbeJ5kZbUY1QGIaIV+ZhDqSNZyrcHIQJ5cZietsTangd4s9+8f58DsMLMT/YwP9zDQmyWX+QDgP6AopWzBQM4mn4keidt+mIS/vxuLa2xslA2rafWbW10rxmDcl6r5uoC4hb0Q4Loui0vq7PaohI5IWv3bDEo0Fg+WZTEzNRZK8oQC6Wqt2TK5tHk1RCxBo9Hk5VffiQJ8h3RvTDKT69y0JSLxti1dXixET/XrkHK5DHP7ZyjltPuUrugLCwsnprqudJfRTkCZYeAJwrVanIs39ehW7B4/XgKLQInQqE5THOwBRKVSSz/77Av/g+fJtBLTt3LxQAvYCwHHHz1Gf+RseBdLWIYeXoQ/7GACUq0slOsICRPDd8dl7XaS1ApWnz4A9w+oHfXmbHp9LZmU6jS5huvRcCQNx/P/QB/vq5nrXDZDrd5o4docx2Fjo6SOhO1CdHz12jIT46HdS5QhTGIPteTBfxRKZH/h0o0grNl0+cY3XyGdSTPQ38PgYD/Dg330D/QGv/V2evlCqcKr3z9DrRYVL98y091FRlNisq3Gd9tArutx8eY6B2eHu55YRkYHKRbGqVxr0lPv6kCcceBfAZ/uqsJdRvca5HuAD/vt8GgFeG0no7l3YleBMqq76qeN70NrAXcd9m/+zVd/plZr7NOHzEQStRHZg2ByZoL5/TMAlCo1KtV6ZOUfVJIwUIuVhtqCYwv6e7Pte2XHUsy6V4bhwd0HwP8BxUgISKcE6ZSNOew9iQL7pkfd8ag1PQb6sqwXkreQLa8WGBzs8wslug/PrM+4r9YbvPn2Re47sgdhibaGf5tZrg/09zDQ18N6IQog9XqD65Ua126sgm+MNzDQy9BQP0ODvQwO9pPNpIJSby6u8cZb53Bd99ZQvU2e9kVtVck2yfNjuolOS42nq9SaXF8pMd3R8bTR0Lk9k5wplWmsNbvVz//ZhYUTP3/y5NPPdJNpN9G9BHkbBfBZQlCH8Mvpn7Appo8D/ipwndh+dyNd4vN3v/vW3kuXFn+mVRwPUcCKxud7sjz0xHEEAsdRJ8vpdCJem3+jb2sNh3rTwbYEvfnMPXVde6skBHhe+HohxodL/3De/QD4P6DNyRKQS1vkjC2Q86MzfOjoJOulGuvFGquFKqsbVVYKFQqFCs2GYTHv54mLw+O0tlHk9bfP88DRvQhLTyUhQCwtrVGpqQW4JyWeB570wmdXks6mI2UmVed6HmvrRdbWw21d+VyWwcE+Uimby1cW6Qb+tgmCt4fuEoe/tFamL5/p+uRN27LYd3Ced9+skyqvYSX7sW9H/8zXzy9unXT30b0E+UdQovok7t3k4ttZ1l9Diejj75DEvQfkedJ67rkXT0gpU6EoPpmbjz7Dox95iEw2A0hWVgv+iVcGmBl/ZsWu61Gs1APHDwM9u5GLVyQIt0UFb6hNpRHKstlPp8lkuFqZr/en+9YP6NYpk7aZGO5tUWlVa00K1SZNaVGsOZSqThuPZ74iXOhtcVAslHn9rfPcf3wflgj5gVKpyqm3L+C6Hq6U4almUhn8eZ7E3do3uq61BQSr1bqxx35zq/utI7sjQWfGd/dkMbFJpZdvbnB4frSzQ7qMcvL5LLN7p7l2zmGg0n5HRAKNAL8O/Fw3mXYL3SvDuwPAfloN7MQWV31/DsXBbwroSc+/8Rtf+fPlcvU+SOYw2+nkj9x/iBH/LOiNQoVavdm2EjNMStjQxywKxdvvTlG9IunDt5RKRx/+xZ/9Pz+tJ8M84b1+jue712/5Ae1EyufSTA73MDeS476ZPj50cIhPPj7PTzx1mIcPTTE+3NtebCSgUKzw+qnzOK4bLMbPnL3SulCIzwGdNG4bFqqdFrGTjO+6z715OQKB43pcurm+paIhiUbGhxkYG6WS7em2iX9pYeHEn+s2026ge8HJjwCPo75TnHuHqLjeFNnrNds7qP3vnejfI8+vv35u/MyZK38tOTqZewcYHhvi8PEDCKEsY9c3SqE1b4tSIFpGsVLHdT3/dCoVl92BLmy7oS1OdkUK/8MFHL6fz7iLCAOkWjyIpHij5LvB8ZsuamVCmHkTDE4RvkvwlcXtTosfUCc02Jtl8OAEDxxUZ7rXmy7XlwtcuVng4o0NLt2MnmVeLFZ47Y2zPHT/fpZXCmwUujDU2vKDbqU9jsVvFwt9K+VskafTIu+UVKJcbXJzpcTUaF+X5Qnm9k1xplyh6Tik3a70859fWDhx/8mTT3ftRm8n090GeQtYIDS0ayemTxLXS+AtoEDU4r5j+tKXvvlLnifz7Y6DTKJ02ubxpx7Csi2klCwub8QAyNdAC/NJ3VTrTWp1Jzx33g+3d7FjGPUhYsZ3xo9PChCeAfSBizFTYK/vDGD3/ZKD35eer5UMwDIEfteV1JoOtbpLvdGk1nCoNV1q9SbVhkO97lCtOyq84eC4Ho8/eJCxkX601DUC3IauMQ7Q3TxvGtfuaqTJp9ozoh9QZ5RN2+ybHmbf9DAf9cNWClUu3Vjn4vV1Llxb5/JigSvXVrhybem2pEb35FO1AbptAeVuC7vDtLhWpvcW9POWnWLvwTnee6tJf3kNS3asn58H/glwosum7mi62yB/HBiiFdD1kEri7HX8WygnN7fCBot/9a+e++FCofykOlRmMwG7/+SneeDx4/T06e1yBZqO05H71abjUSjXI0dXajL3Du9KkjFsl61xxEBU6+ijCySl/yzXmpSrDcq1JvWG4wO4D9L+td7wgbvp4DieEvVDgNZKLZA8M6Vtm77+XhqGUfOd+AJaeiETuHhzUSH8mwDg/bik7SEf0O3T6ECe0YE8jx6ZBsBxPRbXKrx9bpCzV9c4e2WVlY3QNWqCjI9bRr1bBmWV4vbxdqsStgnR23n/6bocIs0x9fPdLGLyvT2MT4+xcqVJfzX5KOo29AsLCye+ePLk09/pJtNOprsJ8v0oY7u4WN70T2+Gm8zVKWCJzufAyO/07NlrA2+8cb7r1dn45Ch7DswByoBmo1hJBO04SQnrxVrbeNvavZy81rGDDEQtm/34HNejWneo1pqUa00qdR/Qqw2KlQbVWhPXk8rIyTd8Un9e8KwN/W5VXz81MYS1C/rck+r0vA/ozlLKtpgZ62NmrI8f9cNWNiqcOrvIG+8tcur8IoVS+99vhMzx6C/Ybhvqdggn3UJdvN92SRaUfn7jls74mJgZY32tQNV1uvFvb6HOnn/05Mmnu5L171S6myD/FJCmvR6+nbj+LMrIrpu2RsbNM8/80S84jjvUjZjcsgQPP/kgwrLwXI/F5Q3FpXaga10v1XBcL7JNLpO2lf6wL9fFa+w8UsZ0MuDYm45LvekGovFaw6Fac6jWFag3HTcAbgXkXnjvybsyl81qd6g7nD4wOLx3NDrYwyce28cnHtuHlJJz19Z5/d0bvHbmJu9cXMJrdLUl645Ty77+tul2sIV9h1SuNri5WmJypC8Ws3mrhWUxv2+ad99pkHYbpFynbdoYHQf+R+BXb6nBO4zuFsjvB/bQmR7evC4CZ7iNXQBf/OLXHl1e3vgJE+CTxbrRsPsePsKA73hjcWUDx/XohBksVxuBHj6fSzPUl2OwL0sukw6c6gTnoe9CKpTr3FgpBYCuthv5nLfmwmUI4pru9gSSsm2mxofYMzvG/PTonZPRd0vGQjEwTvTb9QHI7wwSQnBwdpiDs8P89A/fR73h8Oa5RV45fZ2X37nOhWvrQdrNzOySQ7cApk1jN2v0rWbcHbS4WqYnl6E/351+vqe/l7GxIdYWXQYr61tnCOkfLCyceObkyacvdFXhDqS7AfIWiotvJ45vB/Bl4KWE9LR5bqFm07FeeOGdX+q2wUPDAxw6fhBQ1rjlcg2xhbGelMoZhgTmJgYY6s+RTtkGsIdabE9K7F2K8hvluvLcZ4rOY+gUB6uW5y1raf3U0osOBGno4vGfbctmekIB+8zUCBnbDgH1HkyAcR19u3VGYFD4Ppmkm66kUnexbUHKEqRtsSudP2nKZlI8dmyGx47N8N/+FKwUKrzy9nVefvsa33vzCqubqOa2pF0FztvVWL+cLovT+vmWEy23KGdqfpKNQomakyfX6Ph02RzwfwI/03kLdybdDZA/AgzSHsyhFchd4Luok+SirqbaUwsy/Pqvf/kztVrjsGVZCdx765QrpUQIi8d/6GFs26bRdFla2TDioxy4LqE3n6Evn1FnVlvKh73S3ctAViaNZ8+Tu3bSqzd8kZfe6B6xlk/4CFsA/lYZZGtQ5Nm2LKYnhpmfGWV2aoRUym4xCAyS689h3IvY/b1k+F1v5wgcbofqjke16SEb/jDx3yplKaPTtC1I2xaZ1O4E/9GBHj755EE++eRBpISzV1d56e1rvPT2NV4/c4Nas2Ox8K3RdlnYb5bhHujfw3TJqgjH9bh0I9TPJ5UXAkkYY9s2c/OTXDjrkHYa3ZxW9xcXFk584uTJp/+k0ww7ke4GyD9OCOadiulPo1zWxgXkW0KEvjl79lrve+9d/e8ikVtwnACHjx9gZEI5vbmxtIbnyXD7m1FDOm0z2JdloDdLyrZoNF3Fyfscu4fECjacCd+6POTkdyNJKSlVGsS7vR0Qy8iz3FqH2MH3kSjDqanxIeZnxpidHCaVskOu2Uh3O/ARfq0EbrxD24xbqXM3q3I0NZyk8SGpO1BresGzeldB2oZMygr+cimrI9XYTiAh4NDcCIfmRvhLn3yARtPl9fdu8tLbV3nxrWu8d2UlsuCM5E0Obklx5y3s7yBtc9XlWoOltbJyfNQF9Q8PMjhcoOx2bW3/zxcWTjx+8uTTO8soowu60yB/EBhlc649DvAl4A0jTcfAbj5/4QvPftZ1veH4nng1uYjIM6iw3r4e7n/0PgA2CmVqtYZhTS8RWPT3ZBjsz9GTDXXsjuvRdFx/a51ujgJ6gQYDLaISOK67Kx3ilKvNQEyvQdwEYjM8CCMB3I2vKoSgJ5dRnu9MC/vgir/Qgv6+PPPTY8z4wB7hzGO0vlHm6o1V7j8631ZEbl5bIm8RaLtdBOj0QkA+rfy572bypKTphKqUdvO7jvM8ieNKynUv4iHRtiCbssimLbIpi3zGIpe2d/wCKJO2eeK+GZ64b4a/9Rdgab3CN1+9wDdeusDr793sfIF/V3F5h22j24JurpYZ6M2S7fKo7tk9k7xTqFB3GmSbHatYHgE+C/zLLpu5Y0i021u8TfSXUcf5SRSId3L9Y+CSbh9KXG+jFiS28df2+dlnv7f3S1/65pdt20pbljoT2rbVn2VZ/rPAtm0sS/hxNj/yZxeY2TOD53lcuHxTYbIlyGVshvrzDPZlsS0LS4T76BFQrTmAxDKOqzWPrhUINXn7zylbMDLQtdvFe07Xl4tcXizgehIZ3/ZmbH9zje1v0W1x4bMQgpmpEfbOjpPNpA3degyAZZSrxojTYBoJRCV2XY/f/oOTTE4MsfD4UXKZdBAXFhC9FxhJ7uJz2oKe9O7n4AFKdZdSzVNLu2DRZ9wHbo1DQA/CAOlvmfRQB8WovKH9R0/Wpjdj05uz6culSO+iPYerhSrfeu0i33j5PK+evhHY8CStgcMbSUIS/3fRmjFpNlfFtEpX4jfJdcTDkuVx0ohvX4d5KzdvcwIumeX15jMcmBlGmk4mjDRxh126F9eW17ly/hqDlfVunOQsAYdPnny6K4f4O4XuJCe/B5gmBG/YWkx/iRDg45TEscfnfQnw7LPf+7sg0/Hzz/WEEj4rDl5KmN8/w/SeGRCwtFJASslgX46hGNcOEk8KJZYQ0Gg4wUE1IeeuxPNKdOHn8e+Ub2aJ43qtBiQ7nArlRoKLV7/bE3T0QUIjKJ1KMTs9wtzUaCBmbylzE+qUybYti8GBHq4vrvHs11/ho08cY2JssPOK7hLlUurv/UBSQiVpq1nSJJ/E9elxIAAPhAgXB6D8CBSrDhsVJzjzIJOy6cvZ9Ods+nMp+nJ2R86q7gXx8/ZzAAAgAElEQVSNDOT5qY8f46c+foyNcp0/fe0i33jlAi+9dZWmE/bb7equW9Oxfdvo7qmuPkxXrjZYLVYZHuhuS/Lw6BCrS2tU3CZ9tY69144D/zPwy11VtkPoTnLynwGOsjX3bt5/GeXVzqQ0XXDxTz/9u0+9+OLpL6RSJtducvEiuNfXdDrFT/7cTzAw1E+z2aRSKjPYlyVl24YbUnVj+T7rEYrrqDbUQTVCCJ+TJ8LNR7h7Qg6/L5+mt8vtIPeSag2HN3xxY9JWuXh4EO9vsctkUsxNjSrHNMIKsb8N5x5/Njn3uGGduQVNT2hCwsmXz3Du0k313YCHju9T4vtYvnvByVsW9Kahk4O2dgtVGh7FmmscWOR/O5/7Czj4GIcfqoBkNA2+ukY/e4rDD68yEPl7/tUC+vIpbOmQwmN+coCBHX4gVKna4Nvfv8Q3XrnAC6euUG+6xvhuz8mrSxLH2koyFpnEybfUk1ieTAxP4uSD8C04+ZZ6Sebk4+XZluDwnlFSKatjTh6gVqrw7umL9FWLpDs/e74J3H/y5NPvdpphp9Cd4iHSwGE6M7ID1fuXaAV4jPhErt18rtUa1quvvvcrQIQDUM+SJM4eJA88doyxsQFyaZuy1yDTn1NgoQvwOQMh1eRi+ShTqSsdtbakV9y6wAr4TRl5SXMtWmu49Obbd+BOoxurZX/CDSdv8z6up9fX3p4ss1MjjI0OtoJzB9Qxd5+QcHiwN0jgIXntzQssLm2w8KGjZDPpYI960gIirgXoxPgu/Oqbp0/b0Ju5ZbX/jqVKI3Q3LFHvG7HdiMy7MogLwxKuxkOcGzXzOk2XjWKFQrHMRqHK6nqRpuPiepKB3gzzE4PMTw2yb3qIuYmBHSVF68tn+PGPHOLHP3KIWt3h5BuX+eOXz3Py9cuR0y63pLuqx7/35HqSq0tF9k4PdpUv19fDyMgAa8seg24T0Rmjmwb+GYp53VV0pzj5+4D/is259vj194AbCWVpnfyWnPw/+Af/8q9cvrz0jzbj3M3wbDbN0WN7+NSf+yipTIpatU65WvN16MIQ0Wudur81TkCj6dJouoHOXVhKhB/o4i1adPRhvLoO9efIdWk8ci/IcT2+f+YGTszhTRIHr/X0/b15ZiZHGBrsjXDjnXLuQAQEkoAz4AIMEaLJyd9YWuf5P33dAFOVsyef4zM/9jjplK1AXt4d7l0IpXt/v4jnTSrXXUp1L8Kha+5cg7w2Ooty64QcuWzH2fv3flrH9SiVqxRLVTaKVTaKZSrVesSTYlTSFNqIuK5EWDAzNsC+6SEO7xnl2N5R8tlOd+rePao3XL735hW+8fJ5/vT7lyhVY1ynjHGstDDSRtLoikq2uYlkjy3CzMAWzrtNA+KLehm/k/FwXUzyiySVt3dqkIGebKSc5H4JK3OaTU6fOke6XqGn3sVJhPCpkyeffr6bDPea7tR08wCd74n3gJskA7xOlzSmIkzAmTNXcpcvL/6inlaTOXcVZtsW+/ZNsmfPJEeOH8BO23iuR6nsW1wKgi1w0ufcPanAXIsJqzXHEMlLn1sM75ECT6oJRUhlNe1pkT0Cx3G5ulhk/8wQ3ZyKdy9oca2M48lWzt28+vfDg33MTA7T25NrAe3NKBHwO8iT2HN+5qHBvoCLB6XjRYpg212n9dwO6TbaFvRn1PX9Rk1XUqoreWkwl8Y61ly4RSZpvQhIAhmi6S5dWWJto0y5UlPGna7EleH5Bi24YACIGe86HheurXH26irPv3AWJOydGeL4vnHuPzjOobmRHXG+RDZj8/FH9/LxR/fSdDxeevsqf/zyeb756kUKxvG5t0Vdc//bLC7YhuKuLhXonR/t6pul0mkmpka5fs0j49S7cXn7vwG7CuTvBCefBf4e4elxnVjVfxXlvjaJOrGst//23/5//tbNm2u/ErWg15y7srDPZNLs3TvB3r1TZDIp+gd6ue/howhLUCxWqNebod490K3H9elqK5nreop7b6d/FyKwwtdhjaZLtd6kWndwPQ9LWMxPDjA3MbDd32DbqN50OXX2Jo4b4+IlEU5eepKBgR5lKd+WI5Nt9a+evwBrDYMjB2bIZtMBYgZcPVHOPXg22v+7z32PUlUt3qSEvp4cn/nkE8oboZE2znkD4TnxRrxxieQJ8iXkyaWg730ongfVp6tlB8cAWr1pUhrfckuOXn97zO8fHQvFUpW3zlyi0Yydh2By6lLiuaaEyVMHIPlnJniuGmeuFy4QzLJczyObtjm2b4yHDk7y0OFJZsZ31u+z6bj8ySsX+INvneHFt69EFzdtFtVxTt5Paty0WWQllSc3sbDvmJM3nhLq6IaTB8lIf57Z8YGOOXkA6XmceescbqXKQLUrw/nPnDz59B92k+Fe0p3g5I8CGaJW9SbXHufwPeD8JuVJ4y8x7NVX3+u5cWPtb7Ya7KoftGXZ7Nkzyb59k2QyKYQAz/OY2z8LFjQaTSrVemBUJ5GE1r+K+9OK+HrDU2J6SwS6HO30RunktRMc5aa01mhSqTapNppIqQ6+sYTAslQ915aKjA/1dL3n826QBM5dWcVxtQGUyRXJyJ8nJavrpU2N8BIN8zY5fc7zJLPToyHAb9JQc4CZYDo81EexEu6JXXj8qBLT0zqgWvTsxrOOb4fUSVFCKHB/P4rnNRXrLo4X70kSuPno8qt1DvfjJZHvqYpST329OY4d3sOb71zE9RyC7XUYoCBlGG6oBIiUF4KFjI1ppKRSb/LS29d44c2ruFIy2p/nwcNTPHpkikcOTzJwjw+ZSqdsfuzDB/mxDx/k+nKRP/z2Gb7yp6e5ubqZ2HlnKOw7bUWnOwc0KUv7fFdqF2FZTM+Mc+H8NRqpDJnOjfD+l4WFE185efLpe9+hHdCdmH4O4hsQ05m4/hzKcrEdtQV3Hfb5z//nv+q63rBtR0X1lmUzPz/Bnj0T5HJpLEsoDlwIxqdG6R3oQ0rYKFQUtyCkckyCQHi+gR1CYbwn8YTauiFRz5YQeAKkkFjCUuJgoFCrU642qTUc35LaAPZgUlGif9fzOH1xheMHxneUMRDAtaUihUpD9acvq4+AO3pyNCbKxDCdXkbvg0WUDBZUZjmZTJrHHjwQbVQMTRN/ZQY4Dw/2cvHqMgCHD0wzNT4UKWIT3O6YkspIWTCY276jYz258xzllOseNX/LXFxtY/5QI+GxX3O7FXyQIQhTY6OvJ8fxo3t44+2LuL6INcR3aeSNlauHmUCdgxC0JRzPiSRhcb3C8987y1e/8x5SuuyfHeGRw1M8cd8sDx6aCBaN94Kmx/r5Gz/1OJ/9ycd44c0r/ME3T/PN1y5GtuR1RndwEXCXir6+XPRd3rb+UNo1oX94gL7lNaqe2w3IPwr8NGo32I6nOwHy++nupLl2YnqT4vNFEPb1r7/at7Ky8TeFUACpwN1ibm6cvXsnyGbTWJattnJ5HpYlSKdTzO2fQwLVSp1m0wEhEF50IvUQ/mpFcenVmqNOoxMCTwiwpB8vqDabFEp1SpUGUkhsodQEQij5siCcWDxASOXs1pPKVePpi8vct298x+jnS5UG15YKEUDX3LxpDBUF/XZg3g78E8KM+8ce2E/OdJRD689Xkmzprml4sA9QxnZPPHiwJW/XZNSl69YV6+d82hfPb8OnlECtCU1PGe3tlC13xZpLtRldlCUqxeMIDujeCwA5miO4BlmNQIni6O8/uofX377gA71sHVdGeyKLCxkfo9G6dX3xcnQeT8KZSyu8dX6JL371DbJpm4cOTykvd8dnOTAzfE+cGllC8JEH5vnIA/Osl2o8d/Jd/uBbpzl3td2GpV1MbRC7UmuyXqwx1N/dtqWZuUneLdWop7Nkmx3bOvzqwsKJ390N3Px2g3wfMEb0t2pe4wBfBS5sUWa7xb4E5DPPfPVveJ4ctG0F7jMzYwa4W7iuBFyktAJL+Zk9U2RzWaQnKRQruBJ/25tvPOdPQIHDff9HX64qN7eepeIcF8qVOhvlGvWG6+v+hZ9GzRKW31pP641lWKbwe8SzBIVKgzOXVzi6Z/SeO/MoVhqcubSSoCcPuW/N3bdw9DLGKcVnbcJrC/dncPazUyPMz4yFk66ZVbaK5ZNIAiPDfXhSsvDEUdJpO0FMHC4e4guGxOdN6hNCGdf1bJOhtiuh0gzHTKUJGRuyqXun35dAoepSd0zkDOPC7ymDMHWVkbgIuOrxpVOayB5w5/4X8Dnxvt48Dxzbx/ffOo/b0By9JCqu1+VGwyIN8+/jY1iPcWJt1e3VVG+6vPjWVV586yryP73IyECejz68hx9+bB+PHJ26JwZ8Q305fu5TD/Jzn3qQN88t8vvfPM3zL5ylErfOvxN0l1zbtqMbKyUGerMJp4a2tkk3M5vPMTzSz/qyS6bZQHS2/H8A+FngP9xei+88bbfh3YPAX6FzF7ZvAb/fQbmJxnZf/vK3Rp955o9esm2rPwruoStbIULDO8sSZDJpnvozHyKTSVMqVykUK4FhXNRxjdoWp8XthXKdpuNiCUGj6bFRrgUWrpYlsH1xvP6zg9Po/G18ll9HILq31FUb6VmqvpH+HAfnRu6Z6H69WOO9K6u+a1p8y2UZXqVhdGeGBQZ5nevj24WlUyk++fGHyWXTLSCvKQBmHWHK32PPL79xlscfOtgab9wnGdN18qzDbAuGc2oP/HZQ3YGaS/Ci5iLDEkpacLc9urqepFjzaLgaTI2FWoDJmqs2ueuYUZ7U9h1GuAHKXiRdmCZqtKnSlUpVXj11jmbTxZNesIXONcanNqgLxplrGOkFYZ5hjBc1MtUOnbSRn+OqstpJIfRDf2+Gjz2yl088to/Hj82QvodimGrd4WsvnuU/f/M033/3hm4i8ZsWNEj47QULr9akQXxC0a1hwUIuqY7E1rQpT0bCxod7mRiJHWBjttlgMvRDo97gzFvnydfL3RxHexrlIKfjY+3uBdn/+B//4+0s7yPAXuNZbHF9DbjWQblaxy/Mv1/91X/7y7lc+uPHj+9lfHwwEHVrjh3Uh1SLS3XIyf7DexmZGMGTHsurRSJWpyJaoaZG06VQrrNRrnN9pcTSWpmazz1YiNCC3p+NNSeuT6/TzRF+2qAGHzEMN/jUmy7LGxV6cum7vod+eaPC2SurkcNhosZ1RMLAAHY9ARtcfMDNBRN8eI0a8UXjHntwP6PD/e0b2oalbuG4/TTTk+pUQTMuIt5PAHHjEvk+Zhr9nLFhpGd7ROlSQrkJDWPaELEbCajTTCW2EHdcPCwllBsexZqnzrzX3yuezuTWCb9pPH1ksjUmWp0/sMEzJmOzLrOsdDpFKmX7rqgJ1GHSC8ekFyxIdJgMQMQLxncosfOMcesRVU+p58703fWmw7uXV3j+hbN8+RtvceHaOrYlmBrtw77Li/h0yuLInjE+87GjfPLJg+QyKS4vFqjVo1vHdoay8PaoWncY6s9vfYyxEW2nbJxmk2LNJefUOu2HMeDsZz/76e/fcmPvAm03J38C5bNesrULWwn8Bu33x0faSYyT/83f/P9mv/e9t18eHx/Om9vkAk7aP3wmDLfIZDN87FMfIZPNsLFRoliqGhw8Puce3frmeh7vXl5ltVABSciBB9y5wb0LgWX7nLrJ2WtOXRjPRhq1FY8W17jTo33MTw0aJ+HdGWo0XS7f3GB5oxoB9Ai4G1y7nkDNbXTBKXJxjj4W3nLaXIyTnxwbZuGJo0A4/0fAIb4gM1f0WiVCCORJLIcZtxnQd/KcT8NQdnv0744H5YYClSRpQSupl1MifHFHDPOqTUml4QZABzGuPXgOgTIeZ1rAm1w5xB3jmGAbB2h/DBocvuepiEqtwXdeeifYmeFqbl3GHOIYnLmUIbevt91FtuMF49Pzd5eE47Xpumb3x75G+NDCifqP+Vyapx6c50ce38eTD8zdM4dYjuvxJ69e4Defe51TZxeDBra+R4xkQjrzeTs4eUImIbkOM0y2hPX3ZtkzZXjCS+LkY21tNhqcefM82XqFfKOS0KJEOgscO3ny6Y432t9t2u7RNU5nLmwB6ignOJ1QnEGYWF0t/pOhof685ylf2bZt+YPCQh0lK1CHz1hIqY6x3H94D+l0BsdxKRQrPhjozRqhRb0rBfVGk3KtwfJ6haX1SgDOeBJhE0xGlo8YepWPB8JSun0kgStcpK/r98Cz1KQdpAewRGDgJ6Qq4/pKiZVCjakRJX7abhG+50mur5S4vlxUJ2JpbqUNwGs9fJyjD8WnBkdvxJkorfst/idRh9e0WNNvQe0mJNkSGctj/NC3wsZ4GglYEgZyysBuO6jahKo/TQi/ElPaECxcMNL4IQ0XGq4kbUHaFqSsW190SJRzm4YjqTummN2PD/rNFNfLWN+HHL0uM5DWxMLjdZvzukyKkK1pctkM2UyKSrXRKhnyZLRuGZYRtEWP1QAujPHud7yWUHV8VOwmVKk1+dqL5/jai2fJZVJ85MF5PvHYPn7oofm76nkvZVv86BMH+NEnDvDamRv8u2df40+/f5EtX7HN4vk2k24rFcpqh1NvvvP+TGcyjIwOsrIsyTZrnZ5SdxD4eeALt9jUO07byclngX8Kbbn2+PUC8K+7KN8GeoDj16+vHnz66d/9ghDkzINm2nHztm2Ry+f4kU9/lHQmzcpagVK5ZnDPoYi/UmtQrjaDH/mFG+tIqbh92yjb5NBbrm3ChBC+eFW5vdX3AZdvcvNWKN63BFi2xcRQD1Ojfbe9p75Wd1gtVllcK1NvuCGn5YXAnsjRGyJ8k9vRXFYip55wbeH6/etjDx5k7+x40E4NxknAfSuj1gRK89pOz96Oe7cEjOaVAdztkieh1FDW84mcu9hqEZLcExaQspUKQYv04+8ULrIUV9z0JE03BHYIARLah0W4dgPwQ8BN0smrRIoZNxeG/iRhcvLGmDMlAWb4W2cuc+3GSoseXevnFedu6Nq9ds5wJNJ3nOMa+T3Xww3KjZ2GkvQldF8kJJCxAP2cSdk8ef8cP/z4Pn7o4T303YMDrC5cX+ffP/d9njv5Lg3Hjfz+QpKJ4eGiTLaGJy3eYh3SUt5tcPISyGVSHJobQRvvJLYjVofTaHD6rfOk61V6O3d3+y6Km+923+Jdoe3k5MfozIWtTrPaZflTqENvsr/3e9/6pOM4OSEsbFsVryYIK7gqjt71OXw4eGw/djpFo+lQKFQQlgi4ZglU6iG4a9BdLVRxXLUfXopwYtIcugJGkJY/gQnfal4SKVt6BOeeu6jVCp6ywNdcm+oUg5v3iHhc8xyP6yslbqyUyGfT9PdmGejNMtCb2XKfbtPxqDcd1os1VotVajXHn4RlMNAjYtMOAT6YuNtx9BGOyJ/QZDIXPzk2xL7Z8RZAb/fcjkwAT4zQj8az1N9qE0DVyVMWjG2T/r3hKoD3pKFCiLVft0uHt21Y7IVdwHUk9SBRNE0I0rEJOZZUtgnXYS2TrgnwGABv3ptlxZ71uESG12AaT2qD/zc40MvV6yvB+AraZ9wH6aVRtxGm2XwdhjF22/VXV7QFW9tounzrtYt867WLpFMWj983y488vo+PP7qX/p67c5Levukh/qe//gk+919/iP/w/Cn+09fepFC5C1b5d4BqDYf1Uo2h/s6dF6UyaUbHhlhalOSaNWyvI5u6w8BPos5f2XG03SAv6Fxc3+kGzjxwCBgAZLlck2fPXv+M50ls2/9RSgvLUs+eFwK/ZSnPurlchrn9s0gJy6sFXCmx/D1Y1VqDSrXpny6nwF1KcD2P5fVq8EYKsEUw+XhSBBOzBn/hCbAk0p+VveAHrVBf76bRQO95AsvydYsC5Uwn0MnjW+MbOloVTaXepFJvsrhWVsBjW6RSFilfamFbFq7nUfcP0fH8hgQTF3rSU8CtQV4/t4B8G4A3Le/DcMOwKWKERxgfKHjVtSeX7UpMHwdlEwD8TxR5NjPKdii+RR0A+ZTi4LdD/15qKPF8RBzvf2dzPRKEE3tf3T6MgBbSFmxhWSaZeBW5J5ItAvTxe3OrXAv4GhlCYzcf7IMyZDQ+KC9Wrv8tpBe2Ldxyqc4pCETu/lgMQNsYs0ha0kgpkSJmQ6DTB+8S9tHdEEE3HY/vvHGZ77x+mV/799/mE4/u489/7CiPH5u5K/vwRwd7+MW/+GH+2p9/lN/7k3f44ldf58ZK6c5XvAV12/eLa+rY8G66bHxyhJXldaqZnm7OnP/7/ACAvNbH62+wlROcrTh5AUwDs356B5DPPPPVT9VqjVFlnWoZIKLAuRX4LQ4/cBgrZVOrNyiWagghqdQdyrWmOoPaF6sHk6iUrKxXcD0P5UXPP2zGB0FLCgV8/ttaqLo9JJZ/MI0Fhv5dhZlAH+zz9jl2aYWrIFWjshUIdPYa+PHd6Ypw8vekS8NxW/TGkQcRAnvIyUTBPcLREwX4cM98nNuPh4WAHufokYL+3hz9fXn6+/IM9Pcw2N9DOp1qBZVNns336gi0ZQicAXjDpnvi4zSYVR7sbpdcDwoNaLoh994R0Ov3MGa5dpOd+S5JieLMaCs4h6Ey/h2MsMjuCZ0vtvDQoN7SXj0WgzTR8iPfW0Kj6VCq1PxFpSHul14gEbJTttozH4zBsCwgGIs6XtLK9ZtjLXwnfwx7kTfYkrZrMdBoujz/wln+6IWzzIz18+c+eoRPLxxhMr5N7A5QTy7NX/7xB/nZH7uf5184x7999jXOXFq+4/VuFzWaLuulGsNduCK20ynGxodYvLmGa9mdcvMLCwsnnjp58unv3HJj7xDdS3H9yiZl9aKs9HMoxldjovf225d/1nW9AFCiHLwMqtbA39ObZ/7AvOLiVwqUqnUqNeVHXluxq8lTzYoSxZmuFqqh6M9fCER0iyI6MWixvSeVqN3ze8DywLPVwsBDA33ol1mL6PFAWv5iQoCwJMJHAOHPFgrkpWqzqS/uAujacfJ60m4Bexnl4jWYh/p1gjAT3C3Lor8vR19PCOp9vXm1zTEG2OYE38Fr4HdXRK8exMuY3j1ecBezrh5NYz3b4+Cm4cJGPWxT0sIiAHpawzHj2EQtQdhPkeD4s76X8fsQlEPgjYF+BMTDUuOgrcda8M4yXGQG5cSkAWa+puNy6coiV66v4DhuYAga2oMYx8v6BrYmF29un0OG1vr6ZQKOPcb1h3GhqkD6v83+/l5luNsyuAwyPk4mk2agvyd4x7XVAm6XCwZN15aL/Mvfe5kv/P4rfPj+WT7z0aN89JG9d3wPvm1b/MRTh/iJpw7xwptXeOYrr/HdU1fuaJ23Q+ZvY2m1zFBvbivjlgiNTYywsrRBLZ3rRjf/94G/0FVD7wJtJ8j30Z24fj2hDAFMoKQCAgXwOlx8/vO/v1CpVA9qYzvpg4kGe12FFuV7nuLihW1RKNc5e3VV5RH+ufD4InefRdJW9ovrFeW+NuJrHqQQPghqblCJ5iXagl5ZXXsILE8q0Cd8ewsN9DI480ZodYCfTuvhtU5e95gwxPXCCIOQ+2tLJqgmgTwheKsFSxtgT7jXE206nWJybIi+3hx9vXny2mgoAdCJPyeAkWzzvOX7Ecu7SXgcZOPPaQvGe9UWtdulShOKdYLvFAf1uD4+wrnH0sffJ/6iiX2V0Icyod/NexlL18LpGh2pRfDE42X4VvFvGqQz0/qJHNfjyvUVLl9dpuk4xnGyhmSI+EIzbKPJxQdpMIBegzvRbXoY+QFS6RRjg70MDvQxONjP0GAfqZTF+Ys3+P4bZ5N6uoWaTYc985NMjA8jpWSjUOa1779LtaYsJm6F4/ek5LunrvDdN64w2Jflx586xE9+7Jjvu/3O0ofvn+PD989x5tIK//bZ1/ij753FdXekzRkADcdlzT/ARtNm/S0BK51idGyQpUWPfKPaqaX9Ty0snDh08uTT791mk7eVttMZzgLKOM6EG7HJ9b8Q7WsbmAEGCS3w9Z8HyN/4jef+YbPpzIO/MvfF5OG0KCPh2WyKxz72OC6C85cXqVQavmOa0IENPjcvfCW440quLRUAIw0E+Szdei1D9y+Boxuhlw5hPpPTDsrT8TrRJqMuMgFHADuc2IK4pD8zTgO66q2oeN4zQRxD/L45wEvgofv2Mj2hzpFPa7dvSeCzCYDHAT+xHwxqt64JR8PWIvlAFRJ7zqdgqu/2DeykhEJdObgxy4+3v5tw3Q9CbtJdUoIQwdiIRsmWpEG5sTJbgD1ogwzyxIFaGoVE8kgzjVGuUaErJVdvrPD2mcusrJXwAqmdMQaD7WzEQDv6F3eCE4K9Gute0KYQ9Pv68kyMD7NvzxT3HdnD8aN7mZ0ZZ2Son56enG/nE9oArKwW2n2BCC0urjE6Ouhv98swPT1GsVihWr39c+FrDYc3zy3y5T9+i++8cRkpYX5ygMx2uV9sQ6ODPfyZJw7wmY8pvxanLy3fsoTiTlOt7jIy2NOVbj6XzbC8vI6QkrS72RlqAQkg89nPfvort9bKO0PbuYXul4FjbL51Tt83gV8x8maASf8aImR4L/71v/4vR59//uXfM53b2HZ0m5wOVz7rXQ49eJiP/MiTNB2HU29fDNzKBtvS9BY2IbAsEMLixnKR9VItiLPt6PY3Vb7hnjbJ0U1wzrze/iYidbVulzMd8BhX/L35mrs3FyW6g4JFw+akJzuBMSHTKq6XMtwL3ALsXjLg752b4MD8RCsHvQmgJ4XdynMiiBsdEn/WK4AkINVhgzkYyXcl3UskT8JaFfyD2loWEmbYluHxdwgoBth+shYgjyRtY60e9G3M6AwCJNbfPYjToBnEG0CqAdQoOzCAIza+PMn1xTUuXVmiVm+GYnjpi+F9PbzeDmeK7ONhgUtbc5uclMHZ8lJK5fzGH79z0+NM/v/kvXmYZcdVJ/iL+97LfV8rKysra5dKtahKW1kpy5LlXQbcLLbBGNp8ghlmBk1jGs/3uTENA/TXbA00bqa7BwRt2hgwIHtst7g48HUAACAASURBVBcMlmXZ8iJr3+UqVZVqy8rMyv1lvu3emD9iOxE37nv3LVkSdNSX9e6NOHEibty48TvnRMSJ8SH093Yhk82Kef4Imr+YIois70bV+7Gnvofz5+c9bwL6u1KhLZfDiZsPorOzQwoaEb536jzOnL3kFXC5/Z9foFNClV0sOtqyeNPNAoCPHdjmy9nycHlxHf/57x7B5x56UY8hsopW5bl75fZDlZqATXF+fgZWORzYNtKLYanNmz4fK9Vic/70RawsrmJgY0kfLV4jbALY+fDDH33NLFxopSZ/FwB6zl+13wKAr8j7TgjzfAYJGjwA/kd/9OkPFQqlg+rFK+3YaLHSdM4jLC2tYXl5HW//4bego7MdFy5dwcZGQWvYWpun2jYYypUQ5+bXdCUVqFIalddFCuZ5Shd+GRnAmTWMEyDy9CPdt6xB2KCdpc2rP27+lOnSWlMABdImXmk+sT3KSAb43u5OXLd/h364agAfe95agO6hdwMFRkXCAK/3O3rv094ZA8a6gYH6pu+8oRwCCxvCi50PvNMCujcN5LngRCbdcvvduJd0aDN9iYA9d+ntfqlobHp7OPeutucclxdW8PxL5zB3ZQXlSuho3vZaj5hWr/p4BJ1G88OnxWuewi3uiRsPai2dfiPqOSKr8ewwNjaE5eV1bGwU/AQkhFGE+YVlTIwPI5PJgDFgaKgfPd1duHJlRe+CSQr19MlKGOF7567gc19/CV/61ikUSxVMjvWhq2PrnO30dLbhzht3466b92D2yjpemV3ZsrIaCZvFCob6O+s6AKytLYvFK6sIeIRslMqpXQ7Axj333P1go/VsdWglyL8NYpubb1xyf/MAHoSYx1e+B2PAru7vv/+hsUceefG3OEdgvK/JTESDKBSKmJ9fQaFQwtGbD+HQjdehUglx+uxljctKE1ZgT68vXcmjUKwIALa0ZuJz3iRZ8TqZEQCnGrgTNKibO3NJRm93YI4NumqwhA3stAGtwQ0G9C1Q1/ltEye4R6OX94wxHDu0W6yOd+rqA+u0gE4zeMHe4UFBHEgAcffX0eYzDJjoAbpb4H9kowwsbtp1TwR6T12S6BODbIQY4DPxTnV3cvuUIqXxPPn9UGDXeE37jo9epSk62NePPX0Ks3NLAtwjG8CV62S9aM4x06v5+UhGmHymPnT1PO27nHNE4NgzvQ0jw/3mmblcR0MFiCrYyxjDtrEhzC0so1isbdKtlEMsLq1hYtuIPmujp7sTo6MDuLK4Ko69bnFYzRfxyHMX8Fdffganzi9iYqQXo4NbtzJ/qK8T75jZj5uum8Tpi0uYW0q9cG1LQ8TFbqvUgg4HstksNvIb2KyIffMpw+H77vvCf7rnnrtfE65uWwnyd0OANpA8vqrrFYjDaTqRAOz0/rd/+6//183N4owC9CjiElQF1IZhhOXlPNbWNsC5MKW/8z1vQ09fNy7OLmJ9Xbwcc4iM0rGNNl+pcJyfW5V0Cpxd8I5r7LoeyoSuQT5hhHYHY1kvryXIiYtp9I7G4f+TQB45mo81OMe1HO98PGzN6tq9k/K8dj/AwxOXCOwuTZV7N7jafJpAgT4bAJN9QEeTy1A5gNWCmINXhcQk3ipAb93XSONA1Tn5Wt3JC/qqD1A6+h64/esCPOeEh/NLS6bAf/LMrJwKcnwwEGEzpsnTnR16i6fk7dBZWr3iB4j99mA4fnQfgiBj+reusxxruGsMj4cgCLBtfBAXLy2iUqm93apQLGNtPY9t40NQbzXXlsXEthHkNzZTWQUaCRHnOH1hCf/fgy/gsRcuYrCvE1Nj/Vu27377SC9+8M7rsG/HEF48u4CV9ebXHzQbCqUKhge66sjBkM0FWFpaRyaK0m6n6wLw/D333P10Y7VsbWjlvgt1Sly13wDCLN8ufyvyLyTX1v3CwgpfXFx7b6USIgwjhGEkDo2oRKhUQqytbWJubgkbGwW9EGz3/p0Y3T6GMIxweWHZ1nKhFucZaZ4DmF/Ji3k336CiBwpozcI2GxKPcHQekcPaahaRgUfniegWIBMXOwrT86fmLMUxmp4/58hM6kK26tGvbh6nvlHEMTrUj4kxsZI3CeATwZmnoKkS3DxqB0MjQkIuA+zoa34FfcSFeX6tVLs9yI8BVRdw3TQCwBRMKbGVrqItsOVaoLPK4zYdTbMB3iy05E66yuGNV/zJn6mfA9qcaNwRt+9VXwTINwr7e6LCAW0rTgVdDhYwHLp2Gu1tOVJJ+3l5/AETQ3t7G07cdFBbtWqFhYUVPPf8GSsum83g6JF9GBrs8+ap5xupFR578RI++PtfxPt++W/xP77+EipbuDr+zbfsxf2/9aP48Adux3B/Z+0MWxgqYYTl9fqEqJ7eHnR0tKHQVpejjJ+uq5AtDK2ek+9HdXO9AvkyhLm+qgYPIPr5n/+jNy8vr/8kQMEVCEPhnlatTjUL1hjufvdb0T8ygNnLi1hZzQMw58RrQ7tWyxkQAa/MrsiPSJ77bmz72uSu9XO1Op88pblW5ntnxtQrLbOqyXQwdDUkNXApOvc6igyN0cgJjaO1UDo9SOp7e7Bty+Vw7NBuZIKgKkDVAn3uZqpCkwawvY3ITD66JgIQe98n+5pfQV8Ogfm8WGCXpInX0uhdejfNveewZnVSxfveEwVmkx4HPKg+IumoMGH1IcWHc2P2hv2n83DgzLl52zxvLaJzhACdjwjg4BZ4u2DOQTV58Ts6PIBbbrwGw0P91jdlKwKinnQBWa3Q1pbD2MgA8vlNbKRYNb+2lgdjwOCADeqbmwUsL18d73JLa5t48LEz+MxDL4Jzjn1TwzXdZDcSgoDh0J4x/MibDiGXzeC50/Mop7B6bEUolUMM9fmFDf/bFoupl1c3kQ0ryKTbTrfrvvu+8Il77rm7XvftLQ+t1ORLiGvt9E8dE8sgFidU1eDV/YULV94vNPhQa/D5fAGLi+solSrQ2oX8G5sYwdS+Kb1a13z8DpBFSlLnWFjZQDkMDV3EHe2BauTiL6R7yUka1Zhp2VFkD2Tqz83j1abJ6uC4Fm+vKg4juiqZavoenkmaO7FGxOobAYcOTCGXzTQP8M51GiHADVYe99flQXj1tAGTvWIuvpmwWQZm8+KAGe8zIyGeV6d3qqvvLaBUvw69BmkCsBRsNS9CkxhHARC1/6BA0uJpKmgJpCDA7AA8BXNqmjdrRWS8dp3ss7xRyxmQa8vhhusP4OYbr0VHRztpf25d03Gi3tDb24UTN1+HW285hOHh/pr0p16+gAsX56247u6rr+3OL+Xxh3/9LXzfBz+O//Q338b88tbMo3d15PCzP3QTPvd7P473vPlwy0/WTBOKpQrW6/TH3zfYh1w2i0JbXe/mZ+oqZItCK1u4jNrmesjrNqQw1X/4w38yViyW3xKGwlRfLlewvr6JQkG8IHVaFB0UTtx5CxhjmJtfRrlUsbUQOQDJGD0gzC2ua+3XMsE7Awd1EkMFABfoVd7QB54xAE4AWQXcodj2E4Zx8K4J2hTwXaGgmrme+838nHNMTY5gcKAnGThIPEi8GwcnrtpwWlUIqJXOpTmf3A90ANt70fRc5HIBmNtwQLYeoCfxvjZ002iggEz5JYI0zevw9vFxhQRO85FCqBAgsnESJwiMkKAh1Vw7/csFeFWUdT6CnhqD9b0Bccc2kazLzh2juPO26zExPqRX0sX6sPWOas/FVwuDg704cdNBAfZD1cH+lfP2ids9PfXMGbc2rG+W8LH/8QTe9YufwK/f9yBOX0x7xEh9YaivE//mA2/Ap377x/CWE3u3pIxqYWEl9XnxAMR26+HRfpQzOYRBakvHv5yZuffqnR2cEFrp8a6I2r7rmfxtg+PNzrlmAPDooy+9LwzDDGMMYRjGVp6awUkMDp2d7dh/ZB84gIuzV8hAIpZecwCMK5eyosil9QIKpYp2NRtF4pjXiEOe/y5OixOVY4gicWB8AI5I+qLXp8epeWF5NrxoBCbd4ooBSE8bBAAj99QDHvVkRz3dQd3LlrI84gGI2Xe555IMxroNtSZDzZxqABW/g/09mNw2hPHRAcLDz78WwKcGfc99LDjP6Ns3T9tltFvsgW8mcA7MbwgtHoDXLa3vLHh44qHq6OHjplkVEKmG1m0HGsVtGioAxFiSawu83X5D/kBoOCnLBUz9vZL+pfax26Duu6Zz9qqPOoJ1pAQPk9bT3YnD101joL/XzOFTQUT3fdr/q6+oTxVk/sGBXtxy00EsLq3i5KnzuHIl7jxn+8SIdd/V2Q4WsLp85be3t2FiYhSZTICTJ881VXVAHJDzmYdewGcfegGvPzaNn3jH9Th+zUTTfN0wNd6P3/k/34ZnTs3hP/zFN/DYi5dawrdWy+U3SygUK+ioYzHOwFA/Ls8uopjrQFc6V7djAN4F4G9TF7IFoZUgX0Z63/WAMNnTiSsL6B944IlMoVD6ACCl+Mg/DyK0eXEYzdFbjqCtox3Ly3kUiiXpupbI5A7AAxyzV9aFow1wIJAHy6gPXe5BCvSxsFweRsNFGrh2RauBncs5QQLaZiW+cJ3LZEswACwgkpECcbMcQAI88WNPAB/gse151H+AO4dr2swefs1ArgY/8ZvNZDAxPogd24bRpY66TAL4KvH1xiUKAUim8d7TujFgolccNNNMqETA5byYhweSwdsH9CDxOq+pnubjpql8NEHkIe8OHPJII3FP3wdXFIap3R9sWsPDALqiozSc0njeKRUQjABg1gBwILZtrhrAxxbgaRpY9MoRDmMMt958ENlcxgjgrpkDJF4/awJE1An8lHxwoBeHDu7G177xpJUQBAEmtg1b+Rhj6O7qxNp6DW2TCw99k5NjGB4ZgHLMXSiUcN6xDjQaOAe+9vhZfO3xsziydxw/+c7rcccNu/TY2qpweO8Y/vSXfxB/95Vn8Xt/+U3kN7f+eNuFlQ3sGO31J3redSaXRV9/N1aXInQWN8jXVjX8DF5lkG+luX4D6VbXqz/XDZP67iMA4S/90n13hmE0XamEiQAPmI8b4Lj+xGEAIHPxgq0ZYIzXKg6O9c0S1jZKsUHFMh9qE6EziDgDj7VKPrJX1tvz3NQsbh+24ZsDd83pYRSZOXYPbbU5+TCSK+65n7f67evpwnUHpnD7iYM4sGe7AHj5dtRLoi9sKwDe7RiJAO7pRJpW/gYMmOprHuALFeDCmjhoJlaHKs/li3frb7VlQj7Td2Wcw0dopVRL9QC1Q2/RSQ3YC9AeHhbAy7+IXOs02HXW36zOEwd4d21LfArNXl1P4wFRj+3bhpDNZazC3Xa020pc1FKgeY30pHDq9MVYpx0bHbRX5Mv0np505qajR/djdHTQEvanpycwNORfod9MePrUZXzoD/8e7/nwX+OBR0+3nD9jwI+86RA+9ds/ijcc3+WlabDpvWE1X6h78d/gUB84YyhlUzvUeMvMzL276q1bK0MrQX4W6bbRqd/tNfj9L2kXvnDOMTE1jvEd21AqVbC0vKZNduYjpqZoETm7sO7spYWlKdCFOzHgp2Du0TYUmKptQNq9Jr13hAN6mpa70C4+lx9p0K+2cC90QJ26+aRpQcAwuW0Yr7vhAG6+fh8mxgaFn+4qoKMukoC/IYB3rn35XGIvH3mfDYDpgead3KyXgEvr4qhYrxDiqWc1QK8F5rXaPRE8KR/5/ahtooaGfBukfhToOOy6uKAN916LB/b3BpKffs6Kd5gA8GZ+nlsAb/zWc48VwN76unPHGHkW7vQTe+2A9mXfUhgxYXOziIuX4p5OJydHY3EcQHdX7e1a2VwWmYzf3Hxg/zS6UvBoJJy5tIwP/ccv4ad+7VN44qXZlvMfH+rBR3/xbvzm//EWDPRuzTMAor9cWdmsK093bzfacjkUc6nrxfAqb6drJcifh9HSk1bYU6BPBPmZmXvHAPxA2oI5B255w80AY7g8tyQGBxiNQZuhiQZeKFewtLYRW1wXcTLnR/xX01X2VCjQQEwGJt8COb04L8XCt9i+ec714rso4ohCm4fwHeCUSQQBDfZu3pCjq6MdB/ftwO0nrsM1eyfR3d1RNwjRNHXRCOj78nqSEwHdqiMXe993DTTv5Ga5IEz0FBDTPDul9daRZHSftRrYJ9XDbRN/+TwO2i5/Gk9BkpPvitAo64EBcx6noXxBaN0+H9HFtLambrlW1t+c+52LtJ6eLgwM9ILD8XQnH5q+L/rg6XZHxd9VrXDq9MXYHHtXZ0finvg0mnxnhyu5Gv6ZTIBrr92N7BZsh1PhqZOXcc9vfBq/8AdfxMsXWr9A7+7bDuAzv/vjeMfM/pbzVmF5raAXbVZ7jzqNMQwO96GSydazAO+nZmbu3drTgqqEVoL8BcTBvZq5frIKr5+AmLNPFdrbc7jm+mvAObdM9XSxjpbcOcB5hLnFPMLI0CgNmq6i1w5tXKAPPYNQpA7KUPcmngoCljbtA3oH9EPKw8lHtfgwihCFRPMPHSuAaw2Q10cO7sL2bUOW1p4W3L2g7QPpFACfGvRTCgGdOWD3QPNObhY2xF/qZ0iIp/TVhICaYO8BZKtNKI3Ll74fB3y9vFW6C9qw/2znFuQJODX/UyHBniZwhWzXsibm2MV16AI8EaDNLhdgemrMEmRM+3Fz+pysr3kuu/Xr0uqrkBYKJVx0tskBwOT2uBavQk937RPTOjo980+kHh0dbThwYNeWebRT4cHHzuC9v/RJ/Np9X225C9uB3g781s+9FR/90DsxNtR6V7xhFGE1X58nvv7BXjCGerT57QDeUW/dWhVaba6vIL25vhrI/2g9BR87cRQdXZ1YXF5HoVgygwbUlhquP2IOAcDzS3miuasBg5j7IjtNmc9j2+YSwNNrIifaim3291yHNXi7+aVAoObsQ1UuT/Cox8WK+e7O+Hx7veBeL+A1A/pphIDedqHBN7MFl3Ngdl1o8XXXm8R7Ad0TnxrsCQMLzEEAnNBb4A/PHzegq0AQFJBjtDDppDBO/vRhMQ7/GKhWAXgjLEee79I28dsCt3C8smNiGMr64LaHqWvkWCrgD55+WE94+fRFrS2qwBjD9u0jCTmE4rJ//1RVvh0dyYtMVGn9/T3YtavaUNtA8LRHFHF8+sEX8K4P/SX+8K+/hbWN1rqwveP4Lnz6d96Hd7/pUMuFlqXV+kz2ufYcenq6UcrWtcjnvXUV0sLQSpDnAC4h/bz8NnhW98/M3LsHwE31FHz8tmMAgNnLi9Bz8YgPNGoQWJbb5qiJ3QwizoBCBhV7gRxdDATv3L4BYHuuMMlMb2nuZAALCeDr/fLUzOkB/KoCiIwXA2ENUCGJieCeAH6tBHgKkEk0ADDYCUz1i8V2jYaQiwV2lotaUnBSXV2ATWq3JDBPAnurDtymoyCus9L85P343pcFfO41N3xNWeabAvy84PAxdJwIBFIIJ/04tvAuis/Rxxfp2QIC5xzbtw0jk81qV8eclOk2uG6r1CheH9wXimWcuzBnxWUzAaZ3jhOXuv7yd0yOYefO5GNi4+Z6X+AY3zaM4eGBVPVtRpgBhKOZ//a5x/H9v/AX+PPPP4FSuXVe7Xo62/Bv77kT9/3Sv8DObbUdDaUNG4UyinR7tu+jd66HRvoQ1bcA710zM/du3QKDKqHV7obovHzSfLz6yyG+wh4A3lNPgdunxjExvR3FUhmLS+tEm4jMtTMwXF5cdwYNZ4GPD5TpIrtI8KcAa2vryjxPVglzboOyxwQfcluDpyvlXTC3gN6n9XvoaDnZbBZjIwNekKkFNJxcJAI/ofOBEpqIcwFf3Y51SSc3ib2ldqhEwPkVcZKcC9LVnjsNoNP4pDyxdG7HxdqXy+2fcMDd5e+CuwZaExkTCFSZDjAbIcBYySKYBDuPLRBE3OVLgJ18P17HTx6AtzxPyvoJU70jjMCpG7h1bdo+Hczx2IU/vHzmIqIoQm9vF3bv2o6bb7oWd915Iw7s35mqnL17JjE+PuxN6+hoT13f3Xsm0d7WgiMWU4bVfBF/8JffxLs+9Al85msvoBJGZExtjvfN103i/t/6UXzg+47rk/waCqQeS6v1+rPvRi6brcdk3wvg7XUV0qKwFSBfa/scjffZkeoya9z4+hvA5II71eE5GQzsa+G3eHFlk0j+0PN8XAKgteiOaAp0DlwPNpEpwxYKHM05JJp+NbDnCtwjOz/3x5k5eckzJH/OFAEF/antw9pjSgxUnIFeBQtkkoDOF4+E+EYB3rnmAMa7haObZkKpAryyDBRDu2y3LHWRGugJfQzsU7Y3bXM6SKpL99AHcDVdZfMyQM5jZVMBAET4ddMofwvwLRBV5Xo0f8Iwvrre1soj67uM0xmAF0J4T08XBvodj4zkGdSzq33zrTHTJ0Pt4EAP7nrDDbjtdUdwYP8UBgf7Up1nTvkdvHYag54Fep10Tr5GXTOZDPbtm2rezWOt4DTm7JV1/MofP4Af+8jf4KHHz+q1RO5i4XqBv70ti3/94zP4xK//CPZP+YWgesLyegE8bSU4AMYwMNSLciaHiKWG0VfFZN9qkD+H+rbR7aCZZ2buPQDgWNrCgiDA/kN7wTnH7Ly9upNK9maxDcflK3mPmR0E3M1cttDYeYxea+4EYG1gB8xJcDa403vfKvr4Arso5vrW3nKXvM3OaEE2DQfHlJwP1JqcZ7AHnLgEcK8KcA7wW7w8tLXi4MRxANu6gZEmPYFulIGzK8IHfS3wrvUMvvikNF++RLCHC1bQ70TXUV7TnX5aY6eAS9+34gEC1E6aEQ5IXlIBDipocMNTAzEBesKbCr2u22jbzbQH4F0Tf8SxSy2449LCQYURbvp7Un/2B07+rx0o3bbxYbS3+9YQc6tvVQ8Mhw/tsVbcBwFDW1vS2mQ/097ebkxuH0tTYMvDyXOL+Pnf+wJ+9t9/Fs+cmkMl4qiEkbZCNgr8h/aM4W/+/Xvxc+8+keAHP91bC8MIawkL8PwcOPoGxDHbdWjz3z8zc+9VP5ig1SD/Cmqb6+nvDid/Xab6nbu3o39kCEvLaygWy7F0AdrKAY4YGGYX1/V8O12ty7nRCqyzrQnQR5Q+NiB5tGyPmdwCW2eQs8zrUjuJ87KPkE30Z++UqxblRZxjbKgf7e05e8B22w5mIKwJ7g4PK82JVxe1wDxt3EQPMNwkwK+WgHOrYi6+KngjuT5Jzxijr5Lma0+rTZ0IS2N2QNTiQ94hHH4W8PJ4PK23j4aCudH+CdjrvFRA4Rpwr903hYMHduDg/ilcs38K1+7fgY72tjoBXvTz8dFBTG4fta0LtM4xgE8L255QM28q6cHJkZwhk8ng6OF96GgXJveO9tqLvnzcJneModvrG7+JtqijIo++cBE/9eufwm//+dextlHU4B5GvDrwk+kaN2QzAX72h27Gf/uVH8TYoN+cl+bpltbqM9l3dLajvT2HYi71ArxuAO+sq5AWhK0w1yu+1bbPqT8X5OsyZxy95QgAYHbOv0fTDEKicyytFlAoVhxwd8AxBvTGe50CdArWoZPPPhAGcfDV+T0auAPoYchtIYBTYYAAvbtdTgkkyjueU8b01Jge6NR2InlbFbyRMl1dxMCP5IET30jctp7m/dAvbgIXVmwvZ40APY3nNejTtK8BTodXrF3jQAqLB7fK5NzR8uGY5SWh/e1w4VCHArzkEZsmgCmIA5ZvClOO/MeBXVNjmN4xjl07x7B75zh279yGW244gGw2sL8F55cC/PjoIO687ShO3HQtMlKbcwHeaPRGwEga+NNif1pY5J6rRkJbWw5HjuxDNptFR2dbepbqnUKs6t+3dwrBq3D6m64OB/7ugefwvl/+W3zjyVdQ0WNYZHYHheJPA78WBpKB/9iBCfzNb74XN15by8+aP+Q3S/ZCwVjbxhu7v78HEQtQyaTe8X3VTfatftNliK109ZjrMwAwM3PvQQCH0xaUyQTYf3gfKmGIpeU81MDkBgrYs4vrsnMozQDWsbLaPK61eAXUHnez7h/VKhzQpcBPV+xbK+kJoLu/3qNinT/1cVTIB+KrZ1dnO0aG+hwtzszPKiBoBNx9QF4L8BoF+Ine5gH+cl5sk1OFpK0H+bHjk+gbaE+XxgJwQq/BWF9zraHyhD+VwQJs1R8Iv4iUoeOptm7FQwrD9r0plwgJ5HhY65m4KLOrqwO3HL8GjDFt7fJp9RTc+/u7zbtRfLkU3ijgc1MX09DVdGj6MmuQUGm5Ch9ei8ZUK0bY1dWBw4f2oLvLdP4UrKzQ3tGGXdMpgJCn510/nVgA/a//4xfxa3/8AJbWNo0GL8E9jDgBdjstCfgHezvxJx95F37iHdc3VOvlOrX5Xmmyr2OV/TtnZu5t/Yb/KqGVB9SocB5iQR0d5wH/qXTtAK4D8DTqlHCm9+5A7/AA5uaXwambKo7Y8mrOOaIKx5XlDXHiGweiiAGBqEoQcUTyoJiIi+hQij9BJE+ZC7g+slQ/BIc+qIZFIIfSiENsApgDb8DEYTSqjuIgGq4XvzEGMHVojTqkhpt1MvqXXAPxA2pgpdNGED87d4yKJpJ14rKMpI+UOxc+uqrgJy9Sx9eIYxAAP9DEZhTOxRa51aIpI3ZYjLrm8B4+A3jiVV3Ju9L1Vu0Nu3ta6UDsgBpAHt9I+MbaiLwA77vg8WsKcq4lx4rTaXR4tj9uJTjE4h0BwgZ9w9/CRnk/0N+NG6/fi289+lIM4LeND+LA3h3o7+0ii+9UWT4NXky7cUBbJLwhNfCmoEtHhhTihc2PA319PYke8dIKEMMjg8i15VAqlVGpiBM+za+4rpQrqIQhoijt0zQQOPClb53Et589j5//sVvxxhv3yPGLyxM31QchT+yEnaZooQ7/kuS/+P7bcGjvGP7vP34Am56p3KSwul5INPn7QntHG9rbcyjxKO3JdJ0Avh/AX6UupMmwFSB/FsCtiIM64Af+m9EAyB+5WZjqFzxHN/rCwnIeYRQJUGSy1sNGxQAAIABJREFUYgToBbqrwZZp4AdknDxtjjPoE+IACdQBi4F5EAER4wIIFG0I8ADkdLkIXHZcevpcQMGd1EGBMaPfHPOcROd8kwpYMpkMpidHzUAv49XApU+wczO7cXDiHJpqgkFVgSAFwG/vBfqbAPiIiwV2GyXBMAbeDhAnAr2qmxOPGnwAP5Angb1K1EBvRdvAC1JuHOh57P3Yc9S2WV9GWe/AEgY81gAF0CaPsRDFgJ3+6rzElM7F4S2HD+7CE0+dAhgwMT6Ea/ZNorenSwB25My5y2eIuF0XC/BJ5zSCDMfGZgkbGwXkNwvY2ChiQ/5mshmMDvdjdHgAXd3xjpcWVFMJB6SetQhZmhXdvHrV+vq6rbZTRet+odozjFCuVFAqlTF7aR7rq631ageI+fBf+X8fwJevP4UPvm8GI/1y3QDj5nhSeSy3BfwE6Cnwc3C89cQ+7J0cwi/8/hfwyuWVFLXgKFVCbBbL6JSLJf1CmDUAo3+gB3OXy6hkcsiGqQSK9+IqgjxravGJP7wRwK8AUnCu/Ts7M3PvHwJ4Jm0BmUyAf/XrP4eu3h585/EXyUfBzMBJBkkG4LnT81haLSAImPljDIwxBAEQsAABA4IMA5PXGcbAFF3AtJauaFkgOpTmozohY/IIWSY7JqUjnZUx3SkZBD8A+hhHI0gwDeSmw5Pnq6HNcy7y7Zoaw01H90KhG20rBmiBg/Zht3fUEgAowHvzNgHwk31AXxMnyVUi4MyyOE1O8bTagcSRn+q01egbSIvRgAw09IdbKRaIy1tQINO0FoBzizYOxiaPbVrnNnBqHobOMotH5jqShUSA9uWufVXIOJ2fi+my0+cuY6CvG709XQDEdFtEaONb7yAPnBG0xXIFG/kC1jY2sZ4X4J3fKGBjo4CNzSI2N4tyTUZ8LKTDY2dnG0aGBzA60o+RoX60teekEGPm+622pcIHeR92u5E8ur096yNUPG1XJORx3kFc4IrXx9TDx9vk4eBYXlzFhfOXUakYBzK0H1rt5zQrd68840FPZxt+9odvxt3SZ73R4CE0dkbHQRf4lbXTjKH5zTI+8l/+EV97/IxVt7gwJe6H+7swPtSjnztWe5KPc6BUKOLkS+fQXi6gO502XwQw8vDDH12vSdmCsBWa/HchWsPV3n3m+gDAtkOHdr3v2WfPpC5g1/6d6Bvsx+zcoudFkVLle69EEZbWCnJAABgXHz8CV6NnQARpmpfmHxktQFB6UmNca/1M2ryZjGcBE/GyBZh8SKrlK61c/6n7UBBzLrVzrjosSOcVzxTT5h2oUNMSNOzeOa6GdVBVU5uCPcBMm9O+SAb3ammp40kcY8Bkb3MAXwqB08vmHHhamNccn1KjRzV6cuGm6XxAVc1e5+fmnhNJzJ235d72N43JYfOixehoTnnDgAxJUwkUTKjWbgDJDJSGhpt4F2wkv4jUd3rHmAafiPLXPCjAAcsr67h0eQmX5hYxv7CKYqmkBQBvH68yhNCwuVnEK+cv45XzlwEO9PZ0YUQC/tBQn170Z+VPoUSl0/LTrxtIw64+OptyYLAPPb3duHRxDotXllNySR/WN0v4nf/+dfzjIy/jF943g4nhHgHusjZGQRL3rqmeyXsV0dGexe/+q7fhzz77GP7r/d9FVKPBV/NFAfJWSM7T1tGGjo42lDgH0oF8O4C7AHwmDXGzYStAfg1CK78e6cz1wZ4923+gHpA/cvNhcAALi6v26JkQFlc25eDEpMTPECAO9CwQ2gCiCIwFQnsH1/PvAJPexSLwiAmzujS/64E7guhwgQRpVT8mBplAae4SNQIW2ECu5uVV52XymgPCNuBo8fo/uyGoRgoAQwM9GFROQoCY6VcNttQqEE+Px1cDd8K2ar5aAL+jD+htwllXMQROLwlN3q2barrU8+4aZNPRw0lz8/nSbRoeb7+ENrNN9TxOT8FaAy8MgJCX5ZrdVV4X+KkGGtM8YbQ/icUA59o7HhUKKMBbZSgtn7t1MkLDxkYJl+YEqM/OLWFzs2QvXiUA38qwtr6BtfUNvHzmEgLGcP2RvZiYSPZHzz1X1emqh9qwT95xGn6phA3BL5PJYHJqG/oH+3Dx3CwKxVJC6alYesNjL1zET//Gp/FT338D/sUd1yIImG2qTwB+Dq6BngL/B955HNdOj+KX/+s/Vj2UpqxM9ol+CGS9dcUZevu7MV8ooZzJIZfOZP8O/BMGeQD4BoDjSNbeNfBXKmEwPT12MC3jbDaDvdftRblUwcrqhoPvHJZOKwe+heUNmyriiAIb6BkAFsm4AAjAxdnhzIB4oDR1ySfiBkwDOa2vO5s0JQaAmIdXgmUEueCOaaAHk0IE1e5l/fW1shTA1eKTZRxF19/XhdfdcEAPkFJpN1q8olcao4tCiH+IqcBdXiQBfy3QDyTA9zQD8BWhwbsAT8tNBPqkeAKoSfRAvBnTgr0Ga5Xuvock0FcaeFI+eWVr5g5PC8QdwIcBY8rHzU+BncbR+qt79VpiZcjzIWia+i2WK5ifX8KluSXMzi1jZS0vtHzfdlQF8K0MHnYR53jiqZPgALZvG078LiwWKapVD/DWohE/6dqiNpWg6O7uwr5rdmH+8iLm5q60vK0LpQr+8999Bw88ehof/LFbMT0xUMVUTxfrGeBX9WVMuMT9s3/7g/jwH30ZL51dSCx3NV+sCfI09A/0YP7yEkrZ9npA/qqEzK/+6q9uBd8VGMc2FIOY+7uwsDoShnzy4YefQZkeEpAQpvdO4eY33oK5+WUsL6/HNFbyygEIU/2p80s6zQoKeGGA3NaFub/WtDQrHyGwUJDFvho6oMUHSPJxqwFRATB3tkjJUVZcE41H5t82Nog7XncIHfowC4NYjFSetiMxhMVCPeDupicBvy+eMWCqSYAvVMQcfBLA08DIha/D+oQpVi2eXDSaHltY58ZTICZE9jW3rimIu4w56Ap5Oh/vCAVEMKB91XIf69bDAewIhsjwJlo7zQNgbmEFL5+9hKefPY3Hn3kZZ8/NYWFxHYViWc/JhxLQzT56ISxUDWkAMt5UieHy5UX0dHeiJ+Zwhlctq9Xg28pnqhUYA1gQYKlVpntPpRaWN/CFb34PPZ1t2L9zWPZLuesCbt8hUzt0vJRpPZ3tePvMfswt5XHy3KK3CpUwwlCfZwdDghATZDNYW15HKQI6y6lOtRu4774vfPKee+5OljRaFLZKkz8H4f1uJ2qY61dW8mNBwHDw4E5897sv1WR84OgBgEtTPVxgjocry5uIOCewZZujo4iLFeyR1MbBEHCxaCcAA4fQ9oWmLjT9UAFjAD1QWXPs6k9bArg20XOp9es84AAL9DyrsALAMssrCwAgV5OSNBV82+au3TuJ40d2gzFjdbC0dRXJVYKMdvhZ3boBcNf3ScDvxDMG7OwHutML0rFQkBp8mALgVdlpNHrASUuKJxduH7XS4c/PyasRkcJKBTjtRxre28YxILbrofL5QJ8KEW5eSurjr7as2Xv2yWCrAZ7rLVqx7W8QGvLTz5/Bsy+8YtIjYWUzPuuNxm4DfJMQ1kB2zoHHnzqJY0eAbdsSfKrrd5RcgJr6qFW/mlXktUpC/MXWSRJFEc6/cqn1FhMnVMII/8/ffQfPn57Hve89gfacgC/mmO4hNXhL41eDm7zPBgH+zQfuwPaRXvzpZx6LlVWuhNgsVNDZkR4ie3q7cKVYRjnThlwYn77whHcAeD51AQ2GrXR79A2IZq3mDCfI5zdHgoDh0KHdqZjuPrALxVIFa2vGBF+tay0sV18IoYA+0bsWdzzWEW9byiud9oLncZrj84AXxhzfOP7pI88Z9CGh58r5Dff8ReDguOX4Phw7LNqUakR6n7Aa/FU7cLX6WVCqND3WePLAQ1Mt3SovIZ4xYPoqA3w9dSU/5johPk27+GgULCpvhEbD5mbxGAFgC2TJva1Zm4HeaD02wBsNWoEkiEYE3e8ti1FkKh4Rercu4JCHPinw5eZ5EDfbq3ynzlzSAB9x6G8j5uaWc3IIVDLAV4HVxJR06YSSczz59EnMXr6in6l2puQSuHVR4wlSlFVNgOCEqBorymL24jyKnjl5m2GLAgceePQ0fuH3v4jzc6ti7AwjVELpCEyOydpJjnScU3Ec6ihHOz9x9zH87z9yi7eo1XzBFOpWw/NcPb3CelPKpTY/XhWT/VaC/NdRw3f9xkaxp1wOOxkTmry7OtUN/QO9GNk+hiuLK3azJ3SkShhheT1hgYWTx7hI5HIwkhoC8aFNwTgGwpb3uygG4l7XtdwVAghtxFHxeLrTp81FktYRQnK5LO6aOYLdU+MA7C0yXKC8AHtuPlQ9YKt7mIE5Dj6m+TjshKR0qplZdE58wIBd/UBXEwC/WRGL7OoFeBpqArrneXyCEyFPBHsvjfsuIucehk4vYlN06j3DvFe1YdUCUMKDCgTUTa0RMmzgjaAEBCmIEBFCAXdEOlnEiZhChAQtH3D3T3x3ly4v4rtPnjTeIhWwW9+AK3wnAHydYNMsNqk5+tnLfnNwcsG1S25WaKA05KcqTVJYXV3HlZRmeu658tPVfsAzl5bxwT/4Ir717HnjGS8knj/DCJXQuP9WaZUwDvw/fNch/PyP3Rqzhq5tJC/O84XO7g4EAUM5vYvb26+G97utBPknIVbaa63d/VteXh8OArEHvKOjDfv2+U6eNWH/4X0IgkA7wPECB+kiy2t1HB8IEC3egLIN5o4veu78pQR73wDldWmrBAurHs5Z24Rnb08X3nbHMYyO9PsHcx2nBmQy0EIN1ioP1wM+DUmglQbEfXlVCBgwPQB0NgPwZeDMkjhoptlQFejlhfe5Pc9GomuCvWtJ0deEpwJaRUSBWaSTuUrY7mnd+UnzzwgERjDgRBAw2rsrHERauOCG1sA6ALWXXdRP9ysu3eDqOpt6LS6v46HvPIdKJTLupcl3qH3qc/O8Skg376kFHcENvOqtnSY1+stzi4l06QC7FuyZztEMYNeSDLhTSLlUxoVzl2oxbaI+1UN+s4R/92cP4i+++JQ82MaAuNbYdZxJ037ySfzdt12DD73/dnM+PQfKlQjFElknllBf1S4sCNDd3YGIBQiDTJpHUFvptjRsJchHAB5GFXP9+vrmsHBGI/4OH95VleHe6/aiWCpjLe9f2OAOuEtrPrrqn7/lg94x32vf8wRcuZQStXbvgn1om9ZNeuTcOyfQ0fPgQyON+q0HESbGB/G2O46hu7vDmECJZqeA2wz2gFqTFNPqI6MhqrZKC1IWH9+78eRVc/CdTawQ2ShLE30Lx/Uk0LbaAslptdrIKyQpLVil0V9ODtJR4E6kNw5u0kked8Ed5UXvBVt7qia++t2xDhFaVyM3rmjt54HsY1S4MPXkyG8U8eDDT6FUIodJke+SHvNMtfs65Hndhq0XA2yOCujn9CFa9tqJarmrPU9tbbemWJAqJJVT2Czi5MmzCCuu44mrGzgHPvkPz+DX73sQK/kCMdVzqbGb8deY6pWWzy3gv+vmPfjwB+6wjqxd30w1t65DT69QzOvQ5rfcZL/VRxE9hARzfRTxTKFQGjAe4RiOH9+faLLPZjOY2rMDyyu2k6BqnVUfHVjnx8w5JABHxLTo0dhjc/Dwg72rxce09sg6hCYMnWkBlxcXhzSouGv2TuKNM4eRyWbiYAB7wDYDrjHPKiLL1CnbTAEOBW4fqFUD92pgCYhV9M2Y6PNlsYqePnOrQjXhxAfWNI0npJFsViIFcKMROzwUeDp8FGADjpmdvDuVT5nYodJBQRRk7lzxNmDsN7GTOXpXSLBA3zwT7YvyMGhw6Z3ugW88jY2NomWGd097pIJy2AjA1wq86m1iHh8dj6RGP59wWmbK0YkDKJfFeqSFhWXMzy+hUg5J7jgfKlDUXuhXuwIcwNpaHqdOnkUlxW6orQ2m0k+8dAn/1x/+PU5dWJSmenUCJz3RLrKBXytPBvhnju7ER+65E205oYmv12my7+4VK/LL6Q+seXtdBTQQtsKtLQ3dAL4CsYrfcmm7spLvP3Nm9iAA/SFzzvGxj/09vvvdF2OMpvdN4Sc/+C/x0slzuLK0Zj+Ec8HAkN8s4YmXZi0i5uSIb7+L81PuagNpcQAzbmyFq1rHxS1MmqFVKzxNGgP0nnsm/7NXzjPtt16LPZa7W1GfW284gAN7tut45m6RU/F0ybhaearoAQBmhSon8eLe9adng591X086B3b0A/1NeLLLl4Qv+q0AeDfQ/pK0nS4xrUq6CVTL43GhSIKo8W1g03N3MKdAD1eAUxxMvOKvy+X+dMpPCwkqXQkgUlOPCE9jeVAWBylARxL4wwj/+I2ncPnykhY47IV+ZsrMWgSo24C2pBPpA2DuoSNtTQnd/uy95RalRcAhvvfrD+/F6OhgYpvTNl1d3cCl2QUUCkVsFkooFEqI5PyGNhEzhoGBXowMD2BwqA+ZTEa2h8+Cl+I9VsvDORYXV2Ir6U3b+OJ8bWX3Tzf4MMnPLz64ZLMZ/PS7bsAdx3c5++Rhjb3q3vya8Y8xhidfuoTfuO+rKJbLOLBzRIz9zncWq4es++mXzqFULGFwPfV6jP0PP/zRk2mJ6w1btU9ehTKAmyCOlNVtCwCLi2tjhUKpFzBbzgBgYKAHDz/8bIzRiTtuwuTuHXj5bHyrhjuIAsDlxTxW6KI7D8irsn2Bkf84d/LSwVruf+JWf7MHaA6nM9MEBkPLbT7WtZPelsviTa8/Klx+kpblsn5EjJE84uAfbw5Gto/J51L3bp5mwF1eTPQCg00cNrNeAl65SgCvgiUAVkmz7muk00GVuhhWiUI7pu/SvuCeeNp/RBSFMzIH7wzsbn1s/lQAoGZ7u0xOygMgp4UUWHDdj93+/fAjz+P8xQXL7zwnYBNJM4IBfv+L9wF8PSEted3djgNzc0vo7e1CV1eHFU/bHwA2Ngt44smXsLyyjs3NIsrlSuLzFgpFXLmygksX57G2nkfEOdrb2xDIvbpV6+kD5gSSy7MLuHj+ck0+TYUm2YQRx3efv4iltQIO7RmXO4NVn5PCIUAsW46wAxE/PtyDQ3vH8fBTryCbCdDelk1ZOYZyqYzNjSJyYQVBLT8NIjx2zz13P9noM9cKWw3yANAL4HZ5reFofn5lMgxDS4djDOjv78Hzz5/F8rJtln/Tu94IZHO4dHkppoFbg6m8OTu7ghJ1VB4beKlWHA/M/g9A/HOhd3TA0mN0DOhUqjsAQ3dCi1fsV3TW/r5uvO2NxzEy2EsYMMOTM12mBnQlAGh+RmsXdIrM5AVzaDz7tGNtkRL8x3qAEddfSB1hXWrwW2uISg7Nau40v3lv6t5Ila5WroLdVzhpd2riJ/EegdEt282nwFXRUX5UE7cEBxXHyeCp6+Qx68v7J555GS+eugDOIz0AR5JAm/m52R/fNBrwBjjUzFBN6FDXHJfnltDX24XOTr+EWyqV8fiTL6HkOyK1Sh04gEKhhKXFVVy6tID19Q1wztHWpgA/3l/8jGzrCOcc58/NYmHuSpVMCRV6lcLpi0t4+tQsjuwdR3tbTvdL2pcA0xf1VBYB/pGBLhw9sA3feuac3pOfJgQMWFleR8CjtN7v5u+55+7PNvSgaeqzVYxJ+CIAefSK+OOcZ0qlcjcAqNX16i8IGN74xuMWg+6eLgxvH8PSMjHTVxHUK2GENdc3cSMdjvR0NSCZLWxmjj1pP729Ol/Rmy1yep5RbfXgZh5e00m+inZi2yDuftON6O3uNKZKGDDgcLZBqYHV3UYlJVoVF0VGutXPSwAD3MzHuk1Eadzh15c+3AWMNgHwa68ywAOmP6R9Zm+7qfdCwJLOlatT2qjGrOItTZir986tOqlyrNX3liatBjbzrt1jIs01nUvnps7cLHqj9Yr1P9J/NM7I/C+9fBFPPX9GfCNkcamai/etnqftuNVg0jr26n1GePLpk1hwtp5xAGEY4amnT6KwWcdcsKeCnHMsL6/h1KlzeOzR5/DiC6fFHL6zUI7TKw+fKIpw+uVzWLyStJ6g3ipWz1HV7lDXi+A4dX4Rv/YnX8XF+VU976731etFdwmr7uW8/u7tQ/jF978evV3p5xQ7u9rBWF1b6W6r58nqDVs9J6/CvwPw/ZBjxMZGsfOVV+auUVK7NqOoDzuM8JGP/KnW5g9efwA//DPvxjPPn8baesFrnqfa0sLKBl48eyWuQXlM9q5VwCG3CFyLAWMMGXlogtp6wQKGjMyj5+JVuQEzB9So+R+ZBzBxYGZuXs+xM+DQgZ143Q0H5EEN6nmY0cKZqSedm6f8QOhhkm16WhfScIxc68CtHzc6lj7YKU6UazSsFoFzq68uwLuhmlZvxblmegehbM3bCFvgsKZ1OBc2GZXftLWtxXPnJWitm/Ax8SaRV0n3xRu+BPhVuqTVR8tyW1A4f/EKvvL1pySYGy3eGhcUHwuHnPYhgaQk9k/DK96R7L7VxHy8VY6dg0Os5zl6ZC+Gh8SW1ygSAH/lyqr7FNXL8eOz4UASGWPo7ulEwAJbMCUPTS0wpWIJxWJZ80n+zqsLX7E2SBo3Ej5sP794hWL9gwP9PR340E/chsnRPscTnj1+xsZTmPF7JV/En376ESyrxdxWmeZh1PXZkxewuVnEYH4RrPZgxSGOnq3TqUK6cDU0eQD4BESbBQDY5maxVwBdQICQaa0+kwnwhjcc1Zl37tuJSiXE2vqm3ZkS2k5snasxF5X4iacPnHOyitNo7JXIo9VHrkYv/zhdcU+0ea3tC/rtY4M4ccMBXa5ZtWyu9eAIqdWRp+QgpqpI5VXPYbRA/TE5K6z14KzAQ/6ZMkyrcuvCpPe1A9ubBfhXWYP3BQVovvbQ6eRCvQv1XpSWqxwVmXeo6Lk+O109PAVYqH4A+92rElwzpUjnpG70GiSvfW1biDzlk/5CV+FHHObsd/U/51hYXMNXH34GlTDu1Il+C/bed/vK9y6a+apb2rUSmKloA+orAIAXXjxrAfxW1CHiHGtrG1hZWcfqyjpWV9awsrKG1dV1/bemf/MS4JEoSPxTCCvrBfzmxx7C6YtLWktXW5IrlibvrLqX2n4l5Bjs7cBP/cCNcm7eBJ7QK7u6xVRMSm2eAbi16QdNCFcL5J+FcI4TAAiKxXJ3ECiN1g/0b3jDUeTkNobt09uxspo3oFED6FeqeLmru6MSEZLH4kWIuNlvGXFYYG5tlwtrecozHpvc/fOdnR3k+e3ncTU0NcAqE7wCdQq62mEJ1f6UAADjbEXRUtO+HtCd5vCBu6LpaRNb5RKMJjXDmgL4BvNfjZAW7JUGTre6QYG0nnOGFKyIZh3ZQKp4aj7yXYPQuL9a0HD6hvaWp6Zt5AvmcjU7NJ1jfuf2lj1rykBp9CAr4WWe9XwB//C1J1EsV4yAGxJw52bVfbX2bqhDNDwOJN42HKKI48lnTuKZZ07h0qw95522jFoiTyM5mwnN17tFuQnJ+kYJv/vxb+B7564QYDcOcaipvhIa5U2llcoRRga68Z63HIkvXPYEtbCy8how2V8tkAeAv4DU5kulcjd1gsNYYM3NBwFDT08nbrnlINrashieGMHyqu2D3gV6dVeqhLaXojShhsSdlgnnHGFFzvNQ7ZzO20ccIbcFgJgHPBlPeXR05BI1eFDtnAzOWouDAWo6/wqQgVrxgjNgy3vEAIbOI8OAB0icDJ054ewmxbfhDfmyNNE3lv2qB1970DS9KI16e9PpXAtoLg/Xcx31FqfKFIBqgNpYbOicu/gX6Suu33V8Lt5+BnNtm+QNsNPFS1w7qgGhLZYq+PKDTyC/UbDm38UaFrOlNt6oTbyPtCkNF+un5EnpdPiKOOYS9tA3WJnGszckNLXoy2xyHK5Ft1Eo4/f/8pt44fS8BG/ll0Rp9Ob8D+UmV1lTy5UQYcRxYOcI3vq6fTUr0iHn8MuZ1Pvl/1mA/N8DmCuXK+2c86xZbAc5n81iQH/XXcexbcc42js6sLyyXv0lysRVS4tvpck+DSd5LQeqivKnrLXyyD5oJrI1ewr29hQAR3tbm60xKbDmIGZaG5xpvRTQ07UPUaTqa0z7GtiJRqd4g5uBXfNTmmNCC7VngV39YsVpI6FQAc5ukaObrQw+4SdSAhlIG0YGIKnTGYBo8VwJtcryYiwpdEsaFcIsE7xKU+cwEO090gIi4R1F5l6mU69y9NfyYy87jQZ69dyAfo6IczzwjaexuLxugzsB+6T2dK/8NNyNqOud1YpJyths90wPZC0QfmrkaQmovka/12Kpgo9+8tt4+uTlKho9NdUTX/cS6Geun8axAxMWX/dxM9ks2ttyCIMAEUsFszfPzNzbhEuw5HA1QT4E8FeFQqmbmuWrAf3U1ChufcNxbBZKem7IBtN4T4qtqndDIx9k0rjhMmJ2vBjYyYlIIZmDj0JLc1ce7CxzvowPI7HvVWtwcAZvOICgAVoN6hopDJhzw0OBhTXwk8ezFk8RLVRphUkA3JYBdg8ANc4dSgzFcOs82V2toNtbIp3eows474O8P7hrJLj1XukctRG4jNAHixYxLR36133PsAU2ndfMp1PNX+ShgojJa8WR+O88/j2cu7AAfSaDFH55nS9ZC1D1hhYActpy6ohOkdhA8S3j1xyjNAKaSK2S3sK2KVdC/Jf7H8HjL15yNPrIBn6y0j6KIpQqofay+M7br8WO8f6q5WhtPp33u04ANzT9cJ5wNUEeAD65sVFsp9p6EASgpnsX6G+bOYSVlTySuoq18IEDq3W6IfQyrR3tDe962y34oXfeittuOYh9uyfQ291p1U0BfqVin5AUya0dETEPuafXdUiQp6uU1cCuNDMFGgAQ8Qh6LhS22V4PAB7wUGkxK4EsUy3gMovCxIUrb2UDYNeA+G0klCMB8JVUviRe24HLRjVz7hDTHzrdAW3Z8Er4subu1XuRmrUL8AaMySp2+Y6htXbyDp0VIpZSAAAgAElEQVTzDSJqGQCPzcW76zyMkAg7nQoyXPSfF06dx9PPnbHn3EnHSfzWmhjgW4INvOptiwMn/299SFVOqyvTJD8eu0ii8JgPZVIYRvjTzzyKR567EF98F/o1+nJFrrsKIzDG8J43H0Ffd7I3r+76Ft8BwOvTEtYTmjgSpKGw9LWvPRWeOHGQRHEEQYAoivQ2NM6BIAA4Z9i1fRiPnpyPMeKgijMHA0PEI+Q3y3aiTk0KgtjKkkjmoZU3XZ3tGB0ZAGMMI0N9OHTNTjDGsLFZxOX5JVyeX8bs3DIWFlf14iIxwKktdgwR4wi0kx7RGxkCgAEd7TlEnAupjAmwld4Y9W/EjMc0scXKPDmnz8lFJvFjnkalccbEo0Iws/gxFqMTznLkaM8YskwAfFuqg5jiIZQAX351z75oOogmJIM2N3H6nnPpqEi+O0kkAJjrvhYTpiweOsrWhrhdLqUz9IQuKQ70mkwFKYEAiPtX0IKHoL80t4Svf+s5hJGhsytZox2dKz8NdyNScU2KSo9FfsrEejcCcgl5arVI3QybpL5awkma0DPYj/Wl5cQ6RRHHf//8kyiWKjhxeAoMnKwbYmDM3DMwcB6iLSsHNcbR0Z7De95yBB/73GPS8Zrdlzu76gb5GQD/oZ5nTBOuKsjPzNybm54eH7z11uv0fLAINtCrtGw2g2w2i53jvTg3uwgHvVVWOUhyrG2UEuf03DzUlbvLK2W0Faa2j0hibvl67+psx57pbdg7PQEwIAxDLFxZxezcMi7NLeLs+XkzOEvtRu3BF/s0Q4AF6OxogxjklZBBhRdzzcEdP/WQYA4tEHCSQwN9xAjYkGdggl8y0EOUB9GmGXBMDzB0NNizIg6cWQGKr/bZF00GF8xjcYDR4CWK0nl0ahqJJBMpD1h0nKSZzCTevQYIB5eXqY/qj9RCRLV2kHstRGjHPtyajlhd3cSXH3wCYdiAWSYJixsEyqRs8fiUBdSWUdKw2PKMPHbRooqkGW9bVFRduTnQ2d0FcI615ZXEvJxzfPLLT6NUDjFz/U69T175/dTnkHDhbbESRpYf+9HBbnzfGw7i/n98JlaFXFsO2UwgrARBFpmo5qD2z8Jcf+3Zs5ezzz9/VpvklbleXQPGC157ZzsiHuG63aPo7VLzGsly/Vq+2FTnSZfXTzU1ORZLtaYS5HU2k8G28UEcP7IHd7/5Jhw7vNvLnS5I4hFHri2HKIKcs6cLqcwiPD3PTq4jy8xq59WHgJCV+LoOjumeDmaWYxICTowDO/tZwyfKcS482W2m8gT52g3W1IluU+4HePmfnh6RwGrM5/a7iSgdMcErnmq+XPzG97PT8+LNojoiaHBo871Z72H6lao/9R+vF9ypsrnpl6VSBV/66uMoFKsf2ZkefNOHZsHXxyQVzwSiqnlT4VZLnqhGGS2ga1AAa4ZVEl1XXy+6enuq5uQAPvXV5/DIsxc8J9ZFli+UUjkkpnwxn79/ahi3H98VZ80YOvW8fKpBcdfMzL19aQjrCVcb5I8CwOc//x09Dw9AAj0s0A8Chrb2HHjEkc0wnDg8Rdj4X+nqRimBxPN5WBJ4eqndlyNgASYnhigK2gM8N5m483vrjdcYK0BCdTo6clp7VskWmGsQhzVoW4vyYAb8WD10HVMAvQVaNkjtGGDobfBEOQ6xTS5f3/HNr6kgsN0sVFPtE2srD8C7QhSHLZipOW4z1+0IcxSYrdXw8peAvw3cdplUOweokEh4KXBX9BzSp7wsWzAE5xxf+fpTWFpZS/h2rI8wRev6yWMpNXl6CBoB8yqUiflToGOzUE7fX/OhOUZbL5bULr13cADtCecE0HD/A8/i9MUlsV6KrJkKydnzJbnC3nI7HnG87shOXDs9GuOpyq0EqU2bR9ISpg1Xe07+KAA88siLWFhYwchIP4JADCDKXE8N49lsFlEUgYFh/9QQnj45i8uLar88h5ljFtlqrqxPE7ipwshQH/r7uq15GeXDlLoy7e/tQi6b0VkJC/iM6qTmAGN4653H8LeffRgraxumCoRJV2e7NpPrRG1CT2G25wBnDpXKT0zzynTPOYMpis69UxO/zWe8mzV1otzFVeHR7p9i4OQ/S7gDjKAkI7R53CcsgQgAjtBF5+jBheCm+honDDipADclxKwLtFxfmbTu1NyvynbzEgpd9iOPfw+vXIivp/G2Xz0JjSBHFXkiHp+ygFQySnWKVCUlECXnTVf/tGVXo8u15VAupTG9cesnnlqllHrktwQG/cNDWJpbQLmYPMhUwggf/8IT+N9+5AT6u9vJEeAiMCYsU2peXqSbcfVtMwewtLaJy1fM4WodHUKDD4PUC5SOAvhGXY9XI7wqIB9FEb70pUfw/ve/RQMo54gBfTabQaFU0T7XZ47uxKe++jxhZ5Bwo1BGRc756ViKthYY+oIFvThycBeOHdmDQHrkA2Cfzy4BX/k31nPbZvLaLGAj8+IUrLlcKNfWlsM73nQT7v/8wyiXxbwNFSK6OtrtWirA1UBNyzAgLQZsNafk1KOFQD/YwTDR2+BGeACz68BS3CX0az7EwF3fJ4O3uPbQWKZ8mZogLKgrteuMcyefvGYODwPwsOum8nJzzR2+3rxO+fRZvvfyRTz53GmrTezL2gjJPVfJNHZEY2BQnfnWAHKDoQ6Gbt9sVW3a29uwe+8U5ucWcWV+sfXPWC3UkAy5Q8MYw8DIEJbm5lEpJ8+Nr2+U8PHPP4573nUTctmMXnynkCOKQoRRJMdwMagyJgTugDF83+0H8eefewxleRBQe7uYZo7qA/mWhlfFXA8AX/7yoygUSjBb5oy5PggYstkMWCbQqMQATIz0Yu/kkJdxvpGJXM84096ew5tuP4ZjR/YSnViRO8OOZ0Dh8MRx0QmkYEj+RAcKGDAy2I23vuEYMkGAbCZAJhMgK697ezoQSK09kHnlbkPJ0zxITBujA72lsTl1TQsyXLSDmuftamPYOdB4N5rPAwsbDWd/VYIBRds0b0CSgjdpd6B5gI8BO7fzmUzElA97np37veK5e/MpX/r0/u/BXM/OL+Ohbz2bui3TJsSqUptLHYwb4dYiWEt+MEKyxRBaW+ayQi6bxfSuSQSZDMa3jaJvIP1UcnNP0kBu2b5BwDAwMowgyXGHZH1pYQ33P/As3BPp1Il1pXJoTPnK/4ncbtfd2YbXHd2pWWZzWWQCcRhQSqD/pwvyMzP3DgPYru7z+QLuv/8hZ2+8Ws3IkGsT8/GMMXGqjQT6W49M6bl8FTiAjc1SLK7eMDbSj+976y2Y2DakOVjfnwOkJt0eEBlX2r4BcX0CnFLPGZMrOWUcY9i9cwwnbtgvBZ0ATP52dbaJvIwZjZBL/lDsSFmQ5VOgV4+QYA6uB2yU1pfLcOwZDBr2Zre4Cczma9O9VkJNcBeIrqK0QATn3hYC6gV4j9AGSu9CMgDY4A9vfg8vJy/9FlyBUaWvr2/gyw8+jlC7U3Tr4qmkJ7QU0qoUF49PWXIqnvVLD80+dwp5oV5u3tiJyXHk2sxisu07xtHd3dVY2Ql50rKK0fGEeACZbAYDI0PSKptcwnMvz+Grj56Wc/H2wTXlSigdnJHzRkLjofTovm0YHeyWij5De7s02bNUIH94Zubexs2innA1NfnYgoLPfe6bWFhYcfzXC0DP5rJQe4jNdjKG/p52HN03TriIF5UvJGjy3L7xdwiG666dxlvuPI7OznYdTwdG7uGg0pVp3dawxYUSWgAF9BKEufrl1v2NR/Zhz/Q4AsbEHxi6ujo0mAeKnxYOoIUERurGZMXUvnpGfpsBepWXMWDfcAa5BvfCLxeA86sSFDm1PLz2QmpwJ32GLsAzpnBO8igaWUYzAO/wVnQWjxgNnPzQCT4Ah8OLpiv6ciXEl776ODYL6VZPJr5vHr/xgRd3iasM7rVSapGmyllnB26GZ7PP2OzzFMncNocYg3bs3I72jjYrvp46XY3AwZHN5dA/PAC6/osGhT/feu4CTl1cRpAJtC97tfhOOy3TDszUYjzhC/KOG/fo8b5NtkkYpILbPgDTLXhUHa4myMfMEKVSBR//+D/ovYgK6JW5XsxfIwb0Nx2cRIdz5F++UG6oL7W353Dn64/g+OG9apIZgH8gdBMY4xLUNaSL/yWYBmqSG2SRBrOv4VwzxvHGW49geKhXCD0BQ3dHG7h0jqMWG2gNnptfFcuYqgeXH6DMw1UZhkdd4EIG9b3DGXTmGhM414rAuWUb8OAA5as5LBgQlgCWFtxhwF2RKnBNnKPnjQhbHoBX94SOgrf165ZP6+Yz/1tX/msO4IGvP4UrS2t2Q8YuHSnDE1r67qswiwsO9RjFW0SZgk2sVo3IKvGL+vI7YX0tTwgEVZAJMDU9iWwuYalXosBS6yVVr0v14M/V1tGOvsE+tHe0o6unG70DvRgcGcDI+DBGto1gcGQAfQO9ePCpc1hcLyGTy4ptddJcr7Y3h3KrHV2FH0XixLrDe4Uy2t7Y4ruWhVcV5AHgoYeexqlTFy2gZ0wsuosiDu2cQJvygY62LG6+blLzUPMkbvD3a9OlRof78Y4334zt24b9Qxi3hzOAzKWDatQSq2Hm3qHrTDR4rjFaNDzFbaVtA2hrC/D2O4+hq7MNmYChq6td5hUDpAZtIhio9X6Kv/hVC/CYFizsMqlHJx/IeAZ8DuwcyKC/ozGAz5eAM0uROURHgaYDnK6Gv9Wg74I6BXYt4NQCd3funQIoAVEbYMVFXQBvgTb305HBl/6v6uqnsdvZKyBY/E38sy++gjPnLie2rxsS3yeP3/hoYykcNWhbGKrIKe54ka4Syc9ZT2g4f225KxY28ptmSoaUnc1lsWN6MnHeuyXvokbn8SZbHVzcdHR1on94AD19Pejs6kSuLYcgY49r5TDC57/5PRRKIbK5LMLYXnku/dlHiDg5oySKcNOhHejubENHx6u7+O5VB3nOOf7sz75I/NVDz0fruWVllpbXjAFH9o5joEfs2XLn42sFBuDaA1N40x3H0NXZbo1i1kAG2KANZVEwK9a1K0QX0IlQoABZ0QUa/eW1mamXs/YMvd2duOu2I8hkAnR2tFuA7mrjSgAwwgVFciUc6HkETafQSJfNBDIxTpvEBoTRHobx3sa6TaECvLwUIeQKBLll1o4DfgLoW+npA81jsFzNkRNQV8/rALumSwnuPu2dgncc4FU9DI0P4FVhtQDe9GW7HjqaJKv60fyUmseu7Otnnj8LK8TxF7FKeEI68G8keBgk4XA9+FwXUWt4ppAXmi87gYr25fz6pp0m+097Rxsmpyb0WNhIZaqXXgc/OrD46GJ9Xd6T9LWNIr74nVOIOJBry6JSiVAmDnJCYrKPiHafCQLcenTazMn/TwDyh5ISnn32DL797Rc00GfJPkTjZtAG+kyGYeaocJCzQU+oq/GBtrXlcPuth3Hs8F6zlS1GawBaGsCNpQEe0zuMAAD9K+Gaq19orV+hakAFAFUeERgmtw3jxPH96OrICX4SqC0MZzAuehnXaerB9HfG7Hu1h14JCyqL5mOxEem97QGmhxrbdVmJgJevhKiE9sIz8w36Ad8H+hT4DfjX/rPyGE4xULeAnVSA1peCO2h+GDBO1N41KFfR0CVDXzqvBvCG2rw72sd5QprPTE9oVF5CqvOevzCPlTWygrJRIPdQ+WiT8sfieR20daF0etrmAbnGgFYlsalnTEGaX88nYmRndycmto/XV2YDoRr3/Np6lVR/u5pP2k6fXVjHt587Dw4B9OqwGqrBRyEBeikE7Nw2gOnJYbTlMohYAJ5K8sH+NERpw1UB+ZmZe8cBdFWj+djHvoRQ+gXOZOmiOwX0ZDW6jNu7YwjbR3rl9rnaI8vwYB/e/sabsH1imETT0ZHMW4MsbGNAoOP1NLrRwpUQAqqtQ9vnNXBDmdG5Y1YXVWDOL8Bx6Jqd6O7qgFpIZ6wKqk0ocFMmRqPXZal7Br3P3uTm2jqh71VmDnRkgf2jWQP+dQTOgZevRChUCNggDvYG3G3AN/Hmz6fRp/pTIOvy05dUILDL0OBOAEjz8zyTePZa2jvsPApYQfhWTU8w9ROApnWBL41TKKH57d9YnWR45sVXkBQo35qfaFK6N57XSK/JoDHSKo/BPVe1efLaJM2GasyrpNWqk5iXt6noXW9/L0bHR+IJFn0TFagRNlbXsDQ3jygMa5eV4sU/+/I85pc2EAFgQQBuzcMrDd6do49w4vBOdPeIE0lTgvyuNERpw9XS5HfVIrhwYQFf+tIjYIwhkwmgT1Azlm1LU1Zz9Lcfm8YmWVlPByQrDsDrbz2sfQkDZFBRuCjVaQGgnJQtr7nSfNWKeU40cLoYjmrnhAZG+9cnHLnXMHnpdri4sGCPNowRUzw4wXo5X09M91J+Ihq7FGg4NOaLpxNlZDMM14zlGj429uxSiLVSZICFAg+oph0HZEtbhw38+l3X8WffcgfUuQXqFrB7wF0DH+L1T6+929YEL8DTujQJ8NQU73T/2PdAh0WfmZ4DWF3bwLmLCyCJnstE+PPmqwZ8qcf9WmCcEodTlVcnGG0ZzwZBse5ssqOUiiWUy5WqbTc4NICBwernrqcqMnaRSGGFUrGEK7PzKBV8nu5qAzsNEef4xtOvSH/2IXK5LDi42U4XcanNG5e3YSROq7vtxr3oLuYR2CezJYWBmZl7m280Ga4WyO9OQ/SJT3wFm5tFvWhDbz9LAnoA48M9uPk6tf0++aVlc1m0q32d3B60hOndgLvRlhUYGqFC3tqmew2aIHRM/9NCgUZae4+8zZea0XVlLQuAKYvrfFBCEbVA6MeTdOqemO45iMYO9SxmMx5jHNeMZtHR4Er6S2sRFjYM+llz1TUA3wf6FPjrMdV7TfakfArq1YCdO8+RBO4UOBUHS3uPgbChaR7g4fB28ulMCWmkfnEam/dzL75iafWJoQZJfWN3XJJIZp8GwlLCXBUkrVsgaQSQqwguaRk2Q+WLzctpGrsPcOu9jG4bRWd3V6MySNU6paGKwghLC4vIr6yheiPqrzgxfWF5Ay+cnUdRrrBX7sxDvfiObKcLI70o7/iBCWzva6vCOxZSYWaa8JrR5AFgbW0Dn/zkg3qFvWWq10BrAz048O43H8boYLfm4/sWe6W5hCZRwYHROFmu2h4XgJrJAXtRHQV1EIAlK9clqCo+mh9ss706slVgt1HfiWygiXV9rbpwk83l4QyMenudysc1qR7qGRj2DOfQ29FYN1naiHB+OdQApIDSAntVvA/w4QK0qWor/mKATqwFOpkAO60XSH01yLrgrsHYA8gUjGUBVMNvCuApWFM+Fj/NLAb+7i8htQbzcrmCF09d0Pce7KWVqj62Ojd1g2Y9hDzhNim+rjqkYOIkJpFUNy+nrEXSw9XInJZ6fc1xV8mddy/D0Migt5yE4lOFGF21DsY58qvrWF5YlK7TFb3nYZ2PpVyqYH3NLDJ8/KVZrOaLiKIIERc7wbjaK09PqJNxDEAuw3DbbYdTPhmAFprsX1OaPAB89rPflI5tmAW4YAaMFdAr83VHWxY//QM3aBO6L/R2S5CX2rB6cA3wcs+78VYn02FvM1MaeaA1dCC2CI8b8KRAr+sOboDa3TsvCrLM9kazJ4v4VKA05Pmk1OAAPuHFoU3+umyJPMpaMNEfYLSnMW83+RLHy1fUXJgBUnMPA7BVAJ8+qg/445o6rL+qmj0tl2ZLAHattdNxwQF3la64xbV3V8unQKtoTDqoNSElwJv8HkGBO/lMqfE0Uj9CpWlOnbmEojqcpMbIXC05Hfh7IqsID8nAmbr0dFzrBKpUpdWJ7bq71RvqAPOkkM8bkLe/VXLNgc6uDnQ6J8FVFWKarVhCKBVKWJxbQLkU35Fl5FdTeFgJsbK8hs3NAsKKmHIsl0N894UL2tUt5xzZXMZxisPlFkOOTIYBQYChoV5MTY2lreo/T00eEGfJP/LiLNHik4FeARdjwDXTI3jLib02M9JRepQmL3lwYhkQ0crMzSxgZbIA436WgDOIZi75BNbecybj6LSDU4Z5DEtgUEAfQCz6U2XReXll9pfVIcKAvbXOEgRUmxDBhXOTX63CH+rKYOdgYwfDl0KOl+YrCIlJXAV7y1oy4Guwo6DvYLgvcOefnyYO6JxEWhwcYNcgmKC5q6eyzO4K3DWIKgD2mOcpwFPwrQPgueIFUl4CwNM6kdpaVz4zPYfYG5/UvrEMSYHHb+oB7TT8UjFKUc/aWVPWMgUqx/pu1aZMV24zVKZ72OlhGGJzs2BTcjeTuBwY9mjz9dQo8QHqkwiiMMTywhI5Pc+fP4oiLC+tgctDIPJ5o82/MruC2YV1bY7nAHK5DCJuVtmDc2QzgbgOAkQRx+HDu9JWMzVhrfCa0+R7+3vx/OkFXFpYk+BkA73W4iFNy9xo9e9+03WYGOkFEH9tfb1dBsgleGptnTkaOdSfMXWbOtiL51QdDR9ny5+qAKfWAZh86lnAAG4WwDFNZzJaq+UBe65dF6QuST7ZGvT4WiXWUL6q0braGfaONgbwEQdenKugVOEamCwwI0VVA3wf6FPgj4F/HX8WD/cfQfNawE7rEHtOwBYSVBYFuPDQ0TagAK+o6GtqFOBh18WGKEPkzsPb2wU5ZueWjHc7ZzBPvoqHRCpvpnhBPrKk8uLxybpkKtioRZSCSX3wlC74nrOJzDVD3vJ+52Mlrrp7utAmT2VLKscfna5S/z9579VkSZKlh30ecVVqUVp0dYnW3dPTI3Z2Mdg1UD0ABpgt94HvNJqRD4ThX/AfjBmf+ECa0fAAwgBbADRwueCCAHd7dwYL9uz0dI9oVS1KV+rMm1fG4YOrczzcQ2Rl1VQNPS3yRrgfP37cPcI/P8dV3SsjvImwt72L+ay8iZoOL7C3c4Bi7ifKjUdTzGZz2G/9p5/ew8ztWW+AvpNr0C8K5J3MhRVKoSgIZ86s4sKFRp2dF0eTN5vtX6slNG797DqORxP86Y8/99ovA3pwoDcAaTXlXifHf/tffk8emGI6AStLi+CmcW7yzywIQgKvBWnfGTDpw6xZN2Z7FaOzflYjZ4DraYN7ptFbOSwjPmTgaDgJBfLbeQuiIOSse8eIPINervDG+d6JD5359NEUw4nbz46BqwFBSLA3ksUBn8UXwB8B/9Z/Lj3LV4J6EtjB6M2DHXNPgbsD2ybau+t4hGlwmicAeAHeFKfjaQU87f1Hv/zKV17K1bTNFAkP+h1N2CQIU8xP4GJCVbB8qh2HGPlJEqQnlJO5o8Ohf78CLqEVaH1zvSX3hi4pdDo3RVFgd2vXjNG7jw8AsL9zGD2S9ujQWy32Dsf4/O62m2zngL6bIe/mIIKbjFcAgFKYzwu89dbLTXJ0vQlRE/csNPnLAPq1VMatnd3AaDzBVw/28NFnD6uBXt9qoDbA9srVTfz933/N8csyhTxTWF1Z8OPwDPosDxtfnBwHEpPvfLrkgZXNcJenwvnT58QKgUgHwOO5B3o+HGDH1D2AM+EpmB3Pl/IxWWD58I1uzEvtOy/Aq+d76HZOhvBf7cywc6xfdC2aNJpLsC9r90YiD6xM/RbA73iVOwGpq6TRByQunTpgt4DIwN2TpMG9ufYegDAFAF8CZj8McHKAD395U8156vvhcIzbXz9E6EQDT6FfkriKKk5Twbs5cEmLTB2PegkbMHH+1dyeN1O9D5bh9ul4eIxhODYfZUVYWlnSh49VpdVA2Gb5qY85n82xu7WHovD+B3uHmIzju6hOJlNMp3rDj9l8ji/v7eFgOBaT7QBtZS0KufUt8gyz2Rznzq1jY2OlTtDrJ85i4J4FyF9vQ7y8sYa52bzgX//lJzgaToQJ3JnWDeBqkFIO6AHgj/6TN3Ht4po74GVx0Ee3m/s4gNfgndbLwJoAMd5t/mmrgbl3gCwtC65DYvIjeSgvg6NXLsybzwNN38nKgBvQu+kpGUeRj+foyXc0gCBckft4r212sdw/2Svx8HCOe/v65efj/sogWBns44AfBX1I4A+Xwgm6xBUkXOIXJFvuGATAzk3naXBnIMnjcO3d0nLQdQDP0mCWkEoArwuPAnzA18ob8jC/H3/yNQoqYribuCu7JJBGsTIG0VUeEe4nRYUUYiX4VWJ7Q9oncQ1K6hR4ykAi4M5X9zAejeR7HYmvlMJai3XzVLppJV0jN5vOsL+zD0Dv4jc6jq2p99/G0ZHW5qezOYgIn3y95Y6ZLdis+jkD+PmcoNxpdgVef/2lOrFWzPHsT+yeO5DvL/mN8Y7HM/yrP/+VB0GnAXNgZVq4ue/kGf67P/ouOnmOTGktnmv/3Bxux+a5CV9YylmnwIO6X98uQNdZFzw42zD965fMZdCmc5c+52P/h7LYjogjZM/kGbkOAvE8mEN1bFfCblBvaDYXM1xcPdmWtfujAre3ZhpAlAdDRYBZFejNzDWAnwT9SOOf6gCkZ9NHWTnPGKjXAzvTpMH4uLg8XkR7D7R8G8rTcpYMK+fTAHgf4hW2CI/5nPDLT75pj+DJ8BSMt+fdHGBPR4tv1p1pwKgmvDL9Nr0KFhYNTvk3cMW8wDdf3TUm7gDiA76rq6vI8/KqnXjazSRK5aepm4wn2NnaxfDwuDbidDrHZDLDbKbN/LsHx9g9PGZA74+d1Vveaj/KcgP4c1y8uOH2tK9wN5rnIO2eBchfbEPcs+e5m4L+5Ott/PTX96NAD/vLgT5TyJXC9Uvr+MO/8zoypbCysgi+JE2CrwVKuVYeRK4z4DsFEmj5me18ORqfbc87CzYNHyaX1VnzPkBSRkIA0BDxIHiyLkPJdE8+oj2hTgELnQw3z7baqMG50ZTwycMpyHQiQFoipSzYEHtmDZMD+xjgR0A/AP5SB6DhFePhpSiDugR2nyfbcEnZPZ8ouJOnhUvXy8XT9nQSlJ8awAs6/yzuCfjiq/s4DjQdAXYU+pVdEhyjj4SJ414AACAASURBVFQKL/GmCp51wtS6E/CrDC/lKgitLpN2gpwiFcnwGPV0Osc3X911k9UoJLLPGVpp8yVRaj1qA6J0s0l5DD7ljo5GmLJJeV8/2BXL5/iBNXYXPKvJ2/CrV8/VJXOhsUAV7lmA/Nk2xEr08PQr/yd/9Sn2j8ZSi7f0yp7qZoAegMo1HP6D338NNy6vY9Ge4mbjQ2rufM29hUilFDJziIstpAwQAOusA9bsbv2c2V15GR2tT9OiP7cKeIuBnIhnn0XeybIgL5/tnCjebXFJ6XCWj0wBt87rI23butmc8KsHE7NcxNaXBnv+gSsLcprEAya7CTV8k5US8HPwFx2Ahn8irtPyg44DnKgMoAlJrd3JFHYMyuDuwZbRW2reoSAH6QzETgfgrWcdwJPw1M8fB8vmWgN8CjPbYFuaeyMfSgRWdx7q+LajPRlRQxapDD4Jz4aB4/EEd7++J98hc8fJV9dXkWUR+Cn36U7RNXzJGnTiZrM5xuMp5nNNvLN/jMNjOTbvdsEzwA9lzfX6qNrLl2uhsRV2ptyzAPnG4wqdTo7pvAgaAn1+7x//+19KoGYavVLKAX1uwTLLkGcZ/ps//C4AAjiIIgBpMJBUnj83u3vN2u+EZ7V2ZyIPnw0waz+50Y3T7t0yPMOfTBpQbFMdD/TWWcuG25QHko5r8ErBWtFL4dc3e1jstX8NiPRM+vGsEN+EQkIbsS25YtoqB3xY0OfmdQ/LYdqnccVEtMAcM8WHwB5q7Ulwt3ljXQ0Q80to71zj57JoOgromwO8tJ4EAO+7E7IjAGBrex8PHu2KMkvfRRwlH+ojpDoQFUDcOJk2kWMCNEmPqgItSTn8CdglI0aj1ZVjPVvnhsNjPLj7EFXlp7IMK2u1k8/KzJsFnHoPIVY3IGA2L9zHc+fhnhuDLwr9XRdMu6dMYT6fYzbT4/LLywOsrFSe2/bCjMk37o10OjkmU7NLmvi+Cbfv7uI/fHzXTa6TZnr9nCsFlQOZypApPbP+ypkV/P47lwIzuQV8cmDJtXFvAvcIyzsIHKj1IwNqYUoPJuCFNDpz0uRe6gRYOby5PjVGHw5DOBkpHn52OcfZlZONw3+xNcX+aO4y4Zbk+WJxyEjKz0Xw9WpN8B7wU6BfBv6YXt7m4kMAaU2didoY2KPgzq0UNh7jI2g5nX0KwLvSBI8GAE8xuiAtoenrUL75TR3WlZwAkeAuipNtW+kmAPl8aPGNc1ZJ2IzLqVAFPeK6rgoAHOwd4PHDLfY+lSlXN9ZcGxvneZp5bOracLO72mm3vTfEZDozS+r0ZLti7oGeoPTJdEKbr8TxFwbkGwvaG/QwYWsTQ6D/0598hu39Yw/08ECfKUBlBggzr3GrXOFvvXMZZ1f6AqxdfNjlcfzwF6shy5PgOEB7Xl6TFvvr2/QdjQV2Zc6YNzLY+FxjDzoRVjYb7uRE0MFw/mzSHsXDF3oZXj7TeGWjcPf3Z3h8pOuJWD9CIzxDSZsnUg7UFOMTmq9rQZ/FofChxSUeA/4uXQvqFcDuJI8AexNwL2vv3MLhC6AE3jZOCPAVHQAva8ArBvDgaeqn0WiKz27fK78MLFJV85gEuyZtaop/BRDXCtQ00Uqvup4C968QJhHUhh2lb+pdy/Sbut2tXezt7MG9h+IOyPMcSyvLrRKsq4LGzE5SThHHl94RAY93h5jPCeRm2vsT6QrTpujJd/o6f37dK5Nl99tnru+vrLjGxzr+OJ3N8c//3S8AeA3e/WZsxj0UMgb0UMAPXt3AoJuZcWkwi4BOQIGBMABhqrdhpjNhQd1tcMNp7LMFa6vpOsAPNuRBOCFQLq3zHY9wjB6Ozq//t3xtnjwf7vIMePX84EQb3hyOC3yzM3XfBmfhl84ZzZ41zl4KcoBm81EGz7Km7YHTX6UOQMNL8BBXaCOIyQYhVwjuNoFG4A4LtgntPQHwthPgOwRATPMWPKkFwIcdBQJ+9dkdzO2EKlYSjQBeBLJ8xGjD0AqATzwkfVLteox3tXz1fu1p64RqHFgZrY2sdbzq4hKAxw8es+1jjb/7CLU2X8n+yTA4xvHJyEgSWU3efjNbe0M/AY/8krqiKECKQFSYyXf6N89zrK0tRRICcEqa/Mlste1c495Id1GPTxCR6N0QeWD7+sEe/uLDr/D7716D1YA1bhMyYzQuoP2KjGDXcC31c/zOrU385a+3yloxn3gnABjgarD1m81n+ODDzzCbzsVEOgXg+++9ioVBz0RjkwSVB7qyVYCDJYHDMrcCEJx1nI2t+3DLR9ORC3XYq3RZXj/TQ/8EG97MCsJnD8coyEliJDbp8jVzRj6v6SuAlDbdG6HE+C90+VDkI+PzEarb9lSemn3gsbR17DjSyO+dSrJxiWUnwXtwHjydEHB9lLR5PkkjOgDtAZ6I8ItPvg6KIHYXcVUVFn1s3qpXAtYToVmEqLqXcfL0TgHEUl2chm99TbAMr6YufycEvbnMxtmNaMeg2+tiYWlRbKbTSK6qPJ5ax8CySzOcz+X58KPxBMPjCQb9jlPguLmTSGE+nwkL7vr6MnZ3D2Psf/s0+c6CNx9XafT/93/8Ao92jsSYNb+stq03w/Ea+oW1Pt5+adWZwzNX0HJdu9XnOdgrh54KH//yKxwdHbvdjyaTqb6fzdDvdQwHQAXWAHniXKhpK/bHNXt+YA18RyQV7iwRMF0eEvJfWO1iY6l9344AfP5ogomZTcqPrnXFZrfHhdcyeVeAzLa6IZhaDA/H3V1Mqr+8lLGrDQ+49IW2ztovbo4Hl5bzgy8HCuJa0OWaeJ32XgfwPp0nB3jLwD5+decxDg+Py7DeoDGNkVSCcCRyDF8bdjEcTWMMpjTHZF5q/atBN6bFV/JtI2CdS6R1mjh5sH9geEZQnsxyupO8S8k4TaUP6drn2pnr7Q8B2/tHfre7uZmIZ5bOkdLj8nrynR6XX11NTr57/sfkf/jDf7SGFtaCzZUFuawiAvRK6YL943//KxCRH493IO/H6AHlzPnWvXpxGVc2F2B3ubPgp3/tjHkS2jXn/dXXD7C9e4Asy5Blmr/9Xej3/LABmKkd8We5jp0cgOs0AyBnfrD55TveCT523N90Isw4+dIgw9WNk62Hv7c7xd7x3KXF4Mc1FLYMwcPMf7u3vtscx8oagKBgSyQvlDsALkpDAPdJxIYEEqDO+IOkFALcWYw0uMdM82WwlbPnjby+uDWvUOuO0TCenrYa4AlS9pWlAbrd4DNugPNPxUwfEyL0KWNmE2Zpojp0OSkinihes0htOi9PIkKT/sZsOsPInFTHTfX2f2/Qx4AdQ3uaHYwox1PrJOm18KFCuns4EvvZz/m6eaVn2PNx+cXFAfI8CsUvhCbfqieyvLSAi+c33XP4wfIx6/vbB/jn/+4X2i+zQM9m22cS6B2gA/jO9TWsLHbYGLbtHFgQVf7XacTA7t4hbn/9ALli4G72xs8yYHGx58z2dvIdBE+ua0ut3WneAui5bm9ltYAenpjHLQTkUNWm2MmBm2cHosPT1O2PCtzbnZaM4d7wocfgyZqlnMQAyITxCXlgJny7tg9ey/facPDdedJyB6DlxXmVkuCdgwiwW8Ky1p4Ycw/B3cZh4F4CY0jwFnQCvC2NZ2J5nxTgxX8irK8v4z//g28jyxQnag3w1Y8NW9g6sKoD5BrSNu1/rcQlQIuRJDoq1ezKtO16Ms1oQyWrCa8E0eHeYUgp7uXYfN0LVlGmp9xDKFsfZAJFUSAgwHxeYHg8YbvdmQl4VABKOXC3u98VRYHV1ei4/POvyaNlT6S/uIDz5zfQ6XCtwX95fOxbKYV/+m8+xu27u/4QGTthzdJkXssHPBDmeYbfubWJbicDiJnpXSfCA6de464wnUzxy19/JTT3TCmThkKeZVgc9CQQB78+jOSsfWVPs2Omdq7BgwE/6yBw04AP97/8xLvrZwfonWAcfjonfPFopDVwlobgZNFHwSEjcWILzAxZPeAp/01bU79HcgmmAfhHMLrShfGiGr8wwUtwbg7sHNzB8loN7rXau6Vz9xBl6Z6Ip9ke4J1Vgjzvixc28fu/+xZ8F7OmoCMPlSAV8awG4SbgKDtllS4mXEqI6kSfDGeaF2wrnqlyjvk/DREODw5LGq+rZwIGiwN0+2krY101PLk7AUMyE+8i7+vhcOJM9H4f+wLI5IY483mB2WyO5eWFWArdH/7wH62eIDPCPW2Qb3Wu4MCYLS5d9B0YW3CZ4iCoAFKYFQV+9L/9BLM5Oy3OgiYDeq9xesBcGuT4zo11f+CMhU9xqhyM6Zvwi19/hdm80Bp/5rX4DAboM4X+oO/lYxq9OI5WMRO7A2gSsultcgMNHr559TKHHQD7q5wMAOHCWhdrC+W9ouscEfDZozFmhfdQSr7M7ox6q5T72O6H9wFckEEva72QZmYOxuW/EPHrTPWhVm6v+B9r7wNQTwG7zBKj5DyagLuN4/LJaINOgNgh0NIx/rBpwst5UoC3YTdfvoTvvnurFgRiQJzC0JAuCfAp+kb8m3BIR28NhE0ESgQ9mQyU8H86ro2s8/kcw8Mh7AsSk3R1ffUUZK/hQKWbE7ler4NeryM1effBEIajidgYxx5SQwrCVG+3uV1YSHZwnthk/7RBvtVC7F6/ByLg3Nl1uXm/ADQNkrOiQKYU7jzcx//6r//GTbhza9UzCfTz+RzFvAApDzbn1/p47fKK22vemftNOjbdL766j8OjY2QZSpq8NdcrpbA46Flxmcneyy9+3eUn+YHJ5pbWMU3dn8BHet5AJC1NR06LXxl0cGntZOPwd3YnOBrPfTUojcb8SF02147deMC35e2UfOYHhG2hBX0S4OwB2sep6gRU/0mQLYF5DahTKANZcI1o/WgJ7pYHi+9o3T0sV5FOyWoQArzoAPi0mwK8Jf/WW9dx83riOAoB8MI7RV4R2ox3VYRUe17ikwLkOGFFeNv81/MTgW1Qtc6FWnXI6hRN9db/cP9QkIT3i8tL3oqbTLBtZpu/X03DO50cqysa2uzRsmFdjiYzDeJmW1u/jC4TGrzV6ktzXryr3BKviXuuQD4zkw+UAi5fOufuw4l1GjD0MrssU/jTH3+O/+enXwXAyTR6E68oCLPpDMW8cCBz6+ISzq/pSR9ccwbpdB882sG9+1sC1Dm462c9CW/Q78YBnmveVlN34OfH1oUGz8f2naZuTpJjlgLwcDfur/93c4XrZ/2+/W3c3nCOh/t2fasdc4dPzECKlUF72RIkNwaveNtkBt0VBzMbZFkw9PR7wjOQjgHyk1wl6I+DOrxYDpCrtPYnBfcQjF1nggO8fRKdAFlO1g+cJytr4vnl4RGAJxDmswJb2weIuRgsVANtHORi2EohURgthZdP2Ma3Ae1KgVoxqHeN5T3NvkGDCPHy0r7Do6E7vMbVtXzZsLLewDqdytMplKtkFzIk5HmGtdUFhxXzIp4oUYHj8RQFEWZs8l1B5Ez1evZ94ZbhJYD+ZDuWMfdcgbw9fpAI2NxYweLCwAGanYluQRtgmrcC/ud/+QFu3931HYHAXG/3tldKYV7oXpQ1tXzr5VWsDDpwoEXA1vYePvjZZ/js8zuwp9tZE73tXPhOhN4zf2HQ8xP/jIAWdjWw8+VtEdO91cIDzd9emk6/VKIj4cLJlZOCBvhO3h7hxzPCl1tjx9VxUBqgbc/CATr5ZXxWx1euofPApCiQxa2ns6AkM2S1f3kRA8qTXXISXjkN6wgyXSdrE2BvA+7GMwR3DsYI04SVyWNKbPy9CuAlgLPwBMCDgJ/+/HPs7R8hdFLxSyFu9FF4VrfTzQC+ikscBCP0dTybRKmQJKbFV4JxLVK3Qbga2gZaPFWGxiMTEY4Ojty7FSNdWlkqHUPbPGdNKcvg3cRlmcLa6oKZgGri2feHvJf9nUxnetlcQSiKuZ6JD7i96+Us+3nq6NmTmWG53E/KoMa1EjDPM1HcL710LtDKvWZL8ObrTCnM5gV+9E9+jKPhhGn9IdD7jgEROZNJroBv31hDJ1N48HgHH/zsE/zq029wfDxyQO5m1Ke0+SzDoNd1IKtBKgRsEhq98ZZj6ZDr871p33YI2A53oRbvyopwbrWLpf7JxuG/eDzCrNBoYowaOg2CWQJnAc93UDgIinEHm0cy2r3lxxCQLHOJWB5kJbmQ9SRXKc/2cjTk8iPM/CyLMWAXQA3PpxbcXbbL4F6nvXtQJhfH0zB5TgHgH2/t4+e//LJcfqJMg+Y/2X5S9DZG1ga+BHw2ipgmagfa/KYi4XaZeZJI6bJL+J8wlcrIYWfm8OAwIOf1RYBSWFlPHUPbVkKqfGzDVilgdXUxstSNWyRlKzWZao195s6Zn7t183zincahIqXJP/cg39Jc70GJCFhdXsLmxqoHOAVvriY2Gc+A787+CP/jP/sJ7FIucUJdpgQfq3ETEabTOXo5cGlF4fPP72E8mTrg9to7xJI5lbFnpbAw6Pq985mcTqNnJna39S7rtGg6uXVtaH53oC5M+HZ7XadDY9DNcHEt2iusdd/sjHE8KeIdEWZZ0Cl6y4d1zqJAPj7vJLhfHlEgo86Jg8VgXV2VBu4uy7IS7BkAxwAdHtAljqeBPQR3gGWvCtxtHHfPgJjJQYYOpXgR83yMN+fbEuDnRYE///FHIl9WVvYk7yKNJyEREPOlEscyfaodT6ad4l9DWBked2lMKfOrxJ/aPFaValOpmkd7AqzE+Hjkzm13704QcXl1BUury3GejevsyRwFGV5dXUC3k3sPlmTMrA/obdgLMSZPKECBqd6a7gt0u1Gl7LcN5O1RMV5bf+nKOXQ6uQMwd3IcSIOqMaVrEzrwyZfb+Kf/10d+T3tDzc3ofmzfT9ajgnDj8jr+4L1rxt9r7Jp3JtfGW1O91eLNeLzX1L1Gn4EcGCum0Ztg1xHhZ87rMIoCrLtMgOv4gJApwkubAzcM0MbtHM2wdTjzgqU+HCa7VehLqfH4RIKATJiyYQwmSVmAUQI9Q42eEn+euIqKJC8qiSE0deI8I7LobMS19kbgbuMIcG+gvbN4LhZLH4y3bxzbAzwA/PTDz7EbmOlPBeAJMd/AoxnAV7X0cdJ0TyDWuagFN8bz9IDoyUG5ib/PA8X9WyVSHXx0UJ7TQeKesL65if5idFnZyWRJ0tXHW14eoNfrQL4c/tuQO0V6NzNaPJ98R4SoqX4+L1Jt9m8byOe68XfgBXS7Oa5cOsfATwMaAe44Wa1lG/DOgD/76y/w44++EVo7wMDdWQDYMIAB/N/71lW8dm1THHwjdraDH5PP7Dr8TI/Hl8bibSfDjV8zkzw8UPOjZq1/JmQMTPc2L9CdCTC/86t9LJzgfPjRtMDXO2OhqXOLAZi/wOygM6C1e3LfABm5LNCB2Mx81jsgaKVdXyam0fZJSW01BOQSOLeli3UBuKYeAXVdDk2AnbwmbtuFMF4K3G26XDM34N7GPB8FcGe9SISzRuzx9j4+Csz0TxXgS1ybBLY1059Wum3TLJdBJct4kZVuYjyeUNTmEZIdh3jA4cFRmYK9iwBACtg8fxa9Xs/RtnOpgqsmC93iYq9ieZsp+yJoHey3XBBm87nf7Y4fWmNM9Nxkn3C/XSCvxzvkW0+kcPbMKpaWFti4NLktXS0ggo2ZK6Xwj//kZ/jm4Z4w2dvxeK/NW/D3HYgsU/h7P3wV59YXvRZvOg8O3Evj8kC/3xMmdWFaN/52Vj3fCMdr4ghkkRvhKI+HrmOg3J8OW+xlOLfS3kxfEOH21hjE3jPRp+QPrhNmg0iEK2ZqV2Cz8kvqvwEZ8mXk9sVRLlinZ65mGnqzP4n0KIM/xGerc8TAOQ7sYLIxni6IKszysTgeiD0Yhx2DoBNg5eRpwMrM6Es84wA/n2szfcFQvRLgIy7qXwXwVM21EtCSHYyQf0PCijTL/mnAbY1RJ490cn4NtPjT6DhMp1OMR+P6iArYvHAWOVtWd3plm3b23Vhc7GNpcRAkUK7jdPKEmd273q6TJ2LA7k318/m8NBRm3G/P7HqthWd+PBsQAPnSlXNC+yYir1XbcXcGyvM54X/64/8XR6OJ5w+r2cPxyRhPuw1uv5vjj/7TN7G+PHAT63KVgU+2U2DL55RePgcmuxwOMLq7YvlioM7z6cbyDaorHqbgd8YzcluaTAFXNk+2XO6b7Qkm08KZEUTZw4Csk9d/acr91yDhkw6EUCycg55Ni8CA0/yyHg0BYs/7UFuv0tijAB4B8jpAj42xW41ddDp4WpbnScHdFrUtHwbwLnaVed6mwQA1DvDlDoP1+5uPvsDunjfT1wJ80E5FYbKqUW4C8BVgXBsSZ1DJqaoDUZN8QFYunEqWybKUPvFOTJxpG+CudiR+mjK13n6WvSxf3sEEaevu5vlzyLKqScRNcxDSxePleYb19WUsLQ3KpP7DcR78m+E8C1jTvNXitSZv18fzJXRFZA984357NHkNWmaluF1GxpZULS4M8PJLF9DpdMx4vQYCZ6Y3IM019t2DEf6Xf/VTWBMx33jGadLMDxbolcLyQg9/+HfewNKgJ3a4s+Cau+1s9bh8v9fxvEOgjGr03BLBOwIMXHnZ2O6D3FbO0VxY66HfaV+d20cz7B3PHC8nN8NZi+MO1NmmN9pHC201QkXkwBnkRSYnsDTpg2whMWfNyRa4rPk7/iE0wfhynAiQRwHdi1M2xcPhfBzYTxncY9q7S48JbZt/DvDOKuDSjAA8y+/Wzj4+/MVtUSWyxIO7lgBfDU7NAb4WhGN8aj1rETbwSgBuC7kaRzptfg2jtcHzlKneuqODQ42CgHx3A34EoNPNsXH+bLmNSLr6ukv5DxZ62NhcMZPgUlJJb7+cF/4lNY8FFdJcDwhN3pvu56EhxbrnHuQbC5hlClnHT7wD4JVEYwLeWF/Gm69dw7mzG2winTJADzMBz4/TKwV89s02/uVf/Npo+YBdhua1ZvgwwAGvUsD6ygD/4A9eQ7/bcR2HcNa9nYTX73fFmLrejx5Ou/dAbv28dg52n0GGadM+M90HHQmlgOV+js3l9mb6ybzA/b1JPFAFt3x9PlPB7XG2CNfAJ95YV59keZI5glbO1LdmfJ2otBFUgXObKxQrDugIQJ2Yf3Ng90vhGoC7zWMM3BHX3n2nwNPIDgGjcXziAA/Ss+n/4scfe//TAPhoeMyjKSBVA3y8E5EmjMlcK0m85xHhXS6gSuypxalEpyLFN+Hvyy/WZUm5mlKpKzQCinmB4+EwgFFK3APdXg/rZ8+ENsIT911Cl+UZ1taXsLxiTyhFpCLi9zHFw8pfzI0GPzfHzlJ8CZ29IpPvfntA3s5gd5ovEL3P8wyXL53BW2/cwObGqta8SR4Yw8fOVabw5x98iZ/++h4cuDNzNzfT2z0O+IE259YX8fd++Aq6nbw8q95o2Fmeoddlp9rBC11lOZAavTS/ezANJuCJjoTehOfSRvthGwJwZ3vizkN2HRB4/kBogvc9VhV8bn7Vg6bglggOZCwGQHDDAY6zRXq2UY4LsHQMhAU4N7kofvHA0l8C1AXIhtqIy67LveTDyqQE7gxYS+DO02+gvZfN8yFN2GnQfh9+/AV2zMlhbQE+RssfK8GtKqxFgx4DvyoGlawjgZWAWfJvi0QV9CcCtRMiYQOwjnvHA0Lfw70DiJcV7F2jcpX1FwdQeWi2b5q3kM4/9wc9bG6uotfrVpMagcIvwH8fZVkKN7veLqeTS+i86V4DPwCHX8adbC00c08b5Kf1JNplmV7Gphs+b/6VDaeyqhwWFnv49ju38MYbL2Mw6AZg6S8LyP/szz7GvccHcGDOtHdupnez2jM7Lq5w6cwy/rPv30CeZbAz+P3yOYV+rxvsdCc1ba69lzV6EuAd28IXlk/kurDeQ/cEu9ptH04xnPjz4e0N5+R7leTKSQO4RR9GzSpKdwdIPCsO+VSegc+CysgcgC5/TqJ2BZon/4L0Yx0J1xGoAPZQaw+teU6zPiG4g9E6OW258DaTiMWJ0XgBfH9K+23tnBzgKRZQBfAU5Sx9IqhdJUMimQaeVZIE/jUdhyoGlbzjRVfyiYleX74Rvg20+FSpNIXYWITR8Qiz6bSGh8/reDhCMZ+HQekEqgQkQGUZVteXsbq2BJWFkSh4jjyaylKQz/w/kT+FriC9LXR4Cp29BzzAsyppjKEpl9wV/5TcuCkhEUBFYTR3YmBtQMXCBEGDLAAo4PzZdZw7s4a7dx/j3v3HADhQWpO3PtDmH/8fP8N//1/9AIv9LjIQCqUc2OiBfnIKq4IyZmSAlMLLl9bxt799DX/186+1bBbolR6Pn81mrsJmM91zm80KzAtfmQuDLl66fM4hPLdUcIC1+Q39pVNYGuRYW2hfheNp4falN4YQMW5ui8KnpFzj7+4VzB70Xn744jO5YC+8MefbvoHrI5gPg/fgrQCkPMjatLnZmf08kUvy4BpuOlhwiQGPCwlldsAb4wepYQd+kiWJuCntnach5h3YOmD0b7x6BV/feVTKXyqPpbyWPZ8ZwMfBL00YS7+2A9EYS1IQnXIn6DSclN+TRGtYLlXeBK3Nr53dhOhwsorh9T88DLdTPvnX3xv0sLK6iEz5FV2irkovXhr0Y9OJLFkBgpr70zaL+dxo7N7ibLhgPi/cM/mlTonx1ObuuQD5LNMFrbUZDaBEEgDdL1OV9QSvDJ1M4eVrF3Dpwia+/Poe9vaOmCneT8TbOxrhn/ybn+O//vvvaa2dCGSAvoABMCKdfqHjE/xY8asvbWI6neGDT+6L5XXHx2N88tkdXUEqXOOuzFp5ANAz8HUHhXVcHJB7RBdZd2Dow7MMuHjC0+Xu7k6gtWmfkNugRvZ1GODrCZFi4weP6EGrqFx+3IdjOkwap/WN12Qtb8PBATnZqJpWuRkAMskndRzwmpEJylbA7rzrwZ3HS4E7j8/LLAT4qk5ACPAgwsXz/7+/xAAAIABJREFUm1hdWcT+wbA2r6U8lz1/owBfVauV71At/2iOqxnXYXht5yGRJjUp4wjfVlp8vX+7oQm9l/3qxjqQKVGvYS6L+QyT41Etv/ij91dKYXl1EYNB3yRUU+CisSmb6i3PVCfAz43RQD+d6a1tlQKyjFAUGbIMgEGgLMuc2d64Jwb5p22ubwjyGr2L+VyOXyMAP/KmEb9kzhFhYdDFm69fx5uvX8NCv+d3xGPX7bu7+N//4tdsfFz/iqVpRlPXJMbYbAR66+Z5vHPzvNj9js+8z918AIh5AnY2vt8ch43BC6sFX+IHRqvVbZv/C6t95H7cprF7fDDFeDoX5gHFLlbo5odEus6fae68s+CN9R6ObSfJ+ZiPwq6jtyDEe8DKx9QUrnMAuHFzOqULTDa4JILLU/HcCfM+Dw1N7LYMyA9CxczyYoa7zWvJNA9RZloOgjPPO3l82fI04HhbPy+/s5QAeP2Vq6JUniXAp1wVZTwsATvx3kAzaWo6DlUpV8eq6wG0dSeKVBGtqkIbJEfl24IKHB4eMv+gHsx7cnw4lOV5gqx1e11snF33AB9lVmoJ4nlgwin27GOXeejvrNB0RG7ZnP50lVNsg41xXnyQ52Z1O95ii8ftBGfBRClfuG6Il+A1Yu25sb6C9969hWtXzyPPM6dxWw37g1/dx7/9j194EHVgCwfmbtIbpNlfKeDbr17ErSubDNC9Vu83yfEz8HO2kU44Vi+OnVV2Nj3/g0NSO6t9dSHH8qD94TOjaYGtwykchCqeX+cd3Ct2olxgmnL3JDpgYN7ul83OT7YVdmmesxhQ8FHBfRBP6sog7sG8DOhxUBfAzhDXAa8rGtuZSIM7LxMO7sTltTGIAzc8uNv0HF+WDk+DdwIYPYL0br58AR2zX7d7BZ4RwMdxtGUjT+n0JYvIXTKf9X4nAUGKCFuWJVrSlfmslDnUYqtoG/intPhKXwKO9g/Fdx6r+ycy1SuF5dUlrG+uygNmRJrhu1UG6Xi6JNtE0V55WrssHKSPSM9zM9GczXsCKLbz3XMP8rUCWi1eKQWax8BCOVMy6Ufr60BZ7gWvx/OzLMOVS2fw7ju3cGZz1exYZ9fVA3/5s6/xHz6+40E1BvTWT3mwtbTff+syrpxfjZ8tr/waen2fuXswLZ2vlXeWeGVfCDkhz3YCupnCuZX2Znoi4P7uWBek7TDoEPGSSgsKCU8H0IrdAwBxD/K0ygf7SowgBefDnjnU8icljqgNLwRXXHtHwLME5mCisktQW+2ZJBl4urZhZjLB8gi0fTKEybX1AbiXlsa5xiqtvTte8GlbegpoOt0Obly7GG10ZW3H67MeACua/wiStQLgsOGuo68JlGlXxGblXpbnBLKc2LXnWh2jqlIbJFcRPptMMSqZ4v2bNRmNMJ/N2iVgHxWwur6CBbcPfgVgl/Gb/cZN9UCg+IRhsPhhlDxkBtyzkhVYqeyFBPlKTV6Drs9kQXMBMhrg/MxuAxNw5nNhcuYauKNCv9fBKzcv4/VXX8LiwkCcXPdv//oL/PzzRyYdvvud3/7WbkELDsYGxH/37Ss4v7EkKyuDP3vegjvLo9fc5bI47m/zx8PtdX6tx5dXNHaPDyaYzOxWuLYjIRfChVz9WBP5HqcvXgDm1TcgXmr/jIekD24EkiYwgP8GoFgF1yXQDi8OvElA90CZ1NaNbE2BvTRb3hCXlsS5Ak2DuwN4m0KQnmibycG7jxO1CngT/muvXKkB1fYAH6lpSXIKAJ9ElThxhURpvmm8awuw5R5AOb+JUitHrfTn71tKlMhtrWujxcfq8nD/wPMgWdTDg0CLbyqYAlbXV9Ef9Hwk+cEEzCjyHHks8dDtZNiKuQ3amLUXoNIwb5ZlyPPMbIhTylzjyesp9xsGebvDnTLm+gJcjyehIZo4Lq6FQw9GQiM1/6yJf3VlCW+98TKuXjmHpcWBA90/+ctP8fmdXd+5CLR27ceAnoXnWYbfe+cqNpYXxJCANePbzoKvTM9XmO0NggvgB78037WFDhZ67c30x5MCu8OZNMfDWEDg5Qjvbea5Vs81dN/PIrhxe6uM83dVnBVvWDmlne2MB9YhIALfWc8Brulk2GEER3KCSz6yPwHoZU3dfePcOmDl9NGcqzXJRzR3rpFLk3sA7gFtND2CA28fB46XpOFlQ1hbWcKFc+vgLglzFPWtD+MkERSLtsdpLxmnUobIXcM0qvjH/CuCGrNp7tpzqI5BFU8nYV7mMB4eYz6dCZC0tJNRXMuvdApY27AAH4vnXwYxVFICcCr9D3ko+QgQkGdgGrpVHAFFxIZyrVavOUynUWvFi63Jy0JQwlTBe3VQHJCMhm0JlccMO3tbQWqVHMDPn9vAG69fw7tv3cT1axexubGC//Mnn+Hu48NaoPcHy/jlb51Oht/71lWsLPblbngc3JWfhCePvPWAbyfbZQjXyuvwTqawudzeTF8Q8HB/IqwBztxuhylsUXKgd4XvC5kPHykeZss+UuYAm0XvOkuwqAIwYLcg7kIYwCmynQAqgWvjv9BkLy5xWwJ0JEAdkGycH3g6Ib+41h5q7hzcSxPmKjsCrDzJSgPWdpU7FjEae//arassX+U7/tgWXIXPaQB8ClCr5GuSRlzACH0ib1WJ1vYA4hBTldeYv2eXSLAue7Vy1rsqFkf7h56G/O/K5kYNg7Ln2sYqev0eRGHECjDJg78wVKoL/qxXYXlPh2vw1lt9gqne7M3ihG3f8zzDdDqLafHAiwzyvIdjtfliXrgXkJvdQXDIQ56B5wWCPbbUafpgGjHTRq1f3smwsb6Ml146j9deeQm/uHuI/eGkYhze6NSsIwAjf7+b4wdvX8FCv+MAWphj2PiLE11JGb2/n3CnWD42l7vB2E8zt3Uwwcx2nogwPB6zfNmyZ71R5rS2THAzH8kF6PL2sCUKXpm0XLjzY+DN0nHAHqAl3z9fAK+lNwVSwuvUFfIJriiYs46Ekzfgq6OGVoCQd1lrB5PLAS/nGYC7LdIouCNG603vQntnn5XfuIfxZvKDgCuXz2Bxof+bA/imjqpjUeKpNp1AwFQ+o2lXMI/lu3We2yTYIkYqt3HaeJqVvpHA4eGRObZVUg8WF7CyvlbJlTutwfNdQEneU8S7ljeXm8Qz39hLD0Hbyd5w7b/GO9OuGXzI88xNBJxMknvevMggD1hwt+BVzOcSqAkOSDngAUBmjkZThlkMLJ0jq8XyjgHrFCggz3N8/M0hxtOCmc2NfJliafNJegaOlcJiv4MfvH0Vi4OuP7zGVC7fDtdO9AvPi/dWgvK12Mux2G9vph+O5zgY6XkOVBS4c3cL39x5hAePdnVDbpHXwT0vNnKF6cCWa/0sqmLkXvVmZR2iKCiIg+j3JYAT5IArRO42JvvqpXQSzBFLjgL5GKi3AnYB1iT5JsHdN6glcA/aIJcTFxTR3s2z6DwQl1/7KaVw68bleEU9FYAPvJpgZ4pRmljeNcTnqnzG/KshqSI0ECyWhZQslXI/ay2+HpOdK4rCzaK3349lsriyjMWVpdoE1jbWDMCzj6ZEx+uf0cgPBsmyZy4LtPhOljHLrW3D9YmpGvwJeZ4JTX40miSrBS/AZjhJAX0mPZhOpzOnHyqjrbkNVMBMyMqsa9dNEDLSBUnIhMkZsICvY4uOgtD6ddhkVuDDr/bw7evr6Oa6QpwNwGmy5B+EdquB/vtvXsHf/Po+RpOZ2BUv46DHOhcKQK+bod/J0cn1jPxOppdYgPRZ7928fV+sKAiPDydQAKazAnfvb2E0nkJlCnt7hzg+HuPSxU30el0QaQuHw30jpDMf2UBXXubj4P6u4lAGLRfuC4yCD0l3JtjmOTYSA7CAhSMjRVUfSa1rElfKJdMPmdU3vmEZhI2axCxKxYnSh+AuZUrTkYjjOhnG49aNi/j5L2+jKArGqHRb8jwJwNcxTwF8WzkaAXxV50HQt3sBBbikxUhFbhvQilWqzuK08TQraSvyPTw4wOLykvQ3dbCytoZiNsdoeBzhrrC2uYp+Xw9pltqXUj2G95GwBOjzZ72Rm/ZyE6wNgbPIZr55pDmJYdiiIIzHlTvXDqsCm7inrcnvxjzF+DDT5qejkWiY/GlAHmw1tnrgVAwxVVAhkRN9YCfiMeuycKPJHB99vY95YRDLdRpMmlnmDqexmfH5UVjod/CdNy5ieaErxuWVAmazGQqzde+gl2FzuYcrmwu4tD7A5nIXqwsdLPVz9Lt6F79OrtDr+I5LG7d1OEVRAJPpFHfvP8Z0NvPzAjKFyWSKL79+qHcH9Jksl5Ut9Gh5scmOFoiJWz08nTffe0biuxOmZdLAzdHdkArTO7Hv8AkuKWlq/B7ue+eXZVLS2EO5TIySSR4GUK1WDV9UJoaJx+OkzfghcAvtnZczfBwbwZcl2Spx6fX7Pb0lM68zeVvyTIGFlyUeuRH4puJUypGmiXoFQlalEfOvCKp2QQHEAbN5mrxNPZFAJ81IbUbLEWbTKcajMYsswXd1cx3dfjg3SWFtc42Z6CsSZh+eeGtKL2Tk3tH45zxT7rFr92TJsqDt9xfmhdHkNd3xce3k+cd1BHXuaYN8VEC+ixwf4x6Pxh5wAHDVkjf4thFSCnrMGEwzdmZv2aNyajwg0rRxPFgrDMcz/PLOPojxFZo/qzRv6PYWgn63g3dfu6gn4zFgzZRCLwOubi7g4voAqwsddE5wuEydOxrPMZzMMRpP8ODhDqggs/MesyxkWku+/3Abd+5vgQoSebWO14e/I7ihEoHbBHl4vP1l29E6fwNsSpU/SQU/cx5gQCuQCB4GW/5VmOwZ5kWvGKiLNrkFsMdmy9vGpxLcXbkE9KKIKEgPLi1w+cJitcDC0rQ0r96Sy+mqgMbD01MG+BSYBq9h+NQcaKvT1j9l4mr+ZYJyvkk+VgpSGVAlRYVfquS4XzzNSr515UrA8EBujiN+FbB2ZgN5t2M8lTHR9+R34d5bwnw+x3Qyw3g0wXA4wuHBEKPjSVwAkR4ly8M+63lWhE5u90LR7SRfju0VRJjzWez2tUWdFj99//0f7VURNHFP21y/FfPk5goOmOPjEWzFwTT+igiUZcbiTgAyhzZ5rvQ+wIDZjyWiuZt/AXYZgPLavt1Ex7qD4xk+vXeAVy+tuKEDCz4AQGbMpSjsFDQNmgr6YJt+N8c7t87jF7cf43g8xdJCDzcvr2NteXDSsmzkiAi7R1MMhyM83j7QecsUMmtlzQAUZqfkDMgK4OBwiOPRGFcunsHALjmxnanIyAQAeTiNTRsQoOTq0sUJPhVlI3gwEbz4KAGz3AsexORr0c41ImXachWjWHNYthBIns5PgBELE2URlA2JkECGYKgkbKwYkPN4wvTP4zCazY0VbKyvYHv3AFEXyJEgOWWAT9dOLcDXplNf/8/UTF9Fn+hYxN67VomnwlvHax5hfDzEbL6G3B0rqz9yIn0WOxUFlldXsLe9g4XFAaCA0XCE+XxuDoAp3EEwVFA074OFHvroOt5JOVOdDfObmz33u93MmecB8uAOuOFiAMB87rT7o6O6vfixXUfQxD1tkN8DMAvTsebrUJsfm5nf2uyu3PF/TsM06+YtbGQKsFv5C3hX8kaXsde53aQ75ifAyJDvHE1w++ERblxY8iAnQM8APQVHsZqB424nw1s3zuFwOMbZ9aUTmd3bOqUUlgcZ7twbatmgl3AUBtALkAF3o0Eb/9lsjttfP8TZM6s4s7HqS4J8J8q//yoYB2dIbNbLEy8sQomHa+xJkLmOAvfQjyQ+MAK/9Fh+q8Yy+PCbgX6MNg7MIiQE9hKvVMcgAHf2L9ZBoBpalxZJOULtPQbwthNw68ZlbH/wK5Tc8wTwFQDTKJ20oPXpJ8C2AceScLFsxMuibTehpkOQfMO5X0UdN/CL+pMv+p0Hj8yEYQIVhbd8BbGPj45xfHRcUbEVZSMAmxLlH6Ypn7MsQ7eTIzfKp7MkGwo+mpnpQXhkWYbJZFY1o966JzbVA0/ZXP/++z8iBL0Rv2WsNNVrTf7YtOW61ScDzn4CGGAL2ZpEvL81CsOAio/j9tQxjtgWbN4ED19JDPwfH4zxzdaxoBVD2Nb0DV+5tgMDAP1ejnMbzwbgrVtb7OG91y5ibXngl/Qp5eYSeNO9rwe7w9/DR3v48puHmM/mAPi8CJNnZQ6WobBbBOdfaiSVB29rcvZlaRHbp6OrOTJMIMqQ4M0G+rdy5nx4QXYUELDm7Ln5XZrgSYS7qGJ4ITLWXuIl22kxdACeTTvcANcmkcs7Tz9G6yNx4I6Z5zl/W66W5trVc+j1WJ+d0zqPsnvWAB+T5yQAn0o/lXY13JYJ2sNzknPaL9WhrUu8MTrXhZcqpZbhfDbDbDrT2rkB+RO5ymhl0PY/ZdCPFVwn17uqKrsfCuCGccX+KEZ7J3Oc7MFBo/l0UUt4W/e0x+SBoDfCAVA/e21+MhwH48HQjT4zk5d6RvBhlr82rwdapL0JAN89c+ABOdhSCniwO8KD3ZFLn2fBWgYy1jGwcnTyTMyqf5auZ4YLLpxZlkCvGNArv1GP7ZxkGTAcjvDZ7XvGnESu8wMAZLevddliDWKA1wJ5TBzey3XAz8DGwx95fmTS5WAYojTJ98ALlb5KIB4BYAelEUAX7DmwC3+WVgTYJbgyQAUPrwZ3D9r6nytNQevrB6bMEUnDx7H1wqtSL/+5ce2iqNcaSDx1gE+23HVYUhfYBIBZfbVxMbEp+kDlMOMRl+d0wC9Vh/FPqqKeG/i1pT0515ScwQcs6CL3jiZ8BgaDnmlDYU49tbucZu6sEmRmO3MAID0On9jdLnSnosk/bXM9EPRGwvF47j86jmvMAMx2pmTOf9caZWbWr5OyvRWjMqoyD77BDOBBja+Jt+SKMyBAZcDdnWNkSuHcWt/RcfOzjU/Q2qxeIniyAjtNd+vKJubzAo93h3ocnuVTm/NNOWSAKrwlYj4v8OU3D3FmYxXn3LamZC3yrG2xhUCYTOcYjacYjScYjfTvcDzF8fEEo9EYr9y8jHNn1h2A2Hpw/TEG4P6eZDICfsmN2wtQLblmzUeT9pIShLEGO9ZcNzfJu9Ayv6DB5zJJeh/RFydrzhvQCRO+ub954zJ+9ek3Qe6eHcBXg2+cS3Nsr38JTmamr+DbAODbcvYsm4B2U88qWVLhTXpeVek1/Shb+Mf4kvxt8ubkSmGx33XKEuDN9YA9J0S3UZnKgPkcRAr7++GJekl3Kpr8bwjkAQTj8YA+jagoCqg8d+O6+sQxvgGNcQRfsPpRA72wBHjQFzKIJ4KdclfqGAjwV7i7cwwCcL4C6In8SXfPg1MKePXaGczmhN2DYyAjFIXvKCnyGzrYZzcrlIDH23s4PDrGpYtn9JrOyRTjycz9ekCfYl4UKApCQfqs5Dm7LwrCvQc7OHd2nSOJA+ZQd1Yq+A5ZR1p0qoldDIie1KUayVI4e4g1DLHoyclXIYidIriDvL8A7hJdWXvncQjA0kIfFy9s4t79bXjfWD5TQb8hgK9KKxC2LfBVv3aJ+m7hUvJU8kl9DDWdhiqy09fia7tAT9GVviZ/H4B+7CVaWuyha6y1uu1UTgkCUyDJtqlEODo6xnQ6R0P3woB8xFzPn5UD0tmsQDGbuVmVnNQWlMVsgjXXa08dRmZ3YMvcp6fszHyeriHy4M7mCfiU3ZMCjNmecH5t4Pw40FvT+PPkMqXwxvWz+PlnD3AwnGgrCKCtIqbcLNi7fpEFegDHowk++fwuiqIQoF2QAfJCo67oKMF3FKx1YGvnAOPJDL1OxWtnG3MH3JCmcuUx3n5uwhpQUQ7xhrK2eU56pCChPbA7ChcgeEfBPRUnQt9Se/d0AcCzDsV3330Ff7b7UxyP4ut84wAfNOgvIMDHpKh956oAPhAwVmTNYbU6pDEQt2FQGR6tnCdML1WYIXGq1LR/t9vxlROtg/CL43kh9Lsd9LodvebdNJS6nfMKqW1ItfJJoDlhb6+xFg+8CBPvjHO9kdj6eO2vw4qiwGxsN8nz66dtoRNJU0ieZeKjc0vdrDONkgdzgvdglLYzELwXLp6Sno/2x26M3tG5zsTzBfDW5ZnCrSubbv0mADEhxNaNf1m56cnXkQrqz9K6QmTj+yHoA4T7D7YhusfkvzXvLbtY7nIjA+Ti+glm9VdsAJ7CNFgSFHjycXUxaBCw9bLztFl+HG85/8CnAfcgxtwRxpNyWWIO2pVj767sQzqZL8vLygDosci//XtvoxPpsMWAzTavjUA3wqwaeONcGqVVBcCldJoKzYNqCdKUVeBI8WDPMhE5yTNVitwvBZzNk4mlks5mVQHUuIqomVLo9/l7KwG89HIEz0oprCz2oJTSG+BkMNvWsm3KweY/mU1xDg6OMJs11uKBF3HinQR2Di7ejSM7AHnQUObd1a19nis/VAsAxLVyBKYA7116AWyY2cbWpsVl9iCnw7YPJyWgf07x3bnlxR7ObSwxcNf+EtRtwTHQD4DfxvIbAilH4wvCxmeFQsD9BzserNh4vgbx8qQ7uymOrbcSToNfKnhOg3cMxFHiK/9coxTycNkLOxUu2zI9SJAGSw8ufgju1AjcfZiU1cvn04Ph6+9tajyOpwl5r64s4fd+5002H4XnkbsAHppipSurVFgamsJ2OurqQFaQxoWuhqEyQVvYiue9Pfg1BuJm6NwgPFpBT5heqjATDXrJV/u7vUAEXeSef6DseXmhZw6XUeh0crHxDW9bOeiDgJ2dxP4SaffCaPL3/W1ZiwekyV4fUFCuJHItvnkG0GHr6EsfoY7ggMet0FLWdO9N8F4gZvPlpn7XW5Bp7B5N8WCvdkOD58pdv7TmTj7yPU0G8ubZHa5g/xxwSy2fd4KcFYDRALJ4D4fHODgcOqsLwxXxPVlU9/DKkFgFPgFoJ5T2JHjHtHOgrKE3BXUEclnBUlq7B/cQ2MHAnYOsjxMDd/stCHBHSJvQ8lkeedr2OeR97uwavvftV73QJfcUAD4arznAp+I1IBb+1bLVAHwgZAwjU1CVEsuzbJTxcgClyX5btHgAGCx0Ga0E8MhbJFy3k2NpoQeVKXTz3IN7JlcthZu9HQ7HGA9rt7AN3YO2EWLuWYD8l/amrMlLQqWU3tKQOaudAbo+/Fa12uWZNTErsf7dRNZ4beK5htCCuQMmFi9QPr0UOlCM2StgfzgVGv3z7rqdHC+dX/XLO0xGuBne55Vn1oJ32WRf5lGqWB8PCvcebDPzOQC+T711IRBGwFYO6BD8Iv3qq7ITwC4vSnxIIAXqFthF1yHMjwP2tEke0bieiQfsMgA70LbgbtOwadr0yKVa0t7tPfgzKxtbBlevnsdbr19H2T0lgC8BYFPGIbbWNOmuPMqhVThSC09SiHZwVsp7oygVfrUlWZ9gKZwS/g35NhGCEv7Jzoj273U7yLMK2OMfIi9s89KvLQ+cctTr5uaceCW0edtOWtAvigL7hyPMEvNXKtztthFi7lmA/G17w2fWh87iwuHeIUpHjWkKDc2iAdCntgEezD135TxI+fiOmP9yiUg+C0mF1ur5jZrPlnwu3PnNJSe/NNWzvHETvfMPGLGyKo3hm3/cz7oHj/Ygjo1m+CzAFGDAzRmbaAy7habeELxlVrimHQHzENCDdFLaehtgD03yDtxZ3DbgzuPrcAnuybH3kuXA83fceSeACK++cgU3Xr4YlGhQYKUyj1aEjFsbqQFIhWFPCPCNUDyFSU1cCshTLy/nT4nyOCngIl0b1Z2HKn+K+DUUps7VRF1YYKb6AMDjUvnnpYUuBt2uG2PvWlN95jV2dyhN5vch2dkbgqjAbNTq1Nij99//0aM2EVLuWYD8HQBToKwhh1qfUsDQrCG0jRWPov2Ub8gI5khWp5s7oBCYxDEiBlZR0z0DLGWtBWEMfbe2yMw/L4DrdnKsLvZLZnipsZv7YNhCaOnK7z6geOkoHhbwVMB0OsPjrX0JluZeVA0FFxhAWjB0ZucyOLe5ZDoREQSgg4GxB9Sytp4GdjC+QmsH50EufitwtzSOPm6at89eFtnMpUz/jrcrBx3pW2/fxMULGy4vglngngTgU/BcleZpAnx1R6IG4AMhS7yS/NPAmAL4uAANyo/7VXQs4pFKldQwXpV/KgNpQI65LMvQi024s/chX/acZQprywvukK8MCr1cr+WyQ56ZVSaVH+48Hk8xNODuJ5U3crfbEFe5pw7y77//owLAV3V0FjyOdvf0h+I0fvZyK17uem1i7s7zNWeiW9M9/0RFT8GaeQNPdxt8AmWjQyA3sNR/FisRT9edXV+ITCo0wKz4rnYGxJ0VhqR2bm5KDRnZ4Ij5HsC9B1uS0EJiALRCQ6/JU7T/VuE4rie1fqFd12nqHtTrgD2ptZMFFyrxbgzuNk0rAytLXs5NtXdJx3FSdiiUAr733utYX1/xmU2Ue9zzOQB4FxZnVBunmkDc1L3PbdKuTK7Grx3gtuRd8q/Le9tc1ke1dbkw6LIXPCYPf8lIPK+vDNAxG5xlmR6D7/ZyvzoJ1jwPKJW5XUS3dg41g6LAbNJolzvrbrchrnLPQpMHgC88oMSXz1k3PByC7DKDmvomAjqZPz+OayXlBt+hjnwmSVHCoyBaGJwrhW7nWRXj6bnN1QVplofMe2jKF/5B4YYmeR5uvxfFwwBsbe9jMpn7aoiAJbEnj14eaFLA3PQK1fPonw8ORagEdQesIhkLpnFgF1p70A49KbhHTfOw/kzehPYemudDi4HlnecZfvf7b2JpMX7aYgpkRKc8EYlCj/ApwiAVVpdWXMa0qwW4JtiVSqNCm6Y6moYZjZFV1EgiUrlNbSVT6uWIPjYpUO8Uggl3IaAn+RL6vQ6WF/sG3DMH5nrind0aHNpE72bUK+weDDGb6+M/aTpvu3zui1YZrHDPCp1ux2A31PCUUphO55iOx/qlVfzdjb+UFl8dHWuz78nJAAAgAElEQVSMvF8YUUU1PmUaKv/skYqc9NZXa7QL/Rwvouv3Ohj0Ol7TZpPsnAHeToG3TkkA8+CufcMOgYki4nD0e/Bwm8GULGuB79afAaL24l2B2H3NH+dJMs0ymMNx94BeBnUnOgN1DsZNgN2DtgXrhuAOC9Io0dtnL5/JR5CejGfp2PdEsXRJ8O51O/jd33kLvZ4cxkq14UkwsWUl4ibAKcKiHEYpUkFXC26pyCk8iviWwsrRq+m5XwTgK+lrPSv8k7wpGhirrTTrk5S7jZoqPe3f63X11rKioMOv1v7wDwA4s7aIPMscoNth5m4n8zPqM6nNT2cz7B4MHd9Z9bnxMXe7bYSUe2aafOiR2jSGiDA8OnL3+pd3DwQkIMtNxXH7cazCg/p0j2xGPkGClHtxFR84MM+GcKH3YoI8oE/IC5u0sFaULBBv0vde0bosg3/ZrnLv4Y6+dwDDQJQDI2zZs3XwAiz9c+xKavMlUpYqIxSAHjTGXB6+6Y6QXcjKQmIgZvkwYG4M7iWQNrnibVYC3Ou1d+vrZQZ8uYS8lxYG+MH330SeZzJ/wUtQCfCleImnqs+9iiiaXpxZVWyKEFD0gcphdaLVpJ2IUsOXYj8BRTxy0+JMulS81AtySolXafEpRRIAVpcHRhmCO8UzzxQ6eYZOnollcvY+Uxke7RyCCs/2BDPrv2gbIeWemSafwPSoO94f+sYraHy8r37782CCl6dAfOad5UkyiGDH8vmz5OeAizWa/e6LZ6q3rt/N4WbZC3t7XCOXzxLoYzMaPbDHm/mDw2McHvnlhyHYOQBkQKsCAHbPAqLlH4I4cQAPNPuI1By0haaeAHUPxFJjD2490Ea1dvi8BXG5/DKOKQ/Gw5ajqy9brpH04to7S9/KlOJt8ry+toTvfPu1cieQPE3URcEn8VQDkCFRFZacFsBXydAO49KMqY4mhY+VpVrlWcU7WmHRlBrkqL2r0eLzLEOv2wnSiNz7l9fEUzizuuhmzOdsd9Buh5nqgx3vDoYjHI8mLA3CtN2kO+AF1ORv84eYmZ674eFQflx2KZzx4JvTEBG6ufIVZP4rEyGsfs9TejpN3aXB4hJJTZ5xskv4XkSnX3zvAqW95FLmeImGAfhHnUfBu/e3HSpWAWw5ZnBRzZWIV8nfxfcaetTKwCJxUE+Z4l1bkgB2qbVLcCVDmDbLJ8A9or3LuCbFGu2dWBqehEQ5WOAjAi6c38Dbb16XBQyf52gFlOom8VTFIkIUJWd5j4Wl3+EwnebyNUuD1UOSfzm0kr7W0wZVSdSYTZkqRdxEwFaFKl37zW/0/bn1JXQ6mdfUYTe90VZQu/ENmBZPRHi0vV8Sczb6/6G5vsod7uzKD4hYdQQNGJRZRgff0NlJeOQ9TXj4gpjniMYvKGMWAeP3mzov/jRcvxsZaqjITuvPi+Bm6vO4vFP34NFOEE688gygwYFaDLxtUo2uML7lHQB5FMzhgQxChjKoB7csay2BnfM4JXBPmuZtWuafo3TlHKTB+Lu4IEavaV5+6QJeuXE50rAGLooFiacEk1Rce5cphZevXcKF85ssvQgzSibB+JUp4nES1DUJtP3e6somLOAY2WktmYuRpTm3bllq49o6VQAG/V5Ax+4pkN/8Lg46WFkaeG1d6bNS9FK5DL0u2+0Odn088Hjn0Ey2I8G/5Rr5/fff/9F2PVkz96zWft0HMAIQn3LLnFLA7uNdXUbmmFlyY+IEZzQnr1V28gyjyRz2qFem6DsV3fJwEU2Ya6BUCt8SgaYOX2RNHgj6LxT4WZqIht/4swxRNngcT6Z4vHWAM5srHhzLURyAOLCx/x1hcF8hTiOZk17p1izesEWAIEIYNtCiSQoiUEBMoQxcG4/wEPQhbcnszmTkskXM85y3SA/Aa69ew3A0xp27ie24TxPgSRLZu7W1Zbz2yjUsLPbdsck7O/tRRvXvSZmIog+p7kAkjvNs8P4+YzN9tEOQJm9O1USQqoqvYd/v681rdH2JFzjRRdNziM5vrDgzvG4A2bkmCuh2OmbXULtgW2E8nmJn/6gsExEmw1a7ot5uQ1znnokm//77PyJAfdSU/mBnD/PZTLTwYWNiHgECOjlfryU/K9/2pxtbCuMGDVTg5dyLrMUDwLwo6omSrjHMp6MaUL734DG41mzhXGi5iavSRB+5GvGJ/AnTQfnRv0OEkrYOBDLwPFrwDPg5rZ1CHj7FWs0dEDwkfcQ0z6wlHsOsHJ5fmY5kGxp+P4bm3bdv4czmWvJ9qIIh3s5fvnQWr792LRnO4xL05k+vvXoN7737GhYW+wC0NenNN69jeWkhJU7SkS+ceBwpTBLH4umk43i2kbasir7W0wbV5LwUHK24KFma85O0I6kS9P76MBoSoaXbQJs/s7aIhX63fPCM0lp8lin0mSavzfmEe4/2JGP7fU1nmM1arZH/pA1xnXtmu7gohQ8BfK+OjkjviDY+OkRnfR26jwQwdV77KT9/u5tlolELdVHrI9t1s7rekROIlFfyGRtGouOGs+1fUDefU/T1D7+d0nMp575h4gDT1D18tIedvY+xsryAtdUlrK0sYWl5QQglzeblewR+rV0iSpITkysaHGnpYkAg/SIlGwBGyJuYh6jLlObOWlxi/3gynFu19m7icv6ldL05H0rhu++9hr/6yUc4OByKbKUAPiyPWzev4rrZPnc0nuLLL+8FuC7jnj+3gVu3rqLX7fo3xbQheZbjrbdv4W9++muMJ5N4goGLAXxc9Eo0TQN8DfuTJZkqXebX0vqVoq5PSSRaz7lJeSZcJ8/Q7eoVRCGQR74YAIRBv4tz68tmebByk5DttGI9Lq9n1tt4mVLY2hvieDyJijk5GlUZZ2LuZ62oa9wzBHnVSvD97X0sra+Bm+sBBtikgZ5gZjcqJcLgno2vQGrFANxoRGZihRgH4PfgLAikFAoizAt6YU32dqMG7soAHyBKaDr2XQP9RBT4Rt7uEg9gNJpgOBzh3oNtzOcElSmsrCxidXUJ66vLWFtdRqebS43cJyt7Fw0+qMbfXA2YJ7LkIsbaqCcFdhfyJOAexhfgEgduDqaxsXcpm6MSdHme4XvffR1/9eOPMBpNIqUaAXjSDetbb17HhfObzv/mjcsYjca4f3+7FHcw6OPVV17CxuaqSV92WqxMvW4X77xzC7t7B85PljXPgyZ49GgHh7aTEsiZzk2ahofVgueTmOmbIXMi3URI3Yd0gk5LI3ciLT4iFAN/pRSunl/1R8QaEr36SLf+CmDj8Rr2Z/MCD7aCYR8m36T96XMvJsjnefZhUdgPLWjkA+2biLC/vYdLN17SeMwBGmxTWsammytM596jIELOlnAR/Hi9BnbZeQATgUw/gOCmBcBp/gH9bF4gz17MtfLTWbW5PjlR0QBqDeyBU8SwuMrN5gW2tg/w6PE+5kWBgghLCwOsra1gfX0J58+to9frlfhSrVwyK9KrWXMUb1/KgJtsByPALvmmgFp6cPJwaEDyjXU4mIbNpK+0EhDLZQ3Aly0GXsJ+r4fvfed1/PivP8bUHe4UtAnsptft4FvvvoL11eVSmm+8fl2PhZqxdaUUrl45j+vXL0FlWRTg3eRF82+w0MfFQV/IKWQm+byxuYaPPvoU42gnhZdm1Dsep4kmfQpm+jh9zXtf6kDEmcVqMMn5KWvxCsBg0JVpie+A89Q1fvHMKga9LpQiKLJavAd3Ozzb73T05jdma/X7j3Yxnxf+5eecC8JkeNxW/FMF+We2yHtlZbGdJr+1E3xg8QbG+CDP/aK3sCGQWkkQkd0KgCASaTsaMLMwQXQsXjR3MBzHx49BpYY7BE9XLhQ823CKgE3Al7OwV1FKxydwcHiML7++jw/+5lN88LNPvewnuSJ/Qi5KX/xN4OPqboyafHnx98mnHUvHl4JMy5a/9/AgBPC6C8tcjMezinQxyOfEphumZ+vegyWJd6FkObDy8PyzPNvyW1pewHvvvuonRQXvhL1ZXBzg+99/Mwrw2tCm8M47N7G0tICVlSV897tv4ObNK5UAzwtEtjF82CK0QviwTifHG6/fQKfDdCSSN6lWIe6fjlPVulAtUaJcGybQVp4SVbtCaO4aaPH9fre8RblNPCw4ApYXeji7vsh2sLNj8RlypS1JyqyJ73ZzdyjN0WiKnX2/s11ZPsLkqNWku0Oc4kY4wDME+X/xL/6HR0rhgX2uW6qx91ib4OzHV4jGRd8UngId81EXARpx8xz/qO2TJJeNKPF4iS+kTht+Xt14MsOYHZHLQcr5AaK8gDII27gc0agIwwPG4ADEwE2WvANEexXs/u6DHezsHlaC8ZNcoVwpQIeNI4sAAtQDkGsD7OFkOgc8CXC3HSkO7hx8fXz/tov4QXoerMnFk/XH0ghBk9chK1eb7Y2NVbzz9k1E91YgYGNjBd//3htYsFq2e5d4O6rH1r/z3uv47ndex/LSApNB0kqLRhrgXdkEZcR59Po9vPb6y7qTwoXmeeAu8t3wgHgYJ0tQUOqRkjSSPilVIi5F/WNc0sk2oE4L0Mi5I2V9xTFuPA+EPANeurjhJtplWcY2v7E73cF1AHrdXM+6B/DNg23BS2ShIKAgTNstn/u5nqh+eu4Zb9emPuRPMbO99RseHmNqtgIU5VewimKNa7fDJt/Zhovz5R9qLG34xqjkz75+38Dr++G41azJ58btH1ktXgJLwRtzDhxBS8TBi0GVKEAPZJBxIx+0tCTw+jGcRUdBp/OLX33JUua/AfOKK/3HgIldIaBbgEiBOgsud2oIpfJ3eecA6rJseZPg6+qIvfTuXa4E9/g3IWlt3uT3xPNm6484f94JICehSA9EuHB+E6++ctX7mZtLF8/gvXdfQ7fTKaXLAd7e53nG+EKUkYhrZXN07JnJ6l93/v1D8FxcWsCtW9fc6YxJRxWhFWEENulLyfPKs0whN1us2isL7jOzDas939xu3uKnHFXIHMlSqkMQjVSVqWaeifhxWi5bJ8/RiR4axgvby3nl/Jre3MYCO1h5mzK25ZgphV4nh8qARzuHGI2mUn4hH2E+nmhTfnN3qqZ64BmOyQNAlqkPiei/IOLj7/GXrSgKDPf30R0MvNmFSE+QI5jxdHMDcpUjHGnQyvm6eIIb5CcTnYjcTErX+Nqxf/ZB2Ml5urnSAUfjGYqCjNnxxXF7h2PfODuAYMBiwYyhWthgAkEjD/grbCA4D3Zf7lSh5MFpOIDee7iDrZ1DrK8tM/CNNEVt2pBaAgYW1SRRjsn88jINCMIcifJgDZdsv0g8c+4yPmSeHK8QUIN6D9NhaVTNsOf5tfcvX7uI0WiCr755ABBw88YV3Lh+ScgnQVjmo9Q5qQT48LcM8PYVFWUV1IMt97X1FVy7fhlffnHHp8FdgxdFtzEeXOQ8YSptM02OOpWUi+3yKdtYnUAhvndfZrH2uOKVjn+zJQ5J6gqSkLbZh7yw0GAs3tT12vIAZ9aWdGkpcodyZVkw8c48dztayx9Nprj7aFfwElhv5p9N262PB150kO908g+n07jmW558p2fYr50/Bz1JjlBAwSyIcEBfgJCbeN08w3ReOA3Eny3vX2S7YQ5BT6rQySoX7o6tNfGJPNi7ePDxC1I4HM+wuiBP3HqeXUGE7YNj11ClNTCvwYnvLvQL/L2Xjl9E6EvfcQFt5rd8mByOtNT4aG3+b/3gbVN//jqxS4FtPXk0RkwWigSEbKqAXZP4wpZlmQZ3zofLUKYnEbdcDwkZ6sC4gverr1zDZDLFmbNruHj+TAXPoJPSGuBZ+gmAR5hOwJeCPJw9u4HxeIJ7dx+Jcgrrx2C5a2GUWfrrwMQ1fwzAQyx3q4DKSangjt/Kg6Z0V9hOTCaWkPveC2vNMuVABSubUhajTyUX/TBbfKwJUv6tKCj0+x0R6n6Dl7OTZ3j50roBcGPpALmKUMpsgKMZm+1sO4ACvrizZYA82qq5+/FveGY98GxBnvr97oeTiQd535CzF4yV086jbVx945beUciuYSezba3R6K12bntZ03kBq2l7Td2kZ+KTUgaAlANvu2uRm1VvZYR92e0aetsRMPEBHAynLxTIP94dYjKds/L22jxvnG1jYHv3RHpuhA+W5mPtT4gCNIjROxYoQE6j4PFZRHgEN16sQ/Dg4Q62t/exvr5sGKYbjVPC/kpuSVCPBD4JsMf4NgF3fR+EJsA9RcvBkMuQBkwPqjJNL5tSwNtv36znGfCTaVQBfL2JnsuVAviwjGwaly9fwOh4XN5BT/lZ2Xb/c7gWBG5sl4Ox6xSwtpHvCwLSmqUrJkScbRu5Fys7pfxEZcXCoEy6mXKh+pPS+7KDtJIg2womBSXkaeqodJN4jruFhZ7RxvmLwt5vxu/li5vodTtQMHZZV08wM+d9nVlNv9/p4N6jPQztqXKlDwFOiweA8UHrmfUf1pO0c890TP7v/t0f/ALALFZhYSNERNi++1B+UGZoI/WBd/NMA0Zh+JEj9mPNlj9LV4wtW2CD58HDPLj5dI/Gs+ia8+fV3Xm4760dtkET+WSNuygf+5EjKD9/8Y8qpVV7Wh/IJ9VZmoLi/iGvn//yS0fD6cMr5cI8yMt2OsrcYvTgVDauuUocGH+K8HTywfKRdRTyhY3LpBBysQ6UfL8ZLapoy++L69SJ+pGAyr9X70+M3r9bIv2QJyvfELR9Z9SXl5Q7kDkpLxcilJPlAf4bCeWzY7lZnulJXG5cF24CF5TSM7bZ2Lmd6JVnyszoZmPsxmRvx9rtmDGUv/egFAtjY/RGjlzpcX0nnwv3cikzi9zSa5kydPIceZ658WpfXgkXawhCakr4J7xYzQHQWvbSUi/CR7xYAICza0vYXF3w58MHk+uyzEzAMxPvbJ1O5nPce7znX+SogOa9KAjjw1Yg//X77/9ot02EJu6Zgvw//Id/OFYKnwK2EZHh4Szsg70DTI75iXTxD91+bN2OGWuy/EIQc1+9fBYNHvstN8bS3/rNC+DhXmuzzG/EzexwBpHWiDkwuB46wUwMZQDgNXr+rKN5Lb8AXAE5AGP2ev/ZsYaVvQfyDeD8WbnDpGX8Hj7exaOtff3c6CJxcc7hVcVHyMz5VYC6KBcGIClgD8Hdvqyljqj4RpqDuwPXCC1KtAhkKXeW3TfK5JF0/pvkacp2WJaL4xmmK/Lsy835k/NhfvUAL54jdKGMtnQOD4808LlJWx4YlQF1O/nNAjefJGdp7Ni8nTCnL9NhgAdleSE5Cc/5gc8ezxxvD+4G9A0vB3YM+J1ctjOSZ8hzf766DRMu/KBbuzgDDvDdbo6VlUVJz+uN0fa7uTHT+3qy9SImK5oOQK6A3BxM8+XdLf99yJdQezEtfnY8xnzuVzA1cB+0IW7qnvVh6JRl6ie+QTCekYaFiFAUBfYe72hPO0ZkGndAfrREuqfbMUAvQNk+s4bR+8HTm9oqXMMSNMZgDQEHPSLsH09xPGlVob8RRwS8deMczq4vMkAnFAUxkLQNM2uMWeMuwi0NA4vyWnfWYBceuGwYL2twWsQAhyRfIw+faV93NesIiKRK6ZVAPZKSveHv0UmA3eWbvcOON2wzFnS8mJwQfODlsDK48g3KlgGYoHX5YfXCvy1wOtYWCmD0+QUkX05j68vJ9ywA3odE6IRwTtM72h8CRMjgAdpp6hbUQWVA57Pfmdaf83iZ16Z5fK+py9ngzpIQXpnsVORZpk9WU0pq7ipj2i3vNGQM/H3ebGdGMZ4O8H3tBi7ycaVoY9GZy/MMa6uLeuOaGB//AgEAbl7ZRCeXeeGz55UBfb16wZfFNw+2cTye8o8jIqB9bwjjgyFaur9oG6GJe+Yg3+l03ncPAbjH/HYebOkxIKD8YdtGkgF9N88coWvoXDNo4/hGinc4HFiBpFZYGOAyjZaVxzcIOuzhXuuZlM/UFYUuiSxTuHV1EzevbOgA2/A74DJmb9awkylsPrThyh+8HCXg8DKymr4AuOCi8JRGDhqWjHsY9+DRLh4+2msN3NaVRAmAvEpD5+Uh3684qJ8U2Pk77+oEgVZsZBcdD1GOJHiA5RVB2rYDh5DW3fs6l3S+nHwcXw4236JuIwDs+No8sXK2T08F4EVHIwLwZvKcMoO1GYD9g0MDlgH4WfCE1qDBTN7eRG/N5vDmY2YBsMBr1297kA4uB+blS1mzc+hnOw5Ka/oc0G2ngmvvgj8za4N1CMJOgc0jf+sbu8QHa2szyxTW1hYZf/YhgFhK+u7S2RWsrwycxcRaKng58I6PzfPO3hBbu0eB7CQeQyx7XkD+mc6uB4CVlcX3t7b2EFtGpy/pv33voX0CQU+IsNvMFsT2rCcCKYVunuEYcxDI07oPWbklda4NVQSx5a2pNwUy6ZgpeQQzYc9MVCkIlAEFdA8dUDgezzCazDHoPX/b3BLpU+fcygAAF88sY3Ghi59/9gizkTXjs4ZUgBxroMnXGSxIQYKjBwmu7QpogrMimDQLBoxc20dBurw9uul76286Bh/94jb+4IffOp3yakhACcp0Z4JKzEnchM0ST4txifHgQBiTMUi63MGuoC+BJKMU7WoIxhTECQA7kMMBa4o+wvNZADwUkBFAVjs17YYyEQ/2DsxYu5zJrsfICUrpViLjE+eVn+hlWIrn/4+9Nw2y7LjOxL7M96req7X3fQXQaKCxEQQBkoK4SpTEkcTRSBqNxhOyPSY1thVhyjNjh2XZngkprPH8kRThaMvLjKiwQ0MrRI9nNJQE7hQpEgWSIIiNDaCJbjR636qru6prf+/m8Y885+TJe++rqtfdQDdtZ8frujdvbjdv5vnOOXnyZLwxHaURNlCKKxkN2ySGrKa+dWlXUTRmNlVSiHSyRBc1DTM7afeRUzoRjfeYFvjICXm+rzXaKw/Y6oPa4JzDunUjaPgk2FXyGe53qDWAfdvXG2M6BvL4ZeJ38oDsyZJ03aLAK8fPYeO6ESFyphoZL4lAyhjqE+SXAHy3nwxrDW+7JP+7v/ufHAUwpRFGikhxKf7q5SkUy50EImAAyWZxmswDTZ9AadUJnMDLlksw30+emzZJGivdE8VBNzjwdnfp2kIIAZYYyN/x4RYeP7QDG8bb+n5RdZ/eS+7tjxicQ7B9V+0/6dtMgqRgvrsBddi+jtoEu/2Ocb1iXCfpLk5O49LktezZjf7sTZ1Ub6X0crszgDLvV16mUHphmKA83hBX+b9EIHOmqjRuy/1ezlNqO+rS6/umtHbuwLav1IY6MF5JPV8GeJK2rlQmmTmubYOJuzmAl/Vxu40KChERkBcWFhGKoGu81olKw0PB30rFdu3XOzG0Qya9Z+X5JMknaRx5WT1+rlRuylM2qEtSty4l6PsAdq1fnleM1ux7ZW2zhoRe1eArBuoVHXcnjI8Pm9PgTIbSOAIiYB/cuzlK6bZtgL6fM+9qNRVHT1xEp1NgcKBRratHCMvLWF7qrPx+eXhuYuLwW2LY9XZL8rR//3by3j8D0M/USe5JOoycYadTYPbaNNZt3RzT8E84T0Iqg5CsIxWUEbnKwJMyUNorH8tIkryQn3gdpf6ojhNp3hnS7nTbXQDBO2D9SOuOPGNelhccc95OCVi8Gmg28OjBHTh+5gqOnWF3wgbM4j3/GGGDeV4BNu17+ZZ14FcCM6T0CWxCVk78hR7lxYgjr53Eh350/Zr6pZcUvmKeVbLUSeoxvnxDPeJtXA5oefZyKhjAotK9tD0vyBJByrNV0pNtc7mdGXDn/WoBPmtPBXw5VUanS4BdB/Dleuvym/avFeCj5M6HWAmxYFFZ1n5Fer0+MxvV8ICuj0srHOvzVXI0DLZuj5PZWEM7kpMum0/e0FUu7VN5L3WcI8mlj5h5EUleMkX/IbHFZHyJBCKVCgkEOJ+EAS5fyqEAkEf+TBg2ob8U6VEIBCr7wl5lbo6OtTE42LAfvyZfGgx7tq/D2EhLGbPyZ9B4uebfxSvXcf7yNbRaZQc7qfx6Kb7vrXNviaoeuA3qegDkvXumKCLIa6Ry9S67BghXL05h3ZbNAINqBGYArCoKjqI6iFVjzYbHcreAqJIsoDseVDJafXAgz+XGmZuYA8sseIeAyPnG7ARyrMdycfJvHBt8O/txTYGIMotPBXozeUEE54B7dm/ExvFhPP+D85hbKCBSfRmIs/VjA7hWyrfP6vKC0jfPpXxDoPQdTFkckZcXOQ8CYfLKNP76mZfxwP17sWnD+A301xrTVRG59Lx8Q9VnhGrcDQC7yVWft09w7wnAdfWV6Gud9F4uv3/1/FrSUG1aMv/VpzVlEQO4AUWZ5woKnuePj/m8c5i5PqeSICASIcERL+Q5W6T42+B0TOcU4HssXdv985AyJY2rDkMF27rynE5BaUjsA8dCELMFQi/BNC8uc8Y5JwyOZxYF+ozL9bzEibRkKuU4pQfM3ngCwTOtWW1eEUaG2/EYWSKTMP/gdtyOj7Swd/v6+I0YU5xcO0p+DOTHfbbc6eLI8fMAgKFWs37i9ghLM/8fB/lWa3Bibm4BFtBFcu+1Lr/voYMAZL1IxiYrbngwExGCc2gNeCx1CiOlU+a9DsypiuMbfS5lIf6iu1ow58mEIMSBQcrZxkLGRgbY6O/OCXK0L5w476lPJ8t+zjlsGG/j/Y/uw8vHLuLkxenYDxSPepUtd5khZC+glrQwhwuZvi0beuXSnPxkHltQT+1OdefAdunyVVy6fBXbtm7EA/ftxYb1YzfUf5mkv8K8rgJ6Hlv3vAS78f+aOlYCdwvsGdhKkpsA95S+P3C39dyo9J6uU7m9y1wN4KvagMQsmnZTZODVyZYEltDt4rhzkt5hudPB8uIS5JQySengQD45uxEJX0rRtXnLQBggTxJm0lImAOI786eiQSQgqP7SFpr6UpUTlLSbDWf6XsonaS/TZidzW2WECNmU2q/0NqMVSfuhzwIlyd5HGhwKdblVeiVCuz2I4YlTuGoAACAASURBVJGWfovshezY4f8HGg0cumtr9O7n0nKLd/oluF+Ta3PP0vyRY+fQ6XRBAIZa5mx6MyHqpHgAWJrt2+huYvUkNxZuB8iHH/3RB7/7xS9+d5kIg0Ai1mC1eYxL8VMXL6MoCjQaDT0DXgi7qMyjoVzkjAcHGiB0YhlWHc8aADHIEwJAcAghcpQBST2fpHqnA4d4kIi/etEibBlvvd39uGIQzhz6J0nvVpoHQfuNSRgGGh7vvG8HtmwYwfeOnsciTzoriUfgTwCd1vHjT6R62L34Jp2utzOBkLKFOU90OCcS/Dib0JGpqJKFi5emcPHSFLZv24hD9+3F+nWjPTprDf25YiT1Tkd1l1VArGRZo9ReBvcKsJcKsPd14JuqLoNr3mD9PhWpPI9fOV0PQNb3KreB0vtS/j60Unk1z6V8ZXBFsyfzQQHdqYQfp5TRSTvg+sycWs+L33lRBkiclQ4JTgE+UxlLXeY+x+dcdBeMojxZ6mPEfd15JPdCCexFFW+FJolPAlBsRbSniS/XICtAxYZQgGoACNBnQk+tEEVAVOlD8sVx4rxjmhyyMTU4OICxsaH0gtngzz6sXt+/fyuGWgO5ml6/kUvqeceGd/y9Tp2fwqUr1wHEY8wHm+JQ3Y7jbMCny063X3e2P5iYOHx59WQ3Fm6LJP+bv/n3Fr/0peeeB+g9VnJPajOGFB5EC/OLmL82jbFNGyOAIFq0x/VwwFuJHpETazYciiKqm50DggdccPEvIvgENrwgcBrich1bzVN+nK13TlX1cIkhGWk3Mdy6HV2Zh063wJvnp9HwDvt3rmeVG4QeIVuPZ6CHS2o1xwkJcRLs2jqODeND+M6RM7hwZbZkeIdcioeR1oXQStqQgD8CMnS/fOTmodsUA2T7XrK8lx9g8mTgqaxAbbhwcQoXLk5hx/ZNOHRwL9aNj2TPV8R46nmzRkDP79YC6lmONQB7zH6Lwb2u7hJNLYOw1kT1ddSBu217GZBtnnKaCsDbeuz9SgDPE8QnVFMVvTVxd6wq9Co8E+AjvZm9Ppes6nkOOZ+kdylXJHkB5TKQJwk/VpK0AlZNT0mtL6CqbbTaT1IJPQtW5S91kD7K+0vyG02DCDyidlfgRuzHuKzJdBtg2sJqfFn65IY7/oIaL+v35hrem/lOGB0VgM9GrfmjIxIAsG/7BmxeP6zgLf0syycJ4OXbxfiFxQ5efeO8ljc0OFCpx3Q2SjFYnluo9v3K4S1T1QO3B+QBgLx3EyEIyMtP1OL5ujwRMHnuEkY3bsjUV3ISHfFAEik8OEKr6THXLdL6FyVJP7AKLgqacQCIUQkpeKfhmAajfFCHNKActq5v990B3YLQbNTx4P2HIhAuTF7HyYvT6Hajsdr07CIeuHsrBps+X5cDlBmSYU/KBfBz7kNHwHB7AB98bD9ePXEZL75+UUHdSuvJkY7K5CAKSjAIPBcMU6AExaSBMATGDgBmfATLDBBb9nPaFVzWazh3/grOX7iCnTs249DBvRgbLXnIqgm1T2i1NIYs9yiaah5S6YJKN+Wi6ghJDs5ZQbcc3O0rkEmkWLpG6d3WWwFkUw6Z/7L0FUZiZYAHKG29dZQkTJfmhEr4cs8SZ1Ldxy2pC/MLLMkLk5zkc6uil2ziV10lfySAlaU/FcBLEn925Wz7TCAD+CUVfoAxsLPSt1I6Rm995vSsCum/tBYP5OvwQg1drgFlyZ+4UKHxIZh+B9JyK6BaAOd4jjuHwVYTjYYhUuVBqZfxesNYG3fv2pjW20uaFdW0mG6SZy+8dhrdbtDyhtqls+nBYzIfdHq9cHW2/FVWC/+vBPkwMND8xuLi8j+yUnwV8FP8pZNnse+h+3JOkLJPzpJr5B4HBxqYXeiivPYeYI3tZNCmMgLJIbIlxiFuyoe3cSAMDTSwfqQ/g7uZ+Q4uXF3A6NBA1AIMNjBQe/5x79AtAqZmFjB5bR5Xry+gKARAI/Bdvb6I7xw5g4fv2YZ1o63UP6Kr0ghSjp6yR477MtZ36K4t2LZxFN988SSmZhZjfyhIWyA2EjhK39QQjPTtqtI5Y7tK/rXMgGEW1G3uGgIRcPbcJM6dn8SunZtx/717k4SQJVxTVBa7EvO+IqibmzKw19W7Jqm9Ln8tuJs8NeBu869VNd9Lerf3GThrXDkNVWj56pqCVQDeHCdqJTxL9AXh7Z52OAdPuRy6uLCAZDGf1nQtsDuDIgrwXuVGY4SHDKBVspZ8AqKm//LmOZ7D5huVRo4wESkTr6tLeqZxLMCDADRE4GIGJjBnoAoF7WcHcmnnSyIzTgE8UVYo05SBPcXteIGt8xw5wEc6MDLchveOT8ezH1T+pO/cGmjgoXu2JydELupr1dgOZL4RqTMcADh+6hKmpue095xjozvTq+VrOxZBwMK1OfQZvtlvhn7CbZPkH330wF8988wrSwC1yip7Aff44eIgm7p4BZ3FRQwOtWM8ZB2ZpWrmOj1L9QNsCCMSYJyxbA0qhndILnIlXhzmRBAXa3rSbXkR4JnjhMP2Df1L8VeuL6EIhOn5ZczMd7Dc6WBpYRHrRtsYGRpQP9DiHrIbApaXCywud7HU6WJhsYuZ+cX4XiCdaLbviAgLSwHfeeUsDu7dhH3b12VAn9bnY/8qY6MEC4lQMnHaMD6Ev/HkQXz31bN45Y3LaQ3erLvD1J9J+wL8FqB5ixwsYGf5V2EGSJiBvj8BiIAzZydx9twkdu/cgvvu3YuRkV7fslrBqoBek6gO1NNlf8CuaVYE6tSOKribWmuYgVLRFQDWGtfECKxNerd5VpXeLXibd0ppcmZAjeYzdbhYkTvdJqcwxGDpLJo5s7wHYHR0FOPjI5i9Pgd1M4sE7CI5Clg4gqry4URLqKQpqfn5lZMxXWJMBKf19DoTSIzHNKRroQ82WpcetVBjZS/toGSUJ0xIMna1z4SmIqOv1XV47j3TH0ITZAkEZuudc4R2cwADA41IL5wHuYBQJElbhwj300P3bEd7sFmS2BOQy7Uc5gP+PtdnF/HqiQt2EKI1OMBLKHZcm4FVmpfF0jKWl5Yr32aFcGZi4vDRfjL0G24byP/zf/6rsx/+8D9+mgg/VpbgQwhwLq3HAA7dboGrFyexdd/uCO4eCC4OdpHq5VsQ4ra6wQGPxeUQ1+yptFceQHAODZKBh2g5z9vpItAjrh/zwE4SfFTbNT2webw/kJ9fKjC31NWBRyA0vMfCUgcLS/EYXledv7HTygSwRMwsCFrPUkfeuISpmQU8cs82+IbL1uCZlCURHjyRQ+z35A86pmk2PN7z4G7s3DyGrz9/EvMLyxVAV9DnBXZpZxX4I6EpiCoOd5TJg3Gco8xA6X4lxK3vyaxPT5+9hDPnLmPP7q2478BuDA+3VwTx2pJ6gXDNDdVE1FW3ZmA3cbY95dw3A+7mcS24VyVqU1cJ4OvKzwF77QDfs8ysTylNKhIregF+ltBdkoZlG52AbQQLq/ZN17v3bsepk+ewtLCowB6fJVBRS26Xgw3gKgCP/I+WlW7yZ0CiI670xPZpYl5S/4qhneK8gDaAhuR3rHIXAcDQ2aSWN1I6I2tS0/P8J5HCLRMQ622QuLtOTI8Y4oEcRkZabA/hQD4wnZYlXf3oAIADezZi07phftuqxF7W2DhD45579SRCoSMMADDUzrfO5WPbjrt4tXCtb1X95/rN0G+4ndZi1Gj4LxRF+LGyyj4OErDRHK/fIODy6fPYsnc3GIuTpI4IuiE4eJ+k/FbTY2GpAIW4nSVa4ANRWgd8IASfVO9w0QFGktYjJfCUjPIU6ImwccMQGr485VYOl2cWE5dKrCp3HgMDA1hkDjBbx5POIiU3DGyJaNu/6ldfJhQD6JmL07h2fRFPPLATI0OD6qYX3IbM2h5JfSiaEmukA+ewZ9s6/OKHDuFr33sTJ85d0z2udWv2sMBPXK51VQthUtI7yTnzEmcN8uI3N2lqIbK/QEQ4efoiTp+5jL27t+Lggd0YGmpp20zC+vwrRNSBem0e3CSwc/veKnC37evFTFgiWAZiW78tpSfzYNtBqRypIru3ZcoYlqYoeGeDWNXscEZqNgCvAr0BVgE4Ub03Gh579+3EqRNn0Ol0de3Xcuoi0VvmIHsuVRupX/LZMgSPJa32NFUBHqX0ti8rVvwy72SNHfKOqezMSt7ca48aAYso9a/GgUDepXuzdY7gkiqeGYDARlOt5gAGBwdAFEU0pcmCEU60sYQtG0Zw146Naf29h8Quf4X5cs7jxddOY/r6ArSX+L+4Hp+N8HRbM4EX+1fVv+Ugfzs3doeRkaEvJICyalhD/M315VPnEfjoPlUT69ROBEOMswaaDTNorbrYAmWqO+Yz91xH7sY1AcvWPrfNLXcDrs4um/JSm9pDrVg2/4raX0ARQkpHAUUpPREhFMkgrjBr5NOzi/jKsyfw5rlriaN1dvA7Y5jiMuKjwRC8ofYAPvojB/D+R/ei0fAJkIMBc5g+NdJ6/Gbpe5Wt7cFGdkIUYL8LZxZG8JYFiu5/3zx1AV/+2vfw4vePY2FhKXEfwkCVf+ZGk5p/0uj0zNAJouxnmpIxRalEU5cpR/sn3eoYFsClcr7SO0HGvJ1LirE5Q6ZtL9VXmcvSNluHlGjLlz7S/pS+M+BSqq8O4KV/LSiDxLUs6dj20joncl0swSHf4uZEW+jSHEngEOdGs+GxZ98ODAw0oK5eHbuAdelEufLhMOUT3rxJ6205/BeueriMA3I3reWfc+Z4WnYna1ziOqS2lk/CU/euxsWtuoV19nkqw8bb9qeT8OSvT2nlWSOlE/e6IyOt/BAZuHStx+BGv/SP8Dp8+RAdBXqfXOvab3D2wlUcP33ZTiYA8XS7QfV9sroUT0WBhf781XcAfKWfDDcSXP+qzlsamh/60D8+4b3bLYOp0Ug+mtN1PLPYe48P/O2PYnTD+uRjWT4g/7WDxnlg6voyOkVgH80+fmjzPA1A6OlRzkHPhHY+TXIduACGW038yP1b+nrZM5PzuDS9mDhNZfvjn8nJaT1/2OJqxkcaKqdAUCKGlnkJNdeBCDs2jeGJB3aiNdBUJggwhFsJbrwIWkfOJAHR5uHq9QV8/pljuDA1mzEryfreMjAhiyuy9IGZlQi4RamcoghJvR8IXVmbe4uCb3js27MNB+7ehXYrN7Ckmiu57TWrVppvCuAmpkR3So97Se0mr0mgEF0ui7Kacsm9Nn0JbG3pJXC370W16UrlK0OT58natEqaDOBVKk/QHzE631YlOC/SPiv8kkvrTFLO80rJC4tLOHf6vGkHqQpf6JMEZyZ+pnEAeLkApTrzoIZ2PUdaBbMqacW3BJlklg5IpDLllKcpM945o2o0i0HylNOHZMCLRJsi/Qrw3mPd+hEWDuJunRBCpD0FgRB0i/S7H9iD8dFW/IapKxnITZ87VuMzTb8+v4ivPPMaukVhmMbY1rGRNjaND+u7pMFmGc4Ut3RtFheOnun5PWrC1yYmDn+4nww3Em735m7y3n2RiD7eS2UvgyOEOPivnLmAkQ3rdcub7JmXySke7sSKvj3YwPJ8AYJDcAGevG4Lifvg44pWgIMPBOJTiGS7iQ/s8IbLBQHwDlvW9bcWXwTCpZlF9ZLHb6iDskuE5U63flpbwC8Rx2QEI8/zfewyScuAf+bSDC5dncN7H4rr67K+JiuR2f56VlsSrMrT8UCPav4NY238nY88iKdfOo3vHDmrywb5NjvTNm6zldqUcSDRmEDfI2lQIHNMic9bGUIRcOLN8zh5+iL2792Ge+7ehdag+LHO/tSGVUG9dEE1CfoBdv2/kvfWgnu53hyEqZSvF1jXtbcM+oahKLeBUnqJE8AWCC4DJmNjBeBVeyWDHk7VnGJ060pgIdPAxwIxPNTCjp1bceH8JcMwAFY7ptiePdeWqD2ANhSpXvOGXHey6O8VmJRqXjvHotMZ0sfE70H8skpHWe9uneiQ7fOaePnODSDzYR/puNAtp4Z3IAFeh4ITDQ+3VNHimUp7aVfc7wznCIfu2or1Y22zxMgW8/mrJ2bL1PPMC28wwGuPad+NtC1Tb0dhlfkG3ZBV/VuuqgduP8gH59zniyJ8PK6984q3WYd3zqt3OSLCpVPnsOeh+6C+5gF2McuqK0AtxAlAq+kTpxiA4MU3MyEyFgEBXtfgHY9OYR7sdjmEOKh8ALZvqNl2tUKYnFlEtxvgHFAwwUjrfQ6LC0vxKFhXM2MNgGfRTB1VckcCRRtnfUJbz3TziwFfefYN3LdvMx67bwca3jGgJ4QXBzoyg0iAH4jWsCFRv4Z3eN879uLunRvwuWeO4fK1uQTSpk3lE+0CE52ceBg1tnlfy/i9nVqoUAQcP3Eeb566iP17t+Oeu3ZhcDCfPqu1p4aOoJZgVJL0B+z5ZQ6CKeotAHfzUNpj4b7MoObtrkrmtt20hjS1AO9cmmeMeD5ilnFu43LpXqX69G5WRW8dR0mFYs8yOjaCrWEzJi9dSYyD5QkU8A2zYO+RpHSLUhWq4GpjTW9zskxAMEyBjoG0Ts5YznkIIcTT8YiQPI3q/BdGwDAOSNb2PKUzRoAICdRh+0T2w8cKoprdY2hokI/0JoQiAnPwAQhMm8lh15Z12L9jQ+oS4Wq4oVZ6z50WAc++cgozs+Jj3mq+ojF0a7CJbIwrHUb5AkTAwvT/D/J1gbZv3/iVM2cud4mouZo0T+Rw5cJlLC8soDU0bDhLNQ8DQTziQbfANZse3SJE4zlOG+CAENd1NI8jFGy8Z7UEFug9HIaHmhht99d1l6ajFO/M4TDWmnduYYkZG6oF+vJ6LUcqsGdgzxM4sCa7bGcgoC+S/ZE3LuHMxWl88LH92Dg+VAF6wEj5zjBDhPyQHiZ/O7eM4e//zDsw8dJpfPOlUzoX9Oz47Bcl+MKAfhGS5b24v8223iHlv9nQbxFFEXD8xDmcPHUB+/ftwN37d2BgoH4srArq5lmlHTXAztFZxnpwrwFqjb414G7v68BdyyzXUyvlk7nmnFkeU1pNGsVJHYpORGV2U2skdVZNeccgoJ7sWJCAYb45rYCEAHq0xnfVdCz6r1s3hqIoMH11mqVHp9IoTB4npbrUfsdpnE/e6aqSfD4y7OPymLHfyErcCoAsoWs6fikKQMOntDrnuEKZf3JiHYEyR2Jxhw7SHnnO6JjZikdUc98SdNuc0Nih4cFIg5n+eB+t9ePJgIQCDmMjTTxy7/Z4njwlewtSDi7ZTQgLJQzX8dOTePPslVInpd4bbg+YfrUoX0J8zlYsLmJ5sa+tc2cnJg6/3E+GGw23/USVT3/6v7kG4FsJqNLkjeuuQYEpBEK3W2Dy9HnlHi14xRLMoGIi1R5oZMBmASMEc3paSCpjuZf09rejTyl+YbnA9flOz3PZi0BYWFhS47luERBC+hVFMrZTQ7tsbTpfu5brWHaI76GGe2TAltMFwtTMIv7sr1/Di8cuxkbz5LIESOLkDGbn0qTJfs7BNzze9+g+fPxjj2HX1vFa6/tkzBg/nBIR8zxLR0kT0ZXdAzf5u9HQLQKOvXEWX/3r5/GDY6ex3O1mzAeZm+yfqdxKPPG1q9oLZGXG1GnsQ0FPnkihWq7OEWGPTPuktHIeIGtDSs+12Pz6jqY+aQtq6jHppG+yb2/mv87lDOBTGwCocxgBeDGwA1zmxS6NVVHhi9FZzJ9OIhPJnIHBpTGt2nbHc8AbIz3OE+OBzZs3YGxsJD933RnDLxdV1XKdG+d5ZkhKRm1qcJcb17nyz6WfpJFz7T3YGNBJfd6k4zPjYYztxCDPyVnwxo7Jl+vwel582fit3P5kwCfv47V/mg2PofZAOqee+z/ZX8XzNd51/24MNBqpzkZ8h2bDodGINl3a7y615dr1BTz3ysk0WM14lbiRoVY2zpSJrWB8vJifuo4+w+f7zXCj4XZL8kAU1r5AFN4XgngmisDlvUdZmg8BOH/sJLYfuIvdP0YVcyDAcR7RdgUCfABaAz5xiQF8bCypJJ+2x8V1+cCDLTiCC3wNUiOcHRuHV3idarh0bVGXEwiyXBDfyTlgaWlZDciEyIjReFmmJzPYqDbOMErCrIDSLgFArdwtMxOt8oFnXjqNN89dw48/cRdGhgaRjqVNKkpxUSnr9+DtLyrNk2hCCFvXD+M/+Ol34LuvnsOXvn0cC4tdWJW9gJbaDdi1eHmnDCwt4N3GwNV3OwVeP3YWJ05ewN37dmDfvu1oNhtZmlIWE7GatJ5yUboslWUJVZ6tl9QeH9fkq2Eushzl+kugbe7eMundNDupvYX5dICuUwNm/R0mLo5l2Svvbbv4eaYul2GdeActSwxVrNMaYQAAQrs1iIX5haSiB/L1eWm/PuPGmPPnbVKiSK/KQZ/Lf4ZoBBMp/WVDIGLpN9Uhx+jCpX3uKiGDtRr6jPfEmzLkNXQ/PJ8up0IUqvvhRfomAtrtQTRcPJ0kaN/Jd4t0+uED2zA+0krd7gkgD/KRVuiyLaXv7uDQ6XbxzeePmV05aYzJSGg0HNqDTcUcQ2g1D2wU0Y2A/NuiqgfuDJCH9/6poij+e53YxIOD6tfmL5+7hM78AtzIUBwAgaLrQlENmdkp4DzY9FjuBrUSF+M3OU426NBkwA/Cqcdr76LqaNO6NoYGG32938VrC2oFCkTaoAQKDnPzSzroKpo5E+rW5HPpiJi5EWJIus1Q4sTatSfgE+H0xWl8+vMv48OP78e9ezYp0Kueztk6Y1w8vCdNfiC523FweOLQThzcswmf/cZRHHnjUmyrALpqHqDxdVJ88lP/NgB8zyqo9nGn08XRY6dx4uR53LV/J/bt3YZGozROeoA6P6pUvCKwV/9UgD0vV+4rpdw0uHOqSt4KI1EL3Hn9vdNRpV1J01QysDPA6YXQOyoxA469vbHUTqVlMpHgWZCI9MSWTykRZCkrMQcCTcmvPSDLBYLDEfBtG6C61fKSnYPLXNbWrOhVBIKYTuat4p2GIAwKz2lSgSrWTyCkac/fw6zDxy6h7PAZYjW9MEVkn3uKPkuQGIDgY7oAEX5c3Juu8kNslycH8nE5dd+2cezeuo673hBTb17SMl4E1fQ88+IJzM4twQws5JOMVIo3kchmVmnMFwtLWFroS1XfAfDlfjLcTLjdW+g0fOAD//C4c7g7bZnzqNtWJ3GPfvDd2HnfPazqkX2URv2jf+PEWljqYnq+w2obVhlZNZpzcB7VOEnPBOWxezZh39YeR5bWhJn5Dp47fsVM6hhEfQgAFy5M6da5FFz1siIRUUWiz7aowNybvxbwM9A3QC+ge9/+zfjxd+3HwEBTJ7Zd9wegaQl5PUmtLMZ1sb7vH7uEP/vGa5i+vljZZqdLDSEk9XwIKPT6rdo2Ryvc9R8GB5q4e/9O7NmzDY1GVfzqKa2XKl8V2PlmJXC3YJvXcxPgrvWa9lviVwF308YewF2W3m1ZWd9Y//OsnlfJ14Fd1Kb1dyfr75YpsOCKXHpXiVrBunqKGVehABLrNeI+gGtT1zAzPWuF9tguU69lCLRaqcvkk/evA/JEH/Jo+82y+BLNyPq59K0E9InEjayd5+nr65KPoQ0h2OWYRIvUQRanK9gWiQgYHGxiaKgVtQMhafsijQkYHRrEex/eq9ua7ViOfRaBnijo9yfWuLxy/HxU0xu1Reoj6RTC9i3jaDWbWVyaK2Zsc76Z05O4dv5K3ZfpFf5yYuLwz/aT4WbCHSHJAwAR/SngftNK87JtzkrzIuGfO34SOw7eHT90cAglYznr0x4EtAaboLnleHQsG9eJpzyAt8kFh4LLSYZ20ZOec4SGc9i5qT9V/fmrSYpXgAeUu+90Ciwtd2K8Yc+d4dqpB6YlNX0OslmcSOw1AJ+s3i3AIwPeI8cv4dSFa/iZHz2IXVvG4vgWCYTkHGpLhrhtcGmCMFEVt8IP3rMVd+/agD9/+gf4zpEzyUmOkeCjwZ0Y5MX9sYGAgkIF0O7EsNzp4rXXT+GNk+cj2O/eystPQAW664izXNUCvjzqDewxSQlsTcK3AtzjdTWtBW5LlClPUpNurQCf0NBKzGIomoBV5mCKi8kNqNoyuEzJ5c04V2t7n55bwJY/uo6sFaQ6EuOQAEsPoMtEdar6qi+jPeVxemvKUYFOeBG+jn3O24aV13HpGzkCEbsZd4BqPSzgK9sEqGGxjzvS1aBY9B+O7XBE2mejOwdgqD0YtacUJXPPDrG8A5oDA3j80G4MNLyOE280FfnLxfb6qLbBxSszeOHoGYh20QK97bVmwyeA157sBfAxav5q36r6P+03w82EOwrkifCbzgXI2nyj4ZBxgIF0AE2eu4Sl2Tm0RkfihwyIBzTI+g/PR1HxOwCDAw0sdQo4ROAmR7r+5YVR4L+EeAKT7pcHsG39MAZqpLIV3gnnp+YjEMLp9jwlNgTMzy+mLSWralXStkH7pw7sZYLKMoBa0xsGyto5WMAXCVqur84s4l997iW89+HdeP+j+yL7xEROXPzKwRVCWNMhPtHoAkgH+3hEn9C/9OMP4J0Ht+MzXz6CC1dmc2ZEuHjRCCDF3/HBNHF5qYPXjp7EiTfP4a67dmL3rq1mDbeSHLcS2Kv5e4F7DVibiDWBe5mhKIG2ydVbei+9Y5l5cAyYjNEor79LEIDX9XcgASvi2JW1eNlG51k9nXxYpDyxTAeros+uJYNGMQMAMQR0qX7RMCC1Rxqd4q1kH92uarL0mglUbUNLTzNNbbbGLv3uVK3ecAmohY6o1gIwu5mcAfz06lY1D80baXII4quedxjxVrrA9cbl0bgTiohUTR9PEI0o/677d2G4PcjGjjK2uK+dj9oGpxZPEJP9xaVlfPP54+xGnADZDWCBnC+Hh8zeeCqNw9J8AQjd+b6t6pcA/Lt+MtxsuGPU9QDwvvf9+mve+/vKqvlcXZ+84T38/sex6/570XCA876iqreqdu+AxU7A9i/10wAAIABJREFU1dklVcUni0vJ5zIPd44tTeX6fYe2YvfmkTW/z+T0Ir57/AoPSitdxOcOwKXLV7G0uKRUqjxX6yY1kCaqtYwWIi5AGaNyJzgEpHV4+1xBnbK4siOd7ZvG8Lc+eD82jQ8ZpgFZ2VKuVd3l7UjpAhGWOwW+8K1j+NK330CnKMzOgLR7IBChKAhvy3r8WsMqTal73G4P4u67dmH3ri15KuqRZw3AHpNRfRlUzv12gLtps81r+I86RqAunbx/Ur0jAqaRmgUc7f73CDBp/d0hbbGqA/AIygmMhQFAqY6UR+6TQ5lcEwdcmbyKhdn5NLcV6Pk/YVbgquVLuaUyVwvU8yYHfNvXoe6blQQHeU5MYPQaid4IwyZqeqVDIqSVaEIlHYDx8SGTPnm5e/jANuzcvM62UoWiqDWI4J04EkQNRAC+/K3XcH7yGkIRVf5CT0gcdEghAHZuWReP/dZB2VtNDxBmTl/GtfNXV/8wKfy7iYnDf6ufDDcbbvsWOhtCoM/I2ku2hmzVuGZL3fljJwFE6VDWcFUSFWBRwzOg1RSrdklHqgZO98kQjQJpOU3vsLNPq/qzU/MMUjBlx0FGvA1Ots5lW+Qo/Qrz0zRFvrWu7L9eQbF0b+sgApdFCEUobe2rArxs0ztzaRr/6//9LJ597VxGtHSLkc+ZrLQGyipT+cvE1juHwWYDP/u++/Bf/fs/in3b1mfMRmIO3iaDOxtold8qSevC4uIS3jx5Tgah/El5+CIxZYmQyS/VmfoIVKrXMHXV/DGlLS/S7kTUDI0D1aavqVdBm0xeaQuytihQSD22Dk1npC0L8ADS//G5BXhBcQvwMXESPRVUawBeyhM2wnO85yKSCj6K8d6MdXkuLbTbzzI/77B+6lFKE91v5/7jRWgpb03z2a/B2/Ksz/wk0Hgu2yXf9a7qPz/zV89lVv3Vl3zYi0DlkJdlaIG2p/yX+zL32Z+ErQN7N2P31vVwxpaqgehyOm65ky2BHOeihXzDe7x87BwuTs1Efwgq8KVvlQYk0BpoYkB2xsh4lOsyIwQAgTA/1fepc5/pN8PNhjtGXc/hT4non8g56aKaF3AHog97UdtfuTCJxZlZtMfH+GS6pA527I62gagqavDpdEODDcwudQGkbXPRfN7HrV+yvY7iBHW89233Zl4WWGMoAuEcq+qJEhAyjYFzhPn55AUPIq1wsBx8RdtSJsywXqegxF25cZGamWUve5/LrpWxsSr7nOFaKrr4s6+9iu8fu4if++D9WD/aNvo82fYS2wVAbSRkW51RRCKqYR0oBOzaMob/8ld+BH/13RP4t197DfPdTuS2ieIRkG8zxpdD/9XX52i3WxWJqypv54Cekub9kJezktTOueuGkoyTrD0ppsxY2PFnCjFAn/JTua22LlsPletPdUeiTEYKT9uqwMQ9c3CjMn5iAqyL2RhvQd1I0gbcVcWuD6r+7lM5Ij7GtNYlbllVL0fc2vrkWpe9bDsBkzb1nSvRI5K5laaeyUO2JIgwSt4kZ0KljJ1cC2/E/azSd3pjJKc40C1x3gOguCtKrOnFc2lAZI5YWAc8qTZVyg2esGPjOO7bswn6Jby8I1sB8FY7MIjHnA0QEc5duoYjx87FnVGe1BarCNFaP5hTNwHC2Ahb1SuXiWrQgUvozi1healTk6hnWADw2X4y3IpwR0nyExOHjxDRkXoJvvq32y1w6dRZBApJ0qNkNQ6NS/mGWk2VaK3kXxhplyiCUdQaRHXW/j4s6omA81PzWO4GtVxXrYFYkRNhbn6xerKckdblVzmdTqR7zZf+alyRS/dyQp1VgReBQT072S6quMRJjrW617q5rKOnruD3Pj2Bv37hZGJkYIyHpEOMyC9cvRBn2VOs3sa8x4cfvxv/9B98CA/dsw0FAUWIZ86/FRhPffz6L6k+tFuDSVK30irQl8QuoGmXYqplxJRW7RrTSHk3KblXJHJhBAzTyW3SugRgbD22XSWAz45/hahpzVnhusUzgS6Q1OKSUzBRjeZihlwjpZqmHODFLaqObyvRGwYgLRdCpd4kCXP5RiqXv+oMB/bQrFxytxKxSvnaDuQSvM2nkrfXcuNhXSVp2krRKm3D1OdUmi63MXO+4/Iyszgj7VdP2kN67oCNY0N4+EDcihod3UjbeRcUaySacoCZ1gssLnYw8eIb2ucqvRtNYoqLfT6svup1FKdBzWMzPb8hg7unJiYO9y3632y40yR5APgMUfjtaM2ZG+FZw7sQorX96deOY9ehe6MTBDjdb0kQqT7FeRAa3qHFBnji99aBkn96ABTSBEYIGBpsYtv6tXm5EyJ56vJcVMkzjVJJnomLI94fT7wXtSRx9CzfViT3lIhiJKLmnvIT5PREKbMmnq3RZ8xPFeitZF+EgG4BfPYbR/Hcq+fwSx95EDu3jBtnOJEgVm3lzHOfuHnvuX7nsGndMH79l9+Db3//DP7kiy/jyvRC/v63Jdx47TZn2xx8sRZpvVJzD4anH6k93dvy07jp1ZY8T8psx6WtVhiN9NzUleUzZJUjo7qd7K40A8xWmudnIjVLepaoydUBfEkCVy1BCdwTp8rKqpjel9T7MCAirvacvoNZqhJBW+KcyY88OPte5aBtj/1mmmn/aFCGjxtAjtAQid7xc5cfTkNcB7HUHN9djKFzZlTvwXSaWyXL5IFitwQSB2TpW4qUL+/snUOr1cRj9+/CQLOhGgTrwlCYOxkzibmL2+eefuE4Op0CYoApmp7CI0rx4G/ETOTIEB+GQ5T3V3msS1wgzF6ZqX6XlcPbrqoH7kyQ/1Mi/HYE9ATs4gFP1PbeR2nz2uRVzFy6gnXbNgMwwAIH63ceII0bGmxicbkwXu+S0xzdNqf5HbZtGMIq2AtApBBgqVPg4rUFlV508pMY6MTfyMgQrl6NAyUVT9kEtg8zwxlLeEkVdapWF7BPky8OzlB6JsCtzyqAXwJ4sRkIefzJi9P43U9P4MOP7cdHf+ReNJv2kM74f9CJkp4E4iUS4QRcMpJyAN794C68497t+Munj+Kpp49huVv2J3Arwq1lHVYsjYBWa7AKpDUZbwTYq3ElRrBU+FsG7uU6e4E7eoxrF0FFTnhLYJgDfAJLBm8BAZdM4nSNvAbgndUSCDNAhmFI1bL2ScDBvIBjGlNxyJOk+9TGxHDAxGe7B2DqVaRPHWvrltbXkadsHIhPesXJVKdo4eQiArzTMogBXpNwvYQqQIs1PZFnAYZnOtNysZ2AMvZJzS/96JrA44d2Yag1kF5E1CcEFfSUThLF7XpMx549cgZXpueUmfCIqvnoVIcQPLLTRYNzGB9tp0FZmkBpLshbA4vTc+h2+qJF8wD+op8MtyrccSA/MXH46JNPfvIlInpEvdGFOBjzU+rixy4K4OzRNzC2dXO2pl4GeocE2q1BBv3EEfC6DdShhexacQTs2Li6FJ+AFTg9OY9uIWDNP2WiRcVIGB0bwbXpWXS73Zxb5wEl3H+ZvJfHYk4kKYu32+os6KuTCUrxZcC3/vszy3sD8PZUuxAIX/z2cTz32nn88k88hPv3b4aQoQT3zOTw/jrPSwPRY14kGvKtCmKufrCBn//QA/jAO/fjM1/+Pr71/b7ObH5LwprYgh6J2u3BCojWJr9FwJ6VbcG2pry+wR116+4mdSlv1k6qiycl3rJ2LfYdzqWyNQ0gCJXA1ebhUi3AJwk533OvgM+TNknauUTvZV+3YR6kHVbrAFf2eCfvI/WlrlDf+aX41PZUZorP+xKl24ykUMqTGCmR3A0jpuCJ9H143VsBH9LWBND2kBtn6k/74Ske481o7p0wCbx9jvN77/GOe7djfKRt2uqkMerVzgGRRnN03BrvcPTEJbx+6hJEd+I9IRS8RZI/WmQ8kpfOdmsArYGmGnXr/KhOSI2bvTRdfrha+IuJicPz/Wa6FeGOA3kOfwLgkWhln9T2arRhwN974MyxN3H3E49gsN3WffFJMo8Qk509D4ehVhNzCx0FekIEHPVlz6p274CdG3tvmxNiKgBPAE5emkVBFMcjEwg5itKp+jEOw/XrxnDp8hREKulRQ35bkuylXgsKdneCPlNwF9BG7vYW+f54UG+AL0KygyhL+Jem5vA//skzeM+Du/FLH3kQw+0BJcSyr94zZw9eQwsgwEfJLUSnBxovGpZN64bxn/7CE/jIu+/Bpz//Ek6c62vrSl9hTSB+Q4njmnw9DSnDb+VRVmE/wG5y1ZZpwb2a7wbAPUtPeV7KUiWmFEbrBUQQgl17hwFgAWOnHu4S8CeAl/QW4AVwnUqGJYBHEhyTVK5Nis8zwE79IOM5pknrzulxakPZsx1snCkvu+c0GftHgNlKX+lX6TihUVKfNfADJfU6WMqXvhcQJZifnGdhwD0EsbLgPiPIVnUQgAaDvp41IloAluYdgEP7t2D7ptGM2dBNiuZ7aJtZMHOOcH5yBs+9eopV/uBdV5EpK0SQgBw56xAQjZ7Hhlupq8tjUwduGsBhaRkL1/vG67fVAY4NdyrI/zGA3yFCQ7bBRRBMErz9u7S4jMtvnsaOgwciwJMAQ2Cp0Er3cQIPtxq4Pt9JfuodD7bA1uA+5htuNzE+PFDbyAzgGSyXuwGT04sAENfjhbgIsCtHHQna0MgQmtcGsLy0zO+4cseIaqxMsq3EnpiOklQP6Fq8BfD4c/maO+cpq+Xr4ioqfN6WOPHyabzw+gX88k88jB95aDc7xUmgLWpKeBcPAnK8vEIOhUr0lq+JVwd2b8Q/+cQH8fRLp/Cvv/oKrl1fXGU43YLQJ5CvFAZbgxkgrw7qKVU1fi3gXg/s+qwW3OvAOt1QnrRH+hK4IzGmeVus1G9BlVTCBQzAIoGLZwBLwFkFeHVwYyRsC94yR9M9FMDLEj1QdXEL5OVZHBJAyYBdGRni98o1DlZrkP5PfSBvqJqNEs0gk1bKievqMW0myZtvJGUntT0SHdE+d4a+pLX7KIUDMHQkLpW4qIHlwqPdTWRKZIVO5vi+neuxb8cGiB2GA1izJ+a5xj6D4hKCnHc/M7uIb37vOPd5PGfEI9L0EFJZorr33kUC7YFR9lUfAD5COGktKpMJwNzkTDaO1xAuAfjzfjLcynBHgvzExOGzTz75yacAfEyk9VyCT+vz3scT5k69cgzb772Hd8MxiFDcOifAEV3fxmcN79Ea8FhcLtgBDgEhLgXAubh1zqPn3nhiqhSYtRXjtovXFlDwoI4DlaUEF7f46WSJ2aM0v2EM589Natm9gF7q5DvEyatPewO7iQsG5K2aHkhgDUJVgudBT3affVQV1AK85J2ZW8L/9m+fxTdfOIn/8Gcexab1sT8jwWEPg5Fax2UTAIWrbrdLWhmxl3R43yP78Pj9u/AXTx/FF751DJ3uGnza30KwvpEwONjUtUsbeoF63TOqeVAF9t5l1Krk9WJt4J7KJJs1qzdrN6WS68G9BHDOqLD1P5FiY57cqE0kMdGcxfQC8Pm6vJQfy7Hr72X1vEr0zFVYkJbT2BJAS9GGCfBmPV7ymXRWmk8ajBygzetn9dgHdSQj08BwJivJ6zMuVFTyVo3vzL0An5zuxn8SM8A/71BzulykfYEA57meEDUHIQBbNozgkYM70LDuCRErEA2PGt9ZbgqE5U4XX//uMXSLwCp4MbZjNb0TZtEhsE97YsFudLilWmGhx+ISPYWQbilgdrJvq/r/Y2LicF977W5luCNBnsO/BPAxQPZ3E8TlbVzrMQvq8Ji6eAWzk1MY3bxRtr0DDih4j3xwUJe1suY70m5iYbkA2KIbPiiYeDgUgbBrcxXkdbBT+ZpwYWoBRUiSupXkCxmblLh3coRWq4V2u4W5+SSRWimhTP+Tmr5K/RNnXV6bN1x5r/sQVXTWGU7s/5JlfTDOhoiBn/9KP2Tr9YHw4rEL+I0/+BJ+8cMP4KfeewB6IIj4pwYQmIDrsb+oA/qk8oePR0L+4ocfwAce3Y//6ytH8OwrZ/sdZ29pKH+7Vita1tcLAjXgaJ+UGYPSxUrAHp/UgHsZcDnOgnulnBpwtyX0Anf7rLwE4GRigMGO8ynoIgdaVdXDgJZxmANA91yXAV7UuQrmBuBrJXqTP6nejarbyVbQBE4OQLPZRLfTycA9MQQuq0/aldmtILVXeqcG321XZ8EyDUorHCrfOU7FZLSYDO9cRkcqoK67CJKaXkAberqcAX1A1fdAMrobHWnhiQd3o8nObbKXJfPWAuza6EhjvvG9Y5hfXGKwZkmeGQJRz4vLcojKno19x0aGeDw4PeXPO4cC3HhYwYGwND2PznLfeP2H/Wa4leFOBvmnAJwFsAsA0rq8/GUwMR/izNE3cN/mjfzx2RLWR6B34qdejewcWs0GGs6hG9jXehCuLw5m74Hdm/L98SsBPAE4fzV6uXOyVgRA9/A64f7B64jQibV+wzhm5+YrBNVVLgyhtATTEvqQ2iPgkADdPKMclPVZTbx1jCNr+eVDbkSCL0xaYQAQCItLXfzxUy/iG8+/iV/9+cdx1471DPQAFfEbCWGyDnRIv3A90CMAWzYM49d+8Qn8+BN34//8wks4daFvw5ibCrWYXZOg3W5lkizQC/DfQmAvXeRAbeL7BPesqh7gbuuj7KGLc8YCm5V8S5K0cssq6VoJGYoFKwI8p6tbfxfmwmflWBV+SisFJwCPf8fXjeLa1DVlUMqqfUmrDIAyuTkzkF6qcon62DSupI8tcwKAj4MlluATlqpxGhfALmdiLvEnryp7sZFK7xKt651q/jyr/QPLY9m6fIjnibz3kT1oD0YoEsZMvotoIMD0Mn3y2M5vv3wKV67NxTYTKW33RCiQnN441Qi4SHMRMDw0iIFGlO5lBwAICC6YI3aROpGA2ct905WvT0wc/kG/mW5luKOc4dgwMXG4APBHci9AU3b+ktaICWd/8AY6i8vRDSsZiRQGqGC2igEYHWpqeQJu0bVswLrhQYwOJT5IjFGspEsm38JSF1evL2VSrB6fKtc8KYrAEjK/U3NgAMMjw1WnOPIr0i85rgnxZ9OJQxxpQxE0f/k416Dpe9QZckc6ucvbEBkvCjrRA7dPz4c3En7B/VoQ4djZq/iN/+lL+OPPvYjlbsEHVkRiZx2MOJeIY9mxhpPjhQFWicZ0B/duwj/9xIfw93/2nVg30rqlY5JW+K2aiC9arYESI1XKqs8oMWf6LF4oc2lqr5YlZdiy04X+kzIhDEFqm81HpClMHlImwgKKzJO8XdX6JK18Y7KqWTADzGXkxnXyqwH4MnOAHOA1B6fz/ECAVxzLJAt9KTNpDQS01bmNfabMgUOrPYhWazA5i5G0PHYb4v7Vp/GenM4k17JOn5ccyGROb2B+yUmNOrIxzxzYKY04ynHJDW1D3NiW6ii7to1tSu9RcYijZbMBotRp5nCj4fHEg3swNtwy5STXvDEtklMg+duIVvivvnEBJ89NpW8h34Eoi5Nvap0NOe+wbrSt38U70u+T+jjnWsNyF/PTfRvc/Yt+M9zqcCdL8gDwKQD/LXguWq7RslfeQ/3AXz55GtvvvQtAVLsToqV8ECcIYn1PBBfinnnnlxmUwOvz0RWuqOpFogryzZlBkDiAEMjh/NQ8CpJBljh64Wbl3Ovs/GuuwDnChg3jmJ6eS6q1FYKVnuQ+l/DJEGdZE4vpAyWCW5bu7aER6gbXbpNjap5L9lSR7DOPgqFUBjNe/+Zrr+IbL5zCr/3C43j04PboepLSVsf0gpGFV8t871glGFVwBELwjtWD8fn7H92Ldx3agae++Tq+9J3jtWfQr97LK36AG3rYbg3mqWq+NVUuFMrTfU0Va5baOY6Q39giKf/PXmZjr8zg1Kns6yX9GMvCE8+HlMlKu7r2bqRsK/GTzqtcEhcwRule5yU/yCT6LE9Sz6e1eW6dscaX7XllRgAARsdGMT11TfO6LA+ya3lv3b5bkr7L6vv0xLRBIl15FDLDxLZGicmCSvBitZ/U9oluZCfOucTU8VeMJ9ipNi/1pdST9sOnuf2OA9uxZcOwtlcAVkV2+TBipq/v53Dq4lW8fOx8BG/wjigXl129j0utTtfmRYMqDCSh3Yzb5kiMAvmbyhyy2zaJ1xrmr0yviTabMAXg3/ST4a0Ijd/6rd+63W3oGT7xiZ+e/tSnPvckgAM2vjwxlDOHw+L1Wey6/95sIuj0ILOOZlRvIIeFTjcjus4B771vGzaNt83JagkMM0MUHtivnr6GyZlFLV81TDIjnJWokgGRXDsXjQnn5xczia78EyO3CNimXeY6yDMkIM5OgQrmLyRPFfjL6nplBMiCNrRdZa94mbtckybws9n5JXz12TdxfnIWD9y1Be1WU7+NldTslyT5QIr/PBmRj4mBhsehu7bg3Q/uxtTMAs5fWaNHyTXN4/5YBEm9Y8cWDA+1ap/ZG6p5eiuAvVL2CuBezbc6uNt2rgTuEiJhZ4KKBMICljKRxaFVDsCxjPJ2tjpr9TLAZ/dWopf6TDme02jZDNbe3ru4tORdokXOOTSbDXSLLsBGwpkEXpbKnc8kaDgk6dd5TZvyJDe1qW1O38P+pD3aVivVoiQJK9AmpiQ/hCfOQgebXlJrx6elklJZAHBgz2bcs3sTlxe/rvcOzabRzkkbM+NFh2szC/jG88f4FDkZcMIIJbKQKILRDnG69WND8Vhbicu85wEQhztSfiBMnrjItmBrDv9iYuLwbXGAY8OdLskD0QDvp2xElDDFwh6QrXRAwNXLVzF17gI27t4B8ZwWJcN4OlEIgY+Q5b2a3mGk3cC1OQYq4YgDsGfLiBqjJQBNwKncLAPi2SvzoAAUjpRwkXLwAAJUQnHCMTJnKdNg3fpxXL06g6JG8pSwqpQEA9b6LGcCRMLXIxcp94ZXPrhGntet0VfvRa0fcqO9QFkZ0c4ifpOvPHscz7x8Cn/3Jx7G3/zAfWg0IueercHz23l+7yBAL4dPOGKDG35pnvJbN4zg137xCRw9OYnPfPnIGtbr+wPwteYeHmph/fqxGiCuB3Xg7QF2fc6V3Sy4a2yZgaA8red5IPutdb1dPp2ADsBSmACwSHcuA/jsZcy8U0BHlZmAll9af3dOHVhFJkDmLefJJHEpxwK8/CWMjfHafAlIs7Ral2VQUrn5ffX75UJP+XtIJjJW9rKFFaqViPFG6+eMJE/8rZyLNNH7XLAAkkMcpT3cp47Ukh4B2L1tHIfu2gKVergu8aVf4gKRmDvC/EIH33z+GEIR1/49G01HLR9/dyOoRcEOUUsLB3IOA95jqD0YscDFd4asw/OYdsZPB5zD3NQ0up1upY9XCf+y3wxvRfhhAPnPArgIYJuNFIt7UdU3Gl6B/sTzR7Bh1w5OyZbaFOLpaM46unFwIQ6g4XYTswudiBfeYcv6NtoDDQZ0VEGQESfwDLi+2MHMfLS65LEFoUVOByr0r4I9pckpBnrrN6zH5UtXenZIYjCZKFtiXGIALGMik0+WGFQzIPdi5S5qepH80Vt6t8+KXhJ+ScqPTIFhALi984sd/NGffw+fe+Z1fOJvPob3PLQbVaCXl2VvVjx5VdXPcUo5mSR6ONy3bzP+u49/AN977Tw++43XcO5y31thar/DWoJzDvffdxca3lcYsqzMXqBeSlrHKPQL7pT/l+XNYm4A3G15ZXAHEmBYEAZKbmkV7CH/QVTK3oCS7pVHAlqZYwKcsGmcUbfLPRIoWYDP5qoUJXVws+S51yHnsrQDA0202y10lpcr4J5fJxRXoJe/hqlI3ZLDfRX47TcjjSBuo2yTI5MESD4DxMregjgouZAlBk2d0yw4iGKdADMvY/mbN43g0ft2mvfyOtaSut68eJrC6BaEp184jqXlQi3pAXZgxkx+AWTW9JEBACDO08hhbLStZxooowiH4BDBnusnEFyIvvBvwMPdMxMTh4/0m+mtCHc8yE9MHO48+eQn/3cAv1F+Zv3YW6C/ePo8Zi5PYnyLHFEYub10cIp1dxslw5H2AGbmlqPkHQj7toyqwV7yCgfAgKKqykE4e2UORQiGyLhkQQ/ZRscTlY9ElINpdHLzPBxfN4wrU9ewvNzNJRTkxFWJqQF5mZD6gNtHmo6SZ6ryvTFMBL9bDsxWq0ErSPYJ4CvAXwb4GlQ7N3kdv/NHX8c77t2OX/25d2HfjvW6nS5J9Il4qFtcpghCUOy+epi877p/Bx49uB3PvnIWf/HNH+BizZnQ/QD4WsK+vTswOjaUfz/Ug3qsv4ysdcBeBefsvgbY9bkwdrV5c+YxL27t4F5WzZOpM86HBO4GuXjGkkrQCaxjfrt/3h5BK+CdW6RTembSwGgPxMe8UHe7/u6yunKwBUp7+cvgLwAOwsjoMGaudXpI8GkJQNucaSfq60ydVh/s97EAH6VXywTUSO+cQD3dOSUnnC4KMAL2Mjf1QJoQ6Z78Tw5YP9rGY4d2oelFF5cYcNlXk0vypJ1JIHz75TcxPbvAEjZr7jyA4BLTSAS4CM5gpzfRrS2Pu4bD6NBg1OJyY0VwcPz+kf8jtcxfmlnA8mKHNR9rpgx3hBQP/BCAPIc/APBfoKa9ZaCXfZYnX34ND3z4SXjigcODKkoBBCIP50k0vWh6dnW72IX3wL6to5FTpFyCTxxrkuIJwNnJeV06AMVBlSQCVhWKwV3IJ62z44bn+cZN63Hu7CUTnbuytGONTIQwHdnzCtjn6vwyuGdr90Q1oG92KBApA9RzrZ5Kkv4KAG/f6YXXL+DXf/8p/NR7DuBX/sYjGBtu8VeMRDE6sEhAn4yPEvEoMwZCiDyAdz+4G+86tBPf+v4Z/OXTP8DktbfGtfT4+Aj27NnaE9Bjy1YA9eyGauP7AfZScchi6/KWGn6j4C7XKsGKVG6QUbZgKeiZQpKEmdbJFeREbaaAmMBbAVMZinqAl7XpJLVbgDdgzmn1PSiW7U1TpPUqzQ8OoN2XoxB/AAAgAElEQVRuo7O8zAyAXWc2tAAAnNc3t/USUjp9thLmlEBc4ohBTFes5ZHht0TBSJRoCsipOl+NkF1M5EnU91wuM9qet8uNDbfx+IO7MdDw2mQHp99CgsxPZSJcBOMXXj+D85MzsTqev+LVLjgeM4Uw+6LRM4fwIEr8o8OtqDFA1OySSPwEwIXE/OihOYT5y9PaxjVi/BRuoxvbcrijDe8kfOITPz3zqU997n4AD9c9l/VlO1hmp2aw57670WA/4SkkZHWU7gnAYNPj+nwHznn89BN74J1PIIU6cEyAOvHqRXS6RYIYnSXSRuh9IuhOJ5EkJZ5hg4MDmJ2dx/JyJ7YBCXgFcK2knbQKOTNin8n2OeLte3k65O8aKAP0gGQhH7fu2Tw91uRF2g858K8G8Pm3BY6dmcIXnjmGZrOBe/dsQsMz558oYRUUkmylkqN9plKRd9i9bRwffGw/NowN4czFGSwu9732VtPw+KfR8Hj4oXvRbJqtmFVkrt6WbsqgnF/eJLCnIrLMvZjKGwV3CXHLY3KzClXTWindqt2ttJtLvpomuxemmsulahogGdf1BHjN5zR9GeDjdQ7wUpYCN7e32Wyoyt5zPt0K6pLxnaitvWxT48Iq20iZO9CtdpUf9yTfp7ZwuVyGPWs9S2O/A+RjoVR+/k3k20GYce8wMjSAdz+4B4MDDdNf1W13zsVtddLnsv3uxNkr+P6xc6l+I8RYASYt+Ui/Z0QXznlsGB9Whilf7qT0hkLbQOjOLuPquakMW9YQfn9i4vAX+srxFoYfFkkeAH4PwN/r9TDwcUdijNftdnHqyA9wz3se0y02HtC1NnV9C3F9SWi4KM2vGxlAs2EAnj+4bBMDoCBGAK7NLmF2IXq28gD4ADre+404wBCJG6QNIKQtHYkjd5KBgE2bNuDU7LnSm5KuJUVmIg1UsgRV2qyE2DAoVMonHLhDsrY30n3tddnwTp5Z5qIkyYcABIS1csNZmFvs4I8++z18/pnX8fGPPYYnDu2M22bEJaWABFxJTY81qfqbDY/3P7oP731oN77xwil84ZnXMT23tHrDVnmXu+/ZjVZ7oJapWROol56tBuyaZi3gXpf/VoN7Kb1jIkyMHF7ZMCSJyyHODYEdBRz5TwuDfEvIM6nQJVWzF+AuAbygl42P1TmtyzIXcEY9rwAKvU8giZwZ4HKbAw20WoNYXu7kDIdK9YbBMG3R9pbC6sCTKo9yUOwggTOVcim9XyYocP7scBqXvq8wQlH1zV3vEB2KsaFda6CJxx/YjdZgk5mSNC4c5NOR9qUXT2QcLk1dx/NHzwAQ52aO199jvdn6O2t7Ir0nfS/HeceGo88CCsnRTpTcc0ZUmDQih9nJae6jxNitIqAsATi82pd5O4Prc9/fbQ1PPvnJrwL48Epp7BaV9lAbH/yVn0ej3a44RHDMxTrP8cxhdilg6/gQfu69+yFq6LR9LQG7lWKPnLqKZ169CBmo+WQtrdPFRiaixff8OE1w/nP29AVcn01q5DLRjXHCO1dB3YJ9mSHIQF/TG6DO0uR9kZzirG59L1v+VBOxWlgD1/zowe34+Mcew55t6wBYOwOzpEK5lsK+q7aJr4E87XKnwNe/9ya++O3jmJ1fXrU9JVgGAGzevB6H7r+Ln1cy9M5dC+p8d7PAnorJ85c+DGVtyFG9X3C35YuDlFwKj2CRtoIlEPVmzki+CLZWkk7AbK8lD2C3tmGF9LlnO2fak8qIMy0BeAQue1QsF2UYAeVIQEWB2ZnrJUYiMQgw9dp7ILVDeRl5XjthcuCycfZ7xPEuT0klY0lTpnc5vahqAm2aZsPj8Qd2Y2RoUDkJSl0hvJjGOQADTa/jbXZuCX/1naNY7BS8U4eSIy51yIXkFI0oXgf+S8lpmgOwecNYfMtAKNiJl5QTCgKZuIICisUOzh05haIQZ2lS3oqayD+cmDj8D3o9vB3hh0mSB6I0vyLIW8v3xYVFnDv6BnY/fCiu37jcdz0B7OaW13UANOCxfeNwArLKYCaIU4d4CAPh7ORcdL4A41ebJ7e6sa1w/UjgTpS4WjKHMQDYvGUjZmbmESjfUleZvA7aNgv4vYA9izOGeOnQGU5XA/oV9T7Vq+uzuFAHg9wfZSK1BkbghaMX8A9f/xw++t4D+Pd+6mGMDg/GdvCL6bGSSAZ5WrjdHiNcO8SpRqQ8g80GPvLue/C+R/fha8+dwJe/8wbmFlcHewmDg4M4cGBvBewqIFy66QvU9eLGgF0YwCzKFFJfHm4I3AEkJtceNMKPIz9sv5NTMItMeCogSbVJUlXEyB7F8lQ7tiJDkABegb4C8OD5WV4uSCCcwN3GOy4DWFruqg1CYhJMWTB5DfiXAT4TCGzQD5ALC2Ti4hAvnUyHSDvgjBGeHB4ndMLla/JSfkPmOsDnfgK+4fDY/bswNsxnNZT6KMZB46SpwiwtLRd45sUT6BSB1995nR1gq3mW2sk68aG8/2IKeOcwMtxCg48Rl7yRFMStc7KWL+/uyGFuckbLkmNxV5HmCcDv1z24neGHDeSfAvAqgEMrJSJi96rkcfz5I9j+wL1oNJpxIAQGeAc+GpGtsMnB+QjKW9e11WGLBcE6iY8IuDy9yIZfQqeieogMAWMBgCczSzVOwF1nopHumeloNjE2Poqpq9MZFMoQy9eV4hMhwcRuqnqBvRBrezKdXYbID6mRiZ/bKNRJ71qmkeRX/F5rQfVScIjeC5+aeB1ff/4k/u5PPISPPnkvGt4h8OEYUnYd0EdQt/7vI0jEMwcIIerr0B5s4CffewDvf+c+fPXZN/BXz53AwtJqa/YOB+/di2bDVwCRq6+7hEXinsBeQtHVgL1S1orAHhNQljZPuGZwNwVLdHJoYwBKJgYS2KYSEuCSA7seBdLhowlUy/vP664VTKVaqc4wEU7+GhC21wrOJUDP1PjOMcOPSnmdpWU9ma4M7hXLe2m3eVf7zulBoh85o2RD1fiO5BoCmPGbpb3wLqNhCewjvbR2QKJlFzundxzcgfVjbR0Xts1Wcq/wZh7odgkTL53A/OIyRFMS0/LSjmO1Ps/ZALGE5yU4QpzxfNKcd8DoUBuEAFDg6c7liutkA/bwQHe5g7krswrwXvsijd8aoP/LiYnDr9Z0/m0Nd6zv+rowMXG4L04phIDr167j/GvHWX1jgUm8sSEDIyJg3chgArlg0lL11ykCrs4uQfzRF6wy6hYhXhMyt66B2Ld8IPXlrv7mA/t45zxS3obN6wC4zJd82cd95otefdwHLTvWQ0h+/01ZlO7tc2sVL3FdIi2rG7ge/ml/2TJWAfgbDWT+zS4s4Q8/+xz+8997Cs+9ds5ISslASH1uO6i/briy/3txtGK9mcXr4dYAfvpHD+K3/+Mfw0++5x60Bho927Zz5xasWz+aVKH8I0MUpe0SmdSghoGTnzBppLk0XouwT7J6UmFEiQEEUnuy3szKlHSJeYNtj9RoKhLmTxiCnDFJYEIkRD4dSkRwyZGJSPJyyJPU48wWN30Tl3Mjgl4lZibiJ2nGBPxgYDaMQRnULdg7u7RgPc7xPayHOPHZHoFDNHXWr7z4bBfjs/xvFEYa4gve2+c+83qnfutrflKnLVv85jes5zyZB6X26VyyxnLm50y5j9y7HZvWD8N5j4bL26x1O/7Zsl0cG99++U1MX5/XuZcMFfNlF0C0peYbEQtPpv/GRobiN5BvzhK+bm1W5i9xIQuT10GsPZU8ni0vrSFjKfxuXeTtDj9skjwA/CsA/wzA1rUkDiHg9W89j2333AUMNnVwAnxKEYi9IcXJRA4YHYoqpmhVHqldBMIkBQuhm55biufHyyBkscDBwRUE3cerHCuDi4hJIgEAiFxiVbr3voENG8dx6dLVXAor4eeqRniaxiUiTElFr9cQxkcIfGKEkgajukZvpXcpLwu18+LWhbOXZ/A7f/R1vPO+HfiPfvad2LV13FTKkiGg0rts79GjbtkNYlTdiafElN/DYbg9gI+9/3586F134UvfOY5vvnAKnU6hbRgeGcK+vTuq776KlJ7FU56KKheZ/Ny7XDMONIryBOWyySQsl9ev5G7r94Yoyrp28ogm0kaUIlXycECykE6OUgjJl3wqsyzNV13gVtMw4UcpncQ5s0bvUj2+XK6+U1pKyFX4MV1neVmBJy3f5UsIwpzK6znhVVyyL4Bps9ShHZYFMweN5KnMmlV58/sKK0Ymr4tCL9MHTuOQdhfx+4YA3L9/K7ZuNCd3Mu2rNI/kXqTpWM5zr8RT5eK58IHzR8bMG1V7ZFooGeEhanoKBfv4Js2Gx/DwYPTqqd+W+F0dnBMgBx+dC1CnwOzULLz3BujJfNdaaf67ExOHv447MPzQgfzExOHFJ5/85B8A+O215pm9Podzr/wAu97xQBy+JN6VKDrJAXtLCsDY0ACr2uOIVGAPVpWdHEZMzSzF8+NBcJ6HapFvofFGKhJiU+h9UkHGCJnYydUtAKzfsA6XJ6+h6ASdLDK+lBzr7EU2SSmT7kQqS0yA7PeP9/XgLowOAdn2urL1vF3eqAQhGr0+1C1iAp4/eh4vvn4BH3rsLvydjzyIzetHTANyoGcT3QTuPvZF3HHBQG9c5Yrqf2y4hZ//4CH82Lvuxl89dwJPv3QKy50CBw/ujc6OyoBXE1YFdXNTeUo9yr4ZYOfElN9micvMy2rgbhM673Qs6vyQ88gZ3CPQKbJpvnhJ6l5WYE5AVoNDyquppKyE1E6TGzBPRQNOvOrl2+PqAB4GoJNGIAf/UBQIRWEk0RzcXZbXSqs5w2D/5gxsXTDlMKNAJAAX+0Q1NhIX4pKVaoKc+ebsIEaFHPE6x0zAgb2bsHPrWDautF9KQRgM7XQCvv3907g4OaNnvetfBnHdVUSUjZ1YPy/BOj7EykX1/ehIy4yA+DLan5T6QBg75wjzRor33qEokgvg6I8lMY0G5O9IKR74IQR5Dv8zgP8awNBaM7z+3EvYet89GGgPQkZVNBIhHiBRUhhjb0hxMvBZw0bKtarLEIArM1GS987BFUIwCAWINYM+cp+KE7lbWwW+hCN6H0OawJs2b8D5s5ez2Ir6le+F87Zg3tO6HvYvmCGoB/f43tW1d9s/q4WeKUoPbgbzQ0H46rNv4OvfexM//sTd+Ns/9gA2jg8lYzskIzuYv5FIAJljFR+ljfKWPMBh3WgLP/eB+/GT77kHr5+/jlNXu5hd7PR+tepFLainyzzixoA9JuoJ7j2ldpOvjNlU0/5SOyRJ3OcdGy8q8qiaB0TFZRna7DQ6/i46X5D8yBukzhkDBeAk0ZfV7XCpHAVxaHZ+1gvgTfXCnJQB3oI3HDrLy2ioujc9F+dddcCe8pu+cem5fBjnyquu1W8ABqRopJYAL/4xhsTepFMaksCeyYNuUxWnN3t3rMfe7evz72LqTmNXQNal9jngxdfO4dSFqxhoOjS4fVGcQtLuMIGMrmopP3FOJH4zFJrNBoaHWiD2UQ9+D6GzUXrnv+DthMsF5q5ez75HYrLI3GfS/JsA/jXu0PBDCfITE4cnn3zyk58C8J+tNc/C3ALOvvI69j76IADik41IPdJFiY0wPjJopNg4IApCLr1CQA24MrsYt1y4OAgc8aE0cOy+Nhj3nUwA5NxGQNfArFFMAvt8Yo+vG8Ply1exaK28SwQ7V9PnBNoCu6SxwJxA34A2c86V7XNrld5vItyK8rpFwBe+dQxf/e4b+In33INf+NADWDfaggA9uSSNgO0z9ChbOQQFqFjqi39u2Xc/NtrG+96xAYEIx89fx0snrvKJhCXwXeEF+wH1+OcmgJ0zUH5bbUmZ6egD3POXcCpIe8tESVrvIFvNCLKllfMzqgorQIgSJQBlzhQ4Ey+gdSh4S7Q0XqVJk1cZ8KqDm9hMM49LoM6vUQF7CgFFpwtde0YduNv6BeTysrX9mWRvOAP9KpZbMN/EJa+ZIsSQ9EPcfsRznzWVAuwkxmwJ7BFYMCJg+6Zx3L17k6r9VdCQtAZIk/llbKuDw6snLuL105NJWnZA8C5a0Zs98OLAzDFj4thDnXi9U98nvAw3PtpWQ8CIy6IBkF6K87jgcgnAPFvU2/anv2lffcnS/ncnJg6nNbs7LPxQgjyH/wHArwJorzXDiedfxq5DB9BotaI1PBjgfTR4c85hfHjAqKIRJwIbrqnEyiM+BODK9UUUDKhRqxhHqQdUJy9W/IAYdiUq6AsBd6Pyg0QJZU6TfPOWTTh18myMJpvBALXGiCFVTuwz6d3cw0rkfJ+2ziXGRvbSWpX9nR463YCnnn4dX/7OG/joew/g5z54P8aGW/G7sVpQ1+mRrGkzyR9lS/3kYKfdGoRzQAMO9+4Yw4EdYzh7ZR4vvTmFU5fnegB6ftUT1PVZ9enNAntexurgXsXvenBPavkk9ipEO6cHzDDXy4yu2R6nPk1NpT73S+94vlnJEJSYBV33lrpdAmkF4xJzIOZXCVBhtAA5wNv1d82v5ac521numONSJX0PcFc6YHYQlKR8aYNeZzQgTyjfUqVVYaycAHnsZ2qIYJCDOQFRtc7W9PIcLPFv3jCKg3s38/dw/Nx8I0rtSj2SVPVvnJ3Eq29c1C2vcZeSh5O1cOG+nYC52GPEZTTrLlza5uDQHmygPTgQDa4Bc44IawgE7EUwA0CdLuavzen3kX4ur82n70Fwzp3euHHsD3EHhx9akJ+YOHz+ySc/+b8A+EdrzbM4v4jTR17Dvnc+omqr6PyAuTpHGB8eVM92jI/w3qHTiYNFJXoGg6nrrK4HUHgmLAVPHhlUgQHB/T/kvWmQJMl1JvZ5RN6ZVVl338fMdPccOAY3iEMkyBUFEqQoW0omM8n2h2TLpcloopFLySTTSiYaf2hJGXdNB0guoSVtTUuJh7iLBYmLAEgCxACDazCYezAzPT3dPX1Wd9eVV2RGhLt+uD/35xGRWVnVV3WPl0VFhodf4eHxvveeP3+ureUBR/zs3LyV5N3HbWkmYwqarQZq9Tr6vb4hqnx+3bHtipXBQdzFEVgr9zFn1PLcsUXOuQzc8jqwz3evh1Gc4i+feAVf/s7r+MRHTuI//OjDaNTKHtCjQIqn31oWQQ7orRRjJVSFQ4sNHFxoYK07xHNvrOP05U2ktNqgCIB5GAPqQBbYM+B7i4Ddxe0O3G0eC9Iaaeyui7RJEyUkqRduLpyL4E4DRhKV80tPSTVIundF1es87sqCK0tBS19tlRZ483PnkwCe56G6kji2VuIAmJX2GCaClUV12HYr5OJdbxd8gTQcTcl2Tp6A3ywlsxpKku597sD2rzSsQiCAdquOR44v++3I8huOJEFPK5gvSCm8eXUTz712xWwZq9NJk5fsYwKpjFSvaQ+XqoVgvuoV9BQpBESgMNOqu/dj1AkKyq2JV/pZ9by+hBAC/WsdK7C4Mcb7ytVLfVephL/5uc/90/z83B4K9yzIm/BbAH4RQHO7hBTO/uBFHHr0FEq1mv2o7QAHMFsvO29obK07oOd5uQ/5/jBFb6B3ilMawfVaXvowLJ0iANDlCAHrghEgzpiIjbJzv5bocAorFJb3LeL113o2r6eCM9dZwOfPWATsFKFA/gDGgLvi4E7B1JujMXsX/KNRgk9/9WX81bdO42c+cgo/85FTqFVL2wI9US9PXU+ShAUiZSUlCGC+VcWPvX0f3ndyES+e28BLb25gGGe0e1ODuk5cgPPbArtfVjG4Kz+RV0ZWYzMR3KHJKgdrAn1a705x5F/eSEaZAeO+EwcazsMcYPrczu07xLcAbyIClsYCrG2e811vl1VysIYDeA6yRQwA/QYEVJqCb5STdYTj1PZZ5mBcPDI/fAbGf0EwTCd1G41LWKAnid0Cvv3udS7rSZLend1wpopHH9yHkLXDjShhvx0Cde71DxC4utbF0y9fcO+XXpuhM0EgECi9AoqYQWufYYi2AOyafUWAL4BmtWrdktOYIoldb8xDZUnQHgcqitHb7Nl3qneyZO/A06bYNGd/4Rc+8a+LO3/vhHtig5px4R/+w0/0/vAPvzgL4KPT5kmTFOVyCXMH97tI8/bKpQAffHjFqazZkjlAz+/SunmlFFY3BnjpzTUoImcEkhZshSWi1qLdfC12OgAObOmTcIy0Yyj4USqVUCqH2NrqeWvxc2Bs3T3ClWPX7MMr29uMJgPulJbv/b6jUPgJiMm371CIE4mX3riGv/7uGSgFPHhoXjuxEcJrYb61nnkYgjBAOQz5bZfW/C6HAQ4uNPC2o3NoVErY6A3zYI9iUHf/+Y9bA+wuLlOPuRg/314M7tQOu6YasABnVenCGb5xIqrnSTUx9i3ZjQQFkVuGJoTTjGXjcgDs1UXt8vNxdbpA1t2ue55JAC+MtAulkMSJ21QmIJ8NbP267RvTnsDd55vP2DXuFoiEe8YxR2CZFq6Cdme/jzjjIOx74s8EAbSbNTz24D72nWT6lfcfO8OcN7b6+M4L542gA/t92C9KACVtuGH3HfHsJ0gvIRjjaeiXCATarYYb68Q8KjtaAWNzQENXQaF7ZQOjwchpR22xHptr7wNAo1H773/zN3/hafel7M1wr0vyAPDbAH4JwMy0Gd545kUceOQkKq2m43ChneBwozKlwJy5KCuBkye861sR0hQQgbQGd1q1ZQYnk1gE88IGKMvB23EtYAe9lUZM4HCizAfZnmsjCEo4d+6SBXMvMILvOTMhZgFuAFuwF/6zZyX3XYNx4fDnz7RdAeMT7LRN477EzmCEP/7yc/jcN1/Bf/Sjj+AnP/AQSqVgorGdv+ENER0ncVhRgxEvQPv0fuzYHB49Moc3Vjt47swaVjejXCu3BXXglgF77vZ24F6Uj7VF0A2DEL7FvE7hqeYN82vEOUvys3unC++FO8t5WyeTmm0+L5N5H0pYP/rUJh+UHBNA18TsFQI8Mr8DxxoSGIMxHoDv2CWr6vclef/atYQRj8Kg3MdlhiSd+VayVtolukBpoFXzii2pa7dqePjYstNAKPNP0B7xZvGpApyTWdfznV6kAV467WYAAGzNO5RZqial21EOAoC0GgdhtKw0l0/d0KxXTVMEVKrcWKP19RIgXakwTJgcJBhsDfz3LADua8GNK8vEnP6t3/pHf2IeS0A/xp40vrunPN4VhSef/OQNAP/7TvLEowRnvvcDxsnpMNeoWFAjaZak2zTVXqfI21uaKtzoDHXalHutA/MuZ7zDUZrUeMRTCmkq9UHSNkufKMk822nr/sR4mJOUL5VotBo4/qDeEpc88KVSIUml9kZnfqeelzsJqSSkTI1HPObRLpWmneaQ0lPNq4LjloSigr1DFR+GDO3kz3E5RQew1R3ij77wLH7ln38BX/nO60hS6UlZnoTIpLAsCBFpyEpCDkD07wf3zeDnfuQofvYDR3BspQk7P8qbyvvb3Mx6oYPXTaxvWFm2q01eAmVXNmcIlVcmqETKy14NeFtsepIQXT22Z5TVcbl4A+6Ak6IItC0ZNbGOYch0NojBEpmC3YsgL2ekkrd5bNmsOSwNwL0gFoC6BX0nrfuMgvDGTRgwb27Ws1zgeakLuUTPJfkg0HVkPd55B7w8niYg4+Uu7yEv8Msx+eZma3j0gWWEoXueIBBGYwOrORBCWO98grUpGsb4zvNv2nXnjvlxfUn8noFkkDe9QMCuRMr2P6DbWC4FqNfKdij5BnLuPYEzckqgd3XDjgNPg8EYsqx2ot1u/ubb3/4ArcvxRuheC/eDJA9oV7e/DGBu2gwXXzmDQ4+ewsy+ZcBwlDPMsj7rn56IWCgEYqn3jb++NUCSSj0IFcxgldaLl7b+1FynNARGW28LR3uUnu/lEokOyg5QAFa69yBXKVSrZTxw4gjOnLmAYaS3RnVEOSNxqYwaSmmu1kn2fppcKBjGO0h668Mt4zLyYb0T4V999gf4y6+/gr//sUfxo+8+Zg2mgIwUT5KkkQJgJCBBamvhjHhYcp3bEKP983XsnzuE9d4IL5xbx+lLW4hTaRMVPar/nhhoZhLby91K7cQNe2XZO6wMF0jSUnBe42wfmHFsLeyh4Dmq4UwT9a3ZGljPqbK16dRgRpCZe0lfOrflA957AwMEMUnNP55Zs+kDs/yP1UVgCDjw59ML3ry8rZfKcEwIBzW+Nr74WzMsEmmU7G8yOHbjEqBtWp3hnSIf8IZpm5ut4aEji24rWOFeOLFd7G2CYJoUWcNRjO+8cB6jONHPb76fwHiZ1GvfAQQwLrn1uFGgqnQtwqRLoeC+K804zjRrduy49fBuDCnDMJKRs1JA3IsQDYbsPTvp3zIHwmkOhBAIQ/HK7/zOr3yaXgl7dG2+s8fCPS/JA8CTT35yAzvc/UdKhde+9ZRZYqEHbLkUMoBXsO5a2TkM9fvUknykpeBUen7g9W/KZ6To1Ph+p3Spk9JTBZOH/MxLVo4pUzE/9KZ8fSiEpRIeevAoavW6/kCUkcKZj/zEtNH6tzd+6Kk9ejtFUvt78p0L2VsTQLYo6TZZ9mS4sdnHH/zF9/Hf/h9fwhPPnLNEhQi1BQdlhUUmZTjg4BIgwNIxaQ8CmGtW8JFHV/CffexBfOTRFSzOVCdK6xMldhAz56Rpm8QydMVSO8Alfj8flNOLkOSu/FpB0pAS5NREMRQQlrEQpl5HKjONMNGKDKaUHw/qO5NVsHsWHE0DRSYPEXCql94JL8yCKv+/HcAbNKb6PIk2cOPGm5cPhCeJCwG7x0IgjOTvpYGTspm0npuTJ+0ASc7mN2kSuF2A9T9vrsmv/PxsHaeOLaMUBtauQD+70wwUabeEaWMqJZ566QKiYWKfyfWhXxYgUC7pIxDCMkxefwPGF72yfV+rVlAplxgTREydsnHEVJHABKXQXd209bpxYVpV0J8AsLTU/q39++eVKzUH9nsq3C+SPKBV9r8CYHHaDDcuX8P1189i+cSDUELprYzMJmkAACAASURBVAjJ/zqcVGuN1wAoCVRKIUZJjH6UagAXesAFwjjCCQLP+5Kbmw8ApV2l0iBzo0IYKQKOmhIo5JBRsVw6nwoEjj94GOfPXsLGRscSZ53dSVWWuKNYYs+P0mwiMfn2+IK2zbKDIm5rKGrf1fUefv/TT+HP/+Yl/PSHTuBj73kA1UqoZyAFWSM7ULAFGSkDAqDlSsKIGUbeB8y70GCm32klDPDIkTk8fHgO17civPzmBs5c2UKcyEJp3UZlXqrKXGR1ApOkdi9/VpvgJXNMAqCJvAZkMpoy8+skXjFJ2/aZcoyBFWFdJzpjbeHLjVaEYiSWvglr0U3MFgNpsNycybLgI2i+3lfTZy3vLXUPWJypgKy9nRqbGAIm2VsVdIZhYL/B2sefWWT7MBPsG2MOurhGicYtScKWxplKhALmZut44NC8XxdrhB63jLvy2CwgTSW+//IFdPtDs3MczNI43ULaOQ5mmjMMgGpJQCmpvw6h3YUHUiA1O8W5xRR6LARBgFajCnKWY5fImfdEDKigPjFaiOFGD0mcMgme9Q/cp8v7uFQKX/zUp37ts/TaC449F+4bkH/yyU92PvzhX/5fsEOJ/vS3n8b80cMoVSoIzdw2d/GaVdvTHHUpEEjS1BBFM6AN5RNSL/xAEBjLXfqItSZHEjED4zihB2gKkfmYlb0QhWPIyVACAkeOH0RwYRWrV2/o27QcMEfei0NRGrHDFGOTjUm6k7bc7XBjs4//56+ew6e/9jJ+/L0P4OM/cgJzrRoCOIZNYztRUhRQC5CG2aoArbNRT72q0yzN1vDRx/bhg6eWcfryFl65sGm86d1uYLd3x+RVuTg7GpVmPKkbIOD8ldOQDkjKFxbElBB2JzGPbhLgUafkntipt50ltv/FEIATM0HW+sQEZGDM5nEx7IJ/1wbgvbXwTFolyRjC1/64eXpdqM8I5IHdzTEbhgJAtVpCrVJCtVJCEAgkibbXiVOJJE2RJCniRLr3JJw3OyggFIa2CaGZMAJ7pdCeaeD4wTlke4bgEyCGIUOZTNlSKTz76mVsdSO9/4A06m/ivZgBnRBAGApUQgFAQqaOXmpnNdIwTcqq72np6kyjapxXOVpo351y791qjJQAkhTdG13/G2SGdnwVCI8/eHDptxqNKn9SkfktgMwHd5fDfQPyJvwO9Lr5R6bN0Ov2cfH5l3D0vY/bgZJdXqagcnP15VJgOGM9CDQ9FHYMKQBCSm1uaYi444AN98qIkPUMRowCNZBFc+k+S1jdx6ewsn8JQRDi4sUrWWwZczE5ZEdsPusOEH3a4X+3eeJt2tkfxPj8N17FX33rND70jsP4qQ+dxKnj2veCdpGsCYcyzJv3Qj2xnUnxcFKIlVgtn6ANix45ModTh9u4sRXhlTc38fqVLYySHGpvC+w62ThwnyS1m5z5KmE1U7Svp227A3cFDe5UBkn9CnruUOhIe+16jZCB9aMlsZy2ujlY3jo+TcLTeeDOwN5K76Bvk4EwyCbATcF4mgQG8FB5YLcaARbP67NaBbhyBARmW1WszLcw26waYA89CXtcSFKJjU6Ejc4AG50BOv2h9V6njIziBAHj6KZZx9EDc6wv+Qs3ccxyP5tMQeGF01exttW3KySsBG82tgkCQEkHoNWSySkFhHFnm6rsGnnHXAjoZanVahlKSkhF6+Gp74XdWc68Xs1YQKF/o+NNvdGDCMP0aKnejB2TplQKn/jUp/7xl9kTZwEe2INW9vcVyD/55CfjD3/4l38NwBd2ku+NZ17CvpMPanV9Rmr31fXuOpUKS+0aLlzvWYJXBPYArAGWkze0pb3lhGEkeDiaxUcPj3BgDksIskEIYGllHqVKCefPXrTLAL3PdHvkHhumh/TtEH1CpXuKF84G17g0TfGNZ87hG8+cw8/8xLvwsXcfxbGVGWSX+AjF3Gl6RRnRhgM+EztonTA3oAwALM7U8KHHanjfqWW8cUVL96ubA7/oHQG7vevdVJmUOXBXmRRCMFFKWYZUgTy9OclNp/GB12E1F4h4pxG4qwzI+E5zLKibD8qzj4DTHLBSXVE8niQ7zkBwEp+VrkVGIjdHVj1PRpzED9mldQzcAwHMzdSxstDE8nwT1fLuyHUpDLA018DSXAOABv3NboTVtS6u3Oha168KukvbrTqO7Gu7d5F5B5zPspSFO4GCwkuvX8W19Y5Hz0QGhOn7EKFC2fQP0SoNxW5nSO1FVGu6aFOaQOm94u1WNszhDUn/9p26VJBRjMFW3zFQIq8gKriXvv/9D/86e3yROZD5vWfCfQXyAPDkk5/84oc//MtfBPDT0+ZJ4gRnvvcMgh9/m3GCo4mdJ9Eb4kQeoOJEol4tod0oY6M3YpyfJgjKoLz9Toh+OY7AXNMlfS7CSTwgOuak+6xlfhHgU5nt9gweOnEUb5y5gCTxmcscxt8E6I8rc/uidoLkt/vbuXmu4qVza7i8lWBlro4PnFrGo0faVjrT64GNFbMgN7hwL9nzEIYMcWUUKCPll0sCJw+1ceJgG2udCD+8sIkzl7cwSpyR7+6B3aXOqeSz4M6kJSbCa3AX7vuh6Sv3PMi8WgI6d0PzDUwbRurhwCZ3WYnJ4M5TRKaiAmJOkjq1y+kD8t7q3By9gD8dV6RyhwfwntMbpjnQy8OETX9kXxvH9s+hUg5xq0MpDLDYbmCx3cCDhxdx4eoGLlzdRJJKtFs1HF5p27TjfVgYMGbEjSasXnr9KlbXu7q/SGoHgEAB0tgpmCFdCt2UgSSm2IC6ldpNWs3XCWPrIdBoVBCGejMpIdyaeutWTAhHI62aX6F3vQPGTdrxQTTcqezpvQKtVu3//dVf/fuvYjywZ+P2jJhy34G8Cb8G4Cexg+e7fPockl4Pql1zUrs0vpoNm8vn6+NUxy+26+hGCZKUHCw4L3cW7EGDDQyglZV8ONhz6Z4An7LtBvAbzQZOnDyOM6+fx2g03sXytqDP2r7TcBPy/A5Kuf1huxYkcQxAYXWjj89+9xy+9nwZ7zu5jHc9sIByKbDvRSnFJHw4CZ8kWCtWMEnWcZCMEHEJCliYqeFDj1TxvpNLOHOlg1cvbODaJpu7tz92Duwuq2Ip3A8n0RlCzJgSUgEDsM6cHOa6+XNF/8lQjLggRUywTuckctc9bq5fmekAN6oE+w8I36+9EhaQKY5LcdbSGn5beYutmp+7zQUBOZjxHVfPszl84ebwl9pNnDy6iEatjDsRquUQDx1exPED87ix1UcA4fZX4M+I7Hs3PcbnVKTCi2eu4tp6V49zBuaa7yL7CwPugQAg7U6WnlFdICCkgjLqdWFQnubjK2WBerXs+7MHvDq5WQdpEUadAUbRyANwAnjzYJYJpx4IArHx8z//kf9tOBwGAGS9Xh8H7GBxd59gmXBfLKHLhief/OQPAfzuTvIopbB19Zp1AGM3qVHEZfpOchKtb0cgBJbn6vaNjnUWYv5gGAZz0vcY82Ci7HwQ/7iUuzR3aImTs56nbIqVW6mVcfLh46g3pt6wz5bFj+LImw9Fxe702At1xHHi3p0COv0YX332En7v8y/jq89dxtYgZnOssATPEhg6WUnCSRSw+fLGV57kAaAcBjh1sI2f+cBR/NyPHMPDh+dQLQWwqypYp9C44b3kxh+NOzbOqC/sOKZxTUyIgUMqxJI+4Qo0P4QrBqSZ4EkLGZJM4OBOfWT7kvqZ/86lU5ajdfP2gpduwJtAIa+OpzX7+rc/964ZFAfwtITO1wYItOoVvOvUATx+av8dA3gewjDAynwLi3NNNOtlz8GNHaOsvf7SPf2cL7+xihtmFze7VJAtAyQjxUpZoFoR0F5xmetd9r48FM1cB4FCq1Fj79QMM/6+PcbMhESie70DIPNtZc7Z3ysrs//nBz5wYms4HIooioJ+v5/b/6jg2DPhfpXkAeA3APwD7GBJnZIpeuubqM+185b1IGKn1fhxokDLLVq1EmZqZXQiLSkLkxZARrKH3TCiSJVvDZ52IN27S+XqYGKOYVQRhiEeOnEU585eRGerN3Unev1TEFco8duK71zYC2xznKQMw1yLolGC776yiqdevYZHjszhAw8vY7ldM+9YOCmVWeCRdEKGeHZOUeg8gWAbGbF0lJHyz7eq+ODDy3j/qWVcWuvh7JUOzq12mTpfZZvrYseo5BX8C0oV0DMAgBBWaqcxCDb2adwGoO9LmE1BTLzQ8O7U5hz5+eBSIOkZXjr6Pgw7ZbMJ2K2biWlSGnws+DOmQKdj5Wes/gX7Z397TAAxZnz+HVY9HwTA0X1zOHFkgUmTdy8IAbQaVVTKJW2gx916m8FJMfRelQH4tc2+04oozRxZ8FXCSu/k4dOZoxgVvKBRzvtPgpaeBsbjaL1aQSkMzJbfZARK/U+GrG7KlOhn79qWNfSzTnTMk3CJnqvsy+Xg9C/+4k/+WRzHQkoZKKVUv99X/X4/rNVqaLVa5PUOmbMl73c73JeSPAB861u/s6GU+vWd5AnDEN31TYwGQ6uat9K7cTFL8aSeJ4FlZb6OknHRxCW/Isnel+7hSVREXMdJ974TFF4eXB0olvBFEOD4A4fRmmnkM+4yqDHHTYnE90rIPFc8im2/Fj12qhRePL+Of/XlV/FnXz+Ds1c7vvTC1cZWleiknZzUwdJZMOH3mRQWCODQYhMffmw//tMffRAfe+cBHFtpIQyCbaV2GiZ21Fnm1x/njvsUhrBTPlYA4OZ5FTM0NA0g6Us/H6eVbuDQs1IfUA4/L7uRkd5JMrd9yRliVitJ77ZOqtf85hJ91hrfSajsPpx0SwB/4vAiTh5d3BMAz0OlHGJhto5apZST6LnTHgD44dlr2OgMmNTOJHjD4FTLAtWS3w/O54DrO9fP9L6EZRYEtMOyRrUCp9Hi458xgKw7hQCS3hBRb2ivs9+XixM2DgBOnDjwv9brZdnv94NerxdsbW2J0WgURFEU9Ho90el0xkn1eybcd5K8oEk0QFy8+L1/efjwB34JwGPT5A3DAGkqsbl6HXMH98M4ejQGeLCgq6QyLkfdoAqEwIGFBt681gWJ59z4l1shF8/bA1npHoCbu9cZp5fwgdwcPt09cvQgXv3hG0iTxBfRbOabH6OTsHys9F+Y+GZbssOwg7ZlkyZckmcJihTOZ692cPZqB0uzNbz7oSU8emQOlVIAq/vkAitdCuEKhZ/OTuNn71kNgLADJxACR5dbOLzURJIqnF/Vbbm01keqXJ3u5C6yT5K1rrdzooDZpMlBr2DPpiCsSti1nR6azc8DVoYkou4clvCldi6vbpfvIMf2DAGDbQ9nrPzvxBISHiuY21rOEFBaAnhejvG6xgE+DIBHjq/g4PLU+2rd8SCEwEyzivIoRG8wMrFuwEml8MrZVWx2zOYu5pYA7Px6KdTr3/X0J+xaeWXSuLl3Br5K+fP5TNvValah5X3J6JrRALAxTrFCKKhEonNty5af3cyLj0H+HTUala/9/M+/97uDwSAIgkCZI1BKIU1TmaZpIHUI2u0292MPOO70rod7eqvZbOAAD0BsbV3EwYOPvxYEpX8wTf6/9/feA6W0tb0QAmGt6tT2IClHnzuDGJdu9HndKIUBhAD6w0THsf80+DPt9a/ZP49eI5NXiFxZJtrPlCtXhyAIUKuWsbHRyafLFTrx7m0Pd6PW3X6ZQgBLywvjyy0ouD9M8PrlLXz/9DVs9EZo1sqYaVSoRBDv5tTFWdSy/xiQ5tNp0HVmYyTvBgKYa1VxfN8MHj48h5laBalU6EaxZU5yqvxMBMFrIMwOZIaYksc4DXwuXoCWknHpzc3vOucygpWlaSaBJa+DW76LwJ8jHzeP7kngzLLdzbE7l7MkdXsSu/Ddt3JreS69yzR1c86mnlIo8I4T+7F/sTV2rOylUAq1a9wkTe07kwp49dx1bHYjR9s4rRJAGOjVH2R8aVeUcG7RqorgCxyKDJ7dvWajglIYWkGLAnde5m+UpOvp3+hg2B/ZtNmQ29NDlxV/+MMn/5v9+2c3KF6auQsppTRAr0w80jRV9Xqdq5x89dNdDPcNyGcBno6LF58+e/jw+x4TQjw6KX8YBvixH3vc+nuPen1UWw0gCNlcvAP7rf4IV9YGOZpbr5QQjaT1MuXA3Udrnm082Osf2wF+Ls5F+xnZr2qtiiROMBjwLU7zYS8C/14NzWYd8wvtQjDnwUr2LJ2UCqsbAzz7xhpevbhp59NLYeCNBwJyUnPrayftumsH5lZzRKFobBhQW5jRgH/yYBuNWglxoizTWry9rM3ulpNpBLZxADxgpZYKcghl4hE4JsCCc8DL52pVAucMcLPrIkYgVw6VETinNzkmIAfmjgngTnGCjOe7IBBQaeI5vgkC4PFTB7A015w8SPZYKIX6RdDeGK+dv46tnqEdhNlsvV05FHr6MjvWlcNAxbdhzoCzygB+tVJCpcwNEh0DqtNrjZW3XFQpJFGMzrUtc6nsOQvq2ftzc40//vjH3/YVc600rjtgT9NUcoCXUiKOY9VoNDi474nNau4Ldf04gKdja+vSr7Xbh38CE3apK5VC88L0Ji1pKrFxeRVzhw8yLtFwdEo7lFB2oDpiBgD75+s4t5ogSWnJB2AteEmFygB7nCrfpNxWnc+SI6/S13cJ9BVDi/0HV9Dr9jEcjjAuFOGVU7ezu2OYirdSWFpZ9PrdU9MXdGQuyrz4axsDfOUHF/DV5y7h4cNtPP7AIg4sNECGSVDEGJKzGQXjNVS/Z3IXCtj7ji8TZnCYkUQMgVF9ko/zWiXEw4fmcPJgG/0owbnVDs6tdnCjM/TaSs8gqKBAgAa92ZbGGtSRFEcbqFkVKaAlffZR6PGqv68gO5zs9wDGzdIzZRjanIhJyYRrj2U8WJz97bfJFuGVRfeEJ8W7tA7gj+2fw2K7gXsx1KtlSKnw/Olr6A2GZhko7SQHyEDvsqk38dI2TKQyFzQshGBL6ZQdq8KypPb1gIzmSqUQjVoVUtGujGCplWHMJKCMMxyQFK/QvUbW9MiduYqe3yuVwos/93OP/6GUMhBCKKWUFHrbPymECNI0lUEQBFJKlSSJKJVKQRzHMo7jII5jORqNgmPHju0JgAfuA0l+O4AHIK5d+2H/wIHHrwdB6WfHlaOUwkc/+g5jYKel+dFwhLBUQlitgZx5EMhHoxRX1vpMwhIWfAMhUC2H6PRGdp28/e/RG5/45GjZzUj4uoBCuHUDXaDRaGBjfXNct0wdJsL6fc0AaApRq1Vx4ODKNqmKIsYsEVMk3Ud4/uwaXrm4CaUU5mecdO+YPQZIWdVS4SDJrn4W+cGpXHS5FGBxto4TB2dxbGUG1VKAaJQiilMmyQujZmfXJE0z8HPXmupb9TvL41TzvkROZ65az58zUj5ERuKHrUMQOFM+8kbH0wU8vY4PWXmeVC/8OCEAmbid12abVbztoX257/peCpVyiPVOH70+87lhxkAgtHpevz4mfHChIKsWt9FaAFYsHfFwrXoVNOHPd0W0+YyWlZetFBBt9BEZbQOX0m1rMnF0fuSR/f/ziRPL540RKqnkPRU9/50kiZJSYjQaqevXr4ter6e2trawvLy8J4D+ngb5aQCejkuXnn7+8OH3fUQIcbyoLKWA97znJEKzNIOk+qjbQ222xdT2eulSkkpcuN7L0VAioOWSFlf6wyQnURSDs8hce8/pXxf8yMdlsVXk4wCUzPaMvW4ftytMRdLyHMqtb8iOgpp4mQ37D66gVuSHIKP1GFuMGndPxw6GCc5c6eDp09ex1hmhXilhtmnm7hko2+CNGae2p/SCScFFDCfPwyXuSjnEcruOE4faOLLcQqtehoTCME7tFIIP2lSN8K9Fdm7dzc/TPZFLoxNk1fZ2ft/+NoxEQHn97V2zjIFNB7YtLAzT4jEGnJFw12EBuJMqX8oUgdD2Ou96+MCuXdPupbA018SVGx29wsiMo0BoIzsAbCz6gOwDv3JpAU+IIlW9UkCzVkGpFDJAFwzYdTn8yyJvpelQq+n9Ml0oAnilgJmZ2hd/6qce+zcE7ABAqnp+zQBeKqWwtbWFKIoQRZGK4xjr6+vqiSeeUO9+97u3oRy3P9yzI24nAE/H1talX2m3D38bQKFXmK2tPsrlklXX65eY4vr5i1h64Jgdp1IplMMAjvMUpPkE6bKVAuZnahiMEvQiZ8UuACvd82XDgHKqfFMknQrV+ToLKEdWpW9vUzlj1PqAVjN3Oj30egOwLLcsbDfKRWGiu/5t5MK4FpVKJczNtz2xZRKYT7zP7hTN7Sepwkvn1/HSuTUszFTxzgcW8eiROdQqJaYTF04XacZoAEMUjX6SrIy12pP71CeOgbvhhQVwKlcphXajgpl6GScPtZFKhWubfVxdG+DqZh/DUWqmAUzBgW6JVeubU2C+AwXyVOd6gcZ+YBtH01XMTz0MExI4ToTWvPt5Xcmk6g2ydQhh64AgNT4sA0GcgE9YFMsDx6CAGAhtjHjyyCKatQruhxAGAU4cWcKLp68YOwqlx4o0k9DWP71RxdPLFKSqp2FoppXMQHeaFQDQU0aVSgkGW91Yha7H0lLl7mn1ibRqel5u0fdkp72EQKkk1n/0Rx/6lFJKKD0PGxhgD5RS0lwrIUQgpZRCh2AwGCghRJAkiRRCBKPRSCqlgkuXLglh9P23501MF+7JdfK7AXgA4qWXPnM2SYa/Oa7cTqdvJXh3Vhh0eti8sqolfCPNkyEKY1MtE+A0Rwr75hoFa5FhOFDFrikf8yDGyqJRwj3ZUeD1unKLymZlsXKUUjh05ADCwC1Gyh63MxTVtxePcWFhac6AVt4jnO37gnfJE7l3wd4lT8Xfu5FebnSG+Opzl/CpL7yML3zvPC5c106O6OuwAAUmYQrHxHF1NcDOjMPkeYk4e3UYQCsHAQ7MN/HuE0v4+HuO4scfP4y3HV3AUrsGYeZoBT0b1aPMc8ExEtYPnnJr6P3+4myIiaMIYh6U43XoeZS7zJXBFBysDtaJFMf7zvzz1P4wBJVdl8IQc606Dq3M4n4K+xdn0J6pGQO7wOsD6oAsg2THEou3Iy4zLsNAoFFzq0zcO+JMlRsP/B31b3SRJqkr145vTDwfOzb/e8vLrZ6UUiilgjRNhZTSnpMkEVJKYc5BHMei2+0GaZoGw+EQSZIE/X5fjEajYH19XVQqFWOXeXfnZ+45SX63AE/HCy/8+e89/vh//h8LEbwzW7YGeTcnz9X2a5euImzUUWk2oaRW2ZfDEKOEz0tasqHpFLTa7sBCHReuda2RHqk+6Z/lSE0+S4CyxnomCZ2yEj4rEjaHYARxjJQPAOVyCfsOLOPShStUoNc3RSB3V0fuHglCCCwszhdKCS74NyelVWMSqTEXCkCiJF5+cx0vv7mO+WYVbz++gEePzqNR1cuNchvjCBphDhFJ0qexKew9EoH43u/6LE1eYRgcGqcCCjP1MmYbZTx0cAZJAtzoDHB9a4i1rQiDUaLLY/u+K6XdldqBKVzzAgu0DmLdaDcNJu5FOY2AUML/XlhOivO4AThCAVYnp9HOHqBo/DNmyJQRlgIcO9DOpbwfwsPHVvDMD980go8BYpLiAdNJyu4b72mETDppk/HxBQ3w5qXZPecBtxae7gFg+lQMuxGGxumNboIb80VnCs1m5Tsf+ciD3yCAZ5K7BCCMdB8opaRSykr4cRwrpZRIkkQopUQURSJNU2EAP6jVaiKKIqpvIpW4XeGeAvmbBXgAYjDYkJ3OlV+ZnT34FWSev9MZeNb1dJZS/159/Tz2P3ISCENAKZRLAsPYECrh6DKBK5GBWqWEhZkarnciokP2Hgd8B8pEpHR5Vp3vLiYCvukrD/CpXd6uUhnQL1fZEpVi3ZZ3OW7EvpXAf25+Vs8ZFvTGdp/0OED37mUuVEEET7vWHeLrL1zG11+4jKPLLZw63MaJA21UyoHBrAx4M6InoAmytOMXPo7mmAWit3rqyo5lTcMtqJZDYP98A/vmGoAAeoMYN7Yi3NiKsNWPbSV87t8yGQGI9Jv7pgeUD75Q/neoAcGp5h3wMmbBVebug6vy+aSAASO7iwpjOZg0T2wEgVWzWrnnlstNG2aaVSwvzGD1xpbtykAIpHZ7ZEcGCfj1mHGCjVOjO0m8VqkgCAVkaimho12SpYVwU59QSOIU/fWuKUdY5oCzeHqc+ucwDPrvf/+R35NSCgDCnAOzLt4COj8T0IdhKAaDgVBKBaPRSEkpg8FgIIfDodja2hLNZlNEUWTbdDeA/p4B+VsB8HS8+OKnX3j/+//R75dK1f+a19HtDpgUX3COh7j2xnksPXgcCk5lT4YglsjoSBCrKQDMz9bQHyZ6zbEgUoLdS/c2bV7C1+VuJ+WbljM6KdNtxt+48Tkl+Nvk29y/V4ICsLA0n2OwitLpH8XpcrHbgHphHuQZvXOrHZxd7eBvgos4tjyDU4fbeHD/LEqhD6geeIPmzQn9DOQqBSsYWyWA9qFPoOgELAHBwZQRcAWgVSujVSvj2L4WkkRhozfEWmeIjd4IoziF818uWHYtodtmeYOINA0EsaBWGEkwk5RWATCp34K78CGB2s2VH4LVn1Xhs6l9CAAHl+4Nhze7DccPLeLK9S0S2i0faemMYMPezsk7C3zDvoE0ROVSCeWyXs5sXz8f14JoLQ0LRdPw6N/oGN/0TOAS2XNeoj9woPVH+/fPrBspnubjhVKKA74EEKRpSmcFIEiSRIZhKJIkQalUQpqmqFQqYn19XYRhGIR6PaFr/l0A+nsC5G8lwNNx+vRXfvvhhz/x00IED1E9vV6UU9Vnz50b6yi1WphZXkAlDLzBlAV7AE7SEgr7F+o4t9pFmnJXtY7rpUzTA75NVKjSp59jpXyXHYD2zrWrMCX4F1S5bbjTDMFO2tZqNVCrVfP5JnzD2wN6ceQ0oJ5vhz6lqcSZK5t4/comSmGA4ysa8I/vFkfuPQAAIABJREFUm3HW69nMGWJI6/AtBWeuRj3gh0AAo76lRpBqVFBqt4a/XBJYatex3G5AQWKUKHT6I3QGMTr9EQajlJUBj/AL4aR8/6H1Z07Ntc8BY63vRHWG5uRzAGz5HR9/pIp3XACt9+fgzvOVwwArc/fmmvhpQ61SQrNRQ68fgbQ5wm4VTONE//a22+Z0DLrvQhGgXi2DG4LqpfE8Jb9y4zPa6CMx00C2HVmNE2c4qP210ksf+tCxL2ekeO/MwF6kaSqUUiJNU5AK31jYB6PRSEopgyiKZL1eF91uFwsLC2J1dfWOS+887HmQvx0AD0Csr58dbm5e+KW5uaOfA1AGgF5voAGYqes5wFPc9TPnUG7UUQoDkLWnL5EXg30YBNg/38CF6116OjfwAU8V6Z5/e8CHjfMB38YQF0tpC0Af0OuyPTpf+EZ2ELZjWKewR7mrX8e4YJ5rcYwUX9hmNe5yOkDX1W6jCcggfBHgx4nEa5c28dqlDZTDAA/sn8WpQ20cXZmx6mbiTLmDHWuH72kAzH0a5wIQEmBT4bBgy2KUIAW/vhPQLyVQLQeozNawNKsXwKRKoTdI0Iti9KIY/SiGVNrWhVoLp3/wJHAHxi6tIuldmXHPORTQkGSlCVL4OwaFnpeMyNw9V68QwIHFlm3n/RxWFlo43Y18qZmp5Dmd4oyTPZs09XpFj60UhuFynIBLK6DY+1CQiAdDRJ2Bp4Z334qLI/CnOsNQ9N/3vsO/Y9Jm1fFZoLdqeuPtLkiSRCmlhNmhToxGI5EkCYbDoRgOh6Lf7wuGXzbcaWl+T4P87QJ4Ol5++S+ff+97/8t/Vqk0/wcA6PWGHqjn5+Yd6F965QyCg0c8SX0c2Jun0QYlVT0/v7YVAUL55MOOS+ehjJcHTAn4pgxzxyJ9kZSv69BpU5l6VLJoFN5SknUz43wKBuG21Q2gUq2iOdMsBNLiqGKUn9SKbUHdu1BFl/k2sCE5SiReubCBVy5soFIK8dCBWZw81MbhpaYGKkNG7W4hBpClcouZrITGCTyHWzc5b9tCtiaUCrZ4XarDRYGSEJhrVtBuOnuRaJSiN0wwiBL0h7HZLMqV6TpX6Dl9xd3rwnIoCspsNsO4YGuY6IDbphD2Ac13ZRgGxhgIVtRiu463QlhZaOH0+etIU+2vXhNY9+41naJ5deEYAMBK1/VaBWEQWDV9FuDtkrnM4JeJRH+tx6R1Ang7usaGI0fm/nB5uXEDbJ49a1hHgJ+mqYBR0zOgJ4M7T5pXSgVRFMlSqUT6ohyxupNAv2dB/nYDPB3PPvsn/+K97/0vPhYEpQ/1+9GEOXk/Lul00X31daj5/bq9Y8BePwsAOCOQxZkaIlo/D8A3iPOl+3HqfF2i++cBvonnRHQ7KR+A2zt6ArqPG5W3FPynCXfHUNWGxaU5DzB1GI/y27V20vc+npEoltZzbcq1k+p0aYZxghfPr+HFc2uoV0o4cXAWJw61sX++4TT0AKyBHoyBnhIZfsufj3dSvKX2EFnmlsCR6VR94AUDXqBeLaFRLQFmVVqSKvSHMQajFINhgtjsBmglSmu8Z6owBgSkwrdUmO6xXmTsjK3ffap+HFfXl0oBmjXua/3+DfVqGa1GFVu9CIkEQm5JDxiUzhIkZcdVtezPw3MVvQZ4f57dFgmgv9b1NqspCkXz8K1W5ZuPP77/25PU80aTy6+tmp6s6WlZXRzHIk1TEcexGI1GYjgciiiKhNDucAsbeKeAfs+C/A7DrkE+SSJ1+fKz//jQofd8KUnSdhTFKJUCo7bPL6Xj0n28eQOXr/Vx4OSDTpKxBJWNcJJcGNgeWGjgwrUeojhlYL1zwKdys4AP+FI+5eBSvv9TQKYTvDBuI9ZPGql3nAG4zSEMQ8zOzTqInRLIKewW0Cfdz0rr49rDgd3+z5TTH8V47uwNPPfGDTRqZQ34B9vYN1f3aLUzOjP/mUQPAkfLATCnO0zistfQBTqL6SyTylgCAbfkSuh5/XapgnbLAbl2u5sgTiWSWCLm9iZeO13gUjj/IbynNGwLMRBZyy7oZ5tt3B+Ob6YNKwstdPoRlAQSJRAK10/aGI6YN4DeoVLaM2i1UtZpkFfR6yV2wgNyyhtt9JEMYy/ePxdb2JdK4voHPnDoj7IOb7jVfMGZ5uOtNG+kdyvNx3FMy+pkEATBaDSSrVbL4wuz4U4A/Z4E+V1I8TcVzp//1qW5uWP/pNlc+t1eb4BWqzFGXe+r8huNKi597QeYWZhDc3He8Z5E/AwFJYJiVe/QBkCHlpo4v9pBnCj7FER06WPYDvB5ubrvWBoO+jZesPJ9SZ88S00dpkT2aUbwXmcE+DPMLbTNxzkh/RTfbS7FbkHdnCaDeqbMXFl+BF31ohjPnrmBZ1+/gVa9hOP7ZnBkZQYHF5rGBaxxZuPVKtiZDTaQUOMs2RWMhb63qY7JJmA1BeRmyH5BQoOvYziYwxsBNGohGrWSa4nQrqiTVCFOUiRSIUkkUilh5XxaUgitxucdknMUxKR+C1z0Sym0m9WCt3H/hrmZun1nkgG9slyh8t+V0quTqpUytEMkIDuCPTpmmUMFJYF4MMKwG+Xm4ScZ2QGAEJCnTi3+X7VaKSJpnSzqs1J70VlKKdI0hTlb5zhmbbyI49geo9FIwJiuTOq72w30ew7k75SaPns899yffv797/+FH9/Y6P4njUZt8lI6K90rzM+18OrXvoF3/OzHUapWAJAVqRVz/F3bDGGj+aNDSy28ea2r/UDDEJEMUAtTCONlJwA+YCWrDONAV4Z22rLonpRyO4F9+rDdkM0Uett1VjcRBARqtQrqjToazTpaM82pQJzCZDB3EdsDOrsaA+pAXlq3v6YEdj/OxXYGMZ57Yw3PvbGGMBA4tNjA0X2zOLrcwmyjrAEyM9+qy3HuSAEwgu2YgQxbkDeUcmK1S2OaRmlo3t2s2meaBh3KpRDlEtCsllw+oaepklQiNWf6zvPjnvoiu0RQt49ihABm32IgX6uWckBrBwGbIycNSBgIVKtlQCkflOnlOpGEjUE9SGWaYrDezTZhbOAS/sJC4/PHjrXP0NK47By8Mk5wFHOGw43tpJSSwJ2fkySxBwf6u+3tDthjIH+3AJ6O06f/+jfe/e5HPrp//8J+Z12fB3hueT8318KVK2t4/Rvfxqkf//f0Wl4ANCC5+pGinbMcvU/zocUmzl/rGo7WUdv8unsnNYyV8AFMUuvbojKgr4ldMcM5DkxuavTuFNVv96fC2hOGAWqNmgb1Rg21eg1BMNkDdOHjTAPmBRG7B/VMHYXl7gzYi8pX0PPg51a7ePN6D98KtHHc0eUZHF5qYt9CA6GnpteaK90mMq4rAH5iTi0TbNoj8q8/8PK4JXnc+xnPY40IzTfFmYpSIFAKQ5DhHBWrpEKqHEOv+4Kpnhl4kLgpBBAKcd/4qZ82VCsGSoReC18K9FazRaM2DAKTXvlT9eDcgYnnDAIEJLShHTeyyxvb+QyibWO1dOZd71r5IknuNOcOXc800jyYAZ4MgkBIKRGGIcw6eRHHMSqVioiiCI1GQ5RKJXqUieF2SvN7CuS3Cbcd5NfXz/a3ti7/j1Ie/5dSqiCrrudqe2IA2m3tzWrtzUu4/PxLOPDOx0AfOwAL2kS+sqANKJTLAQ4tNnDhes8S1Kx1fpGEr+MzgG/ucSmfOm870NeEbHpZ/raA/7hwG0X9SrWMeqOOugH2arWSq3I6EPdvTJNnHKBPKr4I1O2vScA+jlGYEtjzmgCiyQobvRG2+mt46fwaKqUQBxYbOLzUxMHFlpOc+T8CcUPYSf5nWA/6jlgyn3A7zKaRD3KqYksgIGYgLHi8wQW7LM7VjCAUKNn0+o41pmcNydLmRrV004s/7rUQCIFKOcQojhGGQCp53+pgLenNWniG50xWLxhkduDq9fBpvHN/HkGA6LHHlv5vISCzanoC8Z0K3STBZ6V5LskbpmCqgm8X0O8ZkJ9Cip+qmJs9Pv3pTz7x8Y9/7GvXrvV+YrK6XgN9pVJGrVZBFI1w/pkX0FxawOzB/Tm1uS/dw6dWSjuV2L/QwJUbPZA63/VNMeCbrGMA36TMAsYY1T4wXpLfCfCPSz1dzjsf9h1a0TvIAY7gTHwAVfBrbJIJUdMBer49OwB183N3wK4TjQN3m19oaA7opgDiNMWFa11cvN5DIFYx16zi4FITBxeaWGrXrAMe+jCsdT0AJ4UxqUw5Od3J48osy6I8YCDtlrwJr0zOaLjA17vDNCtrhOd/VpxZEIax0HGBEGi8Razqs6FZK0OmMZR0gG5XJ5jX2ahX9GtVjjmiMVYM8C4i7o8w6o921CaS8g8fnv3zxcX6dZLghRDeGYCh7Z5VvXcuOtI0zanrzQH6je1J3m0UYfYIyN9CNf3UVY47RqMRVlbCP+10Sse63eFDk5fS6d+zsw1E0QhKKbz+xLfxtk/8+yi3WsiqzX3J2TciArS7z+W5Bq6u9z0CtTvA1xfe3DtQqNqnu1LK/HAb26u7l+N3OqJvF1PQnGlgbm7WottU7RqTaHze6cEc2AbQCwrIpZhUzy0A9nwJugwFWMM5rZJ3gLrRH6Hz5givXthAuRRg33wDB+cb2LfQQKNSsmOd7Fi4gaoulyMu+bhja9dZcMOdO7Hxl8shd1YmfR7cweMEsQ/KTyHgGAgou5vjWy00aiV0ekDR6BNQqFTLCITZ2IgTKgW4NfV8Xt9IREpBJSkGm/1dtavdrj558uT8U7RMjqR4rqpHwTw8t7rnoJ4984Mb4TGQ3y6wYXvrpfm9PhpvFfjviBk4c+b1i48/fvDPgkBspCktn1N2Pt4BvT5mZpzrymE0xOtffxIyHhlu1UlXSrmtRHUMjNrK3Ws3K1icrbm8NE9PpShkyvC3jIVXNktrfrg/xhSYNOTxLoczRcfYMC7Dthl3XeJujiAMsO/APr8/xxw8Tf42603qY+W/U57PPlO2XHp/vNSCtuXqtXUW1MPKdeMoP4ZcfU6FyscPWH6Va7/fXm/82fLcuEoShUvXu3j69Wv4q6fO42+fvYjnz97AxRs948JWf6Bmj86c2tt3TMPiKa3wbwUOfZF1Pqcldn9KgOqg+7lrygN2zrQw4678LROCQGT6wo3WaqXMmB/F/vu/imiEShX66/3ctAjgGMDsmUK1Gp5/xzuWP8Pn4bNz8uag3978PB2MKfCs7LcLyqlLpz5utbHeXZfkt5HipyriVh9f/vKXVz/4wQ92Hn546Y+effbSf5WmqjzJE16r5Xu22ry+jjNPfBsPfuyj2mBLsaE7wbkNDKFcmKkiSfXGHSaL+ywczUJ26ZwpwVbGLZI54ddRpkTWy1xdXwTH3gsZh9fbvrWdAv3tIZgrB1YQlsIikpJPrLZNMTYU8+QqfzWm0Cw55CA6rti8rJ1tBys1X7S9kS1FZQaRxm9jJAe4cUZaKmtsB/MNKH+TGwBb/RE6gxHOXd2CEAL1agkLrRoWZquYa1Yx06gYcKZxoEU/8+XAOapR9i4C4a1osf7qTQLf6p65qQW83+YR2JlqdS3xmApTdukt4Mq2KOgVQvkBVSmXEIaBZ/OTA3jLUfrRkAr9ddoffvpgmLfuY48t/lEYipQDfNbYLjs3Tyr6SSEIApGmKZgUj3Gqe0xHxHZKGKcOe0GSv1lQvuXlf/azn+32er3B8nLjzQMHZv4dl9rJKQ6X6sMwQC1jTXvjwmW8+d3vMynKBCZxWZlMIQcGy3M1tGolJ6XBEWVP+suUkZPyrYTmi3lcUrPHNuvk1ZhjqkSFiacJ2xW482NmtmWXweUP5I8Jj1CYnt5NRj3g3kfmXWXfCfujBozTCIDVxWXt/HjI1w/+bOZfVmLnZbnWFbSbjTGvr1jd2eeEzefih6MUV9Z7ePn8Or7zwyv42rMX8IPXruHM5U2sdYZIU7M7mRCA0C5wSY1upX/QBiXsIQwYW/e28AkIB/UseGeKyZ9JijRP8VZV1yeJ73AIoGWLoR9pf6pcNE+glMJgl4Z2SkE98ED7T1qt8laBBD8VF5aR8L0jjmPIgnn5rPreGN5NEzwMupXS/F2V5HfwILeTCSgs/8KFC9eOHDly4OTJ+e+sr/cOr61FH1ZK2g1ssnP1rVYdUeQbhVx59QzKjQYOvOMxkMqS5v4YzSuU7iGAAwtNXLje1dvTKibDMELlylFMAsnP4+sU/kfluQ4Fc2u7wzApV+4F7KSK2yAQlUolrOxfnphmGyaeUk2OnVCGKvjFL8dmtffH1F3AcXFaWtzG4tKKpPaicpRSzO+8jhPmH79WCvZ9WkYDsOpVrQ1Q1ksjLUNPpcJad4iN3hDnVwWCQKBVL2OuWcFsvYLZVgX1cggy4rNwK2DbBTqZ9tPyO3LxLNh3A6icyheicPubTE9QCTTn/NYLSZJ646ZUClEuBTm6wkZUfmCx6+HWADHzaFcUrPFe5ryyUv/SgQPNM+Mk8owa3kr4485JkkBp73bgznC2A3HlEfUdh1sykO4ayO/Q2G7b4m7yyIWXX375+qFDh/alaare/vbFf/utb10+OBikx4vU9UrppXTXr2/mynnzmRdQbjSw+NBx3VAu1U8AfMMR4OBCE29e72JI7m9N4izgm6z+B2RuZxnXcaBvDe9uIbhOO0oLq7wNtHLlwApEEDDJdLpQgJ87yLNDMGfpxgE6MAHUM4UrPwnGlTotsPOKFYE7jcmM7l4BVkVP4M5uM3AXbNy7+9pVrlPKAwr9QYxomOCq6CMMBCphgNlmBbONClqNMhrVEkIRIPN55KR0spJ39zM+6oVhhBXsHvTcqp4zBLyceIeq5fslJHaOWms3y6UwN4/ufXcqd8NqfKLuAKPBiIH39O1otcovnjw59w027w4AfE7em5dnQA/jm57OQk4h+WwnzWM8ZzgxGMv/m6aCd31OfkK47dL6pONLX/rSjY997GNS+ymWycMPz/7Bs89e/++klLNFS+r4UrpseOPbT6FUr6N9YB9ht25QBvB1nA/4QQDrLCdOpG+hzwBfk0pWtrsNlqgQ9KlEO54yw4pb8N+ucCdkn/Z8G/Vm3Ye4XVZcJJuMSzRVFTsGdJ2pCNS9S1WQtrDcHQA7/6E0sCsLyg70LZNpwV0wF8+w1vgKbg6db5mscl5wFNyOeK7+OJVY60TY6A4RBhq469US6hW9kU2tEqJeLaFaDtnWr75HPMFFfdDnKOwz5Ic/d3XL8+jtfN+KIUn0nHwQOIDPStju3SIT726PBiMMu8NdtaFcDq89+uj8ZzjAF4C5IPuj7UA0M0/vLZ0r+G0Bf4qmTkVRb4W1/V0B+Sml+G2L2eGxo/K+//3vDzc3N/u1Wq2aJImq18X6oUO1PzhzpvurUsqgaCldu90sBHklJV7/+jdx6j/4CTTn2xCG0vGNOCwnm1k/r6CtVg8tNfHmagep9EVtbw2+OY2T8l0qH/Spen8fZt4MznlQvkwBezwIIbC4vFCIuBOgdXKhu+EV7HvYOaB79YwDdXuxHahnyhzXL+PA3dSgR6OT2iWUXSZFe8ZrT2VAKKB9mQfCSecW3PU1pN6+1eG7Yh8KF/MVU0m533r5lcBwlCCOU3T6BPwCgRColkPUqyFqlRJq5RCVSohqOXTSuSlKKTN/b6R4UytoOZ7XKZlmjN6CknyiDc8QCIFyObR2KUDB0Bo39JVCMooRbQ121YYgEKMTJ9r/XxiKeBpc5BL9OA933OJe22NNN8fOjPGA7ankbZVx9qKFyN2S3nPh9ddfv5amqUySJE3TNF1aKr3Sbgd/xo3uyANemko0m+Pdn6Zxgtf+9uuIegNwgzgtCVmhBBlrKRvKYYBDSy3rDpRycGMxCuYOM4byjfeUS+Tlbc40p+xWeM+QPVwleycopdDvs75nfzkrNd5nkw6MeVQu4WYPC43UrvxBhfh9bO5R2axupcDaPN5wjhsCZg3wvKbzcePVAf8ZMn1hW03j2usfapuryPWh/x3Q+6J3QOwD1e8YGMXmv9mDZKRGPuDjJEV3MMLa1gBX1np4c3ULZy5t4PzVLVxd62KjO0BvMDLb1SpP2ued5Mf5En2ayrfcvPxmZwARCFTKJeQW0tE7y9AGT8pXCskoQbQ5YPnGnVXRWR061Px3s7PlG1RDdjlc1vgua00/rVEeN75L0xSj0QhpmorhcIg0TUUURUjTVPT7/amW2WEbbLpZI7w7DvJ32LPdTZXz2c9+9mocxzJJEhnHsUqSRB49Wv7rWg1/7azspefqttWqja0sGUR47W++jlE0ckRzWsBXCtVyiKMrMyiHQQZo8oA/FehnCPHsXHvK7p8cJjEAKlvpHQwbaxvbgvW2TeNAO+b58jA7hklg4JPvM1ZPtm1jQF0V1sXLZ/Uy/LsZYM+PLVeua7/yrmGTOeB3vcXRXmWu+fM5IKd2wT4r7Bhntbv7FG+/Ob0jXS+KsdEd4vrGAFfWuriwuoXLNzq4ttHHRidCpz9CP4r1FraJhEwlKxukkgPw1puX73QHqJbdJjVEgFxfw1z7iE3vIElSRJt9tkfAzurft6/+lf3766ezIE7z6jTPbuokJkAVXdN8PDOwAxnf8fM07TI70d0KzNt12IuSPHDnVPkTwze/+c3elStXtuI4VnEcS3Ooo0fxx2GofpCdl6c188GEdbLDrS289rdPIImTHNGdBPhEiEuhwJGlFiplsyyF00HlAH8q0Ld16YrKlQpqdX/N/+0Inhw9DignMQa7PKJehFE0Krw3bTv4X+651PgjA4ns2RnoFj0m++G3weFeEajbejLl25YwUN8psLvnNe0x9poEeX5/Acosn5bUKiVtPtg6dTyND14pjV3JQJnq9zuJdYZ9Uv5gsM8sbXLF6nTviYpIUonRKEE/itEdjLDVG2KzG2GtM8CNrT5ubA5wY7OHzV6ETm+I7mCEwTBGd4fuV+/10DfTlK4/2Vgc19fmOk1TDDaKnd24wN5f5jw3V/ne4cPNp40Urkx8YWHc+Q1jBMh9bW4t/aTA5+Wz3u+YtD/poSjcVibgboH87QbxW9EGAUB885vfvJokSWoOmaapTNMkOXQo/l0h5Bu+Bzz9PhuN8dI8AAzW1nD6a99AmiY2jujQOMDn1DUMBY4sNVGrhJaQ8oI8or9D0J+ZmymAr7sf1C3621jfKIwfW+8E4J4eyDP9vmNA91s4Dahz3LMtY8BeVG8W2O1Yyr4HKgfsPrWb2prL79cLFpcfjC4V5eCSu0vH+8SV765dWdnnyJUFpe0FWJ9ywLJttf3vg02SpogTiThJEY0SrK5NvxXqvR7SVKI/cIZyDuDZq+V9yfpPphLR5gBKOsaqCMiLzgDQaJRfe/DBma+ZeKWYcZ0Bby+ONK7EBGTPXLqfJMXTOvk0TRUAJEninUejkQKAwWBwS7DqZlT2dxTkmap+quQTjpvNP3UZn/nMZ9Z7vV4cx7EajUbSHErK0WBxcfDbSqU3sqr7Wq2y7dakvdVreOOJb0OaJRvKJ31jAZ/iAqH3om/WaU9mX3rihU0L+koB9WYD5XI5D0BTdtheD92tLpI43QFwA9me8P68vp8CyMHTqB0C+u5AvQjYHQiPB3YHq7yc7HPoeEnjzpPaAZLa7bYIWYLNQVwpkHjP+9RPx9+HaxtPA+8adn7c1wD4ZdFv2/sccOjdsnaxR/EYEHqv650B0m2cS90voduPALixxAGd9xd1on0vUmKw2dfTHjkAL3hn4H0OVCrBlRMnZr9o7gvA+aXP5FWZMzEBY43u5JTvLuskh/u0BxzoTwi3XY2/F9T1twvIbza/AIAoitTzzz9/gyR5o7JP4ziWQgzWm83Ob6WpHHBPeEop1Ovb7ye9efEyzn7ru454MmJPwQcFHUN/AgoH55uYbVQyBHxnoJ8F/pn2bCG6FwLWlB29V4JSCqN4lIXqMaBdzAjxhx/XJ37a8WC+I0C/CVD3GI8MsLuxMB2wI/McHFuptS6N1/t8kLJrDtpZtbxrA2QGkCmeF8/2fQfA5nh5ma4NFlMYuOeAxfWIS8/axt8X5RFCYLQLT233YtjY7LF+YPSLza974K0ApSSirQHSOC0A8uw5X2epJDZPnJj9izBEbNIqnodJ7Nk5eh6vMnm98zZSPLaT4vv9Pphb25vGp91K83cM5CcY3E2V/TbknXpa4POf//xaFEWSS/LD4VCORiMZhltny+XNfyalSrnavlQKEYbbd+/6uQu48L2nfSLLCMZkwAcgFPbN1zHfqhYQW8o4GfSzwN+YbSLgbR+HZNvcLvg273ooV8qo1WoTGzzpefg7yIJ4MZDnwRyZMqYG9GyfZ0DdazvLk21PFthdmdMCO28Ea7VhcElqV64BOr80tZhJfOX5MmcSn9dR/GHgGBhTln0I5rOExngO0FldyDALyj5LHmjcfR4HLw6UBkCzXsEjx5cn2ubcLyFJUlzf6Nqeta9EMgbQG8vKAHyEZKSnKxVLQ4F1a+asIASGx4+3Plsuiz5nrAjYuQObIimerkkdr4yRnWLGdqxMVXQ2vwvXzbP7aoJG4LZK7zzsBUk+G/YSIwAAuHjxYvzCCy+skfEdA/s0iiIlxNVnlNr4/azavlKZbl/pa6ffwOVnnnWEhhHYLOATqZLQdI0+nuXZGpbnagwg8sTclZkHfU5eBQRaM63tGz4BKLdLMiHLbQ2t2ZkcUGePoki/v4pBPPscxeUTUbLQOB2gA14boPx6ed4iUC8Cdj5WxgE7xgG7ck9AAOc9v5IeGDtgVawCWMbAMQBEhKWrDGB95hphwcEDb9cm6gsvbWbe3UvD+4q3lZetXBvce3PlH1yawcNHl1EphUjT+19dv7q25aRmilSZd0J9b/pz2NEAXwzqPtibWHtPCMhDhxpfaDZLaxRXdCaAVVPMx48BcG6MVwTgAGCleTrHcayklCKKoiLL+mnCLQf+uw3ytxKUd5tvqryf+9zn1vv9fjocDtXRzQmJAAAgAElEQVRwOJRxHKsoitRoNJLD4VAmyZkvp2nn32SX1JXs5gyTw5WXX8P1F1/yiL5PTFFI0SlKAmg3q9g/39APpODK8oirX6YZ7Tkga7Zndz+81BTHLrLc7AEh0JxpjQHqYtAe09wJTAIDTVZCEZjvBNCLQN29z+1B3YMmBuoseSGw83GXB/a81K54pfREXoNdb9i5csorTTrnjczWY6+pPFuf/i298Yup8nEpkPelNGkdg0Dt4P1Hz6TjyqUAjx5fweGVNtscR5id2e7PIKXC1eub3nvytCJsbFJ/DnsRRlHsvZssQAO8LO+eWl6ufXV+vnKRlsXpdujfzGjOtrFIkueSO7eyH5eHMwFZtX2apiKOtX99OpO6vtfrZUnHTePWblT2d9Lj3V4A9N3mE+vr6+lzzz23eerUqZnRaKSMIR6p7RFFkRwOf/Cvy+V3NoKg/glaUreTcOG5l6CkxPLb3wba4AMAoHQjir3YKddqQG/NGQS4fKNniShlUYKNOaUldltCpnfCMECj2UCv2yvonlvAXhYh57Rhl5U3Wg1vGiIvMWwXxmdQYy+2ST+mEblY79WpwnuFzMiYm8r7kSuxoEzlcDJbg5QQgflUaNwqAaUk9J5wsOunFczr4++QVj4JKl+ndXmVHaTaYx6glL5LWKu90pqtZ4X+VgQ1mNZuC51PSQURmHoFbLv45jgKyuQzzy5cm+zmO6a+hVYDp44t2d3WuD/7OEmtx737LdzY6OhNaeB4OGL4POaUAL4bYTRwS+2yoE7x/tn9XlysPrlvX+0VDuZFErwBfAvoWRe22TPPV7Q2nu7zc1aCHw6HVvqnYwqjOyBPzW6GMhaGOyLJ75D7uNuAPjb83d/93Vav10uiKJJRFKkoiuRgMFCDwYDO6dWrf/e7o1H3r0man9ZKk8LFF36Iy08/i4y2suDjcQITfWQkXTSrIQ4tNxEGguXlaWA+SF/Sz0r7zdnZghbalmT+bsPoHBfU7o4WSfEFknb2KPxzdGu8RF4AprrvM9KJzVhQO39HlIf3NHtHWUkd1Ppx0rotn5frN7ZIYveEc0rMIq3Ua6RxBbfmXSnF1ORmtTx7PiqES3y8QQS0iqWzEiLFeVK5AxcLINSXvC6bz39Hti72rqxUyqR4AeDBQ/N424MrqJTINa5uAick96M0rxRw5doG65/MewbrWyURdSLEUQz+iujMPoXcbzrPz1e+t29f9XnpNpsBSe/mt8XsIkmeqewVt543+W2aTB4FAHwHOpN3LKmjdfFG43srMOmmw91W1wO3BpzvSF2DwUA988wzneFwKKMoSg3YSwP2cjAYyH6/l54584V/HkWdJ8iv/U7D1VdP483vPqUHJvIg4sEQp/QsWa0c4vBSC2FAEhQncI7YTwJ+oz7IH4VhAjgycrzz3rg1oVQpo1KrTQTqsaA9puE+cKrCwwJOUZHZvp8G0JXfnCyTZgEtU48DPjYSvLrdG9we2OmG3zhrSEd1MIKvS2auaTmTw8ABlglgIMuBXtE35cBW2W+MP79LU1QGJKyK3zTC3ZesbF4GfR9KoVYp490PH8ThlXYG2d1vo19FKlk990m4vLqGaBiz/vW/A9d32oo+Hsb+GGVps4y3D9YK7Xb52QMHaj8wVVtQzgC4FapYeV667Jl2nFNsvp5J7DBnlT0zSR1SSjEaae0ESf3Muj770u8K6O8FZzh3Ks8t6dSnn366v7q6OjRALweDQarBvS/7/b7q9/uy291Kn332T//pcNj97g7a6oW1N87h3JPfhUxT98GgAJwpzgN8TZAq5QBHVlool8OxQEKhqOxRNGYnqHEgOJGOuUTb/+2w6ClCa3a8IeHYxxkD3DlpnBO5XBk+kI8D82kAHbz3GKBPC+o5XGZvZLfA7oAYXrt8QzrWVri0UH58lgmw49sa57kx64DEtdGCNBhIZwDFAnWmH7P3AQfuflpg/0ILH3jbYcw0a7CTXoIRDWFizfa5AveXNN/tR7i0um76Q0IpA7TMlkIpBZlKDLYiJKPU9h2/r9h4oPjs/Var9NLBg7Wn2DsSuXeWz6s46HONaoHq3pvLV26+3rrBHWOYpwCnrh+NRpBSiuFQ08zhcKgI7CeEXWHaTufl7yTI3w1gv+V5lFJ46qmnur1eT/b7fTkYDJQ5y36/n/Z6Pdnr9eRw2Iuff/7PfyOOB8/uoB1e2LxwEW98/VtIk8QSHB81MoCgjNU9tItRqRTKgcDRpSZq5ZIPmFnAKQD+4TiQn9hB2xw3Uchu/yCARqs5HVhPA9z8yL6SbYB8LJhPAeg3A+rw6nAReWDPNq4ojo0/OKM3HeXmmWy7vLXrikn47N16AMuenvLw8hRLIxkYUxkmnX0mBihSufQMdTxmxOWn8vQ5CATe9uAK3n5iP0phoPefFw7Uhbd3LXuTQleV7kKrt9dCKiXOnF+1/kBsXwLuPUDbIw02B0jjpBC83TXL578StFrha4cO1b4NGoUmGGlaZSR2xdJxELdnLvEX3LMH4FnPQxlVvblW2fl4mntnqnoMh8OpXOOycEtV9DzcdpDfoZc74M4A+01J+FtbW+r06dODwWAgh8Oh7Pf7kkC/1+tJ5xShG7/44r/99SSJfriDZ/FC9+pVvP61byAxKiEfXHyC6yfQhwQgAoHDy03MNSsMDHxg8Wm5/nhG0TAHPDcdcig5xXGTod5sQgTBjgC7CLhzAD4GxDmQTwPmulumB/Sdg7oBTw7qirehqHEFjfdy8DZSDUQ8eZw5Z5bUWSZKwkqANk8GuJVC3hGOdL8d6AOKtVNlgEixZ1PefQVXFP8+XP5WvYIPv/MYDi+3DZgLI63rei22Q6vozV0n1YOWZd3Sr+mOh/MXr2M4HLExysarGRNpwj3ZwQCreScmrZtyyedXSqFeD84eOlR/UmexwQIzBRZvVe6sHkVpAGdcp5RS2fXxLO1YIzs+N0+W9CTtZ4G+0+mQ6uZOCapjw16Yk6dwJ5mBm67jwoUL8fr6ekLA3uv1ZLfbldk8g8HG4KWX/uJ/SpLhmR3U64XBjTWc/tsnMBxEOXRw9J5JpUWgD2C5XceBxSaCQHig4gG/SZ6kqZ1j4sXcBgzePmyLzpOP5sx4KX4awN4RgOfAlT8G+8vUXQjoGAfo04O6B+zZTs2h/XTAbttra1JGOlcWOAHYdI4uO6BWJi0sY0BSliP21AEKvDx6FvuUecDJtIHn99LyvuIMAfxnPH5gDh9513G0GhVv+t0BuLDqelLRa229vueW1Glr+05vFxqyPRBubHRxfW3LLVeU/tJFKRWSJNG7yaXe3LjPsMExXe7a0bFqNbhw+HD9CUBL32yuXbGyFAfxzG8Ca59DgAN8Hkfz7MaiHhmJvhDw0zRVWf/1GaO7oi68E5iVC3sB5O8aUN9sHZcuXYoJ4KMoUuPy9HrXuq+88vl/kqajN3fQBi+MNjdx5m//DoNOZyLKOkzIgpe+16yVcXRlBjU+Tw9H7ChPHEX5wseEu8oAbBO0wV11ciOnAe1tAJzCNEA+DsxzgJ55d4VttZfFoM4hOvcQ4x6cQDHzHPk88PMQoCulpXMF+IZ07r4uycznKlcfbJGKxzCAYAAulTcVwJkJSX2n4ECf9w2ll7yfqe06rl4t4UPvPI53njygl8Fl5tkBDd5BZg4ecNI7nz3tRyM89+ol/P/svVmQHcd57/nPqjprr2iggca+cQP3xaQk2xKpxb6SZUvjkHQVcthxvYXtF9vXETOjsH0nxhGWXuZpXmY8jrkxcWfCYzskWXEt2dJYurYlUaZIiCJFggSIlQKxA92NXs5WW+Y85FJfZVWdU+d0o9EgmeTBqcqtsqpO1y//X36Z9cqpS1haTd6bfieEhaVVvPnWNQL15Lelr2cURuit9FKvi82Cvb+Kr9Wcq3v21L8LiGQiPPkp0/ss+gcKekYTbM963Wkg9WvgaxM+gES1UzN9FEWCOt+RF9MMevxtGOxvJ+Q3Cu7DhKHKRFGEmzdvxgrwfcPKyuXlkye/8fk4Dn4yZJtMCFttnP3n76I1v1AIk9xAH8ZCvq52z+w4psdrJskGiN8LaOGcT7lQkq23rEMgF79ZWxtE3n+5KmUAyDEI5uWBboDUD+opeBfF54DdPpd+YNelUh7kiTqHgiwFLKAfviqPLk/yyfIaxAT6ugOQ44Snt7npaAiYF9ySY0Nv046Ahruq8+CuGXz4qbsxOz0GQKv0RMGDWXPfmRXPKOAZrtxYwctvXEK7J+dTHz9zBcutOwP01+aXce78NfPCHXq95H3kCHsheitdo+A5T81Vz3SuyC1T4/tAtcqu7d5d/y5jiA19JctpPQbUWqkDCfBztqHyMwv46tiJUteRRLWbKXMa7HmKnqxZDyEEK+l0p8Mt59rtgPyo8L3V+Ue60EPMg2fLyxeXjx//r38cRb3TQ7bPBB4EePM7z2L54iXr4S1DBjwWPPRzjgGYnapj10wTDkMmX9DzBxBw/XE9TIegFLAZQ328WRrQZcDdF+CwuZnsFMI858SLgE5Bmb31Nrz1AeyKbUDngN1uFD2X1FGTY+kzEuQtcqT1ABLTLicvkhcCaWBDtyOBcjqOzL8nY+pcpE3H6TzIAp1AJzkW0KxX8fTjh/D4fbuUc13iVJd2spP5mYJ6Yi6UFemHSRRzvHbmMk69dV15oMt2Rpzj1ZOXcP7yIukcbb5w6eoi3rp0w7w0yDjc8eTeBt0QvZZvXVt1D9SpUUe9pNOlneAEajV2Zc+e+nccBxG9HkRt5wZr2lwG9DqK5BdCzZencfpYOQ54QhBlrxzvAGQUPYIgEOqTWkGvZBgq/zAe9hsN+Q2D75BlbnV+U6bVur762mtf/U9h2H19yHpMEJzjreeOYvHMWWQe3vbzIoc+FDJjjQr2b59AreKSHjpHFISFABvM9VIIvuWhMdaEw5zcQ5dpYRG8bYDnQzzbscqF+RBAz0Ddvu/9oE4AbXd2chs4EOzZcnRcXteRPMx1Pq6WtddOUgneZVl1FH1cLjJTs0AcrDi4qQG6Tg0hej8EaV8KNDD57tq7FR993z3YPjMOxpg0wzOWGodPxt8TuCeqXZns1V/88moXLxw7j+uLq+YaCEApYAn7t64s4pVTl9Dzw/wf8W0KQgicv3QDl68tks5Z+nfOhYDf8uG3fQNsc56p7zxFD7NdrzsXd++uf48xxFxRdRDc8+Btj8Fr4NMlcEl+Y37XWW246/F56ngHpD3sbYe7IAiE5XQ3TFgXRzs7bPQUus2UfyOOkZu/213sHDv2pT8LgvbLeellw6WXj+HqK8eMupEh5+FvBwtSnsewd3ZcvskOQOAHyYOzqLj9oTwoxfShMTuowkxojI9lOFcE6UJo58JbZFtT0MwsyItgPgDohfc172BZOGegnml03o3MO7Z9TGTBDm2u1cdTkObpfDROEJO8uSZELRqAQ04P1fcEHGkvef0wB0+AzpM7RoGv78F4s4afe+puPHn/HlQ9rd6RjL8b2GvwgzjaKeBrBzt11c5dXMDR18+j2wsywJOw5Mb6sLLaxUsnLuDawsrgH/UGhJVWB6+fvohr88vpMXjByZsGOXqrXbVMbVq5Z/529L1Wvx1O7lez6fxk587ac4DgCvCkY1jkvGc+wo7LU/Z2p4DOi6dj7yrOdAgo8AtAjyiKhAa8/m61WvTP97aDfiPXrt9s4bZ2Onx/1X/11b/94kMP/fv/oVabeM+QdZtw49RZxN0udv3UY4DrmSMnLkAUCKRZ+sEF6fikzfeNqotz51dHbU4xfklCqQs5MFN/0HuVCiq1at/OyhqqH5BFDNrsX67MRRQ5camc9n3PySty4nRCv7K55YR526sjkvFo06cQ3HibS9DKNBlHASHbrq2RQsifglD/yuevqoMDwjGHh16vnuYBYJbGF5DvvnGVcYcJhvsOzuKJI3tQ8ZxkzfmUOR5IW0aJc52iPE32wwg/fuMSFlfacJSqd1TbOZSqEgB35DYX8lzDKMbJn1zDlRsr2Du3BVuVL8BGBj8I8dblBSwtt9Q9szpbXHXgYrVMbRCBQpuOw2fVvOrYEMCPjblnt2+vvCS5a9S26UEKIWw5X0beZxzqTGEyhY7MtRciZ5w+z2yvFbwGPX2PvOVZb/9xJD/ycmHY/H3DRkF+s6n4W2IWGTaEYTd85ZW//l8efvizf1ivT39g1HoWL1xG2POx971PwqlVoV/UYYLhunpph/EClo9Ok5UB440Kdm9tIuh00L1FJsRSv96CTGVvXHN8fI2gLpG7/265etYB6DKlBNRN1HqBXR3ZRHOAOdDDkQLceJ3r57cAwDgDHGGALqBfGsMS4wBXcFbTPWUR/ZIY+csVTIKHOQToSOfR0Nevs5GdWmB6rI6nHz+Eua0TidldHk4G7WRnjPTMxCXqHcnfFgOuL7bw8hsXEUaxPA6T9XFHSNALAe4wCXou05mMhuPIy7u40sHCchdjjSr27dyCua0Tt/y99HHMceX6TVy9sZRa2Eg7MKanyal3wYcx8gAPwPwebJVtjEQAxsfdk9u2ea9qcCMBuw1kbSUQefkMzdOR+vgZT3rSLg3vDNTtRXEU4Ola9hr2gprrgyAQKysr/d4hf1vCO1nJDxNuWScijsP4xz/+m//14Yf/fdBsbv3IkMcxYfXGAt78zrPY994nUZmyXiwj0g8kvRqXw6Rpkqs18vUznPEYB3dO4+piC4srm8sDuAxImcNQH2uUV/EF2dbWlS4r4Yt6CgOAnptlSKhnksorfZHXTq7AzhgEfSsdVzBzlEBhWqEL6H8FeUudpimHgAtACAZG3j6nYQ2hQE9VPJI8snUMXL91DgyCAQ8fnsNTD+xFxZNmAGqeT77Saj7JlxqgN0d97fQVnLlwQ5n09di8BKTDmQG6o+GuqmACMh+XwwFCram/2vFx/OxVnL0wjz07prBtyzjGm7V1UydxzLG00sbichvLqx2jwAEQIKchHYcRei0fcSSd5Wy1DoDE8QI1LzA+7hzfutU7nifDB4zFF5nq86BvlzP15x1Dtj0FedjA1yZ66nynVDwAOR5fYurchod3KuTl02WT5BciFq+++rf/x4MPfqY3Pr79F4c4Tip0V9s4/S/PYs/jD2Ny3x6YR5KjH2JCPVwc9eASSvUn4IgjuW4zGDC3dRxj9Qou31ghS3KmH3KbMdQbTaUob0Xto8j3IvgOrqAY6P3qLaqzhNrPLa8f/jllSH75O0rM6gwSuLpTKSDAlBqXcGfqba+6XQwcQqpfIc3+KhrCdAzUcdQ+BCAcAQEHxp+ZQp/JY3EBzEw08JGn7kqp9xTQzZg6XcDGmvuuAa3I3+76eOHYT3BzpatmqcjgQIMbGTO9hrujzosRJa+n/qtmoxdEOHthAecuLqLiOZiaaGDLZAPTEw1MNGulX2HLOUcQRlht93BzuY2VVjcFcHl/VedNWOZ5IRB2AwSdIGVup+BOwzxZ2c9YcNTNmpx0X92yxT09DNwVZAvT0+1Je83TPFZaah48jVfXKwV3aq4Hkjnyelsr+Xa7vblkPDYO8psKqpsxCCHEsWNf+i9HjvzSzenp/b+KEc+HxzHe+uHL2HZzEXMPPQjmugCEVBfmYSbIuKMAU2oLACLLRD8xVsOh6gwuXl9G15djcLLBeUdnuZsbHRrjTQytw0fuEPQpWBLkMke/61qQMLD+UaGuWlSk8os6AgJqnJwqdgV2Ryj7tVLmumPApYrnjtLwDNIi4Aip7gEwISA4g3D0dWImj3xPPQMYh+AMzIHqKDBwMDgccFyGx+7dhZ9+aD88z0FqHJ2oagN3nQANdJJGoHru4jxePnkRccTl8cg4OxiT4NZmeiZBLrgaElDw1zDnPFH0XEgrACPpQgj4ocCNxRbmb7bgOUDFZahWPHieA891zXe1Ih/rYRgiCCL0wghxbCl1uZHsG7hbCp4L9Fo9REFk8iUgFKltDXj9u7HG4eMtW9wfjY87F8p6zhMTe6oAz6mAxKlTMvCnsO/XU8iY7TXUqeOdDfowDA3goyhKrRK6WcJmVvKbrWOwIflPnPj61w4deubG9u0P/D5jrDJEeRMYAxbPXYC/3MLe9zyOarNBgE7H45XHMBi4WsAkDINMfZWKi4O7tmB+qYMbSx3zsMg/hewmaVnf3bUEz/NQbzZQH2vC9bwRoV2y0CBw9i06COYFiaU6DCWgbqLWCeyZaK4gLk3PYEjArmAlixDVDgFXm+EVzCGgzPBS7WoJnyh0mWDM8mScH5Bj/GDAlukxfPR992DntkkCczK2TtaZT8bcc1Q+KdvpBXj+1Z/g6vyKmWbHkZjiZQdDSnKHMQimOjVcmfDBwDTI9bGE7rBoR1imTPnyvBmTeRwHcNUQRRhJhz3avornwvWczK0zf7M22FWcySMALuTwXRzF8Fd7GfN8Huwp4IVR9UbRB7OzlecbDTavIZzz0e0y3E6anlHqhWZ6RfmMZUDv55nkdR9BHz9vPF7DXUGfkzF5ANJUv7y8XKTih30ajSw58sJGQn4Utb0pQTxkfgxRRgDAuXPfeb7XW1ncu/c9/6PjuJODCmUDg+sy9JaWce5fv489Tz6KyR3byYMLoHN8XQeIZOdUvnihANDbpscwOVbD5flVdHqjOOXlqchy55MXXM9BrdFEvdlApUr6QyPb6df+tyXsp+soxysL9Nwsw0EdoJerjMrPO26yI5TCBtN/LArEAtJL23EgZ7czuGoMmhEmCVUHc5gytcOMyWtPegEGLtR4PYU+Z8rJDYDj4D0P7MVPP3LALGqjKe4YL3r5rz3mbrzsKdzVzunz1/Hi8bcQRRyOwxJTPBKzfPKteybCKH1tpgeTngUOhOysKLO9dHJjerBeWkJUu1xHDm2YtQSgOwYCruOgUvHAAPA4fc9MR0ik752GZbKt8gqBsBci6PhmNbo0aG1nunzACwG4Lmtv2+Y+V6mgpYFawGbeJ00rdTvdBr8AwImqpyDPvLmOdggGjcdr2DPGzNQ5aqZXpvriP7LyYV0BD2y8kh8WerrMpgPxCMcYKv/lyy+d6vWW/9Ndd334j123unOIspCKXT3Y4hiXXngJvXvvxo57DwNIzJPm4zL4oUDMBUTf915L8+CBnVtwc7WLa4stM/Z2a0NyDMd1UGs0UG82UK1Vc/Os/9H7Kev+JcslD6qsn/ofBuoyvr9azysr+ncmrOMJPe4uIFU8V+Z05oALDkcwMEfBSqt26DFvrSrJYdXYdTImTxQ/1CK6XMBxHDAB7N+5BT//3nuwbXosbWoHDKyTMXcN9kS5m3S1wQC0ugGefekMrtxYhuPIjgBXSl2a4uUZcAN03WAmOwNCwFGWjVhI0z0TiYc9ta55jkiOrYcTlMrX90633QFQrXhwXWYUefq20PsiMvEa1PJ3IT+9lo/IT0+PA2DBPXmzHB2Dp4D3PCzMzrpHHQc9C8x5gXY6aGcgBW0L+nTunTHX0zF2C+4pxzvZdK7LZOCuzfRqmwsheJ5XfRiGQgiBnKlztx3wwO0z178tQDzkMYZu0+Li2Wuvvbb8Px058kv/fbU6dl/phgmYB5EeR1w6cxbB0hJ2Pf4wag0JR23Cd1STomCQOk9+g1sm6hhvVnB1voXVjj/MaY0Uao06GmNNVGs1cyhhnnglK1nXP6GSlZUGOclTmLVfZ2MEqJvdAgEyBNjTZQQEdwCHy/FlyCEhwblS6DAgZ2QMX0BACEeOwevxdmj1qsfkZeBKJDsmB8NEs4p/9957cN+BHckStHRVupRqTxawob+fjKULwPFzV/HCsfOI4lgCnIvUuLoDOaauHekAeV7a6Q5cHkIr+cSbXvorGAc8yDQ9tMG0ygegnWZ124QQqHgeKp6rZsgk6fLa2vdZkC/6m0gUeRzF8Fs+eBynzPE63TbPZ2GfAL5Ww6WtW92XARFxrkb8FXPzCM9l5VStg3zb5noDZn26RK1T2NugF/JuwKyqR6Fvgz6OY1MnNdkTFW+mzlmmet2usmHY/NpqUSqwIfKOFMj75Is+zoD09ShzRx/D8+rVBx/81O81Glt+uux1r9Uq8DzXmB419J1qFbseewgTszPmQQcGtHshVpZb8LvDvwZzpd3D1YUWogFWgFGD4zrYtnP7OtW7Tr/3fgAsW7hvkdGADmBEqKv4octmz0VAwHHkVDL5kWCScfKOuY60NDkMJi9Lbcu8ug7mQH3LDgPTaQ5DxXPws48cxAceO4hKxSVmd9UgM8aeVvQkGea1sGofDFht9/CvR0/j0o0l0yYG/beEnDhmjq2d7nTbGWkzdH6ZJZPGdCOE1VEhba1VK+rNePadSN+f1PNdpOOEENBDKYEyz2de0yuQgizdz5rtpaIfG2NnpqedE0KIGGm4cyHH2inM+aAP51wvd6vLp/ZVHp0vpnGxJHUshIj1vq6Pc86jKIp02TiOeRiGOj0OgiCK45hHURT3er04DMM4DEPe6XQi3/dj3/f5yspK1G634wsXLkRBEMRIgF3mw4fMb8oMA/nN4Hg3quoepsxGHmPdy0RRL3zllb/+3+6//5dvTE7u+mSpioUyX+qHhH7iRRGuvPgyegf3Y8d9d4GpB1bFYQiVB+1QZwDpgT/WqOL6Yhs3C1+hOSxckwY0mo1scZM8IrQHFltLZyD7YB36OKUsAGWhXlTPALVudsuBPVUtl+PkifoWAJfz5yW/pO1am/ZlHmbKyXF6NQ8eAAQZh2eGgbhn3yw+/rNHMDPZzCh3ENjS8XU65k7hDlUvALx66jKe+/E5RLHynGdKvTswQwnaYx5Me8erfRBveTA4yuwgpPecNPXHcvEfppU1k+eadEDoqn/y6jgMcF0PtYr0RhDk9gl9yqnfRHJPzG/EBjxX5nnyt2/DXJ4P3eYZ2AMCnAsxNeUcGx9n5zXEZXUpwKcEvM43wONen4yuLzWuHsdxxgKg6otheebTPHpbK/goikwHREzzESMAACAASURBVMdRh7s8z3rGmOh2uzwIgmGmzg2t3EcsA2BzQF6HtUDytoN4ncvocub8hBDi9de/+qWDB58+v2PHA7/DmFPvVxnnwqgLgKgYBfyVty4iWFrGrsceQHVsDJ7jII7J9I+in1PB2TgOw9y2cWyZrOPqQmtExzwakgbUm41sg9bC4JHDqG3ok7EUzJP0vkA3UX2Uem5yXqckB+yZ07fLUagIaCIKBVGzvKz2pGdIecnrH7xcGEd2ArhaOcZRa+XKNXEEZibH8EsfeAD37d+emOaVVSoNemISo38LKsGG+0qrh289dwIXry8ZKwKHMI50emEbhzEDSfk3JYe9OJeAdhzZgXEcYZauZdCe9WqfJ971AIw3PZ02J9sLeJ6LWsWT4/tcXTs15m/AXXhLE3jT7ziU3vO2WZ7CPYG4DfW02Z4x0ZuZcV6q17GgTe9IA75foJ70lOwp1a/VOwB7Gh11pNNj+Jxb89/J+LrgnAsl4oV1jIw3fZ6pXm3zAV71dhgW1FTBjxxuOeSFEILMLb1VkFwvqG6WMrocI9sAgDff/O4Lq6tXLh08+PR/9LxaoUNeHHMDdL0spnktpnoQBq0WLv7gR9hx/90Y27lTPWTpYUucjXVmtaqH/Tunsdr2cW2xjTCKS55ufqhUq3BdBym6jW75T4c1dxRKVlAa5EmerDGuCOj96i0BdbNbTun3A7udX69GJ1OUJz2T4+1COBBMrl0rlAed/u3Jl7cw5cwGBTwJv0rVxUeeuhtPP3EInucY83raeS4dlziwWfA3HQMZ/9KJC/jej85I9e7IRWmglqQVqhF6MRuorgsXMNYC6XSnOi5k3jtjyr9QMDhMJOfDZB7GGAG7jIOQbXNdZZp31VHpIjMM2R+KfU/M9Rfq9sjvsBsg6Aa5YJf1Z+GeNt3DjL+7rljcutV5yXXRpUAHgTbZNo50VrqBs90xoOPwsl0pwNMORd7UOQgh1T7xlqedCm6Pw9Nta+qciOOYK7O+CMNQ+L4vOp1Ovz/sUUC9ZrDTcLu864GNAf6tPM6ox8q7eUNha37+1KVW69r/fOTIJ363Xp96Ivcg2sRnxgrTSl5vCyFw7fgpTN9cQmN6Gzq9oKCJJZpMik00axhv1LCw3MHCUkcuEDL0mQKNZh2ZW7NuP39gzZUNUtYDCuaPrI0CdJVWpr5CqGfryMwsyAN7prrk3IRgYA6XkGcaloB85SxTTmN6OhpTK8Wpl8ioRXO48px/5N6d+OTTD2B6okFM82nPePqdrGqXmOf1N4X7zdUOvvG913Hx2k0z5m8857UlQc19E8ajnqVM846S4RzSmc5Ml9Mr2Smgc6Hao6wIkvkiHacsCNWqi4rnQS8NrGceFN3WPLjTeyog5OI2qz3EYZxR9hTkyX46LjHby/vSaLCfTE87r0PNRIMCJ5A4w6lGmHFy1boMzCmkQToANMAax7Yc7lLe8hrupIxpl1bxRNWnHO+Ux7yxIKjxe2FPnVtZWeFCZO7KsGAfpUzpcMsd7wBsFue7O7UciuphjDn33feLvzQ9vf9TJK8J4+MNNBq1XLinHoTqO4QDMbcbrDrSGjwkpJsSRhzXF1tYaQ/n1Mcchm07tiX+BEN2EEqHvn8Ca/n7GBLkmaRBAqEoSx6YizJnOwalwD4QMEknUzvVudqypADJmHakI46hCtguc4yz3a5tk/jMzz2Me/bNmt9r8vsl22Ap5zVjQFQwd8zvSA0fADj62k/wnaOnEHEOB0m7jCOgqkhvO0xDP91eRo9h4rOOeLoJxvGOnIfusFQrHmpVL+mMgJyLaTmSvwdy6Rns31sCbB7G6LV64MRBVkNcbydxWeDrsXcVH09M4Nj4OLukoa5VNWU2TaPbtmrPi0eBg53aTqWp7Vh1ImLlYGec9uh2rIKODMMwUruRUuqx7/txFEVxFEU8DMO42+3GQRDEvu/HrVYr7na7cbvdji9duhT6vj+yA90oZUjHqVTYLGPyG6mIRznWeh5v2HI5f8oqQgh+4sTXv75373vO79r1+O85jpt6P2UQhBgbqxfC3TbjV4XAhRdfwsTB/Whs3w6mpcqwgaWbWvEYds9OYMtkHdcX2+Ttdv0vQ3O8qR5u/YB2O0K6IcV/cgMaXArmJL1sZ2GQUs9Jzprh7UzlwZ6qVb1Nzix4o5zRBGdw1VK3Qo21u9Dj91IRj1U9/OLP3o9nfuoueI60k2fWnAcMkKHjmfplMfXGNwvujAELyx381395Rap3SChzNXFdb+vFbvRPkCs17wjtiCfnw9N16GWrkrF3ueKdgIgZmCNVPkPyillpmpfTCiueg3qtAtdxJFxFsiCQNudLe35yzVN/QalbRLY4R9D2EfrUuS4BuNwXuWk5cIfjoLNlC35UrWJZw1wehqeUPFHp1ISfGv9WDc0FP82TJ+pVG03nQgddTqt6XUCvVkfqTHn5UxVPzfW+73MyFs8V+Hm32+U5c+OLwm1T+BsCeSGGHpdfLxCPCuG1lF3LMcuWT9V14cILr66uXv2zu+76yO9XKo19OiEIpCOd4yRwzzrjJcrIdR00GjUsn/0JWtfmMXn3IeX0NmSTCs6uWavgwM5ptLoBbiy10fOL13lujjfRHGuknI9uZRiub1wyc46iKlVgmE7DwI5CCaib9CykRwF7OotQY/PKYY0zgMm3wgm1tKsQSM2XBwPe+9B+fPrDD2NqrEbgTJSwNm8TKw81z2vIA0h1DDgX+P7L5/AvR08ijuS8fUd7wUO9px6Afv2tnManKS5N87oToZeyBUNipleqnM6jhwAYk2+jkw6Hqj0K9BXPQa3qwXOlbZ9ztVYe09dSnbceiJeIgl5HQF7onPsAIPIjOfc95/Wn/cCe7NOxeKBSETe2bMHLjoOAOqkjUZiCcy4YYzbgUypfdQgGOeTR+lOdAKrcNdRzOgFmIRt9PCQdCg3xlNk+DEOu4a4z6jrUWDx9rWy/ZWxvG9TtsCHmegD9TPZ3ivl8rWXXo3zfOiuVZu3++z/xH5rNbWY+/ZYtE0rNIxfuybZM63Z9XLo0DwDgzEFj7x5M7dohnd+ydzUnrnxY7Qa4cbMNP0jDvjnexNhEc01139LQD3zDVNC32ChAV2m5RUtCPRO9tnICSOa1O+r1xgqsDEzO9dbmbYfh3n2z+OzPPYrDe7cqmBJzdmoMXv+WSTyQAr+t+s9euIGvfedV3FhsKUtWkkcfnyn1z5y0ad5J/b0o6wGYmQOvoW7ywfobQzodYKh4Luo1D57rJm3XQNeBZf/KUsmMZQAvAIiYI2j3EAXJ2DuAFNTT+zbs03AHBBoNnJ2awhsiPRUu5cRG4+g3VflCpDzk7bnzKZCTMsY8b8E+ttPtufBkO2WiJ6Z7zq158WEYxlEU8W63GwVBwLWpfnV1Ne50OnGr1YovX74c0fMf4TNSWTEktDcD5DcTiG9nB2DdOgGHDj3zvu3b7/81xpxGs1nDtm1T+h7kAj5Jk99vvnnFWAEAgNUbmLr7MOoTqdGA/MAKdwrDasfH/M02/DBGc6xhAX5tHYnhw3r8Pdhqd4RjlQV6QZZ8qOdlHqDWzW75ciL9DwA9Nk+ByRT05f6B3VvwmQ8/gkfu3mVATaGagTuS8W8A6TQFRv3IWW518fffeQXHTl62xtzJ2LlDTf9Jp8MBAb3q55pFcAzsYcr3a7duV8XzUK9VUrMDANohUWdIAc+gjRwm2NYt/SyPugH8dpALdzt/GvbpfWKe701OildqNXEDRLGDgF1vk+8MzEn+1EI4Oj7HXG8D3x6HjxOxnSh7e5snY/EG7BTwcRzHQRDEcRzHURRpyPNerxeFYWgAryAftdvt+MaNG9Hq6mpZSK9bR2BYwAMbOCYvhDHZl3lqF53InVh2PesoVf+5c995fmHh7Nm77vrwbzsOuxuAUuLFcE9M+gxbtkzg2rWbSaN6XSwdew2NuTlM7N8Dx3VLnkGf0yFXY6JZxUSzhlAA3ZhZ0+42phM6OBRBc8hyhcmDKhwC6CZfXp2jQr1/2Tyw0zL6lbLyFbIwcTt3TOIzH3kETz24z6hj6qTGUvBjOaZ4G+4ajgwx5/jXH76Bbz/3BqIolkvqqoFzfc2k07pa2tmYv/UiNgJctYkrUzkDzCtlIZK15x2u/Akca6lappeNFnAcF41axSxFq9+UJ9ScO2mS1+ciEqgzZubjy1aoK2v9rYmIo9eSnvOplBTs7bh8sOv9SkVcnZ7Gq44jAiEvWh7UU4qeKHBB0pMNXZgnb36jQIel6q1OgtBlhcgsXat/gNQrXgM9ZZbXHwV1Mz9eF1Mr33E9ZS6OY64WvRG+74tWq1XwB5L7KRPWUrZv2DAlD2CQmr/TzejrZYpfN5O+47juPff8wsfvvfeRT4yPNxwb7np5TvPwVA9MzjlOnryQ+/IZx3MxefddqG+ZQrl+S7lQb9bldDkhsNoJsNTqwQ9LzrEv24x1/6mXqLBsp8fOU5C1GOhFhcpCffjyg8BuxzlkSdqdWyfx6Z97GB94/HDKAz8Fd4ZUp1QDT0Nf/5YdVQ6k3Otnr+Ar334Z8zdXiTon6psqd73tJJ0M+QfEErO8hrmTHjLQjn0m3bKSMUCZ5auoVBxTVrU4dc7JOciLJ03xKi8pk0QIre8QdgL4Hb+UE2jWVG93BAQAEY+NidfHxsQFJCrSXrSGTocz35aiTznkFaj72NqnYKcKP6Zp/ZR8nrleL3GrzfVaxUdRxKmKp0vYUjN9t9uNO51OfOPGjbDVao2qzkdW9ep6DB02i3c9UPwELPMI7/f0XGv59aqjbF3rhiLOY/7GG1//B8dZOv3EEz//W4A3Q9fYtuGut13Xw9atU7hxYylbZxRj6cRJVCcnMHH4ICqNxprbWW/W0GjWoKXLxFgVE2NVdHohllq9AavnsSGv2DqSfhA8yxTuU2QkoJdtVy6ci+oQ6a0isBeVU/kFB7ZMNfHpn3sYP//ee+RYtIF1gXk7F+4k3oL7/FILX/qnl3Ds1CXjZGpWfFP5ueAS5kytZqfkuJ6LzrmyJnD9fnhZg5w/LyEsV7QTBraO+h3Sl9DUqhVpllevupXKXb2ARjVdyC/lUyf0nwAYiGe9Smf6HBjUSj0CsR/Bb/cyr5dN3YW+Sj7Z10re88Ty1JR42fNES6l3o8qRwNoAy1byNJCyXKtutc0ZY4WAV3VRoFMHO503peRJm+i8eC6EEMREr3sF5vWx+jsIAq3qOVXxvV6Pa6/6IAh4wStlU5ezz6dMWEvZTNhQyAuR8rIHNgfAy9SzXgAfpq6iOkWftNxjHT/+7MkHHtj9v4+P3/uLQPPRIrhTpT83twWLiyuIC146E6ysYuHlV9HYsR3j+/bArYw+t75S8ZCVIAzNWgXNWgV+GGFp1UerS5SKOfMRf/sDi621MyByN/NzFmQYCegFkYVQV/EFHYN0sRywF3UorONNjNXwqQ8/jI//7P2oVTylepGoYCTrvtPxbPV/AneD9jTcgzDG//f91/HNf3sdcSznvHMkC9cwRwBC+wIwQAjTRL0GvX6jHISaAqdgztVb4VKvlCVz65iaUscYgyuAqoK760rVb9bjV2+dY2o+nOS6nHCnTsqod2GAr5f81W+jU5c9jNFrdREFw68omSh4/Z3csHqdn52YEKcAEXMOwWRjUyCnwAcSpQ4yPQ7IV/JIK3OhwQ2dgUxvU9spVY9kQZuMkqcdAQvoXHcIYA0FUAWv66JL2Xa7XU7fGb+8vKwXv7lVIF/rgycTbreSH3RC6wHfza7Ch62T9UkrDGfPnlp84omJfxRi5hyw9WMAaxQ54DmOnE43NzdjPO2LQvfadfg3bmBs/16Mze0A2PBz68MghOfZ4/zJqdUqLnbMNLE1rmO51cNy29+g99jnBdF3N7/EQMoPqGwUoA+orwjOequwU1Ae7ADQrHv45NMP4ZPPPIhmvWpgrelte63byt0hv0vzTeAOAC8dfwt/880XsbDcNgvaCCYBqtedB1dz26GUOmNmLjwEyLvhlUmfyzZwzlUbWAr+8uUyzKxk5znSU75erZgXQ8k6FaKFqgJ6dTsNcXJuamxekPM3a/zrTlck57wH3SB1XfqFPCVvOnFqnzHRnZiIX67XsUghrsEIZNS8jucknlv5M+Z7+lH1p7ZBVsajHQGi4im4TcfBOqZtaTBxYRhyrsbireVqtarnnHNjptfT7IiK58j/w8hc9gGfMsG+3iOFDYc8UfN3mhl9mLpuZZ0jhRMnTqw++eSTM57XOilEcCWOd3wUqB4245o58+fn5mZw/foSwrB4PjsgH2arb76F7sXLmLr7MKpbppNlbK3Aci6J70doNPLz02pcxjAz0cCWiTpWOj5W2j6CsuP2uZWPXjSpomQlA8FL0gqzFCQUAplkKAX1onqGAzsgUPVcfPz99+NTH34Ek2YKJwCQ8XfbPI/0PgBipk86BTpcnV/B//O15/HamctmLJ0zudCO40izuFywRju/0TXohTG/6+n5NB8X2iSv2qk/XFkdlL6uVjw06hVlnVBYFtyY5HUnwCh3aDO/MNYIqejlP/JKMjAhYc8YM+vVBx0fgfKa15dhmGd//rQ5Jmo1fmFiIj7hOAj0O12UKT0P6mbfUva5YAfpKNjQ1mmMMQ37GEhPrdMfYraPSRtSC99o9lNhr99Qp631dht1p0EIwck4vFm6Vqv4VqvFFxYWyqj49VLz68aF26nk10OFD3MhNoMKX0voZ7bvlw8A0O12+YULF9qHDx+eYixuVypXvsr5liNxPPUhxpwmkGe6d7Bv33acPXu5VAOjMMLC8ZOoj49h+t7DYPUG4swc3pxm8xhBGKGSUfP5p8fAMNWsY6pZhx9GWGn7WO0EQz3w+rZnuAoGRRSn9826BqAPaFea5wVQzyTZ7c6CHULO4vj5996Hz/67RzEzOWaNoefAPWcMHiDQJ79JHfwgwlf/24/xze+/Bq5fxORAvtRGK1S16I4ec9dj61xZqpiQw9oOU3BnjIzBw4zNMzVur1oPRwgwx1Hj7XqOOyNT0PRLaBTYBR1qSH5vRqera5lS9Cx5TZQQAlEvhG8tR6vDICVf9Dch5NS4zvh49Eq9LuYl8xKw60DN9eqm2+qcdgRSY+ukHmOmp2lauesAnais7qQ/kFH/OaZ6vaRtZnqe7gRQ1a5M9Fw73SmFbzoJajxeaGc73/c5Wb52UCjTCShdjxjlwUbChnrXpw7M2HouDHNLF5nZgM+GHGvbtm3VX/3VXz3ouq6TzHTwmmG49WnO6/fnva0OYDhx4jyWllqDbmkmTGydxuShA4irdfABjqGNehWNRhWD+03poH+9XAi0ugFW2n5mcZ3SlQyf2D//wKJ9MqxZ+Q8DdRU/lFpPl3EchmeeuAuf+9jj2DEzkQZ3EdzN3PIcuBMvdhr+7cdn8VdfewGLy+3E+12b2Jkec5f1UG96s7CNqjsdx8w6+Zm2GS98KNVeRa3qpRbKAUA6JOQc1d+P/knrc9WIl5uJT4IOuo7IDxG0eoiItYpOFxw2EFO9qNX4uYkJ/gZj0mNd1VtaxYMAH0QdY4CpPifNqHQkKj7W6Tn7uYviCOnAZ/zrDPnJXHjOOY+iKNYL3qgV7Mz69GEYxsokH/m+z3u9XtzpdOJ2ux3Pz89HI65RX/QpXdedDPnbDcGNgvhGdhYGfj7xiU/M3XfffVt0J8txHIcxxqJobH8YTn4IcCfNg4jJh7fvR3jppVOFTniDwuTsFowfPABRqSKmXsDqQVWreWg0a2A0cg0hCGOsdHysdtZj7D6n/FBVDshcCuYkfQDQzd7AevtAPVtJYbl61cNH3nMPPvHMg5jbOpmd465+edqbnSnSaeClfEFyxtt1eOvqIv7zV/4Nr5+5LPOpxXWkmZ7A3AEgLNA7EuSpl8uo6XzpBW5ko/SiPWAMnsvQqFXRqFeNI12izFVnQCTnov500udA06D+EK1OjQ6MAXEQwV/pIgpjU0dasbNU/n7Bfry7Ll8eH49+XK2KZQCcMQYKdBCwFKl4kicFbRqfk2aPvacUOokrBXgKeQJ3k0dHCGWq19Pn7Olyvu/HFPBBEMS9Xi/u9Xpcq/jl5eV4cXExtq+R9VnPDgDtLK0Z0LcN8oAB/bsA38DP9PR05fd+7/fudl3XZSoAcBhjTAinGgRT74nj+iNJknxYXbmyiNOnL5a5rbmBMWBy+1ZMHdwPXq0hijiEEGg2a6jWbsGokQCEEOj0Qqx2A3R64Ujm/MLKR8paplw/mGcTyql0lTYq1DNJAjNTTfzi+x/AR3/6PkyoMfc8uNN55nlw1+q7CO5X51fw1/94FN978XQCS0ZUu4ozc9xZ+o12QHpfO9ElnQB9XKaWqJVDDvWqh3qtmnobnDy2frGMdMCD7jwgidfnksTpfZjzN9dCnygDeBDDX+0i9KO0X4LOQvZHUPJxvR6fHB+PzkABRAOewjxHxadUPtLwMWqcllFwLQQ+VfBWHdrknqfoBwJe15m3oh391m+X0yvbKcDHGvq9Xs+o+MXFxXhhYSHi5GU6a/gM3RFYD8jfbu96YEhdpMKgn/h6PM3L/Bndvh7SiGFpaSk8ceLE4qOPPjoLmI4WtKCvVls/iKLwrO9PPCOEs1U/pPbs2YabN1cxP7880nGFAJavLWDl+iKm5rZh610H4JllcvtcxjVcYcaAsUYFY40KuAJ+q+Oj44d9Fg0pGfoBc5gK+hZbA9Bzs9hQL6qnuENwYNcM/rsPPoSnH79LLsuaA/esmT4NdzreXgT3+aU2/vYff4hv/eA4OJcvtGF6vNvRc9/lG+yYIN7xYID1znc97s2ly72cFsdBvOkFHOagXpFT36g5Xk9/kx0GeQ2E0HPnGRi4fBMd9FQ3mUGfk9DbkB75urzsAMhzj4MIwWoXIV1KmnQcoK6dft5LOOfctoLgunx+YiJ62fN4m0t/Pg14Mw5OAU/AYsa0QaBPtnOhT/OQcfIM+C1lr/sGJp2TKXQ6DUjNnY9JGwYCXvcWjJlAjb8HQRAHQcDVuLvo9Xq80+nwdrvNV1dXVfNSfwijfgaFdQc8gNur5AEDmY1Wx3ecAl/Pz8TEhPeHf/iH99VqtYqt5tUtYQBzfX/80TCsPwYwjzGGOOZ48cWTaLW6ZW5t38AYw/TOWcwc2kdgn5tzzccyQf3UuRBodwO0ugG6vbAIpWs/UKnqshmy/B0F6HntKKonX63rwBjw2L178MsfegiP3LM75a+RmK0J5NcA95XVLv7mmz/E179zDGEszdVyzNxasS6l4tNxgB2X7nzQcfxGvYJGvYp6xTMvqDFthqXe9fkxeq7qPDWMFcwzyp3mV9s8jOCv9BD6yWJPFOr9lfzgvwvGhN9oRMebzei8uqFGtZMbXKQeaUcgFQ9r2pwFbwN+rcp1HgL1mKZbip2qfA34lILXZnldl4J6ivEAhhqHzzPTr6ysxAsLC4PM9Oui2PvchzWHzQj5dwLEb1X7UDbvxz72se3vf//7dyvGm/F5dUsYFPQ5d8e63bEn47hyGADz/RAvvHACvt9vFbrygTGGidlpzBzYh9rMdHIWZcMaf74xF2j3AnS6Ibp+WDj1L/dApY/dB+SpnUEVlgS62R0G6tmIiuvimZ86jE9+8CHsm9uSgbs9hm7DPeXIif5wb3V8fOmfXsRXvv0yfD/MQFzDmoIeLD0ub9KM055IOeMBzJjiG/UqajUPrtX5SDotSn0XwZ1cC6249TmZzgEU4BnUuL3MEPsh/NUE7ua4LLleOlDo23F9Aq/V4rNjY8EbjIlQg90yzwNkTJ6a6S1Fn2vCRxpEKaWOguVtqYIn6cZsT8EuLLN8GcDrPINM9NpMrzsAQRDErVYrooBXU+bWy0w/dCdgvQAPbALIA0ODfjMCfDO1CWXyOY7D/uiP/ujeubm5ibSCTyl6A/0oqm7vdhtPce7Orqy08cILJ0Z2xCsKzalxzBzYg7Eds9o+uq5h0E9dQM7Z7/hyDH/wHPziCgtEcd8ymTyFWfOA3q+AGKpjMDlWx8d+9n58/P33Y3qikQJa7hx3AncNZqAc3HtBiK/800v4f7/xQ7S7PbW2PXGYMx0GAnrtaIdE4QNI1sYnKp5BoFqtoFGTpvhq1TPwTRzxCJiZhLapU+eBzqvOhWmtbo3FCzVWT60HACAEok6A3moPcRQBqc5CclFSnYUc6NN8ecHz4stjY8Exz+MtKLDrbw1zDW2t1HUeEMBQsNvw1p0GotRt8Gec7qiqt6FOoJ8ag7e3RwG8UI53RePwykwf2970N2/ejDudDl0e91bA/p0DecCAfjPBcjO1ZdgPyuTbu3dv/fd///cfqFQqLgjc9bZW9ur2MMYY6/Vqh3q96mPz862xH/7wjXUHPQDUGjXM7N+Did3bwQYslzv8z7d8gSjm6PohOj2p8mPbU3+AEi7dlr7FRgB6YXKx2hcAdm+fwiefeQgfeupus7gLA9LT2QisM2PwOXCn0KIhjGL8/b+8gv/7az/A4lLblDeveFUKOu1IlwCYetRrT3mt4l3XQaNaQb1WRb1WgeOquk17SZtNp0K2S6+Alww/EEWfArKwLBVEuZNrAM4RtHroteT68vRaJZYA2qb0xSqj5KWlgi83Gv4rtVp8XTfQUu4G+CDqXMXpP+KU2iQdgUIFX7RtdwBy1Lyt5Cn8qQNeX8CDmOUp4BXc+zraFQF+dXWVr6ysDGumX7eOwHoCHth8kH8X3hsM+k984hNzH/7wh/frTpYjn1yp8Xm9nUCfVdrt2v1Xr3aPPP/8CS9cy6pzfYJX8TC9dyem9uyEW69lM+TAo3QoErc0gxX8IEI3iOD7EXpBDvT71TEMyFNRJY5RBugmSmRSH7p7Fz75zIN46oF9KZgpcZpR4ok5O4ETQOBeMMcdAOKY45vffw3/11efw7WFlVRbdd0p0LPEKz6r8OUxPM9Bo1ZFreqiXq2gWvGAlBe8NN2bBx7fSgAAIABJREFU8vRcLMVNwU/hnpy/UunmfBPlTufMi0h6yvttnyyIk4DdVuzJtRpOyTMm/FoteL3R8M8xxrhKo1AH2TbpFP4qDyffNI4qeyDnVbP2toa2jieK3qh1lY+OzRfCXkE+pulFCl4r9zzAc87FoHH4VqvFFxcXo1jO9V3vz4aa6XXYNJAHsFbQv1PgXeaDsnkZY+xP/uRP7t+9e/cUFNwtwGfG6XXZOHbGLl8OHvjud08eXq8x+rzgOAwTszOY2jOH+sw0yqyPv+aV7PrUrEMYcfT8ED0/Qi+IEEb9Ojv9QF6Qnpe5MFt5qAPAjplxPPPk3fjQk/dg5+wk1M2FMTMDGbibdCTQgsrXb447AAgB/PPzJ/Cf/+77uHD1ZrpFpJ1Gmau6tFqmir5WqaBec1GvVVCvVlBxHQhtBVCNS8ztGqLJeaXmy1OYgykvfgJeFZ90GlSaan7KfC8Eom4Iv9VF2IuAlAUAsAFOgY/MNdUXJ71PwM0rleB0o9E77rpIjbvrbwvmppyqJxcySrnrKXbUVE9BrtV9RslTs3w/U72l6I1aB/Kd7qiSHwbwQghR1tGu1WrxpaWlOAzDDTfPk2v89oY8gDzQvwvvWwB3+tm9e3f985///MP1er2qbkFmXF5tM4AqejiMMVy/3hr7h3949a4gqNzb796uR6g1apjaM4eJnbNw6vW1VzgUaPuHmHP0ggg9X47nB0GM2Ey/KVv3CDBPRRd1bwSatQp+5tFD+OBT9+CBQ3PELA4Uwb1onXlg8Bx3Hb7/0hn8n195FmfeupFuP4E7jaRj7Z7noFbxpLNcrYJarZJa4EYD1mGQoEcCy7RzXgLWIuBDpJ0FJYBF2pQuiElfX4swgt/qyaVnuQCQDCfAumY28NNQp8eGuZ7WvnDd6Hyj0Xvd8+JVCnC9TYHPcpzuiCq3AZ9S+BTwSINbHzO1BjyIetdlyLh7CvIa2DquCPbEXF8K8GS52pgxVuhJH6tXyFIz/dLSUryG98RvSsADuCMg/+6n/wfrUfbJJ5+c+p3f+Z2HXdelAFfPxrSiJ/fI5AnDkP/d3/1z+/Jl52dqtcl7iu7vegXHYRjfNo2p3TtR31pO3a8V4ulqytUVc44gjOGHEcIwlvCPomRhnr7V9AN6vkJPlVNAevTePfjgk3fjvQ/tV4u7aAUL5ME9vYANUnBPzNoslZZpgQCee/kM/svf/wDH6bsP+oC9UnFR8TxUKw6qVQ/1SgWuHnvX09voMVnaulCs2hOHQNn2BLb0fMz1UHmS+nWHgpQXQNjx4a90lZd81vxvx8G6jtDHIdcwD/oqXnheeKFe777mutGygjUACNnnTo2562OkoE3SU0AXQsDaT+WBZWbWAVnw09XtNOxNuga1ZcJPKXcKdAp4naagbsbiyfi7WclOCMFtT/ogCGLOOS8ah19ZWeGrq6sbvardLQc8gM0HeQDvgn5IQK9XuV/+5V/e+clPfvJequItqFPYW/Pq5ffzzz9/4Stf+a4zO/vQL9RqE4cKb/I6hmq9hqld2zGxcxZuozFCDev9N0DUdG7VAmHEEYTSxB/FMcKII4pixDHPKdsP6CrFSty/awYffPIuPP3E3ZiZbJIx9Hy45wF0FLj7QYRvPvsa/vabP8SFq4uk+dJ07bmO/HgOPMeB57moVjx4LqlMAGa+uQI0BJJ57BSmVvvNPHl6Tqq9jjovJ3U+Mg1qeVo4eiEbej0Skz4PIgSrWrXzxKrhWAsDDYA99LEzCj+pQ8c7TnixVuu8WqlENy2w56p39Z0ZkxfKk94GOiOmew18ta2n2OWZ77kQwqQJa3w+T93bZntdxoK8DX1OgrABT53sigCv4F7oaBcEgXi7TJfLC5sS8gDeBf0IkF6P8n/wB39w11NPPbXPAnhqHj2JT33rDsC1a9dW//Iv//L4/Ly3Z+fORz5erY7tL7jN6xoYY2hOjWNibhvGZrfAqa2DOR9ALl5F39T+ZQuT5U4YS+Dr70h9x5wj5hw8zq9zeqKO9z9+Fz701N04uHurWcKVKtYU3ImDnA33Yea4A8Dichtf/fZL+Np3XkW76yuAK6C7LjzXgeuy3PNN7wqzI6GbgJ4R1S6BngW56RQwapFIj40zyDQ5jq/PMw/uMq+IOMK2BDtdcjZVZ0nYq1swEPgA4LrR5Wq19YrnhfME6hmwy2Oam0IBb0BCoW8D3oK3ba4HrLF32jEgkNchteIdAXls7Re9nCYX8sqDHtpUnwd4PQZfBvBhGPINcLS77SoewOaFPIB3QV8SzOtYBxzHcb7whS88cujQoVl1CxzrPjjqQcNy0vU2hBB47rnnzv/FX/zFuS1b7j8yN/fQL1QqzT35d3r9g+MwNKcmML5jqwR+Ncc7f8gg+uyVKyiyUf0KFGYSEvZcwHUcPHj3Trz3oYN48PBOuK6GTALorGqXEANL8tgQBEvDDuY7Hd66sohvfO8YfvDjs4iixDE7daaZ87B7SCKdRopoQIPRKXAJ2PW8egN6BfaMaifnD90RUB0AQI/Fs2ScP4rht3yErS4CZY7XQwb62ua9tTGtzPPM9DqPzgeSD+o44dVarfNjz/Ovqb8nkdTHNJh1vAE+VfGmNpVf7dpz4s22reqpirfVPf3Oi9MBxBmP5M8Fu9q2x9/Nt46PokhQ8zxV7mUAH0XR297Rzg6bGvIAbNDfSic83MK6b1ebypTP5Gk2m+6f//mfP7Zv375tWsmzRHaYaXbq9jD6DaQV/8rKSvev/uqvXv/mN785v2fPUw9u337kQ7XaxF3YwOA4DGNbJjG+fSvGZqfBKtXcfEP9JfQDV6m6ypoCsolT43Xcd2AORw7twJGDc2jUKwToSAAOGzbI5OuflkDLDq+euohvfPcYjp26ZNq4XmBPbQkNQgVW0vmQ4+WCmN/zVHsCfBNvzhHp6X6hUuxmNToBWHBPfAPS++b6lYS9TEvlE44TXKhU2q95nn9d1i3H2pVSF6S+FPT1pSL7qW0KaXW8zFQ69U0Xx7EXvkl1BtBnLB5pE32mE9AP7Fq5533TNeht0/wgwGtHOwr45eXlOAiCtzXgAdxxkL9T4Ho7jz1s+dz89Xrd/cIXvvDo4cOHd6jbkII63VYhs3AO2XcuXry4+OUvf/n0t771rfnZ2fv279z56AfHxrY+inIec+sWHIdhfMsUxrbPoDEzBadmK/z8v4fh/0rKQnxABgG4joMDu2dw5NAc7j80h13bp4iZeXi4UzN+Xh1pZZmEMIrx3Mtn8Y3vvoqL125aLV9fsNuVadWu20+hD1CnO30+aXWPFOxVHoHEFL/aQ9ANiLJm1ra9n2wD5WCfpIFcfxG5rn+6Umm97jjBCgW7rDcF+lQacsCujmGArPf1NjHRG6BTsNM8alNvUwe6jLmebmuTvbUPaorXNFfbxukOcrydW9+xEELYjnZCCB4EASfALw143/dHWfDmjgM8gM0PeQC3A/TY4OOt9ZjDlu2X36RVq1Xni1/84mP33nvvLnUbHHo/WHYOvSnLChzzLl68ePPLX/7yqW984xs3Jid3zezd+55nJiZ2vpcxZ+329CEDYwz18TqaW6fRnJlGdXIMcNw+JUTf3dLl+ibLnemJBo4cmsN9B+dw74HtqFcrfVV3Am8rjVFzvIwsA34aVts9/LcfHMe3nzuO5dVuH7DnXZ9yYAfy4U6XNGS6nQw5pvtkOqD6tZFzT+IcAYRtH2Hbh9/uIQ4jC+T5cNfmfMZsM32SV8elp+Gl8yX7vOs4neOVyupxxuKAMSYGAD5loiftMyDX34ylzfUE7AYyNvQp2FU0tQTYapd6xgPW1Lmib63adein3nU6BTr9VurdKPk4jkWeF/07HfAA7gzIAwlYNuiDO+A4w5Ytkz+Tx/M854tf/OIjDz74oHbGS43RU4CztJoHSc9Mv1tYWFh97rnn3vrSl750YX5+1dm//6d/Znp6/wdctzKF2xQ8z0VjelJBf6LEPPwh/nb6wRDyHeaHdm/DkUM7cN/BOezcNpmBNKCApaBum9QzcCd5kAP3PPDTcOXGMr75vWN49ken4YdycZe+at3s9gN7Oj0X7ElCJn/ueDvSZnraCQADRBAhaPkIVnsIuj6EECmzOwUzdZzLN9Nn04pgnwU7A2PREmOtVz1v+TRjiLVCp+Cm5nkCfAPxPkpe59Fp9rS6FOQtsMPapzDXybaXvQ3xjKLX31TBI+1UJ4Ds+DuUkrfgTs3yRr1zspJdFEWi3xj8Ow3wwB0EeWDDQI9NXP8wZcvmLcpn4hlj7I//+I/v/8AHPkCn1+nvQjO9Ti8APhhjDudcnDp16uq3vvWtN7/+9X+c37nzpx7ftu3uZyqV5m7c5lAfa6A5M4XG9ARqE02wWs5Y/iDoFQTXcbBj6zgO7N6K+w7O4e5921GregDo290K4N4H/NmV5yy4F6SZ1guB105fwj99/3W8/MZ5pJY+yTtHkRM3AOwyrTzY7Y4DY2kzva3aEcUIOz7CdgC/1UUUxhmIp+GeLF5DwW/DO70PMJbUlbq+ubAPLwPLrzjOynnXdYT6UxEa6rptRLXr8hmFT+FtTaVLQV8fX5vjWdqTPqPytUnd7gBQk71W7nlmfMs8bzoCdAxef2uoWyreTJHT5nnyzXPG4bk2z+uFbjjn3Pf9uNvtincBL8MdBXkAtxL0uEX13klg75eGT33qUzt/8zd/88larVbTwNYAV5/Msri0jrw0mqfb7QanT5++evTo0SvPPXeiWqnseXJ8fPtjt8OUbwfGGGqNGhpT46hNjaM+OQa3Xh/wtjz5t1WteNi1bQq7tk9h9/Zp7N4xjZ3bpownvAGDhjgoODSU+6Q59pi63NEA7wd+HS5dX8KzL57C9186jcWldu55pHft54YN9myZfLDTvMVgt6s1cBUCwg8RdnwEHR9hJ0BETPBpxY4+8Qmwqbk9MdMnqtqGPVCk7EUPaL8BLJ1wnN6iVu3KcVVoqANSqROYm3RmKXqWVfIZr3r6rbdJvL2srb6qGvSppWtpnO4sIFnsxoDc6gjkjsfrfWKSN8C31bsCurDgnnKw0wpem+ftleyiKOKtVou/kwEP4M6DPID1Bj3Wsa71qnuYcmXz9stXOu3IkSPjf/Znf/a+7du3z4BAmih2ey49mDUNz97OA78QQly6dGnx2LHj86dP39x5/Xr8wOpqdNvVPQ3VWgW18aZU+pNjqI41MD7RxK7ZKezaPo1ds1PYvWMas1vGU2DW0GBUkTMk2wBJ75+mr2QCcA2Z/uAH5Fj7D358Ft978RTOXbhBziwP6na8MF/rBvZMWjaeRxxhV05t81sBwq5vFqVJw1ur9jSgi830GtJFqh4kTd+HLOwl6PxLwPLrwM2zjIlYw9wGuvrpU9gLGkdgnYI7I6o9B/gZEz1V67rtGthauZOrbFS5BrrKn4I53YcEe0bN29/K5J6K01BHgXoXQgjy7veMiV6NwYs8wLfbbaPgl5eXY/scN+pzOwEP4M6EPID1Aj3WoY7NCvZBefql56WZuGaz6X7xi198/Iknnrjbvg8W9DPxejsH7CDpthMfA8C63ah24cLi9KVLyzuuX1+u3LixhGvXlnDz5io26nfMGDA21sDU1BimpsYwOTmGPXu24cCBORw4MIetWyfhuA6Y45jvFCTkGakTo9sWOJBNyyhyum3K9od7FHP8+MRb+N6Lp/DyibfUq4LLQF3tCzvWVt96cxC8B6h2LsCDCGE3QNjzEXQCaYIPQrU+fPqcKbwppAfDPW2+t1V9Huzpvm4DELeB1gnO518Dussa5I7jMD2eTpS8ObbjOJmxeNvxTqWZk7Y6AAb4FNYa6IylTPfU9K4vuj0Or1V8ZvydMSZsc30BzHUoNNErRQ8apwGfp971tnawox70nHPe6/XiOI6FXskuDEPearX4yspK3Ov1bMdBWPtvW8ADuHMhDxgybAa4j1rfMOXK5B2Up196UVpePACwz3zmM7t+4zd+48mpqalJCm71yYCa5U/Dg/p2rH1W9C0EXN8X074vZuIYY3HMcePGEq5fX8KNG8vw/RBxHMvV4qIYYRib/TCMEUUR2Y4RhhEqFQ+Tk01MTDQxOTmGqSm5rUGeQL2pzOzJA56adO2Hv1aRjuOAuQwOc6R53TIXm/KyoIE7U7AugnvqWMhPA4CzF27g2RdP4bmXz6LV6SIVBkA9nVLUISgCO93I6RAIARHFiLTJvStN7mEvQNT3rX5J0GBO4O6o626Prxeb7yn47XvaZ3ocF6J7nvPFY3G8cNZxEhM8gXkK9vb4u0pnOZ71GaVOxuuFFafbZq9Tr8HO6T4I3DWErHz0BTOZBXJgvZiGdgJsuGvlntMJSJnt9Xi8nkqnVrgzsBfW+DvnXPR6vZQHfRzHnC5Vu7y8HPu+P2gePAak39GAB3BnQx4wVHgX7Lce7HnxmJiY8P70T//0oQ984AMPOY7j5kA5Ne0uJ82A3UqzOwMsLy/nqPm+mApDTHGOegqYBQpMtWfgimVZeCfp+cuXpssm6fl1M5aGfSZuJLgnqn1xuY1nf3Qaz754EpevL8GEflBXX4Vq3drtD3ZVUcwRBxEiP0QURIj9QG77EaIglOv1r8NzSN8TeR2TbSALdzq+3l/V0/IAY0xw3rsUx8sno+j6G4Df1pDWUM752GZ4VkbB2+PwGu7WC2kK4Q6kPe5t4KttOmdeiORlNZl166lJnnQQMsC3gG4vjJMBvBBynF2nhWFowG872Onxd+1gp+Hebre5NtEPAfhbBvvNAngAdz7kgTRAcj7ok3YrAT1quTJ5B+Xpl16UNky8HYennnpq6vOf//z79u3bt9sGPd3uB3a9nVMeNM5K18ksjlELAj4ZRZiMY9Ty4D5o0RI6NmvDneZPp6fLFsHdXi2tqO5Uulm7XZXT+VU6WHqOux9EOHrsHJ598RReP3NZwlMAWaCTuCGgDgAi5kqBR4hjDh5G4BFHHEXgUYw4isGDCJEC+3pBvEzQ0B4E96zKz78PGo6cdy/H8fIbQXD1DSG6KyXAbo/DG9jTee9EsRuA28DPg3tOXArmQGKiB1HuOk6IZJxe+TRQdU9VuSDbRslrYDPiYW8DXW9T9Q5Aq/QU8PXYvK3egyAQ2jSvGJ8af4/jmHe73bjdbsdhGPLV1VXe6XTEGleywxrKbirAA28TyAO5oIe1/y7Y1xfseXH47d/+7f2f/vSnH9++ffusDWv7HhWl2/DW33ZnQGfIKxvHqIWhmAhDMSEEq2bgmXmQ6/Th4J7Op9Pz6x4K7jl10/ZRK8PychtHj76Bo0dP4AcvvIFeLwCMKpXDA9BDBGBqP1njXggBwQXAhdkWgkPNflb7AiLm4DEHly8KwWZ9dGjQ0+vYb4pcnqpnjIHz3uUoWjrR6106HsetZW1yd12XOY4D13X7QZ4Bifm+wJM+0xHoB3gNYsdxGIVy8ueSv8pdEdw1jDSoAWQWx8lT8uqTUfA0P4W7ArpZwc5S80Ird1u9297zURQJbZ7X4+/UwS4IAr60tGR70ANrAPaw5Tcb4AG8fSCvQw7sR/3gFpcpk3dQnn7pRWlrjc/LB3v7t37rt/Z/9rOffWJubm4HzdsP/APAnZfHnoPPcvZZHItqGIrxKMIY56imH/ZpeNoP+vWG++hDBAmoOBc4fvw8XnjhBJ5//gROnnwr5Yz2bkiub944vb6+OapecN695Ps33+h0LrweRctLrusy13WNCrfAbsbTy3zksfJh77ouQEz0ZJ9BQZaa73Pgn0qjcVBj7LRDoNU6Y8YkD51HpxGFn3K0y1P4yDHJWwpej7Wn0ijQReJchzLqPQgCMcDBbj0V+sCymxHwwNsQ8sCaQI9bXKZM3kF5+qUXpa1HvB03KA8AsF//9V/f97nPfe6J3bt372I5jnewwKzywM5HMpk33dl5HP0ETyo1xXReIeBFERpRJBpxLOoAc/TDn8LWhjtVe3lw77fU6eAhguLXk+rPwsIyjh59A88/fwI//OFJrK528G4YHOg4fdJRSq6vEHErCJZP+/7C6VbrrTOcd3ue52UAreLKAt042eUpepaY6qlyzwBefafSdH7GMuZ5IAG5SbPjipS8yE6ZS02x0171FOwa7jq/DXcN7rw0kYyzG1UfRRGnQat323teq3e1jK1YWlqKfd/fSJN8puxmBTzwNoU8MBToUTLfu2AfLQ8AsGeeeWbm137t1x547LHH7ms0GmPq4eQI6ehDp9ul7p0OetvOY33DjiuI1/tOHKMax6Iex6LOOSoU3v3mRA9a7cwGuR2X15GgdcdxjGPH3sTRo1Ktnz17Ge+G0YLuhKl7EUdR+3yvt3C61bp4qtW6dNV1He55HqOAtvcV6PtBXpvyoQAuAAnsHPVOAW86ASq/oN+6A6DrAtLOdba6ZzmqXnvI6w6BHZen3EGApsGsOwNWvIG2rjcP6ArkUOPvMYW7kKZ8EOVunOuoeg/DUNDpcQXm+VEAPmz+VNnNDHjgbQx5oC/oURC/HmBfr3z90ovS1iPejhslT9E+ms2m+7u/+7uHP/rRjz544MCBA47juCDgRRbceRDPnUdP4J0qU9BpyKh9IeBwjnocixrnqAohhwMGw3vtcHcchqtXb+Lo0RN44YU38KMfnUK36+PdsPYQx/5it3vzVKt1+eTi4qkznPs9x3GY67rC8zxHg1erddd16X4u0D3PM2PyCuQa1hkTvlbk2sQPJPDX8Qq4jIJbgz5nG0B2/D0P8Pa2reRJfGo5W90JIHm47gTY0C8CO+fcVvNGucdxnOxYpvmisfcwDKn3PC9hnr+lwN/scNfhbQ15IPWAB94Fe1lorwXkZfYBgD344IPjv/Irv3L48ccfP3TgwIH91Wq1DmShnPMNKw5IXpyTSWPZIYDU78LRT1UrXQi4nKPKufCEQEUIuDbc88z4yX7x+P7CwjLOnr2MM2cu4fRp+blyZQHvhrUGwYOgc7nXW3pzdfXqTxYXz51rta4tI1HDrFqtGuVdqVQM5CuVSkaZVyqVPPXOXNc1Y/Q22G31TvMCWdgDSKl7nYcCX20zZk2Ns0z3uY54xBRv8ihFDsZS0+c0DFL7JN3AnYKdphFup+Cux9mJehdatfu+zzmZJ69N87Z6J97z3PKeB9YZ4IPy3ymAB94BkNeBgmPAB+uYb1CeUdPXI75M3HruF6UBkO+v/9znPrf7Qx/60OEjR44c3rZt23aWv2oe9LbdAUAC/IGdAgv8uWWsTgQD4HAOD4DHOVwArk6mcKdmfiEELl2ax5kzlw3Uz5y5hOVle334d8MoQYi45/ur5zudxTdXVi6/OT9/6nwYdqgJRNjfjDFUKhXmui4U2BlR9hridDw+tU9M+CnIU2UPBe6iOLVtQK4tAbojwCxlT0FeZKLX30VpFNwU/hradp4cM31q7F3nVYqd6/oI2BNJT+Cux9n1ojYa7hrq2jxvj72XVO8YkL5m4N9JgAfeQZAH+oIeBfHvgn39YD7U9t69e+sf/OAHZx988MHZgwcPzu7cuXN2dnZ2+/j4+BRV4dZ2npo3233Ab6CfA3bdKcjtAAgBVwg4AHN9P2BvvnmVnTlz2Tl37jLOnLmMN9+8At8P8W5Ye+A86oRh56rvty53uzevLC9fOL+4eO6yEJw+iAFkwJ4XJ1zXZZ7nQat1DXIb6jnxuZBXAE6NzRP1LqhZ345X2wASda+3Naht2NN6KNDtfQ1k0hE1kFbpol+c6gAYeKtOATjnKRVPwK7LGMGulbyeEqfhHkWRUGb5jGk+CAKhx95z1HsZUA9KHyrfnQZ3Hd5RkAfSUMC7YF8vsJcF+LDQz8Tt3bu39vTTT88+/PDD2w8fPrx9165dO2ZnZ2cnJiamkT+lLg/afcFugxwAer1et9PptNvtdnt1dbW9vLzcXlpaai8sLLSvXr3aOnr06PVvf/vb83EcY3Jy99bJyd07m82tu+r1yV3V6vguz6vNAsZ68G7oE6TY617z/daVXm/5Sqczf3l5+eIVanbXWXO2h4Z9pVLRpnoDd3ufmunteGub2eAm4/JUyet4A3AgH+q6Y6DBTc32QBroWpnbaTa8i9Q7hT1V9Jxz4TiOmQKn0zXYaYeAgN2ofBKEBXauTfN61TpqmlcvnDFT/Pp81preN9+dCnjgHQh5Hfqo+o0A+yhpw8SPGreW/bJpo27b36m4nTt31t73vvdtnZmZqTUajUqj0fDq9XqlXq979Xq9UqvVvGq1WqnVal6tVqtUKpVKtVr1KjJ4cRyLlZWV9s2bN9s3btxoX7lypf3WW2+1z5071zp58mRXLZGpj5nXpr5xnlfzpqf3z01MzO2q1SZnq9XmjOc1tnpebcZ1q1NWmXdAEDyOw6Uo6i2GYXcxCNqLvd7S9ZWVK5eXly9c5zyiD3aMsD1UHGNMeJ6Xgr2t8AnMmeu6gqj6VJoCcaozwIhZnip+IAF7nsmemuZpvG63LmspfJOPmtoJ8GEB3ozPc865hrkGue4QEBVvylCq0zgb7lEUac/6FNzDMBTdbjemcC9hmkdB/C0B/p0MeOAdDHkgA3qQ7XfBvnbQj5IPJdLL5r8V32XThtp23ao7MTE302xu21qvT83UauNbK5WxmUqlvtV1q9OO440x5lRwBwUheMB51IoifymKuotB0Fnw/dXFXm9pod2eX2y1ri0pkNsPoCJ490sbWcXbaRT2emU7PX5PYJ6CvFb3kIBOmfItEz4DYKcBBOxU5WtgU3Wv81Ko0zRb0dv5uFq1TqdTs7o25xOYa+Cn4lQeOg4v8lQ7AGgvegp3+YKoiNtw930fvu/zdrsdR1FUFsr90taUfqfDXYd3NOR1WKOq75c+Stow8aPGDZtnPdJG3e4XN0qeUb7LppVJHybN7FcqjWqjsWWsWp0Yq1bHxiuVxlil0hjzvNqY69bGXLc65rqVMcacKmOOxxhz1bcHyG/GHA9gHmPMRTYIIUStP69DAAAMQklEQVQM8EgIEQkhYiF4BIhICK4+IhIiDuI4bMdx0I4ivx1FfjsMu+0o6rR9v9Xy/dV2r7fciaIedUTIe8gMihsW8HR7VNinVLAy4xulbit6FWer/Vzo2xDXY/l2nG2O19tFZvq8MXmybea+22nUPK/H1gFkPO3puLvqIMCCu1buphOQp9o12JVzHafj7kEQiBy46/sxDLT7pZVV8OoyvH3A+P+3d3bbieswFFamgb7/yw6dAs1cNEod15b15ySA9lpnWdoSzMy58Bc5AQLys4RTPVXX1CS+1tP0gKIm7dPUKc9z5dYkdWlN6jXrwzAMb2/ntz9/Tm8AAPf75+1+/+T9rutarc2jVqcA3sq1sQb20zAMMI4jnM/nIZncU+jjVL/AHma4p5+Fxx74meTxh2lWYG9N8mkPQP276vPj+xzi6OEkn9bSaT3N0yfo8T76POkv99oR9hTc8etoEe7zR+Q0YK/5auA/E9xRAflMxFQPFf/IYJeCvAfoJbGHZ1m1PdaYk9c8Tk3TVxJ3s+DCveRxAV+rdYE9AAA+iY9H+Sno4RvskPw3ZIDHGLKpfiIm+RXg06fuU5DXju/zKX34eShvmdCzB/AgBXoGeHzwbnnQDh+uwzy/136/37+GYZhKcP/4+Pj6+/fv/Xa74b/JCvaazwb+MwIeICBfVAJ6gAA7t+YRe3iaVdvjGZdyicep9RS1kXhM9NLpvhvscWI/n89/xnFEuK+evAeAFPgLvJPaCtDJJL9cMAAATu4pwNNvyFvgjX83aqLHe+f4Plm+Ano+wac53mcfhmH1pPwwDIBwLx3J40fhrtdr7Wn5ktcd+M8Kd1RAnlBhqocsf1awbwF6Tb22HgXuWphL85Yv7dGqtXlsMdFLYlfYA8A0juNwPp+Xh/MAYPkxm3EcF5BXJvjVEX16AUBM8r8ethvHsejPk/k0x78m/CReaslx+1RZ8SdjAT/njv71ep2u1+syvSPcL5fL/ePj42u+347//1pQLnmuwH92uKMC8gwpjvAlPreX81ppT+v1tV7vWOppeqjVy+PGmrzmcWqWXpRko7DAveR5AD6NtRcA1XWYv9XudDoN5/MZxnEcAH6+cGeOIb0QAFgf2eN7QAJufI/0gbtxHCGDeXFyx/7UT4GeH+fj8fr9fscpf5nW0/x6vUJS+/r8/JwQ7DPcAe+1z0/cW+Fe8kz+qwAeICDPFuMIX+JbejU9VO5Ra8WaOuXVVgvUufC2wN0L7F6w9xS1kXCh3wK+FP4a8Jtgj+vb29twOp3g/f0dH8hLJ/f0AmAFb4D19J7CPo2zB/EGAupfpRqCPIU4ghzgB/jX6xUywC9Av92+v8sAp/b5I3Ew/4KcB8i5Htt/JbijAvJCZVM9JPEjgb0XzHsAXQJ5j1XicXwq5uQ1j/KlPRpxNg7LRC/Je0z0LrAH+P7q2nEcYRzH4f39Hb/udsJpvzS1p+DOv/Cm9rE5/PPSB+wguyBAiJcAj9DGo3kE+uyvAD8Mw3S5XKbL5bL8wEzyb66BN883A/4rwh0VkFeKeb++5D0y2LUQfwTIa7xavZVrwU4BWwpzSb90k5DAveRrAU/VOPAueVbo//LSz9jPR/twOp0G7MF7+QDr++21iR39EtgR6ugl/vI+c88EAHC73ZaJHV+Dk/r9fp/+/fu3PD0/g5ML9FbeDfivDHiAgLxZlfv14Oj1zLk1bayp19Yjwb0F/Dzm5NweyufWe4jaSCTQbwE+97SxFfatXtGFwPwDOMtT+slP3C4TPwDgN/Itf242ya+8vI6TeurjlI7xNE34K3CAD9Ml99VLQE1jLcy1PeRrXx3uqIC8g2bQA2wHdgu8NdCWgFsD9C0hL621PG6sybW+tk8izsYhgXutRuW1OM23gr1lrdbw427Jt+rhd+AvH6lLH8RDD+Dnvvo0TTAlmr+45muaJvj6/jW4Va3y9/IAfCvX9gTcCQXkHWWEvaaHyj1qklhTpzxND7V61KyxJq95nBpH1Outm4ME8hyPA3VOfDTYS2stT1L3gjpV2wT4AfeyAvIdJIC9pkeSc2vesYfXWjXA9oC7BuZeYG8B3Qp8D1EbChf6LeBbAJ/GWrBTtS0gX6p5gZ0T9wZ8npM9AXdaAfmOMsDeknvUWrGmLvV6rtyapJ7Hklopr3mUL+3xEGcD4cK95Elyj3gP2HvWelwEaPukvaL3C7jzFJDfQBXY98y5NY9YU6+tR4G7FOga6JdyicepWXprkmwYEriXfC3gqRoF6lJdAnRODxfgnB4JuFt1b/hbLgbIC4CAu0wB+Y3F+OgdlWt7NX3g1Et5mh7NKq1xYgrYEtBzeyifW99C1IYigb4X8PeAvedqgXzJk/b2hj87D7jrFJDfSY0v1aFyj5o21tQpT9PDWbU92noee+Q1j/KlPR7ibCAWuJc8L8DX4iPB3hvyJc8adwV+wN2mgPzOIr4ul8q5NelrJO/j4dXWHlDnwlsKdE/QSzxOzaMfJd0oqH4L9DWAT/NHg71HrTfka7Ea8AF2PwXkDyLFfftarIF+K+7lcVcNzPeCey/QUz633lutzcQC95LHgTon9oB9L+h71nqDX9u35AF3fwXkDyjiKD+Ne8BcAmopyC2Q16zSmrYurZVyicepSXos4m4eEriXfEuujfeGfY8LgB5g58TNWsC9nwLyB1blKL8WHwHokqn8iHDnAH1r0FN+q+b1Gs0mQb1GAv09AJ/GVthLe70uBKyQb9W1UF/iAPs2Csg/iJjTvXfs4Wl6NKu01vI0saQm9SifW++t1mYigXvJ1wKeqlEwbtUt0PdYvSFf8jzjmNp3UED+wcS4dy+JNXWpV1t7Qn1ruEtA3+rn+K2apk8j7uYhneqtE70U8LX46LD3qGkhz+ld8gD7fgrIP7CEx/mtuJdXW3tA3QL33lO7dqqn/FatpdZrLZuDZqr3AL4X4NP4qLDvNeW7TfcB9/0VkH8CFaZ7yNbeUz2ntsXq5WliTS7xKF/a00sasFO11vSeez0An8Za2HtDv8cFQID9SRWQfzIRwPcCeo8p3RPqW8P96BN8L+hLNg4r3EueJLfAvwfsrdDfG/K/vAD7cRWQf2IZgC/1ND2WVVpreZw657XcXOJRPre+h7RTvTfg83yrKb7kaaHvfSHg4gXYH0MB+ReR8Ehf6ml6NKtHTVKnYklN0qPxpT09xNlIjjTRHxn2HqtH7Vc9wP54Csi/oDLg4+oJeU1vabX2tPqscavW6qc8ym/VLL0cSTcNKdxrfg/A12resN8a+m61APtjKyD/4hIAn1PTvkb6fq3XtDzP2CPX+tz61rJM9T0meq/pnls/AuxNtQD78yggH1qp8LG82trrSH4ruHtP7Rqwe8Ne2ucpC9hrNQ/gewE+jT1h7w19dU+A/TkVkA9VVZnycX0VuHuCXuJRfqvm0V+TdMNo9VsmekvuHXtP75JeLtxXvQH111BAPsQW4+E9z1Xb0/KssaRWyiUep8apb62jHdfnuQX+W8PedQ2wv54C8iG1BEf7nNXa2/KkvV41D49T0/R5ibuJSI/ra/5egE/jh4B9QD0UkA+5qHG0T63WnpYnqeexpdbqpzxrjZLlAkCzWfQ8ri95Rz2yL3nu0A+oh3IF5ENdlEAfoM90r/GssUcu8Tg1Sc9W8j6uL/lW4Eun+0PCPqAeaikgH9pMgmlfWmt5nrFHXvMov1Wz9Fok2UB6HdeXvGc6sl/igHpIqoB8aDcVoJ/G3nDveVTPySUep8ape73Oskl4HteXfM98zyP7X14APeShgHzoUGKCX+NpYkmtlFs9Tk3Ss4W4G4oV7iVvb8CnscgLoId6KSAfOrwa9/dbnjWW1Eq5xKN8bl3bq9UjHtfnuQX+4noAPbSlAvKhh1Vj6q/FXIBbQG/1ODVOnaPae3htDL2P60veHvfn03jxAuihvRWQDz2VKlM/N5bUNHnNo/xWTdPXU5wN5SgTvRvgA+ahoyogH3oZMS8AJDVOLvEon1u39ksk3Ty84F7ypcDXAD5gHno4BeRDISAvADzymkf5rRqnvrcsU73ncX3uVfsD4qFnU0A+FGIouwgA2HaCf5TjeuuT9VRNO9HHJB56aQXkQyFHMS4GtD637vWaljyP6lt11v34AHgo9FsB+VBoRxUuClZlzlt4/V06yPQQXkA7FLIrIB8KPZEaFw1dFVAOhY6n/7Se9gCSBwLeAAAAAElFTkSuQmCCUEsBAhQACgAAAAgAbF5sRpWOI66SAQAAzwMAABsAAAAAAAAAAAAAAAAAAAAAAG5vcm1hdGl2ZS10eXBlcy1uZXR3b3JrLnltbFBLAQIUAAoAAAAAAGxebEYAAAAAAAAAAAAAAAAHAAAAAAAAAAAAEAAAAMsBAABpbWFnZXMvUEsBAhQACgAAAAAAbF5sRgSle3HbbwIA228CABIAAAAAAAAAAAAAAAAA8AEAAGltYWdlcy9uZXR3b3JrLnBuZ1BLBQYAAAAAAwADAL4AAAD7cQIAAAA="} \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/command b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/commandTrial b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/contentMD5.txt
new file mode 100644
index 0000000000..45dfcd2313
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/contentMD5.txt
@@ -0,0 +1 @@
+YmU2YjBlNzVlYWZjYjQ3Nzg3MjQxYWRkNmU1MjI4OTM= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/headers b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/results b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/toExec b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/toExec
new file mode 100644
index 0000000000..ede26fe287
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/10_NormativeTypeCI-network/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.121:8080/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/body.txt b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/body.txt
new file mode 100644
index 0000000000..48df31d188
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/body.txt
@@ -0,0 +1 @@
+{"payloadName":"apache-type.zip","isEncoded":true,"isCompressed":true,"artifactList":[{"artifactName":"apache.png","artifactType":"ICON","artifactDescription":"","artifactPath":"images/apache.png"},{"artifactName":"install_apache.sh","artifactType":"SHELL_SCRIPT","artifactDescription":"","artifactPath":"scripts/install_apache.sh"},{"artifactName":"start_apache.sh","artifactType":"SHELL_SCRIPT","artifactDescription":"","artifactPath":"scripts/start_apache.sh"}],"payloadData":"UEsDBAoAAAAIAAeLb0bDQzKLVgIAAK8FAAAPAAAAYXBhY2hlLXR5cGUueW1shVTPr9owDL73r7A4jwLbDlMPkxDb0w7TAz2QdpieotAaGqlNusSA0LT/fU4a+gOxt3Kgtf19tj87IeNyKQo8KK1IGe3EGa3jlwwouJyqmwrFVdaVWIg5/y7F/ENSoMutaihE7tbb1RLaSGisOSj+vygqYdnIvMQ0IWSfJBRa1piBDOYpXRvsXV3i9+k8nU+3z8vN9tt61wfIE5XGZvAkHa2M1phTknBSY8llCcAUJqHmqTa2lqTObQY3tcZQtvCsqS++o568BbrgfouWi3qMTLQpUIRQn1tWCnXqbS5tm/ZWgAItUxbiYE0dJY1RP278MW6g5+dgAtiVGLVtKeGZkbDjnGCxsehQkwOpo56QGxZDszHiqZQEOfv3CLXU8ogFRxdgT2y6gozkK4adaMCeBnzOrHtV8Vq0LfqnNI5u78zPsVlsfRgdFeAZkVQ6dugf3o0G7ZDQP2dmKMRAT/h5r9SyaSqVSy8PvAYoyWNHonKv2kzV3KGbtWKkjT4mj3PeFu2uj2jurHwo5Kkiv48fOyNncmS5Kxq1MAX8dZLVMNav5X0KhuFxoMdo6hsGwMFYHhveBu76DRmV9Gn+/4qOFvnUWGGsiMUtor8w+anmNRHhYAxAbZVMp6J4d2kns7O0s8vlMgle3449yLyXdku8X9IWPWkeqhgmUZq3bVQrwGb9ssvgNxyRRBzYlddg+/X707ug5Cv8GQG+rFfiZb3+N2jU4xgd7invk63w7QzcTLGQsqpEXCBXdhi28zC7wPA5CEu65b/e3whvHYubBI/uiBGuP0h/AVBLAwQKAAAAAAAHi29GAAAAAAAAAAAAAAAABwAAAGltYWdlcy9QSwMECgAAAAAAB4tvRl588h0WKQAAFikAABEAAABpbWFnZXMvYXBhY2hlLnBuZ4lQTkcNChoKAAAADUlIRFIAAACNAAAAjQgGAAAAil75pQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALmAAAC5gBrJo5gwAAAAd0SU1FB94LFA0TBVUWAGgAACAASURBVHja7dx5fKRHeSfw31P1vm/fd7ekbt1na27PaG5fYzy+sY3B2MZAbAwBsmHNsllCzj2zy5JdQvJhN9nsRRZjAgEMxsbGNvhmZnzNaY9HI2lGt1qtbvXd/Z5V+4c0rA0JYVnIOuT9/qXPq1arqt7nrbtewOVyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcP51Lr7zyd7LZbOjn/b3MLdpfXhs2bG1ddePN/wQAhoY2bvo5fCUB8LlB80tmcHCwe/9lb7u6r6/Pu1LMezp7B68EgIM3vv2Td9x1zwd/xq/VAPwWgHMAdiluMb/1vQ2JzizY1edhv5IBT+yC753TsF/9cyx9dRiBsWk0XlgB6gAwNTU1t/fAlf8r09v9b4b6B+oSsgcAYqn2DZ5gKALgf/xf/OsYgH8J4BoASQCfAPCsGzRvQe+Op67dQp6tc7q5JBpi9UNI/NkmBLpnyap/C6XibWjvrTFIR6of+1XevfmkbM5/Rxb+8V84c98CgGAwUh7q6bnYMYwTQiKy+/IrL+3qH9rx2vFXln7KJPQB+HcA9q3/PAngnwG4b3x8XHGD5q1B+6AWv3mT7e+bYMalHzCTV17U0PyWJDyJ+sIZcjx9TEWEPMFbKRD4giws3kwdmWuQ7n1E1pf28vauvVL7o8educefnZwM/uGf/FlX/8gm7YVnvrex2x9wLtq5959L4krIp1p/Szr2Afh9ADsAtAMwADyradpdp06dOgDgdQCfdIPm79jdmzp2HYjEf7NDKJtFQ9j5sjF3Il8d+n0jPayCwRGEFxqN/DdkvX4tRdv2UqxzjjnNb6jNyhVOPGKC07CaST0tDX2jiIRyirWw6njT7RToH1NH3jU0NHTf73/6Pw6eOzsuPcFIYGZm2hreuvPg5Oun9ACc0b+hc/suAPcC2AIgun59WtO0rx4/frzOOf8WgF4Af5DNZh92g+YXz/Ov2rv/2RZ4r/K0EFk6ZypGsBW9PNbWxSsOUFU3v9Pw4S9RPJtiwdgeFkmNUqxtXHFazzu6uVkGtQC8/kEPw5NMys0NL3kFUxc7bLUj50Ey2jl6tkIyKHwUc5JjwNn7LFuiWsmTLQTaeofUYmFF5udntbtvv2X13765c/sxAHcC2AjAt369parqK/fff//Mtm3bbgEwDGAOwKez2exnAcANml+Aq5LJzFXM/6F+R9th6E5ipWhFR20+0gevJhCE1QK+UliZ3iC19Gb4PICCd1P7yMt+WX4oQPX9JW8w4TDfkx1+dOUYTOIIthj0lKTFlgrAg46CxNOhqhwqJemlzhoNLQZgeiK9sADdMpVGowHTcRC1JaZOHa3+wW//RoRz/ux6TfK7AK4DMAqAX0i3qqrTd911V/mTn/xkL4BLADgAXgbwoWw2e/LC59yg+fng79jYcfWWWDCbzzcyY4ue2+9uRnqEJAgQBICnUJ+fJT26g8JBmwgHqb3vWAj2862ms9XycpsYBg0eNUIePJ8kMVz0sMEC8Eikhh21AAwo6C4yHOo0MbjogykVtCJEhWYA0IA8BaB0pAcwCZgtQzQbDdhC4uyxw+anf+83IwsLCyv33nvvVQDuADD0psRzXt+xY0fzj//4j0PJZLJv/fISgAcBfDybzZpv/LwbNP8PnddbQsmrI4yNyRi74zNbhkdjLQaoDs5p9fp/nludvLUWH/QRJ4cYdlCi63AY5pmGI9uFhyzi6G4w5WTCj/HVBpLSAxMcyVWOM70qm6uoUIQHtYSGYl2FQQp0cCikIUc+WKRCLxEmyIale2ULQXL8iAFAqZCTucWcVDSNLh/bzO+55x5MTEzEC4VC6o0Z8Hq91Y0bN4rf+I3f0Hbu3Nm2ftkG8AqAe7PZ7It/XcbdoPnpKffs73vfjvbgxpWCHq+92rj8DxptQ9wEjArw/ZnFuaWoY33IlxwYrFLw1+uxoftSqG+psUDc0cgihmydaY90Eg4sKrDAYIKjvcTxZAfD1TkOAxw6cfTNcTzfbaNnwQ9RVvGa10DE9EOHAloCTsdKiJQHYBhRrEQmoRXTsHkZ7T6/CgD1yqozPfE6FGnjuUe/xW3bxhuboVAoVN6xYwd++7d/29/f36+9IY8zAL4M4Pez2azzk6aFXT/Bh9va3nEJD99iOc4+GeDq1s3x9DZ/wFNdNZ1HJvJzI3NqR1b4vTYRypycb4db5V8rKgkHBJsYvtznw84FFZLWAmVZA6Z8NkarQVjEYYKjwQgT4VWMlSMwwGGQgrNRHU41AcCH5Z5ldMy2Q4eCFhSU+ouQ57sBAE5mCXwxjXLkDKRmNZ49//VTfr9/rNlsqj+al2QyWd6/fz996lOfCiWTyTeuBjQBvAjgV7PZ7OTfViZu0Pw43/sv7b55NB7sXZpp7OyaFns+XI52+6HAAeF1MionEnrz/ZFkGlUHx1tGc5Kr2r6mXzHBUOXAcylbvGdRMJsYWkT42mAQF017YYLDJI4TfQ4GZ3wwwWBAgUkcM/EGhoscDnnQwlpT9GqiBP/qIPTeVfhnYmhBhQ0G1luAmEmu3e3onKzpq3LBfo3NlycgId6UGVVVmz09PeZll13G7r333rDf73/jr8sATgL4fDab/fpPW0D/4IPmYDS6/WA4elUvlCG/waKzwhxW2rXQjk3x7ot8fs0oWXhuurwwcb7Wur2eHCJiKCoQR0Kmc2cBqgPCRJDjOFfERj3ELDDM+wELVWxveGCCYTLCMS41dDQjMKFgOWCjojiIV6M/DBoDHMvRGfSXMtBJgQ4FK10VOPNdcLwN2J4aPJUOAIARzznNWouX2CzO147Lurn6pvvIGBOdnZ2IJpJWSze17sER6unuQioWRVdXJ3TTqi8u5soTr7/24KMPf+tj/7dl9g8iaMbaIoPbE+GdaYWlAo5CR836/oHtMTWbCMblgtGFedPTd1a2X9zweRysjXjOwKgdCrQWr+mK9/c3mWZXbXwlyOyeiqp0mhrmvQxlVhOXVzmzieFkjGPK8qLLCMEkjtfaDLwtZ8KCAoMUPNvPEJ5NrTU/ULA0UkFwIgUDyg+v1RI5dBTjaEGFDhVmKgd1ZS1Q9O5JiJaKgn0O06XTotTI/9hic0ck43T6Bu3rf/WA5fEHgodeOYFMpguTZ8eRSHeis38I9VLBqqwWpyZef+1/nnzxB//hZynPX4qguWFL+8BVm9s/muBKeqHYXD27WG+/OBWp2i1HTBWbW0MO7/lAPJVJGZyo7gANgZmabh4TzaVcL5O/cnF3b7NkiUMTpQV9ygxd3YrFbCI4xPB8p2bHdV25dNmEA8LDnZrQSh5KOT56JeHgskINAWgwwfG9NEdgJQ4JDS0wzIWXsLcShgEFBR9wzMsRqKbXAiRdhr0ShbD/T3MhyEItOIdIbQAAUPcsyZZdpaZacPL2JF8sn8OP1Cjojvc5KaWLBniWNNaizdUk/He34Zp/9C78yf+4D+VaE5FYQlZXC2axsLTc1TNgDm3Y2O/z+ej08VeeOHn8xU8e/t73Tv2DCJrOUCix3a9dpIBtv35n+z+9azSTRkMANQdLxZb53ZnV5YO+cGe3pTBRd/BiqVo61zSKfhOJmxCNifUaxSLCN+P2cm+nFrxMZ4FW1cbXOJxteY37yAMLDMc7FLutUaWxsuA2MTyQUWT7SpIkFBztqOCWRcACR4sUPNTpoC3XAwMcS/EKLioCNjTopOBsn47qzAAcMAjYwEAO7FzXm/JVCJ2CYwmUxRKW6lOyYubfdI88Hg86I71OypNm/bRRdldDbCU+hV2FFBjVkEcemXgYY1/7nfnp+fnW0fNLQ/VKCZmefqpVina1sCqsZuVYT1eXZTOlxxsIse8/9OC7H33ogSO/dEHzkav7bh9LRg6EGohNztXD1aLZf6+/bah9BcpqwRDPs9byXNCp7s5EMjuZN0R1gUPl2kqhZbAbrXDiQpAUSTpPJKmRbNjePYZPs4nBIYaX015wu4lrFnUIEL6RYiJRjTAfvDCJ4+iwgqsn89LnKKSD4SsdUvYWeigfsBBzCuhtBmGQgvmQgxMiAJ/eAQMKGpFxjJTTaJGCVa+OZUbwNtNrY/iRPMyzCdSVJVSVeeT0KZlrnCdHvnldMRAIIBPqstrULtYtR9FdD/FGxxI2LvkRkjqmeqrYP03wSwPPhVbFzRUf+/51PQ9d++lPDT3w6OPR9oHR9IlDz4jdl13JbNvAmVMnRVsyufTEdx/5aDAc7g2HI7f/989/7gDwI73ov29Bc+P+zrddPBi/ol1T20/MVEZ3pMN9Yx5/cqil+ZCzIJYsPDe3Onei1mzdjdQIJwU64zjpo+aJsGV8dNGOqSAsaCS+G4N8b57zC02OQ4TJkILDQdu+admj2GCwwPBymtvbV4p8QGdkEcNX2zk6S2lY4NCJYby3hpvP2TDBMecFjlBAhq12ynWv4OLZtRGPTgpO9xpYnR0BAFipRaRXIj/spzS7z0MsRFFTFkXePsfy+jRqZvHH8h+LxNATHpQxJYERe1gmGgm2FDsrxwpRCkkD84kSxlY4/NLEsR7LvnLaUrzSxpNtFm5cEviOWHn4stPfic3MzO6erxlqq1K0FIiKP5mOBQJBPnXqFWHZIvfCD579T01LnJifnVt9/eTRI39fgoZvHwrtbi45rWtbqavS5N/cVNmYDHIFKcnv3B/rHdA0FXWB+UJLfm+6sNhfV9mBijctqgI6CN/q8VlhHeq2EkODFFRJwVM9ZB1cKquDTUKdE77aAeeWHOMMCixisMBRI+DxHsKlcwoICkxwvNhl4u6ZGkww6MTw5TYN6dVOmMSxHDUx0igibvhggONIp4NGbggNr4Wodx7tpQQMKCj5LMwQ4GulYUFHznsMfh7Bqr3oFMxZnq/NApA/VhAdyQy6fANOhveSaVroa25hrc4csrkkOICl4Mu4puiDX5oYD1TF3prCvNLG8z3cvPZ8U1Olg8fambxxyaLDVJ/vffVbtzHGnnr6yEsa4wpdtmu7fubsWeH4wjw/P3v2wN5dJhElANyWzWZf+qlnOf9/RMnNCF26F8HLaiS8ztvCt3zq9uymxflm6/CR4gIOezq2We3BekNDranhC9P1kh2er/2bXm9PV1PSB1rRzkLTkn8RZeYVJmkxk+HgolBfjXqcB9LSuWRZ01qkYvOcpj6eCToj9RXaXmbshmXGH+xh2JKTCNva2owsceyaY3i4W8fueR8ccHTk/Lg/qeOqVRUmOHpsiXOkgMgPsxLGkYyBPfMadCgIr9oY5yX49U4Uu+tg5QBqUmJZVOQSP0e29xUU9HlZLCxdeDj5j3RkZU9igHp8g0jItKjpVRqqbecAUOmYB5oAkUCMBCQcNI260+mY3CstLDu6DDlAUAh4bE0GHBNcCmg2dzThKKpfDW/YsOHwmTNnvhzQlA9YpOgAvKMjI/aRl1+WXo+vmzF2mxBiBxF9Vkp5gIjEWy5oDvpiPXe2gn98ELG3B0lTdcaRfxbOF46em9tzINj2K93JobO8Vr7/5dnp/saWvip5oFE01moMxD6ysmj/7vKkMtAUiIHozqKjPdCh2F2mRl01xjuqjNsG59/scuzdi0GlAQ2JnMZfTgXkgrLo7ClqfOc8w0s9DJEVB0nD/8PAGV0I4LlMHSO5FAzJscQIi1QGwYtkheNQsgBW2gADCpp6EjmyYEKDbiowvBOo87IoL06zhlpBvjYPvdGgv6k2j/njojc0zFR4ZB/fgFgzA2YxCDjMDkwCP+zOkADAYDtWjBzVhAnb0SnhWFCFAw8citkSihRQHBWacECQIEngkLCyaRvHXoWiKH/U39N9x9OHDpfkRZv/lIg+sWv79sB3Hn10Su7Yci9j7DsA/vD06dMHADz5VmqefPuj8Wtu0H1/dJse748wLwymoEUKGqSiSSpOM6Oi9NboTq8WrjVs+TnHa1nLF2lV8qACL5pQoXYs4WPzL8itBid7vW/yXJLBEV67u6opddKwyjmeyxgYWUiiQT5U4UExIRCjWXt3XlNMcJxPA42WiXQlApPW5knKHgcFbxPRavvaGlBiEpesBKBDwam0gaXlDdCphgbPQ6cJgDHMN+dkQc+RLf/mDXHhYAQZf69MaWkJiyOr72QKqSgmZ5Eo9PyfeXxRQ01Zle2ilwCgml6yw0tpxWifNd9bTmh10YCuPyeua3DGpMD30pq4dtFgBOCBHq9x62zLgzDHX6XJuSji43Mfvqt43Yd+NQkA4+Pj35yYmtrw9uuvHx0fH7+OiO4XQnxvdHT0tp9pEe7/tT+CtWMwFgDcsLlt//ZYaJPaENrEckOIjOeiW3elR9OqtqVL9cQ0XWIu12ieLtrl+bylpKZZrM32qDXSEEQ4sphLyT8N5+SvzzbpN2Fq/7HnJJZXL4dYr9WtXBqf79lLH8kdx1BrrfO6scTxStBWTiX8IlHSmC41DC9EcCJTQygXgAEv6qterASDSi4zaY4t+TVtmaMScfByqoaOYmZtgs3kqCUcGNUgDHDUnRSeC+dQYaYzXyvyBc8LWCkvwnLMn/jQhfxhdIX6ZJvSJcnk6LD6WNRso5pZIosMKLS2JCTMJsLQwSHApUDRsyIjZoTiqMIPC4uyLDYKG3NOGUkWBaSFzobJNKGBANg+TXLoQJDBCEuS2/2oRBjOp9qMng9+yD/QP6Q89dRTyhVXXGET0aNDAwNRAMhms4+ePXv2s0R09c+8cvuz/FEmA/+/+OS++0Yz4ctIF8pqTh8/cbKEdIl67wokOrBkgbwWzr9aN4+fKsy80MmKPVvbI1FbYdG66t9S535IB6e6JZ5wdNkxH6YGeWBYHso3YvJPO1537lpu8l+fb+Bznc/iTOlyXDiiZS924M+7N+HXZicRslVYYOhucJyxDXYyqcp40Us10hDPRTCXrKJeJqnZQaK6gqqR1Z7InDMHliMaqmEoShTHIjPg1QTqnpowahVW95yBIepidTXHKs3Cj/VDfmRdB6lQWsa0hMwovTJsp5iEg92tMClwaDG6imDZB44KmL8ItHoRgA5VtGCZNdElS4xBgksBO9yi3XkdmnTgkTbMVpW22hUYANrJQUFrYlgo4JBAgKHpMUlu80OPEXzM5hgK4JloN2774G+tAOglonNXXHGFDQCGYTyoadqBC+leWFj4TEdHh/hZg+anbZ68776l/zPXXtt9eSbtDxXyzXJ1oRW7ujfZ3e3xKmg4QF1gZVm3nzi2PI1TTd97F4KdNhhMYtAZx5OeVq7a44/fU3M0qgugISBsQIfEf+0PobAyCAtRVOFBKWTjOuMFcVVNMh2Ez3cGMF+4+M2bWXomcNe5Mpz1tRsTHEvMxKkkZKzYQ1X4UIEXrWAZJcw5vBVkXGVkk44F81UBbqNptFi1tQpDNH5soe/HNs9oGtLhDDJaGkmekE2zKdvlFvIbYcqHZpCpD6+tC2UWcetsAQoETnVJhBaTYJA4Fy87W4o+7oWNlmhgghtyo0wSgwCHxHiPjhvPVaFKBwXFwiIZYq+hse/2Ba3b/BvUp9UVXLs0ARnneCmuoDMIZNIePOYw+EKw6z2blb4r3gd/IDhDRCkp5a9ls9kvXkj/xMTE3uHh4SM/j77GTwya0YHY5uzG0J9cc7Br455dyY78im6eO1fN+72qedmejkxuqdk8+kSucEXT3zeseDQ0BFB30KjY8uFcfZktiNAV9UDgQt+lpCh4rt0RNy4UadDkJEC4MHdyX7cPLxZ3QF/f18xTRbyneFL06QprMsIXUwyF0r4fJlmSjWD6DC6fZ8hzE3nVlg3uUIVZmLIrcEhzWrLGq8YqVhs5/KR+xw8LgwiRUARhHkFIi4uwGoNiGRhV0kgYPqrEDGzMJ4lJgalMDbGlDqxGyohXvPCCg0Mi37kg7zpfJiYlHunSnP7lGGdS4rVOE9fNNqDCwXneQEvEkSANDBIMAicSFXnnQouYlHi8U8N18zoYJP5yW8K+Y3RU+U5zCbfwAhBX8MWmwPu3+ZDTOE6l346BrZdMKIoyvJ6NJzjnnx4aGnrqF7ax6G9qfn7r4/v+y3VXZm7tbA/4ZmbrxkvHVqYMKTz33J3tAoDnDxWchSkKv/M9I/GpGdP8wmOl1g2n6742nSFYd+iOutKx6Ah8OyPN9hIpSdPDmkLDSE5jD2Zi2FKex546wSGCBYYbliTymZM4U7gUAMFZSeALqU72/twKVrlEW7WFU/xBwNeJhlOyDVnn9dwqPeyUUG3W/7oH4MeaFc45wsGQE/fEuJ/74NMSMiDDjk8JKsLOy812m0zVNHYqpItwdQvTDB2Wegz7akFI6eBEpYwOoYJBYknqGBJ5nPE2xYaShzFImLChFmaRMteS0mp5kHQ0MAhEpS0yVoVxKXA+LDCoOyDZBIPErNfCJYsl+J21JNcFoEkHVSYQ61eZOWzAmixKuSVAj5kRxFO9eDboVJDcHhkZGgOA4HoWnxoZGbmGiOQvclTzowXNPnTX8O39/ZGP9nYFOtd2gVlIBryx6y7pjDebtvzOD5rm6HaubNsa4gDw8EMFBC0PLt3ixw+ONeXMt5btd363qgbAcWHF+GiHH894VWtgOanWyYMqeTAd1DGoT6LLIcwpjlPWiC/DwkuCoQWIml2khlElvVWFCftvbz89PgS9QRlmARlUg8zH/DBsQsqfER74UJcl1s+4HK4EifmYPOXlIlwa4T6YMCJ57FwtIQQFOZ8OXfeiAyoWw1WMlAEfGKZ8TfQ2FfjBUCcTZ70tsbcZZN/vaOKanAcE4GSbwO3TOXAQjgeAmgihEx4AEoeiFed9SzYnAI+2q9hW8a8NkSHxvQzhrvMVIMDwfLuGzY6FSEjDoyk/rrp7EPcfz+HiQSEn+BB1jb0HoXCsTEQ+KaUHwBIRBYQQTwQCgY93d3cv/MK3MF74Ye9FqdsGe0O/d9VYZ3KgKxTeOBINXAir1Yot/+rxurx4hNOt+0Ke09OGc/8Lefmem5N0/Z44CiUdX3q0iFsvjtL+T/WpT11ZlWe+uSAueUXyCa9Aw2rCtiz1z/0L0pReURYtVmnUqdRchSmMv65WYIwxhPxRmYh1kgcKIorXCWgBHoQfDbMFaJ1QpNdWyacwS4Nt2+hS8xirRVnR1HEm7hdxvZ+hSkykl2VftYmNZS8thU2cVxldUYxyD6bgkSZOKKvoFV4wGHjVV5PX1z2kSIGi6tgZoSoEiYkYRLpuMgaJ19od3JQDW1HK2JjXZZvwE0kJwdfOhRAk5lJB7FkCCDpeDQgcXG5yhVQgwFAPcvhXTRAkljWJrqSE3BRFwwssGw4u7gsjrwDjTUKjGanG914eLnZ0zm1oz/QAgJSygLXN4RYR/Xtd1+/bunVr6e9s32s2mwxdt7P9Sz0d/q2phDd6/Z6uaG6lJe776mJ1pCcW3J31sBgYvXunH195uuTccbmHb2jXuGKq+MTvnXL2bU3yQtUUxbIhf+0Pz/Nq08H5hSrN5cpcNwxI40KVxpDwxSikBrnGNBFWo2hvHyEP80MjL1p2HlsRdTSm8Tm2CrO+ET4rThSsYas5j/6al8sGwYbAY3HDkeVt/ELQO/4qtvIz2Fv1UFmuYjLlx6VVL/PKORTa6ohXKjRaUbAYqqEKU96U95KKBahS4EibZV+3yJQAmpgNOOKylRrrslooaAKpIpSI8MOGA2oJFpFrIzgpLERsgaNtKq5cYERoYTIAjAUI2BOApQD1ZRt+Zy2AFts1HNwWhwww2H6C+oOy9DoqSQCHN3pw9+1ROH6G+09U8aGr2vBigawZNqZev/dGaJpG64Fy4QE/SUTHiehFIvru8PDwfX/XM/rKwR2Jj7/n7X3XjPSGPZYhxPdfWMkvrojQwYui4VfOVsV/m7IdRowtFXU6P1/m//VrJ51coc50XSefT+MnxvMyHvUhHvHylmXJPZtTcv/WFDt0uAL/4jBUocKxpWwpy+K2RieHDUxHTTbViEl/LQYJQivQkL1kyc0VLy96HVnx9UrNjjI7Upcb5RR2V4gkLEgAT6cF+MpOLiEgQHC0FjbhJG6qMhgwcSolxDtWFaZhHrNhU3aUW3J7w8PyPkuWeUvcXiDOZRNcCkyHOaK6VNJSAUFiJuSwDxQdEGx8u43jhhUPCAaeyHDcuGBDBfBMu4Lb51rwgqFsmPCuD7pOD/lx55VBSJXweE7gxsMFaCAseyR6xzyQF4WBAMMTZw1cv8iIw8FXN3hwy2c70Axr+OahADJvuwQP2eF6du9+3y6vFwBmAfQAOE9E3VLKRxljHxsZGTn3/3OxUNk2lnzPoq7jzz4/VYmGAgHTdlLL+Tp9+dE6hoaiTLSAbUNtcqBDEY2mwu55/1bek/Hi1BlDJD0a27MlQEJK+uJ3C/atN8SVyWmHvvuf685NZwe5BGHVa8qWPy9vKPq5RAlTYSn0eoC2OiCBElqKg1YwR9fPC7Jh4lCM0cblNjLUAhR1Gu9YskgCkCCciEhRL/TQoFwlSYDBbAQ9484HCiaXIHw1ZYn3FjxMRQGzPimEkOzyOqei2pKzIeHcMWcqDBJMSuR9DK8FCTcsShBMPNuh4N0zNWiS8GxCwRU5AT9MLHsJ3HAQchzkPIDashCSwNNtHO9cNqCA4VC7gks+GobsCSDfdND410sICAkJged3e3HnJzohAwyvztgI/vsFNLiU39ydoMzHQzjVSMpKflPjkiuvCa6P4CpSyiDWjpLYUsr7GGOPEdHp4eHhY2+FHQjK0VcK8d0j7cpAezK4bV+EpTMBevlYXb73tgR98b5l565Lk5wrjJbLFkUHgs5l1yT4499vorfdwzaPBKCbAn/52Ip47wfalK9/qSzE53TaW/ZziSrOxUwM6iW6NE8kqYWjEY5yU2VjgkGijBYTmIqW7I/OTry8zgAACJdJREFUmooE4YE0s8eWMopACRPRgvhHc3W2VsEDCz6GSRlmWx0HEiVYAJZiC/inc3UuAXypzXE+vMy4Jm3kvVKeV4hds6qixm28mCDnnmldWR+sY8HH8GyHF++aMQBInAsrCHoMxEd8OBFSpHK2TmnBocPCU21e/Mq0iQYTOBRT8P5FGxUmUFAVRDSGnI9Q/Mdx7Lu1DZWqg4d/N4cPHNfRIom/2u3BLV8cwOuLllicS7CJbzcweM2VOLl/W+Pgvl0XRjznuoHB9SZoHkAXES1LKe8D8JnR0dHCW23bCn3us7/zX4Z6j39kYCCEl4+2ZHaYUK+rYur1Fr/zqhiYArw6ZYjpYkveeFOCP/BgxdnQrbDRQR/NLlg4fKoid+6M4cnPLMvLH7IZA4MFiWeShrghV2fdYq0p/macRKjuZx3kgwSwqkqMx3V8cLYJCeCxDs1htQBP2Sqe6Wjan5ipKMp6wBQV4GuJADZV1pozCwJH22v41EwVEsAX2qTz7gJ4UBCWVcjnw6DrywpMSHy9izsfXjI5AgwIcJxKeXG6ZuHmKRMEYCoAOb7LT+/IenGkJRz74RW+r8hhEPCVLhV3zVlY1iC/36vS+z0cLQ7cF1DxYcvBogL51D4/3fqpNA696FiHv7KiXvKygJlJtowrd3mDu0bIEY6TSnXXQqHQhYP1eQBt60EyQ0Rd6wOBp4noJSnlWV3Xv71t27b8W3WvE505c+YHJ088P1ZceVEarWUujTp/+4EosxzC+LRAoerIsTFOHe0+fPuRmrjxQIBFYyoee6YiIlFBq0sKeT+9iN3TEhLAVIjJ19ps51cCqkJCYlkAj9RN5+CcyrX1pYAj7VwyxaCbqg4ggFd8zCk1FT7cUvBYBtY7/LqakhwkgCIkvqkDl+d8kCDUFeC5bgv3ShsQEv/dMJz3LYD7OGE6xuVxEnRdnmBC4EvDqvORPi+nAIPlY3iwxZA+VMHeZRsE4HCKO42bgnzXxRE8+oO6vevLeWWopeDViCJP+gSuXbHp2YGwrMaa4oYePz9FmjxjSPttflOdi3ebuaFRNT3SRrrprWWzezyKomjrzcoC1t6yACnlAhF1rpd3ZX0jTURK+ToRZQDMEtH9w8PD/+EXPb/ycwua8fHxeQAxImoJIXixkPdMTZysVspH2i/a2hKcEZuZk5C2KW+8LkaHX25gfqHpbNoYZCf/YpXe9T9X4AdDhUs8lg1aPTu5sjfjo6W6gyNzlkg+v0r7igotKgKnO3winyK6Mc0prnDAkTjSEo5ztM63Vzi+3cPt3Xs9ykBAA4TE603g6LEK3j7FABBejzIxMUrs/X1eFFoWHp8z7Ns0VVGCHM8QHOdYjR/IA3UGfG2bx7n71gSnIMMzS6Y9f7ih3PFMHQoIr0YYTm33Wsn3RdVaUYH8X3NywwxkZaSTLW/sNpdtHX39A5pnx0b0b9s8nluYy1qmiUAkNltYnOmJt2VkPJmaZoz1A7CJaEVKmQYgieh1KeXG9fKdB9AlpbSJ6LCUMg9gmXP+1PDw8Nfn5uZ83d3drb9v+7PpzJkzNxPRF6SUIcbYvJQysf60lG3b7i+s5LBaOKo3myXrtdNnQrvHEk3T7uBLX37Zc/N8ASuaR+SHB8WpmKe+eSwWZSAYFEUu75Tb4Q8y01JOnD2BuHNO3HNRnHEAcICW4eCh0y17+/crSocBPDDis294X0xJBhWM53R58izR0ENL2FEinAwSzmR99qYDPmVjxoeHX6sjaDPnsi6Nv9QCxl+v29c+UVXaHIaZsIpHL1adne/J8LwewZFHJvX9C2Gvz++H1RZDtSfVWMmIVlfvSDLTmUV5eUUXwllt7+nOhEKh0vqE5w/f0YK1N0HZ6wHQt379FNbe5WKu1yr9678vSCkDWHs33ZNENA6AFEV5bXBwcOKX5UgQAcDZs2ffK4S4k4jSABJSygwRKQBWpJQlxlhGShkgorMA+qSUHinlyurqqhoKheqapqlYe3MSsHbEc+VC9QzAkVIuLy2czzRyL8HHm2gZ1GzxzpYn30yopRKKsWTTl2ZMYY53+vxShfJ2MBuJcygKTi2ck8nuutWVZJolAzgxpaMt7K0lO3tClpZC/RsvNzqPTvqZEFTf2Cesd1xaTW0YiaZS7eCcn5NSDrwhv+ellH4iupDWIhFZUsoOIpqVUnYA0IioBqAkpexZDwAPgE4A+fXO6lYiOiqlfJqISkS03Gq1/mrbtm0N/APwpmUEKSWNj4+PEdEeIrpLCLGViEhKucgYg5Syb70w60SUXH+6+t8ws3yWiGJSytT6zvYFIopIKcPr379IRDUp5QARXThrXJRSakQUwtoB9CSAwPrwsyaEqK23/SCiuhBilYh63lD9Z9b3TeTXO5SJ9c++MWAKUsoyEQ0AYFLKJmMsv57ORSllaH2v7CQRHZFSqkKIKQCvjI6OPjIxMbETQBjAnBBiM4DpbDb7yt+XPsgveu3pTc6fPx91HGer4ziXrPf4txMRAXhVSrkbwBiABoBDRDQlpYysxZicXu/01QH4pJQ7AVx24ea/IUhniSgFwCulnADQvx5MFoAZKWWSiKJEpAshljjnKSFEUEpZJaI6gAwRrQohCoyxfimlCqBJRAtCiDRj7PR6zQEpZXX9pp8lojCAE5ZlvaRpWreUUpFSnhkdHT0P1y9uu6eUkk9OTm4VQsxls9m/dS5hYmJinxDizvVqvgLgBQBJIuqSUr4upfSuL8L5AbQT0cp68HQS0RYAQSHE6noNASnlJGNsijG223GcywB4iOhFAN+QUp63bfvYpk2b6u4tdrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XC6Xy+VyuVwul8vlcrlcLpfL5XK5XG95/xuL5/e/cxFRuQAAAABJRU5ErkJgglBLAwQKAAAAAAAHi29GAAAAAAAAAAAAAAAACAAAAHNjcmlwdHMvUEsDBAoAAAAIAAeLb0YDt/favwIAAIAGAAAZAAAAc2NyaXB0cy9pbnN0YWxsX2FwYWNoZS5zaJ1UYU/bMBD9nl9xmAqBROoWgTTRtRKiTJrG1qmA9gEh5CaXxqprZ7bTbAL++2wnLQ2DSUyq1Mi+e/fe3fPt7tAZl3TGTB5FmOQKyI3hcg6ssPEcbRc+S2OZEPUZS3I8AiXdD0FlYHOETAmhKn9/CmOccSYP4WZWSlsewlcuLYkuJ+dfhoTaZUGFShYO2iGTaHzx6ezm8vr++2R6PfzQi6Iq5wLB6hIHkKoIgGewXKRcA+k8eJAnAnsjmuKKylKIgS8vXRhAzfws8APLFhiY+WIk3M80ssXAfWY8Wkf/YNx61j4SZerlNKpUqUFjwgs03W7XIxiBWECvexKlLmbN1JSpgqw0qIGumKaCz2haLOZBJTwThaPRXh8aTe3imdKgHAMNRmW2YhphySSbozZgHTiX3ORvkAjl9RLi7JXy9W0zRSiLlFmEx0fYr1H6JwN4IwJ/cQudh/7TQRuD10aA+DfEPzde2E6IPBmdPU+r4UjRJtQpsd2UrvOMVUXk5nsLOxCn0BlPzu+nk8k13G3GiismgASE2gVx8RxHIjfKrQjX1UpCPIWqqmInhZ2uP7ZzQsVb2CcdbzoCw6Eju+1DcgB3WwyaB4GQYsZKYaFQ2oahNV47hXZ6hMLgJnOKhWCJn3PIa4VCxW0Ogcd6vl6HwRRiDjE6XbSVQEMsnZO6n00jqUc23UTJrD0L157mQZ/nTM4RfAife383qcR3IxS1aKy3EWkhG+7OY7ZiXLCZQNr0gLxoTwPvn9FYJeUSpZ0qZbcK+YVRbwT4CP3jbu/4Vbm7rfRgaTfD9ulmlrsv+/AGW9+H98ns9Xpxkxza+g69Z3/pHQ3fLZjmdin+V/VL8sEGobBf8RAnQIKKK9Qr1N/YMmxJJnLlWjMatcGb/7oLazddWeasvJ6sW4TOxRIrqIP+8d5dWgNxUW/bTo9EfwBQSwMECgAAAAgAB4tvRlKBwW9zAAAAsQAAABcAAABzY3JpcHRzL3N0YXJ0X2FwYWNoZS5zaH2LwQ7CIBAF7/sVz9oDHBDjXf+F0kU2aZCwWC9+vCZtPHqaTDJzPPhJip+CZiLltkrk6xBqiJkvA5EkGIPRVIXjhDfujSvcunHXcR+//opwi8UNZ1hLPXMhQJ/zA5579FKkn2b/GxprD60TL8p/wy1LQh9QSwECFAAKAAAACAAHi29Gw0Myi1YCAACvBQAADwAAAAAAAAAAAAAAAAAAAAAAYXBhY2hlLXR5cGUueW1sUEsBAhQACgAAAAAAB4tvRgAAAAAAAAAAAAAAAAcAAAAAAAAAAAAQAAAAgwIAAGltYWdlcy9QSwECFAAKAAAAAAAHi29GXnzyHRYpAAAWKQAAEQAAAAAAAAAAAAAAAACoAgAAaW1hZ2VzL2FwYWNoZS5wbmdQSwECFAAKAAAAAAAHi29GAAAAAAAAAAAAAAAACAAAAAAAAAAAABAAAADtKwAAc2NyaXB0cy9QSwECFAAKAAAACAAHi29GA7f32r8CAACABgAAGQAAAAAAAAAAAAAAAAATLAAAc2NyaXB0cy9pbnN0YWxsX2FwYWNoZS5zaFBLAQIUAAoAAAAIAAeLb0ZSgcFvcwAAALEAAAAXAAAAAAAAAAAAAAAAAAkvAABzY3JpcHRzL3N0YXJ0X2FwYWNoZS5zaFBLBQYAAAAABgAGAHMBAACxLwAAAAA="}
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/command b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/command
new file mode 100644
index 0000000000..c9a5b38dc4
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/command
@@ -0,0 +1,15 @@
+headers=`paste -s -d" " headers`
+
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc1/rest/v1/catalog/resources\" -d \"@body.txt\" ${headers} -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/commandTrial b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/commandTrial
new file mode 100644
index 0000000000..0496ab340d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/commandTrial
@@ -0,0 +1,12 @@
+command="curl -XPOST \"http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources\" -d \"@body.txt\""
+command="$command `paste -s -d\" \" headers`"
+command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/contentMD5.txt
new file mode 100644
index 0000000000..d51f666132
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/contentMD5.txt
@@ -0,0 +1 @@
+YTg2Mjg4MWJhNmI5NzBiNzdmNGFmMTY4OTlhNDFkMWI=
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/headers b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/results b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/toExec b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/toExec
new file mode 100644
index 0000000000..b74e582498
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/11_AlienApacheType/toExec
@@ -0,0 +1 @@
+curl -XPOST "http://172.20.43.122:8080/sdc1/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE" --header "USER_ID: USER_ID" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/body.txt b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/body.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/body.txt
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/command b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/command
new file mode 100644
index 0000000000..96c8a2101c
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/command
@@ -0,0 +1,13 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/tosca.nodes.Database/1.0.0.wd03-SNAPSHOT/artifacts\" -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/commandTrial b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/commandTrial
new file mode 100644
index 0000000000..26c8be9956
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/commandTrial
@@ -0,0 +1,12 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts\" -d \"@body.txt\""
+#command="$command `paste -s -d\" \" headers`"
+#command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/contentMD5.txt
new file mode 100644
index 0000000000..763af5bc63
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/contentMD5.txt
@@ -0,0 +1 @@
+YTg2Mjg4MWJhNmI5NzBiNzdmNGFmMTY4OTlhNDFkMWI= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/headers b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/results b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/results
new file mode 100644
index 0000000000..6bce5fe69a
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/results
@@ -0,0 +1 @@
+.*:204:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/toExec b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/toExec
new file mode 100644
index 0000000000..24ac2f04c7
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/12_DatabaseType-GetArtifacts/toExec
@@ -0,0 +1 @@
+curl "http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/tosca.nodes.Database/1.0.0.wd03-SNAPSHOT/artifacts" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/body.txt b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/body.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/body.txt
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/command b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/command
new file mode 100644
index 0000000000..14783faad6
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/command
@@ -0,0 +1,13 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/install_apache.sh/metadata\" -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/commandTrial b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/commandTrial
new file mode 100644
index 0000000000..26c8be9956
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/commandTrial
@@ -0,0 +1,12 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts\" -d \"@body.txt\""
+#command="$command `paste -s -d\" \" headers`"
+#command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/contentMD5.txt
new file mode 100644
index 0000000000..763af5bc63
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/contentMD5.txt
@@ -0,0 +1 @@
+YTg2Mjg4MWJhNmI5NzBiNzdmNGFmMTY4OTlhNDFkMWI= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/headers b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/results b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/results
new file mode 100644
index 0000000000..da36ee26fc
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/results
@@ -0,0 +1 @@
+.*:200:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/toExec b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/toExec
new file mode 100644
index 0000000000..0dc5652530
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/13_ApacheType-GetMetadata/toExec
@@ -0,0 +1 @@
+curl "http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/install_apache.sh/metadata" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/body.txt b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/body.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/body.txt
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/command b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/command
new file mode 100644
index 0000000000..e18ca5ba6d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/command
@@ -0,0 +1,13 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts\" -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/commandTrial b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/commandTrial
new file mode 100644
index 0000000000..26c8be9956
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/commandTrial
@@ -0,0 +1,12 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts\" -d \"@body.txt\""
+#command="$command `paste -s -d\" \" headers`"
+#command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/contentMD5.txt
new file mode 100644
index 0000000000..763af5bc63
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/contentMD5.txt
@@ -0,0 +1 @@
+YTg2Mjg4MWJhNmI5NzBiNzdmNGFmMTY4OTlhNDFkMWI= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/headers b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results
new file mode 100644
index 0000000000..da36ee26fc
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results
@@ -0,0 +1 @@
+.*:200:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results.json b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results.json
new file mode 100644
index 0000000000..a50cca2af8
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/results.json
@@ -0,0 +1,12 @@
+{
+ "artifacts": [
+ {
+ "name": "start_apache.sh",
+ "url": "http://.*/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/start_apache.sh"
+ },
+ {
+ "name": "install_apache.sh",
+ "url": "http://.*/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/install_apache.sh"
+ }
+ ]
+}
diff --git a/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/toExec b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/toExec
new file mode 100644
index 0000000000..6fdf8907ab
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/14_ApacheType-GetArtifacts/toExec
@@ -0,0 +1 @@
+curl "http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/body.txt b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/body.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/body.txt
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/command b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/command
new file mode 100644
index 0000000000..fc8bcb241f
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/command
@@ -0,0 +1,13 @@
+command="curl -o \"/tmp/start_apache.sh\" \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/start_apache.sh\" -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\" "
+
+echo $command > toExec
+chmod +x toExec
+./toExec
+
+
+#$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/commandTrial b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/commandTrial
new file mode 100644
index 0000000000..26c8be9956
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/commandTrial
@@ -0,0 +1,12 @@
+command="curl \"http://${CATALOG_BE_HOST}:${CATALOG_BE_PORT}/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts\" -d \"@body.txt\""
+#command="$command `paste -s -d\" \" headers`"
+#command="$command -sL -w \":%{http_code}:%{size_request} BYTES:%{time_total} \\n\""
+
+echo $command > 1.log
+
+$command
+
+#curl -XPOST "http://${CATALOG_FE_HOST}:${CATALOG_FE_PORT}/sdc2/rest/v1/catalog/resources" -d "@body.txt" --header "Content-Type: application/json" --header "HTTP_IV_USER: HTTP_IV_USER" --header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME" --header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME" --header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS" --header "Content-MD5: `cat contentMD5.txt`" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \\n"
+
+
+
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/contentMD5.txt b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/contentMD5.txt
new file mode 100644
index 0000000000..763af5bc63
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/contentMD5.txt
@@ -0,0 +1 @@
+YTg2Mjg4MWJhNmI5NzBiNzdmNGFmMTY4OTlhNDFkMWI= \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/headers b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/headers
new file mode 100644
index 0000000000..cd1db0c52d
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/headers
@@ -0,0 +1,8 @@
+--header "Content-Type: application/json"
+--header "HTTP_IV_USER: HTTP_IV_USER"
+--header "HTTP_CSP_FIRSTNAME: HTTP_CSP_FIRSTNAME"
+--header "HTTP_CSP_LASTNAME: HTTP_CSP_LASTNAME"
+--header "HTTP_IV_REMOTE_ADDRESS: HTTP_IV_REMOTE_ADDRESS"
+--header "HTTP_CSP_WSTYPE: HTTP_CSP_WSTYPE"
+--header "USER_ID: USER_ID"
+--header "Content-MD5: `cat contentMD5.txt`"
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/results b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/results
new file mode 100644
index 0000000000..da36ee26fc
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/results
@@ -0,0 +1 @@
+.*:200:.*:.*
diff --git a/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/toExec b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/toExec
new file mode 100644
index 0000000000..91f4750c01
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/15_ApacheType-DownloadArtifact/toExec
@@ -0,0 +1 @@
+curl -o "/tmp/start_apache.sh" "http://172.20.43.124:8080/sdc2/rest/v1/catalog/resources/alien.nodes.Apache/2.0.0-SNAPSHOT/artifacts/start_apache.sh" -sL -w ":%{http_code}:%{size_request} BYTES:%{time_total} \n"
diff --git a/catalog-fe/src/test/resources/CI/tests/env/env.sh b/catalog-fe/src/test/resources/CI/tests/env/env.sh
new file mode 100644
index 0000000000..0a3bc3acc9
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/env/env.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+export CATALOG_FE_HOST=00.000.00.00
+export CATALOG_FE_PORT=0000
+
+export CATALOG_BE_HOST=00.000.00.00
+export CATALOG_BE_PORT=0000
+
+# Example
+#export CATALOG_FE_HOST=10.112.10.22
+#export CATALOG_FE_PORT=8080 \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/CI/tests/runTests.sh b/catalog-fe/src/test/resources/CI/tests/runTests.sh
new file mode 100644
index 0000000000..3fc0d39897
--- /dev/null
+++ b/catalog-fe/src/test/resources/CI/tests/runTests.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+DEBUG=0
+
+function cdAndExit() {
+ cd - >> /dev/null
+ exit $1
+}
+
+function usage() {
+ echo "$0 <envronment file>"
+ exit 3
+}
+
+
+if [ $# -ne 1 ]
+then
+ usage
+fi
+
+envFile=$1
+
+echo $envFile
+
+source $envFile
+
+#source ./envCatalogBE.sh
+
+echo "******************************************"
+echo "********* CATALOG_FE_HOST $CATALOG_FE_HOST "
+echo "********* CATALOG_FE_PORT $CATALOG_FE_PORT "
+echo "********* CATALOG_BE_HOST $CATALOG_BE_HOST "
+echo "********* CATALOG_BE_PORT $CATALOG_BE_PORT "
+echo "******************************************"
+
+if [[ ${DEBUG} -eq 1 ]]
+then
+ read stam
+fi
+
+for folder in *
+do
+
+if [[ -d $folder && $folder != "env" ]]
+then
+
+echo "processing folder" $folder
+cd $folder
+
+commandResult=`./command`
+
+if [ $? -ne 0 ]
+then
+ echo "command $folder failed"
+ cdAndExit 2
+fi
+
+echo "command result is $commandResult"
+results=`cat results`
+
+echo "Going to match $commandResult with pattern $results"
+
+matchNumber=`echo $commandResult | grep -c "$results" `
+echo $matchNumber
+if [ $matchNumber -eq 1 ]
+then
+ echo "command $folder succeed."
+else
+ echo "command $folder failed. Going to exit"
+ cdAndExit 1
+
+fi
+
+echo "Finish processing folder $folder"
+echo "********************************************************"
+
+cd - >> /dev/null
+
+#if folder
+fi
+
+#loop on folder
+done
+
+echo "***************************************"
+echo "* SUCCESS ${envFile} *"
+echo "***************************************"
+
+
+
diff --git a/catalog-fe/src/test/resources/config/catalog-fe/configuration.yaml b/catalog-fe/src/test/resources/config/catalog-fe/configuration.yaml
new file mode 100644
index 0000000000..22bfee90f9
--- /dev/null
+++ b/catalog-fe/src/test/resources/config/catalog-fe/configuration.yaml
@@ -0,0 +1,69 @@
+# Needed for logging purposes. To be populated by DevOps - currently dummy
+feFqdn: asdcFe.att.com
+
+# catalog backend hostname
+beHost: localhost
+
+# catalog backend http port
+beHttpPort: 8181
+
+# catalog backend http context
+beContext: /sdc2/rest/v1/catalog/upload/resources
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8443
+
+# threadpool size for handling requests
+threadpoolSize: 50
+
+# request processing timeout (seconds)
+requestTimeout: 10
+
+# a4c hostname
+a4cHost: localhost
+
+# a4c http port
+a4cHttpPort: 8088
+
+
+identificationHeaderFields:
+ -
+ - &HTTP_IV_USER HTTP_IV_USER
+ - &iv-user iv-user
+ -
+ - &USER_ID USER_ID
+ - &csp-userId csp-userId
+ -
+ - &HTTP_CSP_WSTYPE HTTP_CSP_WSTYPE
+ - &csp-wstype csp-wstype
+
+optionalHeaderFields:
+ -
+ - &HTTP_CSP_FIRSTNAME HTTP_CSP_FIRSTNAME
+ - &csp-firstname csp-firstname
+ -
+ - &HTTP_CSP_LASTNAME HTTP_CSP_LASTNAME
+ - &csp-lastname csp-lastname
+ -
+ - &HTTP_IV_REMOTE_ADDRESS HTTP_IV_REMOTE_ADDRESS
+ - &iv-remote-address iv-remote-address
+
+
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 17
+
+# Protocols
+protocols:
+ - http
+ - https
+
+
+ \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/config/catalog-fe/rest-configuration-info.yaml b/catalog-fe/src/test/resources/config/catalog-fe/rest-configuration-info.yaml
new file mode 100644
index 0000000000..a3559c631f
--- /dev/null
+++ b/catalog-fe/src/test/resources/config/catalog-fe/rest-configuration-info.yaml
@@ -0,0 +1,12 @@
+# rest read timeout - means no timeout
+readTimeoutInSec: 0
+
+# whether to ignore certificate
+ignoreCertificate: false
+
+# the connection pool size
+connectionPoolSize: 10
+
+# create connection timeout
+connectTimeoutInSec: 12
+
diff --git a/catalog-fe/src/test/resources/config/configuration1.yaml b/catalog-fe/src/test/resources/config/configuration1.yaml
new file mode 100644
index 0000000000..12ab2c777f
--- /dev/null
+++ b/catalog-fe/src/test/resources/config/configuration1.yaml
@@ -0,0 +1,17 @@
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 5
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/config/sample.yaml b/catalog-fe/src/test/resources/config/sample.yaml
new file mode 100644
index 0000000000..12ab2c777f
--- /dev/null
+++ b/catalog-fe/src/test/resources/config/sample.yaml
@@ -0,0 +1,17 @@
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 5
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/config/sampleNoProtocol.yaml b/catalog-fe/src/test/resources/config/sampleNoProtocol.yaml
new file mode 100644
index 0000000000..6197232aa4
--- /dev/null
+++ b/catalog-fe/src/test/resources/config/sampleNoProtocol.yaml
@@ -0,0 +1,17 @@
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 5
+
+# Protocols
+#protocols:
+# - http
+# - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd \ No newline at end of file
diff --git a/catalog-fe/src/test/resources/logback-test.xml b/catalog-fe/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..9fc1778eba
--- /dev/null
+++ b/catalog-fe/src/test/resources/logback-test.xml
@@ -0,0 +1,2 @@
+<!-- only one line, shut up logback ! -->
+<configuration /> \ No newline at end of file
diff --git a/catalog-fe/src/test/spec/codeSpec.js b/catalog-fe/src/test/spec/codeSpec.js
new file mode 100644
index 0000000000..1cb5da0b8a
--- /dev/null
+++ b/catalog-fe/src/test/spec/codeSpec.js
@@ -0,0 +1,197 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+describe("General", function() {
+
+
+
+ describe("File Handler", function() {
+
+ var jsHandler = new JsHandler();
+ var fileHandler = jsHandler.getFileHandler();
+ fileHandler.fileContents.fileToUpload = mysqlTypeYml;
+ fileHandler.fileContents.artifactToUpload = installMySqlSH;
+
+
+ describe("checkFile Method", function() {
+
+ it("checkFile Valid", function() {
+ spyOn(window, 'alert');
+
+ affix('#dummyElement').val('stam.zip');
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.chef, 'dummyElement');
+ expect(isValid).toBeTruthy();
+
+
+ $('#dummyElement').val('stam.sh');
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.shell, 'dummyElement');
+ expect(isValid).toBeTruthy();
+
+ $('#dummyElement').val('stam.pp');
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.puppet, 'dummyElement');
+ expect(isValid).toBeTruthy();
+
+ $('#dummyElement').val('stam.yang');
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.yang, 'dummyElement');
+ expect(isValid).toBeTruthy();
+
+
+
+ });
+
+ it("checkFile inValid", function() {
+ spyOn(window, 'alert');
+
+ affix('#dummyElement').val('stam.zip');
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.shell, 'dummyElement');
+ expect(isValid).toBeFalsy();
+
+ $('#dummyElement').val('stam.sh');
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.chef, 'dummyElement');
+ expect(isValid).toBeFalsy();
+
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.puppet, 'dummyElement');
+ expect(isValid).toBeFalsy();
+
+ var isValid = fileHandler.checkFile(fileHandler.filesExtensions.yang, 'dummyElement');
+ expect(isValid).toBeFalsy();
+
+ });
+
+ });
+
+
+
+ describe("getZipName Method", function() {
+ it("getZipName", function() {
+ spyOn(fileHandler, 'getFileName').and.returnValue('mysql-type.yml');
+ expect(fileHandler.getZipName() == 'mysql-type.zip').toBeTruthy();
+ });
+ });
+ /*
+ describe("generateArtifactsList Method", function() {
+ beforeEach(function() {
+ jsHandler.getDomHandler().exisitngArifactsCounter = 2;
+ });
+
+ it("generateArtifactsList", function() {
+ spyOn(fileHandler, 'getFileName').and.returnValue('mysql-type.yml');
+ var artifactList = fileHandler.generateArtifactsList();
+// expect(fileHandler.generateArtifactsList() == 'mysql-type.zip').toBeTruthy();
+ });
+ });*/
+
+ describe("getArtifactsListFromYml Method", function() {
+ it("getArtifactsListFromYml", function() {
+ var expectedArtifacts = fileHandler.getArtifactsListFromYml();
+ expect(expectedArtifacts.length == 3).toBeTruthy();
+ expect($.inArray('scripts/install_mysql.sh', expectedArtifacts) != -1).toBeTruthy();
+ expect($.inArray('scripts/start_mysql.sh', expectedArtifacts) != -1).toBeTruthy();
+ expect($.inArray('images/mysql.png', expectedArtifacts) != -1).toBeTruthy();
+ });
+ });
+
+ describe("uploadFileJson Method", function() {
+ var sentJson, sendEncodedMd5;
+
+ beforeEach(function() {
+ sentJson = '';
+ sendEncodedMd5 = '';
+ spyOn(fileHandler, 'sendAjaxToServer').and.callFake(function(strifiedJson, encodedMd5) {
+ sentJson = JSON.parse(strifiedJson);
+ sendEncodedMd5 = encodedMd5;
+
+ });
+ spyOn(fileHandler, 'getZipName').and.callFake(function() { return 'fakeZipName.zip' });
+ spyOn(fileHandler, 'generateArtifactsList').and.callFake(function() { return 'fakeArtifactsList' });
+ spyOn(fileHandler, 'generateZip').and.callFake(function() { return 'fakeEncodedZipData' });
+ });
+
+ it("sendAjaxToServer method is called", function() {
+ fileHandler.uploadFileJson();
+ expect(fileHandler.sendAjaxToServer).toHaveBeenCalled();
+ });
+
+ it("json contains relevant fields", function() {
+ fileHandler.uploadFileJson();
+
+ expect(sentJson.payloadName == 'fakeZipName.zip').toBeTruthy();
+ expect(sentJson.isEncoded).toBeTruthy();
+ expect(sentJson.isCompressed).toBeTruthy();
+ expect(sentJson.artifactList == 'fakeArtifactsList').toBeTruthy();
+ expect(sentJson.payloadData == 'fakeEncodedZipData').toBeTruthy();
+
+
+
+ });
+
+ it("md5 is validated", function() {
+ fileHandler.uploadFileJson();
+
+ var strifiedJson = JSON.stringify(sentJson);
+ var md5 = CryptoJS.MD5(strifiedJson);
+ var encodedMd5 = btoa(md5);
+ expect(encodedMd5 == sendEncodedMd5).toBeTruthy();
+
+ });
+ });
+
+ describe("generateZip Method", function() {
+
+ it("generateZip", function() {
+
+
+ spyOn(fileHandler, 'getFileName').and.callFake(function(controName) {
+ var fileName;
+ if( controName == 'fileToUpload'){
+ fileName = 'mysql-type.yml';
+ }
+ else if( controName == 'imageToUpload'){
+ fileName = 'root.png';
+ }
+ else if( controName == 'artifactToUpload'){
+ fileName = 'install_mysql.sh';
+ }
+ else{
+ fileName = controName+'.sh';
+ }
+ return fileName;
+ });
+
+ var encodedZip = fileHandler.generateZip();
+ var decodedZip = atob(encodedZip);
+ var myZip = new JSZip(decodedZip);
+
+ var componentContentInZip = myZip.file('mysql-type.yml').asText();
+ expect(componentContentInZip == mysqlTypeYml).toBeTruthy();
+
+ var artifactContentInZip = myZip.folder('scripts').file('install_mysql.sh').asText();
+ expect(artifactContentInZip == installMySqlSH).toBeTruthy();
+
+
+
+ });
+ });
+ });
+
+
+});
+
+
diff --git a/catalog-fe/src/test/testScripts/filesContents.js b/catalog-fe/src/test/testScripts/filesContents.js
new file mode 100644
index 0000000000..b2e7aa2c65
--- /dev/null
+++ b/catalog-fe/src/test/testScripts/filesContents.js
@@ -0,0 +1,207 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var normativeTypesRootYml = "tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03"+
+
+"template_name: tosca-normative-types-root"+
+"template_author: TOSCA TC"+
+"template_version: 1.0.0.wd03-SNAPSHOT"+
+"description: Contains the normative types definition."+
+"node_types:"+
+" tosca.nodes.Root:"+
+" abstract: true"+
+" description: >"+
+" This is the default (root) TOSCA Node Type that all other TOSCA nodes should extends."+
+" This allows all TOSCA nodes to have a consistent set of features for modeling and management"+
+" (e.g, consistent definitions for requirements, capabilities, and lifecycle interfaces)."+
+" tags:"+
+" icon: /images/root.png"+
+" attributes:"+
+" tosca_id:"+
+" type: string"+
+" tosca_name:"+
+" type: string"+
+" requirements:"+
+" dependency:"+
+" type: tosca.capabilities.Root"+
+" lower_bound: 0"+
+" upper_bound: unbounded"+
+" capabilities:"+
+" root:"+
+" type: tosca.capabilities.Root"+
+" interfaces:"+
+" tosca.interfaces.node.lifecycle.Standard:"+
+" description: >"+
+" This lifecycle interface defines the essential, normative operations that TOSCA nodes may support."+
+" create:"+
+" description: Standard lifecycle create operation."+
+" configure:"+
+" description: Standard lifecycle configure operation (pre-start)."+
+" start:"+
+" description: Standard lifecycle start operation."+
+" post_start:"+
+" description: Standard lifecycle post-configure operation (post-start)"+
+" stop:"+
+" description: Standard lifecycle stop operation."+
+" delete:"+
+" description: Standard lifecycle delete operation."+
+"capability_types:"+
+" tosca.capabilities.Root:"+
+" description: This is the default (root) TOSCA Capability Type definition that all other TOSCA Capability Types derive from."+
+"relationship_types:"+
+" tosca.relationships.Root:"+
+" abstract: true"+
+" description: This is the default (root) TOSCA Relationship Type definition that all other TOSCA Relationship Types derive from."+
+" valid_targets: [ tosca.capabilities.Root ]"+
+" attributes:"+
+" tosca_id:"+
+" type: string"+
+" tosca_name:"+
+" type: string"+
+" interfaces:"+
+" tosca.interfaces.relationship.Configure:"+
+" description: >"+
+" The lifecycle interfaces define the essential, normative operations that each TOSCA Relationship Types may support."+
+" pre_configure_source:"+
+" description: Operation to pre-configure the source endpoint."+
+" pre_configure_target:"+
+" description: Operation to pre-configure the target endpoint."+
+" post_configure_source:"+
+" description: Operation to post-configure the source endpoint."+
+" post_configure_target:"+
+" description: Operation to post-configure the target endpoint."+
+" add_target:"+
+" description: Operation to notify the source node of a target node being added via a relationship."+
+" add_source:"+
+" description: Operation to notify the target node of a source node which is now available via a relationship."+
+" remove_target:"+
+" description: Operation to notify the source node of a target node being removed from a relationship."+
+" remove_source:"+
+" description: Operation to notify the target node of a source node being removed from a relationship."+
+" target_changed:"+
+" description: Operation to notify source some property or attribute of the target."+
+" source_changed:"+
+" description: Operation to notify target some property or attribute of the source."+
+"artifact_types:"+
+" tosca.artifacts.Root:"+
+" description: The TOSCA Artifact Type all other TOSCA Artifact Types derive from.";
+
+
+var mysqlTypeYml =
+"tosca_definitions_version: tosca_simple_yaml_1_0_0_wd03"+
+"description: MySQL RDBMS installation on a specific mounted volume path."+
+"template_name: mysql-type"+
+"template_version: 2.0.0-SNAPSHOT"+
+"template_author: FastConnect"+
+
+"imports:"+
+' - "tosca-normative-types:1.0.0.wd03-SNAPSHOT"'+
+
+"node_types:"+
+" alien.nodes.Mysql:"+
+" derived_from: tosca.nodes.Database"+
+" description: >"+
+" A node to install MySQL v5.5 database with data"+
+" on a specific attached volume."+
+" capabilities:"+
+" host:"+
+" type: alien.capabilities.MysqlDatabase"+
+" properties:"+
+" valid_node_types: [ tosca.nodes.WebApplication ]"+
+" requirements:"+
+" - host: tosca.nodes.Compute"+
+" type: tosca.relationships.HostedOn"+
+" tags:"+
+" icon: /images/mysql.png"+
+" properties:"+
+" db_port:"+
+" type: integer"+
+" default: 3306"+
+" description: The port on which the underlying database service will listen to data."+
+" db_name:"+
+" type: string"+
+" required: true"+
+" default: wordpress"+
+" description: The logical name of the database."+
+" db_user:"+
+" type: string"+
+" default: pass"+
+" description: The special user account used for database administration."+
+" db_password:"+
+" type: string"+
+" default: pass"+
+" description: The password associated with the user account provided in the ‘db_user’ property."+
+" bind_address:"+
+" type: boolean"+
+" default: true"+
+" required: false"+
+" description: If true,the server accepts TCP/IP connections on all server host IPv4 interfaces."+
+" storage_path:"+
+" type: string"+
+" default: /mountedStorage"+
+" constraints:"+
+' - valid_values: [ "/mountedStorage", "/var/mysql" ]'+
+" interfaces:"+
+" Standard:"+
+" create: scripts/install_mysql.sh"+
+" start:"+
+" inputs:"+
+" VOLUME_HOME: { get_property: [SELF, storage_path] }"+
+" PORT: { get_property: [SELF, db_port] }"+
+" DB_NAME: { get_property: [SELF, db_name] }"+
+" DB_USER: { get_property: [SELF, db_user] }"+
+" DB_PASSWORD: { get_property: [SELF, db_password] }"+
+" BIND_ADRESS: { get_property: [SELF, bind_address] }"+
+" implementation: scripts/start_mysql.sh"+
+" "+
+"capability_types:"+
+" alien.capabilities.MysqlDatabase:"+
+" derived_from: tosca.capabilities.Container";
+
+
+var installMySqlSH =
+'#!/bin/bash'+
+
+'echo "Debian based MYSQL install 5..."'+
+'LOCK="/tmp/lockaptget"'+
+
+'while true; do'+
+' if mkdir "${LOCK}" &>/dev/null; then'+
+' echo "MySQL take the lock"'+
+' break;'+
+' fi'+
+' echo "Waiting the end of one of our recipes..."'+
+' sleep 0.5'+
+'done'+
+
+'while sudo fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do'+
+' echo "Waiting for other software managers to finish..."'+
+' sleep 0.5'+
+'done'+
+'sudo rm -f /var/lib/dpkg/lock'+
+
+'sudo apt-get update || (sleep 15; sudo apt-get update || exit ${1})'+
+'sudo DEBIAN_FRONTEND=noninteractive apt-get -y install mysql-server-5.5 pwgen || exit ${1}'+
+'rm -rf "${LOCK}"'+
+
+'sudo /etc/init.d/mysql stop'+
+'sudo rm -rf /var/lib/apt/lists/*'+
+'sudo rm -rf /var/lib/mysql/*'+
+'echo "MySQL Installation complete."';
diff --git a/catalog-fe/src/test/testScripts/jasmine-fixture.js b/catalog-fe/src/test/testScripts/jasmine-fixture.js
new file mode 100644
index 0000000000..18ff8c6418
--- /dev/null
+++ b/catalog-fe/src/test/testScripts/jasmine-fixture.js
@@ -0,0 +1,453 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/* jasmine-fixture - 1.2.2
+ * Makes injecting HTML snippets into the DOM easy & clean!
+ * https://github.com/searls/jasmine-fixture
+ */
+(function() {
+ var createHTMLBlock,
+ __slice = [].slice;
+
+ (function($) {
+ var ewwSideEffects, jasmineFixture, originalAffix, originalJasmineDotFixture, originalJasmineFixture, root, _, _ref;
+ root = this;
+ originalJasmineFixture = root.jasmineFixture;
+ originalJasmineDotFixture = (_ref = root.jasmine) != null ? _ref.fixture : void 0;
+ originalAffix = root.affix;
+ _ = function(list) {
+ return {
+ inject: function(iterator, memo) {
+ var item, _i, _len, _results;
+ _results = [];
+ for (_i = 0, _len = list.length; _i < _len; _i++) {
+ item = list[_i];
+ _results.push(memo = iterator(memo, item));
+ }
+ return _results;
+ }
+ };
+ };
+ root.jasmineFixture = function($) {
+ var $whatsTheRootOf, affix, create, jasmineFixture, noConflict;
+ affix = function(selectorOptions) {
+ return create.call(this, selectorOptions, true);
+ };
+ create = function(selectorOptions, attach) {
+ var $top;
+ $top = null;
+ _(selectorOptions.split(/[ ](?=[^\]]*?(?:\[|$))/)).inject(function($parent, elementSelector) {
+ var $el;
+ if (elementSelector === ">") {
+ return $parent;
+ }
+ $el = createHTMLBlock($, elementSelector);
+ if (attach || $top) {
+ $el.appendTo($parent);
+ }
+ $top || ($top = $el);
+ return $el;
+ }, $whatsTheRootOf(this));
+ return $top;
+ };
+ noConflict = function() {
+ var currentJasmineFixture, _ref1;
+ currentJasmineFixture = jasmine.fixture;
+ root.jasmineFixture = originalJasmineFixture;
+ if ((_ref1 = root.jasmine) != null) {
+ _ref1.fixture = originalJasmineDotFixture;
+ }
+ root.affix = originalAffix;
+ return currentJasmineFixture;
+ };
+ $whatsTheRootOf = function(that) {
+ if (that.jquery != null) {
+ return that;
+ } else if ($('#jasmine_content').length > 0) {
+ return $('#jasmine_content');
+ } else {
+ return $('<div id="jasmine_content"></div>').appendTo('body');
+ }
+ };
+ jasmineFixture = {
+ affix: affix,
+ create: create,
+ noConflict: noConflict
+ };
+ ewwSideEffects(jasmineFixture);
+ return jasmineFixture;
+ };
+ ewwSideEffects = function(jasmineFixture) {
+ var _ref1;
+ if ((_ref1 = root.jasmine) != null) {
+ _ref1.fixture = jasmineFixture;
+ }
+ $.fn.affix = root.affix = jasmineFixture.affix;
+ return afterEach(function() {
+ return $('#jasmine_content').remove();
+ });
+ };
+ if ($) {
+ return jasmineFixture = root.jasmineFixture($);
+ } else {
+ return root.affix = function() {
+ var nowJQueryExists;
+ nowJQueryExists = window.jQuery || window.$;
+ if (nowJQueryExists != null) {
+ jasmineFixture = root.jasmineFixture(nowJQueryExists);
+ return affix.call.apply(affix, [this].concat(__slice.call(arguments)));
+ } else {
+ throw new Error("jasmine-fixture requires jQuery to be defined at window.jQuery or window.$");
+ }
+ };
+ }
+ })(window.jQuery || window.$);
+
+ createHTMLBlock = (function() {
+ var bindData, bindEvents, parseAttributes, parseClasses, parseContents, parseEnclosure, parseReferences, parseVariableScope, regAttr, regAttrDfn, regAttrs, regCBrace, regClass, regClasses, regData, regDatas, regEvent, regEvents, regExclamation, regId, regReference, regTag, regTagNotContent, regZenTagDfn;
+ createHTMLBlock = function($, ZenObject, data, functions, indexes) {
+ var ZenCode, arr, block, blockAttrs, blockClasses, blockHTML, blockId, blockTag, blocks, el, el2, els, forScope, indexName, inner, len, obj, origZenCode, paren, result, ret, zc, zo;
+ if ($.isPlainObject(ZenObject)) {
+ ZenCode = ZenObject.main;
+ } else {
+ ZenCode = ZenObject;
+ ZenObject = {
+ main: ZenCode
+ };
+ }
+ origZenCode = ZenCode;
+ if (indexes === undefined) {
+ indexes = {};
+ }
+ if (ZenCode.charAt(0) === "!" || $.isArray(data)) {
+ if ($.isArray(data)) {
+ forScope = ZenCode;
+ } else {
+ obj = parseEnclosure(ZenCode, "!");
+ obj = obj.substring(obj.indexOf(":") + 1, obj.length - 1);
+ forScope = parseVariableScope(ZenCode);
+ }
+ while (forScope.charAt(0) === "@") {
+ forScope = parseVariableScope("!for:!" + parseReferences(forScope, ZenObject));
+ }
+ zo = ZenObject;
+ zo.main = forScope;
+ el = $();
+ if (ZenCode.substring(0, 5) === "!for:" || $.isArray(data)) {
+ if (!$.isArray(data) && obj.indexOf(":") > 0) {
+ indexName = obj.substring(0, obj.indexOf(":"));
+ obj = obj.substr(obj.indexOf(":") + 1);
+ }
+ arr = ($.isArray(data) ? data : data[obj]);
+ zc = zo.main;
+ if ($.isArray(arr) || $.isPlainObject(arr)) {
+ $.map(arr, function(value, index) {
+ var next;
+ zo.main = zc;
+ if (indexName !== undefined) {
+ indexes[indexName] = index;
+ }
+ if (!$.isPlainObject(value)) {
+ value = {
+ value: value
+ };
+ }
+ next = createHTMLBlock($, zo, value, functions, indexes);
+ if (el.length !== 0) {
+ return $.each(next, function(index, value) {
+ return el.push(value);
+ });
+ }
+ });
+ }
+ if (!$.isArray(data)) {
+ ZenCode = ZenCode.substr(obj.length + 6 + forScope.length);
+ } else {
+ ZenCode = "";
+ }
+ } else if (ZenCode.substring(0, 4) === "!if:") {
+ result = parseContents("!" + obj + "!", data, indexes);
+ if (result !== "undefined" || result !== "false" || result !== "") {
+ el = createHTMLBlock($, zo, data, functions, indexes);
+ }
+ ZenCode = ZenCode.substr(obj.length + 5 + forScope.length);
+ }
+ ZenObject.main = ZenCode;
+ } else if (ZenCode.charAt(0) === "(") {
+ paren = parseEnclosure(ZenCode, "(", ")");
+ inner = paren.substring(1, paren.length - 1);
+ ZenCode = ZenCode.substr(paren.length);
+ zo = ZenObject;
+ zo.main = inner;
+ el = createHTMLBlock($, zo, data, functions, indexes);
+ } else {
+ blocks = ZenCode.match(regZenTagDfn);
+ block = blocks[0];
+ if (block.length === 0) {
+ return "";
+ }
+ if (block.indexOf("@") >= 0) {
+ ZenCode = parseReferences(ZenCode, ZenObject);
+ zo = ZenObject;
+ zo.main = ZenCode;
+ return createHTMLBlock($, zo, data, functions, indexes);
+ }
+ block = parseContents(block, data, indexes);
+ blockClasses = parseClasses($, block);
+ if (regId.test(block)) {
+ blockId = regId.exec(block)[1];
+ }
+ blockAttrs = parseAttributes(block, data);
+ blockTag = (block.charAt(0) === "{" ? "span" : "div");
+ if (ZenCode.charAt(0) !== "#" && ZenCode.charAt(0) !== "." && ZenCode.charAt(0) !== "{") {
+ blockTag = regTag.exec(block)[1];
+ }
+ if (block.search(regCBrace) !== -1) {
+ blockHTML = block.match(regCBrace)[1];
+ }
+ blockAttrs = $.extend(blockAttrs, {
+ id: blockId,
+ "class": blockClasses,
+ html: blockHTML
+ });
+ el = $("<" + blockTag + ">", blockAttrs);
+ el.attr(blockAttrs);
+ el = bindEvents(block, el, functions);
+ el = bindData(block, el, data);
+ ZenCode = ZenCode.substr(blocks[0].length);
+ ZenObject.main = ZenCode;
+ }
+ if (ZenCode.length > 0) {
+ if (ZenCode.charAt(0) === ">") {
+ if (ZenCode.charAt(1) === "(") {
+ zc = parseEnclosure(ZenCode.substr(1), "(", ")");
+ ZenCode = ZenCode.substr(zc.length + 1);
+ } else if (ZenCode.charAt(1) === "!") {
+ obj = parseEnclosure(ZenCode.substr(1), "!");
+ forScope = parseVariableScope(ZenCode.substr(1));
+ zc = obj + forScope;
+ ZenCode = ZenCode.substr(zc.length + 1);
+ } else {
+ len = Math.max(ZenCode.indexOf("+"), ZenCode.length);
+ zc = ZenCode.substring(1, len);
+ ZenCode = ZenCode.substr(len);
+ }
+ zo = ZenObject;
+ zo.main = zc;
+ els = $(createHTMLBlock($, zo, data, functions, indexes));
+ els.appendTo(el);
+ }
+ if (ZenCode.charAt(0) === "+") {
+ zo = ZenObject;
+ zo.main = ZenCode.substr(1);
+ el2 = createHTMLBlock($, zo, data, functions, indexes);
+ $.each(el2, function(index, value) {
+ return el.push(value);
+ });
+ }
+ }
+ ret = el;
+ return ret;
+ };
+ bindData = function(ZenCode, el, data) {
+ var datas, i, split;
+ if (ZenCode.search(regDatas) === 0) {
+ return el;
+ }
+ datas = ZenCode.match(regDatas);
+ if (datas === null) {
+ return el;
+ }
+ i = 0;
+ while (i < datas.length) {
+ split = regData.exec(datas[i]);
+ if (split[3] === undefined) {
+ $(el).data(split[1], data[split[1]]);
+ } else {
+ $(el).data(split[1], data[split[3]]);
+ }
+ i++;
+ }
+ return el;
+ };
+ bindEvents = function(ZenCode, el, functions) {
+ var bindings, fn, i, split;
+ if (ZenCode.search(regEvents) === 0) {
+ return el;
+ }
+ bindings = ZenCode.match(regEvents);
+ if (bindings === null) {
+ return el;
+ }
+ i = 0;
+ while (i < bindings.length) {
+ split = regEvent.exec(bindings[i]);
+ if (split[2] === undefined) {
+ fn = functions[split[1]];
+ } else {
+ fn = functions[split[2]];
+ }
+ $(el).bind(split[1], fn);
+ i++;
+ }
+ return el;
+ };
+ parseAttributes = function(ZenBlock, data) {
+ var attrStrs, attrs, i, parts;
+ if (ZenBlock.search(regAttrDfn) === -1) {
+ return undefined;
+ }
+ attrStrs = ZenBlock.match(regAttrDfn);
+ attrs = {};
+ i = 0;
+ while (i < attrStrs.length) {
+ parts = regAttr.exec(attrStrs[i]);
+ attrs[parts[1]] = "";
+ if (parts[3] !== undefined) {
+ attrs[parts[1]] = parseContents(parts[3], data);
+ }
+ i++;
+ }
+ return attrs;
+ };
+ parseClasses = function($, ZenBlock) {
+ var classes, clsString, i;
+ ZenBlock = ZenBlock.match(regTagNotContent)[0];
+ if (ZenBlock.search(regClasses) === -1) {
+ return undefined;
+ }
+ classes = ZenBlock.match(regClasses);
+ clsString = "";
+ i = 0;
+ while (i < classes.length) {
+ clsString += " " + regClass.exec(classes[i])[1];
+ i++;
+ }
+ return $.trim(clsString);
+ };
+ parseContents = function(ZenBlock, data, indexes) {
+ var html;
+ if (indexes === undefined) {
+ indexes = {};
+ }
+ html = ZenBlock;
+ if (data === undefined) {
+ return html;
+ }
+ while (regExclamation.test(html)) {
+ html = html.replace(regExclamation, function(str, str2) {
+ var begChar, fn, val;
+ begChar = "";
+ if (str.indexOf("!for:") > 0 || str.indexOf("!if:") > 0) {
+ return str;
+ }
+ if (str.charAt(0) !== "!") {
+ begChar = str.charAt(0);
+ str = str.substring(2, str.length - 1);
+ }
+ fn = new Function("data", "indexes", "var r=undefined;" + "with(data){try{r=" + str + ";}catch(e){}}" + "with(indexes){try{if(r===undefined)r=" + str + ";}catch(e){}}" + "return r;");
+ val = unescape(fn(data, indexes));
+ return begChar + val;
+ });
+ }
+ html = html.replace(/\\./g, function(str) {
+ return str.charAt(1);
+ });
+ return unescape(html);
+ };
+ parseEnclosure = function(ZenCode, open, close, count) {
+ var index, ret;
+ if (close === undefined) {
+ close = open;
+ }
+ index = 1;
+ if (count === undefined) {
+ count = (ZenCode.charAt(0) === open ? 1 : 0);
+ }
+ if (count === 0) {
+ return;
+ }
+ while (count > 0 && index < ZenCode.length) {
+ if (ZenCode.charAt(index) === close && ZenCode.charAt(index - 1) !== "\\") {
+ count--;
+ } else {
+ if (ZenCode.charAt(index) === open && ZenCode.charAt(index - 1) !== "\\") {
+ count++;
+ }
+ }
+ index++;
+ }
+ ret = ZenCode.substring(0, index);
+ return ret;
+ };
+ parseReferences = function(ZenCode, ZenObject) {
+ ZenCode = ZenCode.replace(regReference, function(str) {
+ var fn;
+ str = str.substr(1);
+ fn = new Function("objs", "var r=\"\";" + "with(objs){try{" + "r=" + str + ";" + "}catch(e){}}" + "return r;");
+ return fn(ZenObject, parseReferences);
+ });
+ return ZenCode;
+ };
+ parseVariableScope = function(ZenCode) {
+ var forCode, rest, tag;
+ if (ZenCode.substring(0, 5) !== "!for:" && ZenCode.substring(0, 4) !== "!if:") {
+ return undefined;
+ }
+ forCode = parseEnclosure(ZenCode, "!");
+ ZenCode = ZenCode.substr(forCode.length);
+ if (ZenCode.charAt(0) === "(") {
+ return parseEnclosure(ZenCode, "(", ")");
+ }
+ tag = ZenCode.match(regZenTagDfn)[0];
+ ZenCode = ZenCode.substr(tag.length);
+ if (ZenCode.length === 0 || ZenCode.charAt(0) === "+") {
+ return tag;
+ } else if (ZenCode.charAt(0) === ">") {
+ rest = "";
+ rest = parseEnclosure(ZenCode.substr(1), "(", ")", 1);
+ return tag + ">" + rest;
+ }
+ return undefined;
+ };
+ regZenTagDfn = /([#\.\@]?[\w-]+|\[([\w-!?=:"']+(="([^"]|\\")+")? {0,})+\]|\~[\w$]+=[\w$]+|&[\w$]+(=[\w$]+)?|[#\.\@]?!([^!]|\\!)+!){0,}(\{([^\}]|\\\})+\})?/i;
+ regTag = /(\w+)/i;
+ regId = /(?:^|\b)#([\w-!]+)/i;
+ regTagNotContent = /((([#\.]?[\w-]+)?(\[([\w!]+(="([^"]|\\")+")? {0,})+\])?)+)/i;
+ /*
+ See lookahead syntax (?!) at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
+ */
+
+ regClasses = /(\.[\w-]+)(?!["\w])/g;
+ regClass = /\.([\w-]+)/i;
+ regReference = /(@[\w$_][\w$_\d]+)/i;
+ regAttrDfn = /(\[([\w-!]+(="?([^"]|\\")+"?)? {0,})+\])/ig;
+ regAttrs = /([\w-!]+(="([^"]|\\")+")?)/g;
+ regAttr = /([\w-!]+)(="?((([\w]+(\[.*?\])+)|[^"\]]|\\")+)"?)?/i;
+ regCBrace = /\{(([^\}]|\\\})+)\}/i;
+ regExclamation = /(?:([^\\]|^))!([^!]|\\!)+!/g;
+ regEvents = /\~[\w$]+(=[\w$]+)?/g;
+ regEvent = /\~([\w$]+)=([\w$]+)/i;
+ regDatas = /&[\w$]+(=[\w$]+)?/g;
+ regData = /&([\w$]+)(=([\w$]+))?/i;
+ return createHTMLBlock;
+ })();
+
+}).call(this);
diff --git a/catalog-fe/tarball.xml b/catalog-fe/tarball.xml
new file mode 100644
index 0000000000..87b944bd4e
--- /dev/null
+++ b/catalog-fe/tarball.xml
@@ -0,0 +1,32 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar</format>
+ </formats>
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.artifactId}-${project.version}.war</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>${project.artifactId}-${project.version}.war</destName>
+ </file>
+ <file>
+ <source>src/main/resources/jetty-ipaccess.xml</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>jetty-ipaccess.xml</destName>
+ </file>
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <directory>src/main/resources/config</directory>
+ <outputDirectory>config</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/scripts</directory>
+ <outputDirectory>scripts</outputDirectory>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/catalog-model/.gitignore b/catalog-model/.gitignore
new file mode 100644
index 0000000000..d9d66d8144
--- /dev/null
+++ b/catalog-model/.gitignore
@@ -0,0 +1,2 @@
+/target
+/bin/
diff --git a/catalog-model/pom.xml b/catalog-model/pom.xml
new file mode 100644
index 0000000000..32116b2370
--- /dev/null
+++ b/catalog-model/pom.xml
@@ -0,0 +1,267 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-model</artifactId>
+
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+
+ <dependencies>
+
+ <!-- Common of SD&C -->
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>common-be</artifactId>
+ <version>${common-be.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- catalog dao -->
+ <dependency>
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>catalog-dao</artifactId>
+ <version>${catalog-dao.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- -->
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version><!--$NO-MVN-MAN-VER$-->
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- spring -->
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>1.1.0.Final</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <version>5.3.4.Final</version>
+ </dependency>
+
+ <!-- Gson -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ </exclusion>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <version>${titan.version}</version>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>slf4j-log4j12</artifactId>
+ <groupId>org.slf4j</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <!-- TITAN END-->
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- CASSANDRA -->
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-core</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-mapping</artifactId>
+ <version>${cassandra.driver.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- CASSANDRA END -->
+
+ <!-- test -->
+
+ <!--dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>test</scope>
+ </dependency-->
+
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <version>1.7.4</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ <version>1.7.4</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ <scope>test</scope>
+ <version>4.0.7.RELEASE</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>Fortify</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.fortify.ps.maven.plugin</groupId>
+ <artifactId>sca-maven-plugin</artifactId>
+ <version>4.30</version>
+ <configuration>
+ <source>1.8</source>
+ <buildId>${project.parent.artifactId}</buildId>
+ <toplevelArtifactId>${project.parent.artifactId}</toplevelArtifactId>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInfoParameterInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInfoParameterInfo.java
new file mode 100644
index 0000000000..674681081c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInfoParameterInfo.java
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+public class AdditionalInfoParameterInfo implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2066876282722907709L;
+
+ String uniqueId;
+ String key;
+ String value;
+
+ public AdditionalInfoParameterInfo() {
+ super();
+ }
+
+ public AdditionalInfoParameterInfo(String key, String value) {
+ super();
+ this.key = key;
+ this.value = value;
+ }
+
+ public AdditionalInfoParameterInfo(String uniqueId, String key, String value) {
+ super();
+ this.uniqueId = uniqueId;
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return "AdditionalInfoParameterInfo [uniqueId=" + uniqueId + ", key=" + key + ", value=" + value + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInformationDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInformationDefinition.java
new file mode 100644
index 0000000000..9ad0718e71
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/AdditionalInformationDefinition.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.AdditionalInfoParameterDataDefinition;
+
+public class AdditionalInformationDefinition extends AdditionalInfoParameterDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 5266684455492488001L;
+
+ private String parentUniqueId;
+
+ private List<AdditionalInfoParameterInfo> parameters;
+
+ public AdditionalInformationDefinition() {
+ super();
+ }
+
+ public AdditionalInformationDefinition(AdditionalInfoParameterDataDefinition p, String parentUniqueId,
+ List<AdditionalInfoParameterInfo> parameters) {
+ super(p);
+ this.parentUniqueId = parentUniqueId;
+ this.parameters = parameters;
+ }
+
+ public AdditionalInformationDefinition(AdditionalInformationDefinition pd) {
+ this.setUniqueId(pd.getUniqueId());
+ this.setCreationTime(pd.getCreationTime());
+ this.setModificationTime(pd.getModificationTime());
+ this.parentUniqueId = pd.parentUniqueId;
+ this.parameters = pd.parameters;
+ }
+
+ public String getParentUniqueId() {
+ return parentUniqueId;
+ }
+
+ public void setParentUniqueId(String parentUniqueId) {
+ this.parentUniqueId = parentUniqueId;
+ }
+
+ public List<AdditionalInfoParameterInfo> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List<AdditionalInfoParameterInfo> parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public String toString() {
+ return "AdditionalInformationDefinition [parameters=" + parameters + ", parentUniqueId=" + parentUniqueId + " "
+ + super.toString() + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactDefinition.java
new file mode 100644
index 0000000000..f822e67715
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactDefinition.java
@@ -0,0 +1,138 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+
+public class ArtifactDefinition extends ArtifactDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8323923665449071631L;
+
+ /**
+ * Base64 encoded Artifact file data
+ */
+ private byte[] payloadData;
+
+ private List<HeatParameterDefinition> heatParameters;
+
+ private String generatedFromId;
+
+ public byte[] getPayloadData() {
+ return payloadData;
+ }
+
+ public void setPayload(byte[] payloadData) {
+ this.payloadData = payloadData;
+ }
+
+ public void setPayloadData(String payloadData) {
+ if (payloadData != null) {
+ this.payloadData = payloadData.getBytes();
+ }
+ }
+
+ public ArtifactDefinition() {
+ super();
+ }
+
+ public ArtifactDefinition(ArtifactDataDefinition a) {
+ super(a);
+
+ }
+
+ public ArtifactDefinition(ArtifactDataDefinition a, String payloadData) {
+ super(a);
+ setPayloadData(payloadData);
+ }
+
+ public List<HeatParameterDefinition> getHeatParameters() {
+ return heatParameters;
+ }
+
+ public void setHeatParameters(List<HeatParameterDefinition> properties) {
+ this.heatParameters = properties;
+ }
+
+ public String getGeneratedFromId() {
+ return generatedFromId;
+ }
+
+ public void setGeneratedFromId(String generatedFromId) {
+ this.generatedFromId = generatedFromId;
+ }
+
+ public boolean checkEsIdExist() {
+ if ((getEsId() != null) && (!getEsId().trim().isEmpty())) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((generatedFromId == null) ? 0 : generatedFromId.hashCode());
+ result = prime * result + ((heatParameters == null) ? 0 : heatParameters.hashCode());
+ result = prime * result + ((payloadData == null) ? 0 : payloadData.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ArtifactDefinition other = (ArtifactDefinition) obj;
+ if (generatedFromId == null) {
+ if (other.generatedFromId != null)
+ return false;
+ } else if (!generatedFromId.equals(other.generatedFromId))
+ return false;
+ if (heatParameters == null) {
+ if (other.heatParameters != null)
+ return false;
+ } else if (heatParameters.size() != other.heatParameters.size())
+ return false;
+ else {
+ for (HeatParameterDefinition heatParam : heatParameters) {
+ if (!other.heatParameters.contains(heatParam)) {
+ return false;
+ }
+ }
+ }
+ if (payloadData == null) {
+ if (other.payloadData != null)
+ return false;
+ } else if (!payloadData.equals(other.payloadData))
+ return false;
+ return true;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactType.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactType.java
new file mode 100644
index 0000000000..a761d74016
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactType.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class ArtifactType {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ArtifactType other = (ArtifactType) obj;
+ if (name == null) {
+ if (other.getClass() != null) {
+ return false;
+ }
+ } else if (!name.equals(other.getName())) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactUiDownloadData.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactUiDownloadData.java
new file mode 100644
index 0000000000..7172ddc35d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ArtifactUiDownloadData.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class ArtifactUiDownloadData {
+
+ // POJO for UI artifact download, holding artifact filename and base64
+ // content
+ String artifactName;
+ String base64Contents;
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public void setBase64Contents(String base64Contents) {
+ this.base64Contents = base64Contents;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public String getBase64Contents() {
+ return base64Contents;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/AttributeDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/AttributeDefinition.java
new file mode 100644
index 0000000000..120a87c610
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/AttributeDefinition.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
+
+public class AttributeDefinition extends AttributeDataDefinition implements IComplexDefaultValue, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6306111879714097811L;
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((parentUniqueId == null) ? 0 : parentUniqueId.hashCode());
+ return result;
+ }
+
+ /**
+ * The resource id which this property belongs to
+ */
+ private String parentUniqueId;
+
+ public AttributeDefinition(AttributeDefinition hpdd) {
+ super(hpdd);
+ }
+
+ public AttributeDefinition() {
+ super();
+ }
+
+ public AttributeDefinition(AttributeDataDefinition p) {
+ super(p);
+ }
+
+ public String getParentUniqueId() {
+ return parentUniqueId;
+ }
+
+ public void setParentUniqueId(String parentUniqueId) {
+ this.parentUniqueId = parentUniqueId;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " [ parentUniqueId=" + parentUniqueId + "]";
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapReqDef.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapReqDef.java
new file mode 100644
index 0000000000..2f9cf48917
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapReqDef.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class CapReqDef {
+ Map<String, List<CapabilityDefinition>> capabilities;
+ Map<String, List<RequirementDefinition>> requirements;
+
+ public CapReqDef() {
+ super();
+ }
+
+ public CapReqDef(Map<String, List<RequirementDefinition>> requirements,
+ Map<String, List<CapabilityDefinition>> capabilities) {
+ super();
+ this.capabilities = capabilities;
+ this.requirements = requirements;
+ }
+
+ public Map<String, List<CapabilityDefinition>> getCapabilities() {
+ return capabilities;
+ }
+
+ public Map<String, List<RequirementDefinition>> getRequirements() {
+ return requirements;
+ }
+
+ public void setCapabilities(Map<String, List<CapabilityDefinition>> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public void setRequirements(Map<String, List<RequirementDefinition>> requirements) {
+ this.requirements = requirements;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityDefinition.java
new file mode 100644
index 0000000000..61ba356aa1
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityDefinition.java
@@ -0,0 +1,266 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Specifies the capabilities that the Node Type exposes.
+ */
+public class CapabilityDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3871825415338268030L;
+
+ private String uniqueId;
+
+ private String description;
+
+ private String name;
+
+ /** Identifies the type of the capability. */
+ private String type;
+
+ private List<String> validSourceTypes;
+
+ private List<String> capabilitySources;
+ /**
+ * The properties field contains all properties defined for
+ * CapabilityDefinition
+ */
+ private List<ComponentInstanceProperty> properties;
+
+ // specifies the resource instance holding this requirement
+ private String ownerId;
+ private String ownerName;
+ private String minOccurrences;
+ private String maxOccurrences;
+
+ public CapabilityDefinition() {
+ super();
+ }
+
+ public CapabilityDefinition(CapabilityDefinition other) {
+ this.uniqueId = other.uniqueId;
+ this.description = other.description;
+ this.name = other.name;
+ this.type = other.type;
+ if (other.validSourceTypes != null) {
+ this.validSourceTypes = new ArrayList<>(other.validSourceTypes);
+ }
+ if (other.capabilitySources != null) {
+ this.capabilitySources = new ArrayList<>(other.capabilitySources);
+ }
+ if (other.properties != null) {
+ this.properties = new ArrayList<>(other.properties);
+ }
+ this.ownerId = other.ownerId;
+ this.ownerName = other.ownerName;
+ this.minOccurrences = other.minOccurrences;
+ this.maxOccurrences = other.maxOccurrences;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List<String> getValidSourceTypes() {
+ return validSourceTypes;
+ }
+
+ public void setValidSourceTypes(List<String> validSourceTypes) {
+ this.validSourceTypes = validSourceTypes;
+ }
+
+ public List<String> getCapabilitySources() {
+ return capabilitySources;
+ }
+
+ public List<ComponentInstanceProperty> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<ComponentInstanceProperty> properties) {
+ this.properties = properties;
+ }
+
+ public void setCapabilitySources(List<String> capabilitySources) {
+ this.capabilitySources = capabilitySources;
+ }
+
+ public String getOwnerId() {
+ return ownerId;
+ }
+
+ public void setOwnerId(String ownerId) {
+ this.ownerId = ownerId;
+ }
+
+ public String getOwnerName() {
+ return ownerName;
+ }
+
+ public void setOwnerName(String ownerName) {
+ this.ownerName = ownerName;
+ }
+
+ public String getMinOccurrences() {
+ return minOccurrences;
+ }
+
+ public void setMinOccurrences(String minOccurrences) {
+ this.minOccurrences = minOccurrences;
+ }
+
+ public String getMaxOccurrences() {
+ return maxOccurrences;
+ }
+
+ public void setMaxOccurrences(String maxOccurrences) {
+ this.maxOccurrences = maxOccurrences;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((capabilitySources == null) ? 0 : capabilitySources.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((maxOccurrences == null) ? 0 : maxOccurrences.hashCode());
+ result = prime * result + ((minOccurrences == null) ? 0 : minOccurrences.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((ownerId == null) ? 0 : ownerId.hashCode());
+ result = prime * result + ((ownerName == null) ? 0 : ownerName.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((validSourceTypes == null) ? 0 : validSourceTypes.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ CapabilityDefinition other = (CapabilityDefinition) obj;
+ if (capabilitySources == null) {
+ if (other.capabilitySources != null)
+ return false;
+ } else if (!capabilitySources.equals(other.capabilitySources))
+ return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (maxOccurrences == null) {
+ if (other.maxOccurrences != null)
+ return false;
+ } else if (!maxOccurrences.equals(other.maxOccurrences))
+ return false;
+ if (minOccurrences == null) {
+ if (other.minOccurrences != null)
+ return false;
+ } else if (!minOccurrences.equals(other.minOccurrences))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (ownerId == null) {
+ if (other.ownerId != null)
+ return false;
+ } else if (!ownerId.equals(other.ownerId))
+ return false;
+ if (ownerName == null) {
+ if (other.ownerName != null)
+ return false;
+ } else if (!ownerName.equals(other.ownerName))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ if (validSourceTypes == null) {
+ if (other.validSourceTypes != null)
+ return false;
+ } else if (!validSourceTypes.equals(other.validSourceTypes))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabilityDefinition [uniqueId=" + uniqueId + ", description=" + description + ", name=" + name
+ + ", type=" + type + ", validSourceTypes=" + validSourceTypes + ", capabilitySources="
+ + capabilitySources + ", properties=" + properties + ", ownerId=" + ownerId + ", ownerName=" + ownerName
+ + ", minOccurrences=" + minOccurrences + ", maxOccurrences=" + maxOccurrences + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityTypeDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityTypeDefinition.java
new file mode 100644
index 0000000000..4291ee746d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabilityTypeDefinition.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.elements.CapabilityTypeDataDefinition;
+
+/**
+ * Specifies the capabilities that the Node Type exposes.
+ */
+public class CapabilityTypeDefinition extends CapabilityTypeDataDefinition {
+
+ private String derivedFrom;
+
+ private Map<String, PropertyDefinition> properties;
+
+ public String getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(String derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public Map<String, PropertyDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, PropertyDefinition> properties) {
+ this.properties = properties;
+ }
+
+ public CapabilityTypeDefinition() {
+ super();
+ }
+
+ public CapabilityTypeDefinition(CapabilityTypeDataDefinition p) {
+ super(p);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " [ derivedFrom=" + derivedFrom + ", properties=" + properties + " ]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabiltyInstance.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabiltyInstance.java
new file mode 100644
index 0000000000..5e5edf99ac
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/CapabiltyInstance.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.Map;
+
+public class CapabiltyInstance {
+
+ private String uniqueId;
+
+ private Map<String, String> properties;
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, String> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabiltyInstance [uniqueId=" + uniqueId + ", properties=" + properties + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Category.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Category.java
new file mode 100644
index 0000000000..03cab66d53
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Category.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class Category {
+
+ private String name;// will be changed to categoryDisplayName
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Category other = (Category) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Category [name=" + name + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Component.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Component.java
new file mode 100644
index 0000000000..57a70de388
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Component.java
@@ -0,0 +1,598 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+
+public abstract class Component implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6373756459967949120L;
+
+ private ComponentMetadataDefinition componentMetadataDefinition;
+ private Map<String, ArtifactDefinition> artifacts;
+ private Map<String, ArtifactDefinition> deploymentArtifacts;
+ private Map<String, ArtifactDefinition> toscaArtifacts;
+
+ private List<CategoryDefinition> categories;
+
+ // User
+ private String creatorUserId;
+ private String creatorFullName;
+ private String lastUpdaterUserId;
+ private String lastUpdaterFullName;
+
+ protected ComponentTypeEnum componentType;
+
+ private List<ComponentInstance> componentInstances;
+
+ private List<RequirementCapabilityRelDef> componentInstancesRelations;
+
+ private Map<String, List<ComponentInstanceInput>> componentInstancesInputs;
+
+ private Map<String, List<ComponentInstanceProperty>> componentInstancesProperties;
+
+ private Map<String, List<ComponentInstanceAttribute>> componentInstancesAttributes;
+
+ private Map<String, List<CapabilityDefinition>> capabilities;
+
+ private Map<String, List<RequirementDefinition>> requirements;
+
+ private List<InputDefinition> inputs;
+
+ private List<GroupDefinition> groups;
+
+ public Component(ComponentMetadataDefinition componentMetadataDefinition) {
+ this.componentMetadataDefinition = componentMetadataDefinition;
+ }
+
+ @JsonIgnore
+ public ComponentMetadataDefinition getComponentMetadataDefinition() {
+ return componentMetadataDefinition;
+ }
+
+ public Map<String, ArtifactDefinition> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(Map<String, ArtifactDefinition> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ public Map<String, ArtifactDefinition> getToscaArtifacts() {
+ return toscaArtifacts;
+ }
+
+ public void setToscaArtifacts(Map<String, ArtifactDefinition> toscaArtifacts) {
+ this.toscaArtifacts = toscaArtifacts;
+ }
+
+ public String getUniqueId() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getUniqueId();
+ }
+
+ public void setUniqueId(String uniqueId) {
+ componentMetadataDefinition.getMetadataDataDefinition().setUniqueId(uniqueId);
+ }
+
+ public void setName(String name) {
+ componentMetadataDefinition.getMetadataDataDefinition().setName(name);
+ }
+
+ public void setVersion(String version) {
+ componentMetadataDefinition.getMetadataDataDefinition().setVersion(version);
+ }
+
+ public void setHighestVersion(Boolean isHighestVersion) {
+ componentMetadataDefinition.getMetadataDataDefinition().setHighestVersion(isHighestVersion);
+ }
+
+ public void setCreationDate(Long creationDate) {
+ componentMetadataDefinition.getMetadataDataDefinition().setCreationDate(creationDate);
+ }
+
+ public void setLastUpdateDate(Long lastUpdateDate) {
+ componentMetadataDefinition.getMetadataDataDefinition().setLastUpdateDate(lastUpdateDate);
+ }
+
+ public void setDescription(String description) {
+ componentMetadataDefinition.getMetadataDataDefinition().setDescription(description);
+ }
+
+ public void setState(LifecycleStateEnum state) {
+ componentMetadataDefinition.getMetadataDataDefinition().setState(state.name());
+ }
+
+ public void setTags(List<String> tags) {
+ componentMetadataDefinition.getMetadataDataDefinition().setTags(tags);
+ }
+
+ public void setIcon(String icon) {
+ componentMetadataDefinition.getMetadataDataDefinition().setIcon(icon);
+ }
+
+ public void setContactId(String contactId) {
+ componentMetadataDefinition.getMetadataDataDefinition().setContactId(contactId);
+ }
+
+ public String getCreatorUserId() {
+ return creatorUserId;
+ }
+
+ public void setCreatorUserId(String creatorUserId) {
+ this.creatorUserId = creatorUserId;
+ }
+
+ public String getCreatorFullName() {
+ return creatorFullName;
+ }
+
+ public void setCreatorFullName(String creatorFullName) {
+ this.creatorFullName = creatorFullName;
+ }
+
+ public String getLastUpdaterUserId() {
+ return lastUpdaterUserId;
+ }
+
+ public void setLastUpdaterUserId(String lastUpdaterUserId) {
+ this.lastUpdaterUserId = lastUpdaterUserId;
+ }
+
+ public String getLastUpdaterFullName() {
+ return lastUpdaterFullName;
+ }
+
+ public void setLastUpdaterFullName(String lastUpdaterFullName) {
+ this.lastUpdaterFullName = lastUpdaterFullName;
+ }
+
+ public String getName() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getName();
+ }
+
+ public String getVersion() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getVersion();
+ }
+
+ public Boolean isHighestVersion() {
+ return componentMetadataDefinition.getMetadataDataDefinition().isHighestVersion();
+ }
+
+ public Long getCreationDate() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getCreationDate();
+ }
+
+ public Long getLastUpdateDate() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getLastUpdateDate();
+ }
+
+ public String getDescription() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getDescription();
+ }
+
+ public LifecycleStateEnum getLifecycleState() {
+ if (componentMetadataDefinition.getMetadataDataDefinition().getState() != null) {
+ return LifecycleStateEnum.valueOf(componentMetadataDefinition.getMetadataDataDefinition().getState());
+ } else {
+ return null;
+ }
+ }
+
+ public List<String> getTags() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getTags();
+ }
+
+ public String getIcon() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getIcon();
+ }
+
+ public String getContactId() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getContactId();
+ }
+
+ public List<InputDefinition> getInputs() {
+ return inputs;
+ }
+
+ public void setInputs(List<InputDefinition> inputs) {
+ this.inputs = inputs;
+ }
+
+ public void setLifecycleState(LifecycleStateEnum state) {
+ if (state != null) {
+ this.componentMetadataDefinition.getMetadataDataDefinition().setState(state.name());
+ }
+ }
+
+ public String getUUID() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getUUID();
+ }
+
+ public void setUUID(String uUID) {
+ componentMetadataDefinition.getMetadataDataDefinition().setUUID(uUID);
+ }
+
+ public void setSystemName(String systemName) {
+ componentMetadataDefinition.getMetadataDataDefinition().setSystemName(systemName);
+ }
+
+ public String getSystemName() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getSystemName();
+ }
+
+ public void setAllVersions(Map<String, String> allVersions) {
+ componentMetadataDefinition.getMetadataDataDefinition().setAllVersions(allVersions);
+ }
+
+ public Map<String, String> getAllVersions() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getAllVersions();
+ }
+
+ public Map<String, ArtifactDefinition> getDeploymentArtifacts() {
+ return deploymentArtifacts;
+ }
+
+ public void setDeploymentArtifacts(Map<String, ArtifactDefinition> deploymentArtifacts) {
+ this.deploymentArtifacts = deploymentArtifacts;
+ }
+
+ public List<CategoryDefinition> getCategories() {
+ return categories;
+ }
+
+ public void setCategories(List<CategoryDefinition> categories) {
+ this.categories = categories;
+ }
+
+ public String getNormalizedName() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getNormalizedName();
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ componentMetadataDefinition.getMetadataDataDefinition().setNormalizedName(normalizedName);
+ }
+
+ public ComponentTypeEnum getComponentType() {
+ return componentType;
+ }
+
+ public void setComponentType(ComponentTypeEnum componentType) {
+ this.componentType = componentType;
+ }
+
+ public Map<String, List<CapabilityDefinition>> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map<String, List<CapabilityDefinition>> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public Map<String, List<RequirementDefinition>> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(Map<String, List<RequirementDefinition>> requirements) {
+ this.requirements = requirements;
+ }
+
+ public List<ComponentInstance> getComponentInstances() {
+ return componentInstances;
+ }
+
+ public void setComponentInstances(List<ComponentInstance> resourceInstances) {
+ this.componentInstances = resourceInstances;
+ }
+
+ public List<RequirementCapabilityRelDef> getComponentInstancesRelations() {
+ return componentInstancesRelations;
+ }
+
+ public void setComponentInstancesRelations(List<RequirementCapabilityRelDef> resourceInstancesRelations) {
+ this.componentInstancesRelations = resourceInstancesRelations;
+ }
+
+ public Map<String, List<ComponentInstanceProperty>> getComponentInstancesProperties() {
+ return componentInstancesProperties;
+ }
+
+ public void setComponentInstancesProperties(
+ Map<String, List<ComponentInstanceProperty>> resourceInstancesProperties) {
+ this.componentInstancesProperties = resourceInstancesProperties;
+ }
+
+ public Boolean getIsDeleted() {
+ return componentMetadataDefinition.getMetadataDataDefinition().isDeleted();
+ }
+
+ public void setIsDeleted(Boolean isDeleted) {
+ componentMetadataDefinition.getMetadataDataDefinition().setIsDeleted(isDeleted);
+ }
+
+ public String getProjectCode() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getProjectCode();
+ }
+
+ public void setProjectCode(String projectCode) {
+ componentMetadataDefinition.getMetadataDataDefinition().setProjectCode(projectCode);
+ }
+
+ public String getCsarUUID() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getCsarUUID();
+ }
+
+ public void setCsarUUID(String csarUUID) {
+ componentMetadataDefinition.getMetadataDataDefinition().setCsarUUID(csarUUID);
+ }
+
+ public String getCsarVersion() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getCsarVersion();
+ }
+
+ public void setCsarVersion(String csarVersion) {
+ componentMetadataDefinition.getMetadataDataDefinition().setCsarVersion(csarVersion);
+ }
+
+ public String getImportedToscaChecksum() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getImportedToscaChecksum();
+ }
+
+ public void setImportedToscaChecksum(String importedToscaChecksum) {
+ componentMetadataDefinition.getMetadataDataDefinition().setImportedToscaChecksum(importedToscaChecksum);
+ }
+
+ public String getInvariantUUID() {
+ return componentMetadataDefinition.getMetadataDataDefinition().getInvariantUUID();
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ componentMetadataDefinition.getMetadataDataDefinition().setInvariantUUID(invariantUUID);
+ }
+
+ public List<GroupDefinition> getGroups() {
+ return groups;
+ }
+
+ public void setGroups(List<GroupDefinition> groups) {
+ this.groups = groups;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((artifacts == null) ? 0 : artifacts.hashCode());
+ result = prime * result + ((categories == null) ? 0 : categories.hashCode());
+ result = prime * result + ((componentMetadataDefinition == null) ? 0 : componentMetadataDefinition.hashCode());
+ result = prime * result + ((creatorUserId == null) ? 0 : creatorUserId.hashCode());
+ result = prime * result + ((creatorFullName == null) ? 0 : creatorFullName.hashCode());
+ result = prime * result + ((deploymentArtifacts == null) ? 0 : deploymentArtifacts.hashCode());
+ result = prime * result + ((lastUpdaterUserId == null) ? 0 : lastUpdaterUserId.hashCode());
+ result = prime * result + ((lastUpdaterFullName == null) ? 0 : lastUpdaterFullName.hashCode());
+ result = prime * result + ((capabilities == null) ? 0 : capabilities.hashCode());
+ result = prime * result + ((requirements == null) ? 0 : requirements.hashCode());
+ result = prime * result + ((componentInstances == null) ? 0 : componentInstances.hashCode());
+ result = prime * result
+ + ((componentInstancesProperties == null) ? 0 : componentInstancesProperties.hashCode());
+ result = prime * result
+ + ((componentInstancesAttributes == null) ? 0 : componentInstancesAttributes.hashCode());
+ result = prime * result + ((componentInstancesInputs == null) ? 0 : componentInstancesInputs.hashCode());
+ result = prime * result + ((componentInstancesRelations == null) ? 0 : componentInstancesRelations.hashCode());
+ result = prime * result + ((groups == null) ? 0 : groups.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Component other = (Component) obj;
+ if (artifacts == null) {
+ if (other.artifacts != null)
+ return false;
+ } else if (!artifacts.equals(other.artifacts))
+ return false;
+ if (categories == null) {
+ if (other.categories != null)
+ return false;
+ } else if (!categories.equals(other.categories))
+ return false;
+ if (componentMetadataDefinition == null) {
+ if (other.componentMetadataDefinition != null)
+ return false;
+ } else if (!componentMetadataDefinition.equals(other.componentMetadataDefinition))
+ return false;
+ if (creatorUserId == null) {
+ if (other.creatorUserId != null)
+ return false;
+ } else if (!creatorUserId.equals(other.creatorUserId))
+ return false;
+ if (creatorFullName == null) {
+ if (other.creatorFullName != null)
+ return false;
+ } else if (!creatorFullName.equals(other.creatorFullName))
+ return false;
+ if (deploymentArtifacts == null) {
+ if (other.deploymentArtifacts != null)
+ return false;
+ } else if (!deploymentArtifacts.equals(other.deploymentArtifacts))
+ return false;
+ if (lastUpdaterUserId == null) {
+ if (other.lastUpdaterUserId != null)
+ return false;
+ } else if (!lastUpdaterUserId.equals(other.lastUpdaterUserId))
+ return false;
+ if (lastUpdaterFullName == null) {
+ if (other.lastUpdaterFullName != null)
+ return false;
+ } else if (!lastUpdaterFullName.equals(other.lastUpdaterFullName))
+ return false;
+ if (componentInstances == null) {
+ if (other.componentInstances != null)
+ return false;
+ } else if (!componentInstances.equals(other.componentInstances))
+ return false;
+ if (componentInstancesProperties == null) {
+ if (other.componentInstancesProperties != null)
+ return false;
+ } else if (!componentInstancesProperties.equals(other.componentInstancesProperties))
+ return false;
+
+ if (!Objects.equals(componentInstancesAttributes, other.componentInstancesAttributes)) {
+ return false;
+ }
+ if (!Objects.equals(componentInstancesInputs, other.componentInstancesInputs)) {
+ return false;
+ }
+ if (componentInstancesRelations == null) {
+ if (other.componentInstancesRelations != null)
+ return false;
+ } else if (!componentInstancesRelations.equals(other.componentInstancesRelations))
+ return false;
+ if (requirements == null) {
+ if (other.requirements != null)
+ return false;
+ } else if (!requirements.equals(other.requirements))
+ return false;
+ if (capabilities == null) {
+ if (other.capabilities != null)
+ return false;
+ } else if (!capabilities.equals(other.capabilities))
+ return false;
+ if (groups == null) {
+ if (other.groups != null)
+ return false;
+ } else if (!groups.equals(other.groups))
+ return false;
+ return true;
+ }
+
+ public void addCategory(String category, String subCategory) {
+ if (category != null || subCategory != null) {
+ if (categories == null) {
+ categories = new ArrayList<>();
+ }
+ CategoryDefinition selectedCategory = null;
+ for (CategoryDefinition categoryDef : categories) {
+ if (categoryDef.getName().equals(category)) {
+ selectedCategory = categoryDef;
+ }
+ }
+ if (selectedCategory == null) {
+ selectedCategory = new CategoryDefinition();
+ selectedCategory.setName(category);
+ categories.add(selectedCategory);
+ }
+ List<SubCategoryDefinition> subcategories = selectedCategory.getSubcategories();
+ if (subcategories == null) {
+ subcategories = new ArrayList<>();
+ selectedCategory.setSubcategories(subcategories);
+ }
+ SubCategoryDefinition selectedSubcategory = null;
+ for (SubCategoryDefinition subcategory : subcategories) {
+ if (subcategory.getName().equals(subCategory)) {
+ selectedSubcategory = subcategory;
+ }
+ }
+ if (selectedSubcategory == null) {
+ selectedSubcategory = new SubCategoryDefinition();
+ selectedSubcategory.setName(subCategory);
+ subcategories.add(selectedSubcategory);
+ }
+ }
+ }
+
+ public void addCategory(CategoryDefinition category) {
+ addCategory(category, null);
+ }
+
+ public void addCategory(CategoryDefinition category, SubCategoryDefinition subCategory) {
+ if (categories == null) {
+ categories = new ArrayList<>();
+ }
+ boolean foundCat = false;
+ for (CategoryDefinition cat : categories) {
+ if (cat.getName().equals(category.getName())) {
+ foundCat = true;
+ if (subCategory != null) {
+ List<SubCategoryDefinition> subcategories = cat.getSubcategories();
+ if (subcategories == null) {
+ subcategories = new ArrayList<>();
+ cat.setSubcategories(subcategories);
+ }
+ for (SubCategoryDefinition subcat : subcategories) {
+ boolean foundSub = false;
+ if (subcat.getName().equals(subCategory.getName())) {
+ foundSub = true;
+ }
+ if (foundSub == false) {
+ subcategories.add(subCategory);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (foundCat == false) {
+ if (subCategory != null) {
+ category.addSubCategory(subCategory);
+ }
+ categories.add(category);
+ }
+ }
+
+ public Map<String, List<ComponentInstanceAttribute>> getComponentInstancesAttributes() {
+ return componentInstancesAttributes;
+ }
+
+ public void setComponentInstancesAttributes(
+ Map<String, List<ComponentInstanceAttribute>> componentInstancesAttributes) {
+ this.componentInstancesAttributes = componentInstancesAttributes;
+ }
+
+ public Map<String, List<ComponentInstanceInput>> getComponentInstancesInputs() {
+ return componentInstancesInputs;
+ }
+
+ public void setComponentInstancesInputs(Map<String, List<ComponentInstanceInput>> componentInstancesInputs) {
+ this.componentInstancesInputs = componentInstancesInputs;
+ }
+
+ public void setSpecificComponetTypeArtifacts(Map<String, ArtifactDefinition> specificComponentTypeArtifacts) {
+ // Implement where needed
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstInputsMap.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstInputsMap.java
new file mode 100644
index 0000000000..ce9ac67ced
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstInputsMap.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class ComponentInstInputsMap {
+
+ Map<String, List<InputDefinition>> componentInstanceInputsMap;
+
+ public Map<String, List<InputDefinition>> getComponentInstanceInputsMap() {
+ return componentInstanceInputsMap;
+ }
+
+ public void setComponentInstanceInputsMap(Map<String, List<InputDefinition>> componentInstanceInputsMap) {
+ this.componentInstanceInputsMap = componentInstanceInputsMap;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstance.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstance.java
new file mode 100644
index 0000000000..baaf89bcfc
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstance.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition;
+
+public class ComponentInstance extends ComponentInstanceDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6721465693884621223L;
+
+ private String icon;
+
+ private String componentName;
+ private String componentVersion;
+ private String toscaComponentName;
+ private Map<String, List<CapabilityDefinition>> capabilities;
+ private Map<String, List<RequirementDefinition>> requirements;
+ private Map<String, ArtifactDefinition> deploymentArtifacts;
+
+ public ComponentInstance() {
+ super();
+ }
+
+ public ComponentInstance(ComponentInstanceDataDefinition r) {
+ super(r);
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public void setComponentName(String resourceName) {
+ this.componentName = resourceName;
+ }
+
+ public String getComponentVersion() {
+ return componentVersion;
+ }
+
+ public String getToscaComponentName() {
+ return toscaComponentName;
+ }
+
+ public void setToscaComponentName(String toscaComponentName) {
+ this.toscaComponentName = toscaComponentName;
+ }
+
+ public void setComponentVersion(String resourceVersion) {
+ this.componentVersion = resourceVersion;
+ }
+
+ public Map<String, List<CapabilityDefinition>> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map<String, List<CapabilityDefinition>> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public Map<String, List<RequirementDefinition>> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(Map<String, List<RequirementDefinition>> requirements) {
+ this.requirements = requirements;
+ }
+
+ public Map<String, ArtifactDefinition> getDeploymentArtifacts() {
+ return deploymentArtifacts;
+ }
+
+ public void setDeploymentArtifacts(Map<String, ArtifactDefinition> deploymentArtifacts) {
+ this.deploymentArtifacts = deploymentArtifacts;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribute.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribute.java
new file mode 100644
index 0000000000..12233e733c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceAttribute.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+public class ComponentInstanceAttribute extends AttributeDefinition
+ implements IComponentInstanceConnectedElement, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -496828411269235795L;
+
+ private Boolean hidden;
+
+ /**
+ * The unique id of the attribute value on graph
+ */
+ private String valueUniqueUid;
+
+ public ComponentInstanceAttribute() {
+ super();
+ }
+
+ public ComponentInstanceAttribute(AttributeDefinition pd, Boolean hidden, String valueUniqueUid) {
+ super(pd);
+
+ this.hidden = hidden;
+ this.valueUniqueUid = valueUniqueUid;
+ setParentUniqueId(pd.getParentUniqueId());
+ }
+
+ public String getValueUniqueUid() {
+ return valueUniqueUid;
+ }
+
+ public void setValueUniqueUid(String valueUniqueUid) {
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentInstanceAttribute [ " + super.toString() + " , value=" + hidden + ", valueUniqueUid = "
+ + valueUniqueUid + " ]";
+ }
+
+ public Boolean isHidden() {
+ return hidden;
+ }
+
+ public void setHidden(Boolean hidden) {
+ this.hidden = hidden;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceInput.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceInput.java
new file mode 100644
index 0000000000..1334fa8c06
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceInput.java
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
+
+public class ComponentInstanceInput extends InputDefinition implements IComponentInstanceConnectedElement {
+
+ /**
+ * Value of property
+ */
+ private String value;
+
+ /**
+ * The unique id of the property value on graph
+ */
+ private String valueUniqueUid;
+
+ private String inputId;
+
+ private List<String> path = null;
+
+ private List<PropertyRule> rules = null;
+ private String componentInstanceName;
+ private String componentInstanceId;
+
+ public ComponentInstanceInput() {
+ super();
+ }
+
+ public ComponentInstanceInput(PropertyDataDefinition curPropertyDef, String inputId, String value,
+ String valueUniqueUid) {
+ super(curPropertyDef);
+ this.inputId = inputId;
+ this.value = value;
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ public ComponentInstanceInput(InputDefinition pd, String value, String valueUniqueUid) {
+ super(pd);
+
+ this.value = value;
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ public String getComponentInstanceName() {
+ return componentInstanceName;
+ }
+
+ public void setComponentInstanceName(String componentInstanceName) {
+ this.componentInstanceName = componentInstanceName;
+ }
+
+ public String getComponentInstanceId() {
+ return componentInstanceId;
+ }
+
+ public void setComponentInstanceId(String componentInstanceId) {
+ this.componentInstanceId = componentInstanceId;
+ }
+
+ public String getInputId() {
+ return inputId;
+ }
+
+ public void setInputId(String inputId) {
+ this.inputId = inputId;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValueUniqueUid() {
+ return valueUniqueUid;
+ }
+
+ public void setValueUniqueUid(String valueUniqueUid) {
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ public boolean isDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ public List<String> getPath() {
+ return path;
+ }
+
+ public void setPath(List<String> path) {
+ this.path = path;
+ }
+
+ public List<PropertyRule> getRules() {
+ return rules;
+ }
+
+ public void setRules(List<PropertyRule> rules) {
+ this.rules = rules;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentInstanceInput [ " + super.toString() + " , value=" + value + ", valueUniqueUid = "
+ + valueUniqueUid + " , rules=" + rules + " , path=" + path + " ]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceProperty.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceProperty.java
new file mode 100644
index 0000000000..a804170c75
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentInstanceProperty.java
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
+
+public class ComponentInstanceProperty extends PropertyDefinition
+ implements IComponentInstanceConnectedElement, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6559573536869242691L;
+
+ /**
+ * Value of property
+ */
+ private String value;
+
+ /**
+ * The unique id of the property value on graph
+ */
+ private String valueUniqueUid;
+
+ private List<String> path = null;
+
+ private List<PropertyRule> rules = null;
+
+ private List<GetInputValueInfo> getInputValues;
+
+ public ComponentInstanceProperty() {
+ super();
+ }
+
+ public ComponentInstanceProperty(PropertyDefinition pd, String value, String valueUniqueUid) {
+ super(pd);
+
+ this.value = value;
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValueUniqueUid() {
+ return valueUniqueUid;
+ }
+
+ public void setValueUniqueUid(String valueUniqueUid) {
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ public boolean isDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ public List<String> getPath() {
+ return path;
+ }
+
+ public void setPath(List<String> path) {
+ this.path = path;
+ }
+
+ public List<PropertyRule> getRules() {
+ return rules;
+ }
+
+ public void setRules(List<PropertyRule> rules) {
+ this.rules = rules;
+ }
+
+ public List<GetInputValueInfo> getGetInputValues() {
+ return getInputValues;
+ }
+
+ public void setGetInputValues(List<GetInputValueInfo> getInputValues) {
+ this.getInputValues = getInputValues;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentInstanceProperty [ " + super.toString() + " , value=" + value + ", valueUniqueUid = "
+ + valueUniqueUid + " , rules=" + rules + " , path=" + path + " ]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentMetadataDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentMetadataDefinition.java
new file mode 100644
index 0000000000..da3947b42f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentMetadataDefinition.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+
+public class ComponentMetadataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3570763790267255590L;
+
+ protected ComponentMetadataDataDefinition componentMetadataDataDefinition;
+
+ public ComponentMetadataDefinition() {
+
+ }
+
+ public ComponentMetadataDefinition(ComponentMetadataDataDefinition component) {
+ this.componentMetadataDataDefinition = component;
+ }
+
+ public ComponentMetadataDataDefinition getMetadataDataDefinition() {
+ return this.componentMetadataDataDefinition;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((componentMetadataDataDefinition == null) ? 0 : componentMetadataDataDefinition.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ComponentMetadataDefinition other = (ComponentMetadataDefinition) obj;
+ if (componentMetadataDataDefinition == null) {
+ if (other.componentMetadataDataDefinition != null)
+ return false;
+ } else if (!componentMetadataDataDefinition.equals(other.componentMetadataDataDefinition))
+ return false;
+ return true;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java
new file mode 100644
index 0000000000..0227de5b50
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ComponentParametersView.java
@@ -0,0 +1,316 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+
+public class ComponentParametersView {
+
+ boolean ignoreUsers = false;
+ boolean ignoreGroups = false;
+ boolean ignoreComponentInstances = false;
+ boolean ignoreComponentInstancesProperties = false;
+ boolean ignoreProperties = false;
+ boolean ignoreCapabilities = false;
+ boolean ignoreRequirements = false;
+ boolean ignoreCategories = false;
+ boolean ignoreAllVersions = false;
+ boolean ignoreAdditionalInformation = false;
+ boolean ignoreArtifacts = false;
+ boolean ignoreInterfaces = false;
+ boolean ignoreDerivedFrom = false;
+ boolean ignoreAttributesFrom = false;
+ boolean ignoreComponentInstancesAttributesFrom = false;
+ boolean ignoreInputs = false;
+ boolean ignoreComponentInstancesInputs = false;
+
+ ///////////////////////////////////////////////////////////////
+ // When adding new member, please update the filter method.
+ ///////////////////////////////////////////////////////////////
+
+ public Component filter(Component component, ComponentTypeEnum componentType) {
+
+ if (ignoreUsers) {
+ component.setCreatorUserId(null);
+ component.setCreatorFullName(null);
+ component.setLastUpdaterUserId(null);
+ component.setLastUpdaterFullName(null);
+ }
+
+ if (ignoreGroups) {
+ component.setGroups(null);
+ }
+
+ if (ignoreComponentInstances) {
+ component.setComponentInstances(null);
+ component.setComponentInstancesRelations(null);
+ }
+
+ if (ignoreComponentInstancesProperties) {
+ component.setComponentInstancesProperties(null);
+ }
+
+ if (ignoreProperties) {
+ switch (componentType) {
+ case RESOURCE:
+ ((Resource) component).setProperties(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ignoreCapabilities) {
+ component.setCapabilities(null);
+ }
+
+ if (ignoreRequirements) {
+ component.setRequirements(null);
+ }
+
+ if (ignoreCategories) {
+ component.setCategories(null);
+ }
+
+ if (ignoreAllVersions) {
+ component.setAllVersions(null);
+ }
+
+ if (ignoreAdditionalInformation) {
+ switch (componentType) {
+ case RESOURCE:
+ ((Resource) component).setAdditionalInformation(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ignoreArtifacts) {
+ component.setArtifacts(null);
+ component.setSpecificComponetTypeArtifacts(null);
+ component.setDeploymentArtifacts(null);
+ component.setToscaArtifacts(null);
+ }
+
+ if (ignoreInterfaces) {
+ switch (componentType) {
+ case RESOURCE:
+ ((Resource) component).setInterfaces(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ignoreDerivedFrom) {
+ switch (componentType) {
+ case RESOURCE:
+ ((Resource) component).setDerivedFrom(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ignoreAttributesFrom) {
+ switch (componentType) {
+ case RESOURCE:
+ ((Resource) component).setAttributes(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ignoreComponentInstancesAttributesFrom) {
+ component.setComponentInstancesAttributes(null);
+ }
+
+ if (ignoreInputs) {
+ component.setInputs(null);
+ }
+
+ if (ignoreComponentInstancesInputs) {
+ component.setComponentInstancesInputs(null);
+ }
+
+ return component;
+
+ }
+
+ public void disableAll() {
+ ignoreUsers = true;
+ ignoreGroups = true;
+ ignoreComponentInstances = true;
+ ignoreComponentInstancesProperties = true;
+ ignoreProperties = true;
+ ignoreCapabilities = true;
+ ignoreRequirements = true;
+ ignoreCategories = true;
+ ignoreAllVersions = true;
+ ignoreAdditionalInformation = true;
+ ignoreArtifacts = true;
+ ignoreInterfaces = true;
+ ignoreDerivedFrom = true;
+ ignoreAttributesFrom = true;
+ ignoreInputs = true;
+ ignoreComponentInstancesAttributesFrom = true;
+ ignoreComponentInstancesInputs = true;
+ }
+
+ public boolean isIgnoreGroups() {
+ return ignoreGroups;
+ }
+
+ public void setIgnoreGroups(boolean ignoreGroups) {
+ this.ignoreGroups = ignoreGroups;
+ }
+
+ public boolean isIgnoreComponentInstances() {
+ return ignoreComponentInstances;
+ }
+
+ public void setIgnoreComponentInstances(boolean ignoreComponentInstances) {
+ this.ignoreComponentInstances = ignoreComponentInstances;
+ }
+
+ public boolean isIgnoreProperties() {
+ return ignoreProperties;
+ }
+
+ public void setIgnoreProperties(boolean ignoreProperties) {
+ this.ignoreProperties = ignoreProperties;
+ }
+
+ public boolean isIgnoreCapabilities() {
+ return ignoreCapabilities;
+ }
+
+ public void setIgnoreCapabilities(boolean ignoreCapabilities) {
+ this.ignoreCapabilities = ignoreCapabilities;
+ }
+
+ public boolean isIgnoreRequirements() {
+ return ignoreRequirements;
+ }
+
+ public void setIgnoreRequirements(boolean ignoreRequirements) {
+ this.ignoreRequirements = ignoreRequirements;
+ }
+
+ public boolean isIgnoreCategories() {
+ return ignoreCategories;
+ }
+
+ public void setIgnoreCategories(boolean ignoreCategories) {
+ this.ignoreCategories = ignoreCategories;
+ }
+
+ public boolean isIgnoreAllVersions() {
+ return ignoreAllVersions;
+ }
+
+ public void setIgnoreAllVersions(boolean ignoreAllVersions) {
+ this.ignoreAllVersions = ignoreAllVersions;
+ }
+
+ public boolean isIgnoreAdditionalInformation() {
+ return ignoreAdditionalInformation;
+ }
+
+ public void setIgnoreAdditionalInformation(boolean ignoreAdditionalInformation) {
+ this.ignoreAdditionalInformation = ignoreAdditionalInformation;
+ }
+
+ public boolean isIgnoreArtifacts() {
+ return ignoreArtifacts;
+ }
+
+ public void setIgnoreArtifacts(boolean ignoreArtifacts) {
+ this.ignoreArtifacts = ignoreArtifacts;
+ }
+
+ public boolean isIgnoreComponentInstancesProperties() {
+ return ignoreComponentInstancesProperties;
+ }
+
+ public void setIgnoreComponentInstancesProperties(boolean ignoreComponentInstancesProperties) {
+ this.ignoreComponentInstancesProperties = ignoreComponentInstancesProperties;
+ }
+
+ public boolean isIgnoreComponentInstancesInputs() {
+ return ignoreComponentInstancesInputs;
+ }
+
+ public void setIgnoreComponentInstancesInputs(boolean ignoreComponentInstancesInputs) {
+ this.ignoreComponentInstancesInputs = ignoreComponentInstancesInputs;
+ }
+
+ public boolean isIgnoreInterfaces() {
+ return ignoreInterfaces;
+ }
+
+ public void setIgnoreInterfaces(boolean ignoreInterfaces) {
+ this.ignoreInterfaces = ignoreInterfaces;
+ }
+
+ public boolean isIgnoreAttributesFrom() {
+ return ignoreAttributesFrom;
+ }
+
+ public void setIgnoreAttributesFrom(boolean ignoreAttributesFrom) {
+ this.ignoreAttributesFrom = ignoreDerivedFrom;
+ }
+
+ public boolean isIgnoreComponentInstancesAttributesFrom() {
+ return ignoreComponentInstancesAttributesFrom;
+ }
+
+ public void setIgnoreComponentInstancesAttributesFrom(boolean ignoreComponentInstancesAttributesFrom) {
+ this.ignoreComponentInstancesAttributesFrom = ignoreComponentInstancesAttributesFrom;
+ }
+
+ public boolean isIgnoreDerivedFrom() {
+ return ignoreDerivedFrom;
+ }
+
+ public void setIgnoreDerivedFrom(boolean ignoreDerivedFrom) {
+ this.ignoreDerivedFrom = ignoreDerivedFrom;
+ }
+
+ public boolean isIgnoreUsers() {
+ return ignoreUsers;
+ }
+
+ public void setIgnoreUsers(boolean ignoreUsers) {
+ this.ignoreUsers = ignoreUsers;
+ }
+
+ public boolean isIgnoreInputs() {
+ return ignoreInputs;
+ }
+
+ public void setIgnoreInputs(boolean ignoreInputs) {
+ this.ignoreInputs = ignoreInputs;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ConsumerDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ConsumerDefinition.java
new file mode 100644
index 0000000000..eef455cbe2
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ConsumerDefinition.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.elements.ConsumerDataDefinition;
+
+public class ConsumerDefinition extends ConsumerDataDefinition {
+
+ public ConsumerDefinition() {
+ super();
+ }
+
+ public ConsumerDefinition(ConsumerDataDefinition a) {
+ super(a);
+
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java
new file mode 100644
index 0000000000..972884682e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java
@@ -0,0 +1,86 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class DataTypeDefinition extends DataTypeDataDefinition {
+
+ // @JsonIgnore
+ // @org.codehaus.jackson.annotate.JsonIgnore
+ private DataTypeDefinition derivedFrom;
+
+ private List<PropertyConstraint> constraints;
+
+ private List<PropertyDefinition> properties;
+
+ public DataTypeDefinition() {
+ super();
+ }
+
+ public DataTypeDefinition(DataTypeDataDefinition p) {
+ super(p);
+ }
+
+ public DataTypeDefinition(DataTypeDefinition pd) {
+ this.setName(pd.getName());
+ this.setDerivedFrom(pd.getDerivedFrom());
+ this.setDerivedFromName(pd.getDerivedFromName());
+ this.setUniqueId(pd.getUniqueId());
+ this.setConstraints(pd.getConstraints());
+ this.setDescription(pd.getDescription());
+ }
+
+ public List<PropertyConstraint> getConstraints() {
+ return constraints;
+ }
+
+ public void setConstraints(List<PropertyConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ public DataTypeDefinition getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(DataTypeDefinition derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public List<PropertyDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<PropertyDefinition> properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " DataTypeDefinition [derivedFrom=" + derivedFrom + ", constraints=" + constraints
+ + ", properties=" + properties + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionStatusEnum.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionStatusEnum.java
new file mode 100644
index 0000000000..89b5bfff31
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionStatusEnum.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public enum DistributionStatusEnum {
+ DISTRIBUTION_NOT_APPROVED("Distribution not approved"),
+ DISTRIBUTION_APPROVED("Distribution approved"),
+ DISTRIBUTED("Distributed"),
+ DISTRIBUTION_REJECTED("Distribution rejected");
+
+ private String value;
+
+ private DistributionStatusEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static DistributionStatusEnum findState(String state) {
+
+ for (DistributionStatusEnum distributionStatus : DistributionStatusEnum.values()) {
+ if (distributionStatus.name().equalsIgnoreCase(state)
+ || distributionStatus.getValue().equalsIgnoreCase(state)) {
+ return distributionStatus;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionTransitionEnum.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionTransitionEnum.java
new file mode 100644
index 0000000000..05f69f3166
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/DistributionTransitionEnum.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public enum DistributionTransitionEnum {
+ APPROVE("approve"), REJECT("reject");
+
+ String displayName;
+
+ private DistributionTransitionEnum(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public static DistributionTransitionEnum getFromDisplayName(String name) {
+
+ for (DistributionTransitionEnum val : DistributionTransitionEnum.values()) {
+ if (name.equalsIgnoreCase(val.getDisplayName())) {
+ return val;
+ }
+ }
+ return null;
+ }
+
+ public static String valuesAsString() {
+ StringBuilder sb = new StringBuilder();
+ for (DistributionTransitionEnum op : DistributionTransitionEnum.values()) {
+ sb.append(op.getDisplayName()).append(" ");
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/FunctionalMenuInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/FunctionalMenuInfo.java
new file mode 100644
index 0000000000..5aa3b85d02
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/FunctionalMenuInfo.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class FunctionalMenuInfo {
+
+ private String functionalMenu;
+
+ public FunctionalMenuInfo() {
+ super();
+ }
+
+ public String getFunctionalMenu() {
+ return functionalMenu;
+ }
+
+ public void setFunctionalMenu(String functionalMenu) {
+ this.functionalMenu = functionalMenu;
+ }
+
+ @Override
+ public String toString() {
+ return "FunctionalMenuInfo [functionalMenu=" + functionalMenu + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/GetInputValueInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GetInputValueInfo.java
new file mode 100644
index 0000000000..bb53e13251
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GetInputValueInfo.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class GetInputValueInfo {
+ String propName;
+ String inputName;
+ Integer indexValue;
+ GetInputValueInfo getInputIndex;
+
+ boolean isList = false;
+
+ public String getPropName() {
+ return propName;
+ }
+
+ public void setPropName(String propName) {
+ this.propName = propName;
+ }
+
+ public String getInputName() {
+ return inputName;
+ }
+
+ public void setInputName(String inputName) {
+ this.inputName = inputName;
+ }
+
+ public Integer getIndexValue() {
+ return indexValue;
+ }
+
+ public void setIndexValue(Integer indexValue) {
+ this.indexValue = indexValue;
+ }
+
+ public GetInputValueInfo getGetInputIndex() {
+ return getInputIndex;
+ }
+
+ public void setGetInputIndex(GetInputValueInfo getInputIndex) {
+ this.getInputIndex = getInputIndex;
+ }
+
+ public boolean isList() {
+ return isList;
+ }
+
+ public void setList(boolean isList) {
+ this.isList = isList;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupDefinition.java
new file mode 100644
index 0000000000..5520e89032
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupDefinition.java
@@ -0,0 +1,133 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition;
+
+public class GroupDefinition extends GroupDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -852613634651112247L;
+
+ // map of componentInstances <name: uniqueId>
+ private Map<String, String> members;
+
+ // properties (properties should be defined in the group type, the
+ // properties here are actually the value for the properties)
+ private List<GroupProperty> properties;
+
+ // artifacts - list of artifact uid. All artifacts in the group must already
+ // be uploaded to the VF
+ private List<String> artifacts;
+
+ private List<String> artifactsUuid;
+
+ // The unique id of the type of this group
+ private String typeUid;
+
+ public GroupDefinition() {
+ super();
+ }
+
+ public GroupDefinition(GroupDataDefinition other) {
+ super(other);
+ }
+
+ public GroupDefinition(GroupDefinition other) {
+ this.setName(other.getName());
+ this.setUniqueId(other.getUniqueId());
+ this.setType(other.getType());
+ this.setVersion(other.getVersion());
+ this.setInvariantUUID(other.getInvariantUUID());
+ this.setGroupUUID(other.getGroupUUID());
+ this.setDescription(other.getDescription());
+ if (other.members != null) {
+ this.members = new HashMap<String, String>(other.getMembers());
+ }
+ if (other.properties != null) {
+ this.properties = other.properties.stream().map(p -> new GroupProperty(p)).collect(Collectors.toList());
+ }
+ if (other.artifacts != null) {
+ this.artifacts = new ArrayList<String>(other.getArtifacts());
+ }
+
+ if (other.artifactsUuid != null) {
+ this.artifactsUuid = new ArrayList<String>(other.getArtifactsUuid());
+ }
+ this.setTypeUid(other.typeUid);
+ }
+
+ public Map<String, String> getMembers() {
+ return members;
+ }
+
+ public void setMembers(Map<String, String> members) {
+ this.members = members;
+ }
+
+ public List<GroupProperty> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<GroupProperty> properties) {
+ this.properties = properties;
+ }
+
+ public List<String> getArtifacts() {
+ return artifacts;
+ }
+
+ public void setArtifacts(List<String> artifacts) {
+ this.artifacts = artifacts;
+ }
+
+ public String getTypeUid() {
+ return typeUid;
+ }
+
+ public void setTypeUid(String typeUid) {
+ this.typeUid = typeUid;
+ }
+
+ public List<String> getArtifactsUuid() {
+ return artifactsUuid;
+ }
+
+ public void setArtifactsUuid(List<String> artifactsUuid) {
+ this.artifactsUuid = artifactsUuid;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupDefinition [" + super.toString() + "members=" + members + ", properties=" + properties
+ + ", artifacts=" + artifacts + ", artifactsUUID=" + artifactsUuid + ", typeUid=" + typeUid + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupProperty.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupProperty.java
new file mode 100644
index 0000000000..cf0afde8e3
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupProperty.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class GroupProperty extends PropertyDefinition {
+
+ /**
+ * current value
+ */
+ private String value;
+
+ /**
+ * The unique is of Group property on graph. If it is null, then the
+ * property's value was not updated. The value is taken from the group type
+ * property.
+ */
+ private String valueUniqueUid;
+
+ public GroupProperty() {
+ super();
+ }
+
+ public GroupProperty(PropertyDefinition pd, String value, String valueUniqueUid) {
+ super(pd);
+ this.value = value;
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ public GroupProperty(GroupProperty other) {
+ super(other);
+ if (other != null) {
+ this.value = other.getValue();
+ this.valueUniqueUid = other.getValueUniqueUid();
+ }
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getValueUniqueUid() {
+ return valueUniqueUid;
+ }
+
+ public void setValueUniqueUid(String valueUniqueUid) {
+ this.valueUniqueUid = valueUniqueUid;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupProperty [ " + super.toString() + " , value=" + value + ", valueUniqueUid = " + valueUniqueUid
+ + " ]";
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupTypeDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupTypeDefinition.java
new file mode 100644
index 0000000000..cd7fd6401b
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/GroupTypeDefinition.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.GroupTypeDataDefinition;
+
+/**
+ * Specifies the group type that the Node Type exposes.
+ */
+public class GroupTypeDefinition extends GroupTypeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -1597773317924162703L;
+
+ private List<PropertyDefinition> properties;
+
+ public List<PropertyDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<PropertyDefinition> properties) {
+ this.properties = properties;
+ }
+
+ public GroupTypeDefinition() {
+ super();
+ }
+
+ public GroupTypeDefinition(GroupTypeDataDefinition p) {
+ super(p);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " [ properties=" + properties + " ]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/HeatParameterDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/HeatParameterDefinition.java
new file mode 100644
index 0000000000..8e03361027
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/HeatParameterDefinition.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.datatypes.elements.HeatParameterDataDefinition;
+
+public class HeatParameterDefinition extends HeatParameterDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3400360721469962105L;
+
+ public HeatParameterDefinition(HeatParameterDataDefinition hpdd) {
+ super(hpdd);
+ }
+
+ public HeatParameterDefinition() {
+ super();
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/IComplexDefaultValue.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/IComplexDefaultValue.java
new file mode 100644
index 0000000000..3d21e07408
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/IComplexDefaultValue.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+
+public interface IComplexDefaultValue {
+ String getType();
+
+ String getName();
+
+ String getDefaultValue();
+
+ void setDefaultValue(String value);
+
+ SchemaDefinition getSchema();
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/IComponentInstanceConnectedElement.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/IComponentInstanceConnectedElement.java
new file mode 100644
index 0000000000..86de3a1584
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/IComponentInstanceConnectedElement.java
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public interface IComponentInstanceConnectedElement {
+ String getUniqueId();
+
+ String getValueUniqueUid();
+
+ void setValueUniqueUid(String value);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/IOperationParameter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/IOperationParameter.java
new file mode 100644
index 0000000000..1419353189
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/IOperationParameter.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+/**
+ * An operation parameter can be either a PropertyValue (value or expression) or
+ * a PropertyDefinition.
+ */
+public interface IOperationParameter {
+ /**
+ * Allow to know if the operation parameter is a property definition or a
+ * property value. Only parameter exposed as property definitions can be
+ * used for "custom" operations.
+ *
+ * @return true if the operation parameter is a property definition and
+ * false if the parameter is a property value.
+ */
+ boolean isDefinition();
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ImplementationArtifact.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ImplementationArtifact.java
new file mode 100644
index 0000000000..fb5943e1bd
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ImplementationArtifact.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+/**
+ * Specifies an implementation artifact for interfaces or operations of a
+ * {@link NodeType node type} or {@link RelationshipType relation type}.
+ *
+ * @author esofer
+ */
+public class ImplementationArtifact {
+ /**
+ * <p>
+ * Specifies the type of this artifact.
+ * </p>
+ */
+ private String artifactType;
+
+ /**
+ * <p>
+ * Identifies an Artifact Template to be used as implementation artifact.
+ * This Artifact Template can be defined in the same Definitions document or
+ * in a separate, imported document.
+ * </p>
+ *
+ * <p>
+ * The type of Artifact Template referenced by the artifactRef attribute
+ * MUST be the same type or a sub-type of the type specified in the
+ * artifactType attribute.
+ * </p>
+ *
+ * <p>
+ * Note: if no Artifact Template is referenced, the artifact type specific
+ * content of the ImplementationArtifact element alone is assumed to
+ * represent the actual artifact. For example, a simple script could be
+ * defined in place within the ImplementationArtifact element.
+ * </p>
+ */
+ private String artifactRef;
+
+ /**
+ * The name of the archive in which the artifact lies.
+ */
+ private String archiveName;
+ /**
+ * The version of the archive in which the artifact lies.
+ */
+ private String archiveVersion;
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/InputDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/InputDefinition.java
new file mode 100644
index 0000000000..3090d7232e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/InputDefinition.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+
+public class InputDefinition extends PropertyDefinition {
+ String label;
+ Boolean hidden;
+ Boolean immutable;
+ List<ComponentInstanceInput> inputsValue;
+ List<ComponentInstanceProperty> properties;
+
+ public InputDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public InputDefinition(PropertyDataDefinition p) {
+ super(p);
+ // TODO Auto-generated constructor stub
+ }
+
+ public InputDefinition(PropertyDefinition pd) {
+ super(pd);
+ // TODO Auto-generated constructor stub
+ }
+
+ public Boolean isHidden() {
+ return hidden;
+ }
+
+ public void setHidden(Boolean hidden) {
+ this.hidden = hidden;
+ }
+
+ public Boolean isImmutable() {
+ return immutable;
+ }
+
+ public void setImmutable(Boolean immutable) {
+ this.immutable = immutable;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public List<ComponentInstanceInput> getInputsValue() {
+ return inputsValue;
+ }
+
+ public void setInputsValue(List<ComponentInstanceInput> inputsValue) {
+ this.inputsValue = inputsValue;
+ }
+
+ public List<ComponentInstanceProperty> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<ComponentInstanceProperty> properties) {
+ this.properties = properties;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/InterfaceDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/InterfaceDefinition.java
new file mode 100644
index 0000000000..51ad31199a
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/InterfaceDefinition.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.elements.InterfaceDataDefinition;
+
+/**
+ * Definition of the operations that can be performed on (instances of) a Node
+ * Type.
+ *
+ * @author esofer
+ */
+public class InterfaceDefinition extends InterfaceDataDefinition implements IOperationParameter, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8220887972866354746L;
+
+ /**
+ * Defines an operation available to manage particular aspects of the Node
+ * Type.
+ */
+ private Map<String, Operation> operations = new HashMap<String, Operation>();
+
+ private boolean definition;
+
+ public InterfaceDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public InterfaceDefinition(String type, String description, Map<String, Operation> operations) {
+ super(type, description);
+ this.operations = operations;
+
+ }
+
+ public InterfaceDefinition(InterfaceDataDefinition p) {
+ super(p);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public boolean isDefinition() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ public Map<String, Operation> getOperations() {
+ return operations;
+ }
+
+ public void setOperations(Map<String, Operation> operations) {
+ this.operations = operations;
+ }
+
+ @Override
+ public String toString() {
+ return "InterfaceDefinition [operations=" + operations + ", definition=" + definition + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/LifeCycleTransitionEnum.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/LifeCycleTransitionEnum.java
new file mode 100644
index 0000000000..7d135c5f8e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/LifeCycleTransitionEnum.java
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public enum LifeCycleTransitionEnum {
+
+ CHECKOUT("checkout"),
+ CHECKIN("checkin"),
+ CERTIFICATION_REQUEST("certificationRequest"),
+ UNDO_CHECKOUT("undoCheckout"),
+ CANCEL_CERTIFICATION("cancelCertification"),
+ START_CERTIFICATION("startCertification"),
+ FAIL_CERTIFICATION("failCertification"),
+ CERTIFY("certify"),
+ DISTRIBUTE("distribute");
+
+ String displayName;
+
+ private LifeCycleTransitionEnum(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public static LifeCycleTransitionEnum getFromDisplayName(String name) {
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.CHECKOUT.getDisplayName())) {
+ return LifeCycleTransitionEnum.CHECKOUT;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.CHECKIN.getDisplayName())) {
+ return LifeCycleTransitionEnum.CHECKIN;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.CERTIFICATION_REQUEST.getDisplayName())) {
+ return LifeCycleTransitionEnum.CERTIFICATION_REQUEST;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.UNDO_CHECKOUT.getDisplayName())) {
+ return LifeCycleTransitionEnum.UNDO_CHECKOUT;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.CANCEL_CERTIFICATION.getDisplayName())) {
+ return LifeCycleTransitionEnum.CANCEL_CERTIFICATION;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.START_CERTIFICATION.getDisplayName())) {
+ return LifeCycleTransitionEnum.START_CERTIFICATION;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.FAIL_CERTIFICATION.getDisplayName())) {
+ return LifeCycleTransitionEnum.FAIL_CERTIFICATION;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.CERTIFY.getDisplayName())) {
+ return LifeCycleTransitionEnum.CERTIFY;
+ }
+ if (name.equalsIgnoreCase(LifeCycleTransitionEnum.DISTRIBUTE.getDisplayName())) {
+ return LifeCycleTransitionEnum.DISTRIBUTE;
+ } else
+ throw new IllegalArgumentException(name + " value does not match any of LifeCycleTransitionEnum values");
+ }
+
+ public static String valuesAsString() {
+ StringBuilder sb = new StringBuilder();
+ for (LifeCycleTransitionEnum op : LifeCycleTransitionEnum.values()) {
+ sb.append(op.getDisplayName()).append(" ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/LifecycleStateEnum.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/LifecycleStateEnum.java
new file mode 100644
index 0000000000..4d9ef81452
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/LifecycleStateEnum.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public enum LifecycleStateEnum {
+
+ READY_FOR_CERTIFICATION,
+
+ CERTIFICATION_IN_PROGRESS,
+
+ CERTIFIED,
+
+ NOT_CERTIFIED_CHECKIN,
+
+ NOT_CERTIFIED_CHECKOUT;
+
+ public static LifecycleStateEnum findState(String state) {
+
+ for (LifecycleStateEnum lifecycleStateEnum : LifecycleStateEnum.values()) {
+ if (lifecycleStateEnum.name().equals(state)) {
+ return lifecycleStateEnum;
+ }
+ }
+ return null;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Operation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Operation.java
new file mode 100644
index 0000000000..a793c68528
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Operation.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.InputsValueDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+
+/**
+ * Defines an operation available to manage particular aspects of the Node Type.
+ *
+ * @author esofer
+ */
+public class Operation extends OperationDataDefinition implements IOperationParameter {
+
+ /** Implementation artifact for the interface. */
+ private ArtifactDefinition implementation;
+
+ /**
+ * This OPTIONAL property contains a list of one or more input parameter
+ * definitions.
+ */
+ // @JsonDeserialize(contentUsing = OperationParameterDeserializer.class)
+ private Map<String, PropertyValueDefinition> inputs;
+
+ private boolean definition;
+
+ /**
+ * <p>
+ * Jackson DeSerialization workaround constructor to create an operation
+ * with no arguments.
+ * </p>
+ *
+ * @param emptyString
+ * The empty string provided by jackson.
+ */
+ public Operation() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public Operation(OperationDataDefinition p) {
+ super(p);
+ // TODO Auto-generated constructor stub
+ }
+
+ public Operation(ArtifactDataDefinition implementation, String description,
+ Map<String, InputsValueDataDefinition> inputs) {
+ super(description);
+
+ }
+
+ @Override
+ public boolean isDefinition() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ public ArtifactDefinition getImplementation() {
+ return implementation;
+ }
+
+ public void setImplementation(ArtifactDefinition implementation) {
+ this.implementation = implementation;
+ }
+
+ public Map<String, PropertyValueDefinition> getInputs() {
+ return inputs;
+ }
+
+ public void setInputs(Map<String, PropertyValueDefinition> inputs) {
+ this.inputs = inputs;
+ }
+
+ @Override
+ public String toString() {
+ return "Operation [implementation=" + implementation + ", inputs=" + inputs + ", definition=" + definition
+ + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ParsedToscaYamlInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ParsedToscaYamlInfo.java
new file mode 100644
index 0000000000..3b0708168b
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ParsedToscaYamlInfo.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.Map;
+
+public class ParsedToscaYamlInfo {
+ Map<String, InputDefinition> inputs;
+
+ Map<String, UploadComponentInstanceInfo> instances;
+
+ Map<String, GroupDefinition> groups;
+
+ public Map<String, UploadComponentInstanceInfo> getInstances() {
+ return instances;
+ }
+
+ public void setInstances(Map<String, UploadComponentInstanceInfo> instances) {
+ this.instances = instances;
+ }
+
+ public Map<String, GroupDefinition> getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Map<String, GroupDefinition> groups) {
+ this.groups = groups;
+ }
+
+ public Map<String, InputDefinition> getInputs() {
+ return inputs;
+ }
+
+ public void setInputs(Map<String, InputDefinition> inputs) {
+ this.inputs = inputs;
+ }
+
+ @Override
+ public String toString() {
+ return "ParsedToscaYamlInfo [inputs=" + inputs + ", instances=" + instances + ", groups=" + groups + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Point.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Point.java
new file mode 100644
index 0000000000..e609f49321
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Point.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class Point {
+
+ String x;
+ String y;
+
+ public Point() {
+ super();
+ }
+
+ public Point(String x, String y) {
+ super();
+ this.x = x;
+ this.y = y;
+ }
+
+ public String getX() {
+ return x;
+ }
+
+ public void setX(String x) {
+ this.x = x;
+ }
+
+ public String getY() {
+ return y;
+ }
+
+ public void setY(String y) {
+ this.y = y;
+ }
+
+ @Override
+ public String toString() {
+ return "Point [x=" + x + ", y=" + y + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/PolicyTypeDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PolicyTypeDefinition.java
new file mode 100644
index 0000000000..9b3e72ccc6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PolicyTypeDefinition.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.PolicyTypeDataDefinition;
+
+/**
+ * Specifies the policy type that the Node Type exposes.
+ */
+public class PolicyTypeDefinition extends PolicyTypeDataDefinition {
+
+ private List<PropertyDefinition> properties;
+
+ public List<PropertyDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<PropertyDefinition> properties) {
+ this.properties = properties;
+ }
+
+ public PolicyTypeDefinition() {
+ super();
+ }
+
+ public PolicyTypeDefinition(PolicyTypeDataDefinition p) {
+ super(p);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " [ properties=" + properties + " ]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Product.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Product.java
new file mode 100644
index 0000000000..04223857b7
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Product.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.ProductMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+
+public class Product extends Component {
+
+ public Product() {
+ super(new ProductMetadataDefinition());
+ componentType = ComponentTypeEnum.PRODUCT;
+ }
+
+ public Product(ProductMetadataDefinition productMetadataDefinition) {
+ super(productMetadataDefinition);
+ componentType = ComponentTypeEnum.PRODUCT;
+ }
+
+ public String getFullName() {
+ return getProductMetadataDefinition().getFullName();
+ }
+
+ public void setFullName(String fullName) {
+ getProductMetadataDefinition().setFullName(fullName);
+ }
+
+ public String getInvariantUUID() {
+ return getProductMetadataDefinition().getInvariantUUID();
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ getProductMetadataDefinition().setInvariantUUID(invariantUUID);
+ }
+
+ public List<String> getContacts() {
+ return getProductMetadataDefinition().getContacts();
+ }
+
+ public void setContacts(List<String> contacts) {
+ getProductMetadataDefinition().setContacts(contacts);
+ }
+
+ public void addContact(String contact) {
+ getProductMetadataDefinition().addContact(contact);
+ }
+
+ public Boolean getIsActive() {
+ return getProductMetadataDefinition().getIsActive();
+ }
+
+ public void setIsActive(Boolean isActive) {
+ getProductMetadataDefinition().setIsActive(isActive);
+ }
+
+ private ProductMetadataDataDefinition getProductMetadataDefinition() {
+ return (ProductMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition();
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ProductMetadataDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ProductMetadataDefinition.java
new file mode 100644
index 0000000000..dc454d13b7
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ProductMetadataDefinition.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.elements.ProductMetadataDataDefinition;
+
+public class ProductMetadataDefinition extends ComponentMetadataDefinition {
+
+ public ProductMetadataDefinition() {
+ super();
+ this.componentMetadataDataDefinition = new ProductMetadataDataDefinition();
+ }
+
+ public ProductMetadataDefinition(ProductMetadataDataDefinition component) {
+ super(component);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyConstraint.java
new file mode 100644
index 0000000000..b1e1552dfa
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyConstraint.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public interface PropertyConstraint {
+
+ void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException;
+
+ void validate(Object propertyValue) throws ConstraintViolationException;
+
+ void validate(ToscaType toscaType, String propertyTextValue) throws ConstraintViolationException;
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyDefinition.java
new file mode 100644
index 0000000000..7974e863ac
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyDefinition.java
@@ -0,0 +1,175 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+
+//import javax.validation.Valid;
+//import javax.validation.constraints.NotNull;
+//
+//
+//import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+//import com.fasterxml.jackson.annotation.JsonProperty;
+//import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class PropertyDefinition extends PropertyDataDefinition
+ implements IOperationParameter, IComplexDefaultValue, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 188403656600317269L;
+
+ private List<PropertyConstraint> constraints;
+ // private Schema schema;
+ private String status;
+
+ private String name;
+
+ /**
+ * The resource id which this property belongs to
+ */
+ private String parentUniqueId;
+
+ public PropertyDefinition() {
+ super();
+ }
+
+ public PropertyDefinition(PropertyDataDefinition p) {
+ super(p);
+ }
+
+ public PropertyDefinition(PropertyDefinition pd) {
+ this.setUniqueId(pd.getUniqueId());
+ this.setConstraints(pd.getConstraints());
+ // this.setSchema(pd.schema);
+ this.setDefaultValue(pd.getDefaultValue());
+ this.setDescription(pd.getDescription());
+ this.setName(pd.getName());
+ this.setSchema(pd.getSchema());
+ this.setParentUniqueId(pd.getParentUniqueId());
+ this.setRequired(pd.isRequired());
+ this.setType(pd.getType());
+ }
+
+ public List<PropertyConstraint> getConstraints() {
+ return constraints;
+ }
+
+ public void setConstraints(List<PropertyConstraint> constraints) {
+ this.constraints = constraints;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " [name=" + name + ", parentUniqueId=" + parentUniqueId + ", constraints="
+ + constraints + "]]";
+ }
+
+ // public void setSchema(Schema entrySchema) {
+ // this.schema = entrySchema;
+ //
+ // }
+ //
+ // public Schema getSchema() {
+ // return schema;
+ // }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getParentUniqueId() {
+ return parentUniqueId;
+ }
+
+ public void setParentUniqueId(String parentUniqueId) {
+ this.parentUniqueId = parentUniqueId;
+ }
+
+ @Override
+ public boolean isDefinition() {
+ return false;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((constraints == null) ? 0 : constraints.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((parentUniqueId == null) ? 0 : parentUniqueId.hashCode());
+ result = prime * result + ((status == null) ? 0 : status.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PropertyDefinition other = (PropertyDefinition) obj;
+ if (constraints == null) {
+ if (other.constraints != null)
+ return false;
+ } else if (!constraints.equals(other.constraints))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (parentUniqueId == null) {
+ if (other.parentUniqueId != null)
+ return false;
+ } else if (!parentUniqueId.equals(other.parentUniqueId))
+ return false;
+ if (status == null) {
+ if (other.status != null)
+ return false;
+ } else if (!status.equals(other.status))
+ return false;
+ return true;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyScope.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyScope.java
new file mode 100644
index 0000000000..e46c433291
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyScope.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class PropertyScope {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PropertyScope other = (PropertyScope) obj;
+ if (name == null) {
+ if (other.getClass() != null) {
+ return false;
+ }
+ } else if (!name.equals(other.getName())) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyValueDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyValueDefinition.java
new file mode 100644
index 0000000000..0c322420b3
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/PropertyValueDefinition.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.elements.InputsValueDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+
+public class PropertyValueDefinition extends InputsValueDataDefinition implements IOperationParameter {
+
+ public PropertyValueDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public PropertyValueDefinition(String name, String value) {
+ super(name, value);
+
+ }
+
+ @Override
+ public boolean isDefinition() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/RelationshipImpl.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RelationshipImpl.java
new file mode 100644
index 0000000000..6028809454
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RelationshipImpl.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+public class RelationshipImpl implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8272341490256251337L;
+
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return "RelationshipImpl [type=" + type + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementAndRelationshipPair.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementAndRelationshipPair.java
new file mode 100644
index 0000000000..84ead66658
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementAndRelationshipPair.java
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+public class RequirementAndRelationshipPair implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -5763126570618602135L;
+
+ private String requirement;
+ private String capabilityOwnerId;
+ private String requirementOwnerId;
+
+ private RelationshipImpl relationship;
+
+ private String capability;
+
+ private String capabilityUid;
+ private String requirementUid;
+
+ public RequirementAndRelationshipPair() {
+ super();
+ }
+
+ public RequirementAndRelationshipPair(String requirement, RelationshipImpl relationship) {
+ super();
+ this.requirement = requirement;
+ this.relationship = relationship;
+ }
+
+ public RequirementAndRelationshipPair(String requirement, RelationshipImpl relationship, String capability) {
+ super();
+ this.requirement = requirement;
+ this.relationship = relationship;
+ this.capability = capability;
+ }
+
+ public String getRequirement() {
+ return requirement;
+ }
+
+ public void setRequirement(String requirement) {
+ this.requirement = requirement;
+ }
+
+ public String getCapabilityOwnerId() {
+ return capabilityOwnerId;
+ }
+
+ public void setCapabilityOwnerId(String capabilityOwnerId) {
+ this.capabilityOwnerId = capabilityOwnerId;
+ }
+
+ public String getRequirementOwnerId() {
+ return requirementOwnerId;
+ }
+
+ public void setRequirementOwnerId(String requirementOwnerId) {
+ this.requirementOwnerId = requirementOwnerId;
+ }
+
+ public RelationshipImpl getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationships(RelationshipImpl relationship) {
+ this.relationship = relationship;
+ }
+
+ public String getCapability() {
+ return capability;
+ }
+
+ public void setCapability(String capability) {
+ this.capability = capability;
+ }
+
+ public String getCapabilityUid() {
+ return capabilityUid;
+ }
+
+ public void setCapabilityUid(String capabilityUid) {
+ this.capabilityUid = capabilityUid;
+ }
+
+ public String getRequirementUid() {
+ return requirementUid;
+ }
+
+ public void setRequirementUid(String requirementUid) {
+ this.requirementUid = requirementUid;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementAndRelationshipPair [requirement=" + requirement + ", relationship=" + relationship
+ + ", capability=" + capability + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementCapabilityRelDef.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementCapabilityRelDef.java
new file mode 100644
index 0000000000..345f2721ff
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementCapabilityRelDef.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+public class RequirementCapabilityRelDef extends TargetCapabilityRelDef implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6396265049494824741L;
+
+ private String fromNode;
+
+ public String getFromNode() {
+ return fromNode;
+ }
+
+ public void setFromNode(String fromNode) {
+ this.fromNode = fromNode;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementCapabilityRelDef [fromNode=" + fromNode + ", toString()=" + super.toString() + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementDefinition.java
new file mode 100644
index 0000000000..3d5741255a
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementDefinition.java
@@ -0,0 +1,241 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+
+/**
+ * Specifies the requirements that the Node Type exposes.
+ */
+public class RequirementDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8840549489409274532L;
+
+ /**
+ * Unique id of the requirement
+ */
+ private String uniqueId;
+
+ private String name;
+
+ /**
+ * specify the capability type
+ */
+ private String capability;
+
+ /**
+ * specify the node type(Optional by tosca)
+ */
+ private String node;
+
+ /**
+ * specify the relationship type(Optional by tosca)
+ */
+ private String relationship;
+
+ // specifies the resource instance holding this requirement
+ private String ownerId;
+ private String ownerName;
+
+ private String minOccurrences;
+ private String maxOccurrences;
+
+ public RequirementDefinition() {
+ super();
+ }
+
+ public RequirementDefinition(RequirementDefinition other) {
+ this.uniqueId = other.uniqueId;
+ this.name = other.name;
+ this.capability = other.capability;
+ this.node = other.node;
+ this.relationship = other.relationship;
+ this.ownerId = other.ownerId;
+ this.ownerName = other.ownerName;
+ this.minOccurrences = other.minOccurrences;
+ this.maxOccurrences = other.maxOccurrences;
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getCapability() {
+ return capability;
+ }
+
+ public void setCapability(String capability) {
+ this.capability = capability;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ public String getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationship(String relationship) {
+ this.relationship = relationship;
+ }
+
+ // public RequirementImplDef getRequirementImpl() {
+ // return requirementImpl;
+ // }
+ //
+ // public void setRequirementImpl(RequirementImplDef requirementImpl) {
+ // this.requirementImpl = requirementImpl;
+ // }
+
+ public String getOwnerId() {
+ return ownerId;
+ }
+
+ public void setOwnerId(String ownerId) {
+ this.ownerId = ownerId;
+ }
+
+ public String getOwnerName() {
+ return ownerName;
+ }
+
+ public void setOwnerName(String ownerName) {
+ this.ownerName = ownerName;
+ }
+
+ public String getMinOccurrences() {
+ return minOccurrences;
+ }
+
+ public void setMinOccurrences(String minOccurrences) {
+ this.minOccurrences = minOccurrences;
+ }
+
+ public String getMaxOccurrences() {
+ return maxOccurrences;
+ }
+
+ public void setMaxOccurrences(String maxOccurrences) {
+ this.maxOccurrences = maxOccurrences;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((capability == null) ? 0 : capability.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((node == null) ? 0 : node.hashCode());
+ result = prime * result + ((ownerId == null) ? 0 : ownerId.hashCode());
+ result = prime * result + ((ownerName == null) ? 0 : ownerName.hashCode());
+ result = prime * result + ((relationship == null) ? 0 : relationship.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((minOccurrences == null) ? 0 : minOccurrences.hashCode());
+ result = prime * result + ((maxOccurrences == null) ? 0 : maxOccurrences.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RequirementDefinition other = (RequirementDefinition) obj;
+ if (capability == null) {
+ if (other.capability != null)
+ return false;
+ } else if (!capability.equals(other.capability))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (node == null) {
+ if (other.node != null)
+ return false;
+ } else if (!node.equals(other.node))
+ return false;
+ if (ownerId == null) {
+ if (other.ownerId != null)
+ return false;
+ } else if (!ownerId.equals(other.ownerId))
+ return false;
+ if (ownerName == null) {
+ if (other.ownerName != null)
+ return false;
+ } else if (!ownerName.equals(other.ownerName))
+ return false;
+ if (relationship == null) {
+ if (other.relationship != null)
+ return false;
+ } else if (!relationship.equals(other.relationship))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ if (minOccurrences == null) {
+ if (other.minOccurrences != null)
+ return false;
+ } else if (!minOccurrences.equals(other.minOccurrences))
+ return false;
+ if (maxOccurrences == null) {
+ if (other.maxOccurrences != null)
+ return false;
+ } else if (!maxOccurrences.equals(other.maxOccurrences))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementDefinition [uniqueId=" + uniqueId + ", name=" + name + ", capability=" + capability
+ + ", node=" + node + ", relationship=" + relationship + ", ownerId=" + ownerId + ", ownerName="
+ + ownerName + ", minOccurrences=" + minOccurrences + ", maxOccurrences=" + maxOccurrences + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementImplDef.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementImplDef.java
new file mode 100644
index 0000000000..eb6bd01d4b
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementImplDef.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.Map;
+
+public class RequirementImplDef {
+
+ private String uniqueId;
+
+ /**
+ * node type(mandatory). Unique id of the node we choose.
+ */
+ private String nodeId;
+
+ private Map<String, CapabiltyInstance> requirementProperties;
+
+ private Point point;
+
+ public String getNodeId() {
+ return nodeId;
+ }
+
+ public void setNodeId(String nodeId) {
+ this.nodeId = nodeId;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Map<String, CapabiltyInstance> getRequirementProperties() {
+ return requirementProperties;
+ }
+
+ public void setRequirementProperties(Map<String, CapabiltyInstance> requirementProperties) {
+ this.requirementProperties = requirementProperties;
+ }
+
+ public Point getPoint() {
+ return point;
+ }
+
+ public void setPoint(Point point) {
+ this.point = point;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementImplDef [uniqueId=" + uniqueId + ", nodeId=" + nodeId + ", requirementProperties="
+ + requirementProperties + ", point=" + point + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementInstance.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementInstance.java
new file mode 100644
index 0000000000..14f4463216
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/RequirementInstance.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+/**
+ * Specifies the requirements that the Node Type exposes.
+ */
+public class RequirementInstance {
+
+ /**
+ * specify the resource instance name as appears in the service
+ */
+ private String node;
+
+ /**
+ * specify the relationship impl
+ */
+ private RelationshipImpl relationship;
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ public RelationshipImpl getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationship(RelationshipImpl relationship) {
+ this.relationship = relationship;
+ }
+
+ @Override
+ public String toString() {
+ return "RequirementInstance [node=" + node + ", relationship=" + relationship + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Resource.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Resource.java
new file mode 100644
index 0000000000..6490fb4ef1
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Resource.java
@@ -0,0 +1,273 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+
+public class Resource extends Component implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -6811540567661368482L;
+
+ public Resource() {
+ super(new ResourceMetadataDefinition());
+ componentType = ComponentTypeEnum.RESOURCE;
+ }
+
+ public Resource(ComponentMetadataDefinition componentMetadataDefinition) {
+ super(componentMetadataDefinition);
+ componentType = ComponentTypeEnum.RESOURCE;
+ }
+
+ private List<String> derivedFrom;
+
+ private List<String> derivedList;
+
+ private List<PropertyDefinition> properties;
+
+ private List<AttributeDefinition> attributes;
+
+ // Later
+ private Map<String, InterfaceDefinition> interfaces;
+
+ private List<String> defaultCapabilities;
+
+ private List<AdditionalInformationDefinition> additionalInformation;
+
+ /**
+ * Please note that more than one "derivedFrom" resource is not currently
+ * supported by the app. The first list element is always addressed.
+ *
+ * @return
+ */
+ public List<String> getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(List<String> derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ /**
+ * The derivedList is a chain of derivedFrom. e.g. if resource C is derived
+ * from resource B that is derived from resource A - then A, B is the
+ * "DerivedList" of resource C
+ *
+ * @return
+ */
+ public List<String> getDerivedList() {
+ return derivedList;
+ }
+
+ public void setDerivedList(List<String> derivedList) {
+ this.derivedList = derivedList;
+ }
+
+ public List<PropertyDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<PropertyDefinition> properties) {
+ this.properties = properties;
+ }
+
+ public List<AttributeDefinition> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(List<AttributeDefinition> attributes) {
+ this.attributes = attributes;
+ }
+
+ public Map<String, InterfaceDefinition> getInterfaces() {
+ return interfaces;
+ }
+
+ public void setInterfaces(Map<String, InterfaceDefinition> interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ public Boolean isAbstract() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .isAbstract();
+ }
+
+ public void setAbstract(Boolean isAbstract) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .setAbstract(isAbstract);
+ }
+
+ public List<String> getDefaultCapabilities() {
+ return defaultCapabilities;
+ }
+
+ public void setDefaultCapabilities(List<String> defaultCapabilities) {
+ this.defaultCapabilities = defaultCapabilities;
+ }
+
+ public String getCost() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .getCost();
+ }
+
+ public void setCost(String cost) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition()).setCost(cost);
+ ;
+ }
+
+ public String getLicenseType() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .getLicenseType();
+ }
+
+ public void setLicenseType(String licenseType) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .setLicenseType(licenseType);
+ }
+
+ public List<AdditionalInformationDefinition> getAdditionalInformation() {
+ return additionalInformation;
+ }
+
+ public void setAdditionalInformation(List<AdditionalInformationDefinition> additionalInformation) {
+ this.additionalInformation = additionalInformation;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + super.hashCode();
+
+ result = prime * result + ((attributes == null) ? 0 : attributes.hashCode());
+ // result = prime * result + ((capabilities == null) ? 0 :
+ // capabilities.hashCode());
+ result = prime * result + ((defaultCapabilities == null) ? 0 : defaultCapabilities.hashCode());
+ result = prime * result + ((derivedFrom == null) ? 0 : derivedFrom.hashCode());
+ result = prime * result + ((interfaces == null) ? 0 : interfaces.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + ((derivedList == null) ? 0 : derivedList.hashCode());
+ // result = prime * result + ((requirements == null) ? 0 :
+ // requirements.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+
+ Resource other = (Resource) obj;
+ if (attributes == null) {
+ if (other.attributes != null)
+ return false;
+ } else if (!attributes.equals(other.attributes))
+ return false;
+ if (defaultCapabilities == null) {
+ if (other.defaultCapabilities != null)
+ return false;
+ } else if (!defaultCapabilities.equals(other.defaultCapabilities))
+ return false;
+ if (derivedFrom == null) {
+ if (other.derivedFrom != null)
+ return false;
+ } else if (!derivedFrom.equals(other.derivedFrom))
+ return false;
+ if (derivedList == null) {
+ if (other.derivedList != null)
+ return false;
+ } else if (!derivedList.equals(other.derivedList))
+ return false;
+ if (interfaces == null) {
+ if (other.interfaces != null)
+ return false;
+ } else if (!interfaces.equals(other.interfaces))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+
+ return super.equals(obj);
+ }
+
+ @Override
+ public String toString() {
+ return "Resource [derivedFrom=" + derivedFrom + ", properties=" + properties + ", attributes=" + attributes
+ + ", interfaces=" + interfaces
+ // + ", capabilities=" + capabilities + ", requirements=" +
+ // requirements
+ + ", defaultCapabilities=" + defaultCapabilities + ", additionalInformation=" + additionalInformation
+ + "Metadata [" + getComponentMetadataDefinition().getMetadataDataDefinition().toString() + "]";
+ }
+
+ public String getToscaResourceName() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .getToscaResourceName();
+ }
+
+ public void setToscaResourceName(String toscaResourceName) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .setToscaResourceName(toscaResourceName);
+ }
+
+ public ResourceTypeEnum getResourceType() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .getResourceType();
+ }
+
+ public void setResourceType(ResourceTypeEnum resourceType) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .setResourceType(resourceType);
+ }
+
+ public void setVendorName(String vendorName) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .setVendorName(vendorName);
+ }
+
+ public void setVendorRelease(String vendorRelease) {
+ ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .setVendorRelease(vendorRelease);
+ }
+
+ public String getVendorName() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .getVendorName();
+ }
+
+ public String getVendorRelease() {
+ return ((ResourceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition())
+ .getVendorRelease();
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceInstanceHeatParameter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceInstanceHeatParameter.java
new file mode 100644
index 0000000000..75dfdff444
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceInstanceHeatParameter.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class ResourceInstanceHeatParameter extends HeatParameterDefinition {
+
+ private String valueUniqueId;
+
+ public ResourceInstanceHeatParameter() {
+ super();
+ }
+
+ public ResourceInstanceHeatParameter(HeatParameterDefinition heatParameterDefinition, String valueId) {
+ super(heatParameterDefinition);
+ valueUniqueId = valueId;
+ }
+
+ public String getValueUniqueId() {
+ return valueUniqueId;
+ }
+
+ public void setValueUniqueId(String valueUniqueId) {
+ this.valueUniqueId = valueUniqueId;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceInstanceHeatParameter [ " + super.toString() + " , valueUniqueId = " + valueUniqueId + " ]";
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceMetadataDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceMetadataDefinition.java
new file mode 100644
index 0000000000..2448191fc9
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ResourceMetadataDefinition.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+
+public class ResourceMetadataDefinition extends ComponentMetadataDefinition {
+
+ public ResourceMetadataDefinition() {
+ super();
+ this.componentMetadataDataDefinition = new ResourceMetadataDataDefinition();
+ }
+
+ public ResourceMetadataDefinition(ResourceMetadataDataDefinition other) {
+ super(other);
+
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Schema.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Schema.java
new file mode 100644
index 0000000000..cce4820870
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Schema.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Schema allows to create new types that can be used along TOSCA definitions.
+ */
+public class Schema {
+ private String derivedFrom;
+ private List<PropertyConstraint> constraints;
+ private Map<String, PropertyDefinition> properties;
+ private PropertyDefinition property;
+
+ public PropertyDefinition getProperty() {
+ return property;
+ }
+
+ public void setProperty(PropertyDefinition property) {
+ this.property = property;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Service.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Service.java
new file mode 100644
index 0000000000..e47333457f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Service.java
@@ -0,0 +1,100 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+
+public class Service extends Component {
+ public Service() {
+ super(new ServiceMetadataDefinition());
+ componentType = ComponentTypeEnum.SERVICE;
+ }
+
+ public Service(ComponentMetadataDefinition serviceMetadataDefinition) {
+ super(serviceMetadataDefinition);
+ componentType = ComponentTypeEnum.SERVICE;
+ }
+
+ private Map<String, ArtifactDefinition> serviceApiArtifacts;
+
+ private List<AdditionalInformationDefinition> additionalInformation;
+
+ public Map<String, ArtifactDefinition> getServiceApiArtifacts() {
+ return serviceApiArtifacts;
+ }
+
+ public void setServiceApiArtifacts(Map<String, ArtifactDefinition> serviceApiArtifacts) {
+ this.serviceApiArtifacts = serviceApiArtifacts;
+ }
+
+ public String getProjectCode() {
+ return getServiceMetadataDefinition().getProjectCode();
+ }
+
+ public void setProjectCode(String projectName) {
+ getServiceMetadataDefinition().setProjectCode(projectName);
+ }
+
+ public DistributionStatusEnum getDistributionStatus() {
+ String distributionStatus = getServiceMetadataDefinition().getDistributionStatus();
+ if (distributionStatus != null) {
+ return DistributionStatusEnum.valueOf(distributionStatus);
+ } else {
+ return null;
+ }
+ }
+
+ public void setDistributionStatus(DistributionStatusEnum distributionStatus) {
+ if (distributionStatus != null)
+ getServiceMetadataDefinition().setDistributionStatus(distributionStatus.name());
+ }
+
+ private ServiceMetadataDataDefinition getServiceMetadataDefinition() {
+ return (ServiceMetadataDataDefinition) getComponentMetadataDefinition().getMetadataDataDefinition();
+ }
+
+ public List<AdditionalInformationDefinition> getAdditionalInformation() {
+ return additionalInformation;
+ }
+
+ public void setAdditionalInformation(List<AdditionalInformationDefinition> additionalInformation) {
+ this.additionalInformation = additionalInformation;
+ }
+
+ @Override
+ public String toString() {
+ return "Service [componentMetadataDefinition=" + getComponentMetadataDefinition()
+ // + ", resourceInstances=" + resourceInstances + ",
+ // resourceInstancesRelations=" + resourceInstancesRelations + ",
+ // resourceInstancesRelations="
+ // + resourceInstancesRelations
+ + " ]";
+ }
+
+ @Override
+ public void setSpecificComponetTypeArtifacts(Map<String, ArtifactDefinition> specificComponentTypeArtifacts) {
+ setServiceApiArtifacts(specificComponentTypeArtifacts);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/ServiceMetadataDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ServiceMetadataDefinition.java
new file mode 100644
index 0000000000..21b2e43873
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/ServiceMetadataDefinition.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
+
+public class ServiceMetadataDefinition extends ComponentMetadataDefinition {
+
+ public ServiceMetadataDefinition() {
+ super();
+ this.componentMetadataDataDefinition = new ServiceMetadataDataDefinition();
+ }
+
+ public ServiceMetadataDefinition(ServiceMetadataDataDefinition component) {
+ super(component);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Tag.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Tag.java
new file mode 100644
index 0000000000..f3edfe2c6c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Tag.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public class Tag {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Tag other = (Tag) obj;
+ if (name == null) {
+ if (other.getClass() != null) {
+ return false;
+ }
+ } else if (!name.equals(other.getName())) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/TargetCapabilityRelDef.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/TargetCapabilityRelDef.java
new file mode 100644
index 0000000000..2e92ca5df3
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/TargetCapabilityRelDef.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class TargetCapabilityRelDef implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -7571489368200736413L;
+
+ private String toNode;
+
+ // private List<ImmutablePair<String, RelationshipImpl>> relationships;
+
+ private List<RequirementAndRelationshipPair> relationships;
+
+ public TargetCapabilityRelDef() {
+ super();
+ }
+
+ public TargetCapabilityRelDef(String toNode, List<RequirementAndRelationshipPair> relationships) {
+ super();
+ this.toNode = toNode;
+ this.relationships = relationships;
+ }
+
+ public String getToNode() {
+ return toNode;
+ }
+
+ public void setToNode(String toNode) {
+ this.toNode = toNode;
+ }
+
+ // public String getCapabilityOwnerId() {
+ // return capabilityOwnerId;
+ // }
+ //
+ // public void setCapabilityOwnerId(String capabilityOwnerId) {
+ // this.capabilityOwnerId = capabilityOwnerId;
+ // }
+
+ public List<RequirementAndRelationshipPair> getRelationships() {
+ return relationships;
+ }
+
+ public void setRelationships(List<RequirementAndRelationshipPair> relationships) {
+ this.relationships = relationships;
+ }
+
+ @Override
+ public String toString() {
+ return "TargetCapabilityRelDef [ toNode=" + toNode
+ // + ", capabilityOwnerId=" + capabilityOwnerId
+ + ", relationships=" + relationships + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadCapInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadCapInfo.java
new file mode 100644
index 0000000000..d16330481e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadCapInfo.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+
+public class UploadCapInfo extends UploadInfo {
+ /**
+ * specify the node type(Optional by tosca)
+ */
+ private List<String> validSourceTypes;
+
+ private List<UploadPropInfo> properties;
+
+ private String node;
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+ public List<String> getValidSourceTypes() {
+ return validSourceTypes;
+ }
+
+ public void setValidSourceTypes(List<String> validSourceTypes) {
+ this.validSourceTypes = validSourceTypes;
+ }
+
+ public List<UploadPropInfo> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<UploadPropInfo> properties) {
+ this.properties = properties;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadComponentInstanceInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadComponentInstanceInfo.java
new file mode 100644
index 0000000000..329e816ea7
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadComponentInstanceInfo.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class UploadComponentInstanceInfo {
+ private String name;
+ private String type;
+ private Map<String, List<UploadCapInfo>> capabilities;
+ private Map<String, List<UploadReqInfo>> requirements;
+ private Map<String, List<UploadPropInfo>> properties;
+
+ public Map<String, List<UploadPropInfo>> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, List<UploadPropInfo>> properties) {
+ this.properties = properties;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Map<String, List<UploadCapInfo>> getCapabilities() {
+ return capabilities;
+ }
+
+ public void setCapabilities(Map<String, List<UploadCapInfo>> capabilities) {
+ this.capabilities = capabilities;
+ }
+
+ public Map<String, List<UploadReqInfo>> getRequirements() {
+ return requirements;
+ }
+
+ public void setRequirements(Map<String, List<UploadReqInfo>> requirements) {
+ this.requirements = requirements;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadInfo.java
new file mode 100644
index 0000000000..75707ee77c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadInfo.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+public abstract class UploadInfo {
+
+ private String type;
+
+ private String name;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadPropInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadPropInfo.java
new file mode 100644
index 0000000000..60a58c9aed
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadPropInfo.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.List;
+import java.util.Map;
+
+public class UploadPropInfo extends UploadInfo {
+
+ private Object value;
+
+ private String description;
+
+ private boolean password;
+
+ private List<GetInputValueInfo> get_input;
+
+ public List<GetInputValueInfo> getGet_input() {
+ return get_input;
+ }
+
+ public void setGet_input(List<GetInputValueInfo> get_input) {
+ this.get_input = get_input;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public boolean isPassword() {
+ return password;
+ }
+
+ public void setPassword(boolean password) {
+ this.password = password;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadReqInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadReqInfo.java
new file mode 100644
index 0000000000..7f2834bddf
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadReqInfo.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.Map;
+
+public class UploadReqInfo extends UploadInfo {
+ /**
+ * specify the node type(Optional by tosca)
+ */
+
+ private String capabilityName;
+
+ private String node;
+
+ public String getCapabilityName() {
+ return capabilityName;
+ }
+
+ public void setCapabilityName(String capabilityName) {
+ this.capabilityName = capabilityName;
+ }
+
+ public String getNode() {
+ return node;
+ }
+
+ public void setNode(String node) {
+ this.node = node;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadResourceInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadResourceInfo.java
new file mode 100644
index 0000000000..385b15c728
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/UploadResourceInfo.java
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.common.api.UploadArtifactInfo;
+
+public class UploadResourceInfo {
+
+ public UploadResourceInfo(String payload, String payloadName, String description, String category,
+ List<String> tags, List<UploadArtifactInfo> artifactsList) {
+ super();
+ this.payloadData = payload;
+ this.payloadName = payloadName;
+ this.description = description;
+ // this.category = category;
+ this.tags = tags;
+ this.artifactList = artifactsList;
+ if (category != null) {
+ String[] arr = category.split("/");
+ if (arr.length >= 2) {
+ categories = new ArrayList<>();
+ CategoryDefinition catDef = new CategoryDefinition();
+ catDef.setName(arr[0]);
+ SubCategoryDefinition subCat = new SubCategoryDefinition();
+ subCat.setName(arr[1]);
+ catDef.addSubCategory(subCat);
+ categories.add(catDef);
+ }
+ }
+ }
+
+ public UploadResourceInfo() {
+ }
+
+ private String payloadData;
+ private String payloadName;
+ private String description;
+ // private String category;
+ private List<String> tags;
+ private List<CategoryDefinition> categories;
+
+ private List<UploadArtifactInfo> artifactList;
+ private String contactId, name, resourceIconPath, icon, vendorName, vendorRelease;
+
+ private String resourceType = "VFC";
+
+ public String getPayloadData() {
+ return payloadData;
+ }
+
+ public void setPayloadData(String payload) {
+ this.payloadData = payload;
+ }
+
+ public String getPayloadName() {
+ return payloadName;
+ }
+
+ public void setPayloadName(String payloadName) {
+ this.payloadName = payloadName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ // public String getCategory() {
+ // return category;
+ // }
+ // public void setCategory(String category) {
+ // this.category = category;
+ // }
+ public List<String> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<String> tags) {
+ this.tags = tags;
+ }
+
+ public List<UploadArtifactInfo> getArtifactList() {
+ return artifactList;
+ }
+
+ public void setArtifactList(List<UploadArtifactInfo> artifactsList) {
+ this.artifactList = artifactsList;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((artifactList == null) ? 0 : artifactList.hashCode());
+ result = prime * result + ((contactId == null) ? 0 : contactId.hashCode());
+ // result = prime * result + ((category == null) ? 0 :
+ // category.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((icon == null) ? 0 : icon.hashCode());
+ result = prime * result + ((payloadData == null) ? 0 : payloadData.hashCode());
+ result = prime * result + ((payloadName == null) ? 0 : payloadName.hashCode());
+ result = prime * result + ((resourceIconPath == null) ? 0 : resourceIconPath.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((tags == null) ? 0 : tags.hashCode());
+ result = prime * result + ((vendorName == null) ? 0 : vendorName.hashCode());
+ result = prime * result + ((vendorRelease == null) ? 0 : vendorRelease.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ UploadResourceInfo other = (UploadResourceInfo) obj;
+ if (artifactList == null) {
+ if (other.artifactList != null)
+ return false;
+ } else if (!artifactList.equals(other.artifactList))
+ return false;
+ if (contactId == null) {
+ if (other.contactId != null)
+ return false;
+ } else if (!contactId.equals(other.contactId))
+ return false;
+ // if (category == null) {
+ // if (other.category != null)
+ // return false;
+ // } else if (!category.equals(other.category))
+ // return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (icon == null) {
+ if (other.icon != null)
+ return false;
+ } else if (!icon.equals(other.icon))
+ return false;
+ if (payloadData == null) {
+ if (other.payloadData != null)
+ return false;
+ } else if (!payloadData.equals(other.payloadData))
+ return false;
+ if (payloadName == null) {
+ if (other.payloadName != null)
+ return false;
+ } else if (!payloadName.equals(other.payloadName))
+ return false;
+ if (resourceIconPath == null) {
+ if (other.resourceIconPath != null)
+ return false;
+ } else if (!resourceIconPath.equals(other.resourceIconPath))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (tags == null) {
+ if (other.tags != null)
+ return false;
+ } else if (!tags.equals(other.tags))
+ return false;
+ if (vendorName == null) {
+ if (other.vendorName != null)
+ return false;
+ } else if (!vendorName.equals(other.vendorName))
+ return false;
+ if (vendorRelease == null) {
+ if (other.vendorRelease != null)
+ return false;
+ } else if (!vendorRelease.equals(other.vendorRelease))
+ return false;
+ return true;
+ }
+
+ public String getContactId() {
+ return contactId;
+ }
+
+ public void setContactId(String userId) {
+ this.contactId = userId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String resourceName) {
+ this.name = resourceName;
+ }
+
+ // Icon when using UI import otherwise resourceIconPath
+ public String getResourceIconPath() {
+ return (resourceIconPath != null) ? resourceIconPath : icon;
+ }
+
+ public void setResourceIconPath(String resourceIconPath) {
+ this.resourceIconPath = resourceIconPath;
+ }
+
+ public String getVendorName() {
+ return vendorName;
+ }
+
+ public void setVendorName(String vendorName) {
+ this.vendorName = vendorName;
+ }
+
+ public String getVendorRelease() {
+ return vendorRelease;
+ }
+
+ public void setVendorRelease(String vendorRelease) {
+ this.vendorRelease = vendorRelease;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public String getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(String resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public List<CategoryDefinition> getCategories() {
+ return categories;
+ }
+
+ public void setCategories(List<CategoryDefinition> categories) {
+ this.categories = categories;
+ }
+
+ public void addSubCategory(String category, String subCategory) {
+ if (category != null || subCategory != null) {
+ if (categories == null) {
+ categories = new ArrayList<>();
+ }
+ CategoryDefinition selectedCategory = null;
+ for (CategoryDefinition categoryDef : categories) {
+ if (categoryDef.getName().equals(category)) {
+ selectedCategory = categoryDef;
+ }
+ }
+ if (selectedCategory == null) {
+ selectedCategory = new CategoryDefinition();
+ selectedCategory.setName(category);
+ categories.add(selectedCategory);
+ }
+ List<SubCategoryDefinition> subcategories = selectedCategory.getSubcategories();
+ if (subcategories == null) {
+ subcategories = new ArrayList<>();
+ selectedCategory.setSubcategories(subcategories);
+ }
+ SubCategoryDefinition selectedSubcategory = null;
+ for (SubCategoryDefinition subcategory : subcategories) {
+ if (subcategory.getName().equals(subCategory)) {
+ selectedSubcategory = subcategory;
+ }
+ }
+ if (selectedSubcategory == null) {
+ selectedSubcategory = new SubCategoryDefinition();
+ selectedSubcategory.setName(subCategory);
+ subcategories.add(selectedSubcategory);
+ }
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/User.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/User.java
new file mode 100644
index 0000000000..129c35b708
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/User.java
@@ -0,0 +1,205 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.resources.data.UserData;
+
+public class User {
+ public static final String FORCE_DELETE_HEADER_FLAG = "FORCE_DELETE";
+
+ private String firstName;
+
+ private String lastName;
+
+ private String userId;
+
+ private String email;
+
+ private String role;
+
+ private Long lastLoginTime;
+
+ private UserStatusEnum status = UserStatusEnum.ACTIVE;
+
+ public User() {
+ }
+
+ public User(UserData userDate) {
+ this(userDate.getFirstName(), userDate.getLastName(), userDate.getUserId(), userDate.getEmail(),
+ userDate.getRole(), userDate.getLastLoginTime());
+ }
+
+ public User(String firstName, String lastName, String userId, String emailAddress, String role,
+ Long lastLoginTime) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.userId = userId;
+ this.email = emailAddress;
+ this.role = role;
+ this.lastLoginTime = lastLoginTime;
+
+ }
+
+ public void copyData(User other) {
+ this.firstName = other.getFirstName();
+ this.lastName = other.getLastName();
+ this.userId = other.getUserId();
+ this.email = other.getEmail();
+ this.role = other.getRole();
+ this.lastLoginTime = other.getLastLoginTime();
+
+ }
+
+ public User(User aUser) {
+ this(aUser.getFirstName(), aUser.getLastName(), aUser.getUserId(), aUser.getEmail(), aUser.getRole(),
+ aUser.getLastLoginTime());
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public String getFullName() {
+ return this.getFirstName() + " " + this.getLastName();
+ }
+
+ public void setLastLoginTime() {
+ DateTime now = new DateTime(DateTimeZone.UTC);
+ this.lastLoginTime = now.getMillis();
+ }
+
+ public void setLastLoginTime(Long time) {
+ this.lastLoginTime = time;
+ }
+
+ public Long getLastLoginTime() {
+ return this.lastLoginTime;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((userId == null) ? 0 : userId.hashCode());
+ result = prime * result + ((email == null) ? 0 : email.hashCode());
+ result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
+ result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
+ result = prime * result + ((role == null) ? 0 : role.hashCode());
+ result = prime * result + ((lastLoginTime == null) ? 0 : lastLoginTime.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ User other = (User) obj;
+ if (userId == null) {
+ if (other.userId != null)
+ return false;
+ } else if (!userId.equals(other.userId))
+ return false;
+ if (email == null) {
+ if (other.email != null)
+ return false;
+ } else if (!email.equals(other.email))
+ return false;
+ if (firstName == null) {
+ if (other.firstName != null)
+ return false;
+ } else if (!firstName.equals(other.firstName))
+ return false;
+ if (lastName == null) {
+ if (other.lastName != null)
+ return false;
+ } else if (!lastName.equals(other.lastName))
+ return false;
+ if (role == null) {
+ if (other.role != null)
+ return false;
+ } else if (!role.equals(other.role))
+ return false;
+ if (lastLoginTime == null) {
+ if (other.lastLoginTime != null)
+ return false;
+ } else if (!lastLoginTime.equals(other.lastLoginTime))
+ return false;
+ return true;
+ }
+
+ public UserStatusEnum getStatus() {
+ return status;
+ }
+
+ public void setStatus(UserStatusEnum status) {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return "User [firstName=" + firstName + ", lastName=" + lastName + ", userId=" + userId + ", email=" + email
+ + ", role=" + role + ", last login time=" + lastLoginTime + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java
new file mode 100644
index 0000000000..bb8a1b0129
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationCache.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+
+import fj.data.Either;
+
+public interface ApplicationCache<T> {
+
+ public abstract Either<Map<String, T>, TitanOperationStatus> getAll();
+
+ public abstract Either<T, TitanOperationStatus> get(String uniqueId);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java
new file mode 100644
index 0000000000..31664c929b
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java
@@ -0,0 +1,335 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheConfig;
+import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheInfo;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.resources.data.DataTypeData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("application-datatype-cache")
+public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefinition>, Runnable {
+
+ private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
+ private final Lock r = rwl.readLock();
+ private final Lock w = rwl.writeLock();
+
+ private Map<String, DataTypeDefinition> data = new HashMap<>();
+
+ private ScheduledExecutorService scheduledPollingService = Executors.newScheduledThreadPool(1,
+ new BasicThreadFactory.Builder().namingPattern("ApplicationDataTypeCacheThread-%d").build());
+ ScheduledFuture<?> scheduledFuture = null;
+
+ private static Logger log = LoggerFactory.getLogger(ApplicationDataTypeCache.class.getName());
+
+ private int firstRunDelayInSec = 30;
+ private int pollingIntervalInSec = 60;
+
+ @Resource
+ private PropertyOperation propertyOperation;
+
+ @PostConstruct
+ public void init() {
+
+ ApplicationL1CacheConfig applicationL1CacheConfig = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getApplicationL1Cache();
+ if (applicationL1CacheConfig != null) {
+ if (applicationL1CacheConfig.getDatatypes() != null) {
+ ApplicationL1CacheInfo datatypesInfo = applicationL1CacheConfig.getDatatypes();
+ if (datatypesInfo.getEnabled()) {
+ Integer intervalInSec = datatypesInfo.getPollIntervalInSec();
+ if (intervalInSec != null) {
+ pollingIntervalInSec = intervalInSec;
+ }
+ Integer firstRunDelay = datatypesInfo.getFirstRunDelay();
+ if (firstRunDelay != null) {
+ firstRunDelayInSec = firstRunDelay;
+ }
+ log.trace("ApplicationDataTypesCache polling interval is " + pollingIntervalInSec + " seconds.");
+ if (scheduledPollingService != null) {
+ log.debug("Start ApplicationDataTypeCache polling task. polling interval {} seconds",
+ pollingIntervalInSec);
+ scheduledFuture = scheduledPollingService.scheduleAtFixedRate(this, firstRunDelayInSec,
+ pollingIntervalInSec, TimeUnit.SECONDS);
+ }
+
+ }
+ } else {
+ BeEcompErrorManager.getInstance().logInternalFlowError("ApplicationDataTypesCache", "Cache is disabled",
+ ErrorSeverity.INFO);
+ }
+ } else {
+ BeEcompErrorManager.getInstance().logInternalFlowError("ApplicationDataTypesCache", "Cache is disabled",
+ ErrorSeverity.INFO);
+ }
+
+ }
+
+ @PreDestroy
+ void destroy() {
+
+ if (scheduledFuture != null) {
+ boolean result = scheduledFuture.cancel(true);
+ log.debug("Stop polling task. result = {}", result);
+
+ scheduledFuture = null;
+ }
+ shutdownExecutor();
+ }
+
+ private void shutdownExecutor() {
+ if (scheduledPollingService == null)
+ return;
+
+ scheduledPollingService.shutdown(); // Disable new tasks from being
+ // submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS)) {
+ scheduledPollingService.shutdownNow(); // Cancel currently
+ // executing tasks
+ // Wait a while for tasks to respond to being cancelled
+ if (!scheduledPollingService.awaitTermination(60, TimeUnit.SECONDS))
+ log.debug("Pool did not terminate");
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ scheduledPollingService.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private Either<Map<String, DataTypeDefinition>, TitanOperationStatus> getAllDataTypesFromGraph() {
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = propertyOperation
+ .getAllDataTypes();
+
+ return allDataTypes;
+
+ }
+
+ @Override
+ public Either<Map<String, DataTypeDefinition>, TitanOperationStatus> getAll() {
+
+ try {
+
+ r.lock();
+ if (data == null || data.isEmpty()) {
+ return getAllDataTypesFromGraph();
+ }
+
+ return Either.left(data);
+
+ } finally {
+ r.unlock();
+ }
+ }
+
+ @Override
+ public Either<DataTypeDefinition, TitanOperationStatus> get(String uniqueId) {
+
+ try {
+ r.lock();
+
+ if (data == null || data.isEmpty()) {
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = propertyOperation
+ .getDataTypeByUid(uniqueId);
+ return dataTypeByUid;
+ } else {
+ DataTypeDefinition dataTypeDefinition = data.values().stream()
+ .filter(p -> p.getUniqueId().equals(uniqueId)).findFirst().orElse(null);
+ if (dataTypeDefinition == null) {
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = propertyOperation
+ .getDataTypeByUid(uniqueId);
+ return dataTypeByUid;
+ } else {
+ return Either.left(dataTypeDefinition);
+ }
+ }
+ } finally {
+ r.unlock();
+ }
+ }
+
+ @Override
+ public void run() {
+ log.trace("run() method. polling db to fetch data types");
+
+ try {
+
+ Long start = System.currentTimeMillis();
+ log.trace("Start fetching all data types from db");
+ Either<List<DataTypeData>, TitanOperationStatus> allDataTypeNodes = propertyOperation.getAllDataTypeNodes();
+ Long end = System.currentTimeMillis();
+ log.trace("Finish fetching all data types from db. Took " + (end - start) + " Milliseconds");
+ if (allDataTypeNodes.isRight()) {
+ TitanOperationStatus status = allDataTypeNodes.right().value();
+ if (status != TitanOperationStatus.OK) {
+ log.debug("ApplicationDataTypesCache - Failed to fetch all data types nodes");
+ BeEcompErrorManager.getInstance().logInternalConnectionError("FetchDataTypes",
+ "Failed to fetch data types from graph(cache)", ErrorSeverity.INFO);
+ }
+ } else {
+
+ List<DataTypeData> list = allDataTypeNodes.left().value();
+ if (list != null) {
+
+ Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime = list.stream()
+ .collect(Collectors.toMap(p -> p.getDataTypeDataDefinition().getName(),
+ p -> new ImmutablePair<Long, Long>(p.getDataTypeDataDefinition().getCreationTime(),
+ p.getDataTypeDataDefinition().getModificationTime())));
+
+ Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime = new HashMap<>();
+ try {
+ r.lock();
+ if (data != null) {
+ currentDataTypeToModificationTime = data.values().stream().collect(Collectors.toMap(
+ p -> p.getName(),
+ p -> new ImmutablePair<Long, Long>(p.getCreationTime(), p.getModificationTime())));
+
+ }
+ } finally {
+ r.unlock();
+ }
+
+ boolean isChanged = compareDataTypes(dataTypeNameToModificationTime,
+ currentDataTypeToModificationTime);
+ if (isChanged) {
+ replaceAllData();
+ }
+
+ }
+ }
+
+ } catch (Exception e) {
+ log.debug("unexpected error occured", e);
+
+ BeEcompErrorManager.getInstance().logInternalUnexpectedError("ApplicationDataTypesCache",
+ "Failed to run refresh data types job", ErrorSeverity.INFO);
+ } finally {
+ try {
+ propertyOperation.getTitanGenericDao().commit();
+ } catch (Exception e) {
+ log.trace("Failed to commit ApplicationDataTypeCache", e);
+ }
+ }
+
+ }
+
+ private boolean compareDataTypes(Map<String, ImmutablePair<Long, Long>> dataTypeNameToModificationTime,
+ Map<String, ImmutablePair<Long, Long>> currentDataTypeToModificationTime) {
+ if (dataTypeNameToModificationTime.size() != currentDataTypeToModificationTime.size()) {
+ return true;
+ } else {
+
+ Set<String> currentkeySet = currentDataTypeToModificationTime.keySet();
+ Set<String> keySet = dataTypeNameToModificationTime.keySet();
+
+ if (currentkeySet.containsAll(keySet)) {
+
+ for (Entry<String, ImmutablePair<Long, Long>> entry : dataTypeNameToModificationTime.entrySet()) {
+ String dataTypeName = entry.getKey();
+ ImmutablePair<Long, Long> creationAndModificationTimes = entry.getValue();
+ long creationTime = creationAndModificationTimes.getLeft() == null ? 0
+ : creationAndModificationTimes.getLeft().longValue();
+ long modificationTime = creationAndModificationTimes.getRight() == null ? 0
+ : creationAndModificationTimes.getRight().longValue();
+
+ ImmutablePair<Long, Long> currentEntry = currentDataTypeToModificationTime.get(dataTypeName);
+ long currentCreationTime = currentEntry.getLeft() == null ? 0 : currentEntry.getLeft().longValue();
+ long currentModificationTime = currentEntry.getRight() == null ? 0
+ : currentEntry.getRight().longValue();
+
+ if (creationTime > currentCreationTime || modificationTime > currentModificationTime) {
+ log.debug("Datatype {} was updated. Creation Time {} vs {}. Modification Time {} vs {}",
+ dataTypeName, currentCreationTime, creationTime, currentModificationTime,
+ modificationTime);
+ return true;
+ }
+ }
+ } else {
+ return true;
+ }
+
+ }
+
+ return false;
+ }
+
+ private void replaceAllData() {
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = propertyOperation
+ .getAllDataTypes();
+
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Failed to fetch all data types from db. Status is {}", status);
+ } else {
+
+ try {
+ w.lock();
+
+ Map<String, DataTypeDefinition> newDataTypes = allDataTypes.left().value();
+ data = newDataTypes;
+
+ BeEcompErrorManager.getInstance().logInternalFlowError("ReplaceDataTypesCache",
+ "Succeed to replace the data types cache", ErrorSeverity.INFO);
+
+ } finally {
+ w.unlock();
+ }
+
+ }
+
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ComponentCache.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ComponentCache.java
new file mode 100644
index 0000000000..6732adbdb1
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ComponentCache.java
@@ -0,0 +1,997 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheCatalogInfo;
+import org.openecomp.sdc.be.config.Configuration.ApplicationL2CacheConfig;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.dao.cassandra.ComponentCassandraDao;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.*;
+import org.openecomp.sdc.be.resources.data.ComponentCacheData;
+import org.openecomp.sdc.common.util.SerializationUtils;
+import org.openecomp.sdc.common.util.ZipUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("component-cache")
+public class ComponentCache {
+
+ private static Logger logger = LoggerFactory.getLogger(ComponentCache.class.getName());
+
+ @javax.annotation.Resource
+ ComponentCassandraDao componentCassandraDao;
+
+ @javax.annotation.Resource
+ ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ ProductOperation productOperation;
+
+ private Map<ComponentTypeEnum, Map<String, Component>> catalogInMemoryCache = new HashMap<>();
+ private final ReentrantReadWriteLock rwCatalogLock = new ReentrantReadWriteLock();
+ private final Lock rCatalogLock = rwCatalogLock.readLock();
+ private final Lock wCatalogLock = rwCatalogLock.writeLock();
+
+ boolean enabled = true;
+ int catalogInMemorySizePerResource = 300;
+ int catalogInMemorySizePerService = 200;
+ int catalogInMemorySizePerProduct = 100;
+ boolean catalogInMemoryEnabled = true;
+ Map<ComponentTypeEnum, Integer> limitMemoryCatalogSizePerType = new HashMap<>();
+
+ @PostConstruct
+ public void init() {
+
+ Configuration configuration = ConfigurationManager.getConfigurationManager().getConfiguration();
+ if (configuration != null) {
+ ApplicationL2CacheConfig applicationL2Cache = configuration.getApplicationL2Cache();
+ if (applicationL2Cache != null) {
+ boolean isEnabled = applicationL2Cache.isEnabled();
+ this.enabled = isEnabled;
+
+ ApplicationL1CacheCatalogInfo catalog = applicationL2Cache.getCatalogL1Cache();
+ if (catalog != null) {
+ catalogInMemoryEnabled = catalog.getEnabled();
+ catalogInMemorySizePerResource = catalog.getResourcesSizeInCache();
+ catalogInMemorySizePerService = catalog.getServicesSizeInCache();
+ catalogInMemorySizePerProduct = catalog.getProductsSizeInCache();
+ }
+ }
+ }
+
+ ComponentTypeEnum[] typesForCache = { ComponentTypeEnum.RESOURCE, ComponentTypeEnum.SERVICE,
+ ComponentTypeEnum.PRODUCT };
+ for (ComponentTypeEnum typeEnum : typesForCache) {
+ Map<String, Component> map = new HashMap<>();
+ catalogInMemoryCache.put(typeEnum, map);
+ }
+
+ limitMemoryCatalogSizePerType.put(ComponentTypeEnum.RESOURCE, catalogInMemorySizePerResource);
+ limitMemoryCatalogSizePerType.put(ComponentTypeEnum.SERVICE, catalogInMemorySizePerService);
+ limitMemoryCatalogSizePerType.put(ComponentTypeEnum.PRODUCT, catalogInMemorySizePerProduct);
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Either<Component, ActionStatus> getComponent(String componentUid, Long lastModificationTime,
+ Function<Component, Component> filterFieldsFunc) {
+
+ Either<ImmutablePair<Component, ComponentCacheData>, ActionStatus> componentFromCache = getComponentFromCache(
+ componentUid, lastModificationTime, filterFieldsFunc);
+
+ if (componentFromCache.isRight()) {
+ return Either.right(componentFromCache.right().value());
+ }
+
+ return Either.left(componentFromCache.left().value().left);
+
+ }
+
+ public Either<List<ComponentCacheData>, ActionStatus> getAllComponentIdTimeAndType() {
+ if (false == isEnabled()) {
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+
+ Either<List<ComponentCacheData>, ActionStatus> componentRes = componentCassandraDao
+ .getAllComponentIdTimeAndType();
+
+ return componentRes;
+
+ }
+
+ /**
+ * get components for catalog
+ *
+ * @param components
+ * @param componentTypeEnum
+ * @return
+ */
+ @Deprecated
+ public Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> getComponentsForCatalog(
+ Set<String> components, ComponentTypeEnum componentTypeEnum) {
+
+ if (false == isEnabled()) {
+ logger.debug("In getComponentsForCatalog for type {}. Cache is disabled.",
+ componentTypeEnum.name().toLowerCase());
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+ logger.debug("In getComponentsForCatalog for type {}", componentTypeEnum.name().toLowerCase());
+
+ Function<List<Component>, List<Component>> filterFieldsFunc = x -> filterForCatalog(x);
+
+ Set<String> leftComponentsForSearch = new HashSet<>();
+ leftComponentsForSearch.addAll(components);
+
+ // get components from inmemory cache
+ List<Component> componentsFromMemory = null;
+ if (true == catalogInMemoryEnabled) {
+ componentsFromMemory = getDataFromInMemoryCache(components, componentTypeEnum);
+ logger.debug("The number of components of type {} fetched from memory is {}",
+ componentTypeEnum.name().toLowerCase(),
+ componentsFromMemory == null ? 0 : componentsFromMemory.size());
+ if (componentsFromMemory != null) {
+ componentsFromMemory.forEach(p -> leftComponentsForSearch.remove(p.getUniqueId()));
+ }
+ } else {
+ logger.debug("Catalog InMemory cache is disabled");
+ }
+
+ logger.debug("Number of components from type {} needed to fetch is {}", componentTypeEnum.name().toLowerCase(),
+ leftComponentsForSearch.size());
+
+ // get components from cassandra cache and filter each component
+ Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> result = getComponents(
+ leftComponentsForSearch, filterFieldsFunc);
+
+ if (result.isLeft()) {
+ // add inmemory components to the valid components(not dirty)
+ List<Component> foundComponents = result.left().value().getLeft();
+ if (componentsFromMemory != null) {
+ foundComponents.addAll(componentsFromMemory);
+ }
+ if (true == catalogInMemoryEnabled) {
+ updateCatalogInMemoryCacheWithCertified(foundComponents, componentTypeEnum);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @param foundComponents
+ * @param componentTypeEnum
+ */
+ private void updateCatalogInMemoryCacheWithCertified(List<Component> foundComponents,
+ ComponentTypeEnum componentTypeEnum) {
+
+ try {
+ wCatalogLock.lock();
+
+ long start = System.currentTimeMillis();
+ Map<String, Component> map = catalogInMemoryCache.get(componentTypeEnum);
+ int mapSizeBefore = map.size();
+ map.clear();
+ Map<String, Component> collect = foundComponents.stream()
+ .filter(p -> p.getLifecycleState() == LifecycleStateEnum.CERTIFIED)
+ .limit(limitMemoryCatalogSizePerType.get(componentTypeEnum))
+ .collect(Collectors.toMap(p -> p.getUniqueId(), p -> p));
+ map.putAll(collect);
+ logger.debug(
+ "Size of in memory cache for catalog {}(certified only): Before {}, After {}. Replacement Time is {} ms.",
+ componentTypeEnum.name().toLowerCase(), mapSizeBefore, map.size(),
+ System.currentTimeMillis() - start);
+ } finally {
+ wCatalogLock.unlock();
+ }
+
+ }
+
+ private List<Component> getDataFromInMemoryCache(Set<String> components, ComponentTypeEnum componentTypeEnum) {
+ List<Component> foundComponents = new ArrayList<>();
+
+ try {
+
+ rCatalogLock.lock();
+
+ Map<String, Component> map = catalogInMemoryCache.get(componentTypeEnum);
+ for (String compUid : components) {
+ Component component = map.get(compUid);
+ if (component != null) {
+ foundComponents.add(component);
+ }
+ }
+
+ } finally {
+ rCatalogLock.unlock();
+ }
+
+ return foundComponents;
+ }
+
+ /**
+ *
+ * get full components from cassandra. On each component apply filter
+ * function in order to remove unused members
+ *
+ * @param components
+ * @param filterFieldsFunc
+ * @return <found components, found dirty components, not found components
+ * list> or Error
+ */
+ public Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> getComponents(
+ Set<String> components, Function<List<Component>, List<Component>> filterFieldsFunc) {
+
+ if (false == isEnabled()) {
+ logger.debug("Component Cache is disabled");
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+
+ Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> componentsFull = getComponentsFull(
+ components);
+
+ if (componentsFull.isRight()) {
+ return Either.right(componentsFull.right().value());
+ }
+
+ ImmutableTriple<List<Component>, List<Component>, Set<String>> immutableTriple = componentsFull.left().value();
+ List<Component> foundResources = immutableTriple.left;
+ List<Component> foundDirtyResources = immutableTriple.middle;
+ Set<String> notFoundResources = immutableTriple.right;
+
+ List<Component> filterdFoundResources = filterFieldsFunc.apply(foundResources);
+ List<Component> filterdFoundDirtyResources = filterFieldsFunc.apply(foundDirtyResources);
+
+ ImmutableTriple<List<Component>, List<Component>, Set<String>> result = new ImmutableTriple<List<Component>, List<Component>, Set<String>>(
+ filterdFoundResources, filterdFoundDirtyResources, notFoundResources);
+
+ return Either.left(result);
+
+ }
+
+ public Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> getComponentsForLeftPanel(
+ ComponentTypeEnum componentTypeEnum, String internalComponentType, Set<String> filteredResources) {
+
+ logger.debug("In getComponentsForLeftPanel componentTypeEnum = {}, internalComponentType = {}",
+ componentTypeEnum, internalComponentType);
+
+ Function<List<Component>, List<Component>> filterFieldsFunc = x -> filterForLeftPanel(x);
+
+ return getComponents(filteredResources, filterFieldsFunc);
+
+ }
+
+ private List<Component> filterForLeftPanel(List<Component> components) {
+
+ List<Component> result = new ArrayList<>();
+ if (components != null) {
+ components.forEach(p -> result.add(filterFieldsForLeftPanel(p)));
+ }
+
+ return result;
+ }
+
+ private List<Component> filterForCatalog(List<Component> components) {
+
+ List<Component> result = new ArrayList<>();
+ if (components != null) {
+ components.forEach(p -> result.add(filterFieldsForCatalog(p)));
+ }
+
+ return result;
+ }
+
+ private Component filterFieldsForLeftPanel(Component component) {
+
+ Component result = null;
+ ComponentTypeEnum componentTypeEnum = component.getComponentType();
+ switch (componentTypeEnum) {
+ case RESOURCE:
+ result = new Resource();
+ copyFieldsForLeftPanel(component, result);
+ break;
+ case SERVICE:
+ result = new Service();
+ copyFieldsForLeftPanel(component, result);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ private Component filterFieldsForCatalog(Component component) {
+
+ Component result = null;
+ ComponentTypeEnum componentTypeEnum = component.getComponentType();
+ switch (componentTypeEnum) {
+ case RESOURCE:
+ result = new Resource();
+ copyFieldsForCatalog(component, result);
+ break;
+ case SERVICE:
+ result = new Service();
+ copyFieldsForCatalog(component, result);
+ break;
+ case PRODUCT:
+ result = new Product();
+ copyFieldsForCatalog(component, result);
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ /**
+ * Copy relevant fields to the filtered component for left panel
+ *
+ * @param component
+ * @param filteredComponent
+ */
+ private void copyFieldsForLeftPanel(Component component, Component filteredComponent) {
+
+ ComponentTypeEnum componentTypeEnum = component.getComponentType();
+ filteredComponent.setCategories(component.getCategories());
+ filteredComponent.setComponentType(component.getComponentType());
+ if (ComponentTypeEnum.RESOURCE.equals(component.getComponentType())
+ && ResourceTypeEnum.VL.equals(((ResourceMetadataDataDefinition) component
+ .getComponentMetadataDefinition().getMetadataDataDefinition()).getResourceType())) {
+ filteredComponent.setCapabilities(component.getCapabilities());
+ filteredComponent.setRequirements(component.getRequirements());
+ }
+ filteredComponent.setVersion(component.getVersion());
+ filteredComponent.setDescription(component.getDescription());
+ filteredComponent.setUniqueId(component.getUniqueId());
+ filteredComponent.setIcon(component.getIcon());
+ filteredComponent.setTags(component.getTags());
+ // filteredComponent.setAllVersions(component.getAllVersions());
+ filteredComponent.setLifecycleState(component.getLifecycleState());
+ // filteredComponent.setHighestVersion(component.isHighestVersion());
+ filteredComponent.setInvariantUUID(component.getInvariantUUID());
+ filteredComponent.setUUID(component.getUUID());
+ filteredComponent.setSystemName(component.getSystemName());
+ filteredComponent.setName(component.getName());
+
+ if (componentTypeEnum == ComponentTypeEnum.RESOURCE) {
+ Resource resource = (Resource) component;
+ Resource filteredResource = (Resource) filteredComponent;
+ filteredResource.setToscaResourceName(resource.getToscaResourceName());
+ // filteredResource.setAbstract(resource.isAbstract());
+ // filteredResource.setVendorName(resource.getVendorName());
+ // filteredResource.setVendorRelease(resource.getVendorRelease());
+ filteredResource.setResourceType(resource.getResourceType());
+ } else if (componentTypeEnum == ComponentTypeEnum.SERVICE) {
+ // Service service = (Service)component;
+ // Service filteredService = (Service)filteredComponent;
+ // filteredService.setDistributionStatus(service.getDistributionStatus());
+ }
+ }
+
+ private void copyFieldsForCatalog(Component component, Component filteredComponent) {
+
+ ComponentTypeEnum componentTypeEnum = component.getComponentType();
+ filteredComponent.setCategories(component.getCategories());
+ filteredComponent.setComponentType(component.getComponentType());
+ filteredComponent.setVersion(component.getVersion());
+ filteredComponent.setDescription(component.getDescription());
+ filteredComponent.setUniqueId(component.getUniqueId());
+ filteredComponent.setIcon(component.getIcon());
+ filteredComponent.setTags(component.getTags());
+ // filteredComponent.setAllVersions(component.getAllVersions());
+ filteredComponent.setLifecycleState(component.getLifecycleState());
+ // filteredComponent.setHighestVersion(component.isHighestVersion());
+ // filteredComponent.setInvariantUUID(component.getInvariantUUID());
+ filteredComponent.setSystemName(component.getSystemName());
+ filteredComponent.setName(component.getName());
+ filteredComponent.setLastUpdateDate(component.getLastUpdateDate());
+
+ if (componentTypeEnum == ComponentTypeEnum.RESOURCE) {
+ Resource resource = (Resource) component;
+ Resource filteredResource = (Resource) filteredComponent;
+ filteredResource.setToscaResourceName(resource.getToscaResourceName());
+ // filteredResource.setAbstract(resource.isAbstract());
+ // filteredResource.setVendorName(resource.getVendorName());
+ // filteredResource.setVendorRelease(resource.getVendorRelease());
+ filteredResource.setResourceType(resource.getResourceType());
+ } else if (componentTypeEnum == ComponentTypeEnum.SERVICE) {
+ Service service = (Service) component;
+ Service filteredService = (Service) filteredComponent;
+ filteredService.setDistributionStatus(service.getDistributionStatus());
+ }
+ }
+
+ /**
+ * get components from cache of a given list ou unique ids.
+ *
+ * for each component data from cassandra, unzip the data if needed and
+ * deserialize the unzipped data to java object(Component).
+ *
+ * @param filteredResources
+ * @return ImmutableTripple or ActionStatus. | |-- components |-- dirty
+ * components - components with dirty flag = true. |-- set of non
+ * cached components
+ *
+ */
+ private Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> getComponentsFull(
+ Set<String> filteredResources) {
+
+ if (false == isEnabled()) {
+ logger.debug("Component Cache is disabled");
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+
+ List<Component> foundResources = new LinkedList<>();
+ List<Component> foundDirtyResources = new LinkedList<>();
+ Set<String> notFoundResources = new HashSet<>();
+ ImmutableTriple<List<Component>, List<Component>, Set<String>> result = new ImmutableTriple<List<Component>, List<Component>, Set<String>>(
+ foundResources, foundDirtyResources, notFoundResources);
+
+ long cassandraFetchStart = System.currentTimeMillis();
+ List<String> uidsList = new ArrayList<>();
+ uidsList.addAll(filteredResources);
+ Either<List<ComponentCacheData>, ActionStatus> componentsFromCache = componentCassandraDao
+ .getComponents(uidsList);
+
+ long cassandraFetchEnd = System.currentTimeMillis();
+ logger.debug("Fetch time from cassandara of all components took {} ms",
+ (cassandraFetchEnd - cassandraFetchStart));
+ if (componentsFromCache.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("FetchFromCache",
+ "Failed to fetch components from cache", ErrorSeverity.ERROR);
+ return Either.right(componentsFromCache.right().value());
+ }
+
+ List<ComponentCacheData> list = componentsFromCache.left().value();
+ logger.debug("Number of components fetched from cassandra is {}", (list == null ? 0 : list.size()));
+ if (list != null && false == list.isEmpty()) {
+
+ List<ComponentCacheData> filteredData = list.stream().filter(p -> filteredResources.contains(p.getId()))
+ .collect(Collectors.toList());
+ logger.debug("Number of components filterd is {}", filteredData == null ? 0 : filteredData.size());
+
+ if (filteredData != null) {
+ long desStart = System.currentTimeMillis();
+
+ for (ComponentCacheData componentCacheData : filteredData) {
+
+ logger.debug("Process uid {} from cache", componentCacheData.getId());
+
+ String compUid = componentCacheData.getId();
+
+ Either<? extends Component, Boolean> deserializeExt = convertComponentCacheToComponent(
+ componentCacheData);
+
+ if (deserializeExt.isLeft()) {
+ Component component = deserializeExt.left().value();
+ if (false == componentCacheData.getIsDirty()) {
+ foundResources.add(component);
+ } else {
+ foundDirtyResources.add(component);
+ }
+ } else {
+ notFoundResources.add(compUid);
+ }
+
+ }
+ long desEnd = System.currentTimeMillis();
+ logger.debug("Deserialization and unzip of {} components took {} ms", filteredData.size(),
+ (desEnd - desStart));
+ }
+ }
+ List<String> foundResourcesUid = foundResources.stream().map(p -> p.getUniqueId()).collect(Collectors.toList());
+ List<String> foundDirtyResourcesUid = foundDirtyResources.stream().map(p -> p.getUniqueId())
+ .collect(Collectors.toList());
+ logger.debug("Number of processed components from cache is {}",
+ (foundResourcesUid.size() + foundDirtyResourcesUid.size()));
+ Set<String> notCachedResources = filteredResources.stream()
+ .filter(p -> false == foundResourcesUid.contains(p) && false == foundDirtyResourcesUid.contains(p))
+ .collect(Collectors.toSet());
+ notFoundResources.addAll(notCachedResources);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Number of components fetched is {}", foundResources.size());
+ logger.debug("Number of components fetched dirty is {}", foundDirtyResources.size());
+ logger.debug("Number of components non cached is {}", notCachedResources.size());
+ }
+
+ return Either.left(result);
+ }
+
+ private Either<? extends Component, Boolean> convertComponentCacheToComponent(
+ ComponentCacheData componentCacheData) {
+
+ String compUid = componentCacheData.getId();
+
+ byte[] dataAsArray = componentCacheData.getDataAsArray();
+
+ if (true == componentCacheData.getIsZipped()) {
+ long startUnzip = System.nanoTime();
+ dataAsArray = ZipUtil.unzip(dataAsArray);
+ long endUnzip = System.nanoTime();
+ logger.trace("Unzip component {} took {} microsecond", compUid, (endUnzip - startUnzip) / 1000);
+ }
+
+ long startDes = System.nanoTime();
+
+ Either<? extends Component, Boolean> deserializeExt = deserializeComponent(componentCacheData, dataAsArray);
+
+ long endDes = System.nanoTime();
+ logger.trace("Deserialize component {} took {} microsecond", compUid, (endDes - startDes) / 1000);
+ return deserializeExt;
+ }
+
+ private Either<? extends Component, Boolean> deserializeComponent(ComponentCacheData componentCacheData,
+ byte[] dataAsArray) {
+ String type = componentCacheData.getType();
+ NodeTypeEnum typeEnum = NodeTypeEnum.getByNameIgnoreCase(type);
+
+ Either<? extends Component, Boolean> deserializeExt = Either.right(false);
+ switch (typeEnum) {
+ case Resource:
+ deserializeExt = SerializationUtils.deserializeExt(dataAsArray, Resource.class, componentCacheData.getId());
+ break;
+ case Service:
+ deserializeExt = SerializationUtils.deserializeExt(dataAsArray, Service.class, componentCacheData.getId());
+ break;
+ case Product:
+ deserializeExt = SerializationUtils.deserializeExt(dataAsArray, Product.class, componentCacheData.getId());
+ break;
+ default:
+ break;
+ }
+ return deserializeExt;
+ }
+
+ public Either<Component, ActionStatus> getComponent(String componentUid) {
+
+ return getComponent(componentUid, null, Function.identity());
+
+ }
+
+ public Either<Component, ActionStatus> getComponent(String componentUid, Long lastModificationTime) {
+
+ return getComponent(componentUid, lastModificationTime, Function.identity());
+
+ }
+
+ public boolean setComponent(String componentUid, Long lastModificationTime, NodeTypeEnum nodeTypeEnum) {
+
+ boolean result = false;
+
+ if (false == isEnabled()) {
+ logger.debug("Component Cache is disabled");
+ return false;
+ }
+
+ ComponentOperation componentOperation = getComponentOperation(nodeTypeEnum);
+
+ if (componentOperation == null) {
+ return false;
+ }
+
+ Either<Component, StorageOperationStatus> either = componentOperation.getComponent(componentUid, false);
+ if (either.isLeft()) {
+ Component component = either.left().value();
+ result = saveComponent(componentUid, lastModificationTime, nodeTypeEnum, component);
+ } else {
+ logger.debug("Failed to get component {} of type {} from graph. Status is {}", componentUid,
+ nodeTypeEnum.name().toLowerCase(), either.right().value());
+ }
+
+ return result;
+
+ }
+
+ private boolean saveComponent(String componentUid, Long lastModificationTime, NodeTypeEnum nodeTypeEnum,
+ Component component) {
+
+ logger.trace("Going to save component {} of type {} in cache", componentUid, nodeTypeEnum.name().toLowerCase());
+
+ boolean result = false;
+
+ Either<byte[], Boolean> serializeExt = SerializationUtils.serializeExt(component);
+ if (serializeExt.isLeft()) {
+ byte[] serializedData = serializeExt.left().value();
+ byte[] zipBytes;
+ try {
+ zipBytes = ZipUtil.zipBytes(serializedData);
+ ComponentCacheData componentCacheData = new ComponentCacheData();
+ componentCacheData.setDataAsArray(zipBytes);
+ componentCacheData.setIsZipped(true);
+ componentCacheData.setId(componentUid);
+ componentCacheData.setModificationTime(new Date(lastModificationTime));
+ componentCacheData.setType(component.getComponentType().name().toLowerCase());
+
+ CassandraOperationStatus status = componentCassandraDao.saveComponent(componentCacheData);
+
+ if (status == CassandraOperationStatus.OK) {
+ result = true;
+ }
+
+ } catch (IOException e) {
+ logger.debug("Failed to prepare component {} of type {} for cache", componentUid,
+ nodeTypeEnum.name().toLowerCase());
+ if (logger.isTraceEnabled()) {
+ logger.trace("Failed to prepare component " + componentUid + " of type "
+ + nodeTypeEnum.name().toLowerCase() + " for cache");
+ }
+ }
+ } else {
+ logger.debug("Failed to serialize component {} of type {} for cache", componentUid,
+ nodeTypeEnum.name().toLowerCase());
+ }
+ return result;
+ }
+
+ public boolean setComponent(Component component, NodeTypeEnum nodeTypeEnum) {
+
+ boolean result = false;
+
+ if (false == isEnabled()) {
+ logger.debug("Component Cache is disabled");
+ return false;
+ }
+
+ String componentUid = component.getUniqueId();
+ Long lastUpdateDate = component.getLastUpdateDate();
+
+ result = saveComponent(componentUid, lastUpdateDate, nodeTypeEnum, component);
+
+ return result;
+
+ }
+
+ private ComponentOperation getComponentOperation(NodeTypeEnum nodeTypeEnum) {
+ ComponentOperation componentOperation = null;
+ switch (nodeTypeEnum) {
+ case Resource:
+ componentOperation = resourceOperation;
+ break;
+ case Service:
+ componentOperation = serviceOperation;
+ break;
+ case Product:
+ componentOperation = productOperation;
+ break;
+ default:
+ break;
+ }
+ return componentOperation;
+ }
+
+ /**
+ * get components from cache of a given list ou unique ids.
+ *
+ * for each component data from cassandra, unzip the data if needed and
+ * deserialize the unzipped data to java object(Component).
+ *
+ * @param filteredResources
+ * @return ImmutableTripple or ActionStatus. | |-- components |-- set of non
+ * cached components
+ *
+ */
+ private Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> getComponentsFull(
+ Map<String, Long> filteredResources) {
+
+ if (false == isEnabled()) {
+ logger.debug("Component Cache is disabled");
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+
+ List<Component> foundResources = new LinkedList<>();
+ Set<String> notFoundResources = new HashSet<>();
+ ImmutablePair<List<Component>, Set<String>> result = new ImmutablePair<List<Component>, Set<String>>(
+ foundResources, notFoundResources);
+
+ long cassandraFetchStart = System.currentTimeMillis();
+
+ Either<ImmutablePair<List<ComponentCacheData>, Set<String>>, ActionStatus> componentsFromCache = componentCassandraDao
+ .getComponents(filteredResources);
+
+ long cassandraFetchEnd = System.currentTimeMillis();
+ logger.debug("Fetch time from cassandara of all components took {} ms",
+ (cassandraFetchEnd - cassandraFetchStart));
+ if (componentsFromCache.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("FetchFromCache",
+ "Failed to fetch components from cache", ErrorSeverity.ERROR);
+ return Either.right(componentsFromCache.right().value());
+ }
+
+ ImmutablePair<List<ComponentCacheData>, Set<String>> immutablePair = componentsFromCache.left().value();
+ List<ComponentCacheData> list = immutablePair.getLeft();
+ logger.debug("Number of components fetched from cassandra is {}", (list == null ? 0 : list.size()));
+ if (list != null && false == list.isEmpty()) {
+
+ // List<ComponentCacheData> filteredData = list.stream().filter(p ->
+ // filteredResources.contains(p.getId())).collect(Collectors.toList());
+ logger.debug("Number of components filterd is {}", list == null ? 0 : list.size());
+
+ if (list != null) {
+ long desStart = System.currentTimeMillis();
+
+ for (ComponentCacheData componentCacheData : list) {
+
+ logger.debug("Process uid {} from cache", componentCacheData.getId());
+
+ String compUid = componentCacheData.getId();
+
+ Either<? extends Component, Boolean> deserializeExt = convertComponentCacheToComponent(
+ componentCacheData);
+
+ if (deserializeExt.isLeft()) {
+ Component component = deserializeExt.left().value();
+ foundResources.add(component);
+ } else {
+ notFoundResources.add(compUid);
+ }
+
+ }
+ long desEnd = System.currentTimeMillis();
+ logger.debug("Deserialization and unzip of {} components took {} ms", list.size(), (desEnd - desStart));
+ }
+ }
+ logger.debug("Number of processed components from cache is {}", foundResources.size());
+
+ Set<String> notFoundInCache = immutablePair.getRight();
+ notFoundResources.addAll(notFoundInCache);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Number of components fetched is {}", foundResources.size());
+ logger.debug("Number of components non cached is {}", notFoundResources.size());
+ }
+
+ return Either.left(result);
+ }
+
+ /**
+ * get components for catalog
+ *
+ * @param components
+ * @param componentTypeEnum
+ * @return
+ */
+ public Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> getComponentsForCatalog(
+ Map<String, Long> components, ComponentTypeEnum componentTypeEnum) {
+
+ if (false == isEnabled()) {
+ logger.debug("In getComponentsForCatalog for type {}. Cache is disabled.",
+ componentTypeEnum.name().toLowerCase());
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+ logger.debug("In getComponentsForCatalog for type {}", componentTypeEnum.name().toLowerCase());
+
+ Function<List<Component>, List<Component>> filterFieldsFunc = x -> filterForCatalog(x);
+
+ Map<String, Long> leftComponentsForSearch = new HashMap<>();
+ leftComponentsForSearch.putAll(components);
+
+ // get components from inmemory cache
+ List<Component> componentsFromMemory = null;
+ if (true == catalogInMemoryEnabled) {
+ componentsFromMemory = getDataFromInMemoryCache(components.keySet(), componentTypeEnum);
+ logger.debug("The number of components of type {} fetched from memory is {}",
+ componentTypeEnum.name().toLowerCase(),
+ componentsFromMemory == null ? 0 : componentsFromMemory.size());
+ if (componentsFromMemory != null) {
+ List<String> ignoredComponents = new ArrayList<>();
+ for (Component componentFromMem : componentsFromMemory) {
+ if (componentFromMem.getLastUpdateDate().longValue() != components
+ .get(componentFromMem.getUniqueId()).longValue()) {
+ // Ignore the component from memory
+ ignoredComponents.add(componentFromMem.getUniqueId());
+ }
+ }
+
+ logger.debug("Number of components from type {} ignored from memory cache is {}",
+ componentTypeEnum.name().toLowerCase(), ignoredComponents.size());
+ // remove from memory result the components which are not valid
+ componentsFromMemory = componentsFromMemory.stream()
+ .filter(p -> false == ignoredComponents.contains(p.getUniqueId())).collect(Collectors.toList());
+ // Remove from leftComponentsForSearch the valid components from
+ // memory
+ componentsFromMemory.forEach(p -> leftComponentsForSearch.remove(p.getUniqueId()));
+
+ }
+ } else {
+ logger.debug("Catalog InMemory cache is disabled");
+ }
+
+ logger.debug("Number of components from type {} needed to fetch is {}", componentTypeEnum.name().toLowerCase(),
+ leftComponentsForSearch.size());
+
+ // get components from cassandra cache and filter each component
+ Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> result = getComponents(
+ leftComponentsForSearch, filterFieldsFunc);
+
+ if (result.isLeft()) {
+ // add inmemory components to the valid components(not dirty)
+ List<Component> foundComponents = result.left().value().getLeft();
+ if (componentsFromMemory != null) {
+ foundComponents.addAll(componentsFromMemory);
+ }
+ if (true == catalogInMemoryEnabled) {
+ updateCatalogInMemoryCacheWithCertified(foundComponents, componentTypeEnum);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @param components
+ * - Map of <componentUniqueId, last update date>
+ * @param filterFieldsFunc
+ * @return
+ */
+ public Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> getComponents(Map<String, Long> components,
+ Function<List<Component>, List<Component>> filterFieldsFunc) {
+
+ if (false == isEnabled()) {
+ logger.debug("Component Cache is disabled");
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+
+ Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> componentsFull = getComponentsFull(
+ components);
+
+ if (componentsFull.isRight()) {
+ return Either.right(componentsFull.right().value());
+ }
+
+ ImmutablePair<List<Component>, Set<String>> immutablePair = componentsFull.left().value();
+ List<Component> foundResources = immutablePair.left;
+ Set<String> notFoundResources = immutablePair.right;
+
+ List<Component> filterdFoundResources = filterFieldsFunc.apply(foundResources);
+
+ ImmutablePair<List<Component>, Set<String>> result = new ImmutablePair<List<Component>, Set<String>>(
+ filterdFoundResources, notFoundResources);
+
+ return Either.left(result);
+
+ }
+
+ /**
+ * get the component and its modification time from cache
+ *
+ * @param componentUid
+ * @param filterFieldsFunc
+ * @return
+ */
+ public Either<ImmutablePair<Component, Long>, ActionStatus> getComponentAndTime(String componentUid,
+ Function<Component, Component> filterFieldsFunc) {
+
+ Either<ImmutablePair<Component, ComponentCacheData>, ActionStatus> componentFromCache = getComponentFromCache(
+ componentUid, null, filterFieldsFunc);
+
+ if (componentFromCache.isRight()) {
+ return Either.right(componentFromCache.right().value());
+ }
+
+ ImmutablePair<Component, ComponentCacheData> immutablePair = componentFromCache.left().value();
+
+ ImmutablePair<Component, Long> result = new ImmutablePair<Component, Long>(immutablePair.left,
+ immutablePair.right.getModificationTime().getTime());
+
+ return Either.left(result);
+ }
+
+ private Either<ImmutablePair<Component, ComponentCacheData>, ActionStatus> getComponentFromCache(
+ String componentUid, Long lastModificationTime, Function<Component, Component> filterFieldsFunc) {
+ if (false == isEnabled()) {
+ return Either.right(ActionStatus.NOT_ALLOWED);
+ }
+
+ Either<ComponentCacheData, ActionStatus> componentRes = componentCassandraDao.getComponent(componentUid);
+
+ if (componentRes.isRight()) {
+ return Either.right(componentRes.right().value());
+ }
+
+ ComponentCacheData componentCacheData = componentRes.left().value();
+
+ if (lastModificationTime != null) {
+ long cacheCompModificationTime = componentCacheData.getModificationTime().getTime();
+ if (lastModificationTime != cacheCompModificationTime) {
+ logger.debug(
+ "Component {} found in cache but its modification time {} does not match to the timestamp in cache {}.",
+ componentUid, lastModificationTime, cacheCompModificationTime);
+ return Either.right(ActionStatus.INVALID_CONTENT);
+ }
+ }
+
+ Either<? extends Component, Boolean> convertRes = convertComponentCacheToComponent(componentCacheData);
+ if (convertRes.isRight()) {
+ return Either.right(ActionStatus.CONVERT_COMPONENT_ERROR);
+ }
+
+ Component component = convertRes.left().value();
+
+ Component filteredComponent = component;
+ if (filterFieldsFunc != null) {
+ filteredComponent = filterFieldsFunc.apply(component);
+ }
+
+ ImmutablePair<Component, ComponentCacheData> result = new ImmutablePair<Component, ComponentCacheData>(
+ filteredComponent, componentCacheData);
+
+ return Either.left(result);
+ }
+
+ public ActionStatus deleteComponentFromCache(String id) {
+ if (false == isEnabled()) {
+ return ActionStatus.NOT_ALLOWED;
+ }
+ CassandraOperationStatus status = this.componentCassandraDao.deleteComponent(id);
+ if (CassandraOperationStatus.OK.equals(status)) {
+ return ActionStatus.OK;
+ } else {
+ logger.debug("delete component failed with error {}", status);
+ return ActionStatus.GENERAL_ERROR;
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/DaoInfo.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/DaoInfo.java
new file mode 100644
index 0000000000..bc63b34fec
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/DaoInfo.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache;
+
+import org.openecomp.sdc.be.model.operations.api.IProductOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+
+public class DaoInfo {
+ private IResourceOperation iResourceOperation;
+ private IServiceOperation iServiceOperation;
+ private IProductOperation iProductOperation;
+ private ComponentCache ComponentCache;
+
+ public DaoInfo(IResourceOperation iResourceOperation, IServiceOperation iServiceOperation,
+ IProductOperation iProductOperation, org.openecomp.sdc.be.model.cache.ComponentCache componentCache) {
+ this.iResourceOperation = iResourceOperation;
+ this.iServiceOperation = iServiceOperation;
+ this.iProductOperation = iProductOperation;
+ ComponentCache = componentCache;
+ }
+
+ public IResourceOperation getResourceOperation() {
+ return iResourceOperation;
+ }
+
+ public IServiceOperation getServiceOperation() {
+ return iServiceOperation;
+ }
+
+ public IProductOperation getProductOperation() {
+ return iProductOperation;
+ }
+
+ public org.openecomp.sdc.be.model.cache.ComponentCache getComponentCache() {
+ return ComponentCache;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/CheckAndUpdateJob.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/CheckAndUpdateJob.java
new file mode 100644
index 0000000000..93249c914a
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/CheckAndUpdateJob.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.jobs;
+
+import fj.data.Either;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.cache.DaoInfo;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.function.Function;
+
+/**
+ * Created by mlando on 9/7/2016.
+ */
+public class CheckAndUpdateJob extends Job {
+ private static Logger log = LoggerFactory.getLogger(CheckAndUpdateJob.class.getName());
+
+ public CheckAndUpdateJob(DaoInfo daoInfo, String componentId, NodeTypeEnum nodeTypeEnum, long timestamp) {
+ super(daoInfo, componentId, nodeTypeEnum, timestamp);
+ }
+
+ @Override
+ public Object doWork() {
+ log.trace("starting work on job.");
+ log.trace("update cache for componentId:{} of nodeTypeEnum:{} with timestamp:{}.", componentId, nodeTypeEnum,
+ timestamp);
+
+ try {
+
+ // get from cache
+ Either<ImmutablePair<Component, Long>, ActionStatus> cacheResult = daoInfo.getComponentCache()
+ .getComponentAndTime(componentId, Function.identity());
+ // if error while getting from cache abort and update
+ if (cacheResult.isRight()) {
+ // genral error
+ if (!ActionStatus.RESOURCE_NOT_FOUND.equals(cacheResult.right().value())
+ && !ActionStatus.INVALID_CONTENT.equals(cacheResult.right().value())) {
+ log.debug("failed to get component:{} from cache error:{}", componentId,
+ cacheResult.right().value());
+ return false;
+ }
+ // component not in cache put there
+ else {
+ return updateCache(componentId, nodeTypeEnum, timestamp);
+ }
+ }
+ ImmutablePair<Component, Long> recored = cacheResult.left().value();
+ // the cache has allready been updated exit
+ if (this.timestamp < recored.getRight()) {
+ log.debug("job timestemp:{} is smaller then the cache timestamp:{} no update is needed.",
+ this.timestamp, recored.getRight());
+ return false;
+ }
+ return updateCache(componentId, nodeTypeEnum, timestamp);
+
+ } catch (Exception e) {
+ log.debug("an exception was encountered during CheckAndUpdateJob", e);
+ } finally {
+ daoInfo.getResourceOperation().getTitanGenericDao().commit();
+ }
+ return false;
+ }
+
+ /**
+ * @param componentId
+ * @param nodeTypeEnum
+ * @return
+ */
+ private boolean updateCache(String componentId, NodeTypeEnum nodeTypeEnum, Long timestamp) {
+ // get component from cache
+ Either<ComponentMetadataData, StorageOperationStatus> metaDataRes = getComponentMetaData(componentId,
+ nodeTypeEnum);
+ if (metaDataRes.isRight()) {
+ return false;
+ }
+ ComponentMetadataData metaData = metaDataRes.left().value();
+ // the job time is older then the one on graph nothing to do there is a
+ // job that will handle this.
+ Long graphTimestamp = metaData.getMetadataDataDefinition().getLastUpdateDate();
+ if (timestamp < graphTimestamp) {
+ log.debug(
+ "the job timestamp:{} is smaller then the graph timestamp:{}. exiting because another job will update the cache.",
+ timestamp, graphTimestamp);
+ return false;
+ } else {
+ // update cache
+ // get component from grath
+ Either<Component, StorageOperationStatus> componentRes = getOperationByType(nodeTypeEnum)
+ .getComponent(componentId, true);
+ if (componentRes.isRight()) {
+ log.debug("failed to get full component:{} from graph status:{}", componentId,
+ componentRes.right().value());
+ return false;
+ }
+ Component component = componentRes.left().value();
+ // store in cache
+ if (!this.daoInfo.getComponentCache().setComponent(component, nodeTypeEnum)) {
+ log.debug("failed to store componentId:{} nodeTypeEnum:", componentId, nodeTypeEnum);
+ return false;
+ }
+ }
+ log.debug("cache successfully updated for componentId:{} nodeTypeEnum:{} timestemp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ return true;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/DeleteJob.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/DeleteJob.java
new file mode 100644
index 0000000000..ac1a56f9db
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/DeleteJob.java
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.jobs;
+
+import fj.data.Either;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.cache.DaoInfo;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by mlando on 9/20/2016.
+ */
+public class DeleteJob extends Job {
+ private static Logger log = LoggerFactory.getLogger(DeleteJob.class.getName());
+
+ public DeleteJob(DaoInfo daoInfo, String componentId, NodeTypeEnum nodeTypeEnum, long timestamp) {
+ super(daoInfo, componentId, nodeTypeEnum, timestamp);
+
+ }
+
+ @Override
+ public Object doWork() {
+ try {
+ log.trace("starting work on job.");
+ log.trace("delete component in cache, componentId:{} of nodeTypeEnum:{} with timestamp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ ActionStatus status = this.daoInfo.getComponentCache().deleteComponentFromCache(componentId);
+ if (!ActionStatus.OK.equals(status)) {
+ log.debug("failed to delete componentId:{} nodeTypeEnum:", componentId, nodeTypeEnum);
+ return false;
+ }
+ log.trace("cache successfully deleted componentId:{} nodeTypeEnum:{} timestamp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ return true;
+ } catch (Exception e) {
+ log.debug("an exception was encountered durring deletejob", e);
+ }
+ return false;
+
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/Job.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/Job.java
new file mode 100644
index 0000000000..4deda8642f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/Job.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.jobs;
+
+import fj.data.Either;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.cache.DaoInfo;
+import org.openecomp.sdc.be.model.operations.api.IComponentOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class Job<E> {
+ private static Logger log = LoggerFactory.getLogger(Job.class.getName());
+ protected DaoInfo daoInfo;
+ protected String componentId;
+ protected long timestamp;
+ protected NodeTypeEnum nodeTypeEnum;
+
+ protected Job(DaoInfo daoInfo, String componentId, NodeTypeEnum nodeTypeEnum, long timestamp) {
+ this.daoInfo = daoInfo;
+ this.componentId = componentId;
+ this.timestamp = timestamp;
+ this.nodeTypeEnum = nodeTypeEnum;
+ }
+
+ protected Job(DaoInfo daoInfo, Component component, NodeTypeEnum nodeTypeEnum) {
+ this.daoInfo = daoInfo;
+ this.componentId = component.getUniqueId();
+ this.timestamp = component.getLastUpdateDate();
+ this.nodeTypeEnum = nodeTypeEnum;
+ }
+
+ public abstract E doWork();
+
+ protected IComponentOperation getOperationByType(NodeTypeEnum nodeTypeEnum) {
+ IComponentOperation operation = null;
+ switch (nodeTypeEnum) {
+ case Product:
+ operation = daoInfo.getProductOperation();
+ break;
+ case Service:
+ operation = daoInfo.getServiceOperation();
+ break;
+ case Resource:
+ operation = daoInfo.getResourceOperation();
+ break;
+ default:
+ log.error("unexpected NodeType received no matching operation found.");
+ }
+ return operation;
+ }
+
+ protected Either<ComponentMetadataData, StorageOperationStatus> getComponentMetaData(String componentId,
+ NodeTypeEnum nodeTypeEnum) {
+ Either<ComponentMetadataData, StorageOperationStatus> metaDataRes = getOperationByType(nodeTypeEnum)
+ .getComponentByLabelAndId(componentId, nodeTypeEnum, ComponentMetadataData.class);
+ if (metaDataRes.isRight()) {
+ // in case we cant find the component on graph exit
+ if (StorageOperationStatus.NOT_FOUND.equals(metaDataRes.right().value())) {
+ log.debug("failed to locate component:{} on graph status:{}", componentId, metaDataRes.right().value());
+ } else {
+ log.debug("failed to get component:{} from graph status:{}", componentId, metaDataRes.right().value());
+ }
+ }
+ return metaDataRes;
+ }
+
+ protected NodeTypeEnum getNodeTypeFromComponentType(ComponentTypeEnum type) {
+ NodeTypeEnum result = null;
+ switch (type) {
+ case PRODUCT:
+ result = NodeTypeEnum.Product;
+ break;
+ case RESOURCE:
+ result = NodeTypeEnum.Resource;
+ break;
+ case SERVICE:
+ result = NodeTypeEnum.Service;
+ break;
+ default:
+
+ }
+ return result;
+
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/OverrideJob.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/OverrideJob.java
new file mode 100644
index 0000000000..e9da68ea59
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/OverrideJob.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.jobs;
+
+import fj.data.Either;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.cache.DaoInfo;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by mlando on 9/20/2016.
+ */
+public class OverrideJob extends Job {
+ private static Logger log = LoggerFactory.getLogger(OverrideJob.class.getName());
+
+ public OverrideJob(DaoInfo daoInfo, String componentId, NodeTypeEnum nodeTypeEnum, long timestamp) {
+ super(daoInfo, componentId, nodeTypeEnum, timestamp);
+
+ }
+
+ @Override
+ public Object doWork() {
+ try {
+ log.trace("starting work on job.");
+ log.trace("override component in cache, componentId:{} of nodeTypeEnum:{} with timestamp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ // get component from grath
+ Either<Component, StorageOperationStatus> componentRes = getOperationByType(nodeTypeEnum)
+ .getComponent(componentId, false);
+ if (componentRes.isRight()) {
+ log.debug("failed to get full component:{} from graph status:{}", componentId,
+ componentRes.right().value());
+ return false;
+ }
+ Component component = componentRes.left().value();
+ // store in cache
+ if (!this.daoInfo.getComponentCache().setComponent(component, nodeTypeEnum)) {
+ log.debug("failed to store componentId:{} nodeTypeEnum:", componentId, nodeTypeEnum);
+ return false;
+ }
+ log.debug("cache successfully overrided componentId:{} nodeTypeEnum:{} timestemp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ return true;
+ } catch (Exception e) {
+ log.debug("an exception was encountered during OverrideJob", e);
+ } finally {
+ this.daoInfo.getResourceOperation().getTitanGenericDao().commit();
+ }
+ return false;
+
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/StoreJob.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/StoreJob.java
new file mode 100644
index 0000000000..410a56a90e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/jobs/StoreJob.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.jobs;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.cache.DaoInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by mlando on 9/11/2016.
+ */
+public class StoreJob extends Job {
+ private static Logger log = LoggerFactory.getLogger(StoreJob.class.getName());
+ private Component component;
+
+ public StoreJob(DaoInfo daoInfo, Component component, NodeTypeEnum nodeTypeEnum) {
+ super(daoInfo, component, nodeTypeEnum);
+ this.component = component;
+ }
+
+ @Override
+ public Object doWork() {
+ try {
+ log.trace("starting work on job.");
+ log.trace("store component in cache, componentId:{} of nodeTypeEnum:{} with timestamp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ if (!this.daoInfo.getComponentCache().setComponent(component, nodeTypeEnum)) {
+ log.debug("failed to store componentId:{} nodeTypeEnum:", componentId, nodeTypeEnum);
+ return false;
+ }
+ log.debug("cache successfully updated for componentId:{} nodeTypeEnum:{} timestemp:{}.", componentId,
+ nodeTypeEnum, timestamp);
+ return true;
+
+ } catch (Exception e) {
+ log.debug("an exception was encountered during StoreJob", e);
+ }
+ return false;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/CacheWorker.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/CacheWorker.java
new file mode 100644
index 0000000000..7d6ff49507
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/CacheWorker.java
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.workers;
+
+import org.openecomp.sdc.be.model.cache.jobs.Job;
+import org.openecomp.sdc.be.model.cache.workers.IWorker;
+import org.openecomp.sdc.be.workers.Worker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by mlando on 9/6/2016. the class represents a worker the pull job
+ * from a queue and evacuates them.
+ *
+ */
+public class CacheWorker implements Runnable, IWorker {
+ private String workerName;
+ private static Logger log = LoggerFactory.getLogger(Worker.class.getName());
+ private LinkedBlockingQueue<Job> jobQueue;
+ private volatile boolean shutdown = false;
+
+ /**
+ * constructor
+ *
+ * @param workerName
+ * the name of the given worker
+ * @param jobQueue
+ * the queue the worker will block on.
+ */
+ public CacheWorker(String workerName, LinkedBlockingQueue<Job> jobQueue) {
+ this.workerName = workerName;
+ this.jobQueue = jobQueue;
+ }
+
+ /**
+ * the method will try to get a job if one is avilable it will be retrived
+ * and handled. if no jobs are available the worker will block for 500
+ * milliseconds and then it wil check if it needs to shutdown. if not it
+ * will block again and so on until sutdown or a new job is available
+ */
+ @Override
+ public void run() {
+ while (true) {
+ log.trace("CacheWorker:{} doing work", workerName);
+ try {
+ Job job = jobQueue.poll(500, TimeUnit.MILLISECONDS);
+ if (job != null) {
+ job.doWork();
+ log.trace("worker:{} done with work", workerName);
+ }
+ } catch (Throwable e) {
+ log.debug("worker {} failed during job execution.", workerName);
+ log.debug("exception", e);
+ }
+ if (shutdown) {
+ log.debug("worker:{} nothing to do stoping", workerName);
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * the method sets the shutdown flag, when set the worker will stop it's
+ * execution as soon as possible with out completing its work
+ */
+ @Override
+ public void shutDown() {
+ this.shutdown = true;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/IWorker.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/IWorker.java
new file mode 100644
index 0000000000..fcdf9f4148
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/IWorker.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.workers;
+
+/**
+ * Created by mlando on 9/6/2016.
+ */
+public interface IWorker {
+ void shutDown();
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/SyncWorker.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/SyncWorker.java
new file mode 100644
index 0000000000..824dd3496f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/workers/SyncWorker.java
@@ -0,0 +1,266 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.cache.workers;
+
+import fj.data.Either;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.operations.impl.CacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.ComponentCacheData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * the class creates a worker that is used to update cache date, in case of
+ * failures and inconsistencies
+ */
+public class SyncWorker implements Runnable, IWorker {
+
+ private static Logger log = LoggerFactory.getLogger(SyncWorker.class.getName());
+ private final CacheMangerOperation cacheMangerOperation;
+ private final String workerName;
+ private volatile boolean shutdown = false;
+ private Map<String, ComponentCacheData> cacheIdAndTimeMap;
+ private long updateDelayInMilliseconds = 60 * 60 * 1000;
+
+ /**
+ * creates the sync worker
+ *
+ * @param workerName
+ * the name of the worker
+ * @param cacheMangerOperation
+ * responsible for all persistence's operations to graph and the
+ * cache
+ */
+ public SyncWorker(String workerName, CacheMangerOperation cacheMangerOperation) {
+ this.workerName = workerName;
+ this.cacheMangerOperation = cacheMangerOperation;
+ }
+
+ /**
+ * the method collects all the resources/services/products from graph and
+ * checks if the component representing them in the cache is valid logic: if
+ * the record is present in the graph but not in cache -> create a job that
+ * will update the record oin cache if the timestamp of the record in cache
+ * is older than the timestamp on the graph -> create a job that will update
+ * the record oin cache otherwise no update is required
+ */
+ @Override
+ public void run() {
+ try {
+ collectAllCacheRecords();
+ syncCacheByComponentType(NodeTypeEnum.Resource);
+ syncCacheByComponentType(NodeTypeEnum.Service);
+ syncCacheByComponentType(NodeTypeEnum.Product);
+ clearCacheRecords();
+
+ } catch (Exception e) {
+ log.debug("sync worker:{} encounered an exception", workerName);
+ log.debug("exception", e);
+ } finally {
+ this.cacheMangerOperation.getTitanGenericDao().commit();
+ }
+ }
+
+ /**
+ * the method checks for each component in the cache except the ones that
+ * were update during the sync, if they exist on the graph if not a job to
+ * remove them is created
+ */
+ private void clearCacheRecords() {
+ cacheIdAndTimeMap.forEach((k, v) -> {
+ try {
+ Either<ComponentMetadataData, TitanOperationStatus> componentFromGraphRes = getComponentMetaData(k,
+ NodeTypeEnum.getByName(v.getType()));
+ if (componentFromGraphRes.isRight()) {
+ TitanOperationStatus error = componentFromGraphRes.right().value();
+ if (TitanOperationStatus.NOT_FOUND.equals(error)) {
+ long delay = System.currentTimeMillis() - v.getModificationTime().getTime();
+ if (delay > updateDelayInMilliseconds) {
+ this.cacheMangerOperation.deleteComponentInCache(k, v.getModificationTime().getTime(),
+ NodeTypeEnum.getByName(v.getType()));
+ } else {
+ log.trace(
+ "no delete done because an hour did not pass since the delete was done timeSinceUpdate {} < updateDelayInMilliseconds {} ",
+ delay, updateDelayInMilliseconds);
+ }
+ } else {
+ log.debug("failed to get metadata for id:{} from graph error:{}", k, error);
+ }
+ } else {
+ log.trace("id {} is in graph nothing to do");
+ }
+ } catch (Exception e) {
+ log.debug("during clean cache records an exception was thrown", e);
+ }
+ });
+ }
+
+ /**
+ * the method collects all the records from cache except the component
+ * itself
+ */
+ public void collectAllCacheRecords() {
+ Either<List<ComponentCacheData>, ActionStatus> getAllRes = this.cacheMangerOperation.getComponentCache()
+ .getAllComponentIdTimeAndType();
+ if (getAllRes.isRight()) {
+ log.debug("error while trying to get all records from cache error:{}", getAllRes.right().value());
+ cacheIdAndTimeMap = new HashMap<>();
+ } else {
+ cacheIdAndTimeMap = getAllRes.left().value().stream().collect(Collectors.toMap(e -> e.getId(), e -> e));
+ }
+ }
+
+ /**
+ * the method checks that the records ot the given type are sync between the
+ * cache and the graph
+ *
+ * @param nodeTypeEnum
+ * the type of components we want to sync
+ */
+ private void syncCacheByComponentType(NodeTypeEnum nodeTypeEnum) {
+ if (!this.shutdown) {
+ log.trace("syncCache records of type:{} .", nodeTypeEnum);
+ Either<List<ComponentMetadataData>, TitanOperationStatus> getAllResult = getAllComponentsMetaData(
+ nodeTypeEnum);
+ List<ComponentMetadataData> componentList = new ArrayList<>();
+ if (getAllResult.isRight() && !TitanOperationStatus.NOT_FOUND.equals(getAllResult.right().value())) {
+ log.debug("error while trying to get all components of type:{} TitanOperationStatus:{}.", nodeTypeEnum,
+ getAllResult.right().value());
+ return;
+ }
+ if (getAllResult.isLeft()) {
+ componentList = getAllResult.left().value();
+ log.trace("get all components of type:{} returned:{} components.", nodeTypeEnum, componentList.size());
+ }
+ componentList.forEach(this::checkAndUpdateCacheComponent);
+ log.trace("syncCache records of type:{} was successful.", nodeTypeEnum);
+ }
+ }
+
+ /**
+ * the method compares the given component to the record in the cache if the
+ * record is not in the cache a job to update the cache for this record will
+ * be created. if the record is present in the graph but not in cache ->
+ * create a job that will update the record oin cache if the timestamp of
+ * the record in cache is older than the timestamp on the graph -> create a
+ * job that will update the record oin cache if the retried component from
+ * cache fails to be deserialized -> create job to override it otherwise no
+ * update is required
+ *
+ * @param metadataData
+ * the date of the node we want to compare to the value in the
+ * cache
+ */
+ private void checkAndUpdateCacheComponent(ComponentMetadataData metadataData) {
+ long timeSinceUpdate = System.currentTimeMillis()
+ - metadataData.getMetadataDataDefinition().getLastUpdateDate();
+ if (timeSinceUpdate >= updateDelayInMilliseconds) {
+ String uid = metadataData.getMetadataDataDefinition().getUniqueId();
+ log.trace("checking cache if record for uid:{} needs to be updated.", uid);
+ Either<Component, ActionStatus> cacheResult = this.cacheMangerOperation.getComponentCache()
+ .getComponent(uid);
+ if (cacheResult.isRight()) {
+ ActionStatus actionStatus = cacheResult.right().value();
+ if (ActionStatus.RESOURCE_NOT_FOUND.equals(actionStatus)) {
+ log.trace("record for uid:{} not found in cache. creating an update job.", uid);
+ this.cacheMangerOperation.updateComponentInCache(uid,
+ metadataData.getMetadataDataDefinition().getLastUpdateDate(),
+ NodeTypeEnum.getByName(metadataData.getLabel()));
+ } else if (ActionStatus.CONVERT_COMPONENT_ERROR.equals(actionStatus)) {
+ log.trace("uid:{} found in cache but we failed deserializing it. creating an override job .", uid);
+ this.cacheMangerOperation.overideComponentInCache(uid,
+ metadataData.getMetadataDataDefinition().getLastUpdateDate(),
+ NodeTypeEnum.getByName(metadataData.getLabel()));
+ } else {
+ log.debug("during lookup for uid:{} an error accords status:{} .", uid, actionStatus);
+ }
+ } else {
+ log.trace("uid:{} found in cache.", uid);
+ this.cacheIdAndTimeMap.remove(uid);
+ Component cacheComponent = cacheResult.left().value();
+ Long cacheTimestamp = cacheComponent.getLastUpdateDate();
+ Long graphTimestamp = metadataData.getMetadataDataDefinition().getLastUpdateDate();
+ if (cacheTimestamp < graphTimestamp) {
+ log.trace("uid:{} found in cache. cache Timestamp {} < graph timestamp , creating an update job .",
+ uid, cacheTimestamp, graphTimestamp);
+ this.cacheMangerOperation.updateComponentInCache(uid, graphTimestamp,
+ NodeTypeEnum.getByName(metadataData.getLabel()));
+ } else {
+ log.trace("uid:{} found in cache. cache Timestamp {} => graph timestamp , no update is needed .",
+ uid, cacheTimestamp, graphTimestamp);
+ }
+ }
+ } else {
+ log.trace(
+ "no update done because an hour did not pass since the update was done timeSinceUpdate {} < updateDelayInMilliseconds {} ",
+ timeSinceUpdate, updateDelayInMilliseconds);
+ }
+ }
+
+ /**
+ * the method sets the shutdown flag, when set the worker will stop it's
+ * execution as soon as possible with out completing its work
+ */
+ @Override
+ public void shutDown() {
+ log.debug("syncWorker {} shuting down.", workerName);
+ this.shutdown = true;
+ }
+
+ /**
+ * the method retrives all nodes matching the given node type from the graph
+ *
+ * @param nodeTypeEnum
+ * node type we want to lookup on the graph
+ * @return a list of retrieved nodes matching the given type or not found in
+ * case no nodes were found or error in case of failure
+ */
+ private Either<List<ComponentMetadataData>, TitanOperationStatus> getAllComponentsMetaData(
+ NodeTypeEnum nodeTypeEnum) {
+ return this.cacheMangerOperation.getTitanGenericDao().getByCriteria(nodeTypeEnum, null,
+ ComponentMetadataData.class);
+ }
+
+ /**
+ * the method retrieves the metadata from graph for the given id
+ *
+ * @param uid
+ * the unique id of the component we want to retrieve
+ * @param nodeTypeEnum
+ * the type of the recored we want to retrieve
+ * @return the meta dat of the component or the error encountered during the
+ * get
+ */
+ private Either<ComponentMetadataData, TitanOperationStatus> getComponentMetaData(String uid,
+ NodeTypeEnum nodeTypeEnum) {
+ return this.cacheMangerOperation.getTitanGenericDao().getNode(UniqueIdBuilder.getKeyByNodeType(nodeTypeEnum),
+ uid, ComponentMetadataData.class);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/CategoryDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/CategoryDefinition.java
new file mode 100644
index 0000000000..9286344af6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/CategoryDefinition.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.category;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.category.CategoryDataDefinition;
+
+public class CategoryDefinition extends CategoryDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6552733796860992476L;
+
+ List<SubCategoryDefinition> subcategories;
+
+ public CategoryDefinition() {
+ super();
+ }
+
+ public CategoryDefinition(CategoryDataDefinition c) {
+ super(c);
+ }
+
+ public List<SubCategoryDefinition> getSubcategories() {
+ return subcategories;
+ }
+
+ public void setSubcategories(List<SubCategoryDefinition> subcategories) {
+ this.subcategories = subcategories;
+ }
+
+ public void addSubCategory(SubCategoryDefinition subcategory) {
+ if (subcategories == null) {
+ subcategories = new ArrayList<SubCategoryDefinition>();
+ }
+ subcategories.add(subcategory);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " CategoryDefinition [subcategories=" + subcategories + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/GroupingDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/GroupingDefinition.java
new file mode 100644
index 0000000000..aeee0a8972
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/GroupingDefinition.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.category;
+
+import org.openecomp.sdc.be.datatypes.category.GroupingDataDefinition;
+
+public class GroupingDefinition extends GroupingDataDefinition {
+
+ public GroupingDefinition() {
+ super();
+ }
+
+ public GroupingDefinition(GroupingDataDefinition g) {
+ super(g);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/SubCategoryDefinition.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/SubCategoryDefinition.java
new file mode 100644
index 0000000000..14559a1354
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/category/SubCategoryDefinition.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.category;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.category.SubCategoryDataDefinition;
+
+public class SubCategoryDefinition extends SubCategoryDataDefinition {
+
+ private List<GroupingDefinition> groupings;
+
+ public SubCategoryDefinition() {
+ super();
+ }
+
+ public SubCategoryDefinition(SubCategoryDataDefinition subCategory) {
+ super(subCategory);
+ }
+
+ public List<GroupingDefinition> getGroupings() {
+ return groupings;
+ }
+
+ public void setGroupings(List<GroupingDefinition> groupingDefinitions) {
+ this.groupings = groupingDefinitions;
+ }
+
+ public void addGrouping(GroupingDefinition groupingDefinition) {
+ if (groupings == null) {
+ groupings = new ArrayList<GroupingDefinition>();
+ }
+ groupings.add(groupingDefinition);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " SubCategoryDefinition [groupings=" + groupings + "]";
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/heat/HeatParameterType.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/heat/HeatParameterType.java
new file mode 100644
index 0000000000..7de0aa561d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/heat/HeatParameterType.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.heat;
+
+import org.openecomp.sdc.be.model.tosca.converters.DefaultConverter;
+import org.openecomp.sdc.be.model.tosca.converters.HeatBooleanConverter;
+import org.openecomp.sdc.be.model.tosca.converters.HeatCommaDelimitedListConverter;
+import org.openecomp.sdc.be.model.tosca.converters.HeatJsonConverter;
+import org.openecomp.sdc.be.model.tosca.converters.HeatNumberConverter;
+import org.openecomp.sdc.be.model.tosca.converters.HeatStringConverter;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.StringConvertor;
+import org.openecomp.sdc.be.model.tosca.validators.HeatBooleanValidator;
+import org.openecomp.sdc.be.model.tosca.validators.HeatCommaDelimitedListValidator;
+import org.openecomp.sdc.be.model.tosca.validators.HeatNumberValidator;
+import org.openecomp.sdc.be.model.tosca.validators.HeatStringValidator;
+import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
+
+public enum HeatParameterType {
+
+ STRING("string", HeatStringValidator.getInstance(), HeatStringConverter.getInstance()),
+
+ BOOLEAN("boolean", HeatBooleanValidator.getInstance(), HeatBooleanConverter.getInstance()),
+
+ NUMBER("number", HeatNumberValidator.getInstance(), HeatNumberConverter.getInstance()),
+
+ JSON("json", HeatStringValidator.getInstance(), HeatJsonConverter.getInstance()),
+
+ COMMA_DELIMITED_LIST("comma_delimited_list", HeatCommaDelimitedListValidator.getInstance(), HeatCommaDelimitedListConverter.getInstance());
+
+ private String type;
+ private PropertyTypeValidator validator;
+ private PropertyValueConverter converter;
+
+ HeatParameterType(String type, PropertyTypeValidator validator, PropertyValueConverter converter) {
+ this.type = type;
+ this.validator = validator;
+ this.converter = converter;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public PropertyTypeValidator getValidator() {
+ return validator;
+ }
+
+ public void setValidator(PropertyTypeValidator validator) {
+ this.validator = validator;
+ }
+
+ public PropertyValueConverter getConverter() {
+ return converter;
+ }
+
+ public void setConverter(PropertyValueConverter converter) {
+ this.converter = converter;
+ }
+
+ public static HeatParameterType isValidType(String typeName) {
+ if (typeName == null) {
+ return null;
+ }
+
+ for (HeatParameterType type : HeatParameterType.values()) {
+ if (type.getType().equals(typeName)) {
+ return type;
+ }
+ }
+ return null;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAdditionalInformationOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAdditionalInformationOperation.java
new file mode 100644
index 0000000000..71167c395f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAdditionalInformationOperation.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInfoParameterInfo;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.resources.data.AdditionalInfoParameterData;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface IAdditionalInformationOperation {
+
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> addAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String key, String value);
+
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> updateAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String origKey, String key, String value);
+
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> deleteAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String key);
+
+ public Either<AdditionalInfoParameterData, TitanOperationStatus> addAdditionalInformationNode(NodeTypeEnum nodeType,
+ String resourceUniqueId);
+
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> addAdditionalInformationNode(
+ NodeTypeEnum nodeType, String componentId, AdditionalInformationDefinition parameters);
+
+ public TitanOperationStatus findResourceAllAdditionalInformationRecursively(String uniqueId,
+ List<AdditionalInformationDefinition> properties);
+
+ public TitanOperationStatus findServiceAllAdditionalInformationRecursively(String uniqueId,
+ List<AdditionalInformationDefinition> properties);
+
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> createAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String key, String value, boolean inTransaction);
+
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> updateAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id, String key, String value, boolean inTransaction);
+
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> deleteAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id, boolean inTransaction);
+
+ public Either<Integer, StorageOperationStatus> getNumberOfAdditionalInformationParameters(NodeTypeEnum nodeType,
+ String resourceId, boolean inTransaction);
+
+ public Either<Integer, TitanOperationStatus> getNumberOfParameters(NodeTypeEnum nodeType, String resourceId);
+
+ public Either<AdditionalInfoParameterInfo, TitanOperationStatus> getAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id);
+
+ public Either<AdditionalInfoParameterInfo, StorageOperationStatus> getAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id, boolean inTransaction);
+
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> getAllAdditionalInformationParameters(
+ NodeTypeEnum nodeType, String resourceId, boolean ignoreVerification);
+
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> getAllAdditionalInformationParameters(
+ NodeTypeEnum nodeType, String resourceId, boolean ignoreVerification, boolean inTransaction);
+
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> deleteAllAdditionalInformationParameters(
+ NodeTypeEnum nodeType, String resourceId, boolean inTransaction);
+
+ public Either<TitanVertex, TitanOperationStatus> addAdditionalInformationNode(NodeTypeEnum nodeType,
+ String componentId, TitanVertex matadatVertex);
+
+ public TitanOperationStatus addAdditionalInformationNode(NodeTypeEnum nodeType, String componentId,
+ AdditionalInformationDefinition parameters, TitanVertex metadataVertex);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IArtifactOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IArtifactOperation.java
new file mode 100644
index 0000000000..873d05e1ed
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IArtifactOperation.java
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface IArtifactOperation {
+
+ public Either<ArtifactDefinition, StorageOperationStatus> addArifactToComponent(ArtifactDefinition artifactInfo, String id, NodeTypeEnum type, boolean failIfExist, boolean inTransaction);
+
+ public Either<ArtifactDefinition, StorageOperationStatus> updateArifactOnResource(ArtifactDefinition artifactInfo, String id, String artifactId, NodeTypeEnum type, boolean inTransaction);
+
+ public Either<ArtifactDefinition, StorageOperationStatus> updateArifactDefinition(ArtifactDefinition artifactInfo, boolean inTransaction);
+
+ public Either<ArtifactDefinition, StorageOperationStatus> removeArifactFromResource(String id, String artifactId, NodeTypeEnum resource, boolean deleteMandatoryArtifact, boolean inTransaction);
+
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifacts(String parentId, NodeTypeEnum parentType, boolean inTransaction);
+
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao);
+
+ public Either<ArtifactDefinition, StorageOperationStatus> getArtifactById(String id, boolean inTransaction);
+
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifacts(String parentId, NodeTypeEnum parentType, boolean inTransaction, String groupType);
+
+ Either<ArtifactDefinition, StorageOperationStatus> addHeatEnvArtifact(ArtifactDefinition artifactHeatEnv, ArtifactDefinition artifactHeat, String parentId, NodeTypeEnum parentType, boolean inTransaction);
+
+ public void updateUUID(ArtifactDataDefinition artifactData, String oldChecksum, String oldVesrion);
+
+ public Either<Integer, StorageOperationStatus> getParentsOfArtifact(String artifactId, NodeTypeEnum type);
+
+ public Either<ArtifactDefinition, StorageOperationStatus> getHeatArtifactByHeatEnvId(String heatEnvId, boolean inTransaction);
+
+ public Either<ArtifactData, StorageOperationStatus> updateToscaArtifactNameOnGraph(ArtifactDefinition artifactInfo, String artifactId, NodeTypeEnum type, String id);
+
+ public StorageOperationStatus addArifactToComponent(ArtifactDefinition artifactInfo, String parentId, NodeTypeEnum type, boolean failIfExist, TitanVertex parentVertex);
+
+ public Either<ArtifactData, StorageOperationStatus> getLatestArtifactDataByArtifactUUID(String artifactUUID, boolean inTransaction);
+
+ StorageOperationStatus addArifactToComponent(TitanVertex artifactInfo, TitanVertex parentVertex, String label);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAttributeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAttributeOperation.java
new file mode 100644
index 0000000000..db2b988f5f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IAttributeOperation.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.be.resources.data.AttributeValueData;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface IAttributeOperation {
+ Either<AttributeData, StorageOperationStatus> deleteAttribute(String attributeId);
+
+ TitanOperationStatus addAttributesToGraph(TitanVertex metadataVertex, Map<String, AttributeDefinition> attributes, String resourceId, Map<String, DataTypeDefinition> dataTypes);
+
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> getAllAttributesOfResourceInstance(ComponentInstance compInstance);
+
+ TitanOperationStatus findAllResourceAttributesRecursively(String resourceId, List<AttributeDefinition> attributes);
+
+ Either<Map<String, AttributeDefinition>, StorageOperationStatus> deleteAllAttributeAssociatedToNode(NodeTypeEnum nodeType, String uniqueId);
+
+ TitanOperationStatus findNodeNonInheretedAttribues(String uniqueId, NodeTypeEnum nodeType, List<AttributeDefinition> attributes);
+
+ Either<AttributeData, StorageOperationStatus> addAttribute(AttributeDefinition attributeDefinition, String resourceId);
+
+ Either<AttributeData, TitanOperationStatus> addAttributeToGraph(AttributeDefinition attribute, String resourceId, Map<String, DataTypeDefinition> dataTypes);
+
+ AttributeDefinition convertAttributeDataToAttributeDefinition(AttributeData attributeData, String attributeName, String resourceId);
+
+ Either<AttributeData, StorageOperationStatus> updateAttribute(String attributeId, AttributeDefinition newAttDef, Map<String, DataTypeDefinition> dataTypes);
+
+ /**
+ * Builds ComponentInstanceAttribute from AttributeValueData
+ *
+ * @param attributeValueData
+ * @param resourceInstanceAttribute
+ * @return
+ */
+ ComponentInstanceAttribute buildResourceInstanceAttribute(AttributeValueData attributeValueData, ComponentInstanceAttribute resourceInstanceAttribute);
+
+ TitanOperationStatus addAttributeToGraphByVertex(TitanVertex metadataVertex, AttributeDefinition attribute, String resourceId, Map<String, DataTypeDefinition> dataTypes);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICacheMangerOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICacheMangerOperation.java
new file mode 100644
index 0000000000..52586dad60
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICacheMangerOperation.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.cache.jobs.CheckAndUpdateJob;
+import org.openecomp.sdc.be.model.cache.jobs.StoreJob;
+
+/**
+ * Created by mlando on 9/5/2016.
+ */
+public interface ICacheMangerOperation {
+
+ /**
+ *
+ *
+ * @param componentId
+ * @param timestamp
+ * @param nodeTypeEnum
+ */
+ void updateComponentInCache(String componentId, long timestamp, NodeTypeEnum nodeTypeEnum);
+
+ void storeComponentInCache(org.openecomp.sdc.be.model.Component component, NodeTypeEnum nodeTypeEnum);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityInstanceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityInstanceOperation.java
new file mode 100644
index 0000000000..e50b658121
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityInstanceOperation.java
@@ -0,0 +1,143 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+/**
+ * public interface ICapabilityInstanceOperation provides methods for CRUD
+ * operations for CapabilityInstance on component instance level
+ *
+ * @author ns019t
+ *
+ */
+public interface ICapabilityInstanceOperation {
+ /**
+ * create capability instance of capability with property values for
+ * resource instance
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @param propertyValues
+ * @param validateCapabilityInstExistance
+ * @param capabilityName
+ * @return
+ */
+ public Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> createCapabilityInstanceOfCapabilityWithPropertyValuesForResourceInstance(
+ String resourceInstanceId, String capabilityId, String capabilityName,
+ List<ComponentInstanceProperty> propertyValues, boolean validateCapabilityInstExistance);
+
+ /**
+ *
+ * @param resourceInstanceVertex
+ * @param capabilityId
+ * @param capabilityName
+ * @param propertyValues
+ * @param validateCapabilityInstExistence
+ * @return
+ */
+ public TitanOperationStatus createCapabilityInstanceOfCapabilityWithPropertyValuesForResourceInstance(
+ TitanVertex resourceInstanceVertex, String resourceInstanceId, String capabilityId, String capabilityName,
+ List<ComponentInstanceProperty> propertyValues, boolean validateCapabilityInstExistence);
+
+ /**
+ * validate capability instance uniqueness
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @return
+ */
+ public Either<Boolean, TitanOperationStatus> validateCapabilityInstExistence(String resourceInstanceId,
+ String capabilityId);
+
+ /**
+ * delete capability instance from resource instance
+ *
+ * @param resourceInstanceId
+ * @param capabilityInstanceId
+ * @return
+ */
+ public Either<CapabilityInstData, TitanOperationStatus> deleteCapabilityInstanceFromResourceInstance(
+ String resourceInstanceId, String capabilityInstanceId);
+
+ /**
+ * get all capability instances for resource instance returns all Capability
+ * Instances related to Resource Instance as List<CapabilityInstData> or
+ * TitanOperationStatus if error occurs or if Resource Instance have no any
+ * related Capability Instance
+ *
+ * @param resourceInstanceId
+ * @return Either<List<CapabilityInstData>, TitanOperationStatus>
+ */
+ public Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getAllCapabilityInstancesOfResourceInstance(
+ String resourceInstanceId);
+
+ /**
+ * get capability instance of capability for resource instance
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @return
+ */
+ public Either<CapabilityInstData, TitanOperationStatus> getCapabilityInstanceOfCapabilityOfResourceInstance(
+ String resourceInstanceId, String capabilityId);
+
+ /**
+ * update capability property values
+ *
+ * @param resourceInstanceId
+ * @param capabilityInstanceId
+ * @param propertyValues
+ * @param capabilityId
+ * @return
+ */
+ public Either<List<PropertyValueData>, TitanOperationStatus> updateCapabilityPropertyValues(
+ String resourceInstanceId, String capabilityId, List<ComponentInstanceProperty> propertyValues);
+
+ /**
+ * clone and associate capability instance with property values
+ *
+ * @param createdComponentInstance
+ * @param capability
+ * @param capabilityInstPair
+ * @return
+ */
+ public Either<ImmutablePair<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> cloneAssociateCapabilityInstanceWithPropertyValues(
+ ComponentInstanceData createdComponentInstance, CapabilityDefinition capability,
+ ImmutablePair<CapabilityInstData, GraphEdge> capabilityInstPair);
+
+ Either<Boolean, TitanOperationStatus> validateCapabilityInstExistence(TitanVertex instanceVertex,
+ String resourceInstanceId, String capabilityId);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityOperation.java
new file mode 100644
index 0000000000..3b692b9607
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityOperation.java
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface ICapabilityOperation {
+
+ public Either<CapabilityDefinition, StorageOperationStatus> addCapability(String resourceId, String capabilityName,
+ CapabilityDefinition capabilityDefinition);
+
+ public Either<CapabilityDefinition, StorageOperationStatus> addCapability(String resourceId, String capabilityName,
+ CapabilityDefinition capabilityDefinition, boolean inTransaction);
+
+ /**
+ * @param uniqueId
+ * @return
+ */
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String uniqueId);
+
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String uniqueId, boolean inTransaction);
+
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String capabilityName, String resourceId);
+
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String capabilityName, String resourceId,
+ boolean inTransaction);
+
+ public Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> getAllCapabilitiesPairs(
+ String resourceId);
+
+ public Either<Map<String, CapabilityDefinition>, StorageOperationStatus> deleteAllCapabilities(String resourceId,
+ boolean inTransaction);
+
+ public Either<CapabilityDefinition, TitanOperationStatus> getCapabilityByCapabilityData(
+ CapabilityData capabilityData);
+
+ public TitanOperationStatus getCapabilitySourcesList(String resourceId, List<String> derivedFromList);
+
+ public Either<Map<String, PropertyData>, StorageOperationStatus> updatePropertiesOfCapability(String uniqueId,
+ String capabilityType, List<PropertyDefinition> newProperties);
+
+ public Either<Map<String, PropertyData>, StorageOperationStatus> updatePropertiesOfCapability(String uniqueId,
+ String capabilityType, List<PropertyDefinition> newProperties, boolean inTransaction);
+
+ StorageOperationStatus addCapability(TitanVertex metadataVertex, String resourceId, String capabilityName,
+ CapabilityDefinition capabilityDefinition, boolean inTransaction);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityTypeOperation.java
new file mode 100644
index 0000000000..6f0b5b8d13
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ICapabilityTypeOperation.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+
+import fj.data.Either;
+
+public interface ICapabilityTypeOperation {
+
+ /**
+ * @param capabilityTypeDefinition
+ * @return
+ */
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(
+ CapabilityTypeDefinition capabilityTypeDefinition);
+
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(
+ CapabilityTypeDefinition capabilityTypeDefinition, boolean inTransaction);
+
+ /**
+ * @param uniqueId
+ * @return
+ */
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId);
+
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId,
+ boolean inTransaction);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentInstanceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentInstanceOperation.java
new file mode 100644
index 0000000000..e51e077906
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentInstanceOperation.java
@@ -0,0 +1,246 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.resources.data.AttributeValueData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+
+import fj.data.Either;
+
+public interface IComponentInstanceOperation {
+
+ /**
+ * add resource instance to service
+ *
+ * @param containerComponentId
+ * - component id
+ * @param instanceNumber
+ * - instance number of the component instance
+ * @param componentInstance
+ * @param inTransaction
+ * @return
+ */
+ public Either<ComponentInstance, StorageOperationStatus> createComponentInstance(String containerComponentId,
+ NodeTypeEnum containerNodeType, String instanceNumber, ComponentInstance componentInstance,
+ NodeTypeEnum instNodeType, boolean inTransaction);
+
+ /**
+ * add resource instance to service with internal transaction
+ *
+ * @param containerComponentId
+ * @param instanceNumber
+ * @param componentInstance
+ * @return
+ */
+ public Either<ComponentInstance, StorageOperationStatus> createComponentInstance(String containerComponentId,
+ NodeTypeEnum containerNodeType, String instanceNumber, ComponentInstance componentInstance,
+ NodeTypeEnum instNodeType);
+
+ /**
+ * delete resource instance from component
+ *
+ * @param containerComponentId
+ * - containerComponent id
+ * @param resourceInstUid
+ * - resource instance uid
+ * @param inTransaction
+ * @return
+ */
+ public Either<ComponentInstance, StorageOperationStatus> deleteComponentInstance(NodeTypeEnum containerNodeType,
+ String containerComponentId, String resourceInstUid, boolean inTransaction);
+
+ public Either<ComponentInstance, StorageOperationStatus> deleteComponentInstance(NodeTypeEnum containerNodeType,
+ String containerComponentId, String resourceInstUid);
+
+ /**
+ * associate 2 resource instances for a given requirement
+ *
+ * @param serviceId
+ * @param fromResInstanceUid
+ * @param toResInstanceUid
+ * @param requirement
+ * @param relationship
+ * @param inTransaction
+ * @return
+ */
+ // public Either<RequirementCapabilityRelDef, StorageOperationStatus>
+ // associateResourceInstances(
+ // String serviceId, NodeTypeEnum nodeType, String fromResInstanceUid,
+ // String toResInstanceUid, String requirement, String relationship,
+ // boolean inTransaction);
+
+ // public Either<RequirementCapabilityRelDef, StorageOperationStatus>
+ // associateResourceInstances(
+ // String serviceId, NodeTypeEnum nodeType, String fromResInstanceUid,
+ // String toResInstanceUid, String requirement, String relationship);
+
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> associateResourceInstances(String serviceId,
+ NodeTypeEnum nodeType, RequirementCapabilityRelDef relation, boolean inTransaction);
+
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> associateResourceInstances(String serviceId,
+ NodeTypeEnum nodeType, RequirementCapabilityRelDef relation);
+
+ /**
+ *
+ * dissociate the relation between 2 resource instances for a given
+ * requirement
+ *
+ * @param serviceId
+ * @param fromResInstanceUid
+ * @param toResInstanceUid
+ * @param requirement
+ * @param inTransaction
+ * @return
+ */
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> dissociateResourceInstances(String serviceId,
+ NodeTypeEnum nodeType, RequirementCapabilityRelDef requirementDef, boolean inTransaction);
+
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> dissociateResourceInstances(String serviceId,
+ NodeTypeEnum nodeType, RequirementCapabilityRelDef requirementDef);
+
+ /**
+ * update the properties of a given resource instance
+ *
+ * @param serviceId
+ * @param resourceInstanceName
+ * @param resourceInstance
+ * @param inTransaction
+ * @return
+ */
+ public Either<ComponentInstance, StorageOperationStatus> updateResourceInstance(String serviceId,
+ NodeTypeEnum nodeType, String resourceInstanceName, ComponentInstance resourceInstance,
+ boolean inTransaction);
+
+ public Either<ComponentInstance, StorageOperationStatus> updateResourceInstance(String serviceId,
+ NodeTypeEnum nodeType, String resourceInstanceName, ComponentInstance resourceInstance);
+
+ /**
+ * get all resource instances of a given service and the relations between
+ * the resource instances
+ *
+ * @param serviceId
+ * @param inTransaction
+ * @return
+ */
+ public Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, StorageOperationStatus> getAllComponentInstances(
+ String componentId, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, boolean inTransaction);
+
+ public Either<List<String>, StorageOperationStatus> getAllComponentInstancesNames(String componentId,
+ NodeTypeEnum nodeType, boolean inTransaction);
+
+ public Either<List<String>, StorageOperationStatus> getAllComponentInstancesNames(String componentId,
+ NodeTypeEnum nodeType);
+
+ /**
+ * get resource instance from id
+ *
+ * @param resourceId
+ * @return resource instance of given id
+ */
+ public Either<ComponentInstance, StorageOperationStatus> getResourceInstanceById(String resourceId);
+
+ public Either<List<ComponentInstance>, StorageOperationStatus> deleteAllComponentInstances(String serviceId,
+ NodeTypeEnum nodeType, boolean inTransaction);
+
+ public Either<List<ComponentInstance>, StorageOperationStatus> deleteAllComponentInstances(String serviceId,
+ NodeTypeEnum nodeType);
+
+ public Either<Integer, StorageOperationStatus> increaseAndGetResourceInstanceSpecificCounter(
+ String resourceInstanceId, GraphPropertiesDictionary counterType, boolean inTransaction);
+
+ public String createComponentInstLogicalName(String instanceNumber, String componentInstanceName);
+
+ public Either<Boolean, StorageOperationStatus> isComponentInstanceNameExist(String parentComponentId,
+ NodeTypeEnum parentNodeType, String compInstId, String componentInstName);
+
+ public Either<Boolean, StorageOperationStatus> validateParent(String parentId, String uniqId,
+ boolean inTransaction);
+
+ public Either<ComponentInstance, StorageOperationStatus> getFullComponentInstance(
+ ComponentInstance componentInstance, NodeTypeEnum compInstNodeType);
+
+ public Either<Boolean, StorageOperationStatus> isAvailableRequirement(ComponentInstance fromResInstance,
+ RequirementAndRelationshipPair relationPair);
+
+ public Either<Boolean, StorageOperationStatus> isAvailableCapabilty(ComponentInstance toResInstance,
+ RequirementAndRelationshipPair relationPair);
+
+ public Either<ComponentInstanceProperty, StorageOperationStatus> addPropertyValueToResourceInstance(
+ ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, Integer index,
+ boolean inTransaction);
+
+ public Either<ComponentInstanceProperty, StorageOperationStatus> addPropertyValueToResourceInstance(
+ ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, boolean isvalidate,
+ Integer index, boolean inTransaction);
+
+ /**
+ * Adds Attribute to resource instance
+ *
+ * @param resourceInstanceAttribute
+ * * @param resourceInstanceId * @param index * @param
+ * inTransaction
+ * @return
+ **/
+ public Either<ComponentInstanceAttribute, StorageOperationStatus> addAttributeValueToResourceInstance(
+ ComponentInstanceAttribute resourceInstanceAttribute, String resourceInstanceId, Integer index,
+ boolean inTransaction);
+
+ public Either<ComponentInstanceProperty, StorageOperationStatus> updatePropertyValueInResourceInstance(
+ ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, boolean inTransaction);
+
+ /**
+ * Updates Attribute on resource instance
+ *
+ * @param attribute
+ * @param resourceInstanceId
+ * @param inTransaction
+ * @return
+ */
+ public Either<ComponentInstanceAttribute, StorageOperationStatus> updateAttributeValueInResourceInstance(
+ ComponentInstanceAttribute attribute, String resourceInstanceId, boolean inTransaction);
+
+ public Either<AttributeValueData, TitanOperationStatus> createOrUpdateAttributeOfResourceInstance(
+ ComponentInstanceAttribute attributeInstanceProperty, String resourceInstanceId);
+
+ public Either<ComponentInstanceInput, StorageOperationStatus> addInputValueToResourceInstance(
+ ComponentInstanceInput input, String resourceInstanceId, Integer innerElement, boolean b);
+
+ public Either<ComponentInstanceInput, StorageOperationStatus> updateInputValueInResourceInstance(
+ ComponentInstanceInput input, String resourceInstanceId, boolean b);
+
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> fetchCIEnvArtifacts(
+ String componentInstanceId);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentOperation.java
new file mode 100644
index 0000000000..e7eff13a9d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IComponentOperation.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+
+import fj.data.Either;
+
+public interface IComponentOperation {
+ public <T extends Component> Either<T, StorageOperationStatus> getComponent(String id, Class<T> clazz);
+
+ public Either<List<ArtifactDefinition>, StorageOperationStatus> getComponentArtifactsForDelete(String parentId,
+ NodeTypeEnum parentType, boolean inTransacton);
+
+ public <T> Either<T, StorageOperationStatus> getLightComponent(String id, boolean inTransaction);
+
+ public <T> Either<T, StorageOperationStatus> getComponent(String id, boolean inTransaction);
+
+ public <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters,
+ boolean inTranscation);
+
+ public <T extends GraphNode> Either<T, StorageOperationStatus> getComponentByLabelAndId(String uniqueId,
+ NodeTypeEnum nodeType, Class<T> clazz);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IConsumerOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IConsumerOperation.java
new file mode 100644
index 0000000000..290552b382
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IConsumerOperation.java
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.resources.data.ConsumerData;
+
+import fj.data.Either;
+
+public interface IConsumerOperation {
+
+ /**
+ * the method updates the node in the graph with the given ConsumerData
+ *
+ * @param consumerData
+ * the object we want to store
+ * @param inTransaction
+ * inTransaction is the operation part of a transaction, in case
+ * the value is false the action will be committed in the end of
+ * the method
+ * @return the updated object returned from the graph
+ */
+ Either<ConsumerData, StorageOperationStatus> updateCredentials(ConsumerData consumerData, boolean inTransaction);
+
+ /**
+ * the method updates the node in the graph with the given ConsumerData
+ *
+ * @param consumerData
+ * the object we want to store
+ * @return the updated object returned from the graph
+ */
+ Either<ConsumerData, StorageOperationStatus> updateCredentials(ConsumerData consumerData);
+
+ /**
+ * the method deletes the node with the given unique id
+ *
+ * @param consumerName
+ * the unique id by witch we will look up the credential we want
+ * to delete
+ * @param inTransaction
+ * inTransaction is the operation part of a transaction, in case
+ * the value is false the action will be committed in the end of
+ * the method
+ * @return the deleted object returned from the graph
+ */
+ Either<ConsumerData, StorageOperationStatus> deleteCredentials(String consumerName, boolean inTransaction);
+
+ /**
+ * the method deletes the node with the given unique id
+ *
+ * @param consumerName
+ * the unique id by witch we will look up the credential we want
+ * to delete
+ * @return the deleted object returned from the graph
+ */
+ Either<ConsumerData, StorageOperationStatus> deleteCredentials(String consumerName);
+
+ /**
+ * the method creates a new nod in the grape representing the supplied
+ * credential object
+ *
+ * @param consumerData
+ * the object we want to store
+ * @param inTransaction
+ * is the operation part of a transaction, in case the value is
+ * false the action will be committed in the end of the method
+ * @return the newly stored object returned from the graph
+ */
+ Either<ConsumerData, StorageOperationStatus> createCredentials(ConsumerData consumerData, boolean inTransaction);
+
+ /**
+ * the method creates a new nod in the grape representing the supplied
+ * credential object
+ *
+ * @param consumerData
+ * the object we want to store
+ * @return the newly stored object returned from the graph
+ */
+ Either<ConsumerData, StorageOperationStatus> createCredentials(ConsumerData consumerData);
+
+ /**
+ * the method retrieves the credential for the given consumer name
+ *
+ * @param consumerName
+ * the unique id by witch we will look up the credential
+ * @return ConsumerData or the error received during the operation
+ */
+ Either<ConsumerData, StorageOperationStatus> getCredentials(String consumerName);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IDataTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IDataTypeOperation.java
new file mode 100644
index 0000000000..b7f1882b45
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IDataTypeOperation.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+import fj.data.Either;
+
+public interface IDataTypeOperation {
+
+ /**
+ * @param dataTypeDefinition
+ * @return
+ */
+ public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition,
+ boolean inTransaction);
+
+ /**
+ * @param name
+ * @return
+ */
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(String name);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(String name, boolean inTransaction);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java
new file mode 100644
index 0000000000..a21c194060
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IElementOperation.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.PropertyScope;
+import org.openecomp.sdc.be.model.Tag;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.resources.data.CategoryData;
+
+import fj.data.Either;
+
+public interface IElementOperation {
+
+ Either<List<CategoryDefinition>, ActionStatus> getAllResourceCategories();
+
+ Either<List<CategoryDefinition>, ActionStatus> getAllServiceCategories();
+
+ Either<List<CategoryDefinition>, ActionStatus> getAllProductCategories();
+
+ public Either<List<Tag>, ActionStatus> getAllTags();
+
+ public Either<List<PropertyScope>, ActionStatus> getAllPropertyScopes();
+
+ public Either<List<ArtifactType>, ActionStatus> getAllArtifactTypes();
+
+ public Either<Map<String, Object>, ActionStatus> getAllDeploymentArtifactTypes();
+
+ public Either<Integer, ActionStatus> getDefaultHeatTimeout();
+
+ public <T extends GraphNode> Either<CategoryData, StorageOperationStatus> getCategoryData(String name,
+ NodeTypeEnum type, Class<T> clazz);
+
+ public <T extends GraphNode> Either<org.openecomp.sdc.be.resources.data.category.CategoryData, StorageOperationStatus> getNewCategoryData(
+ String name, NodeTypeEnum type, Class<T> clazz);
+
+ public Either<Map<String, String>, ActionStatus> getResourceTypesMap();
+
+ Either<CategoryDefinition, ActionStatus> createCategory(CategoryDefinition category, NodeTypeEnum nodeType);
+
+ Either<CategoryDefinition, ActionStatus> createCategory(CategoryDefinition category, NodeTypeEnum nodeType,
+ boolean inTransaction);
+
+ Either<CategoryDefinition, ActionStatus> deleteCategory(NodeTypeEnum nodeType, String categoryId);
+
+ Either<SubCategoryDefinition, ActionStatus> deleteSubCategory(NodeTypeEnum nodeType, String subCategoryId);
+
+ Either<Boolean, ActionStatus> isCategoryUniqueForType(NodeTypeEnum nodeType, String normalizedName);
+
+ Either<SubCategoryDefinition, ActionStatus> createSubCategory(String categoryId, SubCategoryDefinition subCategory,
+ NodeTypeEnum nodeType);
+
+ Either<SubCategoryDefinition, ActionStatus> createSubCategory(String categoryId, SubCategoryDefinition subCategory,
+ NodeTypeEnum nodeType, boolean inTransaction);
+
+ Either<List<CategoryDefinition>, ActionStatus> getAllCategories(NodeTypeEnum nodeType, boolean inTransaction);
+
+ Either<CategoryDefinition, ActionStatus> getCategory(NodeTypeEnum nodeType, String categoryId);
+
+ Either<SubCategoryDefinition, ActionStatus> getSubCategoryUniqueForType(NodeTypeEnum nodeType,
+ String normalizedName);
+
+ Either<Boolean, ActionStatus> isSubCategoryUniqueForCategory(NodeTypeEnum nodeType, String subCategoryNormName,
+ String parentCategoryId);
+
+ Either<GroupingDefinition, ActionStatus> createGrouping(String subCategoryId, GroupingDefinition grouping,
+ NodeTypeEnum nodeType);
+
+ Either<GroupingDefinition, ActionStatus> deleteGrouping(NodeTypeEnum nodeType, String groupingId);
+
+ Either<SubCategoryDefinition, ActionStatus> getSubCategory(NodeTypeEnum nodeType, String subCategoryId);
+
+ Either<Boolean, ActionStatus> isGroupingUniqueForSubCategory(NodeTypeEnum nodeType, String groupingNormName,
+ String parentSubCategoryId);
+
+ Either<GroupingDefinition, ActionStatus> getGroupingUniqueForType(NodeTypeEnum nodeType,
+ String groupingNormalizedName);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGraphLockOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGraphLockOperation.java
new file mode 100644
index 0000000000..d065ce0b09
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGraphLockOperation.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+public interface IGraphLockOperation {
+
+ public abstract StorageOperationStatus lockComponent(String componentId, NodeTypeEnum nodeType);
+
+ public abstract StorageOperationStatus unlockComponent(String componentId, NodeTypeEnum nodeType);
+
+ public abstract StorageOperationStatus lockComponentByName(String name, NodeTypeEnum nodeType);
+
+ public abstract StorageOperationStatus unlockComponentByName(String name, String componentId,
+ NodeTypeEnum nodeType);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupOperation.java
new file mode 100644
index 0000000000..4252ec0622
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupOperation.java
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.GroupData;
+
+import fj.data.Either;
+
+public interface IGroupOperation {
+
+ // add full group to component
+ public Either<GroupData, TitanOperationStatus> addGroupToGraph(NodeTypeEnum nodeTypeEnum, String componentId,
+ GroupDefinition groupDefinition);
+
+ public Either<GroupDefinition, StorageOperationStatus> addGroup(NodeTypeEnum nodeTypeEnum, String componentId,
+ GroupDefinition groupDefinition);
+
+ public Either<GroupDefinition, StorageOperationStatus> addGroup(NodeTypeEnum nodeTypeEnum, String componentId,
+ GroupDefinition groupDefinition, boolean inTransaction);
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> addGroups(NodeTypeEnum nodeTypeEnum,
+ String componentId, List<GroupDefinition> groups, boolean inTransaction);
+
+ // get group
+ public Either<GroupDefinition, TitanOperationStatus> getGroupFromGraph(String uniqueId);
+
+ public Either<GroupDefinition, StorageOperationStatus> getGroup(String uniqueId);
+
+ public Either<GroupDefinition, StorageOperationStatus> getGroup(String uniqueId, boolean inTransaction);
+
+ // get all groups under component
+ public Either<List<GroupDefinition>, TitanOperationStatus> getAllGroupsFromGraph(String componentId,
+ NodeTypeEnum componentTypeEnum);
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> getAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum, boolean inTransaction);
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> getAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum);
+
+ // delete all groups under component
+ public Either<List<GroupDefinition>, TitanOperationStatus> deleteAllGroupsFromGraph(String componentId,
+ NodeTypeEnum compTypeEnum);
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> deleteAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum, boolean inTransaction);
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> deleteAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum);
+
+ // Association
+ public Either<List<String>, StorageOperationStatus> getAssociatedGroupsToComponentInstance(
+ String componentInstanceId, boolean inTransaction);
+
+ public Either<List<String>, StorageOperationStatus> getAssociatedGroupsToComponentInstance(
+ String componentInstanceId);
+
+ public Either<List<String>, TitanOperationStatus> getAssociatedGroupsToComponentInstanceFromGraph(
+ String componentInstanceId);
+
+ public StorageOperationStatus associateGroupsToComponentInstance(List<String> groups, String componentInstanceId,
+ String compInstName, boolean inTransaction);
+
+ public StorageOperationStatus associateGroupsToComponentInstance(List<String> groups, String componentInstanceId,
+ String compInstName);
+
+ public Either<List<GraphRelation>, TitanOperationStatus> associateGroupsToComponentInstanceOnGraph(
+ List<String> groups, String componentInstanceId, String compInstName);
+
+ public Either<List<GraphRelation>, TitanOperationStatus> dissociateAllGroupsFromArtifactOnGraph(String componentId,
+ NodeTypeEnum componentTypeEnum, String artifactId);
+
+ public StorageOperationStatus dissociateAllGroupsFromArtifact(String componentId, NodeTypeEnum componentTypeEnum,
+ String artifactId, boolean inTransaction);
+
+ public StorageOperationStatus dissociateAllGroupsFromArtifact(String componentId, NodeTypeEnum componentTypeEnum,
+ String artifactId);
+
+ public TitanOperationStatus dissociateAndAssociateGroupsFromArtifactOnGraph(String componentId,
+ NodeTypeEnum componentTypeEnum, String oldArtifactId, ArtifactData newArtifact);
+
+ public StorageOperationStatus dissociateAndAssociateGroupsFromArtifact(String componentId,
+ NodeTypeEnum componentTypeEnum, String oldArtifactId, ArtifactData newArtifact, boolean inTransaction);
+
+ public StorageOperationStatus dissociateAndAssociateGroupsFromArtifact(String componentId,
+ NodeTypeEnum componentTypeEnum, String oldArtifactId, ArtifactData newArtifact);
+
+ public boolean isGroupExist(String groupName, boolean inTransaction);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupTypeOperation.java
new file mode 100644
index 0000000000..2b612579b6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IGroupTypeOperation.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.resources.data.GroupTypeData;
+
+import fj.data.Either;
+
+public interface IGroupTypeOperation {
+
+ /**
+ * @param groupTypeDefinition
+ * @return
+ */
+ public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition);
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition,
+ boolean inTransaction);
+
+ /**
+ * @param uniqueId
+ * @return
+ */
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId);
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId, boolean inTransaction);
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String name);
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String name,
+ boolean inTransaction);
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String name,
+ String version);
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String name, String version,
+ boolean inTransaction);
+
+ public Either<GroupTypeData, TitanOperationStatus> getLatestGroupTypeByNameFromGraph(String name);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IHeatParametersOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IHeatParametersOperation.java
new file mode 100644
index 0000000000..f903b4fc41
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IHeatParametersOperation.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.resources.data.HeatParameterValueData;
+
+import fj.data.Either;
+
+public interface IHeatParametersOperation {
+
+ public StorageOperationStatus addPropertiesToGraph(List<HeatParameterDefinition> properties, String resourceId, NodeTypeEnum nodeType);
+
+ public StorageOperationStatus getHeatParametersOfNode(NodeTypeEnum nodeType, String uniqueId, List<HeatParameterDefinition> properties);
+
+ public Either<List<HeatParameterDefinition>, StorageOperationStatus> deleteAllHeatParametersAssociatedToNode(NodeTypeEnum nodeType, String uniqueId);
+
+ public StorageOperationStatus deleteAllHeatValuesAssociatedToNode(NodeTypeEnum parentNodeType, String parentUniqueId);
+
+ public StorageOperationStatus validateAndUpdateProperty(HeatParameterDefinition heatParam);
+
+ public Either<HeatParameterValueData, StorageOperationStatus> updateHeatParameterValue(HeatParameterDefinition heatParam, String artifactId, String resourceInstanceId, String artifactLabel);
+
+ public StorageOperationStatus updateHeatParameters(List<HeatParameterDefinition> properties);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInputsOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInputsOperation.java
new file mode 100644
index 0000000000..2f1f2a70cf
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInputsOperation.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstInputsMap;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.be.resources.data.InputsData;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface IInputsOperation {
+
+ Either<String, StorageOperationStatus> deleteInput(String inputId);
+
+ Either<List<InputDefinition>, TitanOperationStatus> addInputsToGraph(String componentId, NodeTypeEnum nodeType,
+ Map<String, InputDefinition> inputs, Map<String, DataTypeDefinition> dataTypes);
+
+ Either<List<InputDefinition>, StorageOperationStatus> addInputsToComponent(String resourceId, NodeTypeEnum nodeType,
+ ComponentInstInputsMap componentInsInputs, Map<String, DataTypeDefinition> dataTypes);
+
+ TitanOperationStatus findNodeNonInheretedInputs(String uniqueId, List<InputDefinition> inputs);
+
+ Either<List<InputDefinition>, StorageOperationStatus> getInputsOfComponent(String compId, String fromName,
+ int amount);
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> getAllInputsOfResourceInstance(
+ ComponentInstance compInstance);
+
+ Either<Map<String, InputDefinition>, StorageOperationStatus> deleteAllInputsAssociatedToNode(NodeTypeEnum nodeType,
+ String uniqueId);
+
+ // TitanOperationStatus findNodeNonInheretedAttribues(String uniqueId,
+ // NodeTypeEnum nodeType, List<AttributeDefinition> attributes);
+
+ Either<InputsData, StorageOperationStatus> addInput(String inputName, InputDefinition inputDefinition,
+ String componentId, NodeTypeEnum nodeType);
+
+ Either<InputsData, TitanOperationStatus> addInputToGraph(String propertyName, InputDefinition inputDefinition,
+ String componentId, NodeTypeEnum nodeType);
+
+ Either<AttributeData, StorageOperationStatus> updateInput(String inputId, InputDefinition newInDef,
+ Map<String, DataTypeDefinition> dataTypes);
+
+ TitanOperationStatus findAllResourceInputs(String uniqueId, List<InputDefinition> inputs);
+
+ Either<InputDefinition, StorageOperationStatus> getInputById(String uniqueId, boolean skipProperties,
+ boolean skipinputsValue);
+
+ TitanOperationStatus addInputsToGraph(TitanVertex metadata, String componentId, Map<String, InputDefinition> inputs,
+ Map<String, DataTypeDefinition> dataTypes);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInterfaceLifecycleOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInterfaceLifecycleOperation.java
new file mode 100644
index 0000000000..bbcb61fea8
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IInterfaceLifecycleOperation.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.datatypes.elements.InterfaceDataDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.Operation;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface IInterfaceLifecycleOperation {
+
+ public Either<InterfaceDefinition, StorageOperationStatus> createInterfaceOnResource(InterfaceDefinition interf,
+ String resourceId, String interfaceName, boolean failIfExist, boolean inTransaction);
+
+ public StorageOperationStatus createInterfaceOnResource(InterfaceDefinition interf, String resourceId,
+ String interfaceName, boolean failIfExist, boolean inTransaction, TitanVertex metadataVertex);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> addInterfaceToResource(InterfaceDefinition interf,
+ String resourceId, String interfaceName);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> addInterfaceToResource(InterfaceDefinition interf,
+ String resourceId, String interfaceName, boolean inTransaction);
+
+ // public Either<InterfaceDefinition, StorageOperationStatus>
+ // getInterface(String interfaceId);
+ //
+ // public Either<InterfaceDefinition, StorageOperationStatus>
+ // getInterface(String interfaceId, boolean inTransaction);
+
+ public Either<Operation, StorageOperationStatus> updateInterfaceOperation(String resourceId, String interfaceName,
+ String operationName, Operation interf);
+
+ public Either<Operation, StorageOperationStatus> updateInterfaceOperation(String resourceId, String interfaceName,
+ String operationName, Operation interf, boolean inTransaction);
+
+ public Either<Operation, StorageOperationStatus> deleteInterfaceOperation(String resourceId, String interfaceName,
+ String operationName);
+
+ public Either<Operation, StorageOperationStatus> deleteInterfaceOperation(String resourceId, String interfaceName,
+ String operationName, boolean inTransaction);
+
+ public Either<Map<String, InterfaceDefinition>, StorageOperationStatus> getAllInterfacesOfResource(
+ String resourceId, boolean recursively, boolean inTransaction);
+
+ public Either<Map<String, InterfaceDefinition>, StorageOperationStatus> getAllInterfacesOfResource(
+ String resourceId, boolean recursively);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> deleteInterfaceOfResourceOnGraph(String resourceId,
+ InterfaceDefinition interfaceDef, boolean inTransaction);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> createInterfaceType(InterfaceDefinition interf);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> createInterfaceType(InterfaceDefinition interf,
+ boolean inTransaction);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> getInterface(String interfaceId);
+
+ public StorageOperationStatus associateInterfaceToNode(GraphNode node, InterfaceDefinition interfaceDefinition,
+ TitanVertex metadataVertex);
+
+ public Either<Operation, StorageOperationStatus> getSpecificOperation(String resourceId, String interfaceType,
+ String operationName);
+
+ public Either<InterfaceDefinition, StorageOperationStatus> dissociateInterfaceFromNode(GraphNode node,
+ InterfaceDefinition interfaceDefinition);
+
+ public String getShortInterfaceName(InterfaceDataDefinition interfaceDefinition);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ILifecycleOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ILifecycleOperation.java
new file mode 100644
index 0000000000..3b82eb692c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/ILifecycleOperation.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+
+import fj.data.Either;
+
+public interface ILifecycleOperation {
+
+ public ResourceOperation getResourceOperation();
+
+ public Either<User, StorageOperationStatus> getComponentOwner(String resourceId, NodeTypeEnum nodeType,
+ boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> checkinComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> requestCertificationComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> startComponentCertification(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> checkoutComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User currentOwner, boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> certifyComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User currentOwner, boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> cancelOrFailCertification(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, LifecycleStateEnum nextState, boolean b);
+
+ public Either<Boolean, StorageOperationStatus> deleteOldComponentVersions(NodeTypeEnum nodeType,
+ String componentName, String uuid, boolean inTransaction);
+
+ public Either<? extends Component, StorageOperationStatus> undoCheckout(NodeTypeEnum nodeType, Component resource,
+ User modifier, User currentOwner, boolean inTransaction);
+
+ public ComponentOperation getComponentOperation(NodeTypeEnum componentType);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPolicyTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPolicyTypeOperation.java
new file mode 100644
index 0000000000..fc689c81e2
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPolicyTypeOperation.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+
+import fj.data.Either;
+
+public interface IPolicyTypeOperation {
+
+ Either<PolicyTypeDefinition, StorageOperationStatus> getLatestPolicyTypeByType(String policyTypeName);
+
+ Either<PolicyTypeDefinition, StorageOperationStatus> addPolicyType(PolicyTypeDefinition policyType);
+
+ Either<PolicyTypeDefinition, StorageOperationStatus> getPolicyType(String uniqueId, boolean inTransaction);
+
+ Either<PolicyTypeDefinition, StorageOperationStatus> addPolicyType(PolicyTypeDefinition policyType,
+ boolean inTransaction);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IProductOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IProductOperation.java
new file mode 100644
index 0000000000..bbfea262a7
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IProductOperation.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Set;
+
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Product;
+
+import fj.data.Either;
+
+public interface IProductOperation extends IComponentOperation {
+ public Either<List<Product>, StorageOperationStatus> getProductCatalogData(boolean inTransaction);
+
+ public Either<Product, StorageOperationStatus> createProduct(Product product);
+
+ public Either<Product, StorageOperationStatus> createProduct(Product product, boolean inTransaction);
+
+ public Either<Product, StorageOperationStatus> deleteProduct(String productId, boolean inTransaction);
+
+ public Either<List<Product>, StorageOperationStatus> getFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction);
+
+ public void rollback();
+
+ public void commit();
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPropertyOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPropertyOperation.java
new file mode 100644
index 0000000000..6309510f6d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IPropertyOperation.java
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.IComplexDefaultValue;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+
+import fj.data.Either;
+
+public interface IPropertyOperation {
+
+ /**
+ * add property to resource
+ *
+ * @param propertyName
+ * @param propertyDefinition
+ * @param nodeType
+ * @param id
+ * @return
+ *
+ * public Either<PropertyDefinition, StorageOperationStatus>
+ * addPropertyToResource( String propertyName, PropertyDefinition
+ * propertyDefinition, NodeTypeEnum nodeType, String id);
+ */
+
+ /**
+ * get property belongs to resource
+ *
+ * @param propertyName
+ * - property name
+ * @param resourceId
+ * - resource unique id
+ * @return
+ */
+ public Either<PropertyDefinition, StorageOperationStatus> getPropertyOfResource(String propertyName,
+ String resourceId);
+
+ /**
+ * Delete all properties of resource
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @return
+ */
+ public Either<Map<String, PropertyDefinition>, StorageOperationStatus> deleteAllPropertiesAssociatedToNode(
+ NodeTypeEnum nodeType, String uniqueId);
+
+ public boolean isPropertyDefaultValueValid(IComplexDefaultValue propertyDefinition,
+ Map<String, DataTypeDefinition> dataTypes);
+
+ public boolean isPropertyTypeValid(IComplexDefaultValue propertyDefinition);
+
+ public ImmutablePair<String, Boolean> isPropertyInnerTypeValid(IComplexDefaultValue propertyDefinition,
+ Map<String, DataTypeDefinition> dataTypes);
+
+ /**
+ * @param dataTypeDefinition
+ * @return
+ */
+ public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition,
+ boolean inTransaction);
+
+ /**
+ * @param name
+ * @return
+ */
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(String name);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(String name, boolean inTransaction);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByNameWithoutDerived(String name,
+ boolean inTransaction);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByNameWithoutDerived(String name);
+
+ public StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition,
+ Map<String, DataTypeDefinition> dataTypes);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> updateDataType(DataTypeDefinition newDataTypeDefinition,
+ DataTypeDefinition oldDataTypeDefinition, boolean inTransaction);
+
+ public Either<DataTypeDefinition, StorageOperationStatus> updateDataType(DataTypeDefinition newDataTypeDefinition,
+ DataTypeDefinition oldDataTypeDefinition);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IRequirementOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IRequirementOperation.java
new file mode 100644
index 0000000000..a7a9bf6013
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IRequirementOperation.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.RequirementImplDef;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public interface IRequirementOperation {
+
+ /**
+ * add a requirement to resource
+ *
+ * @param reqName
+ * @param reqDefinition
+ * @param nodeType
+ * @param uniqueId
+ * @return
+ */
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource(String reqName,
+ RequirementDefinition reqDefinition, String resourceId);
+
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource(String reqName,
+ RequirementDefinition reqDefinition, String resourceId, boolean inTransaction);
+
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementImplToResource(String reqName,
+ RequirementImplDef reqDefinition, String resourceId, String parentReqUniqueId);
+
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementImplToResource(String reqName,
+ RequirementImplDef reqDefinition, String resourceId, String parentReqUniqueId, boolean inTransaction);
+
+ /**
+ * get requirement of resource
+ *
+ * @param reqName
+ * @param resourceId
+ * @return
+ */
+ public Either<RequirementDefinition, StorageOperationStatus> getRequirementOfResource(String reqName,
+ String resourceId);
+
+ public Either<RequirementDefinition, StorageOperationStatus> getRequirementOfResource(String reqName,
+ String resourceId, boolean inTransaction);
+
+ public Either<Map<String, RequirementDefinition>, StorageOperationStatus> getAllResourceRequirements(
+ String resourceId, boolean inTransaction);
+
+ Either<Map<String, List<RequirementDefinition>>, StorageOperationStatus> getAllRequirementsOfResourceOnly(
+ String resourceId, boolean inTransaction);
+
+ public Either<Map<String, RequirementDefinition>, TitanOperationStatus> getResourceRequirements(String resourceId);
+
+ public Either<Map<String, RequirementDefinition>, StorageOperationStatus> deleteAllRequirements(String resourceId,
+ boolean inTransaction);
+
+ public Either<RequirementDefinition, TitanOperationStatus> getRequirement(String uniqueId);
+
+ StorageOperationStatus addRequirementToResource(TitanVertex metadataVertex, String reqName,
+ RequirementDefinition reqDefinition, String resourceId, boolean inTransaction);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IResourceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IResourceOperation.java
new file mode 100644
index 0000000000..759380c236
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IResourceOperation.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+
+import fj.data.Either;
+
+public interface IResourceOperation extends IComponentOperation {
+
+ public TitanGenericDao getTitanGenericDao();
+
+ // public StorageOperationStatus lockResource(Resource resource);
+ //
+ // public StorageOperationStatus unlockResource(Resource resource);
+
+ public Either<Resource, StorageOperationStatus> createResource(Resource resource);
+
+ public Either<Resource, StorageOperationStatus> createResource(Resource resource, boolean inTransaction);
+
+ public Either<Resource, StorageOperationStatus> getResource(String resourceId);
+
+ // public Either<Resource, StorageOperationStatus> getResource_tx(String
+ // resourceId,boolean inTransaction);
+
+ public Either<Resource, StorageOperationStatus> getResource(String resourceId, boolean inTransaction);
+
+ /**
+ * the method retrieves all the certified resources, the returned values are
+ * only abstract or only none abstract according to the supplied parameters.
+ *
+ * @param getAbstract
+ * the value defines which resources to return only abstract or
+ * only none abstract
+ * @return
+ */
+ public Either<List<Resource>, StorageOperationStatus> getAllCertifiedResources(boolean getAbstract);
+
+ public Either<List<Resource>, StorageOperationStatus> getAllCertifiedResources(boolean getAbstract,
+ Boolean isHighest);
+
+ public Either<Boolean, StorageOperationStatus> validateResourceNameExists(String resourceName,
+ ResourceTypeEnum resourceType);
+
+ public Either<Resource, StorageOperationStatus> deleteResource(String resourceId);
+
+ public Either<Resource, StorageOperationStatus> deleteResource(String resourceId, boolean inTransaction);
+
+ public Either<Resource, StorageOperationStatus> updateResource(Resource resource);
+
+ public Either<Resource, StorageOperationStatus> updateResource(Resource resource, boolean inTransaction);
+
+ public Either<Integer, StorageOperationStatus> getNumberOfResourcesByName(String resourceName);
+
+ // public Either<List<ArtifactDefinition>, StorageOperationStatus>
+ // getResourceArtifactsForDelete(Resource resource);
+
+ public Either<List<Resource>, StorageOperationStatus> getFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction);
+
+ public Either<Set<Resource>, StorageOperationStatus> getCatalogData(Map<String, Object> propertiesToMatch,
+ boolean inTransaction);
+
+ public Either<Resource, StorageOperationStatus> getLatestByName(String resourceName, boolean inTransaction);
+
+ public Either<Resource, StorageOperationStatus> overrideResource(Resource resource, Resource resourceSaved,
+ boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getTesterFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceListByUuid(String uuid, boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getLatestResourceByUuid(String uuid, boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceListBySystemName(String systemName,
+ boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceCatalogData(boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceCatalogDataVFLatestCertifiedAndNonCertified(
+ boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceByNameAndVersion(String name, String version,
+ boolean inTransaction);
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceByNameAndVersion(String name, String version);
+
+ public Either<Resource, StorageOperationStatus> getResourceBySystemNameAndVersion(String name, String version,
+ Map<String, Object> additionalParams, boolean inTransaction);
+
+ // public Either<List<Resource>, StorageOperationStatus>
+ // getAllNotCheckoutResources(boolean getAbstract);
+
+ // public Either<List<Resource>, StorageOperationStatus>
+ // getAllNotCheckoutResources(boolean getAbstract, Boolean isHighest);
+
+ public Either<List<String>, StorageOperationStatus> getAllResourcesMarkedForDeletion();
+
+ public Either<Boolean, StorageOperationStatus> isResourceInUse(String resourceToDelete);
+
+ public Either<Resource, StorageOperationStatus> getLatestByToscaResourceName(String toscaResourceName,
+ boolean inTransaction);
+
+ public Either<Boolean, StorageOperationStatus> validateToscaResourceNameExists(String templateName);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IServiceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IServiceOperation.java
new file mode 100644
index 0000000000..05eb7c66f6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IServiceOperation.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+
+import fj.data.Either;
+
+public interface IServiceOperation extends IComponentOperation {
+
+ public Either<Service, StorageOperationStatus> createService(Service service);
+
+ public Either<Service, StorageOperationStatus> createService(Service service, boolean inTransaction);
+
+ public Either<Service, StorageOperationStatus> getService(String uniqueId);
+
+ public Either<Service, StorageOperationStatus> getService(String uniqueId, boolean inTransaction);
+ // public Either<Service, StorageOperationStatus> getService_tx(String
+ // uniqueId, boolean inTransaction);
+
+ public Either<Service, StorageOperationStatus> deleteService(String uniqueId);
+
+ public Either<Service, StorageOperationStatus> deleteService(String uniqueId, boolean inTransaction);
+
+ public Either<Boolean, StorageOperationStatus> validateServiceNameExists(String serviceName);
+
+ public Either<List<Service>, StorageOperationStatus> getFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction);
+
+ public Either<Service, StorageOperationStatus> updateService(Service service, boolean inTransaction);
+
+ public Either<Set<Service>, StorageOperationStatus> getCatalogData(Map<String, Object> propertiesToMatch,
+ boolean inTransaction);
+
+ public Either<List<Service>, StorageOperationStatus> getTesterFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, boolean inTransaction);
+
+ public Either<Set<Service>, StorageOperationStatus> getCertifiedServicesWithDistStatus(
+ Map<String, Object> propertiesToMatch, Set<DistributionStatusEnum> distStatus, boolean inTransaction);
+
+ public Either<Service, StorageOperationStatus> updateDestributionStatus(Service service, User user,
+ DistributionStatusEnum distributionStatus);
+
+ public Either<List<Service>, StorageOperationStatus> getServiceCatalogData(boolean inTransaction);
+
+ public Either<List<Service>, StorageOperationStatus> getServiceCatalogDataLatestCertifiedAndNotCertified(
+ boolean inTransaction);
+
+ public Either<Service, StorageOperationStatus> getServiceByNameAndVersion(String name, String version,
+ Map<String, Object> additionalParams, boolean inTransaction);
+
+ public Either<Service, StorageOperationStatus> getServiceByNameAndVersion(String name, String version);
+
+ public Either<Service, StorageOperationStatus> getServiceBySystemNameAndVersion(String name, String version,
+ boolean inTransaction);
+
+ public Either<List<Service>, StorageOperationStatus> getServiceListByUuid(String uuid, boolean inTransaction);
+
+ public Either<List<Service>, StorageOperationStatus> getLatestServiceByUuid(String uuid, boolean inTransaction);
+
+ public Either<List<Service>, StorageOperationStatus> getServiceListBySystemName(String systemName,
+ boolean inTransaction);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IUserAdminOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IUserAdminOperation.java
new file mode 100644
index 0000000000..dd9766fc83
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/IUserAdminOperation.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.FunctionalMenuInfo;
+import org.openecomp.sdc.be.model.User;
+
+import fj.data.Either;
+
+public interface IUserAdminOperation {
+
+ public Either<User, ActionStatus> getUserData(String id, boolean inTransaction);
+
+ public Either<User, ActionStatus> getInactiveUserData(String id);
+
+ public Either<User, StorageOperationStatus> saveUserData(User user);
+
+ public Either<User, StorageOperationStatus> updateUserData(User user);
+
+ public Either<User, StorageOperationStatus> deActivateUser(User user);
+
+ public Either<User, ActionStatus> deleteUserData(String id);
+
+ public Either<List<User>, ActionStatus> getAllUsersWithRole(String role, String status);
+
+ public Either<List<Edge>, StorageOperationStatus> getUserPandingTasksList(User user,
+ Map<String, Object> properties);
+
+ public Either<ImmutablePair<User, FunctionalMenuInfo>, ActionStatus> getUserDataWithFunctionalMenu(String userId);
+
+ public Either<FunctionalMenuInfo, TitanOperationStatus> createOrUpdateFunctionalMenu(String userId,
+ String newFunctionalMenu);
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/StorageOperationStatus.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/StorageOperationStatus.java
new file mode 100644
index 0000000000..c2346a316a
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/api/StorageOperationStatus.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.api;
+
+public enum StorageOperationStatus {
+
+ OK, CONNECTION_FAILURE, BAD_REQUEST, ENTITY_ALREADY_EXISTS, GRAPH_IS_LOCK, GENERAL_ERROR, USER_NOT_FOUND, PERMISSION_ERROR, HTTP_PROTOCOL_ERROR, STORAGE_NOT_AVAILABLE, READ_ONLY_STORAGE, STORAGE_LEGACY_INDEX_ERROR, SCHEMA_ERROR, TRANSACTION_ERROR, EXEUCTION_FAILED, NOT_FOUND, OPERATION_NOT_SUPPORTED, CATEGORY_NOT_FOUND, PARENT_RESOURCE_NOT_FOUND, MULTIPLE_PARENT_RESOURCE_FOUND, INCONSISTENCY, GRAPH_IS_NOT_AVAILABLE, SCHEMA_VIOLATION, FAILED_TO_LOCK_ELEMENT, INVALID_ID, MATCH_NOT_FOUND, ARTIFACT_NOT_FOUND, DISTR_ENVIRONMENT_NOT_AVAILABLE, DISTR_ENVIRONMENT_NOT_FOUND, DISTR_ENVIRONMENT_SENT_IS_INVALID, DISTR_ARTIFACT_NOT_FOUND, OVERLOAD, INVALID_TYPE, INVALID_VALUE, INVALID_INNER_TYPE, CSAR_NOT_FOUND, GROUP_INVALID_CONTENT, CANNOT_UPDATE_EXISTING_ENTITY, PROPERTY_NAME_ALREADY_EXISTS, INVALID_PROPERTY,;
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java
new file mode 100644
index 0000000000..c4bbaf58f6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AbstractOperation.java
@@ -0,0 +1,413 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.IComplexDefaultValue;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
+import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public abstract class AbstractOperation {
+ private static Logger log = LoggerFactory.getLogger(AbstractOperation.class.getName());
+ @javax.annotation.Resource
+ protected TitanGenericDao titanGenericDao;
+ public static final String EMPTY_VALUE = null;
+
+ protected Gson gson = new Gson();
+
+ @javax.annotation.Resource
+ protected ApplicationDataTypeCache applicationDataTypeCache;
+
+ protected DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
+
+ protected <SomeData extends GraphNode, SomeDefenition> Either<SomeData, TitanOperationStatus> addDefinitionToNodeType(SomeDefenition someDefinition, NodeTypeEnum nodeType, String nodeUniqueId, final GraphEdgeLabels edgeType,
+ Supplier<SomeData> dataBuilder, Supplier<String> defNameGenerator) {
+ String defName = defNameGenerator.get();
+ log.debug("Got {} {}", defName, someDefinition);
+
+ SomeData someData = dataBuilder.get();
+
+ log.debug("Before adding {} to graph. data = {}", defName, someData);
+
+ @SuppressWarnings("unchecked")
+ Either<SomeData, TitanOperationStatus> eitherSomeData = titanGenericDao.createNode(someData, (Class<SomeData>) someData.getClass());
+
+ log.debug("After adding {} to graph. status is = {}", defName, eitherSomeData);
+
+ if (eitherSomeData.isRight()) {
+ TitanOperationStatus operationStatus = eitherSomeData.right().value();
+ log.error("Failed to add {} to graph. status is {}", defName, operationStatus);
+ return Either.right(operationStatus);
+ }
+ UniqueIdData uniqueIdData = new UniqueIdData(nodeType, nodeUniqueId);
+ log.debug("Before associating {} to {}.", uniqueIdData, defName);
+
+ Either<GraphRelation, TitanOperationStatus> eitherRelations = titanGenericDao.createRelation(uniqueIdData, eitherSomeData.left().value(), edgeType, null);
+ if (eitherRelations.isRight()) {
+ TitanOperationStatus operationStatus = eitherRelations.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("AddDefinitionToNodeType", "Failed to associate" + nodeType.getName() + " " + nodeUniqueId + "to " + defName + "in graph. status is " + operationStatus, ErrorSeverity.ERROR);
+ return Either.right(operationStatus);
+ }
+ return Either.left(eitherSomeData.left().value());
+ }
+
+ protected <SomeData extends GraphNode, SomeDefenition> TitanOperationStatus addDefinitionToNodeType(TitanVertex vertex, SomeDefenition someDefinition, NodeTypeEnum nodeType, String nodeUniqueId, final GraphEdgeLabels edgeType,
+ Supplier<SomeData> dataBuilder, Supplier<String> defNameGenerator) {
+ String defName = defNameGenerator.get();
+ log.debug("Got {} {}", defName, someDefinition);
+
+ SomeData someData = dataBuilder.get();
+
+ log.debug("Before adding {} to graph. data = {}", defName, someData);
+
+ @SuppressWarnings("unchecked")
+ Either<TitanVertex, TitanOperationStatus> eitherSomeData = titanGenericDao.createNode(someData);
+
+ log.debug("After adding {} to graph. status is = {}", defName, eitherSomeData);
+
+ if (eitherSomeData.isRight()) {
+ TitanOperationStatus operationStatus = eitherSomeData.right().value();
+ log.error("Failed to add {} to graph. status is {}", defName, operationStatus);
+ return operationStatus;
+ }
+
+ TitanOperationStatus relations = titanGenericDao.createEdge(vertex, eitherSomeData.left().value(), edgeType, null);
+ if (!relations.equals(TitanOperationStatus.OK)) {
+ TitanOperationStatus operationStatus = relations;
+ BeEcompErrorManager.getInstance().logInternalFlowError("AddDefinitionToNodeType", "Failed to associate" + nodeType.getName() + " " + nodeUniqueId + "to " + defName + "in graph. status is " + operationStatus, ErrorSeverity.ERROR);
+ return operationStatus;
+ }
+ return relations;
+ }
+
+ interface NodeElementFetcher<ElementDefinition> {
+ TitanOperationStatus findAllNodeElements(String nodeId, List<ElementDefinition> listTofill);
+ }
+
+ public <ElementDefinition> TitanOperationStatus findAllResourceElementsDefinitionRecursively(String resourceId, List<ElementDefinition> elements, NodeElementFetcher<ElementDefinition> singleNodeFetcher) {
+
+ if (log.isTraceEnabled())
+ log.trace("Going to fetch elements under resource {}", resourceId);
+ TitanOperationStatus resourceAttributesStatus = singleNodeFetcher.findAllNodeElements(resourceId, elements);
+
+ if (resourceAttributesStatus != TitanOperationStatus.OK) {
+ return resourceAttributesStatus;
+ }
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource,
+ ResourceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (parentNodesStatus != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("findAllResourceElementsDefinitionRecursively", "Failed to find parent elements of resource " + resourceId + ". status is " + parentNodesStatus, ErrorSeverity.ERROR);
+ return parentNodesStatus;
+ }
+ }
+
+ if (parentNodes.isLeft()) {
+ ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentIntStatus = findAllResourceElementsDefinitionRecursively(parentUniqueId, elements, singleNodeFetcher);
+
+ if (addParentIntStatus != TitanOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("findAllResourceElementsDefinitionRecursively", "Failed to find all resource elements of resource " + parentUniqueId, ErrorSeverity.ERROR);
+
+ return addParentIntStatus;
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ protected <T, TStatus> void handleTransactionCommitRollback(boolean inTransaction, Either<T, TStatus> result) {
+ if (!inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ public <ElementTypeDefinition> Either<ElementTypeDefinition, StorageOperationStatus> getElementType(Function<String, Either<ElementTypeDefinition, TitanOperationStatus>> elementGetter, String uniqueId, boolean inTransaction) {
+ Either<ElementTypeDefinition, StorageOperationStatus> result = null;
+ try {
+
+ Either<ElementTypeDefinition, TitanOperationStatus> ctResult = elementGetter.apply(uniqueId);
+
+ if (ctResult.isRight()) {
+ TitanOperationStatus status = ctResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to retrieve information on element uniqueId:" + uniqueId + ". status is " + status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(ctResult.right().value()));
+ return result;
+ }
+
+ result = Either.left(ctResult.left().value());
+
+ return result;
+ } finally {
+ handleTransactionCommitRollback(inTransaction, result);
+
+ }
+
+ }
+
+ /**
+ * @param propertyDefinition
+ * @return
+ */
+
+ protected StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
+
+ log.trace("Going to validate property type and value. {}", propertyDefinition);
+
+ String propertyType = propertyDefinition.getType();
+ String value = propertyDefinition.getDefaultValue();
+
+ ToscaPropertyType type = getType(propertyType);
+
+ if (type == null) {
+
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
+ if (dataTypeDefinition == null) {
+ log.debug("The type {} of property cannot be found.", propertyType);
+ return StorageOperationStatus.INVALID_TYPE;
+ }
+
+ StorageOperationStatus status = validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
+
+ return status;
+
+ }
+ String innerType = null;
+
+ Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, () -> propertyDefinition.getSchema());
+ if (checkInnerType.isRight()) {
+ return StorageOperationStatus.INVALID_TYPE;
+ }
+ innerType = checkInnerType.left().value();
+
+ log.trace("After validating property type {}", propertyType);
+
+ boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
+ if (false == isValidProperty) {
+ log.info("The value {} of property from type {} is invalid", value, type);
+ return StorageOperationStatus.INVALID_VALUE;
+ }
+
+ PropertyValueConverter converter = type.getConverter();
+
+ if (isEmptyValue(value)) {
+ log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
+ propertyDefinition.setDefaultValue(EMPTY_VALUE);
+ } else if (false == isEmptyValue(value)) {
+ String convertedValue = converter.convert(value, innerType, dataTypes);
+ propertyDefinition.setDefaultValue(convertedValue);
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ protected ToscaPropertyType getType(String propertyType) {
+
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+
+ return type;
+
+ }
+
+ protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ if (isEmptyValue(value)) {
+ return true;
+ }
+
+ PropertyTypeValidator validator = type.getValidator();
+
+ boolean isValid = validator.isValid(value, innerType, dataTypes);
+ if (true == isValid) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ public boolean isEmptyValue(String value) {
+ if (value == null) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isNullParam(String value) {
+ if (value == null) {
+ return true;
+ }
+ return false;
+ }
+
+ protected StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
+
+ String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
+
+ ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
+
+ if (validateResult.right.booleanValue() == false) {
+ log.debug("The value {} of property from type {} is invalid", value, propertyType);
+ return StorageOperationStatus.INVALID_VALUE;
+ }
+
+ JsonElement jsonElement = validateResult.left;
+
+ log.trace("Going to update value in property definition {} {}", propertyDefinition.getName(), (jsonElement != null ? jsonElement.toString() : null));
+
+ updateValue(propertyDefinition, jsonElement);
+
+ return StorageOperationStatus.OK;
+ }
+
+ protected void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
+
+ propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
+
+ }
+
+ protected String getValueFromJsonElement(JsonElement jsonElement) {
+ String value = null;
+
+ if (jsonElement == null || jsonElement.isJsonNull()) {
+ value = EMPTY_VALUE;
+ } else {
+ if (jsonElement.toString().isEmpty()) {
+ value = "";
+ } else {
+ value = jsonElement.toString();
+ }
+ }
+
+ return value;
+ }
+
+ protected Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
+ String innerType = null;
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+
+ SchemaDefinition def = schemeGen.get();// propDataDef.getSchema();
+ if (def == null) {
+ log.debug("Schema doesn't exists for property of type {}", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ log.debug("Property in Schema Definition inside property of type {} doesn't exists", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ innerType = propDef.getType();
+ }
+ return Either.left(innerType);
+ }
+
+ /**
+ * Convert Constarint object to json in order to add it to the Graph
+ *
+ * @param constraints
+ * @return
+ */
+ public List<String> convertConstraintsToString(List<PropertyConstraint> constraints) {
+
+ List<String> result = null;
+
+ if (constraints != null && false == constraints.isEmpty()) {
+ result = new ArrayList<String>();
+ for (PropertyConstraint propertyConstraint : constraints) {
+ String constraint = gson.toJson(propertyConstraint);
+ result.add(constraint);
+ }
+
+ }
+
+ return result;
+ }
+
+ public List<PropertyConstraint> convertConstraints(List<String> constraints) {
+
+ if (constraints == null || constraints.size() == 0) {
+ return null;
+ }
+
+ List<PropertyConstraint> list = new ArrayList<PropertyConstraint>();
+ Type constraintType = new TypeToken<PropertyConstraint>() {
+ }.getType();
+
+ Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
+
+ for (String constraintJson : constraints) {
+ PropertyConstraint propertyConstraint = gson.fromJson(constraintJson, constraintType);
+ list.add(propertyConstraint);
+ }
+
+ return list;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperation.java
new file mode 100644
index 0000000000..e2c7f369f1
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperation.java
@@ -0,0 +1,960 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.AdditionalInfoParameterDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInfoParameterInfo;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.AdditionalInfoParameterData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AutoPopulatingList.ElementFactory;
+
+import fj.data.Either;
+
+@Component("additional-information-operation")
+public class AdditionalInformationOperation implements IAdditionalInformationOperation {
+
+ public static final String EMPTY_VALUE = null;
+ public static final String PROPERTY = "property";
+
+ public AdditionalInformationOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(AdditionalInformationOperation.class.getName());
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ @Override
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> addAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String componentId, String key, String value) {
+
+ TitanOperationStatus verifyNodeTypeVsComponent = verifyNodeTypeVsComponent(nodeType, componentId);
+ if (verifyNodeTypeVsComponent != TitanOperationStatus.OK) {
+ return Either.right(verifyNodeTypeVsComponent);
+ }
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ return Either.right(status);
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = getResult.left().value();
+ AdditionalInfoParameterData parameterData = immutablePair.getLeft();
+ Map<String, String> parameters = parameterData.getParameters();
+ if (parameters == null) {
+ parameters = new HashMap<String, String>();
+ parameterData.setParameters(parameters);
+ }
+ Map<String, String> idToKey = parameterData.getIdToKey();
+ if (idToKey == null) {
+ idToKey = new HashMap<String, String>();
+ parameterData.setIdToKey(idToKey);
+ }
+
+ Integer lastCreatedCounter = parameterData.getAdditionalInfoParameterDataDefinition().getLastCreatedCounter();
+ lastCreatedCounter++;
+
+ if (parameters.containsKey(key)) {
+ log.debug("The key {} already exists under component {}", key, componentId);
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+
+ idToKey.put(String.valueOf(lastCreatedCounter), key);
+ parameters.put(key, value);
+ parameterData.getAdditionalInfoParameterDataDefinition().setLastCreatedCounter(lastCreatedCounter);
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(parameterData,
+ AdditionalInfoParameterData.class);
+
+ if (updateNode.isRight()) {
+ TitanOperationStatus status = updateNode.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedUpdateNodeError,
+ "UpdateAdditionalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedUpdateNodeError("UpdateAdditionalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ return Either.right(status);
+ }
+
+ AdditionalInformationDefinition informationDefinition = createInformationDefinitionFromNode(componentId,
+ parameters, idToKey, updateNode.left().value());
+
+ return Either.left(informationDefinition);
+
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> updateAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String componentId, String id, String key, String value) {
+
+ TitanOperationStatus verifyNodeTypeVsComponent = verifyNodeTypeVsComponent(nodeType, componentId);
+ if (verifyNodeTypeVsComponent != TitanOperationStatus.OK) {
+ return Either.right(verifyNodeTypeVsComponent);
+ }
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ return Either.right(status);
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = getResult.left().value();
+ AdditionalInfoParameterData parameterData = immutablePair.getLeft();
+ Map<String, String> parameters = parameterData.getParameters();
+ Map<String, String> idToKey = parameterData.getIdToKey();
+ if (idToKey == null || false == idToKey.containsKey(id)) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ String origKey = idToKey.get(id);
+
+ if (false == origKey.equals(key)) {
+ if (parameters.containsKey(key)) {
+ log.debug("The key {} already exists", key);
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+ String removed = parameters.remove(origKey);
+ log.trace("The key-value " + origKey + "=" + removed + " was removed from additionalInformation");
+ }
+ parameters.put(key, value);
+ idToKey.put(id, key);
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(parameterData,
+ AdditionalInfoParameterData.class);
+
+ if (updateNode.isRight()) {
+ TitanOperationStatus status = updateNode.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedUpdateNodeError,
+ "UpdateAdditionalInformationParameter", "additional information of resource " + componentId,
+ String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedUpdateNodeError("UpdateAdditionalInformationParameter",
+ "additional information of resource " + componentId, String.valueOf(status));
+ return Either.right(status);
+ }
+
+ AdditionalInformationDefinition informationDefinition = createInformationDefinitionFromNode(componentId,
+ parameters, idToKey, updateNode.left().value());
+
+ return Either.left(informationDefinition);
+
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> deleteAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String componentId, String id) {
+
+ TitanOperationStatus verifyNodeTypeVsComponent = verifyNodeTypeVsComponent(nodeType, componentId);
+ if (verifyNodeTypeVsComponent != TitanOperationStatus.OK) {
+ return Either.right(verifyNodeTypeVsComponent);
+ }
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ return Either.right(status);
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = getResult.left().value();
+ AdditionalInfoParameterData parameterData = immutablePair.getLeft();
+ Map<String, String> parameters = parameterData.getParameters();
+ Map<String, String> idToKey = parameterData.getIdToKey();
+
+ if (idToKey == null || false == idToKey.containsKey(id)) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ String key = idToKey.get(id);
+ String removedKey = idToKey.remove(id);
+ String removedValue = parameters.remove(key);
+ log.trace("The key-value " + removedKey + "=" + removedValue + " was removed from additionalInformation");
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(parameterData,
+ AdditionalInfoParameterData.class);
+
+ if (updateNode.isRight()) {
+ TitanOperationStatus status = updateNode.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedUpdateNodeError,
+ "DeleteAdditionalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedUpdateNodeError("DeleteAdditionalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ return Either.right(status);
+ }
+
+ AdditionalInformationDefinition informationDefinition = createInformationDefinitionFromNode(componentId,
+ parameters, idToKey, updateNode.left().value());
+
+ return Either.left(informationDefinition);
+
+ }
+
+ private AdditionalInformationDefinition createInformationDefinitionFromNode(String resourceId,
+ Map<String, String> parameters, Map<String, String> idToKey,
+ AdditionalInfoParameterData additionalInfoParameterData) {
+ AdditionalInfoParameterDataDefinition dataDefinition = additionalInfoParameterData
+ .getAdditionalInfoParameterDataDefinition();
+
+ AdditionalInformationDefinition informationDefinition = new AdditionalInformationDefinition(dataDefinition,
+ resourceId, convertParameters(parameters, idToKey));
+ return informationDefinition;
+ }
+
+ private List<AdditionalInfoParameterInfo> convertParameters(Map<String, String> parameters,
+ Map<String, String> idToKey) {
+
+ List<AdditionalInfoParameterInfo> list = new ArrayList<AdditionalInfoParameterInfo>();
+
+ if (parameters != null) {
+ for (Entry<String, String> idToKeyEntry : idToKey.entrySet()) {
+
+ String id = idToKeyEntry.getKey();
+ String key = idToKeyEntry.getValue();
+
+ String value = parameters.get(key);
+
+ AdditionalInfoParameterInfo parameterInfo = new AdditionalInfoParameterInfo(id, key, value);
+ list.add(parameterInfo);
+ }
+
+ }
+
+ return list;
+ }
+
+ @Override
+ public Either<AdditionalInfoParameterData, TitanOperationStatus> addAdditionalInformationNode(NodeTypeEnum nodeType,
+ String componentId) {
+
+ UniqueIdData from = new UniqueIdData(nodeType, componentId);
+
+ String uniqueId = UniqueIdBuilder.buildAdditionalInformationUniqueId(componentId);
+ AdditionalInfoParameterDataDefinition additionalInfoParameterDataDefinition = new AdditionalInfoParameterDataDefinition();
+ additionalInfoParameterDataDefinition.setUniqueId(uniqueId);
+
+ AdditionalInfoParameterData additionalInfoParameterData = new AdditionalInfoParameterData(
+ additionalInfoParameterDataDefinition, new HashMap<String, String>(), new HashMap<String, String>());
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> createNode = titanGenericDao
+ .createNode(additionalInfoParameterData, AdditionalInfoParameterData.class);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedCreateNodeError,
+ "AddAdditionalInformationNode",
+ "additional information to " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError("AddAdditionalInformationNode", uniqueId,
+ String.valueOf(status));
+ return Either.right(status);
+ }
+
+ AdditionalInfoParameterData to = createNode.left().value();
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(from, to,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, null);
+ if (createRelation.isRight()) {
+ TitanOperationStatus status = createRelation.right().value();
+ return Either.right(status);
+ }
+
+ return Either.left(to);
+ }
+
+ @Override
+ public Either<TitanVertex, TitanOperationStatus> addAdditionalInformationNode(NodeTypeEnum nodeType,
+ String componentId, TitanVertex metadataVertex) {
+
+ String uniqueId = UniqueIdBuilder.buildAdditionalInformationUniqueId(componentId);
+ AdditionalInfoParameterDataDefinition additionalInfoParameterDataDefinition = new AdditionalInfoParameterDataDefinition();
+ additionalInfoParameterDataDefinition.setUniqueId(uniqueId);
+
+ AdditionalInfoParameterData additionalInfoParameterData = new AdditionalInfoParameterData(
+ additionalInfoParameterDataDefinition, new HashMap<String, String>(), new HashMap<String, String>());
+
+ Either<TitanVertex, TitanOperationStatus> createNode = titanGenericDao.createNode(additionalInfoParameterData);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedCreateNodeError,
+ "AddAdditionalInformationNode",
+ "additional information to " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError("AddAdditionalInformationNode", uniqueId,
+ String.valueOf(status));
+ return Either.right(status);
+ }
+
+ TitanVertex additionalInfoVertex = createNode.left().value();
+
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(metadataVertex, additionalInfoVertex,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, null);
+
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ return Either.right(createRelation);
+ }
+ return Either.left(additionalInfoVertex);
+ }
+
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> addAdditionalInformationNode(
+ NodeTypeEnum nodeType, String componentId, AdditionalInformationDefinition parameters) {
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> status = this.addAdditionalInformationNode(nodeType,
+ componentId);
+
+ if (status.isRight()) {
+ return Either.right(status.right().value());
+ }
+
+ AdditionalInfoParameterData parameterData = status.left().value();
+
+ populateParameterNodeWithParameters(parameterData, parameters);
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(parameterData,
+ AdditionalInfoParameterData.class);
+
+ if (updateNode.isRight()) {
+ return Either.right(updateNode.right().value());
+ }
+
+ AdditionalInformationDefinition informationDefinition = convertAdditionalInformationDataToDefinition(
+ updateNode.left().value(), componentId);
+
+ return Either.left(informationDefinition);
+ }
+
+ public TitanOperationStatus addAdditionalInformationNode(NodeTypeEnum nodeType, String componentId,
+ AdditionalInformationDefinition parameters, TitanVertex metadataVertex) {
+
+ Either<TitanVertex, TitanOperationStatus> status = this.addAdditionalInformationNode(nodeType, componentId,
+ metadataVertex);
+
+ if (status.isRight()) {
+ return status.right().value();
+ }
+ TitanVertex additionalInfoVertex = status.left().value();
+
+ Map<String, Object> newProp = titanGenericDao.getProperties(additionalInfoVertex);
+ AdditionalInfoParameterData parameterData = GraphElementFactory.createElement(
+ NodeTypeEnum.AdditionalInfoParameters.getName(), GraphElementTypeEnum.Node, newProp,
+ AdditionalInfoParameterData.class);
+
+ populateParameterNodeWithParameters(parameterData, parameters);
+
+ TitanOperationStatus updateNode = titanGenericDao.updateVertex(parameterData, additionalInfoVertex);
+
+ return updateNode;
+ }
+
+ private void populateParameterNodeWithParameters(AdditionalInfoParameterData parameterData,
+ AdditionalInformationDefinition aiDefinition) {
+
+ if (aiDefinition != null) {
+
+ Integer lastCreatedCounter = aiDefinition.getLastCreatedCounter();
+ parameterData.getAdditionalInfoParameterDataDefinition().setLastCreatedCounter(lastCreatedCounter);
+ log.trace("Set last created counter of additional information to " + lastCreatedCounter);
+
+ List<AdditionalInfoParameterInfo> parameters = aiDefinition.getParameters();
+ if (parameters != null) {
+
+ Map<String, String> idToKey = new HashMap<String, String>();
+ Map<String, String> parametersMap = new HashMap<String, String>();
+ for (AdditionalInfoParameterInfo additionalInfoParameterInfo : parameters) {
+ String uniqueId = additionalInfoParameterInfo.getUniqueId();
+ String key = additionalInfoParameterInfo.getKey();
+ String value = additionalInfoParameterInfo.getValue();
+
+ if (key != null && false == key.isEmpty()) {
+ idToKey.put(uniqueId, key);
+ parametersMap.put(key, value);
+ }
+ }
+ parameterData.setIdToKey(idToKey);
+ parameterData.setParameters(parametersMap);
+ }
+ }
+
+ }
+
+ @Override
+ public TitanOperationStatus findResourceAllAdditionalInformationRecursively(String uniqueId,
+ List<AdditionalInformationDefinition> properties) {
+
+ log.trace("Going to fetch additional information under resource " + uniqueId);
+ TitanOperationStatus resourceCapabilitiesStatus = findAdditionalInformationOfNode(NodeTypeEnum.Resource,
+ uniqueId, properties);
+
+ if (!resourceCapabilitiesStatus.equals(TitanOperationStatus.OK)) {
+ return resourceCapabilitiesStatus;
+ }
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), uniqueId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (false == parentNodesStatus.equals(TitanOperationStatus.NOT_FOUND)) {
+ log.error("Failed to find parent additional information of resource " + uniqueId + ". status is "
+ + parentNodesStatus);
+ return parentNodesStatus;
+ }
+ }
+
+ if (parentNodes.isLeft()) {
+ ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentIntStatus = findResourceAllAdditionalInformationRecursively(parentUniqueId,
+ properties);
+
+ if (addParentIntStatus != TitanOperationStatus.OK) {
+ log.error("Failed to find all resource additional information of resource " + parentUniqueId);
+ return addParentIntStatus;
+ }
+ }
+ return TitanOperationStatus.OK;
+
+ }
+
+ @Override
+ public TitanOperationStatus findServiceAllAdditionalInformationRecursively(String uniqueId,
+ List<AdditionalInformationDefinition> properties) {
+
+ log.trace("Going to fetch additional information under service " + uniqueId);
+ TitanOperationStatus resourceCapabilitiesStatus = findAdditionalInformationOfNode(NodeTypeEnum.Service,
+ uniqueId, properties);
+
+ if (!resourceCapabilitiesStatus.equals(TitanOperationStatus.OK)) {
+ return resourceCapabilitiesStatus;
+ }
+
+ Either<ImmutablePair<ServiceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Service), uniqueId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Service, ServiceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (false == parentNodesStatus.equals(TitanOperationStatus.NOT_FOUND)) {
+ log.error("Failed to find parent additional information of resource " + uniqueId + ". status is "
+ + parentNodesStatus);
+ return parentNodesStatus;
+ }
+ }
+
+ if (parentNodes.isLeft()) {
+ ImmutablePair<ServiceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentIntStatus = findServiceAllAdditionalInformationRecursively(parentUniqueId,
+ properties);
+
+ if (addParentIntStatus != TitanOperationStatus.OK) {
+ log.error("Failed to find all resource additional information of resource " + parentUniqueId);
+ return addParentIntStatus;
+ }
+ }
+ return TitanOperationStatus.OK;
+
+ }
+
+ private TitanOperationStatus findAdditionalInformationOfNode(NodeTypeEnum nodeType, String uniqueId,
+ List<AdditionalInformationDefinition> properties) {
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> childNode = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.ADDITIONAL_INFORMATION,
+ NodeTypeEnum.AdditionalInfoParameters, AdditionalInfoParameterData.class);
+
+ if (childNode.isRight()) {
+ TitanOperationStatus status = childNode.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ return status;
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = childNode.left().value();
+ AdditionalInfoParameterData propertyData = immutablePair.getKey();
+
+ Map<String, String> parameters = propertyData.getParameters();
+ if (parameters != null && false == parameters.isEmpty()) {
+ AdditionalInformationDefinition additionalInfoDef = this
+ .convertAdditionalInformationDataToDefinition(propertyData, uniqueId);
+ properties.add(additionalInfoDef);
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ private AdditionalInformationDefinition convertAdditionalInformationDataToDefinition(
+ AdditionalInfoParameterData additionalInfoData, String uniqueId) {
+
+ Map<String, String> parameters = additionalInfoData.getParameters();
+ Map<String, String> idToKey = additionalInfoData.getIdToKey();
+
+ AdditionalInformationDefinition definition = new AdditionalInformationDefinition(
+ additionalInfoData.getAdditionalInfoParameterDataDefinition(), uniqueId,
+ convertParameters(parameters, idToKey));
+ return definition;
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> createAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String key, String value, boolean inTransaction) {
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> either = this
+ .addAdditionalInformationParameter(nodeType, resourceId, key, value);
+
+ if (either.isRight()) {
+ TitanOperationStatus status = either.right().value();
+ log.debug("Failed to add additional information property {} to component {}. Status is {}", key, resourceId, status);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedUpdateNodeError,
+ "additional information of " + nodeType.getName() + " " + resourceId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedUpdateNodeError("CreateAdditionalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + resourceId, String.valueOf(status));
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+ result = Either.left(additionalInformationDefinition);
+ }
+
+ return result;
+ } finally {
+ commitOrRollback(inTransaction, result);
+ }
+
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> updateAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id, String key, String value, boolean inTransaction) {
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> either = this
+ .updateAdditionalInformationParameter(nodeType, resourceId, id, key, value);
+
+ if (either.isRight()) {
+ log.info("Failed to update additional information property " + key + " to component " + resourceId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value()));
+ } else {
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+ result = Either.left(additionalInformationDefinition);
+ }
+
+ return result;
+
+ } finally {
+ commitOrRollback(inTransaction, result);
+ }
+
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> deleteAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id, boolean inTransaction) {
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> either = this
+ .deleteAdditionalInformationParameter(nodeType, resourceId, id);
+
+ if (either.isRight()) {
+ log.error("Failed to delete additional information id " + id + " to component " + resourceId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value()));
+ } else {
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+ result = Either.left(additionalInformationDefinition);
+ }
+
+ return result;
+
+ } finally {
+ commitOrRollback(inTransaction, result);
+ }
+
+ }
+
+ @Override
+ public Either<Integer, StorageOperationStatus> getNumberOfAdditionalInformationParameters(NodeTypeEnum nodeType,
+ String resourceId, boolean inTransaction) {
+
+ Either<Integer, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<Integer, TitanOperationStatus> either = this.getNumberOfParameters(nodeType, resourceId);
+
+ if (either.isRight()) {
+ log.error("Failed to get the number of additional information properties in component " + resourceId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value()));
+ } else {
+ Integer counter = either.left().value();
+ result = Either.left(counter);
+ }
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<Integer, TitanOperationStatus> getNumberOfParameters(NodeTypeEnum nodeType, String resourceId) {
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), resourceId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ return Either.right(status);
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = getResult.left().value();
+ AdditionalInfoParameterData parameterData = immutablePair.getLeft();
+ Map<String, String> parameters = parameterData.getParameters();
+
+ Integer counter = 0;
+ if (parameters != null) {
+ counter = parameters.size();
+ }
+
+ return Either.left(counter);
+
+ }
+
+ @Override
+ public Either<AdditionalInfoParameterInfo, TitanOperationStatus> getAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String componentId, String id) {
+
+ TitanOperationStatus verifyNodeTypeVsComponent = verifyNodeTypeVsComponent(nodeType, componentId);
+ if (verifyNodeTypeVsComponent != TitanOperationStatus.OK) {
+ return Either.right(verifyNodeTypeVsComponent);
+ }
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ return Either.right(status);
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = getResult.left().value();
+ AdditionalInfoParameterData parameterData = immutablePair.getLeft();
+ Map<String, String> parameters = parameterData.getParameters();
+ Map<String, String> idToKey = parameterData.getIdToKey();
+
+ if (idToKey == null || false == idToKey.containsKey(id)) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ String key = idToKey.get(id);
+ String value = parameters.get(key);
+
+ log.trace("The key-value " + key + "=" + value + " was retrieved for id " + id);
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(parameterData,
+ AdditionalInfoParameterData.class);
+
+ if (updateNode.isRight()) {
+ TitanOperationStatus status = updateNode.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedRetrieveNodeError,
+ "GetAdditionnalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("GetAdditionnalInformationParameter",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ }
+ return Either.right(status);
+ }
+
+ AdditionalInfoParameterInfo additionalInfoParameterInfo = new AdditionalInfoParameterInfo(id, key, value);
+
+ return Either.left(additionalInfoParameterInfo);
+
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, TitanOperationStatus> getAllAdditionalInformationParameters(
+ NodeTypeEnum nodeType, String componentId, boolean ignoreVerification) {
+
+ if (false == ignoreVerification) {
+ TitanOperationStatus verifyNodeTypeVsComponent = verifyNodeTypeVsComponent(nodeType, componentId);
+ if (verifyNodeTypeVsComponent != TitanOperationStatus.OK) {
+ return Either.right(verifyNodeTypeVsComponent);
+ }
+ }
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedRetrieveNodeError,
+ "GetAdditionnalInformationParameters",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("GetAdditionnalInformationParameters",
+ "additional information of " + nodeType.getName() + " " + componentId, String.valueOf(status));
+ }
+ return Either.right(status);
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> immutablePair = getResult.left().value();
+ AdditionalInfoParameterData parameterData = immutablePair.getLeft();
+ Map<String, String> parameters = parameterData.getParameters();
+ Map<String, String> idToKey = parameterData.getIdToKey();
+
+ AdditionalInformationDefinition informationDefinition = createInformationDefinitionFromNode(componentId,
+ parameters, idToKey, parameterData);
+
+ return Either.left(informationDefinition);
+
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> getAllAdditionalInformationParameters(
+ NodeTypeEnum nodeType, String resourceId, boolean ignoreVerification, boolean inTransaction) {
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> either = this
+ .getAllAdditionalInformationParameters(nodeType, resourceId, ignoreVerification);
+
+ if (either.isRight()) {
+ TitanOperationStatus status = either.right().value();
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+ result = Either.left(additionalInformationDefinition);
+ }
+
+ return result;
+
+ } finally {
+ commitOrRollback(inTransaction, result);
+ }
+
+ }
+
+ private void commitOrRollback(boolean inTransaction, Either<? extends Object, StorageOperationStatus> result) {
+
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ private void commitOrRollbackTx(TitanTransaction tx, boolean inTransaction,
+ Either<? extends Object, StorageOperationStatus> result) {
+
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ tx.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ tx.commit();
+ }
+ }
+ }
+
+ @Override
+ public Either<AdditionalInfoParameterInfo, StorageOperationStatus> getAdditionalInformationParameter(
+ NodeTypeEnum nodeType, String resourceId, String id, boolean inTransaction) {
+
+ Either<AdditionalInfoParameterInfo, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<AdditionalInfoParameterInfo, TitanOperationStatus> either = this
+ .getAdditionalInformationParameter(nodeType, resourceId, id);
+
+ if (either.isRight()) {
+ log.error("Failed to fetch additional information property with id " + id + " of component "
+ + resourceId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value()));
+ } else {
+ AdditionalInfoParameterInfo additionalInformationDefinition = either.left().value();
+ result = Either.left(additionalInformationDefinition);
+ }
+
+ return result;
+
+ } finally {
+ commitOrRollback(inTransaction, result);
+ }
+ }
+
+ @Override
+ public Either<AdditionalInformationDefinition, StorageOperationStatus> deleteAllAdditionalInformationParameters(
+ NodeTypeEnum nodeType, String resourceId, boolean inTransaction) {
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<ImmutablePair<AdditionalInfoParameterData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(nodeType), resourceId,
+ GraphEdgeLabels.ADDITIONAL_INFORMATION, NodeTypeEnum.AdditionalInfoParameters,
+ AdditionalInfoParameterData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(StorageOperationStatus.OK);
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedDeleteNodeError,
+ "DeleteAdditionalInformationNode",
+ "additional information of " + nodeType.getName() + " " + resourceId,
+ String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError("DeleteAdditionalInformationNode",
+ "additional information of " + nodeType.getName() + " " + resourceId,
+ String.valueOf(status));
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ return result;
+ }
+
+ ImmutablePair<AdditionalInfoParameterData, GraphEdge> value = getResult.left().value();
+ AdditionalInfoParameterData parameterData = value.getLeft();
+
+ Either<AdditionalInfoParameterData, TitanOperationStatus> deleteNodeRes = titanGenericDao
+ .deleteNode(parameterData, AdditionalInfoParameterData.class);
+ if (deleteNodeRes.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedDeleteNodeError,
+ "DeleteAdditionalInformationNode", (String) parameterData.getUniqueId(),
+ String.valueOf(status));
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError("DeleteAdditionalInformationNode",
+ (String) parameterData.getUniqueId(), String.valueOf(status));
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ AdditionalInformationDefinition informationDefinition = convertAdditionalInformationDataToDefinition(
+ deleteNodeRes.left().value(), resourceId);
+
+ result = Either.left(informationDefinition);
+
+ return result;
+
+ } finally {
+ commitOrRollback(inTransaction, result);
+ }
+ }
+
+ private TitanOperationStatus verifyNodeTypeVsComponent(NodeTypeEnum nodeType, String componentId) {
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = titanGenericDao
+ .getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId);
+ if (vertexByProperty.isRight()) {
+ TitanOperationStatus status = vertexByProperty.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return status;
+ } else {
+ Vertex v = vertexByProperty.left().value();
+ String label = (String) v.property(GraphPropertiesDictionary.LABEL.getProperty()).value();
+ if (label != null) {
+ if (false == label.equals(nodeType.getName())) {
+ log.debug("The node type {} is not appropriate to component {}", nodeType, componentId);
+ return TitanOperationStatus.INVALID_ID;
+ }
+ } else {
+ log.debug("The node type {} with id {} does not have a label property.", nodeType, componentId);
+ return TitanOperationStatus.INVALID_ID;
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AllOperationsUtil.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AllOperationsUtil.java
new file mode 100644
index 0000000000..c005176238
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AllOperationsUtil.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import org.springframework.stereotype.Component;
+
+@Component("all-operations")
+public class AllOperationsUtil {
+
+ @javax.annotation.Resource
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource
+ private RequirementOperation requirementOperation;
+
+ @javax.annotation.Resource
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource
+ private ResourceOperation resourceOperation;
+
+ public PropertyOperation getPropertyOperation() {
+ return propertyOperation;
+ }
+
+ public RequirementOperation getRequirementOperation() {
+ return requirementOperation;
+ }
+
+ public CapabilityOperation getCapabilityOperation() {
+ return capabilityOperation;
+ }
+
+ public ResourceOperation getResourceOperation() {
+ return resourceOperation;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperation.java
new file mode 100644
index 0000000000..a4f4bebd5a
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperation.java
@@ -0,0 +1,1202 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.HeatParameterData;
+import org.openecomp.sdc.be.resources.data.HeatParameterValueData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+//import com.tinkerpop.blueprints.Direction;
+//import com.tinkerpop.blueprints.Edge;
+//import com.tinkerpop.blueprints.Vertex;
+//import com.tinkerpop.blueprints.util.ElementHelper;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@Component("artifact-operation")
+public class ArtifactOperation implements IArtifactOperation {
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ @javax.annotation.Resource
+ private HeatParametersOperation heatParametersOperation;
+
+ @javax.annotation.Resource
+ private GroupOperation groupOperation;
+
+ private static Logger log = LoggerFactory.getLogger(ArtifactOperation.class.getName());
+
+ public ArtifactOperation() {
+ super();
+ }
+
+ public TitanGenericDao getTitanGenericDao() {
+ return titanGenericDao;
+ }
+
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ public HeatParametersOperation getHeatParametersOperation() {
+ return heatParametersOperation;
+ }
+
+ public void setHeatParametersOperation(HeatParametersOperation heatParametersOperation) {
+ this.heatParametersOperation = heatParametersOperation;
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> addArifactToComponent(ArtifactDefinition artifactInfo, String parentId, NodeTypeEnum type, boolean failIfExist, boolean inTransaction) {
+
+ Either<ArtifactData, StorageOperationStatus> status = addArtifactToGraph(artifactInfo, parentId, type, failIfExist);
+
+ if (status.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.debug("Failed to add artifact {} to {} {}", artifactInfo.getArtifactName(), type, parentId);
+ return Either.right(status.right().value());
+ } else {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ ArtifactData artifactData = status.left().value();
+
+ ArtifactDefinition artifactDefResult = convertArtifactDataToArtifactDefinition(artifactData);
+
+ log.debug("The returned ArtifactDefintion is {}", artifactDefResult);
+ return Either.left(artifactDefResult);
+ }
+
+ }
+
+ @Override
+ public StorageOperationStatus addArifactToComponent(TitanVertex artifactInfo, TitanVertex parentVertex, String label) {
+
+ // save logical artifact ref name on edge as property
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), label);
+ String groupInfo = (String) titanGenericDao.getProperty(artifactInfo, GraphPropertiesDictionary.ARTIFACT_GROUP_TYPE.getProperty());
+ if (groupInfo != null)
+ properties.put(GraphEdgePropertiesDictionary.GROUP_TYPE.getProperty(), groupInfo);
+ TitanOperationStatus relation = titanGenericDao.createEdge(parentVertex, artifactInfo, GraphEdgeLabels.ARTIFACT_REF, properties);
+ if (!relation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create relation in graph from id {} to new artifact {}", parentVertex, label);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(relation);
+ }
+ return StorageOperationStatus.OK;
+
+ }
+
+ @Override
+ public StorageOperationStatus addArifactToComponent(ArtifactDefinition artifactInfo, String parentId, NodeTypeEnum type, boolean failIfExist, TitanVertex parentVertex) {
+
+ StorageOperationStatus status = addArtifactToGraph(artifactInfo, parentId, type, failIfExist, parentVertex);
+
+ if (status.equals(StorageOperationStatus.OK)) {
+ log.debug("Failed to add artifact {} {} to {}", artifactInfo.getArtifactName(), type, parentId);
+ }
+ return status;
+ }
+
+ private StorageOperationStatus addArtifactToGraph(ArtifactDefinition artifactInfo, String id, NodeTypeEnum type, boolean failIfexist, TitanVertex parentVertex) {
+
+ if (artifactInfo.getUniqueId() == null || artifactInfo.getUniqueId().isEmpty()) {
+ String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(id, artifactInfo.getArtifactLabel());
+ artifactInfo.setUniqueId(uniqueId);
+ }
+
+ if (validateParentType(type) == false) {
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ ArtifactData artifactData = new ArtifactData(artifactInfo);
+
+ Either<TitanVertex, TitanOperationStatus> existArtifact = titanGenericDao.getVertexByProperty(artifactData.getUniqueIdKey(), artifactData.getUniqueId());
+ if (existArtifact.isRight()) {
+ if (existArtifact.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ // create new node
+ log.debug("Before adding artifact to graph {}", artifactData);
+ if (artifactData.getArtifactDataDefinition().getArtifactUUID() == null || artifactData.getArtifactDataDefinition().getArtifactUUID().isEmpty())
+ updateUUID(artifactData.getArtifactDataDefinition(), null, artifactData.getArtifactDataDefinition().getArtifactVersion());
+ Either<TitanVertex, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(artifactData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.debug("Failed to add artifact {} to graph. status is {}", artifactData.getArtifactDataDefinition().getArtifactName(), operationStatus);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedCreateNodeError, "Failed to add artifact " + artifactData.getArtifactDataDefinition().getArtifactName() + " to graph. status is " + operationStatus,
+ artifactData.getArtifactDataDefinition().getArtifactName(), String.valueOf(operationStatus));
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError("Add artifact", artifactData.getArtifactDataDefinition().getArtifactName(), String.valueOf(operationStatus));
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(operationStatus);
+ }
+
+ // add heat parameters
+ if (artifactInfo.getHeatParameters() != null && !artifactInfo.getHeatParameters().isEmpty() && !artifactInfo.getArtifactType().equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ StorageOperationStatus addPropertiesStatus = heatParametersOperation.addPropertiesToGraph(artifactInfo.getHeatParameters(), artifactData.getUniqueId().toString(), NodeTypeEnum.ArtifactRef);
+ if (addPropertiesStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to create heat parameters on graph for artifact {}", artifactInfo.getArtifactName());
+ return addPropertiesStatus;
+ }
+ }
+
+ } else {
+ log.debug("Failed to check existance of artifact in graph for id {}", artifactData.getUniqueId());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(existArtifact.right().value());
+ }
+ } else if (failIfexist) {
+ log.debug("Artifact {} already exist", artifactData.getUniqueId());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.ALREADY_EXIST);
+ }
+
+ // save logical artifact ref name on edge as property
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), artifactInfo.getArtifactLabel());
+ if (artifactInfo.getArtifactGroupType() != null)
+ properties.put(GraphEdgePropertiesDictionary.GROUP_TYPE.getProperty(), artifactInfo.getArtifactGroupType().getType());
+ TitanOperationStatus relation = titanGenericDao.createEdge(parentVertex, artifactData, GraphEdgeLabels.ARTIFACT_REF, properties);
+ if (!relation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create relation in graph for id {} to new artifact", id);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(relation);
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ private Either<ArtifactData, StorageOperationStatus> addArtifactToGraph(ArtifactDefinition artifactInfo, String id, NodeTypeEnum type, boolean failIfexist) {
+
+ if (artifactInfo.getUniqueId() == null || artifactInfo.getUniqueId().isEmpty()) {
+ String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(id, artifactInfo.getArtifactLabel());
+ artifactInfo.setUniqueId(uniqueId);
+ }
+
+ if (validateParentType(type) == false) {
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ ArtifactData artifactData = new ArtifactData(artifactInfo);
+
+ Either<ArtifactData, TitanOperationStatus> existArtifact = titanGenericDao.getNode(artifactData.getUniqueIdKey(), artifactData.getUniqueId(), ArtifactData.class);
+ if (existArtifact.isRight()) {
+ if (existArtifact.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ // create new node
+ log.debug("Before adding artifact to graph {}", artifactData);
+ if (artifactData.getArtifactDataDefinition().getArtifactUUID() == null || artifactData.getArtifactDataDefinition().getArtifactUUID().isEmpty())
+ updateUUID(artifactData.getArtifactDataDefinition(), null, artifactData.getArtifactDataDefinition().getArtifactVersion());
+ Either<ArtifactData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(artifactData, ArtifactData.class);
+ log.debug("After adding artifact to graph {}", artifactData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.debug("Failed to add artifact {} to graph. status is {}", artifactData.getArtifactDataDefinition().getArtifactName(), operationStatus);
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedCreateNodeError, "Failed to add artifact " + artifactData.getArtifactDataDefinition().getArtifactName() + " to graph. status is " + operationStatus,
+ artifactData.getArtifactDataDefinition().getArtifactName(), String.valueOf(operationStatus));
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError("Add artifact", artifactData.getArtifactDataDefinition().getArtifactName(), String.valueOf(operationStatus));
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationStatus));
+ }
+ artifactData = createNodeResult.left().value();
+
+ // add heat parameters
+ if (artifactInfo.getHeatParameters() != null && !artifactInfo.getHeatParameters().isEmpty() && !artifactInfo.getArtifactType().equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ StorageOperationStatus addPropertiesStatus = heatParametersOperation.addPropertiesToGraph(artifactInfo.getHeatParameters(), artifactData.getUniqueId().toString(), NodeTypeEnum.ArtifactRef);
+ if (addPropertiesStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to create heat parameters on graph for artifact {}", artifactInfo.getArtifactName());
+ return Either.right(addPropertiesStatus);
+ }
+ }
+
+ } else {
+ log.debug("Failed to check existance of artifact in graph for id {}", artifactData.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(existArtifact.right().value()));
+ }
+ } else if (failIfexist) {
+ log.debug("Artifact {} already exist", artifactData.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.ALREADY_EXIST));
+ } else {
+ artifactData = existArtifact.left().value();
+ }
+
+ UniqueIdData parent = new UniqueIdData(type, id);
+
+ // save logical artifact ref name on edge as property
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), artifactInfo.getArtifactLabel());
+ if (artifactInfo.getArtifactGroupType() != null)
+ properties.put(GraphEdgePropertiesDictionary.GROUP_TYPE.getProperty(), artifactInfo.getArtifactGroupType().getType());
+ Either<GraphRelation, TitanOperationStatus> relation = titanGenericDao.createRelation(parent, artifactData, GraphEdgeLabels.ARTIFACT_REF, properties);
+ if (relation.isRight()) {
+ log.debug("Failed to create relation in graph for id {} to new artifact", id);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(relation.right().value()));
+ }
+
+ return Either.left(artifactData);
+ }
+
+ private boolean validateParentType(NodeTypeEnum type) {
+ boolean isValid = false;
+ switch (type) {
+ case Resource:
+ case InterfaceOperation:
+ case Service:
+ case ResourceInstance:
+ isValid = true;
+ break;
+ default:
+ log.debug("Not supported node type for artifact relation : {}", type);
+ }
+ return isValid;
+ }
+
+ protected ArtifactDefinition convertArtifactDataToArtifactDefinition(ArtifactData artifactDefResult) {
+ log.debug("The object returned after create property is {}", artifactDefResult);
+
+ ArtifactDefinition propertyDefResult = new ArtifactDefinition(artifactDefResult.getArtifactDataDefinition());
+ List<HeatParameterDefinition> parameters = new ArrayList<HeatParameterDefinition>();
+ StorageOperationStatus heatParametersOfNode = heatParametersOperation.getHeatParametersOfNode(NodeTypeEnum.ArtifactRef, artifactDefResult.getUniqueId().toString(), parameters);
+ if ((heatParametersOfNode.equals(StorageOperationStatus.OK)) && !parameters.isEmpty()) {
+ propertyDefResult.setHeatParameters(parameters);
+ }
+ return propertyDefResult;
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> updateArifactOnResource(ArtifactDefinition artifactInfo, String id, String artifactId, NodeTypeEnum type, boolean inTransaction) {
+ Either<ArtifactData, StorageOperationStatus> status = updateArtifactOnGraph(artifactInfo, artifactId, type, id);
+
+ if (status.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ if (log.isDebugEnabled()){
+ log.debug("Failed to update artifact {} of {} {}. Status is {}", artifactId, type.getName(), id, status.right().value());
+ }
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedUpdateNodeError, "Failed to update artifact " + artifactId + " of " + type.getName() + " " + id + ". status is" + status.right().value(), artifactId,
+ String.valueOf(status.right().value()));
+ BeEcompErrorManager.getInstance().logBeFailedUpdateNodeError("Update Artifact", artifactId, String.valueOf(status.right().value()));
+ return Either.right(status.right().value());
+ } else {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ ArtifactData artifactData = status.left().value();
+
+ ArtifactDefinition artifactDefResult = convertArtifactDataToArtifactDefinition(artifactData);
+ log.debug("The returned ArtifactDefintion is {}", artifactDefResult);
+ return Either.left(artifactDefResult);
+ }
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> updateArifactDefinition(ArtifactDefinition artifactInfo, boolean inTransaction) {
+ Either<ArtifactData, TitanOperationStatus> status = updateArifactDataDefinition(artifactInfo);
+
+ if (status.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.debug("Failed to update artifact {}", artifactInfo.getUniqueId());
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedUpdateNodeError, "Failed to update artifact " + artifactInfo.getUniqueId() + ". status is" + status.right().value(), artifactInfo.getUniqueId(),
+ String.valueOf(status.right().value()));
+ BeEcompErrorManager.getInstance().logBeFailedUpdateNodeError("Update Artifact", artifactInfo.getUniqueId(), String.valueOf(status.right().value()));
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ } else {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ ArtifactData artifactData = status.left().value();
+
+ ArtifactDefinition artifactDefResult = convertArtifactDataToArtifactDefinition(artifactData);
+ log.debug("The returned ArtifactDefintion is {}", artifactDefResult);
+ return Either.left(artifactDefResult);
+ }
+ }
+
+ private Either<ArtifactData, TitanOperationStatus> updateArifactDataDefinition(ArtifactDefinition artifactInfo) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(graph.right().value());
+ }
+
+ ArtifactData artifactData = new ArtifactData(artifactInfo);
+
+ Either<ArtifactData, TitanOperationStatus> status = titanGenericDao.updateNode(artifactData, ArtifactData.class);
+
+ if (status.isRight()) {
+ return Either.right(status.right().value());
+
+ }
+ return Either.left(status.left().value());
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> removeArifactFromResource(String id, String artifactId, NodeTypeEnum type, boolean deleteMandatoryArtifact, boolean inTransaction) {
+ Either<ArtifactData, TitanOperationStatus> status = removeArtifactOnGraph(id, artifactId, type, id, deleteMandatoryArtifact);
+
+ if (status.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.debug("Failed to delete artifact {} of resource {}", artifactId, id);
+
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedDeleteNodeError, "Failed to delete artifact " + artifactId + " of resource " + id, artifactId, String.valueOf(status.right().value()));
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError("Delete Artifact", artifactId, String.valueOf(status.right().value()));
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ } else {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ ArtifactData artifactData = status.left().value();
+
+ ArtifactDefinition artifactDefResult = convertArtifactDataToArtifactDefinition(artifactData);
+ log.debug("The returned ArtifactDefintion is {}", artifactDefResult);
+ return Either.left(artifactDefResult);
+ }
+ }
+
+ public Either<ArtifactData, StorageOperationStatus> updateToscaArtifactNameOnGraph(ArtifactDefinition artifactInfo, String artifactId, NodeTypeEnum type, String id) {
+ return updateArtifactOnGraph(artifactInfo, artifactId, type, id);
+ }
+
+ @SuppressWarnings("null")
+ private Either<ArtifactData, StorageOperationStatus> updateArtifactOnGraph(ArtifactDefinition artifactInfo, String artifactId, NodeTypeEnum type, String id) {
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ }
+
+ TitanGraph tGraph = graph.left().value();
+
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> verticesArtifact = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId).vertices();
+ Iterator<TitanVertex> iterator = verticesArtifact.iterator();
+ if (!iterator.hasNext()) {
+ log.debug("No artifact node for id = {}", artifactId);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ TitanVertex artifactV = iterator.next();
+
+ Iterator<Edge> iterEdge = artifactV.edges(Direction.IN, GraphEdgeLabels.ARTIFACT_REF.getProperty());
+
+ int edgeCount = 0;
+ Edge edgeFromTo = null;
+ while (iterEdge.hasNext()) {
+ Edge edge = iterEdge.next();
+ Vertex vertexFrom = edge.outVertex();
+ String vertexId = vertexFrom.value(UniqueIdBuilder.getKeyByNodeType(type));
+ if (id.equals(vertexId)) {
+ edgeFromTo = edge;
+ }
+ ++edgeCount;
+ }
+
+ ArtifactData artifactData = new ArtifactData(artifactInfo);
+ if (edgeFromTo == null) {
+ log.debug("No relation between artifact = {} and node with id = {}", artifactId, id);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ Either<Boolean, StorageOperationStatus> setRelevantHeatParamIdRes = null;
+ if (edgeCount > 1) {
+ // need to remove relation, create new node
+ log.debug("artifactRef have more connection. Need to clone node");
+ log.debug("remove edge {}", edgeFromTo);
+ edgeFromTo.remove();
+ // update resource id in new artifact node
+ String uniqueId = UniqueIdBuilder.buildPropertyUniqueId(id, artifactInfo.getArtifactLabel());
+ artifactInfo.setUniqueId(uniqueId);
+ // update UUID and artifact version
+ String oldChecksum = artifactV.valueOrNull(titanGenericDao.getGraph().left().value().getPropertyKey(GraphPropertiesDictionary.ARTIFACT_CHECKSUM.getProperty()));
+ String oldVersion = artifactV.valueOrNull(titanGenericDao.getGraph().left().value().getPropertyKey(GraphPropertiesDictionary.ARTIFACT_VERSION.getProperty()));
+ updateUUID(artifactInfo, oldChecksum, oldVersion);
+ log.debug("try to create new artifact ref node for id {}", uniqueId);
+ Either<ArtifactData, StorageOperationStatus> addedArtifactRes = addArtifactToGraph(artifactInfo, id, type, true);
+
+ if (addedArtifactRes.isLeft()) {
+ // remove all relations between groups to the old artifact
+ // add relation between the same groups to the new artifact
+ StorageOperationStatus reassociateGroupsFromArtifact = groupOperation.dissociateAndAssociateGroupsFromArtifact(id, type, artifactId, addedArtifactRes.left().value(), true);
+ if (reassociateGroupsFromArtifact != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdateArtifact", "Failed to reassociate groups to the new artifact", ErrorSeverity.ERROR);
+ return Either.right(reassociateGroupsFromArtifact);
+ }
+ // If artifact is heat env
+ if (artifactInfo.getArtifactType().equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ ArtifactData addedArtifact = addedArtifactRes.left().value();
+ String newArtifactUniqueId = (String) addedArtifact.getUniqueId();
+ Either<HeatParameterValueData, StorageOperationStatus> updateResult = null;
+
+ setRelevantHeatParamIdRes = setRelevantHeatParamId(artifactV, artifactInfo);
+ if (setRelevantHeatParamIdRes.isRight()) {
+ log.error("Failed to set relevant id to heat parameters for heat env artifact " + artifactInfo.getUniqueId() + ". Status is " + setRelevantHeatParamIdRes.right().value());
+ return Either.right(setRelevantHeatParamIdRes.right().value());
+ }
+ for (HeatParameterDefinition heatEnvParam : artifactInfo.getHeatParameters()) {
+ updateResult = heatParametersOperation.updateHeatParameterValue(heatEnvParam, newArtifactUniqueId, id, artifactInfo.getArtifactLabel());
+ if (updateResult.isRight()) {
+ log.error("Failed to update heat parameter " + heatEnvParam.getName() + ". Status is " + updateResult.right().value());
+ return Either.right(updateResult.right().value());
+ }
+ }
+
+ Iterator<Edge> iterEdgeGeneratedFrom = artifactV.edges(Direction.OUT, GraphEdgeLabels.GENERATED_FROM.getProperty());
+
+ if (!iterEdgeGeneratedFrom.hasNext()) {
+ log.error("No heat artifact node for id = " + artifactId);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Edge edgeToHeat = iterEdgeGeneratedFrom.next();
+ Vertex vertexIn = edgeToHeat.inVertex();
+ String generatedFromArtifactId = vertexIn.value(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef));
+ UniqueIdData generatedFromArtifactNode = new UniqueIdData(NodeTypeEnum.ArtifactRef, generatedFromArtifactId);
+ Either<GraphRelation, TitanOperationStatus> createRelationToGeneratedFromArtifactRes = titanGenericDao.createRelation(addedArtifact, generatedFromArtifactNode, GraphEdgeLabels.GENERATED_FROM, null);
+ if (createRelationToGeneratedFromArtifactRes.isRight()) {
+ log.error("Failed to create relation from heat_env " + addedArtifact.getUniqueId() + " to heat " + generatedFromArtifactNode);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(createRelationToGeneratedFromArtifactRes.right().value()));
+ }
+ }
+ }
+ return addedArtifactRes;
+
+ } else {
+ if (edgeCount == 1) {
+ String oldChecksum = artifactV.valueOrNull(titanGenericDao.getGraph().left().value().getPropertyKey(GraphPropertiesDictionary.ARTIFACT_CHECKSUM.getProperty()));
+ String oldVersion = artifactV.valueOrNull(titanGenericDao.getGraph().left().value().getPropertyKey(GraphPropertiesDictionary.ARTIFACT_VERSION.getProperty()));
+ updateUUID(artifactInfo, oldChecksum, oldVersion);
+ // update exist
+ Either<ArtifactData, TitanOperationStatus> updatedArtifact = titanGenericDao.updateNode(artifactData, ArtifactData.class);
+ if (updatedArtifact.isRight()) {
+ log.debug("failed to update artifact node for id {}", artifactData.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updatedArtifact.right().value()));
+ }
+
+ if (artifactInfo.getArtifactType().equals(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ Either<HeatParameterValueData, StorageOperationStatus> updateResult = null;
+ String artifactUniqueId = artifactInfo.getUniqueId();
+ setRelevantHeatParamIdRes = setRelevantHeatParamId(artifactV, artifactInfo);
+ if (setRelevantHeatParamIdRes.isRight()) {
+ log.error("Failed to set relevant id to heat parameters for heat env artifact {}. Status is {}", artifactInfo.getUniqueId(), setRelevantHeatParamIdRes.right().value());
+ return Either.right(setRelevantHeatParamIdRes.right().value());
+ }
+ for (HeatParameterDefinition heatEnvParam : artifactInfo.getHeatParameters()) {
+ updateResult = heatParametersOperation.updateHeatParameterValue(heatEnvParam, artifactUniqueId, id, artifactInfo.getArtifactLabel());
+ if (updateResult.isRight()) {
+ log.error("Failed to update heat parameter " + heatEnvParam.getName() + ". Status is " + updateResult.right().value());
+ return Either.right(updateResult.right().value());
+ }
+ }
+ } else {
+ if (artifactData.getArtifactDataDefinition().getArtifactChecksum() == null) {
+ // update heat parameters only if it is not heat env
+ if (artifactInfo.getGeneratedFromId() == null) {
+ StorageOperationStatus operationStatus = heatParametersOperation.updateHeatParameters(artifactInfo.getHeatParameters());
+ if (operationStatus != StorageOperationStatus.OK) {
+ return Either.right(operationStatus);
+ }
+ }
+ } else {
+ Either<List<HeatParameterDefinition>, StorageOperationStatus> deleteParameters = heatParametersOperation.deleteAllHeatParametersAssociatedToNode(NodeTypeEnum.ArtifactRef, artifactInfo.getUniqueId());
+ if (deleteParameters.isRight()) {
+ log.debug("failed to update heat parameters for artifact id {}", artifactData.getUniqueId());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ StorageOperationStatus addParameters = heatParametersOperation.addPropertiesToGraph(artifactInfo.getHeatParameters(), artifactId, NodeTypeEnum.ArtifactRef);
+ if (!addParameters.equals(StorageOperationStatus.OK)) {
+ log.debug("failed to update heat parameters for artifact id {}", artifactData.getUniqueId());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+ }
+
+ return Either.left(updatedArtifact.left().value());
+ } else {
+ log.debug("No relevent edges for artifact = {}", artifactId);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+ }
+
+ private Either<Boolean, StorageOperationStatus> setRelevantHeatParamId(TitanVertex artifactV, ArtifactDefinition artifactInfo) {
+
+ Map<String, String> heatParametersHM = new HashMap<String, String>();
+
+ Iterator<Edge> iterHeat = artifactV.edges(Direction.OUT, GraphEdgeLabels.GENERATED_FROM.getProperty());
+ if (!iterHeat.hasNext()) {
+ log.debug("No edges with label GENERATED_FROM for the node {}", artifactInfo.getUniqueId());
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Edge heat = iterHeat.next();
+ Vertex heatVertex = heat.inVertex();
+ String heatUniqueId = (String) heatVertex.value(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef));
+
+ Either<List<ImmutablePair<HeatParameterData, GraphEdge>>, TitanOperationStatus> getHeatParametersRes = titanGenericDao.getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), heatUniqueId, GraphEdgeLabels.HEAT_PARAMETER,
+ NodeTypeEnum.HeatParameter, HeatParameterData.class);
+ if (getHeatParametersRes.isRight()) {
+ log.debug("No heat parameters for heat artifact {}", heatUniqueId);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ List<ImmutablePair<HeatParameterData, GraphEdge>> heatParameters = getHeatParametersRes.left().value();
+ if (heatParameters == null) {
+ log.debug("No heat parameters for heat artifact {}", heatUniqueId);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ for (ImmutablePair<HeatParameterData, GraphEdge> heatParamEdge : heatParameters) {
+ HeatParameterData heatParam = heatParamEdge.getLeft();
+ heatParametersHM.put(heatParam.getName(), (String) heatParam.getUniqueId());
+ }
+ String curName = null;
+ for (HeatParameterDefinition heatEnvParam : artifactInfo.getHeatParameters()) {
+ curName = heatEnvParam.getName();
+ if (heatParametersHM.containsKey(curName)) {
+ heatEnvParam.setUniqueId(heatParametersHM.get(curName));
+ }
+ }
+ return Either.left(true);
+ }
+
+ public Either<Integer, StorageOperationStatus> getParentsOfArtifact(String artifactId, NodeTypeEnum type) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ }
+ TitanGraph tGraph = graph.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> verticesArtifact = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId).vertices();
+ Iterator<TitanVertex> iterator = verticesArtifact.iterator();
+ if (!iterator.hasNext()) {
+ log.debug("No artifact node for id = {}", artifactId);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Vertex artifactV = iterator.next();
+ Iterator<Edge> iterEdge = artifactV.edges(Direction.IN, GraphEdgeLabels.ARTIFACT_REF.getProperty());
+ int edgeCount = 0;
+ while (iterEdge.hasNext()) {
+ Edge edge = iterEdge.next();
+ ++edgeCount;
+ }
+ return Either.left(edgeCount);
+ }
+
+ public Either<ArtifactData, TitanOperationStatus> removeArtifactOnGraph(String id, String artifactId, NodeTypeEnum type, String id2, boolean deleteMandatoryArtifact) {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(graph.right().value());
+ }
+
+ TitanGraph tGraph = graph.left().value();
+ Either<ArtifactData, TitanOperationStatus> artifactData = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId, ArtifactData.class);
+ if (artifactData.isRight()) {
+ log.debug("Failed to retrieve artifact for id = {}", artifactId);
+ return Either.right(artifactData.right().value());
+ }
+ ArtifactDataDefinition artifactDefinition = artifactData.left().value().getArtifactDataDefinition();
+ boolean isMandatory = false;
+ if ((artifactDefinition.getMandatory() || artifactDefinition.getServiceApi()) && !deleteMandatoryArtifact) {
+ // return Either.left(artifactData.left().value());
+ isMandatory = true;
+ }
+
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> verticesArtifact = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId).vertices();
+ Iterator<TitanVertex> iterator = verticesArtifact.iterator();
+ if (!iterator.hasNext()) {
+ log.debug("No artifact node for id = {}", artifactId);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ Vertex artifactV = iterator.next();
+ Iterator<Edge> iterEdge = artifactV.edges(Direction.IN, GraphEdgeLabels.ARTIFACT_REF.getProperty());
+ int edgeCount = 0;
+ Edge edgeFromTo = null;
+ while (iterEdge.hasNext()) {
+ Edge edge = iterEdge.next();
+ Vertex vertexFrom = edge.outVertex();
+ String vertexId = vertexFrom.value(UniqueIdBuilder.getKeyByNodeType(type));
+ if (id.equals(vertexId)) {
+ edgeFromTo = edge;
+ }
+ ++edgeCount;
+ }
+ if (edgeFromTo == null) {
+ log.debug("No relation between artifact = {} and node with id = {}", artifactId, id);
+ return Either.right(TitanOperationStatus.GENERAL_ERROR);
+ }
+
+ // need to remove relation from resource/interface
+
+ log.debug("remove edge {}", edgeFromTo);
+ if (!isMandatory || (isMandatory && edgeCount > 1)) {
+ edgeFromTo.remove();
+ }
+
+ // delete edges from all groups under the component id which related to
+ // this artifact.
+ // Also in case it is a mandatory artifact.
+ Either<List<GraphRelation>, TitanOperationStatus> dissociateAllGroups = groupOperation.dissociateAllGroupsFromArtifactOnGraph(id, type, artifactId);
+ if (dissociateAllGroups.isRight()) {
+ TitanOperationStatus status = dissociateAllGroups.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND && status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ }
+
+ if (edgeCount == 1) {
+ // remove artifactRef node
+ log.debug("Remove artifactRef node from graph");
+ Either<List<HeatParameterDefinition>, StorageOperationStatus> deleteStatus = heatParametersOperation.deleteAllHeatParametersAssociatedToNode(NodeTypeEnum.ArtifactRef, artifactId);
+ if (deleteStatus.isRight()) {
+ log.error("failed to delete heat parameters of artifact " + artifactId);
+ return Either.right(TitanOperationStatus.GENERAL_ERROR);
+ }
+
+ StorageOperationStatus deleteValuesStatus = heatParametersOperation.deleteAllHeatValuesAssociatedToNode(NodeTypeEnum.ArtifactRef, artifactId);
+ if (!deleteValuesStatus.equals(StorageOperationStatus.OK)) {
+ log.error("failed to delete heat values of artifact " + artifactId);
+ return Either.right(TitanOperationStatus.GENERAL_ERROR);
+ }
+ if (!isMandatory) {
+ artifactV.remove();
+ }
+ } else {
+ log.debug("artifactRef have more connection. ArtifactRef node will not be removed ");
+ }
+
+ return Either.left(artifactData.left().value());
+
+ }
+
+ /**
+ *
+ * @param parentId
+ * @param parentType
+ * @param inTransaction
+ * @return
+ */
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifacts(String parentId, NodeTypeEnum parentType, boolean inTransaction) {
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> result = null;
+ try {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Failed to work with graph {}", graph.right().value());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ }
+ TitanGraph tGraph = graph.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(parentType), parentId).vertices();
+ if (vertices == null) {
+ log.debug("No nodes for type {} for id = {}", parentType, parentId);
+ result = Either.right(StorageOperationStatus.NOT_FOUND);
+ return result;
+ }
+ Iterator<TitanVertex> iterator = vertices.iterator();
+
+ Map<String, ArtifactDefinition> artifactMap = new HashMap<String, ArtifactDefinition>();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ Iterator<Edge> iteratorEdge = vertex.edges(Direction.OUT, GraphEdgeLabels.ARTIFACT_REF.getProperty());
+
+ if (iteratorEdge != null) {
+
+ while (iteratorEdge.hasNext()) {
+ Edge edge = iteratorEdge.next();
+
+ Vertex artifactV = edge.inVertex();
+
+ Map<String, Object> properties = this.titanGenericDao.getProperties(artifactV);
+ ArtifactData artifact = GraphElementFactory.createElement(NodeTypeEnum.ArtifactRef.getName(), GraphElementTypeEnum.Node, properties, ArtifactData.class);
+ if (artifact != null) {
+
+ ArtifactDefinition artifactDefinition = new ArtifactDefinition(artifact.getArtifactDataDefinition());
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ StorageOperationStatus heatParametersStatus = heatParametersOperation.getHeatParametersOfNode(NodeTypeEnum.ArtifactRef, artifactDefinition.getUniqueId(), heatParams);
+ if (!heatParametersStatus.equals(StorageOperationStatus.OK)) {
+ log.debug("failed to get heat parameters for node {} {}", parentType.getName(), parentId);
+ return Either.right(heatParametersStatus);
+ }
+ if (!heatParams.isEmpty()) {
+ artifactDefinition.setHeatParameters(heatParams);
+ }
+ artifactMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+ log.debug("Artifact was added to list {}", artifact.getUniqueId());
+ }
+ }
+ }
+ }
+ result = Either.left(artifactMap);
+ return result;
+ } finally {
+ if (inTransaction == false) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+
+ }
+ }
+
+ }
+
+ public Either<Map<String, TitanVertex>, StorageOperationStatus> getArtifactsVertecies(String parentId, NodeTypeEnum parentType, boolean inTransaction) {
+ Either<Map<String, TitanVertex>, StorageOperationStatus> result = null;
+ try {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Failed to work with graph {}", graph.right().value());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ }
+ TitanGraph tGraph = graph.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(parentType), parentId).vertices();
+ if (vertices == null) {
+ log.debug("No nodes for type {} for id = {}", parentType, parentId);
+ result = Either.right(StorageOperationStatus.NOT_FOUND);
+ return result;
+ }
+ Iterator<TitanVertex> iterator = vertices.iterator();
+
+ Map<String, TitanVertex> artifactMap = new HashMap<String, TitanVertex>();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ Iterator<Edge> iteratorEdge = vertex.edges(Direction.OUT, GraphEdgeLabels.ARTIFACT_REF.getProperty());
+
+ if (iteratorEdge != null) {
+
+ while (iteratorEdge.hasNext()) {
+ Edge edge = iteratorEdge.next();
+
+ TitanVertex artifactV = (TitanVertex) edge.inVertex();
+ String label = (String) titanGenericDao.getProperty(artifactV, GraphPropertiesDictionary.ARTIFACT_LABEL.getProperty());
+ artifactMap.put(label, artifactV);
+ log.debug("Artifact was added to list {}", label);
+ }
+ }
+ }
+ result = Either.left(artifactMap);
+ return result;
+ } finally {
+ if (inTransaction == false) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+
+ }
+ }
+
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> getArtifactById(String id, boolean inTransaction) {
+ Either<ArtifactDefinition, StorageOperationStatus> result = null;
+ try {
+ Either<ArtifactData, TitanOperationStatus> artifact = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), id, ArtifactData.class);
+ if (artifact.isRight()) {
+ if (artifact.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ result = Either.right(StorageOperationStatus.ARTIFACT_NOT_FOUND);
+ } else {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(artifact.right().value()));
+ }
+ return result;
+ }
+ ArtifactData artifactData = artifact.left().value();
+
+ ArtifactDefinition artifactDef = new ArtifactDefinition(artifactData.getArtifactDataDefinition());
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ StorageOperationStatus heatParametersStatus = heatParametersOperation.getHeatParametersOfNode(NodeTypeEnum.ArtifactRef, artifactDef.getUniqueId(), heatParams);
+ if (!heatParametersStatus.equals(StorageOperationStatus.OK)) {
+ log.debug("failed to get heat parameters for artifact {}", id);
+ return Either.right(heatParametersStatus);
+ }
+ if (!heatParams.isEmpty()) {
+ artifactDef.setHeatParameters(heatParams);
+ }
+
+ Either<ImmutablePair<ArtifactData, GraphEdge>, TitanOperationStatus> generatedFromArtifact = titanGenericDao.getChild(artifactData.getUniqueIdKey(), (String) artifactData.getUniqueId(), GraphEdgeLabels.GENERATED_FROM,
+ NodeTypeEnum.ArtifactRef, ArtifactData.class);
+ if (generatedFromArtifact.isLeft()) {
+ ImmutablePair<ArtifactData, GraphEdge> pair = generatedFromArtifact.left().value();
+ String generatedFromId = (String) pair.left.getUniqueId();
+ log.debug("artifact {} is generated from {}.", artifactData.getUniqueId(), generatedFromId);
+ artifactDef.setGeneratedFromId(generatedFromId);
+ Either<List<HeatParameterDefinition>, StorageOperationStatus> heatParamsForEnv = getHeatParamsForEnv(artifactDef);
+ if (heatParamsForEnv.isRight()) {
+ log.debug("failed to get heat parameters values for heat artifact {}", artifactDef.getUniqueId());
+ return Either.right(heatParamsForEnv.right().value());
+ } else {
+ artifactDef.setHeatParameters(heatParamsForEnv.left().value());
+ }
+ }
+
+ result = Either.left(artifactDef);
+ return result;
+ } finally {
+ if (inTransaction == false) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+
+ }
+ }
+ }
+
+ public Either<List<HeatParameterDefinition>, StorageOperationStatus> getHeatParamsForEnv(ArtifactDefinition heatEnvArtifact) {
+
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ StorageOperationStatus heatParametersStatus = heatParametersOperation.getHeatParametersOfNode(NodeTypeEnum.ArtifactRef, heatEnvArtifact.getGeneratedFromId(), heatParams);
+ if (!heatParametersStatus.equals(StorageOperationStatus.OK)) {
+ log.debug("failed to get heat parameters for node {}", heatEnvArtifact.getGeneratedFromId());
+ return Either.right(heatParametersStatus);
+ }
+ if (!heatParams.isEmpty()) {
+
+ Map<String, HeatParameterValueData> heatValuesMap = new HashMap<String, HeatParameterValueData>();
+ Either<List<ImmutablePair<HeatParameterValueData, GraphEdge>>, TitanOperationStatus> heatEnvValuesWithEdges = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), heatEnvArtifact.getUniqueId(),
+ GraphEdgeLabels.PARAMETER_VALUE, NodeTypeEnum.HeatParameterValue, HeatParameterValueData.class);
+
+ if (heatEnvValuesWithEdges.isRight() && !heatEnvValuesWithEdges.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus status = heatEnvValuesWithEdges.right().value();
+ if (!status.equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ } else if (heatEnvValuesWithEdges.isLeft()) {
+ for (ImmutablePair<HeatParameterValueData, GraphEdge> pair : heatEnvValuesWithEdges.left().value()) {
+ HeatParameterValueData parameterValue = pair.left;
+ Object heatParameterName = pair.right.getProperties().get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ heatValuesMap.put((String) heatParameterName, parameterValue);
+ }
+ }
+
+ for (HeatParameterDefinition parameter : heatParams) {
+ if (parameter.getCurrentValue() != null) {
+ if ("number".equals(parameter.getType())) {
+ parameter.setDefaultValue(new BigDecimal(parameter.getCurrentValue()).toPlainString());
+ } else {
+ parameter.setDefaultValue(parameter.getCurrentValue());
+ }
+ }
+ HeatParameterValueData heatParameterValueData = heatValuesMap.get(parameter.getName());
+ if (heatParameterValueData != null && heatParameterValueData.getValue() != null) {
+ if ("number".equals(parameter.getType())) {
+ parameter.setCurrentValue(new BigDecimal(heatParameterValueData.getValue()).toPlainString());
+ } else {
+ parameter.setCurrentValue(heatParameterValueData.getValue());
+ }
+ parameter.setUniqueId((String) heatParameterValueData.getUniqueId());
+
+ }
+ }
+ }
+
+ return Either.left(heatParams);
+
+ }
+
+ @Override
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifacts(String parentId, NodeTypeEnum parentType, boolean inTransaction, String groupType) {
+
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> result = null;
+ try {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Failed to work with graph {}", graph.right().value());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ }
+ TitanGraph tGraph = graph.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(parentType), parentId).vertices();
+ if (vertices == null || vertices.iterator() == null || false == vertices.iterator().hasNext()) {
+ log.debug("No nodes for type {} for id = {}", parentType, parentId);
+ result = Either.right(StorageOperationStatus.NOT_FOUND);
+ return result;
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> edgeProperties = null;
+ if (groupType != null) {
+ edgeProperties = new HashMap<>();
+ edgeProperties.put(GraphEdgePropertiesDictionary.GROUP_TYPE.getProperty(), groupType);
+ }
+ Either<List<ImmutablePair<ArtifactData, GraphEdge>>, TitanOperationStatus> childrenByEdgeCriteria = titanGenericDao.getChildrenByEdgeCriteria(vertex, parentId, GraphEdgeLabels.ARTIFACT_REF, NodeTypeEnum.ArtifactRef, ArtifactData.class,
+ edgeProperties);
+
+ if (childrenByEdgeCriteria.isRight()) {
+ TitanOperationStatus status = childrenByEdgeCriteria.right().value();
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ List<ImmutablePair<ArtifactData, GraphEdge>> list = childrenByEdgeCriteria.left().value();
+
+ Map<String, ArtifactDefinition> artifactsMap = new HashMap<>();
+
+ for (ImmutablePair<ArtifactData, GraphEdge> pair : list) {
+ ArtifactData artifactData = pair.getLeft();
+ ArtifactDefinition artifactDefinition = new ArtifactDefinition(artifactData.getArtifactDataDefinition());
+
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ StorageOperationStatus heatParametersStatus = heatParametersOperation.getHeatParametersOfNode(NodeTypeEnum.ArtifactRef, artifactDefinition.getUniqueId(), heatParams);
+ if (!heatParametersStatus.equals(StorageOperationStatus.OK)) {
+ log.debug("failed to get heat parameters for node {} {}", parentType.getName(), parentId);
+ return Either.right(heatParametersStatus);
+ }
+ if (!heatParams.isEmpty()) {
+ artifactDefinition.setHeatParameters(heatParams);
+ }
+
+ Either<ImmutablePair<ArtifactData, GraphEdge>, TitanOperationStatus> getResult = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactDefinition.getUniqueId(), GraphEdgeLabels.GENERATED_FROM,
+ NodeTypeEnum.ArtifactRef, ArtifactData.class);
+
+ if (getResult.isRight()) {
+ TitanOperationStatus status = getResult.right().value();
+ if (!status.equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ } else {
+ ImmutablePair<ArtifactData, GraphEdge> immutablePair = getResult.left().value();
+ artifactDefinition.setGeneratedFromId((String) immutablePair.left.getUniqueId());
+ }
+
+ artifactsMap.put(artifactDefinition.getArtifactLabel(), artifactDefinition);
+ log.debug("Artifact {} was added to list ", artifactData.getUniqueId());
+ }
+
+ result = Either.left(artifactsMap);
+ return result;
+
+ } finally {
+ if (inTransaction == false) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> addHeatEnvArtifact(ArtifactDefinition artifactHeatEnv, ArtifactDefinition artifactHeat, String parentId, NodeTypeEnum parentType, boolean inTransaction) {
+
+ Either<ArtifactDefinition, StorageOperationStatus> result = null;
+ try {
+ Either<ArtifactDefinition, StorageOperationStatus> heatArtifactOnGraph = addArifactToComponent(artifactHeatEnv, parentId, parentType, true, true);
+
+ if (heatArtifactOnGraph.isRight()) {
+ log.debug("failed to create heat env artifact on graph");
+ result = heatArtifactOnGraph;
+ return result;
+ }
+
+ ArtifactDefinition artifactDefinition = heatArtifactOnGraph.left().value();
+
+ // add relation from heatEnv to heat
+ UniqueIdData heatData = new UniqueIdData(NodeTypeEnum.ArtifactRef, artifactHeat.getUniqueId());
+ UniqueIdData heatEnvData = new UniqueIdData(NodeTypeEnum.ArtifactRef, artifactDefinition.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(heatEnvData, heatData, GraphEdgeLabels.GENERATED_FROM, null);
+
+ if (createRelation.isRight()) {
+ TitanOperationStatus status = createRelation.right().value();
+ log.debug("failed to add relation from heat_env artifact to heat artifact. error: {}", status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ artifactDefinition.setGeneratedFromId(artifactHeat.getUniqueId());
+ log.trace("heat env artifact added successfuly to resource instance");
+ result = Either.left(artifactDefinition);
+ return result;
+ } finally {
+ if (inTransaction == false) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+
+ }
+ }
+ }
+
+ public void updateUUID(ArtifactDataDefinition artifactData, String oldChecksum, String oldVesrion) {
+ if (oldVesrion == null || oldVesrion.isEmpty())
+ oldVesrion = "0";
+
+ String currentChecksum = artifactData.getArtifactChecksum();
+ if (oldChecksum == null || oldChecksum.isEmpty()) {
+ if (currentChecksum != null) {
+ generateUUID(artifactData, oldVesrion);
+ }
+ } else if ((currentChecksum != null && !currentChecksum.isEmpty()) && !oldChecksum.equals(currentChecksum)) {
+ generateUUID(artifactData, oldVesrion);
+ }
+
+ }
+
+ private void generateUUID(ArtifactDataDefinition artifactData, String oldVesrion) {
+
+ UUID uuid = UUID.randomUUID();
+ artifactData.setArtifactUUID(uuid.toString());
+ MDC.put("serviceInstanceID", uuid.toString());
+ long time = System.currentTimeMillis();
+ artifactData.setPayloadUpdateDate(time);
+ int newVersion = new Integer(oldVesrion).intValue();
+ newVersion++;
+ artifactData.setArtifactVersion(String.valueOf(newVersion));
+ }
+
+ @Override
+ public Either<ArtifactDefinition, StorageOperationStatus> getHeatArtifactByHeatEnvId(String heatEnvId, boolean inTransaction) {
+
+ Either<ArtifactDefinition, StorageOperationStatus> result = null;
+ try {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Failed to work with graph {}", graph.right().value());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ }
+ TitanGraph tGraph = graph.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = tGraph.query().has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), heatEnvId).vertices();
+ if (vertices == null || vertices.iterator() == null || false == vertices.iterator().hasNext()) {
+ log.debug("No nodes for type {} for id = {}", NodeTypeEnum.ArtifactRef, heatEnvId);
+ result = Either.right(StorageOperationStatus.INVALID_ID);
+ return result;
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> edgeProperties = null;
+ Either<List<ImmutablePair<ArtifactData, GraphEdge>>, TitanOperationStatus> childrenByEdgeCriteria = titanGenericDao.getChildrenByEdgeCriteria(vertex, heatEnvId, GraphEdgeLabels.GENERATED_FROM, NodeTypeEnum.ArtifactRef, ArtifactData.class,
+ edgeProperties);
+
+ if (childrenByEdgeCriteria.isRight()) {
+ TitanOperationStatus status = childrenByEdgeCriteria.right().value();
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ List<ImmutablePair<ArtifactData, GraphEdge>> list = childrenByEdgeCriteria.left().value();
+
+ if (list == null || list.isEmpty() == true) {
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ if (list.size() > 1) {
+ return Either.right(StorageOperationStatus.INVALID_ID);
+ }
+
+ ImmutablePair<ArtifactData, GraphEdge> immutablePair = list.get(0);
+
+ ArtifactDefinition artifactDefinition = new ArtifactDefinition(immutablePair.left.getArtifactDataDefinition());
+
+ log.debug("The artifact {} was generated from artifact {}", heatEnvId, artifactDefinition);
+
+ result = Either.left(artifactDefinition);
+ return result;
+
+ } finally {
+ if (inTransaction == false) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public Either<ArtifactData, StorageOperationStatus> getLatestArtifactDataByArtifactUUID(String artifactUUID, boolean inTransaction) {
+ Either<ArtifactData, StorageOperationStatus> result = null;
+ try {
+ NodeTypeEnum nodeType = NodeTypeEnum.ArtifactRef;
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.ARTIFACT_UUID.getProperty(), artifactUUID);
+ Either<List<ArtifactData>, TitanOperationStatus> getArtifactEither = titanGenericDao.getByCriteria(nodeType, propertiesToMatch, ArtifactData.class);
+ if (getArtifactEither.isRight()) {
+ log.debug("Couldn't fetch artifact data for artifact with uuid {}, error: {}", nodeType, getArtifactEither.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getArtifactEither.right().value()));
+ } else {
+ List<ArtifactData> artifacts = getArtifactEither.left().value();
+ ArtifactData latestArtifact = artifacts.size() == 1 ? artifacts.get(0)
+ : artifacts.stream().max((a1, a2) -> Double.compare(Double.parseDouble(a1.getArtifactDataDefinition().getArtifactVersion()), Double.parseDouble(a2.getArtifactDataDefinition().getArtifactVersion()))).get();
+ result = Either.left(latestArtifact);
+ }
+ return result;
+ } finally {
+ if (!inTransaction) {
+ if (result == null || result.isRight()) {
+ this.titanGenericDao.rollback();
+ } else {
+ this.titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AttributeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AttributeOperation.java
new file mode 100644
index 0000000000..fc81a9affd
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/AttributeOperation.java
@@ -0,0 +1,463 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.operations.api.IAttributeOperation;
+import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.be.resources.data.AttributeValueData;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+/**
+ * Class For Data Model Logic Relevant For Attributes
+ *
+ * @author mshitrit
+ *
+ */
+@Component("attribute-operation")
+public class AttributeOperation extends AbstractOperation implements IAttributeOperation {
+ private static Logger log = LoggerFactory.getLogger(AttributeOperation.class.getName());
+ @Autowired
+ private IPropertyOperation propertyOperation;
+
+ /**
+ *
+ * Add attribute to graph.
+ *
+ * 1. Add attribute node
+ *
+ * 2. Add edge between the former node to its parent(if exists)
+ *
+ * 3. Add property node and associate it to the node created at #1. (per property & if exists)
+ *
+ * @param attributeDefinition
+ * @return
+ */
+ private Either<AttributeData, TitanOperationStatus> addAttributeToNodeType(AttributeDefinition attributeDefinition, NodeTypeEnum nodeType, String nodeUniqueId) {
+ String attUniqueId = UniqueIdBuilder.buildAttributeUid(nodeUniqueId, attributeDefinition.getName());
+ Supplier<AttributeData> dataBuilder = () -> buildAttributeData(attributeDefinition, attUniqueId);
+ Supplier<String> defNameGenerator = () -> "Attribute : " + attributeDefinition.getName();
+
+ return addDefinitionToNodeType(attributeDefinition, nodeType, nodeUniqueId, GraphEdgeLabels.ATTRIBUTE, dataBuilder, defNameGenerator);
+
+ }
+
+ private TitanOperationStatus addAttributeToNodeType(TitanVertex metadataVertex, AttributeDefinition attributeDefinition, NodeTypeEnum nodeType, String nodeUniqueId) {
+ String attUniqueId = UniqueIdBuilder.buildAttributeUid(nodeUniqueId, attributeDefinition.getName());
+ Supplier<AttributeData> dataBuilder = () -> buildAttributeData(attributeDefinition, attUniqueId);
+ Supplier<String> defNameGenerator = () -> "Attribute : " + attributeDefinition.getName();
+
+ return addDefinitionToNodeType(metadataVertex, attributeDefinition, nodeType, nodeUniqueId, GraphEdgeLabels.ATTRIBUTE, dataBuilder, defNameGenerator);
+
+ }
+
+ private AttributeData buildAttributeData(AttributeDefinition attributeDefinition, String attUniqueId) {
+ attributeDefinition.setUniqueId(attUniqueId);
+ return new AttributeData(attributeDefinition);
+ }
+
+ @Override
+ public Either<AttributeData, StorageOperationStatus> deleteAttribute(String attributeId) {
+ Either<AttributeData, TitanOperationStatus> either = deleteAttributeFromGraph(attributeId);
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ return Either.right(storageStatus);
+ }
+ return Either.left(either.left().value());
+ }
+
+ @Override
+ public Either<Map<String, AttributeDefinition>, StorageOperationStatus> deleteAllAttributeAssociatedToNode(NodeTypeEnum nodeType, String uniqueId) {
+ Wrapper<TitanOperationStatus> errorWrapper;
+ List<AttributeDefinition> attributes = new ArrayList<>();
+ TitanOperationStatus findAllResourceAttribues = findNodeNonInheretedAttribues(uniqueId, NodeTypeEnum.Resource, attributes);
+ errorWrapper = (findAllResourceAttribues != TitanOperationStatus.OK) ? new Wrapper<>(findAllResourceAttribues) : new Wrapper<>();
+
+ if (errorWrapper.isEmpty()) {
+ for (AttributeDefinition attDef : attributes) {
+ log.debug("Before deleting attribute from graph {}", attDef.getUniqueId());
+ Either<AttributeData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Attribute), attDef.getUniqueId(), AttributeData.class);
+ if (deleteNode.isRight()) {
+ errorWrapper.setInnerElement(deleteNode.right().value());
+ break;
+ }
+ }
+ }
+
+ if (errorWrapper.isEmpty()) {
+ Map<String, AttributeDefinition> attributesMap = attributes.stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
+ return Either.left(attributesMap);
+ } else {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(errorWrapper.getInnerElement()));
+ }
+
+ }
+
+ private Either<AttributeData, TitanOperationStatus> deleteAttributeFromGraph(String attributeId) {
+ log.debug("Before deleting attribute from graph {}", attributeId);
+ return titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Attribute), attributeId, AttributeData.class);
+ }
+
+ @Override
+ public TitanOperationStatus addAttributesToGraph(TitanVertex metadataVertex, Map<String, AttributeDefinition> attributes, String resourceId, Map<String, DataTypeDefinition> dataTypes) {
+ TitanOperationStatus titanStatus = TitanOperationStatus.OK;
+ for (AttributeDefinition attribute : attributes.values()) {
+ TitanOperationStatus eitherAddAttribute = addAttributeToGraphByVertex(metadataVertex, attribute, resourceId, dataTypes);
+ if (!eitherAddAttribute.equals(TitanOperationStatus.OK)) {
+ titanStatus = eitherAddAttribute;
+ break;
+ }
+ }
+ return titanStatus;
+ }
+
+ @Override
+ public Either<List<ComponentInstanceAttribute>, TitanOperationStatus> getAllAttributesOfResourceInstance(ComponentInstance compInstance) {
+
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> result;
+
+ Either<List<ImmutablePair<AttributeValueData, GraphEdge>>, TitanOperationStatus> attributeImplNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), compInstance.getUniqueId(),
+ GraphEdgeLabels.ATTRIBUTE_VALUE, NodeTypeEnum.AttributeValue, AttributeValueData.class);
+
+ // Build From Resource
+ if (attributeImplNodes.isRight() && attributeImplNodes.right().value() == TitanOperationStatus.NOT_FOUND) {
+ result = getAttributesFromResource(compInstance);
+ }
+ // Build From Instance
+ else if (attributeImplNodes.isLeft()) {
+ List<ImmutablePair<AttributeValueData, GraphEdge>> attributesFromRI = attributeImplNodes.left().value();
+ result = mergeAttributesResults(getAttributesFromResource(compInstance), convertToComponentInstanceAttribute(attributesFromRI));
+ }
+ // Error
+ else {
+ TitanOperationStatus status = attributeImplNodes.right().value();
+ result = Either.right(status);
+ }
+
+ return result;
+ }
+
+ private Either<List<ComponentInstanceAttribute>, TitanOperationStatus> mergeAttributesResults(Either<List<ComponentInstanceAttribute>, TitanOperationStatus> eitherAttributesThatDoesNotExistOnRI,
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> eitherAttributesThatExistOnRI) {
+
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> result;
+ if (eitherAttributesThatExistOnRI.isRight()) {
+ result = Either.right(eitherAttributesThatExistOnRI.right().value());
+ } else if (eitherAttributesThatDoesNotExistOnRI.isRight()) {
+ result = Either.right(eitherAttributesThatDoesNotExistOnRI.right().value());
+ } else {
+ final List<ComponentInstanceAttribute> attributesThatExistOnRI = eitherAttributesThatExistOnRI.left().value();
+ final List<ComponentInstanceAttribute> attributesThatDoesNotExistOnRI = eitherAttributesThatDoesNotExistOnRI.left().value();
+ Set<String> attributesIdThatExistOnRI = attributesThatExistOnRI.stream().map(e -> e.getUniqueId()).collect(Collectors.toSet());
+ // Attributes From The Resource Without attributes that also exist
+ // on the instance
+ Stream<ComponentInstanceAttribute> filterAttributesThatDoesNotExistOnRI = attributesThatDoesNotExistOnRI.stream().filter(e -> !attributesIdThatExistOnRI.contains(e.getUniqueId()));
+ // Add Fields From Resource Attributes
+ fillAttributeInfoFromResource(attributesThatExistOnRI, attributesThatDoesNotExistOnRI);
+ // Adding the Attributes on the instance for the full list
+ List<ComponentInstanceAttribute> mergedList = Stream.concat(filterAttributesThatDoesNotExistOnRI, attributesThatExistOnRI.stream()).collect(Collectors.toList());
+ result = Either.left(mergedList);
+ }
+ return result;
+ }
+
+ private void fillAttributeInfoFromResource(List<ComponentInstanceAttribute> attributesThatExistOnRI, List<ComponentInstanceAttribute> attributesThatDoesNotExistOnRI) {
+ attributesThatExistOnRI.stream()
+ .forEach(e -> addAttributeInfo(e,
+ // Finds the same attribute in the resource
+ attributesThatDoesNotExistOnRI.stream().filter(e2 -> e2.getUniqueId().equals(e.getUniqueId())).findAny().get()));
+
+ }
+
+ private void addAttributeInfo(ComponentInstanceAttribute attributeFromRI, ComponentInstanceAttribute attributeFromResource) {
+ attributeFromRI.setName(attributeFromResource.getName());
+ attributeFromRI.setDescription(attributeFromResource.getDescription());
+ attributeFromRI.setDefaultValue(attributeFromResource.getDefaultValue());
+ attributeFromRI.setStatus(attributeFromResource.getStatus());
+ attributeFromRI.setSchema(attributeFromResource.getSchema());
+ if (StringUtils.isEmpty(attributeFromRI.getValue())) {
+ attributeFromRI.setValue(attributeFromResource.getDefaultValue());
+ }
+ }
+
+ private Either<List<ComponentInstanceAttribute>, TitanOperationStatus> getAttributesFromResource(ComponentInstance compInstance) {
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> result;
+ List<AttributeDefinition> attributes = new ArrayList<>();
+ // Attributes does not exist on Ri - fetch them from resource
+ TitanOperationStatus findAllResourceAttribues = findAllResourceAttributesRecursively(compInstance.getComponentUid(), attributes);
+ if (findAllResourceAttribues != TitanOperationStatus.OK) {
+ result = Either.right(findAllResourceAttribues);
+ } else {
+ List<ComponentInstanceAttribute> buildAttInstanceFromResource = attributes.stream().map(attDef -> new ComponentInstanceAttribute(attDef, false, null)).collect(Collectors.toList());
+
+ // Set Value to be default value in case it is empty
+ Consumer<ComponentInstanceAttribute> valueSetter = data -> {
+ if (StringUtils.isEmpty(data.getValue())) {
+ data.setValue(data.getDefaultValue());
+ }
+ };
+ buildAttInstanceFromResource.stream().forEach(valueSetter);
+
+ result = Either.left(buildAttInstanceFromResource);
+ }
+ return result;
+ }
+
+ private Either<List<ComponentInstanceAttribute>, TitanOperationStatus> convertToComponentInstanceAttribute(List<ImmutablePair<AttributeValueData, GraphEdge>> list) {
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> result = null;
+ List<ComponentInstanceAttribute> componentInstanceAttribute = new ArrayList<>();
+ for (ImmutablePair<AttributeValueData, GraphEdge> attributeValue : list) {
+ AttributeValueData attributeValueData = attributeValue.getLeft();
+ String attributeValueUid = attributeValueData.getUniqueId();
+
+ Either<ImmutablePair<AttributeData, GraphEdge>, TitanOperationStatus> attributeDefRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.AttributeValue), attributeValueUid, GraphEdgeLabels.ATTRIBUTE_IMPL,
+ NodeTypeEnum.Attribute, AttributeData.class);
+
+ if (attributeDefRes.isRight()) {
+ TitanOperationStatus status = attributeDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ result = Either.right(status);
+ break;
+ } else {
+ ImmutablePair<AttributeData, GraphEdge> attributeDefPair = attributeDefRes.left().value();
+ String attributeUniqueId = attributeDefPair.left.getUniqueId();
+
+ ComponentInstanceAttribute resourceInstanceAttribute = new ComponentInstanceAttribute();
+ // set attribute original unique id
+ resourceInstanceAttribute.setUniqueId(attributeUniqueId);
+ // set hidden
+ resourceInstanceAttribute.setHidden(attributeValueData.isHidden());
+ // set value
+ resourceInstanceAttribute.setValue(attributeValueData.getValue());
+ // set property value unique id
+ resourceInstanceAttribute.setValueUniqueUid(attributeValueUid);
+
+ resourceInstanceAttribute.setType(attributeValueData.getType());
+
+ componentInstanceAttribute.add(resourceInstanceAttribute);
+ }
+
+ }
+ if (result == null) {
+ result = Either.left(componentInstanceAttribute);
+ }
+ return result;
+ }
+
+ /**
+ * fetch all attributes under a given resource(includes its parents' resources)
+ *
+ * @param resourceId
+ * @param attributes
+ * @return
+ */
+ @Override
+ public TitanOperationStatus findAllResourceAttributesRecursively(String resourceId, List<AttributeDefinition> attributes) {
+ final NodeElementFetcher<AttributeDefinition> singleNodeFetcher = (resourceIdParam, attributesParam) -> findNodeNonInheretedAttribues(resourceIdParam, NodeTypeEnum.Resource, attributesParam);
+ return findAllResourceElementsDefinitionRecursively(resourceId, attributes, singleNodeFetcher);
+
+ }
+
+ @Override
+ public TitanOperationStatus findNodeNonInheretedAttribues(String uniqueId, NodeTypeEnum nodeType, List<AttributeDefinition> attributes) {
+ Either<List<ImmutablePair<AttributeData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.ATTRIBUTE, NodeTypeEnum.Attribute,
+ AttributeData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ return status;
+ }
+
+ List<ImmutablePair<AttributeData, GraphEdge>> values = childrenNodes.left().value();
+ if (values != null) {
+
+ for (ImmutablePair<AttributeData, GraphEdge> immutablePair : values) {
+ AttributeData attData = immutablePair.getLeft();
+ String attributeName = attData.getAttributeDataDefinition().getName();
+
+ log.debug("Attribute {} is associated to node {}", attributeName, uniqueId);
+ AttributeData attributeData = immutablePair.getKey();
+ AttributeDefinition attributeDefinition = this.convertAttributeDataToAttributeDefinition(attributeData, attributeName, uniqueId);
+
+ attributes.add(attributeDefinition);
+
+ log.trace("findAttributesOfNode - property {} associated to node {}", attributeDefinition, uniqueId);
+ }
+
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ @Override
+ public AttributeDefinition convertAttributeDataToAttributeDefinition(AttributeData attributeData, String attributeName, String resourceId) {
+ log.debug("The object returned after create attribute is {}", attributeData);
+ AttributeDefinition attributeDefResult = new AttributeDefinition(attributeData.getAttributeDataDefinition());
+ attributeDefResult.setName(attributeName);
+ attributeDefResult.setParentUniqueId(resourceId);
+ return attributeDefResult;
+ }
+
+ @Override
+ public Either<AttributeData, StorageOperationStatus> addAttribute(AttributeDefinition attributeDefinition, String resourceId) {
+
+ Either<AttributeData, StorageOperationStatus> eitherResult;
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Cannot find any data type. Status is {}.", status);
+ eitherResult = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ Either<AttributeData, TitanOperationStatus> either = addAttributeToGraph(attributeDefinition, resourceId, allDataTypes.left().value());
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ eitherResult = Either.right(storageStatus);
+ } else {
+ eitherResult = Either.left(either.left().value());
+ }
+ }
+ return eitherResult;
+ }
+
+ @Override
+ public Either<AttributeData, StorageOperationStatus> updateAttribute(String attributeId, AttributeDefinition newAttDef, Map<String, DataTypeDefinition> dataTypes) {
+
+ StorageOperationStatus validateAndUpdateAttribute = propertyOperation.validateAndUpdateProperty(newAttDef, dataTypes);
+ if (validateAndUpdateAttribute != StorageOperationStatus.OK) {
+ return Either.right(validateAndUpdateAttribute);
+ }
+
+ Either<AttributeData, TitanOperationStatus> either = updateAttributeFromGraph(attributeId, newAttDef);
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ return Either.right(storageStatus);
+ }
+ return Either.left(either.left().value());
+ }
+
+ private Either<AttributeData, TitanOperationStatus> updateAttributeFromGraph(String attributeId, AttributeDefinition attributeDefenition) {
+ log.debug("Before updating attribute on graph {}", attributeId);
+
+ // get the original property data
+ Either<AttributeData, TitanOperationStatus> eitherAttribute = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Attribute), attributeId, AttributeData.class);
+ if (eitherAttribute.isRight()) {
+ log.debug("Problem while get Attribute with id {}. Reason - {}", attributeId, eitherAttribute.right().value().name());
+ return Either.right(eitherAttribute.right().value());
+ }
+ AttributeData orgAttributeData = eitherAttribute.left().value();
+ AttributeDataDefinition orgAttributeDataDefinition = orgAttributeData.getAttributeDataDefinition();
+
+ // create new property data to update
+ AttributeData newAttributeData = new AttributeData();
+ newAttributeData.setAttributeDataDefinition(attributeDefenition);
+ AttributeDataDefinition newAttributeDataDefinition = newAttributeData.getAttributeDataDefinition();
+
+ // update the original property data with new values
+ if (!Objects.equals(orgAttributeDataDefinition.getDefaultValue(), newAttributeDataDefinition.getDefaultValue())) {
+ orgAttributeDataDefinition.setDefaultValue(newAttributeDataDefinition.getDefaultValue());
+ }
+
+ if (!Objects.equals(orgAttributeDataDefinition.getDescription(), newAttributeDataDefinition.getDescription())) {
+ orgAttributeDataDefinition.setDescription(newAttributeDataDefinition.getDescription());
+ }
+
+ if (!Objects.equals(orgAttributeDataDefinition.getType(), newAttributeDataDefinition.getType())) {
+ orgAttributeDataDefinition.setType(newAttributeDataDefinition.getType());
+ }
+
+ orgAttributeDataDefinition.setSchema(newAttributeDataDefinition.getSchema());
+
+ return titanGenericDao.updateNode(orgAttributeData, AttributeData.class);
+ }
+
+ @Override
+ public ComponentInstanceAttribute buildResourceInstanceAttribute(AttributeValueData attributeValueData, ComponentInstanceAttribute resourceInstanceAttribute) {
+
+ Boolean hidden = attributeValueData.isHidden();
+ String uid = attributeValueData.getUniqueId();
+ ComponentInstanceAttribute instanceAttribute = new ComponentInstanceAttribute(resourceInstanceAttribute, hidden, uid);
+
+ return instanceAttribute;
+ }
+
+ @Override
+ public Either<AttributeData, TitanOperationStatus> addAttributeToGraph(AttributeDefinition attribute, String resourceId, Map<String, DataTypeDefinition> dataTypes) {
+ Either<AttributeData, TitanOperationStatus> eitherResult;
+ StorageOperationStatus validateAndUpdateAttribute = propertyOperation.validateAndUpdateProperty(attribute, dataTypes);
+ if (validateAndUpdateAttribute != StorageOperationStatus.OK) {
+ log.error("Attribute " + attribute + " is invalid. Status is " + validateAndUpdateAttribute);
+ eitherResult = Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ } else {
+ eitherResult = addAttributeToNodeType(attribute, NodeTypeEnum.Resource, resourceId);
+
+ }
+ return eitherResult;
+ }
+
+ @Override
+ public TitanOperationStatus addAttributeToGraphByVertex(TitanVertex metadataVertex, AttributeDefinition attribute, String resourceId, Map<String, DataTypeDefinition> dataTypes) {
+ StorageOperationStatus validateAndUpdateAttribute = propertyOperation.validateAndUpdateProperty(attribute, dataTypes);
+ TitanOperationStatus result;
+ if (validateAndUpdateAttribute != StorageOperationStatus.OK) {
+ log.error("Attribute {} is invalid. Status is {}", attribute, validateAndUpdateAttribute);
+ result = TitanOperationStatus.ILLEGAL_ARGUMENT;
+ } else {
+ result = addAttributeToNodeType(metadataVertex, attribute, NodeTypeEnum.Resource, resourceId);
+
+ }
+ return result;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CacheMangerOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CacheMangerOperation.java
new file mode 100644
index 0000000000..d05255473d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CacheMangerOperation.java
@@ -0,0 +1,213 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.cache.*;
+import org.openecomp.sdc.be.model.cache.jobs.*;
+import org.openecomp.sdc.be.model.cache.workers.CacheWorker;
+import org.openecomp.sdc.be.model.cache.workers.IWorker;
+import org.openecomp.sdc.be.model.cache.workers.SyncWorker;
+import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.api.IProductOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+import org.openecomp.sdc.be.workers.Manager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.LinkedList;
+import java.util.concurrent.*;
+
+/**
+ * Created by mlando on 9/5/2016. the class is responsible for handling all
+ * cache update operations asynchronously including sync between the graph and
+ * cache and on demand update requests
+ */
+@Component("cacheManger-operation")
+public class CacheMangerOperation implements ICacheMangerOperation {
+ @Autowired
+ private IResourceOperation iResourceOperation;
+ @Autowired
+ private IServiceOperation iServiceOperation;
+ @Autowired
+ private IProductOperation iProductOperation;
+ @Autowired
+ private TitanGenericDao titanGenericDao;
+ @Autowired
+ private ComponentCache componentCache;
+
+ private static Logger log = LoggerFactory.getLogger(Manager.class.getName());
+ private LinkedBlockingQueue<Job> jobQueue = null;
+ private int waitOnShutDownInMinutes;
+ private ScheduledExecutorService syncExecutor;
+ private ExecutorService workerExecutor;
+ private LinkedList<IWorker> workerList = new LinkedList<>();
+ private DaoInfo daoInfo;
+
+ /**
+ * constructor
+ */
+ public CacheMangerOperation() {
+ // daoInfo = new DaoInfo(iResourceOperation, iServiceOperation,
+ // iProductOperation, componentCache);
+ }
+
+ /**
+ * the method checks in the cache is enabled, if it is, it initializes all
+ * the workers according to the configuration values.
+ */
+ @PostConstruct
+ public void init() {
+
+ daoInfo = new DaoInfo(iResourceOperation, iServiceOperation, iProductOperation, componentCache);
+
+ Configuration.ApplicationL2CacheConfig applicationL2CacheConfig = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getApplicationL2Cache();
+ if (applicationL2CacheConfig != null && applicationL2CacheConfig.isEnabled()) {
+ Integer numberOfWorkers = applicationL2CacheConfig.getQueue().getNumberOfCacheWorkers();
+ this.waitOnShutDownInMinutes = applicationL2CacheConfig.getQueue().getWaitOnShutDownInMinutes();
+ jobQueue = new LinkedBlockingQueue<>();
+ log.info("L2 Cache is enabled inishilsing queue");
+ log.debug("initializing SyncWorker, creating {} workers");
+ ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Sync-Cache-Worker-%d").build();
+ this.syncExecutor = Executors.newSingleThreadScheduledExecutor(threadFactory);
+ log.debug("initializing workers, creating {} cacheWorkers", numberOfWorkers);
+ threadFactory = new ThreadFactoryBuilder().setNameFormat("Cache-Worker-%d").build();
+ String workerName = "Sync-Worker";
+ Integer syncWorkerExacutionIntrval = applicationL2CacheConfig.getQueue().getSyncIntervalInSecondes();
+ log.debug("starting Sync worker:{} with executions interval:{} ", workerName, syncWorkerExacutionIntrval);
+ SyncWorker syncWorker = new SyncWorker(workerName, this);
+ this.syncExecutor.scheduleAtFixedRate(syncWorker, 5 * 60, syncWorkerExacutionIntrval, TimeUnit.SECONDS);
+ this.workerExecutor = Executors.newFixedThreadPool(numberOfWorkers, threadFactory);
+ CacheWorker cacheWorker;
+ for (int i = 0; i < numberOfWorkers; i++) {
+ workerName = "Cache-Worker-" + i;
+ log.debug("starting Cache worker:{}", workerName);
+ cacheWorker = new CacheWorker(workerName, jobQueue);
+ this.workerExecutor.submit(cacheWorker);
+ this.workerList.add(cacheWorker);
+ }
+ } else {
+ log.info("L2 Cache is disabled");
+ }
+ log.info("L2 Cache has been initialized and the workers are running");
+ }
+
+ /**
+ * the method creates a job to check it the given component is in the cach
+ * and if so is it valid if the value in the cache is not valid it will be
+ * updated.
+ *
+ * @param componentId
+ * the uid of the component we want to update
+ * @param timestamp
+ * the time of the component update
+ * @param nodeTypeEnum
+ * the type of the component resource/service/product
+ */
+ @Override
+ public void updateComponentInCache(String componentId, long timestamp, NodeTypeEnum nodeTypeEnum) {
+ Configuration.ApplicationL2CacheConfig applicationL2CacheConfig = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getApplicationL2Cache();
+ if (applicationL2CacheConfig != null && applicationL2CacheConfig.isEnabled()) {
+ this.jobQueue.add(new CheckAndUpdateJob(daoInfo, componentId, nodeTypeEnum, timestamp));
+ }
+ }
+
+ public void overideComponentInCache(String componentId, long timestamp, NodeTypeEnum nodeTypeEnum) {
+ Configuration.ApplicationL2CacheConfig applicationL2CacheConfig = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getApplicationL2Cache();
+ if (applicationL2CacheConfig != null && applicationL2CacheConfig.isEnabled()) {
+ this.jobQueue.add(new OverrideJob(daoInfo, componentId, nodeTypeEnum, timestamp));
+ }
+ }
+
+ public void deleteComponentInCache(String componentId, long timestamp, NodeTypeEnum nodeTypeEnum) {
+ Configuration.ApplicationL2CacheConfig applicationL2CacheConfig = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getApplicationL2Cache();
+ if (applicationL2CacheConfig != null && applicationL2CacheConfig.isEnabled()) {
+ this.jobQueue.add(new DeleteJob(daoInfo, componentId, nodeTypeEnum, timestamp));
+ }
+ }
+
+ /**
+ * the method stores the given component in the cache
+ *
+ * @param component
+ * componet to store in cache
+ * @param nodeTypeEnum
+ * the type of the component we want to store
+ */
+ @Override
+ public void storeComponentInCache(org.openecomp.sdc.be.model.Component component, NodeTypeEnum nodeTypeEnum) {
+ Configuration.ApplicationL2CacheConfig applicationL2CacheConfig = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getApplicationL2Cache();
+ if (applicationL2CacheConfig != null && applicationL2CacheConfig.isEnabled()) {
+ this.jobQueue.add(new StoreJob(daoInfo, component, nodeTypeEnum));
+ }
+ }
+
+ /**
+ * the method shutdown's all the worker's. the method has a pre set of how
+ * long it will wait for the workers to shutdown. the pre defined value is
+ * taken from the configuration.
+ */
+ @PreDestroy
+ public void shutDown() {
+ workerExecutor.shutdown();
+ syncExecutor.shutdown();
+ this.workerList.forEach(e -> e.shutDown());
+ try {
+ if (!workerExecutor.awaitTermination(this.waitOnShutDownInMinutes, TimeUnit.MINUTES)) {
+ log.error("timer elapsed while waiting for Cache workers to finish, forcing a shutdown. ");
+ }
+ log.debug("all Cache workers finished");
+ } catch (InterruptedException e) {
+ log.error("failed while waiting for Cache worker", e);
+ }
+ try {
+ if (!workerExecutor.awaitTermination(1, TimeUnit.MINUTES)) {
+ log.error("timer elapsed while waiting for the Sync worker's to finish, forcing a shutdown. ");
+ }
+ log.debug("sync worker finished");
+ } catch (InterruptedException e) {
+ log.error("failed while waiting for sync worker", e);
+ }
+ }
+
+ public TitanGenericDao getTitanGenericDao() {
+ return titanGenericDao;
+ }
+
+ public ComponentCache getComponentCache() {
+ return componentCache;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityInstanceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityInstanceOperation.java
new file mode 100644
index 0000000000..0c4f35fd54
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityInstanceOperation.java
@@ -0,0 +1,1215 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityInstanceOperation;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+/**
+ * public class CapabilityInstanceOperation provides methods for CRUD operations for CapabilityInstance on component instance level
+ *
+ * @author ns019t
+ *
+ */
+@Component("capability-instance-operation")
+public class CapabilityInstanceOperation extends AbstractOperation implements ICapabilityInstanceOperation {
+
+ private static Logger log = LoggerFactory.getLogger(CapabilityOperation.class.getName());
+
+ @Autowired
+ private PropertyOperation propertyOperation;
+
+ @Autowired
+ private CapabilityOperation capabilityOperation;
+
+ /**
+ * String constants for logger
+ */
+ private String statusIs = ". status is ";
+ private String dot = ".";
+ private String onGraph = " on graph ";
+ private String ofRI = " of resource instance ";
+ private String toCapability = " to capability ";
+ private String toCI = " to capability instance ";
+ private String toProperty = " to property ";
+ private String forRI = " for resource instance ";
+ private String failedCreateCI = "Failed to create capability instance of capability ";
+ private String failedAddProperties = "Failed to add properties to capability instance ";
+ private String ofCI = " of component instance ";
+ private String failedDeletePropertyValues = "Failed to delete property values of capability instance ";
+ private String toValue = " to property value ";
+ private String fromRI = " from resource instance ";
+
+ /**
+ * create capability instance of capability with property values for resource instance
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @param propertyValues
+ * @param validateCapabilityInstExistence
+ * @param capabilityName
+ * @return
+ */
+ @Override
+ public Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> createCapabilityInstanceOfCapabilityWithPropertyValuesForResourceInstance(String resourceInstanceId, String capabilityId, String capabilityName,
+ List<ComponentInstanceProperty> propertyValues, boolean validateCapabilityInstExistence) {
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ Wrapper<CapabilityData> overrideCapabilityDataWrapper = new Wrapper<>();
+ Wrapper<CapabilityDefinition> overrideCapabilityDefinitionWrapper = new Wrapper<>();
+ Either<CapabilityInstData, TitanOperationStatus> createCapabilityRes = null;
+ CapabilityInstData createdCapabilityInstance = null;
+
+ Wrapper<Map<String, PropertyDefinition>> defaultPropertiesWrapper = new Wrapper<>();
+ Either<ImmutablePair<CapabilityData, GraphEdge>, TitanOperationStatus> getCapabilityRes = null;
+ Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes = null;
+ Either<List<PropertyValueData>, TitanOperationStatus> addPropertyValuesRes = null;
+ Wrapper<String> createdCapabilityInstanceIdWrapper = new Wrapper<>();
+ if (validateCapabilityInstExistence) {
+ validateCapabilityInstanceExistence(resourceInstanceId, capabilityId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ getCapabilityRes = getCapabilitiesOfResourceInstance(resourceInstanceId, capabilityId, capabilityName, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ getCapabilityDefinitionRes = getCapabiityDefinition(resourceInstanceId, capabilityId, errorWrapper, overrideCapabilityDataWrapper, getCapabilityRes);
+ }
+ if (errorWrapper.isEmpty()) {
+ createCapabilityRes = createCapabilityInstanceOnGraph(resourceInstanceId, capabilityId, errorWrapper, overrideCapabilityDataWrapper, overrideCapabilityDefinitionWrapper, getCapabilityDefinitionRes);
+ }
+ if (errorWrapper.isEmpty() && overrideCapabilityDefinitionWrapper.getInnerElement().getProperties() != null) {
+ createdCapabilityInstance = validateCapabilityInstanceProperties(resourceInstanceId, propertyValues, errorWrapper, overrideCapabilityDefinitionWrapper, createCapabilityRes, defaultPropertiesWrapper, createdCapabilityInstanceIdWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ addPropertyValuesRes = addPropertyValueToCapabilityInstance(resourceInstanceId, propertyValues, errorWrapper, createCapabilityRes, defaultPropertiesWrapper, createdCapabilityInstanceIdWrapper);
+ }
+ Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> result;
+ if (errorWrapper.isEmpty()) {
+ Map<CapabilityInstData, List<PropertyValueData>> resultMap = new HashMap<>();
+ resultMap.put(createdCapabilityInstance, addPropertyValuesRes.left().value());
+ result = Either.left(resultMap);
+ } else {
+ result = Either.right(errorWrapper.getInnerElement());
+ }
+ return result;
+ }
+
+ @Override
+ public TitanOperationStatus createCapabilityInstanceOfCapabilityWithPropertyValuesForResourceInstance(TitanVertex resourceInstanceVertex, String resourceInstanceId, String capabilityId, String capabilityName,
+ List<ComponentInstanceProperty> propertyValues, boolean validateCapabilityInstExistence) {
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ Wrapper<TitanVertex> overrideCapabilityDataWrapper = new Wrapper<>();
+ Wrapper<CapabilityDefinition> overrideCapabilityDefinitionWrapper = new Wrapper<>();
+ Either<TitanVertex, TitanOperationStatus> createCapabilityRes = null;
+ TitanVertex createdCapabilityInstance = null;
+
+ Wrapper<Map<String, PropertyDefinition>> defaultPropertiesWrapper = new Wrapper<>();
+ Either<ImmutablePair<TitanVertex, Edge>, TitanOperationStatus> getCapabilityRes = null;
+ Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes = null;
+ TitanOperationStatus addPropertyValuesRes = null;
+ Wrapper<String> createdCapabilityInstanceIdWrapper = new Wrapper<>();
+ if (validateCapabilityInstExistence) {
+ validateCapabilityInstanceExistence(resourceInstanceVertex, resourceInstanceId, capabilityId, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ getCapabilityRes = getCapabilitiesOfResourceInstance(resourceInstanceVertex, resourceInstanceId, capabilityId, capabilityName, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ getCapabilityDefinitionRes = getCapabiityDefinitionByVertex(resourceInstanceId, capabilityId, errorWrapper, overrideCapabilityDataWrapper, getCapabilityRes);
+ }
+ if (errorWrapper.isEmpty()) {
+ createCapabilityRes = createCapabilityInstanceOnGraphByVertex(resourceInstanceVertex, resourceInstanceId, capabilityId, errorWrapper, overrideCapabilityDataWrapper, overrideCapabilityDefinitionWrapper, getCapabilityDefinitionRes);
+ }
+ if (errorWrapper.isEmpty() && overrideCapabilityDefinitionWrapper.getInnerElement().getProperties() != null) {
+ createdCapabilityInstance = validateCapabilityInstancePropertiesByVertex(resourceInstanceId, propertyValues, errorWrapper, overrideCapabilityDefinitionWrapper, createCapabilityRes.left().value(), defaultPropertiesWrapper,
+ createdCapabilityInstanceIdWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ addPropertyValuesRes = addPropertyValueToCapabilityInstanceByVertex(resourceInstanceId, propertyValues, errorWrapper, createCapabilityRes, defaultPropertiesWrapper, createdCapabilityInstanceIdWrapper);
+ }
+
+ return addPropertyValuesRes;
+ }
+
+ private Either<List<PropertyValueData>, TitanOperationStatus> addPropertyValueToCapabilityInstance(String resourceInstanceId, List<ComponentInstanceProperty> propertyValues, Wrapper<TitanOperationStatus> errorWrapper,
+ Either<CapabilityInstData, TitanOperationStatus> createCapabilityRes, Wrapper<Map<String, PropertyDefinition>> defaultPropertiesWrapper, Wrapper<String> createdCapabilityInstanceIdWrapper) {
+ Either<List<PropertyValueData>, TitanOperationStatus> addPropertyValuesRes;
+ log.debug("Before adding property values to capability instance {} dot", createdCapabilityInstanceIdWrapper.getInnerElement());
+ addPropertyValuesRes = addPropertyValuesToCapabilityInstance(createCapabilityRes.left().value(), propertyValues, defaultPropertiesWrapper.getInnerElement());
+ if (addPropertyValuesRes.isRight()) {
+ errorWrapper.setInnerElement(addPropertyValuesRes.right().value());
+ log.debug("failedAddProperties {} ofRI {} statusIs {} dot", createdCapabilityInstanceIdWrapper.getInnerElement(), resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ log.debug("After adding property values to capability instance {} status is {}.", createdCapabilityInstanceIdWrapper.getInnerElement(), errorWrapper.getInnerElement());
+ return addPropertyValuesRes;
+ }
+
+ private TitanOperationStatus addPropertyValueToCapabilityInstanceByVertex(String resourceInstanceId, List<ComponentInstanceProperty> propertyValues, Wrapper<TitanOperationStatus> errorWrapper,
+ Either<TitanVertex, TitanOperationStatus> createCapabilityRes, Wrapper<Map<String, PropertyDefinition>> defaultPropertiesWrapper, Wrapper<String> createdCapabilityInstanceIdWrapper) {
+ log.trace("Before adding property values to capability instance {}", createdCapabilityInstanceIdWrapper.getInnerElement());
+ TitanOperationStatus addPropertyValuesRes = addPropertyValuesToCapabilityInstance(createCapabilityRes.left().value(), propertyValues, defaultPropertiesWrapper.getInnerElement());
+ if (!addPropertyValuesRes.equals(TitanOperationStatus.OK)) {
+ errorWrapper.setInnerElement(addPropertyValuesRes);
+ log.debug("Failed to add properties to capability instance {} {} {} {} {}", createdCapabilityInstanceIdWrapper.getInnerElement(), ofRI, resourceInstanceId, statusIs, errorWrapper.getInnerElement());
+ }
+ log.trace("After adding property values to capability instance {} {} {}", createdCapabilityInstanceIdWrapper.getInnerElement(), statusIs, errorWrapper.getInnerElement());
+ return addPropertyValuesRes;
+ }
+
+ private CapabilityInstData validateCapabilityInstanceProperties(String resourceInstanceId, List<ComponentInstanceProperty> propertyValues, Wrapper<TitanOperationStatus> errorWrapper,
+ Wrapper<CapabilityDefinition> overrideCapabilityDefinitionWrapper, Either<CapabilityInstData, TitanOperationStatus> createCapabilityRes, Wrapper<Map<String, PropertyDefinition>> defaultPropertiesWrapper,
+ Wrapper<String> createdCapabilityInstanceIdWrapper) {
+ CapabilityInstData createdCapabilityInstance;
+ createdCapabilityInstance = createCapabilityRes.left().value();
+ createdCapabilityInstanceIdWrapper.setInnerElement(createdCapabilityInstance.getUniqueId());
+ Map<String, PropertyDefinition> defaultProperties = overrideCapabilityDefinitionWrapper.getInnerElement().getProperties().stream().collect(Collectors.toMap(PropertyDefinition::getName, Function.identity()));
+ defaultPropertiesWrapper.setInnerElement(defaultProperties);
+ log.debug("Before validating property values of capability instance {}.", createdCapabilityInstanceIdWrapper.getInnerElement());
+ Either<Boolean, TitanOperationStatus> result = validateCapabilityInstanceProperties(defaultProperties, propertyValues);
+ if (result.isRight()) {
+ errorWrapper.setInnerElement(result.right().value());
+ log.debug("failedAddProperties {} ofRI {} statusIs {}.", createdCapabilityInstanceIdWrapper.getInnerElement(), resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ log.debug("After validating property values of capability instance {} status is {}.", createdCapabilityInstanceIdWrapper.getInnerElement(), errorWrapper.getInnerElement());
+ return createdCapabilityInstance;
+ }
+
+ private TitanVertex validateCapabilityInstancePropertiesByVertex(String resourceInstanceId, List<ComponentInstanceProperty> propertyValues, Wrapper<TitanOperationStatus> errorWrapper,
+ Wrapper<CapabilityDefinition> overrideCapabilityDefinitionWrapper, TitanVertex createCapabilityRes, Wrapper<Map<String, PropertyDefinition>> defaultPropertiesWrapper, Wrapper<String> createdCapabilityInstanceIdWrapper) {
+ String id = (String) titanGenericDao.getProperty(createCapabilityRes, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ createdCapabilityInstanceIdWrapper.setInnerElement(id);
+ Map<String, PropertyDefinition> defaultProperties = overrideCapabilityDefinitionWrapper.getInnerElement().getProperties().stream().collect(Collectors.toMap(PropertyDefinition::getName, Function.identity()));
+ defaultPropertiesWrapper.setInnerElement(defaultProperties);
+ log.trace("Before validating property values of capability instance {}", createdCapabilityInstanceIdWrapper.getInnerElement());
+ Either<Boolean, TitanOperationStatus> result = validateCapabilityInstanceProperties(defaultProperties, propertyValues);
+ if (result.isRight()) {
+ errorWrapper.setInnerElement(result.right().value());
+ log.debug("Failed to add properties to capability instance {} {} {} {} {}", createdCapabilityInstanceIdWrapper.getInnerElement(), ofRI, resourceInstanceId, statusIs, errorWrapper.getInnerElement());
+ }
+ log.trace("After validating property values of capability instance {} {} {}", createdCapabilityInstanceIdWrapper.getInnerElement(), statusIs, errorWrapper.getInnerElement());
+ return createCapabilityRes;
+ }
+
+ private Either<CapabilityInstData, TitanOperationStatus> createCapabilityInstanceOnGraph(String resourceInstanceId, String capabilityId, Wrapper<TitanOperationStatus> errorWrapper, Wrapper<CapabilityData> overrideCapabilityDataWrapper,
+ Wrapper<CapabilityDefinition> overrideCapabilityDefinitionWrapper, Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes) {
+ Either<CapabilityInstData, TitanOperationStatus> createCapabilityRes;
+ log.debug("Before creating capability instance of capability {} on graph.", capabilityId);
+ overrideCapabilityDefinitionWrapper.setInnerElement(getCapabilityDefinitionRes.left().value());
+ CapabilityInstData capabilityInstance = buildCapabilityInstanceData(resourceInstanceId, overrideCapabilityDefinitionWrapper.getInnerElement());
+ createCapabilityRes = createCapabilityInstanceOnGraph(resourceInstanceId, overrideCapabilityDataWrapper.getInnerElement(), capabilityInstance);
+ if (createCapabilityRes.isRight()) {
+ errorWrapper.setInnerElement(createCapabilityRes.right().value());
+ log.debug("failedCreateCI {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ log.debug("After creating capability instance of capability {} on graph. Status is {}", capabilityId, errorWrapper.getInnerElement());
+ return createCapabilityRes;
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> createCapabilityInstanceOnGraphByVertex(TitanVertex riVertex, String resourceInstanceId, String capabilityId, Wrapper<TitanOperationStatus> errorWrapper,
+ Wrapper<TitanVertex> overrideCapabilityDataWrapper, Wrapper<CapabilityDefinition> overrideCapabilityDefinitionWrapper, Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes) {
+ Either<TitanVertex, TitanOperationStatus> createCapabilityRes;
+ log.trace("Before creating capability instance of capability {} {}", capabilityId, onGraph);
+ overrideCapabilityDefinitionWrapper.setInnerElement(getCapabilityDefinitionRes.left().value());
+ CapabilityInstData capabilityInstance = buildCapabilityInstanceData(resourceInstanceId, overrideCapabilityDefinitionWrapper.getInnerElement());
+ createCapabilityRes = createCapabilityInstanceOnGraph(riVertex, resourceInstanceId, overrideCapabilityDataWrapper.getInnerElement(), capabilityInstance);
+ if (createCapabilityRes.isRight()) {
+ errorWrapper.setInnerElement(createCapabilityRes.right().value());
+ log.debug("Failed to create capability instance of capability {} {} {} {} {} ", capabilityId, ofRI, resourceInstanceId, statusIs, errorWrapper.getInnerElement());
+ }
+ log.debug("After creating capability instance of capability {} {} {} {} {}", capabilityId, onGraph, statusIs, errorWrapper.getInnerElement());
+ return createCapabilityRes;
+ }
+
+ private Either<CapabilityDefinition, TitanOperationStatus> getCapabiityDefinition(String resourceInstanceId, String capabilityId, Wrapper<TitanOperationStatus> errorWrapper, Wrapper<CapabilityData> overrideCapabilityDataWrapper,
+ Either<ImmutablePair<CapabilityData, GraphEdge>, TitanOperationStatus> getCapabilityRes) {
+ Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes;
+ log.debug("Before getting capability definition {} forRI {}.", capabilityId, resourceInstanceId);
+ CapabilityData overrideCapabilityData = getCapabilityRes.left().value().getLeft();
+ overrideCapabilityDataWrapper.setInnerElement(overrideCapabilityData);
+ getCapabilityDefinitionRes = capabilityOperation.getCapabilityByCapabilityData(overrideCapabilityData);
+ if (getCapabilityDefinitionRes.isRight()) {
+ errorWrapper.setInnerElement(getCapabilityDefinitionRes.right().value());
+ log.debug("Failed to retrieve capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ log.debug("After getting capability definition for {} forRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ return getCapabilityDefinitionRes;
+ }
+
+ private Either<CapabilityDefinition, TitanOperationStatus> getCapabiityDefinitionByVertex(String resourceInstanceId, String capabilityId, Wrapper<TitanOperationStatus> errorWrapper, Wrapper<TitanVertex> overrideCapabilityDataWrapper,
+ Either<ImmutablePair<TitanVertex, Edge>, TitanOperationStatus> getCapabilityRes) {
+ Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes;
+ log.trace("Before getting capability definition {} {} {}", capabilityId, forRI, resourceInstanceId);
+
+ TitanVertex overrideCapabilityData = getCapabilityRes.left().value().getLeft();
+
+ overrideCapabilityDataWrapper.setInnerElement(overrideCapabilityData);
+ getCapabilityDefinitionRes = capabilityOperation.getCapabilityByCapabilityData(overrideCapabilityData);
+ if (getCapabilityDefinitionRes.isRight()) {
+ errorWrapper.setInnerElement(getCapabilityDefinitionRes.right().value());
+ log.debug("Failed to retrieve capability {} ofRI {} statusIs {}", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ log.debug("After getting capability definition for {} forRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ return getCapabilityDefinitionRes;
+ }
+
+ private Either<ImmutablePair<CapabilityData, GraphEdge>, TitanOperationStatus> getCapabilitiesOfResourceInstance(String resourceInstanceId, String capabilityId, String capabilityName, Wrapper<TitanOperationStatus> errorWrapper) {
+ Either<ImmutablePair<CapabilityData, GraphEdge>, TitanOperationStatus> getCapabilityRes;
+ log.debug("Before getting capability {} forRI {}.", capabilityId, resourceInstanceId);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), capabilityName);
+ getCapabilityRes = titanGenericDao.getChildByEdgeCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, GraphEdgeLabels.CALCULATED_CAPABILITY, NodeTypeEnum.Capability, CapabilityData.class, props);
+ if (getCapabilityRes.isRight()) {
+ errorWrapper.setInnerElement(getCapabilityRes.right().value());
+ log.debug("Failed to get capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ log.debug("After getting capability for {} forRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ return getCapabilityRes;
+ }
+
+ private Either<ImmutablePair<TitanVertex, Edge>, TitanOperationStatus> getCapabilitiesOfResourceInstance(TitanVertex instanceVertex, String resourceInstanceId, String capabilityId, String capabilityName,
+ Wrapper<TitanOperationStatus> errorWrapper) {
+ Either<ImmutablePair<TitanVertex, Edge>, TitanOperationStatus> getCapabilityRes;
+ log.trace("Before getting capability {} {} {}", capabilityId, forRI, resourceInstanceId);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), capabilityName);
+ getCapabilityRes = titanGenericDao.getChildByEdgeCriteria(instanceVertex, GraphEdgeLabels.CALCULATED_CAPABILITY, props);
+ if (getCapabilityRes.isRight()) {
+ errorWrapper.setInnerElement(getCapabilityRes.right().value());
+ log.debug("Failed to get capability {} {} {} {} {}", capabilityId, ofRI, resourceInstanceId, statusIs, errorWrapper.getInnerElement());
+ }
+ log.trace("After getting capability for {} {} {} {} {}", capabilityId, forRI, resourceInstanceId, statusIs, errorWrapper.getInnerElement());
+ return getCapabilityRes;
+ }
+
+ private void validateCapabilityInstanceExistence(String resourceInstanceId, String capabilityId, Wrapper<TitanOperationStatus> errorWrapper) {
+ log.debug("Before validation of existence of capability instance of capability {} forRI {}.", capabilityId, resourceInstanceId);
+ boolean capabilityInstOfCapabilityAlreadyExists;
+ Either<Boolean, TitanOperationStatus> validateCapabilityInstExistenceRes = validateCapabilityInstExistence(resourceInstanceId, capabilityId);
+ if (validateCapabilityInstExistenceRes.isRight()) {
+ errorWrapper.setInnerElement(validateCapabilityInstExistenceRes.right().value());
+ log.debug("Failed to validate uniqueness of capability instance of capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ } else {
+ capabilityInstOfCapabilityAlreadyExists = validateCapabilityInstExistenceRes.left().value();
+ if (capabilityInstOfCapabilityAlreadyExists) {
+ errorWrapper.setInnerElement(TitanOperationStatus.ALREADY_EXIST);
+ log.debug("failedCreateCI {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ }
+ log.debug("After validation of existence of capability instance of capability {} forRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+
+ private void validateCapabilityInstanceExistence(TitanVertex resourceInstanceVertex, String resourceInstanceId, String capabilityId, Wrapper<TitanOperationStatus> errorWrapper) {
+ log.trace("Before validation of existence of capability instance of capability {} {} {}", capabilityId, forRI, resourceInstanceId);
+ boolean capabilityInstOfCapabilityAlreadyExists;
+ Either<Boolean, TitanOperationStatus> validateCapabilityInstExistenceRes = validateCapabilityInstExistence(resourceInstanceId, capabilityId);
+ if (validateCapabilityInstExistenceRes.isRight()) {
+ errorWrapper.setInnerElement(validateCapabilityInstExistenceRes.right().value());
+ log.debug("Failed to validate uniqueness of capability instance of capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ } else {
+ capabilityInstOfCapabilityAlreadyExists = validateCapabilityInstExistenceRes.left().value();
+ if (capabilityInstOfCapabilityAlreadyExists) {
+ errorWrapper.setInnerElement(TitanOperationStatus.ALREADY_EXIST);
+ log.debug("failedCreateCI {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+ }
+ log.debug("After validation of existence of capability instance of capability {} forRI {} statusIs {}.", capabilityId, resourceInstanceId, errorWrapper.getInnerElement());
+ }
+
+ private Either<List<PropertyValueData>, TitanOperationStatus> addPropertyValuesToCapabilityInstance(CapabilityInstData createdCapabilityInstance, List<ComponentInstanceProperty> propertyValues, Map<String, PropertyDefinition> defaultProperties) {
+ TitanOperationStatus error = null;
+ List<PropertyValueData> createdPropertyValues = new ArrayList<>();
+ for (ComponentInstanceProperty property : propertyValues) {
+ log.debug("Before adding property value {} toCI {}.", property.getName(), createdCapabilityInstance.getUniqueId());
+ PropertyValueData propertyData = buildPropertyValueData(property.getName(), property.getType(), property.getValue(), createdCapabilityInstance.getUniqueId());
+ Either<PropertyValueData, TitanOperationStatus> addPropertyValueRes = addPropertyValueToCapabilityInstance(createdCapabilityInstance, propertyData, defaultProperties.get(property.getName()));
+ if (addPropertyValueRes.isRight()) {
+ error = addPropertyValueRes.right().value();
+ log.debug("Failed to add property to capability instance {} ofRI. StatusIs {}.", createdCapabilityInstance.getUniqueId(), error);
+ break;
+ } else {
+ createdPropertyValues.add(addPropertyValueRes.left().value());
+ }
+ log.debug("After adding property value {} toCI {} statusIs {}", property.getName(), createdCapabilityInstance.getUniqueId(), error);
+ }
+ if (error == null) {
+ return Either.left(createdPropertyValues);
+ }
+ return Either.right(error);
+ }
+
+ private TitanOperationStatus addPropertyValuesToCapabilityInstance(TitanVertex createdCapabilityInstancevertex, List<ComponentInstanceProperty> propertyValues, Map<String, PropertyDefinition> defaultProperties) {
+ TitanOperationStatus error = null;
+ String id = (String) titanGenericDao.getProperty(createdCapabilityInstancevertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ for (ComponentInstanceProperty property : propertyValues) {
+ log.trace("Before adding property value {} {} {}", property.getName(), toCI, id);
+ PropertyValueData propertyData = buildPropertyValueData(property.getName(), property.getType(), property.getValue(), id);
+ TitanOperationStatus addPropertyValueRes = addPropertyValueToCapabilityInstance(createdCapabilityInstancevertex, propertyData, defaultProperties.get(property.getName()), id);
+ if (!addPropertyValueRes.equals(TitanOperationStatus.OK)) {
+ error = addPropertyValueRes;
+ log.debug("Failed to add property to capability instance {} {} {} {}", id, ofRI, statusIs, error);
+ break;
+ }
+ log.debug("After adding property value {} {} {} {} {}", property.getName(), toCI, id, statusIs, error);
+ }
+ if (error == null) {
+ return TitanOperationStatus.OK;
+ }
+ return error;
+ }
+
+ private PropertyValueData buildPropertyValueData(String propertyName, String propertyType, String propertyValue, String capabilityInstanceId) {
+ PropertyValueData propertyData = new PropertyValueData();
+ String uniqueId = UniqueIdBuilder.buildPropertyValueUniqueId(capabilityInstanceId, propertyName);
+ Long creationTime = System.currentTimeMillis();
+ propertyData.setUniqueId(uniqueId);
+ propertyData.setValue(propertyValue);
+ propertyData.setType(propertyType);
+ propertyData.setCreationTime(creationTime);
+ propertyData.setModificationTime(creationTime);
+ return propertyData;
+ }
+
+ private Either<PropertyValueData, TitanOperationStatus> addPropertyValueToCapabilityInstance(CapabilityInstData createdCapabilityInstance, PropertyValueData propertyValue, PropertyDefinition propertyDefinition) {
+ TitanOperationStatus error = null;
+ Map<String, Object> props = null;
+ Either<GraphRelation, TitanOperationStatus> createRelationRes;
+ PropertyValueData createdValue = null;
+ log.debug("Before creating property value node {} onGraph.", propertyValue.getUniqueId());
+ Either<PropertyValueData, TitanOperationStatus> createValueRes = titanGenericDao.createNode(propertyValue, PropertyValueData.class);
+ if (createValueRes.isRight()) {
+ error = createValueRes.right().value();
+ log.debug("Failed to create property value for capability instance {} ofRI statusIs {}.", createdCapabilityInstance.getUniqueId(), error);
+ }
+ log.debug("After creating property value node {} onGraph statusIs {}.", propertyValue.getUniqueId(), error);
+ if (error == null) {
+ log.debug("Before creating relation from property value node {} toCI {}.", propertyValue.getUniqueId(), createdCapabilityInstance.getUniqueId());
+ createdValue = createValueRes.left().value();
+ props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.PROPERTY_NAME.name(), propertyDefinition.getName());
+ props.put(GraphPropertiesDictionary.PROPERTY_ID.name(), propertyDefinition.getUniqueId());
+ createRelationRes = titanGenericDao.createRelation(createdCapabilityInstance, createdValue, GraphEdgeLabels.PROPERTY_VALUE, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to create relation from capability instance {} toValue {} statusIs {}.", createdCapabilityInstance.getUniqueId(), createdValue.getUniqueId(), error);
+ }
+ log.debug("After creating relation from property value node {} toCI {} statusIs {}.", propertyValue.getUniqueId(), createdCapabilityInstance.getUniqueId(), error);
+ }
+ if (error == null) {
+ log.debug("Before creating relation from property value node {} toProperty {}.", propertyValue.getUniqueId(), propertyDefinition.getUniqueId());
+ createRelationRes = titanGenericDao.createRelation(propertyValue, new PropertyData(propertyDefinition, null), GraphEdgeLabels.PROPERTY_IMPL, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to create relation from property value {} toProperty {} statusIs {}.", createdValue.getUniqueId(), propertyDefinition.getUniqueId(), error);
+ }
+ log.debug("After creating relation from property value node {} toProperty statusIs {}.", propertyValue.getUniqueId(), propertyDefinition.getUniqueId(), error);
+ }
+ if (error == null) {
+ return Either.left(createdValue);
+ }
+ return Either.right(error);
+ }
+
+ private TitanOperationStatus addPropertyValueToCapabilityInstance(TitanVertex createdCapabilityInstanceVertex, PropertyValueData propertyValue, PropertyDefinition propertyDefinition, String id) {
+ TitanOperationStatus error = null;
+ Map<String, Object> props = null;
+ TitanOperationStatus createRelationRes;
+ log.trace("Before creating property value node {} on graph.", propertyValue.getUniqueId());
+ Either<TitanVertex, TitanOperationStatus> createValueRes = titanGenericDao.createNode(propertyValue);
+ if (createValueRes.isRight()) {
+ error = createValueRes.right().value();
+ if (log.isDebugEnabled()){
+ log.debug("Failed to create property value for capability instance {} {} {} {}", id, ofRI, statusIs, error);
+ }
+ }
+ log.trace("After creating property value node {} on graph status is {}", propertyValue.getUniqueId(), error);
+ TitanVertex createdPropVertex = null;
+ String createdId = null;
+ if (error == null) {
+ log.trace("Before creating relation from property value node {} {} {} ", propertyValue.getUniqueId(), toCI, id);
+ props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.PROPERTY_NAME.name(), propertyDefinition.getName());
+ props.put(GraphPropertiesDictionary.PROPERTY_ID.name(), propertyDefinition.getUniqueId());
+ createdPropVertex = createValueRes.left().value();
+ createRelationRes = titanGenericDao.createEdge(createdCapabilityInstanceVertex, createdPropVertex, GraphEdgeLabels.PROPERTY_VALUE, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ createdId = (String) titanGenericDao.getProperty(createdPropVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to create relation from capability instance {} {} {} {} {}", id, toValue, createdId, statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()){
+ log.trace("After creating relation from property value node {} {} {} {} {}", propertyValue.getUniqueId(), toCI, id, statusIs, error);
+ }
+ }
+ if (error == null) {
+ log.trace("Before creating relation from property value node {} {} {}", propertyValue.getUniqueId(), toProperty, propertyDefinition.getUniqueId());
+ createRelationRes = titanGenericDao.createEdge(createdPropVertex, new PropertyData(propertyDefinition, null), GraphEdgeLabels.PROPERTY_IMPL, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ log.debug("Failed to create relation from property value {} {} {} {} {}", createdId, toProperty, propertyDefinition.getUniqueId(), statusIs, error);
+ }
+ log.debug("After creating relation from property value node {} {} {} {} {}", propertyValue.getUniqueId(), toProperty, propertyDefinition.getUniqueId(), statusIs, error);
+ }
+ if (error == null) {
+ return TitanOperationStatus.OK;
+ }
+ return error;
+ }
+
+ private Either<Boolean, TitanOperationStatus> validateCapabilityInstanceProperties(Map<String, PropertyDefinition> defaultProperties, List<ComponentInstanceProperty> propertyValues) {
+ Either<Boolean, TitanOperationStatus> result = Either.left(true);
+ for (ComponentInstanceProperty property : propertyValues) {
+ result = validateUpdateCapabilityInstancePropertyValue(property, defaultProperties);
+ if (result.isRight()) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ private Either<Boolean, TitanOperationStatus> validateUpdateCapabilityInstancePropertyValue(ComponentInstanceProperty property, Map<String, PropertyDefinition> defaultProperties) {
+ PropertyDefinition defaultProperty;
+ String propertyName = property.getName();
+ Either<Boolean, TitanOperationStatus> result = null;
+ if (defaultProperties.containsKey(propertyName)) {
+ defaultProperty = defaultProperties.get(propertyName);
+ String propertyType = property.getType() == null || property.getType().isEmpty() ? defaultProperty.getType() : property.getType();
+
+ String innerType = null;
+ if (property.getSchema() != null && property.getSchema().getProperty() != null)
+ innerType = property.getSchema().getProperty().getType();
+ if (innerType == null && defaultProperty.getSchema() != null && defaultProperty.getSchema().getProperty() != null)
+ innerType = defaultProperty.getSchema().getProperty().getType();
+
+ if (defaultProperty.getType().equals(propertyType)) {
+ String propertyValue = property.getValue();
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Failed to update property value statusIs {}.", status);
+ result = Either.right(status);
+ }
+ if (result == null) {
+ Either<Object, Boolean> updatedPropertyValueRes = propertyOperation.validateAndUpdatePropertyValue(propertyType, propertyValue, innerType, allDataTypes.left().value());
+ if (updatedPropertyValueRes.isLeft()) {
+ if (updatedPropertyValueRes.left().value() != null)
+ property.setDefaultValue(updatedPropertyValueRes.left().value().toString());
+ result = Either.left(true);
+ } else {
+ result = Either.right(TitanOperationStatus.INVALID_PROPERTY);
+ }
+ }
+ log.debug("The property with name {} has invalid type {} or invalid value {}.", propertyName, propertyType, propertyValue);
+
+ } else {
+ result = Either.right(TitanOperationStatus.PROPERTY_NAME_ALREADY_EXISTS);
+ log.debug("The property with name {} and different type already exists.", propertyName);
+ }
+ } else {
+ result = Either.right(TitanOperationStatus.NOT_FOUND);
+ log.debug("Failed to find property with name {}.", propertyName);
+ }
+ return result;
+ }
+
+ /**
+ * validate capability instance uniqueness
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @return
+ */
+ @Override
+ public Either<Boolean, TitanOperationStatus> validateCapabilityInstExistence(String resourceInstanceId, String capabilityId) {
+ Either<Boolean, TitanOperationStatus> result = null;
+ TitanOperationStatus error;
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), capabilityId);
+ Either<Edge, TitanOperationStatus> getCapabilityInstanceEdgeRes = titanGenericDao.getOutgoingEdgeByCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, GraphEdgeLabels.CAPABILITY_INST, props);
+ if (getCapabilityInstanceEdgeRes.isRight()) {
+ error = getCapabilityInstanceEdgeRes.right().value();
+ if (error.equals(TitanOperationStatus.NOT_FOUND)) {
+ result = Either.left(false);
+ } else {
+ log.debug("Failed to get outgoing edge for resource instance {} statusIs {}.", resourceInstanceId, error);
+ result = Either.right(error);
+ }
+ }
+ if (result == null) {
+ result = Either.left(true);
+ }
+ return result;
+ }
+
+ @Override
+ public Either<Boolean, TitanOperationStatus> validateCapabilityInstExistence(TitanVertex instanceVertex, String resourceInstanceId, String capabilityId) {
+ Either<Boolean, TitanOperationStatus> result = null;
+ TitanOperationStatus error;
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), capabilityId);
+ Either<Edge, TitanOperationStatus> getCapabilityInstanceEdgeRes = titanGenericDao.getOutgoingEdgeByCriteria(instanceVertex, GraphEdgeLabels.CAPABILITY_INST, props);
+ if (getCapabilityInstanceEdgeRes.isRight()) {
+ error = getCapabilityInstanceEdgeRes.right().value();
+ if (error.equals(TitanOperationStatus.NOT_FOUND)) {
+ result = Either.left(false);
+ } else {
+ log.debug("Failed to get outgoing edge for resource instance {} {} {}", resourceInstanceId, statusIs, error);
+ result = Either.right(error);
+ }
+ }
+ if (result == null) {
+ result = Either.left(true);
+ }
+ return result;
+ }
+
+ private Either<CapabilityInstData, TitanOperationStatus> createCapabilityInstanceOnGraph(String resourceInstanceId, CapabilityData overrideCapabilityData, CapabilityInstData capabilityInstance) {
+ log.debug("Before creation of capability instance of capability {} forRI {}.", overrideCapabilityData.getUniqueId(), resourceInstanceId);
+
+ Either<GraphRelation, TitanOperationStatus> createRelationRes;
+ CapabilityInstData createdCapabilityInstance = null;
+ String capabilityInstanceId = null;
+ TitanOperationStatus error = null;
+ Either<CapabilityInstData, TitanOperationStatus> createCapabilityInstanceRes = titanGenericDao.createNode(capabilityInstance, CapabilityInstData.class);
+ if (createCapabilityInstanceRes.isRight()) {
+ error = createCapabilityInstanceRes.right().value();
+ log.debug("failedCreateCI {} forRI {} statusIs {}.", overrideCapabilityData.getUniqueId(), resourceInstanceId, error);
+ }
+ log.debug("After creation of capability instance of capability {} forRI {} statusIs {}.", overrideCapabilityData.getUniqueId(), resourceInstanceId, error);
+ if (error == null) {
+ createdCapabilityInstance = createCapabilityInstanceRes.left().value();
+ capabilityInstanceId = createdCapabilityInstance.getUniqueId();
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), overrideCapabilityData.getUniqueId());
+ UniqueIdData resourceInstanceIdData = new UniqueIdData(NodeTypeEnum.ResourceInstance, resourceInstanceId);
+ log.debug("Before associating resource instance {} to capability instance.", resourceInstanceId);
+ createRelationRes = titanGenericDao.createRelation(resourceInstanceIdData, capabilityInstance, GraphEdgeLabels.CAPABILITY_INST, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to assotiate resource instance {} toCI {} statusIs {}.", resourceInstanceId, capabilityInstanceId, error);
+ }
+ log.debug("After associating resource instance {} to CI {} statusIs {}.", resourceInstanceId, capabilityInstanceId, error);
+ }
+ if (error == null) {
+ log.debug("Before associating capability instance {} toCapability {}.", capabilityInstanceId, overrideCapabilityData.getUniqueId());
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), overrideCapabilityData.getUniqueId());
+ createRelationRes = titanGenericDao.createRelation(createdCapabilityInstance, overrideCapabilityData, GraphEdgeLabels.INSTANCE_OF, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to associate capability instance {} toCapability statusIs {}.", capabilityInstanceId, overrideCapabilityData.getUniqueId(), error);
+ }
+ log.debug("After associating capability instance {} toCapability statusIs {}.", capabilityInstanceId, overrideCapabilityData.getUniqueId(), error);
+ }
+ if (error == null) {
+ return createCapabilityInstanceRes;
+ }
+ return Either.right(error);
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> createCapabilityInstanceOnGraph(TitanVertex riVertex, String resourceInstanceId, TitanVertex overrideCapabilityDataVertex, CapabilityInstData capabilityInstance) {
+ String overrideCapabilityDataId = (String) titanGenericDao.getProperty(overrideCapabilityDataVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ log.trace("Before creation of capability instance of capability {} {} {}", overrideCapabilityDataVertex, forRI, resourceInstanceId);
+
+ TitanOperationStatus createRelationRes;
+ TitanVertex createdCapabilityInstance = null;
+ String capabilityInstanceId = null;
+ TitanOperationStatus error = null;
+ Either<TitanVertex, TitanOperationStatus> createCapabilityInstanceRes = titanGenericDao.createNode(capabilityInstance);
+ if (createCapabilityInstanceRes.isRight()) {
+ error = createCapabilityInstanceRes.right().value();
+ log.debug("Failed to create capability instance of capability {} {} {} {} {}", overrideCapabilityDataId, forRI, resourceInstanceId, statusIs, error);
+ }
+ log.trace("After creation of capability instance of capability {} {} {} {} {}", overrideCapabilityDataId, forRI, resourceInstanceId, statusIs, error);
+ if (error == null) {
+ createdCapabilityInstance = createCapabilityInstanceRes.left().value();
+ capabilityInstanceId = (String) titanGenericDao.getProperty(createdCapabilityInstance, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), overrideCapabilityDataId);
+ log.debug("Before associating resource instance {} to capability instance.", resourceInstanceId);
+
+ createRelationRes = titanGenericDao.createEdge(riVertex, capabilityInstance, GraphEdgeLabels.CAPABILITY_INST, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ log.debug("Failed to assotiate resource instance {} {} {} {} {}", resourceInstanceId, toCI, capabilityInstanceId, statusIs, error);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After associating resource instance {} {} {} {} {}", resourceInstanceId, toCI, capabilityInstanceId, statusIs, error);
+ }
+ }
+ if (error == null) {
+ log.trace("Before associating capability instance {} {} {}", capabilityInstanceId, toCapability, overrideCapabilityDataId);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), overrideCapabilityDataId);
+ createRelationRes = titanGenericDao.createEdge(createdCapabilityInstance, overrideCapabilityDataVertex, GraphEdgeLabels.INSTANCE_OF, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ log.debug("Failed to associate capability instance {} {} {} {} {}", capabilityInstanceId, toCapability, overrideCapabilityDataId, statusIs, error);
+ }
+ log.debug("After associating capability instance {} {} {} {} {}", capabilityInstanceId, toCapability, overrideCapabilityDataId, statusIs, error);
+ }
+ if (error == null) {
+ return createCapabilityInstanceRes;
+ }
+ return Either.right(error);
+ }
+
+ private CapabilityInstData buildCapabilityInstanceData(String resourceInstanceId, CapabilityDefinition capability) {
+ CapabilityInstData capabilityInstance = new CapabilityInstData();
+ Long creationTime = System.currentTimeMillis();
+ String uniqueId = UniqueIdBuilder.buildCapabilityInstanceUid(resourceInstanceId, capability.getName());
+
+ capabilityInstance.setCreationTime(creationTime);
+ capabilityInstance.setModificationTime(creationTime);
+ capabilityInstance.setUniqueId(uniqueId);
+
+ return capabilityInstance;
+ }
+
+ /**
+ * delete capability instance from resource instance
+ *
+ * @param resourceInstanceId
+ * @param capabilityInstanceId
+ * @return
+ */
+ @Override
+ public Either<CapabilityInstData, TitanOperationStatus> deleteCapabilityInstanceFromResourceInstance(String resourceInstanceId, String capabilityInstanceId) {
+ log.debug("Before deleting of capability instance {} fromRI {}.", capabilityInstanceId, resourceInstanceId);
+
+ Either<CapabilityInstData, TitanOperationStatus> deleteCapabilityInstRes = null;
+ TitanOperationStatus error = null;
+ Either<Boolean, TitanOperationStatus> deleteProperyValuesRes = deleteAllPropertyValuesOfCapabilityInstance(resourceInstanceId, capabilityInstanceId);
+ if (deleteProperyValuesRes.isRight()) {
+ error = deleteProperyValuesRes.right().value();
+ log.debug("failedDeletePropertyValues {} for RI {} statusIs {}.", capabilityInstanceId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ deleteCapabilityInstRes = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityInst), capabilityInstanceId, CapabilityInstData.class);
+ if (deleteCapabilityInstRes.isRight()) {
+ error = deleteCapabilityInstRes.right().value();
+ log.debug("Failed to delete capability instance {} forRI {} statusIs {}", capabilityInstanceId, resourceInstanceId, error);
+ }
+ }
+ log.debug("After deleting of capability instance {} fromRI {} statusIs {}.", capabilityInstanceId, resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(deleteCapabilityInstRes.left().value());
+ }
+ return Either.right(error);
+ }
+
+ private Either<Boolean, TitanOperationStatus> deleteAllPropertyValuesOfCapabilityInstance(String resourceInstanceId, String capabilityInstanceId) {
+ log.debug("Before deleting all property values of capability instance {} fromRI {}.", capabilityInstanceId, resourceInstanceId);
+ TitanOperationStatus error = null;
+ List<ImmutablePair<PropertyValueData, GraphEdge>> deletePropertiesPairs;
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> getPropertyValuesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityInst), capabilityInstanceId,
+ GraphEdgeLabels.PROPERTY_VALUE, NodeTypeEnum.PropertyValue, PropertyValueData.class);
+ if (getPropertyValuesRes.isRight()) {
+ error = getPropertyValuesRes.right().value();
+ log.debug("Failed to retrieve property values of capability instance {} forRI {} status {}.", capabilityInstanceId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ deletePropertiesPairs = getPropertyValuesRes.left().value();
+ for (ImmutablePair<PropertyValueData, GraphEdge> propertyPair : deletePropertiesPairs) {
+ Either<PropertyValueData, TitanOperationStatus> deletePropertyRes = titanGenericDao.deleteNode(propertyPair.getLeft(), PropertyValueData.class);
+ if (deletePropertyRes.isRight()) {
+ error = deletePropertyRes.right().value();
+ log.debug("failedDeletePropertyValues {} forRI {} statusIs {}.", capabilityInstanceId, resourceInstanceId, error);
+ break;
+ }
+ }
+ }
+ log.debug("After deleting all property values of capability instance {} fromRI {} statusIs {}.", capabilityInstanceId, resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(true);
+ }
+ return Either.right(error);
+ }
+
+ /**
+ * get all capability instances for resource instance returns all Capability Instances related to Resource Instance as List<CapabilityInstData> or TitanOperationStatus if error occurs or if Resource Instance have no any related Capability
+ * Instance
+ *
+ * @param resourceInstanceId
+ * @return Either<List<CapabilityInstData>, TitanOperationStatus>
+ */
+ @Override
+ public Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getAllCapabilityInstancesOfResourceInstance(String resourceInstanceId) {
+ log.debug("Before deleting all capability instances of resource instance {}.", resourceInstanceId);
+ TitanOperationStatus error = null;
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getCapabilityInstancesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId,
+ GraphEdgeLabels.CAPABILITY_INST, NodeTypeEnum.CapabilityInst, CapabilityInstData.class);
+ if (getCapabilityInstancesRes.isRight()) {
+ error = getCapabilityInstancesRes.right().value();
+ log.debug("Failed to retrieve capability Instances of resource instance {} statusIs {}.", resourceInstanceId, error);
+ }
+ log.debug("After deleting all capability instances of resource instance {} statusIs {}", resourceInstanceId, error);
+ if (error == null) {
+ return getCapabilityInstancesRes;
+ }
+ return Either.right(error);
+ }
+
+ /**
+ * get capability instance of capability for resource instance
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @return
+ */
+ @Override
+ public Either<CapabilityInstData, TitanOperationStatus> getCapabilityInstanceOfCapabilityOfResourceInstance(String resourceInstanceId, String capabilityId) {
+ TitanOperationStatus error = null;
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), capabilityId);
+ Either<ImmutablePair<CapabilityInstData, GraphEdge>, TitanOperationStatus> getCapabilityInstanceRes = titanGenericDao.getChildByEdgeCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId,
+ GraphEdgeLabels.CAPABILITY_INST, NodeTypeEnum.CapabilityInst, CapabilityInstData.class, props);
+ if (getCapabilityInstanceRes.isRight()) {
+ error = getCapabilityInstanceRes.right().value();
+ log.debug("Failed to retrieve capability Instance of capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ return Either.left(getCapabilityInstanceRes.left().value().getLeft());
+ }
+ return Either.right(error);
+ }
+
+ /**
+ * update capability property values
+ *
+ * @param resourceInstanceId
+ * @param propertyValues
+ * @param capabilityId
+ * @return
+ */
+ @Override
+ public Either<List<PropertyValueData>, TitanOperationStatus> updateCapabilityPropertyValues(String resourceInstanceId, String capabilityId, List<ComponentInstanceProperty> propertyValues) {
+ log.debug("Before updating property values of capability {} ofRI {}.", capabilityId, resourceInstanceId);
+ TitanOperationStatus error = null;
+ Map<String, Object> props = new HashMap<>();
+ CapabilityInstData capabilityInstance = null;
+ String capabilityInstanceId = null;
+ Either<Boolean, TitanOperationStatus> deleteProperyValuesRes;
+
+ CapabilityData overrideCapabilityData;
+ CapabilityDefinition overrideCapabilityDefinition;
+ Map<String, PropertyDefinition> defaultProperties = null;
+ Either<ImmutablePair<CapabilityData, GraphEdge>, TitanOperationStatus> getCapabilityDataRes = null;
+ Either<List<PropertyValueData>, TitanOperationStatus> addPropertyValuesRes = null;
+ Either<CapabilityDefinition, TitanOperationStatus> getCapabilityDefinitionRes = null;
+
+ log.debug("Before getting all capability instances of RI {}.", resourceInstanceId);
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), capabilityId);
+ Either<ImmutablePair<CapabilityInstData, GraphEdge>, TitanOperationStatus> getCapabilityInstancesRes = titanGenericDao.getChildByEdgeCriteria(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId,
+ GraphEdgeLabels.CAPABILITY_INST, NodeTypeEnum.CapabilityInst, CapabilityInstData.class, props);
+ if (getCapabilityInstancesRes.isRight()) {
+ error = getCapabilityInstancesRes.right().value();
+ log.debug("Failed to retrieve capability Instances of capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, error);
+ }
+ log.debug("After getting all capability instances ofRI {} statusIs {}.", resourceInstanceId, error);
+ if (error == null) {
+ log.debug("Before deleting all capability instances ofRI {}.", resourceInstanceId);
+ capabilityInstance = getCapabilityInstancesRes.left().value().getLeft();
+ capabilityInstanceId = capabilityInstance.getUniqueId();
+ deleteProperyValuesRes = deleteAllPropertyValuesOfCapabilityInstance(resourceInstanceId, capabilityInstanceId);
+ if (deleteProperyValuesRes.isRight()) {
+ error = deleteProperyValuesRes.right().value();
+ log.debug("failedDeletePropertyValues {} forRI {} statusIs {}", capabilityInstanceId, resourceInstanceId, statusIs, error);
+ }
+ log.debug("After deleting all capability instances ofRI {} statusIs {}.", resourceInstanceId, error);
+ }
+ if (error == null) {
+ log.debug("Before getting capability {} ofRI {}.", capabilityId, resourceInstanceId);
+ getCapabilityDataRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, GraphEdgeLabels.CALCULATED_CAPABILITY, NodeTypeEnum.Capability, CapabilityData.class);
+ if (getCapabilityDataRes.isRight()) {
+ error = getCapabilityDataRes.right().value();
+ log.debug("Failed to get capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, error);
+ }
+ log.debug("After getting capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ log.debug("Before getting capability definition for capability {} ofRI {}.", capabilityId, resourceInstanceId);
+ overrideCapabilityData = getCapabilityDataRes.left().value().getLeft();
+ getCapabilityDefinitionRes = capabilityOperation.getCapabilityByCapabilityData(overrideCapabilityData);
+ if (getCapabilityDefinitionRes.isRight()) {
+ error = getCapabilityDefinitionRes.right().value();
+ log.debug("Failed to retrieve capability {} ofRI {} statusIs {}", capabilityId, resourceInstanceId, error);
+ }
+ log.debug("After getting capability definition for capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ log.debug("Before validating capability properties of capability instance {} ofRI {}.", capabilityInstanceId, resourceInstanceId);
+ overrideCapabilityDefinition = getCapabilityDefinitionRes.left().value();
+ if (overrideCapabilityDefinition.getProperties() != null) {
+ defaultProperties = overrideCapabilityDefinition.getProperties().stream().collect(Collectors.toMap(PropertyDefinition::getName, Function.identity()));
+ }
+ Either<Boolean, TitanOperationStatus> result = validateCapabilityInstanceProperties(defaultProperties, propertyValues);
+ if (result.isRight()) {
+ error = result.right().value();
+ log.debug("failedAddProperties {} ofRI {} statusIs {}.", capabilityInstance.getUniqueId(), resourceInstanceId, error);
+ }
+ log.debug("After validating capability properties of capability instance {} of RI {} statusIs {}.", capabilityInstanceId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ log.debug("Before adding property values toCI {} ofRI {}.", capabilityInstanceId, resourceInstanceId);
+ addPropertyValuesRes = addPropertyValuesToCapabilityInstance(capabilityInstance, propertyValues, defaultProperties);
+ if (addPropertyValuesRes.isRight()) {
+ error = addPropertyValuesRes.right().value();
+ log.debug("failedAddProperties {} ofRI {} statusIs {}.", capabilityInstance.getUniqueId(), resourceInstanceId, error);
+ }
+ log.debug("Before adding property values toCI {} ofRI {}.", capabilityInstanceId, resourceInstanceId);
+ }
+ log.debug("After updating property values of capability {} ofRI {} statusIs {}.", capabilityId, resourceInstanceId, error);
+ if (error == null) {
+ return addPropertyValuesRes;
+ }
+ return Either.right(error);
+ }
+
+ /**
+ * clone and associate capability instance with property values
+ *
+ * @param createdComponentInstance
+ * @param capability
+ * @param capabilityInstPair
+ * @return
+ */
+ @Override
+ public Either<ImmutablePair<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> cloneAssociateCapabilityInstanceWithPropertyValues(ComponentInstanceData createdComponentInstance, CapabilityDefinition capability,
+ ImmutablePair<CapabilityInstData, GraphEdge> capabilityInstPair) {
+
+ TitanOperationStatus error = null;
+ String componentInstanceId = createdComponentInstance.getUniqueId();
+ String capabilityInstanceId = capabilityInstPair.getLeft().getUniqueId();
+
+ log.debug("Before cloning capability instance with property values of capability instance {} ofRI {}.", capabilityInstanceId, componentInstanceId);
+ List<ImmutablePair<PropertyValueData, GraphEdge>> propertyValuePairs;
+ List<PropertyValueData> newPropertyValues = new ArrayList<>();
+ CapabilityInstData cloneCapabilityInstance = null;
+ Either<CapabilityInstData, TitanOperationStatus> cloneCapabilityInstanceNodeRes = null;
+
+ log.debug("Before getting all property values ofCI {} ofRI {}.", capabilityInstanceId, componentInstanceId);
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> getPropertyValuesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityInst), capabilityInstPair.getLeft().getUniqueId(),
+ GraphEdgeLabels.PROPERTY_VALUE, NodeTypeEnum.PropertyValue, PropertyValueData.class);
+ if (getPropertyValuesRes.isRight()) {
+ error = getPropertyValuesRes.right().value();
+ log.debug("Failed to retrieve property values of capability instance {} ofCI {} statusIs {}.", capabilityInstPair.getLeft().getUniqueId(), componentInstanceId ,error);
+ }
+ log.debug("After getting all property values ofCI {} ofRI {} statusIs {}.", capabilityInstanceId, componentInstanceId, error);
+ if (error == null) {
+ CapabilityInstData cloneCapabilityInst = buildCapabilityInstanceData(componentInstanceId, capability);
+ log.debug("Before creating capability instance node {} onGraph.", cloneCapabilityInst.getUniqueId());
+ cloneCapabilityInstanceNodeRes = titanGenericDao.createNode(cloneCapabilityInst, CapabilityInstData.class);
+ if (cloneCapabilityInstanceNodeRes.isRight()) {
+ error = cloneCapabilityInstanceNodeRes.right().value();
+ log.debug("Failed to create capability instance of capability {} ofCI {} statusIs {}.", capability.getUniqueId(), componentInstanceId, error);
+ }
+ log.debug("After creating capability instance node {} onGraph. statusIs {}", cloneCapabilityInst.getUniqueId(), error);
+ }
+
+ if (error == null) {
+ log.debug("Before creating relation from capability instance {} toCapability {} onGraph.", cloneCapabilityInstanceNodeRes.left().value().getUniqueId(), capability.getUniqueId());
+ cloneCapabilityInstance = cloneCapabilityInstanceNodeRes.left().value();
+ CapabilityData capabilityData = buildCapabilityData(capability);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), capabilityData.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> createRelationRes = titanGenericDao.createRelation(cloneCapabilityInstance, capabilityData, GraphEdgeLabels.INSTANCE_OF, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to associate capability instance {} toCapability {} statusIs {}.", cloneCapabilityInstance.getUniqueId(), capability.getUniqueId(), error);
+ }
+ log.debug("After creating relation from capability instance {} toCapability {} onGraph. statusIs {}.", cloneCapabilityInstanceNodeRes.left().value().getUniqueId(), capability.getUniqueId(), error);
+ }
+
+ if (error == null) {
+ log.debug("Before cloning property values ofCI {}.", capabilityInstanceId);
+ propertyValuePairs = getPropertyValuesRes.left().value();
+ for (ImmutablePair<PropertyValueData, GraphEdge> propertyValuePair : propertyValuePairs) {
+ Either<PropertyValueData, TitanOperationStatus> clonePropertyValueRes = cloneAssociatePropertyValue(cloneCapabilityInstance, propertyValuePair);
+ if (clonePropertyValueRes.isRight()) {
+ error = clonePropertyValueRes.right().value();
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to clone property value {} ofCapability {} ofCI {}. statusIs {}.", propertyValuePair.getLeft().getUniqueId(), capability.getUniqueId(), componentInstanceId, error);
+ }
+ break;
+ } else {
+ newPropertyValues.add(clonePropertyValueRes.left().value());
+ }
+ }
+ log.debug("After cloning property values of CI {} statusIs {}.", capabilityInstanceId, error);
+ }
+ log.debug("After cloning capability instance with property values of capability instance {} ofRI {} statusIs {}.", capabilityInstanceId, componentInstanceId, error);
+ if (error == null) {
+ return Either.left(new ImmutablePair<CapabilityInstData, List<PropertyValueData>>(cloneCapabilityInstance, newPropertyValues));
+ }
+ return Either.right(error);
+ }
+
+ public Either<TitanVertex, TitanOperationStatus> cloneAssociateCapabilityInstanceWithPropertyValues(TitanVertex componentInstanceVertex, CapabilityDefinition capability, ImmutablePair<CapabilityInstData, GraphEdge> capabilityInstPair) {
+
+ TitanOperationStatus error = null;
+ String componentInstanceId = (String) titanGenericDao.getProperty(componentInstanceVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String capabilityInstanceId = capabilityInstPair.getLeft().getUniqueId();
+
+ if (log.isTraceEnabled()) {
+ log.trace("Before cloning capability instance with property values of capability instance {} {} {}", capabilityInstanceId, ofRI, componentInstanceId);
+ }
+ List<ImmutablePair<TitanVertex, Edge>> propertyValuePairs;
+ Either<TitanVertex, TitanOperationStatus> cloneCapabilityInstanceNodeRes = null;
+
+ if (log.isTraceEnabled()) {
+ log.trace("Before getting all property values {} {} {} {}", ofCI, capabilityInstanceId, ofRI, componentInstanceId);
+ }
+ Either<List<ImmutablePair<TitanVertex, Edge>>, TitanOperationStatus> getPropertyValuesRes = titanGenericDao.getChildrenVertecies(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityInst), capabilityInstPair.getLeft().getUniqueId(),
+ GraphEdgeLabels.PROPERTY_VALUE);
+ if (getPropertyValuesRes.isRight()) {
+ error = getPropertyValuesRes.right().value();
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to retrieve property values of capability instance {} {} {} {} {}", capabilityInstPair.getLeft().getUniqueId(), ofCI, componentInstanceId, statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After getting all property values {} {} {} {} {} {}", ofCI, capabilityInstanceId, ofRI, componentInstanceId, statusIs, error);
+ }
+ if (error == null) {
+ CapabilityInstData cloneCapabilityInst = buildCapabilityInstanceData(componentInstanceId, capability);
+ log.trace("Before creating capability instance node {} {} ", cloneCapabilityInst.getUniqueId(), onGraph);
+ cloneCapabilityInstanceNodeRes = titanGenericDao.createNode(cloneCapabilityInst);
+ if (cloneCapabilityInstanceNodeRes.isRight()) {
+ error = cloneCapabilityInstanceNodeRes.right().value();
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to create capability instance of capability {} {} {} {} {}", capability.getUniqueId(), ofCI, componentInstanceId, statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After creating capability instance node {} {} {} {}", cloneCapabilityInst.getUniqueId(), onGraph, statusIs, error);
+ }
+ }
+ CapabilityData capabilityData;
+ TitanVertex cloneCapabilityInstance = null;
+ if (error == null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Before creating relation from capability instance {} {} {} {}", capability.getUniqueId(), toCapability, capability.getUniqueId(), onGraph);
+ }
+ capabilityData = buildCapabilityData(capability);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(), capabilityData.getUniqueId());
+ cloneCapabilityInstance = cloneCapabilityInstanceNodeRes.left().value();
+ TitanOperationStatus createRelationRes = titanGenericDao.createEdge(cloneCapabilityInstance, capabilityData, GraphEdgeLabels.INSTANCE_OF, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to associate capability instance {} {} {} {} {}", capabilityData.getUniqueId(), toCapability, capability.getUniqueId(), statusIs, createRelationRes);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After creating relation from capability instance {} {} {} {} {} {}", capabilityData.getUniqueId(), toCapability, capability.getUniqueId(), onGraph, statusIs, error);
+ }
+ }
+
+ if (error == null) {
+ log.trace("Before cloning property values {} {} ", ofCI, capabilityInstanceId);
+ propertyValuePairs = getPropertyValuesRes.left().value();
+ for (ImmutablePair<TitanVertex, Edge> propertyValuePair : propertyValuePairs) {
+ TitanOperationStatus clonePropertyValueRes = cloneAssociatePropertyValue(cloneCapabilityInstance, propertyValuePair);
+ if (!clonePropertyValueRes.equals(TitanOperationStatus.OK)) {
+ error = clonePropertyValueRes;
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to clone property value of capability {} {} {} {} {}", capability.getUniqueId(), ofCI, componentInstanceId, statusIs, error);
+ }
+ break;
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("After cloning property values {} {} {} {}", ofCI, capabilityInstanceId, statusIs, error);
+ }
+ }
+ log.debug("After cloning capability instance with property values of capability instance {} ofRI {} statusIs {}.", capabilityInstanceId, componentInstanceId, error);
+ if (error == null) {
+ return Either.left(cloneCapabilityInstance);
+ }
+ return Either.right(error);
+ }
+
+ private CapabilityData buildCapabilityData(CapabilityDefinition capability) {
+ CapabilityData capabilityData = new CapabilityData();
+ capabilityData.setUniqueId(capability.getUniqueId());
+ capabilityData.setDescription(capability.getDescription());
+ capabilityData.setType(capability.getType());
+ capabilityData.setMaxOccurrences(capability.getMaxOccurrences());
+ capabilityData.setMinOccurrences(capability.getMinOccurrences());
+ List<String> validSourceTypes = capability.getValidSourceTypes();
+ if (validSourceTypes != null) {
+ capabilityData.setValidSourceTypes(validSourceTypes);
+ }
+ return capabilityData;
+ }
+
+ private Either<PropertyValueData, TitanOperationStatus> cloneAssociatePropertyValue(CapabilityInstData cloneCapabilityInstance, ImmutablePair<PropertyValueData, GraphEdge> propertyValuePair) {
+ TitanOperationStatus error = null;
+ String propertyValueID = propertyValuePair.getLeft().getUniqueId();
+ String capabilityInstanceId = cloneCapabilityInstance.getUniqueId();
+ log.debug("Before cloning property values {} ofCI {}.", propertyValueID, capabilityInstanceId);
+
+ Map<String, Object> props = propertyValuePair.getRight().getProperties();
+ PropertyData propertyData = new PropertyData();
+ String propertyId = (String) props.get(GraphPropertiesDictionary.PROPERTY_ID.name());
+ propertyData.getPropertyDataDefinition().setUniqueId(propertyId);
+
+ PropertyValueData propertyValue = buildPropertyValueData((String) props.get(GraphPropertiesDictionary.PROPERTY_NAME.name()), propertyValuePair.getLeft().getType(), propertyValuePair.getLeft().getValue(), capabilityInstanceId);
+ PropertyValueData createdValue = null;
+ Either<GraphRelation, TitanOperationStatus> createRelationRes;
+
+ log.debug("Before creating property values node {} onGraph.", propertyValue.getUniqueId());
+ Either<PropertyValueData, TitanOperationStatus> createValueRes = titanGenericDao.createNode(propertyValue, PropertyValueData.class);
+ if (createValueRes.isRight()) {
+ error = createValueRes.right().value();
+ log.debug("Failed to create property value for capability instance {} ofRI. statusIs {}.", cloneCapabilityInstance.getUniqueId(), error);
+ }
+ log.debug("After creating property values node {} onGraph. statusIs {}.", propertyValue.getUniqueId(), error);
+ if (error == null) {
+ createdValue = createValueRes.left().value();
+ log.debug("Before creating relation from capability instance {} toValue {}.", capabilityInstanceId, createdValue.getUniqueId());
+ createRelationRes = titanGenericDao.createRelation(cloneCapabilityInstance, createdValue, GraphEdgeLabels.PROPERTY_VALUE, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to create relation from capability instance {} toValue {} statusIs {}.", cloneCapabilityInstance.getUniqueId(), createdValue.getUniqueId(), error);
+ }
+ log.debug("After creating relation from capability instance {} toValue {} statusIs {}", capabilityInstanceId, createdValue.getUniqueId(), error);
+ }
+ if (error == null) {
+ log.debug("Before creating relation from property value {} toProperty {}.", createdValue, propertyData.getUniqueId());
+ createRelationRes = titanGenericDao.createRelation(createdValue, propertyData, GraphEdgeLabels.PROPERTY_IMPL, props);
+ if (createRelationRes.isRight()) {
+ error = createRelationRes.right().value();
+ log.debug("Failed to create relation from property value {} toProperty {} statusIs {}.", createdValue.getUniqueId(), propertyId, error);
+ }
+ log.debug("Before creating relation from property value {} toProperty {} statusIs {}.", createdValue, propertyData.getUniqueId(), error);
+ }
+ log.debug("After cloning property values {} ofCI {} statusIs {}.", propertyValueID, capabilityInstanceId, error);
+ if (error == null) {
+ return Either.left(createdValue);
+ }
+ return Either.right(error);
+ }
+
+ private TitanOperationStatus cloneAssociatePropertyValue(TitanVertex capabilityInstanceVertex, ImmutablePair<TitanVertex, Edge> propertyValuePair) {
+ TitanOperationStatus error = null;
+ TitanVertex propertyVertex = propertyValuePair.getLeft();
+ String propertyValueID = (String) titanGenericDao.getProperty(propertyVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String capabilityInstanceId = (String) titanGenericDao.getProperty(capabilityInstanceVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ if (log.isTraceEnabled()) {
+ log.trace("Before cloning property values {} {} {}", propertyValueID, ofCI, capabilityInstanceId);
+ }
+
+ Map<String, Object> props = titanGenericDao.getProperties(propertyValuePair.getRight());
+ PropertyData propertyData = new PropertyData();
+ String propertyId = (String) props.get(GraphPropertiesDictionary.PROPERTY_ID.name());
+ propertyData.getPropertyDataDefinition().setUniqueId(propertyId);
+
+ String propertyType = (String) titanGenericDao.getProperty(propertyVertex, GraphPropertiesDictionary.TYPE.getProperty());
+ String propertyValueStr = (String) titanGenericDao.getProperty(propertyVertex, GraphPropertiesDictionary.VALUE.getProperty());
+
+ PropertyValueData propertyValue = buildPropertyValueData((String) props.get(GraphPropertiesDictionary.PROPERTY_NAME.name()), propertyType, propertyValueStr, capabilityInstanceId);
+ TitanVertex createdValue = null;
+ TitanOperationStatus createRelationRes;
+
+ log.trace("Before creating property values node {} {} ", propertyValue.getUniqueId(), onGraph);
+ Either<TitanVertex, TitanOperationStatus> createValueRes = titanGenericDao.createNode(propertyValue);
+ String capabiltyInstId = (String) titanGenericDao.getProperty(capabilityInstanceVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ if (createValueRes.isRight()) {
+ error = createValueRes.right().value();
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to create property value for capability instance {} {} {} {}", capabiltyInstId, ofRI, statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After creating property values node {} {} {} {} ", propertyValue.getUniqueId(), onGraph, statusIs, error);
+ }
+ if (error == null) {
+ createdValue = createValueRes.left().value();
+ log.trace("Before creating relation from capability instance {} {} {}", capabilityInstanceId, toValue, propertyValue.getUniqueId());
+ createRelationRes = titanGenericDao.createEdge(capabilityInstanceVertex, createdValue, GraphEdgeLabels.PROPERTY_VALUE, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to create relation from capability instance {} {} {} {} {}", capabiltyInstId, toValue, propertyValue.getUniqueId(), statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After creating relation from capability instance {} {} {} {} {} ", capabilityInstanceId, toValue, propertyValue.getUniqueId(), statusIs, error);
+ }
+ }
+ if (error == null) {
+ log.trace("Before creating relation from property value {} {} {} ", createdValue, toProperty, propertyData.getUniqueId());
+ createRelationRes = titanGenericDao.createEdge(createdValue, propertyData, GraphEdgeLabels.PROPERTY_IMPL, props);
+ if (!createRelationRes.equals(TitanOperationStatus.OK)) {
+ error = createRelationRes;
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to create relation from property value {} {} {} {} {}", propertyValue.getUniqueId(), toProperty, propertyId, statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Before creating relation from property value c", createdValue, toProperty, propertyData.getUniqueId(), statusIs, error);
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("After cloning property values {} {} {} {} {}", propertyValueID, ofCI, capabilityInstanceId, statusIs, error);
+ }
+ if (error == null) {
+ return TitanOperationStatus.OK;
+ }
+ return error;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityOperation.java
new file mode 100644
index 0000000000..9f00674780
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityOperation.java
@@ -0,0 +1,1196 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.antlr.misc.Graph;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.databind.annotation.JsonAppend.Prop;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@Component("capability-operation")
+public class CapabilityOperation extends AbstractOperation implements ICapabilityOperation {
+
+ public CapabilityOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(CapabilityOperation.class.getName());
+
+ @Autowired
+ private PropertyOperation propertyOperation;
+
+ @Autowired
+ private TitanGenericDao titanGenericDao;
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public Either<CapabilityDefinition, StorageOperationStatus> addCapability(String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition, boolean inTransaction) {
+
+ Either<CapabilityDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<CapabilityData, TitanOperationStatus> addCapStatus = addCapabilityToResource(resourceId, capabilityName, capabilityDefinition);
+
+ if (addCapStatus.isRight()) {
+ log.debug("Failed to add capability {} [ {} ] to graph", capabilityName, capabilityDefinition);
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError("Add Capability", capabilityName, String.valueOf(addCapStatus.right().value()));
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addCapStatus.right().value()));
+ return result;
+ } else {
+ CapabilityData capabilityData = addCapStatus.left().value();
+
+ String capabilityUid = capabilityData.getUniqueId();
+ Either<CapabilityDefinition, StorageOperationStatus> capabilityRes = getCapability(capabilityUid, true);
+ log.debug("After fetching capability {} with uid {}. Status is {}", capabilityName, capabilityUid, capabilityRes);
+
+ if (capabilityRes.isRight()) {
+ StorageOperationStatus status = capabilityRes.right().value();
+ log.debug("Failed to fetch capability {] with uid {}. Status is {}", capabilityName, capabilityUid, status);
+ result = Either.right(status);
+ return result;
+ }
+
+ CapabilityDefinition value = capabilityRes.left().value();
+ log.debug("The returned CapabilityDefinition is {}", value);
+ result = Either.left(value);
+
+ return result;
+ }
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public StorageOperationStatus addCapability(TitanVertex metadataVertex, String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition, boolean inTransaction) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ try {
+
+ TitanOperationStatus addCapStatus = addCapabilityToResource(metadataVertex, resourceId, capabilityName, capabilityDefinition);
+
+ if (!addCapStatus.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to add capability {} [ {} ]", capabilityName, capabilityDefinition);
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError("Add Capability", capabilityName, String.valueOf(addCapStatus));
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(addCapStatus);
+ }
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || !result.equals(TitanOperationStatus.OK)) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ return result;
+ }
+
+ private CapabilityDefinition convertCDataToCDefinition(CapabilityData capabilityData) {
+
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ capabilityDefinition.setType(capabilityData.getType());
+
+ // TODO esofer do something
+
+ return capabilityDefinition;
+ }
+
+ @Override
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String uniqueId) {
+
+ return getCapability(uniqueId, false);
+ }
+
+ public Either<Map<String, CapabilityDefinition>, StorageOperationStatus> getAllCapabilitiesOfResource(String resourceId, boolean recursively, boolean inTransaction) {
+
+ Map<String, CapabilityDefinition> capabilities = new HashMap<>();
+ Either<Map<String, CapabilityDefinition>, StorageOperationStatus> result = null;
+ Set<String> caseInsensitiveCapabilityNames = new HashSet<>();
+
+ try {
+ TitanOperationStatus status = getAllCapabilitiesRecusive(NodeTypeEnum.Resource, resourceId, recursively, capabilities, caseInsensitiveCapabilityNames, inTransaction);
+ if (!status.equals(TitanOperationStatus.OK)) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ result = Either.left(capabilities);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public TitanOperationStatus getAllCapabilitiesRecusive(NodeTypeEnum nodeType, String resourceId, boolean recursively, Map<String, CapabilityDefinition> capabilities, Set<String> caseInsensitiveCapabilityNames, boolean inTransaction) {
+
+ TitanOperationStatus findStatus;
+
+ if (recursively) {
+ findStatus = findAllCapabilitiesRecursively(resourceId, capabilities, caseInsensitiveCapabilityNames);
+
+ } else {
+ findStatus = getCapabilitisOfResourceOnly(resourceId, capabilities, caseInsensitiveCapabilityNames);
+ }
+ if (!findStatus.equals(TitanOperationStatus.OK)) {
+ return findStatus;
+ }
+
+ List<String> derivedFromList = new ArrayList<>();
+ TitanOperationStatus fillResourceDerivedListFromGraph = fillResourceDerivedListFromGraph(resourceId, derivedFromList);
+ if (!fillResourceDerivedListFromGraph.equals(TitanOperationStatus.OK)) {
+ log.debug("fail to find all valid sources of capability. status = {}", fillResourceDerivedListFromGraph.name());
+ return fillResourceDerivedListFromGraph;
+ }
+ capabilities.forEach((name, capability) -> capability.setCapabilitySources(derivedFromList));
+ return TitanOperationStatus.OK;
+ }
+
+ protected TitanOperationStatus findAllCapabilitiesRecursively(String resourceId, Map<String, CapabilityDefinition> capabilities, Set<String> caseInsensitiveCapabilityNames) {
+
+ TitanOperationStatus resourceCapabilitiesStatus = getCapabilitisOfResourceOnly(resourceId, capabilities, caseInsensitiveCapabilityNames);
+
+ if (!resourceCapabilitiesStatus.equals(TitanOperationStatus.OK)) {
+ return resourceCapabilitiesStatus;
+ }
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource,
+ ResourceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (!parentNodesStatus.equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("Failed to find parent capabilities of resource {}. status is {}", resourceId, parentNodesStatus);
+ BeEcompErrorManager.getInstance().logBeFailedFindParentError("Fetch parent capabilities", resourceId, String.valueOf(parentNodesStatus));
+ return parentNodesStatus;
+ }
+ }
+ if (parentNodes.isLeft()) {
+ ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentIntStatus = findAllCapabilitiesRecursively(parentUniqueId, capabilities, caseInsensitiveCapabilityNames);
+
+ if (addParentIntStatus != TitanOperationStatus.OK) {
+ log.debug("Failed to fetch all capabilities of resource {}", parentUniqueId);
+ return addParentIntStatus;
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus getCapabilitisOfResourceOnly(String resourceId, Map<String, CapabilityDefinition> capabilities, Set<String> caseInsensitiveCapabilityNames) {
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> allCapabilitiesRes = getAllCapabilitiesPairs(resourceId);
+ if (allCapabilitiesRes.isRight()) {
+ TitanOperationStatus status = allCapabilitiesRes.right().value();
+ log.debug("After fetching all capabilities of resource {}. status is {}", resourceId, status);
+ if (status.equals(TitanOperationStatus.NOT_FOUND)) {
+ status = TitanOperationStatus.OK;
+ }
+ return status;
+ }
+
+ List<ImmutablePair<CapabilityData, GraphEdge>> capabilityPairs = allCapabilitiesRes.left().value();
+
+ if (capabilityPairs != null) {
+ for (ImmutablePair<CapabilityData, GraphEdge> capabilityPair : capabilityPairs) {
+ CapabilityData capabilityData = capabilityPair.getKey();
+ GraphEdge graphEdge = capabilityPair.getValue();
+ Map<String, Object> edgeProps = graphEdge.getProperties();
+ if (edgeProps != null) {
+ String capabilityName = (String) edgeProps.get(GraphPropertiesDictionary.NAME.getProperty());
+ if (capabilityName == null) {
+ log.error("Capability name was not found for capability {}", capabilityData.getUniqueId());
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+ Either<CapabilityDefinition, TitanOperationStatus> capabilityDefRes = getCapabilityByCapabilityData(capabilityData);
+ if (capabilityDefRes.isRight()) {
+ TitanOperationStatus status = capabilityDefRes.right().value();
+ return status;
+ }
+ CapabilityDefinition capabilityDefinition = capabilityDefRes.left().value();
+ capabilityDefinition.setOwnerId(resourceId);
+ log.debug("Before adding capability {} with definition {} to result.", capabilityName, capabilityDefinition);
+ // US631462
+ if (caseInsensitiveCapabilityNames.contains(capabilityName.toLowerCase())) {
+ log.debug("The capability {} was already defined in derived resource (case insensitive). Ignore {} from resource {}", capabilityName, capabilityName, resourceId);
+ } else {
+ capabilities.put(capabilityName, capabilityDefinition);
+ caseInsensitiveCapabilityNames.add(capabilityName.toLowerCase());
+ }
+ } else {
+ log.debug("Capability name was not found for capability {}", capabilityData.getUniqueId());
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", capabilityData.getUniqueId(), String.valueOf(TitanOperationStatus.INVALID_ELEMENT));
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ @Override
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String uniqueId, boolean inTransaction) {
+
+ Either<CapabilityDefinition, StorageOperationStatus> result = null;
+
+ try {
+ Either<CapabilityData, TitanOperationStatus> capabiltyRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), uniqueId, CapabilityData.class);
+ if (capabiltyRes.isRight()) {
+ TitanOperationStatus status = capabiltyRes.right().value();
+ log.debug("Failed to retrieve capability {} from graph. Status is {}", uniqueId, status);
+
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", uniqueId, String.valueOf(status));
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ CapabilityData capabilityData = capabiltyRes.left().value();
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ capabilityDefinition.setDescription(capabilityData.getDescription());
+ capabilityDefinition.setUniqueId(capabilityData.getUniqueId());
+ capabilityDefinition.setValidSourceTypes(capabilityData.getValidSourceTypes());
+ capabilityDefinition.setMinOccurrences(capabilityData.getMinOccurrences());
+ capabilityDefinition.setMaxOccurrences(capabilityData.getMaxOccurrences());
+
+ Either<CapabilityTypeData, TitanOperationStatus> capabilityTypeRes = getCapabilityTypeOfCapability(uniqueId);
+ if (capabilityTypeRes.isRight()) {
+ TitanOperationStatus status = capabilityTypeRes.right().value();
+ log.debug("Failed to retrieve capability type of capability {}. Status is {}", uniqueId, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", uniqueId, String.valueOf(status));
+
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ CapabilityTypeData capabilityTypeData = capabilityTypeRes.left().value();
+ capabilityDefinition.setType(capabilityTypeData.getCapabilityTypeDataDefinition().getType());
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), uniqueId, GraphEdgeLabels.CAPABILITY,
+ NodeTypeEnum.Resource, ResourceMetadataData.class);
+ if (parentNode.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(parentNode.right().value()));
+ } else {
+ ImmutablePair<ResourceMetadataData, GraphEdge> pair = parentNode.left().value();
+ capabilityDefinition.setOwnerId(pair.left.getMetadataDataDefinition().getUniqueId());
+ List<String> derivedFromList = new ArrayList<>();
+ // derivedFromList.add(pair.left.getMetadataDataDefinition().getName());
+ TitanOperationStatus fillResourceDerivedListFromGraph = fillResourceDerivedListFromGraph(pair.left.getMetadataDataDefinition().getUniqueId(), derivedFromList);
+ if (fillResourceDerivedListFromGraph.equals(TitanOperationStatus.OK)) {
+ capabilityDefinition.setCapabilitySources(derivedFromList);
+ }
+ }
+
+ Either<List<PropertyDefinition>, TitanOperationStatus> getPropertiesRes = getPropertiesOfCapability(uniqueId, capabilityTypeData.getCapabilityTypeDataDefinition().getType());
+ if (getPropertiesRes.isRight() && !getPropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus status = getPropertiesRes.right().value();
+ log.debug("Failed to retrieve properties of capability {}. Status is {}", uniqueId, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Properties of Capability", uniqueId, String.valueOf(status));
+
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ if (getPropertiesRes.isLeft()) {
+ List<ComponentInstanceProperty> properties = new ArrayList<>();
+ for (PropertyDefinition property : getPropertiesRes.left().value()) {
+ properties.add(new ComponentInstanceProperty(property, null, null));
+ }
+ capabilityDefinition.setProperties(properties);
+ }
+ result = Either.left(capabilityDefinition);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private TitanOperationStatus fillResourceDerivedListFromGraph(String uniqueId, List<String> derivedFromList) {
+
+ Either<ResourceMetadataData, TitanOperationStatus> resourceNode = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), uniqueId, ResourceMetadataData.class);
+
+ if (resourceNode.isRight()) {
+ TitanOperationStatus parentNodesStatus = resourceNode.right().value();
+ if (!parentNodesStatus.equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("Failed to find resource {} . status is {}", uniqueId, parentNodesStatus);
+ return parentNodesStatus;
+ }
+ }
+
+ derivedFromList.add(((ResourceMetadataDataDefinition) resourceNode.left().value().getMetadataDataDefinition()).getToscaResourceName());
+ Either<List<ImmutablePair<ResourceMetadataData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), uniqueId, GraphEdgeLabels.DERIVED_FROM,
+ NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ if (childrenNodes.isRight() && (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND)) {
+ return childrenNodes.right().value();
+ } else if (childrenNodes.isLeft()) {
+
+ List<ImmutablePair<ResourceMetadataData, GraphEdge>> pairList = childrenNodes.left().value();
+ for (ImmutablePair<ResourceMetadataData, GraphEdge> pair : pairList) {
+ return fillResourceDerivedListFromGraph(pair.left.getMetadataDataDefinition().getUniqueId(), derivedFromList);
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ public Either<CapabilityDefinition, TitanOperationStatus> getCapabilityByCapabilityData(CapabilityData capabilityData) {
+
+ Either<CapabilityDefinition, TitanOperationStatus> result;
+
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ capabilityDefinition.setDescription(capabilityData.getDescription());
+ capabilityDefinition.setUniqueId(capabilityData.getUniqueId());
+ capabilityDefinition.setValidSourceTypes(capabilityData.getValidSourceTypes());
+ capabilityDefinition.setMinOccurrences(capabilityData.getMinOccurrences());
+ capabilityDefinition.setMaxOccurrences(capabilityData.getMaxOccurrences());
+
+ String capabilityUid = capabilityData.getUniqueId();
+ Either<CapabilityTypeData, TitanOperationStatus> capabilityTypeRes = getCapabilityTypeOfCapability(capabilityUid);
+ if (capabilityTypeRes.isRight()) {
+ TitanOperationStatus status = capabilityTypeRes.right().value();
+ log.debug("Failed to retrieve capability type of capability {} . status is {}", capabilityUid, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", capabilityUid, String.valueOf(status));
+
+ return Either.right(status);
+ }
+
+ CapabilityTypeData capabilityTypeData = capabilityTypeRes.left().value();
+ capabilityDefinition.setType(capabilityTypeData.getCapabilityTypeDataDefinition().getType());
+
+ Either<List<PropertyDefinition>, TitanOperationStatus> capabilityPropertiesRes = getPropertiesOfCapability(capabilityUid, capabilityDefinition.getType());
+ if (capabilityPropertiesRes.isRight() && !capabilityPropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus status = capabilityPropertiesRes.right().value();
+ log.debug("Failed to retrieve properties of capability {} . status is {}", capabilityUid, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", capabilityUid, String.valueOf(status));
+
+ result = Either.right(status);
+ return result;
+ }
+ if (capabilityPropertiesRes.isLeft()) {
+ List<ComponentInstanceProperty> properties = new ArrayList<>();
+ for (PropertyDefinition property : capabilityPropertiesRes.left().value()) {
+ properties.add(new ComponentInstanceProperty(property, null, null));
+ }
+ capabilityDefinition.setProperties(properties);
+ }
+ result = Either.left(capabilityDefinition);
+ return result;
+ }
+
+ public Either<CapabilityDefinition, TitanOperationStatus> getCapabilityByCapabilityData(TitanVertex capabilityDataVertex) {
+
+ Either<CapabilityDefinition, TitanOperationStatus> result;
+
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ Map<String, Object> props = titanGenericDao.getProperties(capabilityDataVertex);
+ CapabilityData capabilityData = GraphElementFactory.createElement((String) props.get(GraphPropertiesDictionary.LABEL.getProperty()), GraphElementTypeEnum.Node, props, CapabilityData.class);
+ capabilityDefinition.setDescription(capabilityData.getDescription());
+ capabilityDefinition.setUniqueId(capabilityData.getUniqueId());
+ capabilityDefinition.setValidSourceTypes(capabilityData.getValidSourceTypes());
+ capabilityDefinition.setMinOccurrences(capabilityData.getMinOccurrences());
+ capabilityDefinition.setMaxOccurrences(capabilityData.getMaxOccurrences());
+
+ String capabilityUid = capabilityData.getUniqueId();
+ Either<CapabilityTypeData, TitanOperationStatus> capabilityTypeRes = getCapabilityTypeOfCapability(capabilityUid);
+ if (capabilityTypeRes.isRight()) {
+ TitanOperationStatus status = capabilityTypeRes.right().value();
+ log.debug("Failed to retrieve capability type of capability {} . status is {}", capabilityUid, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", capabilityUid, String.valueOf(status));
+
+ return Either.right(status);
+ }
+
+ CapabilityTypeData capabilityTypeData = capabilityTypeRes.left().value();
+ capabilityDefinition.setType(capabilityTypeData.getCapabilityTypeDataDefinition().getType());
+
+ Either<List<PropertyDefinition>, TitanOperationStatus> capabilityPropertiesRes = getPropertiesOfCapability(capabilityUid, capabilityDefinition.getType());
+ if (capabilityPropertiesRes.isRight() && !capabilityPropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus status = capabilityPropertiesRes.right().value();
+ log.debug("Failed to retrieve properties of capability {} . status is {}", capabilityUid, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Capability", capabilityUid, String.valueOf(status));
+
+ result = Either.right(status);
+ return result;
+ }
+ if (capabilityPropertiesRes.isLeft()) {
+ List<ComponentInstanceProperty> properties = new ArrayList<>();
+ for (PropertyDefinition property : capabilityPropertiesRes.left().value()) {
+ properties.add(new ComponentInstanceProperty(property, null, null));
+ }
+ capabilityDefinition.setProperties(properties);
+ }
+ result = Either.left(capabilityDefinition);
+ return result;
+ }
+
+ public Either<List<PropertyDefinition>, TitanOperationStatus> getPropertiesOfCapability(String capabilityUid, String capabilityType) {
+ log.debug("Before getting properties of capability {} from graph ", capabilityUid);
+
+ List<PropertyDefinition> properties;
+ Either<List<PropertyDefinition>, TitanOperationStatus> result = null;
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> getPropertiesOfCapabilityTypeRes = null;
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> getPropertiesOfCapabilityRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capabilityUid, GraphEdgeLabels.PROPERTY,
+ NodeTypeEnum.Property, PropertyData.class);
+ if (getPropertiesOfCapabilityRes.isRight() && !getPropertiesOfCapabilityRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus status = getPropertiesOfCapabilityRes.right().value();
+ log.debug("failed to get properties of capability with id {}. status={}", capabilityUid, status);
+ result = Either.right(status);
+ }
+ if (result == null) {
+ String capabilityTypeUid = UniqueIdBuilder.buildCapabilityTypeUid(capabilityType);
+ getPropertiesOfCapabilityTypeRes = getAllCapabilityTypePropertiesFromAllDerivedFrom(capabilityTypeUid);
+ if (getPropertiesOfCapabilityTypeRes.isRight() && !getPropertiesOfCapabilityTypeRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus status = getPropertiesOfCapabilityTypeRes.right().value();
+ log.error("Failed to retrieve properties for capability type {} from graph. Status is {}", capabilityType, status);
+ result = Either.right(status);
+ }
+ }
+ if (result == null) {
+ result = getPropertiesOfCapabilityTypeRes.isRight()
+ ? (getPropertiesOfCapabilityRes.isRight() ? Either.right(TitanOperationStatus.NOT_FOUND)
+ : Either.left(getPropertiesOfCapabilityRes.left().value().stream().map(p -> propertyOperation.convertPropertyDataToPropertyDefinition(p.getKey(), null, capabilityUid)).collect(Collectors.toList())))
+ : (getPropertiesOfCapabilityRes.isRight() ? Either.left(getPropertiesOfCapabilityTypeRes.left().value().values().stream().collect(Collectors.toList())) : null);
+ }
+ if (result == null) {
+ Map<String, PropertyDefinition> propertiesOfCapabilityType = getPropertiesOfCapabilityTypeRes.left().value();
+ properties = getPropertiesOfCapabilityRes.left().value().stream()
+ .map(p -> propertyOperation.convertPropertyDataToPropertyDefinition(p.getKey(), (String) p.getRight().getProperties().get(GraphPropertiesDictionary.NAME.getProperty()), capabilityUid)).collect(Collectors.toList());
+ properties.stream().forEach(p -> propertiesOfCapabilityType.remove(p.getName()));
+ properties.addAll(propertiesOfCapabilityType.values());
+ result = Either.left(properties);
+ }
+ return result;
+ }
+
+ protected Either<CapabilityTypeData, TitanOperationStatus> getCapabilityTypeOfCapability(String uniqueId) {
+
+ Either<ImmutablePair<CapabilityTypeData, GraphEdge>, TitanOperationStatus> capabilityTypeRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), uniqueId, GraphEdgeLabels.TYPE_OF, NodeTypeEnum.CapabilityType,
+ CapabilityTypeData.class);
+
+ if (capabilityTypeRes.isRight()) {
+ TitanOperationStatus status = capabilityTypeRes.right().value();//
+ log.debug("Cannot find capability type associated with capability {}. Status is {}", uniqueId, status);
+ BeEcompErrorManager.getInstance().logBeFailedFindAssociationError("Fetch Capability type", NodeTypeEnum.CapabilityType.getName(), uniqueId, String.valueOf(status));
+ return Either.right(capabilityTypeRes.right().value());
+ }
+
+ CapabilityTypeData capabilityTypeData = capabilityTypeRes.left().value().getKey();
+
+ return Either.left(capabilityTypeData);
+
+ }
+
+ @Override
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String capabilityName, String resourceId) {
+ return getCapability(UniqueIdBuilder.buildCapabilityUid(resourceId, capabilityName));
+ }
+
+ @Override
+ public Either<CapabilityDefinition, StorageOperationStatus> getCapability(String capabilityName, String resourceId, boolean inTransaction) {
+ return getCapability(UniqueIdBuilder.buildCapabilityUid(resourceId, capabilityName), inTransaction);
+ }
+
+ @Override
+ public Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> getAllCapabilitiesPairs(String resourceId) {
+
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> capabilitiesNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.CAPABILITY,
+ NodeTypeEnum.Capability, CapabilityData.class);
+
+ log.debug("After looking for all capabilities under resource {}. Status is {}", resourceId, capabilitiesNodes);
+ if (capabilitiesNodes.isRight()) {
+ TitanOperationStatus status = capabilitiesNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<CapabilityData, GraphEdge>> capabilities = capabilitiesNodes.left().value();
+ if (capabilities == null || true == capabilities.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(capabilitiesNodes.left().value());
+ }
+
+ private Either<CapabilityData, TitanOperationStatus> addCapabilityToResource(String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition) {
+
+ log.debug("Going to add capability {} [ {} ] to resource uid {}", capabilityName, capabilityDefinition, resourceId);
+
+ Either<CapabilityData, TitanOperationStatus> createCapRes = createCapability(resourceId, capabilityName, capabilityDefinition);
+
+ log.debug("After creating capability node in graph. status is {}", createCapRes);
+ if (createCapRes.isRight()) {
+ TitanOperationStatus status = createCapRes.right().value();
+ log.error("Failed to create capability data node in graph. status is {}", status);
+ return Either.right(status);
+ }
+ CapabilityData capabilityData = createCapRes.left().value();
+
+ String capabilityType = capabilityDefinition.getType();
+
+ log.debug("Going to associate capability {} to its capabilityType {}", capabilityName, capabilityType);
+
+ Either<GraphRelation, TitanOperationStatus> associateCapabilityTypeRes = associateCapabilityToCapabilityType(capabilityData, capabilityType);
+ log.debug("After associating capability {} to its capabilityType {}. status is {}", capabilityName, capabilityType, associateCapabilityTypeRes);
+ if (associateCapabilityTypeRes.isRight()) {
+ TitanOperationStatus status = associateCapabilityTypeRes.right().value();
+ log.error("Failed to associate capability {} to its capabilityType {} in graph. status is {} ", capabilityName, capabilityType, status);
+
+ return Either.right(status);
+ }
+ List<ComponentInstanceProperty> ciProperties = capabilityDefinition.getProperties();
+ if (ciProperties != null && !ciProperties.isEmpty()) {
+ List<PropertyDefinition> properties = ciProperties.stream().map(prop -> new PropertyDefinition(prop)).collect(Collectors.toList());
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesRes = addPropertiesToCapability(capabilityData, capabilityType, properties);
+ if (addPropertiesRes.isRight()) {
+ TitanOperationStatus operationStatus = addPropertiesRes.right().value();
+ return Either.right(operationStatus);
+ }
+ }
+
+ Either<GraphRelation, TitanOperationStatus> associateResourceRes = associateResourceToCapability(resourceId, capabilityName, capabilityData);
+ if (associateResourceRes.isRight()) {
+ TitanOperationStatus status = associateResourceRes.right().value();
+ log.error("Failed to associate resource " + resourceId + " to capability " + capabilityData + ". status is " + status);
+ return Either.right(status);
+ }
+
+ return Either.left(capabilityData);
+
+ }
+
+ private TitanOperationStatus addCapabilityToResource(TitanVertex metadataVertex, String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition) {
+
+ log.debug("Going to add capability {} [ {} ] to resource uid {}", capabilityName, capabilityDefinition, resourceId);
+
+ Either<TitanVertex, TitanOperationStatus> createCapRes = createCapabilityVertex(resourceId, capabilityName, capabilityDefinition);
+
+ log.debug("After creating capability node in graph. status is {}", createCapRes);
+ if (createCapRes.isRight()) {
+ TitanOperationStatus status = createCapRes.right().value();
+ log.error("Failed to create capability data node in graph. status is {}", status);
+ return status;
+ }
+ TitanVertex capabilityVertex = createCapRes.left().value();
+
+ String capabilityType = capabilityDefinition.getType();
+
+ log.debug("Going to associate capability {} to its capabilityType {}", capabilityName, capabilityType);
+
+ TitanOperationStatus associateCapabilityTypeRes = associateCapabilityToCapabilityType(capabilityVertex, capabilityType);
+ log.debug("After associating capability {} to its capabilityType {}. status is {}", capabilityName, capabilityType, associateCapabilityTypeRes);
+ if (!associateCapabilityTypeRes.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate capability {} to its capabilityType {} in graph. status is {} ", capabilityName, capabilityType, associateCapabilityTypeRes);
+ return associateCapabilityTypeRes;
+ }
+ List<ComponentInstanceProperty> ciProperties = capabilityDefinition.getProperties();
+ if (ciProperties != null && !ciProperties.isEmpty()) {
+ String capabiltyId = (String) titanGenericDao.getProperty(capabilityVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ List<PropertyDefinition> properties = ciProperties.stream().map(prop -> new PropertyDefinition(prop)).collect(Collectors.toList());
+ TitanOperationStatus addPropertiesRes = addPropertiesToCapability(capabilityVertex, capabilityType, properties, capabiltyId);
+ if (!addPropertiesRes.equals(TitanOperationStatus.OK)) {
+ return addPropertiesRes;
+ }
+ }
+
+ TitanOperationStatus associateResourceRes = associateResourceToCapability(resourceId, capabilityName, capabilityVertex, metadataVertex);
+ if (!associateResourceRes.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource{} to capability {}. status is {} ", resourceId, capabilityName, associateResourceRes);
+ }
+
+ return associateResourceRes;
+
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateCapabilityToCapabilityType(CapabilityData capabilityData, String capabilityType) {
+ UniqueIdData capabilityTypeIdData = new UniqueIdData(NodeTypeEnum.CapabilityType, UniqueIdBuilder.buildCapabilityTypeUid(capabilityType));
+ log.debug("Before associating {} to capability type {}.", capabilityData, capabilityType);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(capabilityData, capabilityTypeIdData, GraphEdgeLabels.TYPE_OF, null);
+ log.debug("After associating {} to capability type {}. status is {}", capabilityData, capabilityType, createRelResult);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.error("Failed to associate capability {} to capability type {} in graph. Status is {}",capabilityData, capabilityTypeIdData, operationStatus);
+ return Either.right(operationStatus);
+ }
+ return Either.left(createRelResult.left().value());
+
+ }
+
+ private TitanOperationStatus associateCapabilityToCapabilityType(TitanVertex capabilityVertex, String capabilityType) {
+
+ UniqueIdData capabilityTypeIdData = new UniqueIdData(NodeTypeEnum.CapabilityType, UniqueIdBuilder.buildCapabilityTypeUid(capabilityType));
+
+ log.debug("Before associating {} to capability type {}.", capabilityVertex, capabilityType);
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(capabilityVertex, capabilityTypeIdData, GraphEdgeLabels.TYPE_OF, null);
+ log.trace("After associating {} to capability type {}. status is {}", capabilityVertex, capabilityType, createRelResult);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate capability {} to capability type {} in graph. status is {}", capabilityVertex, capabilityTypeIdData, createRelResult);
+ }
+ return createRelResult;
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateResourceToCapability(String resourceId, String capabilityName, CapabilityData capabilityData) {
+
+ UniqueIdData resourceIdData = new UniqueIdData(NodeTypeEnum.Resource, resourceId);
+
+ log.debug("Before associating resource {} to capability {}.", resourceId, capabilityData);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), capabilityName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(resourceIdData, capabilityData, GraphEdgeLabels.CAPABILITY, props);
+ log.debug("After associating resource {} to capability {}. Status is {}", resourceId, capabilityData, createRelResult);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.error("Failed to associate resource {} to capability {} in graph. Status is {}", resourceId, capabilityData, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createRelResult.left().value());
+
+ }
+
+ private TitanOperationStatus associateResourceToCapability(String resourceId, String capabilityName, TitanVertex capabilityVertex, TitanVertex resourceVertex) {
+
+ log.debug("Before associating resource {} to capability {}.", resourceId, capabilityName);
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), capabilityName);
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(resourceVertex, capabilityVertex, GraphEdgeLabels.CAPABILITY, props);
+ log.debug("After associating resource {} to capability {}. status is {}", resourceId, capabilityName, createRelResult);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource {} to capability {} in graph. status is {}", resourceId, capabilityName, createRelResult);
+ }
+
+ return createRelResult;
+
+ }
+
+ /**
+ *
+ * create capability node in the graph
+ *
+ * @param resourceId
+ * @param capabilityName
+ * @param capabilityDefinition
+ * @return
+ */
+ private Either<CapabilityData, TitanOperationStatus> createCapability(String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition) {
+
+ CapabilityData capabilityData = new CapabilityData();
+ String uid = UniqueIdBuilder.buildCapabilityUid(resourceId, capabilityName);
+ capabilityData.setUniqueId(uid);
+ Long creationTime = System.currentTimeMillis();
+ capabilityData.setCreationTime(creationTime);
+ capabilityData.setModificationTime(creationTime);
+ capabilityData.setValidSourceTypes(capabilityDefinition.getValidSourceTypes());
+ capabilityData.setMinOccurrences(capabilityDefinition.getMinOccurrences());
+ capabilityData.setMaxOccurrences(capabilityDefinition.getMaxOccurrences());
+ capabilityData.setDescription(capabilityDefinition.getDescription());
+
+ Either<CapabilityData, TitanOperationStatus> createNode = titanGenericDao.createNode(capabilityData, CapabilityData.class);
+
+ log.debug("After creating capability node in the graph. status is {}", createNode);
+
+ return createNode;
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> createCapabilityVertex(String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition) {
+
+ CapabilityData capabilityData = new CapabilityData();
+ String uid = UniqueIdBuilder.buildCapabilityUid(resourceId, capabilityName);
+ capabilityData.setUniqueId(uid);
+ Long creationTime = System.currentTimeMillis();
+ capabilityData.setCreationTime(creationTime);
+ capabilityData.setModificationTime(creationTime);
+ capabilityData.setValidSourceTypes(capabilityDefinition.getValidSourceTypes());
+ capabilityData.setMinOccurrences(capabilityDefinition.getMinOccurrences());
+ capabilityData.setMaxOccurrences(capabilityDefinition.getMaxOccurrences());
+ capabilityData.setDescription(capabilityDefinition.getDescription());
+
+ Either<TitanVertex, TitanOperationStatus> createNode = titanGenericDao.createNode(capabilityData);
+
+ log.debug("After creating capability node in the graph. status is {}", createNode);
+
+ return createNode;
+ }
+
+ @Override
+ public Either<CapabilityDefinition, StorageOperationStatus> addCapability(String resourceId, String capabilityName, CapabilityDefinition capabilityDefinition) {
+
+ return addCapability(resourceId, capabilityName, capabilityDefinition, false);
+
+ }
+
+ public StorageOperationStatus deleteCapabilityFromGraph(String capabilityUid) {
+
+ TitanOperationStatus resultStatus = null;
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deletePropertiesStatus = deletePropertiesOfCapability(capabilityUid);
+
+ if (deletePropertiesStatus.isRight() && !deletePropertiesStatus.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ resultStatus = deletePropertiesStatus.right().value();
+ }
+ if (resultStatus == null) {
+ log.debug("Before deleting capability from graph {}", capabilityUid);
+ Either<CapabilityData, TitanOperationStatus> deleteNodeStatus = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capabilityUid, CapabilityData.class);
+ if (deleteNodeStatus.isRight()) {
+ resultStatus = deleteNodeStatus.right().value();
+ }
+ }
+ if (resultStatus != null) {
+ log.debug("failed to delete capability with id {}. status={}", capabilityUid, resultStatus);
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError("Delete capability", capabilityUid, String.valueOf(resultStatus));
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(resultStatus);
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ public Either<Map<String, CapabilityDefinition>, StorageOperationStatus> deleteAllCapabilities(String resourceId, boolean inTransaction) {
+
+ Either<Map<String, CapabilityDefinition>, StorageOperationStatus> result = null;
+ try {
+
+ Either<Map<String, CapabilityDefinition>, TitanOperationStatus> deleteAllRes = deleteAllCapabilitiesOfResource(resourceId);
+ if (deleteAllRes.isRight()) {
+ TitanOperationStatus status = deleteAllRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to delete capabilities of resource {}. Status is {}", resourceId, status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ Map<String, CapabilityDefinition> value = deleteAllRes.left().value();
+ result = Either.left(value);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ public Either<Map<String, CapabilityDefinition>, StorageOperationStatus> deleteAllCapabilities(String resourceId) {
+
+ return deleteAllCapabilities(resourceId, false);
+
+ }
+
+ private Either<Map<String, CapabilityDefinition>, TitanOperationStatus> deleteAllCapabilitiesOfResource(String resourceId) {
+ TitanOperationStatus resultStatus = null;
+ Map<String, CapabilityDefinition> capabilities = new HashMap<>();
+ Set<String> caseInsensitiveCapabilityNames = new HashSet<>();
+ TitanOperationStatus capabilitisRes = getCapabilitisOfResourceOnly(resourceId, capabilities, caseInsensitiveCapabilityNames);
+ if (capabilitisRes != TitanOperationStatus.OK) {
+ return Either.right(capabilitisRes);
+ }
+
+ if (capabilities.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ for (Entry<String, CapabilityDefinition> entry : capabilities.entrySet()) {
+ CapabilityDefinition capabilityDefinition = entry.getValue();
+ String capabilityUid = capabilityDefinition.getUniqueId();
+
+ log.debug("Before deleting properties of capability {} from graph", capabilityUid);
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deletePropertiesStatus = deletePropertiesOfCapability(capabilityUid);
+ if (deletePropertiesStatus.isRight() && !deletePropertiesStatus.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ resultStatus = deletePropertiesStatus.right().value();
+ }
+ if (resultStatus == null) {
+ Either<CapabilityData, TitanOperationStatus> deleteNodeRes = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capabilityUid, CapabilityData.class);
+ if (deleteNodeRes.isRight()) {
+ resultStatus = deleteNodeRes.right().value();
+ }
+ }
+ if (resultStatus != null) {
+ log.debug("Failed to delete capability {} of resource {}", capabilityUid, resourceId);
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError("Delete capability", capabilityUid, String.valueOf(resultStatus));
+ return Either.right(resultStatus);
+ }
+ }
+
+ return Either.left(capabilities);
+
+ }
+
+ public Map<String, List<CapabilityDefinition>> convertCapabilityMap(Map<String, CapabilityDefinition> capabilityMap, String ownerId, String ownerName) {
+
+ Map<String, List<CapabilityDefinition>> typeToRequirementMap = new HashMap<>();
+ capabilityMap.forEach((capabilityName, capability) -> {
+ capability.setName(capabilityName);
+ if (typeToRequirementMap.containsKey(capability.getType())) {
+ typeToRequirementMap.get(capability.getType()).add(capability);
+ } else {
+ List<CapabilityDefinition> list = new ArrayList<>();
+ list.add(capability);
+ typeToRequirementMap.put(capability.getType(), list);
+ }
+ });
+ return typeToRequirementMap;
+ }
+
+ public TitanOperationStatus getCapabilitySourcesList(String resourceId, List<String> derivedFromList) {
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> getResponse = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesToMatch, ResourceMetadataData.class);
+ if (getResponse.isRight()) {
+ return getResponse.right().value();
+ } else {
+ String toscaResourceName = ((ResourceMetadataDataDefinition) getResponse.left().value().get(0).getMetadataDataDefinition()).getToscaResourceName();
+ derivedFromList.add(toscaResourceName);
+ }
+
+ Either<List<ImmutablePair<ResourceMetadataData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.DERIVED_FROM,
+ NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ while (childrenNodes.isLeft()) {
+
+ List<ImmutablePair<ResourceMetadataData, GraphEdge>> pairList = childrenNodes.left().value();
+ ResourceMetadataData left = pairList.get(0).left;
+ derivedFromList.add(((ResourceMetadataDataDefinition) left.getMetadataDataDefinition()).getToscaResourceName());
+ String id = left.getMetadataDataDefinition().getUniqueId();
+ childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), id, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource, ResourceMetadataData.class);
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapability(CapabilityData capabilityData, String capabilityType, List<PropertyDefinition> properties) {
+ String capabilityTypeUid = UniqueIdBuilder.buildCapabilityTypeUid(capabilityType);
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> allPropertiesOfCapabilityTypeRes = getAllCapabilityTypePropertiesFromAllDerivedFrom(capabilityTypeUid);
+ if (allPropertiesOfCapabilityTypeRes.isRight() && !allPropertiesOfCapabilityTypeRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus operationStatus = allPropertiesOfCapabilityTypeRes.right().value();
+ log.error("Failed to retrieve properties for capability type " + capabilityType + " from graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, PropertyDefinition> propertiesOfCapabilityType = null;
+
+ if (allPropertiesOfCapabilityTypeRes.isLeft() && allPropertiesOfCapabilityTypeRes.left() != null && !allPropertiesOfCapabilityTypeRes.left().value().isEmpty()) {
+
+ propertiesOfCapabilityType = allPropertiesOfCapabilityTypeRes.left().value();
+ Either<List<PropertyDefinition>, TitanOperationStatus> validateAndReducePropertiesRes = validatePropertyUniqueness(propertiesOfCapabilityType, properties);
+ if (validateAndReducePropertiesRes.isRight()) {
+ TitanOperationStatus operationStatus = validateAndReducePropertiesRes.right().value();
+ log.error("Failed to add properties to capability {} in graph. Status is {}", capabilityData.getUniqueId(), operationStatus);
+ return Either.right(operationStatus);
+ }
+ }
+
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapabilityRes = propertyOperation.addPropertiesToElementType(capabilityData.getUniqueId(), NodeTypeEnum.Capability, properties);
+ if (addPropertiesToCapabilityRes.isRight()) {
+ TitanOperationStatus operationStatus = addPropertiesToCapabilityRes.right().value();
+ log.error("Failed to add properties to capability {} in graph. Status is {}", capabilityData.getUniqueId(), operationStatus);
+ return Either.right(operationStatus);
+ }
+ return Either.left(addPropertiesToCapabilityRes.left().value());
+ }
+
+ private TitanOperationStatus addPropertiesToCapability(TitanVertex capabilityVertex, String capabilityType, List<PropertyDefinition> properties, String uniqueId) {
+ String capabilityTypeUid = UniqueIdBuilder.buildCapabilityTypeUid(capabilityType);
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> allPropertiesOfCapabilityTypeRes = getAllCapabilityTypePropertiesFromAllDerivedFrom(capabilityTypeUid);
+ if (allPropertiesOfCapabilityTypeRes.isRight() && !allPropertiesOfCapabilityTypeRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ TitanOperationStatus operationStatus = allPropertiesOfCapabilityTypeRes.right().value();
+ log.error("Failed to retrieve properties for capability type {} from graph. status is {}", capabilityType, operationStatus);
+ return operationStatus;
+ }
+ Map<String, PropertyDefinition> propertiesOfCapabilityType = null;
+
+ if (allPropertiesOfCapabilityTypeRes.isLeft() && allPropertiesOfCapabilityTypeRes.left() != null && !allPropertiesOfCapabilityTypeRes.left().value().isEmpty()) {
+
+ propertiesOfCapabilityType = allPropertiesOfCapabilityTypeRes.left().value();
+ Either<List<PropertyDefinition>, TitanOperationStatus> validateAndReducePropertiesRes = validatePropertyUniqueness(propertiesOfCapabilityType, properties);
+ if (validateAndReducePropertiesRes.isRight()) {
+ TitanOperationStatus operationStatus = validateAndReducePropertiesRes.right().value();
+ log.error("Failed to add properties to capability {} in graph. status is {}", capabilityVertex, operationStatus);
+ return operationStatus;
+ }
+ }
+
+ String capabiltyId = (String) titanGenericDao.getProperty(capabilityVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ TitanOperationStatus addPropertiesToCapabilityRes = propertyOperation.addPropertiesToElementType(capabilityVertex, capabiltyId, NodeTypeEnum.Capability, properties);
+ if (!addPropertiesToCapabilityRes.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to add properties to capability {} in graph. status is {}", capabiltyId, addPropertiesToCapabilityRes);
+ }
+ return addPropertiesToCapabilityRes;
+ }
+
+ public Either<List<PropertyDefinition>, TitanOperationStatus> validatePropertyUniqueness(Map<String, PropertyDefinition> propertiesOfCapabilityType, List<PropertyDefinition> properties) {
+ Either<List<PropertyDefinition>, TitanOperationStatus> result = Either.left(properties);
+
+ for (PropertyDefinition property : properties) {
+ String propertyName = property.getName();
+ String propertyType = property.getType();
+ PropertyDefinition defaultProperty = null;
+
+ if (propertiesOfCapabilityType.containsKey(propertyName)) {
+ defaultProperty = propertiesOfCapabilityType.get(propertyName);
+ if (propertyType != null && defaultProperty.getType() != null && !defaultProperty.getType().equals(propertyType)) {
+ log.error(" Property with name {} and different type already exists.", propertyName);
+ result = Either.right(TitanOperationStatus.PROPERTY_NAME_ALREADY_EXISTS);
+ } else {
+ property.setType(defaultProperty.getType());
+ String innerType = defaultProperty.getSchema() == null ? null : defaultProperty.getSchema().getProperty() == null ? null : defaultProperty.getSchema().getProperty().getType();
+
+ if (property.getSchema() != null && property.getSchema().getProperty() != null) {
+ property.getSchema().getProperty().setType(innerType);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public StorageOperationStatus validateUpdateCapabilityProperty(PropertyDefinition property) {
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + status, ErrorSeverity.ERROR);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(allDataTypes.right().value());
+ }
+ return propertyOperation.validateAndUpdateProperty(property, allDataTypes.left().value());
+ }
+
+ public StorageOperationStatus validateCapabilityProperties(List<PropertyDefinition> properties) {
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ for (PropertyDefinition property : properties) {
+ result = validateUpdateCapabilityProperty(property);
+ if (!result.equals(StorageOperationStatus.OK))
+ break;
+ }
+ return result;
+ }
+
+ public Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deletePropertiesOfCapability(String capabilityUid) {
+ log.debug("Before deleting properties of capability {} from graph ", capabilityUid);
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deletePropertiesStatus = titanGenericDao.deleteChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capabilityUid, GraphEdgeLabels.PROPERTY,
+ NodeTypeEnum.Property, PropertyData.class);
+ if (deletePropertiesStatus.isRight()) {
+ TitanOperationStatus status = deletePropertiesStatus.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.debug("failed to delete properties of capability with id {}. status={}", capabilityUid, status);
+ } else {
+ log.debug("The Capability with id {} have no Properties. status={}", capabilityUid, status);
+ }
+ return Either.right(status);
+ }
+ return Either.left(deletePropertiesStatus.left().value());
+ }
+
+ @Override
+ public Either<Map<String, PropertyData>, StorageOperationStatus> updatePropertiesOfCapability(String uniqueId, String capabilityType, List<PropertyDefinition> newProperties) {
+ return updatePropertiesOfCapability(uniqueId, capabilityType, newProperties, false);
+ }
+
+ @Override
+ public Either<Map<String, PropertyData>, StorageOperationStatus> updatePropertiesOfCapability(String uniqueId, String capabilityType, List<PropertyDefinition> newProperties, boolean inTransaction) {
+
+ Either<Map<String, PropertyData>, StorageOperationStatus> result = null;
+ try {
+ Either<CapabilityData, TitanOperationStatus> capabiltyRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), uniqueId, CapabilityData.class);
+ if (capabiltyRes.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(capabiltyRes.right().value()));
+ }
+ CapabilityData capabilityData = capabiltyRes.left().value();
+ if (result == null) {
+ StorageOperationStatus propertiesValidationRes = validateCapabilityProperties(newProperties);
+ if (!propertiesValidationRes.equals(StorageOperationStatus.OK)) {
+ result = Either.right(propertiesValidationRes);
+ }
+ }
+ if (result == null) {
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deletePropertiesRes = deletePropertiesOfCapability(uniqueId);
+ if (deletePropertiesRes.isRight() && !deletePropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(deletePropertiesRes.right().value()));
+ }
+ }
+ if (result == null) {
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesRes = addPropertiesToCapability(capabilityData, capabilityType, newProperties);
+ if (addPropertiesRes.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertiesRes.right().value()));
+ } else {
+ result = Either.left(addPropertiesRes.left().value());
+ }
+ }
+ if (result.isRight()) {
+ log.debug("Failed to update properties of capability {}. Status is {}", uniqueId, result);
+ }
+ return result;
+ } finally {
+ if (!inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ public Either<CapabilityData, TitanOperationStatus> getCapabilityRelatedToResourceInstance(String resourceInstanceId, String capabilityUid) {
+ TitanOperationStatus error = null;
+ CapabilityData capability = null;
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> getCapabilitiesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId,
+ GraphEdgeLabels.CALCULATED_CAPABILITY, NodeTypeEnum.Capability, CapabilityData.class);
+ if (getCapabilitiesRes.isRight()) {
+ error = getCapabilitiesRes.right().value();
+ log.debug("Failed to retrieve capabilities for resource instance {}. Status is {}", resourceInstanceId, error);
+ } else {
+ List<ImmutablePair<CapabilityData, GraphEdge>> capabilityPairsList = getCapabilitiesRes.left().value();
+ List<CapabilityData> capabilityPair = capabilityPairsList.stream().filter(pair -> pair.getLeft().getUniqueId().equals(capabilityUid)).map(pair -> pair.getLeft()).collect(Collectors.toList());
+ if (capabilityPair.isEmpty()) {
+ error = TitanOperationStatus.NOT_FOUND;
+ log.debug("Failed to retrieve capability {} for resource instance {}. Status is {}", capabilityUid, resourceInstanceId, error);
+ } else {
+ capability = capabilityPair.get(0);
+ }
+ }
+ if (error == null) {
+ return Either.left(capability);
+ }
+ return Either.right(error);
+ }
+
+ public Either<Map<String, PropertyDefinition>, TitanOperationStatus> getAllCapabilityTypePropertiesFromAllDerivedFrom(String firstParentType) {
+ Map<String, PropertyDefinition> allProperies = new HashMap<>();
+ return getCapabilityTypePropertiesFromDerivedFromRecursively(firstParentType, allProperies);
+ }
+
+ private Either<Map<String, PropertyDefinition>, TitanOperationStatus> getCapabilityTypePropertiesFromDerivedFromRecursively(String nextParentType, Map<String, PropertyDefinition> allProperies) {
+ TitanOperationStatus error;
+ Either<List<ImmutablePair<CapabilityTypeData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), nextParentType, GraphEdgeLabels.DERIVED_FROM,
+ NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
+ if (childrenNodes.isRight()) {
+ if (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ error = childrenNodes.right().value();
+ log.debug("Couldn't fetch derived from node for capability type {}, error: {}", nextParentType, error);
+ return Either.right(error);
+ } else {
+ log.debug("Derived from node is not found for type {} - this is OK for root capability.");
+ return Either.left(allProperies);
+ }
+ } else {
+
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> allPropertiesOfCapabilityTypeRes = propertyOperation.findPropertiesOfNode(NodeTypeEnum.CapabilityType, nextParentType);
+ if (allPropertiesOfCapabilityTypeRes.isRight() && !allPropertiesOfCapabilityTypeRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = allPropertiesOfCapabilityTypeRes.right().value();
+ log.error("Failed to retrieve properties for capability type {} from graph. Status is {}", nextParentType, error);
+ return Either.right(error);
+ } else if (allPropertiesOfCapabilityTypeRes.isLeft()) {
+ if (allProperies.isEmpty()) {
+ allProperies.putAll(allPropertiesOfCapabilityTypeRes.left().value());
+ } else {
+ allProperies.putAll(allPropertiesOfCapabilityTypeRes.left().value().entrySet().stream().filter(e -> !allProperies.containsKey(e.getKey())).collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
+ }
+ }
+ return getCapabilityTypePropertiesFromDerivedFromRecursively(childrenNodes.left().value().get(0).getLeft().getUniqueId(), allProperies);
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperation.java
new file mode 100644
index 0000000000..985399cb65
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperation.java
@@ -0,0 +1,416 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("capability-type-operation")
+public class CapabilityTypeOperation extends AbstractOperation implements ICapabilityTypeOperation {
+ @Autowired
+ private PropertyOperation propertyOperation;
+ @Autowired
+ private CapabilityOperation capabilityOperation;
+
+ public CapabilityTypeOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(CapabilityTypeOperation.class.getName());
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(
+ CapabilityTypeDefinition capabilityTypeDefinition, boolean inTransaction) {
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> result = null;
+
+ try {
+ Either<CapabilityTypeDefinition, TitanOperationStatus> validationRes = validateUpdateProperties(
+ capabilityTypeDefinition);
+ if (validationRes.isRight()) {
+ log.error("One or all properties of capability type {} not valid. status is {}",
+ capabilityTypeDefinition, validationRes.right().value().name());
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(validationRes.right().value()));
+ return result;
+ }
+ Either<CapabilityTypeData, TitanOperationStatus> eitherStatus = addCapabilityTypeToGraph(
+ capabilityTypeDefinition);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to add capability {} to Graph. status is {}", capabilityTypeDefinition,
+ eitherStatus.right().value().name());
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ CapabilityTypeData capabilityTypeData = eitherStatus.left().value();
+
+ CapabilityTypeDefinition capabilityTypeDefResult = convertCTDataToCTDefinition(capabilityTypeData);
+ log.debug("The returned CapabilityTypeDefinition is {}", capabilityTypeDefResult);
+ result = Either.left(capabilityTypeDefResult);
+ return result;
+ }
+ }
+
+ finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private Either<CapabilityTypeDefinition, TitanOperationStatus> validateUpdateProperties(
+ CapabilityTypeDefinition capabilityTypeDefinition) {
+ TitanOperationStatus error = null;
+ if (capabilityTypeDefinition.getProperties() != null && !capabilityTypeDefinition.getProperties().isEmpty()
+ && capabilityTypeDefinition.getDerivedFrom() != null) {
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> allPropertiesRes = capabilityOperation
+ .getAllCapabilityTypePropertiesFromAllDerivedFrom(capabilityTypeDefinition.getDerivedFrom());
+ if (allPropertiesRes.isRight()
+ && !allPropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = allPropertiesRes.right().value();
+ log.debug("Couldn't fetch derived from property nodes for capability type {}, error: {}",
+ capabilityTypeDefinition.getType(), error);
+ }
+ if (error == null && !allPropertiesRes.left().value().isEmpty()) {
+ Map<String, PropertyDefinition> derivedFromProperties = allPropertiesRes.left().value();
+ capabilityTypeDefinition.getProperties().entrySet().stream()
+ .filter(e -> derivedFromProperties.containsKey(e.getKey()) && e.getValue().getType() == null)
+ .forEach(e -> e.getValue().setType(derivedFromProperties.get(e.getKey()).getType()));
+
+ Either<List<PropertyDefinition>, TitanOperationStatus> validatePropertiesRes = capabilityOperation
+ .validatePropertyUniqueness(allPropertiesRes.left().value(), capabilityTypeDefinition
+ .getProperties().values().stream().collect(Collectors.toList()));
+ if (validatePropertiesRes.isRight()) {
+ error = validatePropertiesRes.right().value();
+ }
+ }
+ }
+ if (error == null) {
+ return Either.left(capabilityTypeDefinition);
+ }
+ return Either.right(error);
+ }
+
+ /**
+ *
+ * convert between graph Node object to Java object
+ *
+ * @param capabilityTypeData
+ * @return
+ */
+ protected CapabilityTypeDefinition convertCTDataToCTDefinition(CapabilityTypeData capabilityTypeData) {
+ log.debug("The object returned after create capability is {}", capabilityTypeData);
+
+ CapabilityTypeDefinition capabilityTypeDefResult = new CapabilityTypeDefinition(
+ capabilityTypeData.getCapabilityTypeDataDefinition());
+
+ return capabilityTypeDefResult;
+ }
+
+ /**
+ *
+ * Add capability type to graph.
+ *
+ * 1. Add capability type node
+ *
+ * 2. Add edge between the former node to its parent(if exists)
+ *
+ * 3. Add property node and associate it to the node created at #1. (per
+ * property & if exists)
+ *
+ * @param capabilityTypeDefinition
+ * @return
+ */
+ private Either<CapabilityTypeData, TitanOperationStatus> addCapabilityTypeToGraph(
+ CapabilityTypeDefinition capabilityTypeDefinition) {
+
+ log.debug("Got capability type {}", capabilityTypeDefinition);
+
+ String ctUniqueId = UniqueIdBuilder.buildCapabilityTypeUid(capabilityTypeDefinition.getType());
+ // capabilityTypeDefinition.setUniqueId(ctUniqueId);
+
+ CapabilityTypeData capabilityTypeData = buildCapabilityTypeData(capabilityTypeDefinition, ctUniqueId);
+
+ log.debug("Before adding capability type to graph. capabilityTypeData = {}", capabilityTypeData);
+ Either<CapabilityTypeData, TitanOperationStatus> createCTResult = titanGenericDao.createNode(capabilityTypeData,
+ CapabilityTypeData.class);
+ log.debug("After adding capability type to graph. status is = {}", createCTResult);
+
+ if (createCTResult.isRight()) {
+ TitanOperationStatus operationStatus = createCTResult.right().value();
+ log.error("Failed to capability type " + capabilityTypeDefinition.getType() + " to graph. status is "
+ + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ CapabilityTypeData resultCTD = createCTResult.left().value();
+ Map<String, PropertyDefinition> propertiesMap = capabilityTypeDefinition.getProperties();
+ Collection<PropertyDefinition> properties = propertiesMap != null ? propertiesMap.values() : null;
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapablityType = propertyOperation
+ .addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.CapabilityType, propertiesMap);
+ if (addPropertiesToCapablityType.isRight()) {
+ log.error(
+ "Failed add properties " + propertiesMap + " to capability " + capabilityTypeDefinition.getType());
+ return Either.right(addPropertiesToCapablityType.right().value());
+ }
+
+ String derivedFrom = capabilityTypeDefinition.getDerivedFrom();
+ if (derivedFrom != null) {
+ log.debug(
+ "Before creating relation between capability type " + ctUniqueId + " to its parent " + derivedFrom);
+ UniqueIdData from = new UniqueIdData(NodeTypeEnum.CapabilityType, ctUniqueId);
+ UniqueIdData to = new UniqueIdData(NodeTypeEnum.CapabilityType, derivedFrom);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(from, to,
+ GraphEdgeLabels.DERIVED_FROM, null);
+ log.debug("After create relation between capability type {} to its parent {}. Status is {}", ctUniqueId, derivedFrom, createRelation);
+ if (createRelation.isRight()) {
+ return Either.right(createRelation.right().value());
+ }
+ }
+
+ return Either.left(createCTResult.left().value());
+
+ }
+
+ private CapabilityTypeData buildCapabilityTypeData(CapabilityTypeDefinition capabilityTypeDefinition,
+ String ctUniqueId) {
+
+ CapabilityTypeData capabilityTypeData = new CapabilityTypeData(capabilityTypeDefinition);
+
+ capabilityTypeData.getCapabilityTypeDataDefinition().setUniqueId(ctUniqueId);
+ Long creationDate = capabilityTypeData.getCapabilityTypeDataDefinition().getCreationTime();
+ if (creationDate == null) {
+ creationDate = System.currentTimeMillis();
+ }
+ capabilityTypeData.getCapabilityTypeDataDefinition().setCreationTime(creationDate);
+ capabilityTypeData.getCapabilityTypeDataDefinition().setModificationTime(creationDate);
+ return capabilityTypeData;
+ }
+
+ @Override
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId,
+ boolean inTransaction) {
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> result = null;
+ try {
+
+ Either<CapabilityTypeDefinition, TitanOperationStatus> ctResult = this.getCapabilityTypeByUid(uniqueId);
+
+ if (ctResult.isRight()) {
+ TitanOperationStatus status = ctResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to retrieve information on capability type {}. Status is {}", uniqueId, status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(ctResult.right().value()));
+ return result;
+ }
+
+ result = Either.left(ctResult.left().value());
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ /**
+ * Build Capability type object from graph by unique id
+ *
+ * @param uniqueId
+ * @return
+ */
+ public Either<CapabilityTypeDefinition, TitanOperationStatus> getCapabilityTypeByUid(String uniqueId) {
+
+ Either<CapabilityTypeDefinition, TitanOperationStatus> result = null;
+
+ Either<CapabilityTypeData, TitanOperationStatus> capabilityTypesRes = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), uniqueId, CapabilityTypeData.class);
+
+ if (capabilityTypesRes.isRight()) {
+ TitanOperationStatus status = capabilityTypesRes.right().value();
+ log.debug("Capability type {} cannot be found in graph. Status is {}", uniqueId, status);
+ return Either.right(status);
+ }
+
+ CapabilityTypeData ctData = capabilityTypesRes.left().value();
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition(
+ ctData.getCapabilityTypeDataDefinition());
+
+ TitanOperationStatus propertiesStatus = fillProperties(uniqueId, capabilityTypeDefinition);
+ if (propertiesStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of capability type " + uniqueId);
+ return Either.right(propertiesStatus);
+ }
+
+ Either<ImmutablePair<CapabilityTypeData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), uniqueId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
+ log.debug("After retrieving DERIVED_FROM node of {}. Status is {}", uniqueId, parentNode);
+ if (parentNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = parentNode.right().value();
+ if (titanOperationStatus != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find the parent capability of capability type {}. Status is {}", uniqueId, titanOperationStatus);
+ result = Either.right(titanOperationStatus);
+ return result;
+ }
+ } else {
+ // derived from node was found
+ ImmutablePair<CapabilityTypeData, GraphEdge> immutablePair = parentNode.left().value();
+ CapabilityTypeData parentCT = immutablePair.getKey();
+ capabilityTypeDefinition.setDerivedFrom(parentCT.getCapabilityTypeDataDefinition().getType());
+ }
+ result = Either.left(capabilityTypeDefinition);
+
+ return result;
+ }
+
+ private TitanOperationStatus fillProperties(String uniqueId, CapabilityTypeDefinition capabilityTypeDefinition) {
+
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfNode = propertyOperation
+ .findPropertiesOfNode(NodeTypeEnum.CapabilityType, uniqueId);
+ if (findPropertiesOfNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = findPropertiesOfNode.right().value();
+ log.debug("After looking for properties of vertex {}. Status is {}", uniqueId, titanOperationStatus);
+ if (TitanOperationStatus.NOT_FOUND.equals(titanOperationStatus)) {
+ return TitanOperationStatus.OK;
+ } else {
+ return titanOperationStatus;
+ }
+ } else {
+ Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
+ capabilityTypeDefinition.setProperties(properties);
+ return TitanOperationStatus.OK;
+ }
+ }
+
+ public Either<Boolean, StorageOperationStatus> isCapabilityTypeDerivedFrom(String childCandidateType,
+ String parentCandidateType) {
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childCandidateType);
+ Either<List<CapabilityTypeData>, TitanOperationStatus> getResponse = titanGenericDao
+ .getByCriteria(NodeTypeEnum.CapabilityType, propertiesToMatch, CapabilityTypeData.class);
+ if (getResponse.isRight()) {
+ TitanOperationStatus titanOperationStatus = getResponse.right().value();
+ log.debug("Couldn't fetch capability type {}, error: {}", childCandidateType, titanOperationStatus);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
+ }
+ String childUniqueId = getResponse.left().value().get(0).getUniqueId();
+ Set<String> travelledTypes = new HashSet<>();
+ do {
+ travelledTypes.add(childUniqueId);
+ Either<List<ImmutablePair<CapabilityTypeData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childUniqueId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
+ if (childrenNodes.isRight()) {
+ if (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ TitanOperationStatus titanOperationStatus = getResponse.right().value();
+ log.debug("Couldn't fetch derived from node for capability type {}, error: {}", childCandidateType,
+ titanOperationStatus);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
+ } else {
+ log.debug("Derived from node is not found for type {} - this is OK for root capability.");
+ return Either.left(false);
+ }
+ }
+ String derivedFromUniqueId = childrenNodes.left().value().get(0).getLeft().getUniqueId();
+ if (derivedFromUniqueId.equals(parentCandidateType)) {
+ log.debug("Verified that capability type {} derives from capability type {}", childCandidateType,
+ parentCandidateType);
+ return Either.left(true);
+ }
+ childUniqueId = derivedFromUniqueId;
+ } while (!travelledTypes.contains(childUniqueId));
+ // this stop condition should never be used, if we use it, we have an
+ // illegal cycle in graph - "derived from" hierarchy cannot be cycled.
+ // It's here just to avoid infinite loop in case we have such cycle.
+ log.error("Detected a cycle of \"derived from\" edges starting at capability type node {}", childUniqueId);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param propertyOperation
+ */
+ public void setPropertyOperation(PropertyOperation propertyOperation) {
+ this.propertyOperation = propertyOperation;
+ }
+
+ @Override
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(
+ CapabilityTypeDefinition capabilityTypeDefinition) {
+
+ return addCapabilityType(capabilityTypeDefinition, false);
+ }
+
+ @Override
+ public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId) {
+ return getCapabilityType(uniqueId, false);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperation.java
new file mode 100644
index 0000000000..cb85888780
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperation.java
@@ -0,0 +1,5852 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GetInputValueInfo;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.IComponentInstanceConnectedElement;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.operations.api.IAttributeOperation;
+import org.openecomp.sdc.be.model.operations.api.IComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.be.resources.data.AttributeValueData;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.InputValueData;
+import org.openecomp.sdc.be.resources.data.InputsData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.RelationshipInstData;
+import org.openecomp.sdc.be.resources.data.RelationshipTypeData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.TitanVertexQuery;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("component-instance-operation")
+public class ComponentInstanceOperation extends AbstractOperation implements IComponentInstanceOperation {
+
+ public ComponentInstanceOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(ComponentInstanceOperation.class.getName());
+
+ @Autowired
+ private ResourceOperation resourceOperation;
+
+ @Autowired
+ private ServiceOperation serviceOperation;
+
+ @Autowired
+ CapabilityOperation capabilityOperation;
+
+ @Autowired
+ private CapabilityInstanceOperation capabilityInstanceOperation;
+
+ @Autowired
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @Autowired
+ private RequirementOperation requirementOperation;
+
+ @Autowired
+ private ArtifactOperation artifactOperation;
+
+ @Autowired
+ TitanGenericDao titanGenericDao;
+
+ @Autowired
+ PropertyOperation propertyOperation;
+
+ @Autowired
+ InputsOperation inputOperation;
+
+ @Autowired
+ private IAttributeOperation attributeOperation;
+
+ @Autowired
+ private ApplicationDataTypeCache dataTypeCache;
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> createComponentInstance(String parentComponentId, NodeTypeEnum nodeType, String instanceNumber, ComponentInstance componentInstance, NodeTypeEnum compInstNodeType, boolean inTransaction) {
+
+ return createComponentInstance(parentComponentId, nodeType, instanceNumber, true, componentInstance, compInstNodeType, false, inTransaction);
+
+ }
+
+ private Either<ComponentInstance, StorageOperationStatus> createComponentInstance(String containerComponentId, NodeTypeEnum containerNodeType, String instanceNumber, boolean isCreateLocgicalName, ComponentInstance componentInstance,
+ NodeTypeEnum compInstNodeType, boolean allowDeleted, boolean inTransaction) {
+ Either<ComponentInstance, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<ComponentInstance, TitanOperationStatus> addRes = addComponentInstanceToContainerComponent(containerComponentId, containerNodeType, instanceNumber, isCreateLocgicalName, componentInstance, compInstNodeType, allowDeleted);
+ if (addRes.isRight()) {
+ TitanOperationStatus status = addRes.right().value();
+ log.error("Failed to add resource instance {} to service {}. Status is {}", componentInstance, containerComponentId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ ComponentInstance value = addRes.left().value();
+ result = Either.left(value);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+ }
+
+ private Either<TitanVertex, StorageOperationStatus> createComponentInstance(String containerComponentId, NodeTypeEnum containerNodeType, String instanceNumber, boolean isCreateLocgicalName, ComponentInstance componentInstance,
+ NodeTypeEnum compInstNodeType, boolean allowDeleted, boolean inTransaction, TitanVertex metadataVertex) {
+ Either<TitanVertex, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<TitanVertex, TitanOperationStatus> addRes = addComponentInstanceToContainerComponent(containerComponentId, containerNodeType, instanceNumber, isCreateLocgicalName, componentInstance, compInstNodeType, allowDeleted, metadataVertex);
+ if (addRes.isRight()) {
+ TitanOperationStatus status = addRes.right().value();
+ log.error("Failed to add resource instance {} to service {}. status is {}", componentInstance, containerComponentId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ TitanVertex value = addRes.left().value();
+ result = Either.left(value);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> createComponentInstance(String containerComponentId, NodeTypeEnum containerNodeType, String instanceNumber, ComponentInstance componentInstance, NodeTypeEnum instNodeType) {
+
+ return createComponentInstance(containerComponentId, containerNodeType, instanceNumber, componentInstance, instNodeType, false);
+
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> deleteComponentInstance(NodeTypeEnum containerNodeType, String containerComponentId, String resourceInstUid, boolean inTransaction) {
+
+ Either<ComponentInstance, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<ComponentInstance, TitanOperationStatus> deleteRes = removeComponentInstanceFromComponent(containerNodeType, containerComponentId, resourceInstUid);
+
+ if (deleteRes.isRight()) {
+ TitanOperationStatus status = deleteRes.right().value();
+ log.error("Failed to remove resource instance {} from component {}. Status is {}", resourceInstUid, containerComponentId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ ComponentInstance value = deleteRes.left().value();
+ result = Either.left(value);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> deleteComponentInstance(NodeTypeEnum containerNodeType, String containerComponentId, String resourceInstUid) {
+
+ return deleteComponentInstance(containerNodeType, containerComponentId, resourceInstUid, false);
+ }
+
+ private <T> void commitOrRollback(Either<T, StorageOperationStatus> result) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateParent(String parentId, String uniqId, boolean inTransaction) {
+
+ Either<Boolean, StorageOperationStatus> result = null;
+ Either<Boolean, TitanOperationStatus> updateRes = validateParentonGraph(parentId, uniqId, inTransaction);
+
+ if (updateRes.isRight()) {
+ TitanOperationStatus status = updateRes.right().value();
+ log.error("Failed to find resource instance name {}. Status is {}", uniqId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ Boolean value = updateRes.left().value();
+
+ result = Either.left(value);
+
+ return result;
+
+ }
+
+ public Either<Boolean, TitanOperationStatus> validateParentonGraph(String parentId, String uniqId, boolean inTransaction) {
+
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanGenericDao.getGraph();
+ if (graphRes.isRight()) {
+ log.debug("Failed to retrieve graph. status is {}", graphRes);
+ return Either.right(graphRes.right().value());
+ }
+ TitanGraph titanGraph = graphRes.left().value();
+ try {
+ Iterable<TitanVertex> vertices = titanGraph.query().has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), uniqId).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ TitanVertex vertex = vertices.iterator().next();
+
+ TitanVertexQuery query = vertex.query();
+ query = query.labels(GraphEdgeLabels.RESOURCE_INST.getProperty()).direction(Direction.IN);
+ Iterable<Vertex> verts = query.vertices();
+ if (verts == null) {
+ log.debug("No edges in graph for criteria");
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ Iterator<Vertex> vIter = verts.iterator();
+ if (vIter.hasNext()) {
+ Vertex vert = vIter.next();
+ // vert.getProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String resInstName = vert.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ if (resInstName.equals(parentId))
+ return Either.left(Boolean.TRUE);
+ }
+ return Either.left(Boolean.FALSE);
+ } finally {
+ if (false == inTransaction) {
+ titanGraph.tx().commit();
+ }
+ }
+ }
+
+ public Either<ComponentInstance, TitanOperationStatus> addComponentInstanceToContainerComponent(String containerComponentId, NodeTypeEnum containerNodeType, String instanceNumber, boolean isCreateLogicaName, ComponentInstance componentInstance,
+ NodeTypeEnum compInstNodeType, boolean allowDeleted) {
+ log.debug("Going to create component instance {} in component {}", componentInstance, containerComponentId);
+
+ Either<TitanVertex, TitanOperationStatus> metadataVertex = titanGenericDao.getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), containerComponentId);
+ if (metadataVertex.isRight()) {
+ TitanOperationStatus status = metadataVertex.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+ Either<TitanVertex, TitanOperationStatus> addComponentInstanceToContainerComponent = addComponentInstanceToContainerComponent(containerComponentId, containerNodeType, instanceNumber, isCreateLogicaName, componentInstance, compInstNodeType,
+ allowDeleted, metadataVertex.left().value());
+
+ if (addComponentInstanceToContainerComponent.isRight()) {
+ TitanOperationStatus status = addComponentInstanceToContainerComponent.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+ TitanVertex ciVertex = addComponentInstanceToContainerComponent.left().value();
+ Map<String, Object> properties = titanGenericDao.getProperties(ciVertex);
+ ComponentInstanceData createdComponentInstance = GraphElementFactory.createElement(NodeTypeEnum.ResourceInstance.getName(), GraphElementTypeEnum.Node, properties, ComponentInstanceData.class);
+
+ ComponentInstance createdResourceInstance = new ComponentInstance(createdComponentInstance.getComponentInstDataDefinition());
+
+ return Either.left(createdResourceInstance);
+
+ }
+
+ /**
+ *
+ * @param containerComponentId
+ * @param containerNodeType
+ * @param instanceNumber
+ * @param isCreateLogicaName
+ * @param componentInstance
+ * @param compInstNodeType
+ * @param allowDeleted
+ * @param metadataVertex
+ * @return
+ */
+ public Either<TitanVertex, TitanOperationStatus> addComponentInstanceToContainerComponent(String containerComponentId, NodeTypeEnum containerNodeType, String instanceNumber, boolean isCreateLogicaName, ComponentInstance componentInstance,
+ NodeTypeEnum compInstNodeType, boolean allowDeleted, TitanVertex metadataVertex) {
+ TitanOperationStatus status;
+ log.debug("Going to create component instance {} in component {}", componentInstance, containerComponentId);
+ String instOriginComponentId = componentInstance.getComponentUid();
+ String logicalName = componentInstance.getName();
+ if (isCreateLogicaName)
+ logicalName = createComponentInstLogicalName(instanceNumber, componentInstance.getName());
+
+ ComponentInstanceData componentInstanceData = buildComponentInstanceData(componentInstance, containerComponentId, logicalName);
+ Either<TitanVertex, TitanOperationStatus> originVertexEither = titanGenericDao.getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), instOriginComponentId);
+ if (originVertexEither.isRight()) {
+ log.debug("Failed to fetch vertex of origin resource for id {} error {}", instOriginComponentId, originVertexEither.right().value());
+ return Either.right(originVertexEither.right().value());
+ }
+ TitanVertex originVertex = originVertexEither.left().value();
+
+ Boolean isDeleted = (Boolean) titanGenericDao.getProperty(metadataVertex, GraphPropertiesDictionary.IS_DELETED.getProperty());
+
+ if (!allowDeleted && (isDeleted != null) && (isDeleted == true)) {
+ log.debug("Component {} is already deleted. Cannot add component instance", instOriginComponentId);
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ String originType = (String) titanGenericDao.getProperty(originVertex, GraphPropertiesDictionary.LABEL.getProperty());
+ String resourceType = (String) titanGenericDao.getProperty(originVertex, GraphPropertiesDictionary.RESOURCE_TYPE.getProperty());
+ detectOriginType(originType, componentInstanceData, resourceType);
+
+ log.trace("Before adding component instance to graph. componentInstanceData = {}", componentInstanceData);
+
+ Either<TitanVertex, TitanOperationStatus> createCIResult = titanGenericDao.createNode(componentInstanceData);
+
+ log.debug("After adding component instance to graph. status is = {}", createCIResult);
+
+ if (createCIResult.isRight()) {
+ status = createCIResult.right().value();
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Add Component Instance");
+ log.debug("Failed to create component instance node in graph. status is {}", status);
+ return Either.right(status);
+ }
+ TitanVertex createdComponentInstanceVertex = createCIResult.left().value();
+ TitanOperationStatus associateContainerRes = associateContainerCompToComponentInstance(metadataVertex, createdComponentInstanceVertex, logicalName);
+
+ String componentInstanceUniqueId = componentInstanceData.getUniqueId();
+ if (!associateContainerRes.equals(TitanOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Add Component Instance");
+ log.debug("Failed to associate container component {} to component instance {}. Status is {}", containerComponentId, componentInstanceUniqueId, associateContainerRes);
+ return Either.right(associateContainerRes);
+ }
+ String originId = (String) titanGenericDao.getProperty(createdComponentInstanceVertex, GraphPropertiesDictionary.TYPE.getProperty());
+
+ TitanOperationStatus associateToInstOriginComponent = associateToInstOriginComponent(createdComponentInstanceVertex, originVertex, originId);
+ if (!associateToInstOriginComponent.equals(TitanOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Add Component Instance");
+ log.debug("Failed to associate component instance {} to its origin component {}. Status is {}", componentInstanceUniqueId, componentInstanceData.getComponentInstDataDefinition().getComponentUid(), associateToInstOriginComponent);
+ return Either.right(associateToInstOriginComponent);
+ }
+
+ TitanOperationStatus associateCompInstToRequirements = associateCompInstToRequirements(createdComponentInstanceVertex, containerNodeType, compInstNodeType, originId);
+ if (!associateCompInstToRequirements.equals(TitanOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Add Component Instance");
+ log.debug("Failed to associate component instance {} to its origin requirements. Status is {}", componentInstanceUniqueId, associateCompInstToRequirements);
+ return Either.right(associateCompInstToRequirements);
+ }
+ TitanOperationStatus associateCompInstToCapabilities = associateCompInstToCapabilities(createdComponentInstanceVertex, containerNodeType, compInstNodeType, originId);
+ if (!associateCompInstToCapabilities.equals(TitanOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Add Component Instance");
+ log.debug("Failed to associate component instance {} to its origin capabilities. Status is {}", componentInstanceUniqueId, associateCompInstToCapabilities);
+ return Either.right(associateCompInstToCapabilities);
+ }
+ // Capability instance with property values implementation
+ Either<List<ImmutablePair<TitanVertex, GraphEdge>>, TitanOperationStatus> cloneCapabilityInstancesRes = null;
+ Either<List<GraphRelation>, TitanOperationStatus> associateComponentInstanceToCapabilityInstancesRes;
+ status = null;
+ if (!isCreateLogicaName) {
+ // in case of cloning of component instance
+ log.debug("Before cloning of capability instances of component instance {}.", componentInstance.getUniqueId());
+ cloneCapabilityInstancesRes = cloneCapabilityInstancesOfResourceInstance(createdComponentInstanceVertex, componentInstance);
+ if (cloneCapabilityInstancesRes.isRight() && !cloneCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ status = cloneCapabilityInstancesRes.right().value();
+ log.debug("Failed to clone capability instances of component instance {}. Status is {}", componentInstance.getUniqueId(), status);
+ }
+ log.trace("After cloning of capability instances of component instance {}. Status is {}", componentInstance.getUniqueId(), status);
+ } else if (containerNodeType.equals(NodeTypeEnum.Resource) && componentInstance.getCapabilities() != null && !componentInstance.getCapabilities().isEmpty()) {
+ // in case of creation from scar
+ TitanOperationStatus addPropertiesRes = createCapabilityInstancesWithPropertyValues(createdComponentInstanceVertex, componentInstanceUniqueId, componentInstance.getCapabilities(), true);
+ if (!addPropertiesRes.equals(TitanOperationStatus.OK)) {
+ status = addPropertiesRes;
+ log.debug("Failed to create capability instances with property values for component instance {}. Status is {}", componentInstance.getUniqueId(), status);
+ }
+ }
+ if (status == null && containerNodeType.equals(NodeTypeEnum.Service)) {
+ Map<String, Object> properties = titanGenericDao.getProperties(createdComponentInstanceVertex);
+ ComponentInstanceData createdComponentInstance = GraphElementFactory.createElement(NodeTypeEnum.ResourceInstance.getName(), GraphElementTypeEnum.Node, properties, ComponentInstanceData.class);
+ if (cloneCapabilityInstancesRes == null || cloneCapabilityInstancesRes.isRight()) {
+ // in case of creating of service
+ log.trace("Before associating component instance {} to capability instances .", componentInstance.getUniqueId());
+ associateComponentInstanceToCapabilityInstancesRes = associateComponentInstanceToCapabilityInstancesOfResourceInstance(componentInstance);
+ if (associateComponentInstanceToCapabilityInstancesRes.isRight() && !associateComponentInstanceToCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ status = associateComponentInstanceToCapabilityInstancesRes.right().value();
+ log.debug("Failed to associate capability instances to component instance {}. Status is {}", componentInstance.getUniqueId(), status);
+ }
+ log.trace("After associating component instance {} to capability instances . Status is {}", componentInstance.getUniqueId(), status);
+ } else {
+ // in case of cloning of service
+ log.trace("Before associating created component instance {} to cloned capability instances.", componentInstanceUniqueId);
+ TitanOperationStatus associationStatus = associateCreatedComponentInstanceToClonedCapabilityInstances(createdComponentInstanceVertex, componentInstanceUniqueId, cloneCapabilityInstancesRes.left().value());
+ if (!associationStatus.equals(TitanOperationStatus.OK) && !associationStatus.equals(TitanOperationStatus.NOT_FOUND)) {
+ status = associationStatus;
+ log.debug("Failed to associate capability instances to component instance {}. Status is {}", componentInstance.getUniqueId(), status);
+ }
+ log.trace("After associating created component instance {} to cloned capability instances. Status is {}", componentInstanceUniqueId, status);
+ }
+ }
+ if (status == null) {
+ // ComponentInstance createdResourceInstance = new
+ // ComponentInstance(createdComponentInstance.getComponentInstDataDefinition());
+ //
+ // String icon = (String) titanGenericDao.getProperty(originVertex,
+ // GraphPropertiesDictionary.ICON.getProperty());
+ // createdResourceInstance.setIcon(icon);
+ return Either.left(createdComponentInstanceVertex);
+ }
+ return Either.right(status);
+ }
+
+ private Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> createCapabilityInstancesWithPropertyValues(String resourceInstanceId, Map<String, List<CapabilityDefinition>> capabilities,
+ boolean isNewlyCreatedResourceInstance) {
+ TitanOperationStatus error;
+ Map<CapabilityInstData, List<PropertyValueData>> result = new HashMap<>();
+ for (Entry<String, List<CapabilityDefinition>> capailityEntry : capabilities.entrySet()) {
+ CapabilityDefinition capability = capailityEntry.getValue().get(0);
+ if (capability.getProperties() != null && !capability.getProperties().isEmpty()) {
+ Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> addPropertiesRes = addCapabilityPropertyValuesToResourceInstance(resourceInstanceId, capability, isNewlyCreatedResourceInstance);
+ if (addPropertiesRes.isRight()) {
+ error = addPropertiesRes.right().value();
+ log.debug("Failed to add property values to capabilities of component instance {}. Status is {}", resourceInstanceId, error);
+ return Either.right(error);
+ } else {
+ result.putAll(addPropertiesRes.left().value());
+ }
+ }
+ }
+ return Either.left(result);
+ }
+
+ private TitanOperationStatus createCapabilityInstancesWithPropertyValues(TitanVertex resourceInstanceVertex, String resourceInstanceId, Map<String, List<CapabilityDefinition>> capabilities, boolean isNewlyCreatedResourceInstance) {
+ TitanOperationStatus result = TitanOperationStatus.OK;
+
+ for (Entry<String, List<CapabilityDefinition>> capailityEntry : capabilities.entrySet()) {
+ CapabilityDefinition capability = capailityEntry.getValue().get(0);
+ if (capability.getProperties() != null && !capability.getProperties().isEmpty()) {
+ TitanOperationStatus addPropertiesRes = addCapabilityPropertyValuesToResourceInstance(resourceInstanceVertex, resourceInstanceId, capability, isNewlyCreatedResourceInstance);
+ if (!addPropertiesRes.equals(TitanOperationStatus.OK)) {
+ result = addPropertiesRes;
+ log.debug("Failed to add property values to capabilities of component instance {}. Status is {}", resourceInstanceId, result);
+ return result;
+ }
+ }
+ }
+ return result;
+ }
+
+ private Either<List<GraphRelation>, TitanOperationStatus> associateCreatedComponentInstanceToClonedCapabilityInstances(String newComponentResourceId, List<ImmutablePair<CapabilityInstData, GraphEdge>> capabilityInstances) {
+ TitanOperationStatus error = null;
+ List<GraphRelation> relationsToCapabilityInstances = new ArrayList<>();
+ UniqueIdData componentInstanceIdData = new UniqueIdData(NodeTypeEnum.ResourceInstance, newComponentResourceId);
+ for (ImmutablePair<CapabilityInstData, GraphEdge> capInstPair : capabilityInstances) {
+ Either<GraphRelation, TitanOperationStatus> associateComponentInstanceToCapabilityinstanceRes = titanGenericDao.createRelation(componentInstanceIdData, capInstPair.getLeft(), GraphEdgeLabels.CAPABILITY_INST,
+ capInstPair.getRight().getProperties());
+ if (associateComponentInstanceToCapabilityinstanceRes.isRight()) {
+ error = associateComponentInstanceToCapabilityinstanceRes.right().value();
+ log.debug("Failed to associate capability instance {} to resource instance {}. Status is {}.", capInstPair.getLeft().getUniqueId(), newComponentResourceId, error);
+ break;
+ } else {
+ relationsToCapabilityInstances.add(associateComponentInstanceToCapabilityinstanceRes.left().value());
+ }
+ }
+ if (error == null) {
+ return Either.left(relationsToCapabilityInstances);
+ }
+ return Either.right(error);
+ }
+
+ private TitanOperationStatus associateCreatedComponentInstanceToClonedCapabilityInstances(TitanVertex riVertex, String newComponentResourceId, List<ImmutablePair<TitanVertex, GraphEdge>> capabilityInstances) {
+ TitanOperationStatus error = null;
+ for (ImmutablePair<TitanVertex, GraphEdge> capInstPair : capabilityInstances) {
+ TitanOperationStatus associateComponentInstanceToCapabilityinstanceRes = titanGenericDao.createEdge(riVertex, capInstPair.getLeft(), GraphEdgeLabels.CAPABILITY_INST, capInstPair.getRight().getProperties());
+ if (!associateComponentInstanceToCapabilityinstanceRes.equals(TitanOperationStatus.OK)) {
+ error = associateComponentInstanceToCapabilityinstanceRes;
+ log.debug("Failed to associate capability instance {} to resource instance {} status is {} .", capInstPair.getLeft(), newComponentResourceId, error);
+ break;
+ }
+ }
+ if (error == null) {
+ return TitanOperationStatus.OK;
+ }
+ return error;
+ }
+
+ private Either<List<GraphRelation>, TitanOperationStatus> associateComponentInstanceToCapabilityInstancesOfResourceInstance(ComponentInstance componentInstance) {
+ TitanOperationStatus error = null;
+ String resourceId = componentInstance.getComponentUid();
+ String componentResourceId = componentInstance.getUniqueId();
+ UniqueIdData componentInstanceIdData = new UniqueIdData(NodeTypeEnum.ResourceInstance, componentResourceId);
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> resourceInstancesPair;
+ List<ImmutablePair<CapabilityInstData, GraphEdge>> allCapabilityInstancesList = new ArrayList<>();
+ List<GraphRelation> relationsToCapabilityInstances = new ArrayList<>();
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> getAllResourceInstanceRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId,
+ GraphEdgeLabels.RESOURCE_INST, NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+ if (getAllResourceInstanceRes.isRight() && !getAllResourceInstanceRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = getAllResourceInstanceRes.right().value();
+ log.debug("Failed to retrieve resource instances from resource {}. Status is {}.", resourceId, error);
+ }
+ if (getAllResourceInstanceRes.isLeft()) {
+ resourceInstancesPair = getAllResourceInstanceRes.left().value();
+ ComponentInstanceData ri;
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> riPair : resourceInstancesPair) {
+ ri = riPair.getLeft();
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getCapabilityInstancesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), ri.getUniqueId(),
+ GraphEdgeLabels.CAPABILITY_INST, NodeTypeEnum.CapabilityInst, CapabilityInstData.class);
+ if (getCapabilityInstancesRes.isRight() && !getCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = getCapabilityInstancesRes.right().value();
+ log.debug("Failed to retrieve capability instances of resource instance {}. Status is {}", ri.getUniqueId(), error);
+ break;
+ }
+ if (getCapabilityInstancesRes.isLeft()) {
+ allCapabilityInstancesList.addAll(getCapabilityInstancesRes.left().value());
+ }
+ }
+ }
+ if (error == null && !allCapabilityInstancesList.isEmpty()) {
+ for (ImmutablePair<CapabilityInstData, GraphEdge> capInstPair : allCapabilityInstancesList) {
+ Either<GraphRelation, TitanOperationStatus> associateComponentInstanceToCapabilityinstanceRes = titanGenericDao.createRelation(componentInstanceIdData, capInstPair.getLeft(), GraphEdgeLabels.CAPABILITY_INST,
+ capInstPair.getRight().getProperties());
+ if (associateComponentInstanceToCapabilityinstanceRes.isRight()) {
+ error = associateComponentInstanceToCapabilityinstanceRes.right().value();
+ log.debug("Failed to associate capability instance {} to resource instance {}. Status is {}", capInstPair.getLeft().getUniqueId(), componentResourceId, error);
+ break;
+ } else {
+ relationsToCapabilityInstances.add(associateComponentInstanceToCapabilityinstanceRes.left().value());
+ }
+ }
+ }
+ if (error == null) {
+ return Either.left(relationsToCapabilityInstances);
+ }
+ return Either.right(error);
+ }
+
+ private void detectOriginType(String label, ComponentInstanceData componentInstanceData, String resourceTypeStr) {
+ switch (NodeTypeEnum.getByName(label)) {
+ case Service:
+ componentInstanceData.getComponentInstDataDefinition().setOriginType(OriginTypeEnum.SERVICE);
+ break;
+ case Product:
+ componentInstanceData.getComponentInstDataDefinition().setOriginType(OriginTypeEnum.PRODUCT);
+ break;
+ case Resource:
+ ResourceTypeEnum resourceType = ResourceTypeEnum.valueOf(resourceTypeStr);
+ switch (resourceType) {
+ case VF:
+ componentInstanceData.getComponentInstDataDefinition().setOriginType(OriginTypeEnum.VF);
+ break;
+ case VFC:
+ componentInstanceData.getComponentInstDataDefinition().setOriginType(OriginTypeEnum.VFC);
+ break;
+ case CP:
+ componentInstanceData.getComponentInstDataDefinition().setOriginType(OriginTypeEnum.CP);
+ break;
+ case VL:
+ componentInstanceData.getComponentInstDataDefinition().setOriginType(OriginTypeEnum.VL);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateToInstOriginComponent(ComponentInstanceData componentInstanceData, NodeTypeEnum compInstNodeType) {
+
+ UniqueIdData resourceIdData = new UniqueIdData(compInstNodeType, componentInstanceData.getComponentInstDataDefinition().getComponentUid());
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(componentInstanceData, resourceIdData, GraphEdgeLabels.INSTANCE_OF, null);
+
+ log.debug("After associating resource instance {} to resource {}. Status is {}",
+ componentInstanceData.getUniqueId(),
+ componentInstanceData.getComponentInstDataDefinition().getUniqueId(),
+ createRelation);
+
+ return createRelation;
+ }
+
+ private TitanOperationStatus associateToInstOriginComponent(TitanVertex componentInstanceVertex, TitanVertex originVertex, String originId) {
+
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(componentInstanceVertex, originVertex, GraphEdgeLabels.INSTANCE_OF, null);
+
+ log.debug("After associating resource instance {} to resource {}. status is {}", componentInstanceVertex, originId, createRelation);
+
+ return createRelation;
+ }
+
+ private Either<List<GraphRelation>, TitanOperationStatus> associateCompInstToRequirements(ComponentInstanceData componentInstanceData, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType) {
+ log.trace("Starting to copy origin component requirements to its component instance");
+ String compInstOriginId = componentInstanceData.getComponentInstDataDefinition().getComponentUid();
+ List<GraphRelation> graphRelations = new ArrayList<>();
+
+ // case of VFC / CP / VL
+ if (compInstNodeType.equals(NodeTypeEnum.Resource)) {
+ createRequirementRelationsFromAtomicResource(componentInstanceData, compInstOriginId, graphRelations);
+
+ }
+ // case of VF / Service / Product
+ createCalculatedRequirementRelationsFromComponent(componentInstanceData, containerNodeType, compInstNodeType, graphRelations, compInstOriginId);
+
+ log.trace("Finished to copy origin component requirements to its component instance, created {} new calculated requirement relations", graphRelations.size());
+ return Either.left(graphRelations);
+ }
+
+ private TitanOperationStatus associateCompInstToRequirements(TitanVertex componentInstanceVertex, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, String originId) {
+ log.trace("Starting to copy origin component requirements to its component instance");
+ TitanOperationStatus status = TitanOperationStatus.OK;
+ // case of VFC / CP / VL
+ if (compInstNodeType.equals(NodeTypeEnum.Resource)) {
+ status = createRequirementRelationsFromAtomicResource(componentInstanceVertex, originId);
+ if (!status.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed create relation to requirement of origin {} error {}", originId, status);
+ return status;
+ }
+ }
+ // case of VF / Service / Product
+ status = createCalculatedRequirementRelationsFromComponent(componentInstanceVertex, containerNodeType, compInstNodeType, originId);
+
+ log.trace("Finished to copy origin component requirements to its component instance with status {}", status);
+ return status;
+ }
+
+ private void createRequirementRelationsFromAtomicResource(ComponentInstanceData componentInstanceData, String compInstOriginId, List<GraphRelation> graphRelations) {
+ Map<String, RequirementDefinition> requirements = new HashMap<String, RequirementDefinition>();
+ Set<String> caseInsensitiveReqNames = new HashSet<>();
+
+ TitanOperationStatus status = requirementOperation.findAllRequirementsRecursively(compInstOriginId, requirements, caseInsensitiveReqNames);
+ if (status != TitanOperationStatus.OK) {
+ log.debug("Couldn't fetch requirements of component {}, error: {}", compInstOriginId, status);
+ }
+
+ log.trace("Found {} requirements for component {}, ", requirements.size(), compInstOriginId);
+ for (Entry<String, RequirementDefinition> reqPair : requirements.entrySet()) {
+ RequirementDefinition requirementDef = reqPair.getValue();
+ RequirementData requirementData = new RequirementData();
+ requirementData.setUniqueId(requirementDef.getUniqueId());
+
+ log.trace("Creating calculated requirement relation from component instance {} to requirement {}", componentInstanceData.getUniqueId(), requirementDef.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), reqPair.getKey());
+
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), componentInstanceData.getUniqueId());
+ if (requirementDef.getMinOccurrences() == null) {
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), RequirementData.MIN_OCCURRENCES);
+ } else {
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), requirementDef.getMinOccurrences());
+ }
+ if (requirementDef.getMaxOccurrences() == null) {
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), RequirementData.MAX_DEFAULT_OCCURRENCES);
+ } else {
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), requirementDef.getMaxOccurrences());
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(componentInstanceData, requirementData, GraphEdgeLabels.CALCULATED_REQUIREMENT, props);
+ if (createRelation.isRight()) {
+ TitanOperationStatus titanOperationStatus = createRelation.right().value();
+ log.debug("Failed to create calculated requirement from component instance {} to requirement {}, error: {}", componentInstanceData.getUniqueId(), requirementDef.getUniqueId(), titanOperationStatus);
+ }
+ graphRelations.add(createRelation.left().value());
+ }
+ }
+
+ private TitanOperationStatus createRequirementRelationsFromAtomicResource(TitanVertex componentInstanceVertex, String compInstOriginId) {
+ Map<String, RequirementDefinition> requirements = new HashMap<String, RequirementDefinition>();
+ Set<String> caseInsensitiveReqNames = new HashSet<>();
+
+ TitanOperationStatus status = requirementOperation.findAllRequirementsRecursively(compInstOriginId, requirements, caseInsensitiveReqNames);
+ if (status != TitanOperationStatus.OK && status != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Couldn't fetch requirements of component {}, error: {}", compInstOriginId, status);
+ return status;
+ }
+
+ String compoInstId = (String) titanGenericDao.getProperty(componentInstanceVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ log.trace("Found {} requirements for component {}, ", requirements.size(), compInstOriginId);
+ for (Entry<String, RequirementDefinition> reqPair : requirements.entrySet()) {
+ RequirementDefinition requirementDef = reqPair.getValue();
+ RequirementData requirementData = new RequirementData();
+ requirementData.setUniqueId(requirementDef.getUniqueId());
+
+ log.trace("Creating calculated requirement relation from component instance {} to requirement {}", compoInstId, requirementDef.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), reqPair.getKey());
+
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), compoInstId);
+ if (requirementDef.getMinOccurrences() == null) {
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), RequirementData.MIN_OCCURRENCES);
+ } else {
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), requirementDef.getMinOccurrences());
+ }
+ if (requirementDef.getMaxOccurrences() == null) {
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), RequirementData.MAX_DEFAULT_OCCURRENCES);
+ } else {
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), requirementDef.getMaxOccurrences());
+ }
+
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(componentInstanceVertex, requirementData, GraphEdgeLabels.CALCULATED_REQUIREMENT, props);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create calculated requirement from component instance {} to requirement {}, error: {}", compoInstId, requirementDef.getUniqueId(), createRelation);
+ return createRelation;
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private Either<List<GraphRelation>, TitanOperationStatus> associateCompInstToCapabilities(ComponentInstanceData componentInstanceData, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType) {
+
+ log.trace("Starting to copy origin component capabilities to its component instance");
+ List<GraphRelation> graphRelations = new ArrayList<>();
+
+ String compInstOriginId = componentInstanceData.getComponentInstDataDefinition().getComponentUid();
+
+ // case of VFC / CP / VL
+ if (compInstNodeType.equals(NodeTypeEnum.Resource)) {
+ createCaculatedRelationsFromAtomicResource(componentInstanceData, graphRelations, compInstOriginId);
+ }
+
+ // case of VF / Service / Product
+ createCalculatedCapabilityRelationsFromComponent(componentInstanceData, containerNodeType, compInstNodeType, graphRelations, compInstOriginId);
+
+ log.trace("Finished to copy origin component capabilities to its component instance, created {} new calculated capability relations", graphRelations.size());
+ return Either.left(graphRelations);
+ }
+
+ private TitanOperationStatus associateCompInstToCapabilities(TitanVertex componentInstanceVertex, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, String originId) {
+
+ log.trace("Starting to copy origin component capabilities to its component instance");
+ TitanOperationStatus status = TitanOperationStatus.OK;
+
+ // case of VFC / CP / VL
+ if (compInstNodeType.equals(NodeTypeEnum.Resource)) {
+ status = createCaculatedRelationsFromAtomicResource(componentInstanceVertex, originId);
+ if (!status.equals(TitanOperationStatus.OK)) {
+ return status;
+ }
+ }
+
+ // case of VF / Service / Product
+ status = createCalculatedCapabilityRelationsFromComponent(componentInstanceVertex, containerNodeType, compInstNodeType, originId);
+
+ return status;
+ }
+
+ private void createCalculatedRequirementRelationsFromComponent(ComponentInstanceData componentInstanceData, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, List<GraphRelation> graphRelations, String compInstOriginId) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> componentInstancesOfComponent = getComponentInstancesOfComponent(compInstOriginId, containerNodeType, compInstNodeType);
+ if (componentInstancesOfComponent.isLeft() && !componentInstancesOfComponent.left().value().left.isEmpty()) {
+ List<ComponentInstance> componentInstances = componentInstancesOfComponent.left().value().left;
+ for (ComponentInstance componentInstance : componentInstances) {
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(containerNodeType), componentInstance.getUniqueId(),
+ GraphEdgeLabels.CALCULATED_REQUIREMENT, NodeTypeEnum.Requirement, RequirementData.class);
+
+ if (childrenNodes.isLeft() && !childrenNodes.left().value().isEmpty()) {
+ List<ImmutablePair<RequirementData, GraphEdge>> list = childrenNodes.left().value();
+ for (ImmutablePair<RequirementData, GraphEdge> calculatedReq : list) {
+
+ GraphEdge edge = calculatedReq.right;
+ Map<String, Object> properties = edge.getProperties();
+ String source = null;
+ String occurrences = RequirementData.MAX_DEFAULT_OCCURRENCES;
+ String minOccurrences = RequirementData.MIN_OCCURRENCES;
+
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.SOURCE.getProperty())) {
+ source = (String) properties.get(GraphEdgePropertiesDictionary.SOURCE.getProperty());
+ }
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty())) {
+ occurrences = (String) properties.get(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ }
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty())) {
+ minOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ }
+
+ String capabilityName = (String) properties.get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ Either<GraphRelation, TitanOperationStatus> createRelation = createCalculatedRequirementEdge(componentInstanceData, source, capabilityName, calculatedReq.left, componentInstance, occurrences, minOccurrences);
+ if (createRelation.isLeft()) {
+ graphRelations.add(createRelation.left().value());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private TitanOperationStatus createCalculatedRequirementRelationsFromComponent(TitanVertex componentInstanceVertex, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, String compInstOriginId) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> componentInstancesOfComponent = getComponentInstancesOfComponent(compInstOriginId, containerNodeType, compInstNodeType);
+ if (componentInstancesOfComponent.isLeft() && !componentInstancesOfComponent.left().value().left.isEmpty()) {
+ List<ComponentInstance> componentInstances = componentInstancesOfComponent.left().value().left;
+ for (ComponentInstance componentInstance : componentInstances) {
+
+ Either<List<ImmutablePair<TitanVertex, Edge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenVertecies(UniqueIdBuilder.getKeyByNodeType(containerNodeType), componentInstance.getUniqueId(),
+ GraphEdgeLabels.CALCULATED_REQUIREMENT);
+
+ if (childrenNodes.isLeft() && !childrenNodes.left().value().isEmpty()) {
+ List<ImmutablePair<TitanVertex, Edge>> list = childrenNodes.left().value();
+ for (ImmutablePair<TitanVertex, Edge> calculatedReq : list) {
+
+ Edge edge = calculatedReq.right;
+ Map<String, Object> properties = titanGenericDao.getProperties(edge);
+ String source = null;
+ String occurrences = RequirementData.MAX_DEFAULT_OCCURRENCES;
+ String minOccurrences = RequirementData.MIN_OCCURRENCES;
+
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.SOURCE.getProperty())) {
+ source = (String) properties.get(GraphEdgePropertiesDictionary.SOURCE.getProperty());
+ }
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty())) {
+ occurrences = (String) properties.get(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ }
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty())) {
+ minOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ }
+
+ String capabilityName = (String) properties.get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ TitanOperationStatus createRelation = createCalculatedRequirementEdge(componentInstanceVertex, source, capabilityName, calculatedReq.left, componentInstance, occurrences, minOccurrences);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create calculated requirement edge, status ", createRelation);
+ return createRelation;
+ }
+ }
+ }
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private void createCalculatedCapabilityRelationsFromComponent(ComponentInstanceData componentInstanceData, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, List<GraphRelation> graphRelations, String compInstOriginId) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> componentInstancesOfComponent = getComponentInstancesOfComponent(compInstOriginId, containerNodeType, compInstNodeType);
+ if (componentInstancesOfComponent.isLeft() && !componentInstancesOfComponent.left().value().left.isEmpty()) {
+ List<ComponentInstance> componentInstances = componentInstancesOfComponent.left().value().left;
+ for (ComponentInstance componentInstance : componentInstances) {
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(compInstNodeType), componentInstance.getUniqueId(),
+ GraphEdgeLabels.CALCULATED_CAPABILITY, NodeTypeEnum.Capability, CapabilityData.class);
+
+ if (childrenNodes.isLeft() && !childrenNodes.left().value().isEmpty()) {
+ List<ImmutablePair<CapabilityData, GraphEdge>> list = childrenNodes.left().value();
+ for (ImmutablePair<CapabilityData, GraphEdge> calculatedCap : list) {
+
+ GraphEdge edge = calculatedCap.right;
+ Map<String, Object> properties = edge.getProperties();
+ String source = null;
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.SOURCE.getProperty())) {
+ source = (String) properties.get(GraphEdgePropertiesDictionary.SOURCE.getProperty());
+ }
+ String minOccurrences = CapabilityData.MIN_OCCURRENCES;
+ String occurrences = CapabilityData.MAX_OCCURRENCES;
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty())) {
+ minOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ }
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty())) {
+ occurrences = (String) properties.get(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ }
+
+ String capabilityName = (String) properties.get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ Either<GraphRelation, TitanOperationStatus> createRelation = createCalculatedCapabilityEdge(componentInstanceData, source, capabilityName, calculatedCap.left, componentInstance.getUniqueId(), minOccurrences, occurrences);
+ if (createRelation.isLeft()) {
+ graphRelations.add(createRelation.left().value());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private TitanOperationStatus createCalculatedCapabilityRelationsFromComponent(TitanVertex componentInstanceVertex, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, String compInstOriginId) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> componentInstancesOfComponent = getComponentInstancesOfComponent(compInstOriginId, containerNodeType, compInstNodeType);
+ if (componentInstancesOfComponent.isLeft() && !componentInstancesOfComponent.left().value().left.isEmpty()) {
+ List<ComponentInstance> componentInstances = componentInstancesOfComponent.left().value().left;
+ for (ComponentInstance componentInstance : componentInstances) {
+ Either<List<ImmutablePair<TitanVertex, Edge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenVertecies(UniqueIdBuilder.getKeyByNodeType(compInstNodeType), componentInstance.getUniqueId(),
+ GraphEdgeLabels.CALCULATED_CAPABILITY);
+
+ if (childrenNodes.isLeft() && !childrenNodes.left().value().isEmpty()) {
+ List<ImmutablePair<TitanVertex, Edge>> list = childrenNodes.left().value();
+ for (ImmutablePair<TitanVertex, Edge> calculatedCap : list) {
+
+ Edge edge = calculatedCap.right;
+ Map<String, Object> properties = titanGenericDao.getProperties(edge);
+ String source = null;
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.SOURCE.getProperty())) {
+ source = (String) properties.get(GraphEdgePropertiesDictionary.SOURCE.getProperty());
+ }
+ String minOccurrences = CapabilityData.MIN_OCCURRENCES;
+ String occurrences = CapabilityData.MAX_OCCURRENCES;
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty())) {
+ minOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ }
+ if (properties != null && properties.containsKey(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty())) {
+ occurrences = (String) properties.get(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ }
+
+ String capabilityName = (String) properties.get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ TitanOperationStatus createRelation = createCalculatedCapabilityEdge(componentInstanceVertex, source, capabilityName, calculatedCap.left, componentInstance.getUniqueId(), minOccurrences, occurrences);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ return createRelation;
+ }
+ }
+ }
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private void createCaculatedRelationsFromAtomicResource(ComponentInstanceData componentInstanceData, List<GraphRelation> graphRelations, String compInstOriginId) {
+
+ Map<String, CapabilityDefinition> capabilities = new HashMap<String, CapabilityDefinition>();
+ Set<String> caseInsensitiveCapNames = new HashSet<>();
+ TitanOperationStatus getAllCapabilities = capabilityOperation.getAllCapabilitiesRecusive(NodeTypeEnum.Resource, compInstOriginId, true, capabilities, caseInsensitiveCapNames, true);
+
+ if (!getAllCapabilities.equals(TitanOperationStatus.OK)) {
+ if (getAllCapabilities != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Couldn't fetch capabilities of component {}, error: {}", compInstOriginId, getAllCapabilities);
+ return;
+ }
+ }
+ log.trace("Found {} capabilities for component {}, ", capabilities.size(), compInstOriginId);
+ for (Entry<String, CapabilityDefinition> capPair : capabilities.entrySet()) {
+ CapabilityDefinition capabilityData = capPair.getValue();
+ log.trace("Creating calculated capability relation from component instance {} to capability {}", componentInstanceData.getUniqueId(), capabilityData.getUniqueId());
+ CapabilityData capabilityDataNode = new CapabilityData();
+ capabilityDataNode.setUniqueId(capabilityData.getUniqueId());
+ String minOccurrences = CapabilityData.MIN_OCCURRENCES;
+ String occurrences = CapabilityData.MAX_OCCURRENCES;
+ if (capabilityData.getMinOccurrences() != null) {
+ minOccurrences = capabilityData.getMinOccurrences();
+ }
+ if (capabilityData.getMinOccurrences() != null) {
+ occurrences = capabilityData.getMaxOccurrences();
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = createCalculatedCapabilityEdge(componentInstanceData, compInstOriginId, capPair.getKey(), capabilityDataNode, componentInstanceData.getUniqueId(), minOccurrences, occurrences);
+ graphRelations.add(createRelation.left().value());
+ }
+ }
+
+ private TitanOperationStatus createCaculatedRelationsFromAtomicResource(TitanVertex componentInstanceVertex, String compInstOriginId) {
+
+ Map<String, CapabilityDefinition> capabilities = new HashMap<String, CapabilityDefinition>();
+ Set<String> caseInsensitiveCapNames = new HashSet<>();
+ TitanOperationStatus getAllCapabilities = capabilityOperation.getAllCapabilitiesRecusive(NodeTypeEnum.Resource, compInstOriginId, true, capabilities, caseInsensitiveCapNames, true);
+
+ if (!getAllCapabilities.equals(TitanOperationStatus.OK)) {
+ if (getAllCapabilities != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Couldn't fetch capabilities of component {}, error: {}", compInstOriginId, getAllCapabilities);
+ }
+ }
+ log.trace("Found {} capabilities for component {}, ", capabilities.size(), compInstOriginId);
+ String compoInstId = (String) titanGenericDao.getProperty(componentInstanceVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+
+ for (Entry<String, CapabilityDefinition> capPair : capabilities.entrySet()) {
+ CapabilityDefinition capabilityData = capPair.getValue();
+ log.trace("Creating calculated capability relation from component instance {} to capability {}", compoInstId, capabilityData.getUniqueId());
+ CapabilityData capabilityDataNode = new CapabilityData();
+ capabilityDataNode.setUniqueId(capabilityData.getUniqueId());
+ String minOccurrences = CapabilityData.MIN_OCCURRENCES;
+ String occurrences = CapabilityData.MAX_OCCURRENCES;
+ if (capabilityData.getMinOccurrences() != null) {
+ minOccurrences = capabilityData.getMinOccurrences();
+ }
+ if (capabilityData.getMinOccurrences() != null) {
+ occurrences = capabilityData.getMaxOccurrences();
+ }
+
+ TitanOperationStatus createRelation = createCalculatedCapabilityEdge(componentInstanceVertex, compInstOriginId, capPair.getKey(), capabilityDataNode, compoInstId, minOccurrences, occurrences);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ return createRelation;
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> createCalculatedCapabilityEdge(ComponentInstanceData componentInstanceData, String compInstOriginId, String capabilityName, CapabilityData capabilityDataNode, String componentInstanceId,
+ String minOccurrences, String occurrences) {
+ Map<String, Object> props = prepareEdgeCapabiltyProperites(compInstOriginId, capabilityName, componentInstanceId, minOccurrences, occurrences);
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(componentInstanceData, capabilityDataNode, GraphEdgeLabels.CALCULATED_CAPABILITY, props);
+ if (createRelation.isRight()) {
+ TitanOperationStatus titanOperationStatus = createRelation.right().value();
+ log.debug("Failed to create calculated capability from component instance {} to capability {}, error: {}", componentInstanceData.getUniqueId(), capabilityDataNode.getUniqueId(), titanOperationStatus);
+ return Either.right(titanOperationStatus);
+ }
+ return createRelation;
+ }
+
+ private TitanOperationStatus createCalculatedCapabilityEdge(TitanVertex componentInstanceVertex, String compInstOriginId, String capabilityName, CapabilityData capabilityDataNode, String componentInstanceId, String minOccurrences,
+ String occurrences) {
+ Map<String, Object> props = prepareEdgeCapabiltyProperites(compInstOriginId, capabilityName, componentInstanceId, minOccurrences, occurrences);
+
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(componentInstanceVertex, capabilityDataNode, GraphEdgeLabels.CALCULATED_CAPABILITY, props);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create calculated capability from component instance {} to capability {}, error: {}", componentInstanceId, capabilityDataNode.getUniqueId(), createRelation);
+ }
+ return createRelation;
+ }
+
+ private TitanOperationStatus createCalculatedCapabilityEdge(TitanVertex componentInstanceVertex, String compInstOriginId, String capabilityName, TitanVertex capabilityDataVertex, String componentInstanceId, String minOccurrences,
+ String occurrences) {
+ Map<String, Object> props = prepareEdgeCapabiltyProperites(compInstOriginId, capabilityName, componentInstanceId, minOccurrences, occurrences);
+
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(componentInstanceVertex, capabilityDataVertex, GraphEdgeLabels.CALCULATED_CAPABILITY, props);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create calculated capability from component instance {} to capability {}, error: {}", componentInstanceId, capabilityName, createRelation);
+ }
+ return createRelation;
+ }
+
+ private Map<String, Object> prepareEdgeCapabiltyProperites(String compInstOriginId, String capabilityName, String componentInstanceId, String minOccurrences, String occurrences) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ if (capabilityName != null)
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capabilityName);
+ if (compInstOriginId != null)
+ props.put(GraphEdgePropertiesDictionary.SOURCE.getProperty(), compInstOriginId);
+ if (componentInstanceId != null) {
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), componentInstanceId);
+ }
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), minOccurrences);
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), occurrences);
+ return props;
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> createCalculatedRequirementEdge(ComponentInstanceData componentInstanceData, String compInstOriginId, String capabilityName, RequirementData requirementData, ComponentInstance componentInstance,
+ String occurrences, String minOccurrences) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ if (capabilityName != null)
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capabilityName);
+ if (compInstOriginId != null)
+ props.put(GraphEdgePropertiesDictionary.SOURCE.getProperty(), compInstOriginId);
+ if (componentInstance != null) {
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), componentInstance.getUniqueId());
+ }
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), occurrences);
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), minOccurrences);
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(componentInstanceData, requirementData, GraphEdgeLabels.CALCULATED_REQUIREMENT, props);
+ if (createRelation.isRight()) {
+ TitanOperationStatus titanOperationStatus = createRelation.right().value();
+ log.debug("Failed to create calculated requirement from component instance {} to requirement {}, error: {}", componentInstanceData.getUniqueId(), requirementData.getUniqueId(), titanOperationStatus);
+ return Either.right(titanOperationStatus);
+ }
+ return createRelation;
+ }
+
+ private TitanOperationStatus createCalculatedRequirementEdge(TitanVertex componentInstanceVertex, String compInstOriginId, String capabilityName, TitanVertex requirementVertex, ComponentInstance componentInstance, String occurrences,
+ String minOccurrences) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ if (capabilityName != null)
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capabilityName);
+ if (compInstOriginId != null)
+ props.put(GraphEdgePropertiesDictionary.SOURCE.getProperty(), compInstOriginId);
+ if (componentInstance != null) {
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), componentInstance.getUniqueId());
+ }
+ props.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), occurrences);
+ props.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), minOccurrences);
+
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(componentInstanceVertex, requirementVertex, GraphEdgeLabels.CALCULATED_REQUIREMENT, props);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ log.debug("Failed to create calculated requirement from component instance {} to requirement {}, error: {}", componentInstance.getUniqueId(), capabilityName, createRelation);
+ return createRelation;
+ }
+ return createRelation;
+ }
+
+ /**
+ * Make a relation between service to resource instance.
+ *
+ * @param containerCompIdData
+ * @param componentInstanceData
+ * @param logicalName
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateContainerCompToComponentInstance(UniqueIdData containerCompIdData, ComponentInstanceData componentInstanceData, String logicalName) {
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ properties.put(GraphPropertiesDictionary.NAME.getProperty(), logicalName);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(containerCompIdData, componentInstanceData, GraphEdgeLabels.RESOURCE_INST, properties);
+
+ log.debug("After associating container component {} to resource instance {} with logical name {}. Status is {}", containerCompIdData.getUniqueId(), componentInstanceData.getUniqueId(), logicalName, createRelation);
+
+ return createRelation;
+ }
+
+ private TitanOperationStatus associateContainerCompToComponentInstance(TitanVertex containerCompVertex, TitanVertex componentInstanceVertex, String logicalName) {
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ properties.put(GraphPropertiesDictionary.NAME.getProperty(), logicalName);
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(containerCompVertex, componentInstanceVertex, GraphEdgeLabels.RESOURCE_INST, properties);
+
+ return createRelation;
+ }
+
+ @Override
+ public String createComponentInstLogicalName(String instanceNumber, String componentInstanceName) {
+
+ String logicalName = buildComponentInstanceLogicalName(instanceNumber, componentInstanceName);
+
+ return logicalName;
+ }
+
+ private String buildComponentInstanceLogicalName(String instanceNumber, String lastToken) {
+ return lastToken + " " + (instanceNumber == null ? 0 : instanceNumber);
+ }
+
+ private ComponentInstanceData buildComponentInstanceData(ComponentInstance resourceInstance, String componentId, String logicalName) {
+
+ String ciOriginComponentUid = resourceInstance.getComponentUid();
+
+ ComponentInstanceDataDefinition dataDefinition = new ComponentInstanceDataDefinition(resourceInstance);
+
+ Long creationDate = resourceInstance.getCreationTime();
+ if (creationDate == null) {
+ creationDate = System.currentTimeMillis();
+ }
+ dataDefinition.setCreationTime(creationDate);
+ dataDefinition.setModificationTime(creationDate);
+ // dataDefinition.setResourceUid(resourceUid);
+ // String replacmentlogicalName = logicalName.replaceAll(" ",
+ // "_").toLowerCase();
+ dataDefinition.setName(logicalName);
+ if (dataDefinition.getNormalizedName() == null)
+ dataDefinition.setNormalizedName(ValidationUtils.normaliseComponentInstanceName(logicalName));
+ dataDefinition.setUniqueId(UniqueIdBuilder.buildResourceInstanceUniuqeId(componentId, ciOriginComponentUid, dataDefinition.getNormalizedName()));
+
+ ComponentInstanceData resourceInstanceData = new ComponentInstanceData(dataDefinition);
+
+ return resourceInstanceData;
+ }
+
+ public Either<ComponentInstance, TitanOperationStatus> removeComponentInstanceFromComponent(NodeTypeEnum containerNodeType, String containerComponentId, String componentInstanceUid) {
+
+ log.debug("Going to delete component instance {} under component {}", componentInstanceUid, containerComponentId);
+
+ Either<ComponentInstanceData, TitanOperationStatus> node = findResourceInstance(componentInstanceUid);
+
+ if (node.isRight()) {
+ TitanOperationStatus status = node.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "Remove Component Instance");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("Remove Component Instance");
+ log.debug("Failed to delete component instance {}. Status is {}", componentInstanceUid, status);
+ return Either.right(status);
+ }
+
+ TitanOperationStatus isComponentInstOfComponent = verifyResourceInstanceUnderComponent(containerNodeType, containerComponentId, componentInstanceUid);
+ if (isComponentInstOfComponent != TitanOperationStatus.OK) {
+ return Either.right(isComponentInstOfComponent);
+ }
+
+ TitanOperationStatus status = deleteOutgoingRelationships(containerNodeType, containerComponentId, componentInstanceUid);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ status = deleteIncomingRelationships(containerNodeType, containerComponentId, componentInstanceUid);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ // delete associated properties
+ status = deleteAssociatedProperties(componentInstanceUid);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ // delete associated properties
+ status = deleteAssociatedAttributes(componentInstanceUid);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ // delete associated artifacts
+ status = deleteAssociatedArtifacts(componentInstanceUid);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ // delete associated capability instances
+ if (containerNodeType.equals(NodeTypeEnum.Resource)) {
+ status = deleteAssociatedCapabilityInstances(componentInstanceUid);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ }
+ Either<ComponentInstanceData, TitanOperationStatus> deleteRI = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), componentInstanceUid, ComponentInstanceData.class);
+
+ if (deleteRI.isRight()) {
+ TitanOperationStatus deleteRiStatus = deleteRI.right().value();
+ log.error("Failed to delete resource instance {}. Status is {}", componentInstanceUid, deleteRiStatus);
+ return Either.right(deleteRiStatus);
+ }
+
+ ComponentInstanceData deletedResourceInst = deleteRI.left().value();
+
+ ComponentInstance resourceInstance = new ComponentInstance(deletedResourceInst.getComponentInstDataDefinition());
+
+ return Either.left(resourceInstance);
+ }
+
+ private TitanOperationStatus deleteAssociatedCapabilityInstances(String resourceInstanceId) {
+ TitanOperationStatus status = TitanOperationStatus.OK;
+
+ log.debug("Before deleting all capability instances of component istance {}.", resourceInstanceId);
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getCapabilityInstancesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId,
+ GraphEdgeLabels.CAPABILITY_INST, NodeTypeEnum.CapabilityInst, CapabilityInstData.class);
+
+ if (getCapabilityInstancesRes.isRight() && !getCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ status = getCapabilityInstancesRes.right().value();
+ log.debug("Failed to retrieve capability Instances of resource instance {}. Status is {}", resourceInstanceId, status);
+ }
+ if (getCapabilityInstancesRes.isLeft()) {
+ for (ImmutablePair<CapabilityInstData, GraphEdge> capabilityInstancePair : getCapabilityInstancesRes.left().value()) {
+ Either<CapabilityInstData, TitanOperationStatus> deleteCababilityInstanceRes = capabilityInstanceOperation.deleteCapabilityInstanceFromResourceInstance(resourceInstanceId, capabilityInstancePair.getLeft().getUniqueId());
+ if (deleteCababilityInstanceRes.isRight()) {
+ status = deleteCababilityInstanceRes.right().value();
+ }
+ }
+ }
+ log.debug("After deleting all capability instances of component istance {}. Status is {}", resourceInstanceId, status);
+ return status;
+ }
+
+ private TitanOperationStatus deleteAssociatedArtifacts(String resourceInstanceUid) {
+
+ Either<List<ImmutablePair<ArtifactData, GraphEdge>>, TitanOperationStatus> artifactRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceUid, GraphEdgeLabels.ARTIFACT_REF,
+ NodeTypeEnum.ArtifactRef, ArtifactData.class);
+
+ if (artifactRes.isRight()) {
+ TitanOperationStatus status = artifactRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find artifacts of resource instance {}. Status is {}", resourceInstanceUid, status);
+ return status;
+ }
+ } else {
+
+ List<ImmutablePair<ArtifactData, GraphEdge>> artifactPairs = artifactRes.left().value();
+ for (ImmutablePair<ArtifactData, GraphEdge> pair : artifactPairs) {
+ String uniqueId = (String) pair.left.getUniqueId();
+ Either<ArtifactData, TitanOperationStatus> removeArifactFromGraph = artifactOperation.removeArtifactOnGraph(resourceInstanceUid, uniqueId, NodeTypeEnum.ResourceInstance, resourceInstanceUid, true);
+ if (removeArifactFromGraph.isRight()) {
+ TitanOperationStatus status = removeArifactFromGraph.right().value();
+ log.error("Failed to delete artifact of resource instance {}. Status is {}", resourceInstanceUid, status);
+ return status;
+ }
+
+ }
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ private TitanOperationStatus deleteAssociatedProperties(String resourceInstanceUid) {
+ final GraphEdgeLabels edgeConectingToRI = GraphEdgeLabels.PROPERTY_VALUE;
+ final NodeTypeEnum elementTypeToDelete = NodeTypeEnum.PropertyValue;
+ return deleteAssociatedRIElements(elementTypeToDelete, edgeConectingToRI, resourceInstanceUid, () -> PropertyValueData.class);
+
+ }
+
+ private TitanOperationStatus deleteAssociatedAttributes(String resourceInstanceUid) {
+ final GraphEdgeLabels edgeConectingToRI = GraphEdgeLabels.ATTRIBUTE_VALUE;
+ final NodeTypeEnum elementTypeToDelete = NodeTypeEnum.AttributeValue;
+ return deleteAssociatedRIElements(elementTypeToDelete, edgeConectingToRI, resourceInstanceUid, () -> AttributeValueData.class);
+ }
+
+ private <T extends GraphNode> TitanOperationStatus deleteAssociatedRIElements(NodeTypeEnum elementTypeToDelete, GraphEdgeLabels edgeConectingToRI, String resourceInstanceUid, Supplier<Class<T>> classGen) {
+
+ Either<List<ImmutablePair<T, GraphEdge>>, TitanOperationStatus> elementsNodesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceUid, edgeConectingToRI, elementTypeToDelete,
+ classGen.get());
+
+ if (elementsNodesRes.isRight()) {
+ TitanOperationStatus status = elementsNodesRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("deleteAssociatedRIElements", "Failed to find the elements of resource instance " + resourceInstanceUid + ". status is " + status, ErrorSeverity.ERROR);
+ return status;
+ }
+ } else {
+
+ List<ImmutablePair<T, GraphEdge>> relationshipNodes = elementsNodesRes.left().value();
+ if (relationshipNodes != null) {
+ for (ImmutablePair<T, GraphEdge> immutablePair : relationshipNodes) {
+ T elementValueDataData = immutablePair.getKey();
+ Either<T, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(elementValueDataData, classGen.get());
+ if (deleteNode.isRight()) {
+ TitanOperationStatus status = deleteNode.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("deleteAssociatedRIElements", "Failed to delete element value node " + elementValueDataData + ". status is " + status, ErrorSeverity.ERROR);
+ return status;
+ }
+ }
+ }
+
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ /**
+ * delete all relationship instance nodes which has an outgoing edge to a given resource instance
+ *
+ * @param resourceInstanceUid
+ * @return
+ */
+ private TitanOperationStatus deleteIncomingRelationships(NodeTypeEnum componentType, String componentId, String resourceInstanceUid) {
+
+ Either<List<RequirementCapabilityRelDef>, TitanOperationStatus> relationsForTarget = getRelationsForTarget(resourceInstanceUid);
+ if (relationsForTarget.isRight()) {
+ TitanOperationStatus status = relationsForTarget.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find the relationships of resource instance {}. Status is {}", resourceInstanceUid, status);
+ return status;
+ }
+ } else {
+ List<RequirementCapabilityRelDef> relList = relationsForTarget.left().value();
+ for (RequirementCapabilityRelDef relation : relList) {
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> dissociateResourceInstances = dissociateResourceInstances(componentId, componentType, relation, true);
+ if (dissociateResourceInstances.isRight()) {
+ log.error("failed to diassociate component instance {} and component instance {} under component {}. error is {}", relation.getFromNode(), relation.getToNode(), componentId);
+ return TitanOperationStatus.GENERAL_ERROR;
+ }
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ /**
+ * delete all relationship instance nodes which has an incoming edge from a given resource instance
+ *
+ * @param resourceInstanceUid
+ * @return
+ */
+ private TitanOperationStatus deleteOutgoingRelationships(NodeTypeEnum componentType, String componentId, String resourceInstanceUid) {
+
+ Either<List<RequirementCapabilityRelDef>, TitanOperationStatus> relationsForSource = getRelationsForSource(resourceInstanceUid);
+ if (relationsForSource.isRight()) {
+ TitanOperationStatus status = relationsForSource.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find the relationships of resource instance " + resourceInstanceUid + ". status is " + status);
+ return status;
+ }
+ } else {
+ List<RequirementCapabilityRelDef> relList = relationsForSource.left().value();
+ for (RequirementCapabilityRelDef relation : relList) {
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> dissociateResourceInstances = dissociateResourceInstances(componentId, componentType, relation, true);
+ if (dissociateResourceInstances.isRight()) {
+ log.error("failed to diassociate component instance {} and component instance {} under component {}. error is {}", relation.getFromNode(), relation.getToNode(), componentId);
+ return TitanOperationStatus.GENERAL_ERROR;
+ }
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ /**
+ * delete relationship instance nodes
+ *
+ * @param relationshipNodes
+ * @return
+ */
+ private TitanOperationStatus deleteRelationshipNodes(List<ImmutablePair<RelationshipInstData, GraphEdge>> relationshipNodes) {
+
+ if (relationshipNodes != null) {
+ for (ImmutablePair<RelationshipInstData, GraphEdge> immutablePair : relationshipNodes) {
+ RelationshipInstData relationshipTypeImplData = immutablePair.getKey();
+ Either<RelationshipInstData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(relationshipTypeImplData, RelationshipInstData.class);
+ if (deleteNode.isRight()) {
+ TitanOperationStatus status = deleteNode.right().value();
+ log.error("Failed to delete relationship node {}. Status is {}", relationshipTypeImplData, status);
+ return status;
+ }
+ }
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ public Either<List<RelationshipInstData>, TitanOperationStatus> disconnectResourcesInService(String componentId, NodeTypeEnum nodeType, RequirementCapabilityRelDef requirementDef) {
+
+ if (requirementDef.getRelationships() == null) {
+ log.debug("No relation pair in request [ {} ]", requirementDef);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+
+ String fromResInstanceUid = requirementDef.getFromNode();
+ String toResInstanceUid = requirementDef.getToNode();
+
+ // DE191707
+ TitanOperationStatus isResourceInstOfService = verifyResourceInstanceUnderComponent(nodeType, componentId, fromResInstanceUid);
+ if (isResourceInstOfService != TitanOperationStatus.OK) {
+ return Either.right(isResourceInstOfService);
+ }
+ isResourceInstOfService = verifyResourceInstanceUnderComponent(nodeType, componentId, toResInstanceUid);
+ if (isResourceInstOfService != TitanOperationStatus.OK) {
+ return Either.right(isResourceInstOfService);
+ }
+
+ List<RequirementAndRelationshipPair> relationPairList = requirementDef.getRelationships();
+
+ Either<TitanVertex, TitanOperationStatus> riFrom = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), fromResInstanceUid);
+ if (riFrom.isRight()) {
+ log.debug("Failed to fetch component instance {}. Error: {}", fromResInstanceUid, riFrom.right().value());
+ return Either.right(riFrom.right().value());
+ }
+ Iterator<Edge> edgeIter = riFrom.left().value().edges(Direction.OUT, GraphEdgeLabels.RELATIONSHIP_INST.getProperty());
+ if (edgeIter == null) {
+ log.debug("No edges with label {} for owner RI {}", GraphEdgeLabels.CALCULATED_REQUIREMENT_FULLFILLED.getProperty(), fromResInstanceUid);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ List<RelationshipInstData> deletedRelations = new ArrayList<>();
+ List<String> vertexToDelete = new ArrayList<>();
+ while (edgeIter.hasNext()) {
+ TitanEdge edge = (TitanEdge) edgeIter.next();
+ String name = (String) edge.property(GraphEdgePropertiesDictionary.NAME.getProperty()).value();
+ for (RequirementAndRelationshipPair relationPair : relationPairList) {
+ if (relationPair.getRequirement().equals(name)) {
+ TitanVertex inVertex = edge.inVertex();
+ String requirementId = (String) titanGenericDao.getProperty(inVertex, GraphPropertiesDictionary.REQUIREMENT_ID.getProperty());
+ String capabiltyId = (String) titanGenericDao.getProperty(inVertex, GraphPropertiesDictionary.CAPABILITY_ID.getProperty());
+ String requirementOwnerId = (String) titanGenericDao.getProperty(inVertex, GraphPropertiesDictionary.REQUIREMENT_OWNER_ID.getProperty());
+ String capabiltyOwnerId = (String) titanGenericDao.getProperty(inVertex, GraphPropertiesDictionary.CAPABILITY_OWNER_ID.getProperty());
+ String relationVertexId = (String) titanGenericDao.getProperty(inVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+
+ // verify vs requirement id and owner ids. ( for
+ // requirements with same name)
+ if (requirementId.equals(relationPair.getRequirementUid()) && capabiltyId.equals(relationPair.getCapabilityUid()) && requirementOwnerId.equals(relationPair.getRequirementOwnerId())
+ && capabiltyOwnerId.equals(relationPair.getCapabilityOwnerId())) {
+ vertexToDelete.add(relationVertexId);
+ }
+ }
+ }
+ }
+ log.debug("relation node with ids: {} are going to be deleted", vertexToDelete);
+ for (String relationVertexId : vertexToDelete) {
+ // remove relation vertex
+ Either<RelationshipInstData, TitanOperationStatus> relationNode = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.RelationshipInst), relationVertexId, RelationshipInstData.class);
+ if (relationNode.isRight()) {
+ log.debug("Failed to delete relation node with id {}. Error: {}", relationVertexId, relationNode.right().value());
+ return Either.right(relationNode.right().value());
+ }
+ RelationshipInstData deletedRelation = relationNode.left().value();
+ deletedRelations.add(deletedRelation);
+ }
+ if (deletedRelations.size() > 0) {
+ return Either.left(deletedRelations);
+ }
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ @Override
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> dissociateResourceInstances(String componentId, NodeTypeEnum nodeType, RequirementCapabilityRelDef requirementDef, boolean inTransaction) {
+
+ String fromResInstanceUid = requirementDef.getFromNode();
+ String toResInstanceUid = requirementDef.getToNode();
+ String requirement = requirementDef.getRelationships().get(0).getRequirement();
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> result = null;
+ try {
+
+ Either<List<RelationshipInstData>, TitanOperationStatus> dissociateRes = disconnectResourcesInService(componentId, nodeType, requirementDef);
+ if (dissociateRes.isRight()) {
+ TitanOperationStatus status = dissociateRes.right().value();
+ log.error("Failed to dissociate resource instance " + fromResInstanceUid + " from resource instance " + toResInstanceUid + " in service " + componentId + ". status is " + status);
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("dissociateComponentInstances");
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ StorageOperationStatus updateCalculatedCapReqResult = updateCalculatedCapReq(requirementDef, false);
+ if (!updateCalculatedCapReqResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "dissociateComponentInstances");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("dissociateComponentInstances");
+ log.debug("Failed to dissociate component instances {}. Status is {}", requirementDef, updateCalculatedCapReqResult);
+ result = Either.right(updateCalculatedCapReqResult);
+ return result;
+ }
+
+ // RelationshipInstData relationshipInstData =
+ // dissociateRes.left().value();
+ List<RelationshipInstData> relationshipInstData = dissociateRes.left().value();
+ RequirementCapabilityRelDef capabilityRelDef = buildCapabilityResult(fromResInstanceUid, toResInstanceUid, requirement, relationshipInstData);
+
+ result = Either.left(capabilityRelDef);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+
+ }
+
+ private StorageOperationStatus updateCalculatedCapReq(RequirementCapabilityRelDef capabilityRelDef, boolean associate) {
+ GraphEdgeLabels requirmentNewLabel = associate ? GraphEdgeLabels.CALCULATED_REQUIREMENT_FULLFILLED : GraphEdgeLabels.CALCULATED_REQUIREMENT;
+
+ GraphEdgeLabels requirmentCurrentLabel = associate ? GraphEdgeLabels.CALCULATED_REQUIREMENT : GraphEdgeLabels.CALCULATED_REQUIREMENT_FULLFILLED;
+
+ GraphEdgeLabels capabilityNewLabel = associate ? GraphEdgeLabels.CALCULATED_CAPABILITY_FULLFILLED : GraphEdgeLabels.CALCULATED_CAPABILITY;
+
+ GraphEdgeLabels capabilityCurrentLabel = associate ? GraphEdgeLabels.CALCULATED_CAPABILITY : GraphEdgeLabels.CALCULATED_CAPABILITY_FULLFILLED;
+
+ List<RequirementAndRelationshipPair> relationships = capabilityRelDef.getRelationships();
+ for (RequirementAndRelationshipPair pair : relationships) {
+ StorageOperationStatus status = updateRequirementEdges(requirmentNewLabel, requirmentCurrentLabel, pair, capabilityRelDef.getFromNode());
+ if (!status.equals(StorageOperationStatus.OK)) {
+ return status;
+ }
+ status = updateCapabiltyEdges(capabilityNewLabel, capabilityCurrentLabel, pair, capabilityRelDef.getToNode());
+ if (!status.equals(StorageOperationStatus.OK)) {
+ return status;
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus updateRequirementEdges(GraphEdgeLabels requirmentNewLabel, GraphEdgeLabels requirmentCurrentLabel, RequirementAndRelationshipPair pair, String requirementOwnerId) {
+ Either<TitanVertex, TitanOperationStatus> reqOwnerRI = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), requirementOwnerId);
+ if (reqOwnerRI.isRight()) {
+ log.debug("Failed to fetch requirment Owner by Id {}. Error: {}", requirementOwnerId, reqOwnerRI.right().value());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(reqOwnerRI.right().value());
+ }
+ Iterator<Edge> edgeIter = reqOwnerRI.left().value().edges(Direction.OUT, requirmentCurrentLabel.name(), requirmentNewLabel.name());
+ if (edgeIter == null) {
+ log.debug("No edges with label {} for woner RI {}", requirmentCurrentLabel, requirementOwnerId);
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ boolean associate = requirmentNewLabel.equals(GraphEdgeLabels.CALCULATED_REQUIREMENT_FULLFILLED) ? true : false;
+ while (edgeIter.hasNext()) {
+ TitanEdge edge = (TitanEdge) edgeIter.next();
+ String name = (String) edge.property(GraphEdgePropertiesDictionary.NAME.getProperty()).value();
+ if (pair.getRequirement().equals(name)) {
+ TitanVertex reqVertex = edge.inVertex();
+ String requirementId = (String) titanGenericDao.getProperty(reqVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ // verify vs requirement id . ( for requirements with same name)
+ if (requirementId.equals(pair.getRequirementUid())) {
+ String ownerIdOnEdge = (String) edge.value(GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+ if (ownerIdOnEdge.equals(pair.getRequirementOwnerId())) {
+ String requiredOccurrences = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ String leftOccurrences = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+
+ String requiredOccurrencesNew = "0";
+ String leftOccurrencesNew = RequirementData.MAX_DEFAULT_OCCURRENCES;
+ if (requiredOccurrences != null) {
+ Integer iOccurrences = Integer.parseInt(requiredOccurrences);
+ if (associate) {
+ if (iOccurrences > 0) {
+ iOccurrences--;
+ requiredOccurrencesNew = iOccurrences.toString();
+ }
+ } else {
+ String reqMinOccurrences = (String) titanGenericDao.getProperty(reqVertex, GraphPropertiesDictionary.MIN_OCCURRENCES.getProperty());
+ if (reqMinOccurrences == null) {
+ reqMinOccurrences = RequirementData.MIN_OCCURRENCES;
+ }
+ if (Integer.parseInt(reqMinOccurrences) > iOccurrences) {
+ iOccurrences++;
+ requiredOccurrencesNew = iOccurrences.toString();
+ }
+ }
+ }
+ Map<String, Object> properties = titanGenericDao.getProperties(edge);
+ properties.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), requiredOccurrencesNew);
+
+ if (leftOccurrences != null && !leftOccurrences.equals(RequirementData.MAX_OCCURRENCES)) {
+ Integer iOccurrences = Integer.parseInt(leftOccurrences);
+ if (associate) {
+ if (iOccurrences > 0) {
+ iOccurrences--;
+ }
+ } else {
+ iOccurrences++;
+ }
+ leftOccurrencesNew = iOccurrences.toString();
+ properties.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), leftOccurrencesNew);
+ if ((associate && iOccurrences == 0) || (!associate && iOccurrences == 1)) {
+ // move edge to full filled state
+ TitanVertex outVertex = edge.outVertex();
+ TitanEdge newEdge = outVertex.addEdge(requirmentNewLabel.getProperty(), reqVertex);
+ titanGenericDao.setProperties(newEdge, properties);
+ edge.remove();
+ } else {
+ titanGenericDao.setProperties(edge, properties);
+ }
+ } else {
+ leftOccurrencesNew = leftOccurrences;
+ properties.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), leftOccurrencesNew);
+ titanGenericDao.setProperties(edge, properties);
+ }
+ break;
+ }
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+
+ }
+
+ private StorageOperationStatus updateCapabiltyEdges(GraphEdgeLabels capabiltyNewLabel, GraphEdgeLabels capabiltyCurrentLabel, RequirementAndRelationshipPair pair, String capabiltyOwnerId) {
+ Either<TitanVertex, TitanOperationStatus> capOwnerRI = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), capabiltyOwnerId);
+ if (capOwnerRI.isRight()) {
+ log.debug("Failed to fetch requirment Owner by Id {}. Error: {}", capabiltyOwnerId, capOwnerRI.right().value());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(capOwnerRI.right().value());
+ }
+ Iterator<Edge> edgeIter = capOwnerRI.left().value().edges(Direction.OUT, capabiltyCurrentLabel.name(), capabiltyNewLabel.name());
+ if (edgeIter == null) {
+ log.debug("No edges with label {} for owner RI {}", capabiltyCurrentLabel, capabiltyOwnerId);
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ boolean associate = capabiltyNewLabel.equals(GraphEdgeLabels.CALCULATED_CAPABILITY_FULLFILLED) ? true : false;
+
+ while (edgeIter.hasNext()) {
+ TitanEdge edge = (TitanEdge) edgeIter.next();
+ TitanVertex capVertex = edge.inVertex();
+ // edge.property(GraphEdgePropertiesDictionary.NAME.getProperty()).value();
+
+ String capabiltyId = (String) titanGenericDao.getProperty(capVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ // verify vs capability id . ( for capabilty with same name)
+ if (capabiltyId.equals(pair.getCapabilityUid())) {
+ String ownerIdOnEdge = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+ if (ownerIdOnEdge.equals(pair.getCapabilityOwnerId())) {
+
+ String requiredOccurrences = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ String leftOccurrences = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+
+ String requiredOccurrencesNew = "0";
+ String leftOccurrencesNew = CapabilityData.MAX_OCCURRENCES;
+ if (requiredOccurrences != null) {
+ Integer iOccurrences = Integer.parseInt(requiredOccurrences);
+ if (associate) {
+ if (iOccurrences > 0) {
+ iOccurrences--;
+ requiredOccurrencesNew = iOccurrences.toString();
+ }
+ } else {
+ String reqMinOccurrences = (String) titanGenericDao.getProperty(capVertex, GraphPropertiesDictionary.MIN_OCCURRENCES.getProperty());
+ if (reqMinOccurrences == null) {
+ reqMinOccurrences = CapabilityData.MIN_OCCURRENCES;
+ }
+ if (Integer.parseInt(reqMinOccurrences) > iOccurrences) {
+ iOccurrences++;
+ requiredOccurrencesNew = iOccurrences.toString();
+ }
+ }
+ }
+ Map<String, Object> properties = titanGenericDao.getProperties(edge);
+ properties.put(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty(), requiredOccurrencesNew);
+
+ if (leftOccurrences != null && !leftOccurrences.equals(CapabilityData.MAX_OCCURRENCES)) {
+ Integer iOccurrences = Integer.parseInt(leftOccurrences);
+ if (associate) {
+ if (iOccurrences > 0) {
+ iOccurrences--;
+ }
+ } else {
+ iOccurrences++;
+ }
+ leftOccurrencesNew = iOccurrences.toString();
+ properties.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), leftOccurrencesNew);
+ if ((associate && iOccurrences == 0) || (!associate && iOccurrences == 1)) {
+ // move edge to full filled state
+ TitanVertex outVertex = edge.outVertex();
+ TitanEdge newEdge = outVertex.addEdge(capabiltyNewLabel.getProperty(), capVertex);
+ titanGenericDao.setProperties(newEdge, properties);
+ edge.remove();
+ } else {
+ titanGenericDao.setProperties(edge, properties);
+ }
+ } else {
+ properties.put(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty(), leftOccurrencesNew);
+ titanGenericDao.setProperties(edge, properties);
+ }
+ break;
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> dissociateResourceInstances(String serviceId, NodeTypeEnum nodeType, RequirementCapabilityRelDef requirementDef) {
+
+ return dissociateResourceInstances(serviceId, nodeType, requirementDef, false);
+ }
+
+ private RequirementCapabilityRelDef buildCapabilityResult(String fromResInstanceUid, String toResInstanceUid, String requirement, List<RelationshipInstData> relationshipInstDataList) {
+
+ RequirementCapabilityRelDef capabilityRelDef = new RequirementCapabilityRelDef();
+ capabilityRelDef.setFromNode(fromResInstanceUid);
+ capabilityRelDef.setToNode(toResInstanceUid);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<RequirementAndRelationshipPair>();
+ for (RelationshipInstData relationshipInstData : relationshipInstDataList) {
+ RelationshipImpl relationshipImpl = new RelationshipImpl();
+ relationshipImpl.setType(relationshipInstData.getType());
+ RequirementAndRelationshipPair reqRel = new RequirementAndRelationshipPair(requirement, relationshipImpl);
+ capabilityRelDef.setRelationships(relationships);
+ reqRel.setCapabilityOwnerId(relationshipInstData.getCapabilityOwnerId());
+ reqRel.setCapabilityUid(relationshipInstData.getCapabiltyId());
+ reqRel.setRequirementOwnerId(relationshipInstData.getRequirementOwnerId());
+ reqRel.setRequirementUid(relationshipInstData.getRequirementId());
+ relationships.add(reqRel);
+ }
+ return capabilityRelDef;
+
+ }
+
+ public Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService(String componentId, NodeTypeEnum nodeType, String fromResInstanceUid, String toResInstanceUid, RequirementAndRelationshipPair relationPair) {
+ String relationship = null;
+ String requirement = relationPair.getRequirement();
+ if (relationPair.getRelationship() != null) {
+ relationship = relationPair.getRelationship().getType();
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Going to associate resource instance {} to resource instance {} under component {}. Requirement is {}.", fromResInstanceUid, toResInstanceUid, componentId, requirement);
+ }
+
+ Either<ComponentInstanceData, TitanOperationStatus> fromResourceInstDataRes = findMandatoryResourceInstData(fromResInstanceUid);
+ if (fromResourceInstDataRes.isRight()) {
+ TitanOperationStatus status = fromResourceInstDataRes.right().value();
+ log.error("Failed to find resource instance {}. Status is {}", fromResInstanceUid, status);
+ return Either.right(status);
+ }
+ ComponentInstanceData fromResourceInstanceData = fromResourceInstDataRes.left().value();
+ Either<ComponentInstanceData, TitanOperationStatus> toResourceInstDataRes = findMandatoryResourceInstData(toResInstanceUid);
+ if (toResourceInstDataRes.isRight()) {
+ TitanOperationStatus status = toResourceInstDataRes.right().value();
+ log.error("Failed to find resource instance " + toResInstanceUid + ". status is " + status);
+ return Either.right(status);
+ }
+ ComponentInstanceData toResourceInstanceData = toResourceInstDataRes.left().value();
+ // THE component NodeTypeEnum should be sent
+ TitanOperationStatus isResourceInstOfService = verifyResourceInstanceUnderComponent(nodeType, componentId, fromResInstanceUid);
+ if (isResourceInstOfService != TitanOperationStatus.OK) {
+ return Either.right(isResourceInstOfService);
+ }
+ isResourceInstOfService = verifyResourceInstanceUnderComponent(nodeType, componentId, toResInstanceUid);
+ if (isResourceInstOfService != TitanOperationStatus.OK) {
+ return Either.right(isResourceInstOfService);
+ }
+
+ Either<ImmutablePair<RelationshipTypeData, String>, TitanOperationStatus> isValidRes = validateRequirementVsCapability(fromResourceInstanceData, toResourceInstanceData, requirement, relationship, relationPair);
+ if (isValidRes.isRight()) {
+ TitanOperationStatus status = isValidRes.right().value();
+ log.error("Failed to validate requirement {} between resource instance {} to resource instance {}. Status is {}", requirement, fromResInstanceUid, toResInstanceUid, status);
+ return Either.right(status);
+ }
+
+ RelationshipTypeData relationshipTypeData = isValidRes.left().value().getKey();
+ String capabilityName = isValidRes.left().value().getValue();
+ RelationshipInstData relationshipInstData = buildRelationshipInstData(fromResInstanceUid, requirement, relationshipTypeData, relationPair);
+ Either<RelationshipInstData, TitanOperationStatus> createNode = createRelationshipInstData(fromResourceInstDataRes.left().value(), relationshipInstData, relationshipTypeData, requirement);
+
+ if (createNode.isRight()) {
+ return Either.right(createNode.right().value());
+ }
+ RelationshipInstData createdRelInstData = createNode.left().value();
+ Either<GraphRelation, TitanOperationStatus> associateResInst = associateRelationshipInstToTarget(toResourceInstDataRes.left().value(), requirement, capabilityName, createdRelInstData);
+
+ if (associateResInst.isRight()) {
+ TitanOperationStatus status = associateResInst.right().value();
+ log.error("Failed to associate relationship instance {} to target node {}. Status is {}", createdRelInstData.getUniqueId(), toResInstanceUid, status);
+ return Either.right(status);
+ }
+
+ return Either.left(createNode.left().value());
+ }
+
+ private TitanOperationStatus verifyResourceInstanceUnderComponent(NodeTypeEnum containerNodeType, String containerComponentId, String resInstanceUid) {
+
+ Either<ImmutablePair<ComponentMetadataData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resInstanceUid, GraphEdgeLabels.RESOURCE_INST,
+ containerNodeType, ComponentMetadataData.class);
+
+ if (parentNode.isRight()) {
+ TitanOperationStatus status = parentNode.right().value();
+ log.error("Failed to find the service associated to the resource instance {}. Status is {}", resInstanceUid, status);
+ return status;
+ }
+
+ ImmutablePair<ComponentMetadataData, GraphEdge> componentsRes = parentNode.left().value();
+ ComponentMetadataData componentMetadataData = componentsRes.getKey();
+ String uniqueId = (String) componentMetadataData.getUniqueId();
+
+ if (containerComponentId.equals(uniqueId)) {
+ return TitanOperationStatus.OK;
+ } else {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeIncorrectServiceError, "Resource Instance - verifyResourceInstanceUnderComponent", containerComponentId);
+ BeEcompErrorManager.getInstance().logBeIncorrectComponentError("Resource Instance - verifyResourceInstanceUnderComponent", containerNodeType.getName(), containerComponentId);
+ log.debug("The provided component id {} is not equal to the component ({}) which associated to resource instance {}.", containerComponentId, uniqueId, resInstanceUid);
+ return TitanOperationStatus.INVALID_ID;
+ }
+
+ }
+
+ /**
+ * find the resource instance node in graph.
+ *
+ * @param resInstanceUid
+ * @return
+ */
+ private Either<ComponentInstanceData, TitanOperationStatus> findMandatoryResourceInstData(String resInstanceUid) {
+ Either<ComponentInstanceData, TitanOperationStatus> resStatus = findResourceInstance(resInstanceUid);
+ if (resStatus.isRight()) {
+ TitanOperationStatus status = resStatus.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ return Either.right(status);
+ }
+ ComponentInstanceData riData = resStatus.left().value();
+ return Either.left(riData);
+ }
+
+ /**
+ * associate relationship instance node to the target resource instance node.
+ *
+ * @param toResInstance
+ * @param requirement
+ * @param relInstData
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateRelationshipInstToTarget(ComponentInstanceData toResInstance, String requirement, String capabilityName, RelationshipInstData relInstData) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), capabilityName);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(relInstData, toResInstance, GraphEdgeLabels.CAPABILITY_NODE, props);
+ log.debug("After creating relation between relationship instance {} to target node {}", relInstData.getUniqueId(), toResInstance.getUniqueId());
+
+ return createRelation;
+
+ }
+
+ /**
+ * create reslationship instance node and associate the reosurce instance node to it.
+ *
+ * @param resInstance
+ * @param relationshipInstData
+ * @param relationshipTypeData
+ * @param requirementName
+ * @return
+ */
+ private Either<RelationshipInstData, TitanOperationStatus> createRelationshipInstData(ComponentInstanceData resInstance, RelationshipInstData relationshipInstData, RelationshipTypeData relationshipTypeData, String requirementName) {
+
+ Either<RelationshipInstData, TitanOperationStatus> createNode = titanGenericDao.createNode(relationshipInstData, RelationshipInstData.class);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ log.error("Failed to create relationship instance node in graph. status is {}", status);
+ return Either.right(status);
+ }
+
+ RelationshipInstData createdRelationshipInst = createNode.left().value();
+
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put("name", requirementName);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(resInstance, createdRelationshipInst, GraphEdgeLabels.RELATIONSHIP_INST, properties);
+ if (createRelation.isRight()) {
+ TitanOperationStatus status = createRelation.right().value();
+ log.error("Failed to associate resource instance " + resInstance.getUniqueIdKey() + " to relationship instance " + createdRelationshipInst.getUniqueId() + ". status is " + status);
+ return Either.right(status);
+ }
+
+ return Either.left(createdRelationshipInst);
+ }
+
+ /**
+ * check whether we can associate resource instances for a given requirement.
+ *
+ * 1. check the source resource instance contains the requirement
+ *
+ * 2. check the target resource instance contains a capability with the same name as the requirement
+ *
+ * @param fromResInstance
+ * @param toResInstance
+ * @param requirement
+ * @param relationship
+ * @param relationPair
+ * @return
+ */
+ private Either<ImmutablePair<RelationshipTypeData, String>, TitanOperationStatus> validateRequirementVsCapability(ComponentInstanceData fromResInstance, ComponentInstanceData toResInstance, String requirement, String relationship,
+ RequirementAndRelationshipPair relationPair) {
+
+ String fromResourceUid = fromResInstance.getComponentInstDataDefinition().getComponentUid();
+
+ String toResourceUid = toResInstance.getComponentInstDataDefinition().getComponentUid();
+ Either<CapabilityDefinition, StorageOperationStatus> capabilityDefinitionE = capabilityOperation.getCapability(relationPair.getCapabilityUid(), true);
+ if (capabilityDefinitionE.isRight()) {
+ log.error("The capability cannot be found {}", relationPair.getCapabilityUid());
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ Either<RequirementDefinition, TitanOperationStatus> requirementDefinitionE = requirementOperation.getRequirement(relationPair.getRequirementUid());
+ if (requirementDefinitionE.isRight()) {
+ log.error("The requirement cannot be found {}" , relationPair.getRequirementUid());
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ RequirementDefinition requirementDefinition = requirementDefinitionE.left().value();
+ String fetchedRequirementRelationship = requirementDefinition.getRelationship();
+
+ String fetchedRequirementCapability = requirementDefinition.getCapability();
+ // TODO temporary remove of capability sources validation - uncomment
+ // after alignment
+ // String fetchedRequirementNodeName = requirementDefinition.getNode();
+
+ TitanOperationStatus status = validateAvailableRequirement(fromResInstance, relationPair);
+ if (!status.equals(TitanOperationStatus.OK)) {
+ log.error("The requirement isn't available, status {}", status);
+ return Either.right(status);
+ }
+ status = validateAvailableCapabilty(toResInstance, relationPair);
+ if (!status.equals(TitanOperationStatus.OK)) {
+ log.error("The capabilty isn't available, status {}", status);
+ return Either.right(status);
+ }
+ Either<ComponentInstanceData, TitanOperationStatus> originCapabilty = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), relationPair.getCapabilityOwnerId(), ComponentInstanceData.class);
+ if (originCapabilty.isRight()) {
+ log.error("Failed to fetch the origin resource for capabilty resource instance with id {}, error {}", relationPair.getCapabilityOwnerId(), originCapabilty.right().value());
+ return Either.right(originCapabilty.right().value());
+ }
+ // TODO temporary remove of capability sources validation - uncomment
+ // after alignment
+ // String originCapabId =
+ // originCapabilty.left().value().getComponentInstDataDefinition().getComponentUid();
+
+ // List<String> capabilitySources = new ArrayList<>();
+ // TitanOperationStatus capabiltySourcesResult =
+ // resourceOperation.fillResourceDerivedListFromGraph(originCapabId,
+ // capabilitySources);
+ // if (!TitanOperationStatus.OK.equals(capabiltySourcesResult)) {
+ // log.error("Failed to fill capabilty cources for resource with id " +
+ // originCapabId + " , error " + capabiltySourcesResult);
+ // return Either.right(originCapabilty.right().value());
+ // }
+ CapabilityDefinition capabilityDefinition = capabilityDefinitionE.left().value();
+ String capabilityName = requirement;
+
+ if (log.isDebugEnabled()) {
+ log.debug("The capability {} of resource {} appropriates to requiremt {} on resource {}", capabilityDefinition, toResourceUid, requirement, fromResourceUid);
+ }
+ String capabilityType = capabilityDefinition.getType();
+
+ if (false == fetchedRequirementCapability.equals(capabilityType)) {
+ log.error("The capability type in the requirement ({}) does not equal to the capability on the resource {}({})", fetchedRequirementCapability, toResourceUid, capabilityType);
+ return Either.right(TitanOperationStatus.MATCH_NOT_FOUND);
+ }
+
+ // if (fetchedRequirementNodeName != null &&
+ // !capabilitySources.contains(fetchedRequirementNodeName)) {
+ // log.error("The target resource instance " + toResourceUid + " is not
+ // of type " + fetchedRequirementNodeName);
+ // return Either.right(TitanOperationStatus.MATCH_NOT_FOUND);
+ // }
+
+ RelationshipTypeData relationshipTypeData = new RelationshipTypeData();
+ relationshipTypeData.getRelationshipTypeDataDefinition().setType(fetchedRequirementRelationship);
+
+ ImmutablePair<RelationshipTypeData, String> result = new ImmutablePair<RelationshipTypeData, String>(relationshipTypeData, capabilityName);
+ return Either.left(result);
+ }
+
+ private TitanOperationStatus validateAvailableRequirement(ComponentInstanceData fromResInstance, RequirementAndRelationshipPair relationPair) {
+ Either<TitanVertex, TitanOperationStatus> fromRi = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), fromResInstance.getUniqueId());
+ if (fromRi.isRight()) {
+ log.debug("Failed to fetch component instance {}. Error: {}", fromResInstance.getUniqueId(), fromRi.right().value());
+ return fromRi.right().value();
+ }
+ Iterator<Edge> edgeIter = fromRi.left().value().edges(Direction.OUT, GraphEdgeLabels.CALCULATED_REQUIREMENT.name());
+ if (edgeIter == null || !edgeIter.hasNext()) {
+ log.debug("No available CALCULATED_REQUIREMENT edges. All full filled for RI {}", fromResInstance.getUniqueId());
+ return TitanOperationStatus.MATCH_NOT_FOUND;
+ }
+ boolean exist = false;
+ while (edgeIter.hasNext()) {
+ Edge edge = edgeIter.next();
+ TitanVertex reqVertex = (TitanVertex) edge.inVertex();
+ String reqId = (String) titanGenericDao.getProperty(reqVertex, UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement));
+ if (reqId.equals(relationPair.getRequirementUid())) {
+ String ownerIdOnEdge = (String) edge.value(GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+ if (ownerIdOnEdge.equals(relationPair.getRequirementOwnerId())) {
+ String leftOccurrences = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ if (leftOccurrences != null && !leftOccurrences.equals(RequirementData.MAX_OCCURRENCES)) {
+ Integer leftIntValue = Integer.parseInt(leftOccurrences);
+ if (leftIntValue > 0) {
+ exist = true;
+ }
+ } else {
+ exist = true;
+ }
+ break;
+ }
+ }
+ }
+ return exist ? TitanOperationStatus.OK : TitanOperationStatus.MATCH_NOT_FOUND;
+ }
+
+ private TitanOperationStatus validateAvailableCapabilty(ComponentInstanceData toResInstance, RequirementAndRelationshipPair relationPair) {
+ Either<TitanVertex, TitanOperationStatus> fromRi = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), toResInstance.getUniqueId());
+ if (fromRi.isRight()) {
+ log.debug("Failed to fetch component instance {}. error {}", toResInstance.getUniqueId(), fromRi.right().value());
+ return fromRi.right().value();
+ }
+ Iterator<Edge> edgeIter = fromRi.left().value().edges(Direction.OUT, GraphEdgeLabels.CALCULATED_CAPABILITY.name());
+ if (edgeIter == null || !edgeIter.hasNext()) {
+ log.debug("No available CALCULATED_CAPABILITY edges. All full filled for RI {}", toResInstance.getUniqueId());
+ return TitanOperationStatus.MATCH_NOT_FOUND;
+ }
+ boolean exist = false;
+ while (edgeIter.hasNext()) {
+ Edge edge = edgeIter.next();
+ TitanVertex reqVertex = (TitanVertex) edge.inVertex();
+ String capId = (String) titanGenericDao.getProperty(reqVertex, UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability));
+ if (capId.equals(relationPair.getCapabilityUid())) {
+ String ownerIdOnEdge = (String) edge.value(GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+ if (ownerIdOnEdge.equals(relationPair.getCapabilityOwnerId())) {
+ String leftOccurrences = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ if (leftOccurrences != null && !leftOccurrences.equals(CapabilityData.MAX_OCCURRENCES)) {
+ Integer leftIntValue = Integer.parseInt(leftOccurrences);
+ if (leftIntValue > 0) {
+ exist = true;
+ }
+ } else {
+ exist = true;
+ }
+ break;
+ }
+ }
+ }
+ return exist ? TitanOperationStatus.OK : TitanOperationStatus.NOT_FOUND;
+ }
+
+ private List<ImmutablePair<String, CapabilityDefinition>> findCapabilityOfType(Map<String, CapabilityDefinition> capabilities, String fetchedRequirementCapability) {
+
+ List<ImmutablePair<String, CapabilityDefinition>> result = new ArrayList<ImmutablePair<String, CapabilityDefinition>>();
+
+ if (capabilities == null) {
+ return null;
+ }
+
+ for (Entry<String, CapabilityDefinition> entry : capabilities.entrySet()) {
+ CapabilityDefinition capabilityDefinition = entry.getValue();
+ String type = capabilityDefinition.getType();
+ if (fetchedRequirementCapability.equals(type)) {
+ ImmutablePair<String, CapabilityDefinition> pair = new ImmutablePair<String, CapabilityDefinition>(entry.getKey(), capabilityDefinition);
+ result.add(pair);
+ }
+ }
+
+ return result;
+ }
+
+ protected TitanOperationStatus validateTheTargetResourceInstance(String fetchedRequirementNodeName, String resourceUid) {
+
+ if (fetchedRequirementNodeName == null) {
+ return TitanOperationStatus.OK;
+ }
+
+ List<ResourceMetadataData> resourcesPathList = new ArrayList<ResourceMetadataData>();
+ TitanOperationStatus status = resourceOperation.findResourcesPathRecursively(resourceUid, resourcesPathList);
+ if (status != TitanOperationStatus.OK) {
+ log.error("Failed to find the parent list of resource {}. Status is {}", resourceUid, status);
+ return status;
+ }
+
+ boolean found = false;
+ if (resourcesPathList != null) {
+ for (ResourceMetadataData resourceData : resourcesPathList) {
+ String resourceName = resourceData.getMetadataDataDefinition().getName();
+ if (fetchedRequirementNodeName.equals(resourceName)) {
+ found = true;
+ log.debug("The resource {} is of type {}", resourceData.getUniqueId(), fetchedRequirementNodeName);
+ break;
+ }
+ }
+ }
+
+ if (true == found) {
+ return TitanOperationStatus.OK;
+ } else {
+ return TitanOperationStatus.MATCH_NOT_FOUND;
+ }
+
+ }
+
+ private RelationshipInstData buildRelationshipInstData(String fromResInstanceUid, String requirement, RelationshipTypeData relationshipTypeData, RequirementAndRelationshipPair relationPair) {
+
+ RelationshipInstData relationshipInstData = new RelationshipInstData();
+ relationshipInstData.setUniqueId(UniqueIdBuilder.buildRelationsipInstInstanceUid(fromResInstanceUid, requirement));
+ String type = null;
+ if (relationshipTypeData != null) {
+ type = relationshipTypeData.getRelationshipTypeDataDefinition().getType();
+ }
+
+ relationshipInstData.setType(type);
+ Long creationDate = System.currentTimeMillis();
+ relationshipInstData.setCreationTime(creationDate);
+ relationshipInstData.setModificationTime(creationDate);
+ relationshipInstData.setCapabilityOwnerId(relationPair.getCapabilityOwnerId());
+ relationshipInstData.setRequirementOwnerId(relationPair.getRequirementOwnerId());
+ relationshipInstData.setCapabiltyId(relationPair.getCapabilityUid());
+ relationshipInstData.setRequirementId(relationPair.getRequirementUid());
+
+ return relationshipInstData;
+ }
+
+ private Either<ComponentInstanceData, TitanOperationStatus> findResourceInstance(String resInstanceUid) {
+
+ Either<ComponentInstanceData, TitanOperationStatus> node = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resInstanceUid, ComponentInstanceData.class);
+
+ return node;
+
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> updateResourceInstance(String serviceId, NodeTypeEnum nodeType, String resourceInstanceUid, ComponentInstance resourceInstance, boolean inTransaction) {
+
+ Either<ComponentInstance, StorageOperationStatus> result = null;
+ try {
+
+ Either<ComponentInstance, TitanOperationStatus> updateRes = updateResourceInstanceInService(serviceId, resourceInstanceUid, resourceInstance);
+
+ if (updateRes.isRight()) {
+ TitanOperationStatus status = updateRes.right().value();
+ log.error("Failed to update resource instance {}. Status is {}", resourceInstanceUid, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ ComponentInstance value = updateRes.left().value();
+
+ result = Either.left(value);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+
+ }
+
+ /**
+ * prepare new resource instance object for update
+ *
+ * @param resourceInstance
+ * @param currentInst
+ * @return
+ */
+ private ComponentInstance normalizeResourceInstanceForUpdate(ComponentInstance resourceInstance, ComponentInstanceData currentInst) {
+
+ ComponentInstance instance = new ComponentInstance();
+ instance.setUniqueId((String) currentInst.getUniqueId());
+ Long modificationTime = resourceInstance.getModificationTime();
+ if (modificationTime == null) {
+ modificationTime = System.currentTimeMillis();
+ }
+ instance.setModificationTime(modificationTime);
+ instance.setPosX(resourceInstance.getPosX());
+ instance.setPosY(resourceInstance.getPosY());
+ instance.setDescription(resourceInstance.getDescription());
+ instance.setName(resourceInstance.getName());
+ instance.setNormalizedName(resourceInstance.getNormalizedName());
+ instance.setPropertyValueCounter(resourceInstance.getPropertyValueCounter());
+ instance.setAttributeValueCounter(resourceInstance.getAttributeValueCounter());
+ instance.setInputValueCounter(resourceInstance.getInputValueCounter());
+ return instance;
+ }
+
+ private void printDiff(ComponentInstanceData currentInst, ComponentInstance resourceInstance) {
+
+ log.debug("The current Resource Instance details are : {}", currentInst);
+ log.debug("The received Resource Instance details for update are : {}", resourceInstance);
+
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> updateResourceInstance(String serviceId, NodeTypeEnum nodeType, String resourceInstanceName, ComponentInstance resourceInstance) {
+
+ return updateResourceInstance(serviceId, nodeType, resourceInstanceName, resourceInstance, false);
+ }
+
+ public Either<ComponentInstance, TitanOperationStatus> updateResourceInstanceInService(String serviceId, String resourceInstanceUid, ComponentInstance resourceInstance) {
+
+ log.debug("Going to update resource instance {}. Properties are {}", resourceInstanceUid, resourceInstance);
+ Either<ComponentInstanceData, TitanOperationStatus> findInstRes = findResourceInstance(resourceInstanceUid);
+ if (findInstRes.isRight()) {
+ TitanOperationStatus status = findInstRes.right().value();
+ log.error("Failed to find resource instance {}. Status is {}", resourceInstanceUid, status);
+ return Either.right(status);
+ }
+
+ ComponentInstanceData currentInst = findInstRes.left().value();
+ if (log.isDebugEnabled()) {
+ printDiff(currentInst, resourceInstance);
+ }
+
+ ComponentInstance resourceInstanceForUpdate = normalizeResourceInstanceForUpdate(resourceInstance, currentInst);
+
+ ComponentInstanceData resourceInstanceData = new ComponentInstanceData(resourceInstanceForUpdate);
+
+ Either<ComponentInstanceData, TitanOperationStatus> updateNodeRes = titanGenericDao.updateNode(resourceInstanceData, ComponentInstanceData.class);
+ if (updateNodeRes.isRight()) {
+ TitanOperationStatus status = updateNodeRes.right().value();
+ log.error("Failed to update resource instance {}. Status is {}", resourceInstanceUid, status);
+ return Either.right(status);
+ }
+
+ ComponentInstanceData value = updateNodeRes.left().value();
+
+ ComponentInstance instance = new ComponentInstance(value.getComponentInstDataDefinition());
+
+ return Either.left(instance);
+
+ }
+
+ @Override
+ public Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, StorageOperationStatus> getAllComponentInstances(String componentId, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType, boolean inTransaction) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> resInstancesOfService = getComponentInstancesOfComponent(componentId, containerNodeType, compInstNodeType);
+
+ log.trace("After fetching resource instances of component {}. result is {}", componentId, resInstancesOfService);
+ if (resInstancesOfService.isRight()) {
+ TitanOperationStatus status = resInstancesOfService.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find resource instances of service {}. Status is {}", componentId, status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>> immutablePair = resInstancesOfService.left().value();
+ List<ComponentInstance> nodes = immutablePair.getKey();
+ if (nodes == null || nodes.isEmpty()) {
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ result = Either.left(immutablePair);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> isComponentInstanceNameExist(String parentComponentId, NodeTypeEnum nodeType, String compInstId, String componentInstName) {
+
+ Either<Boolean, StorageOperationStatus> result = null;
+ Either<Boolean, TitanOperationStatus> updateRes = isComponentInstanceNameExistOnGraph(parentComponentId, nodeType, compInstId, componentInstName);
+
+ if (updateRes.isRight()) {
+ TitanOperationStatus status = updateRes.right().value();
+ log.error("Failed to find component instance name {}. Status is {}", componentInstName, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ Boolean value = updateRes.left().value();
+
+ result = Either.left(value);
+
+ return result;
+
+ }
+
+ private Either<Boolean, TitanOperationStatus> isComponentInstanceNameExistOnGraph(String parentComponentId, NodeTypeEnum parentNodeType, String compInstId, String componentInstName) {
+
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanGenericDao.getGraph();
+ if (graphRes.isRight()) {
+ log.debug("Failed to retrieve graph. status is {}", graphRes);
+ return Either.right(graphRes.right().value());
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ Iterable<TitanVertex> vertices = titanGraph.query().has(UniqueIdBuilder.getKeyByNodeType(parentNodeType), parentComponentId).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ TitanVertex serviceVertex = vertices.iterator().next();
+ TitanVertexQuery query = serviceVertex.query();
+ query = query.labels(GraphEdgeLabels.RESOURCE_INST.getProperty());
+ Iterable<Vertex> verts = query.vertices();
+ if (verts == null) {
+ log.debug("No edges in graph for criteria");
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ Iterator<Vertex> vIter = verts.iterator();
+ while (vIter.hasNext()) {
+ Vertex vert = vIter.next();
+ String resInstName = vert.value(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty());
+ if (resInstName.equals(componentInstName)) {
+ if (compInstId != null) {// will be null if we got here from
+ // create
+ // Update case - skipping if this is the same component
+ // instance we are updating, that is allowing
+ // update of the unchanged name on a component instance.
+ // This is needed to support position only update, since
+ // name should
+ // always be passed in update, and in position case, the
+ // name will be unchanged.
+ String uniqueId = vert.value(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ if (uniqueId.equals(compInstId)) {
+ continue;
+ }
+ }
+ return Either.left(Boolean.TRUE);
+ }
+ }
+ return Either.left(Boolean.FALSE);
+ }
+
+ /**
+ * find resource instances and the relationships between the relationships of a given resource
+ *
+ * @param componentId
+ * @return
+ */
+ public Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> getComponentInstancesOfComponent(String componentId, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType) {
+
+ if (log.isDebugEnabled())
+ log.debug("Going to fetch all resource instances under component {}", componentId);
+
+ Either<ComponentMetadataData, TitanOperationStatus> componentRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(containerNodeType), componentId, ComponentMetadataData.class);
+ if (componentRes.isRight()) {
+ TitanOperationStatus status = componentRes.right().value();
+ log.error("Failed to find component {}. Status is {}", componentId, status);
+ return Either.right(status);
+ }
+
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> resourceInstancesRes = getAllComponentInstanceFromGraph(componentId, containerNodeType, true);
+ if (resourceInstancesRes.isRight()) {
+ TitanOperationStatus status = resourceInstancesRes.right().value();
+ log.debug("Resource instance was found under component {}. status is {}", componentId, status);
+ return Either.right(status);
+ }
+
+ List<ComponentInstance> resourcesResult = new ArrayList<ComponentInstance>();
+ List<RequirementCapabilityRelDef> requirementsResult = new ArrayList<RequirementCapabilityRelDef>();
+
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> resourceInstances = resourceInstancesRes.left().value();
+ if (resourceInstances != null && false == resourceInstances.isEmpty()) {
+ Map<String, Map<String, CapabilityDefinition>> compInstCapabilities = new HashMap<String, Map<String, CapabilityDefinition>>();
+ Map<String, Map<String, RequirementDefinition>> compInstReq = new HashMap<String, Map<String, RequirementDefinition>>();
+ Map<String, Map<String, ArtifactDefinition>> compInstArtifacts = new HashMap<String, Map<String, ArtifactDefinition>>();
+ Map<String, Component> compInstOriginsMap = new HashMap<String, Component>();
+
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> immutablePair : resourceInstances) {
+
+ ComponentInstanceData resourceInstanceData = immutablePair.getKey();
+ if (log.isDebugEnabled())
+ log.debug("Going to fetch the relationships of resource instance {}", resourceInstanceData);
+
+ ComponentInstance resourceInstance = new ComponentInstance(resourceInstanceData.getComponentInstDataDefinition());
+
+ TitanOperationStatus status = getFullComponentInstance(compInstCapabilities, compInstReq, compInstArtifacts, compInstOriginsMap, resourceInstance, compInstNodeType);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ resourcesResult.add(resourceInstance);
+
+ Either<List<ImmutablePair<RelationshipInstData, GraphEdge>>, TitanOperationStatus> relationshipsRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance),
+ (String) resourceInstanceData.getUniqueId(), GraphEdgeLabels.RELATIONSHIP_INST, NodeTypeEnum.RelationshipInst, RelationshipInstData.class);
+
+ if (relationshipsRes.isRight()) {
+ status = relationshipsRes.right().value();
+ log.debug("After fetching all reslationships of resource instance {} under component {} . status is {}", resourceInstanceData.getUniqueId(), componentId, status);
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ continue;
+ } else {
+ log.error("Failed to find relationhips of resource instance {} under component {}. status is {}", resourceInstanceData.getUniqueId(), componentId, status);
+ return Either.right(status);
+ }
+ }
+
+ String sourceResourceUid = (String) resourceInstanceData.getUniqueId();
+
+ Map<String, List<ImmutablePair<String, RelationshipInstData>>> targetNodeToRelationship = new HashMap<String, List<ImmutablePair<String, RelationshipInstData>>>();
+
+ List<ImmutablePair<RelationshipInstData, GraphEdge>> relationshipsImpl = relationshipsRes.left().value();
+ status = populateTargetAndRelationsForGivenSource(targetNodeToRelationship, relationshipsImpl);
+
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ if (targetNodeToRelationship.isEmpty()) {
+ log.error("No target found for relationship instances of resource instance {}", resourceInstanceData.getUniqueId());
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+
+ buildRelationsForSource(requirementsResult, sourceResourceUid, targetNodeToRelationship);
+
+ }
+
+ return Either.left(new ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>(resourcesResult, requirementsResult));
+ } else {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ }
+
+ private Either<List<RequirementCapabilityRelDef>, TitanOperationStatus> getRelationsForSource(String resourceInstanceUid) {
+ Either<List<ImmutablePair<RelationshipInstData, GraphEdge>>, TitanOperationStatus> relationshipsRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceUid,
+ GraphEdgeLabels.RELATIONSHIP_INST, NodeTypeEnum.RelationshipInst, RelationshipInstData.class);
+
+ TitanOperationStatus status;
+ List<RequirementCapabilityRelDef> requirementsResult = new ArrayList<RequirementCapabilityRelDef>();
+
+ if (relationshipsRes.isRight()) {
+ status = relationshipsRes.right().value();
+ log.debug("After fetching all reslationships of resource instance {}. Status is {}", resourceInstanceUid, status);
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.left(requirementsResult);
+ } else {
+ log.error("Failed to find relationhips of resource instance {}. Status is {}", resourceInstanceUid, status);
+ return Either.right(status);
+ }
+ }
+
+ Map<String, List<ImmutablePair<String, RelationshipInstData>>> targetNodeToRelationship = new HashMap<String, List<ImmutablePair<String, RelationshipInstData>>>();
+
+ List<ImmutablePair<RelationshipInstData, GraphEdge>> relationshipsImpl = relationshipsRes.left().value();
+ status = populateTargetAndRelationsForGivenSource(targetNodeToRelationship, relationshipsImpl);
+
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ if (targetNodeToRelationship.isEmpty()) {
+ log.error("No target found for relationship instances of resource instance {}", resourceInstanceUid);
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+
+ buildRelationsForSource(requirementsResult, resourceInstanceUid, targetNodeToRelationship);
+ return Either.left(requirementsResult);
+ }
+
+ private Either<List<RequirementCapabilityRelDef>, TitanOperationStatus> getRelationsForTarget(String resourceInstanceUid) {
+
+ TitanOperationStatus status;
+
+ Either<List<ImmutablePair<RelationshipInstData, GraphEdge>>, TitanOperationStatus> relationshipsRes = titanGenericDao.getParentNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceUid,
+ GraphEdgeLabels.CAPABILITY_NODE, NodeTypeEnum.RelationshipInst, RelationshipInstData.class);
+
+ List<RequirementCapabilityRelDef> requirementsResult = new ArrayList<RequirementCapabilityRelDef>();
+
+ if (relationshipsRes.isRight()) {
+ status = relationshipsRes.right().value();
+ log.debug("After fetching all reslationships of resource instance {}. Status is {}", resourceInstanceUid, status);
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.left(requirementsResult);
+ } else {
+ log.error("Failed to find relationhips of resource instance {}. Status is {}", resourceInstanceUid, status);
+ return Either.right(status);
+ }
+ }
+
+ Map<String, List<ImmutablePair<String, RelationshipInstData>>> sourceNodeToRelationship = new HashMap<String, List<ImmutablePair<String, RelationshipInstData>>>();
+
+ List<ImmutablePair<RelationshipInstData, GraphEdge>> relationshipsImpl = relationshipsRes.left().value();
+ status = populateSourceAndRelationsForGivenTarget(sourceNodeToRelationship, relationshipsImpl);
+
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ if (sourceNodeToRelationship.isEmpty()) {
+ log.error("No target found for relationship instances of resource instance {}", resourceInstanceUid);
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+
+ buildRelationsForTarget(requirementsResult, resourceInstanceUid, sourceNodeToRelationship);
+ return Either.left(requirementsResult);
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> getFullComponentInstance(ComponentInstance componentInstance, NodeTypeEnum compInstNodeType) {
+ Map<String, Map<String, CapabilityDefinition>> compInstCapabilities = new HashMap<String, Map<String, CapabilityDefinition>>();
+ Map<String, Map<String, RequirementDefinition>> compInstReq = new HashMap<String, Map<String, RequirementDefinition>>();
+ Map<String, Map<String, ArtifactDefinition>> compInstArtifacts = new HashMap<String, Map<String, ArtifactDefinition>>();
+ Map<String, Component> compInstOrigins = new HashMap<String, Component>();
+
+ TitanOperationStatus fullResourceInstance = getFullComponentInstance(compInstCapabilities, compInstReq, compInstArtifacts, compInstOrigins, componentInstance, compInstNodeType);
+ if (!fullResourceInstance.equals(TitanOperationStatus.OK)) {
+ log.debug("failed to get full data of resource instance. error: {}", fullResourceInstance);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(fullResourceInstance));
+ }
+ return Either.left(componentInstance);
+ }
+
+ private TitanOperationStatus getFullComponentInstance(Map<String, Map<String, CapabilityDefinition>> compInstCapabilities, Map<String, Map<String, RequirementDefinition>> compInstReq,
+ Map<String, Map<String, ArtifactDefinition>> compInstArtifacts, Map<String, Component> compInstOrigins, ComponentInstance compInst, NodeTypeEnum compInstNodeType) {
+ Component component = null;
+ ComponentOperation componentOperation = getComponentOperation(compInstNodeType);
+ String componentUid = compInst.getComponentUid();
+ if (compInstOrigins.containsKey(componentUid)) {
+ component = compInstOrigins.get(componentUid);
+ } else {
+ Either<Component, StorageOperationStatus> metadataComponent = componentOperation.getMetadataComponent(componentUid, true);
+ if (metadataComponent.isRight()) {
+ log.debug("Failed to fetch the origin component for component instance, origin Id {}, error: {}", componentUid, metadataComponent.right().value());
+ return TitanOperationStatus.GENERAL_ERROR;
+ }
+ component = metadataComponent.left().value();
+ compInstOrigins.put(componentUid, component);
+
+ }
+ String icon = component.getIcon();
+ if (log.isDebugEnabled())
+ log.debug("Fetch the resource instance icon from the resource itself. icon = {}", icon);
+ compInst.setIcon(icon);
+ String componentName = component.getName();
+ compInst.setComponentName(componentName);
+ compInst.setComponentVersion(component.getVersion());
+ if (component.getComponentType() == ComponentTypeEnum.RESOURCE) {
+ compInst.setToscaComponentName(((Resource) component).getToscaResourceName());
+ }
+
+ List<ComponentInstance> componentInstances = new ArrayList<>();
+ List<String> derivedFromList = new ArrayList<String>();
+
+ // For VFC/VL/CP
+ if (compInstNodeType == NodeTypeEnum.Resource && ((Resource) component).getResourceType() != ResourceTypeEnum.VF) {
+ resourceOperation.fillResourceDerivedListFromGraph(component.getUniqueId(), derivedFromList);
+ } else {
+ // Getting component instances that the origin component of this
+ // component instance is their container, so we can use the logic of
+ // getting req/cap from them
+ // and fill this component instance with those req/cap
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> allComponentInstanceFromGraph = getAllComponentInstanceFromGraph(componentUid, compInstNodeType, true);
+ if (allComponentInstanceFromGraph.isRight() && allComponentInstanceFromGraph.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Couldn't fetch component instances for component {} of type {}", componentUid, compInstNodeType);
+ return allComponentInstanceFromGraph.right().value();
+ }
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> allCIs = allComponentInstanceFromGraph.isLeft() ? allComponentInstanceFromGraph.left().value() : new ArrayList<>();
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> entry : allCIs) {
+ componentInstances.add(new ComponentInstance(entry.left.getComponentInstDataDefinition()));
+ }
+ component.setComponentInstances(componentInstances);
+ }
+
+ StorageOperationStatus capStatus = setCompInstCapabilitiesFromGraph(compInstCapabilities, component, compInstNodeType, compInst, derivedFromList);
+ if (capStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to find capability of resource {}. status is {}", componentName, capStatus);
+
+ }
+ capStatus = setCompInstRequirementsFromGraph(compInstReq, component, compInstNodeType, compInst);
+ if (capStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to find requirements of resource {}. status is {}", componentName, capStatus);
+
+ }
+
+ capStatus = setCompInstDeploymentArtifactsFromGraph(compInstArtifacts, componentUid, compInst);
+ if (capStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to find resource deployment artifacts of resource {}. status is {}", componentName, capStatus);
+
+ }
+
+ capStatus = setCompInstArtifactsFromGraph(compInst);
+ if (capStatus != StorageOperationStatus.OK) {
+ log.debug("Failed to find resource deployment artifacts of resource instance {} . status is {}", compInst.getName(), capStatus);
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ protected StorageOperationStatus setCompInstArtifactsFromGraph(ComponentInstance resourceInstance) {
+
+ Map<String, ArtifactDefinition> deploymentArtifacts = null;
+ if (resourceInstance.getDeploymentArtifacts() == null) {
+ deploymentArtifacts = new HashMap<String, ArtifactDefinition>();
+ } else {
+ deploymentArtifacts = new HashMap<String, ArtifactDefinition>(resourceInstance.getDeploymentArtifacts());
+ }
+
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> result = artifactOperation.getArtifacts(resourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance, true, ArtifactGroupTypeEnum.DEPLOYMENT.getType());
+ if (result.isRight()) {
+ StorageOperationStatus status = result.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ return status;
+ } else {
+ resourceInstance.setDeploymentArtifacts(deploymentArtifacts);
+ return StorageOperationStatus.OK;
+ }
+ }
+
+ Map<String, ArtifactDefinition> artifacts = result.left().value();
+ if ((artifacts != null) && !artifacts.isEmpty()) {
+ for (ArtifactDefinition artifact : artifacts.values()) {
+ if (artifact.getArtifactType().equalsIgnoreCase(ArtifactTypeEnum.HEAT_ENV.getType())) {
+ Either<List<HeatParameterDefinition>, StorageOperationStatus> heatParamsForEnv = artifactOperation.getHeatParamsForEnv(artifact);
+ if (heatParamsForEnv.isRight()) {
+ log.debug("failed to get heat parameters values for heat artifact {}", artifact.getUniqueId());
+ return heatParamsForEnv.right().value();
+ } else {
+ artifact.setHeatParameters(heatParamsForEnv.left().value());
+ }
+ }
+ }
+
+ // add resource instance artifacts to the artifacts inherited from
+ // resource
+ deploymentArtifacts.putAll(artifacts);
+ resourceInstance.setDeploymentArtifacts(deploymentArtifacts);
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ // resourceInstance) {
+ // ArrayList<HeatParameterDefinition>();
+ // heatEnvArtifact.getGeneratedFromId());
+ // Either<List<ImmutablePair<HeatParameterValueData, GraphEdge>>,
+ // TitanOperationStatus> heatEnvValuesWithEdges = titanGenericDao
+ // !heatEnvValuesWithEdges.right().value().equals(TitanOperationStatus.NOT_FOUND))
+ // {
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // heatEnvValuesWithEdges.left().value()){
+ // pair.right.getProperties().get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ // heatValuesMap.get(parameter.getName());
+ private Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> getAllComponentInstanceFromGraph(String componentId, NodeTypeEnum containerNodeType, boolean withEdges) {
+ if (log.isDebugEnabled())
+ log.debug("Going to fetch all resource instances nodes in graph associate to component {}", componentId);
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> resourceInstancesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(containerNodeType), componentId, GraphEdgeLabels.RESOURCE_INST,
+ NodeTypeEnum.ResourceInstance, ComponentInstanceData.class, withEdges);
+ if (log.isDebugEnabled())
+ log.debug("After fetching all component instances under component {}", componentId);
+
+ if (resourceInstancesRes.isLeft()) {
+ printAllResourceInstancesNames(resourceInstancesRes);
+ }
+ return resourceInstancesRes;
+ }
+
+ private void printAllResourceInstancesNames(Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> resourceInstancesRes) {
+ if (log.isTraceEnabled()) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Result is ");
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> listResData = resourceInstancesRes.left().value();
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> resInstPair : listResData) {
+ ComponentInstanceData resdata = resInstPair.getLeft();
+ builder.append(resdata.getName()).append(", ");
+ }
+ log.trace(builder.toString());
+ }
+ }
+
+ private TitanOperationStatus populateTargetAndRelationsForGivenSource(Map<String, List<ImmutablePair<String, RelationshipInstData>>> targetNodeToRelationship, List<ImmutablePair<RelationshipInstData, GraphEdge>> relationshipsImpl) {
+ if (relationshipsImpl != null && false == relationshipsImpl.isEmpty()) {
+ for (ImmutablePair<RelationshipInstData, GraphEdge> pair : relationshipsImpl) {
+ RelationshipInstData relationshipInstData = pair.getKey();
+
+ GraphEdge requirementEdge = pair.getValue();
+ String requirementName = (String) requirementEdge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> targetNodeRes = titanGenericDao.getChild(relationshipInstData.getUniqueIdKey(), relationshipInstData.getUniqueId(), GraphEdgeLabels.CAPABILITY_NODE,
+ NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+
+ if (targetNodeRes.isRight()) {
+ TitanOperationStatus status = targetNodeRes.right().value();
+ log.error("Failed to find the target node of relationship inst {}. Status is {}", relationshipInstData, status);
+ return status;
+ }
+
+ addRelationshipInstToTargetMap(targetNodeToRelationship, relationshipInstData, requirementName, targetNodeRes);
+
+ }
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus populateSourceAndRelationsForGivenTarget(Map<String, List<ImmutablePair<String, RelationshipInstData>>> sourceNodeToRelationship, List<ImmutablePair<RelationshipInstData, GraphEdge>> relationshipsImpl) {
+ if (relationshipsImpl != null && false == relationshipsImpl.isEmpty()) {
+ for (ImmutablePair<RelationshipInstData, GraphEdge> pair : relationshipsImpl) {
+ RelationshipInstData relationshipInstData = pair.getKey();
+
+ GraphEdge requirementEdge = pair.getValue();
+ String requirementName = (String) requirementEdge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> sourceNodeRes = titanGenericDao.getParentNode(relationshipInstData.getUniqueIdKey(), relationshipInstData.getUniqueId(), GraphEdgeLabels.RELATIONSHIP_INST,
+ NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+
+ if (sourceNodeRes.isRight()) {
+ TitanOperationStatus status = sourceNodeRes.right().value();
+ log.error("Failed to find the source node of relationship inst {}. Status is {}", relationshipInstData, status);
+ return status;
+ }
+
+ addRelationshipInstToTargetMap(sourceNodeToRelationship, relationshipInstData, requirementName, sourceNodeRes);
+
+ }
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ private void buildRelationsForSource(List<RequirementCapabilityRelDef> requirementsResult, String sourceResourceUid, Map<String, List<ImmutablePair<String, RelationshipInstData>>> targetNodeToRelationship) {
+ for (Entry<String, List<ImmutablePair<String, RelationshipInstData>>> targetToRel : targetNodeToRelationship.entrySet()) {
+ RequirementCapabilityRelDef requirementCapabilityRelDef = new RequirementCapabilityRelDef();
+ requirementCapabilityRelDef.setFromNode(sourceResourceUid);
+ String targetUid = targetToRel.getKey();
+ requirementCapabilityRelDef.setToNode(targetUid);
+
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<RequirementAndRelationshipPair>();
+
+ populateRelationships(targetToRel, relationships);
+ requirementCapabilityRelDef.setRelationships(relationships);
+
+ requirementsResult.add(requirementCapabilityRelDef);
+ }
+ }
+
+ private void buildRelationsForTarget(List<RequirementCapabilityRelDef> requirementsResult, String targetResourceUid, Map<String, List<ImmutablePair<String, RelationshipInstData>>> sourceNodeToRelationship) {
+ for (Entry<String, List<ImmutablePair<String, RelationshipInstData>>> sourceToRel : sourceNodeToRelationship.entrySet()) {
+ RequirementCapabilityRelDef requirementCapabilityRelDef = new RequirementCapabilityRelDef();
+ requirementCapabilityRelDef.setToNode(targetResourceUid);
+ String sourceUid = sourceToRel.getKey();
+ requirementCapabilityRelDef.setFromNode(sourceUid);
+
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<RequirementAndRelationshipPair>();
+
+ populateRelationships(sourceToRel, relationships);
+ requirementCapabilityRelDef.setRelationships(relationships);
+
+ requirementsResult.add(requirementCapabilityRelDef);
+ }
+ }
+
+ private void addRelationshipInstToTargetMap(Map<String, List<ImmutablePair<String, RelationshipInstData>>> targetNodeToRelationship, RelationshipInstData relationshipInstData, String requirementName,
+ Either<ImmutablePair<ComponentInstanceData, GraphEdge>, TitanOperationStatus> targetNodeRes) {
+
+ ImmutablePair<ComponentInstanceData, GraphEdge> targetResourcePair = targetNodeRes.left().value();
+ ComponentInstanceData targetResourceData = targetResourcePair.getKey();
+
+ GraphEdge edge = targetResourcePair.right;
+ if (edge.getEdgeType().equals(GraphEdgeLabels.RELATIONSHIP_INST)) {
+ requirementName = (String) edge.getProperties().get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ }
+
+ String targetResourceUid = (String) targetResourceData.getUniqueId();
+ List<ImmutablePair<String, RelationshipInstData>> requirementRelationshipPair = targetNodeToRelationship.get(targetResourceUid);
+ if (requirementRelationshipPair == null) {
+ requirementRelationshipPair = new ArrayList<ImmutablePair<String, RelationshipInstData>>();
+ targetNodeToRelationship.put(targetResourceUid, requirementRelationshipPair);
+ }
+ ImmutablePair<String, RelationshipInstData> reqRelationshipPair = new ImmutablePair<String, RelationshipInstData>(requirementName, relationshipInstData);
+ requirementRelationshipPair.add(reqRelationshipPair);
+ }
+
+ private void populateRelationships(Entry<String, List<ImmutablePair<String, RelationshipInstData>>> targetToRel, List<RequirementAndRelationshipPair> relationships) {
+
+ List<ImmutablePair<String, RelationshipInstData>> values = targetToRel.getValue();
+ for (ImmutablePair<String, RelationshipInstData> value : values) {
+ String reqName = value.getKey();
+ RelationshipInstData relationshipInstData = value.getValue();
+ RelationshipImpl relationshipImpl = new RelationshipImpl();
+ relationshipImpl.setType(relationshipInstData.getType());
+ RequirementAndRelationshipPair pair = new RequirementAndRelationshipPair(reqName, relationshipImpl);
+ pair.setCapabilityOwnerId(relationshipInstData.getCapabilityOwnerId());
+ pair.setCapabilityUid(relationshipInstData.getCapabiltyId());
+ pair.setRequirementOwnerId(relationshipInstData.getRequirementOwnerId());
+ pair.setRequirementUid(relationshipInstData.getRequirementId());
+ relationships.add(pair);
+ }
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param resourceOperation
+ */
+ public void setResourceOperation(ResourceOperation resourceOperation) {
+ this.resourceOperation = resourceOperation;
+ }
+
+ @Override
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> associateResourceInstances(String componentId, NodeTypeEnum nodeType, RequirementCapabilityRelDef relation, boolean inTransaction) {
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> result = null;
+ try {
+ Either<RequirementCapabilityRelDef, TitanOperationStatus> multiRequirements = associateResourceInstancesMultiRequirements(componentId, nodeType, relation);
+ if (multiRequirements.isRight()) {
+ TitanOperationStatus status = multiRequirements.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "associateComponentInstances");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("associateComponentInstances");
+ log.debug("Failed to associate component instances. {}. Status is {}", relation, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ StorageOperationStatus updateCalculatedCapReqResult = updateCalculatedCapReq(relation, true);
+ if (!updateCalculatedCapReqResult.equals(StorageOperationStatus.OK)) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeDaoSystemError, "associateComponentInstances");
+ BeEcompErrorManager.getInstance().logBeDaoSystemError("associateComponentInstances");
+ log.debug("Failed to associate component instances. {}. Status is {}", relation, updateCalculatedCapReqResult);
+ result = Either.right(updateCalculatedCapReqResult);
+ return result;
+ }
+ result = Either.left(multiRequirements.left().value());
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+ }
+
+ private Either<RequirementCapabilityRelDef, TitanOperationStatus> associateResourceInstancesMultiRequirements(String componentId, NodeTypeEnum nodeType, RequirementCapabilityRelDef relation) {
+
+ String fromNode = relation.getFromNode();
+ String toNode = relation.getToNode();
+ List<RequirementAndRelationshipPair> relationships = relation.getRelationships();
+ if (relationships == null || relationships.isEmpty()) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedAddingResourceInstanceError, "AssociateResourceInstances - missing relationship", fromNode, componentId);
+ BeEcompErrorManager.getInstance().logBeFailedAddingResourceInstanceError("AssociateResourceInstances - missing relationship", fromNode, componentId);
+ log.debug("No requirement definition sent in order to set the relation between {} to {}", fromNode, toNode);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+
+ List<RequirementAndRelationshipPair> relationshipsResult = new ArrayList<RequirementAndRelationshipPair>();
+ for (RequirementAndRelationshipPair immutablePair : relationships) {
+ String requirement = immutablePair.getRequirement();
+
+ Either<RelationshipInstData, TitanOperationStatus> associateRes = connectResourcesInService(componentId, nodeType, fromNode, toNode, immutablePair);
+
+ if (associateRes.isRight()) {
+ TitanOperationStatus status = associateRes.right().value();
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeFailedAddingResourceInstanceError, "AssociateResourceInstances", fromNode, componentId);
+ BeEcompErrorManager.getInstance().logBeFailedAddingResourceInstanceError("AssociateResourceInstances - missing relationship", fromNode, componentId);
+ log.debug("Failed to associate resource instance {} to resource instnace {}. Status is {}", fromNode, toNode, status);
+ return Either.right(status);
+ }
+
+ RelationshipInstData relationshipInstData = associateRes.left().value();
+ RelationshipImpl relationshipImplResult = new RelationshipImpl();
+ relationshipImplResult.setType(relationshipInstData.getType());
+ RequirementAndRelationshipPair requirementAndRelationshipPair = new RequirementAndRelationshipPair(requirement, relationshipImplResult);
+ requirementAndRelationshipPair.setCapability(immutablePair.getCapability());
+ requirementAndRelationshipPair.setCapabilityOwnerId(relationshipInstData.getCapabilityOwnerId());
+ requirementAndRelationshipPair.setRequirementOwnerId(relationshipInstData.getRequirementOwnerId());
+ requirementAndRelationshipPair.setCapabilityUid(immutablePair.getCapabilityUid());
+ requirementAndRelationshipPair.setRequirementUid(immutablePair.getRequirementUid());
+ relationshipsResult.add(requirementAndRelationshipPair);
+
+ }
+
+ RequirementCapabilityRelDef capabilityRelDef = new RequirementCapabilityRelDef();
+ capabilityRelDef.setFromNode(fromNode);
+ capabilityRelDef.setToNode(toNode);
+ capabilityRelDef.setRelationships(relationshipsResult);
+
+ return Either.left(capabilityRelDef);
+ }
+
+ @Override
+ public Either<RequirementCapabilityRelDef, StorageOperationStatus> associateResourceInstances(String componentId, NodeTypeEnum nodeType, RequirementCapabilityRelDef relation) {
+ return associateResourceInstances(componentId, nodeType, relation, false);
+ }
+
+ @Override
+ public Either<List<ComponentInstance>, StorageOperationStatus> deleteAllComponentInstances(String containerComponentId, NodeTypeEnum containerNodeType, boolean inTransaction) {
+
+ Either<List<ComponentInstance>, StorageOperationStatus> result = null;
+ try {
+ Either<List<ComponentInstance>, TitanOperationStatus> multiRequirements = deleteAllComponentInstancesInternal(containerComponentId, containerNodeType);
+ if (multiRequirements.isRight()) {
+ TitanOperationStatus status = multiRequirements.right().value();
+ if (multiRequirements.right().value() != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.BeSystemError, "deleteAllResourceInstances - missing relationship");
+ BeEcompErrorManager.getInstance().logBeSystemError("deleteAllResourceInstances - missing relationship");
+ }
+ log.debug("Failed to delete resource instances of service {}. Status is {}", containerComponentId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+
+ }
+
+ result = Either.left(multiRequirements.left().value());
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<ComponentInstance>, StorageOperationStatus> deleteAllComponentInstances(String containerComponentId, NodeTypeEnum nodeType) {
+ return deleteAllComponentInstances(containerComponentId, nodeType, false);
+ }
+
+ public Either<List<ComponentInstance>, TitanOperationStatus> deleteAllComponentInstancesInternal(String componentId, NodeTypeEnum nodeType) {
+
+ log.debug("Going to delete all resource instances and their relatioships from service {}", componentId);
+
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> resourceInstancesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId, GraphEdgeLabels.RESOURCE_INST,
+ NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+
+ if (resourceInstancesRes.isRight()) {
+ TitanOperationStatus status = resourceInstancesRes.right().value();
+ log.debug("After fetching all resource instances of service {}. Status is {}", componentId, status);
+ return Either.right(status);
+ }
+
+ List<ComponentInstance> result = new ArrayList<ComponentInstance>();
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> listOfResInstances = resourceInstancesRes.left().value();
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> resInstance : listOfResInstances) {
+ ComponentInstanceData resourceInstanceData = resInstance.getKey();
+ String resourceInstUid = resourceInstanceData.getUniqueId();
+ Either<ComponentInstance, TitanOperationStatus> removeResourceInstanceRes = removeComponentInstanceFromComponent(nodeType, componentId, resourceInstUid);
+ log.debug("After removing resource instance {}. Result is {}", resourceInstUid, removeResourceInstanceRes);
+ if (removeResourceInstanceRes.isRight()) {
+ TitanOperationStatus status = removeResourceInstanceRes.right().value();
+ log.error("After removing resource instance {}. Status is {}", resourceInstUid, status);
+ return Either.right(status);
+ }
+ ComponentInstance resourceInstance = removeResourceInstanceRes.left().value();
+ result.add(resourceInstance);
+ }
+
+ log.debug("The following resource instances was deleted from service {}:{}", componentId, result);
+
+ return Either.left(result);
+ }
+
+ public Either<ImmutablePair<List<ComponentInstance>, Map<String, String>>, StorageOperationStatus> cloneAllComponentInstancesFromContainerComponent(String componentIdFrom, Component component, NodeTypeEnum containerNodeType,
+ NodeTypeEnum compInstNodeType, LifecycleStateEnum targetLifecycle, Map<String, List<ComponentInstanceInput>> inputsValuesMap) {
+
+ List<ComponentInstance> list = new ArrayList<ComponentInstance>();
+ Map<String, String> oldCompInstToNew = new HashMap<>();
+
+ ImmutablePair<List<ComponentInstance>, Map<String, String>> result = new ImmutablePair<List<ComponentInstance>, Map<String, String>>(list, oldCompInstToNew);
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, StorageOperationStatus> allResourceInstances = getAllComponentInstances(componentIdFrom, containerNodeType, compInstNodeType, true);
+
+ if (allResourceInstances.isRight()) {
+ StorageOperationStatus status = allResourceInstances.right().value();
+ if (status.equals(StorageOperationStatus.NOT_FOUND)) {
+
+ return Either.left(result);
+ } else {
+ log.error("failed to get all resource instances for service {}. status={}", componentIdFrom, status);
+ return Either.right(status);
+ }
+ }
+
+ List<ComponentInstance> riList = allResourceInstances.left().value().left;
+ Map<String, ComponentInstance> riMapper = new HashMap<>();
+ int instanceNumber = 0;
+ for (ComponentInstance ri : riList) {
+ instanceNumber++;
+ String origRiUniqueID = ri.getUniqueId();
+ Either<ComponentInstance, StorageOperationStatus> createResourceInstance = createComponentInstance(component.getUniqueId(), containerNodeType, String.valueOf(instanceNumber), false, ri, compInstNodeType, true, true);
+ if (createResourceInstance.isRight()) {
+ StorageOperationStatus status = createResourceInstance.right().value();
+ log.error("failed to clone resource instance {}. status ={}", origRiUniqueID, status);
+ return Either.right(status);
+ }
+ ComponentInstance createdInstance = createResourceInstance.left().value();
+ riMapper.put(origRiUniqueID, createdInstance);
+ StorageOperationStatus associateArtifactsToResource = cloneResourceInstanceArtifacts(createdInstance, ri, targetLifecycle);
+ if (associateArtifactsToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} artifacts. error {} ", ri.getNormalizedName(), associateArtifactsToResource.name());
+ return Either.right(associateArtifactsToResource);
+ }
+
+ StorageOperationStatus associatePropertyValuesToResource = cloneResourceInstancePropertyValues(createdInstance, ri);
+ if (associatePropertyValuesToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} property values. error {} ", ri.getNormalizedName(), associatePropertyValuesToResource.name());
+ return Either.right(associatePropertyValuesToResource);
+ }
+
+ StorageOperationStatus associateAttributeValuesToResource = cloneResourceInstanceAttributeValues(createdInstance, ri);
+ if (associateAttributeValuesToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} attribute values. error {} ", ri.getNormalizedName(), associateAttributeValuesToResource.name());
+ return Either.right(associateAttributeValuesToResource);
+ }
+
+ StorageOperationStatus associateInputValuesToResource = cloneResourceInstanceInputsValues(createdInstance, ri, component, inputsValuesMap);
+ if (associateInputValuesToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} property values. error {} ", ri.getNormalizedName(), associatePropertyValuesToResource.name());
+ return Either.right(associatePropertyValuesToResource);
+ }
+
+ list.add(createdInstance);
+ oldCompInstToNew.put(origRiUniqueID, createdInstance.getUniqueId());
+ }
+
+ List<RequirementCapabilityRelDef> relationsList = allResourceInstances.left().value().right;
+ for (RequirementCapabilityRelDef relation : relationsList) {
+ String origFrom = relation.getFromNode();
+ String origTo = relation.getToNode();
+ relation.setFromNode(riMapper.get(origFrom).getUniqueId());
+ relation.setToNode(riMapper.get(origTo).getUniqueId());
+ List<RequirementAndRelationshipPair> relationships = relation.getRelationships();
+ for (RequirementAndRelationshipPair pair : relationships) {
+ // for all atomic resource instances need to update to relevant
+ // new ri ids
+ String capOwnerId = pair.getCapabilityOwnerId();
+ String reqOwnerId = pair.getRequirementOwnerId();
+ if (isAtomicComponentInstance(riMapper.get(origFrom))) {
+ reqOwnerId = riMapper.get(reqOwnerId).getUniqueId();
+ }
+ if (isAtomicComponentInstance(riMapper.get(origTo))) {
+ capOwnerId = riMapper.get(capOwnerId).getUniqueId();
+ }
+ pair.setRequirementOwnerId(reqOwnerId);
+ pair.setCapabilityOwnerId(capOwnerId);
+ }
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> associateInstances = associateResourceInstances(component.getUniqueId(), containerNodeType, relation, true);
+ if (associateInstances.isRight()) {
+ StorageOperationStatus status = associateInstances.right().value();
+ log.error("failed to assosiate resource instance {} and resource instance {}. status ={}", relation.getFromNode(), relation.getToNode(), status);
+ return Either.right(status);
+ }
+ }
+
+ return Either.left(result);
+ }
+
+ public Either<ImmutablePair<List<ComponentInstance>, Map<String, String>>, StorageOperationStatus> cloneAllComponentInstancesFromContainerComponent(String componentIdFrom, String componentIdTo, NodeTypeEnum containerNodeType,
+ NodeTypeEnum compInstNodeType, LifecycleStateEnum targetLifecycle, TitanVertex metadataVertex, Resource prevResource, Resource newResource, Map<String, List<ComponentInstanceProperty>> inputsPropMap) {
+
+ List<ComponentInstance> list = new ArrayList<ComponentInstance>();
+ Map<String, String> oldCompInstToNew = new HashMap<>();
+
+ ImmutablePair<List<ComponentInstance>, Map<String, String>> result = new ImmutablePair<List<ComponentInstance>, Map<String, String>>(list, oldCompInstToNew);
+
+ // Either<ImmutablePair<List<ComponentInstance>,
+ // List<RequirementCapabilityRelDef>>, StorageOperationStatus>
+ // allResourceInstances = getAllComponentInstances(componentIdFrom,
+ // containerNodeType, compInstNodeType, true);
+ //
+ //
+ // if (allResourceInstances.isRight()) {
+ // StorageOperationStatus status = allResourceInstances.right().value();
+ // if (status.equals(StorageOperationStatus.NOT_FOUND)) {
+ //
+ // return Either.left(result);
+ // } else {
+ // log.error("failed to get all resource instances for service {}.
+ // status={}", componentIdFrom, status);
+ // return Either.right(status);
+ // }
+ // }
+
+ // ImmutablePair<List<ComponentInstance>,
+ // List<RequirementCapabilityRelDef>> instanceRelationPair =
+ // allResourceInstances.left().value();
+
+ // ImmutablePair<List<ComponentInstance>,
+ // List<RequirementCapabilityRelDef>> instanceRelationPair = new
+ // ImmutablePair<List<ComponentInstance>,
+ // List<RequirementCapabilityRelDef>>(prevResource.getComponentInstances(),
+ // prevResource.getComponentInstancesRelations());
+ List<ComponentInstance> riList = prevResource.getComponentInstances();
+ Map<String, ComponentInstance> riMapper = new HashMap<>();
+ int instanceNumber = 0;
+ long timeProperties = 0;
+ if (riList != null) {
+ for (ComponentInstance ri : riList) {
+ instanceNumber++;
+ String origRiUniqueID = ri.getUniqueId();
+ Either<TitanVertex, StorageOperationStatus> createResourceInstance = createComponentInstance(componentIdTo, containerNodeType, String.valueOf(instanceNumber), false, ri, compInstNodeType, true, true, metadataVertex);
+ if (createResourceInstance.isRight()) {
+ StorageOperationStatus status = createResourceInstance.right().value();
+ log.error("failed to clone resource instance {}. status ={}", origRiUniqueID, status);
+ return Either.right(status);
+ }
+ TitanVertex createdInstance = createResourceInstance.left().value();
+ String createdInstanceId = (String) titanGenericDao.getProperty(createdInstance, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+
+ StorageOperationStatus associateArtifactsToResource = cloneResourceInstanceArtifacts(createdInstance, ri, targetLifecycle);
+ if (associateArtifactsToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} artifacts. error {} ", ri.getNormalizedName(), associateArtifactsToResource.name());
+ return Either.right(associateArtifactsToResource);
+ }
+
+ long start = System.currentTimeMillis();
+ StorageOperationStatus associatePropertyValuesToResource = cloneResourceInstancePropertyValues(createdInstance, ri, inputsPropMap, newResource);
+ if (associatePropertyValuesToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} property values. error {} ", ri.getNormalizedName(), associatePropertyValuesToResource.name());
+ return Either.right(associatePropertyValuesToResource);
+ }
+ long end = System.currentTimeMillis();
+ timeProperties += (end - start);
+
+ StorageOperationStatus associateAttributeValuesToResource = cloneResourceInstanceAttributeValues(createdInstance, ri, createdInstanceId);
+ if (associateAttributeValuesToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} attribute values. error {} ", ri.getNormalizedName(), associateAttributeValuesToResource.name());
+ return Either.right(associateAttributeValuesToResource);
+ }
+
+ StorageOperationStatus associateInputValuesToResource = cloneResourceInstanceInputsValues(createdInstance, ri, createdInstanceId, newResource, null);
+ if (associateInputValuesToResource != StorageOperationStatus.OK) {
+ log.debug("failed to clone resource instance {} property values. error {} ", ri.getNormalizedName(), associatePropertyValuesToResource.name());
+ return Either.right(associatePropertyValuesToResource);
+ }
+ Map<String, Object> properties = titanGenericDao.getProperties(createdInstance);
+ ComponentInstanceData createdComponentInstance = GraphElementFactory.createElement(NodeTypeEnum.ResourceInstance.getName(), GraphElementTypeEnum.Node, properties, ComponentInstanceData.class);
+ ComponentInstance createdResourceInstance = new ComponentInstance(createdComponentInstance.getComponentInstDataDefinition());
+ riMapper.put(origRiUniqueID, createdResourceInstance);
+
+ list.add(createdResourceInstance);
+ oldCompInstToNew.put(origRiUniqueID, createdResourceInstance.getUniqueId());
+ }
+ }
+ log.info("*********** total properties in ms {}", timeProperties);
+
+ // List<RequirementCapabilityRelDef> relationsList =
+ // instanceRelationPair.right;
+ List<RequirementCapabilityRelDef> relationsList = prevResource.getComponentInstancesRelations();
+ if (relationsList != null) {
+ for (RequirementCapabilityRelDef relation : relationsList) {
+ String origFrom = relation.getFromNode();
+ String origTo = relation.getToNode();
+ relation.setFromNode(riMapper.get(origFrom).getUniqueId());
+ relation.setToNode(riMapper.get(origTo).getUniqueId());
+ List<RequirementAndRelationshipPair> relationships = relation.getRelationships();
+ for (RequirementAndRelationshipPair pair : relationships) {
+ // for all atomic resource instances need to update to
+ // relevant
+ // new ri ids
+ String capOwnerId = pair.getCapabilityOwnerId();
+ String reqOwnerId = pair.getRequirementOwnerId();
+ if (isAtomicComponentInstance(riMapper.get(origFrom))) {
+ reqOwnerId = riMapper.get(reqOwnerId).getUniqueId();
+ }
+ if (isAtomicComponentInstance(riMapper.get(origTo))) {
+ capOwnerId = riMapper.get(capOwnerId).getUniqueId();
+ }
+ pair.setRequirementOwnerId(reqOwnerId);
+ pair.setCapabilityOwnerId(capOwnerId);
+ }
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> associateInstances = associateResourceInstances(componentIdTo, containerNodeType, relation, true);
+ if (associateInstances.isRight()) {
+ StorageOperationStatus status = associateInstances.right().value();
+ log.error("failed to assosiate resource instance {} and resource instance {}. status ={}", relation.getFromNode(), relation.getToNode(), status);
+ return Either.right(status);
+ }
+ }
+ }
+ return Either.left(result);
+ }
+
+ private boolean isAtomicComponentInstance(ComponentInstance componentInstance) {
+ OriginTypeEnum originType = componentInstance.getOriginType();
+ if (originType.equals(OriginTypeEnum.VFC) || originType.equals(OriginTypeEnum.VL) || originType.equals(OriginTypeEnum.CP)) {
+ return true;
+ }
+ return false;
+ }
+
+ private StorageOperationStatus cloneResourceInstanceArtifacts(ComponentInstance toResourceInstance, ComponentInstance fromResourceInstance, LifecycleStateEnum targetLifecycle) {
+
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> getArtifactsOfRI = artifactOperation.getArtifacts(fromResourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance, true);
+ if (getArtifactsOfRI.isRight()) {
+ StorageOperationStatus status = getArtifactsOfRI.right().value();
+ if (status.equals(StorageOperationStatus.NOT_FOUND)) {
+ status = StorageOperationStatus.OK;
+ }
+ return status;
+ }
+
+ Map<String, ArtifactDefinition> artifacts = getArtifactsOfRI.left().value();
+ for (Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+
+ ArtifactDefinition artifactDefinition = entry.getValue();
+ // US687135 Do not Add VF_MODULES_METADATA when checking out
+ if (ArtifactTypeEnum.VF_MODULES_METADATA.getType().equals(artifactDefinition.getArtifactType())) {
+ // The artifact of type VF_MODULES_METADATA should not be cloned
+ // unless we are changing the state to certified.
+ if (targetLifecycle != null && targetLifecycle != LifecycleStateEnum.CERTIFIED) {
+ continue;
+ }
+ }
+ Either<ArtifactDefinition, StorageOperationStatus> addArifactToResource = Either.left(artifactDefinition);
+
+ addArifactToResource = artifactOperation.addArifactToComponent(artifactDefinition, toResourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance, false, true);
+
+ if (addArifactToResource.isRight()) {
+ return addArifactToResource.right().value();
+ }
+ }
+ toResourceInstance.setDeploymentArtifacts(artifacts);
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus cloneResourceInstanceArtifacts(TitanVertex toResourceInstance, ComponentInstance fromResourceInstance, LifecycleStateEnum targetLifecycle) {
+
+ Either<Map<String, TitanVertex>, StorageOperationStatus> getArtifactsOfRI = artifactOperation.getArtifactsVertecies(fromResourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance, true);
+ if (getArtifactsOfRI.isRight()) {
+ StorageOperationStatus status = getArtifactsOfRI.right().value();
+ if (status.equals(StorageOperationStatus.NOT_FOUND)) {
+ status = StorageOperationStatus.OK;
+ }
+ return status;
+ }
+
+ Map<String, TitanVertex> artifacts = getArtifactsOfRI.left().value();
+ for (Entry<String, TitanVertex> entry : artifacts.entrySet()) {
+
+ TitanVertex artifactVertex = entry.getValue();
+ // US687135 Do not Add VF_MODULES_METADATA when checking out
+ String artifactType = (String) titanGenericDao.getProperty(artifactVertex, GraphPropertiesDictionary.ARTIFACT_TYPE.getProperty());
+ String label = (String) titanGenericDao.getProperty(artifactVertex, GraphPropertiesDictionary.ARTIFACT_LABEL.getProperty());
+ if (ArtifactTypeEnum.VF_MODULES_METADATA.getType().equals(artifactType)) {
+ // The artifact of type VF_MODULES_METADATA should not be cloned
+ // unless we are changing the state to certified.
+ if (targetLifecycle != null && targetLifecycle != LifecycleStateEnum.CERTIFIED) {
+ continue;
+ }
+ }
+
+ StorageOperationStatus addArifactToResource = artifactOperation.addArifactToComponent(artifactVertex, toResourceInstance, label);
+
+ if (!addArifactToResource.equals(StorageOperationStatus.OK)) {
+ return addArifactToResource;
+ }
+ }
+ // toResourceInstance.setDeploymentArtifacts(artifacts);
+ return StorageOperationStatus.OK;
+ }
+
+ public Either<Integer, StorageOperationStatus> increaseAndGetResourceInstanceSpecificCounter(String resourceInstanceId, GraphPropertiesDictionary counterType, boolean inTransaction) {
+
+ Either<Integer, StorageOperationStatus> result = null;
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+ Either<TitanVertex, TitanOperationStatus> vertexService = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId);
+ if (vertexService.isRight()) {
+ log.debug("failed to fetch vertex of resource instance for id = {}", resourceInstanceId);
+ TitanOperationStatus status = vertexService.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(vertexService.right().value()));
+ return result;
+ }
+ Vertex vertex = vertexService.left().value();
+
+ VertexProperty<Object> vertexProperty = vertex.property(counterType.getProperty());
+ Integer counter = 0;
+ if (vertexProperty.isPresent()) {
+ if (vertexProperty.value() != null) {
+ counter = (Integer) vertexProperty.value();
+ }
+ }
+
+ counter++;
+ vertex.property(counterType.getProperty(), counter);
+
+ result = Either.left(counter);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("increaseAndGetResourceInstanceSpecificCounter operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("increaseAndGetResourceInstanceSpecificCounter operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<Integer, StorageOperationStatus> increaseAndGetResourceInstanceSpecificCounter(TitanVertex resourceInstanceVertex, GraphPropertiesDictionary counterType) {
+
+ Either<Integer, StorageOperationStatus> result = null;
+
+ VertexProperty<Object> vertexProperty = resourceInstanceVertex.property(counterType.getProperty());
+ Integer counter = 0;
+ if (vertexProperty.isPresent()) {
+ if (vertexProperty.value() != null) {
+ counter = (Integer) vertexProperty.value();
+ }
+ }
+ counter++;
+ resourceInstanceVertex.property(counterType.getProperty(), counter);
+
+ result = Either.left(counter);
+ return result;
+
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllComponentInstancesNames(String serviceId, NodeTypeEnum nodeType, boolean inTransaction) {
+
+ Either<List<String>, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<List<String>, TitanOperationStatus> resInstancesOfService = getComponentInstancesNameOfService(serviceId, nodeType);
+
+ log.debug("After fetching resource instances of service {}. Result is {}", serviceId, resInstancesOfService);
+ if (resInstancesOfService.isRight()) {
+ TitanOperationStatus status = resInstancesOfService.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find resource instances of service {}. Status is {}", serviceId, status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ List<String> names = resInstancesOfService.left().value();
+
+ if (names == null || names.isEmpty()) {
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ result = Either.left(names);
+
+ } finally {
+ if (false == inTransaction) {
+ commitOrRollback(result);
+ }
+ }
+ return result;
+ }
+
+ private Either<List<String>, TitanOperationStatus> getComponentInstancesNameOfService(String serviceId, NodeTypeEnum nodeType) {
+
+ List<String> resourcesInstanseName = new ArrayList<String>();
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> resourceInstancesRes = getAllComponentInstanceFromGraph(serviceId, nodeType, false);
+ if (resourceInstancesRes.isRight()) {
+ TitanOperationStatus status = resourceInstancesRes.right().value();
+ log.debug("Resource instance was found under service {}. Status is {}", serviceId, status);
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> resourceInstances = resourceInstancesRes.left().value();
+ if (resourceInstances != null && false == resourceInstances.isEmpty()) {
+
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> immutablePair : resourceInstances) {
+ ComponentInstanceData resourceInstanceData = immutablePair.getKey();
+ log.debug("Going to fetch the relationships of resource instance {}", resourceInstanceData);
+ resourcesInstanseName.add(resourceInstanceData.getComponentInstDataDefinition().getName());
+
+ }
+ }
+
+ return Either.left(resourcesInstanseName);
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllComponentInstancesNames(String componentId, NodeTypeEnum nodeType) {
+
+ return getAllComponentInstancesNames(componentId, nodeType, false);
+ }
+
+ @Override
+ public Either<ComponentInstance, StorageOperationStatus> getResourceInstanceById(String resourceId) {
+ Either<ComponentInstanceData, TitanOperationStatus> resourceInstanceData = findResourceInstance(resourceId);
+
+ if (resourceInstanceData.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(resourceInstanceData.right().value()));
+ }
+
+ return Either.left(new ComponentInstance(resourceInstanceData.left().value().getComponentInstDataDefinition()));
+
+ }
+
+ private StorageOperationStatus setCompInstDeploymentArtifactsFromGraph(Map<String, Map<String, ArtifactDefinition>> resourcesArtifacts, String uniqueId, ComponentInstance resourceInstance) {
+
+ if (resourcesArtifacts.containsKey(uniqueId)) {
+ resourceInstance.setDeploymentArtifacts(resourcesArtifacts.get(uniqueId));
+ return StorageOperationStatus.OK;
+ }
+
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> result = artifactOperation.getArtifacts(uniqueId, NodeTypeEnum.Resource, true, ArtifactGroupTypeEnum.DEPLOYMENT.getType());
+ if (result.isRight()) {
+ StorageOperationStatus status = result.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ return status;
+ } else {
+ return StorageOperationStatus.OK;
+ }
+ }
+ Map<String, ArtifactDefinition> artifacts = result.left().value();
+ if (!artifacts.isEmpty()) {
+ Map<String, ArtifactDefinition> tempArtifacts = new HashMap<String, ArtifactDefinition>(artifacts);
+ for (Entry<String, ArtifactDefinition> artifact : artifacts.entrySet()) {
+ if (!artifact.getValue().checkEsIdExist()) {
+ tempArtifacts.remove(artifact.getKey());
+ }
+ }
+ resourceInstance.setDeploymentArtifacts(tempArtifacts);
+ resourcesArtifacts.put(uniqueId, tempArtifacts);
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ private StorageOperationStatus setCompInstCapabilitiesFromGraph(Map<String, Map<String, CapabilityDefinition>> resourcesCapabilities, Component component, NodeTypeEnum compInstType, ComponentInstance resourceInstance,
+ List<String> respourceDerivedList) {
+
+ StorageOperationStatus status;
+ ComponentOperation componentOperation = getComponentOperation(compInstType);
+ Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> eitherCapabilities = componentOperation.getCapabilities(component, compInstType, true);
+ if (eitherCapabilities.isLeft()) {
+ status = StorageOperationStatus.OK;
+ Map<String, List<CapabilityDefinition>> capabilities = eitherCapabilities.left().value();
+ if (capabilities != null && !capabilities.isEmpty()) {
+ capabilities.forEach((type, list) -> {
+ if (list != null && !list.isEmpty()) {
+ list.forEach((capability) -> {
+ // We want to set ownerId only for instances coming
+ // from atomic resources, otherwise we don't want
+ // to overwrite the existing ownerId of underlying
+ // component instances
+ if (isAtomicResource(component)) {
+ capability.setOwnerId(resourceInstance.getUniqueId());
+ capability.setOwnerName(resourceInstance.getName());
+ capability.setCapabilitySources(respourceDerivedList);
+ }
+ });
+ }
+ });
+ resourceInstance.setCapabilities(capabilities);
+ }
+ } else {
+ status = StorageOperationStatus.GENERAL_ERROR;
+ }
+ return status;
+
+ }
+
+ private StorageOperationStatus setCompInstRequirementsFromGraph(Map<String, Map<String, RequirementDefinition>> resourcesReq, Component component, NodeTypeEnum compInstType, ComponentInstance resourceInstance) {
+ StorageOperationStatus status;
+ ComponentOperation componentOperation = getComponentOperation(compInstType);
+ Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> eitherCapabilities = componentOperation.getRequirements(component, compInstType, true);
+ if (eitherCapabilities.isLeft()) {
+ status = StorageOperationStatus.OK;
+ Map<String, List<RequirementDefinition>> requirements = eitherCapabilities.left().value();
+ if (requirements != null && !requirements.isEmpty()) {
+ // We want to set ownerId only for instances coming from atomic
+ // resources, otherwise we don't want
+ // to overwrite the existing ownerId of underlying component
+ // instances
+ if (isAtomicResource(component)) {
+ requirements.forEach((type, list) -> {
+ if (list != null && !list.isEmpty()) {
+ list.forEach((requirement) -> {
+ requirement.setOwnerId(resourceInstance.getUniqueId());
+ requirement.setOwnerName(resourceInstance.getName());
+ });
+ }
+ });
+ }
+ resourceInstance.setRequirements(requirements);
+ }
+ } else {
+ status = StorageOperationStatus.GENERAL_ERROR;
+ }
+ return status;
+ }
+
+ public Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> getCapabilities(ComponentInstance compInstance, NodeTypeEnum nodeTypeEnum) {
+
+ DataNodeCollector<CapabilityData> collector = () -> titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeTypeEnum), compInstance.getUniqueId(), GraphEdgeLabels.CALCULATED_CAPABILITY, NodeTypeEnum.Capability,
+ CapabilityData.class);
+
+ return getDataFromGraph(collector);
+
+ }
+
+ public Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> getRequirements(ComponentInstance compInstance, NodeTypeEnum nodeTypeEnum) {
+
+ DataNodeCollector<RequirementData> collector = () -> titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeTypeEnum), compInstance.getUniqueId(), GraphEdgeLabels.CALCULATED_REQUIREMENT, NodeTypeEnum.Requirement,
+ RequirementData.class);
+
+ return getDataFromGraph(collector);
+
+ }
+
+ public Either<Boolean, StorageOperationStatus> isAvailableRequirement(ComponentInstance fromResInstance, RequirementAndRelationshipPair relationPair) {
+ Either<TitanVertex, TitanOperationStatus> fromRi = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), fromResInstance.getUniqueId());
+ if (fromRi.isRight()) {
+ log.debug("Failed to fetch component instance {} error {}", fromResInstance.getUniqueId(), fromRi.right().value());
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Iterator<Edge> edgeIter = fromRi.left().value().edges(Direction.OUT, GraphEdgeLabels.CALCULATED_REQUIREMENT.name());
+ if (edgeIter == null || !edgeIter.hasNext()) {
+ log.debug("No available CALCULATED_REQUIREMENT edges. All full filled for RI {}", fromResInstance.getUniqueId());
+ return Either.left(false);
+ }
+ boolean exist = false;
+ while (edgeIter.hasNext()) {
+ Edge edge = edgeIter.next();
+ TitanVertex reqVertex = (TitanVertex) edge.inVertex();
+ String reqId = (String) titanGenericDao.getProperty(reqVertex, UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement));
+ if (reqId.equals(relationPair.getRequirementUid())) {
+ String ownerIdOnEdge = (String) edge.value(GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+ if (ownerIdOnEdge.equals(relationPair.getRequirementOwnerId())) {
+ String leftOccurrences = (String) edge.value(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ if (leftOccurrences != null && !leftOccurrences.equals(RequirementData.MAX_OCCURRENCES)) {
+ Integer leftIntValue = Integer.parseInt(leftOccurrences);
+ if (leftIntValue > 0) {
+ exist = true;
+ }
+ } else {
+ exist = true;
+ }
+ break;
+ }
+ }
+ }
+ return Either.left(exist);
+ }
+
+ public Either<Boolean, StorageOperationStatus> isAvailableCapabilty(ComponentInstance toResInstance, RequirementAndRelationshipPair relationPair) {
+ Either<TitanVertex, TitanOperationStatus> fromRi = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), toResInstance.getUniqueId());
+ if (fromRi.isRight()) {
+ log.debug("Failed to fetch component instance {}. Error: {}", toResInstance.getUniqueId(), fromRi.right().value());
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Iterator<Edge> edgeIter = fromRi.left().value().edges(Direction.OUT, GraphEdgeLabels.CALCULATED_CAPABILITY.name());
+ if (edgeIter == null || !edgeIter.hasNext()) {
+ log.debug("No available CALCULATED_CAPABILITY edges. All full filled for RI {}", toResInstance.getUniqueId());
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ boolean exist = false;
+ while (edgeIter.hasNext()) {
+ Edge edge = edgeIter.next();
+ TitanVertex reqVertex = (TitanVertex) edge.inVertex();
+ String capId = (String) titanGenericDao.getProperty(reqVertex, UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability));
+ if (capId.equals(relationPair.getCapabilityUid())) {
+ String ownerIdOnEdge = (String) edge.value(GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+ if (ownerIdOnEdge.equals(relationPair.getCapabilityOwnerId())) {
+ String leftOccurrences = (String) edge.value(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ if (leftOccurrences != null && !leftOccurrences.equals(CapabilityData.MAX_OCCURRENCES)) {
+ Integer leftIntValue = Integer.parseInt(leftOccurrences);
+ if (leftIntValue > 0) {
+ exist = true;
+ }
+ } else {
+ exist = true;
+ }
+ break;
+ }
+ }
+ }
+ return Either.left(exist);
+ }
+
+ interface DataNodeCollector<Data> {
+ Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus> getDataNodes();
+ }
+
+ public <Data> Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus> getDataFromGraph(DataNodeCollector<Data> dataCollector) {
+ Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus> eitherRet;
+
+ Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus> childrenNodes = dataCollector.getDataNodes();
+
+ if (childrenNodes.isLeft()) {
+ List<ImmutablePair<Data, GraphEdge>> collectedData = childrenNodes.left().value().stream().map(element -> new ImmutablePair<Data, GraphEdge>(element.getLeft(), element.getRight())).collect(Collectors.toList());
+ eitherRet = Either.left(collectedData);
+ } else {
+ eitherRet = Either.right(childrenNodes.right().value());
+ }
+ return eitherRet;
+ }
+
+ public ComponentOperation getComponentOperation(NodeTypeEnum componentType) {
+ if (NodeTypeEnum.Service == componentType) {
+ return serviceOperation;
+ } else if (NodeTypeEnum.Resource == componentType) {
+ return resourceOperation;
+ }
+ return null;
+ }
+
+ private boolean isAtomicResource(Component component) {
+ // true if component is of type VL/CP/VFC
+ boolean isFromAtomicResource = (component.getComponentType() == ComponentTypeEnum.RESOURCE && ((Resource) component).getResourceType() != ResourceTypeEnum.VF);
+ return isFromAtomicResource;
+ }
+
+ private StorageOperationStatus cloneResourceInstanceAttributeValues(ComponentInstance createdInstance, ComponentInstance resourceInstance) {
+ Wrapper<StorageOperationStatus> storageStatusWrapper = new Wrapper<>();
+ Wrapper<List<ComponentInstanceAttribute>> compInstanceAttList = new Wrapper<>();
+
+ findAllAttributesOfResourceInstance(resourceInstance, compInstanceAttList, storageStatusWrapper);
+
+ if (storageStatusWrapper.isEmpty()) {
+ validateListNotEmpty(storageStatusWrapper, compInstanceAttList.getInnerElement());
+ }
+
+ if (storageStatusWrapper.isEmpty()) {
+ List<ComponentInstanceAttribute> attributesOnInstance = compInstanceAttList.getInnerElement();
+ for (int i = 0; i < attributesOnInstance.size() && storageStatusWrapper.isEmpty(); i++) {
+ cloneSingleAttributeOnResourceInstance(createdInstance, attributesOnInstance.get(i), storageStatusWrapper);
+ }
+ }
+
+ StorageOperationStatus result = storageStatusWrapper.isEmpty() ? StorageOperationStatus.OK : storageStatusWrapper.getInnerElement();
+ return result;
+
+ }
+
+ private StorageOperationStatus cloneResourceInstanceAttributeValues(TitanVertex createdInstanceVertex, ComponentInstance resourceInstance, String instanceId) {
+ Wrapper<StorageOperationStatus> storageStatusWrapper = new Wrapper<>();
+ Wrapper<List<ComponentInstanceAttribute>> compInstanceAttList = new Wrapper<>();
+
+ findAllAttributesOfResourceInstance(resourceInstance, compInstanceAttList, storageStatusWrapper);
+
+ if (storageStatusWrapper.isEmpty()) {
+ validateListNotEmpty(storageStatusWrapper, compInstanceAttList.getInnerElement());
+ }
+
+ if (storageStatusWrapper.isEmpty()) {
+ List<ComponentInstanceAttribute> attributesOnInstance = compInstanceAttList.getInnerElement();
+ for (int i = 0; i < attributesOnInstance.size() && storageStatusWrapper.isEmpty(); i++) {
+ StorageOperationStatus result = cloneSingleAttributeOnResourceInstance(createdInstanceVertex, attributesOnInstance.get(i), instanceId);
+ if (!result.equals(StorageOperationStatus.OK)) {
+ log.trace("Failed to clone attribute for instance {} error {}", instanceId, result);
+ return result;
+ }
+ }
+ }
+
+ StorageOperationStatus result = storageStatusWrapper.isEmpty() ? StorageOperationStatus.OK : storageStatusWrapper.getInnerElement();
+ return result;
+
+ }
+
+ private <T> void validateListNotEmpty(Wrapper<StorageOperationStatus> storageStatusWrapper, List<T> attributesOnInstance) {
+ if (attributesOnInstance == null || attributesOnInstance.isEmpty() == true) {
+ storageStatusWrapper.setInnerElement(StorageOperationStatus.OK);
+ }
+ }
+
+ private void findAllAttributesOfResourceInstance(ComponentInstance resourceInstance, Wrapper<List<ComponentInstanceAttribute>> compInstanceAttList, Wrapper<StorageOperationStatus> storageStatusWrapper) {
+
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> allAttributes = attributeOperation.getAllAttributesOfResourceInstance(resourceInstance);
+ if (allAttributes.isRight()) {
+ TitanOperationStatus status = allAttributes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ storageStatusWrapper.setInnerElement(storageStatus);
+ } else {
+ compInstanceAttList.setInnerElement(allAttributes.left().value());
+ }
+ }
+
+ private void cloneSingleAttributeOnResourceInstance(ComponentInstance createdInstance, ComponentInstanceAttribute attribute, Wrapper<StorageOperationStatus> storageStatusWrapper) {
+ // Only if valueUniqueId is not empty, then its belongs to the
+ // instance
+ if (attribute.getValueUniqueUid() != null) {
+ attribute.setValueUniqueUid(null);
+ Either<Integer, StorageOperationStatus> counterRes = increaseAndGetResourceInstanceSpecificCounter(createdInstance.getUniqueId(), GraphPropertiesDictionary.ATTRIBUTE_COUNTER, true);
+ if (counterRes.isRight()) {
+ storageStatusWrapper.setInnerElement(counterRes.right().value());
+ } else {
+ Either<AttributeValueData, TitanOperationStatus> addAttributeToResourceInstance = addAttributeToResourceInstance(attribute, createdInstance.getUniqueId(), counterRes.left().value());
+
+ if (addAttributeToResourceInstance.isRight()) {
+ TitanOperationStatus status = addAttributeToResourceInstance.right().value();
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ storageStatusWrapper.setInnerElement(storageStatus);
+ }
+ }
+ }
+
+ }
+
+ private StorageOperationStatus cloneSingleAttributeOnResourceInstance(TitanVertex createdInstanceVertex, ComponentInstanceAttribute attribute, String instanceId) {
+ // Only if valueUniqueId is not empty, then its belongs to the
+ // instance
+ if (attribute.getValueUniqueUid() != null) {
+ attribute.setValueUniqueUid(null);
+ Either<Integer, StorageOperationStatus> counterRes = increaseAndGetResourceInstanceSpecificCounter(createdInstanceVertex, GraphPropertiesDictionary.ATTRIBUTE_COUNTER);
+ if (counterRes.isRight()) {
+ return counterRes.right().value();
+ } else {
+ Either<AttributeValueData, TitanOperationStatus> addAttributeToResourceInstance = addAttributeToResourceInstance(attribute, instanceId, counterRes.left().value());
+
+ if (addAttributeToResourceInstance.isRight()) {
+ TitanOperationStatus status = addAttributeToResourceInstance.right().value();
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+
+ }
+
+ private void connectAttValueDataToComponentInstanceData(Wrapper<TitanOperationStatus> errorWrapper, ComponentInstanceData compIns, AttributeValueData attValueData) {
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(compIns, attValueData, GraphEdgeLabels.ATTRIBUTE_VALUE, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ errorWrapper.setInnerElement(operationStatus);
+ BeEcompErrorManager.getInstance().logInternalFlowError("connectAttValueDataToComponentInstanceData",
+ "Failed to associate resource instance " + compIns.getUniqueId() + " attribute value " + attValueData.getUniqueId() + " in graph. status is " + operationStatus, ErrorSeverity.ERROR);
+ }
+ }
+
+ private void connectInputValueDataToComponentInstanceData(Wrapper<TitanOperationStatus> errorWrapper, ComponentInstanceData compIns, InputValueData attValueData) {
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(compIns, attValueData, GraphEdgeLabels.INPUT_VALUE, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ errorWrapper.setInnerElement(operationStatus);
+ BeEcompErrorManager.getInstance().logInternalFlowError("connectInputValueDataToComponentInstanceData",
+ "Failed to associate resource instance " + compIns.getUniqueId() + " input value " + attValueData.getUniqueId() + " in graph. status is " + operationStatus, ErrorSeverity.ERROR);
+ }
+ }
+
+ private void connectAttValueDataToAttData(Wrapper<TitanOperationStatus> errorWrapper, AttributeData attData, AttributeValueData attValueData) {
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(attValueData, attData, GraphEdgeLabels.ATTRIBUTE_IMPL, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("connectAttValueDataToAttData",
+ "Failed to associate attribute value " + attValueData.getUniqueId() + " to attribute " + attData.getUniqueId() + " in graph. status is " + operationStatus, ErrorSeverity.ERROR);
+
+ errorWrapper.setInnerElement(operationStatus);
+ }
+ }
+
+ private void connectInputValueDataToInputData(Wrapper<TitanOperationStatus> errorWrapper, InputsData attData, InputValueData attValueData) {
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(attValueData, attData, GraphEdgeLabels.INPUT_IMPL, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("connectInputValueDataToInputData", "Failed to associate input value " + attValueData.getUniqueId() + " to input " + attData.getUniqueId() + " in graph. status is " + operationStatus,
+ ErrorSeverity.ERROR);
+
+ errorWrapper.setInnerElement(operationStatus);
+ }
+ }
+
+ private void createAttributeValueDataNode(ComponentInstanceAttribute attributeInstanceProperty, Integer index, Wrapper<TitanOperationStatus> errorWrapper, ComponentInstanceData resourceInstanceData,
+ Wrapper<AttributeValueData> attValueDataWrapper) {
+ String valueUniqueUid = attributeInstanceProperty.getValueUniqueUid();
+ if (valueUniqueUid == null) {
+
+ String attValueDatauniqueId = UniqueIdBuilder.buildResourceInstanceAttributeValueUid(resourceInstanceData.getUniqueId(), index);
+ AttributeValueData attributeValueData = buildAttributeValueDataFromComponentInstanceAttribute(attributeInstanceProperty, attValueDatauniqueId);
+
+ log.debug("Before adding attribute value to graph {}", attributeValueData);
+ Either<AttributeValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(attributeValueData, AttributeValueData.class);
+ log.debug("After adding attribute value to graph {}", attributeValueData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ errorWrapper.setInnerElement(operationStatus);
+ } else {
+ attValueDataWrapper.setInnerElement(createNodeResult.left().value());
+ }
+
+ } else {
+ BeEcompErrorManager.getInstance().logInternalFlowError("CreateAttributeValueDataNode", "attribute value already exists.", ErrorSeverity.ERROR);
+ errorWrapper.setInnerElement(TitanOperationStatus.ALREADY_EXIST);
+ }
+ }
+
+ /*
+ * private void createInputValueDataNode(ComponentInstanceInput inputInstanceProperty, Integer index, Wrapper<TitanOperationStatus> errorWrapper, ComponentInstanceData resourceInstanceData, Wrapper<AttributeValueData> attValueDataWrapper) {
+ * String valueUniqueUid = inputInstanceProperty.getValueUniqueUid(); if (valueUniqueUid == null) {
+ *
+ * String attValueDatauniqueId = UniqueIdBuilder.buildResourceInstanceInputValueUid(resourceInstanceData. getUniqueId(), index); AttributeValueData attributeValueData = buildAttributeValueDataFromComponentInstanceAttribute( inputInstanceProperty,
+ * attValueDatauniqueId);
+ *
+ * log.debug("Before adding attribute value to graph {}", attributeValueData); Either<AttributeValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(attributeValueData, AttributeValueData.class);
+ * log.debug("After adding attribute value to graph {}", attributeValueData);
+ *
+ * if (createNodeResult.isRight()) { TitanOperationStatus operationStatus = createNodeResult.right().value(); errorWrapper.setInnerElement(operationStatus); } else { attValueDataWrapper.setInnerElement(createNodeResult.left().value()); }
+ *
+ * } else { BeEcompErrorManager.getInstance().logInternalFlowError( "CreateAttributeValueDataNode", "attribute value already exists.", ErrorSeverity.ERROR); errorWrapper.setInnerElement(TitanOperationStatus.ALREADY_EXIST); } }
+ */
+
+ private AttributeValueData buildAttributeValueDataFromComponentInstanceAttribute(ComponentInstanceAttribute resourceInstanceAttribute, String uniqueId) {
+ AttributeValueData attributeValueData = new AttributeValueData();
+ attributeValueData.setUniqueId(uniqueId);
+ attributeValueData.setHidden(resourceInstanceAttribute.isHidden());
+ attributeValueData.setValue(resourceInstanceAttribute.getValue());
+ attributeValueData.setType(resourceInstanceAttribute.getType());
+ long currentTimeMillis = System.currentTimeMillis();
+ attributeValueData.setCreationTime(currentTimeMillis);
+ attributeValueData.setModificationTime(currentTimeMillis);
+ return attributeValueData;
+ }
+
+ private InputValueData buildAttributeValueDataFromComponentInstanceAttribute(ComponentInstanceInput resourceInstanceInput, String uniqueId) {
+ InputValueData inputValueData = new InputValueData();
+ inputValueData.setUniqueId(uniqueId);
+ inputValueData.setHidden(resourceInstanceInput.isHidden());
+ inputValueData.setValue(resourceInstanceInput.getValue());
+ inputValueData.setType(resourceInstanceInput.getType());
+ long currentTimeMillis = System.currentTimeMillis();
+ inputValueData.setCreationTime(currentTimeMillis);
+ inputValueData.setModificationTime(currentTimeMillis);
+ return inputValueData;
+ }
+
+ private StorageOperationStatus cloneResourceInstancePropertyValues(ComponentInstance toResourceInstance, ComponentInstance fromResourceInstance) {
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> allProperties = propertyOperation.getAllPropertiesOfResourceInstanceOnlyPropertyDefId(fromResourceInstance.getUniqueId());
+ if (allProperties.isRight()) {
+ TitanOperationStatus status = allProperties.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+
+ List<ComponentInstanceProperty> propertiesOnInstance = allProperties.left().value();
+ if (propertiesOnInstance == null || propertiesOnInstance.isEmpty() == true) {
+ return StorageOperationStatus.OK;
+ }
+
+ for (ComponentInstanceProperty property : propertiesOnInstance) {
+
+ // Only if valueUniqueId is not empty, then its belongs to the
+ // instance
+ if (property.getValueUniqueUid() != null) {
+ property.setValueUniqueUid(null);
+ List<PropertyRule> rules = property.getRules();
+ if (rules != null) {
+ for (PropertyRule propertyRule : rules) {
+ propertyRule.replaceFirstToken(toResourceInstance.getUniqueId());
+ }
+ }
+
+ } else {
+ continue;
+ }
+
+ String resourceInstanceId = toResourceInstance.getUniqueId();
+
+ Either<Integer, StorageOperationStatus> counterRes = this.increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, GraphPropertiesDictionary.PROPERTY_COUNTER, true);
+
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourcePropertyCounter failed resource instance {} property {}", resourceInstanceId, property);
+ StorageOperationStatus status = counterRes.right().value();
+ return status;
+ }
+ Integer index = counterRes.left().value();
+
+ Either<PropertyValueData, TitanOperationStatus> addPropertyToResourceInstance = this.addPropertyToResourceInstance(property, toResourceInstance.getUniqueId(), false, index);
+
+ if (addPropertyToResourceInstance.isRight()) {
+ TitanOperationStatus status = addPropertyToResourceInstance.right().value();
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus cloneResourceInstancePropertyValues(TitanVertex toResourceInstance, ComponentInstance fromResourceInstance, Map<String, List<ComponentInstanceProperty>> inputsPropMap, Resource newResource) {
+
+ String riId = (String) titanGenericDao.getProperty(toResourceInstance, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> allProperties = propertyOperation.getAllPropertiesOfResourceInstanceOnlyPropertyDefId(fromResourceInstance.getUniqueId());
+ List<InputDefinition> newInputs = newResource.getInputs();
+ //
+ if (allProperties.isRight()) {
+ TitanOperationStatus status = allProperties.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+
+ List<ComponentInstanceProperty> propertiesOnInstance = allProperties.left().value();
+ if (propertiesOnInstance == null || propertiesOnInstance.isEmpty() == true) {
+ return StorageOperationStatus.OK;
+ }
+
+ for (ComponentInstanceProperty property : propertiesOnInstance) {
+
+ // Only if valueUniqueId is not empty, then its belongs to the
+ // instance
+ if (property.getValueUniqueUid() != null) {
+ property.setValueUniqueUid(null);
+ List<PropertyRule> rules = property.getRules();
+ if (rules != null) {
+ for (PropertyRule propertyRule : rules) {
+ propertyRule.replaceFirstToken(riId);
+ }
+ }
+
+ } else {
+ continue;
+ }
+
+ String resourceInstanceId = riId;
+
+ Either<Integer, StorageOperationStatus> counterRes = this.increaseAndGetResourceInstanceSpecificCounter(toResourceInstance, GraphPropertiesDictionary.PROPERTY_COUNTER);
+
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourcePropertyCounter failed resource instance {} property {}", resourceInstanceId, property);
+ StorageOperationStatus status = counterRes.right().value();
+ return status;
+ }
+ Integer index = counterRes.left().value();
+
+ Either<ComponentInstanceProperty, TitanOperationStatus> addPropertyToResourceInstance = this.addPropertyToResourceInstance(property, toResourceInstance, false, index, resourceInstanceId);
+
+ if (addPropertyToResourceInstance.isRight() && !addPropertyToResourceInstance.right().value().equals(TitanOperationStatus.OK)) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertyToResourceInstance.right().value());
+ return storageStatus;
+ }
+ if (addPropertyToResourceInstance.isLeft()) {
+ ComponentInstanceProperty newProp = addPropertyToResourceInstance.left().value();
+ Set<String> inputsKey = inputsPropMap.keySet();
+ String inputToAssName = null;
+ GetInputValueInfo getInputInfo = null;
+ for (String inputName : inputsKey) {
+ List<ComponentInstanceProperty> propsList = inputsPropMap.get(inputName);
+ Optional<ComponentInstanceProperty> op = propsList.stream().filter(p -> p.getUniqueId().equals(property.getUniqueId())).findAny();
+ if (op.isPresent()) {
+ ComponentInstanceProperty inpProp = op.get();
+ getInputInfo = new GetInputValueInfo();
+ getInputInfo.setPropName(inpProp.getName());
+ getInputInfo.setInputName(inputName);
+ inputToAssName = inputName;
+ break;
+ }
+
+ }
+ if (inputToAssName != null) {
+ for (InputDefinition input1 : newInputs) {
+ if (input1.getName().equals(inputToAssName)) {
+ this.inputOperation.associatePropertyToInput(riId, input1.getUniqueId(), newProp, getInputInfo);
+ break;
+ }
+ }
+ }
+
+ }
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus cloneResourceInstanceInputsValues(ComponentInstance toResourceInstance, ComponentInstance fromResourceInstance, Component comonentTo, Map<String, List<ComponentInstanceInput>> inputsValuesMap) {
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> allProperties = inputOperation.getAllInputsOfResourceInstanceOnlyInputDefId(fromResourceInstance.getUniqueId());
+ if (allProperties.isRight()) {
+ TitanOperationStatus status = allProperties.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+
+ List<ComponentInstanceInput> propertiesOnInstance = allProperties.left().value();
+ if (propertiesOnInstance == null || propertiesOnInstance.isEmpty() == true) {
+ return StorageOperationStatus.OK;
+ }
+ List<InputDefinition> newInputs = comonentTo.getInputs();
+
+ for (ComponentInstanceInput property : propertiesOnInstance) {
+
+ List<InputDefinition> inputToAss = new ArrayList<InputDefinition>();
+ if (newInputs != null && !inputsValuesMap.isEmpty()) {
+
+ Set<String> inputsName = inputsValuesMap.keySet();
+ for (String name : inputsName) {
+ List<ComponentInstanceInput> inputsValue = inputsValuesMap.get(name);
+ if (inputsValue != null) {
+ Optional<ComponentInstanceInput> op = inputsValue.stream().filter(p -> p.getValueUniqueUid().equals(property.getValueUniqueUid())).findAny();
+ if (op.isPresent()) {
+ Optional<InputDefinition> optional = newInputs.stream().filter(e -> e.getName().equals(name)).findAny();
+ if (optional.isPresent()) {
+ inputToAss.add(optional.get());
+ }
+ }
+ }
+ }
+ }
+
+ // Only if valueUniqueId is not empty, then its belongs to the
+ // instance
+ if (property.getValueUniqueUid() != null) {
+ property.setValueUniqueUid(null);
+ List<PropertyRule> rules = property.getRules();
+ if (rules != null) {
+ for (PropertyRule propertyRule : rules) {
+ propertyRule.replaceFirstToken(toResourceInstance.getUniqueId());
+ }
+ }
+
+ } else {
+ continue;
+ }
+
+ String resourceInstanceId = toResourceInstance.getUniqueId();
+
+ Either<Integer, StorageOperationStatus> counterRes = this.increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, GraphPropertiesDictionary.INPUT_COUNTER, true);
+
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourcePropertyCounter failed resource instance {} property {}", resourceInstanceId, property);
+ StorageOperationStatus status = counterRes.right().value();
+ return status;
+ }
+ Integer index = counterRes.left().value();
+
+ Either<InputValueData, TitanOperationStatus> addPropertyToResourceInstance = this.addInputToResourceInstance(property, toResourceInstance.getUniqueId(), index);
+
+ if (addPropertyToResourceInstance.isRight()) {
+ TitanOperationStatus status = addPropertyToResourceInstance.right().value();
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+
+ for (InputDefinition input : inputToAss) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), input.getName());
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), toResourceInstance.getUniqueId());
+
+ GraphNode inputData = new UniqueIdData(NodeTypeEnum.Input, input.getUniqueId());
+ GraphNode propertyData = new UniqueIdData(NodeTypeEnum.InputValue, addPropertyToResourceInstance.left().value().getUniqueId());
+
+ Either<GraphRelation, TitanOperationStatus> addPropRefResult = titanGenericDao.createRelation(inputData, propertyData, GraphEdgeLabels.GET_INPUT, props);
+
+ if (addPropRefResult.isRight()) {
+ TitanOperationStatus status = addPropRefResult.right().value();
+ log.debug("Failed to associate input {} to input value {} in graph. Status is {}", input.getUniqueId(), propertyData.getUniqueId(), status);
+
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+ }
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus cloneResourceInstanceInputsValues(TitanVertex toResourceInstanceVertex, ComponentInstance fromResourceInstance, String instanceId, Resource newResource, Map<String, List<ComponentInstanceInput>> inputsValuesMap) {
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> allProperties = inputOperation.getAllInputsOfResourceInstanceOnlyInputDefId(fromResourceInstance.getUniqueId());
+ if (allProperties.isRight()) {
+ TitanOperationStatus status = allProperties.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+
+ List<ComponentInstanceInput> propertiesOnInstance = allProperties.left().value();
+ if (propertiesOnInstance == null || propertiesOnInstance.isEmpty() == true) {
+ return StorageOperationStatus.OK;
+ }
+
+ for (ComponentInstanceInput property : propertiesOnInstance) {
+
+ // Only if valueUniqueId is not empty, then its belongs to the
+ // instance
+ if (property.getValueUniqueUid() != null) {
+ property.setValueUniqueUid(null);
+ List<PropertyRule> rules = property.getRules();
+ if (rules != null) {
+ for (PropertyRule propertyRule : rules) {
+ propertyRule.replaceFirstToken(instanceId);
+ }
+ }
+
+ } else {
+ continue;
+ }
+
+ String resourceInstanceId = instanceId;
+
+ Either<Integer, StorageOperationStatus> counterRes = this.increaseAndGetResourceInstanceSpecificCounter(toResourceInstanceVertex, GraphPropertiesDictionary.INPUT_COUNTER);
+
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourcePropertyCounter failed resource instance {} property {}", resourceInstanceId, property);
+ StorageOperationStatus status = counterRes.right().value();
+ return status;
+ }
+ Integer index = counterRes.left().value();
+
+ Either<InputValueData, TitanOperationStatus> addPropertyToResourceInstance = this.addInputToResourceInstance(property, resourceInstanceId, index);
+
+ if (addPropertyToResourceInstance.isRight()) {
+ TitanOperationStatus status = addPropertyToResourceInstance.right().value();
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return storageStatus;
+ }
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ public Either<ComponentInstanceProperty, StorageOperationStatus> updatePropertyValueInResourceInstance(ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, boolean inTransaction) {
+
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = null;
+
+ try {
+ // TODO: verify validUniqueId exists
+ Either<PropertyValueData, TitanOperationStatus> eitherStatus = this.updatePropertyOfResourceInstance(resourceInstanceProperty, resourceInstanceId, true);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to add property value {} to resource instance {} in Graph. status is {}", resourceInstanceProperty, resourceInstanceId, eitherStatus.right().value().name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ PropertyValueData propertyValueData = eitherStatus.left().value();
+
+ ComponentInstanceProperty propertyValueResult = propertyOperation.buildResourceInstanceProperty(propertyValueData, resourceInstanceProperty);
+
+ log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult);
+
+ Either<String, TitanOperationStatus> findDefaultValue = propertyOperation.findDefaultValueFromSecondPosition(resourceInstanceProperty.getPath(), propertyValueData.getUniqueId(), resourceInstanceProperty.getDefaultValue());
+ if (findDefaultValue.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(findDefaultValue.right().value()));
+ return result;
+ }
+ String defaultValue = findDefaultValue.left().value();
+ propertyValueResult.setDefaultValue(defaultValue);
+ log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
+
+ result = Either.left(propertyValueResult);
+ return result;
+ }
+ }
+
+ finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private static final class UpdateDataContainer<SomeData, SomeValueData> {
+ final Wrapper<SomeValueData> valueDataWrapper;
+ final Wrapper<SomeData> dataWrapper;
+ final GraphEdgeLabels graphEdge;
+ final Supplier<Class<SomeData>> someDataClassGen;
+ final Supplier<Class<SomeValueData>> someValueDataClassGen;
+ final NodeTypeEnum nodeType;
+ final NodeTypeEnum nodeTypeValue;
+
+ private UpdateDataContainer(GraphEdgeLabels graphEdge, Supplier<Class<SomeData>> someDataClassGen, Supplier<Class<SomeValueData>> someValueDataClassGen, NodeTypeEnum nodeType, NodeTypeEnum nodeTypeValue) {
+ super();
+ this.valueDataWrapper = new Wrapper<>();
+ this.dataWrapper = new Wrapper<>();
+ this.graphEdge = graphEdge;
+ this.someDataClassGen = someDataClassGen;
+ this.someValueDataClassGen = someValueDataClassGen;
+ this.nodeType = nodeType;
+ this.nodeTypeValue = nodeTypeValue;
+ }
+
+ public Wrapper<SomeValueData> getValueDataWrapper() {
+ return valueDataWrapper;
+ }
+
+ public Wrapper<SomeData> getDataWrapper() {
+ return dataWrapper;
+ }
+
+ public GraphEdgeLabels getGraphEdge() {
+ return graphEdge;
+ }
+
+ public Supplier<Class<SomeData>> getSomeDataClassGen() {
+ return someDataClassGen;
+ }
+
+ public Supplier<Class<SomeValueData>> getSomeValueDataClassGen() {
+ return someValueDataClassGen;
+ }
+
+ public NodeTypeEnum getNodeType() {
+ return nodeType;
+ }
+
+ public NodeTypeEnum getNodeTypeValue() {
+ return nodeTypeValue;
+ }
+ }
+
+ @Override
+ public Either<AttributeValueData, TitanOperationStatus> createOrUpdateAttributeOfResourceInstance(ComponentInstanceAttribute attributeInstanceProperty, String resourceInstanceId) {
+ Either<AttributeValueData, TitanOperationStatus> result;
+ // Create
+ if (attributeInstanceProperty.getValueUniqueUid() == null) {
+ Either<Integer, StorageOperationStatus> counterRes = increaseAndGetResourceInstanceSpecificCounter(resourceInstanceId, GraphPropertiesDictionary.ATTRIBUTE_COUNTER, true);
+ if (counterRes.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("createOrUpdateAttributeOfResourceInstance", "Failed to get AttributeValueData Counter", ErrorSeverity.ERROR);
+ result = Either.right(TitanOperationStatus.GENERAL_ERROR);
+
+ } else {
+ result = addAttributeToResourceInstance(attributeInstanceProperty, resourceInstanceId, counterRes.left().value());
+ }
+ }
+ // Update
+ else {
+ result = updateAttributeOfResourceInstance(attributeInstanceProperty, resourceInstanceId);
+ }
+ return result;
+ }
+
+ /**
+ * update value of attribute on resource instance
+ *
+ * @param resourceInstanceAttribute
+ * @param resourceInstanceId
+ * @return
+ */
+ private Either<AttributeValueData, TitanOperationStatus> updateAttributeOfResourceInstance(ComponentInstanceAttribute resourceInstanceAttribute, String resourceInstanceId) {
+
+ Either<AttributeValueData, TitanOperationStatus> result = null;
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ UpdateDataContainer<AttributeData, AttributeValueData> updateDataContainer = new UpdateDataContainer<>(GraphEdgeLabels.ATTRIBUTE_IMPL, (() -> AttributeData.class), (() -> AttributeValueData.class), NodeTypeEnum.Attribute,
+ NodeTypeEnum.AttributeValue);
+ preUpdateElementOfResourceInstanceValidations(updateDataContainer, resourceInstanceAttribute, resourceInstanceId, errorWrapper);
+ if (errorWrapper.isEmpty()) {
+ AttributeValueData attributeValueData = updateDataContainer.getValueDataWrapper().getInnerElement();
+ attributeValueData.setHidden(resourceInstanceAttribute.isHidden());
+ attributeValueData.setValue(resourceInstanceAttribute.getValue());
+ Either<AttributeValueData, TitanOperationStatus> updateRes = titanGenericDao.updateNode(attributeValueData, AttributeValueData.class);
+ if (updateRes.isRight()) {
+ TitanOperationStatus status = updateRes.right().value();
+ errorWrapper.setInnerElement(status);
+ } else {
+ result = Either.left(updateRes.left().value());
+ }
+ }
+ if (!errorWrapper.isEmpty()) {
+ result = Either.right(errorWrapper.getInnerElement());
+ }
+ return result;
+
+ }
+
+ private Either<AttributeValueData, TitanOperationStatus> addAttributeToResourceInstance(ComponentInstanceAttribute attributeInstanceProperty, String resourceInstanceId, Integer index) {
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ Wrapper<ComponentInstanceData> compInsWrapper = new Wrapper<>();
+ Wrapper<AttributeData> attDataWrapper = new Wrapper<>();
+ Wrapper<AttributeValueData> attValueDataWrapper = new Wrapper<>();
+
+ // Verify RI Exist
+ validateRIExist(resourceInstanceId, compInsWrapper, errorWrapper);
+
+ if (errorWrapper.isEmpty()) {
+ // Verify Attribute Exist
+ validateElementExistInGraph(attributeInstanceProperty.getUniqueId(), NodeTypeEnum.Attribute, () -> AttributeData.class, attDataWrapper, errorWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ // Create AttributeValueData that is connected to RI
+ createAttributeValueDataNode(attributeInstanceProperty, index, errorWrapper, compInsWrapper.getInnerElement(), attValueDataWrapper);
+ }
+ if (errorWrapper.isEmpty()) {
+ // Connect AttributeValueData (Att on RI) to AttData (Att on
+ // Resource)
+ connectAttValueDataToAttData(errorWrapper, attDataWrapper.getInnerElement(), attValueDataWrapper.getInnerElement());
+ }
+ if (errorWrapper.isEmpty()) {
+ // Connect AttributeValueData to RI
+ connectAttValueDataToComponentInstanceData(errorWrapper, compInsWrapper.getInnerElement(), attValueDataWrapper.getInnerElement());
+ }
+
+ if (errorWrapper.isEmpty()) {
+ return Either.left(attValueDataWrapper.getInnerElement());
+ } else {
+ return Either.right(errorWrapper.getInnerElement());
+ }
+
+ }
+
+ /**
+ * update value of attribute on resource instance
+ *
+ * @param resourceInstanceProerty
+ * @param resourceInstanceId
+ * @return
+ */
+ public Either<PropertyValueData, TitanOperationStatus> updatePropertyOfResourceInstance(ComponentInstanceProperty resourceInstanceProerty, String resourceInstanceId, boolean isValidate) {
+
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ UpdateDataContainer<PropertyData, PropertyValueData> updateDataContainer = new UpdateDataContainer<>(GraphEdgeLabels.PROPERTY_IMPL, (() -> PropertyData.class), (() -> PropertyValueData.class), NodeTypeEnum.Property,
+ NodeTypeEnum.PropertyValue);
+
+ preUpdateElementOfResourceInstanceValidations(updateDataContainer, resourceInstanceProerty, resourceInstanceId, errorWrapper);
+ if (!errorWrapper.isEmpty()) {
+ return Either.right(errorWrapper.getInnerElement());
+ }
+
+ else {
+ String value = resourceInstanceProerty.getValue();
+ // Specific Validation Logic
+ PropertyData propertyData = updateDataContainer.getDataWrapper().getInnerElement();
+
+ String innerType = null;
+
+ PropertyDataDefinition propDataDef = propertyData.getPropertyDataDefinition();
+ String propertyType = propDataDef.getType();
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+ log.debug("The type of the property {} is {}", propertyData.getUniqueId(), propertyType);
+
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+ SchemaDefinition def = propDataDef.getSchema();
+ if (def == null) {
+ log.debug("Schema doesn't exists for property of type {}", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ innerType = propDef.getType();
+ }
+ // Specific Update Logic
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + status, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value, isValidate, innerType, allDataTypes.left().value());
+
+ String newValue = value;
+ if (isValid.isRight()) {
+ Boolean res = isValid.right().value();
+ if (res == false) {
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ } else {
+ Object object = isValid.left().value();
+ if (object != null) {
+ newValue = object.toString();
+ }
+ }
+ PropertyValueData propertyValueData = updateDataContainer.getValueDataWrapper().getInnerElement();
+ log.debug("Going to update property value from {} to {}", propertyValueData.getValue(), newValue);
+ propertyValueData.setValue(newValue);
+
+ ImmutablePair<String, Boolean> pair = propertyOperation.validateAndUpdateRules(propertyType, resourceInstanceProerty.getRules(), innerType, allDataTypes.left().value(), isValidate);
+ if (pair.getRight() != null && pair.getRight() == false) {
+ BeEcompErrorManager.getInstance().logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceProerty.getName(), propertyType);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ propertyOperation.updateRulesInPropertyValue(propertyValueData, resourceInstanceProerty, resourceInstanceId);
+
+ Either<PropertyValueData, TitanOperationStatus> updateRes = titanGenericDao.updateNode(propertyValueData, PropertyValueData.class);
+ if (updateRes.isRight()) {
+ TitanOperationStatus status = updateRes.right().value();
+ return Either.right(status);
+ } else {
+ return Either.left(updateRes.left().value());
+ }
+ }
+
+ }
+
+ /**
+ * update value of attribute on resource instance
+ *
+ * @param resourceInstanceProerty
+ * @param resourceInstanceId
+ * @return
+ */
+ public Either<InputValueData, TitanOperationStatus> updateInputOfResourceInstance(ComponentInstanceInput resourceInstanceProerty, String resourceInstanceId) {
+
+ Wrapper<TitanOperationStatus> errorWrapper = new Wrapper<>();
+ UpdateDataContainer<PropertyData, InputValueData> updateDataContainer = new UpdateDataContainer<>(GraphEdgeLabels.INPUT_IMPL, (() -> PropertyData.class), (() -> InputValueData.class), NodeTypeEnum.Input, NodeTypeEnum.InputValue);
+
+ preUpdateElementOfResourceInstanceValidations(updateDataContainer, resourceInstanceProerty, resourceInstanceId, errorWrapper);
+ if (!errorWrapper.isEmpty()) {
+ return Either.right(errorWrapper.getInnerElement());
+ }
+
+ else {
+ String value = resourceInstanceProerty.getValue();
+ // Specific Validation Logic
+ PropertyData propertyData = updateDataContainer.getDataWrapper().getInnerElement();
+
+ String innerType = null;
+
+ PropertyDataDefinition propDataDef = propertyData.getPropertyDataDefinition();
+ String propertyType = propDataDef.getType();
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+ log.debug("The type of the property {} is {}", propertyData.getUniqueId(), propertyType);
+
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+ SchemaDefinition def = propDataDef.getSchema();
+ if (def == null) {
+ log.debug("Schema doesn't exists for property of type {}", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ innerType = propDef.getType();
+ }
+ // Specific Update Logic
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + status, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ /*
+ * Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value, innerType, allDataTypes.left().value());
+ *
+ * String newValue = value; if (isValid.isRight()) { Boolean res = isValid.right().value(); if (res == false) { return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT); } } else { Object object = isValid.left().value(); if (object !=
+ * null) { newValue = object.toString(); } } InputValueData propertyValueData = updateDataContainer.getValueDataWrapper().getInnerElement(); log.debug("Going to update property value from {} to {}", propertyValueData.getValue(), newValue); propertyValueData.setValue(newValue);
+ *
+ * ImmutablePair<String, Boolean> pair = propertyOperation.validateAndUpdateRules(propertyType, resourceInstanceProerty.getRules(), innerType, allDataTypes.left().value()); if (pair.getRight() != null && pair.getRight() == false) {
+ * BeEcompErrorManager.getInstance(). logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceProerty.getName(), propertyType); return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT); }
+ * propertyOperation.updateRulesInPropertyValue(propertyValueData, resourceInstanceProerty, resourceInstanceId);
+ *
+ * Either<PropertyValueData, TitanOperationStatus> updateRes = titanGenericDao.updateNode(propertyValueData, PropertyValueData.class); if (updateRes.isRight()) { TitanOperationStatus status = updateRes.right().value(); return
+ * Either.right(status); } else { return Either.left(updateRes.left().value()); }
+ */
+ }
+ return null;
+
+ }
+
+ private <SomeData extends GraphNode, SomeValueData extends GraphNode> void preUpdateElementOfResourceInstanceValidations(UpdateDataContainer<SomeData, SomeValueData> updateDataContainer, IComponentInstanceConnectedElement resourceInstanceProerty,
+ String resourceInstanceId, Wrapper<TitanOperationStatus> errorWrapper) {
+
+ if (errorWrapper.isEmpty()) {
+ // Verify VFC instance Exist
+ validateRIExist(resourceInstanceId, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Example: Verify Property connected to VFC exist
+ validateElementConnectedToComponentExist(updateDataContainer, resourceInstanceProerty, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Example: Verify PropertyValue connected to VFC Instance exist
+ validateElementConnectedToComponentInstanceExist(updateDataContainer, resourceInstanceProerty, errorWrapper);
+ }
+
+ if (errorWrapper.isEmpty()) {
+ // Example: Verify PropertyValue connected Property
+ validateElementConnectedToInstance(updateDataContainer, resourceInstanceProerty, errorWrapper);
+ }
+ }
+
+ private <SomeData extends GraphNode, SomeValueData extends GraphNode> void validateElementConnectedToInstance(UpdateDataContainer<SomeData, SomeValueData> updateDataContainer, IComponentInstanceConnectedElement resourceInstanceProerty,
+ Wrapper<TitanOperationStatus> errorWrapper) {
+ Either<ImmutablePair<SomeData, GraphEdge>, TitanOperationStatus> child = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(updateDataContainer.getNodeTypeValue()), resourceInstanceProerty.getValueUniqueUid(),
+ updateDataContainer.getGraphEdge(), updateDataContainer.getNodeType(), updateDataContainer.getSomeDataClassGen().get());
+
+ if (child.isRight()) {
+ TitanOperationStatus status = child.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ errorWrapper.setInnerElement(status);
+
+ } else {
+ updateDataContainer.getDataWrapper().setInnerElement(child.left().value().left);
+ }
+ }
+
+ private <SomeValueData extends GraphNode, SomeData extends GraphNode> void validateElementConnectedToComponentInstanceExist(UpdateDataContainer<SomeData, SomeValueData> updateDataContainer,
+ IComponentInstanceConnectedElement resourceInstanceProerty, Wrapper<TitanOperationStatus> errorWrapper) {
+ String valueUniqueUid = resourceInstanceProerty.getValueUniqueUid();
+ if (valueUniqueUid == null) {
+ errorWrapper.setInnerElement(TitanOperationStatus.INVALID_ID);
+ } else {
+ Either<SomeValueData, TitanOperationStatus> findPropertyValueRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(updateDataContainer.getNodeTypeValue()), valueUniqueUid, updateDataContainer.getSomeValueDataClassGen().get());
+ if (findPropertyValueRes.isRight()) {
+ TitanOperationStatus status = findPropertyValueRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ errorWrapper.setInnerElement(status);
+ } else {
+ updateDataContainer.getValueDataWrapper().setInnerElement(findPropertyValueRes.left().value());
+ }
+ }
+ }
+
+ private <SomeData extends GraphNode, SomeValueData extends GraphNode> void validateElementConnectedToComponentExist(UpdateDataContainer<SomeData, SomeValueData> updateDataContainer,
+ IComponentInstanceConnectedElement resourceInstanceElementConnected, Wrapper<TitanOperationStatus> errorWrapper) {
+ String uniqueId = resourceInstanceElementConnected.getUniqueId();
+ Either<SomeData, TitanOperationStatus> findPropertyDefRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(updateDataContainer.getNodeType()), uniqueId, updateDataContainer.getSomeDataClassGen().get());
+
+ if (findPropertyDefRes.isRight()) {
+ TitanOperationStatus status = findPropertyDefRes.right().value();
+ errorWrapper.setInnerElement(status);
+ }
+ }
+
+ private void validateRIExist(String resourceInstanceId, Wrapper<TitanOperationStatus> errorWrapper) {
+ validateRIExist(resourceInstanceId, null, errorWrapper);
+ }
+
+ private void validateRIExist(String resourceInstanceId, Wrapper<ComponentInstanceData> compInsDataWrapper, Wrapper<TitanOperationStatus> errorWrapper) {
+ validateElementExistInGraph(resourceInstanceId, NodeTypeEnum.ResourceInstance, () -> ComponentInstanceData.class, compInsDataWrapper, errorWrapper);
+ }
+
+ public <ElementData extends GraphNode> void validateElementExistInGraph(String elementUniqueId, NodeTypeEnum elementNodeType, Supplier<Class<ElementData>> elementClassGen, Wrapper<ElementData> elementDataWrapper,
+ Wrapper<TitanOperationStatus> errorWrapper) {
+ Either<ElementData, TitanOperationStatus> findResInstanceRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(elementNodeType), elementUniqueId, elementClassGen.get());
+ if (findResInstanceRes.isRight()) {
+ TitanOperationStatus status = findResInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ errorWrapper.setInnerElement(status);
+ } else {
+ if (elementDataWrapper != null) {
+ elementDataWrapper.setInnerElement(findResInstanceRes.left().value());
+ }
+ }
+ }
+
+ /**
+ * add property to resource instance
+ *
+ * @param resourceInstanceProperty
+ * @param resourceInstanceId
+ * @param index
+ * @return
+ */
+ public Either<PropertyValueData, TitanOperationStatus> addPropertyToResourceInstance(ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, boolean isValidate, Integer index) {
+
+ Either<ComponentInstanceData, TitanOperationStatus> findResInstanceRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, ComponentInstanceData.class);
+
+ if (findResInstanceRes.isRight()) {
+ TitanOperationStatus status = findResInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ String propertyId = resourceInstanceProperty.getUniqueId();
+ Either<PropertyData, TitanOperationStatus> findPropertyDefRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+
+ if (findPropertyDefRes.isRight()) {
+ TitanOperationStatus status = findPropertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ String valueUniqueUid = resourceInstanceProperty.getValueUniqueUid();
+ if (valueUniqueUid == null) {
+
+ PropertyData propertyData = findPropertyDefRes.left().value();
+ ComponentInstanceData resourceInstanceData = findResInstanceRes.left().value();
+
+ ImmutablePair<TitanOperationStatus, String> isPropertyValueExists = propertyOperation.findPropertyValue(resourceInstanceId, propertyId);
+ if (isPropertyValueExists.getLeft() == TitanOperationStatus.ALREADY_EXIST) {
+ log.debug("The property {} already added to the resource instance {}", propertyId, resourceInstanceId);
+ resourceInstanceProperty.setValueUniqueUid(isPropertyValueExists.getRight());
+ Either<PropertyValueData, TitanOperationStatus> updatePropertyOfResourceInstance = updatePropertyOfResourceInstance(resourceInstanceProperty, resourceInstanceId, isValidate);
+ if (updatePropertyOfResourceInstance.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + updatePropertyOfResourceInstance.right().value(), ErrorSeverity.ERROR);
+ return Either.right(updatePropertyOfResourceInstance.right().value());
+ }
+ return Either.left(updatePropertyOfResourceInstance.left().value());
+ }
+
+ if (isPropertyValueExists.getLeft() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("After finding property value of {} on component instance {}", propertyId, resourceInstanceId);
+ return Either.right(isPropertyValueExists.getLeft());
+ }
+
+ String innerType = null;
+
+ PropertyDataDefinition propDataDef = propertyData.getPropertyDataDefinition();
+ String propertyType = propDataDef.getType();
+ String value = resourceInstanceProperty.getValue();
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+ SchemaDefinition def = propDataDef.getSchema();
+ if (def == null) {
+ log.debug("Schema doesn't exists for property of type {}", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ innerType = propDef.getType();
+ }
+
+ log.debug("Before validateAndUpdatePropertyValue");
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + status, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value, isValidate, innerType, allDataTypes.left().value());
+ log.debug("After validateAndUpdatePropertyValue. isValid = {}", isValid);
+
+ String newValue = value;
+ if (isValid.isRight()) {
+ Boolean res = isValid.right().value();
+ if (res == false) {
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ } else {
+ Object object = isValid.left().value();
+ if (object != null) {
+ newValue = object.toString();
+ }
+ }
+
+ String uniqueId = UniqueIdBuilder.buildResourceInstancePropertyValueUid(resourceInstanceData.getUniqueId(), index);
+ PropertyValueData propertyValueData = new PropertyValueData();
+ propertyValueData.setUniqueId(uniqueId);
+ propertyValueData.setValue(newValue);
+
+ log.debug("Before validateAndUpdateRules");
+ ImmutablePair<String, Boolean> pair = propertyOperation.validateAndUpdateRules(propertyType, resourceInstanceProperty.getRules(), innerType, allDataTypes.left().value(), isValidate);
+ log.debug("After validateAndUpdateRules. pair = {}", pair);
+ if (pair.getRight() != null && pair.getRight() == false) {
+ BeEcompErrorManager.getInstance().logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceProperty.getName(), propertyType);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ propertyOperation.addRulesToNewPropertyValue(propertyValueData, resourceInstanceProperty, resourceInstanceId);
+
+ log.debug("Before adding property value to graph {}", propertyValueData);
+ Either<PropertyValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyValueData, PropertyValueData.class);
+ log.debug("After adding property value to graph {}", propertyValueData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ return Either.right(operationStatus);
+ }
+ propertyValueData = createNodeResult.left().value();
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(propertyValueData, propertyData, GraphEdgeLabels.PROPERTY_IMPL, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ // TODO: change logger
+ log.error("Failed to associate property value " + uniqueId + " to property " + propertyId + " in graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ createRelResult = titanGenericDao.createRelation(resourceInstanceData, propertyValueData, GraphEdgeLabels.PROPERTY_VALUE, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ // TODO: change logger
+ log.error("Failed to associate resource instance " + resourceInstanceId + " property value " + uniqueId + " in graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(propertyValueData);
+ } else {
+ log.error("property value already exists.");
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+
+ }
+
+ public Either<ComponentInstanceProperty, TitanOperationStatus> addPropertyToResourceInstance(ComponentInstanceProperty resourceInstanceProperty, TitanVertex resourceInstanceVertex, boolean isValidate, Integer index, String resourceInstanceId) {
+
+ String propertyId = resourceInstanceProperty.getUniqueId();
+ Either<PropertyData, TitanOperationStatus> findPropertyDefRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+
+ if (findPropertyDefRes.isRight()) {
+ TitanOperationStatus status = findPropertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ String valueUniqueUid = resourceInstanceProperty.getValueUniqueUid();
+ if (valueUniqueUid == null) {
+
+ PropertyData propertyData = findPropertyDefRes.left().value();
+
+ ImmutablePair<TitanOperationStatus, String> isPropertyValueExists = propertyOperation.findPropertyValue(resourceInstanceId, propertyId);
+ if (isPropertyValueExists.getLeft() == TitanOperationStatus.ALREADY_EXIST) {
+ log.trace("The property {} already added to the resource instance {}", propertyId, resourceInstanceId);
+ resourceInstanceProperty.setValueUniqueUid(isPropertyValueExists.getRight());
+ Either<PropertyValueData, TitanOperationStatus> updatePropertyOfResourceInstance = updatePropertyOfResourceInstance(resourceInstanceProperty, resourceInstanceId, isValidate);
+ if (updatePropertyOfResourceInstance.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + updatePropertyOfResourceInstance.right().value(), ErrorSeverity.ERROR);
+ return Either.right(updatePropertyOfResourceInstance.right().value());
+ }
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ if (isPropertyValueExists.getLeft() != TitanOperationStatus.NOT_FOUND) {
+ log.trace("After finding property value of {} on componenet instance {}", propertyId, resourceInstanceId);
+ return Either.right(isPropertyValueExists.getLeft());
+ }
+
+ String innerType = null;
+
+ PropertyDataDefinition propDataDef = propertyData.getPropertyDataDefinition();
+ String propertyType = propDataDef.getType();
+ String value = resourceInstanceProperty.getValue();
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+ SchemaDefinition def = propDataDef.getSchema();
+ if (def == null) {
+ log.debug("Schema doesn't exists for property of type {}", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ innerType = propDef.getType();
+ }
+
+ log.trace("Before validateAndUpdatePropertyValue");
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + status, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value, isValidate, innerType, allDataTypes.left().value());
+ log.trace("After validateAndUpdatePropertyValue. isValid = {}", isValid);
+
+ String newValue = value;
+ if (isValid.isRight()) {
+ Boolean res = isValid.right().value();
+ if (res == false) {
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ } else {
+ Object object = isValid.left().value();
+ if (object != null) {
+ newValue = object.toString();
+ }
+ }
+
+ String uniqueId = UniqueIdBuilder.buildResourceInstancePropertyValueUid(resourceInstanceId, index);
+ PropertyValueData propertyValueData = new PropertyValueData();
+ propertyValueData.setUniqueId(uniqueId);
+ propertyValueData.setValue(newValue);
+
+ log.trace("Before validateAndUpdateRules");
+ ImmutablePair<String, Boolean> pair = propertyOperation.validateAndUpdateRules(propertyType, resourceInstanceProperty.getRules(), innerType, allDataTypes.left().value(), isValidate);
+ log.debug("After validateAndUpdateRules. pair = {}", pair);
+ if (pair.getRight() != null && pair.getRight() == false) {
+ BeEcompErrorManager.getInstance().logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceProperty.getName(), propertyType);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ propertyOperation.addRulesToNewPropertyValue(propertyValueData, resourceInstanceProperty, resourceInstanceId);
+
+ log.trace("Before adding property value to graph {}", propertyValueData);
+ Either<PropertyValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyValueData, PropertyValueData.class);
+ log.trace("After adding property value to graph {}", propertyValueData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ return Either.right(operationStatus);
+ }
+ propertyValueData = createNodeResult.left().value();
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(propertyValueData, propertyData, GraphEdgeLabels.PROPERTY_IMPL, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ // TODO: change logger
+ log.error("Failed to associate property value " + uniqueId + " to property " + propertyId + " in graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ TitanOperationStatus edgeResult = titanGenericDao.createEdge(resourceInstanceVertex, propertyValueData, GraphEdgeLabels.PROPERTY_VALUE, null);
+
+ if (!edgeResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource instance " + resourceInstanceId + " property value " + uniqueId + " in graph. status is " + edgeResult);
+ return Either.right(edgeResult);
+ }
+
+ ComponentInstanceProperty propertyValueResult = propertyOperation.buildResourceInstanceProperty(propertyValueData, resourceInstanceProperty);
+ log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult);
+
+ return Either.left(propertyValueResult);
+ } else {
+ log.debug("property value already exists.");
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+
+ }
+
+ /**
+ * add property to resource instance
+ *
+ * @param resourceInstanceProperty
+ * @param resourceInstanceId
+ * @param index
+ * @return
+ */
+ public Either<InputValueData, TitanOperationStatus> addInputToResourceInstance(ComponentInstanceInput resourceInstanceInput, String resourceInstanceId, Integer index) {
+
+ Either<ComponentInstanceData, TitanOperationStatus> findResInstanceRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, ComponentInstanceData.class);
+
+ if (findResInstanceRes.isRight()) {
+ TitanOperationStatus status = findResInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ String propertyId = resourceInstanceInput.getUniqueId();
+ Either<InputsData, TitanOperationStatus> findPropertyDefRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Input), propertyId, InputsData.class);
+
+ if (findPropertyDefRes.isRight()) {
+ TitanOperationStatus status = findPropertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ String valueUniqueUid = resourceInstanceInput.getValueUniqueUid();
+ if (valueUniqueUid == null) {
+
+ InputsData propertyData = findPropertyDefRes.left().value();
+
+ ComponentInstanceData resourceInstanceData = findResInstanceRes.left().value();
+
+ ImmutablePair<TitanOperationStatus, String> isInputValueExists = inputOperation.findInputValue(resourceInstanceId, propertyId);
+ if (isInputValueExists.getLeft() == TitanOperationStatus.ALREADY_EXIST) {
+ log.debug("The property {} already added to the resource insance {}", propertyId, resourceInstanceId);
+ resourceInstanceInput.setValueUniqueUid(isInputValueExists.getRight());
+ /*
+ * Either<InputValueData, TitanOperationStatus> updatePropertyOfResourceInstance = updatePropertyOfResourceInstance(resourceInstanceInput, resourceInstanceId); if (updatePropertyOfResourceInstance.isRight()) {
+ * BeEcompErrorManager.getInstance().logInternalFlowError( "UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + updatePropertyOfResourceInstance.right().value(), ErrorSeverity.ERROR);
+ * return Either.right(updatePropertyOfResourceInstance.right().value() ); } return Either.left(updatePropertyOfResourceInstance.left().value());
+ */
+ }
+
+ if (isInputValueExists.getLeft() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("After finding input value of {} on compnent instance {}", propertyId, resourceInstanceId);
+ return Either.right(isInputValueExists.getLeft());
+ }
+
+ String innerType = null;
+
+ PropertyDataDefinition propDataDef = propertyData.getPropertyDataDefinition();
+ String propertyType = propDataDef.getType();
+ String value = resourceInstanceInput.getValue();
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+ SchemaDefinition def = propDataDef.getSchema();
+ if (def == null) {
+ log.debug("Schema doesn't exists for property of type {}", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ log.debug("Property in Schema Definition inside property of type {} doesn't exists", type);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ innerType = propDef.getType();
+ }
+
+ log.debug("Before validateAndUpdatePropertyValue");
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("UpdatePropertyValueOnComponentInstance", "Failed to update property value on instance. Status is " + status, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ // Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value, innerType, allDataTypes.left().value());
+ // log.debug("After validateAndUpdatePropertyValue. isValid = {}", isValid);
+
+ /*String newValue = value;
+ if (isValid.isRight()) {
+ Boolean res = isValid.right().value();
+ if (res == false) {
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ } else {
+ Object object = isValid.left().value();
+ if (object != null) {
+ newValue = object.toString();
+ }
+ }*/
+
+ String uniqueId = UniqueIdBuilder.buildResourceInstanceInputValueUid(resourceInstanceData.getUniqueId(), index);
+ InputValueData propertyValueData = new InputValueData();
+ propertyValueData.setUniqueId(uniqueId);
+ propertyValueData.setValue(value);
+
+ log.debug("Before validateAndUpdateRules");
+ ImmutablePair<String, Boolean> pair = propertyOperation.validateAndUpdateRules(propertyType, resourceInstanceInput.getRules(), innerType, allDataTypes.left().value(), true);
+ log.debug("After validateAndUpdateRules. pair = {}", pair);
+ if (pair.getRight() != null && pair.getRight() == false) {
+ BeEcompErrorManager.getInstance().logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceInput.getName(), propertyType);
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ // propertyOperation.addRulesToNewPropertyValue(propertyValueData,
+ // resourceInstanceInput, resourceInstanceId);
+
+ log.debug("Before adding property value to graph {}", propertyValueData);
+ Either<InputValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyValueData, InputValueData.class);
+ log.debug("After adding property value to graph {}", propertyValueData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ return Either.right(operationStatus);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(propertyValueData, propertyData, GraphEdgeLabels.INPUT_IMPL, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ // TODO: change logger
+ log.error("Failed to associate property value {} to property {} in graph. Status is {}", uniqueId, propertyId, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> properties1 = new HashMap<String, Object>();
+
+ properties1.put(GraphEdgePropertiesDictionary.NAME.getProperty(), resourceInstanceData.getComponentInstDataDefinition().getName());
+ properties1.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), resourceInstanceData.getComponentInstDataDefinition().getUniqueId());
+
+ createRelResult = titanGenericDao.createRelation(resourceInstanceData, propertyValueData, GraphEdgeLabels.INPUT_VALUE, properties1);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ // TODO: change logger
+ log.error("Failed to associate resource instance {} property value {} in graph. Status is {}", resourceInstanceId, uniqueId, operationStatus);
+ return Either.right(operationStatus);
+
+ }
+
+ // inputOperation.associatePropertyToInput(resourceInstanceId,
+ // resourceInstanceInput.getInputId(), propertyValueData,
+ // resourceInstanceInput.getName());
+
+ return Either.left(createNodeResult.left().value());
+ } else {
+ log.error("property value already exists.");
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+
+ }
+
+ @Override
+ public Either<ComponentInstanceAttribute, StorageOperationStatus> addAttributeValueToResourceInstance(ComponentInstanceAttribute resourceInstanceAttribute, String resourceInstanceId, Integer index, boolean inTransaction) {
+ Either<ComponentInstanceAttribute, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<AttributeValueData, TitanOperationStatus> eitherStatus = this.addAttributeToResourceInstance(resourceInstanceAttribute, resourceInstanceId, index);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to add attribute value {} to resource instance {} in Graph. status is {}", resourceInstanceAttribute, resourceInstanceId, eitherStatus.right().value().name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ AttributeValueData attributeValueData = eitherStatus.left().value();
+
+ ComponentInstanceAttribute attributeValueResult = attributeOperation.buildResourceInstanceAttribute(attributeValueData, resourceInstanceAttribute);
+ log.debug("The returned ResourceInstanceAttribute is {}", attributeValueResult);
+
+ result = Either.left(attributeValueResult);
+ return result;
+ }
+ }
+
+ finally {
+ handleTransactionCommitRollback(inTransaction, result);
+ }
+ }
+
+ @Override
+ public Either<ComponentInstanceAttribute, StorageOperationStatus> updateAttributeValueInResourceInstance(ComponentInstanceAttribute resourceInstanceAttribute, String resourceInstanceId, boolean inTransaction) {
+
+ Either<ComponentInstanceAttribute, StorageOperationStatus> result = null;
+
+ try {
+ Either<AttributeValueData, TitanOperationStatus> eitherAttributeValue = updateAttributeOfResourceInstance(resourceInstanceAttribute, resourceInstanceId);
+
+ if (eitherAttributeValue.isRight()) {
+ log.error("Failed to add attribute value {} to resource instance {} in Graph. status is {}", resourceInstanceAttribute, resourceInstanceId, eitherAttributeValue.right().value().name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherAttributeValue.right().value()));
+ return result;
+ } else {
+ AttributeValueData attributeValueData = eitherAttributeValue.left().value();
+
+ ComponentInstanceAttribute attributeValueResult = attributeOperation.buildResourceInstanceAttribute(attributeValueData, resourceInstanceAttribute);
+ log.debug("The returned ResourceInstanceAttribute is {}", attributeValueResult);
+
+ result = Either.left(attributeValueResult);
+ return result;
+ }
+ }
+
+ finally {
+ handleTransactionCommitRollback(inTransaction, result);
+ }
+
+ }
+
+ @Override
+ public Either<ComponentInstanceProperty, StorageOperationStatus> addPropertyValueToResourceInstance(ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, Integer index, boolean inTransaction) {
+ return addPropertyValueToResourceInstance(resourceInstanceProperty, resourceInstanceId, true, index, inTransaction);
+ }
+
+ @Override
+ public Either<ComponentInstanceProperty, StorageOperationStatus> addPropertyValueToResourceInstance(ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, boolean isValidate, Integer index, boolean inTransaction) {
+
+ /// #RULES SUPPORT
+ /// Ignore rules received from client till support
+ resourceInstanceProperty.setRules(null);
+ ///
+ ///
+
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<PropertyValueData, TitanOperationStatus> eitherStatus = addPropertyToResourceInstance(resourceInstanceProperty, resourceInstanceId, isValidate, index);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to add property value {} to resource instance {} in Graph. status is {}", resourceInstanceProperty, resourceInstanceId, eitherStatus.right().value().name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ PropertyValueData propertyValueData = eitherStatus.left().value();
+
+ ComponentInstanceProperty propertyValueResult = propertyOperation.buildResourceInstanceProperty(propertyValueData, resourceInstanceProperty);
+ log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult);
+
+ Either<String, TitanOperationStatus> findDefaultValue = propertyOperation.findDefaultValueFromSecondPosition(resourceInstanceProperty.getPath(), resourceInstanceProperty.getUniqueId(), resourceInstanceProperty.getDefaultValue());
+ if (findDefaultValue.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(findDefaultValue.right().value()));
+ return result;
+ }
+ String defaultValue = findDefaultValue.left().value();
+ propertyValueResult.setDefaultValue(defaultValue);
+ log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
+
+ result = Either.left(propertyValueResult);
+ return result;
+ }
+ }
+
+ finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<ComponentInstanceInput, StorageOperationStatus> addInputValueToResourceInstance(ComponentInstanceInput resourceInstanceInput, String resourceInstanceId, Integer index, boolean inTransaction) {
+
+ /// #RULES SUPPORT
+ /// Ignore rules received from client till support
+ resourceInstanceInput.setRules(null);
+ ///
+ ///
+
+ Either<ComponentInstanceInput, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<InputValueData, TitanOperationStatus> eitherStatus = addInputToResourceInstance(resourceInstanceInput, resourceInstanceId, index);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to add input value {} to resource instance {} in Graph. status is {}", resourceInstanceInput, resourceInstanceId, eitherStatus.right().value().name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ InputValueData propertyValueData = eitherStatus.left().value();
+
+ ComponentInstanceInput propertyValueResult = inputOperation.buildResourceInstanceInput(propertyValueData, resourceInstanceInput);
+ log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult);
+
+ Either<String, TitanOperationStatus> findDefaultValue = propertyOperation.findDefaultValueFromSecondPosition(resourceInstanceInput.getPath(), resourceInstanceInput.getUniqueId(), resourceInstanceInput.getDefaultValue());
+ if (findDefaultValue.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(findDefaultValue.right().value()));
+ return result;
+ }
+ String defaultValue = findDefaultValue.left().value();
+ propertyValueResult.setDefaultValue(defaultValue);
+ log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
+
+ result = Either.left(propertyValueResult);
+ return result;
+ }
+ }
+
+ finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<List<ComponentInstanceProperty>, TitanOperationStatus> getComponentInstancesProperties(List<ComponentInstance> resourceInstances, Map<String, List<PropertyDefinition>> alreadyProcessedResources,
+ Map<String, List<ComponentInstanceProperty>> resourceInstancesProperties, Map<String, ImmutablePair<ComponentInstance, Integer>> processedInstances, List<String> path) {
+
+ List<ComponentInstanceProperty> result = new ArrayList<>();
+
+ for (ComponentInstance componentInstance : resourceInstances) {
+
+ path.add(componentInstance.getUniqueId());
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> componentInstancesProperties = getComponentInstanceProperties(componentInstance, alreadyProcessedResources, resourceInstancesProperties, processedInstances, path);
+ if (componentInstancesProperties.isRight()) {
+ TitanOperationStatus status = componentInstancesProperties.right().value();
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ }
+
+ List<ComponentInstanceProperty> compInstancePropertyList = componentInstancesProperties.left().value();
+ if (compInstancePropertyList != null) {
+ result.addAll(compInstancePropertyList);
+ }
+
+ String uniqueId = componentInstance.getUniqueId();
+ if (false == processedInstances.containsKey(uniqueId)) {
+ processedInstances.put(uniqueId, new ImmutablePair<ComponentInstance, Integer>(componentInstance, path.size()));
+ }
+ path.remove(path.size() - 1);
+
+ }
+
+ return Either.left(result);
+ }
+
+ public Either<List<ComponentInstanceProperty>, TitanOperationStatus> getComponentInstanceProperties(ComponentInstance resourceInstance, Map<String, List<PropertyDefinition>> alreadyProcessedResources,
+ Map<String, List<ComponentInstanceProperty>> alreadyProcessedInstances, Map<String, ImmutablePair<ComponentInstance, Integer>> processedInstances, List<String> path) {
+
+ // 1. Go over each instance
+ // 1.1 get all properties of from the parents of the instance
+ // 1.2 get all updated properties
+ // 1.3 find all instances included in the parent of this instance and
+ // run this method on them.
+ if (log.isDebugEnabled())
+ log.debug("Going to update properties of resource instance {}", resourceInstance.getUniqueId());
+ String resourceUid = resourceInstance.getComponentUid();
+
+ List<PropertyDefinition> properties = alreadyProcessedResources.get(resourceUid);
+ if (properties == null) {
+ properties = new ArrayList<>();
+ TitanOperationStatus findAllRes = propertyOperation.findAllResourcePropertiesRecursively(resourceUid, properties);
+ if (findAllRes != TitanOperationStatus.OK) {
+ return Either.right(findAllRes);
+ }
+ alreadyProcessedResources.put(resourceUid, properties);
+ }
+
+ if (log.isDebugEnabled())
+ log.debug("After getting properties of resource {} . Number of properties is {}", resourceUid, (properties == null ? 0 : properties.size()));
+ List<ComponentInstanceProperty> resourceInstancePropertyList = new ArrayList<>();
+ if (false == properties.isEmpty()) {
+
+ // TODO: WE MAY HAVE INDIRECT PROPERTY VALUE ALSO IN CASE NO
+ // PROPERTY ON THIS COMPONENT
+
+ // String resourceInstanceUid = resourceInstance.getUniqueId();
+
+ for (PropertyDefinition propertyDefinition : properties) {
+
+ String defaultValue = propertyDefinition.getDefaultValue();
+ String value = defaultValue;
+ String valueUid = null;
+
+ // String propertyId = propertyDefinition.getUniqueId();
+
+ ComponentInstanceProperty resourceInstanceProperty = new ComponentInstanceProperty(propertyDefinition, value, valueUid);
+
+ resourceInstanceProperty.setPath(cloneList(path));
+
+ // TODO: currently ignore constraints since they are not inuse
+ // and cause to error in convertion to object.
+ resourceInstanceProperty.setConstraints(null);
+
+ resourceInstancePropertyList.add(resourceInstanceProperty);
+
+ }
+
+ }
+
+ OriginTypeEnum originType = resourceInstance.getOriginType();
+
+ Either<List<ComponentInstance>, TitanOperationStatus> findInstancesUnderParentOfInstance = findInstancesUnderParentOfInstance(originType, resourceUid);
+
+ if (findInstancesUnderParentOfInstance.isRight()) {
+ TitanOperationStatus status = findInstancesUnderParentOfInstance.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(status);
+ }
+ } else {
+ List<ComponentInstance> listOfInstances = findInstancesUnderParentOfInstance.left().value();
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> componentInstancesProperties = getComponentInstancesProperties(listOfInstances, alreadyProcessedResources, alreadyProcessedInstances, processedInstances, path);
+ if (componentInstancesProperties.isRight()) {
+ TitanOperationStatus status = componentInstancesProperties.right().value();
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ }
+ List<ComponentInstanceProperty> currentList = componentInstancesProperties.left().value();
+ if (currentList != null) {
+ resourceInstancePropertyList.addAll(currentList);
+ }
+ }
+
+ return Either.left(resourceInstancePropertyList);
+ }
+
+ public Either<List<ComponentInstance>, TitanOperationStatus> findInstancesUnderParentOfInstance(OriginTypeEnum originType, String resourceUid) {
+
+ NodeTypeEnum containerNodeType = null;
+ NodeTypeEnum compInstNodeType = null;
+
+ switch (originType) {
+
+ case VF:
+ containerNodeType = NodeTypeEnum.Resource;
+ compInstNodeType = NodeTypeEnum.Resource;
+ break;
+ case SERVICE:
+ containerNodeType = NodeTypeEnum.Service;
+ compInstNodeType = NodeTypeEnum.Resource;
+ break;
+ case PRODUCT:
+ containerNodeType = NodeTypeEnum.Product;
+ compInstNodeType = NodeTypeEnum.Service;
+ case VFC:
+ case VL:
+ case CP:
+ break;
+ default:
+ break;
+ }
+
+ if (containerNodeType == null || compInstNodeType == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> componentInstancesOfComponent = this.getComponentInstancesOfComponent(resourceUid, containerNodeType, compInstNodeType);
+
+ if (componentInstancesOfComponent.isRight()) {
+ TitanOperationStatus status = componentInstancesOfComponent.right().value();
+ log.debug("After getting instances of {} from type {}. Status is {}", resourceUid, originType, status);
+ return Either.right(status);
+ } else {
+ List<ComponentInstance> listOfInstances = componentInstancesOfComponent.left().value().getLeft();
+ if (log.isDebugEnabled()) {
+ String msg = "After getting instances of {} from type {} {}.";
+ log.debug(msg, resourceUid, originType, (listOfInstances != null ? listOfInstances.size() : 0));
+ if (log.isTraceEnabled())
+ log.trace(msg, resourceUid, originType, listOfInstances);
+ }
+ return Either.left(listOfInstances);
+ }
+
+ }
+
+ private List<String> cloneList(List<String> list) {
+
+ if (list == null) {
+ return null;
+ }
+
+ List<String> clonedList = new ArrayList();
+ clonedList.addAll(list);
+
+ return clonedList;
+ }
+
+ public Either<Map<String, Map<String, ComponentInstanceProperty>>, TitanOperationStatus> findAllPropertyValueOnInstances(Map<String, ImmutablePair<ComponentInstance, Integer>> processedInstances) {
+
+ if (processedInstances == null) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ Set<Entry<String, ImmutablePair<ComponentInstance, Integer>>> entrySet = processedInstances.entrySet();
+
+ Map<String, Map<String, ComponentInstanceProperty>> propertyToInstanceValue = new HashMap<>();
+
+ for (Entry<String, ImmutablePair<ComponentInstance, Integer>> entry : entrySet) {
+
+ String compInstUniqueId = entry.getKey();
+
+ ImmutablePair<ComponentInstance, Integer> pair = entry.getValue();
+
+ ComponentInstance componentInstance = pair.getLeft();
+ Integer level = pair.getRight();
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> propeprtyValueOnCIResult = findPropertyValueOnComponentInstance(componentInstance);
+
+ if (propeprtyValueOnCIResult.isRight()) {
+ TitanOperationStatus status = propeprtyValueOnCIResult.right().value();
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ continue;
+ }
+
+ List<ComponentInstanceProperty> propertyValuesOnCI = propeprtyValueOnCIResult.left().value();
+ if (propeprtyValueOnCIResult != null) {
+ for (ComponentInstanceProperty instanceProperty : propertyValuesOnCI) {
+ boolean result = addPropertyValue(compInstUniqueId, instanceProperty, propertyToInstanceValue);
+ if (result == false) {
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+ }
+ }
+
+ }
+
+ return Either.left(propertyToInstanceValue);
+ }
+
+ private boolean addPropertyValue(String compInstUniqueId, ComponentInstanceProperty instanceProperty, Map<String, Map<String, ComponentInstanceProperty>> propertyToInstanceValue) {
+
+ String propertyUid = instanceProperty.getUniqueId();
+
+ Map<String, ComponentInstanceProperty> map = propertyToInstanceValue.get(propertyUid);
+ if (map == null) {
+ map = new HashMap<>();
+ propertyToInstanceValue.put(propertyUid, map);
+ }
+
+ ComponentInstanceProperty putIfAbsent = map.putIfAbsent(compInstUniqueId, instanceProperty);
+ if (putIfAbsent != null) {
+ BeEcompErrorManager.getInstance().logInternalUnexpectedError("find property value", "Found 2 values on the same instance", ErrorSeverity.ERROR);
+ return false;
+ }
+
+ return true;
+
+ }
+
+ private boolean addInputValue(String compInstUniqueId, ComponentInstanceInput instanceProperty, Map<String, Map<String, ComponentInstanceInput>> propertyToInstanceValue) {
+
+ String propertyUid = instanceProperty.getUniqueId();
+
+ Map<String, ComponentInstanceInput> map = propertyToInstanceValue.get(propertyUid);
+ if (map == null) {
+ map = new HashMap<>();
+ propertyToInstanceValue.put(propertyUid, map);
+ }
+
+ ComponentInstanceInput putIfAbsent = map.putIfAbsent(compInstUniqueId, instanceProperty);
+ if (putIfAbsent != null) {
+ BeEcompErrorManager.getInstance().logInternalUnexpectedError("find property value", "Found 2 values on the same instance", ErrorSeverity.ERROR);
+ return false;
+ }
+
+ return true;
+
+ }
+
+ private Either<List<ComponentInstanceProperty>, TitanOperationStatus> findPropertyValueOnComponentInstance(ComponentInstance componentInstance) {
+ String resourceInstanceUid = componentInstance.getUniqueId();
+ OriginTypeEnum originType = componentInstance.getOriginType();
+
+ NodeTypeEnum instanceNodeType = findInstanceNodeTypeEnumFromOriginType(originType);
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> propertyValuesResult = propertyOperation.getAllPropertiesOfResourceInstanceOnlyPropertyDefId(resourceInstanceUid, instanceNodeType);
+
+ log.debug("After fetching property under resource instance {}", resourceInstanceUid);
+ if (propertyValuesResult.isRight()) {
+ TitanOperationStatus status = propertyValuesResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(status);
+ }
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ return Either.left(propertyValuesResult.left().value());
+
+ }
+
+ private NodeTypeEnum findInstanceNodeTypeEnumFromOriginType(OriginTypeEnum originType) {
+ NodeTypeEnum nodeType = NodeTypeEnum.ResourceInstance;
+ switch (originType) {
+ case SERVICE:
+ nodeType = NodeTypeEnum.ResourceInstance;
+ break;
+ default:
+ break;
+ }
+
+ return nodeType;
+ }
+
+ /**
+ * add capability property values to resource instance
+ *
+ * @param resourceInstanceId
+ * @param capability
+ * @param isNewlyCreatedResourceInstance
+ * @return
+ */
+ public Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> addCapabilityPropertyValuesToResourceInstance(String resourceInstanceId, CapabilityDefinition capability, boolean isNewlyCreatedResourceInstance) {
+ log.debug("Before adding capability property values to resource instance {}.", resourceInstanceId);
+ TitanOperationStatus error = null;
+
+ Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> addCapInstWithPropertiesRes = capabilityInstanceOperation.createCapabilityInstanceOfCapabilityWithPropertyValuesForResourceInstance(resourceInstanceId,
+ capability.getUniqueId(), capability.getName(), capability.getProperties(), !isNewlyCreatedResourceInstance);
+ if (addCapInstWithPropertiesRes.isRight()) {
+ error = addCapInstWithPropertiesRes.right().value();
+ log.debug("Failed to assotiate capability instance to resource instance {}. Status is {}", resourceInstanceId, error);
+ }
+ log.debug("After adding capability property values to resource instance {}. Status is {}", resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(addCapInstWithPropertiesRes.left().value());
+ }
+ return Either.right(error);
+ }
+
+ public TitanOperationStatus addCapabilityPropertyValuesToResourceInstance(TitanVertex resourceInstanceVertex, String resourceInstanceId, CapabilityDefinition capability, boolean isNewlyCreatedResourceInstance) {
+ log.trace("Before adding capability property values to resource instance {}.", resourceInstanceId);
+ TitanOperationStatus error = TitanOperationStatus.OK;
+
+ TitanOperationStatus addCapInstWithPropertiesRes = capabilityInstanceOperation.createCapabilityInstanceOfCapabilityWithPropertyValuesForResourceInstance(resourceInstanceVertex, resourceInstanceId, capability.getUniqueId(),
+ capability.getName(), capability.getProperties(), !isNewlyCreatedResourceInstance);
+ if (!addCapInstWithPropertiesRes.equals(TitanOperationStatus.OK)) {
+ error = addCapInstWithPropertiesRes;
+ log.debug("Failed to assotiate capability instance to resource instance {} . status is {}", resourceInstanceId, error);
+ }
+ log.debug("After adding capability property values to resource instance {}. Status is {}", resourceInstanceId, error);
+
+ return error;
+ }
+
+ /**
+ * update capability property values of capability
+ *
+ * @param resourceInstanceId
+ * @param capabilityId
+ * @param propertyValues
+ * @return
+ */
+ public Either<List<PropertyValueData>, TitanOperationStatus> updateCapabilityPropertyValuesOfResourceInstance(String resourceInstanceId, String capabilityId, List<ComponentInstanceProperty> propertyValues) {
+ log.debug("Before updating property values of capability {} of resource instance {}.", capabilityId, resourceInstanceId);
+ TitanOperationStatus error = null;
+ Either<List<PropertyValueData>, TitanOperationStatus> updateCapabilityPropertyValuesRes = capabilityInstanceOperation.updateCapabilityPropertyValues(resourceInstanceId, capabilityId, propertyValues);
+ if (updateCapabilityPropertyValuesRes.isRight()) {
+ error = updateCapabilityPropertyValuesRes.right().value();
+ log.debug("Failed to update property values of capability {} of resource instance {}. Status is {}", capabilityId, resourceInstanceId, error);
+ }
+ log.debug("After updating property values of capability {} of resource instance {}. Status is {}", capabilityId, resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(updateCapabilityPropertyValuesRes.left().value());
+ }
+ return Either.right(error);
+ }
+
+ /**
+ * delete property values of capability from resource instance
+ *
+ * @param capabilityId
+ * @param resourceInstanceId
+ * @return
+ */
+ public Either<CapabilityInstData, TitanOperationStatus> deletePropertyValuesOfCapabilityFromResourceInstance(String capabilityId, String resourceInstanceId) {
+ log.debug("Before deleting property values of capability {} from resource instance {}.", capabilityId, resourceInstanceId);
+ TitanOperationStatus error = null;
+ Either<CapabilityInstData, TitanOperationStatus> deleteCapInstWithPropertiesRes = null;
+ Either<CapabilityInstData, TitanOperationStatus> getCapInstByCapabilityRes = capabilityInstanceOperation.getCapabilityInstanceOfCapabilityOfResourceInstance(resourceInstanceId, capabilityId);
+ if (getCapInstByCapabilityRes.isRight()) {
+ error = getCapInstByCapabilityRes.right().value();
+ log.debug("Failed to retrieve capability instance of capability {} of resource instance {}. Status is {}", capabilityId, resourceInstanceId, error);
+ }
+ if (error == null) {
+ String capabilityInstanceId = getCapInstByCapabilityRes.left().value().getUniqueId();
+ deleteCapInstWithPropertiesRes = capabilityInstanceOperation.deleteCapabilityInstanceFromResourceInstance(resourceInstanceId, capabilityInstanceId);
+ if (deleteCapInstWithPropertiesRes.isRight()) {
+ error = deleteCapInstWithPropertiesRes.right().value();
+ log.debug("Failed to delete capability instance {} to resource instance {}. Status is {}", capabilityInstanceId, resourceInstanceId, error);
+ }
+ }
+ log.debug("After deleting property values of capability {} from resource instance {}. Status is {}", capabilityId, resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(deleteCapInstWithPropertiesRes.left().value());
+ }
+ return Either.right(error);
+ }
+
+ /**
+ * clone capability instances of resource instance
+ *
+ * @param createdComponentInstance
+ * @param resourceInstance
+ * @return
+ */
+ private Either<Map<ImmutablePair<CapabilityInstData, GraphEdge>, List<PropertyValueData>>, TitanOperationStatus> cloneCapabilityInstancesOfResourceInstance(ComponentInstanceData createdComponentInstance, ComponentInstance resourceInstance) {
+ TitanOperationStatus error = null;
+ String resourceInstanceId = resourceInstance.getUniqueId();
+ log.debug("Before cloning of capability instances of resource instance {}.", resourceInstanceId);
+
+ Map<ImmutablePair<CapabilityInstData, GraphEdge>, List<PropertyValueData>> result = new HashMap<>();
+ Either<ImmutablePair<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> cloneAssociateCIWithPropertyValuesRes;
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getAllCapabilityInstancesRes = capabilityInstanceOperation.getAllCapabilityInstancesOfResourceInstance(resourceInstanceId);
+ if (getAllCapabilityInstancesRes.isRight() && !getAllCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = getAllCapabilityInstancesRes.right().value();
+ log.debug("Failed to get capability instances of component instance {}. Status is {}", resourceInstanceId, error);
+ }
+ if (getAllCapabilityInstancesRes.isLeft()) {
+ List<ImmutablePair<CapabilityInstData, GraphEdge>> capabilityInstances = getAllCapabilityInstancesRes.left().value();
+ Map<String, List<CapabilityDefinition>> allCapabilitiesMap = resourceInstance.getCapabilities();
+ List<CapabilityDefinition> allCapabilitiesList = new ArrayList<>();
+ for (List<CapabilityDefinition> curList : allCapabilitiesMap.values()) {
+ allCapabilitiesList.addAll(curList);
+ }
+ Map<String, CapabilityDefinition> capabilities = allCapabilitiesList.stream().collect(Collectors.toMap(CapabilityDefinition::getUniqueId, Function.identity()));
+ String propertyName = GraphPropertiesDictionary.CAPABILITY_ID.getProperty();
+ for (ImmutablePair<CapabilityInstData, GraphEdge> capabilityInstPair : capabilityInstances) {
+ String capabilityId = (String) capabilityInstPair.getRight().getProperties().get(propertyName);
+ CapabilityDefinition relatedCapability = capabilities.get(capabilityId);
+ cloneAssociateCIWithPropertyValuesRes = capabilityInstanceOperation.cloneAssociateCapabilityInstanceWithPropertyValues(createdComponentInstance, relatedCapability, capabilityInstPair);
+ if (cloneAssociateCIWithPropertyValuesRes.isRight()) {
+ error = cloneAssociateCIWithPropertyValuesRes.right().value();
+ log.debug("Failed to clone capability instances {} of component instance {}. Status is {}", capabilityInstPair.getLeft().getUniqueId(), resourceInstanceId, error);
+ break;
+ } else {
+ result.put(new ImmutablePair<CapabilityInstData, GraphEdge>(cloneAssociateCIWithPropertyValuesRes.left().value().getLeft(), capabilityInstPair.getRight()), cloneAssociateCIWithPropertyValuesRes.left().value().getRight());
+ }
+ }
+ }
+ log.debug("After cloning of capability instance of resource instance {}. Status is {}", resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(result);
+ }
+ return Either.right(error);
+ }
+
+ private Either<List<ImmutablePair<TitanVertex, GraphEdge>>, TitanOperationStatus> cloneCapabilityInstancesOfResourceInstance(TitanVertex componentInstanceVertex, ComponentInstance resourceInstance) {
+ TitanOperationStatus error = null;
+ String resourceInstanceId = resourceInstance.getUniqueId();
+ log.debug("Before cloning of capability instances of resource instance {}.", resourceInstanceId);
+
+ Either<TitanVertex, TitanOperationStatus> cloneAssociateCIWithPropertyValuesRes = null;
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getAllCapabilityInstancesRes = capabilityInstanceOperation.getAllCapabilityInstancesOfResourceInstance(resourceInstanceId);
+ if (getAllCapabilityInstancesRes.isRight() && !getAllCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = getAllCapabilityInstancesRes.right().value();
+ log.debug("Failed to get capability instances of component instance {}. status is {}", resourceInstanceId, error);
+ }
+ List<ImmutablePair<TitanVertex, GraphEdge>> list = new ArrayList<>();
+ if (getAllCapabilityInstancesRes.isLeft()) {
+ List<ImmutablePair<CapabilityInstData, GraphEdge>> capabilityInstances = getAllCapabilityInstancesRes.left().value();
+ Map<String, List<CapabilityDefinition>> allCapabilitiesMap = resourceInstance.getCapabilities();
+ List<CapabilityDefinition> allCapabilitiesList = new ArrayList<>();
+ for (List<CapabilityDefinition> curList : allCapabilitiesMap.values()) {
+ allCapabilitiesList.addAll(curList);
+ }
+ Map<String, CapabilityDefinition> capabilities = allCapabilitiesList.stream().collect(Collectors.toMap(CapabilityDefinition::getUniqueId, Function.identity()));
+ String propertyName = GraphPropertiesDictionary.CAPABILITY_ID.getProperty();
+ for (ImmutablePair<CapabilityInstData, GraphEdge> capabilityInstPair : capabilityInstances) {
+ String capabilityId = (String) capabilityInstPair.getRight().getProperties().get(propertyName);
+ CapabilityDefinition relatedCapability = capabilities.get(capabilityId);
+ cloneAssociateCIWithPropertyValuesRes = capabilityInstanceOperation.cloneAssociateCapabilityInstanceWithPropertyValues(componentInstanceVertex, relatedCapability, capabilityInstPair);
+ if (cloneAssociateCIWithPropertyValuesRes.isRight()) {
+ error = cloneAssociateCIWithPropertyValuesRes.right().value();
+ log.debug("Failed to clone capability instances {} of component instance {}. status is {}", capabilityInstPair.getLeft().getUniqueId(), resourceInstanceId, error);
+ break;
+ } else {
+ list.add(new ImmutablePair<TitanVertex, GraphEdge>(cloneAssociateCIWithPropertyValuesRes.left().value(), capabilityInstPair.right));
+ }
+ }
+ }
+ log.debug("After cloning of capability instance of resource instance {}. Status is {}", resourceInstanceId, error);
+ if (error == null) {
+ return Either.left(list);
+ }
+ return Either.right(error);
+ }
+
+ public Either<List<ComponentInstance>, StorageOperationStatus> getAllComponentInstancesMetadataOnly(String componentId, NodeTypeEnum containerNodeType) {
+
+ List<ComponentInstance> componentInstancesResult = new ArrayList<ComponentInstance>();
+ Either<List<ComponentInstance>, StorageOperationStatus> result = Either.left(componentInstancesResult);
+
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> resourceInstancesRes = getAllComponentInstanceFromGraph(componentId, containerNodeType, false);
+
+ if (resourceInstancesRes.isRight()) {
+
+ if (log.isDebugEnabled()) {
+ log.debug("Resource instance was found under service {} . status is {} ", componentId, resourceInstancesRes.right().value());
+ }
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(resourceInstancesRes.right().value()));
+ }
+
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> resourceInstances = resourceInstancesRes.left().value();
+ if (resourceInstances != null && false == resourceInstances.isEmpty()) {
+
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> immutablePair : resourceInstances) {
+ ComponentInstanceData resourceInstanceData = immutablePair.getKey();
+ if (log.isDebugEnabled()) {
+ log.debug("Going to fetch the relationships of resource instance {}", resourceInstanceData);
+ }
+ componentInstancesResult.add(new ComponentInstance(resourceInstanceData.getComponentInstDataDefinition()));
+
+ }
+ }
+
+ return result;
+ }
+
+ public Either<List<CapabilityDefinition>, TitanOperationStatus> updateCapDefPropertyValues(ComponentInstance componentInstance, List<CapabilityDefinition> capabilityDefList) {
+ String componentInstanceId = componentInstance.getUniqueId();
+ log.debug("Before updating property values of capabilities of component istance {}.", componentInstanceId);
+ TitanOperationStatus error = null;
+ NodeTypeEnum nodeType = NodeTypeEnum.getByNameIgnoreCase(componentInstance.getOriginType().getInstanceType().trim());
+
+ log.debug("Before getting all capability instances of component istance {}.", componentInstanceId);
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> getCapabilityInstancesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), componentInstanceId, GraphEdgeLabels.CAPABILITY_INST,
+ NodeTypeEnum.CapabilityInst, CapabilityInstData.class);
+ if (getCapabilityInstancesRes.isRight() && !getCapabilityInstancesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ error = getCapabilityInstancesRes.right().value();
+ log.debug("Failed to retrieve capability Instances of resource instance {}. Status is {}", componentInstance.getName(), error);
+ }
+ log.debug("After getting all capability instances of component istance {}. Status is {}", componentInstanceId, error);
+ Map<String, Map<String, PropertyValueData>> overridedCapabilitiesHM = new HashMap<>();
+ if (getCapabilityInstancesRes.isLeft()) {
+ List<ImmutablePair<CapabilityInstData, GraphEdge>> capabilityInstDataPair = getCapabilityInstancesRes.left().value();
+
+ for (ImmutablePair<CapabilityInstData, GraphEdge> curCapabilityPair : capabilityInstDataPair) {
+ CapabilityInstData curCapabilityInst = curCapabilityPair.getLeft();
+ String curCapInstUid = curCapabilityInst.getUniqueId();
+
+ log.debug("Before getting all property values of capability instance {} of component istance {}.", curCapInstUid, componentInstanceId);
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> getOverridedPropertyValuesRes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.getByName(curCapabilityInst.getLabel())),
+ curCapInstUid, GraphEdgeLabels.PROPERTY_VALUE, NodeTypeEnum.PropertyValue, PropertyValueData.class);
+ if (getOverridedPropertyValuesRes.isRight()) {
+ error = getOverridedPropertyValuesRes.right().value();
+ log.debug("Failed to retrieve property values of capability instance {}. Status is {}", curCapInstUid, error);
+ }
+
+ log.debug("After getting all property values of capability instance {} of component istance {}. Status is {}", curCapInstUid, componentInstanceId, error);
+ Map<String, PropertyValueData> overridedPropertyValuesHM = new HashMap<>();
+ List<ImmutablePair<PropertyValueData, GraphEdge>> overridedPropertyValues = getOverridedPropertyValuesRes.left().value();
+ for (ImmutablePair<PropertyValueData, GraphEdge> curPropertyValuePair : overridedPropertyValues) {
+ PropertyValueData curPropertyValue = curPropertyValuePair.getLeft();
+ String propertyValueUid = curPropertyValue.getUniqueId();
+ log.debug("Before getting property related to property value {} of capability instance {} of component istance {}.", propertyValueUid, curCapInstUid, componentInstanceId);
+ Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> getPropertyDataRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.getByName(curPropertyValue.getLabel())), propertyValueUid,
+ GraphEdgeLabels.PROPERTY_IMPL, NodeTypeEnum.Property, PropertyData.class);
+ if (getPropertyDataRes.isRight()) {
+ error = getOverridedPropertyValuesRes.right().value();
+ log.debug("Failed to retrieve property of property value {} Status is {}", propertyValueUid, error);
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("After getting property related to property value {} of capability instance {} of component istance {}. Status is {}", propertyValueUid, curCapInstUid, componentInstanceId, error);
+ }
+ PropertyData propertyData = getPropertyDataRes.left().value().getLeft();
+ overridedPropertyValuesHM.put((String) propertyData.getUniqueId(), curPropertyValue);
+ }
+ overridedCapabilitiesHM.put((String) curCapabilityPair.getRight().getProperties().get(GraphPropertiesDictionary.CAPABILITY_ID.getProperty()), overridedPropertyValuesHM);
+ }
+ }
+ if (error == null && !overridedCapabilitiesHM.isEmpty()) {
+ updateCapabilityPropertyValues(componentInstance.getCapabilities(), capabilityDefList, overridedCapabilitiesHM);
+ }
+ log.debug("After updating property values of capabilities of component istance {}. Status is {}", componentInstanceId, error);
+ if (error == null) {
+ return Either.left(capabilityDefList);
+ }
+ return Either.right(error);
+ }
+
+ private void updateCapabilityPropertyValues(Map<String, List<CapabilityDefinition>> capabilitiesOfRI, List<CapabilityDefinition> capabilitiesOfContainer, Map<String, Map<String, PropertyValueData>> overridedCapabilitiesHM) {
+
+ capabilitiesOfContainer.stream().filter(capability -> overridedCapabilitiesHM.containsKey(capability.getUniqueId())).forEach(capability -> {
+ boolean updateProperties = false;
+ for (ComponentInstanceProperty property : capability.getProperties()) {
+ if (overridedCapabilitiesHM.get(capability.getUniqueId()).containsKey(property.getUniqueId())) {
+ property.setValue(overridedCapabilitiesHM.get(capability.getUniqueId()).get(property.getUniqueId()).getValue());
+ property.setValueUniqueUid(overridedCapabilitiesHM.get(capability.getUniqueId()).get(property.getUniqueId()).getUniqueId());
+ updateProperties = true;
+ }
+ }
+ if (updateProperties) {
+ capabilitiesOfRI.get(capability.getType()).get(0).setProperties(capability.getProperties());
+ }
+ });
+ }
+
+ @Override
+ public Either<ComponentInstanceInput, StorageOperationStatus> updateInputValueInResourceInstance(ComponentInstanceInput input, String resourceInstanceId, boolean b) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Either<Map<String, ArtifactDefinition>, StorageOperationStatus> fetchCIEnvArtifacts(String componentInstanceId) {
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> result = artifactOperation.getArtifacts(componentInstanceId, NodeTypeEnum.ResourceInstance, true, ArtifactGroupTypeEnum.DEPLOYMENT.getType());
+ if (result.isRight() && result.right().value() == StorageOperationStatus.NOT_FOUND)
+ return Either.right(StorageOperationStatus.OK);
+ return result;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperation.java
new file mode 100644
index 0000000000..b243c6ea4d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperation.java
@@ -0,0 +1,2886 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.ImmutableTriple;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.QueryType;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanGraphClient;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.category.GroupingDataDefinition;
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.cache.ComponentCache;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IRequirementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ProductMetadataData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.GroupingData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+import org.openecomp.sdc.be.utils.CommonBeUtils;
+import org.openecomp.sdc.be.workers.Job;
+import org.openecomp.sdc.be.workers.Manager;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openecomp.sdc.common.util.StreamUtils;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanGraphQuery;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+public abstract class ComponentOperation {
+ private static Logger log = LoggerFactory.getLogger(ComponentOperation.class.getName());
+
+ @Autowired
+ protected TitanGenericDao titanGenericDao;
+
+ @Autowired
+ protected IArtifactOperation artifactOperation;
+
+ @Autowired
+ protected IElementOperation elementOperation;
+
+ @Autowired
+ protected ICapabilityOperation capabilityOperation;
+
+ @Autowired
+ protected IRequirementOperation requirementOperation;
+
+ @Autowired
+ protected ComponentInstanceOperation componentInstanceOperation;
+
+ @Autowired
+ private PropertyOperation propertyOperation;
+
+ @Autowired
+ protected InputsOperation inputOperation;
+
+ @Autowired
+ protected IAdditionalInformationOperation additionalInformationOperation;
+
+ @Autowired
+ protected GroupOperation groupOperation;
+
+ @Autowired
+ protected InputsOperation inputsOperation;
+
+ @Autowired
+ protected ApplicationDataTypeCache applicationDataTypeCache;
+
+ @Autowired
+ private ComponentCache componentCache;
+
+ private static Pattern uuidNewVersion = Pattern.compile("^\\d{1,}.1");
+
+ protected Gson prettyJson = new GsonBuilder().setPrettyPrinting().create();
+
+ protected Either<List<TagData>, StorageOperationStatus> createNewTagsList(List<String> tags) {
+
+ List<TagData> existingTags = new ArrayList<TagData>();
+ List<TagData> tagsToCreate = new ArrayList<TagData>();
+ Either<List<TagData>, TitanOperationStatus> either = titanGenericDao.getAll(NodeTypeEnum.Tag, TagData.class);
+
+ if ((either.isRight()) && (either.right().value() != TitanOperationStatus.NOT_FOUND)) {
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ } else if (either.isLeft()) {
+ existingTags = either.left().value();
+ }
+
+ for (String tagName : tags) {
+ TagData tag = new TagData(tagName);
+ if ((existingTags == null) || (!existingTags.contains(tag))) {
+ tagsToCreate.add(tag);
+ }
+ }
+ return Either.left(tagsToCreate);
+
+ }
+
+ protected StorageOperationStatus createTagNodesOnGraph(List<TagData> tagsToCreate) {
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ // In order to avoid duplicate tags
+ tagsToCreate = ImmutableSet.copyOf(tagsToCreate).asList();
+ if (tagsToCreate != null && false == tagsToCreate.isEmpty()) {
+ for (TagData tagData : tagsToCreate) {
+ log.debug("Before creating tag {}", tagData);
+ Either<TagData, TitanOperationStatus> createTagResult = titanGenericDao.createNode(tagData, TagData.class);
+ if (createTagResult.isRight()) {
+ TitanOperationStatus status = createTagResult.right().value();
+ log.error("Cannot create {} in the graph. Status is {}", tagData, status);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+
+ }
+ log.debug("After creating tag {}", tagData);
+ }
+ }
+ return result;
+ }
+
+ public Either<Component, StorageOperationStatus> getLatestComponentByUuid(NodeTypeEnum nodeType, String uuid) {
+ Either<Component, StorageOperationStatus> getComponentResult = null;
+ Either<ComponentMetadataData, StorageOperationStatus> latestComponentMetadataRes = getLatestComponentMetadataByUuid(nodeType, uuid);
+ if (latestComponentMetadataRes.isRight()) {
+ getComponentResult = Either.right(latestComponentMetadataRes.right().value());
+ }
+ if (getComponentResult == null) {
+ ComponentMetadataData latestVersion = latestComponentMetadataRes.left().value();
+ String id = latestVersion.getMetadataDataDefinition().getUniqueId();
+ Either<Component, StorageOperationStatus> component = getComponent(id, false);
+ if (component.isRight()) {
+ log.debug("Couldn't fetch component with type {} and id {}, error: {}", nodeType, id, component.right().value());
+ getComponentResult = Either.right(component.right().value());
+ } else {
+ getComponentResult = Either.left(component.left().value());
+ }
+ }
+ return getComponentResult;
+ }
+
+ public Either<ComponentMetadataData, StorageOperationStatus> getLatestComponentMetadataByUuid(NodeTypeEnum nodeType, String uuid) {
+
+ Either<ComponentMetadataData, StorageOperationStatus> getComponentResult = null;
+ List<ComponentMetadataData> latestVersionList = null;
+ ComponentMetadataData latestVersion = null;
+
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(GraphPropertiesDictionary.UUID.getProperty(), uuid);
+ propertiesToMatch.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Either<List<ComponentMetadataData>, TitanOperationStatus> getComponentEither = titanGenericDao.getByCriteria(nodeType, propertiesToMatch, ComponentMetadataData.class);
+ if (getComponentEither.isRight()) {
+ log.debug("Couldn't fetch metadata for component with type {} and uuid {}, error: {}", nodeType, uuid, getComponentEither.right().value());
+ getComponentResult = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentEither.right().value()));
+
+ }
+ if (getComponentResult == null) {
+ latestVersionList = getComponentEither.left().value();
+ if (latestVersionList.isEmpty()) {
+ log.debug("Component with type {} and uuid {} was not found", nodeType, uuid);
+ getComponentResult = Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ }
+ if (getComponentResult == null) {
+ latestVersion = latestVersionList.size() == 1 ? latestVersionList.get(0)
+ : latestVersionList.stream().max((c1, c2) -> Double.compare(Double.parseDouble(c1.getMetadataDataDefinition().getVersion()), Double.parseDouble(c2.getMetadataDataDefinition().getVersion()))).get();
+ getComponentResult = Either.left(latestVersion);
+ }
+ return getComponentResult;
+ }
+
+ public <T extends GraphNode> Either<T, StorageOperationStatus> getComponentByLabelAndId(String uniqueId, NodeTypeEnum nodeType, Class<T> clazz) {
+
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId);
+ Either<List<T>, TitanOperationStatus> getResponse = titanGenericDao.getByCriteria(nodeType, propertiesToMatch, clazz);
+ if (getResponse.isRight()) {
+ log.debug("Couldn't fetch component with type {} and unique id {}, error: {}", nodeType, uniqueId, getResponse.right().value());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getResponse.right().value()));
+
+ }
+ List<T> serviceDataList = getResponse.left().value();
+ if (serviceDataList.isEmpty()) {
+ log.debug("Component with type {} and unique id {} was not found", nodeType, uniqueId);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ T serviceData = serviceDataList.get(0);
+ return Either.left(serviceData);
+ }
+
+ // protected <T extends GraphNode> Either<T, StorageOperationStatus>
+ // getComponentByLabelAndId_tx(String uniqueId, NodeTypeEnum nodeType,
+ // Class<T> clazz) {
+ //
+ // Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ // propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(nodeType),
+ // uniqueId);
+ // Either<List<T>, TitanOperationStatus> getResponse =
+ // titanGenericDao.getByCriteria_tx(nodeType, propertiesToMatch, clazz);
+ // if (getResponse.isRight()) {
+ // log.debug("Couldn't fetch component with type {} and unique id {}, error:
+ // {}", nodeType, uniqueId, getResponse.right().value());
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getResponse.right().value()));
+ //
+ // }
+ // List<T> serviceDataList = getResponse.left().value();
+ // if (serviceDataList.isEmpty()) {
+ // log.debug("Component with type {} and unique id {} was not found",
+ // nodeType, uniqueId);
+ // return Either.right(StorageOperationStatus.NOT_FOUND);
+ // }
+ // T serviceData = serviceDataList.get(0);
+ // return Either.left(serviceData);
+ // }
+
+ /**
+ *
+ * @param component
+ * @param uniqueId
+ * @param nodeType
+ * @return
+ */
+ protected TitanOperationStatus setComponentCreatorFromGraph(Component component, String uniqueId, NodeTypeEnum nodeType) {
+ Either<ImmutablePair<UserData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.CREATOR, NodeTypeEnum.User, UserData.class);
+ if (parentNode.isRight()) {
+ return parentNode.right().value();
+ }
+
+ ImmutablePair<UserData, GraphEdge> value = parentNode.left().value();
+ if (log.isDebugEnabled())
+ log.debug("Found parent node {}", value);
+ UserData userData = value.getKey();
+ if (log.isDebugEnabled())
+ log.debug("Build resource : set creator userId to {}", userData.getUserId());
+ String fullName = buildFullName(userData);
+ if (log.isDebugEnabled())
+ log.debug("Build resource : set last modifier full name to {} ", fullName);
+ component.setCreatorUserId(userData.getUserId());
+ component.setCreatorFullName(fullName);
+
+ return TitanOperationStatus.OK;
+ }
+
+ protected TitanOperationStatus setComponentLastModifierFromGraph(Component component, String uniqueId, NodeTypeEnum nodeType) {
+
+ Either<ImmutablePair<UserData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.LAST_MODIFIER, NodeTypeEnum.User, UserData.class);
+ if (parentNode.isRight()) {
+ return parentNode.right().value();
+ }
+
+ ImmutablePair<UserData, GraphEdge> value = parentNode.left().value();
+ if (log.isDebugEnabled())
+ log.debug("Found parent node {}", value);
+ UserData userData = value.getKey();
+
+ if (log.isDebugEnabled())
+ log.debug("Build resource : set last modifier userId to {}", userData.getUserId());
+ String fullName = buildFullName(userData);
+ if (log.isDebugEnabled())
+ log.debug("Build resource : set last modifier full name to {}", fullName);
+ component.setLastUpdaterUserId(userData.getUserId());
+ component.setLastUpdaterFullName(fullName);
+
+ return TitanOperationStatus.OK;
+ }
+
+ /**
+ *
+ * @param userData
+ * @return
+ */
+ protected String buildFullName(UserData userData) {
+
+ String fullName = userData.getFirstName();
+ if (fullName == null) {
+ fullName = "";
+ } else {
+ fullName = fullName + " ";
+ }
+ String lastName = userData.getLastName();
+ if (lastName != null) {
+ fullName += lastName;
+ }
+ return fullName;
+ }
+
+ protected Either<UserData, TitanOperationStatus> findUser(String userId) {
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ Either<UserData, TitanOperationStatus> findUser = titanGenericDao.getNode(key, userId, UserData.class);
+ return findUser;
+ }
+
+ protected Either<TitanVertex, TitanOperationStatus> findUserVertex(String userId) {
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ return titanGenericDao.getVertexByProperty(key, userId);
+ }
+
+ protected Either<GroupingData, TitanOperationStatus> findGrouping(NodeTypeEnum nodeType, String groupingId) {
+ String key = UniqueIdBuilder.getKeyByNodeType(nodeType);
+ Either<GroupingData, TitanOperationStatus> findGrouping = titanGenericDao.getNode(key, groupingId, GroupingData.class);
+ return findGrouping;
+ }
+
+ protected Either<SubCategoryData, TitanOperationStatus> findSubCategory(NodeTypeEnum nodeType, String subCategoryId) {
+ String key = UniqueIdBuilder.getKeyByNodeType(nodeType);
+ Either<SubCategoryData, TitanOperationStatus> findSubCategory = titanGenericDao.getNode(key, subCategoryId, SubCategoryData.class);
+ return findSubCategory;
+ }
+
+ protected Either<CategoryData, TitanOperationStatus> findCategory(NodeTypeEnum nodeType, String categoryId) {
+ String key = UniqueIdBuilder.getKeyByNodeType(nodeType);
+ Either<CategoryData, TitanOperationStatus> findCategory = titanGenericDao.getNode(key, categoryId, CategoryData.class);
+ return findCategory;
+ }
+
+ protected TitanOperationStatus associateMetadataToComponent(ComponentMetadataData componentData, UserData userData, UserData updater, CategoryData categoryData, List<ResourceMetadataData> derivedResources) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), componentData.getMetadataDataDefinition().getState());
+ Either<GraphRelation, TitanOperationStatus> result = titanGenericDao.createRelation(updater, componentData, GraphEdgeLabels.STATE, props);
+ log.debug("After associating user {} to component {}. Edge type is {}", updater, componentData.getUniqueId(), GraphEdgeLabels.STATE);
+ if (result.isRight()) {
+ return result.right().value();
+ }
+
+ result = titanGenericDao.createRelation(updater, componentData, GraphEdgeLabels.LAST_MODIFIER, null);
+ log.debug("After associating user {} to component {}. Edge type is {}", updater, componentData.getUniqueId(), GraphEdgeLabels.LAST_MODIFIER);
+ if (result.isRight()) {
+ log.error("Failed to associate user " + updater + " to component " + componentData.getUniqueId() + ". Edge type is " + GraphEdgeLabels.LAST_MODIFIER);
+ return result.right().value();
+ }
+
+ result = titanGenericDao.createRelation(userData, componentData, GraphEdgeLabels.CREATOR, null);
+ log.debug("After associating user {} to component {}. Edge type is {}", userData, componentData.getUniqueId(), GraphEdgeLabels.CREATOR);
+ if (result.isRight()) {
+ log.error("Failed to associate user " + userData + " to component " + componentData.getUniqueId() + ". Edge type is " + GraphEdgeLabels.CREATOR);
+ return result.right().value();
+ }
+
+ if (derivedResources != null) {
+ for (ResourceMetadataData derivedResource : derivedResources) {
+ log.debug("After associating component {} to parent component {}. Egde type is {}", componentData.getUniqueId(), derivedResource.getUniqueId(), GraphEdgeLabels.DERIVED_FROM);
+ result = titanGenericDao.createRelation(componentData, derivedResource, GraphEdgeLabels.DERIVED_FROM, null);
+ if (result.isRight()) {
+ log.error("Failed to associate user {} to component {}. Edge type is {}", userData, componentData.getUniqueId(), GraphEdgeLabels.CREATOR);
+ return result.right().value();
+ }
+ }
+ }
+
+ if (categoryData != null) {
+ result = titanGenericDao.createRelation(componentData, categoryData, GraphEdgeLabels.CATEGORY, null);
+ log.debug("After associating component {} to category {}. Edge type is {}", componentData.getUniqueId(), categoryData, GraphEdgeLabels.CATEGORY);
+ if (result.isRight()) {
+ log.error("Faield to associate component {} to category {}. Edge type is {}", componentData.getUniqueId(), categoryData, GraphEdgeLabels.CATEGORY);
+ return result.right().value();
+ }
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ protected StorageOperationStatus associateArtifactsToComponent(NodeTypeEnum nodeType, ComponentMetadataData componentData, Map<String, ArtifactDefinition> artifacts) {
+
+ if (artifacts != null) {
+ for (Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+
+ ArtifactDefinition artifactDefinition = entry.getValue();
+ Either<ArtifactDefinition, StorageOperationStatus> addArifactToResource = Either.left(artifactDefinition);
+ // if ((artifactDefinition.getUniqueId() != null) &&
+ // !artifactDefinition.getUniqueId().isEmpty()) {
+ addArifactToResource = artifactOperation.addArifactToComponent(artifactDefinition, (String) componentData.getUniqueId(), nodeType, false, true);
+ // }
+
+ if (addArifactToResource.isRight()) {
+ return addArifactToResource.right().value();
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+
+ }
+
+ protected Either<Boolean, StorageOperationStatus> validateResourceNameUniqueness(String name, Map<String, Object> hasProps, Map<String, Object> hasNotProps, TitanGenericDao titanGenericDao) {
+ if (hasProps == null) {
+ hasProps = new HashMap<String, Object>();
+ }
+ String normalizedName = ValidationUtils.normaliseComponentName(name);
+ hasProps.put(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), normalizedName);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> resources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, hasProps, hasNotProps, ResourceMetadataData.class);
+ if (resources.isRight() && resources.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("failed to get resources from graph with property name: {}", name);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(resources.right().value()));
+ }
+ List<ResourceMetadataData> resourceList = (resources.isLeft() ? resources.left().value() : null);
+ if (resourceList != null && resourceList.size() > 0) {
+ if (log.isDebugEnabled()) {
+ StringBuilder builder = new StringBuilder();
+ for (ResourceMetadataData resourceData : resourceList) {
+ builder.append(resourceData.getUniqueId() + "|");
+ }
+ log.debug("resources with property name: {} exists in graph. Found {}", name, builder.toString());
+ }
+ return Either.left(false);
+ } else {
+ log.debug("resources with property name:" + name + " does not exists in graph");
+ return Either.left(true);
+ }
+
+ }
+
+ protected Either<Boolean, StorageOperationStatus> validateToscaResourceNameUniqueness(String name, TitanGenericDao titanGenericDao) {
+ Map<String, Object> properties = new HashMap<>();
+
+ properties.put(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty(), name);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> resources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, properties, ResourceMetadataData.class);
+ if (resources.isRight() && resources.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("failed to get resources from graph with property name:" + name);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(resources.right().value()));
+ }
+ List<ResourceMetadataData> resourceList = (resources.isLeft() ? resources.left().value() : null);
+ if (resourceList != null && resourceList.size() > 0) {
+ if (log.isDebugEnabled()) {
+ StringBuilder builder = new StringBuilder();
+ for (ResourceMetadataData resourceData : resourceList) {
+ builder.append(resourceData.getUniqueId() + "|");
+ }
+ log.debug("resources with property name:" + name + " exists in graph. found " + builder.toString());
+ }
+ return Either.left(false);
+ } else {
+ log.debug("resources with property name: {} dows not exists in the graph", name);
+ return Either.left(true);
+ }
+
+ }
+
+ protected Either<Boolean, StorageOperationStatus> validateServiceNameUniqueness(String name, TitanGenericDao titanGenericDao) {
+ Map<String, Object> properties = new HashMap<>();
+ String normalizedName = ValidationUtils.normaliseComponentName(name);
+ properties.put(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), normalizedName);
+
+ Either<List<ServiceMetadataData>, TitanOperationStatus> services = titanGenericDao.getByCriteria(NodeTypeEnum.Service, properties, ServiceMetadataData.class);
+ if (services.isRight() && services.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("failed to get services from graph with property name: {}", name);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(services.right().value()));
+ }
+ List<ServiceMetadataData> serviceList = (services.isLeft() ? services.left().value() : null);
+ if (serviceList != null && serviceList.size() > 0) {
+ if (log.isDebugEnabled()) {
+ StringBuilder builder = new StringBuilder();
+ for (ServiceMetadataData serviceData : serviceList) {
+ builder.append(serviceData.getUniqueId() + "|");
+ }
+ log.debug("Service with property name: {} exists in graph. Found {}", name, builder.toString());
+ }
+
+ return Either.left(false);
+ } else {
+ log.debug("Service with property name: {} dows not exists in graph", name);
+ return Either.left(true);
+ }
+ }
+
+ protected Either<Boolean, StorageOperationStatus> validateComponentNameUniqueness(String name, TitanGenericDao titanGenericDao, NodeTypeEnum type) {
+ Map<String, Object> properties = new HashMap<>();
+ String normalizedName = ValidationUtils.normaliseComponentName(name);
+ properties.put(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), normalizedName);
+
+ Either<List<ComponentMetadataData>, TitanOperationStatus> components = titanGenericDao.getByCriteria(type, properties, ComponentMetadataData.class);
+ if (components.isRight() && components.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("failed to get components from graph with property name: {}", name);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(components.right().value()));
+ }
+ List<ComponentMetadataData> componentList = (components.isLeft() ? components.left().value() : null);
+ if (componentList != null && componentList.size() > 0) {
+ if (log.isDebugEnabled()) {
+ StringBuilder builder = new StringBuilder();
+ for (ComponentMetadataData componentData : componentList) {
+ builder.append(componentData.getUniqueId() + "|");
+ }
+ log.debug("Component with property name: {} exists in graph. Found {}", name, builder.toString());
+ }
+
+ return Either.left(false);
+ } else {
+ log.debug("Component with property name: {} does not exists in graph", name);
+ return Either.left(true);
+ }
+ }
+
+ // protected TitanOperationStatus setComponentCategoryFromGraph(String
+ // uniqueId, Component component, TitanGenericDao titanGenericDao,
+ // NodeTypeEnum categoryType) {
+ //
+ // Either<List<ImmutablePair<CategoryData, GraphEdge>>,
+ // TitanOperationStatus> parentNode =
+ // titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource),
+ // uniqueId, GraphEdgeLabels.CATEGORY, categoryType,
+ // CategoryData.class);
+ // if (parentNode.isRight()) {
+ // return parentNode.right().value();
+ // }
+ //
+ // List<ImmutablePair<CategoryData, GraphEdge>> listValue =
+ // parentNode.left().value();
+ // log.debug("Result after looking for category nodes pointed by resource "
+ // + uniqueId + ". status is " + listValue);
+ // if (listValue.size() > 1) {
+ // log.error("Multiple edges foud between resource " + uniqueId + " to
+ // category nodes.");
+ // }
+ // ImmutablePair<CategoryData, GraphEdge> value = listValue.get(0);
+ // log.debug("Found parent node {}", value);
+ //
+ // CategoryData categoryData = value.getKey();
+ // String categoryStr = null;
+ // if
+ // (NodeTypeEnum.ResourceCategory.name().equalsIgnoreCase(categoryData.getLabel()))
+ // {
+ // StringBuilder sb = new StringBuilder();
+ // sb.append(((ResourceCategoryData) categoryData).getCategoryName());
+ // sb.append("/");
+ // sb.append(categoryData.getName());
+ // categoryStr = sb.toString();
+ // } else {
+ // categoryStr = categoryData.getName();
+ // }
+ //
+ // component.setCategory(categoryStr);
+ // return TitanOperationStatus.OK;
+ // }
+
+ protected StorageOperationStatus setArtifactFromGraph(String uniqueId, Component component, NodeTypeEnum type, IArtifactOperation artifactOperation) {
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifacts = artifactOperation.getArtifacts(uniqueId, type, true);
+ if (artifacts.isRight()) {
+ result = artifacts.right().value();
+ } else {
+ // component.setArtifacts(artifacts.left().value());
+ createSpecificArtifactList(component, artifacts.left().value());
+ }
+ return result;
+ }
+
+ protected Component createSpecificArtifactList(Component component, Map<String, ArtifactDefinition> artifacts) {
+
+ if (artifacts != null) {
+ Map<String, ArtifactDefinition> deploymentArtifacts = new HashMap<>();
+ Map<String, ArtifactDefinition> serviceApiArtifacts = new HashMap<>();
+ Map<String, ArtifactDefinition> toscaArtifacts = new HashMap<>();
+
+ Set<Entry<String, ArtifactDefinition>> specificet = new HashSet<>();
+
+ for (Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ ArtifactDefinition artifact = entry.getValue();
+ ArtifactGroupTypeEnum artifactGroupType = artifact.getArtifactGroupType();
+ if (artifactGroupType == null) {
+ artifactGroupType = ArtifactGroupTypeEnum.INFORMATIONAL;
+ }
+
+ switch (artifactGroupType) {
+ case DEPLOYMENT:
+ deploymentArtifacts.put(artifact.getArtifactLabel(), artifact);
+ specificet.add(entry);
+ break;
+ case SERVICE_API:
+ serviceApiArtifacts.put(artifact.getArtifactLabel(), artifact);
+ specificet.add(entry);
+ break;
+ case TOSCA:
+ toscaArtifacts.put(artifact.getArtifactLabel(), artifact);
+ specificet.add(entry);
+ break;
+ default:
+ break;
+ }
+
+ }
+ artifacts.entrySet().removeAll(specificet);
+
+ component.setSpecificComponetTypeArtifacts(serviceApiArtifacts);
+ component.setDeploymentArtifacts(deploymentArtifacts);
+ component.setToscaArtifacts(toscaArtifacts);
+ component.setArtifacts(artifacts);
+ }
+ return component;
+ }
+
+ private <T, S extends ComponentMetadataData> Either<List<T>, StorageOperationStatus> collectComponents(TitanGraph graph, NodeTypeEnum neededType, String categoryUid, NodeTypeEnum categoryType, Class<S> clazz) {
+ List<T> components = new ArrayList<>();
+ Either<List<ImmutablePair<S, GraphEdge>>, TitanOperationStatus> parentNodes = titanGenericDao.getParentNodes(UniqueIdBuilder.getKeyByNodeType(categoryType), categoryUid, GraphEdgeLabels.CATEGORY, neededType, clazz);
+ if (parentNodes.isLeft()) {
+ for (ImmutablePair<S, GraphEdge> component : parentNodes.left().value()) {
+ ComponentMetadataDataDefinition componentData = component.getLeft().getMetadataDataDefinition();
+ Boolean isHighest = componentData.isHighestVersion();
+ Boolean isComplex = neededType == NodeTypeEnum.Resource ? ResourceTypeEnum.VF.equals(((ResourceMetadataDataDefinition) componentData).getResourceType()) : true;
+ if (isHighest && isComplex) {
+ Either<T, StorageOperationStatus> result = getLightComponent(componentData.getUniqueId(), true);
+ if (result.isRight()) {
+ return Either.right(result.right().value());
+ }
+ components.add(result.left().value());
+ }
+ }
+ }
+ return Either.left(components);
+ }
+
+ protected <T, S extends ComponentMetadataData> Either<List<T>, StorageOperationStatus> fetchByCategoryOrSubCategoryUid(String categoryUid, NodeTypeEnum categoryType, String categoryLabel, NodeTypeEnum neededType, boolean inTransaction,
+ Class<S> clazz) {
+ try {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+
+ }
+ return collectComponents(graph.left().value(), neededType, categoryUid, categoryType, clazz);
+
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ protected <T, S extends ComponentMetadataData> Either<List<T>, StorageOperationStatus> fetchByCategoryOrSubCategoryName(String categoryName, NodeTypeEnum categoryType, String categoryLabel, NodeTypeEnum neededType, boolean inTransaction,
+ Class<S> clazz) {
+ List<T> components = new ArrayList<>();
+ try {
+ Class categoryClazz = categoryType == NodeTypeEnum.ServiceNewCategory ? CategoryData.class : SubCategoryData.class;
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), categoryName);
+ Either<List<GraphNode>, TitanOperationStatus> getCategory = titanGenericDao.getByCriteria(categoryType, props, categoryClazz);
+ if (getCategory.isRight()) {
+ return Either.right(StorageOperationStatus.CATEGORY_NOT_FOUND);
+ }
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+
+ }
+ for (GraphNode category : getCategory.left().value()) {
+ Either<List<T>, StorageOperationStatus> result = collectComponents(graph.left().value(), neededType, (String) category.getUniqueId(), categoryType, clazz);
+ if (result.isRight()) {
+ return result;
+ }
+ components.addAll(result.left().value());
+ }
+
+ return Either.left(components);
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters, boolean inTransaction, NodeTypeEnum neededType) {
+ return null;
+ }
+
+ protected Either<List<Component>, StorageOperationStatus> getFollowedComponent(String userId, Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction, TitanGenericDao titanGenericDao,
+ NodeTypeEnum neededType) {
+
+ Either<List<Component>, StorageOperationStatus> result = null;
+
+ try {
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graph.right().value()));
+ return result;
+ }
+ Iterable<TitanVertex> users;
+
+ if (userId == null) {
+ // get all users by label
+ // for Tester and Admin retrieve all users
+
+ // users =
+ // graph.left().value().getVertices(GraphPropertiesDictionary.LABEL.getProperty(),
+ // NodeTypeEnum.User.getName());
+ users = graph.left().value().query().has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.User.getName()).vertices();
+
+ } else {
+ // for Designer retrieve specific user
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ // users = graph.left().value().getVertices(key, userId);
+ users = graph.left().value().query().has(key, userId).vertices();
+ }
+ Iterator<TitanVertex> userIterator = users.iterator();
+
+ List<Component> components = new ArrayList<>();
+ while (userIterator.hasNext()) {
+ Vertex vertexUser = userIterator.next();
+
+ // get all resource with current state
+ Iterator<Edge> iterator = vertexUser.edges(Direction.OUT, GraphEdgeLabels.STATE.getProperty());
+
+ List<Component> componentsPerUser = fetchComponents(lifecycleStates, iterator, neededType, inTransaction);
+
+ HashSet<String> ids = new HashSet<String>();
+
+ if (componentsPerUser != null) {
+ for (Component comp : componentsPerUser) {
+ ids.add(comp.getUniqueId());
+ components.add(comp);
+ }
+ }
+
+ if (lastStateStates != null && !lastStateStates.isEmpty()) {
+ // get all resource with last state
+ iterator = vertexUser.edges(Direction.OUT, GraphEdgeLabels.LAST_STATE.getProperty());
+ boolean isFirst;
+ componentsPerUser = fetchComponents(lastStateStates, iterator, neededType, inTransaction);
+ if (componentsPerUser != null) {
+ for (Component comp : componentsPerUser) {
+ isFirst = true;
+
+ if (ids.contains(comp.getUniqueId())) {
+ isFirst = false;
+ }
+ if (isFirst == true) {
+ components.add(comp);
+ }
+
+ }
+ }
+ }
+
+ } // whlile users
+
+ result = Either.left(components);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private List<Component> fetchComponents(Set<LifecycleStateEnum> lifecycleStates, Iterator<Edge> iterator, NodeTypeEnum neededType, boolean inTransaction) {
+ List<Component> components = new ArrayList<>();
+ while (iterator.hasNext()) {
+ Edge edge = iterator.next();
+
+ String stateStr = edge.value(GraphEdgePropertiesDictionary.STATE.getProperty());
+ LifecycleStateEnum state = LifecycleStateEnum.findState(stateStr);
+ if (state == null) {
+ log.debug("not supported STATE for element {}", stateStr);
+ continue;
+ }
+ if (lifecycleStates != null && lifecycleStates.contains(state)) {
+ Vertex vertexComponent = edge.inVertex();
+
+ Boolean isHighest = vertexComponent.value(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty());
+ if (isHighest) {
+
+ String nodeTypeStr = vertexComponent.value(GraphPropertiesDictionary.LABEL.getProperty());
+ // get only latest versions
+ NodeTypeEnum nodeType = NodeTypeEnum.getByName(nodeTypeStr);
+
+ if (nodeType == null) {
+ log.debug("missing node label for vertex {}", vertexComponent);
+ continue;
+ }
+
+ if (neededType.equals(nodeType)) {
+ switch (nodeType) {
+ case Service:
+ handleNode(components, vertexComponent, nodeType, inTransaction);
+ break;
+ case Resource:
+ Boolean isAbtract = vertexComponent.value(GraphPropertiesDictionary.IS_ABSTRACT.getProperty());
+ if (false == isAbtract) {
+ handleNode(components, vertexComponent, nodeType, inTransaction);
+ } // if not abstract
+ break;
+ case Product:
+ handleNode(components, vertexComponent, nodeType, inTransaction);
+ break;
+ default:
+ log.debug("not supported node type {}", nodeType);
+ break;
+ }// case
+ } // needed type
+ }
+ } // if
+ } // while resources
+ return components;
+ }
+
+ protected <T> void handleNode(List<T> components, Vertex vertexComponent, NodeTypeEnum nodeType, boolean inTransaction) {
+ String id;
+
+ id = vertexComponent.value(UniqueIdBuilder.getKeyByNodeType(nodeType));
+ if (id != null) {
+ Either<T, StorageOperationStatus> component = getLightComponent(id, inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get component for id = {} error: {} skip resource", id, component.right().value());
+ } else {
+ components.add(component.left().value());
+ }
+ } else {
+
+ Map<String, Object> properties = this.titanGenericDao.getProperties(vertexComponent);
+ log.debug("missing resource unique id for node with properties {}", properties);
+ }
+ }
+
+ /**
+ *
+ * @param component
+ * @param inTransaction
+ * @param titanGenericDao
+ * @param clazz
+ * @return
+ */
+ public <T> Either<T, StorageOperationStatus> updateComponent(Component component, boolean inTransaction, TitanGenericDao titanGenericDao, Class<T> clazz, NodeTypeEnum type) {
+
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ return updateComponentFilterResult(component, inTransaction, titanGenericDao, clazz, type, componentParametersView);
+
+ }
+
+ private Either<ArtifactData, StorageOperationStatus> generateAndUpdateToscaFileName(String componentType, String componentName, String componentId, NodeTypeEnum type, ArtifactDefinition artifactInfo) {
+ Map<String, Object> getConfig = (Map<String, Object>) ConfigurationManager.getConfigurationManager().getConfiguration().getToscaArtifacts().entrySet().stream().filter(p -> p.getKey().equalsIgnoreCase(artifactInfo.getArtifactLabel()))
+ .findAny().get().getValue();
+ artifactInfo.setArtifactName(componentType + "-" + componentName + getConfig.get("artifactName"));
+ return artifactOperation.updateToscaArtifactNameOnGraph(artifactInfo, artifactInfo.getUniqueId(), type, componentId);
+ }
+
+ protected StorageOperationStatus moveCategoryEdge(Component component, ComponentMetadataData componentData, CategoryDefinition newCategory, NodeTypeEnum type) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+
+ GraphRelation categoryRelation = new GraphRelation();
+ categoryRelation.setType(GraphEdgeLabels.CATEGORY.getProperty());
+ RelationEndPoint relationEndPoint = new RelationEndPoint(type, UniqueIdBuilder.getKeyByNodeType(type), component.getUniqueId());
+ categoryRelation.setFrom(relationEndPoint);
+ Either<GraphRelation, TitanOperationStatus> deleteOutgoingRelation = titanGenericDao.deleteOutgoingRelation(categoryRelation);
+ if (deleteOutgoingRelation.isRight()) {
+ log.error("Failed to delete category from component {}. Edge type is {}", componentData.getUniqueId(), GraphEdgeLabels.CATEGORY);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteOutgoingRelation.right().value());
+ return result;
+ }
+
+ log.debug("After removing edge from graph {}", deleteOutgoingRelation);
+
+ NodeTypeEnum categoryType;
+ if (NodeTypeEnum.Service.name().equalsIgnoreCase(type.name())) {
+ categoryType = NodeTypeEnum.ServiceCategory;
+ } else {
+ categoryType = NodeTypeEnum.ResourceCategory;
+ }
+ Either<CategoryData, StorageOperationStatus> categoryResult = elementOperation.getNewCategoryData(newCategory.getName(), NodeTypeEnum.ServiceNewCategory, CategoryData.class);
+ if (categoryResult.isRight()) {
+ StorageOperationStatus status = categoryResult.right().value();
+ log.error("Cannot find category " + newCategory.getName() + " in the graph. status is " + status);
+ return status;
+ }
+
+ CategoryData categoryData = categoryResult.left().value();
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(componentData, categoryData, GraphEdgeLabels.CATEGORY, null);
+ log.debug("After associating category {} to component {}. Edge type is {}", categoryData, componentData.getUniqueId(), GraphEdgeLabels.CATEGORY);
+ if (createRelation.isRight()) {
+ log.error("Failed to associate category {} to component {}. Edge type is {}", categoryData, componentData.getUniqueId(), GraphEdgeLabels.CATEGORY);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(createRelation.right().value());
+ return result;
+ }
+
+ return result;
+ }
+
+ private StorageOperationStatus moveLastModifierEdge(Component component, ComponentMetadataData componentData, UserData modifierUserData, NodeTypeEnum type) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+
+ GraphRelation lastModifierRelation = new GraphRelation();
+ lastModifierRelation.setType(GraphEdgeLabels.LAST_MODIFIER.getProperty());
+ RelationEndPoint relationEndPoint = new RelationEndPoint(type, UniqueIdBuilder.getKeyByNodeType(type), component.getUniqueId());
+ lastModifierRelation.setTo(relationEndPoint);
+ Either<GraphRelation, TitanOperationStatus> deleteIncomingRelation = titanGenericDao.deleteIncomingRelation(lastModifierRelation);
+ if (deleteIncomingRelation.isRight()) {
+ log.error("Failed to delete user from component {}. Edge type is {}", componentData.getUniqueId(), GraphEdgeLabels.LAST_MODIFIER);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteIncomingRelation.right().value());
+ return result;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(modifierUserData, componentData, GraphEdgeLabels.LAST_MODIFIER, null);
+ log.debug("After associating user {} to component {}. Edge type is {}", modifierUserData, componentData.getUniqueId(), GraphEdgeLabels.LAST_MODIFIER);
+ if (createRelation.isRight()) {
+ log.error("Failed to associate user {} to component {}. Edge type is {}", modifierUserData, componentData.getUniqueId(), GraphEdgeLabels.LAST_MODIFIER);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(createRelation.right().value());
+ return result;
+ }
+ return result;
+ }
+
+ protected abstract ComponentMetadataData getMetaDataFromComponent(Component component);
+
+ public abstract <T> Either<T, StorageOperationStatus> getComponent(String id, boolean inTransaction);
+
+ public abstract <T> Either<T, StorageOperationStatus> getComponent(String id, ComponentParametersView componentParametersView, boolean inTrasnaction);
+
+ // public abstract <T> Either<T, StorageOperationStatus>
+ // getComponent_tx(String id, boolean inTransaction);
+
+ protected abstract <T> Either<T, StorageOperationStatus> getComponentByNameAndVersion(String name, String version, Map<String, Object> additionalParams, boolean inTransaction);
+
+ public abstract <T> Either<T, StorageOperationStatus> getLightComponent(String id, boolean inTransaction);
+
+ public abstract <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters, boolean inTransaction);
+
+ abstract Component convertComponentMetadataDataToComponent(ComponentMetadataData componentMetadataData);
+
+ abstract TitanOperationStatus setComponentCategoriesFromGraph(Component component);
+
+ protected abstract Either<Component, StorageOperationStatus> getMetadataComponent(String id, boolean inTransaction);
+
+ protected abstract <T> Either<T, StorageOperationStatus> updateComponent(T component, boolean inTransaction);
+
+ protected abstract <T> Either<T, StorageOperationStatus> updateComponentFilterResult(T component, boolean inTransaction, ComponentParametersView filterParametersView);
+
+ public abstract Either<Component, StorageOperationStatus> deleteComponent(String id, boolean inTransaction);
+
+ public <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version, boolean inTransaction) {
+ return cloneComponent(other, version, null, inTransaction);
+ }
+
+ public abstract <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version, LifecycleStateEnum targetLifecycle, boolean inTransaction);
+
+ public abstract Component getDefaultComponent();
+
+ public abstract boolean isComponentExist(String componentId);
+
+ public abstract Either<Boolean, StorageOperationStatus> validateComponentNameExists(String componentName);
+
+ public abstract Either<Boolean, StorageOperationStatus> isComponentInUse(String componentId);
+
+ protected Either<Boolean, StorageOperationStatus> isComponentInUse(String componentId, NodeTypeEnum nodeType) {
+
+ Either<GraphRelation, TitanOperationStatus> relationByCriteria = titanGenericDao.getIncomingRelationByCriteria(new UniqueIdData(nodeType, componentId), GraphEdgeLabels.INSTANCE_OF, null);
+ if (relationByCriteria.isRight() && !relationByCriteria.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("failed to check relations for component node. id = {}, type = {}, error = {}", componentId, nodeType, relationByCriteria.right().value().name());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(relationByCriteria.right().value()));
+ }
+
+ if (relationByCriteria.isLeft()) {
+ // component is in use
+ return Either.left(true);
+ } else {
+ return Either.left(false);
+ }
+
+ }
+
+ public abstract Either<List<String>, StorageOperationStatus> getAllComponentsMarkedForDeletion();
+
+ protected Either<List<String>, StorageOperationStatus> getAllComponentsMarkedForDeletion(NodeTypeEnum nodeType) {
+
+ List<String> componentIdsToDelete = new ArrayList<String>();
+ // get all components marked for delete
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.IS_DELETED.getProperty(), true);
+
+ Either<List<ComponentMetadataData>, TitanOperationStatus> componentsToDelete = titanGenericDao.getByCriteria(nodeType, props, ComponentMetadataData.class);
+
+ if (componentsToDelete.isRight()) {
+ TitanOperationStatus error = componentsToDelete.right().value();
+ if (error.equals(TitanOperationStatus.NOT_FOUND)) {
+ log.trace("no components to delete");
+ return Either.left(componentIdsToDelete);
+ } else {
+ log.info("failed to find components to delete. error : {}", error.name());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(error));
+ }
+
+ }
+ for (ComponentMetadataData resourceData : componentsToDelete.left().value()) {
+ componentIdsToDelete.add(resourceData.getMetadataDataDefinition().getUniqueId());
+ }
+ return Either.left(componentIdsToDelete);
+ }
+
+ protected <T extends GraphNode> Either<List<T>, TitanOperationStatus> __getLastVersion(NodeTypeEnum type, Map<String, Object> props, Class<T> clazz) {
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ return Either.right(graph.right().value());
+ }
+
+ TitanGraph tGraph = graph.left().value();
+ TitanGraphQuery<? extends TitanGraphQuery> query = tGraph.query();
+ query = query.has(GraphPropertiesDictionary.LABEL.getProperty(), type.getName());
+
+ if (props != null && !props.isEmpty()) {
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ query = query.hasNot(entry.getKey(), entry.getValue());
+ }
+ }
+ query.has(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Iterable<TitanVertex> vertices = query.vertices();
+
+ if (vertices == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ List<T> result = new ArrayList<T>();
+
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ Map<String, Object> newProp = titanGenericDao.getProperties(vertex);
+ T element = GraphElementFactory.createElement(type.getName(), GraphElementTypeEnum.Node, newProp, clazz);
+ result.add(element);
+ }
+ if (result.size() == 0) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ log.debug("No nodes in graph for criteria : from type = {} and properties = {}", type, props);
+ return Either.left(result);
+ } catch (Exception e) {
+ log.debug("Failed get by criteria for type = {} and properties = {}. {}", type, props, e);
+ return Either.right(TitanGraphClient.handleTitanException(e));
+ }
+ }
+
+ protected <T extends GraphNode> Either<List<T>, TitanOperationStatus> getLastVersion(NodeTypeEnum type, Map<String, Object> hasNotProps, Class<T> clazz) {
+ return getLastVersion(type, null, hasNotProps, clazz);
+ }
+
+ protected <T extends GraphNode> Either<List<T>, TitanOperationStatus> getLastVersion(NodeTypeEnum type, Map<String, Object> hasProps, Map<String, Object> hasNotProps, Class<T> clazz) {
+
+ Map<String, Object> props = new HashMap<>();
+
+ if (hasProps != null) {
+ props.putAll(hasProps);
+ }
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Either<List<T>, TitanOperationStatus> byCriteria = titanGenericDao.getByCriteria(type, props, hasNotProps, clazz);
+
+ return byCriteria;
+
+ }
+
+ public <T, S extends GraphNode> Either<Set<T>, StorageOperationStatus> getComponentCatalogData(NodeTypeEnum type, Map<String, Object> propertiesToMatch, Class<T> clazz1, Class<S> clazz2, boolean inTransaction) {
+ log.debug("Start getComponentCatalogData for type: {}", type.name());
+ Set<T> result = new HashSet<T>();
+ Either<List<S>, TitanOperationStatus> lastVersionNodes = getLastVersion(type, propertiesToMatch, clazz2);
+ Either<Set<T>, StorageOperationStatus> last = retrieveComponentsFromNodes(lastVersionNodes, inTransaction);
+ if (last.isLeft() && last.left().value() != null) {
+ result.addAll(last.left().value());
+ }
+ if (type == NodeTypeEnum.Resource) {
+ propertiesToMatch.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), false);
+ }
+ Either<List<S>, TitanOperationStatus> componentsNodes = titanGenericDao.getByCriteria(type, propertiesToMatch, clazz2);
+ Either<Set<T>, StorageOperationStatus> certified = retrieveComponentsFromNodes(componentsNodes, inTransaction);
+ if (certified.isLeft() && certified.left().value() != null) {
+ result.addAll(certified.left().value());
+ }
+ return Either.left(result);
+
+ }
+
+ protected <T, S extends GraphNode> Either<Set<T>, StorageOperationStatus> retrieveComponentsFromNodes(Either<List<S>, TitanOperationStatus> componentsNodes, boolean inTransaction) {
+ Set<T> result = new HashSet<T>();
+ if (componentsNodes.isRight()) {
+ // in case of NOT_FOUND from Titan client return to UI empty list
+ if (componentsNodes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ log.debug("No components were found");
+ } else {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(componentsNodes.right().value()));
+ }
+ } else {
+ List<S> componentDataList = componentsNodes.left().value();
+ for (S componentData : componentDataList) {
+ // Either<T, StorageOperationStatus> component =
+ // getComponent((String) componentData.getUniqueId(),
+ // inTransaction);
+ Either<T, StorageOperationStatus> component = getLightComponent((String) componentData.getUniqueId(), inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get component for id = {} error : {} skip resource", componentData.getUniqueId(), component.right().value());
+ // return Either.right(service.right().value());
+ } else {
+ result.add(component.left().value());
+ }
+ }
+ }
+ return Either.left(result);
+ }
+
+ protected StorageOperationStatus removeArtifactsFromComponent(Component component, NodeTypeEnum componentType) {
+
+ String componentId = component.getUniqueId();
+ // Map<String, ArtifactDefinition> artifacts = component.getArtifacts();
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifactsRes = artifactOperation.getArtifacts(componentId, componentType, true);
+ if (artifactsRes.isRight() && !artifactsRes.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ return artifactsRes.right().value();
+ }
+ if (artifactsRes.isLeft() && artifactsRes.left().value() != null) {
+ Map<String, ArtifactDefinition> artifacts = artifactsRes.left().value();
+ for (Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+
+ ArtifactDefinition artifactDefinition = entry.getValue();
+ Either<ArtifactDefinition, StorageOperationStatus> removeArifactFromResource = artifactOperation.removeArifactFromResource(componentId, artifactDefinition.getUniqueId(), componentType, true, true);
+ if (removeArifactFromResource.isRight()) {
+ return removeArifactFromResource.right().value();
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ public Either<List<Component>, StorageOperationStatus> getTesterFollowedComponent(String userId, Set<LifecycleStateEnum> lifecycleStates, boolean inTransaction, NodeTypeEnum neededType) {
+ List<Component> resList = new ArrayList<>();
+ Either<List<Component>, StorageOperationStatus> rip = getFollowedComponent(userId, lifecycleStates, null, inTransaction, titanGenericDao, neededType);
+ if (rip.isLeft()) {
+ List<Component> ripRes = rip.left().value();
+ if (ripRes != null && !ripRes.isEmpty()) {
+ resList.addAll(ripRes);
+ }
+ Set<LifecycleStateEnum> rfcState = new HashSet<>();
+ rfcState.add(LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<List<Component>, StorageOperationStatus> rfc = getFollowedComponent(null, rfcState, null, inTransaction, titanGenericDao, neededType);
+ if (rfc.isLeft()) {
+ List<Component> rfcRes = rfc.left().value();
+ if (rfcRes != null && !rfcRes.isEmpty()) {
+ resList.addAll(rfcRes);
+ }
+ } else {
+ return Either.right(rfc.right().value());
+ }
+
+ } else {
+ return Either.right(rip.right().value());
+ }
+ return Either.left(resList);
+
+ }
+
+ /**
+ * generate UUID only for case that version is "XX.01" - (start new version)
+ *
+ * @param component
+ */
+ protected void generateUUID(Component component) {
+ String version = component.getVersion();
+ if (uuidNewVersion.matcher(version).matches()) {
+ UUID uuid = UUID.randomUUID();
+ component.getComponentMetadataDefinition().getMetadataDataDefinition().setUUID(uuid.toString());
+ MDC.put("serviceInstanceID", uuid.toString());
+ }
+ }
+
+ protected <T extends GraphNode> Either<Map<String, String>, TitanOperationStatus> getVersionList(NodeTypeEnum type, String version, Component component, Class<T> clazz) {
+ return getVersionList(type, version, component.getUUID(), component.getSystemName(), clazz);
+ }
+
+ protected <T extends GraphNode> Either<Map<String, String>, TitanOperationStatus> getVersionList(NodeTypeEnum type, String version, String uuid, String systemName, Class<T> clazz) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ Map<String, Object> hasNotProps = new HashMap<String, Object>();
+
+ if (version.startsWith("0")) {
+ props.put(GraphPropertiesDictionary.UUID.getProperty(), uuid);
+ } else {
+ props.put(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), systemName);
+ }
+ hasNotProps.put(GraphPropertiesDictionary.IS_DELETED.getProperty(), true);
+ Either<List<T>, TitanOperationStatus> result = titanGenericDao.getByCriteria(type, props, hasNotProps, clazz);
+
+ Map<String, String> versionMap = new HashMap<String, String>();
+ if (result.isRight()) {
+ if (!result.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.right(result.right().value());
+ }
+
+ } else {
+ switch (type) {
+ case Resource:
+ List<ResourceMetadataData> components = (List<ResourceMetadataData>) result.left().value();
+ for (ResourceMetadataData data : components) {
+ versionMap.put(data.getMetadataDataDefinition().getVersion(), (String) data.getUniqueId());
+ }
+ break;
+ case Service:
+ List<ServiceMetadataData> componentsS = (List<ServiceMetadataData>) result.left().value();
+ for (ServiceMetadataData data : componentsS) {
+ versionMap.put(data.getMetadataDataDefinition().getVersion(), (String) data.getUniqueId());
+ }
+ break;
+ case Product:
+ List<ProductMetadataData> componentsP = (List<ProductMetadataData>) result.left().value();
+ for (ProductMetadataData data : componentsP) {
+ versionMap.put(data.getMetadataDataDefinition().getVersion(), (String) data.getUniqueId());
+ }
+ default:
+ break;
+ }
+ }
+
+ return Either.left(versionMap);
+ }
+
+ protected StorageOperationStatus deleteAdditionalInformation(NodeTypeEnum nodeType, String componentId) {
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> deleteRes = additionalInformationOperation.deleteAllAdditionalInformationParameters(nodeType, componentId, true);
+
+ if (deleteRes.isRight()) {
+ StorageOperationStatus status = deleteRes.right().value();
+ return status;
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ protected StorageOperationStatus addAdditionalInformation(NodeTypeEnum nodeType, String componentId, AdditionalInformationDefinition informationDefinition) {
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> status = additionalInformationOperation.addAdditionalInformationNode(nodeType, componentId, informationDefinition);
+
+ if (status.isRight()) {
+ TitanOperationStatus titanStatus = status.right().value();
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ }
+
+ log.trace("After adding additional information to component {}. Result is {}", componentId, status.left().value());
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ protected StorageOperationStatus addAdditionalInformation(NodeTypeEnum nodeType, String componentId, AdditionalInformationDefinition informationDefinition, TitanVertex metadataVertex) {
+
+ TitanOperationStatus status = additionalInformationOperation.addAdditionalInformationNode(nodeType, componentId, informationDefinition, metadataVertex);
+ log.trace("After adding additional information to component {}. Result is {}", componentId, status);
+
+ if (!status.equals(TitanOperationStatus.OK)) {
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ public Either<List<ArtifactDefinition>, StorageOperationStatus> getComponentArtifactsForDelete(String parentId, NodeTypeEnum parentType, boolean inTransacton) {
+ List<ArtifactDefinition> artifacts = new ArrayList<ArtifactDefinition>();
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifactsResponse = artifactOperation.getArtifacts(parentId, parentType, inTransacton);
+ if (artifactsResponse.isRight()) {
+ if (!artifactsResponse.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ log.debug("failed to retrieve artifacts for {} {}", parentType, parentId);
+ return Either.right(artifactsResponse.right().value());
+ }
+ } else {
+ artifacts.addAll(artifactsResponse.left().value().values());
+ }
+
+ if (NodeTypeEnum.Resource.equals(parentType)) {
+ Either<List<ArtifactDefinition>, StorageOperationStatus> interfacesArtifactsForResource = getAdditionalArtifacts(parentId, false, true);
+ if (artifactsResponse.isRight() && !interfacesArtifactsForResource.right().value().equals(StorageOperationStatus.NOT_FOUND)) {
+ log.debug("failed to retrieve interface artifacts for {} {}", parentType, parentId);
+ return Either.right(interfacesArtifactsForResource.right().value());
+ } else if (artifactsResponse.isLeft()) {
+ artifacts.addAll(interfacesArtifactsForResource.left().value());
+ }
+ }
+ return Either.left(artifacts);
+ }
+
+ protected void addComponentInternalFields(ComponentMetadataData componentMetadataData) {
+ org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition metadataDataDefinition = componentMetadataData.getMetadataDataDefinition();
+ Long creationDate = metadataDataDefinition.getCreationDate();
+
+ long currentDate = System.currentTimeMillis();
+ if (creationDate == null) {
+ metadataDataDefinition.setCreationDate(currentDate);
+ }
+ metadataDataDefinition.setLastUpdateDate(currentDate);
+
+ String lifecycleStateEnum = metadataDataDefinition.getState();
+ if (lifecycleStateEnum == null) {
+ metadataDataDefinition.setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ }
+ String componentUniqueId = UniqueIdBuilder.buildComponentUniqueId();
+ metadataDataDefinition.setUniqueId(componentUniqueId);
+ metadataDataDefinition.setHighestVersion(true);
+ }
+
+ protected StorageOperationStatus createTagsForComponent(Component component) {
+ List<String> tags = component.getTags();
+ if (tags != null && false == tags.isEmpty()) {
+ Either<List<TagData>, StorageOperationStatus> tagsResult = createNewTagsList(tags);
+
+ if (tagsResult == null) {
+ log.debug("tagsResult is null");
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ if (tagsResult.isRight()) {
+ return tagsResult.right().value();
+ }
+ List<TagData> tagsToCreate = tagsResult.left().value();
+ return createTagNodesOnGraph(tagsToCreate);
+ }
+ log.trace("All tags created succesfully for component {}", component.getUniqueId());
+ return StorageOperationStatus.OK;
+ }
+
+ protected Either<List<GroupingData>, StorageOperationStatus> findGroupingsForComponent(NodeTypeEnum nodeTypeEnum, Component component) {
+ List<CategoryDefinition> categories = component.getCategories();
+ List<GroupingData> groupingDataToAssociate = new ArrayList<>();
+ if (categories != null) {
+ groupingDataToAssociate = new ArrayList<>();
+ for (CategoryDefinition categoryDefinition : categories) {
+ List<SubCategoryDefinition> subcategories = categoryDefinition.getSubcategories();
+ if (subcategories != null) {
+ for (SubCategoryDefinition subCategoryDefinition : subcategories) {
+ List<GroupingDefinition> groupingDataDefinitions = subCategoryDefinition.getGroupings();
+ if (groupingDataDefinitions != null) {
+ for (GroupingDataDefinition grouping : groupingDataDefinitions) {
+ String groupingId = grouping.getUniqueId();
+ Either<GroupingData, TitanOperationStatus> findGroupingEither = findGrouping(nodeTypeEnum, groupingId);
+ if (findGroupingEither.isRight()) {
+ TitanOperationStatus status = findGroupingEither.right().value();
+ log.error("Cannot find grouping {} in the graph. status is {}", groupingId, status);
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(StorageOperationStatus.CATEGORY_NOT_FOUND);
+ }
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ groupingDataToAssociate.add(findGroupingEither.left().value());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return Either.left(groupingDataToAssociate);
+ }
+
+ protected TitanOperationStatus associateGroupingsToComponent(ComponentMetadataData componentMetadataData, List<GroupingData> groupingDataToAssociate) {
+ for (GroupingData groupingData : groupingDataToAssociate) {
+ GraphEdgeLabels groupingLabel = GraphEdgeLabels.GROUPING;
+ Either<GraphRelation, TitanOperationStatus> result = titanGenericDao.createRelation(componentMetadataData, groupingData, groupingLabel, null);
+ log.debug("After associating grouping {} to component {}. Edge type is {}", groupingData, componentMetadataData, groupingLabel);
+ if (result.isRight()) {
+ return result.right().value();
+ }
+ }
+ log.trace("All groupings associated succesfully to component {}", componentMetadataData);
+ return TitanOperationStatus.OK;
+ }
+
+ public abstract Either<Integer, StorageOperationStatus> increaseAndGetComponentInstanceCounter(String componentId, boolean inTransaction);
+
+ protected Either<Integer, StorageOperationStatus> increaseAndGetComponentInstanceCounter(String componentId, NodeTypeEnum nodeType, boolean inTransaction) {
+ Either<Integer, StorageOperationStatus> result = null;
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+ Either<TitanVertex, TitanOperationStatus> vertexService = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId);
+ if (vertexService.isRight()) {
+ log.debug("failed to fetch vertex of component metadata, nodeType:{} , id: {}", nodeType, componentId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(vertexService.right().value()));
+ return result;
+ }
+ Vertex vertex = vertexService.left().value();
+ Integer instanceCounter = vertex.value(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty());
+ ++instanceCounter;
+ vertex.property(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty(), instanceCounter);
+ result = Either.left(instanceCounter);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("increaseAndGetComponentInstanceCounter operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("increaseAndGetComponentInstanceCounter operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ protected Either<Integer, StorageOperationStatus> setComponentInstanceCounter(String componentId, NodeTypeEnum nodeType, int counter, boolean inTransaction) {
+ Either<Integer, StorageOperationStatus> result = null;
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+ Either<TitanVertex, TitanOperationStatus> vertexService = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId);
+ if (vertexService.isRight()) {
+ log.debug("failed to fetch vertex of component metadata ofor id = {}", componentId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(vertexService.right().value()));
+ return result;
+ }
+ Vertex vertex = vertexService.left().value();
+ vertex.property(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty(), counter);
+ result = Either.left(counter);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("deleteService operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("deleteService operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ protected TitanOperationStatus setComponentInstancesFromGraph(String uniqueId, Component component, NodeTypeEnum containerNodeType, NodeTypeEnum compInstNodeType) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> resourceInstancesOfService = componentInstanceOperation.getComponentInstancesOfComponent(uniqueId, containerNodeType, compInstNodeType);
+
+ if (resourceInstancesOfService.isRight()) {
+ TitanOperationStatus status = resourceInstancesOfService.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ } else {
+ log.error("Failed to fetch resource instances and their relations. status is {}", status);
+ }
+ return status;
+ }
+
+ ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>> immutablePair = resourceInstancesOfService.left().value();
+ List<ComponentInstance> instances = immutablePair.getKey();
+ List<RequirementCapabilityRelDef> relations = immutablePair.getValue();
+
+ component.setComponentInstances(instances);
+ component.setComponentInstancesRelations(relations);
+
+ return TitanOperationStatus.OK;
+ }
+
+ /**
+ * set all properties of all of its resources
+ *
+ * @param uniqueId
+ * @return
+ */
+ protected TitanOperationStatus ___setComponentInstancesPropertiesFromGraph(String uniqueId, Component component) {
+
+ List<ComponentInstance> resourceInstances = component.getComponentInstances();
+
+ Map<String, List<ComponentInstanceProperty>> resourceInstancesProperties = new HashMap<>();
+
+ Map<String, List<PropertyDefinition>> alreadyProcessedResources = new HashMap<>();
+
+ if (resourceInstances != null) {
+ for (ComponentInstance resourceInstance : resourceInstances) {
+
+ log.debug("Going to update properties of resource instance {}", resourceInstance.getUniqueId());
+ String resourceUid = resourceInstance.getComponentUid();
+
+ List<PropertyDefinition> properties = alreadyProcessedResources.get(resourceUid);
+ if (properties == null) {
+ properties = new ArrayList<>();
+ TitanOperationStatus findAllRes = propertyOperation.findAllResourcePropertiesRecursively(resourceUid, properties);
+ if (findAllRes != TitanOperationStatus.OK) {
+ return findAllRes;
+ }
+ alreadyProcessedResources.put(resourceUid, properties);
+ }
+ log.debug("After getting properties of resource {}. Number of properties is {}", resourceUid, (properties == null ? 0 : properties.size()));
+ if (false == properties.isEmpty()) {
+
+ String resourceInstanceUid = resourceInstance.getUniqueId();
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> propertyValuesRes = propertyOperation.getAllPropertiesOfResourceInstanceOnlyPropertyDefId(resourceInstanceUid);
+ log.debug("After fetching property under resource instance {}", resourceInstanceUid);
+ if (propertyValuesRes.isRight()) {
+ TitanOperationStatus status = propertyValuesRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return status;
+ }
+ }
+
+ Map<String, ComponentInstanceProperty> propertyIdToValue = new HashMap<>();
+ populateMapperWithPropertyValues(propertyValuesRes, propertyIdToValue);
+
+ List<ComponentInstanceProperty> resourceInstancePropertyList = new ArrayList<>();
+ for (PropertyDefinition propertyDefinition : properties) {
+
+ String defaultValue = propertyDefinition.getDefaultValue();
+ String value = defaultValue;
+ String valueUid = null;
+
+ String propertyId = propertyDefinition.getUniqueId();
+ ComponentInstanceProperty valuedProperty = propertyIdToValue.get(propertyId);
+ if (valuedProperty != null) {
+ String newValue = valuedProperty.getValue();
+ // if (newValue != null) {
+ value = newValue;
+ // }
+
+ valueUid = valuedProperty.getValueUniqueUid();
+ log.trace("Found value {} under resource instance whice override the default value {}", value, defaultValue);
+ }
+ ComponentInstanceProperty resourceInstanceProperty = new ComponentInstanceProperty(propertyDefinition, value, valueUid);
+
+ // TODO: currently ignore constraints since they are not
+ // inuse and cause to error in convertion to object.
+ resourceInstanceProperty.setConstraints(null);
+
+ resourceInstancePropertyList.add(resourceInstanceProperty);
+
+ }
+
+ resourceInstancesProperties.put(resourceInstanceUid, resourceInstancePropertyList);
+ }
+
+ }
+
+ component.setComponentInstancesProperties(resourceInstancesProperties);
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ private void populateMapperWithPropertyValues(Either<List<ComponentInstanceProperty>, TitanOperationStatus> propertyValuesRes, Map<String, ComponentInstanceProperty> propertyIdToValue) {
+
+ if (propertyValuesRes.isLeft()) {
+ List<ComponentInstanceProperty> resourceInstanceValues = propertyValuesRes.left().value();
+ if (resourceInstanceValues != null) {
+ for (ComponentInstanceProperty resourceInstanceProperty : resourceInstanceValues) {
+ propertyIdToValue.put(resourceInstanceProperty.getUniqueId(), resourceInstanceProperty);
+ }
+ }
+ }
+ }
+
+ public abstract Either<List<ArtifactDefinition>, StorageOperationStatus> getAdditionalArtifacts(String resourceId, boolean recursively, boolean inTransaction);
+
+ protected abstract StorageOperationStatus validateCategories(Component currentComponent, Component component, ComponentMetadataData componentData, NodeTypeEnum type);
+
+ protected abstract <T extends Component> StorageOperationStatus updateDerived(Component component, Component currentComponent, ComponentMetadataData updatedResourceData, Class<T> clazz);
+
+ public abstract Either<Component, StorageOperationStatus> markComponentToDelete(Component componentToDelete, boolean inTransaction);
+
+ protected Either<Component, StorageOperationStatus> internalMarkComponentToDelete(Component componentToDelete, boolean inTransaction) {
+ Either<Component, StorageOperationStatus> result = null;
+
+ if ((componentToDelete.getIsDeleted() != null) && componentToDelete.getIsDeleted() && !componentToDelete.isHighestVersion()) {
+ // component already marked for delete
+ result = Either.left(componentToDelete);
+ return result;
+ } else {
+
+ ComponentMetadataData componentMetaData = getMetaDataFromComponent(componentToDelete);
+
+ componentMetaData.getMetadataDataDefinition().setIsDeleted(true);
+ componentMetaData.getMetadataDataDefinition().setHighestVersion(false);
+ componentMetaData.getMetadataDataDefinition().setLastUpdateDate(System.currentTimeMillis());
+ try {
+ Either<ComponentMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(componentMetaData, ComponentMetadataData.class);
+
+ StorageOperationStatus updateComponent;
+ if (updateNode.isRight()) {
+ log.debug("Failed to update component {}. Status is {}", componentMetaData.getUniqueId(), updateNode.right().value());
+ updateComponent = DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value());
+ result = Either.right(updateComponent);
+ return result;
+ }
+
+ result = Either.left(componentToDelete);
+ return result;
+ } finally {
+
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("updateResource operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("updateResource operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+
+ }
+ }
+ }
+
+ private Either<List<RequirementDefinition>, TitanOperationStatus> convertReqDataListToReqDefList(ComponentInstance componentInstance, List<ImmutablePair<RequirementData, GraphEdge>> requirementData) {
+ ConvertDataToDef<RequirementDefinition, RequirementData> convertor = (data, edge) -> convertReqDataToReqDef(data, edge);
+ AddOwnerData<RequirementDefinition> dataAdder = (reqDef, compInstance) -> addOwnerDataReq(reqDef, compInstance);
+
+ return convertDataToDefinition(componentInstance, requirementData, convertor, dataAdder);
+ }
+
+ private Either<List<CapabilityDefinition>, TitanOperationStatus> convertCapDataListToCapDefList(ComponentInstance componentInstance, List<ImmutablePair<CapabilityData, GraphEdge>> capabilityData) {
+ ConvertDataToDef<CapabilityDefinition, CapabilityData> convertor = (data, edge) -> convertCapDataToCapDef(data, edge);
+ AddOwnerData<CapabilityDefinition> dataAdder = (capDef, compInstance) -> addOwnerDataCap(capDef, compInstance);
+ Either<List<CapabilityDefinition>, TitanOperationStatus> convertationResult = convertDataToDefinition(componentInstance, capabilityData, convertor, dataAdder);
+ if (convertationResult.isLeft()) {
+ convertationResult = componentInstanceOperation.updateCapDefPropertyValues(componentInstance, convertationResult.left().value());
+ }
+ return convertationResult;
+ }
+
+ private Either<CapabilityDefinition, TitanOperationStatus> convertCapDataToCapDef(CapabilityData data, GraphEdge edge) {
+ Either<CapabilityDefinition, TitanOperationStatus> eitherDef = capabilityOperation.getCapabilityByCapabilityData(data);
+
+ if (eitherDef.isLeft()) {
+ CapabilityDefinition capDef = eitherDef.left().value();
+ Map<String, Object> properties = edge.getProperties();
+ if (properties != null) {
+ String name = (String) properties.get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ String source = (String) properties.get(GraphEdgePropertiesDictionary.SOURCE.getProperty());
+ List<String> sourcesList = new ArrayList<String>();
+ capabilityOperation.getCapabilitySourcesList(source, sourcesList);
+ capDef.setName(name);
+ capDef.setCapabilitySources(sourcesList);
+
+ String requiredOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ if (requiredOccurrences != null) {
+ capDef.setMinOccurrences(requiredOccurrences);
+ }
+ String leftOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ if (leftOccurrences != null) {
+ capDef.setMaxOccurrences(leftOccurrences);
+ }
+
+ }
+ eitherDef = Either.left(capDef);
+ }
+ return eitherDef;
+ }
+
+ private Either<RequirementDefinition, TitanOperationStatus> convertReqDataToReqDef(RequirementData data, GraphEdge edge) {
+ Either<RequirementDefinition, TitanOperationStatus> eitherDef = requirementOperation.getRequirement(data.getUniqueId());
+
+ if (eitherDef.isLeft()) {
+ RequirementDefinition requirementDef = eitherDef.left().value();
+ Map<String, Object> properties = edge.getProperties();
+ if (properties != null) {
+ String name = (String) properties.get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ requirementDef.setName(name);
+ String requiredOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.REQUIRED_OCCURRENCES.getProperty());
+ if (requiredOccurrences != null) {
+ requirementDef.setMinOccurrences(requiredOccurrences);
+ }
+ String leftOccurrences = (String) properties.get(GraphEdgePropertiesDictionary.LEFT_OCCURRENCES.getProperty());
+ if (leftOccurrences != null) {
+ requirementDef.setMaxOccurrences(leftOccurrences);
+ }
+ }
+ eitherDef = Either.left(requirementDef);
+ }
+ return eitherDef;
+ }
+
+ private <Def, Data> Either<List<Def>, TitanOperationStatus> convertDataToDefinition(ComponentInstance componentInstance, List<ImmutablePair<Data, GraphEdge>> requirementData, ConvertDataToDef<Def, Data> convertor, AddOwnerData<Def> dataAdder) {
+ Either<List<Def>, TitanOperationStatus> eitherResult;
+ // Convert Data To Definition
+ Stream<Either<Def, TitanOperationStatus>> reqDefStream = requirementData.stream().map(e -> convertor.convert(e.left, e.right));
+
+ // Collect But Stop After First Error
+ List<Either<Def, TitanOperationStatus>> filteredReqDefList = StreamUtils.takeWhilePlusOne(reqDefStream, p -> p.isLeft()).collect(Collectors.toList());
+ Optional<Either<Def, TitanOperationStatus>> optionalError = filteredReqDefList.stream().filter(p -> p.isRight()).findAny();
+ if (optionalError.isPresent()) {
+ eitherResult = Either.right(optionalError.get().right().value());
+ } else {
+ // Convert From Either To Definition And Collect
+ List<Def> reqDefList = filteredReqDefList.stream().map(e -> e.left().value()).collect(Collectors.toList());
+ // Add Owner Data
+ reqDefList.forEach(e -> dataAdder.addData(e, componentInstance));
+ eitherResult = Either.left(reqDefList);
+ }
+
+ return eitherResult;
+ }
+
+ interface ConvertDataToDef<Def, Data> {
+ Either<Def, TitanOperationStatus> convert(Data d, GraphEdge edge);
+ }
+
+ interface AddOwnerData<Def> {
+ void addData(Def def, ComponentInstance compInstance);
+ }
+
+ private void addOwnerDataCap(CapabilityDefinition capDef, ComponentInstance componentInstance) {
+ capDef.setOwnerId(componentInstance.getUniqueId());
+ capDef.setOwnerName(componentInstance.getName());
+ }
+
+ private void addOwnerDataReq(RequirementDefinition reqDef, ComponentInstance componentInstance) {
+ reqDef.setOwnerId(componentInstance.getUniqueId());
+ reqDef.setOwnerName(componentInstance.getName());
+ }
+
+ public Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> getRequirements(Component component, NodeTypeEnum nodeTypeEnum, boolean inTransaction) {
+ final HashMap<String, List<RequirementDefinition>> emptyMap = new HashMap<>();
+ Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> eitherResult = Either.left(emptyMap);
+ try {
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null) {
+ Function<ComponentInstance, Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus>> dataCollector = e -> componentInstanceOperation.getRequirements(e, nodeTypeEnum);
+ Either<List<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus>>>, TitanOperationStatus> eitherDataCollected = collectDataFromComponentsInstances(componentInstances,
+ dataCollector);
+ if (eitherDataCollected.isRight()) {
+ eitherResult = Either.right(eitherDataCollected.right().value());
+ } else {
+ // Converts Data to Def stop if encountered conversion error
+ DataDefConvertor<RequirementDefinition, RequirementData> someConvertor = (e1, e2) -> convertReqDataListToReqDefList(e1, e2);
+ Either<List<List<RequirementDefinition>>, TitanOperationStatus> fullDefList = convertDataToDefComponentLevel(eitherDataCollected.left().value(), someConvertor);
+ if (fullDefList.isRight()) {
+ eitherResult = Either.right(fullDefList.right().value());
+ } else {
+ Stream<RequirementDefinition> defStream = fullDefList.left().value().stream().flatMap(e -> e.stream());
+ // Collect to Map and using grouping by
+ Map<String, List<RequirementDefinition>> capTypeCapListMap = defStream.collect(Collectors.groupingBy(e -> e.getCapability()));
+ eitherResult = Either.left(capTypeCapListMap);
+ }
+
+ }
+
+ }
+ } finally {
+ if (inTransaction == false) {
+ titanGenericDao.commit();
+ }
+ }
+
+ return eitherResult;
+ }
+
+ public Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> getCapabilities(Component component, NodeTypeEnum nodeTypeEnum, boolean inTransaction) {
+ final HashMap<String, List<CapabilityDefinition>> emptyMap = new HashMap<>();
+ Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> eitherResult = Either.left(emptyMap);
+ try {
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null) {
+ Function<ComponentInstance, Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus>> dataCollector = e -> componentInstanceOperation.getCapabilities(e, nodeTypeEnum);
+ Either<List<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus>>>, TitanOperationStatus> eitherDataCollected = collectDataFromComponentsInstances(componentInstances,
+ dataCollector);
+ if (eitherDataCollected.isRight()) {
+ eitherResult = Either.right(eitherDataCollected.right().value());
+ } else {
+ // Converts CapData to CapDef removes stop if encountered
+ // conversion error
+ DataDefConvertor<CapabilityDefinition, CapabilityData> someConvertor = (e1, e2) -> convertCapDataListToCapDefList(e1, e2);
+ Either<List<List<CapabilityDefinition>>, TitanOperationStatus> fullDefList = convertDataToDefComponentLevel(eitherDataCollected.left().value(), someConvertor);
+ if (fullDefList.isRight()) {
+ eitherResult = Either.right(fullDefList.right().value());
+ } else {
+ Stream<CapabilityDefinition> defStream = fullDefList.left().value().stream().flatMap(e -> e.stream());
+ // Collect to Map grouping by Type
+ Map<String, List<CapabilityDefinition>> capTypeCapListMap = defStream.collect(Collectors.groupingBy(e -> e.getType()));
+ eitherResult = Either.left(capTypeCapListMap);
+ }
+
+ }
+
+ }
+ } finally {
+ if (inTransaction == false) {
+ titanGenericDao.commit();
+ }
+ }
+
+ return eitherResult;
+ }
+
+ public <Data> Either<List<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>>>, TitanOperationStatus> collectDataFromComponentsInstances(List<ComponentInstance> componentInstances,
+ Function<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>> dataGetter) {
+ Either<List<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>>>, TitanOperationStatus> eitherResult;
+
+ // Get List of Each componentInstance and it's Capabilities Data
+ Stream<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>>> ownerDataStream = componentInstances.stream().map(element -> new ImmutablePair<>(element, dataGetter.apply(element)));
+ // Collect but stop after first error
+ List<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>>> ownerCapDataList = StreamUtils
+ .takeWhilePlusOne(ownerDataStream, p -> p.right.isLeft() || p.right.isRight() && p.right.right().value() == TitanOperationStatus.NOT_FOUND).collect(Collectors.toList());
+
+ Optional<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>>> optionalError = ownerCapDataList.stream()
+ .filter(p -> p.right.isRight() && p.right.right().value() != TitanOperationStatus.NOT_FOUND).findAny();
+ if (optionalError.isPresent()) {
+ eitherResult = Either.right(optionalError.get().right.right().value());
+ } else {
+ eitherResult = Either.left(ownerCapDataList.stream().filter(p -> p.right.isLeft()).collect(Collectors.toList()));
+ }
+
+ return eitherResult;
+ }
+
+ interface DataDefConvertor<Def, Data> {
+ Either<List<Def>, TitanOperationStatus> convertDataToDefComponentInstance(ComponentInstance componentInstance, List<ImmutablePair<Data, GraphEdge>> data);
+ }
+
+ public <Def, Data> Either<List<List<Def>>, TitanOperationStatus> convertDataToDefComponentLevel(List<ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>>> ownerCapDataList,
+ DataDefConvertor<Def, Data> convertor) {
+ // Converts CapData to CapDef removes stop if encountered conversion
+ // error
+ TitanOperationStatus error = null;
+ List<List<Def>> defList = new ArrayList<>();
+ for (int i = 0; i < ownerCapDataList.size(); i++) {
+ ImmutablePair<ComponentInstance, Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus>> immutablePair = ownerCapDataList.get(i);
+ Either<List<Def>, TitanOperationStatus> convertCapDataListToCapDefList = convertor.convertDataToDefComponentInstance(immutablePair.left, immutablePair.right.left().value());
+ if (convertCapDataListToCapDefList.isRight()) {
+ error = convertCapDataListToCapDefList.right().value();
+ break;
+ } else {
+ defList.add(convertCapDataListToCapDefList.left().value());
+ }
+
+ }
+ Either<List<List<Def>>, TitanOperationStatus> eitherResult = (error != null) ? Either.right(error) : Either.left(defList);
+ return eitherResult;
+
+ }
+
+ private Map<String, ComponentMetadataData> findLatestVersion(List<ComponentMetadataData> resourceDataList) {
+ Map<Pair<String, String>, ComponentMetadataData> latestVersionMap = new HashMap<Pair<String, String>, ComponentMetadataData>();
+ for (ComponentMetadataData resourceData : resourceDataList) {
+ ComponentMetadataData latestVersionData = resourceData;
+
+ ComponentMetadataDataDefinition metadataDataDefinition = resourceData.getMetadataDataDefinition();
+ Pair<String, String> pair = createKeyPair(latestVersionData);
+ if (latestVersionMap.containsKey(pair)) {
+ latestVersionData = latestVersionMap.get(pair);
+ String currentVersion = latestVersionData.getMetadataDataDefinition().getVersion();
+ String newVersion = metadataDataDefinition.getVersion();
+ if (CommonBeUtils.compareAsdcComponentVersions(newVersion, currentVersion)) {
+ latestVersionData = resourceData;
+ }
+ }
+ if (log.isDebugEnabled())
+ log.debug("last certified version of resource = {} version is {}", latestVersionData.getMetadataDataDefinition().getName(), latestVersionData.getMetadataDataDefinition().getVersion());
+
+ latestVersionMap.put(pair, latestVersionData);
+ }
+
+ Map<String, ComponentMetadataData> resVersionMap = new HashMap<String, ComponentMetadataData>();
+ for (ComponentMetadataData resourceData : latestVersionMap.values()) {
+ ComponentMetadataData latestVersionData = resourceData;
+ ComponentMetadataDataDefinition metadataDataDefinition = resourceData.getMetadataDataDefinition();
+ if (resVersionMap.containsKey(metadataDataDefinition.getUUID())) {
+ latestVersionData = resVersionMap.get(metadataDataDefinition.getUUID());
+ String currentVersion = latestVersionData.getMetadataDataDefinition().getVersion();
+ String newVersion = metadataDataDefinition.getVersion();
+ if (CommonBeUtils.compareAsdcComponentVersions(newVersion, currentVersion)) {
+ latestVersionData = resourceData;
+ }
+ }
+ if (log.isDebugEnabled())
+ log.debug("last uuid version of resource = {} version is {}", latestVersionData.getMetadataDataDefinition().getName(), latestVersionData.getMetadataDataDefinition().getVersion());
+ resVersionMap.put(latestVersionData.getMetadataDataDefinition().getUUID(), latestVersionData);
+ }
+
+ return resVersionMap;
+ }
+
+ private Pair<String, String> createKeyPair(ComponentMetadataData metadataData) {
+ Pair<String, String> pair = null;
+ NodeTypeEnum label = NodeTypeEnum.getByName(metadataData.getLabel());
+ switch (label) {
+ case Resource:
+ pair = new ImmutablePair<String, String>(metadataData.getMetadataDataDefinition().getName(), ((ResourceMetadataDataDefinition) metadataData.getMetadataDataDefinition()).getResourceType().getValue());
+ break;
+ default:
+ pair = new ImmutablePair<String, String>(metadataData.getMetadataDataDefinition().getName(), metadataData.getLabel());
+ break;
+ }
+
+ return pair;
+ }
+
+ public Either<Collection<ComponentMetadataData>, StorageOperationStatus> getLatestVersionNotAbstractComponentsMetadataOnly(boolean isAbstract, Boolean isHighest, ComponentTypeEnum componentTypeEnum, String internalComponentType) {
+ try {
+
+ // Map<String, Object> hasPpropertiesToMatch = new HashMap<>();
+ // Map<String, Object> hasNotPpropertiesToMatch = new HashMap<>();
+ List<ImmutableTriple<QueryType, String, Object>> properties = new ArrayList<>();
+ if (componentTypeEnum.equals(ComponentTypeEnum.RESOURCE)) {
+ // hasPpropertiesToMatch.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(),
+ // isAbstract);
+ properties.add(new ImmutableTriple<>(QueryType.HAS, GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), isAbstract));
+
+ if (internalComponentType != null) {
+ switch (internalComponentType.toLowerCase()) {
+ case "vf":
+ properties.add(new ImmutableTriple<>(QueryType.HAS_NOT, GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name()));
+ properties.add(new ImmutableTriple<>(QueryType.HAS_NOT, GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VL.name()));
+ // hasNotPpropertiesToMatch.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(),
+ // ResourceTypeEnum.VF.name());
+ break;
+ case "service":
+ properties.add(new ImmutableTriple<>(QueryType.HAS_NOT, GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VFC.name()));
+ properties.add(new ImmutableTriple<>(QueryType.HAS_NOT, GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VL.name()));
+ // hasNotPpropertiesToMatch.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(),
+ // ResourceTypeEnum.VFC.name());
+ break;
+ case "vl":
+ properties.add(new ImmutableTriple<>(QueryType.HAS, GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VL.name()));
+ // hasPpropertiesToMatch.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(),
+ // ResourceTypeEnum.VL.name());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // hasNotPpropertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(),
+ // LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ properties.add(new ImmutableTriple<>(QueryType.HAS_NOT, GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name()));
+ // hasNotPpropertiesToMatch.put(GraphPropertiesDictionary.IS_DELETED.getProperty(),
+ // true);
+ properties.add(new ImmutableTriple<>(QueryType.HAS_NOT, GraphPropertiesDictionary.IS_DELETED.getProperty(), true));
+ // Either<List<ComponentMetadataData>, TitanOperationStatus>
+ // resourceNodes = titanGenericDao.getByCriteria(
+ // componentTypeEnum.getNodeType(), hasPpropertiesToMatch,
+ // hasNotPpropertiesToMatch,
+ // ComponentMetadataData.class);
+ Either<List<ComponentMetadataData>, TitanOperationStatus> resourceNodes = titanGenericDao.getByCriteria(componentTypeEnum.getNodeType(), ComponentMetadataData.class, properties);
+ if (resourceNodes.isRight()) {
+ // in case of NOT_FOUND from Titan client return to UI empty
+ // list
+ if (resourceNodes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.left(new ArrayList<>());
+ } else {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(resourceNodes.right().value()));
+ }
+ } else {
+ List<ComponentMetadataData> resourceDataList = resourceNodes.left().value();
+ Collection<ComponentMetadataData> resCollection = resourceDataList;
+ if (isHighest != null && isHighest) {
+ Map<String, ComponentMetadataData> latestVersionListMap = findLatestVersion(resourceDataList);
+ resCollection = latestVersionListMap.values();
+ }
+ return Either.left(resCollection);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+
+ }
+
+ public Either<List<Component>, StorageOperationStatus> getLatestVersionNotAbstractComponents(boolean isAbstract, Boolean isHighest, ComponentTypeEnum componentTypeEnum, String internalComponentType, List<String> componentUids) {
+ try {
+ List<Component> result = new ArrayList<>();
+ Map<String, ResourceTypeEnum> componentUidsMap = new HashMap<>();
+ if (componentUids == null) {
+ Either<Collection<ComponentMetadataData>, StorageOperationStatus> resourceNodes = getLatestVersionNotAbstractComponentsMetadataOnly(isAbstract, isHighest, componentTypeEnum, internalComponentType);
+ if (resourceNodes.isRight()) {
+ return Either.right(resourceNodes.right().value());
+ }
+ Collection<ComponentMetadataData> collection = resourceNodes.left().value();
+
+ if (collection == null) {
+ componentUids = new ArrayList<>();
+ } else {
+ componentUids = collection.stream().map(p -> p.getMetadataDataDefinition().getUniqueId()).collect(Collectors.toList());
+ // collection.forEach(p -> {
+ // if (NodeTypeEnum.Resource.getName().equals(p.getLabel()))
+ // {
+ // componentUidsMap.put(p.getMetadataDataDefinition().getUniqueId(),
+ // ((ResourceMetadataDataDefinition)
+ // p.getMetadataDataDefinition()).getResourceType());
+ // }
+ // });
+
+ }
+
+ }
+ if (false == componentUids.isEmpty()) {
+
+ Manager manager = new Manager();
+ int numberOfWorkers = 5;
+
+ manager.init(numberOfWorkers);
+ for (String componentUid : componentUids) {
+ ComponentParametersView componentParametersView = buildComponentViewForNotAbstract();
+ // ResourceTypeEnum type =
+ // componentUidsMap.get(componentUid);
+ // if (type != null && ResourceTypeEnum.VL.equals(type)) {
+ if (internalComponentType != null && "vl".equalsIgnoreCase(internalComponentType)) {
+ componentParametersView.setIgnoreCapabilities(false);
+ componentParametersView.setIgnoreRequirements(false);
+ }
+ manager.addJob(new Job() {
+ @Override
+ public Either<Component, StorageOperationStatus> doWork() {
+ // long start = System.currentTimeMillis();
+ Either<Component, StorageOperationStatus> component = getComponent(componentUid, componentParametersView, false);
+ // long stop = System.currentTimeMillis();
+ // log.info("********** Time calculation in ms:
+ // getComponent single {}", (stop-start));
+ return component;
+ }
+ });
+ }
+ LinkedBlockingQueue<Either<Component, StorageOperationStatus>> res = manager.start();
+
+ for (Either<Component, StorageOperationStatus> resource : res) {
+ if (resource == null) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to fetch resource returned null ");
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ if (resource.isRight()) {
+ if (log.isDebugEnabled())
+ log.debug("Failed to fetch resource for error is {}", resource.right().value());
+ return Either.right(resource.right().value());
+ }
+ Component component = resource.left().value();
+ component.setContactId(null);
+ component.setCreationDate(null);
+ component.setCreatorUserId(null);
+ component.setCreatorFullName(null);
+ component.setLastUpdateDate(null);
+ component.setLastUpdaterUserId(null);
+ component.setLastUpdaterFullName(null);
+ component.setNormalizedName(null);
+ result.add(resource.left().value());
+ }
+
+ if (componentUids.size() != result.size()) {
+ if (log.isDebugEnabled())
+ log.debug("one of the workers failed to complete job ");
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+ return Either.left(result);
+
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ private ComponentParametersView buildComponentViewForNotAbstract() {
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ // componentParametersView.setIgnoreRequirements(false);
+ // componentParametersView.setIgnoreCapabilities(false);
+ componentParametersView.setIgnoreCategories(false);
+ componentParametersView.setIgnoreAllVersions(false);
+ componentParametersView.setIgnoreAllVersions(false);
+ return componentParametersView;
+ }
+
+ protected TitanOperationStatus setCapabilitiesFromGraph(String uniqueId, Component component, NodeTypeEnum nodeType) {
+ TitanOperationStatus titanStatus;
+ Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> eitherCapabilities = getCapabilities(component, nodeType, true);
+ if (eitherCapabilities.isLeft()) {
+ titanStatus = TitanOperationStatus.OK;
+ Map<String, List<CapabilityDefinition>> capabilities = eitherCapabilities.left().value();
+ if (capabilities != null && !capabilities.isEmpty()) {
+ component.setCapabilities(capabilities);
+ }
+ } else {
+ titanStatus = eitherCapabilities.right().value();
+ }
+ return titanStatus;
+ }
+
+ protected TitanOperationStatus setRequirementsFromGraph(String uniqueId, Component component, NodeTypeEnum nodeType) {
+ TitanOperationStatus status;
+ Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> eitherRequirements = getRequirements(component, nodeType, false);
+ if (eitherRequirements.isLeft()) {
+ status = TitanOperationStatus.OK;
+ Map<String, List<RequirementDefinition>> requirements = eitherRequirements.left().value();
+ if (requirements != null && !requirements.isEmpty()) {
+ component.setRequirements(requirements);
+ }
+ } else {
+ status = eitherRequirements.right().value();
+ }
+ return status;
+ }
+
+ protected boolean isComponentExist(String componentId, NodeTypeEnum nodeType) {
+ boolean result = true;
+ Either<TitanVertex, TitanOperationStatus> compVertex = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId);
+ if (compVertex.isRight()) {
+ log.debug("failed to fetch vertex of component data for id {}", componentId);
+ result = false;
+
+ }
+ return result;
+ }
+
+ <T> Either<T, StorageOperationStatus> getLightComponent(String id, NodeTypeEnum nodeType, boolean inTransaction) {
+ Either<Component, StorageOperationStatus> metadataComponent = getMetadataComponent(id, nodeType, inTransaction);
+ if (metadataComponent.isRight()) {
+
+ }
+ T component = null;
+ try {
+ log.debug("Starting to build light component of type {}, id {}", nodeType, id);
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ }
+ TitanGraph titanGraph = graphResult.left().value();
+ Iterable<TitanVertex> vertecies = titanGraph.query().has(UniqueIdBuilder.getKeyByNodeType(nodeType), id).vertices();
+ if (vertecies != null) {
+ Iterator<TitanVertex> iterator = vertecies.iterator();
+ if (iterator != null && iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ Map<String, Object> resourceProperties = titanGenericDao.getProperties(vertex);
+ ComponentMetadataData componentMetadataData = GraphElementFactory.createElement(nodeType.getName(), GraphElementTypeEnum.Node, resourceProperties, ComponentMetadataData.class);
+ component = (T) convertComponentMetadataDataToComponent(componentMetadataData);
+
+ // get creator
+ Iterator<Edge> iterCreator = vertex.edges(Direction.IN, GraphEdgeLabels.CREATOR.name());
+ if (iterCreator.hasNext() == false) {
+ log.debug("no creator was defined for component {}", id);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ Vertex vertexCreator = iterCreator.next().outVertex();
+ UserData creator = GraphElementFactory.createElement(NodeTypeEnum.User.getName(), GraphElementTypeEnum.Node, titanGenericDao.getProperties(vertexCreator), UserData.class);
+ log.debug("Build component : set creator userId to {}", creator.getUserId());
+ String fullName = buildFullName(creator);
+ log.debug("Build component : set creator full name to {}", fullName);
+ ((Component) component).setCreatorUserId(creator.getUserId());
+ ((Component) component).setCreatorFullName(fullName);
+
+ // get modifier
+ Iterator<Edge> iterModifier = vertex.edges(Direction.IN, GraphEdgeLabels.LAST_MODIFIER.name());
+
+ if (iterModifier.hasNext() == false) {
+ log.debug("no modifier was defined for component {}", id);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ Vertex vertexModifier = iterModifier.next().outVertex();
+ UserData modifier = GraphElementFactory.createElement(NodeTypeEnum.User.getName(), GraphElementTypeEnum.Node, titanGenericDao.getProperties(vertexModifier), UserData.class);
+ log.debug("Build component : set last modifier userId to {}", creator.getUserId());
+ fullName = buildFullName(modifier);
+ log.debug("Build component : set last modifier full name to {}", fullName);
+ ((Component) component).setLastUpdaterUserId(modifier.getUserId());
+ ((Component) component).setLastUpdaterFullName(fullName);
+
+ // get category
+ TitanOperationStatus status = setComponentCategoriesFromGraph((Component) component);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ } else {
+ // Nothing found
+ log.debug("Component with id {} not found", id);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ } else {
+ // Nothing found
+ log.debug("Component with id {} not found", id);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ log.debug("Ended to build light component of type {}, id {}", nodeType, id);
+ return Either.left(component);
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ Either<Component, StorageOperationStatus> getMetadataComponent(String id, NodeTypeEnum nodeType, boolean inTransaction) {
+ Component component = null;
+ try {
+ log.debug("Starting to build metadata component of type {}, id {}", nodeType, id);
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ }
+ TitanGraph titanGraph = graphResult.left().value();
+ Iterable<TitanVertex> vertecies = titanGraph.query().has(UniqueIdBuilder.getKeyByNodeType(nodeType), id).vertices();
+ if (vertecies != null) {
+ Iterator<TitanVertex> iterator = vertecies.iterator();
+ if (iterator != null && iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ Map<String, Object> resourceProperties = titanGenericDao.getProperties(vertex);
+ ComponentMetadataData componentMetadataData = GraphElementFactory.createElement(nodeType.getName(), GraphElementTypeEnum.Node, resourceProperties, ComponentMetadataData.class);
+ component = convertComponentMetadataDataToComponent(componentMetadataData);
+ } else {
+ // Nothing found
+ log.debug("Component with id {} not found", id);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ } else {
+ // Nothing found
+ log.debug("Component with id {} not found", id);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ log.debug("Ended to build metadata component of type {}, id {}", nodeType, id);
+ return Either.left(component);
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ public Either<Integer, StorageOperationStatus> getComponentInstanceCoutner(String origServiceId, NodeTypeEnum nodeType) {
+ Either<Integer, StorageOperationStatus> result;
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+ Either<TitanVertex, TitanOperationStatus> vertexService = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), origServiceId);
+ if (vertexService.isRight()) {
+ log.debug("failed to fetch vertex of component metadata, nodeType:{} , id: {}", nodeType, origServiceId);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(vertexService.right().value()));
+ return result;
+ }
+ Vertex vertex = vertexService.left().value();
+ Integer instanceCounter = vertex.value(GraphPropertiesDictionary.INSTANCE_COUNTER.getProperty());
+ return Either.left(instanceCounter);
+ }
+
+ protected TitanOperationStatus setComponentInstancesPropertiesFromGraph(String uniqueId, Component component) {
+
+ List<ComponentInstance> resourceInstances = component.getComponentInstances();
+
+ Map<String, List<ComponentInstanceProperty>> resourceInstancesProperties = new HashMap<>();
+
+ Map<String, List<PropertyDefinition>> alreadyProcessedResources = new HashMap<>();
+
+ Map<String, List<ComponentInstanceProperty>> alreadyProcessedInstances = new HashMap<>();
+
+ Map<String, ImmutablePair<ComponentInstance, Integer>> processedInstances = new HashMap<>();
+
+ if (resourceInstances != null) {
+
+ for (ComponentInstance resourceInstance : resourceInstances) {
+
+ List<String> path = new ArrayList<>();
+ path.add(resourceInstance.getUniqueId());
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> componentInstanceProperties = componentInstanceOperation.getComponentInstanceProperties(resourceInstance, alreadyProcessedResources, alreadyProcessedInstances,
+ processedInstances, path);
+
+ if (componentInstanceProperties.isRight()) {
+ TitanOperationStatus status = componentInstanceProperties.right().value();
+ if (status != TitanOperationStatus.OK) {
+ return status;
+ }
+ }
+
+ List<ComponentInstanceProperty> listOfProps = componentInstanceProperties.left().value();
+ String resourceInstanceUid = resourceInstance.getUniqueId();
+ resourceInstancesProperties.put(resourceInstanceUid, listOfProps);
+
+ // alreadyProcessedInstances.put(resourceInstance.getUniqueId(),
+ // resourceInstance);
+
+ processedInstances.put(resourceInstance.getUniqueId(), new ImmutablePair<ComponentInstance, Integer>(resourceInstance, path.size()));
+ path.remove(path.size() - 1);
+
+ }
+
+ }
+
+ Either<Map<String, Map<String, ComponentInstanceProperty>>, TitanOperationStatus> findAllPropertiesValuesOnInstances = componentInstanceOperation.findAllPropertyValueOnInstances(processedInstances);
+ // 1. check status
+ if (findAllPropertiesValuesOnInstances.isRight()) {
+ TitanOperationStatus status = findAllPropertiesValuesOnInstances.right().value();
+ if (status != TitanOperationStatus.OK) {
+ return status;
+ }
+ }
+ // 2. merge data from rules on properties (resourceInstancesProperties)
+ propertyOperation.updatePropertiesByPropertyValues(resourceInstancesProperties, findAllPropertiesValuesOnInstances.left().value());
+
+ component.setComponentInstancesProperties(resourceInstancesProperties);
+
+ return TitanOperationStatus.OK;
+ }
+
+ protected TitanOperationStatus setComponentInstancesInputsFromGraph(String uniqueId, Component component) {
+
+ Map<String, List<ComponentInstanceInput>> resourceInstancesInputs = new HashMap<>();
+ TitanOperationStatus status = TitanOperationStatus.OK;
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null) {
+ for (ComponentInstance resourceInstance : componentInstances) {
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> eitherRIAttributes = inputOperation.getAllInputsOfResourceInstance(resourceInstance);
+ if (eitherRIAttributes.isRight()) {
+ if (eitherRIAttributes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ status = eitherRIAttributes.right().value();
+ break;
+ }
+ } else {
+ resourceInstancesInputs.put(resourceInstance.getUniqueId(), eitherRIAttributes.left().value());
+ }
+ }
+ if (!resourceInstancesInputs.isEmpty())
+ component.setComponentInstancesInputs(resourceInstancesInputs);
+ }
+
+ return status;
+ }
+
+ public Either<String, StorageOperationStatus> getInvariantUUID(NodeTypeEnum nodeType, String componentId, boolean inTransaction) {
+ Either<String, StorageOperationStatus> res = null;
+ try {
+ Either<TitanVertex, TitanOperationStatus> vertexByProperty = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId);
+ if (vertexByProperty.isRight()) {
+ TitanOperationStatus status = vertexByProperty.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ res = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ Vertex v = vertexByProperty.left().value();
+ String invariantUUID = v.value(GraphPropertiesDictionary.INVARIANT_UUID.getProperty());
+
+ if (invariantUUID == null || invariantUUID.isEmpty()) {
+
+ log.info("The component {} has empty invariant UUID.", componentId);
+ res = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.INVALID_ELEMENT));
+
+ }
+ res = Either.left(invariantUUID);
+ }
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ return res;
+ }
+
+ protected TitanOperationStatus setGroupsFromGraph(String uniqueId, Component component, NodeTypeEnum nodeTypeEnum) {
+
+ Either<List<GroupDefinition>, TitanOperationStatus> res = groupOperation.getAllGroupsFromGraph(uniqueId, nodeTypeEnum);
+ if (res.isRight()) {
+ TitanOperationStatus status = res.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return TitanOperationStatus.OK;
+ } else {
+ return status;
+ }
+ }
+ component.setGroups(res.left().value());
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ protected TitanOperationStatus setComponentInputsFromGraph(String uniqueId, Component component, boolean inTransaction) {
+
+ List<InputDefinition> inputs = new ArrayList<>();
+ TitanOperationStatus status = inputsOperation.findAllResourceInputs(uniqueId, inputs);
+ if (status == TitanOperationStatus.OK) {
+ component.setInputs(inputs);
+ }
+
+ return status;
+
+ }
+
+ protected StorageOperationStatus deleteGroups(NodeTypeEnum nodeType, String componentId) {
+
+ Either<List<GroupDefinition>, StorageOperationStatus> deleteRes = groupOperation.deleteAllGroups(componentId, nodeType, true);
+
+ if (deleteRes.isRight()) {
+ StorageOperationStatus status = deleteRes.right().value();
+ return status;
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ protected StorageOperationStatus removeInputsFromComponent(NodeTypeEnum typeEnum, Component component) {
+ Either<Map<String, InputDefinition>, StorageOperationStatus> deleteAllInputsAssociatedToNode = inputsOperation.deleteAllInputsAssociatedToNode(typeEnum, component.getUniqueId());
+ return deleteAllInputsAssociatedToNode.isRight() ? deleteAllInputsAssociatedToNode.right().value() : StorageOperationStatus.OK;
+ }
+
+ protected TitanOperationStatus associateInputsToComponent(NodeTypeEnum nodeType, ComponentMetadataData resourceData, List<InputDefinition> properties) {
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Cannot find any data type. Status is {}.", status);
+ return status;
+ }
+
+ Map<String, InputDefinition> convertedProperties = new HashMap<>();
+
+ if (properties != null) {
+ for (InputDefinition propertyDefinition : properties) {
+ convertedProperties.put(propertyDefinition.getName(), propertyDefinition);
+ }
+
+ Either<List<InputDefinition>, TitanOperationStatus> operationStatus = inputsOperation.addInputsToGraph(resourceData.getMetadataDataDefinition().getUniqueId(), nodeType, convertedProperties, allDataTypes.left().value());
+ if (operationStatus.isLeft())
+ return TitanOperationStatus.OK;
+ else
+ return operationStatus.right().value();
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ protected TitanOperationStatus associateInputsToComponent(TitanVertex metadataVertex, String componentId, List<InputDefinition> properties) {
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Cannot find any data type. Status is {}.", status);
+ return status;
+ }
+
+ Map<String, InputDefinition> convertedProperties = new HashMap<>();
+
+ if (properties != null) {
+ for (InputDefinition propertyDefinition : properties) {
+ convertedProperties.put(propertyDefinition.getName(), propertyDefinition);
+ }
+
+ return inputsOperation.addInputsToGraph(metadataVertex, componentId, convertedProperties, allDataTypes.left().value());
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ public Either<List<ComponentInstance>, StorageOperationStatus> getAllComponentInstncesMetadata(String componentId, NodeTypeEnum nodeType) {
+ Instant start = Instant.now();
+ Either<List<ComponentInstance>, StorageOperationStatus> resourceInstancesOfService = componentInstanceOperation.getAllComponentInstancesMetadataOnly(componentId, nodeType);
+ Instant end = Instant.now();
+ log.debug("TOTAL TIME BL GET INSTANCES: {}", Duration.between(start, end)); // prints
+ // PT1M3.553S
+ return resourceInstancesOfService;
+ }
+
+ @Deprecated
+ public Either<List<Component>, ActionStatus> getComponentsFromCacheForCatalog(Set<String> components, ComponentTypeEnum componentType) {
+
+ Either<ImmutableTriple<List<Component>, List<Component>, Set<String>>, ActionStatus> componentsForCatalog = componentCache.getComponentsForCatalog(components, componentType);
+ if (componentsForCatalog.isLeft()) {
+ ImmutableTriple<List<Component>, List<Component>, Set<String>> immutableTriple = componentsForCatalog.left().value();
+ List<Component> foundComponents = immutableTriple.getLeft();
+
+ if (foundComponents != null) {
+ // foundComponents.forEach(p -> result.add((Resource)p));
+ log.debug("The number of {}s added to catalog from cache is {}", componentType.name().toLowerCase(), foundComponents.size());
+
+ }
+ List<Component> foundDirtyComponents = immutableTriple.getMiddle();
+ Set<String> nonCachedComponents = immutableTriple.getRight();
+ int numberDirtyResources = foundDirtyComponents == null ? 0 : foundDirtyComponents.size();
+ int numberNonCached = nonCachedComponents == null ? 0 : nonCachedComponents.size();
+ log.debug("The number of left {}s for catalog is {}", componentType.name().toLowerCase(), numberDirtyResources + numberNonCached);
+ return Either.left(foundComponents);
+ }
+
+ return Either.right(componentsForCatalog.right().value());
+ }
+
+ public <T extends ComponentMetadataData> Either<List<T>, TitanOperationStatus> getListOfHighestComponents(NodeTypeEnum nodeTypeEnum, Class<T> clazz) {
+
+ long startFetchAllStates = System.currentTimeMillis();
+ Map<String, Object> propertiesToMatchHigest = new HashMap<>();
+ propertiesToMatchHigest.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ Either<List<T>, TitanOperationStatus> allHighestStates = titanGenericDao.getByCriteria(nodeTypeEnum, propertiesToMatchHigest, clazz);
+ if (allHighestStates.isRight() && allHighestStates.right().value() != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(allHighestStates.right().value());
+ }
+ long endFetchAllStates = System.currentTimeMillis();
+
+ if (allHighestStates.isRight()) {
+ return Either.left(new ArrayList<>());
+ }
+ List<T> services = allHighestStates.left().value();
+
+ List<T> certifiedHighest = new ArrayList<>();
+ List<T> notCertifiedHighest = new ArrayList<>();
+ for (T reData : services) {
+ if (reData.getMetadataDataDefinition().getState().equals(LifecycleStateEnum.CERTIFIED.name())) {
+ certifiedHighest.add(reData);
+ } else {
+ notCertifiedHighest.add(reData);
+ }
+ }
+
+ log.debug("Fetch catalog {}s all states: certified {}, noncertified {}", nodeTypeEnum.getName(), certifiedHighest.size(), notCertifiedHighest.size());
+ log.debug("Fetch catalog {}s all states from graph took {} ms", nodeTypeEnum.getName(), endFetchAllStates - startFetchAllStates);
+
+ HashMap<String, String> serviceNames = new HashMap<>();
+ for (T data : notCertifiedHighest) {
+ String serviceName = data.getMetadataDataDefinition().getName();
+ serviceNames.put(serviceName, serviceName);
+ }
+
+ for (T data : certifiedHighest) {
+ String serviceName = data.getMetadataDataDefinition().getName();
+ if (!serviceNames.containsKey(serviceName)) {
+ notCertifiedHighest.add(data);
+ }
+ }
+
+ return Either.left(notCertifiedHighest);
+ }
+
+ protected <T extends Component> Either<T, ActionStatus> getComponentFromCacheIfUpToDate(String uniqueId, ComponentMetadataData componentMetadataData, ComponentParametersView componentParametersView, Class<T> clazz,
+ ComponentTypeEnum componentTypeEnum) {
+
+ long start = System.currentTimeMillis();
+ try {
+
+ long lastModificationTime = componentMetadataData.getMetadataDataDefinition().getLastUpdateDate();
+ Either<Component, ActionStatus> cacheComponentRes = this.componentCache.getComponent(uniqueId, lastModificationTime);
+ if (cacheComponentRes.isLeft()) {
+ Component cachedComponent = cacheComponentRes.left().value();
+
+ // Must calculate allVersions
+ if (false == componentParametersView.isIgnoreAllVersions()) {
+ Class<? extends ComponentMetadataData> clazz1 = null;
+ switch (componentTypeEnum) {
+ case RESOURCE:
+ clazz1 = ResourceMetadataData.class;
+ break;
+ case SERVICE:
+ clazz1 = ServiceMetadataData.class;
+ break;
+ case PRODUCT:
+ clazz1 = ProductMetadataData.class;
+ break;
+ default:
+ break;
+ }
+ if (clazz1 != null) {
+ // long startGetAllVersions =
+ // System.currentTimeMillis();
+ Either<Map<String, String>, TitanOperationStatus> versionList = getVersionList(componentTypeEnum.getNodeType(), cachedComponent.getVersion(), cachedComponent.getUUID(), cachedComponent.getSystemName(), clazz1);
+ // log.debug("Fetch all versions for component {} took
+ // {} ms", cachedComponent.getUniqueId(),
+ // System.currentTimeMillis() - startGetAllVersions);
+ if (versionList.isRight()) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ Map<String, String> allVersions = versionList.left().value();
+ cachedComponent.setAllVersions(allVersions);
+ } else {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ }
+ if (componentParametersView != null) {
+ cachedComponent = componentParametersView.filter(cachedComponent, componentTypeEnum);
+ }
+ return Either.left(clazz.cast(cachedComponent));
+ }
+
+ return Either.right(cacheComponentRes.right().value());
+
+ } finally {
+ log.trace("Fetch component {} with uid {} from cache took {} ms", componentTypeEnum.name().toLowerCase(), uniqueId, System.currentTimeMillis() - start);
+ }
+ }
+
+ public Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> getComponentsFromCacheForCatalog(Map<String, Long> components, ComponentTypeEnum componentType) {
+
+ Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> componentsForCatalog = componentCache.getComponentsForCatalog(components, componentType);
+ if (componentsForCatalog.isLeft()) {
+ ImmutablePair<List<Component>, Set<String>> immutablePair = componentsForCatalog.left().value();
+ List<Component> foundComponents = immutablePair.getLeft();
+
+ if (foundComponents != null) {
+ // foundComponents.forEach(p -> result.add((Resource)p));
+ log.debug("The number of {}s added to catalog from cache is {}", componentType.name().toLowerCase(), foundComponents.size());
+ }
+ Set<String> leftComponents = immutablePair.getRight();
+ int numberNonCached = leftComponents == null ? 0 : leftComponents.size();
+ log.debug("The number of left {}s for catalog is {}", componentType.name().toLowerCase(), numberNonCached);
+
+ ImmutablePair<List<Component>, Set<String>> result = new ImmutablePair<List<Component>, Set<String>>(foundComponents, leftComponents);
+ return Either.left(result);
+ }
+
+ return Either.right(componentsForCatalog.right().value());
+ }
+
+ /**
+ *
+ * @param component
+ * @param inTransaction
+ * @param titanGenericDao
+ * @param clazz
+ * @return
+ */
+ public <T> Either<T, StorageOperationStatus> updateComponentFilterResult(Component component, boolean inTransaction, TitanGenericDao titanGenericDao, Class<T> clazz, NodeTypeEnum type, ComponentParametersView filterResult) {
+ Either<T, StorageOperationStatus> result = null;
+
+ try {
+
+ log.debug("In updateComponent. received component uid = {}", (component == null ? null : component.getUniqueId()));
+ if (component == null) {
+ log.error("Service object is null");
+ result = Either.right(StorageOperationStatus.BAD_REQUEST);
+ return result;
+ }
+
+ ComponentMetadataData componentData = getMetaDataFromComponent(component);
+
+ log.debug("After converting component to componentData. ComponentData = {}", componentData);
+
+ if (componentData.getUniqueId() == null) {
+ log.error("Resource id is missing in the request.");
+ return Either.right(StorageOperationStatus.BAD_REQUEST);
+ }
+
+ Either<Integer, StorageOperationStatus> counterStatus = this.getComponentInstanceCoutner(component.getUniqueId(), component.getComponentType().getNodeType());
+
+ if (counterStatus.isRight()) {
+
+ log.error("Cannot find componentInstanceCounter for component {} in the graph. Status is {}", componentData.getUniqueId(), counterStatus);
+ // result = sendError(status,
+ // StorageOperationStatus.USER_NOT_FOUND);
+ return result;
+ }
+
+ componentData.setComponentInstanceCounter(counterStatus.left().value());
+
+ String modifierUserId = component.getLastUpdaterUserId();
+ if (modifierUserId == null || modifierUserId.isEmpty()) {
+ log.error("userId is missing in the request.");
+ result = Either.right(StorageOperationStatus.BAD_REQUEST);
+ return result;
+ }
+ Either<UserData, TitanOperationStatus> findUser = findUser(modifierUserId);
+
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user {} in the graph. Status is {}", modifierUserId, status);
+ // result = sendError(status,
+ // StorageOperationStatus.USER_NOT_FOUND);
+ return result;
+ }
+
+ UserData modifierUserData = findUser.left().value();
+ String resourceId = component.getUniqueId();
+
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreUsers(false);
+ componentParametersView.setIgnoreCategories(false);
+ componentParametersView.setIgnoreDerivedFrom(false);
+ componentParametersView.setIgnoreArtifacts(false);
+ Either<T, StorageOperationStatus> currentComponentResult = this.getComponent(resourceId, componentParametersView, inTransaction);
+ if (currentComponentResult.isRight()) {
+ log.error("Cannot find resource with id {} in the graph.", resourceId);
+ result = Either.right(currentComponentResult.right().value());
+ return result;
+ }
+
+ Component currentComponent = (Component) currentComponentResult.left().value();
+ String currentModifier = currentComponent.getLastUpdaterUserId();
+
+ if (currentModifier.equals(modifierUserData.getUniqueId())) {
+ log.debug("Graph LAST MODIFIER edge should not be changed since the modifier is the same as the last modifier.");
+ } else {
+ log.debug("Going to update the last modifier user of the resource from {} to {}", currentModifier, modifierUserId);
+ StorageOperationStatus status = moveLastModifierEdge(component, componentData, modifierUserData, type);
+ log.debug("Finish to update the last modifier user of the resource from {} to {}. Status is {}", currentModifier, modifierUserId, status);
+ if (status != StorageOperationStatus.OK) {
+ result = Either.right(status);
+ return result;
+ }
+ }
+ final long currentTimeMillis = System.currentTimeMillis();
+ log.debug("Going to update the last Update Date of the resource from {} to {}", component.getLastUpdateDate(), currentTimeMillis);
+ component.setLastUpdateDate(currentTimeMillis);
+
+ StorageOperationStatus checkCategories = validateCategories(currentComponent, component, componentData, type);
+ if (checkCategories != StorageOperationStatus.OK) {
+ result = Either.right(checkCategories);
+ return result;
+ }
+
+ List<String> tags = component.getTags();
+ if (tags != null && false == tags.isEmpty()) {
+ Either<List<TagData>, StorageOperationStatus> tagsResult = createNewTagsList(tags);
+ if (tagsResult.isRight()) {
+ result = Either.right(tagsResult.right().value());
+ return result;
+ }
+ List<TagData> tagsToCreate = tagsResult.left().value();
+ if (tagsToCreate != null && !tagsToCreate.isEmpty()) {
+ tagsToCreate = ImmutableSet.copyOf(tagsToCreate).asList();
+ for (TagData tagData : tagsToCreate) {
+ log.debug("Before creating tag {}", tagData);
+ Either<TagData, TitanOperationStatus> createTagResult = titanGenericDao.createNode(tagData, TagData.class);
+ if (createTagResult.isRight()) {
+ TitanOperationStatus status = createTagResult.right().value();
+ log.error("Cannot find tag {} in the graph. Status is {}", tagData, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ log.debug("After creating tag {}", tagData);
+ }
+ }
+ }
+
+ Either<ComponentMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(componentData, ComponentMetadataData.class);
+
+ if (updateNode.isRight()) {
+ log.error("Failed to update resource {}. Status is {}", component.getUniqueId(), updateNode.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value()));
+ return result;
+ }
+
+ ComponentMetadataData updatedResourceData = updateNode.left().value();
+ log.debug("ComponentData After update is {}", updatedResourceData);
+
+ // DE230195 in case resource name changed update TOSCA artifacts
+ // file names accordingly
+ String newSystemName = updatedResourceData.getMetadataDataDefinition().getSystemName();
+ if (newSystemName != null && !newSystemName.equals(currentComponent.getSystemName())) {
+ Map<String, ArtifactDefinition> toscaArtifacts = component.getToscaArtifacts();
+ if (toscaArtifacts != null) {
+ for (Entry<String, ArtifactDefinition> artifact : toscaArtifacts.entrySet()) {
+ Either<ArtifactData, StorageOperationStatus> updateName = generateAndUpdateToscaFileName(component.getComponentType().getValue().toLowerCase(), newSystemName, updatedResourceData.getMetadataDataDefinition().getUniqueId(),
+ type, artifact.getValue());
+ if (updateName.isRight()) {
+ result = Either.right(updateName.right().value());
+ return result;
+ }
+ }
+ }
+
+ }
+
+ if (component.getComponentType().equals(ComponentTypeEnum.RESOURCE)) {
+ updateDerived(component, currentComponent, componentData, component.getClass());
+ }
+
+ Either<T, StorageOperationStatus> updatedResource = getComponent(component.getUniqueId(), filterResult, inTransaction);
+ if (updatedResource.isRight()) {
+ log.error("Resource id is missing in the request. status is {}", updatedResource.right().value());
+ result = Either.right(StorageOperationStatus.BAD_REQUEST);
+ return result;
+ }
+
+ T updatedResourceValue = updatedResource.left().value();
+ result = Either.left(updatedResourceValue);
+
+ if (log.isDebugEnabled()) {
+ // String json = prettyJson.toJson(result.left().value());
+ // log.debug("Resource retrieved after update is {}", json);
+ }
+
+ return result;
+
+ } finally {
+
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("updateComponent operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("updateComponent operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ConsumerOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ConsumerOperation.java
new file mode 100644
index 0000000000..aafa4ba444
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ConsumerOperation.java
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.operations.api.IConsumerOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ConsumerData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("consumer-operation")
+public class ConsumerOperation implements IConsumerOperation {
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ private static Logger log = LoggerFactory.getLogger(ConsumerOperation.class.getName());
+
+ public ConsumerOperation() {
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> getCredentials(String consumerName) {
+ Either<ConsumerData, StorageOperationStatus> result = null;
+ log.debug("retriving Credentials for: {}.", consumerName);
+ Either<ConsumerData, TitanOperationStatus> getNode = titanGenericDao.getNode(GraphPropertiesDictionary.CONSUMER_NAME.getProperty(), consumerName, ConsumerData.class);
+ if (getNode.isRight()) {
+ TitanOperationStatus status = getNode.right().value();
+ log.error("Error returned after get Consumer Data node " + consumerName + ". status returned is " + status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ ConsumerData consumerData = getNode.left().value();
+ return Either.left(consumerData);
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> createCredentials(ConsumerData consumerData) {
+ return createCredentials(consumerData, false);
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> createCredentials(ConsumerData consumerData, boolean inTransaction) {
+ Either<ConsumerData, StorageOperationStatus> result = null;
+ try {
+ log.debug("creating Credentials for: {}.", consumerData.getUniqueId());
+ Either<ConsumerData, TitanOperationStatus> createNode = titanGenericDao.createNode(consumerData, ConsumerData.class);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ log.error("Error returned after creating Consumer Data node " + consumerData.getUniqueId() + ". status returned is " + status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ ConsumerData createdConsumerData = createNode.left().value();
+ result = Either.left(createdConsumerData);
+ return result;
+ } finally {
+ handleTransaction(inTransaction, result);
+ }
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> deleteCredentials(String consumerName) {
+ return deleteCredentials(consumerName, false);
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> deleteCredentials(String consumerName, boolean inTransaction) {
+ Either<ConsumerData, StorageOperationStatus> result = null;
+ try {
+ log.debug("delete Credentials for: {}", consumerName);
+ Either<ConsumerData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(GraphPropertiesDictionary.CONSUMER_NAME.getProperty(), consumerName, ConsumerData.class);
+ if (deleteNode.isRight()) {
+ TitanOperationStatus status = deleteNode.right().value();
+ log.error("Error returned after delete Consumer Data node {}. Status returned is {}", consumerName, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ ConsumerData deletedConsumerData = deleteNode.left().value();
+ result = Either.left(deletedConsumerData);
+ return result;
+ } finally {
+ handleTransaction(inTransaction, result);
+ }
+
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> updateCredentials(ConsumerData consumerData) {
+ return updateCredentials(consumerData, false);
+ }
+
+ @Override
+ public Either<ConsumerData, StorageOperationStatus> updateCredentials(ConsumerData consumerData, boolean inTransaction) {
+
+ Either<ConsumerData, StorageOperationStatus> result = null;
+ try {
+ log.debug("update Credentials for: {}.", consumerData.getUniqueId());
+ Either<ConsumerData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(consumerData, ConsumerData.class);
+ if (updateNode.isRight()) {
+ TitanOperationStatus status = updateNode.right().value();
+ log.error("Error returned after delete Consumer Data node {}. Status returned is {}", consumerData.getUniqueId(), status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ ConsumerData updatedConsumerData = updateNode.left().value();
+ result = Either.left(updatedConsumerData);
+ return result;
+ } finally {
+ handleTransaction(inTransaction, result);
+ }
+ }
+
+ private void handleTransaction(boolean inTransaction, Either<ConsumerData, StorageOperationStatus> result) {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CsarOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CsarOperation.java
new file mode 100644
index 0000000000..1420ce08d8
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/CsarOperation.java
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.io.filefilter.WildcardFileFilter;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.common.util.ZipUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("csar-operation")
+public class CsarOperation {
+
+ private static Logger log = LoggerFactory.getLogger(CsarOperation.class.getName());
+
+ @javax.annotation.Resource
+ private OnboardingClient onboardingClient;
+
+ public static void main(String[] args) {
+
+ CsarOperation csarOperation = new CsarOperation();
+ csarOperation.init();
+
+ String csarUuid = "70025CF6081B489CA7B1CBA583D5278D";
+ Either<Map<String, byte[]>, StorageOperationStatus> csar = csarOperation.getCsar(csarUuid, null);
+ System.out.println(csar.left().value());
+
+ }
+
+ @PostConstruct
+ public void init() {
+
+ }
+
+ // Mock returning a file from the file system until we have API from onboarding
+ public Either<Map<String, byte[]>, StorageOperationStatus> getMockCsar(String csarUuid) {
+ File dir = new File("/var/tmp/mockCsar");
+ FileFilter fileFilter = new WildcardFileFilter("*.csar");
+ File[] files = dir.listFiles(fileFilter);
+ for (int i = 0; i < files.length; i++) {
+ File csar = files[i];
+ if (csar.getName().startsWith(csarUuid)) {
+ log.debug("Found CSAR file {} matching the passed csarUuid {}", csar.getAbsolutePath(), csarUuid);
+ byte[] data;
+ try {
+ data = Files.readAllBytes(csar.toPath());
+ } catch (IOException e) {
+ log.debug("Error reading mock file for CSAR, error: {}", e);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Map<String, byte[]> readZip = ZipUtil.readZip(data);
+ return Either.left(readZip);
+ }
+ }
+ log.debug("Couldn't find mock file for CSAR starting with {}", csarUuid);
+ return Either.right(StorageOperationStatus.CSAR_NOT_FOUND);
+ }
+
+ /**
+ * get csar from remote repository
+ *
+ * @param csarUuid
+ * @return
+ */
+ public Either<Map<String, byte[]>, StorageOperationStatus> getCsar(String csarUuid, User user) {
+
+ Either<Map<String, byte[]>, StorageOperationStatus> result = onboardingClient.getCsar(csarUuid,
+ user.getUserId());
+
+ if (result.isRight()) {
+ log.debug("Cannot find csar {}. Status returned is {}", csarUuid, result.right().value());
+ } else {
+ Map<String, byte[]> values = result.left().value();
+ if (values != null) {
+ log.debug("The returned files are {}", values.keySet());
+ }
+ }
+
+ return result;
+ }
+
+ public OnboardingClient getOnboardingClient() {
+ return onboardingClient;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DaoStatusConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DaoStatusConverter.java
new file mode 100644
index 0000000000..b887c5b212
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DaoStatusConverter.java
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import org.openecomp.sdc.be.dao.api.ResourceUploadStatus;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+
+public class DaoStatusConverter {
+
+ public static StorageOperationStatus convertTitanStatusToStorageStatus(TitanOperationStatus titanStatus) {
+
+ if (titanStatus == null) {
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ switch (titanStatus) {
+
+ case OK:
+ return StorageOperationStatus.OK;
+
+ case NOT_CONNECTED:
+ return StorageOperationStatus.CONNECTION_FAILURE;
+
+ case NOT_FOUND:
+ return StorageOperationStatus.NOT_FOUND;
+
+ case NOT_CREATED:
+ return StorageOperationStatus.SCHEMA_ERROR;
+
+ case INDEX_CANNOT_BE_CHANGED:
+ return StorageOperationStatus.SCHEMA_ERROR;
+
+ case MISSING_UNIQUE_ID:
+ return StorageOperationStatus.BAD_REQUEST;
+ case ALREADY_LOCKED:
+ return StorageOperationStatus.FAILED_TO_LOCK_ELEMENT;
+
+ case TITAN_SCHEMA_VIOLATION:
+ return StorageOperationStatus.SCHEMA_VIOLATION;
+
+ case INVALID_ID:
+ return StorageOperationStatus.INVALID_ID;
+ case MATCH_NOT_FOUND:
+ return StorageOperationStatus.MATCH_NOT_FOUND;
+
+ case ILLEGAL_ARGUMENT:
+ return StorageOperationStatus.BAD_REQUEST;
+ // case HTTP_PROTOCOL_ERROR:
+ // return StorageOperationStatus.HTTP_PROTOCOL_ERROR;
+ // case DB_NOT_AVAILABLE:
+ // return StorageOperationStatus.STORAGE_NOT_AVAILABLE;
+ // case DB_READ_ONLY:
+ // return StorageOperationStatus.READ_ONLY_STORAGE;
+ // case BAD_REQUEST:
+ // return StorageOperationStatus.BAD_REQUEST;
+ // case LEGACY_INDEX_ERROR:
+ // return StorageOperationStatus.STORAGE_LEGACY_INDEX_ERROR;
+ // case SCHEMA_ERROR:
+ // return StorageOperationStatus.SCHEMA_ERROR;
+ // case TRANSACTION_ERROR:
+ // return StorageOperationStatus.TRANSACTION_ERROR;
+ // case EXECUTION_FAILED:
+ // return StorageOperationStatus.EXEUCTION_FAILED;
+ case ALREADY_EXIST:
+ return StorageOperationStatus.ENTITY_ALREADY_EXISTS;
+ case PROPERTY_NAME_ALREADY_EXISTS:
+ return StorageOperationStatus.PROPERTY_NAME_ALREADY_EXISTS;
+ case INVALID_PROPERTY:
+ return StorageOperationStatus.INVALID_PROPERTY;
+ // case WRONG_INPUT:
+ // return StorageOperationStatus.BAD_REQUEST;
+ // case GENERAL_ERROR:
+ // return StorageOperationStatus.GENERAL_ERROR;
+ // case NOT_SUPPORTED:
+ // return StorageOperationStatus.OPERATION_NOT_SUPPORTED;
+
+ default:
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ }
+
+ public static StorageOperationStatus convertRsrcUploadStatusToStorageStatus(
+ ResourceUploadStatus resourceUploadStatus) {
+ if (resourceUploadStatus == null) {
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ switch (resourceUploadStatus) {
+ case OK:
+ return StorageOperationStatus.OK;
+ case ALREADY_EXIST:
+ return StorageOperationStatus.ENTITY_ALREADY_EXISTS;
+ case NOT_EXIST:
+ return StorageOperationStatus.ARTIFACT_NOT_FOUND;
+ case SERVICE_NOT_EXIST:
+ case COMPONENT_NOT_EXIST:
+ return StorageOperationStatus.NOT_FOUND;
+ default:
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ }
+
+ public static StorageOperationStatus convertCassandraStatusToStorageStatus(CassandraOperationStatus status) {
+ if (status == null) {
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ switch (status) {
+ case OK:
+ return StorageOperationStatus.OK;
+ case CLUSTER_NOT_CONNECTED:
+ return StorageOperationStatus.CONNECTION_FAILURE;
+ case KEYSPACE_NOT_CONNECTED:
+ return StorageOperationStatus.STORAGE_NOT_AVAILABLE;
+ case NOT_FOUND:
+ return StorageOperationStatus.NOT_FOUND;
+
+ default:
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java
new file mode 100644
index 0000000000..248a1d0460
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ElementOperation.java
@@ -0,0 +1,902 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.DeploymentArtifactTypeConfig;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.category.CategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.category.GroupingDataDefinition;
+import org.openecomp.sdc.be.datatypes.category.SubCategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.PropertyScope;
+import org.openecomp.sdc.be.model.Tag;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.GroupingData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+//import com.tinkerpop.blueprints.Vertex;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@Component("element-operation")
+public class ElementOperation implements IElementOperation {
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ public ElementOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(ElementOperation.class.getName());
+
+ /*
+ * Old flow
+ */
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllServiceCategories() {
+ return getAllCategories(NodeTypeEnum.ServiceNewCategory, false);
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllResourceCategories() {
+ return getAllCategories(NodeTypeEnum.ResourceNewCategory, false);
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllProductCategories() {
+ return getAllCategories(NodeTypeEnum.ProductCategory, false);
+ }
+ /*
+ *
+ */
+
+ /*
+ * New flow
+ */
+ @Override
+ public Either<CategoryDefinition, ActionStatus> createCategory(CategoryDefinition category, NodeTypeEnum nodeType) {
+ return createCategory(category, nodeType, false);
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> createCategory(CategoryDefinition category, NodeTypeEnum nodeType,
+ boolean inTransaction) {
+ Either<CategoryDefinition, ActionStatus> result = null;
+ category.setUniqueId(UniqueIdBuilder.buildCategoryUid(category.getNormalizedName(), nodeType));
+ CategoryData categoryData = new CategoryData(nodeType, category);
+
+ try {
+ Either<CategoryData, TitanOperationStatus> createNode = titanGenericDao.createNode(categoryData,
+ CategoryData.class);
+ if (createNode.isRight()) {
+ TitanOperationStatus value = createNode.right().value();
+ ActionStatus actionStatus = ActionStatus.GENERAL_ERROR;
+ log.debug("Problem while creating category, reason {}", value);
+ if (value == TitanOperationStatus.TITAN_SCHEMA_VIOLATION) {
+ actionStatus = ActionStatus.COMPONENT_CATEGORY_ALREADY_EXISTS;
+ }
+ result = Either.right(actionStatus);
+ return result;
+ }
+ CategoryDefinition created = new CategoryDefinition(createNode.left().value().getCategoryDataDefinition());
+ result = Either.left(created);
+ return result;
+ } finally {
+ if (inTransaction == false) {
+ if (result != null && result.isLeft()) {
+ titanGenericDao.commit();
+ } else {
+ titanGenericDao.rollback();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> createSubCategory(String categoryId,
+ SubCategoryDefinition subCategory, NodeTypeEnum nodeType) {
+ return createSubCategory(categoryId, subCategory, nodeType, false);
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> createSubCategory(String categoryId,
+ SubCategoryDefinition subCategory, NodeTypeEnum nodeType, boolean inTransaction) {
+
+ Either<SubCategoryDefinition, ActionStatus> result = null;
+
+ try {
+ // create edge from category to sub-category
+ Either<CategoryData, TitanOperationStatus> categoryNode = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), categoryId, CategoryData.class);
+ ActionStatus actionStatus = ActionStatus.GENERAL_ERROR;
+ if (categoryNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = categoryNode.right().value();
+ log.debug("Problem while fetching category, reason {}", titanOperationStatus);
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ actionStatus = ActionStatus.COMPONENT_CATEGORY_NOT_FOUND;
+ }
+ result = Either.right(actionStatus);
+ return result;
+ }
+
+ CategoryDataDefinition categoryDataDefinition = categoryNode.left().value().getCategoryDataDefinition();
+ subCategory.setUniqueId(UniqueIdBuilder.buildSubCategoryUid(categoryDataDefinition.getUniqueId(),
+ subCategory.getNormalizedName()));
+ SubCategoryData subCategoryData = new SubCategoryData(nodeType, subCategory);
+
+ Either<SubCategoryData, TitanOperationStatus> subCategoryNode = titanGenericDao.createNode(subCategoryData,
+ SubCategoryData.class);
+ if (subCategoryNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = subCategoryNode.right().value();
+ log.debug("Problem while creating category, reason {}", titanOperationStatus);
+ if (titanOperationStatus == TitanOperationStatus.TITAN_SCHEMA_VIOLATION) {
+ actionStatus = ActionStatus.COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY;
+ }
+ result = Either.right(actionStatus);
+ return result;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> relation = titanGenericDao.createRelation(
+ categoryNode.left().value(), subCategoryNode.left().value(), GraphEdgeLabels.SUB_CATEGORY, null);
+ if (relation.isRight()) {
+ log.debug("Problem while create relation between category and sub-category ", relation.right().value());
+ result = Either.right(actionStatus);
+ return result;
+ }
+ SubCategoryDefinition subCategoryCreated = new SubCategoryDefinition(
+ subCategoryNode.left().value().getSubCategoryDataDefinition());
+ result = Either.left(subCategoryCreated);
+ return result;
+ } finally {
+ if (inTransaction == false) {
+ if (result != null && result.isLeft()) {
+ titanGenericDao.commit();
+ } else {
+ titanGenericDao.rollback();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Either<GroupingDefinition, ActionStatus> createGrouping(String subCategoryId, GroupingDefinition grouping,
+ NodeTypeEnum nodeType) {
+
+ Either<GroupingDefinition, ActionStatus> result = null;
+
+ try {
+ // create edge from sub-category to grouping
+ Either<SubCategoryData, TitanOperationStatus> subCategoryNode = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), subCategoryId, SubCategoryData.class);
+ ActionStatus actionStatus = ActionStatus.GENERAL_ERROR;
+ if (subCategoryNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = subCategoryNode.right().value();
+ log.debug("Problem while fetching category, reason {}", titanOperationStatus);
+ if (titanOperationStatus == TitanOperationStatus.TITAN_SCHEMA_VIOLATION) {
+ actionStatus = ActionStatus.COMPONENT_CATEGORY_NOT_FOUND;
+ }
+ result = Either.right(actionStatus);
+ return result;
+ }
+
+ SubCategoryDataDefinition subCatData = subCategoryNode.left().value().getSubCategoryDataDefinition();
+ grouping.setUniqueId(
+ UniqueIdBuilder.buildGroupingUid(subCatData.getUniqueId(), grouping.getNormalizedName()));
+ GroupingData groupingData = new GroupingData(nodeType, grouping);
+
+ Either<GroupingData, TitanOperationStatus> groupingNode = titanGenericDao.createNode(groupingData,
+ GroupingData.class);
+ if (groupingNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = groupingNode.right().value();
+ log.debug("Problem while creating grouping, reason {}", titanOperationStatus);
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ actionStatus = ActionStatus.COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY;
+ }
+ result = Either.right(actionStatus);
+ return result;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> relation = titanGenericDao.createRelation(
+ subCategoryNode.left().value(), groupingNode.left().value(), GraphEdgeLabels.GROUPING, null);
+ if (relation.isRight()) {
+ log.debug("Problem while create relation between sub-category and grouping", relation.right().value());
+ result = Either.right(actionStatus);
+ return result;
+ }
+ GroupingDefinition groupingCreated = new GroupingDefinition(
+ groupingNode.left().value().getGroupingDataDefinition());
+ result = Either.left(groupingCreated);
+ return result;
+ } finally {
+ if (result != null && result.isLeft()) {
+ titanGenericDao.commit();
+ } else {
+ titanGenericDao.rollback();
+ }
+ }
+ }
+
+ @Override
+ public Either<List<CategoryDefinition>, ActionStatus> getAllCategories(NodeTypeEnum nodeType,
+ boolean inTransaction) {
+ try {
+ if (nodeType != NodeTypeEnum.ResourceNewCategory && nodeType != NodeTypeEnum.ServiceNewCategory
+ && nodeType != NodeTypeEnum.ProductCategory) {
+ log.debug("Unknown category type {}", nodeType.name());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ Either<List<org.openecomp.sdc.be.resources.data.category.CategoryData>, TitanOperationStatus> either = titanGenericDao
+ .getAll(nodeType, org.openecomp.sdc.be.resources.data.category.CategoryData.class);
+ if (either.isRight() && (either.right().value() != TitanOperationStatus.NOT_FOUND)) {
+ log.debug("Problem while get all categories. reason - {}", either.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ List<CategoryData> categoryDataList = either.isLeft() ? either.left().value() : null;
+ List<CategoryDefinition> categoryList = new ArrayList<CategoryDefinition>();
+ if (categoryDataList != null) {
+ for (CategoryData elem : categoryDataList) {
+ CategoryDataDefinition categoryDataDefinition = elem.getCategoryDataDefinition();
+
+ CategoryDefinition categoryDefinition = new CategoryDefinition(categoryDataDefinition);
+ String categoryName = categoryDataDefinition.getName();
+ log.trace("Found category {}, category type {}", categoryName, nodeType);
+ TitanOperationStatus setSubCategories = setSubCategories(nodeType, categoryDefinition);
+ if (setSubCategories != TitanOperationStatus.OK) {
+ log.debug("Failed to set sub-categories for category {}, category type {}, error {}",
+ categoryName, nodeType, setSubCategories);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ categoryList.add(categoryDefinition);
+ }
+ }
+ return Either.left(categoryList);
+ } finally {
+ if (!inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ private TitanOperationStatus setSubCategories(NodeTypeEnum parentNodeType, CategoryDefinition parentCategory) {
+ NodeTypeEnum childNodeType = getChildNodeType(parentNodeType);
+ if (childNodeType != null) {
+ String categoryName = parentCategory.getName();
+ log.trace("Getting sub-categories for category {}, category type {}", categoryName, parentNodeType);
+ Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, TitanOperationStatus> parentNode = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(parentNodeType), parentCategory.getUniqueId(),
+ GraphEdgeLabels.SUB_CATEGORY, childNodeType, SubCategoryData.class);
+ if (parentNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = parentNode.right().value();
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ log.trace("Didn't find subcategories for category {}, category type {}", categoryName,
+ parentNodeType);
+ titanOperationStatus = TitanOperationStatus.OK;
+ }
+ return titanOperationStatus;
+ }
+ List<ImmutablePair<SubCategoryData, GraphEdge>> subsCategoriesData = parentNode.left().value();
+ List<SubCategoryDefinition> subCategoriesDefinitions = new ArrayList<>();
+ for (ImmutablePair<SubCategoryData, GraphEdge> subCatPair : subsCategoriesData) {
+ SubCategoryDataDefinition subCategoryDataDefinition = subCatPair.getLeft()
+ .getSubCategoryDataDefinition();
+ SubCategoryDefinition subCategoryDefinition = new SubCategoryDefinition(subCategoryDataDefinition);
+
+ log.trace("Found sub-category {} for category {}, category type {}",
+ subCategoryDataDefinition.getName(), categoryName, parentNodeType);
+ TitanOperationStatus setGroupings = setGroupings(childNodeType, subCategoryDefinition);
+ if (setGroupings != TitanOperationStatus.OK) {
+ log.debug("Failed to set groupings for sub-category {}, sub-category type {}, error {}",
+ subCategoryDataDefinition.getName(), childNodeType, setGroupings);
+ return TitanOperationStatus.GENERAL_ERROR;
+ }
+ subCategoriesDefinitions.add(subCategoryDefinition);
+ }
+ parentCategory.setSubcategories(subCategoriesDefinitions);
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus setGroupings(NodeTypeEnum parentNodeType, SubCategoryDefinition parentSubCategory) {
+ NodeTypeEnum childNodeType = getChildNodeType(parentNodeType);
+ if (childNodeType != null) {
+ String subCategoryName = parentSubCategory.getName();
+ log.trace("Getting groupings for subcategory {}, subcategory type {}", subCategoryName, parentNodeType);
+ Either<List<ImmutablePair<GroupingData, GraphEdge>>, TitanOperationStatus> parentNode = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(parentNodeType), parentSubCategory.getUniqueId(),
+ GraphEdgeLabels.GROUPING, childNodeType, GroupingData.class);
+ if (parentNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = parentNode.right().value();
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ log.trace("Didn't find groupings for subcategory {}, subcategory type {}", subCategoryName,
+ parentNodeType);
+ titanOperationStatus = TitanOperationStatus.OK;
+ }
+ return titanOperationStatus;
+ }
+ List<ImmutablePair<GroupingData, GraphEdge>> groupingData = parentNode.left().value();
+ List<GroupingDefinition> groupingDefinitions = new ArrayList<>();
+ for (ImmutablePair<GroupingData, GraphEdge> groupPair : groupingData) {
+ GroupingDataDefinition groupingDataDefinition = groupPair.getLeft().getGroupingDataDefinition();
+ log.trace("Found grouping {} for sub-category {}, sub-category type {}",
+ groupingDataDefinition.getName(), subCategoryName, parentNodeType);
+ groupingDefinitions.add(new GroupingDefinition(groupingDataDefinition));
+ }
+ parentSubCategory.setGroupings(groupingDefinitions);
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private static NodeTypeEnum getChildNodeType(NodeTypeEnum parentTypeEnum) {
+ NodeTypeEnum res = null;
+ switch (parentTypeEnum) {
+ case ResourceNewCategory:
+ res = NodeTypeEnum.ResourceSubcategory;
+ break;
+ case ProductCategory:
+ res = NodeTypeEnum.ProductSubcategory;
+ break;
+ case ProductSubcategory:
+ res = NodeTypeEnum.ProductGrouping;
+ break;
+ default:
+ break;
+ }
+ return res;
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> getCategory(NodeTypeEnum nodeType, String categoryId) {
+ try {
+ if (nodeType != NodeTypeEnum.ResourceNewCategory && nodeType != NodeTypeEnum.ServiceNewCategory
+ && nodeType != NodeTypeEnum.ProductCategory) {
+ log.debug("Unknown category type {}", nodeType.name());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ Either<CategoryData, TitanOperationStatus> categoryDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), categoryId, CategoryData.class);
+ if (categoryDataEither.isRight()) {
+ TitanOperationStatus titanOperationStatus = categoryDataEither.right().value();
+ log.debug("Problem while get category by id {}. reason {}", categoryId, titanOperationStatus);
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND);
+ }
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ CategoryDataDefinition categoryDataDefinition = categoryDataEither.left().value()
+ .getCategoryDataDefinition();
+ return Either.left(new CategoryDefinition(categoryDataDefinition));
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> getSubCategory(NodeTypeEnum nodeType, String subCategoryId) {
+ try {
+ if (nodeType != NodeTypeEnum.ResourceSubcategory && nodeType != NodeTypeEnum.ProductSubcategory) {
+ log.debug("Unknown sub-category type {}", nodeType.name());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ Either<SubCategoryData, TitanOperationStatus> subCategoryDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), subCategoryId, SubCategoryData.class);
+ if (subCategoryDataEither.isRight()) {
+ TitanOperationStatus titanOperationStatus = subCategoryDataEither.right().value();
+ log.debug("Problem while get sub-category by id {}. reason {}", subCategoryId, titanOperationStatus);
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(ActionStatus.COMPONENT_CATEGORY_NOT_FOUND);
+ }
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ SubCategoryDataDefinition subCategoryDataDefinition = subCategoryDataEither.left().value()
+ .getSubCategoryDataDefinition();
+ return Either.left(new SubCategoryDefinition(subCategoryDataDefinition));
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<CategoryDefinition, ActionStatus> deleteCategory(NodeTypeEnum nodeType, String categoryId) {
+ Either<CategoryDefinition, ActionStatus> result = null;
+ try {
+ if (nodeType != NodeTypeEnum.ResourceNewCategory && nodeType != NodeTypeEnum.ServiceNewCategory
+ && nodeType != NodeTypeEnum.ProductCategory) {
+ log.debug("Unknown category type {}", nodeType.name());
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+ Either<CategoryData, TitanOperationStatus> categoryDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), categoryId, CategoryData.class);
+ if (categoryDataEither.isRight()) {
+ log.debug("Failed to retrieve category for id {} ", categoryId);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Couldn't fetch titan graph");
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+
+ TitanGraph tGraph = graph.left().value();
+
+ Iterable<TitanVertex> verticesArtifact = tGraph.query()
+ .has(UniqueIdBuilder.getKeyByNodeType(nodeType), categoryId).vertices();
+ Iterator<TitanVertex> iterator = verticesArtifact.iterator();
+ if (!iterator.hasNext()) {
+ log.debug("No category node for id = {}", categoryId);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+ Vertex artifactV = iterator.next();
+ artifactV.remove();
+ CategoryDefinition deleted = new CategoryDefinition(
+ categoryDataEither.left().value().getCategoryDataDefinition());
+ result = Either.left(deleted);
+ return result;
+ } finally {
+ if (result != null && result.isLeft()) {
+ titanGenericDao.commit();
+ } else {
+ titanGenericDao.rollback();
+ }
+ }
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> deleteSubCategory(NodeTypeEnum nodeType, String subCategoryId) {
+ Either<SubCategoryDefinition, ActionStatus> result = null;
+ try {
+ if (nodeType != NodeTypeEnum.ResourceSubcategory && nodeType != NodeTypeEnum.ProductSubcategory) {
+ log.debug("Unknown sub-category type {}", nodeType.name());
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+ Either<SubCategoryData, TitanOperationStatus> subCategoryDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), subCategoryId, SubCategoryData.class);
+ if (subCategoryDataEither.isRight()) {
+ log.debug("Failed to retrieve sub-category for id {}", subCategoryId);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Couldn't fetch titan graph");
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+
+ TitanGraph tGraph = graph.left().value();
+
+ Iterable<TitanVertex> verticesArtifact = tGraph.query()
+ .has(UniqueIdBuilder.getKeyByNodeType(nodeType), subCategoryId).vertices();
+ Iterator<TitanVertex> iterator = verticesArtifact.iterator();
+ if (!iterator.hasNext()) {
+ log.debug("No sub-category node for id {}", subCategoryId);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+ Vertex artifactV = iterator.next();
+ artifactV.remove();
+ ;
+ SubCategoryDefinition deleted = new SubCategoryDefinition(
+ subCategoryDataEither.left().value().getSubCategoryDataDefinition());
+ result = Either.left(deleted);
+ return result;
+ } finally {
+ if (result != null && result.isLeft()) {
+ titanGenericDao.commit();
+ } else {
+ titanGenericDao.rollback();
+ }
+ }
+
+ }
+
+ @Override
+ public Either<GroupingDefinition, ActionStatus> deleteGrouping(NodeTypeEnum nodeType, String groupingId) {
+ Either<GroupingDefinition, ActionStatus> result = null;
+ try {
+ if (nodeType != NodeTypeEnum.ProductGrouping) {
+ log.debug("Unknown grouping type {}", nodeType.name());
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+ Either<GroupingData, TitanOperationStatus> groupingDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), groupingId, GroupingData.class);
+ if (groupingDataEither.isRight()) {
+ log.debug("Failed to retrieve grouping for id {}", groupingId);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+
+ Either<TitanGraph, TitanOperationStatus> graph = titanGenericDao.getGraph();
+ if (graph.isRight()) {
+ log.debug("Couldn't fetch titan graph");
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+
+ TitanGraph tGraph = graph.left().value();
+
+ Iterable<TitanVertex> verticesArtifact = tGraph.query()
+ .has(UniqueIdBuilder.getKeyByNodeType(nodeType), groupingId).vertices();
+ Iterator<TitanVertex> iterator = verticesArtifact.iterator();
+ if (!iterator.hasNext()) {
+ log.debug("No grouping node for id {}", groupingId);
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ return result;
+ }
+ Vertex artifactV = iterator.next();
+ artifactV.remove();
+ ;
+ GroupingDefinition deleted = new GroupingDefinition(
+ groupingDataEither.left().value().getGroupingDataDefinition());
+ result = Either.left(deleted);
+ return result;
+ } finally {
+ if (result != null && result.isLeft()) {
+ titanGenericDao.commit();
+ } else {
+ titanGenericDao.rollback();
+ }
+ }
+ }
+
+ @Override
+ public Either<Boolean, ActionStatus> isCategoryUniqueForType(NodeTypeEnum nodeType, String normalizedName) {
+
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), normalizedName);
+ try {
+ Either<List<CategoryData>, TitanOperationStatus> categoryEither = titanGenericDao.getByCriteria(nodeType,
+ properties, CategoryData.class);
+ if (categoryEither.isRight() && categoryEither.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to get categories, nodeType {}, normalizedName {}, error {}", nodeType,
+ normalizedName, categoryEither.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ List<CategoryData> categoryList = (categoryEither.isLeft() ? categoryEither.left().value() : null);
+ if (categoryList != null && categoryList.size() > 0) {
+ log.debug("Found category for nodeType {} with normalizedName {}", nodeType, normalizedName);
+ if (categoryList.size() > 1) {
+ log.debug("Found more than 1 unique categories for nodeType {} with normalizedName", nodeType,
+ normalizedName);
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ return Either.left(false);
+ } else {
+ log.debug("Category for nodeType {} with normalizedName {} doesn't exist in graph", nodeType,
+ normalizedName);
+ return Either.left(true);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<Boolean, ActionStatus> isSubCategoryUniqueForCategory(NodeTypeEnum nodeType,
+ String subCategoryNormName, String parentCategoryId) {
+
+ String subCategoryId = UniqueIdBuilder.buildSubCategoryUid(parentCategoryId, subCategoryNormName);
+ try {
+ Either<SubCategoryData, TitanOperationStatus> subCategoryDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), subCategoryId, SubCategoryData.class);
+ if (subCategoryDataEither.isRight()
+ && subCategoryDataEither.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to get sub-category with id {}, error {}", subCategoryId,
+ subCategoryDataEither.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ SubCategoryData subCategoryData = (subCategoryDataEither.isLeft() ? subCategoryDataEither.left().value()
+ : null);
+ if (subCategoryData != null) {
+ log.debug("Found sub-category with id {}", subCategoryId);
+ return Either.left(false);
+ } else {
+ log.debug("Sub-category for id {} doesn't exist in graph", subCategoryId);
+ return Either.left(true);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<Boolean, ActionStatus> isGroupingUniqueForSubCategory(NodeTypeEnum nodeType, String groupingNormName,
+ String parentSubCategoryId) {
+
+ String groupingId = UniqueIdBuilder.buildGroupingUid(parentSubCategoryId, groupingNormName);
+ try {
+ Either<GroupingData, TitanOperationStatus> groupingDataEither = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), groupingId, GroupingData.class);
+ if (groupingDataEither.isRight() && groupingDataEither.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to get grouping with id {}, error {}", groupingId,
+ groupingDataEither.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ GroupingData groupingData = (groupingDataEither.isLeft() ? groupingDataEither.left().value() : null);
+ if (groupingData != null) {
+ log.debug("Found grouping with id {}", groupingId);
+ return Either.left(false);
+ } else {
+ log.debug("Grouping for id {} doesn't exist in graph", groupingId);
+ return Either.left(true);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<SubCategoryDefinition, ActionStatus> getSubCategoryUniqueForType(NodeTypeEnum nodeType,
+ String normalizedName) {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), normalizedName);
+ try {
+ Either<List<SubCategoryData>, TitanOperationStatus> subCategoryEither = titanGenericDao
+ .getByCriteria(nodeType, properties, SubCategoryData.class);
+ if (subCategoryEither.isRight() && subCategoryEither.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to get sub-categories, nodeType {}, normalizedName {}, error {}", nodeType,
+ normalizedName, subCategoryEither.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ List<SubCategoryData> subCategoryList = (subCategoryEither.isLeft() ? subCategoryEither.left().value()
+ : null);
+ if (subCategoryList != null && subCategoryList.size() > 0) {
+ log.debug("Found sub-category for nodeType {} with normalizedName {}", nodeType, normalizedName);
+ SubCategoryData subCategoryData = subCategoryList.get(0);
+ SubCategoryDefinition subCategoryDefinition = new SubCategoryDefinition(
+ subCategoryData.getSubCategoryDataDefinition());
+ return Either.left(subCategoryDefinition);
+ } else {
+ log.debug("Sub-category for nodeType {} with normalizedName {} doesn't exist in graph", nodeType,
+ normalizedName);
+ return Either.left(null);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public Either<GroupingDefinition, ActionStatus> getGroupingUniqueForType(NodeTypeEnum nodeType,
+ String groupingNormalizedName) {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), groupingNormalizedName);
+ try {
+ Either<List<GroupingData>, TitanOperationStatus> groupingEither = titanGenericDao.getByCriteria(nodeType,
+ properties, GroupingData.class);
+ if (groupingEither.isRight() && groupingEither.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Failed to get grouping, nodeType {}, normalizedName {}, error {}", nodeType,
+ groupingNormalizedName, groupingEither.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ List<GroupingData> groupingList = (groupingEither.isLeft() ? groupingEither.left().value() : null);
+ if (groupingList != null && groupingList.size() > 0) {
+ log.debug("Found grouping for nodeType {} with normalizedName {}", nodeType, groupingNormalizedName);
+ GroupingData groupingData = groupingList.get(0);
+ GroupingDefinition groupingDefinition = new GroupingDefinition(
+ groupingData.getGroupingDataDefinition());
+ return Either.left(groupingDefinition);
+ } else {
+ log.debug("Grouping for nodeType {} with normalizedName {} doesn't exist in graph", nodeType,
+ groupingNormalizedName);
+ return Either.left(null);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ /*
+ *
+ */
+
+ @Override
+ public Either<List<Tag>, ActionStatus> getAllTags() {
+ try {
+ Either<List<TagData>, TitanOperationStatus> either = titanGenericDao.getAll(NodeTypeEnum.Tag,
+ TagData.class);
+ if (either.isRight()) {
+ log.debug("Problem while get all tags. reason - {}", either.right().value());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ List<TagData> tagDataList = either.left().value();
+ List<Tag> tagList = convertToListOfTag(tagDataList);
+ return Either.left(tagList);
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ @Override
+ public <T extends GraphNode> Either<org.openecomp.sdc.be.resources.data.CategoryData, StorageOperationStatus> getCategoryData(
+ String name, NodeTypeEnum type, Class<T> clazz) {
+ if (name != null) {
+ String categoryUid = null;
+ if (type == NodeTypeEnum.ResourceCategory) {
+ String[] categoryFields = name.split("/");
+ if (categoryFields.length != 2) {
+ return Either.right(StorageOperationStatus.CATEGORY_NOT_FOUND);
+ }
+ categoryUid = UniqueIdBuilder.buildResourceCategoryUid(categoryFields[0], categoryFields[1], type);
+ } else {
+ categoryUid = UniqueIdBuilder.buildServiceCategoryUid(name, type);
+ }
+ Either<T, TitanOperationStatus> either = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(type),
+ categoryUid, clazz);
+
+ if (either.isRight()) {
+ TitanOperationStatus titanOperationStatus = either.right().value();
+ log.debug("Problem while geting category with id {}. reason - {}", categoryUid, titanOperationStatus.name());
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(StorageOperationStatus.CATEGORY_NOT_FOUND);
+ } else {
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+ return Either.left((org.openecomp.sdc.be.resources.data.CategoryData) either.left().value());
+ } else {
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+ private List<Tag> convertToListOfTag(List<TagData> tagDataList) {
+ List<Tag> tagList = new ArrayList<Tag>();
+ for (TagData elem : tagDataList) {
+ Tag tag = new Tag();
+ tag.setName(elem.getName());
+ tagList.add(tag);
+ }
+ return tagList;
+ }
+
+ @Override
+ public Either<List<PropertyScope>, ActionStatus> getAllPropertyScopes() {
+ // Mock
+ List<PropertyScope> propertyScopes = new ArrayList<PropertyScope>();
+ PropertyScope propertyScope1 = new PropertyScope();
+ propertyScope1.setName("A&AI");
+ PropertyScope propertyScope2 = new PropertyScope();
+ propertyScope2.setName("Order");
+ PropertyScope propertyScope3 = new PropertyScope();
+ propertyScope3.setName("Runtime");
+ propertyScopes.add(propertyScope1);
+ propertyScopes.add(propertyScope2);
+ propertyScopes.add(propertyScope3);
+ return Either.left(propertyScopes);
+ }
+
+ @Override
+ public Either<List<ArtifactType>, ActionStatus> getAllArtifactTypes() {
+ List<ArtifactType> artifactTypes = new ArrayList<ArtifactType>();
+
+ List<String> artifactTypesList = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getArtifactTypes();
+ for (String artifactType : artifactTypesList) {
+ ArtifactType artifactT = new ArtifactType();
+ artifactT.setName(artifactType);
+ artifactTypes.add(artifactT);
+ }
+ return Either.left(artifactTypes);
+ }
+
+ @Override
+ public Either<Map<String, Object>, ActionStatus> getAllDeploymentArtifactTypes() {
+
+ Map<String, Object> artifactTypes = new HashMap<String, Object>();
+ Map<String, DeploymentArtifactTypeConfig> artifactResourceTypes = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getResourceDeploymentArtifacts();
+ Map<String, DeploymentArtifactTypeConfig> artifactServiceTypes = ConfigurationManager.getConfigurationManager()
+ .getConfiguration().getServiceDeploymentArtifacts();
+ Map<String, DeploymentArtifactTypeConfig> artifactResourceInstanceTypes = ConfigurationManager
+ .getConfigurationManager().getConfiguration().getResourceInstanceDeploymentArtifacts();
+
+ artifactTypes.put("resourceDeploymentArtifacts", artifactResourceTypes);
+ artifactTypes.put("serviceDeploymentArtifacts", artifactServiceTypes);
+ artifactTypes.put("resourceInstanceDeploymentArtifacts", artifactResourceInstanceTypes);
+
+ return Either.left(artifactTypes);
+
+ }
+
+ @Override
+ public Either<Integer, ActionStatus> getDefaultHeatTimeout() {
+ return Either.left(ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getDefaultHeatArtifactTimeoutMinutes());
+ }
+
+ @Override
+ public Either<Map<String, String>, ActionStatus> getResourceTypesMap() {
+ ResourceTypeEnum[] enumConstants = ResourceTypeEnum.class.getEnumConstants();
+ Map<String, String> resourceTypes = new HashMap<String, String>();
+ if (enumConstants != null) {
+ for (int i = 0; i < enumConstants.length; ++i) {
+ resourceTypes.put(enumConstants[i].name(), enumConstants[i].getValue());
+ }
+
+ }
+ return Either.left(resourceTypes);
+ }
+
+ @Override
+ public <T extends GraphNode> Either<CategoryData, StorageOperationStatus> getNewCategoryData(String name,
+ NodeTypeEnum type, Class<T> clazz) {
+ if (name != null) {
+ String categoryUid = UniqueIdBuilder.buildServiceCategoryUid(name, type);
+ Map props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), name);
+ Either<List<T>, TitanOperationStatus> either = titanGenericDao.getByCriteria(type, props, clazz);
+
+ if (either.isRight()) {
+ TitanOperationStatus titanOperationStatus = either.right().value();
+ log.debug("Problem while geting category with id {}. reason - {}", categoryUid, titanOperationStatus.name());
+ if (titanOperationStatus == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(StorageOperationStatus.CATEGORY_NOT_FOUND);
+ } else {
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+ return Either.left((CategoryData) either.left().value().get(0));
+ } else {
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GraphLockOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GraphLockOperation.java
new file mode 100644
index 0000000000..35541e6d46
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GraphLockOperation.java
@@ -0,0 +1,234 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.ICacheMangerOperation;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("graph-lock-operation")
+public class GraphLockOperation implements IGraphLockOperation {
+ private static Logger log = LoggerFactory.getLogger(ResourceOperation.class.getName());
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ @javax.annotation.Resource
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ private ICacheMangerOperation cacheManagerOperation;
+
+ public GraphLockOperation() {
+ super();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.model.operations.impl.IGraphLockOperation#
+ * lockResource(java.lang.String,
+ * org.openecomp.sdc.be.model.operations.api.IResourceOperation)
+ */
+ @Override
+ public StorageOperationStatus lockComponent(String componentId, NodeTypeEnum nodeType) {
+ log.info("lock resource with id {}", componentId);
+ TitanOperationStatus lockElementStatus = null;
+ try {
+
+ // SET LAST UPDATE DATE OF THE COMPONENT.
+ // In this way we mark the component as updated one (and component
+ // won't be fetched from cache since the component in cache has
+ // different timestamp)
+ Either<ComponentMetadataData, TitanOperationStatus> updateTime = updateModificationTimeOfComponent(
+ componentId, nodeType);
+ if (updateTime.isRight()) {
+ TitanOperationStatus operationStatus = updateTime.right().value();
+ if (operationStatus != TitanOperationStatus.OK) {
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(operationStatus);
+ }
+ }
+
+ lockElementStatus = titanGenericDao.lockElement(componentId, nodeType);
+
+ } catch (Exception e) {
+ lockElementStatus = TitanOperationStatus.ALREADY_LOCKED;
+
+ }
+
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(lockElementStatus);
+
+ }
+
+ /**
+ * update the last update date of the component
+ *
+ * @param componentId
+ * @param nodeType
+ * @return
+ */
+ private Either<ComponentMetadataData, TitanOperationStatus> updateModificationTimeOfComponent(String componentId,
+ NodeTypeEnum nodeType) {
+
+ if (nodeType == NodeTypeEnum.Resource || nodeType == NodeTypeEnum.Service || nodeType == NodeTypeEnum.Product) {
+ // We fetch all node since update only timestamp make problems since
+ // there is default resource type (VFC) which changes component
+ // resource type when we update only timestamp(ResourceMetadataData
+ // contains default value VFC on resourceType field).
+ Either<ComponentMetadataData, TitanOperationStatus> findComp = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(nodeType), componentId, ComponentMetadataData.class);
+
+ if (findComp.isRight()) {
+ return Either.right(findComp.right().value());
+ }
+ ComponentMetadataData componentMetadataData = findComp.left().value();
+ componentMetadataData.getMetadataDataDefinition().setLastUpdateDate(System.currentTimeMillis());
+ Either<ComponentMetadataData, TitanOperationStatus> updateNode = titanGenericDao
+ .updateNode(componentMetadataData, ComponentMetadataData.class);
+ return updateNode;
+ }
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.model.operations.impl.IGraphLockOperation#
+ * unlockResource(java.lang.String,
+ * org.openecomp.sdc.be.model.operations.api.IResourceOperation)
+ */
+ @Override
+ public StorageOperationStatus unlockComponent(String componentId, NodeTypeEnum nodeType) {
+
+ Either<Long, StorageOperationStatus> addComponentToCachePart1 = addComponentToCachePart1WithoutCommit(
+ componentId, nodeType);
+
+ TitanOperationStatus lockElementStatus = titanGenericDao.releaseElement(componentId, nodeType);
+
+ if (addComponentToCachePart1.isLeft()) {
+ Long lastUpdateDate = addComponentToCachePart1.left().value();
+ addComponentToCachePart2(componentId, lastUpdateDate, nodeType);
+ }
+
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(lockElementStatus);
+ }
+
+ private ResourceMetadataData getResourceMetaDataFromResource(Resource resource) {
+ ResourceMetadataData resourceData = new ResourceMetadataData((ResourceMetadataDataDefinition) resource.getComponentMetadataDefinition().getMetadataDataDefinition());
+ return resourceData;
+ }
+
+ @Override
+ public StorageOperationStatus unlockComponentByName(String name, String componentId, NodeTypeEnum nodeType) {
+
+ Either<Long, StorageOperationStatus> addComponentToCachePart1 = addComponentToCachePart1WithoutCommit(
+ componentId, nodeType);
+
+ TitanOperationStatus lockElementStatus = titanGenericDao.releaseElement(name, nodeType);
+
+ if (addComponentToCachePart1.isLeft()) {
+ Long lastUpdateDate = addComponentToCachePart1.left().value();
+ addComponentToCachePart2(componentId, lastUpdateDate, nodeType);
+ }
+
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(lockElementStatus);
+ }
+
+ /**
+ * We fetch the last update date of the component
+ *
+ * @param componentId
+ * @param nodeType
+ * @return
+ */
+ private Either<Long, StorageOperationStatus> addComponentToCachePart1WithoutCommit(String componentId,
+ NodeTypeEnum nodeType) {
+ if (componentId != null) { // In case of error, the componentId might be
+ // empty.
+ if (nodeType == NodeTypeEnum.Resource || nodeType == NodeTypeEnum.Service
+ || nodeType == NodeTypeEnum.Product) {
+ Long lastUpdateDate = null;
+ Either<ComponentMetadataData, StorageOperationStatus> resResult = resourceOperation
+ .getComponentByLabelAndId(componentId, nodeType, ComponentMetadataData.class);
+ if (resResult.isLeft()) {
+ ComponentMetadataData resourceMetadataData = resResult.left().value();
+ lastUpdateDate = resourceMetadataData.getMetadataDataDefinition().getLastUpdateDate();
+
+ return Either.left(lastUpdateDate);
+ } else {
+ return Either.right(resResult.right().value());
+ }
+ }
+ }
+ return Either.right(StorageOperationStatus.OPERATION_NOT_SUPPORTED);
+ }
+
+ /**
+ * add the component to the cache
+ *
+ * @param componentId
+ * @param lastUpdateDate
+ * @param nodeType
+ * @return
+ */
+ private Either<Long, StorageOperationStatus> addComponentToCachePart2(String componentId, Long lastUpdateDate,
+ NodeTypeEnum nodeType) {
+ if (componentId != null) { // In case of error, the componentId might be
+ // empty.
+ if (nodeType == NodeTypeEnum.Resource || nodeType == NodeTypeEnum.Service
+ || nodeType == NodeTypeEnum.Product) {
+ // add task to Q
+ log.debug("Going to add component {} of type {} to cache", componentId, nodeType.name().toLowerCase());
+ cacheManagerOperation.updateComponentInCache(componentId, lastUpdateDate, nodeType);
+ }
+ }
+ return Either.right(StorageOperationStatus.OPERATION_NOT_SUPPORTED);
+ }
+
+ @Override
+ public StorageOperationStatus lockComponentByName(String name, NodeTypeEnum nodeType) {
+ log.info("lock resource with name {}", name);
+ TitanOperationStatus lockElementStatus = null;
+ try {
+
+ lockElementStatus = titanGenericDao.lockElement(name, nodeType);
+
+ } catch (Exception e) {
+ lockElementStatus = TitanOperationStatus.ALREADY_LOCKED;
+
+ }
+
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(lockElementStatus);
+
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupOperation.java
new file mode 100644
index 0000000000..9312be45c1
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupOperation.java
@@ -0,0 +1,2093 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.GroupProperty;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.GroupData;
+import org.openecomp.sdc.be.resources.data.GroupTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("group-operation")
+public class GroupOperation extends AbstractOperation implements IGroupOperation {
+
+ private static String ADDING_GROUP = "AddingGroup";
+ private static String DELETING_GROUP = "DeletingGroup";
+ private static String DELETING_ALL_GROUPS = "DeletingAllGroups";
+ private static String ASSOCIATING_GROUP_TO_COMP_INST = "AssociatingGroupToComponentInstance";
+
+ private static Logger log = LoggerFactory.getLogger(GroupOperation.class.getName());
+
+ @javax.annotation.Resource
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource
+ private GroupTypeOperation groupTypeOperation;
+
+ @javax.annotation.Resource
+ private ApplicationDataTypeCache dataTypeCache;
+
+ @Override
+ public Either<GroupData, TitanOperationStatus> addGroupToGraph(NodeTypeEnum nodeTypeEnum, String componentId,
+ GroupDefinition groupDefinition) {
+
+ String groupTypeUid = groupDefinition.getTypeUid();
+
+ if (groupTypeUid == null) {
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, "Group type id is empty",
+ ErrorSeverity.ERROR);
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ ComponentMetadataData metaData = null;
+ if (nodeTypeEnum == NodeTypeEnum.Resource) {
+ metaData = new ResourceMetadataData();
+ } else {
+ metaData = new ServiceMetadataData();
+ }
+ metaData.getMetadataDataDefinition().setUniqueId(componentId);
+
+ groupDefinition.setUniqueId(UniqueIdBuilder.buildGroupUniqueId(componentId, groupDefinition.getName()));
+
+ int propertiesSize = groupDefinition.getProperties() == null ? 0 : groupDefinition.getProperties().size();
+ groupDefinition.setPropertyValueCounter(propertiesSize);
+
+ GroupData groupData = new GroupData(groupDefinition);
+
+ TitanOperationStatus status = null;
+ // Adding group data node to graph
+ log.debug("Before adding group to graph {}", groupData.toString());
+ Either<GroupData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(groupData,
+ GroupData.class);
+ log.debug("After adding group to graph {}", groupData.toString());
+ if (createNodeResult.isRight()) {
+ status = createNodeResult.right().value();
+ log.error("Failed to add group {} to graph. Status is {}", groupDefinition.getName(), status);
+ return Either.right(status);
+ }
+
+ // Associate group to group type
+ log.debug("Going to associate group {} to its groupType {}", groupDefinition.getName(), groupDefinition.getType());
+ Either<GraphRelation, TitanOperationStatus> associateGroupTypeRes = associateGroupToGroupType(groupData,
+ groupTypeUid);
+ log.debug("After associating group {} to its groupType {}. Status is {}", groupDefinition.getName(), groupDefinition.getType(), associateGroupTypeRes);
+ if (associateGroupTypeRes.isRight()) {
+ status = associateGroupTypeRes.right().value();
+ String description = "Failed to associate group " + groupDefinition.getName() + " to its groupType "
+ + groupDefinition.getType() + " in graph.";
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+
+ // Associate group to component RESOURCE/SERVICE/PRODUCT
+ Either<GraphRelation, TitanOperationStatus> associateComponentRes = associateGroupToComponent(groupData,
+ nodeTypeEnum, componentId);
+ if (associateComponentRes.isRight()) {
+ status = associateComponentRes.right().value();
+ String description = "Failed to associate group " + groupDefinition.getName() + " to "
+ + nodeTypeEnum.getName() + " " + componentId + ". status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+
+ Either<GroupTypeDefinition, TitanOperationStatus> groupTypeRes = groupTypeOperation
+ .getGroupTypeByUid(groupDefinition.getTypeUid());
+ if (groupTypeRes.isRight()) {
+ TitanOperationStatus operationStatus = groupTypeRes.right().value();
+ log.debug("Failed to find group type {}", groupDefinition.getTypeUid());
+ if (operationStatus == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ }
+ GroupTypeDefinition groupTypeDefinition = groupTypeRes.left().value();
+ // 1. find properties from group type
+ List<PropertyDefinition> groupTypeProperties = groupTypeDefinition.getProperties();
+
+ // 2. check the properties exists in the group type.
+ // 3. add parent unique id to the properties
+ // 4. add node per group property which the group point to it and it
+ // points to the parent unique id
+
+ // Adding properties to group
+ List<GroupProperty> properties = groupDefinition.getProperties();
+
+ if (properties != null && false == properties.isEmpty()) {
+
+ if (groupTypeProperties == null || true == groupTypeProperties.isEmpty()) {
+ BeEcompErrorManager.getInstance().logInvalidInputError(ADDING_GROUP,
+ "group type does not have properties", ErrorSeverity.INFO);
+ return Either.right(TitanOperationStatus.MATCH_NOT_FOUND);
+ }
+
+ Map<String, PropertyDefinition> groupTypePropertiesMap = groupTypeProperties.stream()
+ .collect(Collectors.toMap(p -> p.getName(), p -> p));
+
+ Either<PropertyValueData, TitanOperationStatus> addPropertyResult = null;
+ int i = 1;
+ for (GroupProperty prop : properties) {
+ addPropertyResult = addPropertyToGroup(groupData, prop, groupTypePropertiesMap.get(prop.getName()), i);
+ if (addPropertyResult.isRight()) {
+ status = addPropertyResult.right().value();
+ String description = "Failed to associate group " + groupData.getUniqueId() + " to property "
+ + prop.getName() + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description,
+ ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ i++;
+ }
+ }
+
+ // Associate artifacts to group
+ List<String> artifacts = groupDefinition.getArtifacts();
+
+ Either<GroupDefinition, TitanOperationStatus> associateArtifactsToGroupOnGraph = associateArtifactsToGroupOnGraph(
+ groupData.getGroupDataDefinition().getUniqueId(), artifacts);
+ if (associateArtifactsToGroupOnGraph.isRight()
+ && associateArtifactsToGroupOnGraph.right().value() != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+ /*
+ * Either<GraphRelation, TitanOperationStatus> addArtifactsRefResult =
+ * null; if (artifacts != null) { for (String artifactId : artifacts) {
+ * Either<ArtifactData, TitanOperationStatus> findArtifactRes =
+ * titanGenericDao .getNode(UniqueIdBuilder
+ * .getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId,
+ * ArtifactData.class); if (findArtifactRes.isRight()) { status =
+ * findArtifactRes.right().value(); if (status ==
+ * TitanOperationStatus.NOT_FOUND) { status =
+ * TitanOperationStatus.INVALID_ID; } String description =
+ * "Failed to associate group " + groupData.getUniqueId() +
+ * " to artifact " + artifactId + " in graph. Status is " + status;
+ * BeEcompErrorManager.getInstance().logInternalFlowError( ADDING_GROUP,
+ * description, ErrorSeverity.ERROR); return Either.right(status); }
+ *
+ * Map<String, Object> props = new HashMap<String, Object>();
+ * props.put(GraphPropertiesDictionary.NAME.getProperty(),
+ * findArtifactRes.left().value().getLabel());
+ *
+ * addArtifactsRefResult = titanGenericDao.createRelation( groupData,
+ * findArtifactRes.left().value(), GraphEdgeLabels.GROUP_ARTIFACT_REF,
+ * props);
+ *
+ * if (addArtifactsRefResult.isRight()) { status =
+ * addArtifactsRefResult.right().value(); String description =
+ * "Failed to associate group " + groupData.getUniqueId() +
+ * " to artifact " + artifactId + " in graph. Status is " + status;
+ * BeEcompErrorManager.getInstance().logInternalFlowError( ADDING_GROUP,
+ * description, ErrorSeverity.ERROR); return Either.right(status); } } }
+ */
+
+ // Associate group to members
+ // map of componentInstances <name: uniqueId>
+ Map<String, String> members = groupDefinition.getMembers();
+
+ if (members != null && false == members.isEmpty()) {
+ Either<GraphRelation, TitanOperationStatus> addMembersRefResult = null;
+ for (Entry<String, String> member : members.entrySet()) {
+ if (member.getValue() == null || member.getValue().isEmpty()) {
+ continue;
+ }
+ Either<ComponentInstanceData, TitanOperationStatus> findComponentInstanceRes = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), member.getValue(),
+ ComponentInstanceData.class);
+ if (findComponentInstanceRes.isRight()) {
+ status = findComponentInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ String description = "Failed to find to find member of group " + member.getValue()
+ + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description,
+ ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), member.getKey());
+ addMembersRefResult = titanGenericDao.createRelation(groupData, findComponentInstanceRes.left().value(),
+ GraphEdgeLabels.GROUP_MEMBER, props);
+
+ if (addMembersRefResult.isRight()) {
+ status = addMembersRefResult.right().value();
+ String description = "Failed to associate group " + groupData.getUniqueId()
+ + " to component instance " + member.getValue() + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description,
+ ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ }
+ }
+
+ return Either.left(groupData);
+ }
+
+ private Either<PropertyValueData, TitanOperationStatus> addPropertyToGroup(GroupData groupData,
+ GroupProperty groupProperty, PropertyDefinition prop, Integer index) {
+
+ if (prop == null) {
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+
+ String propertyId = prop.getUniqueId();
+ Either<PropertyData, TitanOperationStatus> findPropertyDefRes = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+
+ if (findPropertyDefRes.isRight()) {
+ TitanOperationStatus status = findPropertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ PropertyData propertyData = findPropertyDefRes.left().value();
+
+ PropertyDataDefinition propDataDef = propertyData.getPropertyDataDefinition();
+ String propertyType = propDataDef.getType();
+ String value = groupProperty.getValue();
+
+ Either<String, TitanOperationStatus> checkInnerType = propertyOperation.checkInnerType(propDataDef);
+ if (checkInnerType.isRight()) {
+ TitanOperationStatus status = checkInnerType.right().value();
+ return Either.right(status);
+ }
+
+ String innerType = checkInnerType.left().value();
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ BeEcompErrorManager.getInstance().logInternalFlowError("AddPropertyToGroup",
+ "Failed to add property to group. Status is " + status, ErrorSeverity.ERROR);
+ return Either.right(status);
+
+ }
+
+ log.debug("Before validateAndUpdatePropertyValue");
+ Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value,
+ innerType, allDataTypes.left().value());
+ log.debug("After validateAndUpdatePropertyValue. isValid = {}", isValid);
+
+ String newValue = value;
+ if (isValid.isRight()) {
+ Boolean res = isValid.right().value();
+ if (res == false) {
+ return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
+ }
+ } else {
+ Object object = isValid.left().value();
+ if (object != null) {
+ newValue = object.toString();
+ }
+ }
+
+ String uniqueId = UniqueIdBuilder.buildGroupPropertyValueUid((String) groupData.getUniqueId(), index);
+ PropertyValueData propertyValueData = new PropertyValueData();
+ propertyValueData.setUniqueId(uniqueId);
+ propertyValueData.setValue(newValue);
+
+ log.debug("Before adding property value to graph {}",propertyValueData);
+ Either<PropertyValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyValueData,
+ PropertyValueData.class);
+ log.debug("After adding property value to graph {}", propertyValueData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ return Either.right(operationStatus);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(propertyValueData,
+ propertyData, GraphEdgeLabels.PROPERTY_IMPL, null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ String description = "Failed to associate property value " + uniqueId + " to property " + propertyId
+ + " in graph. status is " + operationStatus;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(operationStatus);
+ }
+
+ createRelResult = titanGenericDao.createRelation(groupData, propertyValueData, GraphEdgeLabels.PROPERTY_VALUE,
+ null);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ String description = "Failed to associate group " + groupData.getGroupDataDefinition().getName()
+ + " to property value " + uniqueId + " in graph. Status is " + operationStatus;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateGroupToComponent(GroupData groupData,
+ NodeTypeEnum nodeTypeEnum, String componentId) {
+ UniqueIdData componentIdData = new UniqueIdData(nodeTypeEnum, componentId);
+
+ log.debug("Before associating component {} to group {}.", componentId, groupData);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), groupData.getGroupDataDefinition().getName());
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(componentIdData,
+ groupData, GraphEdgeLabels.GROUP, props);
+ log.debug("After associating component {} to group {}. Status is {}", componentId, groupData, createRelResult);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.debug("Failed to associate component {} to group {} in graph. Status is {}", componentId, groupData, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createRelResult.left().value());
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateGroupToGroupType(GroupData groupData,
+ String groupTypeUid) {
+
+ UniqueIdData groupTypeIdData = new UniqueIdData(NodeTypeEnum.GroupType, groupTypeUid);
+
+ log.debug("Before associating {} to group type {} (uid = {}).", groupData, groupData.getGroupDataDefinition().getType(), groupTypeUid);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(groupData,
+ groupTypeIdData, GraphEdgeLabels.TYPE_OF, null);
+
+ if (log.isDebugEnabled()) {
+ log.debug("After associating {} to group type {} (uid = {}). Result is {}", groupData, groupData.getGroupDataDefinition().getType(), groupTypeUid, createRelResult);
+ }
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ return Either.right(operationStatus);
+ }
+ return createRelResult;
+ }
+
+ @Override
+ public Either<GroupDefinition, StorageOperationStatus> addGroup(NodeTypeEnum nodeTypeEnum, String componentId,
+ GroupDefinition groupDefinition) {
+ return addGroup(nodeTypeEnum, componentId, groupDefinition, false);
+ }
+
+ @Override
+ public Either<GroupDefinition, StorageOperationStatus> addGroup(NodeTypeEnum nodeTypeEnum, String componentId,
+ GroupDefinition groupDefinition, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+ try {
+ Either<GroupData, TitanOperationStatus> addGroupRes = addGroupToGraph(nodeTypeEnum, componentId,
+ groupDefinition);
+ if (addGroupRes.isRight()) {
+ TitanOperationStatus status = addGroupRes.right().value();
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+
+ GroupData groupData = addGroupRes.left().value();
+ String groupUid = groupData.getGroupDataDefinition().getUniqueId();
+ result = this.getGroup(groupUid, true);
+
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph. Failed to add group {} to {}", groupDefinition.getName(), nodeTypeEnum.toString());
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private GroupDefinition convertGroupDataToGroupDefinition(GroupData groupData) {
+ GroupDefinition newGroupDefinition = new GroupDefinition(groupData.getGroupDataDefinition());
+ return newGroupDefinition;
+ }
+
+ public Either<GroupDefinition, StorageOperationStatus> getGroup(String uniqueId) {
+ return getGroup(uniqueId, false);
+ }
+
+ @Override
+ public Either<GroupDefinition, StorageOperationStatus> getGroup(String uniqueId, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(uniqueId);
+
+ if (groupFromGraph.isRight()) {
+ TitanOperationStatus status = groupFromGraph.right().value();
+ log.debug("Failed to retrieve group {} from graph. Status is {}", uniqueId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ GroupDefinition groupDefinition = groupFromGraph.left().value();
+ result = Either.left(groupDefinition);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<GroupDefinition, TitanOperationStatus> getGroupFromGraph(String uniqueId) {
+
+ return getGroupFromGraph(uniqueId, false, false, false);
+
+ }
+
+ /**
+ * get the list of artifacts related to a given group
+ *
+ * @param groupUniqueId
+ * @return
+ */
+ // private Either<List<String>, TitanOperationStatus> getGroupArtifacts(
+ // String groupUniqueId) {
+ //
+ // Either<List<String>, TitanOperationStatus> result = null;
+ //
+ // Either<List<ImmutablePair<ArtifactData, GraphEdge>>,
+ // TitanOperationStatus> childrenNodes = titanGenericDao
+ // .getChildrenNodes(
+ // UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Group),
+ // groupUniqueId, GraphEdgeLabels.GROUP_ARTIFACT_REF,
+ // NodeTypeEnum.ArtifactRef, ArtifactData.class);
+ // if (childrenNodes.isRight()) {
+ // TitanOperationStatus status = childrenNodes.right().value();
+ // if (status == TitanOperationStatus.NOT_FOUND) {
+ // status = TitanOperationStatus.OK;
+ // }
+ // result = Either.right(status);
+ //
+ // } else {
+ //
+ // List<String> artifactsList = new ArrayList<>();
+ // List<ImmutablePair<ArtifactData, GraphEdge>> list = childrenNodes
+ // .left().value();
+ // if (list != null) {
+ // for (ImmutablePair<ArtifactData, GraphEdge> pair : list) {
+ // ArtifactData artifactData = pair.getKey();
+ // String uniqueId = artifactData.getArtifactDataDefinition()
+ // .getUniqueId();
+ // artifactsList.add(uniqueId);
+ // }
+ // }
+ //
+ // log.debug("The artifacts list related to group {} is {}", groupUniqueId, artifactsList);
+ // result = Either.left(artifactsList);
+ // }
+ //
+ // return result;
+ //
+ // }
+
+ /**
+ * get members of group
+ *
+ * @param groupUniqueId
+ * @return
+ */
+ protected Either<Map<String, String>, TitanOperationStatus> getGroupMembers(String groupUniqueId) {
+
+ Either<Map<String, String>, TitanOperationStatus> result = null;
+
+ Either<List<ImmutablePair<ComponentInstanceData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Group), groupUniqueId,
+ GraphEdgeLabels.GROUP_MEMBER, NodeTypeEnum.ResourceInstance, ComponentInstanceData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = Either.right(status);
+
+ } else {
+
+ Map<String, String> compInstaMap = new HashMap<>();
+ List<ImmutablePair<ComponentInstanceData, GraphEdge>> list = childrenNodes.left().value();
+ if (list != null) {
+ for (ImmutablePair<ComponentInstanceData, GraphEdge> pair : list) {
+ ComponentInstanceData componentInstanceData = pair.getKey();
+
+ String compInstUniqueId = componentInstanceData.getComponentInstDataDefinition().getUniqueId();
+ String compInstName = componentInstanceData.getName();
+ compInstaMap.put(compInstName, compInstUniqueId);
+ }
+ }
+
+ result = Either.left(compInstaMap);
+ }
+
+ return result;
+ }
+
+ public Either<GroupTypeDefinition, TitanOperationStatus> getGroupTypeOfGroup(String groupUniqueId) {
+
+ Either<ImmutablePair<GroupTypeData, GraphEdge>, TitanOperationStatus> groupTypeRes = titanGenericDao.getChild(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Group), groupUniqueId, GraphEdgeLabels.TYPE_OF,
+ NodeTypeEnum.GroupType, GroupTypeData.class);
+
+ if (groupTypeRes.isRight()) {
+ TitanOperationStatus status = groupTypeRes.right().value();
+ log.debug("Cannot find group type associated with capability {}. Status is {}", groupUniqueId, status);
+
+ BeEcompErrorManager.getInstance().logBeFailedFindAssociationError("Fetch Group type",
+ NodeTypeEnum.GroupType.getName(), groupUniqueId, String.valueOf(status));
+ return Either.right(groupTypeRes.right().value());
+ }
+
+ GroupTypeData groupTypeData = groupTypeRes.left().value().getKey();
+
+ Either<GroupTypeDefinition, TitanOperationStatus> groupTypeByUid = groupTypeOperation
+ .getGroupTypeByUid(groupTypeData.getGroupTypeDataDefinition().getUniqueId());
+
+ return groupTypeByUid;
+
+ }
+
+ /**
+ * get all properties of the group.
+ *
+ * the propert definition is taken from the group type.
+ *
+ * @param groupUid
+ * @return
+ */
+ public Either<List<GroupProperty>, TitanOperationStatus> getGroupProperties(String groupUid) {
+
+ List<GroupProperty> groupPropertiesList = new ArrayList<>();
+
+ Either<GroupTypeDefinition, TitanOperationStatus> groupTypeOfGroupRes = getGroupTypeOfGroup(groupUid);
+
+ if (groupTypeOfGroupRes.isRight()) {
+ TitanOperationStatus status = groupTypeOfGroupRes.right().value();
+ return Either.right(status);
+ }
+
+ GroupTypeDefinition groupTypeDefinition = groupTypeOfGroupRes.left().value();
+
+ // Get the properties on the group type of this group
+ List<PropertyDefinition> groupTypeProperties = groupTypeDefinition.getProperties();
+
+ if (groupTypeProperties == null || true == groupTypeProperties.isEmpty()) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ Map<String, PropertyDefinition> uidToPropDefMap = groupTypeProperties.stream()
+ .collect(Collectors.toMap(p -> p.getUniqueId(), p -> p));
+
+ // Find all properties values on the group
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Group), groupUid,
+ GraphEdgeLabels.PROPERTY_VALUE, NodeTypeEnum.PropertyValue, PropertyValueData.class);
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ groupPropertiesList = groupTypeProperties.stream()
+ .map(p -> new GroupProperty(p, p.getDefaultValue(), null)).collect(Collectors.toList());
+ return Either.left(groupPropertiesList);
+ } else {
+ return Either.right(status);
+ }
+ }
+
+ List<ImmutablePair<PropertyValueData, GraphEdge>> list = propertyImplNodes.left().value();
+ if (list == null || true == list.isEmpty()) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ List<String> processedProps = new ArrayList<>();
+
+ for (ImmutablePair<PropertyValueData, GraphEdge> propertyValue : list) {
+
+ PropertyValueData propertyValueData = propertyValue.getLeft();
+ String propertyValueUid = propertyValueData.getUniqueId();
+ String value = propertyValueData.getValue();
+
+ Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> propertyDefRes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PropertyValue), propertyValueUid,
+ GraphEdgeLabels.PROPERTY_IMPL, NodeTypeEnum.Property, PropertyData.class);
+ if (propertyDefRes.isRight()) {
+ TitanOperationStatus status = propertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ ImmutablePair<PropertyData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
+
+ PropertyData propertyData = propertyDefPair.left;
+ String propertyUniqueId = propertyData.getPropertyDataDefinition().getUniqueId();
+
+ PropertyDefinition propertyDefinition = uidToPropDefMap.get(propertyUniqueId);
+ GroupProperty groupProperty = new GroupProperty(propertyDefinition, value, propertyValueUid);
+
+ processedProps.add(propertyUniqueId);
+
+ groupPropertiesList.add(groupProperty);
+
+ }
+
+ // Find all properties which does not have property value on the group.
+ List<GroupProperty> leftProps = groupTypeProperties.stream()
+ // filter out the group type properties which already processed
+ .filter(p -> false == processedProps.contains(p.getUniqueId()))
+ .map(p -> new GroupProperty(p, p.getDefaultValue(), null)).collect(Collectors.toList());
+ if (leftProps != null) {
+ groupPropertiesList.addAll(leftProps);
+ }
+
+ return Either.left(groupPropertiesList);
+ }
+
+ public Either<List<GroupDefinition>, TitanOperationStatus> getAllGroupsFromGraph(String componentId,
+ NodeTypeEnum componentTypeEnum) {
+
+ return getAllGroupsFromGraph(componentId, componentTypeEnum, false, false, false);
+
+ }
+
+ @Override
+ public Either<List<GroupDefinition>, StorageOperationStatus> getAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<List<GroupDefinition>, TitanOperationStatus> allGroups = this.getAllGroupsFromGraph(componentId,
+ compTypeEnum);
+
+ if (allGroups.isRight()) {
+ TitanOperationStatus status = allGroups.right().value();
+ log.debug("Failed to retrieve all groups of component {} from graph. Status is {}", componentId,
+ status);
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ List<GroupDefinition> groupsDefinition = allGroups.left().value();
+ result = Either.left(groupsDefinition);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<GroupDefinition>, StorageOperationStatus> getAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum) {
+ return getAllGroups(componentId, compTypeEnum, false);
+ }
+
+ public Either<GroupData, TitanOperationStatus> deleteGroupFromGraph(String groupUniqueId) {
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(groupUniqueId);
+ if (groupFromGraph.isRight()) {
+ TitanOperationStatus status = groupFromGraph.right().value();
+ log.debug("Cannot find group {} on graph. Status is {}", groupUniqueId, status);
+ return Either.right(status);
+ }
+
+ GroupDefinition groupDefinition = groupFromGraph.left().value();
+ // 1. delete all properties values nodes
+ List<GroupProperty> properties = groupDefinition.getProperties();
+ if (properties != null) {
+ for (GroupProperty groupProperty : properties) {
+ String propValueUniqueId = groupProperty.getValueUniqueUid();
+
+ if (propValueUniqueId != null) {
+ UniqueIdData uniqueIdData = new UniqueIdData(NodeTypeEnum.PropertyValue, propValueUniqueId);
+ Either<PropertyValueData, TitanOperationStatus> deleteNode = titanGenericDao
+ .deleteNode(uniqueIdData, PropertyValueData.class);
+ if (deleteNode.isRight()) {
+ TitanOperationStatus status = groupFromGraph.right().value();
+ String description = String.format(
+ "Failed to delete property {} under group {}" + groupUniqueId
+ + " on graph. Status is {}",
+ propValueUniqueId, groupDefinition.getName(), status.name());
+ log.debug(description);
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError(DELETING_GROUP, propValueUniqueId,
+ status.name());
+ return Either.right(status);
+ } else {
+ log.trace("Property {} was deleted from geoup {}", propValueUniqueId, groupDefinition.getName());
+ }
+ }
+ }
+ }
+
+ // 2. delete the group node
+ UniqueIdData uniqueIdData = new UniqueIdData(NodeTypeEnum.Group, groupUniqueId);
+ Either<GroupData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(uniqueIdData, GroupData.class);
+ if (deleteNode.isRight()) {
+ TitanOperationStatus status = groupFromGraph.right().value();
+ String description = String.format(
+ "Failed to delete group {} with uid " + groupUniqueId + " on graph. Status is {}",
+ groupDefinition.getName(), groupUniqueId, status.name());
+ log.debug(description);
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError(DELETING_GROUP, groupUniqueId, status.name());
+ return Either.right(status);
+ } else {
+ log.trace("Group {} was deleted from group", groupUniqueId);
+ }
+
+ GroupData groupData = deleteNode.left().value();
+ return Either.left(groupData);
+ }
+
+ public Either<GroupDefinition, StorageOperationStatus> deleteGroup(String groupUniqueId) {
+ return deleteGroup(groupUniqueId, false);
+ }
+
+ public Either<GroupDefinition, StorageOperationStatus> deleteGroup(String groupUniqueId, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<GroupData, TitanOperationStatus> deleteGroup = this.deleteGroupFromGraph(groupUniqueId);
+
+ if (deleteGroup.isRight()) {
+ TitanOperationStatus status = deleteGroup.right().value();
+ log.debug("Failed to delete group {} from graph. Status is ", groupUniqueId, status.name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ GroupData groupData = deleteGroup.left().value();
+ GroupDefinition groupDefinition = convertGroupDataToGroupDefinition(groupData);
+ result = Either.left(groupDefinition);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<GroupDefinition>, TitanOperationStatus> deleteAllGroupsFromGraph(String componentId,
+ NodeTypeEnum componentTypeEnum) {
+
+ Either<List<ImmutablePair<GroupData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(componentTypeEnum), componentId,
+ GraphEdgeLabels.GROUP, NodeTypeEnum.Group, GroupData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logBeFailedFindAllNodesError(DELETING_ALL_GROUPS,
+ NodeTypeEnum.Group.name(), componentId, status.name());
+ }
+ return Either.right(status);
+ }
+
+ List<GroupDefinition> result = new ArrayList<>();
+
+ List<ImmutablePair<GroupData, GraphEdge>> list = childrenNodes.left().value();
+ if (list != null) {
+ for (ImmutablePair<GroupData, GraphEdge> pair : list) {
+ String uniqueId = pair.left.getGroupDataDefinition().getUniqueId();
+ Either<GroupData, TitanOperationStatus> deleteGroupFromGraph = deleteGroupFromGraph(uniqueId);
+ if (deleteGroupFromGraph.isRight()) {
+ TitanOperationStatus status = deleteGroupFromGraph.right().value();
+ BeEcompErrorManager.getInstance().logBeFailedDeleteNodeError(DELETING_ALL_GROUPS, uniqueId,
+ status.name());
+ return Either.right(status);
+ }
+ GroupData groupData = deleteGroupFromGraph.left().value();
+ GroupDefinition groupDefinition = convertGroupDataToGroupDefinition(groupData);
+ result.add(groupDefinition);
+ }
+ }
+
+ return Either.left(result);
+ }
+
+ @Override
+ public Either<List<GroupDefinition>, StorageOperationStatus> deleteAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<List<GroupDefinition>, TitanOperationStatus> allGroups = this.deleteAllGroupsFromGraph(componentId,
+ compTypeEnum);
+
+ if (allGroups.isRight()) {
+ TitanOperationStatus status = allGroups.right().value();
+ log.debug("Failed to delete all groups of component {} from graph. Status is {}", componentId, status);
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ List<GroupDefinition> groupsDefinition = allGroups.left().value();
+ result = Either.left(groupsDefinition);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<GroupDefinition>, StorageOperationStatus> deleteAllGroups(String componentId,
+ NodeTypeEnum compTypeEnum) {
+ return deleteAllGroups(componentId, compTypeEnum, false);
+ }
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> prepareGroupsForCloning(
+ org.openecomp.sdc.be.model.Component origResource,
+ ImmutablePair<List<ComponentInstance>, Map<String, String>> cloneInstances) {
+
+ List<GroupDefinition> groupsToCreate = new ArrayList<>();
+ Either<List<GroupDefinition>, StorageOperationStatus> result = Either.left(groupsToCreate);
+
+ List<GroupDefinition> groups = origResource.getGroups();
+
+ if (groups != null) {
+ // keep typeUid
+ // keep artifacts uids
+ // remove properties without valueUniqueId
+ for (GroupDefinition groupDefinition : groups) {
+
+ GroupDefinition gdToCreate = new GroupDefinition(groupDefinition);
+ gdToCreate.setUniqueId(null);
+ gdToCreate.setMembers(null);
+
+ List<GroupProperty> properties = groupDefinition.getProperties();
+ if (properties != null) {
+ // Take properties which was updated in the
+ // group(getValueUniqueUid != null),
+ // Then set null instead of the value(prepare for the
+ // creation).
+ List<GroupProperty> propertiesToUpdate = properties.stream()
+ .filter(p -> p.getValueUniqueUid() != null).map(p -> {
+ p.setValueUniqueUid(null);
+ return p;
+ }).collect(Collectors.toList());
+
+ gdToCreate.setProperties(propertiesToUpdate);
+
+ }
+
+ Map<String, String> members = groupDefinition.getMembers();
+ if (cloneInstances != null) {
+ List<ComponentInstance> createdInstances = cloneInstances.left;
+ Map<String, String> oldCompUidToNew = cloneInstances.right;
+ if (members != null && createdInstances != null) {
+
+ Map<String, String> compInstIdToName = createdInstances.stream()
+ .collect(Collectors.toMap(p -> p.getUniqueId(), p -> p.getName()));
+
+ Map<String, String> membersToCreate = new HashMap<>();
+
+ for (String oldCompInstId : members.values()) {
+ String newCompInstUid = oldCompUidToNew.get(oldCompInstId);
+ if (newCompInstUid == null) {
+ result = Either.right(StorageOperationStatus.MATCH_NOT_FOUND);
+ return result;
+ }
+ String newCompInstName = compInstIdToName.get(newCompInstUid);
+ membersToCreate.put(newCompInstName, newCompInstUid);
+ }
+
+ gdToCreate.setMembers(membersToCreate);
+ }
+ }
+
+ log.debug("The group definition for creation is {}", gdToCreate);
+
+ groupsToCreate.add(gdToCreate);
+ }
+
+ }
+
+ return result;
+ }
+
+ @Override
+ public Either<List<GroupDefinition>, StorageOperationStatus> addGroups(NodeTypeEnum nodeTypeEnum,
+ String componentId, List<GroupDefinition> groups, boolean inTransaction) {
+
+ List<GroupDefinition> createdGroups = new ArrayList<>();
+
+ Either<List<GroupDefinition>, StorageOperationStatus> result = null;
+
+ try {
+
+ if (groups != null) {
+ for (GroupDefinition groupDefinition : groups) {
+ Either<GroupDefinition, StorageOperationStatus> addGroup = this.addGroup(nodeTypeEnum, componentId,
+ groupDefinition, true);
+ if (addGroup.isRight()) {
+ StorageOperationStatus status = addGroup.right().value();
+ result = Either.right(status);
+ return result;
+ }
+
+ createdGroups.add(addGroup.left().value());
+ }
+ }
+
+ result = Either.left(createdGroups);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Either<List<String>, TitanOperationStatus> getAssociatedGroupsToComponentInstanceFromGraph(
+ String componentInstanceId) {
+
+ List<String> groups = new ArrayList<>();
+ Either<List<String>, TitanOperationStatus> result = Either.left(groups);
+
+ Either<List<ImmutablePair<GroupData, GraphEdge>>, TitanOperationStatus> parentNodes = titanGenericDao
+ .getParentNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), componentInstanceId,
+ GraphEdgeLabels.GROUP_MEMBER, NodeTypeEnum.Group, GroupData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus status = parentNodes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logBeFailedFindParentError("FetchGroupMembers", componentInstanceId,
+ status.name());
+ }
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<GroupData, GraphEdge>> fetchedGroups = parentNodes.left().value();
+ if (fetchedGroups != null) {
+ List<String> list = fetchedGroups.stream().map(p -> p.left.getGroupDataDefinition().getUniqueId())
+ .collect(Collectors.toList());
+ groups.addAll(list);
+ }
+
+ return result;
+
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAssociatedGroupsToComponentInstance(
+ String componentInstanceId, boolean inTransaction) {
+
+ Either<List<String>, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<List<String>, TitanOperationStatus> groups = this
+ .getAssociatedGroupsToComponentInstanceFromGraph(componentInstanceId);
+
+ if (groups.isRight()) {
+ TitanOperationStatus status = groups.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ List<String> list = groups.left().value();
+
+ return Either.left(list);
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAssociatedGroupsToComponentInstance(
+ String componentInstanceId) {
+ return getAssociatedGroupsToComponentInstance(componentInstanceId, false);
+ }
+
+ @Override
+ public Either<List<GraphRelation>, TitanOperationStatus> associateGroupsToComponentInstanceOnGraph(
+ List<String> groups, String componentInstanceId, String compInstName) {
+
+ List<GraphRelation> relations = new ArrayList<>();
+ Either<List<GraphRelation>, TitanOperationStatus> result = Either.left(relations);
+
+ if (groups != null && false == groups.isEmpty()) {
+
+ UniqueIdData compInstData = new UniqueIdData(NodeTypeEnum.ResourceInstance, componentInstanceId);
+
+ for (String groupId : groups) {
+ UniqueIdData groupData = new UniqueIdData(NodeTypeEnum.Group, groupId);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), compInstName);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(groupData,
+ compInstData, GraphEdgeLabels.GROUP_MEMBER, props);
+ if (createRelation.isRight()) {
+ TitanOperationStatus status = createRelation.right().value();
+ String description = "Failed to associate group " + groupData.getUniqueId()
+ + " to component instance " + compInstName + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ASSOCIATING_GROUP_TO_COMP_INST, description,
+ ErrorSeverity.ERROR);
+ result = Either.right(status);
+ break;
+ }
+ GraphRelation graphRelation = createRelation.left().value();
+ relations.add(graphRelation);
+ }
+ } else {
+ result = Either.right(TitanOperationStatus.OK);
+ }
+
+ return result;
+ }
+
+ public StorageOperationStatus associateGroupsToComponentInstance(List<String> groups, String componentInstanceId,
+ String compInstName) {
+
+ return associateGroupsToComponentInstance(groups, componentInstanceId, compInstName, false);
+ }
+
+ @Override
+ public StorageOperationStatus associateGroupsToComponentInstance(List<String> groups, String componentInstanceId,
+ String compInstName, boolean inTransaction) {
+
+ StorageOperationStatus result = null;
+
+ try {
+ Either<List<GraphRelation>, TitanOperationStatus> either = this
+ .associateGroupsToComponentInstanceOnGraph(groups, componentInstanceId, compInstName);
+
+ if (either.isRight()) {
+ TitanOperationStatus status = either.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return result;
+ }
+
+ result = StorageOperationStatus.OK;
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result != StorageOperationStatus.OK) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<GraphRelation>, TitanOperationStatus> dissociateAllGroupsFromArtifactOnGraph(String componentId,
+ NodeTypeEnum componentTypeEnum, String artifactId) {
+
+ List<GraphRelation> relations = new ArrayList<>();
+ Either<List<GraphRelation>, TitanOperationStatus> result = Either.left(relations);
+
+ Either<List<GroupDefinition>, TitanOperationStatus> allGroupsFromGraph = getAllGroupsFromGraph(componentId,
+ componentTypeEnum, true, true, false);
+ if (allGroupsFromGraph.isRight()) {
+ TitanOperationStatus status = allGroupsFromGraph.right().value();
+ return Either.right(status);
+ }
+
+ List<GroupDefinition> allGroups = allGroupsFromGraph.left().value();
+ if (allGroups == null || allGroups.isEmpty()) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ // Find all groups which contains this artifact id
+ List<GroupDefinition> associatedGroups = allGroups.stream()
+ .filter(p -> p.getArtifacts() != null && p.getArtifacts().contains(artifactId))
+ .collect(Collectors.toList());
+
+ if (associatedGroups != null && false == associatedGroups.isEmpty()) {
+ log.debug("The groups {} contains the artifact {}", associatedGroups.stream().map(p -> p.getName()).collect(Collectors.toList()), artifactId);
+
+ UniqueIdData artifactData = new UniqueIdData(NodeTypeEnum.ArtifactRef, artifactId);
+ for (GroupDefinition groupDefinition : associatedGroups) {
+ UniqueIdData groupData = new UniqueIdData(NodeTypeEnum.Group, groupDefinition.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> deleteRelation = titanGenericDao.deleteRelation(groupData,
+ artifactData, GraphEdgeLabels.GROUP_ARTIFACT_REF);
+ if (deleteRelation.isRight()) {
+ TitanOperationStatus status = deleteRelation.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ relations.add(deleteRelation.left().value());
+ }
+
+ return result;
+
+ } else {
+ log.debug("No group under component id {} is associated to artifact {}", componentId, artifactId);
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ }
+
+ public Either<GroupDefinition, TitanOperationStatus> getGroupFromGraph(String uniqueId, boolean skipProperties,
+ boolean skipMembers, boolean skipArtifacts) {
+
+ Either<GroupDefinition, TitanOperationStatus> result = null;
+
+ Either<GroupData, TitanOperationStatus> groupRes = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Group), uniqueId, GroupData.class);
+ if (groupRes.isRight()) {
+ TitanOperationStatus status = groupRes.right().value();
+ log.debug("Failed to retrieve group {} from graph. Status is {}", uniqueId, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Group", uniqueId,
+ String.valueOf(status));
+ result = Either.right(status);
+ return result;
+ }
+
+ GroupData groupData = groupRes.left().value();
+
+ GroupDefinition groupDefinition = convertGroupDataToGroupDefinition(groupData);
+
+ Either<GroupTypeDefinition, TitanOperationStatus> groupTypeOfGroup = getGroupTypeOfGroup(uniqueId);
+
+ if (groupTypeOfGroup.isRight()) {
+ TitanOperationStatus status = groupTypeOfGroup.right().value();
+ log.debug("Failed to retrieve capability type of capability {}. Status is {}", uniqueId, status);
+
+ result = Either.right(status);
+ return result;
+ }
+
+ GroupTypeDefinition groupTypeDefinition = groupTypeOfGroup.left().value();
+
+ groupDefinition.setTypeUid(groupTypeDefinition.getUniqueId());
+
+ if (false == skipMembers) {
+ Either<Map<String, String>, TitanOperationStatus> membersRes = getGroupMembers(uniqueId);
+ if (membersRes.isRight()) {
+ TitanOperationStatus status = membersRes.right().value();
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(status);
+ return result;
+ }
+ } else {
+ Map<String, String> members = membersRes.left().value();
+ groupDefinition.setMembers(members);
+ }
+ }
+
+ if (false == skipProperties) {
+ Either<List<GroupProperty>, TitanOperationStatus> propertiesRes = getGroupProperties(uniqueId);
+ if (propertiesRes.isRight()) {
+ TitanOperationStatus status = propertiesRes.right().value();
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(status);
+ return result;
+ }
+ } else {
+ List<GroupProperty> properties = propertiesRes.left().value();
+ groupDefinition.setProperties(properties);
+ }
+ }
+
+ if (false == skipArtifacts) {
+ Either<List<ImmutablePair<String, String>>, TitanOperationStatus> artifactsRes = getGroupArtifactsPairs(
+ uniqueId);
+ if (artifactsRes.isRight()) {
+ TitanOperationStatus status = artifactsRes.right().value();
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(status);
+ return result;
+ }
+ } else {
+ List<String> artifactsUid = new ArrayList<>();
+ List<String> artifactsUUID = new ArrayList<>();
+
+ List<ImmutablePair<String, String>> list = artifactsRes.left().value();
+ if (list != null) {
+ for (ImmutablePair<String, String> pair : list) {
+ String uid = pair.left;
+ String UUID = pair.right;
+ artifactsUid.add(uid);
+ artifactsUUID.add(UUID);
+ }
+ groupDefinition.setArtifacts(artifactsUid);
+ groupDefinition.setArtifactsUuid(artifactsUUID);
+ }
+ }
+ }
+ result = Either.left(groupDefinition);
+
+ return result;
+
+ }
+
+ @Override
+ public boolean isGroupExist(String groupName, boolean inTransaction) {
+
+ Either<List<GroupData>, TitanOperationStatus> eitherGroup = null;
+ try {
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.NAME.getProperty(), groupName);
+
+ eitherGroup = titanGenericDao.getByCriteria(NodeTypeEnum.Group, properties, GroupData.class);
+ return eitherGroup.isLeft() && !eitherGroup.left().value().isEmpty();
+
+ } finally {
+ handleTransactionCommitRollback(inTransaction, eitherGroup);
+ }
+ }
+
+ protected Either<List<GroupDefinition>, TitanOperationStatus> getAllGroupsFromGraph(String componentId,
+ NodeTypeEnum componentTypeEnum, boolean skipProperties, boolean skipMembers, boolean skipArtifacts) {
+
+ List<GroupDefinition> groups = new ArrayList<GroupDefinition>();
+
+ Either<List<ImmutablePair<GroupData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(componentTypeEnum), componentId,
+ GraphEdgeLabels.GROUP, NodeTypeEnum.Group, GroupData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<GroupData, GraphEdge>> graphGroups = childrenNodes.left().value();
+
+ if (graphGroups == null || true == graphGroups.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ // Consumer<String> consumer = (x) -> getGroup(x);
+ // StreamUtils.takeWhile(graphGroups.stream().map(p ->
+ // p.left.getUniqueId()), consumer);
+
+ for (ImmutablePair<GroupData, GraphEdge> pair : graphGroups) {
+
+ String groupUniqueId = pair.left.getGroupDataDefinition().getUniqueId();
+ Either<GroupDefinition, TitanOperationStatus> groupRes = this.getGroupFromGraph(groupUniqueId,
+ skipProperties, skipMembers, skipArtifacts);
+
+ if (groupRes.isRight()) {
+ TitanOperationStatus status = groupRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ } else {
+ groups.add(groupRes.left().value());
+ }
+
+ }
+
+ return Either.left(groups);
+ }
+
+ @Override
+ public StorageOperationStatus dissociateAllGroupsFromArtifact(String componentId, NodeTypeEnum componentTypeEnum,
+ String artifactId, boolean inTransaction) {
+
+ StorageOperationStatus result = null;
+
+ try {
+ Either<List<GraphRelation>, TitanOperationStatus> either = this
+ .dissociateAllGroupsFromArtifactOnGraph(componentId, componentTypeEnum, artifactId);
+
+ if (either.isRight()) {
+ TitanOperationStatus status = either.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return result;
+ }
+
+ result = StorageOperationStatus.OK;
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result != StorageOperationStatus.OK) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public StorageOperationStatus dissociateAllGroupsFromArtifact(String componentId, NodeTypeEnum componentTypeEnum,
+ String artifactId) {
+
+ return dissociateAllGroupsFromArtifact(componentId, componentTypeEnum, artifactId, false);
+ }
+
+ @Override
+ public TitanOperationStatus dissociateAndAssociateGroupsFromArtifactOnGraph(String componentId,
+ NodeTypeEnum componentTypeEnum, String oldArtifactId, ArtifactData newArtifact) {
+
+ Either<List<GroupDefinition>, TitanOperationStatus> allGroupsFromGraph = getAllGroupsFromGraph(componentId,
+ componentTypeEnum, true, true, false);
+ if (allGroupsFromGraph.isRight()) {
+ TitanOperationStatus status = allGroupsFromGraph.right().value();
+ return status;
+ }
+
+ List<GroupDefinition> allGroups = allGroupsFromGraph.left().value();
+ if (allGroups == null || allGroups.isEmpty()) {
+ return TitanOperationStatus.OK;
+ }
+
+ // Find all groups which contains this artifact id
+ List<GroupDefinition> associatedGroups = allGroups.stream()
+ .filter(p -> p.getArtifacts() != null && p.getArtifacts().contains(oldArtifactId))
+ .collect(Collectors.toList());
+
+ if (associatedGroups != null && false == associatedGroups.isEmpty()) {
+
+ log.debug("The groups {} contains the artifact {}", associatedGroups.stream().map(p -> p.getName()).collect(Collectors.toList()), oldArtifactId);
+
+ UniqueIdData oldArtifactData = new UniqueIdData(NodeTypeEnum.ArtifactRef, oldArtifactId);
+ UniqueIdData newArtifactData = new UniqueIdData(NodeTypeEnum.ArtifactRef,
+ newArtifact.getArtifactDataDefinition().getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), newArtifactData.getLabel());
+
+ for (GroupDefinition groupDefinition : associatedGroups) {
+ UniqueIdData groupData = new UniqueIdData(NodeTypeEnum.Group, groupDefinition.getUniqueId());
+
+ Either<GraphRelation, TitanOperationStatus> deleteRelation = titanGenericDao.deleteRelation(groupData,
+ oldArtifactData, GraphEdgeLabels.GROUP_ARTIFACT_REF);
+ log.trace("After dissociate group {} from artifac {}", groupDefinition.getName(), oldArtifactId);
+ if (deleteRelation.isRight()) {
+ TitanOperationStatus status = deleteRelation.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return status;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(groupData,
+ newArtifactData, GraphEdgeLabels.GROUP_ARTIFACT_REF, props);
+ log.trace("After associate group {} to artifact {}", groupDefinition.getName(), newArtifact.getUniqueIdKey());
+ if (createRelation.isRight()) {
+ TitanOperationStatus status = createRelation.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return status;
+ }
+ }
+
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ @Override
+ public StorageOperationStatus dissociateAndAssociateGroupsFromArtifact(String componentId,
+ NodeTypeEnum componentTypeEnum, String oldArtifactId, ArtifactData newArtifact, boolean inTransaction) {
+
+ StorageOperationStatus result = null;
+
+ try {
+ TitanOperationStatus status = this.dissociateAndAssociateGroupsFromArtifactOnGraph(componentId,
+ componentTypeEnum, oldArtifactId, newArtifact);
+
+ if (status != TitanOperationStatus.OK && status != TitanOperationStatus.NOT_FOUND) {
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ return result;
+ }
+
+ result = StorageOperationStatus.OK;
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result != StorageOperationStatus.OK) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public StorageOperationStatus dissociateAndAssociateGroupsFromArtifact(String componentId,
+ NodeTypeEnum componentTypeEnum, String oldArtifactId, ArtifactData newArtifact) {
+ return dissociateAndAssociateGroupsFromArtifact(componentId, componentTypeEnum, oldArtifactId, newArtifact,
+ false);
+ }
+
+ private Either<List<ImmutablePair<String, String>>, TitanOperationStatus> getGroupArtifactsPairs(
+ String groupUniqueId) {
+
+ Either<List<ImmutablePair<String, String>>, TitanOperationStatus> result = null;
+
+ Either<List<ImmutablePair<ArtifactData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Group), groupUniqueId,
+ GraphEdgeLabels.GROUP_ARTIFACT_REF, NodeTypeEnum.ArtifactRef, ArtifactData.class);
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ result = Either.right(status);
+
+ } else {
+
+ List<ImmutablePair<String, String>> artifactsList = new ArrayList<>();
+ List<ImmutablePair<ArtifactData, GraphEdge>> list = childrenNodes.left().value();
+ if (list != null) {
+ for (ImmutablePair<ArtifactData, GraphEdge> pair : list) {
+ ArtifactData artifactData = pair.getKey();
+ String uniqueId = artifactData.getArtifactDataDefinition().getUniqueId();
+ String UUID = artifactData.getArtifactDataDefinition().getArtifactUUID();
+ ImmutablePair<String, String> artifact = new ImmutablePair<String, String>(uniqueId, UUID);
+ artifactsList.add(artifact);
+ }
+ }
+
+ log.debug("The artifacts list related to group {} is {}", groupUniqueId, artifactsList);
+ result = Either.left(artifactsList);
+ }
+
+ return result;
+
+ }
+
+ public Either<List<GroupDefinition>, TitanOperationStatus> updateGroupVersionOnGraph(List<String> groupsUniqueId) {
+
+ if (groupsUniqueId != null) {
+
+ List<GroupDefinition> groups = new ArrayList<>();
+ for (String groupUid : groupsUniqueId) {
+ Either<GroupDefinition, TitanOperationStatus> either = updateGroupVersionOnGraph(groupUid);
+ if (either.isRight()) {
+ log.debug("Failed to update version of group {}", groupUid);
+ return Either.right(either.right().value());
+ }
+ groups.add(either.left().value());
+ }
+ return Either.left(groups);
+ }
+
+ return Either.right(TitanOperationStatus.OK);
+
+ }
+
+ /**
+ * update the group version of a given group. It also creates a new UUID.
+ *
+ * @param groupUniqueId
+ * @return
+ */
+ public Either<GroupDefinition, TitanOperationStatus> updateGroupVersionOnGraph(String groupUniqueId) {
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(groupUniqueId, false,
+ false, false);
+
+ if (groupFromGraph.isRight()) {
+ TitanOperationStatus status = groupFromGraph.right().value();
+ return Either.right(status);
+ } else {
+ GroupDefinition groupDefinition = groupFromGraph.left().value();
+ String version = groupDefinition.getVersion();
+ String newVersion = increaseMajorVersion(version);
+ Integer pvCounter = groupDefinition.getPropertyValueCounter();
+
+ GroupData groupData = new GroupData();
+ groupData.getGroupDataDefinition().setUniqueId(groupUniqueId);
+ groupData.getGroupDataDefinition().setVersion(newVersion);
+ groupData.getGroupDataDefinition().setPropertyValueCounter(pvCounter);
+
+ String groupUUID = UniqueIdBuilder.generateUUID();
+ groupData.getGroupDataDefinition().setGroupUUID(groupUUID);
+
+ Either<GroupData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(groupData, GroupData.class);
+ if (updateNode.isRight()) {
+ return Either.right(updateNode.right().value());
+ } else {
+ groupFromGraph = this.getGroupFromGraph(groupUniqueId, false, false, false);
+ return groupFromGraph;
+ }
+
+ }
+ }
+
+ /**
+ * The version of the group is an integer. In order to support BC, we might
+ * get a version in a float format.
+ *
+ * @param version
+ * @return
+ */
+ private String increaseMajorVersion(String version) {
+
+ String[] versionParts = version.split(LifecycleOperation.VERSION_DELIMETER_REGEXP);
+ Integer majorVersion = Integer.parseInt(versionParts[0]);
+
+ majorVersion++;
+
+ return String.valueOf(majorVersion);
+
+ }
+
+ public Either<GroupDefinition, TitanOperationStatus> associateArtifactsToGroupOnGraph(String groupId,
+ List<String> artifactsId) {
+
+ if (artifactsId == null || artifactsId.isEmpty()) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ for (String artifactId : artifactsId) {
+ Either<ArtifactData, TitanOperationStatus> findArtifactRes = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactRef), artifactId, ArtifactData.class);
+ if (findArtifactRes.isRight()) {
+ TitanOperationStatus status = findArtifactRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ String description = "Failed to associate group " + groupId + " to artifact " + artifactId
+ + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), findArtifactRes.left().value().getLabel());
+
+ GraphNode groupData = new UniqueIdData(NodeTypeEnum.Group, groupId);
+ Either<GraphRelation, TitanOperationStatus> addArtifactsRefResult = titanGenericDao.createRelation(
+ groupData, findArtifactRes.left().value(), GraphEdgeLabels.GROUP_ARTIFACT_REF, props);
+
+ if (addArtifactsRefResult.isRight()) {
+ TitanOperationStatus status = addArtifactsRefResult.right().value();
+ String description = "Failed to associate group " + groupData.getUniqueId() + " to artifact "
+ + artifactId + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ }
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(groupId, true, true,
+ false);
+
+ return groupFromGraph;
+ }
+
+ public Either<GroupDefinition, TitanOperationStatus> associateMembersToGroupOnGraph(String groupId,
+ Map<String, String> members) {
+
+ if (members != null && false == members.isEmpty()) {
+ Either<GraphRelation, TitanOperationStatus> addMembersRefResult = null;
+ for (Entry<String, String> member : members.entrySet()) {
+ Either<ComponentInstanceData, TitanOperationStatus> findComponentInstanceRes = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), member.getValue(),
+ ComponentInstanceData.class);
+ if (findComponentInstanceRes.isRight()) {
+
+ TitanOperationStatus status = findComponentInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ String description = "Failed to find to find component instance group " + member.getValue()
+ + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description,
+ ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), member.getKey());
+ GraphNode groupData = new UniqueIdData(NodeTypeEnum.Group, groupId);
+ addMembersRefResult = titanGenericDao.createRelation(groupData, findComponentInstanceRes.left().value(),
+ GraphEdgeLabels.GROUP_MEMBER, props);
+ if (addMembersRefResult.isRight()) {
+ TitanOperationStatus status = addMembersRefResult.right().value();
+ String description = "Failed to associate group " + groupData.getUniqueId()
+ + " to component instance " + member.getValue() + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description,
+ ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+ }
+ }
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(groupId, true, false,
+ true);
+
+ return groupFromGraph;
+ }
+
+ public Either<GroupDefinition, TitanOperationStatus> dissociateArtifactsFromGroupOnGraph(String groupId,
+ List<String> artifactsId) {
+
+ if (artifactsId == null || artifactsId.isEmpty()) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ UniqueIdData groupData = new UniqueIdData(NodeTypeEnum.Group, groupId);
+ for (String artifactId : artifactsId) {
+
+ UniqueIdData artifactData = new UniqueIdData(NodeTypeEnum.Group, artifactId);
+ Either<GraphRelation, TitanOperationStatus> deleteRelation = titanGenericDao.deleteRelation(groupData,
+ artifactData, GraphEdgeLabels.GROUP_ARTIFACT_REF);
+ log.trace("After dissociate group {} from artifact {}", groupId, artifactId);
+
+ if (deleteRelation.isRight()) {
+ TitanOperationStatus status = deleteRelation.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ String description = "Failed to diassociate group " + groupId + " from artifact " + artifactId
+ + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+
+ }
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(groupId, true, true,
+ false);
+
+ return groupFromGraph;
+
+ }
+
+ public Either<GroupDefinition, TitanOperationStatus> dissociateMembersFromGroupOnGraph(String groupId,
+ Map<String, String> members) {
+
+ if (members == null || members.isEmpty()) {
+ return Either.right(TitanOperationStatus.OK);
+ }
+
+ UniqueIdData groupData = new UniqueIdData(NodeTypeEnum.Group, groupId);
+ for (Entry<String, String> member : members.entrySet()) {
+
+ UniqueIdData artifactData = new UniqueIdData(NodeTypeEnum.Group, member.getValue());
+ Either<GraphRelation, TitanOperationStatus> deleteRelation = titanGenericDao.deleteRelation(groupData,
+ artifactData, GraphEdgeLabels.GROUP_MEMBER);
+ log.trace("After dissociate group {} from members", groupId, member.getValue());
+
+ if (deleteRelation.isRight()) {
+ TitanOperationStatus status = deleteRelation.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ String description = "Failed to diassociate group " + groupId + " from member " + member.getValue()
+ + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ADDING_GROUP, description, ErrorSeverity.ERROR);
+ return Either.right(status);
+ }
+
+ }
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(groupId, true, true,
+ false);
+
+ return groupFromGraph;
+
+ }
+
+ /**
+ * dissociate artifacts from a group. It do not delete the artifacts !!!
+ *
+ * @param groupId
+ * @param artifactsId
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinition, StorageOperationStatus> dissociateArtifactsFromGroup(String groupId,
+ List<String> artifactsId, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+ Either<GroupDefinition, TitanOperationStatus> titanRes = this.dissociateArtifactsFromGroupOnGraph(groupId,
+ artifactsId);
+
+ if (titanRes.isRight()) {
+ StorageOperationStatus storageOperationStatus = DaoStatusConverter
+ .convertTitanStatusToStorageStatus(titanRes.right().value());
+ result = Either.right(storageOperationStatus);
+ return result;
+ }
+
+ result = Either.left(titanRes.left().value());
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<GroupDefinition, StorageOperationStatus> dissociateMembersFromGroup(String groupId,
+ Map<String, String> members, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+ Either<GroupDefinition, TitanOperationStatus> titanRes = this.dissociateMembersFromGroupOnGraph(groupId,
+ members);
+
+ if (titanRes.isRight()) {
+ StorageOperationStatus storageOperationStatus = DaoStatusConverter
+ .convertTitanStatusToStorageStatus(titanRes.right().value());
+ result = Either.right(storageOperationStatus);
+ return result;
+ }
+
+ result = Either.left(titanRes.left().value());
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Associate artifacts to a given group
+ *
+ * @param groupId
+ * @param artifactsId
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinition, StorageOperationStatus> associateArtifactsToGroup(String groupId,
+ List<String> artifactsId, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<GroupDefinition, TitanOperationStatus> titanRes = this.associateArtifactsToGroupOnGraph(groupId,
+ artifactsId);
+
+ if (titanRes.isRight()) {
+ StorageOperationStatus status = DaoStatusConverter
+ .convertTitanStatusToStorageStatus(titanRes.right().value());
+ result = Either.right(status);
+ }
+
+ result = Either.left(titanRes.left().value());
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Associate artifacts to a given group
+ *
+ * @param groupId
+ * @param artifactsId
+ * @param inTransaction
+ * @return
+ */
+ public Either<GroupDefinition, StorageOperationStatus> associateMembersToGroup(String groupId,
+ Map<String, String> members, boolean inTransaction) {
+
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<GroupDefinition, TitanOperationStatus> titanRes = this.associateMembersToGroupOnGraph(groupId,
+ members);
+
+ if (titanRes.isRight()) {
+ StorageOperationStatus status = DaoStatusConverter
+ .convertTitanStatusToStorageStatus(titanRes.right().value());
+ result = Either.right(status);
+ return result;
+ }
+
+ result = Either.left(titanRes.left().value());
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> updateGroupVersion(List<String> groupsId,
+ boolean inTransaction) {
+
+ Either<List<GroupDefinition>, StorageOperationStatus> result = null;
+
+ try {
+ Either<List<GroupDefinition>, TitanOperationStatus> updateGroupVersionOnGraph = this
+ .updateGroupVersionOnGraph(groupsId);
+
+ if (updateGroupVersionOnGraph.isRight()) {
+ result = Either.right(DaoStatusConverter
+ .convertTitanStatusToStorageStatus(updateGroupVersionOnGraph.right().value()));
+ return result;
+ }
+
+ result = Either.left(updateGroupVersionOnGraph.left().value());
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<GroupDefinition, StorageOperationStatus> updateGroupName(String uniqueId, String newName,
+ boolean inTransaction) {
+ Either<GroupDefinition, StorageOperationStatus> result = null;
+
+ try {
+ Either<GroupDefinition, TitanOperationStatus> updateGroupNameOnGraph = this.updateGroupNameOnGraph(uniqueId,
+ newName);
+
+ if (updateGroupNameOnGraph.isRight()) {
+ result = Either.right(
+ DaoStatusConverter.convertTitanStatusToStorageStatus(updateGroupNameOnGraph.right().value()));
+ return result;
+ }
+
+ result = Either.left(updateGroupNameOnGraph.left().value());
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ BeEcompErrorManager.getInstance().logBeExecuteRollbackError("Rollback on graph");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private Either<GroupDefinition, TitanOperationStatus> updateGroupNameOnGraph(String uniqueId, String newName) {
+
+ Either<GroupDefinition, TitanOperationStatus> groupFromGraph = this.getGroupFromGraph(uniqueId, false, false,
+ false);
+
+ if (groupFromGraph.isRight()) {
+ TitanOperationStatus status = groupFromGraph.right().value();
+ return Either.right(status);
+ } else {
+ GroupDefinition groupDefinition = groupFromGraph.left().value();
+ String version = groupDefinition.getVersion();
+ String newVersion = increaseMajorVersion(version);
+ Integer pvCounter = groupDefinition.getPropertyValueCounter();
+
+ GroupData groupData = new GroupData();
+ groupData.getGroupDataDefinition().setUniqueId(uniqueId);
+ groupData.getGroupDataDefinition().setVersion(newVersion);
+ groupData.getGroupDataDefinition().setName(newName);
+ groupData.getGroupDataDefinition().setPropertyValueCounter(pvCounter);
+
+ Either<GroupData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(groupData, GroupData.class);
+ if (updateNode.isRight()) {
+ return Either.right(updateNode.right().value());
+ } else {
+ groupFromGraph = this.getGroupFromGraph(uniqueId, false, false, false);
+ return groupFromGraph;
+ }
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupTypeOperation.java
new file mode 100644
index 0000000000..4251e503e6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/GroupTypeOperation.java
@@ -0,0 +1,385 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.GroupTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.GroupTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
+import org.openecomp.sdc.be.resources.data.GroupTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("group-type-operation")
+public class GroupTypeOperation extends AbstractOperation implements IGroupTypeOperation {
+
+ String CREATE_FLOW_CONTEXT = "CreateGroupType";
+ String GET_FLOW_CONTEXT = "GetGroupType";
+
+ @Resource
+ private PropertyOperation propertyOperation;
+
+ public GroupTypeOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(GroupTypeOperation.class.getName());
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition) {
+
+ return addGroupType(groupTypeDefinition, false);
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition,
+ boolean inTransaction) {
+
+ Either<GroupTypeDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<GroupTypeData, TitanOperationStatus> eitherStatus = addGroupTypeToGraph(groupTypeDefinition);
+
+ if (eitherStatus.isRight()) {
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError(CREATE_FLOW_CONTEXT,
+ groupTypeDefinition.getType(), eitherStatus.right().value().name());
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+
+ } else {
+ GroupTypeData groupTypeData = eitherStatus.left().value();
+
+ String uniqueId = groupTypeData.getUniqueId();
+ Either<GroupTypeDefinition, StorageOperationStatus> groupTypeRes = this.getGroupType(uniqueId, true);
+
+ if (groupTypeRes.isRight()) {
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError(GET_FLOW_CONTEXT,
+ groupTypeDefinition.getType(), eitherStatus.right().value().name());
+ }
+
+ result = groupTypeRes;
+
+ }
+
+ return result;
+
+ } finally {
+ handleTransactionCommitRollback(inTransaction, result);
+ }
+
+ }
+
+ public Either<GroupTypeDefinition, TitanOperationStatus> getGroupTypeByUid(String uniqueId) {
+
+ Either<GroupTypeDefinition, TitanOperationStatus> result = null;
+
+ Either<GroupTypeData, TitanOperationStatus> groupTypesRes = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.GroupType), uniqueId, GroupTypeData.class);
+
+ if (groupTypesRes.isRight()) {
+ TitanOperationStatus status = groupTypesRes.right().value();
+ log.debug("Group type {} cannot be found in graph. Status is {}", uniqueId, status);
+ return Either.right(status);
+ }
+
+ GroupTypeData gtData = groupTypesRes.left().value();
+ GroupTypeDefinition groupTypeDefinition = new GroupTypeDefinition(gtData.getGroupTypeDataDefinition());
+
+ TitanOperationStatus propertiesStatus = propertyOperation.fillProperties(uniqueId,
+ properList -> groupTypeDefinition.setProperties(properList));
+
+ if (propertiesStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of capability type {}", uniqueId);
+ return Either.right(propertiesStatus);
+ }
+
+ result = Either.left(groupTypeDefinition);
+
+ return result;
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId) {
+
+ return getGroupType(uniqueId, false);
+
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId, boolean inTransaction) {
+ Function<String, Either<GroupTypeDefinition, TitanOperationStatus>> groupTypeGetter = uId -> getGroupTypeByUid(
+ uId);
+ return getElementType(groupTypeGetter, uniqueId, inTransaction);
+
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String type) {
+ return getLatestGroupTypeByType(type, false);
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String type,
+ boolean inTransaction) {
+ Map<String, Object> mapCriteria = new HashMap<>();
+ mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
+ mapCriteria.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ return getGroupTypeByCriteria(type, mapCriteria, inTransaction);
+
+ }
+
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByCriteria(String type,
+ Map<String, Object> properties, boolean inTransaction) {
+ Either<GroupTypeDefinition, StorageOperationStatus> result = null;
+ try {
+ if (type == null || type.isEmpty()) {
+ log.error("type is empty");
+ result = Either.right(StorageOperationStatus.INVALID_ID);
+ return result;
+ }
+
+ Either<List<GroupTypeData>, TitanOperationStatus> groupTypeEither = titanGenericDao
+ .getByCriteria(NodeTypeEnum.GroupType, properties, GroupTypeData.class);
+ if (groupTypeEither.isRight()) {
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(groupTypeEither.right().value()));
+ } else {
+ GroupTypeDataDefinition dataDefinition = groupTypeEither.left().value().stream()
+ .map(e -> e.getGroupTypeDataDefinition()).findFirst().get();
+ result = getGroupType(dataDefinition.getUniqueId(), inTransaction);
+ }
+
+ return result;
+
+ } finally {
+ handleTransactionCommitRollback(inTransaction, result);
+ }
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String type,
+ String version) {
+ return getGroupTypeByTypeAndVersion(type, version, false);
+ }
+
+ @Override
+ public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String type, String version,
+ boolean inTransaction) {
+ Map<String, Object> mapCriteria = new HashMap<>();
+ mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
+ mapCriteria.put(GraphPropertiesDictionary.VERSION.getProperty(), version);
+
+ return getGroupTypeByCriteria(type, mapCriteria, inTransaction);
+ }
+
+ /**
+ *
+ * Add group type to graph.
+ *
+ * 1. Add group type node
+ *
+ * 2. Add edge between the former node to its parent(if exists)
+ *
+ * 3. Add property node and associate it to the node created at #1. (per
+ * property & if exists)
+ *
+ * @param groupTypeDefinition
+ * @return
+ */
+ private Either<GroupTypeData, TitanOperationStatus> addGroupTypeToGraph(GroupTypeDefinition groupTypeDefinition) {
+
+ log.debug("Got group type {}", groupTypeDefinition);
+
+ String ctUniqueId = UniqueIdBuilder.buildGroupTypeUid(groupTypeDefinition.getType(),
+ groupTypeDefinition.getVersion());
+ // capabilityTypeDefinition.setUniqueId(ctUniqueId);
+
+ GroupTypeData groupTypeData = buildGroupTypeData(groupTypeDefinition, ctUniqueId);
+
+ log.debug("Before adding group type to graph. groupTypeData = {}", groupTypeData);
+
+ Either<GroupTypeData, TitanOperationStatus> createGTResult = titanGenericDao.createNode(groupTypeData,
+ GroupTypeData.class);
+ log.debug("After adding group type to graph. status is = {}", createGTResult);
+
+ if (createGTResult.isRight()) {
+ TitanOperationStatus operationStatus = createGTResult.right().value();
+ log.error("Failed to add group type {} to graph. Status is {}", groupTypeDefinition.getType(), operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ GroupTypeData resultCTD = createGTResult.left().value();
+ List<PropertyDefinition> properties = groupTypeDefinition.getProperties();
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapablityType = propertyOperation
+ .addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.GroupType, properties);
+ if (addPropertiesToCapablityType.isRight()) {
+ log.error("Failed add properties {} to capability {}", properties, groupTypeDefinition.getType());
+ return Either.right(addPropertiesToCapablityType.right().value());
+ }
+
+ String derivedFrom = groupTypeDefinition.getDerivedFrom();
+ if (derivedFrom != null) {
+
+ // TODO: Need to find the parent. need to take the latest one since
+ // we may have many versions of the same type
+ /*
+ * log.debug("Before creating relation between group type {} to its parent {}", ctUniqueId, derivedFrom); UniqueIdData from
+ * = new UniqueIdData(NodeTypeEnum.CapabilityType, ctUniqueId);
+ * UniqueIdData to = new UniqueIdData(NodeTypeEnum.CapabilityType,
+ * derivedFrom); Either<GraphRelation, TitanOperationStatus>
+ * createRelation = titanGenericDao .createRelation(from, to,
+ * GraphEdgeLabels.DERIVED_FROM, null);
+ * log.debug("After create relation between capability type {} to its parent {}. Status is {}", ctUniqueId, derivedFrom, if (createRelation.isRight()) { return
+ * Either.right(createRelation.right().value()); }
+ *
+ */
+ }
+
+ return Either.left(createGTResult.left().value());
+
+ }
+
+ /**
+ *
+ * convert between graph Node object to Java object
+ *
+ * @param capabilityTypeData
+ * @return
+ */
+ protected CapabilityTypeDefinition convertCTDataToCTDefinition(CapabilityTypeData capabilityTypeData) {
+ log.debug("The object returned after create capability is {}", capabilityTypeData);
+
+ CapabilityTypeDefinition capabilityTypeDefResult = new CapabilityTypeDefinition(
+ capabilityTypeData.getCapabilityTypeDataDefinition());
+
+ return capabilityTypeDefResult;
+ }
+
+ private GroupTypeData buildGroupTypeData(GroupTypeDefinition groupTypeDefinition, String ctUniqueId) {
+
+ GroupTypeData groupTypeData = new GroupTypeData(groupTypeDefinition);
+
+ groupTypeData.getGroupTypeDataDefinition().setUniqueId(ctUniqueId);
+ Long creationDate = groupTypeData.getGroupTypeDataDefinition().getCreationTime();
+ if (creationDate == null) {
+ creationDate = System.currentTimeMillis();
+ }
+ groupTypeData.getGroupTypeDataDefinition().setCreationTime(creationDate);
+ groupTypeData.getGroupTypeDataDefinition().setModificationTime(creationDate);
+
+ return groupTypeData;
+ }
+
+ public Either<Boolean, StorageOperationStatus> isCapabilityTypeDerivedFrom(String childCandidateType,
+ String parentCandidateType) {
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childCandidateType);
+ Either<List<CapabilityTypeData>, TitanOperationStatus> getResponse = titanGenericDao
+ .getByCriteria(NodeTypeEnum.CapabilityType, propertiesToMatch, CapabilityTypeData.class);
+ if (getResponse.isRight()) {
+ TitanOperationStatus titanOperationStatus = getResponse.right().value();
+ log.debug("Couldn't fetch capability type {}, error: {}", childCandidateType, titanOperationStatus);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
+ }
+ String childUniqueId = getResponse.left().value().get(0).getUniqueId();
+ Set<String> travelledTypes = new HashSet<>();
+ do {
+ travelledTypes.add(childUniqueId);
+ Either<List<ImmutablePair<CapabilityTypeData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childUniqueId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
+ if (childrenNodes.isRight()) {
+ if (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ TitanOperationStatus titanOperationStatus = getResponse.right().value();
+ log.debug("Couldn't fetch derived from node for capability type {}, error: {}", childCandidateType,
+ titanOperationStatus);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
+ } else {
+ log.debug("Derived from node is not found for type {} - this is OK for root capability.");
+ return Either.left(false);
+ }
+ }
+ String derivedFromUniqueId = childrenNodes.left().value().get(0).getLeft().getUniqueId();
+ if (derivedFromUniqueId.equals(parentCandidateType)) {
+ log.debug("Verified that capability type {} derives from capability type {}", childCandidateType,
+ parentCandidateType);
+ return Either.left(true);
+ }
+ childUniqueId = derivedFromUniqueId;
+ } while (!travelledTypes.contains(childUniqueId));
+ // this stop condition should never be used, if we use it, we have an
+ // illegal cycle in graph - "derived from" hierarchy cannot be cycled.
+ // It's here just to avoid infinite loop in case we have such cycle.
+ log.error("Detected a cycle of \"derived from\" edges starting at capability type node {}", childUniqueId);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param propertyOperation
+ */
+ public void setPropertyOperation(PropertyOperation propertyOperation) {
+ this.propertyOperation = propertyOperation;
+ }
+
+ @Override
+ public Either<GroupTypeData, TitanOperationStatus> getLatestGroupTypeByNameFromGraph(String name) {
+
+ return null;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperation.java
new file mode 100644
index 0000000000..5d7b8c5991
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperation.java
@@ -0,0 +1,492 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.heat.HeatParameterType;
+import org.openecomp.sdc.be.model.operations.api.IHeatParametersOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
+import org.openecomp.sdc.be.resources.data.HeatParameterData;
+import org.openecomp.sdc.be.resources.data.HeatParameterValueData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+
+import fj.data.Either;
+
+@Component("heat-parameter-operation")
+public class HeatParametersOperation implements IHeatParametersOperation {
+
+ public static final String EMPTY_VALUE = null;
+
+ private static Logger log = LoggerFactory.getLogger(HeatParametersOperation.class.getName());
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ private Gson gson = new Gson();
+
+ public TitanGenericDao getTitanGenericDao() {
+ return titanGenericDao;
+ }
+
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ public StorageOperationStatus getHeatParametersOfNode(NodeTypeEnum nodeType, String uniqueId, List<HeatParameterDefinition> properties) {
+
+ Either<List<ImmutablePair<HeatParameterData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.HEAT_PARAMETER, NodeTypeEnum.HeatParameter,
+ HeatParameterData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+
+ List<ImmutablePair<HeatParameterData, GraphEdge>> values = childrenNodes.left().value();
+ if (values != null) {
+
+ for (ImmutablePair<HeatParameterData, GraphEdge> immutablePair : values) {
+ GraphEdge edge = immutablePair.getValue();
+ String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+ if (log.isDebugEnabled())
+ log.debug("Property {} is associated to node {}", propertyName, uniqueId);
+ HeatParameterData propertyData = immutablePair.getKey();
+ HeatParameterDefinition propertyDefinition = convertParameterDataToParameterDefinition(propertyData, propertyName, uniqueId);
+
+ properties.add(propertyDefinition);
+
+ if (log.isTraceEnabled()) {
+ log.trace("getHeatParametersOfNode - property {} associated to node {}", propertyDefinition, uniqueId);
+ }
+ }
+
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ public StorageOperationStatus getParametersValueNodes(NodeTypeEnum parentNodeType, String parentUniqueId, List<HeatParameterValueData> heatValues) {
+
+ Either<List<ImmutablePair<HeatParameterValueData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(parentNodeType), parentUniqueId, GraphEdgeLabels.PARAMETER_VALUE,
+ NodeTypeEnum.HeatParameterValue, HeatParameterValueData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+
+ List<ImmutablePair<HeatParameterValueData, GraphEdge>> values = childrenNodes.left().value();
+ if (values != null) {
+
+ for (ImmutablePair<HeatParameterValueData, GraphEdge> immutablePair : values) {
+ GraphEdge edge = immutablePair.getValue();
+ String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+ log.trace("Heat value " + propertyName + " is associated to node " + parentUniqueId);
+ HeatParameterValueData propertyData = immutablePair.getKey();
+
+ heatValues.add(propertyData);
+ }
+
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<List<HeatParameterDefinition>, StorageOperationStatus> deleteAllHeatParametersAssociatedToNode(NodeTypeEnum nodeType, String uniqueId) {
+
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ StorageOperationStatus propertiesOfNodeRes = getHeatParametersOfNode(nodeType, uniqueId, heatParams);
+
+ if (!propertiesOfNodeRes.equals(StorageOperationStatus.OK) && !propertiesOfNodeRes.equals(StorageOperationStatus.NOT_FOUND)) {
+ return Either.right(propertiesOfNodeRes);
+ }
+
+ for (HeatParameterDefinition propertyDefinition : heatParams) {
+
+ String propertyUid = propertyDefinition.getUniqueId();
+ Either<HeatParameterData, TitanOperationStatus> deletePropertyRes = deleteHeatParameterFromGraph(propertyUid);
+ if (deletePropertyRes.isRight()) {
+ log.error("Failed to delete heat parameter with id {}", propertyUid);
+ TitanOperationStatus status = deletePropertyRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ }
+
+ log.debug("The heat parameters deleted from node {} are {}", uniqueId, heatParams);
+ return Either.left(heatParams);
+ }
+
+ @Override
+ public StorageOperationStatus deleteAllHeatValuesAssociatedToNode(NodeTypeEnum parentNodeType, String parentUniqueId) {
+
+ List<HeatParameterValueData> heatValues = new ArrayList<HeatParameterValueData>();
+ StorageOperationStatus propertiesOfNodeRes = getParametersValueNodes(parentNodeType, parentUniqueId, heatValues);
+
+ if (!propertiesOfNodeRes.equals(StorageOperationStatus.OK) && !propertiesOfNodeRes.equals(StorageOperationStatus.NOT_FOUND)) {
+ return propertiesOfNodeRes;
+ }
+
+ for (HeatParameterValueData propertyDefinition : heatValues) {
+
+ String propertyUid = (String) propertyDefinition.getUniqueId();
+ Either<HeatParameterValueData, TitanOperationStatus> deletePropertyRes = deleteHeatParameterValueFromGraph(propertyUid);
+ if (deletePropertyRes.isRight()) {
+ log.error("Failed to delete heat parameter value with id {}", propertyUid);
+ TitanOperationStatus status = deletePropertyRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+
+ }
+
+ log.debug("The heat values deleted from node {} are {}", parentUniqueId, heatValues);
+ return StorageOperationStatus.OK;
+ }
+
+ private Either<HeatParameterData, TitanOperationStatus> deleteHeatParameterFromGraph(String propertyId) {
+ log.debug("Before deleting heat parameter from graph {}", propertyId);
+ return titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.HeatParameter), propertyId, HeatParameterData.class);
+ }
+
+ private Either<HeatParameterValueData, TitanOperationStatus> deleteHeatParameterValueFromGraph(String propertyId) {
+ log.debug("Before deleting heat parameter from graph {}", propertyId);
+ return titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.HeatParameterValue), propertyId, HeatParameterValueData.class);
+ }
+
+ @Override
+ public StorageOperationStatus addPropertiesToGraph(List<HeatParameterDefinition> properties, String parentId, NodeTypeEnum nodeType) {
+
+ if (properties != null) {
+ for (HeatParameterDefinition propertyDefinition : properties) {
+
+ String propertyName = propertyDefinition.getName();
+
+ // type and value should be validated in business logic:
+ // ArtifactsBusinessLogic.validateAndConvertHeatParamers(ArtifactDefinition)
+
+ // StorageOperationStatus validateAndUpdateProperty =
+ // validateAndUpdateProperty(propertyDefinition);
+ // if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ // log.error("Property " + propertyDefinition + " is invalid.
+ // Status is " + validateAndUpdateProperty);
+ // return StorageOperationStatus.BAD_REQUEST;
+ // }
+
+ Either<HeatParameterData, TitanOperationStatus> addPropertyToGraph = addPropertyToGraph(propertyName, propertyDefinition, parentId, nodeType);
+
+ if (addPropertyToGraph.isRight()) {
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertyToGraph.right().value());
+ }
+ }
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ @Override
+ public StorageOperationStatus updateHeatParameters(List<HeatParameterDefinition> properties) {
+
+ if (properties == null) {
+ return StorageOperationStatus.OK;
+ }
+ for (HeatParameterDefinition property : properties) {
+
+ HeatParameterData heatParameterData = new HeatParameterData(property);
+ Either<HeatParameterData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(heatParameterData, HeatParameterData.class);
+ if (updateNode.isRight()) {
+ log.debug("failed to update heat parameter in graph. id = {}", property.getUniqueId());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value());
+ }
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ public Either<HeatParameterData, TitanOperationStatus> addPropertyToGraph(String propertyName, HeatParameterDefinition propertyDefinition, String parentId, NodeTypeEnum nodeType) {
+
+ UniqueIdData parentNode = new UniqueIdData(nodeType, parentId);
+
+ propertyDefinition.setUniqueId(UniqueIdBuilder.buildHeatParameterUniqueId(parentId, propertyName));
+ HeatParameterData propertyData = new HeatParameterData(propertyDefinition);
+
+ log.debug("Before adding property to graph {}", propertyData);
+ Either<HeatParameterData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyData, HeatParameterData.class);
+ log.debug("After adding property to graph {}", propertyData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add property {} to graph. Status is {}", propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(parentNode, propertyData, GraphEdgeLabels.HEAT_PARAMETER, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+
+ if (log.isDebugEnabled()) {
+ log.error("Failed to associate {} {} to heat parameter {} in graph. Status is {}", nodeType.getName(), parentId, propertyName, operationStatus);
+ }
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+ public StorageOperationStatus validateAndUpdateProperty(HeatParameterDefinition propertyDefinition) {
+
+ log.trace("Going to validate property type and value. {}", propertyDefinition);
+
+ String propertyType = propertyDefinition.getType();
+ HeatParameterType type = getType(propertyType);
+
+ if (type == null) {
+ log.info("The type {} of heat is invalid", type);
+
+ return StorageOperationStatus.INVALID_TYPE;
+ }
+ propertyDefinition.setType(type.getType());
+
+ log.trace("After validating property type {}", propertyType);
+
+ // validate default value
+ String defaultValue = propertyDefinition.getDefaultValue();
+ boolean isValidProperty = isValidValue(type, defaultValue);
+ if (false == isValidProperty) {
+ log.info("The value {} of property from type {} is invalid", defaultValue, type);
+ return StorageOperationStatus.INVALID_VALUE;
+ }
+
+ PropertyValueConverter converter = type.getConverter();
+
+ if (isEmptyValue(defaultValue)) {
+ log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
+ propertyDefinition.setDefaultValue(EMPTY_VALUE);
+ } else if (false == isEmptyValue(defaultValue)) {
+ String convertedValue = converter.convert(defaultValue, null, null);
+ propertyDefinition.setDefaultValue(convertedValue);
+ }
+
+ // validate current value
+ String value = propertyDefinition.getCurrentValue();
+ isValidProperty = isValidValue(type, value);
+ if (false == isValidProperty) {
+ log.info("The value {} of property from type {} is invalid", value, type);
+ return StorageOperationStatus.INVALID_VALUE;
+ }
+
+ if (isEmptyValue(value)) {
+ log.debug("Value was not sent for property {}. Set value to {}", propertyDefinition.getName(), EMPTY_VALUE);
+ propertyDefinition.setCurrentValue(EMPTY_VALUE);
+ } else if (!value.equals("")) {
+ String convertedValue = converter.convert(value, null, null);
+ propertyDefinition.setCurrentValue(convertedValue);
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ public HeatParameterDefinition convertParameterDataToParameterDefinition(HeatParameterData propertyDataResult, String propertyName, String resourceId) {
+ log.debug("convert to HeatParamereDefinition {}", propertyDataResult);
+
+ HeatParameterDefinition propertyDefResult = new HeatParameterDefinition(propertyDataResult.getHeatDataDefinition());
+
+ propertyDefResult.setName(propertyName);
+
+ return propertyDefResult;
+ }
+
+ private HeatParameterType getType(String propertyType) {
+
+ HeatParameterType type = HeatParameterType.isValidType(propertyType);
+
+ return type;
+
+ }
+
+ protected boolean isValidValue(HeatParameterType type, String value) {
+ if (isEmptyValue(value)) {
+ return true;
+ }
+
+ PropertyTypeValidator validator = type.getValidator();
+
+ boolean isValid = validator.isValid(value, null, null);
+ if (true == isValid) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ public boolean isEmptyValue(String value) {
+ if (value == null) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isNullParam(String value) {
+ if (value == null) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Either<HeatParameterValueData, StorageOperationStatus> updateHeatParameterValue(HeatParameterDefinition heatParam, String artifactId, String resourceInstanceId, String artifactLabel) {
+ String heatEnvId = UniqueIdBuilder.buildHeatParameterValueUniqueId(resourceInstanceId, artifactLabel, heatParam.getName());
+ Either<HeatParameterValueData, TitanOperationStatus> getNode = titanGenericDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), heatEnvId, HeatParameterValueData.class);
+ if (getNode.isRight() || getNode.left().value() == null) {
+ if (heatParam.getCurrentValue() == null || (heatParam.getDefaultValue() != null && heatParam.getCurrentValue().equals(heatParam.getDefaultValue()))) {
+ log.debug("Updated heat parameter value equals default value. No need to create heat parameter value for heat parameter {}", heatParam.getUniqueId());
+ return Either.left(null);
+ }
+ return createHeatParameterValue(heatParam, artifactId, resourceInstanceId, artifactLabel);
+ } else {
+ heatParam.setUniqueId(heatEnvId);
+ return updateHeatParameterValue(heatParam);
+ }
+ }
+
+ public Either<HeatParameterValueData, StorageOperationStatus> updateHeatParameterValue(HeatParameterDefinition heatParam) {
+ HeatParameterValueData heatParameterValue = new HeatParameterValueData();
+ heatParameterValue.setUniqueId(heatParam.getUniqueId());
+ if (heatParam.getCurrentValue() == null || (heatParam.getDefaultValue() != null && heatParam.getCurrentValue().equals(heatParam.getDefaultValue()))) {
+ Either<GraphRelation, TitanOperationStatus> deleteParameterValueIncomingRelation = titanGenericDao.deleteIncomingRelationByCriteria(heatParameterValue, GraphEdgeLabels.PARAMETER_VALUE, null);
+ if (deleteParameterValueIncomingRelation.isRight()) {
+ log.debug("Failed to delete heat parameter value incoming relation on graph. id = {}", heatParameterValue.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(deleteParameterValueIncomingRelation.right().value()));
+ }
+ Either<Edge, TitanOperationStatus> getOutgoingRelation = titanGenericDao.getOutgoingEdgeByCriteria(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), (String) heatParameterValue.getUniqueId(), GraphEdgeLabels.PARAMETER_IMPL, null);
+ if (getOutgoingRelation.isRight()) {
+ log.debug("Failed to get heat parameter value outgoing relation from graph. id = {}", heatParameterValue.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getOutgoingRelation.right().value()));
+ }
+ Edge edge = getOutgoingRelation.left().value();
+ if (edge == null) {
+ log.debug("Failed to get heat parameter value outgoing relation from graph. id = {}", heatParameterValue.getUniqueId());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ edge.remove();
+
+ Either<HeatParameterValueData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(heatParameterValue, HeatParameterValueData.class);
+ if (deleteNode.isRight()) {
+ log.debug("Failed to delete heat parameter value on graph. id = {}", heatParameterValue.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(deleteNode.right().value()));
+ }
+ return Either.left(deleteNode.left().value());
+ }
+ heatParameterValue.setValue(heatParam.getCurrentValue());
+ Either<HeatParameterValueData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(heatParameterValue, HeatParameterValueData.class);
+ if (updateNode.isRight()) {
+ log.debug("Failed to update heat parameter value in graph. id = {}", heatParameterValue.getUniqueId());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value()));
+ }
+ return Either.left(updateNode.left().value());
+ }
+
+ public Either<HeatParameterValueData, StorageOperationStatus> createHeatParameterValue(HeatParameterDefinition heatParam, String artifactId, String resourceInstanceId, String artifactLabel) {
+
+ Either<HeatParameterValueData, TitanOperationStatus> addHeatValueToGraph = addHeatValueToGraph(heatParam, artifactLabel, artifactId, resourceInstanceId);
+ if (addHeatValueToGraph.isRight()) {
+ log.debug("Failed to create heat parameters value on graph for artifact {}", artifactId);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addHeatValueToGraph.right().value()));
+ }
+ return Either.left(addHeatValueToGraph.left().value());
+ }
+
+ public Either<HeatParameterValueData, TitanOperationStatus> addHeatValueToGraph(HeatParameterDefinition heatParameter, String artifactLabel, String artifactId, String resourceInstanceId) {
+
+ UniqueIdData heatEnvNode = new UniqueIdData(NodeTypeEnum.ArtifactRef, artifactId);
+ HeatParameterValueData heatValueData = new HeatParameterValueData();
+ heatValueData.setUniqueId(UniqueIdBuilder.buildHeatParameterValueUniqueId(resourceInstanceId, artifactLabel, heatParameter.getName()));
+ heatValueData.setValue(heatParameter.getCurrentValue());
+
+ log.debug("Before adding property to graph {}", heatValueData);
+ Either<HeatParameterValueData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(heatValueData, HeatParameterValueData.class);
+ log.debug("After adding property to graph {}", heatValueData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add heat value {} to graph. Status is {}", heatValueData.getUniqueId(), operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), heatParameter.getName());
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(heatEnvNode, heatValueData, GraphEdgeLabels.PARAMETER_VALUE, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.error("Failed to associate heat value {} to heat env artifact {} in graph. Status is {}", heatValueData.getUniqueId(), artifactId, operationStatus);
+ return Either.right(operationStatus);
+ }
+ UniqueIdData heatParameterNode = new UniqueIdData(NodeTypeEnum.HeatParameter, heatParameter.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> createRel2Result = titanGenericDao.createRelation(heatValueData, heatParameterNode, GraphEdgeLabels.PARAMETER_IMPL, null);
+ if (createRel2Result.isRight()) {
+ TitanOperationStatus operationStatus = createRel2Result.right().value();
+ log.error("Failed to associate heat value {} to heat parameter {} in graph. Status is {}", heatValueData.getUniqueId(), heatParameter.getName(), operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InputsOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InputsOperation.java
new file mode 100644
index 0000000000..e2d13a9cff
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InputsOperation.java
@@ -0,0 +1,1184 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.json.simple.JSONObject;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstInputsMap;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GetInputValueInfo;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.operations.api.IInputsOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.AttributeData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.InputValueData;
+import org.openecomp.sdc.be.resources.data.InputsData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.TitanVertexQuery;
+import com.thinkaurelius.titan.core.attribute.Cmp;
+
+import fj.data.Either;
+
+@Component("input-operation")
+public class InputsOperation extends AbstractOperation implements IInputsOperation {
+
+ private static String ASSOCIATING_INPUT_TO_PROP = "AssociatingInputToComponentInstanceProperty";
+
+ private static Logger log = LoggerFactory.getLogger(InputsOperation.class.getName());
+
+ @Autowired
+ private ComponentInstanceOperation componentInstanceOperation;
+ Gson gson = new Gson();
+
+ /**
+ * Delete specific input from component Although inputId is unique, pass also componentId as all other methods, and also check that the inputId is inside that componentId.
+ */
+ @Override
+ public Either<String, StorageOperationStatus> deleteInput(String inputId) {
+ log.debug(String.format("Before deleting input: %s from graph", inputId));
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> inputsValueStatus = this.getComponentInstanceInputsByInputId(inputId);
+ if(inputsValueStatus.isLeft()){
+ List<ComponentInstanceInput> inputsValueLis = inputsValueStatus.left().value();
+ if(!inputsValueLis.isEmpty()){
+ for(ComponentInstanceInput inputValue: inputsValueLis){
+ Either<InputValueData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.InputValue), inputValue.getValueUniqueUid(), InputValueData.class);
+ if (deleteNode.isRight()) {
+ StorageOperationStatus convertTitanStatusToStorageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteNode.right().value());
+ return Either.right(convertTitanStatusToStorageStatus);
+ }
+ }
+ }
+ }
+ Either<InputsData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Input), inputId, InputsData.class);
+ if (deleteNode.isRight()) {
+ StorageOperationStatus convertTitanStatusToStorageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteNode.right().value());
+ return Either.right(convertTitanStatusToStorageStatus);
+ } else {
+ return Either.left(inputId);
+ }
+ }
+
+ @Override
+ public Either<List<InputDefinition>, TitanOperationStatus> addInputsToGraph(String componentId, NodeTypeEnum nodeType, Map<String, InputDefinition> inputs, Map<String, DataTypeDefinition> dataTypes) {
+
+ List<InputDefinition> newInputs = new ArrayList<InputDefinition>();
+ if (inputs != null) {
+ for (Entry<String, InputDefinition> entry : inputs.entrySet()) {
+
+ String inputName = entry.getKey();
+ InputDefinition propertyDefinition = entry.getValue();
+
+ StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(propertyDefinition, dataTypes);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ log.error("Property " + propertyDefinition + " is invalid. Status is " + validateAndUpdateProperty);
+ return Either.right(TitanOperationStatus.INVALID_PROPERTY);
+ }
+
+ Either<InputsData, TitanOperationStatus> addPropertyToGraph = addInputToGraph(inputName, propertyDefinition, componentId, nodeType);
+
+ if (addPropertyToGraph.isRight()) {
+ return Either.right(addPropertyToGraph.right().value());
+ }
+ InputDefinition createdInputyDefinition = convertInputDataToInputDefinition(addPropertyToGraph.left().value());
+ createdInputyDefinition.setName(inputName);
+ createdInputyDefinition.setParentUniqueId(componentId);
+ newInputs.add(createdInputyDefinition);
+ }
+ }
+
+ return Either.left(newInputs);
+ }
+
+ @Override
+ public TitanOperationStatus addInputsToGraph(TitanVertex metadata, String componentId, Map<String, InputDefinition> inputs, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (inputs != null) {
+ for (Entry<String, InputDefinition> entry : inputs.entrySet()) {
+
+ String inputName = entry.getKey();
+ InputDefinition propertyDefinition = entry.getValue();
+
+ StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(propertyDefinition, dataTypes);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ log.error("Property {} is invalid. Status is {} ", propertyDefinition, validateAndUpdateProperty);
+ return TitanOperationStatus.INVALID_PROPERTY;
+ }
+
+ TitanOperationStatus addPropertyToGraph = addInputToGraph(metadata, inputName, propertyDefinition, componentId);
+
+ if (!addPropertyToGraph.equals(TitanOperationStatus.OK)) {
+ return addPropertyToGraph;
+ }
+
+ }
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ @Override
+ public Either<List<InputDefinition>, StorageOperationStatus> getInputsOfComponent(String compId, String fromName, int amount) {
+ List<InputDefinition> inputs = new ArrayList<>();
+ if ((fromName == null || fromName.isEmpty()) && amount == 0) {
+
+ TitanOperationStatus status = findAllResourceInputs(compId, inputs);
+
+ if (status != TitanOperationStatus.OK) {
+ log.error("Failed to set inputs of resource {}. status is {}", compId, status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ } else {
+
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanGenericDao.getGraph();
+ if (graphRes.isRight()) {
+ log.error("Failed to retrieve graph. status is {}", graphRes);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphRes.right().value()));
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = titanGraph.query().has(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), compId).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.INVALID_ID));
+ }
+
+ TitanVertex rootVertex = vertices.iterator().next();
+ TitanVertexQuery<?> query;
+ if (fromName == null || fromName.isEmpty())
+ query = rootVertex.query().direction(Direction.OUT).labels(GraphEdgeLabels.INPUT.getProperty()).orderBy(GraphEdgePropertiesDictionary.NAME.getProperty(), Order.incr).limit(amount);
+ else
+ query = rootVertex.query().direction(Direction.OUT).labels(GraphEdgeLabels.INPUT.getProperty()).orderBy(GraphEdgePropertiesDictionary.NAME.getProperty(), Order.incr).has(GraphEdgePropertiesDictionary.NAME.getProperty(), Cmp.GREATER_THAN, fromName).limit(amount);
+
+ Iterable<TitanEdge> edgesCreatorEges = query.edges();
+
+ if (edgesCreatorEges == null) {
+ log.debug("No edges in graph for criteria");
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.NOT_FOUND));
+ }
+ Iterator<TitanEdge> edgesCreatorIterator = edgesCreatorEges.iterator();
+
+ if (edgesCreatorIterator != null) {
+ while (edgesCreatorIterator.hasNext()) {
+ Edge edge = edgesCreatorIterator.next();
+ GraphEdge graphEdge = null;
+
+ Map<String, Object> edgeProps = titanGenericDao.getProperties(edge);
+ GraphEdgeLabels edgeTypeFromGraph = GraphEdgeLabels.getByName(edge.label());
+ graphEdge = new GraphEdge(edgeTypeFromGraph, edgeProps);
+
+ Vertex outgoingVertex = edge.inVertex();
+ Map<String, Object> properties = titanGenericDao.getProperties(outgoingVertex);
+ InputsData data = GraphElementFactory.createElement(NodeTypeEnum.Input.getName(), GraphElementTypeEnum.Node, properties, InputsData.class);
+ String inputName = (String) graphEdge.getProperties().get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ InputDefinition inputDefinition = this.convertInputDataToInputDefinition(data);
+ inputDefinition.setName(inputName);
+ inputDefinition.setParentUniqueId(compId);
+
+ inputs.add(inputDefinition);
+
+ }
+ }
+
+ }
+ if (true == inputs.isEmpty()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.NOT_FOUND));
+ }
+
+ return Either.left(inputs);
+
+ }
+
+ @Override
+ public Either<Map<String, InputDefinition>, StorageOperationStatus> deleteAllInputsAssociatedToNode(NodeTypeEnum nodeType, String uniqueId) {
+
+ Wrapper<TitanOperationStatus> errorWrapper;
+ List<InputDefinition> inputs = new ArrayList<>();
+ TitanOperationStatus findAllResourceAttribues = this.findNodeNonInheretedInputs(uniqueId, inputs);
+ errorWrapper = (findAllResourceAttribues != TitanOperationStatus.OK) ? new Wrapper<>(findAllResourceAttribues) : new Wrapper<>();
+
+ if (errorWrapper.isEmpty()) {
+ for (InputDefinition inDef : inputs) {
+ log.debug("Before deleting inputs from graph {}", inDef.getUniqueId());
+ Either<InputsData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Input), inDef.getUniqueId(), InputsData.class);
+ if (deleteNode.isRight()) {
+ errorWrapper.setInnerElement(deleteNode.right().value());
+ break;
+ }
+ }
+ }
+
+ if (errorWrapper.isEmpty()) {
+ Map<String, InputDefinition> inputsMap = inputs.stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
+ return Either.left(inputsMap);
+ } else {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(errorWrapper.getInnerElement()));
+ }
+
+ }
+
+ @Override
+ public Either<InputsData, StorageOperationStatus> addInput(String inputName, InputDefinition inputDefinition, String resourceId, NodeTypeEnum nodeType) {
+
+ ComponentMetadataData componentMetadata = null;
+
+ Either<InputsData, TitanOperationStatus> either = addInputToGraph(inputName, inputDefinition, resourceId, nodeType);
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ return Either.right(storageStatus);
+ }
+ return Either.left(either.left().value());
+ }
+
+ @Override
+ public Either<AttributeData, StorageOperationStatus> updateInput(String inputId, InputDefinition newInDef, Map<String, DataTypeDefinition> dataTypes) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Either<InputsData, TitanOperationStatus> addInputToGraph(String propertyName, InputDefinition inputDefinition, String componentId, NodeTypeEnum nodeType) {
+
+ UniqueIdData from = new UniqueIdData(nodeType, componentId);
+
+ List<PropertyConstraint> constraints = inputDefinition.getConstraints();
+
+ inputDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName));
+ InputsData inputData = new InputsData(inputDefinition, convertConstraintsToString(constraints));
+
+ log.debug("Before adding property to graph {}", inputData);
+ Either<InputsData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(inputData, InputsData.class);
+ log.debug("After adding input to graph {}", inputData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add input {} to graph. status is {}", propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), propertyName);
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(from, inputData, GraphEdgeLabels.INPUT, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to associate resource {} to property {} in graph. status is {}", componentId, propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+ public TitanOperationStatus addInputToGraph(TitanVertex vertex, String propertyName, InputDefinition inputDefinition, String componentId) {
+
+ List<PropertyConstraint> constraints = inputDefinition.getConstraints();
+
+ inputDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName));
+ InputsData inputData = new InputsData(inputDefinition, convertConstraintsToString(constraints));
+
+ log.debug("Before adding property to graph {}", inputData);
+ Either<TitanVertex, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(inputData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add input {} to graph. status is {}", propertyName, operationStatus);
+ return operationStatus;
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), propertyName);
+ TitanVertex inputVertex = createNodeResult.left().value();
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(vertex, inputVertex, GraphEdgeLabels.INPUT, props);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ TitanOperationStatus operationStatus = createRelResult;
+ log.error("Failed to associate resource {} to property {} in graph. status is {}", componentId, propertyName, operationStatus);
+ return operationStatus;
+ }
+
+ return createRelResult;
+
+ }
+
+ public InputDefinition convertInputDataToInputDefinition(InputsData inputDataResult) {
+ if (log.isDebugEnabled())
+ log.debug("The object returned after create property is {}", inputDataResult);
+
+ InputDefinition propertyDefResult = new InputDefinition(inputDataResult.getPropertyDataDefinition());
+ propertyDefResult.setConstraints(convertConstraints(inputDataResult.getConstraints()));
+
+ return propertyDefResult;
+ }
+
+ public boolean isInputExist(List<InputDefinition> inputs, String resourceUid, String inputName) {
+
+ if (inputs == null) {
+ return false;
+ }
+
+ for (InputDefinition propertyDefinition : inputs) {
+ String parentUniqueId = propertyDefinition.getParentUniqueId();
+ String name = propertyDefinition.getName();
+
+ if (parentUniqueId.equals(resourceUid) && name.equals(inputName)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public TitanOperationStatus findAllResourceInputs(String uniqueId, List<InputDefinition> inputs) {
+ // final NodeElementFetcher<InputDefinition> singleNodeFetcher =
+ // (resourceIdParam, attributesParam) ->
+ // findNodeNonInheretedInputs(resourceIdParam, componentType,
+ // attributesParam);
+ // return findAllResourceElementsDefinitionRecursively(uniqueId, inputs,
+ // singleNodeFetcher);
+ return findNodeNonInheretedInputs(uniqueId, inputs);
+ }
+
+ @Override
+ public TitanOperationStatus findNodeNonInheretedInputs(String uniqueId, List<InputDefinition> inputs) {
+ Either<List<ImmutablePair<InputsData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueId, GraphEdgeLabels.INPUT, NodeTypeEnum.Input, InputsData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ return status;
+ }
+
+ List<ImmutablePair<InputsData, GraphEdge>> values = childrenNodes.left().value();
+ if (values != null) {
+
+ for (ImmutablePair<InputsData, GraphEdge> immutablePair : values) {
+ GraphEdge edge = immutablePair.getValue();
+ String inputName = (String) edge.getProperties().get(GraphEdgePropertiesDictionary.NAME.getProperty());
+ log.debug("Input {} is associated to node {}", inputName, uniqueId);
+ InputsData inputData = immutablePair.getKey();
+ InputDefinition inputDefinition = this.convertInputDataToInputDefinition(inputData);
+
+ inputDefinition.setName(inputName);
+ inputDefinition.setParentUniqueId(uniqueId);
+
+ inputs.add(inputDefinition);
+
+ log.trace("findInputsOfNode - input {} associated to node {}", inputDefinition, uniqueId);
+ }
+
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ public Either<InputDefinition, StorageOperationStatus> getInputById(String uniqueId, boolean skipProperties, boolean skipInputsvalue) {
+ Either<InputDefinition, TitanOperationStatus> status = getInputFromGraph(uniqueId, skipProperties, skipInputsvalue);
+
+ if (status.isRight()) {
+ log.error("Failed to get input {} from graph {}. status is {}", uniqueId, status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ }
+
+ return Either.left(status.left().value());
+
+ }
+
+ public <ElementDefinition> TitanOperationStatus findAllResourceElementsDefinitionRecursively(String resourceId, List<ElementDefinition> elements, NodeElementFetcher<ElementDefinition> singleNodeFetcher) {
+
+ log.trace("Going to fetch elements under resource {}", resourceId);
+ TitanOperationStatus resourceAttributesStatus = singleNodeFetcher.findAllNodeElements(resourceId, elements);
+
+ if (resourceAttributesStatus != TitanOperationStatus.OK) {
+ return resourceAttributesStatus;
+ }
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (parentNodesStatus != TitanOperationStatus.NOT_FOUND) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("findAllResourceElementsDefinitionRecursively", "Failed to find parent elements of resource " + resourceId + ". status is " + parentNodesStatus, ErrorSeverity.ERROR);
+ return parentNodesStatus;
+ }
+ }
+
+ if (parentNodes.isLeft()) {
+ ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentIntStatus = findAllResourceElementsDefinitionRecursively(parentUniqueId, elements, singleNodeFetcher);
+
+ if (addParentIntStatus != TitanOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("findAllResourceElementsDefinitionRecursively", "Failed to find all resource elements of resource " + parentUniqueId, ErrorSeverity.ERROR);
+
+ return addParentIntStatus;
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ public TitanOperationStatus associatePropertyToInput(String riId, String inputId, InputValueData property, String name) {
+ TitanOperationStatus status = TitanOperationStatus.OK;
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanGenericDao.getGraph();
+ if (graphRes.isRight()) {
+ log.error("Failed to retrieve graph. status is {}", graphRes);
+ return graphRes.right().value();
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = titanGraph.query().has(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), property.getUniqueId()).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return TitanOperationStatus.INVALID_ID;
+ }
+ // Either<PropertyData, TitanOperationStatus> findPropertyDefRes =
+ // titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property),
+ // propertyId, PropertyData.class);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), name);
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), riId);
+
+ GraphNode inputData = new UniqueIdData(NodeTypeEnum.Input, inputId);
+ GraphNode propertyData = new UniqueIdData(NodeTypeEnum.InputValue, property.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> addPropRefResult = titanGenericDao.createRelation(inputData, propertyData, GraphEdgeLabels.GET_INPUT, props);
+
+ if (addPropRefResult.isRight()) {
+ status = addPropRefResult.right().value();
+ String description = "Failed to associate input " + inputData.getUniqueId() + " to property " + property.getUniqueId() + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ASSOCIATING_INPUT_TO_PROP, description, ErrorSeverity.ERROR);
+ return status;
+ }
+ return status;
+
+ }
+
+ public TitanOperationStatus associatePropertyToInput(String riId, String inputId, ComponentInstanceProperty property, GetInputValueInfo getInput) {
+ TitanOperationStatus status = TitanOperationStatus.OK;
+ Either<TitanGraph, TitanOperationStatus> graphRes = titanGenericDao.getGraph();
+ if (graphRes.isRight()) {
+ log.error("Failed to retrieve graph. status is {}", graphRes);
+ return graphRes.right().value();
+ }
+
+ TitanGraph titanGraph = graphRes.left().value();
+ @SuppressWarnings("unchecked")
+ Iterable<TitanVertex> vertices = titanGraph.query().has(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), property.getUniqueId()).vertices();
+ if (vertices == null || false == vertices.iterator().hasNext()) {
+ return TitanOperationStatus.INVALID_ID;
+ }
+ // Either<PropertyData, TitanOperationStatus> findPropertyDefRes =
+ // titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property),
+ // propertyId, PropertyData.class);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ if(getInput!=null){
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), getInput.getPropName());
+ if (getInput.isList()) {
+ String index = getInput.getIndexValue().toString();
+ if (getInput.getGetInputIndex() != null) {
+ index = getInput.getGetInputIndex().getInputName();
+
+ }
+ props.put(GraphEdgePropertiesDictionary.GET_INPUT_INDEX.getProperty(), index);
+ }
+ }
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), riId);
+
+ GraphNode inputData = new UniqueIdData(NodeTypeEnum.Input, inputId);
+ GraphNode propertyData = new UniqueIdData(NodeTypeEnum.PropertyValue, property.getValueUniqueUid());
+ Either<GraphRelation, TitanOperationStatus> addPropRefResult = titanGenericDao.createRelation(inputData, propertyData, GraphEdgeLabels.GET_INPUT, props);
+
+ if (addPropRefResult.isRight()) {
+ status = addPropRefResult.right().value();
+ String description = "Failed to associate input " + inputData.getUniqueId() + " to property " + property.getUniqueId() + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ASSOCIATING_INPUT_TO_PROP, description, ErrorSeverity.ERROR);
+ return status;
+ }
+ return status;
+
+ }
+
+ private Either<InputDefinition, TitanOperationStatus> getInputFromGraph(String uniqueId, boolean skipProperties, boolean skipInputsValue) {
+
+ Either<InputDefinition, TitanOperationStatus> result = null;
+
+ Either<InputsData, TitanOperationStatus> inputRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Input), uniqueId, InputsData.class);
+ if (inputRes.isRight()) {
+ TitanOperationStatus status = inputRes.right().value();
+ log.debug("Failed to retrieve group {} from graph. Status is {}", uniqueId, status);
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("Fetch Group", uniqueId, String.valueOf(status));
+ result = Either.right(status);
+ return result;
+ }
+
+ InputsData inputData = inputRes.left().value();
+
+ InputDefinition groupDefinition = this.convertInputDataToInputDefinition(inputData);
+
+ if (false == skipInputsValue) {
+ List<ComponentInstanceInput> propsList = new ArrayList<ComponentInstanceInput>();
+
+ Either<List<ImmutablePair<InputValueData, GraphEdge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Input), uniqueId, GraphEdgeLabels.GET_INPUT, NodeTypeEnum.InputValue, InputValueData.class);
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ return Either.right(status);
+ }
+
+ }
+ if(propertyImplNodes.isLeft()){
+ List<ImmutablePair<InputValueData, GraphEdge>> propertyDataPairList = propertyImplNodes.left().value();
+ for (ImmutablePair<InputValueData, GraphEdge> propertyValue : propertyDataPairList) {
+
+ InputValueData propertyValueData = propertyValue.getLeft();
+ String propertyValueUid = propertyValueData.getUniqueId();
+ String value = propertyValueData.getValue();
+
+ Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> propertyDefRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.InputValue), propertyValueUid, GraphEdgeLabels.INPUT_IMPL, NodeTypeEnum.Property, PropertyData.class);
+ if (propertyDefRes.isRight()) {
+ TitanOperationStatus status = propertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ ImmutablePair<PropertyData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
+ String propertyUniqueId = (String) propertyDefPair.left.getUniqueId();
+
+ ComponentInstanceInput resourceInstanceProperty = new ComponentInstanceInput();
+ // set property original unique id
+ resourceInstanceProperty.setUniqueId(propertyUniqueId);
+ // set resource id
+ // TODO: esofer add resource id
+ resourceInstanceProperty.setParentUniqueId(null);
+ // set value
+ resourceInstanceProperty.setValue(value);
+ // set property value unique id
+ resourceInstanceProperty.setValueUniqueUid(propertyValueUid);
+ // set rules
+ // resourceInstanceProperty.setRules(propertyValueData.getRules());
+
+ propsList.add(resourceInstanceProperty);
+
+ }
+
+ groupDefinition.setInputsValue(propsList);
+ }
+
+ }
+
+ if (false == skipProperties) {
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao.getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueId, GraphEdgeLabels.GET_INPUT, NodeTypeEnum.PropertyValue, PropertyValueData.class);
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<PropertyValueData, GraphEdge>> list = propertyImplNodes.left().value();
+
+ if (list == null || true == list.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ List<ComponentInstanceProperty> propsRresult = new ArrayList<>();
+ for (ImmutablePair<PropertyValueData, GraphEdge> propertyValueDataPair : list) {
+ PropertyValueData propertyValueData = propertyValueDataPair.left;
+ String propertyValueUid = propertyValueData.getUniqueId();
+ String value = propertyValueData.getValue();
+
+ Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> propertyDefRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PropertyValue), propertyValueUid, GraphEdgeLabels.PROPERTY_IMPL, NodeTypeEnum.Property, PropertyData.class);
+ if (propertyDefRes.isRight()) {
+ TitanOperationStatus status = propertyDefRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ return Either.right(status);
+ }
+
+ }
+ if(propertyDefRes.isLeft()){
+
+ ImmutablePair<PropertyData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
+ PropertyData propertyData = propertyDefPair.left;
+ String propertyUniqueId = (String) propertyData.getPropertyDataDefinition().getUniqueId();
+
+ ComponentInstanceProperty resourceInstanceProperty = new ComponentInstanceProperty();
+ // set property original unique id
+ resourceInstanceProperty.setUniqueId(propertyUniqueId);
+ // set resource id
+ // TODO: esofer add resource id
+ resourceInstanceProperty.setParentUniqueId(null);
+ // set value
+ resourceInstanceProperty.setValue(value);
+ // set property value unique id
+ resourceInstanceProperty.setValueUniqueUid(propertyValueData.getUniqueId());
+ // set rules
+ resourceInstanceProperty.setRules(propertyValueData.getRules());
+ resourceInstanceProperty.setType(propertyData.getPropertyDataDefinition().getType());
+ resourceInstanceProperty.setSchema(propertyData.getPropertyDataDefinition().getSchema());
+ resourceInstanceProperty.setName((String) propertyValueDataPair.right.getProperties().get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ propsRresult.add(resourceInstanceProperty);
+ }
+
+ groupDefinition.setProperties(propsRresult);
+ }
+
+ }
+
+ result = Either.left(groupDefinition);
+
+ return result;
+
+ }
+
+ public ImmutablePair<TitanOperationStatus, String> findInputValue(String resourceInstanceId, String propertyId) {
+
+ log.debug("Going to check whether the property {} already added to resource instance {}", propertyId, resourceInstanceId);
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> getAllRes = getAllInputsOfResourceInstanceOnlyInputDefId(resourceInstanceId);
+ if (getAllRes.isRight()) {
+ TitanOperationStatus status = getAllRes.right().value();
+ log.trace("After fetching all properties of resource instance {}. Status is {}", resourceInstanceId, status);
+ return new ImmutablePair<TitanOperationStatus, String>(status, null);
+ }
+
+ List<ComponentInstanceInput> list = getAllRes.left().value();
+ if (list != null) {
+ for (ComponentInstanceInput instanceProperty : list) {
+ String propertyUniqueId = instanceProperty.getUniqueId();
+ String valueUniqueUid = instanceProperty.getValueUniqueUid();
+ log.trace("Go over property {} under resource instance {}. valueUniqueId = {}", propertyUniqueId, resourceInstanceId, valueUniqueUid);
+ if (propertyId.equals(propertyUniqueId) && valueUniqueUid != null) {
+ log.debug("The property {} already created under resource instance {}", propertyId, resourceInstanceId);
+ return new ImmutablePair<TitanOperationStatus, String>(TitanOperationStatus.ALREADY_EXIST, valueUniqueUid);
+ }
+ }
+ }
+
+ return new ImmutablePair<TitanOperationStatus, String>(TitanOperationStatus.NOT_FOUND, null);
+ }
+
+ /**
+ * return all properties associated to resource instance. The result does contains the property unique id but not its type, default value...
+ *
+ * @param resourceInstanceUid
+ * @return
+ */
+ public Either<List<ComponentInstanceInput>, TitanOperationStatus> getAllInputsOfResourceInstanceOnlyInputDefId(String resourceInstanceUid) {
+
+ return getAllInputsOfResourceInstanceOnlyInputDefId(resourceInstanceUid, NodeTypeEnum.ResourceInstance);
+
+ }
+
+ /**
+ * return all properties associated to resource instance. The result does contains the property unique id but not its type, default value...
+ *
+ * @param resourceInstanceUid
+ * @return
+ */
+ public Either<List<ComponentInstanceInput>, StorageOperationStatus> getComponentInstanceInputsByInputId(String resourceInstanceUid, String inputId) {
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> status = getComponentInstanceInputsByInputId(inputId);
+ if (status.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ }
+
+ return Either.left(status.left().value());
+
+ }
+
+ /**
+ * return all properties associated to resource instance. The result does contains the property unique id but not its type, default value...
+ *
+ * @param resourceInstanceUid
+ * @return
+ */
+ public Either<List<ComponentInstanceProperty>, StorageOperationStatus> getComponentInstancePropertiesByInputId(String resourceInstanceUid, String inputId) {
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> status = getComponentInstancePropertiesByInputId(inputId);
+ if (status.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ }
+
+ return Either.left(status.left().value());
+
+ }
+
+ public Either<List<ComponentInstanceInput>, TitanOperationStatus> getAllInputsOfResourceInstanceOnlyInputDefId(String resourceInstanceUid, NodeTypeEnum instanceNodeType) {
+
+ Either<ComponentInstanceData, TitanOperationStatus> findResInstanceRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(instanceNodeType), resourceInstanceUid, ComponentInstanceData.class);
+
+ if (findResInstanceRes.isRight()) {
+ TitanOperationStatus status = findResInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ Either<List<ImmutablePair<InputValueData, GraphEdge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(instanceNodeType), resourceInstanceUid, GraphEdgeLabels.INPUT_VALUE, NodeTypeEnum.InputValue, InputValueData.class);
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<InputValueData, GraphEdge>> list = propertyImplNodes.left().value();
+ if (list == null || true == list.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ List<ComponentInstanceInput> result = new ArrayList<>();
+
+
+ for (ImmutablePair<InputValueData, GraphEdge> propertyValueDataPair : list) {
+
+ InputValueData propertyValueData = propertyValueDataPair.getLeft();
+ String propertyValueUid = propertyValueData.getUniqueId();
+ String value = propertyValueData.getValue();
+
+ Either<ImmutablePair<InputsData, GraphEdge>, TitanOperationStatus> inputNodes = titanGenericDao.getParentNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), propertyValueData.getUniqueId(), GraphEdgeLabels.GET_INPUT, NodeTypeEnum.Input, InputsData.class);
+
+ if (inputNodes.isRight()) {
+
+ return Either.right(inputNodes.right().value());
+ }
+
+ InputsData input = inputNodes.left().value().left;
+ String inputId = input.getPropertyDataDefinition().getUniqueId();
+
+ Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> propertyDefRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.InputValue), propertyValueUid, GraphEdgeLabels.INPUT_IMPL, NodeTypeEnum.Property, PropertyData.class);
+ if (propertyDefRes.isRight()) {
+ TitanOperationStatus status = propertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ ImmutablePair<PropertyData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
+ PropertyData propertyData = propertyDefPair.left;
+ Either<Edge, TitanOperationStatus> inputsEges = titanGenericDao.getIncomingEdgeByCriteria(propertyData, GraphEdgeLabels.INPUT, null);
+ if (inputsEges.isRight()) {
+ TitanOperationStatus status = inputsEges.right().value();
+
+ return Either.right(status);
+ }
+ Edge edge = inputsEges.left().value();
+ String inputName = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.NAME.getProperty());
+
+ String propertyUniqueId = (String) propertyData.getPropertyDataDefinition().getUniqueId();
+
+
+ ComponentInstanceInput resourceInstanceProperty = new ComponentInstanceInput(propertyData.getPropertyDataDefinition(), inputId, value, propertyValueUid);
+
+ //resourceInstanceProperty.setName(inputName);
+ // set resource id
+ // TODO: esofer add resource id
+ resourceInstanceProperty.setName(inputName);
+ resourceInstanceProperty.setParentUniqueId(inputId);
+ // set value
+ resourceInstanceProperty.setValue(value);
+ // set property value unique id
+ resourceInstanceProperty.setValueUniqueUid(propertyValueData.getUniqueId());
+ // set rules
+ // resourceInstanceProperty.setRules(propertyValueData.getRules());
+ resourceInstanceProperty.setType(propertyData.getPropertyDataDefinition().getType());
+ resourceInstanceProperty.setSchema(propertyData.getPropertyDataDefinition().getSchema());
+ //resourceInstanceProperty.setComponentInstanceName(componentInsName);
+ resourceInstanceProperty.setComponentInstanceId(resourceInstanceUid);
+
+ result.add(resourceInstanceProperty);
+ }
+
+
+ return Either.left(result);
+ }
+
+ public Either<List<ComponentInstanceInput>, TitanOperationStatus> getComponentInstanceInputsByInputId(String inputId) {
+
+ Either<InputsData, TitanOperationStatus> findResInputRes = titanGenericDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), inputId, InputsData.class);
+
+ if (findResInputRes.isRight()) {
+ TitanOperationStatus status = findResInputRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+
+
+ // Either<List<InputValueData>, TitanOperationStatus> propertyImplNodes
+ // = titanGenericDao.getByCriteria(NodeTypeEnum.InputValue, props,
+ // InputValueData.class);
+ Either<TitanVertex, TitanOperationStatus> vertexService = titanGenericDao.getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), inputId);
+
+ if (vertexService.isRight()) {
+ log.debug("failed to fetch vertex of resource input for id = {}", inputId);
+ TitanOperationStatus status = vertexService.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+
+ return Either.right(status);
+ }
+ TitanVertex vertex = vertexService.left().value();
+
+ Either<List<ImmutablePair<InputValueData, GraphEdge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao.getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), inputId, GraphEdgeLabels.GET_INPUT, NodeTypeEnum.InputValue, InputValueData.class);
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<InputValueData, GraphEdge>> list = propertyImplNodes.left().value();
+
+ if (list == null || true == list.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ List<ComponentInstanceInput> result = new ArrayList<>();
+ for (ImmutablePair<InputValueData, GraphEdge> propertyValueDataPair : list) {
+ InputValueData propertyValueData = propertyValueDataPair.left;
+ String propertyValueUid = propertyValueData.getUniqueId();
+ String value = propertyValueData.getValue();
+ // Either<List<Edge>, TitanOperationStatus> out =
+ // titanGenericDao.getEdgesForNode(propertyValueData,
+ // Direction.OUT);
+ // Either<List<Edge>, TitanOperationStatus> in =
+ // titanGenericDao.getEdgesForNode(propertyValueData, Direction.IN);
+ Either<Edge, TitanOperationStatus> inputsvalueEges = titanGenericDao.getIncomingEdgeByCriteria(propertyValueData, GraphEdgeLabels.INPUT_VALUE, null);
+ if (inputsvalueEges.isRight()) {
+ TitanOperationStatus status = inputsvalueEges.right().value();
+
+ return Either.right(status);
+ }
+ Edge edge = inputsvalueEges.left().value();
+ String componentInsName = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.NAME.getProperty());
+ String componentInsId = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.OWNER_ID.getProperty());
+
+ Either<ImmutablePair<InputsData, GraphEdge>, TitanOperationStatus> propertyDefRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.InputValue), propertyValueUid, GraphEdgeLabels.INPUT_IMPL, NodeTypeEnum.Input, InputsData.class);
+
+ if (propertyDefRes.isRight()) {
+ TitanOperationStatus status = propertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ ImmutablePair<InputsData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
+
+ InputsData propertyData = propertyDefPair.left;
+
+ Either<Edge, TitanOperationStatus> inputsEges = titanGenericDao.getIncomingEdgeByCriteria(propertyData, GraphEdgeLabels.INPUT, null);
+ if (inputsEges.isRight()) {
+ TitanOperationStatus status = inputsEges.right().value();
+
+ return Either.right(status);
+ }
+ edge = inputsEges.left().value();
+ String inputName = (String) titanGenericDao.getProperty(edge, GraphEdgePropertiesDictionary.NAME.getProperty());
+
+ String propertyUniqueId = (String) propertyData.getPropertyDataDefinition().getUniqueId();
+
+ ComponentInstanceInput resourceInstanceProperty = new ComponentInstanceInput(propertyData.getPropertyDataDefinition(), inputId, value, propertyValueUid);
+ // set property original unique id
+ resourceInstanceProperty.setUniqueId(propertyUniqueId);
+ resourceInstanceProperty.setName(inputName);
+ // set resource id
+ // TODO: esofer add resource id
+ resourceInstanceProperty.setParentUniqueId(null);
+ // set value
+ resourceInstanceProperty.setValue(value);
+ // set property value unique id
+ resourceInstanceProperty.setValueUniqueUid(propertyValueData.getUniqueId());
+ // set rules
+ // resourceInstanceProperty.setRules(propertyValueData.getRules());
+ resourceInstanceProperty.setType(propertyData.getPropertyDataDefinition().getType());
+ resourceInstanceProperty.setSchema(propertyData.getPropertyDataDefinition().getSchema());
+ resourceInstanceProperty.setComponentInstanceName(componentInsName);
+ resourceInstanceProperty.setComponentInstanceId(componentInsId);
+
+ result.add(resourceInstanceProperty);
+ }
+
+ return Either.left(result);
+
+ }
+
+ public Either<List<ComponentInstanceProperty>, TitanOperationStatus> getComponentInstancePropertiesByInputId(String inputId) {
+
+ Either<InputsData, TitanOperationStatus> findResInputRes = titanGenericDao.getNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), inputId, InputsData.class);
+
+ if (findResInputRes.isRight()) {
+ TitanOperationStatus status = findResInputRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ //Map<String, Object> props = new HashMap<String, Object>();
+ //props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), resourceInstanceUid);
+
+ // Either<List<PropertyValueData>, TitanOperationStatus>
+ // propertyImplNodes =
+ // titanGenericDao.getByCriteria(NodeTypeEnum.PropertyValue, props,
+ // PropertyValueData.class);
+ Either<TitanVertex, TitanOperationStatus> vertexService = titanGenericDao.getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), inputId);
+
+ if (vertexService.isRight()) {
+ log.debug("failed to fetch vertex of resource input for id = {}", inputId);
+ TitanOperationStatus status = vertexService.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+
+ return Either.right(status);
+ }
+ TitanVertex vertex = vertexService.left().value();
+
+ // Either<List<ImmutablePair<PropertyValueData, GraphEdge>>,
+ // TitanOperationStatus> propertyImplNodes =
+ // titanGenericDao.getChildrenByEdgeCriteria(vertex, inputId,
+ // GraphEdgeLabels.GET_INPUT, NodeTypeEnum.PropertyValue,
+ // PropertyValueData.class, props);
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao.getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), inputId, GraphEdgeLabels.GET_INPUT, NodeTypeEnum.PropertyValue, PropertyValueData.class);
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<PropertyValueData, GraphEdge>> list = propertyImplNodes.left().value();
+
+ if (list == null || true == list.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ List<ComponentInstanceProperty> result = new ArrayList<>();
+ for (ImmutablePair<PropertyValueData, GraphEdge> propertyValueDataPair : list) {
+ PropertyValueData propertyValueData = propertyValueDataPair.left;
+ String propertyValueUid = propertyValueData.getUniqueId();
+ String value = propertyValueData.getValue();
+
+ Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> propertyDefRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PropertyValue), propertyValueUid, GraphEdgeLabels.PROPERTY_IMPL, NodeTypeEnum.Property, PropertyData.class);
+ if (propertyDefRes.isRight()) {
+ TitanOperationStatus status = propertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ ImmutablePair<PropertyData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
+ PropertyData propertyData = propertyDefPair.left;
+ String propertyUniqueId = (String) propertyData.getPropertyDataDefinition().getUniqueId();
+
+ ComponentInstanceProperty resourceInstanceProperty = new ComponentInstanceProperty();
+ // set property original unique id
+ resourceInstanceProperty.setUniqueId(propertyUniqueId);
+ // set resource id
+ // TODO: esofer add resource id
+ resourceInstanceProperty.setParentUniqueId(null);
+ // set value
+ resourceInstanceProperty.setValue(value);
+ // set property value unique id
+ resourceInstanceProperty.setValueUniqueUid(propertyValueData.getUniqueId());
+ // set rules
+ resourceInstanceProperty.setRules(propertyValueData.getRules());
+ resourceInstanceProperty.setType(propertyData.getPropertyDataDefinition().getType());
+ resourceInstanceProperty.setSchema(propertyData.getPropertyDataDefinition().getSchema());
+ resourceInstanceProperty.setName((String) propertyValueDataPair.right.getProperties().get(GraphPropertiesDictionary.NAME.getProperty()));
+
+ result.add(resourceInstanceProperty);
+ }
+
+ return Either.left(result);
+ }
+
+ public ComponentInstanceInput buildResourceInstanceInput(InputValueData propertyValueData, ComponentInstanceInput resourceInstanceInput) {
+
+ String value = propertyValueData.getValue();
+ String uid = propertyValueData.getUniqueId();
+ ComponentInstanceInput instanceProperty = new ComponentInstanceInput(resourceInstanceInput, value, uid);
+ instanceProperty.setPath(resourceInstanceInput.getPath());
+
+ return instanceProperty;
+ }
+
+ public Either<List<ComponentInstanceInput>, TitanOperationStatus> getAllInputsOfResourceInstance(ComponentInstance compInstance) {
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> result;
+
+ return getAllInputsOfResourceInstanceOnlyInputDefId(compInstance.getUniqueId());
+
+ }
+
+ public Either<List<InputDefinition>, StorageOperationStatus> addInputsToComponent(String resourceId, NodeTypeEnum nodeType, ComponentInstInputsMap componentInsInputs, Map<String, DataTypeDefinition> dataTypes) {
+ List<InputDefinition> resList = new ArrayList<InputDefinition>();
+ Map<String, List<InputDefinition>> newInputsMap = componentInsInputs.getComponentInstanceInputsMap();
+ if (newInputsMap != null && !newInputsMap.isEmpty()) {
+ for (Entry<String, List<InputDefinition>> entry : newInputsMap.entrySet()) {
+ String compInstId = entry.getKey();
+ List<InputDefinition> inputs = entry.getValue();
+
+ if (inputs != null && !inputs.isEmpty()) {
+ for (InputDefinition input : inputs) {
+
+ Either<Integer, StorageOperationStatus> counterRes = componentInstanceOperation.increaseAndGetResourceInstanceSpecificCounter(compInstId, GraphPropertiesDictionary.INPUT_COUNTER, true);
+ if (counterRes.isRight()) {
+ log.debug("increaseAndGetResourceInputCounter failed resource instance {}", compInstId);
+ StorageOperationStatus status = counterRes.right().value();
+ return Either.right(status);
+ }
+
+ Either<InputDefinition, TitanOperationStatus> oldInputEither = getInputFromGraph(input.getUniqueId(), true, true);
+ if (oldInputEither.isRight()) {
+ log.error("Failed to get input {} ", input.getUniqueId());
+
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(oldInputEither.right().value()));
+ }
+ JSONObject jobject = new JSONObject();
+ jobject.put("get_input", input.getName());
+ InputDefinition oldInput = oldInputEither.left().value();
+
+ ComponentInstanceInput inputValue = new ComponentInstanceInput(oldInput, jobject.toJSONString(), null);
+ Integer index = counterRes.left().value();
+
+ Either<ComponentInstanceInput, StorageOperationStatus> eitherStatus = componentInstanceOperation.addInputValueToResourceInstance(inputValue, compInstId, index, true);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to add input value {} to resource instance {} in Graph. status is {}", inputValue, compInstId, eitherStatus.right().value().name());
+
+ return Either.right(eitherStatus.right().value());
+ }
+ ComponentInstanceInput inputValueData = eitherStatus.left().value();
+
+ // ComponentInstanceInput propertyValueResult =
+ // buildResourceInstanceInput(propertyValueData,
+ // inputValue);
+ // log.debug("The returned ResourceInstanceProperty is "
+ // + propertyValueResult);
+
+ String inputName = input.getName();
+ input.setSchema(oldInputEither.left().value().getSchema());
+ input.setDefaultValue(oldInput.getDefaultValue());
+ input.setConstraints(oldInput.getConstraints());
+ input.setDescription(oldInput.getDescription());
+ input.setHidden(oldInput.isHidden());
+ input.setImmutable(oldInput.isImmutable());
+ input.setDefinition(oldInput.isDefinition());
+ input.setRequired(oldInput.isRequired());
+
+ StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(input, dataTypes);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ log.error("Property {} is invalid. Status is {}", input, validateAndUpdateProperty);
+ return Either.right(validateAndUpdateProperty);
+ }
+
+ Either<InputsData, TitanOperationStatus> addPropertyToGraph = addInputToGraph(inputName, input, resourceId, nodeType);
+
+ if (addPropertyToGraph.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertyToGraph.right().value()));
+ }
+ InputDefinition createdInputyDefinition = convertInputDataToInputDefinition(addPropertyToGraph.left().value());
+ createdInputyDefinition.setName(inputName);
+ createdInputyDefinition.setParentUniqueId(resourceId);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), createdInputyDefinition.getName());
+ props.put(GraphEdgePropertiesDictionary.OWNER_ID.getProperty(), compInstId);
+
+ GraphNode propertyData = new UniqueIdData(NodeTypeEnum.InputValue, inputValueData.getValueUniqueUid());
+
+ Either<GraphRelation, TitanOperationStatus> addPropRefResult = titanGenericDao.createRelation(addPropertyToGraph.left().value(), propertyData, GraphEdgeLabels.GET_INPUT, props);
+
+ if (addPropRefResult.isRight()) {
+ TitanOperationStatus status = addPropRefResult.right().value();
+ String description = "Failed to associate input " + addPropertyToGraph.left().value().getUniqueId() + " to input value " + inputValueData.getUniqueId() + " in graph. Status is " + status;
+ BeEcompErrorManager.getInstance().logInternalFlowError(ASSOCIATING_INPUT_TO_PROP, description, ErrorSeverity.ERROR);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ resList.add(createdInputyDefinition);
+
+ }
+ }
+
+ }
+ }
+ return Either.left(resList);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InterfaceLifecycleOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InterfaceLifecycleOperation.java
new file mode 100644
index 0000000000..0d29c18a95
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/InterfaceLifecycleOperation.java
@@ -0,0 +1,1308 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import org.apache.cassandra.transport.Event.StatusChange;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.InterfaceDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.operations.api.IInterfaceLifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.InterfaceData;
+import org.openecomp.sdc.be.resources.data.OperationData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("interface-operation")
+public class InterfaceLifecycleOperation implements IInterfaceLifecycleOperation {
+
+ private static Logger log = LoggerFactory.getLogger(InterfaceLifecycleOperation.class.getName());
+
+ public InterfaceLifecycleOperation() {
+ super();
+ }
+
+ @javax.annotation.Resource
+ private ArtifactOperation artifactOperation;
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> addInterfaceToResource(InterfaceDefinition interf,
+ String resourceId, String interfaceName) {
+
+ return addInterfaceToResource(interf, resourceId, interfaceName, false);
+ }
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> addInterfaceToResource(InterfaceDefinition interf,
+ String resourceId, String interfaceName, boolean inTransaction) {
+
+ return createInterfaceOnResource(interf, resourceId, interfaceName, true, inTransaction);
+
+ }
+
+ private Either<OperationData, TitanOperationStatus> addOperationToGraph(InterfaceDefinition interf, String opName,
+ Operation op, InterfaceData interfaceData) {
+
+ op.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId((String) interfaceData.getUniqueId(), opName));
+ OperationData operationData = new OperationData(op);
+
+ log.debug("Before adding operation to graph {}", operationData);
+ Either<OperationData, TitanOperationStatus> createOpNodeResult = titanGenericDao.createNode(operationData,
+ OperationData.class);
+ log.debug("After adding operation to graph {}", operationData);
+
+ if (createOpNodeResult.isRight()) {
+ TitanOperationStatus opStatus = createOpNodeResult.right().value();
+ log.error("Failed to add operation {} to graph. Status is {}", opName, opStatus);
+ return Either.right(opStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), opName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(interfaceData,
+ operationData, GraphEdgeLabels.INTERFACE_OPERATION, props);
+
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createOpNodeResult.right().value();
+ log.error("Failed to associate operation {} to property {} in graph. Status is {}", interfaceData.getUniqueId(), opName, operationStatus);
+
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createOpNodeResult.left().value());
+
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> addOperationToGraph(InterfaceDefinition interf, String opName,
+ Operation op, TitanVertex interfaceVertex) {
+
+ String interfaceId = (String) titanGenericDao.getProperty(interfaceVertex,
+ GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ op.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(interfaceId, opName));
+ OperationData operationData = new OperationData(op);
+
+ log.debug("Before adding operation to graph {}", operationData);
+ Either<TitanVertex, TitanOperationStatus> createOpNodeResult = titanGenericDao.createNode(operationData);
+
+ if (createOpNodeResult.isRight()) {
+ TitanOperationStatus opStatus = createOpNodeResult.right().value();
+ log.error("Failed to add operation {} to graph. status is {}", opName, opStatus);
+ return Either.right(opStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), opName);
+ TitanVertex operationVertex = createOpNodeResult.left().value();
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(interfaceVertex, operationVertex,
+ GraphEdgeLabels.INTERFACE_OPERATION, props);
+
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate operation {} to property {} in graph. status is {}", interfaceId, opName,
+ createRelResult);
+
+ return Either.right(createRelResult);
+ }
+ return Either.left(operationVertex);
+ }
+
+ private InterfaceDefinition convertInterfaceDataToInterfaceDefinition(InterfaceData interfaceData) {
+
+ log.debug("The object returned after create interface is {}", interfaceData);
+
+ InterfaceDefinition interfaceDefResult = new InterfaceDefinition(interfaceData.getInterfaceDataDefinition());
+
+ return interfaceDefResult;
+
+ }
+
+ private Operation convertOperationDataToOperation(OperationData operationData) {
+
+ log.debug("The object returned after create operation is {}", operationData);
+
+ Operation operationDefResult = new Operation(operationData.getOperationDataDefinition());
+
+ return operationDefResult;
+
+ }
+
+ private Either<InterfaceData, TitanOperationStatus> addInterfaceToGraph(InterfaceDefinition interfaceInfo,
+ String interfaceName, String resourceId) {
+
+ InterfaceData interfaceData = new InterfaceData(interfaceInfo);
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceId);
+
+ String interfaceNameSplitted = getShortInterfaceName(interfaceInfo);
+
+ interfaceInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, interfaceNameSplitted));
+
+ Either<InterfaceData, TitanOperationStatus> existInterface = titanGenericDao
+ .getNode(interfaceData.getUniqueIdKey(), interfaceData.getUniqueId(), InterfaceData.class);
+
+ if (existInterface.isRight()) {
+
+ return createInterfaceNodeAndRelation(interfaceNameSplitted, resourceId, interfaceData, resourceData);
+ } else {
+ log.debug("Interface {} already exist", interfaceData.getUniqueId());
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> addInterfaceToGraph(InterfaceDefinition interfaceInfo,
+ String interfaceName, String resourceId, TitanVertex metadataVertex) {
+
+ InterfaceData interfaceData = new InterfaceData(interfaceInfo);
+
+ String interfaceNameSplitted = getShortInterfaceName(interfaceInfo);
+
+ interfaceInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, interfaceNameSplitted));
+
+ Either<TitanVertex, TitanOperationStatus> existInterface = titanGenericDao
+ .getVertexByProperty(interfaceData.getUniqueIdKey(), interfaceData.getUniqueId());
+
+ if (existInterface.isRight()) {
+
+ return createInterfaceNodeAndRelation(interfaceNameSplitted, resourceId, interfaceData, metadataVertex);
+ } else {
+ log.debug("Interface {} already exist", interfaceData.getUniqueId());
+ return Either.right(TitanOperationStatus.ALREADY_EXIST);
+ }
+ }
+
+ private Either<InterfaceData, TitanOperationStatus> createInterfaceNodeAndRelation(String interfaceName,
+ String resourceId, InterfaceData interfaceData, ResourceMetadataData resourceData) {
+ log.debug("Before adding interface to graph {}", interfaceData);
+ Either<InterfaceData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(interfaceData,
+ InterfaceData.class);
+ log.debug("After adding property to graph {}", interfaceData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add interface {} to graph. Status is {}", interfaceName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), interfaceName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(resourceData,
+ interfaceData, GraphEdgeLabels.INTERFACE, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to associate resource {} to property {} in graph. Status is {}", resourceId, interfaceName, operationStatus);
+
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> createInterfaceNodeAndRelation(String interfaceName,
+ String resourceId, InterfaceData interfaceData, TitanVertex metadataVertex) {
+ log.debug("Before adding interface to graph {}", interfaceData);
+ Either<TitanVertex, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(interfaceData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add interface {} to graph. status is {}", interfaceName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), interfaceName);
+ TitanVertex interfaceVertex = createNodeResult.left().value();
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(metadataVertex, interfaceVertex,
+ GraphEdgeLabels.INTERFACE, props);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource {} to property {} in graph. status is {}", resourceId,
+ interfaceName, createRelResult);
+ }
+ return Either.left(interfaceVertex);
+ }
+
+ private Either<OperationData, TitanOperationStatus> createOperationNodeAndRelation(String operationName,
+ OperationData operationData, InterfaceData interfaceData) {
+ log.debug("Before adding operation to graph {}", operationData);
+ Either<OperationData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(operationData,
+ OperationData.class);
+ log.debug("After adding operation to graph {}", interfaceData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add interfoperationce {} to graph. Status is {}", operationName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), operationName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(interfaceData,
+ operationData, GraphEdgeLabels.INTERFACE_OPERATION, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to associate operation {} to interface {} in graph. Status is {}", operationName, interfaceData.getUniqueId(), operationStatus);
+
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+ }
+
+ // @Override
+ // public Either<InterfaceDefinition, StorageOperationStatus> getInterface(
+ // String interfaceId) {
+ //
+ // /*
+ // * Either<InterfaceData, TitanOperationStatus> getResult =
+ // * this.titanGenericDao
+ // * .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface),
+ // * interfaceId, InterfaceData.class); if (getResult.isLeft()) {
+ // * InterfaceData propertyData = getResult.left().value(); return
+ // * Either.left(convertPropertyDataToPropertyDefinition(propertyData)); }
+ // * else { TitanOperationStatus titanStatus = getResult.right().value();
+ // * log.debug("Node with id {} was not found in the graph. Status: {}", propertyId, titanStatus);
+ // * StorageOperationStatus storageOperationStatus =
+ // * DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ // * return Either.right(storageOperationStatus); }
+ // */
+ // return null;
+ // }
+
+ // @Override
+ // public Either<InterfaceDefinition, StorageOperationStatus> getInterface(
+ // String interfaceId, boolean inTransaction) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+
+ @Override
+ public Either<Map<String, InterfaceDefinition>, StorageOperationStatus> getAllInterfacesOfResource(
+ String resourceIdn, boolean recursively) {
+ return getAllInterfacesOfResource(resourceIdn, recursively, false);
+ }
+
+ @Override
+ public Either<Map<String, InterfaceDefinition>, StorageOperationStatus> getAllInterfacesOfResource(
+ String resourceId, boolean recursively, boolean inTransaction) {
+
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> result = null;
+ Map<String, InterfaceDefinition> interfaces = new HashMap<String, InterfaceDefinition>();
+ try {
+ if ((resourceId == null) || resourceId.isEmpty()) {
+ log.error("resourceId is empty");
+ result = Either.right(StorageOperationStatus.INVALID_ID);
+ return result;
+ }
+
+ TitanOperationStatus findInterfacesRes = TitanOperationStatus.GENERAL_ERROR;
+ if (recursively) {
+ findInterfacesRes = findAllInterfacesRecursively(resourceId, interfaces);
+ } else {
+ findInterfacesRes = findAllInterfacesNotRecursively(resourceId, interfaces);
+ }
+ if (!findInterfacesRes.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to get all interfaces of resource {}. Status is {}", resourceId, findInterfacesRes);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(findInterfacesRes));
+ return result;
+ }
+ result = Either.left(interfaces);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private TitanOperationStatus findAllInterfacesNotRecursively(String resourceId,
+ Map<String, InterfaceDefinition> interfaces) {
+
+ Either<List<ImmutablePair<InterfaceData, GraphEdge>>, TitanOperationStatus> interfaceNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId,
+ GraphEdgeLabels.INTERFACE, NodeTypeEnum.Interface, InterfaceData.class);
+
+ if (interfaceNodes.isRight()) {
+ TitanOperationStatus status = interfaceNodes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return status;
+ }
+ } else {
+ List<ImmutablePair<InterfaceData, GraphEdge>> interfaceList = interfaceNodes.left().value();
+ if (interfaceList != null) {
+ for (ImmutablePair<InterfaceData, GraphEdge> interfacePair : interfaceList) {
+ String interfaceUniqueId = (String) interfacePair.getKey().getUniqueId();
+ Either<String, TitanOperationStatus> interfaceNameRes = getPropertyValueFromEdge(
+ interfacePair.getValue(), GraphPropertiesDictionary.NAME);
+ if (interfaceNameRes.isRight()) {
+ log.error("The requirement name is missing on the edge of requirement {}", interfaceUniqueId);
+ return interfaceNameRes.right().value();
+ }
+ String interfaceName = interfaceNameRes.left().value();
+ Either<InterfaceDefinition, TitanOperationStatus> interfaceDefRes = getNonRecursiveInterface(
+ interfacePair.getKey());
+ if (interfaceDefRes.isRight()) {
+ TitanOperationStatus status = interfaceDefRes.right().value();
+ log.error("Failed to get interface actions of interface {}", interfaceUniqueId);
+ return status;
+ }
+
+ InterfaceDefinition interfaceDefinition = interfaceDefRes.left().value();
+ if (true == interfaces.containsKey(interfaceName)) {
+ log.debug("The interface {} was already defined in derived resource. add not overriden operations", interfaceName);
+ InterfaceDefinition existInterface = interfaces.get(interfaceName);
+ addMissingOperationsToInterface(interfaceDefinition, existInterface);
+ } else {
+ interfaces.put(interfaceName, interfaceDefinition);
+ }
+
+ }
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ public TitanOperationStatus findAllInterfacesRecursively(String resourceId,
+ Map<String, InterfaceDefinition> interfaces) {
+
+ TitanOperationStatus findAllInterfacesNotRecursively = findAllInterfacesNotRecursively(resourceId, interfaces);
+ if (!findAllInterfacesNotRecursively.equals(TitanOperationStatus.OK)) {
+ log.error("failed to get interfaces for resource {}. Status is {}", resourceId, findAllInterfacesNotRecursively);
+ }
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (parentNodesStatus == TitanOperationStatus.NOT_FOUND) {
+ log.debug("Finish to lookup for parnet interfaces");
+ return TitanOperationStatus.OK;
+ } else {
+ log.error("Failed to find parent interfaces of resource {}. Status is {}", resourceId, parentNodesStatus);
+ return parentNodesStatus;
+ }
+ }
+ ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentIntStatus = findAllInterfacesRecursively(parentUniqueId, interfaces);
+
+ if (addParentIntStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch all interfaces of resource {}", parentUniqueId);
+ return addParentIntStatus;
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ private Either<String, TitanOperationStatus> getPropertyValueFromEdge(GraphEdge edge,
+ GraphPropertiesDictionary property) {
+ Map<String, Object> edgeProps = edge.getProperties();
+ String interfaceName = null;
+ if (edgeProps != null) {
+ interfaceName = (String) edgeProps.get(property.getProperty());
+ if (interfaceName == null) {
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+ } else {
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+ return Either.left(interfaceName);
+ }
+
+ private Either<InterfaceDefinition, TitanOperationStatus> getNonRecursiveInterface(InterfaceData interfaceData) {
+
+ log.debug("Going to fetch the operations associate to interface {}", interfaceData.getUniqueId());
+ InterfaceDefinition interfaceDefinition = new InterfaceDefinition(interfaceData.getInterfaceDataDefinition());
+
+ String interfaceId = interfaceData.getUniqueId();
+ Either<List<ImmutablePair<OperationData, GraphEdge>>, TitanOperationStatus> operationsRes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), interfaceId,
+ GraphEdgeLabels.INTERFACE_OPERATION, NodeTypeEnum.InterfaceOperation, OperationData.class);
+
+ if (operationsRes.isRight()) {
+ TitanOperationStatus status = operationsRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(status);
+ } else {
+ return Either.left(interfaceDefinition);
+ }
+ }
+
+ List<ImmutablePair<OperationData, GraphEdge>> operationList = operationsRes.left().value();
+ if (operationList != null && !operationList.isEmpty()) {
+ for (ImmutablePair<OperationData, GraphEdge> operationPair : operationList) {
+ Operation operation = new Operation(operationPair.getKey().getOperationDataDefinition());
+ Either<String, TitanOperationStatus> operationNameRes = getPropertyValueFromEdge(
+ operationPair.getValue(), GraphPropertiesDictionary.NAME);
+ if (operationNameRes.isRight()) {
+ log.error("The operation name is missing on the edge of operation {}", operationPair.getKey().getUniqueId());
+ return Either.right(operationNameRes.right().value());
+ }
+ String operationName = operationNameRes.left().value();
+ findOperationImplementation(operation);
+ interfaceDefinition.getOperations().put(operationName, operation);
+ }
+ }
+
+ return Either.left(interfaceDefinition);
+ }
+
+ private StorageOperationStatus findOperationImplementation(Operation operation) {
+
+ String operationId = operation.getUniqueId();
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifactsRes = artifactOperation
+ .getArtifacts(operationId, NodeTypeEnum.InterfaceOperation, true);
+ if (artifactsRes.isRight() || artifactsRes.left().value() == null) {
+ log.error("failed to get artifact from graph for operation id {}. status is {}", operationId,
+ artifactsRes.right().value());
+ return artifactsRes.right().value();
+ } else {
+ Map<String, ArtifactDefinition> artifacts = artifactsRes.left().value();
+ Iterator<String> iter = artifacts.keySet().iterator();
+
+ if (iter.hasNext()) {
+ operation.setImplementation(artifacts.get(iter.next()));
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus addMissingOperationsToInterface(InterfaceDefinition interfaceDefinition,
+ InterfaceDefinition existInterface) {
+ Map<String, Operation> existOperations = existInterface.getOperations();
+ Map<String, Operation> operations = interfaceDefinition.getOperations();
+ if (operations != null && !operations.isEmpty()) {
+ Set<Entry<String, Operation>> operationsSet = operations.entrySet();
+ for (Entry<String, Operation> operation : operationsSet) {
+ if (!existOperations.containsKey(operation.getKey())) {
+ existOperations.put(operation.getKey(), operation.getValue());
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<Operation, StorageOperationStatus> updateInterfaceOperation(String resourceId, String interfaceName,
+ String operationName, Operation interf) {
+
+ return updateInterfaceOperation(resourceId, interfaceName, operationName, interf, false);
+ }
+
+ @Override
+ public Either<Operation, StorageOperationStatus> updateInterfaceOperation(String resourceId, String interfaceName,
+ String operationName, Operation operation, boolean inTransaction) {
+ Either<Operation, StorageOperationStatus> status = updateOperationOnGraph(operation, resourceId, interfaceName,
+ operationName);
+
+ /*
+ * if (status.isRight()) { if (false == inTransaction) {
+ * titanGenericDao.rollback(); } log.error("Failed to update operation "
+ * + operationName + " of interfaceName " + interfaceName +
+ * " of resource" + resourceId); return Either.right(DaoStatusConverter
+ * .convertTitanStatusToStorageStatus(status.right().value())); } else {
+ * if (false == inTransaction) { titanGenericDao.commit(); }
+ * OperationData operationData = status.left().value();
+ *
+ * Operation operationDefResult =
+ * convertOperationDataToOperation(operationData);
+ *
+ *
+ * log.debug("The returned OperationDefintion is {}", operationDefResult); return Either.left(operationDefResult); }
+ */
+ return status;
+ }
+
+ private Either<Operation, StorageOperationStatus> updateOperationOnGraph(Operation operation, String resourceId,
+ String interfaceName, String operationName) {
+
+ Either<List<ImmutablePair<InterfaceData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), resourceId,
+ GraphEdgeLabels.INTERFACE, NodeTypeEnum.Interface, InterfaceData.class);
+
+ if (childrenNodes.isRight()) {
+ /*
+ * InterfaceDefinition intDef = new InterfaceDefinition();
+ * intDef.setType(interfaceName); Map<String, Operation> opMap = new
+ * HashMap<String, Operation>(); opMap.put(operationName,
+ * operation); intDef.setOperations(opMap);
+ * Either<InterfaceDefinition, StorageOperationStatus> statusRes =
+ * this .createInterfaceOnResource(intDef, resourceId,
+ * interfaceName, true); if (statusRes.isRight()) return
+ * Either.right(statusRes.right().value()); else {
+ * InterfaceDefinition newDef = statusRes.left().value(); Operation
+ * res = newDef.getOperations().get(operationName); return
+ * Either.left(res); }
+ */
+ return updateOperationFromParentNode(operation, resourceId, interfaceName, operationName);
+
+ } else {
+ return updateExistingOperation(resourceId, operation, interfaceName, operationName, childrenNodes);
+
+ }
+
+ }
+
+ private Either<Operation, StorageOperationStatus> updateExistingOperation(String resourceId, Operation operation,
+ String interfaceName, String operationName,
+ Either<List<ImmutablePair<InterfaceData, GraphEdge>>, TitanOperationStatus> childrenNodes) {
+ Operation newOperation = null;
+ StorageOperationStatus storageOperationStatus = StorageOperationStatus.GENERAL_ERROR;
+
+ for (ImmutablePair<InterfaceData, GraphEdge> interfaceDataNode : childrenNodes.left().value()) {
+
+ GraphEdge interfaceEdge = interfaceDataNode.getRight();
+ Map<String, Object> interfaceEdgeProp = interfaceEdge.getProperties();
+ InterfaceData interfaceData = interfaceDataNode.getKey();
+
+ if (interfaceEdgeProp.get(GraphPropertiesDictionary.NAME.getProperty()).equals(interfaceName)) {
+ Either<List<ImmutablePair<OperationData, GraphEdge>>, TitanOperationStatus> operationRes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ (String) interfaceDataNode.getLeft().getUniqueId(), GraphEdgeLabels.INTERFACE_OPERATION,
+ NodeTypeEnum.InterfaceOperation, OperationData.class);
+ if (operationRes.isRight()) {
+ log.error("Failed to find operation {} on interface {}", operationName, interfaceName);
+ return Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationRes.right().value()));
+ } else {
+ List<ImmutablePair<OperationData, GraphEdge>> operations = operationRes.left().value();
+ for (ImmutablePair<OperationData, GraphEdge> operationPairEdge : operations) {
+ GraphEdge opEdge = operationPairEdge.getRight();
+ OperationData opData = operationPairEdge.getLeft();
+ Map<String, Object> opEdgeProp = opEdge.getProperties();
+ if (opEdgeProp.get(GraphPropertiesDictionary.NAME.getProperty()).equals(operationName)) {
+ ArtifactDefinition artifact = operation.getImplementation();
+ Either<ImmutablePair<ArtifactData, GraphEdge>, TitanOperationStatus> artifactRes = titanGenericDao
+ .getChild(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ (String) opData.getUniqueId(), GraphEdgeLabels.ARTIFACT_REF,
+ NodeTypeEnum.ArtifactRef, ArtifactData.class);
+ Either<ArtifactDefinition, StorageOperationStatus> artStatus;
+ if (artifactRes.isRight()) {
+ artStatus = artifactOperation.addArifactToComponent(artifact,
+ (String) operationPairEdge.getLeft().getUniqueId(),
+ NodeTypeEnum.InterfaceOperation, true, true);
+ } else {
+ artStatus = artifactOperation.updateArifactOnResource(artifact,
+ (String) operationPairEdge.getLeft().getUniqueId(),
+ (String) artifactRes.left().value().getLeft().getUniqueId(),
+ NodeTypeEnum.InterfaceOperation, true);
+ }
+ if (artStatus.isRight()) {
+ titanGenericDao.rollback();
+ log.error("Failed to add artifact {}", operationName, interfaceName);
+ return Either.right(artStatus.right().value());
+ } else {
+ newOperation = this.convertOperationDataToOperation(opData);
+ newOperation.setImplementation(artStatus.left().value());
+
+ }
+
+ }
+
+ }
+ if (newOperation == null) {
+ Either<InterfaceData, TitanOperationStatus> parentInterfaceStatus = findInterfaceOnParentNode(
+ resourceId, interfaceName);
+ if (parentInterfaceStatus.isRight()) {
+ log.debug("Interface {} not exist", interfaceName);
+ return Either.right(DaoStatusConverter
+ .convertTitanStatusToStorageStatus(parentInterfaceStatus.right().value()));
+ }
+
+ InterfaceData parentInterfaceData = parentInterfaceStatus.left().value();
+ Either<List<ImmutablePair<OperationData, GraphEdge>>, TitanOperationStatus> opRes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ (String) parentInterfaceData.getUniqueId(), GraphEdgeLabels.INTERFACE_OPERATION,
+ NodeTypeEnum.InterfaceOperation, OperationData.class);
+ if (opRes.isRight()) {
+ log.error("Failed to find operation {} on interface", operationName, interfaceName);
+ return Either.right(
+ DaoStatusConverter.convertTitanStatusToStorageStatus(operationRes.right().value()));
+
+ } else {
+ List<ImmutablePair<OperationData, GraphEdge>> parentOperations = opRes.left().value();
+ for (ImmutablePair<OperationData, GraphEdge> operationPairEdge : parentOperations) {
+ GraphEdge opEdge = operationPairEdge.getRight();
+ OperationData opData = operationPairEdge.getLeft();
+ Map<String, Object> opEdgeProp = opEdge.getProperties();
+ if (opEdgeProp.get(GraphPropertiesDictionary.NAME.getProperty())
+ .equals(operationName)) {
+ return copyAndCreateNewOperation(operation, interfaceName, operationName, null,
+ interfaceData, operationRes, opData);
+ }
+ }
+ }
+
+ }
+
+ }
+
+ } else {
+ // not found
+ storageOperationStatus = StorageOperationStatus.ARTIFACT_NOT_FOUND;
+ }
+
+ }
+ if (newOperation == null)
+ return Either.right(storageOperationStatus);
+ else
+ return Either.left(newOperation);
+ }
+
+ private Either<Operation, StorageOperationStatus> copyAndCreateNewOperation(Operation operation,
+ String interfaceName, String operationName, Operation newOperation, InterfaceData interfaceData,
+ Either<List<ImmutablePair<OperationData, GraphEdge>>, TitanOperationStatus> operationRes,
+ OperationData opData) {
+ OperationDataDefinition opDataInfo = opData.getOperationDataDefinition();
+ OperationDataDefinition newOperationInfo = new OperationDataDefinition(opDataInfo);
+ newOperationInfo.setUniqueId(
+ UniqueIdBuilder.buildPropertyUniqueId(interfaceData.getUniqueId(), operationName.toLowerCase()));
+ OperationData newopData = new OperationData(newOperationInfo);
+ Either<OperationData, TitanOperationStatus> operationStatus = createOperationNodeAndRelation(operationName,
+ newopData, interfaceData);
+ if (operationStatus.isRight()) {
+ log.error("Failed to create operation {} on interface {}", operationName, interfaceName);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationRes.right().value()));
+ }
+ ArtifactDefinition artifact = operation.getImplementation();
+ if (artifact != null) {
+ Either<ArtifactDefinition, StorageOperationStatus> artStatus = artifactOperation.addArifactToComponent(
+ artifact, (String) operationStatus.left().value().getUniqueId(), NodeTypeEnum.InterfaceOperation,
+ true, true);
+ if (artStatus.isRight()) {
+ titanGenericDao.rollback();
+ log.error("Failed to add artifact {} to interface {}", operationName, interfaceName);
+ } else {
+ newOperation = this.convertOperationDataToOperation(opData);
+ newOperation.setImplementation(artStatus.left().value());
+
+ }
+ }
+ return Either.left(newOperation);
+ }
+
+ private Either<Operation, StorageOperationStatus> updateOperationFromParentNode(Operation operation,
+ String resourceId, String interfaceName, String operationName) {
+ // Operation newOperation = null;
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceId);
+ Either<InterfaceData, TitanOperationStatus> parentInterfaceStatus = findInterfaceOnParentNode(resourceId,
+ interfaceName);
+ if (parentInterfaceStatus.isRight()) {
+ log.debug("Interface {} not exist", interfaceName);
+ return Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(parentInterfaceStatus.right().value()));
+ }
+
+ InterfaceData interfaceData = parentInterfaceStatus.left().value();
+ InterfaceDataDefinition intDataDefinition = interfaceData.getInterfaceDataDefinition();
+ InterfaceDataDefinition newInterfaceInfo = new InterfaceDataDefinition(intDataDefinition);
+
+ String interfaceNameSplitted = getShortInterfaceName(intDataDefinition);
+
+ newInterfaceInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, interfaceNameSplitted));
+ InterfaceData updatedInterfaceData = new InterfaceData(newInterfaceInfo);
+ Either<InterfaceData, TitanOperationStatus> createStatus = createInterfaceNodeAndRelation(interfaceName,
+ resourceId, updatedInterfaceData, resourceData);
+ if (createStatus.isRight()) {
+ log.debug("failed to create interface node {} on resource {}", interfaceName, resourceId);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(createStatus.right().value()));
+ }
+
+ InterfaceData newInterfaceNode = createStatus.left().value();
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(newInterfaceNode,
+ interfaceData, GraphEdgeLabels.DERIVED_FROM, null);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.error("Failed to associate interface {} to interface {} in graph. Status is {}", interfaceData.getUniqueId(), newInterfaceNode.getUniqueId(), operationStatus);
+
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationStatus));
+ }
+ Either<List<ImmutablePair<OperationData, GraphEdge>>, TitanOperationStatus> operationRes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ (String) interfaceData.getUniqueId(), GraphEdgeLabels.INTERFACE_OPERATION,
+ NodeTypeEnum.InterfaceOperation, OperationData.class);
+ if (operationRes.isRight()) {
+ log.error("Failed to find operation {} on interface {}", operationName, interfaceName);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationRes.right().value()));
+
+ } else {
+ List<ImmutablePair<OperationData, GraphEdge>> operations = operationRes.left().value();
+ for (ImmutablePair<OperationData, GraphEdge> operationPairEdge : operations) {
+ GraphEdge opEdge = operationPairEdge.getRight();
+ OperationData opData = operationPairEdge.getLeft();
+ Map<String, Object> opEdgeProp = opEdge.getProperties();
+ if (opEdgeProp.get(GraphPropertiesDictionary.NAME.getProperty()).equals(operationName)) {
+
+ return copyAndCreateNewOperation(operation, interfaceName, operationName, null, // changed
+ // from
+ // newOperation
+ newInterfaceNode, operationRes, opData);
+
+ }
+ }
+ }
+ // if(newOperation == null)
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ // else
+ // return Either.left(newOperation);
+ }
+
+ private Either<InterfaceData, TitanOperationStatus> findInterfaceOnParentNode(String resourceId,
+ String interfaceName) {
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentRes = titanGenericDao
+ .getChild(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), resourceId, GraphEdgeLabels.DERIVED_FROM,
+ NodeTypeEnum.Resource, ResourceMetadataData.class);
+ if (parentRes.isRight()) {
+ log.debug("interface {} not found", interfaceName);
+ return Either.right(parentRes.right().value());
+ }
+ ImmutablePair<ResourceMetadataData, GraphEdge> parenNode = parentRes.left().value();
+
+ Either<List<ImmutablePair<InterfaceData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ parenNode.getKey().getMetadataDataDefinition().getUniqueId(), GraphEdgeLabels.INTERFACE,
+ NodeTypeEnum.Interface, InterfaceData.class);
+ if (childrenNodes.isRight()) {
+ return findInterfaceOnParentNode(parenNode.getKey().getMetadataDataDefinition().getUniqueId(),
+ interfaceName);
+
+ } else {
+ for (ImmutablePair<InterfaceData, GraphEdge> interfaceDataNode : childrenNodes.left().value()) {
+
+ GraphEdge interfaceEdge = interfaceDataNode.getRight();
+ Map<String, Object> interfaceEdgeProp = interfaceEdge.getProperties();
+
+ if (interfaceEdgeProp.get(GraphPropertiesDictionary.NAME.getProperty()).equals(interfaceName)) {
+ return Either.left(interfaceDataNode.getKey());
+ }
+
+ }
+ return findInterfaceOnParentNode(parenNode.getKey().getMetadataDataDefinition().getUniqueId(),
+ interfaceName);
+ }
+
+ }
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> createInterfaceOnResource(InterfaceDefinition interf,
+ String resourceId, String interfaceName, boolean failIfExist, boolean inTransaction) {
+
+ Either<InterfaceData, TitanOperationStatus> status = addInterfaceToGraph(interf, interfaceName, resourceId);
+
+ if (status.isRight()) {
+ titanGenericDao.rollback();
+ log.error("Failed to add interface {} to resource {}", interfaceName, resourceId);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ } else {
+
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ InterfaceData interfaceData = status.left().value();
+
+ InterfaceDefinition interfaceDefResult = convertInterfaceDataToInterfaceDefinition(interfaceData);
+ Map<String, Operation> operations = interf.getOperations();
+ if (operations != null && !operations.isEmpty()) {
+ Set<String> opNames = operations.keySet();
+ Map<String, Operation> newOperations = new HashMap<String, Operation>();
+ for (String operationName : opNames) {
+
+ Operation op = operations.get(operationName);
+ Either<OperationData, TitanOperationStatus> opStatus = addOperationToGraph(interf, operationName,
+ op, interfaceData);
+ if (status.isRight()) {
+ titanGenericDao.rollback();
+ log.error("Failed to add operation {} to interface {}", operationName, interfaceName);
+ } else if (status.isLeft()) {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ OperationData opData = opStatus.left().value();
+ Operation newOperation = this.convertOperationDataToOperation(opData);
+
+ ArtifactDefinition art = op.getImplementation();
+ if (art != null) {
+ Either<ArtifactDefinition, StorageOperationStatus> artRes = artifactOperation
+ .addArifactToComponent(art, (String) opData.getUniqueId(),
+ NodeTypeEnum.InterfaceOperation, failIfExist, true);
+ if (artRes.isRight()) {
+ titanGenericDao.rollback();
+ log.error("Failed to add artifact {} to interface {}", operationName, interfaceName);
+ } else {
+ newOperation.setImplementation(artRes.left().value());
+ }
+ newOperations.put(operationName, newOperation);
+ }
+ }
+ }
+ interfaceDefResult.setOperations(newOperations);
+ }
+ log.debug("The returned InterfaceDefintion is {}", interfaceDefResult);
+ return Either.left(interfaceDefResult);
+ }
+
+ }
+
+ @Override
+ public StorageOperationStatus createInterfaceOnResource(InterfaceDefinition interf, String resourceId,
+ String interfaceName, boolean failIfExist, boolean inTransaction, TitanVertex metadataVertex) {
+
+ Either<TitanVertex, TitanOperationStatus> interfaceResult = addInterfaceToGraph(interf, interfaceName,
+ resourceId, metadataVertex);
+
+ if (interfaceResult.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.error("Failed to add interface {} to resource {}", interfaceName, resourceId);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(interfaceResult.right().value());
+ } else {
+
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ TitanVertex interfaceVertex = interfaceResult.left().value();
+
+ // InterfaceDefinition interfaceDefResult =
+ // convertInterfaceDataToInterfaceDefinition(interfaceData);
+ Map<String, Operation> operations = interf.getOperations();
+ if (operations != null && !operations.isEmpty()) {
+ Set<String> opNames = operations.keySet();
+ for (String operationName : opNames) {
+
+ Operation op = operations.get(operationName);
+ Either<TitanVertex, TitanOperationStatus> operationResult = addOperationToGraph(interf,
+ operationName, op, interfaceVertex);
+ if (operationResult.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.error("Failed to add operation {} to interface {}", operationName, interfaceName);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(operationResult.right().value());
+ } else {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ TitanVertex operationVertex = operationResult.left().value();
+
+ ArtifactDefinition art = op.getImplementation();
+ if (art != null) {
+ String opId = (String) titanGenericDao.getProperty(operationVertex,
+ GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ StorageOperationStatus artRes = artifactOperation.addArifactToComponent(art, opId,
+ NodeTypeEnum.InterfaceOperation, failIfExist, operationVertex);
+ if (!artRes.equals(StorageOperationStatus.OK)) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.error("Failed to add artifact {} to interface {}", operationName, interfaceName);
+ return artRes;
+ }
+ }
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ }
+
+ @Override
+ public Either<Operation, StorageOperationStatus> deleteInterfaceOperation(String resourceId, String interfaceName,
+ String operationId) {
+ return deleteInterfaceOperation(resourceId, interfaceName, operationId, false);
+ }
+
+ @Override
+ public Either<Operation, StorageOperationStatus> deleteInterfaceOperation(String resourceId, String interfaceName,
+ String operationId, boolean inTransaction) {
+
+ Either<Operation, TitanOperationStatus> status = removeOperationOnGraph(resourceId, interfaceName, operationId);
+ if (status.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.error("Failed to delete operation {} of interface {} resource {}", operationId, interfaceName, resourceId);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status.right().value()));
+ } else {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+
+ Operation opDefResult = status.left().value();// convertOperationDataToOperation(operationData);
+ log.debug("The returned Operation is {}", opDefResult);
+ return Either.left(opDefResult);
+ }
+
+ }
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> deleteInterfaceOfResourceOnGraph(String resourceId,
+ InterfaceDefinition interfaceDef, boolean inTransaction) {
+
+ Map<String, Operation> operations = interfaceDef.getOperations();
+ String interfaceNameSplitted = getShortInterfaceName(interfaceDef);
+ if (operations != null) {
+ for (Entry<String, Operation> entry : operations.entrySet()) {
+
+ Operation op = entry.getValue();
+ Either<Operation, StorageOperationStatus> removeOperationFromResource = deleteInterfaceOperation(
+ resourceId, interfaceNameSplitted, op.getUniqueId(), true);
+ if (removeOperationFromResource.isRight()) {
+ if (false == inTransaction) {
+ titanGenericDao.rollback();
+ }
+ log.error("Failed to delete operation {} of interface {} resource {}", op.getUniqueId(), interfaceDef.getType(), resourceId);
+ return Either.right(removeOperationFromResource.right().value());
+ }
+ }
+ }
+ return Either.left(interfaceDef);
+
+ }
+
+ private Either<Operation, TitanOperationStatus> removeOperationOnGraph(String resourceId, String interfaceName,
+ String operationId) {
+ log.debug("Before deleting operation from graph {}", operationId);
+
+ Either<List<ImmutablePair<InterfaceData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), resourceId,
+ GraphEdgeLabels.INTERFACE, NodeTypeEnum.Interface, InterfaceData.class);
+
+ if (childrenNodes.isRight()) {
+ log.debug("Not found interface {}", interfaceName);
+ return Either.right(childrenNodes.right().value());
+ }
+ OperationData opData = null;
+ for (ImmutablePair<InterfaceData, GraphEdge> interfaceDataNode : childrenNodes.left().value()) {
+
+ GraphEdge interfaceEdge = interfaceDataNode.getRight();
+ Map<String, Object> interfaceEdgeProp = interfaceEdge.getProperties();
+
+ String interfaceSplitedName = splitType(interfaceName);
+
+ if (interfaceEdgeProp.get(GraphPropertiesDictionary.NAME.getProperty()).equals(interfaceSplitedName)) {
+ Either<List<ImmutablePair<OperationData, GraphEdge>>, TitanOperationStatus> operationRes = titanGenericDao
+ .getChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ (String) interfaceDataNode.getLeft().getUniqueId(), GraphEdgeLabels.INTERFACE_OPERATION,
+ NodeTypeEnum.InterfaceOperation, OperationData.class);
+ if (operationRes.isRight()) {
+ log.error("Failed to find operation {}", operationId, interfaceName);
+ return Either.right(operationRes.right().value());
+ }
+ List<ImmutablePair<OperationData, GraphEdge>> operations = operationRes.left().value();
+
+ for (ImmutablePair<OperationData, GraphEdge> operationPairEdge : operations) {
+
+ opData = operationPairEdge.getLeft();
+ if (opData.getUniqueId().equals(operationId)) {
+
+ Either<ImmutablePair<ArtifactData, GraphEdge>, TitanOperationStatus> artifactRes = titanGenericDao
+ .getChild(GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ (String) operationPairEdge.getLeft().getUniqueId(),
+ GraphEdgeLabels.ARTIFACT_REF, NodeTypeEnum.ArtifactRef, ArtifactData.class);
+ Either<ArtifactDefinition, StorageOperationStatus> arStatus = null;
+ if (artifactRes.isLeft()) {
+ ArtifactData arData = artifactRes.left().value().getKey();
+ arStatus = artifactOperation.removeArifactFromResource(
+ (String) operationPairEdge.getLeft().getUniqueId(), (String) arData.getUniqueId(),
+ NodeTypeEnum.InterfaceOperation, true, true);
+ if (arStatus.isRight()) {
+ log.debug("failed to delete artifact {}", arData.getUniqueId());
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ }
+ Either<OperationData, TitanOperationStatus> deleteOpStatus = titanGenericDao.deleteNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.InterfaceOperation), opData.getUniqueId(),
+ OperationData.class);
+ if (deleteOpStatus.isRight()) {
+ log.debug("failed to delete operation {}", opData.getUniqueId());
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+ opData = deleteOpStatus.left().value();
+ Operation operation = new Operation(opData.getOperationDataDefinition());
+ if (arStatus != null) {
+ operation.setImplementation(arStatus.left().value());
+ }
+ if (operations.size() <= 1) {
+ Either<InterfaceData, TitanOperationStatus> deleteInterfaceStatus = titanGenericDao
+ .deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface),
+ interfaceDataNode.left.getUniqueId(), InterfaceData.class);
+ if (deleteInterfaceStatus.isRight()) {
+ log.debug("failed to delete interface {}", interfaceDataNode.left.getUniqueId());
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ }
+
+ }
+
+ return Either.left(operation);
+
+ }
+ }
+ }
+ }
+
+ log.debug("Not found operation {}", interfaceName);
+ return Either.right(TitanOperationStatus.INVALID_ID);
+ // }
+
+ }
+
+ private String splitType(String interfaceName) {
+ String interfaceSplittedName;
+ String[] packageName = interfaceName.split("\\.");
+
+ if (packageName.length == 0) {
+ interfaceSplittedName = interfaceName;
+ } else {
+ interfaceSplittedName = packageName[packageName.length - 1];
+ }
+
+ return interfaceSplittedName.toLowerCase();
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ public void setArtifactOperation(ArtifactOperation artifactOperation) {
+ this.artifactOperation = artifactOperation;
+ }
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> createInterfaceType(InterfaceDefinition interf,
+ boolean inTransaction) {
+ Either<InterfaceDefinition, StorageOperationStatus> result = null;
+ try {
+
+ InterfaceData interfaceData = new InterfaceData(interf);
+ interf.setUniqueId(interf.getType().toLowerCase());
+
+ Either<InterfaceData, TitanOperationStatus> existInterface = titanGenericDao
+ .getNode(interfaceData.getUniqueIdKey(), interfaceData.getUniqueId(), InterfaceData.class);
+
+ if (existInterface.isLeft()) {
+ // already exist
+ log.debug("Interface type already exist {}", interfaceData);
+ result = Either.right(StorageOperationStatus.ENTITY_ALREADY_EXISTS);
+ return result;
+ }
+
+ log.debug("Before adding interface type to graph {}", interfaceData);
+ Either<InterfaceData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(interfaceData,
+ InterfaceData.class);
+ log.debug("After adding property type to graph {}", interfaceData);
+
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add interface {} to graph. Status is {}", interf.getType(), operationStatus);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationStatus));
+ return result;
+ }
+
+ InterfaceDefinition interfaceDefResult = convertInterfaceDataToInterfaceDefinition(interfaceData);
+ Map<String, Operation> operations = interf.getOperations();
+
+ if (operations != null && !operations.isEmpty()) {
+ Map<String, Operation> newOperations = new HashMap<String, Operation>();
+
+ for (Map.Entry<String, Operation> operation : operations.entrySet()) {
+ Either<OperationData, TitanOperationStatus> opStatus = addOperationToGraph(interf,
+ operation.getKey(), operation.getValue(), interfaceData);
+ if (opStatus.isRight()) {
+ titanGenericDao.rollback();
+ log.error("Failed to add operation {} to interface {}", operation.getKey(), interf.getType());
+
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(opStatus.right().value()));
+ return result;
+ } else {
+ OperationData opData = opStatus.left().value();
+ Operation newOperation = this.convertOperationDataToOperation(opData);
+ newOperations.put(operation.getKey(), newOperation);
+ }
+ }
+ interfaceDefResult.setOperations(newOperations);
+ }
+ result = Either.left(interfaceDefResult);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> getInterface(String interfaceId) {
+ Either<InterfaceData, TitanOperationStatus> getResult = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface), interfaceId, InterfaceData.class);
+ if (getResult.isLeft()) {
+ InterfaceData interfaceData = getResult.left().value();
+ return Either.left(convertInterfaceDataToInterfaceDefinition(interfaceData));
+ } else {
+ TitanOperationStatus titanStatus = getResult.right().value();
+ log.debug("Node with id {} was not found in the graph. Status: {}", interfaceId, titanStatus);
+ StorageOperationStatus storageOperationStatus = DaoStatusConverter
+ .convertTitanStatusToStorageStatus(titanStatus);
+ return Either.right(storageOperationStatus);
+ }
+ }
+
+ @Override
+ public StorageOperationStatus associateInterfaceToNode(GraphNode node, InterfaceDefinition interfaceDefinition,
+ TitanVertex metadataVertex) {
+
+ Either<TitanVertex, TitanOperationStatus> interfaceData = titanGenericDao.getVertexByProperty(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface), interfaceDefinition.getUniqueId());
+ if (interfaceData.isRight()) {
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(interfaceData.right().value());
+ }
+
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ String interfaceName = getShortInterfaceName(interfaceDefinition);
+
+ properties.put(GraphPropertiesDictionary.NAME.getProperty(), interfaceName.toLowerCase());
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(metadataVertex, interfaceData.left().value(),
+ GraphEdgeLabels.INTERFACE, properties);
+ if (!createRelation.equals(TitanOperationStatus.OK)) {
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(createRelation);
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ public String getShortInterfaceName(InterfaceDataDefinition interfaceDefinition) {
+ String[] packageName = interfaceDefinition.getType().split("\\.");
+ String interfaceName;
+ if (packageName.length == 0) {
+ interfaceName = interfaceDefinition.getType();
+ } else {
+ interfaceName = packageName[packageName.length - 1];
+ }
+ return interfaceName.toLowerCase();
+ }
+
+ /**
+ *
+ */
+ public Either<InterfaceDefinition, StorageOperationStatus> createInterfaceType(InterfaceDefinition interf) {
+ return createInterfaceType(interf, false);
+ }
+
+ @Override
+ public Either<Operation, StorageOperationStatus> getSpecificOperation(String resourceId, String interfaceType,
+ String operationName) {
+ log.trace("Getting operation, resourceId {}, interfaceType {}, operationName {}", resourceId, interfaceType,
+ operationName);
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> allInterfacesOfResource = getAllInterfacesOfResource(
+ resourceId, false);
+ if (allInterfacesOfResource.isRight() || allInterfacesOfResource.left().value() == null
+ || allInterfacesOfResource.left().value().get(interfaceType) == null) {
+ log.debug("Couldn't find interface definition of type {} for resource id {}", interfaceType, resourceId);
+ return Either.right(allInterfacesOfResource.right().value());
+ }
+ InterfaceDefinition interfaceDefinition = allInterfacesOfResource.left().value().get(interfaceType);
+ Map<String, Operation> operations = interfaceDefinition.getOperations();
+ if (operations == null || operations.get(operationName) == null) {
+ log.debug("Couldn't find operation for operation name {}, interface type {}", operationName, interfaceType);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ return Either.left(operations.get(operationName));
+ }
+
+ @Override
+ public Either<InterfaceDefinition, StorageOperationStatus> dissociateInterfaceFromNode(GraphNode node,
+ InterfaceDefinition interfaceDefinition) {
+
+ Either<InterfaceData, TitanOperationStatus> interfaceData = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface), interfaceDefinition.getUniqueId(),
+ InterfaceData.class);
+ if (interfaceData.isRight()) {
+ log.debug("Couldn't find interface {}", interfaceDefinition);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(interfaceData.right().value()));
+ }
+
+ InterfaceData value = interfaceData.left().value();
+ Either<GraphRelation, TitanOperationStatus> deleteRelation = titanGenericDao.deleteRelation(node, value,
+ GraphEdgeLabels.INTERFACE);
+ if (deleteRelation.isRight()) {
+ TitanOperationStatus status = deleteRelation.right().value();
+ log.debug("Couldn't dissociate interface between node {} to node {}. Status is {}", node.getUniqueId(),
+ value.getUniqueId(), status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ return Either.left(interfaceDefinition);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperation.java
new file mode 100644
index 0000000000..863975893c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperation.java
@@ -0,0 +1,1143 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.ILifecycleOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("lifecycle-operation")
+public class LifecycleOperation implements ILifecycleOperation {
+
+ public static final String VERSION_DELIMETER = ".";
+ public static final String VERSION_DELIMETER_REGEXP = "\\.";
+
+ public LifecycleOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(LifecycleOperation.class.getName());
+
+ @javax.annotation.Resource
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ private ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ private ProductOperation productOperation;
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ public ResourceOperation getResourceOperation() {
+ return resourceOperation;
+ }
+
+ public void setResourceOperation(ResourceOperation resourceOperation) {
+ this.resourceOperation = resourceOperation;
+ }
+
+ public ServiceOperation getServiceOperation() {
+ return serviceOperation;
+ }
+
+ public ComponentOperation getComponentOperation(NodeTypeEnum componentType) {
+ if (NodeTypeEnum.Service.equals(componentType)) {
+ return serviceOperation;
+ } else if (NodeTypeEnum.Resource.equals(componentType)) {
+ return resourceOperation;
+ } else if (NodeTypeEnum.Product.equals(componentType)) {
+ return productOperation;
+ }
+ return null;
+ }
+
+ public void setServiceOperation(ServiceOperation serviceOperation) {
+ this.serviceOperation = serviceOperation;
+ }
+
+ public TitanGenericDao getTitanGenericDao() {
+ return titanGenericDao;
+ }
+
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public Either<User, StorageOperationStatus> getComponentOwner(String resourceId, NodeTypeEnum nodeType,
+ boolean inTransaction) {
+
+ Either<User, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ try {
+
+ Either<ImmutablePair<UserData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(
+ UniqueIdBuilder.getKeyByNodeType(nodeType), resourceId, GraphEdgeLabels.STATE, NodeTypeEnum.User,
+ UserData.class);
+
+ if (parentNode.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(parentNode.right().value()));
+ }
+
+ ImmutablePair<UserData, GraphEdge> value = parentNode.left().value();
+
+ User owner = new User(value.left);
+ result = Either.left(owner);
+
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> checkoutComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User currentOwner, boolean inTransaction) {
+ Either<? extends Component, StorageOperationStatus> result = null;
+
+ try {
+ // update old component
+ if (!component.getLifecycleState().equals(LifecycleStateEnum.CERTIFIED)) {
+ component.setHighestVersion(false);
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+ Either<? extends Component, StorageOperationStatus> updateComponent = componentOperation
+ .updateComponent(component, inTransaction, titanGenericDao, component.getClass(), nodeType);
+ if (updateComponent.isRight()) {
+ StorageOperationStatus error = updateComponent.right().value();
+ log.debug("Couldn't set lifecycle for component {} to state {}, error: {}", component.getUniqueId(),
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, error);
+ return Either.right(error);
+ }
+
+ StorageOperationStatus changeStateToLastState = changeStateRelation(nodeType, component.getUniqueId(),
+ currentOwner, GraphEdgeLabels.STATE, GraphEdgeLabels.LAST_STATE);
+ if (!changeStateToLastState.equals(StorageOperationStatus.OK)) {
+ result = Either.right(changeStateToLastState);
+ return result;
+ }
+ }
+
+ // clone the component
+ result = cloneComponentForCheckout(component, nodeType, modifier);
+ if (result.isRight()) {
+ log.debug("Couldn't set lifecycle for component {} to state {}, error: {}", component.getUniqueId(),
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, result.right().value());
+ return result;
+ }
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Either<? extends Component, StorageOperationStatus> cloneComponentForCertified(Component component,
+ User modifier, Integer majorVersion) {
+
+ // set new version
+ String certifiedVersion = (majorVersion + 1) + VERSION_DELIMETER + "0";
+ component.setVersion(certifiedVersion);
+ component.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ component.setLastUpdateDate(null);
+ component.setLastUpdaterUserId(modifier.getUserId());
+ component.setHighestVersion(true);
+
+ ComponentOperation componentOperation = getComponentOperation(component.getComponentType().getNodeType());
+ Either<? extends Component, StorageOperationStatus> cloneComponentResult = componentOperation
+ .cloneComponent(component, certifiedVersion, LifecycleStateEnum.CERTIFIED, true);
+
+ return cloneComponentResult;
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> undoCheckout(NodeTypeEnum nodeType, Component component,
+ User modifier, User currentOwner, boolean inTransaction) {
+ Either<? extends Component, StorageOperationStatus> result = null;
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+
+ // this is in case prevVersion is 0.0 - returning OOTB component
+ Component prevComponent = componentOperation.getDefaultComponent();
+ try {
+ // find previous version
+ String[] versionParts = component.getVersion().split(VERSION_DELIMETER_REGEXP);
+ Integer minorVersion = Integer.parseInt(versionParts[1]) - 1;
+ String previousVersion = versionParts[0] + VERSION_DELIMETER + minorVersion;
+
+ if (!previousVersion.equals("0.0")) {
+ Either<? extends Component, StorageOperationStatus> updateOldResourceResult = updateOldComponentBeforeUndoCheckout(
+ componentOperation, prevComponent, component, previousVersion, nodeType, true);
+ if (updateOldResourceResult.isRight()) {
+ result = updateOldResourceResult;
+ return result;
+ }
+ prevComponent = updateOldResourceResult.left().value();
+ }
+
+ // delete the component
+ Either<? extends Component, StorageOperationStatus> deleteResourceResult = componentOperation
+ .deleteComponent(component.getUniqueId(), true);
+ if (deleteResourceResult.isRight()) {
+ result = deleteResourceResult;
+ return result;
+ }
+
+ // return the deleted resource
+ result = Either.left(prevComponent);
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> checkinComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, boolean inTransaction) {
+ Either<? extends Component, StorageOperationStatus> result = null;
+ try {
+ StorageOperationStatus updateCheckinInGraph = updateCheckinInGraph(nodeType, component.getUniqueId(),
+ component.getLifecycleState(), modifier, owner);
+ if (!updateCheckinInGraph.equals(StorageOperationStatus.OK)) {
+ log.error("failed to update state of resource {}. status={}", component.getUniqueId(),
+ updateCheckinInGraph);
+ return Either.right(updateCheckinInGraph);
+ }
+ LifecycleStateEnum state = LifecycleStateEnum.NOT_CERTIFIED_CHECKIN;
+ ComponentParametersView componentParametersView = buildFilterForFetchComponentAfterChangeState();
+ result = updateComponentMD(component, modifier, state, nodeType, componentParametersView);
+ if (result.isRight()) {
+ log.debug("Couldn't set lifecycle for component {} to state {}, error: {}", component.getUniqueId(),
+ state, result.right().value());
+ }
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private ComponentParametersView buildFilterForFetchComponentAfterChangeState() {
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ componentParametersView.disableAll();
+ componentParametersView.setIgnoreUsers(false);
+ // Used when we running multiple change states and want to use the
+ // result from another change
+ // state(LifecycleOperationTest.certificationStatusChange)
+ componentParametersView.setIgnoreCategories(false);
+ return componentParametersView;
+ }
+
+ private StorageOperationStatus updateCheckinInGraph(NodeTypeEnum componentType, String componentId,
+ LifecycleStateEnum state, User modifier, User owner) {
+
+ // check if we cancel rfc
+ if (state.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION)) {
+
+ // remove last checkin
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ UniqueIdData resourceData = new UniqueIdData(componentType, componentId);
+ Either<GraphRelation, TitanOperationStatus> deleteResult = titanGenericDao
+ .deleteIncomingRelationByCriteria(resourceData, GraphEdgeLabels.LAST_STATE, props);
+ if (deleteResult.isRight()) {
+ log.debug("failed to update last state relation");
+ return StorageOperationStatus.INCONSISTENCY;
+ }
+ }
+
+ // remove CHECKOUT relation
+ StorageOperationStatus removeUserToResourceRelation = removeUserToResourceRelation(componentType,
+ owner.getUserId(), componentId, GraphEdgeLabels.STATE);
+ if (!removeUserToResourceRelation.equals(StorageOperationStatus.OK)) {
+ return removeUserToResourceRelation;
+ }
+
+ // create CHECKIN relation
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ StorageOperationStatus createUserToResourceRelation = createUserToResourceRelation(componentType,
+ modifier.getUserId(), componentId, GraphEdgeLabels.STATE, props);
+ if (!createUserToResourceRelation.equals(StorageOperationStatus.OK)) {
+ return createUserToResourceRelation;
+ }
+
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> requestCertificationComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, boolean inTransaction) {
+ Either<? extends Component, StorageOperationStatus> result = null;
+ try {
+ StorageOperationStatus updateRfcOnGraph = updateRfcOnGraph(nodeType, component.getUniqueId(),
+ component.getLifecycleState(), modifier, owner);
+ if (!updateRfcOnGraph.equals(StorageOperationStatus.OK)) {
+ log.error("failed to update state of resource {}. status={}", component.getUniqueId(),
+ updateRfcOnGraph);
+ return Either.right(updateRfcOnGraph);
+ }
+
+ LifecycleStateEnum state = LifecycleStateEnum.READY_FOR_CERTIFICATION;
+
+ ComponentParametersView componentParametersView = buildFilterForFetchComponentAfterChangeState();
+
+ result = updateComponentMD(component, modifier, state, nodeType, componentParametersView);
+ if (result.isRight()) {
+ log.debug("Couldn't set lifecycle for component {} to state {}, error: {}", component.getUniqueId(),
+ state, result.right().value());
+ return result;
+ }
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private StorageOperationStatus updateRfcOnGraph(NodeTypeEnum componentType, String componentId,
+ LifecycleStateEnum state, User modifier, User owner) {
+
+ if (state.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT)) {
+ // if this is atomic checkin + RFC: create checkin relation
+
+ // remove CHECKOUT relation
+ StorageOperationStatus relationStatus = removeUserToResourceRelation(componentType, owner.getUserId(),
+ componentId, GraphEdgeLabels.STATE);
+ if (!relationStatus.equals(StorageOperationStatus.OK)) {
+ return relationStatus;
+ }
+
+ // create CHECKIN relation
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ relationStatus = createUserToResourceRelation(componentType, modifier.getUserId(), componentId,
+ GraphEdgeLabels.LAST_STATE, props);
+ if (!relationStatus.equals(StorageOperationStatus.OK)) {
+ return relationStatus;
+ }
+ } else {
+ StorageOperationStatus changeStatus = changeRelationLabel(componentType, componentId, owner,
+ GraphEdgeLabels.STATE, GraphEdgeLabels.LAST_STATE);
+ if (!changeStatus.equals(StorageOperationStatus.OK)) {
+ return changeStatus;
+ }
+ }
+
+ // create RFC relation
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ StorageOperationStatus changeRelationLabel = createUserToResourceRelation(componentType, modifier.getUserId(),
+ componentId, GraphEdgeLabels.STATE, props);
+ if (!changeRelationLabel.equals(StorageOperationStatus.OK)) {
+ return changeRelationLabel;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus changeRelationLabel(NodeTypeEnum componentType, String componentId, User owner,
+ GraphEdgeLabels prevLabel, GraphEdgeLabels toLabel) {
+ UniqueIdData resourceV = new UniqueIdData(componentType, componentId);
+ UserData userV = new UserData();
+ userV.setUserId(owner.getUserId());
+ Either<GraphRelation, TitanOperationStatus> replaceRelationLabelResult = titanGenericDao
+ .replaceRelationLabel(userV, resourceV, prevLabel, toLabel);
+ if (replaceRelationLabelResult.isRight()) {
+ log.error("failed to replace label from last state to state");
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(replaceRelationLabelResult.right().value());
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> startComponentCertification(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, boolean inTransaction) {
+ Either<? extends Component, StorageOperationStatus> result = null;
+ try {
+ StorageOperationStatus updateOnGraph = updateStartCertificationOnGraph(nodeType, component.getUniqueId(),
+ modifier, owner);
+ if (!updateOnGraph.equals(StorageOperationStatus.OK)) {
+ log.error("failed to update state of resource {}. status={}", component.getUniqueId(), updateOnGraph);
+ return Either.right(updateOnGraph);
+ }
+
+ LifecycleStateEnum state = LifecycleStateEnum.CERTIFICATION_IN_PROGRESS;
+ ComponentParametersView componentParametersView = buildFilterForFetchComponentAfterChangeState();
+
+ result = updateComponentMD(component, modifier, state, nodeType, componentParametersView);
+ if (result.isRight()) {
+ log.debug("Couldn't set lifecycle for component {} to state {}, error: {}", component.getUniqueId(),
+ state, result.right().value());
+ }
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private StorageOperationStatus updateStartCertificationOnGraph(NodeTypeEnum componentType, String componentId,
+ User modifier, User owner) {
+ StorageOperationStatus changeRelationLabel = changeRelationLabel(componentType, componentId, owner,
+ GraphEdgeLabels.STATE, GraphEdgeLabels.LAST_STATE);
+ if (!changeRelationLabel.equals(StorageOperationStatus.OK)) {
+ return changeRelationLabel;
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ StorageOperationStatus createUserToResourceRelation = createUserToResourceRelation(componentType,
+ modifier.getUserId(), componentId, GraphEdgeLabels.STATE, props);
+ if (!createUserToResourceRelation.equals(StorageOperationStatus.OK)) {
+ return createUserToResourceRelation;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> certifyComponent(NodeTypeEnum nodeType,
+ Component component, User modifier, User currentOwner, boolean inTransaction) {
+ Either<? extends Component, StorageOperationStatus> result = null;
+
+ try {
+ String resourceIdBeforeCertify = component.getUniqueId();
+ String[] versionParts = component.getVersion().split(VERSION_DELIMETER_REGEXP);
+ Integer majorVersion = Integer.parseInt(versionParts[0]);
+
+ // update old certified resource
+ if (majorVersion > 0) {
+ StorageOperationStatus updateLastCertifiedResource = StorageOperationStatus.OK;
+ updateLastCertifiedResource = updateLastCertifiedComponent(component, majorVersion);
+ if (!updateLastCertifiedResource.equals(StorageOperationStatus.OK)) {
+ return Either.right(updateLastCertifiedResource);
+ }
+ }
+
+ // clone the resource
+ Either<? extends Component, StorageOperationStatus> createResourceResult = Either
+ .right(StorageOperationStatus.GENERAL_ERROR);
+ switch (nodeType) {
+ case Service:
+ case Resource:
+ createResourceResult = cloneComponentForCertified(component, modifier, majorVersion);
+ break;
+ default:
+ log.error("component object is with type {} . It's not supported type");
+ result = Either.right(StorageOperationStatus.BAD_REQUEST);
+ return result;
+ }
+
+ if (createResourceResult.isRight()) {
+ log.error("failed to create new resource version for checkout.");
+ result = createResourceResult;
+ return createResourceResult;
+ }
+
+ Component certifiedResource = createResourceResult.left().value();
+
+ // add rfc relation to preserve follower information
+ StorageOperationStatus addRfcRelation = addRfcRelationToCertfiedComponent(nodeType, resourceIdBeforeCertify,
+ certifiedResource.getUniqueId());
+ if (!addRfcRelation.equals(StorageOperationStatus.OK)) {
+ result = Either.right(addRfcRelation);
+ return result;
+ }
+
+ result = Either.left(certifiedResource);
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> deleteOldComponentVersions(NodeTypeEnum nodeType,
+ String componentName, String uuid, boolean inTransaction) {
+
+ Either<Boolean, StorageOperationStatus> result = null;
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+
+ try {
+ Either<List<Component>, StorageOperationStatus> oldVersionsToDelete = getComponentTempVersions(nodeType,
+ uuid);
+
+ if (oldVersionsToDelete.isRight()) {
+ result = Either.right(oldVersionsToDelete.right().value());
+ return result;
+ }
+
+ for (Component resourceToDelete : oldVersionsToDelete.left().value()) {
+
+ Either<Component, StorageOperationStatus> updateResource = componentOperation
+ .markComponentToDelete(resourceToDelete, inTransaction);
+ if (updateResource.isRight()) {
+ result = Either.right(updateResource.right().value());
+ return result;
+ }
+ }
+ result = Either.left(true);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private StorageOperationStatus addRfcRelationToCertfiedComponent(NodeTypeEnum componentType,
+ String resourceIdBeforeCertify, String uniqueId) {
+
+ // get user of certification request
+ UniqueIdData componentV = new UniqueIdData(componentType, resourceIdBeforeCertify);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> rfcRelationResponse = titanGenericDao
+ .getIncomingRelationByCriteria(componentV, GraphEdgeLabels.LAST_STATE, props);
+ if (rfcRelationResponse.isRight()) {
+ TitanOperationStatus status = rfcRelationResponse.right().value();
+ log.error("failed to find rfc relation for component {}. status=", resourceIdBeforeCertify, status);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+ GraphRelation rfcRelation = rfcRelationResponse.left().value();
+ rfcRelation.setTo(
+ new RelationEndPoint(componentType, GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueId));
+
+ Either<GraphRelation, TitanOperationStatus> createRelationResponse = titanGenericDao
+ .createRelation(rfcRelation);
+ if (createRelationResponse.isRight()) {
+ TitanOperationStatus status = createRelationResponse.right().value();
+ log.error("failed to create rfc relation for component {}. status=", uniqueId, status);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ }
+ return StorageOperationStatus.OK;
+
+ }
+
+ private StorageOperationStatus updateLastCertifiedComponent(Component component, Integer majorVersion) {
+
+ NodeTypeEnum nodeType = component.getComponentType().getNodeType();
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+ Map<String, Object> additionalQueryParams = null;
+ if (nodeType == NodeTypeEnum.Resource) {
+ ResourceTypeEnum resourceType = ((Resource) component).getResourceType();
+ additionalQueryParams = new HashMap<String, Object>();
+ additionalQueryParams.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), resourceType.name());
+ }
+ Either<? extends Component, StorageOperationStatus> getLastCertifiedResponse = componentOperation
+ .getComponentByNameAndVersion(component.getName(), majorVersion + VERSION_DELIMETER + "0",
+ additionalQueryParams, true);
+
+ if (getLastCertifiedResponse.isRight()) {
+ log.error("failed to update last certified resource. status={}", getLastCertifiedResponse.right().value());
+ return getLastCertifiedResponse.right().value();
+ }
+
+ Component lastCertified = getLastCertifiedResponse.left().value();
+ lastCertified.setHighestVersion(false);
+ Either<Component, StorageOperationStatus> updateResource = componentOperation.updateComponent(lastCertified,
+ true);
+ if (updateResource.isRight()) {
+ log.error("failed to update last certified resource. status={}", updateResource.right().value());
+ return updateResource.right().value();
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private Either<Component, StorageOperationStatus> cloneComponentForCheckout(Component component,
+ NodeTypeEnum nodeType, User modifier) {
+
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+ String prevId = component.getUniqueId();
+ // set new version
+ Either<String, StorageOperationStatus> nextVersion = getNextVersion(component.getVersion());
+ if (nextVersion.isRight()) {
+ return Either.right(nextVersion.right().value());
+ }
+
+ // if checkout on certified service - init distribution status back
+ if (nodeType == NodeTypeEnum.Service && component.getLifecycleState().equals(LifecycleStateEnum.CERTIFIED)) {
+ ((Service) component).setDistributionStatus(DistributionStatusEnum.DISTRIBUTION_NOT_APPROVED);
+ }
+
+ String version = nextVersion.left().value();
+ component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ component.setLastUpdateDate(null);
+ component.setLastUpdaterUserId(modifier.getUserId());
+ component.setHighestVersion(true);
+
+ // check version of resource does not exist. Note that resource type VF
+ // can have same name as resource type VFC
+ Map<String, Object> additionalQueryParams = null;
+ if (nodeType == NodeTypeEnum.Resource) {
+ ResourceTypeEnum resourceType = ((Resource) component).getResourceType();
+ additionalQueryParams = new HashMap<String, Object>();
+ additionalQueryParams.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), resourceType.name());
+ }
+ String name = component.getComponentMetadataDefinition().getMetadataDataDefinition().getName();
+ Either<Component, StorageOperationStatus> alreadyExistResult = componentOperation
+ .getComponentByNameAndVersion(name, version, additionalQueryParams, true);
+ if (alreadyExistResult.isLeft()) {
+ log.debug("Component with name {} and version {} already exist", name, version);
+ return Either.right(StorageOperationStatus.ENTITY_ALREADY_EXISTS);
+
+ }
+
+ StorageOperationStatus storageOperationStatus = alreadyExistResult.right().value();
+ if (storageOperationStatus != StorageOperationStatus.NOT_FOUND) {
+ log.debug(
+ "Unexpected error when checking if component with name {} and version {} already exist, error: {}",
+ name, version, storageOperationStatus);
+ return Either.right(storageOperationStatus);
+ }
+
+ Either<Component, StorageOperationStatus> cloneComponentResponse = componentOperation.cloneComponent(component,
+ version, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, true);
+
+ return cloneComponentResponse;
+ }
+
+ private Either<String, StorageOperationStatus> getNextVersion(String currVersion) {
+ String[] versionParts = currVersion.split(VERSION_DELIMETER_REGEXP);
+ if (versionParts == null || versionParts.length != 2) {
+ log.error("invalid version {}", currVersion);
+ return Either.right(StorageOperationStatus.BAD_REQUEST);
+ }
+
+ Integer minorVersion = Integer.parseInt(versionParts[1]) + 1;
+ String newVersion = versionParts[0] + VERSION_DELIMETER + minorVersion;
+ return Either.left(newVersion);
+ }
+
+ private StorageOperationStatus setRelationForCancelCertification(LifecycleStateEnum nextState,
+ NodeTypeEnum componentType, String componentId) {
+
+ StorageOperationStatus result = StorageOperationStatus.GENERAL_ERROR;
+ Map<String, Object> props = new HashMap<String, Object>();
+ UniqueIdData componentData = new UniqueIdData(componentType, componentId);
+
+ // delete relation CERTIFICATION_IN_PROGRESS
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ Either<GraphRelation, TitanOperationStatus> deleteResult = titanGenericDao
+ .deleteIncomingRelationByCriteria(componentData, GraphEdgeLabels.STATE, props);
+ if (deleteResult.isRight()) {
+ log.debug("failed to update last state relation");
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+
+ // delete relation READY_FOR_CERTIFICATION (LAST_STATE)
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), nextState);
+
+ deleteResult = titanGenericDao.deleteIncomingRelationByCriteria(componentData, GraphEdgeLabels.LAST_STATE,
+ props);
+ if (deleteResult.isRight()) {
+ log.debug("failed to update last state relation");
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+ GraphRelation origRelation = deleteResult.left().value();
+
+ // create relation READY_FOR_CERTIFICATION (STATE)
+ UserData user = new UserData();
+ user.setUserId((String) origRelation.getFrom().getIdValue());
+ Either<GraphRelation, TitanOperationStatus> createRelationResult = titanGenericDao.createRelation(user,
+ componentData, GraphEdgeLabels.STATE, origRelation.toGraphMap());
+
+ if (createRelationResult.isRight()) {
+ log.error("failed to update last state relation. status={}", createRelationResult.right().value());
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus setRelationForFailCertification(LifecycleStateEnum nextState,
+ NodeTypeEnum componentType, String componentId) {
+
+ StorageOperationStatus result = null;
+ Map<String, Object> props = new HashMap<String, Object>();
+ UniqueIdData componentData = new UniqueIdData(componentType, componentId);
+
+ // delete relation CERTIFICATION_IN_PROGRESS
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ Either<GraphRelation, TitanOperationStatus> deleteResult = titanGenericDao
+ .deleteIncomingRelationByCriteria(componentData, GraphEdgeLabels.STATE, props);
+ if (deleteResult.isRight()) {
+ log.debug("failed to update last state relation");
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+
+ // delete relation READY_FOR_CERTIFICATION
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+
+ deleteResult = titanGenericDao.deleteIncomingRelationByCriteria(componentData, GraphEdgeLabels.LAST_STATE,
+ props);
+ if (deleteResult.isRight()) {
+ log.debug("failed to update last state relation");
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+
+ // delete relation NOT_CERTIFIED_CHECKIN (in order to change to STATE)
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ deleteResult = titanGenericDao.deleteIncomingRelationByCriteria(componentData, GraphEdgeLabels.LAST_STATE,
+ props);
+ if (deleteResult.isRight()) {
+ log.debug("failed to update last state relation");
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+
+ // create new STATE relation NOT_CERTIFIED_CHECKIN
+ GraphRelation origRelation = deleteResult.left().value();
+ UserData user = new UserData();
+ user.setUserId((String) origRelation.getFrom().getIdValue());
+ Either<GraphRelation, TitanOperationStatus> createRelationResult = titanGenericDao.createRelation(user,
+ componentData, GraphEdgeLabels.STATE, origRelation.toGraphMap());
+
+ if (createRelationResult.isRight()) {
+ log.debug("failed to update last state relation");
+ result = StorageOperationStatus.INCONSISTENCY;
+ return result;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ /**
+ * update service metadata - lastUpdater and state
+ *
+ * @param component
+ * @param modifier
+ * @param nextState
+ * @return
+ */
+ private Either<Component, StorageOperationStatus> updateComponentMD(Component component, User modifier,
+ LifecycleStateEnum nextState, NodeTypeEnum nodeType,
+ ComponentParametersView returnedComponentParametersViewFilter) {
+
+ if (returnedComponentParametersViewFilter == null) {
+ returnedComponentParametersViewFilter = new ComponentParametersView();
+ }
+
+ Either<Component, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ component.setLastUpdateDate(null);
+ component.setLastUpdaterUserId(modifier.getUserId());
+
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+ ComponentParametersView filterParametersView = buildFilterForFetchComponentAfterChangeState();
+ log.debug("updateComponentMD::updateComponentFilterResult start");
+ result = componentOperation.updateComponentFilterResult(component, true, filterParametersView);
+ log.debug("updateComponentMD::updateComponentFilterResult end");
+ if (result.isRight()) {
+ log.debug("Failed to update component for certification request, error: {}", result.right().value());
+ return result;
+ }
+ log.debug("updateComponentMD::getAndUpdateMetadata start");
+ // get service MD
+ Either<ComponentMetadataData, TitanOperationStatus> componentDataResult = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(nodeType), component.getUniqueId(), ComponentMetadataData.class);
+ if (componentDataResult.isRight()) {
+ log.debug("failed to get service data from graph");
+ return Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(componentDataResult.right().value()));
+ }
+
+ // set state on resource
+ ComponentMetadataData componentData = componentDataResult.left().value();
+ componentData.getMetadataDataDefinition().setState(nextState.name());
+ component.setLifecycleState(nextState);
+ Either<ComponentMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(componentData,
+ ComponentMetadataData.class);
+ log.debug("updateComponentMD::getAndUpdateMetadata end");
+ if (updateNode.isRight()) {
+ log.error("Failed to update component " + component.getUniqueId() + ". status is "
+ + updateNode.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value()));
+ return result;
+ }
+ log.debug("updateComponentMD::getAndUpdateMetadata start");
+ Either<Object, StorageOperationStatus> serviceAfterChange = componentOperation
+ .getComponent(component.getUniqueId(), returnedComponentParametersViewFilter, true);
+ log.debug("updateComponentMD::getAndUpdateMetadata end");
+ if (serviceAfterChange.isRight()) {
+ log.error("Failed to get component " + component.getUniqueId() + " after change. status is "
+ + updateNode.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value()));
+ return result;
+ }
+ return Either.left((Component) serviceAfterChange.left().value());
+ }
+
+ /**
+ * update resouce metadata - lastUpdater and state
+ *
+ * @param resource
+ * @param modifier
+ * @param nextState
+ * @return
+ */
+ private Either<Resource, StorageOperationStatus> updateResourceMD(Resource resource, User modifier,
+ LifecycleStateEnum nextState) {
+
+ Either<Resource, StorageOperationStatus> result;
+ resource.setLastUpdateDate(null);
+ resource.setLastUpdaterUserId(modifier.getUserId());
+
+ result = resourceOperation.updateResource(resource, true);
+ if (result.isRight()) {
+ log.debug("failed to update resource for certification request.");
+ return result;
+ }
+ // get resource MD
+ Either<ResourceMetadataData, TitanOperationStatus> resourceDataResult = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resource.getUniqueId(),
+ ResourceMetadataData.class);
+ if (resourceDataResult.isRight()) {
+ log.debug("failed to get resource data from graph");
+ return Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(resourceDataResult.right().value()));
+ }
+
+ // set state on resource
+ ResourceMetadataData resourceData = resourceDataResult.left().value();
+ resourceData.getMetadataDataDefinition().setState(nextState.name());
+ resource.setLifecycleState(nextState);
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+
+ if (updateNode.isRight()) {
+ log.error("Failed to update resource " + resource.getUniqueId() + ". status is "
+ + updateNode.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value()));
+ return result;
+ }
+ return Either.left(resource);
+ }
+
+ private Either<List<Component>, StorageOperationStatus> getComponentTempVersions(NodeTypeEnum nodeType,
+ String uuid) {
+
+ Either<List<Component>, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ List<Component> componentList = new ArrayList<Component>();
+ ComponentOperation componentOperation = getComponentOperation(nodeType);
+
+ Map<String, Object> hasProps = new HashMap<String, Object>();
+ Map<String, Object> hasNotProps = new HashMap<String, Object>();
+
+ createOldVersionsCriteria(nodeType, uuid, hasProps, hasNotProps);
+
+ Either<List<ComponentMetadataData>, TitanOperationStatus> getByCriteria = titanGenericDao
+ .getByCriteria(nodeType, hasProps, hasNotProps, ComponentMetadataData.class);
+
+ if (getByCriteria.isRight()) {
+ log.error("failed to get old versions for component, type:{}, id: {}", nodeType, uuid);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getByCriteria.right().value()));
+ return result;
+ }
+
+ List<ComponentMetadataData> oldVersionComponents = getByCriteria.left().value();
+ for (ComponentMetadataData component : oldVersionComponents) {
+ Either<Component, StorageOperationStatus> resourceRes = componentOperation
+ .getComponent(component.getMetadataDataDefinition().getUniqueId(), true);
+ if (resourceRes.isRight()) {
+ result = Either.right(resourceRes.right().value());
+ return result;
+ } else {
+ componentList.add(resourceRes.left().value());
+ }
+ }
+ result = Either.left(componentList);
+ return result;
+ }
+
+ private void createOldVersionsCriteria(NodeTypeEnum nodeType, String uuid, Map<String, Object> hasProps,
+ Map<String, Object> hasNotProps) {
+
+ hasProps.put(GraphPropertiesDictionary.UUID.getProperty(), uuid);
+ hasProps.put(GraphPropertiesDictionary.LABEL.getProperty(), nodeType.name().toLowerCase());
+ hasNotProps.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+ }
+
+ private Either<? extends Component, StorageOperationStatus> updateOldComponentBeforeUndoCheckout(
+ ComponentOperation componentOperation, Component prevComponent, Component currentComponent,
+ String previousVersion, NodeTypeEnum nodeType, boolean inTransaction) {
+
+ log.debug("update previous version of component");
+ Map<String, Object> additionalQueryParams = new HashMap<String, Object>();
+
+ if (nodeType == NodeTypeEnum.Resource) {
+ ResourceTypeEnum resourceType = ((Resource) currentComponent).getResourceType();
+
+ additionalQueryParams.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), resourceType.name());
+ }
+ ComponentMetadataDataDefinition metadataDataDefinition = currentComponent.getComponentMetadataDefinition()
+ .getMetadataDataDefinition();
+ Either<? extends Component, StorageOperationStatus> getOlderCompResult = componentOperation
+ .getComponentByNameAndVersion(metadataDataDefinition.getName(), previousVersion, additionalQueryParams,
+ true);
+
+ // if previous version exist - set it as current version
+ if (getOlderCompResult.isRight()) {
+ if (StorageOperationStatus.NOT_FOUND.equals(getOlderCompResult.right().value())) {
+ log.debug("No components by name and version : {} {}", metadataDataDefinition.getName(), previousVersion);
+ log.debug("Name may have changed, since the version isn't certified try to fetch by UUID {}", metadataDataDefinition.getUUID());
+ additionalQueryParams.clear();
+ additionalQueryParams.put(GraphPropertiesDictionary.UUID.getProperty(),
+ metadataDataDefinition.getUUID());
+ additionalQueryParams.put(GraphPropertiesDictionary.VERSION.getProperty(), previousVersion);
+
+ Either<List<ComponentMetadataData>, TitanOperationStatus> byUUID = titanGenericDao
+ .getByCriteria(nodeType, additionalQueryParams, ComponentMetadataData.class);
+ if (byUUID.isRight()) {
+ log.debug("Failed to fetch by UUID {}", metadataDataDefinition.getUUID());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byUUID.right().value()));
+ }
+ String prevVersionId = (String) byUUID.left().value().get(0).getUniqueId();
+ Either<? extends Component, StorageOperationStatus> component = componentOperation
+ .getComponent(prevVersionId, inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to fetch previous component by ID {}", prevVersionId);
+ return Either.right(component.right().value());
+ }
+ prevComponent = component.left().value();
+ } else {
+ log.error("failed to find previous version. status={} ", getOlderCompResult.right().value());
+ return getOlderCompResult;
+ }
+ } else {
+ prevComponent = getOlderCompResult.left().value();
+ }
+
+ // if last resource is certified - don't touch it.
+ if (prevComponent.getVersion().endsWith(".0")) {
+ return Either.left(prevComponent);
+ }
+
+ prevComponent.setHighestVersion(true);
+ Either<Component, StorageOperationStatus> updateCompResult = componentOperation.updateComponent(prevComponent,
+ inTransaction);
+ if (updateCompResult.isRight()) {
+ log.debug("failed to update prev version of component");
+ return updateCompResult;
+ }
+
+ User user = new User();
+ user.setUserId(prevComponent.getLastUpdaterUserId());
+ StorageOperationStatus changeStateRelation = changeStateRelation(nodeType, prevComponent.getUniqueId(), user,
+ GraphEdgeLabels.LAST_STATE, GraphEdgeLabels.STATE);
+ if (!changeStateRelation.equals(StorageOperationStatus.OK)) {
+ return Either.right(changeStateRelation);
+ }
+
+ return Either.left(prevComponent);
+ }
+
+ private StorageOperationStatus changeStateRelation(NodeTypeEnum nodeType, String componentId, User currentOwner,
+ GraphEdgeLabels from, GraphEdgeLabels to) {
+ UniqueIdData componentData = new UniqueIdData(nodeType, componentId);
+ UserData userData = new UserData();
+ userData.setUserId(currentOwner.getUserId());
+ Either<GraphRelation, TitanOperationStatus> replaceRelationLabelResult = titanGenericDao
+ .replaceRelationLabel(userData, componentData, from, to);
+ if (replaceRelationLabelResult.isRight()) {
+ TitanOperationStatus titanStatus = replaceRelationLabelResult.right().value();
+ log.error("failed to replace label from {} to {}. status = {}", from, to, titanStatus);
+ StorageOperationStatus storageStatus = StorageOperationStatus.INCONSISTENCY;
+ if (!titanStatus.equals(TitanOperationStatus.INVALID_ID)) {
+ storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ }
+ return storageStatus;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus removeUserToResourceRelation(NodeTypeEnum componentType, String idFrom, String idTo,
+ GraphEdgeLabels label) {
+
+ UniqueIdData componentV = new UniqueIdData(componentType, idTo);
+ UserData userV = new UserData();
+ userV.setUserId(idFrom);
+ // delete relation
+ Either<GraphRelation, TitanOperationStatus> deleteRelationResult = titanGenericDao.deleteRelation(userV,
+ componentV, label);
+ if (deleteRelationResult.isRight()) {
+ log.error("failed to delete relation. status={}", deleteRelationResult.right().value());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(deleteRelationResult.right().value());
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus createUserToResourceRelation(NodeTypeEnum componentType, String idFrom, String idTo,
+ GraphEdgeLabels label, Map<String, Object> props) {
+
+ UniqueIdData componentV = new UniqueIdData(componentType, idTo);
+ UserData userV = new UserData();
+ userV.setUserId(idFrom);
+ // create relation
+ Either<GraphRelation, TitanOperationStatus> createRelationResult = titanGenericDao.createRelation(userV,
+ componentV, label, props);
+ if (createRelationResult.isRight()) {
+ log.error("failed to create relation. status={}", createRelationResult.right().value());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(createRelationResult.right().value());
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<? extends Component, StorageOperationStatus> cancelOrFailCertification(NodeTypeEnum nodeType,
+ Component component, User modifier, User owner, LifecycleStateEnum nextState, boolean inTransaction) {
+
+ Either<? extends Component, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ try {
+
+ ComponentParametersView componentParametersView = buildFilterForFetchComponentAfterChangeState();
+ result = updateComponentMD(component, modifier, nextState, nodeType, componentParametersView);
+ if (result.isRight()) {
+ log.debug("Couldn't set lifecycle for component {} to state {}, error: {}", component.getUniqueId(),
+ nextState, result.right().value());
+ return result;
+ }
+ StorageOperationStatus status = StorageOperationStatus.OK;
+ // cancel certification process
+ if (nextState.equals(LifecycleStateEnum.READY_FOR_CERTIFICATION)) {
+ status = setRelationForCancelCertification(nextState, nodeType, component.getUniqueId());
+
+ } // fail certification
+ else if (nextState.equals(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN)) {
+ status = setRelationForFailCertification(nextState, nodeType, component.getUniqueId());
+ }
+
+ if (!status.equals(StorageOperationStatus.OK)) {
+ result = Either.right(status);
+ }
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/Neo4jStatusConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/Neo4jStatusConverter.java
new file mode 100644
index 0000000000..55531fec79
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/Neo4jStatusConverter.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import org.openecomp.sdc.be.dao.neo4j.Neo4jOperationStatus;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+
+public class Neo4jStatusConverter {
+
+ public static StorageOperationStatus convertNeo4jStatusToStorageStatus(Neo4jOperationStatus neo4jStatus) {
+
+ if (neo4jStatus == null) {
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ switch (neo4jStatus) {
+
+ case OK:
+ return StorageOperationStatus.OK;
+
+ case NOT_CONNECTED:
+ return StorageOperationStatus.CONNECTION_FAILURE;
+
+ case NOT_AUTHORIZED:
+ return StorageOperationStatus.PERMISSION_ERROR;
+
+ case HTTP_PROTOCOL_ERROR:
+ return StorageOperationStatus.HTTP_PROTOCOL_ERROR;
+ case DB_NOT_AVAILABLE:
+ return StorageOperationStatus.STORAGE_NOT_AVAILABLE;
+ case DB_READ_ONLY:
+ return StorageOperationStatus.READ_ONLY_STORAGE;
+ case BAD_REQUEST:
+ return StorageOperationStatus.BAD_REQUEST;
+ case LEGACY_INDEX_ERROR:
+ return StorageOperationStatus.STORAGE_LEGACY_INDEX_ERROR;
+ case SCHEMA_ERROR:
+ return StorageOperationStatus.SCHEMA_ERROR;
+ case TRANSACTION_ERROR:
+ return StorageOperationStatus.TRANSACTION_ERROR;
+ case EXECUTION_FAILED:
+ return StorageOperationStatus.EXEUCTION_FAILED;
+ case ENTITY_ALREADY_EXIST:
+ return StorageOperationStatus.ENTITY_ALREADY_EXISTS;
+ case WRONG_INPUT:
+ return StorageOperationStatus.BAD_REQUEST;
+ case GENERAL_ERROR:
+ return StorageOperationStatus.GENERAL_ERROR;
+ case NOT_SUPPORTED:
+ return StorageOperationStatus.OPERATION_NOT_SUPPORTED;
+ case NOT_FOUND:
+ return StorageOperationStatus.NOT_FOUND;
+
+ default:
+ return StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/OnboardingClient.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/OnboardingClient.java
new file mode 100644
index 0000000000..a7f8275064
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/OnboardingClient.java
@@ -0,0 +1,190 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.io.filefilter.WildcardFileFilter;
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.config.Configuration.OnboardingConfig;
+import org.openecomp.sdc.be.dao.rest.HttpRestClient;
+import org.openecomp.sdc.be.dao.rest.RestConfigurationInfo;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.rest.api.RestResponseAsByteArray;
+import org.openecomp.sdc.common.util.ZipUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("onboarding-client")
+public class OnboardingClient {
+
+ private static Logger log = LoggerFactory.getLogger(OnboardingClient.class.getName());
+
+ private HttpRestClient httpRestClient = null;
+
+ private static Properties downloadCsarHeaders = new Properties();
+
+ static {
+ downloadCsarHeaders.put("Accept", "application/octet-stream");
+ }
+
+ public OnboardingClient() {
+ super();
+ }
+
+ public static void main(String[] args) {
+
+ OnboardingClient csarOperation = new OnboardingClient();
+ csarOperation.init();
+
+ String csarUuid = "70025CF6081B489CA7B1CBA583D5278D";
+ Either<Map<String, byte[]>, StorageOperationStatus> csar = csarOperation.getCsar(csarUuid, null);
+ System.out.println(csar.left().value());
+
+ }
+
+ @PostConstruct
+ public void init() {
+
+ // TODO: read connection configuration from OnboardingConfig
+ // onboardingConfig =
+ // ConfigurationManager.getConfigurationManager().getConfiguration().getOnboarding();
+
+ RestConfigurationInfo restConfigurationInfo = new RestConfigurationInfo();
+ httpRestClient = new HttpRestClient(restConfigurationInfo);
+
+ if (false == httpRestClient.isInitialized()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("InitializeRestClient", "Failed to initialize rest client", ErrorSeverity.FATAL);
+ httpRestClient = null;
+ }
+
+ }
+
+ // Mock returning a file from the file system until we have API from onboarding
+ public Either<Map<String, byte[]>, StorageOperationStatus> getMockCsar(String csarUuid) {
+ File dir = new File("/var/tmp/mockCsar");
+ FileFilter fileFilter = new WildcardFileFilter("*.csar");
+ File[] files = dir.listFiles(fileFilter);
+ for (int i = 0; i < files.length; i++) {
+ File csar = files[i];
+ if (csar.getName().startsWith(csarUuid)) {
+ log.debug("Found CSAR file {} matching the passed csarUuid {}", csar.getAbsolutePath(), csarUuid);
+ byte[] data;
+ try {
+ data = Files.readAllBytes(csar.toPath());
+ } catch (IOException e) {
+ log.debug("Error reading mock file for CSAR, error: {}", e);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Map<String, byte[]> readZip = ZipUtil.readZip(data);
+ return Either.left(readZip);
+ }
+ }
+ log.debug("Couldn't find mock file for CSAR starting with {}", csarUuid);
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ public Either<Map<String, byte[]>, StorageOperationStatus> getCsar(String csarUuid, String userId) {
+
+ if (httpRestClient == null) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("RestClient", "Rest Client could not be initialized", ErrorSeverity.ERROR);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ String url = buildDownloadCsarUrl() + "/" + csarUuid;
+
+ Properties headers = new Properties();
+ if (downloadCsarHeaders != null) {
+ downloadCsarHeaders.forEach((k, v) -> headers.put(k, v));
+ }
+
+ if (userId != null) {
+ headers.put(Constants.USER_ID_HEADER, userId);
+ }
+
+ log.debug("Url for downloading csar is {}. Headers are {}", url, headers);
+
+ RestResponseAsByteArray restResponse = httpRestClient.doGetAsByteArray(url, headers);
+ log.debug("After fetching csar {}. Http return code is {}", csarUuid, restResponse.getHttpStatusCode());
+
+ switch (restResponse.getHttpStatusCode()) {
+ case HttpStatus.SC_OK:
+ byte[] data = restResponse.getResponse();
+ if (data != null && data.length > 0) {
+ Map<String, byte[]> readZip = ZipUtil.readZip(data);
+ return Either.left(readZip);
+ } else {
+ log.debug("Data received from rest is null or empty");
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ case HttpStatus.SC_NOT_FOUND:
+ return Either.right(StorageOperationStatus.CSAR_NOT_FOUND);
+
+ default:
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ }
+
+ public HttpRestClient getHttpRestClient() {
+ return httpRestClient;
+ }
+
+ public void setHttpRestClient(HttpRestClient httpRestClient) {
+ this.httpRestClient = httpRestClient;
+ }
+
+ /**
+ * Build the url for download CSAR
+ *
+ * E.g., http://1.2.3.4:8181/onboarding-api/v1.0/vendor-software-products/packages/
+ *
+ * @return
+ */
+ public String buildDownloadCsarUrl() {
+
+ OnboardingConfig onboardingConfig = ConfigurationManager.getConfigurationManager().getConfiguration().getOnboarding();
+
+ String protocol = onboardingConfig.getProtocol();
+ String host = onboardingConfig.getHost();
+ Integer port = onboardingConfig.getPort();
+ String uri = onboardingConfig.getDownloadCsarUri();
+
+ String getCsarUrl = protocol + "://" + host + ":" + port + uri;
+
+ return getCsarUrl;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperation.java
new file mode 100644
index 0000000000..d085c242e8
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperation.java
@@ -0,0 +1,230 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Resource;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PolicyTypeDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.IPolicyTypeOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.PolicyTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("policy-type-operation")
+public class PolicyTypeOperation extends AbstractOperation implements IPolicyTypeOperation {
+
+ private static final String CREATE_FLOW_CONTEXT = "CreatePolicyType";
+ private static final String GET_FLOW_CONTEXT = "GetPolicyType";
+
+ @Resource
+ private PropertyOperation propertyOperation;
+
+ public PolicyTypeOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(PolicyTypeOperation.class.getName());
+
+ @Override
+ public Either<PolicyTypeDefinition, StorageOperationStatus> getLatestPolicyTypeByType(String policyTypeName) {
+ return getLatestPolicyTypeByType(policyTypeName, false);
+ }
+
+ private Either<PolicyTypeDefinition, StorageOperationStatus> getLatestPolicyTypeByType(String type,
+ boolean inTransaction) {
+ Map<String, Object> mapCriteria = new HashMap<>();
+ mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
+ mapCriteria.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ return getPolicyTypeByCriteria(type, mapCriteria, inTransaction);
+ }
+
+ @Override
+ public Either<PolicyTypeDefinition, StorageOperationStatus> addPolicyType(PolicyTypeDefinition policyType) {
+ return addPolicyType(policyType, false);
+ }
+
+ @Override
+ public Either<PolicyTypeDefinition, StorageOperationStatus> addPolicyType(PolicyTypeDefinition policyTypeDef,
+ boolean inTransaction) {
+
+ Either<PolicyTypeDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<PolicyTypeData, TitanOperationStatus> eitherStatus = addPolicyTypeToGraph(policyTypeDef);
+
+ if (eitherStatus.isRight()) {
+ BeEcompErrorManager.getInstance().logBeFailedCreateNodeError(CREATE_FLOW_CONTEXT,
+ policyTypeDef.getType(), eitherStatus.right().value().name());
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+
+ } else {
+ PolicyTypeData policyTypeData = eitherStatus.left().value();
+
+ String uniqueId = policyTypeData.getUniqueId();
+ Either<PolicyTypeDefinition, StorageOperationStatus> policyTypeRes = this.getPolicyType(uniqueId, true);
+
+ if (policyTypeRes.isRight()) {
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError(GET_FLOW_CONTEXT,
+ policyTypeDef.getType(), eitherStatus.right().value().name());
+ }
+
+ result = policyTypeRes;
+
+ }
+
+ return result;
+
+ } finally {
+ handleTransactionCommitRollback(inTransaction, result);
+ }
+
+ }
+
+ private Either<PolicyTypeData, TitanOperationStatus> addPolicyTypeToGraph(PolicyTypeDefinition policyTypeDef) {
+ log.debug("Got policy type {}", policyTypeDef);
+
+ String ptUniqueId = UniqueIdBuilder.buildPolicyTypeUid(policyTypeDef.getType(), policyTypeDef.getVersion());
+
+ PolicyTypeData policyTypeData = buildPolicyTypeData(policyTypeDef, ptUniqueId);
+
+ log.debug("Before adding policy type to graph. policyTypeData = {}", policyTypeData);
+
+ Either<PolicyTypeData, TitanOperationStatus> eitherPolicyTypeData = titanGenericDao.createNode(policyTypeData,
+ PolicyTypeData.class);
+ log.debug("After adding policy type to graph. status is = {}", eitherPolicyTypeData);
+
+ if (eitherPolicyTypeData.isRight()) {
+ TitanOperationStatus operationStatus = eitherPolicyTypeData.right().value();
+ log.error("Failed to add policy type {} to graph. Status is {}", policyTypeDef.getType(), operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ PolicyTypeData resultCTD = eitherPolicyTypeData.left().value();
+ List<PropertyDefinition> properties = policyTypeDef.getProperties();
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToPolicyType = propertyOperation
+ .addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.PolicyType, properties);
+ if (addPropertiesToPolicyType.isRight()) {
+ log.error("Failed add properties {} to policy {}", properties, policyTypeDef.getType());
+ return Either.right(addPropertiesToPolicyType.right().value());
+ }
+
+ return Either.left(eitherPolicyTypeData.left().value());
+ }
+
+ public Either<PolicyTypeDefinition, StorageOperationStatus> getPolicyTypeByCriteria(String type,
+ Map<String, Object> properties, boolean inTransaction) {
+ Either<PolicyTypeDefinition, StorageOperationStatus> result = null;
+ try {
+ if (type == null || type.isEmpty()) {
+ log.error("type is empty");
+ result = Either.right(StorageOperationStatus.INVALID_ID);
+ return result;
+ }
+
+ Either<List<PolicyTypeData>, TitanOperationStatus> eitherPolicyData = titanGenericDao
+ .getByCriteria(NodeTypeEnum.PolicyType, properties, PolicyTypeData.class);
+ if (eitherPolicyData.isRight()) {
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherPolicyData.right().value()));
+ } else {
+ PolicyTypeDataDefinition dataDefinition = eitherPolicyData.left().value().stream()
+ .map(e -> e.getPolicyTypeDataDefinition()).findFirst().get();
+ result = getPolicyType(dataDefinition.getUniqueId(), inTransaction);
+ }
+
+ return result;
+
+ } finally {
+ handleTransactionCommitRollback(inTransaction, result);
+ }
+ }
+
+ @Override
+ public Either<PolicyTypeDefinition, StorageOperationStatus> getPolicyType(String uniqueId, boolean inTransaction) {
+ Function<String, Either<PolicyTypeDefinition, TitanOperationStatus>> policyTypeGetter = uId -> getPolicyTypeByUid(
+ uId);
+ return getElementType(policyTypeGetter, uniqueId, inTransaction);
+
+ }
+
+ private Either<PolicyTypeDefinition, TitanOperationStatus> getPolicyTypeByUid(String uniqueId) {
+ Either<PolicyTypeDefinition, TitanOperationStatus> result = null;
+
+ Either<PolicyTypeData, TitanOperationStatus> eitherPolicyTypeData = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PolicyType), uniqueId, PolicyTypeData.class);
+
+ if (eitherPolicyTypeData.isRight()) {
+ TitanOperationStatus status = eitherPolicyTypeData.right().value();
+ log.debug("Policy type {} cannot be found in graph. Status is {}", uniqueId, status);
+ return Either.right(status);
+ }
+
+ PolicyTypeData policyTypeData = eitherPolicyTypeData.left().value();
+ PolicyTypeDefinition policyTypeDefinition = new PolicyTypeDefinition(
+ policyTypeData.getPolicyTypeDataDefinition());
+
+ TitanOperationStatus propertiesStatus = propertyOperation.fillProperties(uniqueId,
+ propList -> policyTypeDefinition.setProperties(propList));
+ if (propertiesStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of policy type {}", uniqueId);
+ return Either.right(propertiesStatus);
+ }
+
+ result = Either.left(policyTypeDefinition);
+
+ return result;
+ }
+
+ private PolicyTypeData buildPolicyTypeData(PolicyTypeDefinition policyTypeDefinition, String ptUniqueId) {
+
+ PolicyTypeData policyTypeData = new PolicyTypeData(policyTypeDefinition);
+
+ policyTypeData.getPolicyTypeDataDefinition().setUniqueId(ptUniqueId);
+ Long creationDate = policyTypeData.getPolicyTypeDataDefinition().getCreationTime();
+ if (creationDate == null) {
+ creationDate = System.currentTimeMillis();
+ }
+
+ policyTypeData.getPolicyTypeDataDefinition().setCreationTime(creationDate);
+ policyTypeData.getPolicyTypeDataDefinition().setModificationTime(creationDate);
+ return policyTypeData;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ProductOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ProductOperation.java
new file mode 100644
index 0000000000..2a8192421b
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ProductOperation.java
@@ -0,0 +1,1067 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.Set;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.category.CategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.category.GroupingDataDefinition;
+import org.openecomp.sdc.be.datatypes.category.SubCategoryDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ProductMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.*;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.GroupingDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IProductOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ProductMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.GroupingData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("product-operation")
+public class ProductOperation extends ComponentOperation implements IProductOperation {
+
+ private static Logger log = LoggerFactory.getLogger(ProductOperation.class.getName());
+
+ public ProductOperation() {
+ log.debug("ProductOperation created");
+ }
+
+ @Override
+ protected ComponentMetadataData getMetaDataFromComponent(Component component) {
+ return getProductMetadataDataFromProduct((Product) component);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getProduct(id, inTransaction);
+ }
+
+ // public <T> Either<T, StorageOperationStatus> getComponent_tx(String id,
+ // boolean inTransaction) {
+ // return (Either<T, StorageOperationStatus>) getProduct_tx(id,
+ // inTransaction);
+ // }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected <T> Either<T, StorageOperationStatus> getComponentByNameAndVersion(String name, String version,
+ Map<String, Object> additionalParams, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getByNamesAndVersion(GraphPropertiesDictionary.NAME.getProperty(),
+ name, version, additionalParams, inTransaction);
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> getLightComponent(String id, boolean inTransaction) {
+ return getLightComponent(id, NodeTypeEnum.Product, inTransaction);
+ }
+
+ @Override
+ public <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters,
+ boolean inTransaction) {
+ return getFilteredComponents(filters, inTransaction, NodeTypeEnum.Product);
+ }
+
+ private Product convertProductDataToProduct(ProductMetadataData productData) {
+ ProductMetadataDefinition productMetadataDefinition = new ProductMetadataDefinition(
+ (ProductMetadataDataDefinition) productData.getMetadataDataDefinition());
+
+ Product product = new Product(productMetadataDefinition);
+
+ return product;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> updateComponent(T component, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) updateComponent((Component) component, inTransaction,
+ titanGenericDao, Product.class, NodeTypeEnum.Product);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Either<Component, StorageOperationStatus> deleteComponent(String id, boolean inTransaction) {
+ return (Either<Component, StorageOperationStatus>) (Either<?, StorageOperationStatus>) deleteProduct(id,
+ inTransaction);
+ }
+
+ @Override
+ public Either<List<ArtifactDefinition>, StorageOperationStatus> getAdditionalArtifacts(String resourceId,
+ boolean recursively, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T extends org.openecomp.sdc.be.model.Component> Either<T, StorageOperationStatus> getComponent(String id,
+ Class<T> clazz) {
+ return (Either<T, StorageOperationStatus>) getProduct(id, false);
+ }
+
+ @Override
+ /**
+ * Deletes the product node
+ */
+ public Either<Product, StorageOperationStatus> deleteProduct(String productId, boolean inTransaction) {
+
+ Either<Product, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+
+ Either<ProductMetadataData, TitanOperationStatus> productNode = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Product), productId, ProductMetadataData.class);
+ if (productNode.isRight()) {
+ TitanOperationStatus status = productNode.right().value();
+ log.error("Failed to find product {}. Status is {}", productId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ Either<Product, StorageOperationStatus> productRes = getProduct(productId, true);
+ if (productRes.isRight()) {
+ StorageOperationStatus status = productRes.right().value();
+ log.error("Failed to find product {}", productId, status);
+ result = Either.right(status);
+ return result;
+ }
+ Product product = productRes.left().value();
+
+ Either<List<ComponentInstance>, StorageOperationStatus> deleteAllInstancesRes = componentInstanceOperation
+ .deleteAllComponentInstances(productId, NodeTypeEnum.Product, true);
+ log.debug("After deleting instances under product {}. Result is {}", productId, deleteAllInstancesRes);
+ if (deleteAllInstancesRes.isRight()) {
+ StorageOperationStatus status = deleteAllInstancesRes.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ log.error("Failed to delete instances under product {}. Status is {}", productId, status);
+ result = Either.right(status);
+ return result;
+ }
+ }
+
+ Either<ProductMetadataData, TitanOperationStatus> deleteProductNodeRes = titanGenericDao
+ .deleteNode(productNode.left().value(), ProductMetadataData.class);
+ if (deleteProductNodeRes.isRight()) {
+ TitanOperationStatus status = deleteProductNodeRes.right().value();
+ log.error("Failed to delete product node {}. Status is {}", productId, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ result = Either.left(product);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("deleteProduct operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("deleteProduct operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<Product>, StorageOperationStatus> getProductCatalogData(boolean inTransaction) {
+
+ long start = System.currentTimeMillis();
+ try {
+ /*
+ * Map<String, Object> propertiesToMatch = new HashMap<>();
+ *
+ * propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty
+ * (), LifecycleStateEnum.CERTIFIED.name());
+ * Either<List<ProductMetadataData>, TitanOperationStatus>
+ * lastVersionNodes = getLastVersion(NodeTypeEnum.Product,
+ * propertiesToMatch, ProductMetadataData.class); if
+ * (lastVersionNodes.isRight() && lastVersionNodes.right().value()
+ * != TitanOperationStatus.NOT_FOUND) { return
+ * Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus
+ * (lastVersionNodes.right().value())); } List<ProductMetadataData>
+ * notCertifiedHighest = (lastVersionNodes.isLeft() ?
+ * lastVersionNodes.left().value() : new
+ * ArrayList<ProductMetadataData>());
+ *
+ * propertiesToMatch.put(GraphPropertiesDictionary.
+ * IS_HIGHEST_VERSION.getProperty(), true);
+ * Either<List<ProductMetadataData>, TitanOperationStatus>
+ * componentsNodes =
+ * titanGenericDao.getByCriteria(NodeTypeEnum.Product,
+ * propertiesToMatch, ProductMetadataData.class); if
+ * (componentsNodes.isRight() && componentsNodes.right().value() !=
+ * TitanOperationStatus.NOT_FOUND) { return
+ * Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus
+ * (componentsNodes.right().value())); } List<ProductMetadataData>
+ * certifiedHighest = (componentsNodes.isLeft() ?
+ * componentsNodes.left().value() : new
+ * ArrayList<ProductMetadataData>()); Set<String> names = new
+ * HashSet<String>(); for (ProductMetadataData data :
+ * notCertifiedHighest) { String name =
+ * data.getMetadataDataDefinition().getName(); names.add(name); }
+ *
+ * for (ProductMetadataData data : certifiedHighest) { String
+ * productName = data.getMetadataDataDefinition().getName(); if
+ * (!names.contains(productName)) { notCertifiedHighest.add(data); }
+ * }
+ */
+ Either<List<ProductMetadataData>, TitanOperationStatus> listOfHighestComponents = this
+ .getListOfHighestComponents(NodeTypeEnum.Product, ProductMetadataData.class);
+ if (listOfHighestComponents.isRight()
+ && listOfHighestComponents.right().value() != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(
+ DaoStatusConverter.convertTitanStatusToStorageStatus(listOfHighestComponents.right().value()));
+ }
+
+ List<ProductMetadataData> notCertifiedHighest = listOfHighestComponents.left().value();
+
+ List<Product> result = new ArrayList<>();
+
+ if (notCertifiedHighest != null && false == notCertifiedHighest.isEmpty()) {
+
+ // fetch from cache
+ long startFetchAllFromCache = System.currentTimeMillis();
+
+ Map<String, Long> components = notCertifiedHighest.stream()
+ .collect(Collectors.toMap(p -> p.getMetadataDataDefinition().getUniqueId(),
+ p -> p.getMetadataDataDefinition().getLastUpdateDate()));
+
+ Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> componentsFromCacheForCatalog = this
+ .getComponentsFromCacheForCatalog(components, ComponentTypeEnum.PRODUCT);
+ if (componentsFromCacheForCatalog.isLeft()) {
+ ImmutablePair<List<Component>, Set<String>> immutablePair = componentsFromCacheForCatalog.left()
+ .value();
+ List<Component> list = immutablePair.getLeft();
+ if (list != null) {
+ for (Component component : list) {
+ result.add((Product) component);
+ }
+ List<String> addedUids = list.stream()
+ .map(p -> p.getComponentMetadataDefinition().getMetadataDataDefinition().getUniqueId())
+ .collect(Collectors.toList());
+ notCertifiedHighest = notCertifiedHighest.stream()
+ .filter(p -> false == addedUids.contains(p.getMetadataDataDefinition().getUniqueId()))
+ .collect(Collectors.toList());
+ }
+ }
+ long endFetchAllFromCache = System.currentTimeMillis();
+ log.debug("Fetch all catalog products metadata from cache took {} ms",
+ (endFetchAllFromCache - startFetchAllFromCache));
+ log.debug("The number of products added to catalog from cache is {}", result.size());
+
+ log.debug("The number of products needed to be fetch as light component is {}",
+ notCertifiedHighest.size());
+
+ for (ProductMetadataData data : notCertifiedHighest) {
+ Either<Product, StorageOperationStatus> component = getLightComponent(
+ data.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get product for id = {}, error : {}. skip product", data.getUniqueId(),
+ component.right().value());
+ } else {
+ // get all versions
+ Product product = component.left().value();
+ // setAllVersions(product);
+
+ result.add(product);
+ }
+ }
+ }
+ return Either.left(result);
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ log.debug("Fetch all catalog products took {} ms", (System.currentTimeMillis() - start));
+ }
+ }
+
+ @Override
+ public Either<Product, StorageOperationStatus> createProduct(Product product) {
+ return createProduct(product, false);
+ }
+
+ @Override
+ public Either<Product, StorageOperationStatus> createProduct(Product product, boolean inTransaction) {
+ Either<Product, StorageOperationStatus> result = null;
+
+ try {
+
+ ProductMetadataData productData = getProductMetadataDataFromProduct(product);
+ addComponentInternalFields(productData);
+ String uniqueId = (String) productData.getUniqueId();
+ generateUUID(product);
+
+ String userId = product.getCreatorUserId();
+
+ Either<UserData, TitanOperationStatus> findUser = findUser(userId);
+
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user {} in the graph. status is {}", userId, status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ }
+
+ UserData creatorUserData = findUser.left().value();
+ UserData updaterUserData = creatorUserData;
+ String updaterUserId = product.getLastUpdaterUserId();
+ if (updaterUserId != null && !updaterUserId.equals(userId)) {
+ findUser = findUser(updaterUserId);
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user {} in the graph. status is {}", userId, status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ } else {
+ updaterUserData = findUser.left().value();
+ }
+ }
+
+ log.trace("Creating tags for product {}", uniqueId);
+ StorageOperationStatus storageOperationStatus = createTagsForComponent(product);
+ if (storageOperationStatus != StorageOperationStatus.OK) {
+ return Either.right(storageOperationStatus);
+ }
+
+ log.trace("Finding groupings for product {}", uniqueId);
+ Either<List<GroupingData>, StorageOperationStatus> findGroupingsForComponent = findGroupingsForComponent(
+ NodeTypeEnum.ProductGrouping, product);
+ if (findGroupingsForComponent.isRight()) {
+ return Either.right(findGroupingsForComponent.right().value());
+ }
+ List<GroupingData> groupingDataToAssociate = findGroupingsForComponent.left().value();
+
+ log.debug("try to create product node on graph for id {}", uniqueId);
+ Either<ProductMetadataData, TitanOperationStatus> createNode = titanGenericDao.createNode(productData,
+ ProductMetadataData.class);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ log.error("Error returned after creating product data node {}. Status returned is {}", productData,
+ status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ log.debug("product node created on graph for id {}", productData.getUniqueId());
+
+ TitanOperationStatus associateMetadata = associateMetadataToComponent(productData, creatorUserData,
+ updaterUserData, null, null);
+ if (associateMetadata != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateMetadata));
+ return result;
+ }
+
+ TitanOperationStatus associateCategories = associateCategoriesToProduct(productData,
+ groupingDataToAssociate);
+ if (associateCategories != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateCategories));
+ return result;
+ }
+
+ result = getProduct(uniqueId, true);
+ if (result.isRight()) {
+ log.error("Cannot get full product from the graph. status is {}", result.right().value());
+ return Either.right(result.right().value());
+ }
+
+ if (log.isDebugEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ log.debug("Product retrieved is {}", json);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private TitanOperationStatus associateCategoriesToProduct(ProductMetadataData productData,
+ List<GroupingData> groupingDataToAssociate) {
+ for (GroupingData groupingData : groupingDataToAssociate) {
+ GraphEdgeLabels groupingLabel = GraphEdgeLabels.CATEGORIZED_TO;
+ Either<GraphRelation, TitanOperationStatus> result = titanGenericDao.createRelation(productData,
+ groupingData, groupingLabel, null);
+ log.debug("After associating grouping {} to product {}. Edge type is {}", groupingData, productData,
+ groupingLabel);
+ if (result.isRight()) {
+ return result.right().value();
+ }
+ }
+ log.trace("All groupings associated succesfully to product {}", productData);
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus dissociateCategoriesFromProduct(ProductMetadataData productData,
+ List<GroupingData> groupingDataToDissociate) {
+ for (GroupingData groupingData : groupingDataToDissociate) {
+ GraphEdgeLabels groupingLabel = GraphEdgeLabels.CATEGORIZED_TO;
+ Either<GraphRelation, TitanOperationStatus> result = titanGenericDao.deleteRelation(productData,
+ groupingData, groupingLabel);
+ log.debug("After dissociating grouping {} from product {}. Edge type is {}", groupingData, productData,
+ groupingLabel);
+ if (result.isRight()) {
+ return result.right().value();
+ }
+ }
+ log.trace("All groupings dissociated succesfully from product {}", productData);
+ return TitanOperationStatus.OK;
+ }
+
+ private Either<Product, StorageOperationStatus> getProduct(String uniqueId, boolean inTransaction) {
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ return getProduct(uniqueId, componentParametersView, inTransaction);
+ }
+
+ private Either<Product, StorageOperationStatus> getProduct(String uniqueId,
+ ComponentParametersView componentParametersView, boolean inTransaction) {
+ Product product = null;
+ Either<Product, StorageOperationStatus> result = null;
+ try {
+
+ NodeTypeEnum productNodeType = NodeTypeEnum.Product;
+ NodeTypeEnum compInstNodeType = NodeTypeEnum.Service;
+
+ Either<ProductMetadataData, StorageOperationStatus> getComponentByLabel = getComponentByLabelAndId(uniqueId,
+ productNodeType, ProductMetadataData.class);
+ if (getComponentByLabel.isRight()) {
+ result = Either.right(getComponentByLabel.right().value());
+ return result;
+ }
+ ProductMetadataData productData = getComponentByLabel.left().value();
+
+ // Try to fetch resource from the cache. The resource will be
+ // fetched only if the time on the cache equals to
+ // the time on the graph.
+ Either<Product, ActionStatus> componentFromCacheIfUpToDate = this.getComponentFromCacheIfUpToDate(uniqueId,
+ productData, componentParametersView, Product.class, ComponentTypeEnum.PRODUCT);
+ if (componentFromCacheIfUpToDate.isLeft()) {
+ Product cachedProduct = componentFromCacheIfUpToDate.left().value();
+ log.debug("Product {} with uid {} was fetched from cache.", cachedProduct.getName(),
+ cachedProduct.getUniqueId());
+ return Either.left(cachedProduct);
+ }
+
+ product = convertProductDataToProduct(productData);
+
+ TitanOperationStatus status = null;
+ if (false == componentParametersView.isIgnoreUsers()) {
+ status = setComponentCreatorFromGraph(product, uniqueId, productNodeType);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ status = setComponentLastModifierFromGraph(product, uniqueId, productNodeType);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+
+ }
+ }
+ if (false == componentParametersView.isIgnoreCategories()) {
+ status = setComponentCategoriesFromGraph(product);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreComponentInstances()
+ || false == componentParametersView.isIgnoreComponentInstancesProperties()
+ || false == componentParametersView.isIgnoreCapabilities()
+ || false == componentParametersView.isIgnoreRequirements()) {
+ status = setComponentInstancesFromGraph(uniqueId, product, productNodeType, compInstNodeType);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+
+ }
+ }
+ if (false == componentParametersView.isIgnoreComponentInstancesProperties()) {
+ status = setComponentInstancesPropertiesFromGraph(uniqueId, product);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+ if (false == componentParametersView.isIgnoreCapabilities()) {
+ status = setCapabilitiesFromGraph(uniqueId, product, NodeTypeEnum.Product);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+ if (false == componentParametersView.isIgnoreRequirements()) {
+ status = setRequirementsFromGraph(uniqueId, product, NodeTypeEnum.Product);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreAllVersions()) {
+ status = setAllVersions(product);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ result = Either.left(product);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ // private Either<Product, StorageOperationStatus> getProduct_tx(String
+ // uniqueId, boolean inTransaction) {
+ // Product product = null;
+ // Either<Product, StorageOperationStatus> result = null;
+ // try {
+ //
+ // NodeTypeEnum productNodeType = NodeTypeEnum.Product;
+ // NodeTypeEnum compInstNodeType = NodeTypeEnum.Service;
+ //
+ // Either<ProductMetadataData, StorageOperationStatus> getComponentByLabel =
+ // getComponentByLabelAndId_tx(uniqueId, productNodeType,
+ // ProductMetadataData.class);
+ // if (getComponentByLabel.isRight()) {
+ // result = Either.right(getComponentByLabel.right().value());
+ // return result;
+ // }
+ // ProductMetadataData productData = getComponentByLabel.left().value();
+ // product = convertProductDataToProduct(productData);
+ //
+ // TitanOperationStatus status = setComponentCreatorFromGraph(product,
+ // uniqueId, productNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setComponentLastModifierFromGraph(product, uniqueId,
+ // productNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ // status = setComponentCategoriesFromGraph(product);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setComponentInstancesFromGraph(uniqueId, product,
+ // productNodeType, compInstNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ //
+ // status = setComponentInstancesPropertiesFromGraph(uniqueId, product);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setCapabilitiesFromGraph(uniqueId, product,
+ // NodeTypeEnum.Product);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setRequirementsFromGraph( uniqueId, product,
+ // NodeTypeEnum.Product);;
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ //
+ // status = setAllVersions(product);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // result = Either.left(product);
+ // return result;
+ //
+ // } finally {
+ // if (false == inTransaction) {
+ // if (result == null || result.isRight()) {
+ // titanGenericDao.rollback();
+ // } else {
+ // titanGenericDao.commit();
+ // }
+ // }
+ // }
+ // }
+
+ private TitanOperationStatus setAllVersions(Product product) {
+ Either<Map<String, String>, TitanOperationStatus> res = getVersionList(NodeTypeEnum.Product,
+ product.getVersion(), product, ProductMetadataData.class);
+ if (res.isRight()) {
+ return res.right().value();
+ }
+ product.setAllVersions(res.left().value());
+ return TitanOperationStatus.OK;
+ }
+
+ private Either<Product, StorageOperationStatus> sendError(TitanOperationStatus status,
+ StorageOperationStatus statusIfNotFound) {
+ Either<Product, StorageOperationStatus> result;
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ result = Either.right(statusIfNotFound);
+ return result;
+ } else {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+
+ @Override
+ TitanOperationStatus setComponentCategoriesFromGraph(Component component) {
+ Product product = (Product) component;
+ // Building the cat->subcat->grouping triples
+ Either<List<ImmutablePair<GroupingData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Product), product.getUniqueId(),
+ GraphEdgeLabels.CATEGORIZED_TO, NodeTypeEnum.ProductGrouping, GroupingData.class);
+ if (childrenNodes.isRight()) {
+ if (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.debug("Error when finding groupings for this product, error {}", childrenNodes.right().value());
+ return childrenNodes.right().value();
+ } else {
+ log.debug("No groupings found for this product - this might be normal");
+ return TitanOperationStatus.OK;
+ }
+ }
+ Map<CategoryDefinition, Map<SubCategoryDefinition, List<GroupingDefinition>>> categoriesDataStructure = new HashMap<>();
+
+ List<ImmutablePair<GroupingData, GraphEdge>> valueList = childrenNodes.left().value();
+ for (ImmutablePair<GroupingData, GraphEdge> groupPair : valueList) {
+ GroupingData groupingData = groupPair.getLeft();
+ Either<ImmutablePair<SubCategoryData, GraphEdge>, TitanOperationStatus> parentSubCat = titanGenericDao
+ .getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ProductGrouping),
+ (String) groupingData.getUniqueId(), GraphEdgeLabels.GROUPING,
+ NodeTypeEnum.ProductSubcategory, SubCategoryData.class);
+ if (parentSubCat.isRight()) {
+ log.debug("Cannot find subcategory for grouping {}", groupingData.getUniqueId());
+ return parentSubCat.right().value();
+ }
+ SubCategoryData subCatData = parentSubCat.left().value().getLeft();
+ Either<ImmutablePair<CategoryData, GraphEdge>, TitanOperationStatus> parentCat = titanGenericDao
+ .getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ProductSubcategory),
+ (String) subCatData.getUniqueId(), GraphEdgeLabels.SUB_CATEGORY,
+ NodeTypeEnum.ProductCategory, CategoryData.class);
+ if (parentCat.isRight()) {
+ log.debug("Cannot find category for subcategory {}", subCatData.getUniqueId());
+ return parentCat.right().value();
+ }
+
+ // Building data structure of categories hierarchy
+ CategoryDataDefinition categoryDefinition = parentCat.left().value().getLeft().getCategoryDataDefinition();
+ SubCategoryDataDefinition subDefinition = subCatData.getSubCategoryDataDefinition();
+ GroupingDataDefinition groupingDefinition = groupingData.getGroupingDataDefinition();
+
+ CategoryDefinition categoryDef = new CategoryDefinition(categoryDefinition);
+ SubCategoryDefinition subDef = new SubCategoryDefinition(subDefinition);
+ GroupingDefinition groupingDef = new GroupingDefinition(groupingDefinition);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Found category {} -> subcategory {} -> grouping {} for product {}",
+ categoryDefinition.getUniqueId(), subCatData.getUniqueId(), groupingData.getUniqueId(),
+ product.getUniqueId());
+ }
+ Map<SubCategoryDefinition, List<GroupingDefinition>> subMap = categoriesDataStructure.get(categoryDef);
+ if (subMap == null) {
+ subMap = new HashMap<>();
+ categoriesDataStructure.put(categoryDef, subMap);
+ }
+ List<GroupingDefinition> groupList = subMap.get(subDef);
+ if (groupList == null) {
+ groupList = new ArrayList<>();
+ subMap.put(subDef, groupList);
+ }
+ groupList.add(groupingDef);
+ }
+ convertToCategoriesList(product, categoriesDataStructure);
+ return TitanOperationStatus.OK;
+ }
+
+ private void convertToCategoriesList(Product product,
+ Map<CategoryDefinition, Map<SubCategoryDefinition, List<GroupingDefinition>>> categoriesDataStructure) {
+ List<CategoryDefinition> categoryDataList = product.getCategories();
+ if (categoryDataList == null) {
+ categoryDataList = new ArrayList<CategoryDefinition>();
+ }
+ for (Entry<CategoryDefinition, Map<SubCategoryDefinition, List<GroupingDefinition>>> triple : categoriesDataStructure
+ .entrySet()) {
+ CategoryDefinition categoryDefinition = triple.getKey();
+ List<SubCategoryDefinition> subList = new ArrayList<>();
+ categoryDefinition.setSubcategories(subList);
+ Map<SubCategoryDefinition, List<GroupingDefinition>> value = triple.getValue();
+
+ for (Entry<SubCategoryDefinition, List<GroupingDefinition>> pair : value.entrySet()) {
+ SubCategoryDefinition subCategoryDefinition = pair.getKey();
+ List<GroupingDefinition> list = pair.getValue();
+ subList.add(subCategoryDefinition);
+ subCategoryDefinition.setGroupings(list);
+ }
+ categoryDataList.add(categoryDefinition);
+ }
+ product.setCategories(categoryDataList);
+ log.debug("Fetched categories for product {}, categories: {}", product.getUniqueId(),
+ Arrays.toString(categoryDataList.toArray()));
+ }
+
+ private ProductMetadataData getProductMetadataDataFromProduct(Product product) {
+ ProductMetadataData productMetadata = new ProductMetadataData(
+ (ProductMetadataDataDefinition) product.getComponentMetadataDefinition().getMetadataDataDefinition());
+ return productMetadata;
+ }
+
+ @Override
+ public boolean isComponentExist(String id) {
+ return isComponentExist(id, NodeTypeEnum.Product);
+ }
+
+ // @SuppressWarnings("unchecked")
+ // @Override
+ // public <T> Either<T, StorageOperationStatus> cloneComponent(T other,
+ // String version, boolean inTransaction) {
+ // return (Either<T, StorageOperationStatus>) cloneProduct((Product)other,
+ // version, inTransaction);
+ // }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version,
+ LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) cloneProduct((Product) other, version, targetLifecycle,
+ inTransaction);
+ }
+
+ private Either<Product, StorageOperationStatus> cloneProduct(Product other, String version,
+ LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ Either<Product, StorageOperationStatus> result = null;
+
+ try {
+ String origProductId = other.getUniqueId();
+ other.setVersion(version);
+ other.setUniqueId(null);
+
+ Either<Integer, StorageOperationStatus> counterStatus = getComponentInstanceCoutner(origProductId,
+ NodeTypeEnum.Product);
+ if (counterStatus.isRight()) {
+ StorageOperationStatus status = counterStatus.right().value();
+ log.error("failed to get resource instance counter on product {}. status={}", origProductId,
+ counterStatus);
+ result = Either.right(status);
+ return result;
+ }
+
+ Either<Product, StorageOperationStatus> createProductMD = createProduct(other, inTransaction);
+ if (createProductMD.isRight()) {
+ StorageOperationStatus status = createProductMD.right().value();
+ log.debug("Failed to clone product. status= {}", status);
+ result = Either.right(status);
+ return result;
+ }
+ Product product = createProductMD.left().value();
+
+ Either<ImmutablePair<List<ComponentInstance>, Map<String, String>>, StorageOperationStatus> cloneInstances = componentInstanceOperation.cloneAllComponentInstancesFromContainerComponent(origProductId, product,
+ NodeTypeEnum.Product, NodeTypeEnum.Service, targetLifecycle, null);
+ if (cloneInstances.isRight()) {
+ result = Either.right(cloneInstances.right().value());
+ return result;
+ }
+
+ Either<Integer, StorageOperationStatus> setResourceInstanceCounter = setComponentInstanceCounter(
+ product.getUniqueId(), NodeTypeEnum.Product, counterStatus.left().value(), inTransaction);
+ if (setResourceInstanceCounter.isRight()) {
+ StorageOperationStatus status = setResourceInstanceCounter.right().value();
+ log.error("failed to set resource instance counter on product {}. status={}", product.getUniqueId(),
+ setResourceInstanceCounter);
+ result = Either.right(status);
+ return result;
+ }
+
+ result = this.getProduct(product.getUniqueId(), inTransaction);
+ if (result.isRight()) {
+ log.error("Cannot get full product from the graph. status is {}", result.right().value());
+ return Either.right(result.right().value());
+ }
+
+ if (log.isDebugEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ log.debug("Product retrieved is {}", json);
+ }
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private Either<Product, StorageOperationStatus> getByNamesAndVersion(String nameKey, String nameValue,
+ String version, Map<String, Object> additionalParams, boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(nameKey, nameValue);
+ props.put(GraphPropertiesDictionary.VERSION.getProperty(), version);
+ props.put(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Product.getName());
+ if (additionalParams != null && !additionalParams.isEmpty()) {
+ props.putAll(additionalParams);
+ }
+
+ Either<List<ProductMetadataData>, TitanOperationStatus> byCriteria = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Product, props, ProductMetadataData.class);
+
+ if (byCriteria.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCriteria.right().value()));
+ }
+ List<ProductMetadataData> dataList = byCriteria.left().value();
+ if (dataList != null && !dataList.isEmpty()) {
+ if (dataList.size() > 1) {
+ log.debug("More that one instance of product for name {} and version {}", nameValue, version);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ ProductMetadataData productData = dataList.get(0);
+ Either<Product, StorageOperationStatus> product = getProduct(
+ productData.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (product.isRight()) {
+ log.debug("Failed to fetch product, name {} id {}", productData.getMetadataDataDefinition().getName(),
+ productData.getMetadataDataDefinition().getUniqueId());
+ }
+ return product;
+ }
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ @Override
+ public Product getDefaultComponent() {
+ return new Product();
+ }
+
+ @Override
+ protected <T extends org.openecomp.sdc.be.model.Component> StorageOperationStatus updateDerived(
+ org.openecomp.sdc.be.model.Component component, org.openecomp.sdc.be.model.Component currentComponent,
+ ComponentMetadataData componentData, Class<T> clazz) {
+ log.debug("Derived class isn't supported for product");
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<Integer, StorageOperationStatus> increaseAndGetComponentInstanceCounter(String componentId,
+ boolean inTransaction) {
+ return increaseAndGetComponentInstanceCounter(componentId, NodeTypeEnum.Product, inTransaction);
+ }
+
+ @Override
+ protected StorageOperationStatus validateCategories(Component currentComponent, Component component,
+ ComponentMetadataData componentData, NodeTypeEnum type) {
+ // As agreed with Ella, update categories - delete old and create new
+ StorageOperationStatus status = StorageOperationStatus.OK;
+ List<CategoryDefinition> newcategories = component.getCategories();
+ List<CategoryDefinition> currentcategories = currentComponent.getCategories();
+ if (newcategories != null) {
+ if (currentcategories != null && !currentcategories.isEmpty()) {
+ Either<List<GroupingData>, StorageOperationStatus> findGroupingsForComponent = findGroupingsForComponent(
+ NodeTypeEnum.ProductGrouping, currentComponent);
+ if (findGroupingsForComponent.isRight()) {
+ status = findGroupingsForComponent.right().value();
+ }
+ List<GroupingData> groupingDataToDissociate = findGroupingsForComponent.left().value();
+ TitanOperationStatus titanStatus = dissociateCategoriesFromProduct((ProductMetadataData) componentData,
+ groupingDataToDissociate);
+ if (titanStatus != TitanOperationStatus.OK) {
+ status = DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ }
+ }
+ if (!newcategories.isEmpty()) {
+ Either<List<GroupingData>, StorageOperationStatus> findGroupingsForComponent = findGroupingsForComponent(
+ NodeTypeEnum.ProductGrouping, component);
+ if (findGroupingsForComponent.isRight()) {
+ status = findGroupingsForComponent.right().value();
+ }
+ List<GroupingData> groupingDataToAssociate = findGroupingsForComponent.left().value();
+ TitanOperationStatus titanStatus = associateCategoriesToProduct((ProductMetadataData) componentData,
+ groupingDataToAssociate);
+ if (titanStatus != TitanOperationStatus.OK) {
+ status = DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ }
+ }
+ }
+ return status;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Either<List<Product>, StorageOperationStatus> getFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction) {
+ return (Either<List<Product>, StorageOperationStatus>) (Either<?, StorageOperationStatus>) getFollowedComponent(
+ userId, lifecycleStates, lastStateStates, inTransaction, titanGenericDao, NodeTypeEnum.Product);
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> getMetadataComponent(String id, boolean inTransaction) {
+ return getMetadataComponent(id, NodeTypeEnum.Product, inTransaction);
+ }
+
+ @Override
+ Component convertComponentMetadataDataToComponent(ComponentMetadataData componentMetadataData) {
+ return convertProductDataToProduct((ProductMetadataData) componentMetadataData);
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateComponentNameExists(String productName) {
+ return validateComponentNameUniqueness(productName, titanGenericDao, NodeTypeEnum.Product);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Either<Component, StorageOperationStatus> markComponentToDelete(Component componentToDelete,
+ boolean inTransaction) {
+ // markComponentToDelete is not defined yet for products
+ return (Either<Component, StorageOperationStatus>) (Either<?, StorageOperationStatus>) deleteProduct(
+ componentToDelete.getUniqueId(), inTransaction);
+ }
+
+ @Override
+ public void rollback() {
+ titanGenericDao.rollback();
+
+ }
+
+ @Override
+ public void commit() {
+ titanGenericDao.commit();
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> isComponentInUse(String componentId) {
+ return isComponentInUse(componentId, NodeTypeEnum.Product);
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllComponentsMarkedForDeletion() {
+ // markForDeletion for products is not implemented yet
+ return Either.left(new ArrayList<>());
+ }
+
+ public Either<Product, StorageOperationStatus> getProductByNameAndVersion(String productName, String productVersion,
+ boolean inTransaction) {
+ return getByNamesAndVersion(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(),
+ ValidationUtils.normaliseComponentName(productName), productVersion, null, inTransaction);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id,
+ ComponentParametersView componentParametersView, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getProduct(id, false);
+ }
+
+ public Either<Product, StorageOperationStatus> updateProduct(Product product, boolean inTransaction,
+ ComponentParametersView filterResultView) {
+ return (Either<Product, StorageOperationStatus>) updateComponentFilterResult(product, inTransaction,
+ titanGenericDao, product.getClass(), NodeTypeEnum.Service, filterResultView);
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> updateComponentFilterResult(T component, boolean inTransaction,
+ ComponentParametersView filterResultView) {
+ return (Either<T, StorageOperationStatus>) updateProduct((Product) component, inTransaction, filterResultView);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java
new file mode 100644
index 0000000000..7d775b3b3d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java
@@ -0,0 +1,2788 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.ObjectCodec;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.IComplexDefaultValue;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterOrEqualConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.MinLengthConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
+import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.DataTypeData;
+import org.openecomp.sdc.be.resources.data.InputValueData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.common.api.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.reflect.TypeToken;
+
+import fj.data.Either;
+
+@Component("property-operation")
+public class PropertyOperation extends AbstractOperation implements IPropertyOperation {
+
+ public static void main(String[] args) {
+
+ List<Pattern> buildFunctionPatterns = buildFunctionPatterns();
+
+ for (Pattern pattern : buildFunctionPatterns) {
+
+ String[] strs = { "str_replace", "{ str_replace:", " {str_replace:", " { str_replace:", "{str_replace:" };
+ for (String str : strs) {
+ Matcher m = pattern.matcher(str);
+ System.out.println(pattern.pattern() + " " + str + " " + m.find());
+ }
+ }
+
+ }
+
+ public static final String PROPERTY = "property";
+
+ public PropertyOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(PropertyOperation.class.getName());
+
+ private static List<Pattern> functionPatterns = null;
+
+ static {
+
+ functionPatterns = buildFunctionPatterns();
+ }
+
+ /**
+ * The value of functions is in a json format. Build pattern for each function name
+ *
+ * { str_replace: .... } {str_replace: .... } {str_replace: .... } { str_replace: .... }
+ *
+ * @return
+ */
+ private static List<Pattern> buildFunctionPatterns() {
+
+ List<Pattern> functionPatterns = new ArrayList<>();
+
+ String[] functions = { "get_input", "get_property" };
+
+ for (String function : functions) {
+ Pattern pattern = Pattern.compile("^[ ]*\\{[ ]*" + function + ":");
+ functionPatterns.add(pattern);
+ }
+
+ return functionPatterns;
+ }
+
+ @Override
+ public Either<PropertyDefinition, StorageOperationStatus> getPropertyOfResource(String propertyName, String resourceId) {
+
+ String propertyId = UniqueIdBuilder.buildPropertyUniqueId(resourceId, propertyName);
+
+ Either<PropertyData, TitanOperationStatus> getResult = this.titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+ if (getResult.isLeft()) {
+ PropertyData propertyData = getResult.left().value();
+ return Either.left(convertPropertyDataToPropertyDefinition(propertyData, propertyName, resourceId));
+ } else {
+ TitanOperationStatus titanStatus = getResult.right().value();
+ log.debug("Node with id {} was not found in the graph. Status: {}", propertyId, titanStatus);
+ StorageOperationStatus storageOperationStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ return Either.right(storageOperationStatus);
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.openecomp.sdc.be.model.operations.api.IPropertyOperation# addPropertyToResource(java.lang.String, org.openecomp.sdc.be.model.PropertyDefinition, org.openecomp.sdc.be.dao.neo4j.datatype.NodeTypeEnum, java.lang.String)
+ */
+ /*
+ * @Override public Either<PropertyDefinition, StorageOperationStatus> addPropertyToResource( String propertyName, PropertyDefinition propertyDefinition, NodeTypeEnum nodeType, String resourceId) {
+ *
+ * StorageOperationStatus isValidProperty = isTypeExistsAndValid(propertyDefinition); if (isValidProperty != StorageOperationStatus.OK) { return Either.right(isValidProperty); }
+ *
+ * Either<PropertyData, TitanOperationStatus> status = addPropertyToGraph(propertyName, propertyDefinition, resourceId);
+ *
+ * if (status.isRight()) { titanGenericDao.rollback(); log.error("Failed to add property " + propertyName + " to resource " + resourceId); return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status. right().value())); } else
+ * { titanGenericDao.commit(); PropertyData propertyData = status.left().value();
+ *
+ * PropertyDefinition propertyDefResult = convertPropertyDataToPropertyDefinition(propertyData, propertyName, resourceId); log.debug("The returned PropertyDefintion is {}", propertyDefinition); return Either.left(propertyDefResult); }
+ *
+ *
+ * }
+ */
+ private StorageOperationStatus isTypeExistsAndValid(PropertyDefinition propertyDefinition) {
+
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyDefinition.getType());
+
+ if (type == null) {
+ return StorageOperationStatus.INVALID_TYPE;
+ }
+
+ String propertyType = propertyDefinition.getType();
+ String innerType = null;
+ String value = propertyDefinition.getDefaultValue();
+
+ if (propertyType.equals(ToscaPropertyType.LIST) || propertyType.equals(ToscaPropertyType.MAP)) {
+ SchemaDefinition schema;
+ if ((schema = propertyDefinition.getSchema()) != null) {
+ PropertyDataDefinition property;
+ if ((property = schema.getProperty()) != null) {
+ innerType = property.getType();
+
+ }
+ }
+ }
+
+ PropertyTypeValidator validator = type.getValidator();
+
+ if (value == null || (EMPTY_VALUE != null && EMPTY_VALUE.equals(propertyDefinition.getDefaultValue()))) {
+ return StorageOperationStatus.OK;
+ } else {
+ boolean isValid = validator.isValid(value, innerType, null);
+ if (true == isValid) {
+ return StorageOperationStatus.OK;
+ } else {
+ return StorageOperationStatus.INVALID_VALUE;
+ }
+ }
+
+ }
+
+ public PropertyDefinition convertPropertyDataToPropertyDefinition(PropertyData propertyDataResult, String propertyName, String resourceId) {
+ log.debug("The object returned after create property is {}", propertyDataResult);
+
+ PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
+ propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
+ propertyDefResult.setName(propertyName);
+ propertyDefResult.setParentUniqueId(resourceId);
+
+ return propertyDefResult;
+ }
+
+ public static class PropertyConstraintSerialiser implements JsonSerializer<PropertyConstraint> {
+
+ @Override
+ public JsonElement serialize(PropertyConstraint src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonParser parser = new JsonParser();
+ JsonObject result = new JsonObject();
+ JsonArray jsonArray = new JsonArray();
+ if (src instanceof InRangeConstraint) {
+ InRangeConstraint rangeConstraint = (InRangeConstraint) src;
+ jsonArray.add(parser.parse(rangeConstraint.getRangeMinValue()));
+ jsonArray.add(parser.parse(rangeConstraint.getRangeMaxValue()));
+ result.add("inRange", jsonArray);
+ } else if (src instanceof GreaterThanConstraint) {
+ GreaterThanConstraint greaterThanConstraint = (GreaterThanConstraint) src;
+ jsonArray.add(parser.parse(greaterThanConstraint.getGreaterThan()));
+ result.add("greaterThan", jsonArray);
+ } else if (src instanceof LessOrEqualConstraint) {
+ LessOrEqualConstraint lessOrEqualConstraint = (LessOrEqualConstraint) src;
+ jsonArray.add(parser.parse(lessOrEqualConstraint.getLessOrEqual()));
+ result.add("lessOrEqual", jsonArray);
+ } else {
+ log.warn("PropertyConstraint {} is not supported. Ignored.", src.getClass().getName());
+ }
+
+ return result;
+ }
+
+ }
+
+ public static class PropertyConstraintDeserialiser implements JsonDeserializer<PropertyConstraint> {
+
+ @Override
+ public PropertyConstraint deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+
+ PropertyConstraint propertyConstraint = null;
+
+ Set<Entry<String, JsonElement>> set = json.getAsJsonObject().entrySet();
+
+ if (set.size() == 1) {
+ Entry<String, JsonElement> element = set.iterator().next();
+ String key = element.getKey();
+ JsonElement value = element.getValue();
+
+ ConstraintType constraintType = ConstraintType.getByType(key);
+ if (constraintType == null) {
+ log.warn("ConstraintType was not found for constraint name:{}", key);
+ } else {
+ switch (constraintType) {
+ case IN_RANGE:
+
+ if (value != null) {
+ if (value instanceof JsonArray) {
+ JsonArray rangeArray = (JsonArray) value;
+ if (rangeArray.size() != 2) {
+ log.error("The range constraint content is invalid. value = {}", value);
+ } else {
+ InRangeConstraint rangeConstraint = new InRangeConstraint();
+ String minValue = rangeArray.get(0).getAsString();
+ String maxValue = rangeArray.get(1).getAsString();
+ rangeConstraint.setRangeMinValue(minValue);
+ rangeConstraint.setRangeMaxValue(maxValue);
+ propertyConstraint = rangeConstraint;
+ }
+ }
+
+ } else {
+ log.warn("The value of GreaterThanConstraint is null");
+ }
+ break;
+ case GREATER_THAN:
+ if (value != null) {
+ String asString = value.getAsString();
+ log.debug("Before adding value to GreaterThanConstraint object. value = {}", asString);
+ propertyConstraint = new GreaterThanConstraint(asString);
+ break;
+ } else {
+ log.warn("The value of GreaterThanConstraint is null");
+ }
+ break;
+
+ case LESS_THAN:
+ if (value != null) {
+ String asString = value.getAsString();
+ log.debug("Before adding value to LessThanConstraint object. value = {}", asString);
+ propertyConstraint = new LessThanConstraint(asString);
+ break;
+ } else {
+ log.warn("The value of LessThanConstraint is null");
+ }
+ break;
+ case GREATER_OR_EQUAL:
+ if (value != null) {
+ String asString = value.getAsString();
+ log.debug("Before adding value to GreaterThanConstraint object. value = {}", asString);
+ propertyConstraint = new GreaterOrEqualConstraint(asString);
+ break;
+ } else {
+ log.warn("The value of GreaterOrEqualConstraint is null");
+ }
+ break;
+ case LESS_OR_EQUAL:
+
+ if (value != null) {
+ String asString = value.getAsString();
+ log.debug("Before adding value to LessOrEqualConstraint object. value = {}", asString);
+ propertyConstraint = new LessOrEqualConstraint(asString);
+ } else {
+ log.warn("The value of GreaterThanConstraint is null");
+ }
+ break;
+
+ case VALID_VALUES:
+
+ if (value != null) {
+ if (value instanceof JsonArray) {
+ JsonArray rangeArray = (JsonArray) value;
+ if (rangeArray.size() == 0) {
+ log.error("The valid values constraint content is invalid. value = {}", value);
+ } else {
+ ValidValuesConstraint vvConstraint = new ValidValuesConstraint();
+ List<String> validValues = new ArrayList<String>();
+ for (JsonElement jsonElement : rangeArray) {
+ String item = jsonElement.getAsString();
+ validValues.add(item);
+ }
+ vvConstraint.setValidValues(validValues);
+ propertyConstraint = vvConstraint;
+ }
+ }
+
+ } else {
+ log.warn("The value of ValidValuesConstraint is null");
+ }
+ break;
+
+ case MIN_LENGTH:
+ if (value != null) {
+ int asInt = value.getAsInt();
+ log.debug("Before adding value to Min Length object. value = {}", asInt);
+ propertyConstraint = new MinLengthConstraint(asInt);
+ break;
+ } else {
+ log.warn("The value of MinLengthConstraint is null");
+ }
+ break;
+ default:
+ log.warn("Key {} is not supported. Ignored.", key);
+ }
+ }
+ }
+
+ return propertyConstraint;
+ }
+
+ }
+
+ public TitanOperationStatus addPropertiesToGraph(Map<String, PropertyDefinition> properties, String resourceId, Map<String, DataTypeDefinition> dataTypes) {
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceId);
+
+ if (properties != null) {
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+
+ String propertyName = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+
+ StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(propertyDefinition, dataTypes);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ log.error("Property {} is invalid. Status is {}", propertyDefinition, validateAndUpdateProperty);
+ return TitanOperationStatus.ILLEGAL_ARGUMENT;
+ }
+
+ Either<PropertyData, TitanOperationStatus> addPropertyToGraph = addPropertyToGraph(propertyName, propertyDefinition, resourceId);
+
+ if (addPropertyToGraph.isRight()) {
+ return addPropertyToGraph.right().value();
+ }
+ }
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ public TitanOperationStatus addPropertiesToGraph(TitanVertex metadataVertex, Map<String, PropertyDefinition> properties, Map<String, DataTypeDefinition> dataTypes, String resourceId) {
+
+ if (properties != null) {
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+
+ String propertyName = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+
+ StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(propertyDefinition, dataTypes);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ log.error("Property {} is invalid. Status is {}", propertyDefinition, validateAndUpdateProperty);
+ return TitanOperationStatus.ILLEGAL_ARGUMENT;
+ }
+
+ TitanOperationStatus addPropertyToGraph = addPropertyToGraphByVertex(metadataVertex, propertyName, propertyDefinition, resourceId);
+
+ if (!addPropertyToGraph.equals(TitanOperationStatus.OK)) {
+ return addPropertyToGraph;
+ }
+ }
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ public Either<PropertyData, StorageOperationStatus> addProperty(String propertyName, PropertyDefinition propertyDefinition, String resourceId) {
+
+ Either<PropertyData, TitanOperationStatus> either = addPropertyToGraph(propertyName, propertyDefinition, resourceId);
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ return Either.right(storageStatus);
+ }
+ return Either.left(either.left().value());
+ }
+
+ /**
+ * @param propertyDefinition
+ * @return
+ */
+ @Override
+ public StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
+
+ log.trace("Going to validate property type and value. {}", propertyDefinition);
+
+ String propertyType = propertyDefinition.getType();
+ String value = propertyDefinition.getDefaultValue();
+
+ ToscaPropertyType type = getType(propertyType);
+
+ if (type == null) {
+
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
+ if (dataTypeDefinition == null) {
+ log.debug("The type {} of property cannot be found.", propertyType);
+ return StorageOperationStatus.INVALID_TYPE;
+ }
+
+ StorageOperationStatus status = validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
+
+ return status;
+
+ }
+ String innerType = null;
+
+ Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, () -> propertyDefinition.getSchema());
+ if (checkInnerType.isRight()) {
+ return StorageOperationStatus.INVALID_TYPE;
+ }
+ innerType = checkInnerType.left().value();
+
+ log.trace("After validating property type {}", propertyType);
+
+ boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
+ if (false == isValidProperty) {
+ log.info("The value {} of property from type {} is invalid", value, type);
+ return StorageOperationStatus.INVALID_VALUE;
+ }
+
+ PropertyValueConverter converter = type.getConverter();
+
+ if (isEmptyValue(value)) {
+ log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
+ propertyDefinition.setDefaultValue(EMPTY_VALUE);
+ } else if (false == isEmptyValue(value)) {
+ String convertedValue = converter.convert(value, innerType, dataTypes);
+ propertyDefinition.setDefaultValue(convertedValue);
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ /*
+ * public Either<Object, Boolean> validateAndUpdatePropertyValue(String propertyType, String value, String innerType) {
+ *
+ * log. trace("Going to validate property value and its type. type = {}, value = {}" ,propertyType, value);
+ *
+ * ToscaPropertyType type = getType(propertyType);
+ *
+ * if (type == null) {
+ *
+ * Either<DataTypeDefinition, TitanOperationStatus> externalDataType = getExternalDataType(propertyType); if (externalDataType.isRight()) { TitanOperationStatus status = externalDataType.right().value(); log.debug("The type " + propertyType +
+ * " of property cannot be found. Status is " + status); if (status != TitanOperationStatus.NOT_FOUND) { BeEcompErrorManager.getInstance(). logBeInvalidTypeError("validate property type", propertyType, "property"); } return Either.right(false); }
+ *
+ * DataTypeDefinition dataTypeDefinition = externalDataType.left().value();
+ *
+ * Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypesRes = getAllDataTypes(); if (allDataTypesRes.isRight()) { TitanOperationStatus status = allDataTypesRes.right().value(); return Either.right(false); }
+ *
+ * Map<String, DataTypeDefinition> allDataTypes = allDataTypesRes.left().value();
+ *
+ * ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, allDataTypes);
+ *
+ * if (validateResult.right.booleanValue() == false) { log.debug("The value {} of property from type {} is invalid", value, propertyType); return Either.right(false); }
+ *
+ * JsonElement jsonElement = validateResult.left;
+ *
+ * String valueFromJsonElement = getValueFromJsonElement(jsonElement);
+ *
+ * return Either.left(valueFromJsonElement);
+ *
+ * }
+ *
+ * log.trace("After validating property type " + propertyType);
+ *
+ * boolean isValidProperty = isValidValue(type, value, innerType); if (false == isValidProperty) { log.debug("The value {} of property from type {} is invalid", value, type); return Either.right(false); }
+ *
+ *
+ * Object convertedValue = value; if (false == isEmptyValue(value)) { PropertyValueConverter converter = type.getConverter(); convertedValue = converter.convert(value, null); }
+ *
+ * return Either.left(convertedValue); }
+ */
+
+ public Either<PropertyData, TitanOperationStatus> addPropertyToGraph(String propertyName, PropertyDefinition propertyDefinition, String resourceId) {
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceId);
+
+ List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
+
+ propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, propertyName));
+ PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
+
+ log.debug("Before adding property to graph {}", propertyData);
+ Either<PropertyData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyData, PropertyData.class);
+ log.debug("After adding property to graph {}", propertyData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add property {} to graph. Status is {}", propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(resourceData, propertyData, GraphEdgeLabels.PROPERTY, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to associate resource {} to property {} in graph. Status is {}", resourceId, propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+ public TitanOperationStatus addPropertyToGraphByVertex(TitanVertex metadataVertex, String propertyName, PropertyDefinition propertyDefinition, String resourceId) {
+
+ List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
+
+ propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, propertyName));
+ PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
+
+ log.debug("Before adding property to graph {}", propertyData);
+ Either<TitanVertex, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyData);
+ log.debug("After adding property to graph {}", propertyData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add property {} to graph. status is {}", propertyName, operationStatus);
+ return operationStatus;
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
+ TitanVertex propertyVertex = createNodeResult.left().value();
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(metadataVertex, propertyVertex, GraphEdgeLabels.PROPERTY, props);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource {} to property {} in graph. status is {}", resourceId, propertyName, createRelResult);
+ return createRelResult;
+ }
+
+ return createRelResult;
+
+ }
+
+ public TitanGenericDao getTitanGenericDao() {
+ return titanGenericDao;
+ }
+
+ // public Either<PropertyData, StorageOperationStatus>
+ // deletePropertyFromGraphFromBl(String propertyId) {
+ //
+ // }
+
+ public Either<PropertyData, StorageOperationStatus> deleteProperty(String propertyId) {
+ Either<PropertyData, TitanOperationStatus> either = deletePropertyFromGraph(propertyId);
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ return Either.right(storageStatus);
+ }
+ return Either.left(either.left().value());
+ }
+
+ public Either<PropertyData, TitanOperationStatus> deletePropertyFromGraph(String propertyId) {
+ log.debug("Before deleting property from graph {}", propertyId);
+ return titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+ }
+
+ public Either<PropertyData, StorageOperationStatus> updateProperty(String propertyId, PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
+
+ StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
+ if (validateAndUpdateProperty != StorageOperationStatus.OK) {
+ return Either.right(validateAndUpdateProperty);
+ }
+
+ Either<PropertyData, TitanOperationStatus> either = updatePropertyFromGraph(propertyId, newPropertyDefinition);
+ if (either.isRight()) {
+ StorageOperationStatus storageStatus = DaoStatusConverter.convertTitanStatusToStorageStatus(either.right().value());
+ return Either.right(storageStatus);
+ }
+ return Either.left(either.left().value());
+ }
+
+ public Either<PropertyData, TitanOperationStatus> updatePropertyFromGraph(String propertyId, PropertyDefinition propertyDefinition) {
+ if (log.isDebugEnabled())
+ log.debug("Before updating property on graph {}", propertyId);
+
+ // get the original property data
+ Either<PropertyData, TitanOperationStatus> statusProperty = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+ if (statusProperty.isRight()) {
+ log.debug("Problem while get property with id {}. Reason - {}", propertyId, statusProperty.right().value().name());
+ return Either.right(statusProperty.right().value());
+ }
+ PropertyData orgPropertyData = statusProperty.left().value();
+ PropertyDataDefinition orgPropertyDataDefinition = orgPropertyData.getPropertyDataDefinition();
+
+ // create new property data to update
+ PropertyData newPropertyData = new PropertyData();
+ newPropertyData.setPropertyDataDefinition(propertyDefinition);
+ PropertyDataDefinition newPropertyDataDefinition = newPropertyData.getPropertyDataDefinition();
+
+ // update the original property data with new values
+ if (orgPropertyDataDefinition.getDefaultValue() == null) {
+ orgPropertyDataDefinition.setDefaultValue(newPropertyDataDefinition.getDefaultValue());
+ } else {
+ if (!orgPropertyDataDefinition.getDefaultValue().equals(newPropertyDataDefinition.getDefaultValue())) {
+ orgPropertyDataDefinition.setDefaultValue(newPropertyDataDefinition.getDefaultValue());
+ }
+ }
+ if (orgPropertyDataDefinition.getDescription() == null) {
+ orgPropertyDataDefinition.setDescription(newPropertyDataDefinition.getDescription());
+ } else {
+ if (!orgPropertyDataDefinition.getDescription().equals(newPropertyDataDefinition.getDescription())) {
+ orgPropertyDataDefinition.setDescription(newPropertyDataDefinition.getDescription());
+ }
+ }
+ if (!orgPropertyDataDefinition.getType().equals(newPropertyDataDefinition.getType())) {
+ orgPropertyDataDefinition.setType(newPropertyDataDefinition.getType());
+ }
+ if (newPropertyData.getConstraints() != null) {
+ orgPropertyData.setConstraints(newPropertyData.getConstraints());
+ }
+ orgPropertyDataDefinition.setSchema(newPropertyDataDefinition.getSchema());
+
+ return titanGenericDao.updateNode(orgPropertyData, PropertyData.class);
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ public Either<PropertyData, TitanOperationStatus> addPropertyToNodeType(String propertyName, PropertyDefinition propertyDefinition, NodeTypeEnum nodeType, String uniqueId) {
+
+ List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
+
+ propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(uniqueId, propertyName));
+ PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
+
+ if (log.isDebugEnabled())
+ log.debug("Before adding property to graph {}", propertyData);
+ Either<PropertyData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyData, PropertyData.class);
+ if (log.isDebugEnabled())
+ log.debug("After adding property to graph {}", propertyData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add property {} to graph. Status is {}", propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
+
+ UniqueIdData uniqueIdData = new UniqueIdData(nodeType, uniqueId);
+ log.debug("Before associating {} to property {}.", uniqueIdData, propertyName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(uniqueIdData, propertyData, GraphEdgeLabels.PROPERTY, props);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to associate resource {} to property {} in graph. Status is {}", uniqueId, propertyName, operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+ public TitanOperationStatus addPropertyToNodeType(TitanVertex elementVertex, String propertyName, PropertyDefinition propertyDefinition, NodeTypeEnum nodeType, String uniqueId) {
+
+ List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
+
+ propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(uniqueId, propertyName));
+ PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
+
+ if (log.isDebugEnabled())
+ log.debug("Before adding property to graph {}", propertyData);
+ Either<TitanVertex, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(propertyData);
+ if (log.isDebugEnabled())
+ log.debug("After adding property to graph {}", propertyData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add property {} to graph. status is {} ", propertyName, operationStatus);
+ return operationStatus;
+ }
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
+
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(elementVertex, propertyData, GraphEdgeLabels.PROPERTY, props);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource {} to property {} in graph. status is {}", uniqueId, propertyName, createRelResult);
+ return createRelResult;
+ }
+
+ return createRelResult;
+
+ }
+
+ public Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfNode(NodeTypeEnum nodeType, String uniqueId) {
+
+ Map<String, PropertyDefinition> resourceProps = new HashMap<String, PropertyDefinition>();
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property,
+ PropertyData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus operationStatus = childrenNodes.right().value();
+ return Either.right(operationStatus);
+ }
+
+ List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
+ if (values != null) {
+
+ for (ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
+ GraphEdge edge = immutablePair.getValue();
+ String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+ log.debug("Property {} is associated to node {}", propertyName, uniqueId);
+ PropertyData propertyData = immutablePair.getKey();
+ PropertyDefinition propertyDefinition = this.convertPropertyDataToPropertyDefinition(propertyData, propertyName, uniqueId);
+ resourceProps.put(propertyName, propertyDefinition);
+ }
+
+ }
+
+ log.debug("The properties associated to node {} are {}", uniqueId, resourceProps);
+ return Either.left(resourceProps);
+ }
+
+ public Either<Map<String, PropertyDefinition>, StorageOperationStatus> deleteAllPropertiesAssociatedToNode(NodeTypeEnum nodeType, String uniqueId) {
+
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> propertiesOfNodeRes = findPropertiesOfNode(nodeType, uniqueId);
+
+ if (propertiesOfNodeRes.isRight()) {
+ TitanOperationStatus status = propertiesOfNodeRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.right(StorageOperationStatus.OK);
+ }
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ Map<String, PropertyDefinition> value = propertiesOfNodeRes.left().value();
+ for (PropertyDefinition propertyDefinition : value.values()) {
+
+ String propertyUid = propertyDefinition.getUniqueId();
+ Either<PropertyData, TitanOperationStatus> deletePropertyRes = deletePropertyFromGraph(propertyUid);
+ if (deletePropertyRes.isRight()) {
+ log.error("Failed to delete property with id " + propertyUid);
+ TitanOperationStatus status = deletePropertyRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ }
+
+ log.debug("The properties deleted from node {} are {}", uniqueId, value);
+ return Either.left(value);
+ }
+
+ /**
+ * fetch all properties under a given resource(includes its parents' resources)
+ *
+ * @param resourceId
+ * @param properties
+ * @return
+ */
+ public TitanOperationStatus findAllResourcePropertiesRecursively(String resourceId, List<PropertyDefinition> properties) {
+ final NodeElementFetcher<PropertyDefinition> singleNodeFetcher = (resourceIdParam, attributesParam) -> findPropertiesOfNode(NodeTypeEnum.Resource, resourceIdParam, attributesParam);
+ return findAllResourceElementsDefinitionRecursively(resourceId, properties, singleNodeFetcher);
+ }
+
+ /**
+ *
+ *
+ * @param nodeType
+ * @param uniqueId
+ * @param properties
+ * @return
+ */
+ protected TitanOperationStatus findPropertiesOfNode(NodeTypeEnum nodeType, String uniqueId, List<PropertyDefinition> properties) {
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property,
+ PropertyData.class);
+
+ if (childrenNodes.isRight()) {
+ TitanOperationStatus status = childrenNodes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ }
+ return status;
+ }
+
+ List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
+ if (values != null) {
+
+ for (ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
+ GraphEdge edge = immutablePair.getValue();
+ String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+ if (log.isDebugEnabled())
+ log.debug("Property {} is associated to node {}", propertyName, uniqueId);
+ PropertyData propertyData = immutablePair.getKey();
+ PropertyDefinition propertyDefinition = this.convertPropertyDataToPropertyDefinition(propertyData, propertyName, uniqueId);
+
+ properties.add(propertyDefinition);
+
+ if (log.isTraceEnabled())
+ log.trace("findPropertiesOfNode - property {} associated to node {}", propertyDefinition, uniqueId);
+ }
+
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ public boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName) {
+
+ if (properties == null) {
+ return false;
+ }
+
+ for (PropertyDefinition propertyDefinition : properties) {
+ String parentUniqueId = propertyDefinition.getParentUniqueId();
+ String name = propertyDefinition.getName();
+
+ if (parentUniqueId.equals(resourceUid) && name.equals(propertyName)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * add property to resource instance
+ *
+ * @param innerType
+ * TODO // * @param resourceInstanceProperty // * @param resourceInstanceId // * @param index
+ *
+ * @return
+ */
+ /*
+ * public Either<PropertyValueData, TitanOperationStatus> addPropertyToResourceInstance( ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, Integer index) {
+ *
+ * Either<ComponentInstanceData, TitanOperationStatus> findResInstanceRes = titanGenericDao .getNode(UniqueIdBuilder .getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, ComponentInstanceData.class);
+ *
+ * if (findResInstanceRes.isRight()) { TitanOperationStatus status = findResInstanceRes.right().value(); if (status == TitanOperationStatus.NOT_FOUND) { status = TitanOperationStatus.INVALID_ID; } return Either.right(status); }
+ *
+ * String propertyId = resourceInstanceProperty.getUniqueId(); Either<PropertyData, TitanOperationStatus> findPropertyDefRes = titanGenericDao .getNode(UniqueIdBuilder .getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+ *
+ * if (findPropertyDefRes.isRight()) { TitanOperationStatus status = findPropertyDefRes.right().value(); if (status == TitanOperationStatus.NOT_FOUND) { status = TitanOperationStatus.INVALID_ID; } return Either.right(status); }
+ *
+ * String valueUniqueUid = resourceInstanceProperty.getValueUniqueUid(); if (valueUniqueUid == null) {
+ *
+ * PropertyData propertyData = findPropertyDefRes.left().value(); ComponentInstanceData resourceInstanceData = findResInstanceRes.left().value();
+ *
+ * ImmutablePair<TitanOperationStatus, String> isPropertyValueExists = findPropertyValue(resourceInstanceId, propertyId); if (isPropertyValueExists.getLeft() == TitanOperationStatus.ALREADY_EXIST) { log.debug("The property " + propertyId +
+ * " already added to the resource instance " + resourceInstanceId); resourceInstanceProperty.setValueUniqueUid(isPropertyValueExists.getRight ()); Either<PropertyValueData, TitanOperationStatus> updatePropertyOfResourceInstance =
+ * updatePropertyOfResourceInstance(resourceInstanceProperty, resourceInstanceId); if (updatePropertyOfResourceInstance.isRight()) { BeEcompErrorManager.getInstance().logInternalFlowError( "UpdatePropertyValueOnComponentInstance",
+ * "Failed to update property value on instance. Status is " + updatePropertyOfResourceInstance.right().value(), ErrorSeverity.ERROR); return Either.right(updatePropertyOfResourceInstance.right().value()); } return
+ * Either.left(updatePropertyOfResourceInstance.left().value()); }
+ *
+ * if (isPropertyValueExists.getLeft() != TitanOperationStatus.NOT_FOUND) { log.debug("After finding property value of {} on component instance {}", propertyId, resourceInstanceId); return Either.right(isPropertyValueExists.getLeft()); }
+ *
+ * String propertyType = propertyData.getPropertyDataDefinition().getType(); String value = resourceInstanceProperty.getValue(); Either<Object, Boolean> isValid = validateAndUpdatePropertyValue(propertyType, value);
+ *
+ * String newValue = value; if (isValid.isRight()) { Boolean res = isValid.right().value(); if (res == false) { return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT); } } else { Object object = isValid.left().value(); if (object != null) {
+ * newValue = object.toString(); } }
+ *
+ * String uniqueId = UniqueIdBuilder.buildResourceInstancePropertyValueUid( resourceInstanceData.getUniqueId(), index); PropertyValueData propertyValueData = new PropertyValueData(); propertyValueData.setUniqueId(uniqueId);
+ * propertyValueData.setValue(newValue);
+ *
+ * ImmutablePair<String, Boolean> pair = validateAndUpdateRules(propertyType, resourceInstanceProperty.getRules()); if (pair.getRight() != null && pair.getRight() == false) { BeEcompErrorManager.getInstance().
+ * logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceProperty.getName(), propertyType); return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT); } addRulesToNewPropertyValue(propertyValueData,
+ * resourceInstanceProperty, resourceInstanceId);
+ *
+ * log.debug("Before adding property value to graph {}", propertyValueData); Either<PropertyValueData, TitanOperationStatus> createNodeResult = titanGenericDao .createNode(propertyValueData, PropertyValueData.class);
+ * log.debug("After adding property value to graph {}", propertyValueData);
+ *
+ * Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao .createRelation(propertyValueData, propertyData, GraphEdgeLabels.PROPERTY_IMPL, null);
+ *
+ * if (createRelResult.isRight()) { TitanOperationStatus operationStatus = createNodeResult.right() .value(); //TODO: change logger log.error("Failed to associate property value " + uniqueId + " to property " + propertyId +
+ * " in graph. status is " + operationStatus); return Either.right(operationStatus); }
+ *
+ * createRelResult = titanGenericDao .createRelation(resourceInstanceData, propertyValueData, GraphEdgeLabels.PROPERTY_VALUE, null);
+ *
+ * if (createRelResult.isRight()) { TitanOperationStatus operationStatus = createNodeResult.right() .value(); //TODO: change logger log.error("Failed to associate resource instance " + resourceInstanceId + " property value " + uniqueId +
+ * " in graph. status is " + operationStatus); return Either.right(operationStatus); }
+ *
+ * return Either.left(createNodeResult.left().value()); } else { log.error("property value already exists."); return Either.right(TitanOperationStatus.ALREADY_EXIST); }
+ *
+ * }
+ */
+ public ImmutablePair<String, Boolean> validateAndUpdateRules(String propertyType, List<PropertyRule> rules, String innerType, Map<String, DataTypeDefinition> dataTypes, boolean isValidate) {
+
+ if (rules == null || rules.isEmpty() == true) {
+ return new ImmutablePair<String, Boolean>(null, true);
+ }
+
+ for (PropertyRule rule : rules) {
+ String value = rule.getValue();
+ Either<Object, Boolean> updateResult = validateAndUpdatePropertyValue(propertyType, value, isValidate, innerType, dataTypes);
+ if (updateResult.isRight()) {
+ Boolean status = updateResult.right().value();
+ if (status == false) {
+ return new ImmutablePair<String, Boolean>(value, status);
+ }
+ } else {
+ String newValue = null;
+ Object object = updateResult.left().value();
+ if (object != null) {
+ newValue = object.toString();
+ }
+ rule.setValue(newValue);
+ }
+ }
+
+ return new ImmutablePair<String, Boolean>(null, true);
+ }
+
+ public void addRulesToNewPropertyValue(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId) {
+
+ List<PropertyRule> rules = resourceInstanceProperty.getRules();
+ if (rules == null) {
+ PropertyRule propertyRule = buildRuleFromPath(propertyValueData, resourceInstanceProperty, resourceInstanceId);
+ rules = new ArrayList<>();
+ rules.add(propertyRule);
+ } else {
+ rules = sortRules(rules);
+ }
+
+ propertyValueData.setRules(rules);
+ }
+
+ private PropertyRule buildRuleFromPath(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId) {
+ List<String> path = resourceInstanceProperty.getPath();
+ // FOR BC. Since old Property values on VFC/VF does not have rules on
+ // graph.
+ // Update could be done on one level only, thus we can use this
+ // operation to avoid migration.
+ if (path == null || path.isEmpty() == true) {
+ path = new ArrayList<>();
+ path.add(resourceInstanceId);
+ }
+ PropertyRule propertyRule = new PropertyRule();
+ propertyRule.setRule(path);
+ propertyRule.setValue(propertyValueData.getValue());
+ return propertyRule;
+ }
+
+ private List<PropertyRule> sortRules(List<PropertyRule> rules) {
+
+ // TODO: sort the rules by size and binary representation.
+ // (x, y, .+) --> 110 6 priority 1
+ // (x, .+, z) --> 101 5 priority 2
+
+ return rules;
+ }
+
+ public ImmutablePair<TitanOperationStatus, String> findPropertyValue(String resourceInstanceId, String propertyId) {
+
+ log.debug("Going to check whether the property {} already added to resource instance {}", propertyId, resourceInstanceId);
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> getAllRes = this.getAllPropertiesOfResourceInstanceOnlyPropertyDefId(resourceInstanceId);
+ if (getAllRes.isRight()) {
+ TitanOperationStatus status = getAllRes.right().value();
+ log.trace("After fetching all properties of resource instance {}. Status is {}", resourceInstanceId, status);
+ return new ImmutablePair<TitanOperationStatus, String>(status, null);
+ }
+
+ List<ComponentInstanceProperty> list = getAllRes.left().value();
+ if (list != null) {
+ for (ComponentInstanceProperty instanceProperty : list) {
+ String propertyUniqueId = instanceProperty.getUniqueId();
+ String valueUniqueUid = instanceProperty.getValueUniqueUid();
+ log.trace("Go over property {} under resource instance {}. valueUniqueId = {}", propertyUniqueId, resourceInstanceId, valueUniqueUid);
+ if (propertyId.equals(propertyUniqueId) && valueUniqueUid != null) {
+ log.debug("The property {} already created under resource instance {}", propertyId, resourceInstanceId);
+ return new ImmutablePair<TitanOperationStatus, String>(TitanOperationStatus.ALREADY_EXIST, valueUniqueUid);
+ }
+ }
+ }
+
+ return new ImmutablePair<TitanOperationStatus, String>(TitanOperationStatus.NOT_FOUND, null);
+ }
+
+ /**
+ * update value of property on resource instance
+ *
+ * @param resourceInstanceProperty
+ * @param resourceInstanceId
+ * @return
+ */
+ /*
+ * public Either<PropertyValueData, TitanOperationStatus> updatePropertyOfResourceInstance( ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId) {
+ *
+ * /// #RULES SUPPORT /// Ignore rules received from client till support resourceInstanceProperty.setRules(null); /// /// Either<ComponentInstanceData, TitanOperationStatus> findResInstanceRes = titanGenericDao .getNode(UniqueIdBuilder
+ * .getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, ComponentInstanceData.class);
+ *
+ * if (findResInstanceRes.isRight()) { TitanOperationStatus status = findResInstanceRes.right().value(); if (status == TitanOperationStatus.NOT_FOUND) { status = TitanOperationStatus.INVALID_ID; } return Either.right(status); }
+ *
+ * String propertyId = resourceInstanceProperty.getUniqueId(); Either<PropertyData, TitanOperationStatus> findPropertyDefRes = titanGenericDao .getNode(UniqueIdBuilder .getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
+ *
+ * if (findPropertyDefRes.isRight()) { TitanOperationStatus status = findPropertyDefRes.right().value(); return Either.right(status); }
+ *
+ * String valueUniqueUid = resourceInstanceProperty.getValueUniqueUid(); if (valueUniqueUid == null) { return Either.right(TitanOperationStatus.INVALID_ID); } else { Either<PropertyValueData, TitanOperationStatus> findPropertyValueRes =
+ * titanGenericDao .getNode(UniqueIdBuilder .getKeyByNodeType(NodeTypeEnum.PropertyValue), valueUniqueUid, PropertyValueData.class); if (findPropertyValueRes.isRight()) { TitanOperationStatus status = findPropertyValueRes.right().value(); if
+ * (status == TitanOperationStatus.NOT_FOUND) { status = TitanOperationStatus.INVALID_ID; } return Either.right(status); }
+ *
+ * String value = resourceInstanceProperty.getValue();
+ *
+ * Either<ImmutablePair<PropertyData, GraphEdge>, TitanOperationStatus> child = titanGenericDao.getChild(UniqueIdBuilder .getKeyByNodeType(NodeTypeEnum.PropertyValue), valueUniqueUid, GraphEdgeLabels.PROPERTY_IMPL, NodeTypeEnum.Property,
+ * PropertyData.class);
+ *
+ * if (child.isRight()) { TitanOperationStatus status = child.right().value(); if (status == TitanOperationStatus.NOT_FOUND) { status = TitanOperationStatus.INVALID_ID; } return Either.right(status); }
+ *
+ * PropertyData propertyData = child.left().value().left; String propertyType = propertyData.getPropertyDataDefinition().getType();
+ *
+ * log.debug("The type of the property {} is {}", propertyData.getUniqueId(), propertyType);
+ *
+ * Either<Object, Boolean> isValid = validateAndUpdatePropertyValue(propertyType, value);
+ *
+ * String newValue = value; if (isValid.isRight()) { Boolean res = isValid.right().value(); if (res == false) { return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT); } } else { Object object = isValid.left().value(); if (object != null) {
+ * newValue = object.toString(); } } PropertyValueData propertyValueData = findPropertyValueRes.left().value(); log.debug("Going to update property value from {} to {}", propertyValueData.getValue(), newValue);
+ * propertyValueData.setValue(newValue);
+ *
+ * ImmutablePair<String, Boolean> pair = validateAndUpdateRules(propertyType, resourceInstanceProperty.getRules()); if (pair.getRight() != null && pair.getRight() == false) { BeEcompErrorManager.getInstance().
+ * logBeInvalidValueError("Add property value", pair.getLeft(), resourceInstanceProperty.getName(), propertyType); return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT); } updateRulesInPropertyValue(propertyValueData,
+ * resourceInstanceProperty, resourceInstanceId);
+ *
+ * Either<PropertyValueData, TitanOperationStatus> updateRes = titanGenericDao.updateNode(propertyValueData, PropertyValueData.class); if (updateRes.isRight()) { TitanOperationStatus status = updateRes.right().value(); return
+ * Either.right(status); } else { return Either.left(updateRes.left().value()); } }
+ *
+ * }
+ */
+
+ public void updateRulesInPropertyValue(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId) {
+
+ List<PropertyRule> currentRules = propertyValueData.getRules();
+
+ List<PropertyRule> rules = resourceInstanceProperty.getRules();
+ // if rules are not supported.
+ if (rules == null) {
+
+ PropertyRule propertyRule = buildRuleFromPath(propertyValueData, resourceInstanceProperty, resourceInstanceId);
+ rules = new ArrayList<>();
+ rules.add(propertyRule);
+
+ if (currentRules != null) {
+ rules = mergeRules(currentRules, rules);
+ }
+
+ } else {
+ // Full mode. all rules are sent in update operation.
+ rules = sortRules(rules);
+ }
+
+ propertyValueData.setRules(rules);
+
+ }
+
+ private List<PropertyRule> mergeRules(List<PropertyRule> currentRules, List<PropertyRule> newRules) {
+
+ List<PropertyRule> mergedRules = new ArrayList<>();
+
+ if (newRules == null || newRules.isEmpty() == true) {
+ return currentRules;
+ }
+
+ for (PropertyRule rule : currentRules) {
+ PropertyRule propertyRule = new PropertyRule(rule.getRule(), rule.getValue());
+ mergedRules.add(propertyRule);
+ }
+
+ for (PropertyRule rule : newRules) {
+ PropertyRule foundRule = findRuleInList(rule, mergedRules);
+ if (foundRule != null) {
+ foundRule.setValue(rule.getValue());
+ } else {
+ mergedRules.add(rule);
+ }
+ }
+
+ return mergedRules;
+ }
+
+ private PropertyRule findRuleInList(PropertyRule rule, List<PropertyRule> rules) {
+
+ if (rules == null || rules.isEmpty() == true || rule.getRule() == null || rule.getRule().isEmpty() == true) {
+ return null;
+ }
+
+ PropertyRule foundRule = null;
+ for (PropertyRule propertyRule : rules) {
+ if (rule.getRuleSize() != propertyRule.getRuleSize()) {
+ continue;
+ }
+ boolean equals = propertyRule.compareRule(rule);
+ if (equals == true) {
+ foundRule = propertyRule;
+ break;
+ }
+ }
+
+ return foundRule;
+ }
+
+ /**
+ * return all properties associated to resource instance. The result does contains the property unique id but not its type, default value...
+ *
+ * @param resourceInstanceUid
+ * @return
+ */
+ public Either<List<ComponentInstanceProperty>, TitanOperationStatus> getAllPropertiesOfResourceInstanceOnlyPropertyDefId(String resourceInstanceUid) {
+
+ return getAllPropertiesOfResourceInstanceOnlyPropertyDefId(resourceInstanceUid, NodeTypeEnum.ResourceInstance);
+
+ }
+
+ /*
+ * public Either<ComponentInstanceProperty, StorageOperationStatus> addPropertyValueToResourceInstance( ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, Integer index, boolean inTransaction) {
+ *
+ * /// #RULES SUPPORT /// Ignore rules received from client till support resourceInstanceProperty.setRules(null); /// ///
+ *
+ * Either<ComponentInstanceProperty, StorageOperationStatus> result = null;
+ *
+ * try {
+ *
+ * Either<PropertyValueData, TitanOperationStatus> eitherStatus = this .addPropertyToResourceInstance(resourceInstanceProperty, resourceInstanceId, index);
+ *
+ * if (eitherStatus.isRight()) { log.error( "Failed to add property value {} to resource instance {} in Graph. status is {}" , resourceInstanceProperty, resourceInstanceId, eitherStatus.right().value().name()); result =
+ * Either.right(DaoStatusConverter .convertTitanStatusToStorageStatus(eitherStatus.right() .value())); return result; } else { PropertyValueData propertyValueData = eitherStatus.left() .value();
+ *
+ * ComponentInstanceProperty propertyValueResult = buildResourceInstanceProperty( propertyValueData, resourceInstanceProperty);
+ *
+ * log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult); result = Either.left(propertyValueResult); return result; } }
+ *
+ * finally { if (false == inTransaction) { if (result == null || result.isRight()) { log.error("Going to execute rollback on graph."); titanGenericDao.rollback(); } else { log.debug("Going to execute commit on graph."); titanGenericDao.commit();
+ * } } }
+ *
+ * }
+ *
+ * public Either<ComponentInstanceProperty, StorageOperationStatus> updatePropertyValueInResourceInstance( ComponentInstanceProperty resourceInstanceProperty, String resourceInstanceId, boolean inTransaction) {
+ *
+ * Either<ComponentInstanceProperty, StorageOperationStatus> result = null;
+ *
+ * try { //TODO: verify validUniqueId exists Either<PropertyValueData, TitanOperationStatus> eitherStatus = this .updatePropertyOfResourceInstance(resourceInstanceProperty, resourceInstanceId);
+ *
+ * if (eitherStatus.isRight()) { log.error( "Failed to add property value {} to resource instance {} in Graph. status is {}" , resourceInstanceProperty, resourceInstanceId, eitherStatus.right().value().name()); result =
+ * Either.right(DaoStatusConverter .convertTitanStatusToStorageStatus(eitherStatus.right() .value())); return result; } else { PropertyValueData propertyValueData = eitherStatus.left() .value();
+ *
+ * ComponentInstanceProperty propertyValueResult = buildResourceInstanceProperty( propertyValueData, resourceInstanceProperty);
+ *
+ * log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult); result = Either.left(propertyValueResult); return result; } }
+ *
+ * finally { if (false == inTransaction) { if (result == null || result.isRight()) { log.error("Going to execute rollback on graph."); titanGenericDao.rollback(); } else { log.debug("Going to execute commit on graph."); titanGenericDao.commit();
+ * } } }
+ *
+ * }
+ */
+
+ public Either<PropertyValueData, TitanOperationStatus> removePropertyOfResourceInstance(String propertyValueUid, String resourceInstanceId) {
+
+ Either<ComponentInstanceData, TitanOperationStatus> findResInstanceRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, ComponentInstanceData.class);
+
+ if (findResInstanceRes.isRight()) {
+ TitanOperationStatus status = findResInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ Either<PropertyValueData, TitanOperationStatus> findPropertyDefRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PropertyValue), propertyValueUid, PropertyValueData.class);
+
+ if (findPropertyDefRes.isRight()) {
+ TitanOperationStatus status = findPropertyDefRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> relation = titanGenericDao.getRelation(findResInstanceRes.left().value(), findPropertyDefRes.left().value(), GraphEdgeLabels.PROPERTY_VALUE);
+ if (relation.isRight()) {
+ // TODO: add error in case of error
+ TitanOperationStatus status = relation.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ Either<PropertyValueData, TitanOperationStatus> deleteNode = titanGenericDao.deleteNode(findPropertyDefRes.left().value(), PropertyValueData.class);
+ if (deleteNode.isRight()) {
+ return Either.right(deleteNode.right().value());
+ }
+ PropertyValueData value = deleteNode.left().value();
+ return Either.left(value);
+
+ }
+
+ public Either<ComponentInstanceProperty, StorageOperationStatus> removePropertyValueFromResourceInstance(String propertyValueUid, String resourceInstanceId, boolean inTransaction) {
+
+ Either<ComponentInstanceProperty, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<PropertyValueData, TitanOperationStatus> eitherStatus = this.removePropertyOfResourceInstance(propertyValueUid, resourceInstanceId);
+
+ if (eitherStatus.isRight()) {
+ log.error("Failed to remove property value {} from resource instance {} in Graph. status is {}", propertyValueUid, resourceInstanceId, eitherStatus.right().value().name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ PropertyValueData propertyValueData = eitherStatus.left().value();
+
+ ComponentInstanceProperty propertyValueResult = new ComponentInstanceProperty();
+ propertyValueResult.setUniqueId(resourceInstanceId);
+ propertyValueResult.setValue(propertyValueData.getValue());
+
+ log.debug("The returned ResourceInstanceProperty is {}", propertyValueResult);
+ result = Either.left(propertyValueResult);
+ return result;
+ }
+ }
+
+ finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public ComponentInstanceProperty buildResourceInstanceProperty(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty) {
+
+ String value = propertyValueData.getValue();
+ String uid = propertyValueData.getUniqueId();
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty(resourceInstanceProperty, value, uid);
+ instanceProperty.setPath(resourceInstanceProperty.getPath());
+
+ return instanceProperty;
+ }
+
+ public static class PropertyConstraintJacksonDeserialiser extends org.codehaus.jackson.map.JsonDeserializer<PropertyConstraint> {
+
+ @Override
+ public PropertyConstraint deserialize(org.codehaus.jackson.JsonParser json, DeserializationContext context) throws IOException, JsonProcessingException {
+
+ ObjectCodec oc = json.getCodec();
+ JsonNode node = oc.readTree(json);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isPropertyDefaultValueValid(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
+ if (propertyDefinition == null) {
+ return false;
+ }
+ boolean isValid = false;
+ String innerType = null;
+ String propertyType = propertyDefinition.getType();
+ ToscaPropertyType type = getType(propertyType);
+ if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
+ SchemaDefinition def = propertyDefinition.getSchema();
+ if (def == null) {
+ return false;
+ }
+ PropertyDataDefinition propDef = def.getProperty();
+ if (propDef == null) {
+ return false;
+ }
+ innerType = propDef.getType();
+ }
+ String value = propertyDefinition.getDefaultValue();
+ if (type != null) {
+ isValid = isValidValue(type, value, innerType, dataTypes);
+ } else {
+ log.trace("The given type {} is not a pre defined one.", propertyType);
+
+ DataTypeDefinition foundDt = dataTypes.get(propertyType);
+ if (foundDt != null) {
+ isValid = isValidComplexValue(foundDt, value, dataTypes);
+ } else {
+ isValid = false;
+ }
+ }
+ return isValid;
+ }
+
+ public boolean isPropertyTypeValid(IComplexDefaultValue property) {
+
+ if (property == null) {
+ return false;
+ }
+
+ if (ToscaPropertyType.isValidType(property.getType()) == null) {
+
+ Either<Boolean, TitanOperationStatus> definedInDataTypes = isDefinedInDataTypes(property.getType());
+
+ if (definedInDataTypes.isRight()) {
+ return false;
+ } else {
+ Boolean isExist = definedInDataTypes.left().value();
+ return isExist.booleanValue();
+ }
+
+ }
+ return true;
+ }
+
+ @Override
+ public ImmutablePair<String, Boolean> isPropertyInnerTypeValid(IComplexDefaultValue property, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (property == null) {
+ return new ImmutablePair<String, Boolean>(null, false);
+ }
+
+ SchemaDefinition schema;
+ PropertyDataDefinition innerProp;
+ String innerType = null;
+ if ((schema = property.getSchema()) != null) {
+ if ((innerProp = schema.getProperty()) != null) {
+ innerType = innerProp.getType();
+ }
+ }
+
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+
+ if (innerToscaType == null) {
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
+ if (dataTypeDefinition == null) {
+ log.debug("The inner type {} is not a data type", innerType);
+ return new ImmutablePair<String, Boolean>(innerType, false);
+ } else {
+ log.debug("The inner type {} is a data type. Data type definition is {}", innerType, dataTypeDefinition);
+ }
+ }
+
+ return new ImmutablePair<String, Boolean>(innerType, true);
+ }
+
+ private boolean isValidComplexValue(DataTypeDefinition foundDt, String value, Map<String, DataTypeDefinition> dataTypes) {
+ /*
+ * Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypesRes = getAllDataTypes(); if (allDataTypesRes.isRight()) { TitanOperationStatus status = allDataTypesRes.right().value();
+ * log.debug("Failed to fetch data types from graph. Status is {}", status); return false; }
+ *
+ * Map<String, DataTypeDefinition> allDataTypes = allDataTypesRes.left().value();
+ */
+ ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter.validateAndUpdate(value, foundDt, dataTypes);
+
+ log.trace("The result after validating complex value of type {} is {}", foundDt.getName(), validateAndUpdate);
+
+ return validateAndUpdate.right.booleanValue();
+
+ }
+
+ private Either<Map<String, DataTypeDefinition>, TitanOperationStatus> findAllDataTypeDefinition(DataTypeDefinition dataTypeDefinition) {
+
+ Map<String, DataTypeDefinition> nameToDataTypeDef = new HashMap<>();
+
+ DataTypeDefinition typeDefinition = dataTypeDefinition;
+
+ while (typeDefinition != null) {
+
+ List<PropertyDefinition> properties = typeDefinition.getProperties();
+ if (properties != null) {
+ for (PropertyDefinition propertyDefinition : properties) {
+ String type = propertyDefinition.getType();
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByName = this.getDataTypeUsingName(type);
+ if (dataTypeByName.isRight()) {
+ return Either.right(dataTypeByName.right().value());
+ } else {
+ DataTypeDefinition value = dataTypeByName.left().value();
+ if (false == nameToDataTypeDef.containsKey(type)) {
+ nameToDataTypeDef.put(type, value);
+ }
+ }
+
+ }
+ }
+
+ typeDefinition = typeDefinition.getDerivedFrom();
+ }
+
+ return Either.left(nameToDataTypeDef);
+ }
+
+ public Either<List<ComponentInstanceProperty>, TitanOperationStatus> getAllPropertiesOfResourceInstanceOnlyPropertyDefId(String resourceInstanceUid, NodeTypeEnum instanceNodeType) {
+
+ // Either<ComponentInstanceData, TitanOperationStatus>
+ // findResInstanceRes =
+ // titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(instanceNodeType),
+ // resourceInstanceUid, ComponentInstanceData.class);
+ Either<TitanVertex, TitanOperationStatus> findResInstanceRes = titanGenericDao.getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(instanceNodeType), resourceInstanceUid);
+
+ if (findResInstanceRes.isRight()) {
+ TitanOperationStatus status = findResInstanceRes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+
+ // Either<List<ImmutablePair<PropertyValueData, GraphEdge>>,
+ // TitanOperationStatus> propertyImplNodes =
+ // titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(instanceNodeType),
+ // resourceInstanceUid, GraphEdgeLabels.PROPERTY_VALUE,
+ // NodeTypeEnum.PropertyValue, PropertyValueData.class);
+ Either<List<ImmutablePair<TitanVertex, Edge>>, TitanOperationStatus> propertyImplNodes = titanGenericDao.getChildrenVertecies(UniqueIdBuilder.getKeyByNodeType(instanceNodeType), resourceInstanceUid, GraphEdgeLabels.PROPERTY_VALUE);
+
+ if (propertyImplNodes.isRight()) {
+ TitanOperationStatus status = propertyImplNodes.right().value();
+ return Either.right(status);
+ }
+
+ List<ImmutablePair<TitanVertex, Edge>> list = propertyImplNodes.left().value();
+ if (list == null || true == list.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ List<ComponentInstanceProperty> result = new ArrayList<>();
+ for (ImmutablePair<TitanVertex, Edge> propertyValue : list) {
+ TitanVertex propertyValueDataVertex = propertyValue.getLeft();
+ String propertyValueUid = (String) titanGenericDao.getProperty(propertyValueDataVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ String value = (String) titanGenericDao.getProperty(propertyValueDataVertex, GraphPropertiesDictionary.VALUE.getProperty());
+
+ ImmutablePair<TitanVertex, Edge> propertyDefPair = titanGenericDao.getChildVertex(propertyValueDataVertex, GraphEdgeLabels.PROPERTY_IMPL);
+ if (propertyDefPair == null) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ Map<String, Object> properties = titanGenericDao.getProperties(propertyValueDataVertex);
+ PropertyValueData propertyValueData = GraphElementFactory.createElement(NodeTypeEnum.PropertyValue.getName(), GraphElementTypeEnum.Node, properties, PropertyValueData.class);
+ String propertyUniqueId = (String) titanGenericDao.getProperty(propertyDefPair.left, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+
+ ComponentInstanceProperty resourceInstanceProperty = new ComponentInstanceProperty();
+ // set property original unique id
+ resourceInstanceProperty.setUniqueId(propertyUniqueId);
+ // set resource id
+ // TODO: esofer add resource id
+ resourceInstanceProperty.setParentUniqueId(null);
+ // set value
+ resourceInstanceProperty.setValue(value);
+ // set property value unique id
+ resourceInstanceProperty.setValueUniqueUid(propertyValueUid);
+ // set rules
+ resourceInstanceProperty.setRules(propertyValueData.getRules());
+
+ result.add(resourceInstanceProperty);
+ }
+
+ return Either.left(result);
+ }
+
+ /**
+ * Find the default value from the list of component instances. Start the search from the second component instance
+ *
+ * @param pathOfComponentInstances
+ * @param propertyUniqueId
+ * @param defaultValue
+ * @return
+ */
+ public Either<String, TitanOperationStatus> findDefaultValueFromSecondPosition(List<String> pathOfComponentInstances, String propertyUniqueId, String defaultValue) {
+
+ log.trace("In find default value: path=" + pathOfComponentInstances + "propertyUniqId=" + propertyUniqueId + "defaultValue=" + defaultValue);
+
+ if (pathOfComponentInstances == null || pathOfComponentInstances.size() < 2) {
+ return Either.left(defaultValue);
+ }
+
+ String result = defaultValue;
+
+ for (int i = 1; i < pathOfComponentInstances.size(); i++) {
+ String compInstanceId = pathOfComponentInstances.get(i);
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> propertyValuesResult = this.getAllPropertiesOfResourceInstanceOnlyPropertyDefId(compInstanceId, NodeTypeEnum.ResourceInstance);
+
+ log.trace("After fetching properties values of component instance {}. {}", compInstanceId, propertyValuesResult);
+
+ if (propertyValuesResult.isRight()) {
+ TitanOperationStatus status = propertyValuesResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(status);
+ } else {
+ continue;
+ }
+ }
+
+ ComponentInstanceProperty foundCompInstanceProperty = fetchByPropertyUid(propertyValuesResult.left().value(), propertyUniqueId);
+ log.trace("After finding the component instance property on {}. {}", compInstanceId, foundCompInstanceProperty);
+
+ if (foundCompInstanceProperty == null) {
+ continue;
+ }
+
+ List<PropertyRule> rules = getOrBuildRulesIfNotExists(pathOfComponentInstances.size() - i, pathOfComponentInstances.get(i), foundCompInstanceProperty.getRules(), foundCompInstanceProperty.getValue());
+
+ log.trace("Rules of property {} on component instance {} are {}", propertyUniqueId, compInstanceId, rules);
+ PropertyRule matchedRule = findMatchRule(pathOfComponentInstances, i, rules);
+ log.trace("Match rule is {}", matchedRule);
+
+ if (matchedRule != null) {
+ result = matchedRule.getValue();
+ break;
+ }
+
+ }
+
+ return Either.left(result);
+
+ }
+
+ private ComponentInstanceProperty fetchByPropertyUid(List<ComponentInstanceProperty> list, String propertyUniqueId) {
+
+ ComponentInstanceProperty result = null;
+
+ if (list == null) {
+ return null;
+ }
+
+ for (ComponentInstanceProperty instProperty : list) {
+ if (instProperty.getUniqueId().equals(propertyUniqueId)) {
+ result = instProperty;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ private List<PropertyRule> getOrBuildRulesIfNotExists(int ruleSize, String compInstanceId, List<PropertyRule> rules, String value) {
+
+ if (rules != null) {
+ return rules;
+ }
+
+ rules = buildDefaultRule(compInstanceId, ruleSize, value);
+
+ return rules;
+
+ }
+
+ private List<PropertyRule> getRulesOfPropertyValue(int size, String instanceId, ComponentInstanceProperty componentInstanceProperty) {
+ List<PropertyRule> rules = componentInstanceProperty.getRules();
+ if (rules == null) {
+ rules = buildDefaultRule(instanceId, size, componentInstanceProperty.getValue());
+ }
+ return rules;
+ }
+
+ private List<PropertyRule> buildDefaultRule(String componentInstanceId, int size, String value) {
+
+ List<PropertyRule> rules = new ArrayList<>();
+ List<String> rule = new ArrayList<>();
+ rule.add(componentInstanceId);
+ for (int i = 0; i < size - 1; i++) {
+ rule.add(PropertyRule.RULE_ANY_MATCH);
+ }
+ PropertyRule propertyRule = new PropertyRule(rule, value);
+ rules.add(propertyRule);
+
+ return rules;
+
+ }
+
+ private PropertyRule findMatchRule(List<String> pathOfInstances, int level, List<PropertyRule> rules) {
+
+ PropertyRule propertyRule = null;
+
+ String stringForMatch = buildStringForMatch(pathOfInstances, level);
+
+ String firstCompInstance = pathOfInstances.get(level);
+
+ if (rules != null) {
+
+ for (PropertyRule rule : rules) {
+
+ int ruleSize = rule.getRule().size();
+ // check the length of the rule equals to the length of the
+ // instances path.
+ if (ruleSize != pathOfInstances.size() - level) {
+ continue;
+ }
+ // check that the rule starts with correct component instance id
+ if (false == checkFirstItem(firstCompInstance, rule.getFirstToken())) {
+ continue;
+ }
+
+ String secondToken = rule.getToken(2);
+ if (secondToken != null && (secondToken.equals(PropertyRule.FORCE_ALL) || secondToken.equals(PropertyRule.ALL))) {
+ propertyRule = rule;
+ break;
+ }
+
+ String patternStr = buildStringForMatch(rule.getRule(), 0);
+
+ Pattern pattern = Pattern.compile(patternStr);
+
+ Matcher matcher = pattern.matcher(stringForMatch);
+
+ if (matcher.matches()) {
+ log.trace("{} matches the rule {}", stringForMatch, patternStr);
+ propertyRule = rule;
+ break;
+ }
+ }
+
+ }
+
+ return propertyRule;
+ }
+
+ private boolean checkFirstItem(String left, String right) {
+ if (left != null && left.equals(right)) {
+ return true;
+ }
+ return false;
+ }
+
+ private String buildStringForMatch(List<String> pathOfInstances, int level) {
+ StringBuilder builder = new StringBuilder();
+
+ for (int i = level; i < pathOfInstances.size(); i++) {
+ builder.append(pathOfInstances.get(i));
+ if (i < pathOfInstances.size() - 1) {
+ builder.append("#");
+ }
+ }
+ return builder.toString();
+ }
+
+ public void updatePropertyByBestMatch(String propertyUniqueId, ComponentInstanceProperty instanceProperty, Map<String, ComponentInstanceProperty> instanceIdToValue) {
+
+ List<String> pathOfInstances = instanceProperty.getPath();
+ int level = 0;
+ int size = pathOfInstances.size();
+ int numberOfMatches = 0;
+ for (String instanceId : pathOfInstances) {
+ ComponentInstanceProperty componentInstanceProperty = instanceIdToValue.get(instanceId);
+
+ if (componentInstanceProperty != null) {
+
+ List<PropertyRule> rules = getRulesOfPropertyValue(size - level, instanceId, componentInstanceProperty);
+ // If it is the first level instance, then update valueUniuqeId
+ // parameter in order to know on update that
+ // we should update and not create new node on graph.
+ if (level == 0) {
+ instanceProperty.setValueUniqueUid(componentInstanceProperty.getValueUniqueUid());
+ instanceProperty.setRules(rules);
+ }
+
+ PropertyRule rule = findMatchRule(pathOfInstances, level, rules);
+ if (rule != null) {
+ numberOfMatches++;
+ String value = rule.getValue();
+ if (numberOfMatches == 1) {
+ instanceProperty.setValue(value);
+ if (log.isDebugEnabled()) {
+ log.debug("Set the value of property " + propertyUniqueId + " " + instanceProperty.getName() + " on path " + pathOfInstances + " to be " + value);
+ }
+ } else if (numberOfMatches == 2) {
+ // In case of another property value match, then use the
+ // value to be the default value of the property.
+ instanceProperty.setDefaultValue(value);
+ if (log.isDebugEnabled()) {
+ log.debug("Set the default value of property " + propertyUniqueId + " " + instanceProperty.getName() + " on path " + pathOfInstances + " to be " + value);
+ }
+ break;
+ }
+ }
+ }
+ level++;
+ }
+
+ }
+
+ public void updatePropertiesByPropertyValues(Map<String, List<ComponentInstanceProperty>> resourceInstancesProperties, Map<String, Map<String, ComponentInstanceProperty>> values) {
+
+ if (resourceInstancesProperties == null) {
+ return;
+ }
+
+ List<ComponentInstanceProperty> allProperties = new ArrayList<>();
+ Collection<List<ComponentInstanceProperty>> properties = resourceInstancesProperties.values();
+ if (properties != null) {
+ Iterator<List<ComponentInstanceProperty>> iterator = properties.iterator();
+ while (iterator.hasNext()) {
+ List<ComponentInstanceProperty> compInstancePropertyList = iterator.next();
+ allProperties.addAll(compInstancePropertyList);
+ }
+ }
+
+ // Go over each property and check whether there is a rule which updates
+ // it
+ for (ComponentInstanceProperty instanceProperty : allProperties) {
+
+ String propertyUniqueId = instanceProperty.getUniqueId();
+
+ // get the changes per componentInstanceId.
+ Map<String, ComponentInstanceProperty> instanceIdToValue = values.get(propertyUniqueId);
+
+ if (instanceIdToValue == null) {
+ continue;
+ }
+
+ this.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ }
+
+ }
+
+ /**
+ *
+ * Add data type to graph.
+ *
+ * 1. Add data type node
+ *
+ * 2. Add edge between the former node to its parent(if exists)
+ *
+ * 3. Add property node and associate it to the node created at #1. (per property & if exists)
+ *
+ * @param dataTypeDefinition
+ * @return
+ */
+ private Either<DataTypeData, TitanOperationStatus> addDataTypeToGraph(DataTypeDefinition dataTypeDefinition) {
+
+ log.debug("Got data type " + dataTypeDefinition);
+
+ String dtUniqueId = UniqueIdBuilder.buildDataTypeUid(dataTypeDefinition.getName());
+
+ DataTypeData dataTypeData = buildDataTypeData(dataTypeDefinition, dtUniqueId);
+
+ log.debug("Before adding data type to graph. dataTypeData = " + dataTypeData);
+ Either<DataTypeData, TitanOperationStatus> createDataTypeResult = titanGenericDao.createNode(dataTypeData, DataTypeData.class);
+ log.debug("After adding data type to graph. status is = {}", createDataTypeResult);
+
+ if (createDataTypeResult.isRight()) {
+ TitanOperationStatus operationStatus = createDataTypeResult.right().value();
+ log.debug("Failed to data type " + dataTypeDefinition.getName() + " to graph. status is " + operationStatus);
+ BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("AddDataType", NodeTypeEnum.DataType.getName());
+ return Either.right(operationStatus);
+ }
+
+ DataTypeData resultCTD = createDataTypeResult.left().value();
+ List<PropertyDefinition> properties = dataTypeDefinition.getProperties();
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToDataType = addPropertiesToDataType(resultCTD.getUniqueId(), properties);
+ if (addPropertiesToDataType.isRight()) {
+ log.debug("Failed add properties " + properties + " to data type " + dataTypeDefinition.getName());
+ return Either.right(addPropertiesToDataType.right().value());
+ }
+
+ String derivedFrom = dataTypeDefinition.getDerivedFromName();
+ if (derivedFrom != null) {
+ log.debug("Before creating relation between data type " + dtUniqueId + " to its parent " + derivedFrom);
+ UniqueIdData from = new UniqueIdData(NodeTypeEnum.DataType, dtUniqueId);
+
+ String deriveFromUid = UniqueIdBuilder.buildDataTypeUid(derivedFrom);
+ UniqueIdData to = new UniqueIdData(NodeTypeEnum.DataType, deriveFromUid);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(from, to, GraphEdgeLabels.DERIVED_FROM, null);
+ log.debug("After create relation between capability type " + dtUniqueId + " to its parent " + derivedFrom + ". status is " + createRelation);
+ if (createRelation.isRight()) {
+ return Either.right(createRelation.right().value());
+ }
+ }
+
+ return Either.left(createDataTypeResult.left().value());
+
+ }
+
+ private DataTypeData buildDataTypeData(DataTypeDefinition dataTypeDefinition, String ctUniqueId) {
+
+ DataTypeData dataTypeData = new DataTypeData(dataTypeDefinition);
+
+ dataTypeData.getDataTypeDataDefinition().setUniqueId(ctUniqueId);
+ Long creationDate = dataTypeData.getDataTypeDataDefinition().getCreationTime();
+ if (creationDate == null) {
+ creationDate = System.currentTimeMillis();
+ }
+ dataTypeData.getDataTypeDataDefinition().setCreationTime(creationDate);
+ dataTypeData.getDataTypeDataDefinition().setModificationTime(creationDate);
+
+ return dataTypeData;
+ }
+
+ /**
+ * add properties to capability type.
+ *
+ * Per property, add a property node and associate it to the capability type
+ *
+ * @param uniqueId
+ * @param properties
+ * @return
+ */
+ private Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToDataType(String uniqueId, List<PropertyDefinition> properties) {
+
+ Map<String, PropertyData> propertiesData = new HashMap<String, PropertyData>();
+
+ if (properties != null && false == properties.isEmpty()) {
+ for (PropertyDefinition propertyDefinition : properties) {
+ String propertyName = propertyDefinition.getName();
+
+ String propertyType = propertyDefinition.getType();
+ Either<Boolean, TitanOperationStatus> validPropertyType = isValidPropertyType(propertyType);
+ if (validPropertyType.isRight()) {
+ log.debug("Data type " + uniqueId + " contains invalid property type " + propertyType);
+ return Either.right(validPropertyType.right().value());
+ }
+ Boolean isValid = validPropertyType.left().value();
+ if (isValid == null || isValid.booleanValue() == false) {
+ log.debug("Data type " + uniqueId + " contains invalid property type " + propertyType);
+ return Either.right(TitanOperationStatus.INVALID_TYPE);
+ }
+
+ Either<PropertyData, TitanOperationStatus> addPropertyToNodeType = this.addPropertyToNodeType(propertyName, propertyDefinition, NodeTypeEnum.DataType, uniqueId);
+ if (addPropertyToNodeType.isRight()) {
+ TitanOperationStatus operationStatus = addPropertyToNodeType.right().value();
+ log.debug("Failed to associate data type " + uniqueId + " to property " + propertyName + " in graph. status is " + operationStatus);
+ BeEcompErrorManager.getInstance().logInternalFlowError("AddPropertyToDataType", "Failed to associate property to data type. Status is " + operationStatus, ErrorSeverity.ERROR);
+ return Either.right(operationStatus);
+ }
+ propertiesData.put(propertyName, addPropertyToNodeType.left().value());
+ }
+
+ DataTypeData dataTypeData = new DataTypeData();
+ dataTypeData.getDataTypeDataDefinition().setUniqueId(uniqueId);
+ long modificationTime = System.currentTimeMillis();
+ dataTypeData.getDataTypeDataDefinition().setModificationTime(modificationTime);
+
+ Either<DataTypeData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(dataTypeData, DataTypeData.class);
+ if (updateNode.isRight()) {
+ TitanOperationStatus operationStatus = updateNode.right().value();
+ log.debug("Failed to update modification time data type " + uniqueId + " from graph. status is " + operationStatus);
+ BeEcompErrorManager.getInstance().logInternalFlowError("AddPropertyToDataType", "Failed to fetch data type. Status is " + operationStatus, ErrorSeverity.ERROR);
+ return Either.right(operationStatus);
+ } else {
+ log.debug("Update data type uid {}. Set modification time to {}", uniqueId, modificationTime);
+ }
+
+ }
+
+ return Either.left(propertiesData);
+
+ }
+
+ /**
+ * Build Data type object from graph by unique id
+ *
+ * @param uniqueId
+ * @return
+ */
+ public Either<DataTypeDefinition, TitanOperationStatus> getDataTypeByUid(String uniqueId) {
+
+ Either<DataTypeDefinition, TitanOperationStatus> result = null;
+
+ Either<DataTypeData, TitanOperationStatus> dataTypesRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
+
+ if (dataTypesRes.isRight()) {
+ TitanOperationStatus status = dataTypesRes.right().value();
+ log.debug("Data type " + uniqueId + " cannot be found in graph. status is " + status);
+ return Either.right(status);
+ }
+
+ DataTypeData ctData = dataTypesRes.left().value();
+ DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
+
+ TitanOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
+ if (propertiesStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of data type " + uniqueId);
+ return Either.right(propertiesStatus);
+ }
+
+ Either<ImmutablePair<DataTypeData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.DataType,
+ DataTypeData.class);
+ log.debug("After retrieving DERIVED_FROM node of " + uniqueId + ". status is " + parentNode);
+ if (parentNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = parentNode.right().value();
+ if (titanOperationStatus != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find the parent data type of data type " + uniqueId + ". status is " + titanOperationStatus);
+ result = Either.right(titanOperationStatus);
+ return result;
+ }
+ } else {
+ // derived from node was found
+ ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
+ DataTypeData parentCT = immutablePair.getKey();
+
+ String parentUniqueId = parentCT.getUniqueId();
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
+
+ if (dataTypeByUid.isRight()) {
+ return Either.right(dataTypeByUid.right().value());
+ }
+
+ DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
+
+ dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
+
+ }
+ result = Either.left(dataTypeDefinition);
+
+ return result;
+ }
+
+ private TitanOperationStatus fillProperties(String uniqueId, DataTypeDefinition dataTypeDefinition) {
+
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfNode = this.findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
+ if (findPropertiesOfNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = findPropertiesOfNode.right().value();
+ log.debug("After looking for properties of vertex " + uniqueId + ". status is " + titanOperationStatus);
+ if (TitanOperationStatus.NOT_FOUND.equals(titanOperationStatus)) {
+ return TitanOperationStatus.OK;
+ } else {
+ return titanOperationStatus;
+ }
+ } else {
+ Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
+ if (properties != null && properties.isEmpty() == false) {
+ List<PropertyDefinition> listOfProps = new ArrayList<>();
+
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+ String propName = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
+ newPropertyDefinition.setName(propName);
+ listOfProps.add(newPropertyDefinition);
+ }
+ dataTypeDefinition.setProperties(listOfProps);
+ }
+ return TitanOperationStatus.OK;
+ }
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition, boolean inTransaction) {
+
+ Either<DataTypeDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<DataTypeData, TitanOperationStatus> eitherStatus = addDataTypeToGraph(dataTypeDefinition);
+
+ if (eitherStatus.isRight()) {
+ log.debug("Failed to add data type {} to Graph. status is {}", dataTypeDefinition, eitherStatus.right().value().name());
+ BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("AddDataType", "DataType");
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
+ return result;
+ } else {
+ DataTypeData capabilityTypeData = eitherStatus.left().value();
+
+ DataTypeDefinition dataTypeDefResult = convertDTDataToDTDefinition(capabilityTypeData);
+ log.debug("The returned CapabilityTypeDefinition is " + dataTypeDefResult);
+ result = Either.left(dataTypeDefResult);
+ return result;
+ }
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition) {
+ return addDataType(dataTypeDefinition, true);
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(String name, boolean inTransaction) {
+
+ Either<DataTypeDefinition, StorageOperationStatus> result = null;
+ try {
+
+ String dtUid = UniqueIdBuilder.buildDataTypeUid(name);
+ Either<DataTypeDefinition, TitanOperationStatus> ctResult = this.getDataTypeByUid(dtUid);
+
+ if (ctResult.isRight()) {
+ TitanOperationStatus status = ctResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to retrieve information on capability type " + name + "status is " + status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(ctResult.right().value()));
+ return result;
+ }
+
+ result = Either.left(ctResult.left().value());
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(String name) {
+ return getDataTypeByName(name, true);
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByNameWithoutDerived(String name) {
+ return getDataTypeByNameWithoutDerived(name, true);
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByNameWithoutDerived(String name, boolean inTransaction) {
+
+ Either<DataTypeDefinition, StorageOperationStatus> result = null;
+ try {
+
+ String uid = UniqueIdBuilder.buildDataTypeUid(name);
+ Either<DataTypeDefinition, TitanOperationStatus> ctResult = this.getDataTypeByUidWithoutDerivedDataTypes(uid);
+
+ if (ctResult.isRight()) {
+ TitanOperationStatus status = ctResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to retrieve information on capability type " + name + "status is " + status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(ctResult.right().value()));
+ return result;
+ }
+
+ result = Either.left(ctResult.left().value());
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<DataTypeDefinition, TitanOperationStatus> getDataTypeByUidWithoutDerivedDataTypes(String uniqueId) {
+
+ Either<DataTypeData, TitanOperationStatus> dataTypesRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
+
+ if (dataTypesRes.isRight()) {
+ TitanOperationStatus status = dataTypesRes.right().value();
+ log.debug("Data type " + uniqueId + " cannot be found in graph. status is " + status);
+ return Either.right(status);
+ }
+
+ DataTypeData ctData = dataTypesRes.left().value();
+ DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
+
+ TitanOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
+ if (propertiesStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of data type " + uniqueId);
+ return Either.right(propertiesStatus);
+ }
+
+ return Either.left(dataTypeDefinition);
+ }
+
+ public Either<DataTypeDefinition, TitanOperationStatus> getDataTypeByNameWithoutDerivedDataTypes(String name) {
+
+ String uid = UniqueIdBuilder.buildDataTypeUid(name);
+ return getDataTypeByUidWithoutDerivedDataTypes(uid);
+
+ }
+
+ /**
+ *
+ * convert between graph Node object to Java object
+ *
+ * @param dataTypeData
+ * @return
+ */
+ protected DataTypeDefinition convertDTDataToDTDefinition(DataTypeData dataTypeData) {
+ log.debug("The object returned after create data type is " + dataTypeData);
+
+ DataTypeDefinition dataTypeDefResult = new DataTypeDefinition(dataTypeData.getDataTypeDataDefinition());
+
+ return dataTypeDefResult;
+ }
+
+ private Either<Boolean, TitanOperationStatus> isValidPropertyType(String propertyType) {
+
+ if (propertyType == null || propertyType.isEmpty()) {
+ return Either.left(false);
+ }
+
+ ToscaPropertyType toscaPropertyType = ToscaPropertyType.isValidType(propertyType);
+ if (toscaPropertyType == null) {
+ Either<Boolean, TitanOperationStatus> definedInDataTypes = isDefinedInDataTypes(propertyType);
+ return definedInDataTypes;
+ } else {
+ return Either.left(true);
+ }
+ }
+
+ private Either<Boolean, TitanOperationStatus> isDefinedInDataTypes(String propertyType) {
+
+ String dataTypeUid = UniqueIdBuilder.buildDataTypeUid(propertyType);
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = getDataTypeByUid(dataTypeUid);
+ if (dataTypeByUid.isRight()) {
+ TitanOperationStatus status = dataTypeByUid.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return Either.left(false);
+ }
+ return Either.right(status);
+ }
+
+ return Either.left(true);
+
+ }
+
+ private Either<DataTypeDefinition, TitanOperationStatus> getExternalDataType(String propertyType) {
+
+ String dataTypeUid = UniqueIdBuilder.buildDataTypeUid(propertyType);
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = getDataTypeByUid(dataTypeUid);
+ if (dataTypeByUid.isRight()) {
+ TitanOperationStatus status = dataTypeByUid.right().value();
+ return Either.right(status);
+ }
+
+ return Either.left(dataTypeByUid.left().value());
+
+ }
+
+ public Either<Map<String, DataTypeDefinition>, TitanOperationStatus> getAllDataTypes() {
+
+ Map<String, DataTypeDefinition> dataTypes = new HashMap<>();
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> result = Either.left(dataTypes);
+
+ Either<List<DataTypeData>, TitanOperationStatus> getAllDataTypes = titanGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class);
+ if (getAllDataTypes.isRight()) {
+ TitanOperationStatus status = getAllDataTypes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(status);
+ } else {
+ return result;
+ }
+ }
+
+ List<DataTypeData> list = getAllDataTypes.left().value();
+ if (list != null) {
+
+ log.trace("Number of data types to load is " + list.size());
+
+ List<String> collect = list.stream().map(p -> p.getDataTypeDataDefinition().getName()).collect(Collectors.toList());
+ log.trace("The data types to load are " + collect);
+
+ for (DataTypeData dataTypeData : list) {
+
+ log.trace("Going to fetch data type " + dataTypeData.getDataTypeDataDefinition().getName() + ". uid is " + dataTypeData.getUniqueId());
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = this.getAndAddDataTypeByUid(dataTypeData.getUniqueId(), dataTypes);
+ if (dataTypeByUid.isRight()) {
+ TitanOperationStatus status = dataTypeByUid.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.INVALID_ID;
+ }
+ return Either.right(status);
+ }
+ }
+ }
+
+ if (log.isTraceEnabled()) {
+ if (result.isRight()) {
+ log.trace("After fetching all data types " + result);
+ } else {
+ Map<String, DataTypeDefinition> map = result.left().value();
+ if (map != null) {
+ String types = map.keySet().stream().collect(Collectors.joining(",", "[", "]"));
+ log.trace("After fetching all data types " + types);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Build Data type object from graph by unique id
+ *
+ * @param uniqueId
+ * @return
+ */
+ public Either<DataTypeDefinition, TitanOperationStatus> getAndAddDataTypeByUid(String uniqueId, Map<String, DataTypeDefinition> allDataTypes) {
+
+ Either<DataTypeDefinition, TitanOperationStatus> result = null;
+
+ if (allDataTypes.containsKey(uniqueId)) {
+ return Either.left(allDataTypes.get(uniqueId));
+ }
+
+ Either<DataTypeData, TitanOperationStatus> dataTypesRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
+
+ if (dataTypesRes.isRight()) {
+ TitanOperationStatus status = dataTypesRes.right().value();
+ log.debug("Data type " + uniqueId + " cannot be found in graph. status is " + status);
+ return Either.right(status);
+ }
+
+ DataTypeData ctData = dataTypesRes.left().value();
+ DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
+
+ TitanOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
+ if (propertiesStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of data type " + uniqueId);
+ return Either.right(propertiesStatus);
+ }
+
+ allDataTypes.put(dataTypeDefinition.getName(), dataTypeDefinition);
+
+ String derivedFrom = dataTypeDefinition.getDerivedFromName();
+ if (allDataTypes.containsKey(derivedFrom)) {
+ DataTypeDefinition parentDataTypeDefinition = allDataTypes.get(derivedFrom);
+
+ dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
+
+ return Either.left(dataTypeDefinition);
+ }
+
+ Either<ImmutablePair<DataTypeData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.DataType,
+ DataTypeData.class);
+ log.debug("After retrieving DERIVED_FROM node of " + uniqueId + ". status is " + parentNode);
+ if (parentNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = parentNode.right().value();
+ if (titanOperationStatus != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find the parent data type of data type " + uniqueId + ". status is " + titanOperationStatus);
+ result = Either.right(titanOperationStatus);
+ return result;
+ }
+ } else {
+ // derived from node was found
+ ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
+ DataTypeData parentCT = immutablePair.getKey();
+
+ String parentUniqueId = parentCT.getUniqueId();
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
+
+ if (dataTypeByUid.isRight()) {
+ return Either.right(dataTypeByUid.right().value());
+ }
+
+ DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
+
+ dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
+
+ }
+ result = Either.left(dataTypeDefinition);
+
+ return result;
+ }
+
+ public Either<DataTypeDefinition, TitanOperationStatus> getDataTypeUsingName(String name) {
+
+ String uid = UniqueIdBuilder.buildDataTypeUid(name);
+
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = getDataTypeByUid(uid);
+
+ return dataTypeByUid;
+ }
+
+ public Either<String, TitanOperationStatus> checkInnerType(PropertyDataDefinition propDataDef) {
+
+ String propertyType = propDataDef.getType();
+
+ ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
+
+ Either<String, TitanOperationStatus> result = getInnerType(type, () -> propDataDef.getSchema());
+
+ return result;
+ }
+
+ public Either<List<DataTypeData>, TitanOperationStatus> getAllDataTypeNodes() {
+ Either<List<DataTypeData>, TitanOperationStatus> getAllDataTypes = titanGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class);
+ if (getAllDataTypes.isRight()) {
+ TitanOperationStatus status = getAllDataTypes.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ status = TitanOperationStatus.OK;
+ return Either.right(status);
+ }
+ }
+ return getAllDataTypes;
+ }
+
+ public Either<Object, Boolean> validateAndUpdatePropertyValue(String propertyType, String value, boolean isValidate, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ log.trace("Going to validate property value and its type. type = {}, value = {}", propertyType, value);
+ ToscaPropertyType type = getType(propertyType);
+
+ if (isValidate) {
+
+ if (type == null) {
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
+ ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
+ if (validateResult.right.booleanValue() == false) {
+ log.debug("The value {} of property from type {} is invalid", value, propertyType);
+ return Either.right(false);
+ }
+ JsonElement jsonElement = validateResult.left;
+ String valueFromJsonElement = getValueFromJsonElement(jsonElement);
+ return Either.left(valueFromJsonElement);
+ }
+ log.trace("before validating property type {}", propertyType);
+ boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
+ if (false == isValidProperty) {
+ log.debug("The value {} of property from type {} is invalid", value, type);
+ return Either.right(false);
+ }
+ }
+ Object convertedValue = value;
+ if (false == isEmptyValue(value) && isValidate) {
+ PropertyValueConverter converter = type.getConverter();
+ convertedValue = converter.convert(value, innerType, dataTypes);
+ }
+ return Either.left(convertedValue);
+ }
+
+ public Either<Object, Boolean> validateAndUpdatePropertyValue(String propertyType, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return validateAndUpdatePropertyValue(propertyType, value, true, innerType, dataTypes);
+ }
+
+ /*
+ * @Override public PropertyOperation getPropertyOperation() { return this; }
+ */
+ protected TitanOperationStatus fillProperties(String uniqueId, Consumer<List<PropertyDefinition>> propertySetter) {
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfNode = this.findPropertiesOfNode(NodeTypeEnum.GroupType, uniqueId);
+ if (findPropertiesOfNode.isRight()) {
+ TitanOperationStatus titanOperationStatus = findPropertiesOfNode.right().value();
+ log.debug("After looking for properties of vertex " + uniqueId + ". status is " + titanOperationStatus);
+ if (TitanOperationStatus.NOT_FOUND.equals(titanOperationStatus)) {
+ return TitanOperationStatus.OK;
+ } else {
+ return titanOperationStatus;
+ }
+ } else {
+ Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
+
+ if (properties != null) {
+ List<PropertyDefinition> propertiesAsList = properties.entrySet().stream().map(p -> p.getValue()).collect(Collectors.toList());
+ propertySetter.accept(propertiesAsList);
+ }
+
+ return TitanOperationStatus.OK;
+ }
+ }
+
+ /**
+ * add properties to element type.
+ *
+ * Per property, add a property node and associate it to the element type
+ *
+ * @param uniqueId
+ * @param propertiesMap
+ * TODO
+ * @return
+ */
+ protected Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToElementType(String uniqueId, NodeTypeEnum nodeType, Map<String, PropertyDefinition> propertiesMap) {
+
+ Map<String, PropertyData> propertiesData = new HashMap<String, PropertyData>();
+
+ if (propertiesMap != null) {
+
+ for (Entry<String, PropertyDefinition> propertyDefinitionEntry : propertiesMap.entrySet()) {
+ String propertyName = propertyDefinitionEntry.getKey();
+
+ Either<PropertyData, TitanOperationStatus> addPropertyToNodeType = this.addPropertyToNodeType(propertyName, propertyDefinitionEntry.getValue(), nodeType, uniqueId);
+
+ if (addPropertyToNodeType.isRight()) {
+ TitanOperationStatus operationStatus = addPropertyToNodeType.right().value();
+ log.error("Failed to associate " + nodeType.getName() + " " + uniqueId + " to property " + propertyName + " in graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+ propertiesData.put(propertyName, addPropertyToNodeType.left().value());
+
+ }
+ }
+
+ return Either.left(propertiesData);
+
+ }
+
+ protected TitanOperationStatus addPropertiesToElementType(String uniqueId, NodeTypeEnum nodeType, Map<String, PropertyDefinition> propertiesMap, TitanVertex elementVertex) {
+
+ if (propertiesMap != null) {
+
+ for (Entry<String, PropertyDefinition> propertyDefinitionEntry : propertiesMap.entrySet()) {
+ String propertyName = propertyDefinitionEntry.getKey();
+
+ TitanOperationStatus operationStatus = this.addPropertyToNodeType(elementVertex, propertyName, propertyDefinitionEntry.getValue(), nodeType, uniqueId);
+
+ if (!operationStatus.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate {} {} to property {} in graph. status is {}", nodeType.getName(), uniqueId, propertyName, operationStatus);
+ return operationStatus;
+ }
+ }
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ public Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToElementType(String uniqueId, NodeTypeEnum elementType, List<PropertyDefinition> properties) {
+
+ Map<String, PropertyDefinition> propMap;
+ if (properties == null) {
+ propMap = null;
+ } else {
+ propMap = properties.stream().collect(Collectors.toMap(propDef -> propDef.getName(), propDef -> propDef));
+ }
+ return addPropertiesToElementType(uniqueId, elementType, propMap);
+ }
+
+ public TitanOperationStatus addPropertiesToElementType(TitanVertex elementVertex, String uniqueId, NodeTypeEnum elementType, List<PropertyDefinition> properties) {
+
+ Map<String, PropertyDefinition> propMap;
+ if (properties == null) {
+ propMap = null;
+ } else {
+ propMap = properties.stream().collect(Collectors.toMap(propDef -> propDef.getName(), propDef -> propDef));
+ }
+ return addPropertiesToElementType(uniqueId, elementType, propMap, elementVertex);
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> updateDataType(DataTypeDefinition newDataTypeDefinition, DataTypeDefinition oldDataTypeDefinition) {
+ return updateDataType(newDataTypeDefinition, oldDataTypeDefinition, true);
+ }
+
+ @Override
+ public Either<DataTypeDefinition, StorageOperationStatus> updateDataType(DataTypeDefinition newDataTypeDefinition, DataTypeDefinition oldDataTypeDefinition, boolean inTransaction) {
+
+ Either<DataTypeDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ List<PropertyDefinition> newProperties = newDataTypeDefinition.getProperties();
+
+ List<PropertyDefinition> oldProperties = oldDataTypeDefinition.getProperties();
+
+ String newDerivedFromName = getDerivedFromName(newDataTypeDefinition);
+
+ String oldDerivedFromName = getDerivedFromName(oldDataTypeDefinition);
+
+ String dataTypeName = newDataTypeDefinition.getName();
+
+ List<PropertyDefinition> propertiesToAdd = new ArrayList<>();
+ if (isPropertyOmitted(newProperties, oldProperties, dataTypeName) || isPropertyTypeChanged(dataTypeName, newProperties, oldProperties, propertiesToAdd) || isDerivedFromNameChanged(dataTypeName, newDerivedFromName, oldDerivedFromName)) {
+
+ log.debug("The new data type " + dataTypeName + " is invalid.");
+
+ result = Either.right(StorageOperationStatus.CANNOT_UPDATE_EXISTING_ENTITY);
+ return result;
+ }
+
+ if (propertiesToAdd == null || propertiesToAdd.isEmpty()) {
+ log.debug("No new properties has been defined in the new data type " + newDataTypeDefinition);
+ result = Either.right(StorageOperationStatus.OK);
+ return result;
+ }
+
+ Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToDataType = addPropertiesToDataType(oldDataTypeDefinition.getUniqueId(), propertiesToAdd);
+
+ if (addPropertiesToDataType.isRight()) {
+ log.debug("Failed to update data type {} to Graph. Status is {}", oldDataTypeDefinition, addPropertiesToDataType.right().value().name());
+ BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("UpdateDataType", "Property");
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertiesToDataType.right().value()));
+ return result;
+ } else {
+
+ Either<DataTypeDefinition, TitanOperationStatus> dataTypeByUid = this.getDataTypeByUid(oldDataTypeDefinition.getUniqueId());
+ if (dataTypeByUid.isRight()) {
+ TitanOperationStatus status = addPropertiesToDataType.right().value();
+ log.debug("Failed to get data type {} after update. Status is {}", oldDataTypeDefinition.getUniqueId(), status.name());
+ BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError("UpdateDataType", "Property", status.name());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ result = Either.left(dataTypeByUid.left().value());
+ }
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private String getDerivedFromName(DataTypeDefinition dataTypeDefinition) {
+ String derivedFromName = dataTypeDefinition.getDerivedFromName();
+ // if (derivedFromName == null) {
+ // DataTypeDefinition derivedFrom = dataTypeDefinition.getDerivedFrom();
+ // if (derivedFrom != null) {
+ // log.debug("Dervied from is taken from definition");
+ // derivedFromName = derivedFrom.getName();
+ // }
+ // } else {
+ // log.debug("Dervied from is taken from field derivedFromName");
+ // }
+ return derivedFromName;
+ }
+
+ private boolean isPropertyTypeChanged(String dataTypeName, List<PropertyDefinition> newProperties, List<PropertyDefinition> oldProperties, List<PropertyDefinition> outputPropertiesToAdd) {
+
+ if (newProperties != null && oldProperties != null) {
+
+ Map<String, PropertyDefinition> newPropsMapper = newProperties.stream().collect(Collectors.toMap(p -> p.getName(), p -> p));
+ Map<String, PropertyDefinition> oldPropsMapper = oldProperties.stream().collect(Collectors.toMap(p -> p.getName(), p -> p));
+
+ for (Entry<String, PropertyDefinition> newPropertyEntry : newPropsMapper.entrySet()) {
+
+ String propName = newPropertyEntry.getKey();
+ PropertyDefinition propDef = newPropertyEntry.getValue();
+
+ PropertyDefinition oldPropertyDefinition = oldPropsMapper.get(propName);
+ if (oldPropertyDefinition == null) {
+ log.debug("New property {} received in the data type {}", propName, dataTypeName);
+ outputPropertiesToAdd.add(propDef);
+ continue;
+ }
+
+ String oldType = oldPropertyDefinition.getType();
+ String oldEntryType = getEntryType(oldPropertyDefinition);
+
+ String newType = propDef.getType();
+ String newEntryType = getEntryType(propDef);
+
+ if (false == oldType.equals(newType)) {
+ log.debug("Existing property {} in data type {} has a differnet type {} than the new one {}", propName, dataTypeName, oldType, newType);
+ return true;
+ }
+
+ if (false == equalsEntryTypes(oldEntryType, newEntryType)) {
+ log.debug("Existing property {} in data type {} has a differnet entry type {} than the new one {}", propName, dataTypeName, oldEntryType, newEntryType);
+ return true;
+ }
+
+ }
+
+ }
+
+ return false;
+ }
+
+ private boolean equalsEntryTypes(String oldEntryType, String newEntryType) {
+
+ if (oldEntryType == null && newEntryType == null) {
+ return true;
+ } else if (oldEntryType != null && newEntryType != null) {
+ return oldEntryType.equals(newEntryType);
+ } else {
+ return false;
+ }
+ }
+
+ private String getEntryType(PropertyDefinition oldPropertyDefinition) {
+ String entryType = null;
+ SchemaDefinition schema = oldPropertyDefinition.getSchema();
+ if (schema != null) {
+ PropertyDataDefinition schemaProperty = schema.getProperty();
+ if (schemaProperty != null) {
+ entryType = schemaProperty.getType();
+ }
+ }
+ return entryType;
+ }
+
+ private boolean isPropertyOmitted(List<PropertyDefinition> newProperties, List<PropertyDefinition> oldProperties, String dataTypeName) {
+
+ boolean isValid = validateChangeInCaseOfEmptyProperties(newProperties, oldProperties, dataTypeName);
+ if (false == isValid) {
+ log.debug("At least one property is missing in the new data type {}", dataTypeName);
+ return false;
+ }
+
+ if (newProperties != null && oldProperties != null) {
+
+ List<String> newProps = newProperties.stream().map(p -> p.getName()).collect(Collectors.toList());
+ List<String> oldProps = oldProperties.stream().map(p -> p.getName()).collect(Collectors.toList());
+
+ if (false == newProps.containsAll(oldProps)) {
+ StringJoiner joiner = new StringJoiner(",", "[", "]");
+ newProps.forEach(p -> joiner.add(p));
+ log.debug("Properties {} in data type {} are missing, but they already defined in the existing data type", joiner.toString(), dataTypeName);
+ return true;
+ }
+
+ }
+ return false;
+ }
+
+ private boolean validateChangeInCaseOfEmptyProperties(List<PropertyDefinition> newProperties, List<PropertyDefinition> oldProperties, String dataTypeName) {
+
+ if (newProperties != null) {
+ if (newProperties.isEmpty()) {
+ newProperties = null;
+ }
+ }
+
+ if (oldProperties != null) {
+ if (oldProperties.isEmpty()) {
+ oldProperties = null;
+ }
+ }
+
+ if ((newProperties == null && oldProperties == null) || (newProperties != null && oldProperties != null)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isDerivedFromNameChanged(String dataTypeName, String newDerivedFromName, String oldDerivedFromName) {
+
+ if (newDerivedFromName != null) {
+ boolean isEqual = newDerivedFromName.equals(oldDerivedFromName);
+ if (false == isEqual) {
+ log.debug("The new datatype {} derived from another data type {} than the existing one {}", dataTypeName, newDerivedFromName, oldDerivedFromName);
+ }
+ return !isEqual;
+ } else if (oldDerivedFromName == null) {
+ return false;
+ } else {// new=null, old != null
+ log.debug("The new datatype {} derived from another data type {} than the existing one {}", dataTypeName, newDerivedFromName, oldDerivedFromName);
+ return true;
+ }
+
+ }
+
+ /**
+ *
+ * Future - unfinished
+ *
+ * @param type
+ * @param value
+ * @return
+ */
+ public boolean isValueToscaFunction(String type, String value) {
+
+ boolean result = false;
+
+ if (ToscaPropertyType.STRING.getType().equals(type) || isScalarDerivedFromString(type)) {
+
+ }
+
+ String[] functions = { "get_input" };
+
+ if (value != null) {
+
+ for (String function : functions) {
+
+ }
+
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Future - unfinished
+ *
+ * @param type
+ * @return
+ */
+ private boolean isScalarDerivedFromString(String type) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperation.java
new file mode 100644
index 0000000000..e8892ad333
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperation.java
@@ -0,0 +1,1674 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.CapabiltyInstance;
+import org.openecomp.sdc.be.model.Point;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.RequirementImplDef;
+import org.openecomp.sdc.be.model.operations.api.IRequirementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.resources.data.RequirementImplData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import fj.data.Either;
+
+@Component("requirement-operation")
+public class RequirementOperation implements IRequirementOperation {
+
+ private static final String NA = "NA";
+
+ private static final String EQUAL_SIGN = "=";
+
+ private static final String EMPTY_STRING = "";
+
+ public RequirementOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(RequirementOperation.class.getName());
+
+ @javax.annotation.Resource
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ protected CapabilityTypeDefinition convertCTDataToCTDefinition(CapabilityTypeData capabilityTypeData) {
+ log.debug("The object returned after create capability is " + capabilityTypeData);
+
+ CapabilityTypeDefinition capabilityTypeDefResult = new CapabilityTypeDefinition(
+ capabilityTypeData.getCapabilityTypeDataDefinition());
+
+ return capabilityTypeDefResult;
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param capabilityOperation
+ */
+ public void setCapabilityOperation(CapabilityOperation capabilityOperation) {
+ this.capabilityOperation = capabilityOperation;
+ }
+
+ public void setCapabilityTypeOperation(CapabilityTypeOperation capabilityTypeOperation) {
+ this.capabilityTypeOperation = capabilityTypeOperation;
+ }
+
+ @Override
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource(String reqName,
+ RequirementDefinition reqDefinition, String resourceId) {
+
+ return addRequirementToResource(reqName, reqDefinition, resourceId, false);
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateRequirementToRelationshipType(RequirementData reqData,
+ RequirementDefinition reqDefinition) {
+
+ String relationship = reqDefinition.getRelationship();
+
+ if (relationship == null) {
+ log.debug("The provided relationship is null.");
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ UniqueIdData uniqueIdData = new UniqueIdData(NodeTypeEnum.RelationshipType, relationship);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(reqData,
+ uniqueIdData, GraphEdgeLabels.RELATIONSHIP_TYPE, null);
+
+ return createRelation;
+
+ }
+
+ /**
+ * Associate the requirement node to its capability type
+ *
+ * @param reqData
+ * @param reqDefinition
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateRequirementToCapabilityType(RequirementData reqData,
+ RequirementDefinition reqDefinition) {
+
+ String capability = reqDefinition.getCapability();
+
+ UniqueIdData uniqueIdData = new UniqueIdData(NodeTypeEnum.CapabilityType, capability);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(reqData,
+ uniqueIdData, GraphEdgeLabels.CAPABILITY_TYPE, null);
+
+ log.debug("After associating requirementData " + reqData + " to capability " + capability + ". status is "
+ + createRelation);
+
+ return createRelation;
+ }
+
+ private TitanOperationStatus associateRequirementToCapabilityType(TitanVertex reqData,
+ RequirementDefinition reqDefinition) {
+
+ String capability = reqDefinition.getCapability();
+
+ UniqueIdData uniqueIdData = new UniqueIdData(NodeTypeEnum.CapabilityType, capability);
+ TitanOperationStatus createRelation = titanGenericDao.createEdge(reqData, uniqueIdData,
+ GraphEdgeLabels.CAPABILITY_TYPE, null);
+
+ log.debug("After associating requirementData {} to capability {}. status is {}" + reqData, capability,
+ createRelation);
+
+ return createRelation;
+ }
+
+ /**
+ * Associate requirement impl node to capability instance node
+ *
+ * @param reqImplData
+ * @param capabilityInstData
+ * @param capabilityName
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateRequirementImplToCapabilityInst(
+ RequirementImplData reqImplData, CapabilityInstData capabilityInstData, String capabilityName) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), capabilityName);
+
+ log.debug(
+ "Before associating requirement impl " + reqImplData + " to capability instance " + capabilityInstData);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(reqImplData,
+ capabilityInstData, GraphEdgeLabels.CAPABILITY_INST, props);
+ log.debug("After associating requirement impl " + reqImplData + " to capability instance " + capabilityInstData
+ + ".status is " + createRelation);
+
+ return createRelation;
+
+ }
+
+ /**
+ * Add requirement node to graph
+ *
+ * @param resourceId
+ * @param reqName
+ * @param reqDefinition
+ * @return
+ */
+ private Either<RequirementData, TitanOperationStatus> addRequirementData(String resourceId, String reqName,
+ RequirementDefinition reqDefinition) {
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceId);
+
+ RequirementData requirementData = buildRequirementData(resourceId, reqName, reqDefinition);
+
+ log.debug("Before adding requirement data to graph {}", requirementData);
+ Either<RequirementData, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(requirementData,
+ RequirementData.class);
+
+ log.debug("After adding requirement to graph {}", requirementData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add requirement " + reqName + " [ " + requirementData + " ] " + " to graph. status is "
+ + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ TitanOperationStatus status = associateResourceDataToRequirementData(resourceId, reqName, resourceData,
+ requirementData);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(status);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+ private Either<TitanVertex, TitanOperationStatus> addRequirementData(TitanVertex vertex, String resourceId,
+ String reqName, RequirementDefinition reqDefinition) {
+
+ RequirementData requirementData = buildRequirementData(resourceId, reqName, reqDefinition);
+
+ log.debug("Before adding requirement data to graph {}", requirementData);
+ Either<TitanVertex, TitanOperationStatus> createNodeResult = titanGenericDao.createNode(requirementData);
+
+ log.debug("After adding requirement to graph {}", requirementData);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add requirement " + reqName + " [ " + requirementData + " ] " + " to graph. status is "
+ + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ TitanOperationStatus status = associateResourceDataToRequirementData(resourceId, reqName, vertex,
+ createNodeResult.left().value());
+ if (!status.equals(TitanOperationStatus.OK)) {
+ return Either.right(status);
+ }
+ return Either.left(createNodeResult.left().value());
+ }
+
+ /**
+ * Asssociate resource node to requirement node with REQUIREMENT label and
+ * requirement name as property on the edge.
+ *
+ * @param resourceId
+ * @param reqName
+ * @param resourceData
+ * @param requirementData
+ * @return
+ */
+ private TitanOperationStatus associateResourceDataToRequirementData(String resourceId, String reqName,
+ ResourceMetadataData resourceData, RequirementData requirementData) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), reqName);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(resourceData,
+ requirementData, GraphEdgeLabels.REQUIREMENT, props);
+ log.debug("After creatin edge between resource " + resourceId + " to requirement " + requirementData);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.error("Failed to associate resource " + resourceId + " to requirement " + reqName + "[ "
+ + requirementData + "] in graph. status is " + operationStatus);
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus associateResourceDataToRequirementData(String resourceId, String reqName,
+ TitanVertex resourceVertex, TitanVertex requirementVertex) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), reqName);
+ TitanOperationStatus createRelResult = titanGenericDao.createEdge(resourceVertex, requirementVertex,
+ GraphEdgeLabels.REQUIREMENT, props);
+ log.debug("After creatin edge between resource {} to requirement {}", resourceId, requirementVertex);
+ if (!createRelResult.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate resource {} to requirement {} in graph. status is " + resourceId, reqName,
+ createRelResult);
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private RequirementData buildRequirementData(String resourceId, String reqName,
+ RequirementDefinition reqDefinition) {
+
+ RequirementData requirementData = new RequirementData();
+ requirementData.setNode(reqDefinition.getNode());
+ requirementData.setUniqueId(UniqueIdBuilder.buildRequirementUid(resourceId, reqName));
+ Long creationTime = System.currentTimeMillis();
+ requirementData.setCreationTime(creationTime);
+ requirementData.setModificationTime(creationTime);
+ requirementData.setRelationshipType(reqDefinition.getRelationship());
+ requirementData.setMinOccurrences(reqDefinition.getMinOccurrences());
+ requirementData.setMaxOccurrences(reqDefinition.getMaxOccurrences());
+
+ return requirementData;
+ }
+
+ /**
+ * build requirement impl node associate it to resource, requirement &
+ * implementation resource
+ *
+ * [RESOURCE] --> [REQUIREMENT IMPL] --> [ RESOURCE IMPL ] | V [REQUIREMENT]
+ *
+ * @param resourceLabel
+ * @param resourceId
+ * @param reqName
+ * @param requirementUid
+ * @param reqImplDefinition
+ * @return
+ */
+ private Either<RequirementImplData, TitanOperationStatus> addRequirementImplData(NodeTypeEnum resourceLabel,
+ String resourceId, String reqName, String requirementUid, RequirementImplDef reqImplDefinition) {
+
+ RequirementImplData requirementImplData = buildRequirementImplData(resourceId, reqName, reqImplDefinition);
+
+ log.debug("Before adding requirement impl data to graph " + requirementImplData);
+ Either<RequirementImplData, TitanOperationStatus> createNodeResult = titanGenericDao
+ .createNode(requirementImplData, RequirementImplData.class);
+ log.debug("After adding requirement to graph " + requirementImplData + ". status is " + createNodeResult);
+ if (createNodeResult.isRight()) {
+ TitanOperationStatus operationStatus = createNodeResult.right().value();
+ log.error("Failed to add requirement " + reqName + " [ " + requirementImplData + " ] "
+ + " to graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelResult = associateReqImplRoResource(resourceLabel,
+ resourceId, reqName, requirementImplData);
+ if (createRelResult.isRight()) {
+ TitanOperationStatus operationStatus = createRelResult.right().value();
+ log.error("Failed to associate resource " + resourceId + " to requirement impl " + requirementImplData
+ + "[ " + requirementImplData + "] in graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> associateToResourceImpl = associateReqImplToImplResource(
+ requirementImplData, reqImplDefinition.getNodeId());
+ if (associateToResourceImpl.isRight()) {
+ TitanOperationStatus operationStatus = associateToResourceImpl.right().value();
+ log.error("Failed to associate requirement impl " + requirementImplData + " to resource impl "
+ + reqImplDefinition.getNodeId() + "[ " + requirementImplData + "] in graph. status is "
+ + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> associateToRequirement = associateReqImplToRequirement(
+ requirementImplData, requirementUid);
+ if (associateToRequirement.isRight()) {
+ TitanOperationStatus operationStatus = associateToRequirement.right().value();
+ log.error("Failed to associate requirement impl " + requirementImplData + " to requirement " + reqName
+ + " in graph. status is " + operationStatus);
+ return Either.right(operationStatus);
+ }
+
+ return Either.left(createNodeResult.left().value());
+
+ }
+
+ private RequirementImplData buildRequirementImplData(String resourceId, String reqName,
+ RequirementImplDef reqImplDefinition) {
+ String reqImplUid = UniqueIdBuilder.buildRequirementImplUid(resourceId, reqName);
+ RequirementImplData requirementImplData = new RequirementImplData();
+ requirementImplData.setName(reqName);
+ requirementImplData.setUniqueId(reqImplUid);
+ Long creationTime = System.currentTimeMillis();
+ requirementImplData.setCreationTime(creationTime);
+ requirementImplData.setModificationTime(creationTime);
+ Point point = reqImplDefinition.getPoint();
+ if (point != null) {
+ requirementImplData.setPosX(point.getX());
+ requirementImplData.setPosY(point.getY());
+ }
+ return requirementImplData;
+ }
+
+ /**
+ * associate requirement impl node to the source requirement. The source
+ * requirement maybe belongs to one of parents.
+ *
+ * @param requirementImplData
+ * @param requirementUid
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateReqImplToRequirement(
+ RequirementImplData requirementImplData, String requirementUid) {
+
+ UniqueIdData to = new UniqueIdData(NodeTypeEnum.Requirement, requirementUid);
+ log.debug("Before creating edge between requirement impl " + requirementImplData + " to requirement "
+ + requirementUid);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao
+ .createRelation(requirementImplData, to, GraphEdgeLabels.IMPLEMENTATION_OF, null);
+ log.debug("Before creating edge between requirement impl " + requirementImplData + " to requirement "
+ + requirementUid + ". status is " + createRelResult);
+
+ return createRelResult;
+ }
+
+ /**
+ * Associate requirement impl node to the node which supply this
+ * requirement.
+ *
+ * @param requirementImplData
+ * @param nodeId
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateReqImplToImplResource(
+ RequirementImplData requirementImplData, String nodeId) {
+
+ UniqueIdData nodeImpl = new UniqueIdData(NodeTypeEnum.Resource, nodeId);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), nodeId);
+ log.debug("Before creating edge between requirement impl " + requirementImplData + " to node impl " + nodeId);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao
+ .createRelation(requirementImplData, nodeImpl, GraphEdgeLabels.NODE_IMPL, props);
+ log.debug("After creating edge between requirement " + requirementImplData + " to node impl " + nodeId
+ + ". status is " + createRelResult);
+
+ return createRelResult;
+ }
+
+ /**
+ * create an edge between the requirement impl node to the implementation
+ * resource.
+ *
+ * @param resourceLabel
+ * @param resourceId
+ * @param reqName
+ * @param requirementImplData
+ * @return
+ */
+ private Either<GraphRelation, TitanOperationStatus> associateReqImplRoResource(NodeTypeEnum resourceLabel,
+ String resourceId, String reqName, RequirementImplData requirementImplData) {
+
+ UniqueIdData resource = new UniqueIdData(resourceLabel, resourceId);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), reqName);
+ log.debug(
+ "Before creating edge between resource " + resourceId + " to requirement impl " + requirementImplData);
+ Either<GraphRelation, TitanOperationStatus> createRelResult = titanGenericDao.createRelation(resource,
+ requirementImplData, GraphEdgeLabels.REQUIREMENT_IMPL, props);
+ log.debug("After creating edge between to requirement impl " + requirementImplData + " to resource " + resource
+ + ". status is " + createRelResult);
+
+ return createRelResult;
+ }
+
+ private void validateNodeExists(String node) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource(String reqName,
+ RequirementDefinition reqDefinition, String resourceId, boolean inTransaction) {
+
+ Either<RequirementDefinition, StorageOperationStatus> result = null;
+ try {
+
+ log.debug("Going to add requirement " + reqName + " to resource " + resourceId
+ + ". requirement definition is " + reqDefinition);
+
+ validateNodeExists(reqDefinition.getNode());
+
+ // 1. add requirement node in graph and associate it to the resource
+ log.debug("Going to add requirement node in graph and associate it to the resource");
+ Either<RequirementData, TitanOperationStatus> addRequirementData = addRequirementData(resourceId, reqName,
+ reqDefinition);
+ if (addRequirementData.isRight()) {
+ log.error("Failed to add requirement " + reqName + " node to graph. status is " + addRequirementData);
+ result = Either.right(
+ DaoStatusConverter.convertTitanStatusToStorageStatus(addRequirementData.right().value()));
+ return result;
+ }
+
+ RequirementData requirementData = addRequirementData.left().value();
+
+ log.debug("Going to associate the requirement to the appriopriate capability type");
+ Either<GraphRelation, TitanOperationStatus> associateReqToCapabilityType = associateRequirementToCapabilityType(
+ requirementData, reqDefinition);
+ if (associateReqToCapabilityType.isRight()) {
+ log.error("Failed to associate requirement data node " + requirementData
+ + " to the capability type node " + reqDefinition.getCapability());
+ result = Either.right(DaoStatusConverter
+ .convertTitanStatusToStorageStatus(associateReqToCapabilityType.right().value()));
+ return result;
+ }
+
+ // TODO: esofer associate requirement to the relationship type
+ /*
+ * Either<GraphRelation, TitanOperationStatus>
+ * associateReqToRelshipType =
+ * associateRequirementToRelationshipType( requirementData,
+ * reqDefinition);
+ *
+ * if (associateReqToRelshipType.isRight() &&
+ * associateReqToRelshipType.right().value() !=
+ * TitanOperationStatus.NOT_FOUND) {
+ * log.error("Failed to associate requirement data node " +
+ * requirementData + " to the relationship type node " +
+ * reqDefinition.getRelationship()); result = Either
+ * .right(TitanStatusConverter
+ * .convertTitanStatusToStorageStatus(associateReqToRelshipType
+ * .right().value())); return result; }
+ */
+
+ log.debug("Going to fetch the requirement " + reqName + " from graph");
+ Either<RequirementDefinition, TitanOperationStatus> requirementDefinitionRes = getRequirement(
+ requirementData.getUniqueId());
+ if (requirementDefinitionRes.isRight()) {
+ result = Either.right(
+ DaoStatusConverter.convertTitanStatusToStorageStatus(requirementDefinitionRes.right().value()));
+ return result;
+ }
+
+ result = Either.left(requirementDefinitionRes.left().value());
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public StorageOperationStatus addRequirementToResource(TitanVertex metadataVertex, String reqName,
+ RequirementDefinition reqDefinition, String resourceId, boolean inTransaction) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ try {
+
+ log.debug("Going to add requirement {} to resource . requirement definition is ", reqName, resourceId,
+ reqDefinition);
+
+ validateNodeExists(reqDefinition.getNode());
+
+ // 1. add requirement node in graph and associate it to the resource
+ log.debug("Going to add requirement node in graph and associate it to the resource");
+ Either<TitanVertex, TitanOperationStatus> addRequirementData = addRequirementData(metadataVertex,
+ resourceId, reqName, reqDefinition);
+ if (addRequirementData.isRight()) {
+ log.error("Failed to add requirement {} node to graph. status is {}", reqName,
+ addRequirementData.right().value());
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(addRequirementData.right().value());
+ return result;
+ }
+
+ log.debug("Going to associate the requirement to the appriopriate capability type");
+ TitanOperationStatus associateReqToCapabilityType = associateRequirementToCapabilityType(
+ addRequirementData.left().value(), reqDefinition);
+ if (!associateReqToCapabilityType.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate requirement data node {} to the capability type node {}" + reqDefinition,
+ reqDefinition.getCapability());
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(associateReqToCapabilityType);
+ return result;
+ }
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || !result.equals(TitanOperationStatus.OK)) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Fetch requirement from graph
+ *
+ * @param uniqueId
+ * - the uniqueid of the requirement in the graph
+ * @return
+ */
+ public Either<RequirementDefinition, TitanOperationStatus> getRequirement(String uniqueId) {
+
+ log.debug("Going to fetch the requirement {} from graph.", uniqueId);
+ Either<RequirementData, TitanOperationStatus> reqDataResult = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement), uniqueId, RequirementData.class);
+
+ if (reqDataResult.isRight()) {
+ log.error("Failed to find requirement node in graph " + uniqueId + ". status is " + reqDataResult);
+ return Either.right(reqDataResult.right().value());
+ }
+
+ log.debug("Going to fetch the capability type associate to requirement {}", uniqueId);
+ Either<ImmutablePair<CapabilityTypeData, GraphEdge>, TitanOperationStatus> capabilityTypeRes = titanGenericDao
+ .getChild(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueId, GraphEdgeLabels.CAPABILITY_TYPE,
+ NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
+
+ if (capabilityTypeRes.isRight()) {
+ log.error("Cannot find the capability of a given requirement " + uniqueId + ". status is "
+ + capabilityTypeRes);
+ return Either.right(capabilityTypeRes.right().value());
+ }
+
+ ImmutablePair<CapabilityTypeData, GraphEdge> capability = capabilityTypeRes.left().value();
+
+ String capabilityType = capability.getKey().getCapabilityTypeDataDefinition().getType();
+
+ // TODO: esofer add relationship as edge
+ /*
+ * Either<List<ImmutablePair<RelationshipTypeData, GraphEdge>>,
+ * TitanOperationStatus> relationshipRes = titanGenericDao
+ * .getChildrenNodes( GraphPropertiesDictionary.UNIQUE_ID.getProperty(),
+ * uniqueId, GraphEdgeLabels.RELATIONSHIP_TYPE,
+ * NodeTypeEnum.RelationshipType, RelationshipTypeData.class);
+ *
+ * if (relationshipRes.isRight() && relationshipRes.right().value() !=
+ * TitanOperationStatus.NOT_FOUND) {
+ * log.error("Cannot find the capability of a given requirement " +
+ * uniqueId + ". status is " + capabilityTypesRes); return
+ * Either.right(relationshipRes.right().value()); }
+ *
+ * String relationshipType = null; if (relationshipRes.isLeft()) {
+ * List<ImmutablePair<RelationshipTypeData, GraphEdge>> rstPairs =
+ * relationshipRes .left().value(); if (rstPairs == null || true ==
+ * rstPairs.isEmpty()) { log.error(
+ * "Cannot find the capability of a given requirement " + uniqueId);
+ * return Either.right(TitanOperationStatus.NOT_FOUND); }
+ *
+ * ImmutablePair<RelationshipTypeData, GraphEdge> relationship =
+ * rstPairs .get(0); relationshipType = relationship.getKey().getType();
+ * }
+ */
+
+ log.debug("Going to fetch the capability type associate to requirement {}", uniqueId);
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao
+ .getParentNode(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueId, GraphEdgeLabels.REQUIREMENT,
+ NodeTypeEnum.Resource, ResourceMetadataData.class);
+ if (parentNode.isRight()) {
+ log.error("Cannot find the parent resource for a given requirement " + uniqueId + ". status is "
+ + parentNode.right().value());
+ return Either.right(parentNode.right().value());
+ }
+
+ RequirementData requirementData = reqDataResult.left().value();
+
+ RequirementDefinition requirementDefinition = new RequirementDefinition();
+ requirementDefinition.setOwnerId(parentNode.left().value().getLeft().getMetadataDataDefinition().getUniqueId());
+ requirementDefinition.setNode(requirementData.getNode());
+ requirementDefinition.setUniqueId(requirementData.getUniqueId());
+ requirementDefinition.setCapability(capabilityType);
+ requirementDefinition.setRelationship(requirementData.getRelationshipType());
+ requirementDefinition.setMinOccurrences(requirementData.getMinOccurrences());
+ requirementDefinition.setMaxOccurrences(requirementData.getMaxOccurrences());
+
+ return Either.left(requirementDefinition);
+
+ }
+
+ @Override
+ public Either<RequirementDefinition, StorageOperationStatus> getRequirementOfResource(String reqName,
+ String resourceId) {
+
+ return getRequirementOfResource(reqName, resourceId, false);
+ }
+
+ @Override
+ public Either<RequirementDefinition, StorageOperationStatus> getRequirementOfResource(String reqName,
+ String resourceId, boolean inTransaction) {
+
+ Either<RequirementDefinition, StorageOperationStatus> result = null;
+
+ try {
+ String reqUniqueId = UniqueIdBuilder.buildRequirementUid(resourceId, reqName);
+ Either<RequirementDefinition, TitanOperationStatus> requirementRes = getRequirement(reqUniqueId);
+
+ if (requirementRes.isRight()) {
+ log.debug("Failed to retrieve requirement " + reqName + " associated to resource " + resourceId);
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(requirementRes.right().value()));
+ } else {
+ result = Either.left(requirementRes.left().value());
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementImplToResource(String reqName,
+ RequirementImplDef reqDefinition, String resourceId, String parentReqUniqueId) {
+
+ return addRequirementImplToResource(reqName, reqDefinition, resourceId, parentReqUniqueId, false);
+
+ }
+
+ @Override
+ public Either<RequirementDefinition, StorageOperationStatus> addRequirementImplToResource(String reqName,
+ RequirementImplDef reqImplDefinition, String resourceId, String parentReqUniqueId, boolean inTransaction) {
+
+ Either<RequirementDefinition, StorageOperationStatus> result = null;
+
+ try {
+
+ // find the requirement defined at the resource itself or under one
+ // of its parents
+ Either<RequirementDefinition, TitanOperationStatus> findReq = getRequirement(parentReqUniqueId);
+ log.debug("After looking for requirement " + parentReqUniqueId + ". status is " + findReq);
+ if (findReq.isRight()) {
+ TitanOperationStatus status = findReq.right().value();
+ log.error("The requirment " + parentReqUniqueId + " was not found in the graph. status is "
+ + findReq.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ RequirementDefinition reqDefinition = findReq.left().value();
+ String reqNode = reqDefinition.getNode();
+ String reqCapability = reqDefinition.getCapability();
+
+ String nodeIdImpl = reqImplDefinition.getNodeId();
+
+ checkNodeIdImplementsRequirementNode(nodeIdImpl, reqNode);
+
+ Either<RequirementImplData, TitanOperationStatus> addRequirementImplData = addRequirementImplData(
+ NodeTypeEnum.Resource, resourceId, reqName, parentReqUniqueId, reqImplDefinition);
+
+ if (addRequirementImplData.isRight()) {
+ TitanOperationStatus status = addRequirementImplData.right().value();
+ log.error("Failed to add requirement data impl node in the graph. status is "
+ + addRequirementImplData.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ RequirementImplData requirementImplData = addRequirementImplData.left().value();
+
+ log.debug("Add the properties of the capabilities of the target node " + nodeIdImpl
+ + " to the requirement impl node " + requirementImplData.getUniqueId() + " in graph.");
+ Map<String, CapabiltyInstance> requirementPropertiesPerCapability = reqImplDefinition
+ .getRequirementProperties();
+ TitanOperationStatus addPropsResult = addCapabilityPropertiesToReqImpl(requirementImplData, reqCapability,
+ nodeIdImpl, requirementPropertiesPerCapability);
+
+ if (addPropsResult != TitanOperationStatus.OK) {
+ log.error("Failed to add capabilities properties to Requirement impl " + requirementImplData);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addPropsResult));
+ return result;
+ }
+
+ result = Either.left(reqDefinition);
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Either<RequirementImplDef, TitanOperationStatus> getRequirementImplOfResource(String reqName,
+ String resourceId) {
+
+ RequirementImplDef requirementImplDef = new RequirementImplDef();
+
+ Either<List<ImmutablePair<RequirementImplData, GraphEdge>>, TitanOperationStatus> reqImplNodesRes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId,
+ GraphEdgeLabels.REQUIREMENT_IMPL, NodeTypeEnum.RequirementImpl, RequirementImplData.class);
+ log.debug("After looking for requirement impl edge of resource " + resourceId);
+ if (reqImplNodesRes.isRight()) {
+ TitanOperationStatus status = reqImplNodesRes.right().value();
+ return Either.right(status);
+ }
+
+ boolean found = false;
+ List<ImmutablePair<RequirementImplData, GraphEdge>> reqImplNodes = reqImplNodesRes.left().value();
+ for (ImmutablePair<RequirementImplData, GraphEdge> entry : reqImplNodes) {
+ GraphEdge graphEdge = entry.getValue();
+ String edgeType = (String) graphEdge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
+ if (reqName.equals(edgeType)) {
+ found = true;
+ RequirementImplData requirementImplData = entry.getKey();
+
+ requirementImplDef.setUniqueId(requirementImplData.getUniqueId());
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> nodeImplRes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.RequirementImpl),
+ requirementImplData.getUniqueId(), GraphEdgeLabels.NODE_IMPL, NodeTypeEnum.Resource,
+ ResourceMetadataData.class);
+
+ if (nodeImplRes.isRight()) {
+ TitanOperationStatus status = nodeImplRes.right().value();
+ log.debug("No implementation resource was found under requirement impl "
+ + requirementImplData.getUniqueId() + ". status is " + status);
+
+ return Either.right(status);
+ }
+ String nodeImpl = nodeImplRes.left().value().getKey().getMetadataDataDefinition().getUniqueId();
+ requirementImplDef.setNodeId(nodeImpl);
+
+ String posX = requirementImplData.getPosX();
+ String posY = requirementImplData.getPosY();
+ if (posX != null && posY != null) {
+ Point point = new Point(posX, posY);
+ requirementImplDef.setPoint(point);
+ }
+
+ Either<List<ImmutablePair<CapabilityInstData, GraphEdge>>, TitanOperationStatus> capaInstDataRes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.RequirementImpl),
+ requirementImplData.getUniqueId(), GraphEdgeLabels.CAPABILITY_INST,
+ NodeTypeEnum.CapabilityInst, CapabilityInstData.class);
+ if (capaInstDataRes.isRight()) {
+ TitanOperationStatus status = capaInstDataRes.right().value();
+ log.debug("No capability instance was found under requirement impl "
+ + requirementImplData.getUniqueId() + ". status is " + status);
+
+ return Either.right(status);
+ }
+
+ Map<String, CapabiltyInstance> requirementProperties = new HashMap<String, CapabiltyInstance>();
+
+ List<ImmutablePair<CapabilityInstData, GraphEdge>> list = capaInstDataRes.left().value();
+ for (ImmutablePair<CapabilityInstData, GraphEdge> capabilityInst : list) {
+ CapabilityInstData capabilityInstData = capabilityInst.getKey();
+ GraphEdge edge = capabilityInst.getValue();
+ Map<String, Object> properties = edge.getProperties();
+ if (properties == null) {
+ log.error("Cannot find the property " + GraphPropertiesDictionary.NAME.getProperty()
+ + " on the edge " + edge);
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+ String capabilityName = (String) properties.get(GraphPropertiesDictionary.NAME.getProperty());
+ if (capabilityName == null) {
+ log.error("Cannot find the property " + GraphPropertiesDictionary.NAME.getProperty()
+ + " on the edge " + edge);
+ return Either.right(TitanOperationStatus.INVALID_ELEMENT);
+ }
+
+ // List<String> keyValuePropertiesList = capabilityInstData
+ // .getProperties();
+ // Map<String, String> actualValues = new HashMap<String,
+ // String>();
+ // fillMapFromKeyValueList(keyValuePropertiesList,
+ // actualValues);
+ CapabiltyInstance capabiltyInstance = new CapabiltyInstance();
+ capabiltyInstance.setUniqueId(capabilityInstData.getUniqueId());
+ // capabiltyInstance.setProperties(actualValues);
+ requirementProperties.put(capabilityName, capabiltyInstance);
+
+ Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, TitanOperationStatus> propertyValueNodesRes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityInst),
+ capabilityInstData.getUniqueId(), GraphEdgeLabels.PROPERTY_VALUE,
+ NodeTypeEnum.PropertyValue, PropertyValueData.class);
+
+ if (propertyValueNodesRes.isRight()) {
+ TitanOperationStatus status = propertyValueNodesRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find the property values of capability instance " + capabilityInstData
+ + ". status is " + status);
+ return Either.right(status);
+ }
+ } else {
+ List<ImmutablePair<PropertyValueData, GraphEdge>> propertyValueNodes = propertyValueNodesRes
+ .left().value();
+
+ if (propertyValueNodes != null) {
+
+ Map<String, String> actualValues = new HashMap<String, String>();
+ TitanOperationStatus fillPropertiesResult = fillPropertiesMapFromNodes(propertyValueNodes,
+ actualValues);
+
+ if (fillPropertiesResult != TitanOperationStatus.OK) {
+ log.error("Failed to fetch properties of capability " + capabilityName);
+ return Either.right(fillPropertiesResult);
+ }
+
+ if (false == actualValues.isEmpty()) {
+ capabiltyInstance.setProperties(actualValues);
+ }
+ }
+ }
+
+ }
+
+ requirementImplDef.setRequirementProperties(requirementProperties);
+
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if (false == found) {
+ log.debug("Cannot find requirement impl under resource " + resourceId);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ return Either.left(requirementImplDef);
+ }
+
+ private void fillMapFromKeyValueList(List<String> keyValuePropertiesList, Map<String, String> actualValues) {
+
+ if (keyValuePropertiesList != null) {
+ for (String keyValue : keyValuePropertiesList) {
+ int equalSignLocation = keyValue.indexOf(EQUAL_SIGN);
+ if (equalSignLocation > -1) {
+ String key = keyValue.substring(0, equalSignLocation);
+ String value = EMPTY_STRING;
+ if (equalSignLocation + 1 < keyValue.length()) {
+ value = keyValue.substring(equalSignLocation + 1);
+ }
+ actualValues.put(key, value);
+ }
+ }
+ }
+
+ }
+
+ private TitanOperationStatus fillPropertiesMapFromNodes(
+ List<ImmutablePair<PropertyValueData, GraphEdge>> propertyValueNodes, Map<String, String> actualValues) {
+ if (propertyValueNodes != null) {
+ for (ImmutablePair<PropertyValueData, GraphEdge> propertyValuePair : propertyValueNodes) {
+ PropertyValueData propertyValueData = propertyValuePair.getKey();
+ GraphEdge propertyValueEdge = propertyValuePair.getValue();
+ Map<String, Object> propertyEdgeProps = propertyValueEdge.getProperties();
+ if (propertyEdgeProps == null) {
+ log.error("Cannot find the property " + GraphPropertiesDictionary.NAME.getProperty()
+ + " on the edge " + propertyValueEdge);
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+ String paramName = (String) propertyEdgeProps.get(GraphPropertiesDictionary.NAME.getProperty());
+ if (paramName == null) {
+ log.error("Cannot find the property " + GraphPropertiesDictionary.NAME.getProperty()
+ + " on the edge " + propertyValueEdge);
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+ actualValues.put(paramName, propertyValueData.getValue());
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus addCapabilityPropertiesToReqImpl(RequirementImplData reqImplData, String reqCapability,
+ String nodeIdImpl, Map<String, CapabiltyInstance> propertiesValuePerCapability) {
+
+ TitanOperationStatus result = null;
+
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> allCapabilities = capabilityOperation
+ .getAllCapabilitiesPairs(nodeIdImpl);
+ log.trace("Atter looking for the capabilities of resource " + nodeIdImpl + ". result is " + allCapabilities);
+ if (allCapabilities.isRight()) {
+ TitanOperationStatus status = allCapabilities.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to find capabilities of resource " + nodeIdImpl + ". status is " + status);
+ return status;
+ }
+ } else {
+
+ List<ImmutablePair<CapabilityData, GraphEdge>> capabilitiesValue = allCapabilities.left().value();
+ checkImplNodeContainsReqCapability(reqCapability, capabilitiesValue);
+
+ for (ImmutablePair<CapabilityData, GraphEdge> entry : capabilitiesValue) {
+
+ CapabilityData capabilityData = entry.getKey();
+
+ GraphEdge graphEdge = entry.getValue();
+
+ Either<String, TitanOperationStatus> capabilityNameResult = findCapabilityName(capabilityData,
+ graphEdge);
+
+ if (capabilityNameResult.isRight()) {
+ TitanOperationStatus status = capabilityNameResult.right().value();
+ log.error(
+ "Failed to find capability name from the edge associated to capability " + capabilityData);
+ return status;
+ }
+
+ String capabilityName = capabilityNameResult.left().value();
+ log.debug("Going to set properties of capability " + capabilityName);
+ String cabilityDataUid = capabilityData.getUniqueId();
+
+ Either<CapabilityTypeData, TitanOperationStatus> ctDataResult = capabilityOperation
+ .getCapabilityTypeOfCapability(cabilityDataUid);
+
+ if (ctDataResult.isRight()) {
+ log.error("Cannot find capability type of capbility " + cabilityDataUid + ". status is "
+ + ctDataResult);
+ TitanOperationStatus status = ctDataResult.right().value();
+ return status;
+ }
+
+ CapabilityTypeData capabilityTypeData = ctDataResult.left().value();
+
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> propertiesStatus = findPropertiesOfCapability(
+ capabilityTypeData);
+ if (propertiesStatus.isRight()) {
+ TitanOperationStatus status = propertiesStatus.right().value();
+ log.error("Failed to fetch properties definitions from capability. status is " + status);
+ return status;
+ }
+
+ Map<String, PropertyDefinition> properties = propertiesStatus.left().value();
+
+ CapabiltyInstance capabiltyInstance = null;
+ if (propertiesValuePerCapability != null) {
+ capabiltyInstance = propertiesValuePerCapability.get(capabilityName);
+ }
+
+ Either<CapabilityInstData, TitanOperationStatus> createCapabilityInstanceNode = createCapabilityInstanceNode(
+ capabilityName, reqImplData);
+ if (createCapabilityInstanceNode.isRight()) {
+ TitanOperationStatus status = createCapabilityInstanceNode.right().value();
+ log.error("Failed to create capability instance node (" + capabilityName + ") in graph. status is "
+ + status);
+
+ return status;
+ }
+ CapabilityInstData capabilityInstData = createCapabilityInstanceNode.left().value();
+
+ Either<List<GraphRelation>, TitanOperationStatus> instanceProperties = addPropertiesToCapabilityInstance(
+ properties, capabiltyInstance, capabilityInstData);
+
+ if (instanceProperties.isRight()) {
+ TitanOperationStatus status = instanceProperties.right().value();
+ log.debug("Failed to add properties to capability instance. status is " + status);
+ return status;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> associateCapabilityInstToCapabilityType = associateCapabilityInstToCapabilityType(
+ capabilityInstData, capabilityTypeData);
+ if (associateCapabilityInstToCapabilityType.isRight()) {
+ TitanOperationStatus status = associateCapabilityInstToCapabilityType.right().value();
+ log.error("Failed to associate capability instance " + capabilityInstData
+ + " to capability type node " + capabilityTypeData + " in graph. status is " + status);
+
+ return status;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> associateCapabilityInst = associateRequirementImplToCapabilityInst(
+ reqImplData, capabilityInstData, capabilityName);
+ if (associateCapabilityInst.isRight()) {
+ TitanOperationStatus status = associateCapabilityInst.right().value();
+ log.error("Failed to associate requirement impl " + reqImplData + " to capability instance node "
+ + capabilityInstData + " of capability " + capabilityName + ") in graph. status is "
+ + status);
+
+ return status;
+ }
+
+ }
+ result = TitanOperationStatus.OK;
+ }
+ return result;
+ }
+
+ private Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfCapability(
+ CapabilityTypeData capabilityTypeData) {
+ String capabilityTypeUid = capabilityTypeData.getUniqueId();
+
+ Either<CapabilityTypeDefinition, TitanOperationStatus> capabilityTypeResult = capabilityTypeOperation
+ .getCapabilityTypeByUid(capabilityTypeUid);
+
+ if (capabilityTypeResult.isRight()) {
+ log.error("Failed to find capabilityType " + capabilityTypeUid + " in the graph. status is "
+ + capabilityTypeResult);
+ return Either.right(capabilityTypeResult.right().value());
+ }
+
+ CapabilityTypeDefinition capabilityTypeDef = capabilityTypeResult.left().value();
+ Map<String, PropertyDefinition> properties = capabilityTypeDef.getProperties();
+
+ return Either.left(properties);
+ }
+
+ private Either<String, TitanOperationStatus> findCapabilityName(CapabilityData capabilityData,
+ GraphEdge graphEdge) {
+ Map<String, Object> edgeProps = graphEdge.getProperties();
+ String capabilityName = (String) edgeProps.get(GraphPropertiesDictionary.NAME.getProperty());
+
+ if (capabilityName == null) {
+ log.debug("Cannot find the name of the capability associated to node " + capabilityData);
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+ return Either.left(capabilityName);
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateCapabilityInstToCapabilityType(
+ CapabilityInstData capabilityInstData, CapabilityTypeData capabilityTypeData) {
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(capabilityInstData,
+ capabilityTypeData, GraphEdgeLabels.INSTANCE_OF, null);
+
+ return createRelation;
+
+ }
+
+ /**
+ * add property value node with default value of override value and
+ * associate it to the capability instance node
+ *
+ * @param properties
+ * - properties definition. old also default value
+ * @param capabilityInstance
+ * - hold also properties new value(if exists)
+ * @param capabilityInstData
+ * - the graph node which we associate the properties value node
+ * to.
+ * @return
+ */
+ private Either<List<GraphRelation>, TitanOperationStatus> addPropertiesToCapabilityInstance(
+ Map<String, PropertyDefinition> properties, CapabiltyInstance capabilityInstance,
+ CapabilityInstData capabilityInstData) {
+
+ List<GraphRelation> relationsResult = new ArrayList<GraphRelation>();
+
+ if (properties != null) {
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+
+ String paramName = entry.getKey();
+
+ PropertyDefinition propertyDefinition = entry.getValue();
+
+ String propertyValue = setPropertyValue(capabilityInstance, paramName, propertyDefinition);
+
+ PropertyValueData propertyValueData = buildPropertyValueData(capabilityInstData.getUniqueId(),
+ paramName, propertyValue);
+
+ log.debug("Before creating property value data node " + propertyValueData + " in graph.");
+ Either<PropertyValueData, TitanOperationStatus> createNode = titanGenericDao
+ .createNode(propertyValueData, PropertyValueData.class);
+ log.debug("Before creating property value data node " + propertyValueData + " in graph. status is "
+ + createNode);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ log.error("Failed to create property value node in graph " + propertyValueData + ". status is "
+ + status);
+ return Either.right(status);
+ }
+
+ PropertyValueData propertyValueDataCreated = createNode.left().value();
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = associateCapabilityInstToPropertyValue(
+ capabilityInstData, paramName, propertyValueDataCreated);
+
+ if (createRelation.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ log.error("Failed to create relation between capability instance "
+ + capabilityInstData.getUniqueId() + " to property value "
+ + propertyValueDataCreated.getUniqueId() + " in graph. status is " + status);
+ return Either.right(status);
+ }
+
+ relationsResult.add(createRelation.left().value());
+
+ }
+ }
+
+ return Either.left(relationsResult);
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> associateCapabilityInstToPropertyValue(
+ CapabilityInstData capabilityInstData, String paramName, PropertyValueData propertyValueDataCreated) {
+
+ Map<String, Object> edgeProps = new HashMap<String, Object>();
+ edgeProps.put(GraphPropertiesDictionary.NAME.getProperty(), paramName);
+ log.debug("Begin creating relation between capability instance " + capabilityInstData + " to property value "
+ + propertyValueDataCreated + " in graph.");
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(capabilityInstData,
+ propertyValueDataCreated, GraphEdgeLabels.PROPERTY_VALUE, edgeProps);
+ log.debug("After creating relation between capability instance " + capabilityInstData + " to property value "
+ + propertyValueDataCreated + " in graph. status is " + createRelation);
+
+ return createRelation;
+ }
+
+ private String setPropertyValue(CapabiltyInstance capabilityInstance, String paramName,
+ PropertyDefinition propertyDefinition) {
+ String propertyValue = NA;
+ if (propertyDefinition.getDefaultValue() != null) {
+ propertyValue = propertyDefinition.getDefaultValue();
+ }
+ Map<String, String> propertiesValue = null;
+ if (capabilityInstance != null) {
+ propertiesValue = capabilityInstance.getProperties();
+ if (propertiesValue != null) {
+ String tmpValue = propertiesValue.get(paramName);
+ if (tmpValue != null) {
+ propertyValue = tmpValue;
+ }
+ }
+ }
+ return propertyValue;
+ }
+
+ private String buildPropertykeyValue(String paramName, String paramValue) {
+ return paramName + EQUAL_SIGN + paramValue;
+ }
+
+ private PropertyValueData buildPropertyValueData(String capabilityInstDataUid, String paramName,
+ String propertyValue) {
+ PropertyValueData propertyValueData = new PropertyValueData();
+ propertyValueData.setValue(propertyValue);
+ String uid = UniqueIdBuilder.buildPropertyValueUniqueId(capabilityInstDataUid, paramName);
+ propertyValueData.setUniqueId(uid);
+ Long creationDate = System.currentTimeMillis();
+ propertyValueData.setCreationTime(creationDate);
+ propertyValueData.setModificationTime(creationDate);
+ return propertyValueData;
+ }
+
+ private Either<CapabilityInstData, TitanOperationStatus> createCapabilityInstanceNode(String capabilityName,
+ RequirementImplData reqImplData) {
+
+ CapabilityInstData capabilityInstData = new CapabilityInstData();
+ String uniqueId = UniqueIdBuilder.buildCapabilityInstanceUid(reqImplData.getUniqueId(), capabilityName);
+
+ capabilityInstData.setUniqueId(uniqueId);
+ // capabilityInstData.setProperties(instanceProperties);
+ Long creationDate = System.currentTimeMillis();
+ capabilityInstData.setCreationTime(creationDate);
+ capabilityInstData.setModificationTime(creationDate);
+
+ log.debug("Before creating capability instance node in graph " + capabilityInstData);
+ Either<CapabilityInstData, TitanOperationStatus> createNode = titanGenericDao.createNode(capabilityInstData,
+ CapabilityInstData.class);
+ log.debug(
+ "After creating capability instance node in graph " + capabilityInstData + ". status is " + createNode);
+
+ return createNode;
+ }
+
+ private void checkNodeIdImplementsRequirementNode(String nodeIdImpl, String reqNode) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private void checkImplNodeContainsReqCapability(String reqCapability,
+ List<ImmutablePair<CapabilityData, GraphEdge>> capabilitiesValue) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Either<Map<String, List<RequirementDefinition>>, StorageOperationStatus> getAllRequirementsOfResourceOnly(
+ String resourceId, boolean inTransaction) {
+
+ Either<Map<String, List<RequirementDefinition>>, StorageOperationStatus> result = null;
+
+ try {
+
+ Map<String, RequirementDefinition> requirements = new HashMap<String, RequirementDefinition>();
+ Set<String> caseInsensitiveReqNames = new HashSet<>();
+ TitanOperationStatus status = findAllRequirementsNonRecursive(resourceId, requirements,
+ caseInsensitiveReqNames);
+
+ if (status != TitanOperationStatus.OK) {
+ log.error("Failed to get all requirements of resource " + resourceId + ". status is " + status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ } else {
+ // TODO handle requirementImpl
+ result = Either.left(convertRequirementMap(requirements, null, null));
+ }
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ @Override
+ public Either<Map<String, RequirementDefinition>, TitanOperationStatus> getResourceRequirements(String resourceId) {
+
+ Either<Map<String, RequirementDefinition>, TitanOperationStatus> result = null;
+
+ Map<String, RequirementDefinition> requirements = new HashMap<String, RequirementDefinition>();
+ Set<String> caseInsensitiveReqNames = new HashSet<>();
+
+ TitanOperationStatus status = findAllRequirementsRecursively(resourceId, requirements, caseInsensitiveReqNames);
+ if (status != TitanOperationStatus.OK) {
+ log.error("Failed to get all requirements of resource " + resourceId + ". status is " + status);
+ return Either.right(status);
+ } else {
+ log.debug("The requirements returned for resource {} are {}", resourceId, requirements);
+
+ if (requirements != null) {
+ for (Entry<String, RequirementDefinition> entry : requirements.entrySet()) {
+ String reqName = entry.getKey();
+ Either<RequirementImplDef, TitanOperationStatus> reqImplRes = this
+ .getRequirementImplOfResource(reqName, resourceId);
+ if (reqImplRes.isRight()) {
+
+ TitanOperationStatus reqImplResStatus = reqImplRes.right().value();
+ if (reqImplResStatus == TitanOperationStatus.NOT_FOUND) {
+ log.debug("Cannot find implementation of requirement {} under resource {}", reqName,
+ resourceId);
+ } else {
+ log.error("Cannot find implementation of requirement {} under resource {}", reqName,
+ resourceId);
+ return Either.right(reqImplResStatus);
+ }
+ } else {
+ RequirementDefinition requirementDefinition = entry.getValue();
+ // RequirementImplDef requirementImplDef =
+ // reqImplRes.left().value();
+ // requirementDefinition.setRequirementImpl(requirementImplDef);
+ }
+ }
+ }
+ log.debug("The requirements returned for resource {} after fetching requirement impl are {}", resourceId,
+ requirements);
+
+ result = Either.left(requirements);
+
+ return result;
+ }
+
+ }
+
+ @Override
+ public Either<Map<String, RequirementDefinition>, StorageOperationStatus> getAllResourceRequirements(
+ String resourceId, boolean inTransaction) {
+
+ Either<Map<String, RequirementDefinition>, StorageOperationStatus> result = null;
+
+ try {
+
+ Either<Map<String, RequirementDefinition>, TitanOperationStatus> internalResult = getResourceRequirements(
+ resourceId);
+ if (internalResult.isRight()) {
+ TitanOperationStatus status = internalResult.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to fetch requirements of resource {} . status is {}", resourceId, status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ Map<String, RequirementDefinition> value = internalResult.left().value();
+
+ result = Either.left(value);
+ return result;
+
+ // Map<String, RequirementDefinition> requirements = new
+ // HashMap<String, RequirementDefinition>();
+ // TitanOperationStatus status = findAllRequirementsRecursively(
+ // resourceId, requirements);
+ // if (status != TitanOperationStatus.OK) {
+ // log.error("Failed to get all requirements of resource "
+ // + resourceId + ". status is " + status);
+ // return Either.right(TitanStatusConverter
+ // .convertTitanStatusToStorageStatus(status));
+ // } else {
+ // log.debug("The requirements returned for resource "
+ // + resourceId + " are " + requirements);
+ //
+ // if (requirements != null) {
+ // for (Entry<String, RequirementDefinition> entry : requirements
+ // .entrySet()) {
+ // String reqName = entry.getKey();
+ // Either<RequirementImplDef, TitanOperationStatus> reqImplRes =
+ // this
+ // .getRequirementImplOfResource(reqName,
+ // resourceId);
+ // if (reqImplRes.isRight()) {
+ //
+ // TitanOperationStatus reqImplResStatus = reqImplRes
+ // .right().value();
+ // if (reqImplResStatus == TitanOperationStatus.NOT_FOUND) {
+ // log.warn("Cannot find implementation of requirement "
+ // + reqName
+ // + " under resource "
+ // + resourceId);
+ // } else {
+ // log.error("Cannot find implementation of requirement "
+ // + reqName
+ // + " under resource "
+ // + resourceId);
+ // return Either
+ // .right(TitanStatusConverter
+ // .convertTitanStatusToStorageStatus(reqImplResStatus));
+ // }
+ // } else {
+ // RequirementDefinition requirementDefinition = entry
+ // .getValue();
+ // RequirementImplDef requirementImplDef = reqImplRes
+ // .left().value();
+ // requirementDefinition
+ // .setRequirementImpl(requirementImplDef);
+ // }
+ // }
+ // }
+ // log.debug("The requirements returned for resource "
+ // + resourceId + " after fetching requirement impl are "
+ // + requirements);
+ //
+ // result = Either.left(requirements);
+ //
+ // return result;
+ // }
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<Map<String, RequirementDefinition>, StorageOperationStatus> getAllResourceRequirements(
+ String resourceId) {
+
+ return getAllResourceRequirements(resourceId, false);
+
+ }
+
+ public TitanOperationStatus findAllRequirementsRecursively(String resourceId,
+ Map<String, RequirementDefinition> requirements, Set<String> caseInsensitiveReqNames) {
+
+ TitanOperationStatus nonRecursiveResult = findAllRequirementsNonRecursive(resourceId, requirements,
+ caseInsensitiveReqNames);
+ if (!nonRecursiveResult.equals(TitanOperationStatus.OK)
+ && !nonRecursiveResult.equals(TitanOperationStatus.NOT_FOUND)) {
+ return nonRecursiveResult;
+ }
+
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentNodes = titanGenericDao
+ .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId,
+ GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ if (parentNodes.isRight()) {
+ TitanOperationStatus parentNodesStatus = parentNodes.right().value();
+ if (parentNodesStatus == TitanOperationStatus.NOT_FOUND) {
+ log.debug("Finish to lookup for parnet requirements");
+ return TitanOperationStatus.OK;
+ } else {
+ log.error("Failed to find parent requirements of resource {} . status is {}", resourceId,
+ parentNodesStatus);
+ return parentNodesStatus;
+ }
+ }
+ ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
+ String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
+ TitanOperationStatus addParentReqStatus = findAllRequirementsRecursively(parentUniqueId, requirements,
+ caseInsensitiveReqNames);
+
+ if (addParentReqStatus != TitanOperationStatus.OK) {
+ log.error("Failed to fetch all requirements of resource {}", parentUniqueId);
+ return addParentReqStatus;
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus findAllRequirementsNonRecursive(String resourceId,
+ Map<String, RequirementDefinition> requirements, Set<String> caseInsensitiveReqNames) {
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> requirementNodes = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId,
+ GraphEdgeLabels.REQUIREMENT, NodeTypeEnum.Requirement, RequirementData.class);
+
+ if (requirementNodes.isRight()) {
+ TitanOperationStatus status = requirementNodes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return status;
+ }
+ } else {
+ List<ImmutablePair<RequirementData, GraphEdge>> requirementList = requirementNodes.left().value();
+ if (requirementList != null) {
+ for (ImmutablePair<RequirementData, GraphEdge> requirementPair : requirementList) {
+ String reqUniqueId = requirementPair.getKey().getUniqueId();
+ Map<String, Object> edgeProps = requirementPair.getValue().getProperties();
+ String reqName = null;
+ if (edgeProps != null) {
+ reqName = (String) edgeProps.get(GraphPropertiesDictionary.NAME.getProperty());
+ if (reqName == null) {
+ log.error("The requirement name is missing on the edge of requirement " + reqUniqueId);
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+ } else {
+ log.error("The requirement name is missing on the edge of requirement " + reqUniqueId);
+ return TitanOperationStatus.INVALID_ELEMENT;
+ }
+ Either<RequirementDefinition, TitanOperationStatus> requirementDefRes = this
+ .getRequirement(reqUniqueId);
+ if (requirementDefRes.isRight()) {
+ TitanOperationStatus status = requirementDefRes.right().value();
+ log.error("Failed to get requirement properties of requirement " + reqUniqueId);
+ return status;
+ }
+
+ RequirementDefinition requirementDefinition = requirementDefRes.left().value();
+ requirementDefinition.setName(reqName);
+ // US631462
+ if (caseInsensitiveReqNames.contains(reqName.toLowerCase())) {
+ log.debug(
+ "The requirement {} was already defined in derived resource (case insensitive). Ignore {} from resource {}",
+ reqName, reqName, resourceId);
+ } else {
+ requirements.put(reqName, requirementDefinition);
+ caseInsensitiveReqNames.add(reqName.toLowerCase());
+ }
+
+ }
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ public StorageOperationStatus deleteRequirementFromGraph(String requirementId) {
+ log.debug("Before deleting requirement from graph " + requirementId);
+ Either<RequirementData, TitanOperationStatus> deleteNodeStatus = titanGenericDao.deleteNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement), requirementId, RequirementData.class);
+ if (deleteNodeStatus.isRight()) {
+ log.error("failed to delete requirement with id {}. status={}", requirementId,
+ deleteNodeStatus.right().value());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(deleteNodeStatus.right().value());
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ public Either<Map<String, RequirementDefinition>, StorageOperationStatus> deleteAllRequirements(String resourceId) {
+
+ return getAllResourceRequirements(resourceId, false);
+
+ }
+
+ public Either<Map<String, RequirementDefinition>, StorageOperationStatus> deleteAllRequirements(String resourceId,
+ boolean inTransaction) {
+
+ Either<Map<String, RequirementDefinition>, StorageOperationStatus> result = null;
+
+ try {
+ Either<Map<String, RequirementDefinition>, TitanOperationStatus> deleteAllRes = deleteAllRequirementsOfResource(
+ resourceId);
+ if (deleteAllRes.isRight()) {
+ TitanOperationStatus status = deleteAllRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to delete requirements of resource " + resourceId + ". status is " + status);
+ }
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ Map<String, RequirementDefinition> value = deleteAllRes.left().value();
+ result = Either.left(value);
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ public Either<Map<String, RequirementDefinition>, TitanOperationStatus> deleteAllRequirementsOfResource(
+ String resourceId) {
+
+ Map<String, RequirementDefinition> requirements = new HashMap<String, RequirementDefinition>();
+ Set<String> caseInsensitiveReqNames = new HashSet<>();
+ TitanOperationStatus requirementsRes = findAllRequirementsNonRecursive(resourceId, requirements,
+ caseInsensitiveReqNames);
+ if (requirementsRes != TitanOperationStatus.OK) {
+ return Either.right(requirementsRes);
+ }
+
+ if (requirements.isEmpty()) {
+ return Either.right(TitanOperationStatus.NOT_FOUND);
+ }
+
+ for (Entry<String, RequirementDefinition> entry : requirements.entrySet()) {
+ RequirementDefinition requirementDefinition = entry.getValue();
+
+ String requirementUid = requirementDefinition.getUniqueId();
+
+ Either<RequirementData, TitanOperationStatus> deleteNodeRes = titanGenericDao.deleteNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement), requirementUid, RequirementData.class);
+ if (deleteNodeRes.isRight()) {
+ TitanOperationStatus status = deleteNodeRes.right().value();
+ log.error("Failed to delete requirement " + requirementUid + " of resource " + resourceId);
+ return Either.right(status);
+ }
+ }
+
+ return Either.left(requirements);
+
+ }
+
+ public Map<String, List<RequirementDefinition>> convertRequirementMap(
+ Map<String, RequirementDefinition> requirementMap, String ownerId, String ownerName) {
+
+ Map<String, List<RequirementDefinition>> typeToRequirementMap = new HashMap<String, List<RequirementDefinition>>();
+ requirementMap.forEach((reqName, requirement) -> {
+ // requirement.setOwnerId(ownerId);
+ // requirement.setOwnerName(ownerName);
+ if (typeToRequirementMap.containsKey(requirement.getCapability())) {
+ typeToRequirementMap.get(requirement.getCapability()).add(requirement);
+ } else {
+ List<RequirementDefinition> list = new ArrayList<RequirementDefinition>();
+ list.add(requirement);
+ typeToRequirementMap.put(requirement.getCapability(), list);
+ }
+ });
+ return typeToRequirementMap;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperation.java
new file mode 100644
index 0000000000..22c693d8b3
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperation.java
@@ -0,0 +1,3089 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.AttributeDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.GroupDefinition;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.ResourceMetadataDefinition;
+import org.openecomp.sdc.be.model.cache.ComponentCache;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.IAttributeOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.utils.GraphDeleteUtil;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("resource-operation")
+public class ResourceOperation extends ComponentOperation implements IResourceOperation {
+
+ public ResourceOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(ResourceOperation.class.getName());
+
+ @javax.annotation.Resource
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource
+ private IAttributeOperation attributeOperation;
+
+ @javax.annotation.Resource
+ private RequirementOperation requirementOperation;
+
+ @javax.annotation.Resource
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource
+ private InterfaceLifecycleOperation interfaceLifecycleOperation;
+
+ @javax.annotation.Resource
+ private IElementOperation elementOperation;
+
+ @javax.annotation.Resource
+ private IAdditionalInformationOperation addioAdditionalInformationOperation;
+
+ @javax.annotation.Resource
+ private GroupOperation groupOperation;
+
+ @javax.annotation.Resource
+ private ComponentCache componentCache;
+
+ private Gson prettyJson = new GsonBuilder().setPrettyPrinting().create();
+
+ private GraphDeleteUtil graphDeleteUtil = new GraphDeleteUtil();
+
+ private static Pattern uuidNewVersion = Pattern.compile("^\\d{1,}.1");
+ private static Pattern uuidNormativeNewVersion = Pattern.compile("^\\d{1,}.0");
+
+ @Override
+ public Either<Resource, StorageOperationStatus> createResource(Resource resource) {
+ return createResource(resource, false);
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> createResource(Resource resource, boolean inTransaction) {
+
+ Either<Resource, StorageOperationStatus> result = null;
+
+ try {
+ generateUUID(resource);
+
+ ResourceMetadataData resourceData = getResourceMetaDataFromResource(resource);
+ String resourceUniqueId = resource.getUniqueId();
+ if (resourceUniqueId == null) {
+ resourceUniqueId = UniqueIdBuilder.buildResourceUniqueId();
+ resourceData.getMetadataDataDefinition().setUniqueId(resourceUniqueId);
+ }
+ resourceData.getMetadataDataDefinition().setHighestVersion(true);
+
+ String userId = resource.getCreatorUserId();
+
+ Either<TitanVertex, TitanOperationStatus> findUser = findUserVertex(userId);
+
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user " + userId + " in the graph. status is " + status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ }
+
+ TitanVertex creatorVertex = findUser.left().value();
+ TitanVertex updaterVertex = creatorVertex;
+
+ String updaterUserId = resource.getLastUpdaterUserId();
+ if (updaterUserId != null && !updaterUserId.equals(userId)) {
+ findUser = findUserVertex(updaterUserId);
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user " + userId + " in the graph. status is " + status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ } else {
+ updaterVertex = findUser.left().value();
+ }
+ }
+
+ // get derived from resources
+ List<ResourceMetadataData> derivedResources = null;
+ Either<List<ResourceMetadataData>, StorageOperationStatus> derivedResourcesResult = findDerivedResources(resource);
+ if (derivedResourcesResult.isRight()) {
+ result = Either.right(derivedResourcesResult.right().value());
+ return result;
+ } else {
+ derivedResources = derivedResourcesResult.left().value();
+ }
+
+ List<String> tags = resource.getTags();
+ if (tags != null && false == tags.isEmpty()) {
+ Either<List<TagData>, StorageOperationStatus> tagsResult = createNewTagsList(tags);
+ if (tagsResult.isRight()) {
+ result = Either.right(tagsResult.right().value());
+ return result;
+ }
+ List<TagData> tagsToCreate = tagsResult.left().value();
+ StorageOperationStatus status = createTagNodesOnGraph(tagsToCreate);
+ if (!status.equals(StorageOperationStatus.OK)) {
+ result = Either.right(status);
+ return result;
+ }
+ }
+
+ Either<TitanVertex, TitanOperationStatus> createdVertex = titanGenericDao.createNode(resourceData);
+ if (createdVertex.isRight()) {
+ TitanOperationStatus status = createdVertex.right().value();
+ log.error("Error returned after creating resource data node {}. status returned is ", resourceData, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ TitanVertex metadataVertex = createdVertex.left().value();
+
+ TitanOperationStatus associateMetadata = associateMetadataToResource(resourceData, creatorVertex, updaterVertex, derivedResources, metadataVertex);
+ if (associateMetadata != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateMetadata));
+ return result;
+ }
+ StorageOperationStatus associateCategory = assosiateMetadataToCategory(resource, resourceData);
+ if (associateCategory != StorageOperationStatus.OK) {
+ result = Either.right(associateCategory);
+ return result;
+ }
+
+ TitanOperationStatus associateProperties = associatePropertiesToResource(metadataVertex, resourceUniqueId, resource.getProperties());
+ if (associateProperties != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateProperties));
+ return result;
+ }
+
+ TitanOperationStatus associateAttributes = associateAttributesToResource(metadataVertex, resource.getAttributes(), resourceUniqueId);
+ if (associateAttributes != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateAttributes));
+ return result;
+ }
+
+ TitanOperationStatus associateInputs = associateInputsToComponent(metadataVertex, resourceUniqueId, resource.getInputs());
+ if (associateInputs != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateInputs));
+ return result;
+ }
+
+ StorageOperationStatus associateRequirements = associateRequirementsToResource(metadataVertex, resourceUniqueId, resource.getRequirements());
+ if (associateRequirements != StorageOperationStatus.OK) {
+ result = Either.right(associateRequirements);
+ return result;
+ }
+
+ StorageOperationStatus associateCapabilities = associateCapabilitiesToResource(metadataVertex, resourceUniqueId, resource.getCapabilities());
+ if (associateCapabilities != StorageOperationStatus.OK) {
+ result = Either.right(associateCapabilities);
+ return result;
+ }
+
+ StorageOperationStatus associateInterfaces = associateInterfacesToResource(resourceData, resource.getInterfaces(), metadataVertex);
+ if (associateInterfaces != StorageOperationStatus.OK) {
+ result = Either.right(associateInterfaces);
+ return result;
+ }
+
+ Map<String, ArtifactDefinition> resourceArtifacts = resource.getArtifacts();
+ Map<String, ArtifactDefinition> deploymentArtifacts = resource.getDeploymentArtifacts();
+ Map<String, ArtifactDefinition> toscaArtifacts = resource.getToscaArtifacts();
+ if (resourceArtifacts != null) {
+ if (deploymentArtifacts != null) {
+ resourceArtifacts.putAll(deploymentArtifacts);
+ }
+ } else {
+ resourceArtifacts = deploymentArtifacts;
+ }
+ if (toscaArtifacts != null) {
+ if (resourceArtifacts != null) {
+ resourceArtifacts.putAll(toscaArtifacts);
+ } else {
+ resourceArtifacts = toscaArtifacts;
+ }
+ }
+
+ StorageOperationStatus associateArtifacts = associateArtifactsToResource(metadataVertex, resourceUniqueId, resourceArtifacts);
+ if (associateArtifacts != StorageOperationStatus.OK) {
+ result = Either.right(associateArtifacts);
+ return result;
+ }
+
+ List<AdditionalInformationDefinition> additionalInformation = resource.getAdditionalInformation();
+ StorageOperationStatus addAdditionalInformation = addAdditionalInformationToResource(metadataVertex, resourceUniqueId, additionalInformation);
+ if (addAdditionalInformation != StorageOperationStatus.OK) {
+ result = Either.right(addAdditionalInformation);
+ return result;
+ }
+
+ result = this.getResource(resourceUniqueId, true);
+ if (result.isRight()) {
+ log.error("Cannot get full resource from the graph. status is " + result.right().value());
+ return Either.right(result.right().value());
+ }
+
+ if (log.isDebugEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ log.debug("Resource retrieved is " + json);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private StorageOperationStatus assosiateMetadataToCategory(Resource resource, ResourceMetadataData resourceData) {
+ // get category
+ String categoryName = resource.getCategories().get(0).getName();
+ String subcategoryName = resource.getCategories().get(0).getSubcategories().get(0).getName();
+
+ CategoryData categoryData = null;
+ Either<CategoryData, StorageOperationStatus> categoryResult = elementOperation.getNewCategoryData(categoryName, NodeTypeEnum.ResourceNewCategory, CategoryData.class);
+ if (categoryResult.isRight()) {
+ StorageOperationStatus status = categoryResult.right().value();
+ log.error("Cannot find category " + categoryName + " in the graph. status is " + status);
+ return categoryResult.right().value();
+ }
+ categoryData = categoryResult.left().value();
+ if (categoryData != null) {
+ Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceNewCategory), (String) categoryData.getUniqueId(),
+ GraphEdgeLabels.SUB_CATEGORY, NodeTypeEnum.ResourceSubcategory, SubCategoryData.class);
+ if (childrenNodes.isRight()) {
+ log.debug("Faield to fetch sub categories for resource category" + categoryData.getCategoryDataDefinition().getName());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(childrenNodes.right().value());
+ }
+ for (ImmutablePair<SubCategoryData, GraphEdge> pair : childrenNodes.left().value()) {
+ SubCategoryData subcategoryData = pair.left;
+ if (subcategoryData.getSubCategoryDataDefinition().getName().equals(subcategoryName)) {
+ Either<GraphRelation, TitanOperationStatus> result = titanGenericDao.createRelation(resourceData, subcategoryData, GraphEdgeLabels.CATEGORY, null);
+ log.debug("After associating resource " + resourceData.getUniqueId() + " to subcategory " + subcategoryData + ". Edge type is " + GraphEdgeLabels.CATEGORY);
+ if (result.isRight()) {
+ log.error("Faield to associate resource " + resourceData.getUniqueId() + " to category " + categoryData + ". Edge type is " + GraphEdgeLabels.CATEGORY);
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(result.right().value());
+ }
+
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus addAdditionalInformationToResource(TitanVertex metadataVertex, String resourceUniqueId, List<AdditionalInformationDefinition> additionalInformation) {
+
+ StorageOperationStatus result = null;
+ if (additionalInformation == null || true == additionalInformation.isEmpty()) {
+ result = super.addAdditionalInformation(NodeTypeEnum.Resource, resourceUniqueId, null, metadataVertex);
+ } else {
+ if (additionalInformation.size() == 1) {
+ result = super.addAdditionalInformation(NodeTypeEnum.Resource, resourceUniqueId, additionalInformation.get(0));
+ } else {
+ result = StorageOperationStatus.BAD_REQUEST;
+ log.info("Cannot create resource with more than one additional information object. The number of received object is {}", additionalInformation.size());
+ }
+ }
+ return result;
+ }
+
+ private void generateUUID(Resource resource) {
+ String prevUUID = resource.getUUID();
+ String version = resource.getVersion();
+ if ((prevUUID == null && uuidNormativeNewVersion.matcher(version).matches()) || uuidNewVersion.matcher(version).matches()) {
+ UUID uuid = UUID.randomUUID();
+ resource.setUUID(uuid.toString());
+ MDC.put("serviceInstanceID", uuid.toString());
+ }
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> overrideResource(Resource resource, Resource resourceSaved, boolean inTransaction) {
+ Either<Resource, StorageOperationStatus> result = null;
+ try {
+ String resourceId = resourceSaved.getUniqueId();
+
+ // override interfaces to copy only resource's interfaces and not
+ // derived interfaces
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> interfacesOfResourceOnly = interfaceLifecycleOperation.getAllInterfacesOfResource(resourceSaved.getUniqueId(), false, true);
+ if (interfacesOfResourceOnly.isRight()) {
+ log.error("failed to get interfaces of resource. resourceId {} status is {}", resourceId, interfacesOfResourceOnly.right().value());
+ result = Either.right(interfacesOfResourceOnly.right().value());
+ return result;
+ }
+ resource.setInterfaces(interfacesOfResourceOnly.left().value());
+ resource.setArtifacts(resourceSaved.getArtifacts());
+ resource.setDeploymentArtifacts(resourceSaved.getDeploymentArtifacts());
+ resource.setGroups(resourceSaved.getGroups());
+ resource.setInputs(null);
+ resource.setLastUpdateDate(null);
+ resource.setHighestVersion(true);
+
+ // delete former resource
+ Either<Resource, StorageOperationStatus> deleteResource = deleteResource(resourceId, true);
+ if (deleteResource.isRight()) {
+ log.error("failed to delete old resource with id {}. status = {}", resourceId, deleteResource.right().value());
+ result = deleteResource;
+ return result;
+ }
+
+ Either<Resource, StorageOperationStatus> createResource = createResource(resource, true);
+ if (createResource.isRight()) {
+ log.error("failed to create new version of resource {} status = {}", resourceId, createResource.right().value());
+ result = createResource;
+ return result;
+ }
+ Resource newResource = createResource.left().value();
+
+ Either<List<GroupDefinition>, StorageOperationStatus> cloneGroupEither = cloneGroups(resource, newResource, null, inTransaction);
+ if (cloneGroupEither.isLeft()) {
+ newResource.setGroups(cloneGroupEither.left().value());
+ } else if (cloneGroupEither.right().value() != StorageOperationStatus.OK) {
+ log.error("failed to clone group of resource {} status = {}", resourceId, cloneGroupEither.right().value());
+ result = Either.right(cloneGroupEither.right().value());
+ return result;
+ }
+
+ result = Either.left(newResource);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private StorageOperationStatus associateCapabilitiesToResource(TitanVertex metadataVertex, String resourceIda, Map<String, List<CapabilityDefinition>> capabilities) {
+ StorageOperationStatus addCapabilityToResource = null;
+ if (capabilities != null) {
+ for (Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
+
+ List<CapabilityDefinition> capDefinition = entry.getValue();
+ for (CapabilityDefinition item : capDefinition) {
+ addCapabilityToResource = capabilityOperation.addCapability(metadataVertex, resourceIda, item.getName(), item, true);
+ if (!addCapabilityToResource.equals(StorageOperationStatus.OK)) {
+ return addCapabilityToResource;
+ }
+
+ }
+ }
+
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus associateRequirementsToResource(TitanVertex metadataVertex, String resourceId, Map<String, List<RequirementDefinition>> requirements) {
+
+ if (requirements != null) {
+ for (Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
+
+ List<RequirementDefinition> reqDefinition = entry.getValue();
+ for (RequirementDefinition item : reqDefinition) {
+ StorageOperationStatus addRequirementToResource = requirementOperation.addRequirementToResource(metadataVertex, item.getName(), item, resourceId, true);
+
+ if (!addRequirementToResource.equals(StorageOperationStatus.OK)) {
+ return addRequirementToResource;
+ }
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus associateArtifactsToResource(TitanVertex metadataVertex, String resourceId, Map<String, ArtifactDefinition> artifacts) {
+
+ StorageOperationStatus status = StorageOperationStatus.OK;
+ if (artifacts != null) {
+ for (Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+
+ ArtifactDefinition artifactDefinition = entry.getValue();
+ status = artifactOperation.addArifactToComponent(artifactDefinition, resourceId, NodeTypeEnum.Resource, false, metadataVertex);
+
+ if (!status.equals(StorageOperationStatus.OK)) {
+ return status;
+ }
+ }
+ }
+ return status;
+
+ }
+
+ private StorageOperationStatus associateInterfacesToResource(ResourceMetadataData resourceData, Map<String, InterfaceDefinition> interfaces, TitanVertex metadataVertex) {
+
+ if (interfaces != null) {
+ for (Entry<String, InterfaceDefinition> entry : interfaces.entrySet()) {
+
+ InterfaceDefinition interfaceDefinition = entry.getValue();
+ StorageOperationStatus status;
+ if (((ResourceMetadataDataDefinition) resourceData.getMetadataDataDefinition()).isAbstract()) {
+ status = interfaceLifecycleOperation.associateInterfaceToNode(resourceData, interfaceDefinition, metadataVertex);
+ } else {
+ status = interfaceLifecycleOperation.createInterfaceOnResource(interfaceDefinition, resourceData.getMetadataDataDefinition().getUniqueId(), interfaceDefinition.getType(), false, true, metadataVertex);
+ }
+
+ if (!status.equals(StorageOperationStatus.OK)) {
+ return status;
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+
+ }
+
+ private Either<Resource, StorageOperationStatus> sendError(TitanOperationStatus status, StorageOperationStatus statusIfNotFound) {
+ Either<Resource, StorageOperationStatus> result;
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ result = Either.right(statusIfNotFound);
+ return result;
+ } else {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+
+ private TitanOperationStatus associatePropertiesToResource(TitanVertex metadatVertex, String resourceId, List<PropertyDefinition> properties) {
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Cannot find any data type. Status is {}.", status);
+ return status;
+ }
+
+ Map<String, PropertyDefinition> convertedProperties = new HashMap<>();
+
+ if (properties != null) {
+ for (PropertyDefinition propertyDefinition : properties) {
+ convertedProperties.put(propertyDefinition.getName(), propertyDefinition);
+ }
+ TitanOperationStatus operationStatus = propertyOperation.addPropertiesToGraph(metadatVertex, convertedProperties, allDataTypes.left().value(), resourceId);
+ return operationStatus;
+ }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ private TitanOperationStatus associateAttributesToResource(TitanVertex metadataVertex, List<AttributeDefinition> attributes, String resourceId) {
+ TitanOperationStatus operationStatus = TitanOperationStatus.OK;
+
+ Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = applicationDataTypeCache.getAll();
+ if (allDataTypes.isRight()) {
+ TitanOperationStatus status = allDataTypes.right().value();
+ log.debug("Cannot find any data type. Status is {}.", status);
+ return status;
+ }
+
+ if (attributes != null) {
+ Map<String, AttributeDefinition> convertedAttributes = attributes.stream().collect(Collectors.toMap(e -> e.getName(), e -> e));
+ operationStatus = attributeOperation.addAttributesToGraph(metadataVertex, convertedAttributes, resourceId, allDataTypes.left().value());
+ }
+ return operationStatus;
+ }
+
+ private TitanOperationStatus associateMetadataToResource(ResourceMetadataData resourceData, TitanVertex creatorVertex, TitanVertex updaterVertex, List<ResourceMetadataData> derivedResources, TitanVertex metadataVertex) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), resourceData.getMetadataDataDefinition().getState());
+
+ TitanOperationStatus result = titanGenericDao.createEdge(updaterVertex, metadataVertex, GraphEdgeLabels.STATE, props);
+ log.debug("After associating user {} to resource {}. Edge type is {}", updaterVertex, resourceData.getUniqueId(), GraphEdgeLabels.STATE);
+ if (!result.equals(TitanOperationStatus.OK)) {
+ return result;
+ }
+ result = titanGenericDao.createEdge(updaterVertex, metadataVertex, GraphEdgeLabels.LAST_MODIFIER, null);
+ log.debug("After associating user {} to resource {}. Edge type is {}", updaterVertex, resourceData.getUniqueId(), GraphEdgeLabels.LAST_MODIFIER);
+ if (!result.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate user {} to resource {}. Edge type is {}", updaterVertex, resourceData.getUniqueId(), GraphEdgeLabels.LAST_MODIFIER);
+ return result;
+ }
+
+ result = titanGenericDao.createEdge(creatorVertex, metadataVertex, GraphEdgeLabels.CREATOR, null);
+ log.debug("After associating user {} to resource {}. Edge type is {} ", creatorVertex, resourceData.getUniqueId(), GraphEdgeLabels.CREATOR);
+ if (!result.equals(TitanOperationStatus.OK)) {
+ log.error("Failed to associate user {} to resource {}. Edge type is {} ", creatorVertex, resourceData.getUniqueId(), GraphEdgeLabels.CREATOR);
+ return result;
+ }
+ // TODO Evg : need to change too..
+ if (derivedResources != null) {
+ for (ResourceMetadataData derivedResource : derivedResources) {
+ log.debug("After associating resource " + resourceData.getUniqueId() + " to parent resource " + derivedResource.getUniqueId() + ". Edge type is " + GraphEdgeLabels.DERIVED_FROM);
+ Either<GraphRelation, TitanOperationStatus> createRelationResult = titanGenericDao.createRelation(resourceData, derivedResource, GraphEdgeLabels.DERIVED_FROM, null);
+ if (createRelationResult.isRight()) {
+ log.error("Failed to associate resource {} to derived ", resourceData.getUniqueId());
+ return createRelationResult.right().value();
+ }
+ }
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ public Either<List<ResourceMetadataData>, StorageOperationStatus> findDerivedResources(Resource resource) {
+
+ List<ResourceMetadataData> derivedResources = new ArrayList<ResourceMetadataData>();
+ List<String> derivedFromResources = resource.getDerivedFrom();
+ if (derivedFromResources != null && false == derivedFromResources.isEmpty()) {
+
+ for (String parentResource : derivedFromResources) {
+
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+ // propertiesToMatch.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(),
+ // true);
+ propertiesToMatch.put(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty(), parentResource);
+ propertiesToMatch.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> getParentResources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesToMatch, ResourceMetadataData.class);
+ List<ResourceMetadataData> resources = null;
+ if (getParentResources.isRight()) {
+ /*
+ * log.debug( "Cannot find parent resource by tosca resource name" + parentResource + " in the graph. Try to find by name"); Map<String, Object> propertiesWithResourceNameToMatch = new HashMap<String, Object>();
+ * propertiesWithResourceNameToMatch.put( GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name()); propertiesWithResourceNameToMatch.put( GraphPropertiesDictionary.NAME.getProperty(), parentResource);
+ * propertiesWithResourceNameToMatch.put( GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty( ), true);
+ *
+ * getParentResources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesWithResourceNameToMatch, ResourceData.class); if (getParentResources.isRight()) { log.error(
+ * "Cannot find parent resource by tosca resource name" + parentResource + " in the graph."); return Either.right(StorageOperationStatus. PARENT_RESOURCE_NOT_FOUND); }else{ resources = getParentResources.left().value();
+ *
+ * }
+ */
+ log.error("Cannot find parent resource by tosca resource name" + parentResource + " in the graph.");
+ return Either.right(StorageOperationStatus.PARENT_RESOURCE_NOT_FOUND);
+
+ } else {
+ resources = getParentResources.left().value();
+ if (resources == null || resources.size() == 0) {
+ log.error("Cannot find parent resource by tosc name" + parentResource + " in the graph. resources size is empty");
+ return Either.right(StorageOperationStatus.PARENT_RESOURCE_NOT_FOUND);
+ } else {
+ if (resources.size() > 1) {
+ log.error("Multiple parent resources called " + parentResource + " found in the graph.");
+ return Either.right(StorageOperationStatus.MULTIPLE_PARENT_RESOURCE_FOUND);
+ }
+ ResourceMetadataData parentResourceData = resources.get(0);
+ derivedResources.add(parentResourceData);
+ }
+
+ }
+
+ }
+ }
+ return Either.left(derivedResources);
+ }
+
+ private ResourceMetadataData getResourceMetaDataFromResource(Resource resource) {
+ ResourceMetadataData resourceData = new ResourceMetadataData((ResourceMetadataDataDefinition) resource.getComponentMetadataDefinition().getMetadataDataDefinition());
+ if (resource.getNormalizedName() == null || resource.getNormalizedName().isEmpty()) {
+ resourceData.getMetadataDataDefinition().setNormalizedName(ValidationUtils.normaliseComponentName(resource.getName()));
+ }
+ if (resource.getSystemName() == null || resource.getSystemName().isEmpty()) {
+ resourceData.getMetadataDataDefinition().setSystemName(ValidationUtils.convertToSystemName(resource.getName()));
+ }
+
+ LifecycleStateEnum lifecycleStateEnum = resource.getLifecycleState();
+ if (lifecycleStateEnum == null) {
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT.name());
+ }
+ long currentDate = System.currentTimeMillis();
+ if (resource.getCreationDate() == null) {
+ resourceData.getMetadataDataDefinition().setCreationDate(currentDate);
+ }
+ resourceData.getMetadataDataDefinition().setLastUpdateDate(currentDate);
+
+ return resourceData;
+ }
+
+ private ResourceMetadataData getResourceMetaDataForUpdate(Resource resource) {
+ // PA - please note: if you add here any fields, make sure they are
+ // validated (if needed)
+ // at ResourceBusinessLogic.validateResourceFieldsBeforeUpdate() and
+ // tested at ResourceBusinessLogicTest.
+ ResourceMetadataData resourceData = getResourceMetaDataFromResource(resource);
+ // resourceData.setLastUpdateDate(System.currentTimeMillis());
+ // resourceData.setHighestVersion(resource.isHighestVersion());
+ // resourceData.setNormalizedName(resource.getNormalizedName());
+ // resourceData.setResourceType(resource.getResourceType().name());
+
+ return resourceData;
+ }
+
+ public Either<Resource, StorageOperationStatus> getResource(String uniqueId) {
+ return getResource(uniqueId, false);
+ }
+
+ public Either<Resource, StorageOperationStatus> getResource(String uniqueId, boolean inTransaction) {
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ return getResource(uniqueId, componentParametersView, inTransaction);
+ }
+
+ // public Either<Resource, StorageOperationStatus> getResource(String
+ // uniqueId, boolean inTransaction) {
+ //
+ // Resource resource = null;
+ // try {
+ //
+ // NodeTypeEnum resourceNodeType = NodeTypeEnum.Resource;
+ // NodeTypeEnum compInstNodeType = NodeTypeEnum.Resource;
+ //
+ // Either<ResourceMetadataData, StorageOperationStatus>
+ // componentByLabelAndId = getComponentByLabelAndId(uniqueId,
+ // resourceNodeType, ResourceMetadataData.class);
+ // if (componentByLabelAndId.isRight()) {
+ // return Either.right(componentByLabelAndId.right().value());
+ // }
+ // ResourceMetadataData resourceData = componentByLabelAndId.left().value();
+ // resource = convertResourceDataToResource(resourceData);
+ //
+ // TitanOperationStatus status = setResourceCreatorFromGraph(resource,
+ // uniqueId);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourceLastModifierFromGraph(resource, uniqueId);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourcePropertiesFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourceAttributesFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourceDerivedFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setComponentCategoriesFromGraph(resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setComponentInstancesFromGraph(uniqueId, resource,
+ // resourceNodeType, compInstNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ //
+ // }
+ //
+ // StorageOperationStatus setRequirementsStatus =
+ // setResourceRequirementsFromGraph(uniqueId, resource, true);
+ // if (setRequirementsStatus != StorageOperationStatus.OK) {
+ // log.error("Failed to set requirement of resource " + uniqueId + ". status
+ // is " + setRequirementsStatus);
+ // return Either.right(setRequirementsStatus);
+ // }
+ //
+ // StorageOperationStatus storageStatus =
+ // setResourceCapabilitiesFromGraph(uniqueId, resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ //
+ // storageStatus = setArtifactFromGraph(uniqueId, resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ //
+ // status = setComponentInstancesAttributesFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ //
+ // }
+ //
+ // status = setComponentInstancesPropertiesFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ //
+ // }
+ //
+ // storageStatus = setResourceInterfacesFromGraph(uniqueId, resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ //
+ // storageStatus = setResourceAdditionalInformationFromGraph(uniqueId,
+ // resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ // status = setAllVersions(resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setGroupsFromGraph(uniqueId, resource, NodeTypeEnum.Resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // } finally {
+ // if (false == inTransaction) {
+ // titanGenericDao.commit();
+ // }
+ // }
+ //
+ // return Either.left(resource);
+ // }
+
+ private TitanOperationStatus setComponentInstancesAttributesFromGraph(String uniqueId, Resource component) {
+ Map<String, List<ComponentInstanceAttribute>> resourceInstancesAttributes = new HashMap<>();
+ TitanOperationStatus status = TitanOperationStatus.OK;
+ List<ComponentInstance> componentInstances = component.getComponentInstances();
+ if (componentInstances != null) {
+ for (ComponentInstance resourceInstance : componentInstances) {
+ Either<List<ComponentInstanceAttribute>, TitanOperationStatus> eitherRIAttributes = attributeOperation.getAllAttributesOfResourceInstance(resourceInstance);
+ if (eitherRIAttributes.isRight()) {
+ status = eitherRIAttributes.right().value();
+ break;
+ } else {
+ resourceInstancesAttributes.put(resourceInstance.getUniqueId(), eitherRIAttributes.left().value());
+ }
+ }
+
+ component.setComponentInstancesAttributes(resourceInstancesAttributes);
+ }
+
+ return status;
+
+ }
+
+ // public Either<Resource, StorageOperationStatus> getResource_tx(String
+ // uniqueId, boolean inTransaction) {
+ //
+ // Resource resource = null;
+ // try {
+ //
+ // NodeTypeEnum resourceNodeType = NodeTypeEnum.Resource;
+ // NodeTypeEnum compInstNodeType = NodeTypeEnum.Resource;
+ //
+ // Either<ResourceMetadataData, StorageOperationStatus>
+ // componentByLabelAndId = getComponentByLabelAndId_tx(uniqueId,
+ // resourceNodeType, ResourceMetadataData.class);
+ // if (componentByLabelAndId.isRight()) {
+ // return Either.right(componentByLabelAndId.right().value());
+ // }
+ // ResourceMetadataData resourceData = componentByLabelAndId.left().value();
+ // resource = convertResourceDataToResource(resourceData);
+ //
+ // TitanOperationStatus status = setResourceCreatorFromGraph(resource,
+ // uniqueId);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourceLastModifierFromGraph(resource, uniqueId);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourcePropertiesFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setResourceDerivedFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setComponentCategoriesFromGraph(resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setComponentInstancesFromGraph(uniqueId, resource,
+ // resourceNodeType, compInstNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ //
+ // }
+ //
+ // StorageOperationStatus setRequirementsStatus =
+ // setResourceRequirementsFromGraph(uniqueId, resource, true);
+ // if (setRequirementsStatus != StorageOperationStatus.OK) {
+ // log.error("Failed to set requirement of resource " + uniqueId + ". status
+ // is " + setRequirementsStatus);
+ // return Either.right(setRequirementsStatus);
+ // }
+ //
+ // StorageOperationStatus storageStatus =
+ // setResourceCapabilitiesFromGraph(uniqueId, resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ //
+ // storageStatus = setArtifactFromGraph(uniqueId, resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ //
+ // status = setComponentInstancesPropertiesFromGraph(uniqueId, resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ //
+ // }
+ //
+ // storageStatus = setResourceInterfacesFromGraph(uniqueId, resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ //
+ // storageStatus = setResourceAdditionalInformationFromGraph(uniqueId,
+ // resource);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // return Either.right(storageStatus);
+ // }
+ // status = setAllVersions(resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // } finally {
+ // if (false == inTransaction) {
+ // titanGenericDao.commit();
+ // }
+ // }
+ //
+ // return Either.left(resource);
+ // }
+
+ private StorageOperationStatus setResourceAdditionalInformationFromGraph(String uniqueId, Resource resource) {
+
+ List<AdditionalInformationDefinition> additionalInformation = new ArrayList<>();
+
+ Either<AdditionalInformationDefinition, StorageOperationStatus> either = additionalInformationOperation.getAllAdditionalInformationParameters(NodeTypeEnum.Resource, uniqueId, true, true);
+
+ if (either.isRight()) {
+ StorageOperationStatus status = either.right().value();
+ if (status == StorageOperationStatus.NOT_FOUND) {
+ return StorageOperationStatus.OK;
+ }
+ return status;
+ }
+
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+ additionalInformation.add(additionalInformationDefinition);
+
+ resource.setAdditionalInformation(additionalInformation);
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ private StorageOperationStatus setResourceInterfacesFromGraph(String uniqueId, Resource resource) {
+
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> statusRes = interfaceLifecycleOperation.getAllInterfacesOfResource(uniqueId, true, true);
+ if (statusRes.isRight()) {
+ return statusRes.right().value();
+ }
+ Map<String, InterfaceDefinition> value = statusRes.left().value();
+
+ resource.setInterfaces(value);
+
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus setResourceCapabilitiesFromGraph(String uniqueId, Resource resource) {
+ StorageOperationStatus retStatus;
+ Either<Map<String, CapabilityDefinition>, StorageOperationStatus> result = capabilityOperation.getAllCapabilitiesOfResource(uniqueId, true, true);
+ if (result.isRight()) {
+ StorageOperationStatus status = result.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ retStatus = status;
+ } else {
+ retStatus = StorageOperationStatus.OK;
+ }
+ } else {
+ Map<String, CapabilityDefinition> capabilities = result.left().value();
+ if (capabilities == null || capabilities.isEmpty()) {
+ Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> eitherCapabilities = super.getCapabilities(resource, NodeTypeEnum.Resource, true);
+ if (eitherCapabilities.isLeft()) {
+ retStatus = StorageOperationStatus.OK;
+ Map<String, List<CapabilityDefinition>> calculatedCapabilities = eitherCapabilities.left().value();
+ resource.setCapabilities(calculatedCapabilities);
+ } else {
+ retStatus = StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ } else {
+ retStatus = StorageOperationStatus.OK;
+ resource.setCapabilities(capabilityOperation.convertCapabilityMap(capabilities, null, null));
+ }
+
+ }
+ return retStatus;
+
+ }
+
+ public Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> getCapabilities(org.openecomp.sdc.be.model.Component component, NodeTypeEnum componentTypeEnum, boolean inTransaction) {
+
+ try {
+ Either<Map<String, CapabilityDefinition>, StorageOperationStatus> result = capabilityOperation.getAllCapabilitiesOfResource(component.getUniqueId(), true, true);
+ if (result.isRight() || result.left().value().isEmpty()) {
+ final Either<Map<String, List<CapabilityDefinition>>, TitanOperationStatus> eitherCapabilities = super.getCapabilities(component, componentTypeEnum, inTransaction);
+ return eitherCapabilities;
+ } else {
+ return Either.left(capabilityOperation.convertCapabilityMap(result.left().value(), null, null));
+ }
+ } finally {
+ if (inTransaction == false) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ public Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> getRequirements(org.openecomp.sdc.be.model.Component component, NodeTypeEnum componentTypeEnum, boolean inTransaction) {
+ try {
+ Either<Map<String, RequirementDefinition>, StorageOperationStatus> result = requirementOperation.getAllResourceRequirements(component.getUniqueId(), true);
+ if (result.isRight() || result.left().value().isEmpty()) {
+ final Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> eitherCapabilities = super.getRequirements(component, componentTypeEnum, true);
+ return eitherCapabilities;
+ } else {
+ return Either.left(requirementOperation.convertRequirementMap(result.left().value(), null, null));
+ }
+ } finally {
+ if (inTransaction == false) {
+ titanGenericDao.commit();
+ }
+ }
+
+ }
+
+ private StorageOperationStatus setResourceRequirementsFromGraph(String uniqueId, Resource resource, boolean inTransaction) {
+ StorageOperationStatus retStatus;
+ Either<Map<String, RequirementDefinition>, StorageOperationStatus> result = requirementOperation.getAllResourceRequirements(uniqueId, inTransaction);
+ ;
+ if (result.isRight()) {
+ StorageOperationStatus status = result.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ retStatus = status;
+ } else {
+ retStatus = StorageOperationStatus.OK;
+ }
+ } else {
+ Map<String, RequirementDefinition> requirements = result.left().value();
+ if (requirements == null || requirements.isEmpty()) {
+ Either<Map<String, List<RequirementDefinition>>, TitanOperationStatus> eitherCapabilities = super.getRequirements(resource, NodeTypeEnum.Resource, true);
+ if (eitherCapabilities.isLeft()) {
+ retStatus = StorageOperationStatus.OK;
+ Map<String, List<RequirementDefinition>> calculatedCapabilities = eitherCapabilities.left().value();
+ resource.setRequirements(calculatedCapabilities);
+ } else {
+ retStatus = StorageOperationStatus.GENERAL_ERROR;
+ }
+
+ } else {
+ retStatus = StorageOperationStatus.OK;
+ resource.setRequirements(requirementOperation.convertRequirementMap(requirements, null, null));
+ }
+
+ }
+ return retStatus;
+ }
+
+ private TitanOperationStatus setResourcePropertiesFromGraph(String uniqueId, Resource resource) {
+
+ List<PropertyDefinition> properties = new ArrayList<>();
+ TitanOperationStatus status = propertyOperation.findAllResourcePropertiesRecursively(uniqueId, properties);
+ if (status == TitanOperationStatus.OK) {
+ resource.setProperties(properties);
+ }
+
+ return status;
+
+ }
+
+ private TitanOperationStatus setResourceAttributesFromGraph(String uniqueId, Resource resource) {
+
+ List<AttributeDefinition> attributes = new ArrayList<>();
+ TitanOperationStatus status = attributeOperation.findAllResourceAttributesRecursively(uniqueId, attributes);
+ if (status == TitanOperationStatus.OK) {
+ resource.setAttributes(attributes);
+ }
+
+ return status;
+
+ }
+
+ private TitanOperationStatus setResourceDerivedFromGraph(String uniqueId, Resource resource) {
+ List<String> derivedFromList = new ArrayList<String>();
+
+ TitanOperationStatus listFromGraphStatus = fillResourceDerivedListFromGraph(uniqueId, derivedFromList);
+ if (!TitanOperationStatus.OK.equals(listFromGraphStatus)) {
+ return listFromGraphStatus;
+ }
+
+ if (false == derivedFromList.isEmpty()) {
+ if (derivedFromList.size() > 1) {
+ List<String> lastDerivedFrom = new ArrayList<String>();
+ lastDerivedFrom.add(derivedFromList.get(1));
+ resource.setDerivedFrom(lastDerivedFrom);
+ resource.setDerivedList(derivedFromList);
+ } else {
+ resource.setDerivedFrom(null);
+ resource.setDerivedList(derivedFromList);
+ }
+
+ }
+
+ return TitanOperationStatus.OK;
+ }
+
+ public TitanOperationStatus fillResourceDerivedListFromGraph(String uniqueId, List<String> derivedFromList) {
+ // Either<List<ImmutablePair<ResourceMetadataData, GraphEdge>>,
+ // TitanOperationStatus> childrenNodes =
+ // titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource),
+ // uniqueId, GraphEdgeLabels.DERIVED_FROM,
+ // NodeTypeEnum.Resource, ResourceMetadataData.class);
+ //
+ // if (childrenNodes.isRight() && (childrenNodes.right().value() !=
+ // TitanOperationStatus.NOT_FOUND)) {
+ // return childrenNodes.right().value();
+ // } else if (childrenNodes.isLeft()) {
+ //
+ // List<ImmutablePair<ResourceMetadataData, GraphEdge>> pairList =
+ // childrenNodes.left().value();
+ // for (ImmutablePair<ResourceMetadataData, GraphEdge> pair : pairList)
+ // {
+ // derivedFromList.add(pair.left.getMetadataDataDefinition().getName());
+ // return
+ // fillResourceDerivedListFromGraph(pair.left.getMetadataDataDefinition().getUniqueId(),
+ // derivedFromList);
+ // }
+ // }
+ List<ResourceMetadataData> derivedData = new ArrayList<ResourceMetadataData>();
+ TitanOperationStatus findResourcesPathRecursively = findResourcesPathRecursively(uniqueId, derivedData);
+ if (!findResourcesPathRecursively.equals(TitanOperationStatus.OK)) {
+ return findResourcesPathRecursively;
+ }
+ derivedData.forEach(resourceData -> derivedFromList.add(((ResourceMetadataDataDefinition) resourceData.getMetadataDataDefinition()).getToscaResourceName()));
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus setResourceLastModifierFromGraph(Resource resource, String resourceId) {
+
+ Either<ImmutablePair<UserData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.LAST_MODIFIER, NodeTypeEnum.User,
+ UserData.class);
+ if (parentNode.isRight()) {
+ return parentNode.right().value();
+ }
+
+ ImmutablePair<UserData, GraphEdge> value = parentNode.left().value();
+ if (log.isDebugEnabled())
+ log.debug("Found parent node {}", value);
+ UserData userData = value.getKey();
+
+ if (log.isDebugEnabled()) {
+ log.debug("Build resource : set last modifier userId to {}", userData.getUserId());
+ }
+
+ String fullName = buildFullName(userData);
+ if (log.isDebugEnabled())
+ log.debug("Build resource : set last modifier full name to {}", fullName);
+ resource.setLastUpdaterUserId(userData.getUserId());
+ resource.setLastUpdaterFullName(fullName);
+
+ return TitanOperationStatus.OK;
+ }
+
+ private TitanOperationStatus setResourceCreatorFromGraph(Resource resource, String resourceId) {
+
+ Either<ImmutablePair<UserData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.CREATOR, NodeTypeEnum.User, UserData.class);
+ if (parentNode.isRight()) {
+ log.debug("Failed to find the creator of resource {}", resourceId);
+ return parentNode.right().value();
+ }
+
+ ImmutablePair<UserData, GraphEdge> value = parentNode.left().value();
+ if (log.isDebugEnabled())
+ log.debug("Found parent node {}", value);
+ UserData userData = value.getKey();
+ if (log.isDebugEnabled()) {
+ log.debug("Build resource : set creator userId to {}", userData.getUserId());
+ }
+ String fullName = buildFullName(userData);
+ if (log.isDebugEnabled())
+ log.debug("Build resource : set creator full name to {}", fullName);
+ resource.setCreatorUserId(userData.getUserId());
+ resource.setCreatorFullName(fullName);
+
+ return TitanOperationStatus.OK;
+ }
+
+ @Override
+ TitanOperationStatus setComponentCategoriesFromGraph(Component resource) {
+ String uniqueId = resource.getUniqueId();
+ Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, TitanOperationStatus> parentNode = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), uniqueId, GraphEdgeLabels.CATEGORY,
+ NodeTypeEnum.ResourceSubcategory, SubCategoryData.class);
+ if (parentNode.isRight()) {
+ return parentNode.right().value();
+ }
+
+ List<ImmutablePair<SubCategoryData, GraphEdge>> listValue = parentNode.left().value();
+ log.debug("Result after looking for subcategory nodes pointed by resource {}. status is {}", uniqueId, listValue);
+ if (listValue.size() > 1) {
+ log.error("Multiple edges foud between resource {} to subcategory nodes.", uniqueId);
+ }
+ ImmutablePair<SubCategoryData, GraphEdge> value = listValue.get(0);
+ log.debug("Found parent node {}", value);
+
+ SubCategoryData subcategoryData = value.getKey();
+
+ Either<ImmutablePair<CategoryData, GraphEdge>, TitanOperationStatus> categoryNode = titanGenericDao.getParentNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceSubcategory), (String) subcategoryData.getUniqueId(),
+ GraphEdgeLabels.SUB_CATEGORY, NodeTypeEnum.ResourceNewCategory, CategoryData.class);
+ if (categoryNode.isRight()) {
+ return categoryNode.right().value();
+ }
+
+ CategoryData categoryData = categoryNode.left().value().left;
+ CategoryDefinition catDef = new CategoryDefinition(categoryData.getCategoryDataDefinition());
+ SubCategoryDefinition subcatDef = new SubCategoryDefinition(subcategoryData.getSubCategoryDataDefinition());
+
+ resource.addCategory(catDef, subcatDef);
+ return TitanOperationStatus.OK;
+ }
+
+ public String buildFullName(UserData userData) {
+
+ String fullName = userData.getFirstName();
+ if (fullName == null) {
+ fullName = "";
+ } else {
+ fullName = fullName + " ";
+ }
+ String lastName = userData.getLastName();
+ if (lastName != null) {
+ fullName += lastName;
+ }
+ return fullName;
+ }
+
+ private Resource convertResourceDataToResource(ResourceMetadataData resourceData) {
+
+ ResourceMetadataDefinition resourceMetadataDataDefinition = new ResourceMetadataDefinition((ResourceMetadataDataDefinition) resourceData.getMetadataDataDefinition());
+
+ Resource resource = new Resource(resourceMetadataDataDefinition);
+
+ return resource;
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> deleteResource(String resourceId) {
+ return deleteResource(resourceId, false);
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> updateResource(Resource resource) {
+
+ return updateResource(resource, false);
+
+ }
+
+ @Override
+ public Either<Integer, StorageOperationStatus> getNumberOfResourcesByName(String resourceName) {
+
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(GraphPropertiesDictionary.NAME.getProperty(), resourceName);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> getParentResources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesToMatch, ResourceMetadataData.class);
+ log.debug("result after searching for resources called " + resourceName + " is " + getParentResources);
+ if (getParentResources.isRight()) {
+ TitanOperationStatus titanStatus = getParentResources.right().value();
+ if (titanStatus == TitanOperationStatus.NOT_FOUND) {
+ log.debug("Number of returned resources is 0.");
+ return Either.left(0);
+ }
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus));
+ } else {
+ List<ResourceMetadataData> value = getParentResources.left().value();
+ int numberOFResources = (value == null ? 0 : value.size());
+ log.debug("The number of resources returned after searching for resource called " + resourceName + " is " + numberOFResources);
+ return Either.left(numberOFResources);
+ }
+ }
+
+ public PropertyOperation getPropertyOperation() {
+ return propertyOperation;
+ }
+
+ public void setPropertyOperation(PropertyOperation propertyOperation) {
+ this.propertyOperation = propertyOperation;
+ }
+
+ public RequirementOperation getRequirementOperation() {
+ return requirementOperation;
+ }
+
+ public void setRequirementOperation(RequirementOperation requirementOperation) {
+ this.requirementOperation = requirementOperation;
+ }
+
+ public CapabilityOperation getCapabilityOperation() {
+ return capabilityOperation;
+ }
+
+ public void setCapabilityOperation(CapabilityOperation capabilityOperation) {
+ this.capabilityOperation = capabilityOperation;
+ }
+
+ public IArtifactOperation getArtifactOperation() {
+ return artifactOperation;
+ }
+
+ public void setArtifactOperation(IArtifactOperation artifactOperation) {
+ this.artifactOperation = artifactOperation;
+ }
+
+ public InterfaceLifecycleOperation getInterfaceLifecycleOperation() {
+ return interfaceLifecycleOperation;
+ }
+
+ public void setInterfaceLifecycleOperation(InterfaceLifecycleOperation interfaceLifecycleOperation) {
+ this.interfaceLifecycleOperation = interfaceLifecycleOperation;
+ }
+
+ public TitanGenericDao getTitanGenericDao() {
+ return titanGenericDao;
+ }
+
+ public IElementOperation getElementOperation() {
+ return elementOperation;
+ }
+
+ public void setElementOperation(IElementOperation elementOperation) {
+ this.elementOperation = elementOperation;
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param titanGenericDao
+ */
+ public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
+ this.titanGenericDao = titanGenericDao;
+ }
+
+ @Override
+ public Either<List<Resource>, StorageOperationStatus> getAllCertifiedResources(boolean isAbstract) {
+
+ return getAllCertifiedResources(isAbstract, null);
+
+ }
+
+ @Override
+ /**
+ * Deletes the resource node, property nodes and relation to artifacts. MUST handle deletion of artifact from artifacts repository outside this method (in catalog-be)
+ */
+ public Either<Resource, StorageOperationStatus> deleteResource(String resourceId, boolean inTransaction) {
+
+ Either<Resource, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+
+ TitanGraph titanGraph = graphResult.left().value();
+ Iterable<TitanVertex> vertecies = titanGraph.query().has(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId).vertices();
+ Either<Resource, StorageOperationStatus> resourceEither = getResource(resourceId, true);
+ Resource resource = resourceEither.left().value();
+ if (vertecies != null && resourceEither.isLeft()) {
+ Iterator<TitanVertex> iterator = vertecies.iterator();
+ if (iterator != null && iterator.hasNext()) {
+ Vertex rootVertex = iterator.next();
+ TitanOperationStatus deleteChildrenNodes = graphDeleteUtil.deleteChildrenNodes(rootVertex, GraphEdgeLabels.PROPERTY);
+ log.debug("After deleting properties nodes in the graph. status is " + deleteChildrenNodes);
+ if (deleteChildrenNodes != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(deleteChildrenNodes));
+ return result;
+ }
+ StorageOperationStatus removeInterfacesFromResource = removeInterfacesFromResource(resource);
+ log.debug("After deleting interfaces nodes in the graph. status is " + removeInterfacesFromResource);
+ if (!removeInterfacesFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeInterfacesFromResource);
+ return result;
+ }
+ StorageOperationStatus removeArtifactsFromResource = removeArtifactsFromResource(resource);
+ log.debug("After deleting artifacts nodes in the graph. status is " + removeArtifactsFromResource);
+ if (!removeArtifactsFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeArtifactsFromResource);
+ return result;
+ }
+ StorageOperationStatus removeCapabilitiesFromResource = removeCapabilitiesFromResource(resource);
+ log.debug("After deleting capabilities nodes in the graph. status is " + removeCapabilitiesFromResource);
+ if (!removeCapabilitiesFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeCapabilitiesFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeRequirementsFromResource = removeRequirementsFromResource(resource);
+ log.debug("After deleting requirements nodes in the graph. status is " + removeRequirementsFromResource);
+ if (!removeRequirementsFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeRequirementsFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeRIsFromResource = removeResourceInstanceFromResource(resource);
+ log.debug("After deleting resource instance nodes in the graph. status is " + removeRIsFromResource);
+ if (!removeRIsFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeRIsFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeAttributesFromResource = removeAttributesFromResource(resource);
+ log.debug("After deleting requirements nodes in the graph. status is " + removeRequirementsFromResource);
+ if (removeAttributesFromResource != StorageOperationStatus.OK) {
+ result = Either.right(removeAttributesFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeInputsFromResource = removeInputsFromComponent(NodeTypeEnum.Resource, resource);
+ log.debug("After deleting requirements nodes in the graph. status is " + removeInputsFromResource);
+ if (removeInputsFromResource != StorageOperationStatus.OK) {
+ result = Either.right(removeInputsFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeAdditionalInformationFromResource = super.deleteAdditionalInformation(NodeTypeEnum.Resource, resource.getUniqueId());
+ log.debug("After deleting additional information node in the graph. status is " + removeAdditionalInformationFromResource);
+ if (!removeAdditionalInformationFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeAdditionalInformationFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeGroupsFromResource = super.deleteGroups(NodeTypeEnum.Resource, resource.getUniqueId());
+ log.debug("After deleting group nodes in the graph. status is " + removeGroupsFromResource);
+ if (!removeGroupsFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeGroupsFromResource);
+ return result;
+ }
+
+ rootVertex.remove();
+
+ } else {
+ result = Either.right(StorageOperationStatus.NOT_FOUND);
+ return result;
+ }
+ } else {
+ result = Either.right(StorageOperationStatus.NOT_FOUND);
+ return result;
+ }
+
+ result = Either.left(resource);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("deleteResource operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("deleteResource operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ private StorageOperationStatus removeAttributesFromResource(Resource resource) {
+ Either<Map<String, AttributeDefinition>, StorageOperationStatus> deleteAllAttributeAssociatedToNode = attributeOperation.deleteAllAttributeAssociatedToNode(NodeTypeEnum.Resource, resource.getUniqueId());
+ return deleteAllAttributeAssociatedToNode.isRight() ? deleteAllAttributeAssociatedToNode.right().value() : StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus removeArtifactsFromResource(Resource resource) {
+
+ String resourceId = resource.getUniqueId();
+ Map<String, ArtifactDefinition> allArtifacts = new HashMap<String, ArtifactDefinition>();
+ if (resource.getArtifacts() != null) {
+ allArtifacts.putAll(resource.getArtifacts());
+ }
+ if (resource.getDeploymentArtifacts() != null) {
+ allArtifacts.putAll(resource.getDeploymentArtifacts());
+ }
+ if (allArtifacts != null) {
+ for (Entry<String, ArtifactDefinition> entry : allArtifacts.entrySet()) {
+
+ ArtifactDefinition artifactDefinition = entry.getValue();
+ Either<ArtifactDefinition, StorageOperationStatus> removeArifactFromResource = artifactOperation.removeArifactFromResource(resourceId, artifactDefinition.getUniqueId(), NodeTypeEnum.Resource, true, true);
+ if (removeArifactFromResource.isRight()) {
+ return removeArifactFromResource.right().value();
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus removeInterfacesFromResource(Resource resource) {
+
+ String resourceId = resource.getUniqueId();
+ // delete only interfaces of this resource (not interfaces derived)
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> allInterfacesOfResource = interfaceLifecycleOperation.getAllInterfacesOfResource(resourceId, false, true);
+ if (allInterfacesOfResource.isRight()) {
+ log.error("failed to get interfaces for resource {}. status is {}", resourceId, allInterfacesOfResource.right().value());
+ return allInterfacesOfResource.right().value();
+ }
+ Map<String, InterfaceDefinition> interfaces = allInterfacesOfResource.left().value();
+ if (interfaces != null) {
+ for (Entry<String, InterfaceDefinition> entry : interfaces.entrySet()) {
+ Boolean isAbstract = resource.isAbstract();
+
+ InterfaceDefinition interfaceDefinition = entry.getValue();
+ // esofer - in case the resource is abstract, we deleting only
+ // the edge to the interface.
+ if (isAbstract != null && true == isAbstract.booleanValue()) {
+ log.debug("Going to dissociate resource {} from interface {}", resourceId, interfaceDefinition.getUniqueId());
+ UniqueIdData uniqueIdData = new UniqueIdData(NodeTypeEnum.Resource, resourceId);
+ Either<InterfaceDefinition, StorageOperationStatus> dissociateInterfaceFromNode = interfaceLifecycleOperation.dissociateInterfaceFromNode(uniqueIdData, interfaceDefinition);
+ if (dissociateInterfaceFromNode.isRight()) {
+ log.error("failed to dissociate resource {} from interface {}. status is {}", resourceId, interfaceDefinition.getUniqueId(), dissociateInterfaceFromNode.right().value());
+ return dissociateInterfaceFromNode.right().value();
+ }
+ } else {
+ Either<InterfaceDefinition, StorageOperationStatus> deleteInterfaceOfResourceOnGraph = interfaceLifecycleOperation.deleteInterfaceOfResourceOnGraph(resourceId, interfaceDefinition, true);
+ if (deleteInterfaceOfResourceOnGraph.isRight()) {
+ return deleteInterfaceOfResourceOnGraph.right().value();
+ }
+ }
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private StorageOperationStatus removeCapabilitiesFromResource(Resource resource) {
+
+ String resourceId = resource.getUniqueId();
+
+ Either<Map<String, CapabilityDefinition>, StorageOperationStatus> deleteAllRes = capabilityOperation.deleteAllCapabilities(resourceId, true);
+ if (deleteAllRes.isRight()) {
+ StorageOperationStatus status = deleteAllRes.right().value();
+ if (status == StorageOperationStatus.NOT_FOUND) {
+ return StorageOperationStatus.OK;
+ }
+ return status;
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ private StorageOperationStatus removeRequirementsFromResource(Resource resource) {
+
+ String resourceId = resource.getUniqueId();
+
+ Either<Map<String, RequirementDefinition>, StorageOperationStatus> deleteAllRes = requirementOperation.deleteAllRequirements(resourceId, true);
+
+ if (deleteAllRes.isRight()) {
+ StorageOperationStatus status = deleteAllRes.right().value();
+ if (status == StorageOperationStatus.NOT_FOUND) {
+ return StorageOperationStatus.OK;
+ }
+ return status;
+ }
+
+ return StorageOperationStatus.OK;
+
+ }
+
+ private StorageOperationStatus removeResourceInstanceFromResource(Resource resource) {
+ String resourceId = resource.getUniqueId();
+
+ Either<List<ComponentInstance>, StorageOperationStatus> deleteAllResourceInstancesRes = componentInstanceOperation.deleteAllComponentInstances(resourceId, NodeTypeEnum.Resource, true);
+ if (deleteAllResourceInstancesRes.isRight()) {
+ StorageOperationStatus status = deleteAllResourceInstancesRes.right().value();
+ if (status == StorageOperationStatus.NOT_FOUND) {
+ return StorageOperationStatus.OK;
+ }
+ return status;
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> updateResource(Resource resource, boolean inTransaction) {
+ return (Either<Resource, StorageOperationStatus>) updateComponent(resource, inTransaction, titanGenericDao, resource.getClass(), NodeTypeEnum.Resource);
+
+ }
+
+ @Override
+ protected <T extends Component> StorageOperationStatus updateDerived(Component component, Component currentComponent, ComponentMetadataData updatedResourceData, Class<T> clazz) {
+ Resource resource = (Resource) component;
+ Resource currentResource = (Resource) currentComponent;
+ if (resource.getDerivedFrom() != null) {// meaning derived from changed
+
+ Either<List<ResourceMetadataData>, StorageOperationStatus> findDerivedResourcesOld = findDerivedResources(currentResource);
+ if (findDerivedResourcesOld.isRight()) {
+ log.debug("Couldn't find derived resource {} for current resource in the graph", currentResource.getDerivedFrom().get(0));
+ return findDerivedResourcesOld.right().value();
+ }
+
+ List<ResourceMetadataData> oldDerived = findDerivedResourcesOld.left().value();
+ if (oldDerived.isEmpty()) {
+ log.debug("Derived from list fetched from DB for current resource is empty");
+ return StorageOperationStatus.PARENT_RESOURCE_NOT_FOUND;
+ }
+
+ Either<List<ResourceMetadataData>, StorageOperationStatus> findDerivedResourcesNew = findDerivedResources((Resource) resource);
+ if (findDerivedResourcesNew.isRight()) {
+ log.debug("Couldn't find derived resource {} for update resource in the graph", resource.getDerivedFrom().get(0));
+ return findDerivedResourcesNew.right().value();
+ }
+
+ List<ResourceMetadataData> newDerived = findDerivedResourcesNew.left().value();
+ if (newDerived.isEmpty()) {
+ log.debug("Derived from list fetched from DB for updated resource is empty");
+ return StorageOperationStatus.PARENT_RESOURCE_NOT_FOUND;
+ }
+
+ Either<Boolean, TitanOperationStatus> reassociateDerivedFrom = reassociateDerivedFrom((ResourceMetadataData) updatedResourceData, oldDerived, newDerived);
+ if (reassociateDerivedFrom.isRight()) {
+ log.debug("Couldn't change derived from for the resoure");
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(reassociateDerivedFrom.right().value());
+ }
+ }
+ return StorageOperationStatus.OK;
+ }
+
+ private Either<Boolean, TitanOperationStatus> reassociateDerivedFrom(ResourceMetadataData resourceData, List<ResourceMetadataData> oldDerived, List<ResourceMetadataData> newDerived) {
+ ResourceMetadataData oldDerivedNode = oldDerived.get(0);
+ log.debug("Dissociating resource {} from old parent resource {}", resourceData.getUniqueId(), oldDerivedNode.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> deleteRelation = titanGenericDao.deleteRelation(resourceData, oldDerivedNode, GraphEdgeLabels.DERIVED_FROM);
+ if (deleteRelation.isRight()) {
+ log.debug("Failed to dissociate resource {} from old parent resource {}", resourceData.getUniqueId(), oldDerivedNode.getUniqueId());
+ return Either.right(deleteRelation.right().value());
+ }
+ ResourceMetadataData newDerivedNode = newDerived.get(0);
+ log.debug("Associating resource {} with new parent resource {}", resourceData.getUniqueId(), newDerivedNode.getUniqueId());
+ Either<GraphRelation, TitanOperationStatus> addRelation = titanGenericDao.createRelation(resourceData, newDerivedNode, GraphEdgeLabels.DERIVED_FROM, null);
+ if (addRelation.isRight()) {
+ log.debug("Failed to associate resource {} with new parent resource {}", resourceData.getUniqueId(), newDerivedNode.getUniqueId());
+ return Either.right(addRelation.right().value());
+ }
+
+ return Either.left(true);
+ }
+
+ private StorageOperationStatus moveCategoryEdge(Resource resource, ResourceMetadataData resourceData, CategoryDefinition newCategory) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+
+ GraphRelation categoryRelation = new GraphRelation();
+ categoryRelation.setType(GraphEdgeLabels.CATEGORY.getProperty());
+ RelationEndPoint relationEndPoint = new RelationEndPoint(NodeTypeEnum.Resource, UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resource.getUniqueId());
+ categoryRelation.setFrom(relationEndPoint);
+ Either<GraphRelation, TitanOperationStatus> deleteOutgoingRelation = titanGenericDao.deleteOutgoingRelation(categoryRelation);
+ if (deleteOutgoingRelation.isRight()) {
+ log.error("Failed to delete category from resource " + resourceData.getUniqueId() + ". Edge type is " + GraphEdgeLabels.CATEGORY);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteOutgoingRelation.right().value());
+ return result;
+ }
+
+ log.debug("After removing edge from graph " + deleteOutgoingRelation);
+
+ return assosiateMetadataToCategory(resource, resourceData);
+ }
+
+ private StorageOperationStatus moveLastModifierEdge(Resource resource, ResourceMetadataData resourceData, UserData modifierUserData) {
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+
+ GraphRelation lastModifierRelation = new GraphRelation();
+ lastModifierRelation.setType(GraphEdgeLabels.LAST_MODIFIER.getProperty());
+ RelationEndPoint relationEndPoint = new RelationEndPoint(NodeTypeEnum.Resource, UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resource.getUniqueId());
+ lastModifierRelation.setTo(relationEndPoint);
+ Either<GraphRelation, TitanOperationStatus> deleteIncomingRelation = titanGenericDao.deleteIncomingRelation(lastModifierRelation);
+ if (deleteIncomingRelation.isRight()) {
+ log.error("Failed to delete user from resource " + resourceData.getUniqueId() + ". Edge type is " + GraphEdgeLabels.LAST_MODIFIER);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteIncomingRelation.right().value());
+ return result;
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(modifierUserData, resourceData, GraphEdgeLabels.LAST_MODIFIER, null);
+ log.debug("After associating user " + modifierUserData + " to resource " + resourceData.getUniqueId() + ". Edge type is " + GraphEdgeLabels.LAST_MODIFIER);
+ if (createRelation.isRight()) {
+ log.error("Failed to associate user " + modifierUserData + " to resource " + resourceData.getUniqueId() + ". Edge type is " + GraphEdgeLabels.LAST_MODIFIER);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(createRelation.right().value());
+ return result;
+ }
+
+ return result;
+ }
+
+ private Either<ResourceMetadataData, TitanOperationStatus> findResource(String resourceId) {
+
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource);
+ Either<ResourceMetadataData, TitanOperationStatus> findResource = titanGenericDao.getNode(key, resourceId, ResourceMetadataData.class);
+
+ return findResource;
+ }
+
+ private StorageOperationStatus setArtifactFromGraph(String uniqueId, Resource resource) {
+ StorageOperationStatus result = StorageOperationStatus.OK;
+ Either<Map<String, ArtifactDefinition>, StorageOperationStatus> artifacts = artifactOperation.getArtifacts(uniqueId, NodeTypeEnum.Resource, true);
+ if (artifacts.isRight()) {
+ result = artifacts.right().value();
+ } else {
+ createSpecificArtifactList(resource, artifacts.left().value());
+ }
+ return result;
+ }
+
+ @Override
+ public <T extends Component> Either<T, StorageOperationStatus> getComponent(String id, Class<T> clazz) {
+
+ Either<Resource, StorageOperationStatus> component = getResource(id);
+ if (component.isRight()) {
+ return Either.right(component.right().value());
+ }
+ return Either.left(clazz.cast(component.left().value()));
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateResourceNameExists(String resourceName, ResourceTypeEnum resourceType) {
+ if (resourceType != null) {
+ Map<String, Object> properties = new HashMap<String, Object>();
+ properties.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+ if (resourceType.equals(ResourceTypeEnum.VF)) {
+ return validateResourceNameUniqueness(resourceName, properties, null, titanGenericDao);
+ } else {
+ return validateResourceNameUniqueness(resourceName, null, properties, titanGenericDao);
+ }
+
+ } else {
+ return validateResourceNameUniqueness(resourceName, null, null, titanGenericDao);
+ }
+
+ }
+
+ public Either<Boolean, StorageOperationStatus> validateToscaResourceNameExists(String templateName) {
+ return validateToscaResourceNameUniqueness(templateName, titanGenericDao);
+ }
+
+ public Either<List<ArtifactDefinition>, StorageOperationStatus> getAdditionalArtifacts(String resourceId, boolean recursively, boolean inTransaction) {
+ List<ArtifactDefinition> artifacts = new ArrayList<>();
+
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> interfacesOfResource = interfaceLifecycleOperation.getAllInterfacesOfResource(resourceId, false, true);
+ if (interfacesOfResource.isRight()) {
+ log.error("failed to get all resource interfaces. resource id={}. status ={}", resourceId, interfacesOfResource.right().value());
+ return Either.right(interfacesOfResource.right().value());
+ }
+
+ Map<String, InterfaceDefinition> interfaces = interfacesOfResource.left().value();
+ if (interfaces != null && !interfaces.isEmpty()) {
+ for (Entry<String, InterfaceDefinition> entry : interfaces.entrySet()) {
+
+ InterfaceDefinition interfaceDefinition = entry.getValue();
+ Map<String, Operation> operations = interfaceDefinition.getOperations();
+ if (operations != null && !operations.isEmpty()) {
+ for (Entry<String, Operation> opEntry : operations.entrySet()) {
+
+ Operation operation = opEntry.getValue();
+ ArtifactDefinition artifactDefinition = operation.getImplementation();
+ if (artifactDefinition != null) {
+ artifacts.add(artifactDefinition);
+ }
+ }
+ }
+ }
+ }
+ return Either.left(artifacts);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Either<List<Resource>, StorageOperationStatus> getFollowed(String userId, Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction) {
+ return (Either<List<Resource>, StorageOperationStatus>) (Either<?, StorageOperationStatus>) getFollowedComponent(userId, lifecycleStates, lastStateStates, inTransaction, titanGenericDao, NodeTypeEnum.Resource);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getResource(id, inTransaction);
+ }
+
+ private Optional<ImmutablePair<SubCategoryData, GraphEdge>> validateCategoryHierarcy(List<ImmutablePair<SubCategoryData, GraphEdge>> childNodes, String subCategoryName) {
+ Predicate<ImmutablePair<SubCategoryData, GraphEdge>> matchName = p -> p.getLeft().getSubCategoryDataDefinition().getName().equals(subCategoryName);
+ return childNodes.stream().filter(matchName).findAny();
+ }
+
+ private Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, StorageOperationStatus> getAllSubCategories(String categoryName) {
+ Either<CategoryData, StorageOperationStatus> categoryResult = elementOperation.getNewCategoryData(categoryName, NodeTypeEnum.ResourceNewCategory, CategoryData.class);
+ if (categoryResult.isRight()) {
+ return Either.right(categoryResult.right().value());
+ }
+ CategoryData categoryData = categoryResult.left().value();
+
+ Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceNewCategory), (String) categoryData.getUniqueId(),
+ GraphEdgeLabels.SUB_CATEGORY, NodeTypeEnum.ResourceSubcategory, SubCategoryData.class);
+ if (childrenNodes.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(childrenNodes.right().value()));
+ }
+ return Either.left(childrenNodes.left().value());
+ }
+
+ @Override
+ public <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters, boolean inTransaction) {
+
+ String subCategoryName = filters.get(FilterKeyEnum.SUB_CATEGORY);
+ String categoryName = filters.get(FilterKeyEnum.CATEGORY);
+ Either<List<ImmutablePair<SubCategoryData, GraphEdge>>, StorageOperationStatus> subcategories = null;
+ Optional<ImmutablePair<SubCategoryData, GraphEdge>> subCategoryData = null;
+
+ if (categoryName != null) {
+ subcategories = getAllSubCategories(categoryName);
+ if (subcategories.isRight()) {
+ filters.remove(FilterKeyEnum.SUB_CATEGORY);
+ return Either.right(subcategories.right().value());
+ }
+ }
+ if (subCategoryName != null) { // primary filter
+ if (categoryName != null) {
+ subCategoryData = validateCategoryHierarcy(subcategories.left().value(), subCategoryName);
+ if (!subCategoryData.isPresent()) {
+ return Either.right(StorageOperationStatus.MATCH_NOT_FOUND);
+ }
+ return fetchByCategoryOrSubCategoryUid((String) subCategoryData.get().getLeft().getUniqueId(), NodeTypeEnum.ResourceSubcategory, GraphEdgeLabels.SUB_CATEGORY.getProperty(), NodeTypeEnum.Resource, inTransaction,
+ ResourceMetadataData.class);
+ }
+
+ return fetchByCategoryOrSubCategoryName(subCategoryName, NodeTypeEnum.ResourceSubcategory, GraphEdgeLabels.SUB_CATEGORY.getProperty(), NodeTypeEnum.Resource, inTransaction, ResourceMetadataData.class);
+ }
+ return fetchByMainCategory(subcategories.left().value(), inTransaction);
+ }
+
+ private <T> Either<List<T>, StorageOperationStatus> fetchByMainCategory(List<ImmutablePair<SubCategoryData, GraphEdge>> subcategories, boolean inTransaction) {
+ List<T> components = new ArrayList<>();
+
+ for (ImmutablePair<SubCategoryData, GraphEdge> subCategory : subcategories) {
+ Either<List<T>, StorageOperationStatus> fetched = fetchByCategoryOrSubCategoryUid((String) subCategory.getLeft().getUniqueId(), NodeTypeEnum.ResourceSubcategory, GraphEdgeLabels.SUB_CATEGORY.getProperty(), NodeTypeEnum.Resource,
+ inTransaction, ResourceMetadataData.class);
+ if (fetched.isRight()) {
+ // return fetched;
+ continue;
+ }
+ components.addAll(fetched.left().value());
+ }
+ return Either.left(components);
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> getLightComponent(String id, boolean inTransaction) {
+ return getLightComponent(id, NodeTypeEnum.Resource, inTransaction);
+ }
+
+ // will be implement later
+ @Override
+ protected ComponentMetadataData getMetaDataFromComponent(Component component) {
+ return getResourceMetaDataFromResource((Resource) component);
+ }
+
+ @Override
+ public Either<Set<Resource>, StorageOperationStatus> getCatalogData(Map<String, Object> propertiesToMatch, boolean inTransaction) {
+ return getComponentCatalogData(NodeTypeEnum.Resource, propertiesToMatch, Resource.class, ResourceMetadataData.class, inTransaction);
+ }
+
+ protected TitanOperationStatus findResourcesPathRecursively(String resourceId, List<ResourceMetadataData> resourcesPathList) {
+
+ Either<ResourceMetadataData, TitanOperationStatus> nodeRes = this.titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, ResourceMetadataData.class);
+
+ if (nodeRes.isRight()) {
+ TitanOperationStatus status = nodeRes.right().value();
+ log.error("Failed to fetch resource {} . status is {}", resourceId, status);
+ return status;
+ }
+
+ ResourceMetadataData resourceData = nodeRes.left().value();
+ resourcesPathList.add(resourceData);
+ Either<ImmutablePair<ResourceMetadataData, GraphEdge>, TitanOperationStatus> parentResourceRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.DERIVED_FROM,
+ NodeTypeEnum.Resource, ResourceMetadataData.class);
+
+ while (parentResourceRes.isLeft()) {
+
+ ImmutablePair<ResourceMetadataData, GraphEdge> value = parentResourceRes.left().value();
+ ResourceMetadataData parentResourceData = value.getKey();
+
+ resourcesPathList.add(parentResourceData);
+
+ parentResourceRes = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), parentResourceData.getMetadataDataDefinition().getUniqueId(), GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource,
+ ResourceMetadataData.class);
+ }
+
+ TitanOperationStatus operationStatus = parentResourceRes.right().value();
+
+ if (operationStatus != TitanOperationStatus.NOT_FOUND) {
+ return operationStatus;
+ } else {
+ return TitanOperationStatus.OK;
+ }
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> updateComponent(T component, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) updateResource((Resource) component, inTransaction);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Either<Component, StorageOperationStatus> deleteComponent(String id, boolean inTransaction) {
+ return (Either<Component, StorageOperationStatus>) (Either<?, StorageOperationStatus>) deleteResource(id, inTransaction);
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> getLatestByToscaResourceName(String toscaResourceName, boolean inTransaction) {
+ return getLatestByName(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty(), toscaResourceName, inTransaction);
+
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> getLatestByName(String resourceName, boolean inTransaction) {
+ return getLatestByName(GraphPropertiesDictionary.NAME.getProperty(), resourceName, inTransaction);
+
+ }
+
+ private Either<Resource, StorageOperationStatus> getLatestByName(String property, String resourceName, boolean inTransaction) {
+ Either<Resource, StorageOperationStatus> result = null;
+ try {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(property, resourceName);
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> highestResources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (highestResources.isRight()) {
+ TitanOperationStatus status = highestResources.right().value();
+ log.debug("failed to find resource with name {}. status={} ", resourceName, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ List<ResourceMetadataData> resources = highestResources.left().value();
+ double version = 0.0;
+ ResourceMetadataData highestResource = null;
+ for (ResourceMetadataData resource : resources) {
+ double resourceVersion = Double.parseDouble(resource.getMetadataDataDefinition().getVersion());
+ if (resourceVersion > version) {
+ version = resourceVersion;
+ highestResource = resource;
+ }
+ }
+ result = getResource(highestResource.getMetadataDataDefinition().getUniqueId(), true);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("getLatestByName operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("getLatestByName operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Either<List<Resource>, StorageOperationStatus> getTesterFollowed(String userId, Set<LifecycleStateEnum> lifecycleStates, boolean inTransaction) {
+ return (Either<List<Resource>, StorageOperationStatus>) (Either<?, StorageOperationStatus>) getTesterFollowedComponent(userId, lifecycleStates, inTransaction, NodeTypeEnum.Resource);
+ }
+
+ @Override
+ public Either<List<Resource>, StorageOperationStatus> getResourceCatalogData(boolean inTransaction) {
+ return getResourceCatalogData(inTransaction, null);
+ }
+
+ private Either<List<Resource>, StorageOperationStatus> getResourceCatalogData(boolean inTransaction, Map<String, Object> otherToMatch) {
+
+ long start = System.currentTimeMillis();
+
+ long startFetchAllStates = System.currentTimeMillis();
+ Map<String, Object> propertiesToMatchHigest = new HashMap<>();
+ propertiesToMatchHigest.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ propertiesToMatchHigest.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), false);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> allHighestStates = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesToMatchHigest, ResourceMetadataData.class);
+ if (allHighestStates.isRight() && allHighestStates.right().value() != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(allHighestStates.right().value()));
+ }
+
+ if (allHighestStates.isRight()) {
+ return Either.left(new ArrayList<>());
+ }
+ List<ResourceMetadataData> list = allHighestStates.left().value();
+
+ List<ResourceMetadataData> certified = new ArrayList<>();
+ List<ResourceMetadataData> noncertified = new ArrayList<>();
+ for (ResourceMetadataData reData : list) {
+ if (reData.getMetadataDataDefinition().getState().equals(LifecycleStateEnum.CERTIFIED.name())) {
+ certified.add(reData);
+ } else {
+ noncertified.add(reData);
+ }
+ }
+
+ long endFetchAll = System.currentTimeMillis();
+ log.debug("Fetch catalog resources all states: certified {}, noncertified {}", certified.size(), noncertified.size());
+ log.debug("Fetch catalog resources all states from graph took {} ms", endFetchAll - startFetchAllStates);
+
+ try {
+ List<ResourceMetadataData> notCertifiedHighest = noncertified;
+ List<ResourceMetadataData> certifiedHighestList = certified;
+
+ HashMap<String, String> VFNames = new HashMap<>();
+ HashMap<String, String> VFCNames = new HashMap<>();
+ for (ResourceMetadataData data : notCertifiedHighest) {
+ String serviceName = data.getMetadataDataDefinition().getName();
+ if (((ResourceMetadataDataDefinition) data.getMetadataDataDefinition()).getResourceType().equals(ResourceTypeEnum.VF)) {
+ VFNames.put(serviceName, serviceName);
+ } else {
+ VFCNames.put(serviceName, serviceName);
+ }
+ }
+
+ for (ResourceMetadataData data : certifiedHighestList) {
+ String serviceName = data.getMetadataDataDefinition().getName();
+ if (((ResourceMetadataDataDefinition) data.getMetadataDataDefinition()).getResourceType().equals(ResourceTypeEnum.VF)) {
+ if (!VFNames.containsKey(serviceName)) {
+ notCertifiedHighest.add(data);
+ }
+ } else {
+ if (!VFCNames.containsKey(serviceName)) {
+ notCertifiedHighest.add(data);
+ }
+ }
+ }
+
+ long endFetchAllFromGraph = System.currentTimeMillis();
+ log.debug("Fetch all catalog resources metadata from graph took {} ms", endFetchAllFromGraph - start);
+
+ long startFetchAllFromCache = System.currentTimeMillis();
+
+ List<Resource> result = new ArrayList<>();
+
+ Map<String, Long> components = notCertifiedHighest.stream().collect(Collectors.toMap(p -> p.getMetadataDataDefinition().getUniqueId(), p -> p.getMetadataDataDefinition().getLastUpdateDate()));
+
+ Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> componentsForCatalog = componentCache.getComponentsForCatalog(components, ComponentTypeEnum.RESOURCE);
+ if (componentsForCatalog.isLeft()) {
+ ImmutablePair<List<Component>, Set<String>> immutablePair = componentsForCatalog.left().value();
+ List<Component> foundComponents = immutablePair.getLeft();
+ if (foundComponents != null) {
+ foundComponents.forEach(p -> result.add((Resource) p));
+ log.debug("The number of resources added to catalog from cache is {}", foundComponents.size());
+
+ List<String> foundComponentsUid = foundComponents.stream().map(p -> p.getUniqueId()).collect(Collectors.toList());
+ notCertifiedHighest = notCertifiedHighest.stream().filter(p -> false == foundComponentsUid.contains(p.getUniqueId())).collect(Collectors.toList());
+ }
+ Set<String> nonCachedComponents = immutablePair.getRight();
+ int numberNonCached = nonCachedComponents == null ? 0 : nonCachedComponents.size();
+ log.debug("The number of left resources for catalog is {}", numberNonCached);
+
+ }
+
+ long endFetchAllFromCache = System.currentTimeMillis();
+ log.debug("Fetch all catalog resources metadata from cache took " + (endFetchAllFromCache - startFetchAllFromCache) + " ms");
+
+ long startFetchFromGraph = System.currentTimeMillis();
+ log.debug("The number of resources needed to be fetch as light component is {}", notCertifiedHighest.size());
+ for (ResourceMetadataData data : notCertifiedHighest) {
+ String uniqueId = data.getMetadataDataDefinition().getUniqueId();
+ log.trace("Fetch catalog resource non cached {} {}", uniqueId, data.getMetadataDataDefinition().getName());
+ Either<Resource, StorageOperationStatus> component = getLightComponent(uniqueId, inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get Service for id = " + data.getUniqueId() + " error : " + component.right().value() + " skip resource");
+ } else {
+ result.add(component.left().value());
+ }
+ }
+ long endFetchFromGraph = System.currentTimeMillis();
+ log.debug("Fetch catalog resources from graph took " + (endFetchFromGraph - startFetchFromGraph) + " ms");
+
+ return Either.left(result);
+
+ } finally {
+ long end = System.currentTimeMillis();
+ log.debug("Fetch all catalog resources took {} ms", end - start);
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceCatalogDataVFLatestCertifiedAndNonCertified(boolean inTransaction) {
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ResourceTypeEnum.VF.name());
+
+ return getResourceCatalogDataLatestCertifiedAndNonCertified(inTransaction, propertiesToMatch);
+ }
+
+ private Either<List<Resource>, StorageOperationStatus> getResourceCatalogDataLatestCertifiedAndNonCertified(boolean inTransaction, Map<String, Object> otherToMatch) {
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+
+ if (otherToMatch != null) {
+ propertiesToMatch.putAll(otherToMatch);
+ }
+
+ propertiesToMatch.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> lastVersionNodes = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesToMatch, ResourceMetadataData.class);
+
+ List<Resource> result = new ArrayList<>();
+
+ if (lastVersionNodes.isRight() && lastVersionNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(lastVersionNodes.right().value()));
+ }
+
+ List<ResourceMetadataData> listOfHighest;
+
+ if (lastVersionNodes.isLeft()) {
+ listOfHighest = lastVersionNodes.left().value();
+ } else {
+ return Either.left(result);
+ }
+
+ for (ResourceMetadataData data : listOfHighest) {
+ Either<Resource, StorageOperationStatus> component = getLightComponent(data.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get Service for id = " + data.getUniqueId() + " error : " + component.right().value() + " skip resource");
+ } else {
+ result.add(component.left().value());
+ }
+ }
+ return Either.left(result);
+ }
+
+ private Either<List<Resource>, StorageOperationStatus> getResourceListByCriteria(Map<String, Object> props, boolean inTransaction) {
+
+ props.put(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName());
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+
+ if (byCriteria.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCriteria.right().value()));
+ }
+ List<Resource> resources = new ArrayList<Resource>();
+ List<ResourceMetadataData> resourcesDataList = byCriteria.left().value();
+ for (ResourceMetadataData data : resourcesDataList) {
+ Either<Resource, StorageOperationStatus> resource = getResource(data.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (resource.isLeft()) {
+ resources.add(resource.left().value());
+ } else {
+ log.debug("Failed to fetch resource for name = " + data.getMetadataDataDefinition().getName() + " and id = " + data.getUniqueId());
+ }
+ }
+ return Either.left(resources);
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceListByUuid(String uuid, boolean inTransaction) {
+ return getLatestResourceByUuid(uuid, false, inTransaction);
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> getLatestResourceByUuid(String uuid, boolean inTransaction) {
+ return getLatestResourceByUuid(uuid, true, inTransaction);
+ }
+
+ private Either<List<Resource>, StorageOperationStatus> getLatestResourceByUuid(String uuid, boolean isLatest, boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ if (isLatest) {
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), isLatest);
+ }
+ props.put(GraphPropertiesDictionary.UUID.getProperty(), uuid);
+ return getResourceListByCriteria(props, inTransaction);
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceListBySystemName(String systemName, boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), systemName);
+ return getResourceListByCriteria(props, inTransaction);
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceListByToscaName(String toscaName, boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty(), toscaName);
+ return getResourceListByCriteria(props, inTransaction);
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> getResourceByNameAndVersion(String name, String version, boolean inTransaction) {
+ return getByNamesAndVersion(GraphPropertiesDictionary.NAME.getProperty(), name, version, null, inTransaction);
+ }
+
+ @Override
+ public Either<List<Resource>, StorageOperationStatus> getResourceByNameAndVersion(String name, String version) {
+ return getResourceByNameAndVersion(name, version, false);
+ }
+
+ protected Either<List<Resource>, StorageOperationStatus> getByNamesAndVersion(String nameKey, String nameValue, String version, Map<String, Object> additionalParams, boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(nameKey, nameValue);
+ props.put(GraphPropertiesDictionary.VERSION.getProperty(), version);
+ props.put(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Resource.getName());
+ if (additionalParams != null && !additionalParams.isEmpty()) {
+ props.putAll(additionalParams);
+ }
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ List<Resource> resourcesList = new ArrayList<Resource>();
+ if (byCriteria.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCriteria.right().value()));
+ }
+ List<ResourceMetadataData> dataList = byCriteria.left().value();
+ if (dataList != null && !dataList.isEmpty()) {
+ // if (dataList.size() > 1) {
+ // log.debug("More that one instance of resource for name =" +
+ // nameValue + " and version = " + version);
+ // return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ // }
+ for (ResourceMetadataData resourceData : dataList) {
+ // ResourceMetadataData resourceData = dataList.get(0);
+ Either<Resource, StorageOperationStatus> resource = getResource(resourceData.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (resource.isRight()) {
+ log.debug("Failed to fetch resource for name = " + resourceData.getMetadataDataDefinition().getName() + " and id = " + resourceData.getUniqueId());
+ return Either.right(resource.right().value());
+ }
+ resourcesList.add(resource.left().value());
+ }
+ // return resource;
+ return Either.left(resourcesList);
+ } else {
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> getComponentByNameAndVersion(String name, String version, Map<String, Object> additionalParams, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getResourceBySystemNameAndVersion(name, version, additionalParams, inTransaction);
+ }
+
+ @Override
+ public Either<Resource, StorageOperationStatus> getResourceBySystemNameAndVersion(String name, String version, Map<String, Object> additionalParams, boolean inTransaction) {
+ Either<List<Resource>, StorageOperationStatus> byNamesAndVersion = getByNamesAndVersion(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(), ValidationUtils.normaliseComponentName(name), version, additionalParams, inTransaction);
+ if (byNamesAndVersion.isRight()) {
+ return Either.right(byNamesAndVersion.right().value());
+ }
+ List<Resource> resourcesList = byNamesAndVersion.left().value();
+ if (resourcesList.size() > 1) {
+ log.debug("More that one instance of resource for name =" + name + " and version = " + version);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ return Either.left(resourcesList.get(0));
+ }
+
+ private TitanOperationStatus setAllVersions(Resource resource) {
+ Either<Map<String, String>, TitanOperationStatus> res = getVersionList(NodeTypeEnum.Resource, resource.getVersion(), resource, ResourceMetadataData.class);
+ if (res.isRight()) {
+ return res.right().value();
+ }
+ resource.setAllVersions(res.left().value());
+ return TitanOperationStatus.OK;
+ }
+
+ @Override
+ protected <T extends GraphNode> Either<Map<String, String>, TitanOperationStatus> getVersionList(NodeTypeEnum type, String version, Component component, Class<T> clazz) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ Map<String, Object> hasNotProps = new HashMap<String, Object>();
+
+ if (version.startsWith("0")) {
+ props.put(GraphPropertiesDictionary.UUID.getProperty(), component.getUUID());
+ } else {
+ props.put(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), component.getSystemName());
+ props.put(GraphPropertiesDictionary.RESOURCE_TYPE.getProperty(), ((Resource) component).getResourceType().name());
+ }
+ hasNotProps.put(GraphPropertiesDictionary.IS_DELETED.getProperty(), true);
+ Either<List<T>, TitanOperationStatus> result = titanGenericDao.getByCriteria(type, props, hasNotProps, clazz);
+
+ Map<String, String> versionMap = new HashMap<String, String>();
+ if (result.isRight()) {
+ if (!result.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.right(result.right().value());
+ }
+
+ } else {
+ List<ResourceMetadataData> components = (List<ResourceMetadataData>) result.left().value();
+ for (ResourceMetadataData data : components) {
+ versionMap.put(data.getMetadataDataDefinition().getVersion(), (String) data.getUniqueId());
+ }
+ }
+
+ return Either.left(versionMap);
+ }
+
+ /**
+ * update only the resource object itself without tag, derived from or any other neighbours.
+ *
+ * @param resource
+ * @param inTransaction
+ * @return
+ */
+ protected Either<Resource, StorageOperationStatus> updateResourceMetadata(Resource resource, boolean inTransaction) {
+
+ Either<Resource, StorageOperationStatus> result = null;
+
+ try {
+
+ log.debug("In updateResource. received resource = " + (resource == null ? null : resource.toString()));
+ if (resource == null) {
+ log.error("Resource object is null");
+ result = Either.right(StorageOperationStatus.BAD_REQUEST);
+ return result;
+ }
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setHighestVersion(resource.isHighestVersion());
+ log.debug("After converting resource to ResourceData. ResourceData = " + resourceData);
+
+ if (resourceData.getUniqueId() == null) {
+ log.error("Resource id is missing in the request.");
+ return Either.right(StorageOperationStatus.BAD_REQUEST);
+ }
+
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanGenericDao.updateNode(resourceData, ResourceMetadataData.class);
+
+ if (updateNode.isRight()) {
+ log.error("Failed to update resource " + resource.getUniqueId() + ". status is " + updateNode.right().value());
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateNode.right().value()));
+ return result;
+ }
+
+ Either<Resource, StorageOperationStatus> updatedResource = getResource(resource.getUniqueId(), true);
+ if (updatedResource.isRight()) {
+ log.error("Resource id is missing in the request. status is " + updatedResource.right().value());
+ result = Either.right(StorageOperationStatus.BAD_REQUEST);
+ return result;
+ }
+
+ Resource updatedResourceValue = updatedResource.left().value();
+ result = Either.left(updatedResourceValue);
+
+ if (log.isDebugEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ log.debug("Resource retrieved after update is " + json);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<List<Resource>, StorageOperationStatus> getAllCertifiedResources(boolean isAbstract, Boolean isHighest) {
+
+ try {
+ List<Resource> result = new ArrayList<>();
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(), isAbstract);
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ if (isHighest != null) {
+ propertiesToMatch.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), isHighest.booleanValue());
+ }
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> resourceNodes = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, propertiesToMatch, ResourceMetadataData.class);
+
+ titanGenericDao.commit();
+ if (resourceNodes.isRight()) {
+ // in case of NOT_FOUND from Titan client return to UI empty
+ // list
+ if (resourceNodes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.left(result);
+ } else {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(resourceNodes.right().value()));
+ }
+ } else {
+ List<ResourceMetadataData> resourceDataList = resourceNodes.left().value();
+ for (ResourceMetadataData resourceData : resourceDataList) {
+ Either<Resource, StorageOperationStatus> resource = getResource(resourceData.getMetadataDataDefinition().getUniqueId());
+ if (resource.isRight()) {
+ log.debug("Failed to fetch resource for id = " + resourceData.getUniqueId() + " error is " + resource.right().value());
+ return Either.right(resource.right().value());
+ }
+ result.add(resource.left().value());
+ }
+ return Either.left(result);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+
+ }
+
+ public Either<Resource, StorageOperationStatus> getLatestCertifiedByToscaResourceName(String toscaResourceName, boolean inTransaction) {
+ return getLatestCertifiedByCriteria(GraphPropertiesDictionary.TOSCA_RESOURCE_NAME.getProperty(), toscaResourceName, inTransaction);
+
+ }
+
+ public Either<Resource, StorageOperationStatus> getLatestCertifiedByCriteria(String property, String resourceName, boolean inTransaction) {
+ Either<Resource, StorageOperationStatus> result = null;
+ try {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(property, resourceName);
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> highestResources = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (highestResources.isRight()) {
+ TitanOperationStatus status = highestResources.right().value();
+ log.debug("failed to find resource with name {}. status={} ", resourceName, status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ List<ResourceMetadataData> resources = highestResources.left().value();
+ double version = 0.0;
+ ResourceMetadataData highestResource = null;
+ for (ResourceMetadataData resource : resources) {
+ double resourceVersion = Double.parseDouble(resource.getMetadataDataDefinition().getVersion());
+ if (resourceVersion > version) {
+ version = resourceVersion;
+ highestResource = resource;
+ }
+ }
+ result = getResource(highestResource.getMetadataDataDefinition().getUniqueId(), true);
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.error("getLatestByName operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("getLatestByName operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+
+ }
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> findLastCertifiedResourceByName(Resource resource) {
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.NAME.getProperty(), resource.getName());
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+ return getResourceListByCriteria(props, false);
+
+ }
+
+ public Either<List<Resource>, StorageOperationStatus> findLastCertifiedResourceByUUID(Resource resource) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.UUID.getProperty(), resource.getUUID());
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+ return getResourceListByCriteria(props, false);
+
+ }
+
+ @Override
+ public boolean isComponentExist(String resourceId) {
+ return isComponentExist(resourceId, NodeTypeEnum.Resource);
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version, LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) cloneResource((Resource) other, version, targetLifecycle, inTransaction);
+ }
+
+ @Override
+ public Either<Integer, StorageOperationStatus> increaseAndGetComponentInstanceCounter(String componentId, boolean inTransaction) {
+ return increaseAndGetComponentInstanceCounter(componentId, NodeTypeEnum.Resource, inTransaction);
+ }
+
+ private Either<Resource, StorageOperationStatus> cloneResource(Resource other, String version, boolean inTransaction) {
+ return cloneResource(other, version, null, inTransaction);
+ }
+
+ private Either<Resource, StorageOperationStatus> cloneResource(Resource other, String version, LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ Either<Resource, StorageOperationStatus> result = null;
+
+ try {
+ String origRsourceId = other.getUniqueId();
+
+ StorageOperationStatus overrideStatus = overrideRecursiveMembers(other, origRsourceId);
+ if (!overrideStatus.equals(StorageOperationStatus.OK)) {
+ return Either.right(overrideStatus);
+ }
+ other.setVersion(version);
+ other.setUniqueId(null);
+
+ List<InputDefinition> inputs = other.getInputs();
+ Map<String, List<ComponentInstanceProperty>> inputsPropMap = new HashMap<String, List<ComponentInstanceProperty>>();
+
+ if (inputs != null) {
+ for (InputDefinition input : inputs) {
+
+ Either<List<ComponentInstanceProperty>, TitanOperationStatus> inputPropStatus = inputOperation.getComponentInstancePropertiesByInputId(input.getUniqueId());
+ if (inputPropStatus.isLeft()) {
+ if (inputPropStatus.left().value() != null)
+ inputsPropMap.put(input.getName(), inputPropStatus.left().value());
+
+ }
+
+ }
+ }
+
+ Either<Resource, StorageOperationStatus> createResourceMD = createResource(other, inTransaction);
+ if (createResourceMD.isRight()) {
+ StorageOperationStatus status = createResourceMD.right().value();
+ log.error("failed to clone resource. status= {}", status);
+ result = Either.right(status);
+ return result;
+ }
+ Resource resource = createResourceMD.left().value();
+ Either<TitanVertex, TitanOperationStatus> metadataVertexEither = titanGenericDao.getVertexByProperty(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), resource.getUniqueId());
+ if (metadataVertexEither.isRight()) {
+ TitanOperationStatus error = metadataVertexEither.right().value();
+ log.debug("Failed to fetch vertex of metadata {} error {}", resource.getUniqueId(), error);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(error));
+ return result;
+ }
+
+ TitanVertex metadataVertex = metadataVertexEither.left().value();
+ Either<ImmutablePair<List<ComponentInstance>, Map<String, String>>, StorageOperationStatus> cloneInstances = componentInstanceOperation.cloneAllComponentInstancesFromContainerComponent(origRsourceId, resource.getUniqueId(),
+ NodeTypeEnum.Resource, NodeTypeEnum.Resource, targetLifecycle, metadataVertex, other, resource, inputsPropMap);
+ if (cloneInstances.isRight()) {
+ result = Either.right(cloneInstances.right().value());
+ return result;
+ }
+
+ Either<Integer, StorageOperationStatus> counterStatus = getComponentInstanceCoutner(origRsourceId, NodeTypeEnum.Resource);
+ if (counterStatus.isRight()) {
+ StorageOperationStatus status = counterStatus.right().value();
+ log.error("failed to get resource instance counter on service {}. status={}", origRsourceId, counterStatus);
+ result = Either.right(status);
+ return result;
+ }
+
+ Either<Integer, StorageOperationStatus> setResourceInstanceCounter = setComponentInstanceCounter(resource.getUniqueId(), NodeTypeEnum.Resource, counterStatus.left().value(), true);
+ if (setResourceInstanceCounter.isRight()) {
+ StorageOperationStatus status = setResourceInstanceCounter.right().value();
+ log.error("failed to set resource instance counter on service {}. status={}", resource.getUniqueId(), status);
+ result = Either.right(status);
+ return result;
+ }
+
+ Either<List<GroupDefinition>, StorageOperationStatus> clonedGroups = cloneGroups(other, resource, cloneInstances.left().value(), true);
+ if (clonedGroups.isRight()) {
+ StorageOperationStatus status = clonedGroups.right().value();
+ if (status != StorageOperationStatus.OK) {
+ result = Either.right(status);
+ return result;
+ }
+ }
+
+ result = this.getResource(resource.getUniqueId(), true);
+ if (result.isRight()) {
+ log.error("Cannot get full service from the graph. status is " + result.right().value());
+ return Either.right(result.right().value());
+ }
+
+ if (log.isDebugEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ // log.debug("Resource retrieved is {}", json);
+ }
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private StorageOperationStatus overrideRecursiveMembers(Resource resource, String prevId) {
+ // override requirements to copy only resource's requirements and not
+ // derived requirements
+ Either<Map<String, List<RequirementDefinition>>, StorageOperationStatus> requirementsOfResourceOnly = getRequirementOperation().getAllRequirementsOfResourceOnly(prevId, true);
+ if (requirementsOfResourceOnly.isRight()) {
+ log.error("failed to get requirements of resource. resourceId {} status is {}", prevId, requirementsOfResourceOnly.right().value());
+ return requirementsOfResourceOnly.right().value();
+ }
+ resource.setRequirements(requirementsOfResourceOnly.left().value());
+
+ // override capabilities to copy only resource's requirements and not
+ // derived requirements
+ Either<Map<String, List<CapabilityDefinition>>, StorageOperationStatus> capabilitiesOfResourceOnly = getResourceCapabilitiesMap(prevId);
+
+ resource.setCapabilities(capabilitiesOfResourceOnly.left().value());
+
+ // override interfaces to copy only resource's interfaces and not
+ // derived interfaces
+ Either<Map<String, InterfaceDefinition>, StorageOperationStatus> interfacesOfResourceOnly = getInterfaceLifecycleOperation().getAllInterfacesOfResource(prevId, false, true);
+ if (interfacesOfResourceOnly.isRight()) {
+ log.error("failed to get interfaces of resource. resourceId {} status is {}", prevId, interfacesOfResourceOnly.right().value());
+ return interfacesOfResourceOnly.right().value();
+ }
+ resource.setInterfaces(interfacesOfResourceOnly.left().value());
+
+ List<AttributeDefinition> attributes = new ArrayList<>();
+ TitanOperationStatus status = attributeOperation.findNodeNonInheretedAttribues(prevId, NodeTypeEnum.Resource, attributes);
+ if (status != TitanOperationStatus.OK) {
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(status);
+ } else {
+ resource.setAttributes(attributes);
+ }
+
+ // override properties to copy only resource's properties and not
+ // derived properties
+ Either<Map<String, PropertyDefinition>, TitanOperationStatus> propertiesOfResourceOnly = getPropertyOperation().findPropertiesOfNode(NodeTypeEnum.Resource, prevId);
+
+ List<PropertyDefinition> resourceProperties = null;
+ if (propertiesOfResourceOnly.isRight()) {
+ TitanOperationStatus titanStatus = propertiesOfResourceOnly.right().value();
+ if (titanStatus != TitanOperationStatus.NOT_FOUND) {
+ log.error("failed to get properties of resource. resourceId {} status is {}", prevId, propertiesOfResourceOnly.right().value());
+ return DaoStatusConverter.convertTitanStatusToStorageStatus(titanStatus);
+ }
+ } else {
+ Map<String, PropertyDefinition> propertiesMap = propertiesOfResourceOnly.left().value();
+ if (propertiesMap != null) {
+ resourceProperties = new ArrayList<PropertyDefinition>();
+ resourceProperties.addAll(propertiesMap.values());
+ }
+ }
+ resource.setProperties(resourceProperties);
+
+ return StorageOperationStatus.OK;
+ }
+
+ private Either<Map<String, List<CapabilityDefinition>>, StorageOperationStatus> getResourceCapabilitiesMap(String prevId) {
+
+ Either<Map<String, CapabilityDefinition>, StorageOperationStatus> capabilitiesOfResourceOnly = getCapabilityOperation().getAllCapabilitiesOfResource(prevId, false, true);
+ if (capabilitiesOfResourceOnly.isRight()) {
+ log.error("failed to get capabilities of resource. resourceId {} status is {}", prevId, capabilitiesOfResourceOnly.right().value());
+ return Either.right(capabilitiesOfResourceOnly.right().value());
+ }
+ Map<String, List<CapabilityDefinition>> capabilityMap = getCapabilityOperation().convertCapabilityMap(capabilitiesOfResourceOnly.left().value(), null, null);
+ return Either.left(capabilityMap);
+ }
+
+ @Override
+ protected StorageOperationStatus validateCategories(Component currentComponent, Component component, ComponentMetadataData componentData, NodeTypeEnum type) {
+ StorageOperationStatus status = StorageOperationStatus.OK;
+ List<CategoryDefinition> newCategoryList = component.getCategories();
+ CategoryDefinition newCategory = newCategoryList.get(0);
+ CategoryDefinition currentCategory = currentComponent.getCategories().get(0);
+ boolean categoryWasChanged = false;
+
+ if (newCategory.getName() != null && false == newCategory.getName().equals(currentCategory.getName())) {
+ // the category was changed
+ categoryWasChanged = true;
+ } else {
+ // the sub-category was changed
+ SubCategoryDefinition currSubcategory = currentCategory.getSubcategories().get(0);
+ SubCategoryDefinition newSubcategory = newCategory.getSubcategories().get(0);
+ if (newSubcategory.getName() != null && false == newSubcategory.getName().equals(currSubcategory.getName())) {
+ log.debug("Going to update the category of the resource from " + currentCategory + " to " + newCategory);
+ categoryWasChanged = true;
+ }
+ }
+ if (categoryWasChanged) {
+ status = moveCategoryEdge((Resource) component, (ResourceMetadataData) componentData, newCategory);
+ log.debug("Going to update the category of the resource from " + currentCategory + " to " + newCategory + ". status is " + status);
+ }
+ return status;
+ }
+
+ @Override
+ public Resource getDefaultComponent() {
+ return new Resource();
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> getMetadataComponent(String id, boolean inTransaction) {
+ return getMetadataComponent(id, NodeTypeEnum.Resource, inTransaction);
+ }
+
+ @Override
+ Component convertComponentMetadataDataToComponent(ComponentMetadataData componentMetadataData) {
+ return convertResourceDataToResource((ResourceMetadataData) componentMetadataData);
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateComponentNameExists(String componentName) {
+ return validateComponentNameUniqueness(componentName, titanGenericDao, NodeTypeEnum.Resource);
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> markComponentToDelete(Component componentToDelete, boolean inTransaction) {
+ return internalMarkComponentToDelete(componentToDelete, inTransaction);
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> isComponentInUse(String componentId) {
+ return isResourceInUse(componentId);
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllComponentsMarkedForDeletion() {
+ return getAllResourcesMarkedForDeletion();
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllResourcesMarkedForDeletion() {
+ return getAllComponentsMarkedForDeletion(NodeTypeEnum.Resource);
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> isResourceInUse(String resourceToDelete) {
+ return isComponentInUse(resourceToDelete, NodeTypeEnum.Resource);
+ }
+
+ public Either<List<GroupDefinition>, StorageOperationStatus> cloneGroups(Resource resource, Resource newResource, ImmutablePair<List<ComponentInstance>, Map<String, String>> cloneInstances, boolean inTransaction) {
+
+ Either<List<GroupDefinition>, StorageOperationStatus> result = null;
+
+ if (resource.getGroups() == null) {
+ return Either.right(StorageOperationStatus.OK);
+ }
+
+ Either<List<GroupDefinition>, StorageOperationStatus> prepareGroupsForCloning = groupOperation.prepareGroupsForCloning(resource, cloneInstances);
+ if (prepareGroupsForCloning.isRight()) {
+ StorageOperationStatus status = prepareGroupsForCloning.right().value();
+ if (status != StorageOperationStatus.OK) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("CloneResource", "Failed to prepare groups for cloning", ErrorSeverity.ERROR);
+ }
+ result = Either.right(status);
+ return result;
+ } else {
+ List<GroupDefinition> groupsToCreate = prepareGroupsForCloning.left().value();
+ if (groupsToCreate != null && false == groupsToCreate.isEmpty()) {
+ Either<List<GroupDefinition>, StorageOperationStatus> addGroups = groupOperation.addGroups(NodeTypeEnum.Resource, newResource.getUniqueId(), groupsToCreate, inTransaction);
+ if (addGroups.isRight()) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("CloneResource", "Failed to clone groups", ErrorSeverity.ERROR);
+ result = Either.right(addGroups.right().value());
+ return result;
+ }
+
+ return Either.left(addGroups.left().value());
+ } else {
+ return Either.right(StorageOperationStatus.OK);
+ }
+ }
+ }
+
+ public Either<Resource, StorageOperationStatus> getLatestResourceByCsarOrName(String csarUUID, String systemName) {
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CSAR_UUID.getProperty(), csarUUID);
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ ResourceMetadataData resourceMetadataData = null;
+ List<ResourceMetadataData> resourceMetadataDataList = null;
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCsar = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (byCsar.isRight()) {
+ if (TitanOperationStatus.NOT_FOUND.equals(byCsar.right().value())) {
+ props.clear();
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ props.put(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), systemName);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> bySystemname = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (bySystemname.isRight()) {
+ log.debug("getLatestResourceByCsarOrName - Failed to find by system name {} error {} ", systemName, bySystemname.right().value());
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(bySystemname.right().value()));
+ }
+ if (bySystemname.left().value().size() > 2) {
+ log.debug("getLatestResourceByCsarOrName - getByCriteria(by system name) must return only 2 latest version, but was returned - " + bySystemname.left().value().size());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ resourceMetadataDataList = bySystemname.left().value();
+ if (resourceMetadataDataList.size() == 1) {
+ resourceMetadataData = resourceMetadataDataList.get(0);
+ } else {
+ for (ResourceMetadataData curResource : resourceMetadataDataList) {
+ if (!curResource.getMetadataDataDefinition().getState().equals("CERTIFIED")) {
+ resourceMetadataData = curResource;
+ break;
+ }
+ }
+ }
+ if (resourceMetadataData == null) {
+ log.debug("getLatestResourceByCsarOrName - getByCriteria(by system name) returned 2 latest CERTIFIED versions");
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ if (resourceMetadataData.getMetadataDataDefinition().getCsarUUID() != null && !resourceMetadataData.getMetadataDataDefinition().getCsarUUID().equals(csarUUID)) {
+ log.debug("getLatestResourceByCsarOrName - same system name {} but different csarUUID. exist {} and new {} ", systemName, resourceMetadataData.getMetadataDataDefinition().getCsarUUID(), csarUUID);
+ // correct error will be returned from create flow. with all
+ // correct audit records!!!!!
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+ Either<Resource, StorageOperationStatus> resource = getResource((String) resourceMetadataData.getUniqueId());
+ return resource;
+ }
+ } else {
+ resourceMetadataDataList = byCsar.left().value();
+ if (resourceMetadataDataList.size() > 2) {
+ log.debug("getLatestResourceByCsarOrName - getByCriteria(by csar) must return only 2 latest version, but was returned - " + byCsar.left().value().size());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ if (resourceMetadataDataList.size() == 1) {
+ resourceMetadataData = resourceMetadataDataList.get(0);
+ } else {
+ for (ResourceMetadataData curResource : resourceMetadataDataList) {
+ if (!curResource.getMetadataDataDefinition().getState().equals("CERTIFIED")) {
+ resourceMetadataData = curResource;
+ break;
+ }
+ }
+ }
+ if (resourceMetadataData == null) {
+ log.debug("getLatestResourceByCsarOrName - getByCriteria(by csar) returned 2 latest CERTIFIED versions");
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ Either<Resource, StorageOperationStatus> resource = getResource((String) resourceMetadataData.getMetadataDataDefinition().getUniqueId());
+ return resource;
+ }
+ return null;
+ }
+
+ public Either<List<ResourceMetadataData>, StorageOperationStatus> validateCsarUuidUniqueness(String csarUUID) {
+
+ Map<String, Object> props = new HashMap<>();
+ props.put(GraphPropertiesDictionary.CSAR_UUID.getProperty(), csarUUID);
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCsar = titanGenericDao.getByCriteria(NodeTypeEnum.Resource, props, ResourceMetadataData.class);
+ if (byCsar.isRight()) {
+ if (TitanOperationStatus.NOT_FOUND.equals(byCsar.right().value())) {
+ return Either.left(null);
+ } else {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCsar.right().value()));
+ }
+ }
+ return Either.left(byCsar.left().value());
+ }
+
+ private Either<Resource, StorageOperationStatus> getResource(String uniqueId, ComponentParametersView componentParametersView, boolean inTransaction) {
+
+ Resource resource = null;
+ try {
+
+ NodeTypeEnum resourceNodeType = NodeTypeEnum.Resource;
+ NodeTypeEnum compInstNodeType = NodeTypeEnum.Resource;
+
+ Either<ResourceMetadataData, StorageOperationStatus> componentByLabelAndId = getComponentByLabelAndId(uniqueId, resourceNodeType, ResourceMetadataData.class);
+ if (componentByLabelAndId.isRight()) {
+ return Either.right(componentByLabelAndId.right().value());
+ }
+ ResourceMetadataData resourceData = componentByLabelAndId.left().value();
+
+ // Try to fetch resource from the cache. The resource will be
+ // fetched only if the time on the cache equals to
+ // the time on the graph.
+ Either<Resource, ActionStatus> componentFromCacheIfUpToDate = this.getComponentFromCacheIfUpToDate(uniqueId, resourceData, componentParametersView, Resource.class, ComponentTypeEnum.RESOURCE);
+ if (componentFromCacheIfUpToDate.isLeft()) {
+ Resource cachedResource = componentFromCacheIfUpToDate.left().value();
+ log.debug("Resource {} with uid {} was fetched from cache.", cachedResource.getName(), cachedResource.getUniqueId());
+ return Either.left(cachedResource);
+ }
+
+ resource = convertResourceDataToResource(resourceData);
+
+ TitanOperationStatus status = null;
+ if (false == componentParametersView.isIgnoreUsers()) {
+ status = setResourceCreatorFromGraph(resource, uniqueId);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ status = setResourceLastModifierFromGraph(resource, uniqueId);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreProperties()) {
+ status = setResourcePropertiesFromGraph(uniqueId, resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreAttributesFrom()) {
+ status = setResourceAttributesFromGraph(uniqueId, resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreDerivedFrom()) {
+ status = setResourceDerivedFromGraph(uniqueId, resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreCategories()) {
+ status = setComponentCategoriesFromGraph(resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ // Since capabilities and requirements and instances properties are
+ // based on component instances, then we must fetch the instances.
+ if (false == componentParametersView.isIgnoreComponentInstances() || false == componentParametersView.isIgnoreComponentInstancesProperties() || false == componentParametersView.isIgnoreComponentInstancesInputs()
+ || false == componentParametersView.isIgnoreCapabilities() || false == componentParametersView.isIgnoreRequirements()) {
+
+ status = setComponentInstancesFromGraph(uniqueId, resource, resourceNodeType, compInstNodeType);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreRequirements()) {
+ StorageOperationStatus setRequirementsStatus = setResourceRequirementsFromGraph(uniqueId, resource, true);
+ if (setRequirementsStatus != StorageOperationStatus.OK) {
+ log.error("Failed to set requirement of resource " + uniqueId + ". status is " + setRequirementsStatus);
+ return Either.right(setRequirementsStatus);
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreInputs()) {
+ status = setComponentInputsFromGraph(uniqueId, resource, true);
+ if (status != TitanOperationStatus.OK) {
+ log.error("Failed to set inputs of resource " + uniqueId + ". status is " + status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ }
+
+ StorageOperationStatus storageStatus = null;
+ if (false == componentParametersView.isIgnoreCapabilities()) {
+ storageStatus = setResourceCapabilitiesFromGraph(uniqueId, resource);
+ if (storageStatus != StorageOperationStatus.OK) {
+ return Either.right(storageStatus);
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreArtifacts()) {
+ storageStatus = setArtifactFromGraph(uniqueId, resource);
+ if (storageStatus != StorageOperationStatus.OK) {
+ return Either.right(storageStatus);
+ }
+ }
+ if (false == componentParametersView.isIgnoreComponentInstancesAttributesFrom()) {
+ status = setComponentInstancesAttributesFromGraph(uniqueId, resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreComponentInstancesProperties()) {
+ status = setComponentInstancesPropertiesFromGraph(uniqueId, resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreComponentInstancesInputs()) {
+ status = setComponentInstancesInputsFromGraph(uniqueId, resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreInterfaces()) {
+ storageStatus = setResourceInterfacesFromGraph(uniqueId, resource);
+ if (storageStatus != StorageOperationStatus.OK) {
+ return Either.right(storageStatus);
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreAdditionalInformation()) {
+ storageStatus = setResourceAdditionalInformationFromGraph(uniqueId, resource);
+ if (storageStatus != StorageOperationStatus.OK) {
+ return Either.right(storageStatus);
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreAllVersions()) {
+ status = setAllVersions(resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreGroups()) {
+ status = setGroupsFromGraph(uniqueId, resource, NodeTypeEnum.Resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (true == componentParametersView.isIgnoreComponentInstances()) {
+ resource.setComponentInstances(null);
+ resource.setComponentInstancesRelations(null);
+ }
+
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ }
+
+ return Either.left(resource);
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id, ComponentParametersView componentParametersView, boolean inTrasnaction) {
+
+ Either<Resource, StorageOperationStatus> component = getResource(id, componentParametersView, inTrasnaction);
+ if (component.isRight()) {
+ return Either.right(component.right().value());
+ }
+ return (Either<T, StorageOperationStatus>) component;
+ }
+
+ // @Override
+ public Either<Resource, StorageOperationStatus> updateResource(Resource resource, boolean inTransaction, ComponentParametersView filterResultView) {
+ return (Either<Resource, StorageOperationStatus>) updateComponentFilterResult(resource, inTransaction, titanGenericDao, resource.getClass(), NodeTypeEnum.Resource, filterResultView);
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> updateComponentFilterResult(T component, boolean inTransaction, ComponentParametersView filterResultView) {
+ return (Either<T, StorageOperationStatus>) updateResource((Resource) component, inTransaction, filterResultView);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperation.java
new file mode 100644
index 0000000000..18229f9245
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperation.java
@@ -0,0 +1,1566 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.graph.datatype.RelationEndPoint;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.AdditionalInformationDefinition;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.ServiceMetadataDefinition;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IArtifactOperation;
+import org.openecomp.sdc.be.model.operations.api.IElementOperation;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+
+import fj.data.Either;
+
+@org.springframework.stereotype.Component("service-operation")
+public class ServiceOperation extends ComponentOperation implements IServiceOperation {
+
+ private static Logger log = LoggerFactory.getLogger(ServiceOperation.class.getName());
+
+ @Resource
+ private IArtifactOperation artifactOperation;
+
+ @Resource
+ private IElementOperation elementOperation;
+
+ public ServiceOperation() {
+ log.debug("ServiceOperation created");
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> createService(Service service) {
+ return createService(service, false);
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> createService(Service service, boolean inTransaction) {
+ Either<Service, StorageOperationStatus> result = null;
+
+ try {
+
+ ServiceMetadataData serviceData = getServiceMetaDataFromService(service);
+ addComponentInternalFields(serviceData);
+ String uniqueId = (String) serviceData.getUniqueId();
+ generateUUID(service);
+
+ String userId = service.getCreatorUserId();
+
+ Either<UserData, TitanOperationStatus> findUser = findUser(userId);
+
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user " + userId + " in the graph. status is " + status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ }
+
+ UserData creatorUserData = findUser.left().value();
+ UserData updaterUserData = creatorUserData;
+ String updaterUserId = service.getLastUpdaterUserId();
+ if (updaterUserId != null && !updaterUserId.equals(userId)) {
+ findUser = findUser(updaterUserId);
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user " + userId + " in the graph. status is " + status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ } else {
+ updaterUserData = findUser.left().value();
+ }
+ }
+
+ // get category
+ List<CategoryDefinition> categories = service.getCategories();
+ CategoryData categoryData = null;
+
+ String categoryName = categories.get(0).getName();
+ if (categoryName != null) {
+ Either<CategoryData, StorageOperationStatus> categoryResult = elementOperation
+ .getNewCategoryData(categoryName, NodeTypeEnum.ServiceNewCategory, CategoryData.class);
+ if (categoryResult.isRight()) {
+ StorageOperationStatus status = categoryResult.right().value();
+ /*
+ * TitanOperationStatus titanStatus = null;
+ * if(ActionStatus.CATEGORY_NOT_FOUND.equals(status)){
+ * titanStatus = TitanOperationStatus.NOT_FOUND; }else{
+ * titanStatus = TitanOperationStatus.GENERAL_ERROR; }
+ */
+ log.error("Cannot find category " + categoryName + " in the graph. status is " + status);
+ return Either.right(status);
+ }
+
+ categoryData = categoryResult.left().value();
+ }
+
+ StorageOperationStatus storageOperationStatus = createTagsForComponent(service);
+ if (storageOperationStatus != StorageOperationStatus.OK) {
+ return Either.right(storageOperationStatus);
+ }
+
+ log.debug("try to create service node on graph for id " + serviceData.getUniqueId());
+ Either<ServiceMetadataData, TitanOperationStatus> createNode = titanGenericDao.createNode(serviceData,
+ ServiceMetadataData.class);
+ if (createNode.isRight()) {
+ TitanOperationStatus status = createNode.right().value();
+ log.error("Error returned after creating service data node " + serviceData + ". status returned is "
+ + status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ log.debug("create service node created on graph for id " + serviceData.getUniqueId());
+
+ TitanOperationStatus associateMetadata = associateMetadataToComponent(serviceData, creatorUserData,
+ updaterUserData, null, null);
+ if (associateMetadata != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateMetadata));
+ return result;
+ }
+ TitanOperationStatus associateCategory = associateMetadataCategoryToComponent(serviceData, categoryData);
+ if (associateCategory != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateCategory));
+ return result;
+ }
+
+ Map<String, ArtifactDefinition> allArtifacts = new HashMap<String, ArtifactDefinition>();
+ if (service.getArtifacts() != null) {
+ allArtifacts.putAll(service.getArtifacts());
+ }
+ if (service.getDeploymentArtifacts() != null) {
+ allArtifacts.putAll(service.getDeploymentArtifacts());
+ }
+ if (service.getServiceApiArtifacts() != null) {
+ allArtifacts.putAll(service.getServiceApiArtifacts());
+ }
+ if (service.getToscaArtifacts() != null) {
+ allArtifacts.putAll(service.getToscaArtifacts());
+ }
+
+ StorageOperationStatus associateArtifacts = associateArtifactsToComponent(NodeTypeEnum.Service, serviceData,
+ allArtifacts);
+ if (associateArtifacts != StorageOperationStatus.OK) {
+ result = Either.right(associateArtifacts);
+ return result;
+ }
+
+ TitanOperationStatus associateInputs = associateInputsToComponent(NodeTypeEnum.Service, serviceData,
+ service.getInputs());
+ if (associateInputs != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(associateInputs));
+ return result;
+ }
+
+ List<AdditionalInformationDefinition> additionalInformation = service.getAdditionalInformation();
+ StorageOperationStatus addAdditionalInformation = addAdditionalInformationToService(uniqueId,
+ additionalInformation);
+ if (addAdditionalInformation != StorageOperationStatus.OK) {
+ result = Either.right(addAdditionalInformation);
+ return result;
+ }
+
+ result = this.getService(uniqueId, true);
+ if (result.isRight()) {
+ log.error("Cannot get full service from the graph. status is " + result.right().value());
+ return Either.right(result.right().value());
+ }
+
+ if (log.isDebugEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ log.debug("Service retrieved is " + json);
+ }
+
+ return result;
+
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private TitanOperationStatus associateMetadataCategoryToComponent(ServiceMetadataData serviceData,
+ CategoryData categoryData) {
+ Either<GraphRelation, TitanOperationStatus> result;
+ if (categoryData != null) {
+ result = titanGenericDao.createRelation(serviceData, categoryData, GraphEdgeLabels.CATEGORY, null);
+ log.debug("After associating component " + serviceData.getUniqueId() + " to category " + categoryData
+ + ". Edge type is " + GraphEdgeLabels.CATEGORY);
+ if (result.isRight()) {
+ log.error("Faield to associate component " + serviceData.getUniqueId() + " to category " + categoryData
+ + ". Edge type is " + GraphEdgeLabels.CATEGORY);
+ return result.right().value();
+ }
+ }
+ return TitanOperationStatus.OK;
+ }
+
+ private StorageOperationStatus addAdditionalInformationToService(String resourceUniqueId,
+ List<AdditionalInformationDefinition> additionalInformation) {
+
+ StorageOperationStatus result = null;
+
+ if (additionalInformation == null || true == additionalInformation.isEmpty()) {
+ result = super.addAdditionalInformation(NodeTypeEnum.Service, resourceUniqueId, null);
+ } else {
+ if (additionalInformation.size() == 1) {
+ result = super.addAdditionalInformation(NodeTypeEnum.Service, resourceUniqueId,
+ additionalInformation.get(0));
+ } else {
+ result = StorageOperationStatus.BAD_REQUEST;
+ log.info(
+ "Cannot create resource with more than one additional information object. The number of received object is "
+ + additionalInformation.size());
+ }
+ }
+ return result;
+ }
+
+ public Either<Service, StorageOperationStatus> cloneService(Service other, String version, boolean inTransaction) {
+ return cloneService(other, version, null, inTransaction);
+ }
+
+ public Either<Service, StorageOperationStatus> cloneService(Service other, String version,
+ LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ Either<Service, StorageOperationStatus> result = null;
+
+ try {
+ String origServiceId = other.getUniqueId();
+ other.setVersion(version);
+ other.setUniqueId(null);
+
+ Either<Integer, StorageOperationStatus> counterStatus = getComponentInstanceCoutner(origServiceId,
+ NodeTypeEnum.Service);
+ if (counterStatus.isRight()) {
+ StorageOperationStatus status = counterStatus.right().value();
+ log.error("failed to get resource instance counter on service {}. status={}", origServiceId,
+ counterStatus);
+ result = Either.right(status);
+ return result;
+ }
+ Map<String, List<ComponentInstanceInput>> inputsValuesMap = new HashMap<String, List<ComponentInstanceInput>>();
+ List<InputDefinition> inputs = other.getInputs();
+ if(inputs != null){
+ for(InputDefinition input: inputs){
+
+ Either<List<ComponentInstanceInput>, TitanOperationStatus> inputStatus = inputOperation.getComponentInstanceInputsByInputId(input.getUniqueId());
+
+ if(inputStatus.isLeft()){
+ if(inputStatus.left().value() != null)
+ inputsValuesMap.put(input.getName(), inputStatus.left().value());
+ }
+ }
+ }
+
+ Either<Service, StorageOperationStatus> createServiceMD = createService(other, true);
+
+ if (createServiceMD.isRight()) {
+ StorageOperationStatus status = createServiceMD.right().value();
+ log.error("failed to clone service. status= {}", status);
+ result = Either.right(status);
+ return result;
+ }
+
+ Service service = createServiceMD.left().value();
+
+ Either<ImmutablePair<List<ComponentInstance>, Map<String, String>>, StorageOperationStatus> cloneInstances = componentInstanceOperation.cloneAllComponentInstancesFromContainerComponent(origServiceId, service,
+ NodeTypeEnum.Service, NodeTypeEnum.Resource, targetLifecycle, inputsValuesMap);
+ if (cloneInstances.isRight()) {
+ result = Either.right(cloneInstances.right().value());
+ return result;
+ }
+
+ Either<Integer, StorageOperationStatus> setResourceInstanceCounter = setComponentInstanceCounter(
+ service.getUniqueId(), NodeTypeEnum.Service, counterStatus.left().value(), true);
+ if (setResourceInstanceCounter.isRight()) {
+ StorageOperationStatus status = setResourceInstanceCounter.right().value();
+ log.error("failed to set resource instance counter on service {}. status={}", service.getUniqueId(),
+ setResourceInstanceCounter);
+ result = Either.right(status);
+ return result;
+ }
+
+ result = this.getService(service.getUniqueId(), true);
+ if (result.isRight()) {
+ log.error("Cannot get full service from the graph. status is " + result.right().value());
+ return Either.right(result.right().value());
+ }
+
+ if (log.isTraceEnabled()) {
+ String json = prettyJson.toJson(result.left().value());
+ log.trace("Resource retrieved is {}", json);
+ }
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ private ServiceMetadataData getServiceMetaDataFromService(Service service) {
+ ServiceMetadataData serviceData = new ServiceMetadataData(
+ (ServiceMetadataDataDefinition) service.getComponentMetadataDefinition().getMetadataDataDefinition());
+ if (service.getNormalizedName() == null || service.getNormalizedName().isEmpty()) {
+ serviceData.getMetadataDataDefinition()
+ .setNormalizedName(ValidationUtils.normaliseComponentName(service.getName()));
+ }
+ if (service.getSystemName() == null || service.getSystemName().isEmpty()) {
+ serviceData.getMetadataDataDefinition()
+ .setSystemName(ValidationUtils.convertToSystemName(service.getName()));
+ }
+
+ return serviceData;
+ }
+
+ private Either<Service, StorageOperationStatus> sendError(TitanOperationStatus status,
+ StorageOperationStatus statusIfNotFound) {
+ Either<Service, StorageOperationStatus> result;
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ result = Either.right(statusIfNotFound);
+ return result;
+ } else {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+
+ /**
+ *
+ */
+ public Either<Service, StorageOperationStatus> getService(String uniqueId) {
+ return getService(uniqueId, false);
+ }
+
+ public Either<Service, StorageOperationStatus> getService(String uniqueId, boolean inTransaction) {
+ ComponentParametersView componentParametersView = new ComponentParametersView();
+ return getService(uniqueId, componentParametersView, inTransaction);
+ }
+ // public Either<Service, StorageOperationStatus> getService(String
+ // uniqueId, boolean inTransaction) {
+ //
+ // Service service = null;
+ // Either<Service, StorageOperationStatus> result = null;
+ // try {
+ //
+ // NodeTypeEnum serviceNodeType = NodeTypeEnum.Service;
+ // NodeTypeEnum compInstNodeType = NodeTypeEnum.Resource;
+ //
+ // Either<ServiceMetadataData, StorageOperationStatus> getComponentByLabel =
+ // getComponentByLabelAndId(uniqueId, serviceNodeType,
+ // ServiceMetadataData.class);
+ // if (getComponentByLabel.isRight()) {
+ // result = Either.right(getComponentByLabel.right().value());
+ // return result;
+ // }
+ // ServiceMetadataData serviceData = getComponentByLabel.left().value();
+ // service = convertServiceDataToService(serviceData);
+ //
+ // TitanOperationStatus status = setComponentCreatorFromGraph(service,
+ // uniqueId, serviceNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setComponentLastModifierFromGraph(service, uniqueId,
+ // serviceNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ // status = setComponentCategoriesFromGraph(service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ //
+ // // status = setServicePropertiesFromGraph(uniqueId, resource, vertex);
+ // // if (status != TitanOperationStatus.OK) {
+ // // return
+ // Either.right(TitanStatusConverter.convertTitanStatusToStorageStatus(status));
+ // // }
+ //
+ // StorageOperationStatus storageStatus = setArtifactFromGraph(uniqueId,
+ // service, serviceNodeType, artifactOperation);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // result = Either.right(storageStatus);
+ // return result;
+ // }
+ //
+ // status = setComponentInstancesFromGraph(uniqueId, service,
+ // serviceNodeType, compInstNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ //
+ // status = setComponentInstancesPropertiesFromGraph(uniqueId, service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setCapabilitiesFromGraph(uniqueId, service,
+ // NodeTypeEnum.Service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setRequirementsFromGraph( uniqueId, service,
+ // NodeTypeEnum.Service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setAllVersions(service);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setServiceAdditionalInformationFromGraph(uniqueId, service);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setGroupsFromGraph(uniqueId, service, NodeTypeEnum.Resource);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // result = Either.left(service);
+ // return result;
+ // } finally {
+ // if (false == inTransaction) {
+ // if (result == null || result.isRight()) {
+ // titanGenericDao.rollback();
+ // } else {
+ // titanGenericDao.commit();
+ // }
+ // }
+ // }
+ // }
+
+ public Either<Service, StorageOperationStatus> getService(String uniqueId,
+ ComponentParametersView componentParametersView, boolean inTransaction) {
+
+ Service service = null;
+ Either<Service, StorageOperationStatus> result = null;
+ try {
+
+ NodeTypeEnum serviceNodeType = NodeTypeEnum.Service;
+ NodeTypeEnum compInstNodeType = NodeTypeEnum.Resource;
+
+ Either<ServiceMetadataData, StorageOperationStatus> getComponentByLabel = getComponentByLabelAndId(uniqueId,
+ serviceNodeType, ServiceMetadataData.class);
+ if (getComponentByLabel.isRight()) {
+ result = Either.right(getComponentByLabel.right().value());
+ return result;
+ }
+ ServiceMetadataData serviceData = getComponentByLabel.left().value();
+ // Try to fetch resource from the cache. The resource will be
+ // fetched only if the time on the cache equals to
+ // the time on the graph.
+ Either<Service, ActionStatus> componentFromCacheIfUpToDate = this.getComponentFromCacheIfUpToDate(uniqueId,
+ serviceData, componentParametersView, Service.class, ComponentTypeEnum.SERVICE);
+ if (componentFromCacheIfUpToDate.isLeft()) {
+ Service cachedService = componentFromCacheIfUpToDate.left().value();
+ log.debug("Service {} with uid {} was fetched from cache.", cachedService.getName(),
+ cachedService.getUniqueId());
+ return Either.left(cachedService);
+ }
+
+ service = convertServiceDataToService(serviceData);
+ TitanOperationStatus status = null;
+ if (false == componentParametersView.isIgnoreUsers()) {
+ status = setComponentCreatorFromGraph(service, uniqueId, serviceNodeType);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ status = setComponentLastModifierFromGraph(service, uniqueId, serviceNodeType);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+
+ }
+ }
+ if (false == componentParametersView.isIgnoreCategories()) {
+ status = setComponentCategoriesFromGraph(service);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+
+ }
+ }
+
+ // status = setServicePropertiesFromGraph(uniqueId, resource,
+ // vertex);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(TitanStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+
+ if (false == componentParametersView.isIgnoreArtifacts()) {
+ StorageOperationStatus storageStatus = setArtifactFromGraph(uniqueId, service, serviceNodeType,
+ artifactOperation);
+ if (storageStatus != StorageOperationStatus.OK) {
+ result = Either.right(storageStatus);
+ return result;
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreComponentInstances()
+ || false == componentParametersView.isIgnoreComponentInstancesProperties()
+ || false == componentParametersView.isIgnoreCapabilities()
+ || false == componentParametersView.isIgnoreRequirements()) {
+ status = setComponentInstancesFromGraph(uniqueId, service, serviceNodeType, compInstNodeType);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+
+ }
+ }
+ if (false == componentParametersView.isIgnoreComponentInstancesProperties()) {
+ status = setComponentInstancesPropertiesFromGraph(uniqueId, service);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+ if (false == componentParametersView.isIgnoreCapabilities()) {
+ status = setCapabilitiesFromGraph(uniqueId, service, NodeTypeEnum.Service);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+ if (false == componentParametersView.isIgnoreRequirements()) {
+ status = setRequirementsFromGraph(uniqueId, service, NodeTypeEnum.Service);
+ if (status != TitanOperationStatus.OK) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+ if (false == componentParametersView.isIgnoreAllVersions()) {
+ status = setAllVersions(service);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+ if (false == componentParametersView.isIgnoreAdditionalInformation()) {
+ status = setServiceAdditionalInformationFromGraph(uniqueId, service);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+
+ if (false == componentParametersView.isIgnoreGroups()) {
+ status = setGroupsFromGraph(uniqueId, service, NodeTypeEnum.Resource);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+ }
+ if (false == componentParametersView.isIgnoreInputs()) {
+ status = setComponentInputsFromGraph(uniqueId, service, true);
+ if (status != TitanOperationStatus.OK) {
+ log.error("Failed to set inputs of resource " + uniqueId + ". status is " + status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ }
+
+ if (false == componentParametersView.isIgnoreComponentInstancesInputs()) {
+ status = setComponentInstancesInputsFromGraph(uniqueId, service);
+ if (status != TitanOperationStatus.OK) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+
+ }
+ }
+
+ result = Either.left(service);
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ titanGenericDao.rollback();
+ } else {
+ titanGenericDao.commit();
+ }
+ }
+ }
+ }
+
+ // public Either<Service, StorageOperationStatus> getService_tx(String
+ // uniqueId, boolean inTransaction) {
+ //
+ // Service service = null;
+ // Either<Service, StorageOperationStatus> result = null;
+ // try {
+ //
+ // NodeTypeEnum serviceNodeType = NodeTypeEnum.Service;
+ // NodeTypeEnum compInstNodeType = NodeTypeEnum.Resource;
+ //
+ // Either<ServiceMetadataData, StorageOperationStatus> getComponentByLabel =
+ // getComponentByLabelAndId_tx(uniqueId, serviceNodeType,
+ // ServiceMetadataData.class);
+ // if (getComponentByLabel.isRight()) {
+ // result = Either.right(getComponentByLabel.right().value());
+ // return result;
+ // }
+ // ServiceMetadataData serviceData = getComponentByLabel.left().value();
+ // service = convertServiceDataToService(serviceData);
+ //
+ // TitanOperationStatus status = setComponentCreatorFromGraph(service,
+ // uniqueId, serviceNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setComponentLastModifierFromGraph(service, uniqueId,
+ // serviceNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ // status = setComponentCategoriesFromGraph(service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ //
+ // // status = setServicePropertiesFromGraph(uniqueId, resource, vertex);
+ // // if (status != TitanOperationStatus.OK) {
+ // // return
+ // Either.right(TitanStatusConverter.convertTitanStatusToStorageStatus(status));
+ // // }
+ //
+ // StorageOperationStatus storageStatus = setArtifactFromGraph(uniqueId,
+ // service, serviceNodeType, artifactOperation);
+ // if (storageStatus != StorageOperationStatus.OK) {
+ // result = Either.right(storageStatus);
+ // return result;
+ // }
+ //
+ // status = setComponentInstancesFromGraph(uniqueId, service,
+ // serviceNodeType, compInstNodeType);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ //
+ // }
+ //
+ // status = setComponentInstancesPropertiesFromGraph(uniqueId, service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setCapabilitiesFromGraph(uniqueId, service,
+ // NodeTypeEnum.Service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setRequirementsFromGraph( uniqueId, service,
+ // NodeTypeEnum.Service);
+ // if (status != TitanOperationStatus.OK) {
+ // result =
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // return result;
+ // }
+ //
+ // status = setAllVersions(service);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // status = setServiceAdditionalInformationFromGraph(uniqueId, service);
+ // if (status != TitanOperationStatus.OK) {
+ // return
+ // Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ // }
+ //
+ // result = Either.left(service);
+ // return result;
+ // } finally {
+ // if (false == inTransaction) {
+ // if (result == null || result.isRight()) {
+ // titanGenericDao.rollback();
+ // } else {
+ // titanGenericDao.commit();
+ // }
+ // }
+ // }
+ // }
+
+ @Override
+ TitanOperationStatus setComponentCategoriesFromGraph(Component service) {
+
+ String uniqueId = service.getUniqueId();
+ Either<List<ImmutablePair<CategoryData, GraphEdge>>, TitanOperationStatus> parentNode = titanGenericDao
+ .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Service), uniqueId,
+ GraphEdgeLabels.CATEGORY, NodeTypeEnum.ServiceNewCategory, CategoryData.class);
+ if (parentNode.isRight()) {
+ return parentNode.right().value();
+ }
+
+ List<ImmutablePair<CategoryData, GraphEdge>> listValue = parentNode.left().value();
+ if (log.isDebugEnabled())
+ log.debug("Result after looking for category nodes pointed by service {}. status is {}", uniqueId,
+ listValue);
+ if (listValue.size() > 1) {
+ log.error("Multiple edges foud between resource " + uniqueId + " to category nodes.");
+ }
+ ImmutablePair<CategoryData, GraphEdge> value = listValue.get(0);
+ if (log.isDebugEnabled())
+ log.debug("Found parent node {}", value);
+
+ CategoryData categoryData = value.getKey();
+ CategoryDefinition categoryDefinition = new CategoryDefinition(categoryData.getCategoryDataDefinition());
+
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(categoryDefinition);
+ service.setCategories(categories);
+ return TitanOperationStatus.OK;
+
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> deleteService(String serviceId) {
+ return deleteService(serviceId, false);
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> deleteService(String serviceId, boolean inTransaction) {
+
+ Either<Service, StorageOperationStatus> result = Either.right(StorageOperationStatus.GENERAL_ERROR);
+ try {
+
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ if (graphResult.isRight()) {
+ result = Either
+ .right(DaoStatusConverter.convertTitanStatusToStorageStatus(graphResult.right().value()));
+ return result;
+ }
+
+ Either<ServiceMetadataData, TitanOperationStatus> serviceNode = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Service), serviceId, ServiceMetadataData.class);
+ if (serviceNode.isRight()) {
+ TitanOperationStatus status = serviceNode.right().value();
+ log.error("Failed to find service " + serviceId + ". status is " + status);
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ }
+
+ Either<Service, StorageOperationStatus> serviceRes = getService(serviceId, true);
+ if (serviceRes.isRight()) {
+ StorageOperationStatus status = serviceRes.right().value();
+ log.error("Failed to find sevice " + serviceId + ".status is " + status);
+ result = Either.right(status);
+ return result;
+ }
+ Service service = serviceRes.left().value();
+
+ Either<List<ComponentInstance>, StorageOperationStatus> deleteAllResourceInstancesRes = componentInstanceOperation
+ .deleteAllComponentInstances(serviceId, NodeTypeEnum.Service, true);
+ log.debug("After deleting resource instances under service " + serviceId + ".Result is "
+ + deleteAllResourceInstancesRes);
+ if (deleteAllResourceInstancesRes.isRight()) {
+ StorageOperationStatus status = deleteAllResourceInstancesRes.right().value();
+ if (status != StorageOperationStatus.NOT_FOUND) {
+ log.error(
+ "Failed to delete resource instances under service " + serviceId + " .status is " + status);
+ result = Either.right(status);
+ return result;
+ }
+ }
+ StorageOperationStatus removeArtifactsFromResource = removeArtifactsFromComponent(service,
+ NodeTypeEnum.Service);
+ log.debug("After deleting artifacts nodes in the graph. status is " + removeArtifactsFromResource);
+ if (!removeArtifactsFromResource.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeArtifactsFromResource);
+ return result;
+ }
+
+ StorageOperationStatus removeInputsFromResource = removeInputsFromComponent(NodeTypeEnum.Service, service);
+ log.debug("After deleting requirements nodes in the graph. status is " + removeInputsFromResource);
+ if (removeInputsFromResource != StorageOperationStatus.OK) {
+ result = Either.right(removeInputsFromResource);
+ return result;
+ }
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deleteChildrenNodesRes = titanGenericDao
+ .deleteChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Service), serviceId,
+ GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property, PropertyData.class);
+
+ if (deleteChildrenNodesRes.isRight()) {
+ TitanOperationStatus status = deleteChildrenNodesRes.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+ }
+
+ StorageOperationStatus removeAdditionalInformationFromService = super.deleteAdditionalInformation(
+ NodeTypeEnum.Service, serviceId);
+ log.debug("After deleting additional information node in the graph. status is "
+ + removeAdditionalInformationFromService);
+ if (!removeAdditionalInformationFromService.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeAdditionalInformationFromService);
+ return result;
+ }
+
+ StorageOperationStatus removeGroupsFromService = super.deleteGroups(NodeTypeEnum.Service, serviceId);
+ log.debug("After deleting group nodes in the graph. status is " + removeGroupsFromService);
+ if (!removeGroupsFromService.equals(StorageOperationStatus.OK)) {
+ result = Either.right(removeGroupsFromService);
+ return result;
+ }
+
+ Either<ServiceMetadataData, TitanOperationStatus> deleteServiceNodeRes = titanGenericDao
+ .deleteNode(serviceNode.left().value(), ServiceMetadataData.class);
+ if (deleteServiceNodeRes.isRight()) {
+ TitanOperationStatus status = deleteServiceNodeRes.right().value();
+ log.error("Failed to delete service node " + serviceId + ". status is " + status);
+ result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(status));
+ return result;
+ }
+
+ result = Either.left(service);
+
+ return result;
+ } finally {
+ if (false == inTransaction) {
+ if (result == null || result.isRight()) {
+ log.debug("deleteService operation : Going to execute rollback on graph.");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("deleteService operation : Going to execute commit on graph.");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateServiceNameExists(String serviceName) {
+ return validateServiceNameUniqueness(serviceName, titanGenericDao);
+ }
+
+ private Service convertServiceDataToService(ServiceMetadataData serviceData) {
+ ServiceMetadataDefinition serviceMetadataDefinition = new ServiceMetadataDefinition(
+ (ServiceMetadataDataDefinition) serviceData.getMetadataDataDefinition());
+
+ Service service = new Service(serviceMetadataDefinition);
+
+ return service;
+ }
+
+ @Override
+ public <T extends Component> Either<T, StorageOperationStatus> getComponent(String id, Class<T> clazz) {
+
+ Either<Service, StorageOperationStatus> component = getService(id);
+ if (component.isRight()) {
+ return Either.right(component.right().value());
+ }
+ return Either.left(clazz.cast(component.left().value()));
+ }
+
+ @SuppressWarnings("unchecked")
+ public Either<List<Service>, StorageOperationStatus> getFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, Set<LifecycleStateEnum> lastStateStates, boolean inTransaction) {
+
+ return (Either<List<Service>, StorageOperationStatus>) (Either<?, StorageOperationStatus>) getFollowedComponent(
+ userId, lifecycleStates, lastStateStates, inTransaction, titanGenericDao, NodeTypeEnum.Service);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getService(id, inTransaction);
+ }
+
+ // @Override
+ // public <T> Either<T, StorageOperationStatus> getComponent_tx(String id,
+ // boolean inTransaction) {
+ // return (Either<T, StorageOperationStatus>) getService_tx(id,
+ // inTransaction);
+ // }
+
+ @Override
+ public Either<Set<Service>, StorageOperationStatus> getCatalogData(Map<String, Object> propertiesToMatch,
+ boolean inTransaction) {
+ return getComponentCatalogData(NodeTypeEnum.Service, propertiesToMatch, Service.class,
+ ServiceMetadataData.class, inTransaction);
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> updateService(Service service, boolean inTransaction) {
+ Either<Service, StorageOperationStatus> result = updateComponent(service, inTransaction, titanGenericDao,
+ Service.class, NodeTypeEnum.Service);
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> updateComponent(T component, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) updateService((Service) component, inTransaction);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Either<Component, StorageOperationStatus> deleteComponent(String id, boolean inTransaction) {
+ return (Either<Component, StorageOperationStatus>) (Either<?, StorageOperationStatus>) deleteService(id,
+ inTransaction);
+ }
+
+ @Override
+ protected ComponentMetadataData getMetaDataFromComponent(Component component) {
+ return getServiceMetaDataFromService((Service) component);
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> getLightComponent(String id, boolean inTransaction) {
+ return getLightComponent(id, NodeTypeEnum.Service, inTransaction);
+ }
+
+ @Override
+ public <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters,
+ boolean inTransaction) {
+ Either<List<T>, StorageOperationStatus> components = null;
+
+ String categoryName = filters.get(FilterKeyEnum.CATEGORY);
+ String distributionStatus = filters.get(FilterKeyEnum.DISTRIBUTION_STATUS);
+ DistributionStatusEnum distEnum = DistributionStatusEnum.findState(distributionStatus);
+ if (distributionStatus != null && distEnum == null) {
+ filters.remove(FilterKeyEnum.CATEGORY);
+ return Either.right(StorageOperationStatus.CATEGORY_NOT_FOUND);
+ }
+
+ if (categoryName != null) { // primary filter
+ components = fetchByCategoryOrSubCategoryName(categoryName, NodeTypeEnum.ServiceNewCategory,
+ GraphEdgeLabels.CATEGORY.getProperty(), NodeTypeEnum.Service, inTransaction,
+ ServiceMetadataData.class);
+ if (components.isLeft() && distEnum != null) {// secondary filter
+ Predicate<T> statusFilter = p -> ((Service) p).getDistributionStatus().equals(distEnum);
+ return Either
+ .left(components.left().value().stream().filter(statusFilter).collect(Collectors.toList()));
+ }
+ filters.remove(FilterKeyEnum.DISTRIBUTION_STATUS);
+ return components;
+ }
+ components = fetchByDistributionStatus(distEnum.name(), inTransaction);
+ if (components.isRight()) { // not found == empty list
+ return Either.left(new ArrayList<>());
+ }
+ return components;
+ }
+
+ private <T> Either<List<T>, StorageOperationStatus> fetchByDistributionStatus(String status,
+ boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.DISTRIBUTION_STATUS.getProperty(), status);
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ return (Either<List<T>, StorageOperationStatus>) (Either<?, StorageOperationStatus>) getServiceListByCriteria(
+ props, inTransaction);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Either<List<Service>, StorageOperationStatus> getTesterFollowed(String userId,
+ Set<LifecycleStateEnum> lifecycleStates, boolean inTransaction) {
+ return (Either<List<Service>, StorageOperationStatus>) (Either<?, StorageOperationStatus>) getTesterFollowedComponent(
+ userId, lifecycleStates, inTransaction, NodeTypeEnum.Service);
+ }
+
+ public Either<Service, StorageOperationStatus> updateDestributionStatus(Service service, User user,
+ DistributionStatusEnum distributionStatus) {
+ String userId = user.getUserId();
+ Either<UserData, TitanOperationStatus> findUser = findUser(userId);
+ if (findUser.isRight()) {
+ TitanOperationStatus status = findUser.right().value();
+ log.error("Cannot find user " + userId + " in the graph. status is " + status);
+ return sendError(status, StorageOperationStatus.USER_NOT_FOUND);
+ }
+ UserData userData = findUser.left().value();
+
+ Either<ServiceMetadataData, TitanOperationStatus> serviceMetadataDataRequeset = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Service), service.getUniqueId(),
+ ServiceMetadataData.class);
+ if (serviceMetadataDataRequeset.isRight()) {
+ TitanOperationStatus status = serviceMetadataDataRequeset.right().value();
+ log.error("Cannot find service " + service.getUniqueId() + " in the graph. status is " + status);
+ return sendError(status, StorageOperationStatus.NOT_FOUND);
+ }
+ ServiceMetadataData serviceMetadataData = serviceMetadataDataRequeset.left().value();
+
+ StorageOperationStatus result = StorageOperationStatus.OK;
+
+ Either<GraphRelation, TitanOperationStatus> deleteIncomingRelation = deleteLastDistributionModifierRelation(
+ service);
+ if (deleteIncomingRelation.isRight()
+ && deleteIncomingRelation.right().value() != TitanOperationStatus.NOT_FOUND) {
+ log.error("Failed to delete user from component " + service.getUniqueId() + ". Edge type is "
+ + GraphEdgeLabels.LAST_DISTRIBUTION_STATE_MODIFAIER);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(deleteIncomingRelation.right().value());
+ return Either.right(result);
+ }
+
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(userData,
+ serviceMetadataData, GraphEdgeLabels.LAST_DISTRIBUTION_STATE_MODIFAIER, null);
+ log.debug("After associating user " + userData + " to component " + serviceMetadataData.getUniqueId()
+ + ". Edge type is " + GraphEdgeLabels.LAST_DISTRIBUTION_STATE_MODIFAIER);
+ if (createRelation.isRight()) {
+ log.error("Failed to associate user " + userData + " to component " + serviceMetadataData.getUniqueId()
+ + ". Edge type is " + GraphEdgeLabels.LAST_DISTRIBUTION_STATE_MODIFAIER);
+ result = DaoStatusConverter.convertTitanStatusToStorageStatus(createRelation.right().value());
+ return Either.right(result);
+ }
+ service.setDistributionStatus(distributionStatus);
+ Either<Service, StorageOperationStatus> updateResponse = updateComponent(service, true, titanGenericDao,
+ Service.class, NodeTypeEnum.Service);
+
+ return updateResponse;
+
+ }
+
+ private Either<GraphRelation, TitanOperationStatus> deleteLastDistributionModifierRelation(Service service) {
+ GraphRelation lastDistributionStateModifaierRelation = new GraphRelation();
+ lastDistributionStateModifaierRelation.setType(GraphEdgeLabels.LAST_DISTRIBUTION_STATE_MODIFAIER.getProperty());
+ RelationEndPoint relationEndPoint = new RelationEndPoint(NodeTypeEnum.Service,
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Service), service.getUniqueId());
+ lastDistributionStateModifaierRelation.setTo(relationEndPoint);
+ Either<GraphRelation, TitanOperationStatus> deleteIncomingRelation = titanGenericDao
+ .deleteIncomingRelation(lastDistributionStateModifaierRelation);
+ return deleteIncomingRelation;
+ }
+
+ @Override
+ public Either<Set<Service>, StorageOperationStatus> getCertifiedServicesWithDistStatus(
+ Map<String, Object> propertiesToMatch, Set<DistributionStatusEnum> distStatus, boolean inTransaction) {
+ log.debug("Start getCertifiedServicesWithDistStatus.");
+ Set<Service> servicesSet = new HashSet<Service>();
+ if (distStatus != null && !distStatus.isEmpty()) {
+ for (DistributionStatusEnum status : distStatus) {
+ Map<String, Object> props = new HashMap<>();
+ props.putAll(propertiesToMatch);
+ props.put(GraphPropertiesDictionary.DISTRIBUTION_STATUS.getProperty(), status.name());
+ Either<Set<Service>, StorageOperationStatus> services = retrieveCertifiedServicesWithStatus(
+ inTransaction, servicesSet, props);
+ if (services.isRight()) {
+ return services;
+ } else {
+ servicesSet.addAll(services.left().value());
+ }
+ }
+ return Either.left(servicesSet);
+ } else {
+ return retrieveCertifiedServicesWithStatus(inTransaction, servicesSet, propertiesToMatch);
+ }
+ }
+
+ private Either<Set<Service>, StorageOperationStatus> retrieveCertifiedServicesWithStatus(boolean inTransaction,
+ Set<Service> servicesSet, Map<String, Object> props) {
+ Either<List<ServiceMetadataData>, TitanOperationStatus> criteriaRes = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Service, props, ServiceMetadataData.class);
+ return retrieveComponentsFromNodes(criteriaRes, inTransaction);
+ }
+
+ public Either<List<Service>, StorageOperationStatus> getServiceCatalogData(boolean inTransaction) {
+
+ long start = System.currentTimeMillis();
+
+ try {
+ /*
+ * Map<String, Object> propertiesToMatch = new HashMap<>();
+ * propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty
+ * (), LifecycleStateEnum.CERTIFIED.name());
+ * Either<List<ServiceMetadataData>, TitanOperationStatus>
+ * lastVersionNodes = getLastVersion(NodeTypeEnum.Service,
+ * propertiesToMatch, ServiceMetadataData.class); if
+ * (lastVersionNodes.isRight() && lastVersionNodes.right().value()
+ * != TitanOperationStatus.NOT_FOUND) { return
+ * Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus
+ * (lastVersionNodes.right().value())); } List<ServiceMetadataData>
+ * notCertifiedHighest = (lastVersionNodes.isLeft() ?
+ * lastVersionNodes.left().value() : new
+ * ArrayList<ServiceMetadataData>());
+ *
+ * propertiesToMatch.put(GraphPropertiesDictionary.
+ * IS_HIGHEST_VERSION.getProperty(), true);
+ * Either<List<ServiceMetadataData>, TitanOperationStatus>
+ * componentsNodes =
+ * titanGenericDao.getByCriteria(NodeTypeEnum.Service,
+ * propertiesToMatch, ServiceMetadataData.class); if
+ * (componentsNodes.isRight() && componentsNodes.right().value() !=
+ * TitanOperationStatus.NOT_FOUND) { return
+ * Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus
+ * (componentsNodes.right().value())); } List<ServiceMetadataData>
+ * certifiedHighest = (componentsNodes.isLeft() ?
+ * componentsNodes.left().value() : new
+ * ArrayList<ServiceMetadataData>());
+ */
+
+ Either<List<ServiceMetadataData>, TitanOperationStatus> listOfHighestComponents = this
+ .getListOfHighestComponents(NodeTypeEnum.Service, ServiceMetadataData.class);
+ if (listOfHighestComponents.isRight()
+ && listOfHighestComponents.right().value() != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(
+ DaoStatusConverter.convertTitanStatusToStorageStatus(listOfHighestComponents.right().value()));
+ }
+
+ List<ServiceMetadataData> notCertifiedHighest = listOfHighestComponents.left().value();
+
+ List<Service> result = new ArrayList<>();
+
+ if (notCertifiedHighest != null && false == notCertifiedHighest.isEmpty()) {
+
+ // fetch from cache
+ long startFetchAllFromCache = System.currentTimeMillis();
+
+ Map<String, Long> components = notCertifiedHighest.stream()
+ .collect(Collectors.toMap(p -> p.getMetadataDataDefinition().getUniqueId(),
+ p -> p.getMetadataDataDefinition().getLastUpdateDate()));
+
+ Either<ImmutablePair<List<Component>, Set<String>>, ActionStatus> componentsFromCacheForCatalog = this
+ .getComponentsFromCacheForCatalog(components, ComponentTypeEnum.SERVICE);
+ if (componentsFromCacheForCatalog.isLeft()) {
+ ImmutablePair<List<Component>, Set<String>> immutablePair = componentsFromCacheForCatalog.left()
+ .value();
+ List<Component> list = immutablePair.getLeft();
+ if (list != null) {
+ for (Component component : list) {
+ result.add((Service) component);
+ }
+ List<String> addedUids = list.stream()
+ .map(p -> p.getComponentMetadataDefinition().getMetadataDataDefinition().getUniqueId())
+ .collect(Collectors.toList());
+ notCertifiedHighest = notCertifiedHighest.stream()
+ .filter(p -> false == addedUids.contains(p.getMetadataDataDefinition().getUniqueId()))
+ .collect(Collectors.toList());
+ }
+ }
+ long endFetchAllFromCache = System.currentTimeMillis();
+ log.debug("Fetch all catalog services metadata from cache took {} ms",
+ (endFetchAllFromCache - startFetchAllFromCache));
+ log.debug("The number of services added to catalog from cache is {}", result.size());
+
+ log.debug("The number of services needed to be fetch as light component is {}",
+ notCertifiedHighest.size());
+ for (ServiceMetadataData data : notCertifiedHighest) {
+ Either<Service, StorageOperationStatus> component = getLightComponent(
+ data.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get Service for id = {}, error : {}. Skip service", data.getUniqueId(),
+ component.right().value());
+ } else {
+ result.add(component.left().value());
+ }
+ }
+ }
+ return Either.left(result);
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ log.debug("Fetch all catalog services took " + (System.currentTimeMillis() - start) + " ms");
+ }
+
+ }
+
+ public Either<List<Service>, StorageOperationStatus> getServiceCatalogDataLatestCertifiedAndNotCertified(
+ boolean inTransaction) {
+ Map<String, Object> properties = new HashMap<>();
+
+ properties.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+ List<Service> result = new ArrayList<>();
+ Either<List<ServiceMetadataData>, TitanOperationStatus> lastVersionNodes = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Service, properties, ServiceMetadataData.class);
+
+ if (lastVersionNodes.isRight() && lastVersionNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(lastVersionNodes.right().value()));
+ }
+
+ List<ServiceMetadataData> latestServices;
+
+ if (lastVersionNodes.isLeft()) {
+ latestServices = lastVersionNodes.left().value();
+ } else {
+ return Either.left(result);
+ }
+
+ for (ServiceMetadataData data : latestServices) {
+ Either<Service, StorageOperationStatus> component = getLightComponent(
+ data.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (component.isRight()) {
+ log.debug("Failed to get Service for id = " + data.getUniqueId() + " error : "
+ + component.right().value() + " skip resource");
+ } else {
+ result.add(component.left().value());
+ }
+ }
+
+ return Either.left(result);
+
+ }
+
+ private Either<List<Service>, StorageOperationStatus> getServiceListByCriteria(Map<String, Object> props,
+ boolean inTransaction) {
+ props.put(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Service.getName());
+ Either<List<ServiceMetadataData>, TitanOperationStatus> byCriteria = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Service, props, ServiceMetadataData.class);
+
+ if (byCriteria.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCriteria.right().value()));
+ }
+ List<Service> services = new ArrayList<Service>();
+ List<ServiceMetadataData> servicesDataList = byCriteria.left().value();
+ for (ServiceMetadataData data : servicesDataList) {
+ Either<Service, StorageOperationStatus> service = getService(data.getMetadataDataDefinition().getUniqueId(),
+ inTransaction);
+ if (service.isLeft()) {
+ services.add(service.left().value());
+ } else {
+ log.debug("Failed to fetch resource for name = " + data.getMetadataDataDefinition().getName()
+ + " and id = " + data.getUniqueId());
+ }
+ }
+ return Either.left(services);
+ }
+
+ public Either<List<Service>, StorageOperationStatus> getServiceListByUuid(String uuid, boolean inTransaction) {
+ return getLatestServiceByUuid(uuid, false, inTransaction);
+ }
+
+ public Either<List<Service>, StorageOperationStatus> getLatestServiceByUuid(String uuid, boolean inTransaction) {
+ return getLatestServiceByUuid(uuid, true, inTransaction);
+ }
+
+ private Either<List<Service>, StorageOperationStatus> getLatestServiceByUuid(String uuid, boolean isLatest,
+ boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ if (isLatest) {
+ props.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), isLatest);
+ }
+
+ props.put(GraphPropertiesDictionary.UUID.getProperty(), uuid);
+ return getServiceListByCriteria(props, inTransaction);
+ }
+
+ public Either<List<Service>, StorageOperationStatus> getServiceListBySystemName(String systemName,
+ boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), systemName);
+ return getServiceListByCriteria(props, inTransaction);
+ }
+
+ public Either<Service, StorageOperationStatus> getServiceByNameAndVersion(String name, String version,
+ Map<String, Object> additionalParams, boolean inTransaction) {
+ return getByNamesAndVersion(GraphPropertiesDictionary.NORMALIZED_NAME.getProperty(),
+ ValidationUtils.normaliseComponentName(name), version, additionalParams, inTransaction);
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> getServiceByNameAndVersion(String name, String version) {
+ return getServiceByNameAndVersion(name, version, null, false);
+ }
+
+ protected Either<Service, StorageOperationStatus> getByNamesAndVersion(String nameKey, String nameValue,
+ String version, Map<String, Object> additionalParams, boolean inTransaction) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(nameKey, nameValue);
+ props.put(GraphPropertiesDictionary.VERSION.getProperty(), version);
+ props.put(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.Service.getName());
+ if (additionalParams != null && !additionalParams.isEmpty()) {
+ props.putAll(additionalParams);
+ }
+
+ Either<List<ServiceMetadataData>, TitanOperationStatus> byCriteria = titanGenericDao
+ .getByCriteria(NodeTypeEnum.Service, props, ServiceMetadataData.class);
+
+ if (byCriteria.isRight()) {
+ return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(byCriteria.right().value()));
+ }
+ List<ServiceMetadataData> dataList = byCriteria.left().value();
+ if (dataList != null && !dataList.isEmpty()) {
+ if (dataList.size() > 1) {
+ log.debug("More that one instance of resource for name =" + nameValue + " and version = " + version);
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ ServiceMetadataData serviceData = dataList.get(0);
+ Either<Service, StorageOperationStatus> service = getService(
+ serviceData.getMetadataDataDefinition().getUniqueId(), inTransaction);
+ if (service.isRight()) {
+ log.debug("Failed to fetch resource for name = " + serviceData.getMetadataDataDefinition().getName()
+ + " and id = " + serviceData.getMetadataDataDefinition().getUniqueId());
+ }
+ return service;
+ }
+ return Either.right(StorageOperationStatus.NOT_FOUND);
+ }
+
+ protected <T> Either<T, StorageOperationStatus> getComponentByNameAndVersion(String name, String version,
+ Map<String, Object> additionalParams, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) getServiceByNameAndVersion(name, version, additionalParams,
+ inTransaction);
+ }
+
+ @Override
+ public Either<Service, StorageOperationStatus> getServiceBySystemNameAndVersion(String name, String version,
+ boolean inTransaction) {
+ return getByNamesAndVersion(GraphPropertiesDictionary.SYSTEM_NAME.getProperty(), name, version, null,
+ inTransaction);
+ }
+
+ private TitanOperationStatus setServiceAdditionalInformationFromGraph(String uniqueId, Service service) {
+
+ List<AdditionalInformationDefinition> additionalInformation = new ArrayList<>();
+
+ Either<AdditionalInformationDefinition, TitanOperationStatus> either = additionalInformationOperation
+ .getAllAdditionalInformationParameters(NodeTypeEnum.Service, uniqueId, true);
+
+ if (either.isRight()) {
+ TitanOperationStatus status = either.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ return TitanOperationStatus.OK;
+ }
+ return status;
+ }
+
+ AdditionalInformationDefinition additionalInformationDefinition = either.left().value();
+ additionalInformation.add(additionalInformationDefinition);
+
+ service.setAdditionalInformation(additionalInformation);
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ private TitanOperationStatus setAllVersions(Service service) {
+ Either<Map<String, String>, TitanOperationStatus> res = getVersionList(NodeTypeEnum.Service,
+ service.getVersion(), service, ServiceMetadataData.class);
+ if (res.isRight()) {
+ return res.right().value();
+ }
+ service.setAllVersions(res.left().value());
+ return TitanOperationStatus.OK;
+ }
+
+ public Either<List<ArtifactDefinition>, StorageOperationStatus> getAdditionalArtifacts(String resourceId,
+ boolean recursively, boolean inTransaction) {
+ List<ArtifactDefinition> artifacts = new ArrayList<>();
+ return Either.left(artifacts);
+ }
+
+ @Override
+ public boolean isComponentExist(String serviceId) {
+ return isComponentExist(serviceId, NodeTypeEnum.Service);
+ }
+
+ // @SuppressWarnings("unchecked")
+ // @Override
+ // public <T> Either<T, StorageOperationStatus> cloneComponent(T other,
+ // String version, boolean inTransaction) {
+ // return (Either<T, StorageOperationStatus>) cloneService((Service)other,
+ // version, inTransaction);
+ // }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version,
+ LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ return (Either<T, StorageOperationStatus>) cloneService((Service) other, version, targetLifecycle,
+ inTransaction);
+ }
+
+ @Override
+ public Either<Integer, StorageOperationStatus> increaseAndGetComponentInstanceCounter(String componentId,
+ boolean inTransaction) {
+ return increaseAndGetComponentInstanceCounter(componentId, NodeTypeEnum.Service, inTransaction);
+ }
+
+ @Override
+ protected StorageOperationStatus validateCategories(Component currentComponent, Component component,
+ ComponentMetadataData componentData, NodeTypeEnum type) {
+ List<CategoryDefinition> newcategories = component.getCategories();
+ CategoryDefinition newCat = newcategories.get(0);
+ CategoryDefinition currentCategory = currentComponent.getCategories().get(0);
+
+ StorageOperationStatus status = StorageOperationStatus.OK;
+ if (newCat != null && newCat.getName() != null && false == newCat.getName().equals(currentCategory.getName())) {
+ log.debug(
+ "Going to update the category of the resource from " + currentCategory + " to " + newCat.getName());
+
+ status = moveCategoryEdge(component, componentData, newCat, type);
+ log.debug("Going to update the category of the resource from " + currentCategory + " to " + newCat.getName()
+ + ". status is " + status);
+ }
+ return status;
+ }
+
+ @Override
+ protected <T extends Component> StorageOperationStatus updateDerived(Component component,
+ Component currentComponent, ComponentMetadataData componentData, Class<T> clazz) {
+ log.debug("Derived class isn't supported for resource");
+ return null;
+ }
+
+ @Override
+ public Service getDefaultComponent() {
+ return new Service();
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> getMetadataComponent(String id, boolean inTransaction) {
+ return getMetadataComponent(id, NodeTypeEnum.Service, inTransaction);
+ }
+
+ @Override
+ Component convertComponentMetadataDataToComponent(ComponentMetadataData componentMetadataData) {
+ return convertServiceDataToService((ServiceMetadataData) componentMetadataData);
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateComponentNameExists(String componentName) {
+ return validateComponentNameUniqueness(componentName, titanGenericDao, NodeTypeEnum.Service);
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> markComponentToDelete(Component componentToDelete,
+ boolean inTransaction) {
+ return internalMarkComponentToDelete(componentToDelete, inTransaction);
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> isComponentInUse(String componentId) {
+ return isComponentInUse(componentId, NodeTypeEnum.Service);
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllComponentsMarkedForDeletion() {
+ return getAllComponentsMarkedForDeletion(NodeTypeEnum.Service);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id,
+ ComponentParametersView componentParametersView, boolean inTransaction) {
+
+ Either<Service, StorageOperationStatus> component = getService(id, componentParametersView, inTransaction);
+ if (component.isRight()) {
+ return Either.right(component.right().value());
+ }
+ return (Either<T, StorageOperationStatus>) component;
+ }
+
+ public Either<Service, StorageOperationStatus> updateService(Service service, boolean inTransaction,
+ ComponentParametersView filterResultView) {
+ return (Either<Service, StorageOperationStatus>) updateComponentFilterResult(service, inTransaction,
+ titanGenericDao, service.getClass(), NodeTypeEnum.Service, filterResultView);
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> updateComponentFilterResult(T component, boolean inTransaction,
+ ComponentParametersView filterResultView) {
+ return (Either<T, StorageOperationStatus>) updateService((Service) component, inTransaction, filterResultView);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java
new file mode 100644
index 0000000000..c4bcf6d907
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java
@@ -0,0 +1,244 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.resources.data.ResourceCategoryData;
+import org.openecomp.sdc.be.resources.data.ServiceCategoryData;
+import org.openecomp.sdc.be.resources.data.TagData;
+import org.openecomp.sdc.be.resources.data.UserData;
+
+public class UniqueIdBuilder {
+
+ private static String DOT = ".";
+ private static final String HEAT_PARAM_PREFIX = "heat_";
+
+ public static String buildPropertyUniqueId(String resourceId, String propertyName) {
+ return resourceId + DOT + propertyName;
+ }
+
+ public static String buildHeatParameterUniqueId(String resourceId, String propertyName) {
+ return resourceId + DOT + HEAT_PARAM_PREFIX + propertyName;
+ }
+
+ public static String buildHeatParameterValueUniqueId(String resourceId, String artifactLabel, String propertyName) {
+ return resourceId + DOT + artifactLabel + DOT + propertyName;
+ }
+
+ private static UserData userData = new UserData();
+ private static TagData tagData = new TagData();
+ private static ResourceCategoryData resCategoryData = new ResourceCategoryData();
+ private static ServiceCategoryData serCategoryData = new ServiceCategoryData();
+
+ private static Map<NodeTypeEnum, String> nodeTypeToUniqueKeyMapper = new HashMap<NodeTypeEnum, String>();
+
+ static {
+
+ nodeTypeToUniqueKeyMapper.put(NodeTypeEnum.User, userData.getUniqueIdKey());
+ nodeTypeToUniqueKeyMapper.put(NodeTypeEnum.Tag, tagData.getUniqueIdKey());
+ nodeTypeToUniqueKeyMapper.put(NodeTypeEnum.ResourceCategory, resCategoryData.getUniqueIdKey());
+ nodeTypeToUniqueKeyMapper.put(NodeTypeEnum.ServiceCategory, serCategoryData.getUniqueIdKey());
+ }
+
+ /**
+ * find the unique id key of a node on the graph
+ *
+ * @param nodeTypeEnum
+ * @return
+ */
+ public static String getKeyByNodeType(NodeTypeEnum nodeTypeEnum) {
+
+ String key = nodeTypeToUniqueKeyMapper.get(nodeTypeEnum);
+ if (key == null) {
+ key = GraphPropertiesDictionary.UNIQUE_ID.getProperty();
+ }
+
+ return key;
+ }
+
+ public static String buildResourceUniqueId() {
+ return generateUUID();
+ }
+
+ public static String generateUUID() {
+ UUID uuid = UUID.randomUUID();
+ return uuid.toString();
+ }
+
+ public static String buildComponentUniqueId() {
+ return generateUUID();
+ }
+
+ public static String buildConstantProductId() {
+ return generateUUID();
+ }
+
+ public static String buildCapabilityTypeUid(String type) {
+ return type;
+ }
+
+ public static String buildAttributeUid(String resourceId, String attName) {
+ return NodeTypeEnum.Attribute.getName() + DOT + resourceId + DOT + attName;
+ }
+
+ public static String buildRequirementUid(String resourceId, String reqName) {
+ return resourceId + DOT + reqName;
+ }
+
+ public static String buildRequirementImplUid(String resourceId, String reqName) {
+
+ return NodeTypeEnum.RequirementImpl.getName() + DOT + resourceId + DOT + reqName;
+
+ }
+
+ public static String buildCapabilityUid(String resourceId, String capabilityName) {
+ return NodeTypeEnum.Capability.getName() + DOT + resourceId + DOT + capabilityName;
+ }
+
+ public static String buildCapabilityInstanceUid(String parentId, String capabilityName) {
+ return NodeTypeEnum.CapabilityInst.getName() + DOT + parentId + DOT + capabilityName;
+ }
+
+ public static String buildPropertyValueUniqueId(String parentId, String paramName) {
+ return NodeTypeEnum.PropertyValue.getName() + DOT + parentId + DOT + paramName;
+ }
+
+ public static String buildArtifactByInterfaceUniqueId(String resourceId, String interfaceName, String operation,
+ String artifactLabel) {
+
+ return resourceId + DOT + interfaceName + DOT + operation + DOT + artifactLabel;
+ }
+
+ // public static String
+ // buildArtifactByInterfaceUniqueIdAndRsrcNameVersion(String
+ // resourceName,String resourceVersion,String interfaceName,String
+ // operation,String artifactLabel) {
+ // String resourceId = UniqueIdBuilder.buildResourceUniqueId(resourceName,
+ // resourceVersion);
+ // return resourceId + DOT + interfaceName + DOT +operation + DOT +
+ // artifactLabel;
+ // }
+ public static String buildArtifactByInterfaceUniqueIdAndRsrcId(String resourceId, String interfaceName,
+ String operation, String artifactLabel) {
+ return resourceId + DOT + interfaceName + DOT + operation + DOT + artifactLabel;
+ }
+
+ public static String buildOperationByInterfaceUniqueId(String resourceId, String interfaceName, String operation) {
+
+ return resourceId + DOT + interfaceName + DOT + operation;
+ }
+
+ public static String buildInterfaceUniqueId(String resourceId, String interfaceName) {
+ return resourceId + DOT + interfaceName;
+ }
+
+ public static String buildResourceInstanceUniuqeId(String serviceId, String resourceId, String logicalName) {
+
+ return serviceId + DOT + resourceId + DOT + logicalName;
+ }
+
+ public static String buildRelationsipInstInstanceUid(String resourceInstUid, String requirement) {
+
+ return generateUUID();
+ }
+
+ /*
+ * TODO Pavel To be removed when new category logic comes in
+ */
+ public static String buildResourceCategoryUid(String categoryName, String subcategoryName, NodeTypeEnum type) {
+ return type.getName() + DOT + categoryName + DOT + subcategoryName;
+ }
+
+ /*
+ * TODO Pavel To be removed when new category logic comes in
+ */
+ public static String buildServiceCategoryUid(String categoryName, NodeTypeEnum type) {
+ return type.getName() + DOT + categoryName;
+ }
+
+ // New logic
+ public static String buildCategoryUid(String categoryName, NodeTypeEnum type) {
+ return type.getName() + DOT + categoryName;
+ }
+
+ public static String buildSubCategoryUid(String categoryUid, String subCategoryName) {
+ return categoryUid + DOT + subCategoryName;
+ }
+
+ public static String buildGroupingUid(String subCategoryUid, String groupingName) {
+ return subCategoryUid + DOT + groupingName;
+ }
+
+ public static String buildResourceInstancePropertyValueUid(String resourceInstanceUniqueId, Integer index) {
+ return resourceInstanceUniqueId + DOT + "property" + DOT + index;
+ }
+
+ public static String buildResourceInstanceAttributeValueUid(String resourceInstanceUniqueId, Integer index) {
+ return resourceInstanceUniqueId + DOT + "attribute" + DOT + index;
+ }
+
+ public static String buildResourceInstanceInputValueUid(String resourceInstanceUniqueId, Integer index) {
+ return resourceInstanceUniqueId + DOT + "input" + DOT + index;
+ }
+
+ public static String buildAdditionalInformationUniqueId(String resourceUniqueId) {
+ return resourceUniqueId + DOT + "additionalinformation";
+ }
+
+ public static String buildHeatParamValueUid(String heatEnvArtifactId, String parameterName) {
+ return heatEnvArtifactId + DOT + parameterName;
+ }
+
+ public static String buildDataTypeUid(String name) {
+ return name + DOT + "datatype";
+ }
+
+ public static String buildInvariantUUID() {
+ return generateUUID();
+ }
+
+ public static String buildGroupTypeUid(String type, String version) {
+ return type + DOT + version + DOT + "grouptype";
+ }
+
+ public static String buildPolicyTypeUid(String type, String version) {
+ return type + DOT + version + DOT + "policytype";
+ }
+
+ public static String buildGroupUniqueId(String componentId, String name) {
+ return componentId + DOT + name + DOT + "group";
+ }
+
+ public static String buildGroupPropertyValueUid(String groupUniqueId, Integer index) {
+ return groupUniqueId + DOT + "property" + DOT + index;
+
+ }
+
+ public static String buildUserFunctionalMenuUid(String userId) {
+ return userId + DOT + "functionalmenu";
+
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperation.java
new file mode 100644
index 0000000000..85bb56f39f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperation.java
@@ -0,0 +1,501 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.FunctionalMenuInfo;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.IUserAdminOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.UserFunctionalMenuData;
+import org.openecomp.sdc.common.datastructure.Wrapper;
+import org.openecomp.sdc.common.util.MethodActivationStatusEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@Component("user-operation")
+public class UserAdminOperation implements IUserAdminOperation {
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ public UserAdminOperation() {
+ super();
+ }
+
+ private static Logger log = LoggerFactory.getLogger(UserAdminOperation.class.getName());
+
+ @Override
+ public Either<User, ActionStatus> getInactiveUserData(String id) {
+ return getUserData(id, false, false);
+ }
+
+ @Override
+ public Either<User, ActionStatus> getUserData(String id, boolean inTransaction) {
+ return getUserData(id, true, inTransaction);
+ }
+
+ private Either<User, ActionStatus> getUserData(String id, boolean isActive, boolean inTransaction) {
+ log.debug("getUserData - start");
+ Wrapper<Either<User, ActionStatus>> resultWrapper = new Wrapper<>();
+ Wrapper<UserData> userWrapper = new Wrapper<>();
+ try {
+ validateUserExists(resultWrapper, userWrapper, id);
+
+ if (resultWrapper.isEmpty()) {
+ validateUserData(resultWrapper, userWrapper.getInnerElement(), id);
+
+ }
+ if (resultWrapper.isEmpty()) {
+ if (isActive) {
+ validateActiveUser(resultWrapper, userWrapper.getInnerElement());
+ } else {
+ validateInActiveUser(resultWrapper, userWrapper.getInnerElement());
+ }
+ }
+
+ if (resultWrapper.isEmpty()) {
+ Either<User, ActionStatus> result = Either.left(convertToUser(userWrapper.getInnerElement()));
+ resultWrapper.setInnerElement(result);
+ }
+
+ return resultWrapper.getInnerElement();
+ } finally {
+ if (false == inTransaction) {
+ titanGenericDao.commit();
+ }
+ log.debug("getUserData - end");
+ }
+ }
+
+ private void validateInActiveUser(Wrapper<Either<User, ActionStatus>> resultWrapper, UserData userData) {
+ User user = convertToUser(userData);
+ if (user.getStatus() == UserStatusEnum.ACTIVE) {
+ Either<User, ActionStatus> result = Either.right(ActionStatus.USER_NOT_FOUND);
+ resultWrapper.setInnerElement(result);
+ }
+ }
+
+ private void validateActiveUser(Wrapper<Either<User, ActionStatus>> resultWrapper, UserData userData) {
+ User user = convertToUser(userData);
+ if (user.getStatus() == UserStatusEnum.INACTIVE) {
+ Either<User, ActionStatus> result = Either.right(ActionStatus.USER_INACTIVE);
+ resultWrapper.setInnerElement(result);
+ }
+ }
+
+ private void validateUserData(Wrapper<Either<User, ActionStatus>> resultWrapper, UserData userData, String id) {
+ if (userData == null) {
+ log.debug("Problem get User with userId {}. Reason - either.left().value() = null", id);
+ Either<User, ActionStatus> result = Either.right(ActionStatus.GENERAL_ERROR);
+ resultWrapper.setInnerElement(result);
+ }
+ }
+
+ private void validateUserExists(Wrapper<Either<User, ActionStatus>> resultWrapper, Wrapper<UserData> userWrapper,
+ String id) {
+ Either<User, ActionStatus> result;
+ if (id == null) {
+ log.info("User userId is empty");
+ result = Either.right(ActionStatus.MISSING_INFORMATION);
+ resultWrapper.setInnerElement(result);
+ return;
+ }
+ id = id.toLowerCase();
+ Either<UserData, TitanOperationStatus> either = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), id, UserData.class);
+
+ if (either.isRight()) {
+ if (either.right().value() == TitanOperationStatus.NOT_FOUND) {
+ log.debug("User with userId {} not found", id);
+ result = Either.right(ActionStatus.USER_NOT_FOUND);
+ resultWrapper.setInnerElement(result);
+ } else {
+ log.debug("Problem get User with userId {}. Reason - {}", id, either.right().value().name());
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ resultWrapper.setInnerElement(result);
+ }
+ } else {
+ userWrapper.setInnerElement(either.left().value());
+ }
+ }
+
+ @Override
+ public Either<User, StorageOperationStatus> saveUserData(User user) {
+
+ Either<UserData, TitanOperationStatus> result = null;
+ try {
+ UserData userData = convertToUserData(user);
+ result = titanGenericDao.createNode(userData, UserData.class);
+ if (result.isRight()) {
+ log.debug("Problem while saving User {}. Reason - {}", userData.toString(), result.right().value().name());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ log.debug("User {} saved successfully", userData.toString());
+ return Either.left(convertToUser(result.left().value()));
+
+ } finally {
+
+ if (result == null || result.isRight()) {
+ log.error("saveUserData - Failed");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("saveUserData - end");
+ titanGenericDao.commit();
+ }
+ }
+ }
+
+ @Override
+ public Either<User, StorageOperationStatus> updateUserData(User user) {
+ Either<UserData, TitanOperationStatus> result = null;
+ try {
+ log.debug("updateUserData - start");
+ UserData userData = convertToUserData(user);
+ result = titanGenericDao.updateNode(userData, UserData.class);
+ if (result.isRight()) {
+ log.debug("Problem while updating User {}. Reason - {}", userData.toString(), result.right().value().name());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ log.debug("User {} updated successfully", userData.toString());
+ return Either.left(convertToUser(result.left().value()));
+
+ } finally {
+
+ if (result == null || result.isRight()) {
+ log.error("updateUserData - Failed");
+ titanGenericDao.rollback();
+ } else {
+ log.debug("updateUserData - end");
+ titanGenericDao.commit();
+ }
+
+ }
+ }
+
+ @Override
+ public Either<User, StorageOperationStatus> deActivateUser(User user) {
+ Either<User, StorageOperationStatus> result;
+ user.setStatus(UserStatusEnum.INACTIVE);
+ Either<User, StorageOperationStatus> status = updateUserData(user);
+ if (status.isRight()) {
+ result = Either.right(status.right().value());
+ } else {
+ result = Either.left(user);
+ }
+ return result;
+ }
+
+ @Override
+ public Either<User, ActionStatus> deleteUserData(String id) {
+ Either<User, ActionStatus> result;
+ Either<UserData, TitanOperationStatus> eitherGet = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), id, UserData.class);
+ if (eitherGet.isRight()) {
+ log.debug("Problem while retriving user with userId {}", id);
+ if (eitherGet.right().value() == TitanOperationStatus.NOT_FOUND) {
+ result = Either.right(ActionStatus.USER_NOT_FOUND);
+ } else {
+ result = Either.right(ActionStatus.GENERAL_ERROR);
+ }
+
+ } else {
+ result = deleteUserLogic(eitherGet.left().value());
+ }
+ return result;
+ }
+
+ private Either<User, ActionStatus> deleteUserLogic(UserData userData) {
+ Wrapper<Either<User, ActionStatus>> resultWrapper = new Wrapper<>();
+ try {
+ validateUserHasNoConnections(resultWrapper, userData);
+
+ if (resultWrapper.isEmpty()) {
+ deleteUser(resultWrapper, userData);
+ }
+
+ } finally {
+ titanGenericDao.commit();
+ }
+
+ return resultWrapper.getInnerElement();
+ }
+
+ private void deleteUser(Wrapper<Either<User, ActionStatus>> resultWrapper, UserData userData) {
+ Either<UserData, TitanOperationStatus> eitherDelete = titanGenericDao.deleteNode(userData, UserData.class);
+ if (eitherDelete.isRight()) {
+ log.debug("Problem while deleting User {}. Reason - {}", userData.toString(), eitherDelete.right().value().name());
+ Either<User, ActionStatus> result = Either.right(ActionStatus.GENERAL_ERROR);
+ resultWrapper.setInnerElement(result);
+ } else {
+ log.debug("User {} deleted successfully", userData.toString());
+ Either<User, ActionStatus> result = Either.left(convertToUser(eitherDelete.left().value()));
+ resultWrapper.setInnerElement(result);
+ }
+ }
+
+ private void validateUserHasNoConnections(Wrapper<Either<User, ActionStatus>> resultWrapper, UserData userData) {
+ if (resultWrapper.isEmpty()) {
+
+ Either<List<Edge>, TitanOperationStatus> edgesForNode = titanGenericDao.getEdgesForNode(userData,
+ Direction.BOTH);
+ if (edgesForNode.isRight()) {
+ log.debug("Problem while deleting User {}. Reason - {}", userData.toString(), edgesForNode.right().value().name());
+ Either<User, ActionStatus> result = Either.right(ActionStatus.GENERAL_ERROR);
+ resultWrapper.setInnerElement(result);
+ } else {
+ List<Edge> vertexEdges = edgesForNode.left().value();
+ if (vertexEdges.size() > 0) {
+ Either<User, ActionStatus> result = Either.right(ActionStatus.USER_HAS_ACTIVE_ELEMENTS);
+ resultWrapper.setInnerElement(result);
+ }
+ }
+ }
+ }
+
+ public Either<List<Edge>, StorageOperationStatus> getUserPandingTasksList(User user,
+ Map<String, Object> properties) {
+
+ UserData userData = convertToUserData(user);
+
+ Either<TitanVertex, TitanOperationStatus> vertexUser = titanGenericDao
+ .getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), user.getUserId());
+ if (vertexUser.isRight()) {
+ log.debug("Problem while deleting User {}. Reason - {}", userData.toString(), vertexUser.right().value().name());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+
+ List<Edge> pandingTasks = new ArrayList<>();
+ Either<List<Edge>, TitanOperationStatus> edges = titanGenericDao
+ .getOutgoingEdgesByCriteria(vertexUser.left().value(), GraphEdgeLabels.STATE, properties);
+
+ if (edges.isRight() || edges.left().value() == null) {
+ if (edges.right().value() == TitanOperationStatus.NOT_FOUND) {
+ return Either.left(pandingTasks);
+ } else {
+ log.debug("Problem while deleting User {}", userData.toString(), edges.right().value().name());
+ return Either.right(StorageOperationStatus.GENERAL_ERROR);
+ }
+ }
+
+ for (Edge edge : edges.left().value()) {
+ Vertex componentVertex = edge.inVertex();
+ VertexProperty<Object> property = componentVertex
+ .property(GraphPropertiesDictionary.IS_DELETED.getProperty());
+ if (!property.isPresent()) {
+ pandingTasks.add(edge);
+ } else {
+ Boolean isDeletedValue = (java.lang.Boolean) property.value();
+ if (isDeletedValue == null || isDeletedValue == false) {
+ pandingTasks.add(edge);
+ }
+ }
+ }
+
+ return Either.left(pandingTasks);
+ }
+
+ @Override
+ public Either<List<User>, ActionStatus> getAllUsersWithRole(String role, String status) {
+ try {
+ List<User> result = new ArrayList<>();
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ if (role != null && !role.trim().isEmpty()) {
+ propertiesToMatch.put(GraphPropertiesDictionary.ROLE.getProperty(), role);
+ }
+ if (status != null && !status.isEmpty()) {
+ propertiesToMatch.put(GraphPropertiesDictionary.USER_STATUS.getProperty(), status);
+ }
+
+ Either<List<UserData>, TitanOperationStatus> userNodes = titanGenericDao.getByCriteria(NodeTypeEnum.User,
+ propertiesToMatch, UserData.class);
+
+ titanGenericDao.commit();
+ if (userNodes.isRight()) {
+ // in case of NOT_FOUND from Titan return empty list
+ if (userNodes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
+ return Either.left(result);
+ } else {
+ log.error("Problem while getting all users with role {}. Reason - {}", role, userNodes.right().value().name());
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ } else {
+ List<UserData> userDataList = userNodes.left().value();
+ if (userDataList != null) {
+ for (UserData userData : userDataList) {
+ User user = convertToUser(userData);
+ result.add(user);
+ }
+ return Either.left(result);
+ }
+ log.debug("No users were found with role {}", role);
+ return Either.left(result);
+ }
+ } finally {
+ titanGenericDao.commit();
+ }
+ }
+
+ protected User convertToUser(UserData userData) {
+ User user = new User();
+ user.setUserId(userData.getUserId());
+ user.setEmail(userData.getEmail());
+ user.setFirstName(userData.getFirstName());
+ user.setLastName(userData.getLastName());
+ user.setRole(userData.getRole());
+ user.setLastLoginTime(userData.getLastLoginTime());
+ // Support backward compatibility - user status may not exist in old
+ // users
+ Either<UserStatusEnum, MethodActivationStatusEnum> either = UserStatusEnum.findByName(userData.getStatus());
+ user.setStatus(either.isLeft() ? either.left().value() : UserStatusEnum.ACTIVE);
+ return user;
+ }
+
+ protected UserData convertToUserData(User user) {
+ UserData userData = new UserData();
+ userData.setUserId(user.getUserId().toLowerCase());
+ userData.setEmail(user.getEmail());
+ userData.setFirstName(user.getFirstName());
+ userData.setLastName(user.getLastName());
+ userData.setRole(user.getRole());
+ userData.setStatus(user.getStatus().name());
+ userData.setLastLoginTime(user.getLastLoginTime());
+ return userData;
+ }
+
+ public Either<ImmutablePair<User, FunctionalMenuInfo>, ActionStatus> getUserDataWithFunctionalMenu(String userId) {
+
+ Either<User, ActionStatus> userData = getUserData(userId, true, true);
+
+ if (userData.isRight()) {
+ return Either.right(userData.right().value());
+ }
+ User user = userData.left().value();
+ Either<UserFunctionalMenuData, TitanOperationStatus> functionalMenu = getFunctionalMenu(userId);
+
+ FunctionalMenuInfo functionalMenuInfo = new FunctionalMenuInfo();
+ if (functionalMenu.isRight()) {
+ TitanOperationStatus status = functionalMenu.right().value();
+ if (status != TitanOperationStatus.NOT_FOUND) {
+ return Either.right(ActionStatus.GENERAL_ERROR);
+ }
+ } else {
+ UserFunctionalMenuData userFunctionalMenuData = functionalMenu.left().value();
+ functionalMenuInfo.setFunctionalMenu(userFunctionalMenuData.getFunctionalMenu());
+ }
+
+ ImmutablePair<User, FunctionalMenuInfo> result = new ImmutablePair<User, FunctionalMenuInfo>(user,
+ functionalMenuInfo);
+
+ return Either.left(result);
+ }
+
+ public Either<UserFunctionalMenuData, TitanOperationStatus> getFunctionalMenu(String userId) {
+
+ Either<UserFunctionalMenuData, TitanOperationStatus> result;
+ if (userId == null) {
+ log.info("User userId is empty");
+ result = Either.right(TitanOperationStatus.NOT_FOUND);
+ return result;
+ }
+ userId = userId.toLowerCase();
+ String uid = UniqueIdBuilder.buildUserFunctionalMenuUid(userId);
+
+ Either<UserFunctionalMenuData, TitanOperationStatus> either = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.UserFunctionalMenu), uid, UserFunctionalMenuData.class);
+
+ return either;
+ }
+
+ public Either<FunctionalMenuInfo, TitanOperationStatus> createOrUpdateFunctionalMenu(String userId, String newFunctionalMenu) {
+
+ Either<UserFunctionalMenuData, TitanOperationStatus> functionalMenu = getFunctionalMenu(userId);
+
+ if (functionalMenu.isRight()) {
+ TitanOperationStatus status = functionalMenu.right().value();
+ if (status == TitanOperationStatus.NOT_FOUND) {
+ String uid = UniqueIdBuilder.buildUserFunctionalMenuUid(userId);
+ UserFunctionalMenuData functionalMenuData = new UserFunctionalMenuData(newFunctionalMenu, uid);
+
+ Either<UserFunctionalMenuData, TitanOperationStatus> createNode = titanGenericDao
+ .createNode(functionalMenuData, UserFunctionalMenuData.class);
+
+ if (createNode.isRight()) {
+ return Either.right(createNode.right().value());
+ } else {
+ return Either.left(convert(createNode.left().value()));
+ }
+
+ } else {
+ return Either.right(status);
+ }
+
+ } else {
+ UserFunctionalMenuData userFunctionalMenuData = functionalMenu.left().value();
+ userFunctionalMenuData.setFunctionalMenu(newFunctionalMenu);
+ Either<UserFunctionalMenuData, TitanOperationStatus> updateNode = titanGenericDao
+ .updateNode(userFunctionalMenuData, UserFunctionalMenuData.class);
+
+ if (updateNode.isRight()) {
+ return Either.right(updateNode.right().value());
+ } else {
+ return Either.left(convert(updateNode.left().value()));
+ }
+ }
+
+ }
+
+ private FunctionalMenuInfo convert(UserFunctionalMenuData functionalMenuData) {
+
+ if (functionalMenuData == null) {
+ return null;
+ }
+
+ FunctionalMenuInfo functionalMenuInfo = new FunctionalMenuInfo();
+ functionalMenuInfo.setFunctionalMenu(functionalMenuData.getFunctionalMenu());
+
+ return functionalMenuInfo;
+
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/ComponentValidationUtils.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/ComponentValidationUtils.java
new file mode 100644
index 0000000000..fd6563ac4f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/ComponentValidationUtils.java
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.utils;
+
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.operations.api.IComponentOperation;
+import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
+import org.openecomp.sdc.be.model.operations.api.IServiceOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class ComponentValidationUtils {
+
+ private static Logger log = LoggerFactory.getLogger(ComponentValidationUtils.class.getName());
+
+ public static boolean canWorkOnResource(Resource resource, String userId) {
+ // verify resource is checked-out
+ if (resource.getLifecycleState() != LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT) {
+ log.debug("resource is not checked-out");
+ return false;
+ }
+ // verify resource is not deleted
+ if ((resource.getIsDeleted() != null) && (resource.getIsDeleted() == true)) {
+ log.debug("resource is marked as delete");
+ return false;
+ }
+ // verify resource last update user is the current user
+ if (!userId.equals(resource.getLastUpdaterUserId())) {
+ log.debug("resource last update is not {}", userId);
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean canWorkOnResource(String resourceId, IResourceOperation resourceOperation,
+ String userId) {
+ Either<Resource, StorageOperationStatus> getResourceResult = resourceOperation.getLightComponent(resourceId,
+ false);
+
+ if (getResourceResult.isRight()) {
+ log.debug("Failed to retrive resource, resource id {}", resourceId);
+ return false;
+ }
+ Resource resource = getResourceResult.left().value();
+
+ return canWorkOnResource(resource, userId);
+
+ }
+
+ public static boolean canWorkOnService(String serviceId, IServiceOperation serviceOperation, String userId) {
+ Either<Service, StorageOperationStatus> getResourceResult = serviceOperation.getLightComponent(serviceId,
+ false);
+
+ if (getResourceResult.isRight()) {
+ log.debug("Failed to retrieve service, service id {}", serviceId);
+ return false;
+ }
+ Service service = getResourceResult.left().value();
+
+ return canWorkOnComponent(service, userId);
+
+ }
+
+ public static boolean canWorkOnComponent(String componentId, IComponentOperation componentOperation,
+ String userId) {
+ Either<Component, StorageOperationStatus> getResourceResult = componentOperation.getLightComponent(componentId,
+ false);
+
+ if (getResourceResult.isRight()) {
+ log.debug("Failed to retrieve component, component id {}", componentId);
+ return false;
+ }
+ Component service = getResourceResult.left().value();
+
+ return canWorkOnComponent(service, userId);
+ }
+
+ public static boolean canWorkOnComponent(Component component, String userId) {
+ // verify resource is checked-out
+ if (component.getLifecycleState() != LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT) {
+ log.debug("resource is not checked-out");
+ return false;
+ }
+
+ // verify userId is not null
+ if (userId == null) {
+ log.debug("current user userId is null");
+ return false;
+ }
+
+ // verify resource last update user is the current user
+ if (!userId.equals(component.getLastUpdaterUserId())) {
+ log.debug("resource last updater userId is not {}", userId);
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/GraphDeleteUtil.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/GraphDeleteUtil.java
new file mode 100644
index 0000000000..1d78252de2
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/utils/GraphDeleteUtil.java
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.utils;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GraphDeleteUtil {
+
+ private static Logger log = LoggerFactory.getLogger(GraphDeleteUtil.class.getName());
+
+ public TitanOperationStatus deleteChildrenNodes(Vertex rootVertex, GraphEdgeLabels edgeType) {
+
+ // Iterable<Edge> edgesCreatorIterable =
+ // rootVertex.getEdges(Direction.OUT,
+ // edgeType.name());
+ Iterator<Edge> edgesCreatorIterator = rootVertex.edges(Direction.OUT, edgeType.getProperty());
+
+ while (edgesCreatorIterator.hasNext()) {
+ Edge edge = edgesCreatorIterator.next();
+ Vertex incomingVertex = edge.inVertex();
+ Iterator<Edge> outEdges = incomingVertex.edges(Direction.OUT);
+
+ if (outEdges.hasNext()) {
+ return TitanOperationStatus.CANNOT_DELETE_NON_LEAF_NODE;
+ } else {
+ Map<String, Object> properties = null;
+ if (log.isDebugEnabled()) {
+ properties = getProperties(incomingVertex);
+ log.debug("Going to delete vertex {}", properties);
+ }
+ incomingVertex.remove();
+ if (log.isDebugEnabled()) {
+ log.debug("After deleting vertex {}", properties);
+ }
+ }
+
+ }
+
+ //
+ // if (edgesCreatorIterable != null) {
+ // for (Edge edge : edgesCreatorIterable) {
+ //
+ // Vertex incomingVertex = edge.getVertex(Direction.IN);
+ // Iterable<Edge> outEdges = incomingVertex.getEdges(Direction.OUT);
+ // if (outEdges != null) {
+ // if (outEdges.iterator().hasNext()) {
+ // return TitanOperationStatus.CANNOT_DELETE_NON_LEAF_NODE;
+ // } else {
+ // Map<String, Object> properties = null;
+ // if (log.isDebugEnabled()) {
+ // properties = ElementHelper.getProperties(incomingVertex);
+ // log.debug("Going to delete vertex {}", properties);
+ // }
+ // incomingVertex.remove();
+ // if (log.isDebugEnabled()) {
+ // log.debug("After deleting vertex {}", properties);
+ // }
+ // }
+ // }
+ //
+ // }
+ // }
+
+ return TitanOperationStatus.OK;
+
+ }
+
+ public Map<String, Object> getProperties(Element element) {
+
+ Map<String, Object> result = null;
+
+ if (element.keys() != null && element.keys().size() > 0) {
+ Map<String, Property> propertyMap = ElementHelper.propertyMap(element,
+ element.keys().toArray(new String[element.keys().size()]));
+ result = new HashMap<String, Object>();
+
+ for (Entry<String, Property> entry : propertyMap.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue().value();
+
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaPropertyType.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaPropertyType.java
new file mode 100644
index 0000000000..2bbb84dd20
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaPropertyType.java
@@ -0,0 +1,199 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca;
+
+import org.openecomp.sdc.be.model.tosca.converters.BooleanConverter;
+import org.openecomp.sdc.be.model.tosca.converters.DefaultConverter;
+import org.openecomp.sdc.be.model.tosca.converters.FloatConverter;
+import org.openecomp.sdc.be.model.tosca.converters.IntegerConverter;
+import org.openecomp.sdc.be.model.tosca.converters.JsonConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ListConverter;
+import org.openecomp.sdc.be.model.tosca.converters.MapConverter;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.StringConvertor;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaBooleanConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaFloatConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaJsonValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaListValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaMapValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaStringConvertor;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaValueConverter;
+import org.openecomp.sdc.be.model.tosca.converters.ToscaValueDefaultConverter;
+import org.openecomp.sdc.be.model.tosca.validators.BooleanValidator;
+import org.openecomp.sdc.be.model.tosca.validators.FloatValidator;
+import org.openecomp.sdc.be.model.tosca.validators.IntegerValidator;
+import org.openecomp.sdc.be.model.tosca.validators.JsonValidator;
+import org.openecomp.sdc.be.model.tosca.validators.KeyValidator;
+import org.openecomp.sdc.be.model.tosca.validators.ListValidator;
+import org.openecomp.sdc.be.model.tosca.validators.MapValidator;
+import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
+import org.openecomp.sdc.be.model.tosca.validators.StringValidator;
+
+/**
+ * The primitive type that TOSCA YAML supports.
+ *
+ * @author esofer
+ */
+public enum ToscaPropertyType {
+
+ Root("tosca.datatypes.Root", null, null, null, true),
+
+ STRING("string", StringValidator.getInstance(), StringConvertor.getInstance(), ToscaStringConvertor.getInstance()),
+
+ BOOLEAN("boolean", BooleanValidator.getInstance(), ToscaBooleanConverter.getInstance(),
+ BooleanConverter.getInstance()),
+
+ FLOAT("float", FloatValidator.getInstance(), ToscaFloatConverter.getInstance(), FloatConverter.getInstance()),
+
+ INTEGER("integer", IntegerValidator.getInstance(), DefaultConverter.getInstance(), IntegerConverter.getInstance()),
+
+ SCALAR_UNIT_SIZE("scalar-unit.size", StringValidator.getInstance(), DefaultConverter.getInstance(),
+ ToscaValueDefaultConverter.getInstance()),
+
+ SCALAR_UNIT_TIME("scalar-unit.time", StringValidator.getInstance(), DefaultConverter.getInstance(),
+ ToscaValueDefaultConverter.getInstance()),
+
+ SCALAR_UNIT_FREQUENCY("scalar-unit.frequency", StringValidator.getInstance(), DefaultConverter.getInstance(),
+ ToscaValueDefaultConverter.getInstance()),
+
+ RANGE("range", StringValidator.getInstance(), DefaultConverter.getInstance(),
+ ToscaValueDefaultConverter.getInstance()),
+
+ TIMESTAMP("timestamp", StringValidator.getInstance(), DefaultConverter.getInstance(),
+ ToscaValueDefaultConverter.getInstance()),
+
+ MAP("map", MapValidator.getInstance(), MapConverter.getInstance(), ToscaMapValueConverter.getInstance()),
+
+ LIST("list", ListValidator.getInstance(), ListConverter.getInstance(), ToscaListValueConverter.getInstance()),
+
+ VERSION("version", StringValidator.getInstance(), DefaultConverter.getInstance(),
+ ToscaValueDefaultConverter.getInstance()),
+
+ KEY("key", KeyValidator.getInstance(), StringConvertor.getInstance(), ToscaValueDefaultConverter.getInstance()),
+
+ JSON("json", JsonValidator.getInstance(), JsonConverter.getInstance(), ToscaJsonValueConverter.getInstance());
+
+ // CREDENTIAL("tosca.datatypes.Credential", StringValidator.getInstance(),
+ // DefaultConverter.getInstance());
+
+ private String type;
+ private PropertyTypeValidator validator;
+ private PropertyValueConverter converter;
+ private ToscaValueConverter valueConverter;
+ private boolean isAbstract = false;
+
+ ToscaPropertyType(String type, PropertyTypeValidator validator, PropertyValueConverter converter,
+ ToscaValueConverter valueConverter) {
+ this.type = type;
+ this.validator = validator;
+ this.converter = converter;
+ this.valueConverter = valueConverter;
+ }
+
+ ToscaPropertyType(String type, PropertyTypeValidator validator, PropertyValueConverter converter,
+ ToscaValueConverter valueConverter, boolean isAbstract) {
+ this(type, validator, converter, valueConverter);
+ this.isAbstract = isAbstract;
+ }
+
+ // private static final Pattern TIMESTAMP_REGEX = Pattern
+ // .compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?([Tt]|[
+ // \\t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](\\.[0-9]*)?(([ \\t]*)Z|([
+ // \\t]*)[-+][0-9][0-9]?(:[0-9][0-9])?)?");
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public PropertyTypeValidator getValidator() {
+ return validator;
+ }
+
+ public void setValidator(PropertyTypeValidator validator) {
+ this.validator = validator;
+ }
+
+ public PropertyValueConverter getConverter() {
+ return converter;
+ }
+
+ public void setConverter(PropertyValueConverter converter) {
+ this.converter = converter;
+ }
+
+ public boolean isAbstract() {
+ return isAbstract;
+ }
+
+ public void setAbstract(boolean isAbstract) {
+ this.isAbstract = isAbstract;
+ }
+
+ public ToscaValueConverter getValueConverter() {
+ return valueConverter;
+ }
+
+ public void setValueConverter(ToscaValueConverter valueConverter) {
+ this.valueConverter = valueConverter;
+ }
+
+ public static ToscaPropertyType isValidType(String typeName) {
+ if (typeName == null) {
+ return null;
+ }
+
+ for (ToscaPropertyType type : ToscaPropertyType.values()) {
+ if (type.getType().equals(typeName)) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+ public static boolean isScalarType(String dataTypeName) {
+
+ ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
+
+ return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract() == false;
+
+ }
+
+ public static ToscaPropertyType getTypeIfScalar(String dataTypeName) {
+
+ ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
+
+ if (isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract() == false) {
+ return isPrimitiveToscaType;
+ } else {
+ return null;
+ }
+
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaType.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaType.java
new file mode 100644
index 0000000000..88642f8240
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/ToscaType.java
@@ -0,0 +1,120 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+/**
+ * The primitive type that TOSCA YAML supports.
+ *
+ * @author mkv
+ */
+public enum ToscaType {
+ STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, VERSION;
+
+ // private static final Pattern TIMESTAMP_REGEX = Pattern
+ // .compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?([Tt]|[
+ // \\t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](\\.[0-9]*)?(([ \\t]*)Z|([
+ // \\t]*)[-+][0-9][0-9]?(:[0-9][0-9])?)?");
+
+ public static ToscaType fromYamlTypeName(String typeName) {
+ if (typeName == null) {
+ return null;
+ }
+ try {
+ return ToscaType.valueOf(typeName.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ public boolean isValidValue(String value) {
+ switch (this) {
+ case BOOLEAN:
+ return value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false");
+ case FLOAT:
+ return isFloat(value);
+ case INTEGER:
+ return isInteger(value);
+ case STRING:
+ return true;
+ case TIMESTAMP:
+ try {
+ DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.US).parse(value);
+ return true;
+ } catch (ParseException e) {
+ return false;
+ }
+ case VERSION:
+ return VersionUtil.isValid(value);
+ default:
+ return false;
+ }
+ }
+
+ private boolean isFloat(String value) {
+ try {
+ Float.valueOf(value);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isInteger(String value) {
+ try {
+ Long.valueOf(value);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public Object convert(String value) {
+ switch (this) {
+ case STRING:
+ return value;
+ case BOOLEAN:
+ return Boolean.valueOf(value);
+ case FLOAT:
+ return Double.valueOf(value);
+ case INTEGER:
+ return Long.valueOf(value);
+ case TIMESTAMP:
+ try {
+ return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.US).parse(value);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException("Value must be a valid timestamp", e);
+ }
+ case VERSION:
+ return VersionUtil.parseVersion(value);
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/VersionUtil.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/VersionUtil.java
new file mode 100644
index 0000000000..91d806edd9
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/VersionUtil.java
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca;
+
+import java.util.regex.Pattern;
+
+import org.openecomp.sdc.be.model.tosca.version.ApplicationVersionException;
+import org.openecomp.sdc.be.model.tosca.version.Version;
+
+public final class VersionUtil {
+
+ /** Utility class should not have public constructor. */
+ private VersionUtil() {
+ }
+
+ /**
+ * The version must begin with a bloc of numbers, and then it can have one
+ * or more bloc of numbers separated by '.' and then it can have alpha
+ * numeric bloc separated by '.' or '-'
+ */
+ public static final Pattern VERSION_PATTERN = Pattern.compile("\\d+(?:\\.\\d+)*(?:[\\.-]\\p{Alnum}+)*");
+ private static final String SNAPSHOT_IDENTIFIER = "SNAPSHOT";
+
+ /**
+ * Check if a version is a SNAPSHOT (development) version.
+ *
+ * @param version
+ * The actual version string.
+ * @return True if the version is a SNAPSHOT version, false if not (RELEASE
+ * version).
+ */
+ public static boolean isSnapshot(String version) {
+ return version.toUpperCase().contains(SNAPSHOT_IDENTIFIER);
+ }
+
+ /**
+ * Check if a version is valid
+ *
+ * @param version
+ * version string to parse
+ * @return true if it's following the defined version pattern
+ */
+ public static boolean isValid(String version) {
+ return VERSION_PATTERN.matcher(version).matches();
+ }
+
+ /**
+ * Parse the version's text to produce a comparable version object
+ *
+ * @param version
+ * version text to parse
+ * @return a comparable version object
+ * @throws ApplicationVersionException
+ * if the version text is not following the defined version
+ * pattern
+ */
+ public static Version parseVersion(String version) {
+ if (!isValid(version)) {
+ throw new ApplicationVersionException(
+ "This version is not valid [" + version + "] as it does not match [" + VERSION_PATTERN + "]");
+ } else {
+ return new Version(version);
+ }
+ }
+
+ /**
+ * Compare 2 versions
+ *
+ * @param versionLeft
+ * @param versionRight
+ * @return
+ */
+ public static int compare(String versionLeft, String versionRight) {
+ return parseVersion(versionLeft).compareTo(parseVersion(versionRight));
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractComparablePropertyConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractComparablePropertyConstraint.java
new file mode 100644
index 0000000000..7b46692253
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractComparablePropertyConstraint.java
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+@SuppressWarnings("rawtypes")
+public abstract class AbstractComparablePropertyConstraint extends AbstractPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2002627754053326321L;
+
+ private Comparable comparable;
+
+ protected Comparable getComparable() {
+ return comparable;
+ }
+
+ protected void initialize(String rawTextValue, ToscaType propertyType)
+ throws ConstraintValueDoNotMatchPropertyTypeException {
+ // Perform verification that the property type is supported for
+ // comparison
+ ConstraintUtil.checkComparableType(propertyType);
+ // Check if the text value is valid for the property type
+ if (propertyType.isValidValue(rawTextValue)) {
+ // Convert the raw text value to a comparable value
+ comparable = ConstraintUtil.convertToComparable(propertyType, rawTextValue);
+ } else {
+ // Invalid value throw exception
+ throw new ConstraintValueDoNotMatchPropertyTypeException(
+ "The value [" + rawTextValue + "] is not valid for the type [" + propertyType + "]");
+ }
+ }
+
+ protected abstract void doValidate(Object propertyValue) throws ConstraintViolationException;
+
+ @Override
+ public void validate(Object propertyValue) throws ConstraintViolationException {
+ if (propertyValue == null) {
+ throw new ConstraintViolationException("Value to check is null");
+ }
+ if (!(comparable.getClass().isAssignableFrom(propertyValue.getClass()))) {
+ throw new ConstraintViolationException("Value to check is not comparable to reference type, value type ["
+ + propertyValue.getClass() + "], reference type [" + comparable.getClass() + "]");
+ }
+ doValidate(propertyValue);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractPropertyConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractPropertyConstraint.java
new file mode 100644
index 0000000000..950a7fa9b9
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractPropertyConstraint.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+import org.openecomp.sdc.be.model.tosca.version.ApplicationVersionException;
+
+public abstract class AbstractPropertyConstraint implements PropertyConstraint, Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 4459522275459723374L;
+
+ @Override
+ public void validate(ToscaType toscaType, String propertyTextValue) throws ConstraintViolationException {
+ try {
+ validate(toscaType.convert(propertyTextValue));
+ } catch (IllegalArgumentException | ApplicationVersionException e) {
+ throw new ConstraintViolationException(
+ "String value [" + propertyTextValue + "] is not valid for type [" + toscaType + "]", e);
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractStringPropertyConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractStringPropertyConstraint.java
new file mode 100644
index 0000000000..142caa2017
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/AbstractStringPropertyConstraint.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public abstract class AbstractStringPropertyConstraint extends AbstractPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6857605164938136232L;
+
+ protected abstract void doValidate(String propertyValue) throws ConstraintViolationException;
+
+ @Override
+ public void validate(Object propertyValue) throws ConstraintViolationException {
+ if (propertyValue == null) {
+ throw new ConstraintViolationException("Value to validate is null");
+ }
+ if (!(propertyValue instanceof String)) {
+ throw new ConstraintViolationException("This constraint can only be applied on String value");
+ }
+ doValidate((String) propertyValue);
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ ConstraintUtil.checkStringType(propertyType);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintType.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintType.java
new file mode 100644
index 0000000000..5f19d15293
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintType.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.util.Arrays;
+import java.util.List;
+
+public enum ConstraintType {
+
+ IN_RANGE("inRange"),
+
+ GREATER_THAN("greaterThan", "greater_than"),
+
+ GREATER_OR_EQUAL("greaterOrEqual", "greater_or_equal"),
+
+ LESS_OR_EQUAL("lessOrEqual", "less_or_equal"),
+
+ MIN_LENGTH("minLength", "min_length"),
+
+ VALID_VALUES("validValues", "valid_values"),
+
+ LESS_THAN("lessThan", "less_than");
+
+ List<String> types;
+
+ private ConstraintType(String... types) {
+ this.types = Arrays.asList(types);
+ }
+
+ public List<String> getTypes() {
+ return types;
+ }
+
+ public static ConstraintType getByType(String type) {
+ for (ConstraintType inst : ConstraintType.values()) {
+ if (inst.getTypes().contains(type)) {
+ return inst;
+ }
+ }
+ return null;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintUtil.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintUtil.java
new file mode 100644
index 0000000000..79ac5caf25
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ConstraintUtil.java
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+
+/**
+ * Utility class to validate constraints types.
+ */
+public final class ConstraintUtil {
+
+ private ConstraintUtil() {
+ }
+
+ /**
+ * Validates that the {@link ToscaType} specified is a
+ * {@link ToscaType#STRING}.
+ *
+ * @param propertyType
+ * The property tosca type.
+ * @throws ConstraintValueDoNotMatchPropertyTypeException
+ * In case the type is not {@link ToscaType#STRING}.
+ */
+ public static void checkStringType(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ if (!ToscaType.STRING.equals(propertyType)) {
+ throw new ConstraintValueDoNotMatchPropertyTypeException(
+ "Invalid property type <" + propertyType.toString() + ">");
+ }
+ }
+
+ /**
+ * Verify that the given tosca type is supported for comparison
+ *
+ * @param propertyType
+ * the tosca type to check
+ * @throws ConstraintValueDoNotMatchPropertyTypeException
+ * if the property type cannot be compared
+ */
+ public static void checkComparableType(ToscaType propertyType)
+ throws ConstraintValueDoNotMatchPropertyTypeException {
+ // The validity of the value is already assured by us with our
+ // ToscaType.convert() method
+ // here we just want to check that the constraint is not used on
+ // unsupported type as boolean
+ switch (propertyType) {
+ case FLOAT:
+ case INTEGER:
+ case TIMESTAMP:
+ case VERSION:
+ break;
+ case STRING:
+ case BOOLEAN:
+ throw new ConstraintValueDoNotMatchPropertyTypeException(
+ "Constraint is invalid for property type <" + propertyType.toString() + ">");
+ default:
+ throw new ConstraintValueDoNotMatchPropertyTypeException(
+ "Invalid property type <" + propertyType.toString() + ">");
+ }
+ }
+
+ /**
+ * Convert a string value following its type throw exception if it cannot be
+ * converted to a comparable
+ *
+ * @param propertyType
+ * the type of the property
+ * @param value
+ * the value to convert
+ * @return the converted comparable
+ * @throws ConstraintValueDoNotMatchPropertyTypeException
+ * if the converted value is not a comparable
+ */
+ @SuppressWarnings("rawtypes")
+ public static Comparable convertToComparable(ToscaType propertyType, String value)
+ throws ConstraintValueDoNotMatchPropertyTypeException {
+ Object comparableObj = propertyType.convert(value);
+ if (!(comparableObj instanceof Comparable)) {
+ throw new IllegalArgumentException(
+ "Try to convert a value of a type which is not comparable [" + propertyType + "] to Comparable");
+ } else {
+ return (Comparable) comparableObj;
+ }
+ }
+
+ public static class ConstraintInformation {
+ public ConstraintInformation(String name, Object reference, String value, String type) {
+
+ this.name = name;
+ this.reference = reference;
+ this.value = value;
+ this.type = type;
+
+ }
+
+ private String name;
+ private Object reference;
+ private String value;
+ private String type;
+ }
+
+ public static ConstraintInformation getConstraintInformation(Object constraint) throws IntrospectionException {
+ PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(constraint.getClass())
+ .getPropertyDescriptors();
+ PropertyDescriptor firstDescriptor = null;
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ if (propertyDescriptor.getReadMethod() != null && propertyDescriptor.getWriteMethod() != null) {
+ firstDescriptor = propertyDescriptor;
+ break;
+ }
+ }
+ if (firstDescriptor == null) {
+ throw new IntrospectionException("Cannot find constraint name");
+ }
+ try {
+ return new ConstraintInformation(firstDescriptor.getName(),
+ firstDescriptor.getReadMethod().invoke(constraint), null, null);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new IntrospectionException("Cannot retrieve constraint reference " + e.getMessage());
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/EqualConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/EqualConstraint.java
new file mode 100644
index 0000000000..530dcb0cc6
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/EqualConstraint.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+//import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class EqualConstraint extends AbstractPropertyConstraint implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -1596093341744641483L;
+
+ @NotNull
+ private String equal;
+
+ // @JsonIgnore
+ private Object typed;
+
+ public EqualConstraint(String equal) {
+ super();
+ this.equal = equal;
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ if (propertyType.isValidValue(equal)) {
+ typed = propertyType.convert(equal);
+ } else {
+ throw new ConstraintValueDoNotMatchPropertyTypeException("equal constraint has invalid value <" + equal
+ + "> property type is <" + propertyType.toString() + ">");
+ }
+ }
+
+ @Override
+ public void validate(Object propertyValue) throws ConstraintViolationException {
+ if (propertyValue == null) {
+ if (typed != null) {
+ fail(null);
+ }
+ } else if (typed == null) {
+ fail(propertyValue);
+ } else if (!typed.equals(propertyValue)) {
+ fail(propertyValue);
+ }
+ }
+
+ private void fail(Object propertyValue) throws ConstraintViolationException {
+ throw new ConstraintViolationException("Equal constraint violation, the reference is <" + equal
+ + "> but the value to compare is <" + propertyValue + ">");
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterOrEqualConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterOrEqualConstraint.java
new file mode 100644
index 0000000000..4f2c3ad9ca
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterOrEqualConstraint.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public class GreaterOrEqualConstraint extends AbstractComparablePropertyConstraint implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = -5937851077034490609L;
+
+ @NotNull
+ private String greaterOrEqual;
+
+ public GreaterOrEqualConstraint(String greaterOrEqual) {
+ super();
+ this.greaterOrEqual = greaterOrEqual;
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ initialize(greaterOrEqual, propertyType);
+ }
+
+ @Override
+ protected void doValidate(Object propertyValue) throws ConstraintViolationException {
+ if (getComparable().compareTo(propertyValue) > 0) {
+ throw new ConstraintViolationException(propertyValue + " <= " + greaterOrEqual);
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterThanConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterThanConstraint.java
new file mode 100644
index 0000000000..aea2a201ab
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/GreaterThanConstraint.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public class GreaterThanConstraint extends AbstractComparablePropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 405723215512121896L;
+
+ public GreaterThanConstraint(String greaterThan) {
+ super();
+ this.greaterThan = greaterThan;
+ }
+
+ @NotNull
+ private String greaterThan;
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ initialize(greaterThan, propertyType);
+ }
+
+ @Override
+ protected void doValidate(Object propertyValue) throws ConstraintViolationException {
+ if (getComparable().compareTo(propertyValue) >= 0) {
+ throw new ConstraintViolationException(propertyValue + " < " + greaterThan);
+ }
+ }
+
+ public String getGreaterThan() {
+ return greaterThan;
+ }
+
+ public void setGreaterThan(String greaterThan) {
+ this.greaterThan = greaterThan;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/InRangeConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/InRangeConstraint.java
new file mode 100644
index 0000000000..e8821c2c21
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/InRangeConstraint.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+//import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.Lists;
+
+public class InRangeConstraint extends AbstractPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8038401707152824493L;
+
+ private List<String> inRange;
+
+ private Comparable min;
+ private Comparable max;
+
+ public InRangeConstraint(List<String> inRange) {
+ super();
+ this.inRange = inRange;
+ }
+
+ public InRangeConstraint() {
+ super();
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ // Perform verification that the property type is supported for
+ // comparison
+ ConstraintUtil.checkComparableType(propertyType);
+ if (inRange == null || inRange.size() != 2) {
+ throw new ConstraintValueDoNotMatchPropertyTypeException("In range constraint must have two elements.");
+ }
+ String minRawText = inRange.get(0);
+ String maxRawText = inRange.get(1);
+ if (!propertyType.isValidValue(minRawText)) {
+ throw new ConstraintValueDoNotMatchPropertyTypeException("Invalid min value for in range constraint ["
+ + minRawText + "] as it does not follow the property type [" + propertyType + "]");
+ }
+ if (!propertyType.isValidValue(maxRawText)) {
+ throw new ConstraintValueDoNotMatchPropertyTypeException("Invalid max value for in range constraint ["
+ + maxRawText + "] as it does not follow the property type [" + propertyType + "]");
+ }
+ min = ConstraintUtil.convertToComparable(propertyType, minRawText);
+ max = ConstraintUtil.convertToComparable(propertyType, maxRawText);
+ }
+
+ @Override
+ public void validate(Object propertyValue) throws ConstraintViolationException {
+ if (propertyValue == null) {
+ throw new ConstraintViolationException("Value to check is null");
+ }
+ if (!(min.getClass().isAssignableFrom(propertyValue.getClass()))) {
+ throw new ConstraintViolationException("Value to check is not comparable to range type, value type ["
+ + propertyValue.getClass() + "], range type [" + min.getClass() + "]");
+ }
+ if (min.compareTo(propertyValue) > 0 || max.compareTo(propertyValue) < 0) {
+ throw new ConstraintViolationException("The value [" + propertyValue + "] is out of range " + inRange);
+ }
+ }
+
+ // @JsonProperty
+ @NotNull
+ public String getRangeMinValue() {
+ if (inRange != null) {
+ return inRange.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ // @JsonProperty
+ public void setRangeMinValue(String minValue) {
+ if (inRange == null) {
+ inRange = Lists.newArrayList(minValue, "");
+ } else {
+ inRange.set(0, minValue);
+ }
+ }
+
+ // @JsonProperty
+ @NotNull
+ public String getRangeMaxValue() {
+ if (inRange != null) {
+ return inRange.get(1);
+ } else {
+ return null;
+ }
+ }
+
+ // @JsonProperty
+ public void setRangeMaxValue(String maxValue) {
+ if (inRange == null) {
+ inRange = Lists.newArrayList("", maxValue);
+ } else {
+ inRange.set(1, maxValue);
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LengthConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LengthConstraint.java
new file mode 100644
index 0000000000..2ba0071f2f
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LengthConstraint.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public class LengthConstraint extends AbstractStringPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6249912030281791233L;
+
+ @NotNull
+ private Integer length;
+
+ @Override
+ protected void doValidate(String propertyValue) throws ConstraintViolationException {
+ if (propertyValue.length() != length) {
+ throw new ConstraintViolationException("The length of the value is not equals to [" + length + "]");
+ }
+ }
+
+ public Integer getLength() {
+ return length;
+ }
+
+ public void setLength(Integer length) {
+ this.length = length;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessOrEqualConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessOrEqualConstraint.java
new file mode 100644
index 0000000000..1491fe327d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessOrEqualConstraint.java
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+//import alien4cloud.json.deserializer.TextDeserializer;
+//import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class LessOrEqualConstraint extends AbstractComparablePropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -4907864317687138678L;
+
+ // @JsonDeserialize(using = TextDeserializer.class)
+ @NotNull
+ private String lessOrEqual;
+
+ public LessOrEqualConstraint(String lessOrEqual) {
+ super();
+ this.lessOrEqual = lessOrEqual;
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ initialize(lessOrEqual, propertyType);
+ }
+
+ @Override
+ protected void doValidate(Object propertyValue) throws ConstraintViolationException {
+ if (getComparable().compareTo(propertyValue) < 0) {
+ throw new ConstraintViolationException(propertyValue + " >= " + lessOrEqual);
+ }
+ }
+
+ public String getLessOrEqual() {
+ return lessOrEqual;
+ }
+
+ public void setLessOrEqual(String lessOrEqual) {
+ this.lessOrEqual = lessOrEqual;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessThanConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessThanConstraint.java
new file mode 100644
index 0000000000..2fc43febbf
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/LessThanConstraint.java
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public class LessThanConstraint extends AbstractComparablePropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2267623014703859501L;
+
+ @NotNull
+ private String lessThan;
+
+ public LessThanConstraint(String lessThan) {
+ super();
+ this.lessThan = lessThan;
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ initialize(lessThan, propertyType);
+ }
+
+ @Override
+ protected void doValidate(Object propertyValue) throws ConstraintViolationException {
+ if (getComparable().compareTo(propertyValue) <= 0) {
+ throw new ConstraintViolationException(propertyValue + " > " + lessThan);
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MaxLengthConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MaxLengthConstraint.java
new file mode 100644
index 0000000000..b6a80afced
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MaxLengthConstraint.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public class MaxLengthConstraint extends AbstractStringPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6377603705670201256L;
+
+ @NotNull
+ private Integer maxLength;
+
+ public MaxLengthConstraint(Integer maxLength) {
+ this.maxLength = maxLength;
+ }
+
+ public MaxLengthConstraint() {
+ super();
+ }
+
+ @Override
+ protected void doValidate(String propertyValue) throws ConstraintViolationException {
+ if (propertyValue.length() > maxLength) {
+ throw new ConstraintViolationException("The length of the value is greater than [" + maxLength + "]");
+ }
+ }
+
+ public Integer getMaxLength() {
+ return maxLength;
+ }
+
+ public void setMaxLength(Integer maxLength) {
+ this.maxLength = maxLength;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MinLengthConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MinLengthConstraint.java
new file mode 100644
index 0000000000..f92e5fbb2c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/MinLengthConstraint.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+public class MinLengthConstraint extends AbstractStringPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 32422424680811240L;
+
+ @NotNull
+ private Integer minLength;
+
+ public MinLengthConstraint(Integer minLength) {
+ this.minLength = minLength;
+ }
+
+ public MinLengthConstraint() {
+ super();
+ }
+
+ @Override
+ protected void doValidate(String propertyValue) throws ConstraintViolationException {
+ if (propertyValue.length() < minLength) {
+ throw new ConstraintViolationException("The length of the value is less than [" + minLength + "]");
+ }
+ }
+
+ public Integer getMinLength() {
+ return minLength;
+ }
+
+ public void setMinLength(Integer minLength) {
+ this.minLength = minLength;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/PatternConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/PatternConstraint.java
new file mode 100644
index 0000000000..c85c1601e4
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/PatternConstraint.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+import java.util.regex.Pattern;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+//import com.fasterxml.jackson.annotation.JsonIgnore;
+
+public class PatternConstraint extends AbstractStringPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8708185294968697107L;
+
+ @NotNull
+ private String pattern;
+
+ // @JsonIgnore
+ private Pattern compiledPattern;
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ this.compiledPattern = Pattern.compile(this.pattern);
+ }
+
+ @Override
+ protected void doValidate(String propertyValue) throws ConstraintViolationException {
+ if (!compiledPattern.matcher(propertyValue).matches()) {
+ throw new ConstraintViolationException("The value do not match pattern " + pattern);
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ValidValuesConstraint.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ValidValuesConstraint.java
new file mode 100644
index 0000000000..738f5150e3
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/ValidValuesConstraint.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.constraints.NotNull;
+
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
+import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
+
+//import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.common.collect.Sets;
+
+public class ValidValuesConstraint extends AbstractPropertyConstraint implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 5906087180079892853L;
+
+ @NotNull
+ private List<String> validValues;
+ // @JsonIgnore
+ private Set<Object> validValuesTyped;
+
+ public ValidValuesConstraint(List<String> validValues) {
+ super();
+ this.validValues = validValues;
+ }
+
+ public ValidValuesConstraint() {
+ super();
+ }
+
+ @Override
+ public void initialize(ToscaType propertyType) throws ConstraintValueDoNotMatchPropertyTypeException {
+ validValuesTyped = Sets.newHashSet();
+ if (validValues == null) {
+ throw new ConstraintValueDoNotMatchPropertyTypeException(
+ "validValues constraint has invalid value <> property type is <" + propertyType.toString() + ">");
+ }
+ for (String value : validValues) {
+ if (!propertyType.isValidValue(value)) {
+ throw new ConstraintValueDoNotMatchPropertyTypeException("validValues constraint has invalid value <"
+ + value + "> property type is <" + propertyType.toString() + ">");
+ } else {
+ validValuesTyped.add(propertyType.convert(value));
+ }
+ }
+ }
+
+ @Override
+ public void validate(Object propertyValue) throws ConstraintViolationException {
+ if (propertyValue == null) {
+ throw new ConstraintViolationException("Value to validate is null");
+ }
+ if (!validValuesTyped.contains(propertyValue)) {
+ throw new ConstraintViolationException("The value is not in the list of valid values");
+ }
+ }
+
+ public List<String> getValidValues() {
+ return validValues;
+ }
+
+ public void setValidValues(List<String> validValues) {
+ this.validValues = validValues;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintFunctionalException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintFunctionalException.java
new file mode 100644
index 0000000000..3fd165f41e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintFunctionalException.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil.ConstraintInformation;
+
+/**
+ * All functional error related to constraint processing must go here
+ *
+ * @author mkv
+ *
+ */
+public class ConstraintFunctionalException extends FunctionalException {
+
+ private static final long serialVersionUID = 1L;
+
+ protected ConstraintInformation constraintInformation;
+
+ public ConstraintFunctionalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConstraintFunctionalException(String message) {
+ super(message);
+ }
+
+ public ConstraintFunctionalException(String message, Throwable cause, ConstraintInformation constraintInformation) {
+ super(message, cause);
+ this.constraintInformation = constraintInformation;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintRequiredParameterException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintRequiredParameterException.java
new file mode 100644
index 0000000000..2416405de4
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintRequiredParameterException.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil.ConstraintInformation;
+
+/**
+ * Exception to be thrown when a required property is not provided
+ *
+ * @author mourouvi
+ *
+ */
+public class ConstraintRequiredParameterException extends ConstraintFunctionalException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ConstraintRequiredParameterException(String message) {
+ super(message);
+ }
+
+ public ConstraintRequiredParameterException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConstraintRequiredParameterException(String message, Throwable cause,
+ ConstraintInformation constraintInformation) {
+ super(message, cause);
+ this.constraintInformation = constraintInformation;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintTechnicalException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintTechnicalException.java
new file mode 100644
index 0000000000..3816ac61dd
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintTechnicalException.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+/**
+ * Base class for all constraint related exceptions
+ *
+ * @author esofer
+ *
+ */
+public class ConstraintTechnicalException extends Exception {
+
+ private static final long serialVersionUID = 5829360730980521567L;
+
+ public ConstraintTechnicalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConstraintTechnicalException(String message) {
+ super(message);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintValueDoNotMatchPropertyTypeException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintValueDoNotMatchPropertyTypeException.java
new file mode 100644
index 0000000000..6ba0d9b864
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintValueDoNotMatchPropertyTypeException.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil.ConstraintInformation;
+
+/**
+ * Exception to be thrown when a constraint definition is invalid because the
+ * specified value doesn't match the property type.
+ *
+ * @author esofer
+ */
+public class ConstraintValueDoNotMatchPropertyTypeException extends ConstraintFunctionalException {
+
+ private static final long serialVersionUID = 4342613849660957651L;
+
+ public ConstraintValueDoNotMatchPropertyTypeException(String message) {
+ super(message);
+ }
+
+ public ConstraintValueDoNotMatchPropertyTypeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConstraintValueDoNotMatchPropertyTypeException(String message, Throwable cause,
+ ConstraintInformation constraintInformation) {
+ super(message, cause);
+ this.constraintInformation = constraintInformation;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintViolationException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintViolationException.java
new file mode 100644
index 0000000000..7fce63841d
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/ConstraintViolationException.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
+import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil.ConstraintInformation;
+
+/**
+ * Exception happened while user violated a predefined constraint
+ *
+ * @author mkv
+ *
+ */
+public class ConstraintViolationException extends ConstraintFunctionalException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ConstraintViolationException(String message) {
+ super(message);
+ }
+
+ public ConstraintViolationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConstraintViolationException(String message, Throwable cause, ConstraintInformation constraintInformation) {
+ super(message, cause);
+ this.constraintInformation = constraintInformation;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/FunctionalException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/FunctionalException.java
new file mode 100644
index 0000000000..1454306e89
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/FunctionalException.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+/**
+ * All functional exception which is related to user input must go here. It's a
+ * checked exception to force error handling and checking.
+ *
+ * @author mkv
+ *
+ */
+public class FunctionalException extends Exception {
+
+ private static final long serialVersionUID = 6712845685798792493L;
+
+ public FunctionalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FunctionalException(String message) {
+ super(message);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/InvalidPropertyConstraintImplementationException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/InvalidPropertyConstraintImplementationException.java
new file mode 100644
index 0000000000..2b231d98e3
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/InvalidPropertyConstraintImplementationException.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+/**
+ * Exception thrown when the user defines invalid custom property constraint
+ *
+ * @author mkv
+ *
+ */
+public class InvalidPropertyConstraintImplementationException extends ConstraintTechnicalException {
+
+ private static final long serialVersionUID = 2797550944328544706L;
+
+ public InvalidPropertyConstraintImplementationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidPropertyConstraintImplementationException(String message) {
+ super(message);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/TechnicalException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/TechnicalException.java
new file mode 100644
index 0000000000..1bddeea69b
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/constraints/exception/TechnicalException.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.constraints.exception;
+
+/**
+ * Base class for all Alien technical exception
+ *
+ * @author mkv
+ *
+ */
+public abstract class TechnicalException extends RuntimeException {
+
+ private static final long serialVersionUID = -9152473183025390161L;
+
+ public TechnicalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TechnicalException(String message) {
+ super(message);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/BooleanConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/BooleanConverter.java
new file mode 100644
index 0000000000..f721efb3c9
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/BooleanConverter.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class BooleanConverter implements ToscaValueConverter {
+ private static BooleanConverter booleanConverter = new BooleanConverter();
+
+ public static BooleanConverter getInstance() {
+ return booleanConverter;
+ }
+
+ private BooleanConverter() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return Boolean.valueOf(value);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/DefaultConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/DefaultConverter.java
new file mode 100644
index 0000000000..c190298b52
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/DefaultConverter.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class DefaultConverter implements PropertyValueConverter {
+
+ private static DefaultConverter defaultConverter = new DefaultConverter();
+
+ public static DefaultConverter getInstance() {
+ return defaultConverter;
+ }
+
+ private DefaultConverter() {
+
+ }
+
+ @Override
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return value;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/FloatConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/FloatConverter.java
new file mode 100644
index 0000000000..d3edd9b8bf
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/FloatConverter.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class FloatConverter implements ToscaValueConverter {
+ private static FloatConverter floatConverter = new FloatConverter();
+
+ public static FloatConverter getInstance() {
+ return floatConverter;
+ }
+
+ private FloatConverter() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return Double.parseDouble(value);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatBooleanConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatBooleanConverter.java
new file mode 100644
index 0000000000..147b2e9c52
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatBooleanConverter.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class HeatBooleanConverter implements PropertyValueConverter {
+
+ private static HeatBooleanConverter booleanConverter = new HeatBooleanConverter();
+
+ public static HeatBooleanConverter getInstance() {
+ return booleanConverter;
+ }
+
+ private HeatBooleanConverter() {
+
+ }
+
+ @Override
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (value == null) {
+ return null;
+ }
+
+ if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("t") || value.equalsIgnoreCase("on")
+ || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("y") || value.equalsIgnoreCase("1")) {
+ return "true";
+ } else {
+ return "false";
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatCommaDelimitedListConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatCommaDelimitedListConverter.java
new file mode 100644
index 0000000000..a0834791ff
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatCommaDelimitedListConverter.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class HeatCommaDelimitedListConverter implements PropertyValueConverter {
+
+ private static HeatCommaDelimitedListConverter stringConverter = new HeatCommaDelimitedListConverter();
+
+ public static HeatCommaDelimitedListConverter getInstance() {
+ return stringConverter;
+ }
+
+ private HeatCommaDelimitedListConverter() {
+
+ }
+
+ @Override
+ public String convert(String original, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ String coverted = ValidationUtils.removeNoneUtf8Chars(original);
+ coverted = ValidationUtils.removeHtmlTagsOnly(coverted);
+ coverted = ValidationUtils.normaliseWhitespace(coverted);
+ coverted = ValidationUtils.stripOctets(coverted);
+
+ return coverted;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatJsonConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatJsonConverter.java
new file mode 100644
index 0000000000..6e077d60a1
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatJsonConverter.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class HeatJsonConverter implements PropertyValueConverter {
+
+ private static HeatJsonConverter jsonConverter = new HeatJsonConverter();
+
+ public static HeatJsonConverter getInstance() {
+ return jsonConverter;
+ }
+
+ private HeatJsonConverter() {
+
+ }
+
+ @Override
+ public String convert(String original, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ String coverted = ValidationUtils.removeNoneUtf8Chars(original);
+ coverted = ValidationUtils.removeHtmlTagsOnly(coverted);
+ coverted = ValidationUtils.normaliseWhitespace(coverted);
+ coverted = ValidationUtils.stripOctets(coverted);
+ // As opposed to string converter, keeping the " and ' symbols
+ return coverted;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatNumberConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatNumberConverter.java
new file mode 100644
index 0000000000..90781be367
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatNumberConverter.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class HeatNumberConverter implements PropertyValueConverter {
+
+ private static HeatNumberConverter numberConverter = new HeatNumberConverter();
+
+ public static HeatNumberConverter getInstance() {
+ return numberConverter;
+ }
+
+ private HeatNumberConverter() {
+
+ }
+
+ @Override
+ public String convert(String original, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (original == null) {
+ return null;
+ }
+
+ return new BigDecimal(original).toPlainString();
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatStringConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatStringConverter.java
new file mode 100644
index 0000000000..475db1db7c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/HeatStringConverter.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class HeatStringConverter implements PropertyValueConverter {
+
+ private static HeatStringConverter stringConverter = new HeatStringConverter();
+
+ public static HeatStringConverter getInstance() {
+ return stringConverter;
+ }
+
+ private HeatStringConverter() {
+
+ }
+
+ @Override
+ public String convert(String original, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ String coverted = ValidationUtils.removeNoneUtf8Chars(original);
+ coverted = ValidationUtils.normaliseWhitespace(coverted);
+ coverted = ValidationUtils.stripOctets(coverted);
+ coverted = ValidationUtils.removeHtmlTagsOnly(coverted);
+ coverted = coverted.replaceAll("\"", "").replaceAll("\'", "");
+
+ return coverted;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/IntegerConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/IntegerConverter.java
new file mode 100644
index 0000000000..076e5aceef
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/IntegerConverter.java
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class IntegerConverter implements ToscaValueConverter {
+
+ private static IntegerConverter integerConverter = new IntegerConverter();
+
+ public static IntegerConverter getInstance() {
+ return integerConverter;
+ }
+
+ private IntegerConverter() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return Integer.parseInt(value);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/JsonConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/JsonConverter.java
new file mode 100644
index 0000000000..3879430e06
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/JsonConverter.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.io.StringReader;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.GsonFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.stream.JsonReader;
+
+public class JsonConverter implements PropertyValueConverter {
+
+ private static JsonConverter jsonConverter = new JsonConverter();
+
+ private static JsonParser jsonParser = new JsonParser();
+
+ private static Gson gson = GsonFactory.getGson();
+
+ public static JsonConverter getInstance() {
+ return jsonConverter;
+ }
+
+ private JsonConverter() {
+
+ }
+
+ @Override
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ StringReader reader = new StringReader(value);
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+ JsonElement jsonElement = jsonParser.parse(jsonReader);
+ if (jsonElement.isJsonPrimitive()) {
+ return value;
+ }
+ return gson.toJson(jsonElement);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ListConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ListConverter.java
new file mode 100644
index 0000000000..8265cc2690
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ListConverter.java
@@ -0,0 +1,217 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
+import org.openecomp.sdc.be.model.tosca.validators.ListValidator;
+import org.openecomp.sdc.common.util.GsonFactory;
+import org.openecomp.sdc.common.util.JsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+
+import fj.data.Either;
+
+public class ListConverter implements PropertyValueConverter {
+
+ private static ListConverter listConverter = new ListConverter();
+ private static Gson gson = GsonFactory.getGson();
+ private static Logger log = LoggerFactory.getLogger(ListValidator.class.getName());
+
+ DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
+
+ private static JsonParser jsonParser = new JsonParser();
+
+ public static ListConverter getInstance() {
+ return listConverter;
+ }
+
+ @Override
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ Either<String, Boolean> convertWithErrorResult = this.convertWithErrorResult(value, innerType, dataTypes);
+ if (convertWithErrorResult.isRight()) {
+ return null;
+ }
+
+ return convertWithErrorResult.left().value();
+ }
+
+ public Either<String, Boolean> convertWithErrorResult(String value, String innerType,
+ Map<String, DataTypeDefinition> dataTypes) {
+ if (value == null || innerType == null) {
+ return Either.left(value);
+ }
+
+ PropertyValueConverter innerConverter;
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+
+ if (innerToscaType != null) {
+ PropertyValueConverter innerConverter1;
+ switch (innerToscaType) {
+ case STRING:
+ innerConverter1 = ToscaPropertyType.STRING.getConverter();
+ break;
+ case INTEGER:
+ innerConverter1 = ToscaPropertyType.INTEGER.getConverter();
+ break;
+ case FLOAT:
+ innerConverter1 = ToscaPropertyType.FLOAT.getConverter();
+ break;
+ case BOOLEAN:
+ innerConverter1 = ToscaPropertyType.BOOLEAN.getConverter();
+ break;
+ case JSON:
+ innerConverter1 = ToscaPropertyType.JSON.getConverter();
+ break;
+ default:
+ log.debug("inner Tosca Type is unknown");
+ return Either.left(value);
+ }
+ innerConverter = innerConverter1;
+ } else {
+ log.debug("inner Tosca Type {} ia a complex data type.", innerType);
+
+ Either<String, Boolean> validateComplexInnerType = convertComplexInnerType(value, innerType, dataTypes);
+
+ return validateComplexInnerType;
+ }
+
+ try {
+ ArrayList<String> newList = new ArrayList<String>();
+
+ JsonArray jo = (JsonArray) jsonParser.parse(value);
+ int size = jo.size();
+ for (int i = 0; i < size; i++) {
+ JsonElement currentValue = jo.get(i);
+ String element = JsonUtils.toString(currentValue);
+
+ if (element == null || element.isEmpty()) {
+ continue;
+ }
+ element = innerConverter.convert(element, null, dataTypes);
+ newList.add(element);
+ }
+
+ switch (innerToscaType) {
+ case STRING:
+ value = gson.toJson(newList);
+ break;
+ case INTEGER:
+ List<BigInteger> intList = new ArrayList<BigInteger>();
+
+ for (String str : newList) {
+ int base = 10;
+ if (str.contains("0x")) {
+ str = str.replaceFirst("0x", "");
+ base = 16;
+ }
+ if (str.contains("0o")) {
+ str = str.replaceFirst("0o", "");
+ base = 8;
+ }
+ intList.add(new BigInteger(str, base));
+ }
+ value = gson.toJson(intList);
+ break;
+ case FLOAT:
+ value = "[";
+ for (String str : newList) {
+ value += str + ",";
+ }
+ value = value.substring(0, value.length() - 1);
+ value += "]";
+ break;
+ case BOOLEAN:
+ List<Boolean> boolList = new ArrayList<Boolean>();
+ for (String str : newList) {
+ boolList.add(Boolean.valueOf(str));
+ }
+ value = gson.toJson(boolList);
+ break;
+ default:
+ value = gson.toJson(newList);
+ log.debug("inner Tosca Type unknown : {}", innerToscaType);
+ }
+
+ } catch (JsonParseException e) {
+ log.debug("Failed to parse json : {}. {}", value, e);
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("List Converter");
+ return Either.right(false);
+ }
+
+ return Either.left(value);
+ }
+
+ private Either<String, Boolean> convertComplexInnerType(String value, String innerType,
+ Map<String, DataTypeDefinition> allDataTypes) {
+
+ DataTypeDefinition dataTypeDefinition = allDataTypes.get(innerType);
+ if (dataTypeDefinition == null) {
+ log.debug("Cannot find data type {}", innerType);
+ return Either.right(false);
+ }
+
+ List<JsonElement> newList = new ArrayList<>();
+
+ try {
+
+ JsonArray jo = (JsonArray) jsonParser.parse(value);
+ int size = jo.size();
+ for (int i = 0; i < size; i++) {
+ JsonElement currentValue = jo.get(i);
+
+ if (currentValue != null) {
+
+ String element = JsonUtils.toString(currentValue);
+
+ ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter
+ .validateAndUpdate(element, dataTypeDefinition, allDataTypes);
+ if (validateAndUpdate.right.booleanValue() == false) {
+ log.debug("Cannot parse value {} from type {} in list position {}", currentValue, innerType, i);
+ return Either.right(false);
+ }
+ JsonElement newValue = validateAndUpdate.left;
+ newList.add(newValue);
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Failed to parse the value {} of list parameter.", value);
+ return Either.right(false);
+ }
+ value = gson.toJson(newList);
+ return Either.left(value);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/LowerCaseConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/LowerCaseConverter.java
new file mode 100644
index 0000000000..f33be29327
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/LowerCaseConverter.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class LowerCaseConverter implements PropertyValueConverter {
+
+ private static LowerCaseConverter booleanConverter = new LowerCaseConverter();
+
+ public static LowerCaseConverter getInstance() {
+ return booleanConverter;
+ }
+
+ private LowerCaseConverter() {
+
+ }
+
+ @Override
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (value == null) {
+ return null;
+ }
+ return value.toLowerCase();
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/MapConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/MapConverter.java
new file mode 100644
index 0000000000..921c6d0d41
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/MapConverter.java
@@ -0,0 +1,249 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
+import org.openecomp.sdc.be.model.tosca.validators.ListValidator;
+import org.openecomp.sdc.common.util.GsonFactory;
+import org.openecomp.sdc.common.util.JsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+
+import fj.data.Either;
+
+public class MapConverter implements PropertyValueConverter {
+
+ private static MapConverter mapConverter = new MapConverter();
+ private static Gson gson = GsonFactory.getGson();
+ private static Logger log = LoggerFactory.getLogger(ListValidator.class.getName());
+
+ DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
+
+ private static JsonParser jsonParser = new JsonParser();
+
+ public static MapConverter getInstance() {
+ return mapConverter;
+ }
+
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+
+ Either<String, Boolean> convertWithErrorResult = this.convertWithErrorResult(value, innerType, dataTypes);
+ if (convertWithErrorResult.isRight()) {
+ return null;
+ }
+
+ return convertWithErrorResult.left().value();
+ }
+
+ public Either<String, Boolean> convertWithErrorResult(String value, String innerType,
+ Map<String, DataTypeDefinition> dataTypes) {
+
+ if (value == null || value == "" || innerType == null) {
+ return Either.left(value);
+ }
+
+ PropertyValueConverter innerConverter;
+ PropertyValueConverter keyConverter = ToscaPropertyType.STRING.getConverter();
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+
+ if (innerToscaType != null) {
+ switch (innerToscaType) {
+ case STRING:
+ innerConverter = ToscaPropertyType.STRING.getConverter();
+ break;
+ case INTEGER:
+ innerConverter = ToscaPropertyType.INTEGER.getConverter();
+ break;
+ case FLOAT:
+ innerConverter = ToscaPropertyType.FLOAT.getConverter();
+ break;
+ case BOOLEAN:
+ innerConverter = ToscaPropertyType.BOOLEAN.getConverter();
+ break;
+ case JSON:
+ innerConverter = ToscaPropertyType.JSON.getConverter();
+ break;
+ default:
+ log.debug("inner Tosca Type is unknown");
+ return Either.left(value);
+ }
+
+ } else {
+
+ log.debug("inner Tosca Type {} ia a complex data type.", innerType);
+
+ Either<String, Boolean> validateComplexInnerType = convertComplexInnerType(value, innerType, keyConverter,
+ dataTypes);
+
+ return validateComplexInnerType;
+
+ }
+
+ try {
+ Map<String, String> newMap = new HashMap<String, String>();
+
+ JsonElement jsonObject = jsonParser.parse(value);
+ JsonObject asJsonObject = jsonObject.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String key = entry.getKey();
+ JsonElement jsonValue = entry.getValue();
+
+ key = keyConverter.convert(entry.getKey(), null, dataTypes);
+
+ String element = JsonUtils.toString(jsonValue);
+
+ String val = innerConverter.convert(element, null, dataTypes);
+ newMap.put(key, val);
+ }
+
+ String objVal;
+ switch (innerToscaType) {
+ case STRING:
+ value = gson.toJson(newMap);
+ break;
+ case INTEGER:
+ String key = null;
+ Map<String, Integer> intMap = new HashMap<String, Integer>();
+ for (Map.Entry<String, String> entry : newMap.entrySet()) {
+ objVal = entry.getValue();
+ key = entry.getKey();
+ if (objVal != null) {
+ intMap.put(key, Integer.valueOf(objVal.toString()));
+ } else {
+ intMap.put(key, null);
+ }
+
+ }
+ value = gson.toJson(intMap);
+ break;
+ case FLOAT:
+ value = "{";
+ for (Map.Entry<String, String> entry : newMap.entrySet()) {
+ objVal = entry.getValue();
+ if (objVal == null) {
+ objVal = "null";
+ }
+ key = entry.getKey();
+ value += "\"" + key + "\":" + objVal.toString() + ",";
+ }
+ value = value.substring(0, value.length() - 1);
+ value += "}";
+ break;
+ case BOOLEAN:
+ Map<String, Boolean> boolMap = new HashMap<String, Boolean>();
+ for (Map.Entry<String, String> entry : newMap.entrySet()) {
+ objVal = entry.getValue();
+ key = entry.getKey();
+ if (objVal != null) {
+ boolMap.put(key, Boolean.valueOf(objVal.toString()));
+ } else {
+ boolMap.put(key, null);
+ }
+ }
+ value = gson.toJson(boolMap);
+ break;
+ default:
+ value = gson.toJson(newMap);
+ log.debug("inner Tosca Type unknown : {}", innerToscaType);
+ }
+ } catch (JsonParseException e) {
+ log.debug("Failed to parse json : {}. {}", value, e);
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("Map Converter");
+ return Either.right(false);
+ }
+
+ return Either.left(value);
+
+ }
+
+ /**
+ * convert the json value of map when the inner type is a complex data type
+ *
+ * @param value
+ * @param innerType
+ * @param keyConverter
+ * @param allDataTypes
+ * @return
+ */
+ private Either<String, Boolean> convertComplexInnerType(String value, String innerType,
+ PropertyValueConverter keyConverter, Map<String, DataTypeDefinition> allDataTypes) {
+
+ DataTypeDefinition dataTypeDefinition = allDataTypes.get(innerType);
+ if (dataTypeDefinition == null) {
+ log.debug("Cannot find data type {}", innerType);
+ return Either.right(false);
+ }
+
+ Map<String, JsonElement> newMap = new HashMap<String, JsonElement>();
+
+ try {
+
+ JsonElement jsonObject = jsonParser.parse(value);
+ JsonObject asJsonObject = jsonObject.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String currentKey = keyConverter.convert(entry.getKey(), null, allDataTypes);
+
+ JsonElement currentValue = entry.getValue();
+
+ if (currentValue != null) {
+
+ String element = JsonUtils.toString(currentValue);
+
+ ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter
+ .validateAndUpdate(element, dataTypeDefinition, allDataTypes);
+ if (validateAndUpdate.right.booleanValue() == false) {
+ log.debug("Cannot parse value {} from type {} of key {}", currentValue, innerType, currentKey);
+ return Either.right(false);
+ }
+ JsonElement newValue = validateAndUpdate.left;
+ newMap.put(currentKey, newValue);
+ } else {
+ newMap.put(currentKey, null);
+ }
+ }
+
+ } catch (Exception e) {
+ log.debug("Cannot parse value {} of map from inner type {}", value, innerType);
+ return Either.right(false);
+ }
+
+ value = gson.toJson(newMap);
+ return Either.left(value);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/PropertyValueConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/PropertyValueConverter.java
new file mode 100644
index 0000000000..254785fe8a
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/PropertyValueConverter.java
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public interface PropertyValueConverter {
+
+ String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/StringConvertor.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/StringConvertor.java
new file mode 100644
index 0000000000..f5a7ff632e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/StringConvertor.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class StringConvertor implements PropertyValueConverter {
+
+ private static StringConvertor stringConverter = new StringConvertor();
+
+ public static StringConvertor getInstance() {
+ return stringConverter;
+ }
+
+ private StringConvertor() {
+
+ }
+
+ @Override
+ public String convert(String original, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ if (original == null) {
+ return null;
+ }
+ String coverted = ValidationUtils.removeNoneUtf8Chars(original);
+
+ // coverted = ValidationUtils.convertHtmlTagsToEntities(coverted);
+ coverted = ValidationUtils.normaliseWhitespace(coverted);
+ coverted = ValidationUtils.stripOctets(coverted);
+ coverted = ValidationUtils.removeHtmlTagsOnly(coverted);
+
+ return coverted;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaBooleanConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaBooleanConverter.java
new file mode 100644
index 0000000000..977415b909
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaBooleanConverter.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class ToscaBooleanConverter implements PropertyValueConverter {
+
+ private static ToscaBooleanConverter booleanConverter = new ToscaBooleanConverter();
+
+ public static ToscaBooleanConverter getInstance() {
+ return booleanConverter;
+ }
+
+ private ToscaBooleanConverter() {
+
+ }
+
+ @Override
+ public String convert(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (value == null) {
+ return null;
+ }
+
+ if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("on") || value.equalsIgnoreCase("yes")
+ || value.equalsIgnoreCase("y")) {
+ return "true";
+ } else {
+ return "false";
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaFloatConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaFloatConverter.java
new file mode 100644
index 0000000000..5d7f98e438
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaFloatConverter.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class ToscaFloatConverter implements PropertyValueConverter {
+
+ private static ToscaFloatConverter numberConverter = new ToscaFloatConverter();
+
+ public static ToscaFloatConverter getInstance() {
+ return numberConverter;
+ }
+
+ private ToscaFloatConverter() {
+
+ }
+
+ @Override
+ public String convert(String original, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ if (original == null) {
+ return null;
+ }
+ if (original.contains("f") || original.contains("F"))
+ original = original.toLowerCase().replaceFirst("f", "");
+ return new BigDecimal(original).toPlainString();
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaJsonValueConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaJsonValueConverter.java
new file mode 100644
index 0000000000..d70088e044
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaJsonValueConverter.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.io.StringReader;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.stream.JsonReader;
+
+public class ToscaJsonValueConverter extends ToscaValueBaseConverter implements ToscaValueConverter {
+ private static ToscaJsonValueConverter toscaJsonConverter = new ToscaJsonValueConverter();
+
+ public static ToscaJsonValueConverter getInstance() {
+ return toscaJsonConverter;
+ }
+
+ private ToscaJsonValueConverter() {
+
+ }
+
+ JsonParser jsonParser = new JsonParser();
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ StringReader reader = new StringReader(value);
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+ JsonElement jsonElement = jsonParser.parse(jsonReader);
+ if (jsonElement.isJsonPrimitive()) {
+ return value;
+ }
+ return handleComplexJsonValue(jsonElement);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaListValueConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaListValueConverter.java
new file mode 100644
index 0000000000..043446e783
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaListValueConverter.java
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+
+public class ToscaListValueConverter extends ToscaValueBaseConverter implements ToscaValueConverter {
+ private static ToscaListValueConverter listConverter = new ToscaListValueConverter();
+ private JsonParser jsonParser = new JsonParser();
+ private static Logger log = LoggerFactory.getLogger(ToscaListValueConverter.class.getName());
+
+ public static ToscaListValueConverter getInstance() {
+ return listConverter;
+ }
+
+ private ToscaListValueConverter() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ if (value == null) {
+ return null;
+ }
+ try {
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+ ToscaValueConverter innerConverter = null;
+ boolean isScalar = true;
+ if (innerToscaType != null) {
+ innerConverter = innerToscaType.getValueConverter();
+ } else {
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
+
+ if (dataTypeDefinition != null) {
+ ToscaPropertyType toscaPropertyType = null;
+ if ((toscaPropertyType = isScalarType(dataTypeDefinition)) != null) {
+ innerConverter = toscaPropertyType.getValueConverter();
+ } else {
+ isScalar = false;
+ innerConverter = ToscaMapValueConverter.getInstance();
+ }
+ } else {
+ log.debug("inner Tosca Type is null");
+ return value;
+ }
+ }
+ JsonElement jsonElement = null;
+ try {
+ StringReader reader = new StringReader(value);
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+
+ jsonElement = jsonParser.parse(jsonReader);
+ } catch (JsonSyntaxException e) {
+ log.debug("convertToToscaValue failed to parse json value :", e);
+ return null;
+ }
+ if (jsonElement == null || true == jsonElement.isJsonNull()) {
+ log.debug("convertToToscaValue json element is null");
+ return null;
+ }
+ if (jsonElement.isJsonArray() == false) {
+ // get_input all array like get_input: qrouter_names
+ return handleComplexJsonValue(jsonElement);
+ }
+ JsonArray asJsonArray = jsonElement.getAsJsonArray();
+
+ ArrayList<Object> toscaList = new ArrayList<Object>();
+ final boolean isScalarF = isScalar;
+ final ToscaValueConverter innerConverterFinal = innerConverter;
+ asJsonArray.forEach(e -> {
+ Object convertedValue = null;
+ if (isScalarF) {
+ log.debug("try to convert scalar value {}", e.getAsString());
+ if (e.getAsString() == null) {
+ convertedValue = null;
+ } else {
+ JsonElement singleElement = jsonParser.parse(e.getAsString());
+ if (singleElement.isJsonPrimitive()) {
+ convertedValue = innerConverterFinal.convertToToscaValue(e.getAsString(), innerType,
+ dataTypes);
+ } else {
+ convertedValue = handleComplexJsonValue(singleElement);
+ }
+ }
+ } else {
+ JsonObject asJsonObject = e.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
+ Map<String, PropertyDefinition> allProperties = getAllProperties(dataTypeDefinition);
+ Map<String, Object> toscaObjectPresentation = new HashMap<>();
+ // log.debug("try to convert datatype value {}",
+ // e.getAsString());
+
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String propName = entry.getKey();
+
+ JsonElement elementValue = entry.getValue();
+ PropertyDefinition propertyDefinition = allProperties.get(propName);
+ if (propertyDefinition == null) {
+ log.debug("The property {} was not found under data type {}", propName, dataTypeDefinition.getName());
+ continue;
+ // return null;
+ }
+ String type = propertyDefinition.getType();
+ ToscaPropertyType propertyType = ToscaPropertyType.isValidType(type);
+ Object convValue;
+ if (propertyType != null) {
+ if (elementValue.isJsonPrimitive()) {
+ ToscaValueConverter valueConverter = propertyType.getValueConverter();
+ convValue = valueConverter.convertToToscaValue(elementValue.getAsString(), type,
+ dataTypes);
+ } else {
+ if (ToscaPropertyType.MAP.equals(type) || ToscaPropertyType.LIST.equals(propertyType)) {
+ ToscaValueConverter valueConverter = propertyType.getValueConverter();
+ String json = gson.toJson(elementValue);
+ String innerTypeRecursive = propertyDefinition.getSchema().getProperty().getType();
+ convValue = valueConverter.convertToToscaValue(json, innerTypeRecursive, dataTypes);
+ } else {
+ convValue = handleComplexJsonValue(elementValue);
+ }
+ }
+ } else {
+ String json = gson.toJson(elementValue);
+ convValue = convertToToscaValue(json, type, dataTypes);
+ }
+ toscaObjectPresentation.put(propName, convValue);
+ }
+ convertedValue = toscaObjectPresentation;
+ }
+ toscaList.add(convertedValue);
+ });
+ return toscaList;
+ } catch (
+
+ JsonParseException e) {
+ log.debug("Failed to parse json : {}. {}", value, e);
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("List Converter");
+ return null;
+ }
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaMapValueConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaMapValueConverter.java
new file mode 100644
index 0000000000..601d8f0fc8
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaMapValueConverter.java
@@ -0,0 +1,184 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+
+public class ToscaMapValueConverter extends ToscaValueBaseConverter implements ToscaValueConverter {
+ private static ToscaMapValueConverter mapConverter = new ToscaMapValueConverter();
+
+ private JsonParser jsonParser = new JsonParser();
+ private static Logger log = LoggerFactory.getLogger(ToscaMapValueConverter.class.getName());
+
+ public static ToscaMapValueConverter getInstance() {
+ return mapConverter;
+ }
+
+ private ToscaMapValueConverter() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ if (value == null) {
+ return value;
+ }
+ try {
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+ ToscaValueConverter innerConverter = null;
+ boolean isScalar = true;
+ if (innerToscaType != null) {
+ innerConverter = innerToscaType.getValueConverter();
+ } else {
+
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
+ if (dataTypeDefinition != null) {
+ ToscaPropertyType toscaPropertyType = null;
+ if ((toscaPropertyType = isScalarType(dataTypeDefinition)) != null) {
+ innerConverter = toscaPropertyType.getValueConverter();
+ } else {
+ isScalar = false;
+ }
+ } else {
+ // TODO handle getinput
+ log.debug("inner Tosca Type is null");
+ return value;
+ }
+
+ }
+ JsonElement jsonElement = null;
+ try {
+ StringReader reader = new StringReader(value);
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+
+ jsonElement = jsonParser.parse(jsonReader);
+
+ } catch (JsonSyntaxException e) {
+ log.debug("convertToToscaValue failed to parse json value :", e);
+ return null;
+ }
+ if (jsonElement == null || true == jsonElement.isJsonNull()) {
+ log.debug("convertToToscaValue json element is null");
+ return null;
+ }
+ JsonObject asJsonObject = jsonElement.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+
+ Map<String, Object> toscaMap = new HashMap<>();
+ final boolean isScalarF = isScalar;
+ final ToscaValueConverter innerConverterFinal = innerConverter;
+ entrySet.forEach(e -> {
+ log.debug("try convert element {}", e.getValue());
+ Object convertedValue = convertDataTypeToToscaMap(innerType, dataTypes, innerConverterFinal, isScalarF,
+ e.getValue());
+ toscaMap.put(e.getKey(), convertedValue);
+ });
+ return toscaMap;
+ } catch (JsonParseException e) {
+ log.debug("Failed to parse json : {}. {}", value, e);
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("List Converter");
+ return null;
+ }
+ }
+
+ public Object convertDataTypeToToscaMap(String innerType, Map<String, DataTypeDefinition> dataTypes,
+ ToscaValueConverter innerConverter, final boolean isScalarF, JsonElement entryValue) {
+ Object convertedValue = null;
+ if (isScalarF && entryValue.isJsonPrimitive()) {
+ log.debug("try convert scalar value {}", entryValue.getAsString());
+ if (entryValue.getAsString() == null) {
+ convertedValue = null;
+ } else {
+ convertedValue = innerConverter.convertToToscaValue(entryValue.getAsString(), innerType, dataTypes);
+ }
+ } else {
+ JsonObject asJsonObjectIn = entryValue.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySetIn = asJsonObjectIn.entrySet();
+
+ DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
+ Map<String, PropertyDefinition> allProperties = getAllProperties(dataTypeDefinition);
+ Map<String, Object> toscaObjectPresentation = new HashMap<>();
+
+ for (Entry<String, JsonElement> entry : entrySetIn) {
+ String propName = entry.getKey();
+
+ JsonElement elementValue = entry.getValue();
+ Object convValue;
+ if (isScalarF == false) {
+ PropertyDefinition propertyDefinition = allProperties.get(propName);
+ if (propertyDefinition == null && isScalarF) {
+ log.debug("The property {} was not found under data type {}", propName, dataTypeDefinition.getName());
+ continue;
+ }
+
+ String type = propertyDefinition.getType();
+ ToscaPropertyType propertyType = ToscaPropertyType.isValidType(type);
+ if (propertyType != null) {
+ if (elementValue.isJsonPrimitive()) {
+ ToscaValueConverter valueConverter = propertyType.getValueConverter();
+ convValue = valueConverter.convertToToscaValue(elementValue.getAsString(), type, dataTypes);
+ } else {
+ if (ToscaPropertyType.MAP.equals(type) || ToscaPropertyType.LIST.equals(propertyType)) {
+ ToscaValueConverter valueConverter = propertyType.getValueConverter();
+ String json = gson.toJson(elementValue);
+ String innerTypeRecursive = propertyDefinition.getSchema().getProperty().getType();
+ convValue = valueConverter.convertToToscaValue(json, innerTypeRecursive, dataTypes);
+ } else {
+ convValue = handleComplexJsonValue(elementValue);
+ }
+ }
+ } else {
+ convValue = convertToToscaValue(elementValue.getAsString(), type, dataTypes);
+ }
+ } else {
+ if (elementValue.isJsonPrimitive()) {
+ convValue = json2JavaPrimitive(elementValue.getAsJsonPrimitive());
+ } else {
+ convValue = handleComplexJsonValue(elementValue);
+ }
+ }
+ toscaObjectPresentation.put(propName, convValue);
+ }
+ convertedValue = toscaObjectPresentation;
+ }
+ return convertedValue;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaStringConvertor.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaStringConvertor.java
new file mode 100644
index 0000000000..e228d256c2
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaStringConvertor.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class ToscaStringConvertor implements ToscaValueConverter {
+ private static ToscaStringConvertor stringConverter = new ToscaStringConvertor();
+
+ public static ToscaStringConvertor getInstance() {
+ return stringConverter;
+ }
+
+ private ToscaStringConvertor() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return value;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueBaseConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueBaseConverter.java
new file mode 100644
index 0000000000..e886327481
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueBaseConverter.java
@@ -0,0 +1,153 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+public class ToscaValueBaseConverter {
+ protected Gson gson = new Gson();
+ private static Logger log = LoggerFactory.getLogger(ToscaValueBaseConverter.class.getName());
+
+ protected Map<String, PropertyDefinition> getAllProperties(DataTypeDefinition dataTypeDefinition) {
+
+ Map<String, PropertyDefinition> allParentsProps = new HashMap<>();
+
+ while (dataTypeDefinition != null) {
+
+ List<PropertyDefinition> currentParentsProps = dataTypeDefinition.getProperties();
+ if (currentParentsProps != null) {
+ currentParentsProps.stream().forEach(p -> allParentsProps.put(p.getName(), p));
+ }
+
+ dataTypeDefinition = dataTypeDefinition.getDerivedFrom();
+ }
+
+ return allParentsProps;
+ }
+
+ public ToscaPropertyType isScalarType(DataTypeDefinition dataTypeDef) {
+
+ ToscaPropertyType result = null;
+
+ DataTypeDefinition dataType = dataTypeDef;
+
+ while (dataType != null) {
+
+ String name = dataType.getName();
+ ToscaPropertyType typeIfScalar = ToscaPropertyType.getTypeIfScalar(name);
+ if (typeIfScalar != null) {
+ result = typeIfScalar;
+ break;
+ }
+
+ dataType = dataType.getDerivedFrom();
+ }
+
+ return result;
+ }
+
+ public Object handleComplexJsonValue(JsonElement elementValue) {
+ Object jsonValue = null;
+
+ Map<String, Object> value = new HashMap<String, Object>();
+ if ( elementValue.isJsonObject() ){
+ JsonObject jsonOb = elementValue.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = jsonOb.entrySet();
+ Iterator<Entry<String, JsonElement>> iteratorEntry = entrySet.iterator();
+ while (iteratorEntry.hasNext()) {
+ Entry<String, JsonElement> entry = iteratorEntry.next();
+ if (entry.getValue().isJsonArray()) {
+ List<Object> array = handleJsonArray(entry.getValue());
+ value.put(entry.getKey(), array);
+ } else {
+ Object object;
+ if (entry.getValue().isJsonPrimitive()) {
+ object = json2JavaPrimitive(entry.getValue().getAsJsonPrimitive());
+ } else {
+ object = handleComplexJsonValue(entry.getValue());
+ }
+ value.put(entry.getKey(), object);
+ }
+ }
+ jsonValue = value;
+ }else{
+ if ( elementValue.isJsonArray() ){
+ jsonValue = handleJsonArray(elementValue);
+ }else{
+ log.debug("not supported json type {} ",elementValue);
+ }
+ }
+
+ return jsonValue;
+ }
+
+ private List<Object> handleJsonArray(JsonElement entry) {
+ List<Object> array = new ArrayList<>();
+ JsonArray jsonArray = entry.getAsJsonArray();
+ Iterator<JsonElement> iterator = jsonArray.iterator();
+ while (iterator.hasNext()) {
+ Object object;
+ JsonElement element = iterator.next();
+ if (element.isJsonPrimitive()) {
+ object = json2JavaPrimitive(element.getAsJsonPrimitive());
+ } else {
+ object = handleComplexJsonValue(element);
+ }
+ array.add(object);
+ }
+ return array;
+ }
+
+ public Object json2JavaPrimitive(JsonPrimitive prim) {
+ if (prim.isBoolean()) {
+ return prim.getAsBoolean();
+ } else if (prim.isString()) {
+ return prim.getAsString();
+ } else if (prim.isNumber()) {
+ String strRepesentation = prim.getAsString();
+ if (strRepesentation.contains(".")) {
+ return prim.getAsDouble();
+ } else {
+ return prim.getAsInt();
+ }
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueConverter.java
new file mode 100644
index 0000000000..1b5d4697be
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueConverter.java
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public interface ToscaValueConverter {
+ Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes);
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueDefaultConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueDefaultConverter.java
new file mode 100644
index 0000000000..b6eb24276e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/converters/ToscaValueDefaultConverter.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.converters;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class ToscaValueDefaultConverter implements ToscaValueConverter {
+ private static ToscaValueDefaultConverter deafultConverter = new ToscaValueDefaultConverter();
+
+ public static ToscaValueDefaultConverter getInstance() {
+ return deafultConverter;
+ }
+
+ private ToscaValueDefaultConverter() {
+
+ }
+
+ @Override
+ public Object convertToToscaValue(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+ return value;
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/BooleanValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/BooleanValidator.java
new file mode 100644
index 0000000000..def0e7c391
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/BooleanValidator.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class BooleanValidator implements PropertyTypeValidator {
+
+ private static BooleanValidator booleanValidator = new BooleanValidator();
+ private static String[] validValues = { "true", "t", "on", "yes", "y", "1", "false", "f", "off", "no", "n", "0" };
+
+ public static BooleanValidator getInstance() {
+ return booleanValidator;
+ }
+
+ private BooleanValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ return (Arrays.stream(validValues).filter(str -> str.equalsIgnoreCase(value)).toArray().length == 1);
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, null, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/DataTypeValidatorConverter.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/DataTypeValidatorConverter.java
new file mode 100644
index 0000000000..d376a1ec13
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/DataTypeValidatorConverter.java
@@ -0,0 +1,499 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+
+public class DataTypeValidatorConverter {
+
+ private static DataTypeValidatorConverter dataTypeValidatorConverter = new DataTypeValidatorConverter();
+
+ public static DataTypeValidatorConverter getInstance() {
+ return dataTypeValidatorConverter;
+ }
+
+ private DataTypeValidatorConverter() {
+
+ }
+
+ private static Logger log = LoggerFactory.getLogger(DataTypeValidatorConverter.class.getName());
+
+ JsonParser jsonParser = new JsonParser();
+
+ Gson gson = new Gson();
+
+ ImmutablePair<JsonElement, Boolean> falseResult = new ImmutablePair<JsonElement, Boolean>(null, false);
+ ImmutablePair<JsonElement, Boolean> trueEmptyResult = new ImmutablePair<JsonElement, Boolean>(null, true);
+
+ ImmutablePair<String, Boolean> trueStringEmptyResult = new ImmutablePair<String, Boolean>(null, true);
+ ImmutablePair<String, Boolean> falseStringEmptyResult = new ImmutablePair<String, Boolean>(null, true);
+
+ private ToscaPropertyType isDataTypeDerviedFromScalarType(DataTypeDefinition dataTypeDef) {
+
+ ToscaPropertyType result = null;
+
+ DataTypeDefinition dataType = dataTypeDef;
+
+ while (dataType != null) {
+
+ String name = dataType.getName();
+ ToscaPropertyType typeIfScalar = ToscaPropertyType.getTypeIfScalar(name);
+ if (typeIfScalar != null) {
+ result = typeIfScalar;
+ break;
+ }
+
+ dataType = dataType.getDerivedFrom();
+ }
+
+ return result;
+ }
+
+ private ImmutablePair<JsonElement, Boolean> validateAndUpdate(JsonElement jsonElement,
+ DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> allDataTypes) {
+
+ Map<String, PropertyDefinition> allProperties = getAllProperties(dataTypeDefinition);
+
+ ToscaPropertyType toscaPropertyType = null;
+ if ((toscaPropertyType = isDataTypeDerviedFromScalarType(dataTypeDefinition)) != null) {
+
+ PropertyTypeValidator validator = toscaPropertyType.getValidator();
+ PropertyValueConverter converter = toscaPropertyType.getConverter();
+ if (jsonElement == null || true == jsonElement.isJsonNull()) {
+ boolean valid = validator.isValid(null, null, allDataTypes);
+ if (false == valid) {
+ log.trace("Failed in validation of property {} from type {}", dataTypeDefinition.getName(), dataTypeDefinition.getName());
+ return falseResult;
+ }
+ return new ImmutablePair<JsonElement, Boolean>(jsonElement, true);
+
+ } else {
+ if (true == jsonElement.isJsonPrimitive()) {
+ String value = null;
+ if (jsonElement != null) {
+ if (jsonElement.toString().isEmpty()) {
+ value = "";
+ } else {
+ value = jsonElement.toString();
+ }
+ }
+ boolean valid = validator.isValid(value, null, null);
+ if (false == valid) {
+ log.trace("Failed in validation of property {} from type {}. Json primitive value is {}", dataTypeDefinition.getName(), dataTypeDefinition.getName(), value);
+ return falseResult;
+ }
+
+ String convertedValue = converter.convert(value, null, allDataTypes);
+ JsonElement element = null;
+ try {
+ element = jsonParser.parse(convertedValue);
+ } catch (JsonSyntaxException e) {
+ log.debug("Failed to parse value {} of property {}. {}", convertedValue, dataTypeDefinition.getName(), e);
+ return falseResult;
+ }
+
+ return new ImmutablePair<JsonElement, Boolean>(element, true);
+
+ } else {
+ // MAP, LIST, OTHER types cannot be applied data type
+ // definition scalar type. We currently cannot derived from
+ // map/list. (cannot add the entry schema to it)
+ log.debug("We cannot derive from list/map. Thus, the value cannot be not primitive since the data type {} is scalar one", dataTypeDefinition.getName());
+
+ return falseResult;
+ }
+ }
+ } else {
+
+ if (jsonElement == null || jsonElement.isJsonNull()) {
+
+ return new ImmutablePair<JsonElement, Boolean>(jsonElement, true);
+
+ } else {
+
+ if (jsonElement.isJsonObject()) {
+
+ JsonObject buildJsonObject = new JsonObject();
+
+ JsonObject asJsonObject = jsonElement.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String propName = entry.getKey();
+
+ JsonElement elementValue = entry.getValue();
+
+ PropertyDefinition propertyDefinition = allProperties.get(propName);
+ if (propertyDefinition == null) {
+ log.debug("The property {} was not found under data type {}", propName, dataTypeDefinition.getName());
+ return falseResult;
+ }
+ String type = propertyDefinition.getType();
+ boolean isScalarType = ToscaPropertyType.isScalarType(type);
+
+ if (true == isScalarType) {
+ ToscaPropertyType propertyType = ToscaPropertyType.isValidType(type);
+ if (propertyType == null) {
+ log.debug("cannot find the {} under default tosca property types", type);
+ return falseResult;
+ }
+ PropertyTypeValidator validator = propertyType.getValidator();
+ String innerType = null;
+ if (propertyType == ToscaPropertyType.LIST || propertyType == ToscaPropertyType.MAP) {
+ if (propertyDefinition.getSchema() != null
+ && propertyDefinition.getSchema().getProperty() != null) {
+ innerType = propertyDefinition.getSchema().getProperty().getType();
+ if (innerType == null) {
+ log.debug("Property type {} must have inner type in its declaration.", propertyType);
+ return falseResult;
+ }
+ }
+ }
+
+ String value = null;
+ if (elementValue != null) {
+ if (elementValue.isJsonPrimitive() && elementValue.getAsString().isEmpty()) {
+ value = "";
+ } else {
+ value = elementValue.toString();
+ }
+ }
+
+ boolean isValid = validator.isValid(value, innerType, allDataTypes);
+ if (false == isValid) {
+ log.debug("Failed to validate the value {} from type {}", value, propertyType);
+ return falseResult;
+ }
+
+ PropertyValueConverter converter = propertyType.getConverter();
+ String convertedValue = converter.convert(value, innerType, allDataTypes);
+
+ JsonElement element = null;
+ if (convertedValue != null) {
+ if (convertedValue.isEmpty()) {
+ element = new JsonPrimitive("");
+ } else {
+ try {
+ element = jsonParser.parse(convertedValue);
+ } catch (JsonSyntaxException e) {
+ log.debug("Failed to parse value {} of type {}. {}", convertedValue, propertyType, e);
+ return falseResult;
+ }
+ }
+ }
+ buildJsonObject.add(propName, element);
+
+ } else {
+
+ DataTypeDefinition typeDefinition = allDataTypes.get(type);
+ if (typeDefinition == null) {
+ log.debug("The data type {] cannot be found in the given data type list.", type);
+ return falseResult;
+ }
+
+ ImmutablePair<JsonElement, Boolean> isValid = validateAndUpdate(elementValue,
+ typeDefinition, allDataTypes);
+
+ if (false == isValid.getRight().booleanValue()) {
+ log.debug("Failed in validation of value {} from type {}", (elementValue != null ? elementValue.toString() : null), typeDefinition.getName());
+ return falseResult;
+ }
+
+ buildJsonObject.add(propName, isValid.getLeft());
+ }
+
+ }
+
+ return new ImmutablePair<JsonElement, Boolean>(buildJsonObject, true);
+ } else {
+ log.debug("The value {} of type {} should be json object", (jsonElement != null ? jsonElement.toString() : null), dataTypeDefinition.getName());
+ return falseResult;
+ }
+
+ }
+ }
+
+ }
+
+ public ImmutablePair<JsonElement, Boolean> validateAndUpdate(String value, DataTypeDefinition dataTypeDefinition,
+ Map<String, DataTypeDefinition> allDataTypes) {
+
+ ImmutablePair<JsonElement, Boolean> result = falseResult;
+
+ if (value == null || value.isEmpty()) {
+ return trueEmptyResult;
+ }
+
+ JsonElement jsonElement = null;
+ try {
+ jsonElement = jsonParser.parse(value);
+ } catch (JsonSyntaxException e) {
+ return falseResult;
+ }
+
+ result = validateAndUpdate(jsonElement, dataTypeDefinition, allDataTypes);
+
+ return result;
+ }
+
+ private Map<String, PropertyDefinition> getAllProperties(DataTypeDefinition dataTypeDefinition) {
+
+ Map<String, PropertyDefinition> allParentsProps = new HashMap<String, PropertyDefinition>();
+
+ while (dataTypeDefinition != null) {
+
+ List<PropertyDefinition> currentParentsProps = dataTypeDefinition.getProperties();
+ if (currentParentsProps != null) {
+ currentParentsProps.stream().forEach(p -> allParentsProps.put(p.getName(), p));
+ }
+
+ dataTypeDefinition = dataTypeDefinition.getDerivedFrom();
+ }
+
+ return allParentsProps;
+ }
+
+ private String getValueFromJsonElement(JsonElement jsonElement) {
+ String value = null;
+
+ if (jsonElement == null || jsonElement.isJsonNull()) {
+ value = PropertyOperation.EMPTY_VALUE;
+ } else {
+ if (jsonElement.toString().isEmpty()) {
+ value = "";
+ } else {
+ value = jsonElement.toString();
+ }
+ }
+
+ return value;
+ }
+
+ public boolean isValid(String value, DataTypeDefinition dataTypeDefinition,
+ Map<String, DataTypeDefinition> allDataTypes) {
+
+ boolean result = false;
+
+ if (value == null || value.isEmpty()) {
+ return true;
+ }
+
+ JsonElement jsonElement = null;
+ try {
+ jsonElement = jsonParser.parse(value);
+ } catch (JsonSyntaxException e) {
+ log.debug("Failed to parse the value {} from type {}. {}", value, dataTypeDefinition, e);
+ return false;
+ }
+
+ result = isValid(jsonElement, dataTypeDefinition, allDataTypes);
+
+ return result;
+ }
+
+ private boolean isValid(JsonElement jsonElement, DataTypeDefinition dataTypeDefinition,
+ Map<String, DataTypeDefinition> allDataTypes) {
+
+ Map<String, PropertyDefinition> allProperties = getAllProperties(dataTypeDefinition);
+
+ ToscaPropertyType toscaPropertyType = null;
+ if ((toscaPropertyType = isDataTypeDerviedFromScalarType(dataTypeDefinition)) != null) {
+
+ PropertyTypeValidator validator = toscaPropertyType.getValidator();
+ if (jsonElement == null || true == jsonElement.isJsonNull()) {
+ boolean valid = validator.isValid(null, null, allDataTypes);
+ if (false == valid) {
+ log.trace("Failed in validation of property " + dataTypeDefinition.getName() + " from type "
+ + dataTypeDefinition.getName());
+ return false;
+ }
+
+ return true;
+
+ } else {
+ if (true == jsonElement.isJsonPrimitive()) {
+ String value = null;
+ if (jsonElement != null) {
+ if (jsonElement.toString().isEmpty()) {
+ value = "";
+ } else {
+ value = jsonElement.toString();
+ }
+ }
+ boolean valid = validator.isValid(value, null, allDataTypes);
+ if (false == valid) {
+ log.trace("Failed in validation of property {} from type {}. Json primitive value is {}", dataTypeDefinition.getName(), dataTypeDefinition.getName(), value);
+ return false;
+ }
+
+ return true;
+
+ } else {
+ // MAP, LIST, OTHER types cannot be applied data type
+ // definition scalar type. We currently cannot derived from
+ // map/list. (cannot add the entry schema to it)
+ log.debug("We cannot derive from list/map. Thus, the value cannot be not primitive since the data type {} is scalar one", dataTypeDefinition.getName());
+
+ return false;
+ }
+ }
+ } else {
+
+ if (jsonElement == null || jsonElement.isJsonNull()) {
+
+ return true;
+
+ } else {
+
+ if (jsonElement.isJsonObject()) {
+
+ JsonObject asJsonObject = jsonElement.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String propName = entry.getKey();
+
+ JsonElement elementValue = entry.getValue();
+
+ PropertyDefinition propertyDefinition = allProperties.get(propName);
+ if (propertyDefinition == null) {
+ log.debug("The property {} was not found under data tpye {}", propName, dataTypeDefinition.getName());
+ return false;
+ }
+ String type = propertyDefinition.getType();
+ boolean isScalarType = ToscaPropertyType.isScalarType(type);
+
+ if (true == isScalarType) {
+ ToscaPropertyType propertyType = ToscaPropertyType.isValidType(type);
+ if (propertyType == null) {
+ log.debug("cannot find the {} under default tosca property types", type);
+ return false;
+ }
+ PropertyTypeValidator validator = propertyType.getValidator();
+ String innerType = null;
+ if (propertyType == ToscaPropertyType.LIST || propertyType == ToscaPropertyType.MAP) {
+ if (propertyDefinition.getSchema() != null
+ && propertyDefinition.getSchema().getProperty() != null) {
+ innerType = propertyDefinition.getSchema().getProperty().getType();
+ if (innerType == null) {
+ log.debug("Property type {} must have inner type in its decleration.", propertyType);
+ return false;
+ }
+ }
+ }
+
+ String value = null;
+ if (elementValue != null) {
+ if (elementValue.isJsonPrimitive() && elementValue.getAsString().isEmpty()) {
+ value = "";
+ } else {
+ value = elementValue.toString();
+ }
+ }
+
+ boolean isValid = validator.isValid(value, innerType, allDataTypes);
+ if (false == isValid) {
+ log.debug("Failed to validate the value {} from type {}", value, propertyType);
+ return false;
+ }
+
+ } else {
+
+ DataTypeDefinition typeDefinition = allDataTypes.get(type);
+ if (typeDefinition == null) {
+ log.debug("The data type {} canot be found in the given data type list.", type);
+ return false;
+ }
+
+ boolean isValid = isValid(elementValue, typeDefinition, allDataTypes);
+
+ if (false == isValid) {
+ log.debug("Failed in validation of value {} from type {}", (elementValue != null ? elementValue.toString() : null), typeDefinition.getName());
+ return false;
+ }
+
+ }
+
+ }
+
+ return true;
+ } else {
+ log.debug("The value {} of type {} should be json object", (jsonElement != null ? jsonElement.toString() : null), dataTypeDefinition.getName());
+ return false;
+ }
+
+ }
+ }
+
+ }
+
+ // public ImmutablePair<String, Boolean>
+ // validateAndUpdateAndReturnString(String value, DataTypeDefinition
+ // dataTypeDefinition, Map<String, DataTypeDefinition> allDataTypes) {
+ //
+ // ImmutablePair<JsonElement, Boolean> result = falseResult;
+ //
+ // if (value == null || value.isEmpty()) {
+ // return trueStringEmptyResult;
+ // }
+ //
+ // JsonElement jsonElement = null;
+ // try {
+ // jsonElement = jsonParser.parse(value);
+ // } catch (JsonSyntaxException e) {
+ // return falseStringEmptyResult;
+ // }
+ //
+ // result = validateAndUpdate(jsonElement, dataTypeDefinition,
+ // allDataTypes);
+ //
+ // if (result.right.booleanValue() == false) {
+ // log.debug("The value {} of property from type {} is invalid", value, dataTypeDefinition.getName());
+ // return new ImmutablePair<String, Boolean>(value, false);
+ // }
+ //
+ // String valueFromJsonElement = getValueFromJsonElement(result.left);
+ //
+ // return new ImmutablePair<String, Boolean>(valueFromJsonElement, true);
+ // }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/FloatValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/FloatValidator.java
new file mode 100644
index 0000000000..2518eaa51e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/FloatValidator.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class FloatValidator implements PropertyTypeValidator {
+
+ private static FloatValidator FloatValidator = new FloatValidator();
+
+ public static FloatValidator getInstance() {
+ return FloatValidator;
+ }
+
+ private FloatValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ try {
+ Float.parseFloat(value);
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatBooleanValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatBooleanValidator.java
new file mode 100644
index 0000000000..ec4051e65c
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatBooleanValidator.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class HeatBooleanValidator implements PropertyTypeValidator {
+
+ private static HeatBooleanValidator booleanValidator = new HeatBooleanValidator();
+
+ public static HeatBooleanValidator getInstance() {
+ return booleanValidator;
+ }
+
+ private HeatBooleanValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false") || value.equalsIgnoreCase("t")
+ || value.equalsIgnoreCase("f") || value.equalsIgnoreCase("on") || value.equalsIgnoreCase("off")
+ || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("no") || value.equalsIgnoreCase("y")
+ || value.equalsIgnoreCase("n") || value.equalsIgnoreCase("1") || value.equalsIgnoreCase("0")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatCommaDelimitedListValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatCommaDelimitedListValidator.java
new file mode 100644
index 0000000000..464dbf0975
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatCommaDelimitedListValidator.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class HeatCommaDelimitedListValidator implements PropertyTypeValidator {
+
+ private static HeatCommaDelimitedListValidator stringValidator = new HeatCommaDelimitedListValidator();
+
+ public static HeatCommaDelimitedListValidator getInstance() {
+ return stringValidator;
+ }
+
+ private HeatCommaDelimitedListValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ String coverted = ValidationUtils.removeNoneUtf8Chars(value);
+ return ValidationUtils.validateIsEnglish(coverted);
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatNumberValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatNumberValidator.java
new file mode 100644
index 0000000000..37c4a46829
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatNumberValidator.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class HeatNumberValidator implements PropertyTypeValidator {
+
+ private static HeatNumberValidator numberValidator = new HeatNumberValidator();
+
+ private static FloatValidator floatValidator = FloatValidator.getInstance();
+ private static IntegerValidator integerValidator = IntegerValidator.getInstance();
+
+ public static HeatNumberValidator getInstance() {
+ return numberValidator;
+ }
+
+ private HeatNumberValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+ boolean valid = integerValidator.isValid(value, null, allDataTypes);
+
+ if (!valid) {
+ valid = floatValidator.isValid(value, null, allDataTypes);
+ }
+
+ return valid;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatStringValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatStringValidator.java
new file mode 100644
index 0000000000..0e7b7f6648
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/HeatStringValidator.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class HeatStringValidator implements PropertyTypeValidator {
+
+ private static HeatStringValidator stringValidator = new HeatStringValidator();
+
+ public static HeatStringValidator getInstance() {
+ return stringValidator;
+ }
+
+ private HeatStringValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ String coverted = ValidationUtils.removeNoneUtf8Chars(value);
+ return ValidationUtils.validateIsEnglish(coverted);
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidator.java
new file mode 100644
index 0000000000..61d321c45e
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidator.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class IntegerValidator implements PropertyTypeValidator {
+
+ private static IntegerValidator integerValidator = new IntegerValidator();
+
+ private IntegerValidator() {
+ }
+
+ public static IntegerValidator getInstance() {
+ return integerValidator;
+ }
+
+ private class PatternBase {
+ public PatternBase(Pattern pattern, Integer base) {
+ this.pattern = pattern;
+ this.base = base;
+ }
+
+ Pattern pattern;
+ Integer base;
+ }
+
+ private PatternBase base8Pattern = new PatternBase(Pattern.compile("([-+])?0o([0-7]+)"), 8);
+ private PatternBase base10Pattern = new PatternBase(Pattern.compile("([-+])?(0|[1-9][0-9]*)"), 10);
+ private PatternBase base16Pattern = new PatternBase(Pattern.compile("([-+])?0x([0-9a-fA-F]+)"), 16);
+
+ private PatternBase[] patterns = { base10Pattern, base8Pattern, base16Pattern };
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ for (PatternBase patternBase : patterns) {
+ Matcher matcher = patternBase.pattern.matcher(value);
+ Long parsed = null;
+ if (matcher.matches()) {
+ try {
+ parsed = Long.parseLong(matcher.group(2), patternBase.base);
+ if (matcher.group(1) != null && matcher.group(1).compareTo("-") == 0) {
+ parsed *= -1;
+ }
+ return (Integer.MIN_VALUE <= parsed && parsed <= (Integer.MAX_VALUE)) ? true : false;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/JsonValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/JsonValidator.java
new file mode 100644
index 0000000000..164fe62792
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/JsonValidator.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.io.StringReader;
+import java.util.Map;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.common.util.GsonFactory;
+import org.openecomp.sdc.common.util.JsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+
+public class JsonValidator implements PropertyTypeValidator {
+
+ private static JsonValidator jsonValidator = new JsonValidator();
+
+ private static Logger log = LoggerFactory.getLogger(JsonValidator.class.getName());
+
+ private static JsonParser jsonParser = new JsonParser();
+
+ public static JsonValidator getInstance() {
+ return jsonValidator;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || value.isEmpty()) {
+ return true;
+ }
+ try {
+ StringReader reader = new StringReader(value);
+ JsonReader jsonReader = new JsonReader(reader);
+ jsonReader.setLenient(true);
+ jsonParser.parse(jsonReader);
+ } catch (JsonSyntaxException e) {
+ log.debug("Error parsing JSON property", e);
+ return false;
+ }
+ return true;
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/KeyValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/KeyValidator.java
new file mode 100644
index 0000000000..4b11e26d05
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/KeyValidator.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+public class KeyValidator implements PropertyTypeValidator {
+
+ public static final int STRING_MAXIMUM_LENGTH = 100;
+
+ private static KeyValidator keyValidator = new KeyValidator();
+
+ public static KeyValidator getInstance() {
+ return keyValidator;
+ }
+
+ private KeyValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return false;
+ }
+
+ if (value.length() > STRING_MAXIMUM_LENGTH) {
+ return false;
+ }
+ String coverted = ValidationUtils.removeNoneUtf8Chars(value);
+ return ValidationUtils.validateIsEnglish(coverted);
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ListValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ListValidator.java
new file mode 100644
index 0000000000..92834690b8
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ListValidator.java
@@ -0,0 +1,160 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.common.util.JsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+public class ListValidator implements PropertyTypeValidator {
+
+ private static ListValidator listValidator = new ListValidator();
+
+ private static Logger log = LoggerFactory.getLogger(ListValidator.class.getName());
+ Gson gson = new Gson();
+
+ private static JsonParser jsonParser = new JsonParser();
+
+ private static DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
+
+ public static ListValidator getInstance() {
+ return listValidator;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ log.debug("Going to validate value {} with inner type {}", value, innerType);
+
+ if (value == null || value == "") {
+ return true;
+ }
+ if (innerType == null) {
+ return false;
+ }
+
+ PropertyTypeValidator innerValidator;
+
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+
+ if (innerToscaType != null) {
+ switch (innerToscaType) {
+ case STRING:
+ innerValidator = ToscaPropertyType.STRING.getValidator();
+ break;
+ case INTEGER:
+ innerValidator = ToscaPropertyType.INTEGER.getValidator();
+ break;
+ case FLOAT:
+ innerValidator = ToscaPropertyType.FLOAT.getValidator();
+ break;
+ case BOOLEAN:
+ innerValidator = ToscaPropertyType.BOOLEAN.getValidator();
+ break;
+ case JSON:
+ innerValidator = ToscaPropertyType.JSON.getValidator();
+ break;
+ default:
+ log.debug("inner Tosca Type is unknown: {}", innerToscaType);
+ return false;
+ }
+
+ } else {
+ log.debug("inner Tosca Type is: {}", innerType);
+
+ boolean isValid = validateComplexInnerType(value, innerType, allDataTypes);
+ log.debug("Finish to validate value {} of list with inner type {}. result is: {}", value, innerType, isValid);
+ return isValid;
+ }
+
+ try {
+ JsonArray jo = (JsonArray) jsonParser.parse(value);
+ int size = jo.size();
+ for (int i = 0; i < size; i++) {
+ JsonElement currentValue = jo.get(i);
+ String element = JsonUtils.toString(currentValue);
+ if (!innerValidator.isValid(element, null, allDataTypes)) {
+ log.debug("validation of element : {} failed", element);
+ return false;
+ }
+
+ }
+ return true;
+
+ } catch (JsonSyntaxException e) {
+ log.debug("Failed to parse json : {}. {}", value, e);
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("List Validator");
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+
+ private boolean validateComplexInnerType(String value, String innerType,
+ Map<String, DataTypeDefinition> allDataTypes) {
+
+ DataTypeDefinition innerDataTypeDefinition = allDataTypes.get(innerType);
+ if (innerDataTypeDefinition == null) {
+ log.debug("Data type {} cannot be found in our data types.", innerType);
+ return false;
+ }
+
+ try {
+
+ JsonArray jo = (JsonArray) jsonParser.parse(value);
+ int size = jo.size();
+ for (int i = 0; i < size; i++) {
+ JsonElement currentValue = jo.get(i);
+ if (currentValue != null) {
+ String element = JsonUtils.toString(currentValue);
+ boolean isValid = dataTypeValidatorConverter.isValid(element, innerDataTypeDefinition,
+ allDataTypes);
+ if (isValid == false) {
+ log.debug("Cannot parse value {} from type {} in list parameter", currentValue, innerType);
+ return false;
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ log.debug("Error when parsing JSON of object of type ", e);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/MapValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/MapValidator.java
new file mode 100644
index 0000000000..c8ffc3f4b8
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/MapValidator.java
@@ -0,0 +1,184 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.common.util.JsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSyntaxException;
+
+/*
+ * Property Type Map correct usage:
+ * null key null value = Yaml reader error
+valid key null value = key & value deleted
+duplicated keys = last key is taken
+mismatch between inner type and values type = returned mismatch in data type
+validators and converters works the same as before
+
+Types:
+when written line by line :
+ key1 : val1
+ key2 : val2
+key1 and val does not need " " , even if val1 is a string.
+
+when written as one line : {"key1":val1 , "key2":val2}
+Keys always need " " around them.
+ */
+public class MapValidator implements PropertyTypeValidator {
+
+ private static MapValidator mapValidator = new MapValidator();
+
+ private static Logger log = LoggerFactory.getLogger(MapValidator.class.getName());
+ Gson gson = new Gson();
+
+ private static DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
+
+ private static JsonParser jsonParser = new JsonParser();
+
+ public static MapValidator getInstance() {
+ return mapValidator;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || value == "") {
+ return true;
+ }
+ if (innerType == null) {
+ return false;
+ }
+
+ PropertyTypeValidator innerValidator;
+ PropertyTypeValidator keyValidator = ToscaPropertyType.KEY.getValidator();
+ ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
+
+ if (innerToscaType != null) {
+ switch (innerToscaType) {
+ case STRING:
+ innerValidator = ToscaPropertyType.STRING.getValidator();
+ break;
+ case INTEGER:
+ innerValidator = ToscaPropertyType.INTEGER.getValidator();
+ break;
+ case FLOAT:
+ innerValidator = ToscaPropertyType.FLOAT.getValidator();
+ break;
+ case BOOLEAN:
+ innerValidator = ToscaPropertyType.BOOLEAN.getValidator();
+ break;
+ case JSON:
+ innerValidator = ToscaPropertyType.JSON.getValidator();
+ break;
+ default:
+ log.debug("inner Tosca Type is unknown: {}", innerToscaType);
+ return false;
+ }
+
+ } else {
+ log.debug("inner Tosca Type is: {}", innerType);
+
+ boolean isValid = validateComplexInnerType(value, innerType, allDataTypes);
+ log.debug("Finish to validate value {} of map with inner type {}. Result is {}", value, innerType, isValid);
+ return isValid;
+
+ }
+
+ try {
+ JsonElement jsonObject = jsonParser.parse(value);
+ JsonObject asJsonObject = jsonObject.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String currentKey = entry.getKey();
+ JsonElement jsonValue = entry.getValue();
+
+ String element = JsonUtils.toString(jsonValue);
+
+ if (!innerValidator.isValid(element, null, allDataTypes)
+ || !keyValidator.isValid(entry.getKey(), null, allDataTypes)) {
+ log.debug("validation of key : {}, element: {} failed", currentKey, entry.getValue());
+ return false;
+ }
+ }
+
+ return true;
+ } catch (JsonSyntaxException e) {
+ log.debug("Failed to parse json : {}. {}", value, e);
+ BeEcompErrorManager.getInstance().logBeInvalidJsonInput("Map Validator");
+ }
+
+ return false;
+
+ }
+
+ private boolean validateComplexInnerType(String value, String innerType,
+ Map<String, DataTypeDefinition> allDataTypes) {
+
+ DataTypeDefinition innerDataTypeDefinition = allDataTypes.get(innerType);
+ if (innerDataTypeDefinition == null) {
+ log.debug("Data type {} cannot be found in our data types.", innerType);
+ return false;
+ }
+
+ try {
+ JsonElement jsonObject = jsonParser.parse(value);
+ JsonObject asJsonObject = jsonObject.getAsJsonObject();
+ Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
+ for (Entry<String, JsonElement> entry : entrySet) {
+ String currentKey = entry.getKey();
+ JsonElement currentValue = entry.getValue();
+
+ if (currentValue != null) {
+ String element = JsonUtils.toString(currentValue);
+ boolean isValid = dataTypeValidatorConverter.isValid(element, innerDataTypeDefinition,
+ allDataTypes);
+ if (isValid == false) {
+ log.debug("Cannot parse value {} from type {} of key {}", currentValue, innerType, currentKey);
+ return false;
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ log.debug("Cannot parse value {} of map from inner type {}. {}", value, innerType, e);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/PropertyTypeValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/PropertyTypeValidator.java
new file mode 100644
index 0000000000..35862148f9
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/PropertyTypeValidator.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public interface PropertyTypeValidator {
+
+ boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes);
+
+ boolean isValid(String value, String innerType);
+ /*
+ * The value format should be validated according to the “Property Type�? :
+ * “integer�? - valid tag:yaml.org,2002:int , the number base 8,10,18 should
+ * be handled ( hint : to validate by calling parseInt(
+ * s,10)/parseInt(s,16)/parseInt(s,8) or just regexp [-+]?[0-9]+ for Base 10
+ * , [-+]?0[0-7]+ for Base 8 , [-+]?0x[0-9a-fA-F]+ for Base 16
+ *
+ * “float�? - valid tag:yaml.org,2002:float , parseFloat() “boolean�? - valid
+ * tag:yaml.org,2002:bool : can be only “true�? or “false�? ( upper case
+ * characters should be converted to lower case : TRUE ->true, True->true
+ * “string�? - valid tag:yaml.org,2002:str and limited to 100 chars.
+ *
+ */
+
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/StringValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/StringValidator.java
new file mode 100644
index 0000000000..06994505a9
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/StringValidator.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Map;
+
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.config.Configuration.ToscaValidatorsConfig;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StringValidator implements PropertyTypeValidator {
+
+ public static final int DEFAULT_STRING_MAXIMUM_LENGTH = 100;
+
+ public static int STRING_MAXIMUM_LENGTH = DEFAULT_STRING_MAXIMUM_LENGTH;
+
+ private static Logger log = LoggerFactory.getLogger(StringValidator.class.getName());
+
+ private static StringValidator stringValidator = new StringValidator();
+
+ public static StringValidator getInstance() {
+ return stringValidator;
+ }
+
+ private StringValidator() {
+ if (ConfigurationManager.getConfigurationManager() != null) {
+ ToscaValidatorsConfig toscaValidators = ConfigurationManager.getConfigurationManager().getConfiguration()
+ .getToscaValidators();
+ log.debug("toscaValidators={}", toscaValidators);
+ if (toscaValidators != null) {
+ Integer stringMaxLength = toscaValidators.getStringMaxLength();
+ if (stringMaxLength != null) {
+ STRING_MAXIMUM_LENGTH = stringMaxLength;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> allDataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ if (value.length() > STRING_MAXIMUM_LENGTH) {
+ log.debug("parameter String length {} is higher the configured({})", value.length(), STRING_MAXIMUM_LENGTH);
+ return false;
+ }
+ String coverted = ValidationUtils.removeNoneUtf8Chars(value);
+ boolean isValid = ValidationUtils.validateIsAscii(coverted);
+
+ if (false == isValid) {
+ log.debug("parameter String value {} is not ascii string.", (value != null ? value.substring(0, Math.min(value.length(), 20)) : null));
+ }
+
+ return isValid;
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ToscaBooleanValidator.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ToscaBooleanValidator.java
new file mode 100644
index 0000000000..7f8dff42d0
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/validators/ToscaBooleanValidator.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+
+public class ToscaBooleanValidator implements PropertyTypeValidator {
+
+ private static ToscaBooleanValidator booleanValidator = new ToscaBooleanValidator();
+
+ private static String[] validValues = { "true", "on", "yes", "y", "false", "off", "no", "n" };
+
+ public static ToscaBooleanValidator getInstance() {
+ return booleanValidator;
+ }
+
+ private ToscaBooleanValidator() {
+
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
+
+ if (value == null || true == value.isEmpty()) {
+ return true;
+ }
+
+ return (Arrays.stream(validValues).filter(str -> str.equalsIgnoreCase(value)).toArray().length == 1);
+ }
+
+ @Override
+ public boolean isValid(String value, String innerType) {
+ return isValid(value, innerType, null);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ApplicationVersionException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ApplicationVersionException.java
new file mode 100644
index 0000000000..dadfd49831
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ApplicationVersionException.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.version;
+
+import org.openecomp.sdc.be.model.tosca.constraints.exception.TechnicalException;
+
+public class ApplicationVersionException extends TechnicalException {
+
+ private static final long serialVersionUID = -5192834855057177252L;
+
+ public ApplicationVersionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ApplicationVersionException(String message) {
+ super(message);
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ComparableVersion.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ComparableVersion.java
new file mode 100644
index 0000000000..905d8bf3bc
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/ComparableVersion.java
@@ -0,0 +1,463 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.version;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Stack;
+
+/**
+ * Generic implementation of version comparison.
+ *
+ * <p>
+ * Features:
+ * <ul>
+ * <li>mixing of '<code>-</code>' (dash) and '<code>.</code>' (dot)
+ * separators,</li>
+ * <li>transition between characters and digits also constitutes a separator:
+ * <code>1.0alpha1 =&gt; [1, 0, alpha, 1]</code></li>
+ * <li>unlimited number of version components,</li>
+ * <li>version components in the text can be digits or strings,</li>
+ * <li>strings are checked for well-known qualifiers and the qualifier ordering
+ * is used for version ordering. Well-known qualifiers (case insensitive) are:
+ * <ul>
+ * <li><code>alpha</code> or <code>a</code></li>
+ * <li><code>beta</code> or <code>b</code></li>
+ * <li><code>milestone</code> or <code>m</code></li>
+ * <li><code>rc</code> or <code>cr</code></li>
+ * <li><code>snapshot</code></li>
+ * <li><code>(the empty string)</code> or <code>ga</code> or
+ * <code>final</code></li>
+ * <li><code>sp</code></li>
+ * </ul>
+ * Unknown qualifiers are considered after known qualifiers, with lexical order
+ * (always case insensitive),</li>
+ * <li>a dash usually precedes a qualifier, and is always less important than
+ * something preceded with a dot.</li>
+ * </ul>
+ * </p>
+ *
+ * @see <a href=
+ * "https://cwiki.apache.org/confluence/display/MAVENOLD/Versioning">"Versioning"
+ * on Maven Wiki</a>
+ * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a>
+ * @author <a href="mailto:hboutemy@apache.org">Hervé Boutemy</a>
+ */
+public class ComparableVersion implements Comparable<ComparableVersion> {
+ private String value;
+
+ private String canonical;
+
+ private ListItem items;
+
+ private interface Item {
+ int INTEGER_ITEM = 0;
+ int STRING_ITEM = 1;
+ int LIST_ITEM = 2;
+
+ int compareTo(Item item);
+
+ int getType();
+
+ boolean isNull();
+ }
+
+ /**
+ * Represents a numeric item in the version item list.
+ */
+ private static class IntegerItem implements Item {
+ private static final BigInteger BIG_INTEGER_ZERO = new BigInteger("0");
+
+ private final BigInteger value;
+
+ public static final IntegerItem ZERO = new IntegerItem();
+
+ private IntegerItem() {
+ this.value = BIG_INTEGER_ZERO;
+ }
+
+ public IntegerItem(String str) {
+ this.value = new BigInteger(str);
+ }
+
+ @Override
+ public int getType() {
+ return INTEGER_ITEM;
+ }
+
+ @Override
+ public boolean isNull() {
+ return BIG_INTEGER_ZERO.equals(value);
+ }
+
+ @Override
+ public int compareTo(Item item) {
+ if (item == null) {
+ return BIG_INTEGER_ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1
+ // > 1
+ }
+
+ switch (item.getType()) {
+ case INTEGER_ITEM:
+ return value.compareTo(((IntegerItem) item).value);
+
+ case STRING_ITEM:
+ return 1; // 1.1 > 1-sp
+
+ case LIST_ITEM:
+ return 1; // 1.1 > 1-1
+
+ default:
+ throw new RuntimeException("invalid item: " + item.getClass());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+ }
+
+ /**
+ * Represents a string in the version item list, usually a qualifier.
+ */
+ private static class StringItem implements Item {
+ private static final String[] QUALIFIERS = { "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" };
+
+ private static final List<String> _QUALIFIERS = Arrays.asList(QUALIFIERS);
+
+ private static final Properties ALIASES = new Properties();
+ static {
+ ALIASES.put("ga", "");
+ ALIASES.put("final", "");
+ ALIASES.put("cr", "rc");
+ }
+
+ /**
+ * A comparable value for the empty-string qualifier. This one is used
+ * to determine if a given qualifier makes the version older than one
+ * without a qualifier, or more recent.
+ */
+ private static final String RELEASE_VERSION_INDEX = String.valueOf(_QUALIFIERS.indexOf(""));
+
+ private String value;
+
+ public StringItem(String value, boolean followedByDigit) {
+ if (followedByDigit && value.length() == 1) {
+ // a1 = alpha-1, b1 = beta-1, m1 = milestone-1
+ switch (value.charAt(0)) {
+ case 'a':
+ value = "alpha";
+ break;
+ case 'b':
+ value = "beta";
+ break;
+ case 'm':
+ value = "milestone";
+ break;
+ }
+ }
+ this.value = ALIASES.getProperty(value, value);
+ }
+
+ @Override
+ public int getType() {
+ return STRING_ITEM;
+ }
+
+ @Override
+ public boolean isNull() {
+ return (comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX) == 0);
+ }
+
+ /**
+ * Returns a comparable value for a qualifier.
+ *
+ * This method takes into account the ordering of known qualifiers then
+ * unknown qualifiers with lexical ordering.
+ *
+ * just returning an Integer with the index here is faster, but requires
+ * a lot of if/then/else to check for -1 or QUALIFIERS.size and then
+ * resort to lexical ordering. Most comparisons are decided by the first
+ * character, so this is still fast. If more characters are needed then
+ * it requires a lexical sort anyway.
+ *
+ * @param qualifier
+ * @return an equivalent value that can be used with lexical comparison
+ */
+ public static String comparableQualifier(String qualifier) {
+ int i = _QUALIFIERS.indexOf(qualifier);
+
+ return i == -1 ? (_QUALIFIERS.size() + "-" + qualifier) : String.valueOf(i);
+ }
+
+ // @Override
+ public int compareTo(Item item) {
+ if (item == null) {
+ // 1-rc < 1, 1-ga > 1
+ return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX);
+ }
+ switch (item.getType()) {
+ case INTEGER_ITEM:
+ return -1; // 1.any < 1.1 ?
+
+ case STRING_ITEM:
+ return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value));
+
+ case LIST_ITEM:
+ return -1; // 1.any < 1-1
+
+ default:
+ throw new RuntimeException("invalid item: " + item.getClass());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+ /**
+ * Represents a version list item. This class is used both for the global
+ * item list and for sub-lists (which start with '-(number)' in the version
+ * specification).
+ */
+ private static class ListItem extends ArrayList<Item> implements Item {
+
+ private static final long serialVersionUID = -4740226741001149657L;
+
+ @Override
+ public int getType() {
+ return LIST_ITEM;
+ }
+
+ @Override
+ public boolean isNull() {
+ return (size() == 0);
+ }
+
+ void normalize() {
+ for (ListIterator<Item> iterator = listIterator(size()); iterator.hasPrevious();) {
+ Item item = iterator.previous();
+ if (item.isNull()) {
+ iterator.remove(); // remove null trailing items: 0, "",
+ // empty list
+ } else {
+ break;
+ }
+ }
+ }
+
+ @Override
+ public int compareTo(Item item) {
+ if (item == null) {
+ if (size() == 0) {
+ return 0; // 1-0 = 1- (normalize) = 1
+ }
+ Item first = get(0);
+ return first.compareTo(null);
+ }
+ switch (item.getType()) {
+ case INTEGER_ITEM:
+ return -1; // 1-1 < 1.0.x
+
+ case STRING_ITEM:
+ return 1; // 1-1 > 1-sp
+
+ case LIST_ITEM:
+ Iterator<Item> left = iterator();
+ Iterator<Item> right = ((ListItem) item).iterator();
+
+ while (left.hasNext() || right.hasNext()) {
+ Item l = left.hasNext() ? left.next() : null;
+ Item r = right.hasNext() ? right.next() : null;
+
+ int result = 0;
+ if (r != null && l != null) {
+ result = l.compareTo(r);
+ } else if (r == null && l == null) {
+ result = 0;
+ } else if (l == null) {
+ result = -1;
+ } else {
+ result = 1;
+ }
+
+ // if this is shorter, then invert the compare and mul with
+ // -1
+ // int result = (l == null ? (r == null ? 0 : -1 *
+ // r.compareTo(l)) : l.compareTo(r));
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ return 0;
+
+ default:
+ throw new RuntimeException("invalid item: " + item.getClass());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder("(");
+ for (Iterator<Item> iter = iterator(); iter.hasNext();) {
+ buffer.append(iter.next());
+ if (iter.hasNext()) {
+ buffer.append(',');
+ }
+ }
+ buffer.append(')');
+ return buffer.toString();
+ }
+ }
+
+ public ComparableVersion(String version) {
+ parseVersion(version);
+ }
+
+ public final void parseVersion(String version) {
+ this.value = version;
+
+ items = new ListItem();
+
+ version = version.toLowerCase(Locale.ENGLISH);
+
+ ListItem list = items;
+
+ Stack<Item> stack = new Stack<Item>();
+ stack.push(list);
+
+ boolean isDigit = false;
+
+ int startIndex = 0;
+
+ for (int i = 0; i < version.length(); i++) {
+ char c = version.charAt(i);
+
+ if (c == '.') {
+ if (i == startIndex) {
+ list.add(IntegerItem.ZERO);
+ } else {
+ list.add(parseItem(isDigit, version.substring(startIndex, i)));
+ }
+ startIndex = i + 1;
+ } else if (c == '-') {
+ if (i == startIndex) {
+ list.add(IntegerItem.ZERO);
+ } else {
+ list.add(parseItem(isDigit, version.substring(startIndex, i)));
+ }
+ startIndex = i + 1;
+
+ if (isDigit) {
+ list.normalize(); // 1.0-* = 1-*
+
+ if ((i + 1 < version.length()) && Character.isDigit(version.charAt(i + 1))) {
+ // new ListItem only if previous were digits and new
+ // char is a digit,
+ // ie need to differentiate only 1.1 from 1-1
+ list.add(list = new ListItem());
+
+ stack.push(list);
+ }
+ }
+ } else if (Character.isDigit(c)) {
+ if (!isDigit && i > startIndex) {
+ list.add(new StringItem(version.substring(startIndex, i), true));
+ startIndex = i;
+ }
+
+ isDigit = true;
+ } else {
+ if (isDigit && i > startIndex) {
+ list.add(parseItem(true, version.substring(startIndex, i)));
+ startIndex = i;
+ }
+
+ isDigit = false;
+ }
+ }
+
+ if (version.length() > startIndex) {
+ list.add(parseItem(isDigit, version.substring(startIndex)));
+ }
+
+ while (!stack.isEmpty()) {
+ list = (ListItem) stack.pop();
+ list.normalize();
+ }
+
+ canonical = items.toString();
+ }
+
+ private static Item parseItem(boolean isDigit, String buf) {
+ return isDigit ? new IntegerItem(buf) : new StringItem(buf, false);
+ }
+
+ @Override
+ public int compareTo(ComparableVersion o) {
+ return items.compareTo(o.items);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof ComparableVersion) && canonical.equals(((ComparableVersion) o).canonical);
+ }
+
+ @Override
+ public int hashCode() {
+ return canonical.hashCode();
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/Version.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/Version.java
new file mode 100644
index 0000000000..268ee28b96
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/tosca/version/Version.java
@@ -0,0 +1,192 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.version;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+/**
+ * Default implementation of artifact versioning.
+ *
+ * @author <a href="mailto:brett@apache.org">Brett Porter</a>
+ */
+public class Version implements Comparable<Version> {
+ private Integer majorVersion;
+
+ private Integer minorVersion;
+
+ private Integer incrementalVersion;
+
+ private Integer buildNumber;
+
+ private String qualifier;
+
+ private ComparableVersion comparable;
+
+ public Version(String version) {
+ parseVersion(version);
+ }
+
+ @Override
+ public int hashCode() {
+ return 11 + comparable.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof Version)) {
+ return false;
+ }
+
+ return compareTo((Version) other) == 0;
+ }
+
+ public int compareTo(Version otherVersion) {
+ return this.comparable.compareTo(otherVersion.comparable);
+ }
+
+ public int getMajorVersion() {
+ return majorVersion != null ? majorVersion : 0;
+ }
+
+ public int getMinorVersion() {
+ return minorVersion != null ? minorVersion : 0;
+ }
+
+ public int getIncrementalVersion() {
+ return incrementalVersion != null ? incrementalVersion : 0;
+ }
+
+ public int getBuildNumber() {
+ return buildNumber != null ? buildNumber : 0;
+ }
+
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public final void parseVersion(String version) {
+ comparable = new ComparableVersion(version);
+
+ int index = version.indexOf("-");
+
+ String part1;
+ String part2 = null;
+
+ if (index < 0) {
+ part1 = version;
+ } else {
+ part1 = version.substring(0, index);
+ part2 = version.substring(index + 1);
+ }
+
+ if (part2 != null) {
+ try {
+ if ((part2.length() == 1) || !part2.startsWith("0")) {
+ buildNumber = Integer.valueOf(part2);
+ } else {
+ qualifier = part2;
+ }
+ } catch (NumberFormatException e) {
+ qualifier = part2;
+ }
+ }
+
+ if ((!part1.contains(".")) && !part1.startsWith("0")) {
+ try {
+ majorVersion = Integer.valueOf(part1);
+ } catch (NumberFormatException e) {
+ // qualifier is the whole version, including "-"
+ qualifier = version;
+ buildNumber = null;
+ }
+ } else {
+ boolean fallback = false;
+
+ StringTokenizer tok = new StringTokenizer(part1, ".");
+ try {
+ majorVersion = getNextIntegerToken(tok);
+ if (tok.hasMoreTokens()) {
+ minorVersion = getNextIntegerToken(tok);
+ }
+ if (tok.hasMoreTokens()) {
+ incrementalVersion = getNextIntegerToken(tok);
+ }
+ if (tok.hasMoreTokens()) {
+ qualifier = tok.nextToken();
+ fallback = Pattern.compile("\\d+").matcher(qualifier).matches();
+ }
+
+ // string tokenzier won't detect these and ignores them
+ if (part1.contains("..") || part1.startsWith(".") || part1.endsWith(".")) {
+ fallback = true;
+ }
+ } catch (NumberFormatException e) {
+ fallback = true;
+ }
+
+ if (fallback) {
+ // qualifier is the whole version, including "-"
+ qualifier = version;
+ majorVersion = null;
+ minorVersion = null;
+ incrementalVersion = null;
+ buildNumber = null;
+ }
+ }
+ }
+
+ private static Integer getNextIntegerToken(StringTokenizer tok) {
+ String s = tok.nextToken();
+ if ((s.length() > 1) && s.startsWith("0")) {
+ throw new NumberFormatException("Number part has a leading 0: '" + s + "'");
+ }
+ return Integer.valueOf(s);
+ }
+
+ @Override
+ public String toString() {
+ return comparable.toString();
+ }
+}
diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/unittests/utils/FactoryUtils.java b/catalog-model/src/main/java/org/openecomp/sdc/be/unittests/utils/FactoryUtils.java
new file mode 100644
index 0000000000..f95a89db62
--- /dev/null
+++ b/catalog-model/src/main/java/org/openecomp/sdc/be/unittests/utils/FactoryUtils.java
@@ -0,0 +1,233 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.unittests.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+
+public final class FactoryUtils {
+ private FactoryUtils() {
+ };
+
+ public static final class Constants {
+ public static final String DEFAULT_CAPABILITY_TYPE = "tosca.capabilities.Node";
+ }
+
+ public static Resource createVFWithRI(String riVersion) {
+ Resource vf = createVF();
+ ComponentInstance ri = createResourceInstanceWithVersion(riVersion);
+ addComponentInstanceToVF(vf, ri);
+ return vf;
+ }
+
+ public static Resource createVF() {
+ Resource resource = new Resource();
+ String uniqueId = UUID.randomUUID().toString();
+ resource.setUniqueId(uniqueId);
+ return resource;
+ }
+
+ public static void addComponentInstanceToVF(Resource vf, ComponentInstance resourceInstance) {
+ List<ComponentInstance> componentsInstances = vf.getComponentInstances() != null ? vf.getComponentInstances()
+ : new ArrayList<>();
+ componentsInstances.add(resourceInstance);
+ vf.setComponentInstances(componentsInstances);
+ }
+
+ public static ComponentInstance createResourceInstance() {
+ ComponentInstance ri = new ComponentInstance();
+ ri.setComponentVersion("0.1");
+ String uniqueId = UUID.randomUUID().toString();
+ ri.setComponentUid(uniqueId);
+ ri.setUniqueId(uniqueId);
+ ri.setName("genericRI" + uniqueId);
+ ri.setOriginType(OriginTypeEnum.VF);
+ return ri;
+
+ }
+
+ public static ComponentInstance createResourceInstanceWithVersion(String riVersion) {
+ ComponentInstance ri = createResourceInstance();
+ ri.setComponentVersion(riVersion);
+ return ri;
+ }
+
+ public static CapabilityData createCapabilityData() {
+ CapabilityData capData = new CapabilityData();
+ String uniqueId = UUID.randomUUID().toString();
+ capData.setUniqueId(uniqueId);
+
+ capData.setType(Constants.DEFAULT_CAPABILITY_TYPE);
+ return capData;
+ }
+
+ public static RequirementData createRequirementData() {
+ RequirementData reqData = new RequirementData();
+ String uniqueId = UUID.randomUUID().toString();
+ reqData.setUniqueId(uniqueId);
+ return reqData;
+ }
+
+ public static CapabilityDefinition convertCapabilityDataToCapabilityDefinitionAddProperties(
+ CapabilityData capData) {
+ CapabilityDefinition capDef = new CapabilityDefinition();
+ capDef.setName("Cap2");
+ capDef.setDescription(capData.getDescription());
+ capDef.setUniqueId(capData.getUniqueId());
+ capDef.setValidSourceTypes(capData.getValidSourceTypes());
+ capDef.setType(capData.getType());
+ capDef.setProperties(new ArrayList<>());
+ ComponentInstanceProperty host = new ComponentInstanceProperty();
+ host.setUniqueId(UUID.randomUUID().toString());
+ host.setName("host");
+ host.setDefaultValue("defhost");
+ host.setType("string");
+
+ host.setSchema(new SchemaDefinition());
+ host.getSchema().setProperty(new PropertyDataDefinition());
+ host.getSchema().getProperty().setType("string");
+
+ capDef.getProperties().add(host);
+ ComponentInstanceProperty port = new ComponentInstanceProperty();
+ port.setName("port");
+ port.setDefaultValue("defport");
+ port.setUniqueId(UUID.randomUUID().toString());
+ port.setType("string");
+
+ port.setSchema(new SchemaDefinition());
+ port.getSchema().setProperty(new PropertyDataDefinition());
+ port.getSchema().getProperty().setType("string");
+
+ capDef.getProperties().add(port);
+ return capDef;
+ }
+
+ public static List<ComponentInstanceProperty> createComponentInstancePropertyList() {
+ List<ComponentInstanceProperty> properties = new ArrayList<>();
+ ComponentInstanceProperty host = new ComponentInstanceProperty();
+ host.setUniqueId(UUID.randomUUID().toString());
+ host.setName("host");
+ host.setValue("newhost");
+ host.setType("string");
+
+ host.setSchema(new SchemaDefinition());
+ host.getSchema().setProperty(new PropertyDataDefinition());
+ host.getSchema().getProperty().setType("string");
+
+ properties.add(host);
+ ComponentInstanceProperty port = new ComponentInstanceProperty();
+ port.setName("port");
+ port.setValue("newport");
+ port.setUniqueId(UUID.randomUUID().toString());
+ port.setType("string");
+
+ port.setSchema(new SchemaDefinition());
+ port.getSchema().setProperty(new PropertyDataDefinition());
+ port.getSchema().getProperty().setType("string");
+
+ properties.add(port);
+ return properties;
+ }
+
+ public static RequirementDefinition convertRequirementDataIDToRequirementDefinition(String reqDataId) {
+ RequirementDefinition reqDef = new RequirementDefinition();
+ reqDef.setUniqueId(reqDataId);
+ reqDef.setCapability(Constants.DEFAULT_CAPABILITY_TYPE);
+ return reqDef;
+ }
+
+ public static GraphEdge createGraphEdge() {
+ GraphEdge graphEdge = new GraphEdge();
+ return graphEdge;
+ }
+
+ public static CapabilityInstData createCapabilityInstData() {
+ CapabilityInstData capInstData = new CapabilityInstData();
+ String uniqueId = UUID.randomUUID().toString();
+ capInstData.setUniqueId(uniqueId);
+ return capInstData;
+ }
+
+ public static PropertyValueData createPropertyData() {
+ PropertyValueData propData = new PropertyValueData();
+ String uniqueId = UUID.randomUUID().toString();
+ propData.setValue("localhost");
+ propData.setUniqueId(uniqueId);
+ return propData;
+ }
+
+ public static PropertyData convertCapabilityDefinitionToCapabilityData(PropertyDefinition propDef) {
+ PropertyData propData = new PropertyData();
+ propData.getPropertyDataDefinition().setUniqueId(propDef.getUniqueId());
+ propData.getPropertyDataDefinition().setDefaultValue(propDef.getDefaultValue());
+ return propData;
+ }
+
+ public static CapabilityDefinition convertCapabilityDataToCapabilityDefinitionRoot(CapabilityData capData) {
+ CapabilityDefinition capDef = new CapabilityDefinition();
+ capDef.setName("Cap1");
+ capDef.setDescription(capData.getDescription());
+ capDef.setUniqueId(capData.getUniqueId());
+ capDef.setValidSourceTypes(capData.getValidSourceTypes());
+ capDef.setType(capData.getType());
+ capDef.setProperties(new ArrayList<>());
+ ComponentInstanceProperty host = new ComponentInstanceProperty();
+ host.setUniqueId(UUID.randomUUID().toString());
+ host.setName("host");
+ host.setDefaultValue("roothost");
+ host.setType("string");
+
+ host.setSchema(new SchemaDefinition());
+ host.getSchema().setProperty(new PropertyDataDefinition());
+ host.getSchema().getProperty().setType("string");
+
+ capDef.getProperties().add(host);
+ ComponentInstanceProperty port = new ComponentInstanceProperty();
+ port.setName("port");
+ port.setDefaultValue("rootport");
+ port.setUniqueId(UUID.randomUUID().toString());
+ port.setType("string");
+
+ port.setSchema(new SchemaDefinition());
+ port.getSchema().setProperty(new PropertyDataDefinition());
+ port.getSchema().getProperty().setType("string");
+
+ capDef.getProperties().add(port);
+ return capDef;
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/ModelTestBase.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/ModelTestBase.java
new file mode 100644
index 0000000000..f18aa61b74
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/ModelTestBase.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model;
+
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+
+public class ModelTestBase {
+
+ protected static ConfigurationManager configurationManager;
+
+ public static void init() {
+ if (ConfigurationManager.getConfigurationManager() == null) {
+ String appConfigDir = "src/test/resources/config";
+ ConfigurationSource configurationSource = new FSConfigurationSource(
+ ExternalConfiguration.getChangeListener(), appConfigDir);
+ configurationManager = new ConfigurationManager(configurationSource);
+
+ Configuration configuration = new Configuration();
+ configuration.setTitanInMemoryGraph(true);
+
+ configurationManager.setConfiguration(configuration);
+ }
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/JsonObjectTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/JsonObjectTest.java
new file mode 100644
index 0000000000..dd102e7ed5
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/JsonObjectTest.java
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.sdc.be.model.UploadResourceInfo;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.api.UploadArtifactInfo;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JsonObjectTest {
+
+ private ObjectMapper mapper;
+ UploadResourceInfo inputObjectRef;
+ private final String INPUT_RESOURCE_STRING = "{ \"payloadData\" : \"My Test Object\", \"payloadName\" : \"TestName\", "
+ + " \"description\":\"my_description\",\"tags\":[\"tag1\"], "
+ + "\"artifactList\" : [ { \"artifactName\" : \"myArtifact0\", \"artifactPath\" : \"scripts/\", \"artifactType\" : \"PUPPET\", "
+ + " \"artifactDescription\" : \"This is Description\", \"artifactData\" : null }, "
+ + "{ \"artifactName\" : \"myArtifact1\", \"artifactPath\" : \"scripts/\", \"artifactType\" : \"PUPPET\", \"artifactDescription\" : \"This is Description\", "
+ + " \"artifactData\" : null } ], \"contactId\" : null, \"name\" : null, \"resourceIconPath\" : null, \"vendorName\" : null, \"vendorRelease\" : null , \"resourceType\" : \"VFC\" }";
+
+ @Before
+ public void setup() {
+ mapper = new ObjectMapper();
+ ArrayList<UploadArtifactInfo> artifactList = new ArrayList<UploadArtifactInfo>();
+ for (int i = 0; i < 2; i++) {
+ UploadArtifactInfo artifactInfo = new UploadArtifactInfo("myArtifact" + i, "scripts/",
+ ArtifactTypeEnum.PUPPET, "This is Description");
+ artifactList.add(artifactInfo);
+ }
+ ArrayList<String> tags = new ArrayList<>();
+ tags.add("tag1");
+ inputObjectRef = new UploadResourceInfo("My Test Object", "TestName", "my_description", null, tags,
+ artifactList);
+
+ }
+
+ @Test
+ public void testStringToUploadResourceInfo() throws JsonParseException, JsonMappingException, IOException {
+ UploadResourceInfo resourceObjectTest = mapper.readValue(INPUT_RESOURCE_STRING, UploadResourceInfo.class);
+ assertEquals(inputObjectRef, resourceObjectTest);
+
+ }
+
+ // @Test
+ public void testUploadResourceInfoToString() throws JsonParseException, JsonMappingException, IOException {
+ String refAsString = mapper.writeValueAsString(inputObjectRef);
+ String unFormattedString = refAsString.replace("\n", "").replace("\t", "").replace(" ", "");
+
+ assertEquals(unFormattedString, INPUT_RESOURCE_STRING.replace("\n", "").replace("\t", "").replace(" ", ""));
+
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperationTest.java
new file mode 100644
index 0000000000..3e871f1fa0
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/AdditionalInformationOperationTest.java
@@ -0,0 +1,216 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.Point;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.IAdditionalInformationOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.exception.DeleteReferencedObjectException;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thinkaurelius.titan.core.TitanGraph;
+//import com.tinkerpop.blueprints.Vertex;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class AdditionalInformationOperationTest extends ModelTestBase {
+
+ private Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ private static String USER_ID = "muuserid";
+ private static String CATEGORY_NAME = "category/mycategory";
+
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "additional-information-operation")
+ private IAdditionalInformationOperation additionalInformationOperation;
+
+ @Before
+ public void createUserAndCategory() {
+ deleteAndCreateCategory(CATEGORY_NAME);
+ deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID);
+
+ }
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ ModelTestBase.init();
+
+ }
+
+ @Test
+ public void testDummy() {
+
+ assertTrue(additionalInformationOperation != null);
+
+ }
+
+ private int getNumberOfVerticesOnGraph() {
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanDao.getGraph();
+ TitanGraph graph = graphResult.left().value();
+
+ int i = 0;
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ i++;
+ }
+
+ }
+
+ titanDao.commit();
+
+ return i;
+ }
+
+ @Test
+ public void testCreateAndDeleteResource() {
+
+ int before = getNumberOfVerticesOnGraph();
+
+ Resource newResource = createResource(USER_ID, CATEGORY_NAME, "testCreateAndDeleteResource", "0.1", null, false,
+ true);
+ String resourceId = newResource.getUniqueId();
+
+ Either<Resource, StorageOperationStatus> deleteResource = resourceOperation.deleteResource(resourceId);
+ assertTrue(deleteResource.isLeft());
+
+ int after = getNumberOfVerticesOnGraph();
+
+ assertEquals("check number of vertices not changed", before, after);
+ }
+
+ private Resource buildResourceMetadata(String userId, String category, String resourceName,
+ String resourceVersion) {
+
+ Resource resource = new Resource();
+ resource.setName(resourceName);
+ resource.setVersion(resourceVersion);
+ ;
+ resource.setDescription("description 1");
+ resource.setAbstract(false);
+ resource.setCreatorUserId(userId);
+ resource.setContactId("contactId@sdc.com");
+ resource.setVendorName("vendor 1");
+ resource.setVendorRelease("1.0.0");
+ String[] categoryArr = category.split("/");
+ resource.addCategory(categoryArr[0], categoryArr[1]);
+ resource.setIcon("images/my.png");
+ // List<String> tags = new ArrayList<String>();
+ // tags.add("TAG1");
+ // tags.add("TAG2");
+ // resource.setTags(tags);
+ return resource;
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanDao);
+ }
+
+ public Resource createResource(String userId, String category, String resourceName, String resourceVersion,
+ String parentResourceName, boolean isAbstract, boolean isHighestVersion) {
+
+ List<String> derivedFrom = new ArrayList<String>();
+ if (parentResourceName != null) {
+ derivedFrom.add(parentResourceName);
+ }
+ Resource resource = buildResourceMetadata(userId, category, resourceName, resourceVersion);
+
+ resource.setAbstract(isAbstract);
+ resource.setHighestVersion(isHighestVersion);
+
+ Either<Resource, StorageOperationStatus> result = resourceOperation.createResource(resource, true);
+
+ assertTrue(result.isLeft());
+ Resource resultResource = result.left().value();
+
+ assertEquals("check resource state", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ resultResource.getLifecycleState());
+
+ return resultResource;
+
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperationTest.java
new file mode 100644
index 0000000000..0143e50dc7
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactOperationTest.java
@@ -0,0 +1,574 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.HeatParameterData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class ArtifactOperationTest extends ModelTestBase {
+
+ private static final String ARTIFACT_NAME = "myHeatArtifact";
+
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "service-operation")
+ private ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ private IGraphLockOperation graphLockOperation;
+
+ @javax.annotation.Resource
+ private ArtifactOperation artifactOperation;
+
+ @javax.annotation.Resource(name = "requirement-operation")
+ private RequirementOperation requirementOperation;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource(name = "capability-operation")
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @javax.annotation.Resource(name = "component-instance-operation")
+ private ComponentInstanceOperation resourceInstanceOperation;
+
+ @javax.annotation.Resource(name = "lifecycle-operation")
+ private LifecycleOperation lifecycleOperation;
+
+ private static Logger log = LoggerFactory.getLogger(ServiceOperation.class.getName());
+
+ private static String RESOURCE_ID = "resourceId";
+ private static String RESOURCE_ID_2 = "resourceId2";
+
+ private static String USER_ID = "muserid";
+ private static String CATEGORY_NAME = "category/mycategory";
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ ModelTestBase.init();
+ }
+
+ @Before
+ public void createUserAndCategory() {
+ deleteAndCreateCategory(CATEGORY_NAME);
+ deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID, null);
+ }
+
+// @Test
+ public void testAddArtifactToServiceVersionAndUUIDNotNull() {
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName(CATEGORY_NAME);
+
+ String serviceName = "servceTest2";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug("{}", serviceAfterSave);
+ String serviceId = serviceAfterSave.getUniqueId();
+
+ ArtifactDefinition artifactInfo = addArtifactToService(userId, serviceId, "install_apache");
+
+ assertEquals("add informational artifact version : " + artifactInfo.getArtifactVersion(), "1",
+ artifactInfo.getArtifactVersion());
+
+ assertNotNull("add informational artifact version : " + artifactInfo.getArtifactUUID(),
+ artifactInfo.getArtifactUUID());
+
+ Either<Service, StorageOperationStatus> service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ Map<String, ArtifactDefinition> artifacts = service.left().value().getArtifacts();
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ String artifactId = entry.getValue().getUniqueId();
+ String description = entry.getValue().getDescription();
+
+ artifactOperation.removeArifactFromResource(serviceId, artifactId, NodeTypeEnum.Service, true, false);
+ }
+ service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ artifacts = service.left().value().getArtifacts();
+ assertEquals(0, artifacts.size());
+
+ Either<Service, StorageOperationStatus> serviceDelete = serviceOperation.deleteService(serviceId);
+
+ Either<List<ArtifactData>, TitanOperationStatus> byCriteria = titanDao.getByCriteria(NodeTypeEnum.ArtifactRef,
+ null, ArtifactData.class);
+ assertTrue(byCriteria.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, byCriteria.right().value());
+
+ serviceOperation.deleteService(serviceAfterSave.getUniqueId());
+
+ }
+
+// @Test
+ public void testUpdateArtifactToServiceVersionNotChanged() {
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName(CATEGORY_NAME);
+ String serviceName = "servceTest2";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug("{}", serviceAfterSave);
+ String serviceId = serviceAfterSave.getUniqueId();
+
+ ArtifactDefinition artifactInfo = addArtifactToService(userId, serviceId, "install_apache");
+
+ String version = artifactInfo.getArtifactVersion();
+ String artUuid = artifactInfo.getArtifactUUID();
+ assertEquals("add informational artifact version : " + version, "1", version);
+
+ artifactInfo.setDescription("jghlsk new desfnjdh");
+
+ Either<ArtifactDefinition, StorageOperationStatus> artifact = artifactOperation.updateArifactOnResource(
+ artifactInfo, serviceId, artifactInfo.getUniqueId(), NodeTypeEnum.Service, false);
+ String newVersion = artifact.left().value().getArtifactVersion();
+ String newArtUuid = artifactInfo.getArtifactUUID();
+ assertEquals("add informational artifact version : " + newVersion, newVersion, version);
+ assertEquals("add informational artifact uuid : " + newArtUuid, newArtUuid, artUuid);
+
+ Either<Service, StorageOperationStatus> service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ Map<String, ArtifactDefinition> artifacts = service.left().value().getArtifacts();
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ String artifactId = entry.getValue().getUniqueId();
+ String description = entry.getValue().getDescription();
+
+ artifactOperation.removeArifactFromResource(serviceId, artifactId, NodeTypeEnum.Service, true, false);
+ }
+ service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ artifacts = service.left().value().getArtifacts();
+ assertEquals(0, artifacts.size());
+
+ Either<Service, StorageOperationStatus> serviceDelete = serviceOperation.deleteService(serviceId);
+
+ Either<List<ArtifactData>, TitanOperationStatus> byCriteria = titanDao.getByCriteria(NodeTypeEnum.ArtifactRef,
+ null, ArtifactData.class);
+ assertTrue(byCriteria.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, byCriteria.right().value());
+
+ serviceOperation.deleteService(serviceAfterSave.getUniqueId());
+
+ }
+
+ @Test
+ public void testCreateDeleteArtifactWithHeatParams() {
+
+ ArtifactDefinition artifactWithHeat = createResourceWithHeat();
+
+ List<HeatParameterDefinition> heatParameters = artifactWithHeat.getHeatParameters();
+ assertNotNull(heatParameters);
+ assertTrue(heatParameters.size() == 1);
+ HeatParameterDefinition parameter = heatParameters.get(0);
+ HeatParameterData parameterData = new HeatParameterData(parameter);
+ Either<HeatParameterData, TitanOperationStatus> parameterNode = titanDao.getNode(parameterData.getUniqueIdKey(),
+ parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNode.isLeft());
+
+ Either<ArtifactDefinition, StorageOperationStatus> removeArifact = artifactOperation.removeArifactFromResource(
+ RESOURCE_ID, artifactWithHeat.getUniqueId(), NodeTypeEnum.Resource, true, false);
+ assertTrue(removeArifact.isLeft());
+
+ ArtifactData artifactData = new ArtifactData(artifactWithHeat);
+ Either<ArtifactData, TitanOperationStatus> artifactAfterDelete = titanDao.getNode(artifactData.getUniqueIdKey(),
+ artifactData.getUniqueId(), ArtifactData.class);
+ assertTrue(artifactAfterDelete.isRight());
+
+ Either<HeatParameterData, TitanOperationStatus> parameterNodeAfterDelete = titanDao
+ .getNode(parameterData.getUniqueIdKey(), parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNodeAfterDelete.isRight());
+
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID), ResourceMetadataData.class);
+ }
+
+ @Test
+ public void testUpdateArtifactWithHeatParams() {
+
+ ArtifactDefinition artifactWithHeat = createResourceWithHeat();
+
+ List<HeatParameterDefinition> heatParameters = artifactWithHeat.getHeatParameters();
+ assertNotNull(heatParameters);
+ assertTrue(heatParameters.size() == 1);
+ HeatParameterDefinition parameter = heatParameters.get(0);
+ HeatParameterData parameterData = new HeatParameterData(parameter);
+ Either<HeatParameterData, TitanOperationStatus> parameterNode = titanDao.getNode(parameterData.getUniqueIdKey(),
+ parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNode.isLeft());
+
+ // update to artifact without params
+ ArtifactDefinition artifactNoParams = createArtifactDefinition(USER_ID, RESOURCE_ID, ARTIFACT_NAME);
+ artifactNoParams.setUniqueId(artifactWithHeat.getUniqueId());
+ artifactNoParams.setArtifactType("HEAT");
+ artifactNoParams.setArtifactVersion("2");
+ artifactNoParams.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifact = artifactOperation.updateArifactOnResource(
+ artifactNoParams, RESOURCE_ID, artifactWithHeat.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertTrue(updateArifact.isLeft());
+
+ ArtifactData artifactData = new ArtifactData(artifactWithHeat);
+ Either<ArtifactData, TitanOperationStatus> artifactAfterUpdate = titanDao.getNode(artifactData.getUniqueIdKey(),
+ artifactData.getUniqueId(), ArtifactData.class);
+ assertTrue(artifactAfterUpdate.isLeft());
+ ArtifactData artifactAfterUpdateValue = artifactAfterUpdate.left().value();
+ assertTrue(artifactNoParams.getArtifactVersion()
+ .equals(artifactAfterUpdateValue.getArtifactDataDefinition().getArtifactVersion()));
+
+ Either<HeatParameterData, TitanOperationStatus> parameterNodeAfterDelete = titanDao
+ .getNode(parameterData.getUniqueIdKey(), parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNodeAfterDelete.isRight());
+
+ artifactOperation.removeArifactFromResource(RESOURCE_ID, artifactWithHeat.getUniqueId(), NodeTypeEnum.Resource,
+ true, false);
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID), ResourceMetadataData.class);
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID_2), ResourceMetadataData.class);
+ }
+
+ @Test
+ public void testUpdateArtifactMetadataWithHeatParams() {
+
+ ArtifactDefinition artifactWithHeat = createResourceWithHeat();
+
+ List<HeatParameterDefinition> heatParameters = artifactWithHeat.getHeatParameters();
+ assertNotNull(heatParameters);
+ assertTrue(heatParameters.size() == 1);
+ HeatParameterDefinition parameter = heatParameters.get(0);
+ HeatParameterData parameterData = new HeatParameterData(parameter);
+ Either<HeatParameterData, TitanOperationStatus> parameterNode = titanDao.getNode(parameterData.getUniqueIdKey(),
+ parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNode.isLeft());
+
+ // update to artifact without params
+ artifactWithHeat.setArtifactVersion("2");
+ artifactWithHeat.setArtifactChecksum(null);
+ artifactWithHeat.setPayloadData(null);
+
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifact = artifactOperation.updateArifactOnResource(
+ artifactWithHeat, RESOURCE_ID, artifactWithHeat.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertTrue(updateArifact.isLeft());
+
+ ArtifactData artifactData = new ArtifactData(artifactWithHeat);
+ Either<ArtifactData, TitanOperationStatus> artifactAfterUpdate = titanDao.getNode(artifactData.getUniqueIdKey(),
+ artifactData.getUniqueId(), ArtifactData.class);
+ assertTrue(artifactAfterUpdate.isLeft());
+ ArtifactData artifactAfterUpdateValue = artifactAfterUpdate.left().value();
+ assertTrue(artifactWithHeat.getArtifactVersion()
+ .equals(artifactAfterUpdateValue.getArtifactDataDefinition().getArtifactVersion()));
+
+ Either<HeatParameterData, TitanOperationStatus> parameterNodeAfterDelete = titanDao
+ .getNode(parameterData.getUniqueIdKey(), parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNodeAfterDelete.isLeft());
+
+ Either<ArtifactDefinition, StorageOperationStatus> removeArifact = artifactOperation.removeArifactFromResource(
+ RESOURCE_ID_2, (String) artifactAfterUpdateValue.getUniqueId(), NodeTypeEnum.Resource, true, false);
+ removeArifact = artifactOperation.removeArifactFromResource(RESOURCE_ID, artifactWithHeat.getUniqueId(),
+ NodeTypeEnum.Resource, true, false);
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID), ResourceMetadataData.class);
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID_2), ResourceMetadataData.class);
+
+ }
+
+ @Test
+ public void updateHeatArtifactWithTwoResources() {
+ ArtifactDefinition artifactWithHeat = createResourceWithHeat();
+
+ ResourceMetadataData resource2 = createResource(RESOURCE_ID_2);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), ArtifactGroupTypeEnum.DEPLOYMENT.name());
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanDao.createRelation(resource2,
+ new ArtifactData(artifactWithHeat), GraphEdgeLabels.ARTIFACT_REF, props);
+ assertTrue(createRelation.isLeft());
+
+ List<HeatParameterDefinition> heatParameters = artifactWithHeat.getHeatParameters();
+ assertNotNull(heatParameters);
+ assertTrue(heatParameters.size() == 1);
+ HeatParameterDefinition parameter = heatParameters.get(0);
+ HeatParameterData parameterData = new HeatParameterData(parameter);
+ Either<HeatParameterData, TitanOperationStatus> parameterNode = titanDao.getNode(parameterData.getUniqueIdKey(),
+ parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNode.isLeft());
+
+ ArtifactDefinition atifactToUpdate = new ArtifactDefinition(artifactWithHeat);
+
+ // update to artifact without params
+ atifactToUpdate.setArtifactVersion("2");
+ atifactToUpdate.setArtifactChecksum(null);
+ atifactToUpdate.setPayloadData(null);
+
+ HeatParameterDefinition heatParamUpdate = new HeatParameterDefinition(parameter);
+ List<HeatParameterDefinition> heatParametersUpdated = new ArrayList<HeatParameterDefinition>();
+ heatParamUpdate.setCurrentValue("55");
+ heatParametersUpdated.add(heatParamUpdate);
+ atifactToUpdate.setHeatParameters(heatParametersUpdated);
+
+ Either<ArtifactDefinition, StorageOperationStatus> updateArifact = artifactOperation.updateArifactOnResource(
+ atifactToUpdate, RESOURCE_ID_2, atifactToUpdate.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertTrue(updateArifact.isLeft());
+
+ // verify old artifact and parameter still exist
+ ArtifactData artifactData = new ArtifactData(artifactWithHeat);
+ Either<ArtifactData, TitanOperationStatus> origArtifact = titanDao.getNode(artifactData.getUniqueIdKey(),
+ artifactData.getUniqueId(), ArtifactData.class);
+ assertTrue(origArtifact.isLeft());
+ ArtifactData origArtifactData = origArtifact.left().value();
+ assertTrue(artifactWithHeat.getArtifactVersion()
+ .equals(origArtifactData.getArtifactDataDefinition().getArtifactVersion()));
+
+ Either<HeatParameterData, TitanOperationStatus> parameterNodeAfterDelete = titanDao
+ .getNode(parameterData.getUniqueIdKey(), parameterData.getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNodeAfterDelete.isLeft());
+
+ // verify new artifact and new parameter
+ ArtifactDefinition artifactDefinitionUpdated = updateArifact.left().value();
+ ArtifactData artifactDataUpdated = new ArtifactData(artifactDefinitionUpdated);
+ Either<ArtifactData, TitanOperationStatus> updatedArtifact = titanDao
+ .getNode(artifactDataUpdated.getUniqueIdKey(), artifactDataUpdated.getUniqueId(), ArtifactData.class);
+ assertTrue(updatedArtifact.isLeft());
+ ArtifactData updatedArtifactData = updatedArtifact.left().value();
+ assertTrue(atifactToUpdate.getArtifactVersion()
+ .equals(updatedArtifactData.getArtifactDataDefinition().getArtifactVersion()));
+ assertFalse(
+ ((String) updatedArtifactData.getUniqueId()).equalsIgnoreCase((String) origArtifactData.getUniqueId()));
+
+ List<HeatParameterDefinition> heatParametersAfterUpdate = artifactDefinitionUpdated.getHeatParameters();
+ assertNotNull(heatParametersAfterUpdate);
+ assertTrue(heatParametersAfterUpdate.size() == 1);
+ HeatParameterDefinition UpdatedHeatParameter = heatParametersAfterUpdate.get(0);
+ assertFalse(UpdatedHeatParameter.getUniqueId().equalsIgnoreCase((String) parameterData.getUniqueId()));
+ Either<HeatParameterData, TitanOperationStatus> parameterNodeAfterUpdate = titanDao.getNode(
+ new HeatParameterData(UpdatedHeatParameter).getUniqueIdKey(), UpdatedHeatParameter.getUniqueId(),
+ HeatParameterData.class);
+ assertTrue(parameterNodeAfterUpdate.isLeft());
+
+ // delete new artifact
+ Either<ArtifactDefinition, StorageOperationStatus> removeArifact = artifactOperation.removeArifactFromResource(
+ RESOURCE_ID_2, artifactDefinitionUpdated.getUniqueId(), NodeTypeEnum.Resource, true, false);
+ assertTrue(removeArifact.isLeft());
+
+ // verify old artifact and parameter still exist
+ origArtifact = titanDao.getNode(artifactData.getUniqueIdKey(), artifactData.getUniqueId(), ArtifactData.class);
+ assertTrue(origArtifact.isLeft());
+ origArtifactData = origArtifact.left().value();
+ assertTrue(artifactWithHeat.getArtifactVersion()
+ .equals(origArtifactData.getArtifactDataDefinition().getArtifactVersion()));
+
+ parameterNodeAfterDelete = titanDao.getNode(parameterData.getUniqueIdKey(), parameterData.getUniqueId(),
+ HeatParameterData.class);
+ assertTrue(parameterNodeAfterDelete.isLeft());
+
+ // verify new artifact is deleted
+ Either<ArtifactData, TitanOperationStatus> artifactAfterDelete = titanDao
+ .getNode(artifactDataUpdated.getUniqueIdKey(), artifactDataUpdated.getUniqueId(), ArtifactData.class);
+ assertTrue(artifactAfterDelete.isRight());
+
+ parameterNodeAfterDelete = titanDao.getNode(new HeatParameterData(UpdatedHeatParameter).getUniqueIdKey(),
+ new HeatParameterData(UpdatedHeatParameter).getUniqueId(), HeatParameterData.class);
+ assertTrue(parameterNodeAfterDelete.isRight());
+
+ artifactOperation.removeArifactFromResource(RESOURCE_ID, artifactWithHeat.getUniqueId(), NodeTypeEnum.Resource,
+ true, false);
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID), ResourceMetadataData.class);
+ titanDao.deleteNode(new UniqueIdData(NodeTypeEnum.Resource, RESOURCE_ID_2), ResourceMetadataData.class);
+ }
+
+ private ArtifactDefinition createResourceWithHeat() {
+ ResourceMetadataData resource = createResource(RESOURCE_ID);
+ ArtifactDefinition artifactDefinition = createArtifactDefinition(USER_ID, RESOURCE_ID, ARTIFACT_NAME);
+ artifactDefinition.setArtifactType("HEAT");
+ artifactDefinition.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ HeatParameterDefinition heatParam = new HeatParameterDefinition();
+ heatParam.setCurrentValue("11");
+ heatParam.setDefaultValue("22");
+ heatParam.setDescription("desc");
+ heatParam.setName("myParam");
+ heatParam.setType("number");
+ heatParams.add(heatParam);
+ artifactDefinition.setHeatParameters(heatParams);
+
+ Either<ArtifactDefinition, StorageOperationStatus> artifact = artifactOperation
+ .addArifactToComponent(artifactDefinition, RESOURCE_ID, NodeTypeEnum.Resource, true, false);
+ assertTrue(artifact.isLeft());
+ ArtifactDefinition artifactWithHeat = artifact.left().value();
+ return artifactWithHeat;
+ }
+
+ private ArtifactDefinition addArtifactToService(String userId, String serviceId, String artifactName) {
+ ArtifactDefinition artifactInfo = createArtifactDefinition(userId, serviceId, artifactName);
+
+ Either<ArtifactDefinition, StorageOperationStatus> artifact = artifactOperation
+ .addArifactToComponent(artifactInfo, serviceId, NodeTypeEnum.Service, true, true);
+ assertTrue(artifact.isLeft());
+ return artifact.left().value();
+ }
+
+ private ArtifactDefinition createArtifactDefinition(String userId, String serviceId, String artifactName) {
+ ArtifactDefinition artifactInfo = new ArtifactDefinition();
+
+ artifactInfo.setArtifactName(artifactName + ".sh");
+ artifactInfo.setArtifactType("SHELL");
+ artifactInfo.setDescription("hdkfhskdfgh");
+ artifactInfo.setArtifactChecksum("UEsDBAoAAAAIAAeLb0bDQz");
+
+ artifactInfo.setUserIdCreator(userId);
+ String fullName = "Jim H";
+ artifactInfo.setUpdaterFullName(fullName);
+ long time = System.currentTimeMillis();
+ artifactInfo.setCreatorFullName(fullName);
+ artifactInfo.setCreationDate(time);
+ artifactInfo.setLastUpdateDate(time);
+ artifactInfo.setUserIdLastUpdater(userId);
+ artifactInfo.setArtifactLabel(artifactName);
+ artifactInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(serviceId, artifactInfo.getArtifactLabel()));
+ return artifactInfo;
+ }
+
+ public Service createService(String userId, CategoryDefinition category, String serviceName, String serviceVersion,
+ boolean isHighestVersion) {
+
+ Service service = buildServiceMetadata(userId, category, serviceName, serviceVersion);
+
+ service.setHighestVersion(isHighestVersion);
+
+ Either<Service, StorageOperationStatus> result = serviceOperation.createService(service, true);
+
+ log.info(result.toString());
+ assertTrue(result.isLeft());
+ Service resultService = result.left().value();
+
+ // assertEquals("check resource unique id",
+ // UniqueIdBuilder.buildServiceUniqueId(serviceName, serviceVersion),
+ // resultService.getUniqueId());
+ assertEquals("check resource state", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ resultService.getLifecycleState());
+
+ return resultService;
+ }
+
+ private Service buildServiceMetadata(String userId, CategoryDefinition category, String serviceName,
+ String serviceVersion) {
+
+ Service service = new Service();
+ service.setName(serviceName);
+ service.setVersion(serviceVersion);
+ service.setDescription("description 1");
+
+ service.setCreatorUserId(userId);
+ service.setContactId("contactId@sdc.com");
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ service.setCategories(categories);
+ service.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ service.setTags(tags);
+ return service;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateServiceCategory(category, titanDao);
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanDao);
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName, String role) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+ if (role != null && !role.isEmpty()) {
+ userData.setRole(role);
+ } else {
+ userData.setRole("ADMIN");
+ }
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+ public ResourceMetadataData createResource(String resourceName) {
+
+ ResourceMetadataData serviceData1 = new ResourceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(resourceName);
+ Either<ResourceMetadataData, TitanOperationStatus> createNode = titanDao.createNode(serviceData1,
+ ResourceMetadataData.class);
+
+ assertTrue("check resource created", createNode.isLeft());
+ return createNode.left().value();
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperationTest.java
new file mode 100644
index 0000000000..5b8420d5dc
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/CapabilityTypeOperationTest.java
@@ -0,0 +1,345 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.annotation.Resource;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+// @TestExecutionListeners(listeners = {
+// DependencyInjectionTestExecutionListener.class,
+// DirtiesContextTestExecutionListener.class,
+// TransactionalTestExecutionListener.class })
+public class CapabilityTypeOperationTest extends ModelTestBase {
+
+ @Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ // ExternalConfiguration.setAppName("catalog-model");
+ // String appConfigDir = "src/test/resources/config/catalog-model";
+ // ConfigurationSource configurationSource = new
+ // FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ // appConfigDir);
+
+ // configurationManager = new ConfigurationManager(
+ // new ConfigurationSource() {
+ //
+ // @Override
+ // public <T> T getAndWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T> void addWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ // });
+ //
+ // Configuration configuration = new Configuration();
+ // configuration.setTitanInMemoryGraph(true);
+ //
+ // configurationManager.setConfiguration(configuration);
+ ModelTestBase.init();
+
+ }
+
+ @Test
+ public void testDummy() {
+
+ assertTrue(capabilityTypeOperation != null);
+
+ }
+
+ @Test
+ public void testAddCapabilityType() {
+
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition();
+ capabilityTypeDefinition.setDescription("desc1");
+ capabilityTypeDefinition.setType("tosca.capabilities.Container1");
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType1 = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition, true);
+ assertEquals("check capability type added", true, addCapabilityType1.isLeft());
+
+ CapabilityTypeDefinition capabilityTypeAdded = addCapabilityType1.left().value();
+ compareBetweenCreatedToSent(capabilityTypeDefinition, capabilityTypeAdded);
+
+ Either<CapabilityTypeDefinition, TitanOperationStatus> capabilityTypeByUid = capabilityTypeOperation
+ .getCapabilityTypeByUid(capabilityTypeAdded.getUniqueId());
+ compareBetweenCreatedToSent(capabilityTypeByUid.left().value(), capabilityTypeDefinition);
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType2 = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition, true);
+ assertEquals("check capability type failed", true, addCapabilityType2.isRight());
+ assertEquals("check returned error", StorageOperationStatus.SCHEMA_VIOLATION,
+ addCapabilityType2.right().value());
+
+ }
+
+ @Test
+ public void testAddDerviedCapabilityType() {
+
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition();
+ capabilityTypeDefinition.setDescription("desc1");
+ capabilityTypeDefinition.setType("tosca.capabilities.Container2");
+ capabilityTypeDefinition.setDerivedFrom("derivedFrom");
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType1 = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition, true);
+ // assertEquals("check capability type parent not exist",
+ // StorageOperationStatus.INVALID_ID,
+ // addCapabilityType1.right().value());
+ // TODO: esofer change to INVALID_ID
+ assertEquals("check capability type parent not exist", StorageOperationStatus.INVALID_ID,
+ addCapabilityType1.right().value());
+ }
+
+ public CapabilityTypeDefinition createCapability(String capabilityTypeName) {
+
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition();
+ capabilityTypeDefinition.setDescription("desc1");
+ capabilityTypeDefinition.setType(capabilityTypeName);
+
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+
+ String propName1 = "disk_size";
+ String propName2 = "num_cpus";
+
+ PropertyDefinition property1 = buildProperty1();
+
+ properties.put(propName1, property1);
+
+ PropertyDefinition property2 = buildProperty2();
+
+ properties.put(propName2, property2);
+
+ capabilityTypeDefinition.setProperties(properties);
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType1 = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition, true);
+
+ CapabilityTypeDefinition capabilityTypeDefinitionCreated = addCapabilityType1.left().value();
+ Either<CapabilityTypeDefinition, StorageOperationStatus> capabilityType = capabilityTypeOperation
+ .getCapabilityType(capabilityTypeDefinitionCreated.getUniqueId(), true);
+ assertEquals("check capability type fetched", true, capabilityType.isLeft());
+ CapabilityTypeDefinition fetchedCTD = capabilityType.left().value();
+
+ Map<String, PropertyDefinition> fetchedProps = fetchedCTD.getProperties();
+
+ compareProperties(fetchedProps, properties);
+
+ return fetchedCTD;
+
+ }
+
+ @Test
+ public void testAddCapabilityTypeWithProperties() {
+
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition();
+ capabilityTypeDefinition.setDescription("desc1");
+ capabilityTypeDefinition.setType("tosca.capabilities.Container3");
+
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+
+ String propName1 = "disk_size";
+ String propName2 = "num_cpus";
+
+ PropertyDefinition property1 = buildProperty1();
+
+ properties.put(propName1, property1);
+
+ PropertyDefinition property2 = buildProperty2();
+
+ properties.put(propName2, property2);
+
+ capabilityTypeDefinition.setProperties(properties);
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType1 = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition, true);
+
+ CapabilityTypeDefinition capabilityTypeDefinitionCreated = addCapabilityType1.left().value();
+ Either<CapabilityTypeDefinition, StorageOperationStatus> capabilityType = capabilityTypeOperation
+ .getCapabilityType(capabilityTypeDefinitionCreated.getUniqueId());
+ assertEquals("check capability type fetched", true, capabilityType.isLeft());
+ CapabilityTypeDefinition fetchedCTD = capabilityType.left().value();
+
+ Map<String, PropertyDefinition> fetchedProps = fetchedCTD.getProperties();
+
+ compareProperties(fetchedProps, properties);
+ }
+
+ private void compareProperties(Map<String, PropertyDefinition> first, Map<String, PropertyDefinition> second) {
+
+ assertTrue("check properties are full or empty",
+ ((first == null && second == null) || (first != null && second != null)));
+ if (first != null) {
+ assertEquals("check properties size", first.size(), second.size());
+
+ for (Entry<String, PropertyDefinition> entry : first.entrySet()) {
+
+ String propName = entry.getKey();
+ PropertyDefinition secondPD = second.get(propName);
+ assertNotNull("Cannot find property " + propName + " in " + second, secondPD);
+
+ PropertyDefinition firstPD = entry.getValue();
+
+ comparePropertyDefinition(firstPD, secondPD);
+ }
+
+ }
+
+ }
+
+ @Test
+ public void testGetCapabilityTypeNotFound() {
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> capabilityType = capabilityTypeOperation
+ .getCapabilityType("not_exists");
+ assertEquals("check not found is returned", StorageOperationStatus.NOT_FOUND, capabilityType.right().value());
+
+ }
+
+ private void comparePropertyDefinition(PropertyDefinition first, PropertyDefinition second) {
+
+ assertTrue("check objects are full or empty",
+ ((first == null && second == null) || (first != null && second != null)));
+ if (first != null) {
+ assertTrue("check property default value", compareValue(first.getDefaultValue(), second.getDefaultValue()));
+ assertTrue("check property description", compareValue(first.getDescription(), second.getDescription()));
+ assertTrue("check property type", compareValue(first.getType(), second.getType()));
+ compareList(first.getConstraints(), second.getConstraints());
+ }
+
+ }
+
+ private void compareList(List<PropertyConstraint> first, List<PropertyConstraint> second) {
+
+ assertTrue("check lists are full or empty",
+ ((first == null && second == null) || (first != null && second != null)));
+ if (first != null) {
+ assertEquals("check list size", first.size(), second.size());
+ }
+ }
+
+ private PropertyDefinition buildProperty2() {
+ PropertyDefinition property2 = new PropertyDefinition();
+ property2.setDefaultValue("2");
+ property2.setDescription("Number of (actual or virtual) CPUs associated with the Compute node.");
+ property2.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints3 = new ArrayList<PropertyConstraint>();
+ List<String> range = new ArrayList<String>();
+ range.add("1");
+ range.add("4");
+
+ InRangeConstraint propertyConstraint3 = new InRangeConstraint(range);
+ constraints3.add(propertyConstraint3);
+ // property2.setConstraints(constraints3);
+ property2.setConstraints(constraints3);
+ return property2;
+ }
+
+ private PropertyDefinition buildProperty1() {
+ PropertyDefinition property1 = new PropertyDefinition();
+ property1.setDefaultValue("10");
+ property1.setDescription(
+ "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.");
+ property1.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints = new ArrayList<PropertyConstraint>();
+ GreaterThanConstraint propertyConstraint1 = new GreaterThanConstraint("0");
+ constraints.add(propertyConstraint1);
+
+ LessOrEqualConstraint propertyConstraint2 = new LessOrEqualConstraint("10");
+ constraints.add(propertyConstraint2);
+
+ property1.setConstraints(constraints);
+ return property1;
+ }
+
+ private void compareBetweenCreatedToSent(CapabilityTypeDefinition x, CapabilityTypeDefinition y) {
+
+ assertTrue(compareValue(x.getDerivedFrom(), y.getDerivedFrom()));
+ assertTrue(compareValue(x.getType(), y.getType()));
+ assertTrue(compareValue(x.getDescription(), y.getDescription()));
+
+ }
+
+ public boolean compareValue(String first, String second) {
+
+ if (first == null && second == null) {
+ return true;
+ }
+ if (first != null) {
+ return first.equals(second);
+ } else {
+ return false;
+ }
+ }
+
+ public void setOperations(TitanGenericDao titanDao, CapabilityTypeOperation capabilityTypeOperation) {
+ this.titanDao = titanDao;
+ this.capabilityTypeOperation = capabilityTypeOperation;
+
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationSpringTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationSpringTest.java
new file mode 100644
index 0000000000..2dcb1ee72e
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationSpringTest.java
@@ -0,0 +1,543 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+
+import org.apache.tinkerpop.gremlin.structure.io.IoCore;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.ComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.LifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ServiceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.operations.impl.util.ResourceCreationUtils;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.unittests.utils.FactoryUtils;
+import org.openecomp.sdc.be.unittests.utils.FactoryUtils.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class ComponentInstanceOperationSpringTest extends ModelTestBase {
+ private static Logger log = LoggerFactory.getLogger(ComponentInstanceOperationSpringTest.class.getName());
+ @Resource(name = "component-instance-operation")
+ private ComponentInstanceOperation componentInstanceOperation;
+
+ @Resource(name = "component-instance-operation")
+ private ComponentInstanceOperation resourceInstanceOperation;
+
+ @Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @Resource(name = "capability-operation")
+ public CapabilityOperation capabilityOperation;
+
+ @Resource(name = "service-operation")
+ private ServiceOperation serviceOperation;
+
+ @Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @Resource(name = "lifecycle-operation")
+ private LifecycleOperation lifecycleOperation;
+
+ TitanGenericDao titanGenericDao;
+
+ private static String CATEGORY_NAME = "category/mycategory";
+
+ User rfcUser;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ ModelTestBase.init();
+
+ }
+
+ @Before
+ public void cleanUp() {
+ titanGenericDao = componentInstanceOperation.titanGenericDao;
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ TitanGraph graph = graphResult.left().value();
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertex.remove();
+ }
+
+ }
+ titanGenericDao.commit();
+ deleteAndCreateCategory(CATEGORY_NAME);
+ UserData modifierData = deleteAndCreateUser(ResourceCreationUtils.MODIFIER_ATT_UID + "rfc",
+ ResourceCreationUtils.MODIFIER_FIRST_NAME, ResourceCreationUtils.MODIFIER_LAST_NAME, "ADMIN");
+ rfcUser = convertUserDataToUser(modifierData);
+ }
+
+ @Test
+ public void testAddCapabilityPropertyValuesToResourceInstance() {
+ String rootName = "Root123";
+ org.openecomp.sdc.be.model.Resource rootResource = createResource(rfcUser.getUserId(), CATEGORY_NAME, rootName,
+ "1.0", null, false, true);
+
+ // certification request
+ Either<? extends org.openecomp.sdc.be.model.Component, StorageOperationStatus> requestCertificationResult = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, rootResource, rfcUser, rfcUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ org.openecomp.sdc.be.model.Resource resultResource = (org.openecomp.sdc.be.model.Resource) requestCertificationResult
+ .left().value();
+
+ // start certification
+ Either<? extends org.openecomp.sdc.be.model.Component, StorageOperationStatus> startCertificationResult = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Resource, resultResource, rfcUser, rfcUser, false);
+ assertEquals(true, startCertificationResult.isLeft());
+
+ Either<? extends org.openecomp.sdc.be.model.Component, StorageOperationStatus> certifiedResourceRes = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Resource, rootResource, rfcUser, rfcUser, false);
+ assertTrue(certifiedResourceRes.isLeft());
+
+ CapabilityTypeDefinition capabilityType = buildCapabilityType();
+ Either<CapabilityTypeDefinition, StorageOperationStatus> capabilityTypeRes = capabilityTypeOperation
+ .addCapabilityType(capabilityType);
+ assertTrue(capabilityTypeRes.isLeft());
+
+ CapabilityData capData = FactoryUtils.createCapabilityData();
+ CapabilityDefinition capabilityDefinitionRoot = FactoryUtils
+ .convertCapabilityDataToCapabilityDefinitionRoot(capData);
+
+ Either<CapabilityDefinition, StorageOperationStatus> addCapabilityRootRes = capabilityOperation.addCapability(
+ (String) certifiedResourceRes.left().value().getUniqueId(), capabilityDefinitionRoot.getName(),
+ capabilityDefinitionRoot);
+ assertTrue(addCapabilityRootRes.isLeft());
+
+ String resourceName = "tosca.nodes.Apache.2.0";
+
+ CapabilityDefinition capabilityDefinition = FactoryUtils
+ .convertCapabilityDataToCapabilityDefinitionAddProperties(capData);
+ org.openecomp.sdc.be.model.Resource resource = createResource(rfcUser.getUserId(), CATEGORY_NAME, resourceName,
+ "0.1", rootName, false, true);
+
+ Either<CapabilityDefinition, StorageOperationStatus> addCapabilityRes = capabilityOperation
+ .addCapability((String) resource.getUniqueId(), capabilityDefinition.getName(), capabilityDefinition);
+ assertTrue(addCapabilityRes.isLeft());
+ List<ComponentInstanceProperty> properties = addCapabilityRes.left().value().getProperties();
+ assertTrue(properties.size() == 2);
+
+ Either<org.openecomp.sdc.be.model.Resource, StorageOperationStatus> clonedResourceRes = resourceOperation
+ .cloneComponent(resource, "0.2", false);
+ assertTrue(clonedResourceRes.isLeft());
+ org.openecomp.sdc.be.model.Resource clonedResource = clonedResourceRes.left().value();
+
+ ComponentInstance instance = buildResourceInstance(clonedResource.getUniqueId(), "1", "tosca.nodes.Apache");
+
+ Service origService = createService(rfcUser.getUserId(), CATEGORY_NAME, "my-service", "1.0", true);
+ Either<Service, StorageOperationStatus> service2 = serviceOperation.getService(origService.getUniqueId(),
+ false);
+ assertTrue(service2.isLeft());
+ origService = service2.left().value();
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+ String json = prettyGson.toJson(origService);
+ log.debug(json);
+
+ Service fullService = origService;
+
+ Either<ComponentInstance, TitanOperationStatus> status = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent((String) origService.getUniqueId(), NodeTypeEnum.Service, "1",
+ true, instance, NodeTypeEnum.Resource, false);
+ assertTrue(status.isLeft());
+
+ ComponentInstance resourceInstance = status.left().value();
+ CapabilityDefinition capability = addCapabilityRes.left().value();
+ capability.setName(capabilityDefinition.getName());
+ List<ComponentInstanceProperty> propertyValues = FactoryUtils.createComponentInstancePropertyList();
+ capability.setProperties(propertyValues);
+
+ Either<Map<CapabilityInstData, List<PropertyValueData>>, TitanOperationStatus> addCPVsToRiRes = componentInstanceOperation
+ .addCapabilityPropertyValuesToResourceInstance(resourceInstance.getUniqueId(), capability, true);
+ assertTrue(addCPVsToRiRes.isLeft());
+
+ Either<Service, StorageOperationStatus> createService = serviceOperation.cloneService(fullService, "2.0",
+ false);
+ assertTrue(createService.isLeft());
+ Map<String, List<CapabilityDefinition>> capabilitiesMap = createService.left().value().getCapabilities();
+ assertTrue(capabilitiesMap != null && capabilitiesMap.size() == 1);
+ Map<String, CapabilityDefinition> capabilities = capabilitiesMap.values().iterator().next().stream()
+ .collect(Collectors.toMap(CapabilityDefinition::getName, Function.identity()));
+ assertTrue(capabilities.containsKey("Cap1") && capabilities.containsKey("Cap2"));
+
+ // String outputFile = exportGraphMl();
+
+ }
+
+ public String exportGraphMl() {
+ String result = null;
+ String outputFile = "C:\\Output" + File.separator + "exportGraph." + System.currentTimeMillis() + ".graphml";
+ TitanGraph graph = titanGenericDao.getGraph().left().value();
+ try {
+ try (final OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile))) {
+ graph.io(IoCore.graphml()).writer().normalize(true).create().writeGraph(os, graph);
+ }
+ result = outputFile;
+ graph.tx().commit();
+ } catch (Exception e) {
+ graph.tx().rollback();
+ e.printStackTrace();
+ }
+ return result;
+
+ }
+
+ private CapabilityTypeDefinition buildCapabilityType() {
+ CapabilityTypeDefinition capabilityType = new CapabilityTypeDefinition();
+ Map<String, PropertyDefinition> properties = new HashMap();
+
+ capabilityType.setType(Constants.DEFAULT_CAPABILITY_TYPE);
+ capabilityType.setProperties(properties);
+
+ PropertyDefinition host = new PropertyDefinition();
+ host.setUniqueId(UUID.randomUUID().toString());
+ host.setName("host");
+ host.setDefaultValue("captypehost");
+ host.setType("string");
+
+ host.setSchema(new SchemaDefinition());
+ host.getSchema().setProperty(new PropertyDataDefinition());
+ host.getSchema().getProperty().setType("string");
+
+ PropertyDefinition port = new PropertyDefinition();
+ port.setName("port");
+ port.setDefaultValue("captypeport");
+ port.setUniqueId(UUID.randomUUID().toString());
+ port.setType("string");
+
+ port.setSchema(new SchemaDefinition());
+ port.getSchema().setProperty(new PropertyDataDefinition());
+ port.getSchema().getProperty().setType("string");
+
+ PropertyDefinition rootproperty = new PropertyDefinition();
+ rootproperty.setName("captypeproperty");
+ rootproperty.setDefaultValue("captypevalue");
+ rootproperty.setUniqueId(UUID.randomUUID().toString());
+ rootproperty.setType("string");
+
+ rootproperty.setSchema(new SchemaDefinition());
+ rootproperty.getSchema().setProperty(new PropertyDataDefinition());
+ rootproperty.getSchema().getProperty().setType("string");
+
+ properties.put("host", host);
+ properties.put("port", port);
+ properties.put("captypeproperty", rootproperty);
+ return capabilityType;
+ }
+
+ private CapabilityInstData buildCapabilityInstanceData(String resourceInstanceId, CapabilityDefinition capability) {
+ CapabilityInstData capabilityInstance = new CapabilityInstData();
+ Long creationTime = System.currentTimeMillis();
+ String uniqueId = UniqueIdBuilder.buildCapabilityInstanceUid(resourceInstanceId, capability.getName());
+ capabilityInstance.setCreationTime(creationTime);
+ capabilityInstance.setModificationTime(creationTime);
+ capabilityInstance.setUniqueId(uniqueId);
+ return capabilityInstance;
+ }
+
+ private ComponentInstance buildResourceInstance(String respurceUid, String instanceNumber, String name) {
+ ComponentInstance resourceInstance = new ComponentInstance();
+ resourceInstance.setName(name);
+ resourceInstance.setDescription("desc1");
+ resourceInstance.setPosX("20");
+ resourceInstance.setPosY("40");
+ resourceInstance.setComponentUid(respurceUid);
+ resourceInstance.setCreationTime(System.currentTimeMillis());
+ resourceInstance.setModificationTime(System.currentTimeMillis());
+ resourceInstance.setNormalizedName(ResourceInstanceOperationTest.normaliseComponentInstanceName(name));
+ return resourceInstance;
+ }
+
+ public ResourceMetadataData createResource(String resourceName, TitanGenericDao titanGenericDao) {
+ ResourceMetadataData serviceData1 = new ResourceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(resourceName);
+ Either<ResourceMetadataData, TitanOperationStatus> createNode = titanGenericDao.createNode(serviceData1,
+ ResourceMetadataData.class);
+ assertTrue("check service created", createNode.isLeft());
+ return createNode.left().value();
+ }
+
+ public ServiceMetadataData createServiceMetadataData(String serviceName, TitanGenericDao titanGenericDao) {
+ ServiceMetadataData serviceData1 = new ServiceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(serviceName);
+ Either<ServiceMetadataData, TitanOperationStatus> createNode = titanGenericDao.createNode(serviceData1,
+ ServiceMetadataData.class);
+ assertTrue("check service created", createNode.isLeft());
+ return createNode.left().value();
+ }
+
+ public Service createService(String userId, String category, String serviceName, String serviceVersion,
+ boolean isHighestVersion) {
+ Service service = buildServiceMetadata(userId, category, serviceName, serviceVersion);
+ service.setHighestVersion(isHighestVersion);
+ Either<Service, StorageOperationStatus> result = serviceOperation.createService(service, true);
+ assertTrue(result.isLeft());
+ Service resultService = result.left().value();
+ assertEquals("check resource state", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ resultService.getLifecycleState());
+ return resultService;
+ }
+
+ private Service buildServiceMetadata(String userId, String category, String serviceName, String serviceVersion) {
+ Service service = new Service();
+ service.setName(serviceName);
+ service.setVersion(serviceVersion);
+ service.setDescription("description 1");
+ service.setCreatorUserId(userId);
+ service.setContactId("contactId@sdc.com");
+ CategoryDefinition categoryDef = new CategoryDefinition();
+ categoryDef.setName(category);
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(categoryDef);
+ service.setCategories(categories);
+ service.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ service.setTags(tags);
+ return service;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateServiceCategory(category, titanGenericDao);
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanGenericDao);
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName, String role) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+ if (role != null && !role.isEmpty()) {
+ userData.setRole(role);
+ } else {
+ userData.setRole("ADMIN");
+ }
+ titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanGenericDao.createNode(userData, UserData.class);
+ titanGenericDao.commit();
+ return userData;
+ }
+
+ public org.openecomp.sdc.be.model.Resource createResource(String userId, String category, String resourceName,
+ String resourceVersion, String parentResourceName, boolean isAbstract, boolean isHighestVersion) {
+
+ String propName1 = "disk_size";
+ String propName2 = "num_cpus";
+
+ List<String> derivedFrom = new ArrayList<String>();
+ if (parentResourceName != null) {
+ derivedFrom.add(parentResourceName);
+ }
+ org.openecomp.sdc.be.model.Resource resource = buildResourceMetadata(userId, category, resourceName,
+ resourceVersion);
+
+ resource.setAbstract(isAbstract);
+ resource.setHighestVersion(isHighestVersion);
+
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+
+ PropertyDefinition property1 = new PropertyDefinition();
+ property1.setDefaultValue("10");
+ property1.setDescription(
+ "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.");
+ property1.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints = new ArrayList<PropertyConstraint>();
+ GreaterThanConstraint propertyConstraint1 = new GreaterThanConstraint("0");
+ log.debug("{}", propertyConstraint1);
+
+ constraints.add(propertyConstraint1);
+
+ LessOrEqualConstraint propertyConstraint2 = new LessOrEqualConstraint("10");
+ constraints.add(propertyConstraint2);
+
+ property1.setConstraints(constraints);
+
+ properties.put(propName1, property1);
+
+ PropertyDefinition property2 = new PropertyDefinition();
+ property2.setDefaultValue("2");
+ property2.setDescription("Number of (actual or virtual) CPUs associated with the Compute node.");
+ property2.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints3 = new ArrayList<PropertyConstraint>();
+ List<String> range = new ArrayList<String>();
+ range.add("1");
+ range.add("4");
+
+ InRangeConstraint propertyConstraint3 = new InRangeConstraint(range);
+ constraints3.add(propertyConstraint3);
+ property2.setConstraints(constraints3);
+ properties.put(propName2, property2);
+
+ resource.setDerivedFrom(derivedFrom);
+
+ resource.setProperties(convertMapToList(properties));
+
+ Either<org.openecomp.sdc.be.model.Resource, StorageOperationStatus> result = resourceOperation
+ .createResource(resource, true);
+
+ assertTrue(result.isLeft());
+ org.openecomp.sdc.be.model.Resource resultResource = result.left().value();
+ assertEquals("check resource state", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ resultResource.getLifecycleState());
+
+ String resourceId = resultResource.getUniqueId();
+
+ Either<PropertyDefinition, StorageOperationStatus> either = propertyOperation.getPropertyOfResource(propName1,
+ resourceId);
+
+ assertTrue(either.isLeft());
+ PropertyDefinition propertyDefinition = either.left().value();
+ assertEquals("check property default value", property1.getDefaultValue(), propertyDefinition.getDefaultValue());
+ assertEquals("check property description", property1.getDescription(), propertyDefinition.getDescription());
+ assertEquals("check property type", property1.getType(), propertyDefinition.getType());
+ assertEquals("check property unique id", property1.getUniqueId(), propertyDefinition.getUniqueId());
+ assertEquals("check property consitraints size", property1.getConstraints().size(),
+ propertyDefinition.getConstraints().size());
+
+ return resultResource;
+ }
+
+ private org.openecomp.sdc.be.model.Resource buildResourceMetadata(String userId, String category,
+ String resourceName, String resourceVersion) {
+
+ org.openecomp.sdc.be.model.Resource resource = new org.openecomp.sdc.be.model.Resource();
+ resource.setName(resourceName);
+ resource.setVersion(resourceVersion);
+ ;
+ resource.setDescription("description 1");
+ resource.setAbstract(false);
+ resource.setCreatorUserId(userId);
+ resource.setContactId("contactId@sdc.com");
+ resource.setVendorName("vendor 1");
+ resource.setVendorRelease("1.0.0");
+ resource.setToscaResourceName(resourceName);
+ String[] categoryArr = category.split("/");
+ resource.addCategory(categoryArr[0], categoryArr[1]);
+ resource.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ resource.setTags(tags);
+ return resource;
+ }
+
+ public static List<PropertyDefinition> convertMapToList(Map<String, PropertyDefinition> properties) {
+ if (properties == null) {
+ return null;
+ }
+
+ List<PropertyDefinition> definitions = new ArrayList<>();
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+ String name = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ propertyDefinition.setName(name);
+ definitions.add(propertyDefinition);
+ }
+
+ return definitions;
+ }
+
+ private User convertUserDataToUser(UserData modifierData) {
+ User modifier = new User();
+ modifier.setUserId(modifierData.getUserId());
+ modifier.setEmail(modifierData.getEmail());
+ modifier.setFirstName(modifierData.getFirstName());
+ modifier.setLastName(modifierData.getLastName());
+ modifier.setRole(modifierData.getRole());
+ return modifier;
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationTest.java
new file mode 100644
index 0000000000..e77c9f0291
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentInstanceOperationTest.java
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.operations.impl.ComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.unittests.utils.FactoryUtils;
+
+import fj.data.Either;
+
+public class ComponentInstanceOperationTest {
+
+ @InjectMocks
+ ComponentInstanceOperation componentInstanceOperation = new ComponentInstanceOperation();
+ @InjectMocks
+ private TitanGenericDao titanGenericDao = Mockito.mock(TitanGenericDao.class);
+
+ @Before
+ public void beforeTest() {
+ Mockito.reset(titanGenericDao);
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testGetCapabilities() {
+
+ ComponentInstance ri = FactoryUtils.createResourceInstance();
+ CapabilityData capData = FactoryUtils.createCapabilityData();
+ Either<List<ImmutablePair<GraphNode, GraphEdge>>, TitanOperationStatus> childNodesReturned = prepareChildNodeRetValue(
+ capData);
+
+ Mockito.when(titanGenericDao.getChildrenNodes(Mockito.anyString(), Mockito.anyString(),
+ Mockito.any(GraphEdgeLabels.class), Mockito.any(NodeTypeEnum.class), Mockito.any()))
+ .thenReturn(childNodesReturned);
+
+ // ImmutablePair<ComponentInstance, List<ImmutablePair<CapabilityData,
+ // GraphEdge>>> instanceAndCapabilities =
+ // componentInstanceOperation.getCapabilities(ri,
+ // NodeTypeEnum.Resource);
+
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> instanceAndCapabilities = componentInstanceOperation
+ .getCapabilities(ri, NodeTypeEnum.Resource);
+
+ // assertTrue(instanceAndCapabilities.left.getUniqueId().equals(ri.getUniqueId()));
+ assertTrue(instanceAndCapabilities.left().value().size() == 1);
+ assertTrue(instanceAndCapabilities.left().value().get(0).left.getUniqueId().equals(capData.getUniqueId()));
+
+ }
+
+ @Test
+ public void testGetRequirements() {
+ ComponentInstance ri = FactoryUtils.createResourceInstance();
+ RequirementData reqData = FactoryUtils.createRequirementData();
+ Either<List<ImmutablePair<GraphNode, GraphEdge>>, TitanOperationStatus> childNodesReturned = prepareChildNodeRetValue(
+ reqData);
+
+ Mockito.when(titanGenericDao.getChildrenNodes(Mockito.anyString(), Mockito.anyString(),
+ Mockito.any(GraphEdgeLabels.class), Mockito.any(NodeTypeEnum.class), Mockito.any()))
+ .thenReturn(childNodesReturned);
+
+ // ImmutablePair<ComponentInstance, List<ImmutablePair<RequirementData,
+ // GraphEdge>>> instanceAndCapabilities =
+ // componentInstanceOperation.getRequirements(ri,
+ // NodeTypeEnum.Resource);
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> instanceAndCapabilities = componentInstanceOperation
+ .getRequirements(ri, NodeTypeEnum.Resource);
+
+ // assertTrue(instanceAndCapabilities.left.getUniqueId().equals(ri.getUniqueId()));
+ // assertTrue(instanceAndCapabilities.right.size() == 1);
+ // assertTrue(instanceAndCapabilities.right.get(0).left.getUniqueId().equals(reqData.getUniqueId()));
+
+ assertTrue(instanceAndCapabilities.left().value().size() == 1);
+ assertTrue(instanceAndCapabilities.left().value().get(0).left.getUniqueId().equals(reqData.getUniqueId()));
+
+ }
+
+ private CapabilityInstData buildCapabilityInstanceData(String resourceInstanceId, CapabilityDefinition capability) {
+ CapabilityInstData capabilityInstance = new CapabilityInstData();
+ Long creationTime = System.currentTimeMillis();
+ String uniqueId = UniqueIdBuilder.buildCapabilityInstanceUid(resourceInstanceId, capability.getName());
+
+ capabilityInstance.setCreationTime(creationTime);
+ capabilityInstance.setModificationTime(creationTime);
+ capabilityInstance.setUniqueId(uniqueId);
+
+ return capabilityInstance;
+ }
+
+ private Either<List<ImmutablePair<GraphNode, GraphEdge>>, TitanOperationStatus> prepareChildNodeRetValue(
+ GraphNode data) {
+ ImmutablePair<GraphNode, GraphEdge> pair = new ImmutablePair<>(data, FactoryUtils.createGraphEdge());
+ List<ImmutablePair<GraphNode, GraphEdge>> retList = new ArrayList<>();
+ retList.add(pair);
+ return Either.left(retList);
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperationTest.java
new file mode 100644
index 0000000000..14018d31f9
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ComponentOperationTest.java
@@ -0,0 +1,395 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+
+import com.thinkaurelius.titan.core.TitanTransaction;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.ComponentParametersView;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.ICapabilityOperation;
+import org.openecomp.sdc.be.model.operations.api.IRequirementOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.ComponentOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.CapabilityInstData;
+import org.openecomp.sdc.be.resources.data.ComponentMetadataData;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.PropertyValueData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.unittests.utils.FactoryUtils;
+
+import fj.data.Either;
+
+public class ComponentOperationTest {
+ @InjectMocks
+ ComponentOperation compOperation = getAnnonimusImpl();
+
+ ComponentInstanceOperation componentInstanceOperation = Mockito.mock(ComponentInstanceOperation.class);
+ TitanGenericDao titanGenericDao = Mockito.mock(TitanGenericDao.class);
+ ICapabilityOperation capabilityOperation = Mockito.mock(ICapabilityOperation.class);
+ IRequirementOperation requirementOperation = Mockito.mock(IRequirementOperation.class);
+
+ @Before
+ public void beforeTest() {
+ Mockito.reset(componentInstanceOperation, requirementOperation, capabilityOperation);
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testGetCapabilities() {
+ Resource vf = FactoryUtils.createVF();
+ ComponentInstance ri = FactoryUtils.createResourceInstance();
+ CapabilityData capData = FactoryUtils.createCapabilityData();
+
+ FactoryUtils.addComponentInstanceToVF(vf, ri);
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> capDataList = prepareCompOperationReturnValue(
+ ri, capData);
+
+ prepareMocksForCapabilitiesMethods(ri, capDataList);
+
+ Map<String, List<CapabilityDefinition>> capabilities = compOperation
+ .getCapabilities(vf, NodeTypeEnum.Resource, false).left().value();
+ assertTrue(capabilities.size() == 1);
+ Entry<String, List<CapabilityDefinition>> entry = capabilities.entrySet().iterator().next();
+ assertTrue(entry.getKey().equals(capData.getType()));
+ assertTrue(entry.getValue().size() == 1);
+ assertTrue(entry.getValue().get(0).getUniqueId().equals(capData.getUniqueId()));
+ }
+
+ @Test
+ public void testGetRequirments() {
+ Resource vf = FactoryUtils.createVF();
+ ComponentInstance ri = FactoryUtils.createResourceInstance();
+
+ RequirementData reqData = FactoryUtils.createRequirementData();
+
+ FactoryUtils.addComponentInstanceToVF(vf, ri);
+
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> reqDataEdgeList = prepareCompOperationReturnValue(
+ ri, reqData);
+
+ prepareMocksForRequirmenetsMethods(ri, reqDataEdgeList);
+
+ Map<String, List<RequirementDefinition>> requirements = compOperation
+ .getRequirements(vf, NodeTypeEnum.Resource, false).left().value();
+ assertTrue(requirements.size() == 1);
+ Entry<String, List<RequirementDefinition>> entry = requirements.entrySet().iterator().next();
+ assertTrue(entry.getKey().equals(FactoryUtils.Constants.DEFAULT_CAPABILITY_TYPE));
+ assertTrue(entry.getValue().size() == 1);
+ assertTrue(entry.getValue().get(0).getUniqueId().equals(reqData.getUniqueId()));
+ }
+
+ private void prepareMocksForRequirmenetsMethods(ComponentInstance ri,
+ Either<List<ImmutablePair<RequirementData, GraphEdge>>, TitanOperationStatus> reqDataEdgeList) {
+
+ when(componentInstanceOperation.getRequirements(ri, NodeTypeEnum.Resource)).thenReturn(reqDataEdgeList);
+ when(requirementOperation.getRequirement(Mockito.anyString())).then(createReqDefAnswer());
+ }
+
+ private void prepareMocksForCapabilitiesMethods(ComponentInstance ri,
+ Either<List<ImmutablePair<CapabilityData, GraphEdge>>, TitanOperationStatus> capDataList) {
+ when(componentInstanceOperation.getCapabilities(ri, NodeTypeEnum.Resource)).thenReturn(capDataList);
+ when(capabilityOperation.getCapabilityByCapabilityData(Mockito.any(CapabilityData.class)))
+ .then(createCapDefByDataAnswer());
+ List<ImmutablePair<CapabilityInstData, GraphEdge>> capInstList = new ArrayList<>();
+ CapabilityInstData curCapabilityInst = FactoryUtils.createCapabilityInstData();
+ GraphEdge edge = new GraphEdge();
+ Map<String, Object> properties = new HashMap<>();
+ properties.put(GraphPropertiesDictionary.CAPABILITY_ID.getProperty(),
+ capDataList.left().value().get(0).getLeft().getUniqueId());
+ edge.setProperties(properties);
+ ImmutablePair<CapabilityInstData, GraphEdge> pair = new ImmutablePair<CapabilityInstData, GraphEdge>(
+ curCapabilityInst, edge);
+ capInstList.add(pair);
+ when(titanGenericDao.getChildrenNodes(
+ UniqueIdBuilder.getKeyByNodeType(
+ NodeTypeEnum.getByNameIgnoreCase(ri.getOriginType().getInstanceType().trim())),
+ ri.getUniqueId(), GraphEdgeLabels.CAPABILITY_INST, NodeTypeEnum.CapabilityInst,
+ CapabilityInstData.class)).thenReturn(Either.left(capInstList));
+
+ when(titanGenericDao.getChild(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.getByName(curCapabilityInst.getLabel())),
+ curCapabilityInst.getUniqueId(), GraphEdgeLabels.INSTANCE_OF, NodeTypeEnum.Capability,
+ CapabilityData.class)).thenReturn(Either.left(capDataList.left().value().get(0)));
+
+ PropertyValueData propertyValueData = FactoryUtils.createPropertyData();
+ ImmutablePair<PropertyValueData, GraphEdge> propPair = new ImmutablePair<PropertyValueData, GraphEdge>(
+ propertyValueData, null);
+ List<ImmutablePair<PropertyValueData, GraphEdge>> propPairList = new ArrayList<>();
+ propPairList.add(propPair);
+ when(titanGenericDao.getChildrenNodes(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.getByName(curCapabilityInst.getLabel())),
+ curCapabilityInst.getUniqueId(), GraphEdgeLabels.PROPERTY_VALUE, NodeTypeEnum.PropertyValue,
+ PropertyValueData.class)).thenReturn(Either.left(propPairList));
+
+ CapabilityDefinition capDef = FactoryUtils
+ .convertCapabilityDataToCapabilityDefinitionAddProperties(capDataList.left().value().get(0).getLeft());
+ List<PropertyDefinition> propDefList = capDef.getProperties().stream().filter(p -> p.getName().equals("host"))
+ .collect(Collectors.toList());
+ PropertyDefinition propDef = propDefList.get(0);
+ PropertyData propData = FactoryUtils.convertCapabilityDefinitionToCapabilityData(propDef);
+
+ ImmutablePair<PropertyData, GraphEdge> defPropPair = new ImmutablePair<PropertyData, GraphEdge>(propData, edge);
+
+ when(titanGenericDao.getChild(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.getByName(propertyValueData.getLabel())),
+ propertyValueData.getUniqueId(), GraphEdgeLabels.PROPERTY_IMPL, NodeTypeEnum.Property,
+ PropertyData.class)).thenReturn(Either.left(defPropPair));
+ List<CapabilityDefinition> capDefList = new ArrayList<>();
+ capDefList.add(capDef);
+ when(componentInstanceOperation.updateCapDefPropertyValues(Mockito.any(ComponentInstance.class),
+ Mockito.any(List.class))).thenReturn(Either.left(capDefList));
+ }
+
+ private <Data> Either<List<ImmutablePair<Data, GraphEdge>>, TitanOperationStatus> prepareCompOperationReturnValue(
+ ComponentInstance ri, Data data) {
+ ImmutablePair<Data, GraphEdge> dataEdgePair = new ImmutablePair<>(data, new GraphEdge());
+ List<ImmutablePair<Data, GraphEdge>> dataEdgeList = new ArrayList<>();
+ dataEdgeList.add(dataEdgePair);
+ return Either.left(dataEdgeList);
+ }
+
+ private Answer<Either<RequirementDefinition, TitanOperationStatus>> createReqDefAnswer() {
+ return new Answer<Either<RequirementDefinition, TitanOperationStatus>>() {
+
+ @Override
+ public Either<RequirementDefinition, TitanOperationStatus> answer(InvocationOnMock invocation)
+ throws Throwable {
+ String reqDataId = (String) invocation.getArguments()[0];
+ return Either.left(FactoryUtils.convertRequirementDataIDToRequirementDefinition(reqDataId));
+ }
+ };
+ }
+
+ private Answer<Either<CapabilityDefinition, TitanOperationStatus>> createCapDefByDataAnswer() {
+ return new Answer<Either<CapabilityDefinition, TitanOperationStatus>>() {
+
+ @Override
+ public Either<CapabilityDefinition, TitanOperationStatus> answer(InvocationOnMock invocation)
+ throws Throwable {
+ CapabilityData capData = (CapabilityData) invocation.getArguments()[0];
+ return Either.left(FactoryUtils.convertCapabilityDataToCapabilityDefinitionAddProperties(capData));
+ }
+ };
+ }
+
+ private ComponentOperation getAnnonimusImpl() {
+ return new ComponentOperation() {
+
+ @Override
+ protected StorageOperationStatus validateCategories(Component currentComponent, Component component,
+ ComponentMetadataData componentData, NodeTypeEnum type) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected <T extends Component> StorageOperationStatus updateDerived(Component component,
+ Component currentComponent, ComponentMetadataData updatedResourceData, Class<T> clazz) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> updateComponent(T component, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Integer, StorageOperationStatus> increaseAndGetComponentInstanceCounter(String componentId,
+ boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected ComponentMetadataData getMetaDataFromComponent(Component component) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> getLightComponent(String id, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> getComponentByNameAndVersion(String name, String version,
+ Map<String, Object> additionalParams, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ // @Override
+ // public <T> Either<T, StorageOperationStatus>
+ // getComponent_tx(String id, boolean inTransaction) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+
+ @Override
+ public Either<List<ArtifactDefinition>, StorageOperationStatus> getAdditionalArtifacts(String resourceId,
+ boolean recursively, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version,
+ boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Component getDefaultComponent() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean isComponentExist(String componentId) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> getMetadataComponent(String id, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ Component convertComponentMetadataDataToComponent(ComponentMetadataData componentMetadataData) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ TitanOperationStatus setComponentCategoriesFromGraph(Component component) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> validateComponentNameExists(String componentName) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> markComponentToDelete(Component componentToDelete,
+ boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Component, StorageOperationStatus> deleteComponent(String id, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<Boolean, StorageOperationStatus> isComponentInUse(String componentId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Either<List<String>, StorageOperationStatus> getAllComponentsMarkedForDeletion() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> cloneComponent(T other, String version,
+ LifecycleStateEnum targetLifecycle, boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> Either<T, StorageOperationStatus> getComponent(String id,
+ ComponentParametersView componentParametersView, boolean inTrasnaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T> Either<List<T>, StorageOperationStatus> getFilteredComponents(Map<FilterKeyEnum, String> filters,
+ boolean inTransaction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ protected <T> Either<T, StorageOperationStatus> updateComponentFilterResult(T component,
+ boolean inTransaction, ComponentParametersView filterParametersView) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ };
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java
new file mode 100644
index 0000000000..a529074db6
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ElementOperationTest.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactType;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.impl.ElementOperation;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class ElementOperationTest extends ModelTestBase {
+
+ @javax.annotation.Resource(name = "element-operation")
+ private ElementOperation elementOperation;
+
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ private static String CATEGORY = "category";
+ private static String SUBCATEGORY = "subcategory";
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ // ExternalConfiguration.setAppName("catalog-model");
+ // String appConfigDir = "src/test/resources/config/catalog-model";
+ // ConfigurationSource configurationSource = new
+ // FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ // appConfigDir);
+
+ ModelTestBase.init();
+
+ }
+
+ @Test
+ public void testGetArtifactsTypes() {
+
+ List<String> artifactTypesCfg = new ArrayList<String>();
+ artifactTypesCfg.add("type1");
+ artifactTypesCfg.add("type2");
+ artifactTypesCfg.add("type3");
+ artifactTypesCfg.add("type4");
+ configurationManager.getConfiguration().setArtifactTypes(artifactTypesCfg);
+ Either<List<ArtifactType>, ActionStatus> allArtifactTypes = elementOperation.getAllArtifactTypes();
+ assertTrue(allArtifactTypes.isLeft());
+ assertEquals(artifactTypesCfg.size(), allArtifactTypes.left().value().size());
+
+ artifactTypesCfg.remove(0);
+ allArtifactTypes = elementOperation.getAllArtifactTypes();
+ assertTrue(allArtifactTypes.isLeft());
+ assertEquals(artifactTypesCfg.size(), allArtifactTypes.left().value().size());
+
+ artifactTypesCfg.add("type5");
+ }
+
+ // @Test
+ public void testGetResourceAndServiceCategoty() {
+ String id = OperationTestsUtil.deleteAndCreateResourceCategory(CATEGORY, SUBCATEGORY, titanDao);
+
+ Either<CategoryDefinition, ActionStatus> res = elementOperation.getCategory(NodeTypeEnum.ResourceNewCategory,
+ id);
+ assertTrue(res.isLeft());
+ CategoryDefinition categoryDefinition = (CategoryDefinition) res.left().value();
+ assertEquals(CATEGORY, categoryDefinition.getName());
+ assertEquals(SUBCATEGORY, categoryDefinition.getSubcategories().get(0).getName());
+
+ id = OperationTestsUtil.deleteAndCreateServiceCategory(CATEGORY, titanDao);
+
+ res = elementOperation.getCategory(NodeTypeEnum.ServiceNewCategory, id);
+ assertTrue(res.isLeft());
+ categoryDefinition = (CategoryDefinition) res.left().value();
+ assertEquals(CATEGORY, categoryDefinition.getName());
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperationTest.java
new file mode 100644
index 0000000000..6765557bab
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/HeatParametersOperationTest.java
@@ -0,0 +1,289 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.heat.HeatParameterType;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.HeatParametersOperation;
+import org.openecomp.sdc.be.resources.data.HeatParameterData;
+import org.openecomp.sdc.be.resources.data.HeatParameterValueData;
+
+import fj.data.Either;
+
+public class HeatParametersOperationTest extends ModelTestBase {
+
+ HeatParametersOperation heatParametersOperation = new HeatParametersOperation();
+
+ TitanGenericDao titanGenericDao = Mockito.mock(TitanGenericDao.class);
+
+ @Before
+ public void setup() {
+ heatParametersOperation.setTitanGenericDao(titanGenericDao);
+
+ }
+
+ @Test
+ public void addPropertyToResourceTest() {
+
+ String propName = "myProp";
+ HeatParameterDefinition property = buildHeatPropertyDefinition();
+
+ HeatParameterData propertyData = new HeatParameterData(property);
+
+ Either<HeatParameterData, TitanOperationStatus> either = Either.left(propertyData);
+
+ GraphRelation graphRelation = new GraphRelation();
+ Either<GraphRelation, TitanOperationStatus> relationResult = Either.left(graphRelation);
+
+ when(titanGenericDao.createNode((HeatParameterData) anyObject(), eq(HeatParameterData.class)))
+ .thenReturn(either);
+ when(titanGenericDao.createRelation((GraphNode) anyObject(), (GraphNode) anyObject(),
+ eq(GraphEdgeLabels.HEAT_PARAMETER), anyMap())).thenReturn(relationResult);
+
+ Either<HeatParameterData, TitanOperationStatus> result = heatParametersOperation.addPropertyToGraph(propName,
+ property, "resourceId.artifactId", NodeTypeEnum.ArtifactRef);
+
+ assertTrue(result.isLeft());
+
+ }
+
+ @Test
+ public void addPropertyListToResourceTest() {
+
+ HeatParameterDefinition property = buildHeatPropertyDefinition();
+ HeatParameterDefinition property2 = buildHeatPropertyDefinition();
+ property2.setName("p2");
+
+ List<HeatParameterDefinition> parameters = new ArrayList<HeatParameterDefinition>();
+ parameters.add(property);
+ parameters.add(property2);
+
+ HeatParameterData propertyData = new HeatParameterData(property);
+
+ Either<HeatParameterData, TitanOperationStatus> either = Either.left(propertyData);
+
+ GraphRelation graphRelation = new GraphRelation();
+ Either<GraphRelation, TitanOperationStatus> relationResult = Either.left(graphRelation);
+
+ when(titanGenericDao.createNode((HeatParameterData) anyObject(), eq(HeatParameterData.class)))
+ .thenReturn(either);
+ when(titanGenericDao.createRelation((GraphNode) anyObject(), (GraphNode) anyObject(),
+ eq(GraphEdgeLabels.HEAT_PARAMETER), anyMap())).thenReturn(relationResult);
+
+ StorageOperationStatus result = heatParametersOperation.addPropertiesToGraph(parameters,
+ "resourceId.artifactId", NodeTypeEnum.ArtifactRef);
+
+ assertEquals(StorageOperationStatus.OK, result);
+
+ }
+
+ @Test
+ public void testStringValues() {
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.STRING, "50aaa"));
+ }
+
+ @Test
+ public void testNumberValues() {
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.NUMBER, "50"));
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.NUMBER, "50.5"));
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.NUMBER, "0x11"));
+
+ assertFalse(heatParametersOperation.isValidValue(HeatParameterType.NUMBER, "aaa"));
+ assertFalse(heatParametersOperation.isValidValue(HeatParameterType.NUMBER, "?>!"));
+ }
+
+ @Test
+ public void testJsonValues() {
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.JSON, "{ \"member\" : \"50\"}"));
+ HeatParameterDefinition propertyDefinition = buildHeatBooleanPropertyDefinition(
+ HeatParameterType.JSON.getType(), "{ \"member\" : \"50\"}");
+ StorageOperationStatus operationStatus = heatParametersOperation.validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals(HeatParameterType.JSON.getType(), propertyDefinition.getType());
+
+ }
+
+ @Test
+ public void testListValues() {
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.COMMA_DELIMITED_LIST, "one, two"));
+ HeatParameterDefinition propertyDefinition = buildHeatBooleanPropertyDefinition(
+ HeatParameterType.COMMA_DELIMITED_LIST.getType(), "one, two");
+ StorageOperationStatus operationStatus = heatParametersOperation.validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals(HeatParameterType.COMMA_DELIMITED_LIST.getType(), propertyDefinition.getType());
+ assertEquals("one, two", propertyDefinition.getDefaultValue());
+ }
+
+ @Test
+ public void testBooleanValues() {
+
+ String[] trueArray = { "true", "t", "1", "on", "y", "yes" };
+ String[] falseArray = { "false", "f", "0", "off", "n", "no" };
+
+ for (int i = 0; i < trueArray.length; i++) {
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, trueArray[i]));
+ HeatParameterDefinition propertyDefinition = buildHeatBooleanPropertyDefinition(
+ HeatParameterType.BOOLEAN.getType(), trueArray[i]);
+ StorageOperationStatus operationStatus = heatParametersOperation
+ .validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals("true", propertyDefinition.getDefaultValue());
+
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, trueArray[i]));
+ propertyDefinition = buildHeatBooleanPropertyDefinition(HeatParameterType.BOOLEAN.getType(),
+ trueArray[i].toUpperCase());
+ operationStatus = heatParametersOperation.validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals("true", propertyDefinition.getDefaultValue());
+
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, trueArray[i]));
+ propertyDefinition = buildHeatBooleanPropertyDefinition(HeatParameterType.BOOLEAN.getType(),
+ trueArray[i].toLowerCase());
+ operationStatus = heatParametersOperation.validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals("true", propertyDefinition.getDefaultValue());
+ }
+
+ for (int i = 0; i < falseArray.length; i++) {
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, falseArray[i]));
+ HeatParameterDefinition propertyDefinition = buildHeatBooleanPropertyDefinition(
+ HeatParameterType.BOOLEAN.getType(), falseArray[i]);
+ StorageOperationStatus operationStatus = heatParametersOperation
+ .validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals("false", propertyDefinition.getDefaultValue());
+
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, falseArray[i]));
+ propertyDefinition = buildHeatBooleanPropertyDefinition(HeatParameterType.BOOLEAN.getType(),
+ falseArray[i].toUpperCase());
+ operationStatus = heatParametersOperation.validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals("false", propertyDefinition.getDefaultValue());
+
+ assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, falseArray[i]));
+ propertyDefinition = buildHeatBooleanPropertyDefinition(HeatParameterType.BOOLEAN.getType(),
+ falseArray[i].toLowerCase());
+ operationStatus = heatParametersOperation.validateAndUpdateProperty(propertyDefinition);
+ assertEquals(StorageOperationStatus.OK, operationStatus);
+ assertEquals("false", propertyDefinition.getDefaultValue());
+ }
+
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "true"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "t"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "1"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "on"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "y"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "yes"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "false"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "f"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "0"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "off"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "n"));
+ // assertTrue(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN,
+ // "no"));
+
+ assertFalse(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, "blabla"));
+ assertFalse(heatParametersOperation.isValidValue(HeatParameterType.BOOLEAN, "2"));
+ }
+
+ private HeatParameterDefinition buildHeatPropertyDefinition() {
+ HeatParameterDefinition parameter = new HeatParameterDefinition();
+
+ parameter.setName("p1");
+ parameter.setType("string");
+ parameter.setDefaultValue("def");
+ parameter.setCurrentValue("current");
+ parameter.setDescription("description");
+
+ return parameter;
+ }
+
+ private HeatParameterDefinition buildHeatBooleanPropertyDefinition(String type, String boolValue) {
+ HeatParameterDefinition parameter = new HeatParameterDefinition();
+
+ parameter.setName("parameter1");
+ parameter.setType(type);
+ parameter.setDefaultValue(boolValue);
+ parameter.setDescription("description");
+
+ return parameter;
+ }
+
+ @Test
+ public void addPropertyToResourceInstanceTest() {
+
+ HeatParameterDefinition property = buildHeatPropertyDefinition();
+
+ HeatParameterValueData propertyData = new HeatParameterValueData();
+ propertyData.setUniqueId("bla");
+ propertyData.setValue("value1");
+
+ Either<HeatParameterValueData, TitanOperationStatus> either = Either.left(propertyData);
+
+ GraphRelation graphRelation = new GraphRelation();
+ Either<GraphRelation, TitanOperationStatus> relationResult = Either.left(graphRelation);
+
+ when(titanGenericDao.createNode((HeatParameterValueData) anyObject(), eq(HeatParameterValueData.class)))
+ .thenReturn(either);
+ when(titanGenericDao.createRelation((GraphNode) anyObject(), (GraphNode) anyObject(),
+ eq(GraphEdgeLabels.PARAMETER_VALUE), anyMap())).thenReturn(relationResult);
+ when(titanGenericDao.createRelation((GraphNode) anyObject(), (GraphNode) anyObject(),
+ eq(GraphEdgeLabels.PARAMETER_IMPL), anyMap())).thenReturn(relationResult);
+
+ Either<HeatParameterValueData, TitanOperationStatus> result = heatParametersOperation
+ .addHeatValueToGraph(property, "artifactLabel", "resourceInstanceId.artifactId", "resourceInstanceId");
+
+ assertTrue(result.isLeft());
+
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/InterfaceOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/InterfaceOperationTest.java
new file mode 100644
index 0000000000..e1eb7db070
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/InterfaceOperationTest.java
@@ -0,0 +1,272 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class InterfaceOperationTest {
+ private static Logger log = LoggerFactory.getLogger(InterfaceOperationTest.class.getName());
+ private Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ private static String USER_ID = "muserId";
+ private static String CATEGORY_NAME = "category/mycategory";
+ // InterfaceLifecycleOperation interfaceOperation = new
+ // InterfaceLifecycleOperation();
+
+ // TitanGenericDao titanGenericDao = Mockito.mock(TitanGenericDao.class);
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "interface-operation")
+ private InterfaceLifecycleOperation interfaceOperation;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ // @Resource(name = "artifact-operation")
+ // private ArtifactOperation artifactOperation;
+
+ @Before
+ public void createUserAndCategory() {
+ deleteAndCreateCategory(CATEGORY_NAME);
+ deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID);
+ }
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ // ExternalConfiguration.setAppName("catalog-model");
+ // String appConfigDir = "src/test/resources/config/catalog-model";
+ // ConfigurationSource configurationSource = new
+ // FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ // appConfigDir);
+
+ ModelTestBase.init();
+
+ }
+
+ @Test
+ public void testDummy() {
+
+ assertTrue(interfaceOperation != null);
+
+ }
+
+ @Test
+ public void addInterfaceToResourceTest() {
+
+ String capabilityTypeName = "mycapability1";
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute1";
+ String rootName = "Root100";
+ String softwareCompName = "tosca.nodes.SoftwareComponent";
+ String computeNodeName = "tosca.nodes.Compute";
+ String myResourceVersion = "300.0";
+ String reqRelationship = "myrelationship";
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "100.0", null,
+ true, true);
+
+ String interfaceName = "standard";
+ InterfaceDefinition interfaceDefinition = buildInterfaceDefinition();
+
+ Operation op = buildOperationDefinition();
+ Map<String, Operation> operations = new HashMap<String, Operation>();
+ operations.put("Create", op);
+ interfaceDefinition.setOperations(operations);
+
+ Either<InterfaceDefinition, StorageOperationStatus> result = interfaceOperation
+ .addInterfaceToResource(interfaceDefinition, rootResource.getUniqueId(), "standard");
+
+ assertTrue(result.isLeft());
+ log.debug("{}", result.left().value());
+
+ Either<Resource, StorageOperationStatus> getResourceRes = resourceOperation
+ .getResource(rootResource.getUniqueId());
+ assertTrue(getResourceRes.isLeft());
+ Resource resourceWithInterface = getResourceRes.left().value();
+ Map<String, InterfaceDefinition> interfaces = resourceWithInterface.getInterfaces();
+ assertNotNull(interfaces);
+ assertFalse(interfaces.isEmpty());
+ InterfaceDefinition interfaceDefinition2 = interfaces.get(interfaceName);
+ assertNotNull(interfaceDefinition2.getOperations());
+ assertFalse(interfaceDefinition2.getOperations().isEmpty());
+
+ }
+
+ @Test
+ public void updateInterfaceToResourceTest() {
+
+ String reqName = "host";
+ String rootName = "Root200";
+ String softwareCompName = "tosca.nodes.SoftwareComponent";
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "200.0", null,
+ true, true);
+
+ String interfaceName = "standard";
+ InterfaceDefinition interfaceDefinition = buildInterfaceDefinition();
+
+ Operation op = buildOperationDefinition();
+ Map<String, Operation> operations = new HashMap<String, Operation>();
+ operations.put("create", op);
+ interfaceDefinition.setOperations(operations);
+
+ Either<InterfaceDefinition, StorageOperationStatus> result = interfaceOperation
+ .addInterfaceToResource(interfaceDefinition, rootResource.getUniqueId(), "standard");
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId());
+
+ assertTrue(fetchRootResource.isLeft());
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "400.0", rootResource.getName(), true, true);
+
+ assertTrue(result.isLeft());
+ log.debug("{}", result.left().value());
+
+ addImplementationToOperation(op);
+ // String resourceId, String interfaceName, String
+ // operationName,Operation interf
+
+ Either<Operation, StorageOperationStatus> opResult = interfaceOperation
+ .updateInterfaceOperation(softwareComponent.getUniqueId(), "standard", "create", op);
+ // PrintGraph pg = new PrintGraph();
+ // System.out.println(pg.buildGraphForWebgraphWiz(titanDao.getGraph().left().value()));
+ assertTrue(opResult.isLeft());
+ log.debug("{}", opResult.left().value());
+
+ Either<Resource, StorageOperationStatus> getResourceRes = resourceOperation
+ .getResource(softwareComponent.getUniqueId());
+ assertTrue(getResourceRes.isLeft());
+ Resource resourceWithInterface = getResourceRes.left().value();
+ Map<String, InterfaceDefinition> interfaces = resourceWithInterface.getInterfaces();
+ assertNotNull(interfaces);
+ assertFalse(interfaces.isEmpty());
+ InterfaceDefinition interfaceDefinition2 = interfaces.get(interfaceName);
+ assertNotNull(interfaceDefinition2.getOperations());
+ assertFalse(interfaceDefinition2.getOperations().isEmpty());
+ Operation operation = interfaceDefinition2.getOperations().get("create");
+ assertNotNull(operation);
+ assertNotNull(operation.getImplementation());
+ }
+
+ private void addImplementationToOperation(Operation op) {
+ ArtifactDataDefinition artifactDataDef = new ArtifactDataDefinition();
+ artifactDataDef.setArtifactChecksum("YTg2Mjg4MWJhNmI5NzBiNzdDFkMWI=");
+ artifactDataDef.setArtifactName("create_myRoot.sh");
+ artifactDataDef.setArtifactLabel("create_myRoot");
+ artifactDataDef.setArtifactType("SHELL");
+ artifactDataDef.setDescription("good description");
+ artifactDataDef.setEsId("esId");
+ artifactDataDef.setUniqueId(op.getUniqueId() + "." + artifactDataDef.getArtifactLabel());
+ ArtifactDefinition artifactDef = new ArtifactDefinition(artifactDataDef, "UEsDBAoAAAAIAAeLb0bDQz");
+ op.setImplementation(artifactDef);
+ }
+
+ private InterfaceDefinition buildInterfaceDefinition() {
+ InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
+ interfaceDefinition.setType("tosca.interfaces.standard");
+ interfaceDefinition.setCreationDate(new Long(101232));
+
+ return interfaceDefinition;
+ }
+
+ private Operation buildOperationDefinition() {
+ Operation op = new Operation();
+ op.setCreationDate(new Long(101232));
+ op.setDescription("asda");
+
+ return op;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanDao);
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperationTest.java
new file mode 100644
index 0000000000..2b090f6f9f
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/LifecycleOperationTest.java
@@ -0,0 +1,1991 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.components.ServiceMetadataDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.InterfaceDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.Operation;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.operations.impl.util.ResourceCreationUtils;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.be.resources.data.CapabilityData;
+import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
+import org.openecomp.sdc.be.resources.data.InterfaceData;
+import org.openecomp.sdc.be.resources.data.OperationData;
+import org.openecomp.sdc.be.resources.data.RequirementData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thinkaurelius.titan.core.TitanGraph;
+//import com.tinkerpop.blueprints.Vertex;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class LifecycleOperationTest extends ModelTestBase {
+ private static Logger log = LoggerFactory.getLogger(LifecycleOperationTest.class.getName());
+ private static final String CAPABILITY_HOSTED_ON = "HostedOn";
+
+ private static final String INTERFACE_OPERATION_CREATE = "create";
+
+ private static final String INTERFACE_NAME = "standard";
+
+ private static final String CATEGORY_NAME = "category/mycategory";
+
+ private static final String SERVICE_NAME = "myService";
+
+ private static final String REQUIREMENT_NAME = "requirementName";
+
+ private static final String CAPABILITY_NAME = "capName";
+
+ private static final String USER_ID = "muserId";
+
+ @javax.annotation.Resource
+ private TitanGenericDao titanGenericDao;
+
+ @javax.annotation.Resource
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource
+ private ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ private LifecycleOperation lifecycleOperation;
+
+ @javax.annotation.Resource
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @javax.annotation.Resource
+ private ArtifactOperation artifactOperation;
+
+ @javax.annotation.Resource
+ private InterfaceLifecycleOperation interfaceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource(name = "capability-operation")
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource(name = "component-instance-operation")
+ private ComponentInstanceOperation resourceInstanceOperation;
+
+ @javax.annotation.Resource(name = "requirement-operation")
+ private RequirementOperation requirementOperation;
+
+ User checkoutUser;
+ User checkinUser;
+ User rfcUser;
+ User testerUser;
+ User adminUser;
+
+ @Rule
+ public TestName name = new TestName();
+
+ @BeforeClass
+ public static void initLifecycleOperation() {
+ ModelTestBase.init();
+ // new ConfigurationSource() {
+ //
+ // @Override
+ // public <T> T getAndWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T> void addWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ // });
+ //
+
+ }
+
+ @Before
+ public void setupBefore() {
+ clearGraph();
+ UserData modifierData = deleteAndCreateUser(ResourceCreationUtils.MODIFIER_ATT_UID + "co",
+ ResourceCreationUtils.MODIFIER_FIRST_NAME, ResourceCreationUtils.MODIFIER_LAST_NAME, "ADMIN");
+ checkoutUser = convertUserDataToUser(modifierData);
+
+ modifierData = deleteAndCreateUser(ResourceCreationUtils.MODIFIER_ATT_UID + "ci",
+ ResourceCreationUtils.MODIFIER_FIRST_NAME, ResourceCreationUtils.MODIFIER_LAST_NAME, "ADMIN");
+ checkinUser = convertUserDataToUser(modifierData);
+
+ modifierData = deleteAndCreateUser(ResourceCreationUtils.MODIFIER_ATT_UID + "rfc",
+ ResourceCreationUtils.MODIFIER_FIRST_NAME, ResourceCreationUtils.MODIFIER_LAST_NAME, "ADMIN");
+ rfcUser = convertUserDataToUser(modifierData);
+
+ modifierData = deleteAndCreateUser(ResourceCreationUtils.MODIFIER_ATT_UID + "tester",
+ ResourceCreationUtils.MODIFIER_FIRST_NAME, ResourceCreationUtils.MODIFIER_LAST_NAME, "TESTER");
+ testerUser = convertUserDataToUser(modifierData);
+
+ modifierData = deleteAndCreateUser(ResourceCreationUtils.MODIFIER_ATT_UID + "admin",
+ ResourceCreationUtils.MODIFIER_FIRST_NAME, ResourceCreationUtils.MODIFIER_LAST_NAME, "ADMIN");
+ adminUser = convertUserDataToUser(modifierData);
+
+ modifierData = deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID, "ADMIN");
+ adminUser = convertUserDataToUser(modifierData);
+
+ String[] category = CATEGORY_NAME.split("/");
+ OperationTestsUtil.deleteAndCreateServiceCategory(CATEGORY_NAME, titanGenericDao);
+ OperationTestsUtil.deleteAndCreateResourceCategory(category[0], category[1], titanGenericDao);
+
+ }
+
+ @After
+ public void teardown() {
+ clearGraph();
+ }
+
+ private void clearGraph() {
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ TitanGraph graph = graphResult.left().value();
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ // graph.removeVertex(vertex);
+ vertex.remove();
+ }
+
+ }
+ titanGenericDao.commit();
+ }
+
+ @Test
+ public void getOwnerTest() {
+
+ Resource resultResource = createTestResource(checkoutUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(resultResource.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals("check modifier", checkoutUser.getUserId(), resourceOwner.getUserId());
+
+ }
+
+ /*********************** CHECKOUT ***************************************************************/
+
+ @Test
+ public void checkoutCertifiedTest() {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "1.0", LifecycleStateEnum.CERTIFIED, null);
+ String origUniqueId = resultResource.getUniqueId();
+ Either<Resource, StorageOperationStatus> origResourceResult = resourceOperation.getResource(origUniqueId);
+ Resource origResource = origResourceResult.left().value();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkout
+ Either<Resource, StorageOperationStatus> checkoutResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse.isLeft());
+ Resource checkoutResource = checkoutResponse.left().value();
+
+ assertEquals(checkoutResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(checkoutResource.getVersion(), "1.1");
+ assertEquals(checkoutResource.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(checkoutResource.getLastUpdaterUserId(), checkoutUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkoutResource.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkoutUser));
+
+ // assert original resource not deleted
+ Either<Resource, StorageOperationStatus> getOrigResource = resourceOperation.getResource(origUniqueId);
+ assertEquals("check resource created", true, getOrigResource.isLeft());
+ // assertEquals("assert original resource not changed", origResource,
+ // getOrigResource.left().value());
+ }
+
+ @Test
+ public void checkoutDefaultTest() {
+
+ Resource resultResource = createTestResource(checkinUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkout
+ Either<Resource, StorageOperationStatus> checkoutResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse.isLeft());
+ Resource checkoutResource = checkoutResponse.left().value();
+
+ assertEquals(checkoutResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(checkoutResource.getVersion(), "0.2");
+ assertEquals(checkoutResource.getCreatorUserId(), checkinUser.getUserId());
+ assertEquals(checkoutResource.getLastUpdaterUserId(), checkoutUser.getUserId());
+ assertEquals(checkoutResource.isHighestVersion(), true);
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkoutResource.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkoutUser));
+
+ // assert original resource not deleted
+ Either<Resource, StorageOperationStatus> getOrigResource = resourceOperation.getResource(origUniqueId);
+ assertEquals("check resource created", true, getOrigResource.isLeft());
+ // assertEquals("assert original resource not changed", origResource,
+ // getOrigResource.left().value());
+ assertEquals("assert original resource not highest version", false,
+ getOrigResource.left().value().isHighestVersion());
+ }
+
+ @Test
+ public void checkoutFullResourceTest() {
+
+ Resource origResource = createFullTestResource(checkinUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ String origUniqueId = origResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkout
+ Either<Resource, StorageOperationStatus> checkoutResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, origResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse.isLeft());
+ Resource checkoutResource = checkoutResponse.left().value();
+
+ assertEquals(checkoutResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(checkoutResource.getVersion(), "0.2");
+ assertEquals(checkoutResource.getCreatorUserId(), checkinUser.getUserId());
+ assertEquals(checkoutResource.getLastUpdaterUserId(), checkoutUser.getUserId());
+ assertEquals(checkoutResource.isHighestVersion(), true);
+
+ assertNotNull(checkoutResource.getArtifacts());
+ assertFalse(checkoutResource.getArtifacts().isEmpty());
+ assertNotNull(checkoutResource.getInterfaces());
+ assertFalse(checkoutResource.getInterfaces().isEmpty());
+ Map<String, InterfaceDefinition> interfaces = checkoutResource.getInterfaces();
+ assertTrue(interfaces.containsKey(INTERFACE_NAME));
+ InterfaceDefinition interfaceDef = interfaces.get(INTERFACE_NAME);
+ Map<String, Operation> operations = interfaceDef.getOperations();
+ assertNotNull(operations);
+ assertFalse(operations.isEmpty());
+ assertTrue(operations.containsKey(INTERFACE_OPERATION_CREATE));
+ Operation op = operations.get(INTERFACE_OPERATION_CREATE);
+ assertNotNull(op.getImplementation());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkoutResource.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkoutUser));
+
+ // assert original resource not deleted
+ Either<Resource, StorageOperationStatus> getOrigResource = resourceOperation.getResource(origUniqueId);
+ assertEquals("check resource created", true, getOrigResource.isLeft());
+ // assertEquals("assert original resource not changed", origResource,
+ // getOrigResource.left().value());
+ assertEquals("assert original resource not highest version", false,
+ getOrigResource.left().value().isHighestVersion());
+ }
+
+ @Test
+ public void getResourceOwnerResourceNotExistTest() {
+
+ // create resource metadata
+ Resource resource = buildResourceMetadata(adminUser.getUserId(), CATEGORY_NAME);
+ resource.setLifecycleState(LifecycleStateEnum.CERTIFIED);
+ resource.setUniqueId("my-resource.0.1");
+
+ Either<Resource, StorageOperationStatus> origResourceResult = resourceOperation.getResource("my-resource.0.1");
+ assertEquals("assert resource not exist", true, origResourceResult.isRight());
+
+ // get resource owner
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner("my-resource.0.1",
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("assert no owner", true, getOwnerResponse.isRight());
+ StorageOperationStatus status = getOwnerResponse.right().value();
+
+ assertEquals(StorageOperationStatus.INVALID_ID, status);
+
+ }
+
+ @Test
+ public void checkoutResourceTwice() {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "1.0", LifecycleStateEnum.CERTIFIED, null);
+ String origUniqueId = resultResource.getUniqueId();
+ Either<Resource, StorageOperationStatus> origResourceResult = resourceOperation.getResource(origUniqueId);
+ Resource origResource = origResourceResult.left().value();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // first checkout
+ Either<Resource, StorageOperationStatus> checkoutResponse1 = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse1.isLeft());
+
+ // second checkout
+ Either<Resource, StorageOperationStatus> checkoutResponse2 = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, origResource, checkoutUser, resourceOwner, false);
+ assertEquals("check checkout failed", true, checkoutResponse2.isRight());
+ assertEquals(StorageOperationStatus.ENTITY_ALREADY_EXISTS, checkoutResponse2.right().value());
+
+ }
+
+ /******** SERVICE */
+ @Test
+ public void checkoutServiceDefaultTest() {
+
+ Service resultResource = createTestService(checkinUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkout
+ Either<? extends Component, StorageOperationStatus> checkoutResponse = lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Service, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse.isLeft());
+ Component checkoutResource = checkoutResponse.left().value();
+
+ assertEquals(checkoutResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(checkoutResource.getVersion(), "0.2");
+ assertEquals(checkoutResource.getCreatorUserId(), checkinUser.getUserId());
+ assertEquals(checkoutResource.getLastUpdaterUserId(), checkoutUser.getUserId());
+ assertEquals(checkoutResource.isHighestVersion(), true);
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkoutResource.getUniqueId(), NodeTypeEnum.Service, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkoutUser));
+
+ // assert original resource not deleted
+ Either<Service, StorageOperationStatus> getOrigResource = serviceOperation.getService(origUniqueId);
+ assertEquals("check resource created", true, getOrigResource.isLeft());
+ // assertEquals("assert original resource not changed", origResource,
+ // getOrigResource.left().value());
+ assertEquals("assert original resource not highest version", false,
+ getOrigResource.left().value().isHighestVersion());
+ }
+
+ @Test
+ public void checkoutFullServiceTest() {
+
+ Service origService = createTestService(checkinUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+ String origUniqueId = origService.getUniqueId();
+
+ // add artifacts
+ addArtifactToService(checkinUser.getUserId(), origService.getUniqueId(), "install_apache");
+ addArtifactToService(checkinUser.getUserId(), origService.getUniqueId(), "start_apache");
+
+ // add resource instances
+ ResourceInstanceOperationTest riTest = new ResourceInstanceOperationTest();
+ riTest.setOperations(titanGenericDao, capabilityTypeOperation, requirementOperation, capabilityOperation,
+ resourceOperation, propertyOperation, resourceInstanceOperation);
+ riTest.addResourceInstancesAndRelation(origService.getUniqueId());
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ Either<Service, StorageOperationStatus> serviceBeforeCheckout = serviceOperation.getService(origUniqueId, true);
+ assertTrue(serviceBeforeCheckout.isLeft());
+ origService = serviceBeforeCheckout.left().value();
+
+ // checkout
+ Either<? extends Component, StorageOperationStatus> checkoutResponse = lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Service, origService, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse.isLeft());
+ Service checkoutResource = (Service) checkoutResponse.left().value();
+
+ assertEquals(checkoutResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ assertEquals(checkoutResource.getVersion(), "0.2");
+ assertEquals(checkoutResource.getCreatorUserId(), checkinUser.getUserId());
+ assertEquals(checkoutResource.getLastUpdaterUserId(), checkoutUser.getUserId());
+ assertEquals(checkoutResource.isHighestVersion(), true);
+
+ assertNotNull(checkoutResource.getArtifacts());
+ assertFalse(checkoutResource.getArtifacts().isEmpty());
+ assertNotNull(checkoutResource.getComponentInstances());
+ assertFalse(checkoutResource.getComponentInstances().isEmpty());
+ assertNotNull(checkoutResource.getComponentInstancesRelations());
+ assertFalse(checkoutResource.getComponentInstancesRelations().isEmpty());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkoutResource.getUniqueId(), NodeTypeEnum.Service, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkoutUser));
+
+ // assert original resource not deleted
+ Either<Service, StorageOperationStatus> getOrigResource = serviceOperation.getService(origUniqueId);
+ assertEquals("check service created", true, getOrigResource.isLeft());
+ // assertEquals("assert original resource not changed", origResource,
+ // getOrigResource.left().value());
+ assertEquals("assert original service not highest version", false,
+ getOrigResource.left().value().isHighestVersion());
+ }
+
+ @Test
+ public void checkoutServiceTwice() {
+
+ Service resultResource = createTestService(adminUser.getUserId(), "1.0", LifecycleStateEnum.CERTIFIED, null);
+ String origUniqueId = resultResource.getUniqueId();
+ Either<Service, StorageOperationStatus> origResourceResult = serviceOperation.getService(origUniqueId);
+ Service origResource = origResourceResult.left().value();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // first checkout
+ Either<? extends Component, StorageOperationStatus> checkoutResponse1 = lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Service, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkoutResponse1.isLeft());
+
+ // second checkout
+ Either<? extends Component, StorageOperationStatus> checkoutResponse2 = lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Service, origResource, checkoutUser, resourceOwner, false);
+ assertEquals("check checkout failed", true, checkoutResponse2.isRight());
+ assertEquals(StorageOperationStatus.ENTITY_ALREADY_EXISTS, checkoutResponse2.right().value());
+
+ }
+
+ /**************************** CHECKIN ********************************************************************/
+
+ @Test
+ public void checkinDefaultTest() {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkin
+ Either<Resource, StorageOperationStatus> checkinResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkinComponent(NodeTypeEnum.Resource, resultResource, checkinUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkinResponse.isLeft());
+ Resource checkinResource = checkinResponse.left().value();
+
+ assertEquals(checkinResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(checkinResource.getVersion(), "0.1");
+ assertEquals(checkinResource.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(checkinResource.getLastUpdaterUserId(), checkinUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkinResource.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkinUser));
+
+ }
+
+ @Test
+ public void checkinFromRfcTest() {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkin
+ Either<Resource, StorageOperationStatus> checkinResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkinComponent(NodeTypeEnum.Resource, resultResource, checkinUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, checkinResponse.isLeft());
+
+ // rfc
+ Either<Resource, StorageOperationStatus> rfcResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, checkinResponse.left().value(), rfcUser,
+ checkinUser, false);
+ assertEquals("check resource object is returned", true, checkinResponse.isLeft());
+
+ // checkin (cancel rfc)
+ checkinResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkinComponent(NodeTypeEnum.Resource, rfcResponse.left().value(), checkinUser, rfcUser, false);
+ assertEquals("check resource object is returned", true, checkinResponse.isLeft());
+ resultResource = checkinResponse.left().value();
+
+ assertEquals(resultResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(resultResource.getVersion(), "0.1");
+ assertEquals(resultResource.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(resultResource.getLastUpdaterUserId(), checkinUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(resultResource.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkinUser));
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(resultResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ }
+
+ /*** SERVICE */
+ @Test
+ public void checkinServiceDefaultTest() {
+
+ Service resultService = createTestService(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+ String origUniqueId = resultService.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkin
+ Either<? extends Component, StorageOperationStatus> checkinResponse = lifecycleOperation
+ .checkinComponent(NodeTypeEnum.Service, resultService, checkinUser, resourceOwner, false);
+ assertEquals("check service object is returned", true, checkinResponse.isLeft());
+ Service checkinResource = (Service) checkinResponse.left().value();
+
+ assertEquals(checkinResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(checkinResource.getVersion(), "0.1");
+ assertEquals(checkinResource.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(checkinResource.getLastUpdaterUserId(), checkinUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(checkinResource.getUniqueId(), NodeTypeEnum.Service, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkinUser));
+
+ }
+
+ @Test
+ public void checkinServiceFromRfcTest() {
+
+ Service resultResource = createTestService(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkin
+ Either<? extends Component, StorageOperationStatus> checkinResponse = lifecycleOperation
+ .checkinComponent(NodeTypeEnum.Service, resultResource, checkinUser, resourceOwner, false);
+ assertEquals("check service object is returned", true, checkinResponse.isLeft());
+
+ // rfc
+ Either<? extends Component, StorageOperationStatus> rfcResponse = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, checkinResponse.left().value(), rfcUser,
+ checkinUser, false);
+ assertEquals("check service object is returned", true, checkinResponse.isLeft());
+
+ // checkin (cancel rfc)
+ checkinResponse = lifecycleOperation.checkinComponent(NodeTypeEnum.Service, rfcResponse.left().value(),
+ checkinUser, rfcUser, false);
+ assertEquals("check resource object is returned", true, checkinResponse.isLeft());
+ resultResource = (Service) checkinResponse.left().value();
+
+ assertEquals(resultResource.getLifecycleState(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ assertEquals(resultResource.getVersion(), "0.1");
+ assertEquals(resultResource.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(resultResource.getLastUpdaterUserId(), checkinUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(resultResource.getUniqueId(), NodeTypeEnum.Service, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(checkinUser));
+
+ // assert relations
+ ServiceMetadataDataDefinition metadata = new ServiceMetadataDataDefinition();
+ metadata.setUniqueId(resultResource.getUniqueId());
+ ServiceMetadataData resourceData = new ServiceMetadataData(metadata);
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ }
+
+ /****************************
+ * UNDO CHECKOUT
+ ********************************************************************/
+
+ @Test
+ public void undoCheckoutNewResourceTest() {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ //
+
+ // undo checkout
+ Either<Resource, StorageOperationStatus> undoCheckoutResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .undoCheckout(NodeTypeEnum.Resource, resultResource, adminUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, undoCheckoutResponse.isLeft());
+
+ Either<Resource, StorageOperationStatus> origResourceResult = resourceOperation.getResource(origUniqueId);
+ assertTrue(origResourceResult.isRight());
+ /*
+ * assertTrue(origResourceResult.isLeft());
+ * assertTrue(origResourceResult.left().value().getIsDeleted() == true);
+ */
+ }
+
+ @Test
+ public void undoCheckoutNewFullResourceTest() {
+
+ Resource resultResource = createFullTestResource(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // undo checkout
+ Either<Resource, StorageOperationStatus> undoCheckoutResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .undoCheckout(NodeTypeEnum.Resource, resultResource, adminUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, undoCheckoutResponse.isLeft());
+
+ Either<Resource, StorageOperationStatus> origResourceResult = resourceOperation.getResource(origUniqueId);
+ /*
+ * assertTrue(origResourceResult.isLeft());
+ * assertTrue(origResourceResult.left().value().getIsDeleted() == true);
+ */ assertTrue(origResourceResult.isRight());
+
+ String interfaceId = origUniqueId + "." + INTERFACE_NAME;
+ Either<InterfaceData, TitanOperationStatus> node = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Interface), interfaceId, InterfaceData.class);
+ assertTrue(node.isRight());
+
+ String operationId = interfaceId + "." + INTERFACE_OPERATION_CREATE;
+ Either<OperationData, TitanOperationStatus> op = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.InterfaceOperation), operationId, OperationData.class);
+ assertTrue(op.isRight());
+
+ String capabilityId = "capability." + origUniqueId + "." + CAPABILITY_NAME;
+ Either<CapabilityData, TitanOperationStatus> capability = titanGenericDao
+ .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capabilityId, CapabilityData.class);
+ assertTrue(capability.isRight());
+
+ String requirementId = origUniqueId + "." + REQUIREMENT_NAME;
+ Either<RequirementData, TitanOperationStatus> req = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Requirement), requirementId, RequirementData.class);
+ assertTrue(req.isRight());
+
+ }
+
+ @Test
+ public void undoCheckoutExistingResourceTest() {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(resultResource.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ String prevResourceId = resultResource.getUniqueId();
+ Either<Resource, StorageOperationStatus> result2 = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource created", true, result2.isLeft());
+ Resource resultResource2 = result2.left().value();
+
+ // get resource owner
+ getOwnerResponse = lifecycleOperation.getComponentOwner(resultResource2.getUniqueId(), NodeTypeEnum.Resource,
+ false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ resourceOwner = getOwnerResponse.left().value();
+ assertEquals(resourceOwner, checkoutUser);
+
+ // undo checkout
+ Either<Resource, StorageOperationStatus> undoCheckoutResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .undoCheckout(NodeTypeEnum.Resource, resultResource2, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, undoCheckoutResponse.isLeft());
+
+ // get previous resource
+ Either<Resource, StorageOperationStatus> resourceAfterUndo = resourceOperation.getResource(prevResourceId);
+ assertTrue(resourceAfterUndo.isLeft());
+ Resource actualResource = resourceAfterUndo.left().value();
+ assertTrue(actualResource.isHighestVersion());
+ assertEquals(adminUser.getUserId(), actualResource.getCreatorUserId());
+ assertEquals(adminUser.getUserId(), actualResource.getLastUpdaterUserId());
+ assertEquals("0.1", actualResource.getVersion());
+ assertEquals(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, actualResource.getLifecycleState());
+
+ Either<Resource, StorageOperationStatus> origResourceResult = resourceOperation
+ .getResource(resultResource2.getUniqueId());
+ /*
+ * assertTrue(origResourceResult.isLeft());
+ * assertTrue(origResourceResult.left().value().getIsDeleted() == true);
+ */ assertTrue(origResourceResult.isRight());
+
+ }
+
+ /**** SERVICE ***/
+ @Test
+ public void undoCheckoutNewServiceTest() {
+
+ Service resultResource = createTestService(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ //
+
+ // undo checkout
+ Either<? extends Component, StorageOperationStatus> undoCheckoutResponse = lifecycleOperation
+ .undoCheckout(NodeTypeEnum.Service, resultResource, adminUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, undoCheckoutResponse.isLeft());
+
+ Either<Service, StorageOperationStatus> origResourceResult = serviceOperation.getService(origUniqueId);
+ /*
+ * assertTrue(origResourceResult.isLeft());
+ * assertTrue(origResourceResult.left().value().getIsDeleted() == true);
+ */ assertTrue(origResourceResult.isRight());
+
+ }
+
+ @Test
+ public void undoCheckoutNewFullServiceTest() {
+
+ Service origService = createTestService(checkinUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+ String origUniqueId = origService.getUniqueId();
+
+ // add artifacts
+ addArtifactToService(checkinUser.getUserId(), origService.getUniqueId(), "install_apache");
+ addArtifactToService(checkinUser.getUserId(), origService.getUniqueId(), "start_apache");
+
+ // add resource instances
+ ResourceInstanceOperationTest riTest = new ResourceInstanceOperationTest();
+ riTest.setOperations(titanGenericDao, capabilityTypeOperation, requirementOperation, capabilityOperation,
+ resourceOperation, propertyOperation, resourceInstanceOperation);
+ riTest.addResourceInstancesAndRelation(origService.getUniqueId());
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ Either<Service, StorageOperationStatus> service = serviceOperation.getService(origUniqueId);
+ assertTrue(service.isLeft());
+
+ Service resultResource = service.left().value();
+ List<ComponentInstance> resourceInstances = resultResource.getComponentInstances();
+
+ // undo checkout
+ Either<? extends Component, StorageOperationStatus> undoCheckoutResponse = lifecycleOperation
+ .undoCheckout(NodeTypeEnum.Service, resultResource, adminUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, undoCheckoutResponse.isLeft());
+
+ Either<Service, StorageOperationStatus> origResourceResult = serviceOperation.getService(origUniqueId);
+ /*
+ * assertTrue(origResourceResult.isLeft());
+ * assertTrue(origResourceResult.left().value().getIsDeleted() == true);
+ */ assertTrue(origResourceResult.isRight());
+
+ for (ComponentInstance ri : resourceInstances) {
+ Either<ComponentInstanceData, TitanOperationStatus> node = titanGenericDao.getNode(
+ UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), ri.getUniqueId(),
+ ComponentInstanceData.class);
+ assertTrue(node.isRight());
+ }
+
+ }
+
+ @Test
+ public void undoCheckoutExistingServiceTest() {
+
+ Service resultResource = createTestService(adminUser.getUserId(), "0.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(resultResource.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ String prevResourceId = resultResource.getUniqueId();
+ Either<? extends Component, StorageOperationStatus> result2 = lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Service, resultResource, checkoutUser, resourceOwner, false);
+ assertEquals("check resource created", true, result2.isLeft());
+ Component resultResource2 = result2.left().value();
+ String result2Uid = resultResource.getUniqueId();
+
+ // get resource owner
+ getOwnerResponse = lifecycleOperation.getComponentOwner(resultResource2.getUniqueId(), NodeTypeEnum.Resource,
+ false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ resourceOwner = getOwnerResponse.left().value();
+ assertEquals(resourceOwner, checkoutUser);
+
+ // undo checkout
+ Either<? extends Component, StorageOperationStatus> undoCheckoutResponse = lifecycleOperation
+ .undoCheckout(NodeTypeEnum.Service, resultResource2, checkoutUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, undoCheckoutResponse.isLeft());
+
+ // get previous resource
+ Either<Service, StorageOperationStatus> resourceAfterUndo = serviceOperation.getService(prevResourceId);
+ assertTrue(resourceAfterUndo.isLeft());
+ Service actualResource = resourceAfterUndo.left().value();
+ assertTrue(actualResource.isHighestVersion());
+ assertEquals(adminUser.getUserId(), actualResource.getCreatorUserId());
+ assertEquals(adminUser.getUserId(), actualResource.getLastUpdaterUserId());
+ assertEquals("0.1", actualResource.getVersion());
+ assertEquals(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, actualResource.getLifecycleState());
+
+ Either<Service, StorageOperationStatus> origResourceResult = serviceOperation.getService(result2Uid);
+ /*
+ * assertTrue(origResourceResult.isLeft());
+ * assertTrue(origResourceResult.left().value().getIsDeleted() == true);
+ */ assertTrue(origResourceResult.isRight());
+
+ }
+
+ /****************************
+ * CERTIFICATION REQUEST
+ ********************************************************************/
+
+ @Test
+ public void certReqDefaultTest() {
+ Resource actualResource = testCertificationRequest(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(adminUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ }
+
+ @Test
+ public void atomicCheckinCertReqTest() {
+ Resource actualResource = testCertificationRequest(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ private Resource testCertificationRequest(LifecycleStateEnum preState) {
+
+ Resource resultResource = createTestResource(adminUser.getUserId(), "0.1", preState, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkin
+ Either<Resource, StorageOperationStatus> certReqResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, resultResource, rfcUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, certReqResponse.isLeft());
+ Resource resourceAfterChange = certReqResponse.left().value();
+
+ assertEquals(resourceAfterChange.getLifecycleState(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(resourceAfterChange.getVersion(), "0.1");
+ assertEquals(resourceAfterChange.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(resourceAfterChange.getLastUpdaterUserId(), rfcUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(resourceAfterChange.getUniqueId(), NodeTypeEnum.Resource, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(rfcUser));
+
+ return resourceAfterChange;
+ }
+
+ /** SERVICE **/
+ @Test
+ public void certServiceReqDefaultTest() {
+ Service actualResource = testServiceCertificationRequest(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+
+ // assert relations
+ ServiceMetadataDataDefinition metadata = new ServiceMetadataDataDefinition();
+ metadata.setUniqueId(actualResource.getUniqueId());
+ ServiceMetadataData serviceData = new ServiceMetadataData(metadata);
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(serviceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(serviceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(adminUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ }
+
+ @Test
+ public void atomicServiceCheckinCertReqTest() {
+ Service actualResource = testServiceCertificationRequest(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+
+ // assert relations
+ ServiceMetadataDataDefinition metadata = new ServiceMetadataDataDefinition();
+ metadata.setUniqueId(actualResource.getUniqueId());
+ ServiceMetadataData serviceData = new ServiceMetadataData(metadata);
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(serviceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(serviceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ private Service testServiceCertificationRequest(LifecycleStateEnum preState) {
+
+ Service resultResource = createTestService(adminUser.getUserId(), "0.1", preState, null);
+ String origUniqueId = resultResource.getUniqueId();
+
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation.getComponentOwner(origUniqueId,
+ NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+
+ // checkin
+ Either<? extends Component, StorageOperationStatus> certReqResponse = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultResource, rfcUser, resourceOwner, false);
+ assertEquals("check resource object is returned", true, certReqResponse.isLeft());
+ Service resourceAfterChange = (Service) certReqResponse.left().value();
+
+ assertEquals(resourceAfterChange.getLifecycleState(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ assertEquals(resourceAfterChange.getVersion(), "0.1");
+ assertEquals(resourceAfterChange.getCreatorUserId(), adminUser.getUserId());
+ assertEquals(resourceAfterChange.getLastUpdaterUserId(), rfcUser.getUserId());
+
+ // assert owner changed
+ Either<User, StorageOperationStatus> getOwnerCheckoutResponse = lifecycleOperation
+ .getComponentOwner(resourceAfterChange.getUniqueId(), NodeTypeEnum.Service, false);
+ assertEquals("check user object is returned", true, getOwnerCheckoutResponse.isLeft());
+ resourceOwner = getOwnerCheckoutResponse.left().value();
+ assertTrue(resourceOwner.equals(rfcUser));
+
+ return resourceAfterChange;
+ }
+
+ /****************************
+ * START CERTIFICATION
+ ********************************************************************/
+
+ @Test
+ public void startCertificationTest() {
+
+ Resource resultResource = createTestResource(checkinUser.getUserId(), "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // certification request
+ Either<Resource, StorageOperationStatus> requestCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, resultResource, rfcUser, checkinUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ // start certification
+ Either<Resource, StorageOperationStatus> startCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Resource, resultResource, testerUser, rfcUser, false);
+
+ assertEquals(true, startCertificationResult.isLeft());
+ Resource actualResource = startCertificationResult.left().value();
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(actualResource.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals(testerUser.getUserId(), resourceOwner.getUserId());
+
+ assertTrue(actualResource.isHighestVersion());
+ assertEquals(checkinUser.getUserId(), actualResource.getCreatorUserId());
+ assertEquals(testerUser.getUserId(), actualResource.getLastUpdaterUserId());
+ assertEquals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS, actualResource.getLifecycleState());
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(testerUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ /** SERVICE */
+ @Test
+ public void startServiceCertificationTest() {
+
+ Service resultResource = createTestService(checkinUser.getUserId(), "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // certification request
+ Either<? extends Component, StorageOperationStatus> requestCertificationResult = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultResource, rfcUser, checkinUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ // start certification
+ Either<? extends Component, StorageOperationStatus> startCertificationResult = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultResource, testerUser, rfcUser, false);
+
+ assertEquals(true, startCertificationResult.isLeft());
+ Service actualResource = (Service) startCertificationResult.left().value();
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(actualResource.getUniqueId(), NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals(testerUser.getUserId(), resourceOwner.getUserId());
+
+ assertTrue(actualResource.isHighestVersion());
+ assertEquals(checkinUser.getUserId(), actualResource.getCreatorUserId());
+ assertEquals(testerUser.getUserId(), actualResource.getLastUpdaterUserId());
+ assertEquals(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS, actualResource.getLifecycleState());
+
+ // assert relations
+ ServiceMetadataDataDefinition metadata = new ServiceMetadataDataDefinition();
+ metadata.setUniqueId(actualResource.getUniqueId());
+ ServiceMetadataData serviceData = new ServiceMetadataData(metadata);
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(serviceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(testerUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(serviceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(serviceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ /****************************
+ * FAIL CERTIFICATION
+ ********************************************************************/
+
+ @Test
+ public void failCertificationTest() {
+
+ Resource actualResource = certificationStatusChange(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, checkinUser);
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ // old edges removed
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ // new state is checkin
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ /*** SERVICE **/
+
+ @Test
+ public void failCertificationServiceTest() {
+
+ Service actualService = certificationStatusChangeService(LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, checkinUser);
+
+ // assert relations
+ ServiceMetadataData resourceData = new ServiceMetadataData((ServiceMetadataDataDefinition) actualService
+ .getComponentMetadataDefinition().getMetadataDataDefinition());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ // old edges removed
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ // new state is checkin
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ /****************************
+ * CANCEL CERTIFICATION
+ ********************************************************************/
+
+ @Test
+ public void cancelCertificationTest() {
+
+ Resource actualResource = certificationStatusChange(LifecycleStateEnum.READY_FOR_CERTIFICATION, rfcUser);
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ // old edges removed
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ // new state is rfc
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ /** SERVICE **/
+ @Test
+ public void cancelCertificationServiceTest() {
+
+ Service actualService = certificationStatusChangeService(LifecycleStateEnum.READY_FOR_CERTIFICATION, rfcUser);
+
+ // assert relations
+ ServiceMetadataData ServiceNode = new ServiceMetadataData();
+ ServiceNode.getMetadataDataDefinition().setUniqueId(actualService.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ // old edges removed
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(ServiceNode, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(ServiceNode,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(checkinUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ // new state is rfc
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(ServiceNode, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+ }
+
+ /**************************** CERTIFY ********************************************************************/
+
+ @Test
+ public void certifyTest() {
+
+ Resource resultResource = createTestResource(checkinUser.getUserId(), "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // certification request
+ Either<Resource, StorageOperationStatus> requestCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, resultResource, rfcUser, checkinUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ // start certification
+ Either<Resource, StorageOperationStatus> startCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Resource, resultResource, testerUser, rfcUser, false);
+ assertEquals(true, startCertificationResult.isLeft());
+ Resource actualResource = startCertificationResult.left().value();
+
+ // cancel certification
+ Either<? extends Component, StorageOperationStatus> CertificationResult = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Resource, actualResource, testerUser, testerUser, false);
+
+ assertEquals(true, CertificationResult.isLeft());
+ actualResource = (Resource) CertificationResult.left().value();
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(actualResource.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals(testerUser.getUserId(), resourceOwner.getUserId());
+
+ assertTrue(actualResource.isHighestVersion());
+ assertEquals(checkinUser.getUserId(), actualResource.getCreatorUserId());
+ assertEquals(testerUser.getUserId(), actualResource.getLastUpdaterUserId());
+ assertEquals(LifecycleStateEnum.CERTIFIED, actualResource.getLifecycleState());
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualResource.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ // old edges removed
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ // new state is certified
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(testerUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ }
+
+ /******** SERVICE **/
+
+ @Test
+ public void certifyServiceTest() {
+
+ Service resultService = createTestService(checkinUser.getUserId(), "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // certification request
+ Either<? extends Component, StorageOperationStatus> requestCertificationResult = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService, rfcUser, checkinUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ // start certification
+ Either<? extends Component, StorageOperationStatus> startCertificationResult = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService, testerUser, rfcUser, false);
+ assertEquals(true, startCertificationResult.isLeft());
+ Service actualService = (Service) startCertificationResult.left().value();
+
+ // cancel certification
+ Either<? extends Component, StorageOperationStatus> CertificationResult = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Service, actualService, testerUser, testerUser, false);
+
+ assertEquals(true, CertificationResult.isLeft());
+ actualService = (Service) CertificationResult.left().value();
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(actualService.getUniqueId(), NodeTypeEnum.Service, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals(testerUser.getUserId(), resourceOwner.getUserId());
+
+ assertTrue(actualService.isHighestVersion());
+ assertEquals(checkinUser.getUserId(), actualService.getCreatorUserId());
+ assertEquals(testerUser.getUserId(), actualService.getLastUpdaterUserId());
+ assertEquals(LifecycleStateEnum.CERTIFIED, actualService.getLifecycleState());
+
+ // assert relations
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(actualService.getUniqueId());
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ // old edges removed
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+ Either<GraphRelation, TitanOperationStatus> incomingRelationByCriteria = titanGenericDao
+ .getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.NOT_CERTIFIED_CHECKIN);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isRight());
+
+ // new state is certified
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData, GraphEdgeLabels.STATE,
+ props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(testerUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ props.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ incomingRelationByCriteria = titanGenericDao.getIncomingRelationByCriteria(resourceData,
+ GraphEdgeLabels.LAST_STATE, props);
+ assertTrue(incomingRelationByCriteria.isLeft());
+ assertEquals(rfcUser.getUserId(), incomingRelationByCriteria.left().value().getFrom().getIdValue());
+
+ }
+
+ @Test
+ public void testDeleteOldVersionsResource() {
+ // simulate
+ createTestResource(checkinUser.getUserId(), "1.0", LifecycleStateEnum.CERTIFIED, null);
+ Resource resourceNewVersion = createTestResource(checkinUser.getUserId(), "1.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+ createTestResource(checkinUser.getUserId(), "1.2", LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ resourceNewVersion.getUUID());
+ createTestResource(checkinUser.getUserId(), "1.3", LifecycleStateEnum.CERTIFICATION_IN_PROGRESS,
+ resourceNewVersion.getUUID());
+ Resource certifiedResource = createTestResource(checkinUser.getUserId(), "2.0", LifecycleStateEnum.CERTIFIED,
+ resourceNewVersion.getUUID());
+
+ Either<Boolean, StorageOperationStatus> deleteOldComponentVersions = lifecycleOperation
+ .deleteOldComponentVersions(NodeTypeEnum.Resource, certifiedResource.getName(),
+ certifiedResource.getUUID(), false);
+
+ assertTrue(deleteOldComponentVersions.isLeft());
+
+ String resourceName = certifiedResource.getName();
+ Either<List<Resource>, StorageOperationStatus> resource = resourceOperation
+ .getResourceByNameAndVersion(resourceName, "1.0", false);
+ assertTrue(resource.isLeft());
+
+ resource = resourceOperation.getResourceByNameAndVersion(resourceName, "2.0", false);
+ assertTrue(resource.isLeft());
+
+ resource = resourceOperation.getResourceByNameAndVersion(resourceName, "1.1", false);
+ assertTrue(resource.isLeft());
+ assertTrue(resource.left().value().size() == 1);
+ Resource deleted = resource.left().value().get(0);
+ assertTrue(deleted.getIsDeleted());
+ // assertEquals(StorageOperationStatus.NOT_FOUND,
+ // resource.right().value());
+
+ resource = resourceOperation.getResourceByNameAndVersion(resourceName, "1.2", false);
+ // assertTrue(resource.isRight());
+ // assertEquals(StorageOperationStatus.NOT_FOUND,
+ // resource.right().value());
+ assertTrue(resource.isLeft());
+ assertTrue(resource.left().value().size() == 1);
+ deleted = resource.left().value().get(0);
+ assertTrue(deleted.getIsDeleted());
+
+ resource = resourceOperation.getResourceByNameAndVersion(resourceName, "1.3", false);
+ // assertTrue(resource.isRight());
+ // assertEquals(StorageOperationStatus.NOT_FOUND,
+ // resource.right().value());
+ assertTrue(resource.isLeft());
+ assertTrue(resource.left().value().size() == 1);
+ deleted = resource.left().value().get(0);
+ assertTrue(deleted.getIsDeleted());
+ }
+
+ @Test
+ public void testDeleteOldVersionsService() {
+ // simulate
+ createTestService(checkinUser.getUserId(), "1.0", LifecycleStateEnum.CERTIFIED, null);
+ Service serviceNewUUid = createTestService(checkinUser.getUserId(), "1.1",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+ createTestService(checkinUser.getUserId(), "1.2", LifecycleStateEnum.NOT_CERTIFIED_CHECKIN,
+ serviceNewUUid.getUUID());
+ createTestService(checkinUser.getUserId(), "1.3", LifecycleStateEnum.CERTIFICATION_IN_PROGRESS,
+ serviceNewUUid.getUUID());
+ Service certifiedService = createTestService(checkinUser.getUserId(), "2.0", LifecycleStateEnum.CERTIFIED,
+ serviceNewUUid.getUUID());
+
+ Either<Boolean, StorageOperationStatus> deleteOldComponentVersions = lifecycleOperation
+ .deleteOldComponentVersions(NodeTypeEnum.Service, certifiedService.getName(),
+ certifiedService.getUUID(), false);
+
+ assertTrue(deleteOldComponentVersions.isLeft());
+
+ String resourceName = certifiedService.getName();
+ Either<Service, StorageOperationStatus> service = serviceOperation.getServiceByNameAndVersion(resourceName,
+ "1.0", null, false);
+ assertTrue(service.isLeft());
+
+ service = serviceOperation.getServiceByNameAndVersion(resourceName, "2.0", null, false);
+ assertTrue(service.isLeft());
+
+ service = serviceOperation.getServiceByNameAndVersion(resourceName, "1.1", null, false);
+ /*
+ * assertTrue(resource.isRight());
+ * assertEquals(StorageOperationStatus.NOT_FOUND,
+ * resource.right().value());
+ */
+ assertTrue(service.isLeft());
+ assertTrue(service.left().value().getIsDeleted());
+
+ service = serviceOperation.getServiceByNameAndVersion(resourceName, "1.2", null, false);
+
+ service = serviceOperation.getServiceByNameAndVersion(resourceName, "1.3", null, false);
+ /*
+ * assertTrue(service.isRight());
+ * assertEquals(StorageOperationStatus.NOT_FOUND,
+ * service.right().value());
+ */
+ assertTrue(service.isLeft());
+ assertTrue(service.left().value().getIsDeleted());
+
+ service = serviceOperation.getServiceByNameAndVersion(resourceName, "1.3", null, false);
+ /*
+ * assertTrue(service.isRight());
+ * assertEquals(StorageOperationStatus.NOT_FOUND,
+ * service.right().value());
+ */
+ assertTrue(service.isLeft());
+ assertTrue(service.left().value().getIsDeleted());
+
+ }
+
+ private Resource certificationStatusChange(LifecycleStateEnum nextState, User expectedOwner) {
+ Resource resultResource = createTestResource(checkinUser.getUserId(), "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // certification request
+ Either<Resource, StorageOperationStatus> requestCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, resultResource, rfcUser, checkinUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ // start certification
+ Either<Resource, StorageOperationStatus> startCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Resource, resultResource, testerUser, rfcUser, false);
+ assertEquals(true, startCertificationResult.isLeft());
+ Resource actualResource = startCertificationResult.left().value();
+
+ // cancel certification
+ Either<Resource, StorageOperationStatus> failCertificationResult = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .cancelOrFailCertification(NodeTypeEnum.Resource, actualResource, testerUser, testerUser, nextState,
+ false);
+
+ assertEquals(true, failCertificationResult.isLeft());
+ actualResource = failCertificationResult.left().value();
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(actualResource.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals(expectedOwner, resourceOwner);
+
+ assertTrue(actualResource.isHighestVersion());
+ assertEquals(checkinUser.getUserId(), actualResource.getCreatorUserId());
+ assertEquals(testerUser.getUserId(), actualResource.getLastUpdaterUserId());
+ assertEquals(nextState, actualResource.getLifecycleState());
+ return actualResource;
+ }
+
+ private Service certificationStatusChangeService(LifecycleStateEnum nextState, User expectedOwner) {
+ Service resultService = createTestService(checkinUser.getUserId(), "0.2",
+ LifecycleStateEnum.NOT_CERTIFIED_CHECKIN, null);
+
+ // certification request
+ Either<? extends Component, StorageOperationStatus> requestCertificationResult = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService, rfcUser, checkinUser, false);
+ assertTrue(requestCertificationResult.isLeft());
+
+ // start certification
+ Either<? extends Component, StorageOperationStatus> startCertificationResult = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService, testerUser, rfcUser, false);
+ assertEquals(true, startCertificationResult.isLeft());
+ Service actualService = (Service) startCertificationResult.left().value();
+
+ // cancel certification
+ Either<? extends Component, StorageOperationStatus> failCertificationResult = lifecycleOperation
+ .cancelOrFailCertification(NodeTypeEnum.Service, actualService, testerUser, testerUser, nextState,
+ false);
+
+ assertEquals(true, failCertificationResult.isLeft());
+ actualService = (Service) failCertificationResult.left().value();
+
+ // get resource owner
+ Either<User, StorageOperationStatus> getOwnerResponse = lifecycleOperation
+ .getComponentOwner(actualService.getUniqueId(), NodeTypeEnum.Resource, false);
+
+ assertEquals("check user object is returned", true, getOwnerResponse.isLeft());
+ User resourceOwner = getOwnerResponse.left().value();
+ assertEquals(expectedOwner, resourceOwner);
+
+ assertTrue(actualService.isHighestVersion());
+ assertEquals(checkinUser.getUserId(), actualService.getCreatorUserId());
+ assertEquals(testerUser.getUserId(), actualService.getLastUpdaterUserId());
+ assertEquals(nextState, actualService.getLifecycleState());
+ return actualService;
+ }
+
+ private Resource createTestResource(String userId, String version, LifecycleStateEnum state, String uuid) {
+ // create resource in graph
+
+ Resource resource2 = buildResourceMetadata(userId, CATEGORY_NAME);
+ resource2.setVersion(version);
+ ;
+ resource2.setLifecycleState(state);
+ resource2.setUUID(uuid);
+
+ Either<Resource, StorageOperationStatus> result = resourceOperation.createResource(resource2);
+ assertEquals("check resource created", true, result.isLeft());
+ Resource resultResource = result.left().value();
+ return resultResource;
+ }
+
+ private Service createTestService(String userId, String version, LifecycleStateEnum state, String uuid) {
+ // create resource in graph
+
+ Service service = new Service();
+ service.setName(SERVICE_NAME);
+ service.setVersion(version);
+ service.setDescription("description 1");
+ service.setCreatorUserId(userId);
+ service.setContactId("contactId@sdc.com");
+ CategoryDefinition category = new CategoryDefinition();
+ category.setName(CATEGORY_NAME);
+
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(category);
+ service.setCategories(categories);
+ service.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ service.setTags(tags);
+ service.setUUID(uuid);
+
+ service.setLifecycleState(state);
+
+ Either<Service, StorageOperationStatus> result = serviceOperation.createService(service);
+ assertEquals("check service created", true, result.isLeft());
+ Service resultResource = result.left().value();
+ return resultResource;
+ }
+
+ private Resource createFullTestResource(String userId, String version, LifecycleStateEnum state) {
+
+ Resource resource2 = buildResourceMetadata(userId, CATEGORY_NAME);
+ resource2.setVersion(version);
+ ;
+ resource2.setLifecycleState(state);
+
+ InterfaceDefinition inter = new InterfaceDefinition(INTERFACE_NAME, "interface description", null);
+
+ Operation operation = new Operation();
+ operation.setDescription("op description");
+ operation.setUniqueId(inter.getUniqueId() + "." + INTERFACE_OPERATION_CREATE);
+
+ ArtifactDataDefinition artifactDataDef = new ArtifactDataDefinition();
+ artifactDataDef.setArtifactChecksum("YTg2Mjg4MWJhNmI5NzBiNzdDFkMWI=");
+ artifactDataDef.setArtifactName("create_myRoot.sh");
+ artifactDataDef.setArtifactLabel("create_myRoot");
+ artifactDataDef.setArtifactType("SHELL");
+ artifactDataDef.setDescription("good description");
+ artifactDataDef.setEsId("esId");
+ artifactDataDef.setUniqueId(operation.getUniqueId() + "." + artifactDataDef.getArtifactLabel());
+ ArtifactDefinition artifactDef = new ArtifactDefinition(artifactDataDef, "UEsDBAoAAAAIAAeLb0bDQz");
+
+ operation.setImplementation(artifactDef);
+ operation.setCreationDate(System.currentTimeMillis());
+ Map<String, Operation> ops = new HashMap<>();
+ ops.put(INTERFACE_OPERATION_CREATE, operation);
+ inter.setOperations(ops);
+
+ Map<String, InterfaceDefinition> interfaces = new HashMap<>();
+ interfaces.put(INTERFACE_NAME, inter);
+
+ resource2.setInterfaces(interfaces);
+
+ String capabilityTypeName = CAPABILITY_HOSTED_ON;
+ createCapabilityOnGraph(capabilityTypeName);
+
+ // create capability definition
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ capabilityDefinition.setDescription("my capability");
+ capabilityDefinition.setType(capabilityTypeName);
+ capabilityDefinition.setName(CAPABILITY_NAME);
+ Map<String, List<CapabilityDefinition>> capabilities = new HashMap<>();
+ List<String> validSourceTypes = new ArrayList<String>();
+ validSourceTypes.add("tosca.nodes.SC");
+ capabilityDefinition.setValidSourceTypes(validSourceTypes);
+ List<CapabilityDefinition> caplist = new ArrayList<CapabilityDefinition>();
+ caplist.add(capabilityDefinition);
+ capabilities.put(capabilityTypeName, caplist);
+ resource2.setCapabilities(capabilities);
+
+ // add requirement definition
+ RequirementDefinition reqDefinition = new RequirementDefinition();
+ // reqDefinition.setNode(reqNodeName);
+ // reqDefinition.setRelationship(reqRelationship);
+
+ reqDefinition.setCapability(capabilityTypeName);
+ reqDefinition.setName(REQUIREMENT_NAME);
+ Map<String, List<RequirementDefinition>> requirements = new HashMap<>();
+ List<RequirementDefinition> reqlist = new ArrayList<RequirementDefinition>();
+ reqlist.add(reqDefinition);
+ requirements.put(capabilityTypeName, reqlist);
+ resource2.setRequirements(requirements);
+
+ Either<Resource, StorageOperationStatus> result = resourceOperation.createResource(resource2);
+ assertEquals("check resource created", true, result.isLeft());
+ Resource resultResource = result.left().value();
+
+ // add artifacts to resource
+ // ArtifactDataDefinition artifactDataDef = new
+ // ArtifactDataDefinition();
+ artifactDataDef.setArtifactChecksum("YTg2Mjg4MWJhNmI5NzBiNzdDFkMWI=");
+ artifactDataDef.setArtifactName("create_myRoot.sh");
+ artifactDataDef.setArtifactLabel("create_myRoot");
+ artifactDataDef.setArtifactType("SHELL");
+ artifactDataDef.setDescription("good description");
+ artifactDataDef.setEsId("esId");
+ artifactDataDef.setUniqueId(resultResource.getUniqueId() + "." + artifactDataDef.getArtifactLabel());
+ artifactDef = new ArtifactDefinition(artifactDataDef, "UEsDBAoAAAAIAAeLb0bDQz");
+ // artifacts.put("myArtifact", artifactDef);
+ // resource2.setArtifacts(artifacts);
+
+ Either<ArtifactDefinition, StorageOperationStatus> addArifactToResource = artifactOperation
+ .addArifactToComponent(artifactDef, resultResource.getUniqueId(), NodeTypeEnum.Resource, false, true);
+ assertTrue(addArifactToResource.isLeft());
+
+ Either<Resource, StorageOperationStatus> resource = resourceOperation.getResource(resultResource.getUniqueId());
+ assertTrue(resource.isLeft());
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+ String json = prettyGson.toJson(resource.left().value());
+ log.debug(json);
+ return resource.left().value();
+ }
+
+ private void createCapabilityOnGraph(String capabilityTypeName) {
+
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition();
+ capabilityTypeDefinition.setDescription("desc1");
+ capabilityTypeDefinition.setType(capabilityTypeName);
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+ String propName1 = "disk_size";
+ PropertyDefinition property1 = buildProperty1();
+ properties.put(propName1, property1);
+ capabilityTypeDefinition.setProperties(properties);
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType1 = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition);
+ assertTrue(addCapabilityType1.isLeft());
+ }
+
+ private User convertUserDataToUser(UserData modifierData) {
+ User modifier = new User();
+ modifier.setUserId(modifierData.getUserId());
+ modifier.setEmail(modifierData.getEmail());
+ modifier.setFirstName(modifierData.getFirstName());
+ modifier.setLastName(modifierData.getLastName());
+ modifier.setRole(modifierData.getRole());
+ return modifier;
+ }
+
+ private Resource buildResourceMetadata(String userId, String category) {
+ // deleteAndCreateCategory(category);
+
+ Resource resource = new Resource();
+ resource.setName("my-resource");
+ resource.setVersion("1.0");
+ ;
+ resource.setDescription("description 1");
+ resource.setAbstract(false);
+ resource.setCreatorUserId(userId);
+ resource.setContactId("contactId@sdc.com");
+ resource.setVendorName("vendor 1");
+ resource.setVendorRelease("1.0.0");
+ String[] categoryArr = category.split("/");
+ resource.addCategory(categoryArr[0], categoryArr[1]);
+ resource.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ resource.setTags(tags);
+ return resource;
+ }
+
+ public UserData deleteAndCreateUser(String userId, String firstName, String lastName, String role) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+ userData.setRole(role);
+
+ titanGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanGenericDao.createNode(userData, UserData.class);
+ titanGenericDao.commit();
+
+ return userData;
+ }
+
+ private PropertyDefinition buildProperty1() {
+ PropertyDefinition property1 = new PropertyDefinition();
+ property1.setDefaultValue("10");
+ property1.setDescription(
+ "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.");
+ property1.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints = new ArrayList<PropertyConstraint>();
+ GreaterThanConstraint propertyConstraint1 = new GreaterThanConstraint("0");
+ constraints.add(propertyConstraint1);
+
+ LessOrEqualConstraint propertyConstraint2 = new LessOrEqualConstraint("10");
+ constraints.add(propertyConstraint2);
+
+ property1.setConstraints(constraints);
+ return property1;
+ }
+
+ private ArtifactDefinition addArtifactToService(String userId, String serviceId, String artifactName) {
+ ArtifactDefinition artifactInfo = new ArtifactDefinition();
+
+ artifactInfo.setArtifactName(artifactName + ".sh");
+ artifactInfo.setArtifactType("SHELL");
+ artifactInfo.setDescription("hdkfhskdfgh");
+ artifactInfo.setPayloadData("UEsDBAoAAAAIAAeLb0bDQz");
+
+ artifactInfo.setUserIdCreator(userId);
+ String fullName = "Jim H";
+ artifactInfo.setUpdaterFullName(fullName);
+ long time = System.currentTimeMillis();
+ artifactInfo.setCreatorFullName(fullName);
+ artifactInfo.setCreationDate(time);
+ artifactInfo.setLastUpdateDate(time);
+ artifactInfo.setUserIdLastUpdater(userId);
+ artifactInfo.setArtifactLabel(artifactName);
+ artifactInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(serviceId, artifactInfo.getArtifactLabel()));
+
+ Either<ArtifactDefinition, StorageOperationStatus> artifact = artifactOperation
+ .addArifactToComponent(artifactInfo, serviceId, NodeTypeEnum.Service, true, true);
+ assertTrue(artifact.isLeft());
+ return artifactInfo;
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperationTest.java
new file mode 100644
index 0000000000..5bd6c831a5
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PolicyTypeOperationTest.java
@@ -0,0 +1,120 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Iterator;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PolicyTypeDataDefinition;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PolicyTypeDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PolicyTypeOperation;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+
+public class PolicyTypeOperationTest extends ModelTestBase {
+
+ @Resource(name = "policy-type-operation")
+ private PolicyTypeOperation policyTypeOperation;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ ModelTestBase.init();
+
+ }
+
+ @Before
+ public void cleanUp() {
+ TitanGenericDao titanGenericDao = policyTypeOperation.titanGenericDao;
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanGenericDao.getGraph();
+ TitanGraph graph = graphResult.left().value();
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ vertex.remove();
+ }
+
+ }
+ titanGenericDao.commit();
+ }
+
+ @Test
+ public void testAddPolicyType() {
+
+ PolicyTypeDefinition policyTypePreCreate = createPolicyTypeDef();
+ assertTrue(StringUtils.isEmpty(policyTypePreCreate.getUniqueId()));
+ Either<PolicyTypeDefinition, StorageOperationStatus> addPolicyType = policyTypeOperation
+ .addPolicyType(policyTypePreCreate);
+ assertTrue(addPolicyType.isLeft());
+ PolicyTypeDefinition policyTypePostCreate = addPolicyType.left().value();
+ assertEquals(policyTypePostCreate.getType(), policyTypePreCreate.getType());
+ assertEquals(policyTypePostCreate.getDescription(), policyTypePreCreate.getDescription());
+
+ assertTrue(!StringUtils.isEmpty(policyTypePostCreate.getUniqueId()));
+ }
+
+ @Test
+ public void testGetLatestPolicyTypeByType() {
+ PolicyTypeDefinition policyTypeCreated = policyTypeOperation.addPolicyType(createPolicyTypeDef()).left()
+ .value();
+ Either<PolicyTypeDefinition, StorageOperationStatus> eitherPolicyTypeFetched = policyTypeOperation
+ .getLatestPolicyTypeByType(policyTypeCreated.getType());
+ assertTrue(eitherPolicyTypeFetched.isLeft());
+ PolicyTypeDefinition policyTypeFetched = eitherPolicyTypeFetched.left().value();
+ assertEquals(policyTypeFetched.toString(), policyTypeCreated.toString());
+
+ }
+
+ private PolicyTypeDefinition createPolicyTypeDef() {
+ PolicyTypeDataDefinition policyTypeDataDefinition = new PolicyTypeDataDefinition();
+ policyTypeDataDefinition
+ .setDescription("description: The TOSCA Policy Type all other TOSCA Policy Types derive from");
+ policyTypeDataDefinition.setType("tosca.policies.Root");
+ PolicyTypeDefinition policyTypeDefinition = new PolicyTypeDefinition(policyTypeDataDefinition);
+ policyTypeDefinition.setHighestVersion(true);
+ policyTypeDefinition.setVersion("1.0");
+ return policyTypeDefinition;
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperationTest.java
new file mode 100644
index 0000000000..6caa3044ad
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperationTest.java
@@ -0,0 +1,548 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+
+import fj.data.Either;
+
+public class PropertyOperationTest extends ModelTestBase {
+
+ PropertyOperation propertyOperation = new PropertyOperation();
+
+ TitanGenericDao titanGenericDao = Mockito.mock(TitanGenericDao.class);
+
+ @Before
+ public void setup() {
+ propertyOperation.setTitanGenericDao(titanGenericDao);
+
+ }
+
+ /*
+ * @Test public void addPropertyToResourceTest() {
+ *
+ * String propName = "myProp"; PropertyDefinition property =
+ * buildPropertyDefinition(); List<PropertyConstraint> constraints =
+ * buildConstraints(); property.setConstraints(constraints);
+ *
+ * PropertyData propertyData = new PropertyData(property,
+ * propertyOperation.convertConstraintsToString(constraints));
+ *
+ * Either<PropertyData, TitanOperationStatus> either =
+ * Either.left(propertyData);
+ * //when(propertyDao.create((GraphNeighbourTable)anyObject(),
+ * eq(PropertyData.class), eq(NodeTypeEnum.Property))).thenReturn(either);
+ * GraphRelation graphRelation = new GraphRelation(); Either<GraphRelation,
+ * TitanOperationStatus> relationResult = Either.left(graphRelation);
+ *
+ * when(titanGenericDao.createNode((PropertyData)anyObject(),
+ * eq(PropertyData.class))).thenReturn(either);
+ * when(titanGenericDao.createRelation((GraphNode)anyObject(),
+ * (GraphNode)anyObject(), eq(GraphEdgeLabels.PROPERTY),
+ * anyMap())).thenReturn(relationResult);
+ *
+ * Either<PropertyDefinition, StorageOperationStatus> result =
+ * propertyOperation.addPropertyToResource(propName, property,
+ * NodeTypeEnum.Resource, "my-resource.1.0");
+ *
+ * assertTrue(result.isLeft()); System.out.println(result.left().value());
+ * PropertyDefinition propertyDefinition = result.left().value();
+ *
+ * List<PropertyConstraint> originalConstraints = property.getConstraints();
+ * List<PropertyConstraint> propertyConstraintsResult =
+ * propertyDefinition.getConstraints();
+ * assertEquals(propertyConstraintsResult.size(),
+ * originalConstraints.size());
+ *
+ * }
+ */
+ private PropertyDefinition buildPropertyDefinition() {
+ PropertyDefinition property = new PropertyDefinition();
+ property.setDefaultValue("10");
+ property.setDescription(
+ "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.");
+ property.setType(ToscaType.INTEGER.name().toLowerCase());
+ return property;
+ }
+
+ @Test
+ public void addPropertiesToGraphTableTest() {
+
+ // Map<String, PropertyDefinition> properties = new HashMap<String,
+ // PropertyDefinition>();
+ // String propName = "myProp";
+ // PropertyDefinition property = buildPropertyDefinition();
+ //
+ // List<PropertyConstraint> constraints = buildConstraints();
+ // property.setConstraints(constraints);
+ //
+ // properties.put(propName, property);
+ //
+ // GraphNeighbourTable graphNeighbourTable = new GraphNeighbourTable();
+ // ResourceData resourceData = new ResourceData();
+ // String resourceName = "my-resource";
+ // String resourceVersion = "1.0";
+ // String resourceId = resourceName + "." + resourceVersion;
+ // resourceData.setUniqueId(resourceId);
+ // int resourceIndex = graphNeighbourTable.addNode(resourceData);
+ //
+ // heatParametersOperation.addPropertiesToGraphTable(properties,
+ // graphNeighbourTable, resourceIndex, resourceId);
+ //
+ // assertEquals(2, graphNeighbourTable.getNodes().size());
+ // assertEquals(1, graphNeighbourTable.getDirectedEdges().size());
+ // List<GraphNode> nodes = graphNeighbourTable.getNodes();
+ // boolean nodeFound = false;
+ // for (GraphNode neo4jNode : nodes) {
+ // if (neo4jNode instanceof PropertyData) {
+ // PropertyData propertyData = (PropertyData)neo4jNode;
+ // assertEquals("check property unique id", resourceId + "." + propName,
+ // propertyData.getUniqueId());
+ // assertEquals(property.getDescription(),
+ // propertyData.getPropertyDataDefinition().getDescription());
+ // nodeFound = true;
+ // }
+ // }
+ // assertEquals("looking for PropertyData object in table", true,
+ // nodeFound);
+ //
+ // NodeRelation nodeRelation =
+ // graphNeighbourTable.getDirectedEdges().get(0);
+ // assertEquals("check from index to index edge", 0,
+ // nodeRelation.getFromIndex());
+ // assertEquals("check from index to index edge", 1,
+ // nodeRelation.getToIndex());
+ // assertEquals("check edge type",
+ // GraphEdgePropertiesDictionary.PROPERTY,
+ // nodeRelation.getEdge().getEdgeType());
+ // assertEquals("check propert name on edge", true,
+ // nodeRelation.getEdge().getProperties().values().contains(propName));
+ }
+
+ @Test
+ public void convertConstraintsTest() {
+
+ List<PropertyConstraint> constraints = buildConstraints();
+ List<String> convertedStringConstraints = propertyOperation.convertConstraintsToString(constraints);
+ assertEquals("constraints size", constraints.size(), convertedStringConstraints.size());
+
+ List<PropertyConstraint> convertedConstraints = propertyOperation
+ .convertConstraints(convertedStringConstraints);
+ assertEquals("check size of constraints", constraints.size(), convertedConstraints.size());
+
+ Set<String> constraintsClasses = new HashSet<String>();
+ for (PropertyConstraint propertyConstraint : constraints) {
+ constraintsClasses.add(propertyConstraint.getClass().getName());
+ }
+
+ for (PropertyConstraint propertyConstraint : convertedConstraints) {
+ assertTrue("check all classes generated",
+ constraintsClasses.contains(propertyConstraint.getClass().getName()));
+ }
+ }
+
+ @Test
+ public void testIsPropertyDefaultValueValid_NoDefault() {
+ PropertyDefinition property = new PropertyDefinition();
+ property.setName("myProperty");
+ property.setType(ToscaPropertyType.BOOLEAN.getType());
+ assertTrue(propertyOperation.isPropertyDefaultValueValid(property, null));
+ }
+
+ @Test
+ public void testIsPropertyDefaultValueValid_ValidDefault() {
+ PropertyDefinition property = new PropertyDefinition();
+ property.setName("myProperty");
+ property.setType(ToscaPropertyType.INTEGER.getType());
+ property.setDefaultValue("50");
+ assertTrue(propertyOperation.isPropertyDefaultValueValid(property, null));
+ }
+
+ @Test
+ public void testIsPropertyDefaultValueValid_InvalidDefault() {
+ PropertyDefinition property = new PropertyDefinition();
+ property.setName("myProperty");
+ property.setType(ToscaPropertyType.BOOLEAN.getType());
+ property.setDefaultValue("50");
+ assertFalse(propertyOperation.isPropertyDefaultValueValid(property, null));
+ }
+
+ private List<PropertyConstraint> buildConstraints() {
+ List<PropertyConstraint> constraints = new ArrayList<PropertyConstraint>();
+ GreaterThanConstraint propertyConstraint1 = new GreaterThanConstraint("0");
+ LessOrEqualConstraint propertyConstraint2 = new LessOrEqualConstraint("10");
+ List<String> range = new ArrayList<String>();
+ range.add("0");
+ range.add("100");
+ InRangeConstraint propertyConstraint3 = new InRangeConstraint(range);
+ constraints.add(propertyConstraint1);
+ constraints.add(propertyConstraint2);
+ constraints.add(propertyConstraint3);
+ return constraints;
+ }
+
+ @Test
+ public void findPropertyValueBestMatch1() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+ ComponentInstanceProperty instanceProperty1 = new ComponentInstanceProperty();
+ instanceProperty1.setValue("v1node1");
+ instanceIdToValue.put("node1", instanceProperty1);
+
+ ComponentInstanceProperty instanceProperty2 = new ComponentInstanceProperty();
+ instanceProperty2.setValue("v1node2");
+ instanceIdToValue.put("node2", instanceProperty2);
+
+ ComponentInstanceProperty instanceProperty3 = new ComponentInstanceProperty();
+ instanceProperty3.setValue("v1node3");
+ instanceIdToValue.put("node3", instanceProperty3);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", "v1node1", instanceProperty.getValue());
+ assertEquals("check default value", "v1node2", instanceProperty.getDefaultValue());
+
+ }
+
+ @Test
+ public void findPropertyValueBestMatch2() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+
+ ComponentInstanceProperty instanceProperty2 = new ComponentInstanceProperty();
+ instanceProperty2.setValue("v1node2");
+ instanceProperty2.setValueUniqueUid("aaaa");
+ instanceIdToValue.put("node2", instanceProperty2);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", "v1node2", instanceProperty.getValue());
+ assertEquals("check default value", "vv1", instanceProperty.getDefaultValue());
+ assertNull("check value unique id is null", instanceProperty.getValueUniqueUid());
+
+ }
+
+ @Test
+ public void findPropertyValueBestMatch3() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+ ComponentInstanceProperty instanceProperty1 = new ComponentInstanceProperty();
+ instanceProperty1.setValue("v1node1");
+ instanceProperty1.setValueUniqueUid("aaaa");
+ instanceIdToValue.put("node1", instanceProperty1);
+
+ ComponentInstanceProperty instanceProperty3 = new ComponentInstanceProperty();
+ instanceProperty3.setValue("v1node3");
+ instanceIdToValue.put("node3", instanceProperty3);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", "v1node1", instanceProperty.getValue());
+ assertEquals("check default value", "v1node3", instanceProperty.getDefaultValue());
+ assertEquals("check valid unique id", instanceProperty1.getValueUniqueUid(),
+ instanceProperty.getValueUniqueUid());
+
+ }
+
+ @Test
+ public void findPropertyValueBestMatch1Rules() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+ ComponentInstanceProperty instanceProperty1 = new ComponentInstanceProperty();
+ instanceProperty1.setValue("v1node1");
+
+ List<PropertyRule> rules = new ArrayList<>();
+ PropertyRule propertyRule = new PropertyRule();
+ String[] ruleArr = { "node1", ".+", "node3" };
+ List<String> rule1 = new ArrayList<>(Arrays.asList(ruleArr));
+ propertyRule.setRule(rule1);
+ propertyRule.setValue("88");
+ rules.add(propertyRule);
+ instanceProperty1.setRules(rules);
+
+ instanceIdToValue.put("node1", instanceProperty1);
+
+ ComponentInstanceProperty instanceProperty2 = new ComponentInstanceProperty();
+ instanceProperty2.setValue("v1node2");
+ instanceIdToValue.put("node2", instanceProperty2);
+
+ ComponentInstanceProperty instanceProperty3 = new ComponentInstanceProperty();
+ instanceProperty3.setValue("v1node3");
+ instanceIdToValue.put("node3", instanceProperty3);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", propertyRule.getValue(), instanceProperty.getValue());
+ assertEquals("check default value", "v1node2", instanceProperty.getDefaultValue());
+
+ }
+
+ @Test
+ public void findPropertyValueBestMatch2Rules() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+ ComponentInstanceProperty instanceProperty1 = new ComponentInstanceProperty();
+ instanceProperty1.setValue("v1node1");
+
+ List<PropertyRule> rules = new ArrayList<>();
+ PropertyRule propertyRule1 = new PropertyRule();
+ String[] ruleArr1 = { "node1", "node2", ".+" };
+ List<String> rule1 = new ArrayList<>(Arrays.asList(ruleArr1));
+ propertyRule1.setRule(rule1);
+ propertyRule1.setValue("88");
+
+ PropertyRule propertyRule2 = new PropertyRule();
+ String[] ruleArr2 = { "node1", "node2", "node3" };
+ List<String> rule2 = new ArrayList<>(Arrays.asList(ruleArr2));
+ propertyRule2.setRule(rule2);
+ propertyRule2.setValue("99");
+
+ rules.add(propertyRule2);
+ rules.add(propertyRule1);
+
+ instanceProperty1.setRules(rules);
+
+ instanceIdToValue.put("node1", instanceProperty1);
+
+ ComponentInstanceProperty instanceProperty2 = new ComponentInstanceProperty();
+ instanceProperty2.setValue("v1node2");
+ instanceIdToValue.put("node2", instanceProperty2);
+
+ ComponentInstanceProperty instanceProperty3 = new ComponentInstanceProperty();
+ instanceProperty3.setValue("v1node3");
+ instanceIdToValue.put("node3", instanceProperty3);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", propertyRule2.getValue(), instanceProperty.getValue());
+ assertEquals("check default value", "v1node2", instanceProperty.getDefaultValue());
+
+ }
+
+ @Test
+ public void findPropertyValueBestMatch1RuleLowLevel() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+ ComponentInstanceProperty instanceProperty1 = new ComponentInstanceProperty();
+ instanceProperty1.setValue("v1node1");
+
+ List<PropertyRule> rules = new ArrayList<>();
+ PropertyRule propertyRule1 = new PropertyRule();
+ String[] ruleArr1 = { "node1", "node2", ".+" };
+ List<String> rule1 = new ArrayList<>(Arrays.asList(ruleArr1));
+ propertyRule1.setRule(rule1);
+ propertyRule1.setValue("88");
+
+ PropertyRule propertyRule2 = new PropertyRule();
+ String[] ruleArr2 = { "node1", "node2", "node3" };
+ List<String> rule2 = new ArrayList<>(Arrays.asList(ruleArr2));
+ propertyRule2.setRule(rule2);
+ propertyRule2.setValue("99");
+
+ rules.add(propertyRule2);
+ rules.add(propertyRule1);
+
+ instanceProperty1.setRules(rules);
+
+ instanceIdToValue.put("node1", instanceProperty1);
+
+ ComponentInstanceProperty instanceProperty2 = new ComponentInstanceProperty();
+ instanceProperty2.setValue("v1node2");
+
+ List<PropertyRule> rules3 = new ArrayList<>();
+ PropertyRule propertyRule3 = new PropertyRule();
+ String[] ruleArr3 = { "node2", "node3" };
+ List<String> rule3 = new ArrayList<>(Arrays.asList(ruleArr3));
+ propertyRule3.setRule(rule3);
+ propertyRule3.setValue("77");
+ rules3.add(propertyRule3);
+
+ instanceProperty2.setRules(rules3);
+ instanceIdToValue.put("node2", instanceProperty2);
+
+ ComponentInstanceProperty instanceProperty3 = new ComponentInstanceProperty();
+ instanceProperty3.setValue("v1node3");
+ instanceIdToValue.put("node3", instanceProperty3);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", propertyRule2.getValue(), instanceProperty.getValue());
+ assertEquals("check default value", propertyRule3.getValue(), instanceProperty.getDefaultValue());
+
+ }
+
+ @Test
+ public void findPropertyValueBestMatchDefaultValueNotChanged() {
+
+ String propertyUniqueId = "x1";
+ ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty();
+ instanceProperty.setValue("v1");
+ instanceProperty.setDefaultValue("vv1");
+ List<String> path = new ArrayList<>();
+ path.add("node1");
+ path.add("node2");
+ path.add("node3");
+ instanceProperty.setPath(path);
+
+ Map<String, ComponentInstanceProperty> instanceIdToValue = new HashMap<String, ComponentInstanceProperty>();
+ ComponentInstanceProperty instanceProperty1 = new ComponentInstanceProperty();
+ instanceProperty1.setValue("v1node1");
+
+ List<PropertyRule> rules = new ArrayList<>();
+ PropertyRule propertyRule1 = new PropertyRule();
+ String[] ruleArr1 = { "node1", "node2", ".+" };
+ List<String> rule1 = new ArrayList<>(Arrays.asList(ruleArr1));
+ propertyRule1.setRule(rule1);
+ propertyRule1.setValue("88");
+
+ PropertyRule propertyRule2 = new PropertyRule();
+ String[] ruleArr2 = { "node1", "node2", "node3" };
+ List<String> rule2 = new ArrayList<>(Arrays.asList(ruleArr2));
+ propertyRule2.setRule(rule2);
+ propertyRule2.setValue("99");
+
+ rules.add(propertyRule2);
+ rules.add(propertyRule1);
+
+ instanceProperty1.setRules(rules);
+
+ instanceIdToValue.put("node1", instanceProperty1);
+
+ ComponentInstanceProperty instanceProperty2 = new ComponentInstanceProperty();
+ instanceProperty2.setValue("v1node2");
+
+ List<PropertyRule> rules3 = new ArrayList<>();
+ PropertyRule propertyRule3 = new PropertyRule();
+ String[] ruleArr3 = { "node2", "node333" };
+ List<String> rule3 = new ArrayList<>(Arrays.asList(ruleArr3));
+ propertyRule3.setRule(rule3);
+ propertyRule3.setValue("77");
+ rules3.add(propertyRule3);
+
+ instanceProperty2.setRules(rules3);
+ instanceIdToValue.put("node2", instanceProperty2);
+
+ propertyOperation.updatePropertyByBestMatch(propertyUniqueId, instanceProperty, instanceIdToValue);
+
+ assertEquals("check value", propertyRule2.getValue(), instanceProperty.getValue());
+ assertEquals("check default value", "vv1", instanceProperty.getDefaultValue());
+
+ }
+
+ // add all rule types
+ // add rule with size = 1(instance itself = ALL). relevant for VLi. equals
+ // to X.*.*.* in all paths size
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperationTest.java
new file mode 100644
index 0000000000..fe4b501148
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/RequirementOperationTest.java
@@ -0,0 +1,236 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.CapabiltyInstance;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.RequirementImplDef;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.RequirementOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.operations.impl.util.PrintGraph;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class RequirementOperationTest extends ModelTestBase {
+ private static Logger log = LoggerFactory.getLogger(RequirementOperationTest.class.getName());
+ private Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ private static String USER_ID = "muserId";
+ private static String CATEGORY_NAME = "category/mycategory";
+
+ private static ConfigurationManager configurationManager;
+
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "requirement-operation")
+ private RequirementOperation requirementOperation;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource(name = "capability-operation")
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ // configurationManager = new ConfigurationManager(
+ // new ConfigurationSource() {
+ //
+ // @Override
+ // public <T> T getAndWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T> void addWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ // });
+ //
+ // Configuration configuration = new Configuration();
+ // configuration.setTitanInMemoryGraph(true);
+ //
+ // configurationManager.setConfiguration(configuration);
+ ModelTestBase.init();
+ }
+
+ @Test
+ public void testDummy() {
+
+ assertTrue(requirementOperation != null);
+
+ }
+
+ @Test
+ public void testAddRequirementNotExistCapability() {
+
+ String reqName = "host";
+ RequirementDefinition reqDefinition = new RequirementDefinition();
+ reqDefinition.setNode("tosca.nodes.Compute");
+ reqDefinition.setRelationship("myrelationship");
+ reqDefinition.setCapability("mycapability___2");
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource", "0.1", null,
+ true, true);
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = requirementOperation
+ .addRequirementToResource(reqName, reqDefinition, resource.getUniqueId());
+ assertEquals("check error", StorageOperationStatus.INVALID_ID, addRequirementToResource.right().value());
+
+ }
+
+ @Before
+ public void createUserAndCategory() {
+ String[] category = CATEGORY_NAME.split("/");
+ OperationTestsUtil.deleteAndCreateResourceCategory(category[0], category[1], titanDao);
+ deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID);
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+ @Test
+ public void testAddRequirementWithCapability() {
+
+ String capabilityTypeName = "tosca.nodes.Container";
+
+ String reqName = "host";
+ RequirementDefinition reqDefinition = new RequirementDefinition();
+ reqDefinition.setNode("tosca.nodes.Compute");
+ reqDefinition.setRelationship("myrelationship");
+ reqDefinition.setCapability(capabilityTypeName);
+
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+
+ capabilityTypeOperationTest.createCapability(capabilityTypeName);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource", "2.0", null,
+ true, true);
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = requirementOperation
+ .addRequirementToResource(reqName, reqDefinition, resource.getUniqueId());
+
+ assertEquals("check requirement was added", true, addRequirementToResource.isLeft());
+
+ Either<Resource, StorageOperationStatus> resource2 = resourceOperation.getResource(resource.getUniqueId());
+ String json = prettyGson.toJson(resource2);
+ log.debug(json);
+ }
+
+ private void compareProperties(Map<String, PropertyDefinition> capabilityProperties,
+ CapabiltyInstance capabiltyInstance, Map<String, String> actual) {
+
+ Map<String, String> properties = capabiltyInstance.getProperties();
+
+ for (Entry<String, PropertyDefinition> entry : capabilityProperties.entrySet()) {
+ String paramName = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ String defaultValue = propertyDefinition.getDefaultValue();
+
+ String value = properties.get(paramName);
+
+ String actualValue = null;
+ if (actual != null) {
+ actualValue = actual.get(paramName);
+ }
+ if (actualValue != null) {
+ assertEquals("check property value of key " + paramName, value, actualValue);
+ } else {
+ assertEquals("check property value of key " + paramName, value, defaultValue);
+ }
+ }
+
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceInstanceOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceInstanceOperationTest.java
new file mode 100644
index 0000000000..bef51f415c
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceInstanceOperationTest.java
@@ -0,0 +1,2511 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.HeatParameterDefinition;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.RelationshipImpl;
+import org.openecomp.sdc.be.model.RequirementAndRelationshipPair;
+import org.openecomp.sdc.be.model.RequirementCapabilityRelDef;
+import org.openecomp.sdc.be.model.RequirementDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ArtifactOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.ComponentInstanceOperation;
+import org.openecomp.sdc.be.model.operations.impl.HeatParametersOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.RequirementOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.operations.impl.util.PrintGraph;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.HeatParameterData;
+import org.openecomp.sdc.be.resources.data.HeatParameterValueData;
+import org.openecomp.sdc.be.resources.data.RelationshipInstData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.ServiceMetadataData;
+import org.openecomp.sdc.be.resources.data.UniqueIdData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class ResourceInstanceOperationTest extends ModelTestBase {
+ private static Logger log = LoggerFactory.getLogger(ResourceInstanceOperationTest.class.getName());
+ private Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+
+ private static String USER_ID = "muserId";
+ private static String CATEGORY_NAME = "category/mycategory";
+
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "requirement-operation")
+ private RequirementOperation requirementOperation;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource(name = "capability-operation")
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @javax.annotation.Resource(name = "component-instance-operation")
+ private ComponentInstanceOperation resourceInstanceOperation;
+
+ @javax.annotation.Resource
+ private HeatParametersOperation heatParameterOperation;
+
+ @javax.annotation.Resource
+ private ArtifactOperation artifactOperation;
+
+ private String CAPABILITY_1 = "mycapability101";
+ private String CAPABILITY_2 = "mycapability102";
+
+ private Integer TEST_CLASS_NUMBER = 1;
+
+ public final static Pattern COMPONENT_NAME_DELIMETER_PATTERN = Pattern.compile("[\\.\\-\\_]+");
+
+ public final static Pattern COMPONENT_INCTANCE_NAME_DELIMETER_PATTERN = Pattern.compile("[\\.\\-\\_]+");
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ // configurationManager = new ConfigurationManager(
+ // new ConfigurationSource() {
+ //
+ // @Override
+ // public <T> T getAndWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T> void addWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ // });
+ //
+ // Configuration configuration = new Configuration();
+ //
+ // ////inmemory
+ // boolean useInMemory = true;
+ // if (useInMemory) {
+ // configuration.setTitanInMemoryGraph(true);
+ // } else {
+ // configuration.setTitanInMemoryGraph(false);
+ // configuration.setTitanCfgFile("C:\\Git_work\\D2-SDnC\\catalog-be\\src\\main\\resources\\config\\titan.properties");
+ // }
+ //
+ //
+ //
+ // configurationManager.setConfiguration(configuration);
+ ModelTestBase.init();
+ }
+
+ public void setOperations(TitanGenericDao titanDao, CapabilityTypeOperation capabilityTypeOperation,
+ RequirementOperation requirementOperation, CapabilityOperation capabilityOperation,
+ ResourceOperation resourceOperation, PropertyOperation propertyOperation,
+ ComponentInstanceOperation resourceInstanceOperation2) {
+ this.titanDao = titanDao;
+ this.capabilityTypeOperation = capabilityTypeOperation;
+ this.capabilityOperation = capabilityOperation;
+ this.requirementOperation = requirementOperation;
+ this.resourceOperation = resourceOperation;
+ this.propertyOperation = propertyOperation;
+ this.resourceInstanceOperation = resourceInstanceOperation2;
+ }
+
+ @Test
+ public void testDummy() {
+
+ assertTrue(requirementOperation != null);
+
+ }
+
+ @Test
+ public void testAddResourceInstanceInvalidServiceId() {
+
+ try {
+ ComponentInstance instance = buildResourceInstance("tosca.nodes.Apache.2.0", "1", "tosca.nodes.Apache");
+
+ Either<ComponentInstance, TitanOperationStatus> status = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent("service1", NodeTypeEnum.Service, "1", true, instance,
+ NodeTypeEnum.Resource, false);
+ assertEquals("check failed status - service is not in graph", true, status.isRight());
+ assertEquals("check failed status value - service is not in graph", TitanOperationStatus.INVALID_ID,
+ status.right().value());
+ } finally {
+ titanDao.rollback();
+ }
+
+ }
+
+ @Test
+ public void testAddResourceInstanceValidServiceIdInvalidResourceId() {
+ try {
+
+ ServiceMetadataData serviceData1 = createService("myservice1.1.0");
+
+ ComponentInstance instance = buildResourceInstance("tosca.nodes.Apache.2.0", "1", "tosca.nodes.Apache");
+
+ Either<ComponentInstance, TitanOperationStatus> status = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent((String) serviceData1.getUniqueId(), NodeTypeEnum.Service,
+ "1", true, instance, NodeTypeEnum.Resource, false);
+
+ assertEquals("check failed status - service is not in graph", true, status.isRight());
+ assertEquals("check failed status value - service is not in graph", TitanOperationStatus.INVALID_ID,
+ status.right().value());
+
+ } finally {
+ titanDao.rollback();
+ }
+
+ }
+
+ @Test
+ public void testAddResourceInstanceValidServiceId() {
+ try {
+ String serviceName = "myservice1.1.0";
+ String resourceName = "tosca.nodes.Apache.2.0";
+ ServiceMetadataData serviceData1 = createService(serviceName);
+ ResourceMetadataData resourceData = createResource(resourceName);
+
+ ComponentInstance instance = buildResourceInstance(resourceData.getMetadataDataDefinition().getUniqueId(),
+ "1", "tosca.nodes.Apache");
+
+ Either<ComponentInstance, TitanOperationStatus> status = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent((String) serviceData1.getUniqueId(), NodeTypeEnum.Service,
+ "1", true, instance, NodeTypeEnum.Resource, false);
+
+ assertEquals("check success status - service is not in graph", true, status.isLeft());
+
+ ComponentInstance value = status.left().value();
+ assertEquals("check name exists", "tosca.nodes.Apache 1", value.getName());
+
+ ServiceMetadataData serviceData2 = deleteService(serviceName);
+ ResourceMetadataData resourceData2 = deleteResource(resourceName);
+
+ } finally {
+ titanDao.rollback();
+ }
+ }
+
+ @Test
+ public void testUpdateResourceInstance() {
+ try {
+ String serviceName = "myservice1.1.0";
+ String resourceName = "tosca.nodes.Apache.2.0";
+ ServiceMetadataData serviceData1 = createService(serviceName);
+ ResourceMetadataData resourceData = createResource(resourceName);
+
+ ComponentInstance instance = buildResourceInstance(resourceData.getMetadataDataDefinition().getUniqueId(),
+ "1", "tosca.nodes.Apache");
+
+ Either<ComponentInstance, TitanOperationStatus> status = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent((String) serviceData1.getUniqueId(), NodeTypeEnum.Service,
+ "1", true, instance, NodeTypeEnum.Resource, false);
+
+ ComponentInstance resourceInstance = status.left().value();
+ Long creationTime = resourceInstance.getCreationTime();
+ String name = resourceInstance.getName();
+ assertEquals("check success status - service is not in graph", true, status.isLeft());
+
+ ComponentInstance value = status.left().value();
+ assertEquals("check name exists", "tosca.nodes.Apache 1", value.getName());
+
+ Either<ComponentInstance, StorageOperationStatus> u1Res = resourceInstanceOperation.updateResourceInstance(
+ (String) serviceData1.getUniqueId(), NodeTypeEnum.Service, resourceInstance.getUniqueId(), value,
+ true);
+ assertTrue("check update succeed", u1Res.isLeft());
+
+ Long lastModificationTimeNC = value.getModificationTime();
+ String desc = "AAAAA";
+ String posX = "15";
+ String posY = "12";
+ String updatedName = "Shlokshlik";
+ value.setDescription(desc);
+ value.setPosX(posX);
+ Either<ComponentInstance, StorageOperationStatus> u2Res = resourceInstanceOperation.updateResourceInstance(
+ (String) serviceData1.getUniqueId(), NodeTypeEnum.Service, resourceInstance.getUniqueId(), value,
+ true);
+ assertTrue("check update succeed", u2Res.isLeft());
+ assertEquals("check resource instance updated", desc, u2Res.left().value().getDescription());
+ assertEquals("check resource instance updated", posX, u2Res.left().value().getPosX());
+ assertEquals("check resource instance updated", resourceInstance.getPosY(), u2Res.left().value().getPosY());
+ assertEquals("check modification time was not updated since it was supplied",
+ u2Res.left().value().getModificationTime(), lastModificationTimeNC);
+
+ Long lastModificationTime = value.getModificationTime();
+ value.setPosY(posY);
+ value.setModificationTime(null);
+ value.setName(updatedName);
+ Either<ComponentInstance, StorageOperationStatus> u3Res = resourceInstanceOperation.updateResourceInstance(
+ (String) serviceData1.getUniqueId(), NodeTypeEnum.Service, resourceInstance.getUniqueId(), value,
+ true);
+ assertTrue("check update succeed", u3Res.isLeft());
+ assertEquals("check resource instance updated", desc, u3Res.left().value().getDescription());
+ assertEquals("check resource pos x updated", posX, u3Res.left().value().getPosX());
+ assertEquals("check resource pos y updated", posY, u3Res.left().value().getPosY());
+ assertTrue("check modification time was updated",
+ u3Res.left().value().getModificationTime() >= lastModificationTime);
+ assertEquals("check creation time was not updated", creationTime, u3Res.left().value().getCreationTime());
+ assertEquals("check name was updated", updatedName, u3Res.left().value().getName());
+
+ ServiceMetadataData serviceData2 = deleteService(serviceName);
+ ResourceMetadataData resourceData2 = deleteResource(resourceName);
+
+ } finally {
+ titanDao.rollback();
+ }
+ }
+
+ @Test
+ public void testRemoveResourceInstance() {
+ try {
+ String serviceName = "myservice1.1.0";
+ String resourceName = "tosca.nodes.Apache.2.0";
+ ServiceMetadataData serviceData1 = createService(serviceName);
+ ResourceMetadataData resourceData = createResource(resourceName);
+
+ ComponentInstance instance = buildResourceInstance(resourceData.getMetadataDataDefinition().getUniqueId(),
+ "1", "tosca.nodes.Apache");
+
+ Either<ComponentInstance, TitanOperationStatus> status = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent((String) serviceData1.getUniqueId(), NodeTypeEnum.Service,
+ "1", true, instance, NodeTypeEnum.Resource, false);
+
+ assertEquals("check success status - service is not in graph", true, status.isLeft());
+
+ ComponentInstance value = status.left().value();
+ assertEquals("check name exists", "tosca.nodes.Apache 1", value.getName());
+
+ Either<ComponentInstance, TitanOperationStatus> status1 = resourceInstanceOperation
+ .removeComponentInstanceFromComponent(NodeTypeEnum.Service, serviceName, value.getUniqueId());
+
+ assertTrue("check resource service was deleted.", status1.isLeft());
+ assertEquals("check resource instance returned.", "tosca.nodes.Apache 1", status1.left().value().getName());
+
+ ServiceMetadataData serviceData2 = deleteService(serviceName);
+ ResourceMetadataData resourceData2 = deleteResource(resourceName);
+
+ } finally {
+ titanDao.rollback();
+ }
+ }
+
+ @Test
+ public void testRemoveResourceInstanceNotFound() {
+ try {
+ String serviceName = "myservice1.1.0";
+ ServiceMetadataData serviceData1 = createService(serviceName);
+
+ Either<ComponentInstance, TitanOperationStatus> status1 = resourceInstanceOperation
+ .removeComponentInstanceFromComponent(NodeTypeEnum.Service, serviceName, "stam");
+
+ assertTrue("check resource service was not deleted.", status1.isRight());
+ assertEquals("check NOT_FOUND returned.", TitanOperationStatus.NOT_FOUND, status1.right().value());
+
+ ServiceMetadataData serviceData2 = deleteService(serviceName);
+
+ } finally {
+ titanDao.rollback();
+ }
+ }
+
+ public ServiceMetadataData createService(String serviceName) {
+
+ ServiceMetadataData serviceData1 = new ServiceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(serviceName);
+ Either<ServiceMetadataData, TitanOperationStatus> createNode = titanDao.createNode(serviceData1,
+ ServiceMetadataData.class);
+
+ assertTrue("check service created", createNode.isLeft());
+ return createNode.left().value();
+ }
+
+ public ServiceMetadataData deleteService(String serviceName) {
+
+ ServiceMetadataData serviceData1 = new ServiceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(serviceName);
+ Either<ServiceMetadataData, TitanOperationStatus> createNode = titanDao.deleteNode(serviceData1,
+ ServiceMetadataData.class);
+ assertTrue("check service deleted", createNode.isLeft());
+ return createNode.left().value();
+ }
+
+ public ResourceMetadataData createResource(String resourceName) {
+
+ ResourceMetadataData serviceData1 = new ResourceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(resourceName);
+ Either<ResourceMetadataData, TitanOperationStatus> createNode = titanDao.createNode(serviceData1,
+ ResourceMetadataData.class);
+
+ assertTrue("check service created", createNode.isLeft());
+ return createNode.left().value();
+ }
+
+ public ResourceMetadataData deleteResource(String resourceName) {
+
+ ResourceMetadataData serviceData1 = new ResourceMetadataData();
+ serviceData1.getMetadataDataDefinition().setUniqueId(resourceName);
+ Either<ResourceMetadataData, TitanOperationStatus> createNode = titanDao.deleteNode(serviceData1,
+ ResourceMetadataData.class);
+
+ assertTrue("check service created", createNode.isLeft());
+ return createNode.left().value();
+ }
+
+ @Test
+ public void testAddResourceInstanceJson() {
+ addResourceInstanceJson();
+ }
+
+ public ComponentInstance addResourceInstanceJson() {
+
+ ComponentInstance resourceInstance = buildResourceInstance("tosca.nodes.Apache.2.0", "1", "tosca.nodes.Apache");
+
+ String json = prettyGson.toJson(resourceInstance);
+ log.debug(json);
+
+ return resourceInstance;
+
+ }
+
+ private ComponentInstance buildResourceInstance(String respurceUid, String instanceNumber, String name) {
+ ComponentInstance resourceInstance = new ComponentInstance();
+ // resourceInstance
+ // .setUniqueId("<SN>.tosca.nodes.Apache.2.0." + instanceNumber);
+ resourceInstance.setName(name);
+ resourceInstance.setDescription("desc1");
+ resourceInstance.setPosX("20");
+ resourceInstance.setPosY("40");
+ resourceInstance.setComponentUid(respurceUid);
+ resourceInstance.setCreationTime(System.currentTimeMillis());
+ resourceInstance.setModificationTime(System.currentTimeMillis());
+ resourceInstance.setNormalizedName(normaliseComponentName(name));
+
+ // Map<String, RequirementInstance> requirements = new HashMap<String,
+ // RequirementInstance>();
+ //
+ // RequirementInstance requirementInstance1 = new RequirementInstance();
+ // requirementInstance1.setNode("NA");
+ // RelationshipImpl relationshipImpl = new RelationshipImpl();
+ // relationshipImpl.setType("tosca.relationships.HostedOn");
+ // requirementInstance1.setRelationship(relationshipImpl);
+ //
+ // requirements.put("host", requirementInstance1);
+ //
+ // RequirementInstance requirementInstance2 = new RequirementInstance();
+ // requirementInstance2.setNode("NA");
+ // RelationshipImpl relationshipImpl2 = new RelationshipImpl();
+ // relationshipImpl2.setType("tosca.relationships.LinkTo");
+ // requirementInstance2.setRelationship(relationshipImpl2);
+ //
+ // requirements.put("link", requirementInstance2);
+ //
+ // resourceInstance.setRequirements(requirements);
+ return resourceInstance;
+ }
+
+ @Test
+ public void testConenctResourceInstancesJson() {
+ RequirementCapabilityRelDef addRelationship = addRelationship("apache_1", "compute_100");
+ String json = prettyGson.toJson(addRelationship);
+ log.debug(json);
+
+ RequirementCapabilityRelDef capabilityRelDef = prettyGson.fromJson(json, RequirementCapabilityRelDef.class);
+ log.debug("{}", capabilityRelDef);
+
+ }
+
+ public RequirementCapabilityRelDef addRelationship(String from, String to) {
+ RequirementCapabilityRelDef requirementCapabilityRelDef = new RequirementCapabilityRelDef();
+ requirementCapabilityRelDef.setFromNode(from);
+ requirementCapabilityRelDef.setToNode(to);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<RequirementAndRelationshipPair>();
+
+ String req = "host";
+ RelationshipImpl relationshipImpl = new RelationshipImpl();
+ relationshipImpl.setType("tosca.nodes.HostedOn");
+ RequirementAndRelationshipPair rels = new RequirementAndRelationshipPair(req, relationshipImpl);
+ relationships.add(rels);
+
+ requirementCapabilityRelDef.setRelationships(relationships);
+
+ return requirementCapabilityRelDef;
+ }
+
+ @Before
+ public void createUserAndCategory() {
+ deleteAndCreateCategory(CATEGORY_NAME);
+ deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID);
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanDao);
+ OperationTestsUtil.deleteAndCreateServiceCategory(category, titanDao);
+
+ /*
+ * CategoryData categoryData = new CategoryData();
+ * categoryData.setName(category);
+ *
+ * titanDao.deleteNode(categoryData, CategoryData.class);
+ * Either<CategoryData, TitanOperationStatus> createNode = titanDao
+ * .createNode(categoryData, CategoryData.class);
+ * System.out.println("after creating caetgory " + createNode);
+ */
+ }
+
+ @Test
+ @Ignore
+ public void testConnectResourceInstances() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+ try {
+
+ String capabilityTypeName = CAPABILITY_2;
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName);
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ // rollbackAndPrint();
+
+ // Add capabilities to Compute Resource
+ CapabilityDefinition addCapability = addCapabilityToResource(capabilityTypeName, "host", computeComponent);
+
+ // CapabilityDefinition capabilityDefinition = new
+ // CapabilityDefinition();
+ // capabilityDefinition.setDescription("my capability");
+ // capabilityDefinition.setType(capabilityTypeName);
+ // List<String> validSourceTypes = new ArrayList<String>();
+ // validSourceTypes.add("tosca.nodes.SC");
+ // capabilityDefinition.setValidSourceTypes(validSourceTypes);
+ // Either<CapabilityDefinition, StorageOperationStatus>
+ // addCapability = capabilityOperation
+ // .addCapability(computeComponent.getUniqueId(), "host",
+ // capabilityDefinition, true);
+ // //logger.debug("addCapability result " + addCapability);
+ // assertTrue("check capability created ", addCapability.isLeft());
+ //
+ // =============================================
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeName, reqName, reqNodeName, reqRelationship, softwareComponent);
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceName = "myservice.1.0";
+ List<ComponentInstance> resInstances = buildServiceAndConnectBetweenResourceInstances(serviceName, resource,
+ computeComponent, "host", false, addCapability.getUniqueId(),
+ addRequirementToResource.left().value().getUniqueId());
+
+ PrintGraph printGraph = new PrintGraph();
+ String webGraph = printGraph.buildGraphForWebgraphWiz(titanDao.getGraph().left().value());
+ log.debug(webGraph);
+
+ Either<Resource, StorageOperationStatus> resourceFull = resourceOperation
+ .getResource(resource.getUniqueId());
+ assertTrue(resourceFull.isLeft());
+ List<RequirementCapabilityRelDef> componentInstancesRelations = resourceFull.left().value()
+ .getComponentInstancesRelations();
+
+ RequirementCapabilityRelDef capabilityRelDef = componentInstancesRelations.get(0);
+ capabilityRelDef.getRelationships().get(0).setRequirement("host");
+
+ // disconnectResourcesInService(serviceName, resInstances.get(0),
+ // "host");
+ disconnectResourcesInService(serviceName, capabilityRelDef);
+
+ } finally {
+ rollbackAndPrint(false);
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ @Test
+ @Ignore
+ public void testConnectResourceInstances1Requirement2Capabilities() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ try {
+
+ String capabilityTypeName1 = CAPABILITY_1;
+ String capabilityTypeName2 = CAPABILITY_2;
+ String reqName1 = "host1";
+ String reqName2 = "host2";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef1 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName1);
+ CapabilityTypeDefinition createCapabilityDef2 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName2);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ // Add capabilities to Compute Resource
+ CapabilityDefinition capabilty1 = addCapabilityToResource(capabilityTypeName1, reqName1, computeComponent);
+ CapabilityDefinition capabilty2 = addCapabilityToResource(capabilityTypeName2, reqName2, computeComponent);
+
+ // rollbackAndPrint();
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeName1, reqName1, reqNodeName, reqRelationship, softwareComponent);
+
+ String requirementId = addRequirementToResource.left().value().getUniqueId();
+ String parentReqUniqId = requirementId;
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceName = "myservice.1.0";
+ List<ComponentInstance> resInstances = buildServiceAndConnectBetweenResourceInstances(serviceName, resource,
+ computeComponent, reqName1, false, capabilty1.getUniqueId(), requirementId);
+
+ PrintGraph printGraph = new PrintGraph();
+ String webGraph = printGraph.buildGraphForWebgraphWiz(titanDao.getGraph().left().value());
+ log.debug(webGraph);
+
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirement(reqName2);
+
+ relationPair.setCapabilityUid(capabilty1.getUniqueId());
+ relationPair.setRequirementUid(requirementId);
+
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService1 = resourceInstanceOperation
+ .connectResourcesInService(serviceName, NodeTypeEnum.Service, resInstances.get(0).getUniqueId(),
+ resInstances.get(1).getUniqueId(), relationPair);
+ assertEquals("check cannot associate resource instances", TitanOperationStatus.ILLEGAL_ARGUMENT,
+ connectResourcesInService1.right().value());
+ relationPair.setRequirement(reqName1);
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService2 = resourceInstanceOperation
+ .connectResourcesInService(serviceName, NodeTypeEnum.Service, resInstances.get(0).getUniqueId(),
+ resInstances.get(1).getUniqueId(), relationPair);
+ assertEquals("check cannot associate resource instances", TitanOperationStatus.TITAN_SCHEMA_VIOLATION,
+ connectResourcesInService2.right().value());
+
+ relationPair.setRequirement(reqName1);
+
+ RequirementCapabilityRelDef capabilityRelDef = new RequirementCapabilityRelDef();
+ capabilityRelDef.setFromNode(resInstances.get(0).getUniqueId());
+ capabilityRelDef.setToNode(resInstances.get(1).getUniqueId());
+ List<RequirementAndRelationshipPair> list = new ArrayList<>();
+ list.add(relationPair);
+
+ disconnectResourcesInService(serviceName, capabilityRelDef);
+
+ } finally {
+ rollbackAndPrint();
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ private void rollbackAndPrint() {
+ rollbackAndPrint(false);
+ }
+
+ private void rollbackAndPrint(boolean print) {
+ TitanOperationStatus rollback = titanDao.rollback();
+ if (print) {
+ log.debug("rollback status={}", rollback);
+ PrintGraph printGraph = new PrintGraph();
+ printGraph.printGraphVertices(titanDao.getGraph().left().value());
+ }
+ }
+
+ @Test
+ public void testConnectResourceInstances2Requirement2Capabilities() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ try {
+
+ String capabilityTypeName1 = CAPABILITY_1;
+ String capabilityTypeName2 = CAPABILITY_2;
+ String reqName1 = "host1";
+ String reqName2 = "host2";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef1 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName1);
+ CapabilityTypeDefinition createCapabilityDef2 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName2);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // rollbackAndPrint();
+ // OKKKKKKK
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ // rollbackAndPrint();
+ // OKKKKKKKKKK
+
+ // Add capabilities to Compute Resource
+ CapabilityDefinition capabilty1 = addCapabilityToResource(capabilityTypeName1, reqName1, computeComponent);
+ CapabilityDefinition capabilty2 = addCapabilityToResource(capabilityTypeName2, reqName2, computeComponent);
+
+ // rollbackAndPrint();
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource1 = addRequirementToResource(
+ capabilityTypeName1, reqName1, reqNodeName, reqRelationship, softwareComponent);
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource2 = addRequirementToResource(
+ capabilityTypeName2, reqName2, reqNodeName, reqRelationship, softwareComponent);
+
+ // create my resource derived from software component
+ String MY_RESOURCE = "my-resource";
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, MY_RESOURCE,
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceName = "myservice.1.0";
+ String requirementId1 = addRequirementToResource1.left().value().getUniqueId();
+ String requirementId2 = addRequirementToResource2.left().value().getUniqueId();
+ List<ComponentInstance> resInstances = buildServiceAndConnectBetweenResourceInstances(serviceName, resource,
+ computeComponent, reqName1, false, capabilty1.getUniqueId(), requirementId1);
+
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirement(reqName2);
+ relationPair.setCapabilityUid(capabilty2.getUniqueId());
+ relationPair.setRequirementUid(requirementId2);
+ relationPair.setCapabilityOwnerId(resInstances.get(1).getUniqueId());
+ relationPair.setRequirementOwnerId(resInstances.get(0).getUniqueId());
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService1 = resourceInstanceOperation
+ .connectResourcesInService(serviceName, NodeTypeEnum.Service, resInstances.get(0).getUniqueId(),
+ resInstances.get(1).getUniqueId(), relationPair);
+ assertTrue("check associate resource instances succeed " + reqName2, connectResourcesInService1.isLeft());
+
+ // rollbackAndPrint();
+
+ PrintGraph printGraph = new PrintGraph();
+ String webGraph = printGraph.buildGraphForWebgraphWiz(titanDao.getGraph().left().value());
+ log.debug(webGraph);
+
+ RequirementCapabilityRelDef reqCapDef = new RequirementCapabilityRelDef();
+ reqCapDef.setFromNode(resInstances.get(0).getUniqueId());
+ reqCapDef.setToNode(resInstances.get(1).getUniqueId());
+
+ relationPair.setRequirement(reqName1);
+ relationPair.setCapabilityUid(capabilty1.getUniqueId());
+ relationPair.setRequirementUid(requirementId1);
+ RelationshipImpl relationship = new RelationshipImpl();
+ relationship.setType(reqName1);
+ relationPair.setRelationships(relationship);
+
+ List<RequirementAndRelationshipPair> list = new ArrayList<>();
+ list.add(relationPair);
+ reqCapDef.setRelationships(list);
+
+ disconnectResourcesInService(serviceName, reqCapDef);
+
+ reqCapDef.getRelationships().clear();
+
+ RequirementAndRelationshipPair relationPair1 = new RequirementAndRelationshipPair();
+ relationPair1.setRequirement(reqName2);
+ relationPair1.setCapabilityUid(capabilty2.getUniqueId());
+ relationPair1.setRequirementUid(requirementId2);
+ relationPair1.setCapabilityOwnerId(resInstances.get(1).getUniqueId());
+ relationPair1.setRequirementOwnerId(resInstances.get(0).getUniqueId());
+ relationship.setType(reqName2);
+ relationPair1.setRelationships(relationship);
+ reqCapDef.getRelationships().add(relationPair1);
+
+ disconnectResourcesInService(serviceName, reqCapDef);
+
+ RequirementCapabilityRelDef relation = new RequirementCapabilityRelDef();
+ String fromResUid = resInstances.get(0).getUniqueId();
+ String toResUid = resInstances.get(1).getUniqueId();
+ relation.setFromNode(fromResUid);
+ relation.setToNode(toResUid);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<RequirementAndRelationshipPair>();
+ RequirementAndRelationshipPair immutablePair1 = new RequirementAndRelationshipPair(reqName1, null);
+ RequirementAndRelationshipPair immutablePair2 = new RequirementAndRelationshipPair(reqName2, null);
+ immutablePair1.setCapabilityUid(capabilty1.getUniqueId());
+ immutablePair1.setRequirementUid(addRequirementToResource1.left().value().getUniqueId());
+ immutablePair1.setRequirementOwnerId(resInstances.get(0).getUniqueId());
+ immutablePair1.setCapabilityOwnerId(resInstances.get(1).getUniqueId());
+
+ immutablePair2.setCapabilityUid(capabilty2.getUniqueId());
+ immutablePair2.setRequirementUid(addRequirementToResource2.left().value().getUniqueId());
+ immutablePair2.setRequirementOwnerId(resInstances.get(0).getUniqueId());
+ immutablePair2.setCapabilityOwnerId(resInstances.get(1).getUniqueId());
+
+ relationships.add(immutablePair1);
+ relationships.add(immutablePair2);
+ relation.setRelationships(relationships);
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> associateResourceInstances = resourceInstanceOperation
+ .associateResourceInstances(serviceName, NodeTypeEnum.Service, relation, true);
+ assertTrue("check return code after associating 2 requirements in one request",
+ associateResourceInstances.isLeft());
+ RequirementCapabilityRelDef capabilityRelDef = associateResourceInstances.left().value();
+ String fromNode = capabilityRelDef.getFromNode();
+ assertEquals("check from node", resInstances.get(0).getUniqueId(), fromNode);
+ String toNode = capabilityRelDef.getToNode();
+ assertEquals("check to node", resInstances.get(1).getUniqueId(), toNode);
+ List<RequirementAndRelationshipPair> relationships2 = capabilityRelDef.getRelationships();
+ assertEquals("check number of relations", 2, relationships2.size());
+
+ for (RequirementAndRelationshipPair pair : relationships2) {
+ String key = pair.getRequirement();
+ RelationshipImpl relationshipImpl = pair.getRelationship();
+ if (key.equals(reqName1)) {
+ String type = relationshipImpl.getType();
+ assertEquals("Check relationship type name", reqRelationship, type);
+ } else if (key.equals(reqName2)) {
+ String type = relationshipImpl.getType();
+ assertEquals("Check relationship type name", reqRelationship, type);
+ } else {
+ assertTrue("requirement " + key + " was not found in the original request", false);
+ }
+ }
+
+ verifyGetAllResourceInstanceFromService(reqName1, reqName2, serviceName, fromResUid, toResUid);
+
+ List<ResourceMetadataData> resourcesPathList = new ArrayList<ResourceMetadataData>();
+ TitanOperationStatus findResourcesPathRecursively = resourceOperation
+ .findResourcesPathRecursively(resource.getUniqueId(), resourcesPathList);
+ assertEquals("check returned status", TitanOperationStatus.OK, findResourcesPathRecursively);
+ assertEquals("check list size", 3, resourcesPathList.size());
+
+ TitanOperationStatus validateTheTargetResourceInstance = resourceInstanceOperation
+ .validateTheTargetResourceInstance(MY_RESOURCE, resource.getUniqueId());
+ assertEquals("check resource name in the path", TitanOperationStatus.OK, validateTheTargetResourceInstance);
+ validateTheTargetResourceInstance = resourceInstanceOperation
+ .validateTheTargetResourceInstance(softwareCompName, resource.getUniqueId());
+ assertEquals("check resource name in the path", TitanOperationStatus.OK, validateTheTargetResourceInstance);
+
+ validateTheTargetResourceInstance = resourceInstanceOperation
+ .validateTheTargetResourceInstance(softwareCompName + "STAM", resource.getUniqueId());
+ assertEquals("check resource name not in the path", TitanOperationStatus.MATCH_NOT_FOUND,
+ validateTheTargetResourceInstance);
+
+ Either<ComponentInstance, StorageOperationStatus> deleteResourceInstance = resourceInstanceOperation
+ .deleteComponentInstance(NodeTypeEnum.Service, serviceName, toResUid, true);
+ assertTrue("check resource instance was deleted.", deleteResourceInstance.isLeft());
+
+ } finally {
+ rollbackAndPrint(false);
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ private void verifyGetAllResourceInstanceFromService(String reqName1, String reqName2, String serviceName,
+ String fromResUid, String toResUid) {
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, StorageOperationStatus> allResourceInstances = resourceInstanceOperation
+ .getAllComponentInstances(serviceName, NodeTypeEnum.Service, NodeTypeEnum.Resource, true);
+ // assertTrue("check return code after get all resource instances",
+ // associateResourceInstances.isLeft());
+ ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>> immutablePair = allResourceInstances
+ .left().value();
+ List<ComponentInstance> nodes = immutablePair.getKey();
+ List<RequirementCapabilityRelDef> edges = immutablePair.getValue();
+ assertEquals("check 2 nodes returned", 2, nodes.size());
+ assertEquals("check one relation returned", 1, edges.size());
+ RequirementCapabilityRelDef requirementCapabilityRelDef = edges.get(0);
+ assertEquals("check from node", requirementCapabilityRelDef.getFromNode(), fromResUid);
+ requirementCapabilityRelDef.getToNode();
+ assertEquals("check to node", requirementCapabilityRelDef.getToNode(), toResUid);
+ int size = requirementCapabilityRelDef.getRelationships().size();
+ assertEquals("check number of relations", 2, size);
+ String req1 = requirementCapabilityRelDef.getRelationships().get(0).getRequirement();
+ String req2 = requirementCapabilityRelDef.getRelationships().get(1).getRequirement();
+
+ List<String> requirements = new ArrayList<String>();
+ requirements.add(req1);
+ requirements.add(req2);
+
+ assertTrue("check requirement returned " + reqName1, requirements.contains(reqName1));
+ assertTrue("check requirement returned " + reqName2, requirements.contains(reqName2));
+
+ String nodesStr = prettyGson.toJson(nodes);
+ String edgesStr = prettyGson.toJson(edges);
+
+ log.debug(nodesStr);
+ log.debug(edgesStr);
+ }
+
+ private Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource(String capabilityTypeName1,
+ String reqName1, String reqNodeName, String reqRelationship, Resource softwareComponent) {
+ RequirementDefinition reqDefinition1 = new RequirementDefinition();
+ reqDefinition1.setNode(reqNodeName);
+ reqDefinition1.setRelationship(reqRelationship);
+ reqDefinition1.setCapability(capabilityTypeName1);
+ // add requirement to software component
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = requirementOperation
+ .addRequirementToResource(reqName1, reqDefinition1, softwareComponent.getUniqueId(), true);
+ assertEquals("check requirement was added", true, addRequirementToResource.isLeft());
+ return addRequirementToResource;
+ }
+
+ private CapabilityDefinition addCapabilityToResource(String capabilityTypeName1, String reqName1,
+ Resource computeComponent) {
+ CapabilityDefinition capabilityDefinition1 = new CapabilityDefinition();
+ capabilityDefinition1.setDescription("my capability");
+ capabilityDefinition1.setType(capabilityTypeName1);
+ List<String> validSourceTypes = new ArrayList<String>();
+ validSourceTypes.add("tosca.nodes.SC");
+ capabilityDefinition1.setValidSourceTypes(validSourceTypes);
+ Either<CapabilityDefinition, StorageOperationStatus> addCapability = capabilityOperation
+ .addCapability(computeComponent.getUniqueId(), reqName1, capabilityDefinition1, true);
+ assertTrue("check capability created ", addCapability.isLeft());
+ return addCapability.left().value();
+ }
+
+ @Test
+ public void testConnectResourceInstancesCapabilityNameDiffFromReqName() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ try {
+
+ String capabilityTypeName = CAPABILITY_2;
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ String DIFFERENT_CAPABILITY = "hostDiffernet";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ CapabilityDefinition capabilty = addCapabilityToResource(capabilityTypeName, DIFFERENT_CAPABILITY,
+ computeComponent);
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeName, reqName, reqNodeName, reqRelationship, softwareComponent);
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceName = "myservice.1.0";
+ List<ComponentInstance> resInstances = buildServiceAndConnectBetweenResourceInstances(serviceName, resource,
+ computeComponent, "host", false, capabilty.getUniqueId(), parentReqUniqId);
+
+ PrintGraph printGraph = new PrintGraph();
+ String webGraph = printGraph.buildGraphForWebgraphWiz(titanDao.getGraph().left().value());
+ // log.debug(webGraph);
+
+ } finally {
+ rollbackAndPrint();
+
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ @Test
+ public void testConnectResourceInstancesInvalidCapability() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ try {
+
+ String capabilityTypeName = CAPABILITY_2;
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ String capabilityTypeNameOther = CAPABILITY_2 + "othertype";
+
+ String DIFFERENT_CAPABILITY = "hostDiffernet";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName);
+
+ CapabilityTypeDefinition createCapabilityDef2 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeNameOther);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ addCapabilityToResource(capabilityTypeName, DIFFERENT_CAPABILITY, computeComponent);
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeNameOther, reqName, reqNodeName, reqRelationship, softwareComponent);
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceName = "myservice.1.0";
+ List<ComponentInstance> resInstances = buildServiceAndConnectBetweenResourceInstancesWithError(serviceName,
+ resource, computeComponent, "host", false, TitanOperationStatus.ILLEGAL_ARGUMENT);
+
+ PrintGraph printGraph = new PrintGraph();
+ String webGraph = printGraph.buildGraphForWebgraphWiz(titanDao.getGraph().left().value());
+ log.debug(webGraph);
+
+ } finally {
+ rollbackAndPrint();
+
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ private void compareGraphSize(int numberOfVertices, Set<String> toRemoveFromSet) {
+ PrintGraph printGraph2 = new PrintGraph();
+ int numberOfVerticesCurr = printGraph2.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ Set<String> set = printGraph2.getVerticesSet(titanDao.getGraph().left().value());
+ if (toRemoveFromSet != null) {
+ set.removeAll(toRemoveFromSet);
+ }
+
+ assertEquals("check all data deleted from graph " + set, numberOfVertices, numberOfVerticesCurr);
+ }
+
+ private void compareGraphSize(int numberOfVertices) {
+ PrintGraph printGraph2 = new PrintGraph();
+ int numberOfVerticesCurr = printGraph2.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ assertEquals(
+ "check all data deleted from graph " + printGraph2.getVerticesSet(titanDao.getGraph().left().value()),
+ numberOfVertices, numberOfVerticesCurr);
+ }
+
+ @Test
+ public void testConnectResourceInstancesRequirementNotFound() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+ try {
+
+ String capabilityTypeName = CAPABILITY_2;
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ String DIFFERENT_CAPABILITY = "hostDiffernet";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ addCapabilityToResource(capabilityTypeName, reqName, computeComponent);
+
+ // create requirement definition
+
+ RequirementDefinition reqDefinition = new RequirementDefinition();
+ reqDefinition.setNode(reqNodeName);
+ reqDefinition.setRelationship(reqRelationship);
+ reqDefinition.setCapability(capabilityTypeName);
+ // add requirement to software component
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = requirementOperation
+ .addRequirementToResource(reqName + "ssssssss", reqDefinition, softwareComponent.getUniqueId(),
+ true);
+ assertEquals("check requirement was added", true, addRequirementToResource.isLeft());
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceName = "myservice.1.0";
+ List<ComponentInstance> resInstances = buildServiceAndConnectBetweenResourceInstancesWithError(serviceName,
+ resource, computeComponent, "host", false, TitanOperationStatus.ILLEGAL_ARGUMENT);
+
+ PrintGraph printGraph = new PrintGraph();
+ String webGraph = printGraph.buildGraphForWebgraphWiz(titanDao.getGraph().left().value());
+ log.debug(webGraph);
+
+ } finally {
+ titanDao.rollback();
+
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ private void disconnectResourcesInService(String serviceName, RequirementCapabilityRelDef reqCapDef) {
+
+ Either<List<RelationshipInstData>, TitanOperationStatus> disconnectResourcesInService = resourceInstanceOperation
+ .disconnectResourcesInService(serviceName, NodeTypeEnum.Service, reqCapDef);
+ assertTrue("check relatioship instance was deleted", disconnectResourcesInService.isLeft());
+
+ disconnectResourcesInService = resourceInstanceOperation.disconnectResourcesInService(serviceName,
+ NodeTypeEnum.Service, reqCapDef);
+ assertTrue("check relatioship instance already was deleted", disconnectResourcesInService.isRight());
+ assertEquals("check relatioship instance already was deleted. status NOT_FOUND", TitanOperationStatus.NOT_FOUND,
+ disconnectResourcesInService.right().value());
+ }
+
+ private List<ComponentInstance> buildServiceAndConnectBetweenResourceInstancesWithError(String serviceName,
+ Resource resource, Resource computeComponent, String requirement, boolean ignoreCreatingService,
+ TitanOperationStatus titanOperationStatus) {
+
+ String serviceId = "myservice.1.0";
+
+ if (false == ignoreCreatingService) {
+ ServiceMetadataData createService = createService(serviceId);
+ }
+ ComponentInstance myresourceInstance = buildResourceInstance(resource.getUniqueId(), "1", resource.getName());
+
+ ComponentInstance computeInstance = buildResourceInstance(computeComponent.getUniqueId(), "2",
+ computeComponent.getName());
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+ ComponentInstance value1 = myinstanceRes.left().value();
+ Either<ComponentInstance, TitanOperationStatus> computeInstTes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "2", true, computeInstance,
+ NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", computeInstTes.isLeft());
+ ComponentInstance value2 = computeInstTes.left().value();
+
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirement(requirement);
+
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService = resourceInstanceOperation
+ .connectResourcesInService(serviceId, NodeTypeEnum.Service, value1.getUniqueId(), value2.getUniqueId(),
+ relationPair);
+
+ assertTrue("check relation was not created", connectResourcesInService.isRight());
+ assertEquals("check error code after connect resource instances failed", titanOperationStatus,
+ connectResourcesInService.right().value());
+
+ List<ComponentInstance> resInstances = new ArrayList<ComponentInstance>();
+ resInstances.add(value1);
+
+ return resInstances;
+
+ }
+
+ private List<ComponentInstance> buildServiceAndConnectBetweenResourceInstances(String serviceName,
+ Resource resource, Resource computeComponent, String requirement, boolean ignoreCreatingService,
+ String capabilityId, String requirementId) {
+
+ String serviceId = "myservice.1.0";
+
+ if (false == ignoreCreatingService) {
+ ServiceMetadataData createService = createService(serviceId);
+ }
+ ComponentInstance myresourceInstance = buildResourceInstance(resource.getUniqueId(), "1", resource.getName());
+
+ ComponentInstance computeInstance = buildResourceInstance(computeComponent.getUniqueId(), "2",
+ computeComponent.getName());
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+ ComponentInstance value1 = myinstanceRes.left().value();
+ Either<ComponentInstance, TitanOperationStatus> computeInstTes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "2", true, computeInstance,
+ NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", computeInstTes.isLeft());
+ ComponentInstance value2 = computeInstTes.left().value();
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirement(requirement);
+
+ relationPair.setCapabilityUid(capabilityId);
+ relationPair.setRequirementUid(requirementId);
+ relationPair.setRequirementOwnerId(value1.getUniqueId());
+ relationPair.setCapabilityOwnerId(value2.getUniqueId());
+
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService = resourceInstanceOperation
+ .connectResourcesInService(serviceId, NodeTypeEnum.Service, value1.getUniqueId(), value2.getUniqueId(),
+ relationPair);
+
+ assertTrue("check relation created", connectResourcesInService.isLeft());
+
+ List<ComponentInstance> resInstances = new ArrayList<ComponentInstance>();
+ resInstances.add(value1);
+ resInstances.add(value2);
+
+ return resInstances;
+
+ }
+
+ @Test
+ public void getAllResourceInstancesThree() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+ try {
+
+ Set<String> vertexSetBeforeMethod = printGraph1.getVerticesSet(titanDao.getGraph().left().value());
+
+ String capabilityTypeName = CAPABILITY_2;
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ // rollbackAndPrint();
+
+ // Add capabilities to Compute Resource
+ CapabilityDefinition capability = addCapabilityToResource(capabilityTypeName, "host", computeComponent);
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeName, reqName, reqNodeName, reqRelationship, softwareComponent);
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceId = "myservice.1.0";
+
+ ServiceMetadataData createService = createService(serviceId);
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, StorageOperationStatus> allResourceInstances = resourceInstanceOperation
+ .getAllComponentInstances(serviceId, NodeTypeEnum.Service, NodeTypeEnum.Resource, true);
+ assertTrue("check NOT_FOUND is returned", allResourceInstances.isRight());
+ assertEquals("check NOT_FOUND is returned", allResourceInstances.right().value(),
+ StorageOperationStatus.NOT_FOUND);
+
+ ComponentInstance myresourceInstance = buildResourceInstance(resource.getUniqueId(), "1", "my-resource");
+ myresourceInstance.setName("my-resource");
+
+ ComponentInstance computeInstance1 = buildResourceInstance(computeComponent.getUniqueId(), "2",
+ "tosca.nodes.Compute2");
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+ ComponentInstance value1 = myinstanceRes.left().value();
+
+ allResourceInstances = resourceInstanceOperation.getAllComponentInstances(serviceId, NodeTypeEnum.Service,
+ NodeTypeEnum.Resource, true);
+ assertTrue("check resource instances found", allResourceInstances.isLeft());
+ ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>> immutablePair = allResourceInstances
+ .left().value();
+ List<ComponentInstance> nodes = immutablePair.getKey();
+ List<RequirementCapabilityRelDef> edges = immutablePair.getValue();
+
+ assertEquals("check resource instances size", 1, nodes.size());
+ assertEquals("check resource instances size", 0, edges.size());
+
+ Either<ComponentInstance, TitanOperationStatus> computeInstTes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "2", true,
+ computeInstance1, NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", computeInstTes.isLeft());
+ ComponentInstance value2 = computeInstTes.left().value();
+
+ allResourceInstances = resourceInstanceOperation.getAllComponentInstances(serviceId, NodeTypeEnum.Service,
+ NodeTypeEnum.Resource, true);
+ assertTrue("check resource instances found", allResourceInstances.isLeft());
+ immutablePair = allResourceInstances.left().value();
+ nodes = immutablePair.getKey();
+ edges = immutablePair.getValue();
+
+ assertEquals("check resource instances size", 2, nodes.size());
+ assertEquals("check resource instances size", 0, edges.size());
+
+ String requirement = "host";
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirement(requirement);
+ relationPair.setCapabilityUid(capability.getUniqueId());
+ relationPair.setRequirementUid(addRequirementToResource.left().value().getUniqueId());
+ relationPair.setRequirementOwnerId(value1.getUniqueId());
+ relationPair.setCapabilityOwnerId(value2.getUniqueId());
+
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService = resourceInstanceOperation
+ .connectResourcesInService(serviceId, NodeTypeEnum.Service, value1.getUniqueId(),
+ value2.getUniqueId(), relationPair);
+
+ assertTrue("check relation created", connectResourcesInService.isLeft());
+
+ allResourceInstances = resourceInstanceOperation.getAllComponentInstances(serviceId, NodeTypeEnum.Service,
+ NodeTypeEnum.Resource, true);
+ assertTrue("check resource instances found", allResourceInstances.isLeft());
+ immutablePair = allResourceInstances.left().value();
+ nodes = immutablePair.getKey();
+ edges = immutablePair.getValue();
+
+ assertEquals("check resource instances size", 2, nodes.size());
+ assertEquals("check resource instances size", 1, edges.size());
+
+ List<ComponentInstance> resInstances2 = new ArrayList<ComponentInstance>();
+ resInstances2.add(value1);
+ resInstances2.add(value2);
+
+ ComponentInstance myresourceInstance2 = buildResourceInstance(resource.getUniqueId(), "1", "myresource2");
+
+ Either<ComponentInstance, TitanOperationStatus> newResource = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "3", true,
+ myresourceInstance2, NodeTypeEnum.Resource, false);
+
+ assertTrue("added resource instance successfully", newResource.isLeft());
+
+ relationPair.setRequirement(requirement);
+ relationPair.setRequirementOwnerId(newResource.left().value().getUniqueId());
+
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService2 = resourceInstanceOperation
+ .connectResourcesInService(serviceId, NodeTypeEnum.Service,
+ newResource.left().value().getUniqueId(), value2.getUniqueId(), relationPair);
+ assertTrue("check resource instance was added to service", connectResourcesInService2.isLeft());
+
+ allResourceInstances = resourceInstanceOperation.getAllComponentInstances(serviceId, NodeTypeEnum.Service,
+ NodeTypeEnum.Resource, true);
+ assertTrue("check resource instances found", allResourceInstances.isLeft());
+ immutablePair = allResourceInstances.left().value();
+ nodes = immutablePair.getKey();
+ edges = immutablePair.getValue();
+
+ assertEquals("check resource instances size", 3, nodes.size());
+ assertEquals("check resource instances size", 2, edges.size());
+
+ Either<List<ComponentInstance>, TitanOperationStatus> deleteAllResourceInstancesOfService = resourceInstanceOperation
+ .deleteAllComponentInstancesInternal(serviceId, NodeTypeEnum.Service);
+ assertTrue("check resource instances was deleted.", deleteAllResourceInstancesOfService.isLeft());
+ assertEquals("check number of deleted resource instances.", 3,
+ deleteAllResourceInstancesOfService.left().value().size());
+
+ Either<List<RelationshipInstData>, TitanOperationStatus> allRelatinshipInst = titanDao
+ .getAll(NodeTypeEnum.RelationshipInst, RelationshipInstData.class);
+ assertTrue("allRelatinshipInst is empty", allRelatinshipInst.isRight());
+ assertEquals("allRelatinshipInst result is NOT_FOUND", TitanOperationStatus.NOT_FOUND,
+ allRelatinshipInst.right().value());
+
+ Either<Resource, StorageOperationStatus> deleteComputeResource = resourceOperation
+ .deleteResource(computeComponent.getUniqueId(), true);
+ assertTrue("delete compute resource succeed", deleteComputeResource.isLeft());
+
+ Either<Resource, StorageOperationStatus> deleteSCResource = resourceOperation
+ .deleteResource(softwareComponent.getUniqueId(), true);
+ assertTrue("delete software component resource succeed", deleteSCResource.isLeft());
+
+ Either<Resource, StorageOperationStatus> deleteMyResource = resourceOperation
+ .deleteResource(resource.getUniqueId(), true);
+ assertTrue("delete my resource succeed", deleteMyResource.isLeft());
+
+ Either<Resource, StorageOperationStatus> rootResourceDeleted = resourceOperation
+ .deleteResource(rootResource.getUniqueId(), true);
+ assertTrue("delete root resource succeed", rootResourceDeleted.isLeft());
+
+ Set<String> vertexSetAfterDelete = printGraph1.getVerticesSet(titanDao.getGraph().left().value());
+
+ vertexSetAfterDelete.removeAll(vertexSetBeforeMethod);
+
+ log.debug("vertexSetAfterDelete={}", vertexSetAfterDelete);
+ log.debug("vertexSetAfterDelete size={}", vertexSetAfterDelete.size());
+
+ // int numberOfVerticesAfterOperation =
+ // printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+ // System.out.println(numberOfVerticesAfterOperation);
+ // 6 - service, 2 tags, capability + 2 parameters
+ // compareGraphSize(numberOfVertices + 6, vertexSetBeforeMethod);
+
+ } finally {
+ rollbackAndPrint(false);
+ compareGraphSize(numberOfVertices);
+ // printGraph1.printGraphVertices(titanDao.getGraph().left().value());
+ }
+
+ }
+
+ public void testCreateRootResource() {
+
+ String name = "tosca.nodes.Root";
+
+ String state = LifecycleStateEnum.CERTIFIED.name();
+
+ ResourceMetadataData resourceData1 = new ResourceMetadataData();
+ resourceData1.getMetadataDataDefinition().setUniqueId(UniqueIdBuilder.buildResourceUniqueId());
+ resourceData1.getMetadataDataDefinition().setName(name);
+ resourceData1.getMetadataDataDefinition().setState(state);
+ resourceData1.getMetadataDataDefinition().setHighestVersion(true);
+ resourceData1.getMetadataDataDefinition().setContactId("contactId");
+ Either<ResourceMetadataData, TitanOperationStatus> createNode1 = titanDao.createNode(resourceData1,
+ ResourceMetadataData.class);
+
+ log.debug("{}", createNode1);
+
+ titanDao.commit();
+ }
+
+ public void testMultiResourceCertified() {
+ boolean create = true;
+ String name = "myresource7";
+ if (create) {
+
+ String state = LifecycleStateEnum.CERTIFIED.name();
+ boolean isHighestVersion = true;
+
+ ResourceMetadataData resourceData1 = new ResourceMetadataData();
+ resourceData1.getMetadataDataDefinition().setUniqueId(name + "." + "1.0");
+ resourceData1.getMetadataDataDefinition().setName(name);
+ resourceData1.getMetadataDataDefinition().setState(state);
+ resourceData1.getMetadataDataDefinition().setHighestVersion(true);
+ resourceData1.getMetadataDataDefinition().setContactId("contactId");
+ Either<ResourceMetadataData, TitanOperationStatus> createNode1 = titanDao.createNode(resourceData1,
+ ResourceMetadataData.class);
+
+ log.debug("{}", createNode1);
+
+ titanDao.commit();
+
+ // resourceData1.setHighestVersion(false);
+ resourceData1.getMetadataDataDefinition().setContactId("222contactId222");
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData1,
+ ResourceMetadataData.class);
+
+ titanDao.commit();
+
+ // TitanGraph titanGraph = titanDao.getGraph().left().value();
+ // Iterable<Result<Vertex>> vertices =
+ // titanGraph.indexQuery("highestVersion",
+ // "v.highestVersion:true").vertices();
+ // for (Result<Vertex> vertex : vertices) {
+ // Vertex element = vertex.getElement();
+ // System.out.println( ElementHelper.getProperties(element));
+ // }
+
+ }
+
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = searchForResource(name);
+
+ log.debug("{}", byCriteria.left().value().size());
+
+ byCriteria = searchForResource(name);
+
+ log.debug("{}", byCriteria.left().value().size());
+
+ }
+
+ private Either<List<ResourceMetadataData>, TitanOperationStatus> searchForResource(String name) {
+ Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+ // propertiesToMatch.put(GraphPropertiesDictionary.IS_ABSTRACT.getProperty(),
+ // true);
+ propertiesToMatch.put(GraphPropertiesDictionary.NAME.getProperty(), name);
+ propertiesToMatch.put(GraphPropertiesDictionary.CONTACT_ID.getProperty(), "contactId");
+ // propertiesToMatch.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(),
+ // true);
+ Either<List<ResourceMetadataData>, TitanOperationStatus> byCriteria = titanDao
+ .getByCriteria(NodeTypeEnum.Resource, propertiesToMatch, ResourceMetadataData.class);
+ return byCriteria;
+ }
+
+ @Test
+ public void testCreateResourceInstanceTwice() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+ try {
+
+ String capabilityTypeName = CAPABILITY_2;
+ String reqName = "host";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ ComponentInstance myresourceInstance = buildResourceInstance(softwareComponent.getUniqueId(), "1",
+ softwareCompName);
+
+ String serviceName = "myservice.1.0";
+ ServiceMetadataData createService = createService(serviceName);
+ Either<ComponentInstance, StorageOperationStatus> myinstanceRes1 = resourceInstanceOperation
+ .createComponentInstance(serviceName, NodeTypeEnum.Service, "1", myresourceInstance,
+ NodeTypeEnum.Resource, true);
+ assertTrue("check resource instance was created", myinstanceRes1.isLeft());
+
+ Either<ComponentInstance, StorageOperationStatus> myinstanceRes2 = resourceInstanceOperation
+ .createComponentInstance(serviceName, NodeTypeEnum.Service, "1", myresourceInstance,
+ NodeTypeEnum.Resource, true);
+ assertTrue("check resource instance was not created", myinstanceRes2.isRight());
+ assertEquals("check error code", StorageOperationStatus.SCHEMA_VIOLATION, myinstanceRes2.right().value());
+
+ Either<ComponentInstance, StorageOperationStatus> deleteResourceInstance = resourceInstanceOperation
+ .deleteComponentInstance(NodeTypeEnum.Service, serviceName,
+ myinstanceRes1.left().value().getUniqueId(), true);
+ assertTrue("check resource instance was deleted", deleteResourceInstance.isLeft());
+
+ deleteResourceInstance = resourceInstanceOperation.deleteComponentInstance(NodeTypeEnum.Service,
+ serviceName, myinstanceRes1.left().value().getUniqueId(), true);
+ assertTrue("check resource instance was not deleted", deleteResourceInstance.isRight());
+ assertEquals("check resource instance was not deleted", StorageOperationStatus.NOT_FOUND,
+ deleteResourceInstance.right().value());
+
+ } finally {
+ rollbackAndPrint(false);
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ @Test
+ public void testConnectResourceInstancesTwice() {
+
+ PrintGraph printGraph1 = new PrintGraph();
+ int numberOfVertices = printGraph1.getNumberOfVertices(titanDao.getGraph().left().value());
+
+ try {
+
+ String capabilityTypeName1 = CAPABILITY_1;
+ String capabilityTypeName2 = CAPABILITY_2;
+ String reqName1 = "host1";
+ String reqName2 = "host2";
+ String reqNodeName = "tosca.nodes.Compute2" + TEST_CLASS_NUMBER;
+ String rootName = "Root2" + TEST_CLASS_NUMBER;
+ String softwareCompName = "tosca.nodes.SoftwareComponent2" + TEST_CLASS_NUMBER;
+ String computeNodeName = reqNodeName;
+ String myResourceVersion = "4.0" + TEST_CLASS_NUMBER;
+ String reqRelationship = "myrelationship";
+
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef1 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName1);
+ CapabilityTypeDefinition createCapabilityDef2 = capabilityTypeOperationTest
+ .createCapability(capabilityTypeName2);
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ // create root resource
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null,
+ true, true);
+
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+
+ // create software component
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ // create compute component
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName,
+ "1.0", rootResource.getName(), true, true);
+
+ // Add capabilities to Compute Resource
+ CapabilityDefinition capabilty1 = addCapabilityToResource(capabilityTypeName1, reqName1, computeComponent);
+ CapabilityDefinition capabilty2 = addCapabilityToResource(capabilityTypeName2, reqName2, computeComponent);
+
+ // rollbackAndPrint();
+
+ // create requirement definition
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeName1, reqName1, reqNodeName, reqRelationship, softwareComponent);
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ // create my resource derived from software component
+ Resource resource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, "my-resource",
+ myResourceVersion, softwareComponent.getName(), true, true);
+
+ String serviceId = "myservice.1.0";
+
+ ServiceMetadataData createService = createService(serviceId);
+ ComponentInstance myresourceInstance = buildResourceInstance(resource.getUniqueId(), "1", "my-resource");
+
+ ComponentInstance computeInstance = buildResourceInstance(computeComponent.getUniqueId(), "2",
+ computeNodeName);
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+ ComponentInstance value1 = myinstanceRes.left().value();
+ Either<ComponentInstance, TitanOperationStatus> computeInstTes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "2", true,
+ computeInstance, NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", computeInstTes.isLeft());
+ ComponentInstance value2 = computeInstTes.left().value();
+
+ RequirementCapabilityRelDef relation = new RequirementCapabilityRelDef();
+ String fromResUid = value1.getUniqueId();
+ String toResUid = value2.getUniqueId();
+ relation.setFromNode(fromResUid);
+ relation.setToNode(toResUid);
+ List<RequirementAndRelationshipPair> relationships = new ArrayList<RequirementAndRelationshipPair>();
+ RequirementAndRelationshipPair immutablePair1 = new RequirementAndRelationshipPair(reqName1, null);
+ immutablePair1.setCapabilityUid(capabilty1.getUniqueId());
+ immutablePair1.setRequirementUid(parentReqUniqId);
+ immutablePair1.setRequirementOwnerId(fromResUid);
+ immutablePair1.setCapabilityOwnerId(toResUid);
+ relationships.add(immutablePair1);
+
+ relation.setRelationships(relationships);
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> connectResourcesInService = resourceInstanceOperation
+ .associateResourceInstances(serviceId, NodeTypeEnum.Service, relation, true);
+ assertTrue("check association succeed", connectResourcesInService.isLeft());
+
+ relationships.clear();
+ RequirementAndRelationshipPair immutablePair2 = new RequirementAndRelationshipPair(reqName2, null);
+ immutablePair2.setCapabilityUid(capabilty2.getUniqueId());
+ immutablePair2.setRequirementUid(parentReqUniqId);
+ relationships.add(immutablePair2);
+
+ RequirementCapabilityRelDef firstRelation = connectResourcesInService.left().value();
+ connectResourcesInService = resourceInstanceOperation.associateResourceInstances(serviceId,
+ NodeTypeEnum.Service, relation, true);
+ assertTrue("check association succeed", connectResourcesInService.isRight());
+ assertEquals("check association failed", StorageOperationStatus.MATCH_NOT_FOUND,
+ connectResourcesInService.right().value());
+
+ Either<RequirementCapabilityRelDef, StorageOperationStatus> disconnectResourcesInService = resourceInstanceOperation
+ .dissociateResourceInstances(serviceId, NodeTypeEnum.Service, firstRelation, true);
+
+ assertTrue("check dissociation succeed", disconnectResourcesInService.isLeft());
+
+ disconnectResourcesInService = resourceInstanceOperation.dissociateResourceInstances(serviceId,
+ NodeTypeEnum.Service, relation, true);
+
+ assertTrue("check dissociation failed", disconnectResourcesInService.isRight());
+ assertEquals("check association failed", StorageOperationStatus.NOT_FOUND,
+ disconnectResourcesInService.right().value());
+ } finally {
+ rollbackAndPrint();
+ compareGraphSize(numberOfVertices);
+ }
+
+ }
+
+ private Resource createComputeWithCapability(String capabilityTypeName, String computeNodeName,
+ ResourceOperationTest resourceOperationTest, Resource rootResource) {
+ // create compute component
+ // String id = UniqueIdBuilder.buildResourceUniqueId(computeNodeName,
+ // "1.0");
+ // if (resourceOperation.getResource(id).isLeft()){
+ // resourceOperation.deleteResource(id);
+ // }
+ Either<List<Resource>, StorageOperationStatus> oldResource = resourceOperation
+ .getResourceByNameAndVersion(computeNodeName, "1.0", false);
+ if (oldResource.isLeft()) {
+ for (Resource old : oldResource.left().value()) {
+ if (old.getResourceType().equals(ResourceTypeEnum.VFC)) {
+ resourceOperation.deleteResource(old.getUniqueId());
+ }
+ }
+
+ }
+
+ Resource computeComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, computeNodeName, "1.0",
+ rootResource.getName(), true, true);
+
+ // rollbackAndPrint();
+
+ // Add capabilities to Compute Resource
+ addCapabilityToResource(capabilityTypeName, "host", computeComponent);
+ return resourceOperation.getResource(computeComponent.getUniqueId()).left().value();
+ }
+
+ private Resource createSoftwareComponentWithReq(String softwareCompName,
+ ResourceOperationTest resourceOperationTest, Resource rootResource, String capabilityTypeName,
+ String reqName, String reqRelationship, String reqNodeName) {
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode;
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ // create software component
+ // String id = UniqueIdBuilder.buildResourceUniqueId(softwareCompName,
+ // "1.0");
+ // if (resourceOperation.getResource(id).isLeft()){
+ // resourceOperation.deleteResource(id);
+ // }
+ Either<List<Resource>, StorageOperationStatus> oldResource = resourceOperation
+ .getResourceByNameAndVersion(softwareCompName, "1.0", false);
+ if (oldResource.isLeft()) {
+ if (oldResource.isLeft()) {
+ for (Resource old : oldResource.left().value()) {
+ if (old.getResourceType().equals(ResourceTypeEnum.VFC)) {
+ resourceOperation.deleteResource(old.getUniqueId());
+ }
+ }
+
+ }
+ }
+
+ Resource softwareComponent = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, softwareCompName,
+ "1.0", rootResource.getName(), true, true);
+
+ resourceData.getMetadataDataDefinition().setUniqueId(softwareComponent.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ updateNode = titanDao.updateNode(resourceData, ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<RequirementDefinition, StorageOperationStatus> addRequirementToResource = addRequirementToResource(
+ capabilityTypeName, reqName, reqNodeName, reqRelationship, softwareComponent);
+
+ String parentReqUniqId = addRequirementToResource.left().value().getUniqueId();
+
+ return resourceOperation.getResource(softwareComponent.getUniqueId()).left().value();
+ }
+
+ private Resource createRootResource(String rootName, ResourceOperationTest resourceOperationTest) {
+ // create root resource
+ // String rootId = UniqueIdBuilder.buildResourceUniqueId(rootName,
+ // "1.0");
+ Either<List<Resource>, StorageOperationStatus> oldResource = resourceOperation
+ .getResourceByNameAndVersion(rootName, "1.0", false);
+ if (oldResource.isLeft()) {
+ for (Resource old : oldResource.left().value()) {
+ if (old.getResourceType().equals(ResourceTypeEnum.VFC)) {
+ resourceOperation.deleteResource(old.getUniqueId());
+ }
+ }
+
+ }
+ Resource rootResource = resourceOperationTest.createResource(USER_ID, CATEGORY_NAME, rootName, "1.0", null, true,
+ true);
+ ResourceMetadataData rootResourceData = new ResourceMetadataData();
+ rootResourceData.getMetadataDataDefinition().setUniqueId(rootResource.getUniqueId());
+ rootResourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(rootResourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Either<Resource, StorageOperationStatus> fetchRootResource = resourceOperation
+ .getResource(rootResource.getUniqueId(), true);
+
+ String rootResourceJson = prettyGson.toJson(fetchRootResource.left().value());
+ log.debug(rootResourceJson);
+ return rootResource;
+ }
+
+ public void addResourceInstancesAndRelation(String serviceId) {
+
+ String rootName = "tosca.nodes.test.root";
+ String softwareCompName = "tosca.nodes.test.softwarecomponent";
+ String capabilityTypeName = "myCapability";
+ String reqName = "host";
+ String computeNodeName = "tosca.nodes.test.compute";
+ String reqRelationship = "myRelationship";
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = createRootResource(rootName, resourceOperationTest);
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ CapabilityTypeDefinition createCapabilityDef = capabilityTypeOperationTest.createCapability(capabilityTypeName);
+
+ Resource softwareComponentResource = createSoftwareComponentWithReq(softwareCompName, resourceOperationTest,
+ rootResource, capabilityTypeName, reqName, reqRelationship, computeNodeName);
+ Resource compute = createComputeWithCapability(capabilityTypeName, computeNodeName, resourceOperationTest,
+ rootResource);
+
+ // resource1
+ ComponentInstance myresourceInstance = buildResourceInstance(softwareComponentResource.getUniqueId(), "1",
+ "tosca.nodes.test.root");
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+
+ // resource2
+ ComponentInstance computeInstance = buildResourceInstance(compute.getUniqueId(), "2",
+ "tosca.nodes.test.compute");
+ ComponentInstance value1 = myinstanceRes.left().value();
+
+ Either<ComponentInstance, TitanOperationStatus> computeInstTes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "2", true, computeInstance,
+ NodeTypeEnum.Resource, false);
+ assertTrue("check instance added to service", computeInstTes.isLeft());
+ ComponentInstance value2 = computeInstTes.left().value();
+
+ RequirementAndRelationshipPair relationPair = new RequirementAndRelationshipPair();
+ relationPair.setRequirement(reqName);
+ relationPair.setCapability(capabilityTypeName);
+
+ String capId = "";
+ Map<String, List<CapabilityDefinition>> capabilities = compute.getCapabilities();
+ for (Map.Entry<String, List<CapabilityDefinition>> entry : capabilities.entrySet()) {
+ capId = entry.getValue().get(0).getUniqueId();
+ }
+ relationPair.setCapabilityUid(capId);
+ Map<String, List<RequirementDefinition>> requirements = softwareComponentResource.getRequirements();
+ String reqId = "";
+ for (Map.Entry<String, List<RequirementDefinition>> entry : requirements.entrySet()) {
+ reqId = entry.getValue().get(0).getUniqueId();
+ }
+ relationPair.setRequirementUid(reqId);
+ relationPair.setRequirementOwnerId(value1.getUniqueId());
+ relationPair.setCapabilityOwnerId(value2.getUniqueId());
+ relationPair.setCapabilityUid(capId);
+
+ Either<RelationshipInstData, TitanOperationStatus> connectResourcesInService = resourceInstanceOperation
+ .connectResourcesInService(serviceId, NodeTypeEnum.Service, value1.getUniqueId(), value2.getUniqueId(),
+ relationPair);
+
+ assertTrue("check relation created", connectResourcesInService.isLeft());
+
+ }
+
+ @Test
+ public void addResourceInstancesResourceDeleted() {
+
+ String rootName = "tosca.nodes.test.root";
+ String softwareCompName = "tosca.nodes.test.softwarecomponent";
+ String capabilityTypeName = "myCapability";
+ String reqName = "host";
+ String computeNodeName = "tosca.nodes.test.compute";
+ String reqRelationship = "myRelationship";
+
+ ServiceMetadataData origService = createService("myService");
+ String serviceId = (String) origService.getUniqueId();
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = createRootResource(rootName, resourceOperationTest);
+ // Create Capability type
+ CapabilityTypeOperationTest capabilityTypeOperationTest = new CapabilityTypeOperationTest();
+ capabilityTypeOperationTest.setOperations(titanDao, capabilityTypeOperation);
+ capabilityTypeOperationTest.createCapability(capabilityTypeName);
+
+ Resource softwareComponentResource = createSoftwareComponentWithReq(softwareCompName, resourceOperationTest,
+ rootResource, capabilityTypeName, reqName, reqRelationship, computeNodeName);
+
+ deleteResource(softwareComponentResource.getUniqueId());
+
+ // resource1
+ ComponentInstance myresourceInstance = buildResourceInstance(softwareComponentResource.getUniqueId(), "1",
+ "tosca.nodes.test.root");
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+
+ assertTrue("check instance not added to service", myinstanceRes.isRight());
+
+ }
+
+ @Test
+ public void testDeploymentArtifactsOnRI() {
+
+ String rootName = "tosca.nodes.test.root";
+
+ ServiceMetadataData origService = createService("testDeploymentArtifactsOnRI");
+ String serviceId = (String) origService.getUniqueId();
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = createRootResource(rootName, resourceOperationTest);
+ ArtifactDefinition addArtifactToResource = addArtifactToResource(USER_ID, rootResource.getUniqueId(),
+ "myArtifact");
+
+ // resource1
+ ComponentInstance myresourceInstance = buildResourceInstance(rootResource.getUniqueId(), "1", rootName);
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> resourceInstancesOfService = resourceInstanceOperation
+ .getComponentInstancesOfComponent(serviceId, NodeTypeEnum.Service, NodeTypeEnum.Resource);
+ assertTrue(resourceInstancesOfService.isLeft());
+ List<ComponentInstance> resourceInstanceList = resourceInstancesOfService.left().value().left;
+ assertTrue(resourceInstanceList.size() == 1);
+ ComponentInstance resourceInstance = resourceInstanceList.get(0);
+ assertTrue(resourceInstance.getDeploymentArtifacts().size() == 1);
+ Map<String, ArtifactDefinition> artifacts = resourceInstance.getDeploymentArtifacts();
+ assertNotNull(artifacts.get(addArtifactToResource.getArtifactLabel()));
+
+ ArtifactDefinition heatEnvArtifact = new ArtifactDefinition(addArtifactToResource);
+ heatEnvArtifact.setArtifactType("HEAT_ENV");
+ heatEnvArtifact.setArtifactLabel(addArtifactToResource.getArtifactLabel() + "env");
+ heatEnvArtifact.setUniqueId(null);
+
+ Either<ArtifactDefinition, StorageOperationStatus> either = artifactOperation.addHeatEnvArtifact(
+ heatEnvArtifact, addArtifactToResource, resourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance,
+ false);
+ assertTrue(either.isLeft());
+
+ resourceInstancesOfService = resourceInstanceOperation.getComponentInstancesOfComponent(serviceId,
+ NodeTypeEnum.Service, NodeTypeEnum.Resource);
+ assertTrue(resourceInstancesOfService.isLeft());
+ resourceInstanceList = resourceInstancesOfService.left().value().left;
+ assertTrue(resourceInstanceList.size() == 1);
+ resourceInstance = resourceInstanceList.get(0);
+ assertTrue(resourceInstance.getDeploymentArtifacts().size() == 2);
+ artifacts = resourceInstance.getDeploymentArtifacts();
+ assertNotNull(artifacts.get(addArtifactToResource.getArtifactLabel()));
+ assertNotNull(artifacts.get(addArtifactToResource.getArtifactLabel() + "env"));
+ ArtifactDefinition heatEnvFromRI = artifacts.get(addArtifactToResource.getArtifactLabel() + "env");
+ assertEquals(addArtifactToResource.getUniqueId(), heatEnvFromRI.getGeneratedFromId());
+
+ List<HeatParameterDefinition> heatParameters = artifacts.get(addArtifactToResource.getArtifactLabel())
+ .getHeatParameters();
+ assertNotNull(heatParameters);
+ assertTrue(heatParameters.size() == 1);
+
+ List<HeatParameterDefinition> heatEnvParameters = heatEnvFromRI.getHeatParameters();
+ assertNotNull(heatEnvParameters);
+ assertTrue(heatEnvParameters.size() == 1);
+
+ resourceOperation.deleteResource(rootResource.getUniqueId());
+
+ }
+
+ @Test
+ public void deleteResourceInstanceWithArtifacts() {
+ String rootName = "tosca.nodes.test.root";
+
+ ServiceMetadataData origService = createService("deleteResourceInstanceWithArtifacts");
+ String serviceId = (String) origService.getUniqueId();
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = createRootResource(rootName, resourceOperationTest);
+ ArtifactDefinition addArtifactToResource = addArtifactToResource(USER_ID, rootResource.getUniqueId(),
+ "myArtifact");
+
+ // resource1
+ ComponentInstance myresourceInstance = buildResourceInstance(rootResource.getUniqueId(), "1", rootName);
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+
+ ArtifactDefinition heatEnvArtifact = new ArtifactDefinition(addArtifactToResource);
+ heatEnvArtifact.setArtifactType("HEAT_ENV");
+ heatEnvArtifact.setArtifactLabel(addArtifactToResource.getArtifactLabel() + "env");
+ heatEnvArtifact.setUniqueId(null);
+
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> resourceInstancesOfService = resourceInstanceOperation
+ .getComponentInstancesOfComponent(serviceId, NodeTypeEnum.Service, NodeTypeEnum.Resource);
+ assertTrue(resourceInstancesOfService.isLeft());
+ List<ComponentInstance> resourceInstanceList = resourceInstancesOfService.left().value().left;
+ assertTrue(resourceInstanceList.size() == 1);
+ ComponentInstance resourceInstance = resourceInstanceList.get(0);
+
+ Either<ArtifactDefinition, StorageOperationStatus> either = artifactOperation.addHeatEnvArtifact(
+ heatEnvArtifact, addArtifactToResource, resourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance,
+ false);
+ assertTrue(either.isLeft());
+ ArtifactDefinition heatEnvDefinition = either.left().value();
+
+ // delete resource instance
+ Either<ComponentInstance, StorageOperationStatus> deleteResourceInstance = resourceInstanceOperation
+ .deleteComponentInstance(NodeTypeEnum.Service, serviceId, resourceInstance.getUniqueId());
+ assertTrue(deleteResourceInstance.isLeft());
+
+ // check heat env deleted
+ ArtifactData artifactData = new ArtifactData();
+ Either<ArtifactData, TitanOperationStatus> getDeletedArtifact = titanDao.getNode(artifactData.getUniqueIdKey(),
+ heatEnvDefinition.getUniqueId(), ArtifactData.class);
+ assertTrue(getDeletedArtifact.isRight());
+
+ // check heat is not deleted
+ getDeletedArtifact = titanDao.getNode(artifactData.getUniqueIdKey(), addArtifactToResource.getUniqueId(),
+ ArtifactData.class);
+ assertTrue(getDeletedArtifact.isLeft());
+
+ HeatParameterData heatParamData = new HeatParameterData();
+ Either<HeatParameterData, TitanOperationStatus> heatParamNode = titanDao.getNode(heatParamData.getUniqueIdKey(),
+ addArtifactToResource.getHeatParameters().get(0).getUniqueId(), HeatParameterData.class);
+ assertTrue(heatParamNode.isLeft());
+
+ resourceOperation.deleteResource(rootResource.getUniqueId());
+
+ }
+
+ @Test
+ public void getHeatEnvParams() {
+ String rootName = "tosca.nodes.test.root";
+
+ ServiceMetadataData origService = createService("getHeatEnvParams");
+ String serviceId = (String) origService.getUniqueId();
+
+ ResourceOperationTest resourceOperationTest = new ResourceOperationTest();
+ resourceOperationTest.setOperations(titanDao, resourceOperation, propertyOperation);
+
+ Resource rootResource = createRootResource(rootName, resourceOperationTest);
+ ArtifactDefinition addArtifactToResource = addArtifactToResource(USER_ID, rootResource.getUniqueId(),
+ "myArtifact");
+
+ // resource1
+ ComponentInstance myresourceInstance = buildResourceInstance(rootResource.getUniqueId(), "1", rootName);
+
+ Either<ComponentInstance, TitanOperationStatus> myinstanceRes = resourceInstanceOperation
+ .addComponentInstanceToContainerComponent(serviceId, NodeTypeEnum.Service, "1", true,
+ myresourceInstance, NodeTypeEnum.Resource, false);
+
+ ArtifactDefinition heatEnvArtifact = new ArtifactDefinition(addArtifactToResource);
+ heatEnvArtifact.setArtifactType("HEAT_ENV");
+ heatEnvArtifact.setArtifactLabel(addArtifactToResource.getArtifactLabel() + "env");
+ heatEnvArtifact.setUniqueId(null);
+
+ assertTrue("check instance added to service", myinstanceRes.isLeft());
+
+ Either<ImmutablePair<List<ComponentInstance>, List<RequirementCapabilityRelDef>>, TitanOperationStatus> resourceInstancesOfService = resourceInstanceOperation
+ .getComponentInstancesOfComponent(serviceId, NodeTypeEnum.Service, NodeTypeEnum.Resource);
+ assertTrue(resourceInstancesOfService.isLeft());
+ List<ComponentInstance> resourceInstanceList = resourceInstancesOfService.left().value().left;
+ assertTrue(resourceInstanceList.size() == 1);
+ ComponentInstance resourceInstance = resourceInstanceList.get(0);
+
+ Either<ArtifactDefinition, StorageOperationStatus> either = artifactOperation.addHeatEnvArtifact(
+ heatEnvArtifact, addArtifactToResource, resourceInstance.getUniqueId(), NodeTypeEnum.ResourceInstance,
+ false);
+ assertTrue(either.isLeft());
+ ArtifactDefinition heatEnvDefinition = either.left().value();
+
+ // update value
+ String newHeatValue = "123";
+ addHeatValueToEnv(heatEnvDefinition.getUniqueId(), addArtifactToResource.getHeatParameters().get(0),
+ newHeatValue);
+
+ // check values received
+
+ resourceInstancesOfService = resourceInstanceOperation.getComponentInstancesOfComponent(serviceId,
+ NodeTypeEnum.Service, NodeTypeEnum.Resource);
+ assertTrue(resourceInstancesOfService.isLeft());
+ resourceInstanceList = resourceInstancesOfService.left().value().left;
+ assertTrue(resourceInstanceList.size() == 1);
+ resourceInstance = resourceInstanceList.get(0);
+ assertTrue(resourceInstance.getDeploymentArtifacts().size() == 2);
+ Map<String, ArtifactDefinition> artifacts = resourceInstance.getDeploymentArtifacts();
+ assertNotNull(artifacts.get(addArtifactToResource.getArtifactLabel()));
+ assertNotNull(artifacts.get(addArtifactToResource.getArtifactLabel() + "env"));
+
+ List<HeatParameterDefinition> heatParameters = artifacts.get(addArtifactToResource.getArtifactLabel())
+ .getHeatParameters();
+ assertNotNull(heatParameters);
+ assertTrue(heatParameters.size() == 1);
+ HeatParameterDefinition heatParameterTemplate = heatParameters.get(0);
+
+ List<HeatParameterDefinition> heatEnvParameters = artifacts
+ .get(addArtifactToResource.getArtifactLabel() + "env").getHeatParameters();
+ assertNotNull(heatEnvParameters);
+ assertTrue(heatEnvParameters.size() == 1);
+ HeatParameterDefinition heatParameterEnv = heatEnvParameters.get(0);
+
+ assertEquals(heatParameterEnv.getDefaultValue(), heatParameterTemplate.getCurrentValue());
+ assertEquals(newHeatValue, heatParameterEnv.getCurrentValue());
+ assertFalse(newHeatValue.equals(heatParameterTemplate.getCurrentValue()));
+
+ resourceOperation.deleteResource(rootResource.getUniqueId());
+
+ }
+
+ public void addHeatValueToEnv(String artifactId, HeatParameterDefinition heatDefinition, String value) {
+ HeatParameterValueData heatValueData = new HeatParameterValueData();
+ heatValueData.setValue(value);
+ heatValueData.setUniqueId(artifactId + "." + heatDefinition.getName());
+ Either<HeatParameterValueData, TitanOperationStatus> createValue = titanDao.createNode(heatValueData,
+ HeatParameterValueData.class);
+ assertTrue(createValue.isLeft());
+ HeatParameterValueData value2 = createValue.left().value();
+ HeatParameterData heatParamData = new HeatParameterData(heatDefinition);
+ Either<GraphRelation, TitanOperationStatus> createRelation = titanDao.createRelation(value2, heatParamData,
+ GraphEdgeLabels.PROPERTY_IMPL, null);
+ assertTrue(createRelation.isLeft());
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(GraphEdgePropertiesDictionary.NAME.getProperty(), heatDefinition.getName());
+ Either<GraphRelation, TitanOperationStatus> createRelation2 = titanDao.createRelation(
+ new UniqueIdData(NodeTypeEnum.ArtifactRef, artifactId), value2, GraphEdgeLabels.PARAMETER_VALUE, props);
+ assertTrue(createRelation2.isLeft());
+ titanDao.commit();
+
+ }
+
+ public static String normaliseComponentName(String name) {
+ String[] split = splitComponentName(name);
+ StringBuffer sb = new StringBuffer();
+ for (String splitElement : split) {
+ sb.append(splitElement);
+ }
+ return sb.toString();
+
+ }
+
+ private static String[] splitComponentName(String name) {
+ String normalizedName = name.toLowerCase();
+ normalizedName = COMPONENT_NAME_DELIMETER_PATTERN.matcher(normalizedName).replaceAll(" ");
+ String[] split = normalizedName.split(" ");
+ return split;
+ }
+
+ public static String normaliseComponentInstanceName(String name) {
+ String[] split = splitComponentInstanceName(name);
+ StringBuffer sb = new StringBuffer();
+ for (String splitElement : split) {
+ sb.append(splitElement);
+ }
+ return sb.toString();
+
+ }
+
+ private static String[] splitComponentInstanceName(String name) {
+ String normalizedName = name.toLowerCase();
+ normalizedName = COMPONENT_INCTANCE_NAME_DELIMETER_PATTERN.matcher(normalizedName).replaceAll(" ");
+ String[] split = normalizedName.split(" ");
+ return split;
+ }
+
+ private ArtifactDefinition addArtifactToResource(String userId, String resourceId, String artifactName) {
+ ArtifactDefinition artifactInfo = new ArtifactDefinition();
+
+ artifactInfo.setArtifactName(artifactName + ".yml");
+ artifactInfo.setArtifactType("HEAT");
+ artifactInfo.setDescription("hdkfhskdfgh");
+ artifactInfo.setArtifactChecksum("UEsDBAoAAAAIAAeLb0bDQz");
+ artifactInfo.setArtifactGroupType(ArtifactGroupTypeEnum.DEPLOYMENT);
+
+ artifactInfo.setUserIdCreator(userId);
+ String fullName = "Jim H";
+ artifactInfo.setUpdaterFullName(fullName);
+ long time = System.currentTimeMillis();
+ artifactInfo.setCreatorFullName(fullName);
+ artifactInfo.setCreationDate(time);
+ artifactInfo.setLastUpdateDate(time);
+ artifactInfo.setUserIdLastUpdater(userId);
+ artifactInfo.setArtifactLabel(artifactName);
+ artifactInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(resourceId, artifactInfo.getArtifactLabel()));
+ artifactInfo.setEsId(artifactInfo.getUniqueId());
+
+ List<HeatParameterDefinition> heatParams = new ArrayList<HeatParameterDefinition>();
+ HeatParameterDefinition heatParam = new HeatParameterDefinition();
+ heatParam.setCurrentValue("11");
+ heatParam.setDefaultValue("22");
+ heatParam.setDescription("desc");
+ heatParam.setName("myParam");
+ heatParam.setType("number");
+ heatParams.add(heatParam);
+ artifactInfo.setHeatParameters(heatParams);
+
+ Either<ArtifactDefinition, StorageOperationStatus> artifact = artifactOperation
+ .addArifactToComponent(artifactInfo, resourceId, NodeTypeEnum.Resource, true, true);
+ assertTrue(artifact.isLeft());
+ return artifact.left().value();
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperationTest.java
new file mode 100644
index 0000000000..f977509f6b
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ResourceOperationTest.java
@@ -0,0 +1,734 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.CapabilityDefinition;
+import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstanceProperty;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PropertyConstraint;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityOperation;
+import org.openecomp.sdc.be.model.operations.impl.CapabilityTypeOperation;
+import org.openecomp.sdc.be.model.operations.impl.LifecycleOperation;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.ResourceOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.tosca.ToscaType;
+import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
+import org.openecomp.sdc.be.resources.data.PropertyData;
+import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class ResourceOperationTest extends ModelTestBase {
+
+ private static Logger log = LoggerFactory.getLogger(ResourceOperationTest.class.getName());
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource(name = "lifecycle-operation")
+ private LifecycleOperation lifecycleOperation;
+
+ @javax.annotation.Resource(name = "capability-operation")
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ private static String CATEGORY_NAME = "category/mycategory";
+ private static String CATEGORY_NAME_UPDATED = "category1/updatedcategory";
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+
+ ModelTestBase.init();
+ }
+
+ public void setOperations(TitanGenericDao titanGenericDao, ResourceOperation resourceOperation,
+ PropertyOperation propertyOperation) {
+ this.titanDao = titanGenericDao;
+ this.resourceOperation = resourceOperation;
+ this.propertyOperation = propertyOperation;
+ }
+
+ @Test
+ public void dummyTest() {
+
+ }
+
+ private Resource buildResourceMetadata(String userId, String category, String resourceName,
+ String resourceVersion) {
+
+ Resource resource = new Resource();
+ resource.setName(resourceName);
+ resource.setVersion(resourceVersion);
+ ;
+ resource.setDescription("description 1");
+ resource.setAbstract(false);
+ resource.setCreatorUserId(userId);
+ resource.setContactId("contactId@sdc.com");
+ resource.setVendorName("vendor 1");
+ resource.setVendorRelease("1.0.0");
+ resource.setToscaResourceName(resourceName);
+ String[] categoryArr = category.split("/");
+ resource.addCategory(categoryArr[0], categoryArr[1]);
+ resource.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ resource.setTags(tags);
+ return resource;
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanDao);
+ }
+
+ public Resource createResource(String userId, String category, String resourceName, String resourceVersion,
+ String parentResourceName, boolean isAbstract, boolean isHighestVersion) {
+
+ String propName1 = "disk_size";
+ String propName2 = "num_cpus";
+
+ List<String> derivedFrom = new ArrayList<String>();
+ if (parentResourceName != null) {
+ derivedFrom.add(parentResourceName);
+ }
+ Resource resource = buildResourceMetadata(userId, category, resourceName, resourceVersion);
+
+ resource.setAbstract(isAbstract);
+ resource.setHighestVersion(isHighestVersion);
+
+ Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>();
+
+ PropertyDefinition property1 = new PropertyDefinition();
+ property1.setDefaultValue("10");
+ property1.setDescription(
+ "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.");
+ property1.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints = new ArrayList<PropertyConstraint>();
+ GreaterThanConstraint propertyConstraint1 = new GreaterThanConstraint("0");
+ log.debug("{}", propertyConstraint1);
+
+ constraints.add(propertyConstraint1);
+
+ LessOrEqualConstraint propertyConstraint2 = new LessOrEqualConstraint("10");
+ constraints.add(propertyConstraint2);
+
+ property1.setConstraints(constraints);
+
+ properties.put(propName1, property1);
+
+ PropertyDefinition property2 = new PropertyDefinition();
+ property2.setDefaultValue("2");
+ property2.setDescription("Number of (actual or virtual) CPUs associated with the Compute node.");
+ property2.setType(ToscaType.INTEGER.name().toLowerCase());
+ List<PropertyConstraint> constraints3 = new ArrayList<PropertyConstraint>();
+ List<String> range = new ArrayList<String>();
+ range.add("1");
+ range.add("4");
+
+ InRangeConstraint propertyConstraint3 = new InRangeConstraint(range);
+ constraints3.add(propertyConstraint3);
+ // property2.setConstraints(constraints3);
+ property2.setConstraints(constraints3);
+ properties.put(propName2, property2);
+
+ resource.setDerivedFrom(derivedFrom);
+
+ resource.setProperties(convertMapToList(properties));
+
+ Either<Resource, StorageOperationStatus> result = resourceOperation.createResource(resource, true);
+
+ assertTrue(result.isLeft());
+ Resource resultResource = result.left().value();
+
+ // assertEquals("check resource unique id",
+ // UniqueIdBuilder.buildResourceUniqueId(resourceName,
+ // resourceVersion), resultResource.getUniqueId());
+ assertEquals("check resource state", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ resultResource.getLifecycleState());
+
+ // retrieve property from graph
+ String resourceId = resultResource.getUniqueId();
+ // String resourceId = UniqueIdBuilder.buildResourceUniqueId(
+ // resource.getResourceName(), resource.getResourceVersion());
+
+ Either<PropertyDefinition, StorageOperationStatus> either = propertyOperation.getPropertyOfResource(propName1,
+ resourceId);
+
+ assertTrue(either.isLeft());
+ PropertyDefinition propertyDefinition = either.left().value();
+ assertEquals("check property default value", property1.getDefaultValue(), propertyDefinition.getDefaultValue());
+ assertEquals("check property description", property1.getDescription(), propertyDefinition.getDescription());
+ assertEquals("check property type", property1.getType(), propertyDefinition.getType());
+ assertEquals("check property unique id", property1.getUniqueId(), propertyDefinition.getUniqueId());
+ assertEquals("check property consitraints size", property1.getConstraints().size(),
+ propertyDefinition.getConstraints().size());
+
+ return resultResource;
+
+ }
+
+ public static List<PropertyDefinition> convertMapToList(Map<String, PropertyDefinition> properties) {
+ if (properties == null) {
+ return null;
+ }
+
+ List<PropertyDefinition> definitions = new ArrayList<>();
+ for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
+ String name = entry.getKey();
+ PropertyDefinition propertyDefinition = entry.getValue();
+ propertyDefinition.setName(name);
+ definitions.add(propertyDefinition);
+ }
+
+ return definitions;
+ }
+
+ @Test
+ public void testFollowed() {
+ String rootName = "Root123";
+
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ deleteAndCreateUser(userId, "first_" + userId, "last_" + userId);
+ deleteAndCreateCategory(category);
+
+ Resource rootResource = createResource(userId, category, rootName, "1.0", null, false, true);
+ log.debug(" *** create **");
+ log.debug("{}", rootResource);
+ String resourceId = rootResource.getUniqueId();
+
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ lifecycleStates.add(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Set<LifecycleStateEnum> lastStateStates = new HashSet<LifecycleStateEnum>();
+ lastStateStates.add(LifecycleStateEnum.CERTIFIED);
+
+ Either<List<Resource>, StorageOperationStatus> followed = resourceOperation.getFollowed(userId, lifecycleStates,
+ lastStateStates, false);
+ assertTrue(followed.isLeft());
+ List<Resource> list = followed.left().value();
+
+ assertEquals(1, list.size());
+ resourceOperation.deleteResource(resourceId);
+
+ followed = resourceOperation.getFollowed(userId, lifecycleStates, lastStateStates, false);
+ assertTrue(followed.isLeft());
+ list = followed.left().value();
+ assertTrue(list.isEmpty());
+
+ }
+
+ @Ignore
+ @Test
+ public void testGetLatestVersion() {
+ String rootName = "Root123";
+
+ String userId = "jh0003";
+ String category = "category/mycategory";
+ deleteAndCreateUser(userId, "first_" + userId, "last_" + userId);
+ deleteAndCreateCategory(category);
+
+ Either<Resource, StorageOperationStatus> latestByName = resourceOperation.getLatestByName(rootName, true);
+ assertTrue(latestByName.isRight());
+ assertEquals(StorageOperationStatus.NOT_FOUND, latestByName.right().value());
+
+ Resource rootResource = createResource(userId, category, rootName, "1.0", null, false, true);
+
+ latestByName = resourceOperation.getLatestByName(rootName, true);
+ assertTrue(latestByName.isLeft());
+
+ Resource rootResourceHighest = createResource(userId, category, rootName, "1.3", null, false, true);
+
+ latestByName = resourceOperation.getLatestByName(rootName, false);
+ assertTrue(latestByName.isLeft());
+ assertEquals(rootResourceHighest.getUniqueId(), latestByName.left().value().getUniqueId());
+
+ resourceOperation.deleteResource(rootResource.getUniqueId());
+ resourceOperation.deleteResource(rootResourceHighest.getUniqueId());
+ }
+
+ @Test
+ public void testOverrideResource() {
+ String rootName = "Root123";
+
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ String updatedCategory = CATEGORY_NAME_UPDATED;
+ deleteAndCreateUser(userId, "first_" + userId, "last_" + userId);
+ deleteAndCreateCategory(category);
+ deleteAndCreateCategory(updatedCategory);
+
+ Resource rootResource = createResource(userId, category, rootName, "1.1", null, false, true);
+
+ rootResource.setCategories(null);
+ String[] updateArr = updatedCategory.split("/");
+ rootResource.addCategory(updateArr[0], updateArr[1]);
+ List<PropertyDefinition> properties = rootResource.getProperties();
+ PropertyDefinition propertyDefinition = findProperty(properties, "disk_size");
+
+ rootResource.setProperties(new ArrayList<PropertyDefinition>());
+ propertyDefinition.setName("myProperty");
+ rootResource.getProperties().add(propertyDefinition);
+
+ Either<Resource, StorageOperationStatus> overrideResource = resourceOperation.overrideResource(rootResource,
+ rootResource, false);
+
+ assertTrue(overrideResource.isLeft());
+ Resource resourceAfter = overrideResource.left().value();
+ assertEquals(1, resourceAfter.getProperties().size());
+
+ assertNotNull(findProperty(resourceAfter.getProperties(), "myProperty"));
+ assertEquals(1, resourceAfter.getCategories().size());
+ assertEquals(1, resourceAfter.getCategories().get(0).getSubcategories().size());
+
+ assertEquals(updateArr[0], resourceAfter.getCategories().get(0).getName());
+ assertEquals(updateArr[1], resourceAfter.getCategories().get(0).getSubcategories().get(0).getName());
+
+ resourceOperation.deleteResource(rootResource.getUniqueId());
+ }
+
+ @Test
+ public void testResourceWithCapabilities() {
+ String rootName = "Root123";
+
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ String updatedCategory = CATEGORY_NAME_UPDATED;
+ deleteAndCreateUser(userId, "first_" + userId, "last_" + userId);
+ deleteAndCreateCategory(category);
+ deleteAndCreateCategory(updatedCategory);
+
+ Resource rootResource = createResource(userId, category, rootName, "1.1", null, false, true);
+
+ CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition();
+ capabilityTypeDefinition.setType("tosca.capabilities.Container");
+ PropertyDefinition delaultProperty1 = new PropertyDefinition();
+ delaultProperty1.setName("def");
+ delaultProperty1.setType("string");
+ delaultProperty1.setDefaultValue("def");
+
+ PropertyDefinition delaultProperty2 = new PropertyDefinition();
+ delaultProperty2.setName("host");
+ delaultProperty2.setType("string");
+ delaultProperty2.setDefaultValue("true");
+
+ HashMap<String, PropertyDefinition> props = new HashMap<String, PropertyDefinition>();
+ props.put(delaultProperty1.getName(), delaultProperty1);
+ props.put(delaultProperty2.getName(), delaultProperty2);
+ capabilityTypeDefinition.setProperties(props);
+
+ Either<CapabilityTypeDefinition, StorageOperationStatus> addTypeRes = capabilityTypeOperation
+ .addCapabilityType(capabilityTypeDefinition);
+ assertTrue(addTypeRes.isLeft());
+
+ CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
+ capabilityDefinition.setDescription("firstCap");
+ capabilityDefinition.setName("firstCap");
+ capabilityDefinition.setType("tosca.capabilities.Container");
+
+ List<ComponentInstanceProperty> properties = new ArrayList<ComponentInstanceProperty>();
+ ComponentInstanceProperty propertyDefinition1 = new ComponentInstanceProperty();
+ propertyDefinition1.setName("version");
+ propertyDefinition1.setType("string");
+ propertyDefinition1.setDefaultValue("007");
+ properties.add(propertyDefinition1);
+
+ ComponentInstanceProperty propertyDefinition2 = new ComponentInstanceProperty();
+ propertyDefinition2.setName("host");
+ propertyDefinition2.setType("string");
+ propertyDefinition2.setDefaultValue("localhost");
+ properties.add(propertyDefinition2);
+
+ capabilityDefinition.setProperties(properties);
+
+ Either<CapabilityDefinition, StorageOperationStatus> addCapabilityRes = capabilityOperation
+ .addCapability(rootResource.getUniqueId(), capabilityDefinition.getName(), capabilityDefinition);
+ assertTrue(addCapabilityRes.isLeft());
+
+ List<PropertyDefinition> newProperties = new ArrayList<PropertyDefinition>();
+ propertyDefinition1 = new ComponentInstanceProperty();
+ propertyDefinition1.setName("num_cpu");
+ propertyDefinition1.setType("string");
+ propertyDefinition1.setDefaultValue("4");
+ newProperties.add(propertyDefinition1);
+
+ propertyDefinition2 = new ComponentInstanceProperty();
+ propertyDefinition2.setName("port");
+ propertyDefinition2.setType("string");
+ propertyDefinition2.setDefaultValue("4444");
+ newProperties.add(propertyDefinition2);
+
+ CapabilityDefinition addedCap = addCapabilityRes.left().value();
+
+ Either<Map<String, PropertyData>, StorageOperationStatus> updatePropertiesRes = capabilityOperation
+ .updatePropertiesOfCapability(addedCap.getUniqueId(), addedCap.getType(), newProperties);
+ assertTrue(updatePropertiesRes.isLeft());
+
+ PropertyDefinition invalidProperty = new PropertyDefinition();
+ invalidProperty.setName("port");
+ invalidProperty.setType("rrr");
+ invalidProperty.setDefaultValue("666");
+ newProperties.add(invalidProperty);
+
+ Either<Map<String, PropertyData>, StorageOperationStatus> updatePropertiesInvalidRes = capabilityOperation
+ .updatePropertiesOfCapability(addedCap.getUniqueId(), addedCap.getType(), newProperties);
+ assertTrue(updatePropertiesInvalidRes.isRight());
+
+ Either<CapabilityDefinition, StorageOperationStatus> getCapabilityRes = capabilityOperation
+ .getCapability(addedCap.getUniqueId());
+ assertTrue(getCapabilityRes.isLeft());
+
+ Either<List<ImmutablePair<PropertyData, GraphEdge>>, TitanOperationStatus> deletePropertiesOfCapabilityRes = capabilityOperation
+ .deletePropertiesOfCapability(addedCap.getUniqueId());
+ assertTrue(deletePropertiesOfCapabilityRes.isLeft());
+
+ StorageOperationStatus deleteCapabilityRes = capabilityOperation
+ .deleteCapabilityFromGraph(addedCap.getUniqueId());
+ assertTrue(deleteCapabilityRes.equals(StorageOperationStatus.OK));
+
+ getCapabilityRes = capabilityOperation.getCapability(addedCap.getUniqueId());
+ assertTrue(getCapabilityRes.isRight()
+ && getCapabilityRes.right().value().equals(StorageOperationStatus.NOT_FOUND));
+
+ resourceOperation.deleteResource(rootResource.getUniqueId());
+ }
+
+ private PropertyDefinition findProperty(List<PropertyDefinition> properties, String propName) {
+
+ if (properties == null) {
+ return null;
+ }
+
+ for (PropertyDefinition propertyDefinition : properties) {
+ String name = propertyDefinition.getName();
+ if (name.equals(propName)) {
+ return propertyDefinition;
+ }
+ }
+
+ return null;
+ }
+
+ @Test
+ public void testOverrideResourceNotExist() {
+ String rootName = "Root123";
+
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ deleteAndCreateUser(userId, "first_" + userId, "last_" + userId);
+ deleteAndCreateCategory(category);
+
+ Resource rootResource = buildResourceMetadata(userId, category, rootName, "1.1");
+ rootResource.setUniqueId(UniqueIdBuilder.buildResourceUniqueId());
+
+ Either<Resource, StorageOperationStatus> overrideResource = resourceOperation.overrideResource(rootResource,
+ rootResource, false);
+
+ assertTrue(overrideResource.isRight());
+
+ }
+
+ @Ignore
+ @Test
+ public void testCatalogResource() {
+ String resName = "myResource";
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ deleteAndCreateCategory(category);
+ // resourceOperation.deleteResource(UniqueIdBuilder.buildResourceUniqueId(resName,"0.1"));
+ Resource newResource = createResource(userId, category, resName, "0.1", null, false, true);
+ String resourceId = newResource.getUniqueId();
+
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ Either<Set<Resource>, StorageOperationStatus> catalog = resourceOperation.getCatalogData(propertiesToMatch,
+ false);
+ assertTrue(catalog.isLeft());
+ Set<Resource> catalogSet = catalog.left().value();
+ Set<String> idSet = new HashSet<>();
+ for (Resource resource : catalogSet) {
+ idSet.add(resource.getUniqueId());
+ }
+ assertTrue(idSet.contains(resourceId));
+ resourceOperation.deleteResource(resourceId);
+ }
+
+ @Ignore
+ @Test
+ public void testTesterFollowed() {
+ String rootName = "Test1";
+ String rootName2 = "Test2";
+ String rootName3 = "Test3";
+ String userId = "jh0003";
+ String testerUserId = "tt0004";
+ String category = CATEGORY_NAME;
+ deleteAndCreateUser(testerUserId, "tester", "last");
+ deleteAndCreateCategory(category);
+
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ Either<UserData, TitanOperationStatus> findUser = titanDao.getNode(key, userId, UserData.class);
+ User adminUser = OperationTestsUtil.convertUserDataToUser(findUser.left().value());
+ Either<UserData, TitanOperationStatus> findTesterUser = titanDao.getNode(key, testerUserId, UserData.class);
+ User testerUser = OperationTestsUtil.convertUserDataToUser(findTesterUser.left().value());
+
+ // Create 3 new resources
+ Resource resultResource = createResource(userId, category, rootName, "1.0", null, false, true);
+ log.debug("{}", resultResource);
+ String resourceId = resultResource.getUniqueId();
+ Resource resultResource2 = createResource(userId, category, rootName2, "1.0", null, false, true);
+ log.debug("{}", resultResource2);
+ String resourceId2 = resultResource2.getUniqueId();
+ Resource resultResource3 = createResource(userId, category, rootName3, "1.0", null, false, true);
+ log.debug("{}", resultResource3);
+ String resourceId3 = resultResource3.getUniqueId();
+
+ // update 1 resource to READY_FOR_CERTIFICATION
+ Either<Resource, StorageOperationStatus> certReqResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Resource, resultResource, adminUser, adminUser, false);
+ Resource RFCResource = certReqResponse.left().value();
+ assertEquals(RFCResource.getLifecycleState(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+
+ // update 1 resource to CERTIFICATION_IN_PROGRESS
+ Either<Resource, StorageOperationStatus> startCertificationResponse = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Resource, resultResource2, testerUser, adminUser, false);
+ Resource IPResource = startCertificationResponse.left().value();
+ assertEquals(IPResource.getLifecycleState(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ lifecycleStates.add(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ Either<List<Resource>, StorageOperationStatus> resources = resourceOperation.getTesterFollowed(testerUserId,
+ lifecycleStates, false);
+
+ assertTrue(resources.isLeft());
+ List<Resource> result = resources.left().value();
+
+ List<String> idSet = new ArrayList();
+ for (Resource resource : result) {
+ idSet.add(resource.getUniqueId());
+ }
+ assertTrue(idSet.contains(resourceId));
+ assertTrue(idSet.contains(resourceId2));
+ assertFalse(idSet.contains(resourceId3));
+ resourceOperation.deleteResource(resourceId);
+ resourceOperation.deleteResource(resourceId2);
+ resourceOperation.deleteResource(resourceId3);
+
+ }
+
+ @Test
+ public void getVersionListNotDeleted() {
+ String resName = "myResource";
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ deleteAndCreateCategory(category);
+
+ Resource newResource = createResource(userId, category, resName, "0.1", null, false, true);
+ String resourceId1 = newResource.getUniqueId();
+
+ User admin = new User("j", "h", userId, null, "ADMIN", System.currentTimeMillis());
+ Either<Resource, StorageOperationStatus> checkoutResource = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, newResource, admin, admin, false);
+ assertTrue(checkoutResource.isLeft());
+ Resource newResource2 = checkoutResource.left().value();
+ String resourceId2 = newResource2.getUniqueId();
+
+ Resource newResource3 = createResource(userId, category, resName, "0.1", null, false, true);
+ String resourceId3 = newResource3.getUniqueId();
+
+ Either<Map<String, String>, TitanOperationStatus> versionList = resourceOperation.getVersionList(
+ NodeTypeEnum.Resource, "0.2", newResource2.getUUID(), newResource2.getSystemName(),
+ ResourceMetadataData.class);
+ assertTrue(versionList.isLeft());
+ Map<String, String> versionMap = versionList.left().value();
+
+ assertTrue(versionMap.size() == 2);
+ assertTrue(versionMap.containsValue(resourceId1));
+ assertTrue(versionMap.containsValue(resourceId2));
+ assertFalse(versionMap.containsValue(resourceId3));
+
+ Either<Resource, StorageOperationStatus> deleteResource = resourceOperation.deleteResource(resourceId1);
+ assertTrue(deleteResource.isLeft());
+ deleteResource = resourceOperation.deleteResource(resourceId2);
+ assertTrue(deleteResource.isLeft());
+ deleteResource = resourceOperation.deleteResource(resourceId3);
+ assertTrue(deleteResource.isLeft());
+
+ }
+
+ @Test
+ public void getVersionListWithDeleted() {
+ String resName = "myResource";
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+ deleteAndCreateCategory(category);
+
+ Resource newResource = createResource(userId, category, resName, "0.1", null, false, true);
+ String resourceId1 = newResource.getUniqueId();
+
+ User admin = new User("j", "h", userId, null, "ADMIN", System.currentTimeMillis());
+ Either<Resource, StorageOperationStatus> checkoutResource = (Either<Resource, StorageOperationStatus>) lifecycleOperation
+ .checkoutComponent(NodeTypeEnum.Resource, newResource, admin, admin, false);
+ assertTrue(checkoutResource.isLeft());
+ Resource newResource2 = checkoutResource.left().value();
+ String resourceId2 = newResource2.getUniqueId();
+
+ Either<Resource, StorageOperationStatus> resource = resourceOperation.getResource(resourceId1, false);
+ assertTrue(resource.isLeft());
+ Either<Component, StorageOperationStatus> markResourceToDelete = resourceOperation
+ .markComponentToDelete(resource.left().value(), false);
+ assertTrue(markResourceToDelete.isLeft());
+
+ Either<Map<String, String>, TitanOperationStatus> versionList = resourceOperation.getVersionList(
+ NodeTypeEnum.Resource, "0.2", newResource2.getUUID(), newResource2.getSystemName(),
+ ResourceMetadataData.class);
+
+ assertTrue(versionList.isLeft());
+ Map<String, String> versionMap = versionList.left().value();
+
+ assertTrue(versionMap.size() == 1);
+ assertFalse(versionMap.containsValue(resourceId1));
+ assertTrue(versionMap.containsValue(resourceId2));
+
+ Either<Resource, StorageOperationStatus> deleteResource = resourceOperation.deleteResource(resourceId1);
+ assertTrue(deleteResource.isLeft());
+ deleteResource = resourceOperation.deleteResource(resourceId2);
+ assertTrue(deleteResource.isLeft());
+ }
+
+ @Test
+ public void testDerviedPropertiesInResource() {
+
+ try {
+ String userId = "jh0003";
+ String category = CATEGORY_NAME;
+
+ deleteAndCreateUser(userId, "first_" + userId, "last_" + userId);
+ deleteAndCreateCategory(category);
+
+ Resource createResource1 = createResource(userId, category, "myResource1", "0.1", null, true, false);
+ ResourceMetadataData resourceData = new ResourceMetadataData();
+ resourceData.getMetadataDataDefinition().setUniqueId(createResource1.getUniqueId());
+ resourceData.getMetadataDataDefinition().setState(LifecycleStateEnum.CERTIFIED.name());
+ Either<ResourceMetadataData, TitanOperationStatus> updateNode = titanDao.updateNode(resourceData,
+ ResourceMetadataData.class);
+ assertTrue(updateNode.isLeft());
+
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ String json = gson.toJson(createResource1);
+ log.debug(json);
+
+ Resource createResource2 = createResource(userId, category, "myResource2", "0.1", createResource1.getName(),
+ true, false);
+
+ json = gson.toJson(createResource2);
+ log.debug(json);
+
+ List<PropertyDefinition> propList1 = new ArrayList<>();
+ TitanOperationStatus findAllResourcePropertiesRecursively1 = propertyOperation
+ .findAllResourcePropertiesRecursively(createResource1.getUniqueId(), propList1);
+ assertEquals("check search properties succeed", findAllResourcePropertiesRecursively1,
+ TitanOperationStatus.OK);
+
+ List<PropertyDefinition> propList2 = new ArrayList<>();
+ TitanOperationStatus findAllResourcePropertiesRecursively2 = propertyOperation
+ .findAllResourcePropertiesRecursively(createResource2.getUniqueId(), propList2);
+ assertEquals("check search properties succeed", findAllResourcePropertiesRecursively2,
+ TitanOperationStatus.OK);
+
+ assertEquals("check number of properties", propList1.size() * 2, propList2.size());
+
+ resourceOperation.deleteResource(createResource1.getUniqueId());
+ resourceOperation.deleteResource(createResource2.getUniqueId());
+
+ } finally {
+
+ }
+
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperationTest.java
new file mode 100644
index 0000000000..81a5a5ed1d
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ServiceOperationTest.java
@@ -0,0 +1,964 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ArtifactDefinition;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.DistributionStatusEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.category.CategoryDefinition;
+import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.util.OperationTestsUtil;
+import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
+import org.openecomp.sdc.be.resources.data.ArtifactData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.thinkaurelius.titan.core.TitanGraph;
+//import com.tinkerpop.blueprints.Vertex;
+import com.thinkaurelius.titan.core.TitanVertex;
+
+import fj.data.Either;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:application-context-test.xml")
+public class ServiceOperationTest extends ModelTestBase {
+
+ @javax.annotation.Resource(name = "titan-generic-dao")
+ private TitanGenericDao titanDao;
+
+ @javax.annotation.Resource(name = "service-operation")
+ private ServiceOperation serviceOperation;
+
+ @javax.annotation.Resource
+ private IGraphLockOperation graphLockOperation;
+
+ @javax.annotation.Resource
+ private ArtifactOperation artifactOperation;
+
+ @javax.annotation.Resource(name = "requirement-operation")
+ private RequirementOperation requirementOperation;
+
+ @javax.annotation.Resource(name = "resource-operation")
+ private ResourceOperation resourceOperation;
+
+ @javax.annotation.Resource(name = "property-operation")
+ private PropertyOperation propertyOperation;
+
+ @javax.annotation.Resource(name = "capability-operation")
+ private CapabilityOperation capabilityOperation;
+
+ @javax.annotation.Resource(name = "capability-type-operation")
+ private CapabilityTypeOperation capabilityTypeOperation;
+
+ @javax.annotation.Resource(name = "component-instance-operation")
+ private ComponentInstanceOperation resourceInstanceOperation;
+
+ @javax.annotation.Resource(name = "lifecycle-operation")
+ private LifecycleOperation lifecycleOperation;
+
+ private static Logger log = LoggerFactory.getLogger(ServiceOperation.class.getName());
+ private static String USER_ID = "muserId";
+ private static String CATEGORY_NAME = "category/mycategory";
+
+ @BeforeClass
+ public static void setupBeforeClass() {
+ // ExternalConfiguration.setAppName("catalog-model");
+ // String appConfigDir = "src/test/resources/config/catalog-model";
+ // ConfigurationSource configurationSource = new
+ // FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ // appConfigDir);
+
+ // configurationManager = new ConfigurationManager(new
+ // ConfigurationSource() {
+ //
+ // @Override
+ // public <T> T getAndWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T> void addWatchConfiguration(Class<T> className,
+ // ConfigurationListener configurationListener) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ // });
+
+ // String appConfigDir = "src/test/resources/config";
+ // ConfigurationSource configurationSource = new
+ // FSConfigurationSource(ExternalConfiguration.getChangeListener(),
+ // appConfigDir);
+ // configurationManager = new ConfigurationManager(configurationSource);
+ //
+ // Configuration configuration = new Configuration();
+ // configuration.setTitanInMemoryGraph(true);
+ //// configuration.setTitanInMemoryGraph(false);
+ //// configuration.setTitanCfgFile("C:\\Dev\\d2\\D2-SDnC\\catalog-be\\src\\main\\resources\\config\\titan.properties");
+ //
+ // configurationManager.setConfiguration(configuration);
+
+ ModelTestBase.init();
+ }
+
+ @Before
+ public void createUserAndCategory() {
+ deleteAndCreateCategory(CATEGORY_NAME);
+ deleteAndCreateUser(USER_ID, "first_" + USER_ID, "last_" + USER_ID, null);
+ }
+
+ @Test
+ public void dummyTest() {
+
+ }
+
+ @Test
+ public void testCreateService() {
+ String category = CATEGORY_NAME;
+ String serviceName = "servceTest";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug(" *** create **");
+ log.debug("{}", serviceAfterSave);
+ String uniqueId = serviceAfterSave.getUniqueId();
+
+ Either<Service, StorageOperationStatus> serviceGet = serviceOperation.getService(uniqueId);
+ assertTrue(serviceGet.isLeft());
+ log.debug(" *** get **");
+ log.debug("{}", serviceGet.left().value());
+
+ Either<Service, StorageOperationStatus> serviceDelete = serviceOperation.deleteService(uniqueId);
+
+ assertTrue(serviceDelete.isLeft());
+ log.debug(" *** delete **");
+ log.debug("{}", serviceDelete.left().value());
+
+ Either<List<ArtifactData>, TitanOperationStatus> artifacts = titanDao.getByCriteria(NodeTypeEnum.ArtifactRef,
+ null, ArtifactData.class);
+ assertTrue(artifacts.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, artifacts.right().value());
+
+ serviceOperation.deleteService(serviceAfterSave.getUniqueId());
+ }
+
+ @Test
+ public void testUtilsService() {
+ String category = CATEGORY_NAME;
+ String serviceName = "servceTest2";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug(" *** create **");
+ log.debug("{}", serviceAfterSave);
+ String uniqueId = serviceAfterSave.getUniqueId();
+
+ boolean canWorkOnComponent = ComponentValidationUtils.canWorkOnComponent(uniqueId, serviceOperation, userId);
+ assertTrue(canWorkOnComponent);
+
+ canWorkOnComponent = ComponentValidationUtils.canWorkOnComponent(serviceAfterSave, userId);
+ assertTrue(canWorkOnComponent);
+
+ StorageOperationStatus lockComponent = graphLockOperation.lockComponent(uniqueId, NodeTypeEnum.Service);
+ assertEquals(StorageOperationStatus.OK, lockComponent);
+
+ lockComponent = graphLockOperation.unlockComponent(uniqueId, NodeTypeEnum.Service);
+ assertEquals(StorageOperationStatus.OK, lockComponent);
+
+ Either<Service, StorageOperationStatus> serviceDelete = serviceOperation.deleteService(uniqueId);
+ }
+
+ @Test
+ public void testInstanceCounter() {
+ String category = CATEGORY_NAME;
+ String serviceName = "servceTest2";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug(" *** create **");
+ log.debug("{}", serviceAfterSave);
+
+ Either<Integer, StorageOperationStatus> counter = serviceOperation
+ .increaseAndGetComponentInstanceCounter(serviceAfterSave.getUniqueId(), NodeTypeEnum.Service, false);
+ assertTrue(counter.isLeft());
+ assertEquals(new Integer(1), (Integer) counter.left().value());
+
+ counter = serviceOperation.increaseAndGetComponentInstanceCounter(serviceAfterSave.getUniqueId(),
+ NodeTypeEnum.Service, false);
+ assertTrue(counter.isLeft());
+ assertEquals(new Integer(2), (Integer) counter.left().value());
+ Either<Service, StorageOperationStatus> serviceDelete = serviceOperation
+ .deleteService(serviceAfterSave.getUniqueId());
+ }
+
+ @Test
+ public void testAddArtifactToService() {
+ String category = CATEGORY_NAME;
+ String serviceName = "servceTest2";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug("{}", serviceAfterSave);
+ String serviceId = serviceAfterSave.getUniqueId();
+
+ ArtifactDefinition artifactInfo = addArtifactToService(userId, serviceId, "install_apache");
+
+ Either<Service, StorageOperationStatus> service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ Map<String, ArtifactDefinition> artifacts = service.left().value().getArtifacts();
+ assertEquals(1, artifacts.size());
+
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ String artifactId = entry.getValue().getUniqueId();
+ String description = entry.getValue().getDescription();
+ assertEquals("hdkfhskdfgh", description);
+
+ artifactInfo.setDescription("jghlsk new desfnjdh");
+
+ artifactOperation.updateArifactOnResource(artifactInfo, serviceId, artifactId, NodeTypeEnum.Service, false);
+ }
+
+ service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ artifacts = service.left().value().getArtifacts();
+ for (Map.Entry<String, ArtifactDefinition> entry : artifacts.entrySet()) {
+ String artifactId = entry.getValue().getUniqueId();
+ String description = entry.getValue().getDescription();
+ assertEquals("jghlsk new desfnjdh", description);
+
+ artifactOperation.removeArifactFromResource(serviceId, artifactId, NodeTypeEnum.Service, true, false);
+ }
+ service = serviceOperation.getService(serviceId);
+ assertTrue(service.isLeft());
+
+ artifacts = service.left().value().getArtifacts();
+ assertEquals(0, artifacts.size());
+
+ Either<Service, StorageOperationStatus> serviceDelete = serviceOperation.deleteService(serviceId);
+
+ Either<List<ArtifactData>, TitanOperationStatus> byCriteria = titanDao.getByCriteria(NodeTypeEnum.ArtifactRef,
+ null, ArtifactData.class);
+ assertTrue(byCriteria.isRight());
+ assertEquals(TitanOperationStatus.NOT_FOUND, byCriteria.right().value());
+
+ serviceOperation.deleteService(serviceAfterSave.getUniqueId());
+
+ }
+
+ private ArtifactDefinition addArtifactToService(String userId, String serviceId, String artifactName) {
+ ArtifactDefinition artifactInfo = new ArtifactDefinition();
+
+ artifactInfo.setArtifactName(artifactName + ".sh");
+ artifactInfo.setArtifactType("SHELL");
+ artifactInfo.setDescription("hdkfhskdfgh");
+ artifactInfo.setPayloadData("UEsDBAoAAAAIAAeLb0bDQz");
+
+ artifactInfo.setUserIdCreator(userId);
+ String fullName = "Jim H";
+ artifactInfo.setUpdaterFullName(fullName);
+ long time = System.currentTimeMillis();
+ artifactInfo.setCreatorFullName(fullName);
+ artifactInfo.setCreationDate(time);
+ artifactInfo.setLastUpdateDate(time);
+
+ artifactInfo.setUserIdLastUpdater(userId);
+ artifactInfo.setArtifactLabel(artifactName);
+ artifactInfo.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(serviceId, artifactInfo.getArtifactLabel()));
+
+ Either<ArtifactDefinition, StorageOperationStatus> artifact = artifactOperation
+ .addArifactToComponent(artifactInfo, serviceId, NodeTypeEnum.Service, true, true);
+ assertTrue(artifact.isLeft());
+ return artifactInfo;
+ }
+
+ @Test
+ public void testFollowed() {
+ String category = CATEGORY_NAME;
+ String serviceName = "servceTest2";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug("{}", serviceAfterSave);
+ String serviceId = serviceAfterSave.getUniqueId();
+
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ lifecycleStates.add(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ Set<LifecycleStateEnum> lastStateStates = new HashSet<LifecycleStateEnum>();
+ lastStateStates.add(LifecycleStateEnum.CERTIFIED);
+
+ Either<List<Service>, StorageOperationStatus> followed = serviceOperation.getFollowed(userId, lifecycleStates,
+ lastStateStates, false);
+ assertTrue(followed.isLeft());
+ List<Service> list = followed.left().value();
+ assertEquals(1, list.size());
+ serviceOperation.deleteService(serviceId);
+ }
+
+ @Test
+ public void testUpdateService() {
+ String category = CATEGORY_NAME;
+ String serviceName = "12";
+ String serviceVersion = "0.1";
+ String userId = USER_ID;
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug("{}", serviceAfterSave);
+ String serviceId = serviceAfterSave.getUniqueId();
+ serviceAfterSave.setDescription("new description");
+ Either<Service, StorageOperationStatus> updateService = serviceOperation.updateService(serviceAfterSave, false);
+ assertTrue(updateService.isLeft());
+
+ titanDao.commit();
+
+ Either<Service, StorageOperationStatus> serviceAfterUpdate = serviceOperation.getService(serviceId, false);
+ assertTrue(serviceAfterUpdate.isLeft());
+
+ serviceOperation.deleteService(serviceId);
+ assertEquals("new description", serviceAfterUpdate.left().value().getDescription());
+
+ }
+
+ public Service createService(String userId, String category, String serviceName, String serviceVersion,
+ boolean isHighestVersion) {
+
+ Service service = buildServiceMetadata(userId, category, serviceName, serviceVersion);
+
+ service.setHighestVersion(isHighestVersion);
+
+ Either<Service, StorageOperationStatus> result = serviceOperation.createService(service, true);
+
+ log.info(result.toString());
+ assertTrue(result.isLeft());
+ Service resultService = result.left().value();
+
+ // assertEquals("check resource unique id",
+ // UniqueIdBuilder.buildServiceUniqueId(serviceName, serviceVersion),
+ // resultService.getUniqueId());
+ assertEquals("check resource state", LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT,
+ resultService.getLifecycleState());
+
+ return resultService;
+ }
+
+ // @Test
+ public void testCloneFullService() {
+ // try{
+ String userId = USER_ID;
+ // Either<Service, StorageOperationStatus> deleteService =
+ // serviceOperation.deleteService(UniqueIdBuilder.buildServiceUniqueId("my-service",
+ // "1.0"), false);
+ // log.info("testCloneFullService - after delete service. result
+ // is="+deleteService);
+ Service origService = createService(userId, CATEGORY_NAME, "my-service", "1.0", true);
+
+ // add artifacts
+ addArtifactToService(userId, origService.getUniqueId(), "install_apache");
+ addArtifactToService(userId, origService.getUniqueId(), "start_apache");
+
+ // add resource instances
+ ResourceInstanceOperationTest riTest = new ResourceInstanceOperationTest();
+ riTest.setOperations(titanDao, capabilityTypeOperation, requirementOperation, capabilityOperation,
+ resourceOperation, propertyOperation, resourceInstanceOperation);
+ riTest.addResourceInstancesAndRelation(origService.getUniqueId());
+
+ Either<Service, StorageOperationStatus> service2 = serviceOperation.getService(origService.getUniqueId(),
+ false);
+ assertTrue(service2.isLeft());
+ origService = service2.left().value();
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+ String json = prettyGson.toJson(origService);
+ log.debug(json);
+
+ Service fullService = origService;
+
+ Either<Service, StorageOperationStatus> createService = serviceOperation.cloneService(fullService, "2.0",
+ false);
+ assertTrue(createService.isLeft());
+ Either<Service, StorageOperationStatus> serviceAfterCreate = serviceOperation
+ .getServiceByNameAndVersion("my-service", "2.0", null, false);
+ assertTrue(serviceAfterCreate.isLeft());
+ fullService = serviceAfterCreate.left().value();
+
+ Either<Service, StorageOperationStatus> getOrigService = serviceOperation
+ .getServiceByNameAndVersion("my-service", "1.0", null, false);
+ assertTrue(getOrigService.isLeft());
+ origService = getOrigService.left().value();
+
+ // assertEquals(origService.getComponentMetadataDefinition(),
+ // fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getArtifacts().size(), fullService.getArtifacts().size());
+ assertEquals(origService.getComponentInstances().size(), fullService.getComponentInstances().size());
+ assertEquals(origService.getComponentInstancesRelations().size(),
+ fullService.getComponentInstancesRelations().size());
+
+ origService.setUniqueId(fullService.getUniqueId());
+ origService.setVersion(fullService.getVersion());
+
+ assertEquals(origService.getComponentMetadataDefinition(), fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getCategories(), fullService.getCategories());
+
+ serviceOperation.deleteService(origService.getUniqueId());
+ serviceOperation.deleteService(serviceAfterCreate.left().value().getUniqueId());
+
+ // } finally {
+ // titanDao.rollback();
+ // Either<Service, StorageOperationStatus> serviceAfterCreate =
+ // serviceOperation.getService(UniqueIdBuilder.buildServiceUniqueId("my-service",
+ // "2.0"), true);
+ // assertTrue(serviceAfterCreate.isRight());
+ //
+ // Either<Service, StorageOperationStatus> getOrigService =
+ // serviceOperation.getService(UniqueIdBuilder.buildServiceUniqueId("my-service",
+ // "1.0"), true);
+ // assertTrue(getOrigService.isRight());
+ // titanDao.rollback();
+ // }
+ }
+
+ // @Test
+ public void testCloneServiceWithoutResourceInstances() {
+ // try{
+ String userId = USER_ID;
+ // Either<Service, StorageOperationStatus> deleteService =
+ // serviceOperation.deleteService(UniqueIdBuilder.buildServiceUniqueId("my-service",
+ // "1.0"), false);
+ // log.info("testCloneServiceWithoutResourceInstances - after delete
+ // service. result is="+deleteService);
+ Service origService = createService(userId, CATEGORY_NAME, "my-service", "1.0", true);
+
+ // add artifacts
+ addArtifactToService(userId, origService.getUniqueId(), "install_apache");
+ addArtifactToService(userId, origService.getUniqueId(), "start_apache");
+
+ Either<Service, StorageOperationStatus> service2 = serviceOperation.getService(origService.getUniqueId(),
+ false);
+ assertTrue(service2.isLeft());
+ origService = service2.left().value();
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+ String json = prettyGson.toJson(origService);
+ log.debug(json);
+
+ Service fullService = origService;
+
+ Either<Service, StorageOperationStatus> createService = serviceOperation.cloneService(fullService, "2.0",
+ false);
+ assertTrue(createService.isLeft());
+ Either<Service, StorageOperationStatus> serviceAfterCreate = serviceOperation
+ .getServiceByNameAndVersion("my-service", "2.0", null, false);
+ assertTrue(serviceAfterCreate.isLeft());
+ fullService = serviceAfterCreate.left().value();
+
+ Either<Service, StorageOperationStatus> getOrigService = serviceOperation
+ .getServiceByNameAndVersion("my-service", "1.0", null, false);
+ assertTrue(getOrigService.isLeft());
+ origService = getOrigService.left().value();
+
+ // assertEquals(origService.getComponentMetadataDefinition(),
+ // fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getArtifacts().size(), fullService.getArtifacts().size());
+ assertEquals(origService.getComponentInstances(), fullService.getComponentInstances());
+ assertEquals(origService.getComponentInstancesRelations(), fullService.getComponentInstancesRelations());
+
+ origService.setUniqueId(fullService.getUniqueId());
+ origService.setVersion(fullService.getVersion());
+
+ assertEquals(origService.getComponentMetadataDefinition(), fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getCategories(), fullService.getCategories());
+
+ serviceOperation.deleteService(getOrigService.left().value().getUniqueId());
+ serviceOperation.deleteService(serviceAfterCreate.left().value().getUniqueId());
+
+ // } finally {
+ // titanDao.rollback();
+ // }
+ }
+
+ // @Test
+ public void testCloneServiceWithoutArtifacts() {
+ // try{
+
+ String userId = USER_ID;
+
+ Service origService = createService(userId, CATEGORY_NAME, "my-service", "1.0", true);
+
+ // add resource instances
+ ResourceInstanceOperationTest riTest = new ResourceInstanceOperationTest();
+ riTest.setOperations(titanDao, capabilityTypeOperation, requirementOperation, capabilityOperation,
+ resourceOperation, propertyOperation, resourceInstanceOperation);
+ riTest.addResourceInstancesAndRelation(origService.getUniqueId());
+
+ Either<Service, StorageOperationStatus> service2 = serviceOperation.getService(origService.getUniqueId(),
+ false);
+ assertTrue(service2.isLeft());
+ origService = service2.left().value();
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+ String json = prettyGson.toJson(origService);
+ log.debug(json);
+
+ Service fullService = origService;
+
+ Either<Service, StorageOperationStatus> createService = serviceOperation.cloneService(fullService, "2.0",
+ false);
+ assertTrue(createService.isLeft());
+ Either<Service, StorageOperationStatus> serviceAfterCreate = serviceOperation
+ .getServiceByNameAndVersion("my-service", "2.0", null, false);
+ assertTrue(serviceAfterCreate.isLeft());
+ fullService = serviceAfterCreate.left().value();
+
+ Either<Service, StorageOperationStatus> getOrigService = serviceOperation
+ .getServiceByNameAndVersion("my-service", "1.0", null, false);
+ assertTrue(getOrigService.isLeft());
+ origService = getOrigService.left().value();
+
+ assertEquals(origService.getArtifacts(), fullService.getArtifacts());
+ assertEquals(origService.getComponentInstances().size(), fullService.getComponentInstances().size());
+ assertEquals(origService.getComponentInstancesRelations().size(),
+ fullService.getComponentInstancesRelations().size());
+
+ origService.setUniqueId(fullService.getUniqueId());
+ origService.setVersion(fullService.getVersion());
+
+ assertEquals(origService.getComponentMetadataDefinition(), fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getCategories(), fullService.getCategories());
+
+ serviceOperation.deleteService(serviceAfterCreate.left().value().getUniqueId());
+ serviceOperation.deleteService(getOrigService.left().value().getUniqueId());
+
+ // } finally {
+ // titanDao.rollback();
+ // }
+ }
+
+ // @Test
+ public void testCloneServiceSimple() {
+ // try{
+ String userId = USER_ID;
+ String serviceName = "serviceToClone";
+ //
+ // Either<Service, StorageOperationStatus> deleteService =
+ // serviceOperation.deleteService(UniqueIdBuilder.buildServiceUniqueId(serviceName,
+ // "1.0"));
+ // log.info("testCloneServiceSimple - after delete service. result
+ // is="+deleteService);
+
+ Service origService = createService(userId, CATEGORY_NAME, serviceName, "1.0", true);
+
+ Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
+ String json = prettyGson.toJson(origService);
+ log.debug(json);
+
+ Service fullService = origService;
+
+ Either<Service, StorageOperationStatus> createService = serviceOperation.cloneService(fullService, "2.0",
+ false);
+ assertTrue(createService.isLeft());
+ Either<Service, StorageOperationStatus> serviceAfterCreate = serviceOperation
+ .getServiceByNameAndVersion(serviceName, "2.0", null, false);
+ assertTrue(serviceAfterCreate.isLeft());
+ fullService = serviceAfterCreate.left().value();
+
+ Either<Service, StorageOperationStatus> getOrigService = serviceOperation
+ .getServiceByNameAndVersion(serviceName, "1.0", null, false);
+ assertTrue(getOrigService.isLeft());
+ origService = getOrigService.left().value();
+
+ // assertEquals(origService.getComponentMetadataDefinition(),
+ // fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getArtifacts(), fullService.getArtifacts());
+ assertEquals(origService.getComponentInstances(), fullService.getComponentInstances());
+ assertEquals(origService.getComponentInstancesRelations(), fullService.getComponentInstancesRelations());
+ origService.setUniqueId(fullService.getUniqueId());
+ origService.setVersion(fullService.getVersion());
+
+ assertEquals(origService.getComponentMetadataDefinition(), fullService.getComponentMetadataDefinition());
+ assertEquals(origService.getCategories(), fullService.getCategories());
+
+ serviceOperation.deleteService(getOrigService.left().value().getUniqueId());
+ serviceOperation.deleteService(serviceAfterCreate.left().value().getUniqueId());
+
+ // } finally {
+ // titanDao.rollback();
+ // }
+ }
+
+ private Service buildServiceMetadata(String userId, String category, String serviceName, String serviceVersion) {
+
+ Service service = new Service();
+ service.setName(serviceName);
+ service.setVersion(serviceVersion);
+ service.setDescription("description 1");
+
+ service.setCreatorUserId(userId);
+ service.setContactId("contactId@sdc.com");
+ CategoryDefinition categoryDef = new CategoryDefinition();
+ categoryDef.setName(category);
+
+ List<CategoryDefinition> categories = new ArrayList<>();
+ categories.add(categoryDef);
+ service.setCategories(categories);
+
+ service.setIcon("images/my.png");
+ List<String> tags = new ArrayList<String>();
+ tags.add("TAG1");
+ tags.add("TAG2");
+ service.setTags(tags);
+ return service;
+ }
+
+ private void deleteAndCreateCategory(String category) {
+ String[] names = category.split("/");
+ OperationTestsUtil.deleteAndCreateServiceCategory(category, titanDao);
+ OperationTestsUtil.deleteAndCreateResourceCategory(names[0], names[1], titanDao);
+
+ /*
+ * CategoryData categoryData = new CategoryData();
+ * categoryData.setName(category);
+ *
+ * titanDao.deleteNode(categoryData, CategoryData.class);
+ * Either<CategoryData, TitanOperationStatus> createNode =
+ * titanDao.createNode(categoryData, CategoryData.class);
+ * System.out.println("after creating caetgory " + createNode);
+ */
+
+ }
+
+ private UserData deleteAndCreateUser(String userId, String firstName, String lastName, String role) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setFirstName(firstName);
+ userData.setLastName(lastName);
+ if (role != null && !role.isEmpty()) {
+ userData.setRole(role);
+ } else {
+ userData.setRole("ADMIN");
+ }
+
+ titanDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class);
+ titanDao.createNode(userData, UserData.class);
+ titanDao.commit();
+
+ return userData;
+ }
+
+ @Test
+ public void testCatalogService() {
+ String userId = USER_ID;
+ String category = CATEGORY_NAME;
+ String serviceName = "MyService";
+ String serviceVersion = "0.1";
+ Service serviceAfterSave = createService(userId, category, serviceName, serviceVersion, true);
+ log.debug("{}", serviceAfterSave);
+ String serviceId = serviceAfterSave.getUniqueId();
+
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ Either<Set<Service>, StorageOperationStatus> catalog = serviceOperation.getCatalogData(propertiesToMatch,
+ false);
+ assertTrue(catalog.isLeft());
+ Set<Service> catalogSet = catalog.left().value();
+ Set<String> idSet = new HashSet<>();
+ for (Service service : catalogSet) {
+ idSet.add(service.getUniqueId());
+ }
+ assertTrue(idSet.contains(serviceId));
+ serviceOperation.deleteService(serviceId);
+ }
+
+ @After
+ public void teardown() {
+ clearGraph();
+ }
+
+ private void clearGraph() {
+ Either<TitanGraph, TitanOperationStatus> graphResult = titanDao.getGraph();
+ TitanGraph graph = graphResult.left().value();
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ TitanVertex vertex = iterator.next();
+ // graph.removeVertex(vertex);
+ vertex.remove();
+ }
+
+ }
+ titanDao.commit();
+ }
+
+ @Test
+ public void testTesterFollowed() {
+ String serviceName = "Test1";
+ String serviceName2 = "Test2";
+ String serviceName3 = "Test3";
+ String userId = USER_ID;
+ String testerUserId = "tt0004";
+ String category = CATEGORY_NAME;
+ deleteAndCreateUser(testerUserId, "tester", "last", "TESTER");
+ // deleteAndCreateCategory(category);
+
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ Either<UserData, TitanOperationStatus> findUser = titanDao.getNode(key, userId, UserData.class);
+ User adminUser = OperationTestsUtil.convertUserDataToUser(findUser.left().value());
+ Either<UserData, TitanOperationStatus> findTesterUser = titanDao.getNode(key, testerUserId, UserData.class);
+ User testerUser = OperationTestsUtil.convertUserDataToUser(findTesterUser.left().value());
+
+ // Create 3 new services
+ Service resultService = createService(userId, category, serviceName, "0.1", false);
+ log.debug("{}", resultService);
+ String serviceId = resultService.getUniqueId();
+ Service resultService2 = createService(userId, category, serviceName2, "0.1", false);
+ log.debug("{}", resultService2);
+ String serviceId2 = resultService2.getUniqueId();
+ Service resultService3 = createService(userId, category, serviceName3, "0.1", false);
+ log.debug("{}", resultService3);
+ String serviceId3 = resultService3.getUniqueId();
+
+ // update 1 service to READY_FOR_CERTIFICATION
+ Either<? extends Component, StorageOperationStatus> certReqResponse = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService, adminUser, adminUser, false);
+ Service RFCService = (Service) certReqResponse.left().value();
+ assertEquals(RFCService.getLifecycleState(), LifecycleStateEnum.READY_FOR_CERTIFICATION);
+
+ // update 1 service to CERTIFICATION_IN_PROGRESS
+ Either<? extends Component, StorageOperationStatus> startCertificationResponse = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService2, testerUser, adminUser, false);
+ Service IPService = (Service) startCertificationResponse.left().value();
+ assertEquals(IPService.getLifecycleState(), LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ Set<LifecycleStateEnum> lifecycleStates = new HashSet<LifecycleStateEnum>();
+ lifecycleStates.add(LifecycleStateEnum.CERTIFICATION_IN_PROGRESS);
+
+ Either<List<Service>, StorageOperationStatus> services = serviceOperation.getTesterFollowed(testerUserId,
+ lifecycleStates, false);
+
+ assertTrue(services.isLeft());
+ List<Service> result = services.left().value();
+
+ List<String> ids = new ArrayList<>();
+ for (Service service : result) {
+ ids.add(service.getUniqueId());
+ }
+ assertTrue(ids.contains(serviceId));
+ assertTrue(ids.contains(serviceId2));
+ assertFalse(ids.contains(serviceId3));
+ serviceOperation.deleteService(serviceId);
+ serviceOperation.deleteService(serviceId2);
+ serviceOperation.deleteService(serviceId3);
+
+ }
+
+ @Test
+ public void testOpsFollowed() {
+ String serviceName = "Test1";
+ String serviceName2 = "Test2";
+ String serviceName3 = "Test3";
+ String serviceName4 = "Test4";
+ String userId = USER_ID;
+ String category = CATEGORY_NAME;
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ Either<UserData, TitanOperationStatus> findUser = titanDao.getNode(key, userId, UserData.class);
+ User adminUser = OperationTestsUtil.convertUserDataToUser(findUser.left().value());
+
+ // Create 4 new services
+ Service resultService = createService(userId, category, serviceName, "0.1", false);
+ log.debug("{}", resultService);
+ String serviceId = resultService.getUniqueId();
+ Service resultService2 = createService(userId, category, serviceName2, "0.1", false);
+ log.debug("{}", resultService2);
+ String serviceId2 = resultService2.getUniqueId();
+ Service resultService3 = createService(userId, category, serviceName3, "0.1", false);
+ log.debug("{}", resultService3);
+ String serviceId3 = resultService3.getUniqueId();
+ Service resultService4 = createService(userId, category, serviceName4, "0.1", false);
+ log.debug("{}", resultService3);
+ String serviceId4 = resultService4.getUniqueId();
+
+ // update 1 service to CERTIFIED dist status DISTRIBUTED
+ Either<? extends Component, StorageOperationStatus> reqCertificationResult = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService, adminUser, adminUser, false);
+ Either<? extends Component, StorageOperationStatus> startCertificationResult = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService, adminUser, adminUser, false);
+ Service actualService = (Service) startCertificationResult.left().value();
+
+ Either<? extends Component, StorageOperationStatus> certResponse = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Service, resultService, adminUser, adminUser, false);
+ Service certifiedService = (Service) certResponse.left().value();
+ serviceOperation.updateDestributionStatus(resultService, adminUser, DistributionStatusEnum.DISTRIBUTED);
+
+ // update 1 service to CERTIFIED dist status DISTRIBUTION_APPROVED
+ Either<? extends Component, StorageOperationStatus> reqCertificationResult2 = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService2, adminUser, adminUser, false);
+ Either<? extends Component, StorageOperationStatus> startCertificationResult2 = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService2, adminUser, adminUser, false);
+ Service actualService2 = (Service) startCertificationResult2.left().value();
+
+ Either<? extends Component, StorageOperationStatus> certResponse2 = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Service, resultService2, adminUser, adminUser, false);
+ Service certifiedService2 = (Service) certResponse2.left().value();
+ serviceOperation.updateDestributionStatus(resultService2, adminUser,
+ DistributionStatusEnum.DISTRIBUTION_APPROVED);
+
+ // update 1 service to CERTIFIED dist status DISTRIBUTION_REJECTED
+ Either<? extends Component, StorageOperationStatus> reqCertificationResult3 = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService3, adminUser, adminUser, false);
+ Either<? extends Component, StorageOperationStatus> startCertificationResult3 = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService3, adminUser, adminUser, false);
+ Service actualService3 = (Service) startCertificationResult3.left().value();
+
+ Either<? extends Component, StorageOperationStatus> certResponse3 = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Service, actualService3, adminUser, adminUser, false);
+ Service certifiedService3 = (Service) certResponse3.left().value();
+ serviceOperation.updateDestributionStatus(certifiedService3, adminUser,
+ DistributionStatusEnum.DISTRIBUTION_REJECTED);
+
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ Set<DistributionStatusEnum> distStatus = new HashSet<DistributionStatusEnum>();
+ distStatus.add(DistributionStatusEnum.DISTRIBUTION_APPROVED);
+ distStatus.add(DistributionStatusEnum.DISTRIBUTED);
+
+ Either<Set<Service>, StorageOperationStatus> services = serviceOperation
+ .getCertifiedServicesWithDistStatus(propertiesToMatch, distStatus, false);
+
+ assertTrue(services.isLeft());
+ Set<Service> result = services.left().value();
+
+ List<String> ids = new ArrayList<>();
+ for (Service service : result) {
+ ids.add(service.getUniqueId());
+ }
+ assertTrue(ids.contains(certifiedService.getUniqueId()));
+ assertTrue(ids.contains(certifiedService2.getUniqueId()));
+ assertFalse(ids.contains(certifiedService3.getUniqueId()));
+ assertFalse(ids.contains(resultService4.getUniqueId()));
+ serviceOperation.deleteService(serviceId);
+ serviceOperation.deleteService(serviceId2);
+ serviceOperation.deleteService(serviceId3);
+ serviceOperation.deleteService(serviceId4);
+ }
+
+ @Test
+ public void testGovernorFollowed() {
+ String serviceName = "Test1";
+ String serviceName2 = "Test2";
+ String serviceName3 = "Test3";
+ String userId = USER_ID;
+ String category = CATEGORY_NAME;
+ String key = UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User);
+ Either<UserData, TitanOperationStatus> findUser = titanDao.getNode(key, userId, UserData.class);
+ User adminUser = OperationTestsUtil.convertUserDataToUser(findUser.left().value());
+
+ // Create 3 new services
+ Service resultService = createService(userId, category, serviceName, "0.1", false);
+ log.debug("{}", resultService);
+ String serviceId = resultService.getUniqueId();
+ Service resultService2 = createService(userId, category, serviceName2, "0.1", false);
+ log.debug("{}", resultService2);
+ String serviceId2 = resultService2.getUniqueId();
+ Service resultService3 = createService(userId, category, serviceName3, "0.1", false);
+ log.debug("{}", resultService3);
+ String serviceId3 = resultService3.getUniqueId();
+
+ // update 1 service to CERTIFIED + DISTRIBUTED
+ Either<? extends Component, StorageOperationStatus> reqCertificationResult = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService, adminUser, adminUser, false);
+ Either<? extends Component, StorageOperationStatus> startCertificationResult = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService, adminUser, adminUser, false);
+ Service actualService = (Service) startCertificationResult.left().value();
+
+ Either<? extends Component, StorageOperationStatus> certResponse = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Service, actualService, adminUser, adminUser, false);
+ Service certifiedService = (Service) certResponse.left().value();
+ serviceOperation.updateDestributionStatus(certifiedService, adminUser, DistributionStatusEnum.DISTRIBUTED);
+
+ // update 1 service to CERTIFIED dist status + DISTRIBUTION_REJECTED
+ Either<? extends Component, StorageOperationStatus> reqCertificationResult2 = lifecycleOperation
+ .requestCertificationComponent(NodeTypeEnum.Service, resultService2, adminUser, adminUser, false);
+ Either<? extends Component, StorageOperationStatus> startCertificationResult2 = lifecycleOperation
+ .startComponentCertification(NodeTypeEnum.Service, resultService2, adminUser, adminUser, false);
+ Service actualService2 = (Service) startCertificationResult2.left().value();
+
+ Either<? extends Component, StorageOperationStatus> certResponse2 = lifecycleOperation
+ .certifyComponent(NodeTypeEnum.Service, actualService2, adminUser, adminUser, false);
+ Service certifiedService2 = (Service) certResponse2.left().value();
+ serviceOperation.updateDestributionStatus(certifiedService2, adminUser, DistributionStatusEnum.DISTRIBUTED);
+
+ Map<String, Object> propertiesToMatch = new HashMap<>();
+ propertiesToMatch.put(GraphPropertiesDictionary.STATE.getProperty(), LifecycleStateEnum.CERTIFIED.name());
+
+ Either<Set<Service>, StorageOperationStatus> services = serviceOperation
+ .getCertifiedServicesWithDistStatus(propertiesToMatch, null, false);
+
+ assertTrue(services.isLeft());
+ Set<Service> result = services.left().value();
+
+ List<String> ids = new ArrayList<>();
+ for (Service service : result) {
+ ids.add(service.getUniqueId());
+ }
+ assertTrue(ids.contains(certifiedService.getUniqueId()));
+ assertTrue(ids.contains(certifiedService2.getUniqueId()));
+ assertFalse(ids.contains(serviceId3));
+ serviceOperation.deleteService(serviceId);
+ serviceOperation.deleteService(serviceId2);
+ serviceOperation.deleteService(serviceId3);
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperationTest.java
new file mode 100644
index 0000000000..f77e477ed7
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/UserAdminOperationTest.java
@@ -0,0 +1,239 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.dao.utils.UserStatusEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.model.operations.impl.UserAdminOperation;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.common.api.UserRoleEnum;
+
+import fj.data.Either;
+
+public class UserAdminOperationTest extends ModelTestBase {
+ @InjectMocks
+ private static final UserAdminOperation userAdminOperation = new UserAdminOperation();
+ private static final TitanGenericDao titanGenericDao = mock(TitanGenericDao.class);
+
+ private static final String ADMIN = "admin";
+
+ @BeforeClass
+ public static void setup() {
+ ModelTestBase.init();
+ }
+
+ @Before
+ public void initMocks() {
+ MockitoAnnotations.initMocks(this);
+ Mockito.reset(titanGenericDao);
+ mockTitanUpdate();
+ mockTitanDelete();
+
+ }
+
+ @Test
+ public void testDeActivateUserDataSuccess() {
+ UserData userData = mockTitanGet(ADMIN, UserRoleEnum.ADMIN, true);
+
+ Either<User, StorageOperationStatus> eitherUser = userAdminOperation
+ .deActivateUser(userAdminOperation.convertToUser(userData));
+
+ verify(titanGenericDao, times(1)).updateNode(Mockito.eq(userData), Mockito.eq(UserData.class));
+ verify(titanGenericDao, times(0)).deleteNode(Mockito.any(UserData.class), Mockito.eq(UserData.class));
+ assertTrue(eitherUser.isLeft());
+ User user = eitherUser.left().value();
+ assertTrue(user.getStatus() == UserStatusEnum.INACTIVE);
+
+ }
+
+ /*
+ * @Test public void testDeActivateUserDataFail(){ UserData userData =
+ * mockTitanGet(ADMIN, UserRoleEnum.ADMIN, false);
+ *
+ * Either<User, StorageOperationStatus> eitherUser =
+ * userAdminOperation.deActivateUser(userAdminOperation.convertToUser(
+ * userData));
+ *
+ * verify(titanGenericDao, times(0)).updateNode(Mockito.any(UserData.class),
+ * Mockito.eq(UserData.class)); verify(titanGenericDao,
+ * times(0)).deleteNode(Mockito.any(UserData.class),
+ * Mockito.eq(UserData.class)); assertTrue(eitherUser.isRight());
+ * assertTrue(eitherUser.right().value() ==
+ * StorageOperationStatus.USER_INACTIVE);
+ *
+ * }
+ */
+
+ @Test
+ public void testDeleteUserWithoutResources() {
+ UserData userData = mockTitanGet(ADMIN, UserRoleEnum.ADMIN, true);
+
+ List<Edge> edgesList = new ArrayList<Edge>();
+
+ Either<List<Edge>, TitanOperationStatus> eitherResult = Either.left(edgesList);
+ when(titanGenericDao.getEdgesForNode(userData, Direction.BOTH)).thenReturn(eitherResult);
+
+ Either<User, ActionStatus> eitherUser = userAdminOperation.deleteUserData(ADMIN);
+ verify(titanGenericDao, times(0)).updateNode(Mockito.any(UserData.class), Mockito.eq(UserData.class));
+ verify(titanGenericDao, times(1)).deleteNode(userData, UserData.class);
+ assertTrue(eitherUser.isLeft());
+
+ }
+
+ @Test
+ public void testDeleteUserWithResources() {
+ UserData userData = mockTitanGet(ADMIN, UserRoleEnum.ADMIN, true);
+
+ List<Edge> edgesList = new ArrayList<Edge>();
+ edgesList.add(getEmptyEdgeImpl());
+
+ Either<List<Edge>, TitanOperationStatus> eitherResult = Either.left(edgesList);
+ when(titanGenericDao.getEdgesForNode(userData, Direction.BOTH)).thenReturn(eitherResult);
+
+ Either<User, ActionStatus> eitherUser = userAdminOperation.deleteUserData(ADMIN);
+ verify(titanGenericDao, times(0)).updateNode(Mockito.any(UserData.class), Mockito.eq(UserData.class));
+ verify(titanGenericDao, times(0)).deleteNode(Mockito.any(UserData.class), Mockito.eq(UserData.class));
+ assertTrue(eitherUser.isRight());
+ assertTrue(eitherUser.right().value() == ActionStatus.USER_HAS_ACTIVE_ELEMENTS);
+
+ }
+
+ private Edge getEmptyEdgeImpl() {
+ return new Edge() {
+
+ @Override
+ public Object id() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String label() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Graph graph() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <V> Property<V> property(String key, V value) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void remove() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Iterator<Vertex> vertices(Direction direction) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <V> Iterator<Property<V>> properties(String... propertyKeys) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ };
+ }
+
+ private UserData mockTitanGet(String userId, UserRoleEnum role, boolean isActive) {
+ UserData userData = buildUserData(userId, role, isActive);
+ Either<UserData, TitanOperationStatus> eitherUserData = Either.left(userData);
+ when(titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.User), userId, UserData.class))
+ .thenReturn(eitherUserData);
+ return userData;
+ }
+
+ private static void mockTitanUpdate() {
+ doAnswer(new Answer<Either<UserData, TitanOperationStatus>>() {
+ public Either<UserData, TitanOperationStatus> answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ UserData retValue = (UserData) args[0];
+ Either<UserData, TitanOperationStatus> result = Either.left(retValue);
+ return result;
+ }
+
+ }).when(titanGenericDao).updateNode(Mockito.any(UserData.class), Mockito.eq(UserData.class));
+ }
+
+ private static void mockTitanDelete() {
+ doAnswer(new Answer<Either<UserData, TitanOperationStatus>>() {
+ public Either<UserData, TitanOperationStatus> answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ UserData retValue = (UserData) args[0];
+ Either<UserData, TitanOperationStatus> result = Either.left(retValue);
+ return result;
+ }
+
+ }).when(titanGenericDao).deleteNode(Mockito.any(UserData.class), Mockito.eq(UserData.class));
+ }
+
+ private static UserData buildUserData(String userId, UserRoleEnum role, boolean isActive) {
+ UserData userData = new UserData();
+ userData.setUserId(userId);
+ userData.setRole(role.getName());
+ userData.setStatus(isActive ? UserStatusEnum.ACTIVE.name() : UserStatusEnum.INACTIVE.name());
+ return userData;
+
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/DataTypeValidatorTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/DataTypeValidatorTest.java
new file mode 100644
index 0000000000..2b9c296eb5
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/DataTypeValidatorTest.java
@@ -0,0 +1,1004 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl.util;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.Test;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.model.tosca.converters.ListConverter;
+import org.openecomp.sdc.be.model.tosca.converters.MapConverter;
+import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
+import org.openecomp.sdc.be.model.tosca.validators.ListValidator;
+import org.openecomp.sdc.be.model.tosca.validators.MapValidator;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+
+import fj.data.Either;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTypeValidatorTest {
+ private static Logger log = LoggerFactory.getLogger(DataTypeValidatorTest.class.getName());
+ private static Gson gson = new Gson();
+
+ DataTypeValidatorConverter dataTypeValidator = DataTypeValidatorConverter.getInstance();
+
+ @Test
+ public void testDerivedFromPrimitiveEmptyValue() {
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ allDataTypes.put("integer", getPrimitiveDataType("integer"));
+
+ DataTypeDefinition fromIntegerType = buildDerivedFromIntegerType();
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate("", fromIntegerType,
+ allDataTypes);
+
+ assertTrue("check result is valid", validate.right.booleanValue());
+ assertEquals("check value is the same as sent", null, validate.left);
+
+ validate = dataTypeValidator.validateAndUpdate(null, fromIntegerType, allDataTypes);
+
+ assertTrue("check result is valid", validate.right.booleanValue());
+ assertEquals("check value is the same as sent", null, validate.left);
+
+ validate = dataTypeValidator.validateAndUpdate("88", fromIntegerType, allDataTypes);
+
+ assertTrue("check result is valid", validate.right.booleanValue());
+ assertEquals("check value is the same as sent", "88", validate.left.toString());
+
+ }
+
+ @Test
+ public void testCompositeWithParameterDerivedFromPrimitiveEmptyValue() {
+
+ DataTypeDefinition derivedFromIntegerType = buildDerivedFromIntegerType();
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ allDataTypes.put("myinteger", derivedFromIntegerType);
+
+ DataTypeDefinition personDataType = buildPersonDataType();
+
+ Person person = new Person("my address", 32);
+ String json = gson.toJson(person);
+ log.debug(json);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, personDataType,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ person = new Person("my address", 32);
+ json = gson.toJson(person);
+ json = json.replace("32", "32a");
+ log.debug(json);
+
+ validate = dataTypeValidator.validateAndUpdate(json, personDataType, allDataTypes);
+ assertFalse("check valid value", validate.right.booleanValue());
+
+ }
+
+ @Test
+ public void testCompositeWithEmptyListValue() {
+
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ String[] strArr = {};
+ List<String> strList = Arrays.asList(strArr);
+
+ // Check empty list
+ Credential credential = new Credential("protcol<br>>", 5, "token_type", "token", null, "user", true, strList);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+ log.debug(json);
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ Credential credentialRes = gson.fromJson(validate.left.toString(), Credential.class);
+ assertEquals("check empty list", 0, credentialRes.getMylist().size());
+
+ log.debug("Result is = {}", validate.left.toString());
+
+ }
+
+ @Test
+ public void testCompositeWithListNullValue() {
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check list is NULL
+ Credential credential = new Credential("protcol<br>>", 5, "token_type", "token", null, "user", true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ Credential credentialRes = gson.fromJson(validate.left.toString(), Credential.class);
+ assertNull("check list is null", credentialRes.getMylist());
+ log.debug("Result is = {}", validate.left.toString());
+
+ }
+
+ @Test
+ public void testCompositeWithUserNullValue() {
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check user is null
+ Credential credential = new Credential("protcol<br>>", 5, "token_type", "token", null, null, true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ Credential credentialRes = gson.fromJson(validate.left.toString(), Credential.class);
+ assertNull("check list is null", credentialRes.getUser());
+ log.debug("Result is = {}", validate.left.toString());
+ }
+
+ @Test
+ public void testCompositeWithEmptyUserValue() {
+
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+ // Check user is empty
+ Credential credential = new Credential("protcol<br>>", 5, "token_type", "token", null, "", true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+ log.debug(json);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ Credential credentialRes = gson.fromJson(validate.left.toString(), Credential.class);
+ assertNotNull("check list is not null", credentialRes.getUser());
+ assertEquals("check user is empty", "", credentialRes.getUser());
+ log.debug("Result is = {}", validate.left.toString());
+
+ }
+
+ @Test
+ public void testCompositeWithSumNullValue() {
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check user is null
+ Credential credential = new Credential("protcol<br>>", null, "token_type", "token", null, null, true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ Credential credentialRes = gson.fromJson(validate.left.toString(), Credential.class);
+ assertNull("check list is null", credentialRes.getSum());
+ log.debug("Result is = {}", validate.left.toString());
+ }
+
+ @Test
+ public void testInvalidJson() {
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check user is null
+ Credential credential = new Credential("protcol<br>>", null, "token_type", "token", null, null, true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ json += "fdfd";
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertFalse("check valid value", validate.right.booleanValue());
+
+ }
+
+ @Test
+ public void testInvalidInnerValue() {
+
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check user is null
+ Credential credential = new Credential("protcol<br>>", null, "token_type", "token", null, null, true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ json = json.replace("55", "a55b");
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertFalse("check valid value", validate.right.booleanValue());
+
+ }
+
+ @Test
+ public void testInvalidInnerJson() {
+
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check user is null
+ Credential credential = new Credential("protcol<br>>", null, "token_type", "token", null, null, true, null);
+ City mycity = new City("", null);
+
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ json = json.replace("{\"address\":\"\"}", "scalar");
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertFalse("check valid value", validate.right.booleanValue());
+
+ }
+
+ @Test
+ public void testInvalidPropertyJson() {
+
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ // Check user is null
+ Credential credential = new Credential("protcol<br>>", null, "token_type", "token", null, null, true, null);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ json = json.replace("55", "a55b");
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertFalse("check valid value", validate.right.booleanValue());
+
+ }
+
+ @Test
+ public void testCompositeDataTypeWithInternalComposite() {
+
+ DataTypeDefinition dataTypeDefinition = buildCredentialDataType();
+
+ String[] strArr = { "aaa", "bbb", "c<br>dcc" };
+ List<String> strList = Arrays.asList(strArr);
+
+ Credential credential = new Credential("protcol<br>>", 5, "token_type", "token", null, "user", true, strList);
+ City mycity = new City("myadd<br><<br>", 55);
+ credential.setMycity(mycity);
+
+ String json = gson.toJson(credential);
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ ImmutablePair<JsonElement, Boolean> validate = dataTypeValidator.validateAndUpdate(json, dataTypeDefinition,
+ allDataTypes);
+ assertTrue("check valid value", validate.right.booleanValue());
+
+ log.debug("Result is = {}", validate.left.toString());
+
+ }
+
+ @Test
+ public void testMapValidator() {
+
+ MapValidator validator = new MapValidator();
+ Gson gson = new Gson();
+ // Happy Scenarios
+ // 1 - Map<String,Integer> check OK
+ Map<String, Integer> map_1 = new HashMap<>();
+ map_1.put("key1", 2);
+ map_1.put("key2", 3);
+ String value = gson.toJson(map_1);
+ String innerType = "integer";
+ assertTrue("Test Map validation with inner integer type", validator.isValid(value, innerType, null));
+
+ // 2 - Map<String,Boolean> check OK
+ Map<String, Boolean> map_2 = new HashMap<>();
+ map_2.put("key1", true);
+ map_2.put("key2", false);
+ value = gson.toJson(map_2);
+ innerType = "boolean";
+ assertTrue("Test Map validation with inner boolean type", validator.isValid(value, innerType, null));
+
+ // 3 - give integer with quotes
+ innerType = "integer";
+ value = "{\"key1\":\"5\",\"key2\":\"7\"}";
+ assertTrue("Test Map validation with inner integer type, but qouted values",
+ validator.isValid(value, innerType, null));
+
+ // 4 - empty default value
+ innerType = "float";
+ value = "";
+ assertTrue("Test Map validation with inner float type", validator.isValid(value, innerType, null));
+
+ // Faulty Scenarios
+ // 5 - mismatch in data type
+ value = gson.toJson(map_1);
+ innerType = "boolean";
+ assertFalse("Test Map faulty validation with inner boolean type", validator.isValid(value, innerType, null));
+ // 6 - mismatch in data type
+ value = gson.toJson(map_2);
+ innerType = "integer";
+ assertFalse("Test Map faulty validation with inner integer type", validator.isValid(value, innerType, null));
+
+ }
+
+ @Test
+ public void testMapConverter() {
+
+ MapConverter converter = new MapConverter();
+ Gson gson = new Gson();
+ // Happy Scenarios
+ Map<String, String> map_1 = new HashMap<>();
+ Map<String, String> resMap_1 = new HashMap<>();
+
+ // 1 - check Spaces eliminated + html square brackets eliminated
+ map_1.put("key1", "<b>test</b>");
+ map_1.put("key2", " test");
+ resMap_1.put("key1", "test");
+ resMap_1.put("key2", " test");
+ String value = gson.toJson(map_1);
+ String expectedVal = gson.toJson(resMap_1);
+ String innerType = "string";
+ assertEquals("Test Map validation with inner string type", expectedVal,
+ converter.convert(value, innerType, null));
+
+ // 2 - float converter
+ innerType = "float";
+ value = "{\"key1\":0.4545,\"key2\":0.2f}";
+ expectedVal = "{\"key1\":0.4545,\"key2\":0.2}";
+ assertEquals("Test Map validation with inner float type", expectedVal,
+ converter.convert(value, innerType, null));
+
+ // 3 - check default empty value converter
+ innerType = "float";
+ value = "";
+ expectedVal = "";
+ assertEquals("Test Map validation with inner float type", expectedVal,
+ converter.convert(value, innerType, null));
+
+ // 4 - invalid json
+ // 3 - check default empty value converter
+ innerType = "float";
+ value = "{1345234556@#(";
+ expectedVal = null;
+ assertEquals("Test Map validation with inner float type", expectedVal,
+ converter.convert(value, innerType, null));
+
+ }
+
+ @Test
+ public void testCompositeDataTypeWithMapComposite() {
+
+ DataTypeDefinition fileDataTypeDefinition = buildFileDataType();
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+
+ MyFile myFile = new MyFile();
+ myFile.setAge(88);
+ Map<String, City> attributes = new HashMap<>();
+ attributes.put("key1", new City("address1<br>", 11));
+ attributes.put("key2", new City("address2<br>", 22));
+ myFile.setAttributes(attributes);
+
+ String str = gson.toJson(myFile);
+ log.debug(str);
+
+ ImmutablePair<JsonElement, Boolean> convert = dataTypeValidator.validateAndUpdate(str, fileDataTypeDefinition,
+ allDataTypes);
+
+ assertTrue("check map converter succeed", convert.right);
+
+ JsonElement convertedValue = convert.left;
+
+ log.debug("{}", convertedValue);
+ MyFile fromJson = gson.fromJson(convertedValue, MyFile.class);
+
+ assertEquals("check age", 88, fromJson.getAge().intValue());
+ assertEquals("check address 1", "address1", fromJson.getAttributes().get("key1").getAddress());
+ assertEquals("check address 2", "address2", fromJson.getAttributes().get("key2").getAddress());
+
+ }
+
+ @Test
+ public void testMapConverterWithComplexInnerType() {
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition credentialDataTypeDefinition = buildCredentialDataType();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+ allDataTypes.put("credential", credentialDataTypeDefinition);
+
+ Gson gson = new Gson();
+ // Happy Scenarios
+ Map<String, Object> map_1 = new HashMap<>();
+
+ // 1 - check Spaces eliminated + html square brackets eliminated
+
+ String[] strArr = { "aaa", "bbb", "c<br>dcc" };
+ List<String> strList = Arrays.asList(strArr);
+ Credential credential1 = new Credential("protocol;:,.\"<br>>", 5, "token_type", "token", null, "user", true,
+ strList);
+ City mycity1 = new City("myadd<br><<br>", 55);
+ credential1.setMycity(mycity1);
+
+ Credential credential2 = new Credential("protocol;:,.\"<br>>", 5, "token_type", "token", null, "user", true,
+ strList);
+ City mycity2 = new City("myadd<br><<br>", 66);
+ credential2.setMycity(mycity2);
+
+ map_1.put("key1", credential1);
+ map_1.put("key2", credential2);
+
+ String str = gson.toJson(map_1);
+ log.debug(str);
+
+ MapConverter mapConverter = new MapConverter();
+ Either<String, Boolean> convert = mapConverter.convertWithErrorResult(str, "credential", allDataTypes);
+
+ assertTrue("check map converter succeed", convert.isLeft());
+
+ String convertedValue = convert.left().value();
+
+ Type type = new TypeToken<Map<String, Credential>>() {
+ }.getType();
+
+ Map<String, Credential> fromJson = gson.fromJson(convertedValue, type);
+
+ Credential actualCredential1 = fromJson.get("key1");
+ assertEquals("check sum", 5, actualCredential1.getSum().intValue());
+ assertEquals("check protocol", "protocol;:,.\">", actualCredential1.getProtocol());
+ String[] convertedStrArr = { "aaa", "bbb", "cdcc" };
+ List<String> convertedStrList = Arrays.asList(convertedStrArr);
+ assertEquals("check list", convertedStrList, actualCredential1.getMylist());
+
+ assertEquals("check city address", "myadd<", actualCredential1.getMycity().getAddress());
+ assertEquals("check city address", 55, actualCredential1.getMycity().getAge().intValue());
+
+ Credential actualCredential2 = fromJson.get("key2");
+ assertEquals("check city address", 66, actualCredential2.getMycity().getAge().intValue());
+
+ }
+
+ @Test
+ public void testListConverterWithComplexInnerType() {
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition credentialDataTypeDefinition = buildCredentialDataType();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+ allDataTypes.put("credential", credentialDataTypeDefinition);
+
+ Gson gson = new Gson();
+
+ List<Object> list = buildListOf2CredentialObjects();
+
+ String str = gson.toJson(list);
+ log.debug(str);
+
+ ListConverter listConverter = new ListConverter();
+
+ Either<String, Boolean> convert = listConverter.convertWithErrorResult(str, "credential", allDataTypes);
+
+ assertTrue("check map converter succeed", convert.isLeft());
+
+ String convertedValue = convert.left().value();
+
+ validateListOfCredential(gson, convertedValue);
+
+ list.add(null);
+
+ str = gson.toJson(list);
+ log.debug(str);
+
+ convert = listConverter.convertWithErrorResult(str, "credential", allDataTypes);
+
+ assertTrue("check map converter succeed", convert.isLeft());
+
+ validateListOfCredential(gson, convertedValue);
+ }
+
+ @Test
+ public void testListValidatorWithComplexInnerType() {
+
+ Map<String, DataTypeDefinition> allDataTypes = new HashMap<String, DataTypeDefinition>();
+ DataTypeDefinition credentialDataTypeDefinition = buildCredentialDataType();
+ DataTypeDefinition cityDataType = buildCityDataType();
+ allDataTypes.put("city", cityDataType);
+ allDataTypes.put("credential", credentialDataTypeDefinition);
+
+ Gson gson = new Gson();
+ // Happy Scenarios
+ List<Object> list = buildListOf2CredentialObjects();
+
+ String str = gson.toJson(list);
+ log.debug(str);
+
+ ListValidator listValidator = new ListValidator();
+
+ boolean isValid = listValidator.isValid(str, "credential", allDataTypes);
+
+ assertTrue("check valid value", isValid);
+
+ String badStr = str.replace("protocol", "protocol1");
+
+ isValid = listValidator.isValid(badStr, "credential", allDataTypes);
+
+ assertFalse("check valid value", isValid);
+
+ badStr = str.replace("55", "\"aa\"");
+
+ isValid = listValidator.isValid(badStr, "credential", allDataTypes);
+
+ assertFalse("check valid value", isValid);
+
+ }
+
+ private List<Object> buildListOf2CredentialObjects() {
+ List<Object> list = new ArrayList<>();
+
+ String[] strArr = { "aaa", "bbb", "c<br>dcc" };
+ List<String> strList = Arrays.asList(strArr);
+ Credential credential1 = new Credential("protocol.,\":;<br>>", 5, "token_type", "token", null, "user", true,
+ strList);
+ City mycity1 = new City("myadd<br><<br>", 55);
+ credential1.setMycity(mycity1);
+
+ Credential credential2 = new Credential("protocol.,\":;<br>>", 5, "token_type", "token", null, "user", true,
+ strList);
+ City mycity2 = new City("myadd<br><<br>", 66);
+ credential2.setMycity(mycity2);
+
+ list.add(credential1);
+ list.add(credential2);
+ return list;
+ }
+
+ private void validateListOfCredential(Gson gson, String convertedValue) {
+
+ log.debug(convertedValue);
+ Type type = new TypeToken<List<Credential>>() {
+ }.getType();
+
+ List<Credential> fromJson = gson.fromJson(convertedValue, type);
+
+ assertEquals("check list size", 2, fromJson.size());
+
+ // Credential actualCredential1 = gson.fromJson(list.get(0).toString(),
+ // Credential.class);
+ Credential actualCredential1 = fromJson.get(0);
+ assertEquals("check sum", 5, actualCredential1.getSum().intValue());
+ assertEquals("check protocol", "protocol.,\":;>", actualCredential1.getProtocol());
+ String[] convertedStrArr = { "aaa", "bbb", "cdcc" };
+ List<String> convertedStrList = Arrays.asList(convertedStrArr);
+ assertEquals("check list", convertedStrList, actualCredential1.getMylist());
+
+ assertEquals("check city address", "myadd<", actualCredential1.getMycity().getAddress());
+ assertEquals("check city address", 55, actualCredential1.getMycity().getAge().intValue());
+
+ // Credential actualCredential2 = gson.fromJson(list.get(1).toString(),
+ // Credential.class);
+ Credential actualCredential2 = fromJson.get(1);
+ assertEquals("check city address", 66, actualCredential2.getMycity().getAge().intValue());
+ }
+
+ private DataTypeDefinition buildCredentialDataType() {
+ DataTypeDefinition dataTypeDefinition = new DataTypeDefinition();
+ dataTypeDefinition.setName("datatype.1");
+ List<PropertyDefinition> properties = new ArrayList<>();
+ PropertyDefinition propertyDefinition1 = new PropertyDefinition();
+ propertyDefinition1.setName("sum");
+ propertyDefinition1.setType(ToscaPropertyType.INTEGER.getType());
+ PropertyDefinition propertyDefinition2 = new PropertyDefinition();
+ propertyDefinition2.setName("protocol");
+ propertyDefinition2.setType(ToscaPropertyType.STRING.getType());
+ PropertyDefinition propertyDefinition3 = new PropertyDefinition();
+ propertyDefinition3.setName("token_type");
+ propertyDefinition3.setType(ToscaPropertyType.STRING.getType());
+ PropertyDefinition propertyDefinition4 = new PropertyDefinition();
+ propertyDefinition4.setName("token");
+ propertyDefinition4.setType(ToscaPropertyType.STRING.getType());
+ PropertyDefinition propertyDefinition5 = new PropertyDefinition();
+ propertyDefinition5.setName("keys");
+ propertyDefinition5.setType(ToscaPropertyType.MAP.getType());
+ PropertyDefinition propertyDefinition6 = new PropertyDefinition();
+ propertyDefinition6.setName("mylist");
+ propertyDefinition6.setType(ToscaPropertyType.LIST.getType());
+ SchemaDefinition entrySchema = new SchemaDefinition();
+ PropertyDataDefinition property = new PropertyDataDefinition();
+ property.setType("string");
+ entrySchema.setProperty(property);
+ propertyDefinition6.setSchema(entrySchema);
+ PropertyDefinition propertyDefinition7 = new PropertyDefinition();
+ propertyDefinition7.setName("user");
+ propertyDefinition7.setType(ToscaPropertyType.STRING.getType());
+ PropertyDefinition propertyDefinition8 = new PropertyDefinition();
+ propertyDefinition8.setName("isMandatory");
+ propertyDefinition8.setType(ToscaPropertyType.BOOLEAN.getType());
+
+ PropertyDefinition propertyDefinition9 = new PropertyDefinition();
+ propertyDefinition9.setName("mycity");
+ propertyDefinition9.setType("city");
+
+ properties.add(propertyDefinition1);
+ properties.add(propertyDefinition2);
+ properties.add(propertyDefinition3);
+ properties.add(propertyDefinition4);
+ properties.add(propertyDefinition5);
+ properties.add(propertyDefinition6);
+ properties.add(propertyDefinition7);
+ properties.add(propertyDefinition8);
+ properties.add(propertyDefinition9);
+
+ dataTypeDefinition.setProperties(properties);
+ return dataTypeDefinition;
+ }
+
+ private static DataTypeDefinition buildCityDataType() {
+ DataTypeDefinition cityDataType = new DataTypeDefinition();
+ cityDataType.setName("city");
+ List<PropertyDefinition> cityProperties = new ArrayList<>();
+ PropertyDefinition cityPropertyDefinition1 = new PropertyDefinition();
+ cityPropertyDefinition1.setName("age");
+ cityPropertyDefinition1.setType(ToscaPropertyType.INTEGER.getType());
+ PropertyDefinition cityPropertyDefinition2 = new PropertyDefinition();
+ cityPropertyDefinition2.setName("address");
+ cityPropertyDefinition2.setType(ToscaPropertyType.STRING.getType());
+
+ cityProperties.add(cityPropertyDefinition1);
+ cityProperties.add(cityPropertyDefinition2);
+
+ cityDataType.setProperties(cityProperties);
+ return cityDataType;
+ }
+
+ private static DataTypeDefinition buildPersonDataType() {
+ DataTypeDefinition personDataType = new DataTypeDefinition();
+ personDataType.setName("person");
+ List<PropertyDefinition> personProperties = new ArrayList<>();
+ PropertyDefinition personPropertyDefinition1 = new PropertyDefinition();
+ personPropertyDefinition1.setName("age");
+ personPropertyDefinition1.setType("myinteger");
+ PropertyDefinition personPropertyDefinition2 = new PropertyDefinition();
+ personPropertyDefinition2.setName("address");
+ personPropertyDefinition2.setType(ToscaPropertyType.STRING.getType());
+
+ personProperties.add(personPropertyDefinition1);
+ personProperties.add(personPropertyDefinition2);
+
+ personDataType.setProperties(personProperties);
+ return personDataType;
+ }
+
+ private static DataTypeDefinition buildFileDataType() {
+ DataTypeDefinition fileDataType = new DataTypeDefinition();
+ fileDataType.setName("file");
+ List<PropertyDefinition> fileProperties = new ArrayList<>();
+ PropertyDefinition filePropertyDefinition1 = new PropertyDefinition();
+ filePropertyDefinition1.setName("age");
+ filePropertyDefinition1.setType("integer");
+
+ PropertyDefinition filePropertyDefinition2 = new PropertyDefinition();
+ filePropertyDefinition2.setName("attributes");
+ filePropertyDefinition2.setType(ToscaPropertyType.MAP.getType());
+
+ fileProperties.add(filePropertyDefinition1);
+ fileProperties.add(filePropertyDefinition2);
+
+ SchemaDefinition entrySchema = new SchemaDefinition();
+ PropertyDataDefinition property = new PropertyDataDefinition();
+ property.setType("city");
+ entrySchema.setProperty(property);
+ filePropertyDefinition2.setSchema(entrySchema);
+
+ fileDataType.setProperties(fileProperties);
+ return fileDataType;
+ }
+
+ private static DataTypeDefinition getPrimitiveDataType(String type) {
+
+ DataTypeDefinition derivedFrom = new DataTypeDefinition();
+ derivedFrom.setName(type);
+
+ return derivedFrom;
+
+ }
+
+ private static DataTypeDefinition buildDerivedFromIntegerType() {
+
+ DataTypeDefinition derivedFrom = getPrimitiveDataType("integer");
+
+ DataTypeDefinition myIntegerDataType = new DataTypeDefinition();
+ myIntegerDataType.setDerivedFrom(derivedFrom);
+
+ myIntegerDataType.setName("myinteger");
+
+ return myIntegerDataType;
+ }
+
+ public static class MyFile {
+
+ Integer age;
+
+ Map<String, City> attributes;
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ public Map<String, City> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(Map<String, City> attributes) {
+ this.attributes = attributes;
+ }
+
+ }
+
+ public static class City {
+
+ String address;
+ Integer age;
+
+ public City(String address, Integer age) {
+ super();
+ this.address = address;
+ this.age = age;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ }
+
+ public static class Person {
+
+ String address;
+ Integer age;
+
+ public Person(String address, Integer age) {
+ super();
+ this.address = address;
+ this.age = age;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ @Override
+ public String toString() {
+ return "Person [address=" + address + ", age=" + age + "]";
+ }
+
+ }
+
+ public static class Credential {
+
+ String protocol;
+ Integer sum;
+ String token_type;
+ String token;
+ Map<String, String> keys;
+ String user;
+ Boolean isMandatory;
+ List<String> mylist;
+ City mycity;
+
+ public Credential(String protocol, Integer sum, String token_type, String token, Map<String, String> keys,
+ String user, Boolean isMandatory, List<String> mylist) {
+ super();
+ this.protocol = protocol;
+ this.sum = sum;
+ this.token_type = token_type;
+ this.token = token;
+ this.keys = keys;
+ this.user = user;
+ this.isMandatory = isMandatory;
+ this.mylist = mylist;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getToken_type() {
+ return token_type;
+ }
+
+ public void setToken_type(String token_type) {
+ this.token_type = token_type;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public Map<String, String> getKeys() {
+ return keys;
+ }
+
+ public void setKeys(Map<String, String> keys) {
+ this.keys = keys;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public Boolean getIsMandatory() {
+ return isMandatory;
+ }
+
+ public void setIsMandatory(Boolean isMandatory) {
+ this.isMandatory = isMandatory;
+ }
+
+ public Integer getSum() {
+ return sum;
+ }
+
+ public void setSum(Integer sum) {
+ this.sum = sum;
+ }
+
+ public List<String> getMylist() {
+ return mylist;
+ }
+
+ public void setMylist(List<String> mylist) {
+ this.mylist = mylist;
+ }
+
+ public City getMycity() {
+ return mycity;
+ }
+
+ public void setMycity(City mycity) {
+ this.mycity = mycity;
+ }
+
+ @Override
+ public String toString() {
+ return "Credential [protocol=" + protocol + ", token_type=" + token_type + ", token=" + token + ", keys="
+ + keys + ", user=" + user + ", isMandatory=" + isMandatory + "]";
+ }
+
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/OperationTestsUtil.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/OperationTestsUtil.java
new file mode 100644
index 0000000000..e356b49a10
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/OperationTestsUtil.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl.util;
+
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.be.resources.data.ResourceCategoryData;
+import org.openecomp.sdc.be.resources.data.ServiceCategoryData;
+import org.openecomp.sdc.be.resources.data.UserData;
+import org.openecomp.sdc.be.resources.data.category.CategoryData;
+import org.openecomp.sdc.be.resources.data.category.SubCategoryData;
+import org.openecomp.sdc.common.util.ValidationUtils;
+
+import fj.data.Either;
+
+public class OperationTestsUtil {
+
+ public static String deleteAndCreateServiceCategory(String category, TitanGenericDao titanDao) {
+ CategoryData categoryData = new CategoryData(NodeTypeEnum.ServiceNewCategory);
+ categoryData.getCategoryDataDefinition().setName(category);
+ categoryData.getCategoryDataDefinition()
+ .setNormalizedName(ValidationUtils.normalizeCategoryName4Uniqueness(category));
+ categoryData.getCategoryDataDefinition().setUniqueId(UniqueIdBuilder.buildCategoryUid(
+ ValidationUtils.normalizeCategoryName4Uniqueness(category), NodeTypeEnum.ServiceNewCategory));
+ titanDao.deleteNode(categoryData, CategoryData.class);
+ Either<CategoryData, TitanOperationStatus> createNode = titanDao.createNode(categoryData, CategoryData.class);
+ return (String) createNode.left().value().getUniqueId();
+ }
+
+ public static String deleteAndCreateResourceCategory(String category, String subcategory,
+ TitanGenericDao titanDao) {
+
+ CategoryData categoryData = new CategoryData(NodeTypeEnum.ResourceNewCategory);
+ categoryData.getCategoryDataDefinition().setName(category);
+ categoryData.getCategoryDataDefinition()
+ .setNormalizedName(ValidationUtils.normalizeCategoryName4Uniqueness(category));
+ categoryData.getCategoryDataDefinition().setUniqueId(UniqueIdBuilder.buildCategoryUid(
+ ValidationUtils.normalizeCategoryName4Uniqueness(category), NodeTypeEnum.ResourceNewCategory));
+
+ SubCategoryData subcategoryData = new SubCategoryData(NodeTypeEnum.ResourceSubcategory);
+ subcategoryData.getSubCategoryDataDefinition().setName(subcategory);
+ subcategoryData.getSubCategoryDataDefinition()
+ .setNormalizedName(ValidationUtils.normalizeCategoryName4Uniqueness(subcategory));
+ subcategoryData.getSubCategoryDataDefinition().setUniqueId(UniqueIdBuilder
+ .buildSubCategoryUid(categoryData.getCategoryDataDefinition().getUniqueId(), subcategory));
+ titanDao.deleteNode(categoryData, CategoryData.class);
+ titanDao.deleteNode(subcategoryData, SubCategoryData.class);
+ Either<CategoryData, TitanOperationStatus> createNode = titanDao.createNode(categoryData, CategoryData.class);
+ titanDao.createNode(subcategoryData, SubCategoryData.class);
+ titanDao.createRelation(categoryData, subcategoryData, GraphEdgeLabels.SUB_CATEGORY, null);
+ return (String) createNode.left().value().getUniqueId();
+ }
+
+ public static void deleteServiceCategory(String category, TitanGenericDao titanDao) {
+ ServiceCategoryData categoryData = new ServiceCategoryData(category);
+ titanDao.deleteNode(categoryData, ServiceCategoryData.class);
+ }
+
+ public static void deleteResourceCategory(String category, String subcategory, TitanGenericDao titanDao) {
+ ResourceCategoryData categoryData = new ResourceCategoryData(category, subcategory);
+ titanDao.deleteNode(categoryData, ResourceCategoryData.class);
+ }
+
+ public static User convertUserDataToUser(UserData modifierData) {
+ User modifier = new User();
+ modifier.setUserId(modifierData.getUserId());
+ modifier.setEmail(modifierData.getEmail());
+ modifier.setFirstName(modifierData.getFirstName());
+ modifier.setLastName(modifierData.getLastName());
+ modifier.setRole(modifierData.getRole());
+ return modifier;
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/PrintGraph.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/PrintGraph.java
new file mode 100644
index 0000000000..b58ce5598d
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/PrintGraph.java
@@ -0,0 +1,461 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
+import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+//import com.tinkerpop.blueprints.Direction;
+//import com.tinkerpop.blueprints.Edge;
+//import com.tinkerpop.blueprints.Vertex;
+//import com.tinkerpop.blueprints.util.ElementHelper;
+
+public class PrintGraph {
+
+ public void printGraphVertices(TitanGraph graph) {
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ // System.out.println(vertex);
+ // System.out.println(ElementHelper.getProperties(vertex));
+ // System.out.println("=======================================");
+ }
+
+ }
+ // graph.commit();
+ graph.tx().commit();
+ }
+
+ public void printGraphEdges(TitanGraph graph) {
+
+ Iterable<TitanEdge> vertices = graph.query().edges();
+
+ if (vertices != null) {
+ Iterator<TitanEdge> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Edge edge = iterator.next();
+
+ // System.out.println(edge);
+ // System.out.println("edge=" + edge.getLabel() + ",
+ // properties="+ ElementHelper.getProperties(edge));
+ // System.out.println("edge=" + edge.label() + ", properties="+
+ // getProperties(edge));
+ // System.out.println("=======================================");
+ }
+
+ }
+ // graph.commit();
+ graph.tx().commit();
+ }
+
+ public String buildGraphForWebgraphWiz(TitanGraph graph) {
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("digraph finite_state_machine {\n");
+ builder.append("rankdir=LR;\n");
+ builder.append("size=\"15,10\" \n");
+ // node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
+ // node [shape = circle];
+
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ // System.out.println(vertex);
+ // System.out.println(ElementHelper.getProperties(vertex));
+ // System.out.println(getProperties(vertex));
+ // System.out.println("=======================================");
+
+ Map<String, Object> properties = getProperties(vertex);
+
+ String nodeLabel = (String) properties.get(GraphPropertiesDictionary.LABEL.getProperty());
+
+ String color = getColorByNodeType(nodeLabel);
+
+ String uid = getNodeIdByLabel(nodeLabel, properties);
+
+ // System.out.println("uid=" + uid);
+
+ String nodeRecord = buildNodeRecord(uid, color, properties);
+
+ // System.out.println(nodeRecord);
+
+ builder.append(nodeRecord);
+
+ // if (nodeLabel.equals(NodeTypeEnum.Category)) {
+ //
+ // String
+ //
+ // } else (nodeLabel.equals(NodeTypeEnum.User)) {
+ //
+ // }
+
+ }
+
+ }
+
+ Iterable<TitanEdge> edges = graph.query().edges();
+
+ if (edges != null) {
+ Iterator<TitanEdge> iterator = edges.iterator();
+ while (iterator.hasNext()) {
+ Edge edge = iterator.next();
+
+ // Vertex vertexFrom = edge.getVertex(Direction.OUT);
+ // Vertex vertexTo = edge.getVertex(Direction.IN);
+ Vertex vertexFrom = edge.outVertex();
+ Vertex vertexTo = edge.inVertex();
+
+ // String fromUid =
+ // getNodeIdByLabel((String)vertexFrom.getProperty(GraphPropertiesDictionary.LABEL.getProperty()),
+ // ElementHelper.getProperties(vertexFrom));
+ // String toUid =
+ // getNodeIdByLabel((String)vertexTo.getProperty(GraphPropertiesDictionary.LABEL.getProperty()),
+ // ElementHelper.getProperties(vertexTo));
+ String fromUid = getNodeIdByLabel(vertexFrom.value(GraphPropertiesDictionary.LABEL.getProperty()),
+ getProperties(vertexFrom));
+ String toUid = getNodeIdByLabel(vertexTo.value(GraphPropertiesDictionary.LABEL.getProperty()),
+ getProperties(vertexTo));
+
+ // String edgeLabel = edge.getLabel();
+ String edgeLabel = edge.label();
+
+ // String edgeRecord = buildEdgeRecord(fromUid, toUid,
+ // edgeLabel, ElementHelper.getProperties(edge));
+ String edgeRecord = buildEdgeRecord(fromUid, toUid, edgeLabel, getProperties(edge));
+
+ builder.append(edgeRecord);
+
+ // System.out.println(edge);
+ // System.out.println("edge=" + edge.getLabel() + ",
+ // properties="
+ // + ElementHelper.getProperties(edge));
+ // System.out.println("edge=" + edge.label() + ", properties="
+ // + getProperties(edge));
+ // System.out.println("=======================================");
+ }
+
+ }
+
+ builder.append(" } ");
+
+ return builder.toString();
+
+ }
+
+ // LR_0 [ style = "bold" color = "red" shape = "Mrecord" label =
+ // "hello&#92;nworld | { name | apache } | { version | 1.0 } | { uid |
+ // apache.1.0 } | { state| CERTIFIED } |{ b |{c|<here> d|e}| f}| g | h"
+ // ]
+
+ // LR_0 -> LR_2 [ label = "SS(B)" ];
+ // LR_0 -> LR_1 [ label = "SS(S)" ];
+ // LR_1 -> LR_3 [ label = "S($end)" ];
+ // LR_2 -> LR_6 [ label = "SS(b)" ];
+ // LR_2 -> LR_5 [ label = "SS(a)" ];
+ // LR_2 -> LR_4 [ label = "S(A)" ];
+ // LR_5 -> LR_7 [ label = "S(b)" ];
+ // LR_5 -> LR_5 [ label = "S(a)" ];
+ // LR_6 -> LR_6 [ label = "S(b)" ];
+ // LR_6 -> LR_5 [ label = "S(a)" ];
+ // LR_7 -> LR_8 [ label = "S(b)" ];
+ // LR_7 -> LR_5 [ label = "S(a)" ];
+ // LR_8 -> LR_6 [ label = "S(b)" ];
+ // LR_8 -> LR_5 [ label = "S(a)" ];
+
+ private String buildEdgeRecord(String fromUid, String toUid, String edgeLabel, Map<String, Object> properties) {
+
+ StringBuilder builder = new StringBuilder();
+ // LR_0 -> LR_2 [ label = "SS(B)" ];
+
+ String generatedProps = generateStringFromProperties(properties);
+
+ String color = getEdgeColorByLabel(edgeLabel);
+
+ builder.append("\"" + fromUid + "\"" + " -> " + "\"" + toUid + "\"" + " [ color = " + color + " label = \""
+ + edgeLabel + "(" + generatedProps + ")\"" + " ] " + "\n");
+
+ return builder.toString();
+ }
+
+ private String getEdgeColorByLabel(String edgeLabel) {
+
+ GraphEdgeLabels edgeLabelEnum = GraphEdgeLabels.getByName(edgeLabel);
+
+ String color = "black";
+
+ switch (edgeLabelEnum) {
+ case PROPERTY:
+ color = "orange";
+ break;
+ case CAPABILITY:
+ break;
+ case DERIVED_FROM:
+ color = "red";
+ default:
+ break;
+ }
+
+ return color;
+ }
+
+ private String generateStringFromProperties(Map<String, Object> properties) {
+
+ StringBuilder builder = new StringBuilder();
+
+ if (properties != null) {
+ for (Entry<String, Object> entry : properties.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue().toString();
+ builder.append(key + "=" + value + "__");
+ }
+ }
+ return builder.toString();
+
+ }
+
+ private String buildNodeRecord(String uid, String color, Map<String, Object> properties) {
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("\"" + uid + "\"" + " [ ");
+ builder.append("style = \"bold\" ");
+ builder.append(" color = \"" + color + "\"");
+ builder.append("shape = \"Mrecord\" ");
+
+ String label = "";
+ int maxKeyLength = 0;
+ for (Entry<String, Object> entry1 : properties.entrySet()) {
+ String key = entry1.getKey();
+ int keyLength = key.length();
+ if (keyLength > maxKeyLength) {
+ maxKeyLength = keyLength;
+ }
+ }
+
+ boolean first = true;
+ for (Entry<String, Object> entry : properties.entrySet()) {
+
+ String key = entry.getKey();
+ String value = entry.getValue().toString();
+
+ if (key.equals(GraphPropertiesDictionary.CONSTRAINTS.getProperty())) {
+ value = value.replaceAll("[^\\w\\s]", "_");
+ }
+
+ key = padKey(key, maxKeyLength);
+
+ if (first) {
+ first = false;
+ } else {
+ label += " | ";
+ }
+ label += " { " + key + " | " + value + " } ";
+ }
+
+ builder.append("label = \"" + label + "\" ");
+ builder.append(" ] ");
+
+ // LR_0 [ style = "bold" color = "red" shape = "Mrecord" label =
+ // "hello&#92;nworld | { name | apache } | { version | 1.0 } | { uid |
+ // apache.1.0 } | { state| CERTIFIED } |{ b |{c|<here> d|e}| f}| g | h"
+ // ]
+
+ builder.append(" \n ");
+ return builder.toString();
+ }
+
+ private String getNodeIdByLabel(String nodeLabel, Map<String, Object> properties) {
+
+ NodeTypeEnum typeEnum = NodeTypeEnum.getByName(nodeLabel);
+
+ String uid = null;
+ switch (typeEnum) {
+
+ case User:
+ uid = (String) properties.get(GraphPropertiesDictionary.USER_ID.getProperty());
+ break;
+ case ServiceCategory:
+ case ResourceCategory:
+ case Tag:
+ uid = (String) properties.get(GraphPropertiesDictionary.NAME.getProperty());
+ break;
+
+ default:
+ uid = (String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty());
+ break;
+ }
+
+ return uid;
+ }
+
+ private String getColorByNodeType(String nodeLabel) {
+
+ NodeTypeEnum typeEnum = NodeTypeEnum.getByName(nodeLabel);
+
+ String color = "red";
+ switch (typeEnum) {
+ case ServiceCategory:
+ color = "blue";
+ break;
+ case ResourceCategory:
+ color = "blue";
+ break;
+ case Resource:
+ color = "forestgreen";
+ break;
+ case User:
+ color = "green";
+ break;
+ case Capability:
+ color = "lightgreen";
+ break;
+ case CapabilityType:
+ color = "gray";
+ break;
+ case Property:
+ color = "cyan";
+ break;
+ case RelationshipType:
+ color = "darkorchid";
+ break;
+ case Requirement:
+ color = "gold";
+ break;
+ case RequirementImpl:
+ // color = "forestgreen";
+ color = "gold";
+ break;
+ case Service:
+ color = "cyan4";
+ break;
+ case Tag:
+ color = "dimgrey";
+ break;
+ default:
+ break;
+
+ }
+
+ return color;
+ }
+
+ private String padKey(String key, int maxKeyLength) {
+
+ int len = key.length();
+ for (int i = len; i < maxKeyLength; i++) {
+ key += " ";
+ }
+
+ return key;
+ }
+
+ public int getNumberOfVertices(TitanGraph graph) {
+ int counter = 0;
+ Iterable<TitanVertex> vertices = graph.query().vertices();
+
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+ counter++;
+ }
+ }
+ return counter;
+ }
+
+ public Set<String> getVerticesSet(TitanGraph titanGraph) {
+
+ Set<String> set = new HashSet<String>();
+
+ Iterable<TitanVertex> vertices = titanGraph.query().vertices();
+
+ if (vertices != null) {
+ Iterator<TitanVertex> iterator = vertices.iterator();
+ while (iterator.hasNext()) {
+ Vertex vertex = iterator.next();
+
+ // System.out.println(vertex);
+ // System.out.println(ElementHelper.getProperties(vertex));
+ // System.out.println(getProperties(vertex));
+ // System.out.println("=======================================");
+
+ // Map<String, Object> properties =
+ // ElementHelper.getProperties(vertex);
+ Map<String, Object> properties = getProperties(vertex);
+
+ String nodeLabel = (String) properties.get(GraphPropertiesDictionary.LABEL.getProperty());
+
+ String uid = getNodeIdByLabel(nodeLabel, properties);
+
+ set.add(uid);
+ }
+ }
+
+ return set;
+
+ }
+
+ public Map<String, Object> getProperties(Element element) {
+
+ Map<String, Object> result = null;
+
+ if (element.keys() != null && element.keys().size() > 0) {
+ Map<String, Property> propertyMap = ElementHelper.propertyMap(element,
+ element.keys().toArray(new String[element.keys().size()]));
+ result = new HashMap<String, Object>();
+
+ for (Entry<String, Property> entry : propertyMap.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue().value();
+
+ result.put(key, value);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/ResourceCreationUtils.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/ResourceCreationUtils.java
new file mode 100644
index 0000000000..b6b951bdc4
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/util/ResourceCreationUtils.java
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.operations.impl.util;
+
+public class ResourceCreationUtils {
+
+ public static String ATT_UID = "jm007a";
+ public static String FIRST_NAME = "Julia";
+ public static String LAST_NAME = "Hendrix";
+ public static String DEFAULT_RESOURCE_NAME = "my-resource";
+ public static String DEFAULT_RESOURCE_VERSION = "1.0";
+ public static String DEFAULT_USER_ID = "jh0003";
+
+ public static String MODIFIER_ATT_UID = "jk9990";
+ public static String MODIFIER_FIRST_NAME = "Roki";
+ public static String MODIFIER_LAST_NAME = "Balaboa";
+
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/serialize/TestResourceSerialization.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/serialize/TestResourceSerialization.java
new file mode 100644
index 0000000000..699e23affd
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/serialize/TestResourceSerialization.java
@@ -0,0 +1,222 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.serialize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.model.ComponentInstance;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.common.util.SerializationUtils;
+
+import fj.data.Either;
+
+public class TestResourceSerialization {
+
+ // @Test
+ public void findAllClassesUsedByResource() {
+
+ Set<Class> classesWithoutSerialzable = new HashSet<>();
+ Set<String> classestoIgnore = new HashSet<>();
+ classestoIgnore.add("java.util.List");
+ classestoIgnore.add("java.util.Map");
+ classestoIgnore.add("long");
+
+ Set<Class> allClasses = new HashSet<>();
+ findAllClassesUsedByResource(Resource.class, allClasses);
+ ArrayList l;
+ for (Class clazz : allClasses) {
+ Class[] interfaces = clazz.getInterfaces();
+ if (interfaces != null) {
+ String collect = Arrays.stream(interfaces).map(p -> p.getName()).collect(Collectors.joining("\n"));
+
+ Class orElse = Arrays.stream(interfaces).filter(p -> p.getName().equals("java.io.Serializable"))
+ .findAny().orElse(null);
+ if (orElse == null) {
+ classesWithoutSerialzable.add(clazz);
+ }
+
+ }
+ }
+
+ List<Class> collect = classesWithoutSerialzable.stream()
+ .filter(p -> false == classestoIgnore.contains(p.getName())).collect(Collectors.toList());
+
+ if (collect != null) {
+ System.out.println(collect.stream().map(p -> p.getName()).collect(Collectors.joining("\n")));
+ assertEquals("check all classes implements Serializable", 0, collect.size());
+ }
+
+ }
+
+ public void findAllClassesUsedByResource(Class clazz, Set<Class> allClasses) {
+
+ Class superclass = clazz.getSuperclass();
+ findAllClassesOfClass(clazz, allClasses);
+
+ if (superclass != null) {
+ findAllClassesOfClass(superclass, allClasses);
+ }
+
+ }
+
+ public void findAllClassesOfClass(Class clazz, Set<Class> allClasses) {
+
+ Field[] fields = clazz.getDeclaredFields();
+ if (fields != null) {
+ for (Field field : fields) {
+ String name = field.getName();
+ Class type = field.getType();
+
+ if (type.toString().contains(".List")) {
+ ParameterizedType stringListType = (ParameterizedType) field.getGenericType();
+ Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
+ // System.out.println(stringListClass); // class
+ // java.lang.String.
+ allClasses.add(stringListClass);
+ }
+
+ if (type.toString().contains("java.util.Map")) {
+ ParameterizedType stringListType = (ParameterizedType) field.getGenericType();
+
+ Type[] actualTypeArguments = stringListType.getActualTypeArguments();
+ if (actualTypeArguments != null) {
+ for (Type actualType : actualTypeArguments) {
+
+ String typeName = actualType.getTypeName();
+ // System.out.println("field " + name + "," +
+ // typeName);
+
+ if (typeName.startsWith("java.util.List<")) {
+ String internalClass = typeName.replace("java.util.List<", "").replace(">", "");
+ // create class from string
+ Class myClass;
+ try {
+ myClass = Class.forName(internalClass);
+ allClasses.add(myClass);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ assertTrue("Failed to convert " + internalClass + " to class", false);
+ }
+
+ } else {
+ try {
+ Class myClass = Class.forName(typeName);
+ allClasses.add(myClass);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ assertTrue("Failed to convert " + typeName + " to class", false);
+ }
+
+ }
+ }
+ }
+
+ }
+
+ // System.out.println(type);
+ allClasses.add(type);
+ }
+ }
+
+ }
+
+ private void addInternalTypeOfList(Class clazz) {
+
+ // clazz.
+ // ParameterizedType stringListType = (ParameterizedType)
+ // field.getGenericType();
+ // Class<?> stringListClass = (Class<?>)
+ // stringListType.getActualTypeArguments()[0];
+ // //System.out.println(stringListClass); // class java.lang.String.
+ // allClasses.add(stringListClass);
+ //
+ }
+
+ private boolean isClassImplementedSerialize(Class clazz) {
+
+ Type[] genericInterfaces = clazz.getGenericInterfaces();
+ if (genericInterfaces != null) {
+ Type orElse = Arrays.stream(genericInterfaces).filter(p -> p.getTypeName().equals("java.io.Serializable"))
+ .findAny().orElse(null);
+ if (orElse != null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // @Test
+ public void testSimpleResourceSerialize() {
+
+ Resource resource = new Resource();
+ String name = "res1";
+ Map<String, String> allVersions = new HashMap<String, String>();
+ allVersions.put("keya", "valuea");
+
+ resource.setName(name);
+ // all versions
+ resource.setAllVersions(allVersions);
+ List<ComponentInstance> resourceInstances = new ArrayList<ComponentInstance>();
+ // component instances
+ ComponentInstance componentInstance = new ComponentInstance();
+ componentInstance.setDescription("desc1");
+ componentInstance.setComponentUid("comUid");
+ resourceInstances.add(componentInstance);
+
+ resource.setComponentInstances(resourceInstances);
+
+ Either<byte[], Boolean> serialize = SerializationUtils.serialize(resource);
+ assertTrue("check object serialized", serialize.isLeft());
+ byte[] value = serialize.left().value();
+
+ Either<Object, Boolean> deserialize = SerializationUtils.deserialize(value);
+ assertTrue("check object deserialized", deserialize.isLeft());
+ Object obj = deserialize.left().value();
+ Resource desResource = (Resource) obj;
+ assertEquals("check name", name, desResource.getName());
+ verifyAllVersions(desResource);
+
+ }
+
+ private void verifyAllVersions(Resource desResource) {
+ assertNotNull("check all versions", desResource.getAllVersions());
+ assertEquals("check all version size", 1, desResource.getAllVersions().size());
+ assertEquals("check all version key", "keya", desResource.getAllVersions().keySet().iterator().next());
+ assertEquals("check all version value", "valuea", desResource.getAllVersions().values().iterator().next());
+ }
+}
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidatorTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidatorTest.java
new file mode 100644
index 0000000000..2dfe9a8de7
--- /dev/null
+++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/tosca/validators/IntegerValidatorTest.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.model.tosca.validators;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import javax.validation.constraints.AssertTrue;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.model.tosca.validators.IntegerValidator;
+
+public class IntegerValidatorTest {
+ private static IntegerValidator validator = IntegerValidator.getInstance();
+
+ @Test
+ public void testIntegerValidatorDecimal() {
+ assertTrue(validator.isValid(null, null));
+ assertTrue(validator.isValid("", null));
+ assertTrue(validator.isValid("0", null));
+ assertTrue(validator.isValid("+0", null));
+ assertTrue(validator.isValid("-0", null));
+ assertTrue(validator.isValid("+65465", null));
+ assertTrue(validator.isValid("-65465", null));
+ assertTrue(validator.isValid("2147483647", null));
+ assertFalse(validator.isValid("2147483648", null));
+ assertTrue(validator.isValid("-2147483648", null));
+ assertFalse(validator.isValid("-2147483649", null));
+ }
+
+ @Test
+ public void testIntegerValidatorHexa() {
+ assertTrue(validator.isValid("-0xadc", null));
+ assertTrue(validator.isValid("+0xadf", null));
+ assertTrue(validator.isValid("0x7FFFFFFF", null));
+ assertFalse(validator.isValid("0x80000000", null));
+ assertTrue(validator.isValid("-0x80000000", null));
+ assertFalse(validator.isValid("-0x80000001", null));
+ }
+
+ public void testIntegerValidatorOctal() {
+ assertTrue(validator.isValid("0o545435", null));
+ assertTrue(validator.isValid("-0o545435", null));
+ assertTrue(validator.isValid("0o17777777777", null));
+ assertFalse(validator.isValid("0o20000000000", null));
+ assertTrue(validator.isValid("-0o20000000000", null));
+ assertFalse(validator.isValid("-0o20000000001", null));
+ }
+
+ @Test
+ public void testIntegerValidatorIncorrect() {
+ assertFalse(validator.isValid("-2.147483649", null));
+ assertFalse(validator.isValid("dsfasf342342", null));
+ }
+}
diff --git a/catalog-model/src/test/resources/application-context-test.xml b/catalog-model/src/test/resources/application-context-test.xml
new file mode 100644
index 0000000000..43f4b088e4
--- /dev/null
+++ b/catalog-model/src/test/resources/application-context-test.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
+
+
+ <context:component-scan
+ base-package="org.openecomp.sdc.be.model.operations.impl,
+ org.openecomp.sdc.be.model.cache,
+ org.openecomp.sdc.be.dao.titan,
+ org.openecomp.sdc.be.dao.cassandra">
+
+ </context:component-scan>
+
+</beans>
diff --git a/catalog-model/src/test/resources/config/ecomp-error-configuration.yaml b/catalog-model/src/test/resources/config/ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..f8639668ee
--- /dev/null
+++ b/catalog-model/src/test/resources/config/ecomp-error-configuration.yaml
@@ -0,0 +1,308 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+## Range of BE codes - 3010-7999
+
+errors:
+
+ BeRestApiGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4000,
+ severity: ERROR,
+ description: "Unexpected error during BE REST API execution",
+ alarmSeverity: CRITICAL
+ }
+
+ BeHealthCheckError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3010,
+ severity: WARN,
+ description: "Error during BE Health Check",
+ alarmSeverity: INFORMATIONAL
+ }
+
+ BeInitializationError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4019,
+ severity: ERROR,
+ description: "Catalog-BE was not initialized properly",
+ alarmSeverity: CRITICAL
+ }
+
+ BeResourceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3011,
+ severity: ERROR,
+ description: "Mandatory resource %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeServiceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3012,
+ severity: ERROR,
+ description: "Mandatory service %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedAddingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3013,
+ severity: ERROR,
+ description: "Failed to add resource instance of resource %s to service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeIncorrectServiceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3014,
+ severity: ERROR,
+ description: "Service %s is not valid",
+ alarmSeverity: MAJOR
+ }
+
+ BeRepositoryDeleteError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3015,
+ severity: ERROR,
+ description: "Failed to delete object %s from repository",
+ alarmSeverity: CRITICAL
+ }
+
+ BeRepositoryQueryError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3016,
+ severity: ERROR,
+ description: "Failed to fetch from repository %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeInvalidConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3017,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. Value configured is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4001,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3019,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebObjectNotFoundError: {
+ type: DATA_ERROR,
+ code: ASDC_4005,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Data not found: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3021,
+ severity: ERROR,
+ description: "Error occured in Distribution Engine. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebAuthenticationError: {
+ type: AUTHENTICATION_PROBLEM,
+ code: ASDC_4003,
+ severity: ERROR,
+ description: "Authentication problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebUnkownHostError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4002,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Cannot reach host %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4004,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server.",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineInvalidArtifactType: {
+ type: DATA_ERROR,
+ code: ASDC_4006,
+ severity: WARN,
+ description: "The artifact type %s does not appear in the list of valid artifacts %s",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4008,
+ severity: WARN,
+ description: "The type %s of %s is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidValueError: {
+ type: DATA_ERROR,
+ code: ASDC_3028,
+ severity: WARN,
+ description: "The value %s of %s from type %s is invalid",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeletingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3029,
+ severity: ERROR,
+ description: "Failed to delete resource instance %s from service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3030,
+ severity: FATAL,
+ description: "Configuration parameter %s is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeConfigurationInvalidListSizeError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3031,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. At least %s values shall be configured",
+ alarmSeverity: MAJOR
+ }
+
+ ErrorConfigFileFormat: {
+ type: CONFIG_ERROR,
+ code: ASDC_3032,
+ severity: ERROR,
+ description: "Error element not found in YAML name: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingArtifactInformationError: {
+ type: DATA_ERROR,
+ code: ASDC_4010,
+ severity: ERROR,
+ description: "Artifact uploaded has missing information. Missing %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4011,
+ severity: ERROR,
+ description: "Artifact %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactPayloadInvalid: {
+ type: DATA_ERROR,
+ code: ASDC_4012,
+ severity: ERROR,
+ description: "Payload of artifact uploaded is invalid (invalid MD5 or encryption)",
+ alarmSeverity: MAJOR
+ }
+
+ BeUserMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4009,
+ severity: ERROR,
+ description: "User %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactInformationInvalidError: {
+ type: DATA_ERROR,
+ code: ASDC_4013,
+ severity: ERROR,
+ description: "Input for artifact metadata is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeFailedAddingCapabilityTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4015,
+ severity: ERROR,
+ description: "Failed adding capability type",
+ alarmSeverity: CRITICAL
+ }
+
+ BeCapabilityTypeMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4016,
+ severity: ERROR,
+ description: "Capability Type %s not found",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInterfaceMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4020,
+ severity: ERROR,
+ description: "Interface %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeDaoSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4014,
+ severity: ERROR,
+ description: "Operation towards database failed",
+ alarmSeverity: CRITICAL
+ }
+
+ BeSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4017,
+ severity: ERROR,
+ description: "Unexpected error during operation",
+ alarmSeverity: CRITICAL
+ }
+
+ BeFailedLockObjectError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4007,
+ severity: WARN,
+ description: "Failed to lock object for update",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInvalidJsonInput: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4018,
+ severity: ERROR,
+ description: "Failed to convert json input to object",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4021,
+ severity: ERROR,
+ description: "Distribution %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+
+ # last error code: ASDC_4021 \ No newline at end of file
diff --git a/catalog-model/src/test/resources/logback-test.xml b/catalog-model/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..d2b9bff23f
--- /dev/null
+++ b/catalog-model/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+<!-- only one line, shut up logback ! -->
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>
+ %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+ </Pattern>
+ </encoder>
+ </appender>
+ <root level="OFF">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration> \ No newline at end of file
diff --git a/catalog-ui/Gruntfile.js b/catalog-ui/Gruntfile.js
new file mode 100644
index 0000000000..619d8316a1
--- /dev/null
+++ b/catalog-ui/Gruntfile.js
@@ -0,0 +1,807 @@
+// Generated on 2015-04-28 using
+// generator-webapp 0.5.1
+'use strict';
+
+// # Globbing
+// for performance reasons we're only matching one level down:
+// 'test/spec/{,*/}*.js'
+// If you want to recursively match all subfolders, use:
+// 'test/spec/**/*.js'
+
+module.exports = function (grunt) {
+
+ // Time how long tasks take. Can help when optimizing build times
+ require('time-grunt')(grunt);
+
+ // Load grunt tasks automatically
+ require('load-grunt-tasks')(grunt);
+
+ // Configurable paths
+ var config = {
+ app: 'app',
+ appModuleName: 'sdcApp',
+ dist: 'app/dist'
+ };
+
+ // Define the configuration for all the tasks
+ grunt.initConfig({
+
+ // Project settings
+ config: config,
+
+ // Watches files for changes and runs tasks based on the changed files
+ watch: {
+ html: {
+ files: ['<%= config.app %>/scripts/**/*.html'],
+ tasks: ['ngtemplates:app']
+ },
+ less: {
+ files: ['<%= config.app %>/**/*.less'],
+ tasks: ['less:all']
+ },
+ ts: {
+ files: ['<%= config.app %>/scripts/**/*.ts'],
+ tasks: ['ts:all']
+ },
+ bower: {
+ files: ['bower.json'],
+ tasks: ['wiredep']
+ },
+
+ gruntfile: {
+ files: ['Gruntfile.js']
+ },
+
+ livereload: {
+ options: {
+ livereload: '<%= connect.options.livereload %>'
+ },
+ files: [
+ '<%= config.app %>/{,*/}*.html',
+ '<%= config.app %>/scripts/**/*.html',
+ '<%= config.app %>/scripts/**/*.css',
+ '.tmp/styles/{,*/}*.css',
+ '<%= config.app %>/images/{,*/}*'
+ ]
+ },
+ configurations: {
+ files: [
+ 'configurations/*.json'
+ ],
+ tasks: ['ngconstant']
+ }
+ },
+
+ ngconstant: {
+ options: {
+ dest: 'app/scripts/modules/configurations.js',
+ name: 'Sdc.Config'
+ },
+ main: {
+ constants: {
+ sdcConfig: grunt.file.readJSON(grunt.option('env') ? 'configurations/' + grunt.option('env') + '.json' : 'configurations/prod.json'),
+ sdcMenu: grunt.file.readJSON('configurations/menu.json')
+ }
+ }
+ },
+
+ express: {
+ options: {
+ port: process.env.PORT || 9000
+ },
+ mock: {
+ options: {
+ script: 'server-mock/mock-server.js'
+ }
+ }
+ },
+
+ ts: {
+ all: {
+ src: [
+ 'app/scripts/**/*.ts',
+ 'typings/**/*.ts'
+ ],
+ reference: 'app/scripts/references.ts'
+ },
+ single: {
+ src: []
+ }
+ },
+ ngtemplates: {
+ app: {
+ options: {
+ module: '<%= config.appModuleName %>',
+ prefix: '/'
+ },
+ src: [
+ '<%= config.app %>/scripts/**/*.html',
+ '!index.html'
+ ],
+ dest: '<%= config.app %>/scripts/templates.js'
+ }
+ },
+ less: {
+ all: {
+ options: {
+ paths: ['<%= config.app %>/scripts',
+ '<%= config.app %>/styles']
+ },
+ files: {
+ '<%= config.app %>/styles/app.css': '<%= config.app %>/styles/app.less'
+ }
+ },
+ single: {
+ paths: ['<%= config.app %>/scripts',
+ '<%= config.app %>/styles'],
+ files: []
+ }
+ },
+
+ injector: {
+ options: {},
+ // Inject application script files into index.html (doesn't include bower)
+ scripts_models: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ return '<script src="' + filePath + '"></script>';
+ },
+ starttag: '<!-- injector:js_models -->',
+ endtag: '<!-- endinjector:js_models -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ [
+ '<%= config.app %>/scripts/models/**/*.js',
+ '!<%= config.app %>/scripts/models/**/*-tests.js'
+ ]
+ ]
+ }
+ },
+
+ scripts_utils: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ return '<script src="' + filePath + '"></script>';
+ },
+ starttag: '<!-- injector:js_utils -->',
+ endtag: '<!-- endinjector:js_utils -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ [
+ '<%= config.app %>/scripts/utils/**/*.js',
+ '!<%= config.app %>/scripts/models/**/*-tests.js'
+ ]
+ ]
+ }
+ },
+
+ scripts_filters: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ return '<script src="' + filePath + '"></script>';
+ },
+ starttag: '<!-- injector:js_filters -->',
+ endtag: '<!-- endinjector:js_filters -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ ['<%= config.app %>/scripts/filters/**/*.js',
+ '!<%= config.app %>/scripts/filters/**/*-tests.js'
+ ]
+ ]
+ }
+ },
+
+ scripts_directives: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ return '<script src="' + filePath + '"></script>';
+ },
+ starttag: '<!-- injector:js_directives -->',
+ endtag: '<!-- endinjector:js_directives -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ ['<%= config.app %>/scripts/directives/**/*.js',
+ '!<%= config.app %>/scripts/directives/**/*-tests.js'
+ ]
+ ]
+ }
+ },
+
+ scripts_services: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ return '<script src="' + filePath + '"></script>';
+ },
+ starttag: '<!-- injector:js_services -->',
+ endtag: '<!-- endinjector:js_services -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ ['<%= config.app %>/scripts/services/**/*.js',
+ '!<%= config.app %>/scripts/services/**/*-tests.js'
+ ]
+ ]
+ }
+ },
+
+ scripts_view_models: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ return '<script src="' + filePath + '"></script>';
+ },
+ starttag: '<!-- injector:js_view_models -->',
+ endtag: '<!-- endinjector:js_view_models -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ ['<%= config.app %>/scripts/view-models/**/*.js',
+ '!<%= config.app %>/scripts/view-models/**/*-tests.js']
+ ]
+ }
+ },
+
+ // Inject component less into app.less
+ less: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/scripts/', '../scripts/');
+ filePath = filePath.replace('/app/styles/', '');
+ return '@import \'' + filePath + '\';';
+ },
+ starttag: '// injector:less',
+ endtag: '// endinjector:less'
+ },
+ files: {
+ '<%= config.app %>/styles/app.less': [
+ '<%= config.app %>/styles/**/*.less',
+ '<%= config.app %>/scripts/**/*.less',
+ '!<%= config.app %>/styles/app.less'
+ ]
+ }
+ },
+
+ // Inject component css into index.html
+ css: {
+ options: {
+ transform: function (filePath) {
+ filePath = filePath.replace('/app/', '');
+ filePath = filePath.replace('/.tmp/', '');
+ return '<link rel="stylesheet" href="' + filePath + '">';
+ },
+ starttag: '<!-- injector:css -->',
+ endtag: '<!-- endinjector -->'
+ },
+ files: {
+ '<%= config.app %>/index.html': [
+ '<%= config.app %>/scripts/**/*.css',
+ '<%= config.app %>/styles/**/*.css',
+ '!<%= config.app %>/styles/app.css'
+ ]
+ }
+ }
+ },
+
+ // The actual grunt server settings
+ connect: {
+ options: {
+ port: 9000,
+ open: true,
+ livereload: 35729,
+ // Change this to '0.0.0.0' to access the server from outside
+ hostname: 'localhost'
+ },
+ livereload: {
+ options: {
+ middleware: function (connect) {
+ return [
+ connect().use(function (req, res, next) {
+ var mockApis = require('./configurations/mock.json').sdcConfig;
+ var userType;
+ switch (grunt.option('role')) {
+ case "admin":
+ userType = mockApis.userTypes.admin;
+ break;
+ case "tester":
+ userType = mockApis.userTypes.tester;
+ break;
+ case "governor":
+ userType = mockApis.userTypes.governor;
+ break;
+ case "ops":
+ userType = mockApis.userTypes.ops;
+ break;
+ case "designer":
+ userType = mockApis.userTypes.designer;
+ break;
+ case "product_strategist":
+ userType = mockApis.userTypes.product_strategist;
+ break;
+ case "product_manager":
+ userType = mockApis.userTypes.product_manager;
+ break;
+ default:
+ userType = mockApis.userTypes.designer;
+ }
+ res.cookie(mockApis.cookie.userIdSuffix, req.headers[mockApis.cookie.userIdSuffix] || userType.userId);
+ res.cookie(mockApis.cookie.userEmail, req.headers[mockApis.cookie.userEmail] || userType.email);
+ res.cookie(mockApis.cookie.userFirstName, req.headers[mockApis.cookie.userFirstName] || userType.firstName);
+ res.cookie(mockApis.cookie.userLastName, req.headers[mockApis.cookie.userLastName] || userType.lastName);
+ next();
+ }),
+ connect().use(require('http-proxy-middleware')(['/onboarding', '/onboarding-api'], {
+ target: 'http://feHost:8181/',
+ changeOrigin: true,
+ secure: false
+ })),
+ connect().use('/bower_components', connect.static('./bower_components')),
+ connect().use('/non_bower_components', connect.static('./non_bower_components')),
+ connect.static(config.app)
+ ];
+ }
+ }
+ },
+ dist: {
+ options: {
+ base: '<%= config.dist %>',
+ livereload: false
+ }
+ }
+ },
+
+ // Empties folders to start fresh
+ clean: {
+ generated: {
+ files: [{
+ dot: true,
+ src: [
+ '<%= config.app %>/scripts/**/*.js',
+ '<%= config.app %>/scripts/**/*.css',
+ '!<%= config.app %>/scripts/**/welcome/styles/*.css',
+ '<%= config.app %>/styles/**/*.css',
+ '<%= config.app %>/scripts/**/*.js.map'
+ ]
+ }]
+ },
+ dist: {
+ files: [{
+ dot: true,
+ src: [
+ '.tmp',
+ '<%= config.dist %>/*',
+ '!<%= config.dist %>/.git*'
+ ]
+ }]
+ },
+ server: '.tmp'
+ },
+ // Add vendor prefixed styles
+ autoprefixer: {
+ options: {
+ browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1']
+ },
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '.tmp/css/',
+ src: '{,*/**/}*.css',
+ dest: '.tmp/css/'
+ }]
+ }
+ },
+
+ // Automatically inject Bower components into the HTML file
+ wiredep: {
+ app: {
+ ignorePath: /^\/|\.\.\//,
+ src: ['<%= config.app %>/index.html']
+ }
+ },
+
+ // Renames files for browser caching purposes
+ rev: {
+ dist: {
+ files: {
+ src: [
+ '<%= config.dist %>/scripts/{,*/}*.js',
+ '<%= config.dist %>/styles/{,*/}*.css',
+ '<%= config.dist %>/images/{,*/}*.*',
+ '!<%= config.dist %>/images/resource-icons/{,*/}*.*',
+ '!<%= config.dist %>/images/service-icons/{,*/}*.*',
+ '!<%= config.dist %>/images/relationship-icons/{,*/}*.*',
+ '<%= config.dist %>/*.{ico,png}'
+ ]
+ }
+ }
+ },
+
+ // Reads HTML for usemin blocks to enable smart builds that automatically
+ // concat, minify and revision files. Creates configurations in memory so
+ // additional tasks can operate on them
+ useminPrepare: {
+ options: {
+ dest: '<%= config.dist %>'
+ },
+ sdc: {
+ src: ['<%= config.app %>/index.html']
+ },
+ html: '<%= config.app %>/index.html'
+ },
+
+ // Performs rewrites based on rev and the useminPrepare configuration
+ usemin: {
+ options: {
+ assetsDirs: [
+ '<%= config.dist %>',
+ '<%= config.dist %>/images',
+ '<%= config.dist %>/styles'
+ ],
+ // This is so we update image references in our ng-templates
+ patterns: {
+ js: [
+ [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
+ ]
+ }
+ },
+ html: ['<%= config.dist %>/{,*/}*.html'],
+ css: ['<%= config.dist %>/styles/{,*/}*.css'],
+ js: ['<%= config.dist %>/public/{,*/}*.js']
+ },
+
+ // The following *-min tasks produce minified files in the dist folder
+ imagemin: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '<%= config.app %>/images',
+ src: '<%= config.app %>/**/*.{gif,jpeg,jpg,png}',
+ dest: '<%= config.dist %>/images'
+ }]
+ }
+ },
+
+ svgmin: {
+ dist: {
+ files: [{
+ expand: true,
+ cwd: '<%= config.app %>/images',
+ src: '{,*/}*.svg',
+ dest: '<%= config.dist %>/images'
+ }]
+ }
+ },
+
+ htmlmin: {
+ dist: {
+ options: {
+ collapseBooleanAttributes: true,
+ collapseWhitespace: true,
+ conservativeCollapse: true,
+ removeAttributeQuotes: true,
+ removeCommentsFromCDATA: true,
+ removeEmptyAttributes: true,
+ removeOptionalTags: true,
+ removeRedundantAttributes: true,
+ useShortDoctype: true
+ },
+ files: [{
+ expand: true,
+ cwd: '<%= config.dist %>',
+ src: '{,*/}*.html',
+ dest: '<%= config.dist %>'
+ }]
+ }
+ },
+
+// By default, your `index.html`'s <!-- Usemin block --> will take care
+// of minification. These next options are pre-configured if you do not
+// wish to use the Usemin blocks.
+ cssmin: {
+ dist: {
+ files: {
+ '<%= config.dist %>/styles/main.css': [
+ '.tmp/css/{,*/**/}*.css',
+ '<%= config.app %>/scripts/{,*/**/}*.css',
+ '<%= config.app %>/styles/app.css'
+ ]
+ }
+ }
+ },
+ uglify: {
+ dist: {
+ files: {
+ '<%= config.dist %>/scripts/scripts.js': [
+ '<%= config.dist %>/scripts/scripts.js'
+ ]
+ }
+ }
+ },
+ replace: {
+ cssReplace: {
+ src: ['<%= config.app %>/scripts/{,*/**/}*.css',
+ '<%= config.app %>/styles/{,*/**/}*.css'],
+ overwrite: true,
+ replacements: [
+ {
+ from: '../../../images/',
+ to: '../images/'
+ },
+ {
+ from: '../../images/',
+ to: '../images/'
+ },
+ {
+ from: '../../../fonts/',
+ to: '../fonts/'
+ },
+ {
+ from: '../../fonts/',
+ to: '../fonts/'
+ },
+ {
+ from: '../../../styles/images/',
+ to: 'images/'
+ }
+ ]
+ },
+ },
+ concat: {
+ dist: {
+ options: {
+ separator: ';\n'
+ },
+ src: ['<%= config.app %>/scripts/{,*/**/}*.js'],
+ dest: '.tmp/concat/scripts/scripts.js'
+ },
+ generated: {
+ options: {
+ separator: '\n'
+ }
+ }
+ },
+
+ // Copies remaining files to places other tasks can use
+ copy: {
+ dist: {
+ files: [{
+ expand: true,
+ dot: true,
+ cwd: '<%= config.app %>',
+ dest: '<%= config.dist %>',
+ src: [
+ '*.{ico,png,txt}',
+ '.htaccess',
+ // 'bower_components/**/*',
+ 'styles/images/**/*',
+ 'styles/fonts/**/*',
+ 'languages/**/*',
+ 'index.html'
+ ]
+ }, {
+ src: 'node_modules/apache-server-configs/dist/.htaccess',
+ dest: '<%= config.dist %>/.htaccess'
+ },
+ {
+ expand: true,
+ cwd: '.tmp/images',
+ dest: '<%= config.dist %>/images',
+ src: ['generated/*']
+ },
+ //TODO to remove this section after integration onboard finished
+ {
+ expand: true,
+ cwd: '<%= config.app %>/third-party',
+ dest: '<%= config.dist %>/third-party',
+ src: ['onboard_bundle_full.js']
+ },
+ {
+ expand: true,
+ dest: '<%= config.dist %>',
+ src: [
+ 'package.json'
+ ]
+ }
+ ]
+ },
+ styles: {
+ expand: true,
+ cwd: '<%= config.app %>/styles',
+ dest: '.tmp/css/',
+ src: '{,*/**/}*.css'
+ }
+ },
+
+ // Run some tasks in parallel to speed up build process
+ concurrent: {
+ server: ['copy:styles'],
+ test: ['copy:styles'],
+ dist: [
+ 'copy:styles',
+ 'imagemin'
+ ]
+ },
+
+ // Test settings
+ karma: {
+ dev: {
+ configFile: "tests/karma.unit.conf.js",
+ singleRun: true,
+ options: {
+ browsers: ['Chrome'],
+ coverageReporter: {
+ type: 'html',
+ dir: 'tests/Coverage'
+ }
+ }
+ },
+ debug: {
+ configFile: "tests/karma.unit.conf.js",
+ singleRun: false,
+ //comment out this line if you want to cancel the watch and see the UT log
+ background: true,
+ options: {
+ browsers: ['Chrome'],
+ reporters: [
+ 'junit',
+ 'dots',
+ 'progress'
+ ]
+ }
+ },
+ jenkins: {
+ configFile: "tests/karma.unit.conf.js",
+ singleRun: true,
+ options: {
+ browsers: ['PhantomJS'],
+ coverageReporter: {
+ type: 'text-summary',
+ dir: 'tests/Coverage',
+ file: 'coverage.txt'
+ }
+ }
+ }
+ },
+
+ tslint: {
+ options: {
+ configuration: 'tslint.json'
+ },
+ files: {
+ src: ['<%= config.app %>/**/*.ts']
+ }
+ }
+ });
+
+ grunt.registerTask('serve', 'start the server and preview your app, --allow-remote for remote access', function (target) {
+
+ var env = grunt.option('env');
+
+ if (grunt.option('allow-remote')) {
+ grunt.config.set('connect.options.hostname', '0.0.0.0');
+ }
+ if (target === 'dist') {
+ return grunt.task.run(['build', 'connect:dist:keepalive']);
+ }
+
+
+ if (env === 'mock') {
+ grunt.task.run([
+ 'express:mock',
+ 'clean:generated',
+ 'ts:all',
+ 'ngtemplates:app',
+ 'injector',
+ 'less:all',
+ 'ngconstant',
+ 'wiredep',
+ 'concurrent:server',
+ 'autoprefixer',
+ 'connect:livereload',
+ 'watch:html',
+ 'watch:less'
+ ]);
+ }
+
+ grunt.task.run([
+ 'clean:generated',
+ 'ts:all',
+ 'ngtemplates:app',
+ 'injector',
+ 'less:all',
+ 'ngconstant',
+ 'wiredep',
+ 'concurrent:server',
+ 'autoprefixer',
+ 'connect:livereload',
+ 'watch'
+ ]);
+ });
+
+ grunt.registerTask('build', [
+ 'clean:generated',
+ 'less:all',
+ 'ts:all',
+ 'ngconstant',
+ 'ngtemplates:app',
+ 'wiredep',
+ 'replace',
+ 'clean:dist',
+ 'useminPrepare:sdc',
+ 'concurrent:dist',
+ 'autoprefixer:dist',
+ 'concat',
+ 'copy:dist',
+ 'cssmin',
+ 'uglify',
+ 'rev',
+ 'usemin'
+ ]);
+
+ grunt.registerTask("test", function (target) {
+
+ if (!(target === 'debug' || target === 'dev' || target === 'jenkins')) {
+ throw new Error("target available for test are <dev|debug|jenkins>");
+ }
+ var tasks = [
+ // "tslint:karma",
+ // "ngconstant",
+ //// "concurrent:test",
+ // "servicesIconConstants",
+ // "autoprefixer",
+ // "ngtemplates:testsTemplates",
+ // "connect:test"
+ ];
+
+ tasks.push('karma:' + target);
+ if (target === 'debug') {
+ if (grunt.config.get('watch.ts')) {
+ tasks.push("watch:ts");
+ } else {
+ throw new Error("target watch:ts is not available, verify that it exists in your Gruntfile");
+ }
+ }
+ grunt.task.run(tasks);
+ });
+
+
+ var lessSingleTask = function (filePath) {
+ var lessSingleFiles = [{
+ expand: true,
+ src: [filePath.replace(/\\/g, '/')],
+ ext: '.css'
+ }];
+ grunt.config('less.single.files', lessSingleFiles);
+ grunt.config('watch.less.tasks', 'less:single');
+
+ };
+
+ var tsSingleTask = function (filePath) {
+
+ var tsSingleData = {
+ src: [filePath.replace(/\\/g, '/')]
+ };
+ // grunt.config('ts.single', tsSingleData);
+
+ };
+
+ var singleTaskByTaskName = {
+ //less: lessSingleTask,
+ ts: tsSingleTask
+ };
+
+ var onGruntWatchEvent = function (action, filepath, target) {
+ if (singleTaskByTaskName[target]) {
+ singleTaskByTaskName[target].call(undefined, filepath);
+ }
+ };
+ grunt.event.on('watch', onGruntWatchEvent);
+};
diff --git a/catalog-ui/SETTING-ENVIRONMENT.md b/catalog-ui/SETTING-ENVIRONMENT.md
new file mode 100644
index 0000000000..edccca1304
--- /dev/null
+++ b/catalog-ui/SETTING-ENVIRONMENT.md
@@ -0,0 +1,36 @@
+# Prerequisites
+
+1. install [node.js](http://nodejs.org/download/)
+2. install [git](http://git-scm.com/). __Make sure to select the option to add git into $PATH__
+3. install grunt with dependencies `npm install -g bower grunt-cli`
+
+ if running on MacOS/Linux the command should be run with `sudo`
+
+# Running the server
+
+Make sure all the client and npm dependencies installed by running the following commands:
+
+1. `npm install`
+2. `bower install`
+
+
+
+You are then presented with 3 options `ngnix`, `test`, `build`
+
+1. `grunt serve --env=mock` will setup a dev(nginx) server under `http://localhost:9000` with mock configurations. The are also `grunt serve:test` and `grunt serve:prod` options
+2. `grunt test` will run all the unit tests in the project
+3. `grunt build` will run a build process resulting with a `dist/` folder including the version ready to be deployed (this task should be mainly run on the CI server)
+3. `grunt build:dev` will deploy to nginx a production artifact, (minify files)
+
+# Webstorm
+
+Although any text editor can be used to write angular applications Webstorm is the most convenient for the task. In case Webstorm is chosen make sure it has the following plugins:
+
+* `.editorconfig` - this plugin will keep line indentation same across all developers
+* `angular.js` - this plugin will help autocompleting angular syntax
+* `markdown` - this one will give nice support to write .md files such as this one you are reading right now
+
+These can be found in plugins settings section by pressing PC: `CTRL + SHIFT + A` MAC: `CMD + SHIFT + A` and typing addons
+
+
+--> DO NOT COMMIT ANYTHING BEFORE RUNNING grunt build / grunt nginx / grunt nginx:mock --env=mock <--
diff --git a/catalog-ui/SETTING-MOCK-SERVER.md b/catalog-ui/SETTING-MOCK-SERVER.md
new file mode 100644
index 0000000000..5db508f225
--- /dev/null
+++ b/catalog-ui/SETTING-MOCK-SERVER.md
@@ -0,0 +1,53 @@
+# Prerequisites
+
+1. install [node.js](http://nodejs.org/download/)
+2. install [git](http://git-scm.com/). __Make sure to select the option to add git into $PATH__
+3. install dependencies [express,cors] npm install express, npm install cors
+
+
+
+# Create the server file
+Example:
+
+#############################################
+ar express = require('express');
+var mockUris = require('../configurations/mock.json');
+var cors = require('cors');
+
+
+var app = express();
+
+// declare server cross browser
+app.use(cors({
+ origin: '*',
+ methods: 'GET, POST, PUT, DELETE',
+ allowedHeaders: 'Content-Type,Authorization,If-Modified-Since'
+}));
+
+/******************************************* MOCKS ENPOINTS *************************************************/
+/* poiFind */
+app.get('/v1' + mockUris.generalConf.getPoiFind.split('v1')[1], function (req, res) {
+ var pois = require('./data/poi/poi-search.json'); // the json response for the api call
+ res.send(pois);
+});
+
+/**************************************************** *******************************************************/
+// declare server listener port
+var server = app.listen(9999, function () {
+ console.log('mock server listening on port %d', server.address().port);
+});
+
+################################
+
+#create mockDate
+
+1. create json file with the response.
+2. add the api end point in the server file and declare the json file for the response/
+
+
+
+# Running the server
+
+1. go to server file folder
+2. run command : node <FileName>
+
diff --git a/catalog-ui/app/_favicon.png b/catalog-ui/app/_favicon.png
new file mode 100644
index 0000000000..6e9f04df69
--- /dev/null
+++ b/catalog-ui/app/_favicon.png
Binary files differ
diff --git a/catalog-ui/app/index.html b/catalog-ui/app/index.html
new file mode 100644
index 0000000000..f56fdfd256
--- /dev/null
+++ b/catalog-ui/app/index.html
@@ -0,0 +1,392 @@
+<!doctype html>
+<html class="no-js" data-ng-app="sdcApp">
+<head>
+ <meta charset="utf-8">
+ <title>SDC</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width">
+ <link rel="shortcut icon" href="favicon.png" type="image/png">
+ <!--<link rel="icon" href="favicon.png" type="image/png">-->
+
+ <!-- build:css(.) styles/vendor.css -->
+ <!-- bower:css -->
+ <link rel="stylesheet" href="bower_components/perfect-scrollbar/src/perfect-scrollbar.css" />
+ <link rel="stylesheet" href="bower_components/angular-tooltips/dist/angular-tooltips.min.css" />
+ <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
+ <link rel="stylesheet" href="bower_components/animate.css/animate.css" />
+ <link rel="stylesheet" href="bower_components/angular-ui-notification/dist/angular-ui-notification.css" />
+ <!-- endbower -->
+ <link rel="stylesheet" href="bower_components/angular-resizable/src/angular-resizable.css"/>
+ <link rel="stylesheet" href="bower_components/qtip2/jquery.qtip.min.css"/>
+
+
+
+ <!-- endbuild -->
+ <!-- build:css(.tmp) styles/main.css -->
+ <link rel="stylesheet" href="styles/app.css">
+ <!-- /include -->
+
+ <!-- injector:css -->
+
+ <!-- endinjector -->
+ <!-- endbuild -->
+
+ <!-- include: "type": "css", "files": "scripts/*.css" -->
+ <!-- /include -->
+</head>
+<body data-ng-class="bodyClass">
+<!--[if lt IE 10]>
+<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade
+ your browser</a> to improve your experience.</p>
+<![endif]-->
+
+
+<div class="sdc-loading-page">
+ <h1 class="caption1">Signing in</h1>
+ <p class="caption2">Please wait. If you are using Internet Explorer, please reattempt access using Chrome or
+ Firefox.</p>
+
+ </p>
+
+ <div class="main-loader">
+ <div class="loader">
+ <svg class="circular" viewBox="25 25 50 50">
+ <circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"/>
+ </svg>
+ </div>
+ </div>
+</div>
+
+<div class="full-height" data-ui-view=""></div>
+
+<!-- build:js(.) scripts/vendor.js -->
+
+<!-- bower:js -->
+<script src="bower_components/jquery/dist/jquery.js"></script>
+<script src="bower_components/angular/angular.js"></script>
+<script src="bower_components/angular-base64/angular-base64.js"></script>
+<script src="bower_components/angular-base64-upload/src/angular-base64-upload.js"></script>
+<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
+<script src="bower_components/jquery-ui/jquery-ui.js"></script>
+<script src="bower_components/angular-dragdrop/src/angular-dragdrop.js"></script>
+<script src="bower_components/angular-filter/dist/angular-filter.js"></script>
+<script src="bower_components/angular-mocks/angular-mocks.js"></script>
+<script src="bower_components/perfect-scrollbar/src/perfect-scrollbar.js"></script>
+<script src="bower_components/angular-perfect-scrollbar/src/angular-perfect-scrollbar.js"></script>
+<script src="bower_components/angular-resource/angular-resource.js"></script>
+<script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
+<script src="bower_components/angular-tooltips/dist/angular-tooltips.min.js"></script>
+<script src="bower_components/angular-translate/angular-translate.js"></script>
+<script src="bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js"></script>
+<script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
+<script src="bower_components/angular-uuid4/angular-uuid4.js"></script>
+<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
+<script src="bower_components/checklist-model/checklist-model.js"></script>
+<script src="bower_components/jspdf/dist/jspdf.min.js"></script>
+<script src="bower_components/angular-clipboard/angular-clipboard.js"></script>
+<script src="bower_components/angular-resizable/src/angular-resizable.js"></script>
+<script src="bower_components/angular-ui-notification/dist/angular-ui-notification.js"></script>
+<script src="bower_components/js-md5/src/md5.js"></script>
+<script src="bower_components/cytoscape/dist/cytoscape.js"></script>
+<script src="bower_components/lodash/lodash.js"></script>
+<script src="bower_components/restangular/dist/restangular.js"></script>
+<script src="bower_components/cytoscape-expand-collapse/cytoscape-expand-collapse.js"></script>
+<script src="bower_components/ev-emitter/ev-emitter.js"></script>
+<script src="bower_components/imagesloaded/imagesloaded.js"></script>
+<script src="bower_components/qtip2/jquery.qtip.js"></script>
+<script src="bower_components/qtip2/basic/jquery.qtip.js"></script>
+<script src="bower_components/cytoscape-qtip/cytoscape-qtip.js"></script>
+<!-- endbower -->
+<script src="non_bower_components/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js"></script>
+<!-- endbuild -->
+
+
+<!-- build:js({app,.tmp}) scripts/main.js -->
+
+
+<!-- include utils -->
+
+<script src="scripts/utils/dictionary/dictionary.js"></script>
+<script src="scripts/utils/prototypes.js"></script>
+<script src="scripts/utils/artifacts-utils.js"></script>
+<script src="scripts/utils/file-utils.js"></script>
+<script src="scripts/utils/validation-utils.js"></script>
+<script src="scripts/utils/component-factory.js"></script>
+<script src="scripts/utils/component-instance-factory.js"></script>
+<script src="scripts/utils/change-lifecycle-state-handler.js"></script>
+<script src="scripts/utils/modals-handler.js"></script>
+<script src="scripts/utils/menu-handler.js"></script>
+<script src="scripts/utils/constants.js"></script>
+<script src="scripts/utils/common-utils.js"></script>
+<script src="scripts/utils/functions.js"></script>
+
+<!-- endinjector:js_utils -->
+<script src="scripts/modules/utils.js"></script>
+<!-- /end include utils-->
+
+<!-- injector:js_models -->
+<script src="scripts/models/activity.js"></script>
+<script src="scripts/models/additional-information.js"></script>
+<script src="scripts/models/app-config.js"></script>
+<script src="scripts/models/artifacts.js"></script>
+<script src="scripts/models/aschema-property.js"></script>
+<script src="scripts/models/attributes.js"></script>
+<script src="scripts/models/capability.js"></script>
+<script src="scripts/models/category.js"></script>
+<script src="scripts/models/comments.js"></script>
+<script src="scripts/models/components/component.js"></script>
+<script src="scripts/models/components/displayComponent.js"></script>
+<script src="scripts/models/components/product.js"></script>
+<script src="scripts/models/components/resource.js"></script>
+<script src="scripts/models/components/service.js"></script>
+<script src="scripts/models/componentsInstances/componentInstance.js"></script>
+<script src="scripts/models/componentsInstances/productInstance.js"></script>
+<script src="scripts/models/componentsInstances/resourceInstance.js"></script>
+<script src="scripts/models/componentsInstances/serviceInstance.js"></script>
+<script src="scripts/models/csar-component.js"></script>
+<script src="scripts/models/data-type-properties.js"></script>
+<script src="scripts/models/data-types-map.js"></script>
+<script src="scripts/models/data-types.js"></script>
+<script src="scripts/models/distribution.js"></script>
+<script src="scripts/models/file-download.js"></script>
+<script src="scripts/models/graph/d2-node.js"></script>
+<script src="scripts/models/graph/graph-links/common-base-link.js"></script>
+<script src="scripts/models/graph/graph-links/common-ci-link-base.js"></script>
+<script src="scripts/models/graph/graph-links/composition-graph-links/composition-ci-link-base.js"></script>
+<script src="scripts/models/graph/graph-links/composition-graph-links/composition-ci-simple-link.js"></script>
+<script src="scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-host-link.js"></script>
+<script src="scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-link.js"></script>
+<script src="scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-link.js"></script>
+<script src="scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-ucpe-link.js"></script>
+<script src="scripts/models/graph/graph-links/links-factory.js"></script>
+<script src="scripts/models/graph/graph-links/module-graph-links/module-ci-link-base.js"></script>
+<script src="scripts/models/graph/graph-links/module-graph-links/module-ci-vl-link.js"></script>
+<script src="scripts/models/graph/graphTooltip.js"></script>
+<script src="scripts/models/graph/link-menu.js"></script>
+<script src="scripts/models/graph/match-relation.js"></script>
+<script src="scripts/models/graph/nodes/base-common-node.js"></script>
+<script src="scripts/models/graph/nodes/common-ci-node-base.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-cp.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-service.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe-cp.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vf.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vfc.js"></script>
+<script src="scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.js"></script>
+<script src="scripts/models/graph/nodes/modules-graph-nodes/module-node-base.js"></script>
+<script src="scripts/models/graph/nodes/nodes-factory.js"></script>
+<script src="scripts/models/graph/point.js"></script>
+<script src="scripts/models/graph/relationMenuObjects.js"></script>
+<script src="scripts/models/graph/relationship.js"></script>
+<script src="scripts/models/inputs.js"></script>
+<script src="scripts/models/instance-inputs-properties-map.js"></script>
+<script src="scripts/models/instances-inputs-map.js"></script>
+<script src="scripts/models/left-panel.js"></script>
+<script src="scripts/models/member.js"></script>
+<script src="scripts/models/modules/base-module.js"></script>
+<script src="scripts/models/properties.js"></script>
+<script src="scripts/models/requirement.js"></script>
+<script src="scripts/models/schema-attribute.js"></script>
+<script src="scripts/models/tab.js"></script>
+<script src="scripts/models/tooltip-data.js"></script>
+<script src="scripts/models/user.js"></script>
+<script src="scripts/models/validate.js"></script>
+<!-- endinjector:js_models -->
+
+<script src="third-party/ng-infinite-scroll/build/ng-infinite-scroll.js"></script>
+
+<!-- injector:js_filters -->
+<script src="scripts/filters/_category-name-filter.js"></script>
+<script src="scripts/filters/capitalize-filter.js"></script>
+<script src="scripts/filters/catalog-status-filter.js"></script>
+<script src="scripts/filters/category-icon-filter.js"></script>
+<script src="scripts/filters/category-type-filter.js"></script>
+<script src="scripts/filters/clear-whitespaces-filter.js"></script>
+<script src="scripts/filters/entity-filter.js"></script>
+<script src="scripts/filters/graph-resource-name-filter.js"></script>
+<script src="scripts/filters/product-category-name-filter.js"></script>
+<script src="scripts/filters/product-subcategory-name-filter.js"></script>
+<script src="scripts/filters/relation-name-fllter.js"></script>
+<script src="scripts/filters/resource-name-filter.js"></script>
+<script src="scripts/filters/resource-type-filter.js"></script>
+<script src="scripts/filters/string-to-date-filter.js"></script>
+<script src="scripts/filters/tests-id-filter.js"></script>
+<script src="scripts/filters/trim-filter.js"></script>
+<script src="scripts/filters/truncate-filter.js"></script>
+<script src="scripts/filters/underscoreless-filter.js"></script>
+<!-- endinjector:js_filters -->
+<script src="scripts/modules/filters.js"></script>
+
+<script src="third-party/PunchOutRegistry.js"></script>
+
+
+<!-- injector:js_directives -->
+<script src="scripts/directives/clicked-outside/clicked-outside-directive.js"></script>
+<script src="scripts/directives/custom-validation/custom-validation.js"></script>
+<script src="scripts/directives/download-artifact/download-artifact.js"></script>
+<script src="scripts/directives/ecomp-header/ecomp-header.js"></script>
+<script src="scripts/directives/edit-name-popover/edit-name-popover-directive.js"></script>
+<script src="scripts/directives/elements/checkbox/checkbox.js"></script>
+<script src="scripts/directives/elements/radiobutton/radiobutton.js"></script>
+<script src="scripts/directives/ellipsis/ellipsis-directive.js"></script>
+<script src="scripts/directives/events/on-last-repeat/on-last-repeat.js"></script>
+<script src="scripts/directives/file-opener/file-opener.js"></script>
+<script src="scripts/directives/file-type/file-type.js"></script>
+<script src="scripts/directives/file-upload/file-upload.js"></script>
+<script src="scripts/directives/graphs-v2/common/common-graph-utils.js"></script>
+<script src="scripts/directives/graphs-v2/common/style/component-instances-nodes-style.js"></script>
+<script src="scripts/directives/graphs-v2/common/style/module-node-style.js"></script>
+<script src="scripts/directives/graphs-v2/composition-graph/composition-graph.directive.js"></script>
+<script src="scripts/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.js"></script>
+<script src="scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.js"></script>
+<script src="scripts/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.js"></script>
+<script src="scripts/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.js"></script>
+<script src="scripts/directives/graphs-v2/deployment-graph/deployment-graph.directive.js"></script>
+<script src="scripts/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.js"></script>
+<script src="scripts/directives/graphs-v2/image-creator/image-creator.service.js"></script>
+<script src="scripts/directives/graphs-v2/palette/palette.directive.js"></script>
+<script src="scripts/directives/graphs-v2/relation-menu/relation-menu.js"></script>
+<script src="scripts/directives/info-tooltip/info-tooltip.js"></script>
+<script src="scripts/directives/invalid-characters/invalid-characters.js"></script>
+<script src="scripts/directives/layout/top-nav/top-nav.js"></script>
+<script src="scripts/directives/layout/top-progress/top-progress.js"></script>
+<script src="scripts/directives/loader/loader-directive.js"></script>
+<script src="scripts/directives/modal/sdc-modal.js"></script>
+<script src="scripts/directives/page-scroller/page-scroller.js"></script>
+<script src="scripts/directives/perfect-scrollbar/angular-perfect-scrollbar.js"></script>
+<script src="scripts/directives/print-graph-screen/print-graph-screen.js"></script>
+<script src="scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.js"></script>
+<script src="scripts/directives/property-types/type-list/type-list-directive.js"></script>
+<script src="scripts/directives/property-types/type-map/type-map-directive.js"></script>
+<script src="scripts/directives/punch-out/punch-out.js"></script>
+<script src="scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab-directive.js"></script>
+<script src="scripts/directives/sdc-tabs/sdc-tabs-directive.js"></script>
+<script src="scripts/directives/structure-tree/structure-tree-directive.js"></script>
+<script src="scripts/directives/tag/tag-directive.js"></script>
+<script src="scripts/directives/tutorial/tutorial-directive.js"></script>
+<script src="scripts/directives/user-header-details/user-header-details-directive.js"></script>
+<script src="scripts/directives/utils/expand-collapse-menu-box/expand-collaps-menu-box.js"></script>
+<script src="scripts/directives/utils/expand-collapse/expand-collapse.js"></script>
+<script src="scripts/directives/utils/page-selector/page-selector.js"></script>
+<script src="scripts/directives/utils/sdc-keyboard-events/sdc-keyboard-events.js"></script>
+<script src="scripts/directives/utils/sdc-tags/sdc-tags.js"></script>
+<script src="scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.js"></script>
+<script src="scripts/directives/utils/sdc_messages/sdc-message.js"></script>
+<script src="scripts/directives/utils/sdc_messages/sdc-messages.js"></script>
+<script src="scripts/directives/utils/smart-tooltip/smart-tooltip.js"></script>
+<script src="scripts/directives/utils/wizard_steps/sdc-wizard-steps.js"></script>
+<!-- endinjector:js_directives -->
+<script src="scripts/modules/directive-module.js"></script>
+
+<!-- injector:js_services -->
+<script src="scripts/services/activity-log-service.js"></script>
+<script src="scripts/services/angular-js-bridge-service.js"></script>
+<script src="scripts/services/available-icons-service.js"></script>
+<script src="scripts/services/cache-service.js"></script>
+<script src="scripts/services/category-resource-service.js"></script>
+<script src="scripts/services/components/component-service.js"></script>
+<script src="scripts/services/components/product-service.js"></script>
+<script src="scripts/services/components/resource-service.js"></script>
+<script src="scripts/services/components/service-service.js"></script>
+<script src="scripts/services/components/utils/composition-left-palette-service.js"></script>
+<script src="scripts/services/configuration-ui-service.js"></script>
+<script src="scripts/services/cookie-service.js"></script>
+<script src="scripts/services/data-types-service.js"></script>
+<script src="scripts/services/ecomp-service.js"></script>
+<script src="scripts/services/entity-service.js"></script>
+<script src="scripts/services/event-listener-service.js"></script>
+<script src="scripts/services/header-interceptor.js"></script>
+<script src="scripts/services/http-error-interceptor.js"></script>
+<script src="scripts/services/loader-service.js"></script>
+<script src="scripts/services/onboarding-service.js"></script>
+<script src="scripts/services/progress-service.js"></script>
+<script src="scripts/services/relation-icons-service.js"></script>
+<script src="scripts/services/sdc-version-service.js"></script>
+<script src="scripts/services/sharing-service.js"></script>
+<script src="scripts/services/url-tobase64-service.js"></script>
+<script src="scripts/services/user-resource-service.js"></script>
+<!-- endinjector:js_services -->
+<script src="scripts/modules/service-module.js"></script>
+
+<!-- injector:js_view_models -->
+<script src="scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.js"></script>
+<script src="scripts/view-models/admin-dashboard/admin-dashboard-view-model.js"></script>
+<script src="scripts/view-models/admin-dashboard/category-management/category-management-view-model.js"></script>
+<script src="scripts/view-models/admin-dashboard/user-management/user-management-view-model.js"></script>
+<script src="scripts/view-models/catalog/catalog-view-model.js"></script>
+<script src="scripts/view-models/component-viewer/component-viewer-view-model.js"></script>
+<script src="scripts/view-models/dashboard/cover/dashboard-cover-view-model.js"></script>
+<script src="scripts/view-models/dashboard/dashboard-view-model.js"></script>
+<script src="scripts/view-models/forms/artifact-form/artifact-form-view-model.js"></script>
+<script src="scripts/view-models/forms/attribute-form/attribute-from-view-model.js"></script>
+<script src="scripts/view-models/forms/env-parameters-form/env-parameters-form.js"></script>
+<script src="scripts/view-models/forms/property-form/property-form-view-model.js"></script>
+<script src="scripts/view-models/forms/resource-instance-name-form/resource-instance-name-model.js"></script>
+<script src="scripts/view-models/modals/confirmation-modal/confirmation-modal-view-model.js"></script>
+<script src="scripts/view-models/modals/email-modal/email-modal-view-model.js"></script>
+<script src="scripts/view-models/modals/error-modal/error-view-model.js"></script>
+<script src="scripts/view-models/modals/message-modal/message-base-modal-model.js"></script>
+<script src="scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.js"></script>
+<script src="scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.js"></script>
+<script src="scripts/view-models/modals/onboarding-modal/onboarding-modal-view-model.js"></script>
+<script src="scripts/view-models/onboard-vendor/onboard-vendor-view-model.js"></script>
+<script src="scripts/view-models/preloading/preloading-view.js"></script>
+<script src="scripts/view-models/support/support-view-model.js"></script>
+<script src="scripts/view-models/tabs/hierarchy/hierarchy-view-model.js"></script>
+<script src="scripts/view-models/tutorial-end/tutorial-end.js"></script>
+<script src="scripts/view-models/welcome/welcome-steps-controller.js"></script>
+<script src="scripts/view-models/welcome/welcome-view.js"></script>
+<script src="scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.js"></script>
+<script src="scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.js"></script>
+<script src="scripts/view-models/wizard/artifact-information-step/artifact-information-step.js"></script>
+<script src="scripts/view-models/wizard/general-step/general-step.js"></script>
+<script src="scripts/view-models/wizard/hierarchy-step/hierarchy-step.js"></script>
+<script src="scripts/view-models/wizard/icons-step/icons-step.js"></script>
+<script src="scripts/view-models/wizard/properties-step/properties-step.js"></script>
+<script src="scripts/view-models/wizard/property-form/property-form-view-model.js"></script>
+<script src="scripts/view-models/wizard/wizard-creation-base.js"></script>
+<script src="scripts/view-models/wizard/wizard-state/create-wizard.js"></script>
+<script src="scripts/view-models/wizard/wizard-state/edit-wizard.js"></script>
+<script src="scripts/view-models/wizard/wizard-state/import-wizard.js"></script>
+<script src="scripts/view-models/workspace/tabs/activity-log/activity-log.js"></script>
+<script src="scripts/view-models/workspace/tabs/attributes/attributes-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/composition/composition-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/composition/tabs/details/details-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.js"></script>
+<script src="scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/deployment/deployment-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/distribution/distribution-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/general/general-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/icons/icons-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/management-workflow/management-workflow-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/properties/properties-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.js"></script>
+<script src="scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.js"></script>
+<script src="scripts/view-models/workspace/workspace-view-model.js"></script>
+<!-- endinjector:js_view_models -->
+<script src="scripts/modules/view-model-module.js"></script>
+
+<!--init -->
+<script src="scripts/modules/configurations.js"></script>
+<script src="scripts/app.js"></script>
+<script src="scripts/templates.js"></script>
+
+<!-- endbuild -->
+
+<script src="/onboarding/punch-outs_en.js" async></script>
+
+<!-- include: "type": "js", "files": "scripts/*.js" -->
+
+<!-- /include -->
+</body>
+</html>
diff --git a/catalog-ui/app/languages/en_US_OS.json b/catalog-ui/app/languages/en_US_OS.json
new file mode 100644
index 0000000000..d7f639e076
--- /dev/null
+++ b/catalog-ui/app/languages/en_US_OS.json
@@ -0,0 +1,420 @@
+{
+ "=========== VALIDATION ===========": "",
+ "VALIDATION_ERROR_MAX_LENGTH": "Max length {{max}} characters.",
+ "VALIDATION_ERROR_MIN_LENGTH": "Min length {{min}} characters.",
+ "VALIDATION_ERROR_REQUIRED": "{{field}} is required.",
+ "VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED": "Special characters not allowed.",
+ "LABEL_MAX_SIZE_XX": "Max size is up to {{size}}",
+ "LABEL_ALL_FIELDS_ARE_MANDATORY": "All fields are mandatory.",
+ "VALIDATION_ERROR_BOOLEAN": "Value should be 'TRUE' or 'FALSE'.",
+ "VALIDATION_ERROR_TYPE": "Value should be of type {{type}}.",
+ "VALIDATION_ERROR_MAX_LENGTH_NOT_INCLUDING_NAME": "Max length {{max}} characters (including name length).",
+ "VALIDATION_ERROR_MAX_FILE_SIZE": "Max file size is 10 MB.",
+ "VALIDATION_ERROR_EMPTY_FILE": "The file you uploaded was empty, please upload a valid file.",
+ "VALIDATION_ERROR_VALUE_MUST_BE_CHANGED": "Value must be changed",
+
+ "=========== GENERAL ===========": "",
+ "GENERAL_LABEL_TYPE": "Type:",
+ "GENERAL_LABEL_VERSION": "Version:",
+ "GENERAL_LABEL_CATEGORY": "Category:",
+ "GENERAL_LABEL_SUB_CATEGORY": "Sub Category:",
+ "GENERAL_LABEL_CREATION_DATE": "Creation Date:",
+ "GENERAL_LABEL_AUTHOR": "Author:",
+ "GENERAL_LABEL_CONTACT_ID": "Contact ID:",
+ "GENERAL_LABEL_STATUS": "Status:",
+ "GENERAL_LABEL_PROJECT_CODE": "Project code:",
+ "GENERAL_LABEL_DESCRIPTION": "Description:",
+ "GENERAL_LABEL_TAGS": "Tags:",
+ "GENERAL_LABEL_RESOURCE_TYPE": "Resource Type:",
+ "GENERAL_LABEL_VENDOR_NAME": "Vendor Name:",
+ "GENERAL_LABEL_VENDOR_RELEASE": "Vendor Release:",
+ "GENERAL_LABEL_LICENSE_TYPE": "License Type:",
+ "GENERAL_LABEL_SERVICE": "Service:",
+ "GENERAL_LABEL_RESOURCE": "Resource:",
+ "GENERAL_LABEL_PRODUCT": "Product:",
+
+ "=========== GENERAL ERROR PAGES ===========": "",
+ "GENERAL_ERROR_403_TITLE": "SDC",
+ "GENERAL_ERROR_403_DESCRIPTION": "Sorry, You are not authorized to view this page, Please contact the <a data-ng-href='mailto:{{mailto}}'> Service Design and Creation administrator</a> for access permission.",
+
+ "=========== ERROR VIEW MODEL ===========": "",
+ "ADMIN_EMAIL": "",
+ "EMAIL_SUBJECT_PREFIX": "SDC Access Request for",
+
+ "=========== TOP MENU ===========": "",
+ "TOP_MENU_HOME_BUTTON": "HOME",
+ "TOP_MENU_CATALOG_BUTTON": "CATALOG",
+ "TOP_MENU_ON_BOARD_BUTTON": "ONBOARD",
+
+ "=========== SIGN IN PAGE ===========": "",
+ "SIGN_IN_CAPTION": "Signing in",
+ "SIGN_IN_DESCRIPTION": "Please wait. If you are using Internet Explorer, please reattempt access using Chrome or Firefox.",
+
+ "=========== TUTORIAL PAGE ===========": "",
+ "TUTRIAL_GENERAL_PREVIOUS_BUTTON": "prev",
+ "TUTRIAL_GENERAL_NEXT_BUTTON": "next",
+ "TUTRIAL_GENERAL_SKIP_BUTTON": "skip",
+ "TUTRIAL_GENERAL_CLOSE_BUTTON": "close",
+ "TUTRIAL_GENERAL_NEXT_BUTTON_END": "done",
+
+ "TUTRIAL_GENERAL_TAB_1": "Workspace",
+ "TUTRIAL_GENERAL_TAB_2": "Catalog",
+ "TUTRIAL_GENERAL_TAB_3": "Design Studio",
+
+ "TUTORIAL_PAGE1_TITLE": "Your Personal Workspace",
+ "TUTORIAL_PAGE1_TEXT": "SDC keeps track of the Resources and Services you are assigned to work on. You access these assets directly from your Workspace.",
+
+ "TUTORIAL_PAGE2_TITLE": "Folders",
+ "TUTORIAL_PAGE2_TEXT": "Your personal Workspace shows the Resources and Services on which you have recently worked. To view a subset, filter by selecting a folder from the left pane.",
+
+ "TUTORIAL_PAGE3_TITLE": "Assets Status and Type",
+ "TUTORIAL_PAGE3_TEXT": "Each of your assets is represented as an item on your Workspace.",
+
+ "TUTORIAL_PAGE4_TITLE": "Asset Type",
+ "TUTORIAL_PAGE4_TEXT": "At the top left corner, R indicates a Resource and S indicates a Service.",
+
+ "TUTORIAL_PAGE5_TITLE": "Asset Name",
+ "TUTORIAL_PAGE5_TEXT": "At the bottom, Resource or Service name and version number are displayed.",
+
+ "TUTORIAL_PAGE6_TITLE": "Asset Icon",
+ "TUTORIAL_PAGE6_TEXT": "A Resource or Service is represented by an icon chosen by the Designer.",
+
+ "TUTORIAL_PAGE7_TITLE": "Asset Status",
+ "TUTORIAL_PAGE7_TEXT": "In the top right corner, an indication of the asset's current status is displayed.",
+
+ "TUTORIAL_PAGE8_TITLE": "Asset Menu",
+ "TUTORIAL_PAGE8_TEXT": "Hover over the three vertical dots to view a menu of actions you can request for the Resource or Service.",
+
+ "TUTORIAL_PAGE9_TITLE": "SDC Catalog",
+ "TUTORIAL_PAGE9_TEXT": "In the SDC Catalog you can view all available Resources and Services. Access the Catalog by clicking Catalog in the left pane.",
+
+ "TUTORIAL_PAGE10_TITLE": "Catalog Filtering",
+ "TUTORIAL_PAGE10_TEXT": "You can filter the Resources and Services displayed in the Catalog. Click on combinations of the Type, Category, and State criteria displayed in the left pane to filter by those criteria.",
+
+ "TUTORIAL_PAGE11_TITLE": "Catalog Search",
+ "TUTORIAL_PAGE11_TEXT": "You can also filter by using the search bar. Type a term, or multiple terms separated by commas. All Resources and Services with a Name, Tag, or Description containing the search term(s) will be displayed.",
+
+ "TUTORIAL_PAGE12_TITLE": "Design Studio",
+ "TUTORIAL_PAGE12_TEXT": "As a Designer, you have access to the Design Studio. You can add, change, and delete information related to a Resource or Service.",
+
+ "TUTORIAL_PAGE13_TITLE": "Create & Modify Asset",
+ "TUTORIAL_PAGE13_TEXT": "From your personal Workspace you can:<ul><li>Create a New Resource or Service (Click Create New)</li><li>Modify an existing Service or Resource (using the menu actions)</li></uL>",
+
+ "TUTORIAL_PAGE14_TITLE": "Create New Asset",
+ "TUTORIAL_PAGE14_TEXT": "When you create a new Resource or Service, you need to fill in profile data to create a new Catalog entry.",
+
+ "TUTORIAL_PAGE15_TITLE": "Left Pane",
+ "TUTORIAL_PAGE15_TEXT": "In creating or editing a Service, you can choose elements from the left pane to add to your Service. You can enter search criteria to change the visible elements in the left pane.",
+
+ "TUTORIAL_PAGE16_TITLE": "Drag & Drop",
+ "TUTORIAL_PAGE16_TEXT": "To include an element in your Service, drag it onto the Design Studio. If a green guide indication appears, drop the element near the guide and the Design Studio will automatically connect the two elements for you.",
+
+ "TUTORIAL_PAGE17_TITLE": "Relationship Selection",
+ "TUTORIAL_PAGE17_TEXT": "When you connect two elements, you can define the relationship between the two. Design Studio will display the allowed relationships from which you can choose.",
+
+ "TUTORIAL_PAGE18_TITLE": "View & Delete Relationship",
+ "TUTORIAL_PAGE18_TEXT": "You can view or delete the relationship between the two elements by selecting the relationship icon on the connecting line.",
+
+ "TUTORIAL_PAGE19_TITLE": "Information tab",
+ "TUTORIAL_PAGE19_TEXT": "Here you can review general information about the Resource, such as version, category, author, and description.",
+
+ "TUTORIAL_PAGE20_TITLE": "Structure tab",
+ "TUTORIAL_PAGE20_TEXT": "Here you can review the structural components of the Resource in a hierarchical tree diagram, including any connected Resources.",
+
+ "TUTORIAL_PAGE21_TITLE": "Deployment Artifacts tab",
+ "TUTORIAL_PAGE21_TEXT": "Here you can view and manage the Artifact files associated with the Resource. You can download existing files, upload additional files, and manage HEAT template parameters.",
+
+ "TUTORIAL_PAGE22_TITLE": "Properties tab",
+ "TUTORIAL_PAGE22_TEXT": "Here you can manage the properties of the Resource. You can view, edit, add and delete properties.",
+
+ "TUTORIAL_PAGE23_TITLE": "Information Artifacts tab",
+ "TUTORIAL_PAGE23_TEXT": "Here you can manage documents associated with the Resource (such as test scripts, features, and capacity).",
+
+ "TUTORIAL_PAGE24_TITLE": "Requirements and Capabilities tab",
+ "TUTORIAL_PAGE24_TEXT": "Here you can view the Resource capabilities and requirements, and from where the requirements are derived. If a Resource is connected to other Resources, the relationship associated with this connection is reflected (in the Requirements section) of this tab.",
+
+ "TUTORIAL_PAGE25_TITLE": "Information tab",
+ "TUTORIAL_PAGE25_TEXT": "Here you can view general information about the Service, such as version, category, author, and description.",
+
+ "TUTORIAL_PAGE26_TITLE": "Structure tab",
+ "TUTORIAL_PAGE26_TEXT": "Here you can view the Service structure in a hierarchical tree diagram, including the component Resources.",
+
+ "TUTORIAL_PAGE27_TITLE": "Deployment Artifacts tab",
+ "TUTORIAL_PAGE27_TEXT": "Here you can view and manage the Artifact files associated with the Service. You can download existing files, upload additional files, and manage HEAT template parameters.",
+
+ "TUTORIAL_PAGE28_TITLE": "Inputs tab",
+ "TUTORIAL_PAGE28_TEXT": "Here you can view and edit the properties of the Service, and see from which Resource the parameters originated. Properties are grouped by Resource instance.",
+
+ "TUTORIAL_PAGE29_TITLE": "Information Artifacts tab",
+ "TUTORIAL_PAGE29_TEXT": "Here you can manage documents associated with the Service (such as service topology, message flows, and management flows). Some of these documents are created by embedded tools, such as ASE tools.",
+
+ "TUTORIAL_PAGE30_TITLE": "API tab",
+ "TUTORIAL_PAGE30_TEXT": "Here you can manage the Service API (such as monitoring and instantiation), and you can upload an archive containing the HTML pages and the API URL.",
+
+ "TUTORIAL_LAST_PAGE_TITLE": "Great!",
+ "TUTORIAL_LAST_PAGE_TEXT": "Now that you are familiar with the system, you can begin building!",
+ "TUTORIAL_LAST_PAGE_BTN_ACTION_MY_DASHBOARD": "My Dashboard",
+
+ "=========== CATALOG PAGE ===========": "",
+ "HEADER_CAPTION_CATALOG": "Catalog",
+ "SORT_CAPTION": "Order By:",
+ "SORT_BY_UPDATE_DATE": "Last updated",
+ "SORT_ALPHABETICAL": "Alphabetical order",
+
+
+ "=========== HEADER PAGE ===========": "",
+ "HEADER_HELP_TEXT_CATALOG_TUTORIAL": "Catalog Tutorial",
+ "HEADER_HELP_TEXT_DESIGNER_TUTORIAL": "Design Studio Tutorial",
+ "HEADER_HELP_TEXT_WORKSPACE_TUTORIAL": "Workspace Tutorial",
+
+ "=========== WELCOME PAGE ===========": "",
+ "WELCOME_HELLO": "Hello {{username}},",
+ "WELCOME_MESSAGE_1": "Welcome to Service Design and Creation (SDC) Portal. SDC supports Domain 2.0 Resource Onboarding and Service Creation.",
+ "WELCOME_MESSAGE_2": "This tutorial walks first-time users through the SDC portal components. Future logins directly access the SDC work platform without displaying the tutorial. To see the tutorial, click the Tutorial link on each SDC page.",
+ "WELCOME_MESSAGE_CURRENT_VERSION": "Current Version {{version}}",
+ "WELCOME_MESSAGE_SEE_WHATS_NEW": "See what's new",
+ "WELCOME_BTN_START_TUTORIAL": "Let's start",
+ "WELCOME_BTN_SKIP": "skip for now",
+ "WELCOME_BTN_ACTION_DESIGN_STUDIO": "Design Studio",
+ "WELCOME_BTN_ACTION_CERTIFICATION_STUDIO": "Tester's Workspace",
+ "WELCOME_BTN_ACTION_GOVERNOR_BOARD": "Governance Board",
+ "WELCOME_BTN_ACTION_DISTRIBUTION_STUDIO": "Distribution Studio",
+ "WELCOME_BTN_ACTION_CATALOG": "Catalog",
+
+ "=========== ONBOARD VENDOR PAGE ===========": "",
+ "HEADER_CAPTION_ONBOARD":"On Board",
+
+ "=========== NEW RESOURCE SERVICE ===========": "",
+ "NEW_SERVICE_RESOURCE_WRAPPER_TAB_GENERAL_INFORMATION": "General Information",
+ "NEW_SERVICE_RESOURCE_WRAPPER_TAB_ASSIGN_PRODUCT_HIERARCHY": "Assign Product Hierarchy",
+ "NEW_SERVICE_RESOURCE_WRAPPER_TAB_ADDITIONAL_INFO": "Additional Information",
+ "NEW_SERVICE_RESOURCE_WRAPPER_TAB_BILLING_AND_ORDERING": "Order Attributes & Rules",
+ "NEW_SERVICE_RESOURCE_WRAPPER_TAB_PRODUCT_COMPOSITION": "Bill Attributes & Rules",
+ "NEW_SERVICE_RESOURCE_SAVE_BUTTON": "Save",
+ "NEW_SERVICE_RESOURCE_DONE_BUTTON": "Done",
+
+ "=========== NEW RESOURCE SERVICE ERRORS ===========": "",
+ "NEW_SERVICE_RESOURCE_ERROR_SERVICE_NAME_REQUIRED": "Service name is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_MAX_LENGTH_25": "Max length 25 characters.",
+ "NEW_SERVICE_RESOURCE_ERROR_MAX_LENGTH_50": "Max length 50 characters.",
+ "NEW_SERVICE_RESOURCE_ERROR_MAX_LENGTH_128": "Max length 128 characters.",
+ "NEW_SERVICE_RESOURCE_ERROR_MAX_LENGTH_1024": "Max length 1024 characters.",
+ "NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS": "Name already exists.",
+ "NEW_SERVICE_RESOURCE_ERROR_SPECIAL_CHARS": "Special characters not allowed.",
+ "NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED": "category is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_REQUIRED": "Project code is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_NOT_VALID": "Project code is not valid.",
+ "NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED": "Contact is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID": "Contact is not valid.",
+ "NEW_SERVICE_RESOURCE_ERROR_SERVICE_DESCRIPTION_REQUIRED": "Service description is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_MIN_ONE_TAG": "Enter at least one tag.",
+ "NEW_SERVICE_RESOURCE_ERROR_TAG_NAME_EXIST": "Tag name already exists.",
+ "NEW_SERVICE_RESOURCE_ERROR_SERVICE_ICON": "Icon required.",
+ "NEW_SERVICE_RESOURCE_ERROR_RESOURCE_ICON": "Icon required.",
+ "NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED": "Name is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_PRODUCT_NAME_REQUIRED": "Name is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_RESOURCE_DESCRIPTION_REQUIRED": "Description is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_PRODUCT_DESCRIPTION_REQUIRED": "Description is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_VENDOR_NAME_REQUIRED": "Vendor name is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_VENDOR_RELEASE_REQUIRED": "Vendor Release is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_TEMPLATE_REQUIRED": "Template is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_TAG_PATTERN": "{{text}}",
+ "NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE": "Invalid Tosca file",
+ "NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS": "File extension should be {{extensions}}.",
+ "NEW_SERVICE_RESOURCE_ERROR_TOSCA_FILE_REQUIRED": "Tosca file is required.",
+ "NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE": "Invalid csar file",
+ "NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS": "File extension should be {{extensions}}.",
+ "NEW_PRODUCT_NO_CATEGORIES_TO_DISPLAY": "Your product is not assigned to any group yet. <br> To select/find a group, begin typing above and select group to add",
+
+ "=========== SUGGESTED ICONS TOOLTIP ===========": "",
+ "call_controll": "Call Control",
+ "mobility": "Mobility",
+ "network_l_1-3": "Network L 1-3",
+ "network_l_4": "Network L 4+",
+ "router": "Router",
+ "database": "Database",
+ "network": "Network",
+ "objectStorage": "Object Storage",
+ "connector": "Connector",
+ "brocade": "Brocade",
+ "cisco": "Cisco",
+ "ericsson": "Ericsson",
+ "tropo": "Tropo",
+ "fortinet": "Fortinet",
+ "att": "AT&T",
+ "broadsoft": "BroadSoft",
+ "alcatelLucent": "Alcatel-Lucent",
+ "metaswitch": "Metaswitch",
+ "aricent": "Aricent",
+ "mySql": "MySQL",
+ "juniper": "Juniper Networks",
+ "oracle": "Oracle Corporation",
+ "nokia_siemens": "Nokia Networks",
+ "borderElement": "Border Element",
+ "applicationServer": "Application Server",
+ "server": "Server",
+ "port": "Port",
+ "loadBalancer": "Load Balancer",
+ "compute": "Compute",
+ "gateway": "Gateway",
+ "defaulticon": "Default Icon",
+
+ "=========== ADD ARTIFACT FROM ===========": "",
+ "ADD_ARTIFACT_ERROR_VALID_EXTENSIONS": "File extension should be {{extensions}}.",
+ "ADD_ARTIFACT_ERROR_FILE_REQUIRED": "Artifact file is required.",
+ "ADD_ARTIFACT_ERROR_LABEL_REQUIRED": "Artifact label is required.",
+ "ADD_ARTIFACT_ERROR_LABEL_MAXLENGTH": "Max length 25 characters.",
+ "ADD_ARTIFACT_ERROR_LABEL_PATTERN": "not allowed special characters.",
+ "ADD_ARTIFACT_ERROR_TYPE_REQUIRED": "type is required.",
+ "ADD_ARTIFACT_ERROR_TIMEOUT_MIN": "Timeout can't be set to zero.",
+ "ADD_ARTIFACT_ERROR_TIMEOUT_MAXLENGTH": "Max length 25 characters.",
+ "ADD_ARTIFACT_ERROR_TIMEOUT_PATTERN": "Invalid value.",
+ "ADD_ARTIFACT_ERROR_APIURL_REQUIRED": "Artifact URL is required.",
+ "ADD_ARTIFACT_ERROR_APIURL_MAXLENGTH": "Max length 100 characters.",
+ "ADD_ARTIFACT_ERROR_APIURL_URL": "Url not valid",
+ "ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED": "Description is required.",
+ "ADD_ARTIFACT_ERROR_DESCRIPTION_MAXLENGTH": "Max length 256 characters.",
+ "ADD_ARTIFACT_ERROR_DESCRIPTION_PATTERN": "not allowed special characters.",
+
+ "=========== ARTIFACT VIEW ===========": "",
+ "ARTIFACT_VIEW_DELETE_MODAL_TITLE": "Delete Artifact Confirmation",
+ "ARTIFACT_VIEW_DELETE_MODAL_TEXT": "Are you sure you want to delete '{{name}}'?",
+
+ "=========== PROPERTY VIEW ===========": "",
+ "PROPERTY_VIEW_DELETE_MODAL_TITLE": "Delete Property Confirmation",
+ "PROPERTY_VIEW_DELETE_MODAL_TEXT": "Are you sure you want to delete '{{name}}'?",
+ "PROPERTY_EDIT_PATTERN": "Invalid value.",
+ "PROPERTY_EDIT_LIST_STRING": "Invalid value. The correct value is \"X\",\"Y\"",
+ "PROPERTY_EDIT_MAP_STRING": "Invalid value. The correct value is \"Key\":\"value\"",
+ "PROPERTY_EDIT_LIST_GENERIC": "Invalid value. The correct value is X, Y",
+ "PROPERTY_EDIT_MAP_GENERIC": "Invalid value. The correct value is \"Key\":value",
+ "PROPERTY_EDIT_MAP_UNIQUE_KEYS": "Key must be unique.",
+
+ "=========== ATTRIBUTE VIEW ===========": "",
+ "ATTRIBUTE_VIEW_DELETE_MODAL_TITLE": "Delete Attribute Confirmation",
+ "ATTRIBUTE_VIEW_DELETE_MODAL_TEXT": "Are you sure you want to delete '{{name}}'?",
+ "ATTRIBUTE_EDIT_PATTERN": "Invalid value.",
+ "NEW_ATTRIBUTE_ERROR_NAME_EXISTS":"Name already exists.",
+ "ATTRIBUTE_EDIT_LIST_STRING": "Invalid value. The correct value is \"X\",\"Y\".",
+ "ATTRIBUTE_EDIT_MAP_STRING": "Invalid value. The correct value is \"Key\":\"value\".",
+ "ATTRIBUTE_EDIT_LIST_BOOLEAN": "Invalid value. The correct value is true,false.",
+ "ATTRIBUTE_EDIT_MAP_BOOLEAN": "Invalid value. The correct value is \"Key\":true.",
+ "ATTRIBUTE_EDIT_LIST_GENERIC": "Invalid value. The correct value is X, Y.",
+ "ATTRIBUTE_EDIT_MAP_GENERIC": "Invalid value. The correct value is \"Key\":value.",
+ "ATTRIBUTE_EDIT_MAP_UNIQUE_KEYS": "Key must be unique.",
+
+ "=========== UPDATE PARAMETERS FROM ===========": "",
+ "UPDATE_PARAMETERS_TEXT": "Review the parameters and their default values. If necessary, you can modify the values by editing the text fields.",
+ "ENV_FILE_GENERATION": "Please notice: empty fields will not be included in the ENV file",
+
+ "=========== ENTITY VIEWER ===========": "",
+ "ENTITY_VIEWER_PROPERTIES_TAB": "Properties",
+ "ENTITY_VIEWER_ACTIVITY_LOG_TAB": "Activity Log",
+
+ "=========== WHATS NEW ===========": "",
+ "WHATS_NEW_ON": "What's new on {{version}}",
+ "WHATS_NEW_LIST": "",
+ "WHATS_NEW_1_TITLE": "VNF On-Boarding",
+ "WHATS_NEW_1_BODY": "Vendors can upload VNF HEAT files & License artifacts<ul><li>Upload VNF HEAT files</li><li>Licensing</li><li>Questionnaire</li>",
+ "WHATS_NEW_2_TITLE": "Call & Network Flow-Tool",
+ "WHATS_NEW_2_BODY": "Introduced the ability to host a 3rd party<br/>Enhance VF and Service design with Call and Network flows; Call & Network Flows created and stored as Information Artifacts.",
+ "WHATS_NEW_3_TITLE": "Import VF from On-Boarded VNF",
+ "WHATS_NEW_3_BODY": "Import VF Metadata from vendor supplied CSAR file<br/>Create the VF based on data included in the CSAR file. Includes; VF topology and deployment artifacts<br/>Once checked in the VF is Displayed in the Catalog",
+ "WHATS_NEW_4_TITLE": "App Navigation",
+ "WHATS_NEW_4_BODY": "This release introduces View and Edit modes<ol><li>Use the view mode when:<ul><li>User does not have authorization to edit an Asset</li><li>Asset is in Check-In Mode</li></ul></li><li>Use the Edit Mode when:<ul><li>User has authorization to edit an Asset</li><li>Asset is in Check-out Mode</li></ul></li></ol>",
+ "WHATS_NEW_5_TITLE": "Certification Studio",
+ "WHATS_NEW_5_BODY": "Tester, Governor and Ops roles can view VF and Service Composition.<br/>They can also download the asset’s artifacts, and download the asset’s resource instance artifacts",
+ "WHATS_NEW_6_TITLE": "Composition: Virtual Link",
+ "WHATS_NEW_6_BODY": "The ability to link VFs via a Virtual link has been expanded. Now a Virtual link can be defined as having either Point-to-Point connectivity or Multi-point connectivity. Additionally from within a Virtual Link, Point-to-Point connectivity can be dynamically changed to Multi-Point connectivity.",
+ "WHATS_NEW_7_TITLE": "Distribution",
+ "WHATS_NEW_7_BODY": "<ul><li>Distribution of artifact to App-C</li><li>Distribution of artifact to ALTS</li><li>Distribution of artifact to A&AI</li></ul>",
+ "WHATS_NEW_8_TITLE": "New Brand",
+ "WHATS_NEW_8_BODY": "Effort was invested in improving the overall application Usability, and the User Interface was modified to better reflect branding standards.",
+
+ "=========== USER_MANAGEMENT SCREEN ===========": "",
+ "USER_MANAGEMENT": "User Management",
+ "USER_MANAGEMENT_TABLE_HEADER_USER_ID": "User id",
+ "USER_MANAGEMENT_SEARCH_LABEL": "Search user by name, user id, email or role",
+ "USER_MANAGEMENT_SEARCH_TEXT": "Enter user id",
+ "CATEGORY_MANAGEMENT": "Category Management",
+ "ECOMP": "ECOMP",
+ "MONITOR": "Monitor",
+ "USER_MANAGEMENT_VIEW_DELETE_MODAL_TITLE": "Delete User Confirmation",
+ "USER_MANAGEMENT_VIEW_DELETE_MODAL_TEXT": "Are you sure you want to delete the user?",
+ "NEW_USER_ERROR_USER_ID_REQUIRED": "User id is required.",
+ "NEW_USER_ERROR_USER_ID_NOT_VALID": "User id not valid.",
+ "NEW_USER_ERROR_ROLE_REQUIRED": "User role is required.",
+
+ "=========== EMAIL_MODAL ===========": "",
+ "EMAIL_MODAL_TITLE": "Submit For Testing",
+ "EMAIL_MODAL_SUBJECT": "{{entityName}}; version {{entityVersion}}",
+ "EMAIL_MODAL_MESSAGE": "Enter your message here...",
+ "EMAIL_OUTLOOK_MESSAGE": "mailto:{{to}}?subject={{subject}} is now ready for testing&body={{message}}%0D%0A%0D%0AClick on the link below to open {{entityNameAndVersion}} that is ready for testing. %0D%0A{{link}}%0D%0A",
+
+ "=========== CATEGORY_MANAGEMENT SCREEN ===========": "",
+ "RESOURCE_CATEGORY_HEADER": "Resource Category",
+ "SERVICE_CATEGORY_HEADER": "Service Category",
+ "SUBCATEGORY_HEADER": "Sub Category",
+ "ADD_CATEGORY": "New",
+ "ADD_SUBCATEGORY": "New",
+ "DELETE_CATEGORY_MODAL_HEADER": "Delete {{modelType}}",
+ "DELETE_CATEGORY_MODAL_CATEGORY_NAME": "Are you sure you want to delete the {{modelType}}?",
+ "CREATE_CATEGORY_MODAL_HEADER": "Create new {{modelType}}",
+ "CREATE_CATEGORY_MODAL_CATEGORY_NAME": "Enter {{modelType}} name",
+ "CREATE_CATEGORY_MODAL_REQUIRED": "{{modelType}} name is mandatory",
+ "CREATE_CATEGORY_MODAL_MINLENGTH": "{{modelType}} name should be at least {{minlength}} characters",
+ "CREATE_CATEGORY_MODAL_PATTERN": "{{modelType}} name is not valid"
+
+,"=========== PDF FILE ===========": "",
+ "PDF_FILE_DECLARATION_BOLD": "AT&T Proprietary (Restricted)",
+ "PDF_FILE_DECLARATION": "Only for use by authorized individuals or any above-designated team(s) within the AT&T companies and not for general distribution"
+
+,"=========== DEPLOYMENT ARTIFACTS ===========": "",
+ "DEPLOYMENT_ARTIFACT_NO_ARTIFACTS_TO_DISPLAY": "There are no deployment artifacts to display",
+ "DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT": "Add {{name}}",
+ "DEPLOYMENT_ARTIFACT_BUTTON_ADD_BASE_HEAT": "Add Base HEAT Artifact",
+ "DEPLOYMENT_ARTIFACT_BUTTON_ADD_NETWORK_HEAT": "Add Network HEAT Artifact",
+ "DEPLOYMENT_ARTIFACT_BUTTON_ADD_VOLUME_HEAT": "Add Volume HEAT Artifact",
+ "DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER": "Add Other Artifact"
+
+,"=========== IMPORT VF ===========": "",
+ "IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_TITLE": "Create VF",
+ "IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_DESCRIPTION": "Your VF is being created.<br/>It can take up to 10 minutes.<br/>When done, you can view it in SDC Catalog.",
+ "IMPORT_VF_MESSAGE_CREATE_FINISHED_TITLE": "Create/Update",
+ "IMPORT_VF_MESSAGE_CREATE_FINISHED_DESCRIPTION": "saved successfully",
+ "IMPORT_VF_ALREADY_EXISTS_TITLE": "Create VF",
+ "IMPORT_VF_ALREADY_EXISTS_DESCRIPTION": "The VF {{vfName}} is already certified.<br/>When updating a VF that was already certified, the values for several parameters will not be changed.<br/>These parameters are: Name, Category and Icon.",
+
+ "=========== TABS TITLE ===========": "",
+ "HIERARCHY_TAB_TITLE": "HIERARCHY",
+
+ "=========== DISTRIBUTION VIEW ===========": "",
+ "DISTRIBUTION_VIEW_TITLE_USER_ID": "User id:",
+
+ "=========== SUCCESS MESSAGES ===========": "",
+ "CHECKIN_SUCCESS_MESSAGE_TEXT": "Checked in successfully",
+ "CHECKIN_SUCCESS_MESSAGE_TITLE": "Check in",
+ "CHECKOUT_SUCCESS_MESSAGE_TEXT": "Checked out successfully",
+ "CHECKOUT_SUCCESS_MESSAGE_TITLE": "Check out",
+ "DELETE_SUCCESS_MESSAGE_TEXT": "Deleted successfully",
+ "DELETE_SUCCESS_MESSAGE_TITLE": "Delete",
+ "SUBMIT_FOR_TESTING_SUCCESS_MESSAGE_TEXT": "Submitted successfully for testing",
+ "SUBMIT_FOR_TESTING_SUCCESS_MESSAGE_TITLE": "Submit For Testing",
+ "START_TESTING_SUCCESS_MESSAGE_TEXT": "Tested successfully",
+ "START_TESTING_SUCCESS_MESSAGE_TITLE": "Start Testing",
+ "CANCEL_TESTING_SUCCESS_MESSAGE_TEXT": "Cancelled successfully",
+ "CANCEL_TESTING_SUCCESS_MESSAGE_TITLE": "Cancel",
+ "REJECT_SUCCESS_MESSAGE_TEXT": "Rejected successfully",
+ "REJECT_SUCCESS_MESSAGE_TITLE": "Reject",
+ "ACCEPT_TESTING_SUCCESS_MESSAGE_TEXT": "Accepted successfully",
+ "ACCEPT_TESTING_SUCCESS_MESSAGE_TITLE": "Accept",
+ "APPROVE_SUCCESS_MESSAGE_TEXT": "Approved successfully",
+ "APPROVE_SUCCESS_MESSAGE_TITLE": "Approve",
+ "DISTRIBUTE_SUCCESS_MESSAGE_TEXT": "Distributed successfully",
+ "DISTRIBUTE_SUCCESS_MESSAGE_TITLE": "Distribute",
+
+ "=========== ON BOARDING MODAL INFO MESSAGES ===========": "",
+ "ON_BOARDING_GENERAL_INFO": "Displays a table of VSPs created using Onboarding.<br/> Each row displays details for a single VSP.<br/> When expanded you can either import CSAR files that are yet to be imported or update CSAR files that were previously imported.",
+ "ON_BOARDING_IMPORT_INFO": "Displays the Onboarding repository and supports importing on-boarded CSAR files.<br/> Select an imported CSAR file to create a VF from the on-boarded and imported information.",
+ "ON_BOARDING_UPDATE_INFO": "Displays the Onboarding repository and supports updating on-boarded and previously imported CSAR files.<br/> Clicking Update, updates the existing VF with information available from a new version of the CSAR file."
+}
diff --git a/catalog-ui/app/scripts/app.ts b/catalog-ui/app/scripts/app.ts
new file mode 100644
index 0000000000..513810595b
--- /dev/null
+++ b/catalog-ui/app/scripts/app.ts
@@ -0,0 +1,936 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="./references"/>
+/*
+ SD&C Web Portal Wireframes – Designer Home Page and Create New Service Flow
+ */
+//libraries variables to prevent compile errors
+declare let jsPDF:any;
+
+module Sdc {
+ import User = Sdc.Models.User;
+ import UserResourceService = Sdc.Services.UserResourceService;
+
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+ let moduleName:string = 'sdcApp';
+ let viewModelsModuleName:string = 'Sdc.ViewModels';
+ let directivesModuleName:string = 'Sdc.Directives';
+ let servicesModuleName:string = 'Sdc.Services';
+ let filtersModuleName:string = 'Sdc.Filters';
+ let utilsModuleName: string = 'Sdc.Utils';
+ let dependentModules:Array<string> = [
+ 'ui.router',
+ 'ui.bootstrap',
+ 'ngDragDrop',
+ 'ui-notification',
+ 'ngResource',
+ 'ngSanitize',
+ 'Sdc.Config',
+ 'naif.base64',
+ 'base64',
+ 'uuid4',
+ 'checklist-model',
+ 'angular.filter',
+ 'pascalprecht.translate',
+ '720kb.tooltips',
+ 'restangular',
+ 'angular-clipboard',
+ 'angularResizable',
+ 'infinite-scroll',
+ viewModelsModuleName,
+ directivesModuleName,
+ servicesModuleName,
+ filtersModuleName,
+ utilsModuleName
+ ];
+
+ let appModule:ng.IModule = angular.module(moduleName, dependentModules);
+
+ appModule.config([
+ '$stateProvider',
+ '$translateProvider',
+ '$urlRouterProvider',
+ '$httpProvider',
+ 'tooltipsConfigProvider',
+ 'NotificationProvider',
+ ($stateProvider:any,
+ $translateProvider:any,
+ $urlRouterProvider:ng.ui.IUrlRouterProvider,
+ $httpProvider:ng.IHttpProvider,
+ tooltipsConfigProvider:any,
+ NotificationProvider:any):void => {
+
+ NotificationProvider.setOptions({
+ delay: 10000,
+ startTop: 10,
+ startRight: 10,
+ closeOnClick: true,
+ verticalSpacing: 20,
+ horizontalSpacing: 20,
+ positionX: 'right',
+ positionY: 'top'
+ });
+
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+ console.info('appModule.config: ', viewModelsHtmlBasePath);
+
+ $translateProvider.useStaticFilesLoader({
+ prefix: 'languages/',
+ langKey: '',
+ suffix: '.json?d=' + (new Date()).getTime()
+ });
+ $translateProvider.useSanitizeValueStrategy('escaped');
+ $translateProvider.preferredLanguage('en_US_OS'); // For open source changed to en_US_OS
+
+ $httpProvider.interceptors.push('Sdc.Services.HeaderInterceptor');
+ $httpProvider.interceptors.push('Sdc.Services.HttpErrorInterceptor');
+
+ $urlRouterProvider.otherwise('welcome');
+
+ $stateProvider.state(
+ 'dashboard', {
+ url: '/dashboard?show&folder',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'dashboard/dashboard-view.html');
+ }],
+ controller: viewModelsModuleName + '.DashboardViewModel',
+
+ }
+ );
+
+ $stateProvider.state(
+ 'welcome', {
+ url: '/welcome',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'welcome/welcome-view.html');
+ }],
+ controller: viewModelsModuleName + '.WelcomeViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'dashboard.cover', {
+ url: '/cover',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'dashboard/cover/dashboard-cover-view.html');
+ }],
+ controller: viewModelsModuleName + '.DashboardCoverViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'dashboard.tutorial-end', {
+ url: '/tutorial-end',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'tutorial-end/tutorial-end.html');
+ }],
+ controller: viewModelsModuleName + '.TutorialEndViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'additionalInformation', {
+ url: '/additionalInformation',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'additional-information/additional-information-view.html');
+ }],
+ controller: viewModelsModuleName + '.AdditionalInformationViewModel'
+ }
+ );
+
+ let componentsParam:Array<any> = ['$stateParams', 'Sdc.Services.EntityService','Sdc.Services.CacheService' , ($stateParams:any, EntityService:Sdc.Services.EntityService, cacheService:Services.CacheService) => {
+ if(cacheService.get('breadcrumbsComponents')){
+ return cacheService.get('breadcrumbsComponents');
+ } else {
+ return EntityService.getCatalog(); //getAllComponents() doesnt return components from catalog
+ }
+ }];
+
+
+ $stateProvider.state (
+ 'workspace', {
+ url: '/workspace/:id/:type/',
+ params: {'importedFile':null,'componentCsar':null,'resourceType': null, 'disableButtons': null}, //'vspComponent': null,
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/workspace-view.html');
+ }],
+ controller: viewModelsModuleName + '.WorkspaceViewModel',
+ resolve: {
+ injectComponent: ['$stateParams', 'ComponentFactory' , function ($stateParams, ComponentFactory) {
+ /*
+ if($stateParams.vspComponent){
+ return $stateParams.vspComponent;
+ } else
+ */
+ if($stateParams.id){
+ return ComponentFactory.getComponentFromServer($stateParams.type.toUpperCase(), $stateParams.id);
+ } else if ($stateParams.componentCsar && $stateParams.componentCsar.csarUUID) {
+ return $stateParams.componentCsar;
+ } else {
+ let emptyComponent = ComponentFactory.createEmptyComponent($stateParams.type.toUpperCase());
+ if (emptyComponent.isResource() && $stateParams.resourceType){
+ // Set the resource type
+ (<Resource>emptyComponent).resourceType = $stateParams.resourceType;
+ }
+ if($stateParams.importedFile){
+ (<Models.Components.Resource>emptyComponent).importedFile = $stateParams.importedFile;
+ }
+ return emptyComponent;
+ }
+ }],
+ components: componentsParam
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_GENERAL, {
+ url: 'general',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.GeneralViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/general/general-view.html');
+ }],
+ data: {unsavedChanges:false,bodyClass:'general'}
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_ICONS, {
+ url: 'icons',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.IconsViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/icons/icons-view.html');
+ }],
+ data: {unsavedChanges:false,bodyClass:'icons'}
+
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_ACTIVITY_LOG, {
+ url: 'activity_log',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.ActivityLogViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/activity-log/activity-log.html');
+ }],
+ data: {unsavedChanges:false}
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_DEPLOYMENT_ARTIFACTS, {
+ url: 'deployment_artifacts',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.DeploymentArtifactsViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/deployment-artifacts/deployment-artifacts-view.html');
+ }],
+ data:{
+ bodyClass:'deployment_artifacts'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_HIERARCHY, {
+ url: 'hierarchy',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.ProductHierarchyViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/product-hierarchy/product-hierarchy-view.html');
+ }]
+
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_INFORMATION_ARTIFACTS, {
+ url: 'information_artifacts',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.InformationArtifactsViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/information-artifacts/information-artifacts-view.html');
+ }],
+ data:{
+ bodyClass:'information_artifacts'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_TOSCA_ARTIFACTS, {
+ url: 'tosca_artifacts',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.ToscaArtifactsViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/tosca-artifacts/tosca-artifacts-view.html');
+ }],
+ data:{
+ bodyClass:'tosca_artifacts'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_PROPERTIES, {
+ url: 'properties',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.PropertiesViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/properties/properties-view.html');
+ }],
+ data:{
+ bodyClass:'properties'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_SERVICE_INPUTS, {
+ url: 'service_inputs',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.ServiceInputsViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/inputs/service-input/service-inputs-view.html');
+ }],
+ data:{
+ bodyClass:'workspace-inputs'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_RESOURCE_INPUTS, {
+ url: 'resource_inputs',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.ResourceInputsViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/inputs/resource-input/resource-inputs-view.html');
+ }],
+ data:{
+ bodyClass:'workspace-inputs'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_ATTRIBUTES, {
+ url: 'attributes',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.AttributesViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/attributes/attributes-view.html');
+ }],
+ data:{
+ bodyClass:'attributes'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_REQUIREMENTS_AND_CAPABILITIES, {
+ url: 'req_and_capabilities',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.ReqAndCapabilitiesViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/req-and-capabilities/req-and-capabilities-view.html');
+ }],
+ data:{
+ bodyClass:'attributes'
+ }
+ }
+ );
+
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_MANAGEMENT_WORKFLOW, {
+ parent: 'workspace',
+ url: 'management_workflow',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/management-workflow/management-workflow-view.html');
+ }],
+ controller: viewModelsModuleName + '.ManagementWorkflowViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_NETWORK_CALL_FLOW, {
+ parent: 'workspace',
+ url: 'network_call_flow',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/network-call-flow/network-call-flow-view.html');
+ }],
+ controller: viewModelsModuleName + '.NetworkCallFlowViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_DISTRIBUTION, {
+ parent: 'workspace',
+ url: 'distribution',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/distribution/distribution-view.html');
+ }],
+ controller: viewModelsModuleName + '.DistributionViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_COMPOSITION, {
+ url: 'composition/',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.CompositionViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/composition-view.html');
+ }],
+ data:{
+ bodyClass:'composition'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ Utils.Constants.States.WORKSPACE_DEPLOYMENT, {
+ url: 'deployment/',
+ parent: 'workspace',
+ controller: viewModelsModuleName + '.DeploymentViewModel',
+ templateProvider: ['$templateCache', ($templateCache:ng.ITemplateCacheService):string => {
+
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/deployment/deployment-view.html');
+ }],
+ data:{
+ bodyClass:'composition'
+ }
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.details', {
+ url: 'details',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/details/details-view.html');
+ }],
+ controller: viewModelsModuleName + '.DetailsViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.properties', {
+ url: 'properties',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourcePropertiesViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.artifacts', {
+ url: 'artifacts',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/artifacts/artifacts-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourceArtifactsViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.relations', {
+ url: 'relations',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/relations/relations-view.html');
+ }],
+ controller: viewModelsModuleName + '.RelationsViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.relationships', {
+ url: 'relationships',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'resource-relationships/resource-relationships-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourceRelationshipsViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.structure', {
+ url: 'structure',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/structure/structure-view.html');
+ }],
+ controller: viewModelsModuleName + '.StructureViewModel'
+ }
+ );
+ $stateProvider.state(
+ 'workspace.composition.lifecycle', {
+ url: 'lifecycle',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/artifacts/artifacts-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourceArtifactsViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'workspace.composition.api', {
+ url: 'api',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/artifacts/artifacts-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourceArtifactsViewModel'
+ }
+ );
+ $stateProvider.state(
+ 'workspace.composition.deployment', {
+ url: 'deployment',
+ parent: 'workspace.composition',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'workspace/tabs/composition/tabs/artifacts/artifacts-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourceArtifactsViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'edit-resource', {
+ url: '/edit-resource/:id',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'entity-handler/resource-form/resource-form-view.html');
+ }],
+ controller: viewModelsModuleName + '.ResourceFormViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'edit-product', {
+ url: '/edit-product/:id',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'entity-handler/product-form/product-form-view.html');
+ }],
+ controller: viewModelsModuleName + '.ProductFormViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'adminDashboard', {
+ url: '/adminDashboard',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'admin-dashboard/admin-dashboard-view.html');
+ }],
+ controller: viewModelsModuleName + '.AdminDashboardViewModel',
+ permissions: ['ADMIN']
+ }
+ );
+
+ $stateProvider.state(
+ 'onboardVendor', {
+ url: '/onboardVendor',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'onboard-vendor/onboard-vendor-view.html');
+ }],
+ controller: viewModelsModuleName + '.OnboardVendorViewModel'//,
+ //resolve: {
+ // auth: ["$q", "Sdc.Services.UserResourceService", function ($q:any, userResourceService:Sdc.Services.IUserResourceClass) {
+ // let userInfo:Sdc.Services.IUserResource = userResourceService.getLoggedinUser();
+ // if (userInfo) {
+ // return $q.when(userInfo);
+ // } else {
+ // return $q.reject({authenticated: false});
+ // }
+ // }]
+ //}
+ }
+ );
+
+ $stateProvider.state(
+ 'catalog', {
+ url: '/catalog',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'catalog/catalog-view.html');
+ }],
+ controller: viewModelsModuleName + '.CatalogViewModel',
+ resolve: {
+ auth: ["$q", "Sdc.Services.UserResourceService", function ($q:any, userResourceService:Sdc.Services.IUserResourceClass) {
+ let userInfo:Sdc.Services.IUserResource = userResourceService.getLoggedinUser();
+ if (userInfo) {
+ return $q.when(userInfo);
+ } else {
+ return $q.reject({authenticated: false});
+ }
+ }]
+ }
+ }
+ );
+
+ $stateProvider.state(
+ 'distribution', {
+ url: '/distribution',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'distribution/distribution-view.html');
+ }],
+ controller: viewModelsModuleName + '.DistributionViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'support', {
+ url: '/support',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'support/support-view.html');
+ }],
+ controller: viewModelsModuleName + '.SupportViewModel'
+ }
+ );
+
+ $stateProvider.state(
+ 'error-403', {
+ url: '/error-403',
+ templateProvider: ['$templateCache', ($templateCache):string => {
+ return $templateCache.get(viewModelsHtmlBasePath + 'modals/error-modal/error-403-view.html');
+ }],
+ controller: viewModelsModuleName + '.ErrorViewModel'
+ }
+ );
+
+ tooltipsConfigProvider.options({
+
+ side:'bottom',
+ delay: '600',
+ class: 'tooltip-custom',
+ lazy:0,
+ try:0
+
+ });
+
+ }
+ ])
+ .run(['AngularJSBridge', (AngularJSBridge)=>{
+
+ }]);
+ appModule.value('ValidationPattern', /^[\s\w\&_.:-]{1,1024}$/);
+ appModule.value('PropertyNameValidationPattern', /^[a-zA-Z0-9_:-]{1,50}$/);// DE210977
+ appModule.value('TagValidationPattern', /^[\s\w_.-]{1,50}$/);
+ // appModule.value('VendorValidationPattern', /^[^?\\<>:"/|*]{1,25}$/);
+ appModule.value('VendorValidationPattern', /^[\x20-\x21\x23-\x29\x2B-\x2E\x30-\x39\x3B\x3D\x40-\x5B\x5D-\x7B\x7D-\xFF]{1,25}$/);
+ appModule.value('ContactIdValidationPattern', /^[\s\w-]{1,50}$/);
+ appModule.value('UserIdValidationPattern',/^[\s\w-]{1,50}$/);
+ appModule.value('ProjectCodeValidationPattern', /^[\s\w-]{1,50}$/);
+ appModule.value('LabelValidationPattern', /^[\sa-zA-Z0-9+-]{1,25}$/);
+ appModule.value('UrlValidationPattern', /^(https?|ftp):\/\/(((([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([A-Za-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([A-Za-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([A-Za-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([A-Za-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([A-Za-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([A-Za-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/);
+ appModule.value('IntegerValidationPattern', /^(([-+]?\d+)|([-+]?0x[0-9a-fA-F]+))$/);
+ appModule.value('IntegerNoLeadingZeroValidationPattern', /^(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)$/);
+ appModule.value('FloatValidationPattern', /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?$/);
+ appModule.value('NumberValidationPattern', /^((([-+]?\d+)|([-+]?0x[0-9a-fA-F]+))|([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?))$/);
+ appModule.value('KeyValidationPattern', /^[\s\w-]{1,50}$/);
+ appModule.value('CommentValidationPattern', /^[\u0000-\u00BF]*$/);
+ appModule.value('BooleanValidationPattern', /^([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])$/);
+
+
+ appModule.run([
+ '$http',
+ 'Sdc.Services.CacheService',
+ 'Sdc.Services.CookieService',
+ 'Sdc.Services.ConfigurationUiService',
+ 'Sdc.Services.UserResourceService',
+ 'Sdc.Services.CategoryResourceService',
+ 'Sdc.Services.SdcVersionService',
+ '$state',
+ '$rootScope',
+ '$location',
+ 'sdcConfig',
+ 'sdcMenu',
+ 'ModalsHandler',
+ 'Sdc.Services.EcompHeaderService',
+ 'LeftPaletteLoaderService',
+ ($http:ng.IHttpService,
+ cacheService:Services.CacheService,
+ cookieService:Services.CookieService,
+ ConfigurationUi:Services.ConfigurationUiService,
+ UserResourceClass:Services.IUserResourceClass,
+ categoryResourceService:Sdc.Services.ICategoryResourceClass,
+ sdcVersionService:Services.SdcVersionService,
+ $state:ng.ui.IStateService,
+ $rootScope:ng.IRootScopeService,
+ $location: ng.ILocationService,
+ sdcConfig: Models.IAppConfigurtaion,
+ sdcMenu: Models.IAppMenu,
+ ModalsHandler:Utils.ModalsHandler,
+ ecompHeaderService:Sdc.Services.EcompHeaderService,
+ LeftPaletteLoaderService:Services.Components.LeftPaletteLoaderService
+ ):void => {
+
+ //handle cache data - version
+ let initSdcVersion:Function = ():void => {
+
+ let onFailed = (response) => {
+ console.info('onFailed initSdcVersion', response);
+ cacheService.set('version', 'N/A');
+ };
+
+ let onSuccess = (version:any) => {
+ console.log("Version returned from server: " + version);
+ let tmpVerArray = version.version.split(".");
+ let ver = tmpVerArray[0] + "." + tmpVerArray[1] + "." + tmpVerArray[2];
+ cacheService.set('version', ver);
+ };
+
+ sdcVersionService.getVersion().then(onSuccess, onFailed);
+
+ };
+
+ let initEcompMenu:Function = (user):void => {
+ ecompHeaderService.getMenuItems(user.userId).then((data)=> {
+ $rootScope['menuItems'] = data;
+ });
+ };
+
+ let initConfigurationUi:Function = ():void => {
+ ConfigurationUi
+ .getConfigurationUi()
+ .then((configurationUi:any) => {
+ cacheService.set('UIConfiguration', configurationUi);
+ });
+ };
+
+ let initCategories:Function = ():void => {
+ let onError = ():void => {
+ console.log('Failed to init categories');
+ };
+
+ categoryResourceService.getAllCategories({types: 'services'}, (categories:Array<Models.IMainCategory>):void => {
+ cacheService.set('serviceCategories', categories);
+ }, onError);
+
+ categoryResourceService.getAllCategories({types: 'resources'}, (categories:Array<Models.IMainCategory>):void => {
+ cacheService.set('resourceCategories', categories);
+ }, onError);
+
+ categoryResourceService.getAllCategories({types: 'products'}, (categories:Array<Models.IMainCategory>):void => {
+ cacheService.set('productCategories', categories);
+ }, onError);
+ };
+
+ let initBaseUrl:Function = ():void => {
+ let env:string = sdcConfig.environment;
+ let baseUrl:string = $location.absUrl();
+ console.log("baseUrl="+baseUrl);
+
+ if(baseUrl) {
+ sdcConfig.api.baseUrl = baseUrl;
+
+ if(env==='prod'){
+ //let tempUrl = $location.absUrl().split('/sdc1/');
+ var mainUrl = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: '');
+ console.log("mainUrl="+mainUrl);
+ sdcConfig.api.root = mainUrl + sdcConfig.api.root;
+ console.log("sdcConfig.api.root="+sdcConfig.api.root);
+ }
+ }
+ };
+
+ let initLeftPalette:Function = ():void => {
+ LeftPaletteLoaderService.loadLeftPanel();
+ };
+
+ //handle http config
+ $http.defaults.withCredentials = true;
+ $http.defaults.headers.common[cookieService.getUserIdSuffix()] = cookieService.getUserId();
+
+ initBaseUrl();
+ initSdcVersion();
+ initConfigurationUi();
+ Utils.Constants.IMAGE_PATH = sdcConfig.imagesPath;
+ initLeftPalette();
+
+ //handle stateChangeStart
+ let internalDeregisterStateChangeStartWatcher:Function = ():void => {
+ if (deregisterStateChangeStartWatcher) {
+ deregisterStateChangeStartWatcher();
+ deregisterStateChangeStartWatcher = null;
+ }
+ };
+
+ let removeLoader:Function = ():void => {
+ $(".sdc-loading-page .main-loader").addClass("animated fadeOut");
+ $(".sdc-loading-page .caption1").addClass("animated fadeOut");
+ $(".sdc-loading-page .caption2").addClass("animated fadeOut");
+ window.setTimeout(():void=>{
+ $(".sdc-loading-page .main-loader").css("display", "none");
+ $(".sdc-loading-page .caption1").css("display", "none");
+ $(".sdc-loading-page .caption2").css("display", "none");
+ $(".sdc-loading-page").addClass("animated fadeOut");
+ },1000);
+ };
+
+ let onNavigateOut:Function = (toState, toParams):void => {
+ let onOk = ():void => {
+ $state.current.data.unsavedChanges = false;
+ $state.go(toState.name, toParams);
+ };
+
+ let data = sdcMenu.alertMessages.exitWithoutSaving;
+ //open notify to user if changes are not saved
+ ModalsHandler.openAlertModal(data.title, data.message).then(onOk);
+ };
+
+ let onStateChangeStart:Function = (event, toState, toParams, fromState, fromParams):void => {
+ console.info((new Date()).getTime());
+ console.info('$stateChangeStart', toState.name);
+ //set body class
+ $rootScope['bodyClass'] = 'default-class';
+ if(toState.data && toState.data.bodyClass){
+ $rootScope['bodyClass'] = toState.data.bodyClass;
+ }
+
+ // Workaround in case we are entering other state then workspace (user move to catalog)
+ // remove the changeComponentCsarVersion, user should open again the VSP list and select one for update.
+ if (toState.name.indexOf('workspace') === -1) {
+ if (cacheService.contains(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG)){
+ cacheService.remove(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG);
+ }
+ }
+
+ //saving last state to params , for breadcrumbs
+ if (['dashboard', 'catalog', 'onboardVendor'].indexOf(fromState.name) > -1) {
+ toParams.previousState = fromState.name;
+ } else {
+ toParams.previousState = fromParams.previousState;
+ }
+
+ if (toState.name !== 'error-403' && !UserResourceClass.getLoggedinUser()) {
+ internalDeregisterStateChangeStartWatcher();
+ event.preventDefault();
+
+ UserResourceClass.authorize().$promise.then((user:Services.IUserResource) => {
+ if(!doesUserHasAccess(toState, user)){
+ $state.go('error-403');
+ console.info('User has no permissions');
+ registerStateChangeStartWatcher();
+ return;
+ }
+ UserResourceClass.setLoggedinUser(user);
+ cacheService.set('user', user);
+ initCategories();
+ // initEcompMenu(user);
+ setTimeout(function () {
+
+ removeLoader();
+
+ // initCategories();
+ if(UserResourceClass.getLoggedinUser().role === 'ADMIN'){
+ // toState.name = "adminDashboard";
+ $state.go("adminDashboard", toParams);
+ registerStateChangeStartWatcher();
+ return;
+ }
+
+ // After user authorized init categories
+ window.setTimeout(():void=>{
+ //if ($state.current.name==='' || $state.current.name==='preloading') {
+ if ($state.current.name === "welcome" && sdcConfig.openSource) {
+ event.preventDefault();
+ $state.go("dashboard");
+ registerStateChangeStartWatcher();
+ }
+ else if ($state.current.name==='') {
+ $state.go(toState.name, toParams);
+ }
+
+ console.log("------$state.current.name=" + $state.current.name);
+ console.info('-----registerStateChangeStartWatcher authorize $stateChangeStart');
+ registerStateChangeStartWatcher();
+
+ },1000);
+
+ }, 0);
+
+ }, () => {
+ $state.go('error-403');
+
+ console.info('registerStateChangeStartWatcher error-403 $stateChangeStart');
+ registerStateChangeStartWatcher();
+ });
+ }
+ else if(UserResourceClass.getLoggedinUser()){
+ internalDeregisterStateChangeStartWatcher();
+ if(!doesUserHasAccess(toState, UserResourceClass.getLoggedinUser())){
+ event.preventDefault();
+ $state.go('error-403');
+ console.info('User has no permissions');
+ }
+ if(toState.name === "welcome") {
+ $state.go("dashboard");
+ }
+ registerStateChangeStartWatcher();
+ //if form is dirty and not save - notify to user
+ if(fromState.data && fromState.data.unsavedChanges && fromParams.id != toParams.id){
+ event.preventDefault();
+ onNavigateOut(toState, toParams);
+ }
+ }
+
+ };
+
+ let doesUserHasAccess:Function = (toState, user):boolean =>{
+
+ let isUserHasAccess = true;
+ if(toState.permissions && toState.permissions.length > 0) {
+ isUserHasAccess = _.includes(toState.permissions, user.role);
+ }
+ return isUserHasAccess;
+ };
+ let deregisterStateChangeStartWatcher:Function;
+
+ let registerStateChangeStartWatcher:Function = ():void => {
+ internalDeregisterStateChangeStartWatcher();
+ console.info('registerStateChangeStartWatcher $stateChangeStart');
+ deregisterStateChangeStartWatcher = $rootScope.$on('$stateChangeStart', (event, toState, toParams, fromState, fromParams):void => {
+ onStateChangeStart(event, toState, toParams, fromState, fromParams);
+ });
+ };
+
+ registerStateChangeStartWatcher();
+
+ }]);
+
+
+
+}
+
diff --git a/catalog-ui/app/scripts/directives/clicked-outside/clicked-outside-directive.ts b/catalog-ui/app/scripts/directives/clicked-outside/clicked-outside-directive.ts
new file mode 100644
index 0000000000..1b0af4ef99
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/clicked-outside/clicked-outside-directive.ts
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+
+ class ClickedOutsideModel{
+
+ private clickedOutsideContainerSelector: string;
+ private onClickedOutsideGetter: Function;
+ private clickedOutsideEnableGetter: Function;
+
+ constructor(clickedOutsideData: any) {
+ this.clickedOutsideContainerSelector = clickedOutsideData.clickedOutsideContainerSelector;
+ this.onClickedOutsideGetter = clickedOutsideData.onClickedOutsideGetter;
+ this.clickedOutsideEnableGetter = clickedOutsideData.clickedOutsideEnableGetter;
+ }
+
+ public getClickedOutsideContainerSelector = (): string => {
+ return this.clickedOutsideContainerSelector;
+ }
+
+ public getOnClickedOutsideGetter = (): Function => {
+ return this.onClickedOutsideGetter;
+ }
+
+ public getClickedOutsideEnableGetter = (): Function => {
+ return this.clickedOutsideEnableGetter;
+ }
+ }
+
+ export interface IClickedOutsideDirectiveScope extends ng.IScope{}
+
+ export class ClickedOutsideDirective implements ng.IDirective {
+
+ constructor(private $document: JQuery, private $parse: ng.IParseService) {}
+
+ restrict = 'A';
+
+ link = (scope:IClickedOutsideDirectiveScope, element: JQuery, attrs) => {
+
+ let container: HTMLElement;
+ let attrsAfterEval = scope.$eval(attrs.clickedOutside);
+ attrsAfterEval.onClickedOutsideGetter = this.$parse(attrsAfterEval.onClickedOutside);
+ attrsAfterEval.clickedOutsideEnableGetter = this.$parse(attrsAfterEval.clickedOutsideEnable);
+
+ let clickedOutsideModel: ClickedOutsideModel = new ClickedOutsideModel(attrsAfterEval);
+
+
+ let getContainer: Function = ():HTMLElement => {
+ if(!container){
+ let clickedOutsideContainerSelector: string = clickedOutsideModel.getClickedOutsideContainerSelector();
+ if(!angular.isUndefined(clickedOutsideContainerSelector) && clickedOutsideContainerSelector !== ''){
+ container = element.parents(clickedOutsideContainerSelector+':first')[0];
+ if(!container){
+ container = element[0];
+ }
+ }else{
+ container = element[0];
+ }
+ }
+ return container;
+ };
+
+
+ let onClickedOutside = (event: JQueryEventObject) => {
+ let containerDomElement: HTMLElement = getContainer();
+ let targetDomElementJq: JQuery = angular.element(event.target);
+ if(targetDomElementJq.hasClass('tooltip') || targetDomElementJq.parents('.tooltip:first').length){
+ return;
+ }
+ let targetDomElement: HTMLElement = targetDomElementJq[0];
+ if (!containerDomElement.contains(targetDomElement)){
+ scope.$apply(() => {
+ let onClickedOutsideGetter:Function = clickedOutsideModel.getOnClickedOutsideGetter();
+ onClickedOutsideGetter(scope);
+ });
+ }
+ };
+
+ let attachDomEvents: Function = () => {
+ this.$document.on('mousedown', onClickedOutside);
+ };
+
+ let detachDomEvents: Function = () => {
+ this.$document.off('mousedown', onClickedOutside);
+ };
+
+ //
+ scope.$on('$destroy', () => {
+ detachDomEvents();
+ });
+
+
+ scope.$watch(() => {
+ let clickedOutsideEnableGetter: Function = clickedOutsideModel.getClickedOutsideEnableGetter();
+ return clickedOutsideEnableGetter(scope);
+ }, (newValue: boolean) => {
+ if(newValue){
+ attachDomEvents();
+ return;
+ }
+ detachDomEvents();
+ });
+
+
+ }
+
+ public static factory = ($document: JQuery, $parse: ng.IParseService) => {
+ return new ClickedOutsideDirective($document, $parse);
+ }
+ }
+
+ ClickedOutsideDirective.factory.$inject = ['$document', '$parse'];
+}
diff --git a/catalog-ui/app/scripts/directives/custom-validation/custom-validation.ts b/catalog-ui/app/scripts/directives/custom-validation/custom-validation.ts
new file mode 100644
index 0000000000..e2f831ed53
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/custom-validation/custom-validation.ts
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+
+ export interface ICustomValidationScope extends ng.IScope {
+ validationFunc: Function;
+ }
+
+ export class CustomValidationDirective implements ng.IDirective {
+
+ constructor() {}
+
+ require = 'ngModel';
+ restrict = 'A';
+
+ scope = {
+ validationFunc: '='
+ };
+
+ link = (scope:ICustomValidationScope, elem, attrs, ngModel) => {
+
+ ngModel.$validators.customValidation = (modelValue, viewValue) :boolean => {
+ return scope.validationFunc(viewValue);
+ };
+
+ };
+
+ public static factory = ()=> {
+ return new CustomValidationDirective();
+ };
+
+ }
+
+ CustomValidationDirective.factory.$inject = [];
+}
diff --git a/catalog-ui/app/scripts/directives/download-artifact/download-artifact.ts b/catalog-ui/app/scripts/directives/download-artifact/download-artifact.ts
new file mode 100644
index 0000000000..49bf14618c
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/download-artifact/download-artifact.ts
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class DOWNLOAD_CSS_CLASSES {
+ static DOWNLOAD_ICON = "table-download-btn tosca";
+ static LOADER_ICON = "tlv-loader small loader";
+ }
+
+ export interface IDownloadArtifactScope extends ng.IScope {
+ $window:any;
+ artifact: Models.ArtifactModel;
+ component: Models.Components.Component;
+ instance:boolean;
+ download: Function;
+ showLoader:boolean;
+ updateDownloadIcon:Function;
+ }
+
+ export class DownloadArtifactDirective implements ng.IDirective {
+
+ constructor(private $window:any,private cacheService:Services.CacheService, private EventListenerService:Services.EventListenerService, private fileUtils:Sdc.Utils.FileUtils) {}
+
+ scope = {
+ artifact: '=',
+ component: '=',
+ instance:'=',
+ showLoader:'='
+ };
+ restrict = 'EA';
+
+ link = (scope:IDownloadArtifactScope, element:any) => {
+ scope.$window = this.$window;
+
+ element.on("click", function() {
+ scope.download(scope.artifact);
+ });
+
+
+ let initDownloadLoader = ()=>{
+ //if the artifact is in a middle of download progress register form callBack & change icon from download to loader
+ if(scope.showLoader && this.cacheService.get(scope.artifact.uniqueId)){
+ this.EventListenerService.registerObserverCallback(Utils.Constants.EVENTS.DOWNLOAD_ARTIFACT_FINISH_EVENT + scope.artifact.uniqueId, scope.updateDownloadIcon);
+ window.setTimeout(():void => {
+ if(this.cacheService.get(scope.artifact.uniqueId)){
+ element[0].className = DOWNLOAD_CSS_CLASSES.LOADER_ICON;
+ }
+ },1000);
+
+ }
+ };
+
+ let setDownloadedFileLoader = ()=> {
+ if(scope.showLoader){
+ //set in cache service thet the artifact is in download progress
+ this.cacheService.set(scope.artifact.uniqueId,true);
+ initDownloadLoader();
+ }
+ };
+
+ let removeDownloadedFileLoader = ()=> {
+ if (scope.showLoader) {
+ this.cacheService.set(scope.artifact.uniqueId, false);
+ this.EventListenerService.notifyObservers(Utils.Constants.EVENTS.DOWNLOAD_ARTIFACT_FINISH_EVENT + scope.artifact.uniqueId);
+ }
+ };
+
+
+ //replace the loader to download icon
+ scope.updateDownloadIcon = () =>{
+ element[0].className = DOWNLOAD_CSS_CLASSES.DOWNLOAD_ICON;
+ };
+
+
+ initDownloadLoader();
+
+ scope.download = (artifact:Models.ArtifactModel):void => {
+
+ let onFaild = (response):void => {
+ console.info('onFaild', response);
+ removeDownloadedFileLoader();
+ };
+
+ let onSuccess = (data:Models.IFileDownload):void => {
+ downloadFile(data);
+ removeDownloadedFileLoader();
+ };
+
+ setDownloadedFileLoader();
+
+ if(scope.instance){
+ scope.component.downloadInstanceArtifact(artifact.uniqueId).then(onSuccess, onFaild);
+ }else {
+ scope.component.downloadArtifact(artifact.uniqueId).then(onSuccess, onFaild);
+ }
+ };
+
+ let downloadFile = (file:Models.IFileDownload):void => {
+ if (file){
+ let blob = this.fileUtils.base64toBlob(file.base64Contents,'');
+ let fileName = file.artifactName;
+ this.fileUtils.downloadFile(blob, fileName);
+ }
+ };
+
+ element.on('$destroy', ()=>{
+ //remove listener of download event
+ if(scope.artifact && scope.artifact.uniqueId){
+ this.EventListenerService.unRegisterObserver(Utils.Constants.EVENTS.DOWNLOAD_ARTIFACT_FINISH_EVENT + scope.artifact.uniqueId);
+ }
+ });
+
+ };
+
+ public static factory = ($window:any,cacheService:Sdc.Services.CacheService,EventListenerService:Services.EventListenerService, fileUtils:Sdc.Utils.FileUtils)=> {
+ return new DownloadArtifactDirective($window,cacheService,EventListenerService, fileUtils);
+ };
+
+ }
+
+ DownloadArtifactDirective.factory.$inject = ['$window', 'Sdc.Services.CacheService', 'EventListenerService', 'FileUtils'];
+}
diff --git a/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.html b/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.html
new file mode 100644
index 0000000000..e86f9df8b0
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.html
@@ -0,0 +1,73 @@
+<div class="sdc-ecomp-header-wrapper">
+
+ <div class="sdc-ecomp-header">
+
+ <div class="sdc-ecomp-logo-wrapper">
+ <a class="sdc-ecomp-header-title" data-ng-if="clickableLogo==='true'" data-ui-sref="dashboard"><span class="sdc-ecomp-logo"></span>ASDC</a>
+ <a class="sdc-ecomp-header-title" data-ng-if="clickableLogo==='false'"><span class="sdc-ecomp-logo"></span>ASDC</a>
+ <div class="sdc-ecomp-header-version"> v.{{version}}</div>
+ </div>
+
+ <div class="sdc-ecomp-menu">
+
+ <!-- Top level menu -->
+ <ul class="sdc-ecomp-menu-top-level">
+ <li class="sdc-ecomp-menu-top-level-item"
+ ng-repeat="item in megaMenuDataObject"
+ data-ng-if="item.children && item.children.length>0"
+ >
+ <span class="selected" data-ng-if="item.menuId === selectedTopMenu.menuId"></span>
+ <a href data-ng-click="firstMenuLevelClick(item.menuId)">{{item.text}}</a>
+
+ <!-- Second level menu -->
+ <div class="sdc-ecomp-menu-second-level" data-ng-if="item.menuId === selectedTopMenu.menuId" data-ng-mouseleave="subMenuLeaveAction(item.menuId)">
+ <ul>
+ <li class="sdc-ecomp-menu-second-level-item"
+ ng-repeat="subItem in selectedTopMenu.children | orderBy : 'column'"
+ aria-label="{{subItem.text}}"
+ data-ng-class="{'sdc-ecomp-menu-item-hover': menuItemHover===true}"
+ ng-mouseover="subMenuEnterAction(subItem.menuId)"
+ ng-mouseenter="menuItemHover=true"
+ ng-mouseleave="menuItemHover=false">
+
+ <!--<i ng-if="subItem.text=='Favorites'" id="favorite-star" class="icon-star favorites-icon-active"></i>-->
+
+ <a href title="{{subItem.text}}" data-ng-click="memuItemClick(subItem)">{{subItem.text}}</a>
+
+ <!-- Third and Four menu panel -->
+ <ul class="sdc-ecomp-menu-third-level" data-ng-if="subItem.menuId === selectedSubMenu.menuId && (selectedSubMenu.children && selectedSubMenu.children.length>0)">
+ <li class="sdc-ecomp-menu-third-level-item"
+ ng-repeat="thirdItem in selectedSubMenu.children | orderBy : 'column'"
+ aria-label="{{thirdItem.text}}">
+ <a class="sdc-ecomp-menu-third-level-title" href title="{{thirdItem.text}}" data-ng-click="memuItemClick(thirdItem)">{{thirdItem.text}}</a>
+ <span class="sdc-ecomp-menu-four-level-seperator"></span>
+ <ul class="sdc-ecomp-menu-four-level">
+ <li class="sdc-ecomp-menu-four-level-item" data-ng-repeat="fourItem in thirdItem.children | orderBy : 'column'">
+ <a href title="{{fourItem.text}}" data-ng-click="memuItemClick(fourItem)">{{fourItem.text}}</a>
+ </li>
+ </ul>
+ </li>
+ </ul>
+
+ </li>
+ </ul>
+ </div>
+
+ </li>
+
+ </ul>
+
+ </div>
+ <div class="sdc-ecomp-user-wrapper">
+ <span class="sdc-ecomp-user-icon"></span>
+ <div class="sdc-ecomp-user-details">
+ <div class="sdc-ecomp-user-details-name-role">
+ <div sdc-smart-tooltip class="sdc-ecomp-user-details-name" data-ng-bind="user.getName()"></div>
+ <div class="sdc-ecomp-user-details-role" data-ng-bind="user.getRoleToView()"></div>
+ </div>
+ <div class="sdc-ecomp-user-details-last-login" data-ng-show="user.getLastLogin()!==''">Last Login: {{user.getLastLogin() | date: 'MMM dd &nbsp; hh:mm a' : 'UTC'}}&nbsp;UTC</div>
+ </div>
+ </div>
+ </div>
+
+</div>
diff --git a/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.less b/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.less
new file mode 100644
index 0000000000..a6f7e9b5a2
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.less
@@ -0,0 +1,296 @@
+@first-level-color: #067ab4;
+@second-level-color: #f8f8f8;
+
+@first-level-height: 60px;
+@second-level-height: 60px;
+@third-four-level-height: 370px;
+
+@max-item-width: 250px;
+
+.sdc-ecomp-header-wrapper {
+
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 999;
+
+ ul {
+ margin: 0;
+ padding: 0;
+ }
+
+ .sdc-ecomp-header {
+
+ background-color: @first-level-color;
+ height: @first-level-height;
+ line-height: @first-level-height;
+ vertical-align: middle;
+ display: flex;
+ flex-direction: row;
+ padding: 0 20px;
+
+ .sdc-ecomp-menu {
+ flex-grow: 98;
+ }
+
+ }
+
+ /* LEVEL 1 */
+ .sdc-ecomp-menu-top-level {
+ list-style: none;
+
+ .sdc-ecomp-menu-top-level-item:first-child {
+ margin-left: 0;
+ }
+
+ .sdc-ecomp-menu-top-level-item {
+ display: inline-block;
+ margin: 0 20px;
+ position: relative;
+ a {
+ .p_14_m;
+ text-decoration: none;
+ }
+
+ span {
+ &.selected {
+ position: absolute;
+ bottom: 0;
+ width: 0;
+ height: 0;
+ border-left: 15px solid transparent;
+ border-right: 15px solid transparent;
+ border-bottom: 15px solid @second-level-color;
+ }
+ }
+ }
+ }
+
+ /* LEVEL 2 */
+ .sdc-ecomp-menu-second-level {
+ background-color: @second-level-color;
+ box-shadow: 0px 1px 0px 0px rgba(0, 0, 0, 0.33);
+ height: @second-level-height;
+ list-style: none;
+ line-height: @second-level-height;
+ vertical-align: middle;
+ display: flex;
+ flex-direction: row;
+ padding: 0 20px;
+ position: fixed;
+ left: 0;
+ right: 0;
+
+ .sdc-ecomp-menu-second-level-item:first-child {
+ margin-left: 0;
+ }
+
+ .sdc-ecomp-menu-second-level-item {
+ display: inline-block;
+ height: @second-level-height;
+ line-height: @second-level-height;
+
+ &.sdc-ecomp-menu-item-hover {
+ border-bottom: 4px solid #067ab4;
+ }
+
+ a {
+ .m_14_r;
+ text-decoration: none;
+ text-align:center;
+ padding: 0 20px;
+ display: block;
+
+ &:hover {
+ .s_14_r;
+ font-weight:bold;
+ }
+
+ &:active {
+ font-weight: bold;
+ }
+
+ /* fix jump when hovering text */
+ &:after {
+ display:block;
+ content:attr(title);
+ font-weight:bold;
+ height:1px;
+ color:transparent;
+ overflow:hidden;
+ visibility:hidden;
+ }
+ }
+
+ }
+
+ }
+
+ /* LEVEL 3 */
+ ul.sdc-ecomp-menu-third-level {
+ background-color: #ffffff;
+ box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1);
+ min-height: @third-four-level-height;
+ list-style: none;
+
+ display: flex;
+ flex-direction: row;
+ /*flex-wrap: wrap;*/
+ position: fixed;
+ top: @first-level-height + @second-level-height;
+ left: 0;
+ right: 0;
+ padding: 0 40px;
+
+ li.sdc-ecomp-menu-third-level-item {
+ height: 40px;
+ line-height: 40px;
+ position: relative;
+ /*width: @max-item-width;*/
+ max-width: @max-item-width;
+
+ .sdc-ecomp-menu-third-level-title {
+ .m_14_m;
+ text-decoration: none;
+ text-align: left;
+ display: block;
+ padding: 0 20px;
+ line-height: 20px;
+ margin-top: 20px;
+ font-weight: bold;
+ cursor: pointer;
+ }
+
+ .sdc-ecomp-menu-four-level-seperator {
+ position: absolute;
+ border-right: solid 1px #e5e5e5;
+ height: @third-four-level-height - 20;
+ top: 10px;
+ }
+ }
+
+ li.sdc-ecomp-menu-third-level-item:first-child {
+ .sdc-ecomp-menu-four-level-seperator {
+ border: none;
+ }
+ }
+ }
+
+ /* LEVEL 4 */
+ ul.sdc-ecomp-menu-four-level {
+ display: flex;
+ flex-direction: column;
+ margin-top: 10px;
+
+ li.sdc-ecomp-menu-four-level-item {
+ display: block;
+ /*width: @max-item-width;*/
+ max-width: @max-item-width;
+ line-height: 20px;
+
+ a {
+ .m_14_r;
+ text-decoration: none;
+ text-align: left;
+ display: block;
+
+ &:hover {
+ .s_14_r;
+ font-weight: bold;
+ }
+ }
+ }
+ }
+
+}
+
+.sdc-ecomp-logo-wrapper {
+ flex-grow: 1;
+ .p_18_m;
+ white-space: nowrap;
+ min-width: 210px;
+ text-align: left;
+ position: relative;
+
+ .sdc-ecomp-logo {
+ background-image: url("images/att_logo_white.png");
+ background-repeat: no-repeat;
+ display: inline-block;
+ vertical-align: middle;
+ width: 55px;
+ height: 55px;
+ }
+
+ .sdc-ecomp-header-version {
+ .c_16;
+ .opacity(0.8);
+ position: absolute;
+ top: 34px;
+ line-height: 20px;
+ left: 56px;
+ }
+
+ a.sdc-ecomp-header-title {
+ .p_24;
+ text-decoration: none;
+ }
+}
+
+
+.sdc-ecomp-user-wrapper {
+
+ flex-grow: 1;
+ .p_14_m;
+ white-space: nowrap;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ height: @first-level-height;
+
+ .sdc-ecomp-user-icon {
+ margin-right: 20px;
+ .tlv-sprite;
+ .tlv-sprite.user;
+ }
+
+ .sdc-ecomp-user-details {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .sdc-ecomp-user-details-name-role {
+ line-height: 20px;
+
+ .sdc-ecomp-user-details-name {
+ max-width: 160px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ vertical-align: bottom;
+
+ .bold;
+ display: inline-block;
+ }
+
+ .sdc-ecomp-user-details-role {
+ .bold;
+ display: inline-block;
+ margin-left: 6px;
+
+ &:before {
+ content: '';
+ margin-right: 8px;
+ border-left: 1px solid @color_m;
+ }
+ }
+ }
+
+ .sdc-ecomp-user-details-last-login {
+ .font-type._3;
+ display: block;
+ line-height: 20px;
+ height: 20px;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.ts b/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.ts
new file mode 100644
index 0000000000..7102c810ba
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/ecomp-header/ecomp-header.ts
@@ -0,0 +1,235 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class MenuItem {
+ menuId:number;
+ column:number;
+ text:string;
+ parentMenuId:number;
+ url:string;
+ children:Array<MenuItem>
+ }
+
+ export interface IEcompHeaderDirectiveScope extends ng.IScope {
+ menuData:Array<MenuItem>;
+ version:string;
+ clickableLogo:string;
+ contactUsUrl:string;
+ getAccessUrl:string;
+ megaMenuDataObjectTemp:Array<any>;
+ megaMenuDataObject:Array<any>;
+
+ selectedTopMenu:MenuItem;
+ selectedSubMenu:MenuItem;
+
+ firstMenuLevelClick:Function;
+ subMenuEnterAction:Function;
+ subMenuLeaveAction:Function;
+
+ memuItemClick:Function;
+ user: Models.IUser;
+ }
+
+ export class EcompHeaderDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $http:ng.IHttpService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private UserResourceClass:Services.IUserResourceClass) {
+
+ }
+
+ scope = {
+ menuData: '=',
+ version: '@',
+ clickableLogo: '@?'
+ };
+
+ public replace = true;
+ public restrict = 'E';
+ public controller = EcompHeaderController;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/ecomp-header/ecomp-header.html');
+ };
+
+ link = ($scope:IEcompHeaderDirectiveScope, $elem:JQuery, attr:any) => {
+
+ if (!$scope.clickableLogo){
+ $scope.clickableLogo="true";
+ }
+
+ let findMenuItemById = (menuId):MenuItem => {
+ let selectedMenuItem:MenuItem = _.find($scope.menuData, (item:MenuItem)=>{
+ if (item.menuId === menuId){
+ return item;
+ }
+ });
+ return selectedMenuItem;
+ };
+
+ let initUser = ():void => {
+ let defaultUserId:string;
+ let user:Services.IUserResource = this.UserResourceClass.getLoggedinUser();
+ if (!user) {
+ defaultUserId = this.$http.defaults.headers.common[this.sdcConfig.cookie.userIdSuffix];
+ user = this.UserResourceClass.get({id: defaultUserId}, ():void => {
+ $scope.user = new Models.User(user);
+ });
+ } else {
+ $scope.user = new Models.User(user);
+ }
+ };
+
+ $scope.firstMenuLevelClick = (menuId:number):void => {
+ let selectedMenuItem:MenuItem = _.find($scope.megaMenuDataObjectTemp, (item:MenuItem)=>{
+ if (item.menuId === menuId){
+ return item;
+ }
+ });
+ if (selectedMenuItem) {
+ $scope.selectedTopMenu = selectedMenuItem;
+ //console.log("Selected menu item: " + selectedMenuItem.text);
+ }
+ };
+
+ $scope.subMenuEnterAction = (menuId:number):void => {
+ $scope.selectedSubMenu = findMenuItemById(menuId);
+ };
+
+ $scope.subMenuLeaveAction = (menuId:number):void => {
+ $scope.selectedTopMenu = undefined;
+ };
+
+ $scope.memuItemClick = (menuItem:MenuItem):void => {
+ if (menuItem.url){
+ window.location.href=menuItem.url;
+ } else {
+ console.log("Menu item: " + menuItem.text + " does not have defined URL!");
+ }
+ };
+
+ initUser();
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService,
+ $http:ng.IHttpService,
+ sdcConfig:Models.IAppConfigurtaion,
+ UserResourceClass:Services.IUserResourceClass)=> {
+ return new EcompHeaderDirective($templateCache, $http, sdcConfig, UserResourceClass);
+ };
+
+ }
+
+ export class EcompHeaderController {
+
+ messages:any;
+ getAttachId:Function;
+ render:any;
+ reRender:Function;
+ register:Function;
+ deregister:Function;
+ head:any;
+
+ static '$inject' = [
+ '$element',
+ '$scope',
+ '$attrs',
+ '$animate'
+ ];
+
+ constructor(private $element:JQuery,
+ private $scope:IEcompHeaderDirectiveScope,
+ private $attrs:ng.IAttributes,
+ private $animate:any) {
+
+ this.$scope = $scope;
+
+ this.$scope.$watch('menuData', (newVal, oldVal) => {
+ if (newVal){
+ this.init();
+ }
+ });
+
+ }
+
+ init = ():void => {
+
+ this.$scope.contactUsUrl = "https://wiki.web.att.com/display/EcompPortal/ECOMP+Portal+Home";
+ this.$scope.getAccessUrl = "http://ecomp-tlv-dev2.uccentral.att.com:8080/ecompportal/get_access";
+
+ let unflatten = ( array, parent?, tree? ) => {
+ tree = typeof tree !== 'undefined' ? tree : [];
+ parent = typeof parent !== 'undefined' ? parent : { menuId: null };
+ let children = _.filter( array, function(child){ return child["parentMenuId"] == parent.menuId; });
+ if( !_.isEmpty( children ) ){
+ if( parent.menuId === null ){
+ tree = children;
+ }else{
+ parent['children'] = children
+ }
+ _.each( children, function( child ){ unflatten( array, child ) } );
+ }
+ return tree;
+ };
+
+ let menuStructureConvert = (menuItems) => {
+ console.log(menuItems);
+ this.$scope.megaMenuDataObjectTemp = [
+ {
+ menuId: 1001,
+ text: "ECOMP",
+ children: menuItems
+ },
+ {
+ menuId: 1002,
+ text: "Help",
+ children: [
+ {
+ text:"Contact Us",
+ url: this.$scope.contactUsUrl
+ }]
+ }
+ ];
+
+ /*{
+ text:"Get Access",
+ url: this.$scope.getAccessUrl
+ }*/
+ return this.$scope.megaMenuDataObjectTemp;
+ };
+
+ let a = unflatten(this.$scope.menuData);
+ this.$scope.megaMenuDataObject = menuStructureConvert(a);
+ //console.log(this.$scope.megaMenuDataObject);
+ };
+ }
+
+ EcompHeaderDirective.factory.$inject = ['$templateCache', '$http', 'sdcConfig', 'Sdc.Services.UserResourceService'];
+
+}
+
+
+
+
diff --git a/catalog-ui/app/scripts/directives/edit-name-popover/edit-module-name-popover.html b/catalog-ui/app/scripts/directives/edit-name-popover/edit-module-name-popover.html
new file mode 100644
index 0000000000..d90c52d9a6
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/edit-name-popover/edit-module-name-popover.html
@@ -0,0 +1,31 @@
+<div>
+ <form name="popoverForm" class="w-sdc-form" data-tests-id="popover-form">
+ <span class="tlv-sprite tlv-x-btn close-popover-btn" data-tests-id="popover-x-button" ng-click="closePopover()"></span>
+ <div class="form-group">
+ <span class="popover-label" data-tests-id="popover-vfinstance-name" tooltips tooltip-content="{{module.vfInstanceName}}">{{module.vfInstanceName}}</span>
+ <div class="popover-input">
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(popoverForm.heatName)}">
+ <input type="text"
+ data-ng-model="module.heatName"
+ data-ng-maxlength="50"
+ maxlength="50"
+ name="heatName"
+ placeholder="Enter Name"
+ autocomplete="off"
+ data-ng-init="onInit()"
+ data-ng-pattern="heatNameValidationPattern"
+ data-tests-id="popover-heat-name"
+ class="form-control"
+ />
+ </div>
+ <div class="input-error" data-ng-show="validateField(popoverForm.heatName)">
+ <span ng-show="popoverForm.heatName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="popoverForm.heatName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <span class="popover-label" data-tests-id="popover-module-name" tooltips tooltip-content="{{module.moduleName}}">{{module.moduleName}}</span>
+ <button class="tlv-btn grey popover-btn" data-tests-id="popover-close-button" data-ng-click="closePopover()">Cancel</button>
+ <button class="tlv-btn blue popover-btn" data-tests-id="popover-save-button" data-ng-class="{'disabled': (validateField(popoverForm.heatName) || popoverForm.heatName.$viewValue === originalName)}" data-ng-click="updateHeatName()">Save</button>
+ </div>
+ </form>
+</div>
diff --git a/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-directive.ts b/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-directive.ts
new file mode 100644
index 0000000000..a033df054b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-directive.ts
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface IEditNamePopoverDirectiveScope extends ng.IScope {
+ isOpen: boolean;
+ templateUrl: string;
+ module: any;
+ direction: string;
+ header: string;
+ heatNameValidationPattern:RegExp;
+ originalName:string;
+ onSave:any;
+
+ closePopover(isCancel:boolean):void;
+ validateField(field:any, originalName:string):boolean;
+ updateHeatName(heatName:string):void;
+ onInit():void;
+ }
+
+ export class EditNamePopoverDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService, private ValidationPattern:RegExp) {
+ }
+
+ scope = {
+ direction: "@?",
+ module: "=",
+ header: "@?",
+ onSave: "&"
+ };
+
+ link = (scope:IEditNamePopoverDirectiveScope) => {
+ if(!scope.direction) {
+ scope.direction = 'top';
+ }
+
+ scope.originalName = '';
+ scope.templateUrl = "/app/scripts/directives/edit-name-popover/edit-module-name-popover.html";
+ scope.isOpen = false;
+
+ scope.closePopover = (isCancel:boolean = true) => {
+ scope.isOpen = !scope.isOpen;
+
+ if(isCancel) {
+ scope.module.heatName = scope.originalName;
+ }
+ };
+
+ scope.onInit = () => {
+ scope.originalName = scope.module.heatName;
+ };
+
+ scope.validateField = (field:any):boolean => {
+ return !!(field && field.$dirty && field.$invalid);
+ };
+
+ scope.heatNameValidationPattern = this.ValidationPattern;
+
+ scope.updateHeatName = () => {
+ scope.closePopover(false);
+ scope.onSave();
+ }
+
+ };
+
+ replace = true;
+ restrict = 'E';
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/edit-name-popover/edit-name-popover-view.html');
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, ValidationPattern:RegExp)=> {
+ return new EditNamePopoverDirective($templateCache, ValidationPattern);
+ }
+ }
+
+ EditNamePopoverDirective.factory.$inject = ['$templateCache', 'ValidationPattern'];
+}
diff --git a/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-view.html b/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-view.html
new file mode 100644
index 0000000000..17beead6b3
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover-view.html
@@ -0,0 +1 @@
+<div uib-popover-template="templateUrl" popover-title="{{header}}" popover-placement="{{direction}}" popover-is-open="isOpen" popover-append-to-body="true"></div>
diff --git a/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover.less b/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover.less
new file mode 100644
index 0000000000..3d76a352ce
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/edit-name-popover/edit-name-popover.less
@@ -0,0 +1,71 @@
+.popover {
+ max-width: none;
+ width: 310px;
+ left: initial !important;
+ right: 10px;
+ z-index: 100;
+ border-radius: 0;
+
+ .top > .arrow {
+ bottom: -8px !important;
+ }
+
+ .popover-title {
+ background-color: transparent;
+ border-bottom: 2px solid #40b7e4;
+ margin-left: 20px;
+ margin-right: 20px;
+ padding: 8px 14px 8px 0px;
+ font-family: @font-omnes-medium;
+ font-weight: bold;
+ }
+
+ .arrow {
+ left: 95% !important;
+ border-width: 7px;
+ bottom: -8px !important;
+ }
+
+ .popover-content {
+ width: inherit;
+ padding: 9px 20px;
+ }
+
+ .form-group {
+ margin-top: 9px;
+ }
+
+ .popover-btn {
+ float:right;
+ margin-left: 10px;
+ margin-bottom: 20px;
+ }
+
+ .close-popover-btn {
+ position: absolute;
+ top: 11px;
+ right: 25px;
+ }
+
+ .close-popover-btn:hover {
+ cursor: pointer;
+ }
+
+ .popover-input {
+ height: 47px;
+ margin-bottom:5px;
+ }
+
+ .popover-label {
+ text-overflow: ellipsis;
+ display: block;
+ white-space: nowrap;
+ margin-bottom: 10px;
+ font-family: @font-omnes-medium;
+ color: @main_color_l;
+ }
+
+ .w-sdc-form .i-sdc-form-item {
+ margin-bottom: 0px;
+ }
+}
diff --git a/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.html b/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.html
new file mode 100644
index 0000000000..daf2a89ac2
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.html
@@ -0,0 +1,13 @@
+<label class="tlv-checkbox" ng-class="{'disabled' : disabled}">
+ <input id="{{elemId}}"
+ name="{{elemId}}"
+ class="tlv-checkbox-i"
+ type="checkbox"
+ checked=""
+ ng-disabled="disabled"
+ checklist-model="sdcChecklistModel"
+ checklist-value="sdcChecklistValue"
+ />
+
+ <span sdc-smart-tooltip class="tlv-checkbox-label" data-tests-id="{{elemId}}">{{text}}</span>
+</label>
diff --git a/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.less b/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.less
new file mode 100644
index 0000000000..0747a680a9
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.less
@@ -0,0 +1,35 @@
+label.tlv-checkbox {
+ font-weight: normal;
+}
+
+/*
+input[type="checkbox"] {
+ display:none;
+}
+
+input[type="checkbox"] + label span {
+ margin-right: 6px;
+ vertical-align: text-bottom;
+ .sprite-new;
+ .checkbox_unchecked;
+ cursor:pointer;
+}
+
+input[type="checkbox"]:checked + label span {
+ vertical-align: text-bottom;
+ .sprite-new;
+ .checkbox_checked;
+}
+
+input[type="checkbox"]:focus + label span {
+ vertical-align: text-bottom;
+ .sprite-new;
+ .checkbox_focus;
+}
+
+input[type="checkbox"][disabled] + label{
+ vertical-align: text-bottom;
+ .sprite-new;
+ .checkbox_disabled;
+}
+*/
diff --git a/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.ts b/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.ts
new file mode 100644
index 0000000000..c45a9d92e1
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/elements/checkbox/checkbox.ts
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ICheckboxElementScope extends ng.IScope {
+ elemId: string;
+ text: string;
+ sdcChecklistModel: any;
+ sdcChecklistValue: string;
+ disabled:boolean;
+ }
+
+ export class CheckboxElementDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $filter:ng.IFilterService) {
+ }
+
+ public replace = true;
+ public restrict = 'E';
+ public transclude = false;
+
+ scope = {
+ elemId: '@',
+ text: '@',
+ disabled: '=',
+ sdcChecklistModel: '=',
+ sdcChecklistValue: '='
+ };
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/elements/checkbox/checkbox.html');
+ };
+
+ public link = (scope:ICheckboxElementScope, $elem:ng.IAugmentedJQuery, $attrs:angular.IAttributes) => {
+ //$elem.removeAttr("id")
+ //console.log(scope.sdcChecklistValue);
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, $filter:ng.IFilterService)=> {
+ return new CheckboxElementDirective($templateCache, $filter);
+ };
+
+ }
+
+ CheckboxElementDirective.factory.$inject = ['$templateCache', '$filter'];
+}
diff --git a/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.html b/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.html
new file mode 100644
index 0000000000..b31fae5d73
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.html
@@ -0,0 +1,5 @@
+<div class="tlv-radio">
+ <input name="{{elemName}}" id="{{elemId}}" class="tlv-radio-i" type="radio" ng-model="sdcModel" ng-click="onValueChange()"
+ ng-disabled="disabled" value="{{value}}" />
+ <label for="{{elemId}}" sdc-smart-tooltip class="tlv-radio-label">{{text}}</label>
+</div>
diff --git a/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.less b/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.less
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.less
diff --git a/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.ts b/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.ts
new file mode 100644
index 0000000000..9fe58d8f8b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/elements/radiobutton/radiobutton.ts
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ import INgModelController = angular.INgModelController;
+ 'use strict';
+
+ export interface IRadiobuttonElementScope extends ng.IScope {
+ elemId: string;
+ elemName: string;
+ text: string;
+ sdcModel: any;
+ value: any;
+ disabled: boolean;
+ onValueChange:Function;
+ }
+
+ export class RadiobuttonElementDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $filter:ng.IFilterService) {
+ }
+
+ public replace = true;
+ public restrict = 'E';
+ public transclude = false;
+
+ scope = {
+ elemId: '@',
+ elemName: '@',
+ text: '@',
+ sdcModel: '=',
+ value: '@',
+ disabled: '=',
+ onValueChange: '&'
+ };
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/elements/radiobutton/radiobutton.html');
+ };
+
+ public link = (scope:IRadiobuttonElementScope, $elem:ng.IAugmentedJQuery, $attrs:angular.IAttributes) => {
+ //$elem.removeAttr("id")
+ //console.log(scope.sdcChecklistValue);
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, $filter:ng.IFilterService)=> {
+ return new RadiobuttonElementDirective($templateCache, $filter);
+ };
+
+ }
+
+ RadiobuttonElementDirective.factory.$inject = ['$templateCache', '$filter'];
+}
diff --git a/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.html b/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.html
new file mode 100644
index 0000000000..31fa06adda
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.html
@@ -0,0 +1,7 @@
+{{actualText}}
+
+<span class="ellipsis-directive-more-less"
+ data-ng-click="collapsed = !collapsed; toggleText()"
+ data-ng-hide="ellipsis.length <= maxChars">
+ {{collapsed ? "More" : "Less"}}
+</span>
diff --git a/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.less b/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.less
new file mode 100644
index 0000000000..d8dfdbb73b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.less
@@ -0,0 +1,10 @@
+.ellipsis-directive-more-less {
+ .a_9;
+ .bold;
+ .hand;
+ float: right;
+ margin-right: 17px;
+ line-height: 23px;
+ text-decoration: underline;
+ text-align: left;
+}
diff --git a/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.ts b/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.ts
new file mode 100644
index 0000000000..a5ccf248e0
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/ellipsis/ellipsis-directive.ts
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface IEllipsisScope extends ng.IScope {
+ ellipsis: string;
+ maxChars: number;
+ toggleText(): void;
+ collapsed: boolean;
+ actualText: string;
+
+ }
+
+ export class EllipsisDirective implements ng.IDirective {
+
+ constructor(private $templateCache: ng.ITemplateCacheService) {}
+
+ scope = {
+ ellipsis: '=',
+ moreClass: '@',
+ maxChars: '='
+ };
+
+ replace = false;
+ restrict = 'A';
+ template = (): string => {
+ return this.$templateCache.get('/app/scripts/directives/ellipsis/ellipsis-directive.html');
+ };
+
+ link = (scope:IEllipsisScope, $elem:any) => {
+
+
+ scope.collapsed = true;
+
+ scope.toggleText = (): void => {
+ if(scope.ellipsis && scope.collapsed) {
+ scope.actualText = scope.ellipsis.substr(0, scope.maxChars);
+ scope.actualText += scope.ellipsis.length > scope.maxChars ? '...' : '';
+ }
+ else
+ {
+ scope.actualText = scope.ellipsis;
+ }
+ };
+
+ scope.$watch("ellipsis", function(){
+ scope.collapsed = true;
+ scope.toggleText();
+ });
+
+
+
+ };
+
+ public static factory = ($templateCache: ng.ITemplateCacheService)=> {
+ return new EllipsisDirective($templateCache);
+ };
+
+ }
+
+ EllipsisDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/events/on-last-repeat/on-last-repeat.ts b/catalog-ui/app/scripts/directives/events/on-last-repeat/on-last-repeat.ts
new file mode 100644
index 0000000000..0fb682d202
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/events/on-last-repeat/on-last-repeat.ts
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ /**
+ * Usage:
+ * In data-ng-repeat html: <ol ng-repeat="record in records" on-last-repeat>
+ * In the controller, catch the last repeat:
+ * $scope.$on('onRepeatLast', function(scope, element, attrs){
+ * //work your magic
+ * });
+ */
+ export interface IOnLastRepeatDirectiveScope extends ng.IScope {
+ $last:any;
+ }
+
+ export class OnLastRepeatDirective implements ng.IDirective {
+
+ constructor() {}
+
+ scope = {};
+
+ restrict = 'AE';
+ replace = true;
+
+ link = (scope:IOnLastRepeatDirectiveScope, element:any, attrs:any) => {
+ let s:any = scope.$parent; // repeat scope
+ if (s.$last) {
+ setTimeout(function(){
+ s.$emit('onRepeatLast', element, attrs);
+ }, 1);
+ }
+ };
+
+ public static factory = ()=> {
+ return new OnLastRepeatDirective();
+ };
+
+ }
+
+ OnLastRepeatDirective.factory.$inject = [];
+}
diff --git a/catalog-ui/app/scripts/directives/file-opener/file-opener.html b/catalog-ui/app/scripts/directives/file-opener/file-opener.html
new file mode 100644
index 0000000000..38f82554e9
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/file-opener/file-opener.html
@@ -0,0 +1,3 @@
+<div>
+<input class="i-sdc-dashboard-item-upload-input" type="file" data-tests-id="file-{{testsId}}" data-ng-model="importFile" base-sixty-four-input data-ng-change="onFileSelect()" accept="{{getExtensionsWithDot()}}"/>
+</div>
diff --git a/catalog-ui/app/scripts/directives/file-opener/file-opener.ts b/catalog-ui/app/scripts/directives/file-opener/file-opener.ts
new file mode 100644
index 0000000000..b7e3e1804c
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/file-opener/file-opener.ts
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface IFileOpenerScope extends ng.IScope {
+ importFile:any;
+ testsId:any;
+ extensions:string;
+
+ onFileSelect():void;
+ onFileUpload(file:any):void;
+ getExtensionsWithDot():string;
+ }
+
+ export class FileOpenerDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $compile:ng.ICompileService) {
+ }
+
+ scope = {
+ onFileUpload: '&',
+ testsId: '@',
+ extensions: '@'
+ };
+
+ restrict = 'AE';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/file-opener/file-opener.html');
+ };
+
+ link = (scope:IFileOpenerScope, element:any) => {
+
+ scope.onFileSelect = () => {
+ scope.onFileUpload({file: scope.importFile});
+ element.html(this.$templateCache.get('/app/scripts/directives/file-opener/file-opener.html'));
+ this.$compile(element.contents())(scope);
+ };
+
+ scope.getExtensionsWithDot = ():string => {
+ let ret = [];
+ _.each(scope.extensions.split(','), function(item){
+ ret.push("." + item.toString());
+ });
+ return ret.join(",");
+ };
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, $compile:ng.ICompileService)=> {
+ return new FileOpenerDirective($templateCache, $compile);
+ };
+
+ }
+
+ FileOpenerDirective.factory.$inject = ['$templateCache', '$compile'];
+}
diff --git a/catalog-ui/app/scripts/directives/file-type/file-type.ts b/catalog-ui/app/scripts/directives/file-type/file-type.ts
new file mode 100644
index 0000000000..e7dee17960
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/file-type/file-type.ts
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class FileTypeDirective implements ng.IDirective {
+
+ constructor() {}
+
+ require = 'ngModel';
+
+ link = (scope, elem, attrs, ngModel) => {
+
+ let typesToApprove = "";
+
+ attrs.$observe('fileType', (val:string) => {
+ typesToApprove = val;
+ validate(ngModel.$viewValue);
+ });
+
+ let validate: Function = function (value) {
+ let fileName:string = elem.val(), valid:boolean = true;
+
+ if (fileName && value && typesToApprove) {
+ let extension:string = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
+ valid = typesToApprove.split(',').indexOf(extension) > -1;
+ }
+
+ ngModel.$setValidity('filetype', valid);
+ if(!value) {
+ ngModel.$setPristine();
+ }
+ return value;
+ };
+
+ //For DOM -> model validation
+ ngModel.$parsers.unshift(validate);
+
+ };
+
+ public static factory = ()=> {
+ return new FileTypeDirective();
+ };
+
+ }
+
+ FileTypeDirective.factory.$inject = [];
+}
diff --git a/catalog-ui/app/scripts/directives/file-upload/file-upload.html b/catalog-ui/app/scripts/directives/file-upload/file-upload.html
new file mode 100644
index 0000000000..7cbc8d25f3
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/file-upload/file-upload.html
@@ -0,0 +1,22 @@
+<div class="i-sdc-form-item i-sdc-form-file-upload">
+ <span class="i-sdc-form-file-name" data-tests-id="filename">{{(fileModel && fileModel.filename) || defaultText}}</span>
+ <div class="i-sdc-form-file-upload-x-btn" ng-click="cancel()" data-ng-show="fileModel.filename && fileModel.filename!=='' && elementDisabled!=='true'"></div>
+ <label class="i-sdc-form-file-upload-label">
+ <input
+ type="file"
+ name="{{elementName}}"
+ ng-model="myFileModel"
+ base-sixty-four-input
+ accept="{{getExtensionsWithDot()}}"
+ file-type="{{extensions}}"
+ data-ng-change="onFileChange()"
+ onchange="angular.element(this).scope().setEmptyError(this)"
+ onclick="angular.element(this).scope().onFileClick(this)"
+ data-ng-required="{{elementRequired}}"
+ data-ng-disabled="elementDisabled==='true'"
+ data-tests-id="browseButton"
+ maxsize="10240"
+ />
+ <div class="file-upload-browse-btn" data-ng-class="{'disabled':elementDisabled==='true'}">Browse</div>
+ </label>
+</div>
diff --git a/catalog-ui/app/scripts/directives/file-upload/file-upload.less b/catalog-ui/app/scripts/directives/file-upload/file-upload.less
new file mode 100644
index 0000000000..1c4b010853
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/file-upload/file-upload.less
@@ -0,0 +1,75 @@
+.i-sdc-form-file-upload {
+
+ display: flex;
+ margin-top: 0;
+ width: 100%;
+ .p_1;
+ .bg_c;
+ .border-radius(2px);
+ border: solid 1px @border_color_f;
+ height: 30px;
+
+ input[type="file"] {
+ cursor: inherit;
+ display: block;
+ filter: alpha(opacity=0);
+ width: 100px;
+ height: 30px;
+ opacity: 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 0;
+ }
+
+ .i-sdc-form-file-name{
+ flex-grow: 999;
+ text-align: left;
+ padding: 3px 10px;
+ opacity: 0.6;
+ width: 80%;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+
+ }
+
+ .i-sdc-form-file-upload-x-btn{
+ flex-grow: 1;
+ .sprite;
+ .sprite.small-x-btn-black;
+ cursor: pointer;
+ top: 10px;
+ right: 9px;
+ width: 10px;
+ position: relative;
+ }
+ .i-sdc-form-file-upload-label {
+ float: right;
+ width: 100px;
+ height: 100%;
+ .bg_n;
+ .b_9;
+
+ .file-upload-browse-btn {
+ .noselect;
+ padding: 4px 6px;
+ cursor: pointer;
+ z-index: 999;
+ position: absolute;
+ width: 100px;
+ height: 28px;
+ text-align: center;
+
+ &.disabled {
+ cursor: default;
+ }
+ }
+ }
+
+ &.error {
+ border-color: #da1f3d;
+ outline: none;
+ box-sizing: border-box;
+ }
+}
diff --git a/catalog-ui/app/scripts/directives/file-upload/file-upload.ts b/catalog-ui/app/scripts/directives/file-upload/file-upload.ts
new file mode 100644
index 0000000000..16db3e7e21
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/file-upload/file-upload.ts
@@ -0,0 +1,134 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 1/27/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class FileUploadModel {
+ filetype: string;
+ filename: string;
+ filesize: number;
+ base64: string;
+ }
+
+ export interface IFileUploadScope extends ng.IScope {
+ fileModel: FileUploadModel;
+ formElement:ng.IFormController;
+ extensions: string;
+ elementDisabled: string;
+ elementName: string;
+ elementRequired: string;
+ myFileModel: any; // From the ng bind to <input type=file
+ defaultText: string;
+ onFileChangedInDirective:Function;
+
+ getExtensionsWithDot():string;
+ onFileChange():void
+ onFileClick(element:any):void;
+ setEmptyError(element):void;
+ validateField(field:any):boolean;
+ cancel():void;
+ }
+
+
+ export class FileUploadDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService, private sdcConfig:Models.IAppConfigurtaion) {
+ }
+
+ scope = {
+ fileModel: '=',
+ formElement: '=',
+ extensions: '@',
+ elementDisabled: '@',
+ elementName: '@',
+ elementRequired: '@',
+ onFileChangedInDirective: '=?',
+ defaultText: '=',
+ };
+
+ restrict = 'E';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/file-upload/file-upload.html');
+ };
+
+ link = (scope:IFileUploadScope, element:any, $attr:any) => {
+
+ // In case the browse has filename, set it valid.
+ // When editing artifact the file is not sent again, so if we have filename I do not want to show error.
+ if (scope.fileModel && scope.fileModel.filename && scope.fileModel.filename!==''){
+ scope.formElement[scope.elementName].$setValidity('required', true);
+ }
+
+ scope.getExtensionsWithDot = ():string => {
+ let ret = [];
+ if(scope.extensions) {
+ _.each(scope.extensions.split(','), function (item) {
+ ret.push("." + item.toString());
+ });
+ }
+ return ret.join(",");
+ };
+
+ scope.onFileChange = ():void => {
+ if (scope.onFileChangedInDirective) {
+ scope.onFileChangedInDirective();
+ }
+ if (scope.myFileModel) {
+ scope.fileModel = scope.myFileModel;
+ scope.formElement[scope.elementName].$setValidity('required', true);
+ }
+ };
+
+ scope.setEmptyError = (element):void => {
+ if(element.files[0].size){
+ scope.formElement[scope.elementName].$setValidity('emptyFile', true);
+ }else{
+ scope.formElement[scope.elementName].$setValidity('emptyFile', false);
+ scope.fileModel = undefined;
+ }
+
+ };
+
+ // Workaround, in case user select a file then cancel (X) then select the file again, the event onChange is not fired.
+ // This is a workaround to fix this issue.
+ scope.onFileClick = (element:any):void => {
+ element.value = null;
+ };
+
+ scope.cancel = ():void => {
+ scope.fileModel.filename = '';
+ scope.formElement[scope.elementName].$pristine;
+ scope.formElement[scope.elementName].$setValidity('required', false);
+ }
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, sdcConfig:Models.IAppConfigurtaion)=> {
+ return new FileUploadDirective($templateCache, sdcConfig);
+ };
+
+ }
+
+ FileUploadDirective.factory.$inject = ['$templateCache', 'sdcConfig'];
+}
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/common/common-graph-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/common/common-graph-utils.ts
new file mode 100644
index 0000000000..e01e455e93
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/common/common-graph-utils.ts
@@ -0,0 +1,361 @@
+/**
+ * Created by obarda on 12/21/2016.
+ */
+/**
+ * Created by obarda on 12/13/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Graph.Utils {
+
+ export class CommonGraphUtils {
+
+ constructor(private NodesFactory:Sdc.Utils.NodesFactory, private LinksFactory:Sdc.Utils.LinksFactory) {
+
+ }
+
+ public safeApply = (scope:ng.IScope, fn:any) => { //todo remove to general utils
+ let phase = scope.$root.$$phase;
+ if (phase == '$apply' || phase == '$digest') {
+ if (fn && (typeof(fn) === 'function')) {
+ fn();
+ }
+ } else {
+ scope.$apply(fn);
+ }
+ };
+
+ /**
+ * Draw node on the graph
+ * @param cy
+ * @param compositionGraphNode
+ * @param position
+ * @returns {CollectionElements}
+ */
+ public addNodeToGraph(cy:Cy.Instance, compositionGraphNode:Models.Graph.CommonNodeBase, position?:Cy.Position):Cy.CollectionElements {
+
+ var node = cy.add(<Cy.ElementDefinition> {
+ group: 'nodes',
+ position: position,
+ data: compositionGraphNode,
+ classes: compositionGraphNode.classes
+ });
+
+ if(!node.data().isUcpe) { //ucpe should not have tooltip
+ this.initNodeTooltip(node);
+ }
+ return node;
+ };
+
+ /**
+ * The function will create a component instance node by the componentInstance position.
+ * If the node is UCPE the function will create all cp lan&wan for the ucpe
+ * @param cy
+ * @param compositionGraphNode
+ * @returns {Cy.CollectionElements}
+ */
+ public addComponentInstanceNodeToGraph(cy:Cy.Instance, compositionGraphNode:Models.Graph.CompositionCiNodeBase):Cy.CollectionElements {
+
+ let nodePosition = {
+ x: +compositionGraphNode.componentInstance.posX,
+ y: +compositionGraphNode.componentInstance.posY
+ };
+
+ let node = this.addNodeToGraph(cy, compositionGraphNode, nodePosition);
+ if (compositionGraphNode.isUcpe) {
+ this.createUcpeCpNodes(cy, node);
+ }
+ return node;
+ };
+
+ /**
+ * This function will create CP_WAN & CP_LAN for the UCPE. this is a special node on the group that will behave like ports on the ucpe
+ * @param cy
+ * @param ucpeGraphNode
+ */
+ private createUcpeCpNodes(cy:Cy.Instance, ucpeGraphNode:Cy.CollectionNodes):void {
+
+ let requirementsArray:Array<any> = ucpeGraphNode.data().componentInstance.requirements["tosca.capabilities.Node"];
+ //show only LAN or WAN requirements
+ requirementsArray = _.reject(requirementsArray, (requirement:any) => {
+ let name:string = requirement.ownerName.toLowerCase();
+ return name.indexOf('lan') === -1 && name.indexOf('wan') === -1;
+ });
+ requirementsArray.sort(function (a, b) {
+ let nameA = a.ownerName.toLowerCase().match(/[^ ]+/)[0];
+ let nameB = b.ownerName.toLowerCase().match(/[^ ]+/)[0];
+ let numA = _.last(a.ownerName.toLowerCase().split(' '));
+ let numB = _.last(b.ownerName.toLowerCase().split(' '));
+
+ if (nameA === nameB) return numA > numB ? 1 : -1;
+ return nameA < nameB ? 1 : -1;
+ });
+ let position = angular.copy(ucpeGraphNode.boundingbox());
+ //add CP nodes to group
+ let topCps:number = 0;
+ for (let i = 0; i < requirementsArray.length; i++) {
+
+ let cpNode = this.NodesFactory.createUcpeCpNode(angular.copy(ucpeGraphNode.data().componentInstance));
+ cpNode.componentInstance.capabilities = requirementsArray[i];
+ cpNode.id = requirementsArray[i].ownerId;
+ cpNode.group = ucpeGraphNode.data().componentInstance.uniqueId;
+ cpNode.name = requirementsArray[i].ownerName; //for tooltip
+ cpNode.displayName = requirementsArray[i].ownerName;
+ cpNode.displayName = cpNode.displayName.length > 5 ? cpNode.displayName.substring(0, 5) + '...' : cpNode.displayName;
+
+
+ if (cpNode.name.toLowerCase().indexOf('lan') > -1) {
+ cpNode.textPosition = "top";
+ cpNode.componentInstance.posX = position.x1 + (i * 90) - (topCps * 90) + 53;
+ cpNode.componentInstance.posY = position.y1 + 400 + 27;
+ } else {
+ cpNode.textPosition = "bottom";
+ cpNode.componentInstance.posX = position.x1 + (topCps * 90) + 53;
+ cpNode.componentInstance.posY = position.y1 + 27;
+ topCps++;
+ }
+ let cyCpNode = this.addComponentInstanceNodeToGraph(cy, cpNode);
+ cyCpNode.lock();
+ }
+ };
+
+ /**
+ *
+ * @param nodes - all nodes in graph in order to find the edge connecting the two nodes
+ * @param fromNodeId
+ * @param toNodeId
+ * @returns {boolean} true/false if the edge is certified (from node and to node are certified)
+ */
+ public isRelationCertified(nodes:Cy.CollectionNodes, fromNodeId:string, toNodeId:string):boolean {
+ let resourceTemp = _.filter(nodes, function (node:Cy.CollectionFirst) {
+ return node.data().id === fromNodeId || node.data().id === toNodeId;
+ });
+ let certified:boolean = true;
+
+ _.forEach(resourceTemp, (item) => {
+ certified = certified && item.data().certified;
+ });
+
+ return certified;
+ }
+
+ /**
+ * Add link to graph - only draw the link
+ * @param cy
+ * @param link
+ */
+ public insertLinkToGraph = (cy:Cy.Instance, link:Models.CompositionCiLinkBase) => {
+
+ if (!this.isRelationCertified(cy.nodes(), link.source, link.target)) {
+ link.classes = 'not-certified-link';
+ }
+ cy.add({
+ group: 'edges',
+ data: link,
+ classes: link.classes
+ });
+
+ };
+
+ /**
+ * go over the relations and draw links on the graph
+ * @param cy
+ * @param instancesRelations
+ */
+ public initGraphLinks(cy:Cy.Instance, instancesRelations:Array<Models.RelationshipModel>) {
+
+ if (instancesRelations) {
+ _.forEach(instancesRelations, (relationshipModel:Models.RelationshipModel) => {
+ _.forEach(relationshipModel.relationships, (relationship:Models.Relationship) => {
+ let linkToCreate = this.LinksFactory.createGraphLink(cy, relationshipModel, relationship);
+ this.insertLinkToGraph(cy, linkToCreate);
+ });
+ });
+ }
+ }
+
+ /**
+ * Determine which nodes are in the UCPE and set child data for them.
+ * @param cy
+ */
+ public initUcpeChildren(cy:Cy.Instance){
+ let ucpe:Cy.CollectionNodes = cy.nodes('[?isUcpe]'); // Get ucpe on graph if exist
+ _.each(cy.edges('.ucpe-host-link'), (link)=>{
+
+ let ucpeChild:Cy.CollectionNodes = (link.source().id() == ucpe.id())? link.target() : link.source();
+ this.initUcpeChildData(ucpeChild, ucpe);
+
+ //vls dont have ucpe-host-link connection, so need to find them and iterate separately
+ let connectedVLs = ucpeChild.connectedEdges().connectedNodes('.vl-node');
+ _.forEach(connectedVLs, (vl)=>{ //all connected vls must be UCPE children because not allowed to connect to a VL outside of the UCPE
+ this.initUcpeChildData(vl, ucpe);
+ });
+ });
+ }
+
+ /**
+ * Set properties for nodes contained by the UCPE
+ * @param childNode- node contained in UCPE
+ * @param ucpe- ucpe container node
+ */
+ public initUcpeChildData(childNode:Cy.CollectionNodes, ucpe:Cy.CollectionNodes){
+
+ if(!childNode.data('isInsideGroup')){
+ this.updateUcpeChildPosition(childNode, ucpe);
+ childNode.data({isInsideGroup: true});
+ }
+
+ }
+
+ /**
+ * Updates UCPE child node offset, which allows child nodes to be dragged in synchronization with ucpe
+ * @param childNode- node contained in UCPE
+ * @param ucpe- ucpe container node
+ */
+ public updateUcpeChildPosition(childNode:Cy.CollectionNodes, ucpe:Cy.CollectionNodes){
+ let childPos:Cy.Position = childNode.relativePosition();
+ let ucpePos:Cy.Position = ucpe.relativePosition();
+ let offset:Cy.Position = {
+ x: childPos.x - ucpePos.x,
+ y: childPos.y - ucpePos.y
+ };
+ childNode.data("ucpeOffset", offset);
+ }
+
+ /**
+ * Removes ucpe-child properties from the node
+ * @param childNode- node being removed from UCPE
+ */
+ public removeUcpeChildData(childNode:Cy.CollectionNodes){
+ childNode.removeData("ucpeOffset");
+ childNode.data({isInsideGroup: false});
+
+ }
+
+
+ public HTMLCoordsToCytoscapeCoords(cytoscapeBoundingBox:Cy.Extent, mousePos:Cy.Position):Cy.Position {
+ return {x: mousePos.x + cytoscapeBoundingBox.x1, y: mousePos.y + cytoscapeBoundingBox.y1}
+ };
+
+
+ public getCytoscapeNodePosition = (cy: Cy.Instance, event:IDragDropEvent):Cy.Position => {
+ let targetOffset = $(event.target).offset();
+ let x = event.pageX - targetOffset.left;
+ let y = event.pageY - targetOffset.top;
+
+ return this.HTMLCoordsToCytoscapeCoords(cy.extent(), {
+ x: x,
+ y: y
+ });
+ };
+
+
+ public getNodePosition(node:Cy.CollectionFirstNode):Cy.Position{
+ let nodePosition = node.relativePoint();
+ if(node.data().isUcpe){ //UCPEs use bounding box and not relative point.
+ nodePosition = {x: node.boundingbox().x1, y: node.boundingbox().y1};
+ }
+
+ return nodePosition;
+ }
+
+ /**
+ * return true/false if first node contains in second - this used in order to verify is node is entirely inside ucpe
+ * @param firstBox
+ * @param secondBox
+ * @returns {boolean}
+ */
+ public isFirstBoxContainsInSecondBox(firstBox:Cy.BoundingBox, secondBox:Cy.BoundingBox) {
+
+ return firstBox.x1 > secondBox.x1 && firstBox.x2 < secondBox.x2 && firstBox.y1 > secondBox.y1 && firstBox.y2 < secondBox.y2;
+
+ };
+
+
+ /**
+ * Check if node node bounds position is inside any ucpe on graph, and return the ucpe
+ * @param {diagram} the diagram.
+ * @param {nodeActualBounds} the actual bound position of the node.
+ * @return the ucpe if found else return null
+ */
+ public isInUcpe = (cy: Cy.Instance, nodeBounds: Cy.BoundingBox): Cy.CollectionElements => {
+
+ let ucpeNodes = cy.nodes('[?isUcpe]').filterFn((ucpeNode) => {
+ return this.isFirstBoxContainsInSecondBox(nodeBounds, ucpeNode.boundingbox());
+ });
+ return ucpeNodes;
+ };
+
+ /**
+ *
+ * @param cy
+ * @param node
+ * @returns {Array}
+ */
+ public getLinkableNodes(cy:Cy.Instance, node:Cy.CollectionFirstNode):Array<Models.Graph.CompositionCiNodeBase>{
+ let compatibleNodes = [];
+ _.each(cy.nodes(), (tempNode)=>{
+ if(this.nodeLocationsCompatible(cy, node, tempNode)){
+ compatibleNodes.push(tempNode.data());
+ }
+ });
+ return compatibleNodes;
+ }
+
+ /**
+ * Checks whether node locations are compatible in reference to UCPEs.
+ * Returns true if both nodes are in UCPE or both nodes out, or one node is UCPEpart.
+ * @param node1
+ * @param node2
+ */
+ public nodeLocationsCompatible(cy:Cy.Instance, node1:Cy.CollectionFirstNode, node2:Cy.CollectionFirstNode){
+
+ let ucpe = cy.nodes('[?isUcpe]');
+ if(!ucpe.length){ return true; }
+ if(node1.data().isUcpePart || node2.data().isUcpePart) { return true; }
+
+ return (this.isFirstBoxContainsInSecondBox(node1.boundingbox(), ucpe.boundingbox()) == this.isFirstBoxContainsInSecondBox(node2.boundingbox(), ucpe.boundingbox()));
+
+ }
+
+ /**
+ * This function will init qtip tooltip on the node
+ * @param node - the node we want the tooltip to apply on
+ */
+ public initNodeTooltip(node:Cy.CollectionNodes) {
+
+ let opts = {
+ content: function () {
+ return this.data('name');
+ },
+ position: {
+ my: 'top center',
+ at: 'bottom center',
+ adjust: {x:0, y:-5}
+ },
+ style: {
+ classes: 'qtip-dark qtip-rounded qtip-custom',
+ tip: {
+ width: 16,
+ height: 8
+ }
+ },
+ show: {
+ event: 'mouseover',
+ delay: 1000
+ },
+ hide: {event: 'mouseout mousedown'},
+ includeLabels: true
+ };
+
+ if (node.data().isUcpePart){ //fix tooltip positioning for UCPE-cps
+ opts.position.adjust = {x:0, y:20};
+ }
+
+ node.qtip(opts);
+ };
+ };
+
+
+
+ CommonGraphUtils.$inject = ['NodesFactory', 'LinksFactory'];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/common/style/component-instances-nodes-style.ts b/catalog-ui/app/scripts/directives/graphs-v2/common/style/component-instances-nodes-style.ts
new file mode 100644
index 0000000000..2ec0174aa9
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/common/style/component-instances-nodes-style.ts
@@ -0,0 +1,259 @@
+/**
+ * Created by obarda on 12/18/2016.
+ */
+/**
+ * Created by obarda on 12/13/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils.ComponentIntanceNodesStyle {
+
+ export function getCompositionGraphStyle():Array<Cy.Stylesheet> {
+ return [
+ {
+ selector: 'core',
+ css: {
+ 'shape': 'rectangle',
+ 'active-bg-size': 0,
+ 'selection-box-color': 'rgb(0, 159, 219)',
+ 'selection-box-opacity': 0.2,
+ 'selection-box-border-color': '#009fdb',
+ 'selection-box-border-width': 1
+
+ }
+ },
+ {
+ selector: 'node',
+ css: {
+ 'font-family': 'omnes-regular,sans-serif',
+ 'font-size': 14,
+ 'events': 'yes',
+ 'text-events': 'yes',
+ 'text-border-width': 15,
+ 'text-border-color': Sdc.Utils.Constants.GraphColors.NODE_UCPE,
+ 'text-margin-y': 5
+ }
+ },
+ {
+ selector: '.vf-node',
+ css: {
+ 'background-color': 'transparent',
+ 'shape': 'rectangle',
+ 'label': 'data(displayName)',
+ 'background-image': 'data(img)',
+ 'width': 65,
+ 'height': 65,
+ 'background-opacity': 0,
+ "background-width": 65,
+ "background-height": 65,
+ 'text-valign': 'bottom',
+ 'text-halign': 'center',
+ 'background-fit': 'cover',
+ 'background-clip': 'node',
+ 'overlay-color': Sdc.Utils.Constants.GraphColors.NODE_BACKGROUND_COLOR,
+ 'overlay-opacity': 0
+ }
+ },
+
+ {
+ selector: '.service-node',
+ css: {
+ 'background-color': 'transparent',
+ 'label': 'data(displayName)',
+ 'events': 'yes',
+ 'text-events': 'yes',
+ 'background-image': 'data(img)',
+ 'width': 64,
+ 'height': 64,
+ "border-width": 0,
+ 'text-valign': 'bottom',
+ 'text-halign': 'center',
+ 'background-opacity': 0,
+ 'overlay-color': Sdc.Utils.Constants.GraphColors.NODE_BACKGROUND_COLOR,
+ 'overlay-opacity': 0
+ }
+ },
+ {
+ selector: '.cp-node',
+ css: {
+ 'background-color': 'rgb(255,255,255)',
+ 'shape': 'rectangle',
+ 'label': 'data(displayName)',
+ 'background-image': 'data(img)',
+ 'background-width': 21,
+ 'background-height': 21,
+ 'width': 21,
+ 'height': 21,
+ 'text-valign': 'bottom',
+ 'text-halign': 'center',
+ 'background-opacity': 0,
+ 'overlay-color': Sdc.Utils.Constants.GraphColors.NODE_BACKGROUND_COLOR,
+ 'overlay-opacity': 0
+ }
+ },
+ {
+ selector: '.vl-node',
+ css: {
+ 'background-color': 'rgb(255,255,255)',
+ 'shape': 'rectangle',
+ 'label': 'data(displayName)',
+ 'background-image': 'data(img)',
+ 'background-width': 21,
+ 'background-height': 21,
+ 'width': 21,
+ 'height': 21,
+ 'text-valign': 'bottom',
+ 'text-halign': 'center',
+ 'background-opacity': 0,
+ 'overlay-color': Sdc.Utils.Constants.GraphColors.NODE_BACKGROUND_COLOR,
+ 'overlay-opacity': 0
+ }
+ },
+ {
+ selector: '.ucpe-cp',
+ css: {
+ 'background-color': Sdc.Utils.Constants.GraphColors.NODE_UCPE_CP,
+ 'background-width': 15,
+ 'background-height': 15,
+ 'width': 15,
+ 'height': 15,
+ 'text-halign': 'center',
+ 'overlay-opacity': 0,
+ 'label': 'data(displayName)',
+ 'text-valign': 'data(textPosition)',
+ 'text-margin-y': (ele:Cy.Collection) => {
+ return (ele.data('textPosition') == 'top')? -5 : 5;
+ },
+ 'font-size': 12
+ }
+ },
+ {
+ selector: '.ucpe-node',
+ css: {
+ 'background-fit': 'cover',
+ 'padding-bottom': 0,
+ 'padding-top': 0
+ }
+ },
+ {
+ selector: '.simple-link',
+ css: {
+ 'width': 1,
+ 'line-color': Sdc.Utils.Constants.GraphColors.BASE_LINK,
+ 'target-arrow-color': '#3b7b9b',
+ 'target-arrow-shape': 'triangle',
+ 'curve-style': 'bezier',
+ 'control-point-step-size': 30
+ }
+ },
+ {
+ selector: '.vl-link',
+ css: {
+ 'width': 3,
+ 'line-color': Sdc.Utils.Constants.GraphColors.VL_LINK,
+ 'curve-style': 'bezier',
+ 'control-point-step-size': 30
+ }
+ },
+ {
+ selector: '.ucpe-host-link',
+ css: {
+ 'width': 0
+ }
+ },
+ {
+ selector: '.not-certified-link',
+ css: {
+ 'width': 1,
+ 'line-color': Sdc.Utils.Constants.GraphColors.NOT_CERTIFIED_LINK,
+ 'curve-style': 'bezier',
+ 'control-point-step-size': 30,
+ 'line-style': 'dashed',
+ 'target-arrow-color': '#3b7b9b',
+ 'target-arrow-shape': 'triangle'
+
+ }
+ },
+
+ {
+ selector: '.not-certified',
+ css: {
+ 'shape': 'rectangle',
+ 'background-image': (ele:Cy.Collection) => {
+ return ele.data().initImage(ele)
+ },
+ "border-width": 0
+ }
+ },
+ {
+ selector: 'node:selected',
+ css: {
+ "border-width": 2,
+ "border-color": Sdc.Utils.Constants.GraphColors.NODE_SELECTED_BORDER_COLOR,
+ 'shape': 'rectangle'
+ }
+ },
+ {
+ selector: 'edge:selected',
+ css: {
+ 'line-color': Sdc.Utils.Constants.GraphColors.ACTIVE_LINK
+
+ }
+ },
+ {
+ selector: 'edge:active',
+ css: {
+ 'overlay-opacity': 0
+ }
+ }
+ ]
+ }
+
+ export function getBasicNodeHanlde() {
+ return {
+ positionX: "center",
+ positionY: "top",
+ offsetX: 15,
+ offsetY: -20,
+ color: "#27a337",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["basic-node"],
+ imageUrl: Sdc.Utils.Constants.IMAGE_PATH + '/styles/images/resource-icons/' + 'canvasPlusIcon.png',
+ lineWidth: 2,
+ lineStyle: 'dashed'
+
+ }
+ }
+
+ export function getBasicSmallNodeHandle() {
+ return {
+ positionX: "center",
+ positionY: "top",
+ offsetX: 3,
+ offsetY: -25,
+ color: "#27a337",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["basic-small-node"],
+ imageUrl: Sdc.Utils.Constants.IMAGE_PATH + '/styles/images/resource-icons/' + 'canvasPlusIcon.png',
+ lineWidth: 2,
+ lineStyle: 'dashed'
+ }
+ }
+
+ export function getUcpeCpNodeHandle() {
+ return {
+ positionX: "center",
+ positionY: "center",
+ offsetX: -8,
+ offsetY: -10,
+ color: "#27a337",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["ucpe-cp-node"],
+ imageUrl: Sdc.Utils.Constants.IMAGE_PATH + '/styles/images/resource-icons/' + 'canvasPlusIcon.png',
+ lineWidth: 2,
+ lineStyle: 'dashed'
+ }
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/common/style/module-node-style.ts b/catalog-ui/app/scripts/directives/graphs-v2/common/style/module-node-style.ts
new file mode 100644
index 0000000000..62436fbf74
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/common/style/module-node-style.ts
@@ -0,0 +1,92 @@
+/**
+ * Created by obarda on 1/1/2017.
+ */
+/**
+ * Created by obarda on 12/18/2016.
+ */
+/**
+ * Created by obarda on 12/13/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils.ModulesNodesStyle {
+
+ export function getModuleGraphStyle():Array<Cy.Stylesheet> {
+
+ return [
+ {
+ selector: '.cy-expand-collapse-collapsed-node',
+ css: {
+ 'background-image': 'data(img)',
+ 'width': 34,
+ 'height': 32,
+ 'background-opacity': 0,
+ 'shape': 'rectangle',
+ 'label': 'data(displayName)',
+ 'events': 'yes',
+ 'text-events': 'yes',
+ 'text-valign': 'bottom',
+ 'text-halign': 'center',
+ 'text-margin-y': 5,
+ 'border-opacity': 0
+ }
+ },
+ {
+ selector: '.module-node',
+ css: {
+ 'background-color': 'transparent',
+ 'background-opacity': 0,
+ "border-width": 2,
+ "border-color": Sdc.Utils.Constants.GraphColors.NODE_SELECTED_BORDER_COLOR,
+ 'border-style': 'dashed',
+ 'label': 'data(displayName)',
+ 'events': 'yes',
+ 'text-events': 'yes',
+ 'text-valign': 'bottom',
+ 'text-halign': 'center',
+ 'text-margin-y': 8
+ }
+ },
+ {
+ selector: 'node:selected',
+ css: {
+ "border-opacity": 0
+ }
+ },
+ {
+ selector: '.simple-link:selected',
+ css: {
+ 'line-color': Sdc.Utils.Constants.GraphColors.BASE_LINK,
+ }
+ },
+ {
+ selector: '.vl-link:selected',
+ css: {
+ 'line-color': Sdc.Utils.Constants.GraphColors.VL_LINK,
+ }
+ },
+ {
+ selector: '.cy-expand-collapse-collapsed-node:selected',
+ css: {
+ "border-color": Sdc.Utils.Constants.GraphColors.NODE_SELECTED_BORDER_COLOR,
+ 'border-opacity': 1,
+ 'border-style': 'solid',
+ 'border-width': 2
+ }
+ },
+ {
+ selector: '.module-node:selected',
+ css: {
+ "border-color": Sdc.Utils.Constants.GraphColors.NODE_SELECTED_BORDER_COLOR,
+ 'border-opacity': 1
+ }
+ },
+ {
+ selector: '.dummy-node',
+ css: {
+ 'width': 20,
+ 'height': 20
+ }
+ },
+ ]
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.directive.ts b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.directive.ts
new file mode 100644
index 0000000000..708f1d091a
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.directive.ts
@@ -0,0 +1,555 @@
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+
+ import ComponentFactory = Sdc.Utils.ComponentFactory;
+ import LoaderService = Sdc.Services.LoaderService;
+ import GRAPH_EVENTS = Sdc.Utils.Constants.GRAPH_EVENTS;
+
+ interface ICompositionGraphScope extends ng.IScope {
+
+ component:Models.Components.Component;
+ isViewOnly:boolean;
+ // Link menu - create link menu
+ relationMenuDirectiveObj:Models.RelationMenuDirectiveObj;
+ isLinkMenuOpen:boolean;
+ createLinkFromMenu:(chosenMatch:Models.MatchBase, vl:Models.Components.Component)=>void;
+
+ //modify link menu - for now only delete menu
+ relationMenuTimeout:ng.IPromise<any>;
+ linkMenuObject:Models.LinkMenu;
+
+ //left palette functions callbacks
+ dropCallback(event:JQueryEventObject, ui:any):void;
+ beforeDropCallback(event:IDragDropEvent):void;
+ verifyDrop(event:JQueryEventObject, ui:any):void;
+
+ //Links menus
+ deleteRelation(link:Cy.CollectionEdges):void;
+ hideRelationMenu();
+ }
+
+ export class CompositionGraph implements ng.IDirective {
+ private _cy:Cy.Instance;
+ private _currentlyCLickedNodePosition:Cy.Position;
+ private $document:JQuery = $(document);
+ private dragElement:JQuery;
+ private dragComponent: Sdc.Models.ComponentsInstances.ComponentInstance;
+
+ constructor(private $q:ng.IQService,
+ private $filter:ng.IFilterService,
+ private $log:ng.ILogService,
+ private $timeout:ng.ITimeoutService,
+ private NodesFactory:Sdc.Utils.NodesFactory,
+ private CompositionGraphLinkUtils:Sdc.Graph.Utils.CompositionGraphLinkUtils,
+ private GeneralGraphUtils:Graph.Utils.CompositionGraphGeneralUtils,
+ private ComponentInstanceFactory:Utils.ComponentInstanceFactory,
+ private NodesGraphUtils:Sdc.Graph.Utils.CompositionGraphNodesUtils,
+ private eventListenerService:Services.EventListenerService,
+ private ComponentFactory:ComponentFactory,
+ private LoaderService:LoaderService,
+ private commonGraphUtils:Graph.Utils.CommonGraphUtils,
+ private matchCapabilitiesRequirementsUtils:Graph.Utils.MatchCapabilitiesRequirementsUtils) {
+
+ }
+
+ restrict = 'E';
+ templateUrl = '/app/scripts/directives/graphs-v2/composition-graph/composition-graph.html';
+ scope = {
+ component: '=',
+ isViewOnly: '='
+ };
+
+ link = (scope:ICompositionGraphScope, el:JQuery) => {
+ this.loadGraph(scope, el);
+
+ scope.$on('$destroy', () => {
+ this._cy.destroy();
+ _.forEach(GRAPH_EVENTS, (event) => {
+ this.eventListenerService.unRegisterObserver(event);
+ });
+ });
+
+ };
+
+ private loadGraph = (scope:ICompositionGraphScope, el:JQuery) => {
+
+
+ let graphEl = el.find('.sdc-composition-graph-wrapper');
+ this.initGraph(graphEl, scope.isViewOnly);
+ this.initGraphNodes(scope.component.componentInstances, scope.isViewOnly);
+ this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations);
+ this.commonGraphUtils.initUcpeChildren(this._cy);
+ this.initDropZone(scope);
+ this.registerCytoscapeGraphEvents(scope);
+ this.registerCustomEvents(scope, el);
+ this.initViewMode(scope.isViewOnly);
+
+ };
+
+ private initGraph(graphEl:JQuery, isViewOnly:boolean) {
+
+ this._cy = cytoscape({
+ container: graphEl,
+ style: Sdc.Graph.Utils.ComponentIntanceNodesStyle.getCompositionGraphStyle(),
+ zoomingEnabled: false,
+ selectionType: 'single',
+ boxSelectionEnabled: true,
+ autolock: isViewOnly,
+ autoungrabify: isViewOnly
+ });
+ }
+
+ private initViewMode(isViewOnly:boolean) {
+
+ if (isViewOnly) {
+ //remove event listeners
+ this._cy.off('drag');
+ this._cy.off('handlemouseout');
+ this._cy.off('handlemouseover');
+ this._cy.edges().unselectify();
+ }
+ };
+
+ private registerCustomEvents(scope:ICompositionGraphScope, el:JQuery) {
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (component:Models.DisplayComponent) => {
+ this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${component.uniqueId}`);
+
+ let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
+ let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
+
+ if (this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.containsKey(component.uniqueId)) {
+ let cacheComponent = this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.getValue(component.uniqueId);
+ let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findByMatchingCapabilitiesToRequirements(cacheComponent, nodesData, nodesLinks);
+
+ this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
+ this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
+
+ return;
+ }
+
+ component.component.updateRequirementsCapabilities()
+ .then((res) => {
+ component.component.capabilities = res.capabilities;
+ component.component.requirements = res.requirements;
+
+ let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findByMatchingCapabilitiesToRequirements(component.component, nodesData, nodesLinks);
+ this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy);
+ this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy)
+ });
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
+ this._cy.emit('hidehandles');
+ this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
+
+ this.dragElement = dragElement;
+ this.dragComponent = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (event:IDragDropEvent) => {
+ this._onComponentDrag(event);
+
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component:Models.ComponentsInstances.ComponentInstance) => {
+
+ let selectedNode = this._cy.getElementById(component.uniqueId);
+ selectedNode.data().componentInstance.name = component.name;
+ selectedNode.data('displayName', selectedNode.data().getDisplayName());
+
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstance:Models.ComponentsInstances.ComponentInstance) => {
+ let nodeToDelete = this._cy.getElementById(componentInstance.uniqueId);
+ this.NodesGraphUtils.deleteNode(this._cy, scope.component, nodeToDelete);
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_MULTIPLE_COMPONENTS, () => {
+
+ this._cy.$('node:selected').each((i:number, node:Cy.CollectionNodes) => {
+ this.NodesGraphUtils.deleteNode(this._cy, scope.component, node);
+ });
+
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading:boolean, linksToDelete:Cy.CollectionEdges) => {
+ this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, releaseLoading, linksToDelete);
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, (node:Cy.CollectionNodes, ucpe:Cy.CollectionNodes, updateExistingNode: boolean) => {
+
+ this.commonGraphUtils.initUcpeChildData(node, ucpe);
+ //check if item is a VL, and if so, skip adding the binding to ucpe
+ if(!(node.data() instanceof Sdc.Models.Graph.CompositionCiNodeVl)){
+ this.CompositionGraphLinkUtils.createVfToUcpeLink(scope.component, this._cy, ucpe.data(), node.data()); //create link from the node to the ucpe
+ }
+
+ if(updateExistingNode){
+ let vlsPendingDeletion:Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node); //delete connected VLs that no longer have 2 links
+ this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
+ this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
+ }
+
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, (node:Cy.CollectionNodes, ucpe:Cy.CollectionNodes) => {
+ this.commonGraphUtils.removeUcpeChildData(node);
+ let vlsPendingDeletion:Cy.CollectionNodes = this.NodesGraphUtils.deleteNodeVLsUponMoveToOrFromUCPE(scope.component, node.cy(), node);
+ this.CompositionGraphLinkUtils.deleteLinksWhenNodeMovedFromOrToUCPE(scope.component, node.cy(), node, vlsPendingDeletion); //delete all connected links if needed
+ this.GeneralGraphUtils.pushUpdateComponentInstanceActionToQueue(scope.component, true, node.data().componentInstance); //update componentInstance position
+ });
+
+ this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component:Models.Components.Component) => {
+ scope.component = component;
+ this.loadGraph(scope, el);
+ });
+
+
+ scope.createLinkFromMenu = (chosenMatch:Models.MatchBase, vl:Models.Components.Component):void => {
+ scope.isLinkMenuOpen = false;
+
+ this.CompositionGraphLinkUtils.createLinkFromMenu(this._cy, chosenMatch, vl, scope.component);
+ };
+
+ scope.hideRelationMenu = () => {
+ this.commonGraphUtils.safeApply(scope, () => {
+ scope.linkMenuObject = null;
+ this.$timeout.cancel(scope.relationMenuTimeout);
+ });
+ };
+
+
+ scope.deleteRelation = (link:Cy.CollectionEdges) => {
+ scope.hideRelationMenu();
+
+ //if multiple edges selected, delete the VL itself so edges get deleted automatically
+ if (this._cy.$('edge:selected').length > 1) {
+ this.NodesGraphUtils.deleteNode(this._cy, scope.component, this._cy.$('node:selected'));
+ } else {
+ this.CompositionGraphLinkUtils.deleteLink(this._cy, scope.component, true, link);
+ }
+ };
+ }
+
+
+ private registerCytoscapeGraphEvents(scope:ICompositionGraphScope) {
+
+ this._cy.on('addedgemouseup', (event, data) => {
+ scope.relationMenuDirectiveObj = this.CompositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
+ if (scope.relationMenuDirectiveObj != null) {
+ scope.$apply(() => {
+ scope.isLinkMenuOpen = true;
+ });
+ }
+ });
+ this._cy.on('tapstart', 'node', (event:Cy.EventObject) => {
+ this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag
+ if(event.cyTarget.data().isUcpe){
+ this._cy.nodes('.ucpe-cp').unlock();
+ event.cyTarget.style('opacity', 0.5);
+ }
+ });
+
+ this._cy.on('drag', 'node', (event:Cy.EventObject) => {
+
+ if (event.cyTarget.data().isDraggable) {
+ event.cyTarget.style({'overlay-opacity': 0.24});
+ if (this.GeneralGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
+ event.cyTarget.style({'overlay-color': Utils.Constants.GraphColors.NODE_BACKGROUND_COLOR});
+ } else {
+ event.cyTarget.style({'overlay-color': Utils.Constants.GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR});
+ }
+ }
+
+ if(event.cyTarget.data().isUcpe){
+ let pos = event.cyTarget.position();
+
+ this._cy.nodes('[?isInsideGroup]').positions((i, node)=>{
+ return {
+ x: pos.x + node.data("ucpeOffset").x,
+ y: pos.y + node.data("ucpeOffset").y
+ }
+ });
+ }
+ });
+
+
+ this._cy.on('handlemouseover', (event, payload) => {
+
+ if (payload.node.grabbed()) { //no need to add opacity while we are dragging and hovering othe nodes
+ return;
+ }
+
+ let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes());
+ let nodesLinks = this.GeneralGraphUtils.getAllCompositionCiLinks(this._cy);
+
+ let linkableNodes = this.commonGraphUtils.getLinkableNodes(this._cy, payload.node);
+ let filteredNodesData = this.matchCapabilitiesRequirementsUtils.findByMatchingCapabilitiesToRequirements(payload.node.data().componentInstance, linkableNodes, nodesLinks);
+ this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
+ this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
+
+ });
+
+ this._cy.on('handlemouseout', () => {
+ this._cy.emit('hidehandles');
+ this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
+ });
+
+
+ this._cy.on('tapend', (event:Cy.EventObject) => {
+
+ if (event.cyTarget === this._cy) { //On Background clicked
+ if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
+ }
+ scope.hideRelationMenu();
+ }
+
+ else if (event.cyTarget.isEdge()) { //On Edge clicked
+ if (scope.isViewOnly) return;
+ this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event);
+ this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000);
+ }
+
+ else { //On Node clicked
+ this._cy.nodes(':grabbed').style({'overlay-opacity': 0});
+
+ let isUcpe:boolean = event.cyTarget.data().isUcpe;
+ let newPosition = event.cyTarget[0].position();
+ //node position changed (drop after drag event) - we need to update position
+ if (this._currentlyCLickedNodePosition.x !== newPosition.x || this._currentlyCLickedNodePosition.y !== newPosition.y) {
+ let nodesMoved:Cy.CollectionNodes = this._cy.$(':grabbed');
+ if(isUcpe){
+ nodesMoved = nodesMoved.add(this._cy.nodes('[?isInsideGroup]:free')); //'child' nodes will not be recognized as "grabbed" elements within cytoscape. manually add them to collection of nodes moved.
+ }
+ this.NodesGraphUtils.onNodesPositionChanged(this._cy, scope.component, nodesMoved);
+ } else {
+ this.$log.debug('composition-graph::onNodeSelectedEvent:: fired');
+ scope.$apply(() => {
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
+ });
+ }
+
+ if(isUcpe){
+ this._cy.nodes('.ucpe-cp').lock();
+ event.cyTarget.style('opacity', 1);
+ }
+
+ }
+ });
+
+ this._cy.on('boxselect', 'node', (event:Cy.EventObject) => {
+ this.eventListenerService.notifyObservers(Utils.Constants.GRAPH_EVENTS.ON_NODE_SELECTED, event.cyTarget.data().componentInstance);
+ });
+ }
+
+ private openModifyLinkMenu = (scope:ICompositionGraphScope, linkMenuObject:Models.LinkMenu, timeOutInMilliseconds?:number) => {
+
+ this.commonGraphUtils.safeApply(scope, () => {
+ scope.linkMenuObject = linkMenuObject;
+ });
+
+ scope.relationMenuTimeout = this.$timeout(() => {
+ scope.hideRelationMenu();
+ }, timeOutInMilliseconds ? timeOutInMilliseconds : 6000);
+ };
+
+ private initGraphNodes(componentInstances:Models.ComponentsInstances.ComponentInstance[], isViewOnly:boolean) {
+
+ if (!isViewOnly) { //Init nodes handle extension - enable dynamic links
+ setTimeout(()=> {
+ let handles = new CytoscapeEdgeEditation;
+ handles.init(this._cy, 18);
+ handles.registerHandle(Sdc.Graph.Utils.ComponentIntanceNodesStyle.getBasicNodeHanlde());
+ handles.registerHandle(Sdc.Graph.Utils.ComponentIntanceNodesStyle.getBasicSmallNodeHandle());
+ handles.registerHandle(Sdc.Graph.Utils.ComponentIntanceNodesStyle.getUcpeCpNodeHandle());
+ }, 0);
+ }
+
+ _.each(componentInstances, (instance) => {
+ let compositionGraphNode:Models.Graph.CompositionCiNodeBase = this.NodesFactory.createNode(instance);
+ this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
+ });
+
+
+
+ }
+
+
+ private initDropZone(scope:ICompositionGraphScope) {
+
+ if (scope.isViewOnly) {
+ return;
+ }
+ scope.dropCallback = (event:IDragDropEvent) => {
+ this.$log.debug(`composition-graph::dropCallback:: fired`);
+ this.addNode(event, scope);
+ };
+
+ scope.verifyDrop = (event:JQueryEventObject) => {
+
+ if(this.dragElement.hasClass('red')){
+ return false;
+ }
+ return true;
+ };
+
+ scope.beforeDropCallback = (event:IDragDropEvent): ng.IPromise<void> => {
+ let deferred: ng.IDeferred<void> = this.$q.defer<void>();
+ if(this.dragElement.hasClass('red')){
+ deferred.reject();
+ } else {
+ deferred.resolve();
+ }
+
+ return deferred.promise;
+ }
+ }
+
+ private _getNodeBBox(event:IDragDropEvent, position?:Cy.Position) {
+ let bbox = <Cy.BoundingBox>{};
+ if (!position) {
+ position = this.commonGraphUtils.getCytoscapeNodePosition(this._cy, event);
+ }
+ let cushionWidth:number = 40;
+ let cushionHeight:number = 40;
+
+ bbox.x1 = position.x - cushionWidth / 2;
+ bbox.y1 = position.y - cushionHeight / 2;
+ bbox.x2 = position.x + cushionWidth / 2;
+ bbox.y2 = position.y + cushionHeight / 2;
+ return bbox;
+ }
+
+ private createComponentInstanceOnGraphFromComponent(fullComponent:Models.Components.Component, event:IDragDropEvent, scope:ICompositionGraphScope) {
+
+ let componentInstanceToCreate:Models.ComponentsInstances.ComponentInstance = this.ComponentInstanceFactory.createComponentInstanceFromComponent(fullComponent);
+ let cytoscapePosition:Cy.Position = this.commonGraphUtils.getCytoscapeNodePosition(this._cy, event);
+
+ componentInstanceToCreate.posX = cytoscapePosition.x;
+ componentInstanceToCreate.posY = cytoscapePosition.y;
+
+
+ let onFailedCreatingInstance:(error:any) => void = (error:any) => {
+ this.LoaderService.hideLoader('composition-graph');
+ };
+
+ //on success - update node data
+ let onSuccessCreatingInstance = (createInstance:Models.ComponentsInstances.ComponentInstance):void => {
+
+ this.LoaderService.hideLoader('composition-graph');
+
+ createInstance.name = this.$filter('resourceName')(createInstance.name);
+ createInstance.requirements = new Models.RequirementsGroup(fullComponent.requirements);
+ createInstance.capabilities = new Models.CapabilitiesGroup(fullComponent.capabilities);
+ createInstance.componentVersion = fullComponent.version;
+ createInstance.icon = fullComponent.icon;
+ createInstance.setInstanceRC();
+
+ let newNode:Models.Graph.CompositionCiNodeBase = this.NodesFactory.createNode(createInstance);
+ let cyNode:Cy.CollectionNodes = this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, newNode);
+
+ //check if node was dropped into a UCPE
+ let ucpe:Cy.CollectionElements = this.commonGraphUtils.isInUcpe(this._cy, cyNode.boundingbox());
+ if (ucpe.length > 0) {
+ this.eventListenerService.notifyObservers(Utils.Constants.GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, cyNode, ucpe, false);
+ }
+
+ };
+
+ // Create the component instance on server
+ this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIAction(() => {
+ scope.component.createComponentInstance(componentInstanceToCreate).then(onSuccessCreatingInstance, onFailedCreatingInstance);
+ });
+ }
+
+ private _onComponentDrag(event:IDragDropEvent) {
+
+ if(event.clientX < Sdc.Utils.Constants.GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET || event.clientY < Sdc.Utils.Constants.GraphUIObjects.DIAGRAM_HEADER_OFFSET){ //hovering over palette. Dont bother computing validity of drop
+ this.dragElement.removeClass('red');
+ return;
+ }
+
+ let offsetPosition = {x: event.clientX - Sdc.Utils.Constants.GraphUIObjects.DIAGRAM_PALETTE_WIDTH_OFFSET, y: event.clientY - Sdc.Utils.Constants.GraphUIObjects.DIAGRAM_HEADER_OFFSET}
+ let bbox = this._getNodeBBox(event, offsetPosition);
+
+ if (this.GeneralGraphUtils.isPaletteDropValid(this._cy, bbox, this.dragComponent)) {
+ this.dragElement.removeClass('red');
+ } else {
+ this.dragElement.addClass('red');
+ }
+ }
+
+ private addNode(event:IDragDropEvent, scope:ICompositionGraphScope) {
+ this.LoaderService.showLoader('composition-graph');
+
+ this.$log.debug('composition-graph::addNode:: fired');
+ let draggedComponent:Models.Components.Component = event.dataTransfer.component;
+
+ if (this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.containsKey(draggedComponent.uniqueId)) {
+ this.$log.debug('composition-graph::addNode:: capabilities found in cache, creating component');
+ let fullComponent = this.GeneralGraphUtils.componentRequirementsAndCapabilitiesCaching.getValue(draggedComponent.uniqueId);
+ this.createComponentInstanceOnGraphFromComponent(fullComponent, event, scope);
+ return;
+ }
+
+ this.$log.debug('composition-graph::addNode:: capabilities not found, requesting from server');
+ this.ComponentFactory.getComponentFromServer(draggedComponent.getComponentSubType(), draggedComponent.uniqueId)
+ .then((fullComponent:Models.Components.Component) => {
+ this.createComponentInstanceOnGraphFromComponent(fullComponent, event, scope);
+ });
+ }
+
+ public static factory = ($q,
+ $filter,
+ $log,
+ $timeout,
+ NodesFactory,
+ LinksGraphUtils,
+ GeneralGraphUtils,
+ ComponentInstanceFactory,
+ NodesGraphUtils,
+ EventListenerService,
+ ComponentFactory,
+ LoaderService,
+ CommonGraphUtils,
+ MatchCapabilitiesRequirementsUtils) => {
+ return new CompositionGraph(
+ $q,
+ $filter,
+ $log,
+ $timeout,
+ NodesFactory,
+ LinksGraphUtils,
+ GeneralGraphUtils,
+ ComponentInstanceFactory,
+ NodesGraphUtils,
+ EventListenerService,
+ ComponentFactory,
+ LoaderService,
+ CommonGraphUtils,
+ MatchCapabilitiesRequirementsUtils);
+ }
+ }
+
+ CompositionGraph.factory.$inject = [
+ '$q',
+ '$filter',
+ '$log',
+ '$timeout',
+ 'NodesFactory',
+ 'CompositionGraphLinkUtils',
+ 'CompositionGraphGeneralUtils',
+ 'ComponentInstanceFactory',
+ 'CompositionGraphNodesUtils',
+ 'EventListenerService',
+ 'ComponentFactory',
+ 'LoaderService',
+ 'CommonGraphUtils',
+ 'MatchCapabilitiesRequirementsUtils'
+ ];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.html b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.html
new file mode 100644
index 0000000000..5f2c488341
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.html
@@ -0,0 +1,22 @@
+<loader display="isLoading" loader-type="composition-graph"></loader>
+<div class="sdc-composition-graph-wrapper" ng-class="{'view-only':isViewOnly}"
+ data-drop="true"
+ data-jqyoui-options="{accept: verifyDrop}"
+ data-jqyoui-droppable="{onDrop:'dropCallback', beforeDrop: 'beforeDropCallback'}">
+</div>
+
+<relation-menu relation-menu-directive-obj="relationMenuDirectiveObj" is-link-menu-open="isLinkMenuOpen"
+ create-relation="createLinkFromMenu" cancel="cancelRelationMenu()"></relation-menu>
+
+
+<div class="w-sdc-canvas-menu"
+ data-ng-show="linkMenuObject" ng-style="{left: linkMenuObject.position.x, top: linkMenuObject.position.y}"
+ id="relationMenu">
+
+ <div class="w-sdc-canvas-menu-content hand" data-ng-click="deleteRelation(linkMenuObject.link)">
+ <div class="w-sdc-canvas-menu-content-delete-button"></div>
+ <!--{{relationComponent.data.relation.relationships[0].relationship.type | relationName }}-->
+ Delete
+ </div>
+
+</div>
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.less b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.less
new file mode 100644
index 0000000000..7b999967b7
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/composition-graph.less
@@ -0,0 +1,14 @@
+composition-graph {
+ display: block;
+
+ height:100%;
+ width: 100%;
+ .sdc-composition-graph-wrapper{
+ height:100%;
+ width: 100%;
+ }
+
+ &.view-only{
+ background-color:rgb(248, 248, 248);
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts
new file mode 100644
index 0000000000..495a243d75
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts
@@ -0,0 +1,243 @@
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils {
+
+ import Dictionary = Sdc.Utils.Dictionary;
+
+ export class CompositionGraphGeneralUtils {
+
+ public componentRequirementsAndCapabilitiesCaching = new Dictionary<string, Models.Components.Component>();
+ protected static graphUtilsUpdateQueue: Sdc.Utils.Functions.QueueUtils;
+
+ constructor(private $q: ng.IQService,
+ private LoaderService: Services.LoaderService,
+ private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
+ private matchCapabilitiesRequirementsUtils: Graph.Utils.MatchCapabilitiesRequirementsUtils) {
+ CompositionGraphGeneralUtils.graphUtilsUpdateQueue = new Sdc.Utils.Functions.QueueUtils(this.$q);
+ }
+
+
+ /**
+ * Get the offset for the link creation Menu
+ * @param point
+ * @returns {Cy.Position}
+ */
+ public calcMenuOffset: Function = (point: Cy.Position): Cy.Position => {
+ point.x = point.x + 60;
+ point.y = point.y + 105;
+ return point;
+ };
+
+ /**
+ * return the top left position of the link menu
+ * @param cy
+ * @param targetNodePosition
+ * @returns {Cy.Position}
+ */
+ public getLinkMenuPosition = (cy: Cy.Instance, targetNodePosition: Cy.Position) => {
+ let menuPosition: Cy.Position = this.calcMenuOffset(targetNodePosition); //get the link mid point
+ if (document.body.scrollHeight < menuPosition.y + Sdc.Utils.Constants.GraphUIObjects.LINK_MENU_HEIGHT + $(document.getElementsByClassName('sdc-composition-graph-wrapper')).offset().top) { // if position menu is overflow bottom
+ menuPosition.y = document.body.scrollHeight - Sdc.Utils.Constants.GraphUIObjects.TOP_HEADER_HEIGHT - Sdc.Utils.Constants.GraphUIObjects.LINK_MENU_HEIGHT;
+ }
+ return menuPosition;
+ };
+
+
+ /**
+ * will return true/false if two nodes overlapping
+ *
+ * @param graph node
+ */
+ private isNodesOverlapping(node: Cy.CollectionFirstNode, draggedNode: Cy.CollectionFirstNode): boolean {
+
+ let nodeBoundingBox: Cy.BoundingBox = node.renderedBoundingBox();
+ let secondNodeBoundingBox: Cy.BoundingBox = draggedNode.renderedBoundingBox();
+
+ return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
+ }
+
+ /**
+ * Checks whether the bounding boxes of two nodes are overlapping on any side
+ * @param nodeOneBBox
+ * @param nodeTwoBBox
+ * @returns {boolean}
+ */
+ private isBBoxOverlapping(nodeOneBBox: Cy.BoundingBox, nodeTwoBBox: Cy.BoundingBox) {
+ return (((nodeOneBBox.x1 < nodeTwoBBox.x1 && nodeOneBBox.x2 > nodeTwoBBox.x1) ||
+ (nodeOneBBox.x1 < nodeTwoBBox.x2 && nodeOneBBox.x2 > nodeTwoBBox.x2) ||
+ (nodeTwoBBox.x1 < nodeOneBBox.x1 && nodeTwoBBox.x2 > nodeOneBBox.x2)) &&
+ ((nodeOneBBox.y1 < nodeTwoBBox.y1 && nodeOneBBox.y2 > nodeTwoBBox.y1) ||
+ (nodeOneBBox.y1 < nodeTwoBBox.y2 && nodeOneBBox.y2 > nodeTwoBBox.y2) ||
+ (nodeTwoBBox.y1 < nodeOneBBox.y1 && nodeTwoBBox.y2 > nodeOneBBox.y2)))
+ }
+
+
+ /**
+ * Checks whether a specific component instance can be hosted on the UCPE instance
+ * @param cy - Cytoscape instance
+ * @param fromUcpeInstance
+ * @param toComponentInstance
+ * @returns {Models.MatchReqToCapability}
+ */
+ public canBeHostedOn(cy: Cy.Instance, fromUcpeInstance: Models.ComponentsInstances.ComponentInstance, toComponentInstance: Models.ComponentsInstances.ComponentInstance): Models.MatchReqToCapability {
+
+ let matches: Array<Models.MatchBase> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromUcpeInstance, toComponentInstance, this.getAllCompositionCiLinks(cy));
+ let hostedOnMatch: Models.MatchBase = _.find(matches, (match: Models.MatchReqToCapability) => {
+ return match.requirement.capability.toLowerCase() === 'tosca.capabilities.container';
+ });
+
+ return <Models.MatchReqToCapability>hostedOnMatch;
+ };
+
+
+ /**
+ * Checks whether node can be dropped into UCPE
+ * @param cy
+ * @param nodeToInsert
+ * @param ucpeNode
+ * @returns {boolean}
+ */
+ private isValidDropInsideUCPE(cy: Cy.Instance, nodeToInsert: Models.ComponentsInstances.ComponentInstance, ucpeNode: Models.ComponentsInstances.ComponentInstance): boolean {
+
+ let hostedOnMatch: Models.MatchReqToCapability = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
+ let result: boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
+ return result;
+
+ };
+
+
+ /**
+ * For drops from palette, checks whether the node can be dropped. If node is being held over another node, check if capable of hosting
+ * @param cy
+ * @param pseudoNodeBBox
+ * @param paletteComponentInstance
+ * @returns {boolean}
+ */
+ public isPaletteDropValid(cy: Cy.Instance, pseudoNodeBBox: Cy.BoundingBox, paletteComponentInstance:Sdc.Models.ComponentsInstances.ComponentInstance) {
+
+ let componentIsUCPE:boolean = (paletteComponentInstance.capabilities && paletteComponentInstance.capabilities['tosca.capabilities.Container'] && paletteComponentInstance.name.toLowerCase().indexOf('ucpe') > -1);
+
+ if(componentIsUCPE && cy.nodes('[?isUcpe]').length > 0) { //second UCPE not allowed
+ return false;
+ }
+
+ let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => {
+
+ if(this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())){
+ if (!componentIsUCPE && graphNode.data().isUcpe) {
+ return !this.isValidDropInsideUCPE(cy, paletteComponentInstance, graphNode.data().componentInstance); //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
+ }
+ return true;
+ }
+
+ return false;
+ });
+
+ return illegalOverlappingNodes.length === 0;
+ }
+
+ /**
+ * will return true/false if a drop of a single node is valid
+ *
+ * @param graph node
+ */
+ public isValidDrop(cy: Cy.Instance, draggedNode: Cy.CollectionFirstNode): boolean {
+
+ let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
+
+ if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
+ return false;
+ }
+ if(draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
+ return false;
+ }
+
+ if (!draggedNode.data().isUcpe && !(draggedNode.data() instanceof Sdc.Models.Graph.CompositionCiNodeUcpeCp) && graphNode.data().isUcpe) { //case we are dragging a node into UCPE
+ let isEntirelyInUCPE:boolean = this.commonGraphUtils.isFirstBoxContainsInSecondBox(draggedNode.renderedBoundingBox(), graphNode.renderedBoundingBox());
+ if (isEntirelyInUCPE){
+ if(this.isValidDropInsideUCPE(cy, draggedNode.data().componentInstance, graphNode.data().componentInstance)){ //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
+ return false;
+ }
+ }
+ }
+ return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
+
+ });
+ // return false;
+ return illegalOverlappingNodes.length === 0;
+ };
+
+ /**
+ * will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
+ *
+ * @param nodesArray - the selected drags nodes
+ */
+ public isGroupValidDrop(cy: Cy.Instance, nodesArray: Cy.CollectionNodes): boolean {
+ var filterDraggedNodes = nodesArray.filter('[?isDraggable]');
+ let isValidDrop = _.every(filterDraggedNodes, (node: Cy.CollectionFirstNode) => {
+ return this.isValidDrop(cy, node);
+
+ });
+ return isValidDrop;
+ };
+
+ /**
+ * get all links in diagram
+ * @param cy
+ * @returns {any[]|boolean[]}
+ */
+ public getAllCompositionCiLinks = (cy: Cy.Instance): Array<Models.CompositionCiLinkBase> => {
+ return _.map(cy.edges("[isSdcElement]"), (edge: Cy.CollectionEdges) => {
+ return edge.data();
+ });
+ };
+
+
+ /**
+ * Get Graph Utils server queue
+ * @returns {Sdc.Utils.Functions.QueueUtils}
+ */
+ public getGraphUtilsServerUpdateQueue(): Sdc.Utils.Functions.QueueUtils {
+ return CompositionGraphGeneralUtils.graphUtilsUpdateQueue;
+ }
+ ;
+
+ /**
+ *
+ * @param blockAction - true/false if this is a block action
+ * @param instances
+ * @param component
+ */
+ public pushMultipleUpdateComponentInstancesRequestToQueue = (blockAction: boolean, instances: Array<Models.ComponentsInstances.ComponentInstance>, component: Models.Components.Component): void => {
+ if (blockAction) {
+ this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
+ () => component.updateMultipleComponentInstances(instances)
+ );
+ } else {
+ this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
+ () => component.updateMultipleComponentInstances(instances),
+ () => this.LoaderService.hideLoader('composition-graph'));
+ }
+ };
+
+ /**
+ * this function will update component instance data
+ * @param blockAction - true/false if this is a block action
+ * @param updatedInstance
+ */
+ public pushUpdateComponentInstanceActionToQueue = (component: Models.Components.Component, blockAction: boolean, updatedInstance: Models.ComponentsInstances.ComponentInstance): void => {
+
+ if (blockAction) {
+ this.LoaderService.showLoader('composition-graph');
+ this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
+ () => component.updateComponentInstance(updatedInstance)
+ );
+ } else {
+ this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
+ () => component.updateComponentInstance(updatedInstance),
+ () => this.LoaderService.hideLoader('composition-graph'));
+ }
+ };
+ }
+
+ CompositionGraphGeneralUtils.$inject = ['$q', 'LoaderService', 'CommonGraphUtils', 'MatchCapabilitiesRequirementsUtils'];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts
new file mode 100644
index 0000000000..602e6b6def
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts
@@ -0,0 +1,347 @@
+/**
+ * Created by obarda on 6/28/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils {
+
+ import ImageCreatorService = Sdc.Utils.ImageCreatorService;
+ import Module = Sdc.Models.Module;
+ export class CompositionGraphLinkUtils {
+
+ private p2pVL:Models.Components.Component;
+ private mp2mpVL:Models.Components.Component;
+
+ constructor(private linksFactory:Sdc.Utils.LinksFactory,
+ private loaderService:Services.LoaderService,
+ private generalGraphUtils:Sdc.Graph.Utils.CompositionGraphGeneralUtils,
+ private leftPaletteLoaderService:Services.Components.LeftPaletteLoaderService,
+ private componentInstanceFactory:Sdc.Utils.ComponentInstanceFactory,
+ private nodesFactory:Sdc.Utils.NodesFactory,
+ private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
+ private matchCapabilitiesRequirementsUtils: Graph.Utils.MatchCapabilitiesRequirementsUtils) {
+
+ this.initScopeVls();
+
+ }
+
+
+ /**
+ * Delete the link on server and then remove it from graph
+ * @param component
+ * @param releaseLoading - true/false release the loader when finished
+ * @param link - the link to delete
+ */
+ public deleteLink = (cy:Cy.Instance, component:Models.Components.Component, releaseLoading:boolean, link:Cy.CollectionEdges) => {
+
+ this.loaderService.showLoader('composition-graph');
+ let onSuccessDeleteRelation = (response) => {
+ cy.remove(link);
+ };
+
+ if (!releaseLoading) {
+ this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
+ () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation)
+ );
+ } else {
+ this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
+ () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation),
+ () => this.loaderService.hideLoader('composition-graph'));
+ }
+ };
+
+ /**
+ * create the link on server and than draw it on graph
+ * @param link - the link to create
+ * @param cy
+ * @param component
+ */
+ public createLink = (link:Models.CompositionCiLinkBase, cy:Cy.Instance, component:Models.Components.Component):void => {
+
+ this.loaderService.showLoader('composition-graph');
+
+ let onSuccess:(response:Models.RelationshipModel) => void = (relation:Models.RelationshipModel) => {
+ link.setRelation(relation);
+ this.commonGraphUtils.insertLinkToGraph(cy, link);
+ };
+
+ link.updateLinkDirection();
+
+ this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
+ () => component.createRelation(link.relation).then(onSuccess),
+ () => this.loaderService.hideLoader('composition-graph')
+ );
+ };
+
+
+ public initScopeVls = ():void => {
+
+ let vls = this.leftPaletteLoaderService.getFullDataComponentList(Sdc.Utils.Constants.ResourceType.VL);
+ vls.forEach((item) => {
+ let key = _.find(Object.keys(item.capabilities), (key) => {
+ return _.includes(key.toLowerCase(), 'linkable');
+ });
+ let linkable = item.capabilities[key];
+ if (linkable) {
+ if (linkable[0].maxOccurrences == '2') {
+ this.p2pVL = _.find(vls, (component:Models.Components.Component) => {
+ return component.uniqueId === item.uniqueId;
+ });
+
+ } else {//assuming unbounded occurrences
+ this.mp2mpVL = _.find(vls, (component:Models.Components.Component) => {
+ return component.uniqueId === item.uniqueId;
+ });
+ }
+ }
+ });
+ };
+
+ private setVLlinks = (match:Models.MatchReqToReq, vl:Models.ComponentsInstances.ComponentInstance):Array<Models.RelationshipModel> => {
+
+ let relationship1 = new Models.Relationship();
+ let relationship2 = new Models.Relationship();
+ let newRelationshipModel1 = new Models.RelationshipModel();
+ let newRelationshipModel2 = new Models.RelationshipModel();
+
+ let capability:Models.Capability = vl.capabilities.findValueByKey('linkable')[0];
+ relationship1.setRelationProperties(capability, match.requirement);
+ relationship2.setRelationProperties(capability, match.secondRequirement);
+
+ newRelationshipModel1.setRelationshipModelParams(match.fromNode, vl.uniqueId, [relationship1]);
+ newRelationshipModel2.setRelationshipModelParams(match.toNode, vl.uniqueId, [relationship2]);
+
+ return [newRelationshipModel1, newRelationshipModel2];
+ };
+
+ private createVlinks = (cy:Cy.Instance, component:Models.Components.Component, matchReqToReq:Models.MatchReqToReq, vl:Models.Components.Component):void => {
+
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = this.componentInstanceFactory.createComponentInstanceFromComponent(vl);
+ let fromNodePosition:Cy.Position = cy.getElementById(matchReqToReq.fromNode).relativePosition();
+ let toNodePosition:Cy.Position = cy.getElementById(matchReqToReq.toNode).relativePosition();
+ let location:Cy.Position = {
+ x: 0.5 * (fromNodePosition.x + toNodePosition.x),
+ y: 0.5 * (fromNodePosition.y + toNodePosition.y)
+ }
+
+ componentInstance.posX = location.x;
+ componentInstance.posY = location.y;
+
+ let onFailed:(error:any) => void = (error:any) => {
+ this.loaderService.hideLoader('composition-graph');
+ console.info('onFailed', error);
+ };
+
+ let onSuccess = (response:Models.ComponentsInstances.ComponentInstance):void => {
+
+ console.info('onSuccses', response);
+ response.requirements = new Models.RequirementsGroup(vl.requirements);
+ response.capabilities = new Models.CapabilitiesGroup(vl.capabilities);
+ response.componentVersion = vl.version;
+ response.setInstanceRC();
+
+ let newLinks = this.setVLlinks(matchReqToReq, response);
+ let newNode = this.nodesFactory.createNode(response);
+
+ this.commonGraphUtils.addComponentInstanceNodeToGraph(cy, newNode);
+
+ _.forEach(newLinks, (link) => {
+ let linkObg:Models.CompositionCiLinkBase = this.linksFactory.createGraphLink(cy, link, link.relationships[0]);
+ this.createLink(linkObg, cy, component);
+ });
+ };
+ component.createComponentInstance(componentInstance).then(onSuccess, onFailed);
+ };
+
+ private createSimpleLink = (match:Models.MatchReqToCapability, cy:Cy.Instance, component:Models.Components.Component):void => {
+ let newRelation:Models.RelationshipModel = match.matchToRelationModel();
+ let linkObg:Models.CompositionCiLinkBase = this.linksFactory.createGraphLink(cy,newRelation, newRelation.relationships[0]);
+ this.createLink(linkObg, cy, component);
+ };
+
+ public createLinkFromMenu = (cy:Cy.Instance, chosenMatch:Models.MatchBase, vl:Models.Components.Component, component:Models.Components.Component):void => {
+
+ if (chosenMatch) {
+ if (chosenMatch && chosenMatch instanceof Models.MatchReqToReq) {
+ this.createVlinks(cy, component, chosenMatch, vl); //TODO orit implement
+ }
+ if (chosenMatch && chosenMatch instanceof Models.MatchReqToCapability) {
+ this.createSimpleLink(chosenMatch, cy, component);
+ }
+ }
+ };
+
+
+ /**
+ * Filters the matches for UCPE links so that shown requirements and capabilites are only related to the selected ucpe-cp
+ * @param fromNode
+ * @param toNode
+ * @param matchesArray
+ * @returns {Array<Models.MatchBase>}
+ */
+ public filterUcpeLinks(fromNode: Models.Graph.CompositionCiNodeBase, toNode: Models.Graph.CompositionCiNodeBase, matchesArray: Array<Models.MatchBase>): any {
+
+ let matchLink: Array<Models.MatchBase>;
+
+ if (fromNode.isUcpePart) {
+ matchLink = _.filter(matchesArray, (match: Models.MatchBase) => {
+ return match.isOwner(fromNode.id);
+ });
+ }
+
+ if (toNode.isUcpePart) {
+ matchLink = _.filter(matchesArray, (match: Models.MatchBase) => {
+ return match.isOwner(toNode.id);
+ });
+ }
+ return matchLink ? matchLink : matchesArray;
+ }
+
+
+ /**
+ * open the connect link menu if the link drawn is valid - match requirements & capabilities
+ * @param cy
+ * @param fromNode
+ * @param toNode
+ * @returns {any}
+ */
+ public onLinkDrawn(cy:Cy.Instance, fromNode:Cy.CollectionFirstNode, toNode:Cy.CollectionFirstNode):Models.RelationMenuDirectiveObj {
+
+ if(!this.commonGraphUtils.nodeLocationsCompatible(cy, fromNode, toNode)){ return null; }
+ let linkModel:Array<Models.CompositionCiLinkBase> = this.generalGraphUtils.getAllCompositionCiLinks(cy);
+
+ let possibleRelations:Array<Models.MatchBase> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromNode.data().componentInstance,
+ toNode.data().componentInstance, linkModel, this.mp2mpVL); //TODO orit - add p2p and mp2mp
+
+ //filter relations found to limit to specific ucpe-cp
+ possibleRelations = this.filterUcpeLinks(fromNode.data(), toNode.data(), possibleRelations);
+
+ //if found possibleRelations between the nodes we create relation menu directive and open the link menu
+ if (possibleRelations.length) {
+ let menuPosition = this.generalGraphUtils.getLinkMenuPosition(cy, toNode.renderedPoint());
+ return new Models.RelationMenuDirectiveObj(fromNode.data(), toNode.data(), this.mp2mpVL, this.p2pVL, menuPosition, possibleRelations);
+ }
+ return null;
+ };
+
+
+ /**
+ * when we drag instance in to UCPE or out of UCPE - get all links we need to delete - one node in ucpe and one node outside of ucpe
+ * @param node - the node we dragged into or out of the ucpe
+ */
+ public deleteLinksWhenNodeMovedFromOrToUCPE(component:Models.Components.Component, cy:Cy.Instance, nodeMoved:Cy.CollectionNodes, vlsPendingDeletion?:Cy.CollectionNodes):void {
+
+
+ let linksToDelete:Cy.CollectionElements = cy.collection();
+ _.forEach(nodeMoved.neighborhood('node'), (neighborNode)=>{
+
+ if(neighborNode.data().isUcpePart){ //existing connections to ucpe or ucpe-cp - we want to delete even though nodeLocationsCompatible will technically return true
+ linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode)); // This will delete the ucpe-host-link, or the vl-ucpe-link if nodeMoved is vl
+ } else if(!this.commonGraphUtils.nodeLocationsCompatible(cy, nodeMoved, neighborNode)){ //connection to regular node or vl - check if locations are compatible
+ if(!vlsPendingDeletion || !vlsPendingDeletion.intersect(neighborNode).length){ //Check if this is a link to a VL pending deletion, to prevent double deletion of between the node moved and vl
+ linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode));
+ }
+ }
+ });
+
+
+
+ linksToDelete.each((i, link)=>{
+ this.deleteLink(cy, component, false, link);
+ });
+
+ };
+
+
+ /**
+ * Creates a hostedOn link between a VF and UCPE
+ * @param component
+ * @param cy
+ * @param ucpeNode
+ * @param vfNode
+ */
+ public createVfToUcpeLink = (component: Models.Components.Component, cy:Cy.Instance, ucpeNode:Models.Graph.NodeUcpe, vfNode:Models.Graph.CompositionCiNodeVf):void => {
+ let hostedOnMatch:Models.MatchReqToCapability = this.generalGraphUtils.canBeHostedOn(cy, ucpeNode.componentInstance, vfNode.componentInstance);
+ /* create relation */
+ let newRelation = new Models.RelationshipModel();
+ newRelation.fromNode = ucpeNode.id;
+ newRelation.toNode = vfNode.id;
+
+ let link:Models.CompositionCiLinkBase = this.linksFactory.createUcpeHostLink(newRelation);
+ link.relation = hostedOnMatch.matchToRelationModel();
+ this.createLink(link, cy, component);
+ };
+
+
+ /**
+ * Handles click event on links.
+ * If one edge selected: do nothing.
+ /*Two edges selected - always select all
+ /* Three or more edges: first click - select all, secondary click - select single.
+ * @param cy
+ * @param event
+ */
+ public handleLinkClick(cy:Cy.Instance, event : Cy.EventObject) {
+ if(cy.$('edge:selected').length > 2 && event.cyTarget[0].selected()) {
+ cy.$(':selected').unselect();
+ } else {
+
+ let vl: Cy.CollectionNodes = event.cyTarget[0].target('.vl-node');
+ let connectedEdges:Cy.CollectionEdges = vl.connectedEdges();
+ if (vl.length && connectedEdges.length > 1) {
+
+ setTimeout(() => {
+ vl.select();
+ connectedEdges.select();
+ }, 0);
+ }
+ }
+
+ }
+
+
+ /**
+ * Calculates the position for the menu that modifies an existing link
+ * @param event
+ * @param elementWidth
+ * @param elementHeight
+ * @returns {Sdc.Models.Graph.Point}
+ */
+ public calculateLinkMenuPosition(event, elementWidth, elementHeight): Sdc.Models.Graph.Point {
+ let point: Sdc.Models.Graph.Point = new Sdc.Models.Graph.Point(event.originalEvent.x,event.originalEvent.y);
+ if(event.originalEvent.view.screen.height-elementHeight<point.y){
+ point.y = event.originalEvent.view.screen.height-elementHeight;
+ }
+ if(event.originalEvent.view.screen.width-elementWidth<point.x){
+ point.x = event.originalEvent.view.screen.width-elementWidth;
+ }
+ return point;
+ };
+
+
+ /**
+ * Gets the menu that is displayed when you click an existing link.
+ * @param link
+ * @param event
+ * @returns {Models.LinkMenu}
+ */
+ public getModifyLinkMenu(link:Cy.CollectionFirstEdge, event:Cy.EventObject):Models.LinkMenu{
+ let point:Sdc.Models.Graph.Point = this.calculateLinkMenuPosition(event,Sdc.Utils.Constants.GraphUIObjects.MENU_LINK_VL_WIDTH_OFFSET,Sdc.Utils.Constants.GraphUIObjects.MENU_LINK_VL_HEIGHT_OFFSET);
+ let menu:Models.LinkMenu = new Models.LinkMenu(point, true, link);
+ return menu;
+ };
+
+ }
+
+
+
+ CompositionGraphLinkUtils.$inject = [
+ 'LinksFactory',
+ 'LoaderService',
+ 'CompositionGraphGeneralUtils',
+ 'LeftPaletteLoaderService',
+ 'ComponentInstanceFactory',
+ 'NodesFactory',
+ 'CommonGraphUtils',
+ 'MatchCapabilitiesRequirementsUtils'
+ ];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts
new file mode 100644
index 0000000000..95c31d16b1
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts
@@ -0,0 +1,220 @@
+/**
+ * Created by obarda on 11/9/2016.
+ */
+
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils {
+
+ export class CompositionGraphNodesUtils {
+ constructor(private NodesFactory:Sdc.Utils.NodesFactory, private $log:ng.ILogService,
+ private GeneralGraphUtils:Graph.Utils.CompositionGraphGeneralUtils,
+ private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils,
+ private eventListenerService: Services.EventListenerService,
+ private loaderService:Services.LoaderService) {
+
+ }
+
+ /**
+ * Returns component instances for all nodes passed in
+ * @param nodes - Cy nodes
+ * @returns {any[]}
+ */
+ public getAllNodesData(nodes:Cy.CollectionNodes) {
+ return _.map(nodes, (node:Cy.CollectionFirstNode)=> {
+ return node.data();
+ })
+ };
+
+ /**
+ * Deletes component instances on server and then removes it from the graph as well
+ * @param cy
+ * @param component
+ * @param nodeToDelete
+ */
+ public deleteNode(cy: Cy.Instance, component:Models.Components.Component, nodeToDelete:Cy.CollectionNodes):void {
+
+ this.loaderService.showLoader('composition-graph');
+ let onSuccess:(response:Models.ComponentsInstances.ComponentInstance) => void = (response:Models.ComponentsInstances.ComponentInstance) => {
+ console.info('onSuccess', response);
+
+ //if node to delete is a UCPE, remove all children (except UCPE-CPs) and remove their "hostedOn" links
+ if (nodeToDelete.data().isUcpe){
+ _.each(cy.nodes('[?isInsideGroup]'), (node)=>{
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node, nodeToDelete);
+ });
+ }
+
+ //check whether the node is connected to any VLs that only have one other connection. If so, delete that VL as well
+ if(!(nodeToDelete.data() instanceof Sdc.Models.Graph.CompositionCiNodeVl)){
+ let connectedVls:Array<Cy.CollectionFirstNode> = this.getConnectedVlToNode(nodeToDelete);
+ this.handleConnectedVlsToDelete(connectedVls);
+ }
+
+ //update UI
+ cy.remove(nodeToDelete);
+
+ };
+
+ let onFailed:(response:any) => void = (response:any) => {
+ console.info('onFailed', response);
+ };
+
+
+ this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
+ () => component.deleteComponentInstance(nodeToDelete.data().componentInstance.uniqueId).then(onSuccess, onFailed),
+ () => this.loaderService.hideLoader('composition-graph')
+ );
+
+ };
+
+
+ /**
+ * Finds all VLs connected to a single node
+ * @param node
+ * @returns {Array<Cy.CollectionFirstNode>}
+ */
+ public getConnectedVlToNode = (node: Cy.CollectionNodes): Array<Cy.CollectionFirstNode> => {
+ let connectedVls: Array<Cy.CollectionFirstNode> = new Array<Cy.CollectionFirstNode>();
+ _.forEach(node.connectedEdges().connectedNodes(), (node: Cy.CollectionFirstNode) => {
+ if (node.data() instanceof Models.Graph.CompositionCiNodeVl) {
+ connectedVls.push(node);
+ }
+ });
+ return connectedVls;
+ };
+
+
+ /**
+ * Delete all VLs that have only two connected nodes (this function is called when deleting a node)
+ * @param connectedVls
+ */
+ public handleConnectedVlsToDelete = (connectedVls: Array<Cy.CollectionFirstNode>) => {
+ _.forEach(connectedVls, (vlToDelete: Cy.CollectionNodes) => {
+
+ if (vlToDelete.connectedEdges().length === 2) { // if vl connected only to 2 nodes need to delete the vl
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, vlToDelete.data().componentInstance);
+ }
+ });
+ };
+
+
+ /**
+ * This function is called when moving a node in or out of UCPE.
+ * Deletes all connected VLs that have less than 2 valid connections remaining after the move
+ * Returns the collection of vls that are in the process of deletion (async) to prevent duplicate calls while deletion is in progress
+ * @param component
+ * @param cy
+ * @param node - node that was moved in/out of ucpe
+ */
+ public deleteNodeVLsUponMoveToOrFromUCPE = (component:Models.Components.Component, cy:Cy.Instance, node:Cy.CollectionNodes):Cy.CollectionNodes =>{
+ if(node.data() instanceof Models.Graph.CompositionCiNodeVl){ return;}
+
+ let connectedVLsToDelete:Cy.CollectionNodes = cy.collection();
+ _.forEach(node.neighborhood('node'), (connectedNode) => {
+
+ //Find all neighboring nodes that are VLs
+ if(connectedNode.data() instanceof Models.Graph.CompositionCiNodeVl){
+
+ //check VL's neighbors to see if it has 2 or more nodes whose location is compatible with VL (regardless of whether VL is in or out of UCPE)
+ let compatibleNodeCount = 0;
+ let vlNeighborhood = connectedNode.neighborhood('node');
+ _.forEach(vlNeighborhood, (vlNeighborNode)=>{
+ if(this.commonGraphUtils.nodeLocationsCompatible(cy, connectedNode, vlNeighborNode)) {
+ compatibleNodeCount ++;
+ }
+ });
+
+ if(compatibleNodeCount < 2) {
+ connectedVLsToDelete = connectedVLsToDelete.add(connectedNode);
+ }
+ }
+ });
+
+ connectedVLsToDelete.each((i, vlToDelete:Cy.CollectionNodes)=>{
+ this.deleteNode(cy, component, vlToDelete);
+ });
+ return connectedVLsToDelete;
+ };
+
+ /**
+ * This function will update nodes position. if the new position is into or out of ucpe, the node will trigger the ucpe events
+ * @param cy
+ * @param component
+ * @param nodesMoved - the node/multiple nodes now moved by the user
+ */
+ public onNodesPositionChanged = (cy: Cy.Instance, component:Models.Components.Component, nodesMoved: Cy.CollectionNodes): void => {
+
+ if (nodesMoved.length === 0) {
+ return;
+ }
+
+ let isValidMove:boolean = this.GeneralGraphUtils.isGroupValidDrop(cy, nodesMoved);
+ if (isValidMove) {
+
+ this.$log.debug(`composition-graph::ValidDrop:: updating node position`);
+ let instancesToUpdateInNonBlockingAction:Array<Models.ComponentsInstances.ComponentInstance> = new Array<Models.ComponentsInstances.ComponentInstance>();
+
+ _.each(nodesMoved, (node:Cy.CollectionFirstNode)=> { //update all nodes new position
+
+ if(node.data().isUcpePart && !node.data().isUcpe){ return; }//No need to update UCPE-CPs
+
+ //update position
+ let newPosition:Cy.Position = this.commonGraphUtils.getNodePosition(node);
+ node.data().componentInstance.updatePosition(newPosition.x, newPosition.y);
+
+ //check if node moved to or from UCPE
+ let ucpe = this.commonGraphUtils.isInUcpe(node.cy(), node.boundingbox());
+ if(node.data().isInsideGroup || ucpe.length) {
+ this.handleUcpeChildMove(node, ucpe, instancesToUpdateInNonBlockingAction);
+ } else {
+ instancesToUpdateInNonBlockingAction.push(node.data().componentInstance);
+ }
+
+ });
+
+ if (instancesToUpdateInNonBlockingAction.length > 0) {
+ this.GeneralGraphUtils.pushMultipleUpdateComponentInstancesRequestToQueue(false, instancesToUpdateInNonBlockingAction, component);
+ }
+ } else {
+ this.$log.debug(`composition-graph::notValidDrop:: node return to latest position`);
+ //reset nodes position
+ nodesMoved.positions((i, node) => {
+ return {
+ x: +node.data().componentInstance.posX,
+ y: +node.data().componentInstance.posY
+ };
+ })
+ }
+
+ this.GeneralGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(() => {
+ }, () => {
+ this.loaderService.hideLoader('composition-graph');
+ });
+
+ };
+
+ /**
+ * Checks whether the node has been added or removed from UCPE and triggers appropriate events
+ * @param node - node moved
+ * @param ucpeContainer - UCPE container that the node has been moved to. When moving a node out of ucpe, param will be empty
+ * @param instancesToUpdateInNonBlockingAction
+ */
+ public handleUcpeChildMove(node:Cy.CollectionFirstNode, ucpeContainer:Cy.CollectionElements, instancesToUpdateInNonBlockingAction:Array<Models.ComponentsInstances.ComponentInstance>){
+
+ if(node.data().isInsideGroup){
+ if(ucpeContainer.length){ //moving node within UCPE. Simply update position
+ this.commonGraphUtils.updateUcpeChildPosition(<Cy.CollectionNodes>node, ucpeContainer);
+ instancesToUpdateInNonBlockingAction.push(node.data().componentInstance);
+ } else { //removing node from UCPE. Notify observers
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_REMOVE_NODE_FROM_UCPE, node, ucpeContainer);
+ }
+ } else if(!node.data().isInsideGroup && ucpeContainer.length && !node.data().isUcpePart){ //adding node to UCPE
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_INSERT_NODE_TO_UCPE, node, ucpeContainer, true);
+ }
+ }
+
+ }
+
+
+ CompositionGraphNodesUtils.$inject = ['NodesFactory', '$log', 'CompositionGraphGeneralUtils', 'CommonGraphUtils', 'EventListenerService', 'LoaderService'];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts
new file mode 100644
index 0000000000..5a401df317
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts
@@ -0,0 +1,265 @@
+/**
+ * Created by obarda on 1/1/2017.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils {
+
+ export class MatchCapabilitiesRequirementsUtils {
+
+ constructor() {
+ }
+
+
+
+ public static linkable(requirement1:Models.Requirement, requirement2:Models.Requirement, vlCapability:Models.Capability):boolean {
+ return MatchCapabilitiesRequirementsUtils.isMatch(requirement1, vlCapability) && MatchCapabilitiesRequirementsUtils.isMatch(requirement2, vlCapability);
+ };
+
+
+ /**
+ * Shows + icon in corner of each node passed in
+ * @param filteredNodesData
+ * @param cy
+ */
+ public highlightMatchingComponents(filteredNodesData, cy:Cy.Instance) {
+ _.each(filteredNodesData, (data:any) => {
+ let node = cy.getElementById(data.id);
+ cy.emit('showhandle', [node]);
+ });
+ }
+
+ /**
+ * Adds opacity to each node that cannot be linked to hovered node
+ * @param filteredNodesData
+ * @param nodesData
+ * @param cy
+ * @param hoveredNodeData
+ */
+ public fadeNonMachingComponents(filteredNodesData, nodesData, cy:Cy.Instance, hoveredNodeData?) {
+ let fadeNodes = _.xorWith(nodesData, filteredNodesData, (node1, node2) => {
+ return node1.id === node2.id;
+ });
+ if (hoveredNodeData) {
+ _.remove(fadeNodes, hoveredNodeData);
+ }
+ cy.batch(()=> {
+ _.each(fadeNodes, (node) => {
+ cy.getElementById(node.id).style({'background-image-opacity': 0.4});
+ });
+ })
+ }
+
+ /**
+ * Resets all nodes to regular opacity
+ * @param cy
+ */
+ public resetFadedNodes(cy:Cy.Instance) {
+ cy.batch(()=> {
+ cy.nodes().style({'background-image-opacity': 1});
+ })
+ }
+
+ // -------------------------------------------ALL FUNCTIONS NEED REFACTORING---------------------------------------------------------------//
+
+ private static requirementFulfilled(fromNodeId:string, requirement:any, links:Array<Models.CompositionCiLinkBase>):boolean {
+ return _.some(links, {
+ 'relation': {
+ 'fromNode': fromNodeId,
+ 'relationships': [{
+ 'requirementOwnerId': requirement.ownerId,
+ 'requirement': requirement.name,
+ 'relationship': {
+ 'type': requirement.relationship
+ }
+ }
+ ]
+ }
+ });
+ };
+
+ private static isMatch(requirement:Models.Requirement, capability:Models.Capability):boolean {
+ if (capability.type === requirement.capability) {
+ if (requirement.node) {
+ if (_.includes(capability.capabilitySources, requirement.node)) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ private getFromToMatches(requirements1:Models.RequirementsGroup,
+ requirements2:Models.RequirementsGroup,
+ capabilities:Models.CapabilitiesGroup,
+ links:Array<Models.CompositionCiLinkBase>,
+ fromId:string,
+ toId:string,
+ vlCapability?:Models.Capability):Array<Models.MatchBase> {
+ let matches:Array<Models.MatchBase> = new Array<Models.MatchBase>();
+ _.forEach(requirements1, (requirementValue:Array<Models.Requirement>, key) => {
+ _.forEach(requirementValue, (requirement:Models.Requirement) => {
+ if (requirement.name !== "dependency" && !MatchCapabilitiesRequirementsUtils.requirementFulfilled(fromId, requirement, links)) {
+ _.forEach(capabilities, (capabilityValue:Array<Models.Capability>, key) => {
+ _.forEach(capabilityValue, (capability:Models.Capability) => {
+ if (MatchCapabilitiesRequirementsUtils.isMatch(requirement, capability)) {
+ let match:Models.MatchReqToCapability = new Models.MatchReqToCapability(requirement, capability, true, fromId, toId);
+ matches.push(match);
+ }
+ });
+ });
+ if (vlCapability) {
+ _.forEach(requirements2, (requirement2Value:Array<Models.Requirement>, key) => {
+ _.forEach(requirement2Value, (requirement2:Models.Requirement) => {
+ if (!MatchCapabilitiesRequirementsUtils.requirementFulfilled(toId, requirement2, links) && MatchCapabilitiesRequirementsUtils.linkable(requirement, requirement2, vlCapability)) {
+ let match:Models.MatchReqToReq = new Models.MatchReqToReq(requirement, requirement2, true, fromId, toId);
+ matches.push(match);
+ }
+ });
+ });
+ }
+ }
+ });
+ });
+ return matches;
+ }
+
+ private getToFromMatches(requirements:Models.RequirementsGroup, capabilities:Models.CapabilitiesGroup, links:Array<Models.CompositionCiLinkBase>, fromId:string, toId:string):Array<Models.MatchReqToCapability> {
+ let matches:Array<Models.MatchReqToCapability> = [];
+ _.forEach(requirements, (requirementValue:Array<Models.Requirement>, key) => {
+ _.forEach(requirementValue, (requirement:Models.Requirement) => {
+ if (requirement.name !== "dependency" && !MatchCapabilitiesRequirementsUtils.requirementFulfilled(toId, requirement, links)) {
+ _.forEach(capabilities, (capabilityValue:Array<Models.Capability>, key) => {
+ _.forEach(capabilityValue, (capability:Models.Capability) => {
+ if (MatchCapabilitiesRequirementsUtils.isMatch(requirement, capability)) {
+ let match:Models.MatchReqToCapability = new Models.MatchReqToCapability(requirement, capability, false, toId, fromId);
+ matches.push(match);
+ }
+ });
+ });
+ }
+ });
+ });
+ return matches;
+ }
+
+ public getMatchedRequirementsCapabilities(fromComponentInstance:Models.ComponentsInstances.ComponentInstance,
+ toComponentInstance:Models.ComponentsInstances.ComponentInstance,
+ links:Array<Models.CompositionCiLinkBase>,
+ vl?:Models.Components.Component):Array<Models.MatchBase> {//TODO allow for VL array
+ let linkCapability;
+ if (vl) {
+ let linkCapabilities:Array<Models.Capability> = vl.capabilities.findValueByKey('linkable');
+ if (linkCapabilities) {
+ linkCapability = linkCapabilities[0];
+ }
+ }
+ let fromToMatches:Array<Models.MatchBase> = this.getFromToMatches(fromComponentInstance.requirements,
+ toComponentInstance.requirements,
+ toComponentInstance.capabilities,
+ links,
+ fromComponentInstance.uniqueId,
+ toComponentInstance.uniqueId,
+ linkCapability);
+ let toFromMatches:Array<Models.MatchReqToCapability> = this.getToFromMatches(toComponentInstance.requirements,
+ fromComponentInstance.capabilities,
+ links,
+ fromComponentInstance.uniqueId,
+ toComponentInstance.uniqueId);
+
+ return fromToMatches.concat(toFromMatches);
+ }
+
+
+
+
+
+ /**
+ * Step I: Check if capabilities of component match requirements of nodeDataArray
+ * 1. Get component capabilities and loop on each capability
+ * 2. Inside the loop, perform another loop on all nodeDataArray, and fetch the requirements for each one
+ * 3. Loop on the requirements, and verify match (see in code the rules)
+ *
+ * Step II: Check if requirements of component match capabilities of nodeDataArray
+ * 1. Get component requirements and loop on each requirement
+ * 2.
+ *
+ * @param component - this is the hovered resource of the left panel of composition screen
+ * @param nodeDataArray - Array of resource instances that are on the canvas
+ * @param links -getMatchedRequirementsCapabilities
+ * @param vl -
+ * @returns {any[]|T[]}
+ */
+ public findByMatchingCapabilitiesToRequirements(component:Models.Components.Component,
+ nodeDataArray:Array<Models.Graph.CompositionCiNodeBase>,
+ links:Array<Models.CompositionCiLinkBase>,
+ vl?:Models.Components.Component):Array<any> {//TODO allow for VL array
+ let res = [];
+
+ // STEP I
+ {
+ let capabilities:any = component.capabilities;
+ _.forEach(capabilities, (capabilityValue:Array<any>, capabilityKey)=> {
+ _.forEach(capabilityValue, (capability)=> {
+ _.forEach(nodeDataArray, (node:Models.Graph.CompositionCiNodeBase)=> {
+ if (node && node.componentInstance) {
+ let requirements:any = node.componentInstance.requirements;
+ let fromNodeId:string = node.componentInstance.uniqueId;
+ _.forEach(requirements, (requirementValue:Array<any>, requirementKey)=> {
+ _.forEach(requirementValue, (requirement)=> {
+ if (requirement.name !== "dependency" && MatchCapabilitiesRequirementsUtils.isMatch(requirement, capability)
+ && !MatchCapabilitiesRequirementsUtils.requirementFulfilled(fromNodeId, requirement, links)) {
+ res.push(node);
+ }
+ });
+ });
+ }
+ });
+ });
+ });
+ }
+
+ // STEP II
+ {
+ let requirements:any = component.requirements;
+ let fromNodeId:string = component.uniqueId;
+ let linkCapability:Array<Models.Capability> = vl ? vl.capabilities.findValueByKey('linkable') : undefined;
+
+ _.forEach(requirements, (requirementValue:Array<any>, requirementKey)=> {
+ _.forEach(requirementValue, (requirement)=> {
+ if (requirement.name !== "dependency" && !MatchCapabilitiesRequirementsUtils.requirementFulfilled(fromNodeId, requirement, links)) {
+ _.forEach(nodeDataArray, (node:any)=> {
+ if (node && node.componentInstance && node.category !== 'groupCp') {
+ let capabilities:any = node.componentInstance.capabilities;
+ _.forEach(capabilities, (capabilityValue:Array<any>, capabilityKey)=> {
+ _.forEach(capabilityValue, (capability)=> {
+ if (MatchCapabilitiesRequirementsUtils.isMatch(requirement, capability)) {
+ res.push(node);
+ }
+ });
+ });
+ if (linkCapability) {
+ let linkRequirements = node.componentInstance.requirements;
+ _.forEach(linkRequirements, (value:Array<any>, key)=> {
+ _.forEach(value, (linkRequirement)=> {
+ if (!MatchCapabilitiesRequirementsUtils.requirementFulfilled(node.componentInstance.uniqueId, linkRequirement, links)
+ && MatchCapabilitiesRequirementsUtils.linkable(requirement, linkRequirement, linkCapability[0])) {
+ res.push(node);
+ }
+ });
+ });
+ }
+ }
+ });
+ }
+ });
+ });
+ }
+
+ return _.uniq(res);
+ };
+ }
+
+ MatchCapabilitiesRequirementsUtils.$inject = [];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts
new file mode 100644
index 0000000000..d6d4aef374
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts
@@ -0,0 +1,114 @@
+/**
+ * Created by obarda on 12/19/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+
+ import Util = jasmine.Util;
+
+ interface IDeploymentGraphScope extends ng.IScope {
+ component:Models.Components.Component;
+ }
+
+ export class DeploymentGraph implements ng.IDirective {
+ private _cy:Cy.Instance;
+
+ constructor(private NodesFactory:Utils.NodesFactory, private commonGraphUtils:Graph.Utils.CommonGraphUtils,
+ private deploymentGraphGeneralUtils:Graph.Utils.DeploymentGraphGeneralUtils, private ComponentInstanceFactory: Sdc.Utils.ComponentInstanceFactory) {
+ }
+
+ restrict = 'E';
+ templateUrl = '/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.html';
+ scope = {
+ component: '=',
+ isViewOnly: '='
+ };
+
+ link = (scope:IDeploymentGraphScope, el:JQuery) => {
+ if(scope.component.isResource()) {
+ this.loadGraph(scope, el);
+ this.registerGraphEvents();
+ }
+ };
+
+
+ public initGraphNodes = (cy:Cy.Instance, component:Models.Components.Component):void => {
+ if (component.groups) { // Init module nodes
+ _.each(component.groups, (groupModule:Models.Module) => {
+ let moduleNode = this.NodesFactory.createModuleNode(groupModule);
+ this.commonGraphUtils.addNodeToGraph(cy, moduleNode);
+
+ });
+ }
+ _.each(component.componentInstances, (instance:Models.ComponentsInstances.ComponentInstance) => { // Init component instance nodes
+ let componentInstanceNode = this.NodesFactory.createNode(instance);
+ componentInstanceNode.parent = this.deploymentGraphGeneralUtils.findInstanceModule(component.groups, instance.uniqueId);
+ if (componentInstanceNode.parent) { // we are not drawing instances that are not a part of a module
+ this.commonGraphUtils.addComponentInstanceNodeToGraph(cy, componentInstanceNode);
+ }
+ });
+
+ // This is a special functionality to pass the cytoscape default behavior - we can't create Parent module node without children's
+ // so we must add an empty dummy child node
+ _.each(this._cy.nodes('[?isGroup]'), (moduleNode: Cy.CollectionFirstNode) => {
+ if (!moduleNode.isParent()) {
+ let dummyInstance = this.ComponentInstanceFactory.createEmptyComponentInstance();
+ let componentInstanceNode = this.NodesFactory.createNode(dummyInstance);
+ componentInstanceNode.parent = moduleNode.id();
+ let dummyNode = this.commonGraphUtils.addNodeToGraph(cy, componentInstanceNode, moduleNode.position());
+ dummyNode.addClass('dummy-node');
+ }
+ })
+ };
+
+ private registerGraphEvents() {
+
+ this._cy.on('afterExpand', (event) => {
+ event.cyTarget.qtip({});
+ });
+
+ this._cy.on('afterCollapse', (event) => {
+ this.commonGraphUtils.initNodeTooltip(event.cyTarget);
+ });
+ }
+
+ private loadGraph = (scope:IDeploymentGraphScope, el:JQuery) => {
+
+ let graphEl = el.find('.sdc-deployment-graph-wrapper');
+ this._cy = cytoscape({
+ container: graphEl,
+ style: Sdc.Graph.Utils.ComponentIntanceNodesStyle.getCompositionGraphStyle().concat(Sdc.Graph.Utils.ModulesNodesStyle.getModuleGraphStyle()),
+ zoomingEnabled: false,
+ selectionType: 'single',
+
+ });
+
+ //adding expand collapse extension
+ this._cy.expandCollapse({
+ layoutBy: {
+ name: "grid",
+ animate: true,
+ randomize: false,
+ fit: true
+ },
+ fisheye: false,
+ undoable: false,
+ expandCollapseCueSize: 18,
+ expandCueImage: Sdc.Utils.Constants.IMAGE_PATH + '/styles/images/resource-icons/' + 'closeModule.png',
+ collapseCueImage: Sdc.Utils.Constants.IMAGE_PATH + '/styles/images/resource-icons/' + 'openModule.png',
+ expandCollapseCueSensitivity: 2,
+ cueOffset: -20
+ });
+
+ this.initGraphNodes(this._cy, scope.component); //creating instances nodes
+ this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations);
+ this._cy.collapseAll();
+ };
+
+ public static factory = (NodesFactory:Utils.NodesFactory, CommonGraphUtils:Graph.Utils.CommonGraphUtils, DeploymentGraphGeneralUtils:Graph.Utils.DeploymentGraphGeneralUtils, ComponentInstanceFactory: Utils.ComponentInstanceFactory) => {
+ return new DeploymentGraph(NodesFactory, CommonGraphUtils, DeploymentGraphGeneralUtils, ComponentInstanceFactory)
+ }
+ }
+
+ DeploymentGraph.factory.$inject = ['NodesFactory', 'CommonGraphUtils', 'DeploymentGraphGeneralUtils', 'ComponentInstanceFactory'];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.html b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.html
new file mode 100644
index 0000000000..55e1c131f4
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.html
@@ -0,0 +1,2 @@
+<div class="sdc-deployment-graph-wrapper" ng-class="{'view-only':isViewOnly}">
+</div> \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.less b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.less
new file mode 100644
index 0000000000..ff8fc46380
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-graph.less
@@ -0,0 +1,14 @@
+deployment-graph {
+ display: block;
+ height:100%;
+ width: 100%;
+
+ .sdc-deployment-graph-wrapper {
+ height:100%;
+ width: 100%;
+ }
+
+ .view-only{
+ background-color:rgb(248, 248, 248);
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts
new file mode 100644
index 0000000000..3ad9da56be
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts
@@ -0,0 +1,24 @@
+/**
+ * Created by obarda on 12/21/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Graph.Utils {
+
+ export class DeploymentGraphGeneralUtils {
+
+ constructor() {
+
+ }
+
+ public findInstanceModule = (groupsArray:Array<Models.Module>, componentInstanceId:string):string => {
+ let parentGroup:Sdc.Models.Module = _.find(groupsArray, (group:Sdc.Models.Module) => {
+ return _.find(group.members, (member) => {
+ return member === componentInstanceId;
+ });
+ });
+ return parentGroup ? parentGroup.uniqueId : "";
+ };
+ }
+
+ DeploymentGraphGeneralUtils.$inject = [];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/image-creator/image-creator.service.ts b/catalog-ui/app/scripts/directives/graphs-v2/image-creator/image-creator.service.ts
new file mode 100644
index 0000000000..e3b17e163d
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/image-creator/image-creator.service.ts
@@ -0,0 +1,46 @@
+module Sdc.Utils {
+ export class ImageCreatorService {
+ static '$inject' = ['$q'];
+ private _canvas: HTMLCanvasElement;
+
+ constructor(private $q: ng.IQService) {
+ this._canvas = <HTMLCanvasElement>$('<canvas>')[0];
+ this._canvas.setAttribute('style', 'display:none');
+
+ let body = document.getElementsByTagName('body')[0];
+ body.appendChild(this._canvas);
+ }
+
+ getImageBase64(imageBaseUri: string, imageLayerUri: string): ng.IPromise<string> {
+ let deferred = this.$q.defer();
+ let imageBase = new Image();
+ let imageLayer = new Image();
+ let imagesLoaded = 0;
+ let onImageLoaded = () => {
+ imagesLoaded++;
+
+ if (imagesLoaded < 2) {
+ return;
+ }
+ this._canvas.setAttribute('width', imageBase.width.toString());
+ this._canvas.setAttribute('height', imageBase.height.toString());
+
+ let canvasCtx = this._canvas.getContext('2d');
+ canvasCtx.clearRect(0, 0, this._canvas.width, this._canvas.height);
+
+ canvasCtx.drawImage(imageBase, 0, 0, imageBase.width, imageBase.height);
+ canvasCtx.drawImage(imageLayer, imageBase.width - imageLayer.width, 0, imageLayer.width, imageLayer.height);
+
+ let base64Image = this._canvas.toDataURL();
+ deferred.resolve(base64Image);
+ };
+
+ imageBase.onload = onImageLoaded;
+ imageLayer.onload = onImageLoaded;
+ imageBase.src = imageBaseUri;
+ imageLayer.src = imageLayerUri;
+
+ return deferred.promise;
+ }
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts b/catalog-ui/app/scripts/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts
new file mode 100644
index 0000000000..26c042611c
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/palette/interfaces/i-dragdrop-event.d.ts
@@ -0,0 +1,7 @@
+interface IDragDropEvent extends JQueryEventObject {
+ dataTransfer: any;
+ toElement: {
+ naturalWidth: number;
+ naturalHeight: number;
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.directive.ts b/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.directive.ts
new file mode 100644
index 0000000000..c00da6d1df
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.directive.ts
@@ -0,0 +1,327 @@
+/// <reference path="../../../references"/>
+
+module Sdc.Directives {
+ import Dictionary = Sdc.Utils.Dictionary;
+ import GRAPH_EVENTS = Sdc.Utils.Constants.GRAPH_EVENTS;
+ import ImageCreatorService = Sdc.Utils.ImageCreatorService;
+ interface IPaletteScope {
+ components: any;
+ currentComponent: any;
+ model: any;
+ displaySortedCategories: any;
+ expandedSection: string;
+
+ p2pVL: Models.Components.Component;
+ mp2mpVL: Models.Components.Component;
+ vlType: string;
+ dragElement: JQuery;
+ dragbleNode: {
+ event: JQueryEventObject,
+ components: Models.DisplayComponent,
+ ui: any
+ }
+
+ sectionClick: (section: string)=>void;
+ searchComponents: (searchText: string)=>void;
+ onMouseOver: (displayComponent: Models.DisplayComponent)=>void;
+ onMouseOut: (displayComponent: Models.DisplayComponent)=>void;
+ dragStartCallback: (event: JQueryEventObject, ui, displayComponent: Models.DisplayComponent)=>void;
+ dragStopCallback: ()=>void;
+ onDragCallback: (event:JQueryEventObject) => void;
+ setElementTemplate: (e: JQueryEventObject)=>void;
+
+ isOnDrag: boolean;
+ isDragable: boolean;
+ isLoading: boolean;
+ isViewOnly: boolean;
+ }
+
+ export class Palette implements ng.IDirective {
+ constructor(private $log: ng.ILogService,
+ private LeftPaletteLoaderService,
+ private sdcConfig,
+ private ComponentFactory,
+ private ComponentInstanceFactory: Utils.ComponentInstanceFactory,
+ private NodesFactory: Utils.NodesFactory,
+ private CompositionGraphGeneralUtils: Graph.Utils.CompositionGraphGeneralUtils,
+ private EventListenerService: Services.EventListenerService,
+ private sdcMenu: Models.IAppMenu) {
+
+ }
+
+ private fetchingComponentFromServer: boolean = false;
+ private nodeHtmlSubstitute: JQuery;
+
+ scope = {
+ components: '=',
+ currentComponent: '=',
+ isViewOnly: '=',
+ isLoading: '='
+ };
+ restrict = 'E';
+ templateUrl = '/app/scripts/directives/graphs-v2/palette/palette.html';
+
+ link = (scope: IPaletteScope, el: JQuery) => {
+ this.nodeHtmlSubstitute = $('<div class="node-substitute"><span></span><img /></div>');
+ el.append(this.nodeHtmlSubstitute);
+
+ this.initComponents(scope);
+ this.initScopeVls(scope);
+ this.initEvents(scope);
+ this.initDragEvents(scope);
+ this._initExpandedSection(scope, '');
+ };
+
+ private leftPanelResourceFilter(resourcesNotAbstract: Array<Models.DisplayComponent>, resourceFilterTypes: Array<string>): Array<Models.DisplayComponent> {
+ let filterResources = _.filter(resourcesNotAbstract, (component) => {
+ return resourceFilterTypes.indexOf(component.getComponentSubType()) > -1;
+ });
+ return filterResources;
+ }
+
+ private initLeftPanel(leftPanelComponents: Array<Models.DisplayComponent>, resourceFilterTypes: Array<string>): Models.LeftPanelModel {
+ let leftPanelModel = new Models.LeftPanelModel();
+
+ if (resourceFilterTypes && resourceFilterTypes.length) {
+ leftPanelComponents = this.leftPanelResourceFilter(leftPanelComponents, resourceFilterTypes);
+ }
+ leftPanelModel.numberOfElements = leftPanelComponents && leftPanelComponents.length || 0;
+
+ if (leftPanelComponents && leftPanelComponents.length) {
+
+ let categories: any = _.groupBy(leftPanelComponents, 'mainCategory');
+ for (let category in categories)
+ categories[category] = _.groupBy(categories[category], 'subCategory');
+
+ leftPanelModel.sortedCategories = categories;
+ }
+ return leftPanelModel;
+ }
+
+ private initScopeVls(scope: IPaletteScope): void {
+ let vls = this.LeftPaletteLoaderService.getFullDataComponentList(Utils.Constants.ResourceType.VL);
+ scope.vlType = null;
+ vls.forEach((item) => {
+ let key = _.find(Object.keys(item.capabilities), (key) => {
+ return _.includes(key.toLowerCase(), 'linkable');
+ });
+ let linkable = item.capabilities[key];
+ if (linkable) {
+ if (linkable[0].maxOccurrences == '2') {
+ scope.p2pVL = _.find(vls, (component: Models.Components.Component) => {
+ return component.uniqueId === item.uniqueId;
+ });
+
+ } else {//assuming unbounded occurrences
+ scope.mp2mpVL = _.find(vls, (component: Models.Components.Component) => {
+ return component.uniqueId === item.uniqueId;
+ });
+ }
+ }
+ });
+ };
+
+ private initEvents(scope: IPaletteScope) {
+ /**
+ *
+ * @param section
+ */
+ scope.sectionClick = (section: string) => {
+ if (section === scope.expandedSection) {
+ scope.expandedSection = '';
+ return;
+ }
+ scope.expandedSection = section;
+ };
+
+ scope.onMouseOver = (displayComponent: Models.DisplayComponent) => {
+ if (scope.isOnDrag) {
+ return;
+ }
+ scope.isOnDrag = true;
+
+ this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, displayComponent);
+ this.$log.debug('palette::onMouseOver:: fired');
+
+ if (this.CompositionGraphGeneralUtils.componentRequirementsAndCapabilitiesCaching.containsKey(displayComponent.uniqueId)) {
+ this.$log.debug(`palette::onMouseOver:: component id ${displayComponent.uniqueId} found in cache`);
+ let cacheComponent: Models.Components.Component = this.CompositionGraphGeneralUtils.componentRequirementsAndCapabilitiesCaching.getValue(displayComponent.uniqueId);
+
+ //TODO: Danny: fire event to highlight matching nodes
+ //showMatchingNodes(cacheComponent);
+ return;
+ }
+
+ this.$log.debug(`palette::onMouseOver:: component id ${displayComponent.uniqueId} not found in cache, initiating server get`);
+ // This will bring the component from the server including requirements and capabilities
+ // Check that we do not fetch many times, because only in the success we add the component to componentRequirementsAndCapabilitiesCaching
+ if (this.fetchingComponentFromServer) {
+ return;
+ }
+
+ this.fetchingComponentFromServer = true;
+ this.ComponentFactory.getComponentFromServer(displayComponent.componentSubType, displayComponent.uniqueId)
+ .then((component: Models.Components.Component) => {
+ this.$log.debug(`palette::onMouseOver:: component id ${displayComponent.uniqueId} fetch success`);
+ this.LeftPaletteLoaderService.updateSpecificComponentLeftPalette(component, scope.currentComponent.componentType);
+ this.CompositionGraphGeneralUtils.componentRequirementsAndCapabilitiesCaching.setValue(component.uniqueId, component);
+ this.fetchingComponentFromServer = false;
+
+ //TODO: Danny: fire event to highlight matching nodes
+ //showMatchingNodes(component);
+ })
+ .catch(() => {
+ this.$log.debug('palette::onMouseOver:: component id fetch error');
+ this.fetchingComponentFromServer = false;
+ });
+
+
+ };
+
+ scope.onMouseOut = () => {
+ scope.isOnDrag = false;
+ this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT);
+ }
+ }
+
+ private initComponents(scope: IPaletteScope) {
+ scope.searchComponents = (searchText: any): void => {
+ scope.displaySortedCategories = this._searchComponents(searchText, scope.model.sortedCategories);
+ this._initExpandedSection(scope, searchText);
+ };
+
+ scope.isDragable = scope.currentComponent.isComplex();
+ let entityType: string = scope.currentComponent.componentType.toLowerCase();
+ let resourceFilterTypes: Array<string> = this.sdcConfig.resourceTypesFilter[entityType];
+
+ scope.components = this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent.componentType);
+ scope.model = this.initLeftPanel(scope.components, resourceFilterTypes);
+ scope.displaySortedCategories = angular.copy(scope.model.sortedCategories);
+ }
+
+ private _initExpandedSection(scope: IPaletteScope, searchText: string): void {
+ if (searchText == '') {
+ let isContainingCategory: boolean = false;
+ let categoryToExpand: string;
+ if (scope.currentComponent && scope.currentComponent.categories && scope.currentComponent.categories[0]) {
+ categoryToExpand = this.sdcMenu.categoriesDictionary[scope.currentComponent.categories[0].name];
+ for (let category in scope.model.sortedCategories) {
+ if (categoryToExpand == category) {
+ isContainingCategory = true;
+ break;
+ }
+ }
+ }
+ isContainingCategory ? scope.expandedSection = categoryToExpand : scope.expandedSection = 'Generic';
+ }
+ else {
+ scope.expandedSection = Object.keys(scope.displaySortedCategories).sort()[0];
+ }
+ };
+
+ private initDragEvents(scope: IPaletteScope) {
+ scope.dragStartCallback = (event: IDragDropEvent, ui, displayComponent: Models.DisplayComponent): void => {
+ if (scope.isLoading || !scope.isDragable || scope.isViewOnly) {
+ return;
+ }
+
+ let component = _.find(this.LeftPaletteLoaderService.getFullDataComponentListWithVls(scope.currentComponent.componentType), (componentFullData: Models.DisplayComponent) => {
+ return displayComponent.uniqueId === componentFullData.uniqueId;
+ });
+ this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, scope.dragElement, component);
+
+ scope.isOnDrag = true;
+
+
+
+ // this.graphUtils.showMatchingNodes(component, myDiagram, scope.sdcConfig.imagesPath);
+ // document.addEventListener('mousemove', moveOnDocument);
+ event.dataTransfer.component = component;
+ };
+
+ scope.dragStopCallback = () => {
+ scope.isOnDrag = false;
+ };
+
+ scope.onDragCallback = (event:IDragDropEvent): void => {
+ this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, event);
+ };
+ scope.setElementTemplate = (e) => {
+ let dragComponent: Models.Components.Component = _.find(this.LeftPaletteLoaderService.getFullDataComponentListWithVls(scope.currentComponent.componentType),
+ (fullComponent: Models.Components.Component) => {
+ return (<any>angular.element(e.currentTarget).scope()).component.uniqueId === fullComponent.uniqueId;
+ });
+ let componentInstance: Models.ComponentsInstances.ComponentInstance = this.ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent);
+ let node: Models.Graph.CompositionCiNodeBase = this.NodesFactory.createNode(componentInstance);
+
+ // myDiagram.dragFromPalette = node;
+ this.nodeHtmlSubstitute.find("img").attr('src', node.img);
+ scope.dragElement = this.nodeHtmlSubstitute.clone().show();
+
+ return scope.dragElement;
+ };
+ }
+
+ private _searchComponents = (searchText: string, categories: any): void => {
+ let displaySortedCategories = angular.copy(categories);
+ if (searchText != '') {
+ angular.forEach(categories, function (category: any, categoryKey) {
+
+ angular.forEach(category, function (subcategory: Array<Models.DisplayComponent>, subcategoryKey) {
+ let filteredResources = [];
+ angular.forEach(subcategory, function (component: Models.DisplayComponent) {
+
+ let resourceFilterTerm: string = component.searchFilterTerms;
+ if (resourceFilterTerm.indexOf(searchText.toLowerCase()) >= 0) {
+ filteredResources.push(component);
+ }
+ });
+ if (filteredResources.length > 0) {
+ displaySortedCategories[categoryKey][subcategoryKey] = filteredResources;
+ }
+ else {
+ delete displaySortedCategories[categoryKey][subcategoryKey];
+ }
+ });
+ if (!(Object.keys(displaySortedCategories[categoryKey]).length > 0)) {
+ delete displaySortedCategories[categoryKey];
+ }
+
+ });
+ }
+ return displaySortedCategories;
+ };
+
+ public static factory = ($log,
+ LeftPaletteLoaderService,
+ sdcConfig,
+ ComponentFactory,
+ ComponentInstanceFactory,
+ NodesFactory,
+ CompositionGraphGeneralUtils,
+ EventListenerService,
+ sdcMenu) => {
+ return new Palette($log,
+ LeftPaletteLoaderService,
+ sdcConfig,
+ ComponentFactory,
+ ComponentInstanceFactory,
+ NodesFactory,
+ CompositionGraphGeneralUtils,
+ EventListenerService,
+ sdcMenu);
+ };
+ }
+
+ Palette.factory.$inject = [
+ '$log',
+ 'LeftPaletteLoaderService',
+ 'sdcConfig',
+ 'ComponentFactory',
+ 'ComponentInstanceFactory',
+ 'NodesFactory',
+ 'CompositionGraphGeneralUtils',
+ 'EventListenerService',
+ 'sdcMenu'
+ ];
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.html b/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.html
new file mode 100644
index 0000000000..a8dd827927
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.html
@@ -0,0 +1,59 @@
+<div class="w-sdc-designer-leftbar">
+ <div class="w-sdc-designer-leftbar-title">Elements <span class="w-sdc-designer-leftbar-title-count">{{model.numberOfElements}}</span>
+ </div>
+
+ <div class="w-sdc-designer-leftbar-search">
+ <input type="text" class="w-sdc-designer-leftbar-search-input" placeholder="Search..."
+ data-ng-model="searchText" data-ng-change="searchComponents(searchText)"
+ ng-model-options="{ debounce: 500 }" data-tests-id="searchAsset"/>
+ <span class="w-sdc-search-icon leftbar" data-ng-class="{'cancel':searchText, 'magnification':!searchText}"
+ data-ng-click="searchText=''; searchComponents('',categories)"></span>
+ </div>
+ <div class="i-sdc-designer-leftbar-section"
+ data-ng-repeat="(entityCategory, objCategory) in displaySortedCategories track by $index"
+ data-ng-class="{'expanded': expandedSection.indexOf(entityCategory) !== -1}">
+ <div class="i-sdc-designer-leftbar-section-title pointer" data-ng-click="sectionClick(entityCategory)"
+ data-tests-id="leftbar-section-title-{{entityCategory}}">
+ {{entityCategory}}
+ <div class="i-sdc-designer-leftbar-section-title-icon"></div>
+ </div>
+ <div class="i-sdc-designer-leftbar-section-content"
+ data-ng-repeat="(subCategory, components) in objCategory track by $index">
+ <div class="i-sdc-designer-leftbar-section-content-subcat i-sdc-designer-leftbar-section-content-item">
+ {{subCategory}}
+ </div>
+ <div class="i-sdc-designer-leftbar-section-content-item"
+ data-ng-class="{'default-pointer': isViewOnly}"
+ data-ng-mouseover="!isViewOnly && onMouseOver(component)"
+ data-ng-mouseleave="!isViewOnly && onMouseOut()"
+ data-drag="{{!isViewOnly}}"
+ data-jqyoui-options="{revert: 'invalid', helper:setElementTemplate, appendTo:'body', cursorAt: {left:38, top: 38}, cursor:'move'}"
+ jqyoui-draggable="{index:{{$index}},animate:true,onStart:'dragStartCallback(component)',onStop:'dragStopCallback()', onDrag:'onDragCallback()'}"
+ data-ng-repeat="component in components | orderBy: 'displayName' track by $index"
+ data-tests-id={{component.displayName}}>
+ <div class="i-sdc-designer-leftbar-section-content-item-icon-ph">
+ <div class="medium {{component.iconClass}}"
+ data-tests-id="leftbar-section-content-item-{{component.displayName}}">
+ <div class="{{component.certifiedIconClass}}" uib-tooltip="Not certified"
+ tooltip-class="uib-custom-tooltip" tooltip-placement="bottom" tooltip-popup-delay="700">
+ </div>
+ </div>
+ </div>
+ <div class="i-sdc-designer-leftbar-section-content-item-info">
+ <span class="i-sdc-designer-leftbar-section-content-item-info-title"
+ uib-tooltip="{{component.displayName}}" tooltip-class="uib-custom-tooltip"
+ tooltip-placement="bottom" tooltip-popup-delay="700">
+ {{component.displayName}}</span>
+ <div class="i-sdc-designer-leftbar-section-content-item-info-text">
+ V.{{component.version}}
+ </div>
+ <div class="i-sdc-designer-leftbar-section-content-item-info-text"> Type:
+ {{component.componentSubType}}
+ <a data-ng-click="openViewerModal(component)"
+ class="i-sdc-designer-leftbar-section-content-item-info-text-link hand">More</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.less b/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.less
new file mode 100644
index 0000000000..85657a43a5
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/palette/palette.less
@@ -0,0 +1,92 @@
+.drag-icon-border{
+ border: 7px solid red;
+ border-radius: 500px;
+ -webkit-border-radius: 500px;
+ -moz-border-radius: 500px;
+ width: 53px;
+ height: 53px;
+}
+
+.drag-icon-circle{
+ width: 60px;
+ height: 60px;
+ -webkit-border-radius: 50%;
+ -moz-border-radius: 50%;
+ border-radius: 50%;
+ position: relative;
+
+}
+
+
+@green-shadow: rgba(29, 154, 149, 0.3);
+@red-shadow: rgba(218, 31, 61, 0.3);
+.drag-icon-circle .sprite-resource-icons {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+}
+
+.drag-icon-circle.red {
+ background: @red-shadow;
+}
+
+.drag-icon-circle.green {
+ background: @green-shadow;
+}
+
+
+.node-substitute {
+ display: none;
+ position: absolute;
+ z-index: 9999;
+ height: 80px;
+ width: 80px;
+ border-radius: 50%;
+ text-align: center;
+
+ span {
+ display: inline-block;
+ vertical-align: middle;
+ height: 100%;
+ }
+
+ img {
+ height: 40px;
+ width: 40px;
+ box-shadow: 0 0 0 10px @green-shadow;
+ border-radius: 50%;
+
+ -webkit-user-drag: none;
+ -moz-user-drag: none;
+ user-drag: none;
+ }
+ &.red img {
+ box-shadow: 0 0 0 10px @red-shadow;
+ }
+ &.bounce img {
+ -moz-animation:bounceOut 0.3s linear;
+ -webkit-animation:bounceOut 0.3s linear;
+ animation:bounceOut 0.3s linear;
+ }
+}
+
+@keyframes bounceOut {
+ 0%{ box-shadow: 0 0 0 10px @green-shadow; width: 40px; height: 40px; }
+ 60%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; }
+ 85%{ box-shadow: 0 0 0 0px @green-shadow; width: 75px; height: 75px; }
+ 100%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; }
+}
+
+@-moz-keyframes bounceOut {
+ 0%{ box-shadow: 0 0 0 10px @green-shadow; width: 40px; height: 40px; }
+ 60%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; }
+ 85%{ box-shadow: 0 0 0 0px @green-shadow; width: 75px; height: 75px; }
+ 100%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; }
+}
+
+@-webkit-keyframes bounceOut {
+ 0%{ box-shadow: 0 0 0 10px @green-shadow; width: 40px; height: 40px; }
+ 60%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; }
+ 85%{ box-shadow: 0 0 0 0px @green-shadow; width: 75px; height: 75px; }
+ 100%{ box-shadow: 0 0 0 0px @green-shadow; width: 60px; height: 60px; }
+}
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.html b/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.html
new file mode 100644
index 0000000000..a0a9e4af27
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.html
@@ -0,0 +1,63 @@
+<div class="link-menu-open" data-tests-id="link-menu-open" data-ng-show="isLinkMenuOpen" ng-style="{left: relationMenuDirectiveObj.menuPosition.x, top: relationMenuDirectiveObj.menuPosition.y}" clicked-outside="{onClickedOutside: 'hideRelationMatch()', clickedOutsideEnable: 'isLinkMenuOpen'}" >
+ <h4 sdc-smart-tooltip>{{relationMenuDirectiveObj.leftSideLink.componentInstance.name | resourceName}}</h4>
+ <h4 sdc-smart-tooltip>{{relationMenuDirectiveObj.rightSideLink.componentInstance.name | resourceName}}</h4>
+
+ <p>Select one of the options below to connect</p>
+
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div class="inner-title" data-ng-show="hasMatchesToShow(relationMenuDirectiveObj.leftSideLink.requirements, relationMenuDirectiveObj.rightSideLink.selectedMatch)">Requirements</div>
+ <div class="link-item" data-tests-id="link-item-requirements" data-ng-repeat="(req ,matchArr) in relationMenuDirectiveObj.leftSideLink.requirements"
+ data-ng-click="relationMenuDirectiveObj.leftSideLink.selectMatchArr(matchArr); updateSelectionText()"
+ data-ng-show="showMatch(relationMenuDirectiveObj.rightSideLink.selectedMatch, matchArr)"
+ data-ng-class="{ 'selected': relationMenuDirectiveObj.leftSideLink.selectedMatch === matchArr}">
+ <div sdc-smart-tooltip>{{matchArr[0].requirement.getFullTitle()}}</div>
+ </div>
+
+ <div class="inner-title" data-ng-show="hasMatchesToShow(relationMenuDirectiveObj.leftSideLink.capabilities, relationMenuDirectiveObj.rightSideLink.selectedMatch)">Capabilities</div>
+ <div class="link-item" data-tests-id="link-item-capabilities" data-ng-repeat="(cap, matchArr) in relationMenuDirectiveObj.leftSideLink.capabilities"
+ data-ng-click="relationMenuDirectiveObj.leftSideLink.selectMatchArr(matchArr); updateSelectionText()"
+ data-ng-show="showMatch(relationMenuDirectiveObj.rightSideLink.selectedMatch, matchArr)"
+ data-ng-class="{ 'selected': relationMenuDirectiveObj.leftSideLink.selectedMatch === matchArr}">
+ <div sdc-smart-tooltip>{{matchArr[0].capability.getFullTitle()}}</div>
+ </div>
+ </perfect-scrollbar>
+
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div class="inner-title" data-ng-show="hasMatchesToShow(relationMenuDirectiveObj.rightSideLink.requirements, relationMenuDirectiveObj.leftSideLink.selectedMatch)">Requirements</div>
+ <div class="link-item" data-tests-id="link-item-requirements" data-ng-repeat="(req, matchArr) in relationMenuDirectiveObj.rightSideLink.requirements"
+ data-ng-click="relationMenuDirectiveObj.rightSideLink.selectMatchArr(matchArr); updateSelectionText()"
+ data-ng-show="showMatch(relationMenuDirectiveObj.leftSideLink.selectedMatch, matchArr)"
+ data-ng-class="{ 'selected': relationMenuDirectiveObj.rightSideLink.selectedMatch === matchArr}">
+ <div sdc-smart-tooltip>{{matchArr[0].secondRequirement ? matchArr[0].secondRequirement.getFullTitle() : matchArr[0].requirement.getFullTitle()}}</div>
+ </div>
+
+ <div class="inner-title" data-ng-show="hasMatchesToShow(relationMenuDirectiveObj.rightSideLink.capabilities, relationMenuDirectiveObj.leftSideLink.selectedMatch)">Capabilities</div>
+ <div class="link-item" data-tests-id="link-item-capabilities" data-ng-repeat="(cap, matchArr) in relationMenuDirectiveObj.rightSideLink.capabilities"
+ data-ng-click="relationMenuDirectiveObj.rightSideLink.selectMatchArr(matchArr); updateSelectionText()"
+ data-ng-show="showMatch(relationMenuDirectiveObj.leftSideLink.selectedMatch, matchArr)"
+ data-ng-class="{ 'selected': relationMenuDirectiveObj.rightSideLink.selectedMatch === matchArr}">
+ <div sdc-smart-tooltip>{{matchArr[0].capability.getFullTitle()}}</div>
+ </div>
+ </perfect-scrollbar>
+
+ <div class="vl-type" data-ng-class="{'disabled': !relationMenuDirectiveObj.leftSideLink.selectedMatch[0].secondRequirement || !relationMenuDirectiveObj.rightSideLink.selectedMatch[0].secondRequirement}">
+ <sdc-radio-button sdc-model="relationMenuDirectiveObj.vlType" value="ptp"
+ disabled="!relationMenuDirectiveObj.leftSideLink.selectedMatch[0].secondRequirement || !relationMenuDirectiveObj.rightSideLink.selectedMatch[0].secondRequirement || !relationMenuDirectiveObj.p2pVL"
+ text="Point to point" elem-id="radioPTP" elem-name="vlType"></sdc-radio-button>
+
+ <sdc-radio-button sdc-model="relationMenuDirectiveObj.vlType" value="mptmp"
+ disabled="!relationMenuDirectiveObj.leftSideLink.selectedMatch[0].secondRequirement || !relationMenuDirectiveObj.rightSideLink.selectedMatch[0].secondRequirement || !relationMenuDirectiveObj.mp2mpVL"
+ text="Multi point" elem-id="radioMPTMP" elem-name="vlType"></sdc-radio-button>
+
+ <span class="sprite-new info-icon" tooltips tooltip-content="You are required to choose the type of the Virtual Link."></span>
+ </div>
+
+ <div class="result" sdc-smart-tooltip>&#8203;{{relationMenuDirectiveObj.selectionText}}
+
+ </div>
+
+ <button class="tlv-btn grey" data-tests-id="link-menu-button-cancel" data-ng-click="hideRelationMatch()">Cancel</button>
+ <button class="tlv-btn blue" data-tests-id="link-menu-button-connect" data-ng-disabled="!relationMenuDirectiveObj.leftSideLink.selectedMatch || !relationMenuDirectiveObj.rightSideLink.selectedMatch ||
+ (relationMenuDirectiveObj.leftSideLink.selectedMatch[0].secondRequirement && !relationMenuDirectiveObj.vlType)"
+ data-ng-click="saveRelation()">Connect</button>
+</div>
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.less b/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.less
new file mode 100644
index 0000000000..dea814dbec
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.less
@@ -0,0 +1,118 @@
+.link-menu-open {
+ display: block !important;
+ color: @main_color_m;
+ font-size: 14px;
+ position: absolute;
+ z-index: 99999;
+ border-radius: 2px;
+ background-color: #ffffff;
+ box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.5);
+ width: 460px;
+ height: 418px;
+
+ h4 {
+ width: 50%;
+ float: left;
+ background-color: @tlv_color_u;
+ font-size: 14px;
+ font-weight: bold;
+ line-height: 36px;
+ margin: 0;
+ padding: 0 15px;
+
+ & + h4 {
+ border-left: #d8d8d8 1px solid;
+ }
+ }
+ p {
+ clear: both;
+ text-indent: 15px;
+ border-bottom: #d8d8d8 1px solid;
+ line-height: 34px;
+ margin: 0;
+ color: @func_color_s;
+ }
+
+ .scrollbar-container {
+ height: 232px;
+ width: 50%;
+ float: left;
+ margin-bottom: 5px;
+ .perfect-scrollbar;
+
+ & + .scrollbar-container {
+ border-left: #d8d8d8 1px solid;
+ }
+
+ .inner-title {
+ width: 189px;
+ margin: 5px auto 3px auto;
+ //text-indent: 10px;
+ color: @func_color_s;
+ text-transform: uppercase;
+ font-weight: bold;
+
+ //&:not(:first-child) {
+ // margin-top: 10px;
+ //}
+ }
+
+ .link-item {
+ padding: 0 10px;
+ line-height: 23px;
+ height: 23px;
+ text-indent: 5px;
+ .hand;
+
+ &.selected {
+ background-color: @tlv_color_v;
+ }
+ }
+ }
+
+ .vl-type {
+ height: 33px;
+ border-top: #d8d8d8 solid 1px;
+ clear: both;
+ padding: 0 10px;
+ line-height: 32px;
+ color: @main_color_m;
+
+ &.disabled {
+ background-color: #f2f2f2;
+ color: @color_m;
+ }
+ .info-icon {
+ float:right;
+ margin-top: 9px;
+ }
+ .tlv-radio {
+ margin-right: 10px;
+ }
+ }
+
+ .result {
+ background-color: @main_color_m;
+ line-height: 29px;
+ color: #ffffff;
+ padding: 0 15px;
+ }
+
+ button {
+ float: right;
+ margin-top: 9px;
+ margin-right: 10px;
+ }
+}
+.link-menu-item {
+ cursor: pointer;
+ line-height: 24px;
+ padding: 0 10px;
+ &:hover {
+ color: @color_a;
+ }
+}
+.link-menu::before {
+ right: inherit !important;
+ left: 50px;
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.ts b/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.ts
new file mode 100644
index 0000000000..22a2d078b7
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/graphs-v2/relation-menu/relation-menu.ts
@@ -0,0 +1,113 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+
+ export interface IRelationMenuScope extends ng.IScope {
+ relationMenuDirectiveObj:Models.RelationMenuDirectiveObj;
+ createRelation:Function;
+ isLinkMenuOpen:boolean;
+ hideRelationMatch:Function;
+ cancel:Function;
+
+ saveRelation();
+ showMatch(arr1:Array<Models.MatchBase>, arr2:Array<Models.MatchBase>):boolean;
+ hasMatchesToShow(matchesObj:Models.MatchBase, selectedMatch:Array<Models.MatchBase>);
+ updateSelectionText():void;
+
+ }
+
+
+ export class RelationMenuDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $filter:ng.IFilterService
+ ) {
+ }
+
+ scope = {
+ relationMenuDirectiveObj: '=',
+ isLinkMenuOpen: '=',
+ createRelation: '&',
+ cancel:'&'
+ };
+
+ restrict = 'E';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/graphs-v2/relation-menu/relation-menu.html');
+ };
+
+ link = (scope:IRelationMenuScope, element:JQuery, $attr:ng.IAttributes) => {
+
+ scope.saveRelation = ():void=> {
+ let chosenMatches:Array<any> = _.intersection(scope.relationMenuDirectiveObj.rightSideLink.selectedMatch, scope.relationMenuDirectiveObj.leftSideLink.selectedMatch);
+ let chosenMatch:Models.MatchBase = chosenMatches[0];
+ let chosenVL:Models.Components.Component;
+ if ("mptmp" === scope.relationMenuDirectiveObj.vlType) {
+ chosenVL = scope.relationMenuDirectiveObj.mp2mpVL;
+ } else {
+ chosenVL = scope.relationMenuDirectiveObj.p2pVL;
+ }
+ scope.createRelation()(chosenMatch,chosenVL);
+ };
+
+
+ scope.hideRelationMatch = () => {
+ scope.isLinkMenuOpen = false;
+ scope.cancel();
+ };
+
+ //to show options in link menu
+ scope.showMatch = (arr1:Array<Models.MatchBase>, arr2:Array<Models.MatchBase>):boolean => {
+ return !arr1 || !arr2 || _.intersection(arr1, arr2).length > 0;
+ };
+
+ //to show requirements/capabilities title
+ scope.hasMatchesToShow = (matchesObj:Models.MatchBase, selectedMatch:Array<Models.MatchBase>):boolean => {
+ let result:boolean = false;
+ _.forEach(matchesObj, (matchesArr:Array<Models.MatchBase>) => {
+ if (!result) {
+ result = scope.showMatch(matchesArr, selectedMatch);
+ }
+ });
+ return result;
+ };
+
+
+ scope.updateSelectionText = ():void => {
+ let left:string = scope.relationMenuDirectiveObj.leftSideLink.selectedMatch ? this.$filter('resourceName')(scope.relationMenuDirectiveObj.leftSideLink.selectedMatch[0].getDisplayText('left')) : '';
+ let both:string = scope.relationMenuDirectiveObj.leftSideLink.selectedMatch && scope.relationMenuDirectiveObj.rightSideLink.selectedMatch ? ' - ' +
+ this.$filter('resourceName')(scope.relationMenuDirectiveObj.leftSideLink.selectedMatch[0].requirement.relationship) + ' - ' : '';
+ let right:string = scope.relationMenuDirectiveObj.rightSideLink.selectedMatch ? this.$filter('resourceName')(scope.relationMenuDirectiveObj.rightSideLink.selectedMatch[0].getDisplayText('right')) : '';
+ scope.relationMenuDirectiveObj.selectionText = left + both + right;
+ };
+
+
+ }
+ public static factory = ($templateCache:ng.ITemplateCacheService , $filter:ng.IFilterService)=> {
+ return new RelationMenuDirective($templateCache, $filter);
+ };
+ }
+
+ RelationMenuDirective.factory.$inject = ['$templateCache', '$filter'];
+}
diff --git a/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.html b/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.html
new file mode 100644
index 0000000000..5c2bdcf5f1
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.html
@@ -0,0 +1,10 @@
+<div>
+ <span class="sprite-new info-icon hand" data-ng-click="showMessage=!showMessage"></span>
+ <div class="info-tooltip" data-ng-show="showMessage">
+ <div class="info-tooltip-arrow"></div>
+ <div class="info-tooltip-content" data-ng-class="direction">
+ <span class="close-tooltip sprite-new close-info-tooltip-button" data-ng-click="showMessage=false"></span>
+ <p class="info-tooltip-message" translate="{{infoMessageTranslate}}"></p>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.less b/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.less
new file mode 100644
index 0000000000..8811af16a4
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.less
@@ -0,0 +1,39 @@
+.info-tooltip {
+ position: fixed;
+ z-index: 1070;
+ display: block;
+ width: 250px;
+ .info-tooltip-arrow {
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 0 5px 5px 5px;
+ border-color: transparent transparent @main_color_a transparent;
+ position: relative;
+ left: 2px;
+ }
+ .info-tooltip-content {
+ box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.5);
+ border: 1px solid @main_color_o;
+ border-radius: 3px;
+ border-top: 3px solid @main_color_a;
+ position: relative;
+ background-color: white;
+ &.right{
+ left: -13px;
+ }
+ &.left{
+ left: -223px;
+ }
+ .close-tooltip{
+ float: right;
+ margin: 5px;
+ }
+
+ .info-tooltip-message{
+ margin: 15px;
+ word-break: normal;
+ font-size: 14px;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.ts b/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.ts
new file mode 100644
index 0000000000..cd81b14ce8
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/info-tooltip/info-tooltip.ts
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/25/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface IInfoTooltipScope extends ng.IScope {
+ infoMessageTranslate:string;
+ direction:string;
+ }
+
+
+ export class InfoTooltipDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ infoMessageTranslate:'@',
+ direction:'@'//get 'right' or 'left', the default is 'right'
+ };
+
+ restrict = 'E';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/info-tooltip/info-tooltip.html');
+ };
+
+ link = (scope:IInfoTooltipScope, element:any, $attr:any) => {
+ scope.direction = scope.direction || 'right';
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new InfoTooltipDirective($templateCache);
+ };
+
+ }
+
+ InfoTooltipDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/invalid-characters/invalid-characters.ts b/catalog-ui/app/scripts/directives/invalid-characters/invalid-characters.ts
new file mode 100644
index 0000000000..7ab98b0d23
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/invalid-characters/invalid-characters.ts
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class InvalidCharactersDirective implements ng.IDirective {
+
+ constructor() {}
+
+ require = 'ngModel';
+
+ link = (scope, elem, attrs, ngModel) => {
+
+ let invalidCharacters = [];
+
+ attrs.$observe('invalidCharacters', (val:string) => {
+ invalidCharacters = val.split('');
+ validate(ngModel.$viewValue);
+ });
+
+ let validate: Function = function (value) {
+
+ let valid:boolean = true;
+
+ if(value) {
+ for (let i = 0; i < invalidCharacters.length; i++) {
+ if (value.indexOf(invalidCharacters[i]) != - 1) {
+ valid = false;
+ }
+ }
+ }
+
+ ngModel.$setValidity('invalidCharacters', valid);
+ if(!value) {
+ ngModel.$setPristine();
+ }
+ return value;
+ };
+
+ //For DOM -> model validation
+ ngModel.$parsers.unshift(validate);
+ //For model -> DOM validation
+ ngModel.$formatters.unshift(validate);
+
+ };
+
+ public static factory = ()=> {
+ return new InvalidCharactersDirective();
+ };
+
+ }
+
+ InvalidCharactersDirective.factory.$inject = [];
+}
diff --git a/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.html b/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.html
new file mode 100644
index 0000000000..40b1e86d90
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.html
@@ -0,0 +1,54 @@
+<nav class="top-nav">
+
+ <div class="asdc-app-title-wrapper">
+ <a class="asdc-app-title">SDC</a> <!-- data-ui-sref="dashboard" -->
+ <div class="asdc-version"> v.{{version}}</div>
+ </div>
+
+ <ul class="top-menu" data-ng-show="!menuModel">
+ <!-- no hierarchy & dropdowns mode -->
+ <li data-ng-repeat="item in topLvlMenu.menuItems"
+ data-ng-class="{'selected': $index == topLvlMenu.selectedIndex}">
+ <a data-ng-click="menuItemClick(topLvlMenu, item)"
+ data-tests-id="main-menu-button-{{item.text | lowercase}}">{{item.text}}</a>
+ </li>
+ </ul>
+
+ <ul class="top-menu" data-ng-show="menuModel">
+ <!-- with hierarchy & dropdowns mode -->
+ <li data-ng-repeat-start="groupItem in menuModel"
+ data-ng-class="{'selected': $last }">
+ <a data-ng-click="menuItemClick(groupItem, groupItem.menuItems[groupItem.selectedIndex])"
+ data-tests-id="breadcrumbs-button-{{$index}}">
+ {{groupItem.menuItems[groupItem.selectedIndex].text}}
+ </a>
+ </li>
+ <li data-ng-repeat-end="" class="triangle-dropdown"
+ data-ng-class="{'item-click': groupItem.itemClick}" data-ng-mouseover="groupItem.itemClick = true">
+ <div class="triangle"><span class="sprite-new arrow-right"></span></div>
+ <perfect-scrollbar scroll-y-margin-offset="15" include-padding="true">
+ <ul>
+ <li data-ng-repeat="ddItem in groupItem.menuItems"
+ data-ng-click="menuItemClick(groupItem, ddItem)"
+ data-ng-class="{'selected': $index == groupItem.selectedIndex, 'disabled': ddItem.isDisabled}"
+ data-tests-id="sub-menu-button-{{ddItem.text | lowercase}}">
+ <span sdc-smart-tooltip="">{{ddItem.text}}</span>
+ </li>
+ </ul>
+ </perfect-scrollbar>
+ </li>
+ </ul>
+
+ <div class="top-search" data-ng-hide="hideSearch === true">
+ <input type="text"
+ class="search-text"
+ placeholder="Search"
+ data-ng-model="searchBind"
+ data-tests-id="main-menu-input-search"
+ ng-model-options="{ debounce: 500 }" />
+ <span class="w-sdc-search-icon magnification"></span>
+ </div>
+
+ <div class="notification-icon" data-ng-disabled= "progress > 0" data-ng-class="{'disabled' : progress > 0}" data-ng-if="user.role === 'DESIGNER' && notificationIconCallback" data-ng-click="notificationIconCallback()" tooltips tooltip-side="left" tooltip-content="Vendor Software Product Repository" data-tests-id="repository-icon"></div>
+
+</nav>
diff --git a/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.less b/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.less
new file mode 100644
index 0000000000..65021bdc4d
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.less
@@ -0,0 +1,218 @@
+.top-nav {
+ position: fixed;
+ top: @header_height;
+ background-color: @main_color_p;
+ .box-shadow(0px 1px 3px 0px rgba(0, 0, 0, 0.33));
+ width: 100%;
+ height: @top_nav_height;
+ line-height: @top_nav_height;
+ z-index: 10;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+
+ .asdc-app-title-wrapper {
+ flex-grow: 1;
+ line-height: 16px;
+ margin: 0 20px;
+
+ a.asdc-app-title {
+ .m_18_r;
+ text-decoration: none;
+ }
+
+ .asdc-version {
+ .m_12_r;
+ .opacity(0.8);
+ line-height: 14px;
+ flex-grow: 1;
+ }
+
+ }
+
+ ul.top-menu {
+ list-style-type: none;
+ margin: 0 0 0 20px;
+ padding: 0;
+ flex-grow: 999;
+
+ & > li {
+ float: left;
+ cursor: pointer;
+ line-height: 50px;
+ height: 50px;
+ padding: 0 20px;
+
+ &.selected {
+ border-bottom: solid 4px @main_color_a;
+
+ a {
+ color: @func_color_s;
+ }
+ }
+
+ /*&:hover {
+ border-bottom: solid 4px @main_color_a;
+ }*/
+
+ a {
+ font-family: @font-omnes-medium;
+ color: @main_color_m;
+ font-size: 18px;
+ display: block;
+ text-align: center;
+ text-decoration: none;
+ }
+
+ &.triangle-dropdown {
+ padding: 0;
+ position: relative;
+
+ div.triangle {
+ margin-top: 15px;
+ border-radius: 2px;
+ width: 17px;
+ height: 18px;
+
+ //temp use - until new triangle gets in
+ line-height: 18px;
+ text-align: center;
+ font-size: 10px;
+
+ &:hover {
+ background-color: rgba(156, 156, 156, 0.2);
+
+ span {
+ .arrow-right-hover;
+ }
+ }
+ }
+
+ + li a {
+ font-size: 16px;
+ }
+
+ .ps-container {
+ .perfect-scrollbar;
+ position: absolute;
+ left: 0;
+ top: 40px;
+ z-index: 1;
+
+ overflow: hidden;
+ max-height: 0;
+ -webkit-transition: max-height 200ms ease-in;
+ -moz-transition: max-height 200ms ease-in;
+ -o-transition: max-height 200ms ease-in;
+ transition: max-height 200ms ease-in;
+
+ div ul {
+
+ padding: 0;
+ background-color: white;
+
+ li {
+
+ height: 35px;
+ background-color: white;
+ font-size: 13px;
+ width: 150px;
+ line-height: 35px;
+ padding: 0 10px;
+
+ &.disabled {
+ opacity: 1;
+ }
+ &.selected {
+ background-color: @tlv_color_v;
+ font-weight: bold;
+ }
+ &:hover {
+ color: @main_color_a;
+ }
+ span {
+ height: 35px;
+ width: 130px;
+ display: inline-block;
+ }
+ }
+ }
+ }
+ &.item-click:hover .ps-container,
+ &.item-click:active .ps-container {
+ max-height: 500px;
+ border: 1px solid @func_color_b;
+ border-radius: 2px;
+ box-shadow: 0px 2px 2px 0px rgba(24, 24, 25, 0.1);
+
+ div ul {
+
+ }
+ }
+ }
+ }
+
+ }
+
+ .top-search {
+ position: relative;
+ flex-grow: 1;
+ padding: 0 20px;
+
+ input.search-text {
+ .border-radius(2px);
+ width: 245px;
+ height: 32px;
+ line-height: 32px;
+ border: 1px solid @main_color_o;
+ outline: none;
+ text-indent: 10px;
+
+ &::-webkit-input-placeholder { font-style: italic; } /* Safari, Chrome and Opera */
+ &:-moz-placeholder { font-style: italic; } /* Firefox 18- */
+ &::-moz-placeholder { font-style: italic; } /* Firefox 19+ */
+ &:-ms-input-placeholder { font-style: italic; } /* IE 10+ */
+ &:-ms-input-placeholder { font-style: italic; } /* Edge */
+ /* font-style: italic;
+ }*/
+ /* Firefox 18- */
+ &::-moz-placeholder {
+ font-style: italic;
+ }
+ /* Firefox 19+ */
+ &:-ms-input-placeholder {
+ font-style: italic;
+ }
+ /* IE 10+ */
+ &:-ms-input-placeholder {
+ font-style: italic;
+ }
+ /* Edge */
+ }
+
+ .magnification {
+ position: absolute;
+ top: 19px;
+ right: 26px;
+ }
+
+ }
+
+ .notification-icon {
+ cursor: pointer;
+ flex-grow: 1;
+ margin: 0 10px 6px 0;
+ .sprite-new;
+ .vsp-list-icon;
+
+ &:hover {
+ .vsp-list-icon-hover;
+ }
+
+ &:active {
+ .vsp-list-icon-active;
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.ts b/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.ts
new file mode 100644
index 0000000000..356e43b7f7
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/layout/top-nav/top-nav.ts
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ITopNavScope extends ng.IScope {
+ topLvlSelectedIndex: number;
+ hideSearch: boolean;
+ searchBind: any;
+ menuModel: Array<Utils.MenuItemGroup>;
+
+ topLvlMenu: Utils.MenuItemGroup;
+ goToState(state:string, params:Array<any>):ng.IPromise<boolean>;
+ menuItemClick: Function;
+ user: Models.IUserProperties;
+ version:string;
+ }
+
+
+ export class TopNavDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $filter:ng.IFilterService,
+ private $state:ng.ui.IStateService,
+ private $q: ng.IQService,
+ private userResourceService: Sdc.Services.IUserResourceClass
+ ) {
+ }
+
+ public replace = true;
+ public restrict = 'E';
+ public transclude = false;
+
+
+ scope = {
+ topLvlSelectedIndex: '@?',
+ hideSearch: '=',
+ searchBind: '=',
+ version: '@',
+ notificationIconCallback: '=',
+ menuModel: '=?',
+ };
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/layout/top-nav/top-nav.html');
+ };
+
+ public link = (scope:ITopNavScope, $elem:ng.IAugmentedJQuery, $attrs:angular.IAttributes) => {
+
+ let getTopLvlSelectedIndexByState = ():number => {
+ if (!scope.topLvlMenu.menuItems) {
+ return 0;
+ }
+
+ let result = -1;
+
+ //set result to current state
+ scope.topLvlMenu.menuItems.forEach((item:Utils.MenuItem, index:number)=> {
+ if (item.state === this.$state.current.name) {
+ result = index;
+ }
+ });
+
+ //if it's a different state , checking previous state param
+ if (result === -1) {
+ scope.topLvlMenu.menuItems.forEach((item:Utils.MenuItem, index:number)=> {
+ if (item.state === this.$state.params['previousState']) {
+ result = index;
+ }
+ });
+ }
+
+ if (result === -1) {
+ result = 0;
+ }
+
+ return result;
+ };
+
+ scope.user = this.userResourceService.getLoggedinUser();
+
+ let tmpArray:Array<Utils.MenuItem> = [
+ new Utils.MenuItem(this.$filter('translate')("TOP_MENU_HOME_BUTTON"), null, "dashboard", "goToState", null, null),
+ new Utils.MenuItem(this.$filter('translate')("TOP_MENU_CATALOG_BUTTON"), null, "catalog", "goToState", null, null)
+ ];
+
+ // Only designer can perform onboarding
+ if (scope.user && scope.user.role === 'DESIGNER'){
+ tmpArray.push(new Utils.MenuItem(this.$filter('translate')("TOP_MENU_ON_BOARD_BUTTON"), null, "onboardVendor", "goToState", null, null));
+ }
+
+ scope.topLvlMenu = new Utils.MenuItemGroup(0, tmpArray , true );
+ scope.topLvlMenu.selectedIndex = isNaN(scope.topLvlSelectedIndex) ? getTopLvlSelectedIndexByState() : scope.topLvlSelectedIndex;
+
+ let generateMenu = () => {
+ if (scope.menuModel && scope.menuModel[0] !== scope.topLvlMenu) {
+ scope.menuModel.unshift(scope.topLvlMenu);
+ }
+ };
+ scope.$watch('menuModel', generateMenu);
+
+ generateMenu();
+
+ /////scope functions////
+
+ scope.goToState = (state:string, params:Array<any>):ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+ this.$state.go(state, params && params.length > 0 ? [0] : undefined);
+ deferred.resolve(true);
+ return deferred.promise;
+ };
+
+ scope.menuItemClick = (itemGroup:Utils.MenuItemGroup, item:Utils.MenuItem) => {
+
+ itemGroup.itemClick = false;
+
+ let onSuccess = ():void => {
+ itemGroup.selectedIndex = itemGroup.menuItems.indexOf(item);
+ };
+ let onFailed = ():void => {};
+
+ if (item.callback) {
+ (item.callback.apply(undefined, item.params)).then(onSuccess, onFailed);
+ } else {
+ scope[item.action](item.state, item.params).then(onSuccess, onFailed);
+ }
+ };
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, $filter:ng.IFilterService, $state:ng.ui.IStateService, $q: ng.IQService, userResourceService: Sdc.Services.IUserResourceClass)=> {
+ return new TopNavDirective($templateCache, $filter, $state,$q, userResourceService);
+ };
+
+ }
+
+ TopNavDirective.factory.$inject = ['$templateCache', '$filter', '$state','$q', 'Sdc.Services.UserResourceService'];
+}
diff --git a/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.html b/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.html
new file mode 100644
index 0000000000..ab2c8e364e
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.html
@@ -0,0 +1,22 @@
+<div class="top-progress">
+
+ <!--======================= Top progress var =======================-->
+ <div data-ng-if="progressValue>0 && progressValue<100">
+ <span class="sdc-progress-title">{{progressMessage}}<span class="progress-percentage">{{progressValue}}&nbsp;%</span></span>
+ <div class="sdc-progress">
+ <div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="{{progressValue}}" aria-valuemin="0" aria-valuemax="100" data-ng-style="{width: progressValue+'%'}"></div>
+ </div>
+ </div>
+
+ <div class="sdc-progress-success-wrapper" data-ng-if="progressValue===100">
+ <span class="sdc-progress-success"></span>
+ <span class="sdc-progress-success-title">{{progressMessage}}</span>
+ </div>
+
+ <div class="sdc-progress-error-wrapper" data-ng-if="progressValue===-1">
+ <span class="sdc-progress-error"></span>
+ <span class="sdc-progress-error-title">{{progressMessage}}</span>
+ </div>
+ <!--======================= Top progress var =======================-->
+
+</div>
diff --git a/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.less b/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.less
new file mode 100644
index 0000000000..acce826f80
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.less
@@ -0,0 +1,58 @@
+.top-progress {
+ text-align: left;
+
+ .sdc-progress-title {
+ .n_12_r;
+
+ .progress-percentage {
+ float: right;
+ }
+ }
+
+ .sdc-progress {
+ position: relative;
+ display: block;
+ height: 6px;
+ background-color: @main_color_o;
+ border-radius: 3px;
+ box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
+
+ .progress-bar {
+ border-radius: 3px;
+ background-color: @main_color_a;
+ }
+
+ }
+
+ .sdc-progress-success-wrapper {
+ display: flex;
+ align-items: flex-end;
+
+ .sdc-progress-success-title {
+ .d_12_r;
+ margin-left: 10px;
+ }
+
+ .sdc-progress-success {
+ .sprite-new;
+ .success-circle;
+ }
+ }
+
+ .sdc-progress-error-wrapper {
+ display: flex;
+ align-items: flex-end;
+
+ .sdc-progress-error-title {
+ .q_12_r;
+ margin-left: 10px;
+ }
+
+ .sdc-progress-error {
+ .sprite-new;
+ .error-icon;
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.ts b/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.ts
new file mode 100644
index 0000000000..8e8a289281
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/layout/top-progress/top-progress.ts
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ITopProgressScope extends ng.IScope {
+ progressValue:number;
+ progressMessage:string;
+ }
+
+ export class TopProgressDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {}
+
+ public replace = true;
+ public restrict = 'E';
+ public transclude = false;
+
+ scope = {
+ progressValue: '=',
+ progressMessage: '='
+ };
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/layout/top-progress/top-progress.html');
+ };
+
+ public link = (scope:ITopProgressScope, $elem:ng.IAugmentedJQuery, $attrs:angular.IAttributes) => {
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new TopProgressDirective($templateCache);
+ };
+
+ }
+
+ TopProgressDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/loader/loader-directive.html b/catalog-ui/app/scripts/directives/loader/loader-directive.html
new file mode 100644
index 0000000000..e40b059a57
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/loader/loader-directive.html
@@ -0,0 +1,4 @@
+<div data-ng-if="display" data-tests-id="tlv-loader">
+ <div class="tlv-loader-back " data-ng-class="{'tlv-loader-relative':relative}"></div>
+ <div class="tlv-loader {{size}}"></div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/loader/loader-directive.less b/catalog-ui/app/scripts/directives/loader/loader-directive.less
new file mode 100644
index 0000000000..ae0b41aab1
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/loader/loader-directive.less
@@ -0,0 +1,74 @@
+.tlv-loader-back {
+ background-color: @main_color_p;
+ position: fixed;
+ top: 50px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 9999;
+ opacity: 0.5;
+}
+
+.tlv-loader-relative { position: absolute; top: 0;}
+
+.tlv-loader {
+ z-index: 10002;
+}
+
+@keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 0.8; }
+}
+
+/* Firefox < 16 */
+@-moz-keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 0.8; }
+}
+
+/* Safari, Chrome and Opera > 12.1 */
+@-webkit-keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 0.8; }
+}
+
+/* Internet Explorer */
+@-ms-keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 0.8; }
+}
+
+/* Opera < 12.1 */
+@-o-keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 0.8; }
+}
+
+@keyframes fadeout {
+ from { opacity: 0.8; }
+ to { opacity: 0; }
+}
+
+/* Firefox < 16 */
+@-moz-keyframes fadeout {
+ from { opacity: 0.8; }
+ to { opacity: 0; }
+}
+
+/* Safari, Chrome and Opera > 12.1 */
+@-webkit-keyframes fadeout {
+ from { opacity: 0.8; }
+ to { opacity: 0; }
+}
+
+/* Internet Explorer */
+@-ms-keyframes fadeout {
+ from { opacity: 0.8; }
+ to { opacity: 0; }
+}
+
+/* Opera < 12.1 */
+@-o-keyframes fadeout {
+ from { opacity: 0.8; }
+ to { opacity: 0; }
+}
diff --git a/catalog-ui/app/scripts/directives/loader/loader-directive.ts b/catalog-ui/app/scripts/directives/loader/loader-directive.ts
new file mode 100644
index 0000000000..77c8977ac5
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/loader/loader-directive.ts
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface ILoaderScope extends ng.IScope {
+ display: boolean; // Toggle show || hide scroll
+ size: string; // small || medium || large
+ elementSelector: string; // Jquery selector to hide and scroll inside
+ relative: boolean; // Will use the parent of <loader> element and hide it and scroll inside
+ loaderType: string;
+ }
+
+ export class LoaderDirective implements ng.IDirective {
+
+ constructor(private $templateCache: ng.ITemplateCacheService, private EventListenerService: Services.EventListenerService) {
+ }
+
+ /*
+ * relative is used when inserting the HTML loader inside some div <loader data-display="isLoading" relative="true"></loader>
+ * elementSelector when we want to pass the Jquery selector of the loader.
+ */
+ scope = {
+ display: '=',
+ size: '@?',
+ elementSelector: '@?',
+ relative: '=?',
+ loaderType: '@?'
+ };
+
+ public replace = false;
+ public restrict = 'E';
+ template = (): string => {
+ return this.$templateCache.get('/app/scripts/directives/loader/loader-directive.html');
+ };
+
+ link = (scope: ILoaderScope, element: any) => {
+
+ let interval;
+
+ this.EventListenerService.registerObserverCallback(Utils.Constants.EVENTS.SHOW_LOADER_EVENT, (loaderType)=> {
+ if (scope.loaderType !== loaderType) {
+ return;
+ }
+ scope.display = true;
+ });
+ this.EventListenerService.registerObserverCallback(Utils.Constants.EVENTS.HIDE_LOADER_EVENT, (loaderType)=> {
+ if (scope.loaderType !== loaderType) {
+ return;
+ }
+ scope.display = false;
+ });
+
+ let calculateSizesForFixPosition = (positionStyle: string): void => {
+ // This is problematic, I do not want to change the parent position.
+ // set the loader on all the screen
+ let parentPosition = element.parent().position();
+ let parentWidth = element.parent().width();
+ let parentHeight = element.parent().height();
+ element.css('position', positionStyle);
+ element.css('top', parentPosition.top);
+ element.css('left', parentPosition.left);
+ element.css('width', parentWidth);
+ element.css('height', parentHeight);
+ };
+
+ let setStyle = (positionStyle: string): void => {
+
+ switch (positionStyle) {
+ case 'absolute':
+ case 'fixed':
+ // The parent size is not set yet, still loading, so need to use interval to update the size.
+ interval = window.setInterval(()=> {
+ calculateSizesForFixPosition(positionStyle);
+ }, 2000);
+ break;
+ default:
+ // Can change the parent position to relative without causing style issues.
+ element.parent().css('position', 'relative');
+ break;
+ }
+ };
+
+ // This should be executed after the dom loaded
+ window.setTimeout((): void => {
+
+ element.css('display', 'none');
+
+ if (scope.elementSelector) {
+ let elemParent = angular.element(scope.elementSelector);
+ let positionStyle: string = elemParent.css('position');
+ setStyle(positionStyle);
+ }
+
+ if (scope.relative === true) {
+ let positionStyle: string = element.parent().css('position');
+ setStyle(positionStyle);
+ }
+
+ if (!scope.size) {
+ scope.size = 'large';
+ }
+
+ }, 0);
+
+ if (scope.elementSelector) {
+
+ }
+
+ function cleanUp() {
+ clearInterval(interval);
+ }
+
+ scope.$watch("display", (newVal, oldVal) => {
+ element.css('display', 'none');
+ if (newVal === true) {
+ window.setTimeout((): void => {
+ element.css('display', 'block');
+ }, 500);
+ } else {
+ window.setTimeout((): void => {
+ element.css('display', 'none');
+ }, 0);
+ }
+ });
+
+ scope.$on('$destroy', cleanUp);
+
+ };
+
+ public static factory = ($templateCache: ng.ITemplateCacheService, EventListenerService: Services.EventListenerService)=> {
+ return new LoaderDirective($templateCache, EventListenerService);
+ };
+
+ }
+
+ LoaderDirective.factory.$inject = ['$templateCache', 'EventListenerService'];
+}
diff --git a/catalog-ui/app/scripts/directives/modal/sdc-modal.html b/catalog-ui/app/scripts/directives/modal/sdc-modal.html
new file mode 100644
index 0000000000..a8419f162d
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/modal/sdc-modal.html
@@ -0,0 +1,18 @@
+<div data-ng-class="{'w-sdc-modal': type===undefined, 'w-sdc-classic-modal': type==='classic'}">
+ <div class="w-sdc-modal-head">
+ <span data-ng-if="header" class="w-sdc-modal-head-text">{{header}}</span>
+ <span data-ng-if="headerTranslate" class="w-sdc-modal-head-text" translate="{{headerTranslate}}" translate-values="{{headerTranslateValues}}"></span>
+ <div data-ng-if="showCloseButton==='true'" class="w-sdc-modal-close" data-ng-click="cancel()"></div>
+ </div>
+ <div class="w-sdc-modal-body" data-ng-class="{'classic': type==='classic'}">
+ <ng-transclude></ng-transclude>
+ </div>
+ <div class="w-sdc-modal-footer" data-ng-if="type==='classic' && buttons!==undefined">
+ <button data-ng-repeat="button in buttons"
+ data-tests-id="{{button.name}}"
+ class="tlv-btn {{button.css}}"
+ data-ng-class="{'disabled': button.disabled===true}"
+ data-ng-disabled="button.disabled===true"
+ data-ng-click="button.callback()">{{button.name}}</button>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/modal/sdc-modal.less b/catalog-ui/app/scripts/directives/modal/sdc-modal.less
new file mode 100644
index 0000000000..d8dfdbb73b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/modal/sdc-modal.less
@@ -0,0 +1,10 @@
+.ellipsis-directive-more-less {
+ .a_9;
+ .bold;
+ .hand;
+ float: right;
+ margin-right: 17px;
+ line-height: 23px;
+ text-decoration: underline;
+ text-align: left;
+}
diff --git a/catalog-ui/app/scripts/directives/modal/sdc-modal.ts b/catalog-ui/app/scripts/directives/modal/sdc-modal.ts
new file mode 100644
index 0000000000..338035c9f1
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/modal/sdc-modal.ts
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ISdcModalScope extends ng.IScope {
+ modal:ng.ui.bootstrap.IModalServiceInstance;
+ hideBackground:string;
+ ok():void;
+ close(result:any):void;
+ cancel(reason:any):void;
+ }
+
+ export interface ISdcModalButton {
+ name:string;
+ css:string;
+ disabled?:boolean;
+ callback:Function;
+ }
+
+ export class SdcModalDirective implements ng.IDirective {
+
+ constructor(
+ private $templateCache: ng.ITemplateCacheService
+ ) {}
+
+ scope = {
+ modal: '=',
+ type: '@',
+ header: '@',
+ headerTranslate: '@',
+ headerTranslateValues: '@',
+ showCloseButton: '@',
+ hideBackground: '@',
+ buttons: '=',
+ getCloseModalResponse: '='
+ };
+
+ public replace = true;
+ public restrict = 'E';
+ public transclude = true;
+
+ template = (): string => {
+ return this.$templateCache.get('/app/scripts/directives/modal/sdc-modal.html');
+ };
+
+ link = (scope:ISdcModalScope, $elem:any) => {
+
+ if (scope.hideBackground==="true"){
+ $(".modal-backdrop").css('opacity','0');
+ }
+
+ scope.close = function (result:any) {
+ scope.modal.close(result);
+ };
+
+ scope.ok = function () {
+ scope.modal.close();
+ };
+
+ scope.cancel = function (reason:any) {
+ if(this.getCloseModalResponse)
+ scope.modal.dismiss(this.getCloseModalResponse());
+ else {
+ scope.modal.dismiss();
+ }
+ };
+
+ if (scope.modal) {
+ scope.modal.result.then(function (selectedItem) {
+ //$scope.selected = selectedItem;
+ }, function () {
+ //console.info('Modal dismissed at: ' + new Date());
+ });
+ }
+ }
+
+ public static factory = ($templateCache: ng.ITemplateCacheService)=> {
+ return new SdcModalDirective($templateCache);
+ };
+
+ }
+
+ SdcModalDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/page-scroller/page-scroller.html b/catalog-ui/app/scripts/directives/page-scroller/page-scroller.html
new file mode 100644
index 0000000000..7359386901
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/page-scroller/page-scroller.html
@@ -0,0 +1,22 @@
+<div class="sdc-page-scroller">
+
+ <nav data-ng-if="showNav!==false" class="welcome-nav">
+ <div data-ng-if="showCloseButton===true" data-ng-click="onCloseButtonClick()" class="asdc-welcome-close"></div>
+ <ul>
+ <li data-ng-repeat="slide in slidesData | orderBy:'+position'"><a href="#{{slide.id}}" data-ng-click="onNavButtonClick(slide)" class=""></a></li>
+ </ul>
+ </nav>
+
+ <div class="nav-previous-next" data-ng-if="showPreviousNext===true">
+ <span class="go-prev" data-ng-click="goToPrevSlide()">previous slide</span>
+ <span class="go-next" data-ng-click="goToNextSlide()">next slide</span>
+ </div>
+
+ <div class="slides-container">
+ <section data-ng-repeat="slide in slidesData | orderBy:'+position'" class="slide" id="{{slide.id}}" on-last-repeat>
+ <ng-include src="slide.url"></ng-include>
+ </section>
+ </div>
+
+</div>
+
diff --git a/catalog-ui/app/scripts/directives/page-scroller/page-scroller.less b/catalog-ui/app/scripts/directives/page-scroller/page-scroller.less
new file mode 100644
index 0000000000..14f8568f07
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/page-scroller/page-scroller.less
@@ -0,0 +1,98 @@
+.sdc-page-scroller {
+
+ /****************** Navigation ***************/
+ nav {
+ position: fixed;
+ top: 0;
+ right: 0;
+ z-index: 100;
+ display: flex;
+ flex-direction: column;
+ width: 100px;
+ bottom: 0;
+ background-color: #000;
+ align-items: center;
+ justify-content: center;
+ }
+
+ nav ul {
+ list-style: none;
+ text-align: center;
+ margin-top: 0;
+ padding: 0;
+ }
+
+ nav ul li {
+ display: block;
+ margin-bottom: 15px;
+
+ }
+
+ nav ul li:last-child {
+
+ }
+
+ nav a {
+ display: block;
+ height: 6px;
+ width: 6px;
+ border-radius: 50%;
+ background-color: #4a4c4d;
+ }
+
+ nav a.active {
+ position: relative;
+ }
+
+ nav a.active::after {
+ content: '';
+ display: block;
+ position: absolute;
+ border: 2px solid #0198d1;
+ width: 16px;
+ height: 16px;
+ border-radius: 50%;
+ top: -5px;
+ left: -5px;
+ }
+
+ /****************** Previous Next navigation ***************/
+ .go-prev, .go-next {
+ cursor: pointer;
+ font-weight: bold;
+ text-decoration: underline;
+ }
+
+ .slides-container {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow-y: hidden;
+ z-index: 10;
+ }
+
+ .slide {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ }
+
+ .slide .centered {
+ width: 60%;
+ margin: 200px auto 0;
+ }
+
+ .slide .centered h1 {
+ text-align: center;
+ }
+
+ .slide .centered p {
+ text-align: center;
+ margin-top: 20px;
+ font-size: 20px;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/page-scroller/page-scroller.ts b/catalog-ui/app/scripts/directives/page-scroller/page-scroller.ts
new file mode 100644
index 0000000000..bb89f9a55a
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/page-scroller/page-scroller.ts
@@ -0,0 +1,247 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface SlideData {
+ url: string;
+ id: string;
+ index: number;
+ callback: Function;
+ }
+
+ export interface ISdcPageScrollDirectiveScope extends ng.IScope {
+ slidesData:Array<SlideData>;
+ showNav: boolean;
+ showPreviousNext: boolean;
+ currentSlide:SlideData;
+ showCloseButton:boolean;
+ closeButtonCallback:Function;
+ startSlideIndex:number;
+
+ onNavButtonClick(slideName):void;
+ onCloseButtonClick():void;
+ goToPrevSlide():void;
+ goToNextSlide():void;
+ goToSlide(slide:SlideData):void;
+ onSlideChangeEnd():void;
+ onMouseWheel(event):void;
+ onKeyDown(event):void;
+ onResize(event):void;
+ gotoSlideIndex(index):void;
+ }
+
+ export class SdcPageScrollDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+
+ }
+
+ scope = {
+ slidesData: '=',
+ showNav: '=',
+ showPreviousNext: '=',
+ showCloseButton: '=',
+ closeButtonCallback: '=',
+ startSlideIndex: '=?'
+ };
+
+ public replace = true;
+ public restrict = 'E';
+ private delayExec:any;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/page-scroller/page-scroller.html');
+ };
+
+ link = ($scope:ISdcPageScrollDirectiveScope, $elem:JQuery, attr:any) => {
+ let isAnimating = false; //Animating flag - is our app animating
+ let pageHeight = $(window).innerHeight(); //The height of the window
+ let slidesContainer;
+ let navButtons;
+ let slides:any; //Only graph-links that starts with
+
+ //Key codes for up and down arrows on keyboard. We'll be using this to navigate change slides using the keyboard
+ let keyCodes = {
+ UP : 38,
+ DOWN: 40
+ };
+
+ $scope.onCloseButtonClick = ():void => {
+ if ($scope.closeButtonCallback){
+ $scope.closeButtonCallback();
+ };
+ };
+
+ // Wait for the dom to load (after ngRepeat).
+ $scope.$on('onRepeatLast', (scope, element, attrs) => {
+ slides = $(".slide", slidesContainer);
+ slidesContainer = $(".slides-container");
+ navButtons = $("nav a").filter("[href^='#']");
+
+ // Adding event listeners
+ $(window).on("resize", (e) => {$scope.onResize(e);}).resize();
+ $(window).on("mousewheel DOMMouseScroll", (e) => {$scope.onMouseWheel(e);});
+ $(document).on("keydown", (e) => {$scope.onKeyDown(e);});
+
+ //Going to the first slide
+ if ($scope.startSlideIndex){
+ $scope.gotoSlideIndex($scope.startSlideIndex);
+ } else {
+ $scope.gotoSlideIndex(0);
+ }
+
+ });
+
+ $scope.gotoSlideIndex = (index) => {
+ $scope.goToSlide($scope.slidesData[index]);
+ };
+
+ // When a button is clicked - first get the button href, and then slide to the container, if there's such a container
+ $scope.onNavButtonClick = (slide:SlideData):void => {
+ $scope.goToSlide(slide);
+ };
+
+ // If there's a previous slide, slide to it
+ $scope.goToPrevSlide = ():void => {
+ let previousSlide = $scope.slidesData[$scope.currentSlide.index-1];
+ if (previousSlide) {
+ $scope.goToSlide(previousSlide);
+ }
+ };
+
+ // If there's a next slide, slide to it
+ $scope.goToNextSlide = ():void => {
+ let nextSlide = $scope.slidesData[$scope.currentSlide.index+1];
+ if (nextSlide) {
+ $scope.goToSlide(nextSlide);
+ }
+ };
+
+ // Actual transition between slides
+ $scope.goToSlide = (slide:SlideData):void => {
+ //console.log("start goToSlide");
+ //If the slides are not changing and there's such a slide
+ if(!isAnimating && slide) {
+ //setting animating flag to true
+ isAnimating = true;
+ $scope.currentSlide = slide;
+ $scope.currentSlide.callback();
+
+ //Sliding to current slide
+ let calculatedY = pageHeight * ($scope.currentSlide.index);
+ //console.log("$scope.currentSlide.index: " + $scope.currentSlide.index + " | calculatedY: " + calculatedY);
+
+ $('.slides-container').animate(
+ {
+ scrollTop: calculatedY + 'px'
+ },
+ {
+ duration: 1000,
+ specialEasing: {
+ width: "linear",
+ height: "easeInOutQuart"
+ },
+ complete: function() {
+ $scope.onSlideChangeEnd();
+ }
+ }
+ );
+
+ //Animating menu items
+ $(".sdc-page-scroller nav a.active").removeClass("active");
+ $(".sdc-page-scroller nav [href='#" + $scope.currentSlide.id + "']").addClass("active");
+ }
+ };
+
+ // Once the sliding is finished, we need to restore "isAnimating" flag.
+ // You can also do other things in this function, such as changing page title
+ $scope.onSlideChangeEnd = ():void => {
+
+
+
+ isAnimating = false;
+ };
+
+ // When user scrolls with the mouse, we have to change slides
+ $scope.onMouseWheel = (event):void => {
+ //Normalize event wheel delta
+ let delta = event.originalEvent.wheelDelta / 30 || -event.originalEvent.detail;
+
+ //If the user scrolled up, it goes to previous slide, otherwise - to next slide
+ if(delta < -1) {
+ this.delayAction($scope.goToNextSlide);
+ } else if(delta > 1) {
+ this.delayAction($scope.goToPrevSlide);
+ }
+ event.preventDefault();
+ };
+
+ // Getting the pressed key. Only if it's up or down arrow, we go to prev or next slide and prevent default behaviour
+ // This way, if there's text input, the user is still able to fill it
+ $scope.onKeyDown = (event):void => {
+ let PRESSED_KEY = event.keyCode;
+
+ if(PRESSED_KEY == keyCodes.UP){
+ $scope.goToPrevSlide();
+ event.preventDefault();
+ } else if(PRESSED_KEY == keyCodes.DOWN){
+ $scope.goToNextSlide();
+ event.preventDefault();
+ }
+ };
+
+ // When user resize it's browser we need to know the new height, so we can properly align the current slide
+ $scope.onResize = (event):void => {
+ //This will give us the new height of the window
+ let newPageHeight = $(window).innerHeight();
+
+ // If the new height is different from the old height ( the browser is resized vertically ), the slides are resized
+ if(pageHeight !== newPageHeight) {
+ pageHeight = newPageHeight;
+ }
+ };
+ };
+
+ private initSlides = ():void => {
+ //pageHeight
+ };
+
+ private delayAction = (action:Function):void => {
+ clearTimeout(this.delayExec);
+ this.delayExec = setTimeout(function () {
+ action();
+ }, 100);
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new SdcPageScrollDirective($templateCache);
+ };
+
+ }
+
+ SdcPageScrollDirective.factory.$inject = ['$templateCache'];
+
+}
+
+
+
+
diff --git a/catalog-ui/app/scripts/directives/perfect-scrollbar/angular-perfect-scrollbar.ts b/catalog-ui/app/scripts/directives/perfect-scrollbar/angular-perfect-scrollbar.ts
new file mode 100644
index 0000000000..b53a059a40
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/perfect-scrollbar/angular-perfect-scrollbar.ts
@@ -0,0 +1,159 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+
+ 'use strict';
+
+ export interface IPerfectScrollerScope extends ng.IScope {
+ //update(event:string): void;
+ }
+
+ export class PerfectScrollerDirective implements ng.IDirective {
+
+ constructor(
+ private $templateCache: ng.ITemplateCacheService,
+ private $parse:any,
+ private $window:any) {
+
+ }
+
+ replace = true;
+ restrict = 'EA';
+ transclude = true;
+
+ template = (): string => {
+ return '<div><div ng-transclude></div></div>';
+ };
+
+ link = ($scope:IPerfectScrollerScope, $elem, $attr) => {
+ let self = this;
+ let options = {};
+
+ let psOptions = [
+ 'wheelSpeed', 'wheelPropagation', 'minScrollbarLength', 'useBothWheelAxes',
+ 'useKeyboard', 'suppressScrollX', 'suppressScrollY', 'scrollXMarginOffset',
+ 'scrollYMarginOffset', 'includePadding'//, 'onScroll', 'scrollDown'
+ ];
+
+ for (let i=0, l=psOptions.length; i<l; i++) {
+ let opt = psOptions[i];
+ if ($attr[opt] !== undefined) {
+ options[opt] = self.$parse($attr[opt])();
+ }
+ }
+
+ $scope.$evalAsync(function() {
+ $elem.perfectScrollbar(options);
+ let onScrollHandler = self.$parse($attr.onScroll)
+ $elem.scroll(function(){
+ let scrollTop = $elem.scrollTop()
+ let scrollHeight = $elem.prop('scrollHeight') - $elem.height()
+ $scope.$apply(function() {
+ onScrollHandler($scope, {
+ scrollTop: scrollTop,
+ scrollHeight: scrollHeight
+ })
+ })
+ });
+ });
+
+ /*
+ $scope.update = (event:string): void => {
+ $scope.$evalAsync(function() {
+ //if ($attr.scrollDown == 'true' && event != 'mouseenter') {
+ if (event != 'mouseenter') {
+ setTimeout(function () {
+ $($elem).scrollTop($($elem).prop("scrollHeight"));
+ }, 100);
+ }
+ $elem.perfectScrollbar('update');
+ });
+ };
+ */
+
+ // This is necessary when you don't watch anything with the scrollbar
+ $elem.bind('mouseenter', function(){
+ //console.log("mouseenter");
+ $elem.perfectScrollbar('update');
+ });
+
+ $elem.bind('mouseleave', function(){
+ //console.log("mouseleave");
+ setTimeout(function () {
+ $(window).trigger('mouseup');
+ $elem.perfectScrollbar('update');
+ }, 10);
+ });
+
+ $elem.bind('click', function(){
+ //console.log("click");
+ // Wait 500 milliseconds until the collapse finish closing and update.
+ setTimeout(function () {
+ $elem.perfectScrollbar('update');
+ }, 500);
+ });
+
+ /**
+ * Check if the content of the scroller was changed, and if changed update the scroller.
+ * Because DOMSubtreeModified event is fire many time (while filling the content), I'm checking that
+ * there is at least 100 milliseconds between DOMSubtreeModified events to update the scrollbar.
+ * @type {boolean}
+ */
+ let insideDOMSubtreeModified=false;
+ $elem.bind('DOMSubtreeModified', function(){
+ if (insideDOMSubtreeModified==false) {
+ insideDOMSubtreeModified=true;
+ setTimeout(function () {
+ insideDOMSubtreeModified=false;
+ $elem.perfectScrollbar('update');
+ }, 100);
+ }
+ });
+
+ // Possible future improvement - check the type here and use the appropriate watch for non-arrays
+ if ($attr.refreshOnChange) {
+ $scope.$watchCollection($attr.refreshOnChange, function() {
+ $elem.perfectScrollbar('update');
+ });
+ }
+
+ /*
+ // this is from a pull request - I am not totally sure what the original issue is but seems harmless
+ if ($attr.refreshOnResize) {
+ self.$window.on('resize', function(e){$scope.update(e)});
+ }
+ */
+
+ $elem.bind('$destroy', function() {
+ //self.$window.off('resize', function(e){$scope.update(e)});
+ $elem.perfectScrollbar('destroy');
+ });
+
+ };
+
+ public static factory = ($templateCache: ng.ITemplateCacheService, $parse:any, $window:any)=> {
+ return new PerfectScrollerDirective($templateCache, $parse, $window);
+ };
+
+ }
+
+ PerfectScrollerDirective.factory.$inject = ['$templateCache','$parse','$window'];
+}
diff --git a/catalog-ui/app/scripts/directives/print-graph-screen/print-graph-screen.ts b/catalog-ui/app/scripts/directives/print-graph-screen/print-graph-screen.ts
new file mode 100644
index 0000000000..8204928e6f
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/print-graph-screen/print-graph-screen.ts
@@ -0,0 +1,211 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface IPrintGraphScreenScope extends ng.IScope {
+ entity:Models.Components.Component;
+ }
+
+
+ export class PrintGraphScreenDirective implements ng.IDirective {
+
+ constructor(
+ private $filter: ng.IFilterService,
+ private sdcMenu:Models.IAppMenu,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private urlToBase64Service:Sdc.Services.UrlToBase64Service
+ ) {}
+
+ scope = {
+ entity: '='
+ };
+ restrict = 'A';
+ link = (scope:IPrintGraphScreenScope, element:any) => {
+
+
+ element.bind('click', function() {
+ printScreen();
+ });
+
+
+ // TODO we need to implement export to PDF in cytoscape
+ let printScreen = ():void => {
+
+ //
+ // let pdf :any = new jsPDF('landscape', 'mm', 'a4');
+ // pdf.setProperties({
+ // title: scope.entity.name,
+ // subject: 'Design Snapshot for ' + scope.entity.name,
+ // author: scope.entity.creatorFullName,
+ // keywords: scope.entity.tags.join(', '),
+ // creator: scope.entity.creatorFullName
+ // });
+ //
+ // // A4 measures is 210 × 297 millimeters
+ // let pdfWidth :number = 297,
+ // pdfHeight :number = 210,
+ // leftColumnWidth :number = 80;
+ //
+ // //left bar background
+ // pdf.setDrawColor(0);
+ // pdf.setFillColor(248, 249, 251);
+ // pdf.rect(0, 0, leftColumnWidth, pdfHeight, 'F');
+ //
+ // //entity name
+ // pdf.setFontSize(12);
+ // pdf.setTextColor(38, 61, 77);
+ // let splitTitle :any = pdf.splitTextToSize(scope.entity.name, 50);
+ // pdf.text(22, 15 - (splitTitle.length - 1) * 2, splitTitle);
+ //
+ // //line
+ // pdf.setLineWidth(0.2);
+ // pdf.setDrawColor(208, 209, 213);
+ // pdf.line(0, 28, leftColumnWidth, 28);
+ //
+ //
+ // pdf.setFontSize(10);
+ // let properties :any = getPdfProperties();
+ //
+ // let topOffset :number = 39, lines;
+ // properties.forEach( (item:any) => {
+ // if (!item.value) {
+ // return;
+ // }
+ // if (item.title === 'Description:') {
+ // topOffset += 5;
+ // }
+ //
+ // pdf.setTextColor(38, 61, 77);
+ // pdf.text(5, topOffset, item.title);
+ // pdf.setTextColor(102, 102, 102);
+ // lines = pdf.splitTextToSize(item.value, 49);
+ // pdf.text(5 + item.offset, topOffset, lines[0]);
+ // if (lines.length > 1) {
+ // lines = pdf.splitTextToSize(item.value.substring(lines[0].length + 1), 65);
+ // if (lines.length > 8) {
+ // lines = lines.slice(0, 7);
+ // lines[lines.length - 1] += '...';
+ // }
+ // pdf.text(5, topOffset + 4, lines);
+ // topOffset += 4 * (lines.length);
+ // }
+ //
+ // topOffset += 6;
+ // });
+ //
+ //
+ // //another background in case the text was too long
+ // let declarationLineOffset :number = 176;
+ // pdf.setDrawColor(0);
+ // pdf.setFillColor(248, 249, 251);
+ // pdf.rect(0, declarationLineOffset, leftColumnWidth, pdfHeight - declarationLineOffset, 'F');
+ // //line
+ // pdf.setLineWidth(0.2);
+ // pdf.setDrawColor(208, 209, 213);
+ // pdf.line(0, declarationLineOffset, leftColumnWidth, declarationLineOffset);
+ //
+ // //declaration
+ // pdf.setFontSize(10.5);
+ // pdf.setTextColor(38, 61, 77);
+ // pdf.text(5, 185, 'Declaration');
+ // pdf.setFontSize(9);
+ // pdf.setTextColor(102, 102, 102);
+ // pdf.setFontType('bold');
+ // pdf.text(5, 190, this.$filter('translate')('PDF_FILE_DECLARATION_BOLD'));
+ // pdf.setFontType('normal');
+ // pdf.text(5, 194, pdf.splitTextToSize(this.$filter('translate')('PDF_FILE_DECLARATION'), 65));
+ //
+ // //entity icon
+ // let self = this;
+ // let addEntityIcon:Function = () => {
+ // let iconPath:string = self.sdcConfig.imagesPath + '/styles/images/';
+ // if (scope.entity.isService()) {
+ // iconPath += 'service-icons/' + scope.entity.icon + '.png';
+ // } else {
+ // iconPath += 'resource-icons/' + scope.entity.icon + '.png';
+ // }
+ // self.urlToBase64Service.downloadUrl(iconPath, (base64string:string):void => {
+ // if (base64string) {
+ // pdf.addImage(base64string, 'JPEG', 5, 7, 15, 15);
+ // }
+ // pdf.save(scope.entity.name + '.pdf');
+ // });
+ // };
+ //
+ // //actual snapshop of canvas
+ //
+ // let diagramDiv :any = document.getElementById('myDiagram');
+ // let diagram :any = null;// Sdc.Graph.Diagram.fromDiv(diagramDiv), canvasImg = new Image();
+ // diagram.startTransaction('print screen');
+ // let canvasImgBase64:any = diagram.makeImageData({
+ // //scale: 1,
+ // // size: new Sdc.Graph.Size(pdfHeight * 5, NaN),
+ // background: 'white',
+ // type: 'image/jpeg'
+ // });
+ // diagramDiv.firstElementChild.toDataURL();
+ // diagram.commitTransaction('print screen');
+ //
+ // canvasImg.onload = () => {
+ // if (canvasImg.height > 0) {
+ // let canvasImgRatio:number = Math.min((pdfWidth - leftColumnWidth - 15) / canvasImg.width, pdfHeight / canvasImg.height);
+ // let canvasImgWidth:number = canvasImg.width * canvasImgRatio,
+ // canvasImgHeight:number = canvasImg.height * canvasImgRatio;
+ // let canvasImgOffset:number = (pdfHeight - canvasImgHeight) / 2;
+ // pdf.addImage(canvasImg, 'JPEG', leftColumnWidth, canvasImgOffset, canvasImgWidth, canvasImgHeight);
+ //
+ // addEntityIcon();
+ // }
+ // };
+ //
+ // if(canvasImg.src === 'data:,') { //empty canvas
+ // addEntityIcon();
+ // } else {
+ // canvasImg.src = canvasImgBase64;
+ // }
+ };
+
+ let getPdfProperties = ():Array<any> => {
+ return [
+ {title: this.$filter('translate')('GENERAL_LABEL_TYPE'), value: scope.entity.getComponentSubType(), offset: 10},
+ {title: this.$filter('translate')('GENERAL_LABEL_VERSION'), value: scope.entity.version, offset: 15},
+ {title: this.$filter('translate')('GENERAL_LABEL_CATEGORY'), value: scope.entity.categories.length ? scope.entity.categories[0].name : '', offset: 16},
+ {title: this.$filter('translate')('GENERAL_LABEL_CREATION_DATE'), value: this.$filter('date')(scope.entity.creationDate, 'MM/dd/yyyy'), offset: 24},
+ {title: this.$filter('translate')('GENERAL_LABEL_AUTHOR'), value: scope.entity.creatorFullName, offset: 13},
+ {title: this.$filter('translate')('GENERAL_LABEL_CONTACT_ID'), value: scope.entity.contactId, offset: 41},
+ {title: this.$filter('translate')('GENERAL_LABEL_STATUS'), value: (<any>this.sdcMenu).LifeCycleStatuses[scope.entity.lifecycleState].text, offset: 13},
+ {title: this.$filter('translate')('GENERAL_LABEL_PROJECT_CODE'), value: scope.entity.projectCode, offset: 15},
+ {title: this.$filter('translate')('GENERAL_LABEL_DESCRIPTION'), value: scope.entity.description, offset: 20},
+ {title: this.$filter('translate')('GENERAL_LABEL_TAGS'), value: scope.entity.tags.join(', '), offset: 10}
+ ];
+ };
+
+ };
+
+ public static factory = ($filter:ng.IFilterService, sdcMenu:Models.IAppMenu, sdcConfig:Models.IAppConfigurtaion, urlToBase64Service:Sdc.Services.UrlToBase64Service)=> {
+ return new PrintGraphScreenDirective($filter, sdcMenu, sdcConfig, urlToBase64Service);
+ };
+
+ }
+
+ PrintGraphScreenDirective.factory.$inject = ['$filter', 'sdcMenu', 'sdcConfig', 'Sdc.Services.UrlToBase64Service'];
+}
diff --git a/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.html b/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.html
new file mode 100644
index 0000000000..b4583fd304
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.html
@@ -0,0 +1,82 @@
+<div class="data-type-fields-structure">
+ <div class="open-close">
+ <div class="open-close-button" data-ng-class="{'expand':expand,'collapse':!expand}" data-ng-click="expandAndCollapse()"></div>
+ <span class="data-type-name">{{typeName.replace("org.openecomp.datatypes.heat.","")}}</span>
+ </div>
+ <div data-ng-show="expand" data-ng-repeat="property in dataTypeProperties" class="property">
+ <div class="i-sdc-form-item property-name">
+ <div tooltips tooltip-content="{{property.name}}">
+ <input class="i-sdc-form-input"
+ type="text"
+ data-ng-disabled="true"
+ value="{{property.name}}"/>
+ </div>
+ </div>
+ <!--<div class="property-value">-->
+ <div data-ng-if="dataTypesService.isDataTypeForDataTypePropertyType(property,types)" class="inner-structure">
+ <fields-structure value-obj-ref="(valueObjRef[property.name])"
+ type-name="property.type"
+ parent-form-obj="parentFormObj"
+ fields-prefix-name="fieldsPrefixName+property.name"
+ read-only="readOnly"
+ default-value="{{currentTypeDefaultValue[property.name]}}"
+ types="types"></fields-structure>
+ </div>
+ <div data-ng-if="!dataTypesService.isDataTypeForDataTypePropertyType(property,types)" ng-switch="property.type">
+ <div ng-switch-when="map">
+ <type-map value-obj-ref="valueObjRef[property.name]"
+ schema-property="property.schema.property"
+ parent-form-obj="parentFormObj"
+ fields-prefix-name="fieldsPrefixName+property.name"
+ read-only="readOnly"
+ default-value="{{currentTypeDefaultValue[property.name]}}"
+ types="types"></type-map>
+ </div>
+ <div ng-switch-when="list">
+ <type-list value-obj-ref="valueObjRef[property.name]"
+ schema-property="property.schema.property"
+ parent-form-obj="parentFormObj"
+ fields-prefix-name="fieldsPrefixName+property.name"
+ read-only="readOnly"
+ default-value="{{currentTypeDefaultValue[property.name]}}"
+ types="types"></type-list>
+ </div>
+ <div ng-switch-default class="primitive-value-field">
+ <div class="i-sdc-form-item" data-ng-class="{error:(parentFormObj[fieldsPrefixName+property.name].$dirty && parentFormObj[fieldsPrefixName+property.name].$invalid)}">
+ <input class="i-sdc-form-input"
+ data-tests-id="{{fieldsPrefixName+property.name}}"
+ ng-if="!((property.simpleType||property.type) == 'boolean')"
+ data-ng-maxlength="100"
+ data-ng-disabled="readOnly"
+ maxlength="100"
+ data-ng-model="valueObjRef[property.name]"
+ type="text"
+ name="{{fieldsPrefixName+property.name}}"
+ data-ng-pattern="getValidationPattern((property.simpleType||property.type))"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="!parentFormObj[fieldsPrefixName+property.name].$error.pattern && ('integer'==property.type && parentFormObj[fieldsPrefixName+property.name].$setValidity('pattern', validateIntRange(valueObjRef[property.name])) || onValueChange(property.name, (property.simpleType||property.type)))"
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="{{fieldsPrefixName+property.name}}"
+ ng-if="(property.simpleType||property.type) == 'boolean'"
+ data-ng-disabled="readOnly"
+ name="{{fieldsPrefixName+property.name}}"
+ data-ng-change="onValueChange(property.name,'boolean')"
+ data-ng-model="valueObjRef[property.name]"
+ data-ng-options="option.v as option.n for option in [{ n: '', v: undefined }, { n: 'false', v: false }, { n: 'true', v: true }]">
+ </select>
+
+ <div class="input-error" data-ng-show="parentFormObj[fieldsPrefixName+property.name].$dirty && parentFormObj[fieldsPrefixName+property.name].$invalid">
+ <span ng-show="parentFormObj[fieldsPrefixName+property.name].$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="parentFormObj[fieldsPrefixName+property.name].$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ <span ng-show="parentFormObj[fieldsPrefixName+property.name].$error.customValidation" translate="PROPERTY_EDIT_MAP_UNIQUE_KEYS"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <!--</div>-->
+
+ </div>
+</div>
+
+
diff --git a/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.less b/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.less
new file mode 100644
index 0000000000..5c65fdc9dc
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.less
@@ -0,0 +1,90 @@
+.data-type-fields-structure{
+ background-color: @tlv_color_v;
+ padding:10px;
+ display: table-caption;
+ .open-close{
+ position: relative;
+ .open-close-button{
+ position: absolute;
+ top: 50%;
+ margin-top: -7px;
+ &.expand{
+ .sprite-new;
+ .expand-collapse-minus-icon;
+ }
+ &.collapse{
+ .sprite-new;
+ .expand-collapse-plus-icon;
+ }
+ }
+
+ }
+
+
+ .data-type-name{
+ .m_16_m;
+ margin-left: 22px;
+ }
+
+ .i-sdc-form-input:disabled{
+ .disabled;
+ }
+
+ .property{
+ display: flex;
+ min-width: 365px;
+ min-height: 46px;
+ input[type="text"],select{
+ width: 170px;
+ }
+ .property-name{
+ float: left;
+ margin-top: 8px;
+ }
+ .primitive-value-field{
+ float: right;
+ margin-top: 8px;
+ margin-left: 10px;
+ }
+ .inner-structure{
+ display: -webkit-box;
+ }
+ }
+
+ [ng-switch-when="map"]{
+ margin-top: 8px;
+ margin-left: 10px;
+ .map-item{
+ border: solid 1px @main_color_o;
+ min-width: 401px;
+ min-height: 69px;
+ float: none !important;
+ }
+ .add-map-item{
+ width: auto;
+ float: none;
+ &:nth-child(1){
+ position: relative;
+ top: 6px;
+ }
+ .add-btn{
+ float: none;
+ }
+ }
+
+ }
+
+ [ng-switch-when="list"]{
+ float: left;
+ margin-top: 8px;
+ margin-left: 10px;
+ min-width: 280px;
+ .dt-list-item {
+ border: solid 1px @main_color_o;
+ }
+ .list-value-items{
+ width:280px;
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts b/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts
new file mode 100644
index 0000000000..94567ca36b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 1/27/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface IDataTypeFieldsStructureScope extends ng.IScope {
+ parentFormObj:ng.IFormController;
+ dataTypeProperties:Array<Models.DataTypePropertyModel>;
+ typeName:string;
+ valueObjRef:any;
+ propertyNameValidationPattern: RegExp;
+ fieldsPrefixName:string;
+ readOnly:boolean;
+ currentTypeDefaultValue:any;
+ types:Models.DataTypesMap;
+ expandByDefault:boolean;
+ expand:boolean;
+ expanded:boolean;
+ dataTypesService:Sdc.Services.DataTypesService;
+
+ expandAndCollapse():void;
+ getValidationPattern(type:string):RegExp;
+ validateIntRange(value:string):boolean;
+ onValueChange(propertyName:string, type:string):void
+ }
+
+
+ export class DataTypeFieldsStructureDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private DataTypesService:Sdc.Services.DataTypesService,
+ private PropertyNameValidationPattern: RegExp,
+ private ValidationUtils:Sdc.Utils.ValidationUtils) {
+ }
+
+ scope = {
+ valueObjRef: '=',
+ typeName: '=',
+ parentFormObj: '=',
+ fieldsPrefixName: '=',
+ readOnly: '=',
+ defaultValue: '@',
+ types: '=',
+ expandByDefault: '='
+ };
+
+ restrict = 'E';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.html');
+ };
+ public types=Utils.Constants.PROPERTY_DATA.TYPES;
+
+ //get data type properties array and return object with the properties and their default value
+ //(for example: get: [{name:"prop1",defaultValue:1 ...},{name:"prop2", defaultValue:"bla bla" ...}]
+ // return: {prop1: 1, prop2: "bla bla"}
+ private getDefaultValue = (dataTypeProperties:Array<Models.DataTypePropertyModel>):any => {
+ let defaultValue = {};
+ for(let i=0; i < dataTypeProperties.length; i++){
+ if(dataTypeProperties[i].type!='string'){
+ if(dataTypeProperties[i].defaultValue){
+ defaultValue[dataTypeProperties[i].name] = JSON.parse(dataTypeProperties[i].defaultValue);
+ }
+ }else{
+ defaultValue[dataTypeProperties[i].name] = dataTypeProperties[i].defaultValue;
+ }
+ }
+ return defaultValue;
+ };
+
+ private initDataOnScope = (scope:any, $attr:any):void =>{
+ scope.dataTypesService = this.DataTypesService;
+ scope.dataTypeProperties = this.DataTypesService.getFirsLevelOfDataTypeProperties(scope.typeName,scope.types);
+ if($attr.defaultValue){
+ scope.currentTypeDefaultValue = JSON.parse($attr.defaultValue);
+ }else{
+ scope.currentTypeDefaultValue = this.getDefaultValue(scope.dataTypeProperties);
+ }
+
+ if(!scope.valueObjRef) {
+ scope.valueObjRef = {};
+ }
+
+ _.forEach(scope.currentTypeDefaultValue, (value, key)=> {
+ if(!scope.valueObjRef[key]){
+ if(typeof scope.currentTypeDefaultValue[key] == 'object'){
+ angular.copy(scope.currentTypeDefaultValue[key], scope.valueObjRef[key]);
+ }else{
+ scope.valueObjRef[key] = scope.currentTypeDefaultValue[key];
+ }
+ }
+ });
+ };
+
+ private rerender = (scope:any):void =>{
+ scope.expanded = false;
+ scope.expand = false;
+ if(scope.expandByDefault){
+ scope.expandAndCollapse();
+ }
+ };
+
+ link = (scope:IDataTypeFieldsStructureScope, element:any, $attr:any) => {
+ scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+
+ scope.$watchCollection('[typeName,fieldsPrefixName]', (newData:any):void => {
+ this.rerender(scope);
+ });
+
+
+ scope.expandAndCollapse = ():void => {
+ if(!scope.expanded){
+ this.initDataOnScope(scope,$attr);
+ scope.expanded=true;
+ }
+ scope.expand=!scope.expand;
+ };
+
+ scope.getValidationPattern = (type:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(type);
+ };
+
+ scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ scope.onValueChange = (propertyName:string, type:string):void => {
+ scope.valueObjRef[propertyName] = !angular.isUndefined(scope.valueObjRef[propertyName]) ? scope.valueObjRef[propertyName] : scope.currentTypeDefaultValue[propertyName];
+ if(scope.valueObjRef[propertyName] && type != 'string'){
+ scope.valueObjRef[propertyName] = JSON.parse(scope.valueObjRef[propertyName]);
+ }
+ };
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService,
+ DataTypesService:Sdc.Services.DataTypesService,
+ PropertyNameValidationPattern:RegExp,
+ ValidationUtils:Sdc.Utils.ValidationUtils)=> {
+ return new DataTypeFieldsStructureDirective($templateCache,DataTypesService,PropertyNameValidationPattern,ValidationUtils);
+ };
+ }
+
+ DataTypeFieldsStructureDirective.factory.$inject = ['$templateCache','Sdc.Services.DataTypesService','PropertyNameValidationPattern','ValidationUtils'];
+}
diff --git a/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.html b/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.html
new file mode 100644
index 0000000000..410a24e62b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.html
@@ -0,0 +1,57 @@
+<div>
+ <div data-ng-if="!isSchemaTypeDataType">
+ <div class="i-sdc-form-item list-new-item" data-ng-class="{error:(parentFormObj['listNewItem'+fieldsPrefixName].$dirty && parentFormObj['listNewItem'+fieldsPrefixName].$invalid)}">
+ <input class="i-sdc-form-input"
+ data-tests-id="listNewItem{{fieldsPrefixName}}"
+ ng-if="!((schemaProperty.simpleType||schemaProperty.type) == 'boolean')"
+ data-ng-disabled="readOnly"
+ data-ng-model="listNewItem.value"
+ type="text"
+ name="listNewItem{{fieldsPrefixName}}"
+ data-ng-pattern="getValidationPattern((schemaProperty.simpleType||schemaProperty.type))"
+ data-ng-model-options="{ debounce: 200 }"
+ placeholder="Type a value and then click ADD"
+ data-ng-maxlength="maxLength"
+ maxlength="{{maxLength}}"
+ sdc-keyboard-events="" key-enter="schemaProperty.type && !parentFormObj['listNewItem'+fieldsPrefixName].$invalid && listNewItem.value && addListItem"
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="listNewItem{{fieldsPrefixName}}"
+ ng-if="(schemaProperty.simpleType||schemaProperty.type) == 'boolean'"
+ data-ng-disabled="readOnly"
+ name="listNewItem{{fieldsPrefixName}}"
+ data-ng-model="listNewItem.value">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+ <div class="input-error" data-ng-show="parentFormObj['listNewItem'+fieldsPrefixName].$dirty && parentFormObj['listNewItem'+fieldsPrefixName].$invalid">
+ <span ng-show="parentFormObj['listNewItem'+fieldsPrefixName].$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ <span ng-show="parentFormObj['listNewItem'+fieldsPrefixName].$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span>
+ </div>
+ </div>
+ <div class="add-btn add-list-item" data-tests-id="add-list-item{{fieldsPrefixName}}"
+ data-ng-class="{'disabled': readOnly || !schemaProperty.type || parentFormObj['listNewItem'+fieldsPrefixName].$invalid || !listNewItem.value}" data-ng-click="addListItem()">Add</div>
+ <div class="list-value-items">
+ <span class="list-value-item" data-ng-repeat="value in valueObjRef track by $index">
+ {{value}}
+ <span class="delete-list-item sprite-new small-x-button" data-ng-click="deleteListItem($index)"></span>
+ </span>
+ </div>
+ </div>
+ <div data-ng-if="isSchemaTypeDataType">
+ <div class="dt-list">
+ <div data-ng-repeat="value in valueObjRef track by $index" class="dt-list-item">
+ <span class="delete-dt-list-item" data-ng-click="deleteListItem($index)"></span>
+ <fields-structure value-obj-ref="valueObjRef[$index]"
+ type-name="schemaProperty.type"
+ parent-form-obj="parentFormObj"
+ fields-prefix-name="fieldsPrefixName+''+$index"
+ read-only="readOnly"
+ types="types"></fields-structure>
+ </div>
+ <div class="add-btn add-list-item" data-tests-id="add-list-item"
+ data-ng-class="{'disabled': readOnly}" data-ng-click="listNewItem.value='{}';addListItem();">Add</div>
+ </div>
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.less b/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.less
new file mode 100644
index 0000000000..eb4214e135
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.less
@@ -0,0 +1,85 @@
+.list-new-item{
+ float: left;
+ width: 50%;
+ min-width: 221px;
+ margin-right: 15px;
+ input{
+ min-width: 221px;
+ }
+}
+
+.list-value-items{
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+ border: 1px solid @main_color_o;
+ padding-bottom: 10px;
+ min-height: 100px;
+ clear: both;
+ background-color: white;
+ .list-value-item{
+ display: inline-block;
+ background-color: @tlv_color_v;
+ margin: 10px 0 0 10px;
+ padding-left: 8px;
+ .delete-list-item{
+ margin: 0 6px 0 10px;
+ .hand;
+ }
+ }
+}
+
+.add-btn {
+ .f-color.a;
+ .f-type._14_m;
+ .hand;
+
+ &.add-list-item {
+ float: left;
+ margin-top: 5px;
+ width: 44px;
+ }
+
+ &:before {
+ .sprite-new;
+ .plus-icon;
+ margin-right: 5px;
+ content: "";
+
+ }
+ &:hover {
+ .f-color.b;
+ &:before {
+ .sprite-new;
+ .plus-icon-hover;
+ }
+ }
+
+}
+
+.dt-list{
+ display: table-caption;
+ .dt-list-item {
+ border-radius: 3px;
+ background-color: @tlv_color_v;
+ display: inline-block;
+ .delete-dt-list-item{
+ float: right;
+ position: relative;
+ top: 5px;
+ right: 5px;
+ .sprite-new;
+ .delete-icon;
+ &:hover{
+ .delete-icon-hover;
+ }
+ }
+ .data-type-name{
+ margin-right: 16px;
+ }
+ }
+ &>.add-list-item{
+ float:none;
+ }
+}
+
diff --git a/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.ts b/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.ts
new file mode 100644
index 0000000000..ce5ee1ffa6
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/type-list/type-list-directive.ts
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/15/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ /// import Model = go.Model;
+
+ export interface ITypeListScope extends ng.IScope {
+ parentFormObj:ng.IFormController;
+ schemaProperty:Models.SchemaProperty;
+ isSchemaTypeDataType:boolean;
+ valueObjRef:any;
+ propertyNameValidationPattern: RegExp;
+ fieldsPrefixName:string;
+ readOnly:boolean;
+ listDefaultValue:any;
+ types:Models.DataTypesMap;
+ listNewItem:any;
+ maxLength:number;
+
+ getValidationPattern(type:string):RegExp;
+ validateIntRange(value:string):boolean;
+ addListItem():void;
+ deleteListItem(listItemIndex:number):void
+ }
+
+
+ export class TypeListDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private DataTypesService:Sdc.Services.DataTypesService,
+ private PropertyNameValidationPattern: RegExp,
+ private ValidationUtils:Sdc.Utils.ValidationUtils) {
+ }
+
+ scope = {
+ valueObjRef: '=',//ref to list object in the parent value object
+ schemaProperty: '=',//get the schema.property object
+ parentFormObj: '=',//ref to parent form (get angular form object)
+ fieldsPrefixName: '=',//prefix for form fields names
+ readOnly: '=',//is form read only
+ defaultValue: '@',//this list default value
+ types: '=',//data types list
+ maxLength: '='
+ };
+
+ restrict = 'E';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/property-types/type-list/type-list-directive.html');
+ };
+
+ link = (scope:ITypeListScope, element:any, $attr:any) => {
+ scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+
+ //reset valueObjRef when schema type is changed
+ scope.$watchCollection('schemaProperty.type', (newData:any):void => {
+ scope.isSchemaTypeDataType = this.DataTypesService.isDataTypeForSchemaType(scope.schemaProperty,scope.types);
+ //insert 1 empty item dt by default
+ if(scope.isSchemaTypeDataType && (!scope.valueObjRef||!scope.valueObjRef.length)){
+ scope.valueObjRef = scope.valueObjRef ||[];
+ scope.valueObjRef.push({});
+ }
+ });
+
+ //when user brows between properties in "edit property form"
+ scope.$watchCollection('fieldsPrefixName', (newData:any):void => {
+ scope.listNewItem={value:''};
+
+ if($attr.defaultValue){
+ scope.listDefaultValue = JSON.parse($attr.defaultValue);
+ }
+ });
+
+ scope.getValidationPattern = (type:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(type);
+ };
+
+ scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ scope.addListItem = ():void => {
+ scope.valueObjRef = scope.valueObjRef ||[];
+ let newVal = ((scope.schemaProperty.simpleType||scope.schemaProperty.type)==Utils.Constants.PROPERTY_TYPES.STRING?scope.listNewItem.value:JSON.parse(scope.listNewItem.value));
+ scope.valueObjRef.push(newVal);
+ scope.listNewItem.value = "";
+ };
+
+ scope.deleteListItem = (listItemIndex:number):void => {
+ scope.valueObjRef.splice(listItemIndex,1);
+ if (!scope.valueObjRef.length) {
+ if (scope.listDefaultValue ) {
+ angular.copy(scope.listDefaultValue, scope.valueObjRef);
+ }
+ }
+ };
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService,
+ DataTypesService:Sdc.Services.DataTypesService,
+ PropertyNameValidationPattern:RegExp,
+ ValidationUtils:Sdc.Utils.ValidationUtils)=> {
+ return new TypeListDirective($templateCache,DataTypesService,PropertyNameValidationPattern,ValidationUtils);
+ };
+ }
+
+ TypeListDirective.factory.$inject = ['$templateCache','Sdc.Services.DataTypesService','PropertyNameValidationPattern','ValidationUtils'];
+}
+
diff --git a/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.html b/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.html
new file mode 100644
index 0000000000..ed82b840dc
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.html
@@ -0,0 +1,70 @@
+<div>
+ <div data-ng-repeat="i in getNumber(mapKeys.length) track by $index" class="map-item" data-ng-class="{'primitive-value-map':!isSchemaTypeDataType}">
+ <div class="i-sdc-form-item map-item-field" data-ng-class="{error:(parentFormObj['mapKey'+fieldsPrefixName+$index].$dirty && parentFormObj['mapKey'+fieldsPrefixName+$index].$invalid)}">
+ <label class="i-sdc-form-label required">Key</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="mapKey{{fieldsPrefixName}}{{$index}}"
+ data-ng-model="mapKeys[$index]"
+ type="text"
+ name="mapKey{{fieldsPrefixName}}{{$index}}"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="changeKeyOfMap(mapKeys[$index], $index,'mapKey'+fieldsPrefixName+$index);$event.stopPropagation();"
+ data-ng-disabled="readOnly"
+ data-required
+ autofocus/>
+ <div class="input-error" data-ng-show="parentFormObj['mapKey'+fieldsPrefixName+$index].$dirty && parentFormObj['mapKey'+fieldsPrefixName+$index].$invalid">
+ <span ng-show="parentFormObj['mapKey'+fieldsPrefixName+$index].$error.keyExist" translate="PROPERTY_EDIT_MAP_UNIQUE_KEYS"></span>
+ <span ng-show="parentFormObj['mapKey'+fieldsPrefixName+$index].$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Key' }"></span>
+ </div>
+ </div>
+ <div data-ng-if="!isSchemaTypeDataType" class="i-sdc-form-item map-item-field" data-ng-class="{error:(parentFormObj['mapValue'+fieldsPrefixName+$index].$dirty && parentFormObj['mapValue'+fieldsPrefixName+$index].$invalid)}">
+ <label class="i-sdc-form-label required">Value</label>
+ <input class="i-sdc-form-input"
+ ng-if="!((schemaProperty.simpleType||schemaProperty.type) == 'boolean')"
+ data-ng-disabled="readOnly"
+ data-ng-model="valueObjRef[mapKeys[$index]]"
+ type="text"
+ name="mapValue{{fieldsPrefixName}}{{$index}}"
+ data-tests-id="mapValue{{fieldsPrefixName}}{{$index}}"
+ data-ng-pattern="getValidationPattern((schemaProperty.simpleType||schemaProperty.type))"
+ data-ng-change="!parentFormObj['mapValue'+fieldsPrefixName+$index].$error.pattern && parseToCorrectType(valueObjRef, key, (schemaProperty.simpleType||schemaProperty.type))"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-maxlength="maxLength"
+ maxlength="{{maxLength}}"
+ data-required
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="mapValue{{fieldsPrefixName}}{{$index}}"
+ ng-if="(schemaProperty.simpleType||schemaProperty.type) == 'boolean'"
+ data-ng-disabled="readOnly"
+ name="mapValue{{fieldsPrefixName}}{{$index}}"
+ data-ng-model="valueObjRef[mapKeys[$index]]"
+ data-required>
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+ <div class="input-error" data-ng-show="parentFormObj['mapValue'+fieldsPrefixName+$index].$dirty && parentFormObj['mapValue'+fieldsPrefixName+$index].$invalid">
+ <span ng-show="parentFormObj['mapValue'+fieldsPrefixName+$index].$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value' }"></span>
+ <span ng-show="parentFormObj['mapValue'+fieldsPrefixName+$index].$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ <span ng-show="parentFormObj['mapValue'+fieldsPrefixName+$index].$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span>
+ </div>
+ </div>
+ <div data-ng-if="isSchemaTypeDataType" class="i-sdc-form-item map-item-field">
+ <label class="i-sdc-form-label">Value</label>
+ <fields-structure value-obj-ref="valueObjRef[mapKeys[$index]]"
+ type-name="schemaProperty.type"
+ parent-form-obj="parentFormObj"
+ fields-prefix-name="'mapValue'+fieldsPrefixName+''+$index"
+ read-only="readOnly"
+ types="types"
+ ></fields-structure>
+ </div>
+ <span ng-click="deleteMapItem($index)" class="delete-map-item" data-tests-id="delete-map-item{{fieldsPrefixName}}{{$index}}" data-ng-class="{'disabled': readOnly}"></span>
+ </div>
+ <div class="add-map-item" data-ng-class="{'schema-data-type':isSchemaTypeDataType}">
+ <div class="add-btn" data-tests-id="add-map-item"
+ data-ng-class="{'disabled': readOnly || !schemaProperty.type || mapKeys.indexOf('')>-1}" data-ng-click="addMapItemFields()">Add</div>
+ </div>
+</div>
+
diff --git a/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.less b/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.less
new file mode 100644
index 0000000000..2480b626f2
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.less
@@ -0,0 +1,83 @@
+.add-map-item{
+ &:nth-child(odd){
+ float: right;
+ }
+ &:nth-child(1){
+ float: none;
+ .add-btn{
+ float: none;
+ }
+ }
+ width: 400px;
+ .add-btn{
+ width: 44px;
+ float: right;
+ }
+ &.schema-data-type{
+ float:none;
+ .add-btn{
+ float: none;
+ }
+ }
+}
+
+.add-btn {
+ .f-color.a;
+ .f-type._14_m;
+ .hand;
+
+ &:before {
+ .sprite-new;
+ .plus-icon;
+ margin-right: 5px;
+ content: "";
+
+ }
+ &:hover {
+ .f-color.b;
+ &:before {
+ .sprite-new;
+ .plus-icon-hover;
+ }
+ }
+
+}
+
+.map-item{
+ min-width: 389px;
+ min-height: 65px;
+ background-color: @tlv_color_v;
+ border-radius: 3px;
+ margin-bottom: 8px;
+ float: left;
+ display: flex;
+ &:nth-child(even).primitive-value-map{
+ float: right;
+ }
+ .delete-map-item {
+ float: right;
+ position: relative;
+ top: 5px;
+ right: 5px;
+ .sprite-new;
+ .delete-icon;
+ &:hover{
+ .delete-icon-hover;
+ }
+ }
+ .map-item-field {
+ margin: 7px 12px !important;
+ float: left;
+ min-width: 170px;
+ min-height: 50px;
+ select{
+ width:171px;
+ }
+ input[type="text"]{
+ width: 170px;
+ }
+ &>.data-type-fields-structure{
+ padding: 0;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.ts b/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.ts
new file mode 100644
index 0000000000..d94ccf3886
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/property-types/type-map/type-map-directive.ts
@@ -0,0 +1,157 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/15/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ITypeMapScope extends ng.IScope {
+ parentFormObj:ng.IFormController;
+ schemaProperty:Models.SchemaProperty;
+ isSchemaTypeDataType:boolean;
+ valueObjRef:any;
+ mapKeys:Array<string>;//array of map keys
+ propertyNameValidationPattern: RegExp;
+ fieldsPrefixName:string;
+ readOnly:boolean;
+ mapDefaultValue:any;
+ types:Models.DataTypesMap;
+ maxLength:number;
+
+ getValidationPattern(type:string):RegExp;
+ validateIntRange(value:string):boolean;
+ changeKeyOfMap(newKey:string, index:number, fieldName:string):void;
+ deleteMapItem(index:number):void;
+ addMapItemFields():void;
+ parseToCorrectType(objectOfValues:any, locationInObj:string, type:string):void;
+ getNumber(num:number):Array<any>;
+ }
+
+
+ export class TypeMapDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private DataTypesService:Sdc.Services.DataTypesService,
+ private PropertyNameValidationPattern: RegExp,
+ private ValidationUtils:Sdc.Utils.ValidationUtils,
+ private $timeout: ng.ITimeoutService) {
+ }
+
+ scope = {
+ valueObjRef: '=',//ref to map object in the parent value object
+ schemaProperty: '=',//get the schema.property object
+ parentFormObj: '=',//ref to parent form (get angular form object)
+ fieldsPrefixName: '=',//prefix for form fields names
+ readOnly: '=',//is form read only
+ defaultValue: '@',//this map default value
+ types: '=',//data types list
+ maxLength: '='
+ };
+
+ restrict = 'E';
+ replace = true;
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/property-types/type-map/type-map-directive.html');
+ };
+
+ link = (scope:ITypeMapScope, element:any, $attr:any) => {
+ scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+
+ //reset valueObjRef and mapKeys when schema type is changed
+ scope.$watchCollection('schemaProperty.type', (newData:any):void => {
+ scope.isSchemaTypeDataType = this.DataTypesService.isDataTypeForSchemaType(scope.schemaProperty,scope.types);
+ if(scope.valueObjRef){
+ scope.mapKeys = Object.keys(scope.valueObjRef);
+ }
+ });
+
+ //when user brows between properties in "edit property form"
+ scope.$watchCollection('fieldsPrefixName', (newData:any):void => {
+ if(!scope.valueObjRef) {
+ scope.valueObjRef={};
+ }
+ scope.mapKeys = Object.keys(scope.valueObjRef);
+
+ if($attr.defaultValue){
+ scope.mapDefaultValue = JSON.parse($attr.defaultValue);
+ }
+ });
+
+ //return dummy array in order to prevent rendering map-keys ng-repeat again when a map key is changed
+ scope.getNumber = (num:number):Array<any> => {
+ return new Array(num);
+ };
+
+ scope.getValidationPattern = (type:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(type);
+ };
+
+ scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ scope.changeKeyOfMap = (newKey:string, index:number, fieldName:string) : void => {
+ let oldKey = Object.keys(scope.valueObjRef)[index];
+ if(Object.keys(scope.valueObjRef).indexOf(newKey)>-1){
+ scope.parentFormObj[fieldName].$setValidity('keyExist', false);
+ }else{
+ scope.parentFormObj[fieldName].$setValidity('keyExist', true);
+ if(!scope.parentFormObj[fieldName].$invalid){
+ angular.copy(JSON.parse(JSON.stringify(scope.valueObjRef).replace('"'+oldKey+'":', '"'+newKey+'":')),scope.valueObjRef);//update key
+ }
+ }
+ };
+
+ scope.deleteMapItem=(index:number):void=>{
+ delete scope.valueObjRef[scope.mapKeys[index]];
+ scope.mapKeys.splice(index,1);
+ if (!scope.mapKeys.length) {//only when user removes all pairs of key-value fields - put the default
+ if ( scope.mapDefaultValue ) {
+ angular.copy(scope.mapDefaultValue, scope.valueObjRef);
+ scope.mapKeys = Object.keys(scope.valueObjRef);
+ }
+ }
+ };
+
+ scope.addMapItemFields = ():void => {
+ scope.valueObjRef['']= null;
+ scope.mapKeys = Object.keys(scope.valueObjRef);
+ };
+
+ scope.parseToCorrectType = (objectOfValues:any, locationInObj:string, type:string):void => {
+ if(objectOfValues[locationInObj] && type != Utils.Constants.PROPERTY_TYPES.STRING){
+ objectOfValues[locationInObj] = JSON.parse(objectOfValues[locationInObj]);
+ }
+ }
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService,
+ DataTypesService:Sdc.Services.DataTypesService,
+ PropertyNameValidationPattern:RegExp,
+ ValidationUtils:Sdc.Utils.ValidationUtils,
+ $timeout: ng.ITimeoutService)=> {
+ return new TypeMapDirective($templateCache,DataTypesService,PropertyNameValidationPattern,ValidationUtils,$timeout);
+ };
+ }
+
+ TypeMapDirective.factory.$inject = ['$templateCache','Sdc.Services.DataTypesService','PropertyNameValidationPattern','ValidationUtils','$timeout'];
+}
diff --git a/catalog-ui/app/scripts/directives/punch-out/punch-out.ts b/catalog-ui/app/scripts/directives/punch-out/punch-out.ts
new file mode 100644
index 0000000000..f00b7971a9
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/punch-out/punch-out.ts
@@ -0,0 +1,99 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface IPunchOutScope extends ng.IScope {
+ name: string;
+ data: any;
+ user: Models.IUserProperties;
+ onEvent: Function;
+ }
+
+ export class PunchOutDirective implements ng.IDirective {
+
+ constructor(
+ private sdcConfig: Sdc.Models.IAppConfigurtaion) {}
+
+ scope = {
+ name: '=',
+ data: '=',
+ user: '=',
+ onEvent: '&'
+ };
+
+ replace = false;
+ restrict = 'E';
+
+ link = (scope: IPunchOutScope, element: ng.IAugmentedJQuery):void => {
+ // global registry object
+ let PunchOutRegistry = window['PunchOutRegistry'];
+
+ let render = ():void => {
+ let cookieConfig = this.sdcConfig.cookie;
+ let props = {
+ name: scope.name,
+ options: {
+ data: scope.data,
+ apiRoot: this.sdcConfig.api.root,
+ apiHeaders: {
+ userId: {
+ name: cookieConfig.userIdSuffix,
+ value: scope.user.userId
+ },
+ userFirstName: {
+ name: cookieConfig.userFirstName,
+ value: scope.user.firstName
+ },
+ userLastName: {
+ name: cookieConfig.userLastName,
+ value: scope.user.lastName
+ },
+ userEmail: {
+ name: cookieConfig.userEmail,
+ value: scope.user.email
+ }
+ }
+ },
+ onEvent: (...args) => {
+ scope.$apply(() => {
+ scope.onEvent().apply(null, args);
+ });
+ }
+ };
+ PunchOutRegistry.render(props, element[0]);
+ };
+
+ let unmount = ():void => {
+ PunchOutRegistry.unmount(element[0]);
+ };
+
+ scope.$watch('data', render);
+ element.on('$destroy', unmount);
+ };
+
+ public static factory = (sdcConfig: Sdc.Models.IAppConfigurtaion) => {
+ return new PunchOutDirective(sdcConfig);
+ };
+
+ }
+
+ PunchOutDirective.factory.$inject = ['sdcConfig'];
+}
diff --git a/catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab-directive.ts b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab-directive.ts
new file mode 100644
index 0000000000..26390a7501
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab-directive.ts
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class SdcSingleTabDirective implements ng.IDirective {
+
+ constructor(private $compile:ng.ICompileService, private $parse:ng.IParseService) {
+ }
+ restrict = 'E';
+
+ link = (scope, elem:any, attrs:any, ctrl:any) => {
+ if(!elem.attr('inner-sdc-single-tab')) {
+ let name = this.$parse(elem.attr('ctrl'))(scope);
+ elem = elem.removeAttr('ctrl');
+ elem.attr('inner-sdc-single-tab', name);
+ this.$compile(elem)(scope);
+ }
+ };
+
+ public static factory = ($compile:ng.ICompileService, $parse:ng.IParseService)=> {
+ return new SdcSingleTabDirective($compile, $parse);
+ };
+ }
+
+ export class InnerSdcSingleTabDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ singleTab: "=",
+ isViewOnly: "="
+ };
+
+ replace = true;
+ restrict = 'A';
+ controller = '@';
+ template = '<div ng-include src="singleTab.templateUrl"></div>';
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new InnerSdcSingleTabDirective($templateCache);
+ };
+ }
+
+ SdcSingleTabDirective.factory.$inject = ['$compile', '$parse'];
+ InnerSdcSingleTabDirective.factory.$inject = ['$templateCache'];
+
+}
diff --git a/catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab.less b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab.less
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab.less
@@ -0,0 +1 @@
+
diff --git a/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive-view.html b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive-view.html
new file mode 100644
index 0000000000..d51d221922
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive-view.html
@@ -0,0 +1,17 @@
+<div class="sdc-tabs-body">
+ <div class="sdc-tabs" ng-class="{'not-active': !isActive}">
+ <div class="sdc-tab-arrow" ng-click="isActive = !isActive">
+ <span class="sprite-new close-open-left-arrow" ng-class="{'close-open-right-arrow': !isActive}"></span>
+ </div>
+ <div ng-repeat="tab in tabs track by $index">
+ <div class="sdc-tab" ng-click="onTabSelected(tab)" data-tests-id="{{tab.name}}-tab" ng-mouseenter="hover = true"
+ ng-mouseleave="hover = false"
+ ng-class="{'last-tab':$last, 'first-tab': $first, 'selected' :tab.name === selectedTab.name }">
+ <div class="sdc-tab-icon sprite-new {{tab.icon}}" ng-class="{'selected' :tab.name === selectedTab.name, 'hover': hover}"></div>
+ </div>
+ </div>
+ </div>
+ <div class="sdc-single-tab-content" ng-if="isActive">
+ <sdc-single-tab class="sdc-single-tab-content-body" ng-if="selectedTab" ctrl="selectedTab.controller" data-dests-id="selected-tab" single-tab="selectedTab" is-view-only="isViewOnly"></sdc-single-tab>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive.ts b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive.ts
new file mode 100644
index 0000000000..91d1744ae5
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs-directive.ts
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 7/28/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ISdcTabsDirectiveScope extends ng.IScope {
+ tabs:Array<Models.Tab>;
+ selectedTab: Models.Tab;
+ isActive: boolean;
+ onTabSelected(selectedTab: Models.Tab);
+ }
+
+ export class SdcTabsDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ tabs: "=",
+ selectedTab: "=?",
+ isViewOnly: "="
+ };
+
+ replace = true;
+ restrict = 'E';
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/sdc-tabs/sdc-tabs-directive-view.html');
+ };
+
+ link = (scope:ISdcTabsDirectiveScope) => {
+ scope.isActive = true;
+
+ if(!scope.selectedTab){
+ scope.selectedTab = scope.tabs[0];
+ }
+
+ scope.onTabSelected = (selectedTab: Models.Tab) => {
+ scope.selectedTab = selectedTab;
+ }
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new SdcTabsDirective($templateCache);
+ };
+ }
+
+ SdcTabsDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs.less b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs.less
new file mode 100644
index 0000000000..ad390010ed
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/sdc-tabs/sdc-tabs.less
@@ -0,0 +1,68 @@
+.sdc-tabs-body {
+ height: 100%;
+ width: 330px;
+ position: absolute;
+ .sdc-tabs {
+ display: inline-block;
+ width: 40px;
+ vertical-align: top;
+ position: relative;
+ z-index: 99;
+ right: 332px;
+ .sdc-tab-arrow {
+ cursor: pointer;
+ width: 40px;
+ height: 20px;
+ background-color: @tlv_color_u;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.17);
+ text-align: center;
+ padding: 1px 4px 0px 0px;
+
+ &:hover {
+ background-color: @main_color_o;
+ }
+
+ }
+ .sdc-tab {
+ cursor: pointer;
+ width: 40px;
+ height: 43px;
+ background-color: @tlv_color_u;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.17);
+ text-align: center;
+
+ .sdc-tab-icon {
+ margin-top: 12px;
+ }
+ }
+ .selected {
+ background-color: @tlv_color_t;
+ }
+
+ .last-tab {
+ border-bottom-left-radius: 12px;
+ }
+ }
+
+ .not-active {
+ // position: absolute;
+ right: 41px;
+ }
+
+ .sdc-single-tab-content {
+ padding: 15px 0px 0px 0px;
+ width: 290px;
+ background-color: @tlv_color_t;
+ height: 100%;
+ display: inline-block;
+ bottom: 0;
+ top: 0;
+ position: absolute;
+ box-shadow: 0.3px 1px 3px rgba(24, 24, 25, 0.42);
+ right: 331px;
+ .sdc-single-tab-content-body {
+ height: 100%;
+ display: flex;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.html b/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.html
new file mode 100644
index 0000000000..7d8a883b33
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.html
@@ -0,0 +1,54 @@
+<div class="structure-tree">
+ <div class="component-container">
+ <div class="{{component.iconSprite}} small" ng-class="structureTree.serviceRoot.icon"></div>
+ <div class="component-container-text" tooltip-class="tooltip-custom break-word-tooltip" tooltips tooltip-content="&#8203;{{structureTree.serviceRoot.name}}"> {{structureTree.serviceRoot.name}}</div>
+ </div>
+ <ul>
+ <li data-ng-repeat="firstLevelResourcesInstances in structureTree.serviceRoot.resourceInstancesList">
+ <div class="component-container">
+ <div class="small {{firstLevelResourcesInstances.icon}}" ng-class="{'sprite-resource-icons': !component.isProduct(), 'sprite-services-icons': component.isProduct()}">
+ <div data-ng-class="{'non-certified':!firstLevelResourcesInstances.certified}"
+ tooltips tooltip-side="top" tooltip-content="Not certified">
+ </div>
+ </div>
+ <div class="component-container-text" tooltips tooltip-content="{{firstLevelResourcesInstances.name}}" > {{firstLevelResourcesInstances.name}} </div>
+ </div>
+ <ul>
+ <li data-ng-repeat="secondLevelResourcesInstances in firstLevelResourcesInstances.resourceInstancesList">
+ <div class="component-container">
+ <div class="sprite-resource-icons small" ng-class="secondLevelResourcesInstances.icon">
+ <div data-ng-class="{'non-certified':!secondLevelResourcesInstances.certified}"
+ tooltips tooltip-side="top" tooltip-content="Not certified">
+ </div>
+ </div>
+ <div class="component-container-text" tooltips tooltip-content="{{secondLevelResourcesInstances.name}}"> {{secondLevelResourcesInstances.name}} </div>
+ </div>
+ <ul>
+ <li data-ng-repeat="thirdLevelResourcesInstances in secondLevelResourcesInstances.resourceInstancesList">
+ <div class="component-container">
+ <div class="sprite-resource-icons small" ng-class="thirdLevelResourcesInstances.icon">
+ <div data-ng-class="{'non-certified':!thirdLevelResourcesInstances.certified}"
+ tooltips tooltip-side="top" tooltip-content="Not certified">
+ </div>
+ </div>
+ <div class="component-container-text" tooltips tooltip-content="{{thirdLevelResourcesInstances.name}}" > {{thirdLevelResourcesInstances.name}} </div>
+ </div>
+ <ul>
+ <li data-ng-repeat="forthLevelResourcesInstances in thirdLevelResourcesInstances.resourceInstancesList">
+ <div class="component-container">
+ <div class="sprite-resource-icons small" ng-class="forthLevelResourcesInstances.icon">
+ <div data-ng-class="{'non-certified':!forthLevelResourcesInstances.certified}"
+ tooltips tooltip-side="top" tooltip-content="Not certified">
+ </div>
+ </div>
+ <div class="component-container-text" tooltips tooltip-content="{{forthLevelResourcesInstances.name}}"> {{forthLevelResourcesInstances.name}} </div>
+ </div>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ </ul>
+</div>
diff --git a/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.less b/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.less
new file mode 100644
index 0000000000..094c3f70ba
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.less
@@ -0,0 +1,68 @@
+.structure-tree{
+ padding: 9px 0px 10px 30px;
+ position: relative;
+ ul{
+ position: relative;
+ list-style: none;
+ padding-left:25px;
+ ::before{
+ content: "";
+ position: absolute;
+ left: -27px;
+ }
+ ::after{
+ content: "";
+ position: absolute;
+ left: -27px;
+ }
+ li{
+ position: relative;
+ &::before{
+ border-top: 1px solid #666666;
+ top: 20px;
+ width: 10px;
+ height: 0;
+ }
+ &::after{
+ border-left: 1px solid #666666;
+ height: 100%;
+ width: 0px;
+ top: -2px;
+ }
+ &:last-child::after{
+ height: 23px
+ }
+ }
+ }
+ .component-container{
+ display: inline-block;
+ margin: 6px 0px 0px -16px;
+ }
+ .component-container-icon{
+ display: inline-block;
+ }
+ .component-container-text{
+ padding-left: 8px;
+ float: right;
+
+ text-overflow: ellipsis;
+ max-width:120px;
+ display: inline-block;
+ white-space: nowrap;
+ font-size: 13px;
+ color: #666666;;
+ overflow: hidden;
+ line-height: 28px;
+ float: none;
+ }
+
+ .non-certified{
+ position: relative;
+ left: 18px;
+ bottom: 8px;
+ .sprite;
+ .s-sdc-state-non-certified;
+ display:block;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.ts b/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.ts
new file mode 100644
index 0000000000..1edce6f36e
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/structure-tree/structure-tree-directive.ts
@@ -0,0 +1,197 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+
+ export interface IStructureTreeScope extends ng.IScope {
+
+ component: Models.Components.Component;
+ structureTree: StructureTree;
+ }
+
+ class StructureTree {
+
+ serviceRoot:ResourceInstanceNode;
+
+ constructor(private uniqueId:string, private resourceInstanceName:string, private resourceInstanceIcon:string, private certified:boolean) {
+ this.serviceRoot = new ResourceInstanceNode(uniqueId, resourceInstanceName, resourceInstanceIcon, certified);
+ }
+
+ }
+
+ class ResourceInstanceNode {
+ id:string;
+ icon:string;
+ name:string;
+ resourceInstancesList:Array<ResourceInstanceNode>;
+ isAlreadyInTree:boolean;
+ certified:boolean;
+
+
+ constructor(private uniqueId:string, private resourceInstanceName:string, private resourceInstanceIcon:string, certified:boolean) {
+ this.id = uniqueId;
+ this.name = resourceInstanceName;
+ this.icon = resourceInstanceIcon;
+ this.resourceInstancesList = [];
+ this.isAlreadyInTree = false;
+ this.certified = certified;
+ }
+ }
+
+ export class StructureTreeDirective implements ng.IDirective {
+
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ component: '=',
+ };
+ restrict = 'E';
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/structure-tree/structure-tree-directive.html');
+ };
+
+ link = (scope:IStructureTreeScope, $elem:any) => {
+
+ let RESOURCE_INSTANCE_LIST:string = "resourceInstancesChildesList";
+ let resourceInstanceMap:Utils.Dictionary<string, ResourceInstanceNode>;
+ let relations:Array<Models.RelationshipModel>;
+ //************* Start Building Tree Functions *******************//
+
+ //remove unnecessary instances
+ let initResourceInstanceMap = ():void => {
+
+ resourceInstanceMap = new Utils.Dictionary<string, ResourceInstanceNode>();
+
+ _.forEach(scope.component.componentInstances, (resourceInstance:Models.ComponentsInstances.ComponentInstance)=> {
+ if (_.some(Object.keys(resourceInstance.capabilities), (key:string)=> {
+ return 'tosca.capabilities.container' == key.toLowerCase();
+ }) || _.some(Object.keys(resourceInstance.requirements),(key:string)=> {
+ return 'tosca.capabilities.container' == key.toLowerCase();
+ })) {
+
+ let isCertified = 0 === (parseFloat(resourceInstance.componentVersion) % 1);
+ let node:ResourceInstanceNode = new ResourceInstanceNode(resourceInstance.uniqueId,
+ resourceInstance.name,
+ resourceInstance.icon,
+ isCertified);
+ resourceInstanceMap.setValue(resourceInstance.uniqueId, node);
+ }
+ });
+ };
+
+ //remove unnecessary relations
+ let initRelations = ():void => {
+ relations = _.filter(scope.component.componentInstancesRelations, (relation:Models.RelationshipModel)=> {
+ return resourceInstanceMap.containsKey(relation.fromNode) && resourceInstanceMap.containsKey(relation.toNode);
+ });
+ };
+
+ let buildTree = ():void => {
+ if (scope.component) {
+ scope.structureTree = new StructureTree(scope.component.uniqueId, scope.component.name, scope.component.icon, 'CERTIFIED' === scope.component.lifecycleState);
+ initResourceInstanceMap();
+ initRelations();
+
+ let parentNodesList = _.groupBy(relations, (node:any)=> {
+ return node.fromNode;
+ });
+
+ for (let parent in parentNodesList) {
+ _.forEach(parentNodesList[parent], (childNode)=> {
+ parentNodesList[parent][RESOURCE_INSTANCE_LIST] = [];
+ parentNodesList[parent][RESOURCE_INSTANCE_LIST].push(mergeAllSubtrees(childNode, parentNodesList));
+ });
+ }
+
+ //add the resourceInstanceList for the service root node
+ for (let parent in parentNodesList) {
+ let resourceInstanceNode:ResourceInstanceNode = resourceInstanceMap.getValue(parent);
+ resourceInstanceNode.resourceInstancesList = parentNodesList[parent];
+ resourceInstanceNode.resourceInstancesList = parentNodesList[parent][RESOURCE_INSTANCE_LIST];
+ resourceInstanceNode.isAlreadyInTree = true;
+ scope.structureTree.serviceRoot.resourceInstancesList.push(resourceInstanceNode);
+ }
+
+ // Add all node that have no connection to the rootNode
+ resourceInstanceMap.forEach((key:string, value:ResourceInstanceNode) => {
+ if (!value.isAlreadyInTree) {
+ scope.structureTree.serviceRoot.resourceInstancesList.push(value);
+ }
+ });
+ }
+ };
+
+ //this recursion is merging all the subtrees
+ let mergeAllSubtrees = (connectionData:any, parentNodesList:any):ResourceInstanceNode => {
+ let resourceInstanceNode:ResourceInstanceNode = resourceInstanceMap.getValue(connectionData.toNode);
+ resourceInstanceNode.isAlreadyInTree = true;
+ if (parentNodesList[resourceInstanceNode.id]) {
+ if (parentNodesList[resourceInstanceNode.id][RESOURCE_INSTANCE_LIST]) {
+ resourceInstanceNode.resourceInstancesList = parentNodesList[resourceInstanceNode.id][RESOURCE_INSTANCE_LIST];
+ }
+ else {
+ _.forEach(parentNodesList[resourceInstanceNode.id], (children)=> {
+ resourceInstanceNode.resourceInstancesList.push(mergeAllSubtrees(children, parentNodesList));
+ });
+ }
+ delete parentNodesList[resourceInstanceNode.id];
+ }
+ return resourceInstanceNode;
+ };
+ //************* End Building Tree Functions *******************//
+
+ //************* Start Watchers *******************//
+ scope.$watch('component.name', ():void => {
+ if (scope.structureTree)
+ scope.structureTree.serviceRoot.name = scope.component.name;
+ });
+
+ scope.$watch('component.icon', ():void => {
+ if (scope.structureTree)
+ scope.structureTree.serviceRoot.icon = scope.component.icon;
+ });
+
+ scope.$watchCollection('component.componentInstancesRelations', ():void => {
+ buildTree();
+ });
+
+ scope.$watchCollection('component.componentInstances', ():void => {
+ buildTree();
+ });
+
+ //************* End Watchers *******************//
+
+ buildTree();
+
+ };
+
+
+ public static factory = ($templateCache:ng.ITemplateCacheService) => {
+ return new StructureTreeDirective($templateCache);
+ };
+ }
+
+ StructureTreeDirective.factory.$inject = ['$templateCache'];
+
+}
diff --git a/catalog-ui/app/scripts/directives/tag/tag-directive.html b/catalog-ui/app/scripts/directives/tag/tag-directive.html
new file mode 100644
index 0000000000..28c22a7978
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tag/tag-directive.html
@@ -0,0 +1,10 @@
+<div class="sdc-tag">
+ <div class="tag" data-tests-id="i-sdc-tag-text" sdc-smart-tooltip data-ng-bind="tagData.tag"></div>
+ <div class="category" data-ng-hide="hideTooltip===true">
+ <span class="relation-categoty-icon" data-tooltips data-tooltip-side="bottom" data-tooltip="'<span class='tag-tooltip-wrap'>{{tagData.tooltip}}</span>'" data-tooltip-enable="false"></span>
+ </div>
+
+ <div class="delete" data-ng-if="!hideDelete && !sdcDisable" data-ng-click="delete()" data-tests-id="i-sdc-tag-delete">
+ <span class="delete-icon"></span>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/tag/tag-directive.less b/catalog-ui/app/scripts/directives/tag/tag-directive.less
new file mode 100644
index 0000000000..f72e366ac6
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tag/tag-directive.less
@@ -0,0 +1,51 @@
+.sdc-tag{
+
+ background-color:#F2F2F2 ;
+ .border-radius(4px);
+ min-width:150px;
+ height:30px;
+ display: flex;
+ align-items: center;
+ padding: 0 10px;
+ margin: 2px;
+
+ .tag{
+ display: inline-block;
+ }
+
+ .category{
+ margin-right: 4px;
+ margin-left: 25px;
+ width: 25px;
+
+ }
+ .relation-categoty-icon{
+ .sprite;
+ .sprite.relation-icon;
+ .hand;
+ vertical-align: middle;
+
+ }
+
+ .relation-categoty-icon:hover{
+ .sprite;
+ .sprite.relation-icon-hover;
+ }
+
+ .delete{
+
+ }
+ .delete-icon{
+ .sprite;
+ .sprite.x-btn-black;
+ .hand;
+ vertical-align: middle;
+ }
+}
+
+.tag-tooltip-wrap {
+ background-color: rgba(80, 99, 113, 0.9);
+ position: relative;
+ display: inline-block;
+ margin: -5px -14px 0px -14px;
+}
diff --git a/catalog-ui/app/scripts/directives/tag/tag-directive.ts b/catalog-ui/app/scripts/directives/tag/tag-directive.ts
new file mode 100644
index 0000000000..64d245e242
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tag/tag-directive.ts
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export class TagData {
+ tag:string;
+ tooltip:string;
+ id: string;
+ }
+
+ export interface ITagScope extends ng.IScope {
+ tagData: TagData;
+ onDelete: Function;
+ delete:Function;
+ hideTooltip:boolean;
+ hideDelete:boolean;
+ sdcDisable: boolean;
+ }
+
+ export class TagDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ tagData: '=',
+ onDelete: '&',
+ hideTooltip: '=',
+ hideDelete: '=',
+ sdcDisable: '='
+ };
+
+ replace = true;
+ restrict = 'EA';
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/tag/tag-directive.html');
+ };
+
+ link = (scope:ITagScope) => {
+ scope.delete = ()=>{
+ scope.onDelete({'uniqueId':scope.tagData.id});
+ }
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new TagDirective($templateCache);
+ };
+
+ }
+
+ TagDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/tutorial/image-template.html b/catalog-ui/app/scripts/directives/tutorial/image-template.html
new file mode 100644
index 0000000000..7e7f7af356
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tutorial/image-template.html
@@ -0,0 +1,7 @@
+<perfect-scrollbar include-padding="true" class="sdc-tutorial-container-content sdc-tutorial-image-template">
+ <div class="{{pageObject.data.imageClass}}"></div>
+ <div class="sdc-tutorial-image-template-text">
+ <h1 translate="{{pageObject.data.title}}"></h1>
+ <p class="sdc-welcome-page-description2" translate="{{pageObject.data.description}}"></p>
+ </div>
+</perfect-scrollbar>
diff --git a/catalog-ui/app/scripts/directives/tutorial/text-template.html b/catalog-ui/app/scripts/directives/tutorial/text-template.html
new file mode 100644
index 0000000000..dc1173be64
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tutorial/text-template.html
@@ -0,0 +1,4 @@
+<perfect-scrollbar include-padding="true" class="sdc-tutorial-container-content sdc-tutorial-text-template">
+ <h1 translate="{{pageObject.data.title}}"></h1>
+ <p class="sdc-welcome-page-description2" translate="{{pageObject.data.description}}"></p>
+</perfect-scrollbar>
diff --git a/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.html b/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.html
new file mode 100644
index 0000000000..191752fc1f
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.html
@@ -0,0 +1,22 @@
+<div class="sdc-tutorial-page" data-ng-if="showTutorial">
+ <div class="sdc-tutorial-container-wrapper">
+ <div class="sdc-tutorial-skip" translate="{{isFirstTime?'TUTRIAL_GENERAL_SKIP_BUTTON':'TUTRIAL_GENERAL_CLOSE_BUTTON'}}" data-ng-click="closeTutorial()"></div>
+ <div class="sdc-tutorial-container">
+ <div class="sdc-tutorial-container-tabs">
+ <div class="sdc-tutorial-container-tab" data-ng-repeat="tab in tabs" data-ng-class="{'selected': tab.id===pageObject.tab}">
+ <span translate="{{tab.name}}" data-ng-click="initPage(tab.defaultPage)"></span>
+ </div>
+ </div>
+ <ng-include src="templateUrl"></ng-include>
+ </div>
+
+ <div class="sdc-tutorial-footer">
+ <div class="sdc-tutorial-footer-prev-button"><span data-ng-show="hasPrevious()" translate="TUTRIAL_GENERAL_PREVIOUS_BUTTON" data-ng-click="previous()"></span></div>
+ <div class="sdc-tutorial-footer-page-counter"><span class="selected" data-ng-bind="currentPageIndex+1"></span>/<span class="total" data-ng-bind="totalPages"></span></div>
+ <div class="sdc-tutorial-footer-next-button">
+ <span data-ng-if="hasNext()" translate="TUTRIAL_GENERAL_NEXT_BUTTON" data-ng-click="next()"></span>
+ <span data-ng-if="(currentPageIndex+1) === totalPages" translate="TUTRIAL_GENERAL_NEXT_BUTTON_END" data-ng-click="closeAndShowLastPage()"></span>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.less b/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.less
new file mode 100644
index 0000000000..410a54e9c1
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.less
@@ -0,0 +1,213 @@
+.sdc-tutorial-page {
+
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0,0,0,0.8);
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 3000;
+
+ .sdc-tutorial-container-wrapper {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .sdc-tutorial-container {
+ .bg_c;
+ width: 830px;
+ height: 466px;
+ box-shadow: 1px 2px 2px 0px rgba(0, 0, 0, 0.35);
+ }
+
+ .sdc-tutorial-container-tabs {
+ height: 56px;
+ display: flex;
+ flex-direction: row;
+ }
+
+ .sdc-tutorial-container-tab {
+ .a_6;
+ flex-grow: 1;
+ align-items: center;
+ justify-content: center;
+ display: flex;
+ height: 56px;
+ position: relative;
+ opacity: 0.8;
+
+ span {
+ .hand;
+ }
+
+ &::after {
+ content: '';
+ display: block;
+ border-right: solid 1px ;
+ border-color: rgba(59, 123, 155, 0.31);
+ height: 28px;
+ right: 0;
+ position: absolute;
+ top: 14px; //(56-28)/2
+ width: 1px;
+ }
+
+ &:last-child:after {
+ display: none;
+ }
+
+ &.selected {
+ opacity: 1;
+ .bold;
+ }
+
+ }
+
+ .sdc-tutorial-container-content {
+ .bg_a;
+ .perfect-scrollbar;
+ display: flex;
+ align-items: center;
+ height: 410px;
+ }
+
+ .sdc-tutorial-skip {
+ .c_1;
+ .hand;
+ text-align: right;
+ margin-bottom: 9px;
+ }
+
+ .sdc-tutorial-footer {
+ .c_4;
+ margin-top: 9px;
+
+ .sdc-tutorial-footer-prev-button {
+ float: left;
+ position: relative;
+ padding-left: 14px;
+ .noselect;
+
+ span {
+ .hand;
+ &::before {
+ content: '<';
+ display: block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+ }
+ }
+
+ .sdc-tutorial-footer-page-counter {
+ .e_3;
+ position: absolute;
+ left: 50%;
+ margin-top: 2px;
+ cursor: default;
+ .noselect;
+
+ .selected {
+ .c_3;
+ .bold;
+ margin-right: 2px;
+ }
+
+ .total {
+ margin-left: 2px;
+ }
+ }
+
+ .sdc-tutorial-footer-next-button {
+ float: right;
+ position: relative;
+ padding-right: 14px;
+ .noselect;
+
+ span {
+ .hand;
+
+ &::after {
+ content: '>';
+ display: block;
+ position: absolute;
+ right: 0;
+ top: 0;
+ }
+ }
+ }
+
+ }
+
+}
+
+///////////////// TEXT TEMPLATE
+.sdc-tutorial-text-template {
+
+ padding: 20px 65px;
+
+ h1 {
+ .c_15;
+ margin-top: 0;
+ }
+
+ p {
+ .c_10;
+ }
+}
+
+///////////////// IMAGE TEMPLATE
+.sdc-tutorial-image-template {
+
+ .sdc-tutorial-image-template-text {
+ padding: 16px 38px;
+ height: 118px;
+ h1 {
+ .c_11;
+ margin: 0 0 4px 0;
+ }
+
+ p {
+ .c_4;
+ font-weight: 300;
+ line-height: 21px;
+ }
+
+ }
+
+ .sdc-tutorial-page-2-image { background: transparent url('../../../styles/images/tutorial/2.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-3-image { background: transparent url('../../../styles/images/tutorial/3.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-4-image { background: transparent url('../../../styles/images/tutorial/4.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-5-image { background: transparent url('../../../styles/images/tutorial/5.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-6-image { background: transparent url('../../../styles/images/tutorial/6.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-7-image { background: transparent url('../../../styles/images/tutorial/7.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-8-image { background: transparent url('../../../styles/images/tutorial/8.png') no-repeat 0 0; width: 830px; height: 292px;}
+
+ .sdc-tutorial-page-10-image { background: transparent url('../../../styles/images/tutorial/10.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-11-image { background: transparent url('../../../styles/images/tutorial/11.png') no-repeat 0 0; width: 830px; height: 292px;}
+
+ .sdc-tutorial-page-13-image { background: transparent url('../../../styles/images/tutorial/13.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-14-image { background: transparent url('../../../styles/images/tutorial/14.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-15-image { background: transparent url('../../../styles/images/tutorial/15.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-16-image { background: transparent url('../../../styles/images/tutorial/16.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-17-image { background: transparent url('../../../styles/images/tutorial/17.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-18-image { background: transparent url('../../../styles/images/tutorial/18.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-19-image { background: transparent url('../../../styles/images/tutorial/19.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-20-image { background: transparent url('../../../styles/images/tutorial/20.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-21-image { background: transparent url('../../../styles/images/tutorial/21.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-22-image { background: transparent url('../../../styles/images/tutorial/22.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-23-image { background: transparent url('../../../styles/images/tutorial/23.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-24-image { background: transparent url('../../../styles/images/tutorial/24.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-25-image { background: transparent url('../../../styles/images/tutorial/25.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-26-image { background: transparent url('../../../styles/images/tutorial/26.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-27-image { background: transparent url('../../../styles/images/tutorial/27.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-28-image { background: transparent url('../../../styles/images/tutorial/28.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-29-image { background: transparent url('../../../styles/images/tutorial/29.png') no-repeat 0 0; width: 830px; height: 292px;}
+ .sdc-tutorial-page-30-image { background: transparent url('../../../styles/images/tutorial/30.png') no-repeat 0 0; width: 830px; height: 292px;}
+
+}
diff --git a/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.ts b/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.ts
new file mode 100644
index 0000000000..7df35cade9
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/tutorial/tutorial-directive.ts
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface ITutorialScope extends ng.IScope {
+ showTutorial:boolean;
+ isFirstTime:boolean;
+ templateUrl:string;
+ totalPages: number;
+ currentPageIndex: number;
+ page:number;
+ tabs:Array<string>;
+ tutorialData:any;
+ pageObject:any;
+
+ initPage:Function;
+ next:Function;
+ previous:Function;
+ hasNext():boolean;
+ hasPrevious():boolean;
+ closeTutorial:Function;
+ closeAndShowLastPage:Function;
+ }
+
+ export class TutorialDirective implements ng.IDirective {
+
+ constructor(
+ private $templateCache:ng.ITemplateCacheService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private $state:ng.ui.IStateService
+ ) {
+ }
+
+ scope = {
+ page: '=',
+ showTutorial: '=',
+ isFirstTime: '='
+ };
+
+ replace = false;
+ restrict = 'EA';
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/tutorial/tutorial-directive.html');
+ };
+
+ link = (scope:ITutorialScope, $elem:any) => {
+
+ let findPageIndex:Function = (pageId:number):number=> {
+ for (let i:number=0;i<scope.totalPages;i++){
+ if (scope.tutorialData.pages[i].id===pageId){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ let showCurrentPage:Function = ():void=> {
+ scope.pageObject = scope.tutorialData.pages[scope.currentPageIndex];
+ scope.templateUrl = '/app/scripts/directives/tutorial/' + scope.pageObject.template + '.html';
+ }
+
+ scope.tutorialData = this.sdcConfig.tutorial;
+
+ scope.closeTutorial = ()=> {
+ scope.showTutorial = false;
+ if(scope.isFirstTime){
+ scope.isFirstTime=false;
+ }
+ }
+
+ scope.closeAndShowLastPage = ()=> {
+ if(scope.isFirstTime){
+ this.$state.go('dashboard.tutorial-end');
+ }
+ scope.closeTutorial();
+ }
+
+ let init:Function = ():void => {
+ scope.tabs = scope.tutorialData.tabs;
+ scope.totalPages = scope.tutorialData.pages.length;
+ scope.initPage(scope.page);
+
+ }
+
+ scope.initPage = (pageId) => {
+ scope.currentPageIndex = findPageIndex(pageId);
+ showCurrentPage();
+ }
+
+ scope.next = ():void => {
+ if (scope.hasNext()){
+ scope.currentPageIndex++;
+ showCurrentPage();
+ }
+ }
+
+ scope.previous = ():void => {
+ if (scope.hasPrevious()){
+ scope.currentPageIndex--;
+ showCurrentPage();
+ }
+ }
+
+ scope.hasNext = ():boolean => {
+ return (scope.currentPageIndex+1) < scope.totalPages;
+ }
+
+ scope.hasPrevious = ():boolean => {
+ return scope.currentPageIndex>0;
+ }
+
+ angular.element(document).ready(function () {
+ init();
+ });
+
+ scope.$watch('showTutorial', (showTutorial:any):void => {
+ scope.initPage(scope.page);
+ });
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, sdcConfig:Models.IAppConfigurtaion, $state:ng.ui.IStateService)=> {
+ return new TutorialDirective($templateCache, sdcConfig, $state);
+ };
+
+ }
+
+ TutorialDirective.factory.$inject = ['$templateCache', 'sdcConfig', '$state'];
+}
diff --git a/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.html b/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.html
new file mode 100644
index 0000000000..1c99a18ab5
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.html
@@ -0,0 +1,9 @@
+<div class="w-sdc-header-user-container" data-tests-id="ffff">
+ <div class="w-sdc-header-user-icon"></div>
+ <div class="w-sdc-header-user-details">
+ <div sdc-smart-tooltip class="w-sdc-header-user-name" data-ng-bind="user.getName()"></div>
+ <div class="w-sdc-header-user-role" data-ng-bind="user.getRoleToView()"></div>
+ <div class="w-sdc-header-user-last-login" data-ng-show="user.getLastLogin()!==''">Last Login: {{user.getLastLogin() | date: 'MMM dd &nbsp; hh:mm a' : 'UTC'}}&nbsp;UTC</div>
+ </div>
+ <!--<div class="w-sdc-header-logout-icon"></div>-->
+</div>
diff --git a/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.less b/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.less
new file mode 100644
index 0000000000..a14db7c6ee
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.less
@@ -0,0 +1,62 @@
+.w-sdc-header-user-container {
+ .b_7;
+ width: 400px;
+ .flex-fixed(400px);
+ padding: 0 23px;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.w-sdc-header-user-icon {
+ background: no-repeat url('../../../styles/images/anonymous.jpg');
+ border-radius: 50%;
+ height: 47px;
+ width: 47px;
+ background-size: cover;
+ border: solid 2px @color_m;
+ .flex-fixed(47px);
+}
+
+.w-sdc-header-user-details {
+ padding: 4px 4px 4px 14px;
+ .vcenter;
+}
+
+.w-sdc-header-user-name {
+ max-width: 160px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ vertical-align: bottom;
+
+ .bold;
+ display: inline-block;
+}
+
+.w-sdc-header-user-role {
+ .bold;
+ display: inline-block;
+ margin-left: 6px;
+
+ &:before {
+ content: '';
+ margin-right: 8px;
+ border-left: 1px solid @color_m;
+ }
+}
+
+.w-sdc-header-user-last-login {
+ .font-type._3;
+ display: block;
+}
+
+.w-sdc-header-logout-icon {
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAYAAACAl21KAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjE5RjgxM0QxMDgzQTExRTVBQzA2OTg5ODlFNDVBNTZDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjE5RjgxM0QyMDgzQTExRTVBQzA2OTg5ODlFNDVBNTZDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTlGODEzQ0YwODNBMTFFNUFDMDY5ODk4OUU0NUE1NkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MTlGODEzRDAwODNBMTFFNUFDMDY5ODk4OUU0NUE1NkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6Riq1bAAAA20lEQVR42mJ0CUv+z0Aa+AvEertXzrmGLMgCpZcD8S4iDfoDxHfQBWEGnQLasICBAsDEQCUwcAa5hqeEAHEKNVykB8SzgYZVUGpQPRDPAOJ2oGHNZBsEjF1QussC4mlAXAM0rAM5+tHDwQJIVRBh7m0gLgeql6ZarLHgcP4JIBWAJ+YYgdQUIPYH4k6g+gpyop8RGj6gcGoGGYLTRQRAIxBnAHEl0JAOvF4jAC4BcSrQkDkEw4hA9K8ZGpkW5jUzYGwkkFCwrQJ68Rc2gyKhmNii9hwQoxS1AAEGAO5PP/+ClIDAAAAAAElFTkSuQmCC');
+ height: 20px;
+ width: 18px;
+ position: absolute;
+ right: 20px;
+ top: 29px;
+}
+
+
diff --git a/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.ts b/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.ts
new file mode 100644
index 0000000000..46c43a266b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/user-header-details/user-header-details-directive.ts
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface IUserHeaderDetailsScope extends ng.IScope {
+ name: string;
+ role: string;
+ iconUrl: string;
+ UserResourceClass:Services.IUserResourceClass;
+ user: Models.IUser;
+ sdcConfig:Models.IAppConfigurtaion;
+ initUser:Function;
+ }
+
+ export class UserHeaderDetailsDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService, private $http:ng.IHttpService, private sdcConfig:Models.IAppConfigurtaion, private UserResourceClass:Services.IUserResourceClass) {
+ }
+
+ scope = {
+ iconUrl: '=?'
+ };
+
+ replace = true;
+ restrict = 'E';
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/user-header-details/user-header-details-directive.html');
+ };
+
+ link = (scope:IUserHeaderDetailsScope) => {
+
+ scope.initUser = ():void => {
+ let defaultUserId:string;
+ let user:Services.IUserResource = this.UserResourceClass.getLoggedinUser();
+ if (!user) {
+ defaultUserId = this.$http.defaults.headers.common[this.sdcConfig.cookie.userIdSuffix];
+ user = this.UserResourceClass.get({id: defaultUserId}, ():void => {
+ scope.user = new Models.User(user);
+ });
+ } else {
+ scope.user = new Models.User(user);
+ }
+ };
+ scope.initUser();
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, $http:ng.IHttpService, sdcConfig:Models.IAppConfigurtaion, UserResourceClass:Services.IUserResourceClass)=> {
+ return new UserHeaderDetailsDirective($templateCache, $http, sdcConfig, UserResourceClass);
+ };
+
+ }
+
+ UserHeaderDetailsDirective.factory.$inject = ['$templateCache', '$http', 'sdcConfig', 'Sdc.Services.UserResourceService'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collaps-menu-box.ts b/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collaps-menu-box.ts
new file mode 100644
index 0000000000..9756ff9e49
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collaps-menu-box.ts
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface IExpandCollapseMenuBoxDirectiveScope extends ng.IScope {
+ menuItemsGroup: Utils.MenuItemGroup;
+ menuTitle: string;
+ parentScope: ng.IScope;
+ onMenuItemClick(menuItem: Utils.MenuItem):void;
+ }
+
+ export class ExpandCollapseMenuBoxDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ menuTitle: '@',
+ menuItemsGroup: '=',
+ parentScope: '='
+ };
+
+ public replace = false;
+ public restrict = 'AE';
+ public transclude = true;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.html');
+ };
+
+ link = (scope:IExpandCollapseMenuBoxDirectiveScope, $elem:any) => {
+ scope.onMenuItemClick = (menuItem: Utils.MenuItem):void => {
+ let onSuccess = ():void => {
+ scope.menuItemsGroup.selectedIndex = scope.menuItemsGroup.menuItems.indexOf(menuItem);
+ };
+ let onFailed = ():void => {};
+ scope.parentScope[menuItem.action](menuItem.state).then(onSuccess, onFailed);
+ }
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new ExpandCollapseMenuBoxDirective($templateCache);
+ };
+
+ }
+
+ ExpandCollapseMenuBoxDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.html b/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.html
new file mode 100644
index 0000000000..bbd7e59e7c
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.html
@@ -0,0 +1,15 @@
+<div class="expand-collapse-menu-box">
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content" class="expand-collapse-menu-box-title">
+ <div class="expand-collapse-menu-box-title-icon"></div>
+ <span class="w-sdc-designer-sidebar-section-title-text" data-ng-bind="menuTitle" tooltips tooltip-content="{{menuTitle}}"></span>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content" >
+ <div class="i-sdc-designer-sidebar-section-content-item expand-collapse-menu-box-item"
+ ng-class="{'selected': $index == menuItemsGroup.selectedIndex}" ng-repeat="(key, menuItem) in menuItemsGroup.menuItems track by $index">
+ <div class="expand-collapse-menu-box-item-text" ng-click="onMenuItemClick(menuItem)" ng-class="{'disabled': menuItem.isDisabled }" data-tests-id="{{menuItem.text}}step" >{{menuItem.text}}</div>
+ </div>
+ </div>
+
+</div>
+
diff --git a/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.less b/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.less
new file mode 100644
index 0000000000..d8ceeaea71
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.less
@@ -0,0 +1,55 @@
+.expand-collapse-menu-box {
+ line-height: 20px;
+ padding: 13px 0px 5px 10px;
+ background-color: @func_color_r;
+ margin: 3px 3px 5px 0px;
+
+
+ .expand-collapse-menu-box-title {
+ .f-type._18_m;
+ color: @main_color_m;
+ font-weight: bold;
+ .hand;
+ .w-sdc-designer-sidebar-section-title-text{
+ max-width: 185px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ }
+
+ &.expanded {
+ .expand-collapse-menu-box-title-icon {
+ transform: rotate(180deg);
+ }
+ }
+ }
+ .expand-collapse-menu-box-title-icon {
+ .hand;
+ .sprite-new;
+ .arrow-up;
+ margin-right: 6px;
+ transition: .3s all;
+ position: relative;
+
+ }
+ .w-sdc-designer-sidebar-section-content {
+ overflow: hidden;
+ padding-top: 13px;
+ .expand-collapse-menu-box-item {
+ .hand;
+ padding-left: 14px;
+ margin: 0px 0px 10px 10px;
+ font-family: @font-omnes-medium;
+ color: @main_color_m;
+
+ line-height: 18px;
+ &.selected {
+ padding-left: 10px;
+ font-weight: bold;
+ border-left: 4px solid @main_color_a;
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.html b/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.html
new file mode 100644
index 0000000000..a2358ea2b7
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.html
@@ -0,0 +1 @@
+<ng-transclude></ng-transclude>
diff --git a/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.less b/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.less
new file mode 100644
index 0000000000..d0d8fa3251
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.less
@@ -0,0 +1,10 @@
+.ellipsis-directive-more-less {
+ .a_9;
+ .bold;
+ .hand;
+ float: right;
+ margin-right: 10px;
+ line-height: 23px;
+ text-decoration: underline;
+ text-align: left;
+}
diff --git a/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.ts b/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.ts
new file mode 100644
index 0000000000..b294da6c13
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/expand-collapse/expand-collapse.ts
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface IExpandCollapseScope extends ng.IScope {
+ toggle(): void;
+ collapsed: boolean;
+ expandedSelector: string;
+ content:string;
+ isCloseOnInit:boolean;
+ loadDataFunction: Function;
+ isLoadingData: boolean;
+ }
+
+ export class ExpandCollapseDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ expandedSelector: '@',
+ loadDataFunction: '&?',
+ isCloseOnInit: '=?'
+ };
+
+ public replace = false;
+ public restrict = 'AE';
+ public transclude = true;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/expand-collapse/expand-collapse.html');
+ };
+
+ link = (scope:IExpandCollapseScope, $elem:any) => {
+ scope.collapsed = false;
+ scope.isLoadingData = false;
+ $elem.addClass('expanded');
+
+
+ if(scope.isCloseOnInit) {
+ window.setTimeout(function () {
+ toggle();
+ },0);
+ }
+
+ $elem.click(function(){
+ toggle();
+ });
+
+ let expand = ():void => {
+ $elem.addClass('expanded');
+ scope.collapsed = false;
+
+ let element = $(scope.expandedSelector)[0];
+ let prevWidth = element.style.height;
+ element.style.height = 'auto';
+ let endWidth = getComputedStyle(element).height;
+ element.style.height = prevWidth;
+ element.offsetHeight; // force repaint
+ element.style.transition = 'height .3s ease-in-out';
+ element.style.height = endWidth;
+ element.hidden = false;
+ element.addEventListener('transitionend', function transitionEnd(event) {
+ if (event['propertyName'] == 'height') {
+ element.style.transition = '';
+ element.style.height = 'auto';
+ element.removeEventListener('transitionend', transitionEnd, false);
+ }
+ }, false)
+ };
+
+ let collapse = ():void => {
+ $elem.removeClass('expanded');
+ scope.collapsed = true;
+
+ let element = $(scope.expandedSelector)[0];
+ element.style.height = getComputedStyle(element).height;
+ element.style.transition = 'height .5s ease-in-out';
+ element.offsetHeight; // force repaint
+ element.style.height = '0px';
+ element.hidden = true;
+ };
+
+ let toggle = ():void => {
+ if (scope.collapsed === true){
+ if(scope.loadDataFunction) {
+ scope.isLoadingData = true;
+ let onSuccess = () => {
+ window.setTimeout(function () {
+ expand();
+ scope.isLoadingData = false;
+ },0);
+ };
+ scope.loadDataFunction().then(onSuccess);
+ }
+ else {
+ if(scope.isLoadingData === false) {
+ expand();
+ }
+ }
+
+ } else {
+ if(scope.isLoadingData === false) {
+ collapse();
+ }
+ }
+ }
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new ExpandCollapseDirective($templateCache);
+ };
+
+ }
+
+ ExpandCollapseDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.html b/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.html
new file mode 100644
index 0000000000..4fbea447e2
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.html
@@ -0,0 +1,9 @@
+<div class="i-sdc-left-sidebar-page-nav">
+ <ul data-ng-class="{'expanded': expanded===true}">
+ <li data-ng-repeat="item in list | filter:exceptSelectedComparator"
+ data-ng-click="expanded=false"
+ class="sidebar-page-nav-item"
+ ui-sref="{{item.url}}">{{item.name}}</li>
+ </ul>
+ <div class="sidebar-page-nav-item-selected" data-ng-click="openCollapse()">{{selected}}<span data-ng-class="{'expanded': expanded===true}"></span></div>
+</div>
diff --git a/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.less b/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.less
new file mode 100644
index 0000000000..da70218263
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.less
@@ -0,0 +1,51 @@
+.i-sdc-left-sidebar-page-nav {
+
+ height: 64px;
+
+ .sidebar-page-nav-item-selected,
+ .sidebar-page-nav-item {
+ .i_11;
+ background-color: #e0e5e9;
+ width: 100%;
+ height: 64px;
+ border-bottom: solid 1px #cccccc;
+ line-height: 64px;
+ text-align: center;
+ cursor: pointer;
+ vertical-align: middle;
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+
+ .sidebar-page-nav-item-selected {
+ z-index: 1010;
+ position: absolute;
+ top: 0px;
+ }
+
+ .sidebar-page-nav-item-selected span {
+ .sprite;
+ .sprite.table-arrow;
+ position: absolute;
+ top: 28px;
+ margin-left: 10px;
+
+ &.expanded {
+ .sprite;
+ .sprite.table-arrow.opened;
+ top: 30px;
+ }
+ }
+
+ ul {
+ position: absolute;
+ top: 0px;
+ padding: 0;
+ width: 100%;
+ z-index: 99;
+ visibility: hidden; //Need this and not display none, so I can use the function: getComputedStyle
+ .box-shadow(0px 4px 2px -2px rgba(0, 0, 0, 0.36));
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.ts b/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.ts
new file mode 100644
index 0000000000..c185fe1c15
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/page-selector/page-selector.ts
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ class ListItem {
+ name;
+ url;
+ }
+
+ export interface IPageSelectorScope extends ng.IScope {
+ selected:string;
+ expanded: boolean;
+ list:Array<ListItem>;
+ exceptSelectedComparator(actual, expected):boolean;
+ openCollapse();
+ }
+
+ export class PageSelectorDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ list: '=',
+ selected: '@',
+ };
+
+ public replace = true;
+ public restrict = 'E';
+ public transclude = false;
+
+ private ulElement:HTMLElement;
+ private itemHeight:number = 64;
+
+ private getUlHeight = ():number => {
+ let tmp:string = getComputedStyle(this.ulElement).height;
+ //console.log("tmp: " + tmp);
+ let ulHeight:number = parseInt(tmp.substr(0,tmp.length-2));
+ //console.log("ulHeight: " + ulHeight);
+ return ulHeight;
+ };
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/page-selector/page-selector.html');
+ };
+
+ link = (scope:IPageSelectorScope, $elem:any) => {
+ scope.expanded=false;
+
+ window.setTimeout(() => {
+ this.ulElement = angular.element(".i-sdc-left-sidebar-page-nav ul")[0];
+ console.log("this.ulElement: " + this.ulElement);
+ console.log("this.itemHeight: " + this.itemHeight);
+ this.ulElement.style.top = (this.itemHeight - this.getUlHeight() - 5) + 'px';
+ this.ulElement.style.visibility = 'visible';
+ },10);
+
+ this.ulElement = angular.element(".i-sdc-left-sidebar-page-nav ul")[0];
+
+ scope.exceptSelectedComparator = (actual) => {
+ if (actual.name===scope.selected) {
+ return false;
+ }
+ return true;
+ };
+
+ scope.openCollapse = ():void => {
+ scope.expanded=!scope.expanded;
+ if (scope.expanded===true) {
+ this.ulElement.style.transition = 'top 0.4s ease-out';
+ this.ulElement.style.top = this.itemHeight + 'px';
+ } else {
+ this.ulElement.style.transition = 'top 0.4s ease-in';
+ this.ulElement.style.top = (this.itemHeight - this.getUlHeight() - 5) + 'px';
+ }
+ };
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new PageSelectorDirective($templateCache);
+ };
+
+ }
+
+ PageSelectorDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc-keyboard-events/sdc-keyboard-events.ts b/catalog-ui/app/scripts/directives/utils/sdc-keyboard-events/sdc-keyboard-events.ts
new file mode 100644
index 0000000000..9e61caa812
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc-keyboard-events/sdc-keyboard-events.ts
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ISdcKeyboardEventsScope extends ng.IScope {
+ keyEnter:Function;
+ keyShift:Function;
+ keyCtrl:Function;
+ keyEscape:Function;
+ keySpace:Function;
+ }
+
+ export class SdcKeyboardEventsDirective implements ng.IDirective {
+
+ constructor() {
+ }
+
+ scope = {
+ keyEnter: '=',
+ keyShift: '=',
+ keyCtrl: '=',
+ keyEscape: '=',
+ keySpace: '='
+ };
+
+ public replace = false;
+ public restrict = 'A';
+ public transclude = false;
+
+ link = (scope:ISdcKeyboardEventsScope, element:ng.IAugmentedJQuery, attrs:angular.IAttributes) => {
+
+ element.bind("keydown keypress", function (event) {
+ //console.log(event.which);
+ switch (event.which) {
+ case 13: // enter key
+ scope.$apply(function (){
+ if (scope.keyEnter) {
+ scope.keyEnter();
+ event.preventDefault();
+ }
+ });
+ break;
+ case 16: // shift key
+ scope.$apply(function (){
+ if (scope.keyShift) {
+ scope.keyShift();
+ event.preventDefault();
+ }
+ });
+ break;
+ case 17: // ctrl key
+ scope.$apply(function (){
+ if (scope.keyCtrl) {
+ scope.keyCtrl();
+ event.preventDefault();
+ }
+ });
+ break;
+ case 27: // escape key
+ scope.$apply(function (){
+ if (scope.keyEscape) {
+ scope.keyEscape();
+ event.preventDefault();
+ }
+ });
+ break;
+ case 32: // space key
+ scope.$apply(function (){
+ if (scope.keySpace) {
+ scope.keySpace();
+ event.preventDefault();
+ }
+ });
+ break;
+ }
+ });
+
+ };
+
+ public static factory = ()=> {
+ return new SdcKeyboardEventsDirective();
+ };
+
+ }
+
+ SdcKeyboardEventsDirective.factory.$inject = [];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.html b/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.html
new file mode 100644
index 0000000000..fb1ada69c3
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.html
@@ -0,0 +1,27 @@
+<div class="tags-box" >
+ <input type="text"
+ name="{{elementName}}"
+ class="new-tag-input"
+ data-ng-class="{'view-mode':sdcDisabled}"
+ data-ng-change="validateName()"
+ data-ng-model="newTag"
+ data-ng-maxlength="50"
+ data-ng-pattern="pattern"
+ data-tests-id="i-sdc-tag-input"
+ maxlength="50"
+ sdc-keyboard-events
+ key-enter="addTag"
+
+ />
+ <perfect-scrollbar class="perfect-scrollbar tags-wrapper" data-ng-class="{'view-mode':sdcDisabled}" include-padding="true">
+ <div data-tests-id="i-sdc-tags-wrapper" >
+ <div class="group-tag" data-ng-show="specialTag">
+ <sdc-tag data-hide-tooltip="true" data-hide-delete="true"
+ data-tag-data="{tag: specialTag, id: specialTag }"></sdc-tag>
+ </div>
+ <div class="group-tag" ng-repeat="tag in tags track by $index">
+ <sdc-tag ng-if="tag != specialTag" data-on-delete="deleteTag(tag)" sdc-disable="sdcDisabled" data-hide-delete="sdcDisabled" data-hide-tooltip="true" data-tag-data="{tag: tag, id: tag }"></sdc-tag>
+ </div>
+ </div>
+ </perfect-scrollbar>
+</div>
diff --git a/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.less b/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.less
new file mode 100644
index 0000000000..942196e663
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.less
@@ -0,0 +1,61 @@
+.tags-box {
+
+ height: 297px;
+ .bg_c;
+
+ .perfect-scrollbar {
+ height: 265px;
+ }
+
+ .new-tag-input {
+ display: block;
+
+ -webkit-border-bottom-left-radius: 0 !important;
+ -moz-border-radius-bottomleft: 0 !important;
+ -khtml-border-bottom-left-radius: 0 !important;
+ border-bottom-left-radius: 0 !important;
+
+ -webkit-border-bottom-right-radius: 0 !important;
+ -moz-border-radius-bottomright: 0 !important;
+ -khtml-border-bottom-right-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+
+ border: solid 1px #d8d8d8;
+ width: 100%;
+ height: 30px;
+ line-height: 30px;
+ padding: 2px 10px;
+ outline: none;
+ }
+
+ .tags-wrapper {
+ padding: 10px;
+ .border-radius-bottom-left(2px);
+ .border-radius-bottom-right(2px);
+ border: solid 1px #d8d8d8;
+ border-top: none;
+
+ .group-tag {
+ display: inline-block;
+
+ .sdc-tag {
+ border: solid 1px @main_color_n;
+ background-color: @main_color_p;
+ min-width: auto;
+ .tag {
+ margin-right: 10px;
+ }
+ }
+ }
+ &.view-mode .group-tag {
+ opacity: 1;
+ background-color: #f8f8f8 !important;
+ .sdc-tag {
+ background: none;
+ border-color: @main_color_o;
+ }
+ }
+ }
+
+}
+
diff --git a/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.ts b/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.ts
new file mode 100644
index 0000000000..3f4147c920
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc-tags/sdc-tags.ts
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ISdcTagsScope extends ng.IScope {
+ tags:Array<string>;
+ specialTag:string;
+ newTag:string;
+ formElement:ng.IFormController;
+ elementName:string;
+ pattern:any;
+ sdcDisabled:boolean;
+ maxTags:number;
+ deleteTag(tag:string):void;
+ addTag(tag:string):void;
+ validateName():void;
+ }
+
+ export class SdcTagsDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ tags: '=',
+ specialTag: '=',
+ pattern: '=',
+ sdcDisabled: '=',
+ formElement: '=',
+ elementName: '@',
+ maxTags: '@'
+ };
+
+ public replace = false;
+ public restrict = 'E';
+ public transclude = false;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/sdc-tags/sdc-tags.html');
+ };
+
+ link = (scope:ISdcTagsScope, element:ng.INgModelController) => {
+
+ scope.deleteTag = (tag:string):void => {
+ scope.tags.splice(scope.tags.indexOf(tag),1);
+ };
+
+ scope.addTag = ():void => {
+ let valid = scope.formElement[scope.elementName].$valid;
+ if (valid &&
+ scope.tags.length<scope.maxTags &&
+ scope.newTag &&
+ scope.newTag!=='' &&
+ scope.tags.indexOf(scope.newTag)===-1 &&
+ scope.newTag!==scope.specialTag) {
+ scope.tags.push(scope.newTag);
+ scope.newTag='';
+ }
+ };
+
+ scope.validateName = ():void => {
+ if (scope.tags.indexOf(scope.newTag)>-1) {
+ scope.formElement[scope.elementName].$setValidity('nameExist', false);
+ }else{
+ scope.formElement[scope.elementName].$setValidity('nameExist', true);
+ }
+ }
+
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new SdcTagsDirective($templateCache);
+ };
+
+ }
+
+ SdcTagsDirective.factory.$inject = ['$templateCache'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.html b/catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.html
new file mode 100644
index 0000000000..376381b8af
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.html
@@ -0,0 +1,6 @@
+<div class="i-sdc-form-item-error-message" style="display: none;">
+ <span class="i-sdc-form-item-error-icon-open"></span>
+ <ng-transclude>
+
+ </ng-transclude>
+</div>
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.ts b/catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.ts
new file mode 100644
index 0000000000..dc30ea7f41
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.ts
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ISdcErrorTooltipScope extends ng.IScope {
+ alignToSelector: string;
+ topMargin: string;
+ }
+
+ export class SdcErrorTooltipDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ alignToSelector: '@', // Jquery selector to align to
+ topMargin: '@' // The margin from the top, in case there is label or not the top margin is different.
+ };
+
+ public replace = false;
+ public restrict = 'E';
+ public transclude = true;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/sdc_error_tooltip/sdc_error_tooltip.html');
+ };
+
+ link = (scope:ISdcErrorTooltipScope, $elem:any) => {
+ let _self = this;
+
+ $elem.addClass("i-sdc-form-item-error-icon");
+
+ // Calculate the position of the elements after they loaded to the dom.
+ window.setTimeout(function(){
+ _self.calculatePosition(scope, $elem);
+ },100);
+
+ $elem.bind('mouseover', function(){
+ $(".i-sdc-form-item-error-message",$elem).css("display", "block");
+ });
+
+ $elem.bind('mouseleave', function(){
+ $(".i-sdc-form-item-error-message",$elem).css("display", "none");
+ });
+
+ }
+
+ private calculatePosition(scope:ISdcErrorTooltipScope, $elem:any):void {
+ let leftMargin = 13;
+ let topMargin = scope.topMargin? parseInt(scope.topMargin) : 10;
+
+ if (scope.alignToSelector) {
+ // Set the position of the error, in case user add align-to-selector attribute
+ let jObj = $(scope.alignToSelector);
+ if (jObj.length > 0) {
+ let height1 = jObj.outerHeight();
+ $elem.css('left', jObj.position().left + jObj.outerWidth() + leftMargin);
+ //$elem.css('top', jObj.position().top + topMargin + (height1 / 2));
+ $elem.css('top', jObj.position().top + (height1 / 2) - 5); // Label margin is: 2
+ }
+ } else {
+ // Set the position of the error, according to the input element.
+ let inputElm = $elem.siblings('input');
+ let textareaElm = $elem.siblings('textarea');
+ let selectElm = $elem.siblings('select');
+ if (inputElm.length > 0) {
+ $elem.css('left', inputElm.outerWidth() + leftMargin);
+ $elem.css('top', inputElm.position().top + topMargin);
+ } else if (textareaElm.length > 0) {
+ $elem.css('left', textareaElm.outerWidth() + leftMargin);
+ let height2 = textareaElm.outerHeight();
+ let elmHeight2 = $elem.outerHeight();
+ //let top = textareaElm.position().top;
+ $elem.css('bottom', (height2 - (elmHeight2 / 2)) / 2);
+ } else if (selectElm.length > 0) {
+ $elem.css('left', selectElm.outerWidth() + leftMargin);
+ $elem.css('top', selectElm.position().top + topMargin);
+ }
+ }
+ }
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new SdcErrorTooltipDirective($templateCache);
+ };
+
+ }
+
+ SdcErrorTooltipDirective.factory.$inject = ['$templateCache'];
+
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts
new file mode 100644
index 0000000000..d41ef1ce04
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-message.ts
@@ -0,0 +1,179 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface ISdcMessageScope extends ng.IScope {
+ sdcTranslate: string;
+ sdcTranslateValues:string;
+ sdcAlign:string;
+ }
+
+ export class SdcMessageDirective implements ng.IDirective {
+
+ constructor(private $animate:any, private $filter:any, private $parse:any) {
+ }
+
+ scope = {
+ field: '=',
+ required: '@',
+ pattern: '@',
+ sdcTranslate: '@',
+ sdcTranslateValues: '@',
+ sdcAlign: '@'
+ };
+
+ public terminal = true;
+ public restrict = 'A';
+ public transclude = 'element';
+ public require = '^^sdcMessages';
+
+ link = (scope:ISdcMessageScope, $element:any, $attrs:any,sdcMessagesCtrl:any, $transclude:any) => {
+ let self = this;
+
+ let commentNode = $element[0];
+
+ let records;
+ let staticExp = $attrs.sdcMessage || $attrs.when;
+ let dynamicExp = $attrs.sdcMessageExp || $attrs.whenExp;
+ let assignRecords = function(items) {
+ records = items
+ ? (angular.isArray(items)
+ ? items
+ : items.split(/[\s,]+/))
+ : null;
+ sdcMessagesCtrl.reRender();
+ };
+
+ if (dynamicExp) {
+ assignRecords(scope.$eval(dynamicExp));
+ scope.$watchCollection(dynamicExp, assignRecords);
+ } else {
+ assignRecords(staticExp);
+ }
+
+ let currentElement, messageCtrl;
+ sdcMessagesCtrl.register(commentNode, messageCtrl = {
+ test: function (name) {
+ return self.contains(records, name);
+ },
+ attach: function () {
+ if (!currentElement) {
+ $transclude(scope, function (elm) {
+
+ self.$animate.enter(elm, null, $element);
+ currentElement = elm;
+
+ elm.addClass("i-sdc-form-item-error-message");
+
+ //$compile
+ let text;
+ if (scope.sdcTranslate) {
+ text = self.$filter('translate')(scope.sdcTranslate, scope.sdcTranslateValues);
+ } else {
+ //TODO: Need to handle this
+ //let t = elm.html();
+ //let t = angular.element("<span>" + elm.html() + "</span>");
+ //text = self.$parse(t);
+ }
+
+ //scope.sdcTranslateValues
+ elm.html(text);
+
+ elm.prepend("<span class='error'></span>");
+
+ // Adding OK to close the message
+ //let okElm = $('<span />').attr('class', 'ok').html('OK');
+ //okElm.click(function(e){
+ // messageCtrl.detach();
+ //});
+ //elm.append(okElm);
+
+ // Handle the position
+ if (scope.sdcAlign){
+ let choosenElm = $(scope.sdcAlign);
+ if (choosenElm.length > 0) {
+ let height1 = choosenElm.outerHeight();
+ let elmHeight1 = elm.outerHeight();
+ elm.css('left', choosenElm.outerWidth());
+ elm.css('bottom', (height1 - (elmHeight1 / 2)) / 2);
+ }
+ } else {
+ // Set the position of the error, according to the input element.
+ let inputElm = elm.parent().siblings('input');
+ let textareaElm = elm.parent().siblings('textarea');
+ let selectElm = elm.parent().siblings('select');
+ if (inputElm.length > 0) {
+ elm.css('left', inputElm.outerWidth());
+ elm.css('top', inputElm.position().top);
+ } else if (textareaElm.length > 0) {
+ elm.css('left', textareaElm.outerWidth());
+ let height = textareaElm.outerHeight();
+ let elmHeight = elm.outerHeight();
+ //let top = textareaElm.position().top;
+ elm.css('bottom', (height - (elmHeight / 2)) / 2);
+ } else if (selectElm.length > 0) {
+ elm.css('left', selectElm.outerWidth());
+ elm.css('top', selectElm.position().top);
+ }
+ }
+
+ // Each time we attach this node to a message we get a new id that we can match
+ // when we are destroying the node later.
+ let $$attachId = currentElement.$$attachId = sdcMessagesCtrl.getAttachId();
+
+ // in the event that the parent element is destroyed
+ // by any other structural directive then it's time
+ // to deregister the message from the controller
+ currentElement.on('$destroy', function () {
+ if (currentElement && currentElement.$$attachId === $$attachId) {
+ sdcMessagesCtrl.deregister(commentNode);
+ messageCtrl.detach();
+ }
+ });
+ });
+ }
+ },
+ detach: function () {
+ if (currentElement) {
+ let elm = currentElement;
+ currentElement = null;
+ self.$animate.leave(elm);
+ }
+ }
+ });
+ }
+
+ contains = (collection, key):any => {
+ if (collection) {
+ return angular.isArray(collection)
+ ? collection.indexOf(key) >= 0
+ : collection.hasOwnProperty(key);
+ }
+ }
+
+ public static factory = ($animate:any, $filter:any, $parse:any)=> {
+ return new SdcMessageDirective($animate, $filter, $parse);
+ };
+
+ }
+
+ SdcMessageDirective.factory.$inject = ['$animate', '$filter', '$parse'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less
new file mode 100644
index 0000000000..d8dfdbb73b
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.less
@@ -0,0 +1,10 @@
+.ellipsis-directive-more-less {
+ .a_9;
+ .bold;
+ .hand;
+ float: right;
+ margin-right: 17px;
+ line-height: 23px;
+ text-decoration: underline;
+ text-align: left;
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts
new file mode 100644
index 0000000000..f8b435b1fa
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc-messages.ts
@@ -0,0 +1,245 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+ export interface ISdcMessagesScope extends ng.IScope {
+ sdcMessages: any;
+ editForm:ng.IFormController;
+ }
+
+ export class SdcMessagesDirective implements ng.IDirective {
+
+ constructor() {}
+
+ scope = {
+ sdcMessages: '='
+ };
+
+ public restrict = 'AE';
+ public require = 'sdcMessages';
+ public controller = SdcMessagesController;
+
+ /*template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/sdc-messages/sdc-messages.html');
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new SdcMessagesDirective($templateCache);
+ };*/
+
+ public static factory = ()=> {
+ return new SdcMessagesDirective();
+ }
+
+ }
+
+ export class SdcMessagesController {
+
+ messages:any;
+ getAttachId:Function;
+ render:any;
+ reRender:Function;
+ register:Function;
+ deregister:Function;
+ head:any;
+
+ static '$inject' = [
+ '$element',
+ '$scope',
+ '$attrs',
+ '$animate'
+ ];
+
+ constructor(private $element:JQuery,
+ private $scope:ISdcMessagesScope,
+ private $attrs:ng.IAttributes,
+ private $animate:any
+ ) {
+
+ this.init();
+
+ }
+
+ init=():void => {
+ let self = this;
+
+ let ACTIVE_CLASS:string = 'ng-active';
+ let INACTIVE_CLASS:string = 'ng-inactive';
+
+ let ctrl = this;
+ let latestKey = 0;
+ let nextAttachId = 0;
+
+ this.getAttachId = function getAttachId() { return nextAttachId++; };
+
+ let messages = this.messages = {};
+ let renderLater, cachedCollection;
+
+ this.render = function(collection) {
+ collection = collection || {};
+
+ renderLater = false;
+ cachedCollection = collection;
+
+ // this is true if the attribute is empty or if the attribute value is truthy
+ let multiple = self.isAttrTruthy(self.$scope, self.$attrs['sdcMessagesMultiple']) || self.isAttrTruthy(self.$scope, self.$attrs['multiple']);
+
+ let unmatchedMessages = [];
+ let matchedKeys = {};
+ let messageItem = ctrl.head;
+ let messageFound = false;
+ let totalMessages = 0;
+
+ // we use != instead of !== to allow for both undefined and null values
+ while (messageItem != null) {
+ totalMessages++;
+ let messageCtrl = messageItem.message;
+
+ let messageUsed = false;
+ if (!messageFound) {
+ _.each(collection, function(value, key) {
+ if (!messageUsed && self.truthy(value) && messageCtrl.test(key)) {
+ // this is to prevent the same error name from showing up twice
+ if (matchedKeys[key]) return;
+ matchedKeys[key] = true;
+
+ messageUsed = true;
+ messageCtrl.attach();
+ }
+ });
+ }
+
+ if (messageUsed) {
+ // unless we want to display multiple messages then we should
+ // set a flag here to avoid displaying the next message in the list
+ messageFound = !multiple;
+ } else {
+ unmatchedMessages.push(messageCtrl);
+ }
+
+ messageItem = messageItem.next;
+ }
+
+ _.each(unmatchedMessages, function(messageCtrl) {
+ messageCtrl.detach();
+ });
+
+ unmatchedMessages.length !== totalMessages
+ ? ctrl.$animate.setClass(self.$element, ACTIVE_CLASS, INACTIVE_CLASS)
+ : ctrl.$animate.setClass(self.$element, INACTIVE_CLASS, ACTIVE_CLASS);
+ };
+
+ self.$scope.$watchCollection('sdcMessages' || self.$attrs['for'], function(newVal:any, oldVal:any){
+ ctrl.render(newVal);
+ });
+
+ this.reRender = function() {
+ if (!renderLater) {
+ renderLater = true;
+ self.$scope.$evalAsync(function() {
+ if (renderLater) {
+ cachedCollection && ctrl.render(cachedCollection);
+ }
+ });
+ }
+ };
+
+ this.register = function(comment, messageCtrl) {
+ let nextKey = latestKey.toString();
+ messages[nextKey] = {
+ message: messageCtrl
+ };
+ insertMessageNode(self.$element[0], comment, nextKey);
+ comment.$$sdcMessageNode = nextKey;
+ latestKey++;
+
+ ctrl.reRender();
+ };
+
+ this.deregister = function(comment) {
+ let key = comment.$$sdcMessageNode;
+ delete comment.$$sdcMessageNode;
+ removeMessageNode(self.$element[0], comment, key);
+ delete messages[key];
+ ctrl.reRender();
+ };
+
+ function findPreviousMessage(parent, comment) {
+ let prevNode = comment;
+ let parentLookup = [];
+ while (prevNode && prevNode !== parent) {
+ let prevKey = prevNode.$$sdcMessageNode;
+ if (prevKey && prevKey.length) {
+ return messages[prevKey];
+ }
+
+ // dive deeper into the DOM and examine its children for any sdcMessage
+ // comments that may be in an element that appears deeper in the list
+ if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
+ parentLookup.push(prevNode);
+ prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
+ } else {
+ prevNode = prevNode.previousSibling || prevNode.parentNode;
+ }
+ }
+ }
+
+ function insertMessageNode(parent, comment, key) {
+ let messageNode = messages[key];
+ if (!ctrl.head) {
+ ctrl.head = messageNode;
+ } else {
+ let match = findPreviousMessage(parent, comment);
+ if (match) {
+ messageNode.next = match.next;
+ match.next = messageNode;
+ } else {
+ messageNode.next = ctrl.head;
+ ctrl.head = messageNode;
+ }
+ }
+ }
+
+ function removeMessageNode(parent, comment, key) {
+ let messageNode = messages[key];
+
+ let match = findPreviousMessage(parent, comment);
+ if (match) {
+ match.next = messageNode.next;
+ } else {
+ ctrl.head = messageNode.next;
+ }
+ }
+ }
+
+ isAttrTruthy = (scope, attr):any => {
+ return (angular.isString(attr) && attr.length === 0) || //empty attribute
+ this.truthy(scope.$eval(attr));
+ }
+
+ truthy = (val):any => {
+ return angular.isString(val) ? val.length : !!val;
+ }
+
+ }
+
+ SdcMessagesDirective.factory.$inject = ['$templateCache','$animate'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html
new file mode 100644
index 0000000000..09b1cad4d2
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/sdc_messages/sdc_messages.html
@@ -0,0 +1 @@
+<span>aaa</span>
diff --git a/catalog-ui/app/scripts/directives/utils/smart-tooltip/smart-tooltip.ts b/catalog-ui/app/scripts/directives/utils/smart-tooltip/smart-tooltip.ts
new file mode 100644
index 0000000000..49a57245e7
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/smart-tooltip/smart-tooltip.ts
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+ 'use strict';
+
+ export interface ISmartTooltipScope extends ng.IScope {
+ sdcSmartToolip;
+ }
+
+ export class SmartTooltipDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $compile:ng.ICompileService) {
+ }
+
+ public replace = false;
+ public restrict = 'A';
+ public transclude = false;
+
+ public link = (scope:ISmartTooltipScope, $elem:ng.IAugmentedJQuery, $attrs:angular.IAttributes) => {
+
+ if ($elem[0].hasAttribute('style')===false){
+ $elem[0].setAttribute("style", "overflow: hidden; white-space: nowrap; text-overflow: ellipsis;");
+ } else {
+ let styles = $elem.attr('style');
+ $elem[0].setAttribute("style", styles + ";overflow: hidden; white-space: nowrap; text-overflow: ellipsis;");
+ }
+
+ $elem.bind('mouseenter', () => {
+ if($elem[0].offsetWidth < $elem[0].scrollWidth && !$elem.attr('tooltips')){
+ $attrs.$set('tooltips', 'tooltips');
+ if ($attrs['sdcSmartTooltip'] && $attrs['sdcSmartTooltip'].length>0){
+ $elem.attr('tooltip-content', $attrs['sdcSmartTooltip']);
+ } else {
+ $attrs.$set('tooltip-content', $elem.text());
+ }
+
+ //One possible problem arises when the ngIf is placed on the root element of the template.
+ //ngIf removes the node and places a comment in it's place. Then it watches over the expression and adds/removes the actual HTML element as necessary.
+ //The problem seems to be that if it is placed on the root element of the template, then a single comment is what is left from the
+ //whole template (even if only temporarily), which gets ignored (I am not sure if this is browser-specific behaviour), resulting in an empty template.
+
+ // Remove ng-if attribute and its value (if we reach here, we pass ng-if (ng-if===true), so we can remove it).
+ $elem.removeAttr('ng-if');
+ $elem.removeAttr('data-ng-if');
+
+ // Remove me (the directive from the element)
+ let template = $elem[0].outerHTML;
+ template = template.replace('sdc-smart-tooltip=""','');
+ template = template.replace('sdc-smart-tooltip="' + $elem.text() + '"','');
+ //console.log(template);
+
+ let el = this.$compile(template)(scope);
+ console.log(el);
+ $elem.replaceWith(el);
+ }
+ });
+ };
+
+ public static factory = ($templateCache:ng.ITemplateCacheService, $compile:ng.ICompileService)=> {
+ return new SmartTooltipDirective($templateCache, $compile);
+ };
+
+ }
+
+ SmartTooltipDirective.factory.$inject = ['$templateCache', '$compile'];
+}
diff --git a/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.html b/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.html
new file mode 100644
index 0000000000..0c9b97a58c
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.html
@@ -0,0 +1,16 @@
+<ul class="sdc-wizard-step">
+ <li class="step" data-ng-repeat="step in steps track by $index">
+ <div class="step-wrapper">
+ <button class="step-index"
+ data-ng-click="controllerStepClicked(step.name)"
+ data-ng-class="{'selected': step.selected===true, 'valid': step.valid===true, 'disabled': !step.enabled || step.enabled===false}">
+ {{$index+1}}
+ </button>
+ <span class="step-name"
+ data-ng-class="{'selected': step.selected===true, 'valid': step.valid===true, 'disabled': !step.enabled || step.enabled===false}">{{step.name}}
+ </span>
+ </div>
+ <div class="step-seperator"></div>
+ </li>
+</ul>
+
diff --git a/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.less b/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.less
new file mode 100644
index 0000000000..8b777923a0
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.less
@@ -0,0 +1,69 @@
+@circle-radius: 18px;
+@gap: 70px;
+@gap-width: 2px;
+@valid-width: 2px;
+
+ul.sdc-wizard-step {
+ padding: 0;
+ margin: 0;
+
+ li.step {
+ position: relative;
+ list-style: none;
+
+ .step-wrapper {
+ line-height: @circle-radius*2;
+ height: @circle-radius*2;
+ margin-bottom: @gap;
+
+ button.step-index {
+ ._w-sdc-wizard-step-btn(@circle-radius);
+ z-index: 99;
+ display: inline-block;
+
+ &.valid {
+ display: inline-block;
+ }
+
+ }
+
+ span.step-name {
+ .b_7;
+ line-height: @circle-radius;
+ display: inline-block;
+ word-wrap: break-word;
+ width: calc(~"100%" - @circle-radius*2 + 4);
+ vertical-align: middle;
+ padding-left: 10px;
+ white-space: normal;
+
+ &.selected {
+ .a_7;
+ font-weight: bold;
+ }
+
+ &.disabled {
+ border: none;
+ background-color: transparent;
+ }
+
+ }
+ }
+
+ .step-seperator {
+ border-right: @gap-width solid @color_n;
+ height: @gap + @circle-radius*2;
+ position: absolute;
+ top: @circle-radius*2-@circle-radius;
+ left: @circle-radius - @gap-width/2;
+ }
+
+ }
+
+ li.step:last-child {
+ .step-seperator {
+ display: none;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.ts b/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.ts
new file mode 100644
index 0000000000..9cad36ab78
--- /dev/null
+++ b/catalog-ui/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.ts
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Directives {
+
+ 'use strict';
+
+ export interface IWizardStep {
+ name: string;
+ selected?: boolean;
+ valid?:boolean;
+ enabled?:boolean;
+ callback: Function;
+ }
+
+ export interface ISdcWizardStepScope extends ng.IScope {
+ steps:Array<IWizardStep>;
+ control:any;
+ internalControl:any;
+
+ stepClicked(stepName:string):void;
+ controllerStepClicked(stepName:string):void;
+
+ setStepValidity(stepName:string, valid:boolean):void;
+ controllerSetStepValidity(step:IWizardStep, valid:boolean):void;
+ }
+
+ export interface SdcWizardStepMethods {
+ unSelectAllSteps():void;
+ selectStep(step:IWizardStep):void;
+ }
+
+ export class SdcWizardStepDirective implements ng.IDirective {
+
+ constructor(private $templateCache:ng.ITemplateCacheService) {
+ }
+
+ scope = {
+ steps: '=',
+ control: '='
+ };
+
+ public replace = false;
+ public restrict = 'E';
+ public transclude = true;
+ public controller = SdcWizardStepDirectiveController;
+
+ template = ():string => {
+ return this.$templateCache.get('/app/scripts/directives/utils/wizard_steps/sdc-wizard-steps.html');
+ };
+
+ link = (scope:ISdcWizardStepScope, $elem:JQuery, attr:any, controller:SdcWizardStepDirectiveController) => {
+ scope.internalControl = scope.control || {};
+ scope.internalControl.stepClicked = (step:string):void => {
+ scope.controllerStepClicked(step);
+ };
+
+ scope.internalControl.setStepValidity = (step:IWizardStep, valid:boolean):void => {
+ scope.controllerSetStepValidity(step, valid);
+ };
+ }
+
+ public static factory = ($templateCache:ng.ITemplateCacheService)=> {
+ return new SdcWizardStepDirective($templateCache);
+ };
+
+ }
+
+ SdcWizardStepDirective.factory.$inject = ['$templateCache'];
+
+ export class SdcWizardStepDirectiveController {
+ static $inject = ['$element', '$scope'];
+
+ methods:SdcWizardStepMethods = <SdcWizardStepMethods>{};
+
+ constructor(public $element: JQuery,
+ public $scope: ISdcWizardStepScope) {
+
+ this.initMethods();
+ this.initScope();
+ }
+
+ private initScope = ():void => {
+
+ this.$scope.controllerStepClicked = (stepName:string):void => {
+ let selectedStep:IWizardStep = <IWizardStep>_.find(this.$scope.steps, function (item) {
+ return item.name === stepName;
+ });
+
+ if (selectedStep && selectedStep.enabled===true){
+ let result:boolean = selectedStep.callback();
+ if (result===true){
+ this.methods.unSelectAllSteps();
+ this.methods.selectStep(selectedStep);
+ }
+ }
+ };
+
+ this.$scope.controllerSetStepValidity = (step:IWizardStep, valid:boolean):void => {
+ step.valid=valid;
+ };
+
+ };
+
+ private initMethods = ():void => {
+
+ this.methods.unSelectAllSteps = ():void => {
+ this.$scope.steps.forEach(function (step) {
+ step.selected = false;
+ });
+ }
+
+ this.methods.selectStep = (step:IWizardStep):void => {
+ if (step.enabled===true){
+ step.selected=true;
+ }
+ }
+ };
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/_category-name-filter.ts b/catalog-ui/app/scripts/filters/_category-name-filter.ts
new file mode 100644
index 0000000000..77bbf47684
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/_category-name-filter.ts
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class CategoryNameFilter{
+
+ constructor() {
+ let filter = <CategoryNameFilter>( (name:string) => {
+ if(name){
+ let newName:string = _.last(name.split('/'));
+ if (newName){
+ return newName;
+ }
+ return name;
+ }
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/capitalize-filter.ts b/catalog-ui/app/scripts/filters/capitalize-filter.ts
new file mode 100644
index 0000000000..ef0469aaa1
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/capitalize-filter.ts
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Filters {
+
+ export class CapitalizeFilter{
+
+ constructor() {
+ let filter = <CapitalizeFilter>( (sentence:string) => {
+ if (sentence != null) {
+ let newSentence:string = "";
+ let words = sentence.split(' ');
+ for (let i=0; i < words.length; ++i){
+ let word:string = words[i].toLowerCase();
+ newSentence += word.substring(0,1).toUpperCase()+word.substring(1) + ' ';
+ }
+ return newSentence.trim();
+ }else{
+ return sentence;
+ }
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/catalog-status-filter.ts b/catalog-ui/app/scripts/filters/catalog-status-filter.ts
new file mode 100644
index 0000000000..5b382f6513
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/catalog-status-filter.ts
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+/**
+ * Created by obarda on 19/08/2015.
+ */
+module Sdc.Filters {
+
+ export class CatalogStatusFilter{
+
+ constructor() {
+ let filter = <CatalogStatusFilter>( (statuses:any) => {
+ let filtered = [];
+ angular.forEach(statuses, function(status) {
+ filtered.push(status);
+ });
+ return filtered;
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/category-icon-filter.ts b/catalog-ui/app/scripts/filters/category-icon-filter.ts
new file mode 100644
index 0000000000..6916a13399
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/category-icon-filter.ts
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class CategoryIconFilter{
+
+ constructor() {
+ let filter = <CategoryIconFilter>( (category:string) => {
+ let map = {
+ 'Application Layer 4+/Application Servers': ['applicationServer', 'server'],
+ 'Application Layer 4+/Media Servers': ['applicationServer', 'server'],
+ 'Application Layer 4+/Web Server': ['applicationServer', 'server'],
+ 'Network Layer 4+/Common Network Resources': ['network', 'loadBalancer'],
+ 'Generic/Infrastructure': ['objectStorage', 'compute'],
+ 'Generic/Network Elements': ['port', 'network', 'router'],
+ 'Application Layer 4+/Database': ['database'],
+ 'Generic/Database': ['database'],
+ 'Network Layer 2-3/Router': ['router'],
+ 'Network Layer 2-3/Gateway': ['gateway'],
+ 'Network Layer 2-3/LAN Connectors': ['connector'],
+ 'Network Layer 2-3/WAN Connectors': ['connector'],
+ 'Application Layer 4+/Border Elements': ['borderElement'],
+ 'Application Layer 4+/Load Balancer': ['loadBalancer'],
+ 'Application Layer 4+/Call Control': ['call_controll'],
+ 'VoIP Call Control': ['call_controll'],
+ 'Mobility': ['mobility'],
+ 'Network L1-3': ['network_l_1-3'],
+ 'Network L4': ['network_l_4']
+ }
+ return map[category];
+
+ });
+ return filter;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/filters/category-type-filter.ts b/catalog-ui/app/scripts/filters/category-type-filter.ts
new file mode 100644
index 0000000000..482e566e5a
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/category-type-filter.ts
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class CategoryTypeFilter {
+
+ static $inject = ['Sdc.Services.CacheService'];
+
+ constructor(cacheService:Services.CacheService) {
+ let filter = <CategoryTypeFilter> (categories:any, selectedType:Array<string>) => {
+
+ if (!selectedType.length)
+ return categories;
+
+ let filteredCategories:any = [];
+ selectedType.forEach((type:string) => {
+ filteredCategories = filteredCategories.concat(cacheService.get(type.toLowerCase() + 'Categories'));
+ });
+
+ return _.filter(categories, function (category:any) {
+ return filteredCategories.indexOf(category) != -1;
+ });
+ };
+ return filter;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/filters/clear-whitespaces-filter.ts b/catalog-ui/app/scripts/filters/clear-whitespaces-filter.ts
new file mode 100644
index 0000000000..5c946e1715
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/clear-whitespaces-filter.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class ClearWhiteSpacesFilter {
+
+ constructor() {
+ let filter = <ClearWhiteSpacesFilter>( (text:string) => {
+ if (!angular.isString(text)) {
+ return text;
+ }
+
+ return text.replace(/ /g,''); // remove also whitespaces inside
+ });
+
+ return filter;
+ }
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/filters/entity-filter.ts b/catalog-ui/app/scripts/filters/entity-filter.ts
new file mode 100644
index 0000000000..ce60d69833
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/entity-filter.ts
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class EntityFilter{
+
+ constructor() {
+
+ let filter = <EntityFilter>( (components:Array<Models.Components.Component>, filter:any) => {
+
+ let filteredComponents:Array<Models.Components.Component> = components;
+
+ // filter by type
+ // --------------------------------------------------------------------------
+ if ((filter.selectedComponentTypes && filter.selectedComponentTypes.length>0) || (filter.selectedResourceSubTypes && filter.selectedResourceSubTypes.length>0)) {
+ let filteredTypes = [];
+ angular.forEach(components, (component:Models.Components.Component):void => {
+ // Filter by component type
+ let typeLower:string = component.componentType.toLowerCase();
+ let typeFirstCapital:string = typeLower.charAt(0).toUpperCase() + typeLower.slice(1);
+ if (filter.selectedComponentTypes.indexOf(typeFirstCapital) !== -1) {
+ filteredTypes.push(component);
+ }
+
+ // Filter by resource sub type, only in case the resource checkbox was not selected (because in this case we already added all the components in above section).
+ if (component.isResource() && filter.selectedComponentTypes.indexOf("Resource") === -1 && filter.selectedResourceSubTypes.length > 0) {
+ //filteredComponents.pop(); // Remove the last inserted component.
+ let resource:Sdc.Models.Components.Resource = <Sdc.Models.Components.Resource>component;
+ if (filter.selectedResourceSubTypes.indexOf(resource.getComponentSubType()) !== -1) {
+ filteredTypes.push(component);
+ }
+ }
+ });
+ filteredComponents = filteredTypes;
+ }
+
+ // filter by categories & subcategories & groupings
+ // --------------------------------------------------------------------------
+ if (filter.selectedCategoriesModel && filter.selectedCategoriesModel.length>0) {
+ let filteredCategories = [];
+ angular.forEach(filteredComponents, (component:Models.Components.Component):void => {
+ if (component.categories && filter.selectedCategoriesModel.indexOf(component.categories[0].uniqueId) !== -1) {
+ filteredCategories.push(component);
+ } else if (component.categories && component.categories[0].subcategories && filter.selectedCategoriesModel.indexOf(component.categories[0].subcategories[0].uniqueId) !== -1) {
+ filteredCategories.push(component);
+ } else if (component.categories && component.categories[0].subcategories && component.categories[0].subcategories[0].groupings && filter.selectedCategoriesModel.indexOf(component.categories[0].subcategories[0].groupings[0].uniqueId) !== -1) {
+ filteredCategories.push(component);
+ }
+ });
+ filteredComponents = filteredCategories;
+ }
+
+ // filter by statuses
+ // --------------------------------------------------------------------------
+ if (filter.selectedStatuses && filter.selectedStatuses.length > 0) {
+ //convert array of array to string array
+ let selectedStatuses:Array<string> = [].concat.apply([],filter.selectedStatuses);
+
+ let filteredStatuses = [];
+ angular.forEach(filteredComponents, (component:Models.Components.Component):void => {
+ if (selectedStatuses.indexOf(component.lifecycleState) > -1) {
+ filteredStatuses.push(component);
+ }
+ //if status DISTRIBUTED && CERTIFIED are selected the component will added in CERTIFIED status , not need to add twice
+ if(selectedStatuses.indexOf('DISTRIBUTED') > -1 && !(selectedStatuses.indexOf('CERTIFIED') > -1)){
+ if( component.distributionStatus && component.distributionStatus.indexOf('DISTRIBUTED') > -1 && component.lifecycleState.indexOf('CERTIFIED') > -1){
+ filteredStatuses.push(component);
+ }
+ }
+ });
+ filteredComponents = filteredStatuses;
+ }
+
+ // filter by statuses and distributed
+ // --------------------------------------------------------------------------
+ if (filter.distributed != undefined && filter.distributed.length > 0) {
+ let filterDistributed: Array<any> = filter.distributed;
+ let filteredDistributed = [];
+ angular.forEach(filteredComponents, (entity) => {
+ filterDistributed.forEach((distribute) => {
+ let distributeItem = distribute.split(',');
+ distributeItem.forEach((item) => {
+ if (item !== undefined && entity.distributionStatus === item){
+ filteredDistributed.push(entity);
+ }
+ })
+ });
+ });
+ filteredComponents = filteredDistributed;
+ }
+
+ return filteredComponents;
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/graph-resource-name-filter.ts b/catalog-ui/app/scripts/filters/graph-resource-name-filter.ts
new file mode 100644
index 0000000000..63f0d780be
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/graph-resource-name-filter.ts
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class GraphResourceNameFilter {
+
+
+ constructor() {
+ let filter = <GraphResourceNameFilter>( (name:string) => {
+ let context = document.createElement("canvas").getContext("2d");
+ context.font = "13px Arial";
+
+ if(67 < context.measureText(name).width) {
+ let newLen = name.length - 3;
+ let newName = name.substring(0, newLen);
+
+ while (59 < (context.measureText(newName).width)) {
+ newName = newName.substring(0, (--newLen));
+ }
+ return newName + '...';
+ }
+
+ return name;
+ });
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/product-category-name-filter.ts b/catalog-ui/app/scripts/filters/product-category-name-filter.ts
new file mode 100644
index 0000000000..afe8c7ef08
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/product-category-name-filter.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Filters {
+
+ export class ProductCategoryNameFilter{
+
+ constructor() {
+ let filter = <CategoryNameFilter>( (name:string) => {
+ if(name){
+ let newName:string = name.split('/')[1];
+ if (newName){
+ return newName;
+ }
+ return name;
+ }
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/product-subcategory-name-filter.ts b/catalog-ui/app/scripts/filters/product-subcategory-name-filter.ts
new file mode 100644
index 0000000000..66d7a76c28
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/product-subcategory-name-filter.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Filters {
+
+ export class ProductSubCategoryNameFilter{
+
+ constructor() {
+ let filter = <CategoryNameFilter>( (name:string) => {
+ if(name){
+ let newName:string = _.last(name.split('/'));
+ if (newName){
+ return newName;
+ }
+ return name;
+ }
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/relation-name-fllter.ts b/catalog-ui/app/scripts/filters/relation-name-fllter.ts
new file mode 100644
index 0000000000..7d97eea372
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/relation-name-fllter.ts
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class RelationNameFilter{
+
+ constructor() {
+ let filter = <RelationNameFilter>( (relationshipType:string) => {
+ let icons: Array<string> = [
+ 'AttachesTo',
+ 'BindsTo',
+ 'DependsOn',
+ 'HostedOn',
+ 'LinksTo',
+ 'RoutesTo'
+ ];
+
+ let result:string = 'ConnectedTo';
+
+ if (relationshipType) {
+ let arr = relationshipType.split('.'); // looks like tosca.relationships.AttachesTo
+ relationshipType = arr[arr.length - 1];
+ if (icons.indexOf(relationshipType) > -1) {
+ result = relationshipType;
+ }
+ }
+
+ return result;
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/resource-name-filter.ts b/catalog-ui/app/scripts/filters/resource-name-filter.ts
new file mode 100644
index 0000000000..a1f6162a4c
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/resource-name-filter.ts
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class ResourceNameFilter{
+
+
+ constructor() {
+ let filter = <ResourceNameFilter>( (name:string) => {
+ if(name){
+ //let newName:string = _.last(name.split('.'));
+ let newName =
+ _.last(_.last(_.last(_.last(_.last(_.last(_.last(_.last(name.split('tosca.nodes.'))
+ .split('network.')).split('relationships.')).split('org.openecomp.')).split('resource.nfv.'))
+ .split('nodes.module.')).split('cp.')).split('vl.'));
+ if (newName){
+ return newName;
+ }
+ return name;
+ }
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/resource-type-filter.ts b/catalog-ui/app/scripts/filters/resource-type-filter.ts
new file mode 100644
index 0000000000..6aa79dae76
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/resource-type-filter.ts
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class ResourceTypeFilter{
+ static '$inject' = ['Sdc.Services.CacheService'];
+ constructor(cacheService:Services.CacheService)
+ {
+ let filter = <ResourceTypeFilter> (resourceType:string) => {
+ let uiConfiguration:any = cacheService.get('UIConfiguration');
+
+ if(uiConfiguration.resourceTypes && uiConfiguration.resourceTypes[resourceType]){
+ return uiConfiguration.resourceTypes[resourceType];
+ }
+ return resourceType;
+ }
+ return filter;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/filters/string-to-date-filter.ts b/catalog-ui/app/scripts/filters/string-to-date-filter.ts
new file mode 100644
index 0000000000..1c4919d419
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/string-to-date-filter.ts
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class StringToDateFilter{
+
+ constructor() {
+ let filter = <StringToDateFilter>( (date:string) => {
+ if(date){
+ return new Date(date.replace(" UTC", '').replace(" ", 'T') + '+00:00');
+ }
+ });
+ return filter;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/filters/tests-id-filter.ts b/catalog-ui/app/scripts/filters/tests-id-filter.ts
new file mode 100644
index 0000000000..12c5e6fd79
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/tests-id-filter.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Filters {
+
+ export class TestsIdFilter{
+
+ constructor() {
+ let filter = <TestsIdFilter>( (testId:string) => {
+ return testId.replace(/\s/g, '_').toLowerCase();
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/filters/trim-filter.ts b/catalog-ui/app/scripts/filters/trim-filter.ts
new file mode 100644
index 0000000000..fd231abc8d
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/trim-filter.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Filters {
+
+ export class TrimFilter {
+
+ constructor() {
+ let filter = <TrimFilter>( (text:string) => {
+ if (!angular.isString(text)) {
+ return text;
+ }
+
+ return text.replace(/^\s+|\s+$/g, ''); // you could use .trim, but it's not going to work in IE<9
+ });
+
+ return filter;
+ }
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/filters/truncate-filter.ts b/catalog-ui/app/scripts/filters/truncate-filter.ts
new file mode 100644
index 0000000000..1470e5937d
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/truncate-filter.ts
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Filters {
+
+ export class TruncateFilter {
+ constructor() {
+ let filter = <TruncateFilter> (str:string, length:number) => {
+ if (str.length <= length) {
+ return str;
+ }
+
+ //if(str[length - 1] === ' '){
+ // return str.substring(0, length - 1) + '...';
+ //}
+
+ let char;
+ let index = length;
+ while (char !== ' ' && index !== 0) {
+ index--;
+ char = str[index];
+ }
+ if (index === 0) {
+ return (index === 0) ? str : str.substring(0, length - 3) + '...';
+ }
+ return (index === 0) ? str : str.substring(0, index) + '...';
+ };
+ return filter;
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/filters/underscoreless-filter.ts b/catalog-ui/app/scripts/filters/underscoreless-filter.ts
new file mode 100644
index 0000000000..6849a36f04
--- /dev/null
+++ b/catalog-ui/app/scripts/filters/underscoreless-filter.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Filters {
+
+ export class UnderscoreLessFilter{
+
+ constructor() {
+ let filter = <UnderscoreLessFilter>( (sentence:string) => {
+ return sentence.replace(/_/g, ' ');
+ });
+
+ return filter;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/activity.ts b/catalog-ui/app/scripts/models/activity.ts
new file mode 100644
index 0000000000..4f8648d6b7
--- /dev/null
+++ b/catalog-ui/app/scripts/models/activity.ts
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 19/11/2015.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ /*this is in uppercase because of the server response*/
+ export class Activity{
+ TIMESTAMP: string;
+ ACTION:string;
+ MODIFIER:string;
+ STATUS:string;
+ DESC:string;
+ COMMENT:string;
+ //custom data
+ public dateFormat:string;
+
+ constructor() {
+ }
+ public toJSON = ():any => {
+ this.dateFormat = undefined;
+ return this;
+ };
+
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/additional-information.ts b/catalog-ui/app/scripts/models/additional-information.ts
new file mode 100644
index 0000000000..bcac2e5d12
--- /dev/null
+++ b/catalog-ui/app/scripts/models/additional-information.ts
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+
+ export interface IAdditionalInformationModel {
+ uniqueId: string;
+ key: string;
+ value: string;
+ }
+
+
+ export class AdditionalInformationModel implements IAdditionalInformationModel {
+ uniqueId:string;
+ key:string;
+ value:string;
+
+ constructor() {
+ this.uniqueId = '';
+ this.key = '';
+ this.value = '';
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/app-config.ts b/catalog-ui/app/scripts/models/app-config.ts
new file mode 100644
index 0000000000..f0a316fc92
--- /dev/null
+++ b/catalog-ui/app/scripts/models/app-config.ts
@@ -0,0 +1,232 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+
+ 'use strict';
+ export interface IApi {
+ baseUrl:string;
+
+ //***** NEW API *******//
+ GET_component: string;
+ PUT_component: string;
+ GET_component_validate_name: string;
+ POST_changeLifecycleState: string;
+ component_api_root:string;
+ welcome_page_video_url:string;
+ //*********//
+
+ GET_user: string;
+ GET_user_authorize: string;
+ GET_all_users: string;
+ POST_create_user;
+ DELETE_delete_user;
+ POST_edit_user_role;
+ GET_resource: string;
+ GET_resources_latestversion_notabstract:string;
+ GET_resources_certified_not_abstract: string;
+ GET_resources_certified_abstract: string;
+ PUT_resource: string;
+ GET_resource_property: string;
+ GET_resource_artifact:string;
+ GET_download_instance_artifact:string;
+ POST_instance_artifact:string;
+ GET_resource_additional_information:string;
+ GET_service_artifact:string;
+ GET_resource_interface_artifact:string;
+ GET_resource_api_artifact:string;
+ GET_resource_validate_name: string;
+ GET_resource_artifact_types: string;
+ GET_activity_log: string;
+ GET_configuration_ui: string;
+ GET_service: string;
+ PUT_product: string;
+ GET_product: string;
+ GET_ecomp_menu_items: string;
+ GET_product_validate_name: string;
+ GET_service_validate_name: string;
+ GET_service_distributions: string;
+ GET_service_distributions_components: string;
+ POST_service_distribution_deploy: string;
+ GET_element: string;
+ GET_catalog: string;
+ GET_resource_category: string;
+ GET_service_category: string;
+ resource_instance: string;
+ GET_resource_instance_property: string;
+ GET_relationship:string;
+ GET_lifecycle_state_resource:string;
+ GET_lifecycle_state_CHECKIN:string;
+ GET_lifecycle_state_CERTIFICATIONREQUEST:string;
+ GET_lifecycle_state_UNDOCHECKOUT:string;
+ root: string;
+ PUT_service: string;
+ GET_download_artifact: string;
+ GET_SDC_Version: string;
+ GET_categories: string;
+ POST_category: string;
+ POST_subcategory: string;
+ POST_change_instance_version: string;
+ GET_requirements_capabilities: string;
+ GET_onboarding: string;
+ GET_component_from_csar_uuid: string;
+ kibana:string;
+
+ //Added by Ikram -- starts
+ GET_product_category: string;
+ GET_product_category_temp: string;
+ GET_product_sub_category: string;
+ //Added by Ikram -- ends
+
+ }
+
+ export interface ILogConfig {
+ minLogLevel: string;
+ prefix: string;
+ }
+
+ export interface ICookie {
+ junctionName: string;
+ prefix: string;
+ userIdSuffix: string;
+ userFirstName: string;
+ userLastName: string;
+ userEmail: string;
+ }
+ export interface IUserTypes {
+ admin: any;
+ designer: any;
+ tester: any;
+ }
+
+ export interface IConfigStatuses {
+ inDesign: IConfigStatus;
+ readyForCertification: IConfigStatus;
+ inCertification: IConfigStatus;
+ certified: IConfigStatus;
+ distributed: IConfigStatus;
+
+ }
+
+ export interface IConfigStatus {
+ name: string;
+ values: Array<string>;
+ }
+
+ export interface IConfigRoles {
+ ADMIN: IConfigRole;
+ DESIGNER: IConfigRole;
+ TESTER: IConfigRole;
+ OPS: IConfigRole;
+ GOVERNOR: IConfigRole;
+ PRODUCT_MANAGER: IConfigRole;
+ PRODUCT_STRATEGIST: IConfigRole;
+ }
+
+ export interface IConfigRole {
+ pages: Array<string>;
+ states: IConfigState;
+ }
+
+ export interface IConfigState {
+ NOT_CERTIFIED_CHECKOUT: Array<IConfigDistribution>;
+ NOT_CERTIFIED_CHECKIN: Array<IConfigDistribution>;
+ READY_FOR_CERTIFICATION: Array<IConfigDistribution>;
+ CERTIFICATION_IN_PROGRESS: Array<IConfigDistribution>;
+ CERTIFIED: Array<IConfigDistribution>;
+ }
+
+ export interface IConfigDistribution {
+ DISTRIBUTION_NOT_APPROVED: Array<ConfigMenuItem>;
+ DISTRIBUTION_APPROVED: Array<ConfigMenuItem>;
+ DISTRIBUTED: Array<ConfigMenuItem>;
+ DISTRIBUTION_REJECTED: Array<ConfigMenuItem>;
+ }
+
+ export interface IConfirmationMessage {
+ showComment: boolean;
+ title: string;
+ message: string;
+ }
+
+ export interface IConfirmationMessages {
+ checkin: IConfirmationMessage;
+ checkout: IConfirmationMessage;
+ certify: IConfirmationMessage;
+ failCertification: IConfirmationMessage;
+ certificationRequest: IConfirmationMessage;
+ approve: IConfirmationMessage;
+ reject: IConfirmationMessage;
+ }
+
+ export interface IAlertMessage {
+ title: string;
+ message: string;
+ }
+
+ export interface IAlertMessages {
+ deleteInstance: IAlertMessage;
+ exitWithoutSaving: IConfirmationMessage;
+ }
+
+ class ConfigMenuItem {
+ text:string;
+ action:string;
+ url:string;
+ disable:boolean = false;
+ }
+
+ export interface IAppConfigurtaion {
+ environment:string;
+ api: IApi;
+ resourceTypesFilter:IResourceTypesFilter;
+ logConfig: ILogConfig;
+ cookie: ICookie;
+ imagesPath: string;
+ toscaFileExtension:string;
+ csarFileExtension:string;
+ testers: Array<ITester>
+ tutorial:any;
+ roles: Array<string>;
+ cpEndPointInstances: Array<string>;
+ openSource:boolean;
+ }
+ export interface IResourceTypesFilter {
+ resource: Array<string>;
+ }
+
+ export interface ITester {
+ email: string;
+ }
+
+ export interface IAppMenu {
+ roles: IConfigRoles;
+ confirmationMessages: IConfirmationMessages;
+ alertMessages: IAlertMessages;
+ statuses: IConfigStatuses;
+ catalogMenuItem: any;
+ categoriesDictionary:any;
+ canvas_buttons:Object;
+ component_workspace_menu_option: any;
+ LifeCycleStatuses: any;
+ DistributionStatuses: any;
+ ChangeLifecycleStateButton:any;
+ }
+}
diff --git a/catalog-ui/app/scripts/models/artifacts.ts b/catalog-ui/app/scripts/models/artifacts.ts
new file mode 100644
index 0000000000..8ee98d90d1
--- /dev/null
+++ b/catalog-ui/app/scripts/models/artifacts.ts
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ //this object contains keys, each key contain ArtifactModel
+ export class ArtifactGroupModel{
+ constructor(artifacts?:Models.ArtifactGroupModel) {
+ _.forEach(artifacts, (artifact:Models.ArtifactModel, key) => {
+ this[key] = new Models.ArtifactModel(artifact);
+ });
+ }
+
+ public filteredByType (type:string): Models.ArtifactGroupModel {
+ return JSON.parse(JSON.stringify(_.pick(this, (artifact)=>{ return artifact.artifactType == type})));
+ };
+ }
+
+ export class ArtifactModel {
+
+ artifactDisplayName:string;
+ artifactGroupType:string;
+ uniqueId:string;
+ artifactName:string;
+ artifactLabel:string;
+ artifactType:string;
+ artifactUUID:string;
+ artifactVersion:string;
+ creatorFullName:string;
+ creationDate:number;
+ lastUpdateDate:number;
+ description:string;
+ mandatory:boolean;
+ serviceApi:boolean;
+ payloadData:string;
+ timeout:number;
+ esId:string;
+ "Content-MD5":string;
+ artifactChecksum:string;
+ apiUrl:string;
+ heatParameters:Array<any>;
+ generatedFromId:string;
+
+ //custom properties
+ selected:boolean;
+ originalDescription:string;
+
+ constructor(artifact?:ArtifactModel) {
+ if(artifact) {
+ this.artifactDisplayName = artifact.artifactDisplayName;
+ this.artifactGroupType = artifact.artifactGroupType;
+ this.uniqueId = artifact.uniqueId;
+ this.artifactName = artifact.artifactName;
+ this.artifactLabel = artifact.artifactLabel;
+ this.artifactType = artifact.artifactType;
+ this.artifactUUID = artifact.artifactUUID;
+ this.artifactVersion = artifact.artifactVersion;
+ this.creatorFullName = artifact.creatorFullName;
+ this.creationDate = artifact.creationDate;
+ this.lastUpdateDate = artifact.lastUpdateDate;
+ this.description = artifact.description;
+ this.mandatory = artifact.mandatory;
+ this.serviceApi = artifact.serviceApi;
+ this.payloadData = artifact.payloadData;
+ this.timeout = artifact.timeout;
+ this.esId = artifact.esId;
+ this["Content-MD5"] = artifact["Content-MD5"];
+ this.artifactChecksum = artifact.artifactChecksum;
+ this.apiUrl = artifact.apiUrl;
+ this.heatParameters = _.sortBy(artifact.heatParameters, 'name');
+ this.generatedFromId = artifact.generatedFromId;
+ this.selected = artifact.selected ? artifact.selected : false;
+ this.originalDescription = artifact.description;
+ }
+ }
+
+ public isHEAT = ():boolean => {
+ return Utils.Constants.ArtifactType.HEAT === this.artifactType.substring(0,4);
+ };
+
+ // public isEditableInInstanceLevel = ():boolean => {
+ // return true;
+ // };
+
+ public isThirdParty = ():boolean => {
+ return _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, this.artifactType);
+ };
+
+ public toJSON = ():any => {
+ this.selected = undefined;
+ this.originalDescription = undefined;
+ return this;
+ };
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/aschema-property.ts b/catalog-ui/app/scripts/models/aschema-property.ts
new file mode 100644
index 0000000000..7ecc85c302
--- /dev/null
+++ b/catalog-ui/app/scripts/models/aschema-property.ts
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by osonsino on 16/05/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class SchemaPropertyGroupModel{
+ property: SchemaProperty;
+
+ constructor(schemaProperty?:Models.SchemaProperty) {
+ this.property = schemaProperty;
+ }
+ }
+
+ export class SchemaProperty {
+
+ type: string;
+ required: boolean;
+ definition: boolean;
+ description: string;
+ password: boolean;
+ //custom properties
+ simpleType: string;
+
+ constructor(schemaProperty?:SchemaProperty) {
+ if(schemaProperty) {
+ this.type = schemaProperty.type;
+ this.required = schemaProperty.required;
+ this.definition = schemaProperty.definition;
+ this.description = schemaProperty.description;
+ this.password = schemaProperty.password;
+ this.simpleType = schemaProperty.simpleType;
+ }
+ }
+
+ public toJSON = ():any => {
+ this.simpleType = undefined;
+ return this;
+ };
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/attributes.ts b/catalog-ui/app/scripts/models/attributes.ts
new file mode 100644
index 0000000000..ea4c7a5a23
--- /dev/null
+++ b/catalog-ui/app/scripts/models/attributes.ts
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class AttributesGroup {
+ constructor(attributesObj?:Models.AttributesGroup) {
+ _.forEach(attributesObj, (attributes:Array<Models.AttributeModel>, instance) => {
+ this[instance] = [];
+ _.forEach(attributes, (attribute:Models.AttributeModel):void => {
+ attribute.resourceInstanceUniqueId = instance;
+ attribute.readonly = true;
+ this[instance].push(new Models.AttributeModel(attribute));
+ });
+ });
+ }
+ }
+
+ export interface IAttributeModel {
+
+ //server data
+ uniqueId:string;
+ name:string;
+ defaultValue:string;
+ description:string;
+ type:string;
+ schema:Models.SchemaAttributeGroupModel;
+ status:string;
+ value:string;
+ hidden:boolean;
+ parentUniqueId:string;
+ //custom data
+ resourceInstanceUniqueId:string;
+ readonly:boolean;
+ valueUniqueUid:string;
+ }
+
+ export class AttributeModel implements IAttributeModel {
+
+ //server data
+ uniqueId:string;
+ name:string;
+ defaultValue:string;
+ description:string;
+ type:string;
+ schema:Models.SchemaAttributeGroupModel;
+ status:string;
+ value:string;
+ hidden:boolean;
+ parentUniqueId:string;
+ //custom data
+ resourceInstanceUniqueId:string;
+ readonly:boolean;
+ valueUniqueUid:string;
+
+ constructor(attribute?:Models.AttributeModel) {
+ if (attribute) {
+ this.uniqueId = attribute.uniqueId;
+ this.name = attribute.name;
+ this.defaultValue = attribute.defaultValue;
+ this.description = attribute.description;
+ this.type = attribute.type;
+ this.status = attribute.status;
+ this.schema = attribute.schema;
+ this.value = attribute.value;
+ this.hidden = attribute.hidden;
+ this.parentUniqueId = attribute.parentUniqueId;
+ this.resourceInstanceUniqueId = attribute.resourceInstanceUniqueId;
+ this.readonly = attribute.readonly;
+ this.valueUniqueUid = attribute.valueUniqueUid;
+ }
+
+ if (!this.schema || !this.schema.property) {
+ this.schema = new Models.SchemaPropertyGroupModel(new Models.SchemaProperty());
+ } else {
+ //forcing creating new object, so editing different one than the object in the table
+ this.schema = new Models.SchemaAttributeGroupModel(new Models.SchemaAttribute(this.schema.property));
+ }
+
+ this.convertValueToView();
+ }
+
+ public convertToServerObject:Function = ():string => {
+ if (this.defaultValue && this.type === 'map') {
+ this.defaultValue = '{' + this.defaultValue + '}';
+ }
+ if (this.defaultValue && this.type === 'list') {
+ this.defaultValue = '[' + this.defaultValue + ']';
+ }
+ this.defaultValue = this.defaultValue != "" && this.defaultValue != "[]" && this.defaultValue != "{}" ? this.defaultValue : null;
+
+ return JSON.stringify(this);
+ };
+
+
+ public convertValueToView() {
+ //unwrapping value {} or [] if type is complex
+ if (this.defaultValue && (this.type === 'map' || this.type === 'list') &&
+ ['[', '{'].indexOf(this.defaultValue.charAt(0)) > -1 &&
+ [']', '}'].indexOf(this.defaultValue.slice(-1)) > -1) {
+ this.defaultValue = this.defaultValue.slice(1, -1);
+ }
+
+ //also for value - for the modal in canvas
+ if (this.value && (this.type === 'map' || this.type === 'list') &&
+ ['[', '{'].indexOf(this.value.charAt(0)) > -1 &&
+ [']', '}'].indexOf(this.value.slice(-1)) > -1) {
+ this.value = this.value.slice(1, -1);
+ }
+ }
+
+ public toJSON = ():any => {
+ if (!this.resourceInstanceUniqueId) {
+ this.value = undefined;
+ }
+ this.readonly = undefined;
+ this.resourceInstanceUniqueId = undefined;
+ return this;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/models/capability.ts b/catalog-ui/app/scripts/models/capability.ts
new file mode 100644
index 0000000000..815be5a389
--- /dev/null
+++ b/catalog-ui/app/scripts/models/capability.ts
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 4/20/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ //this is an object contains keys, when each key has matching array.
+ // for example: key = tosca.capabilities.network.Linkable and the match array is array of capabilities objects
+ export class CapabilitiesGroup {
+ constructor(capabilityGroupObj?:Models.CapabilitiesGroup) {
+ _.forEach(capabilityGroupObj, (capabilitiesArrayObj:Array<Models.Capability>, instance) => {
+ this[instance] = [];
+ _.forEach(capabilitiesArrayObj, (capability:Models.Capability):void => {
+ this[instance].push(new Models.Capability(capability));
+ });
+ });
+ }
+
+ public findValueByKey(keySubstring:string):Array<Models.Capability> {
+ let key:string = _.find(Object.keys(this), (key)=> {
+ return _.includes(key.toLowerCase(), keySubstring);
+ });
+ return this[key];
+ }
+ }
+
+ export class Capability {
+
+ //server data
+ name:string;
+ ownerId:string;
+ ownerName:string;
+ type:string;
+ uniqueId:string;
+ capabilitySources:Array<String>;
+ minOccurrences:string;
+ maxOccurrences:string;
+ properties:Array<Models.PropertyModel>;
+ description:string;
+ validSourceTypes:Array<string>;
+ //custom
+ selected:boolean;
+ filterTerm:string;
+
+ constructor(capability?:Capability) {
+
+ if (capability) {
+ //server data
+ this.name = capability.name;
+ this.ownerId = capability.ownerId;
+ this.ownerName = capability.ownerName;
+ this.type = capability.type;
+ this.uniqueId = capability.uniqueId;
+ this.capabilitySources = capability.capabilitySources;
+ this.minOccurrences = capability.minOccurrences;
+ this.maxOccurrences = capability.maxOccurrences;
+ this.properties = capability.properties;
+ this.description = capability.description;
+ this.validSourceTypes = capability.validSourceTypes;
+ this.selected = capability.selected;
+ this.initFilterTerm();
+
+ }
+ }
+
+ public getFullTitle():string {
+ let maxOccurrences:string = this.maxOccurrences === 'UNBOUNDED' ? '∞' : this.maxOccurrences;
+ return this.ownerName + ': ' + this.name + ': [' + this.minOccurrences + ', ' + maxOccurrences + ']';
+ }
+
+ public toJSON = ():any => {
+ this.selected = undefined;
+ this.filterTerm = undefined;
+ return this;
+ };
+
+ private initFilterTerm = ():void =>{
+ this.filterTerm = this.name + " " +
+ (this.type ? (this.type.substring("tosca.capabilities.".length) + " " ) : "") +
+ (this.description||"") + " " +
+ (this.ownerName||"") + " " +
+ (this.validSourceTypes ? (this.validSourceTypes.join(',') + " ") : "") +
+ this.minOccurrences+","+this.maxOccurrences;
+ if(this.properties && this.properties.length){
+ _.forEach(this.properties,(prop:Models.PropertyModel)=>{
+ this.filterTerm += " "+ prop.name +
+ " " + (prop.description||"") +
+ " " + prop.type +
+ (prop.schema && prop.schema.property?(" " + prop.schema.property.type):"");
+ });
+ }
+ }
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/category.ts b/catalog-ui/app/scripts/models/category.ts
new file mode 100644
index 0000000000..730460cbc0
--- /dev/null
+++ b/catalog-ui/app/scripts/models/category.ts
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class ICategoryBase {
+
+ //server properties
+ name: string;
+ normalizedName: string;
+ uniqueId:string;
+ icons: Array<string>;
+
+ //custom properties
+ filterTerms: string;
+ isDisabled: boolean;
+ filteredGroup: Array<Models.IGroup>;
+
+ constructor(category?: ICategoryBase){
+ if (category) {
+ this.name = category.name;
+ this.normalizedName = category.normalizedName;
+ this.icons = category.icons;
+ this.filterTerms = category.filterTerms;
+ this.isDisabled = category.isDisabled;
+ this.filteredGroup = category.filteredGroup;
+ }
+ }
+ }
+
+ export class IMainCategory extends ICategoryBase {
+ subcategories:Array<ISubCategory>;
+ constructor();
+ constructor(category?: IMainCategory){
+ super(category);
+ if (category) {
+ this.subcategories = category.subcategories;
+ }
+ }
+ }
+
+ export class ISubCategory extends ICategoryBase {
+ groupings:Array<ICategoryBase>;
+ }
+
+ export interface IGroup extends ICategoryBase {
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/comments.ts b/catalog-ui/app/scripts/models/comments.ts
new file mode 100644
index 0000000000..0f7643690d
--- /dev/null
+++ b/catalog-ui/app/scripts/models/comments.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class AsdcComment{
+ public userRemarks: string;
+
+ constructor() {
+ }
+ }
+}
+
+
+
diff --git a/catalog-ui/app/scripts/models/components/component.ts b/catalog-ui/app/scripts/models/components/component.ts
new file mode 100644
index 0000000000..c0fb3a9fbb
--- /dev/null
+++ b/catalog-ui/app/scripts/models/components/component.ts
@@ -0,0 +1,828 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.Components {
+ 'use strict';
+ import Util = jasmine.Util;
+
+ export interface IComponent {
+
+ //---------------------------------------------- API CALLS ----------------------------------------------------//
+
+ //Component API
+ getComponent():ng.IPromise<Models.Components.Component>;
+ updateComponent():ng.IPromise<Models.Components.Component>;
+ createComponentOnServer():ng.IPromise<Models.Components.Component>;
+ changeLifecycleState(state:string, commentObj:Models.AsdcComment):ng.IPromise<Models.Components.Component>;
+ validateName(newName:string):ng.IPromise<Models.IValidate>;
+ updateRequirementsCapabilities():ng.IPromise<any>;
+
+ //Artifacts API
+ addOrUpdateArtifact(artifact:ArtifactModel):ng.IPromise<Models.ArtifactModel>;
+ updateMultipleArtifacts(artifacts:Array<Models.ArtifactModel>):ng.IPromise<any>;
+ deleteArtifact(artifactId:string, artifactLabel:string):ng.IPromise<Models.ArtifactModel>;
+ downloadInstanceArtifact(artifactId:string):ng.IPromise<Models.IFileDownload>;
+ downloadArtifact(artifactId:string):ng.IPromise<Models.IFileDownload>;
+
+ //Property API
+ addOrUpdateProperty(property:Models.PropertyModel):ng.IPromise<Models.PropertyModel>;
+ deleteProperty(propertyId:string):ng.IPromise<Models.PropertyModel>;
+ updateInstanceProperty(property:Models.PropertyModel):ng.IPromise<Models.PropertyModel>;
+
+ //Attribute API
+ deleteAttribute(attributeId:string):ng.IPromise<Models.AttributeModel>;
+ addOrUpdateAttribute(attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel>;
+ updateInstanceAttribute(attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel>;
+
+
+
+
+ //Component Instance API
+ createComponentInstance(componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ deleteComponentInstance(componentInstanceId:string):ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ addOrUpdateInstanceArtifact(artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel>;
+ deleteInstanceArtifact(artifactId:string, artifactLabel:string):ng.IPromise<Models.ArtifactModel>;
+ uploadInstanceEnvFile(artifact:Models.ArtifactModel): ng.IPromise<Models.ArtifactModel>;
+ changeComponentInstanceVersion(componentUid:string):ng.IPromise<Models.Components.Component>;
+ updateComponentInstance(componentInstance:Models.ComponentsInstances.ComponentInstance): ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ updateMultipleComponentInstances(instances: Array<Models.ComponentsInstances.ComponentInstance>):ng.IPromise<Array<Models.ComponentsInstances.ComponentInstance>>;
+
+ //Inputs API
+ getComponentInstanceInputProperties(componentInstanceId: string, inputId: string):ng.IPromise<Array<Models.PropertyModel>>
+ getComponentInputs(componentId: string):ng.IPromise<Array<Models.InputModel>>;
+
+ createRelation(link:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel>;
+ deleteRelation(link:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel>;
+
+
+ //Modules
+ getModuleForDisplay(moduleId:string):ng.IPromise<Models.DisplayModule>;
+ updateGroupMetadata(group:Models.Module):ng.IPromise<Models.Module>;
+ //---------------------------------------------- HELP FUNCTIONS ----------------------------------------------------//
+
+ getComponentSubType():string;
+ isAlreadyCertified():boolean;
+ isProduct():boolean;
+ isService():boolean;
+ isResource():boolean;
+ isComplex():boolean;
+ getAdditionalInformation():Array<Models.AdditionalInformationModel>;
+ getAllVersionsAsSortedArray():Array<any>;
+ getStatus(sdcMenu:Models.IAppMenu):string;
+ }
+
+
+ export class Component implements IComponent {
+
+ //server data
+ public abstract:string;
+ public uniqueId:string;
+ public uuid:string;
+ public invariantUUID:string;
+ public name:string;
+ public version:string;
+ public creationDate:number;
+ public lastUpdateDate:number;
+ public description:string;
+ public lifecycleState:string;
+ public tags:Array<string>;
+ public icon:string;
+ public contactId:string;
+ public allVersions:any;
+ public creatorUserId:string;
+ public creatorFullName:string;
+ public lastUpdaterUserId:string;
+ public lastUpdaterFullName:string;
+ public componentType:string;
+ public deploymentArtifacts:Models.ArtifactGroupModel;
+ public artifacts:Models.ArtifactGroupModel;
+ public toscaArtifacts:Models.ArtifactGroupModel;
+ public distributionStatus:string;
+ public categories:Array<Models.IMainCategory>;
+ public componentInstancesProperties:Models.PropertiesGroup;
+ public componentInstancesAttributes:Models.AttributesGroup;
+ public componentInstancesRelations:Array<Models.RelationshipModel>;
+ public componentInstances:Array<Models.ComponentsInstances.ComponentInstance>;
+ public inputs:Array<Models.InputModel>;
+ public capabilities:Models.CapabilitiesGroup;
+ public requirements:Models.RequirementsGroup;
+ public additionalInformation:any;
+ public properties:Array<Models.PropertyModel>;
+ public attributes:Array<Models.AttributeModel>;
+ public highestVersion:boolean;
+ public vendorName:string;
+ public vendorRelease:string;
+ public derivedList:Array<any>;
+ public interfaces:any;
+ public normalizedName:string;
+ public systemName:string;
+ public projectCode:string;
+ public groups:Array<Models.Module>;
+ //custom properties
+ public componentService:Sdc.Services.Components.IComponentService;
+ public filterTerm:string;
+ public iconSprite:string;
+ public selectedInstance:Models.ComponentsInstances.ComponentInstance;
+ public mainCategory:string;
+ public subCategory:string;
+ public selectedCategory:string;
+ public showMenu:boolean;
+
+
+ constructor(componentService:Sdc.Services.Components.IComponentService,
+ protected $q:ng.IQService,
+ component?:Component) {
+ if (component) {
+ this.abstract = component.abstract;
+ this.uniqueId = component.uniqueId;
+ this.uuid = component.uuid;
+ this.invariantUUID = component.invariantUUID;
+ this.additionalInformation = component.additionalInformation;
+ this.artifacts = new Sdc.Models.ArtifactGroupModel(component.artifacts);
+ this.toscaArtifacts = new Sdc.Models.ArtifactGroupModel(component.toscaArtifacts);
+ this.contactId = component.contactId;
+ this.categories = component.categories;
+ this.creatorUserId = component.creatorUserId;
+ this.creationDate = component.creationDate;
+ this.creatorFullName = component.creatorFullName;
+ this.description = component.description;
+ this.icon = component.icon;
+ this.lastUpdateDate = component.lastUpdateDate;
+ this.lastUpdaterUserId = component.lastUpdaterUserId;
+ this.lastUpdaterFullName = component.lastUpdaterFullName;
+ this.lifecycleState = component.lifecycleState;
+ this.initComponentInstanceRelations(component.componentInstancesRelations);
+ this.componentInstancesProperties = new Models.PropertiesGroup(component.componentInstancesProperties);
+ this.componentInstancesAttributes = new Models.AttributesGroup(component.componentInstancesAttributes);
+ this.name = component.name;
+ this.version = component.version;
+ this.tags = component.tags;
+ this.capabilities = new Models.CapabilitiesGroup(component.capabilities);
+ this.requirements = new Models.RequirementsGroup(component.requirements);
+ this.allVersions = component.allVersions;
+ this.deploymentArtifacts = new Sdc.Models.ArtifactGroupModel(component.deploymentArtifacts);
+ this.componentType = component.componentType;
+ this.distributionStatus = component.distributionStatus;
+ this.highestVersion = component.highestVersion;
+ this.vendorName = component.vendorName;
+ this.vendorRelease = component.vendorRelease;
+ this.derivedList = component.derivedList;
+ this.interfaces = component.interfaces;
+ this.normalizedName = component.normalizedName;
+ this.systemName = component.systemName;
+ this.projectCode = component.projectCode;
+ this.inputs = component.inputs;
+ this.componentInstances = Utils.CommonUtils.initComponentInstances(component.componentInstances);
+ this.properties = Utils.CommonUtils.initProperties(component.properties, this.uniqueId);
+ this.attributes = Utils.CommonUtils.initAttributes(component.attributes, this.uniqueId);
+ this.selectedInstance = component.selectedInstance;
+ this.iconSprite = component.iconSprite;
+ this.showMenu = true;
+ this.groups = Utils.CommonUtils.initModules(component.groups);
+ }
+
+ //custom properties
+ this.componentService = componentService;
+ }
+
+ public setUniqueId = (uniqueId:string):void => {
+ this.uniqueId = uniqueId;
+ };
+
+ public setSelectedInstance = (componentInstance:Models.ComponentsInstances.ComponentInstance):void => {
+ this.selectedInstance = componentInstance;
+ };
+
+ //------------------------------------------ Init Functions ----------------------------------------------------------------//
+
+ private initComponentInstanceRelations = (componentInstanceRelationsObj:Array<Models.RelationshipModel>):void => {
+ if (componentInstanceRelationsObj) {
+ this.componentInstancesRelations = [];
+ _.forEach(componentInstanceRelationsObj, (instanceRelation:Models.RelationshipModel):void => {
+ this.componentInstancesRelations.push(new Models.RelationshipModel(instanceRelation));
+ });
+ }
+ };
+ //----------------------------------------------------------------------------------------------------------------------//
+
+ //------------------------------------------ API Calls ----------------------------------------------------------------//
+ public changeLifecycleState = (state:string, commentObj:Models.AsdcComment):ng.IPromise<Models.Components.Component> => {
+ return this.componentService.changeLifecycleState(this, state, JSON.stringify(commentObj));
+ };
+
+ public getComponent = ():ng.IPromise<Models.Components.Component> => {
+ return this.componentService.getComponent(this.uniqueId);
+ };
+
+ public createComponentOnServer = ():ng.IPromise<Models.Components.Component> => {
+ this.handleTags();
+ return this.componentService.createComponent(this);
+ };
+
+ public updateComponent = ():ng.IPromise<Models.Components.Component> => {
+ this.handleTags();
+ return this.componentService.updateComponent(this);
+ };
+
+ public validateName = (newName:string, subtype?:string):ng.IPromise<Models.IValidate> => {
+ return this.componentService.validateName(newName, subtype);
+ };
+
+ public downloadArtifact = (artifactId:string):ng.IPromise<Models.IFileDownload> => {
+ return this.componentService.downloadArtifact(this.uniqueId, artifactId);
+ };
+
+ public addOrUpdateArtifact = (artifact:ArtifactModel):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (artifactObj:Models.ArtifactModel):void => {
+ let newArtifact = new Models.ArtifactModel(artifactObj);
+ let artifacts = this.getArtifactsByType(artifactObj.artifactGroupType);
+ artifacts[artifactObj.artifactLabel] = newArtifact;
+ deferred.resolve(newArtifact);
+ };
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.addOrUpdateArtifact(this.uniqueId, artifact).then(onSuccess, onError);
+ return deferred.promise;
+ };
+
+ public updateMultipleArtifacts = (artifacts:Array<Models.ArtifactModel>):ng.IPromise<any>=> {
+ let deferred = this.$q.defer();
+ let onSuccess = (response:any):void => {
+ deferred.resolve(response);
+ };
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+ let q = new Utils.Functions.QueueUtils(this.$q);
+
+ _.forEach(artifacts, (artifact)=> {
+ q.addBlockingUIAction(()=> this.addOrUpdateArtifact(artifact).then(onSuccess, onError));
+ });
+ return deferred.promise;
+ };
+
+
+ public deleteArtifact = (artifactId:string, artifactLabel:string):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (artifactObj:Models.ArtifactModel):void => {
+ let newArtifact = new Models.ArtifactModel(artifactObj);
+ let artifacts = this.getArtifactsByType(artifactObj.artifactGroupType);
+ if (newArtifact.mandatory || newArtifact.serviceApi) {
+ artifacts[newArtifact.artifactLabel] = newArtifact;
+ }
+ else {
+ delete artifacts[artifactLabel];
+ }
+ deferred.resolve(newArtifact);
+ };
+ this.componentService.deleteArtifact(this.uniqueId, artifactId, artifactLabel).then(onSuccess);
+ return deferred.promise;
+ };
+
+
+ public addOrUpdateProperty = (property:Models.PropertyModel):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+
+ if (!property.uniqueId) {
+ let onSuccess = (property:Models.PropertyModel):void => {
+ let newProperty = new Models.PropertyModel(property);
+ this.properties.push(newProperty);
+ deferred.resolve(newProperty);
+ };
+ this.componentService.addProperty(this.uniqueId, property).then(onSuccess, onError);
+ }
+ else {
+ let onSuccess = (newProperty:Models.PropertyModel):void => {
+ // find exist instance property in parent component for update the new value ( find bu uniqueId )
+ let existProperty:Models.PropertyModel = <Models.PropertyModel>_.find(this.properties, {uniqueId: newProperty.uniqueId});
+ let propertyIndex = this.properties.indexOf(existProperty);
+ newProperty.readonly = this.uniqueId != newProperty.parentUniqueId;
+ this.properties[propertyIndex] = newProperty;
+ deferred.resolve(newProperty);
+ };
+ this.componentService.updateProperty(this.uniqueId, property).then(onSuccess, onError);
+ }
+ return deferred.promise;
+ };
+
+ public addOrUpdateAttribute = (attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+
+ if (!attribute.uniqueId) {
+ let onSuccess = (attribute:Models.AttributeModel):void => {
+ let newAttribute = new Models.AttributeModel(attribute);
+ this.attributes.push(newAttribute);
+ deferred.resolve(newAttribute);
+ };
+ this.componentService.addAttribute(this.uniqueId, attribute).then(onSuccess, onError);
+ }
+ else {
+ let onSuccess = (newAttribute:Models.AttributeModel):void => {
+ let existAttribute:Models.AttributeModel = <Models.AttributeModel>_.find(this.attributes, {uniqueId: newAttribute.uniqueId});
+ let attributeIndex = this.attributes.indexOf(existAttribute);
+ newAttribute.readonly = this.uniqueId != newAttribute.parentUniqueId;
+ this.attributes[attributeIndex] = newAttribute;
+ deferred.resolve(newAttribute);
+ };
+ this.componentService.updateAttribute(this.uniqueId, attribute).then(onSuccess, onError);
+ }
+ return deferred.promise;
+ };
+
+ public deleteProperty = (propertyId:string):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = ():void => {
+ console.log("Property deleted");
+ delete _.remove(this.properties, {uniqueId: propertyId})[0];
+ deferred.resolve();
+ };
+ let onFailed = ():void => {
+ console.log("Failed to delete property");
+ deferred.reject();
+ };
+ this.componentService.deleteProperty(this.uniqueId, propertyId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public deleteAttribute = (attributeId:string):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = ():void => {
+ console.log("Attribute deleted");
+ delete _.remove(this.attributes, {uniqueId: attributeId})[0];
+ };
+ let onFailed = ():void => {
+ console.log("Failed to delete attribute");
+ };
+ this.componentService.deleteAttribute(this.uniqueId, attributeId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public updateInstanceProperty = (property:Models.PropertyModel):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (newProperty:Models.PropertyModel):void => {
+ // find exist instance property in parent component for update the new value ( find bu uniqueId & path)
+ let existProperty:Models.PropertyModel = <Models.PropertyModel>_.find(this.componentInstancesProperties[newProperty.resourceInstanceUniqueId], {uniqueId: newProperty.uniqueId,path: newProperty.path});
+ let index = this.componentInstancesProperties[newProperty.resourceInstanceUniqueId].indexOf(existProperty);
+ this.componentInstancesProperties[newProperty.resourceInstanceUniqueId][index] = newProperty;
+ deferred.resolve(newProperty);
+ };
+ let onFailed = (error:any):void => {
+ console.log('Failed to update property value');
+ deferred.reject(error);
+ };
+ this.componentService.updateInstanceProperty(this.uniqueId, property).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public updateInstanceAttribute = (attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (newAttribute:Models.AttributeModel):void => {
+ let existAttribute:Models.AttributeModel = <Models.AttributeModel>_.find(this.componentInstancesAttributes[newAttribute.resourceInstanceUniqueId], {uniqueId: newAttribute.uniqueId});
+ let index = this.componentInstancesAttributes[newAttribute.resourceInstanceUniqueId].indexOf(existAttribute);
+ this.componentInstancesAttributes[newAttribute.resourceInstanceUniqueId][index] = newAttribute;
+ deferred.resolve(newAttribute);
+ };
+ let onFailed = (error:any):void => {
+ console.log('Failed to update attribute value');
+ deferred.reject(error);
+ };
+ this.componentService.updateInstanceAttribute(this.uniqueId, attribute).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public downloadInstanceArtifact = (artifactId:string):ng.IPromise<Models.IFileDownload> => {
+ return this.componentService.downloadInstanceArtifact(this.uniqueId, this.selectedInstance.uniqueId, artifactId);
+ };
+
+ public deleteInstanceArtifact = (artifactId:string, artifactLabel:string):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (artifactObj:Models.ArtifactModel):void => {
+ let newArtifact = new Models.ArtifactModel(artifactObj);
+ let artifacts = this.selectedInstance.deploymentArtifacts;
+ if (newArtifact.mandatory || newArtifact.serviceApi) {//?????????
+ artifacts[newArtifact.artifactLabel] = newArtifact;
+ }
+ else {
+ delete artifacts[artifactLabel];
+ }
+ deferred.resolve(newArtifact);
+ };
+ this.componentService.deleteInstanceArtifact(this.uniqueId,this.selectedInstance.uniqueId, artifactId, artifactLabel).then(onSuccess);
+ return deferred.promise;
+ };
+
+ public addOrUpdateInstanceArtifact = (artifact:ArtifactModel):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (artifactObj:Models.ArtifactModel):void => {
+ this.selectedInstance.deploymentArtifacts[artifactObj.artifactLabel] = artifactObj;
+ deferred.resolve(artifactObj);
+ };
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+ if(artifact.uniqueId){
+ this.componentService.updateInstanceArtifact(this.uniqueId, this.selectedInstance.uniqueId, artifact).then(onSuccess, onError);
+ }else{
+ this.componentService.addInstanceArtifact(this.uniqueId, this.selectedInstance.uniqueId, artifact).then(onSuccess, onError);
+ }
+ return deferred.promise;
+ };
+
+ public uploadInstanceEnvFile = (artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (artifactObj:Models.ArtifactModel):void => {
+ this.selectedInstance.deploymentArtifacts[artifactObj.artifactLabel] = artifactObj;
+ deferred.resolve(artifactObj);
+ };
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.uploadInstanceEnvFile(this.uniqueId, this.selectedInstance.uniqueId, artifact).then(onSuccess, onError);
+ return deferred.promise;
+ };
+
+ //this function will update the instance version than the function call getComponent to update the current component and return the new instance version
+ public changeComponentInstanceVersion = (componentUid:string):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ let onSuccess = (componentInstance:Models.ComponentsInstances.ComponentInstance):void => {
+ let onSuccess = (component:Models.Components.Component):void => {
+ component.setSelectedInstance(componentInstance);
+ deferred.resolve(component);
+ };
+ this.getComponent().then(onSuccess, onFailed);
+ };
+ this.componentService.changeResourceInstanceVersion(this.uniqueId, this.selectedInstance.uniqueId, componentUid).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public createComponentInstance = (componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (instance:Models.ComponentsInstances.ComponentInstance):void => {
+ let onSuccess = (component:Models.Components.Component):void => {
+ this.componentInstances = Utils.CommonUtils.initComponentInstances(component.componentInstances);
+ this.componentInstancesProperties = new Models.PropertiesGroup(component.componentInstancesProperties);
+ this.componentInstancesAttributes = new Models.AttributesGroup(component.componentInstancesAttributes);
+ deferred.resolve(instance);
+ };
+ this.getComponent().then(onSuccess);
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.createComponentInstance(this.uniqueId, componentInstance).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public updateComponentInstance = (componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (updatedInstance:Models.ComponentsInstances.ComponentInstance):void => {
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = _.find(this.componentInstances, (instance:Models.ComponentsInstances.ComponentInstance) => {
+ return instance.uniqueId === updatedInstance.uniqueId;
+ });
+
+ let index = this.componentInstances.indexOf(componentInstance);
+ this.componentInstances[index] = componentInstance;
+ deferred.resolve(updatedInstance);
+
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.updateComponentInstance(this.uniqueId, componentInstance).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public updateMultipleComponentInstances = (instances: Array<Models.ComponentsInstances.ComponentInstance>):ng.IPromise<Array<Models.ComponentsInstances.ComponentInstance>> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (updatedInstances:Array<Models.ComponentsInstances.ComponentInstance>):void => {
+ deferred.resolve(updatedInstances);
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.updateMultipleComponentInstances(this.uniqueId, instances).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public deleteComponentInstance = (componentInstanceId:string):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ let onSuccess = ():void => {
+ let onSuccess = (component:Models.Components.Component):void => {
+ this.componentInstances = Utils.CommonUtils.initComponentInstances(component.componentInstances);
+ this.componentInstancesProperties = new Models.PropertiesGroup(component.componentInstancesProperties);
+ this.componentInstancesAttributes = new Models.AttributesGroup(component.componentInstancesAttributes);
+ this.initComponentInstanceRelations(component.componentInstancesRelations);
+ deferred.resolve();
+ };
+ this.getComponent().then(onSuccess);
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.deleteComponentInstance(this.uniqueId, componentInstanceId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+
+ public createRelation = (relation:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (relation:Models.RelationshipModel):void => {
+ console.info('Link created successfully', relation);
+ if (!this.componentInstancesRelations) {
+ this.componentInstancesRelations = [];
+ }
+ this.componentInstancesRelations.push(new Models.RelationshipModel(relation));
+ deferred.resolve(relation);
+ };
+ let onFailed = (error:any):void => {
+ console.info('Failed to create relation', error);
+ deferred.reject(error);
+ };
+ this.componentService.createRelation(this.uniqueId, relation).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public deleteRelation = (relation:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (responseRelation:Models.RelationshipModel):void => {
+ console.log("Link Deleted In Server");
+ let relationToDelete = _.find(this.componentInstancesRelations, (item) => {
+ return item.fromNode === relation.fromNode && item.toNode === relation.toNode && _.some(item.relationships, (relationship)=> {
+ return angular.equals(relation.relationships[0], relationship);
+ });
+ });
+ let index = this.componentInstancesRelations.indexOf(relationToDelete);
+ if (relationToDelete != undefined && index > -1) {
+ if (relationToDelete.relationships.length == 1) {
+ this.componentInstancesRelations.splice(index, 1);
+ } else {
+ this.componentInstancesRelations[index].relationships =
+ _.reject(this.componentInstancesRelations[index].relationships, relation.relationships[0]);
+ }
+ } else {
+ console.error("Error while deleting relation - the return delete relation from server was not found in UI")
+ }
+ deferred.resolve(relation);
+ };
+ let onFailed = (error:any):void => {
+ console.error("Failed To Delete Link");
+ deferred.reject(error);
+ };
+ this.componentService.deleteRelation(this.uniqueId, relation).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public updateRequirementsCapabilities = ():ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (response:any):void => {
+ this.capabilities = response.capabilities;
+ this.requirements = response.requirements;
+ deferred.resolve(response);
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.getRequirementsCapabilities(this.uniqueId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public getModuleForDisplay = (moduleId:string):ng.IPromise<Models.DisplayModule> => {
+
+ let deferred = this.$q.defer();
+ let onSuccess = (response:Models.DisplayModule):void => {
+ deferred.resolve(response);
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+ this.componentService.getModuleForDisplay(this.uniqueId, moduleId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ // this function get all instances filtered by inputs and properties (optional) - if no search string insert - this function will
+ // get all the instances of the component (in service only VF instances)
+ public getComponentInstancesFilteredByInputsAndProperties = (searchText?:string):ng.IPromise<Array<Models.ComponentsInstances.ComponentInstance>> => {
+
+ let deferred = this.$q.defer();
+ let onSuccess = (response: Array<Models.ComponentsInstances.ComponentInstance>):void => {
+ deferred.resolve(response);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.getComponentInstancesFilteredByInputsAndProperties(this.uniqueId, searchText).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+
+ // get inputs for instance - Pagination function
+ public getComponentInputs = ():ng.IPromise<Array<Models.InputModel>> => {
+
+ let deferred = this.$q.defer();
+ let onSuccess = (inputsRes: Array<Models.InputModel>):void => {
+ this.inputs = inputsRes;
+ deferred.resolve(inputsRes);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.getComponentInputs(this.uniqueId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+
+ // get inputs instance - Pagination function
+ public getComponentInstanceInputs = (componentInstanceId: string, originComponentUid: string):ng.IPromise<Array<Models.InputModel>> => {
+
+ let deferred = this.$q.defer();
+ let onSuccess = (response: Array<Models.InputModel>):void => {
+ deferred.resolve(response);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.getComponentInstanceInputs(this.uniqueId, componentInstanceId, originComponentUid).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ // get inputs inatnce - Pagination function
+ public getComponentInstanceInputProperties = (componentInstanceId: string, inputId: string):ng.IPromise<Array<Models.PropertyModel>> => {
+
+ let deferred = this.$q.defer();
+ let onSuccess = (response: Array<Models.PropertyModel>):void => {
+ deferred.resolve(response);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.getComponentInstanceInputProperties(this.uniqueId, componentInstanceId, inputId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public updateGroupMetadata = (module: Models.Module):ng.IPromise<Models.Module> => {
+
+ let deferred = this.$q.defer();
+
+ let onSuccess = (updatedModule:Models.Module):void => {
+ let groupIndex: number = _.indexOf(this.groups, _.find(this.groups, (module: Models.Module) => {
+ return module.uniqueId === updatedModule.uniqueId;
+ }));
+
+ if(groupIndex !== -1) {
+ this.groups[groupIndex] = updatedModule;
+ }
+ deferred.resolve(updatedModule);
+ };
+ let onFailed = (error:any):void => {
+ deferred.reject(error);
+ };
+
+ this.componentService.updateGroupMetadata(this.uniqueId, module).then(onSuccess, onFailed);
+
+ return deferred.promise;
+ };
+
+ //------------------------------------------ Help Functions ----------------------------------------------------------------//
+
+ public isProduct = ():boolean => {
+ return this instanceof Product;
+ };
+
+ public isService = ():boolean => {
+ return this instanceof Service;
+ };
+
+ public isResource = ():boolean => {
+ return this instanceof Resource;
+ };
+
+ public getComponentSubType = ():string => {
+ return this.componentType;
+ };
+
+ public isAlreadyCertified = ():boolean => {
+ return parseInt(this.version) >= 1;
+ };
+
+ public isComplex = ():boolean => {
+ return true;
+ };
+
+ //sort string version value from hash to sorted version (i.e 1.9 before 1.11)
+ private sortVersions = (v1:string, v2:string):number => {
+ let ver1 = v1.split('.');
+ let ver2 = v2.split('.');
+ let diff = parseInt(_.first(ver1)) - parseInt(_.first(ver2));
+ if (!diff){
+ return parseInt(_.last(ver1)) - parseInt(_.last(ver2));
+ }
+ return diff;
+ };
+
+ public getAllVersionsAsSortedArray = ():Array<any> => {
+ let res = [];
+ if(this.allVersions){
+ let keys = Object.keys(this.allVersions).sort(this.sortVersions);
+ _.forEach(keys, (key)=> {
+ res.push({
+ versionNumber: key,
+ versionId: this.allVersions[key]
+ })
+ });
+ }
+ return res;
+ };
+
+ public isLatestVersion = ():boolean => {
+ if (this.allVersions){
+ return this.version === _.last(Object.keys(this.allVersions).sort(this.sortVersions));
+ }else{
+ return true;
+ }
+
+ };
+
+ public getAdditionalInformation = ():Array<Models.AdditionalInformationModel> => {
+ let additionalInformationObject:any = _.find(this.additionalInformation, (obj:any):boolean => {
+ return obj.parentUniqueId == this.uniqueId;
+ });
+ if (additionalInformationObject) {
+ return additionalInformationObject.parameters;
+ }
+ return [];
+ };
+
+ public handleTags = ():void => {
+ let isContainTag = _.find(this.tags, (tag)=> {
+ return tag === this.name;
+ });
+ if (!isContainTag) {
+ this.tags.push(this.name);
+ }
+ };
+
+ public getArtifactsByType = (artifactGroupType:string):Models.ArtifactGroupModel => {
+ switch (artifactGroupType) {
+ case Utils.Constants.ArtifactGroupType.DEPLOYMENT:
+ return this.deploymentArtifacts;
+ case Utils.Constants.ArtifactGroupType.INFORMATION:
+ return this.artifacts;
+ }
+ };
+
+ public getStatus =(sdcMenu:Models.IAppMenu):string =>{
+ let status:string = sdcMenu.LifeCycleStatuses[this.lifecycleState].text;
+ if(this.lifecycleState == "CERTIFIED" && sdcMenu.DistributionStatuses[this.distributionStatus]) {
+ status = sdcMenu.DistributionStatuses[this.distributionStatus].text;
+ }
+ return status;
+ };
+
+ public toJSON = ():any => {
+ this.componentService = undefined;
+ this.filterTerm = undefined;
+ this.iconSprite = undefined;
+ this.mainCategory = undefined;
+ this.subCategory = undefined;
+ this.selectedInstance = undefined;
+ this.showMenu = undefined;
+ this.$q = undefined;
+ this.selectedCategory = undefined;
+ return this;
+ };
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/components/displayComponent.ts b/catalog-ui/app/scripts/models/components/displayComponent.ts
new file mode 100644
index 0000000000..578f392470
--- /dev/null
+++ b/catalog-ui/app/scripts/models/components/displayComponent.ts
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 7/5/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class DisplayComponent {
+
+ uniqueId:string;
+ displayName:string;
+ version:string;
+ mainCategory:string;
+ subCategory:string;
+ iconClass:string;
+ componentSubType:string;
+ searchFilterTerms:string;
+ certifiedIconClass:string;
+ icon:string;
+ isRequirmentAndCapabilitiesLoaded:boolean;
+
+ constructor(public component:Models.Components.Component) {
+ this.icon = component.icon;
+ this.version = component.version;
+ this.uniqueId = component.uniqueId;
+ this.isRequirmentAndCapabilitiesLoaded = false;
+
+ if (component.categories && component.categories[0] && component.categories[0].subcategories && component.categories[0].subcategories[0]) {
+ this.mainCategory = component.categories[0].name;
+ this.subCategory = component.categories[0].subcategories[0].name;
+ } else {
+ this.mainCategory = 'Generic';
+ this.subCategory = 'Generic';
+ }
+ if (component instanceof Models.Components.Resource) {
+ this.componentSubType = (<Models.Components.Resource>component).resourceType;
+ } else {
+ this.componentSubType = component.componentType;
+ }
+
+ this.initDisplayName(component.name);
+ this.searchFilterTerms = (this.displayName + ' ' + component.description + ' ' + component.tags.join(' ')).toLowerCase() + ' ' + component.version;
+ this.initIconSprite(component.icon);
+ this.certifiedIconClass = component.lifecycleState != 'CERTIFIED' ? 'non-certified' : '';
+ if(component.icon === 'vl' || component.icon === 'cp') {
+ this.certifiedIconClass = this.certifiedIconClass + " " + 'smaller-icon';
+ }
+ }
+
+ public initDisplayName = (name:string):void => {
+ let newName =
+ _.last(_.last(_.last(_.last(_.last(_.last(_.last(_.last(name.split('tosca.nodes.'))
+ .split('network.')).split('relationships.')).split('org.openecomp.')).split('resource.nfv.'))
+ .split('nodes.module.')).split('cp.')).split('vl.'));
+ if (newName){
+ this.displayName = newName;
+ } else {
+ this.displayName = name;
+ }
+ };
+
+ public initIconSprite = (icon:string ):void => {
+ switch (this.componentSubType) {
+ case Utils.Constants.ComponentType.SERVICE:
+ this.iconClass = "sprite-services-icons " + icon;
+ break;
+ case Utils.Constants.ComponentType.PRODUCT:
+ this.iconClass = "sprite-product-icons " + icon;
+ break;
+ default:
+ this.iconClass = "sprite-resource-icons " + icon;
+ }
+ }
+
+ public getComponentSubType = ():string => {
+ return this.componentSubType;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/models/components/product.ts b/catalog-ui/app/scripts/models/components/product.ts
new file mode 100644
index 0000000000..6ba3404afb
--- /dev/null
+++ b/catalog-ui/app/scripts/models/components/product.ts
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.Components {
+ 'use strict';
+
+ export class Product extends Component{
+
+ public contacts:Array<string>;
+ public componentService: Services.Components.IProductService;
+ public fullName: string;
+
+ constructor(componentService: Services.Components.IProductService, $q:ng.IQService, component?:Product) {
+ super(componentService, $q, component);
+
+ if(component) {
+ this.fullName = component.fullName;
+ this.filterTerm = this.name + ' ' + this.description + ' ' + (this.tags ? this.tags.toString() : '') + ' ' + this.version;
+ this.contacts = component.contacts;
+ }
+ this.componentService = componentService;
+ this.iconSprite = "sprite-product-icons";
+ }
+
+ public deleteGroup = (uniqueId: string): void => {
+ _.forEach(this.categories, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ subcategory.groupings = _.reject (subcategory.groupings, (group:Models.IGroup) => {
+ return group.uniqueId === uniqueId;
+ });
+ if(subcategory.groupings.length == 0){ // if there is no groups, delete the subcategory
+ category.subcategories = _.reject (category.subcategories, (subcategoryObj:Models.ISubCategory) => {
+ return subcategoryObj.uniqueId === subcategory.uniqueId;
+ });
+ if(category.subcategories.length == 0){ // if there is no subcategory, delete the category
+ this.categories = _.reject (this.categories , (categoryObj:Models.IMainCategory) => {
+ return categoryObj.uniqueId === category.uniqueId;
+ });
+ }
+ }
+ });
+ });
+ };
+
+ private getCategoryObjectById = (categoriesArray:Array<Models.ICategoryBase>, categoryUniqueId:string):Models.ICategoryBase => {
+ let categorySelected = _.find(categoriesArray, (category) => {
+ return category.uniqueId === categoryUniqueId;
+ });
+ return categorySelected;
+ };
+
+ public addGroup = (category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup): void => {
+ if(!this.categories){
+ this.categories = new Array<Models.IMainCategory>();
+ }
+ let existingCategory:Models.IMainCategory = <Models.IMainCategory>this.getCategoryObjectById(this.categories, category.uniqueId);
+ let newGroup = angular.copy(group);
+ newGroup.filterTerms = undefined;
+ newGroup.isDisabled = undefined;
+ if(!existingCategory){
+ let newCategory: Models.IMainCategory = angular.copy(category);
+ newCategory.filteredGroup = undefined;
+ newCategory.subcategories = [];
+ let newSubcategory:Models.ISubCategory = angular.copy(subcategory);
+ newSubcategory.groupings = [];
+ newSubcategory.groupings.push(newGroup);
+ newCategory.subcategories.push(newSubcategory);
+ this.categories.push(newCategory);
+ }
+ else{
+ let existingSubcategory:Models.ISubCategory = <Models.ISubCategory> this.getCategoryObjectById(existingCategory.subcategories, subcategory.uniqueId);
+ if(!existingSubcategory){
+ let newSubcategory:Models.ISubCategory = angular.copy(subcategory);
+ newSubcategory.groupings = [];
+ newSubcategory.groupings.push(newGroup);
+ existingCategory.subcategories.push(newSubcategory);
+
+ } else {
+ let existingGroup:Models.IGroup = <Models.IGroup> this.getCategoryObjectById(existingSubcategory.groupings, group.uniqueId);
+ if(!existingGroup){
+ existingSubcategory.groupings.push(newGroup);
+ }
+ }
+ }
+ };
+
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/components/resource.ts b/catalog-ui/app/scripts/models/components/resource.ts
new file mode 100644
index 0000000000..243ef3463c
--- /dev/null
+++ b/catalog-ui/app/scripts/models/components/resource.ts
@@ -0,0 +1,185 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/3/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.Components {
+ 'use strict';
+
+ export class Resource extends Component {
+
+ public interfaces: any;
+ public derivedFrom:Array<string>;
+ public componentService: Services.Components.IResourceService;
+ public resourceType:string;
+ public payloadData:string;
+ public payloadName:string;
+ public importedFile: Sdc.Directives.FileUploadModel;
+
+ // Onboarding parameters
+ public csarUUID:string;
+ public csarVersion:string;
+ public csarPackageType:string;
+ public packageId:string;
+
+ constructor(componentService: Services.Components.IResourceService, $q: ng.IQService, component?:Resource) {
+ super(componentService, $q, component);
+ if(component) {
+
+ this.interfaces = component.interfaces;
+ this.derivedFrom = component.derivedFrom;
+ this.payloadData = component.payloadData ? component.payloadData : undefined;
+ this.payloadName = component.payloadName ? component.payloadName : undefined;
+ this.resourceType = component.resourceType;
+ this.csarUUID = component.csarUUID;
+ this.csarVersion = component.csarVersion;
+ this.filterTerm = this.name + ' ' + this.description + ' ' + (this.tags ? this.tags.toString() : '') + ' ' + this.version + ' ' + this.resourceType;
+
+ if (component.categories && component.categories[0] && component.categories[0].subcategories && component.categories[0].subcategories[0]) {
+ component.mainCategory = component.categories[0].name;
+ component.subCategory = component.categories[0].subcategories[0].name;
+ this.selectedCategory = component.mainCategory + "_#_" + component.subCategory;
+ this.importedFile = component.importedFile;
+ }
+ } else {
+ this.resourceType = Utils.Constants.ResourceType.VF;
+ }
+
+ this.componentService = componentService;
+ this.iconSprite = "sprite-resource-icons";
+ }
+
+ public getComponentSubType = ():string => {
+ return this.resourceType;
+ };
+
+ public isComplex = ():boolean => {
+ return this.resourceType === Utils.Constants.ResourceType.VF;
+ };
+
+ public isVl = ():boolean => {
+ return Utils.Constants.ResourceType.VL == this.resourceType;
+ };
+
+ public isCsarComponent = ():boolean => {
+ return !!this.csarUUID;
+ };
+
+ public createComponentOnServer = ():ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (component:Models.Components.Resource):void => {
+ this.payloadData = undefined;
+ this.payloadName = undefined;
+ deferred.resolve(component);
+ };
+ let onError = (error:any):void => {
+ deferred.reject(error);
+ };
+
+ this.handleTags();
+ if(this.importedFile){
+ this.payloadData = this.importedFile.base64;
+ this.payloadName = this.importedFile.filename;
+ }
+ this.componentService.createComponent(this).then(onSuccess, onError);
+ return deferred.promise;
+ };
+
+ /* we need to change the name of the input to vfInstanceName + input name before sending to server in order to create the inputs on the service
+ * we also need to remove already selected inputs (the inputs that already create on server, and disabled in the view - but they are selected so they are still in the view model
+ */
+ public createInputsFormInstances = (instanceInputsPropertiesMap:Models.InstanceInputsPropertiesMapData):ng.IPromise<Array<Models.InputModel>> => {
+ let deferred = this.$q.defer();
+ /*
+ let instanceInputsPropertiesMapToCreate: Models.InstanceInputsPropertiesMapData = new Models.InstanceInputsPropertiesMapData();
+ _.forEach(instanceInputsPropertiesMap, (properties:Array<Models.PropertyModel>, instanceId:string) => {
+
+ if(properties && properties.length > 0) {
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = _.find(this.componentInstances, (instace:Models.ComponentsInstances.ComponentInstance) => {
+ return instace.uniqueId === instanceId;
+ });
+
+ instanceInputsPropertiesMapToCreate[instanceId] = new Array<Models.PropertyModel>();
+ _.forEach(properties, (property:Models.PropertyModel) => {
+
+ if(!property.isAlreadySelected) {
+ let newInput = new Models.PropertyModel(property);
+ newInput.name = componentInstance.normalizedName + '_' + property.name;
+ instanceInputsPropertiesMapToCreate[instanceId].push(newInput);
+ }
+ });
+ if( instanceInputsPropertiesMapToCreate[instanceId].length === 0) {
+ delete instanceInputsPropertiesMapToCreate[instanceId];
+ }
+ } else {
+ delete instanceInputsPropertiesMapToCreate[instanceId];
+ }
+ });
+
+ if(Object.keys(instanceInputsPropertiesMapToCreate).length > 0) {
+ let deferred = this.$q.defer();
+ let onSuccess = (propertiesCreated: Array<Models.PropertyModel>):void => {
+ this.inputs = propertiesCreated.concat(this.inputs);
+ deferred.resolve(propertiesCreated);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.createInputsFromInstancesInputsProperties(this.uniqueId, new Models.InstanceInputsPropertiesMap(instanceInputsPropertiesMapToCreate)).then(onSuccess, onFailed);
+ }
+ */
+ return deferred.promise;
+ };
+
+ // we need to change the name of the input to vfInstanceName + input name before sending to server in order to create the inputs on the service
+ public getResourceInputInputs = (inputId:string):ng.IPromise<Array<Models.InputModel>> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (inputInputs: Array<Models.InputModel>):void => {
+ let input: Models.InputModel = _.find(this.inputs, (input:Models.InputModel) => {
+ return input.uniqueId === inputId;
+ });
+ input.inputs = inputInputs;
+ deferred.resolve(inputInputs);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.getComponentInputInputs(this.uniqueId, inputId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public toJSON = ():any => {
+ this.componentService = undefined;
+ this.filterTerm = undefined;
+ this.iconSprite = undefined;
+ this.mainCategory = undefined;
+ this.subCategory = undefined;
+ this.selectedInstance = undefined;
+ this.showMenu = undefined;
+ this.$q = undefined;
+ this.selectedCategory = undefined;
+ this.importedFile = undefined;
+ return this;
+ };
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/components/service.ts b/catalog-ui/app/scripts/models/components/service.ts
new file mode 100644
index 0000000000..b1730aae94
--- /dev/null
+++ b/catalog-ui/app/scripts/models/components/service.ts
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.Components {
+ 'use strict';
+
+
+ export class Service extends Component {
+
+ public serviceApiArtifacts:Models.ArtifactGroupModel;
+ public componentService:Services.Components.IServiceService;
+
+ constructor(componentService:Services.Components.IServiceService, $q:ng.IQService, component?:Service) {
+ super(componentService, $q, component);
+ if (component) {
+ this.serviceApiArtifacts = new Models.ArtifactGroupModel(component.serviceApiArtifacts);
+ this.filterTerm = this.name + ' ' + this.description + ' ' + (this.tags ? this.tags.toString() : '') + ' ' + this.version;
+ if (component.categories && component.categories[0]) {
+ this.mainCategory = component.categories[0].name;
+ this.selectedCategory = this.mainCategory;
+ }
+ }
+ this.componentService = componentService;
+ this.iconSprite = "sprite-services-icons";
+ }
+
+ public getDistributionsList = ():ng.IPromise<Array<Models.Distribution>> => {
+ return this.componentService.getDistributionsList(this.uuid);
+ };
+
+ public getDistributionsComponent = (distributionId:string):ng.IPromise<Array<Models.DistributionComponent>> => {
+ return this.componentService.getDistributionComponents(distributionId);
+ };
+
+ public markAsDeployed = (distributionId:string):ng.IPromise<any> => {
+ return this.componentService.markAsDeployed(this.uniqueId, distributionId);
+ };
+
+ /* we need to change the name of the input to vfInstanceName + input name before sending to server in order to create the inputs on the service
+ * we also need to remove already selected inputs (the inputs that already create on server, and disabled in the view - but they are selected so they are still in the view model
+ */
+ public createInputsFormInstances = (instancesInputsMap:Models.InstancesInputsMapData):ng.IPromise<Array<Models.InputModel>> => {
+ let deferred = this.$q.defer();
+
+ let instancesInputsMapToCreate: Models.InstancesInputsMapData = new Models.InstancesInputsMapData();
+ _.forEach(instancesInputsMap, (inputs:Array<Models.InputModel>, instanceId:string) => {
+
+ if(inputs && inputs.length > 0) {
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = _.find(this.componentInstances, (instace:Models.ComponentsInstances.ComponentInstance) => {
+ return instace.uniqueId === instanceId;
+ });
+ instancesInputsMapToCreate[instanceId] = new Array<Models.InputModel>();
+ _.forEach(inputs, (input:Models.InputModel) => {
+
+ if(!input.isAlreadySelected) {
+ let newInput = new Models.InputModel(input);
+ newInput.name = componentInstance.normalizedName + '_' + input.name;
+ instancesInputsMapToCreate[instanceId].push(newInput);
+ }
+ });
+ if( instancesInputsMapToCreate[instanceId].length === 0) {
+ delete instancesInputsMapToCreate[instanceId];
+ }
+ } else {
+ delete instancesInputsMapToCreate[instanceId];
+ }
+ });
+
+ if(Object.keys(instancesInputsMapToCreate).length > 0) {
+ let deferred = this.$q.defer();
+ let onSuccess = (inputsCreated: Array<Models.InputModel>):void => {
+ this.inputs = inputsCreated.concat(this.inputs);
+ deferred.resolve(inputsCreated);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.createInputsFromInstancesInputs(this.uniqueId, new Models.InstancesInputsMap(instancesInputsMapToCreate)).then(onSuccess, onFailed);
+ }
+ return deferred.promise;
+ };
+
+ // we need to change the name of the input to vfInstanceName + input name before sending to server in order to create the inputs on the service
+ public getServiceInputInputs = (inputId:string):ng.IPromise<Array<Models.InputModel>> => {
+ let deferred = this.$q.defer();
+ let onSuccess = (inputInputs: Array<Models.InputModel>):void => {
+ let input: Models.InputModel = _.find(this.inputs, (input:Models.InputModel) => {
+ return input.uniqueId === inputId;
+ });
+ input.inputs = inputInputs;
+ deferred.resolve(inputInputs);
+ };
+ let onFailed = (error:any): void => {
+ deferred.reject(error);
+ };
+ this.componentService.getComponentInputInputs(this.uniqueId, inputId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public deleteServiceInput = (inputId:string):ng.IPromise<Models.InputModel> => {
+ var deferred = this.$q.defer();
+
+ var onSuccess = (input: Models.InputModel):void => {
+ deferred.resolve(input)
+ };
+
+ var onFailed = (error:any) : void => {
+ deferred.reject(error);
+ };
+
+ this.componentService.deleteComponentInput(this.uniqueId, inputId).then(onSuccess, onFailed);
+ return deferred.promise;
+ };
+
+ public getArtifactsByType = (artifactGroupType:string):Models.ArtifactGroupModel => {
+ switch (artifactGroupType) {
+ case Utils.Constants.ArtifactGroupType.DEPLOYMENT:
+ return this.deploymentArtifacts;
+ case Utils.Constants.ArtifactGroupType.INFORMATION:
+ return this.artifacts;
+ case Utils.Constants.ArtifactGroupType.SERVICE_API:
+ return this.serviceApiArtifacts;
+ }
+ };
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/componentsInstances/componentInstance.ts b/catalog-ui/app/scripts/models/componentsInstances/componentInstance.ts
new file mode 100644
index 0000000000..af2f338998
--- /dev/null
+++ b/catalog-ui/app/scripts/models/componentsInstances/componentInstance.ts
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.ComponentsInstances {
+ 'use strict';
+
+ export class ComponentInstance{
+
+ public componentUid: string;
+ public componentName:string;
+ public posX: number;
+ public posY: number;
+ public componentVersion:string;
+ public description: string;
+ public icon: string;
+ public name: string;
+ public normalizedName:string;
+ public originType: string;
+ public deploymentArtifacts: Models.ArtifactGroupModel;
+ public propertyValueCounter: number;
+ public uniqueId: string;
+ public creationTime: number;
+ public modificationTime: number;
+ public capabilities: Models.CapabilitiesGroup;
+ public requirements: Models.RequirementsGroup;
+
+ //custom properties
+ public certified: boolean;
+ public iconSprite:string;
+ public inputs: Array<Models.InputModel>;
+ public properties: Array<Models.PropertyModel>;
+
+ constructor(componentInstance?: ComponentInstance) {
+
+ if(componentInstance) {
+ this.componentUid = componentInstance.componentUid;
+ this.componentName = componentInstance.componentName;
+
+ this.componentVersion = componentInstance.componentVersion;
+ this.description = componentInstance.description;
+ this.icon = componentInstance.icon;
+ this.name = componentInstance.name;
+ this.normalizedName = componentInstance.normalizedName;
+ this.originType = componentInstance.originType;
+ this.deploymentArtifacts = new Models.ArtifactGroupModel(componentInstance.deploymentArtifacts);
+ this.uniqueId = componentInstance.uniqueId;
+ this.creationTime = componentInstance.creationTime;
+ this.modificationTime = componentInstance.modificationTime;
+ this.propertyValueCounter = componentInstance.propertyValueCounter;
+ this.capabilities = new Models.CapabilitiesGroup(componentInstance.capabilities);
+ this.requirements = new Models.RequirementsGroup(componentInstance.requirements);
+ this.certified = componentInstance.certified;
+ this.updatePosition(componentInstance.posX, componentInstance.posY);
+ }
+ }
+
+ public isUcpe = ():boolean =>{
+ if(this.originType === 'VF' && this.capabilities && this.capabilities['tosca.capabilities.Container'] && this.name.toLowerCase().indexOf('ucpe') > -1){
+ return true;
+ }
+ return false;
+ };
+
+ public isVl = ():boolean =>{
+ return this.originType === 'VL';
+ };
+
+
+ public setInstanceRC = ():void=>{
+ _.forEach(this.requirements, (requirementValue:Array<any>, requirementKey)=> {
+ _.forEach(requirementValue, (requirement)=> {
+ if (!requirement.ownerName){
+ requirement['ownerId'] = this.uniqueId;
+ requirement['ownerName'] = this.name;
+ }
+ });
+ });
+ _.forEach(this.capabilities, (capabilityValue:Array<any>, capabilityKey)=> {
+ _.forEach(capabilityValue, (capability)=> {
+ if (!capability.ownerName){
+ capability['ownerId'] = this.uniqueId;
+ capability['ownerName'] = this.name;
+ }
+ });
+ });
+ };
+
+ public updatePosition (posX:number, posY:number) {
+ this.posX = posX;
+ this.posY = posY;
+ }
+
+ public toJSON = ():any => {
+
+ var serverInstance = angular.copy(this);
+ serverInstance.certified = undefined;
+ serverInstance.iconSprite = undefined;
+ serverInstance.inputs = undefined;
+ serverInstance.properties = undefined;
+ serverInstance.requirements = undefined;
+ serverInstance.capabilities = undefined;
+ return serverInstance;
+ };
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/componentsInstances/productInstance.ts b/catalog-ui/app/scripts/models/componentsInstances/productInstance.ts
new file mode 100644
index 0000000000..71ef9bb7d3
--- /dev/null
+++ b/catalog-ui/app/scripts/models/componentsInstances/productInstance.ts
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.ComponentsInstances {
+ 'use strict';
+
+ export class ProductInstance extends ComponentInstance{
+
+ constructor(componentInstance?: ProductInstance) {
+ super(componentInstance);
+ this.iconSprite = "sprite-product-icons";
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/componentsInstances/resourceInstance.ts b/catalog-ui/app/scripts/models/componentsInstances/resourceInstance.ts
new file mode 100644
index 0000000000..67df05ded9
--- /dev/null
+++ b/catalog-ui/app/scripts/models/componentsInstances/resourceInstance.ts
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.ComponentsInstances {
+ 'use strict';
+
+ export class ResourceInstance extends ComponentInstance{
+
+ constructor(componentInstance?: ResourceInstance) {
+ super(componentInstance);
+
+ this.iconSprite = "sprite-resource-icons";
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/componentsInstances/serviceInstance.ts b/catalog-ui/app/scripts/models/componentsInstances/serviceInstance.ts
new file mode 100644
index 0000000000..0d78feafd3
--- /dev/null
+++ b/catalog-ui/app/scripts/models/componentsInstances/serviceInstance.ts
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models.ComponentsInstances {
+ 'use strict';
+
+ export class ServiceInstance extends ComponentInstance{
+
+ constructor(componentInstance?: ServiceInstance) {
+ super(componentInstance);
+ this.iconSprite = "sprite-services-icons";
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/csar-component.ts b/catalog-ui/app/scripts/models/csar-component.ts
new file mode 100644
index 0000000000..da649c1efd
--- /dev/null
+++ b/catalog-ui/app/scripts/models/csar-component.ts
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.Models {
+ 'use strict';
+
+ export interface ICsarComponent {
+ displayName:string;
+ description:string;
+ vspName:string;
+ version:string;
+ packageId:string;
+ category:string;
+ subCategory:string
+ vendorName:string;
+ packageType:string;
+ vendorRelease:string;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/data-type-properties.ts b/catalog-ui/app/scripts/models/data-type-properties.ts
new file mode 100644
index 0000000000..973978d9b2
--- /dev/null
+++ b/catalog-ui/app/scripts/models/data-type-properties.ts
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/25/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class DataTypePropertyModel {
+
+ //server data
+ uniqueId:string;
+ type:string;
+ required:boolean;
+ definition:boolean;
+ description:string;
+ password:boolean;
+ name:string;
+ parentUniqueId:string;
+ defaultValue:string;
+ constraints:Array<any>;
+ //custom
+ simpleType:string;
+
+ constructor(dataTypeProperty:DataTypePropertyModel) {
+ if (dataTypeProperty) {
+ this.uniqueId = dataTypeProperty.uniqueId;
+ this.type = dataTypeProperty.type;
+ this.required = dataTypeProperty.required;
+ this.definition = dataTypeProperty.definition;
+ this.description = dataTypeProperty.description;
+ this.password = dataTypeProperty.password;
+ this.name = dataTypeProperty.name;
+ this.parentUniqueId = dataTypeProperty.parentUniqueId;
+ this.defaultValue = dataTypeProperty.defaultValue;
+ this.constraints = dataTypeProperty.constraints;
+ this.simpleType = dataTypeProperty.simpleType;
+ }
+ }
+
+ public toJSON = ():any => {
+ this.simpleType = undefined;
+ return this;
+ };
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/data-types-map.ts b/catalog-ui/app/scripts/models/data-types-map.ts
new file mode 100644
index 0000000000..d1bee48e41
--- /dev/null
+++ b/catalog-ui/app/scripts/models/data-types-map.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/25/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class DataTypesMapData {
+ [dataTypeId:string]: Array<DataTypeModel>;
+ }
+
+ export class DataTypesMap {
+ dataTypesMap:DataTypesMapData;
+
+ constructor(dataTypesMap:DataTypesMapData) {
+ this.dataTypesMap = dataTypesMap;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/data-types.ts b/catalog-ui/app/scripts/models/data-types.ts
new file mode 100644
index 0000000000..d7de238f3b
--- /dev/null
+++ b/catalog-ui/app/scripts/models/data-types.ts
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/25/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class DataTypeModel {
+
+ //server data
+ name:string;
+ uniqueId:string;
+ derivedFromName:string;
+ creationTime:string;
+ modificationTime:string;
+ properties:Array<Models.DataTypePropertyModel>;
+
+ constructor(dataType:DataTypeModel) {
+ if (dataType) {
+ this.uniqueId = dataType.uniqueId;
+ this.name = dataType.name;
+ this.derivedFromName = dataType.derivedFromName;
+ this.creationTime = dataType.creationTime;
+ this.modificationTime = dataType.modificationTime;
+ this.properties = dataType.properties;
+ }
+ }
+
+ public toJSON = ():any => {
+
+ return this;
+ };
+ }
+
+}
+
diff --git a/catalog-ui/app/scripts/models/distribution.ts b/catalog-ui/app/scripts/models/distribution.ts
new file mode 100644
index 0000000000..1c3a9568dd
--- /dev/null
+++ b/catalog-ui/app/scripts/models/distribution.ts
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class DistributionStatuses{
+ public omfComponentID: string;
+ public url: string;
+ public timestamp:string;
+ public status: string;
+
+ constructor() {
+ }
+ }
+
+
+ export class DistributionComponent{
+ public omfComponentID: string;
+ public url: string;
+ public timestamp:string;
+ public status: string;
+
+ constructor() {
+ }
+ }
+
+ export class Distribution {
+ public distributionID:string;
+ public timestamp:string;
+ public userId:string;
+ public deployementStatus:string;
+ public distributionComponents:Array<Models.DistributionComponent>;
+ public statusCount:any;
+ //custom data
+ public dateFormat:string;
+
+ constructor() {
+ }
+ public toJSON = ():any => {
+ this.dateFormat = undefined;
+ return this;
+ };
+
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/models/file-download.ts b/catalog-ui/app/scripts/models/file-download.ts
new file mode 100644
index 0000000000..8a74ed57c1
--- /dev/null
+++ b/catalog-ui/app/scripts/models/file-download.ts
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export interface IFileDownload{
+ artifactName: string;
+ base64Contents:string;
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/d2-node.ts b/catalog-ui/app/scripts/models/graph/d2-node.ts
new file mode 100644
index 0000000000..16daa5470d
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/d2-node.ts
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+
+// module Sdc.Models {
+//
+// export interface D2Node extends go.Node {
+// //TODO:should be typesafe!
+// resource: any;
+// key:string;
+// data:any;
+// canvasPosition: {x:number;y:number};
+// }
+// }
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/common-base-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/common-base-link.ts
new file mode 100644
index 0000000000..7d21c5d978
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/common-base-link.ts
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/29/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Models {
+
+ export class CommonLinkBase {
+
+ img:string;
+ color:string;
+ classes: string;
+
+ //this is cytoscapejs fields
+ public source: string;
+ public target: string;
+ public type: string;
+ public isSdcElement: boolean;
+
+ constructor() {
+ this.isSdcElement = true;
+ this.type = 'sdc-link';
+
+ }
+
+ public setImage = (imgUrl: string) => {
+ this.img = imgUrl;
+ };
+
+ public setColor = (color: string) => {
+ this.color = color;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/common-ci-link-base.ts b/catalog-ui/app/scripts/models/graph/graph-links/common-ci-link-base.ts
new file mode 100644
index 0000000000..1e7416ac3e
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/common-ci-link-base.ts
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/29/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Models {
+
+ export interface ICommonCiLinkBase {
+
+ }
+
+ export class CommonCiLinkBase extends CommonLinkBase implements ICommonCiLinkBase {
+
+ relation:RelationshipModel;
+
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super();
+ if (relation) {
+ if(singleRelationship){
+ this.relation = new Models.RelationshipModel(relation, singleRelationship);
+ }else{
+ this.relation = new Models.RelationshipModel(relation);
+ }
+ this.source = relation.fromNode;
+ this.target = relation.toNode;
+ } else {
+ this.relation = new RelationshipModel();
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-link-base.ts b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-link-base.ts
new file mode 100644
index 0000000000..3587198615
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-link-base.ts
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export interface ICompositionCiLinkBase extends ICommonCiLinkBase{
+ updateLinkDirection():void;
+ }
+
+ export class CompositionCiLinkBase extends CommonCiLinkBase implements ICompositionCiLinkBase {
+
+ type:string;
+ visible:boolean;
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super(relation, singleRelationship);
+ this.visible = true;
+ }
+
+ public setRelation = (relation: Models.RelationshipModel) => {
+ this.relation = relation;
+ };
+
+ updateLinkDirection():void{
+ this.source = this.relation.fromNode;
+ this.target = this.relation.toNode;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-simple-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-simple-link.ts
new file mode 100644
index 0000000000..c2deddbfc3
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-simple-link.ts
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export class CompositionCiSimpleLink extends CompositionCiLinkBase {
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super(relation, singleRelationship);
+ this.color = Utils.Constants.GraphColors.BASE_LINK;
+ this.classes = 'simple-link';
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-host-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-host-link.ts
new file mode 100644
index 0000000000..7a30c20eee
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-host-link.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 4/20/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+ export class LinkUcpeHost extends CompositionCiLinkBase {
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super(relation, singleRelationship);
+ this.visible = false;
+ this.classes = "ucpe-host-link";
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-link.ts
new file mode 100644
index 0000000000..5d035ccc2c
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-ucpe-link.ts
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export class CompositionCiUcpeLink extends CompositionCiLinkBase {
+
+ isFromUcpe: boolean;
+ constructor(relation?:RelationshipModel, from?:boolean, singleRelation?:Relationship) {
+ super(relation, singleRelation);
+ this.isFromUcpe = from;
+ this.target = relation.toNode;
+ this.source = singleRelation.requirementOwnerId;
+ this.relation.relationships = [singleRelation];
+ this.color = Utils.Constants.GraphColors.BASE_LINK;
+ }
+
+ updateLinkDirection():void {}
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-link.ts
new file mode 100644
index 0000000000..a347db6cb5
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-link.ts
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export class CompositionCiVLink extends CompositionCiLinkBase {
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super(relation, singleRelationship);
+ this.color = Utils.Constants.GraphColors.VL_LINK;
+ this.classes ='vl-link';
+ }
+
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-ucpe-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-ucpe-link.ts
new file mode 100644
index 0000000000..2ebc796cb9
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/composition-graph-links/composition-ci-vl-ucpe-link.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 4/20/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export class CompositionCiVlUcpeLink extends CompositionCiUcpeLink {
+
+ constructor(relation?:RelationshipModel, from?:boolean, singleRelation?:Relationship) {
+ super(relation, from, singleRelation);
+ this.color = Utils.Constants.GraphColors.VL_LINK;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/links-factory.ts b/catalog-ui/app/scripts/models/graph/graph-links/links-factory.ts
new file mode 100644
index 0000000000..8f6cd6d321
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/links-factory.ts
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 5/1/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Utils {
+ 'use strict';
+
+ export class LinksFactory {
+
+ constructor() {
+ }
+
+ public createGraphLink = (cy: Cy.Instance, relation:Models.RelationshipModel, singleRelation:Models.Relationship):Models.CompositionCiLinkBase => {
+
+ let newRelation:Models.CompositionCiLinkBase;
+
+ let fromNode:Models.Graph.CompositionCiNodeBase = cy.getElementById(relation.fromNode).data();
+ let toNode:Models.Graph.CompositionCiNodeBase = cy.getElementById(relation.toNode).data() ;
+
+ if ((relation.fromNode && fromNode.isUcpePart) || (relation.toNode && toNode.isUcpePart )) { //Link from or to node inside ucpe
+
+ if (singleRelation && singleRelation.relationship.type && singleRelation.relationship.type == 'tosca.relationships.HostedOn') {
+ newRelation = new Models.LinkUcpeHost(relation, singleRelation);
+ } else if (singleRelation.relationship.type && _.includes(singleRelation.relationship.type.toLowerCase(), 'link')) {
+ newRelation = new Models.CompositionCiVlUcpeLink(relation, fromNode.isUcpePart, singleRelation);
+ } else {
+ newRelation = new Models.CompositionCiUcpeLink(relation, fromNode.isUcpePart, singleRelation);
+ }
+ } else if (singleRelation.relationship.type && _.includes(singleRelation.relationship.type.toLowerCase(), 'link')) {
+ newRelation = new Models.CompositionCiVLink(relation, singleRelation);
+ } else {
+ newRelation = new Models.CompositionCiSimpleLink(relation, singleRelation);
+ }
+
+ return newRelation;
+ };
+
+ public createUcpeHostLink = (relation:Models.RelationshipModel):Models.LinkUcpeHost => {
+ return new Models.LinkUcpeHost(relation);
+ };
+
+ public createVLLink = (relation:Models.RelationshipModel):Models.CompositionCiVLink => {
+ return new Models.CompositionCiVLink(relation);
+ }
+
+
+ public createModuleGraphLinks= (relation:Models.RelationshipModel, singleRelation:Models.Relationship):Models.ModuleCiLinkBase => {
+
+ let newRelation:Models.ModuleCiLinkBase;
+
+ if (_.includes(singleRelation.relationship.type.toLowerCase(), 'link')) {
+ newRelation = new Models.ModuleCiVlLink(relation, singleRelation);
+ } else {
+ newRelation = new Models.ModuleCiLinkBase(relation, singleRelation);
+ }
+
+ return newRelation;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-link-base.ts b/catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-link-base.ts
new file mode 100644
index 0000000000..b85e7673f5
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-link-base.ts
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/29/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export interface IModuleCiLinkBase extends ICommonCiLinkBase{
+
+ }
+
+ export class ModuleCiLinkBase extends CommonCiLinkBase implements IModuleCiLinkBase {
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super(relation, singleRelationship);
+ this.color = Utils.Constants.GraphColors.BASE_LINK;
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-vl-link.ts b/catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-vl-link.ts
new file mode 100644
index 0000000000..a421610792
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graph-links/module-graph-links/module-ci-vl-link.ts
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/29/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.Models {
+
+ export interface IModuleCiVlLink extends ICommonCiLinkBase{
+
+ }
+
+ export class ModuleCiVlLink extends CommonCiLinkBase implements IModuleCiVlLink {
+
+ constructor(relation?:RelationshipModel, singleRelationship?:Models.Relationship) {
+ super(relation, singleRelationship);
+ this.color = Utils.Constants.GraphColors.VL_LINK;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/graphTooltip.ts b/catalog-ui/app/scripts/models/graph/graphTooltip.ts
new file mode 100644
index 0000000000..08a85e1126
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/graphTooltip.ts
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class GraphTooltip{
+ position: Cy.Position;
+ isShow:boolean;
+ text:string;
+
+ constructor();
+ constructor(position: Cy.Position, isShow:boolean, text: string);
+ constructor(position?: Cy.Position, isShow?:boolean, text?: string) {
+ this.position = position;
+ this.isShow = isShow;
+ this.text = text;
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/graph/link-menu.ts b/catalog-ui/app/scripts/models/graph/link-menu.ts
new file mode 100644
index 0000000000..606c392982
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/link-menu.ts
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class LinkMenu {
+ position:Sdc.Models.Graph.Point;
+ isShow:boolean;
+ link:Cy.CollectionFirstEdge;
+
+ constructor();
+ constructor(point:Sdc.Models.Graph.Point, isShow:boolean, link:Cy.CollectionFirstEdge);
+ constructor(point?:Sdc.Models.Graph.Point, isShow?:boolean, link?:Cy.CollectionFirstEdge) {
+ this.position = point ? point: new Sdc.Models.Graph.Point();
+ this.isShow = isShow ? isShow : false;
+ this.link = link ? link : null;
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/graph/match-relation.ts b/catalog-ui/app/scripts/models/graph/match-relation.ts
new file mode 100644
index 0000000000..8d864c675b
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/match-relation.ts
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class MatchBase {
+ requirement:Models.Requirement;
+ isFromTo:boolean;
+ fromNode:string;
+ toNode:string;
+
+ constructor(requirement:Models.Requirement, isFromTo:boolean, fromNode:string, toNode:string) {
+ this.requirement = requirement;
+ this.isFromTo = isFromTo;
+ this.fromNode = fromNode;
+ this.toNode = toNode;
+ }
+
+ public getDisplayText = (menuSide:string):string => {return '';};
+
+ public isOwner = (id:string):boolean => { return false; }
+
+ }
+
+ export class MatchReqToReq extends MatchBase {
+
+ secondRequirement:Models.Requirement;
+
+ constructor(requirement:Models.Requirement, secondRequirement:Models.Requirement, isFromTo:boolean, fromNode:string, toNode:string) {
+ super(requirement, isFromTo, fromNode, toNode);
+ this.secondRequirement = secondRequirement;
+ }
+
+ public getDisplayText = (menuSide:string):string => {
+ if ('left' == menuSide) {
+ return this.requirement.getFullTitle();
+ }
+ return this.secondRequirement.getFullTitle();
+ };
+
+ public isOwner = (id:string):boolean => {
+ return this.secondRequirement.ownerId === id || this.requirement.ownerId === id;
+ }
+ }
+
+ export class MatchReqToCapability extends MatchBase {
+
+ capability:Models.Capability;
+
+ constructor(requirement:Models.Requirement, capability:Models.Capability, isFromTo:boolean, fromNode:string, toNode:string) {
+ super(requirement, isFromTo, fromNode, toNode);
+ this.capability = capability;
+ }
+
+ public matchToRelation = ():Models.Relationship => {
+ let relationship:Models.Relationship = new Models.Relationship();
+ relationship.capability = this.capability.name;
+ relationship.capabilityOwnerId = this.capability.ownerId;
+ relationship.capabilityUid = this.capability.uniqueId;
+ relationship.relationship = new Models.RelationType(this.capability.type);
+ relationship.requirement = this.requirement.name;
+ relationship.requirementOwnerId = this.requirement.ownerId;
+ relationship.requirementUid = this.requirement.uniqueId;
+ return relationship;
+ };
+
+
+ public getDisplayText = (menuSide:string):string => {
+ if (this.isFromTo && 'left' == menuSide || !this.isFromTo && 'right' == menuSide) {
+ return this.requirement.getFullTitle();
+ }
+ return this.capability.getFullTitle();
+
+ };
+
+ public isOwner = (id:string):boolean => {
+ return this.capability.ownerId === id || this.requirement.ownerId === id;
+ };
+
+
+ public matchToRelationModel = ():Models.RelationshipModel => {
+ let relationshipModel:Models.RelationshipModel = new Models.RelationshipModel();
+ let relationship:Models.Relationship = this.matchToRelation();
+ relationshipModel.setRelationshipModelParams(this.fromNode, this.toNode, [relationship]);
+ return relationshipModel;
+ };
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/models/graph/nodes/base-common-node.ts b/catalog-ui/app/scripts/models/graph/nodes/base-common-node.ts
new file mode 100644
index 0000000000..e1957e61aa
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/base-common-node.ts
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/29/2016.
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.Models.Graph {
+ 'use strict';
+
+ export abstract class CommonNodeBase {
+
+ public displayName:string;
+ public name:string;
+ public img:string;
+ public certified:boolean;
+ public isGroup:boolean;
+ public imagesPath: string;
+ public isDraggable: boolean; //we need to to manage manually the dragging on the graph inside groups (ucpe-cp is not draggable)
+
+ //cytoscape fields
+ public id:string;
+ public type:string; //type is for the edge edition extension, by type we put the green plus icon in position
+ public isSdcElement:boolean; //this fields is in order to filter sdc elements from all extensions elements
+ public classes: string;
+ public parent: string;
+ public allowConnection: boolean; //this is for egeEdition extension in order to decide if connection to a node is available
+
+ constructor() {
+
+ this.imagesPath = Services.AngularJSBridge.getAngularConfig().imagesPath;
+ this.type = "basic-node";
+ this.isSdcElement = true;
+ this.isDraggable = true;
+ this.allowConnection = true;
+ }
+
+ public updateNameForDisplay =() => {
+ let context = document.createElement("canvas").getContext("2d");
+ context.font = "13px Arial";
+
+ if (63 < context.measureText(this.name).width) {
+ let newLen = this.name.length - 3;
+ let newName = this.name.substring(0, newLen);
+
+ while (60 < (context.measureText(newName).width)) {
+ newName = newName.substring(0, (--newLen));
+ }
+ this.displayName = newName + '...';
+ return;
+ }
+
+ this.displayName = this.name;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/common-ci-node-base.ts b/catalog-ui/app/scripts/models/graph/nodes/common-ci-node-base.ts
new file mode 100644
index 0000000000..1597650654
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/common-ci-node-base.ts
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Models.Graph {
+
+ export abstract class CommonCINodeBase extends CommonNodeBase {
+
+ public certified:boolean;
+ public template:string;
+ public componentInstance:Models.ComponentsInstances.ComponentInstance;
+ public group:string;
+
+ constructor(instance:Models.ComponentsInstances.ComponentInstance) {
+ super();
+ this.componentInstance = instance;
+ this.id = this.componentInstance.uniqueId;
+ this.name = this.componentInstance.name;
+ this.img = '';
+ this.certified = this.isCertified(this.componentInstance.componentVersion);
+ this.displayName = instance.name;
+ }
+
+ private isCertified(version:string):boolean {
+ return 0 === (parseFloat(version)) % 1;
+ }
+
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts
new file mode 100644
index 0000000000..5f4c0df3c2
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts
@@ -0,0 +1,72 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export interface ICompositionCiNodeBase {
+
+ }
+
+
+ export abstract class CompositionCiNodeBase extends CommonCINodeBase implements ICompositionCiNodeBase {
+
+ public textPosition: string; //need to move to cp UCPE
+ public isUcpe: boolean;
+ public isInsideGroup: boolean;
+ public isUcpePart: boolean;
+
+ constructor(instance: Models.ComponentsInstances.ComponentInstance,
+ public imageCreator: Utils.ImageCreatorService) {
+ super(instance);
+ this.init();
+ }
+
+ private init() {
+
+ this.displayName = this.getDisplayName();
+ this.isUcpe = false;
+ this.isGroup = false;
+ this.isUcpePart = false;
+ this.isInsideGroup = false;
+
+ }
+
+ public initImage(node: Cy.Collection): string {
+
+ this.imageCreator.getImageBase64(this.imagesPath + Utils.Constants.ImagesUrl.RESOURCE_ICONS + this.componentInstance.icon + '.png',
+ this.imagesPath + Utils.Constants.ImagesUrl.RESOURCE_ICONS + 'uncertified.png')
+ .then(imageBase64 => {
+ this.img = imageBase64;
+ node.style({'background-image': this.img});
+ });
+
+ return this.img;
+ }
+
+ protected getDisplayName(): string {
+
+ let graphResourceName = Services.AngularJSBridge.getFilter('graphResourceName');
+ let resourceName = Services.AngularJSBridge.getFilter('resourceName');
+ return graphResourceName(resourceName(this.componentInstance.name));
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-cp.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-cp.ts
new file mode 100644
index 0000000000..6286c8245d
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-cp.ts
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export class CompositionCiNodeCp extends CompositionCiNodeBase {
+
+ constructor(instance:Models.ComponentsInstances.ComponentInstance,
+ imageCreator: Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.initCp();
+ }
+
+ private initCp():void {
+ let sdcConfig = Services.AngularJSBridge.getAngularConfig();
+ this.img = sdcConfig.imagesPath + Utils.Constants.ImagesUrl.RESOURCE_ICONS + this.componentInstance.icon + '.png';
+ this.type = "basic-small-node";
+ //if the cp from type cpEndPointInstances create with another template
+ if(sdcConfig.cpEndPointInstances.indexOf(this.componentInstance.icon) > -1){
+ this.classes = 'cp-end-point-node';
+ }else {
+ this.classes = 'cp-node';
+ }
+ if(!this.certified) {
+ this.classes = this.classes + ' not-certified';
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-service.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-service.ts
new file mode 100644
index 0000000000..41bf0cef98
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-service.ts
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export class CompositionCiNodeService extends CompositionCiNodeBase {
+
+ constructor(instance:Models.ComponentsInstances.ComponentInstance,
+ imageCreator: Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.initService();
+ }
+
+ private initService():void {
+
+ this.img = this.imagesPath + Utils.Constants.ImagesUrl.SERVICE_ICONS + this.componentInstance.icon + '.png';
+ this.classes = 'service-node'
+ if(!this.certified) {
+ this.classes = this.classes + ' not-certified';
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe-cp.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe-cp.ts
new file mode 100644
index 0000000000..9123ff7224
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe-cp.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export class CompositionCiNodeUcpeCp extends CompositionCiNodeCp {
+
+ constructor(instance:Models.ComponentsInstances.ComponentInstance,
+ imageCreator: Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.isUcpePart = true;
+ this.classes = 'ucpe-cp'; // the css class for the node
+ this.parent = instance.uniqueId;
+ this.type = 'ucpe-cp-node'; //the type is for the handle (plus icon) extension
+ this.isDraggable = false;
+ }
+
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe.ts
new file mode 100644
index 0000000000..bc91e004f4
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-ucpe.ts
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export class NodeUcpe extends CompositionCiNodeBase {
+ constructor(instance:Models.ComponentsInstances.ComponentInstance,
+ imageCreator:Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.initUcpe();
+ }
+
+ private initUcpe():void {
+ this.isUcpe = true;
+ this.isGroup = true;
+ this.isUcpePart = true;
+ this.classes = 'ucpe-node';
+ this.type = 'ucpe-node';
+ this.allowConnection = false;
+
+ if (!this.certified) {
+ this.classes = this.classes + ' not-certified-ucpe';
+ }
+ }
+
+ }
+}
+
+
+
+
+
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vf.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vf.ts
new file mode 100644
index 0000000000..d090960046
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vf.ts
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export class CompositionCiNodeVf extends CompositionCiNodeBase {
+
+ constructor(instance: Models.ComponentsInstances.ComponentInstance,
+ imageCreator: Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.initVf();
+ }
+
+ private initVf(): void {
+ this.img = this.imagesPath + Utils.Constants.ImagesUrl.RESOURCE_ICONS + this.componentInstance.icon + '.png';
+ this.classes = 'vf-node';
+ if(!this.certified) {
+ this.classes = this.classes + ' not-certified';
+ }
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vfc.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vfc.ts
new file mode 100644
index 0000000000..04f45c87fb
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vfc.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+ export class CompositionCiNodeVfc extends CompositionCiNodeBase {
+ constructor(instance:Models.ComponentsInstances.ComponentInstance, imageCreator: Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.initVfc();
+ }
+
+ private initVfc():void {
+ this.img = this.imagesPath + Utils.Constants.ImagesUrl.RESOURCE_ICONS+ this.componentInstance.icon + '.png';
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts
new file mode 100644
index 0000000000..ed9a0d9d87
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+
+ export class CompositionCiNodeVl extends CompositionCiNodeBase {
+ private toolTipText:string;
+
+ constructor(instance:Models.ComponentsInstances.ComponentInstance, imageCreator: Utils.ImageCreatorService) {
+ super(instance, imageCreator);
+ this.initVl();
+
+ }
+
+ private initVl():void {
+ this.type = "basic-small-node";
+ this.toolTipText = 'Point to point';
+ let key:string = _.find(Object.keys(this.componentInstance.capabilities), (key)=> {
+ return _.includes(key.toLowerCase(), 'linkable');
+ });
+ let linkable = this.componentInstance.capabilities[key];
+ if (linkable) {
+ if ('UNBOUNDED' == linkable[0].maxOccurrences) {
+ this.toolTipText = 'Multi point';
+ }
+ }
+ this.img = this.imagesPath + Utils.Constants.ImagesUrl.RESOURCE_ICONS + 'vl.png';
+
+ this.classes = 'vl-node';
+ if(!this.certified) {
+ this.classes = this.classes + ' not-certified';
+ }
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/modules-graph-nodes/module-node-base.ts b/catalog-ui/app/scripts/models/graph/nodes/modules-graph-nodes/module-node-base.ts
new file mode 100644
index 0000000000..cd6ab3ba85
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/modules-graph-nodes/module-node-base.ts
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/29/2016.
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.Models.Graph {
+ 'use strict';
+
+ export interface IModuleNodeBase {
+ }
+
+ export class ModuleNodeBase extends CommonNodeBase implements IModuleNodeBase {
+
+ module:Module;
+
+ constructor(module:Module) {
+ super();
+ this.module = module;
+ this.init();
+ }
+
+ private init() {
+
+ this.id = this.module.uniqueId;
+ this.name = this.module.name;
+ this.displayName = this.module.name;
+ this.isGroup = true;
+ this.img = Utils.Constants.IMAGE_PATH + Utils.Constants.ImagesUrl.MODULE_ICON;
+ this.classes = "module-node";
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/graph/nodes/nodes-factory.ts b/catalog-ui/app/scripts/models/graph/nodes/nodes-factory.ts
new file mode 100644
index 0000000000..b19b1a7261
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/nodes/nodes-factory.ts
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.Utils {
+ 'use strict';
+
+ export class NodesFactory {
+
+ constructor(
+ private imageCreator:ImageCreatorService) {
+ }
+
+ public createNode = (instance:Models.ComponentsInstances.ComponentInstance):Models.Graph.CompositionCiNodeBase => {
+
+ if (instance.isUcpe()) {
+ return new Models.Graph.NodeUcpe(instance, this.imageCreator);
+ }
+ if (instance.originType === Utils.Constants.ComponentType.SERVICE) {
+ return new Models.Graph.CompositionCiNodeService(instance, this.imageCreator);
+ }
+ if (instance.originType === Utils.Constants.ResourceType.CP) {
+ return new Models.Graph.CompositionCiNodeCp(instance, this.imageCreator);
+ }
+ if (instance.originType === Utils.Constants.ResourceType.VL) {
+ return new Models.Graph.CompositionCiNodeVl(instance, this.imageCreator);
+ }
+
+ return new Models.Graph.CompositionCiNodeVf(instance, this.imageCreator);
+ };
+
+ public createModuleNode = (module:Models.Module):Models.Graph.ModuleNodeBase => {
+
+ return new Models.Graph.ModuleNodeBase(module);
+ };
+
+ public createUcpeCpNode = (instance:Models.ComponentsInstances.ComponentInstance):Models.Graph.CompositionCiNodeCp => {
+
+
+ return new Models.Graph.CompositionCiNodeUcpeCp(instance, this.imageCreator);
+ }
+ }
+
+ NodesFactory.$inject = [
+ 'ImageCreatorService'
+ ];
+}
diff --git a/catalog-ui/app/scripts/models/graph/point.ts b/catalog-ui/app/scripts/models/graph/point.ts
new file mode 100644
index 0000000000..0efd4c6040
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/point.ts
@@ -0,0 +1,26 @@
+/**
+ * Created by obarda on 11/7/2016.
+ */
+/// <reference path="../../references"/>
+
+module Sdc.Models.Graph {
+
+
+ export class Point {
+ /**
+ * The two-argument constructor produces the Point(x, y).
+ * @param {number} x
+ * @param {number} y
+ */
+ constructor(x?:number, y?:number) {
+ this.x = x || 0;
+ this.y = y || 0;
+ }
+
+ /**Gets or sets the x value of the Point.*/
+ x:number;
+
+ /**Gets or sets the y value of the Point.*/
+ y:number;
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/models/graph/relationMenuObjects.ts b/catalog-ui/app/scripts/models/graph/relationMenuObjects.ts
new file mode 100644
index 0000000000..266ed76cfa
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/relationMenuObjects.ts
@@ -0,0 +1,138 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+
+ export class RelationMenuDirectiveObj {
+
+ fromNode:Models.Graph.CompositionCiNodeBase;
+ toNode:Models.Graph.CompositionCiNodeBase;
+ // modelLinks:Array<Models.CompositionCiLinkBase>;
+ mp2mpVL:Models.Components.Component;
+ p2pVL:Models.Components.Component;
+ menuPosition: Cy.Position;
+ rightSideLink:GraphLinkMenuSide;
+ leftSideLink:GraphLinkMenuSide;
+ selectionText:string;
+ vlType:string;
+
+ constructor(fromNode:Models.Graph.CompositionCiNodeBase, toNode:Models.Graph.CompositionCiNodeBase, mp2mpVL:Models.Components.Component, p2pVL:Models.Components.Component, menuPosition:Cy.Position, possibleRelations:Array<Models.MatchBase>) {
+ this.fromNode = fromNode;
+ this.toNode = toNode;
+ // this.modelLinks = modelLinks;
+ this.mp2mpVL = mp2mpVL;
+ this.p2pVL = p2pVL;
+ this.menuPosition = menuPosition;
+ this.leftSideLink = new GraphLinkMenuSide(this.fromNode.componentInstance);
+ this.rightSideLink = new GraphLinkMenuSide(this.toNode.componentInstance);
+ this.selectionText = '';
+ this.vlType = null;
+
+ possibleRelations.forEach((match:any) => {
+
+ let reqObjKey: string = match.requirement.ownerName + match.requirement.uniqueId;
+ let capObjKey: string = match.secondRequirement ? match.secondRequirement.ownerName + match.secondRequirement.uniqueId
+ : match.capability.ownerName + match.capability.uniqueId;
+
+ if (match.fromNode === this.leftSideLink.componentInstance.uniqueId) {
+ //init the left side requirements Array
+ if (!this.leftSideLink.requirements[reqObjKey]) {
+ this.leftSideLink.requirements[reqObjKey] = [];
+ }
+ //push the match to fromNode object (from node is always the requirement)
+ this.leftSideLink.requirements[reqObjKey].push(match);
+
+ if (match instanceof Models.MatchReqToReq) {
+ //init the right side requirements Array
+ if (!this.rightSideLink.requirements[capObjKey]) {
+ this.rightSideLink.requirements[capObjKey] = [];
+ }
+ this.rightSideLink.requirements[capObjKey].push(match);
+ } else {
+ //init the right side capabilities Array
+ if (!this.rightSideLink.capabilities[capObjKey]) {
+ this.rightSideLink.capabilities[capObjKey] = [];
+ }
+ //add to array
+ this.rightSideLink.capabilities[capObjKey].push(match);
+ }
+
+ } else {
+ if (!this.rightSideLink.requirements[reqObjKey]) {
+ this.rightSideLink.requirements[reqObjKey] = [];
+ }
+ this.rightSideLink.requirements[reqObjKey].push(match);
+
+ if (!this.leftSideLink.capabilities[capObjKey]) {
+ this.leftSideLink.capabilities[capObjKey] = [];
+ }
+ this.leftSideLink.capabilities[capObjKey].push(match);
+ }
+ });
+
+ }
+ }
+
+
+ export class GraphLinkMenuSide {
+ public componentInstance:Models.ComponentsInstances.ComponentInstance;
+ public selectedMatch:Array<any>; //match array returned by function in utils
+ public requirements:any; //array of matches returned by function in utils
+ public capabilities:any; //array of matches returned by function in utils
+
+ constructor(componentInstance:Models.ComponentsInstances.ComponentInstance) {
+ this.componentInstance = componentInstance;
+ this.capabilities = {};
+ this.requirements = {};
+ }
+
+ public selectMatchArr(matchArr:Array<Models.MatchBase>):void {
+ if (this.selectedMatch === matchArr) {
+ this.selectedMatch = undefined;
+ } else {
+ this.selectedMatch = matchArr;
+ }
+ }
+
+
+ //TODO move to match object
+ public getPreviewText(showReq:boolean):string {
+ if (!this.selectedMatch) {
+ return '';
+ }
+
+ let match:any = this.selectedMatch[0];
+ if (showReq) {
+ return match.requirement.ownerName + ': ' + match.requirement.name +
+ ': [' + match.requirement.minOccurrences + ', ' + match.requirement.maxOccurrences + ']';
+ } else if (match.secondRequirement) {
+ return match.secondRequirement.ownerName + ': ' + match.secondRequirement.name +
+ ': [' + match.secondRequirement.minOccurrences + ', ' + match.secondRequirement.maxOccurrences + ']';
+ }
+ else {
+ return match.capability.ownerName + ': ' + match.capability.name +
+ ': [' + match.capability.minOccurrences + ', ' + match.capability.maxOccurrences + ']';
+ }
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/graph/relationship.ts b/catalog-ui/app/scripts/models/graph/relationship.ts
new file mode 100644
index 0000000000..e0dfbbd6d1
--- /dev/null
+++ b/catalog-ui/app/scripts/models/graph/relationship.ts
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class RelationshipModel {
+ fromNode:string;
+ toNode:string;
+ relationships:Array<Relationship>;
+
+ constructor(relationshipModel?:RelationshipModel, singleRelationship?:Relationship) {
+ if(relationshipModel){
+ this.fromNode = relationshipModel.fromNode;
+ this.toNode = relationshipModel.toNode;
+ this.relationships = [];
+ if (relationshipModel.relationships && !singleRelationship) {
+ _.forEach(relationshipModel.relationships, (relation:Models.Relationship):void => {
+ this.relationships.push(new Models.Relationship(relation));
+ });
+ }else if(singleRelationship){
+ this.relationships.push(singleRelationship);
+ }
+ }
+ }
+
+ public setRelationshipModelParams (fromNode: string, toNode:string, relationships:Array<Relationship>) {
+ this.fromNode = fromNode;
+ this.toNode = toNode;
+ this.relationships = relationships;
+ }
+ }
+
+ export class RelationType {
+ type:string;
+
+ constructor(type?:string) {
+ if(type){
+ this.type = type;
+ }
+ }
+ }
+
+ export class Relationship {
+ capability:string;
+ capabilityOwnerId:string;
+ capabilityUid:string;
+ relationship:RelationType;
+ requirement:string;
+ requirementOwnerId:string;
+ requirementUid:string;
+
+ constructor(relationship?:Models.Relationship) {
+ if(relationship) {
+ this.capability = relationship.capability;
+ this.capabilityOwnerId = relationship.capabilityOwnerId;
+ this.capabilityUid = relationship.capabilityUid;
+ this.relationship = new RelationType(relationship.relationship.type);
+ this.requirement = relationship.requirement;
+ this.requirementOwnerId = relationship.requirementOwnerId;
+ this.requirementUid = relationship.requirementUid;
+ } else {
+ this.relationship = new RelationType();
+ }
+
+ }
+
+ //public setRelationProperties = (capability:string, capabilityOwnerId:string, capabilityUid:string, relationship:RelationType, requirement:string, requirementOwnerId:string, requirementUid:string )=>{
+ // this.capability = capability;
+ // this.capabilityOwnerId = capabilityOwnerId;
+ // this.capabilityUid = capabilityUid;
+ // this.relationship = relationship;
+ // this.requirement =requirement;
+ // this.requirementOwnerId = requirementOwnerId;
+ // this.requirementUid = requirementUid;
+ //}
+
+
+ public setRelationProperties = (capability:Models.Capability, requirement:Models.Requirement)=>{
+ this.capability = capability.name;
+ this.capabilityOwnerId = capability.ownerId;
+ this.capabilityUid = capability.uniqueId;
+ this.relationship = new Models.RelationType(capability.type);
+ this.requirement = requirement.name;
+ this.requirementOwnerId = requirement.ownerId;
+ this.requirementUid = requirement.uniqueId;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/models/inputs.ts b/catalog-ui/app/scripts/models/inputs.ts
new file mode 100644
index 0000000000..68e26e246e
--- /dev/null
+++ b/catalog-ui/app/scripts/models/inputs.ts
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 8/24/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class InputModel {
+
+ //server data
+ uniqueId:string;
+ name:string;
+ type:string;
+ password:boolean;
+ required:boolean;
+ definition:boolean;
+ parentUniqueId:string;
+ description:string;
+ componentInstanceName:string;
+ componentInstanceId:string;
+
+ //costom properties
+ isNew: boolean;
+ properties:Array<Models.PropertyModel>;
+ inputs:Array<Models.InputModel>;
+ isAlreadySelected: boolean;
+ filterTerm: string;
+
+ constructor(input:InputModel) {
+ if (input) {
+ this.uniqueId = input.uniqueId;
+ this.name = input.name;
+ this.type = input.type;
+ this.description = input.description;
+ this.password = input.password;
+ this.required = input.required;
+ this.definition = input.definition;
+ this.parentUniqueId = input.parentUniqueId;
+ this.description = input.description;
+ this.componentInstanceName = input.componentInstanceName;
+ this.componentInstanceId = input.componentInstanceId;
+ this.filterTerm = this.name + ' ' + this.description + ' ' + this.type + ' ' + this.componentInstanceName;
+ }
+ }
+
+ public toJSON = ():any => {
+ this.isNew = undefined;
+ this.properties = undefined;
+ this.inputs = undefined;
+ this.isAlreadySelected = undefined;
+ this.filterTerm = undefined;
+ return this;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/models/instance-inputs-properties-map.ts b/catalog-ui/app/scripts/models/instance-inputs-properties-map.ts
new file mode 100644
index 0000000000..2c67dfd718
--- /dev/null
+++ b/catalog-ui/app/scripts/models/instance-inputs-properties-map.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 9/12/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class InstanceInputsPropertiesMapData {
+ [instanceId:string]: Array<PropertyModel>;
+ }
+
+ export class InstanceInputsPropertiesMap {
+ componentInstanceInputsProperties:InstanceInputsPropertiesMapData;
+
+ constructor(componentInstanceInputsPropertiesMapData:InstanceInputsPropertiesMapData) {
+ this.componentInstanceInputsProperties = componentInstanceInputsPropertiesMapData;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/instances-inputs-map.ts b/catalog-ui/app/scripts/models/instances-inputs-map.ts
new file mode 100644
index 0000000000..1643a125ae
--- /dev/null
+++ b/catalog-ui/app/scripts/models/instances-inputs-map.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 9/12/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class InstancesInputsMapData {
+ [instanceId:string]: Array<InputModel>;
+ }
+
+ export class InstancesInputsMap {
+ componentInstanceInputsMap:InstancesInputsMapData;
+
+ constructor(componentInstanceInputsMapData:InstancesInputsMapData) {
+ this.componentInstanceInputsMap = componentInstanceInputsMapData;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/models/left-panel.ts b/catalog-ui/app/scripts/models/left-panel.ts
new file mode 100644
index 0000000000..a47170c7c2
--- /dev/null
+++ b/catalog-ui/app/scripts/models/left-panel.ts
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class LeftPanelModel {
+ numberOfElements:number;
+ sortedCategories:any;
+
+ constructor() {
+ this.numberOfElements = 0;
+ this.sortedCategories = {};
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/member.ts b/catalog-ui/app/scripts/models/member.ts
new file mode 100644
index 0000000000..21dc907333
--- /dev/null
+++ b/catalog-ui/app/scripts/models/member.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 8/2/2016.
+ */
+
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class Members {
+
+ [index: string]: string;
+
+ constructor(members?:Members) {
+ _.forEach(members, (memberId:string, index) => {
+ this[index] = memberId;
+ });
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/models/modules/base-module.ts b/catalog-ui/app/scripts/models/modules/base-module.ts
new file mode 100644
index 0000000000..2df52cc907
--- /dev/null
+++ b/catalog-ui/app/scripts/models/modules/base-module.ts
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class Module {
+
+ public name:string;
+ public groupUUID:string;
+ public invariantUUID:string;
+ public propertyValueCounter:number;
+ public type:string;
+ public typeUid:string;
+ public uniqueId:string;
+ public version: string;
+ public artifacts: Array<string> | Array<Models.ArtifactModel>;
+ public artifactsUuid: Array<string>;
+ public properties: Array<Models.PropertyModel>;
+ public members: Array<string>;
+
+
+ constructor(module?: Module) {
+ if(module) {
+ this.name = module.name;
+ this.groupUUID = module.groupUUID;
+ this.invariantUUID = module.invariantUUID;
+ this.propertyValueCounter = module.propertyValueCounter;
+ this.type = module.type;
+ this.typeUid = module.typeUid;
+ this.uniqueId = module.uniqueId;
+ this.version = module.version;
+ this.artifacts = module.artifacts;
+ this.artifactsUuid = module.artifactsUuid;
+ this.properties = Utils.CommonUtils.initProperties(module.properties);
+ this.members = module.members;
+
+ this.name = this.name.replace(/:/g, '..');
+
+ }
+ }
+ }
+
+ export class DisplayModule extends Module {
+
+ isBase: string;
+ artifacts:Array<Models.ArtifactModel>;
+
+ //custom properties
+ public vfInstanceName: string;
+ public heatName: string;
+ public moduleName: string;
+
+ constructor(displayModule?:Models.DisplayModule) {
+ super(displayModule);
+
+ this.isBase = displayModule.isBase;
+ this.initArtifactsForDisplay(displayModule.artifacts);
+
+ //splitting module name for display and edit
+ let splitName:Array<string> = this.name.split('..');
+ this.vfInstanceName = splitName[0];
+ this.heatName = splitName[1];
+ this.moduleName = splitName[2];
+ }
+
+ private initArtifactsForDisplay = (artifacts:Array<Models.ArtifactModel>):void => {
+ this.artifacts = new Array<Models.ArtifactModel>();
+ _.forEach(artifacts, (artifact:Models.ArtifactModel) => {
+ this.artifacts.push(new Models.ArtifactModel(artifact));
+ });
+ };
+
+ public updateName = ():void => {
+ this.name = this.vfInstanceName + '..' + this.heatName + '..' + this.moduleName;
+ };
+
+ public toJSON = ():any => {
+ this.vfInstanceName = undefined;
+ this.heatName = undefined;
+ this.moduleName = undefined;
+ this.isBase = undefined;
+ this.artifacts = undefined;
+ return this;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/models/properties.ts b/catalog-ui/app/scripts/models/properties.ts
new file mode 100644
index 0000000000..679ca03b44
--- /dev/null
+++ b/catalog-ui/app/scripts/models/properties.ts
@@ -0,0 +1,176 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class PropertiesGroup {
+ constructor(propertiesObj?:Models.PropertiesGroup){
+ _.forEach(propertiesObj, (properties:Array<Models.PropertyModel>, instance) => {
+ this[instance] = [];
+ _.forEach(properties, (property:Models.PropertyModel):void => {
+ property.resourceInstanceUniqueId = instance;
+ property.readonly = true;
+ this[instance].push(new Models.PropertyModel(property));
+ });
+ });
+ }
+ }
+
+ export interface IPropertyModel {
+
+ //server data
+ uniqueId: string;
+ name: string;
+ constraints: Array<Object>;
+ defaultValue: string;
+ description: string;
+ password: boolean;
+ required: boolean;
+ type: string;
+ source: string;
+ parentUniqueId: string;
+ schema: Models.SchemaPropertyGroupModel;
+
+ //instance properties
+ value:string;
+ valueUniqueUid:string;
+ path:Array<string>;
+ rules:Array<Object>;
+
+ //custom properties
+ resourceInstanceUniqueId: string;
+ readonly: boolean;
+ simpleType: string;
+ }
+
+ export class PropertyModel implements IPropertyModel{
+
+ //server data
+ uniqueId:string;
+ name:string;
+ constraints:Array<Object>;
+ defaultValue:string;
+ description:string;
+ password:boolean;
+ required:boolean;
+ type:string;
+ source:string;
+ parentUniqueId:string;
+ schema: Models.SchemaPropertyGroupModel;
+
+ //instance properties
+ value:string;
+ valueUniqueUid:string;
+ path:Array<string>;
+ rules:Array<Object>;
+
+ //custom properties
+ resourceInstanceUniqueId:string;
+ readonly:boolean;
+ simpleType: string;
+ filterTerm: string;
+ isAlreadySelected: boolean;
+
+ constructor(property?:Models.PropertyModel) {
+ if (property) {
+ this.uniqueId = property.uniqueId;
+ this.name = property.name;
+ this.constraints = property.constraints;
+ this.defaultValue = property.defaultValue;
+ this.description = property.description;
+ this.password = property.password;
+ this.required = property.required;
+ this.type = property.type;
+ this.source = property.source;
+ this.parentUniqueId = property.parentUniqueId;
+ this.schema = property.schema;
+ this.value = property.value?property.value:property.defaultValue;
+ this.valueUniqueUid = property.valueUniqueUid;
+ this.path = property.path;
+ this.rules = property.rules;
+ this.resourceInstanceUniqueId = property.resourceInstanceUniqueId;
+ this.readonly = property.readonly;
+ this.simpleType = property.simpleType;
+
+
+ }
+
+ if(!this.schema || !this.schema.property) {
+ this.schema = new Models.SchemaPropertyGroupModel(new Models.SchemaProperty());
+ } else {
+ //forcing creating new object, so editing different one than the object in the table
+ this.schema = new Models.SchemaPropertyGroupModel(new Models.SchemaProperty(this.schema.property));
+ }
+ if(property) {
+ this.filterTerm = this.name + " " + (this.description||"") +" " + this.type;
+ if(this.schema.property && this.schema.property.type) {
+ this.filterTerm += " " +this.schema.property.type;
+ }
+ }
+ }
+
+ public convertToServerObject:Function = ():string => {
+ let serverObject = {};
+ let mapData = {
+ "type": this.type,
+ "required": this.required || false,
+ "defaultValue": this.defaultValue != "" && this.defaultValue != "[]" && this.defaultValue != "{}" ? this.defaultValue :null,
+ "description": this.description,
+ "constraints": this.constraints,
+ "isPassword": this.password || false,
+ "schema": this.schema,
+ "name": this.name
+ };
+ serverObject[this.name] = mapData;
+
+ return JSON.stringify(serverObject);
+ };
+
+
+ // public convertValueToView () {
+ // //unwrapping value {} or [] if type is complex
+ // if (this.defaultValue && (this.type === 'map' || this.type === 'list') &&
+ // ['[','{'].indexOf(this.defaultValue.charAt(0)) > -1 &&
+ // [']','}'].indexOf(this.defaultValue.slice(-1)) > -1) {
+ // this.defaultValue = this.defaultValue.slice(1, -1);
+ // }
+ //
+ // //also for value - for the modal in canvas
+ // if (this.value && (this.type === 'map' || this.type === 'list') &&
+ // ['[','{'].indexOf(this.value.charAt(0)) > -1 &&
+ // [']','}'].indexOf(this.value.slice(-1)) > -1) {
+ // this.value = this.value.slice(1, -1);
+ // }
+ // }
+
+ public toJSON = ():any => {
+ if(!this.resourceInstanceUniqueId){
+ this.value = undefined;
+ }
+ this.readonly = undefined;
+ this.resourceInstanceUniqueId = undefined;
+ this.simpleType = undefined;
+ this.value = this.value === "{}" || this.value === "[]" ? undefined: this.value;
+ this.defaultValue = this.defaultValue === "{}" || this.defaultValue === "[]" ? undefined: this.defaultValue;
+ return this;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/models/requirement.ts b/catalog-ui/app/scripts/models/requirement.ts
new file mode 100644
index 0000000000..091bfc139e
--- /dev/null
+++ b/catalog-ui/app/scripts/models/requirement.ts
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+ /*
+ * Created by obarda on 4/20/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+ //this is an object contains keys, when each key has matching array.
+ // for example: key = tosca.capabilities.network. and the match array is array of requirements objects
+ export class RequirementsGroup{
+ constructor(requirementGroupObj?:Models.RequirementsGroup){
+ _.forEach(requirementGroupObj, (requirementsArrayObj:Array<Models.Requirement>, instance) => {
+ this[instance] = [];
+ _.forEach(requirementsArrayObj, (requirement:Models.Requirement):void => {
+ this[instance].push(new Models.Requirement(requirement));
+ });
+ });
+ }
+ }
+
+ export class Requirement {
+
+ //server data
+ capability:string;
+ name: string;
+ ownerId: string;
+ ownerName:string;
+ node:string;
+ uniqueId:string;
+ relationship: string;
+ minOccurrences: string;
+ maxOccurrences: string;
+ //custom
+ filterTerm:string;
+ constructor(requirement?:Requirement) {
+
+ if(requirement) {
+ this.capability = requirement.capability;
+ this.name = requirement.name;
+ this.ownerId = requirement.ownerId;
+ this.ownerName = requirement.ownerName;
+ this.node = requirement.node;
+ this.uniqueId = requirement.uniqueId;
+ this.relationship = requirement.relationship;
+ this.minOccurrences = requirement.minOccurrences;
+ this.maxOccurrences = requirement.maxOccurrences;
+ this.initFilterTerm();
+
+ }
+ }
+
+ public getFullTitle():string {
+ return this.ownerName + ': ' + this.name +
+ ': [' + this.minOccurrences + ', ' + this.maxOccurrences + ']';
+ }
+
+ public toJSON = ():any => {
+ this.filterTerm = undefined;
+ return this;
+ };
+
+ private initFilterTerm = ():void =>{
+ this.filterTerm = (this.name + " ") +
+ (this.ownerName + " " ) +
+ (this.capability ? (this.capability.substring("tosca.capabilities.".length) + " " ) : "") +
+ (this.node? (this.node.substring("tosca.nodes.".length) +" ") : "") +
+ (this.relationship? (this.relationship.substring("tosca.relationships.".length) +" ") : "") +
+ this.minOccurrences+","+this.maxOccurrences;
+ }
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/schema-attribute.ts b/catalog-ui/app/scripts/models/schema-attribute.ts
new file mode 100644
index 0000000000..725a7589e0
--- /dev/null
+++ b/catalog-ui/app/scripts/models/schema-attribute.ts
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class SchemaAttributeGroupModel{
+ property: SchemaAttribute;
+
+ constructor(schemaAttribute?:Models.SchemaAttribute) {
+ this.property = schemaAttribute;
+ }
+ }
+
+ export class SchemaAttribute extends SchemaProperty{
+
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/tab.ts b/catalog-ui/app/scripts/models/tab.ts
new file mode 100644
index 0000000000..cc42d4f348
--- /dev/null
+++ b/catalog-ui/app/scripts/models/tab.ts
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 7/31/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class Tab {
+
+ public templateUrl:string;
+ public controller:string;
+ public data:any;
+ public icon:string;
+ public name:string;
+
+ constructor(templateUrl:string, controller:string, name:string, data?:any, icon?:string) {
+
+ this.templateUrl = templateUrl;
+ this.controller = controller;
+ this.icon = icon;
+ this.data = data;
+ this.name = name;
+ }
+ }
+}
+
+
+
diff --git a/catalog-ui/app/scripts/models/tooltip-data.ts b/catalog-ui/app/scripts/models/tooltip-data.ts
new file mode 100644
index 0000000000..027904b245
--- /dev/null
+++ b/catalog-ui/app/scripts/models/tooltip-data.ts
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export class TooltipData{
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/models/user.ts b/catalog-ui/app/scripts/models/user.ts
new file mode 100644
index 0000000000..836066f5f9
--- /dev/null
+++ b/catalog-ui/app/scripts/models/user.ts
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export enum UserRole {
+ ADMIN,
+ DESIGNER,
+ TESTER,
+ GOVERNOR,
+ OPS,
+ PRODUCT_MANAGER,
+ PRODUCT_STRATEGIST
+ }
+
+ export interface IUserManager {
+ isInEditMode: boolean;
+ filterTerm: string;
+ }
+
+ export interface IUserProperties extends IUserManager{
+ firstName: string;
+ lastName: string;
+ userId: string;
+ email: string;
+ role: string;
+ tempRole:string;
+ lastLoginTime: string;
+ status:string;
+ }
+
+ export interface IUser {
+ resource: Services.IUserResource;
+ getRole(): UserRole;
+ getRoleToView(): string;
+ getName(): string;
+ getFirstName(): string;
+ getLastName(): string;
+ }
+
+ export class User implements IUser {
+
+ constructor(public resource:Services.IUserResource) {
+ }
+
+ public getLastName = () => {
+ return this.resource.lastName;
+ }
+
+ public getFirstName = () => {
+ return this.resource.firstName;
+ }
+
+ public getName = () => {
+ return this.resource.firstName + ' ' + this.resource.lastName;
+ }
+
+ public getLastLogin = () => {
+ if (!this.resource.lastLoginTime || this.resource.lastLoginTime === "0") {
+ return "";
+ } else {
+ return this.resource.lastLoginTime;
+ }
+ }
+
+ public getRole = ():UserRole => {
+ let role:UserRole;
+ switch (UserRole[this.resource.role.toUpperCase()]) {
+ case UserRole.ADMIN:
+ role = UserRole.ADMIN;
+ break;
+ case UserRole.DESIGNER:
+ role = UserRole.DESIGNER;
+ break;
+ case UserRole.TESTER:
+ role = UserRole.TESTER;
+ break;
+ case UserRole.GOVERNOR:
+ role = UserRole.GOVERNOR;
+ break;
+ case UserRole.OPS:
+ role = UserRole.OPS;
+ break;
+ case UserRole.PRODUCT_MANAGER:
+ role = UserRole.PRODUCT_MANAGER;
+ break;
+ case UserRole.PRODUCT_STRATEGIST:
+ role = UserRole.PRODUCT_STRATEGIST;
+ break;
+ }
+ return role;
+ }
+
+ public getRoleToView = ():string => {
+ let role:string = this.resource.role.toLowerCase().replace('governor','governance_Rep');
+ return role.charAt(0).toUpperCase() + role.slice(1).replace('_',' ');
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/models/validate.ts b/catalog-ui/app/scripts/models/validate.ts
new file mode 100644
index 0000000000..21540d38b6
--- /dev/null
+++ b/catalog-ui/app/scripts/models/validate.ts
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Models {
+ 'use strict';
+
+ export interface IValidate{
+ isValid : boolean;
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/modules/directive-module.ts b/catalog-ui/app/scripts/modules/directive-module.ts
new file mode 100644
index 0000000000..70a15378fa
--- /dev/null
+++ b/catalog-ui/app/scripts/modules/directive-module.ts
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc {
+ let moduleName:string = 'Sdc.Directives';
+ let directiveModule:ng.IModule = angular.module(moduleName, []);
+
+ directiveModule.directive('clickedOutside', Directives.ClickedOutsideDirective.factory);
+ directiveModule.directive('loader', Directives.LoaderDirective.factory);
+ directiveModule.directive('userHeaderDetails', Directives.UserHeaderDetailsDirective.factory);
+ directiveModule.directive('ellipsis', Directives.EllipsisDirective.factory);
+ directiveModule.directive('downloadArtifact', Directives.DownloadArtifactDirective.factory);
+ directiveModule.directive('fileType', Directives.FileTypeDirective.factory);
+ directiveModule.directive('invalidCharacters', Directives.InvalidCharactersDirective.factory);
+ directiveModule.directive('tutorial', Directives.TutorialDirective.factory);
+ directiveModule.directive('perfectScrollbar', Directives.PerfectScrollerDirective.factory);
+ directiveModule.directive('expandCollapse', Directives.ExpandCollapseDirective.factory);
+ directiveModule.directive('sdcModal', Directives.SdcModalDirective.factory);
+ directiveModule.directive('sdcMessages', Directives.SdcMessagesDirective.factory);
+ directiveModule.directive('sdcMessage', Directives.SdcMessageDirective.factory);
+ directiveModule.directive('sdcErrorTooltip', Directives.SdcErrorTooltipDirective.factory);
+ directiveModule.directive('fileOpener', Directives.FileOpenerDirective.factory);
+ directiveModule.directive('fileUpload', Directives.FileUploadDirective.factory);
+ directiveModule.directive('structureTree', Directives.StructureTreeDirective.factory);
+ directiveModule.directive('sdcWizardStep', Directives.SdcWizardStepDirective.factory);
+ directiveModule.directive('sdcPageSelector', Directives.PageSelectorDirective.factory);
+ directiveModule.directive('sdcSmartTooltip', Directives.SmartTooltipDirective.factory);
+ directiveModule.directive('printGraphScreen', Directives.PrintGraphScreenDirective.factory);
+ directiveModule.directive('sdcTag', Directives.TagDirective.factory);
+ directiveModule.directive('sdcTags', Directives.SdcTagsDirective.factory);
+ directiveModule.directive('sdcKeyboardEvents', Directives.SdcKeyboardEventsDirective.factory);
+ directiveModule.directive('expandCollapseMenuBox', Directives.ExpandCollapseMenuBoxDirective.factory);
+ directiveModule.directive('sdcPageScroll', Directives.SdcPageScrollDirective.factory);
+ directiveModule.directive('punchOut', Directives.PunchOutDirective.factory);
+ directiveModule.directive('relationMenu', Directives.RelationMenuDirective.factory);
+ directiveModule.directive('customValidation', Directives.CustomValidationDirective.factory);
+ directiveModule.directive('ecompHeader', Directives.EcompHeaderDirective.factory);
+ directiveModule.directive('editNamePopover', Directives.EditNamePopoverDirective.factory);
+ directiveModule.directive('fieldsStructure', Directives.DataTypeFieldsStructureDirective.factory);
+ directiveModule.directive('typeMap', Directives.TypeMapDirective.factory);
+ directiveModule.directive('typeList', Directives.TypeListDirective.factory);
+ directiveModule.directive('infoTooltip', Directives.InfoTooltipDirective.factory);
+
+ directiveModule.directive('sdcTabs', Directives.SdcTabsDirective.factory);
+ directiveModule.directive('sdcSingleTab', Directives.SdcSingleTabDirective.factory);
+ directiveModule.directive('innerSdcSingleTab', Directives.InnerSdcSingleTabDirective.factory);
+
+ //composition
+ directiveModule.directive('palette', Directives.Palette.factory);
+ directiveModule.directive('compositionGraph', Directives.CompositionGraph.factory);
+
+ //deployment
+ directiveModule.directive('deploymentGraph', Directives.DeploymentGraph.factory);
+
+ // Layouts
+ directiveModule.directive('topNav', Directives.TopNavDirective.factory);
+ directiveModule.directive('topProgress', Directives.TopProgressDirective.factory);
+
+ // Elements
+ directiveModule.directive('sdcCheckbox', Directives.CheckboxElementDirective.factory);
+ directiveModule.directive('sdcRadioButton', Directives.RadiobuttonElementDirective.factory);
+
+ //Graph Utils - Common
+ directiveModule.service('CommonGraphUtils', Sdc.Graph.Utils.CommonGraphUtils);
+
+ //Composition Graph Utils
+ directiveModule.service('CompositionGraphNodesUtils', Sdc.Graph.Utils.CompositionGraphNodesUtils);
+ directiveModule.service('CompositionGraphGeneralUtils', Sdc.Graph.Utils.CompositionGraphGeneralUtils);
+ directiveModule.service('CompositionGraphLinkUtils', Sdc.Graph.Utils.CompositionGraphLinkUtils);
+ directiveModule.service('MatchCapabilitiesRequirementsUtils', Sdc.Graph.Utils.MatchCapabilitiesRequirementsUtils);
+
+ //Composition Graph Utils
+ directiveModule.service('DeploymentGraphGeneralUtils', Sdc.Graph.Utils.DeploymentGraphGeneralUtils);
+
+ //Util service for graph
+ directiveModule.service('NodesFactory', Sdc.Utils.NodesFactory);
+ directiveModule.service('LinksFactory', Sdc.Utils.LinksFactory);
+ directiveModule.service('ImageCreatorService', Sdc.Utils.ImageCreatorService);
+
+ //directiveModule.service('GraphUtilsServerUpdateQueue', Sdc.Directives.GraphUtilsServerUpdateQueue);
+
+ //controller for go.js
+ directiveModule.controller('SdcWizardStepDirectiveController', Directives.SdcWizardStepDirectiveController);
+
+ // Events
+ directiveModule.directive('onLastRepeat', Directives.OnLastRepeatDirective.factory);
+}
+
+
diff --git a/catalog-ui/app/scripts/modules/filters.ts b/catalog-ui/app/scripts/modules/filters.ts
new file mode 100644
index 0000000000..1bf31507fd
--- /dev/null
+++ b/catalog-ui/app/scripts/modules/filters.ts
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc {
+
+ let moduleName: string = 'Sdc.Filters';
+ let filterModule: ng.IModule = angular.module(moduleName, []);
+ filterModule.filter("resourceName", Sdc.Filters.ResourceNameFilter);
+ filterModule.filter("graphResourceName", Sdc.Filters.GraphResourceNameFilter);
+ filterModule.filter("categoryNameFilter", Sdc.Filters.CategoryNameFilter);
+ filterModule.filter("entityFilter", Sdc.Filters.EntityFilter);
+ filterModule.filter("truncate", Sdc.Filters.TruncateFilter);
+ filterModule.filter("catalogStatusFilter", Sdc.Filters.CatalogStatusFilter);
+ filterModule.filter("categoryTypeFilter", Sdc.Filters.CategoryTypeFilter);
+ filterModule.filter("stringToDateFilter", Sdc.Filters.StringToDateFilter);
+ filterModule.filter("categoryIcon", Sdc.Filters.CategoryIconFilter);
+ filterModule.filter("capitalizeFilter", Sdc.Filters.CapitalizeFilter);
+ filterModule.filter("underscoreLessFilter", Sdc.Filters.UnderscoreLessFilter);
+ filterModule.filter("resourceTypeName", Sdc.Filters.ResourceTypeFilter);
+ filterModule.filter("relationName", Sdc.Filters.RelationNameFilter);
+ filterModule.filter("trim", Sdc.Filters.TrimFilter);
+ filterModule.filter("clearWhiteSpaces", Sdc.Filters.ClearWhiteSpacesFilter);
+ filterModule.filter('testsId', Sdc.Filters.TestsIdFilter);
+
+ //Added by Ikram
+ filterModule.filter("productCategoryNameFilter", Sdc.Filters.ProductCategoryNameFilter);
+}
diff --git a/catalog-ui/app/scripts/modules/service-module.ts b/catalog-ui/app/scripts/modules/service-module.ts
new file mode 100644
index 0000000000..c77e8b0ad4
--- /dev/null
+++ b/catalog-ui/app/scripts/modules/service-module.ts
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc {
+ let moduleName:string = 'Sdc.Services';
+ let serviceModule:ng.IModule = angular.module(moduleName, []);
+
+ serviceModule.service('Sdc.Services.ConfigurationUiService', Services.ConfigurationUiService);
+ serviceModule.service('Sdc.Services.CookieService', Services.CookieService);
+ serviceModule.service('Sdc.Services.EntityService', Services.EntityService);
+ serviceModule.service('Sdc.Services.AvailableIconsService', Services.AvailableIconsService);
+ serviceModule.service('Sdc.Services.RelationIconsService', Services.RelationIconsService);
+ serviceModule.service('Sdc.Services.UrlToBase64Service', Services.UrlToBase64Service);
+ serviceModule.service('Sdc.Services.CacheService', Services.CacheService);
+ serviceModule.service('Sdc.Services.HeaderInterceptor', Services.HeaderInterceptor);
+ serviceModule.service('Sdc.Services.HttpErrorInterceptor', Services.HttpErrorInterceptor);
+ serviceModule.service('Sdc.Services.SharingService', Services.SharingService);
+ serviceModule.service('Sdc.Services.SdcVersionService', Services.SdcVersionService);
+ serviceModule.service('Sdc.Services.ActivityLogService', Services.ActivityLogService);
+ serviceModule.service('Sdc.Services.OnboardingService', Services.OnboardingService);
+ serviceModule.service('Sdc.Services.EcompHeaderService', Services.EcompHeaderService);
+ serviceModule.service('Sdc.Services.DataTypesService', Services.DataTypesService);
+
+ //Components Services
+ serviceModule.service('Sdc.Services.Components.ComponentService', Services.Components.ComponentService);
+ serviceModule.service('Sdc.Services.Components.ServiceService', Services.Components.ServiceService);
+ serviceModule.service('Sdc.Services.Components.ResourceService', Services.Components.ResourceService);
+ serviceModule.service('Sdc.Services.Components.ProductService', Services.Components.ProductService);
+ serviceModule.service('LeftPaletteLoaderService', Services.Components.LeftPaletteLoaderService);
+ serviceModule.service('EventListenerService', Services.EventListenerService);
+ serviceModule.service('Sdc.Services.ProgressService', Services.ProgressService);
+
+ //Utils
+ serviceModule.service('ArtifactsUtils', Sdc.Utils.ArtifactsUtils);
+ serviceModule.service('FileUtils', Sdc.Utils.FileUtils);
+ serviceModule.service('ValidationUtils', Sdc.Utils.ValidationUtils);
+
+
+
+
+ serviceModule.service('AngularJSBridge', Sdc.Services.AngularJSBridge);
+ serviceModule.service('LoaderService', Sdc.Services.LoaderService);
+
+ serviceModule.factory('Sdc.Services.UserResourceService', Services.UserResourceService.getResource);
+ serviceModule.factory('Sdc.Services.CategoryResourceService', Services.CategoryResourceService.getResource);
+
+}
diff --git a/catalog-ui/app/scripts/modules/utils.ts b/catalog-ui/app/scripts/modules/utils.ts
new file mode 100644
index 0000000000..fd5eaf2a4b
--- /dev/null
+++ b/catalog-ui/app/scripts/modules/utils.ts
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/11/2016.
+ */
+/// <reference path="../references"/>
+module Sdc {
+ let moduleName:string = 'Sdc.Utils';
+ let serviceModule:ng.IModule = angular.module(moduleName, []);
+
+
+
+ //Utils
+ serviceModule.service('ComponentFactory', Sdc.Utils.ComponentFactory);
+ serviceModule.service('ComponentInstanceFactory', Sdc.Utils.ComponentInstanceFactory);
+ serviceModule.service('ChangeLifecycleStateHandler', Sdc.Utils.ChangeLifecycleStateHandler);
+ serviceModule.service('ModalsHandler', Sdc.Utils.ModalsHandler);
+ serviceModule.service('MenuHandler', Sdc.Utils.MenuHandler);
+
+
+
+}
diff --git a/catalog-ui/app/scripts/modules/view-model-module.ts b/catalog-ui/app/scripts/modules/view-model-module.ts
new file mode 100644
index 0000000000..19cf5b45f4
--- /dev/null
+++ b/catalog-ui/app/scripts/modules/view-model-module.ts
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc {
+ let moduleName: string = 'Sdc.ViewModels';
+ let viewModelModule: ng.IModule = angular.module(moduleName, []);
+
+ viewModelModule
+ .controller(moduleName+'.DashboardViewModel', ViewModels.DashboardViewModel)
+ .controller(moduleName+'.CompositionViewModel', ViewModels.CompositionViewModel)
+
+ .controller(moduleName+'.DetailsViewModel', ViewModels.DetailsViewModel)
+ .controller(moduleName+'.ResourceArtifactsViewModel', ViewModels.ResourceArtifactsViewModel)
+ .controller(moduleName+'.PropertyFormViewModel', ViewModels.PropertyFormViewModel)
+ .controller(moduleName+'.ArtifactResourceFormViewModel', ViewModels.ArtifactResourceFormViewModel)
+ .controller(moduleName+'.AttributeFormViewModel', ViewModels.AttributeFormViewModel)
+ .controller(moduleName+'.ResourcePropertiesViewModel', ViewModels.ResourcePropertiesViewModel)
+ .controller(moduleName+'.CatalogViewModel', ViewModels.CatalogViewModel)
+ .controller(moduleName+'.OnboardVendorViewModel', ViewModels.OnboardVendorViewModel)
+ .controller(moduleName+'.DistributionViewModel', ViewModels.DistributionViewModel)
+ .controller(moduleName+'.SupportViewModel', ViewModels.SupportViewModel)
+ .controller(moduleName+'.ConfirmationModalViewModel', ViewModels.ConfirmationModalViewModel)
+ .controller(moduleName+'.EmailModalViewModel', ViewModels.EmailModalViewModel)
+ .controller(moduleName+'.MessageModalViewModel', ViewModels.MessageModalViewModel)
+ .controller(moduleName+'.ServerMessageModalViewModel', ViewModels.ServerMessageModalViewModel)
+ .controller(moduleName+'.ClientMessageModalViewModel', ViewModels.ClientMessageModalViewModel)
+ .controller(moduleName+'.ErrorViewModel', ViewModels.ErrorViewModel)
+ .controller(moduleName+'.ComponentViewerViewModel', ViewModels.ComponentViewerViewModel)
+ .controller(moduleName+'.RelationsViewModel', ViewModels.RelationsViewModel)
+ .controller(moduleName+'.ResourceInstanceNameViewModel', ViewModels.ResourceInstanceNameViewModel)
+ .controller(moduleName+'.WelcomeViewModel', ViewModels.WelcomeViewModel)
+ .controller(moduleName+'.PreLoadingViewModel', ViewModels.PreLoadingViewModel)
+ .controller(moduleName+'.TutorialEndViewModel', ViewModels.TutorialEndViewModel)
+ .controller(moduleName+'.AdminDashboardViewModel', ViewModels.AdminDashboardViewModel)
+ .controller(moduleName+'.EnvParametersFormViewModel', ViewModels.EnvParametersFormViewModel)
+ .controller(moduleName+'.StructureViewModel', ViewModels.StructureViewModel)
+ .controller(moduleName+'.AddCategoryModalViewModel', ViewModels.AddCategoryModalViewModel)
+ .controller(moduleName+'.DashboardCoverViewModel', ViewModels.DashboardCoverViewModel)
+ .controller(moduleName+'.UserManagementViewModel', ViewModels.UserManagementViewModel)
+ .controller(moduleName+'.CategoryManagementViewModel', ViewModels.CategoryManagementViewModel)
+ .controller(moduleName+'.WelcomeStepsControllerViewModel', ViewModels.WelcomeStepsControllerViewModel)
+ .controller(moduleName+'.OnboardingModalViewModel', ViewModels.OnboardingModalViewModel)
+ .controller(moduleName+'.DistributionStatusModalViewModel', ViewModels.DistributionStatusModalViewModel)
+
+ .controller(moduleName+'.Wizard.EditWizardViewModel', ViewModels.Wizard.EditWizardViewModel)
+ .controller(moduleName+'.Wizard.CreateWizardViewModel', ViewModels.Wizard.CreateWizardViewModel)
+ .controller(moduleName+'.Wizard.ImportWizardViewModel', ViewModels.Wizard.ImportWizardViewModel)
+ .controller(moduleName+'.Wizard.GeneralStepViewModel', ViewModels.Wizard.GeneralStepViewModel)
+ .controller(moduleName+'.Wizard.IconsStepViewModel', ViewModels.Wizard.IconsStepViewModel)
+ .controller(moduleName+'.Wizard.ArtifactInformationStepViewModel', ViewModels.Wizard.ArtifactInformationStepViewModel)
+ .controller(moduleName+'.Wizard.ArtifactDeploymentStepViewModel', ViewModels.Wizard.ArtifactDeploymentStepViewModel)
+ .controller(moduleName+'.Wizard.PropertiesStepViewModel', ViewModels.Wizard.PropertiesStepViewModel)
+ .controller(moduleName+'.Wizard.ArtifactResourceFormStepViewModel', ViewModels.Wizard.ArtifactResourceFormStepViewModel)
+ .controller(moduleName+'.Wizard.PropertyFormViewModel', ViewModels.Wizard.PropertyFormViewModel)
+ .controller(moduleName+'.Wizard.HierarchyStepViewModel',ViewModels.Wizard.HierarchyStepViewModel)
+
+ //NEW
+ .controller(moduleName+'.WorkspaceViewModel', ViewModels.WorkspaceViewModel)
+ .controller(moduleName+'.GeneralViewModel', ViewModels.GeneralViewModel)
+ .controller(moduleName+'.IconsViewModel', ViewModels.IconsViewModel)
+ .controller(moduleName+'.DeploymentArtifactsViewModel', ViewModels.DeploymentArtifactsViewModel)
+ .controller(moduleName+'.InformationArtifactsViewModel', ViewModels.InformationArtifactsViewModel)
+ .controller(moduleName+'.ToscaArtifactsViewModel', ViewModels.ToscaArtifactsViewModel)
+ .controller(moduleName+'.PropertiesViewModel', ViewModels.PropertiesViewModel)
+ .controller(moduleName+'.AttributesViewModel', ViewModels.AttributesViewModel)
+ .controller(moduleName+'.ProductHierarchyViewModel',ViewModels.ProductHierarchyViewModel)
+ .controller(moduleName+'.ActivityLogViewModel',ViewModels.ActivityLogViewModel)
+ .controller(moduleName+'.ManagementWorkflowViewModel',ViewModels.ManagementWorkflowViewModel)
+ .controller(moduleName+'.NetworkCallFlowViewModel',ViewModels.NetworkCallFlowViewModel)
+ .controller(moduleName+'.DeploymentViewModel',ViewModels.DeploymentViewModel)
+ .controller(moduleName+'.ResourceInputsViewModel',ViewModels.ResourceInputsViewModel)
+ .controller(moduleName+'.ServiceInputsViewModel', ViewModels.ServiceInputsViewModel)
+ .controller(moduleName+'.ReqAndCapabilitiesViewModel', ViewModels.ReqAndCapabilitiesViewModel)
+
+
+
+ //TABS
+ .controller(moduleName+'.HierarchyViewModel',ViewModels.HierarchyViewModel)
+}
diff --git a/catalog-ui/app/scripts/services/activity-log-service.ts b/catalog-ui/app/scripts/services/activity-log-service.ts
new file mode 100644
index 0000000000..6fe27c447e
--- /dev/null
+++ b/catalog-ui/app/scripts/services/activity-log-service.ts
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ // Define an interface of the object you want to use, providing it's properties
+ export interface IActivityLogService{
+ getActivityLogService(type :string, id: string): ng.IPromise<Array<Models.Activity>>;
+ }
+
+ export class ActivityLogService implements IActivityLogService{
+
+
+ static '$inject' = ['$http', '$q','sdcConfig'];
+ private api: Models.IApi;
+
+ constructor(private $http: ng.IHttpService, private $q: ng.IQService, sdcConfig: Models.IAppConfigurtaion){
+ this.api = sdcConfig.api;
+ }
+
+ getActivityLogService = (type:string, id:string): ng.IPromise<Array<Models.Activity>> =>{
+ let defer = this.$q.defer<any>();
+ this.$http.get(this.api.root + this.api.GET_activity_log.replace(':type', type).replace(':id', id))
+ .success((activityLog: any) => {
+ defer.resolve(activityLog);
+ });
+ return defer.promise;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/services/angular-js-bridge-service.ts b/catalog-ui/app/scripts/services/angular-js-bridge-service.ts
new file mode 100644
index 0000000000..2d8fb01b13
--- /dev/null
+++ b/catalog-ui/app/scripts/services/angular-js-bridge-service.ts
@@ -0,0 +1,22 @@
+module Sdc.Services {
+ export class AngularJSBridge{
+ private static _$filter: ng.IFilterService;
+ private static _sdcConfig: Models.IAppConfigurtaion;
+
+ public static getFilter(filterName: string){
+ return AngularJSBridge._$filter(filterName);
+ }
+
+ public static getAngularConfig(){
+ return AngularJSBridge._sdcConfig;
+ }
+
+
+ constructor($filter: ng.IFilterService, sdcConfig: Models.IAppConfigurtaion){
+ AngularJSBridge._$filter = $filter;
+ AngularJSBridge._sdcConfig = sdcConfig;
+ }
+ }
+
+ AngularJSBridge.$inject = ['$filter', 'sdcConfig']
+} \ No newline at end of file
diff --git a/catalog-ui/app/scripts/services/available-icons-service.ts b/catalog-ui/app/scripts/services/available-icons-service.ts
new file mode 100644
index 0000000000..5c20afe5f2
--- /dev/null
+++ b/catalog-ui/app/scripts/services/available-icons-service.ts
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/23/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ interface IAvailableIconsService {
+ getIcons(componentType: Utils.Constants.ComponentType):Array<string>;
+ }
+
+ export class AvailableIconsService implements IAvailableIconsService {
+ constructor() {}
+ public getIcons = (componentType: string): Array<string> => {
+ let icons: string[];
+ switch (componentType){
+ case Utils.Constants.ComponentType.SERVICE:
+ icons = [
+ 'call_controll',
+ 'mobility',
+ 'network_l_1-3',
+ 'network_l_4'
+ ];
+ break;
+
+ case Utils.Constants.ComponentType.RESOURCE:
+ icons= [
+ 'router',
+ 'database',
+ 'network',
+ 'objectStorage',
+ 'connector',
+ 'brocade',
+ 'cisco',
+ 'ericsson',
+ 'tropo',
+ 'fortinet',
+ 'att',
+ 'broadsoft',
+ 'alcatelLucent',
+ 'metaswitch',
+ 'aricent',
+ 'mySql',
+ 'oracle',
+ 'nokia_siemens',
+ 'juniper',
+ 'call_controll',
+ 'borderElement',
+ 'applicationServer',
+ 'server',
+ 'port',
+ 'loadBalancer',
+ 'compute',
+ 'gateway',
+ 'cp',
+ 'vl',
+ 'vfw',
+ 'firewall'
+ ];
+ break;
+
+ case Utils.Constants.ComponentType.PRODUCT:
+ icons = [
+ 'vfw',
+ 'network',
+ 'security',
+ 'cloud',
+ 'setting',
+ 'orphan',
+ 'wanx',
+ 'vrouter',
+ 'ucpe',
+ 'mobility'
+
+ ];
+ break;
+
+ }
+ return icons;
+ }
+
+ }
+}
+
+
+
diff --git a/catalog-ui/app/scripts/services/cache-service.ts b/catalog-ui/app/scripts/services/cache-service.ts
new file mode 100644
index 0000000000..3e5e5495c7
--- /dev/null
+++ b/catalog-ui/app/scripts/services/cache-service.ts
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ interface ICacheService {
+ get(key:string):any;
+ set(key:string, value:any):void;
+ }
+
+ export class CacheService implements ICacheService {
+
+ static '$inject' = ['sdcConfig', '$document'];
+ private storage:Utils.Dictionary<string, any>;
+
+ constructor() {
+ this.storage = new Utils.Dictionary<string, any>();
+ };
+
+ public get = (key:string):any => {
+ return this.storage.getValue(key);
+ };
+
+ public set = (key:string, value:any):void => {
+ this.storage.setValue(key, value);
+ };
+
+ public remove = (key:string):void => {
+ if (this.storage.containsKey(key)){
+ this.storage.remove(key);
+ }
+ };
+
+ public contains = (key:string):boolean => {
+ return this.storage.containsKey(key);
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/category-resource-service.ts b/catalog-ui/app/scripts/services/category-resource-service.ts
new file mode 100644
index 0000000000..eef1b445e6
--- /dev/null
+++ b/catalog-ui/app/scripts/services/category-resource-service.ts
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ import IMainCategory = Sdc.Models.IMainCategory;
+ 'use strict';
+
+ // Define an interface of the object you want to use, providing it's properties
+ export interface ICategoryResource extends Models.IUserProperties,ng.resource.IResource<ICategoryResource>{
+ name:string;
+ uniqueId:string;
+ subcategories:Array<ICategoryResource>;
+
+ getAllCategories(params?: Object, success?: Function, error?: Function): Array<IMainCategory>;
+ $saveSubCategory(params?: Object, success?: Function, error?: Function): any;
+ $deleteSubCategory(params?: Object, success?: Function, error?: Function): any;
+ }
+
+ // Define your resource, adding the signature of the custom actions
+ export interface ICategoryResourceClass extends ng.resource.IResourceClass<ICategoryResource>{
+ getAllCategories(params?: Object, success?: Function, error?: Function): Array<IMainCategory>;
+ saveSubCategory(params?: Object, success?: Function, error?: Function): any;
+ deleteSubCategory(params?: Object, success?: Function, error?: Function): any;
+ }
+
+ export class CategoryResourceService{
+
+ public static getResource = (
+ $resource: ng.resource.IResourceService,
+ sdcConfig: Models.IAppConfigurtaion
+ ): ICategoryResourceClass => {
+
+ // Define your custom actions here as IActionDescriptor
+ let getAllCategoriesAction : ng.resource.IActionDescriptor = {
+ method: 'GET',
+ isArray: true,
+ url: sdcConfig.api.root + sdcConfig.api.GET_categories
+ };
+ let saveSubCategory : ng.resource.IActionDescriptor = {
+ method: 'POST',
+ isArray: false,
+ url: sdcConfig.api.root + sdcConfig.api.POST_subcategory
+ };
+ let deleteSubCategory: ng.resource.IActionDescriptor = {
+ method: 'DELETE',
+ isArray: false,
+ url: sdcConfig.api.root + sdcConfig.api.POST_subcategory
+ };
+
+
+ let url: string = sdcConfig.api.root + sdcConfig.api.POST_category;
+ let categoryResource: ICategoryResourceClass = <ICategoryResourceClass>$resource(
+ url,
+ { types: '@types', categoryId: '@categoryId' },
+ {
+ getAllCategories: getAllCategoriesAction,
+ saveSubCategory: saveSubCategory,
+ deleteSubCategory: deleteSubCategory
+ }
+ );
+
+ return categoryResource;
+ }
+ }
+ CategoryResourceService.getResource.$inject = ['$resource', 'sdcConfig'];
+}
diff --git a/catalog-ui/app/scripts/services/components/component-service.ts b/catalog-ui/app/scripts/services/components/component-service.ts
new file mode 100644
index 0000000000..393ea71c03
--- /dev/null
+++ b/catalog-ui/app/scripts/services/components/component-service.ts
@@ -0,0 +1,698 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.Services.Components {
+
+ 'use strict';
+
+ declare let CryptoJS:any;
+
+ export interface IComponentService {
+
+ getComponent(id:string);
+ updateComponent(component:Models.Components.Component):ng.IPromise<Models.Components.Component>;
+ changeLifecycleState(component:Models.Components.Component, state:string, userRemarks:any):ng.IPromise<Models.Components.Component> ;
+ validateName(newName:string, subtype?:string):ng.IPromise<Models.IValidate>;
+ createComponent(component:Models.Components.Component):ng.IPromise<Models.Components.Component>;
+ addOrUpdateArtifact(componentId:string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel>;
+ deleteArtifact(componentId:string, artifact:string, artifactLabel):ng.IPromise<Models.ArtifactModel>;
+ addProperty(componentId:string, property:Models.PropertyModel):ng.IPromise<Models.PropertyModel>;
+ updateProperty(componentId:string, property:Models.PropertyModel):ng.IPromise<Models.PropertyModel>;
+ addAttribute(componentId:string, attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel>;
+ updateAttribute(componentId:string, attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel>;
+ deleteProperty(componentId:string, propertyId:string):ng.IPromise<Models.PropertyModel>;
+ deleteAttribute(componentId:string, attributeId:string):ng.IPromise<Models.AttributeModel>;
+ changeResourceInstanceVersion(componentId:string, componentInstanceId:string, componentUid:string):ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ updateInstanceArtifact(componentId:string, instanceId:string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel>;
+ addInstanceArtifact(componentId: string, instanceId: string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel>;
+ deleteInstanceArtifact(componentId: string, instanceId: string, artifact:string, artifactLabel):ng.IPromise<Models.ArtifactModel>;
+ createComponentInstance(componentId:string, componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ updateComponentInstance(componentId:string, componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ updateMultipleComponentInstances(componentId:string, instances:Array<Models.ComponentsInstances.ComponentInstance>):ng.IPromise< Array<Models.ComponentsInstances.ComponentInstance>>;
+ downloadArtifact(componentId:string, artifactId:string):ng.IPromise<Models.IFileDownload>;
+ uploadInstanceEnvFile(componentId:string, instanceId:string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel>;
+ downloadInstanceArtifact(componentId:string, instanceId:string, artifactId:string):ng.IPromise<Models.IFileDownload>;
+ deleteComponentInstance(componentId:string, componentInstanceId:string):ng.IPromise<Models.ComponentsInstances.ComponentInstance>;
+ createRelation(componentId:string, link:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel>;
+ deleteRelation(componentId:string, link:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel>;
+ getRequirementsCapabilities(componentId:string):ng.IPromise<any>;
+ updateInstanceProperty(componentId:string, property:Models.PropertyModel):ng.IPromise<Models.PropertyModel>;
+ updateInstanceAttribute(componentId:string, attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel>;
+ getComponentInstancesFilteredByInputsAndProperties(componentId:string, searchText:string):ng.IPromise<Array<Models.ComponentsInstances.ComponentInstance>>
+ getComponentInstanceInputs(componentId:string, instanceId:string, originComponentUid):ng.IPromise<Array<Models.InputModel>>;
+ getComponentInputs(componentId:string):ng.IPromise<Array<Models.InputModel>>;
+ getComponentInstanceInputProperties(componentId:string, instanceId:string, inputId:string):ng.IPromise<Array<Models.PropertyModel>>;
+ getModuleForDisplay(componentId:string, moduleId:string):ng.IPromise<Models.DisplayModule>;
+ updateGroupMetadata(componentId:string, group:Models.Module):ng.IPromise<Models.Module>;
+ getComponentInputInputs(serviceId:string, input:string): ng.IPromise<Array<Models.InputModel>>;
+ createInputsFromInstancesInputs(serviceId:string, instancesInputsMap:Models.InstancesInputsMap): ng.IPromise<Array<Models.InputModel>>;
+ createInputsFromInstancesInputsProperties(resourceId:string, instanceInputsPropertiesMap:Models.InstanceInputsPropertiesMap): ng.IPromise<Array<Models.PropertyModel>>;
+ deleteComponentInput(serviceId:string, inputId:string):ng.IPromise<Models.InputModel>;
+ }
+
+ export class ComponentService implements IComponentService {
+
+ static '$inject' = [
+ '$log',
+ 'Restangular',
+ 'sdcConfig',
+ 'Sdc.Services.SharingService',
+ '$q',
+ '$interval',
+ '$base64',
+ 'ComponentInstanceFactory'
+ ];
+
+ constructor(protected $log: ng.ILogService,
+ protected restangular:restangular.IElement,
+ protected sdcConfig:Models.IAppConfigurtaion,
+ protected sharingService:Sdc.Services.SharingService,
+ protected $q:ng.IQService,
+ protected $interval:any,
+ protected $base64:any,
+ protected ComponentInstanceFactory:Utils.ComponentInstanceFactory) {
+
+ this.restangular.setBaseUrl(sdcConfig.api.root + sdcConfig.api.component_api_root);
+ this.restangular.setRequestInterceptor(function (elem, operation) {
+ if (operation === "remove") {
+ return null;
+ }
+ return elem;
+ });
+ // this.restangular.setDefaultHeaders({'Content-Type': 'application/json; charset=UTF-8'});
+ }
+
+ //this function is override by each service, we need to change this method to abstract when updtaing typescript version
+ protected createComponentObject = (component:Models.Components.Component):Models.Components.Component => {
+ return component;
+ };
+
+ public getComponent = (id:string):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(id).get().then((response:Models.Components.Component) => {
+ let component:Models.Components.Component = this.createComponentObject(response);
+ //this.$log.debug("Component Loaded successfully : ", component);
+ deferred.resolve(component);
+ }, (err)=> {
+ this.$log.debug("Failed to load component with ID: " + id);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateComponent = (component:Models.Components.Component):ng.IPromise<Models.Components.Component> => {
+ // If this is resource
+ if (component instanceof Sdc.Models.Components.Resource) {
+ let resource:Sdc.Models.Components.Resource = <Sdc.Models.Components.Resource>component;
+ if (resource.importedFile) {
+ // Update resource with payload data.
+ return this.updateResourceWithPayload(resource);
+ } else {
+ if (component.csarUUID) {
+ // Update resource without payload data.
+ return this.updateResource(component);
+ } else {
+ // Update resource without payload data (metadata).
+ return this.updateResourceMetadata(component);
+ }
+ }
+ } else {
+ return this.updateService(component);
+ }
+ };
+
+ private updateService = (component:Models.Components.Component):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(component.uniqueId).one("metadata").customPUT(JSON.stringify(component)).then((response:Models.Components.Component) => {
+ let component:Models.Components.Component = this.createComponentObject(response);
+ deferred.resolve(component);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ private updateResource = (component:Models.Components.Component):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(component.uniqueId).customPUT(JSON.stringify(component)).then((response:Models.Components.Component) => {
+ let component:Models.Components.Component = this.createComponentObject(response);
+ deferred.resolve(component);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ private updateResourceMetadata = (component:Models.Components.Component):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(component.uniqueId).one('metadata').customPUT(JSON.stringify(component)).then((response:Models.Components.Component) => {
+ let component:Models.Components.Component = this.createComponentObject(response);
+ deferred.resolve(component);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ /**
+ * Only resource can be updated with payload data
+ * @param component
+ * @returns {IPromise<T>}
+ */
+ private updateResourceWithPayload = (resource:Sdc.Models.Components.Resource):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+
+ resource.payloadData = resource.importedFile.base64;
+ resource.payloadName = resource.importedFile.filename;
+ let headerObj = this.getHeaderMd5(resource);
+
+ this.restangular.one(resource.uniqueId).customPUT(JSON.stringify(resource), '', {}, headerObj).then((response:Models.Components.Component) => {
+ let componentResult:Models.Components.Component = this.createComponentObject(response);
+ deferred.resolve(componentResult);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ public createComponent = (component:Models.Components.Component):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ let headerObj = this.getHeaderMd5(component);
+ this.restangular.customPOST(JSON.stringify(component), '', {}, headerObj).then((response:Models.Components.Component) => {
+ let component:Models.Components.Component = this.createComponentObject(response);
+ deferred.resolve(component);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public validateName = (newName:string, subtype?:string):ng.IPromise<Models.IValidate> => {
+ let deferred = this.$q.defer();
+ this.restangular.one("validate-name").one(newName).get({'subtype': subtype}).then((response:any) => {
+ deferred.resolve(response.plain());
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public changeLifecycleState = (component:Models.Components.Component, state:string, userRemarks:any):ng.IPromise<Models.Components.Component> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(component.uniqueId).one(state).customPOST(userRemarks).then((response:Models.Components.Component) => {
+ this.sharingService.addUuidValue(response.uniqueId, response.uuid);
+ let component:Models.Components.Component = this.createComponentObject(response);
+ deferred.resolve(component);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ // ------------------------------------------------ Artifacts API --------------------------------------------------//
+ public addOrUpdateArtifact = (componentId:string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let headerObj = {};
+ if (artifact.payloadData) {
+ headerObj = this.getHeaderMd5(artifact);
+ }
+ this.restangular.one(componentId).one("artifacts").customPOST(JSON.stringify(artifact), artifact.uniqueId, {}, headerObj).then((response:any) => {
+ deferred.resolve(response.plain());
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public downloadArtifact = (componentId:string, artifactId:string):ng.IPromise<Models.IFileDownload> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("artifacts").one(artifactId).get().then((response:any) => {
+ deferred.resolve(response.plain());
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public deleteArtifact = (componentId:string, artifactId:string, artifactLabel:string):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("artifacts").one(artifactId).remove({'operation': artifactLabel}).then((response:Models.ArtifactModel) => {
+ deferred.resolve(response);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+
+ // ------------------------------------------------ Properties API --------------------------------------------------//
+ public addProperty = (componentId:string, property:Models.PropertyModel):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("properties").customPOST(property.convertToServerObject()).then((response:any) => {
+ let property:Models.PropertyModel = new Models.PropertyModel(response[Object.keys(response)[0]]);
+ deferred.resolve(property);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateProperty = (componentId:string, property:Models.PropertyModel):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("properties").one(property.uniqueId).customPUT(property.convertToServerObject()).then((response:any) => {
+ let property:Models.PropertyModel = new Models.PropertyModel(response[Object.keys(response)[0]]);
+ deferred.resolve(property);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public deleteProperty = (componentId:string, propertyId:string):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("properties").one(propertyId).remove().then((response:any) => {
+ deferred.resolve(response);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ // ------------------------------------------------ Attributes API --------------------------------------------------//
+ public addAttribute = (componentId:string, attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("attributes").customPOST(attribute.convertToServerObject()).then((response:any) => {
+ let attribute:Models.AttributeModel = new Models.AttributeModel(response);
+ deferred.resolve(attribute);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateAttribute = (componentId:string, attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("attributes").one(attribute.uniqueId).customPUT(attribute.convertToServerObject()).then((response:any) => {
+ let attribute:Models.AttributeModel = new Models.AttributeModel(response);
+ deferred.resolve(attribute);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public deleteAttribute = (componentId:string, attributeId:string):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("attributes").one(attributeId).remove().then((response:any) => {
+ deferred.resolve(response);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ // ------------------------------------------------ Component Instances API --------------------------------------------------//
+
+ public createComponentInstance = (componentId:string, componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").customPOST(JSON.stringify(componentInstance)).then((response:any) => {
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = Utils.ComponentInstanceFactory.createComponentInstance(response);
+ this.$log.debug("Component Instance created", componentInstance);
+ deferred.resolve(componentInstance);
+ }, (err)=> {
+ this.$log.debug("Failed to create componentInstance. With Name: " + componentInstance.name);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateComponentInstance = (componentId:string, componentInstance:Models.ComponentsInstances.ComponentInstance):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").one(componentInstance.uniqueId).customPOST(JSON.stringify(componentInstance)).then((response:any) => {
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = Utils.ComponentInstanceFactory.createComponentInstance(response);
+ this.$log.debug("Component Instance was updated", componentInstance);
+ deferred.resolve(componentInstance);
+ }, (err)=> {
+ this.$log.debug("Failed to update componentInstance. With ID: " + componentInstance.uniqueId + "Name: " + componentInstance.name);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateMultipleComponentInstances = (componentId:string, instances:Array<Models.ComponentsInstances.ComponentInstance>):ng.IPromise<Array<Models.ComponentsInstances.ComponentInstance>> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance/multipleComponentInstance").customPOST(JSON.stringify(instances)).then((response:any) => {
+ this.$log.debug("Multiple Component Instances was updated", response);
+ let updateInstances:Array<Models.ComponentsInstances.ComponentInstance> = new Array<Models.ComponentsInstances.ComponentInstance>();
+ _.forEach(response, (componentInstance:Models.ComponentsInstances.ComponentInstance) => {
+ let updatedComponentInstance:Models.ComponentsInstances.ComponentInstance = Utils.ComponentInstanceFactory.createComponentInstance(componentInstance);
+ updateInstances.push(updatedComponentInstance);
+ });
+ deferred.resolve(updateInstances);
+ }, (err)=> {
+ this.$log.debug("Failed to update Multiple componentInstance.");
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public deleteComponentInstance = (componentId:string, componentInstanceId:string):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").one(componentInstanceId).remove().then(() => {
+ this.$log.debug("Component Instance was deleted");
+ deferred.resolve();
+ }, (err)=> {
+ this.$log.debug("Failed to delete componentInstance. With ID: " + componentInstanceId);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public changeResourceInstanceVersion = (componentId:string, componentInstanceId:string, componentUid:string):ng.IPromise<Models.ComponentsInstances.ComponentInstance> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").one(componentInstanceId).one("changeVersion").customPOST({'componentUid': componentUid}).then((response:any) => {
+ let componentInstance:Models.ComponentsInstances.ComponentInstance = Utils.ComponentInstanceFactory.createComponentInstance(response);
+ deferred.resolve(componentInstance);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public downloadInstanceArtifact = (componentId:string, instanceId:string, artifactId:string):ng.IPromise<Models.IFileDownload> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstances").one(instanceId).one("artifacts").one(artifactId).get().then((response:any) => {
+ deferred.resolve(response.plain());
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateInstanceArtifact = (componentId:string, instanceId:string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let headerObj = {};
+ if(artifact.payloadData){
+ headerObj = this.getHeaderMd5(artifact);
+ }
+ this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("artifacts").customPOST(JSON.stringify(artifact), artifact.uniqueId , {}, headerObj).then((response: any) => {
+ let newArtifact = new Models.ArtifactModel(response);
+ deferred.resolve(newArtifact);
+ }, (err)=>{
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public addInstanceArtifact = (componentId: string, instanceId: string, artifact:Models.ArtifactModel): ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let headerObj = {};
+ if(artifact.payloadData){
+ headerObj = this.getHeaderMd5(artifact);
+ }
+ this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("artifacts").customPOST(JSON.stringify(artifact), artifact.uniqueId , {}, headerObj).then((response: any) => {
+ let artifact:Models.ArtifactModel = new Models.ArtifactModel(response.plain());
+ deferred.resolve(artifact);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public deleteInstanceArtifact = (componentId: string , instanceId: string, artifactId:string, artifactLabel: string): ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("artifacts").one(artifactId).remove({'operation': artifactLabel}).then((response: Models.ArtifactModel) => {
+ deferred.resolve(response);
+ }, (err)=>{
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public uploadInstanceEnvFile = (componentId:string, instanceId:string, artifact:Models.ArtifactModel):ng.IPromise<Models.ArtifactModel> => {
+ let deferred = this.$q.defer();
+ let headerObj = {};
+ if (artifact.payloadData) {
+ headerObj = this.getHeaderMd5(artifact);
+ }
+ this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("artifacts").customPOST(JSON.stringify(artifact), artifact.uniqueId, {}, headerObj).then((response:any) => {
+ let newArtifact = new Models.ArtifactModel(response);
+ deferred.resolve(newArtifact);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateInstanceProperty = (componentId:string, property:Models.PropertyModel):ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ let instanceId = property.resourceInstanceUniqueId;
+ this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("property").customPOST(JSON.stringify(property)).then((response:any) => {
+ let newProperty = new Models.PropertyModel(response);
+ newProperty.readonly = true;
+ newProperty.resourceInstanceUniqueId = instanceId;
+ deferred.resolve(newProperty);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public updateInstanceAttribute = (componentId:string, attribute:Models.AttributeModel):ng.IPromise<Models.AttributeModel> => {
+ let deferred = this.$q.defer();
+ let instanceId = attribute.resourceInstanceUniqueId;
+ this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("attribute").customPOST(JSON.stringify(attribute)).then((response:any) => {
+ let newAttribute = new Models.AttributeModel(response);
+ newAttribute.readonly = true;
+ newAttribute.resourceInstanceUniqueId = instanceId;
+ deferred.resolve(newAttribute);
+ }, (err)=> {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public createRelation = (componentId:string, link:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").one("associate").customPOST(JSON.stringify(link)).then((response:any) => {
+ let relation:Models.RelationshipModel = new Models.RelationshipModel(response.plain());
+ this.$log.debug("Link created successfully ", relation);
+ deferred.resolve(relation);
+ }, (err)=> {
+ this.$log.debug("Failed to create Link From: " + link.fromNode + "To: " + link.toNode);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public deleteRelation = (componentId:string, link:Models.RelationshipModel):ng.IPromise<Models.RelationshipModel> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("resourceInstance").one("dissociate").customPUT(JSON.stringify(link)).then((response:any) => {
+ let relation:Models.RelationshipModel = new Models.RelationshipModel(response);
+ this.$log.debug("Link deleted successfully ", relation);
+ deferred.resolve(relation);
+ }, (err)=> {
+ this.$log.debug("Failed to delete Link From: " + link.fromNode + "To: " + link.toNode);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public getRequirementsCapabilities = (componentId:string):ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("requirmentsCapabilities").get().then((response:any) => {
+ this.$log.debug("Component requirement capabilities recived: ", response);
+ deferred.resolve(response);
+ }, (err)=> {
+ this.$log.debug("Failed to get requirements & capabilities");
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public getModuleForDisplay = (componentId:string, moduleId:string):ng.IPromise<Models.DisplayModule> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("groups").one(moduleId).get().then((response:any) => {
+ this.$log.debug("module loaded successfully: ", response);
+ let module:Models.DisplayModule = new Models.DisplayModule(response);
+ deferred.resolve(module);
+ }, (err)=> {
+ this.$log.debug("Failed to get module with id: ", moduleId);
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ public getComponentInstancesFilteredByInputsAndProperties = (componentId:string, searchText?:string):ng.IPromise<Array<Models.ComponentsInstances.ComponentInstance>> => {
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("componentInstances").get({'searchText': searchText}).then((response:any) => {
+ this.$log.debug("component instances return successfully: ", response);
+ let componentInstances:Array<Models.ComponentsInstances.ComponentInstance> = Utils.CommonUtils.initComponentInstances(response);
+ deferred.resolve(componentInstances);
+ }, (err) => {
+ this.$log.debug("Failed to get component instances of component with id: " + componentId);
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ public getComponentInstanceInputs = (componentId:string, instanceId:string, originComponentUid):ng.IPromise<Array<Models.InputModel>> => {
+
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("componentInstances").one(instanceId).one(originComponentUid).one("inputs").get().then((response:any) => {
+ this.$log.debug("component instance input return successfully: ", response);
+ let inputsArray:Array<Models.InputModel> = new Array<Models.InputModel>();
+ _.forEach(response, (inputObj:Models.InputModel) => {
+ inputsArray.push(new Models.InputModel(inputObj));
+ });
+ deferred.resolve(inputsArray);
+ }, (err) => {
+ this.$log.debug("Failed to get component instance input with id: " + instanceId);
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ public getComponentInputs = (componentId:string):ng.IPromise<Array<Models.InputModel>> => {
+
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("inputs").get().then((response:any) => {
+ this.$log.debug("component inputs return successfully: ", response);
+ let inputsArray:Array<Models.InputModel> = new Array<Models.InputModel>();
+ _.forEach(response, (inputObj:Models.InputModel) => {
+ inputsArray.push(new Models.InputModel(inputObj));
+ });
+ deferred.resolve(inputsArray);
+ }, (err) => {
+ this.$log.debug("Failed to get component inputs for component with id: " + componentId);
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ public getComponentInstanceInputProperties = (componentId:string, instanceId:string, inputId:string):ng.IPromise<Array<Models.PropertyModel>> => {
+
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("componentInstances").one(instanceId).one(inputId).one("properties").get().then((response:any) => {
+ this.$log.debug("component instance input properties return successfully: ", response);
+ let propertiesArray:Array<Models.PropertyModel> = new Array<Models.PropertyModel>();
+ _.forEach(response, (propertyObj:Models.PropertyModel) => {
+ propertiesArray.push(new Models.PropertyModel(propertyObj));
+ });
+ deferred.resolve(propertiesArray);
+ }, (err) => {
+ this.$log.debug("Failed to get component instance input properties with instanceId: " + instanceId + "and input id: " + inputId);
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ public updateGroupMetadata = (componentId:string, group:Models.Module):ng.IPromise<Models.Module> => {
+
+ let deferred = this.$q.defer();
+ this.restangular.one(componentId).one("groups").one(group.uniqueId).one("metadata").customPUT(JSON.stringify(group)).then((response:Models.Module) => {
+ this.$log.debug("group metadata updated successfully: ", response);
+ let updatedGroup:Models.Module = new Models.Module(response);
+
+ deferred.resolve(updatedGroup);
+ }, (err) => {
+ this.$log.debug("Failed to update group metadata for component: " + componentId + " for group with id: " + group.uniqueId);
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ };
+
+ public getComponentInputInputs = (serviceId:string, inputId:string): ng.IPromise<Array<Models.InputModel>> => {
+ let defer = this.$q.defer<any>();
+ this.restangular.one(serviceId).one("inputs").one(inputId).one("inputs").get().then((response: any) => {
+ let inputsArray:Array<Models.InputModel> = new Array<Models.InputModel>();
+ _.forEach(response, (inputObj:Models.InputModel) => {
+ inputsArray.push(new Models.InputModel(inputObj));
+ });
+ defer.resolve(inputsArray);
+ }, (err)=>{
+ this.$log.debug("failed to get inputs of input : ", err);
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ createInputsFromInstancesInputsProperties = (resourceId:string, instancePropertyMap:Models.InstanceInputsPropertiesMap): ng.IPromise<Array<Models.PropertyModel>> => {
+ let defer = this.$q.defer<any>();
+ this.restangular.one(resourceId).one("create/properties").customPOST(instancePropertyMap).then((response: any) => {
+ let inputsArray:Array<Models.PropertyModel> = new Array<Models.PropertyModel>();
+ _.forEach(response, (inputObj:Models.PropertyModel) => {
+ inputsArray.push(new Models.PropertyModel(inputObj));
+ });
+ defer.resolve(inputsArray);
+ }, (err)=>{
+ this.$log.debug("failed to create service inputs from VF instances inputs : ", err);
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ createInputsFromInstancesInputs = (serviceId:string, instancesMap:Models.InstancesInputsMap): ng.IPromise<Array<Models.InputModel>> => {
+ let defer = this.$q.defer<any>();
+ this.restangular.one(serviceId).one("create/inputs").customPOST(instancesMap).then((response: any) => {
+ let inputsArray:Array<Models.InputModel> = new Array<Models.InputModel>();
+ _.forEach(response, (inputObj:Models.InputModel) => {
+ inputsArray.push(new Models.InputModel(inputObj));
+ });
+ defer.resolve(inputsArray);
+ }, (err)=>{
+ this.$log.debug("failed to create service inputs from VF instances inputs : ", err);
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ deleteComponentInput = (serviceId:string, inputId:string) : ng.IPromise<Models.InputModel> => {
+ var defer = this.$q.defer();
+ this.restangular.one(serviceId).one("delete").one(inputId).one("input").remove().then((response: any) => {
+ var inputToDelete = new Models.InputModel(response);
+
+ defer.resolve(inputToDelete);
+ }, (err)=> {
+ console.log("failed to delete input from service: ", err);
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ private getHeaderMd5 = (object:any):any => {
+ let headerObj={};
+ // This is ugly workaround!!!
+ // The md5 result is not correct if we do not add the line JSON.stringify(resource); twice.
+ JSON.stringify(object);
+ let componentString:string = JSON.stringify(object);
+ let md5Result = md5(componentString).toLowerCase();
+ headerObj = {'Content-MD5': this.$base64.encode(md5Result)};
+ return headerObj;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/components/product-service.ts b/catalog-ui/app/scripts/services/components/product-service.ts
new file mode 100644
index 0000000000..69171fbbfa
--- /dev/null
+++ b/catalog-ui/app/scripts/services/components/product-service.ts
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/8/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Services.Components {
+ 'use strict';
+
+ export interface IProductService extends IComponentService {
+
+ }
+
+ export class ProductService extends ComponentService implements IProductService {
+
+ static '$inject' = [
+ '$log',
+ 'Restangular',
+ 'sdcConfig',
+ 'Sdc.Services.SharingService',
+ '$q',
+ '$interval',
+ '$base64',
+ 'ComponentInstanceFactory'
+ ];
+
+ constructor(protected $log: ng.ILogService,
+ protected restangular: restangular.IElement,
+ protected sdcConfig: Models.IAppConfigurtaion,
+ protected sharingService: Sdc.Services.SharingService,
+ protected $q: ng.IQService,
+ protected $interval: any,
+ protected $base64: any,
+ protected ComponentInstanceFactory: Utils.ComponentInstanceFactory) {
+ super($log, restangular, sdcConfig, sharingService, $q, $interval, $base64, ComponentInstanceFactory);
+
+ this.restangular = restangular.one("products");
+ }
+
+ createComponentObject = (component: Models.Components.Component): Models.Components.Component => {
+ return new Models.Components.Product(this, this.$q, <Models.Components.Product>component);
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/services/components/resource-service.ts b/catalog-ui/app/scripts/services/components/resource-service.ts
new file mode 100644
index 0000000000..48781da48e
--- /dev/null
+++ b/catalog-ui/app/scripts/services/components/resource-service.ts
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Services.Components {
+ 'use strict';
+
+ export interface IResourceService extends IComponentService {
+
+ }
+
+ export class ResourceService extends ComponentService implements IResourceService {
+
+ static '$inject' = [
+ '$log',
+ 'Restangular',
+ 'sdcConfig',
+ 'Sdc.Services.SharingService',
+ '$q',
+ '$interval',
+ '$base64',
+ 'ComponentInstanceFactory'
+ ];
+
+ constructor(protected $log: ng.ILogService,
+ protected restangular: restangular.IElement,
+ protected sdcConfig: Models.IAppConfigurtaion,
+ protected sharingService: Sdc.Services.SharingService,
+ protected $q: ng.IQService,
+ protected $interval: any,
+ protected $base64: any,
+ protected ComponentInstanceFactory: Utils.ComponentInstanceFactory) {
+ super($log, restangular, sdcConfig, sharingService, $q, $interval, $base64, ComponentInstanceFactory);
+
+ this.restangular = restangular.one("resources");
+ }
+
+ createComponentObject = (component: Models.Components.Component): Models.Components.Component => {
+ return new Models.Components.Resource(this, this.$q, <Models.Components.Resource>component);
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/services/components/service-service.ts b/catalog-ui/app/scripts/services/components/service-service.ts
new file mode 100644
index 0000000000..fef0b47512
--- /dev/null
+++ b/catalog-ui/app/scripts/services/components/service-service.ts
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/4/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.Services.Components {
+ 'use strict';
+
+
+ export interface IServiceService extends IComponentService {
+ getDistributionsList(uuid: string): ng.IPromise<Array<Models.Distribution>>;
+ getDistributionComponents(distributionId: string): ng.IPromise<Array<Models.DistributionComponent>>;
+ markAsDeployed(serviceId: string, distributionId: string): ng.IPromise<any>;
+ }
+
+ export class ServiceService extends ComponentService implements IServiceService {
+
+ static '$inject' = [
+ '$log',
+ 'Restangular',
+ 'sdcConfig',
+ 'Sdc.Services.SharingService',
+ '$q',
+ '$interval',
+ '$base64',
+ 'ComponentInstanceFactory'
+ ];
+
+ public distribution: string = "distribution";
+
+ constructor(protected $log: ng.ILogService,
+ protected restangular: restangular.IElement,
+ protected sdcConfig: Models.IAppConfigurtaion,
+ protected sharingService: Sdc.Services.SharingService,
+ protected $q: ng.IQService,
+ protected $interval: any,
+ protected $base64: any,
+ protected ComponentInstanceFactory: Utils.ComponentInstanceFactory) {
+ super($log, restangular, sdcConfig, sharingService, $q, $interval, $base64, ComponentInstanceFactory);
+
+ this.restangular = restangular.one("services");
+ }
+
+ getDistributionsList = (uuid: string): ng.IPromise<Array<Models.Distribution>> => {
+ let defer = this.$q.defer<Array<Models.Distribution>>();
+ this.restangular.one(uuid).one("distribution").get().then((distributions: any) => {
+ defer.resolve(<Array<Models.Distribution>> distributions.distributionStatusOfServiceList);
+ }, (err)=> {
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ getDistributionComponents = (distributionId: string): ng.IPromise<Array<Models.DistributionComponent>> => {
+ let defer = this.$q.defer<Array<Models.DistributionComponent>>();
+ this.restangular.one("distribution").one(distributionId).get().then((distributions: any) => {
+ defer.resolve(<Array<Models.DistributionComponent>> distributions.distributionStatusList);
+ }, (err)=> {
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ markAsDeployed = (serviceId: string, distributionId: string): ng.IPromise<any> => {
+ let defer = this.$q.defer<any>();
+ this.restangular.one(serviceId).one("distribution").one(distributionId).one("markDeployed").customPOST().then((result: any) => {
+ defer.resolve(result);
+ }, (err)=> {
+
+ defer.reject(err);
+ });
+ return defer.promise;
+ };
+
+ createComponentObject = (component: Models.Components.Component): Models.Components.Component => {
+ return new Models.Components.Service(this, this.$q, <Models.Components.Service>component);
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/services/components/utils/composition-left-palette-service.ts b/catalog-ui/app/scripts/services/components/utils/composition-left-palette-service.ts
new file mode 100644
index 0000000000..25ac1cdf17
--- /dev/null
+++ b/catalog-ui/app/scripts/services/components/utils/composition-left-palette-service.ts
@@ -0,0 +1,248 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 3/13/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.Services.Components {
+
+ 'use strict';
+
+ export class LeftPanelLatestVersion {
+ uid: string;
+ version: string;
+ }
+
+ export class LeftPaletteDataObject {
+ currentUpdatingIdsList: Array<string>;
+ latestVersionAndIdsList: Array<LeftPanelLatestVersion>;
+ fullDataLeftPaletteComponents: Array<Models.Components.Component>;
+ displayLeftPanelComponents: Array<Models.DisplayComponent>;
+ onFinishLoadingEvent: string;
+
+ constructor(onFinishEventListener: string) {
+
+ this.fullDataLeftPaletteComponents = new Array<Models.Components.Component>();
+ this.displayLeftPanelComponents = new Array<Models.DisplayComponent>();
+ this.currentUpdatingIdsList = new Array<string>();
+ this.latestVersionAndIdsList = new Array<LeftPanelLatestVersion>();
+ this.onFinishLoadingEvent = onFinishEventListener;
+ }
+ }
+
+ export class LeftPaletteLoaderService {
+
+ static '$inject' = [
+ 'Restangular',
+ 'sdcConfig',
+ '$q',
+ '$base64',
+ 'ComponentFactory',
+ 'EventListenerService'
+
+ ];
+
+ constructor(protected restangular: restangular.IElement,
+ protected sdcConfig: Models.IAppConfigurtaion,
+ protected $q: ng.IQService,
+ protected $base64: any,
+ protected ComponentFactory: Utils.ComponentFactory,
+ protected EventListenerService: Services.EventListenerService) {
+
+ this.restangular.setBaseUrl(sdcConfig.api.root + sdcConfig.api.component_api_root);
+ }
+
+ private serviceLeftPaletteData: LeftPaletteDataObject;
+ private resourceLeftPaletteData: LeftPaletteDataObject;
+ private productLeftPaletteData: LeftPaletteDataObject;
+ private vlData: LeftPaletteDataObject;
+
+ public loadLeftPanel = (): void => {
+
+ this.serviceLeftPaletteData = new LeftPaletteDataObject(Utils.Constants.EVENTS.SERVICE_LEFT_PALETTE_UPDATE_EVENT);
+ this.resourceLeftPaletteData = new LeftPaletteDataObject(Utils.Constants.EVENTS.RESOURCE_LEFT_PALETTE_UPDATE_EVENT);
+ this.productLeftPaletteData = new LeftPaletteDataObject(Utils.Constants.EVENTS.PRODUCT_LEFT_PALETTE_UPDATE_EVENT);
+ this.vlData = new LeftPaletteDataObject(Utils.Constants.EVENTS.VL_LEFT_PALETTE_UPDATE_EVENT);
+
+ //initiating service palette
+ this.updateComponentLeftPalette(Utils.Constants.ComponentType.SERVICE);
+
+ //initiating resource palette
+ this.updateComponentLeftPalette(Utils.Constants.ComponentType.RESOURCE);
+
+ //initiating product palette
+ this.updateComponentLeftPalette(Utils.Constants.ComponentType.PRODUCT);
+
+ //initiating vl
+ this.updateComponentLeftPalette(Utils.Constants.ResourceType.VL);
+ };
+
+ private updateData = (latestVersionComponents: Array<Models.Components.Component>, leftPaletteDataObj: LeftPaletteDataObject) => {
+
+ let fullDataComponentsArray: Array<Models.Components.Component> = new Array<Models.Components.Component>();
+ let displayComponentsArray: Array<Models.DisplayComponent> = new Array<Models.DisplayComponent>();
+
+ _.forEach(latestVersionComponents, (componentObj: any) => {
+ let component: Models.Components.Component = this.ComponentFactory.createComponent(componentObj);
+ fullDataComponentsArray.push(component);
+ displayComponentsArray.push(new Models.DisplayComponent(component));
+ });
+
+ leftPaletteDataObj.fullDataLeftPaletteComponents = leftPaletteDataObj.fullDataLeftPaletteComponents.concat(fullDataComponentsArray);
+ leftPaletteDataObj.displayLeftPanelComponents = leftPaletteDataObj.displayLeftPanelComponents.concat(displayComponentsArray);
+ };
+
+ private getTypeUrl = (componentType: string): string => {
+ return Utils.Constants.ComponentType.PRODUCT === componentType ? "services" : "resources";
+ };
+
+ private onFinishLoading = (componentType: string, leftPaletteData: LeftPaletteDataObject): void => {
+ leftPaletteData.currentUpdatingIdsList = [];
+ this.EventListenerService.notifyObservers(leftPaletteData.onFinishLoadingEvent);
+ };
+
+ private getPartialLastVersionFullComponents = (componentType: string, componentInternalType: string, leftPaletteData: LeftPaletteDataObject): void => {
+ this.restangular.one(this.getTypeUrl(componentType)).one('/latestversion/notabstract').customPOST(leftPaletteData.currentUpdatingIdsList, '', {'internalComponentType': componentInternalType}).then((componentsArray: any) => {
+ this.updateData(componentsArray, leftPaletteData);
+ this.onFinishLoading(componentType, leftPaletteData); //when finish loading update view
+ });
+ };
+
+ private removeNotUpdatedComponents = (leftPaletteObject: LeftPaletteDataObject) => {
+
+ leftPaletteObject.fullDataLeftPaletteComponents = _.filter(leftPaletteObject.fullDataLeftPaletteComponents, (component)=> {
+ return leftPaletteObject.currentUpdatingIdsList.indexOf(component.uniqueId) != -1;
+ });
+ leftPaletteObject.displayLeftPanelComponents = _.filter(leftPaletteObject.displayLeftPanelComponents, (component)=> {
+ return leftPaletteObject.currentUpdatingIdsList.indexOf(component.uniqueId) != -1;
+ });
+ };
+
+ private findIdsToUpdate = (leftPaletteObj: LeftPaletteDataObject): Array<string> => {
+ let idsToUpdate = <string[]>_.difference(leftPaletteObj.currentUpdatingIdsList, _.map(leftPaletteObj.fullDataLeftPaletteComponents, 'uniqueId'));
+ let neededUpdate = _.filter(leftPaletteObj.fullDataLeftPaletteComponents, (component) => {
+ let updated = _.find(leftPaletteObj.latestVersionAndIdsList, (versionAndId: LeftPanelLatestVersion) => {
+ return versionAndId.uid === component.uniqueId && versionAndId.version != component.version
+ });
+ return updated != undefined;
+ });
+ if (neededUpdate && neededUpdate.length > 0) {
+ let neededUpdateIds = <string[]>_.map(neededUpdate, 'uid');
+ idsToUpdate.concat(neededUpdateIds);
+ }
+ return idsToUpdate;
+ };
+
+ private updateCurrentIdsList = (componentType: string, leftPaletteObj: LeftPaletteDataObject): void=> {
+ this.removeNotUpdatedComponents(leftPaletteObj);
+ leftPaletteObj.currentUpdatingIdsList = this.findIdsToUpdate(leftPaletteObj);
+ //remove all components that needed update from current lists
+ if (leftPaletteObj.currentUpdatingIdsList.length > 0) {
+ leftPaletteObj.displayLeftPanelComponents = _.filter(leftPaletteObj.displayLeftPanelComponents, (component)=> {
+ return leftPaletteObj.currentUpdatingIdsList.indexOf(component.uniqueId) === -1;
+ });
+ leftPaletteObj.fullDataLeftPaletteComponents = _.filter(leftPaletteObj.fullDataLeftPaletteComponents, (component)=> {
+ return leftPaletteObj.currentUpdatingIdsList.indexOf(component.uniqueId) === -1;
+ });
+ }
+ };
+
+ private updateLeftPalette = (componentType, componentInternalType: string, leftPaletteData: LeftPaletteDataObject): void => {
+ if (leftPaletteData.currentUpdatingIdsList.length > 0) return; //this means the service is still performing update
+ this.restangular.one(this.getTypeUrl(componentType)).one('/latestversion/notabstract/uidonly').get({'internalComponentType': componentInternalType}).then((latestVersionUniqueIds: Array<LeftPanelLatestVersion>) => {
+ leftPaletteData.latestVersionAndIdsList = latestVersionUniqueIds;
+ leftPaletteData.currentUpdatingIdsList = <string[]>_.map(latestVersionUniqueIds, 'uid')
+
+ if (leftPaletteData.fullDataLeftPaletteComponents.length === 0) { //this is when first loading product or resource left palette
+ this.getPartialLastVersionFullComponents(componentType, componentInternalType, leftPaletteData);
+ } else {
+ this.updateCurrentIdsList(componentType, leftPaletteData);
+ if (leftPaletteData.currentUpdatingIdsList.length === 0) {
+ this.onFinishLoading(componentType, leftPaletteData); //when finish loading update view
+ return;
+ }
+ this.getPartialLastVersionFullComponents(componentType, componentInternalType, leftPaletteData);
+ }
+ });
+ };
+
+ public getLeftPanelComponentsForDisplay = (componentType: string): Array<Models.DisplayComponent> => {
+ switch (componentType) {
+ case Utils.Constants.ComponentType.SERVICE:
+ return this.serviceLeftPaletteData.displayLeftPanelComponents;
+ case Utils.Constants.ComponentType.PRODUCT:
+ return this.productLeftPaletteData.displayLeftPanelComponents;
+ default:
+ return this.resourceLeftPaletteData.displayLeftPanelComponents;
+ }
+ };
+
+ public getFullDataComponentList = (componentType: string): Array<Models.Components.Component> => {
+ switch (componentType) {
+ case Utils.Constants.ResourceType.VL:
+ return this.vlData.fullDataLeftPaletteComponents;
+ case Utils.Constants.ComponentType.SERVICE:
+ return this.serviceLeftPaletteData.fullDataLeftPaletteComponents;
+ case Utils.Constants.ComponentType.PRODUCT:
+ return this.productLeftPaletteData.fullDataLeftPaletteComponents;
+ default :
+ return this.resourceLeftPaletteData.fullDataLeftPaletteComponents;
+ }
+ };
+
+ public getFullDataComponentListWithVls = (componentType: string): Array<Models.Components.Component> => {
+ let listPart1: Array<Models.Components.Component>;
+ let listPart2: Array<Models.Components.Component>;
+ if (componentType === Utils.Constants.ResourceType.VL) {
+ listPart1 = [];
+ listPart2 = this.getFullDataComponentList(Utils.Constants.ResourceType.VL);
+ } else {
+ listPart1 = this.getFullDataComponentList(componentType);
+ listPart2 = this.getFullDataComponentList(Utils.Constants.ResourceType.VL);
+ }
+ return listPart1.concat(listPart2);
+ };
+
+ public updateSpecificComponentLeftPalette = (component: Models.Components.Component, componentType: string): void => {
+ let listComponents: Array<Models.Components.Component> = this.getFullDataComponentList(componentType);
+ for (let i in listComponents) {
+ if (listComponents[i].uniqueId === component.uniqueId) {
+ listComponents[i] = component;
+ }
+ }
+ };
+
+ public updateComponentLeftPalette = (componentType): void => {
+ switch (componentType) {
+ case Utils.Constants.ResourceType.VL:
+ this.updateLeftPalette(Utils.Constants.ComponentType.RESOURCE, Utils.Constants.ResourceType.VL, this.vlData);
+ break;
+ case Utils.Constants.ComponentType.SERVICE:
+ this.updateLeftPalette(Utils.Constants.ComponentType.SERVICE, Utils.Constants.ComponentType.SERVICE, this.serviceLeftPaletteData);
+ break;
+ case Utils.Constants.ComponentType.PRODUCT:
+ this.updateLeftPalette(Utils.Constants.ComponentType.PRODUCT, Utils.Constants.ComponentType.SERVICE, this.productLeftPaletteData);
+ break;
+ default:
+ this.updateLeftPalette(Utils.Constants.ComponentType.RESOURCE, Utils.Constants.ResourceType.VF, this.resourceLeftPaletteData);
+ }
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/services/configuration-ui-service.ts b/catalog-ui/app/scripts/services/configuration-ui-service.ts
new file mode 100644
index 0000000000..51eb3dcd9c
--- /dev/null
+++ b/catalog-ui/app/scripts/services/configuration-ui-service.ts
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict'
+
+ interface IConfigurationUiService {
+ getConfigurationUi(): ng.IPromise<any>;
+ }
+
+ export class ConfigurationUiService implements IConfigurationUiService{
+
+ static '$inject' = ['$http', '$q','sdcConfig'];
+ private api: Models.IApi;
+
+ constructor(private $http: ng.IHttpService, private $q: ng.IQService, sdcConfig: Models.IAppConfigurtaion){
+ this.api = sdcConfig.api;
+ }
+
+ getConfigurationUi = (): ng.IPromise<any> =>{
+ let defer = this.$q.defer<any>();
+ this.$http.get(this.api.root+this.api.GET_configuration_ui)
+ .success((result: any) => {
+ defer.resolve(result);
+ });
+ return defer.promise;
+ }
+ }
+
+
+}
diff --git a/catalog-ui/app/scripts/services/cookie-service.ts b/catalog-ui/app/scripts/services/cookie-service.ts
new file mode 100644
index 0000000000..b23a7dccde
--- /dev/null
+++ b/catalog-ui/app/scripts/services/cookie-service.ts
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ interface ICookieService {
+ getUserId(): string;
+ getFirstName(): string;
+ getLastName(): string;
+ getEmail(): string;
+ getUserIdSuffix(): string;
+ }
+
+ export class CookieService implements ICookieService {
+
+ static '$inject' = ['sdcConfig', '$document'];
+ private cookie: Sdc.Models.ICookie;
+ private cookiePrefix: string;
+
+
+ constructor(sdcConfig: Models.IAppConfigurtaion, private $document) {
+ this.cookie = sdcConfig.cookie;
+
+ this.cookiePrefix = '';
+ let junctionName: string = this.getCookieByName(this.cookie.junctionName);
+ if ((junctionName !== null) && (junctionName !== '')){
+ this.cookiePrefix = this.cookie.prefix+junctionName+'!';
+ }
+ }
+
+ private getCookieByName = (cookieName: string): string => {
+ cookieName += '=';
+ let cookies: Array<string> = this.$document[0].cookie.split(';');
+ let cookieVal: string = '';
+ cookies.forEach((cookie: string) => {
+ while(cookie.charAt(0) === ' '){
+ cookie = cookie.substring(1);
+ }
+ if(cookie.indexOf(cookieName) === 0){
+ cookieVal = cookie.substring(cookieName.length, cookie.length);
+ return;
+ }
+ });
+ return cookieVal;
+ };
+
+ public getUserIdSuffix = (): string => {
+ return this.cookie.userIdSuffix;
+ };
+
+ public getUserId = (): string => {
+ let userIdCookieName: string = this.cookiePrefix+this.cookie.userIdSuffix;
+ let userId: string = this.getCookieByName(userIdCookieName);
+ return userId;
+ };
+
+ public getFirstName = (): string => {
+ let firstNameCookieName: string = this.cookiePrefix+this.cookie.userFirstName;
+ let firstName: string = this.getCookieByName(firstNameCookieName);
+ return firstName;
+ };
+
+ public getLastName = (): string => {
+ let lastNameCookieName: string = this.cookiePrefix+this.cookie.userLastName;
+ let lastName: string = this.getCookieByName(lastNameCookieName);
+ return lastName;
+ };
+
+ public getEmail = (): string => {
+ let emailCookieName: string = this.cookiePrefix+this.cookie.userEmail;
+ let email: string = this.getCookieByName(emailCookieName);
+ return email;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/data-types-service.ts b/catalog-ui/app/scripts/services/data-types-service.ts
new file mode 100644
index 0000000000..0e5fca8b5d
--- /dev/null
+++ b/catalog-ui/app/scripts/services/data-types-service.ts
@@ -0,0 +1,129 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ export interface IDataTypesService {
+ //declare methods
+ getAllDataTypes():ng.IPromise<Models.PropertyModel>;
+ getFirsLevelOfDataTypeProperties(dataTypeName:string, dataTypes:Models.DataTypesMap):Array<Models.DataTypePropertyModel>;
+ isDataTypeForSchemaType(property:Models.SchemaProperty, types:Models.DataTypesMap):boolean;
+ isDataTypeForPropertyType(property:Models.PropertyModel, types:Models.DataTypesMap):boolean;
+ isDataTypeForDataTypePropertyType(property:Models.DataTypePropertyModel, types:Models.DataTypesMap):boolean;
+ }
+
+ export class DataTypesService implements IDataTypesService {
+
+ static '$inject' = [
+ 'sdcConfig',
+ '$q',
+ '$http'
+ ];
+
+ constructor(private sdcConfig:Models.IAppConfigurtaion,
+ private $q:ng.IQService,
+ private $http:ng.IHttpService) {
+ }
+
+ //if the dt derived from simple- return the first parent type, else- return null
+ private getTypeForDataTypeDerivedFromSimple = (dataTypeName:string, dataTypes:Models.DataTypesMap):string => {
+ /////////temporary hack for tosca primitives///////////////////////
+ if(!dataTypes[dataTypeName]){
+ return 'string';
+ }
+ ///////////////////////////////////////////////////////////////////
+ if(dataTypes[dataTypeName].derivedFromName == "tosca.datatypes.Root" || dataTypes[dataTypeName].properties){
+ return null;
+ }
+ if(Utils.Constants.PROPERTY_DATA.SIMPLE_TYPES.indexOf(dataTypes[dataTypeName].derivedFromName) > -1 ){
+ return dataTypes[dataTypeName].derivedFromName
+ }
+ return this.getTypeForDataTypeDerivedFromSimple(dataTypes[dataTypeName].derivedFromName,dataTypes);
+ };
+
+ public getAllDataTypes = ():ng.IPromise<Models.PropertyModel> => {
+ let deferred = this.$q.defer();
+ this.$http({
+ url: this.sdcConfig.api.root + this.sdcConfig.api.component_api_root + "dataTypes",
+ method: "get"
+ })
+ .success((response:any) => {
+ deferred.resolve(response);
+ })
+ .error((err) => {
+ deferred.reject(err);
+ });
+ return deferred.promise;
+ };
+
+ //return list of data type properties and all its parents properties
+ //(not include the properties of its properties, in case this data type has not primitive properties)
+ public getFirsLevelOfDataTypeProperties = (dataTypeName:string, dataTypes:Models.DataTypesMap):Array<Models.DataTypePropertyModel> => {
+ let properties = dataTypes[dataTypeName].properties || [];
+ if(dataTypes[dataTypeName].derivedFromName != "tosca.datatypes.Root" ){
+ properties = this.getFirsLevelOfDataTypeProperties(dataTypes[dataTypeName].derivedFromName,dataTypes).concat(properties);
+ }
+ return properties;
+ };
+
+ //return false when type= data type (=not simple type) that not derived from simple type
+ public isDataTypeForSchemaType = (property:Models.SchemaProperty, types:Models.DataTypesMap):boolean=>{
+ property.simpleType="";
+ if(property.type && Utils.Constants.PROPERTY_DATA.TYPES.indexOf(property.type) > -1){
+ return false;
+ }
+ let simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
+ if(simpleType){
+ property.simpleType=simpleType;
+ return false;
+ }
+ return true;
+ };
+
+ public isDataTypeForPropertyType = (property:Models.PropertyModel, types:Models.DataTypesMap):boolean=>{
+ property.simpleType="";
+ if(property.type && Utils.Constants.PROPERTY_DATA.TYPES.indexOf(property.type) > -1){
+ return false;
+ }
+ let simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
+ if(simpleType){
+ property.simpleType=simpleType;
+ return false;
+ }
+ return true;
+ };
+
+ public isDataTypeForDataTypePropertyType = (property:Models.DataTypePropertyModel, types:Models.DataTypesMap):boolean=>{
+ property.simpleType="";
+ if(property.type && Utils.Constants.PROPERTY_DATA.TYPES.indexOf(property.type) > -1){
+ return false;
+ }
+ let simpleType = this.getTypeForDataTypeDerivedFromSimple(property.type, types);
+ if(simpleType){
+ property.simpleType=simpleType;
+ return false;
+ }
+ return true;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/ecomp-service.ts b/catalog-ui/app/scripts/services/ecomp-service.ts
new file mode 100644
index 0000000000..d7910bd612
--- /dev/null
+++ b/catalog-ui/app/scripts/services/ecomp-service.ts
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ interface IEcompHeaderService {
+ getMenuItems(userId): ng.IPromise<Array<any>>;
+ }
+
+ export class EcompHeaderService implements IEcompHeaderService {
+ static '$inject' = ['$http', '$q', 'sdcConfig'];
+ private api:Models.IApi;
+
+ constructor(private $http:ng.IHttpService,
+ private $q:ng.IQService,
+ private sdcConfig:Models.IAppConfigurtaion) {
+ this.api = sdcConfig.api;
+ }
+
+ getMenuItems = (userId):ng.IPromise<Array<any>> => {
+ let defer = this.$q.defer<Array<any>>();
+ //defer.resolve(this.mockData);
+ this.$http.get(this.api.root + this.api.GET_ecomp_menu_items.replace(':userId', userId))
+ .success((response:any) => {
+ defer.resolve(response);
+ })
+ .error((response) => {
+ defer.reject(response);
+ });
+
+ return defer.promise;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/entity-service.ts b/catalog-ui/app/scripts/services/entity-service.ts
new file mode 100644
index 0000000000..a9d5a421ce
--- /dev/null
+++ b/catalog-ui/app/scripts/services/entity-service.ts
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ interface IEntityService {
+ getAllComponents(): ng.IPromise<Array<Models.Components.Component>>;
+ }
+
+ interface IComponentsArray {
+ services:Array<Models.Components.Service>;
+ resources:Array<Models.Components.Resource>;
+ products:Array<Models.Components.Product>;
+ }
+
+ export class EntityService implements IEntityService {
+ static '$inject' = ['$http', '$q', 'sdcConfig', 'Sdc.Services.SharingService','ComponentFactory','Sdc.Services.CacheService'];
+ private api:Models.IApi;
+
+ constructor(private $http:ng.IHttpService,
+ private $q:ng.IQService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private sharingService:Sdc.Services.SharingService,
+ private ComponentFactory: Sdc.Utils.ComponentFactory,
+ private cacheService:Sdc.Services.CacheService
+ ) {
+ this.api = sdcConfig.api;
+ }
+
+ getCatalog = ():ng.IPromise<Array<Models.Components.Component>> => {
+ let defer = this.$q.defer<Array<Models.Components.Component>>();
+ this.$http.get(this.api.root + this.api.GET_catalog)
+ .success((followedResponse:IComponentsArray) => {
+
+ let componentsList:Array<Models.Components.Component> = new Array();
+
+ followedResponse.services.forEach((serviceResponse: Models.Components.Service) => {
+ let component:Models.Components.Service = this.ComponentFactory.createService(serviceResponse); // new Models.Components.Service(serviceResponse);
+ componentsList.push(component);
+ this.sharingService.addUuidValue(component.uniqueId, component.uuid);
+ });
+
+ followedResponse.resources.forEach((resourceResponse:Models.Components.Resource) => {
+ let component:Models.Components.Resource = this.ComponentFactory.createResource(resourceResponse);
+ componentsList.push(component);
+ this.sharingService.addUuidValue(component.uniqueId, component.uuid);
+ });
+
+ followedResponse.products.forEach((productResponse:Models.Components.Product) => {
+
+ let component:Models.Components.Product = this.ComponentFactory.createProduct(productResponse);
+ componentsList.push(component);
+ this.sharingService.addUuidValue(component.uniqueId, component.uuid);
+ });
+
+ this.cacheService.set('breadcrumbsComponents',componentsList);
+ defer.resolve(componentsList);
+ })
+ .error((responce) => {
+ defer.reject(responce);
+ });
+ return defer.promise;
+ };
+
+ getAllComponents = ():ng.IPromise<Array<Models.Components.Component>> => {
+ let defer = this.$q.defer<Array<Models.Components.Component>>();
+ this.$http.get(this.api.root + this.api.GET_element)
+ .success((componentResponse:IComponentsArray) => {
+ let componentsList:Array<Models.Components.Component> = [];
+
+ componentResponse.services && componentResponse.services.forEach((serviceResponse:Models.Components.Service) => {
+ let component:Models.Components.Service = this.ComponentFactory.createService(serviceResponse);
+ componentsList.push(component);
+ this.sharingService.addUuidValue(component.uniqueId, component.uuid);
+ });
+
+ componentResponse.resources && componentResponse.resources.forEach((resourceResponse:Models.Components.Resource) => {
+ let component:Models.Components.Resource = this.ComponentFactory.createResource(resourceResponse);
+ componentsList.push(component);
+ this.sharingService.addUuidValue(component.uniqueId, component.uuid);
+ });
+
+ componentResponse.products && componentResponse.products.forEach((productsResponse:Models.Components.Product) => {
+ let component:Models.Components.Product = this.ComponentFactory.createProduct(productsResponse);
+ componentsList.push(component);
+ this.sharingService.addUuidValue(component.uniqueId, component.uuid);
+ });
+ this.cacheService.set('breadcrumbsComponents',componentsList);
+ defer.resolve(componentsList);
+ });
+
+ return defer.promise;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/services/event-listener-service.ts b/catalog-ui/app/scripts/services/event-listener-service.ts
new file mode 100644
index 0000000000..67afd65c56
--- /dev/null
+++ b/catalog-ui/app/scripts/services/event-listener-service.ts
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 7/4/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ interface IEventListenerService {
+
+ }
+
+ interface ICallbackData {
+ callback:Function;
+ args:any[];
+ }
+
+ export class EventListenerService implements IEventListenerService {
+
+ public observerCallbacks:Utils.Dictionary<string, ICallbackData[]> = new Utils.Dictionary<string, Array<ICallbackData>>();
+
+ //register an observer + callback
+ public registerObserverCallback = (eventName:string, callback:Function, ...args) => {
+ let callbackData = {
+ callback: callback,
+ args: args
+ }
+
+ if (this.observerCallbacks.containsKey(eventName)) {
+ let callbacks = this.observerCallbacks.getValue(eventName);
+
+ // Only insert the callback if the callback is different from existing callbacks.
+ for (let i = 0; i < callbacks.length; i++) {
+ if (callbacks[i].toString() === callback.toString()) {
+ return; // Do not add this callback.
+ }
+ }
+
+ callbacks.push(callbackData);
+ this.observerCallbacks.setValue(eventName, callbacks);
+ } else {
+ this.observerCallbacks.setValue(eventName, [callbackData]);
+ }
+ };
+
+ //unregister an observer
+ public unRegisterObserver = (eventName:string) => {
+ if (this.observerCallbacks.containsKey(eventName)) {
+ this.observerCallbacks.remove(eventName);
+ }
+ };
+
+ public notifyObservers = function (eventName:string, ...args) {
+ _.forEach(this.observerCallbacks.getValue(eventName), (callbackData:ICallbackData) => {
+ callbackData.callback(...args);
+ });
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/services/header-interceptor.ts b/catalog-ui/app/scripts/services/header-interceptor.ts
new file mode 100644
index 0000000000..7f362d3f8c
--- /dev/null
+++ b/catalog-ui/app/scripts/services/header-interceptor.ts
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ //Method name should be exactly "response" - http://docs.angularjs.org/api/ng/service/$http
+ export interface IInterceptor {
+ request: Function;
+
+ }
+
+ export class HeaderInterceptor implements IInterceptor {
+ public static $inject = [
+ '$log',
+ '$injector',
+ '$q',
+ 'uuid4',
+ 'Sdc.Services.SharingService',
+ 'sdcConfig',
+ '$location'
+
+
+ ];
+
+ public static Factory($log: ng.ILogService,
+ $injector: ng.auto.IInjectorService,
+ $q: ng.IQService,
+ uuid4: any,
+ sharingService: Sdc.Services.SharingService,
+ sdcConfig: Models.IAppConfigurtaion,
+ $location: ng.ILocationService) {
+ return new HeaderInterceptor($log, $injector, $q, uuid4, sharingService, sdcConfig, $location);
+ }
+
+ constructor(private $log: ng.ILogService,
+ private $injector: ng.auto.IInjectorService,
+ private $q: ng.IQService,
+ private uuid4: any,
+ private sharingService: Sdc.Services.SharingService,
+ private sdcConfig: Models.IAppConfigurtaion,
+ private $location: ng.ILocationService) {
+ this.$log.debug('initializing AuthenticationInterceptor');
+ }
+
+ public request = (requestSuccess): ng.IPromise<any> => {
+ requestSuccess.headers['X-ECOMP-RequestID'] = this.uuid4.generate();
+ /**
+ * For every request to the server, that the service id, or resource id is sent in the URL, need to pass UUID in the header.
+ * Check if the unique id exists in uuidMap, and if so get the UUID and add it to the header.
+ */
+ let map: Utils.Dictionary<string, string> = this.sharingService.getUuidMap();
+ if (map && requestSuccess.url.indexOf(this.sdcConfig.api.root) === 0) {
+ this.$log.debug("url: " + requestSuccess.url);
+ map.forEach((key: string) => {
+ if (requestSuccess.url.indexOf(key) !== -1) {
+ requestSuccess.headers['X-ECOMP-ServiceID'] = this.sharingService.getUuidValue(key);
+ }
+ });
+ }
+ return requestSuccess;
+ };
+
+ public response = (responseSuccess): ng.IPromise<any> => {
+ let responseData = responseSuccess.data;
+ if (responseData) {
+ let data = JSON.stringify(responseData);
+ if (data && (data.indexOf("Global Logon: Login") > 0)) {
+ this.$location.path('dashboard/welcome');
+ window.location.reload();
+ }
+ }
+ return responseSuccess;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/services/http-error-interceptor.ts b/catalog-ui/app/scripts/services/http-error-interceptor.ts
new file mode 100644
index 0000000000..c04659dfec
--- /dev/null
+++ b/catalog-ui/app/scripts/services/http-error-interceptor.ts
@@ -0,0 +1,118 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ export class HttpErrorInterceptor {
+ public static $inject = ['$injector', '$q'];
+
+ public static Factory($injector: ng.auto.IInjectorService, $q: angular.IQService) {
+ return new HttpErrorInterceptor($injector, $q);
+ }
+
+ constructor(private $injector: ng.auto.IInjectorService, private $q: angular.IQService) {
+ }
+
+ public formatMessageArrays = (message: string, variables: Array<string>)=> {
+ return message.replace(/\[%(\d+)\]/g, function (_, m) {
+ let tmp = [];
+ let list = variables[--m].split(";");
+ list.forEach(function (item) {
+ tmp.push("<li>" + item + "</li>");
+ });
+ return "<ul>" + tmp.join("") + "</ul>";
+ });
+ };
+
+ public responseError = (rejection: any)=> {
+
+ let text: string;
+ let variables;
+ let messageId: string = "";
+ let isKnownException = false;
+
+ if (rejection.data && rejection.data.serviceException) {
+ text = rejection.data.serviceException.text;
+ variables = rejection.data.serviceException.variables;
+ messageId = rejection.data.serviceException.messageId;
+ isKnownException = true;
+ } else if (rejection.data && rejection.data.requestError && rejection.data.requestError.serviceException) {
+ text = rejection.data.requestError.serviceException.text;
+ variables = rejection.data.requestError.serviceException.variables;
+ messageId = rejection.data.requestError.serviceException.messageId;
+ isKnownException = true;
+ } else if (rejection.data && rejection.data.requestError && rejection.data.requestError.policyException) {
+ text = rejection.data.requestError.policyException.text;
+ variables = rejection.data.requestError.policyException.variables;
+ messageId = rejection.data.requestError.policyException.messageId;
+ isKnownException = true;
+ } else if (rejection.data) {
+ text = 'Wrong error format from server';
+ console.error(text);
+ isKnownException = false;
+ }
+
+ let data: Sdc.ViewModels.IServerMessageModalModel;
+ if (isKnownException) {
+ // Remove the "Error: " text at the begining
+ if (text.trim().indexOf("Error:") === 0) {
+ text = text.replace("Error:", "").trim();
+ }
+
+ //mshitrit DE199895 bug fix
+ let count: number = 0;
+ variables.forEach(function (item) {
+ variables[count] = item ? item.replace('<', '&lt').replace('>', '&gt') : '';
+ count++;
+ });
+
+ // Format the message in case has array to <ul><li>
+ text = this.formatMessageArrays(text, variables);
+
+ // Format the message %1 %2
+ text = text.format(variables);
+
+ // Need to inject the MessageService manually to prevent circular dependencies (because MessageService use $templateCache that use $http).
+ data = {
+ title: 'Error',
+ message: text,
+ messageId: messageId,
+ status: rejection.status,
+ severity: Utils.Constants.SEVERITY.ERROR
+ };
+ } else {
+ // Need to inject the MessageService manually to prevent circular dependencies (because MessageService use $templateCache that use $http).
+ data = {
+ title: 'Error',
+ message: rejection.status !== -1 ? rejection.statusText : "Error getting response from server",
+ messageId: messageId,
+ status: rejection.status,
+ severity: Utils.Constants.SEVERITY.ERROR
+ };
+ }
+
+ let modalsHandler = this.$injector.get('ModalsHandler');
+ modalsHandler.openServerMessageModal(data);
+
+ return this.$q.reject(rejection);
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/services/loader-service.ts b/catalog-ui/app/scripts/services/loader-service.ts
new file mode 100644
index 0000000000..6a1a1febe1
--- /dev/null
+++ b/catalog-ui/app/scripts/services/loader-service.ts
@@ -0,0 +1,26 @@
+/**
+ * Created by obarda on 3/13/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ export class LoaderService {
+
+
+ constructor(private eventListenerService: Services.EventListenerService) {
+
+ }
+
+ public showLoader(...args) {
+ this.eventListenerService.notifyObservers(Utils.Constants.EVENTS.SHOW_LOADER_EVENT, ...args);
+ }
+
+ public hideLoader(...args) {
+ this.eventListenerService.notifyObservers(Utils.Constants.EVENTS.HIDE_LOADER_EVENT, ...args);
+ }
+
+ }
+
+ LoaderService.$inject = ['EventListenerService'];
+}
diff --git a/catalog-ui/app/scripts/services/onboarding-service.ts b/catalog-ui/app/scripts/services/onboarding-service.ts
new file mode 100644
index 0000000000..c09871d67f
--- /dev/null
+++ b/catalog-ui/app/scripts/services/onboarding-service.ts
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+
+ interface IOnboardingService {
+ getOnboardingComponents(): ng.IPromise<Array<Models.Components.IComponent>>;
+ getComponentFromCsarUuid(csarUuid:string): ng.IPromise<Models.Components.Component>;
+ downloadOnboardingCsar(packageId:string):ng.IPromise<Models.IFileDownload>;
+ }
+
+ export class OnboardingService implements IOnboardingService {
+
+ static '$inject' = ['$http', '$q', 'sdcConfig', 'ComponentFactory'];
+ private api:Models.IApi;
+
+ constructor(private $http:ng.IHttpService,
+ private $q:ng.IQService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private ComponentFactory: Sdc.Utils.ComponentFactory
+ ) {
+ this.api = sdcConfig.api;
+ }
+
+ getOnboardingComponents = ():ng.IPromise<Array<Models.Components.IComponent>> => {
+ let defer = this.$q.defer<Array<Models.Components.IComponent>>();
+ this.$http.get(this.api.GET_onboarding)
+ .success((response:any) => {
+ let onboardingComponents:Array<Models.ICsarComponent> = response.results;
+ let componentsList:Array<Models.Components.IComponent> = new Array();
+
+ onboardingComponents.forEach((obc: Models.ICsarComponent) => {
+ let component:Models.Components.Component = this.ComponentFactory.createFromCsarComponent(obc);
+ componentsList.push(component);
+ });
+
+ defer.resolve(componentsList);
+ })
+ .error((response) => {
+ defer.reject(response);
+ });
+
+ return defer.promise;
+ };
+
+ downloadOnboardingCsar = (packageId:string):ng.IPromise<Models.IFileDownload> => {
+ let defer = this.$q.defer();
+ this.$http({
+ url: this.api.GET_onboarding + "/" + packageId,
+ method: "get",
+ responseType: "blob"
+ })
+ .success((response:any) => {
+ defer.resolve(response);
+ })
+ .error((err) => {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ };
+
+ getComponentFromCsarUuid = (csarUuid:string):ng.IPromise<Models.Components.Component> => {
+ let defer = this.$q.defer<Models.Components.Component>();
+ this.$http.get(this.api.root + this.api.GET_component_from_csar_uuid.replace(':csar_uuid', csarUuid))
+ .success((response:any) => {
+ let component:Models.Components.Resource;
+ // If the status is 400, this means that the component not found.
+ // I do not want to return error from server, because a popup will appear in client with the error.
+ // So returning success (200) with status 400.
+ if (response.status!==400) {
+ component = new Models.Components.Resource(null, this.$q, <Models.Components.Resource>response);
+ }
+ defer.resolve(component);
+ })
+ .error((response) => {
+ defer.reject(response);
+ });
+
+ return defer.promise;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/progress-service.ts b/catalog-ui/app/scripts/services/progress-service.ts
new file mode 100644
index 0000000000..caa463fc98
--- /dev/null
+++ b/catalog-ui/app/scripts/services/progress-service.ts
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 7/7/2016.
+ */
+/**
+ * Created by obarda on 7/4/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+
+ 'use strict';
+ import IIntervalService = angular.IIntervalService;
+
+ export class ProgressService {
+
+ public progresses:any = {};
+
+ static '$inject' = ['$interval'];
+
+ constructor(
+ protected $interval:any
+ ) {}
+
+ private totalProgress:number = 90;
+ private startProgress:number = 10;
+ private onePercentIntervalSeconds:number = 5;
+ private createComponentInterval;
+
+ public setProgressValue(name:string, value:number):void {
+ if (!this.progresses[name]) {
+ this.progresses[name]={};
+ }
+ this.progresses[name].value = value;
+ }
+
+ public getProgressValue(name:string):number{
+ if (this.progresses[name]){
+ return this.progresses[name].value;
+ }
+ return 0;
+ }
+
+ public deleteProgressValue(name:string):void{
+ this.stopCreateComponentInterval();
+ delete this.progresses[name];
+ }
+
+
+ private stopCreateComponentInterval = ():void => {
+ this.$interval.cancel(this.createComponentInterval);
+ };
+
+
+
+ public initCreateComponentProgress = (componentId:string):void => {
+ var progressValue:number = this.startProgress;
+ if(!this.getProgressValue(componentId)){
+ this.stopCreateComponentInterval();
+ this.setProgressValue(componentId, this.startProgress);
+ this.createComponentInterval = this.$interval(():void => {
+ //TODO replace getProgressMockData to real data after BE provide the API
+ var progressValue = this.getProgressMockData(componentId);
+ if (progressValue<=this.totalProgress ) {
+ this.setProgressValue(componentId, progressValue);
+ } else {
+ /**
+ * Currently the progress is not really checking against the BE.
+ * So the progress can pass 100. So the workaround for now, in case we pass 90 (totalProgress)
+ * stop the interval, so the progress will be kept at 90 until the promise will return value and set
+ * the progress to 100.
+ */
+ this.deleteProgressValue(componentId);
+ }
+ }, this.onePercentIntervalSeconds*1000);
+ }
+
+ };
+
+
+ private getProgressMockData =(id:string):number =>{
+ var progressValue = this.getProgressValue(id);
+ if(progressValue>0){
+ progressValue = progressValue + 1;
+ }
+ //if not finish always stay on 90%
+ if (progressValue>90){
+ progressValue =90;
+ }
+
+ return progressValue;
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/relation-icons-service.ts b/catalog-ui/app/scripts/services/relation-icons-service.ts
new file mode 100644
index 0000000000..3d3b494f16
--- /dev/null
+++ b/catalog-ui/app/scripts/services/relation-icons-service.ts
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ export interface IRelationIconsService {
+ getIconPath(iconName:string):string;
+ }
+
+ export class RelationIconsService implements IRelationIconsService {
+ constructor() {}
+ private icons: Array<string> = [
+ 'AttachesTo',
+ 'BindsTo',
+ 'DependsOn',
+ 'HostedOn',
+ 'LinksTo',
+ 'RoutesTo'
+ ];
+
+ public getIconPath = (relationshipType:string): string => {
+ let result:string = 'ConnectedTo';
+ let baseUrl:string = '/styles/images/relationship-icons/';
+
+ if (relationshipType) {
+ let arr = relationshipType.split('.'); // looks like tosca.relationships.AttachesTo
+ relationshipType = arr[arr.length - 1];
+ if (this.icons.indexOf(relationshipType) > -1) {
+ result = relationshipType;
+ }
+ if('LinksTo'==result){
+ return '';
+ }
+ }
+
+ return baseUrl + result + '.svg';
+ }
+
+ }
+}
+
+
+
diff --git a/catalog-ui/app/scripts/services/sdc-version-service.ts b/catalog-ui/app/scripts/services/sdc-version-service.ts
new file mode 100644
index 0000000000..1744381bca
--- /dev/null
+++ b/catalog-ui/app/scripts/services/sdc-version-service.ts
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ export interface ISdcVersionService {
+ getVersion():ng.IPromise<any>;
+ }
+ export class SdcVersionService implements ISdcVersionService{
+
+ static '$inject' = ['$http', '$q','sdcConfig'];
+ private api: Models.IApi;
+
+ constructor(private $http: ng.IHttpService, private $q: ng.IQService, sdcConfig: Models.IAppConfigurtaion){
+ this.api = sdcConfig.api;
+ }
+
+ public getVersion():ng.IPromise<any>{
+ let defer = this.$q.defer<Array<Models.Distribution>>();
+ let url = this.api.root + this.api.GET_SDC_Version;
+ console.log("======================>" + url);
+ this.$http.get(url)
+ .success((version: any) => {
+ defer.resolve(version);
+ });
+ return defer.promise;
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/services/sharing-service.ts b/catalog-ui/app/scripts/services/sharing-service.ts
new file mode 100644
index 0000000000..14c3158611
--- /dev/null
+++ b/catalog-ui/app/scripts/services/sharing-service.ts
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ export class SharingService {
+
+ private uuidMap: Utils.Dictionary<string, string> = new Utils.Dictionary<string,string>();
+
+ public getUuidValue = (uniqueId:string):string => {
+ return this.uuidMap.getValue(uniqueId);
+ };
+
+ public addUuidValue = (uniqueId:string, uuid:string):void => {
+ this.uuidMap.setValue(uniqueId, uuid);
+ };
+
+ public getUuidMap = ():Utils.Dictionary<string, string> => {
+ return this.uuidMap;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/services/url-tobase64-service.ts b/catalog-ui/app/scripts/services/url-tobase64-service.ts
new file mode 100644
index 0000000000..2d6980da3f
--- /dev/null
+++ b/catalog-ui/app/scripts/services/url-tobase64-service.ts
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ export interface IUrlToBase64Service {
+ downloadUrl(url:string, callback:Function):void;
+ }
+
+ export class UrlToBase64Service implements IUrlToBase64Service {
+ constructor() {}
+
+ public downloadUrl = (url:string, callback:Function): void => {
+ let xhr :any = new XMLHttpRequest();
+
+ xhr.onload = ():void => {
+ let reader = new FileReader();
+ reader.onloadend = ():void => {
+ if (xhr.status === 200) {
+ callback(reader.result);
+ } else {
+ callback(null);
+ }
+ };
+ reader.readAsDataURL(xhr.response);
+ };
+ xhr.open('GET', url);
+ xhr.responseType = 'blob';
+ xhr.send();
+ }
+
+ }
+}
+
diff --git a/catalog-ui/app/scripts/services/user-resource-service.ts b/catalog-ui/app/scripts/services/user-resource-service.ts
new file mode 100644
index 0000000000..7414e2221e
--- /dev/null
+++ b/catalog-ui/app/scripts/services/user-resource-service.ts
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Services {
+ 'use strict';
+
+ // Define an interface of the object you want to use, providing it's properties
+ export interface IUserResource extends Models.IUserProperties,ng.resource.IResource<IUserResource>{
+
+ }
+
+ // Define your resource, adding the signature of the custom actions
+ export interface IUserResourceClass extends ng.resource.IResourceClass<IUserResource>{
+ authorize(): IUserResource;
+ getLoggedinUser(): IUserResource;
+ setLoggedinUser(user: IUserResource): void;
+ getAllUsers(success?: Function, error?: Function): Array<IUserResource>;
+ createUser(IResourceResource, success?: Function, error?: Function): void;
+ editUserRole(IResourceResource, success?: Function, error?: Function): void;
+ deleteUser(IResourceResource, success?: Function, error?: Function): void;
+ }
+
+ export class UserResourceService{
+
+ public static getResource = (
+ $resource: ng.resource.IResourceService,
+ sdcConfig: Models.IAppConfigurtaion,
+ cookieService: Services.CookieService
+ ): IUserResourceClass => {
+
+ let url: string = sdcConfig.api.root+sdcConfig.api.GET_user;
+ let authorizeUrl: string = sdcConfig.api.root+sdcConfig.api.GET_user_authorize;
+ let authorizeActionHeaders: any = {};
+ let cookie: Models.ICookie = sdcConfig.cookie;
+ authorizeActionHeaders[cookie.userFirstName] = cookieService.getFirstName();
+ authorizeActionHeaders[cookie.userLastName] = cookieService.getLastName();
+ authorizeActionHeaders[cookie.userEmail] = cookieService.getEmail();
+ authorizeActionHeaders[cookie.userIdSuffix] = cookieService.getUserId();
+
+ // Define your custom actions here as IActionDescriptor
+ let authorizeAction : ng.resource.IActionDescriptor = {
+ method: 'GET',
+ isArray: false,
+ url: authorizeUrl,
+ headers: authorizeActionHeaders
+ };
+
+ let getAllUsers : ng.resource.IActionDescriptor = {
+ method: 'GET',
+ isArray: true,
+ url: sdcConfig.api.root + sdcConfig.api.GET_all_users
+ };
+
+ let editUserRole : ng.resource.IActionDescriptor = {
+ method: 'POST',
+ isArray: false,
+ url: sdcConfig.api.root + sdcConfig.api.POST_edit_user_role,
+ transformRequest: (data, headers)=>{
+ data.payloadData = undefined;
+ data.payloadName = undefined;
+ return JSON.stringify(data);
+ }
+ };
+
+ let deleteUser : ng.resource.IActionDescriptor = {
+ method: 'DELETE',
+ isArray: false,
+ url: sdcConfig.api.root + sdcConfig.api.DELETE_delete_user
+ };
+
+ let createUser : ng.resource.IActionDescriptor = {
+ method: 'POST',
+ isArray: false,
+ url: sdcConfig.api.root + sdcConfig.api.POST_create_user,
+ transformRequest: (data, headers)=>{
+ data.payloadData = undefined;
+ data.payloadName = undefined;
+ return JSON.stringify(data);
+ }
+ };
+ let userResource: IUserResourceClass = <IUserResourceClass>$resource(
+ url,
+ { id: '@id'},
+ {
+ authorize: authorizeAction,
+ getAllUsers: getAllUsers,
+ createUser: createUser,
+ editUserRole:editUserRole,
+ deleteUser:deleteUser}
+ );
+
+ let _loggedinUser: IUserResource;
+
+ userResource.getLoggedinUser = () => {
+ return _loggedinUser;
+ };
+
+ userResource.setLoggedinUser = (loggedinUser: IUserResource) => {
+ _loggedinUser = loggedinUser;
+ };
+
+ return userResource;
+ }
+ }
+ UserResourceService.getResource.$inject = ['$resource', 'sdcConfig', 'Sdc.Services.CookieService'];
+}
diff --git a/catalog-ui/app/scripts/utils/artifacts-utils.ts b/catalog-ui/app/scripts/utils/artifacts-utils.ts
new file mode 100644
index 0000000000..439a0e98d0
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/artifacts-utils.ts
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Utils {
+ export class ArtifactsUtils {
+
+ static '$inject' = [
+ '$filter',
+ '$templateCache',
+ '$modal'
+ ];
+
+ constructor(private $filter:ng.IFilterService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $modal:ng.ui.bootstrap.IModalService) {
+
+ }
+
+ public getArtifactTypeByState(currentState:string):string {
+ switch (currentState) {
+ case "workspace.composition.lifecycle":
+ return "interface";
+ case "workspace.composition.api":
+ return "api";
+ case "workspace.composition.deployment":
+ return "deployment";
+ default:
+ return "normal";
+ }
+ }
+
+ public getTitle(artifactType:string, selectedComponent:Models.Components.Component):string {
+ switch (artifactType) {
+ case "interface":
+ return "Lifecycle Management";
+ case "api":
+ return "API Artifacts";
+ case "deployment":
+ return "Deployment Artifacts";
+ default:
+ if (!selectedComponent) {
+ return "";
+ } else {
+ return this.$filter("resourceName")(selectedComponent.name) + ' Artifacts';
+ }
+ }
+ }
+
+ public setArtifactType = (artifact:Models.ArtifactModel, artifactType:string):void => {
+ switch (artifactType) {
+ case "api":
+ artifact.artifactGroupType = 'SERVICE_API';
+ break;
+ case "deployment":
+ artifact.artifactGroupType = 'DEPLOYMENT';
+ break;
+ default:
+ artifact.artifactGroupType = 'INFORMATIONAL';
+ break;
+ }
+ };
+
+ public isLicenseType = (artifactType:string) :boolean => {
+ let isLicense:boolean = false;
+
+ if(Utils.Constants.ArtifactType.VENDOR_LICENSE === artifactType || Utils.Constants.ArtifactType.VF_LICENSE === artifactType) {
+ isLicense = true;
+ }
+
+ return isLicense;
+ };
+
+ public removeArtifact = (artifact:Models.ArtifactModel, artifactsArr:Array<Models.ArtifactModel>):void => {
+
+ if (!artifact.mandatory && (Utils.Constants.ArtifactGroupType.INFORMATION == artifact.artifactGroupType ||
+ Utils.Constants.ArtifactGroupType.DEPLOYMENT == artifact.artifactGroupType)) {
+ _.remove(artifactsArr, {uniqueId: artifact.uniqueId});
+ }
+ else {
+ let artifactToDelete = _.find(artifactsArr, {uniqueId: artifact.uniqueId});
+
+ delete artifactToDelete.esId;
+ delete artifactToDelete.description;
+ delete artifactToDelete.artifactName;
+ delete artifactToDelete.apiUrl;
+ }
+ };
+
+ public addAnotherAfterSave(scope:Sdc.ViewModels.IArtifactResourceFormViewModelScope) {
+ let newArtifact = new Models.ArtifactModel();
+ this.setArtifactType(newArtifact, scope.artifactType);
+ scope.editArtifactResourceModel.artifactResource = newArtifact;
+
+ scope.forms.editForm['description'].$setPristine();
+ if(scope.forms.editForm['artifactLabel']){
+ scope.forms.editForm['artifactLabel'].$setPristine();
+ }
+ if(scope.forms.editForm['type']){
+ scope.forms.editForm['type'].$setPristine();
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/change-lifecycle-state-handler.ts b/catalog-ui/app/scripts/utils/change-lifecycle-state-handler.ts
new file mode 100644
index 0000000000..7722a899b9
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/change-lifecycle-state-handler.ts
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/11/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Utils {
+
+ export class ChangeLifecycleStateHandler {
+
+ static '$inject' = [
+ 'sdcConfig',
+ 'sdcMenu',
+ 'ComponentFactory',
+ '$templateCache',
+ '$filter',
+ '$modal',
+ 'ModalsHandler'
+ ];
+
+ constructor(
+ private sdcConfig:Models.IAppConfigurtaion,
+ private sdcMenu:Models.IAppMenu,
+ private ComponentFactory: Sdc.Utils.ComponentFactory,
+ private $templateCache:ng.ITemplateCacheService,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private ModalsHandler: Utils.ModalsHandler
+
+ ) {
+
+ }
+
+ changeLifecycleState = (component:Models.Components.Component, data:any, scope:any, onSuccessCallback?: Function, onErrorCallback?: Function):void => {
+
+ let self = this;
+
+ let getContacts = (component:Models.Components.Component):string =>{
+ let testers = this.sdcConfig.testers;
+ let result:string = testers[component.componentType][component.categories[0].name]?
+ testers[component.componentType][component.categories[0].name]:
+ testers[component.componentType]['default'];
+ return result;
+ };
+
+ let onSuccess = (newComponent:Models.Components.Component):void => {
+ //scope.isLoading = false;
+ console.info(component.componentType.toLowerCase + ' change state ' , newComponent);
+ if(onSuccessCallback) {
+ onSuccessCallback(self.ComponentFactory.createComponent(newComponent));
+ }
+ };
+
+ let onError = (error):void => {
+ scope.isLoading = false;
+ console.info('Failed to changeLifecycleState to ', data.url);
+ if(onErrorCallback) {
+ onErrorCallback(error);
+ }
+ };
+
+ let comment:Models.AsdcComment = new Models.AsdcComment();
+ if (data.alertModal) {
+ // Show alert dialog if defined in menu.json
+ //-------------------------------------------------
+ let onOk = (confirmationText):void => {
+ comment.userRemarks = confirmationText;
+ scope.isLoading = true;
+ component.changeLifecycleState(data.url, comment).then(onSuccess, onError);
+ };
+
+ let onCancel = ():void => {
+ console.info('Cancel pressed');
+ scope.isLoading = false;
+ };
+
+ let modalTitle = this.sdcMenu.alertMessages[data.alertModal].title;
+ let modalMessage = this.sdcMenu.alertMessages[data.alertModal].message.format([component.componentType.toLowerCase()]);
+ this.ModalsHandler.openAlertModal(modalTitle, modalMessage).then(onOk, onCancel);
+ } else if (data.confirmationModal) {
+ // Show confirmation dialog if defined in menu.json
+ //-------------------------------------------------
+ let onOk = (confirmationText):void => {
+ comment.userRemarks = confirmationText;
+ scope.isLoading = true;
+ component.changeLifecycleState(data.url, comment).then(onSuccess, onError);
+ };
+
+ let onCancel = ():void => {
+ console.info('Cancel pressed');
+ scope.isLoading = false;
+ };
+
+ let modalTitle = this.sdcMenu.confirmationMessages[data.confirmationModal].title;
+ let modalMessage = this.sdcMenu.confirmationMessages[data.confirmationModal].message.format([component.componentType.toLowerCase()]);
+ let modalShowComment = this.sdcMenu.confirmationMessages[data.confirmationModal].showComment;
+ this.ModalsHandler.openConfirmationModal(modalTitle, modalMessage, modalShowComment).then(onOk, onCancel);
+
+ } else if (data.emailModal) {
+ // Show email dialog if defined in menu.json
+ //-------------------------------------------------
+ let onOk = (resource):void => {
+ if (resource){
+ onSuccess(resource);
+ } else {
+ onError("Error changing life cycle state");
+ }
+ };
+
+ let onCancel = ():void => {
+ scope.isLoading = false;
+ };
+
+ let emailModel: ViewModels.IEmailModalModel = <ViewModels.IEmailModalModel>{};
+ emailModel.email = <ViewModels.IEmailModalModel_Email>{};
+ emailModel.data = <ViewModels.IEmailModalModel_Data>{};
+ emailModel.title = this.$filter('translate')("EMAIL_MODAL_TITLE");
+ emailModel.email.to = getContacts(component);
+ emailModel.email.subject = this.$filter('translate')("EMAIL_MODAL_SUBJECT", "{'entityName': '" + this.$filter('resourceName')(component.name) + "','entityVersion': '" + component.version + "'}");
+ emailModel.email.message = '';
+ emailModel.data.component = component;
+ emailModel.data.stateUrl = data.url;
+
+ this.ModalsHandler.openEmailModal(emailModel).then(onOk, onCancel);
+
+ } else {
+ // Submit to server only (no modal is shown).
+ scope.isLoading = true;
+ component.changeLifecycleState(data.url, comment).then(onSuccess, onError);
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/common-utils.ts b/catalog-ui/app/scripts/utils/common-utils.ts
new file mode 100644
index 0000000000..aef6b9908d
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/common-utils.ts
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 6/30/2016.
+ */
+/// <reference path="../references.ts"/>
+module Sdc.Utils {
+
+ export class CommonUtils {
+
+ static initProperties(propertiesObj:Array<Sdc.Models.PropertyModel>, uniqueId?:string):Array<Sdc.Models.PropertyModel> {
+
+ let properties = new Array<Sdc.Models.PropertyModel>();
+ if (propertiesObj) {
+ _.forEach(propertiesObj, (property:Sdc.Models.PropertyModel):void => {
+ if (uniqueId) {
+ property.readonly = property.parentUniqueId != uniqueId;
+ }
+ properties.push(new Sdc.Models.PropertyModel(property));
+ });
+ }
+ return properties;
+ };
+
+ static initAttributes(attributesObj:Array<Sdc.Models.AttributeModel>, uniqueId?:string):Array<Sdc.Models.AttributeModel> {
+
+ let attributes = new Array<Sdc.Models.AttributeModel>();
+ if (attributesObj) {
+ _.forEach(attributesObj, (attribute:Sdc.Models.AttributeModel):void => {
+ if (uniqueId) {
+ attribute.readonly = attribute.parentUniqueId != uniqueId;
+ }
+ attributes.push(new Sdc.Models.AttributeModel(attribute));
+ });
+ }
+ return attributes;
+ };
+
+ static initComponentInstances(componentInstanceObj:Array<Models.ComponentsInstances.ResourceInstance>):Array<Models.ComponentsInstances.ResourceInstance> {
+
+ let componentInstances = new Array<Models.ComponentsInstances.ResourceInstance>();
+ if (componentInstanceObj) {
+ _.forEach(componentInstanceObj, (instance:Models.ComponentsInstances.ResourceInstance):void => {
+ componentInstances.push(Utils.ComponentInstanceFactory.createComponentInstance(instance));
+ });
+ }
+ return componentInstances;
+ };
+
+ static initModules(moduleArrayObj:Array<Models.Module>):Array<Models.Module> {
+
+ let modules = new Array<Models.Module>();
+
+ if (moduleArrayObj) {
+ _.forEach(moduleArrayObj, (module:Models.Module):void => {
+ if(module.type === "org.openecomp.groups.VfModule"){
+ modules.push(new Models.Module(module));
+ }
+ });
+ }
+ return modules;
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/component-factory.ts b/catalog-ui/app/scripts/utils/component-factory.ts
new file mode 100644
index 0000000000..1bb139dc45
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/component-factory.ts
@@ -0,0 +1,181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/8/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Utils {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+
+ export class ComponentFactory {
+
+ static '$inject' = [
+ 'Sdc.Services.Components.ResourceService',
+ 'Sdc.Services.Components.ServiceService',
+ 'Sdc.Services.Components.ProductService',
+ 'Sdc.Services.CacheService',
+ '$q'
+ ];
+
+ constructor(
+ private ResourceService:Services.Components.ResourceService,
+ private ServiceService:Services.Components.ServiceService,
+ private ProductService:Services.Components.ProductService,
+ private cacheService:Services.CacheService,
+ private $q: ng.IQService) {
+ }
+
+ public createComponent = (component:Models.Components.Component):Models.Components.Component => {
+ let newComponent:Models.Components.Component;
+ switch (component.componentType) {
+
+ case 'SERVICE':
+ newComponent = new Models.Components.Service(this.ServiceService, this.$q, <Models.Components.Service> component);
+ break;
+
+ case 'RESOURCE':
+ newComponent = new Models.Components.Resource(this.ResourceService, this.$q, <Models.Components.Resource> component);
+ break;
+
+ case 'PRODUCT':
+ newComponent = new Models.Components.Product(this.ProductService, this.$q, <Models.Components.Product> component);
+ break;
+ }
+ return newComponent;
+ };
+
+ public createProduct = (product:Models.Components.Product):Models.Components.Product => {
+ let newProduct:Models.Components.Product = new Models.Components.Product(this.ProductService, this.$q, <Models.Components.Product> product);
+ return newProduct;
+ };
+
+ public createService = (service:Models.Components.Service):Models.Components.Service => {
+ let newService:Models.Components.Service = new Models.Components.Service(this.ServiceService, this.$q, <Models.Components.Service> service);
+ return newService;
+ };
+
+ public createResource = (resource:Models.Components.Resource):Models.Components.Resource => {
+ let newResource:Models.Components.Resource = new Models.Components.Resource(this.ResourceService, this.$q, <Models.Components.Resource> resource);
+ return newResource;
+ };
+
+ public createFromCsarComponent = (csar:Models.ICsarComponent):Models.Components.Component => {
+ let newResource:Sdc.Models.Components.Resource = <Sdc.Models.Components.Resource>this.createEmptyComponent(Sdc.Utils.Constants.ComponentType.RESOURCE);
+ newResource.name = csar.vspName;
+
+ /**
+ * Onboarding CSAR contains category and sub category that are uniqueId.
+ * Need to find the category and sub category and extract the name from them.
+ * First concat all sub categories to one array.
+ * Then find the selected sub category and category.
+ * @type {any}
+ */
+ let availableCategories = angular.copy(this.cacheService.get('resourceCategories'));
+ let allSubs = [];
+ _.each(availableCategories, (main:Models.IMainCategory)=>{
+ if (main.subcategories) {
+ allSubs = allSubs.concat(main.subcategories);
+ }
+ });
+
+ let selectedCategory:Models.IMainCategory = _.find(availableCategories, function(main:Models.IMainCategory){
+ return main.uniqueId === csar.category;
+ });
+
+ let selectedSubCategory:Models.ISubCategory = _.find(allSubs,(sub:Models.ISubCategory)=>{
+ return sub.uniqueId === csar.subCategory;
+ });
+
+ // Build the categories and sub categories array (same format as component category)
+ let categories:Array<Models.IMainCategory> = new Array();
+ let subcategories:Array<Models.ISubCategory> = new Array();
+ if (selectedCategory && selectedSubCategory) {
+ subcategories.push(selectedSubCategory);
+ selectedCategory.subcategories = subcategories;
+ categories.push(selectedCategory);
+ }
+
+ // Fill the component with details from CSAR
+ newResource.selectedCategory = selectedCategory && selectedSubCategory ? selectedCategory.name + "_#_" + selectedSubCategory.name : '';
+ newResource.categories = categories;
+ newResource.vendorName = csar.vendorName;
+ newResource.vendorRelease = csar.vendorRelease;
+ newResource.csarUUID = csar.packageId;
+ newResource.csarPackageType = csar.packageType;
+ newResource.csarVersion = csar.version;
+ newResource.packageId = csar.packageId;
+ newResource.description = csar.description;
+ return newResource;
+ };
+
+ public createEmptyComponent = (componentType: string):Models.Components.Component => {
+ let newComponent:Models.Components.Component;
+
+ switch (componentType) {
+
+ case Utils.Constants.ComponentType.SERVICE:
+ newComponent = new Models.Components.Service(this.ServiceService, this.$q);
+ break;
+
+ case Utils.Constants.ComponentType.RESOURCE:
+ case Utils.Constants.ResourceType.VF:
+ case Utils.Constants.ResourceType.VL:
+ case Utils.Constants.ResourceType.VFC:
+ case Utils.Constants.ResourceType.CP:
+ newComponent = new Models.Components.Resource(this.ResourceService, this.$q);
+ break;
+
+ case Utils.Constants.ComponentType.PRODUCT:
+ newComponent = new Models.Components.Product(this.ProductService, this.$q);
+ break;
+ }
+ newComponent.componentType = componentType;
+ newComponent.tags = [];
+ newComponent.icon = Utils.Constants.DEFAULT_ICON;
+ return newComponent;
+ };
+
+
+ public getServiceFromServer = (componentId: string): ng.IPromise<Models.Components.Service> => {
+ let service: Models.Components.Service = <Models.Components.Service>this.createEmptyComponent(Utils.Constants.ComponentType.SERVICE);
+ service.setUniqueId(componentId);
+ return service.getComponent();
+ };
+
+ public getResourceFromServer = (componentId: string): ng.IPromise<Models.Components.Resource> => {
+ let resource: Models.Components.Resource = <Models.Components.Resource>this.createEmptyComponent(Utils.Constants.ComponentType.RESOURCE);
+ resource.setUniqueId(componentId);
+ return resource.getComponent();
+ };
+
+ public getComponentFromServer = (componentType: string, componentId: string): ng.IPromise<Models.Components.Component> => {
+ let newComponent: Models.Components.Component = this.createEmptyComponent(componentType);
+ newComponent.setUniqueId(componentId);
+ return newComponent.getComponent();
+ };
+
+ public createComponentOnServer = (componentObject:Models.Components.Component):ng.IPromise<Models.Components.Component> => {
+ let component: Models.Components.Component = this.createComponent(componentObject);
+ return component.createComponentOnServer();
+
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/component-instance-factory.ts b/catalog-ui/app/scripts/utils/component-instance-factory.ts
new file mode 100644
index 0000000000..5f698aa46c
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/component-instance-factory.ts
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 3/7/2016.
+ */
+/**
+ * Created by obarda on 2/8/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Utils {
+ 'use strict';
+
+ export class ComponentInstanceFactory {
+
+ static createComponentInstance(componentInstance:Models.ComponentsInstances.ComponentInstance):Models.ComponentsInstances.ComponentInstance {
+ let newComponentInstance:Models.ComponentsInstances.ComponentInstance;
+ switch (componentInstance.originType) {
+ case 'SERVICE':
+ newComponentInstance = new Models.ComponentsInstances.ServiceInstance(componentInstance);
+ break;
+
+ case 'PRODUCT':
+ newComponentInstance = new Models.ComponentsInstances.ProductInstance(componentInstance);
+ break;
+
+ default :
+ newComponentInstance = new Models.ComponentsInstances.ResourceInstance(componentInstance);
+ break;
+ }
+ return newComponentInstance;
+ };
+
+ public createEmptyComponentInstance = (componentInstanceType?: string):Models.ComponentsInstances.ComponentInstance => {
+ let newComponentInstance:Models.ComponentsInstances.ComponentInstance;
+ switch (componentInstanceType) {
+ case 'SERVICE':
+ newComponentInstance = new Models.ComponentsInstances.ServiceInstance();
+ break;
+
+ case 'PRODUCT':
+ newComponentInstance = new Models.ComponentsInstances.ProductInstance();
+ break;
+
+ default :
+ newComponentInstance = new Models.ComponentsInstances.ResourceInstance();
+ break;
+ }
+ return newComponentInstance;
+ };
+
+ public createComponentInstanceFromComponent = (component: Models.Components.Component):Models.ComponentsInstances.ComponentInstance => {
+ let newComponentInstance:Models.ComponentsInstances.ComponentInstance = this.createEmptyComponentInstance(component.componentType);
+ newComponentInstance.uniqueId = component.uniqueId + (new Date()).getTime();
+ newComponentInstance.posX = 0;
+ newComponentInstance.posY = 0;
+ newComponentInstance.name = component.name;
+ newComponentInstance.componentVersion = component.version;
+ newComponentInstance.originType = component.getComponentSubType();
+ //new component instance -> req. & cap. are added on successful instance creation
+ newComponentInstance.requirements = component.requirements;
+ newComponentInstance.capabilities = component.capabilities;
+ newComponentInstance.icon = component.icon;
+ newComponentInstance.componentUid = component.uniqueId;
+ return newComponentInstance;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/constants.ts b/catalog-ui/app/scripts/utils/constants.ts
new file mode 100644
index 0000000000..6db1edf29d
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/constants.ts
@@ -0,0 +1,247 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/18/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Utils.Constants {
+
+ import SuiteOrSpec = jasmine.SuiteOrSpec;
+ export let DEFAULT_ICON = 'defaulticon';
+ export let CP_END_POINT = 'CpEndPoint';
+ export let CHANGE_COMPONENT_CSAR_VERSION_FLAG = 'changeComponentCsarVersion';
+ export let IMAGE_PATH = '';
+
+ export class ComponentType {
+ static SERVICE = 'SERVICE';
+ static RESOURCE = 'RESOURCE';
+ static PRODUCT = 'PRODUCT';
+ }
+
+ export class ResourceType {
+ static VF = 'VF';
+ static VL = 'VL';
+ static CP = 'CP';
+ static VFC = 'VFC';
+ }
+
+ export class ComponentState {
+ static CERTIFICATION_IN_PROGRESS = 'CERTIFICATION_IN_PROGRESS';
+ static CERTIFIED = 'CERTIFIED';
+ static NOT_CERTIFIED_CHECKOUT = 'NOT_CERTIFIED_CHECKOUT';
+ static NOT_CERTIFIED_CHECKIN = 'NOT_CERTIFIED_CHECKIN';
+ static READY_FOR_CERTIFICATION = 'READY_FOR_CERTIFICATION';
+ }
+
+ export class DistributionStatus {
+ DISTRIBUTION_NOT_APPROVED = 'DISTRIBUTION_NOT_APPROVED';
+ DISTRIBUTION_APPROVED = 'DISTRIBUTION_APPROVED';
+ DISTRIBUTED = 'DISTRIBUTED';
+ DISTRIBUTION_REJECTED = 'DISTRIBUTION_REJECTED';
+ }
+
+ export class ArtifactGroupType {
+ static DEPLOYMENT = "DEPLOYMENT";
+ static INFORMATION = "INFORMATIONAL";
+ static SERVICE_API = "SERVICE_API";
+ }
+
+ export class ArtifactType {
+ static HEAT = "HEAT";
+ static VF_LICENSE = "VF_LICENSE";
+ static VENDOR_LICENSE = "VENDOR_LICENSE";
+ static THIRD_PARTY_RESERVED_TYPES = { WORKFLOW:"WORKFLOW",
+ NETWORK_CALL_FLOW:"NETWORK_CALL_FLOW",
+ AAI_SERVICE_MODEL:"AAI_SERVICE_MODEL",
+ AAI_VF_MODEL:"AAI_VF_MODEL",
+ AAI_VF_MODULE_MODEL:"AAI_VF_MODULE_MODEL",
+ AAI_VF_INSTANCE_MODEL:"AAI_VF_INSTANCE_MODEL"};
+ static TOSCA = { TOSCA_TEMPLATE:"TOSCA_TEMPLATE", TOSCA_CSAR:"TOSCA_CSAR"};
+ }
+
+ export class SEVERITY {
+ public static DEBUG = 'DEBUG';
+ public static INFO = 'INFO';
+ public static WARNING = 'WARNING';
+ public static ERROR = 'ERROR';
+ }
+
+ export class PROPERTY_TYPES {
+ public static STRING = 'string';
+ public static INTEGER = 'integer';
+ public static FLOAT = 'float';
+ public static BOOLEAN = 'boolean';
+ public static JSON = 'json';
+ public static MAP = 'map';
+ public static LIST = 'list';
+ }
+
+ export class SOURCES {
+ public static A_AND_AI = 'A&AI';
+ public static ORDER = 'Order';
+ public static RUNTIME = 'Runtime';
+ }
+
+ export class PROPERTY_DATA {
+ public static TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP];
+ public static SIMPLE_TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON];
+ public static SOURCES = [SOURCES.A_AND_AI, SOURCES.ORDER, SOURCES.RUNTIME];
+ }
+
+ export class PROPERTY_VALUE_CONSTRAINTS {
+ public static MAX_LENGTH = 100;
+ public static JSON_MAX_LENGTH = 4096;
+ }
+
+ export class Role {
+ public static ADMIN = 'ADMIN';
+ public static DESIGNER = 'DESIGNER';
+ public static PRODUCT_STRATEGIST = 'PRODUCT_STRATEGIST';
+ public static PRODUCT_MANAGER = 'PRODUCT_MANAGER';
+ public static TESTER = 'TESTER';
+ public static OPS = 'OPS';
+ public static GOVERNOR = 'GOVERNOR';
+ }
+
+ export enum FormState{
+ CREATE,
+ UPDATE,
+ IMPORT,
+ VIEW
+ }
+
+ export class WorkspaceMode {
+ public static CREATE = 'create';
+ public static EDIT = 'edit';
+ public static IMPORT = 'import';
+ public static VIEW = 'view';
+ }
+
+ export class ImagesUrl {
+ public static RESOURCE_ICONS = '/styles/images/resource-icons/';
+ public static SERVICE_ICONS = '/styles/images/service-icons/';
+ public static SELECTED_UCPE_INSTANCE = '/styles/images/resource-icons/selectedUcpeInstance.png';
+ public static SELECTED_CP_INSTANCE = '/styles/images/resource-icons/selectedCPInstance.png';
+ public static SELECTED_VL_INSTANCE = '/styles/images/resource-icons/selectedVLInstance.png';
+ public static CANVAS_PLUS_ICON = '/styles/images/resource-icons/canvasPlusIcon.png';
+ public static MODULE_ICON = '/styles/images/resource-icons/module.png';
+ public static OPEN_MODULE_ICON = '/styles/images/resource-icons/openModule.png';
+ public static OPEN_MODULE_HOVER_ICON = '/styles/images/resource-icons/openModuleHover.png';
+ public static CLOSE_MODULE_ICON = '/styles/images/resource-icons/closeModule.png';
+ public static CLOSE_MODULE_HOVER_ICON = '/styles/images/resource-icons/closeModuleHover.png';
+ }
+
+ export class ModalType {
+ static STANDARD = 'standard';
+ static ERROR = 'error';
+ static ALERT = 'alert';
+ }
+
+ export class GraphColors {
+ public static NOT_CERTIFIED_LINK = 'rgb(218,31,61)';
+ public static VL_LINK = 'rgb(216,216,216)';
+ public static ACTIVE_LINK = '#30bdf2';
+ public static BASE_LINK = 'rgb(55,55,55)';
+ public static NODE_BACKGROUND_COLOR = 'rgba(46, 162, 157, 0.24)';
+ public static NODE_SHADOW_COLOR = 'rgba(198, 230, 228, 0.7)';
+ public static NODE_OVERLAPPING_BACKGROUND_COLOR = 'rgba(179, 10, 60, 0.24)';
+ public static NODE_OVERLAPPING_SHADOW_COLOR = 'rgba(236, 194, 206, 0.7)';
+ public static NODE_UCPE_CP = '#9063cd';
+ public static NODE_UCPE = '#fbfbfb';
+ public static NODE_SELECTED_BORDER_COLOR = '#30bdf2';
+ }
+
+ export class GraphTransactionLogText {
+ public static REMOVE_TEMP_LINK = "remove tempLink";
+ public static DELETE_LINK = "delete link";
+ public static ADD_LINK = "delete link";
+ public static ADD_NODE = "adding node";
+ }
+
+ export class GraphUIObjects {
+ public static LINK_MENU_HEIGHT = 420;
+ public static TOP_HEADER_HEIGHT = 200;
+ public static TOOLTIP_OFFSET_X = 50;
+ public static TOOLTIP_OFFSET_Y = 145;
+ public static TOOLTIP_LINK_OFFSET_X = 35;
+ public static TOOLTIP_LINK_OFFSET_Y = 75;
+ public static MENU_LINK_VL_HEIGHT_OFFSET = 250;
+ public static MENU_LINK_VL_WIDTH_OFFSET = 200;
+ public static MENU_LINK_SIMPLE_HEIGHT_OFFSET = 180;
+ public static MENU_LINK_SIMPLE_WIDTH_OFFSET = 130;
+ public static DIAGRAM_RIGHT_WIDTH_OFFSET = 248;
+ public static DIAGRAM_HEADER_OFFSET = 103;
+ public static DIAGRAM_PALETTE_WIDTH_OFFSET = 247;
+
+ }
+
+ export class States {
+ public static WORKSPACE_GENERAL = 'workspace.general';
+ public static WORKSPACE_ICONS = 'workspace.icons';
+ public static WORKSPACE_ACTIVITY_LOG = 'workspace.activity_log';
+ public static WORKSPACE_DEPLOYMENT_ARTIFACTS = 'workspace.deployment_artifacts';
+ public static WORKSPACE_PROPERTIES = 'workspace.properties';
+ public static WORKSPACE_SERVICE_INPUTS = 'workspace.service_inputs';
+ public static WORKSPACE_RESOURCE_INPUTS = 'workspace.resource_inputs';
+ public static WORKSPACE_ATTRIBUTES = 'workspace.attributes';
+ public static WORKSPACE_HIERARCHY = 'workspace.hierarchy';
+ public static WORKSPACE_INFORMATION_ARTIFACTS = 'workspace.information_artifacts';
+ public static WORKSPACE_TOSCA_ARTIFACTS = 'workspace.tosca_artifacts';
+ public static WORKSPACE_COMPOSITION = 'workspace.composition';
+ public static WORKSPACE_NETWORK_CALL_FLOW = 'workspace.network_call_flow';
+ public static WORKSPACE_MANAGEMENT_WORKFLOW = 'workspace.management_workflow';
+ public static WORKSPACE_DEPLOYMENT = 'workspace.deployment';
+ public static WORKSPACE_DISTRIBUTION = 'workspace.distribution';
+ public static WORKSPACE_REQUIREMENTS_AND_CAPABILITIES = 'workspace.reqAndCap';
+ }
+
+ export class EVENTS {
+ static RESOURCE_LEFT_PALETTE_UPDATE_EVENT = "resourceLeftPanelUpdateEvent";
+ static SERVICE_LEFT_PALETTE_UPDATE_EVENT = "serviceLeftPanelUpdateEvent";
+ static PRODUCT_LEFT_PALETTE_UPDATE_EVENT = "productLeftPanelUdateEvent";
+ static VL_LEFT_PALETTE_UPDATE_EVENT = "vlLeftPanelUdateEvent";
+ static ON_CSAR_LOADING = "onCsarLoading";
+ static DOWNLOAD_ARTIFACT_FINISH_EVENT = "downloadArtifactFinishEvent";
+ static ON_WORKSPACE_SAVE_BUTTON_CLICK = "onWorkspaceSaveButtonClick";
+ static ON_WORKSPACE_SAVE_BUTTON_SUCCESS = "onWorkspaceSaveButtonSuccess";
+ static ON_WORKSPACE_SAVE_BUTTON_ERROR = "onWorkspaceSaveButtonError";
+
+ //Loader events
+ static SHOW_LOADER_EVENT = "showLoaderEvent";
+ static HIDE_LOADER_EVENT = "hideLoaderEvent";
+ }
+
+ export class GRAPH_EVENTS {
+ static ON_NODE_SELECTED = "onNodeSelected";
+ static ON_GRAPH_BACKGROUND_CLICKED = "onGraphBackgroundClicked";
+ static ON_PALETTE_COMPONENT_HOVER_IN = 'onPaletteComponentHoverIn';
+ static ON_PALETTE_COMPONENT_HOVER_OUT = 'onPaletteComponentHoverOut';
+ static ON_PALETTE_COMPONENT_DRAG_START = 'onPaletteComponentDragStart';
+ static ON_PALETTE_COMPONENT_DRAG_ACTION = 'onPaletteComponentDragAction';
+ static ON_COMPONENT_INSTANCE_NAME_CHANGED = 'onComponentInstanceNameChanged';
+ static ON_DELETE_COMPONENT_INSTANCE = 'onDeleteComponentInstance';
+ static ON_DELETE_MULTIPLE_COMPONENTS = 'onDeleteMultipleComponents';
+ static ON_DELETE_EDGE = 'onDeleteEdge';
+ static ON_INSERT_NODE_TO_UCPE = 'onInsertNodeToUCPE';
+ static ON_REMOVE_NODE_FROM_UCPE = 'onRemoveNodeFromUCPE';
+ static ON_VERSION_CHANGED = 'onVersionChanged';
+ }
+
+}
diff --git a/catalog-ui/app/scripts/utils/dictionary/dictionary.ts b/catalog-ui/app/scripts/utils/dictionary/dictionary.ts
new file mode 100644
index 0000000000..ef9a1bc4ea
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/dictionary/dictionary.ts
@@ -0,0 +1,257 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+
+ This code was copy from collections.ts lib
+ https://github.com/basarat/typescript-collections
+**/
+
+module Sdc.Utils{
+ 'use strict';
+
+ // Used internally by dictionary
+ interface IDictionaryPair<K, V>{
+ key: K;
+ value: V;
+ }
+
+ export class Dictionary<K, V>{
+
+ /**
+ * Object holding the key-value pairs.
+ * @type {Object}
+ * @private
+ */
+ private table: { [key: string]: IDictionaryPair<K, V> };
+ //: [key: K] will not work since indices can only by strings in javascript and typescript enforces this.
+
+ /**
+ * Number of elements in the list.
+ * @type {number}
+ * @private
+ */
+ private nElements: number;
+
+ /**
+ * Function used to convert keys to strings.
+ * @type {function(Object):string}
+ * @private
+ */
+ private toStr: (key: K) => string;
+
+
+ /**
+ * Creates an empty dictionary.
+ * @class <p>Dictionaries map keys to values; each key can map to at most one value.
+ * This implementation accepts any kind of objects as keys.</p>
+ *
+ * <p>If the keys are custom objects a function which converts keys to unique
+ * strings must be provided. Example:</p>
+ * <pre>
+ * function petToString(pet) {
+ * return pet.name;
+ * }
+ * </pre>
+ * @constructor
+ * @param {function(Object):string=} toStrFunction optional function used
+ * to convert keys to strings. If the keys aren"t strings or if toString()
+ * is not appropriate, a custom function which receives a key and returns a
+ * unique string must be provided.
+ */
+ constructor(toStrFunction?: (key: K) => string) {
+ this.table = {};
+ this.nElements = 0;
+ this.toStr = toStrFunction || this.defaultToString;
+ }
+
+
+ /**
+ copy from angular.js isUndefined
+ */
+ private isUndefined = (value: any):boolean => {
+ return typeof value === 'undefined';
+ }
+
+ defaultToString = (item: any): string => {
+ return item.toString();
+ }
+
+ /**
+ * Returns the value to which this dictionary maps the specified key.
+ * Returns undefined if this dictionary contains no mapping for this key.
+ * @param {Object} key key whose associated value is to be returned.
+ * @return {*} the value to which this dictionary maps the specified key or
+ * undefined if the map contains no mapping for this key.
+ */
+ getValue = (key: K): V => {
+ let pair: IDictionaryPair<K, V> = this.table[this.toStr(key)];
+ if (this.isUndefined(pair)) {
+ return undefined;
+ }
+ return pair.value;
+ }
+
+
+ /**
+ * Associates the specified value with the specified key in this dictionary.
+ * If the dictionary previously contained a mapping for this key, the old
+ * value is replaced by the specified value.
+ * @param {Object} key key with which the specified value is to be
+ * associated.
+ * @param {Object} value value to be associated with the specified key.
+ * @return {*} previous value associated with the specified key, or undefined if
+ * there was no mapping for the key or if the key/value are undefined.
+ */
+ setValue = (key: K, value: V): V => {
+
+ if (this.isUndefined(key) || this.isUndefined(value)) {
+ return undefined;
+ }
+
+ let ret: V;
+ let k = this.toStr(key);
+ let previousElement: IDictionaryPair<K, V> = this.table[k];
+ if (this.isUndefined(previousElement)) {
+ this.nElements++;
+ ret = undefined;
+ } else {
+ ret = previousElement.value;
+ }
+ this.table[k] = {
+ key: key,
+ value: value
+ };
+ return ret;
+ }
+
+ /**
+ * Removes the mapping for this key from this dictionary if it is present.
+ * @param {Object} key key whose mapping is to be removed from the
+ * dictionary.
+ * @return {*} previous value associated with specified key, or undefined if
+ * there was no mapping for key.
+ */
+ remove = (key: K): V => {
+ let k = this.toStr(key);
+ let previousElement: IDictionaryPair<K, V> = this.table[k];
+ if (!this.isUndefined(previousElement)) {
+ delete this.table[k];
+ this.nElements--;
+ return previousElement.value;
+ }
+ return undefined;
+ }
+
+ /**
+ * Returns an array containing all of the keys in this dictionary.
+ * @return {Array} an array containing all of the keys in this dictionary.
+ */
+ keys = (): K[] => {
+ let array: K[] = [];
+ for (let name in this.table) {
+ if (this.table.hasOwnProperty(name)) {
+ let pair: IDictionaryPair<K, V> = this.table[name];
+ array.push(pair.key);
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Returns an array containing all of the values in this dictionary.
+ * @return {Array} an array containing all of the values in this dictionary.
+ */
+ values = (): V[] => {
+ let array: V[] = [];
+ for (let name in this.table) {
+ if (this.table.hasOwnProperty(name)) {
+ let pair: IDictionaryPair<K, V> = this.table[name];
+ array.push(pair.value);
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Executes the provided function once for each key-value pair
+ * present in this dictionary.
+ * @param {function(Object,Object):*} callback function to execute, it is
+ * invoked with two arguments: key and value. To break the iteration you can
+ * optionally return false.
+ */
+ forEach = (callback: (key: K, value: V) => any): void => {
+ for (let name in this.table) {
+ if (this.table.hasOwnProperty(name)) {
+ let pair: IDictionaryPair<K, V> = this.table[name];
+ let ret = callback(pair.key, pair.value);
+ if (ret === false) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if this dictionary contains a mapping for the specified key.
+ * @param {Object} key key whose presence in this dictionary is to be
+ * tested.
+ * @return {boolean} true if this dictionary contains a mapping for the
+ * specified key.
+ */
+ containsKey = (key: K): boolean => {
+ return !this.isUndefined(this.getValue(key));
+ }
+
+ /**
+ * Removes all mappings from this dictionary.
+ * @this {Dictionary}
+ */
+ clear = () => {
+
+ this.table = {};
+ this.nElements = 0;
+ }
+
+ /**
+ * Returns the number of keys in this dictionary.
+ * @return {number} the number of key-value mappings in this dictionary.
+ */
+ size = (): number => {
+ return this.nElements;
+ }
+
+ /**
+ * Returns true if this dictionary contains no mappings.
+ * @return {boolean} true if this dictionary contains no mappings.
+ */
+ isEmpty = (): boolean => {
+ return this.nElements <= 0;
+ }
+
+ toString = (): string => {
+ let toret = "{";
+ this.forEach((k, v) => {
+ toret = toret + "\n\t" + k.toString() + " : " + v.toString();
+ });
+ return toret + "\n}";
+ }
+ } // End of dictionary
+
+}
diff --git a/catalog-ui/app/scripts/utils/file-utils.ts b/catalog-ui/app/scripts/utils/file-utils.ts
new file mode 100644
index 0000000000..db6251f199
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/file-utils.ts
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Utils {
+ export class FileUtils {
+
+ static '$inject' = [
+ '$window'
+ ];
+
+ constructor(private $window: any) {
+ }
+
+ public byteCharactersToBlob = (byteCharacters, contentType): any => {
+ contentType = contentType || '';
+ let sliceSize = 1024;
+ let bytesLength = byteCharacters.length;
+ let slicesCount = Math.ceil(bytesLength / sliceSize);
+ let byteArrays = new Array(slicesCount);
+
+ for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
+ let begin = sliceIndex * sliceSize;
+ let end = Math.min(begin + sliceSize, bytesLength);
+
+ let bytes = new Array(end - begin);
+ for (let offset = begin, i = 0; offset < end; ++i, ++offset) {
+ bytes[i] = byteCharacters[offset].charCodeAt(0);
+ }
+ byteArrays[sliceIndex] = new Uint8Array(bytes);
+ }
+ return new Blob(byteArrays, {type: contentType});
+ };
+
+ public base64toBlob = (base64Data, contentType): any => {
+ let byteCharacters = atob(base64Data);
+ return this.byteCharactersToBlob(byteCharacters, contentType);
+ };
+
+ public downloadFile = (blob, fileName): void=> {
+ let url = this.$window.URL.createObjectURL(blob);
+ let downloadLink = document.createElement("a");
+
+ downloadLink.setAttribute('href', url);
+ downloadLink.setAttribute('download', fileName);
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+
+ //time out for firefox
+ setTimeout(()=> {
+ document.body.removeChild(downloadLink);
+ this.$window.URL.revokeObjectURL(url);
+ }, 100);
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/functions.ts b/catalog-ui/app/scripts/utils/functions.ts
new file mode 100644
index 0000000000..32bc9243cf
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/functions.ts
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+module Sdc.Utils.Functions {
+
+ export class QueueUtils {
+
+ private executionQueue : any;
+
+ constructor(private $q:ng.IQService){
+ this.executionQueue = this.getDummyPromise();
+ }
+
+
+ private getDummyPromise = (): ng.IPromise<boolean> => {
+ let deferred : ng.IDeferred<boolean>= this.$q.defer();
+ deferred.resolve(true);
+ return deferred.promise;
+ };
+
+
+ private addMethodToQueue = (runMe:Function) : void => {
+ this.executionQueue = this.executionQueue.then(runMe, runMe);
+ };
+
+ addNonBlockingUIAction = (update:Function , releaseUIcallBack:Function) : void => {
+ releaseUIcallBack();
+ this.addMethodToQueue(update);
+ };
+
+ // The Method call is responsible for releasing the UI
+ addBlockingUIAction = ( blockingServerRequest : Function ):void => {
+ this.addMethodToQueue(blockingServerRequest);
+ };
+
+ addBlockingUIActionWithReleaseCallback = ( blockingServerRequest : Function, releaseUIcallBack:Function):void=>{
+ this.addMethodToQueue(blockingServerRequest);
+ this.addMethodToQueue(releaseUIcallBack);
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/menu-handler.ts b/catalog-ui/app/scripts/utils/menu-handler.ts
new file mode 100644
index 0000000000..0f2b7de3be
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/menu-handler.ts
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references.ts"/>
+module Sdc.Utils {
+
+ 'use strict';
+
+ export class MenuItem {
+ text: string;
+ callback: (...args: Array<any>) => ng.IPromise<boolean>;
+ state: string;
+ action: string;
+ params: Array<any>;
+ isDisabled: boolean;
+ disabledRoles: Array<string>;
+ blockedForTypes:Array<string>; // This item will not be shown for specific components types.
+
+ //TODO check if needed
+ confirmationModal:string; // Open confirmation modal (user should select "OK" or "Cancel"), and continue with the action.
+ emailModal:string; // Open email modal (user should fill email details), and continue with the action.
+ url:string; // Data added to menu item, in case the function need to use it, example: for function "changeLifecycleState", I need to pass also the state "CHECKOUT" that I want the state to change to.
+
+
+ constructor(text: string, callback: (...args: Array<any>) => ng.IPromise<boolean>, state:string, action:string, params?: Array<any>, blockedForTypes?:Array<string>) {
+ this.text = text;
+ this.callback = callback;
+ this.state = state;
+ this.action = action;
+ this.params = params;
+ this.blockedForTypes = blockedForTypes;
+ }
+ }
+
+ export class MenuItemGroup {
+ selectedIndex: number;
+ menuItems: Array<MenuItem>;
+ itemClick: boolean;
+
+ constructor(selectedIndex?:number, menuItems?: Array<MenuItem>, itemClick?: boolean) {
+ this.selectedIndex = selectedIndex;
+ this.menuItems = menuItems;
+ this.itemClick = itemClick;
+ }
+
+ public updateSelectedMenuItemText (newText: string) {
+ this.menuItems[this.selectedIndex].text = newText;
+ }
+ }
+
+
+ export class MenuHandler {
+
+ static '$inject' = [
+ 'sdcConfig',
+ 'sdcMenu',
+ 'ComponentFactory',
+ '$templateCache',
+ '$filter',
+ '$modal',
+ 'ModalsHandler',
+ '$state',
+ '$q'
+ ];
+
+ constructor(
+ private sdcConfig:Models.IAppConfigurtaion,
+ private sdcMenu:Models.IAppMenu,
+ private ComponentFactory: ComponentFactory,
+ private $templateCache:ng.ITemplateCacheService,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private ModalsHandler: ModalsHandler,
+ private $state:ng.ui.IStateService,
+ private $q:ng.IQService
+ ) {
+
+ }
+
+
+ generateBreadcrumbsModelFromComponents = (components: Array<Sdc.Models.Components.Component>, selected:Sdc.Models.Components.Component):MenuItemGroup => {
+ let result = new MenuItemGroup(0, [], false);
+ if (components) {
+
+ // Search the component in all components by uuid (and not uniqueid, gives access to an assets's minor versions).
+ let selectedItem = _.find(components, (item:Sdc.Models.Components.Component) => {
+ return item.uuid === selected.uuid;
+ });
+
+ // If not found search by invariantUUID
+ if(undefined == selectedItem){
+ selectedItem = _.find(components, (item:Sdc.Models.Components.Component) => {
+ //invariantUUID && Certified State matches between major versions
+ return item.invariantUUID === selected.invariantUUID && item.lifecycleState === Utils.Constants.ComponentState.CERTIFIED;
+ });
+ }
+
+ // If not found search by name (name is unique).
+ if(undefined == selectedItem){
+ selectedItem = _.find(components, (item:Sdc.Models.Components.Component) => {
+ return item.name === selected.name;
+ });
+ }
+
+ result.selectedIndex = components.indexOf(selectedItem);
+ components[result.selectedIndex] = selected;
+ let clickItemCallback = (component: Sdc.Models.Components.Component): ng.IPromise<boolean> => {
+ this.$state.go('workspace.general', {id: component.uniqueId, type:component.componentType.toLowerCase(), mode: Utils.Constants.WorkspaceMode.VIEW});
+ return this.$q.when(true);
+ };
+
+ components.forEach((component:Sdc.Models.Components.Component) => {
+ let menuItem = new MenuItem(
+ // component.name,
+ component.getComponentSubType() + ': ' + this.$filter('resourceName')(component.name),
+ clickItemCallback,
+ null,
+ null,
+ [component]
+ );
+ // menuItem.text = component.name;
+ result.menuItems.push(menuItem);
+ });
+ }
+ return result;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/modals-handler.ts b/catalog-ui/app/scripts/utils/modals-handler.ts
new file mode 100644
index 0000000000..f3e80a7a24
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/modals-handler.ts
@@ -0,0 +1,275 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 2/11/2016.
+ */
+/// <reference path="../references"/>
+module Sdc.Utils {
+
+ export interface IModalsHandler {
+
+ openViewerModal(component:Models.Components.Component):void;
+ openDistributionStatusModal(distribution: Models.Distribution,status:string):void;
+ openConfirmationModal (title:string, message:string, showComment:boolean, size?: string):ng.IPromise<any>;
+ openAlertModal (title:string, message:string, size?: string):ng.IPromise<any>;
+ openStandardModal (title:string, message:string, size?: string):ng.IPromise<any>;
+ openErrorModal (title:string, message:string, size?: string):ng.IPromise<any>;
+ openEmailModal(emailModel:ViewModels.IEmailModalModel) :ng.IPromise<any>;
+ openServerMessageModal(data:Sdc.ViewModels.IServerMessageModalModel): ng.IPromise<any>;
+ openClientMessageModal(data:Sdc.ViewModels.IClientMessageModalModel): ng.IPromise<ng.ui.bootstrap.IModalServiceInstance>;
+ openWizardArtifactModal(artifact: Models.ArtifactModel, component:Models.Components.Component): ng.IPromise<any>;
+ openWizard(componentType: Utils.Constants.ComponentType, component?:Models.Components.Component, importedFile?: any): ng.IPromise<any>;
+ }
+
+ export class ModalsHandler implements IModalsHandler{
+
+ static '$inject' = [
+ '$templateCache',
+ '$modal',
+ '$q'
+ ];
+
+ constructor(private $templateCache:ng.ITemplateCacheService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $q:ng.IQService) {
+ }
+
+ openViewerModal = (component:Models.Components.Component):void => {
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/component-viewer/component-viewer.html'),
+ controller: 'Sdc.ViewModels.ComponentViewerViewModel',
+ size: 'lg',
+ backdrop: 'static',
+ resolve: {
+ component: ():Models.Components.Component=> {
+ return component;
+ }
+ }
+ };
+ this.$modal.open(modalOptions);
+ };
+
+
+ openDistributionStatusModal = (distribution: Models.Distribution,status:string): ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html'),
+ controller: 'Sdc.ViewModels.DistributionStatusModalViewModel',
+ size: 'sdc-xl',
+ backdrop: 'static',
+ resolve: {
+ data: ():any => {
+ return {
+ 'distribution': distribution,
+ 'status': status
+ };
+ }
+ }
+ };
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+ };
+
+
+
+ openAlertModal = (title:string, message:string, size?: string):ng.IPromise<any> => {
+ return this.openConfirmationModalBase(title, message, false, Utils.Constants.ModalType.ALERT, size);
+ };
+
+ openStandardModal = (title:string, message:string, size?: string):ng.IPromise<any> => {
+ return this.openConfirmationModalBase(title, message, false, Utils.Constants.ModalType.STANDARD, size);
+ };
+
+ openErrorModal = (title:string, message:string, size?: string):ng.IPromise<any> => {
+ return this.openConfirmationModalBase(title, message, false, Utils.Constants.ModalType.ERROR, size);
+ };
+
+ openConfirmationModal = (title:string, message:string, showComment:boolean, size?: string):ng.IPromise<any> => {
+ return this.openConfirmationModalBase(title, message, showComment, Utils.Constants.ModalType.STANDARD, size);
+ };
+
+ private openConfirmationModalBase = (title:string, message:string, showComment:boolean, type:Utils.Constants.ModalType, size?: string):ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view.html'),
+ controller: 'Sdc.ViewModels.ConfirmationModalViewModel',
+ size: size? size:'sdc-sm',
+ backdrop: 'static',
+ resolve: {
+ confirmationModalModel: ():Sdc.ViewModels.IConfirmationModalModel => {
+ let model:Sdc.ViewModels.IConfirmationModalModel = {
+ title: title,
+ message: message,
+ showComment: showComment,
+ type: type
+ };
+ return model;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+ };
+
+ openEmailModal = (emailModel:ViewModels.IEmailModalModel):ng.IPromise<any> => {
+
+ let deferred = this.$q.defer();
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/modals/email-modal/email-modal-view.html'),
+ controller: 'Sdc.ViewModels.EmailModalViewModel',
+ size: 'sdc-sm',
+ backdrop: 'static',
+ resolve: {
+ emailModalModel: ():ViewModels.IEmailModalModel => {
+ return emailModel;
+ }
+ }
+ };
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+
+ };
+
+ openServerMessageModal = (data:Sdc.ViewModels.IServerMessageModalModel):ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html'),
+ controller: 'Sdc.ViewModels.ServerMessageModalViewModel',
+ size: 'sdc-sm',
+ backdrop: 'static',
+ resolve: {
+ serverMessageModalModel: ():Sdc.ViewModels.IServerMessageModalModel => {
+ return data;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+ };
+
+ openClientMessageModal = (data:Sdc.ViewModels.IClientMessageModalModel):ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html'),
+ controller: 'Sdc.ViewModels.ClientMessageModalViewModel',
+ size: 'sdc-sm',
+ backdrop: 'static',
+ resolve: {
+ clientMessageModalModel: ():Sdc.ViewModels.IClientMessageModalModel => {
+ return data;
+ }
+ }
+ };
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance);
+ return deferred.promise;
+ };
+
+ openOnboadrdingModal = (okButtonText:string,currentCsarUUID?:string): ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view.html'),
+ controller: 'Sdc.ViewModels.OnboardingModalViewModel',
+ size: 'sdc-xl',
+ backdrop: 'static',
+ resolve: {
+ okButtonText:():string=>{
+ return okButtonText;
+ },
+ currentCsarUUID:():string=>{
+ return currentCsarUUID||null;
+ }
+ }
+ };
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+ };
+
+
+ openWizard = (componentType: Utils.Constants.ComponentType, component?:Models.Components.Component, importedFile?: any): ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let template = this.$templateCache.get('/app/scripts/view-models/wizard/wizard-creation-base.html');
+
+ let controller:string;
+ if(component){
+ controller = 'Sdc.ViewModels.Wizard.EditWizardViewModel'; //Edit mode
+ } else {
+ if (importedFile){
+ controller = 'Sdc.ViewModels.Wizard.ImportWizardViewModel'; // Import Mode
+ } else {
+ controller = 'Sdc.ViewModels.Wizard.CreateWizardViewModel'; // Create Mode
+ }
+ }
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: template,
+ controller: controller,
+ size: 'sdc-xl',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ data: ():any => {
+ return {
+ 'componentType': componentType,
+ 'component': component,
+ 'importFile':importedFile
+ };
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+ };
+
+ openWizardArtifactModal = (artifact: Models.ArtifactModel, component:Models.Components.Component): ng.IPromise<any> => {
+ let deferred = this.$q.defer();
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'wizard/artifact-form-step/artifact-form-step-view.html'),
+ controller: 'Sdc.ViewModels.Wizard.ArtifactResourceFormStepViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ artifact: ():Models.ArtifactModel => {
+ return artifact;
+ },
+ component: (): Models.Components.Component => {
+ return component;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ deferred.resolve(modalInstance.result);
+ return deferred.promise;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/utils/prototypes.ts b/catalog-ui/app/scripts/utils/prototypes.ts
new file mode 100644
index 0000000000..de961cfc4b
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/prototypes.ts
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+interface String {
+ format(variables:Array<string>):string
+}
+
+interface Array<T> {
+ clean(o: T): Array<T>;
+}
+
+
+/**
+ * This function will replace the %<number> with strings (from array).
+ * Example: "Requested '%1' resource was not found.".format(["MyResource"]);
+ * Note: in case the array contains empty string the function will also remove the '' or the "".
+ */
+if (!String.hasOwnProperty("format")) {
+ String.prototype["format"] = function (variables:Array<string>) : string {
+
+ if (variables===null || variables===undefined || variables.length===0){
+ variables=[''];
+ }
+
+ for (let i=0;i<variables.length;i++){
+ if (variables[i]==='' || variables[i]===null){
+ variables[i]='--DELETE--';
+ }
+ }
+
+ let res = this.replace(/%(\d+)/g, function(_,m) {
+ return variables[--m];
+ });
+
+ res = res.replace(" '--DELETE--' "," ");
+ res = res.replace(" \"--DELETE--\" "," ");
+ res = res.replace("'--DELETE--'","");
+ res = res.replace("\"--DELETE--\"","");
+ res = res.replace("--DELETE--","");
+
+ return res;
+ };
+}
+
+if (!String.hasOwnProperty("capitalizeFirstLetter")) {
+ String.prototype["capitalizeFirstLetter"] = function() {
+ return this.charAt(0).toUpperCase() + this.slice(1);
+ };
+}
+
+if (!String.hasOwnProperty("replaceAll")) {
+ String.prototype["replaceAll"] = function (find:string, replace:string) : string {
+ return this.replace(new RegExp(find, 'g'), replace);
+ };
+}
+
+if (!Array.hasOwnProperty("clean")) {
+ Array.prototype.clean = function (deleteValue) {
+ for (let i = 0; i < this.length; i++) {
+ if (this[i] == deleteValue) {
+ this.splice(i, 1);
+ i--;
+ }
+ }
+ return this;
+ };
+}
+
+if (!Array.prototype.map) {
+ Array.prototype.map = function(callback, thisArg) {
+
+ let T, A, k;
+
+ if (this == null) {
+ throw new TypeError(" this is null or not defined");
+ }
+
+ // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
+ let O = Object(this);
+
+ // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
+ // 3. Let len be ToUint32(lenValue).
+ let len = O.length >>> 0;
+
+ // 4. If IsCallable(callback) is false, throw a TypeError exception.
+ // See: http://es5.github.com/#x9.11
+ if (typeof callback !== "function") {
+ throw new TypeError(callback + " is not a function");
+ }
+
+ // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
+ if (thisArg) {
+ T = thisArg;
+ }
+
+ // 6. Let A be a new array created as if by the expression new Array(len) where Array is
+ // the standard built-in constructor with that name and len is the value of len.
+ A = new Array(len);
+
+ // 7. Let k be 0
+ k = 0;
+
+ // 8. Repeat, while k < len
+ while(k < len) {
+
+ let kValue, mappedValue;
+
+ // a. Let Pk be ToString(k).
+ // This is implicit for LHS operands of the in operator
+ // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
+ // This step can be combined with c
+ // c. If kPresent is true, then
+ if (k in O) {
+
+ // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
+ kValue = O[ k ];
+
+ // ii. Let mappedValue be the result of calling the Call internal method of callback
+ // with T as the this value and argument list containing kValue, k, and O.
+ mappedValue = callback.call(T, kValue, k, O);
+
+ // iii. Call the DefineOwnProperty internal method of A with arguments
+ // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
+ // and false.
+
+ // In browsers that support Object.defineProperty, use the following:
+ // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
+
+ // For best browser support, use the following:
+ A[ k ] = mappedValue;
+ }
+ // d. Increase k by 1.
+ k++;
+ }
+
+ // 9. return A
+ return A;
+ };
+}
diff --git a/catalog-ui/app/scripts/utils/validation-utils.ts b/catalog-ui/app/scripts/utils/validation-utils.ts
new file mode 100644
index 0000000000..7618e7d0e3
--- /dev/null
+++ b/catalog-ui/app/scripts/utils/validation-utils.ts
@@ -0,0 +1,173 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../references"/>
+
+module Sdc.Utils {
+ class basePattern{
+ pattern:RegExp;
+ base:number;
+ constructor(pattern:RegExp, base:number){
+ this.pattern = pattern;
+ this.base = base;
+ }
+ }
+
+ export interface IMapRegex{
+ integer: RegExp;
+ boolean: RegExp;
+ float: RegExp;
+ string: RegExp;
+ }
+
+ export class ValidationUtils {
+
+ static '$inject' = [
+ 'IntegerNoLeadingZeroValidationPattern',
+ 'FloatValidationPattern',
+ 'CommentValidationPattern',
+ 'BooleanValidationPattern',
+ 'NumberValidationPattern',
+ 'LabelValidationPattern',
+ ];
+ private trueRegex : string = '[t][r][u][e]|[t]|[o][n]|[y]|[y][e][s]|[1]';
+ private falseRegex : string = '[f][a][l][s][e]|[f]|[o][f][f]|[n]|[n][o]|[0]';
+ private heatBooleanValidationPattern : RegExp = new RegExp( '^('+this.trueRegex+'|'+this.falseRegex+')$');
+
+
+ constructor(private IntegerNoLeadingZeroValidationPattern:RegExp,
+ private FloatValidationPattern:RegExp,
+ private CommentValidationPattern:RegExp,
+ private BooleanValidationPattern:RegExp,
+ private NumberValidationPattern:RegExp,
+ private LabelValidationPattern:RegExp) {}
+
+ public stripAndSanitize(text:string):string{
+ if(!text){
+ return null;
+ }
+ return text.replace(/\s+/g, ' ').replace(/%[A-Fa-f0-9]{2}/g, '').trim();
+ }
+
+ public getValidationPattern = (validationType:string , parameterType?:string) : RegExp => {
+ switch (validationType){
+ case 'integer':
+ return this.IntegerNoLeadingZeroValidationPattern;
+ case 'float':
+ return this.FloatValidationPattern;
+ case 'number':
+ return this.NumberValidationPattern;
+ case 'string':
+ return this.CommentValidationPattern;
+ case 'boolean':
+ {
+ //Bug Fix DE197437 [Patch]Mismatch between BE to FE regarding supported characters in Boolean filed
+ if( parameterType && parameterType === 'heat'){
+ return this.heatBooleanValidationPattern;
+ }
+ else{
+ return this.BooleanValidationPattern;
+ }
+
+ }
+
+ case 'label':
+ return this.LabelValidationPattern;
+ case 'category':
+ return this.LabelValidationPattern;
+ default :
+ return null;
+ }
+ };
+
+ public getPropertyListPatterns():IMapRegex {
+ return {
+ integer: /^(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)(,?(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+))*$/,
+ string: /^"[\u0000-\u0021\u0023-\u00BF]+"(\s*,?\s*"[\u0000-\u0021\u0023-\u00BF]+")*$/,
+ boolean: /^([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])(,?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]))*$/,
+ float: /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?(,?[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?)*$/
+ };
+ }
+ public getPropertyMapPatterns():IMapRegex {
+ return {
+ integer: /^"\w+"\s*:\s?(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)+(\s*,?\s*"\w+"\s?:\s?(0|[-+]?[1-9][0-9]*|[-+]?0x[0-9a-fA-F]+|[-+]?0o[0-7]+)+)*$/,
+ string: /^"\w+"\s?:\s?"[\u0000-\u0021\u0023-\u00BF]*"(\s*,?\s*"\w+"\s?:\s?"[\u0000-\u0021\u0023-\u00BF]*")*$/,
+ boolean: /^"\w+"\s?:\s?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])(\s*,?\s*"\w+"\s?:\s?([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]))*$/,
+ float: /^"\w+"\s?:\s?[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?(\s*,?\s*"\w+"\s?:\s?[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?f?)*$/
+ };
+ }
+ public validateUniqueKeys(viewValue:string):boolean {
+ if(!viewValue) {
+ return true; //allow empty value
+ }
+
+ let json:string = "{" + viewValue.replace(/\s\s+/g, ' ') + "}";
+ try{
+ let obj:any = JSON.parse(json);
+ /*
+ //Method #1 : check json string length before & after parsing
+ let newJson:string = JSON.stringify(obj);
+ if (newJson.length < json.length) {
+ return false;
+ }*/
+
+ //Method #2 : check how many times we can find "KEY": in json string
+ let result:boolean = true;
+ Object.keys(obj).forEach((key:string) => {
+ result = result && json.split('"' + key + '":').length === 2;
+ });
+ return result;
+
+ }catch(e){
+ return false; //not a valid JSON
+ }
+
+ //return true;
+ }
+
+ public validateJson = (json:string):boolean => {
+ try{
+ JSON.parse(json);
+ return true;
+ }catch(err){
+ console.log('invalid json');
+ return false;
+ }
+ };
+
+ public validateIntRange = (value:string):boolean => {
+
+ let base8 = new basePattern(/^([-+]?0o[0-7]+)$/, 8);
+ let base10 = new basePattern(/^(0|[-+]?[1-9][0-9]*)$/, 10);
+ let base16 = new basePattern(/^([-+]?0x[0-9a-fA-F]+)$/, 16);
+
+ let min:number = -0x80000000;
+ let max:number = 0x7fffffff;
+ let intPatterns:Array<basePattern> = [base8, base10, base16];
+ let matchedBase = _.find(intPatterns, (item)=> {
+ return item.pattern.test(value);
+ });
+
+ let parsed:number = parseInt(value.replace('o',''), matchedBase.base);
+ if(parsed){
+ return min <= parsed && max >= parsed;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts b/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts
new file mode 100644
index 0000000000..93c1dac174
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view-model.ts
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IAddCategoryModalViewModelScope extends ng.IScope {
+ category:Sdc.Services.ICategoryResource;
+ modelType:string;
+ footerButtons: Array<any>;
+ forms:any;
+
+ save():void;
+ close():void;
+ }
+
+ export class AddCategoryModalViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.CategoryResourceService',
+ '$modalInstance',
+ 'parentCategory',
+ 'type'
+ ];
+
+ constructor(
+ private $scope:IAddCategoryModalViewModelScope,
+ private categoryResourceService:Sdc.Services.ICategoryResourceClass,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private parentCategory:Sdc.Services.ICategoryResource,
+ private type:string
+ ){
+ this.initScope();
+ }
+
+ private initScope = ():void => {
+ this.$scope.forms = {};
+ this.$scope.modelType = this.parentCategory ? 'sub category' : 'category';
+ this.$scope.category = new this.categoryResourceService();
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.dismiss();
+ };
+
+ this.$scope.save = ():void => {
+
+ let onOk = (newCategory :Sdc.Services.ICategoryResource):void => {
+ this.$modalInstance.close(newCategory);
+ };
+
+ let onCancel = ():void => {
+ //error
+ };
+
+ if(!this.parentCategory) {
+ this.$scope.category.$save({types: this.type+"s"}, onOk, onCancel);
+ }else{
+ this.$scope.category.$saveSubCategory({types: this.type+"s", categoryId: this.parentCategory.uniqueId}, onOk, onCancel);
+ }
+
+ };
+
+ this.$scope.footerButtons = [
+ {'name': 'OK', 'css': 'blue', 'callback': this.$scope.save, 'disabled': true},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ }
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html b/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html
new file mode 100644
index 0000000000..5718982661
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html
@@ -0,0 +1,41 @@
+<sdc-modal modal="modalInstance"
+ type="classic"
+ class="i-sdc-admin-add-category-modal modal-type-confirmation"
+ header-translate="CREATE_CATEGORY_MODAL_HEADER"
+ buttons="footerButtons"
+ header-translate-values="{'modelType': '{{modelType}}' }"
+ show-close-button="true"
+ hide-background="false"
+>
+
+ <form novalidate class="w-sdc-form" name="forms.editForm">
+
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(editForm.categoryName.$dirty && editForm.categoryName.$invalid)}">
+ <label class="i-sdc-form-label required" translate="CREATE_CATEGORY_MODAL_CATEGORY_NAME"
+ translate-values="{'modelType': '{{modelType}}' }"></label>
+ <input class="i-sdc-form-input"
+ data-ng-model="category.name"
+ data-ng-model-options="{ debounce: 200 }"
+ type="text"
+ name="categoryName"
+ required="required"
+ data-ng-minlength="4"
+ data-ng-pattern="namePattern"
+ maxlength="25"
+ autofocus />
+
+ <div class="input-error" data-ng-show="editForm.categoryName.$dirty && editForm.categoryName.$invalid">
+ <span ng-show="editForm.categoryName.$error.required" translate="CREATE_CATEGORY_MODAL_REQUIRED" translate-values="{'modelType': '{{modelType}}' }"></span>
+ <span ng-show="editForm.categoryName.$error.minlength" translate="CREATE_CATEGORY_MODAL_MINLENGTH" translate-values="{'minlength': '4', 'modelType': '{{modelType}}' }"></span>
+ <span ng-show="editForm.categoryName.$error.pattern" translate="CREATE_CATEGORY_MODAL_PATTERN" translate-values="{'modelType': '{{modelType}}' }"></span>
+ </div>
+
+ </div>
+
+ </div>
+
+ </form>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less b/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less
new file mode 100644
index 0000000000..39d84aab23
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less
@@ -0,0 +1,3 @@
+.i-sdc-admin-add-category-modal {
+
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view-model.ts b/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view-model.ts
new file mode 100644
index 0000000000..d7cbbcc68d
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view-model.ts
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+
+ interface IAdminDashboardViewModelScope extends ng.IScope {
+ version:string;
+ sdcConfig:Models.IAppConfigurtaion;
+ isLoading: boolean;
+ currentTab: string;
+ templateUrl:string;
+ monitorUrl:string;
+ moveToTab(tab:string):void;
+ isSelected(tab:string):boolean;
+ }
+
+
+ export class AdminDashboardViewModel {
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.CacheService',
+ 'sdcConfig'
+ ];
+
+ constructor(private $scope:IAdminDashboardViewModelScope,
+ private cacheService:Services.CacheService,
+ private sdcConfig:Models.IAppConfigurtaion) {
+
+ this.initScope();
+ }
+
+
+ private initScope = ():void => {
+
+ this.$scope.version = this.cacheService.get('version');
+ this.$scope.sdcConfig = this.sdcConfig;
+ this.$scope.monitorUrl = this.$scope.sdcConfig.api.kibana;
+ this.$scope.isSelected=(tab:string):boolean => {
+ return tab===this.$scope.currentTab;
+ }
+
+ this.$scope.moveToTab=(tab:string):void => {
+ if (tab===this.$scope.currentTab){
+ return;
+ }
+ else if(tab === 'USER_MANAGEMENT'){
+ this.$scope.templateUrl = '/app/scripts/view-models/admin-dashboard/user-management/user-management-view.html';
+ }
+ else if(tab ==='CATEGORY_MANAGEMENT'){
+ this.$scope.templateUrl = '/app/scripts/view-models/admin-dashboard/category-management/category-management-view.html';
+ }
+ /* else if(tab ==='ECOMP'){
+ this.$scope.templateUrl = '/app/scripts/view-models/admin-dashboard/ecomp/ecomp-view.html';
+ }*/
+ this.$scope.currentTab = tab;
+ };
+
+ this.$scope.moveToTab('USER_MANAGEMENT');
+
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view.html b/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view.html
new file mode 100644
index 0000000000..063525a4bf
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard-view.html
@@ -0,0 +1,24 @@
+<div class="sdc-admin-container">
+
+ <!--<ecomp-header menu-data="menuItems" version="{{version}}" clickable-logo="false"></ecomp-header>-->
+
+ <nav class="sdc-admin-top-bar-menu">
+ <button class="sdc-admin-top-bar-menu-tab" data-tests-id="usermanagmenttab"
+ data-ng-class="{'selected': isSelected('USER_MANAGEMENT')}"
+ data-ng-click="moveToTab('USER_MANAGEMENT')"
+ translate="USER_MANAGEMENT">
+ </button>
+ <button class="sdc-admin-top-bar-menu-tab" data-tests-id="categorymanagmenttab"
+ data-ng-class="{'selected': isSelected('CATEGORY_MANAGEMENT')}"
+ data-ng-click="moveToTab('CATEGORY_MANAGEMENT')"
+ translate="CATEGORY_MANAGEMENT">
+ </button>
+ <a href={{monitorUrl}} target="_blank" ng-show="monitorUrl!=''" >
+ <button class="sdc-admin-top-bar-menu-monitor-btn" translate="MONITOR" data-tests-id="monitor"></button>
+ </a>
+ </nav>
+
+ <div class="sdc-admin-body">
+ <ng-include src="templateUrl" ng-if="true"></ng-include>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard.less b/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard.less
new file mode 100644
index 0000000000..874a02c431
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/admin-dashboard.less
@@ -0,0 +1,49 @@
+.sdc-admin-container{
+ height: 100%;
+
+ .sdc-admin-top-bar-menu{
+ .bg_k;
+ height: @top_nav_admin_height;
+ padding-left:260px;
+ .box-shadow(-1px 0px 3px 0px rgba(0, 0, 0, 0.33));
+ position: absolute;
+ top: @header_height;
+ left: 0;
+ right: 0;
+ z-index: 2;
+
+ .sdc-admin-top-bar-menu-tab{
+ .b_17;
+ .hand;
+ height: 44px;
+ background-color: transparent;
+ position: relative;
+ padding: 0px 10px 0px 10px;
+ border: none;
+ outline: none;
+ margin-right: 15px;
+ &.selected {
+ outline: none;
+ border-bottom: solid 4px @color_t;
+ }
+ }
+ .sdc-admin-top-bar-menu-monitor-btn{
+ .bg_a;
+ .c_6;
+ float: right;
+ border: none;
+ position: relative;
+ padding: 11px 24px;
+ }
+ }
+
+ .sdc-admin-body{
+ .bg_n;
+ padding: 40px 260px 60px 260px;
+ position: absolute;
+ top: @top_nav_admin_height;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view-model.ts b/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view-model.ts
new file mode 100644
index 0000000000..a3ad7a2714
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view-model.ts
@@ -0,0 +1,197 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface ICategoryManagementViewModelScope extends ng.IScope {
+ SERVICE:string;
+ RESOURCE:string;
+ categoriesToShow: Array<Sdc.Services.ICategoryResource>;
+ serviceCategories: Array<Sdc.Services.ICategoryResource>;
+ resourceCategories: Array<Sdc.Services.ICategoryResource>;
+ selectedCategory: Sdc.Services.ICategoryResource;
+ selectedSubCategory: Sdc.Services.ICategoryResource;
+ modalInstance:ng.ui.bootstrap.IModalServiceInstance;
+ isLoading:boolean;
+ type:string;
+ namePattern:RegExp;
+
+ selectCategory(category:Sdc.Services.ICategoryResource) :void;
+ selectSubCategory(subcategory:Sdc.Services.ICategoryResource) :void;
+ selectType(type:string) :void;
+ deleteCategory(category:Sdc.Services.ICategoryResource, subCategory:Sdc.Services.ICategoryResource) :void;
+ createCategoryModal(parentCategory:Sdc.Services.ICategoryResource) :void;
+ }
+
+ export class CategoryManagementViewModel {
+ static '$inject' = [
+ '$scope',
+ 'sdcConfig',
+ 'Sdc.Services.CacheService',
+ '$templateCache',
+ '$modal',
+ '$filter',
+ 'ValidationUtils',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:ICategoryManagementViewModelScope,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private cacheService:Services.CacheService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $filter:ng.IFilterService,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private ModalsHandler: Utils.ModalsHandler
+ ) {
+
+ this.initScope();
+ this.$scope.selectType(Sdc.Utils.Constants.ComponentType.SERVICE.toLocaleLowerCase());
+
+ }
+
+ private initScope = ():void => {
+ let scope:ICategoryManagementViewModelScope = this.$scope;
+ scope.SERVICE = Sdc.Utils.Constants.ComponentType.SERVICE.toLocaleLowerCase();
+ scope.RESOURCE = Sdc.Utils.Constants.ComponentType.RESOURCE.toLocaleLowerCase();
+
+ scope.namePattern = this.ValidationUtils.getValidationPattern('cssClasses');
+
+ scope.selectCategory = (category :Sdc.Services.ICategoryResource) => {
+ if(scope.selectedCategory !== category) {
+ scope.selectedSubCategory = null;
+ }
+ scope.selectedCategory = category;
+ };
+ scope.selectSubCategory = (subcategory :Sdc.Services.ICategoryResource) => {
+ scope.selectedSubCategory = subcategory;
+ };
+ scope.selectType = (type:string):void => {
+ if (scope.type !== type) {
+ scope.selectedCategory = null;
+ scope.selectedSubCategory = null;
+ }
+
+ scope.type = type;
+ scope.categoriesToShow = scope[type + 'Categories'];
+ };
+
+ scope.createCategoryModal = (parentCategory:Sdc.Services.ICategoryResource):void => {
+ //can't create a sub category for service
+ if(parentCategory && scope.type === Sdc.Utils.Constants.ComponentType.SERVICE.toLowerCase()) {
+ return;
+ }
+
+ let type:string = scope.type;
+
+ let onOk = (newCategory :Sdc.Services.ICategoryResource):void => {
+ if(!parentCategory) {
+ scope[type + 'Categories'].push(newCategory);
+ }else{
+ if(!parentCategory.subcategories) {
+ parentCategory.subcategories = [];
+ }
+ parentCategory.subcategories.push(newCategory);
+ }
+ };
+
+ let onCancel = ():void => {
+
+ };
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.html'),
+ controller: 'Sdc.ViewModels.AddCategoryModalViewModel',
+ size: 'sdc-xsm',
+ backdrop: 'static',
+ scope: scope,
+ resolve: {
+ parentCategory: function () {
+ return parentCategory;
+ },
+ type: function () {
+ return type;
+ }
+ }
+ };
+
+ scope.modalInstance = this.$modal.open(modalOptions);
+ scope.modalInstance.result.then(onOk, onCancel);
+
+ };
+
+ scope.deleteCategory = (category: Sdc.Services.ICategoryResource, subCategory: Sdc.Services.ICategoryResource): void => {
+
+ let onOk = ():void => {
+
+ scope.isLoading = true;
+ let type:string = scope.type;
+
+ let onError = (response):void => {
+ scope.isLoading = false;
+ console.info('onFaild', response);
+ };
+
+ let onSuccess = (response: any) :void => {
+ let arr:Array<Sdc.Services.ICategoryResource>;
+
+ if(!subCategory) {
+ arr = this.$scope[type + 'Categories'];
+ arr.splice(arr.indexOf(category), 1);
+ if(category === scope.selectedCategory) {
+ scope.selectedCategory = null;
+ scope.selectedSubCategory = null;
+ }
+ } else {
+ arr = category.subcategories;
+ arr.splice(arr.indexOf(subCategory), 1);
+ }
+
+ scope.isLoading = false;
+ };
+
+ if(!subCategory) {
+ category.$delete({
+ types: type+"s",
+ categoryId: category.uniqueId
+ }
+ , onSuccess, onError);
+ } else {
+ category.$deleteSubCategory({
+ types: type+"s",
+ categoryId: category.uniqueId,
+ subCategoryId: subCategory.uniqueId,
+ }
+ , onSuccess, onError);
+ }
+ };
+ let modelType:string = subCategory ? 'sub category' : 'cssClasses';
+ let title:string = this.$filter('translate')("DELETE_CATEGORY_MODAL_HEADER", "{'modelType': '" + modelType +"' }");
+ let message:string = this.$filter('translate')("DELETE_CATEGORY_MODAL_CATEGORY_NAME", "{'modelType': '" + modelType +"' }");
+
+ this.ModalsHandler.openConfirmationModal(title, message, false, 'sdc-xsm').then(onOk);
+ };
+
+ this.$scope.serviceCategories = this.cacheService.get('serviceCategories');
+ this.$scope.resourceCategories = this.cacheService.get('resourceCategories');
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view.html b/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view.html
new file mode 100644
index 0000000000..95a002d3d7
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management-view.html
@@ -0,0 +1,53 @@
+<div data-ng-controller="Sdc.ViewModels.CategoryManagementViewModel" class="category-management">
+
+ <loader data-display="isLoading"></loader>
+
+ <div class="row">
+
+ <div class="col-sm-6">
+
+ <h4>
+ <span class="hand selected" data-ng-click="selectType(SERVICE)" data-tests-id="servicecategoryheader"
+ data-ng-class="{'selected': type === SERVICE}" translate="SERVICE_CATEGORY_HEADER"></span>
+ <span class="hand" data-ng-click="selectType(RESOURCE)" data-tests-id="resourcecategoryheader"
+ data-ng-class="{'selected': type === RESOURCE}" translate="RESOURCE_CATEGORY_HEADER"></span>
+ </h4>
+ <span data-ng-click="createCategoryModal(null)" translate="ADD_CATEGORY" data-tests-id="newcategory"></span>
+
+ <perfect-scrollbar class="perfect-scrollbar">
+ <ul>
+ <li data-ng-repeat="category in categoriesToShow"
+ data-ng-class="{'selected': selectedCategory === category, 'gray': selectedSubCategory}"
+ data-ng-click="selectCategory(category)"
+ data-tests-id="{{ type === SERVICE ? 'servicecategory' : 'resourcecategory' }}">
+ {{category.name}}
+
+ <!--<button class="sprite e-sdc-small-icons-delete" data-ng-click="deleteCategory(category, null)" type="button"></button>-->
+ <!--button class="sprite e-sdc-small-icons-pad" data-ng-click="" type="button"></button-->
+ </li>
+ </ul>
+ </perfect-scrollbar>
+ </div>
+
+ <div class="col-sm-6">
+
+ <h4><span translate="SUBCATEGORY_HEADER" data-tests-id="subcategoryheader"></span></h4>
+ <span data-ng-if="type === RESOURCE && selectedCategory" data-ng-click="selectedCategory ? createCategoryModal(selectedCategory) : ''" translate="ADD_SUBCATEGORY" data-tests-id="newsubcategory"></span>
+
+ <perfect-scrollbar class="perfect-scrollbar">
+ <ul>
+ <li data-ng-repeat="subcategory in selectedCategory.subcategories"
+ data-ng-class="{'selected': selectedSubCategory === subcategory}"
+ data-ng-click="selectSubCategory(subcategory)"
+ data-tests-id="subcategory">
+ {{subcategory.name}}
+
+ <!--<button class="sprite e-sdc-small-icons-delete" data-ng-click="deleteCategory(selectedCategory, subcategory)" type="button"></button>-->
+ <!--button class="sprite e-sdc-small-icon-pad" data-ng-click="" type="button"></button-->
+ </li>
+ </ul>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management.less b/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management.less
new file mode 100644
index 0000000000..011122c9e8
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/category-management/category-management.less
@@ -0,0 +1,118 @@
+
+.category-management {
+
+ .row {
+ display: table;
+ width: 70%;
+ min-width: 800px;
+ margin: auto;
+
+ [class*="col-"] {
+ float: none;
+ display: table-cell;
+ vertical-align: top;
+ background-color: white;
+ border: 1px solid #c1c1c1;
+ padding: 0;
+
+ &:not(:last-child) {
+ border-right: none;
+ }
+
+ h4 {
+ float:left;
+ color:white;
+ background-color: rgb(155,168,176);
+ margin: 0;
+ padding: 0px 0px 0px 16px;
+ width: 100%;
+ height: 31px;
+ font-size: 15px;
+
+ span{
+ display: inline-block;
+ line-height: 30px;
+ margin-right: 5px;
+ padding: 0 7px;
+
+ &.selected {
+ border-bottom: 2px #067ab4 solid;
+ }
+ }
+ }
+ h4+span{
+ .hand;
+ float:right;
+ display: inline-block;
+ background-color: rgb(59,124,156);
+ text-align: center;
+ padding: 5.5px 0px;
+ width: 60px;
+ color: white;
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 1;
+ color: white;
+ }
+
+
+ .perfect-scrollbar {
+ width: 100%;
+ height: 500px;
+ margin-top: 40px;
+ margin-bottom: 15px;
+ }
+
+ ul {
+ clear: both;
+ margin: 5px 0 10px 0;
+ list-style-type: none;
+ padding: 0;
+ position: relative;
+
+ li{
+ .hand;
+ padding: 0 8px;
+ text-indent: 9px;
+ font-size: 13px;
+ line-height: 25px;
+ border: 1px solid white;
+ border-right: none;
+ border-left: none;
+ margin-right: 5px;
+
+ button {
+ background-color: transparent;
+ border: none;
+ float: right;
+ margin: 5px 3px;
+ display: none;
+ }
+
+ &:hover {
+ background-color: #d9e6ec;
+ color: #3b7b9b;
+ border-color: #d9e6ec;
+
+ button {
+ display: inline-block;
+ }
+ }
+ &.selected {
+ background-color: rgb(219,230,236);
+ color: #3b7b9b;
+ border-color: rgba(59, 123, 155, 0.42);
+
+ &.gray {
+ background-color: rgba(155, 168, 176, 0.09);
+ border-color: white;
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/ecomp/ecomp-view.html b/catalog-ui/app/scripts/view-models/admin-dashboard/ecomp/ecomp-view.html
new file mode 100644
index 0000000000..7c89b545c5
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/ecomp/ecomp-view.html
@@ -0,0 +1 @@
+<div></div>
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view-model.ts b/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view-model.ts
new file mode 100644
index 0000000000..3921d0cf8f
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view-model.ts
@@ -0,0 +1,220 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+
+ import IUserProperties = Sdc.Models.IUserProperties;
+ 'use strict';
+
+ interface IUserManagementViewModelScope extends ng.IScope {
+ sdcConfig:Models.IAppConfigurtaion;
+ usersList: Array<Models.IUserProperties>;
+ isLoading: boolean;
+ isNewUser: boolean;
+ sortBy:string;
+ reverse:boolean;
+ tableHeadersList:any;
+ roles:Array<string>;
+ newUser: Models.IUser;
+ currentUser: Sdc.Services.IUserResource;
+ userIdValidationPattern: RegExp;
+ editForm:ng.IFormController;
+ getAllUsers():void;
+ editUserRole(user:IUserProperties);
+ sort(sortBy:string): void;
+ createUser(): void;
+ deleteUser(userId:string) : void;
+ onEditUserPressed(user:IUserProperties): void;
+ saveUserChanges(user:IUserProperties) :void;
+ getTitle(role:string): string;
+ clearForm():void;
+
+ }
+
+
+ export class UserManagementViewModel {
+ static '$inject' = [
+ '$scope',
+ 'sdcConfig',
+ 'Sdc.Services.UserResourceService',
+ '$templateCache',
+ '$modal',
+ 'UserIdValidationPattern',
+ '$filter',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:IUserManagementViewModelScope,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private userResourceService:Sdc.Services.IUserResourceClass,
+ private $templateCache:ng.ITemplateCacheService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private UserIdValidationPattern:RegExp,
+ private $filter:ng.IFilterService,
+ private ModalsHandler: Utils.ModalsHandler
+ ) {
+
+ this.initScope();
+
+ }
+
+
+
+ private getAllUsers = ():void => {
+ this.$scope.isLoading = true;
+
+ let onError = (response) => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+ };
+ let onSuccess = (response: Array<Models.IUserProperties>) => {
+ this.$scope.usersList = response;
+ _.forEach(this.$scope.usersList,(user:any,i:number)=>{
+ user.index = i;
+ });
+ this.$scope.isLoading = false;
+ };
+ this.userResourceService.getAllUsers(onSuccess, onError);
+ };
+
+ private updateUserFilterTerm = (user: IUserProperties): void =>{
+ user.filterTerm = user.firstName + ' ' + user.lastName + ' ' + user.userId + ' ' + user.email + ' ' + user.role + ' ' + this.$filter('date')(user.lastLoginTime, "MM/dd/yyyy");
+ };
+
+ private initScope = ():void => {
+ let self=this;
+
+ this.$scope.tableHeadersList = [
+ {title: "First Name", property: 'firstName'},
+ {title: "Last Name", property: 'lastName'},
+ {title: this.$filter('translate')("USER_MANAGEMENT_TABLE_HEADER_USER_ID"), property: 'userId'},
+ {title: "Email", property: 'email'},
+ {title: "Role", property: 'role'},
+ {title: "Last Active", property: 'lastLoginTime'}
+ ];
+ this.$scope.userIdValidationPattern = this.UserIdValidationPattern;
+ this.$scope.sortBy = 'lastLoginTime';
+ this.$scope.reverse = false;
+ this.$scope.roles = this.sdcConfig.roles;
+ this.$scope.isNewUser = false;
+ this.$scope.currentUser = this.userResourceService.getLoggedinUser();
+ this.getAllUsers();
+
+ let resource : Services.IUserResource = <Services.IUserResource>{};
+ this.$scope.newUser = new Sdc.Models.User(resource);
+
+ this.$scope.sort = (sortBy:string):void => {//default sort by descending last update. default for alphabetical = ascending
+ this.$scope.isNewUser = false;
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? ( !this.$scope.reverse) : this.$scope.reverse = false;
+ this.$scope.sortBy = sortBy;
+ };
+
+ this.$scope.createUser = () : void => {
+
+ let onError = (response) => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+ };
+
+ let onSuccess = (response: Models.IUserProperties) => {
+ this.$scope.newUser.resource['index'] = this.$scope.usersList.length;
+ this.$scope.newUser.resource.lastLoginTime = "0";
+ this.$scope.newUser.resource.status = response.status;
+ this.updateUserFilterTerm(this.$scope.newUser.resource);
+ this.$scope.usersList.unshift(this.$scope.newUser.resource);
+ this.$scope.isNewUser = true;
+ this.$scope.sortBy = 'index';
+ this.$scope.reverse = true;
+ this.$scope.isLoading = false;
+ this.$scope.newUser = new Sdc.Models.User(null);
+ this.$scope.editForm.$setPristine();
+ let _self = this;
+ setTimeout(function () {
+ _self.$scope.isNewUser = false;
+ }, 7000);
+ };
+ this.userResourceService.createUser({ userId: this.$scope.newUser.resource.userId, role: this.$scope.newUser.resource.role}, onSuccess, onError);
+ };
+
+
+ this.$scope.onEditUserPressed = (user:IUserProperties): void => {
+ user.isInEditMode = true;
+ user.tempRole = user.role;
+ };
+
+ this.$scope.editUserRole = (user:IUserProperties): void => {
+ let roleBeforeUpdate: string = user.role;
+ user.role= user.tempRole;
+
+ let onError = (response) => {
+ this.$scope.isLoading = false;
+ user.role = roleBeforeUpdate;
+ console.info('onFaild', response);
+ };
+ let onSuccess = (response: any) => {
+ this.$scope.isLoading = false;
+ user.tempRole = user.role;
+ this.updateUserFilterTerm(user);
+ };
+
+ this.userResourceService.editUserRole({ id: user.userId, role: user.role}, onSuccess, onError);
+ };
+
+ this.$scope.saveUserChanges = (user:IUserProperties): void => {
+ if(user.tempRole != user.role){
+ this.$scope.editUserRole(user)
+ }
+ user.isInEditMode = false;
+ };
+
+ this.$scope.deleteUser = (userId:string): void => {
+
+ let onOk = ():void => {
+ this.$scope.isLoading = true;
+
+ let onError = (response):void => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+ };
+
+ let onSuccess = (response: any) :void => {
+ _.remove(this.$scope.usersList, {userId: userId });
+ this.$scope.isLoading = false;
+ };
+ this.userResourceService.deleteUser({ id: userId}, onSuccess, onError);
+ };
+
+ let title:string = this.$filter('translate')("USER_MANAGEMENT_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("USER_MANAGEMENT_VIEW_DELETE_MODAL_TEXT");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+
+ this.$scope.getTitle = (role:string):string =>{
+ return role.toLowerCase().replace('governor','governance_Rep').replace('_',' ');
+ };
+
+ this.$scope.clearForm =():void =>{
+ if(!this.$scope.editForm['contactId'].$viewValue && !this.$scope.editForm['role'].$viewValue){
+ this.$scope.editForm.$setPristine();
+ }
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view.html b/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view.html
new file mode 100644
index 0000000000..26e720b044
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management-view.html
@@ -0,0 +1,102 @@
+<div ng-controller="Sdc.ViewModels.UserManagementViewModel">
+ <loader data-display="isLoading"></loader>
+ <div class="sdc-user-management-top-bar">
+ <div class="sdc-user-management-top-bar-search-container">
+ <label class="sdc-user-management-top-bar-search-text">Search User</label>
+ <input type="text" class="sdc-user-management-top-bar-form-input" placeholder="{{ USER_MANAGEMENT_SEARCH_LABEL | translate }}" data-ng-model="search.filterTerm" ng-model-options="{ debounce: 500 }" data-tests-id="searchbox" />
+ <span class="w-sdc-search-icon" data-ng-class="{'cancel':search.filterTerm, 'magnification':!search.filterTerm}" data-ng-click="search.filterTerm=''" ></span>
+ </div>
+ <div class="vertical-border-container">
+ <div class="vertical-border"></div>
+ </div>
+ <form class="sdc-user-management-top-bar-create-user-container w-sdc-form" name="editForm">
+ <label class="sdc-user-management-top-bar-title">Create New User</label>
+ <div class="sdc-user-management-top-bar-wrapper">
+ <div class="i-sdc-form-item sdc-user-management-top-bar-form-container" data-ng-class="{error:(editForm.contactId.$dirty && editForm.contactId.$invalid)}">
+ <input ng-focus="search.filterTerm=''" type="text"
+ data-ng-model="newUser.resource.userId"
+ class="i-sdc-form-input"
+ placeholder="{{ USER_MANAGEMENT_SEARCH_TEXT | translate}}"
+ data-ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 750, 'blur': 0 } }"
+ name="contactId"
+ data-ng-pattern="userIdValidationPattern"
+ data-ng-change="clearForm()"
+ data-ng-blur="clearForm()"
+ data-required
+ data-tests-id="newuserid" />
+
+ <div class="input-error" data-ng-show="editForm.contactId.$dirty && editForm.contactId.$invalid">
+ <span ng-show="editForm.contactId.$error.required" translate="NEW_USER_ERROR_USER_ID_REQUIRED"></span>
+ <span ng-show="editForm.contactId.$error.pattern" translate="NEW_USER_ERROR_USER_ID_NOT_VALID"></span>
+ </div>
+ </div>
+ <div class="i-sdc-form-item sdc-user-management-top-bar-form-container" data-ng-class="{error:(editForm.role.$dirty && editForm.role.$invalid
+ && editForm.contactId.$viewValue)}">
+ <select class="i-sdc-form-select capitalize"
+ data-required
+ name="role"
+ data-tests-id="selectrole"
+ data-ng-model = "newUser.resource.role"
+ data-ng-options="role as (getTitle(role)) for role in roles | orderBy:'role'"
+ ng-focus="search.filterTerm=''">
+ <option value="">Select Role</option>
+ </select>
+ <div class="input-error" data-ng-show="editForm.role.$dirty && editForm.role.$invalid && editForm.contactId.$viewValue">
+ <span ng-show="editForm.role.$error.required" translate="NEW_USER_ERROR_ROLE_REQUIRED"></span>
+ </div>
+ </div>
+ <button data-tests-id="creategreen" data-ng-disabled="editForm.$invalid" class="sdc-user-management-top-bar-create-btn" ng-click="search.filterTerm = '' ; createUser()">Create</button>
+ </div>
+ </form>
+ </div>
+
+
+ <div class="sdc-user-management-table-container-flex">
+
+ <div class="sdc-user-management-table">
+ <div class="head sdc-user-management-flex-container">
+ <div class="sdc-user-management-table-header head-row hand sdc-user-management-flex-item" data-tests-id="th{{header.title}}" ng-repeat="header in tableHeadersList" ng-click="sort(header.property)">{{header.title}}
+ <span ng-if="sortBy === header.property" class="sdc-user-management-table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="sdc-user-management-table-no-text-header head-row sdc-user-management-flex-item"></div>
+ <div class="sdc-user-management-table-no-text-header head-row sdc-user-management-flex-item"></div>
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div ng-init="user.filterTerm = user.firstName + ' ' + user.lastName + ' ' + user.userId + ' ' + user.email + ' ' + user.role + ' ' + (user.lastLoginTime | date: 'MM/dd/yyyy')"
+ ng-repeat="user in usersList | filter: search | orderBy:sortBy:reverse"
+ data-ng-class="{'sdc-user-management-table-new-user-row': (isNewUser && $first), 'sdc-user-management-table-row-edit-mode': user.isInEditMode}"
+ class="sdc-user-management-flex-container data-row" data-tests-id="tdRow">
+
+ <div sdc-smart-tooltip class="sdc-user-management-table-col-general sdc-user-management-flex-item" data-tests-id="td{{tableHeadersList[0].title}}">{{user.firstName || '---'}}</div>
+ <div sdc-smart-tooltip class="sdc-user-management-table-col-general sdc-user-management-flex-item" data-tests-id="td{{tableHeadersList[1].title}}">{{user.lastName || '---' }}</div>
+ <div class="sdc-user-management-table-col-userid sdc-user-management-flex-item" data-tests-id="td{{tableHeadersList[2].title}}">{{user.userId || '---'}}</div>
+ <div sdc-smart-tooltip class="sdc-user-management-table-col-general sdc-user-management-flex-item" data-tests-id="td{{tableHeadersList[3].title}}">{{user.email || '---'}}</div>
+ <div class="sdc-user-management-table-col-general sdc-user-management-flex-item" data-tests-id="td{{tableHeadersList[4].title}}">
+ <div class="sdc-user-management-table-role-select capitalize sdc-user-management-table-role-label"
+ data-ng-if="!user.isInEditMode"
+ data-ng-bind="getTitle(user.role)"></div>
+ <select class="sdc-user-management-table-role-select capitalize"
+ data-tests-id="tdselectrole"
+ data-ng-if="user.isInEditMode"
+ data-ng-model="user.tempRole"
+ data-ng-options="role as (getTitle(role)) for role in roles | orderBy:'role'">
+ </select>
+ </div>
+ <div class="sdc-user-management-table-col-general sdc-user-management-flex-item" data-tests-id="td{{tableHeadersList[5].title}}">{{user.lastLoginTime == 0 ? 'Waiting' : (user.lastLoginTime | date:'MM/dd/yyyy')}}</div>
+ <div class="sdc-user-management-table-btn-col sdc-user-management-flex-item">
+ <button data-ng-disabled="user.isInEditMode" data-ng-hide="user.isInEditMode || currentUser.userId === user.userId" class="sdc-user-management-table-edit-btn" ng-click="onEditUserPressed(user)" data-tests-id="updateuser{{user.userId}}"> </button>
+ <button data-ng-show="user.isInEditMode" class="sdc-user-management-table-save-btn" ng-click="saveUserChanges(user)" data-tests-id="tdsave"> </button>
+ </div>
+ <div class="sdc-user-management-table-btn-col sdc-user-management-flex-item">
+ <button data-ng-hide="currentUser.userId === user.userId" class="sdc-user-management-table-delete-btn" ng-click="deleteUser(user.userId)" data-tests-id="delete{{user.userId}}"> </button>
+ </div>
+
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management.less b/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management.less
new file mode 100644
index 0000000000..934faab9e7
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/admin-dashboard/user-management/user-management.less
@@ -0,0 +1,251 @@
+.sdc-user-management-top-bar {
+ display: flex;
+ width: 100%;
+ label {
+ .i_17;
+ }
+ .sdc-user-management-top-bar-form-input,
+ .sdc-user-management-top-bar-form-select {
+ .b_9;
+ color: @color_b;
+ height: 28px;
+ padding-left: 10px;
+ border-radius: 2px;
+ border: 1px solid @border_color_f;
+ }
+
+ .sdc-user-management-top-bar-search-container {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ width: 400px;
+
+ label {
+ margin-bottom: 20px;
+ }
+
+ .w-sdc-search-icon {
+ right: 11px;
+ top: 49px;
+ }
+ }
+ .vertical-border-container {
+ min-width: 50px;
+ margin: 0px auto;
+
+ .vertical-border {
+
+ width: 1px;
+ height: 70px;
+ background-color: @color_e;
+ display: table;
+ margin: 0 auto;
+ }
+ }
+
+ .sdc-user-management-top-bar-wrapper {
+ display: flex;
+ }
+
+ .sdc-user-management-top-bar-title {
+ .i_17;
+ font-weight: bold;
+ }
+
+ .sdc-user-management-top-bar-create-user-container {
+
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ float: right;
+ padding-top: 0px;
+ text-align: left;
+ width: 650px;
+
+ label {
+ margin-bottom: 20px;
+ }
+
+ .sdc-user-management-top-bar-form-container {
+ width: 233px;
+ margin-right: 35px;
+ }
+
+ .sdc-user-management-top-bar-create-btn {
+ .w-sdc-btn-light-green;
+ height: 30px;
+ width: 100px;
+ line-height: 0px;
+ padding-bottom: 3px;
+ margin-right: 0px;
+ }
+ }
+}
+
+
+.sdc-user-management-table-container-flex {
+ height: 650px;
+ margin-top: 35px;
+ .sdc-user-management-table {
+ width: 100%;
+ border: 1px solid @color_m;
+ .head {
+ .bg_m;
+ .head-row {
+ .c_18;
+ font-weight: bold;
+
+ border-right: 1px solid @border_color_d;
+
+ .sdc-user-management-table-header-sort-arrow {
+ display: inline-block;
+ background-color: transparent;
+ border: none;
+ .c_9;
+ width: 0;
+ height: 0;
+ float: right;
+ margin: 8px 8px 0px 0px;
+ &.up {
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid;
+ }
+ &.down {
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid;
+ }
+ }
+ }
+ .sdc-user-management-table-header:hover {
+ .bg_j;
+ }
+
+ }
+ .body {
+ .scrollbar-container {
+ max-height: 421px;
+ .perfect-scrollbar;
+ }
+ .b_9;
+
+ .data-row {
+ &:nth-of-type(odd) {
+ .bg_c;
+ }
+ &.sdc-user-management-table-new-user-row {
+
+ animation: change 7s step-end both;
+
+ @keyframes change {
+ from {
+ color: @color_z
+ }
+ to {
+ color: @color_b
+ }
+ }
+ }
+ &.sdc-user-management-table-row-edit-mode {
+ .bg_j;
+ }
+ div {
+
+ border-right: 1px solid @border_color_d;
+
+ &:last-child {
+ border-right: none;
+ }
+
+ .sdc-user-management-table-role-select {
+ background-color: transparent;
+ border: 0;
+ width: 100%;
+
+ }
+ .sdc-user-management-table-role-label {
+ margin-left:4px;
+ }
+
+ }
+
+ }
+ .data-row:hover {
+ .bg_j;
+ }
+
+ }
+
+ .sdc-user-management-table-btn-col {
+
+ line-height: 0px;
+ text-align: center;
+ .sdc-user-management-table-delete-btn {
+ background-color: transparent;
+ border: none;
+ .sprite;
+ .sprite.e-sdc-small-icon-delete;
+ opacity: 0.7;
+ }
+ .sdc-user-management-table-edit-btn {
+ background-color: transparent;
+ border: none;
+ .sprite;
+ .e-sdc-small-icon-pencil;
+ opacity: 0.7;
+ }
+ .sdc-user-management-table-save-btn {
+ background-color: transparent;
+ border: none;
+ .sprite;
+ .sprite.e-sdc-green-save;
+ }
+ }
+
+ }
+
+ .sdc-user-management-flex-container {
+ display: flex;
+ }
+
+ .sdc-user-management-flex-item {
+ width:10px;
+ padding: 5px;
+ text-align: center;
+ }
+
+ .sdc-user-management-flex-item:nth-child(1) {
+ flex-grow: 5;
+ }
+
+ .sdc-user-management-flex-item:nth-child(2) {
+ flex-grow: 7;
+ }
+
+ .sdc-user-management-flex-item:nth-child(3) {
+ flex-grow: 4;
+ }
+
+ .sdc-user-management-flex-item:nth-child(4) {
+ flex-grow: 8;
+ }
+
+ .sdc-user-management-flex-item:nth-child(5) {
+ flex-grow: 8;
+ }
+
+ .sdc-user-management-flex-item:nth-child(6) {
+ flex-grow: 8;
+ }
+
+ .sdc-user-management-flex-item:nth-child(7) {
+ flex-grow: 1;
+ }
+
+ .sdc-user-management-flex-item:nth-child(8) {
+ flex-grow: 1;
+ }
+
+}
+
diff --git a/catalog-ui/app/scripts/view-models/catalog/catalog-view-model.ts b/catalog-ui/app/scripts/view-models/catalog/catalog-view-model.ts
new file mode 100644
index 0000000000..bf37e92e56
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/catalog/catalog-view-model.ts
@@ -0,0 +1,312 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+
+ interface Checkboxes {
+ componentTypes:Array<string>;
+ resourceSubTypes:Array<string>;
+ }
+
+ interface CheckboxesFilter {
+ // Types
+ selectedComponentTypes:Array<string>;
+ selectedResourceSubTypes:Array<string>;
+ // Categories
+ selectedCategoriesModel:Array<string>;
+ // Statuses
+ selectedStatuses:Array<string>;
+ }
+
+ interface Gui {
+ isLoading: boolean;
+ onResourceSubTypesClick:Function;
+ onComponentTypeClick:Function;
+ onCategoryClick:Function;
+ onSubcategoryClick:Function;
+ onGroupClick:Function;
+ }
+
+ export interface ICatalogViewModelScope extends ng.IScope {
+ checkboxes:Checkboxes;
+ checkboxesFilter:CheckboxesFilter;
+ gui:Gui;
+
+ categories: Array<Models.IMainCategory>;
+ confStatus: Models.IConfigStatuses;
+ sdcMenu:Models.IAppMenu;
+ catalogFilterdItems: Array<Models.Components.Component>;
+ expandedSection: Array<string>;
+ actionStrategy: any;
+ user: Models.IUserProperties;
+ catalogMenuItem: any;
+ version:string;
+ sortBy:string;
+ reverse:boolean;
+
+ //this is for UI paging
+ numberOfItemToDisplay:number;
+ isAllItemDisplay: boolean;
+
+ openViewerModal(isResource: boolean, uniqueId: string): void;
+ changeLifecycleState(entity:any,state:string): void;
+ sectionClick (section:string):void;
+ order(sortBy:string): void;
+ getNumOfElements(num:number): string;
+ goToComponent(component:Models.Components.Component):void;
+ raiseNumberOfElementToDisplay():void;
+ }
+
+ export class CatalogViewModel {
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ 'Sdc.Services.EntityService',
+ 'sdcConfig',
+ 'sdcMenu',
+ '$state',
+ '$q',
+ 'Sdc.Services.UserResourceService',
+ '$modal',
+ '$templateCache',
+ 'Sdc.Services.CacheService',
+ 'ComponentFactory',
+ 'ChangeLifecycleStateHandler',
+ 'ModalsHandler',
+ 'MenuHandler'
+ ];
+
+ constructor(private $scope:ICatalogViewModelScope,
+ private $filter:ng.IFilterService,
+ private EntityService:Services.EntityService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private sdcMenu:Models.IAppMenu,
+ private $state:any,
+ private $q:any,
+ private userResourceService:Sdc.Services.IUserResourceClass,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private cacheService:Services.CacheService,
+ private ComponentFactory: Sdc.Utils.ComponentFactory,
+ private ChangeLifecycleStateHandler: Sdc.Utils.ChangeLifecycleStateHandler,
+ private OpenViewModalHandler: Utils.ModalsHandler,
+ private MenuHandler: Utils.MenuHandler
+ ) {
+
+ this.initScopeMembers();
+ this.initCatalogData(); // Async task to get catalog from server.
+ this.initScopeMethods();
+ }
+
+ private initCatalogData = ():void => {
+ let onSuccess = (followedResponse:Array<Models.Components.Component>):void => {
+ this.$scope.catalogFilterdItems = followedResponse;
+ this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.catalogFilterdItems.length;
+ this.$scope.categories = this.cacheService.get('serviceCategories').concat(this.cacheService.get('resourceCategories')).concat(this.cacheService.get('productCategories'));
+ this.$scope.gui.isLoading = false;
+ };
+
+ let onError = ():void => {
+ console.info('Failed to load catalog CatalogViewModel::initCatalog');
+ this.$scope.gui.isLoading = false;
+ };
+ this.EntityService.getCatalog().then(onSuccess, onError);
+ };
+
+
+
+ private initScopeMembers = ():void => {
+ // Gui init
+ this.$scope.gui = <Gui>{};
+ this.$scope.gui.isLoading = true;
+ this.$scope.numberOfItemToDisplay = 0;
+ //this.$scope.categories = this.cacheService.get('categoriesMap');
+ this.$scope.sdcMenu = this.sdcMenu;
+ this.$scope.confStatus = this.sdcMenu.statuses;
+ this.$scope.expandedSection = ["type", "cssClasses", "product-category", "status"];
+ this.$scope.user = this.userResourceService.getLoggedinUser();
+ this.$scope.catalogMenuItem = this.sdcMenu.catalogMenuItem;
+ this.$scope.version = this.cacheService.get('version');
+ this.$scope.sortBy = 'lastUpdateDate';
+ this.$scope.reverse = true;
+
+
+ // Checklist init
+ this.$scope.checkboxes = <Checkboxes>{};
+ this.$scope.checkboxes.componentTypes = ['Resource', 'Service', 'Product'];
+ this.$scope.checkboxes.resourceSubTypes = ['VF', 'VFC', 'CP', 'VL'];
+
+ // Checkboxes filter init
+ this.$scope.checkboxesFilter = <CheckboxesFilter>{};
+ this.$scope.checkboxesFilter.selectedComponentTypes = [];
+ this.$scope.checkboxesFilter.selectedResourceSubTypes = [];
+ this.$scope.checkboxesFilter.selectedCategoriesModel = [];
+ this.$scope.checkboxesFilter.selectedStatuses = [];
+
+ // this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.catalogFilterdItems.length;
+ };
+
+ private initScopeMethods = ():void => {
+ this.$scope.sectionClick = (section:string):void => {
+ let index:number = this.$scope.expandedSection.indexOf(section);
+ if (index!==-1) {
+ this.$scope.expandedSection.splice(index,1);
+ } else {
+ this.$scope.expandedSection.push(section);
+ }
+ };
+
+
+ this.$scope.order = (sortBy:string):void => {//default sort by descending last update. default for alphabetical = ascending
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : (sortBy === 'lastUpdateDate') ? true: false;
+ this.$scope.sortBy = sortBy;
+ };
+
+
+ this.$scope.goToComponent = (component:Models.Components.Component):void => {
+ this.$scope.gui.isLoading = true;
+ this.$state.go('workspace.general', {id: component.uniqueId, type:component.componentType.toLowerCase()});
+ };
+
+
+ // Will print the number of elements found in catalog
+ this.$scope.getNumOfElements = (num:number) : string => {
+ if (!num || num===0){
+ return "No Elements found";
+ } else if (num===1){
+ return "1 Element found";
+ }else {
+ return num + " Elements found";
+ }
+ };
+
+ /**
+ * Select | unselect sub resource when resource is clicked | unclicked.
+ * @param type
+ */
+ this.$scope.gui.onComponentTypeClick = (type:string): void => {
+ if (type==='Resource'){
+ if (this.$scope.checkboxesFilter.selectedComponentTypes.indexOf('Resource')===-1){
+ // If the resource was not selected, unselect all childs.
+ this.$scope.checkboxesFilter.selectedResourceSubTypes = [];
+ } else {
+ // If the resource was selected, select all childs
+ this.$scope.checkboxesFilter.selectedResourceSubTypes = angular.copy(this.$scope.checkboxes.resourceSubTypes);
+ }
+ }
+ };
+
+ /**
+ * Selecting | unselect resources when sub resource is clicked | unclicked.
+ */
+ this.$scope.gui.onResourceSubTypesClick = ():void => {
+ if (this.$scope.checkboxesFilter.selectedResourceSubTypes && this.$scope.checkboxesFilter.selectedResourceSubTypes.length===this.$scope.checkboxes.resourceSubTypes.length){
+ this.$scope.checkboxesFilter.selectedComponentTypes.push('Resource');
+ } else {
+ this.$scope.checkboxesFilter.selectedComponentTypes = _.without(this.$scope.checkboxesFilter.selectedComponentTypes,'Resource');
+ }
+ };
+
+ this.$scope.gui.onCategoryClick = (category:Models.IMainCategory): void => {
+ // Select | Unselect all childs
+ if (this.isCategorySelected(category.uniqueId)){
+ this.$scope.checkboxesFilter.selectedCategoriesModel = this.$scope.checkboxesFilter.selectedCategoriesModel.concat(angular.copy(_.map(category.subcategories, (item) => { return item.uniqueId; })));
+ if (category.subcategories) {
+ category.subcategories.forEach((sub:Models.ISubCategory)=> { // Loop on all selected subcategories and mark the childrens
+ this.$scope.checkboxesFilter.selectedCategoriesModel = this.$scope.checkboxesFilter.selectedCategoriesModel.concat(angular.copy(_.map(sub.groupings, (item) => {
+ return item.uniqueId;
+ })));
+ });
+ }
+ } else {
+ this.$scope.checkboxesFilter.selectedCategoriesModel = _.difference(this.$scope.checkboxesFilter.selectedCategoriesModel, _.map(category.subcategories, (item) => { return item.uniqueId; }));
+ if (category.subcategories) {
+ category.subcategories.forEach((sub:Models.ISubCategory)=> { // Loop on all selected subcategories and un mark the childrens
+ this.$scope.checkboxesFilter.selectedCategoriesModel = _.difference(this.$scope.checkboxesFilter.selectedCategoriesModel, _.map(sub.groupings, (item) => {
+ return item.uniqueId;
+ }));
+ });
+ }
+ }
+ };
+
+ this.$scope.gui.onSubcategoryClick = (category:Models.IMainCategory, subCategory:Models.ISubCategory) : void => {
+ // Select | Unselect all childs
+ if (this.isCategorySelected(subCategory.uniqueId)){
+ this.$scope.checkboxesFilter.selectedCategoriesModel = this.$scope.checkboxesFilter.selectedCategoriesModel.concat(angular.copy(_.map(subCategory.groupings, (item) => { return item.uniqueId; })));
+ } else {
+ this.$scope.checkboxesFilter.selectedCategoriesModel = _.difference(this.$scope.checkboxesFilter.selectedCategoriesModel, _.map(subCategory.groupings, (item) => { return item.uniqueId; }));
+ }
+
+ // Mark | Un mark the parent when all childs selected.
+ if (this.areAllCategoryChildsSelected(category)){
+ // Add the category to checkboxesFilter.selectedCategoriesModel
+ this.$scope.checkboxesFilter.selectedCategoriesModel.push(category.uniqueId);
+ } else {
+ this.$scope.checkboxesFilter.selectedCategoriesModel = _.without(this.$scope.checkboxesFilter.selectedCategoriesModel, category.uniqueId);
+ }
+
+ };
+
+ this.$scope.raiseNumberOfElementToDisplay = () : void => {
+ this.$scope.numberOfItemToDisplay = this.$scope.numberOfItemToDisplay +35;
+ if(this.$scope.catalogFilterdItems) {
+ this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.catalogFilterdItems.length;
+ }
+ };
+
+ this.$scope.gui.onGroupClick = (subCategory:Models.ISubCategory) : void => {
+ // Mark | Un mark the parent when all childs selected.
+ if (this.areAllSubCategoryChildsSelected(subCategory)){
+ // Add the category to checkboxesFilter.selectedCategoriesModel
+ this.$scope.checkboxesFilter.selectedCategoriesModel.push(subCategory.uniqueId);
+ } else {
+ this.$scope.checkboxesFilter.selectedCategoriesModel = _.without(this.$scope.checkboxesFilter.selectedCategoriesModel, subCategory.uniqueId);
+ }
+ };
+
+
+ };
+
+ private areAllCategoryChildsSelected = (category:Models.IMainCategory):boolean => {
+ if (!category.subcategories){return false;}
+ let allIds = _.map(category.subcategories, (sub:Models.ISubCategory)=>{return sub.uniqueId;});
+ let total = _.intersection(this.$scope.checkboxesFilter.selectedCategoriesModel, allIds);
+ return total.length === category.subcategories.length?true:false;
+ };
+
+ private areAllSubCategoryChildsSelected = (subCategory:Models.ISubCategory):boolean => {
+ if (!subCategory.groupings){return false;}
+ let allIds = _.map(subCategory.groupings, (group:Models.IGroup)=>{return group.uniqueId;});
+ let total = _.intersection(this.$scope.checkboxesFilter.selectedCategoriesModel, allIds);
+ return total.length === subCategory.groupings.length?true:false;
+ };
+
+ private isCategorySelected = (uniqueId:string):boolean => {
+ if (this.$scope.checkboxesFilter.selectedCategoriesModel.indexOf(uniqueId)!==-1){
+ return true;
+ }
+ return false;
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/catalog/catalog-view-tests.ts b/catalog-ui/app/scripts/view-models/catalog/catalog-view-tests.ts
new file mode 100644
index 0000000000..3e21835233
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/catalog/catalog-view-tests.ts
@@ -0,0 +1,309 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+describe("test catalog-view", () => {
+
+ let $controllerMock:ng.IControllerService;
+ let $qMock:ng.IQService;
+ let $httpBackendMock:ng.IHttpBackendService;
+ let $scopeMock:Sdc.ViewModels.ICatalogViewModelScope;
+ let $stateMock:ng.ui.IStateService;
+ let $stateParams:any;
+ let entityServiceMock;
+ let cacheServiceMock;
+
+ beforeEach(angular.mock.module('sdcApp'));
+
+ let getAllEntitiesResponseMock = [
+ {
+ "uniqueId": "855acdc7-7976-4913-9fa6-25220bd5a069",
+ "uuid": "8bc54f94-082c-42fa-9049-84767df3ff05",
+ "contactId": "qa1234",
+ "category": "VoIP Call Control",
+ "creationDate": 1447234712398,
+ "description": "ddddd",
+ "highestVersion": true,
+ "icon": "mobility",
+ "lastUpdateDate": 1447234712398,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "distributionStatus": "DISTRIBUTION_NOT_APPROVED",
+ "projectCode": "233233",
+ "name": "mas mas mas mas mas mas mas mas mas mas mas mas ma",
+ "version": "0.1",
+ "type": 0,
+ "tags": [
+ "mas mas mas mas mas mas mas mas mas mas mas mas ma"
+ ],
+ "systemName": "MasMasMasMasMasMasMasMasMasMasMasMasMa",
+ "vnf": true,
+ "$$hashKey": "object:30"
+ },
+ {
+ "uniqueId": "4bb577ce-cb2c-4cb7-bb39-58644b5e73cb",
+ "uuid": "e27f4723-c9ec-4160-89da-dbf84d19a7e3",
+ "contactId": "qa1111",
+ "category": "Mobility",
+ "creationDate": 1447238503181,
+ "description": "aqa",
+ "highestVersion": true,
+ "icon": "call_controll",
+ "lastUpdateDate": 1447248991388,
+ "lastUpdaterUserId": "jm0007",
+ "lastUpdaterFullName": "Joni Mitchell",
+ "lifecycleState": "CERTIFIED",
+ "distributionStatus": "DISTRIBUTION_REJECTED",
+ "projectCode": "111111",
+ "name": "martin18",
+ "version": "1.0",
+ "type": 0,
+ "tags": [
+ "martin18"
+ ],
+ "systemName": "Martin18",
+ "vnf": true
+ },
+ {
+ "uniqueId": "f192f4a6-7fbf-42e4-a546-37509df28dc1",
+ "uuid": "0b77dc0d-222e-4d10-85cd-e420c9481417",
+ "contactId": "fd1212",
+ "category": "Application Layer 4+/Web Server",
+ "creationDate": 1447233679778,
+ "description": "geefw",
+ "highestVersion": true,
+ "icon": "database",
+ "lastUpdateDate": 1447233681582,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "ger",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "ger"
+ ],
+ "vendorName": "fewwfe",
+ "vendorRelease": "fewew",
+ "systemName": "Ger",
+ "$$hashKey": "object:31"
+ },
+ {
+ "uniqueId": "78392d08-1859-47c2-b1f2-1a35b7f8c30e",
+ "uuid": "8cdd63b2-6a62-4376-9012-624f424f71d4",
+ "contactId": "qw1234",
+ "category": "Application Layer 4+/Application Servers",
+ "creationDate": 1447234046114,
+ "description": "test",
+ "highestVersion": true,
+ "icon": "router",
+ "lastUpdateDate": 1447234050545,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "test",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "test"
+ ],
+ "vendorName": "test",
+ "vendorRelease": "test",
+ "systemName": "Test",
+ "$$hashKey": "object:32"
+ },
+ {
+ "uniqueId": "939e153d-2236-410f-b4a9-3b4bf8c79c9e",
+ "uuid": "84862547-4f56-4058-b78e-40df5f374d7e",
+ "contactId": "qw1234",
+ "category": "Application Layer 4+/Application Servers",
+ "creationDate": 1447235242560,
+ "description": "jlk",
+ "highestVersion": true,
+ "icon": "database",
+ "lastUpdateDate": 1447235328062,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKIN",
+ "name": "new",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "new"
+ ],
+ "vendorName": "e",
+ "vendorRelease": "e",
+ "systemName": "New",
+ "$$hashKey": "object:33"
+ },
+ {
+ "uniqueId": "ece818e0-fd59-477a-baf6-e27461a7ce23",
+ "uuid": "8db823c2-6a9c-4636-8676-f5e713270dd7",
+ "contactId": "uf2345",
+ "category": "Network Layer 2-3/Router",
+ "creationDate": 1447235352429,
+ "description": "u",
+ "highestVersion": true,
+ "icon": "network",
+ "lastUpdateDate": 1447235370064,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "u",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "u"
+ ],
+ "vendorName": "u",
+ "vendorRelease": "u",
+ "systemName": "U",
+ "$$hashKey": "object:34"
+ }
+ ];
+
+ let resourceCategoriesResponseMock = [{"name":"Network L2-3","normalizedName":"network l2-3","uniqueId":"resourceNewCategory.network l2-3","subcategories":[{"name":"Gateway","normalizedName":"gateway","uniqueId":"resourceNewCategory.network l2-3.gateway","icons":["gateway"]},{"name":"Infrastructure","normalizedName":"infrastructure","uniqueId":"resourceNewCategory.network l2-3.infrastructure","icons":["ucpe"]},{"name":"WAN Connectors","normalizedName":"wan connectors","uniqueId":"resourceNewCategory.network l2-3.wan connectors","icons":["network","connector","port"]},{"name":"LAN Connectors","normalizedName":"lan connectors","uniqueId":"resourceNewCategory.network l2-3.lan connectors","icons":["network","connector","port"]},{"name":"Router","normalizedName":"router","uniqueId":"resourceNewCategory.network l2-3.router","icons":["router","vRouter"]}]},{"name":"Network L4+","normalizedName":"network l4+","uniqueId":"resourceNewCategory.network l4+","subcategories":[{"name":"Common Network Resources","normalizedName":"common network resources","uniqueId":"resourceNewCategory.network l4+.common network resources","icons":["network"]}]},{"name":"Application L4+","normalizedName":"application l4+","uniqueId":"resourceNewCategory.application l4+","subcategories":[{"name":"Load Balancer","normalizedName":"load balancer","uniqueId":"resourceNewCategory.application l4+.load balancer","icons":["loadBalancer"]},{"name":"Media Servers","normalizedName":"media servers","uniqueId":"resourceNewCategory.application l4+.media servers","icons":["applicationServer"]},{"name":"Application Server","normalizedName":"application server","uniqueId":"resourceNewCategory.application l4+.application server","icons":["applicationServer"]},{"name":"Database","normalizedName":"database","uniqueId":"resourceNewCategory.application l4+.database","icons":["database"]},{"name":"Call Control","normalizedName":"call control","uniqueId":"resourceNewCategory.application l4+.call control","icons":["call_controll"]},{"name":"Border Element","normalizedName":"border element","uniqueId":"resourceNewCategory.application l4+.border element","icons":["borderElement"]},{"name":"Web Server","normalizedName":"web server","uniqueId":"resourceNewCategory.application l4+.web server","icons":["applicationServer"]},{"name":"Firewall","normalizedName":"firewall","uniqueId":"resourceNewCategory.application l4+.firewall","icons":["firewall"]}]},{"name":"Generic","normalizedName":"generic","uniqueId":"resourceNewCategory.generic","subcategories":[{"name":"Database","normalizedName":"database","uniqueId":"resourceNewCategory.generic.database","icons":["database"]},{"name":"Abstract","normalizedName":"abstract","uniqueId":"resourceNewCategory.generic.abstract","icons":["objectStorage","compute"]},{"name":"Network Elements","normalizedName":"network elements","uniqueId":"resourceNewCategory.generic.network elements","icons":["network","connector"]},{"name":"Infrastructure","normalizedName":"infrastructure","uniqueId":"resourceNewCategory.generic.infrastructure","icons":["connector"]}]},{"name":"NewCategory","normalizedName":"newcategory","uniqueId":"resourceNewCategory.newcategory","subcategories":[{"name":"MyNewSubCategory","normalizedName":"mynewsubcategory","uniqueId":"resourceNewCategory.newcategory.mynewsubcategory"}]}];
+
+ let getAllEntitiesDefered:ng.IDeferred<any> = null;
+
+ beforeEach(angular.mock.inject((_$controller_:ng.IControllerService,
+ _$httpBackend_:ng.IHttpBackendService,
+ _$rootScope_,
+ _$q_:ng.IQService,
+ _$state_:ng.ui.IStateService,
+ _$stateParams_:any) => {
+
+ $controllerMock = _$controller_;
+ $httpBackendMock = _$httpBackend_
+ $scopeMock = _$rootScope_.$new();
+ $qMock = _$q_;
+ $stateMock = _$state_;
+ $stateParams = _$stateParams_;
+
+
+ //handle all http request thet not relevant to the tests
+ $httpBackendMock.expectGET(/.*languages\/en_US.json.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*rest\/version.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*configuration\/ui.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*user\/authorize.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/services.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/resources.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/products.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET('http://feHost:8181/sdc1/feProxy/rest/version').respond(200, JSON.stringify({}));
+
+ /**
+ * Mock the service
+ * @type {any}
+ */
+ getAllEntitiesDefered = $qMock.defer();
+ getAllEntitiesDefered.resolve(getAllEntitiesResponseMock);
+
+ cacheServiceMock = jasmine.createSpyObj('cacheServiceMock', ['get']);
+ cacheServiceMock.get.and.callFake(function(string){return resourceCategoriesResponseMock;});
+ /*
+ cacheServiceMock.get.and.callFake(function(value:string){
+ switch(value){
+ case 'serviceCategories':
+ console.log('serviceCategories');
+ break;
+ case 'resourceCategories':
+ console.log('resourceCategories');
+ break;
+ case 'productCategories':
+ console.log('productCategories');
+ break;
+ default :
+ console.log('default');
+ break;
+ }
+ });
+ */
+
+ entityServiceMock = jasmine.createSpyObj('entityServiceMock', ['getCatalog']);
+ entityServiceMock.getCatalog.and.returnValue(getAllEntitiesDefered.promise);
+
+ // $stateParams['show'] = '';
+
+ /**
+ * Need to inject into the controller only the objects that we want to MOCK
+ * those that we need to change theirs behaviors
+ */
+ $controllerMock(Sdc.ViewModels.CatalogViewModel, {
+ '$scope': $scopeMock,
+ '$stateParams': $stateParams,
+ 'Sdc.Services.EntityService': entityServiceMock,
+ 'Sdc.Services.CacheService': cacheServiceMock
+ });
+
+ }));
+
+
+ beforeEach(function () {
+ });
+
+ describe("test GUI events on checkbox type resource click", function () {
+
+ /**
+ * The function checks only for resource type.
+ * Select the Resource and verify that the sub resources are selected.
+ *
+ */
+ it('test onComponentTypeClick (check select checkbox of Resource type)', function () {
+ $scopeMock.$apply();
+ $scopeMock.checkboxesFilter.selectedComponentTypes = ['Resource'];
+ $scopeMock.gui.onComponentTypeClick('Resource');
+ expect($scopeMock.checkboxesFilter.selectedResourceSubTypes.length === 4).toBeTruthy();
+ });
+
+ /**
+ * The function checks only for resource type.
+ * Un select the Resource and verify that the sub resources are selected.
+ *
+ */
+ it('test onComponentTypeClick (check un select checkbox of Resource type)', function () {
+ $scopeMock.$apply();
+ $scopeMock.gui.onComponentTypeClick('Resource');
+ expect($scopeMock.checkboxesFilter.selectedResourceSubTypes.length === 0).toBeTruthy();
+ });
+
+ });
+
+ describe("test GUI events on checkbox main category click -> sub categories are selected", function () {
+
+ /**
+ * The function checks that after selecting 2 main categories, the subcategories are selected also.
+ *
+ */
+ it('test onComponentTypeClick (check select checkbox of Resource type)', function () {
+ let category1 = resourceCategoriesResponseMock[0];
+ let category2 = resourceCategoriesResponseMock[1];
+
+ $scopeMock.$apply();
+ $scopeMock.checkboxesFilter.selectedCategoriesModel = [category1.uniqueId, category2.uniqueId];
+ $scopeMock.gui.onCategoryClick(category1);
+ $scopeMock.gui.onCategoryClick(category2);
+
+ expect($scopeMock.checkboxesFilter.selectedCategoriesModel.length===8).toBeTruthy();
+ });
+
+ });
+
+
+});
diff --git a/catalog-ui/app/scripts/view-models/catalog/catalog-view.html b/catalog-ui/app/scripts/view-models/catalog/catalog-view.html
new file mode 100644
index 0000000000..0d46dc2a24
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/catalog/catalog-view.html
@@ -0,0 +1,190 @@
+<div class="sdc-catalog-container">
+
+ <loader data-display="gui.isLoading"></loader>
+<!--
+ <ecomp-header menu-data="menuItems" version="{{version}}"></ecomp-header>
+-->
+
+ <div class="w-sdc-main-container">
+
+ <!-- LEFT SIDE -->
+ <perfect-scrollbar scroll-y-margin-offset="0" class="sdc-catalog-body-container w-sdc-left-sidebar" include-padding="true">
+ <div class="sdc-catalog-leftbar-container">
+
+ <div class="sdc-catalog-type-filter-container">
+ <div
+ class="i-sdc-designer-leftbar-section-title pointer"
+ data-ng-click="sectionClick('type')"
+ data-ng-class="{'expanded': expandedSection.indexOf('type') !== -1}">
+ <span class="i-sdc-designer-leftbar-section-title-icon"></span>
+ <span class="i-sdc-designer-leftbar-section-title-text" data-tests-id="typeFilterTitle">Type</span>
+ </div>
+ <div class="i-sdc-designer-leftbar-section-content">
+ <ul class="list-unstyled i-sdc-designer-leftbar-section-content-ul">
+ <li class="i-sdc-designer-leftbar-section-content-ul-li" data-ng-repeat="type in checkboxes.componentTypes">
+
+ <sdc-checkbox elem-id="checkbox-{{type | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedComponentTypes"
+ sdc-checklist-value="type"
+ data-ng-click="gui.onComponentTypeClick(type)"
+ text="{{type}}"></sdc-checkbox>
+
+ <ul class="list-unstyled i-sdc-catalog-subcategories-checkbox" data-ng-if="type==='Resource'">
+ <li data-ng-repeat="subType in checkboxes.resourceSubTypes">
+
+ <sdc-checkbox elem-id="checkbox-{{subType | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedResourceSubTypes"
+ sdc-checklist-value="subType"
+ data-ng-click="gui.onResourceSubTypesClick()"
+ text="{{subType}}"></sdc-checkbox>
+
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ <div class="sdc-catalog-categories-filter-container">
+ <div
+ class="i-sdc-designer-leftbar-section-title pointer"
+ data-ng-click="sectionClick('category')"
+ data-ng-class="{'expanded': expandedSection.indexOf('category') !== -1}">
+ <span class="i-sdc-designer-leftbar-section-title-icon"></span>
+ <span class="i-sdc-designer-leftbar-section-title-text" data-tests-id="categoriesFilterTitle">Categories</span>
+ </div>
+ <div class="i-sdc-designer-leftbar-section-content">
+
+ <!-- CATEGORY CHECKBOX -->
+ <ul class="list-unstyled i-sdc-designer-leftbar-section-content-ul">
+ <li class="i-sdc-designer-leftbar-section-content-ul-li"
+ data-ng-repeat="category in categories track by category.uniqueId | categoryTypeFilter:checkboxesFilter.selectedComponentTypes | orderBy: category">
+
+ <sdc-checkbox elem-id="checkbox-{{category.uniqueId | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedCategoriesModel"
+ sdc-checklist-value="category.uniqueId"
+ data-tests-id="{{category.uniqueId}}"
+ data-ng-click="gui.onCategoryClick(category)"
+ text="{{category.name}}"></sdc-checkbox>
+
+ <!-- SUB CATEGORY CHECKBOX -->
+ <ul class="list-unstyled i-sdc-catalog-subcategories-checkbox" data-ng-if="category.subcategories && category.subcategories.length>0">
+ <li ng-repeat="subcategory in category.subcategories track by subcategory.uniqueId | orderBy:'name'">
+
+ <sdc-checkbox elem-id="checkbox-{{subcategory.uniqueId | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedCategoriesModel"
+ sdc-checklist-value="subcategory.uniqueId"
+ data-tests-id="{{subcategory.uniqueId}}"
+ data-ng-click="gui.onSubcategoryClick($parent.category, subcategory)"
+ text="{{subcategory.name}}"></sdc-checkbox>
+
+ <!-- GROUPING CHECKBOX -->
+ <ul class=" list-unstyled i-sdc-catalog-grouping-checkbox" data-ng-if="subcategory.groupings && subcategory.groupings.length>0">
+ <li ng-repeat="grouping in subcategory.groupings track by grouping.uniqueId | orderBy:'name'">
+
+ <sdc-checkbox elem-id="checkbox-{{grouping.uniqueId | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedCategoriesModel"
+ sdc-checklist-value="grouping.uniqueId"
+ data-ng-click="gui.onGroupClick($parent.subcategory)"
+ text="{{grouping.name}}"></sdc-checkbox>
+
+ </li>
+ </ul>
+ </li><!-- Close subcategory -->
+ </ul><!-- Close subcategories -->
+ </li><!-- Close main category -->
+ </ul><!-- Close main categories -->
+
+ </div>
+ </div>
+
+ <!-- STATUS -->
+ <div class="sdc-catalog-status-filter-container">
+ <div
+ class="i-sdc-designer-leftbar-section-title pointer"
+ data-ng-click="sectionClick('status')"
+ data-ng-class="{'expanded': expandedSection.indexOf('status') !== -1}">
+ <span class="i-sdc-designer-leftbar-section-title-icon"></span>
+ <span class="i-sdc-designer-leftbar-section-title-text" data-tests-id="statusFilterTitle">Status</span>
+ </div>
+
+ <div class="i-sdc-designer-leftbar-section-content">
+ <ul class="list-unstyled i-sdc-designer-leftbar-section-content-ul">
+ <!--li data-ng-repeat="(key, value) in confStatus" -->
+
+ <li class="i-sdc-designer-leftbar-section-content-ul-li"
+ data-ng-repeat="(key, state) in confStatus | catalogStatusFilter">
+
+ <sdc-checkbox elem-id="checkbox-{{key | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedStatuses"
+ sdc-checklist-value="state.values"
+ text="{{state.name}}"></sdc-checkbox>
+
+ <div class="i-sdc-categories-list-item-icon"></div>
+ </label>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ </div>
+ </perfect-scrollbar>
+
+ <!-- RIGHT SIDE -->
+ <perfect-scrollbar id="catalog-main-scroll" include-padding="true" class="w-sdc-main-right-container w-sdc-catalog-main">
+
+ <!-- HEADER -->
+ <div>
+ <div class="w-sdc-dashboard-catalog-header">
+ {{getNumOfElements((catalogFilterdItems | entityFilter:checkboxesFilter | filter:search).length)}}
+ </div>
+ <div class="w-sdc-dashboard-catalog-header-right">
+ <span class="w-sdc-dashboard-catalog-header-order" translate="SORT_CAPTION"></span>&nbsp;&nbsp;
+ <a class="w-sdc-dashboard-catalog-sort" data-tests-id="sort-by-last-update" data-ng-class="{'blue' : sortBy==='lastUpdateDate'}"
+ ng-click="order('lastUpdateDate')" translate="SORT_BY_UPDATE_DATE"></a>&nbsp;
+ <span data-ng-show="sortBy === 'lastUpdateDate'" class="w-sdc-catalog-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"></span>
+ &nbsp;|&nbsp;
+ <a class="w-sdc-dashboard-catalog-sort" data-tests-id="sort-by-alphabetical" data-ng-class="{'blue' : sortBy!=='lastUpdateDate'}"
+ ng-click="order('name | resourceName')" translate="SORT_ALPHABETICAL"></a>&nbsp;
+ <span data-ng-show="sortBy !== 'lastUpdateDate'" class="w-sdc-catalog-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"></span>
+ </div>
+ </div>
+
+ <div infinite-scroll-disabled='isAllItemDisplay' infinite-scroll="raiseNumberOfElementToDisplay()" infinite-scroll-container="'#catalog-main-scroll'" infinite-scroll-parent>
+ <!-- CARDS -->
+ <div data-ng-class="{'sdc-hide-popover': hidePopover}"
+ data-ng-init="component.filterTerm = component.name + ' ' + component.description + ' ' + component.tags.toString() + ' ' + component.version"
+ class="w-sdc-dashboard-card"
+ data-ng-repeat="component in catalogFilterdItems | entityFilter:checkboxesFilter | filter:search | orderBy:sortBy:reverse | limitTo:numberOfItemToDisplay"
+ data-ng-class="{'resource' : component.isResource(), 'service' : component.isService(), 'product' : component.isProduct()}">
+
+ <div class="w-sdc-dashboard-card-body" data-ng-click="gui.isLoading || goToComponent(component)">
+ <div class="w-sdc-dashboard-card-avatar"><span data-tests-id="asset-type" class="{{component.getComponentSubType()}}"></span></div>
+ <!--<div class="w-sdc-dashboard-card-edit " data-ng-class="component.lifecycleState" data-tests-id="assetlifecycleState {{getStatus()}}"></div>-->
+ <div class="w-sdc-dashboard-card-schema-image {{component.icon}}" data-tests-id="{{component.categories[0].subcategories[0].uniqueId}}" data-ng-class="{'sprite-resource-icons':component.isResource(), 'sprite-services-icons':component.isService(), 'sprite-product-icons':component.isProduct()}"></div>
+ <!--<div class="w-sdc-dashboard-card-description">{{component.description}}</div>-->
+ <div class="w-sdc-dashboard-card-info-name-container">
+ <span class="w-sdc-dashboard-card-info-name" tooltips
+ tooltip-content="{{component.name | resourceName}}"> {{component.name | resourceName}}</span>
+ </div>
+ </div>
+
+ <div class="w-sdc-dashboard-card-footer">
+ <div class="w-sdc-dashboard-card-info">
+ <div class="w-sdc-dashboard-card-info-lifecycleState">
+ <span class="w-sdc-dashboard-card-info-lifecycleState" tooltips
+ tooltip-content="{{component.getStatus(sdcMenu)}}"> {{component.getStatus(sdcMenu)}}</span>
+ </div>
+ <div class="w-sdc-dashboard-card-info-user">V {{component.version}}</div>
+ </div>
+ <!--<div class="w-sdc-dashboard-card-info-lifecycleState-icon sprite-new {{sdcMenu.LifeCycleStatuses[component.lifecycleState].icon}}"></div>-->
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+
+ </div>
+
+ <top-nav top-lvl-selected-index="1" search-bind="search.filterTerm" version="{{version}}"></top-nav>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/catalog/catalog.less b/catalog-ui/app/scripts/view-models/catalog/catalog.less
new file mode 100644
index 0000000000..8be90a6a59
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/catalog/catalog.less
@@ -0,0 +1,304 @@
+.sdc-catalog-container {
+
+ .i-sdc-categories-list-item {
+ font-weight: normal;
+ }
+
+ // Checkboxes
+ .i-sdc-designer-leftbar-section-content-ul {
+ padding: 0;
+ margin: 0;
+
+ .i-sdc-catalog-subcategories-checkbox {
+ padding: 0 0 0 20px;
+ margin: 0;
+
+ .i-sdc-catalog-grouping-checkbox {
+ padding: 0 0 0 20px;
+ margin: 0;
+ }
+
+ }
+
+ }
+
+ .i-sdc-designer-leftbar-section-content-li {
+ &:last-child {
+ .i-sdc-categories-list-item {
+ margin: 0;
+ }
+ }
+ }
+
+ .i-sdc-categories-list-item {
+ display: block;
+ //margin-bottom: 5px;
+ //padding-left: 15px;
+ //text-indent: -24px;
+ vertical-align: top;
+ font-weight: bold;
+ }
+
+ .i-sdc-subcategories-list-item {
+ display: block;
+ //padding-left: 20px;
+ vertical-align: top;
+ font-weight: normal;
+ margin: 0;
+ //text-indent: -10px;
+ }
+
+ /*Added by - Ikram */
+ .i-sdc-product-input,
+ .i-sdc-product-select {
+ border: 1px solid @border_color_f;
+ min-height: 30px;
+ padding: 0;
+ width: 100%;
+ margin: 1px 0;
+ background-color: #F2F2F2;
+ outline: none;
+
+ &:disabled {
+ .disabled;
+ }
+ optgroup{
+ color: @color_u;
+ option{
+ color: @color_b;
+ }
+ }
+ }
+
+ .i-sdc-categories-list-item-icon {
+ display: inline-block;
+ float: right;
+ position: relative;
+ right: -8px;
+ top: 6px;
+ }
+
+ .i-sdc-categories-list-item {
+ margin-top: 7px;
+ &.NOT_CERTIFIED_CHECKOUT,
+ &.NOT_CERTIFIED_CHECKIN {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -2889px;
+ width: 14px;
+ height: 14px;
+
+ }
+ }
+
+ &.CERTIFIED {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -3034px;
+ width: 14px;
+ height: 16px;
+ }
+ }
+
+ &.READY_FOR_CERTIFICATION {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -2985px;
+ width: 14px;
+ height: 16px;
+ }
+ }
+
+ &.CERTIFICATION_IN_PROGRESS {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -2934px;
+ width: 14px;
+ height: 16px;
+ }
+ }
+
+ &.DISTRIBUTED,
+ &.TBD {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -43px -3087px;
+ width: 24px;
+ height: 14px;
+
+ }
+ }
+ }
+
+ .i-sdc-categories-list-input {
+ margin: 8px;
+
+ }
+
+ .i-sdc-subcategories-list-input {
+
+ margin: 8px;
+ }
+ .i-sdc-subcategories-list-input-container {
+ margin: 0px 0px 0px 20px;
+ padding: 2px;
+ }
+
+ .w-sdc-header-catalog-search-container {
+ display: table;
+ padding: 21px 0;
+ position: relative;
+
+ .w-sdc-designer-leftbar-search-input {
+ color: #000;
+ width: 300px;
+ }
+
+ // .magnification {
+ // .sprite;
+ // .sprite.magnification-glass;
+ // .hand;
+ // position: absolute;
+ // top: 40px;
+ // right: 42px;
+ // }
+ }
+
+ .w-sdc-catalog-main {
+ padding: 10px 12px;
+ }
+ .w-sdc-dashboard-catalog-header {
+ .b_9;
+ display: inline-block;
+ font-style: italic;
+ font-weight: bold;
+ padding-left: 10px;
+ }
+
+ .w-sdc-dashboard-catalog-header-order {
+ .b_9;
+ font-weight: 800;
+ }
+
+ .w-sdc-dashboard-catalog-sort {
+ .b_9;
+ font-weight: bold;
+ white-space:pre;
+ &:hover{
+ .hand;
+ text-decoration: none;
+ .a_9;
+ }
+ &.blue {
+ .a_9;
+ }
+ }
+
+ .w-sdc-catalog-sort-arrow{
+ display: inline-block;
+ &.up{
+ .b_9;
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid ;
+ }
+ &.down{
+ .b_9;
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid;
+ }
+ }
+
+
+
+
+ .w-sdc-dashboard-catalog-header-right{
+ float: right;
+ display: inline-block;
+ padding-right:34px;
+ }
+
+ .w-sdc-header-catalog-search-input {
+ width: 420px;
+ display: table-cell;
+ padding: 0 25px 1px 10px;
+ border: 1px solid #bcbcbc;
+ .border-radius(10px);
+ height: 30px;
+ margin: 10px 30px;
+ outline: none;
+ }
+
+ .sdc-catalog-type-filter-container {
+ margin-top: -1px;
+ }
+
+ .i-sdc-designer-leftbar-section-title {
+ text-transform: uppercase;
+ .l_14_m;
+ line-height: 30px;
+ }
+
+ .i-sdc-designer-leftbar-section-title-icon {
+ .hand;
+ .tlv-sprite;
+ .footer-close;
+ transition: .3s all;
+ margin-top: -4px;
+ }
+
+ .i-sdc-designer-leftbar-section-title-text {
+ margin-left: 20px;
+ }
+
+ .seperator-left,
+ .seperator-right {
+ border-right: solid 1px @color_m;
+ display: table-cell;
+ width: 2px;
+ }
+
+ // Rotate catalog left side arrows
+ .i-sdc-designer-leftbar-section-title.expanded .i-sdc-designer-leftbar-section-title-icon {
+ transform: rotate(180deg);
+ }
+
+ // Transform catalog left side sections
+ .i-sdc-designer-leftbar-section-title + .i-sdc-designer-leftbar-section-content {
+ max-height: 0px;
+ margin: 0 auto;
+ transition: all .3s;
+ overflow: hidden;
+ padding: 0 10px 0 18px;
+ }
+
+ .i-sdc-designer-leftbar-section-title.expanded + .i-sdc-designer-leftbar-section-content {
+ max-height: 9999px;
+ margin: 0 auto 1px;
+ transition: all .3s;
+ padding: 10px 18px 10px 18px;
+ overflow: hidden;
+ }
+
+}
+
+.w-sdc-search-icon{
+ position: absolute;
+ right: 40px;
+ top: 40px;
+ &.leftbar{
+ top: 19px;
+ right: 18px;
+ }
+ &.magnification {
+ .sprite;
+ .sprite.magnification-glass;
+ .hand;
+ }
+
+ &.cancel {
+ .sprite;
+ .sprite.clear-text;
+ .hand;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.html b/catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.html
new file mode 100644
index 0000000000..ac51e9014c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.html
@@ -0,0 +1,16 @@
+<div ng-repeat="activityDate in activityDateArray " class="w-sdc-component-viewer-right-activity-log" >
+ <div class="w-sdc-component-viewer-right-activity-log-date" >{{activityDate | date: 'longDate'}}</div>
+ <div ng-repeat="activity in activityLog[activityDate] | orderBy: '-TIMESTAMP'">
+ <div class="w-sdc-component-viewer-right-activity-log-time">{{activity.TIMESTAMP.replace(" UTC", '') | stringToDateFilter | date: 'mediumTime':'UTC'}}</div>
+ <div class="w-sdc-component-viewer-right-activity-log-content">{{"Action: " + parseAction(activity.ACTION) + " Performed by: " + activity.MODIFIER + " Status: " + activity.STATUS}}</div>
+ </div>
+ </div>
+</div>
+
+
+
+
+
+
+
+
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.less b/catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.less
new file mode 100644
index 0000000000..4a7676b6e2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/activity-log/activity-log-view.less
@@ -0,0 +1,18 @@
+.w-sdc-component-viewer-right-activity-log{
+
+ .w-sdc-component-viewer-right-activity-log-date{
+ .backgroundColor.n;
+ .font-color.g;
+ padding: 4px 11px
+ }
+ .w-sdc-component-viewer-right-activity-log-time{
+ .g_3;
+ padding: 12px 0px 0px 11px;
+ }
+
+ .w-sdc-component-viewer-right-activity-log-content{
+ .g_1;
+ padding: 0px 0px 12px 11px;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.17);
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/component-viewer-view-model.ts b/catalog-ui/app/scripts/view-models/component-viewer/component-viewer-view-model.ts
new file mode 100644
index 0000000000..3ae8ad70fb
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/component-viewer-view-model.ts
@@ -0,0 +1,211 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IComponentViewerViewModelScope extends ng.IScope {
+ component: Models.Components.Component;
+ additionalInformations: Array<Models.AdditionalInformationModel>;
+ activityLog: any;
+ activityDateArray: Array<any>; //this is in order to sort the dates
+ inputs: Array<any>;
+ isLoading: boolean;
+ templateUrl: string;
+ currentTab:string;
+ preVersion:string;
+ sdcMenu:Models.IAppMenu;
+ versionsList:Array<any>;
+ close(): void;
+ hasItems(obj:any): boolean;
+ onVersionChanged(version:any) : void;
+ moveToTab(tab:string):void;
+ isSelected(tab:string):boolean;
+ getActivityLog(uniqueId:string):void;
+ parseAction(action:string):string;
+ }
+
+ export class ComponentViewerViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$modalInstance',
+ 'component',
+ 'Sdc.Services.ActivityLogService',
+ 'sdcMenu',
+ 'ComponentFactory'
+ ];
+
+ constructor(private $scope:IComponentViewerViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private component:Models.Components.Component,
+ private activityLogService:Services.ActivityLogService,
+ private sdcMenu:Models.IAppMenu,
+ private ComponentFactory: Utils.ComponentFactory) {
+ this.initScope(component);
+ }
+
+ //creating objects for versions
+ private initVersionObject:Function = ():void => {
+ this.$scope.versionsList = [];
+ for (let version in this.$scope.component.allVersions) {
+ this.$scope.versionsList.push({
+ versionNumber: version,
+ versioning: this.versioning(version),
+ versionId: this.$scope.component.allVersions[version]
+ });
+ }
+
+ };
+
+ private versioning:Function = (versionNumber:string):string => {
+ let version:Array<string> = versionNumber.split('.');
+ return '00000000'.slice(version[0].length) + version[0] + '.' + '00000000'.slice(version[1].length) + version[1];
+ };
+
+ private showComponentInformationView:Function = ():void => {
+ if (this.$scope.component.isResource()) {
+ this.$scope.templateUrl = '/app/scripts/view-models/component-viewer/properties/resource-properties-view.html';
+ } else if(this.$scope.component.isService()) {
+ this.$scope.templateUrl = '/app/scripts/view-models/component-viewer/properties/service-properties-view.html';
+ } else {
+ this.$scope.templateUrl = '/app/scripts/view-models/component-viewer/properties/product-properties-view.html';
+ }
+ };
+
+ private showActivityLogView:Function = ():void => {
+ this.$scope.templateUrl = '/app/scripts/view-models/component-viewer/activity-log/activity-log-view.html';
+ };
+
+ private initComponent = (component:Models.Components.Component):void => {
+ this.$scope.component = component;
+ this.$scope.additionalInformations = component.getAdditionalInformation();
+ this.initVersionObject();
+ this.$scope.isLoading = false;
+ };
+
+ private initScope = (component:Models.Components.Component):void => {
+ this.$scope.isLoading = false;
+ this.initComponent(component);
+ this.$scope.currentTab = 'PROPERTIES';
+ this.$scope.preVersion = component.version;
+ this.$scope.sdcMenu = this.sdcMenu;
+ this.showComponentInformationView();
+ //service inputs
+ if (component.isService()) {
+ let inputs:Array<any> = [];
+
+ for (let group in component.componentInstancesProperties) {
+ if (component.componentInstancesProperties[group]) {
+ component.componentInstancesProperties[group].forEach((property:Models.PropertyModel):void => {
+ if (!property.value) {
+ property.value = property.defaultValue;
+ }
+ inputs.push({
+ name: property.name,
+ value: property.value,
+ type: property.type
+ });
+ });
+ }
+ }
+ this.$scope.inputs = inputs;
+ }
+
+ this.$scope.hasItems = (obj:any):boolean => {
+ return Object.keys(obj).length > 0;
+ };
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.dismiss();
+ };
+
+ this.$scope.onVersionChanged = (version:any):void => {
+ if (version.versionNumber != this.$scope.component.version) {
+ this.$scope.isLoading = true;
+ this.ComponentFactory.getComponentFromServer(this.component.componentType, version.versionId).then((component: Models.Components.Component):void => {
+ this.initComponent(component);
+ });
+ if (this.$scope.currentTab === 'ACTIVITY_LOG') {
+ this.$scope.getActivityLog(version.versionId);
+ }
+
+ }
+ };
+
+ this.$scope.getActivityLog = (uniqueId:any):void => {
+
+ let onError = (response) => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+
+ };
+ let onSuccess = (response:Array<Models.Activity>) => {
+ this.$scope.activityLog = _.groupBy(response, function (activity:Models.Activity) { //group by date only
+ let dateTime:Date = new Date(activity.TIMESTAMP.replace(" UTC", '').replace(" ", 'T'));
+ // let date:Date = new Date(dateTime.getFullYear(), dateTime.getMonth(), dateTime.getDate());
+ return dateTime.getTime();
+ });
+ /*this is in order to sort the jsonObject by date*/
+ this.$scope.activityDateArray = Object.keys(this.$scope.activityLog);
+ this.$scope.activityDateArray.sort().reverse();
+ this.$scope.isLoading = false;
+ };
+
+ this.$scope.isLoading = true;
+ if (this.$scope.component.isResource()) {
+ this.activityLogService.getActivityLogService('resources', uniqueId).then(onSuccess, onError);
+ }
+ if (this.$scope.component.isService()) {
+ this.activityLogService.getActivityLogService('services', uniqueId).then(onSuccess, onError);
+ }
+
+ };
+
+ this.$scope.moveToTab = (tab:string):void => {
+ if (tab === this.$scope.currentTab) {
+ return;
+ } else if (tab === 'PROPERTIES') {
+ this.showComponentInformationView();
+ this.$scope.preVersion = this.$scope.component.version;
+ } else if (tab === 'ACTIVITY_LOG') {
+ if (!this.$scope.activityLog || this.$scope.preVersion != this.$scope.component.version) {
+ this.$scope.activityLog = this.$scope.getActivityLog(this.$scope.component.uniqueId);
+ }
+ this.showActivityLogView();
+ } else {
+ console.error("Tab " + tab + " not found!");
+ return;
+ }
+ this.$scope.currentTab = tab;
+ };
+
+ this.$scope.isSelected = (tab:string):boolean => {
+ return tab === this.$scope.currentTab;
+ };
+
+ this.$scope.parseAction = (action:string) => {
+ return action ? action.split(/(?=[A-Z])/).join(' ') : '';
+ };
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/component-viewer.html b/catalog-ui/app/scripts/view-models/component-viewer/component-viewer.html
new file mode 100644
index 0000000000..6f244b048e
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/component-viewer.html
@@ -0,0 +1,55 @@
+<div class="w-sdc-resource-viewer">
+ <loader data-display="isLoading"></loader>
+ <div class="w-sdc-resource-viewer-modal-close sprite x-btn-black" data-ng-click="close()">X</div>
+ <div class="w-sdc-resource-viewer-content" data-ng-if="component">
+ <div class="w-sdc-resource-viewer-left">
+ <h3 class="w-sdc-resource-viewer-left-title clearfix">
+ <div class="w-sdc-resource-viewer-left-title-icon i-sdc-form-item-suggested-icon borderElement large {{component.iconSprite}} {{component.icon}}"></div>
+ <span class="w-sdc-resource-viewer-left-title-name"
+ tooltips tooltip-content="{{component.name | resourceName}}">{{component.name | resourceName}}</span>
+ <br/>
+ <span class="w-sdc-resource-viewer-left-title-version">v{{component.version}}</span>
+ </h3>
+ <p class="w-sdc-resource-viewer-left-title-uuid">
+ UUID: {{component.uuid}}
+ </p>
+ <div class="w-sdc-resource-viewer-leftbar-section">
+ <div class="w-sdc-resource-viewer-leftbar-section-title">Version History</div>
+ <perfect-scrollbar class="w-sdc-resource-viewer-version">
+ <div class="i-sdc-resource-viewer-version-container">
+ <div data-ng-repeat="version in versionsList | orderBy: '-versioning'">
+ <span class="i-sdc-resource-viewer-version-item" data-ng-class="{'active': version.versionNumber == component.version}" data-ng-click="onVersionChanged(version)">{{ version.versionNumber }}</span>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ <div class="w-sdc-resource-viewer-leftbar-section">
+ <div class="w-sdc-resource-viewer-leftbar-section-title">Composition</div>
+ <perfect-scrollbar class="w-sdc-resource-viewer-leftbar-section-structure" ng-show="component.isComplex()">
+ <structure-tree component="component"></structure-tree>
+ </perfect-scrollbar>
+ </div>
+ </div>
+
+ <div class="w-sdc-resource-viewer-right">
+ <button class="w-sdc-resource-viewer-right-tab"
+ data-ng-class="{'selected': isSelected('PROPERTIES')}"
+ data-ng-click="moveToTab('PROPERTIES')"
+ translate="ENTITY_VIEWER_PROPERTIES_TAB">
+ </button>
+
+ <button class="w-sdc-resource-viewer-right-tab"
+ data-ng-if="component.isResource() || component.isService()"
+ data-ng-class="{'selected': isSelected('ACTIVITY_LOG')}"
+ data-ng-click="moveToTab('ACTIVITY_LOG')"
+ translate="ENTITY_VIEWER_ACTIVITY_LOG_TAB">
+ </button>
+
+ <perfect-scrollbar include-padding="true" class="w-sdc-resource-viewer-right-content">
+ <ng-include src="templateUrl" ng-if="true"></ng-include>
+ </perfect-scrollbar>
+ <div style="clear:both;"></div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/component-viewer.less b/catalog-ui/app/scripts/view-models/component-viewer/component-viewer.less
new file mode 100644
index 0000000000..2fe5676d62
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/component-viewer.less
@@ -0,0 +1,148 @@
+html .modal-component-viewer{
+ width: 1084px;
+}
+
+.w-sdc-resource-viewer {
+ .b_7;
+ .w-sdc-resource-viewer-modal-close{
+ z-index: 2;
+ text-indent: -100px;
+ overflow: hidden;
+ top: 19px;
+ }
+ .w-sdc-resource-viewer-content {
+ position: relative;
+ overflow: hidden;
+
+ .w-sdc-resource-viewer-left {
+
+ .bg_j;
+ display: table-cell;
+ width: 282px;
+
+ .w-sdc-resource-viewer-left-title {
+ margin: 0;
+ display: block;
+ height: 100px;
+ padding: 20px 0;
+ }
+
+ .w-sdc-resource-viewer-left-title-icon {
+ margin: 0 15px;
+ vertical-align: middle;
+ float: left;
+ }
+
+ .w-sdc-resource-viewer-left-title-name {
+ .g_7;
+ max-width: 160px;
+ overflow: hidden;
+ display: inline-block;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+ line-height: 23px;
+ padding-top: 10px;
+ font-weight: bold;
+ white-space: nowrap;
+ }
+
+ .w-sdc-resource-viewer-left-title-version {
+ .g_13;
+ float: left;
+ }
+
+ .w-sdc-resource-viewer-left-title-uuid {
+ .g_14;
+ text-align: center;
+ border-top: 1px solid rgba(120, 136, 148, 0.26);
+ width: 95%;
+ margin: auto;
+ padding: 7px 0;
+ }
+
+ .w-sdc-resource-viewer-leftbar-section {
+ font-family: omnes-medium, sans-serif;
+ }
+
+ .w-sdc-resource-viewer-leftbar-section-title {
+ .bg_o;
+ color: #fff;
+ font-size: 14px;
+ padding: 12px 20px;
+ text-transform: uppercase;
+ }
+
+ .w-sdc-resource-viewer-leftbar-section-structure{
+ .perfect-scrollbar;
+ max-height: 525px;
+ }
+ .w-sdc-resource-viewer-version {
+ font-weight: bold;
+ .perfect-scrollbar;
+ }
+
+ .i-sdc-resource-viewer-version-container {
+ padding: 13px 0px 13px 13px;
+ max-height: 218px;
+
+ .i-sdc-resource-viewer-version-item {
+
+ &.active {
+ .a_7;
+ }
+ &:hover {
+ cursor: pointer;
+ }
+ }
+
+ }
+ }
+
+ .w-sdc-resource-viewer-right {
+ .bg_c;
+ display: table-cell;
+ vertical-align: top;
+ padding: 0; // for the scroller to be on all width
+ width: 716px;
+ padding: 25px 0px 0px 35px;
+
+ .w-sdc-resource-viewer-right-content {
+ padding: 0 52px 0 0px;
+ margin-bottom: 25px;
+ height: 700px;
+ overflow: hidden;
+ position: relative;
+
+ }
+
+ .w-sdc-resource-viewer-right-tab {
+ .b_6;
+
+ .hand;
+ background-color: transparent;
+ position: relative;
+ font-weight: 500;
+ line-height: 30px;
+ border: none;
+ border-bottom: solid 1px @color_c;
+ vertical-align: middle;
+ padding: 0px 30px 20px 0px;
+
+
+ &:focus,
+ &:active {
+ outline: none;
+
+ }
+ &.selected {
+ outline: none;
+ font-weight: 700;
+ .font-color.a;
+ }
+ }
+
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/properties/product-properties-view.html b/catalog-ui/app/scripts/view-models/component-viewer/properties/product-properties-view.html
new file mode 100644
index 0000000000..8aeda603f8
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/properties/product-properties-view.html
@@ -0,0 +1,76 @@
+<div class="w-sdc-component-viewer-right-properties">
+
+ <h4 class="w-sdc-resource-viewer-right-title">General Information</h4>
+ <div class="w-sdc-resource-viewer-right-content-section">
+ <div class='sdc-resource-viewer-sidebar-section-content-column-1'>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_TYPE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" translate="GENERAL_LABEL_PRODUCT"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_VERSION"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.version"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CATEGORY"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" tooltips tooltip-content="{{component.category}}" data-ng-bind="component.category"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CREATION_DATE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.creationDate | date: 'MM/dd/yyyy'"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_AUTHOR"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.creatorFullName"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CONTACT_ID"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.contacts[0]"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_PROJECT_CODE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.projectCode"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">Life Cycle Status:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value">
+ {{sdcMenu.LifeCycleStatuses[component.lifecycleState].text}}
+ </span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">Distribution Status:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value">
+ {{sdcMenu.DistributionStatuses[component.distributionStatus].text}}
+ </span>
+ </div>
+ </div>
+ <div class='sdc-resource-viewer-sidebar-section-content-column-2'>
+ <div class="sdc-resource-viewer-sidebar-section-content-item description">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_DESCRIPTION"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.description"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item" >
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_TAGS"></span>
+ <span tooltips tooltip-content="{{component.tags.join(', ')}}" class="sdc-resource-viewer-sidebar-section-content-tags" data-ng-repeat="(tag, tagName) in component.tags">
+ {{tagName}}{{$last ? '' : ','}}
+ </span>
+ </div>
+ </div>
+ </div>
+ <h4 class="w-sdc-resource-viewer-right-title">Additional Information</h4>
+
+ <div class="sdc-properties-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="additionalInformations.length">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Key</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Value</th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="additionalInformation in additionalInformations">
+ <td><span class="ellipsis-cols2" tooltips tooltip-content="{{additionalInformation.key}}">{{additionalInformation.key}}</span></td>
+ <td><span class="ellipsis-cols2" tooltips tooltip-content="{{additionalInformation.value}}">{{additionalInformation.value}}</span></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/properties/properties-view.less b/catalog-ui/app/scripts/view-models/component-viewer/properties/properties-view.less
new file mode 100644
index 0000000000..c0beed338f
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/properties/properties-view.less
@@ -0,0 +1,128 @@
+.w-sdc-component-viewer-right-properties {
+ .w-sdc-resource-viewer-tabs {
+ height: 42px;
+ }
+
+ .w-sdc-resource-viewer-right-content-section {
+ margin: 0 0 20px 16px;
+ }
+
+ .sdc-resource-viewer-sidebar-section-content-column-1,
+ .sdc-resource-viewer-sidebar-section-content-column-2 {
+ display: table-cell;
+ width: 50%;
+ }
+ .sdc-resource-viewer-sidebar-section-content-item {
+ .b_7;
+ margin-bottom: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 305px;
+ }
+ .sdc-resource-viewer-sidebar-section-content-item-label {
+ .bold;
+ .g_9;
+ }
+ .w-sdc-resource-viewer-right .sdc-resource-viewer-sidebar-section-content-column-1 {
+ .sdc-resource-viewer-sidebar-section-content-item {
+ width: 390px;
+ }
+ }
+ .sdc-resource-viewer-sidebar-section-content-item.description {
+ margin: 0;
+
+ .sdc-resource-viewer-sidebar-section-content-item-value {
+ word-wrap: break-word;
+ white-space: normal;
+ display: block;
+
+ }
+ .sdc-resource-viewer-sidebar-section-content-tags {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ display: inline-block;
+ max-width: 167px;
+
+ }
+ }
+
+ .w-sdc-resource-viewer-right-title {
+ .g_1;
+ .bg_n;
+ padding: 7px 15px;
+ margin: 0px 0 25px;
+ font-weight: bold;
+ }
+
+ .w-sdc-resource-viewer-right-table-head-cell {
+ .g_9;
+ text-align: left;
+ }
+
+ .cols-1 {
+ width: 100%;
+ }
+ .cols-2 {
+ width: 50%;
+ }
+ .cols-3 {
+ width: 33%;
+ }
+
+ .sdc-properties-container table tbody td.label {
+ .bold;
+ }
+
+ .w-sdc-designer-sidebar-section-content,
+ .w-sdc-resource-viewer-right-table {
+ display: table;
+ width: 100%;
+ .b_9;
+ word-break: break-all;
+
+ tbody td {
+ padding: 4px 20px 0 0;
+
+ .ellipsis-directive-more-less {
+ display: none;
+ }
+
+ .ellipsis-cols2 {
+ .sdc-ellipsis;
+ max-width: 340px;
+ }
+ .ellipsis-cols3 {
+ .sdc-ellipsis;
+ max-width: 200px;
+ }
+ }
+
+ }
+
+ .i-sdc-designer-sidebar-section-content-column-1 {
+ display: table-cell;
+ width: 50%;
+ }
+
+ .i-sdc-designer-sidebar-section-content-column-2 {
+ display: table-cell;
+ width: 50%;
+ }
+
+ .i-sdc-resource-viewer-artifacts-item-action,
+ .sdc-information-artifacts-icon {
+ .sprite;
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+ vertical-align: middle;
+ &.download {
+ .sprite.e-sdc-small-download;
+ }
+ &.preview {
+ .e-sdc-small-icon-eye;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/properties/resource-properties-view.html b/catalog-ui/app/scripts/view-models/component-viewer/properties/resource-properties-view.html
new file mode 100644
index 0000000000..c02e7aba7e
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/properties/resource-properties-view.html
@@ -0,0 +1,169 @@
+<div class="w-sdc-component-viewer-right-properties">
+
+ <h4 class="w-sdc-resource-viewer-right-title">General Information</h4>
+ <div class="w-sdc-resource-viewer-right-content-section">
+ <div class='sdc-resource-viewer-sidebar-section-content-column-1'>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_TYPE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" >Resource</span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_RESOURCE_TYPE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.resourceType | resourceTypeName"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_VERSION"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.version"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CATEGORY"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" tooltips tooltip-content="{{component.categories[0].name}}" data-ng-bind="component.categories[0].name"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_SUB_CATEGORY"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" tooltips tooltip-content="{{component.categories[0].subcategories[0].name}}" data-ng-bind="component.categories[0].subcategories[0].name"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CREATION_DATE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.creationDate | date: 'MM/dd/yyyy'"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_AUTHOR"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.creatorFullName"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_VENDOR_NAME"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.vendorName"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_VENDOR_RELEASE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.vendorRelease"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CONTACT_ID"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.contactId"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">Life Cycle Status:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value">
+ {{sdcMenu.LifeCycleStatuses[component.lifecycleState].text}}
+ </span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">System Name:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" tooltips tooltip-content="{{component.systemName}}" data-ng-bind="component.systemName"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_LICENSE_TYPE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.licenseType"></span>
+ </div>
+ </div>
+ <div class='sdc-resource-viewer-sidebar-section-content-column-2'>
+ <div class="sdc-resource-viewer-sidebar-section-content-item description">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_DESCRIPTION"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.description"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_TAGS"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-tags" data-ng-repeat="(tag, tagName) in component.tags" tooltips tooltip-content="{{component.tags.join(', ')}}">{{tagName}}{{$last ? '' : ','}}</span>
+ </div>
+ </div>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">Additional Information</h4>
+
+ <div class="sdc-properties-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="additionalInformations.length">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Key</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Value</th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="additionalInformation in additionalInformations">
+ <td><span class="ellipsis-cols2" tooltips tooltip-content="{{additionalInformation.key}}">{{additionalInformation.key}}</span></td>
+ <td><span class="ellipsis-cols2" tooltips tooltip-content="{{additionalInformation.value}}">{{additionalInformation.value}}</span></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">Properties</h4>
+ <div class="sdc-properties-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="component.properties.length">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Type (Constraints)</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Default Value</th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="property in component.properties">
+ <td><span class="ellipsis-cols2" data-tests-id="{{property.name}}" tooltips tooltip-content="{{property.name}}">{{property.name}}</span></td>
+ <td><span data-tests-id="{{property.type}}">{{property.type}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{property.defaultValue}}" tooltips tooltip-content="{{property.defaultValue}}">{{property.defaultValue}}</span></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">Requirements</h4>
+ <div class="sdc-requirements-container w-sdc-resource-viewer-right-content-section" >
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="hasItems(component.requirements)">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-1">Type</th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="(key, value) in component.requirements">
+ <td>{{value[0].name}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+
+ <h4 class="w-sdc-resource-viewer-right-title">Deployment Artifacts</h4>
+
+ <div class="sdc-information-container w-sdc-resource-viewer-right-content-section" >
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="hasItems(component.deploymentArtifacts)">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">File</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Version</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3"></th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="(artifactLogicName, artifact) in component.deploymentArtifacts">
+ <td><span class="ellipsis-cols2" data-tests-id="{{artifact.artifactDisplayName}}" tooltips tooltip-content="{{artifact.artifactDisplayName}}">{{artifact.artifactDisplayName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactName}}" tooltips tooltip-content="{{artifact.artifactName}}">{{artifact.artifactName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactVersion}}" tooltips tooltip-content="{{artifact.artifactVersion}}" data-ng-if="artifact.esId">{{artifact.artifactVersion}}</span></td>
+ <td class="cols-3">
+ <download-artifact class="sdc-information-artifacts-icon download" data-ng-if="artifact.artifactName" component="component" artifact="artifact"></download-artifact>
+ <!--span class="sdc-information-artifacts-icon preview"></span-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">Information Artifacts</h4>
+ <div class="sdc-information-container w-sdc-resource-viewer-right-content-section" >
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="hasItems(component.artifacts)">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">File</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Version</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3"></th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="(artifactLogicName, artifact) in component.artifacts">
+ <td><span class="ellipsis-cols2" data-tests-id="{{artifact.artifactDisplayName}}" tooltips tooltip-content="{{artifact.artifactDisplayName}}">{{artifact.artifactDisplayName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactName}}" tooltips tooltip-content="{{artifact.artifactName}}">{{artifact.artifactName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactVersion}}" tooltips tooltip-content="{{artifact.artifactVersion}}" data-ng-if="artifact.esId">{{artifact.artifactVersion}}</span></td>
+ <td class="cols-3">
+ <download-artifact class="sdc-information-artifacts-icon download" data-ng-if="artifact.artifactName" component="component" artifact="artifact"></download-artifact>
+ <!--span class="sdc-information-artifacts-icon preview"></span-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/component-viewer/properties/service-properties-view.html b/catalog-ui/app/scripts/view-models/component-viewer/properties/service-properties-view.html
new file mode 100644
index 0000000000..01f872c13c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/component-viewer/properties/service-properties-view.html
@@ -0,0 +1,167 @@
+<div class="w-sdc-component-viewer-right-properties">
+
+ <h4 class="w-sdc-resource-viewer-right-title">General Information</h4>
+ <div class="w-sdc-resource-viewer-right-content-section">
+ <div class='sdc-resource-viewer-sidebar-section-content-column-1'>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_TYPE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" translate="GENERAL_LABEL_SERVICE"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_VERSION"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.version"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CATEGORY"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" tooltips tooltip-content="{{component.categories[0].name}}" data-ng-bind="component.categories[0].name"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CREATION_DATE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.creationDate | date: 'MM/dd/yyyy'"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_AUTHOR"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.creatorFullName"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CONTACT_ID"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.contactId"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_PROJECT_CODE"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.projectCode"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">Life Cycle Status:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value"> {{sdcMenu.LifeCycleStatuses[component.lifecycleState].text}}</span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">Distribution Status:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value">{{sdcMenu.DistributionStatuses[component.distributionStatus].text}}</span>
+ </div>
+
+ <div class="sdc-resource-viewer-sidebar-section-content-item">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label">System Name:</span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" tooltips tooltip-content="{{component.systemName}}" data-ng-bind="component.systemName"></span>
+ </div>
+ </div>
+ <div class='sdc-resource-viewer-sidebar-section-content-column-2'>
+ <div class="sdc-resource-viewer-sidebar-section-content-item description">
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_DESCRIPTION"></span>
+ <span class="sdc-resource-viewer-sidebar-section-content-item-value" data-ng-bind="component.description"></span>
+ </div>
+ <div class="sdc-resource-viewer-sidebar-section-content-item" >
+ <span class="sdc-resource-viewer-sidebar-section-content-item-label" translate="GENERAL_LABEL_TAGS"></span>
+ <span tooltips tooltip-content="{{component.tags.join(', ')}}" class="sdc-resource-viewer-sidebar-section-content-tags" data-ng-repeat="(tag, tagName) in component.tags">{{tagName}}{{$last ? '' : ','}}</span>
+ </div>
+ </div>
+ </div>
+ <h4 class="w-sdc-resource-viewer-right-title">Additional Information</h4>
+
+ <div class="sdc-properties-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="additionalInformations.length">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Key</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Value</th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="additionalInformation in additionalInformations">
+ <td><span class="ellipsis-cols2" tooltips tooltip-content="{{additionalInformation.key}}">{{additionalInformation.key}}</span></td>
+ <td><span class="ellipsis-cols2" tooltips tooltip-content="{{additionalInformation.value}}">{{additionalInformation.value}}</span></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">Inputs</h4>
+
+ <div class="sdc-properties-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="inputs.length">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Type (Constraints)</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Default Value</th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="input in inputs">
+ <td><span class="ellipsis-cols2" data-tests-id="{{input.name}}" tooltips tooltip-content="{{input.name}}">{{input.name}}</span></td>
+ <td>{{input.type}}</td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{input.value}}" tooltips tooltip-content="{{input.value}}">{{input.value}}</span></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">API Artifacts</h4>
+
+ <div class="sdc-requirements-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="hasItems(component.serviceApiArtifacts)">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">File</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Version</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3"></th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="(artifactLogicName, artifact) in component.serviceApiArtifacts">
+ <td><span class="ellipsis-cols2" data-tests-id="{{artifact.artifactDisplayName}}" tooltips tooltip-content="{{artifact.artifactDisplayName}}">{{artifact.artifactDisplayName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactName}}" tooltips tooltip-content="{{artifact.artifactName}}">{{artifact.artifactName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactVersion}}" tooltips tooltip-content="{{artifact.artifactVersion}}" data-ng-if="artifact.esId">{{artifact.artifactVersion}}</span></td>
+ <td class="cols-3">
+ <download-artifact class="sdc-information-artifacts-icon download" data-ng-if="artifact.artifactName" component="component" artifact="artifact"></download-artifact>
+ <!--span class="sdc-information-artifacts-icon preview"></span-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <h4 class="w-sdc-resource-viewer-right-title">Deployment Artifacts</h4>
+
+ <div class="sdc-information-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="hasItems(component.deploymentArtifacts)">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">File</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Version</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3"></th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="(artifactLogicName, artifact) in component.deploymentArtifacts">
+ <td><span class="ellipsis-cols2" data-tests-id="{{artifact.artifactDisplayName}}" tooltips tooltip-content="{{artifact.artifactDisplayName}}">{{artifact.artifactDisplayName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactName}}" tooltips tooltip-content="{{artifact.artifactName}}">{{artifact.artifactName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactVersion}}" tooltips tooltip-content="{{artifact.artifactVersion}}" data-ng-if="artifact.esId">{{artifact.artifactVersion}}</span></td>
+ <td class="cols-3">
+ <download-artifact class="sdc-information-artifacts-icon download" data-ng-if="artifact.artifactName" component="component" artifact="artifact"></download-artifact>
+ <!--span class="sdc-information-artifacts-icon preview"></span-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+
+ <h4 class="w-sdc-resource-viewer-right-title">Information Artifacts</h4>
+
+ <div class="sdc-information-container w-sdc-resource-viewer-right-content-section">
+ <table class="w-sdc-resource-viewer-right-table" data-ng-show="hasItems(component.artifacts)">
+ <thead class="w-sdc-resource-viewer-right-table-head">
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-2">Name</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">File</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3">Version</th>
+ <th class="w-sdc-resource-viewer-right-table-head-cell cols-3"></th>
+ </thead>
+ <tbody>
+ <tr data-ng-repeat="(artifactLogicName, artifact) in component.artifacts">
+ <td><span class="ellipsis-cols2" data-tests-id="{{artifact.artifactDisplayName}}" tooltips tooltip-content="{{artifact.artifactDisplayName}}">{{artifact.artifactDisplayName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactName}}" tooltips tooltip-content="{{artifact.artifactName}}">{{artifact.artifactName}}</span></td>
+ <td><span class="ellipsis-cols3" data-tests-id="{{artifact.artifactVersion}}" tooltips tooltip-content="{{artifact.artifactVersion}}" data-ng-if="artifact.esId">{{artifact.artifactVersion}}</span></td>
+ <td class="cols-3">
+ <download-artifact class="sdc-information-artifacts-icon download" data-ng-if="artifact.artifactName" component="component" artifact="artifact"></download-artifact>
+ <!--span class="sdc-information-artifacts-icon preview"></span-->
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view-model.ts b/catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view-model.ts
new file mode 100644
index 0000000000..9979b6451b
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view-model.ts
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+
+ export interface IDashboardCoverViewModelScope extends ng.IScope {
+ showTutorial:boolean;
+ version:string;
+ modalInstance:ng.ui.bootstrap.IModalServiceInstance;
+ }
+
+ export class DashboardCoverViewModel {
+ static '$inject' = [
+ '$scope',
+ '$stateParams',
+ 'Sdc.Services.CacheService',
+ '$templateCache',
+ '$state',
+ '$modal',
+ 'sdcConfig'
+ ];
+
+ constructor(private $scope:IDashboardCoverViewModelScope,
+ private $stateParams:any,
+ private cacheService:Services.CacheService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $state:any,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private sdcConfig:Models.IAppConfigurtaion) {
+
+ // Show the tutorial if needed when the dashboard page is opened.<script src="bower_components/angular-filter/dist/angular-filter.min.js"></script>
+ // This is called from the welcome page.
+ if (this.$stateParams.show === 'tutorial') {
+ this.$scope.showTutorial = true;
+ } else if (this.$stateParams.show === 'whatsnew') {
+ this.$scope.version = this.cacheService.get('version');
+ this.openWhatsNewModal(this.$scope);
+ }
+
+ this.initScope();
+ }
+
+ private initScope = ():void => {
+
+ };
+
+ private openWhatsNewModal = (scope:IDashboardCoverViewModelScope):void => {
+
+ let onOk = ():void => {};
+
+ let onCancel = ():void => {
+ this.$state.go('dashboard.welcome', {show: ''});
+ };
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/whats-new/whats-new-view.html'),
+ controller: 'Sdc.ViewModels.WhatsNewViewModel',
+ size: 'sdc-l',
+ backdrop: 'static',
+ scope: scope,
+ resolve: {
+ 'version': scope.version
+ }
+ };
+
+ scope.modalInstance = this.$modal.open(modalOptions);
+ scope.modalInstance.result.then(onOk, onCancel);
+ };
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view.html b/catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view.html
new file mode 100644
index 0000000000..c8657cba23
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/dashboard/cover/dashboard-cover-view.html
@@ -0,0 +1 @@
+<div class="sdc-welcome-page"></div>
diff --git a/catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model-tests.ts b/catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model-tests.ts
new file mode 100644
index 0000000000..d97d9bb5ec
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model-tests.ts
@@ -0,0 +1,276 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+
+describe("dashboard View Model ", () => {
+
+ let $controllerMock:ng.IControllerService;
+ let $qMock:ng.IQService;
+ let $httpBackendMock:ng.IHttpBackendService;
+ let $scopeMock:Sdc.ViewModels.IDashboardViewModelScope;
+ let $stateMock:ng.ui.IStateService;
+ let $stateParams:any;
+ let entityServiceMock;
+
+
+ let getAllEntitiesResponseMock = [
+ {
+ "uniqueId": "855acdc7-7976-4913-9fa6-25220bd5a069",
+ "uuid": "8bc54f94-082c-42fa-9049-84767df3ff05",
+ "contactId": "qa1234",
+ "category": "VoIP Call Control",
+ "creationDate": 1447234712398,
+ "description": "ddddd",
+ "highestVersion": true,
+ "icon": "mobility",
+ "lastUpdateDate": 1447234712398,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "distributionStatus": "DISTRIBUTION_NOT_APPROVED",
+ "projectCode": "233233",
+ "name": "mas mas mas mas mas mas mas mas mas mas mas mas ma",
+ "version": "0.1",
+ "type": 0,
+ "tags": [
+ "mas mas mas mas mas mas mas mas mas mas mas mas ma"
+ ],
+ "systemName": "MasMasMasMasMasMasMasMasMasMasMasMasMa",
+ "vnf": true,
+ "$$hashKey": "object:30"
+ },
+ {
+ "uniqueId": "4bb577ce-cb2c-4cb7-bb39-58644b5e73cb",
+ "uuid": "e27f4723-c9ec-4160-89da-dbf84d19a7e3",
+ "contactId": "qa1111",
+ "category": "Mobility",
+ "creationDate": 1447238503181,
+ "description": "aqa",
+ "highestVersion": true,
+ "icon": "call_controll",
+ "lastUpdateDate": 1447248991388,
+ "lastUpdaterUserId": "jm0007",
+ "lastUpdaterFullName": "Joni Mitchell",
+ "lifecycleState": "CERTIFIED",
+ "distributionStatus": "DISTRIBUTION_REJECTED",
+ "projectCode": "111111",
+ "name": "martin18",
+ "version": "1.0",
+ "type": 0,
+ "tags": [
+ "martin18"
+ ],
+ "systemName": "Martin18",
+ "vnf": true
+ },
+ {
+ "uniqueId": "f192f4a6-7fbf-42e4-a546-37509df28dc1",
+ "uuid": "0b77dc0d-222e-4d10-85cd-e420c9481417",
+ "contactId": "fd1212",
+ "category": "Application Layer 4+/Web Server",
+ "creationDate": 1447233679778,
+ "description": "geefw",
+ "highestVersion": true,
+ "icon": "database",
+ "lastUpdateDate": 1447233681582,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "ger",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "ger"
+ ],
+ "vendorName": "fewwfe",
+ "vendorRelease": "fewew",
+ "systemName": "Ger",
+ "$$hashKey": "object:31"
+ },
+ {
+ "uniqueId": "78392d08-1859-47c2-b1f2-1a35b7f8c30e",
+ "uuid": "8cdd63b2-6a62-4376-9012-624f424f71d4",
+ "contactId": "qw1234",
+ "category": "Application Layer 4+/Application Servers",
+ "creationDate": 1447234046114,
+ "description": "test",
+ "highestVersion": true,
+ "icon": "router",
+ "lastUpdateDate": 1447234050545,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "test",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "test"
+ ],
+ "vendorName": "test",
+ "vendorRelease": "test",
+ "systemName": "Test",
+ "$$hashKey": "object:32"
+ },
+ {
+ "uniqueId": "939e153d-2236-410f-b4a9-3b4bf8c79c9e",
+ "uuid": "84862547-4f56-4058-b78e-40df5f374d7e",
+ "contactId": "qw1234",
+ "category": "Application Layer 4+/Application Servers",
+ "creationDate": 1447235242560,
+ "description": "jlk",
+ "highestVersion": true,
+ "icon": "database",
+ "lastUpdateDate": 1447235328062,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKIN",
+ "name": "new",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "new"
+ ],
+ "vendorName": "e",
+ "vendorRelease": "e",
+ "systemName": "New",
+ "$$hashKey": "object:33"
+ },
+ {
+ "uniqueId": "ece818e0-fd59-477a-baf6-e27461a7ce23",
+ "uuid": "8db823c2-6a9c-4636-8676-f5e713270dd7",
+ "contactId": "uf2345",
+ "category": "Network Layer 2-3/Router",
+ "creationDate": 1447235352429,
+ "description": "u",
+ "highestVersion": true,
+ "icon": "network",
+ "lastUpdateDate": 1447235370064,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "u",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "u"
+ ],
+ "vendorName": "u",
+ "vendorRelease": "u",
+ "systemName": "U",
+ "$$hashKey": "object:34"
+ }
+ ];
+ let getAllEntitiesDefered:ng.IDeferred<any> = null;
+
+ beforeEach(angular.mock.module('sdcApp'));
+
+ beforeEach(angular.mock.inject((_$controller_:ng.IControllerService,
+ _$httpBackend_:ng.IHttpBackendService,
+ _$rootScope_,
+ _$q_:ng.IQService,
+ _$state_:ng.ui.IStateService,
+ _$stateParams_:any) => {
+
+ $controllerMock = _$controller_;
+ $httpBackendMock = _$httpBackend_
+ $scopeMock = _$rootScope_.$new();
+ $qMock = _$q_;
+ $stateMock = _$state_;
+ $stateParams = _$stateParams_;
+
+
+ //handle all http request thet not relevant to the tests
+ $httpBackendMock.expectGET(/.*languages\/en_US.json.*/).respond(200, JSON.stringify({}));
+ // $httpBackendMock.expectGET(/.*resources\/certified\/abstract.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*rest\/version.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*configuration\/ui.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*user\/authorize.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/services.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/resources.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/products.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET('http://feHost:8181/sdc1/feProxy/rest/version').respond(200, JSON.stringify({}));
+
+ /**
+ * Mock the service
+ * @type {any}
+ */
+ getAllEntitiesDefered = $qMock.defer();
+ getAllEntitiesDefered.resolve(getAllEntitiesResponseMock);
+ entityServiceMock = jasmine.createSpyObj('entityServiceMock', ['getAllComponents']);
+ entityServiceMock.getAllComponents.and.returnValue(getAllEntitiesDefered.promise);
+
+ // $stateParams['show'] = '';
+
+ /**
+ * Need to inject into the controller only the objects that we want to MOCK
+ * those that we need to change theirs behaviors
+ */
+ $controllerMock(Sdc.ViewModels.DashboardViewModel, {
+ '$scope': $scopeMock,
+ '$stateParams': $stateParams,
+ 'Sdc.Services.EntityService': entityServiceMock,
+ });
+
+ }));
+
+
+ describe("when Controller 'DashboardViewModel' created", () => {
+
+ it('should generate all entities', () => {
+ $scopeMock.$apply();
+ expect($scopeMock.components.length).toBe(getAllEntitiesResponseMock.length);
+ });
+
+
+ it('should show tutorial page ', () => {
+ $stateParams.show = 'tutorial';
+
+ $controllerMock(Sdc.ViewModels.DashboardViewModel, {
+ '$scope': $scopeMock,
+ '$stateParams': $stateParams,
+ 'Sdc.Services.EntityService': entityServiceMock,
+ //to complete injects
+ });
+
+ $scopeMock.$apply();
+ expect($scopeMock.isFirstTime).toBeTruthy();
+ expect($scopeMock.showTutorial).toBeTruthy();
+ });
+
+ });
+
+
+ describe("when function 'entitiesCount' invoked", () => {
+
+ beforeEach(() => {
+ $controllerMock(Sdc.ViewModels.DashboardViewModel, {
+ '$scope': $scopeMock,
+ 'Sdc.Services.EntityService': entityServiceMock,
+ });
+ $scopeMock.$apply();
+ });
+
+ it('should return entities count per folder', () => {
+
+ });
+
+
+ });
+});
diff --git a/catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model.ts b/catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model.ts
new file mode 100644
index 0000000000..8325a3f133
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/dashboard/dashboard-view-model.ts
@@ -0,0 +1,415 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+ import ResourceType = Sdc.Utils.Constants.ResourceType;
+
+ export interface IDashboardViewModelScope extends ng.IScope {
+
+ isLoading: boolean;
+ components: Array<Models.Components.Component>;
+ folders: FoldersMenu;
+ roles: Models.IConfigRoles;
+ user: Models.IUserProperties;
+ sdcConfig:Models.IAppConfigurtaion;
+ sdcMenu:Models.IAppMenu;
+ sharingService:Sdc.Services.SharingService;
+ showTutorial:boolean;
+ isFirstTime:boolean;
+ version:string;
+ checkboxesFilter:CheckboxesFilter;
+
+ onImportVfc(file:any):void;
+ onImportVf(file:any):void;
+ openCreateModal(componentType: Utils.Constants.ComponentType, importedFile:any): void;
+ openWhatsNewModal(version:string):void;
+ openDesignerModal(isResource:boolean, uniqueId:string): void;
+ openViewerModal(entity:any) : void;
+ setSelectedFolder(folderItem: FoldersItemsMenu): void;
+ entitiesCount(folderItem: FoldersItemsMenu): number;
+ getCurrentFolderDistributed(): Array<Models.Components.Component>;
+ changeLifecycleState(entity:any, data:any): void;
+ goToComponent(component:Models.Components.Component):void;
+ wizardDebugEdit:Function;
+ notificationIconCallback:Function;
+ }
+
+ interface CheckboxesFilter {
+ // Statuses
+ selectedStatuses:Array<string>;
+ // distributed
+ distributed:Array<string>;
+ }
+
+ export interface IItemMenu {
+
+ }
+
+ export interface IMenuItemProperties {
+ text: string;
+ group: string;
+ state: string;
+ dist: string;
+ groupname: string;
+ states: Array<any>;
+ }
+
+ export class FoldersMenu {
+
+ private _folders: Array<FoldersItemsMenu> = [];
+
+ constructor(folders: Array<IMenuItemProperties>) {
+ let self = this;
+ folders.forEach(function(folder: IMenuItemProperties) {
+ if (folder.groupname){
+ self._folders.push(new FoldersItemsMenuGroup(folder));
+ } else {
+ self._folders.push(new FoldersItemsMenu(folder));
+ }
+ });
+ self._folders[0].setSelected(true);
+ }
+
+ public getFolders = (): Array<FoldersItemsMenu> => {
+ return this._folders;
+ };
+
+ public getCurrentFolder = (): FoldersItemsMenu => {
+ let menuItem: FoldersItemsMenu = undefined;
+ this.getFolders().forEach(function(tmpFolder: FoldersItemsMenu) {
+ if (tmpFolder.isSelected()){
+ menuItem = tmpFolder;
+ }
+ });
+ return menuItem;
+ };
+
+ public setSelected = (folder: FoldersItemsMenu):void => {
+ this.getFolders().forEach(function(tmpFolder: FoldersItemsMenu) {
+ tmpFolder.setSelected(false);
+ });
+ folder.setSelected(true);
+ }
+
+ }
+
+ export class FoldersItemsMenu implements IItemMenu {
+
+ public text:string;
+ public group: string;
+ public state: string;
+ public dist: string;
+ public states: Array<any>;
+
+ private selected: boolean = false;
+
+ constructor(menuProperties: IMenuItemProperties) {
+ this.text = menuProperties.text;
+ this.group = menuProperties.group;
+ this.state = menuProperties.state;
+ this.states = menuProperties.states;
+ this.dist = menuProperties.dist;
+ }
+
+ public isSelected = ():boolean => {
+ return this.selected;
+ };
+
+ public setSelected = (value: boolean):void => {
+ this.selected = value;
+ };
+
+ public isGroup = ():boolean => {
+ return false;
+ }
+
+ }
+
+ export class FoldersItemsMenuGroup extends FoldersItemsMenu {
+
+ public groupname:string;
+
+ constructor(menuProperties: IMenuItemProperties) {
+ super(menuProperties);
+ this.groupname = menuProperties.groupname;
+ }
+
+ public isGroup = ():boolean => {
+ return true;
+ }
+
+ }
+
+ export class DashboardViewModel {
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ 'Sdc.Services.EntityService',
+ '$http',
+ 'sdcConfig',
+ 'sdcMenu',
+ '$modal',
+ '$templateCache',
+ '$state',
+ '$stateParams',
+ 'Sdc.Services.UserResourceService',
+ 'Sdc.Services.SharingService',
+ 'Sdc.Services.CacheService',
+ '$q',
+ 'ComponentFactory',
+ 'ChangeLifecycleStateHandler',
+ 'ModalsHandler',
+ 'MenuHandler'
+ ];
+
+ private components: Array<Models.Components.Component>;
+
+ constructor(private $scope:IDashboardViewModelScope,
+ private $filter:ng.IFilterService,
+ private entityService:Services.EntityService,
+ private $http:ng.IHttpService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private sdcMenu:Models.IAppMenu,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $state:any,
+ private $stateParams:any,
+ private userResourceService:Sdc.Services.IUserResourceClass,
+ private sharingService:Services.SharingService,
+ private cacheService:Services.CacheService,
+ private $q:ng.IQService,
+ private ComponentFactory: Sdc.Utils.ComponentFactory,
+ private ChangeLifecycleStateHandler: Sdc.Utils.ChangeLifecycleStateHandler,
+ private ModalsHandler: Sdc.Utils.ModalsHandler,
+ private MenuHandler: Utils.MenuHandler
+ ) {
+ this.initScope();
+ this.initFolders();
+ this.initEntities(true);
+
+ if (this.$stateParams){
+
+ if (this.$state.params.folder){
+ let self = this;
+ let folderName = this.$state.params.folder.replaceAll("_"," ");
+
+ this.$scope.folders.getFolders().forEach(function(tmpFolder: FoldersItemsMenu) {
+ if (tmpFolder.text === folderName){
+ self.$scope.setSelectedFolder(tmpFolder);
+ }
+ });
+ }
+
+ // Show the tutorial if needed when the dashboard page is opened.<script src="bower_components/angular-filter/dist/angular-filter.min.js"></script>
+ // This is called from the welcome page.
+ else if (this.$stateParams.show==='tutorial'){
+ this.$scope.showTutorial = true;
+ this.$scope.isFirstTime = true;
+ }
+ }
+ }
+
+ private initFolders = ():void => {
+ if (this.$scope.user) {
+ this.$scope.folders = new FoldersMenu(this.$scope.roles[this.$scope.user.role].folder);
+ }
+ };
+
+ private initScope = ():void => {
+ let self = this;
+
+ this.$scope.version = this.cacheService.get('version');
+ this.$scope.sharingService = this.sharingService;
+ this.$scope.isLoading = false;
+ this.$scope.sdcConfig = this.sdcConfig;
+ this.$scope.sdcMenu = this.sdcMenu;
+ this.$scope.user = this.userResourceService.getLoggedinUser();
+ this.$scope.roles = this.sdcMenu.roles;
+ this.$scope.showTutorial = false;
+ this.$scope.isFirstTime = false;
+
+ // Open onboarding modal
+ this.$scope.notificationIconCallback = ():void => {
+ this.ModalsHandler.openOnboadrdingModal('Import').then(()=>{
+ // OK
+ }, ()=>{
+ // ERROR
+ });
+ };
+
+ // Checkboxes filter init
+ this.$scope.checkboxesFilter = <CheckboxesFilter>{};
+ this.$scope.checkboxesFilter.selectedStatuses = [];
+ this.$scope.checkboxesFilter.distributed = [];
+
+ let appendTemplateAndControllerForProduct:Function = (modalOptions:ng.ui.bootstrap.IModalSettings, isViewer:boolean):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+
+ if (isViewer) {
+ modalOptions.template = this.$templateCache.get(viewModelsHtmlBasePath + 'entity-viewer/product-viewer-view.html');
+ modalOptions.controller = 'Sdc.ViewModels.ResourceViewerViewModel';
+ } else {
+ modalOptions.template = this.$templateCache.get(viewModelsHtmlBasePath + 'entity-handler/product-form/product-form-view.html');
+ modalOptions.controller = 'Sdc.ViewModels.ProductFormViewModel';
+ }
+
+ };
+
+ this.$scope.onImportVf = (file:any):void => {
+ if(file && file.filename) {
+ // Check that the file has valid extension.
+ let fileExtension:string = file.filename.split(".").pop();
+ if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1){
+ this.$state.go('workspace.general', {type:Utils.Constants.ComponentType.RESOURCE.toLowerCase(), importedFile: file, resourceType: ResourceType.VF});
+ }else {
+ let data:Sdc.ViewModels.IClientMessageModalModel = {
+ title: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE"),
+ message: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS", "{'extensions': '" + this.sdcConfig.csarFileExtension + "'}"),
+ severity: Utils.Constants.SEVERITY.ERROR
+ };
+ this.ModalsHandler.openClientMessageModal(data);
+ }
+ }
+ };
+
+ this.$scope.onImportVfc = (file:any):void => {
+ if(file && file.filename) {
+ // Check that the file has valid extension.
+ let fileExtension:string = file.filename.split(".").pop();
+ if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1){
+ this.$state.go('workspace.general', {type:Utils.Constants.ComponentType.RESOURCE.toLowerCase(), importedFile: file, resourceType: ResourceType.VFC});
+ }else {
+ let data:Sdc.ViewModels.IClientMessageModalModel = {
+ title: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE"),
+ message: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS", "{'extensions': '" + this.sdcConfig.toscaFileExtension + "'}"),
+ severity: Utils.Constants.SEVERITY.ERROR
+ };
+ this.ModalsHandler.openClientMessageModal(data);
+ }
+ }
+ };
+
+ this.$scope.openCreateModal = (componentType: string, importedFile:any):void => {
+ if (importedFile){
+ this.initEntities(true); // Return from import
+ } else {
+ this.$state.go('workspace.general', {type:componentType.toLowerCase()});
+ }
+
+ };
+
+ this.$scope.entitiesCount = (folderItem: FoldersItemsMenu): any => {
+ let self = this;
+ let total: number = 0;
+ if (folderItem.isGroup()){
+ this.$scope.folders.getFolders().forEach(function(tmpFolder: FoldersItemsMenu){
+ if (tmpFolder.group && tmpFolder.group===(<FoldersItemsMenuGroup>folderItem).groupname){
+ total = total + self._getTotalCounts(tmpFolder, self);
+ }
+ });
+ } else {
+ total = total + self._getTotalCounts(folderItem, self);
+ }
+ return total;
+ };
+
+ this.$scope.getCurrentFolderDistributed = (): Array<any> => {
+ let self = this;
+ let states = [];
+ if (this.$scope.folders) {
+ let folderItem:FoldersItemsMenu = this.$scope.folders.getCurrentFolder();
+ if (folderItem.isGroup()) {
+ this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) {
+ if (tmpFolder.group && tmpFolder.group === (<FoldersItemsMenuGroup>folderItem).groupname) {
+ self._setStates(tmpFolder, states);
+ }
+ });
+ } else {
+ self._setStates(folderItem, states);
+ }
+ }
+ return states;
+ };
+
+ this.$scope.setSelectedFolder = (folderItem: FoldersItemsMenu):void => {
+ this.$scope.folders.setSelected(folderItem);
+ };
+
+ this.$scope.goToComponent = (component:Models.Components.Component):void => {
+ this.$scope.isLoading=true;
+ this.$state.go('workspace.general', {id: component.uniqueId, type:component.componentType.toLowerCase() });
+ };
+
+ };
+
+ private _getTotalCounts(tmpFolder, self): number {
+ let total: number = 0;
+ if (tmpFolder.dist !== undefined) {
+ let distributions = tmpFolder.dist.split(',');
+ distributions.forEach((item:any) => {
+ total = total + self.getEntitiesByStateDist(tmpFolder.state, item).length;
+ });
+ }
+ else {
+ total = total + self.getEntitiesByStateDist(tmpFolder.state, tmpFolder.dist).length;
+ }
+ return total;
+ }
+
+ private _setStates(tmpFolder, states) {
+ if (tmpFolder.states !== undefined) {
+ tmpFolder.states.forEach(function (item:any) {
+ states.push({"state": item.state, "dist": item.dist});
+ });
+ } else {
+ states.push({"state": tmpFolder.state, "dist": tmpFolder.dist});
+ }
+ }
+
+ private initEntities = (reload:boolean):void => {
+ this.$scope.isLoading = reload;
+ this.entityService.getAllComponents().then(
+ (components: Array<Models.Components.Component>) => {
+ this.components = components;
+ this.$scope.components = components;
+ this.$scope.isLoading = false;
+ });
+ };
+
+ private getEntitiesByStateDist = (state: string, dist: string) : Array<Models.Components.Component> => {
+ let gObj:Array<Models.Components.Component>;
+ if (this.components && (state || dist)) {
+ gObj = this.components.filter(function (obj:Models.Components.Component) {
+ if (dist !== undefined && obj.distributionStatus === dist && obj.lifecycleState === state){
+ return true;
+ } else if (dist === undefined && obj.lifecycleState === state) {
+ return true;
+ }
+ return false;
+ });
+ } else {
+ gObj = [];
+ }
+ return gObj;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/dashboard/dashboard-view.html b/catalog-ui/app/scripts/view-models/dashboard/dashboard-view.html
new file mode 100644
index 0000000000..0aef4e19c6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/dashboard/dashboard-view.html
@@ -0,0 +1,106 @@
+<div class="sdc-catalog-container">
+ <loader data-display="isLoading"></loader>
+
+ <!-- HEADER -->
+<!--
+ <ecomp-header menu-data="menuItems" version="{{version}}"></ecomp-header>
+-->
+
+ <div class="w-sdc-main-container">
+
+ <perfect-scrollbar include-padding="true" class="w-sdc-main-right-container">
+
+ <!-- ADD Component -->
+ <div ng-if="user.role === 'DESIGNER' || user.role === 'PRODUCT_MANAGER'" class="w-sdc-dashboard-card-new"
+ data-ng-mouseleave="displayActions = false"
+ data-ng-mouseover="displayActions = true"
+ data-ng-init="displayActions = false">
+ <div class="w-sdc-dashboard-card-new-content" data-tests-id="AddButtonsArea">
+ <div class="w-sdc-dashboard-card-new-content-plus" data-ng-show="!displayActions"></div>
+ <div class="sdc-dashboard-create-element-container" data-ng-show="displayActions">
+ <button data-ng-if="roles[user.role].dashboard.showCreateNewProduct" class="tlv-btn outline blue" data-ng-click="openCreateModal('PRODUCT')">Create Product</button>
+ <button data-ng-if="roles[user.role].dashboard.showCreateNew" data-tests-id="createResourceButton" class="tlv-btn outline blue" data-ng-click="openCreateModal('RESOURCE')">Add VF</button>
+ <button data-ng-if="roles[user.role].dashboard.showCreateNew" data-tests-id="createServiceButton" class="tlv-btn outline blue" data-ng-click="openCreateModal('SERVICE')">Add Service</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Import Component -->
+ <div ng-if="user.role === 'DESIGNER'" class="w-sdc-dashboard-card-new"
+ data-ng-mouseleave="displayActions = false"
+ data-ng-mouseover="displayActions = true"
+ data-ng-init="displayActions = false">
+ <div class="w-sdc-dashboard-card-new-content" data-tests-id="importButtonsArea" >
+ <div class="w-sdc-dashboard-card-import-content-plus" data-ng-show="!displayActions"></div>
+ <div class="sdc-dashboard-import-element-container" data-ng-show="displayActions">
+ <div data-ng-if="roles[user.role].dashboard.showCreateNew" class="tlv-btn outline blue">Import VFC
+ <file-opener on-file-upload="onImportVfc(file)" data-tests-id="importVFCbutton" extensions="{{sdcConfig.toscaFileExtension}}" data-ng-click="displayActions=false"></file-opener>
+ </div>
+ <div data-ng-if="roles[user.role].dashboard.showCreateNew" class="tlv-btn outline blue" data-ng-click="notificationIconCallback()">Import VSP</div>
+ <div data-ng-if="roles[user.role].dashboard.showCreateNew" class="tlv-btn outline blue import-dcae">Import DCAE asset
+ <file-opener on-file-upload="onImportVf(file)" data-tests-id="importVFbutton" extensions="{{sdcConfig.csarFileExtension}}" data-ng-click="displayActions=false"></file-opener>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div
+ data-ng-class="{'sdc-hide-popover': hidePopover,'resource' : component.isResource(),'service' : component.isService()}"
+ class="w-sdc-dashboard-card"
+ data-ng-repeat="component in components | entityFilter:checkboxesFilter | filter:search"
+ >
+ <div class="w-sdc-dashboard-card-body" data-tests-id="dashboard-Elements" data-ng-click="goToComponent(component)">
+ <!--<div class="w-sdc-dashboard-card-description">{{entity.description}}</div>-->
+ <div class="w-sdc-dashboard-card-avatar"><span data-tests-id="asset-type" class="{{component.getComponentSubType()}}"></span></div>
+ <!--<div class="w-sdc-dashboard-card-edit" data-ng-class="component.lifecycleState" data-tests-id="{{component.lifecycleState}}"></div>-->
+ <div class="w-sdc-dashboard-card-schema-image {{component.iconSprite}} {{component.icon}}"
+ data-ng-class="{'sprite-resource-icons': component.isResource(), 'sprite-services-icons': component.isService()}"
+ data-tests-id="{{component.name}}"></div>
+ <div class="w-sdc-dashboard-card-info-name-container">
+ <span class="w-sdc-dashboard-card-info-name" tooltips
+ tooltip-content="{{component.name | resourceName}}"> {{component.name | resourceName}}</span>
+ </div>
+ </div>
+ <div class="w-sdc-dashboard-card-footer">
+
+ <div class="w-sdc-dashboard-card-info">
+ <div class="w-sdc-dashboard-card-info-lifecycleState">
+ <span class="w-sdc-dashboard-card-info-lifecycleState" tooltips
+ tooltip-content="{{component.getStatus(sdcMenu)}}"> {{component.getStatus(sdcMenu)}}</span>
+ </div>
+ <div class="w-sdc-dashboard-card-info-user"data-tests-id="{{component.name}}Version">V {{component.version}}</div>
+ </div>
+ <!--<div class="w-sdc-dashboard-card-info-lifecycleState-icon sprite-new {{sdcMenu.LifeCycleStatuses[component.lifecycleState].icon}}"></div>-->
+ </div>
+ </div>
+
+ </perfect-scrollbar>
+
+ <div class="w-sdc-left-sidebar">
+ <div class="i-sdc-left-sidebar-item "
+ data-ng-repeat="folder in folders.getFolders()"
+ data-ng-class="{'category-title': folder.isGroup(), 'selectedLink': folder.isSelected()}"
+ >
+ <span data-ng-if="folder.isGroup()">{{folder.text}}</span>
+
+ <sdc-checkbox data-ng-if="!folder.isGroup() && !folder.dist"
+ elem-id="checkbox-{{folder.text | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.selectedStatuses"
+ sdc-checklist-value="folder.state"
+ text="{{folder.text}}"></sdc-checkbox>
+
+ <sdc-checkbox data-ng-if="!folder.isGroup() && folder.dist"
+ elem-id="checkbox-{{folder.text | lowercase | clearWhiteSpaces}}"
+ sdc-checklist-model="checkboxesFilter.distributed"
+ sdc-checklist-value="folder.dist"
+ text="{{folder.text}}"></sdc-checkbox>
+ <span class="i-sdc-left-sidebar-item-state-count">{{entitiesCount(folder)}}</span>
+ </div>
+ </div>
+
+ </div>
+
+ <top-nav top-lvl-selected-index="0" version="{{version}}" search-bind="search.filterTerm" notification-icon-callback="notificationIconCallback" version="{{version}}"></top-nav>
+
+</div>
+<div data-ui-view=""></div>
diff --git a/catalog-ui/app/scripts/view-models/dashboard/dashboard.less b/catalog-ui/app/scripts/view-models/dashboard/dashboard.less
new file mode 100644
index 0000000000..7b2522113b
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/dashboard/dashboard.less
@@ -0,0 +1,420 @@
+.sdc-dashboard-container {
+ .tlv-loader {
+ top: -110px;
+ left: 80px;
+ }
+ .sdc-hide-popover {
+ .popover {
+ display: none !important;
+ }
+ }
+}
+
+.w-sdc-left-sidebar-nav {
+ margin-top: 46px;
+}
+
+.w-sdc-main-right-container-element {
+ float: left;
+ height: 217px;
+ width: 217px;
+ margin: 10px;
+ position: relative;
+}
+
+.w-sdc-main-right-container-element-details-container {
+ position: absolute;
+ top: 165px;
+ left: 50px;
+}
+
+.w-sdc-main-right-container-element-name {
+ font-weight: bold;
+}
+
+.w-sdc-main-right-container-element-owner {
+
+}
+
+//////////////////////////////Cards////////////////////
+.w-sdc-dashboard-card-new {
+ border: 2px dashed @color_m;
+ .border-radius(2px);
+ cursor: pointer;
+ display: inline-block;
+ height: 200px;
+ margin: 9px;
+ position: relative;
+ vertical-align: middle;
+ width: 204px;
+}
+
+.w-sdc-dashboard-card-new-content {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ height: 100%;
+}
+
+.w-sdc-dashboard-card-new-content-plus {
+ .sprite-new;
+ .add-icon;
+ position: relative;
+ margin-bottom: 20px;
+
+ &:after {
+ .n_14_m;
+ content: 'ADD';
+ position: absolute;
+ top: 25px;
+ left: -3px;
+ vertical-align: -50%;
+ }
+}
+
+.w-sdc-dashboard-card-import-content-plus {
+ .sprite-new;
+ .import-icon;
+ position: relative;
+ margin-bottom: 20px;
+
+ &:after {
+ .n_14_m;
+ content: 'IMPORT';
+ position: absolute;
+ top: 25px;
+ left: -16px;
+ vertical-align: -50%;
+ }
+}
+
+.sdc-dashboard-create-element-container,
+.sdc-dashboard-import-element-container {
+
+ width: 140px;
+
+ .tlv-btn.import-dcae {
+ padding: 0;
+ }
+
+ .tlv-btn {
+ position: relative;
+ width: 100%;
+ margin-bottom: 10px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ input[type="file"] {
+ cursor: inherit;
+ filter: alpha(opacity=0);
+ opacity: 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 138px;
+ height: 30px;
+ }
+}
+
+.w-sdc-dashboard-card {
+ width: 204px;
+ height: 200px;
+ background-color: @main_color_p;
+ .border-radius(2px);
+ .box-shadow(0px 2px 2px 0px rgba(24, 24, 25, 0.05));
+ display: inline-block;
+ margin: 10px;
+ position: relative;
+ vertical-align: middle;
+ border: solid 1px @main_color_p;
+
+ &:hover {
+ border: solid 1px @main_color_o;
+ .box-shadow(3px 3px 2px 0px rgba(24, 24, 25, 0.05));
+ }
+
+ &:active {
+ border: solid 1px @main_color_c;
+ .box-shadow(3px 3px 2px 0px rgba(24, 24, 25, 0.05));
+ }
+}
+
+.w-sdc-dashboard-card-body {
+ .hand;
+ border-bottom: 1px solid @color_j;
+ height: 155px;
+ position: relative;
+ text-align: center;
+}
+
+.w-sdc-dashboard-card-description {
+ .c_3;
+ .hand;
+ background-color: rgba(57, 73, 84, 0.9);
+ border-radius: 4px 4px 0 0;
+ bottom: 0;
+ left: 0;
+ opacity: 0;
+ padding: 10px;
+ position: absolute;
+ right: 0;
+ text-align: left;
+ top: 0;
+ word-wrap: break-word;
+ z-index: 4;
+ min-height: 100px;
+ overflow: hidden;
+}
+
+
+.w-sdc-dashboard-card-schema {
+ margin-top: 30px;
+}
+
+.w-sdc-dashboard-card-edit {
+ .hand;
+ position: absolute;
+ right: 13px;
+ top: 15px;
+ z-index: 2;
+}
+
+.w-sdc-dashboard-card-footer {
+ padding: 3px 12px 10px 12px;
+ position: relative;
+}
+
+.w-sdc-dashboard-card-avatar {
+ .uppercase;
+ border-radius: 50%;
+ display: inline-block;
+ position: absolute;
+ left: -6px;
+ text-align: center;
+ top: -6px;
+
+ span {
+
+ background-color: @main_color_p;
+ .border-radius(15px);
+ color: @color_c;
+ content: '';
+ height: 30px;
+ text-align: center;
+ display: block;
+ border: solid 2px #ECEFF3;
+ padding: 3px 10px 2px 10px;
+
+ &.VF {
+ .j_14_m;
+ &::before {
+ content: 'VF';
+ }
+ }
+
+ &.VFC {
+ .j_14_m;
+ &::before {
+ content: 'VFC';
+ }
+ }
+
+ &.CP {
+ .j_14_m;
+ &::before {
+ content: 'CP';
+ }
+ }
+
+ &.VL {
+ .j_14_m;
+ &::before {
+ content: 'VL';
+ }
+ }
+
+ &.SERVICE {
+ .c_14_m;
+ &::before {
+ content: 'S';
+ }
+ }
+
+ &.PRODUCT {
+ .b_14_m;
+ &::before {
+ content: 'P';
+ }
+ }
+
+ &.green {
+ .d_12;
+ &::before {
+ content: 'R';
+ }
+ }
+ &.red {
+ .r_12;
+ &::before {
+ content: 'S';
+ }
+ }
+ &.dblack {
+ .s_12;
+ &::before {
+ content: 'P';
+ }
+ }
+ }
+}
+
+.w-sdc-dashboard-card-info {
+ display: inline-block;
+ vertical-align: middle;
+ max-width: 165px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.w-sdc-dashboard-card-info-name-container{
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ margin: 0 0 2px 10px;
+}
+.w-sdc-dashboard-card-info-name {
+ .m_14_m;
+ display: inline-block;
+ vertical-align: middle;
+ max-width: 165px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.w-sdc-dashboard-card-info-lifecycleState {
+ .m_13_m;
+ display: inline-block;
+ vertical-align: middle;
+ max-width: 165px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.w-sdc-dashboard-card-info-user {
+ .n_13_r;
+ line-height: 18px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 100%;
+}
+
+.w-sdc-dashboard-card-menu-button {
+ display: inline-block;
+ padding: 12px 0 0 10px;
+ position: absolute;
+ right: 12px;
+ top: 8px;
+ border-left: solid 1px @color_k;
+ height: 42px;
+
+ &:hover {
+ .w-sdc-dashboard-card-menu {
+ display: block;
+ }
+ }
+}
+
+.w-sdc-dashboard-card-menu {
+ .bg_c;
+ border-radius: 0 0 4px 4px;
+ border-top: 3px solid @color_a;
+ box-shadow: 0 2px 2px 0px rgba(0, 0, 0, 0.2);
+ color: @color_s;
+ display: none;
+ min-height: 30px;
+ padding: 9px 0;
+ position: absolute;
+ right: -27px;
+ width: 208px;
+ z-index: 9;
+ max-height: 164px;
+
+ &::before {
+ //TODO: Missing image for small blue triangle.
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE1OTIzNDI1MENFQjExRTU4ODRERTI1MDM2REZCOUYzIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE1OTIzNDI2MENFQjExRTU4ODRERTI1MDM2REZCOUYzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU5MjM0MjMwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTU5MjM0MjQwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4gBXTlAAAAOElEQVR42mK0rp7NgASMgZgFiE/CBJjQJPcA8U4gNkdXAJMUAGJ+ZEVMaJIwAFfEhEUSRRFAgAEAVtgJyiLAPWAAAAAASUVORK5CYII=');
+ content: '';
+ display: block;
+ height: 21px;
+ position: absolute;
+ right: 24px;
+ top: -24px;
+ width: 184px;
+ background-repeat: no-repeat;
+ background-position: 175px 16px;
+ }
+}
+
+.i-sdc-dashboard-card-menu-item {
+ .hand;
+ line-height: 24px;
+ padding: 0 10px;
+ &:hover { .a_7; }
+}
+
+.w-sdc-dashboard-card-info-lifecycleState-icon{
+ position:absolute;
+ bottom:18px;
+ right:10px;
+}
+
+// Same for dashboard and catalog view.
+.w-sdc-dashboard-card-schema-image {
+ position: absolute;
+ top: 41%;
+
+ //TODO: Israel - remove this after getting the services sprite.
+ height: 45px;
+ width: 53px;
+ background-repeat: no-repeat;
+
+ // Center the icon vertical and horizontal.
+ margin: auto;
+ left: 0;
+ right: 0;
+ top: -10px;
+ bottom: 0;
+}
+
+/* dashboard card main icons */
+.w-sdc-dashboard-card-schema-image.service { .s-sdc-service }
+.w-sdc-dashboard-card-schema-image.resource { .s-sdc-resource }
+
+/* dashboard card statuses icons */
+.w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKIN { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKIN; }
+.w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKOUT { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKOUT; }
+.w-sdc-dashboard-card-edit.CERTIFIED { .sprite; .s-sdc-state.CERTIFIED; }
+.w-sdc-dashboard-card-edit.READY_FOR_CERTIFICATION { .sprite; .s-sdc-state.READY_FOR_CERTIFICATION; }
+.w-sdc-dashboard-card-edit.CERTIFICATION_IN_PROGRESS { .sprite; .s-sdc-state.CERTIFICATION_IN_PROGRESS; }
+.w-sdc-dashboard-card-edit.DISTRIBUTED { .sprite; .s-sdc-state.DISTRIBUTED; }
+
+.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKIN { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKIN.green; }
+.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKOUT { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKOUT.green; }
+.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.CERTIFIED { .sprite; .s-sdc-state.CERTIFIED.green; }
+.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.READY_FOR_CERTIFICATION { .sprite; .s-sdc-state.READY_FOR_CERTIFICATION.green; }
+.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.CERTIFICATION_IN_PROGRESS { .sprite; .s-sdc-state.CERTIFICATION_IN_PROGRESS.green; }
+.w-sdc-dashboard-card-avatar.green + .w-sdc-dashboard-card-edit.DISTRIBUTED { .sprite; .s-sdc-state.DISTRIBUTED.green; }
+
+.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKIN { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKIN.red; }
+.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.NOT_CERTIFIED_CHECKOUT { .sprite; .s-sdc-state.NOT_CERTIFIED_CHECKOUT.red; }
+.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.CERTIFIED { .sprite; .s-sdc-state.CERTIFIED.red; }
+.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.READY_FOR_CERTIFICATION { .sprite; .s-sdc-state.READY_FOR_CERTIFICATION.red; }
+.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.CERTIFICATION_IN_PROGRESS { .sprite; .s-sdc-state.CERTIFICATION_IN_PROGRESS.red; }
+.w-sdc-dashboard-card-avatar.red + .w-sdc-dashboard-card-edit.DISTRIBUTED { .sprite; .s-sdc-state.DISTRIBUTED.red; }
diff --git a/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view-model.ts b/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view-model.ts
new file mode 100644
index 0000000000..092594b0d5
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view-model.ts
@@ -0,0 +1,354 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+
+ export interface IEditArtifactModel {
+ artifactResource: Models.ArtifactModel;
+ artifactTypes: Array<string>;
+ artifactFile: any;
+ }
+
+ export interface IArtifactResourceFormViewModelScope extends ng.IScope {
+ forms:any;
+ $$childTail: any;
+ isNew: boolean;
+ isLoading: boolean;
+ validationPattern: RegExp;
+ urlValidationPattern: RegExp;
+ labelValidationPattern: RegExp;
+ integerValidationPattern: RegExp;
+ commentValidationPattern: RegExp;
+ artifactType: string;
+ editArtifactResourceModel: IEditArtifactModel;
+ defaultHeatTimeout: number;
+ validExtensions: any;
+ originalArtifactName: string;
+ editForm: ng.IFormController;
+ footerButtons: Array<any>;
+ modalInstanceArtifact:ng.ui.bootstrap.IModalServiceInstance;
+
+ fileExtensions():string;
+ save(doNotCloseModal?:boolean): void;
+ saveAndAnother(): void;
+ close(): void;
+ getOptions(): Array<string>;
+ isDeploymentHeat(): boolean;
+ onFileChange(): void;
+ setDefaultTimeout(): void;
+ openEditEnvParametersModal(artifact:Models.ArtifactModel):void;
+ getFormTitle():string;
+ fileUploadRequired():string;
+ isArtifactOwner():boolean;
+ }
+
+ export class ArtifactResourceFormViewModel {
+
+ private artifactArr:Array<Models.ArtifactModel>;
+
+ static '$inject' = [
+ '$scope',
+ '$modalInstance',
+ 'artifact',
+ 'Sdc.Services.CacheService',
+ 'ValidationPattern',
+ 'UrlValidationPattern',
+ 'LabelValidationPattern',
+ 'IntegerValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ '$base64',
+ '$state',
+ 'ArtifactsUtils',
+ '$modal',
+ '$templateCache',
+ 'component'
+ ];
+
+ private formState:Utils.Constants.FormState;
+ private entityId:string;
+
+ constructor(private $scope:IArtifactResourceFormViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private artifact:Models.ArtifactModel,
+ private cacheService:Services.CacheService,
+ private ValidationPattern:RegExp,
+ private UrlValidationPattern:RegExp,
+ private LabelValidationPattern:RegExp,
+ private IntegerValidationPattern : RegExp,
+ private CommentValidationPattern: RegExp,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private $base64:any,
+ private $state:any,
+ private artifactsUtils:Sdc.Utils.ArtifactsUtils,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private component:Models.Components.Component) {
+
+
+ this.entityId = this.component.uniqueId;
+ this.artifactArr = [];
+ this.formState = angular.isDefined(artifact.artifactLabel) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE;
+ this.initScope();
+ }
+
+ private initEntity = ():void => {
+ this.$scope.editArtifactResourceModel.artifactResource = this.artifact;
+ this.$scope.originalArtifactName = this.artifact.artifactName;
+ };
+
+
+ private initFooterButtons = ():void =>{
+
+ this.$scope.footerButtons = [
+ {'name': 'Done', 'css': 'blue', 'callback': this.$scope.save}
+ ];
+ if (this.$scope.isNew){
+ this.$scope.footerButtons.push( {'name': 'Add Another', 'css': 'grey', 'disabled': !this.$scope.isNew && 'deployment' === this.$scope.artifactType, 'callback': this.$scope.saveAndAnother});
+ }
+
+ };
+
+
+ private initArtifactTypes = ():void => {
+
+ let artifactTypes:any = this.cacheService.get('UIConfiguration');
+
+ if('deployment' === this.$scope.artifactType) {
+
+ this.$scope.validExtensions = ('HEAT_ENV' == this.artifact.artifactType||this.component.selectedInstance)?//to remove the first condition?
+ artifactTypes.artifacts.deployment.resourceInstanceDeploymentArtifacts
+ : this.component.isResource() ? artifactTypes.artifacts.deployment.resourceDeploymentArtifacts
+ : artifactTypes.artifacts.deployment.serviceDeploymentArtifacts;
+
+ if(this.$scope.validExtensions) {
+ this.$scope.editArtifactResourceModel.artifactTypes = Object.keys(this.$scope.validExtensions);
+ }
+ this.$scope.defaultHeatTimeout = artifactTypes.defaultHeatTimeout;
+ if(this.$scope.isNew) {
+ let isHeat='HEAT_ENV' == this.artifact.artifactType;
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> {
+ return 'HEAT' == item.substring(0,4)||(!isHeat && item == "VF_MODULES_METADATA") ||
+ _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, item);
+ });
+ }
+
+ }if (this.$scope.artifactType === 'normal') {
+ this.$scope.editArtifactResourceModel.artifactTypes = artifactTypes.artifacts.other.map((element:any)=> {
+ return element.name;
+ });
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> {
+ return _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) ||
+ _.has(Utils.Constants.ArtifactType.TOSCA, item);
+ })
+ }
+
+ if(this.component.isResource() && (<Resource>this.component).isCsarComponent()) {
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string) => {
+ return this.artifactsUtils.isLicenseType(item);
+ })
+ }
+
+ };
+
+ private initEditArtifactResourceModel = ():void => {
+ this.$scope.editArtifactResourceModel = {
+ artifactResource: null,
+ artifactTypes: null,
+ artifactFile:{}
+ };
+
+ this.initEntity();
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.urlValidationPattern = this.UrlValidationPattern;
+ this.$scope.labelValidationPattern = this.LabelValidationPattern;
+ this.$scope.integerValidationPattern = this.IntegerValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.isLoading = false;
+ this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE);
+ this.$scope.artifactType = this.artifactsUtils.getArtifactTypeByState(this.$state.current.name);
+ this.$scope.modalInstanceArtifact = this.$modalInstance;
+
+ this.initEditArtifactResourceModel();
+ this.initArtifactTypes();
+
+ // In case of edit, show the file name in browse.
+ if (this.artifact.artifactName!=="" && 'HEAT_ENV'!==this.artifact.artifactType){
+ this.$scope.editArtifactResourceModel.artifactFile = {};
+ this.$scope.editArtifactResourceModel.artifactFile.filename = this.artifact.artifactName;
+ }
+
+ //scope methods
+ this.$scope.isDeploymentHeat = ():boolean => {
+ return !this.$scope.isNew && this.$scope.artifactType === 'deployment' &&
+ 'HEAT' === this.$scope.editArtifactResourceModel.artifactResource.artifactType.substring(0,4);
+ };
+ this.$scope.onFileChange = ():void => {
+ if(this.$scope.editArtifactResourceModel.artifactFile && this.$scope.editArtifactResourceModel.artifactFile.filename) {
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename;
+ } else {
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.originalArtifactName;
+ }
+ };
+ this.$scope.setDefaultTimeout = ():void => {
+ if(this.$scope.isDeploymentHeat() && !this.$scope.editArtifactResourceModel.artifactResource.timeout) {
+ this.$scope.editArtifactResourceModel.artifactResource.timeout = this.$scope.defaultHeatTimeout;
+ }
+ };
+
+ this.$scope.fileExtensions = ():string => {
+ let type:string = this.$scope.editArtifactResourceModel.artifactResource.artifactType;
+ return type && this.$scope.validExtensions && this.$scope.validExtensions[type].acceptedTypes ?
+ this.$scope.validExtensions[type].acceptedTypes.join(',') : "";
+ };
+
+ this.$scope.save = (doNotCloseModal?:boolean):void => {
+ this.$scope.isLoading = true;
+ this.$scope.editArtifactResourceModel.artifactResource.description = this.ValidationUtils.stripAndSanitize(this.$scope.editArtifactResourceModel.artifactResource.description);
+
+ if (!this.$scope.isDeploymentHeat()) {
+ this.$scope.editArtifactResourceModel.artifactResource.timeout = null;
+ }
+
+ if (this.$scope.editArtifactResourceModel.artifactFile) {
+ this.$scope.editArtifactResourceModel.artifactResource.payloadData = this.$scope.editArtifactResourceModel.artifactFile.base64;
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename;
+ }
+
+ let onFaild = (response):void => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+ };
+
+ let onSuccess = (artifactResource:Models.ArtifactModel):void => {
+ this.$scope.isLoading = false;
+ this.$scope.originalArtifactName = "";
+
+ if(this.$scope.isDeploymentHeat()){
+ if(artifactResource.heatParameters) {
+ this.$scope.openEditEnvParametersModal(artifactResource);
+ }
+ }
+
+ if (!doNotCloseModal) {
+ this.$modalInstance.close();
+ //this.artifactArr = [];
+ } else {
+ this.$scope.editArtifactResourceModel.artifactFile = null;
+ angular.element("input[type='file']").val(null); // for support chrome when upload the same file
+ this.artifactsUtils.addAnotherAfterSave(this.$scope);
+ }
+
+ };
+
+ if('HEAT_ENV' == this.artifact.artifactType){
+ this.component.uploadInstanceEnvFile(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+ }else if(this.$scope.isArtifactOwner()){
+ this.component.addOrUpdateInstanceArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+ }else {
+ this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFaild);
+ }
+ };
+
+ this.$scope.isArtifactOwner = ():boolean=> {
+ return this.component.isService() && !!this.component.selectedInstance;
+ };
+
+ this.$scope.saveAndAnother = ():void => {
+ this.$scope.save(true);
+ };
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.close();
+ this.artifactArr = [];
+ };
+
+ this.$scope.fileUploadRequired = ():string => {
+ if (this.$scope.editArtifactResourceModel.artifactFile.filename){
+ // This is edit mode
+ return 'false';
+ } else {
+ return 'true';
+ }
+ };
+
+ this.$scope.getFormTitle =(): string =>{
+ if('HEAT_ENV' == this.artifact.artifactType){
+ return 'Update HEAT ENV';
+ }
+ if(this.$scope.isDeploymentHeat()){
+ if(!this.$scope.editArtifactResourceModel.artifactResource.artifactChecksum){
+ return 'Add HEAT Template';
+ }
+ return 'Update HEAT Template';
+ }
+ if(this.$scope.isNew){
+ return 'Add Artifact';
+ }
+ return 'Update Artifact';
+ };
+
+ this.$scope.openEditEnvParametersModal = (artifactResource:Models.ArtifactModel):void => {
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.html'),
+ controller: 'Sdc.ViewModels.EnvParametersFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ resolve: {
+ artifact: ():Models.ArtifactModel => {
+ return artifactResource;
+ },
+ component: ():Models.Components.Component => {
+ return this.component;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ modalInstance
+ .result
+ .then(():void => {
+ });
+ };
+
+ this.$scope.forms = {};
+
+ this.initFooterButtons();
+
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ if(this.$scope.isNew){
+ this.$scope.footerButtons[1].disabled = this.$scope.forms.editForm.$invalid;
+ }
+ });
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view.html b/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view.html
new file mode 100644
index 0000000000..74a19c8776
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form-view.html
@@ -0,0 +1,169 @@
+<sdc-modal modal="modalInstanceArtifact" type="classic" class="sdc-add-artifact" buttons="footerButtons" header="{{getFormTitle()}}" show-close-button="true" get-close-modal-response="close">
+
+ <loader data-display="isLoading"></loader>
+
+ <div class="sdc-edit-artifact-form-container"
+ data-ng-class="{'mandatory-artifact': (editArtifactResourceModel.artifactResource.mandatory && artifactType !=='deployment') || artifactType === 'api'}">
+ <form novalidate class="w-sdc-form" name="forms.editForm">
+
+ <!--------------------- ARTIFACT FILE START-------------------->
+ <div class="i-sdc-form-item">
+ <label class="required">Upload File</label>
+ <file-upload id="fileUploadElement"
+ form-element="forms.editForm"
+ element-required="{{::fileUploadRequired()}}"
+ element-name="myArtifactFile"
+ file-model="editArtifactResourceModel.artifactFile"
+ extensions="{{fileExtensions()}}"
+ element-disabled="{{!editArtifactResourceModel.artifactResource.artifactType}}"
+ data-ng-class="{'error': forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid}"></file-upload>
+
+ <div class="input-error-file-upload" data-ng-show="forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid">
+ <span ng-show="forms.editForm.myArtifactFile.$error.required && !forms.editForm.myArtifactFile.$error.emptyFile" translate="ADD_ARTIFACT_ERROR_FILE_REQUIRED"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.maxsize" translate="VALIDATION_ERROR_MAX_FILE_SIZE"></span>
+ <span ng-if="artifactType === 'deployment'" ng-show="forms.editForm.myArtifactFile.$error.filetype" translate="ADD_ARTIFACT_ERROR_VALID_EXTENSIONS" translate-values="{'extensions': '{{fileExtensions()}}' }"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ </div>
+ </div>
+ <!--------------------- ARTIFACT FILE END -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column" data-ng-if="artifactType === 'deployment' || (!editArtifactResourceModel.artifactResource.mandatory && artifactType !== 'api')">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid)}"
+ data-ng-if="!isDeploymentHeat()">
+ <label class="i-sdc-form-label required">Artifact Label</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactLabel"
+ type="text"
+ name="artifactLabel"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="labelValidationPattern"
+ maxlength="25"
+ data-ng-disabled="!isNew"
+ data-tests-id="artifact-label"
+ autofocus/>
+
+ <div class="input-error" data-ng-show="forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid">
+ <span ng-show="forms.editForm.artifactLabel.$error.required" translate="ADD_ARTIFACT_ERROR_LABEL_REQUIRED"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label required">Type</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="type"
+ data-ng-disabled="!isNew"
+ data-ng-change="setDefaultTimeout()"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactType"
+ data-ng-options="type as type for type in editArtifactResourceModel.artifactTypes track by type | uppercase"
+ data-tests-id="artifacttype">
+ <option value="">Choose Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="ADD_ARTIFACT_ERROR_TYPE_REQUIRED"></span>
+ </div>
+
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid)}" data-ng-if="isDeploymentHeat()">
+ <label class="i-sdc-form-label">Deployment Timeout (minutes)</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.timeout"
+ type="number"
+ name="timeout"
+ min="1"
+ max="2147483647"
+ data-ng-disabled="'HEAT_ENV'==editArtifactResourceModel.artifactResource.artifactType"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="integerValidationPattern"
+ data-ng-init="setDefaultTimeout()"
+ data-ng-change="setDefaultTimeout()"
+ maxlength="25"
+ data-tests-id="timeout" />
+
+ <div class="input-error" data-ng-show="forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid">
+ <span ng-show="forms.editForm.timeout.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.timeout.$error.pattern" translate="ADD_ARTIFACT_ERROR_TIMEOUT_PATTERN"></span>
+ <span ng-show="forms.editForm.timeout.$error.min" translate="ADD_ARTIFACT_ERROR_TIMEOUT_MIN"></span>
+ </div>
+
+ </div>
+
+ </div><!-- w-sdc-form-column -->
+
+ <div class="w-sdc-form-column i-sdc-form-url" data-ng-if="artifactType==='api'">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.apiUrl.$dirty && forms.editForm.apiUrl.$invalid)}">
+ <label class="i-sdc-form-label required">URL</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="100"
+ data-ng-model="editArtifactResourceModel.artifactResource.apiUrl"
+ data-ng-model-options="{ debounce: 200 }"
+ type="url"
+ name="apiUrl"
+ data-required
+ ng-pattern="urlValidationPattern"
+ maxlength="100"
+ autofocus
+ invalid-characters=',#?&@$<>~^`\[]{}|")(*!+=;%' />
+
+ <div class="input-error" data-ng-show="forms.editForm.apiUrl.$dirty && forms.editForm.apiUrl.$invalid">
+ <span ng-show="forms.editForm.apiUrl.$error.required" translate="ADD_ARTIFACT_ERROR_APIURL_REQUIRED"></span>
+ <span ng-show="forms.editForm.apiUrl.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="forms.editForm.apiUrl.$error.url || forms.editForm.apiUrl.$error.pattern || forms.editForm.apiUrl.$error.invalidCharacters" translate="ADD_ARTIFACT_ERROR_APIURL_URL"></span>
+ </div>
+
+ </div>
+
+ </div><!-- w-sdc-form-column -->
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-required
+ name="description"
+ data-ng-model="editArtifactResourceModel.artifactResource.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="commentValidationPattern"
+ data-tests-id="description"></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_PATTERN"></span>
+ </div>
+
+ <div class="w-sdc-form-column artifact-info" data-ng-show="!isNew && editArtifactResourceModel.artifactResource.esId">
+ UUID <span data-ng-bind="editArtifactResourceModel.artifactResource.artifactUUID"></span>
+ <br />
+ Version <span data-ng-bind="editArtifactResourceModel.artifactResource.artifactVersion"></span>
+ </div>
+ </div>
+
+ </div><!-- w-sdc-form-column -->
+
+ </div><!-- w-sdc-form-columns-wrapper -->
+
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+
+ </form>
+ </div>
+</sdc-modal>
+
diff --git a/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form.less b/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form.less
new file mode 100644
index 0000000000..1f77958c88
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/artifact-form/artifact-form.less
@@ -0,0 +1,44 @@
+.sdc-edit-artifact-form-container {
+
+ .w-sdc-form-note {
+ .h_9;
+ display: block;
+ position: relative;
+ top: 13px;
+ }
+
+ .i-sdc-form-textarea{
+ min-height: 95px;
+ }
+
+ .i-sdc-form-url {
+ padding-bottom: 0px;
+ }
+
+ &.mandatory-artifact {
+ .w-sdc-form-column {
+ width: 100%;
+ padding: 0;
+ min-height: initial;
+ }
+ }
+ .w-sdc-form .i-sdc-form-item.upload input[type="file"] {
+ display: none
+ }
+
+ .artifact-info {
+ text-align: left;
+ color: rgb(140, 140, 140);
+ font-size: 13px;
+ margin-top: -10px;
+ margin-bottom: 5px;
+ width: 100%;
+ min-height: initial;
+
+ span {
+ color: #666666;
+ padding-left: 4px;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-form-view.html b/catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-form-view.html
new file mode 100644
index 0000000000..432b32fbd3
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-form-view.html
@@ -0,0 +1,153 @@
+<sdc-modal modal="modalInstanceAttribute" type="classic" class="sdc-edit-attribute-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Attribute" show-close-button="true">
+
+ <div class="sdc-edit-attribute-form-container" >
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!-- Name -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.attributeName.$dirty && forms.editForm.attributeName.$invalid)}">
+ <label class="i-sdc-form-label required">Name</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="attributeName"
+ data-ng-maxlength="50"
+ data-ng-disabled="!isNew"
+ maxlength="50"
+ data-ng-model="editAttributeModel.attribute.name"
+ type="text"
+ name="attributeName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="validateName()"
+ autofocus />
+ <div class="input-error" data-ng-show="forms.editForm.attributeName.$dirty && forms.editForm.attributeName.$invalid">
+ <span ng-show="forms.editForm.attributeName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Attribute name' }"></span>
+ <span ng-show="forms.editForm.attributeName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="forms.editForm.attributeName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.attributeName.$error.nameExist" translate="NEW_ATTRIBUTE_ERROR_NAME_EXISTS"></span>
+ </div>
+ </div>
+
+ <!-- Description -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ data-ng-disabled="editAttributeModel.attribute.readonly"
+ maxlength="256"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="editAttributeModel.attribute.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"></textarea>
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+
+
+ </div>
+
+ <div class="w-sdc-form-column">
+ <!-- Type -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label required">Type</label>
+ <select class="i-sdc-form-select"
+ data-tests-id="type-field"
+ data-required
+ data-ng-disabled="editAttributeModel.attribute.readonly"
+ name="type"
+ data-ng-change="onTypeChange()"
+ data-ng-model="editAttributeModel.attribute.type"
+ data-ng-options="type for type in editAttributeModel.types">
+ <option value="">Choose Type</option>
+ </select>
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+
+ <!-- schema -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schema.$dirty && forms.editForm.schema.$invalid)}"
+ data-ng-if="showSchema()">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select" ng-if="isSchemaEditable()"
+ data-required
+ name="schema"
+ data-ng-disabled="editAttributeModel.attribute.readonly"
+ data-ng-change="onTypeChange(false)"
+ data-ng-model="editAttributeModel.attribute.schema.property.type"
+ data-ng-options="type for type in editAttributeModel.simpleTypes">
+ <option value="">Choose Schema Type</option>
+ </select>
+ <input class="i-sdc-form-input"
+ ng-if="!isSchemaEditable()"
+ data-tests-id="schema"
+ data-ng-disabled="true"
+ data-ng-model="editAttributeModel.attribute.schema.property.type"
+ type="text"
+ name="schema"/>
+ <div class="input-error" data-ng-show="forms.editForm.schema.$dirty && forms.editForm.schema.$invalid">
+ <span ng-show="forms.editForm.schema.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+
+ <!-- Default value -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <label class="i-sdc-form-label">Default Value</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="!(editAttributeModel.attribute.type == 'boolean')"
+ data-ng-maxlength="100"
+ data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()"
+ maxlength="100"
+ data-ng-model="editAttributeModel.attribute[isAttributeValueOwner()?'value':'defaultValue']"
+ type="text"
+ name="value"
+ data-custom-validation="" data-validation-func="validateUniqueKeys"
+ data-tests-id="defaultvalue"
+ data-ng-pattern="validationPattern"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="!forms.editForm.value.$error.pattern && ('integer'==editAttributeModel.attribute.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editAttributeModel.attribute.value)) || onValueChange())"
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="editAttributeModel.attribute.type == 'boolean'"
+ data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="editAttributeModel.attribute[isAttributeValueOwner()?'value':'defaultValue']">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Default value' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="{{getValidationTranslate()}}"></span>
+ <span ng-show="forms.editForm.value.$error.customValidation" translate="ATTRIBUTE_EDIT_MAP_UNIQUE_KEYS"></span>
+ </div>
+ </div>
+
+ <!-- hidden -->
+ <div class="i-sdc-form-item" data-ng-if="isAttributeValueOwner()">
+ <label class="i-sdc-form-label">Hidden</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="hidden"
+ data-ng-disabled="editAttributeModel.attribute.readonly && !isAttributeValueOwner()"
+ data-ng-model="editAttributeModel.attribute.hidden"
+ type="checkbox"
+ name="hidden"/>
+ </div>
+ </div>
+
+ </div>
+
+ </form>
+ </div>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-from-view-model.ts b/catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-from-view-model.ts
new file mode 100644
index 0000000000..d369cfa5d1
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/attribute-form/attribute-from-view-model.ts
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IEditAttributeModel {
+ attribute: Models.AttributeModel;
+ types: Array<string>;
+ simpleTypes: Array<string>;
+ }
+
+ interface IAttributeFormViewModelScope extends ng.IScope {
+ $$childTail: any;
+ forms:any;
+ editForm:ng.IFormController;
+ footerButtons: Array<any>;
+ isService: boolean;
+ editAttributeModel: IEditAttributeModel;
+ modalInstanceAttribute:ng.ui.bootstrap.IModalServiceInstance;
+ isNew: boolean;
+ listRegex: Sdc.Utils.IMapRegex;
+ mapRegex: Sdc.Utils.IMapRegex;
+ propertyNameValidationPattern: RegExp;
+ commentValidationPattern: RegExp;
+ isLoading: boolean;
+ validationPattern: RegExp;
+
+ save():void;
+ close(): void;
+ onTypeChange():void;
+ onValueChange(): void;
+ isAttributeValueOwner():boolean;
+ validateIntRange(value:string):boolean;
+ validateUniqueKeys(viewValue:string):boolean;
+ getValidationTranslate(): string;
+ showSchema(): boolean;
+ isSchemaEditable(): boolean;
+ validateName():void;
+ }
+
+ export class AttributeFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$modalInstance',
+ 'attribute',
+ 'ValidationUtils',
+ 'CommentValidationPattern',
+ 'PropertyNameValidationPattern',
+ 'component'
+ ];
+
+ private formState: Sdc.Utils.Constants.FormState;
+
+
+ constructor(private $scope:IAttributeFormViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private attribute: Models.AttributeModel,
+ private ValidationUtils:Sdc.Utils.ValidationUtils,
+ private CommentValidationPattern:RegExp,
+ private PropertyNameValidationPattern: RegExp,
+ private component: Models.Components.Component) {
+ this.formState = angular.isDefined(attribute.name) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE;
+ this.initScope();
+ }
+
+ private initResource = ():void => {
+ this.$scope.editAttributeModel.attribute = new Sdc.Models.AttributeModel(this.attribute);
+ if (this.$scope.editAttributeModel.types.indexOf(this.attribute.type) === -1) {//attribute defaulte type is string too?
+ this.attribute.type = "string";
+ }
+ };
+
+ private initEditAttributeModel = ():void => {
+ this.$scope.editAttributeModel = {
+ attribute: null,
+ types: ['integer', 'string', 'float', 'boolean', 'list', 'map'],
+ simpleTypes: ['integer', 'string', 'float', 'boolean']
+ };
+
+ this.initResource();
+ };
+
+ private initScope = ():void => {
+
+ //scope attributes
+ this.$scope.forms = {};
+ this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+
+ this.$scope.modalInstanceAttribute = this.$modalInstance;
+ this.$scope.listRegex = this.ValidationUtils.getPropertyListPatterns();
+ this.$scope.mapRegex = this.ValidationUtils.getPropertyMapPatterns();
+
+ this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE);
+ this.$scope.isLoading = false;
+
+ this.initEditAttributeModel();
+ this.setValidationPattern();
+
+ //scope methods
+ this.$scope.save = ():void => {
+ if(!this.$scope.forms.editForm.$invalid){
+ let attribute:Models.AttributeModel = this.$scope.editAttributeModel.attribute;
+ this.$scope.editAttributeModel.attribute.description = this.ValidationUtils.stripAndSanitize(this.$scope.editAttributeModel.attribute.description);
+ ////if read only - just closes the modal
+ if (this.$scope.editAttributeModel.attribute.readonly && !this.$scope.isAttributeValueOwner()) {
+ this.$modalInstance.close();
+ return;
+ }
+ this.$scope.isLoading = true;
+ let onAttributeFaild = (response):void => {
+ console.info('onFaild', response);
+ this.$scope.isLoading = false;
+ };
+
+ let onAttributeSuccess = (attributeFromBE:Models.AttributeModel):void => {
+ console.info('onAttributeResourceSuccess : ', attributeFromBE);
+ this.$scope.isLoading = false;
+ this.$modalInstance.close();
+ };
+
+ //in case we have uniqueId we call update method
+ if (this.$scope.isAttributeValueOwner()) {
+ this.component.updateInstanceAttribute(attribute).then(onAttributeSuccess, onAttributeFaild);
+ } else {
+ this.component.addOrUpdateAttribute(attribute).then(onAttributeSuccess, onAttributeFaild);
+ }
+ }
+ };
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.close();
+ };
+
+ this.$scope.validateName = ():void => {
+ let existsAttr: Models.AttributeModel = _.find(this.component.attributes, (attribute:Models.AttributeModel) => {
+ return attribute.name === this.$scope.editAttributeModel.attribute.name;
+ });
+ if(existsAttr){
+ this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', false);
+ }else{
+ this.$scope.forms.editForm["attributeName"].$setValidity('nameExist', true);
+ }
+
+ };
+
+ this.$scope.onTypeChange = ():void => {
+ this.$scope.editAttributeModel.attribute.value = '';
+ this.$scope.editAttributeModel.attribute.defaultValue = '';
+ this.setValidationPattern();
+ };
+
+ this.$scope.isAttributeValueOwner = ():boolean=> {
+ return this.component.isService() || !!this.component.selectedInstance;
+ };
+
+ this.$scope.onValueChange = ():void => {
+ if (!this.$scope.editAttributeModel.attribute.value) {
+ if (this.$scope.isAttributeValueOwner()) {
+ this.$scope.editAttributeModel.attribute.value = this.$scope.editAttributeModel.attribute.defaultValue;
+ }
+ }
+ };
+
+
+ this.$scope.validateUniqueKeys = (viewValue:string) : boolean => {
+ if(this.$scope.editAttributeModel.attribute.type === 'map') {
+ return this.ValidationUtils.validateUniqueKeys(viewValue);
+ }
+ else {
+ return true; //always valid if not a map
+ }
+ };
+
+ this.$scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ this.$scope.isSchemaEditable = () :boolean => {
+ let schemaType=this.$scope.editAttributeModel.attribute.schema.property.type;
+ return this.$scope.editAttributeModel.simpleTypes.indexOf(schemaType) > -1||!schemaType;
+ };
+
+ this.$scope.showSchema = () :boolean => {
+ return ['list', 'map'].indexOf(this.$scope.editAttributeModel.attribute.type) > -1;
+ };
+
+ this.$scope.getValidationTranslate = () : string => {
+ let result = "ATTRIBUTE_EDIT_PATTERN";
+ if (this.$scope.showSchema()) {
+
+ result = "ATTRIBUTE_EDIT_" + this.$scope.editAttributeModel.attribute.type.toUpperCase();
+
+ if(this.$scope.editAttributeModel.attribute.schema.property.type === Utils.Constants.PROPERTY_TYPES.STRING) {
+ result += "_STRING";
+ }else if(this.$scope.editAttributeModel.attribute.schema.property.type === Utils.Constants.PROPERTY_TYPES.BOOLEAN) {
+ result += "_BOOLEAN";
+ } else {
+ result += "_GENERIC";
+ }
+ }
+
+ return result;
+ };
+
+ // Add the done button at the footer.
+ this.$scope.footerButtons = [
+ {'name': 'Done', 'css':'blue', 'callback': this.$scope.save},
+ {'name':'Cancel', 'css':'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watchCollection("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ }
+
+
+ private setValidationPattern = ():void => {
+
+ if(this.$scope.editAttributeModel.attribute.type === 'list') {
+ this.$scope.validationPattern = this.$scope.listRegex[this.$scope.editAttributeModel.attribute.schema.property.type];
+ }
+ else if(this.$scope.editAttributeModel.attribute.type === 'map') {
+ this.$scope.validationPattern = this.$scope.mapRegex[this.$scope.editAttributeModel.attribute.schema.property.type];
+ }
+ else{
+ this.$scope.validationPattern = this.ValidationUtils.getValidationPattern(this.$scope.editAttributeModel.attribute.type);
+ }
+
+ };
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.html b/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.html
new file mode 100644
index 0000000000..69367dc68c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.html
@@ -0,0 +1,39 @@
+<sdc-modal modal="envParametersModal" type="classic" class="sdc-env-form-container" buttons="saveButton" header="{{isInstance()?'HEAT Template':'Update Parameters'}}" show-close-button="true">
+ <div class="sdc-env-form-container">
+ <div class="w-sdc-modal-body">
+ <span class="w-sdc-modal-body-content" data-ng-if="!isInstance()" translate="UPDATE_PARAMETERS_TEXT"></span>
+ <form novalidate class="" name="editForm">
+ <perfect-scrollbar class="perfect-scrollbar w-sdc-form w-sdc-env-form-container">
+ <div class="i-sdc-form-item" data-ng-repeat="parameter in heatParameters track by $index">
+ <div class="left-column-container">
+ <ng-form name="editForm">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}"
+ data-ng-bind="parameter.name +' (' + parameter.type + ')'" tooltips tooltip-content="{{parameter.name +' (' + parameter.type + ')'}}"></label>
+ <input class="i-sdc-form-input" data-ng-class="{error:(editForm.currentValue.$invalid)}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && editForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="(editForm.currentValue.$error.required && (parameter.currentValue=parameter.defaultValue))"
+ />
+
+ <div class="input-error" data-ng-show="editForm.currentValue.$invalid">
+ <span ng-show="editForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="editForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="editForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </ng-form>
+ </div>
+ <div class="i-sdc-env-form-label-description">
+ <label class="i-sdc-env-form-label" data-ng-bind="parameter.description" tooltips tooltip-content="{{parameter.description}}"></label>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ <div class="env-file-generation-label" data-ng-if="isInstance()" translate="ENV_FILE_GENERATION"></div>
+ </form>
+ </div>
+ </div>
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.less b/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.less
new file mode 100644
index 0000000000..c58c94ab22
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.less
@@ -0,0 +1,74 @@
+
+.sdc-env-form-container{
+ .w-sdc-modal-body{
+ padding: 20px 10px 2px 10px;
+ }
+ .w-sdc-modal-body-content{
+ .b_6;
+ display: block;
+ }
+
+ .env-file-generation-label{
+ .p_9;
+ .bold;
+ margin-bottom: 20px;
+ }
+}
+
+.w-sdc-env-form-container {
+ border-top: 1px solid #cdcdcd;
+ border-bottom: 1px solid #cdcdcd;
+ height: 356px;
+ margin: 35px 0 10px 0;
+
+ .w-sdc-form {
+ text-align: left;
+ }
+ .i-sdc-form-item{
+ display: inline-block;
+ .description{
+ margin-bottom: 2px;
+ vertical-align: baseline;
+ padding: 32px 0 0 0;
+ text-transform: capitalize;
+ .b_1;
+ }
+ }
+ .left-column-container{
+ width: 250px;
+ float: left;
+ .i-sdc-env-form-label {
+ overflow: hidden;
+ max-width: 100%;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+
+ &.required::before {
+ color: #f33;
+ content: '*';
+ margin-right: 4px;
+ }
+ }
+ }
+ .i-sdc-env-form-label-description {
+ float: right;
+
+ .i-sdc-env-form-label {
+ .p_9;
+ // height: 20px;
+ margin: 30px 0px 0px 30px;
+ overflow: hidden;
+ max-width: 245px;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+
+ &.required::before {
+ color: #f33;
+ content: '*';
+ margin-right: 4px;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.ts b/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.ts
new file mode 100644
index 0000000000..d1bae440cc
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.ts
@@ -0,0 +1,149 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IEnvParametersFormViewModelScope extends ng.IScope {
+ isLoading: boolean;
+ type:string;
+ heatParameters:any;
+ editForm:ng.IFormController;
+ artifactResource:Models.ArtifactModel;
+ saveButton: Array<any>;
+ envParametersModal: ng.ui.bootstrap.IModalServiceInstance;
+
+ getValidationPattern(type:string):RegExp;
+ isInstance():boolean;
+ validateJson(json:string):boolean;
+ close(): void;
+ save():void;
+ }
+
+ export class EnvParametersFormViewModel {
+
+
+ static '$inject' = [
+ '$scope',
+ '$state',
+ '$modalInstance',
+ 'artifact',
+ // 'ArtifactsUtils',
+ 'ValidationUtils',
+ 'component'
+ ];
+
+
+ constructor(private $scope:IEnvParametersFormViewModelScope,
+ private $state:any,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private artifact:Models.ArtifactModel,
+ // private artifactsUtils:Sdc.Utils.ArtifactsUtils,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private component:Models.Components.Component) {
+
+
+ this.initScope();
+ }
+
+ private updateInstanceHeat = ():void => {
+ let success =(responseArtifact:Models.ArtifactModel): void => {
+ this.$scope.isLoading = false;
+ this.$modalInstance.close();
+ };
+
+ let error = ():void => {
+ this.$scope.isLoading = false;
+ console.info('Failed to load save artifact');
+ };
+
+ this.component.addOrUpdateInstanceArtifact(this.$scope.artifactResource).then(success, error);
+
+ };
+
+ private initScope = ():void => {
+ this.$scope.envParametersModal = this.$modalInstance;
+ this.$scope.artifactResource= this.artifact;
+ this.$scope.heatParameters = angular.copy(this.artifact.heatParameters);
+
+ this.$scope.getValidationPattern = (validationType:string , parameterType?:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(validationType, parameterType);
+ };
+
+ this.$scope.validateJson = (json:string):boolean => {
+ if(!json){
+ return true;
+ }
+ return this.ValidationUtils.validateJson(json);
+ };
+
+ this.$scope.isInstance =(): boolean =>{
+ return !!this.component.selectedInstance;
+ };
+
+
+ this.$scope.save = ():void => {
+ this.$scope.isLoading = true;
+ this.artifact.heatParameters = this.$scope.heatParameters;
+ this.artifact.heatParameters.forEach((parameter:any):void => {
+ /* if ("" === parameter.currentValue) {
+ parameter.currentValue = null;
+ }else */
+ if(!parameter.currentValue && parameter.defaultValue) {
+ parameter.currentValue = parameter.defaultValue;
+ }
+ });
+
+ if(this.$scope.isInstance()){
+ this.updateInstanceHeat();
+ return;
+ }
+
+ let success =(responseArtifact:Models.ArtifactModel): void => {
+ this.$scope.isLoading = false;
+ this.$modalInstance.close();
+
+ };
+
+ let error = ():void => {
+ this.$scope.isLoading = false;
+ console.info('Failed to load save artifact');
+ };
+
+ this.component.addOrUpdateArtifact(this.$scope.artifactResource).then(success, error);
+ };
+
+ this.$scope.saveButton = [
+ {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save}
+ ];
+
+ this.$scope.close = ():void => {
+ //this.artifact.heatParameters.forEach((parameter:any):void => {
+ // if (!parameter.currentValue && parameter.defaultValue) {
+ // parameter.currentValue = parameter.defaultValue;
+ // }
+ //});
+ this.$modalInstance.dismiss();
+ };
+
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/property-form/property-form-view-model.ts b/catalog-ui/app/scripts/view-models/forms/property-form/property-form-view-model.ts
new file mode 100644
index 0000000000..c9732aa9a6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/property-form/property-form-view-model.ts
@@ -0,0 +1,330 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IEditPropertyModel {
+ property: Models.PropertyModel;
+ types: Array<string>;
+ simpleTypes: Array<string>;
+ sources: Array<string>;
+ }
+
+ interface IPropertyFormViewModelScope extends ng.IScope {
+ forms:any;
+ editForm:ng.IFormController;
+ footerButtons: Array<any>;
+ isNew: boolean;
+ isLoading: boolean;
+ isService: boolean;
+ validationPattern: RegExp;
+ propertyNameValidationPattern: RegExp;
+ commentValidationPattern: RegExp;
+ editPropertyModel: IEditPropertyModel;
+ modalInstanceProperty:ng.ui.bootstrap.IModalServiceInstance;
+ currentPropertyIndex:number;
+ isLastProperty:boolean;
+ myValue:any;
+ nonPrimitiveTypes:Array<string>;
+ dataTypes:Models.DataTypesMap;
+ isTypeDataType:boolean;
+ maxLength:number;
+
+ save(doNotCloseModal?:boolean): void;
+ getValidationPattern(type:string): RegExp;
+ validateIntRange(value:string):boolean;
+ close(): void;
+ onValueChange(): void;
+ onSchemaTypeChange():void;
+ onTypeChange(resetSchema:boolean): void;
+ isPropertyValueOwner():boolean;
+ showSchema(): boolean;
+ delete(property:Models.PropertyModel): void;
+ getPrev(): void;
+ getNext(): void;
+ isSimpleType(typeName:string):boolean;
+ getDefaultValue():any;
+ }
+
+ export class PropertyFormViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.DataTypesService',
+ '$modalInstance',
+ 'property',
+ 'ValidationPattern',
+ 'PropertyNameValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ 'component',
+ '$filter',
+ 'ModalsHandler',
+ 'filteredProperties',
+ '$timeout'
+ ];
+
+ private formState: Sdc.Utils.Constants.FormState;
+
+ constructor(private $scope:IPropertyFormViewModelScope,
+ private DataTypesService:Sdc.Services.DataTypesService,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private property: Models.PropertyModel,
+ private ValidationPattern:RegExp,
+ private PropertyNameValidationPattern: RegExp,
+ private CommentValidationPattern:RegExp,
+ private ValidationUtils:Sdc.Utils.ValidationUtils,
+ private component: Models.Components.Component,
+ private $filter:ng.IFilterService,
+ private ModalsHandler:Utils.ModalsHandler,
+ private filteredProperties: Array<Models.PropertyModel>,
+ private $timeout: ng.ITimeoutService) {
+
+ this.formState = angular.isDefined(property.name) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE;
+ this.initScope();
+ }
+
+ private initResource = ():void => {
+ this.$scope.editPropertyModel.property = new Sdc.Models.PropertyModel(this.property);
+ this.$scope.editPropertyModel.property.type = this.property.type? this.property.type: null;
+ this.setMaxLength();
+ // if (this.$scope.editPropertyModel.types.indexOf(this.property.type) === -1 && !this.$scope.isNew) {
+ // this.property.type = "string";
+ // }
+ };
+
+ private initEditPropertyModel = ():void => {
+ this.$scope.editPropertyModel = {
+ property: null,
+ types: Utils.Constants.PROPERTY_DATA.TYPES,
+ simpleTypes: Utils.Constants.PROPERTY_DATA.SIMPLE_TYPES,
+ sources: Utils.Constants.PROPERTY_DATA.SOURCES
+ };
+
+ this.initResource();
+ };
+
+ private initForNotSimpleType = ():void => {
+ let property = this.$scope.editPropertyModel.property;
+ this.$scope.isTypeDataType=this.DataTypesService.isDataTypeForPropertyType(this.$scope.editPropertyModel.property,this.$scope.dataTypes);
+ if(property.type && this.$scope.editPropertyModel.simpleTypes.indexOf(property.type)==-1){
+ if(!(property.value||property.defaultValue)) {
+ switch (property.type) {
+ case Utils.Constants.PROPERTY_TYPES.MAP:
+ this.$scope.myValue = {'':null};
+ break;
+ case Utils.Constants.PROPERTY_TYPES.LIST:
+ this.$scope.myValue = [];
+ break;
+ default:
+ this.$scope.myValue = {};
+ }
+ }else{
+ this.$scope.myValue = JSON.parse(property.value||property.defaultValue);
+ }
+ }
+ };
+
+ private setMaxLength = ():void => {
+ switch (this.$scope.editPropertyModel.property.type) {
+ case Utils.Constants.PROPERTY_TYPES.MAP:
+ case Utils.Constants.PROPERTY_TYPES.LIST:
+ this.$scope.maxLength = this.$scope.editPropertyModel.property.schema.property.type == Utils.Constants.PROPERTY_TYPES.JSON?
+ Utils.Constants.PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH:
+ Utils.Constants.PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
+ break;
+ case Utils.Constants.PROPERTY_TYPES.JSON:
+ this.$scope.maxLength = Utils.Constants.PROPERTY_VALUE_CONSTRAINTS.JSON_MAX_LENGTH;
+ break;
+ default:
+ this.$scope.maxLength = Utils.Constants.PROPERTY_VALUE_CONSTRAINTS.MAX_LENGTH;
+ }
+ };
+
+
+ private initScope = ():void => {
+
+ //scope properties
+ this.$scope.forms = {};
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.isLoading = false;
+ this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE);
+ this.$scope.isService = this.component.isService();
+ this.$scope.modalInstanceProperty = this.$modalInstance;
+ this.$scope.currentPropertyIndex = _.findIndex(this.filteredProperties, i=> i.name == this.property.name );
+ this.$scope.isLastProperty= this.$scope.currentPropertyIndex==(this.filteredProperties.length-1);
+
+ this.initEditPropertyModel();
+
+ this.DataTypesService.getAllDataTypes().then((response:any) => {
+ this.$scope.dataTypes = response;
+ delete response['tosca.datatypes.Root'];
+ this.$scope.nonPrimitiveTypes =_.filter(Object.keys(response),(type:string)=>{
+ return this.$scope.editPropertyModel.types.indexOf(type)==-1;
+ });
+ this.initForNotSimpleType();
+ }, (err)=> {});
+
+
+
+
+
+ //scope methods
+ this.$scope.save = (doNotCloseModal?:boolean):void => {
+ let property:Models.PropertyModel = this.$scope.editPropertyModel.property;
+ this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description);
+ ////if read only - just closes the modal
+ if (this.$scope.editPropertyModel.property.readonly && !this.$scope.isPropertyValueOwner()) {
+ this.$modalInstance.close();
+ return;
+ }
+
+ this.$scope.isLoading = true;
+
+ let onPropertyFaild = (response):void => {
+ console.info('onFaild', response);
+ this.$scope.isLoading = false;
+ };
+
+ let onPropertySuccess = (propertyFromBE:Models.PropertyModel):void => {
+ console.info('onPropertyResourceSuccess : ', propertyFromBE);
+ this.$scope.isLoading = false;
+
+ if (!doNotCloseModal) {
+ this.$modalInstance.close();
+ } else {
+ this.$scope.forms.editForm.$setPristine();
+ this.$scope.editPropertyModel.property = new Models.PropertyModel();
+ }
+ };
+
+ //in case we have uniqueId we call update method
+ if (this.$scope.isPropertyValueOwner()) {
+ if(!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)){
+ let myValueString:string = JSON.stringify(this.$scope.myValue);
+ property.value = myValueString;
+ }
+ this.component.updateInstanceProperty(property).then(onPropertySuccess, onPropertyFaild);
+ } else {
+ if(!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)){
+ let myValueString:string = JSON.stringify(this.$scope.myValue);
+ property.defaultValue = myValueString;
+ }else{
+ this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.value;
+ }
+ this.component.addOrUpdateProperty(property).then(onPropertySuccess, onPropertyFaild);
+ }
+ };
+
+
+ this.$scope.isPropertyValueOwner = ():boolean=> {
+ return this.component.isService() || !!this.component.selectedInstance;
+ };
+
+ this.$scope.getPrev = ():void=> {
+ this.property = this.filteredProperties[--this.$scope.currentPropertyIndex];
+ this.initResource();
+ this.initForNotSimpleType();
+ this.$scope.isLastProperty=false;
+ };
+
+ this.$scope.getNext = ():void=> {
+ this.property = this.filteredProperties[++this.$scope.currentPropertyIndex];
+ this.initResource();
+ this.initForNotSimpleType();
+ this.$scope.isLastProperty= this.$scope.currentPropertyIndex==(this.filteredProperties.length-1);
+ };
+
+ this.$scope.isSimpleType = (typeName:string):boolean=>{
+ return typeName && this.$scope.editPropertyModel.simpleTypes.indexOf(typeName)!=-1;
+ };
+
+ this.$scope.showSchema = () :boolean => {
+ return [Utils.Constants.PROPERTY_TYPES.LIST, Utils.Constants.PROPERTY_TYPES.MAP].indexOf(this.$scope.editPropertyModel.property.type) > -1;
+ };
+
+ this.$scope.getValidationPattern = (type:string):RegExp => {
+ return this.ValidationUtils.getValidationPattern(type);
+ };
+
+ this.$scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.close();
+ };
+
+ // put default value when instance value is empty
+ this.$scope.onValueChange = ():void => {
+ if (!this.$scope.editPropertyModel.property.value) {
+ if (this.$scope.isPropertyValueOwner()) {
+ this.$scope.editPropertyModel.property.value = this.$scope.editPropertyModel.property.defaultValue;
+ }
+ }
+ };
+
+ // Add the done button at the footer.
+ this.$scope.footerButtons = [
+ {'name': 'Save', 'css': 'blue', 'callback': this.$scope.save },
+ {'name': 'Cancel', 'css': 'grey', 'callback':this.$scope.close }
+ ];
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ this.$scope.getDefaultValue = ():any => {
+ return this.$scope.isPropertyValueOwner() ? this.$scope.editPropertyModel.property.defaultValue : null;
+ };
+
+ this.$scope.onTypeChange = ():void => {
+ this.$scope.editPropertyModel.property.value = '';
+ this.$scope.editPropertyModel.property.defaultValue = '';
+ this.setMaxLength();
+ this.initForNotSimpleType();
+ };
+
+ this.$scope.onSchemaTypeChange = ():void => {
+ if(this.$scope.editPropertyModel.property.type==Utils.Constants.PROPERTY_TYPES.MAP){
+ this.$scope.myValue={'':null};
+ }else if(this.$scope.editPropertyModel.property.type==Utils.Constants.PROPERTY_TYPES.LIST){
+ this.$scope.myValue=[];
+ }
+ this.setMaxLength();
+ };
+
+ this.$scope.delete = (property:Models.PropertyModel):void => {
+ let onOk = ():void => {
+ this.component.deleteProperty(property.uniqueId).then(
+ this.$scope.close
+ );
+ };
+ let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/property-form/property-form-view.html b/catalog-ui/app/scripts/view-models/forms/property-form/property-form-view.html
new file mode 100644
index 0000000000..d593d47a77
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/property-form/property-form-view.html
@@ -0,0 +1,219 @@
+<sdc-modal modal="modalInstanceProperty" type="classic" class="sdc-edit-property-container" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true" data-tests-id="sdc-edit-property-container">
+ <div class="sdc-modal-top-bar" data-ng-if="!isNew">
+ <div class="sdc-modal-top-bar-buttons">
+ <span ng-click="delete(editPropertyModel.property)" data-ng-class="{'disabled' : isPropertyValueOwner()||editPropertyModel.property.readonly}" class="sprite-new delete-btn" data-tests-id="delete_property" sdc-smart-tooltip="">Delete</span>
+ <span class="delimiter"></span>
+ <span data-ng-click="getPrev()" data-ng-class="{'disabled' : !currentPropertyIndex }" class="sprite-new left-arrow" data-tests-id="get-prev" sdc-smart-tooltip="">Previous</span>
+ <span data-ng-click="getNext()" data-ng-class="{'disabled' : isLastProperty }" class="sprite-new right-arrow" data-tests-id="get-next" sdc-smart-tooltip="">Next</span>
+ </div>
+ </div>
+
+ <div class="sdc-edit-property-form-container" >
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!-- Name -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Name</label>
+ <input class="i-sdc-form-input"
+ data-tests-id="propertyName"
+ data-ng-maxlength="50"
+ data-ng-disabled="!isNew || editPropertyModel.property.readonly"
+ maxlength="50"
+ data-ng-model="editPropertyModel.property.name"
+ type="text"
+ name="propertyName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ autofocus />
+
+ <div class="input-error" data-ng-show="forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid">
+ <span ng-show="forms.editForm.propertyName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property name' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!-- Input source -->
+ <!--div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.source.$dirty && forms.editForm.source.$invalid)}">
+ <label class="i-sdc-form-label required">Input Source</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="source"
+ data-ng-model="editPropertyModel.property.source"
+ data-ng-options="source for source in editPropertyModel.sources">
+ <option value="" >Choose Input Source</option>
+ </select>
+ <sdc-error-tooltip data-ng-show="forms.editForm.source.$dirty && forms.editForm.source.$invalid">
+ <span ng-show="forms.editForm.source.$error.required">source is required.</span>
+ </sdc-error-tooltip>
+ </div-->
+
+
+ </div>
+
+ <div class="w-sdc-form-column">
+ <div class="w-sdc-form-columns-wrapper">
+ <div class="w-sdc-form-column">
+ <!-- Type -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Type</label>
+ <select class="i-sdc-form-select"
+ data-tests-id="propertyType"
+ data-required
+ data-ng-disabled="isPropertyValueOwner() || editPropertyModel.property.readonly"
+ name="type"
+ data-ng-change="onTypeChange()"
+ data-ng-model="editPropertyModel.property.type">
+ <option value="">Choose Type</option>
+ <option data-ng-repeat="type in editPropertyModel.types"
+ value="{{type}}">{{type}}</option>
+ <option data-ng-repeat="type in nonPrimitiveTypes"
+ value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+ </div>
+ <div class="w-sdc-form-column" data-ng-if="showSchema()">
+ <!-- Entry Schema -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select"
+ data-required
+ data-tests-id="schema-type"
+ data-ng-disabled="isPropertyValueOwner() || editPropertyModel.property.readonly"
+ name="schemaType"
+ data-ng-change="onSchemaTypeChange()"
+ data-ng-model="editPropertyModel.property.schema.property.type">
+ <option value="">Choose Schema Type</option>
+ <option data-ng-repeat="type in editPropertyModel.simpleTypes"
+ value="{{type}}">{{type}}</option>
+ <option data-ng-repeat="type in nonPrimitiveTypes"
+ value="{{type}}">{{type.replace("org.openecomp.datatypes.heat.","")}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid">
+ <span ng-show="forms.editForm.schemaType.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Constraints by type -->
+ <div class="i-sdc-form-item" data-ng-if="false">
+ <label class="i-sdc-form-label required">Constraints by type</label>
+ <div>
+ Should be constraints by type(TBD)
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ <!-- Description -->
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ data-ng-disabled="isPropertyValueOwner() || editPropertyModel.property.readonly"
+ maxlength="256"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="editPropertyModel.property.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"
+ ></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+ <!-- Default value -->
+
+ <div data-ng-if="dataTypes" class="default-value-section i-sdc-form-item">
+ <label class="i-sdc-form-label">Default Value</label>
+ <div data-ng-if="isTypeDataType">
+ <fields-structure value-obj-ref="myValue"
+ type-name="editPropertyModel.property.type"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ default-value="{{getDefaultValue()}}"
+ types="dataTypes"
+ expand-by-default="true"></fields-structure>
+
+ </div>
+ <div data-ng-if="!isTypeDataType" ng-switch="editPropertyModel.property.type">
+ <div ng-switch-when="map">
+ <type-map value-obj-ref="myValue"
+ schema-property="editPropertyModel.property.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ default-value="{{getDefaultValue()}}"
+ types="dataTypes"
+ max-length="maxLength"></type-map>
+ </div>
+ <div ng-switch-when="list">
+ <type-list value-obj-ref="myValue"
+ schema-property="editPropertyModel.property.schema.property"
+ parent-form-obj="forms.editForm"
+ fields-prefix-name="currentPropertyIndex"
+ read-only="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ default-value="{{getDefaultValue()}}"
+ types="dataTypes"
+ max-length="maxLength"></type-list>
+ </div>
+ <div ng-switch-default>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <input class="i-sdc-form-input"
+ data-tests-id="defaultvalue"
+ ng-if="!((editPropertyModel.property.simpleType||editPropertyModel.property.type) == 'boolean')"
+ data-ng-maxlength="maxLength"
+ data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ maxlength="{{maxLength}}"
+ data-ng-model="editPropertyModel.property.value"
+ type="text"
+ name="value"
+ data-ng-pattern="getValidationPattern((editPropertyModel.property.simpleType||editPropertyModel.property.type))"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="!forms.editForm.value.$error.pattern && ('integer'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editPropertyModel.property.value)) || onValueChange())"
+ autofocus />
+ <select class="i-sdc-form-select"
+ data-tests-id="booleantype"
+ ng-if="(editPropertyModel.property.simpleType||editPropertyModel.property.type) == 'boolean'"
+ data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="editPropertyModel.property.value">
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '{{maxLength}}' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="PROPERTY_EDIT_PATTERN"></span>
+ <span ng-show="forms.editForm.value.$error.customValidation" translate="PROPERTY_EDIT_MAP_UNIQUE_KEYS"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+ </form>
+ </perfect-scrollbar>
+ </div>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/forms/property-form/property-form.less b/catalog-ui/app/scripts/view-models/forms/property-form/property-form.less
new file mode 100644
index 0000000000..15e30af4ee
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/property-form/property-form.less
@@ -0,0 +1,63 @@
+.sdc-edit-property-container {
+ .scrollbar-container{
+ height: 415px;
+ width: 830px;
+ .perfect-scrollbar;
+ }
+
+ form{
+ width: 813px;
+ [name="description"]{
+ min-height:50px;
+ }
+ }
+
+ .sdc-modal-top-bar{
+ height: 40px;
+ .sdc-modal-top-bar-buttons {
+ float: right;
+
+ > span:not(.delimiter){
+ vertical-align: middle;
+ .hand;
+
+ &.sprite-new {
+ text-indent: 100%;
+ }
+ &.disabled, &:hover.disabled {
+ pointer-events: none;
+ }
+ }
+
+ .delete-btn{
+ margin-right: 6px;
+ }
+
+ .left-arrow{
+ margin-right: 8px;
+ }
+
+ .delimiter {
+ height: 20px;
+ width: 1px;
+ background-color: #959595;
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 10px;
+ }
+ }
+ }
+
+ .w-sdc-form-note {
+ .h_9;
+ display: block;
+ position: relative;
+ top: 13px;
+ }
+
+ .default-value-section{
+ border-top: solid 1px @main_color_a;
+ padding-top: 15px;
+ margin-top: 15px;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts b/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts
new file mode 100644
index 0000000000..b69bf4a2a6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IResourceInstanceViewModelScope extends ng.IScope {
+
+ componentInstanceModel: Sdc.Models.ComponentsInstances.ComponentInstance;
+ validationPattern: RegExp;
+ oldName:string;
+ isAlreadyPressed:boolean;
+ footerButtons: Array<any>;
+ forms:any;
+ modalInstanceName:ng.ui.bootstrap.IModalServiceInstance;
+
+ save(): void;
+ close(): void;
+ }
+
+ export class ResourceInstanceNameViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'ValidationPattern',
+ '$modalInstance',
+ 'ComponentInstanceFactory',
+ 'component'
+ ];
+
+
+ constructor(private $scope:IResourceInstanceViewModelScope,
+ private ValidationPattern:RegExp,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private ComponentInstanceFactory:Utils.ComponentInstanceFactory,
+ private component:Models.Components.Component) {
+
+ this.initScope();
+ }
+
+
+ private initScope = ():void => {
+ this.$scope.forms = {};
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.componentInstanceModel = Utils.ComponentInstanceFactory.createComponentInstance(this.component.selectedInstance);
+ this.$scope.oldName = this.component.selectedInstance.name;
+ this.$scope.modalInstanceName = this.$modalInstance;
+
+ this.$scope.isAlreadyPressed = false;
+
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.dismiss();
+ };
+
+ this.$scope.save = ():void => {
+
+ let onFailed = () => {
+ this.$scope.isAlreadyPressed = true;
+ };
+
+ let onSuccess = (componentInstance:Models.ComponentsInstances.ComponentInstance) => {
+ this.$modalInstance.close();
+ this.$scope.isAlreadyPressed = false;
+ this.$scope.componentInstanceModel = componentInstance;
+ //this.component.name = componentInstance.name;//DE219124
+ this.component.selectedInstance.name = componentInstance.name;
+
+ };
+
+ this.$scope.isAlreadyPressed = true;
+ if (this.$scope.oldName != this.$scope.componentInstanceModel.name) {
+ this.component.updateComponentInstance(this.$scope.componentInstanceModel).then(onSuccess, onFailed);
+ }
+ };
+
+ this.$scope.footerButtons = [
+ {'name': 'OK', 'css': 'blue', 'callback': this.$scope.save, 'disabled': (!this.$scope.componentInstanceModel.name || this.$scope.componentInstanceModel.name === this.$scope.oldName) || this.$scope.isAlreadyPressed},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch("forms.editNameForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editNameForm.$invalid;
+ });
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-view.html b/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-view.html
new file mode 100644
index 0000000000..e04343adbd
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name-view.html
@@ -0,0 +1,72 @@
+<sdc-modal modal="modalInstanceName" type="classic" class="w-sdc-modal-resource-instance-name modal-type-confirmation" buttons="footerButtons" header="Instance Name" show-close-button="true">
+
+ <form novalidate class="w-sdc-form" name="forms.editNameForm">
+ <div class="i-sdc-form-item" data-ng-class="{error:(editNameForm.componentInstanceName.$dirty && editNameForm.resourceInstanceName.$invalid)}">
+ <label class="i-sdc-form-label required">Instance Name</label>
+ <input class="w-sdc-modal-resource-instance-input i-sdc-form-input"
+ name="componentInstanceName"
+ data-ng-maxlength="50"
+ data-ng-model="componentInstanceModel.name"
+ type="text"
+ data-required
+ data-ng-pattern="validationPattern"
+ maxlength="50"
+ autofocus
+ placeholder="Enter instance name..."
+ data-tests-id="instanceName"
+ />
+
+ <div class="input-error" data-ng-show="forms.editNameForm.componentInstanceName.$dirty && forms.editNameForm.componentInstanceName.$invalid">
+ <span ng-show="forms.editNameForm.componentInstanceName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Resource name' }"></span>
+ <span ng-show="forms.editNameForm.componentInstanceName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="forms.editNameForm.componentInstanceName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+ </form>
+
+</sdc-modal>
+
+
+
+<!--
+
+<div class="w-sdc-modal w-sdc-modal-resource-instance-name">
+ <header>
+ <div class="w-sdc-modal-head">
+ Instance Name
+ </div>
+ </header>
+ <div>
+ <form novalidate class="w-sdc-modal-body w-sdc-form" name="editNameForm">
+ <div class="i-sdc-form-item" data-ng-class="{error:(editNameForm.componentInstanceName.$dirty && editNameForm.resourceInstanceName.$invalid)}">
+ <label class="i-sdc-form-label required">Instance Name</label>
+ <input class="w-sdc-modal-resource-instance-input i-sdc-form-input"
+ name="componentInstanceName"
+ data-ng-maxlength="50"
+ data-ng-model="componentInstanceModel.name"
+ type="text"
+ data-required
+ data-ng-pattern="validationPattern"
+ maxlength="50"
+ autofocus
+ placeholder="Enter instance name..."
+ data-tests-id="instanceName"
+ />
+
+ <div class="input-error" data-ng-show="editNameForm.componentInstanceName.$dirty && editNameForm.componentInstanceName.$invalid">
+ <span ng-show="editNameForm.componentInstanceName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Resource name' }"></span>
+ <span ng-show="editNameForm.componentInstanceName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editNameForm.componentInstanceName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+ </form>
+
+ <div class="w-sdc-modal-action">
+ <button class="w-sdc-btn-blue" data-ng-click="save()" type="button" data-ng-disabled="(!componentInstanceModel.name || componentInstanceModel.name === oldName) || isAlreadyPressed">Ok</button>
+ <button class="w-sdc-btn-dark-gray" data-ng-click="close()" type="button">Cancel</button>
+ </div>
+ </div>
+</div>
+-->
diff --git a/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name.less b/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name.less
new file mode 100644
index 0000000000..57698bef17
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/forms/resource-instance-name-form/resource-instance-name.less
@@ -0,0 +1,29 @@
+.w-sdc-modal-resource-instance-name {
+
+ .w-sdc-modal-body {
+ overflow: visible;
+ }
+
+ .w-sdc-modal-action {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .w-sdc-modal-resource-instance-input {
+ .p_1;
+ border: solid 1px @color_p;
+ height: 45px;
+ padding: 0 20px;
+ margin: 0 auto 0 auto;
+ display: block;
+ }
+ .w-sdc-modal-body {
+ border-bottom: none;
+ }
+
+ .w-sdc-form .i-sdc-form-item.error::after {
+ top: 13px;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts b/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts
new file mode 100644
index 0000000000..f906593d8a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view-model.ts
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IConfirmationModalModel {
+ title: string;
+ message: string;
+ showComment: boolean;
+ type: Utils.Constants.ModalType;
+ }
+
+ interface IConfirmationModalViewModelScope {
+ modalInstanceConfirmation:ng.ui.bootstrap.IModalServiceInstance;
+ confirmationModalModel: IConfirmationModalModel;
+ comment: any;
+ commentValidationPattern:RegExp;
+ editForm:ng.IFormController;
+ okButtonColor: string;
+ hideCancelButton: boolean;
+ ok(): any;
+ cancel(): void;
+ }
+
+ export class ConfirmationModalViewModel {
+
+ static '$inject' = ['$scope', '$modalInstance', 'confirmationModalModel', 'CommentValidationPattern', 'ValidationUtils', '$templateCache', '$modal'];
+
+ constructor(private $scope:IConfirmationModalViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ confirmationModalModel:IConfirmationModalModel,
+ private CommentValidationPattern: RegExp,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private $templateCache:ng.ITemplateCacheService,
+ private $modal:ng.ui.bootstrap.IModalService) {
+
+ this.initScope(confirmationModalModel);
+ }
+
+ private initScope = (confirmationModalModel:IConfirmationModalModel):void => {
+ let self = this;
+ this.$scope.hideCancelButton = false;
+ this.$scope.modalInstanceConfirmation = this.$modalInstance;
+ this.$scope.confirmationModalModel = confirmationModalModel;
+ this.$scope.comment = {"text": ''};
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+
+ this.$scope.ok = ():any => {
+ self.$modalInstance.close(this.ValidationUtils.stripAndSanitize(self.$scope.comment.text));
+ };
+
+ this.$scope.cancel = ():void => {
+ console.info('Cancel pressed on: ' + this.$scope.confirmationModalModel.title);
+ self.$modalInstance.dismiss();
+ };
+
+ // Set the OK button color according to modal type (standard, error, alert)
+ let _okButtonColor = 'blue'; // Default
+ switch (confirmationModalModel.type) {
+ case Sdc.Utils.Constants.ModalType.STANDARD:
+ _okButtonColor='blue';
+ break;
+ case Sdc.Utils.Constants.ModalType.ERROR:
+ _okButtonColor='red';
+ break;
+ case Sdc.Utils.Constants.ModalType.ALERT:
+ this.$scope.hideCancelButton = true;
+ _okButtonColor='grey';
+ break;
+ default:
+ _okButtonColor='blue';
+ break;
+ }
+ this.$scope.okButtonColor = _okButtonColor;
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view.html b/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view.html
new file mode 100644
index 0000000000..09c27f8cd3
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal-view.html
@@ -0,0 +1,29 @@
+<sdc-modal modal="modalInstanceConfirmation" type="classic" class="w-sdc-modal-confirmation modal-type-{{confirmationModalModel.type}}" header="{{confirmationModalModel.title}}" show-close-button="true">
+ <form novalidate class="w-sdc-form" name="editForm">
+ <label class="i-sdc-form-label required w-sdc-modal-label" data-ng-bind-html="confirmationModalModel.message"></label>
+
+ <div class="i-sdc-form-item">
+ <textarea class="w-sdc-modal-body-comment"
+ data-tests-id="checkindialog"
+ autofocus="autofocus"
+ data-ng-show="confirmationModalModel.showComment===true"
+ data-ng-model="comment.text"
+ placeholder="Comment..."
+ maxlength="256"
+ data-required
+ name="comment1"
+ data-ng-pattern="commentValidationPattern"
+ data-ng-maxlength="256"></textarea>
+
+ <div class="input-error" data-ng-show="editForm.comment1.$dirty && editForm.comment1.$invalid">
+ <span ng-show="editForm.comment1.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="editForm.comment1.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Comment' }"></span>
+ </div>
+ </div>
+ </form>
+ <div class="w-sdc-modal-footer classic">
+ <button class="tlv-btn {{okButtonColor}}" data-tests-id="OK" data-ng-click="ok()" data-ng-disabled="confirmationModalModel.showComment===true && (!comment.text || comment.text && comment.text.length===0)">OK</button>
+ <button class="tlv-btn grey" data-ng-if="hideCancelButton===false" data-tests-id="Cancel" data-ng-click="cancel()" >Cancel</button>
+ <button class="tlv-btn blue add-property-add-another" data-ng-if="isNew" data-ng-click="saveAndAnother()" type="reset" data-ng-disabled="editForm.$invalid">Add Another</button>
+ </div>
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal.less b/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal.less
new file mode 100644
index 0000000000..666c41d5ed
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/confirmation-modal/confirmation-modal.less
@@ -0,0 +1,30 @@
+.w-sdc-modal-confirmation {
+ form.w-sdc-form{
+ padding: 0;
+ }
+
+ .w-sdc-modal-body-content {
+ .b_6;
+ word-break: break-word;
+
+ }
+ .w-sdc-modal-body {
+ height: auto;
+ /* padding: 47px 60px 20px 60px; */
+ border-bottom: none;
+ }
+ .w-sdc-modal-body-content {
+ padding: 0;
+ }
+ .w-sdc-modal-body-comment {
+ width: 430px;
+ height: 127px;
+ border: solid 1px @color_e;
+ margin: 20px 0 0 0;
+ padding: 15px;
+ }
+ .w-sdc-modal-label {
+ .m_14_r;
+ text-align: left;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view-model.ts b/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view-model.ts
new file mode 100644
index 0000000000..6430a955a6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view-model.ts
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IEmailModalModel_Email {
+ to: string;
+ subject: string;
+ message: string;
+ }
+
+ export interface IEmailModalModel_Data {
+ component: Models.Components.Component;
+ stateUrl: string;
+ }
+
+ export interface IEmailModalModel {
+ title: string;
+ email: IEmailModalModel_Email;
+ data: IEmailModalModel_Data;
+ }
+
+ interface IEmailModalViewModelScope {
+ modalInstanceEmail:ng.ui.bootstrap.IModalServiceInstance;
+ emailModalModel: IEmailModalModel;
+ submitInProgress:boolean;
+ commentValidationPattern:RegExp;
+ isLoading:boolean;
+ submit(): any;
+ cancel(): void;
+ validateField(field:any):boolean;
+ }
+
+ export class EmailModalViewModel {
+
+ static '$inject' = ['$scope', '$filter', 'sdcConfig', '$modalInstance', 'emailModalModel', 'ValidationUtils', 'CommentValidationPattern'];
+
+ constructor(private $scope:IEmailModalViewModelScope,
+ private $filter:ng.IFilterService,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private emailModalModel:IEmailModalModel,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private CommentValidationPattern: RegExp) {
+
+ this.initScope(emailModalModel);
+ }
+
+ private initScope = (emailModalModel:IEmailModalModel):void => {
+ this.$scope.emailModalModel = emailModalModel;
+ this.$scope.submitInProgress=false;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.modalInstanceEmail = this.$modalInstance;
+
+ this.$scope.submit = ():any => {
+
+ let onSuccess = (component:Models.Components.Component) => {
+ this.$scope.isLoading = false;
+ this.$scope.submitInProgress=false;
+ let link:string = encodeURI(this.sdcConfig.api.baseUrl + "?folder=Ready_For_Testing");
+ let outlook:string = this.$filter('translate')("EMAIL_OUTLOOK_MESSAGE", "{'to': '" + emailModalModel.email.to + "','subject': '" + emailModalModel.email.subject + "','message': '" + emailModalModel.email.message + "', 'entityNameAndVersion': '" + emailModalModel.email.subject + "','link': '" + link + "'}");
+ if(!this.sdcConfig.openSource) {
+ window.location.href=outlook; // Open outlook with the email to send
+ }
+ this.$modalInstance.close(component); // Close the dialog
+ };
+
+ let onError = () => {
+ this.$scope.isLoading = false;
+ this.$scope.submitInProgress=false;
+ this.$modalInstance.close(); // Close the dialog
+ };
+
+ // Submit to server
+ // Prevent from user pressing multiple times on submit.
+ if (this.$scope.submitInProgress===false) {
+ this.$scope.isLoading = true;
+ this.$scope.submitInProgress = true;
+ let comment:Models.AsdcComment = new Models.AsdcComment();
+ comment.userRemarks = emailModalModel.email.message;
+ emailModalModel.data.component.changeLifecycleState(emailModalModel.data.stateUrl, comment).then(onSuccess, onError);
+ }
+ };
+
+ this.$scope.cancel = ():void => {
+ this.$modalInstance.dismiss();
+ };
+
+ this.$scope.validateField = (field:any):boolean => {
+ if (field && field.$dirty && field.$invalid){
+ return true;
+ }
+ return false;
+ };
+ }
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view.html b/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view.html
new file mode 100644
index 0000000000..82293a3091
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal-view.html
@@ -0,0 +1,79 @@
+<sdc-modal modal="modalInstanceEmail" type="classic" class="w-sdc-modal-email modal-type-standard" header="{{emailModalModel.title}}" show-close-button="true">
+ <loader data-display="isLoading"></loader>
+ <form novalidate class="w-sdc-form" name="editForm">
+ <div class="w-sdc-modal-body">
+
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.to)}">
+ <label class="i-sdc-form-label col-sm-2">To</label>
+ <div class="col-sm-10">
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="emailModalModel.email.to"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="255"
+ data-required
+ name="to"
+ id="to"
+ data-ng-disabled="true"
+ />
+ </div>
+
+ <div class="input-error" data-ng-show="validateField(editForm.to)" alignToSelector="#to">
+ <span ng-show="editForm.to.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'To' }"></span>
+ <span ng-show="editForm.to.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '255' }"></span>
+ <span ng-show="editForm.to.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.subject)}">
+ <label class="i-sdc-form-label col-sm-2">Subject</label>
+ <div class="col-sm-10">
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="emailModalModel.email.subject"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="255"
+ data-required
+ name="subject"
+ data-ng-disabled="true"
+ />
+ </div>
+
+ <div class="input-error" data-ng-show="validateField(editForm.subject)">
+ <span ng-show="editForm.subject.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Subject' }"></span>
+ <span ng-show="editForm.subject.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '255' }"></span>
+ <span ng-show="editForm.subject.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.message)}">
+ <label class="i-sdc-form-label required col-sm-2">Message</label>
+ <div class="col-sm-10">
+ <textarea class="w-sdc-modal-body-email"
+ data-ng-model="emailModalModel.email.message"
+ placeholder="{{'EMAIL_MODAL_MESSAGE' | translate }}"
+ data-required
+ name="message"
+ data-ng-pattern="commentValidationPattern"
+ maxlength="255"
+ data-tests-id="changeLifeCycleMessage"
+ data-ng-maxlength="255">
+ </textarea>
+
+ <div class="input-error" data-ng-show="validateField(editForm.message)">
+ <span ng-show="editForm.message.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Message' }"></span>
+ <span ng-show="editForm.message.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '255' }"></span>
+ <span ng-show="editForm.message.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ </form>
+
+ <div class="w-sdc-modal-footer classic">
+ <button class="tlv-btn blue" data-tests-id="OK" data-ng-click="submit()" data-ng-disabled="editForm.$invalid">OK</button>
+ <button class="tlv-btn grey" data-tests-id="Cancel" data-ng-click="cancel()" >Cancel</button>
+ </div>
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal.less b/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal.less
new file mode 100644
index 0000000000..b946a097cd
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/email-modal/email-modal.less
@@ -0,0 +1,56 @@
+.w-sdc-modal-email {
+
+ .w-sdc-modal-body {
+ border-bottom: none;
+ }
+
+ form.w-sdc-form{
+ padding: 0;
+
+ .i-sdc-form-item {
+ clear: both;
+ label {
+ min-height: 30px;
+ padding-top: 4px;
+ }
+
+ .col-sm-10 {
+ padding-right: 0;
+ }
+
+ }
+
+ .w-sdc-modal-body-email {
+ border-style: solid;
+ border-width: 1px;
+ border-color: @color_e;
+ box-sizing: border-box;
+ width: 100%;
+ height: 127px;
+ }
+
+ label {.m_14_m; text-align: left;}
+ input {.m_14_r;}
+ textarea {.m_14_r;}
+ /* I made the subject and to fields as input (for future use), but for now they look like labels: */
+ input:disabled {
+ .bg_c;
+ border: none;
+ }
+ }
+
+ .w-sdc-modal-action {
+ background-color: @main_color_p;
+ padding: 0 13px 0 0;
+ height: 90px;
+ line-height: 65px;
+
+ button {width: 174px;}
+ }
+
+ .w-sdc-form .i-sdc-form-item label.required::before {
+ position: absolute;
+ left: -13px;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/error-modal/error-403-view.html b/catalog-ui/app/scripts/view-models/modals/error-modal/error-403-view.html
new file mode 100644
index 0000000000..185fcce461
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/error-modal/error-403-view.html
@@ -0,0 +1,4 @@
+<div class="sdc-error-403-container" >
+ <div class="sdc-error-403-container-title" translate="GENERAL_ERROR_403_TITLE"></div>
+ <div class="w-sdc-error-403-text w-sdc-form" translate="GENERAL_ERROR_403_DESCRIPTION" translate-values="{{ mailtoJson }}"></div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/modals/error-modal/error-view-model.ts b/catalog-ui/app/scripts/view-models/modals/error-modal/error-view-model.ts
new file mode 100644
index 0000000000..b8b2bfbbe7
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/error-modal/error-view-model.ts
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IErrorViewModelScope{
+ mailtoJson: any;
+ }
+
+ export class ErrorViewModel {
+
+ static '$inject' = ['$scope', 'Sdc.Services.CookieService', '$window', '$filter'];
+
+ constructor($scope:IErrorViewModelScope, cookieService:Services.CookieService, $window, $filter:ng.IFilterService){
+ let adminEmail:string = $filter('translate')('ADMIN_EMAIL');
+ let subjectPrefix:string = $filter('translate')('EMAIL_SUBJECT_PREFIX');
+ let userDetails = cookieService.getFirstName() + ' '+cookieService.getLastName() + ' ('+cookieService.getUserId() + ')';
+ let line = adminEmail+'?subject='+$window.encodeURIComponent(subjectPrefix+' '+userDetails);
+ $scope.mailtoJson = {
+ "mailto": line
+ };
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/error-modal/error.less b/catalog-ui/app/scripts/view-models/modals/error-modal/error.less
new file mode 100644
index 0000000000..8297b5053d
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/error-modal/error.less
@@ -0,0 +1,13 @@
+.sdc-error-403-container {
+ .bg_n;
+ width: 700px;
+ height: 400px;
+ margin: auto;
+ margin-top: 196px;
+
+ .w-sdc-error-403-text {
+ .q_11;
+ margin-top: 20px;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-base-modal-model.ts b/catalog-ui/app/scripts/view-models/modals/message-modal/message-base-modal-model.ts
new file mode 100644
index 0000000000..26df780d25
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-base-modal-model.ts
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IMessageModalModel {
+ title: string;
+ message: string;
+ severity: Utils.Constants.SEVERITY;
+ }
+
+ export interface IMessageModalViewModelScope extends ng.IScope {
+ footerButtons: Array<any>;
+ messageModalModel: IMessageModalModel;
+ modalInstanceError:ng.ui.bootstrap.IModalServiceInstance;
+ ok(): void;
+ }
+
+ export class MessageModalViewModel {
+
+ constructor(private $baseScope:IMessageModalViewModelScope,
+ private $baseModalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private baseMessageModalModel:IMessageModalModel) {
+
+ this.initScope(baseMessageModalModel);
+ }
+
+ private initScope = (messageModalViewModel:IMessageModalModel):void => {
+
+ this.$baseScope.messageModalModel = messageModalViewModel;
+ this.$baseScope.modalInstanceError = this.$baseModalInstance;
+
+ this.$baseScope.ok = ():void => {
+ this.$baseModalInstance.close();
+ };
+
+ this.$baseScope.footerButtons = [
+ {
+ 'name': 'OK',
+ 'css': 'grey',
+ 'callback': this.$baseScope.ok
+ }
+ ];
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts b/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts
new file mode 100644
index 0000000000..82afe11fe4
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view-model.ts
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IClientMessageModalModel extends IMessageModalModel {
+ }
+
+ export interface IClientMessageModalViewModelScope extends IMessageModalViewModelScope {
+ clientMessageModalModel: IClientMessageModalModel;
+ }
+
+ export class ClientMessageModalViewModel extends MessageModalViewModel {
+
+ static '$inject' = ['$scope', '$modalInstance', 'clientMessageModalModel'];
+
+ constructor(private $scope:IClientMessageModalViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private clientMessageModalModel:IClientMessageModalModel) {
+
+ super($scope, $modalInstance, clientMessageModalModel);
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html b/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html
new file mode 100644
index 0000000000..cfb0a35f69
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal-view.html
@@ -0,0 +1,16 @@
+<sdc-modal modal="modalInstanceError"
+ type="classic"
+ class="w-sdc-modal modal-type-alert"
+ header="{{messageModalModel.title}}"
+ buttons="footerButtons"
+ show-close-button="true">
+
+ <perfect-scrollbar include-padding="true">
+ <div class="w-sdc-modal-icon w-sdc-modal-icon-{{messageModalModel.severity}}"></div>
+ <div class="w-sdc-modal-caption">
+ <div ng-bind-html="messageModalModel.message" data-tests-id="message"></div>
+ </div>
+ <!--<div class="w-sdc-modal-body-content" data-ng-bind-html="messageModalModel.message"></div>-->
+ </perfect-scrollbar>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal.less b/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal.less
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-client-modal/client-message-modal.less
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts b/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts
new file mode 100644
index 0000000000..f7a0dcfabf
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view-model.ts
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IServerMessageModalModel extends IMessageModalModel {
+ status: string;
+ messageId: string;
+ }
+
+ export interface IServerMessageModalViewModelScope extends IMessageModalViewModelScope {
+ serverMessageModalModel: IServerMessageModalModel;
+ }
+
+ export class ServerMessageModalViewModel extends MessageModalViewModel {
+
+ static '$inject' = ['$scope', '$modalInstance', 'serverMessageModalModel'];
+
+ constructor(private $scope:IServerMessageModalViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private serverMessageModalModel:IServerMessageModalModel) {
+
+ super($scope, $modalInstance, serverMessageModalModel);
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html b/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html
new file mode 100644
index 0000000000..294dc76c4c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal-view.html
@@ -0,0 +1,17 @@
+<sdc-modal modal="modalInstanceError"
+ type="classic"
+ class="w-sdc-modal modal-type-error"
+ header="{{messageModalModel.title}}"
+ buttons="footerButtons"
+ show-close-button="true">
+
+ <perfect-scrollbar include-padding="true">
+ <div class="w-sdc-modal-icon w-sdc-modal-icon-{{messageModalModel.severity}}"></div>
+ <div class="w-sdc-modal-caption">
+ <div>Error code: {{messageModalModel.messageId}}</div>
+ <div>Status code: {{messageModalModel.status}}</div>
+ </div>
+ <div class="w-sdc-modal-body-content" data-ng-bind-html="messageModalModel.message" data-tests-id="message"></div>
+ </perfect-scrollbar>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal.less b/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal.less
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/message-modal/message-server-modal/server-message-modal.less
diff --git a/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts b/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts
new file mode 100644
index 0000000000..a6e85c4abc
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view-model.ts
@@ -0,0 +1,249 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+ import ComponentFactory = Sdc.Utils.ComponentFactory;
+
+ interface IOnboardingModalViewModelScope {
+ modalOnboarding: ng.ui.bootstrap.IModalServiceInstance;
+ componentsList: Array<Models.Components.IComponent>;
+ tableHeadersList: Array<any>;
+ selectedComponent: Models.Components.Component;
+ componentFromServer: Models.Components.Component;
+ reverse: boolean;
+ sortBy: string;
+ searchBind: string;
+ okButtonText: string;
+ isCsarComponentExists: boolean;
+ user: Models.IUser;
+ isLoading: boolean;
+
+ doSelectComponent(component: Models.Components.Component): void;
+ doUpdateCsar(): void;
+ doImportCsar(): void;
+ sort(sortBy: string): void;
+ downloadCsar(packageId: string): void;
+ }
+
+ export class OnboardingModalViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$state',
+ 'sdcConfig',
+ '$modalInstance',
+ 'Sdc.Services.OnboardingService',
+ 'okButtonText',
+ 'currentCsarUUID',
+ 'Sdc.Services.CacheService',
+ 'FileUtils',
+ 'ComponentFactory',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope: IOnboardingModalViewModelScope,
+ private $filter: ng.IFilterService,
+ private $state: any,
+ private sdcConfig: Models.IAppConfigurtaion,
+ private $modalInstance: ng.ui.bootstrap.IModalServiceInstance,
+ private onBoardingService: Sdc.Services.OnboardingService,
+ private okButtonText: string,
+ private currentCsarUUID: string,
+ private cacheService: Services.CacheService,
+ private fileUtils: Sdc.Utils.FileUtils,
+ private componentFactory: Utils.ComponentFactory,
+ private modalsHandler: Sdc.Utils.ModalsHandler) {
+
+ this.init();
+ }
+
+ /**
+ * Called from controller constructor, this will call onboarding service to get list
+ * of "mini" components (empty components created from CSAR).
+ * The list is inserted to componentsList on $scope.
+ * And then call initScope method.
+ */
+ private init = (): void => {
+ this.initOnboardingComponentsList();
+ };
+
+ private initScope = (): void => {
+
+ this.initSortedTableScope();
+ this.initModalScope();
+ this.$scope.sortBy = "name"; // Default sort by
+ this.$scope.user = this.cacheService.get('user');
+ this.$scope.okButtonText = this.okButtonText;
+
+ // Dismiss the modal and pass the "mini" component to workspace general page
+ this.$scope.doImportCsar = (): void => {
+ this.$modalInstance.dismiss();
+ this.$state.go('workspace.general', {
+ type: Utils.Constants.ComponentType.RESOURCE.toLowerCase(),
+ componentCsar: this.$scope.selectedComponent
+ });
+ };
+
+ this.$scope.doUpdateCsar = (): void => {
+ // In case user select on update the checkin and submit for testing buttons (in general page) should be disabled.
+ // to do that we need to pass to workspace.general state parameter to know to disable the buttons.
+ this.$modalInstance.close();
+ // Change the component version to the CSAR version we want to update.
+ /*(<Resource>this.$scope.componentFromServer).csarVersion = (<Resource>this.$scope.selectedComponent).csarVersion;
+ let component:Models.Components.Component = this.componentFactory.createComponent(this.$scope.componentFromServer);
+ this.$state.go('workspace.general', {vspComponent: component, disableButtons: true });*/
+ this.cacheService.set(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG, (<Resource>this.$scope.selectedComponent).csarVersion);
+ this.$state.go('workspace.general', {
+ id: this.$scope.componentFromServer.uniqueId,
+ type: this.$scope.componentFromServer.componentType.toLowerCase(),
+ disableButtons: true
+ });
+ };
+
+ this.$scope.downloadCsar = (packageId: string): void => {
+ this.onBoardingService.downloadOnboardingCsar(packageId).then(
+ (file: any): void => {
+ if (file) {
+ this.fileUtils.downloadFile(file, packageId + '.zip');
+ }
+ }, (): void => {
+ let data: Sdc.ViewModels.IServerMessageModalModel = {
+ title: 'Download error',
+ message: "Error downloading file",
+ severity: Utils.Constants.SEVERITY.ERROR,
+ messageId: "",
+ status: ""
+ };
+ this.modalsHandler.openServerMessageModal(data);
+ }
+ );
+ };
+
+ // When the user select a row, set the component as selectedComponent
+ this.$scope.doSelectComponent = (component: Models.Components.Component): void => {
+
+ if (this.$scope.selectedComponent === component) {
+ // Collapse the item
+ this.$scope.selectedComponent = undefined;
+ return;
+ }
+
+ this.$scope.isLoading = true;
+ this.$scope.componentFromServer = undefined;
+ this.$scope.selectedComponent = component;
+
+ let onSuccess = (componentFromServer: Models.Components.Component): void => {
+ this.$scope.isLoading = false;
+ if (componentFromServer) {
+ this.$scope.componentFromServer = componentFromServer;
+ this.$scope.isCsarComponentExists = true;
+ } else {
+ this.$scope.componentFromServer = component;
+ this.$scope.isCsarComponentExists = false;
+ }
+ };
+
+ let onError = (): void => {
+ this.$scope.isLoading = false;
+ this.$scope.componentFromServer = component;
+ this.$scope.isCsarComponentExists = false;
+ };
+
+ this.onBoardingService.getComponentFromCsarUuid((<Resource>component).csarUUID).then(onSuccess, onError);
+ };
+
+ };
+
+ private initSortedTableScope = (): void => {
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Vendor', property: 'vendorName'},
+ {title: 'Category', property: 'categories'},
+ {title: 'Version', property: 'csarVersion'},
+ {title: '#', property: 'importAndUpdate'}
+ //{title: 'Date', property: 'componentDate'}
+ ];
+
+ this.$scope.sort = (sortBy: string): void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+ };
+
+ private initModalScope = (): void => {
+ // Enable the modal directive to close
+ this.$scope.modalOnboarding = this.$modalInstance;
+ };
+
+ private initOnboardingComponentsList = (): void => {
+ let onSuccess = (onboardingResponse: Array<Models.Components.IComponent>): void => {
+ initMaxVersionOfItemsInList(onboardingResponse);
+
+ if (this.currentCsarUUID) {
+ //this.$scope.componentsList = this.$filter('filter')(this.$scope.componentsList, {csarUUID: this.currentCsarUUID});
+ this.$scope.componentsList = this.$filter('filter')(this.$scope.componentsList,
+ (input): boolean => {
+ return input.csarUUID === this.currentCsarUUID;
+ }
+ );
+ }
+ this.initScope();
+ };
+
+ let onError = (): void => {
+ console.log("Error getting onboarding list");
+ this.initScope();
+ };
+
+ let initMaxVersionOfItemsInList = (onboardingResponse: Array<Models.Components.IComponent>): void => {
+ // Get only the latest version of each item
+ this.$scope.componentsList = [];
+
+ // Get all unique items from the list
+ let uniqueItems = _.uniqBy(onboardingResponse, 'packageId');
+
+ // Loop on all the items with unique packageId
+ _.each(uniqueItems, (item: any): void => {
+ // Find all the items that has same packageId
+ let ItemsFound: Array<Models.Components.IComponent> = _.filter(onboardingResponse, (inListItem: any): any => {
+ return inListItem.packageId === item.packageId;
+ });
+
+ // Loop on all the items with same packageId and find the max version.
+ let maxItem: any;
+ _.each(ItemsFound, (ItemFound: any): void => {
+ if (!maxItem) {
+ maxItem = ItemFound;
+ } else if (maxItem && parseInt(maxItem.csarVersion) < parseInt(ItemFound.csarVersion)) {
+ maxItem = ItemFound;
+ }
+ });
+ this.$scope.componentsList.push(maxItem);
+ });
+ };
+
+ this.onBoardingService.getOnboardingComponents().then(onSuccess, onError);
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view.html b/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view.html
new file mode 100644
index 0000000000..246915212c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal-view.html
@@ -0,0 +1,142 @@
+<sdc-modal modal="modalOnboarding" class="w-sdc-modal-onboarding w-sdc-classic-top-line-modal" buttons="footerButtons" header="Import VF" show-close-button="true">
+ <info-tooltip class="general-info-button" info-message-translate="ON_BOARDING_GENERAL_INFO "></info-tooltip>
+ <div class="title-wrapper">
+ <div>
+ <p class="sub-title">Select one of the software product component below:</p>
+ </div>
+
+ <div class="top-search">
+ <input type="text"
+ class="search-text"
+ placeholder="Search"
+ data-ng-model="searchBind"
+ data-tests-id="onboarding-search"
+ ng-model-options="{ debounce: 500 }" />
+ <span class="w-sdc-search-icon magnification"></span>
+ </div>
+ </div>
+
+ <div class="table-container-flex">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+
+ <!-- Table headers -->
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)" data-tests-id="{{header.title}}">{{header.title}}
+ <span data-ng-show="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ </div>
+
+ <!-- Table body -->
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+
+ <!-- In case the component list is empty -->
+ <div data-ng-if="!componentsList || componentsList.length===0" class="no-row-text">
+ There are no software product component to display
+ </div>
+
+ <!-- Loop on components list -->
+ <div data-ng-repeat-start="component in componentsList | filter: searchBind | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': component === selectedComponent}"
+ data-ng-click="doSelectComponent(component);"
+ data-tests-id="csar-row"
+ >
+
+ <!-- Name -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ <span class="sprite table-arrow" data-ng-class="{'opened': component === selectedComponent}" data-tests-id="{{component.name}}"></span>
+ {{component.name}}
+ </div>
+
+ <!-- Vendor -->
+ <div class="table-col-general flex-item" data-tests-id="{{component.vendorName}}" sdc-smart-tooltip>
+ {{component.vendorName}}
+ </div>
+
+ <!-- Category -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{component.categories[0].name}}&nbsp;{{component.categories[0].subcategories[0].name}}
+ </div>
+
+ <!-- Version -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{component.csarVersion}}
+ </div>
+
+ <!-- Import And Update -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip></div>
+
+ </div>
+
+ <div data-ng-repeat-end="" data-ng-if="component===selectedComponent" class="item-opened">
+
+ <div class="item-opened-description">
+ <div class="item-opened-description-title">VSP Description:</div>
+ {{component.description}}
+ </div>
+
+ <div class="item-opened-metadata1">
+ <div data-ng-if="isCsarComponentExists===true">
+ <div class="item-opened-metadata-title">VF'S Meta Data:</div>
+ <div><span class="th">Name:</span> {{componentFromServer.name}}</div>
+ <div><span class="th">Lifecycle:</span> {{componentFromServer.lifecycleState}}</div>
+ <div><span class="th">Creator:</span> {{componentFromServer.creatorFullName}}</div>
+ </div>
+ </div>
+
+ <div class="item-opened-metadata2">
+ <div data-ng-if="isCsarComponentExists===true">
+ <div class="item-opened-metadata-title">&nbsp;</div>
+ <div><span class="th">UUID:</span> {{componentFromServer.uuid}}</div>
+ <div><span class="th">Version:</span> {{componentFromServer.version}}</div>
+ <div><span class="th">Modifier:</span> {{componentFromServer.lastUpdaterFullName}}</div>
+ <div data-ng-if="componentFromServer.lifecycleState==='NOT_CERTIFIED_CHECKOUT' && componentFromServer.lastUpdaterUserId !== user.userId">
+ <span class="note">Designers cannot update a VSP if the VF is checked out by another user.</span>
+ </div>
+ <div data-ng-if="componentFromServer.lifecycleState==='READY_FOR_CERTIFICATION'">
+ <span class="note">Designers cannot update a VSP if the VF is in Ready for testing state.</span>
+ </div>
+ </div>
+ </div>
+
+ <div class="item-opened-metadata3">
+ <info-tooltip class="info-button" info-message-translate="{{isCsarComponentExists?'ON_BOARDING_UPDATE_INFO':'ON_BOARDING_IMPORT_INFO'}}" direction="left"></info-tooltip>
+ </div>
+
+ <div class="item-opened-icon">
+ <span data-ng-if="isCsarComponentExists!==true"
+ class="sprite-new import-file-btn"
+ data-ng-click="doImportCsar()"
+ uib-tooltip="Import VSP"
+ tooltip-class="uib-custom-tooltip"
+ tooltip-placement="bottom"
+ data-tests-id="import-csar"></span>
+
+ <span data-ng-if="isCsarComponentExists===true"
+ class="sprite-new refresh-file-btn"
+ uib-tooltip="Update VSP"
+ tooltip-class="uib-custom-tooltip"
+ tooltip-placement="bottom"
+ data-ng-class="{'disabled': (componentFromServer.lifecycleState==='NOT_CERTIFIED_CHECKOUT' && componentFromServer.lastUpdaterUserId!==user.userId) || componentFromServer.lifecycleState==='READY_FOR_CERTIFICATION'}"
+ data-ng-click="doUpdateCsar()"
+ data-tests-id="update-csar"></span>
+
+ <span data-ng-click="downloadCsar(component.packageId)"
+ class="sprite-new download-file-btn hand"
+ uib-tooltip="Download VSP"
+ tooltip-class="uib-custom-tooltip"
+ tooltip-placement="bottom"
+ data-tests-id="download-csar"></span>
+ </div>
+ <loader data-display="isLoading" relative="true" size="small"></loader>
+
+ </div>
+
+ </perfect-scrollbar>
+ </div><!-- End table body -->
+ </div><!-- End table -->
+ </div><!-- End table-container-flex -->
+ <div class="w-sdc-modal-footer classic"></div>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal.less b/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal.less
new file mode 100644
index 0000000000..c745a86888
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/modals/onboarding-modal/onboarding-modal.less
@@ -0,0 +1,148 @@
+.w-sdc-modal-onboarding {
+
+ width: 100%;
+ display: inline-block;
+
+ .general-info-button{
+ position: relative;
+ top: -40px;
+ left: 86px;
+ float: left;
+ }
+
+ .title-wrapper {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-end;
+
+ .sub-title {
+ .m_14_r;
+ float:left;
+ }
+ }
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table{
+ height: 472px;
+ margin-bottom: 0;
+ }
+
+ .table-container-flex {
+ margin-top: 10px;
+
+ .table {
+ .body {
+ .data-row + div.item-opened {
+ word-wrap: break-word;
+ display: flex;
+ justify-content: space-between;
+ padding: 10px 0;
+
+ .item-opened-description-title,
+ .item-opened-metadata-title {
+ .m_14_m;
+ }
+
+ .item-opened-description,
+ .item-opened-metadata1,
+ .item-opened-metadata2,
+ .item-opened-metadata3 {
+ .th { .m_14_m; }
+ flex-basis: 0;
+ overflow: hidden;
+ padding: 5px 15px;
+ }
+
+ .item-opened-description,
+ .item-opened-metadata3 {
+ border-right: 1px solid @main_color_o;
+ }
+
+ .item-opened-metadata2 {
+ word-break: break-word;
+ .note {
+ color: @func_color_q;
+ }
+ }
+
+ .item-opened-icon {
+ flex-basis: 0;
+ overflow: hidden;
+ padding: 5px 15px;
+ align-self: center;
+ }
+
+ .item-opened-description {flex-grow: 25;}
+ .item-opened-metadata1 {flex-grow: 25;}
+ .item-opened-metadata2 {flex-grow: 30;}
+ .item-opened-metadata3 {
+ flex-grow: 10;
+ .info-button{
+ float: right;
+ }
+ }
+ .item-opened-icon {flex-grow: 10;}
+ }
+ }
+ }
+
+ .flex-item:nth-child(1) {
+ flex-grow: 25;
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {flex-grow: 25;}
+ .flex-item:nth-child(3) {flex-grow: 30;}
+ .flex-item:nth-child(4) {flex-grow: 10; text-align: center; }
+ .flex-item:nth-child(5) {flex-grow: 10; }
+
+ }
+
+ .download-file-btn {
+ cursor: pointer;
+ margin-left: 4px;
+ }
+
+ .refresh-file-btn,
+ .import-file-btn {
+ cursor: pointer;
+ margin-left: 20px;
+ }
+
+ .top-search {
+ float: right;
+ position: relative;
+
+ input.search-text {
+ .border-radius(2px);
+ width: 245px;
+ height: 32px;
+ line-height: 32px;
+ border: 1px solid @main_color_o;
+ margin: 0;
+ outline: none;
+ text-indent: 10px;
+
+ &::-webkit-input-placeholder { font-style: italic; } /* Safari, Chrome and Opera */
+ &:-moz-placeholder { font-style: italic; } /* Firefox 18- */
+ &::-moz-placeholder { font-style: italic; } /* Firefox 19+ */
+ &:-ms-input-placeholder { font-style: italic; } /* IE 10+ */
+ &:-ms-input-placeholder { font-style: italic; } /* Edge */
+ }
+
+ .magnification {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view-model.ts b/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view-model.ts
new file mode 100644
index 0000000000..c8be2b7361
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view-model.ts
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references.ts"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export class BreadcrumbsMenuItem {
+ key: string;
+ displayText: string;
+ }
+
+ export class BreadcrumbsMenu {
+ selectedKey: string;
+ menuItems: Array<BreadcrumbsMenuItem>;
+ }
+
+ export class BreadcrumbsPath {
+ selectedKeys: Array<string>;
+ }
+
+ export class VendorData {
+ breadcrumbs: BreadcrumbsPath;
+ }
+
+ export interface IOnboardVendorViewModelScope extends ng.IScope {
+ vendorData: VendorData;
+ onVendorEvent: Function;
+ topNavMenuModel: Array<Utils.MenuItemGroup>;
+ topNavRootMenu: Utils.MenuItemGroup;
+ user:Models.IUserProperties;
+ version:string;
+ }
+
+ export class OnboardVendorViewModel {
+ static '$inject' = [
+ '$scope',
+ '$q',
+ 'Sdc.Services.CacheService'
+ ];
+
+ private firstControlledTopNavMenu: Utils.MenuItemGroup;
+
+ constructor(
+ private $scope: IOnboardVendorViewModelScope,
+ private $q: ng.IQService,
+ private cacheService:Services.CacheService
+ ) {
+
+ this.$scope.vendorData = {
+ breadcrumbs: {
+ selectedKeys: []
+ }
+ };
+
+ this.$scope.version = this.cacheService.get('version');
+
+ this.$scope.onVendorEvent = (eventName:string, data:any): void => {
+ switch (eventName) {
+ case 'breadcrumbsupdated':
+ this.handleBreadcrumbsUpdate(data);
+ break;
+ }
+ };
+
+ this.$scope.topNavMenuModel = [];
+
+ this.$scope.user = this.cacheService.get('user');
+ }
+
+ updateBreadcrumbsPath = (selectedKeys: Array<string>): ng.IPromise<boolean> => {
+ let topNavMenuModel = this.$scope.topNavMenuModel;
+ let startIndex = topNavMenuModel.indexOf(this.firstControlledTopNavMenu);
+ if (startIndex === -1) {
+ startIndex = topNavMenuModel.length;
+ }
+ topNavMenuModel.splice(startIndex + selectedKeys.length);
+ this.$scope.vendorData = {
+ breadcrumbs: { selectedKeys: selectedKeys }
+ };
+
+ return this.$q.when(true);
+ };
+
+ handleBreadcrumbsUpdate(breadcrumbsMenus: Array<BreadcrumbsMenu>): void {
+ let selectedKeys = [];
+ let topNavMenus = breadcrumbsMenus.map((breadcrumbMenu, breadcrumbIndex) => {
+ let topNavMenu = new Utils.MenuItemGroup();
+ topNavMenu.menuItems = breadcrumbMenu.menuItems.map(menuItem =>
+ new Utils.MenuItem(
+ menuItem.displayText,
+ this.updateBreadcrumbsPath,
+ null,
+ null,
+ [selectedKeys.concat([menuItem.key])]
+ )
+ );
+ topNavMenu.selectedIndex = _.findIndex(
+ breadcrumbMenu.menuItems,
+ menuItem => menuItem.key === breadcrumbMenu.selectedKey
+ );
+ selectedKeys.push(breadcrumbMenu.selectedKey);
+ return topNavMenu;
+ });
+
+ let topNavMenuModel = this.$scope.topNavMenuModel;
+ let len = topNavMenuModel.length;
+ let startIndex = topNavMenuModel.indexOf(this.firstControlledTopNavMenu);
+ if (startIndex === -1) {
+ startIndex = len;
+ }
+ topNavMenuModel.splice(startIndex, len - startIndex);
+ topNavMenuModel.push.apply(topNavMenuModel, topNavMenus);
+ this.firstControlledTopNavMenu = topNavMenus[0];
+
+ if (startIndex === 1 && this.$scope.topNavRootMenu == null) {
+ let topNavRootMenu = topNavMenuModel[0];
+ let onboardItem = topNavRootMenu.menuItems[topNavRootMenu.selectedIndex];
+ let originalCallback = onboardItem.callback;
+ onboardItem.callback = (...args) => {
+ let ret = this.updateBreadcrumbsPath([]);
+ return originalCallback && originalCallback.apply(undefined, args) || ret;
+ };
+ this.$scope.topNavRootMenu = topNavRootMenu;
+ }
+
+ this.updateBreadcrumbsPath(selectedKeys);
+ }
+ }
+
+
+}
diff --git a/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view.html b/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view.html
new file mode 100644
index 0000000000..733e2d0cc0
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor-view.html
@@ -0,0 +1,14 @@
+<div class="sdc-catalog-container">
+
+ <loader data-display="gui.isLoading"></loader>
+<!--
+ <ecomp-header menu-data="menuItems" version="{{version}}"></ecomp-header>
+-->
+
+ <div class="w-sdc-main-container">
+ <punch-out name="'onboarding/vendor'" data="vendorData" user="user" on-event="onVendorEvent"></punch-out>
+ </div>
+
+ <top-nav top-lvl-selected-index="2" search-bind="search.filterTerm" menu-model="topNavMenuModel" version="{{version}}" hide-search="true"></top-nav>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor.less b/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor.less
new file mode 100644
index 0000000000..2b43bbb321
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/onboard-vendor/onboard-vendor.less
@@ -0,0 +1,303 @@
+.sdc-catalog-container {
+
+ .i-sdc-categories-list-item {
+ font-weight: normal;
+ }
+
+ // Checkboxes
+ .i-sdc-designer-leftbar-section-content-ul {
+ padding: 0;
+ margin: 0;
+
+ .i-sdc-catalog-subcategories-checkbox {
+ padding: 0 0 0 20px;
+ margin: 0;
+
+ .i-sdc-catalog-grouping-checkbox {
+ padding: 0 0 0 20px;
+ margin: 0;
+ }
+
+ }
+
+ }
+
+ .i-sdc-designer-leftbar-section-content-li {
+ &:last-child {
+ .i-sdc-categories-list-item {
+ margin: 0;
+ }
+ }
+ }
+
+ .i-sdc-categories-list-item {
+ display: block;
+ //margin-bottom: 5px;
+ //padding-left: 15px;
+ //text-indent: -24px;
+ vertical-align: top;
+ font-weight: bold;
+ }
+
+ .i-sdc-subcategories-list-item {
+ display: block;
+ //padding-left: 20px;
+ vertical-align: top;
+ font-weight: normal;
+ margin: 0;
+ //text-indent: -10px;
+ }
+
+ /*Added by - Ikram */
+ .i-sdc-product-input,
+ .i-sdc-product-select {
+ border: 1px solid @border_color_f;
+ min-height: 30px;
+ padding: 0;
+ width: 100%;
+ margin: 1px 0;
+ background-color: #F2F2F2;
+ outline: none;
+
+ &:disabled {
+ .disabled;
+ }
+ optgroup{
+ color: @color_u;
+ option{
+ color: @color_b;
+ }
+ }
+ }
+
+ .i-sdc-categories-list-item-icon {
+ display: inline-block;
+ float: right;
+ position: relative;
+ right: -8px;
+ top: 6px;
+ }
+
+ .i-sdc-categories-list-item {
+ margin-top: 7px;
+ &.NOT_CERTIFIED_CHECKOUT,
+ &.NOT_CERTIFIED_CHECKIN {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -2889px;
+ width: 14px;
+ height: 14px;
+
+ }
+ }
+
+ &.CERTIFIED {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -3034px;
+ width: 14px;
+ height: 16px;
+ }
+ }
+
+ &.READY_FOR_CERTIFICATION {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -2985px;
+ width: 14px;
+ height: 16px;
+ }
+ }
+
+ &.CERTIFICATION_IN_PROGRESS {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -53px -2934px;
+ width: 14px;
+ height: 16px;
+ }
+ }
+
+ &.DISTRIBUTED,
+ &.TBD {
+ .i-sdc-categories-list-item-icon {
+ background: url('../../../styles/images/sprites/sprite-global-old.png') no-repeat -43px -3087px;
+ width: 24px;
+ height: 14px;
+
+ }
+ }
+ }
+
+ .i-sdc-categories-list-input {
+ margin: 8px;
+
+ }
+
+ .i-sdc-subcategories-list-input {
+
+ margin: 8px;
+ }
+ .i-sdc-subcategories-list-input-container {
+ margin: 0px 0px 0px 20px;
+ padding: 2px;
+ }
+
+ .w-sdc-header-catalog-search-container {
+ display: table;
+ padding: 21px 0;
+ position: relative;
+
+ .w-sdc-designer-leftbar-search-input {
+ color: #000;
+ width: 300px;
+ }
+
+ // .magnification {
+ // .sprite;
+ // .sprite.magnification-glass;
+ // .hand;
+ // position: absolute;
+ // top: 40px;
+ // right: 42px;
+ // }
+ }
+
+ .w-sdc-catalog-main {
+ padding: 10px 12px;
+ }
+ .w-sdc-dashboard-catalog-header {
+ .b_9;
+ display: inline-block;
+ font-style: italic;
+ font-weight: bold;
+ padding-left: 10px;
+ }
+
+ .w-sdc-dashboard-catalog-header-order {
+ .b_9;
+ font-weight: 800;
+ }
+
+ .w-sdc-dashboard-catalog-sort {
+ .b_9;
+ font-weight: bold;
+ white-space:pre;
+ &:hover{
+ .hand;
+ text-decoration: none;
+ .a_9;
+ }
+ &.blue {
+ .a_9;
+ }
+ }
+
+ .w-sdc-catalog-sort-arrow{
+ display: inline-block;
+ &.up{
+ .b_9;
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid ;
+ }
+ &.down{
+ .b_9;
+ width: 0;
+ height: 0;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid;
+ }
+ }
+
+
+
+
+ .w-sdc-dashboard-catalog-header-right{
+ float: right;
+ display: inline-block;
+ padding-right:34px;
+ }
+
+ .w-sdc-header-catalog-search-input {
+ width: 420px;
+ display: table-cell;
+ padding: 0 25px 1px 10px;
+ border: 1px solid #bcbcbc;
+ .border-radius(10px);
+ height: 30px;
+ margin: 10px 30px;
+ outline: none;
+ }
+
+ .sdc-catalog-type-filter-container {
+ margin-top: -1px;
+ }
+
+ .i-sdc-designer-leftbar-section-title {
+ text-transform: uppercase;
+ .l_14_m;
+ line-height: 30px;
+ }
+
+ .i-sdc-designer-leftbar-section-title-icon {
+ .hand;
+ .tlv-sprite;
+ .footer-close;
+ transition: .3s all;
+ margin-top: -4px;
+ }
+
+ .i-sdc-designer-leftbar-section-title-text {
+ margin-left: 20px;
+ }
+
+ .seperator-left,
+ .seperator-right {
+ border-right: solid 1px @color_m;
+ display: table-cell;
+ width: 2px;
+ }
+
+ // Rotate catalog left side arrows
+ .i-sdc-designer-leftbar-section-title.expanded .i-sdc-designer-leftbar-section-title-icon {
+ transform: rotate(180deg);
+ }
+
+ // Transform catalog left side sections
+ .i-sdc-designer-leftbar-section-title + .i-sdc-designer-leftbar-section-content {
+ max-height: 0px;
+ margin: 0 auto;
+ transition: all .3s;
+ overflow: hidden;
+ padding: 0 10px 0 18px;
+ }
+
+ .i-sdc-designer-leftbar-section-title.expanded + .i-sdc-designer-leftbar-section-content {
+ max-height: 9999px;
+ margin: 0 auto 1px;
+ transition: all .3s;
+ padding: 10px 18px 10px 18px;
+ overflow: hidden;
+ }
+
+}
+
+.w-sdc-search-icon{
+ position: absolute;
+ right: 40px;
+ top: 40px;
+ &.leftbar{
+ top: 19px;
+ right: 18px;
+ }
+ &.magnification {
+ .sprite;
+ .sprite.magnification-glass;
+ .hand;
+ }
+ &.cancel {
+ .sprite;
+ .sprite.clear-text;
+ .hand;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/preloading/preloading-view.html b/catalog-ui/app/scripts/view-models/preloading/preloading-view.html
new file mode 100644
index 0000000000..c0512dd9ec
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/preloading/preloading-view.html
@@ -0,0 +1,9 @@
+<div class="sdc-loading-page">
+ <h1 class="caption1" translate="SIGN_IN_CAPTION"></h1>
+ <p class="caption2" translate="SIGN_IN_DESCRIPTION"></p>
+
+ <div class="load-container-wrapper">
+ <div class="load-container load2 animated fadeIn"><div class="loader">Loading...</div></div>
+ </div>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/preloading/preloading-view.less b/catalog-ui/app/scripts/view-models/preloading/preloading-view.less
new file mode 100644
index 0000000000..b02ea54621
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/preloading/preloading-view.less
@@ -0,0 +1,107 @@
+/*
+.sdc-loading-page {
+
+ background-color: @main_color_l;
+ width: 100%;
+ height: 100%;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+
+ h1 {
+ .c_5;
+ text-align: center;
+ }
+
+ p {
+ display: block;
+ .e_4;
+ }
+
+ .caption1, .caption2 {
+ visibility: hidden;
+ }
+
+ .load-container-wrapper {
+ position: relative;
+
+ .load-container {
+
+ @background_color: #000000;
+
+ .loader,
+ .loader:before,
+ .loader:after {
+ border-radius: 50%;
+ }
+ .loader:before,
+ .loader:after {
+ position: absolute;
+ content: '';
+ }
+ .loader:before {
+ width: 5.2em;
+ height: 10.2em;
+ background: @background_color;
+ border-radius: 10.2em 0 0 10.2em;
+ top: -0.1em;
+ left: -0.1em;
+ -webkit-transform-origin: 5.2em 5.1em;
+ transform-origin: 5.2em 5.1em;
+ -webkit-animation: load2 2s infinite ease 1.5s;
+ animation: load2 2s infinite ease 1.5s;
+ }
+ .loader {
+ color: #ffffff;
+ font-size: 11px;
+ text-indent: -99999em;
+ margin: 0 auto;
+ /!*margin: 55px auto;*!/
+ position: relative;
+ width: 10em;
+ height: 10em;
+ box-shadow: inset 0 0 0 1em;
+ -webkit-transform: translateZ(0);
+ -ms-transform: translateZ(0);
+ transform: translateZ(0);
+ }
+ .loader:after {
+ width: 5.2em;
+ height: 10.2em;
+ background: @background_color;
+ border-radius: 0 10.2em 10.2em 0;
+ top: -0.1em;
+ left: 5.1em;
+ -webkit-transform-origin: 0px 5.1em;
+ transform-origin: 0px 5.1em;
+ -webkit-animation: load2 2s infinite ease;
+ animation: load2 2s infinite ease;
+ }
+ @-webkit-keyframes load2 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+ @keyframes load2 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+ }
+ }
+
+ }
+
+}
+*/
diff --git a/catalog-ui/app/scripts/view-models/preloading/preloading-view.ts b/catalog-ui/app/scripts/view-models/preloading/preloading-view.ts
new file mode 100644
index 0000000000..7127b70e3c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/preloading/preloading-view.ts
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IPreLoadingViewScope {
+ startZoomIn: boolean;
+ }
+
+ export class PreLoadingViewModel {
+
+ static '$inject' = ['$scope'];
+ constructor(private $scope:IPreLoadingViewScope){
+ this.init($scope);
+ }
+
+ private init = ($scope:IPreLoadingViewScope):void => {
+ this.animate($('.caption1'),'fadeInUp',400);
+ this.animate($('.caption2'),'fadeInUp',800);
+ };
+
+ private animate = (element:any, animation:string, when:number):void => {
+ window.setTimeout(()=>{
+ element.addClass("animated " + animation);
+ element[0].style="visibility: visible;";
+ },when);
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/support/support-view-model.ts b/catalog-ui/app/scripts/view-models/support/support-view-model.ts
new file mode 100644
index 0000000000..2142cffdda
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/support/support-view-model.ts
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface ISupportViewModelScope {
+ version:string;
+ }
+
+ export class SupportViewModel{
+
+ static '$inject' = ['$scope','Sdc.Services.CacheService'];
+ constructor(private $scope:ISupportViewModelScope,
+ private cacheService:Services.CacheService){
+ this.$scope.version = this.cacheService.get('version');
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/support/support-view.html b/catalog-ui/app/scripts/view-models/support/support-view.html
new file mode 100644
index 0000000000..0e6d09ddd7
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/support/support-view.html
@@ -0,0 +1,33 @@
+<div class="full-height" >
+ <loader data-display="isLoading"></loader>
+ <div class="w-sdc-header">
+ <div class="w-sdc-header-logo">
+ <div class="w-sdc-header-logo-icon sprite logo"></div>
+ <a class="w-sdc-header-logo-link" data-ui-sref="dashboard">ASDC</a>
+ <div class="w-sdc-header-version"> v.{{version}}</div>
+ </div>
+ <div class="i-sdc-header-caption">Support</div>
+ <user-header-details ></user-header-details>
+ </div>
+ <div class="w-sdc-main-container">
+ <div class="w-sdc-left-sidebar">
+ <div class="w-sdc-left-sidebar-in-progress" >
+ <div class="i-sdc-left-sidebar-item category-title" data-ng-class="{'selectedLink':selectedLeftBarGroupLink === inProgressEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroup(inProgressEnumVal)" >In Design ({{numOfCheckOutEntities+numOfCheckInEntities}})</div>
+ <div class="i-sdc-left-sidebar-item" data-ng-class="{'selectedLink':selectedLeftBarStateLink===notCertifiedCheckOutEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroupAndEntityState(inProgressEnumVal, notCertifiedCheckOutEnumVal)" >Checked Out ({{numOfCheckOutEntities}})</div>
+ <div class="i-sdc-left-sidebar-item" data-ng-class="{'selectedLink':selectedLeftBarStateLink===notCertifiedCheckInEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroupAndEntityState(inProgressEnumVal, notCertifiedCheckInEnumVal)" >Checked In ({{numOfCheckInEntities}})</div>
+ </div>
+ <div class="w-sdc-left-sidebar-following" >
+ <div class="i-sdc-left-sidebar-item category-title" data-ng-class="{'selectedLink':selectedLeftBarGroupLink===followingEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroup(followingEnumVal)" >Completed Design ({{numOfReadyForCertificationEntities+numOfCertificationInProgressEntities+numOfCertifiedEntities}})</div>
+ <div class="i-sdc-left-sidebar-item" data-ng-class="{'selectedLink':selectedLeftBarStateLink===readyForCertificationEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroupAndEntityState(followingEnumVal, readyForCertificationEnumVal)" >Ready For Certification ({{numOfReadyForCertificationEntities}})</div>
+ <div class="i-sdc-left-sidebar-item" data-ng-class="{'selectedLink':selectedLeftBarStateLink===certificationInProgressEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroupAndEntityState(followingEnumVal, certificationInProgressEnumVal)" >Certification In Progress ({{numOfCertificationInProgressEntities}})</div>
+ <div class="i-sdc-left-sidebar-item" data-ng-class="{'selectedLink':selectedLeftBarStateLink===certifiedEnumVal}" data-ng-click="setSelectedEntitiesByEntityGroupAndEntityState(followingEnumVal, certifiedEnumVal)" >Certified ({{numOfCertifiedEntities}})</div>
+ </div>
+ <div class="w-sdc-left-sidebar-nav">
+ <div class="i-sdc-left-sidebar-nav-item catalog" data-ui-sref="catalog">Catalog</div>
+ <div class="i-sdc-left-sidebar-nav-item support" data-ui-sref="support">Support</div>
+ </div>
+ </div>
+
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/support/support.less b/catalog-ui/app/scripts/view-models/support/support.less
new file mode 100644
index 0000000000..8159e38320
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/support/support.less
@@ -0,0 +1,8 @@
+.w-sdc-left-sidebar-in-progress,
+.w-sdc-left-sidebar-following {
+ .b_7;
+}
+
+.w-sdc-left-sidebar-following {
+ padding: 13px 0;
+}
diff --git a/catalog-ui/app/scripts/view-models/tabs/general-tab.less b/catalog-ui/app/scripts/view-models/tabs/general-tab.less
new file mode 100644
index 0000000000..a8b4f5b9be
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tabs/general-tab.less
@@ -0,0 +1,131 @@
+.sdc-general-tab {
+
+ display: flex;
+ min-height: 100%;
+ flex-flow: column;
+
+ .sdc-edit-icon {
+ .sprite;
+ .e-sdc-small-icon-pencil;
+ }
+ .sdc-general-tab-title {
+
+ .f-color.a;
+ .f-type._14_m;
+ padding: 0px 0px 15px 0px;
+ margin: 0px 20px 0px 20px;
+ border-bottom: 1px solid @main_color_o;
+ }
+
+ .sdc-general-tab-sub-title {
+
+ .f-color.a;
+ .f-type._14_m;
+ padding: 15px 20px 15px 20px;
+
+ }
+
+ //scrollbar
+ .general-tab-scrollbar-container {
+
+ .perfect-scrollbar;
+ width: 100%;
+ }
+
+ //plus minus expand collapse
+ .general-tab-expand-collapse {
+
+ &.expanded {
+ .expand-collapse-title {
+ .expand-collapse-title-icon {
+ .expand-collapse-minus-icon;
+
+ &:hover {
+ .expand-collapse-minus-icon.hover;
+ }
+ }
+ }
+ }
+
+ .expand-collapse-title {
+
+ padding: 8px 20px 4px 20px;
+ cursor: pointer;
+ &:hover {
+ background-color: @main_color_o;
+ }
+
+ .expand-collapse-title-icon {
+ .hand;
+ .sprite-new;
+ .expand-collapse-plus-icon;
+ &:hover {
+ .expand-collapse-plus-icon.hover;
+ }
+
+ }
+ .expand-collapse-title-text {
+ max-width: 225px;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding-left: 10px;
+ line-height: 15px;
+ }
+ }
+ .selected {
+ background-color: @main_color_a;
+ .f-color.p;
+ }
+
+ }
+
+ .expand-collapse-sub-title {
+ max-width: 190px;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding-left: 43px;
+
+ }
+
+ //resizable view
+ .resizable-container {
+
+ flex: 1 1 auto;
+ display: flex;
+ flex-direction: column;
+ height: 90%;
+
+ .resizable-section {
+ min-height: 50px;
+ flex: 1;
+ display: flex;
+ flex-flow: column;
+ &.resizable {
+ flex: 0 0 300px;
+ }
+ }
+
+ //this is the resizable icon custom design for the angular resizable directive
+ .rg-top {
+ span {
+ margin-top: -5px;
+ &:before {
+ border-top: 1px dotted @main_color_m;
+ content: '';
+ display: inline-block;
+ width: 39px;
+ height: 6px;
+ }
+
+ border-top: 1px dotted @main_color_m;
+ border-bottom: 1px dotted @main_color_m;
+ width: 39px;
+ height: 4px;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view-model.ts b/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view-model.ts
new file mode 100644
index 0000000000..bd59199eb4
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view-model.ts
@@ -0,0 +1,99 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 7/28/2016.
+ */
+/**
+ * Created by obarda on 4/4/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import Module = Sdc.Models.Module;
+
+ export interface IHierarchyScope extends ng.IScope {
+ component:Models.Components.Component;
+ selectedIndex: number;
+ selectedModule:Models.DisplayModule;
+ singleTab:Models.Tab;
+ templateUrl:string;
+ isLoading:boolean;
+
+ onModuleSelected(moduleId:string, selectedIndex: number):void;
+ onModuleNameChanged(module:Models.DisplayModule):void;
+ updateHeatName():void;
+ }
+
+ export class HierarchyViewModel {
+
+ static '$inject' = [
+ '$scope'
+ ];
+
+ constructor(private $scope:IHierarchyScope) {
+ this.$scope.component = this.$scope.singleTab.data;
+ this.$scope.isLoading = false;
+ this.initScopeMethods();
+ }
+
+ private initScopeMethods():void {
+
+ this.$scope.templateUrl = '/app/scripts/view-models/tabs/hierarchy/edit-module-name-popover.html';
+ this.$scope.onModuleSelected = (moduleId:string, selectedIndex: number):void => {
+
+ let onSuccess = (module:Models.DisplayModule) => {
+ console.log("Module Loaded: ", module);
+ this.$scope.selectedModule = module;
+ this.$scope.isLoading = false;
+ };
+
+ let onFailed = () => {
+ this.$scope.isLoading = false;
+ };
+
+ this.$scope.selectedIndex = selectedIndex;
+ if( !this.$scope.selectedModule || (this.$scope.selectedModule && this.$scope.selectedModule.uniqueId != moduleId)) {
+ this.$scope.isLoading = true;
+ this.$scope.component.getModuleForDisplay(moduleId).then(onSuccess, onFailed);
+ }
+ };
+
+ this.$scope.updateHeatName = () => {
+ this.$scope.isLoading = true;
+
+ let originalName:string = this.$scope.selectedModule.name;
+
+ let onSuccess = (module:Models.Module) => {
+ console.log("Module name updated:", module.name);
+ this.$scope.selectedModule.name = module.name;
+ this.$scope.isLoading = false;
+ };
+
+ let onFailed = () => {
+ this.$scope.isLoading = false;
+ this.$scope.selectedModule.name = originalName;
+ };
+
+ this.$scope.selectedModule.updateName();
+ this.$scope.component.updateGroupMetadata(new Models.DisplayModule(this.$scope.selectedModule)).then(onSuccess, onFailed);
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view.html b/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view.html
new file mode 100644
index 0000000000..971105c191
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy-view.html
@@ -0,0 +1,57 @@
+<div class="sdc-general-tab hierarchy-tab" ng-class="">
+ <loader data-display="isLoading" relative="true" size="medium"></loader>
+ <div class="sdc-general-tab-title" data-tests-id="tab-header" translate="HIERARCHY_TAB_TITLE"></div>
+ <div class="sdc-general-tab-sub-title" data-tests-id="tab-sub-header">{{component.name}}</div>
+
+ <div class="resizable-container">
+ <div class="resizable-section">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true"
+ class="general-tab-scrollbar-container">
+
+ <expand-collapse expanded-selector=".hierarchy-module-member-list.{{$index}}"
+ class="general-tab-expand-collapse" is-close-on-init="true"
+ data-tests-id="hierarchy-module-{{$index}}"
+ data-ng-repeat-start="module in component.groups">
+ <div class="expand-collapse-title" data-tests-id="hierarchy-module-{{$index}}-title" ng-class="{'selected': selectedIndex === $index}" data-ng-click="onModuleSelected(module.uniqueId, $index)">
+ <div class="expand-collapse-title-icon"></div>
+ <span class="expand-collapse-title-text" data-ng-bind="module.name" tooltips
+ tooltip-content="{{module.name}}"></span>
+
+ </div>
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="hierarchy-module-member-list {{$index}}">
+ <div ng-repeat="(memberName, value) in ::module.members track by $index">
+ <div class="expand-collapse-sub-title" tooltips tooltip-content="{{memberName}}">{{memberName}}</div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ <div resizable r-directions="['top']" r-flex="true" ng-if="selectedModule" class="resizable-section module-data-container" data-tests-id="selected-module-data">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true"
+ class="general-tab-scrollbar-container">
+ <div class="module-data">
+ <div>
+ <div class="module-name module-text-overflow" data-tests-id="selected-module-name" tooltips tooltip-content="{{selectedModule.name}}">{{selectedModule.name}}</div>
+ <div class="edit-name-container">
+ <edit-name-popover header="Edit Module Name" direction="auto top" module="selectedModule" on-save="updateHeatName()" ng-class="{'disabled': isViewOnly}" class="sdc-edit-icon" data-tests-id="edit-name-popover-icon"></edit-name-popover>
+ </div>
+ </div>
+ <div class="module-text-overflow" data-tests-id="selected-module-group-uuid" tooltips tooltip-content="{{selectedModule.groupUUID}}"> Module ID: {{selectedModule.groupUUID}}</div>
+ <div class="module-text-overflow" data-tests-id="selected-module-is-base" tooltips tooltip-content="{{selectedModule.invariantUUID}}">Invariant UUID: {{selectedModule.invariantUUID}}</div>
+ <div data-tests-id="selected-module-version">Version: {{selectedModule.version}}</div>
+ <div data-tests-id="selected-module-is-base">IsBase: {{selectedModule.isBase}}</div>
+
+ </div>
+ <div ng-repeat="artifact in selectedModule.artifacts track by $index">
+ <div class="artifact-data">
+ <div class="artifact-name module-text-overflow" data-tests-id="selected-module-artifact-name" tooltips tooltip-content="{{artifact.artifactName}}">{{artifact.artifactName}}</div>
+ <div class="module-text-overflow" tooltips data-tests-id="selected-module-artifact-uuid" tooltip-content="{{artifact.artifactUUID}}"> UUID: {{artifact.artifactUUID}}</div>
+ <div data-tests-id="selected-module-artifact-version">Version: {{artifact.artifactVersion}}</div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy.less b/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy.less
new file mode 100644
index 0000000000..5e8572678d
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tabs/hierarchy/hierarchy.less
@@ -0,0 +1,71 @@
+.hierarchy-tab{
+
+ .hierarchy-module-list-container{
+ padding: 0px 20px 0px 20px;
+ }
+ .module-data-container{
+
+ width: 100%;
+ height: 100%;
+ background-color: #e6f6fb;
+ border: 1px solid #009fdb;
+ border-top: 4px solid #009fdb;
+ box-shadow: 0.3px 1px 2px rgba(24, 24, 25, 0.32);
+
+ .module-data {
+
+ .selectable;
+ .module-name {
+ .f-type._14_m;
+ width: 87%;
+ }
+ .f-type._14_r;
+ .f-color.a;
+ padding: 10px 0px 10px 0px;
+ margin: 0px 20px 0px 20px;
+ border-bottom: 1px solid rgba(0, 159, 219, 0.6);
+ }
+
+ .artifact-data{
+ .selectable;
+ .f-type._12_r;
+ .f-color.m;
+
+ padding: 10px 0px 10px 0px;
+ margin: 0px 20px 0px 20px;
+ border-bottom: 1px solid rgba(0, 159, 219, 0.6);
+ .artifact-name {
+ .f-type._14_r;
+ font-weight: bold;
+ }
+ }
+
+ .module-text-overflow {
+ max-width: 240px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ display: inline-block;
+ }
+ }
+
+
+ .hierarchy-module-member-list {
+ overflow: hidden;
+ }
+
+ .edit-name-container {
+ float: right;
+ border-left: 1px solid #5cc1e7;
+ height: 20px;
+ width: 12%;
+
+ .sdc-edit-icon {
+ float: right;
+ cursor: pointer;
+ position: relative;
+ top: 4px;
+ right: 5px;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.html b/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.html
new file mode 100644
index 0000000000..6e478fc471
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.html
@@ -0,0 +1,10 @@
+<div class="sdc-tutorial-end-page">
+ <perfect-scrollbar include-padding="true" class="sdc-tutorial-end-page-main">
+ <h2 translate="TUTORIAL_LAST_PAGE_TITLE"></h2>
+ <p class="sdc-tutorial-end-page-description1" translate="TUTORIAL_LAST_PAGE_TEXT"></p>
+ <div>
+ <button class="w-sdc-btn-blue" data-ui-sref="dashboard" type="button" translate="TUTORIAL_LAST_PAGE_BTN_ACTION_MY_DASHBOARD"></button>
+ <button class="w-sdc-btn-blue" data-ui-sref="catalog" type="button" translate="WELCOME_BTN_ACTION_CATALOG"></button>
+ </div>
+ </perfect-scrollbar>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.less b/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.less
new file mode 100644
index 0000000000..71648a4f86
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.less
@@ -0,0 +1,41 @@
+.sdc-tutorial-end-page {
+
+ .bg_s;
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 999;
+ .opacity(0.8);
+ display: flex;
+ align-items: center;
+
+ background-image: url('../../../styles/images/welcome.png');
+ background-repeat: no-repeat;
+ background-position: bottom left;
+
+ .sdc-tutorial-end-page-main {
+ width: 600px;
+ height: 400px;
+ margin: 100px auto 100px auto;
+ padding: 80px;
+ }
+
+ h2 {
+ .t_15;
+ margin: 0;
+ }
+
+ .sdc-tutorial-end-page-description1 {
+ .c_2;
+ .opacity(0.8);
+ margin-top: 10px;
+ line-height: 22px;
+ }
+
+ .w-sdc-btn-blue {
+ margin: 40px 10px 0 0;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.ts b/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.ts
new file mode 100644
index 0000000000..411d3f8d24
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/tutorial-end/tutorial-end.ts
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface ITutorialEndViewModelScope extends ng.IScope {}
+
+ export class TutorialEndViewModel {
+
+ static '$inject' = [
+ '$scope'
+ ];
+ constructor(
+ private $scope:ITutorialEndViewModelScope
+ ){
+ this.init();
+ }
+
+ private init = ():void => {
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide0.html b/catalog-ui/app/scripts/view-models/welcome/slide0.html
new file mode 100644
index 0000000000..48d37215a4
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide0.html
@@ -0,0 +1,50 @@
+<div class="slide" id="slide-0" data-ng-controller="Sdc.ViewModels.WelcomeStepsControllerViewModel">
+ <div class="asdc-welcome-frame frame-0">
+ <div class="asdc-welcome-header">
+ <a class="sprite-welcome logo" class="welcome-logo" ui-sref="dashboard"></a>
+ </div>
+
+ <div class="asdc-welcome-cover"></div>
+ <div class="asdc-whats-new">
+
+ <div class="news-items-row">
+ <div class="news-item-wrapper bg-1">
+ <div class="news-title" translate="WHATS_NEW_1_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_1_BODY"></div>
+ </div>
+ <div class="news-item-wrapper bg-2">
+ <div class="news-title" translate="WHATS_NEW_2_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_2_BODY"></div>
+ </div>
+ <div class="news-item-wrapper bg-3">
+ <div class="news-title" translate="WHATS_NEW_3_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_3_BODY"></div>
+ </div>
+ <div class="news-item-wrapper bg-4">
+ <div class="news-title" translate="WHATS_NEW_4_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_4_BODY"></div>
+ </div>
+ </div>
+
+ <div class="news-items-row">
+ <div class="news-item-wrapper bg-5">
+ <div class="news-title" translate="WHATS_NEW_5_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_5_BODY"></div>
+ </div>
+ <div class="news-item-wrapper bg-6">
+ <div class="news-title" translate="WHATS_NEW_6_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_6_BODY"></div>
+ </div>
+ <div class="news-item-wrapper bg-7">
+ <div class="news-title" translate="WHATS_NEW_7_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_7_BODY"></div>
+ </div>
+ <div class="news-item-wrapper bg-8">
+ <div class="news-title" translate="WHATS_NEW_8_TITLE"></div>
+ <div class="news-body" translate="WHATS_NEW_8_BODY"></div>
+ </div>
+ </div>
+
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide1.html b/catalog-ui/app/scripts/view-models/welcome/slide1.html
new file mode 100644
index 0000000000..9252026a6b
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide1.html
@@ -0,0 +1,34 @@
+<div class="slide" id="slide-1" data-ng-controller="Sdc.ViewModels.WelcomeStepsControllerViewModel">
+ <div class="asdc-welcome-frame frame-01">
+ <div class="asdc-welcome-header">
+ <!--<a class="sprite-welcome logo" class="welcome-logo" ui-sref="dashboard"></a>-->
+ <!--<a class="whats-new" data-ng-click="gotoSlideIndex(0)">What`s New</a>-->
+ </div>
+
+ <!--<video id="asdc-welcome-video" class="asdc-welcome-video">&lt;!&ndash; autoplay loop muted &ndash;&gt;
+ <source ng-src="{{video_mp4}}" type="video/mp4" />
+ <source ng-src="{{video_ogg}}" type='video/ogg' />
+ </video>-->
+
+ <div class="asdc-welcome-cover"></div>
+ <div class="asdc-welcome-main">
+
+ <!--<div class="asdc-welcome-main-title">INNOVATIVE. RAPID. RELIABLE</div>
+ <div class="asdc-welcome-main-message">
+ AT&Ts leading collaborative network solution design platform
+ </div>
+
+ <div class="asdc-welcome-main-back-btn-ph">
+ <a class="asdc-welcome-main-back-btn" ui-sref="dashboard">Home</a>
+ </div>
+
+ <div class="asdc-welcome-video-icon">
+ <div class="asdc-welcome-video-icon-play sprite-welcome play" data-ng-click="onPlayVideo()"></div>
+ <div class="asdc-welcome-inner-circle"></div>
+ </div>-->
+
+ <h1>Welcome to SDC</h1>
+
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide2.html b/catalog-ui/app/scripts/view-models/welcome/slide2.html
new file mode 100644
index 0000000000..4329bf462d
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide2.html
@@ -0,0 +1,26 @@
+<div class="slide" id="slide-2">
+ <div class="asdc-welcome-frame frame-02">
+ <div class="asdc-welcome-slide-text-box">
+ <div class="asdc-welcome-slide-text-box-title">
+ Innovate <br>
+ network- design <br>
+ platform <br>
+ </div>
+ <div class="asdc-welcome-slide-text-box-content">
+ <p>
+ Adapt swiftly to the constant demands placed on <br> networks by ongoing technological advances.
+ </p>
+ <p>
+ Using ASDC’s innovative network-design platform,<br> quickly and easily create and share software
+ <br>
+ components.
+ </p>
+ </div>
+ </div>
+ <div class="asdc-welcome-slide-image-box">
+ <img src="styles/images/welcome/ss-01.png" alt="01" class="asdc-welcome-slide-image">
+ </div>
+ <div class="asdc-welcome-frame-shape"></div>
+ <div class="asdc-welcome-frame-connection"></div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide3.html b/catalog-ui/app/scripts/view-models/welcome/slide3.html
new file mode 100644
index 0000000000..dd5448beac
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide3.html
@@ -0,0 +1,27 @@
+<div class="slide" id="slide-3">
+ <div class="asdc-welcome-frame frame-03">
+
+ <div class="asdc-welcome-slide-text-box">
+ <div class="asdc-welcome-slide-text-box-title">
+ Enhance <br>
+ and extend
+ </div>
+ <div class="asdc-welcome-slide-text-box-content">
+ <p>
+ Blend, build and arrange resources and services in the <br> designer workspace.
+ </p>
+ <p>
+ Let your creativity lead the way to any number of <br> network solutions.
+ </p>
+ <p>
+ Then simply click on elements to customize and refine <br> their specific properties to match your needs.
+ </p>
+ </div>
+ </div>
+ <div class="asdc-welcome-slide-image-box">
+ <img src="styles/images/welcome/ss-02.png" alt="02" class="asdc-welcome-slide-image">
+ </div>
+ <div class="asdc-welcome-frame-shape"></div>
+ <div class="asdc-welcome-frame-connection"></div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide4.html b/catalog-ui/app/scripts/view-models/welcome/slide4.html
new file mode 100644
index 0000000000..1428ce5375
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide4.html
@@ -0,0 +1,29 @@
+<div class="slide" id="slide-4">
+ <div class="asdc-welcome-frame frame-04">
+ <div class="asdc-welcome-slide-text-box">
+ <div class="asdc-welcome-slide-text-box-title">
+ Share <br>
+ and Collaborate
+ </div>
+ <div class="asdc-welcome-slide-text-box-content">
+ <p>
+ Graphically arranged, the Catalog is an easily <br> searchable collection of resources, services and
+ <br> products that provides you with a variety of <br> network elements.
+ </p>
+ <p>
+ Benefit from these assets by using them as <br> building blocks to form any number of network
+ <br> solutions.
+ </p>
+ <p>
+ After being certified for release, share and <br> collaborate with others as your solution is
+ <br> automatically included in the catalog.
+ </p>
+ </div>
+ </div>
+ <div class="asdc-welcome-slide-image-box">
+ <img src="styles/images/welcome/ss-03.png" alt="03" class="asdc-welcome-slide-image">
+ </div>
+ <div class="asdc-welcome-frame-shape"></div>
+ <div class="asdc-welcome-frame-connection"></div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide5.html b/catalog-ui/app/scripts/view-models/welcome/slide5.html
new file mode 100644
index 0000000000..913573c8fc
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide5.html
@@ -0,0 +1,27 @@
+<div class="slide" id="slide-5">
+ <div class="asdc-welcome-frame frame-05">
+ <div class="asdc-welcome-slide-text-box">
+ <div class="asdc-welcome-slide-text-box-title">
+ Fast, efficient <br>
+ and reliable
+ </div>
+ <div class="asdc-welcome-slide-text-box-content">
+ <p>
+ ASDC is a platform built around a simple error-free
+ <br>process for resource and service design and distribution.
+ <br>
+ <br>An integrated certification process makes ASDC a safe
+ <br>and reliable environment to experiment, review and test
+ <br>your work.
+ <br>
+ <br>Once approved your solution is ready to be used in
+ <br>the real-world.
+ </p>
+
+ </div>
+
+ </div>
+ <div class="asdc-welcome-frame-shape"></div>
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/slide6.html b/catalog-ui/app/scripts/view-models/welcome/slide6.html
new file mode 100644
index 0000000000..22006f7f82
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/slide6.html
@@ -0,0 +1,26 @@
+<div class="slide" id="slide-6">
+ <div class="asdc-welcome-frame frame-06">
+
+ <div class="asdc-welcome-cover"></div>
+ <div class="asdc-welcome-main">
+
+ <div class="asdc-welcome-main-title">ASDC</div>
+ <div class="asdc-welcome-main-message">
+ jump starting your network solutions.
+ </div>
+ <div class="asdc-welcome-main-back-btn-ph">
+ <a class="asdc-welcome-main-back-btn" ui-sref="dashboard">Home</a>
+ </div>
+ </div>
+
+ <div class="asdc-welcome-footer">
+ © 2016 AT&T Intellectual Property.© 2016 AT&T Intellectual Property. link. This link will open a new
+ window This link will open a new window All rights reserved.
+ <br/>
+ AT&T, Globe logo, Mobilizing Your World and DIRECTV are registered trademarks of AT&T Intellectual
+ Property and/or AT&T affiliated companies. All other marks are the property of their respective owners.
+
+ </div>
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/welcome-steps-controller.ts b/catalog-ui/app/scripts/view-models/welcome/welcome-steps-controller.ts
new file mode 100644
index 0000000000..816afcf2d2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/welcome-steps-controller.ts
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IWelcomeStepsController {
+ video_mp4: string;
+ video_ogg: string;
+ onPlayVideo: Function;
+ }
+
+ export class WelcomeStepsControllerViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$sce',
+ 'sdcConfig',
+ '$state',
+ '$filter'
+ ];
+
+ constructor(
+ private $scope:IWelcomeStepsController,
+ private $sce:any,
+ private sdcConfig: Models.IAppConfigurtaion,
+ private $state:ng.ui.IStateService,
+ private $filter:ng.IFilterService
+ ){
+ this.init();
+ this.initScope();
+ }
+
+ private init = ():void => {
+
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.onPlayVideo = ():void => {
+ //console.log("onPlayVideo");
+ $("#sdc-page-scroller").removeClass("animated fadeIn");
+ $("#sdc-page-scroller").addClass("animated fadeOut");
+ window.setTimeout(()=>{$("#sdc-page-scroller").css("display","none");},500);
+
+ $("#sdc-welcome-video-wrapper").removeClass("animated fadeOut");
+ $("#sdc-welcome-video-wrapper").addClass("animated fadeIn");
+ window.setTimeout(()=>{$("#sdc-welcome-video-wrapper").css("display","block");},0);
+
+ let videoElement:any = $("#asdc-welcome-video")[0];
+ videoElement.play();
+ };
+
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/welcome/welcome-view.html b/catalog-ui/app/scripts/view-models/welcome/welcome-view.html
new file mode 100644
index 0000000000..ba41e88a4e
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/welcome-view.html
@@ -0,0 +1,22 @@
+<div class="sdc-welcome-new-page">
+
+ <!--<div id="sdc-welcome-video-wrapper">
+ <div class="asdc-welcome-video-close sprite-welcome close" data-ng-click="onCloseVideoButton()"></div>
+ <video id="asdc-welcome-video" class="asdc-welcome-video">
+ <source ng-src="{{video_mp4}}" type="video/mp4" />
+ <source ng-src="{{video_ogg}}" type='video/ogg' />
+ </video>
+ </div>-->
+
+ <div class="os-welcome">Welcome to SDC</div>
+
+ <!--<sdc-page-scroll id="sdc-page-scroller"
+ start-slide-index="1"
+ slides-data="slides"
+ show-nav="true"
+ show-close-button="true"
+ close-button-callback="onCloseButton">
+
+ </sdc-page-scroll>
+-->
+</div>
diff --git a/catalog-ui/app/scripts/view-models/welcome/welcome-view.ts b/catalog-ui/app/scripts/view-models/welcome/welcome-view.ts
new file mode 100644
index 0000000000..0a0c923481
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/welcome/welcome-view.ts
@@ -0,0 +1,267 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IWelcomeViewMode {
+ slides:Array<Sdc.Directives.SlideData>;
+ onCloseButton():void;
+ onCloseVideoButton():void;
+
+ video_mp4: string;
+ video_ogg: string;
+ }
+
+ export class WelcomeViewModel {
+
+ firstLoad:boolean = true;
+ alreadyAnimated:Array<number> = [];
+
+ static '$inject' = [
+ '$scope',
+ '$sce',
+ 'sdcConfig',
+ '$state',
+ '$filter'
+ ];
+
+ constructor(
+ private $scope:IWelcomeViewMode,
+ private $sce:any,
+ private sdcConfig: Models.IAppConfigurtaion,
+ private $state:ng.ui.IStateService,
+ private $filter:ng.IFilterService
+ ){
+ /*this.init();
+ this.initScope();
+ window.setTimeout(():void => {
+ this.loadImages(():void=> {
+ window.setTimeout(():void =>{
+ $(".sdc-welcome-new-page").addClass("animated fadeIn");
+ this.animateGeneral();
+ this.animate1();
+ },1000);
+ });
+ },0);*/
+ }
+
+ private initScope = ():void => {
+
+ this.$scope.onCloseButton = ():void => {
+ //console.log("onCloseButton");
+ this.$state.go("dashboard", {});
+ };
+
+ this.$scope.onCloseVideoButton = ():void => {
+ //console.log("onCloseVideoButton");
+ $("#sdc-page-scroller").removeClass("animated fadeOut");
+ $("#sdc-page-scroller").addClass("animated fadeIn");
+ window.setTimeout(()=>{$("#sdc-page-scroller").css("display","block");},0);
+
+ $("#sdc-welcome-video-wrapper").removeClass("animated fadeIn");
+ $("#sdc-welcome-video-wrapper").addClass("animated fadeOut");
+ window.setTimeout(()=>{$("#sdc-welcome-video-wrapper").css("display","none");},500);
+
+ let videoElement:any = $("#asdc-welcome-video")[0];
+ videoElement.pause();
+ };
+
+ let url: string = this.sdcConfig.api.welcome_page_video_url;
+
+ this.$scope.video_mp4 = this.$sce.trustAsResourceUrl(url + ".mp4");
+ this.$scope.video_ogg = this.$sce.trustAsResourceUrl(url + ".ogg");
+
+ };
+
+ private init = ():void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+ this.$scope.slides = [
+ {"url": viewModelsHtmlBasePath + 'welcome/slide0.html', "id": "slide-0", "index": 0, "callback": () => {this.animate0();}},
+ {"url": viewModelsHtmlBasePath + 'welcome/slide1.html', "id": "slide-1", "index": 1, "callback": () => {}},
+ {"url": viewModelsHtmlBasePath + 'welcome/slide2.html', "id": "slide-2", "index": 2, "callback": () => {this.animate2();}},
+ {"url": viewModelsHtmlBasePath + 'welcome/slide3.html', "id": "slide-3", "index": 3, "callback": () => {this.animate3();}},
+ {"url": viewModelsHtmlBasePath + 'welcome/slide4.html', "id": "slide-4", "index": 4, "callback": () => {this.animate4();}},
+ {"url": viewModelsHtmlBasePath + 'welcome/slide5.html', "id": "slide-5", "index": 5, "callback": () => {this.animate5();}},
+ {"url": viewModelsHtmlBasePath + 'welcome/slide6.html', "id": "slide-6", "index": 6, "callback": () => {this.animate6();}}
+ ];
+
+ $('body').keyup((e):void=> {
+ if (e.keyCode == 27) { // escape key maps to keycode `27`
+ this.$state.go('dashboard');
+ }
+ });
+ };
+
+ private animateGeneral = ():void => {
+ //console.log("animateGeneral");
+
+ /*// Animate the right navigation
+ if (this.firstLoad===true) {
+ //TODO: Israel
+ //TweenLite.from('.page-nav', 2, {x: "100px", delay: 2});
+ }
+ */
+
+ this.firstLoad = false;
+ };
+
+ /*private loadImages = (callback: Function):void => {
+ let src = $('#slide-1 .asdc-welcome-frame').css('background-image');
+ let url = src.match(/\((.*?)\)/)[1].replace(/('|")/g,'');
+
+ let img = new Image();
+ img.onload = function() {
+ callback();
+ //alert('image loaded');
+ };
+ img.src = url;
+ /!*if (img.complete){
+ callback;
+ }*!/
+ };*/
+
+ private animate = (element:any, animation:string, when:number):void => {
+ window.setTimeout(()=>{
+ element.addClass("animated " + animation);
+ if (element[0]) {
+ element[0].style = "visibility: visible;";
+ }
+ },when);
+ };
+
+ private hide = (element:any, animation:string, animationToHide:string, when:number):void => {
+ element.addClass("animated " + animation);
+ element[0].style="visibility: hidden;";
+ };
+
+ private animate0 = ():void => {
+ if (this.alreadyAnimated.indexOf(0)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(0);
+ }
+ //console.log("slide 0 - animate");
+ this.animate($('#slide-0 .bg-1'),'fadeInDown',500);
+ this.animate($('#slide-0 .bg-2'),'fadeInDown',1000);
+ this.animate($('#slide-0 .bg-3'),'fadeInDown',1500);
+ this.animate($('#slide-0 .bg-4'),'fadeInDown',2000);
+
+ this.animate($('#slide-0 .bg-5'),'fadeInDown',2500);
+ this.animate($('#slide-0 .bg-6'),'fadeInDown',3000);
+ this.animate($('#slide-0 .bg-7'),'fadeInDown',3500);
+ this.animate($('#slide-0 .bg-8'),'fadeInDown',4000);
+ };
+
+ private animate1 = ():void => {
+ if (this.alreadyAnimated.indexOf(1)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(1);
+ }
+ //console.log("slide 1 - animate");
+
+ this.animate($('#slide-1 .asdc-welcome-main-title'),'fadeInUp',1000);
+ this.animate($('#slide-1 .asdc-welcome-main-message'),'fadeInUp',2000);
+
+ this.animate($('#slide-1 .asdc-welcome-main-back-btn'),'fadeIn',3000);
+
+ this.animate($('#slide-1 .asdc-welcome-video-icon'),'zoomIn',3000);
+ this.animate($('#slide-1 .asdc-welcome-inner-circle'),'zoomIn',3000);
+
+ this.animate($('.welcome-nav'),'slideInRight',2000);
+ };
+
+ private animate2 = ():void => {
+ if (this.alreadyAnimated.indexOf(2)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(2);
+ }
+ //console.log("slide 2 - animate");
+ this.animate($('#slide-2 .asdc-welcome-frame-shape'),'zoomIn',500);
+ this.animate($('#slide-2 .asdc-welcome-slide-text-box-content'),'fadeInUp',2000);
+ this.animate($('#slide-2 .asdc-welcome-slide-text-box-title'),'fadeInUp',1000);
+ };
+
+ private animate3 = ():void => {
+ if (this.alreadyAnimated.indexOf(3)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(3);
+ }
+ //console.log("slide 3 - animate");
+ this.animate($('#slide-3 .asdc-welcome-frame-shape'),'zoomIn',500);
+ this.animate($('#slide-3 .asdc-welcome-slide-text-box-content'),'fadeInUp',2000);
+ this.animate($('#slide-3 .asdc-welcome-slide-text-box-title'),'fadeInUp',1000);
+ };
+
+ private animate4 = ():void => {
+ if (this.alreadyAnimated.indexOf(4)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(4);
+ }
+ //console.log("slide 4 - animate");
+ this.animate($('#slide-4 .asdc-welcome-frame-shape'),'zoomIn',500);
+ this.animate($('#slide-4 .asdc-welcome-slide-text-box-content'),'fadeInUp',2000);
+ this.animate($('#slide-4 .asdc-welcome-slide-text-box-title'),'fadeInUp',1000);
+ };
+
+ private animate5 = ():void => {
+ if (this.alreadyAnimated.indexOf(5)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(5);
+ }
+ //console.log("slide 5 - animate");
+ this.animate($('#slide-5 .asdc-welcome-frame-shape'),'zoomIn',500);
+ this.animate($('#slide-5 .asdc-welcome-slide-text-box-content'),'fadeInUp',2000);
+ this.animate($('#slide-5 .asdc-welcome-slide-text-box-title'),'fadeInUp',1000);
+ };
+
+ private animate6 = ():void => {
+ if (this.alreadyAnimated.indexOf(6)!==-1){
+ return;
+ } else {
+ this.alreadyAnimated.push(6);
+ }
+ //console.log("slide 6 - animate");
+ this.animate($('#slide-6 .asdc-welcome-main-message'),'fadeInUp',2000);
+ this.animate($('#slide-6 .asdc-welcome-main-title'),'fadeInUp',1000);
+ this.animate($('#slide-6 .asdc-welcome-main-back-btn'),'fadeInUp',3000);
+ };
+
+ private animateCss = (element:JQuery, animationName:string):void => {
+ let animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
+ element.addClass('animated ' + animationName).one(animationEnd, function() {
+ element.removeClass('animated ' + animationName);
+ });
+ };
+
+ private unAnimateCss = (element:JQuery, animationName:string):void => {
+ let animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
+ element.addClass('animated ' + animationName).one(animationEnd, function() {
+ element.removeClass('animated ' + animationName);
+ });
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/ReadMe.txt b/catalog-ui/app/scripts/view-models/wizard/ReadMe.txt
new file mode 100644
index 0000000000..ca5cfb988a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/ReadMe.txt
@@ -0,0 +1,54 @@
+import-asset wizard
+========================================================
+
+What should be done in each wizard step:
+----------------------------------------
+
+1. Each step scope should extend IAssetCreationStepScope (this way you can call methods on the wizard scope).
+ Example:
+ export interface IGeneralStepScope extends IAssetCreationStepScope {
+
+ }
+
+2. Each step class should implements IAssetCreationStep
+ Example: export class GeneralStepViewModel implements IAssetCreationStep {
+
+ }
+
+3. Add the method: public save = (callback:Function):void => {}
+ The method should perform the save and call: callback(true); in case of success, or callback(false); in case of error.
+ Example:
+ var onSuccess:Function = (resourceProperties:Services.IResourceResource) => {
+ this.$scope.setAngularResourceOfResource(resourceProperties);
+ var resourceObj = new Sdc.Models.Resource(resourceProperties);
+ this.$scope.setEntity(resourceObj);
+ this.$scope.latestEntityName = (resourceProperties.resourceName);
+ callback(true);
+ };
+
+4. Add the first line after the constructor: this.$scope.registerChild(this);
+ This will register the current step reference in the wizard.
+
+5. Each step can get and set angular $resource of resource from the wizard.
+ // Will be called from each step to get current entity.
+ this.$scope.getAngularResourceOfResource = ():Services.IResourceResource => {
+ return this.resourceProperties;
+ };
+
+ // Will be called from each step after save to update the resource.
+ this.$scope.setAngularResourceOfResource = (resourceProperties:Services.IResourceResource):void => {
+ this.resourceProperties = resourceProperties;
+ this.fillAssetNameAndType();
+ };
+
+ Note: after success save, set setAngularResourceOfResource in the wizard (see example in step 3).
+
+6. The wizard needs to know if the step is valid (to know if to show next button), I used the following to update the wizard:
+ this.$scope.$watch("editForm.$valid", function(newVal, oldVal){
+ this.$scope.setValidState(newVal);
+ });
+
+ Note: in case there is no save for the step, and the step is always valid, call: this.$scope.setValidState(true);
+
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html
new file mode 100644
index 0000000000..97817d59f2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html
@@ -0,0 +1,137 @@
+<div class="artifact-deployment-step" data-ng-controller="Sdc.ViewModels.Wizard.ArtifactDeploymentStepViewModel">
+
+ <div data-tests-id="add-deployment-artifact-button" data-tests-id="addGrey" class="w-sdc-classic-btn gray" data-ng-click="addOrUpdate({})">Add</div>
+
+ <div class="table-container-flex">
+
+ <div class="table">
+
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+
+ <form class="body" name="editForm">
+
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+
+ <!-- Artifact row -->
+ <div ng-if="noArtifactsToShow()" class="no-row-text" translate="DEPLOYMENT_ARTIFACT_NO_ARTIFACTS_TO_DISPLAY"></div>
+ <div data-ng-repeat-start="artifact in artifacts | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected || undefined==artifact.selected && updateInProgress}"
+ data-ng-if="artifact.esId">
+
+ <div class="table-col-general flex-item" data-ng-click="update(artifact)">
+ <loader data-display="isLoading"></loader>
+ <span class="sprite table-arrow" data-tests-id="{{artifact.artifactDisplayName}}" data-ng-class="{'opened': artifact.selected || undefined==artifact.selected && updateInProgress}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+ <div class="table-col-general flex-item" data-tests-id="{{artifact.timeout}}">
+ {{artifact.timeout? artifact.timeout:''}}
+ </div>
+
+ <div class="table-btn-col flex-item" sdc-keyboard-events>
+ <button class="table-edit-btn" data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="table-delete-btn" data-ng-click="delete(artifact)"> </button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected || undefined==artifact.selected && updateInProgress" class="w-sdc-form item-opened">
+ <!-- Artifact panel opened -->
+
+ <!-- Description field -->
+ <div class="w-sdc-form-item" ng-form="descriptionForm" data-ng-class="{error:(descriptionForm.$dirty && descriptionForm.$invalid)}">
+ <label class="i-sdc-env-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea {{$index}}"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-ng-required="true"
+ name="description"
+ data-ng-model="artifact.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="getValidationPattern('string')"
+ data-tests-id="description">
+ </textarea>
+
+ <div class="input-error" data-ng-show="descriptionForm.$dirty && descriptionForm.$invalid">
+ <span ng-show="descriptionForm.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="descriptionForm.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="descriptionForm.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!-- Parameters in 2 columns -->
+ <div class="w-sdc-form-columns-wrapper" data-ng-if="artifact.heatParameters">
+ <!-- Left column -->
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item" ng-form="parameterForm" data-ng-repeat="parameter in artifact.heatParameters.slice(0, artifact.heatParameters.length%2+artifact.heatParameters.length/2) | orderBy: 'name' track by $index">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}" tooltip-side="top" sdc-smart-tooltip>{{parameter.name +' (' + parameter.type + ')'}}</label>
+ <span class="parameter-description" tooltips tooltip-side="top" tooltip-content="{{parameter.description}}">?</span>
+ <input class="i-sdc-form-input" data-ng-class="{error:(parameterForm.currentValue.$invalid)}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && parameterForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="!parameterForm.currentValue.$error.pattern && resetValue(parameter)"
+ />
+
+ <div class="input-error" data-ng-show="parameterForm.currentValue.$invalid">
+ <span ng-show="parameterForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+ </div>
+
+ <!-- Right column -->
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item" ng-form="parameterForm" data-ng-repeat="parameter in artifact.heatParameters.slice(artifact.heatParameters.length%2+artifact.heatParameters.length/2) | orderBy: 'name' track by $index">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}" tooltip-side="top" sdc-smart-tooltip>{{parameter.name +' (' + parameter.type + ')'}}</label>
+ <span class="parameter-description" tooltips tooltip-side="top" tooltip-content="{{parameter.description}}">?</span>
+ <input class="i-sdc-form-input" data-ng-class="{error:(parameterForm.currentValue.$invalid)}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && parameterForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="!parameterForm.currentValue.$error.pattern && resetValue(parameter)"
+ />
+
+ <div class="input-error" data-ng-show="parameterForm.currentValue.$invalid">
+ <span ng-show="parameterForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+ </div>
+
+ </div><!-- Close: Parameters in 2 columns -->
+ </div><!-- Close: Artifact panel opened -->
+
+ <!-- Add artifacts buttons -->
+ <button class="add-button" data-ng-repeat="artifact in artifacts track by $index"
+ data-ng-show="!artifact.esId"
+ data-tests-id="{{artifact.artifactDisplayName}}"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT"
+ translate-values="{'name': '{{artifact.artifactDisplayName}}'}"
+ data-ng-click="addOrUpdate(artifact)"></button>
+
+ <!-- Top add button -->
+ <button class="add-button" translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER" data-ng-click="addOrUpdate({})"></button>
+ </perfect-scrollbar>
+ </form>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less
new file mode 100644
index 0000000000..043fba3277
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less
@@ -0,0 +1,107 @@
+.artifact-deployment-step {
+
+ .table-container-flex .table .body .data-row + div.item-opened {
+ padding: 10px 40px 10px 30px;
+ }
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table {
+ height:412px;
+ margin-bottom: 0;
+ }
+
+ .parameter-description {
+ .circle(18px, @color_p);
+ content: '?';
+ line-height: 18px;
+ vertical-align: middle;
+ margin-left: 5px;
+ cursor: default;
+ display: inline-block;
+ position: absolute;
+ top: 16px;
+ }
+
+ .table-container-flex {
+
+ margin-top: 0px;
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+ padding: 5px 4px;
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ padding: 5px 4px;
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ padding: 5px 4px;
+ flex-grow: 9;
+ }
+
+ .flex-item:nth-child(4) {
+ padding: 5px 4px;
+ flex-grow: 3;
+ padding-top: 10px;
+ }
+ }
+ .w-sdc-form{
+ text-align: left;
+
+ .w-sdc-env-params{
+ border-top: 1px solid #cdcdcd;
+ margin: 25px 0 10px 0;
+ }
+
+ .i-sdc-form-textarea {
+ border: 1px solid @color_e;
+ min-height: 60px;
+ padding: 10px 13px;
+ width: 100%;
+ resize: none;
+
+ &:disabled {
+ .disabled;
+ }
+ }
+
+ .w-sdc-form-item {
+ &.error {
+ .i-sdc-form-input,
+ .i-sdc-form-select,
+ .i-sdc-form-textarea {
+ border-color: @color_h;
+ outline: none;
+ box-sizing: border-box;
+ }
+ }
+ }
+
+ .i-sdc-env-form-label{
+ .p_9;
+ overflow: hidden;
+ max-width: 245px;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ margin-top: 14px;
+
+ &.required::before {
+ color: #f33;
+ content: '*';
+ margin-right: 4px;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts
new file mode 100644
index 0000000000..80f145b9b1
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.ts
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ interface IArtifactDeploymentStepViewModelScope extends IWizardCreationStepScope {
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+ component: Models.Components.Component;
+ artifacts: Array<Models.ArtifactModel>;
+ editForm:ng.IFormController;
+ isLoading:boolean;
+ artifactDescriptions:any;
+ updateInProgress:boolean;
+
+ addOrUpdate(artifact:Models.ArtifactModel): void;
+ update(artifact:Models.ArtifactModel): void;
+ delete(artifact:Models.ArtifactModel): void;
+ sort(sortBy:string): void;
+ noArtifactsToShow():boolean;
+ getValidationPattern(validationType:string, parameterType?:string):RegExp;
+ validateJson(json:string):boolean;
+ resetValue(parameter:any):void;
+ }
+
+ export class ArtifactDeploymentStepViewModel implements IWizardCreationStep {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ValidationUtils',
+ 'ModalsHandler'
+ ];
+
+ constructor(
+ private $scope:IArtifactDeploymentStepViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private validationUtils: Sdc.Utils.ValidationUtils,
+ private ModalsHandler: Utils.ModalsHandler
+ ){
+ this.$scope.registerChild(this);
+ this.$scope.setValidState(true);
+ this.initScope();
+ }
+
+ private initDescriptions = ():void =>{
+ this.$scope.artifactDescriptions = {};
+ _.forEach(this.$scope.component.deploymentArtifacts,(artifact:Models.ArtifactModel):void => {
+ this.$scope.artifactDescriptions[artifact.artifactLabel] = artifact.description;
+ });
+ };
+
+
+ private setArtifact = (artifact:Models.ArtifactModel):void =>{
+ if(artifact.heatParameters) {
+ artifact.heatParameters.forEach((parameter:any):void => {
+ if (!parameter.currentValue && parameter.defaultValue) {
+ parameter.currentValue = parameter.defaultValue;
+ } else if ("" === parameter.currentValue) {
+ parameter.currentValue = null;
+ }
+ });
+ }
+ if(!artifact.description || !this.$scope.getValidationPattern('string').test(artifact.description)){
+ artifact.description = this.$scope.artifactDescriptions[artifact.artifactLabel];
+ }
+ };
+
+ private updateAll = ():void =>{
+ let artifacts:Array<Models.ArtifactModel>= [];
+ _.forEach(this.$scope.component.deploymentArtifacts,(artifact:Models.ArtifactModel): void => {
+ if(artifact.selected) {
+ this.setArtifact(artifact);
+ artifacts.push(artifact);
+ }
+ });
+ this.$scope.component.updateMultipleArtifacts(artifacts);
+ };
+
+
+
+ private initScope = (): void => {
+ let self = this;
+ this.$scope.isLoading = false;
+ this.$scope.updateInProgress = false;
+ this.$scope.component = this.$scope.getComponent();
+ this.initDescriptions();
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+
+
+ this.$scope.tableHeadersList = [
+ {title:'Name', property: 'artifactDisplayName'},
+ {title:'Type', property: 'artifactType'},
+ {title:'Deployment timeout', property: 'timeout'}
+ ];
+
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+ this.$scope.getValidationPattern = (validationType:string, parameterType?:string):RegExp => {
+ return this.validationUtils.getValidationPattern(validationType, parameterType);
+ };
+
+ this.$scope.validateJson = (json:string):boolean => {
+ if(!json){
+ return true;
+ }
+ return this.validationUtils.validateJson(json);
+ };
+
+
+ this.$scope.addOrUpdate = (artifact:Models.ArtifactModel): void => {
+ artifact.artifactGroupType = 'DEPLOYMENT';
+ let artifactCopy = new Models.ArtifactModel(artifact);
+ this.ModalsHandler.openWizardArtifactModal(artifactCopy, this.$scope.component).then(() => {
+ this.$scope.artifactDescriptions[artifactCopy.artifactLabel]= artifactCopy.description;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+ })
+ };
+
+ this.$scope.noArtifactsToShow = ():boolean =>{
+ return !_.some(this.$scope.artifacts, 'esId');
+ };
+
+ this.$scope.resetValue = (parameter:any):void => {
+ if(!parameter.currentValue && parameter.defaultValue){
+ parameter.currentValue = parameter.defaultValue;
+ }
+ else if('boolean'==parameter.type){
+ parameter.currentValue = parameter.currentValue.toUpperCase();
+ }
+ };
+
+
+ this.$scope.$watch('editForm.$valid', ():void => {
+ if(this.$scope.editForm) {
+ this.$scope.setValidState(this.$scope.editForm.$valid);
+ }
+ });
+
+ this.$scope.update = (artifact:Models.ArtifactModel):void =>{
+ if(false == this.$scope.isLoading) {
+ if(artifact.selected) {
+ this.$scope.isLoading = true;
+ this.$scope.updateInProgress = true;
+ let onSuccess = (responseArtifact:Models.ArtifactModel):void => {
+ this.$scope.artifactDescriptions[responseArtifact.artifactLabel] = responseArtifact.description;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+ this.$scope.isLoading = false;
+ this.$scope.updateInProgress = false;
+ artifact.selected = !artifact.selected;
+ };
+
+ let onFailed = (error:any):void => {
+ console.log('Delete artifact returned error:', error);
+ this.$scope.isLoading = false;
+ this.$scope.updateInProgress = false;
+ artifact.selected = !artifact.selected;
+ };
+
+ this.setArtifact(artifact);
+ this.$scope.component.addOrUpdateArtifact(artifact).then(onSuccess, onFailed);
+ } else {
+ artifact.selected = !artifact.selected;
+ }
+ }
+ };
+
+ this.$scope.delete = (artifact:Models.ArtifactModel):void => {
+ let onOk = ():void => {
+ this.$scope.isLoading = true;
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+ };
+
+ let onFailed = (error:any):void => {
+ this.$scope.isLoading = false;
+ console.log('Delete artifact returned error:', error);
+ };
+
+ this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed);
+ };
+
+ let title:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE");
+ let message:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ };
+
+ public save = (callback:Function):void => {
+ this.updateAll();
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ };
+
+ public back = (callback:Function):void => {
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts
new file mode 100644
index 0000000000..459729c179
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view-model.ts
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+
+ export interface IEditArtifactStepModel {
+ artifactResource: Models.ArtifactModel;
+ artifactTypes: Array<string>;
+ artifactsFormList: any;
+ artifactFile:any;
+ }
+
+ export interface IArtifactResourceFormStepViewModelScope extends ng.IScope {
+ editForm:ng.IFormController;
+ forms:any;
+ footerButtons: Array<any>;
+ isNew: boolean;
+ isPlaceHolderArtifact:boolean;
+ isLoading: boolean;
+ validationPattern: RegExp;
+ urlValidationPattern: RegExp;
+ labelValidationPattern: RegExp;
+ integerValidationPattern: RegExp;
+ commentValidationPattern: RegExp;
+ artifactType: string;
+ artifactGroupType:string;
+ editArtifactResourceModel: IEditArtifactStepModel;
+ defaultHeatTimeout: number;
+ validExtensions: any;
+ originalArtifactName: string;
+ modalInstanceArtifact:ng.ui.bootstrap.IModalServiceInstance;
+ selectedArtifact:string;
+
+ fileExtensions():string;
+ save(): void;
+ close(): void;
+ changeArtifact(selectedArtifact:string):void;
+ getOptions(): Array<string>;
+ removeInputLabel(): void;
+ fileUploadRequired():string;
+ isDeploymentHeat():boolean;
+ setDefaultTimeout():void;
+ getFormTitle():string;
+ }
+
+ export class ArtifactResourceFormStepViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$modalInstance',
+ 'artifact',
+ 'component',
+ 'Sdc.Services.CacheService',
+ 'ValidationPattern',
+ 'UrlValidationPattern',
+ 'LabelValidationPattern',
+ 'IntegerValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ 'ArtifactsUtils',
+ '$state',
+ '$modal',
+ '$templateCache'
+ ];
+
+ private formState:Utils.Constants.FormState;
+ private entityId:string;
+
+ constructor(private $scope:IArtifactResourceFormStepViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private artifact:Models.ArtifactModel,
+ private component:Models.Components.Component,
+ private cacheService:Services.CacheService,
+ private ValidationPattern:RegExp,
+ private UrlValidationPattern:RegExp,
+ private LabelValidationPattern:RegExp,
+ private IntegerValidationPattern:RegExp,
+ private CommentValidationPattern:RegExp,
+ private ValidationUtils:Sdc.Utils.ValidationUtils,
+ private ArtifactsUtils:Sdc.Utils.ArtifactsUtils,
+ private $state:any,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService) {
+
+
+ this.entityId = this.component.uniqueId;
+ this.formState = angular.isDefined(artifact.artifactLabel) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE;
+
+ this.initScope();
+ this.initEditArtifactResourceModel();
+ this.initComponent();
+ this.initArtifactTypes();
+ }
+
+ private initEditArtifactResourceModel = ():void => {
+ this.$scope.editArtifactResourceModel = {
+ artifactResource: null,
+ artifactTypes: null,
+ artifactsFormList: {},
+ artifactFile: {}
+ }
+ };
+
+ private initComponent = ():void => {
+ this.$scope.editArtifactResourceModel.artifactResource = this.artifact;
+ this.$scope.originalArtifactName = this.artifact.artifactName;
+ let artifacts:any = Utils.Constants.ArtifactGroupType.INFORMATION === this.artifact.artifactGroupType?
+ this.component.artifacts : this.component.deploymentArtifacts;
+ this.$scope.editArtifactResourceModel.artifactsFormList = _.pick(artifacts, (artifact:Models.ArtifactModel)=> {
+ return artifact.artifactLabel && !artifact.esId;
+ });
+ this.$scope.editArtifactResourceModel.artifactFile.filename= this.artifact.artifactName?this.artifact.artifactName:'';
+
+ if(this.artifact.artifactLabel){//this is edit mode
+ this.$scope.editArtifactResourceModel.artifactsFormList[this.artifact.artifactLabel]= this.artifact;
+ this.$scope.selectedArtifact = this.artifact.artifactDisplayName;
+ }
+ };
+
+ private initArtifactTypes = ():void => {
+ let artifactTypes:any = this.cacheService.get('UIConfiguration');
+
+ if (Utils.Constants.ArtifactGroupType.INFORMATION === this.artifact.artifactGroupType) {
+ this.$scope.editArtifactResourceModel.artifactTypes = artifactTypes.artifacts.other.map((element:any)=> {
+ return element.name;
+ });
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> {
+ return _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, item) ||
+ _.has(Utils.Constants.ArtifactType.TOSCA, item);
+ })
+ }else if(Utils.Constants.ArtifactGroupType.DEPLOYMENT === this.artifact.artifactGroupType) {
+
+ this.$scope.validExtensions = artifactTypes.artifacts.deployment.resourceDeploymentArtifacts;
+ if(this.$scope.validExtensions) {
+ this.$scope.editArtifactResourceModel.artifactTypes = Object.keys(this.$scope.validExtensions);
+ }
+ this.$scope.defaultHeatTimeout = artifactTypes.defaultHeatTimeout;
+
+ if(!this.$scope.isPlaceHolderArtifact) {
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string)=> {
+ return Utils.Constants.ArtifactType.HEAT == item.substring(0,4) ||
+ _.has(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES, item);
+ })
+ }
+
+ if(this.component.isResource() && (<Resource>this.component).isCsarComponent()) {
+ _.remove(this.$scope.editArtifactResourceModel.artifactTypes, (item:string) => {
+ return this.ArtifactsUtils.isLicenseType(item);
+ })
+ }
+ }
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.modalInstanceArtifact = this.$modalInstance;
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.urlValidationPattern = this.UrlValidationPattern;
+ this.$scope.labelValidationPattern = this.LabelValidationPattern;
+ this.$scope.integerValidationPattern = this.IntegerValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.isLoading = false;
+ this.$scope.isPlaceHolderArtifact = true;
+ this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE);
+ this.$scope.artifactGroupType = this.artifact.artifactGroupType;
+ this.$scope.selectedArtifact = '?';
+
+ this.$scope.fileExtensions = ():string => {
+ let type:string = this.$scope.editArtifactResourceModel.artifactResource.artifactType;
+ return type && this.$scope.validExtensions && this.$scope.validExtensions[type].acceptedTypes ?
+ this.$scope.validExtensions[type].acceptedTypes.join(',') : "";
+ };
+
+ this.$scope.removeInputLabel = ():void => {
+ this.$scope.isPlaceHolderArtifact = true;
+ };
+
+ this.$scope.fileUploadRequired = ():string => {
+ if (this.$scope.isNew===false){
+ return 'false'; // This is edit mode
+ } else {
+ return 'true';
+ }
+ };
+
+ this.$scope.isDeploymentHeat = ():boolean =>{
+ return Utils.Constants.ArtifactGroupType.DEPLOYMENT === this.artifact.artifactGroupType
+ && this.$scope.editArtifactResourceModel.artifactResource
+ && this.$scope.editArtifactResourceModel.artifactResource.artifactType
+ && Utils.Constants.ArtifactType.HEAT === this.$scope.editArtifactResourceModel.artifactResource.artifactType.substring(0,4);
+ };
+
+ this.$scope.getFormTitle =(): string =>{
+ let title:string = this.artifact.esId? 'Update':'Add';
+ if (Utils.Constants.ArtifactGroupType.DEPLOYMENT === this.artifact.artifactGroupType) {
+ title += ' Deployment';
+ }
+ title += ' Artifact';
+ return title;
+ };
+
+ this.$scope.setDefaultTimeout = ():void => {
+ if(!this.$scope.editArtifactResourceModel.artifactResource.timeout) {
+ this.$scope.editArtifactResourceModel.artifactResource.timeout = this.$scope.defaultHeatTimeout;
+ }
+ };
+
+ this.$scope.changeArtifact = (selectedArtifact:string):void => {
+ let tempArtifact:Models.ArtifactModel = this.$scope.editArtifactResourceModel.artifactResource;
+ this.$scope.editArtifactResourceModel.artifactResource = null;
+
+ if (selectedArtifact && selectedArtifact != '' && selectedArtifact != '?') {
+ let artifactResource = <Models.ArtifactModel>_.find(this.$scope.editArtifactResourceModel.artifactsFormList,{'artifactDisplayName':selectedArtifact});
+ this.$scope.editArtifactResourceModel.artifactResource = new Sdc.Models.ArtifactModel(artifactResource);
+ this.$scope.originalArtifactName = this.$scope.editArtifactResourceModel.artifactResource.artifactName;
+ this.$scope.isPlaceHolderArtifact = true;
+ if(this.$scope.isDeploymentHeat()){
+ this.$scope.setDefaultTimeout();
+ }
+ } else if (selectedArtifact === "") {
+ //this.$scope.editArtifactResourceModel.artifactFile = {};
+ this.$scope.editArtifactResourceModel.artifactResource = <Models.ArtifactModel>{};
+ this.$scope.editArtifactResourceModel.artifactResource.artifactGroupType = this.$scope.artifactGroupType;
+ this.$scope.isPlaceHolderArtifact = false;
+ }
+
+ if (_.size(this.$scope.editArtifactResourceModel.artifactFile) && this.$scope.editArtifactResourceModel.artifactResource) {
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename;
+ }
+ if(tempArtifact && tempArtifact.description != ''){
+ this.$scope.editArtifactResourceModel.artifactResource.description = tempArtifact.description;
+ }
+
+ this.initArtifactTypes();
+ this.$scope.isNew = true;
+
+ };
+
+
+ this.$scope.save = ():void => {
+ this.$scope.isLoading = true;
+ this.$scope.editArtifactResourceModel.artifactResource.description =
+ this.ValidationUtils.stripAndSanitize(this.$scope.editArtifactResourceModel.artifactResource.description);
+
+ if (this.$scope.editArtifactResourceModel.artifactFile) {
+ this.$scope.editArtifactResourceModel.artifactResource.payloadData = this.$scope.editArtifactResourceModel.artifactFile.base64;
+ this.$scope.editArtifactResourceModel.artifactResource.artifactName = this.$scope.editArtifactResourceModel.artifactFile.filename;
+ }
+
+ let onFailed = (response) => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+ this.$scope.editArtifactResourceModel.artifactResource.esId = undefined;
+ };
+
+ let onSuccess = () => {
+ this.$scope.isLoading = false;
+ this.$scope.originalArtifactName = "";
+ this.$modalInstance.close();
+
+ };
+
+ this.component.addOrUpdateArtifact(this.$scope.editArtifactResourceModel.artifactResource).then(onSuccess, onFailed);
+ };
+
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.dismiss();
+ };
+
+ //new form layout for import asset
+ this.$scope.forms = {};
+ this.$scope.footerButtons = [
+ {'name': this.artifact.esId ? 'Update' : 'Add', 'css': 'blue', 'callback': this.$scope.save},
+ {'name': 'Cancel', 'css': 'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch("forms.editForm.$invalid", (newVal, oldVal) => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html
new file mode 100644
index 0000000000..2643b99c20
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step-view.html
@@ -0,0 +1,147 @@
+<sdc-modal modal="modalInstanceArtifact" type="classic" class="sdc-add-artifact" buttons="footerButtons" header="{{getFormTitle()}}" show-close-button="true">
+
+ <loader data-display="isLoading"></loader>
+
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm">
+
+ <!--------------------- ARTIFACT FILE -------------------->
+ <div class="i-sdc-form-item">
+ <label class="required">Upload File</label>
+ <file-upload id="fileUploadElement"
+ form-element="forms.editForm"
+ element-required="{{fileUploadRequired()}}"
+ element-name="myArtifactFile"
+ file-model="editArtifactResourceModel.artifactFile"
+ extensions="{{fileExtensions()}}"
+ data-ng-class="{'error': forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid}"></file-upload>
+
+ <div class="input-error-file-upload" data-ng-show="forms.editForm.myArtifactFile.$dirty && forms.editForm.myArtifactFile.$invalid">
+ <span ng-show="forms.editForm.myArtifactFile.$error.required && !forms.editForm.myArtifactFile.$error.emptyFile" translate="ADD_ARTIFACT_ERROR_FILE_REQUIRED"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.maxsize" translate="VALIDATION_ERROR_MAX_FILE_SIZE"></span>
+ <span ng-if="'DEPLOYMENT' === artifactGroupType" ng-show="forms.editForm.myArtifactFile.$error.filetype" translate="ADD_ARTIFACT_ERROR_VALID_EXTENSIONS" translate-values="{'extensions': '{{fileExtensions()}}'}"></span>
+ <span ng-show="forms.editForm.myArtifactFile.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ </div>
+ </div>
+ <!--------------------- ARTIFACT FILE -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.placeHolder.$dirty && forms.editForm.placeHolder.$invalid)}" data-ng-show="isPlaceHolderArtifact">
+ <label class="i-sdc-form-label required">Artifact</label>
+ <select class="i-sdc-form-select"
+ name="placeHolder"
+ data-ng-disabled="!isNew"
+ data-ng-model="selectedArtifact"
+ data-ng-change="changeArtifact(selectedArtifact)"
+ data-tests-id="selectArtifact">
+ <option disabled value="?">Select Artifact</option>
+ <option data-ng-repeat="(key,value) in editArtifactResourceModel.artifactsFormList">{{value.artifactDisplayName}}</option>
+ <option value="">Create New Artifact</option>
+
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.placeHolder.$dirty && forms.editForm.placeHolder.$invalid">
+ <span ng-show="forms.editForm.placeHolder.$error.required" translate="ADD_ARTIFACT_ERROR_TYPE_REQUIRED"></span>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid)}" data-ng-show="!isPlaceHolderArtifact">
+ <label class="i-sdc-form-label required">Artifact Label</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactLabel"
+ type="text"
+ name="artifactLabel"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="labelValidationPattern"
+ maxlength="25"
+ data-ng-disabled="!isNew || editArtifactResourceModel.artifactResource.mandatory"
+ data-tests-id="artifactLabel"
+ autofocus/>
+ <span class="w-sdc-icon-cancel" data-ng-click="selectedArtifact='?'; removeInputLabel()"></span>
+
+ <div class="input-error" data-ng-show="forms.editForm.artifactLabel.$dirty && forms.editForm.artifactLabel.$invalid">
+ <span ng-show="forms.editForm.artifactLabel.$error.required" translate="ADD_ARTIFACT_ERROR_LABEL_REQUIRED"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.artifactLabel.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid)}"
+ data-ng-if="isDeploymentHeat()">
+ <label class="i-sdc-form-label">Deployment Timeout (minutes)</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="25"
+ data-ng-model="editArtifactResourceModel.artifactResource.timeout"
+ type="number"
+ name="timeout"
+ min="1"
+ max="2147483647"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="integerValidationPattern"
+ data-ng-init="setDefaultTimeout()"
+ data-ng-change="setDefaultTimeout()"
+ maxlength="25"
+ data-tests-id="timeout"/>
+
+ <div class="input-error" data-ng-show="forms.editForm.timeout.$dirty && forms.editForm.timeout.$invalid">
+ <span ng-show="forms.editForm.timeout.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="forms.editForm.timeout.$error.pattern" translate="ADD_ARTIFACT_ERROR_TIMEOUT_PATTERN"></span>
+ <span ng-show="forms.editForm.timeout.$error.min" translate="ADD_ARTIFACT_ERROR_TIMEOUT_MIN"></span>
+ </div>
+ </div>
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label required">Type</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="type"
+ data-ng-disabled="!isNew || editArtifactResourceModel.artifactResource.mandatory || '?'==selectedArtifact"
+ data-ng-model="editArtifactResourceModel.artifactResource.artifactType"
+ data-ng-options="type as type for type in editArtifactResourceModel.artifactTypes track by type | uppercase"
+ data-tests-id="artifacttype">
+ <option value="">Choose Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="ADD_ARTIFACT_ERROR_TYPE_REQUIRED"></span>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item"
+ data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-required
+ name="description"
+ data-ng-model="editArtifactResourceModel.artifactResource.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="commentValidationPattern"
+ data-tests-id="description"
+ >
+ </textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <span class="w-sdc-form-note" data-ng-show="forms.editForm.$invalid && false" translate="LABEL_ALL_FIELDS_ARE_MANDATORY"></span>
+
+ </form>
+
+</sdc-modal>
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less
new file mode 100644
index 0000000000..a189c12d62
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-form-step/artifact-form-step.less
@@ -0,0 +1,45 @@
+.sdc-add-artifact {
+
+ .w-sdc-form-note {
+ .h_9;
+ display: block;
+ position: relative;
+ top: 13px;
+ }
+
+ .w-sdc-form {
+
+ .i-sdc-form-textarea{
+ min-height: 95px;
+ }
+
+ .i-sdc-form-item.upload input[type="file"] {
+ display: none
+ }
+
+ .w-sdc-icon-cancel {
+ position: absolute;
+ right: 7px;
+ top: 33px;
+ .sprite;
+ .sprite.small-x-btn-black;
+ .hand;
+ }
+ }
+
+ .artifact-info {
+ text-align: left;
+ color: rgb(140, 140, 140);
+ font-size: 13px;
+ margin-top: -40px;
+ margin-bottom: 5px;
+ width: 600px;
+ min-height: initial;
+
+ span {
+ color: #666666;
+ padding-left: 4px;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html
new file mode 100644
index 0000000000..ea4566561c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html
@@ -0,0 +1,48 @@
+<div class="artifact-step" data-ng-controller="Sdc.ViewModels.Wizard.ArtifactInformationStepViewModel">
+ <div data-tests-id="add-information-artifact-button" data-tests-id="addGrey" class="w-sdc-classic-btn gray" data-ng-click="addOrUpdate({})" type="button">Add </div>
+ <div class="table-container-flex">
+ <div class="table">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-show="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="showNoArtifactMessage()" class="no-row-text">
+ There are no information artifacts to display
+ </div>
+ <div data-ng-repeat-start="artifact in artifacts| orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected}"
+ data-ng-if="artifact.esId">
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected">
+ <span class="sprite table-arrow" data-ng-class="{'opened': artifact.selected}" data-tests-id="{{artifact.artifactDisplayName}}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+
+ <div class="table-btn-col flex-item" sdc-keyboard-events>
+ <button class="table-edit-btn" data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="table-delete-btn" data-ng-click="delete(artifact)"> </button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected" class="item-opened" data-ng-bind="artifact.description"></div>
+ <button class="add-button" data-ng-repeat="artifact in artifacts track by $index"
+ data-ng-show="!artifact.esId"
+ data-tests-id="{{artifact.artifactDisplayName}}"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT"
+ translate-values="{'name': '{{artifact.artifactDisplayName}}'}"
+ data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="add-button" translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER"
+ data-ng-click="addOrUpdate({})"></button>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less
new file mode 100644
index 0000000000..b0600ca483
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.less
@@ -0,0 +1,50 @@
+.artifact-step {
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table{
+ height: 412px;
+ margin-bottom: 0;
+ }
+
+ .table-container-flex {
+ margin-top: 0px;
+
+ .item-opened{
+ word-wrap: break-word;
+ }
+
+
+ .flex-item:nth-child(1) {
+ padding: 5px 4px;
+ flex-grow: 15;
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ padding: 5px 4px;
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ padding: 5px 4px;
+ flex-grow: 3;
+ padding-top: 10px;
+ }
+
+ .flex-item:nth-child(4) {
+ padding: 5px 4px;
+ flex-grow: 1;
+ }
+
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts
new file mode 100644
index 0000000000..f6e25a53a3
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.ts
@@ -0,0 +1,168 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ export interface IArtifactInformationStepScope extends IWizardCreationScope {
+ artifacts: Array<Models.ArtifactModel>;
+ tableHeadersList: Array<any>;
+ artifactType: string;
+ isResourceInstance:boolean;
+ downloadFile:Models.IFileDownload;
+ isLoading:boolean;
+ sortBy:string;
+ reverse:boolean;
+ component:Models.Components.Component;
+
+ getTitle(): string;
+ addOrUpdate(artifact:Models.ArtifactModel): void;
+ delete(artifact:Models.ArtifactModel): void;
+ download(artifact:Models.ArtifactModel): void;
+ clickArtifactName(artifact:any):void;
+ openEditEnvParametersModal(artifactResource: Models.ArtifactModel):void;
+ sort(sortBy:string): void;
+ showNoArtifactMessage():boolean;
+ }
+
+ export class ArtifactInformationStepViewModel implements IWizardCreationStep {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'Sdc.Services.SharingService',
+ '$state',
+ 'sdcConfig',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:IArtifactInformationStepScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private sharingService:Sdc.Services.SharingService,
+ private $state:any,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private ModalsHandler: Utils.ModalsHandler) {
+ this.$scope.registerChild(this);
+ this.$scope.setValidState(true);
+ this.initScope();
+ }
+
+
+ private getMappedObjects():any {
+ return {
+ normal: this.$scope.component.artifacts
+ };
+ }
+
+ private initScope = ():void => {
+ let self = this;
+ this.$scope.isLoading = false;
+ this.$scope.sortBy = 'artifactDisplayName';
+ this.$scope.reverse = false;
+
+ this.$scope.artifactType = 'normal';
+ this.$scope.getTitle = ():string => {
+ return this.$filter("resourceName")(this.$scope.component.name) + ' Artifacts';
+
+ };
+
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'artifactDisplayName'},
+ {title: 'Type', property: 'artifactType'}
+ ];
+
+
+ this.$scope.component = this.$scope.getComponent();
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.artifacts);
+
+
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+
+ this.$scope.addOrUpdate = (artifact:Models.ArtifactModel):void => {
+ artifact.artifactGroupType = 'INFORMATIONAL';
+ this.ModalsHandler.openWizardArtifactModal(artifact, this.$scope.getComponent()).then(() => {
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.artifacts);
+ });
+ };
+
+ this.$scope.showNoArtifactMessage = ():boolean => {
+ let artifacts:any = [];
+ artifacts = _.filter(this.$scope.artifacts, (artifact:Models.ArtifactModel)=> {
+ return artifact.esId;
+ });
+
+ if (artifacts.length === 0) {
+ return true;
+ }
+ return false;
+ }
+
+ this.$scope.delete = (artifact:Models.ArtifactModel):void => {
+
+ let onOk = ():void => {
+ this.$scope.isLoading = true;
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.artifacts);
+ };
+
+ let onFailed = (error:any):void => {
+ console.log('Delete artifact returned error:', error);
+ this.$scope.isLoading = false;
+ };
+
+ this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed);
+ };
+
+ let title:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+
+ this.$scope.clickArtifactName = (artifact:any) => {
+ if ('deployment' !== this.$scope.artifactType || 'HEAT' !== artifact.artifactType || !artifact.esId) {
+ this.$scope.addOrUpdate(artifact);
+ }
+
+ };
+
+ }
+
+ public save = (callback:Function):void => {
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ }
+
+ public back = (callback:Function):void => {
+ callback(true);
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html
new file mode 100644
index 0000000000..db975caf47
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.html
@@ -0,0 +1,270 @@
+<div include-padding="true" class="sdc-wizard-general-step">
+ <div ng-controller="Sdc.ViewModels.Wizard.GeneralStepViewModel">
+ <form novalidate class="w-sdc-form" name="editForm">
+ <div class="w-sdc-form-section-container">
+
+ <!--------------------- IMPORT TOSCA FILE -------------------->
+ <file-upload id="fileUploadElement"
+ ng-if="!isCreate"
+ element-name="fileElement"
+ element-disabled="{{!isNew}}"
+ form-element="editForm"
+ file-model="model.tosca"
+ extensions="{{toscaFileExtensions}}"
+ data-ng-class="{'error': !editForm.fileElement.$valid || !model.tosca.filename}"></file-upload>
+
+ <div class="input-error-file-upload" data-ng-show="!isCreate && (!editForm.fileElement.$valid || !model.tosca.filename)">
+ <!-- editForm.fileElement.$error.required <== Can not use this, because the browse is done from outside for the first time -->
+ <span ng-show="!model.tosca.filename && !editForm.fileElement.$error.emptyFile" translate="NEW_SERVICE_RESOURCE_ERROR_TOSCA_FILE_REQUIRED"></span><!-- Required -->
+ <span ng-show="editForm.fileElement.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ <span ng-show="editForm.fileElement.$error.filetype" translate="NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS" translate-values="{'extensions': '{{toscaFileExtensions}}' }"></span>
+ </div>
+ <!--------------------- IMPORT TOSCA FILE -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!--------------------- NAME -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.componentName)}">
+ <label class="i-sdc-form-label required">Name</label>
+ <input class="i-sdc-form-input"
+ name="componentName"
+ data-ng-init="isNew && validateName(true)"
+ data-ng-change="validateName()"
+ data-ng-maxlength="{{component.isProduct()?'25':'50'}}"
+ maxlength="{{component.isProduct()?'25':'50'}}"
+ data-ng-minlength="{{component.isProduct()?'4':'0'}}"
+ minlength="{{component.isProduct()?'4':'0'}}"
+ data-ng-model="model.name"
+ type="text"
+ data-required
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.validationPattern"
+ data-ng-disabled="model.isAlreadyCertified"
+ data-tests-id="name"
+ autofocus
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.componentName)">
+ <span ng-show="editForm.componentName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED"></span>
+ <span ng-show="editForm.componentName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editForm.componentName.$error.minlength" translate="VALIDATION_ERROR_MIN_LENGTH" translate-values="{'min': '4' }"></span>
+ <span ng-show="editForm.componentName.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS"></span>
+ <span ng-show="editForm.componentName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- NAME -------------------->
+
+ <!--------------------- FULL NAME -------------------->
+ <div ng-if="component.isProduct()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.fullName)}">
+ <label class="i-sdc-form-label required">Full Name</label>
+ <input class="i-sdc-form-input"
+ name="fullName"
+ data-ng-change="validateName()"
+ data-ng-maxlength="100"
+ maxlength="100"
+ data-ng-minlength="4"
+ minlength="4"
+ data-ng-model="model.fullName"
+ type="text"
+ data-required
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.validationPattern"
+ data-tests-id="fullName"
+ autofocus
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.fullName)">
+ <span ng-show="editForm.fullName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED"></span>
+ <span ng-show="editForm.fullName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editForm.fullName.$error.minlength" translate="VALIDATION_ERROR_MIN_LENGTH" translate-values="{'min': '4' }"></span>
+ <span ng-show="editForm.fullName.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS"></span>
+ <span ng-show="editForm.fullName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- NAME -------------------->
+
+ <!--------------------- DESCRIPTION -------------------->
+ <div class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.description)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea description"
+ name="description"
+ data-ng-maxlength="1024"
+ data-required
+ data-ng-model="model.description"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.commentValidationPattern"
+ maxlength="1024"
+ data-tests-id="description"></textarea>
+ <!-- placeholder="Description here..." -->
+
+ <div class="input-error" data-ng-show="validateField(editForm.description)">
+ <span ng-show="editForm.description.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '1024' }"></span>
+ <span ng-show="editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- DESCRIPTION -------------------->
+
+ <!--------------------- CATEGORIES -------------------->
+ <div class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.category)}"
+ data-ng-if="categories && categories.length && !component.isProduct()">
+ <label class="i-sdc-form-label required">Category</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="category"
+ data-ng-change="setIconToDefault()"
+ data-ng-disabled="model.isAlreadyCertified"
+ data-ng-model="model.category"
+ data-tests-id="selectGeneralCategory"
+ >
+ <option value="">Select category</option>
+ <optgroup ng-if="component.isResource()" data-ng-repeat="mainCategory in categories | orderBy:['name']" label="{{mainCategory.name}}" data-tests-id="{{mainCategory.name}}">
+ <option data-ng-repeat="subCategory in mainCategory.subcategories track by $index"
+ data-ng-selected="model.category===calculateUnique(mainCategory.name,subCategory.name)"
+ data-tests-id="{{subCategory.name}}"
+ value="{{calculateUnique(mainCategory.name,subCategory.name)}}">{{subCategory.name}}
+
+ </option>
+ </optgroup>
+ <option ng-if="component.isService()" data-ng-repeat="mainCategory in categories | orderBy:['name']"
+ data-ng-selected="model.category===mainCategory.name"
+ value="{{mainCategory.name}}"
+ data-tests-id="{{mainCategory.name}}">{{mainCategory.name}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="validateField(editForm.category)">
+ <span ng-show="editForm.category.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED"></span>
+ </div>
+ </div>
+ <!--------------------- CATEGORIES -------------------->
+
+ <!--------------------- VENDOR NAME -------------------->
+ <div ng-if="component.isResource()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.vendorName)}">
+ <label class="i-sdc-form-label required">Vendor</label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="model.vendorName"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="25"
+ data-required
+ ng-click="oldValue = model.vendorName"
+ name="vendorName"
+ data-ng-change="onVendorNameChange(oldValue)"
+ data-ng-pattern="validation.vendorValidationPattern"
+ maxlength="25"
+ data-ng-disabled="model.isAlreadyCertified"
+ data-tests-id="vendorName"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.vendorName)">
+ <span ng-show="editForm.vendorName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_VENDOR_NAME_REQUIRED"></span>
+ <span ng-show="editForm.vendorName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="editForm.vendorName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!--------------------- VENDOR NAME -------------------->
+
+ <!--------------------- VENDOR RELEASE -------------------->
+ <div ng-if="component.isResource()"
+ class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.vendorRelease)}">
+ <label class="i-sdc-form-label required">Vendor Release</label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="model.vendorRelease"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="25"
+ data-required
+ name="vendorRelease"
+ data-ng-pattern="validation.vendorValidationPattern"
+ maxlength="25"
+ data-tests-id="vendorRelease"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.vendorRelease)">
+ <span ng-show="editForm.vendorRelease.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_VENDOR_RELEASE_REQUIRED"></span>
+ <span ng-show="editForm.vendorRelease.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="editForm.vendorRelease.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- VENDOR RELEASE -------------------->
+
+
+
+ </div><!-- Close w-sdc-form-column -->
+
+ <div class="w-sdc-form-column">
+
+ <!--------------------- RESOURCE TAGS -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.tags)}">
+ <label class="i-sdc-form-label">Tags</label>
+
+ <sdc-tags form-element="editForm" element-name="tags" max-tags="20" class="i-sdc-form-item-tags" tags="model.tags" pattern="validation.tagValidationPattern" special-tag="model.name"></sdc-tags>
+
+ <div class="input-error" data-ng-show="validateField(editForm.tags)">
+ <span ng-show="editForm.tags.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- RESOURCE TAGS -------------------->
+
+ <!--------------------- CONTACT ID -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.contactId)}">
+ <label class="i-sdc-form-label required" translate="GENERAL_LABEL_CONTACT_ID"></label>
+ <input class="i-sdc-form-input disabled" type="text"
+ data-ng-model="model.userId"
+ data-required
+ name="contactId"
+ data-ng-pattern="validation.contactIdValidationPattern"
+ data-ng-model-options="{ debounce: 500 }"
+ data-tests-id="userId"
+ maxlength="50"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.contactId)">
+ <span ng-show="editForm.contactId.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED"></span>
+ <span ng-show="editForm.contactId.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID"></span>
+ </div>
+ </div>
+ <!--------------------- CONTACT ID -------------------->
+
+ <!--------------------- PROJECT CODE -------------------->
+ <div class="i-sdc-form-item" data-ng-if="!component.isResource()"
+ data-ng-class="{'error': validateField(editForm.projectCode)}">
+ <label class="i-sdc-form-label required" translate="GENERAL_LABEL_PROJECT_CODE"></label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="model.projectCode"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="128"
+ data-required
+ name="projectCode"
+ data-ng-pattern="validation.projectCodeValidationPattern"
+ maxlength="50"
+ data-tests-id="projectCode"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.projectCode)">
+ <span ng-show="editForm.projectCode.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_REQUIRED"></span>
+ <span ng-show="editForm.projectCode.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_NOT_VALID"></span>
+ </div>
+ </div>
+ <!--------------------- VENDOR RELEASE -------------------->
+
+
+ </div><!-- Close w-sdc-form-column -->
+
+ </div><!-- Close w-sdc-form-column -->
+
+ <div class="w-sdc-form-messages-wrapper">
+ <span class="w-sdc-form-messages-msg" data-ng-show="isSaved"><span class="w-sdc-form-messages-msg-v"></span>Your resource has been saved</span>
+ </div>
+
+ </div><!-- Close w-sdc-form-section-container -->
+
+ </form>
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less
new file mode 100644
index 0000000000..700997a423
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.less
@@ -0,0 +1,34 @@
+.sdc-wizard-general-step {
+
+ .w-sdc-form {
+ padding: 0;
+
+ .w-sdc-form-section-container {
+ text-align: center;
+ }
+
+ .i-sdc-form-item {
+ &.upload {
+ margin-top: 0;
+ width: auto;
+ padding: 10px;
+ }
+ }
+
+ .template-desc {
+ border: 1px dashed @border_color_f;
+ height: 130px;
+ overflow: hidden;
+ padding: 10px 6px 6px 6px;
+ margin-top: 10px;
+ }
+
+ .sdc-tag .tag {
+ max-width: 225px;
+ }
+
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts
new file mode 100644
index 0000000000..74c681e433
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/general-step/general-step.ts
@@ -0,0 +1,381 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ import ISubCategory = Sdc.Models.ISubCategory;
+ import IMainCategory = Sdc.Models.IMainCategory;
+ 'use strict';
+
+ /*
+ * TODO: The template (derived from is not necessary here).
+ * Need to delete it from all remarks.
+ * */
+
+ export class UIModel {
+ tosca:Sdc.Directives.FileUploadModel;
+ name:string;
+ description:string;
+ vendorName:string;
+ vendorRelease:string;
+ category:string;
+ tags:Array<string>;
+ userId:string;
+ icon:string;
+ projectCode:string;
+ fullName:string;
+ isAlreadyCertified:boolean;
+ }
+
+ export class Validation {
+ validationPattern: RegExp;
+ contactIdValidationPattern: RegExp;
+ tagValidationPattern: RegExp;
+ vendorValidationPattern: RegExp;
+ commentValidationPattern: RegExp;
+ projectCodeValidationPattern: RegExp;
+ }
+
+ export interface IGeneralStepScope extends IWizardCreationStepScope {
+ model:UIModel;
+ validation:Validation;
+ editForm:ng.IFormController;
+ component: Models.Components.Component;
+ categories: Array<IMainCategory>;
+ latestComponentName:string;
+ latestCategoryId: string;
+ latestVendorName: string;
+ isNew:boolean;
+ toscaFileExtensions:any;
+ isCreate:boolean;
+
+ onToscaFileChange():void
+ validateField(field:any):boolean;
+ validateName(isInit:boolean): void;
+ calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category
+ calculatedTagsMaxLength():number;
+ setIconToDefault():void;
+ onVendorNameChange(oldVendorName: string): void;
+ }
+
+ export class GeneralStepViewModel implements IWizardCreationStep {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.CacheService',
+ 'ValidationPattern',
+ 'ContactIdValidationPattern',
+ 'TagValidationPattern',
+ 'VendorValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ 'sdcConfig',
+ 'ComponentFactory',
+ 'ProjectCodeValidationPattern'
+ ];
+
+ constructor(private $scope:IGeneralStepScope,
+ private cacheService:Services.CacheService,
+ private ValidationPattern:RegExp,
+ private ContactIdValidationPattern:RegExp,
+ private TagValidationPattern:RegExp,
+ private VendorValidationPattern:RegExp,
+ private CommentValidationPattern: RegExp,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private ComponentFactory: Sdc.Utils.ComponentFactory,
+ private ProjectCodeValidationPattern:RegExp
+ ) {
+
+ this.$scope.registerChild(this);
+ this.initScopeValidation();
+ this.initScopeMethods();
+ this.initScope();
+ this.$scope.isCreate = this.$scope.data.importFile === undefined;
+ }
+
+ private initScopeValidation = (): void => {
+ this.$scope.validation = new Validation();
+ this.$scope.validation.validationPattern = this.ValidationPattern;
+ this.$scope.validation.contactIdValidationPattern = this.ContactIdValidationPattern;
+ this.$scope.validation.tagValidationPattern = this.TagValidationPattern;
+ this.$scope.validation.vendorValidationPattern = this.VendorValidationPattern;
+ this.$scope.validation.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern;
+ };
+
+ private initScope = ():void => {
+
+ // Init UIModel
+ this.$scope.model = new UIModel();
+
+ // Init categories
+ if(this.$scope.data.componentType === Utils.Constants.ComponentType.RESOURCE){
+ this.$scope.categories = this.cacheService.get('resourceCategories');
+ }
+ if (this.$scope.data.componentType === Utils.Constants.ComponentType.SERVICE) {
+ this.$scope.categories = this.cacheService.get('serviceCategories');
+ }
+
+ this.$scope.model.category='';
+
+ //init file extenstions
+ this.$scope.toscaFileExtensions = this.sdcConfig.toscaFileExtension;
+
+ // Init Tosca import file
+ if (this.$scope.data.importFile) {
+ this.$scope.model.tosca = this.$scope.data.importFile;
+ }
+
+ // Case insert or update
+ this.$scope.component = this.$scope.getComponent();
+ if ( this.$scope.component!==undefined){
+ // Update mode
+
+ //this.$scope.latestCategoryId = this.$scope.component[0].uniqueId;
+ //this.$scope.latestVendorName = this.$scope.component.vendorName;
+ this.$scope.latestComponentName = this.$scope.component.name;
+ this.$scope.isNew=false;
+ this.resource2ModelUi(this.$scope.component);
+ } else {
+ // Create mode
+ this.$scope.isNew=true;
+ this.$scope.model.tags=[]; // Init tags
+ this.$scope.model.userId = this.cacheService.get("user").userId; // Fill user ID from logged in user
+ this.$scope.model.icon = Utils.Constants.DEFAULT_ICON; // Set the default icon
+ this.$scope.component = this.ComponentFactory.createEmptyComponent(this.$scope.data.componentType);
+ }
+ };
+
+ private initScopeMethods = ():void => {
+
+ this.$scope.validateField = (field:any):boolean => {
+ if (field && field.$dirty && field.$invalid){
+ return true;
+ }
+ return false;
+ };
+
+ this.$scope.validateName = (isInit:boolean):void => {
+ if (isInit===undefined){isInit=false;}
+
+ let name = this.$scope.model.name;
+ if (!name || name===""){
+ if (this.$scope.editForm
+ && this.$scope.editForm["componentName"]
+ && this.$scope.editForm["componentName"].$error){
+
+ // Clear the error name already exists
+ this.$scope.editForm["componentName"].$setValidity('nameExist', true);
+ }
+
+ return;
+ }
+ let subtype:string = Utils.Constants.ComponentType.RESOURCE == this.$scope.data.componentType?
+ this.$scope.data.importFile? 'VFC':'VF' : undefined;
+
+ let onFailed = (response) => {
+ //console.info('onFaild', response);
+ //this.$scope.isLoading = false;
+ };
+
+ let onSuccess = (validation:Models.IValidate) => {
+ this.$scope.editForm["componentName"].$setValidity('nameExist', validation.isValid);
+ };
+
+ if (isInit){
+ // When page is init after update
+ if (this.$scope.model.name !== this.$scope.latestComponentName){
+ if(!this.$scope.component.isProduct()) {//TODO remove when backend is ready
+ this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
+ }
+ }
+ } else {
+ // Validating on change (has debounce)
+ if (this.$scope.editForm
+ && this.$scope.editForm["componentName"]
+ && this.$scope.editForm["componentName"].$error
+ && !this.$scope.editForm["componentName"].$error.pattern
+ && this.$scope.model.name !== this.$scope.latestComponentName
+ ) {
+ if(!this.$scope.component.isProduct()) { //TODO remove when backend is ready
+ this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
+ }
+ } else if (this.$scope.model.name === this.$scope.latestComponentName) {
+ // Clear the error
+ this.$scope.editForm["componentName"].$setValidity('nameExist', true);
+ }
+ }
+ };
+
+ this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => {
+ let uniqueId: string = mainCategory;
+ if(subCategory) {
+ uniqueId += "_#_" + subCategory; // Set the select category combobox to show the selected category.
+ }
+ return uniqueId;
+ };
+
+ // Notify the parent if this step valid or not.
+ this.$scope.$watch("editForm.$valid", (newVal, oldVal) => {
+ //console.log("editForm validation: " + newVal);
+ this.$scope.setValidState(newVal);
+ });
+
+ this.$scope.setIconToDefault = ():void => {
+ this.$scope.model.icon = Utils.Constants.DEFAULT_ICON;
+ };
+
+ this.$scope.onVendorNameChange = (oldVendorName: string):void => {
+ if(this.$scope.component.icon === oldVendorName) {
+ this.$scope.setIconToDefault();
+ }
+ };
+ };
+
+ public save = (callback:Function):void => {
+ this.modelUi2Resource();
+
+ let onFailed = (response) => {
+ callback(false);
+ };
+
+ let onSuccess = (component:Models.Components.Component) => {
+ this.$scope.component = component;
+ this.$scope.setComponent(this.$scope.component);
+ this.$scope.latestComponentName = (component.name);
+ callback(true);
+ };
+
+ try {
+ //Send the form with attached tosca file.
+ if (this.$scope.isNew===true) {
+ this.ComponentFactory.createComponentOnServer(this.$scope.component).then(onSuccess, onFailed);
+ } else {
+ this.$scope.component.updateComponent().then(onSuccess, onFailed);
+ }
+ }catch(e){
+ //console.log("ERROR: Error in updating/creating component: " + e);
+ callback(false);
+ }
+
+ };
+
+ public back = (callback:Function):void => {
+ callback(true);
+ }
+
+ // Fill the resource properties object with data from UIModel
+ private modelUi2Resource = ():void => {
+
+ this.$scope.component.name = this.$scope.model.name;
+ this.$scope.component.description = this.ValidationUtils.stripAndSanitize(this.$scope.model.description);
+ this.$scope.component.vendorName = this.$scope.model.vendorName;
+ this.$scope.component.vendorRelease = this.$scope.model.vendorRelease;
+ this.$scope.component.tags = angular.copy(this.$scope.model.tags);
+ this.$scope.component.tags.push(this.$scope.model.name);
+ this.$scope.component.contactId = this.$scope.model.userId;
+ this.$scope.component.icon = this.$scope.model.icon;
+
+ if(this.$scope.component.isResource()) {
+ (<Models.Components.Resource>this.$scope.component).resourceType = "VF";
+
+ // Handle the tosca file
+ if (this.$scope.model.tosca && this.$scope.isNew) {
+ (<Models.Components.Resource>this.$scope.component).payloadData = this.$scope.model.tosca.base64;
+ (<Models.Components.Resource>this.$scope.component).payloadName = this.$scope.model.tosca.filename;
+ }
+
+ this.$scope.component.categories = this.convertCategoryStringToOneArray();
+ }
+
+ if(this.$scope.component.isProduct()) {
+ this.$scope.component.projectCode = this.$scope.model.projectCode;
+ // Handle the tosca file
+ this.$scope.component.categories = undefined;
+ (<Models.Components.Product>this.$scope.component).contacts = new Array<string>();
+ (<Models.Components.Product>this.$scope.component).contacts.push(this.$scope.component.contactId);
+ (<Models.Components.Product>this.$scope.component).fullName = this.$scope.model.fullName;
+ }
+
+ if(this.$scope.component.isService()) {
+ this.$scope.component.projectCode = this.$scope.model.projectCode;
+ this.$scope.component.categories = this.convertCategoryStringToOneArray();
+ }
+ };
+
+ // Fill the UIModel from data from resource properties
+ private resource2ModelUi = (component: Models.Components.Component):void => {
+ this.$scope.model.name = component.name;
+ this.$scope.model.description = component.description;
+ this.$scope.model.vendorName = component.vendorName;
+ this.$scope.model.vendorRelease = component.vendorRelease;
+ this.$scope.model.tags = _.reject(component.tags, (item)=>{return item===component.name});
+ this.$scope.model.userId = component.contactId;
+ this.$scope.model.icon = component.icon;
+ this.$scope.model.projectCode = component.projectCode;
+ this.$scope.model.isAlreadyCertified = component.isAlreadyCertified();
+
+ if(!this.$scope.component.isProduct()) {
+ this.$scope.model.category = this.convertCategoryOneArrayToString(component.categories);
+ }
+
+ if(component.isProduct()) {
+ this.$scope.model.fullName = (<Models.Components.Product>component).fullName;
+
+ }
+
+ };
+
+ // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except)
+ private convertCategoryStringToOneArray = ():Array<Models.IMainCategory> => {
+ let tmp = this.$scope.model.category.split("_#_");
+ let mainCategory = tmp[0];
+ let subCategory = tmp[1];
+
+ // Find the selected category and add the relevant sub category.
+ let selectedMainCategory:IMainCategory = <Models.IMainCategory>_.find(this.$scope.categories, function (item) {
+ return item["name"] === mainCategory
+ });
+ let mainCategoryClone = jQuery.extend(true, {}, selectedMainCategory);
+ if(subCategory) {
+ mainCategoryClone['subcategories'] = [{
+ "name": subCategory
+ }];
+ }
+ let tmpSelected = <Models.IMainCategory> mainCategoryClone;
+
+ let result:Array<Models.IMainCategory> = [];
+ result.push(tmpSelected);
+
+ return result;
+ };
+
+ private convertCategoryOneArrayToString = (categories:Array<Models.IMainCategory>):string => {
+ let mainCategory:string = categories[0].name;
+ let subCategory:string = '';
+ if(categories[0].subcategories) {
+ subCategory = categories[0].subcategories[0].name;
+ }
+ return this.$scope.calculateUnique(mainCategory, subCategory);
+ };
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html
new file mode 100644
index 0000000000..7fc3e9224f
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html
@@ -0,0 +1,40 @@
+<div class="hierarchy-step" data-ng-controller="Sdc.ViewModels.Wizard.HierarchyStepViewModel">
+ <div class="dropdown-container" clicked-outside="{onClickedOutside: 'clickOutside()', clickedOutsideEnable: 'true'}" >
+ <input placeholder="Add Group" data-ng-click="onInputTextClicked()" class="dropdown-input-text" data-ng-model="search.filterTerms" data-ng-model-options="{debounce: 200}"/>
+ <div data-ng-class="{'show': showDropDown}" class="dropdown-content" >
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="false" class="scrollbar-container">
+ <div ng-repeat="category in categoriesOptions track by $index">
+ <div ng-repeat="subcategory in category.subcategories track by $index">
+ <div class="dropdown-option" ng-show="!category.filteredGroup || category.filteredGroup.length > 0">
+ <div class="category-container">
+ <div class="category">{{category.name}}</div>
+ <div class="subcategory">{{subcategory.name}}</div>
+ </div>
+ <div class="groupings-container">
+ <div ng-init="group.filterTerms = group.name + ' ' + category.name + ' ' + subcategory.name"
+ ng-repeat="group in (category.filteredGroup = (subcategory.groupings | filter:search )) track by $index">
+ <div class="group" data-ng-disabled="group.isDisabled" data-ng-class="{'disabled-group': group.isDisabled}" ng-click="onGroupSelected(category, subcategory, group)">
+ <span >{{group.name}}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ <div class="hierarchy-groups-container">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div ng-if="!product.categories.length || product.categories.length === 0" class="no-groups-text" translate="NEW_PRODUCT_NO_CATEGORIES_TO_DISPLAY"></div>
+ <div ng-repeat="category in product.categories track by $index">
+ <div ng-repeat="subcategory in category.subcategories track by $index">
+ <div class="group-tag" ng-repeat="group in subcategory.groupings track by $index"
+ data-ng-init="tooltip = '<b>' + category.name + '</b><br />' + subcategory.name">
+ <sdc-tag data-on-delete="deleteGroup(uniqueId)" data-tag-data="{tag: group.name, tooltip: tooltip, id: group.uniqueId }"></sdc-tag>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less
new file mode 100644
index 0000000000..74786c127a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.less
@@ -0,0 +1,125 @@
+.hierarchy-step {
+ margin-top: 35px;
+
+ .scrollbar-container{
+ max-height:400px;
+ .perfect-scrollbar;
+ }
+
+ .dropdown-container {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+
+ &:after{
+ top: 47%;
+ right: 1%;
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-color: rgba(0, 0, 0, 0);
+ border-top-color: black;
+ border-width: 4px;
+ margin-left: -4px;
+ }
+
+ .dropdown-input-text {
+ width: 100%;
+ padding: 4px 10px;
+ }
+
+ .dropdown-content {
+ .perfect-scrollbar;
+ border: 1px solid #d8d8d8;
+ display: none;
+ position: absolute;
+ overflow: hidden;
+ width: 100%;
+ .bg_c;
+ max-height: 400px;
+ z-index: 999999;
+
+ .dropdown-option {
+ border-bottom: 1px solid #d8d8d8;
+ display: inline-block;
+ width: 100%;
+ }
+
+ .category-container{
+ width: 250px;
+ float: left;
+ padding-left: 5px;
+
+ .category {
+ .bold;
+ padding: 3px 3px 2px 3px;
+ &:after{
+ .sprite;
+ .arrow-left;
+ content: '';
+ margin-left: 5px;
+ transform: rotate(180deg);
+ }
+ }
+ .subcategory {
+ padding-left: 3px;
+ }
+ }
+
+ .groupings-container{
+ display: inline-block;
+ width: 424px;
+ border-left: 1px solid #d8d8d8;
+ min-height: 55px;
+ .group{
+ padding: 3px 3px 3px 10px;
+ &:hover{
+ .hand;
+ .bg_n;
+ }
+ &.disabled-group {
+ opacity: 0.5;
+ &:hover{
+ cursor: auto;
+ .bg_c;
+ }
+ }
+ }
+ }
+
+ .seperator {
+ height: 1px;
+ width: 100%;
+ .bg_j;
+ margin: 5px 0px;
+ }
+ }
+ .show {
+ display: block;
+ }
+ }
+
+ .hierarchy-groups-container{
+ .b_9;
+ width: 100%;
+ border: 1px solid #d8d8d8;
+ height: 425px;
+ padding: 15px;
+ text-align: center;
+
+ .no-group-text{
+ text-align: center;
+ margin-top:25px;
+ a {
+ cursor: pointer;
+ }
+ }
+ .group-tag{
+ display: inline-block;
+ float: left;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts
new file mode 100644
index 0000000000..a974c0af81
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.ts
@@ -0,0 +1,149 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export interface IHierarchyStepScope extends IWizardCreationScope {
+
+ categoriesOptions: Array<Models.IMainCategory>;
+ product:Models.Components.Product;
+ isLoading:boolean;
+ showDropDown:boolean;
+
+ onInputTextClicked():void;
+ onGroupSelected(category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup):void;
+ clickOutside():void;
+ deleteGroup(uniqueId:string):void;
+ }
+
+ export class HierarchyStepViewModel implements IWizardCreationStep {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.CacheService',
+ 'ComponentFactory'
+ ];
+
+ constructor(private $scope:IHierarchyStepScope,
+ private cacheService:Sdc.Services.CacheService,
+ private ComponentFactory: Sdc.Utils.ComponentFactory) {
+
+ this.$scope.registerChild(this);
+ this.$scope.setValidState(true);
+ this.$scope.product = <Models.Components.Product>this.$scope.getComponent();
+ this.initScope();
+ }
+
+ private initCategories = () => {
+ this.$scope.categoriesOptions = angular.copy(this.cacheService.get('productCategories'));
+ let selectedGroup:Array<Models.IGroup> = [];
+ _.forEach(this.$scope.product.categories, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ selectedGroup = selectedGroup.concat(subcategory.groupings);
+ });
+ });
+ _.forEach(this.$scope.categoriesOptions, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ _.forEach(subcategory.groupings, (group:Models.ISubCategory) => {
+ let componentGroup:Models.IGroup = _.find(selectedGroup, (componentGroupObj) => {
+ return componentGroupObj.uniqueId == group.uniqueId;
+ });
+ if(componentGroup){
+ group.isDisabled = true;
+ }
+ });
+ });
+ });
+ };
+
+ private setFormValidation = ():void => {
+ if(!this.$scope.product.categories || this.$scope.product.categories.length === 0){
+ this.$scope.setValidState(false);
+ }
+ else{
+ this.$scope.setValidState(true);
+ }
+
+ };
+
+ private initScope = ():void => {
+ this.$scope.isLoading= false;
+ this.$scope.showDropDown =false;
+ this.initCategories();
+ this.setFormValidation();
+
+ this.$scope.onGroupSelected = (category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup):void => {
+ this.$scope.showDropDown = false;
+ this.$scope.product.addGroup(category, subcategory, group);
+ group.isDisabled = true;
+ this.setFormValidation();
+ };
+
+ this.$scope.onInputTextClicked = ():void => {//just edit the component in place, no pop up nor server update ?
+ this.$scope.showDropDown = !this.$scope.showDropDown;
+ };
+
+ this.$scope.clickOutside = (): any => {
+ this.$scope.showDropDown = false;
+ };
+
+ this.$scope.deleteGroup = (uniqueId:string) : void => {
+ //delete group from component
+ this.$scope.product.deleteGroup(uniqueId);
+ this.setFormValidation();
+ //enabled group
+ _.forEach(this.$scope.categoriesOptions, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ let groupObj:Models.IGroup = _.find (subcategory.groupings, (group) => {
+ return group.uniqueId === uniqueId;
+ });
+ if(groupObj){
+ groupObj.isDisabled = false;
+ }
+ });
+ });
+ }
+ };
+
+ public save = (callback:Function):void => {
+ let onFailed = (response) => {
+ callback(false);
+ };
+
+ let onSuccess = (component: Models.Components.Component) => {
+ this.$scope.product = <Models.Components.Product> this.ComponentFactory.createComponent(component);
+ this.$scope.setComponent(this.$scope.product);
+ callback(true);
+ };
+
+ try {
+ this.$scope.product.updateComponent().then(onSuccess, onFailed);
+ }catch(e){
+ //console.log("ERROR: Error in updating/creating component: " + e);
+ callback(false);
+ }
+ };
+
+ public back = (callback:Function):void => {
+ this.save(callback);
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html
new file mode 100644
index 0000000000..2ae386283c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.html
@@ -0,0 +1,26 @@
+<div class="icons-step" data-ng-controller="Sdc.ViewModels.Wizard.IconsStepViewModel">
+
+ <form novalidate class="w-sdc-form" name="iconForm">
+ <label class="i-sdc-form-label icons-label required">Icons</label>
+ <div class="selected-icon-container">
+ <div class="i-sdc-form-item-suggested-icon medium selected-icon {{iconSprite}} {{component.icon}}"
+ ng-model="component.icon"
+ tooltips tooltip-content='{{component.icon | translate}}'
+ >
+ </div>
+ </div>
+
+ <label class="i-sdc-form-label icons-label required">Select one of the icons below for the asset</label>
+ <div class="i-sdc-form-item suggested-icons-container">
+ <div class ="suggested-icon-wrapper" ng-class="component.icon==='{{iconSrc}}' ? 'selected' : '' " data-ng-repeat="iconSrc in icons track by $index">
+ <div class="i-sdc-form-item-suggested-icon medium {{iconSprite}} {{iconSrc}}" data-ng-class="component.isAlreadyCertified()? 'disable':'hand'"
+ ng-model="component.icon"
+ data-tests-id="{{iconSrc}} iconBox"
+ data-ng-click="!component.isAlreadyCertified() && setComponentIcon(iconSrc)"
+ tooltips tooltip-content='{{iconSrc | translate}}'
+ >
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less
new file mode 100644
index 0000000000..c03c949962
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.less
@@ -0,0 +1,55 @@
+.icons-step {
+
+ .w-sdc-form {
+ padding-top: 0px;
+ padding-bottom: 0px;
+ .selected-icon-container {
+ text-align: left;
+ border: 1px solid #cfcfcf;
+ clear: both;
+ margin-bottom: 15px;
+ padding-left: 3px;
+ padding-bottom: 3px;
+ .selected-icon {
+ margin: 8px 5px 0px 6px;
+ }
+ }
+
+ .suggested-icons-container {
+ text-align: left;
+ border: 1px solid #cfcfcf;
+ clear: both;
+ padding-left: 3px;
+ height: 340px;
+ margin-bottom: 0px;
+
+ .suggested-icon-wrapper {
+ margin: 8px 5px 0px 6px;
+ display: inline-block;
+
+ &.selected {
+ border: 1px solid @color_p;
+ border-radius: 25px;
+ box-shadow: 0 0 2px #888;
+ display: inline-block;
+ line-height: 0px;
+ padding: 2px;
+ }
+
+ }
+ .suggested-icon {
+ // margin: 8px 5px 0px 6px;
+ display: inline-block;
+ &.disable{
+ opacity: 0.4;
+ }
+ }
+
+
+ }
+
+ .icons-label {
+ float: left;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts
new file mode 100644
index 0000000000..4dc5e377fa
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/icons-step/icons-step.ts
@@ -0,0 +1,150 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export interface IIconsStepScope extends IWizardCreationStepScope{
+ icons : Array<string>;
+ component: Models.Components.Component;
+ iconSprite: string;
+ setComponentIcon(iconSrc:string): void;
+ }
+
+ export class IconsStepViewModel implements IWizardCreationStep {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.AvailableIconsService',
+ 'ComponentFactory'
+ ];
+
+ constructor(private $scope:IIconsStepScope,
+ private availableIconsService:Services.AvailableIconsService,
+ private ComponentFactory: Sdc.Utils.ComponentFactory) {
+
+ this.$scope.registerChild(this);
+ this.$scope.component = this.$scope.getComponent();
+ this.$scope.iconSprite = this.$scope.component.iconSprite;
+ this.initScope();
+ this.initIcons();
+
+ if(this.$scope.component.isResource()) {
+ this.initVendor();
+ }
+ // In case there is one icons select it.
+ if( this.$scope.icons.length == 1 && !this.$scope.component.isAlreadyCertified()){
+ this.$scope.setComponentIcon(this.$scope.icons[0]);
+ }
+ }
+
+ private initIcons = ():void => {
+
+ // For subcategories that where created by admin, there is no icons
+ this.$scope.icons = new Array<string>();
+ if (this.$scope.component.categories && this.$scope.component.categories.length > 0) {
+
+ _.forEach(this.$scope.component.categories, (category:Models.IMainCategory):void => {
+ if (category.icons) {
+ this.$scope.icons = this.$scope.icons.concat(category.icons);
+ }
+ if (category.subcategories) {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory):void => {
+ if (subcategory.icons) {
+ this.$scope.icons = this.$scope.icons.concat(subcategory.icons);
+ }
+ });
+ }
+ });
+ }
+
+ if (this.$scope.component.isResource()) {
+ let resourceType:string = this.$scope.component.getComponentSubType();
+ if (resourceType === 'VL') {
+ this.$scope.icons = ['vl'];
+ }
+ if (resourceType === 'CP') {
+ this.$scope.icons = ['cp'];
+ }
+ }
+
+ if (this.$scope.icons.length === 0) {
+ this.$scope.icons = this.availableIconsService.getIcons(this.$scope.component.componentType);
+ }
+
+ };
+
+ private initVendor = ():void => {
+ let vendors:Array<string> = this.availableIconsService.getIcons(this.$scope.component.componentType).slice(5, 19);
+ let vendorName = this.$scope.component.vendorName.toLowerCase();
+ if ('at&t' === vendorName){
+ vendorName = 'att';
+ }
+ if ('nokia' === vendorName){
+ vendorName = 'nokiasiemens';
+ }
+
+ let vendor:string = _.find(vendors, (vendor:string)=>{
+ return vendor.replace(/[_]/g, '').toLowerCase() === vendorName;
+ });
+
+ if(vendor && this.$scope.icons.indexOf(vendor)===-1) {
+ this.$scope.icons.push(vendor);
+ }
+ };
+
+ private initScope():void {
+ this.$scope.icons = [];
+
+ if(this.$scope.component.icon === Utils.Constants.DEFAULT_ICON){
+ this.$scope.setValidState(false);
+ }
+
+ this.$scope.setComponentIcon = (iconSrc:string):void => {
+ this.$scope.component.icon = iconSrc;
+ this.$scope.setValidState(true);
+ }
+ }
+
+ save(callback:Function):void {
+ let onFailed = () => {
+ callback(false);
+ };
+
+ let onSuccess = (component:Models.Components.Component) => {
+ this.$scope.component = component;
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ };
+
+ try {
+ this.$scope.component.updateComponent().then(onSuccess, onFailed);
+ }catch(e){
+ callback(false);
+ }
+ }
+
+ public back = (callback:Function):void => {
+ this.save(callback);
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html
new file mode 100644
index 0000000000..4429451871
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.html
@@ -0,0 +1,57 @@
+<div class="properties-step" data-ng-controller="Sdc.ViewModels.Wizard.PropertiesStepViewModel">
+
+ <div class="w-sdc-classic-btn gray" data-tests-id="addGrey" data-ng-click="addOrUpdateProperty()">Add</div>
+
+ <div class="table-container-flex">
+
+ <div class="table">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ <!--div class="table-no-text-header head-row flex-item"></div-->
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="component.properties.length === 0 " class="no-row-text">
+ There are no properties to display <br>
+ click <a data-ng-click="addOrUpdateProperty()">here</a> to add one
+ </div>
+ <div data-ng-repeat-start="property in component.properties | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row" data-ng-class="{'selected': property.selected}"
+ data-ng-click="property.selected = !property.selected">
+
+ <div class="table-col-general flex-item text">
+ <span class="sprite table-arrow" data-ng-class="{'opened': property.selected}"></span>
+ <span data-tests-id="{{property.name}}" tooltips tooltip-content="{{property.name}}">{{property.name}}</span>
+
+ </div>
+
+ <div class="table-col-general flex-item text" data-tests-id="{{property.type}}" data-ng-bind="property.type"></div>
+
+ <div class="table-col-general flex-item text">
+ <span tooltips tooltip-content="{{property.defaultValue}}" data-tests-id="{{property.defaultValue}}" data-ng-bind="property.defaultValue"></span>
+ </div>
+
+ <div class="table-btn-col flex-item">
+ <button class="table-edit-btn" data-ng-show="property.parentUniqueId==component.uniqueId"
+ data-ng-click="addOrUpdateProperty(property); $event.stopPropagation();"> </button>
+ <button class="table-delete-btn" data-ng-show="property.parentUniqueId==component.uniqueId"
+ data-ng-click="delete(property); $event.stopPropagation();"> </button>
+ </div>
+
+ <!--div class="table-btn-col flex-item">
+
+ </div-->
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="property.selected && property.description" class="item-opened" data-ng-bind="property.description">
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less
new file mode 100644
index 0000000000..0d7dad8dc2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.less
@@ -0,0 +1,55 @@
+.properties-step {
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table{
+ height: 412px;
+ margin-bottom: 0;
+ }
+
+ .table-container-flex {
+ margin-top: 0px;
+
+ .text{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ padding: 5px 4px;
+
+ }
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 9;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 3;
+ //padding-top: 10px;
+ padding: 10px 4px;
+
+ }
+
+ .flex-item:nth-child(5) {
+ flex-grow: 1;
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts
new file mode 100644
index 0000000000..08dfb5e153
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/properties-step/properties-step.ts
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ interface IPropertiesStepViewModelScope extends IWizardCreationStepScope {
+ component: Models.Components.Component;
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+
+ addOrUpdateProperty(): void;
+ delete(property: Models.PropertyModel): void;
+ sort(sortBy:string): void;
+ }
+
+ export class PropertiesStepViewModel implements IWizardCreationStep {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ModalsHandler'
+ ];
+
+
+ constructor(
+ private $scope:IPropertiesStepViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private ModalsHandler: Utils.ModalsHandler
+ ){
+
+ this.$scope.registerChild(this);
+ this.$scope.setValidState(true);
+ this.initScope();
+ }
+
+ public save = (callback:Function):void => {
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ };
+
+ public back = (callback:Function):void => {
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ }
+
+
+ private openEditPropertyModal = (property: Models.PropertyModel): void => {
+ let viewModelsHtmlBasePath: string = '/app/scripts/view-models/';
+
+ let modalOptions: ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath+'wizard/property-form/property-form.html'),
+ controller: 'Sdc.ViewModels.Wizard.PropertyFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ property: (): Models.PropertyModel => {
+ return property;
+ },
+ component: (): Models.Components.Component => {
+ return <Models.Components.Component> this.$scope.getComponent();
+ }
+ }
+ };
+ this.$modal.open(modalOptions);
+ };
+
+ private initScope = (): void => {
+
+ let self = this;
+ this.$scope.component = this.$scope.getComponent();
+ this.$scope.sortBy = 'name';
+ this.$scope.reverse = false;
+
+ this.$scope.tableHeadersList = [
+ {title:'Name', property: 'name'},
+ {title:'Type', property: 'type'},
+ {title:'Default Value', property: 'defaultValue'}
+ ];
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+ this.$scope.addOrUpdateProperty = (property?: Models.PropertyModel): void => {
+ this.openEditPropertyModal(property ? property : new Models.PropertyModel());
+ };
+
+ this.$scope.delete = (property: Models.PropertyModel): void => {
+
+ let onOk = (): void => {
+ this.$scope.component.deleteProperty(property.uniqueId);
+ };
+ let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts
new file mode 100644
index 0000000000..3f390841ca
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model-tests.ts
@@ -0,0 +1,163 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+describe("property form View Model ", () => {
+
+ let $controllerMock:ng.IControllerService;
+ let $qMock:ng.IQService;
+ let $httpBackendMock:ng.IHttpBackendService;
+ let $scopeMock:Sdc.ViewModels.Wizard.IPropertyFormViewModelScope;
+ let $stateMock:ng.ui.IStateService;
+ let $stateParams:any;
+ let component = {
+ "uniqueId": "ece818e0-fd59-477a-baf6-e27461a7ce23",
+ "uuid": "8db823c2-6a9c-4636-8676-f5e713270dd7",
+ "contactId": "uf2345",
+ "category": "Network Layer 2-3/Router",
+ "creationDate": 1447235352429,
+ "description": "u",
+ "highestVersion": true,
+ "icon": "network",
+ "lastUpdateDate": 1447235370064,
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "name": "u",
+ "version": "0.1",
+ "type": 1,
+ "tags": [
+ "u"
+ ],
+ "vendorName": "u",
+ "vendorRelease": "u",
+ "systemName": "U",
+ "$$hashKey": "object:34"
+ };
+
+
+ beforeEach(angular.mock.module('sdcApp'));
+
+ beforeEach(angular.mock.inject((_$controller_:ng.IControllerService,
+ _$httpBackend_:ng.IHttpBackendService,
+ _$rootScope_,
+ _$q_:ng.IQService,
+ _$state_:ng.ui.IStateService,
+ _$stateParams_:any) => {
+
+ $controllerMock = _$controller_;
+ $httpBackendMock = _$httpBackend_
+ $scopeMock = _$rootScope_.$new();
+ $qMock = _$q_;
+ $stateMock = _$state_;
+ $stateParams = _$stateParams_;
+
+
+ //handle all http request thet not relevant to the tests
+ $httpBackendMock.expectGET(/.*languages\/en_US.json.*/).respond(200, JSON.stringify({}));
+ // $httpBackendMock.expectGET(/.*resources\/certified\/abstract.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*rest\/version.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*configuration\/ui.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*user\/authorize.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/services.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/resources.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET(/.*categories\/products.*/).respond(200, JSON.stringify({}));
+ $httpBackendMock.expectGET('http://feHost:8181/sdc1/feProxy/rest/version').respond(200, JSON.stringify({}));
+
+ /**
+ * Mock the service
+ * @type {any}
+ */
+ //getAllEntitiesDefered = $qMock.defer();
+ //getAllEntitiesDefered.resolve(getAllEntitiesResponseMock);
+ //entityServiceMock = jasmine.createSpyObj('entityServiceMock', ['getAllComponents']);
+ //entityServiceMock.getAllComponents.and.returnValue(getAllEntitiesDefered.promise);
+
+ // $stateParams['show'] = '';
+
+ /**
+ * Need to inject into the controller only the objects that we want to MOCK
+ * those that we need to change theirs behaviors
+ */
+ $controllerMock(Sdc.ViewModels.Wizard.PropertyFormViewModel, {
+ '$scope': $scopeMock,
+ 'property': new Sdc.Models.PropertyModel(),
+ 'component': component,
+ });
+
+ }));
+
+ describe("when Controller 'PropertyFormViewModel' created", () => {
+
+ it('should have a regexp per each type', () => {
+ $scopeMock.$apply();
+ expect(Object.keys($scopeMock.listRegex).length).toBe($scopeMock.editPropertyModel["simpleTypes"].length);
+ });
+
+ it('should have equal regexps for map and list', () => {
+ $scopeMock.$apply();
+ expect(Object.keys($scopeMock.listRegex).length).toBe(Object.keys($scopeMock.mapRegex).length);
+ });
+
+ });
+
+ /*describe("when Controller 'DashboardViewModel' created", () => {
+
+ it('should generate all entities', () => {
+ $scopeMock.$apply();
+ expect($scopeMock.components.length).toBe(getAllEntitiesResponseMock.length);
+ });
+
+
+ it('should show tutorial page ', () => {
+ $stateParams.show = 'tutorial';
+
+ $controllerMock(Sdc.ViewModels.DashboardViewModel, {
+ '$scope': $scopeMock,
+ '$stateParams': $stateParams,
+ 'Sdc.Services.EntityService': entityServiceMock,
+ //to complete injects
+ });
+
+ $scopeMock.$apply();
+ expect($scopeMock.isFirstTime).toBeTruthy();
+ expect($scopeMock.showTutorial).toBeTruthy();
+ });
+
+ });
+
+
+ describe("when function 'entitiesCount' invoked", () => {
+
+ beforeEach(() => {
+ $controllerMock(Sdc.ViewModels.DashboardViewModel, {
+ '$scope': $scopeMock,
+ 'Sdc.Services.EntityService': entityServiceMock,
+ });
+ $scopeMock.$apply();
+ });
+
+ it('should return entities count per folder', () => {
+
+ });
+
+
+ });*/
+});
diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts
new file mode 100644
index 0000000000..5cb0ef1ddd
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form-view-model.ts
@@ -0,0 +1,250 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export interface IEditPropertyModel{
+ property: Models.PropertyModel;
+ types: Array<string>;
+ simpleTypes: Array<string>;
+ sources: Array<string>;
+ }
+
+ export interface IPropertyFormViewModelScope extends ng.IScope{
+
+ $$childTail: any;
+ editForm:ng.IFormController;
+ forms:any;
+ footerButtons: Array<any>;
+ isNew: boolean;
+ isLoading: boolean;
+ validationPattern: RegExp;
+ propertyNameValidationPattern: RegExp;
+ integerValidationPattern: RegExp;
+ floatValidationPattern: RegExp;
+ commentValidationPattern: RegExp;
+ listRegex: Sdc.Utils.IMapRegex;
+ mapRegex: Sdc.Utils.IMapRegex;
+ editPropertyModel: IEditPropertyModel;
+ modalInstanceProperty: ng.ui.bootstrap.IModalServiceInstance;
+ save(doNotCloseModal?: boolean): void;
+ saveAndAnother(): void;
+ getValidation(): RegExp;
+ validateIntRange(value:string):boolean;
+ close(): void;
+ onValueChange(): void;
+ onTypeChange(resetSchema:boolean): void;
+ showSchema(): boolean;
+ getValidationTranslate():string;
+ validateUniqueKeys(viewValue:string):boolean;
+ }
+
+ export class PropertyFormViewModel{
+
+ private originalValue: string;
+
+ static '$inject' = [
+ '$scope',
+ '$modalInstance',
+ 'property',
+ 'ValidationPattern',
+ 'PropertyNameValidationPattern',
+ 'IntegerNoLeadingZeroValidationPattern',
+ 'FloatValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ 'component'
+ ];
+
+ private formState: Utils.Constants.FormState;
+ private entityId: string;
+ private resourceInstanceUniqueId: string;
+ private readonly: boolean;
+
+ constructor(
+ private $scope:IPropertyFormViewModelScope,
+ private $modalInstance: ng.ui.bootstrap.IModalServiceInstance,
+ private property : Models.PropertyModel,
+ private ValidationPattern : RegExp,
+ private PropertyNameValidationPattern: RegExp,
+ private IntegerNoLeadingZeroValidationPattern : RegExp,
+ private FloatValidationPattern : RegExp,
+ private CommentValidationPattern: RegExp,
+ private ValidationUtils: Sdc.Utils.ValidationUtils,
+ private component:Models.Components.Component
+ ){
+ this.entityId = this.component.uniqueId;
+ this.formState = angular.isDefined(property.name) ? Utils.Constants.FormState.UPDATE : Utils.Constants.FormState.CREATE;
+ this.initScope();
+ }
+
+
+ private initResource = (): void => {
+ this.$scope.editPropertyModel.property = new Sdc.Models.PropertyModel(this.property);
+ this.originalValue = this.property.defaultValue;
+ if(this.$scope.editPropertyModel.types.indexOf(this.property.type) === -1 && !this.$scope.isNew){
+ this.property.type = "string";
+ }
+ };
+
+ private initEditPropertyModel = (): void => {
+ this.$scope.editPropertyModel = {
+ property: null,
+ types: ['integer', 'string', 'float', 'boolean', 'list', 'map'],
+ simpleTypes: ['integer', 'string', 'float', 'boolean'],
+ sources: ['A&AI', 'Order', 'Runtime']
+ };
+
+ this.initResource();
+ };
+
+ private initScope = (): void => {
+
+ this.$scope.modalInstanceProperty = this.$modalInstance;
+ //scope properties
+ this.$scope.validationPattern = this.ValidationPattern;
+ this.$scope.propertyNameValidationPattern = this.PropertyNameValidationPattern;
+ this.$scope.integerValidationPattern = this.IntegerNoLeadingZeroValidationPattern;
+ this.$scope.floatValidationPattern = this.FloatValidationPattern;
+ this.$scope.commentValidationPattern = this.CommentValidationPattern;
+
+ //map & list validation patterns
+ this.$scope.listRegex = this.ValidationUtils.getPropertyListPatterns();
+ this.$scope.mapRegex = this.ValidationUtils.getPropertyMapPatterns();
+
+ this.$scope.isLoading = false;
+ this.$scope.isNew = (this.formState === Utils.Constants.FormState.CREATE);
+ this.initEditPropertyModel();
+
+ //scope methods
+ this.$scope.save = (): void => {
+ this.$scope.editPropertyModel.property.description = this.ValidationUtils.stripAndSanitize(this.$scope.editPropertyModel.property.description);
+ this.$scope.isLoading = true;
+
+ let onFailed = (response) => {
+ console.info('onFaild',response);
+ this.$scope.isLoading = false;
+ this.$scope.editPropertyModel.property.readonly = this.readonly;
+ this.$scope.editPropertyModel.property.resourceInstanceUniqueId = this.resourceInstanceUniqueId;
+ };
+
+ let onSuccess = (property: Models.PropertyModel): void => {
+ console.info('property added : ',property);
+ this.$scope.isLoading = false;
+ property.resourceInstanceUniqueId = this.resourceInstanceUniqueId;
+ property.readonly = (property.parentUniqueId !== this.component.uniqueId) /*|| this.component.isService()*/;
+
+ this.$modalInstance.close();
+ };
+
+ this.resourceInstanceUniqueId = this.$scope.editPropertyModel.property.resourceInstanceUniqueId;
+ this.readonly = this.$scope.editPropertyModel.property.readonly;
+ this.$scope.editPropertyModel.property.defaultValue = this.$scope.editPropertyModel.property.defaultValue ? this.$scope.editPropertyModel.property.defaultValue:null;
+
+ this.component.addOrUpdateProperty(this.$scope.editPropertyModel.property).then(onSuccess, onFailed);
+ };
+
+ this.$scope.saveAndAnother = (): void => {
+ this.$scope.save();
+ };
+
+ this.$scope.showSchema = () :boolean => {
+ return ['list', 'map'].indexOf(this.$scope.editPropertyModel.property.type) > -1;
+ };
+
+ this.$scope.getValidationTranslate = () : string => {
+ let result = "PROPERTY_EDIT_PATTERN";
+ if (this.$scope.showSchema()) {
+
+ result = "PROPERTY_EDIT_" + this.$scope.editPropertyModel.property.type.toUpperCase();
+
+ if(this.$scope.editPropertyModel.property.schema.property.type === 'string') {
+ result += "_STRING";
+ } else {
+ result += "_GENERIC";
+ }
+ }
+
+ return result;
+ };
+
+ this.$scope.getValidation = () : RegExp => {
+ let type = this.$scope.editPropertyModel.property.type;
+ switch (type){
+ case 'integer':
+ return this.$scope.integerValidationPattern;
+ case 'float':
+ return this.$scope.floatValidationPattern;
+ case 'list':
+ return this.$scope.listRegex[this.$scope.editPropertyModel.property.schema.property.type];
+ case 'map':
+ return this.$scope.mapRegex[this.$scope.editPropertyModel.property.schema.property.type];
+ default :
+ return null;
+ }
+ };
+
+ this.$scope.validateUniqueKeys = (viewValue:string) : boolean => {
+ if(this.$scope.editPropertyModel.property.type === 'map') {
+ return this.ValidationUtils.validateUniqueKeys(viewValue);
+ }
+ else {
+ return true; //always valid if not a map
+ }
+ };
+
+ this.$scope.validateIntRange = (value:string):boolean => {
+ return !value || this.ValidationUtils.validateIntRange(value);
+ };
+
+ this.$scope.close = (): void => {
+ this.$modalInstance.close();
+ };
+
+ this.$scope.onValueChange = (): void => {
+ if(!this.$scope.editPropertyModel.property.defaultValue && this.$scope.editPropertyModel.property.required) {
+ this.$scope.editPropertyModel.property.defaultValue = this.originalValue;
+ }
+ };
+
+ this.$scope.onTypeChange = (resetSchema:boolean): void => {
+ this.$scope.editPropertyModel.property.defaultValue = '';
+ if (resetSchema) {
+ this.$scope.editPropertyModel.property.schema.property.type = '';
+ }
+ };
+
+ //new form layout for import asset
+ this.$scope.forms = {};
+ this.$scope.footerButtons = [
+ {'name': this.$scope.isNew ? 'Add' : 'Update', 'css':'blue', 'callback': this.$scope.save},
+ {'name':'Cancel', 'css':'grey', 'callback': this.$scope.close}
+ ];
+
+ this.$scope.$watch('forms.editForm.$invalid', () => {
+ this.$scope.footerButtons[0].disabled = this.$scope.forms.editForm.$invalid;
+ });
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html
new file mode 100644
index 0000000000..be237112a4
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.html
@@ -0,0 +1,133 @@
+<sdc-modal modal="modalInstanceProperty" type="classic" class="sdc-add-property" buttons="footerButtons" header="{{isNew ? 'Add' : 'Update' }} Property" show-close-button="true">
+
+ <form novalidate class="w-sdc-form two-columns" name="forms.editForm" >
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Name</label>
+ <input class="i-sdc-form-input"
+ data-ng-maxlength="50"
+ data-ng-disabled="!isNew || editPropertyModel.property.readonly"
+ maxlength="50"
+ data-ng-model="editPropertyModel.property.name"
+ type="text"
+ name="propertyName"
+ data-ng-pattern="propertyNameValidationPattern"
+ data-required
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="propertyName"
+ autofocus />
+
+ <div class="input-error" data-ng-show="forms.editForm.propertyName.$dirty && forms.editForm.propertyName.$invalid">
+ <span ng-show="forms.editForm.propertyName.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property name' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="forms.editForm.propertyName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid)}"
+ data-ng-if="showSchema()">
+ <label class="i-sdc-form-label required">Entry Schema</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="schemaType"
+ data-tests-id="schemaType"
+ data-ng-change="onTypeChange(false)"
+ data-ng-model="editPropertyModel.property.schema.property.type"
+ data-ng-options="type for type in editPropertyModel.simpleTypes">
+ <option value="">Choose Schema Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.schemaType.$dirty && forms.editForm.schemaType.$invalid">
+ <span ng-show="forms.editForm.schemaType.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Entry schema' }"></span>
+ </div>
+ </div>
+
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.value.$dirty && forms.editForm.value.$invalid)}">
+ <label class="i-sdc-form-label">Default Value</label>
+ <input class="i-sdc-form-input"
+ ng-if="!(editPropertyModel.property.type == 'boolean')"
+ data-ng-maxlength="100"
+ data-ng-disableddddddd="editPropertyModel.property.readonly && !isService && !isPropertyValueOwner()"
+ maxlength="100"
+ data-ng-model="editPropertyModel.property.defaultValue"
+ type="text"
+ name="value"
+ data-custom-validation="" data-validation-func="validateUniqueKeys"
+ data-ng-pattern="getValidation()"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-change="!forms.editForm.value.$error.pattern && ('integer'==editPropertyModel.property.type && forms.editForm.value.$setValidity('pattern', validateIntRange(editPropertyModel.property.defaultValue)) || onValueChange())"
+ data-tests-id="defaultValue"
+ autofocus />
+ <select class="i-sdc-form-select"
+ ng-if="editPropertyModel.property.type == 'boolean'"
+ data-ng-disabled="editPropertyModel.property.readonly && !isPropertyValueOwner()"
+ name="value"
+ data-ng-change="onValueChange()"
+ data-ng-model="editPropertyModel.property.defaultValue">
+ <option value=""></option>
+ <option value="true">true</option>
+ <option value="false">false</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.value.$dirty && forms.editForm.value.$invalid">
+ <span ng-show="forms.editForm.value.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Property' }"></span>
+ <span ng-show="forms.editForm.value.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '100' }"></span>
+ <span ng-show="forms.editForm.value.$error.pattern" translate="{{getValidationTranslate()}}"></span>
+ <span ng-show="forms.editForm.value.$error.customValidation" translate="PROPERTY_EDIT_MAP_UNIQUE_KEYS"></span>
+ </div>
+ </div>
+
+ </div>
+
+ <div class="w-sdc-form-column">
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.type.$dirty && forms.editForm.type.$invalid)}">
+ <label class="i-sdc-form-label" ng-class="{'required': !isService}">Type</label>
+ <select class="i-sdc-form-select"
+ data-required
+ data-ng-disableddddddd="editPropertyModel.property.readonly"
+ data-tests-id="propertyType"
+ name="type"
+ data-ng-change="onTypeChange(true)"
+ data-ng-model="editPropertyModel.property.type"
+ data-ng-options="type for type in editPropertyModel.types">
+ <option value="">Choose Type</option>
+ </select>
+
+ <div class="input-error" data-ng-show="forms.editForm.type.$dirty && forms.editForm.type.$invalid">
+ <span ng-show="forms.editForm.type.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Type' }"></span>
+ </div>
+ </div>
+
+ <div class="i-sdc-form-item" data-ng-class="{error:(forms.editForm.description.$dirty && forms.editForm.description.$invalid)}">
+ <label class="i-sdc-form-label">Description</label>
+ <textarea class="i-sdc-form-textarea"
+ data-ng-maxlength="256"
+ data-ng-disableddddddd="editPropertyModel.property.readonly"
+ maxlength="256"
+ data-ng-pattern="commentValidationPattern"
+ name="description"
+ data-ng-model="editPropertyModel.property.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-tests-id="description"
+ ></textarea>
+
+ <div class="input-error" data-ng-show="forms.editForm.description.$dirty && forms.editForm.description.$invalid">
+ <span ng-show="forms.editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="forms.editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="forms.editForm.description.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Description' }"></span>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+ </form>
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less
new file mode 100644
index 0000000000..52b8564fdb
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/property-form/property-form.less
@@ -0,0 +1,7 @@
+.sdc-add-property{
+
+ .w-sdc-form {
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html
new file mode 100644
index 0000000000..afa9307265
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.html
@@ -0,0 +1,12 @@
+<loader data-display="isLoading"></loader>
+<div class="sdc-wizard-name-type-label" data-ng-if="assetName!==undefined"><span class="sprite sprite-green-tick animated flash"></span><span class="name">{{assetName}}</span>&nbsp;|&nbsp;<span class="type">{{assetType}}</span></div>
+<sdc-modal modal="modalInstance" type="classic" get-close-modal-response="getComponent" buttons="footerButtons" class="sdc-wizard" header="{{modalTitle}}" show-close-button="true">
+ <div class="sdc-wizard-wrapper">
+ <div class="sdc-wizard-left-content">
+ <sdc-wizard-step steps="directiveSteps" control="assetCreationControl" class="wizard-steps-line"></sdc-wizard-step>
+ </div>
+ <div class="sdc-wizard-right-content">
+ <ng-include src="templateUrl" class="sdc-wizard-right-include"></ng-include>
+ </div>
+ </div>
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less
new file mode 100644
index 0000000000..591186789b
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.less
@@ -0,0 +1,60 @@
+.sdc-wizard-wrapper {
+ display: flex;
+ padding: 80px 0;
+ height: 620px;
+ // So input validation error will be shown outside the modal (long messages).
+ // overflow: hidden;
+
+ .sdc-wizard-left-content {
+ width: 400px;
+ white-space: nowrap;
+
+ .wizard-steps-line {
+ padding: 0 80px 0 0;
+ display: block;
+ }
+
+ }
+
+ .sdc-wizard-right-content {
+ margin-right: 200px;
+ /* background-color: #fafafa; */
+ height: 100%;
+ width: 100%;
+ .perfect-scrollbar;
+ overflow: visible;
+ //.animation-duration(2s);
+
+ .sdc-wizard-right-include {
+
+ }
+ }
+
+}
+
+.sprite-green-tick.animated {
+ .animation-duration(3s);
+}
+
+.sdc-wizard-name-type-label {
+ position: absolute;
+ top: 64px;
+ right: 40px;
+
+ span {
+ .b_7;
+ &.name {.bold;}
+ &.sprite-green-tick {
+ position: absolute;
+ left: -22px;
+ top: 5px;
+ }
+ }
+
+}
+
+.w-wizard-footer {
+ button.cancel {
+ margin-right: 120px;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts
new file mode 100644
index 0000000000..365d3aedf6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/wizard-creation-base.ts
@@ -0,0 +1,399 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export class StepNames {
+ static general = "General";
+ static icon = "Icon";
+ static deploymentArtifact = "Deployment Artifact";
+ static informationArtifact = "Information Artifact";
+ static properties = "Properties";
+ static hierarchy = "Hierarchy";
+ }
+
+ class FooterButtons {
+ static cancel = "Cancel";
+ static back = "Back";
+ static next = "Next";
+ static finish = "Finish";
+ }
+
+ export class WizardCreationTypes {
+ static importAsset = "importAsset";
+ static create = "create";
+ static edit = "edit";
+ }
+
+ export class _CreationStep {
+ name:string;
+ url:string;
+ }
+
+ export class CurrentStep {
+ assetCreationStep:_CreationStep;
+ reference:IWizardCreationStep;
+ index:number;
+ valid: boolean;
+ }
+
+ export interface IWizardCreationStep {
+ save(callback:Function):void;
+ back(callback:Function):void;
+ }
+
+ export interface IWizardCreationStepScope extends ng.IScope {
+ data:any;
+ getComponent():Sdc.Models.Components.Component;
+ setComponent(component: Sdc.Models.Components.Component):void;
+ registerChild(child:IWizardCreationStep):void; // Called from the step
+ setValidState(valid:boolean):void; // Called from the step
+ }
+
+ export interface IWizardCreationScope extends ng.IScope {
+ isLoading: boolean;
+ data:any; // data passed from dashboard (opener), need it on the scope, because general step will use this (extends the scope)
+ directiveSteps: Array<Sdc.Directives.IWizardStep>; // Steps for the directive, on the scope (on the scope because need to pass to directive via HTML)
+ templateUrl: string; // On the scope because need to pass to <ng-include> via HTML
+ footerButtons: Array<Sdc.Directives.ISdcModalButton>; // Wizard footer buttons (on the scope because need to pass to directive via HTML)
+ assetCreationControl:any; // Link to wizard directive functions.
+ modalInstance:ng.ui.bootstrap.IModalServiceInstance; // Reference to the modal, so we can close it (on the scope because need to pass to directive via HTML)
+ assetName:string;
+ assetType:string;
+ modalTitle:string;
+ getComponent():Sdc.Models.Components.Component;
+ setComponent(component: Sdc.Models.Components.Component):void;
+ registerChild(child:IWizardCreationStep):void; // Called from the step
+ setValidState(valid:boolean):void; // Called from the step
+ }
+
+ export class WizardCreationBaseViewModel {
+
+ component: Sdc.Models.Components.Component;
+ protected assetCreationSteps: Array<_CreationStep>; // Contains URL and name so we can replace them
+ currentStep:CurrentStep;
+ protected type:string;
+
+ constructor(protected $scope:IWizardCreationScope,
+ protected data:any,
+ protected ComponentFactory: Utils.ComponentFactory,
+ protected $modalInstance: ng.ui.bootstrap.IModalServiceInstance
+ ) {
+
+ this.$scope.data = data;
+ this.currentStep = new CurrentStep();
+ this.currentStep.valid=false;
+ this.$scope.modalInstance = this.$modalInstance;
+ this.initScope();
+ this.noBackspaceNav();
+
+ // In case the modal was opened with filled resource (edit mode).
+ if (data.component){
+ this.$scope.setComponent(data.component);
+ data.componentType = this.$scope.getComponent().componentType;
+ window.setTimeout(()=>{
+ this.safeApply(this.setCurrentStepByIndex(0, false));
+ },100);
+ } else {
+ // Default step to start with
+ window.setTimeout(()=>{
+ this.safeApply(this.setCurrentStepByIndex(0, false));
+ },100);
+ }
+
+ }
+
+ private safeApply = (fn:any) => {
+ let phase = this.$scope.$root.$$phase;
+ if (phase == '$apply' || phase == '$digest') {
+ if (fn && (typeof(fn) === 'function')) {
+ fn();
+ }
+ } else {
+ this.$scope.$apply(fn);
+ }
+ };
+
+ private initScope = ():void => {
+
+ // Control to call functions on wizard step directive
+ this.$scope.assetCreationControl = {};
+
+ // Footer buttons definitions for the modal directive.
+ this.$scope.footerButtons = [
+ {"name":FooterButtons.cancel, "css":'white cancel',"callback": ()=>{this.btnCancelClicked();}},
+ {"name":FooterButtons.back, "disabled":true, "css":'white back',"callback": ()=>{this.btnBackClicked();}},
+ {"name":FooterButtons.next, "disabled":true, "css":'blue next',"callback": ()=>{this.btnNextClicked();}},
+ {"name":FooterButtons.finish, "disabled":true, "css":'white finish',"callback": ()=>{this.btnFinishedClicked();}}
+ ];
+
+ // Will be called from step constructor to register him.
+ // So the current step will be the reference.
+ this.$scope.registerChild=(child:IWizardCreationStep):void => {
+ this.currentStep.reference=child;
+ };
+
+ // Will be called from each step to notify if the step is valid
+ // The wizard will set the "Next", "Finish" buttons accordingly.
+ this.$scope.setValidState = (valid:boolean):void => {
+ this.currentStep.valid=valid;
+ let currentDirectiveStep:Sdc.Directives.IWizardStep = this.$scope.directiveSteps[this.currentStep.index];
+ this.$scope.assetCreationControl.setStepValidity(currentDirectiveStep, valid);
+ this.footerButtonsStateMachine();
+ this.wizardButtonsIconsStateMachine();
+ };
+
+ /**
+ * Will be called from each step to get current entity.
+ * This will return copy of the entity (not reference), because I do not want that the step will change entity parameters.
+ * If the step need to update the entity it can call setComponent function.
+ * @returns {Sdc.Models.IEntity}
+ */
+ this.$scope.getComponent = ():Sdc.Models.Components.Component => {
+ return this.component;
+ };
+
+ // Will be called from each step after save to update the resource.
+ this.$scope.setComponent = (component:Sdc.Models.Components.Component):void => {
+ this.component = component;
+ };
+
+ };
+
+ protected setCurrentStepByName = (stepName:string):boolean => {
+ let stepIndex:number = this.getStepIndex(stepName);
+ return this.setCurrentStepByIndex(stepIndex);
+ };
+
+ // Set the current step, change the URL in ng-include.
+ protected setCurrentStepByIndex = (index:number, doSave:boolean=true):boolean => {
+ let result:boolean = false;
+ if (this.currentStep.index!==index) { // Check that not pressing on same step, also the first time currentStepIndex=undefined
+ if (doSave===true) {
+ this.callStepSave(() => {
+ // This section will be executed only if success save.
+
+ // Set current step in the left wizard directive = valid
+ let currentDirectiveStep:Sdc.Directives.IWizardStep = this.$scope.directiveSteps[this.currentStep.index];
+ this.$scope.assetCreationControl.setStepValidity(currentDirectiveStep, true);
+
+ // Move to next step
+ let step:_CreationStep = this.assetCreationSteps[index];
+ this.currentStep.index = index;
+ this.currentStep.assetCreationStep = step;
+ this.$scope.templateUrl = step.url;
+
+ // Update the next/back buttons and steps buttons.
+ this.footerButtonsStateMachine();
+ this.wizardButtonsIconsStateMachine();
+
+ // Can not perform step click without enabling the step
+ this.$scope.directiveSteps[index].enabled = true; // Need to set the step enabled, before clicking it.
+ this.$scope.assetCreationControl.stepClicked(step.name);
+
+ // After saving the asset name and type will be shown in the top right of the screen.
+ this.fillAssetNameAndType();
+
+ result=true;
+ });
+ } else {
+ // For the first time
+ let step:_CreationStep = this.assetCreationSteps[index];
+ this.currentStep.index = index;
+ this.currentStep.assetCreationStep=step;
+ this.$scope.templateUrl = step.url;
+ this.$scope.directiveSteps[index].enabled = true; // Need to set the step enabled, before clicking it.
+ this.$scope.assetCreationControl.stepClicked(step.name);
+ result=true;
+ }
+
+ //this.updateFooterButtonsStates();
+
+ } else {
+ result=true;
+ }
+ return result;
+ };
+
+ // Save the current step
+ private callStepSave = (successCallback:Function):void => {
+ this.$scope.isLoading = true;
+ this.currentStep.reference.save((result:boolean)=>{
+ this.$scope.isLoading = false;
+ if (result===true){
+ successCallback();
+ } else {
+ // Set the next and finish button enabled.
+ //this.updateFooterButtonsStates(true);
+ }
+ });
+ };
+
+ // Save the current step
+ private callStepBack = (successCallback:Function):void => {
+ this.$scope.isLoading = true;
+ this.currentStep.reference.back((result:boolean)=>{
+ this.$scope.isLoading = false;
+ if (result===true){
+ successCallback();
+ } else {
+ // Set the next and finish button enabled.
+ //this.updateFooterButtonsStates(true);
+ }
+ });
+ };
+
+ private getStepIndex = (stepName:string):number => {
+ let index:number=-1;
+ let tmp = _.find(this.assetCreationSteps, function (item, indx) {
+ index = indx;
+ return item.name === stepName;
+ });
+ return index;
+ };
+
+ private btnNextClicked = ():void => {
+ if (this.hasNext()===true) {
+ let tmp = this.currentStep.index+1;
+ this.setCurrentStepByIndex(tmp);
+ }
+ };
+
+ private btnBackClicked = ():void => {
+ if (this.hasBack()===true) {
+ this.callStepBack(() => {
+ let tmp = this.currentStep.index-1;
+ this.setCurrentStepByIndex(tmp, false);
+ });
+ }
+ };
+
+ private btnCancelClicked = ():void => {
+ this.$modalInstance.dismiss(this.$scope.getComponent());
+ };
+
+ private btnFinishedClicked = ():void => {
+ this.callStepSave(() => {
+ this.$modalInstance.close(this.$scope.getComponent());
+ });
+ };
+
+ // Check if we can move next
+ private hasNext = ():boolean => {
+ if (this.assetCreationSteps.length-1>this.currentStep.index){
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ // Check if we can move back
+ private hasBack = ():boolean => {
+ if (this.currentStep.index===0){
+ return false;
+ } else {
+ return true;
+ }
+ };
+
+ private fillAssetNameAndType=():void => {
+ this.$scope.assetName = this.$scope.getComponent().name;
+ this.$scope.assetType = this.$scope.getComponent().getComponentSubType();
+
+ };
+
+ protected enableAllWizardSteps=():void => {
+ this.$scope.directiveSteps.forEach((step:Sdc.Directives.IWizardStep) => {
+ step.enabled=true;
+ });
+ };
+
+ protected disableAllWizardSteps=():void => {
+ this.$scope.directiveSteps.forEach((step:Sdc.Directives.IWizardStep) => {
+ step.enabled=false;
+ });
+ };
+
+ private footerButtonsStateMachine = ():void => {
+ //console.log("footerButtonsStateMachine, current step validity: " + this.currentStep.valid);
+ let stepIndex:number = this.currentStep.index;
+ let cancelButton = this.$scope.footerButtons[0];
+ let backButton = this.$scope.footerButtons[1];
+ let nextButton = this.$scope.footerButtons[2];
+ let finishButton = this.$scope.footerButtons[3];
+
+ // NEXT button
+ // Disable next button if it is the last step, and if not check the validity of the step.
+ if (this.hasNext()){
+ nextButton.disabled = !this.currentStep.valid;
+ } else {
+ nextButton.disabled = true;
+ }
+
+ // BACK button
+ backButton.disabled = !this.hasBack();
+
+ // FINISH button
+ // If step 2 is valid show the finish button.
+ if (stepIndex>=1 && this.currentStep.valid===true) {
+ finishButton.disabled = false;
+ }
+ if (this.currentStep.valid===false){
+ finishButton.disabled = true;
+ }
+
+ // EDIT
+ if (this.type===WizardCreationTypes.edit && this.currentStep.valid===true){
+ finishButton.disabled = false;
+ }
+
+ };
+
+
+
+ private wizardButtonsIconsStateMachine = ():void => {
+
+ // Enable or disable wizard directive next step, in case the current step form is valid or not.
+ let stepIndex:number = this.currentStep.index;
+ if (this.$scope.directiveSteps[stepIndex + 1]) {
+ this.$scope.directiveSteps[stepIndex + 1].enabled = this.currentStep.valid;
+ }
+
+ // In case step 1 and 2 are valid, we can open all other steps.
+ if (this.$scope.directiveSteps[0].valid===true && this.$scope.directiveSteps[1].valid===true){
+ // Enable all wizard directive steps
+ this.enableAllWizardSteps();
+ } else if (this.currentStep.valid===false) {
+ // Disable all steps
+ this.disableAllWizardSteps();
+ }
+ };
+
+ private noBackspaceNav:Function = ():void => {
+ this.$scope.$on('$locationChangeStart', (event, newUrl, oldUrl):void =>{
+ event.preventDefault();
+ })
+ };
+
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts
new file mode 100644
index 0000000000..9490cddfdb
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/wizard-state/create-wizard.ts
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 3/15/2016.
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export class CreateWizardViewModel extends WizardCreationBaseViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'data',
+ 'ComponentFactory',
+ '$modalInstance'
+ ];
+
+ constructor(public $scope:IWizardCreationScope,
+ public data:any,
+ public ComponentFactory: Sdc.Utils.ComponentFactory,
+ public $modalInstance: ng.ui.bootstrap.IModalServiceInstance) {
+
+ super($scope, data, ComponentFactory, $modalInstance);
+ this.type = WizardCreationTypes.create;
+ this.init();
+ this.initCreateAssetScope();
+ }
+
+ private init = ():void => {
+
+ switch (this.data.componentType){
+ case Utils.Constants.ComponentType.RESOURCE: {
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'},
+ {"name": StepNames.deploymentArtifact, "url": '/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html'},
+ {"name": StepNames.informationArtifact, "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'},
+ {"name": StepNames.properties, "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'}
+ ];
+ }
+ break;
+
+ case Utils.Constants.ComponentType.SERVICE: {
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}
+ ];
+ }
+ break;
+
+ case Utils.Constants.ComponentType.PRODUCT: {
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.hierarchy, "url": '/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}
+ ];
+ }
+ break;
+ }
+ };
+
+ private initCreateAssetScope = ():void => {
+ switch (this.data.componentType){
+ case Utils.Constants.ComponentType.RESOURCE: {
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}},
+ {"name": StepNames.deploymentArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.deploymentArtifact);}},
+ {"name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.informationArtifact);}},
+ {"name": StepNames.properties, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.properties);}}
+ ];
+ this.$scope.modalTitle = "Create VF";
+ break;
+ }
+ case Utils.Constants.ComponentType.SERVICE: {
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}},
+
+ ];
+ this.$scope.modalTitle = "Create Service";
+ break;
+ }
+ case Utils.Constants.ComponentType.PRODUCT: {
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.hierarchy, "enabled":false, "callback": ()=> {return this.setCurrentStepByName(StepNames.hierarchy);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}
+ ];
+ this.$scope.modalTitle = "Create Product";
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts
new file mode 100644
index 0000000000..353c487e0a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/wizard-state/edit-wizard.ts
@@ -0,0 +1,164 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export class EditWizardViewModel extends WizardCreationBaseViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'data',
+ 'ComponentFactory',
+ '$modalInstance'
+ ];
+
+ constructor(public $scope:IWizardCreationScope,
+ public data:any,
+ public ComponentFactory: Sdc.Utils.ComponentFactory,
+ public $modalInstance: ng.ui.bootstrap.IModalServiceInstance) {
+
+ super($scope, data, ComponentFactory, $modalInstance);
+ this.type = WizardCreationTypes.edit;
+ this.init();
+ this.initCreateAssetScope();
+
+ // Enable all wizard directive steps
+ this.enableAllWizardSteps();
+ }
+
+ private init = ():void => {
+ switch (this.data.component.componentType){
+ case Utils.Constants.ComponentType.RESOURCE: {
+ if(this.data.component.isComplex()) {
+ this.assetCreationSteps = [
+ {
+ "name": StepNames.general,
+ "url": '/app/scripts/view-models/wizard/general-step/general-step.html'
+ },
+ {
+ "name": StepNames.icon,
+ "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'
+ },
+ {
+ "name": StepNames.deploymentArtifact,
+ "url": '/app/scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.html'
+ },
+ {
+ "name": StepNames.informationArtifact,
+ "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'
+ },
+ {
+ "name": StepNames.properties,
+ "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'
+ }
+ ];
+ }else{
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'},
+ {"name": StepNames.informationArtifact, "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'},
+ {"name": StepNames.properties, "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'}
+ ];
+ }
+ break;
+ }
+ case Utils.Constants.ComponentType.SERVICE: {
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}
+ ];
+ break;
+ }
+ case Utils.Constants.ComponentType.PRODUCT: {
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.hierarchy, "url": '/app/scripts/view-models/wizard/hierarchy-step/hierarchy-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'}
+ ];
+ break;
+ }
+ }
+ };
+
+ private initCreateAssetScope = ():void => {
+ switch (this.data.component.componentType){
+ case Utils.Constants.ComponentType.RESOURCE: {
+ if(this.data.component.isComplex()) {
+ this.$scope.directiveSteps = [
+ {
+ "name": StepNames.general, "enabled": true, "callback": ()=> {
+ return this.setCurrentStepByName(StepNames.general);
+ }
+ },
+ {
+ "name": StepNames.icon, "enabled": false, "callback": ()=> {
+ return this.setCurrentStepByName(StepNames.icon);
+ }
+ },
+ {
+ "name": StepNames.deploymentArtifact, "enabled": false, "callback": ()=> {
+ return this.setCurrentStepByName(StepNames.deploymentArtifact);
+ }
+ },
+ {
+ "name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {
+ return this.setCurrentStepByName(StepNames.informationArtifact);
+ }
+ },
+ {
+ "name": StepNames.properties, "enabled": false, "callback": ()=> {
+ return this.setCurrentStepByName(StepNames.properties);
+ }
+ }
+ ];
+ }else{
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}},
+ {"name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.informationArtifact);}},
+ {"name": StepNames.properties, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.properties);}}
+ ];
+ }
+ this.$scope.modalTitle = "Edit " + this.data.component.resourceType;
+ break;
+ }
+ case Utils.Constants.ComponentType.SERVICE: {
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}
+ ];
+ this.$scope.modalTitle = "Edit Service";
+ break;
+ }
+ case Utils.Constants.ComponentType.PRODUCT: {
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.hierarchy, "enabled":false, "callback": ()=> {return this.setCurrentStepByName(StepNames.hierarchy);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}}
+ ];
+ this.$scope.modalTitle = "Edit Product";
+ break;
+ }
+ }
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts b/catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts
new file mode 100644
index 0000000000..5fe1bf7e59
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/wizard/wizard-state/import-wizard.ts
@@ -0,0 +1,64 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../references"/>
+module Sdc.ViewModels.Wizard {
+ 'use strict';
+
+ export class ImportWizardViewModel extends WizardCreationBaseViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'data',
+ 'ComponentFactory',
+ '$modalInstance'
+ ];
+
+ constructor(public $scope:IWizardCreationScope,
+ public data:any,
+ public ComponentFactory: Sdc.Utils.ComponentFactory,
+ public $modalInstance: ng.ui.bootstrap.IModalServiceInstance) {
+
+ super($scope, data, ComponentFactory, $modalInstance );
+ this.type = WizardCreationTypes.importAsset;
+ this.init();
+ this.initImportAssetScope();
+ }
+
+ private init = ():void => {
+ this.assetCreationSteps = [
+ {"name": StepNames.general, "url": '/app/scripts/view-models/wizard/general-step/general-step.html'},
+ {"name": StepNames.icon, "url": '/app/scripts/view-models/wizard/icons-step/icons-step.html'},
+ {"name": StepNames.informationArtifact, "url": '/app/scripts/view-models/wizard/artifact-information-step/artifact-information-step.html'},
+ {"name": StepNames.properties, "url": '/app/scripts/view-models/wizard/properties-step/properties-step.html'}
+ ];
+ };
+
+ private initImportAssetScope = ():void => {
+ this.$scope.directiveSteps = [
+ {"name": StepNames.general, "enabled": true, "callback": ()=> {return this.setCurrentStepByName(StepNames.general);}},
+ {"name": StepNames.icon, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.icon);}},
+ {"name": StepNames.informationArtifact, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.informationArtifact);}},
+ {"name": StepNames.properties, "enabled": false, "callback": ()=> {return this.setCurrentStepByName(StepNames.properties);}}
+ ];
+
+ this.$scope.modalTitle = "Import Asset";
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.html b/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.html
new file mode 100644
index 0000000000..23c08f6ec6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.html
@@ -0,0 +1,85 @@
+<div class="activity-log">
+
+ <div class="title-wrapper">
+ <div class="top-search">
+ <input type="text"
+ class="search-text"
+ placeholder="Search"
+ data-ng-model="searchBind"
+ data-tests-id="main-menu-input-search"
+ ng-model-options="{ debounce: 500 }" />
+ <span class="w-sdc-search-icon magnification"></span>
+ </div>
+ </div>
+
+ <div class="table-container-flex">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+
+ <!-- Table headers -->
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-show="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ </div>
+
+ <!-- Table body -->
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+
+ <!-- In case the logs are empty -->
+ <div data-ng-if="!activityLog || activityLog.length===0" class="no-row-text">
+ There are no logs to display
+ </div>
+
+ <!-- Loop on logs list -->
+ <div data-ng-repeat="item in activityLog | filter: searchBind | orderBy:sortBy:reverse track by $index"
+ data-ng-init="item.dateFormat = ( item.TIMESTAMP.replace(' UTC', '') | stringToDateFilter | date: 'MM/dd/yyyy':'UTC')+' | '+(item.TIMESTAMP.replace(' UTC', '') | stringToDateFilter | date: 'shortTime':'UTC' )"
+ class="flex-container data-row"
+ data-ng-class="{'selected': component === selectedComponent}"
+ data-ng-click="doSelectComponent(component);"
+ >
+
+ <!-- Date -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{item.dateFormat}}
+ </div>
+
+ <!-- Action -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{item.ACTION}}
+ </div>
+
+ <!-- Comment -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{item.COMMENT}}
+ </div>
+
+ <!-- Username -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{item.MODIFIER}}
+ </div>
+
+ <!-- Status -->
+ <div class="table-col-general flex-item" sdc-smart-tooltip>
+ {{item.STATUS}}
+ <span data-ng-class="{'success': item.STATUS>='200' && item.STATUS<='204','error': item.STATUS<'200' || item.STATUS>='300'}"></span>
+ </div>
+
+ </div>
+
+ </perfect-scrollbar>
+ </div><!-- End table body -->
+ </div><!-- End table -->
+ </div><!-- End table-container-flex -->
+
+</div>
+
+
+
+<!--<div ng-repeat="activityDate in activityDateArray " class="w-sdc-component-viewer-right-activity-log" >
+ <div class="w-sdc-component-viewer-right-activity-log-date" >{{activityDate | date: 'longDate'}}</div>
+ <div ng-repeat="activity in activityLog[activityDate] | orderBy: '-TIMESTAMP'">
+ <div class="w-sdc-component-viewer-right-activity-log-time">{{activity.TIMESTAMP.replace(" UTC", '') | stringToDateFilter | date: 'mediumTime':'UTC'}}</div>
+ <div class="w-sdc-component-viewer-right-activity-log-content">{{"Action: " + parseAction(activity.ACTION) + " Performed by: " + activity.MODIFIER + " Status: " + activity.STATUS}}</div>
+ </div>
+</div>-->
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.less b/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.less
new file mode 100644
index 0000000000..61bb3e9f01
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.less
@@ -0,0 +1,83 @@
+.activity-log {
+
+ margin-top: 30px;
+
+ .title-wrapper {
+ display: flex;
+ justify-content: flex-end;
+ }
+
+ .table-container-flex .table .body .scrollbar-container {
+ max-height: 448px;
+ }
+
+ .view-mode {
+ background-color: @main_color_p;
+ }
+
+ .table{
+ height: 490px;
+ margin-bottom: 0;
+ }
+
+ .table-container-flex {
+ margin-top: 10px;
+
+ .flex-item:nth-child(1) { width: 200px; }
+ .flex-item:nth-child(2) { flex-grow: 20; }
+ .flex-item:nth-child(3) { flex-grow: 30; }
+ .flex-item:nth-child(4) { flex-grow: 20; }
+ .flex-item:nth-child(5) { width: 80px; }
+
+ .success {
+ position: absolute;
+ top: 11px;
+ right: 20px;
+ .sprite-new;
+ .sdc-success;
+ }
+
+ .error {
+ position: absolute;
+ top: 11px;
+ right: 20px;
+ .sprite-new;
+ .sdc-error;
+ }
+
+ }
+
+ .data-row {
+ position: relative;
+ }
+
+ .top-search {
+ float: right;
+ position: relative;
+
+ input.search-text {
+ .border-radius(2px);
+ width: 245px;
+ height: 32px;
+ line-height: 32px;
+ border: 1px solid @main_color_o;
+ margin: 0;
+ outline: none;
+ text-indent: 10px;
+
+ &::-webkit-input-placeholder { font-style: italic; } /* Safari, Chrome and Opera */
+ &:-moz-placeholder { font-style: italic; } /* Firefox 18- */
+ &::-moz-placeholder { font-style: italic; } /* Firefox 19+ */
+ &:-ms-input-placeholder { font-style: italic; } /* IE 10+ */
+ &:-ms-input-placeholder { font-style: italic; } /* Edge */
+ }
+
+ .magnification {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.ts
new file mode 100644
index 0000000000..665d0c0ef6
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/activity-log/activity-log.ts
@@ -0,0 +1,122 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IActivityLogViewModelScope extends IWorkspaceViewModelScope {
+ activityDateArray: Array<any>; //this is in order to sort the dates
+ activityLog: Array<Models.Activity>;
+ preVersion:string;
+
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+ searchBind:string;
+
+ getActivityLog(uniqueId:string):void;
+ onVersionChanged(version:any) : void;
+ parseAction(action:string):string;
+ sort(sortBy:string): void;
+ }
+
+ export class ActivityLogViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$state',
+ 'Sdc.Services.ActivityLogService'
+ ];
+
+ constructor(private $scope:IActivityLogViewModelScope,
+ private $state:ng.ui.IStateService,
+ private activityLogService:Services.ActivityLogService
+ ) {
+
+ this.initScope();
+ this.$scope.setValidState(true);
+ this.initSortedTableScope();
+ this.$scope.updateSelectedMenuItem();
+
+ // Set default sorting
+ this.$scope.sortBy = 'logDate';
+ }
+
+ private initScope():void {
+
+ this.$scope.preVersion = this.$scope.component.version;
+
+ this.$scope.onVersionChanged = (version:any):void => {
+ if (version.versionNumber != this.$scope.component.version) {
+ this.$scope.isLoading = true;
+ this.$scope.getActivityLog(version.versionId);
+ }
+ };
+
+ this.$scope.getActivityLog = (uniqueId:any):void => {
+
+ let onError = (response) => {
+ this.$scope.isLoading = false;
+ console.info('onFaild', response);
+
+ };
+
+ let onSuccess = (response:Array<Models.Activity>) => {
+ this.$scope.activityLog = _.sortBy(response, function(o) { return o.TIMESTAMP; }); //response; //
+ this.$scope.isLoading = false;
+ };
+
+ this.$scope.isLoading = true;
+ if (this.$scope.component.isResource()) {
+ this.activityLogService.getActivityLogService('resources', uniqueId).then(onSuccess, onError);
+ }
+ if (this.$scope.component.isService()) {
+ this.activityLogService.getActivityLogService('services', uniqueId).then(onSuccess, onError);
+ }
+
+ };
+
+ if (!this.$scope.activityLog || this.$scope.preVersion != this.$scope.component.version) {
+ this.$scope.getActivityLog(this.$scope.component.uniqueId);
+ }
+
+ this.$scope.parseAction = (action:string) => {
+ return action ? action.split(/(?=[A-Z])/).join(' ') : '';
+ };
+
+ }
+
+ private initSortedTableScope = ():void => {
+ this.$scope.tableHeadersList = [
+ {title: 'Date', property: 'logDate'},
+ {title: 'Action', property: 'logAction'},
+ {title: 'Comment', property: 'logComment'},
+ {title: 'Username', property: 'logUsername'},
+ {title: 'Status', property: 'logStatus'}
+ ];
+
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view-model.ts
new file mode 100644
index 0000000000..469da6a2e1
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view-model.ts
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IAttributesViewModelScope extends IWorkspaceViewModelScope {
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+
+ addOrUpdateAttribute(attribute?:Models.AttributeModel): void;
+ delete(attribute:Models.AttributeModel): void;
+ sort(sortBy:string): void;
+ }
+
+ export class AttributesViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ModalsHandler'
+ ];
+
+
+ constructor(private $scope:IAttributesViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private ModalsHandler:Utils.ModalsHandler) {
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+
+ private openEditAttributeModal = (attribute:Models.AttributeModel):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/attribute-form/attribute-form-view.html'),
+ controller: 'Sdc.ViewModels.AttributeFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ attribute: ():Models.AttributeModel => {
+ return attribute;
+ },
+ component: ():Models.Components.Component => {
+ return <Models.Components.Component> this.$scope.component;
+ }
+ }
+ };
+ this.$modal.open(modalOptions);
+ };
+
+ private initScope = ():void => {
+
+ //let self = this;
+ this.$scope.sortBy = 'name';
+ this.$scope.reverse = false;
+ this.$scope.setValidState(true);
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Type', property: 'type'},
+ {title: 'Default Value', property: 'defaultValue'}
+ ];
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+ this.$scope.addOrUpdateAttribute = (attribute?:Models.AttributeModel):void => {
+ this.openEditAttributeModal(attribute ? attribute : new Models.AttributeModel());
+ };
+
+ this.$scope.delete = (attribute:Models.AttributeModel):void => {
+
+ let onOk = ():void => {
+ this.$scope.component.deleteAttribute(attribute.uniqueId);
+ };
+ let title:string = this.$filter('translate')("ATTRIBUTE_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("ATTRIBUTE_VIEW_DELETE_MODAL_TEXT", "{'name': '" + attribute.name + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view.html
new file mode 100644
index 0000000000..59ba933a0a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes-view.html
@@ -0,0 +1,52 @@
+<div class="workspace-attributes">
+ <div class="add-btn" data-tests-id="add-attribute-button" ng-if="!isViewMode()"
+ data-ng-class="{'disabled': isDisableMode()}" data-ng-click="addOrUpdateAttribute()" data-tests-id="add-attribute-button">Add</div>
+ <div class="table-container-flex">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item" ng-if="!isViewMode()"></div>
+ <!--div class="table-no-text-header head-row flex-item"></div-->
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="component.attributes.length === 0" class="no-row-text" data-ng-class="{'disabled': isDisableMode()}">
+ There are no attributes to display <br>
+ <span ng-if="!isViewMode()"> click <a data-ng-click="addOrUpdateAttribute()">here</a> to add one </span>
+
+ </div>
+ <div data-ng-repeat-start="attribute in component.attributes | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row" data-ng-class="{'selected': attribute.selected}"
+ data-ng-click="attribute.selected = !attribute.selected" data-tests-id="attributes-table-row">
+
+ <div class="table-col-general flex-item text">
+ <span class="sprite table-arrow" data-ng-class="{'opened': attribute.selected}"></span>
+ <span data-tests-id="{{attribute.name}}" tooltips tooltip-content="{{attribute.name}}">{{attribute.name}}</span>
+
+ </div>
+
+ <div class="table-col-general flex-item text" data-tests-id="{{attribute.type}}" data-ng-bind="attribute.type"></div>
+
+ <div class="table-col-general flex-item text">
+ <span tooltips tooltip-content="{{attribute.defaultValue}}" data-tests-id="{{attribute.defaultValue}}" data-ng-bind="attribute.defaultValue"></span>
+ </div>
+
+ <div class="table-btn-col flex-item" ng-if="!isViewMode()">
+ <button class="table-edit-btn" data-tests-id="edit_{{attribute.name}}" data-ng-show="attribute.parentUniqueId==component.uniqueId"
+ data-ng-click="addOrUpdateAttribute(attribute); $event.stopPropagation();" data-ng-class="{'disabled': isViewMode()}"> </button>
+ <button class="table-delete-btn" data-tests-id="delete_{{attribute.name}}" data-ng-show="attribute.parentUniqueId==component.uniqueId"
+ data-ng-click="delete(attribute); $event.stopPropagation();" data-ng-class="{'disabled': isViewMode()}"> </button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="attribute.selected && attribute.description" class="item-opened" data-ng-bind="attribute.description">
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes.less b/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes.less
new file mode 100644
index 0000000000..ffd28afce4
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/attributes/attributes.less
@@ -0,0 +1,54 @@
+.workspace-attributes {
+
+ width: 93%;
+ display: inline-block;
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table{
+ height:490px;
+ margin-bottom: 0;
+ }
+
+ .table-container-flex {
+ margin-top: 27px;
+
+ .text{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ }
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 9;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 3;
+ padding-top: 10px;
+ }
+
+ .flex-item:nth-child(5) {
+ flex-grow: 1;
+
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view-model.ts
new file mode 100644
index 0000000000..f8eeaf7f64
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view-model.ts
@@ -0,0 +1,232 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface ICompositionViewModelScope extends IWorkspaceViewModelScope {
+
+ currentComponent: Models.Components.Component;
+ selectedComponent: Models.Components.Component;
+ isLoading: boolean;
+ graphApi:any;
+ sharingService:Sdc.Services.SharingService;
+ sdcMenu:Models.IAppMenu;
+ version:string;
+ isViewOnly:boolean;
+ isLoadingRightPanel:boolean;
+ setComponent(component: Models.Components.Component);
+ isComponentInstanceSelected():boolean;
+ updateSelectedComponent(): void
+ openUpdateModal();
+ deleteSelectedComponentInstance():void;
+ onBackgroundClick():void;
+ setSelectedInstance(componentInstance: Models.ComponentsInstances.ComponentInstance): void;
+ printScreen():void;
+
+ cacheComponentsInstancesFullData: Models.Components.Component;
+ }
+
+ export class CompositionViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$log',
+ 'sdcMenu',
+ 'MenuHandler',
+ '$modal',
+ '$templateCache',
+ '$state',
+ 'Sdc.Services.SharingService',
+ '$filter',
+ 'Sdc.Services.CacheService',
+ 'ComponentFactory',
+ 'ChangeLifecycleStateHandler',
+ 'LeftPaletteLoaderService',
+ 'ModalsHandler',
+ 'EventListenerService'
+ ];
+
+ constructor(private $scope:ICompositionViewModelScope,
+ private $log: ng.ILogService,
+ private sdcMenu:Models.IAppMenu,
+ private MenuHandler: Utils.MenuHandler,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $state:ng.ui.IStateService,
+ private sharingService:Services.SharingService,
+ private $filter:ng.IFilterService,
+ private cacheService:Services.CacheService,
+ private ComponentFactory: Utils.ComponentFactory,
+ private ChangeLifecycleStateHandler: Sdc.Utils.ChangeLifecycleStateHandler,
+ private LeftPaletteLoaderService: Services.Components.LeftPaletteLoaderService,
+ private ModalsHandler: Sdc.Utils.ModalsHandler,
+ private eventListenerService:Services.EventListenerService) {
+
+ this.$scope.setValidState(true);
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ this.registerGraphEvents(this.$scope);
+ }
+ private cacheComponentsInstancesFullData: Array<Models.Components.Component>;
+
+ private initComponent = ():void => {
+
+ this.$scope.currentComponent = this.$scope.component;
+ this.$scope.selectedComponent = this.$scope.currentComponent;
+ this.updateUuidMap();
+ this.$scope.isViewOnly = this.$scope.isViewMode();
+ };
+ private registerGraphEvents = (scope:ICompositionViewModelScope):void => {
+
+ this.eventListenerService.registerObserverCallback(Utils.Constants.GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance);
+ this.eventListenerService.registerObserverCallback(Utils.Constants.GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick);
+
+ }
+ private openUpdateComponentInstanceNameModal = ():void => {
+
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/resource-instance-name-form/resource-instance-name-view.html'),
+ controller: 'Sdc.ViewModels.ResourceInstanceNameViewModel',
+ size: 'sdc-sm',
+ backdrop: 'static',
+ resolve: {
+ component: ():Models.Components.Component => {
+ return this.$scope.currentComponent;
+
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ modalInstance.result.then(():void => {
+ this.eventListenerService.notifyObservers(Utils.Constants.GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, this.$scope.currentComponent.selectedInstance);
+ //this.$scope.graphApi.updateNodeName(this.$scope.currentComponent.selectedInstance);
+ });
+ };
+
+ private removeSelectedComponentInstance = ():void => {
+ this.eventListenerService.notifyObservers(Utils.Constants.GRAPH_EVENTS.ON_DELETE_MULTIPLE_COMPONENTS);
+ };
+
+ private updateUuidMap = ():void => {
+ /**
+ * In case user press F5, the page is refreshed and this.sharingService.currentEntity will be undefined,
+ * but after loadService or loadResource this.sharingService.currentEntity will be defined.
+ * Need to update the uuidMap with the new resource or service.
+ */
+ this.sharingService.addUuidValue(this.$scope.currentComponent.uniqueId,this.$scope.currentComponent.uuid);
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.sharingService = this.sharingService;
+ this.$scope.sdcMenu = this.sdcMenu;
+ this.$scope.isLoading = false;
+ this.$scope.isLoadingRightPanel = false;
+ this.$scope.graphApi = {};
+ this.$scope.version = this.cacheService.get('version');
+ this.initComponent();
+
+ this.cacheComponentsInstancesFullData = new Array<Models.Components.Component>();
+
+ this.$scope.isComponentInstanceSelected = ():boolean => {
+ return this.$scope.currentComponent && this.$scope.currentComponent.selectedInstance != undefined && this.$scope.currentComponent.selectedInstance != null;
+ };
+
+ this.$scope.updateSelectedComponent = (): void => {
+ if(this.$scope.currentComponent.selectedInstance){
+
+ let componentParent = _.find(this.cacheComponentsInstancesFullData, (component) => {
+ return component.uniqueId === this.$scope.currentComponent.selectedInstance.componentUid;
+ });
+ if(componentParent) {
+ this.$scope.selectedComponent = componentParent;
+ }
+ else {
+ try {
+ let onSuccess = (component:Models.Components.Component) => {
+ this.$scope.isLoadingRightPanel = false;
+ this.$scope.selectedComponent = component;
+ this.cacheComponentsInstancesFullData.push(component);
+ };
+ let onError = (component:Models.Components.Component) => {
+ console.log("Error updating selected component");
+ this.$scope.isLoadingRightPanel = false;
+ };
+ this.ComponentFactory.getComponentFromServer(this.$scope.currentComponent.selectedInstance.originType, this.$scope.currentComponent.selectedInstance.componentUid).then(onSuccess, onError);
+ } catch(e){
+ console.log("Error updating selected component", e);
+ this.$scope.isLoadingRightPanel = false;
+ }
+ }
+ }
+ else {
+ this.$scope.selectedComponent = this.$scope.currentComponent;
+ }
+ };
+
+ this.$scope.setSelectedInstance = (selectedComponent:Models.ComponentsInstances.ComponentInstance):void => {
+
+ this.$log.debug('composition-view-model::onNodeSelected:: with id: '+ selectedComponent.uniqueId);
+ this.$scope.currentComponent.setSelectedInstance(selectedComponent);
+ this.$scope.updateSelectedComponent();
+
+ if (this.$state.current.name === 'workspace.composition.api') {
+ this.$state.go('workspace.composition.details');
+ }
+ if (this.$state.current.name === 'workspace.composition.relations' && this.$scope.currentComponent.isProduct()) {
+ this.$state.go('workspace.composition.details');
+ }
+ };
+
+ this.$scope.onBackgroundClick = ():void => {
+ this.$scope.currentComponent.selectedInstance = null;
+ this.$scope.selectedComponent = this.$scope.currentComponent;
+
+ if (this.$state.current.name === 'workspace.composition.api') {
+ this.$state.go('workspace.composition.details');
+ }
+ };
+
+ this.$scope.openUpdateModal = ():void => {
+ this.openUpdateComponentInstanceNameModal();
+ };
+
+ this.$scope.deleteSelectedComponentInstance = ():void => {
+ let state = "deleteInstance";
+ let onOk = ():void => {
+ this.removeSelectedComponentInstance();
+ //this.$scope.graphApi.deleteSelectedNodes();
+ };
+ let title:string = this.$scope.sdcMenu.alertMessages[state].title;
+ let message:string = this.$scope.sdcMenu.alertMessages[state].message.format([this.$scope.currentComponent.selectedInstance.name]);
+ this.ModalsHandler.openAlertModal(title, message).then(onOk);
+ };
+
+ this.$scope.setComponent = (component: Models.Components.Product):void => {
+ this.$scope.currentComponent = component;
+ }
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view.html
new file mode 100644
index 0000000000..4efc74c31b
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition-view.html
@@ -0,0 +1,99 @@
+<div class="workspace-composition">
+ <loader data-display="isLoading"></loader>
+ <div class="w-sdc-designer-canvas" data-ng-class="{sidebaractive: displayDesignerRightSidebar}" >
+ <palette components="leftPanelComponents"
+ current-component="currentComponent"
+ is-view-only="isViewOnly"
+ is-loading="isLoading"></palette>
+
+ <composition-graph component="currentComponent" is-view-only="isViewOnly"></composition-graph>
+ <!--<graph-creator left-panel-components="leftPanelComponents"-->
+ <!--data-tests-id="canvas"-->
+ <!--on-instance-selected="setSelectedInstance(componentInstance)"-->
+ <!--on-background-click="onBackgroundClick()" current-component="currentComponent"-->
+ <!--api="graphApi" is-view-only="isViewOnly" is-loading="isLoading"></graph-creator>-->
+ </div>
+
+ <div class="w-sdc-designer-sidebar-toggle" data-ng-class="{'active': displayDesignerRightSidebar}"
+ data-ng-init="displayDesignerRightSidebar = true"
+ data-ng-click="displayDesignerRightSidebar = !displayDesignerRightSidebar">
+ <div class="w-sdc-designer-sidebar-toggle-icon sprite-new pointer menu-open-left"></div>
+ </div>
+
+ <div class="w-sdc-designer-sidebar" data-ng-class="{'view-mode':isViewOnly}">
+
+ <div class="w-sdc-designer-sidebar-head" data-tests-id="w-sdc-designer-sidebar-head">
+ <div class="w-sdc-designer-sidebar-logo-ph">
+ <div class="large {{selectedComponent.iconSprite}} {{selectedComponent.icon}}">
+ <div ng-if="isComponentInstanceSelected()"
+ data-ng-class="{'non-certified':'CERTIFIED' !== selectedComponent.lifecycleState, 'smaller-icon': selectedComponent.icon==='vl' || selectedComponent.icon==='cp'}"
+ tooltips tooltip-side="top" tooltip-content="Not certified"></div>
+ </div>
+ </div>
+
+ <div class="w-sdc-designer-sidebar-logo">
+ <span class="w-sdc-designer-sidebar-logo-title" data-tests-id="selectedCompTitle" tooltips
+ tooltip-class="tooltip-custom break-word-tooltip"
+ tooltip-content="&#8203;{{isComponentInstanceSelected() ? currentComponent.selectedInstance.name : currentComponent.name | resourceName}}"
+ data-ng-bind="isComponentInstanceSelected() ? currentComponent.selectedInstance.name : currentComponent.name | resourceName"></span>
+ </div>
+ <div class="sprite e-sdc-small-icon-pencil w-sdc-designer-update-resource-icon"
+ data-ng-if="!isViewOnly && isComponentInstanceSelected()"
+ data-ng-click="openUpdateModal()" id="editPencil"></div>
+
+ <div class="sprite e-sdc-small-icon-delete w-sdc-designer-delete-resource-icon"
+ data-tests-id="e-sdc-small-icon-delete"
+ data-ng-if="!isViewOnly && isComponentInstanceSelected()"
+ data-ng-click="!isLoading && deleteSelectedComponentInstance()" title="Delete Resource Instance"></div>
+ </div>
+
+ <div class="w-sdc-designer-sidebar-tabs">
+ <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"
+ data-ui-sref="workspace.composition.details"
+ tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new info"></div>
+ </button>
+ <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"
+ ui-sref="workspace.composition.structure"
+ tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Composition">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new structure"></div>
+ </button>
+ <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"
+ data-ui-sref="workspace.composition.deployment"
+ tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Deployment Artifacts"
+ data-tests-id="deployment-artifact-tab">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new deployment-artifacts"></div>
+ </button>
+ <button tooltips tooltip-class="tooltip-custom tab-tooltip"
+ tooltip-content="{{selectedComponent.isResource() ? 'Properties and Attributes': 'Inputs'}}"
+ class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"
+ data-ui-sref="workspace.composition.properties"
+ data-tests-id="properties-and-attributes-tab">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new"
+ ng-class="selectedComponent.isResource() ? 'properties': 'inputs'"></div>
+ </button>
+ <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"
+ data-ui-sref="workspace.composition.artifacts"
+ tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information Artifacts">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new information-artifacts"></div>
+ </button>
+ <button data-ng-show="!selectedComponent.isService()" class="i-sdc-designer-sidebar-tab"
+ data-ui-sref-active="active" ui-sref="workspace.composition.relations"
+ tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside"
+ tooltip-content="Requirements and Capabilities">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new relations"></div>
+ </button>
+ <button data-ng-show="selectedComponent.isService()" class="i-sdc-designer-sidebar-tab"
+ data-ui-sref-active="active" ui-sref="workspace.composition.api" data-tests-id="tab-api"
+ tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" tooltip-content="API">
+ <div class="i-sdc-designer-sidebar-tab-icon sprite-new api"></div>
+ </button>
+
+ </div>
+
+ <div data-ui-view="" class="w-sdc-designer-sidebar-tab-content-view"></div>
+
+ <loader data-display="isLoadingRightPanel" relative="true" size="medium"></loader>
+
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition.less b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition.less
new file mode 100644
index 0000000000..4c4c0a87a5
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/composition.less
@@ -0,0 +1,864 @@
+
+.composition{
+ .sdc-workspace-container{
+ .w-sdc-main-container{
+ .w-sdc-main-right-container{
+ left:0;
+ .sdc-workspace-top-bar {
+ padding-left: 295px;
+ .not-latest{
+ left: 270px;
+ }
+ }
+ .w-sdc-main-container-body-content{
+ padding: 0 0 0 247px;
+ }
+
+ > div:first-child{
+ padding: 0;
+ }
+ }
+ }
+ }
+}
+
+.workspace-composition {
+ height:100%;
+ display: block;
+ text-align: left;
+ align-items: left;
+ padding: 0;
+
+
+
+ // ---------------------------------------------------------------------------------------------------
+ // Sidebar
+ // ---------------------------------------------------------------------------------------------------
+
+
+
+ .w-sdc-designer-sidebar-toggle {
+ background-color: @main_color_p;
+ border-left: 1px solid @main_color_o;
+ border-bottom: 1px solid @main_color_o;
+ height: 21px;
+ position: absolute;
+ right: 0;
+ top: 53px;
+ width: 17px;
+ transition: right 0.2s;
+ z-index: 10;
+ .box-shadow(-1px 1px 3px 0 @main_color_n);
+
+ &.active {
+ right: 302px;
+ .w-sdc-designer-sidebar-toggle-icon{
+ transform: rotate(180deg);
+ }
+ }
+
+ }
+
+ .w-sdc-designer-sidebar-toggle-icon {
+ margin-left: 6px;
+ margin-top: 6px;
+ }
+
+ .w-sdc-designer-sidebar {
+ background-color:@main_color_p ;
+ .noselect;
+ bottom: 0;
+ position: fixed;
+ right: -302px;
+ width: 302px;
+ top: 102px;
+ transition: right 0.2s;
+ z-index: 9;
+ .box-shadow(-7px -3px 6px -8px @main_color_n);
+
+ }
+
+ .w-sdc-designer-sidebar-toggle.active + .w-sdc-designer-sidebar {
+ right: 0;
+
+ }
+
+ .w-sdc-designer-sidebar-head {
+ padding: 36px 30px 30px 30px;
+ height: 120px;
+ }
+
+ .w-sdc-designer-sidebar-logo-ph {
+ display: inline-block;
+ vertical-align: middle;
+ line-height: 48px;
+ height: 48px;
+ }
+
+ .w-sdc-designer-sidebar-logo {
+ .g_6;
+ display: inline-block;
+ margin-left: 10px;
+ font-weight: 500;
+ }
+
+ .w-sdc-designer-sidebar-logo-title {
+ .s_16_r;
+ .selectable;
+ vertical-align: middle;
+ text-overflow: ellipsis;
+ max-width: 167px;
+ display: inline-block;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
+ .w-sdc-designer-update-resource-icon {
+ .hand;
+ position: absolute;
+ right: 20px;
+ top: 10px;
+ }
+
+ .w-sdc-designer-delete-resource-icon {
+ .hand;
+ position: absolute;
+ right: 40px;
+ top: 10px;
+ }
+
+ .w-sdc-designer-sidebar-tabs {
+ .bg_e;
+ }
+
+ .w-sdc-designer-sidebar-tabs::after {
+ clear: both;
+ content: '';
+ display: table;
+ }
+
+ .i-sdc-designer-sidebar-tab {
+ background-color: @main_color_p;
+ border: 1px solid @tlv_color_u;;
+ border-left: none;
+ display: inline-block;
+ float: left;
+ height: 36px;
+ padding-top: 9px;
+ text-align: center;
+ width: 50px;
+ .hand;
+
+ &:focus {
+ outline: none;
+ }
+ &.tab-disabled {
+ /* .disabled; */
+ }
+ &.active, &:hover:enabled {
+ background-color: @tlv_color_u;
+ .i-sdc-designer-sidebar-tab-icon {
+ opacity: 1;
+
+
+ }
+
+ }
+
+ div& {
+ padding-top: 0;
+ }
+ /*for tooltip on disabled buttons*/
+ }
+
+ .i-sdc-designer-sidebar-tab-icon {
+ margin-top: 5px ;
+ // opacity: .4;
+ }
+
+ .w-sdc-designer-sidebar-tab-content {
+ .perfect-scrollbar;
+ height: 100%;
+ }
+
+ .w-sdc-designer-sidebar-tab-content-view {
+ position: absolute;
+ top: 156px;
+ bottom: 0px;
+ width: 100%;
+
+ }
+
+ .w-sdc-designer-sidebar-section {
+ }
+
+ .w-sdc-designer-sidebar-section-title {
+ .m_14_m;
+ background-color: @tlv_color_u;
+ .hand;
+ clear: both;
+ height: 32px;
+ line-height: 32px;
+ margin-top: 1px;
+ padding: 0 40px 0 20px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ position: relative;
+ width: 100%;
+ display: block;
+
+ &.expanded {
+ .w-sdc-designer-sidebar-section-title-icon {
+ transform: rotate(180deg);
+ }
+ }
+ }
+
+ .w-sdc-designer-sidebar-section-title-text {
+ max-width: 240px;
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ position: relative;
+ }
+
+ .w-sdc-designer-sidebar-section-title-icon {
+ .hand;
+ .sprite-new;
+ .arrow-up;
+ right: 16px;
+ top: 10px;
+ transition: .3s all;
+ position: absolute;
+ }
+
+ .w-sdc-designer-sidebar-section-content {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .w-sdc-designer-sidebar-section-title + .w-sdc-designer-sidebar-section-content {
+ margin: 0 auto;
+ }
+
+ .w-sdc-designer-sidebar-section-title.expanded + .w-sdc-designer-sidebar-section-content {
+ margin: 0 auto 1px;
+
+ }
+
+ .i-sdc-designer-sidebar-section-content-item {
+ .b_7;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ //max-width: 250px;
+
+ &.description {
+ margin-top: 28px;
+ white-space: normal;
+ word-wrap: break-word;
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-tag {
+ .g_7;
+ .bg_c;
+ border-radius: 4px;
+ display: inline-block;
+ line-height: 25px;
+ margin: 0 4px 6px 0;
+ min-width: 50px;
+ padding: 0 9px;
+ text-align: center;
+ max-width: 280px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .w-sdc-designer-sidebar-section-footer {
+ margin-top: 10px;
+ text-align: center;
+ width: 100%;
+ }
+
+
+
+ .w-sdc-designer-sidebar-section-footer-action {
+ width: 180px;
+ margin-top: 10px;
+ }
+
+ //////////////////////Relationship
+ .w-sdc-designer-sidebar-section-requirements {
+ border-bottom: 1px solid @color_e;
+ margin: 0 13px 20px 13px;
+ padding: 15px 0 0;
+ }
+
+ .w-sdc-designer-sidebar-section-requirements-item {
+ margin-bottom: 20px;
+ }
+
+ .w-sdc-designer-sidebar-section-requirements-label {
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+ white-space: nowrap;
+ width: 102px;
+ }
+
+ .w-sdc-designer-sidebar-section-requirements-select {
+ border: 1px solid @color_e;
+ min-height: 30px;
+ padding: 4px 13px;
+ width: 168px;
+ }
+
+ //////////////////////Properties
+ .i-sdc-designer-sidebar-section-content-item-property-and-attribute {
+ .b_7;
+ border-bottom: 1px solid @color_e;
+ min-height: 72px;
+ padding: 15px 10px 10px 18px;
+ position: relative;
+
+ &:first-child {
+ //margin-top: -18px;
+ }
+
+ &:hover {
+ // .bg_c_hover;
+ .bg_c;
+ transition: all .3s;
+
+ .i-sdc-designer-sidebar-section-content-item-button {
+ display: block;
+ }
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-property-and-attribute-label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 200px;
+ white-space: nowrap;
+ display: inline-block;
+ &:hover {
+ .a_7;
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-property-value {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 200px;
+ display: inline-block;
+ white-space: nowrap;
+
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-property-label-value {
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-button {
+ display: none;
+ position: absolute;
+ top: 25px;
+
+ &.update {
+ background-color: transparent;
+ border: 0;
+ right: 60px;
+ }
+
+ &.delete {
+ background-color: transparent;
+ border: 0;
+ right: 13px;
+ }
+
+ &.download {
+ background-color: transparent;
+ border: 0;
+ right: 35px;
+ }
+
+ &.download-env {
+ background-color: transparent;
+ border: 0;
+ right: 35px;
+ margin-top: 65px;
+ }
+
+ &.attach {
+ background-color: transparent;
+ border: 0;
+ right: 15px;
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+ // Canvas
+ // ---------------------------------------------------------------------------------------------------
+ .w-sdc-designer-canvas {
+ height:100%;
+ .noselect;
+ .bg_c;
+ bottom: 0;
+ // position: fixed;
+ //right: 0;
+ //left: 240px;
+ //top: 94px;
+ .view-mode{
+ background-color: #f8f8f8;
+ border:0;
+ }
+ }
+
+ .w-sdc-designer-canvas.sidebaractive {
+ //right: 300px;
+ }
+
+ .w-sdc-designer-element {
+ .hand;
+ width: 200px;
+ height: 100px;
+ position: absolute;
+ text-align: center;
+ top: 50%;
+ margin-top: -200px;
+ left: 50%;
+ margin-left: -50px;
+ }
+
+ .w-sdc-designer-resource-label {
+ .q_7;
+ }
+
+ .w-sdc-designer-resource-label-indicator {
+ .bg_q;
+ border-radius: 50%;
+ display: inline-block;
+ height: 10px;
+ margin-right: 6px;
+ vertical-align: middle;
+ width: 10px;
+
+ &.valid {
+ .bg_l;
+ }
+
+ &.invalid {
+ .bg_h;
+ }
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+ // Leftbar
+ // ---------------------------------------------------------------------------------------------------
+ .w-sdc-designer-leftbar {
+ background-color: @main_color_p;
+ bottom: 0;
+ left: 0;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ position: absolute;
+ top: 0;
+ width: 244px;
+ .box-shadow(7px -3px 6px -8px @main_color_n);
+
+ }
+
+ .w-sdc-designer-leftbar-title {
+
+ .p_16_m;
+ background-color: @main_color_n;
+ line-height: 40px;
+ padding: 0 17px;
+ }
+
+ .w-sdc-designer-leftbar-title-count {
+ float: right;
+ }
+
+ .w-scd-diagram-container {
+ // left: 240px;
+ //right: 300px;
+ }
+
+ .w-sdc-designer-leftbar-search {
+ background-color: @tlv_color_u;
+ padding: 10px;
+ white-space: nowrap;
+ position: relative;
+ }
+
+ .w-sdc-designer-leftbar-search-input {
+ border: 1px solid @color_e;
+ .border-radius(4px);
+ height: 30px;
+ margin: 0;
+ padding: 0px 28px 3px 10px;
+ vertical-align: 4px;
+ width: 100%;
+ outline: none;
+ font-style: italic;
+ }
+
+ .w-sdc-designer-leftbar-search-filter {
+
+ }
+
+ .i-sdc-designer-leftbar-section {
+ .hand;
+ }
+
+ .i-sdc-designer-leftbar-section-title {
+ .m_14_m;
+ background-color: @tlv_color_u;
+ .hand;
+ clear: both;
+ height: 40px;
+ line-height: 40px;
+ margin-top: 1px;
+ padding: 0 10px;
+ position: relative;
+ text-transform: uppercase;
+ font-weight: bold;
+ }
+
+ .i-sdc-designer-leftbar-section-title-icon {
+ .hand;
+ .sprite-new;
+ .arrow-up;
+ width: 15px;
+ height: 9px;
+ position: absolute;
+ right: 13px;
+ top: 18px;
+ transition: .3s all;
+ }
+
+ .i-sdc-designer-leftbar-section.expanded .i-sdc-designer-leftbar-section-title-icon {
+ transform: rotate(180deg);
+ margin-right: 2px;
+ }
+
+ .i-sdc-designer-leftbar-section-content {
+ background-color: @main_color_o;
+ }
+
+ .i-sdc-designer-leftbar-section-content-item {
+ background-color: @main_color_p;
+ overflow: hidden;
+
+ &:hover {
+ background-color: @main_color_p;
+ }
+
+ .cp{
+ margin: 6px;
+ }
+
+ .vl{
+ margin: 6px;
+ }
+ }
+
+ .i-sdc-designer-leftbar-section-content-subcat {
+ .m_14_m;
+ background-color: @tlv_color_t;
+ line-height: 35px;
+ padding: 0 10px;
+ cursor: default;
+
+
+ &:hover {
+ background-color: @func_color_r;
+ }
+
+
+ }
+
+ .i-sdc-designer-leftbar-section .i-sdc-designer-leftbar-section-content .i-sdc-designer-leftbar-section-content-item {
+ max-height: 0px;
+ margin: 0 auto;
+ transition: all .3s;
+ }
+
+ .i-sdc-designer-leftbar-section.expanded .i-sdc-designer-leftbar-section-content .i-sdc-designer-leftbar-section-content-item {
+ max-height: 64px;
+ margin: 0 auto 1px auto;
+ // padding: 4px 13px;
+ }
+
+ .i-sdc-designer-leftbar-section.expanded .i-sdc-designer-leftbar-section-content .i-sdc-designer-leftbar-section-content-subcat {
+ margin: 0;
+ }
+
+ .i-sdc-designer-leftbar-section-content-item-icon-ph {
+ display: inline-block;
+ margin: 12px 0 12px 10px;
+ pointer-events: auto;
+
+ .non-certified {
+ position: relative;
+ left: 27px;
+ bottom: 6px;
+ .sprite;
+ .s-sdc-state-non-certified;
+ display: block;
+
+ &.smaller-icon {
+ bottom: 6px;
+ left: 13px;
+ }
+ }
+
+
+
+ }
+
+ .non-certified {
+ position: relative;
+ left: 43px;
+ bottom: 3px;
+ .sprite;
+ .s-sdc-state-non-certified;
+ display: block;
+
+ &.smaller-icon {
+ left: 35px;
+ bottom: -14px;
+ }
+ }
+ /*
+ .i-sdc-composition-leftbar-section-content-item-icon {
+ background-image: url('../../../styles/images/resource-icons/default.png');
+ // position: absolute;
+ right: 20px;
+ top: 10px;
+ height: 40px;
+ width: 40px;
+ background-size: 40px;
+ }
+ */
+
+ .i-sdc-designer-leftbar-section-content-item-info {
+ display: inline-block;
+ // margin-left: 10px;
+ //overflow: hidden;
+ // vertical-align: middle;
+ width: 160px;
+ padding: 0 0 0 10px;
+ }
+
+ .i-sdc-designer-leftbar-section-content-item-info-title {
+ .m_14_m;
+ line-height: 14px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-transform: uppercase;
+ max-width: 120px;
+ display: inline-block;
+ white-space: nowrap;
+ vertical-align: bottom;
+ }
+
+ .i-sdc-designer-leftbar-section-content-item-info-text {
+ .p_3;
+ line-height: 15px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ //margin: -1px 0 2px 0;
+ }
+
+ .i-sdc-designer-leftbar-section-content-item-info-text-link {
+ color: @color_s;
+ text-decoration: underline;
+ float: right;
+ position: absolute;
+ right: 17px;
+ // bottom: 5px;
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+ // Form actions
+ // ---------------------------------------------------------------------------------------------------
+ .w-sdc-form-actions-container.add-property {
+ text-align: center;
+ width: 100%;
+ margin-top: 2px;
+ margin-bottom: 12px;
+
+ .w-sdc-form-action {
+ width: 245px;
+ }
+ .w-sdc-form-action.add-property-add-another {
+ .bg_a;
+ margin-left: 35px;
+ }
+ .w-sdc-form-action.add-property-done {
+ margin-left: 312px;
+ }
+ .w-sdc-form-action.save {
+ margin-left: 327px;
+ margin-bottom: 30px;
+ }
+
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+ // Top menu
+ // ---------------------------------------------------------------------------------------------------
+ .w-sdc-header-menu {
+ padding: 25px 0;
+ text-align: center;
+ white-space: nowrap;
+ }
+
+ .i-sdc-header-menu-item {
+ cursor: pointer;
+ display: inline-block;
+ height: 43px;
+ min-width: 93px;
+ padding: 0 38px;
+ position: relative;
+ vertical-align: middle;
+
+ &::after {
+ border-right: 1px solid @color_m;
+ content: '';
+ display: block;
+ height: 43px;
+ right: 0;
+ position: absolute;
+ top: 0;
+ width: 2px;
+ }
+
+ &:first-child {
+ &::before {
+ border-right: 1px solid @color_m;
+ content: '';
+ display: block;
+ height: 43px;
+ left: 0;
+ position: absolute;
+ top: 0;
+ width: 2px;
+ }
+ }
+ }
+
+ .i-sdc-header-menu-item-icon {
+ display: inline-block;
+ height: 20px;
+ width: 28px;
+ }
+
+ .i-sdc-header-menu-item-label {
+ .g_1;
+ line-height: 18px;
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+ // Canvas inline menu
+ // ---------------------------------------------------------------------------------------------------
+ .w-sdc-canvas-menu {
+ position: fixed;
+ z-index: 100;
+
+ border-style: solid;
+ border-width: 1px;
+ border-color: #d8d8d8;
+ box-sizing: border-box;
+ background-color: #ffffff;
+ box-shadow: 0px 2px 2px 0px rgba(24, 24, 25, 0.1);
+ width: 91px;
+
+/* &.vl-type-select{
+ width: 173px;
+ }
+*/
+
+ h3 {
+ color: @func_color_s;
+ font-size: 14px;
+ font-weight: bold;
+ margin: 0;
+ padding: 7px 11px;
+ border-bottom: 1px solid #e5e5e5;
+ }
+
+ .w-sdc-canvas-menu-content {
+ padding: 5px 5px;
+
+ &.vl-select{
+ border-bottom: #d8d8d8 solid 1px;
+ line-height: 15px;
+
+ .tlv-radio {
+ padding: 3px 0px;
+
+ .tlv-radio-label {
+ padding: 3px 0px;
+
+ &::before {
+ margin-right: 10px;
+ }
+ }
+ }
+ }
+
+ .w-sdc-canvas-menu-content-update-button {
+ .sprite;
+ .sprite.e-sdc-small-icon-delete;
+ .hand;
+ position: absolute;
+ top: 15px;
+ right: 10px;
+ }
+ .w-sdc-canvas-menu-content-delete-button {
+ .sprite;
+ .sprite.e-sdc-small-icon-delete;
+ .hand;
+ margin: 0 8px 0 6px;
+ }
+ }
+
+ .w-sdc-canvas-menu-arrow {
+ //TODO: Missing image for small blue triangle.
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkE1OTIzNDI1MENFQjExRTU4ODRERTI1MDM2REZCOUYzIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkE1OTIzNDI2MENFQjExRTU4ODRERTI1MDM2REZCOUYzIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU5MjM0MjMwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTU5MjM0MjQwQ0VCMTFFNTg4NERFMjUwMzZERkI5RjMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4gBXTlAAAAOElEQVR42mK0rp7NgASMgZgFiE/CBJjQJPcA8U4gNkdXAJMUAGJ+ZEVMaJIwAFfEhEUSRRFAgAEAVtgJyiLAPWAAAAAASUVORK5CYII=');
+ content: '';
+ display: block;
+ height: 21px;
+ position: absolute;
+ right: 12px;
+ top: -24px;
+ width: 184px;
+ background-repeat: no-repeat;
+ background-position: 175px 16px;
+ }
+
+ }
+}
+/*.right-tab-loader {
+ border: 16px solid #f3f3f3; !* Light grey *!
+ border-top: 16px solid #3498db; !* Blue *!
+ border-radius: 50%;
+ width: 120px;
+ height: 120px;
+ animation: spin 2s linear infinite;
+}*/
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts
new file mode 100644
index 0000000000..5bb5d2cbbd
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+
+ export interface IArtifactsViewModelScope extends ICompositionViewModelScope {
+ artifacts: Array<Models.ArtifactModel>;
+ artifactType: string;
+ downloadFile:Models.IFileDownload;
+ isLoading:boolean;
+
+ getTitle(): string;
+ addOrUpdate(artifact:Models.ArtifactModel): void;
+ delete(artifact:Models.ArtifactModel): void;
+ download(artifact:Models.ArtifactModel): void;
+ openEditEnvParametersModal(artifact:Models.ArtifactModel):void;
+ getEnvArtifact(heatArtifact:Models.ArtifactModel):any;
+ getEnvArtifactName(artifact:Models.ArtifactModel):string;
+ isLicenseArtifact(artifact:Models.ArtifactModel):boolean;
+ isVFiArtifact(artifact:Models.ArtifactModel):boolean;
+ }
+
+ export class ResourceArtifactsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ '$state',
+ 'sdcConfig',
+ 'ArtifactsUtils',
+ 'ModalsHandler',
+ 'Sdc.Services.CacheService'
+ ];
+
+ constructor(private $scope:IArtifactsViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $state:any,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private artifactsUtils:Sdc.Utils.ArtifactsUtils,
+ private ModalsHandler: Utils.ModalsHandler,
+ private cacheService:Services.CacheService) {
+
+ this.initScope();
+ }
+
+
+ private initArtifactArr = (artifactType:string):void => {
+ let artifacts:Array<Models.ArtifactModel> = [];
+
+ if (this.$scope.selectedComponent) {
+ if ('interface' == artifactType) {
+ let interfaces = this.$scope.selectedComponent.interfaces;
+ if (interfaces && interfaces.standard && interfaces.standard.operations) {
+
+ angular.forEach(interfaces.standard.operations, (operation:any, interfaceName:string):void => {
+ let item:Sdc.Models.ArtifactModel = <Sdc.Models.ArtifactModel>{};
+ if (operation.implementation) {
+ item = <Sdc.Models.ArtifactModel> operation.implementation;
+ }
+ item.artifactDisplayName = interfaceName;
+ item.artifactLabel = interfaceName;
+ item.mandatory = false;
+ artifacts.push(item);
+ });
+ }
+ }else {
+ //init normal artifacts, deployment or api artifacts
+ let artifactsObj:Models.ArtifactGroupModel;
+ switch (artifactType) {
+ case "api":
+ artifactsObj = (<Models.Components.Service>this.$scope.selectedComponent).serviceApiArtifacts;
+ break;
+ case "deployment":
+ if (!this.$scope.isComponentInstanceSelected()) {
+ artifactsObj = this.$scope.selectedComponent.deploymentArtifacts;
+ } else {
+ artifactsObj = this.$scope.currentComponent.selectedInstance.deploymentArtifacts;
+ }
+ break;
+ default:
+ artifactsObj = this.$scope.selectedComponent.artifacts;
+ break;
+ }
+ _.forEach(artifactsObj, (artifact:Models.ArtifactModel, key) => {
+ artifacts.push(artifact);
+ });
+ }
+ }
+ this.$scope.artifacts = artifacts;
+ };
+
+ private openEditArtifactModal = (artifact:Models.ArtifactModel):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/artifact-form/artifact-form-view.html'),
+ controller: 'Sdc.ViewModels.ArtifactResourceFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ artifact: ():Models.ArtifactModel => {
+ return artifact;
+ },
+ component: (): Models.Components.Component => {
+ return this.$scope.currentComponent;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ modalInstance
+ .result
+ .then(():void => {
+ this.initArtifactArr(this.$scope.artifactType);
+ });
+ };
+
+ private initScope = ():void => {
+ let self = this;
+ this.$scope.isLoading= false;
+ this.$scope.artifactType = this.artifactsUtils.getArtifactTypeByState(this.$state.current.name);
+ this.initArtifactArr(this.$scope.artifactType);
+
+ this.$scope.getTitle = ():string => {
+ return this.artifactsUtils.getTitle(this.$scope.artifactType, this.$scope.selectedComponent);
+ };
+
+ let vfiArtifactTypes:any = this.cacheService.get('UIConfiguration').artifacts.deployment.resourceInstanceDeploymentArtifacts;
+
+ this.$scope.isVFiArtifact=(artifact:Models.ArtifactModel):boolean=>{
+ return vfiArtifactTypes[artifact.artifactType];
+ }
+
+ this.$scope.$watch('selectedComponent', (newResource:Models.Components.Component):void => {
+ if (newResource) {
+ this.initArtifactArr(this.$scope.artifactType);
+ }
+ });
+
+
+ this.$scope.$watch('currentComponent.selectedInstance', (newInstance:Models.ComponentsInstances.ComponentInstance):void => {
+ if (newInstance) {
+ this.initArtifactArr(this.$scope.artifactType);
+ }
+ });
+
+ this.$scope.addOrUpdate = (artifact:Models.ArtifactModel):void => {
+ this.artifactsUtils.setArtifactType(artifact, this.$scope.artifactType);
+ let artifactCopy = new Models.ArtifactModel(artifact);
+ this.openEditArtifactModal(artifactCopy);
+ };
+
+
+ this.$scope.delete = (artifact:Models.ArtifactModel):void => {
+
+ let onOk = ():void => {
+ this.$scope.isLoading= true;
+ this.artifactsUtils.removeArtifact(artifact, this.$scope.artifacts);
+
+ let success = (responseArtifact:Models.ArtifactModel):void => {
+ this.initArtifactArr(this.$scope.artifactType);
+ this.$scope.isLoading= false;
+ };
+
+ let error =(error:any):void =>{
+ console.log('Delete artifact returned error:', error);
+ this.initArtifactArr(this.$scope.artifactType);
+ this.$scope.isLoading= false;
+ };
+ if(this.$scope.isComponentInstanceSelected()){
+ this.$scope.currentComponent.deleteInstanceArtifact(artifact.uniqueId, artifact.artifactLabel).then(success, error);
+ }else{
+ this.$scope.currentComponent.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(success, error);//TODO simulate error (make sure error returns)
+ }
+ };
+ let title: string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE");
+ let message: string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+
+
+ this.$scope.getEnvArtifact = (heatArtifact:Models.ArtifactModel):any=>{
+ return _.find(this.$scope.artifacts, (item:Models.ArtifactModel)=>{
+ return item.generatedFromId === heatArtifact.uniqueId;
+ });
+ };
+
+ this.$scope.getEnvArtifactName = (artifact:Models.ArtifactModel):string =>{
+ let envArtifact = this.$scope.getEnvArtifact(artifact);
+ if(envArtifact){
+ return envArtifact.artifactDisplayName;
+ }
+ };
+
+ this.$scope.isLicenseArtifact = (artifact:Models.ArtifactModel) :boolean => {
+ let isLicense:boolean = false;
+ if(this.$scope.component.isResource() && (<Resource>this.$scope.component).isCsarComponent()) {
+ isLicense = this.artifactsUtils.isLicenseType(artifact.artifactType);
+ }
+
+ return isLicense;
+ };
+
+ this.$scope.openEditEnvParametersModal = (artifact:Models.ArtifactModel):void => {
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get('/app/scripts/view-models/forms/env-parameters-form/env-parameters-form.html'),
+ controller: 'Sdc.ViewModels.EnvParametersFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ resolve: {
+ artifact: ():Models.ArtifactModel => {
+ return artifact;
+ },
+ component: (): Models.Components.Component => {
+ return this.$scope.currentComponent;
+ }
+ }
+ };
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ modalInstance
+ .result
+ .then(():void => {
+ this.initArtifactArr(this.$scope.artifactType);
+ });
+ };
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html
new file mode 100644
index 0000000000..8c0138964f
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view.html
@@ -0,0 +1,55 @@
+<perfect-scrollbar class="w-sdc-designer-sidebar-tab-content artifacts">
+ <div class="w-sdc-designer-sidebar-section">
+ <expand-collapse
+ expanded-selector=".w-sdc-designer-sidebar-section-content" class="w-sdc-designer-sidebar-section-title">
+ <span class="w-sdc-designer-sidebar-section-title-text" data-ng-bind="getTitle()" tooltips tooltip-content="{{getTitle()}}"></span>
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content" data-ng-hide="'deployment' == artifactType && !selectedComponent.isComplex()">
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <div class="i-sdc-designer-sidebar-section-content-item-artifact"
+ data-ng-repeat="artifact in artifacts | orderBy: ['-mandatory', 'artifactDisplayName'] track by $index" data-ng-if="!isComponentInstanceSelected() || (isVFiArtifact(artifact)|| artifact.esId) && 'HEAT_ENV' !== artifact.artifactType">
+ <span data-ng-if="isComponentInstanceSelected() && artifact.heatParameters.length" class="i-sdc-designer-sidebar-section-content-item-file-link"></span>
+ <div class="i-sdc-designer-sidebar-section-content-item-artifact-details" data-ng-class="{'heat':artifact.isHEAT() && artifact.heatParameters.length}">
+ <div class="i-sdc-designer-sidebar-section-content-item-artifact-filename" data-tests-id="artifactName"
+ data-ng-class="{'hand enabled':!isComponentInstanceSelected() && artifact.heatParameters.length && !isViewMode()}"
+ data-ng-bind="artifact.artifactName" tooltips tooltip-content="{{artifact.artifactName}}"
+ data-ng-click="!isViewMode() && !isComponentInstanceSelected() && artifact.heatParameters.length && openEditEnvParametersModal(artifact)" data-ng-if="artifact.artifactName"></div>
+ <div>
+ <span class="i-sdc-designer-sidebar-section-content-item-artifact-details-name" data-tests-id="artifact_Display_Name-{{artifact.artifactDisplayName}}"
+ data-ng-class="{'hand enabled': (!isComponentInstanceSelected()||isVFiArtifact(artifact)) && !isViewMode() && !artifact.isHEAT() && !artifact.isThirdParty() && !isLicenseArtifact(artifact)}"
+ data-ng-bind="artifact.artifactDisplayName" data-ng-click="!isViewMode() && !isLoading && (!isComponentInstanceSelected()||isVFiArtifact(artifact)) && !artifact.isHEAT() && !artifact.isThirdParty() && !isLicenseArtifact(artifact) && addOrUpdate(artifact)"
+ tooltips tooltip-content="{{artifact.artifactDisplayName}}"></span>
+ <div class="i-sdc-designer-sidebar-section-content-item-artifact-heat-env" ng-if="isComponentInstanceSelected() && artifact.heatParameters.length">
+ <span class="enabled" data-ng-bind="getEnvArtifactName(artifact)" data-ng-click="!isViewMode() && addOrUpdate(getEnvArtifact(artifact))"></span>
+ <download-artifact class="i-sdc-designer-sidebar-section-content-item-button download-env sprite e-sdc-small-download hand" artifact="getEnvArtifact(artifact)"
+ component="currentComponent" instance="true"
+ data-tests-id="download"></download-artifact>
+ </div>
+ </div>
+
+ <div class="i-sdc-designer-sidebar-section-content-item-artifact-details-desc">
+ <span class="i-sdc-designer-sidebar-section-content-item-artifact-details-desc-label" data-ng-show="artifact.description">Description:</span>{{artifact.description}}
+ </div>
+ </div>
+ <button ng-if="!isViewMode() && artifact.esId && (!isComponentInstanceSelected()||isVFiArtifact(artifact)) && !artifact.isHEAT() && !artifact.isThirdParty() && !isLicenseArtifact(artifact)" class="i-sdc-designer-sidebar-section-content-item-button delete sprite e-sdc-small-icon-delete"
+ data-tests-id="delete" data-ng-click="delete(artifact)" type="button"></button>
+ <button ng-if="!isViewMode() && isComponentInstanceSelected() && (getEnvArtifact(artifact)).heatParameters.length"
+ class="i-sdc-designer-sidebar-section-content-item-button attach sprite e-sdc-small-icon-pad"
+ data-ng-click="openEditEnvParametersModal(getEnvArtifact(artifact))" type="button"></button>
+ <download-artifact ng-if="artifact.esId && 'deployment' != artifactType" class="i-sdc-designer-sidebar-section-content-item-button download sprite e-sdc-small-download hand"
+ artifact="artifact" component="selectedComponent" data-tests-id="download"></download-artifact>
+ <download-artifact ng-if="artifact.esId && 'deployment' == artifactType" class="i-sdc-designer-sidebar-section-content-item-button download sprite e-sdc-small-download hand"
+ artifact="artifact" component="currentComponent" instance="isComponentInstanceSelected()" data-tests-id="download"></download-artifact>
+ <button ng-if="!isViewMode() && !artifact.esId && artifactType==='deployment' && !isComponentInstanceSelected() && !artifact.isThirdParty()" class="i-sdc-designer-sidebar-section-content-item-button attach sprite e-sdc-small-icon-upload"
+ data-ng-click="addOrUpdate(artifact)" type="button" data-tests-id="add_Artifact"></button>
+ </div>
+ </div>
+
+ </div>
+ <div class="w-sdc-designer-sidebar-section-footer" data-ng-if="!isViewMode() && artifactType!=='api' && (!isComponentInstanceSelected()||selectedComponent.resourceType=='VF') && !currentComponent.isProduct() && ('deployment' != artifactType || selectedComponent.isComplex())">
+ <button class="w-sdc-designer-sidebar-section-footer-action tlv-btn blue" data-tests-id="add_Artifact_Button" data-ng-click="addOrUpdate({})" type="button">Add Artifact</button>
+ </div>
+ </div>
+</perfect-scrollbar>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less
new file mode 100644
index 0000000000..5726ca66fc
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less
@@ -0,0 +1,172 @@
+.w-sdc-designer-sidebar-tab-content.artifacts {
+
+ .i-sdc-designer-sidebar-section-content-item-artifact.hand {
+ .hand;
+ }
+
+ .w-sdc-designer-sidebar-section-content {
+ padding: 0;
+ }
+ .w-sdc-designer-sidebar-section-title {
+ &.expanded {
+ margin-bottom: 0;
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-artifact-details {
+ display: inline-block;
+ margin-left: 5px;
+ vertical-align: middle;
+ width: 180px;
+ &.heat {
+ line-height: 18px;
+ width: 250px;
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-artifact-details-name {
+ .g_7;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width:220px;
+ display: inline-block;
+ //text-transform: capitalize;
+ &.enabled {
+ &:hover {
+ .a_7;
+ }
+ }
+
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-artifact-heat-env {
+ .g_7;
+ margin-top: 6px;
+ line-height: 42px;
+ padding-top: 10px;
+ border-top:1px solid #c8cdd1;
+ .enabled {
+ &:hover {
+ .hand;
+ .a_7;
+ }
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-artifact-filename {
+ .g_7;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width: 225px;
+ display: inline-block;
+ .bold;
+ &.enabled {
+ &:hover {
+ .a_7;
+ }
+ }
+ }
+
+
+ .i-sdc-designer-sidebar-section-content-item-file-link{
+ border-left: 1px #848586 solid;
+ height: 58px;
+ margin-left: -11px;
+ margin-top: 11px;
+ border-top: 1px #848586 solid;
+ border-bottom: 1px #848586 solid;
+ width: 12px;
+ float: left;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-artifact-details-desc {
+ display: none;
+ line-height: 16px;
+ word-wrap: break-word;
+ white-space: normal;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-artifact-details-desc-label {
+ .b_3;
+ }
+
+
+ .i-sdc-designer-sidebar-section-content-item-artifact {
+ border-bottom: 1px solid #c8cdd1;
+ padding: 5px 10px 5px 18px;
+ position: relative;
+ // line-height: 36px;
+ min-height: 61px;
+ cursor: default;
+ display: flex;
+ align-items: center;
+
+
+ .i-sdc-designer-sidebar-section-content-item-button {
+ top: 20px;
+ line-height: 10px;
+ }
+
+ &:hover {
+ //background-color: @color_c;
+ .bg_c;
+ transition: all .3s;
+
+ .i-sdc-designer-sidebar-section-content-item-button {
+ display: block;
+
+ }
+
+ }
+ }
+
+}
+
+///////////////////Lifecycle Management
+.i-sdc-designer-sidebar-section-content-item-lm {
+ .b_7;
+ border-bottom: 1px solid @color_e;
+ cursor: pointer;
+ height: 65px;
+ padding: 22px 0;
+ position: relative;
+
+ &:hover {
+ .bg_c_hover;
+ margin-left: -10px;
+ margin-right: -10px;
+ padding: 22px 10px;
+
+ .i-sdc-designer-sidebar-section-content-item-lm-icon {
+ right: 16px;
+ }
+ }
+}
+
+.i-sdc-designer-sidebar-section-content-item-lm:first-child {
+ margin-top: -18px;
+}
+
+.i-sdc-designer-sidebar-section-content-item-lm-icon {
+ position: absolute;
+ right: 6px;
+
+ //TODO: Replace the icons.
+ &.icon-view {
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAJCAYAAAACTR1pAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjdGMDNBRUJDMDkxNjExRTVCMjRBOEI5QzMxQTlBQjY4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjdGMDNBRUJEMDkxNjExRTVCMjRBOEI5QzMxQTlBQjY4Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6N0YwM0FFQkEwOTE2MTFFNUIyNEE4QjlDMzFBOUFCNjgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6N0YwM0FFQkIwOTE2MTFFNUIyNEE4QjlDMzFBOUFCNjgiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4U2decAAABRUlEQVR42pyRPUvDUBSG39t2UyyaBgvWQZMqKoFq4lBH0U0UqXSxKPaPieCoWPAPKIgdjG1ohaghOrSR1iaLKMXpmnMhQVfPcLmc8zxwPhjnHFE4rx3eMO/w4rr4+vwQuZHRMcwqClaMVeRnplnEskg8O7/g96YJSZLgBz5+R0bKIAgC6IaBvdKukBP0HJ+cctu2cVCtQsrIUFUVlcp+LE6EItWIIVaIV9c3/PnRRqlcxkJeYV2vi+/hEO/9QSx6b56oEUMsOal6/TacQcXinCpaGE+n0QnljufFIuUoiAlZTk6iWFyD6zpoWi3RwvrmBlLJZCzRn3IUxBBLjlgO9e06T9ja3sGyrqPX66NtWQLWCgVks5Owmg1c1mpQ8vM4OqywP1s1w1PkpnJY0jTIsizy/sDHQ7sFmt0ITxJtlf33jj8CDADhB52tEX6ifAAAAABJRU5ErkJggg==');
+ height: 9px;
+ top: 29px;
+ width: 14px;
+ }
+
+ //TODO: Replace the icons.
+ &.icon-alert {
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAANCAYAAAB2HjRBAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjhBM0YxQTBCMDkyMDExRTVBNzlCQUYxNEYwMDUwOTQ5IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjhBM0YxQTBDMDkyMDExRTVBNzlCQUYxNEYwMDUwOTQ5Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6OEEzRjFBMDkwOTIwMTFFNUE3OUJBRjE0RjAwNTA5NDkiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OEEzRjFBMEEwOTIwMTFFNUE3OUJBRjE0RjAwNTA5NDkiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7exgceAAAB5klEQVR42oySy2tTQRTGfzN3cpvbpFTaUlzUjeCuVFyJf4DuCl34wL/CrbpUunLnwpUuFFy48IFiwYWhpYuW+gCLrbWKBQumaRIba5ub3MccJ5WECCV44MDMme873/lmBhGhnZulcn/3/sfrO7J/45R017pT0xUS2bC93vy2LAOLt5DfCeUn14RD4h/ysbHRDii/8pyo7DmFiP7VZ4Rbu9KT3I7i+qJklp7ibQt2J6HeNOhXV/gv8tDcbaLtXYLJKeTcZfxqSPPzvGu6ID3J1Q8z0ny/hIkM5swF8qenUFUfwixHCtd7Kwez04jOgEmc+hq6tuZ8N9E6xG5U+Pn2kRxKLs7eE7tRc0XlToRM2sD+2sGmDiY+kWsSFG528Kr1Xgfjfn0n5uElvFrrzVqVDHqkiTKjxCsV9LBx08QkgcY/eZH+89Oqo+x9eumkwQaQaoPEKWpsHHN8ArItgLt5UdiG6736mFLxixyQt+bviik8QPuaNFRIkjqQRZ2YJDo6QWydjTRtOcGPPdJKgnlxFfN9fVmGNhecR4U3mLrL6nPTRTQHXZO5+4jyyI4Iqs99GJvgBiKjcySlj3897y6/kbreI4w0QS7ASxrsa4/cXkxkIzK5PKmNqRMw7NfdeczA+Fn1R4ABAPnMAeCjkgf5AAAAAElFTkSuQmCC');
+ height: 13px;
+ top: 27px;
+ width: 15px;
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts
new file mode 100644
index 0000000000..b28de8d331
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts
@@ -0,0 +1,132 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+
+ export interface IEditResourceVersion {
+ allVersions: any;
+ changeVersion: string;
+ }
+
+ interface IDetailsViewModelScope extends ICompositionViewModelScope {
+ isLoading: boolean;
+ $parent: ICompositionViewModelScope;
+ expandedSection: Array<string>;
+ editForm:ng.IFormController;
+ editResourceVersion: IEditResourceVersion;
+
+ changeResourceVersion(): void;
+ }
+
+ export class DetailsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'LeftPaletteLoaderService',
+ 'EventListenerService'
+
+ ];
+
+ constructor(private $scope:IDetailsViewModelScope,
+ private LeftPaletteLoaderService:Services.Components.LeftPaletteLoaderService,
+ private eventListenerService:Services.EventListenerService) {
+ this.initScope();
+ }
+
+ private clearSelectedVersion = ():void => {
+ this.$scope.editResourceVersion = {
+ allVersions: {},
+ changeVersion: null
+ };
+ };
+
+ private versioning:Function = (versionNumber:string):string => {
+ let version:Array<string> = versionNumber.split('.');
+ return '00000000'.slice(version[0].length) + version[0] + '.' + '00000000'.slice(version[1].length) + version[1];
+ };
+
+ private initEditResourceVersion = ():void => {
+ this.clearSelectedVersion();
+ this.$scope.editResourceVersion.allVersions[this.$scope.currentComponent.selectedInstance.componentVersion] = this.$scope.currentComponent.selectedInstance.componentUid;
+ _.merge(this.$scope.editResourceVersion.allVersions, angular.copy(this.$scope.selectedComponent.allVersions));
+ let sorted:any= _.sortBy(_.toPairs(this.$scope.editResourceVersion.allVersions), (item)=>{
+ return this.versioning(item[0]);
+ });
+ this.clearSelectedVersion();
+ _.forEach(sorted, (item)=> {
+ this.$scope.editResourceVersion.allVersions[item[0]]= item[1];
+ });
+
+ let highestVersion = _.last(Object.keys(this.$scope.selectedComponent.allVersions));
+
+ //TODO - ask ronny - what happend if the parent is not in the leftPalette (instance of csar for example)
+ if (parseFloat(highestVersion) % 1) { //if highest is minor, make sure it is the latest checked in -
+ let latestVersionComponent:Models.Components.Component = _.find(this.LeftPaletteLoaderService.getFullDataComponentListWithVls(this.$scope.currentComponent.componentType), (component:Models.Components.Component) => { //latest checked in
+ return (component.systemName === this.$scope.selectedComponent.systemName
+ || component.uuid === this.$scope.selectedComponent.uuid);
+ });
+ let latestVersion:string = latestVersionComponent ? latestVersionComponent.version : highestVersion;
+
+ if (highestVersion != latestVersion) { //highest is checked out - remove from options
+ this.$scope.editResourceVersion.allVersions = _.omit(this.$scope.editResourceVersion.allVersions, highestVersion);
+ }
+ }
+ this.$scope.editResourceVersion.changeVersion = this.$scope.currentComponent.selectedInstance.componentVersion;
+ };
+
+ private initScope = ():void => {
+ this.$scope.isLoading = false;
+ this.$scope.$parent.isLoading = false;
+ this.$scope.expandedSection = ['general', 'tags'];
+ //this.clearSelectedVersion();
+
+ this.$scope.$watch('selectedComponent', (component:Models.Components.Component) => {
+ if (this.$scope.isComponentInstanceSelected()) {
+ this.initEditResourceVersion();
+ }
+ });
+
+ this.$scope.changeResourceVersion = ():void => {
+ this.$scope.isLoading = true;
+ this.$scope.$parent.isLoading = true;
+
+ let onSuccess = (component:Models.Components.Component)=> {
+ this.$scope.isLoading = false;
+ this.$scope.$parent.isLoading = false;
+ this.$scope.setComponent(component);
+ this.$scope.updateSelectedComponent();
+
+ this.eventListenerService.notifyObservers(Sdc.Utils.Constants.GRAPH_EVENTS.ON_VERSION_CHANGED, this.$scope.currentComponent);
+ };
+
+ let onFailed = (error:any)=> {
+ this.$scope.isLoading = false;
+ this.$scope.$parent.isLoading = false;
+ console.log(error);
+ };
+
+ let componentUid:string = this.$scope.editResourceVersion.allVersions[this.$scope.editResourceVersion.changeVersion];
+ this.$scope.currentComponent.changeComponentInstanceVersion(componentUid).then(onSuccess, onFailed);
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view.html
new file mode 100644
index 0000000000..6ae462760c
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details-view.html
@@ -0,0 +1,129 @@
+<perfect-scrollbar include-padding="true" class="w-sdc-designer-sidebar-tab-content details">
+
+ <div class="w-sdc-designer-sidebar-section">
+ <loader data-display="isLoading"></loader>
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content.general" class="w-sdc-designer-sidebar-section-title">
+
+ General Info
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content general">
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Type:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-tests-id="rightTab_componentType" data-ng-bind="selectedComponent.componentType"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-if="selectedComponent.isResource()">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Resource Type:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-if="selectedComponent.isResource()" data-ng-bind="selectedComponent.resourceType"
+ tooltips tooltip-content="&#8203;{{selectedComponent.resourceType | resourceTypeName}}"
+ data-tests-id="rightTab_resourceType"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item">
+
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Version:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value"
+ data-ng-if="!isComponentInstanceSelected() || selectedComponent.isVl()" data-tests-id="rightTab_version" data-ng-bind="selectedComponent.version"></span>
+
+ <ng-form name="editForm" data-ng-if="isComponentInstanceSelected() && !selectedComponent.isVl()">
+ <select data-ng-model="editResourceVersion.changeVersion" name="changeVersion" data-ng-disabled="$parent.isViewOnly"
+ class="i-sdc-designer-sidebar-section-content-item-value i-sdc-form-select"
+ data-ng-class="{'minor': (editResourceVersion.changeVersion)%1}"
+ data-ng-change="changeResourceVersion()">
+ <option class="select-instance-version" data-ng-class="{'minor': key%1}"
+ ng-repeat="(key, value) in editResourceVersion.allVersions">{{key}}</option>
+ </select></ng-form>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-if="selectedComponent.categories && selectedComponent.categories[0]">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Category:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.categories[0].name"
+ tooltips tooltip-content="&#8203;{{selectedComponent.categories[0].name}}"
+ data-tests-id="rightTab_category"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-if="selectedComponent.categories && selectedComponent.categories[0] && selectedComponent.categories[0].subcategories">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Sub Category:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.categories[0].subcategories[0].name"
+ tooltips tooltip-content="&#8203;{{selectedComponent.categories[0].subcategories[0].name}}"
+ data-tests-id="rightTab_subCategory"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Creation Date:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.creationDate | date: 'MM/dd/yyyy'"
+ data-tests-id="rightTab_creationDate"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Author:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.creatorFullName"
+ tooltips tooltip-content="&#8203;{{selectedComponent.creatorFullName}}"
+ data-tests-id="rightTab_author">
+ </span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-if="selectedComponent.isService()">
+ <span class="i-sdc-designer-sidebar-section-content-item-label" translate="GENERAL_LABEL_PROJECT_CODE"></span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value"
+ data-tests-id="rightTab_projectCode" data-ng-bind="selectedComponent.projectCode"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-if="selectedComponent.isResource()">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Vendor Name:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.vendorName"
+ tooltips tooltip-content="&#8203;{{selectedComponent.vendorName}}"
+ data-tests-id="rightTab_vendorName">
+ </span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-if="selectedComponent.isResource()">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Vendor Release:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.vendorRelease"
+ tooltips tooltip-class="tooltip-custom break-word-tooltip" tooltip-content="&#8203;{{selectedComponent.vendorRelease}}"
+ data-tests-id="rightTab_vendorRelease">
+ </span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <span class="i-sdc-designer-sidebar-section-content-item-label" translate="GENERAL_LABEL_CONTACT_ID"></span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value" data-ng-bind="selectedComponent.contactId"
+ data-tests-id="rightTab_userId"></span>
+ </div>
+ <div class="i-sdc-designer-sidebar-section-content-item description">
+ <span class="i-sdc-designer-sidebar-section-content-item-label">Description:
+
+ <span class="i-sdc-designer-sidebar-section-content-description-item-value" ellipsis="selectedComponent.description" max-chars="55"
+ data-tests-id="rightTab_description"></span>
+ </span>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="w-sdc-designer-sidebar-section additionalInformation">
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content.additionalInformation" class="w-sdc-designer-sidebar-section-title">
+ Additional Information
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content additionalInformation">
+ <div class="i-sdc-designer-sidebar-section-content-item" data-ng-repeat="additionalInformation in selectedComponent.getAdditionalInformation() track by $index">
+ <span class="i-sdc-designer-sidebar-section-content-item-label additional-information" data-ng-bind="additionalInformation.key" tooltips tooltip-content="{{additionalInformation.key}}"></span>
+ <span class="i-sdc-designer-sidebar-section-content-item-label">:</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-value additional-information" data-ng-bind="additionalInformation.value"
+ tooltips tooltip-class="tooltip-custom break-word-tooltip" tooltip-content="{{additionalInformation.value}}"></span>
+ </div>
+ </div>
+ </div>
+
+
+ <div class="w-sdc-designer-sidebar-section tags">
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content.tags" class="w-sdc-designer-sidebar-section-title">
+ Tags
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content tags">
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <span class="i-sdc-designer-sidebar-section-content-item-tag" data-ng-repeat="tag in selectedComponent.tags track by $index" data-ng-bind="tag"
+ data-tests-id="rightTab_tag" tooltips tooltip-content="{{tag}}"></span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</perfect-scrollbar>
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details.less b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details.less
new file mode 100644
index 0000000000..e88e130379
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/details/details.less
@@ -0,0 +1,68 @@
+.w-sdc-designer-sidebar-tab-content.details {
+
+ .w-sdc-designer-sidebar-section-title + .w-sdc-designer-sidebar-section-content {
+ padding: 0 10px 0 18px;
+ }
+
+ .w-sdc-designer-sidebar-section-title.expanded + .w-sdc-designer-sidebar-section-content {
+ padding: 10px 10px 10px 18px;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-label {
+ font-weight: bold;
+ &.additional-information{
+ max-width:100px;
+ display: inline-block;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ vertical-align: bottom;
+ }
+
+ }
+
+
+
+ .i-sdc-designer-sidebar-section-content-item-value {
+ // .hyphenate;
+ padding-left: 10px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ display: inline-block;
+ max-width: 160px;
+ vertical-align:bottom;
+ font-weight: normal;
+ &.additional-information{
+ max-width:160px;
+ display: inline-block;
+ }
+ &.i-sdc-form-select {
+ .b_1;
+ border: 1px solid @border_color_f;
+ width: 210px;
+ max-width: 210px;
+ padding-left: 4px;
+
+ .select-instance-version {
+ .b_1;
+ &.minor {
+ .h_1;
+ }
+ }
+ }
+ &.minor {
+ .h_1;
+ }
+ }
+ .i-sdc-designer-sidebar-section-content-description-item-value{
+ max-width: none;
+ font-weight: normal;
+ }
+
+ .w-sdc-designer-sidebar-section.tags {
+ .i-sdc-designer-sidebar-section-content-item {
+ white-space: normal;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts
new file mode 100644
index 0000000000..aef25c51ce
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IResourcePropertiesAndAttributesViewModelScope extends ICompositionViewModelScope {
+ properties: Models.PropertiesGroup;
+ attributes: Models.AttributesGroup;
+ propertiesMessage: string;
+ addProperty(): void;
+ updateProperty(property:Models.PropertyModel): void;
+ deleteProperty(property:Models.PropertyModel): void;
+ viewAttribute(attribute:Models.AttributeModel): void;
+ groupNameByKey(key:string): string;
+ isPropertyOwner():boolean;
+ }
+
+ export class ResourcePropertiesViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ModalsHandler'
+ ];
+
+
+ constructor(private $scope:IResourcePropertiesAndAttributesViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private ModalsHandler: Utils.ModalsHandler) {
+
+ this.initScope();
+ }
+
+ private initComponentProperties = ():void => {
+ let result:Models.PropertiesGroup = {};
+
+ if(this.$scope.selectedComponent){
+ this.$scope.propertiesMessage = undefined;
+ if(this.$scope.isComponentInstanceSelected()){
+ if (this.$scope.currentComponent.selectedInstance.originType==='VF') {
+ // Temporally fix to hide properties for VF (UI stack when there are many properties)
+ this.$scope.propertiesMessage = "Note: properties for VF are disabled";
+ } else {
+ result[this.$scope.currentComponent.selectedInstance.uniqueId] = this.$scope.currentComponent.componentInstancesProperties[this.$scope.currentComponent.selectedInstance.uniqueId];
+ }
+ }else if(this.$scope.currentComponent.isService()){
+ // Temporally fix to hide properties for service (UI stack when there are many properties)
+ //result = this.$scope.currentComponent.componentInstancesProperties;
+ this.$scope.propertiesMessage = "Note: properties for service are disabled";
+ }else{
+ let key = this.$scope.selectedComponent.uniqueId;
+ result[key]= Array<Models.PropertyModel>();
+ let derived = Array<Models.PropertyModel>();
+ _.forEach(this.$scope.selectedComponent.properties, (property:Models.PropertyModel) => {
+ if(key == property.parentUniqueId){
+ result[key].push(property);
+ }else{
+ property.readonly = true;
+ derived.push(property);
+ }
+ });
+ if(derived.length){
+ result['derived']= derived;
+ }
+ }
+ this.$scope.properties = result;
+ }
+ };
+
+
+ private initComponentAttributes = ():void => {
+ let result:Models.AttributesGroup = {};
+
+ if(this.$scope.selectedComponent){
+ if(this.$scope.isComponentInstanceSelected()){
+ result[this.$scope.currentComponent.selectedInstance.uniqueId] = this.$scope.currentComponent.componentInstancesAttributes[this.$scope.currentComponent.selectedInstance.uniqueId];
+ }else if(this.$scope.currentComponent.isService()){
+ result = this.$scope.currentComponent.componentInstancesAttributes;
+ }
+ this.$scope.attributes = result;
+ }
+ };
+
+ private openEditPropertyModal = (property:Models.PropertyModel):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/property-form/property-form-view.html'),
+ controller: 'Sdc.ViewModels.PropertyFormViewModel',
+ size: 'sdc-l',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ property: ():Models.PropertyModel => {
+ return property;
+ },
+ component: ():Models.Components.Component => {
+ return this.$scope.currentComponent;
+ },
+ filteredProperties: ():Array<Models.PropertyModel> => {
+ return this.$scope.selectedComponent.properties
+ }
+ }
+ };
+
+
+ let modalInstance:ng.ui.bootstrap.IModalServiceInstance = this.$modal.open(modalOptions);
+ modalInstance
+ .result
+ .then(():void => {
+ // this.initComponentProperties();
+ });
+ };
+
+ private openAttributeModal = (atrribute:Models.AttributeModel):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/attribute-form/attribute-form-view.html'),
+ controller: 'Sdc.ViewModels.AttributeFormViewModel',
+ size: 'sdc-md',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ attribute: ():Models.AttributeModel => {
+ return atrribute;
+ },
+ component: ():Models.Components.Component => {
+ return this.$scope.currentComponent;
+ }
+ }
+ };
+ this.$modal.open(modalOptions);
+ };
+
+
+
+
+ private initScope = ():void => {
+ this.initComponentProperties();
+ this.initComponentAttributes();
+
+ this.$scope.$watchCollection('currentComponent.componentInstancesProperties', (newData:any):void => {
+ this.initComponentProperties();
+ });
+
+ this.$scope.$watchCollection('currentComponent.properties', (newData:any):void => {
+ this.initComponentProperties();
+ });
+
+ this.$scope.$watch('currentComponent.selectedInstance', (newInstance:Models.ComponentsInstances.ComponentInstance):void => {
+ if (angular.isDefined(newInstance)) {
+ this.initComponentProperties();
+ this.initComponentAttributes();
+ }
+ });
+
+ this.$scope.$watchCollection('currentComponent.componentInstancesAttributes', (newData:any):void => {
+ this.initComponentAttributes();
+ });
+
+ this.$scope.isPropertyOwner = ():boolean => {
+ return this.$scope.currentComponent && this.$scope.currentComponent.isResource() &&
+ !this.$scope.isComponentInstanceSelected();
+ };
+
+ this.$scope.addProperty = ():void => {
+ let property = new Models.PropertyModel();
+ this.openEditPropertyModal(property);
+ };
+
+ this.$scope.updateProperty = (property:Models.PropertyModel):void => {
+ this.openEditPropertyModal(property);
+ };
+
+ this.$scope.deleteProperty = (property:Models.PropertyModel):void => {
+
+ let onOk = ():void => {
+ this.$scope.currentComponent.deleteProperty(property.uniqueId);
+ };
+
+ let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+
+ this.$scope.viewAttribute = (attribute:Models.AttributeModel):void => {
+ this.openAttributeModal(attribute);
+ };
+
+ this.$scope.groupNameByKey = (key:string):string => {
+ switch (key){
+ case 'derived':
+ return "Derived";
+
+ case this.$scope.currentComponent.uniqueId:
+ return this.$filter("resourceName")(this.$scope.currentComponent.name);
+
+ default:
+ return this.$filter("resourceName")((_.find(this.$scope.currentComponent.componentInstances, {uniqueId:key})).name);
+ }
+ };
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html
new file mode 100644
index 0000000000..3022ee6e90
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view.html
@@ -0,0 +1,81 @@
+<perfect-scrollbar class="w-sdc-designer-sidebar-tab-content properties">
+ <div class="w-sdc-designer-sidebar-section">
+
+ <!--expand-collapse data-ng-if="isPropertyOwner() && !currentComponent.properties.length" expanded-selector=".w-sdc-composition-sidebar-section-content.{{currentComponent.name}}"
+ class="w-sdc-composition-sidebar-section-title">
+ <span class="w-sdc-composition-sidebar-section-title-text" tooltips tooltip-content="{{currentComponent.name | resourceName}}&nbsp;Properties"
+ data-ng-bind="(currentComponent.name | resourceName)+ ' Properties'"></span>
+ <div class="w-sdc-composition-sidebar-section-title-icon"></div>
+ </expand-collapse-->
+
+
+ <!--properties-->
+ <expand-collapse data-ng-repeat-start="(key, group) in properties"
+ expanded-selector=".w-sdc-designer-sidebar-section-content.properties.{{$index}}" class="w-sdc-designer-sidebar-section-title">
+ <span class="w-sdc-designer-sidebar-section-title-text" data-ng-bind="groupNameByKey(key) + ' Properties'"
+ tooltips tooltip-content="{{groupNameByKey(key)}}&nbsp;Properties"></span>
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="w-sdc-designer-sidebar-section-content properties {{$index}}"> <!--data-ng-show="isShowDetailsSection" -->
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <div class="i-sdc-designer-sidebar-section-content-item-property-and-attribute" data-tests-id="propertyRow"
+ data-ng-repeat="property in group | orderBy: 'name' track by $index">
+ <div>
+ <span class="i-sdc-designer-sidebar-section-content-item-property-and-attribute-label"
+ data-ng-class="{'hand enabled': !$parent.isViewOnly}"
+ tooltips tooltip-content="{{property.name}}"
+ data-ng-click="!$parent.isViewOnly && updateProperty(property)">{{property.name}}</span>
+ </div>
+ <div>
+ <span class="i-sdc-designer-sidebar-section-content-item-property-value" data-ng-if="isPropertyOwner()"
+ tooltips tooltip-content="{{property.defaultValue}}">{{property.defaultValue}}</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-property-value" data-ng-if="!isPropertyOwner()"
+ tooltips tooltip-content="{{property.value}}">{{property.value}}</span>
+ </div>
+ <button class="i-sdc-designer-sidebar-section-content-item-button delete sprite e-sdc-small-icon-delete"
+ data-ng-if="!$parent.isViewOnly&&(isPropertyOwner() && !property.readonly)"
+ data-ng-click="deleteProperty(property)" type="button"></button>
+ </div>
+ </div>
+
+ </div>
+ <div class="w-sdc-designer-sidebar-section-footer">
+ <button class="w-sdc-designer-sidebar-section-footer-action tlv-btn blue" data-ng-click="addProperty()" type="button" data-ng-if="!$parent.isViewOnly && isPropertyOwner()">
+ Add Property
+ </button>
+ </div>
+
+
+ <!--attributes-->
+ <expand-collapse data-ng-repeat-start="(key, group) in attributes"
+ expanded-selector=".w-sdc-designer-sidebar-section-content.attributes.{{$index}}" class="w-sdc-designer-sidebar-section-title">
+ <span class="w-sdc-designer-sidebar-section-title-text" data-ng-bind="groupNameByKey(key) + ' Attributes'"
+ tooltips tooltip-content="{{groupNameByKey(key)}}&nbsp;Attributes"></span>
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="w-sdc-designer-sidebar-section-content attributes {{$index}}"> <!--data-ng-show="isShowDetailsSection" -->
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <div class="i-sdc-designer-sidebar-section-content-item-property-and-attribute"
+ data-ng-repeat="attribute in group | orderBy: 'name' track by $index">
+ <div>
+ <span class="i-sdc-designer-sidebar-section-content-item-property-and-attribute-label"
+ data-ng-class="{'hand enabled': !$parent.isViewOnly}"
+ tooltips tooltip-content="{{attribute.name}}"
+ data-ng-click="!$parent.isViewOnly && viewAttribute(attribute)"
+ data-tests-id="{{attribute.name}}-attr">{{attribute.name}}</span>
+ </div>
+ <div>
+ <span class="i-sdc-designer-sidebar-section-content-item-property-value" data-ng-if="isPropertyOwner()"
+ tooltips tooltip-content="{{attribute.defaultValue}}">{{attribute.defaultValue}}</span>
+ <span class="i-sdc-designer-sidebar-section-content-item-property-value" data-ng-if="!isPropertyOwner()"
+ tooltips tooltip-content="{{attribute.value}}" data-tests-id="value-of-{{attribute.name}}">{{attribute.value}}</span>
+ </div>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+</perfect-scrollbar>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less
new file mode 100644
index 0000000000..2ad87b9fca
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less
@@ -0,0 +1,16 @@
+.w-sdc-designer-sidebar-tab-content.properties {
+ .i-sdc-designer-sidebar-section-content-item-property-and-attribute-label{
+ font-weight: bold;
+ }
+ .i-sdc-designer-sidebar-section-content-item-button.update{
+ right: 17px;
+ }
+ .i-sdc-designer-sidebar-section-content-item-button.delete{
+ right: 35px;
+ }
+
+ .w-sdc-designer-sidebar-properties-disabled {
+ .s_14_m;
+ padding: 20px 20px;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts
new file mode 100644
index 0000000000..119a59d5af
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../../references"/>>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IRelationsViewModelScope extends ICompositionViewModelScope {
+ isLoading: boolean;
+ $parent: ICompositionViewModelScope;
+ getRelation(requirement:any): any;
+ }
+
+ export class RelationsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter'
+ ];
+
+ constructor(private $scope:IRelationsViewModelScope,
+ private $filter:ng.IFilterService) {
+ this.initScope();
+ }
+
+
+ private updateRC = ():void =>{
+ if(this.$scope.currentComponent) {
+ this.$scope.currentComponent.updateRequirementsCapabilities();
+ }
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.isLoading = this.$scope.$parent.isLoading;
+
+ this.$scope.getRelation = (requirement:any):any => {
+
+ if(this.$scope.isComponentInstanceSelected() && this.$scope.currentComponent.componentInstancesRelations ) {
+ let relationItem = _.filter(this.$scope.currentComponent.componentInstancesRelations, (relation:any) => {
+ return relation.fromNode === this.$scope.currentComponent.selectedInstance.uniqueId &&
+ _.some(relation.relationships, {'requirement': requirement.name,
+ 'requirementOwnerId': requirement.ownerId});
+ });
+
+ if (relationItem && relationItem.length) {
+ return {
+ type: requirement.relationship.split('.').pop(),
+ requirementName: this.$filter('resourceName')(this.$scope.currentComponent.componentInstances[_.map
+ (this.$scope.currentComponent.componentInstances, "uniqueId").indexOf(relationItem[0].toNode)].name)
+ };
+ }
+ }
+ return null;
+ };
+
+ if(!this.$scope.isComponentInstanceSelected()) {
+ this.$scope.$watch('currentComponent.componentInstances + currentComponent.componentInstancesRelations', ():void => {
+ this.updateRC();
+ });
+
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view.html
new file mode 100644
index 0000000000..72eaae27cf
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations-view.html
@@ -0,0 +1,57 @@
+<perfect-scrollbar class="w-sdc-designer-sidebar-tab-content relations">
+
+ <div class="w-sdc-designer-sidebar-section w-sdc-designer-sidebar-section-relations">
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content.capabilities" class="w-sdc-designer-sidebar-section-title">
+ Capabilities
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content capabilities">
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-group" data-ng-repeat="(key, value) in (isComponentInstanceSelected() ? currentComponent.selectedInstance.capabilities : selectedComponent.capabilities) track by $index">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations" data-ng-repeat="capability in value track by $index">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details-name">{{capability.name}}&nbsp;
+ <span ng-if="selectedComponent.isComplex() && capability.ownerName"
+ class="i-sdc-designer-sidebar-section-content-item-relations-details-ownerName"
+ tooltips tooltip-class="tooltip-custom break-word-tooltip"
+ tooltip-content="{{capability.ownerName | resourceName}}">&nbsp;&nbsp;{{capability.ownerName | resourceName}}</span></div>
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details-desc">{{capability.type}}</div>
+ </div></div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="w-sdc-designer-sidebar-section w-sdc-designer-sidebar-section-relations">
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content.requirements" class="w-sdc-designer-sidebar-section-title">
+ Requirements
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content requirements">
+ <div class="i-sdc-designer-sidebar-section-content-item">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-group" data-ng-repeat="(key, value) in (isComponentInstanceSelected() ? currentComponent.selectedInstance.requirements : selectedComponent.requirements) track by $index">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations" data-ng-repeat="requirement in value track by $index">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details-name">{{requirement.name}}&nbsp;
+ <span ng-if="selectedComponent.isComplex() && requirement.ownerName"
+ class="i-sdc-designer-sidebar-section-content-item-relations-details-ownerName"
+ tooltips tooltip-class="tooltip-custom break-word-tooltip"
+ tooltip-content="{{requirement.ownerName | resourceName}}">&nbsp;&nbsp;{{requirement.ownerName | resourceName}}</span></div>
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details-desc">{{requirement.node}}
+ <div data-ng-if="getRelation(requirement) != null">
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details-indent-box"></div>
+ <div class="i-sdc-designer-sidebar-section-content-item-relations-details-child">
+ <span class="i-sdc-designer-sidebar-section-content-item-relations-details-desc">{{getRelation(requirement).type}} <br/></span>
+ <span class="i-sdc-designer-sidebar-section-content-item-relations-details-name">{{getRelation(requirement).requirementName}}</span>
+ </div>
+ </div>
+ </div>
+ </div></div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</perfect-scrollbar>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations.less b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations.less
new file mode 100644
index 0000000000..212b9785e9
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/relations/relations.less
@@ -0,0 +1,116 @@
+.w-sdc-designer-sidebar-tab-content.relations {
+
+ .w-sdc-designer-sidebar-section-content {
+ padding: 0;
+ }
+
+ .w-sdc-designer-sidebar-section-title {
+ &.expanded {
+ margin-bottom: 0;
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations {
+ border-bottom: 1px solid @color_e;
+ padding: 10px 10px 10px 18px;
+ position: relative;
+ cursor: default;
+
+ .i-sdc-designer-sidebar-section-content-item-button {
+ top: 15px;
+ line-height: 10px;
+ }
+
+ &:hover {
+ background-color: @color_c;
+
+ .i-sdc-designer-sidebar-section-content-item-button {
+ display: block;
+
+ }
+
+ }
+
+ }
+ .w-sdc-designer-sidebar-section-relations:not(:last-child) .i-sdc-designer-sidebar-section-content-item-relations-group:last-child .i-sdc-designer-sidebar-section-content-item-relations:last-child {
+ border-bottom: none;
+ }
+
+
+ .i-sdc-designer-sidebar-section-content-item-relations.hand {
+ .hand;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-group {
+ //border-bottom: 1px solid @color_e;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-details {
+ display: inline-block;
+ margin-left: 5px;
+ vertical-align: middle;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-details-name {
+ .b_1;
+ .bold;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ //width: 100%;
+ text-transform: capitalize;
+ max-width: 240px;
+ display: inline-block;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-details-ownerName {
+ .b_13;
+ font-weight:400;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+
+ &:before{
+ .sprite;
+ .arrow-left;
+ content: '';
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-details-desc {
+ .p_1;
+ line-height: 14px;
+ word-wrap: break-word;
+ white-space: normal;
+
+ .i-sdc-designer-sidebar-section-content-item-relations-details-indent-box{
+ border-left: 1px #848586 solid;
+ height: 40px;
+ margin-left: 2px;
+ margin-top: 5px;
+ border-bottom: 1px #848586 solid;
+ width: 25px;
+ float: left;
+ }
+ .i-sdc-designer-sidebar-section-content-item-relations-details-child{
+ margin-top: 30px;
+ float: left;
+ padding-left: 10px;
+ max-width: 230px;
+ }
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-details-desc-label {
+ font-family: omnes-medium, sans-serif;
+ }
+
+ .i-sdc-designer-sidebar-section-content-item-relations-view {
+ position: absolute;
+ right: 0;
+ top: 22px;
+ display: none;
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.html
new file mode 100644
index 0000000000..2070041990
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.html
@@ -0,0 +1,13 @@
+<perfect-scrollbar include-padding="true" class="w-sdc-designer-sidebar-tab-content">
+
+ <div class="w-sdc-designer-sidebar-section">
+ <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content" class="w-sdc-designer-sidebar-section-title">
+ Composition
+ <div class="w-sdc-designer-sidebar-section-title-icon"></div>
+ </expand-collapse>
+
+ <div class="w-sdc-designer-sidebar-section-content" ng-show="selectedComponent.isComplex()">
+ <structure-tree component="selectedComponent"></structure-tree>
+ </div>
+ </div>
+</perfect-scrollbar>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts
new file mode 100644
index 0000000000..daeab7f2f3
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/composition/tabs/structure/structure-view.ts
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IStructureViewModel extends ICompositionViewModelScope {}
+
+ export class StructureViewModel {
+ static '$inject' = [
+ '$scope'
+ ];
+
+ constructor(private $scope:IStructureViewModel) {
+ }
+ }
+ }
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts
new file mode 100644
index 0000000000..43511e2deb
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts
@@ -0,0 +1,253 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ interface IDeploymentArtifactsViewModelScope extends IWorkspaceViewModelScope {
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+ artifacts: Array<Models.ArtifactModel>;
+ editForm:ng.IFormController;
+ isLoading:boolean;
+ artifactDescriptions:any;
+ updateInProgress:boolean;
+
+ addOrUpdate(artifact:Models.ArtifactModel): void;
+ update(artifact:Models.ArtifactModel): void;
+ delete(artifact:Models.ArtifactModel): void;
+ sort(sortBy:string): void;
+ noArtifactsToShow():boolean;
+ getValidationPattern(validationType:string, parameterType?:string):RegExp;
+ validateJson(json:string):boolean;
+ resetValue(parameter:any):void;
+ viewModeOrCsarComponent():boolean;
+ isLicenseArtifact(artifact:Models.ArtifactModel): boolean;
+
+ }
+
+ export class DeploymentArtifactsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ValidationUtils',
+ 'ArtifactsUtils',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:IDeploymentArtifactsViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private validationUtils:Sdc.Utils.ValidationUtils,
+ private artifactsUtils:Sdc.Utils.ArtifactsUtils,
+ private ModalsHandler:Utils.ModalsHandler) {
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+ private initDescriptions = ():void => {
+ this.$scope.artifactDescriptions = {};
+ _.forEach(this.$scope.component.deploymentArtifacts, (artifact:Models.ArtifactModel):void => {
+ this.$scope.artifactDescriptions[artifact.artifactLabel] = artifact.description;
+ });
+ };
+
+
+ private setArtifact = (artifact:Models.ArtifactModel):void => {
+ if (artifact.heatParameters) {
+ artifact.heatParameters.forEach((parameter:any):void => {
+ if (!parameter.currentValue && parameter.defaultValue) {
+ parameter.currentValue = parameter.defaultValue;
+ } else if ("" === parameter.currentValue) {
+ parameter.currentValue = null;
+ }
+ });
+ }
+ if (!artifact.description || !this.$scope.getValidationPattern('string').test(artifact.description)) {
+ artifact.description = this.$scope.artifactDescriptions[artifact.artifactLabel];
+ }
+ };
+
+ private updateAll = ():void => {
+ let artifacts:Array<Models.ArtifactModel> = [];
+ _.forEach(this.$scope.component.deploymentArtifacts, (artifact:Models.ArtifactModel):void => {
+ if (artifact.selected) {
+ this.setArtifact(artifact);
+ artifacts.push(artifact);
+ }
+ });
+ this.$scope.component.updateMultipleArtifacts(artifacts);
+ };
+
+
+ private initScope = ():void => {
+ let self = this;
+ this.$scope.isLoading = false;
+ this.$scope.updateInProgress = false;
+ this.initDescriptions();
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+ this.$scope.setValidState(true);
+
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'artifactDisplayName'},
+ {title: 'Type', property: 'artifactType'},
+ {title: 'Deployment timeout', property: 'timeout'}
+ ];
+
+ this.$scope.isLicenseArtifact = (artifact:Models.ArtifactModel) :boolean => {
+ let isLicense:boolean = false;
+ if(this.$scope.component.isResource() && (<Resource>this.$scope.component).isCsarComponent()) {
+
+ isLicense = this.artifactsUtils.isLicenseType(artifact.artifactType);
+ }
+
+ return isLicense;
+ };
+
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+ this.$scope.getValidationPattern = (validationType:string, parameterType?:string):RegExp => {
+ return this.validationUtils.getValidationPattern(validationType, parameterType);
+ };
+
+ this.$scope.validateJson = (json:string):boolean => {
+ if(!json){
+ return true;
+ }
+ return this.validationUtils.validateJson(json);
+ };
+
+ this.$scope.viewModeOrCsarComponent = ():boolean => {
+ return this.$scope.isViewMode() || (this.$scope.component.isResource() && (<Resource>this.$scope.component).isCsarComponent());
+ };
+
+
+ this.$scope.addOrUpdate = (artifact:Models.ArtifactModel):void => {
+ artifact.artifactGroupType = 'DEPLOYMENT';
+ let artifactCopy = new Models.ArtifactModel(artifact);
+
+ let success = (response:any):void => {
+ self.$scope.artifactDescriptions[artifactCopy.artifactLabel] = artifactCopy.description;
+ self.$scope.artifacts = <ArtifactModel[]>_.values(self.$scope.component.deploymentArtifacts);
+ };
+
+ let error = (err:any):void =>{
+ console.log(err);
+ self.$scope.artifacts = <ArtifactModel[]>_.values(self.$scope.component.deploymentArtifacts);
+ };
+
+
+ this.ModalsHandler.openWizardArtifactModal(artifactCopy, self.$scope.component).then(success, error);
+ };
+
+ this.$scope.noArtifactsToShow = ():boolean => {
+ return !_.some(this.$scope.artifacts, 'esId');
+ };
+
+
+ this.$scope.resetValue = (parameter:any):void => {
+ if (!parameter.currentValue && parameter.defaultValue) {
+ parameter.currentValue = parameter.defaultValue;
+ }
+ else if ('boolean' == parameter.type) {
+ parameter.currentValue = parameter.currentValue.toUpperCase();
+ }
+ };
+
+
+ this.$scope.$watch('editForm.$valid', ():void => {
+ if (this.$scope.editForm) {
+ // this.$scope.setValidState(this.$scope.editForm.$valid);
+ }
+ });
+
+ this.$scope.update = (artifact:Models.ArtifactModel):void => {
+ if (false == this.$scope.isLoading) {
+ if (artifact.selected && !this.$scope.isViewMode()) {
+ this.$scope.isLoading = true;
+ this.$scope.updateInProgress = true;
+ let onSuccess = (responseArtifact:Models.ArtifactModel):void => {
+ this.$scope.artifactDescriptions[responseArtifact.artifactLabel] = responseArtifact.description;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+ this.$scope.isLoading = false;
+ artifact.selected = !artifact.selected;
+ this.$scope.updateInProgress = false;
+ };
+
+ let onFailed = (error:any):void => {
+ console.log('Delete artifact returned error:', error);
+ this.$scope.isLoading = false;
+ artifact.selected = !artifact.selected;
+ this.$scope.updateInProgress = false;
+ };
+
+ this.setArtifact(artifact);
+ this.$scope.component.addOrUpdateArtifact(artifact).then(onSuccess, onFailed);
+ } else {
+ artifact.selected = !artifact.selected;
+
+ }
+ }
+ };
+
+ this.$scope.delete = (artifact:Models.ArtifactModel):void => {
+ let onOk = ():void => {
+ this.$scope.isLoading = true;
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.deploymentArtifacts);
+ };
+
+ let onFailed = (error:any):void => {
+ this.$scope.isLoading = false;
+ console.log('Delete artifact returned error:', error);
+ };
+
+ this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed);
+ };
+
+ let title:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE");
+ let message:string = self.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ };
+
+ public save = (callback:Function):void => {
+ this.updateAll();
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ };
+
+ public back = (callback:Function):void => {
+ this.$scope.setComponent(this.$scope.component);
+ callback(true);
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html
new file mode 100644
index 0000000000..1547618134
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view.html
@@ -0,0 +1,149 @@
+<div class="workspace-deployment-artifact">
+
+ <div data-tests-id="add-deployment-artifact-button" ng-if="!isViewMode()" data-ng-class="{'disabled': isDisableMode()}" data-tests-id="add-property-button" class="add-btn" data-ng-click="addOrUpdate({})">Add</div>
+
+ <div class="table-container-flex">
+
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <loader data-display="isLoading"></loader>
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+
+ <form class="body" name="editForm">
+
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+
+ <!-- Artifact row -->
+ <div ng-if="noArtifactsToShow()" data-ng-class="{'disabled': isDisableMode()}" class="no-row-text" translate="DEPLOYMENT_ARTIFACT_NO_ARTIFACTS_TO_DISPLAY"></div>
+ <div data-ng-repeat-start="artifact in artifacts | orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected || undefined==artifact.selected && updateInProgress}"
+ data-ng-if="artifact.esId">
+
+ <div class="table-col-general flex-item" data-ng-click="update(artifact)">
+
+ <span class="sprite table-arrow" data-tests-id="{{artifact.artifactDisplayName}}" data-ng-class="{'opened': artifact.selected || undefined==artifact.selected && updateInProgress}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+ <div class="table-col-general flex-item" data-tests-id="{{artifact.timeout}}">
+ {{artifact.timeout? artifact.timeout:''}}
+ </div>
+
+ <div class="table-btn-col flex-item">
+ <button class="table-edit-btn" data-tests-id="edit_{{artifact.artifactDisplayName}}"
+ data-ng-if="!isViewMode() && !artifact.isHEAT() && !artifact.isThirdParty() && !isLicenseArtifact(artifact)" data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="table-delete-btn" data-tests-id="delete_{{artifact.artifactDisplayName}}"
+ data-ng-if="!isViewMode() && !artifact.isHEAT() && !artifact.isThirdParty() && !isLicenseArtifact(artifact)" data-ng-click="delete(artifact)"> </button>
+ <button class="table-download-btn" download-artifact data-tests-id="download_{{artifact.artifactDisplayName}}"
+ data-ng-if="artifact.artifactName" component="component" artifact="artifact"></button>
+
+
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected || undefined==artifact.selected && updateInProgress" class="w-sdc-form item-opened">
+ <!-- Artifact panel opened -->
+
+ <!-- Description field -->
+ <div class="w-sdc-form-item" ng-form="descriptionForm" data-ng-class="{error:(descriptionForm.$dirty && descriptionForm.$invalid)}">
+ <label class="i-sdc-env-form-label required">Description</label>
+ <textarea class="i-sdc-form-textarea {{$index}}" data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-maxlength="256"
+ maxlength="256"
+ data-ng-required="true"
+ name="description"
+ data-ng-model="artifact.description"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-pattern="getValidationPattern('string')"
+ ng-readonly="isViewMode()"
+ data-tests-id="description">
+ </textarea>
+
+ <div class="input-error" data-ng-show="descriptionForm.$dirty && descriptionForm.$invalid">
+ <span ng-show="descriptionForm.$error.required" translate="ADD_ARTIFACT_ERROR_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="descriptionForm.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span>
+ <span ng-show="descriptionForm.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!-- Parameters in 2 columns -->
+ <div class="w-sdc-form-columns-wrapper" data-ng-if="artifact.heatParameters">
+ <!-- Left column -->
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item" ng-form="parameterForm"
+ data-ng-repeat="parameter in artifact.heatParameters.slice(0, artifact.heatParameters.length%2+artifact.heatParameters.length/2) track by $index">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}" tooltip-side="top" sdc-smart-tooltip>{{parameter.name +' (' + parameter.type + ')'}}</label>
+ <span class="parameter-description" tooltips tooltip-side="top" tooltip-content="{{parameter.description}}">?</span>
+ <input class="i-sdc-form-input" data-ng-class="{error:(parameterForm.currentValue.$invalid),'view-mode': isViewMode() }"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && parameterForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="!parameterForm.currentValue.$error.pattern && resetValue(parameter)"
+ />
+
+ <div class="input-error" data-ng-show="parameterForm.currentValue.$invalid">
+ <span ng-show="parameterForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+ </div>
+
+ <!-- Right column -->
+ <div class="w-sdc-form-column">
+ <div class="i-sdc-form-item" ng-form="parameterForm" data-ng-repeat="parameter in artifact.heatParameters.slice(artifact.heatParameters.length%2+artifact.heatParameters.length/2) track by $index">
+ <label class="i-sdc-env-form-label" data-ng-class="{required:parameter.defaultValue}" tooltip-side="top" sdc-smart-tooltip>{{parameter.name +' (' + parameter.type + ')'}}</label>
+ <span class="parameter-description" tooltips tooltip-side="top" tooltip-content="{{parameter.description}}">?</span>
+ <input class="i-sdc-form-input" data-ng-class="{error:(parameterForm.currentValue.$invalid), 'view-mode': isViewMode()}"
+ data-ng-model-options="{ debounce: 200 }"
+ data-ng-model="parameter.currentValue"
+ type="text"
+ name="currentValue"
+ data-ng-pattern="getValidationPattern(parameter.type, 'heat')"
+ data-ng-required="parameter.defaultValue"
+ data-ng-change="'json'==parameter.type && parameterForm.currentValue.$setValidity('pattern', validateJson(parameter.currentValue))"
+ data-ng-blur="!parameterForm.currentValue.$error.pattern && resetValue(parameter)"
+ />
+
+ <div class="input-error" data-ng-show="parameterForm.currentValue.$invalid">
+ <span ng-show="parameterForm.currentValue.$error.required" translate="VALIDATION_ERROR_REQUIRED" translate-values="{'field': 'Value'}"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && parameter.type==='string'" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="parameterForm.currentValue.$error.pattern && !(parameter.type==='string')" translate="VALIDATION_ERROR_TYPE" translate-values="{'type': '{{parameter.type}}'}"></span>
+ </div>
+ </div>
+ </div>
+
+
+ </div><!-- Close: Parameters in 2 columns -->
+ </div><!-- Close: Artifact panel opened -->
+
+ <!-- Add artifacts buttons -->
+ <button class="add-button" data-ng-repeat="artifact in artifacts track by $index"
+ type="button"
+ data-ng-show="!artifact.esId"
+ data-ng-if="!viewModeOrCsarComponent()"
+ data-ng-class="{'disabled': isDisableMode() || component.isCsarComponent()}"
+ data-tests-id="{{artifact.artifactDisplayName}} deployment_artifact"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT"
+ translate-values="{'name': '{{artifact.artifactDisplayName}}'}"
+ data-ng-click="addOrUpdate(artifact)"></button>
+
+ <!-- Top add button -->
+ <button class="add-button" type="button" data-ng-if="!isViewMode()" data-ng-class="{'disabled': isDisableMode()}" translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER" data-ng-click="addOrUpdate({})"></button>
+ </perfect-scrollbar>
+ </form>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less
new file mode 100644
index 0000000000..9f90a47d5a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less
@@ -0,0 +1,102 @@
+.workspace-deployment-artifact {
+ width: 93%;
+ display: inline-block;
+ .table-container-flex .table .body .data-row + div.item-opened {
+ align-items: center;
+ padding: 10px 40px 10px 30px;
+ }
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table {
+ height:490px;
+ margin-bottom: 0;
+ }
+
+ .parameter-description {
+ .circle(18px, @color_p);
+ content: '?';
+ line-height: 18px;
+ vertical-align: middle;
+ margin-left: 5px;
+ cursor: default;
+ display: inline-block;
+ position: absolute;
+ top: 16px;
+ }
+
+ .table-container-flex {
+
+ margin-top: 27px;
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 9;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 3;
+ padding-top: 10px;
+ }
+ }
+ .w-sdc-form{
+ text-align: left;
+
+ .w-sdc-env-params{
+ border-top: 1px solid #cdcdcd;
+ margin: 25px 0 10px 0;
+ }
+
+ .i-sdc-form-textarea {
+ border: 1px solid @color_e;
+ min-height: 60px;
+ padding: 10px 13px;
+ width: 100%;
+ resize: none;
+
+ }
+
+ .w-sdc-form-item {
+ &.error {
+ .i-sdc-form-input,
+ .i-sdc-form-select,
+ .i-sdc-form-textarea {
+ border-color: @color_h;
+ outline: none;
+ box-sizing: border-box;
+ }
+ }
+ }
+
+ .i-sdc-env-form-label{
+ font-family: @font-omnes-medium;
+ color: @main_color_m;
+ overflow: hidden;
+ max-width: 450px;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ margin-top: 14px;
+
+ &.required::before {
+ color: #f33;
+ content: '*';
+ margin-right: 4px;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view-model.ts
new file mode 100644
index 0000000000..f8afc0b758
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view-model.ts
@@ -0,0 +1,127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IDeploymentViewModelScope extends IWorkspaceViewModelScope {
+
+ currentComponent: Models.Components.Component;
+ selectedComponent: Models.Components.Component;
+ isLoading: boolean;
+ sharingService:Sdc.Services.SharingService;
+ sdcMenu:Models.IAppMenu;
+ version:string;
+ isViewOnly:boolean;
+ tabs:Array<Models.Tab>;
+
+ setComponent(component: Models.Components.Component);
+ isComponentInstanceSelected():boolean;
+ updateSelectedComponent(): void
+ openUpdateModal();
+ deleteSelectedComponentInstance():void;
+ onBackgroundClick():void;
+ setSelectedInstance(componentInstance: Models.ComponentsInstances.ComponentInstance): void;
+ printScreen():void;
+
+ }
+
+ export class DeploymentViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'sdcMenu',
+ 'MenuHandler',
+ '$modal',
+ '$templateCache',
+ '$state',
+ 'Sdc.Services.SharingService',
+ '$filter',
+ 'Sdc.Services.CacheService',
+ 'ComponentFactory',
+ 'ChangeLifecycleStateHandler',
+ 'LeftPaletteLoaderService',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:IDeploymentViewModelScope,
+ private sdcMenu:Models.IAppMenu,
+ private MenuHandler: Utils.MenuHandler,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private $state:ng.ui.IStateService,
+ private sharingService:Services.SharingService,
+ private $filter:ng.IFilterService,
+ private cacheService:Services.CacheService,
+ private ComponentFactory: Utils.ComponentFactory,
+ private ChangeLifecycleStateHandler: Sdc.Utils.ChangeLifecycleStateHandler,
+ private LeftPaletteLoaderService: Services.Components.LeftPaletteLoaderService,
+ private ModalsHandler: Sdc.Utils.ModalsHandler) {
+
+ this.$scope.setValidState(true);
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+
+ private initComponent = ():void => {
+
+ this.$scope.currentComponent = this.$scope.component;
+ this.$scope.selectedComponent = this.$scope.currentComponent;
+ this.updateUuidMap();
+ this.$scope.isViewOnly = this.$scope.isViewMode();
+ };
+
+
+ private updateUuidMap = ():void => {
+ /**
+ * In case user press F5, the page is refreshed and this.sharingService.currentEntity will be undefined,
+ * but after loadService or loadResource this.sharingService.currentEntity will be defined.
+ * Need to update the uuidMap with the new resource or service.
+ */
+ this.sharingService.addUuidValue(this.$scope.currentComponent.uniqueId,this.$scope.currentComponent.uuid);
+ };
+
+ private initRightTabs = ()=> {
+ if(this.$scope.currentComponent.groups){
+
+ let hierarchyTab = new Models.Tab('/app/scripts/view-models/tabs/hierarchy/hierarchy-view.html', 'Sdc.ViewModels.HierarchyViewModel', 'hierarchy', this.$scope.currentComponent, 'hierarchy');
+ this.$scope.tabs = Array<Models.Tab>();
+ this.$scope.tabs.push(hierarchyTab)
+ }
+
+ }
+ private initScope = ():void => {
+
+ this.$scope.sharingService = this.sharingService;
+ this.$scope.sdcMenu = this.sdcMenu;
+ this.$scope.isLoading = false;
+
+ this.$scope.version = this.cacheService.get('version');
+ this.initComponent();
+
+ this.$scope.setComponent = (component: Models.Components.Product):void => {
+ this.$scope.currentComponent = component;
+ }
+
+ this.initRightTabs();
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view.html
new file mode 100644
index 0000000000..9e26656f5f
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment-view.html
@@ -0,0 +1,10 @@
+<div class="deployment-view">
+ <div class="w-sdc-deployment-canvas" data-ng-class="{sidebaractive: displayDesignerRightSidebar}">
+ <!--<module-graph data-tests-id="canvas" current-component="currentComponent" is-view-only="isViewOnly" is-loading="isLoading"></module-graph>-->
+ <deployment-graph component="currentComponent" is-view-only="isViewOnly"></deployment-graph>
+ </div>
+
+ <div class="w-sdc-deployment-right-bar">
+ <sdc-tabs tabs="tabs" is-view-only="isViewOnly"></sdc-tabs>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment.less b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment.less
new file mode 100644
index 0000000000..0439ccd0fa
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/deployment/deployment.less
@@ -0,0 +1,33 @@
+.deployment-view{
+
+ display: inline-block;
+ text-align: left;
+ align-items: left;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+
+ .w-sdc-deployment-canvas {
+ .noselect;
+ .bg_c;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+
+ .view-mode{
+ background-color: #f8f8f8;
+ border:0;
+ }
+ }
+
+ .w-sdc-deployment-right-bar {
+
+ .noselect;
+ bottom: 0;
+ position: absolute;
+ right: 0px;
+ transition: right 0.2s;
+ z-index: 10000;
+ top: @action_nav_height;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts
new file mode 100644
index 0000000000..c0d6aba915
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IDistributionStatusModalViewModelScope {
+ distribution:Models.Distribution;
+ status:string;
+ getStatusCount(distributionComponent:Array<Models.DistributionComponent>):any;
+ getUrlName(url:string):string;
+ modalDitributionStatus:ng.ui.bootstrap.IModalServiceInstance;
+ footerButtons: Array<any>;
+ close(): void;
+ }
+
+ export class DistributionStatusModalViewModel {
+
+ static '$inject' = ['$scope','$modalInstance', 'data'];
+
+ constructor(private $scope:IDistributionStatusModalViewModelScope,
+ private $modalInstance:ng.ui.bootstrap.IModalServiceInstance,
+ private data:any
+ ) {
+ this.initScope();
+ }
+
+ private initScope = ():void => {
+ this.$scope.distribution = this.data.distribution;
+ this.$scope.status = this.data.status;
+ this.$scope.modalDitributionStatus = this.$modalInstance;
+
+ this.$scope.getUrlName = (url:string):string =>{
+ let urlName:string = _.last(url.split('/'));
+ return urlName;
+ };
+
+ this.$scope.close = ():void => {
+ this.$modalInstance.close();
+ };
+
+ this.$scope.footerButtons = [
+ {'name': 'Close', 'css': 'blue', 'callback': this.$scope.close }
+ ];
+
+ };
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html
new file mode 100644
index 0000000000..b3393e99e0
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view.html
@@ -0,0 +1,126 @@
+<sdc-modal modal="modalDitributionStatus" type="classic" class="w-sdc-classic-top-line-modal" buttons="footerButtons" header="Distribution by Status" show-close-button="true">
+
+ <div class="w-sdc-distribution-view">
+ <div class="w-sdc-distribution-view-header">
+
+ </div>
+
+
+ <perfect-scrollbar include-padding="true" class="w-sdc-distribution-view-content">
+ <div class="w-sdc-distribution-view-content-section w-sdc-distribute-parent-block">
+ <ul>
+ <li class="w-sdc-distribute-parent-block" >
+ <div class="w-sdc-distribute-row w-sdc-distribute-row-extends extends">
+
+ <div class="w-sdc-distribute-row-content">
+ <div class="w-sdc-distribute-content">
+ <div class="title-section item-1">
+ <div class="title">Distribution ID</div>
+ <div data-ng-bind="distribution.distributionID"></div>
+ </div>
+ <div class="title-section item-2">
+ <div class="title" translate="DISTRIBUTION_VIEW_TITLE_USER_ID"></div>
+ <div data-ng-bind="distribution.userId"></div>
+ </div>
+ <div class="title-section item-3">
+ <div class="title">Time[UTC]:</div>
+ <div
+ data-ng-bind="distribution.timestamp | stringToDateFilter | date: 'MM/dd/yyyy h:mma':'UTC'"></div>
+ </div>
+ <div class="title-section item-4">
+ <span class="sprite-new status-icon" data-ng-class="distribution.deployementStatus"></span>
+ <span class="sprite-new" data-ng-bind="distribution.deployementStatus"></span>
+ </div>
+ </div>
+ <div class="w-sdc-distribute-status-block" data-ng-show="distribution.statusCount">
+ <div class="status-item-1">Status: {{status}} <span data-ng-bind="(distribution.distributionComponents | filter:status:true).length"
+ class="blue-font"></span></div>
+
+ </div>
+ </div>
+ </div>
+
+ <ul class="w-sdc-distribute-components-block disable-hover">
+ <li data-ng-repeat="(omfComponentID,omfComponentList) in distribution.distributionComponents | orderBy: '-timestamp' | filter:status:true | groupBy:'omfComponentID'"
+ class="disable-hover">
+ <div class="w-sdc-distribute-row omf-component-row w-sdc-distribute-row-extends "
+ data-ng-class="{'extends': omfComponentListExtends}">
+ <div class="w-sdc-distribution-arrow-btn" data-ng-click="omfComponentListExtends=!omfComponentListExtends"
+ ng-class="{'extends': omfComponentListExtends}"
+ data-ng-init="omfComponentListExtends=false"
+ ></div>
+ <div class="w-sdc-distribute-status-block">
+ <div class="status-item-1">{{omfComponentID}} <span class="blue-font">{{omfComponentList.length}}</span>
+ </div>
+ </div>
+ </div>
+ <div data-ng-show="omfComponentListExtends"
+ class="w-sdc-distribute-omfComponent-block disable-hover">
+ <div class="w-sdc-distribute-row-extends disable-hover">
+ <div class="disable-hover">
+ <div class="w-sdc-distribute-row omfComponent-table-head">
+ <div class="title item-1">Component ID</div>
+ <div class="title item-2">Artifact Name</div>
+ <div class="title item-3">URL</div>
+ <div class="title item-4">Time(UTC)</div>
+ <div class="title item-5">Status</div>
+ </div>
+
+ <div class="w-sdc-distribute-row omfComponent-table-row"
+ data-ng-repeat-start="(url,urlList) in omfComponentList | orderBy: '-timestamp' | groupBy:'url'"
+ data-ng-class="urlListExtends?'extends row-{{$index}}':'row-{{$index}}'" >
+ <div class="w-sdc-distribute-cell item-1">
+ <div class="w-sdc-distribution-arrow-btn" data-ng-click="urlListExtends=!urlListExtends"
+ data-ng-class="{'extends': urlListExtends}"
+ data-ng-init="urlListListExtends=false"
+ ></div>
+ {{urlList[0].omfComponentID}}
+ </div>
+ <div class="w-sdc-distribute-cell item-2" sdc-smart-tooltip>
+ {{getUrlName(urlList[0].url)}}
+ </div>
+ <div class="w-sdc-distribute-cell item-3 disable-hover">
+ <div sdc-smart-tooltip class="distribution-url">{{urlList[0].url}}</div>
+ <div sdc-smart-tooltip title="Copy url" clipboard text="urlList[0].url"
+ class="sprite-new link-btn copy-link disable-hover"></div>
+ </div>
+ <div class="w-sdc-distribute-cell item-4"><span
+ data-ng-bind="urlList[0].timestamp | date: 'MM/dd/yyyy h:mma':'UTC'"></span>
+ </div>
+ <div class="w-sdc-distribute-cell item-5">{{urlList[0].status}}</div>
+ </div>
+
+
+ <div data-ng-repeat-end data-ng-show="urlListExtends" class="disable-hover">
+ <div class="w-sdc-distribute-row extends disable-hover">
+ <ul data-ng-show="urlListExtends"
+ class="w-sdc-distribute-url-block disable-hover">
+ <li data-ng-repeat="distributionComponent in urlList | orderBy: '-timestamp'"
+ class="disable-hover">
+ <span
+ data-ng-bind="distributionComponent.timestamp | date: 'MM/dd/yyyy h:mma':'UTC'"
+ class="disable-hover"></span>
+ <span
+ class="disable-hover">{{distributionComponent.status}}</span>
+ <span
+ class="disable-hover reason" data-ng-show="distributionComponent.status == 'NOT_NOTIFIED'">Reason: Component has determined artifact is not needed.</span>
+ <span
+ class="disable-hover reason" data-ng-show="distributionComponent.errorReason">Reason: {{distributionComponent.errorReason}}</span>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+
+ </perfect-scrollbar>
+ </div>
+
+
+</sdc-modal>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less
new file mode 100644
index 0000000000..02321b6e2f
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less
@@ -0,0 +1,33 @@
+.w-sdc-classic-top-line-modal {
+
+ .w-sdc-modal-head {
+ // border-bottom: none;
+ }
+ .w-sdc-distribution-view {
+
+ .w-sdc-distribution-view-content {
+ height: 500px;
+ }
+
+ .w-sdc-distribution-view-content-section {
+
+ .w-sdc-distribute-parent-block {
+ .w-sdc-distribute-components-block {
+
+ .omf-component-row {
+ .w-sdc-distribute-status-block {
+ margin-left: 0;
+ }
+
+ }
+ div {
+ padding-left: 0;
+ }
+ }
+
+ }
+
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view-model.ts
new file mode 100644
index 0000000000..219585fc3d
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view-model.ts
@@ -0,0 +1,135 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IDistributionViewModel extends IWorkspaceViewModelScope{
+ modalDistribution:ng.ui.bootstrap.IModalServiceInstance;
+ service: Models.Components.Service;
+ distributions : Array<Models.Distribution>;
+ showComponents(distribution:Models.Distribution): void;
+ markAsDeployed(distribution:Models.Distribution): void;
+ getStatusCount(distributionComponent:Array<Models.DistributionComponent>):any;
+ initDistributions():void;
+ getUrlName(url:string):string;
+ close(): void;
+ openDisributionStatusModal:Function;
+ }
+
+ export class DistributionViewModel{
+
+ static '$inject' = [
+ '$scope',
+ 'ModalsHandler'
+
+ ];
+
+ constructor(
+ private $scope:IDistributionViewModel,
+ private ModalsHandler: Sdc.Utils.ModalsHandler
+ ){
+ this.initScope();
+ this.$scope.setValidState(true);
+ this.$scope.updateSelectedMenuItem();
+ }
+
+ private initScope = (): void => {
+ this.$scope.service = <Models.Components.Service>this.$scope.component;
+
+
+ // Open Distribution status modal
+ this.$scope.openDisributionStatusModal = (distribution: Models.Distribution,status:string):void => {
+ this.ModalsHandler.openDistributionStatusModal(distribution,status).then(()=>{
+ // OK
+ }, ()=>{
+ // ERROR
+ });
+ };
+
+
+ this.$scope.showComponents = (distribution: Models.Distribution): void => {
+ let onError = (response) => {
+ console.info('onError showComponents',response);
+ };
+ let onSuccess = (distributionComponents: Array<Models.DistributionComponent>) => {
+ distribution.distributionComponents = distributionComponents;
+ distribution.statusCount = this.$scope.getStatusCount(distribution.distributionComponents);
+ // distribution.components = this.aggregateDistributionComponent(distributionComponents);;
+ };
+ this.$scope.service.getDistributionsComponent(distribution.distributionID).then(onSuccess, onError);
+ };
+
+ this.$scope.getStatusCount = (distributionComponent:Array<Models.DistributionComponent>):any => {
+ return _.countBy(distributionComponent, 'status')
+ };
+
+ this.$scope.getUrlName = (url:string):string =>{
+ let urlName:string = _.last(url.split('/'));
+ return urlName;
+ };
+
+ this.$scope.markAsDeployed = (distribution: Models.Distribution): void => {
+ let onError = (response) => {
+ console.info('onError markAsDeployed',response);
+ };
+ let onSuccess = (result: any) => {
+ distribution.deployementStatus = 'Deployed';
+ };
+ this.$scope.service.markAsDeployed(distribution.distributionID).then(onSuccess, onError);
+
+ };
+
+ this.$scope.initDistributions = (): void => {
+ let onError = (response) => {
+ console.info('onError initDistributions',response);
+ };
+ let onSuccess = (distributions: Array<Models.Distribution>) => {
+ this.$scope.distributions = distributions;
+ };
+ this.$scope.service.getDistributionsList().then(onSuccess, onError);
+ };
+
+ this.$scope.initDistributions();
+
+ };
+
+
+ private aggregateDistributionComponent = (distributionComponents:Array<Models.DistributionComponent>):any =>{
+ let aggregateDistributions:Utils.Dictionary<string,Utils.Dictionary<string,Array<Models.DistributionComponent>>> = new Utils.Dictionary<string,Utils.Dictionary<string,Array<Models.DistributionComponent>>>();
+ let tempAggregateDistributions:any= _.groupBy(distributionComponents,'omfComponentID');
+ let aa = new Utils.Dictionary<string,Array<Models.DistributionComponent>>();
+
+ let tempAggregate:any;
+ _.forEach(tempAggregateDistributions,(distributionComponents:Array<Models.DistributionComponent>,omfComponentID:string)=>{
+
+ let urls:any = _.groupBy(distributionComponents,'url');
+ aggregateDistributions.setValue(omfComponentID,urls);
+ // aggregateDistributions[omfComponentID] = ;
+
+ });
+ console.log(aggregateDistributions);
+ return aggregateDistributions;
+ };
+
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view.html
new file mode 100644
index 0000000000..1ab0f1e111
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution-view.html
@@ -0,0 +1,171 @@
+<div class="w-sdc-distribution-view">
+ <div class="w-sdc-distribution-view-header">
+ <div class="w-sdc-distribution-view-title">DISTRIBUTION <span data-ng-bind="'[' + distributions.length +']'"
+ class="blue-font"></span></div>
+ <div class="header-spacer"></div>
+ <div class="top-search">
+ <input type="text"
+ class="search-text"
+ data-tests-id="searchTextbox"
+ placeholder="Search"
+ data-ng-model="searchBind"
+ data-tests-id="main-menu-input-search"
+ ng-model-options="{ debounce: 500 }"/>
+ <span class="w-sdc-search-icon magnification"></span>
+ </div>
+ <div class="sprite-new refresh-btn" data-tests-id="refreshButton" data-ng-click="initDistributions()" sdc-smart-tooltip=""
+ title="Refresh"></div>
+ </div>
+
+
+ <perfect-scrollbar include-padding="true" class="w-sdc-distribution-view-content">
+ <div class="w-sdc-distribution-view-content-section" data-tests-id="ditributionTable">
+ <ul>
+ <li data-ng-repeat="item in distributions | orderBy: '-timestamp' | filter:searchBind"
+ data-ng-init="item.dateFormat = ( item.timestamp | stringToDateFilter | date: 'MM/dd/yyyy h:mma':'UTC' )"
+ class="w-sdc-distribute-parent-block" data-tests-id="record_{{$index}}" data-ng-class="{'extends': item.showDetails}">
+ <div class="w-sdc-distribute-row w-sdc-distribute-row-extends"
+ data-ng-class="{'extends': item.showDetails && item.distributionComponents.length}">
+ <div class="w-sdc-distribution-arrow-btn" data-tests-id="ShowRecordButton_{{$index}}" data-ng-click="showComponents(item); item.showDetails=!item.showDetails"
+ data-ng-class="{'extends': item.showDetails}"
+ ></div>
+ <div class="w-sdc-distribute-row-content">
+ <div class="w-sdc-distribute-content">
+ <div class="title-section item-1">
+ <div class="title">Distribution ID</div>
+ <div data-ng-bind="item.distributionID"></div>
+ </div>
+ <div class="title-section item-2">
+ <div class="title" translate="DISTRIBUTION_VIEW_TITLE_USER_ID"></div>
+ <div data-ng-bind="item.userId"></div>
+ </div>
+ <div class="title-section item-3">
+ <div class="title">Time[UTC]:</div>
+ <div
+ data-ng-bind="item.dateFormat"></div>
+ </div>
+ <div class="title-section item-4">
+ <span class="sprite-new status-icon" data-ng-class="item.deployementStatus"></span>
+ <span class="sprite-new" data-ng-bind="item.deployementStatus"></span>
+ </div>
+ <div>
+ <div class="sprite-new distribution-bth item-5"
+ data-ng-class="{'disable':item.deployementStatus==='Deployed'}"
+ data-ng-click="(item.deployementStatus==='Deployed') || markAsDeployed(item)"></div>
+ </div>
+ </div>
+ <div class="w-sdc-distribute-status-block" data-ng-show="item.statusCount">
+ <div class="status-item-1">Total Artifacts:<span data-ng-bind="(item.statusCount.NOT_NOTIFIED || 0) + (item.statusCount.NOTIFIED || 0) "
+ class="blue-font" data-tests-id="totalArtifacts_{{$index}}"></span></div>
+ <div class="status-item-2 " ><sapn class="link" data-ng-click="openDisributionStatusModal(item,'NOTIFIED')">Notified:</sapn><span
+ data-ng-bind="item.statusCount.NOTIFIED || 0" class="blue-font" data-tests-id="notified_{{$index}}"></span></div>
+ <div class="status-item-3 link" ><sapn class="link" data-ng-click="openDisributionStatusModal(item,'DOWNLOAD_OK')">Downloaded:</sapn><span
+ data-ng-bind="item.statusCount.DOWNLOAD_OK || 0" class="blue-font" data-tests-id="downloaded_{{$index}}"></span></div>
+ <div class="status-item-4 link"><sapn class="link" data-ng-click="openDisributionStatusModal(item,'DEPLOY_OK')">Deployed:</sapn><span
+ data-ng-bind="item.statusCount.DEPLOY_OK || 0" class="blue-font" data-tests-id="deployed_{{$index}}" ></span><span
+ data-ng-class="{'deployed':(item.statusCount.DEPLOY_OK > 0)}"></span></div>
+ <div class="status-item-5 link" ><sapn class="link" data-ng-click="openDisributionStatusModal(item,'NOT_NOTIFIED')">Not Notified:</sapn><span
+ data-ng-bind="item.statusCount.NOT_NOTIFIED || 0" class="blue-font" data-tests-id="NotNotified_{{$index}}"></span></div>
+ <div class="status-item-6"><sapn class="link" data-ng-click="openDisributionStatusModal(item,'DOWNLOAD_ERROR')" >Errors:</sapn><span
+ data-ng-bind="item.statusCount.DOWNLOAD_ERROR || 0" class="red-font "></span><span
+ data-ng-class="{'error':(item.statusCount.DOWNLOAD_ERROR > 0)}" data-tests-id="errors_{{$index}}"></span></div>
+ </div>
+ </div>
+ </div>
+
+ <ul data-ng-show="item.showDetails && item.distributionComponents.length"
+ class="w-sdc-distribute-components-block disable-hover">
+
+ <li data-ng-repeat="(omfComponentID,omfComponentList) in item.distributionComponents | orderBy: '-timestamp' | filter:searchBind | groupBy:'omfComponentID' "
+ class="disable-hover"
+ data-ng-init="statusCount = getStatusCount(omfComponentList);">
+ <div class="w-sdc-distribute-row omf-component-row w-sdc-distribute-row-extends"
+ data-ng-class="{'extends': omfComponentListExtends}">
+ <div class="w-sdc-distribution-arrow-btn" data-ng-click="omfComponentListExtends=!omfComponentListExtends"
+ ng-class="{'extends': omfComponentListExtends}"
+ data-ng-init="omfComponentListExtends=false"
+ ></div>
+ <div class="w-sdc-distribute-status-block">
+ <div class="status-item-1">{{omfComponentID}} <span class="blue-font">{{(statusCount.NOT_NOTIFIED || 0) + (statusCount.NOTIFIED || 0) }}</span>
+ </div>
+ <div class="status-item-2">Notified:<span data-ng-bind="statusCount.NOTIFIED || 0"
+ class="blue-font"></span></div>
+ <div class="status-item-3">Downloaded:<span
+ data-ng-bind="statusCount.DOWNLOAD_OK || 0" class="blue-font"></span></div>
+ <div class="status-item-4">Deployed:<span data-ng-bind="statusCount.DEPLOY_OK || 0"
+ class="blue-font"></span><span
+ data-ng-class="{'deployed':(statusCount.DEPLOY_OK > 0)}"></span></div>
+ <div class="status-item-5">Not Notified:<span
+ data-ng-bind="statusCount.NOT_NOTIFIED || 0" class="blue-font"></span></div>
+ <div class="status-item-6">Errors:<span
+ data-ng-bind="statusCount.DOWNLOAD_ERROR || 0" class="red-font"></span><span
+ data-ng-class="{'error':(statusCount.DOWNLOAD_ERROR > 0)}"></span></div>
+ </div>
+ </div>
+ <div data-ng-show="omfComponentListExtends"
+ class="w-sdc-distribute-omfComponent-block disable-hover">
+ <div class="w-sdc-distribute-row-extends disable-hover">
+ <div class="disable-hover">
+ <div class="w-sdc-distribute-row omfComponent-table-head">
+ <div class="title item-1">Component ID</div>
+ <div class="title item-2">Artifact Name</div>
+ <div class="title item-3">URL</div>
+ <div class="title item-4">Time(UTC)</div>
+ <div class="title item-5">Status</div>
+ </div>
+
+ <div class="w-sdc-distribute-row omfComponent-table-row"
+ data-ng-repeat-start="(url,urlList) in omfComponentList | orderBy: '-timestamp' | groupBy:'url'"
+ data-ng-class="urlListExtends?'extends row-{{$index}}':'row-{{$index}}'">
+ <div class="w-sdc-distribute-cell item-1">
+ <div class="w-sdc-distribution-arrow-btn" data-ng-click="urlListExtends=!urlListExtends"
+ data-ng-class="{'extends': urlListExtends}"
+ data-ng-init="urlListListExtends=false"
+ ></div>
+ {{urlList[0].omfComponentID}}
+ </div>
+ <div class="w-sdc-distribute-cell item-2" sdc-smart-tooltip>
+ {{getUrlName(urlList[0].url)}}
+ </div>
+ <div class="w-sdc-distribute-cell item-3 disable-hover">
+ <div sdc-smart-tooltip class="distribution-url">{{urlList[0].url}}</div>
+ <div sdc-smart-tooltip title="Copy url" clipboard text="urlList[0].url"
+ class="sprite-new link-btn copy-link disable-hover"></div>
+ </div>
+ <div class="w-sdc-distribute-cell item-4"><span
+ data-ng-bind="urlList[0].timestamp | date: 'MM/dd/yyyy h:mma':'UTC'"></span>
+ </div>
+ <div class="w-sdc-distribute-cell item-5">{{urlList[0].status}}</div>
+ </div>
+
+
+ <div data-ng-repeat-end data-ng-show="urlListExtends" class="disable-hover" >
+ <div class="w-sdc-distribute-row extends disable-hover">
+ <ul data-ng-show="urlListExtends"
+ class="w-sdc-distribute-url-block disable-hover">
+ <li data-ng-repeat="distributionComponent in urlList | orderBy: '-timestamp'"
+ class="disable-hover">
+ <span
+ data-ng-bind="distributionComponent.timestamp | date: 'MM/dd/yyyy h:mma':'UTC'"
+ class="disable-hover"></span>
+ <span
+ class="disable-hover">{{distributionComponent.status}}</span>
+ <span
+ class="disable-hover reason" data-ng-show="distributionComponent.status == 'NOT_NOTIFIED'">Reason: Component has determined artifact is not needed.</span>
+ <span
+ class="disable-hover reason" data-ng-show="distributionComponent.errorReason">Reason: {{distributionComponent.errorReason}}</span>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+
+ </perfect-scrollbar>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution.less b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution.less
new file mode 100644
index 0000000000..8ad8c1793e
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/distribution/distribution.less
@@ -0,0 +1,361 @@
+
+.w-sdc-distribution-view {
+ text-align: left;
+
+ .g_1;
+ min-height: 500px;
+
+ .w-sdc-distribution-view-distributed-green-text {
+ .l_9;
+ .bold;
+ }
+ .w-sdc-distribution-view-distributed-error-red-text {
+ .h_9;
+ .bold;
+ }
+
+ .bg_c;
+ vertical-align: top;
+ padding: 30px 10px;
+ width: 100%;
+
+ .w-sdc-distribution-view-header {
+ display: flex;
+ -webkit-justify-content: space-between;
+ margin: 0 25px 5px 40px;
+
+ .header-spacer {
+ flex-grow: 5;
+ }
+ }
+
+ .top-search {
+ position: relative;
+ input {
+ &.search-text {
+ height: 26px;
+ line-height: 26px;
+ margin: 0 18px 4px 20px;
+ padding-right: 25px;
+ }
+
+ }
+ .magnification {
+ top: 8px;
+ right: 25px;
+ }
+ }
+
+ .w-sdc-distribution-view-content {
+ .perfect-scrollbar;
+ padding: 0 25px 0 0px;
+ margin-bottom: 25px;
+ height: 700px;
+ overflow: hidden;
+ position: relative;
+
+ }
+
+ .w-sdc-distribution-view-title {
+ .s_14_r;
+
+ line-height: 30px;
+
+ span {
+ padding-left: 5px;
+ }
+ }
+
+ .blue-font {
+ .a_14_m;
+
+ }
+
+ .red-font {
+ .q_14_m;
+ }
+
+ .w-sdc-distribution-view-block {
+ div {
+ display: inline-block;
+ }
+ }
+
+ .w-sdc-distribution-view-content-section {
+ ul {
+ list-style-type: none;
+ }
+
+ .distribution-bth {
+ .hand;
+ &.disabled {
+ cursor: none;
+ }
+ }
+
+ .copy-link {
+ padding-right: 19px;
+ margin-left: 8px;
+ cursor: pointer;
+
+ }
+
+ .w-sdc-distribute-row-extends {
+ border-Left: solid 4px transparent;
+ &.extends {
+ border-left: solid 4px @main_color_c;
+ border-bottom: 1px solid @border_color_f;
+ margin-bottom: 10px;
+ }
+ }
+ .w-sdc-distribute-parent-block {
+ border: 1px solid @main_color_o;;
+ width: 100%;
+ margin-bottom: 6px;
+
+ .status-icon {
+ vertical-align: middle;
+ margin-bottom: 4px;
+ }
+
+ &.extends {
+ background-color: @tlv_color_t;
+ }
+
+ :not(.disable-hover):hover {
+ background-color: @tlv_color_u;
+ }
+
+ .title-section {
+ display: inline-block;
+ margin-right: 10px;
+ flex-basis: 0;
+ }
+
+ .title {
+ .l_12_m;
+ font-weight: bold;
+ }
+ .w-sdc-distribute-content {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-left: 10px;
+ }
+
+ .w-sdc-distribution-arrow-btn {
+ .sprite-new;
+ .arrow-up-small;
+ margin: 0 6px;
+ }
+ .extends.w-sdc-distribution-arrow-btn {
+ -webkit-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+ }
+
+ .w-sdc-distribute-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .w-sdc-distribute-row-content {
+ margin: 15px 31px 10px 0;
+ width: 100%;
+ .w-sdc-distribute-status-block {
+ border-top: solid 1px @main_color_o;
+ }
+ .item-1 {
+ flex-grow: 2;
+ }
+ .item-2 {
+ flex-grow: 1;
+ }
+ .item-3 {
+ flex-grow: 1;
+ }
+ .item-4 {
+ flex-grow: 1;
+ }
+ .item-5 {
+ flex-grow: 1;
+ }
+ }
+ }
+
+ .w-sdc-distribute-status-block {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin: 10px 5px 0 5px;
+ padding: 5px 5px 0 5px;;
+ width: 100%;
+ div {
+ border-left: 1px solid @main_color_o;
+ padding: 0 12px;
+ }
+
+ .link {
+ .a_14_m;
+ cursor: pointer;
+ &:hover{
+ text-decoration: underline;
+ .b_14_m;
+ }
+ }
+
+ span {
+ padding: 2px;
+ }
+
+ .deployed {
+ margin-left: 10px;
+ .sprite-new;
+ .success-circle-small;
+ }
+
+ .error {
+ .q_14_m;
+ margin-left: 10px;
+ .sprite-new;
+ .error-icon;
+ }
+
+ .status-item-1 {
+ border-left: 0;
+ }
+
+ .status-item-6 {
+ flex-grow: 1;
+ border-left: none;
+ text-align: right;
+ }
+ }
+
+ .w-sdc-distribute-components-block {
+ padding: 0;
+ padding-bottom: 5px;
+ list-style-type: none;
+
+ li {
+ margin: 5px 2px;
+ }
+
+ .omf-component-row {
+ border: 1px solid @border_color_f;
+ padding-left: 3px;
+ background-color: white;
+ margin: 0 30px;
+ &.extends {
+ padding-left: 0;
+ border-Left: solid 4px @main_color_c;
+
+ }
+
+ .w-sdc-distribute-status-block {
+ margin: 5px;
+ padding: 5px;
+ }
+
+ .blue-font {
+ .a_16_m;
+
+ }
+
+ &:hover {
+ background-color: @tlv_color_u;
+ }
+
+ }
+
+ }
+
+ .w-sdc-distribute-omfComponent-block {
+ background-color: white;
+ margin: 0 30px;
+ padding: 8px 10px;
+ border: 1px solid @border_color_f;
+
+ .omfComponent-table-head {
+ margin-bottom: 5px;
+ background-color: @tlv_color_u;
+ .title {
+ padding: 6px 10px;
+ border-left: 1px solid @border_color_f;
+ &:first-child {
+ border: none;
+ }
+ }
+ }
+
+ .omfComponent-table-row {
+ border-bottom: 1px solid @border_color_f;
+ &.row-0 {
+ border-top: 1px solid @border_color_f;
+ }
+ .w-sdc-distribute-cell {
+ padding: 10px;
+ border-left: 1px solid @border_color_f;
+ &:last-child {
+ border-right: 1px solid @border_color_f;
+ }
+ &.item-5 {
+ .m_14_m;
+ }
+ }
+ }
+
+ .distribution-url {
+
+ }
+
+ .w-sdc-distribute-row.extends {
+ border-Left: solid 4px @main_color_c;
+ .item-1 {
+ border: none;
+ }
+
+ }
+
+ .item-1 {
+ width: 20%;
+ }
+ .item-2 {
+ width: 20%;
+ }
+
+ .item-3 {
+ width: 24%;
+ display: flex;
+ }
+
+ .item-4 {
+ width: 18%;
+ }
+
+ .item-5 {
+ width: 18%;
+ }
+ }
+
+ .w-sdc-distribute-url-block {
+
+ padding: 10px 15px;
+ border: none;
+ border-right: 1px solid @border_color_f;
+ border-bottom: 1px solid @border_color_f;
+ width: 100%;
+ li {
+ border: none;
+ span {
+ padding-right: 30px;
+ .m_12_r;
+ }
+ }
+
+ }
+ }
+
+ }
+}
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view-model.ts
new file mode 100644
index 0000000000..f613648596
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view-model.ts
@@ -0,0 +1,379 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+ import ISubCategory = Sdc.Models.ISubCategory;
+ import IMainCategory = Sdc.Models.IMainCategory;
+ import ResourceType = Sdc.Utils.Constants.ResourceType;
+
+ export class Validation {
+ validationPattern:RegExp;
+ contactIdValidationPattern:RegExp;
+ tagValidationPattern:RegExp;
+ vendorValidationPattern:RegExp;
+ commentValidationPattern:RegExp;
+ projectCodeValidationPattern:RegExp;
+ }
+
+ export interface IGeneralScope extends IWorkspaceViewModelScope {
+ validation:Validation;
+ editForm:ng.IFormController;
+ categories: Array<IMainCategory>;
+ latestCategoryId: string;
+ latestVendorName: string;
+ importedFileExtension:any;
+ isCreate:boolean;
+ isShowFileBrowse:boolean;
+ isShowOnboardingSelectionBrowse:boolean;
+ importedToscaBrowseFileText:string;
+ importCsarProgressKey:string;
+ browseFileLabel:string;
+
+
+ onToscaFileChange():void
+ validateField(field:any):boolean;
+ validateName(isInit:boolean): void;
+ calculateUnique(mainCategory:string, subCategory:string):string; // Build unique string from main and sub category
+ onVendorNameChange(oldVendorName:string): void;
+ convertCategoryStringToOneArray(category:string, subcategory:string):Array<Models.IMainCategory>;
+ onCategoryChange():void;
+ openOnBoardingModal():void;
+ initCategoreis():void;
+ }
+
+ export class GeneralViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.CacheService',
+ 'ValidationPattern',
+ 'ContactIdValidationPattern',
+ 'TagValidationPattern',
+ 'VendorValidationPattern',
+ 'CommentValidationPattern',
+ 'ValidationUtils',
+ 'sdcConfig',
+ 'ProjectCodeValidationPattern',
+ '$state',
+ 'ModalsHandler',
+ 'EventListenerService',
+ 'Notification',
+ 'Sdc.Services.ProgressService',
+ '$interval',
+ '$filter',
+ '$timeout'
+ ];
+
+ constructor(private $scope:IGeneralScope,
+ private cacheService:Services.CacheService,
+ private ValidationPattern:RegExp,
+ private ContactIdValidationPattern:RegExp,
+ private TagValidationPattern:RegExp,
+ private VendorValidationPattern:RegExp,
+ private CommentValidationPattern:RegExp,
+ private ValidationUtils:Sdc.Utils.ValidationUtils,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private ProjectCodeValidationPattern:RegExp,
+ private $state:ng.ui.IStateService,
+ private ModalsHandler: Sdc.Utils.ModalsHandler,
+ private EventListenerService:Services.EventListenerService,
+ private Notification:any,
+ private progressService:Sdc.Services.ProgressService,
+ protected $interval:any,
+ private $filter:ng.IFilterService,
+ private $timeout:ng.ITimeoutService
+ ){
+
+ this.registerToSuccessSaveEvent();
+ this.initScopeValidation();
+ this.initScopeMethods();
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+ private registerToSuccessSaveEvent = ():void => {
+ // Register to save success to show notification to user.
+ this.EventListenerService.registerObserverCallback(Utils.Constants.EVENTS.ON_WORKSPACE_SAVE_BUTTON_SUCCESS, this.showSuccessNotificationMessage);
+
+ };
+
+ private showSuccessNotificationMessage = ():void => {
+ // In case we import CSAR. Notify user when import VF was finished.
+ this.Notification.success({
+ message: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_FINISHED_DESCRIPTION"),
+ title: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_FINISHED_TITLE")
+ });
+
+ //set the form Pristine after save to reset the unsaved changes (whit for dom reload)
+ this.$timeout(()=> {
+ if(this.$scope.editForm) {
+ this.$scope.editForm.$setPristine();
+ }
+ }, 500);
+
+ };
+
+
+
+ private initScopeValidation = ():void => {
+ this.$scope.validation = new Validation();
+ this.$scope.validation.validationPattern = this.ValidationPattern;
+ this.$scope.validation.contactIdValidationPattern = this.ContactIdValidationPattern;
+ this.$scope.validation.tagValidationPattern = this.TagValidationPattern;
+ this.$scope.validation.vendorValidationPattern = this.VendorValidationPattern;
+ this.$scope.validation.commentValidationPattern = this.CommentValidationPattern;
+ this.$scope.validation.projectCodeValidationPattern = this.ProjectCodeValidationPattern;
+ };
+
+ private initScope = ():void => {
+
+ // Work around to change the csar version
+ if (this.cacheService.get(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG)) {
+ (<Resource>this.$scope.component).csarVersion = this.cacheService.get(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG);
+ }
+
+ this.$scope.importedToscaBrowseFileText = this.$scope.component.name + " (" + (<Resource>this.$scope.component).csarVersion + ")";
+ this.$scope.importCsarProgressKey = "importCsarProgressKey";
+ this.$scope.browseFileLabel = this.$scope.component.isResource() && (<Resource>this.$scope.component).resourceType===ResourceType.VF? "Upload file" : "Upload VFC";
+ this.$scope.progressService = this.progressService;
+
+ // Workaround to short vendor name to 25 chars
+ // onboarding send 27 chars, and the validation pattern is 25 chars.
+ if (this.$scope.component.vendorName){
+ this.$scope.component.vendorName = this.$scope.component.vendorName.substr(0, 25);
+ }
+
+ // Init UIModel
+ this.$scope.component.tags = _.without(this.$scope.component.tags, this.$scope.component.name);
+
+ // Init categories
+ this.$scope.initCategoreis();
+
+ // Init the decision if to show file browse.
+ this.$scope.isShowFileBrowse = false;
+ if (this.$scope.component.isResource()){
+ let resource:Sdc.Models.Components.Resource = <Sdc.Models.Components.Resource>this.$scope.component;
+ console.log(resource.name + ": " + resource.csarUUID);
+ if (resource.importedFile){ // Component has imported file.
+ this.$scope.isShowFileBrowse = true;
+ }
+ if (this.$scope.isEditMode() && resource.resourceType== ResourceType.VF && !resource.csarUUID){
+ this.$scope.isShowFileBrowse = true;
+ }
+ };
+
+ // Init the decision if to show onboarding
+ this.$scope.isShowOnboardingSelectionBrowse = false;
+ if (this.$scope.component.isResource() &&
+ this.$scope.isEditMode() &&
+ (<Resource>this.$scope.component).resourceType== ResourceType.VF &&
+ (<Resource>this.$scope.component).csarUUID) {
+ this.$scope.isShowOnboardingSelectionBrowse = true;
+ }
+
+ //init file extensions based on the file that was imported.
+ if (this.$scope.component.isResource() && (<Resource>this.$scope.component).importedFile){
+ let fileName:string = (<Resource>this.$scope.component).importedFile.filename;
+ let fileExtension:string = fileName.split(".").pop();
+ if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1){
+ this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension;
+ (<Resource>this.$scope.component).importedFile.filetype="csar";
+ } else if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1){
+ (<Resource>this.$scope.component).importedFile.filetype="yaml";
+ this.$scope.importedFileExtension = this.sdcConfig.toscaFileExtension;
+ }
+ }else if(this.$scope.isEditMode()&& (<Resource>this.$scope.component).resourceType === ResourceType.VF){
+ this.$scope.importedFileExtension = this.sdcConfig.csarFileExtension;
+ //(<Resource>this.$scope.component).importedFile.filetype="csar";
+ }
+
+ this.$scope.setValidState(true);
+
+ this.$scope.calculateUnique = (mainCategory:string, subCategory:string):string => {
+ let uniqueId:string = mainCategory;
+ if (subCategory) {
+ uniqueId += "_#_" + subCategory; // Set the select category combobox to show the selected category.
+ }
+ return uniqueId;
+ };
+
+ //TODO remove this after handling contact in UI
+ if(this.$scope.component.isProduct() && this.$scope.isCreateMode()){
+ (<Models.Components.Product>this.$scope.component).contacts = [];
+ (<Models.Components.Product>this.$scope.component).contacts.push(this.cacheService.get("user").userId);
+ }else if(this.$scope.isCreateMode()){
+ this.$scope.component.contactId = this.cacheService.get("user").userId;
+ }
+
+ };
+
+ // Convert category string MainCategory_#_SubCategory to Array with one item (like the server except)
+ private convertCategoryStringToOneArray = ():Array<Models.IMainCategory> => {
+ let tmp = this.$scope.component.selectedCategory.split("_#_");
+ let mainCategory = tmp[0];
+ let subCategory = tmp[1];
+
+ // Find the selected category and add the relevant sub category.
+ let selectedMainCategory:IMainCategory = <Models.IMainCategory>_.find(this.$scope.categories, function (item) {
+ return item["name"] === mainCategory;
+
+ });
+
+ let mainCategoryClone = angular.copy(selectedMainCategory);
+ if (subCategory) {
+ let selectedSubcategory = <Models.ISubCategory>_.find(selectedMainCategory.subcategories, function (item) {
+ return item["name"] === subCategory;
+ });
+ mainCategoryClone['subcategories'] = [angular.copy(selectedSubcategory)];
+ }
+ let tmpSelected = <Models.IMainCategory> mainCategoryClone;
+
+ let result:Array<Models.IMainCategory> = [];
+ result.push(tmpSelected);
+
+ return result;
+ };
+
+ private updateComponentNameInBreadcrumbs = ():void => {
+ //update breadcrum after changing name
+ this.$scope.breadcrumbsModel[1].updateSelectedMenuItemText(this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name);
+ this.$scope.updateMenuComponentName(this.$scope.component.name);
+ };
+
+ private initScopeMethods = ():void => {
+
+ this.$scope.initCategoreis = ():void => {
+ if (this.$scope.componentType === Utils.Constants.ComponentType.RESOURCE) {
+ this.$scope.categories = this.cacheService.get('resourceCategories');
+
+ }
+ if (this.$scope.componentType === Utils.Constants.ComponentType.SERVICE) {
+ this.$scope.categories = this.cacheService.get('serviceCategories');
+ }
+ }
+
+ this.$scope.validateField = (field:any):boolean => {
+ if (field && field.$dirty && field.$invalid) {
+ return true;
+ }
+ return false;
+ };
+
+ this.$scope.openOnBoardingModal=():void => {
+ let csarUUID = (<Resource>this.$scope.component).csarUUID;
+ this.ModalsHandler.openOnboadrdingModal('Update', csarUUID).then(()=>{
+ // OK
+ this.$scope.uploadFileChangedInGeneralTab();
+ }, ()=>{
+ // ERROR
+ });
+ };
+
+ this.$scope.validateName = (isInit:boolean):void => {
+ if (isInit === undefined) {
+ isInit = false;
+ }
+
+ let name = this.$scope.component.name;
+ if (!name || name === "") {
+ if (this.$scope.editForm
+ && this.$scope.editForm["componentName"]
+ && this.$scope.editForm["componentName"].$error) {
+
+ // Clear the error name already exists
+ this.$scope.editForm["componentName"].$setValidity('nameExist', true);
+ }
+
+ return;
+ }
+ //?????????????????????????
+ let subtype:string = Utils.Constants.ComponentType.RESOURCE == this.$scope.componentType ? this.$scope.component.getComponentSubType() : undefined;
+
+ let onFailed = (response) => {
+ //console.info('onFaild', response);
+ //this.$scope.isLoading = false;
+ };
+
+ let onSuccess = (validation:Models.IValidate) => {
+ this.$scope.editForm["componentName"].$setValidity('nameExist', validation.isValid);
+ if(validation.isValid){
+ //update breadcrumb after changing name
+ this.updateComponentNameInBreadcrumbs();
+ }
+ };
+
+ if (isInit) {
+ // When page is init after update
+ if (this.$scope.component.name !== this.$scope.originComponent.name) {
+ if (!(this.$scope.componentType===Utils.Constants.ComponentType.RESOURCE && (<Resource>this.$scope.component).csarUUID!==undefined)
+ ){
+ this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
+ }
+ }
+ } else {
+ // Validating on change (has debounce)
+ if (this.$scope.editForm
+ && this.$scope.editForm["componentName"]
+ && this.$scope.editForm["componentName"].$error
+ && !this.$scope.editForm["componentName"].$error.pattern
+ && this.$scope.component.name !== this.$scope.originComponent.name
+ ) {
+ if (!(this.$scope.componentType===Utils.Constants.ComponentType.RESOURCE && (<Resource>this.$scope.component).csarUUID!==undefined)
+ ){
+ this.$scope.component.validateName(name, subtype).then(onSuccess, onFailed);
+ }
+ } else if (this.$scope.component.name === this.$scope.originComponent.name) {
+ // Clear the error
+ this.$scope.editForm["componentName"].$setValidity('nameExist', true);
+ }
+ }
+ };
+
+ this.$scope.$watchCollection('component.name', (newData:any):void => {
+ this.$scope.validateName(false);
+ });
+
+ // Notify the parent if this step valid or not.
+ this.$scope.$watch("editForm.$valid", (newVal, oldVal) => {
+ this.$scope.setValidState(newVal);
+ });
+
+ this.$scope.$watch("editForm.$dirty", (newVal, oldVal) => {
+ if (newVal!==oldVal) {
+ this.$state.current.data.unsavedChanges = newVal && !this.$scope.isCreateMode();
+ }
+ });
+
+ this.$scope.onCategoryChange = ():void => {
+ this.$scope.component.categories = this.convertCategoryStringToOneArray();
+ this.$scope.component.icon = Utils.Constants.DEFAULT_ICON;
+ };
+
+ this.$scope.onVendorNameChange = (oldVendorName:string):void => {
+ if (this.$scope.component.icon === oldVendorName) {
+ this.$scope.component.icon = Utils.Constants.DEFAULT_ICON;
+ }
+ };
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view.html
new file mode 100644
index 0000000000..1c1d4fedad
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/general/general-view.html
@@ -0,0 +1,307 @@
+<div include-padding="true" class="sdc-workspace-general-step">
+
+ <form novalidate class="w-sdc-form" name="editForm">
+
+ <div class="w-sdc-form-section-container">
+
+ <!--------------------- IMPORT TOSCA FILE USING BROWSE (ALSO VFC) -------------------->
+ <div class="i-sdc-form-item" ng-if="isShowFileBrowse">
+ <label class="i-sdc-form-label required">{{browseFileLabel}}</label>
+ <file-upload id="fileUploadElement"
+ class="i-sdc-form-input"
+ element-name="fileElement"
+ element-disabled="{{!isCreateMode()&&!(isEditMode()&&component.resourceType=='VF')}} || {{isViewMode()}}"
+ form-element="editForm"
+ file-model="component.importedFile"
+ on-file-changed-in-directive="uploadFileChangedInGeneralTab"
+ extensions="{{importedFileExtension}}"
+ default-text="'Browse to select file'"
+ data-ng-class="{'error':!(isEditMode()&&component.resourceType=='VF') && (!editForm.fileElement.$valid || !component.importedFile.filename)}"></file-upload>
+ </div>
+
+ <!--------------------- IMPORT TOSCA FILE USING ONBOARDING -------------------->
+ <div class="i-sdc-form-item" ng-if="isShowOnboardingSelectionBrowse">
+ <label class="i-sdc-form-label required">Select VSP</label>
+ <div class="i-sdc-form-file-upload i-sdc-form-input">
+ <span class="i-sdc-form-file-name" data-tests-id="filename">{{(fileModel && fileModel.filename) || importedToscaBrowseFileText}}</span>
+ <div class="i-sdc-form-file-upload-x-btn" ng-click="cancel()" data-ng-show="fileModel.filename && fileModel.filename!=='' && elementDisabled!=='true'"></div>
+ <input type="button" name="fileElement"/>
+ <div class="file-upload-browse-btn" data-ng-click="openOnBoardingModal()" data-tests-id="browseButton">Browse</div>
+ </div>
+ </div>
+
+ <div class="input-error-file-upload" data-ng-show="component.importedFile && (!editForm.fileElement.$valid || !component.importedFile.filename)">
+ <!-- editForm.fileElement.$error.required <== Can not use this, because the browse is done from outside for the first time -->
+ <span ng-show="!(isEditMode()&&component.resourceType=='VF')&&!component.importedFile.filename" translate="NEW_SERVICE_RESOURCE_ERROR_TOSCA_FILE_REQUIRED"></span><!-- Required -->
+ <span ng-show="editForm.fileElement.$error.maxsize" translate="VALIDATION_ERROR_MAX_FILE_SIZE"></span>
+ <span ng-show="editForm.fileElement.$error.filetype" translate="NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS" translate-values="{'extensions': '{{importedFileExtension}}' }"></span>
+ <span ng-show="editForm.fileElement.$error.emptyFile" translate="VALIDATION_ERROR_EMPTY_FILE"></span>
+ </div>
+ <!--------------------- IMPORT TOSCA FILE -------------------->
+
+ <div class="w-sdc-form-columns-wrapper">
+
+ <div class="w-sdc-form-column">
+
+ <!--------------------- NAME -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.componentName)}">
+ <label class="i-sdc-form-label required">Name</label>
+ <input class="i-sdc-form-input"
+ data-ng-class="{'view-mode': isViewMode()}"
+ name="componentName"
+ data-ng-init="isCreateMode() && validateName(true)"
+ data-ng-maxlength="{{component.isProduct()?'25':'50'}}"
+ maxlength="{{component.isProduct()?'25':'50'}}"
+ data-ng-minlength="{{component.isProduct()?'4':'0'}}"
+ minlength="{{component.isProduct()?'4':'0'}}"
+ data-ng-model="component.name"
+ type="text"
+ data-required
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.validationPattern"
+ data-ng-disabled="component.isAlreadyCertified()"
+ data-tests-id="name"
+ autofocus
+ ng-readonly="isViewMode()"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.componentName)">
+ <span ng-show="editForm.componentName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED"></span>
+ <span ng-show="editForm.componentName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editForm.componentName.$error.minlength" translate="VALIDATION_ERROR_MIN_LENGTH" translate-values="{'min': '4' }"></span>
+ <span ng-show="editForm.componentName.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS"></span>
+ <span ng-show="editForm.componentName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- NAME -------------------->
+
+ <!--------------------- FULL NAME -------------------->
+ <div ng-if="component.isProduct()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.fullName)}">
+ <label class="i-sdc-form-label required">Full Name</label>
+ <input class="i-sdc-form-input"
+ name="fullName"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-change="validateName()"
+ data-ng-maxlength="100"
+ maxlength="100"
+ data-ng-minlength="4"
+ minlength="4"
+ data-ng-model="component.fullName"
+ type="text"
+ data-required
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.commentValidationPattern"
+ data-tests-id="fullName"
+ autofocus
+ ng-readonly="isViewMode()"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.fullName)">
+ <span ng-show="editForm.fullName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_NAME_REQUIRED"></span>
+ <span ng-show="editForm.fullName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span>
+ <span ng-show="editForm.fullName.$error.minlength" translate="VALIDATION_ERROR_MIN_LENGTH" translate-values="{'min': '4' }"></span>
+ <span ng-show="editForm.fullName.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_NAME_EXISTS"></span>
+ <span ng-show="editForm.fullName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- NAME -------------------->
+
+ <!--------------------- DESCRIPTION -------------------->
+ <div class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.description)}">
+ <label class="i-sdc-form-label required">Description</label>
+ <textarea class="description"
+ name="description"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-maxlength="1024"
+ data-required
+ data-ng-model="component.description"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-pattern="validation.commentValidationPattern"
+ maxlength="1024"
+ data-tests-id="description"></textarea>
+ <!-- placeholder="Description here..." -->
+
+ <div class="input-error" data-ng-show="validateField(editForm.description)">
+ <span ng-show="editForm.description.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_RESOURCE_DESCRIPTION_REQUIRED"></span>
+ <span ng-show="editForm.description.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '1024' }"></span>
+ <span ng-show="editForm.description.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- DESCRIPTION -------------------->
+
+ <!--------------------- CATEGORIES -------------------->
+ <div class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.category)}"
+ data-ng-if="!component.isProduct()">
+ <loader data-display="!categories && !initCategoreis()" relative="true"></loader>
+ <label class="i-sdc-form-label required">Category</label>
+ <select class="i-sdc-form-select"
+ data-required
+ name="category"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-change="onCategoryChange()"
+ data-ng-disabled="component.isAlreadyCertified() || (component.isCsarComponent() && component.selectedCategory && component.selectedCategory!=='')"
+ data-ng-model="component.selectedCategory"
+ data-tests-id="selectGeneralCategory"
+ >
+ <option value="">Select category</option>
+ <optgroup ng-if="component.isResource()" data-ng-repeat="mainCategory in categories | orderBy:['name']" label="{{mainCategory.name}}" data-tests-id="{{mainCategory.name}}">
+ <option data-ng-repeat="subCategory in mainCategory.subcategories track by $index"
+ data-ng-selected="component.selectedCategory === calculateUnique(mainCategory.name,subCategory.name)"
+ data-tests-id="{{subCategory.name}}"
+ value="{{calculateUnique(mainCategory.name, subCategory.name)}}">{{subCategory.name}}
+
+ </option>
+ </optgroup>
+ <option ng-if="component.isService()" data-ng-repeat="mainCategory in categories | orderBy:['name']"
+ data-ng-selected="component.selectedCategory===mainCategory.name"
+ value="{{mainCategory.name}}"
+ data-tests-id="{{mainCategory.name}}">{{mainCategory.name}}</option>
+ </select>
+
+ <div class="input-error" data-ng-show="validateField(editForm.category)">
+ <span ng-show="editForm.category.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CATEGORY_REQUIRED"></span>
+ </div>
+ </div>
+ <!--------------------- CATEGORIES -------------------->
+
+ <!--------------------- PROJECT CODE -------------------->
+ <div class="i-sdc-form-item" data-ng-if="!component.isResource()"
+ data-ng-class="{'error': validateField(editForm.projectCode)}">
+ <label class="i-sdc-form-label required" translate="GENERAL_LABEL_PROJECT_CODE"></label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-model="component.projectCode"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="128"
+ data-required
+ name="projectCode"
+ data-ng-pattern="validation.projectCodeValidationPattern"
+ maxlength="50"
+ data-tests-id="projectCode"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.projectCode)">
+ <span ng-show="editForm.projectCode.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_REQUIRED"></span>
+ <span ng-show="editForm.projectCode.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_NOT_VALID"></span>
+ </div>
+ </div>
+
+ <!--------------------- VENDOR NAME -------------------->
+ <div ng-if="component.isResource()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.vendorName)}">
+ <label class="i-sdc-form-label required">Vendor</label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-model="component.vendorName"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="25"
+ data-required
+ ng-click="oldValue = component.vendorName"
+ name="vendorName"
+ data-ng-change="onVendorNameChange(oldValue)"
+ data-ng-pattern="validation.vendorValidationPattern"
+ maxlength="25"
+ data-ng-disabled="component.isAlreadyCertified() || (component.isCsarComponent() && component.vendorName && component.vendorName!=='')"
+ data-tests-id="vendorName"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.vendorName)">
+ <span ng-show="editForm.vendorName.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_VENDOR_NAME_REQUIRED"></span>
+ <span ng-show="editForm.vendorName.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span>
+ <span ng-show="editForm.vendorName.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+
+ </div>
+
+ <!--------------------- VENDOR NAME -------------------->
+
+ <!--------------------- VENDOR RELEASE -------------------->
+ <div ng-if="component.isResource()"
+ class="i-sdc-form-item"
+ data-ng-class="{'error': validateField(editForm.vendorRelease)}">
+ <label class="i-sdc-form-label required">Vendor Release</label>
+ <input class="i-sdc-form-input" type="text"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-model="component.vendorRelease"
+ data-ng-model-options="{ debounce: 500 }"
+ data-ng-maxlength="25"
+ data-required
+ name="vendorRelease"
+ data-ng-pattern="validation.vendorValidationPattern"
+ maxlength="25"
+ data-ng-disabled="component.isCsarComponent() && component.vendorRelease && component.vendorRelease!==''"
+ data-tests-id="vendorRelease"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.vendorRelease)">
+ <span ng-show="editForm.vendorRelease.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_VENDOR_RELEASE_REQUIRED"></span>
+ <span ng-show="editForm.vendorRelease.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '128' }"></span>
+ <span ng-show="editForm.vendorRelease.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ </div>
+ </div>
+ <!--------------------- VENDOR RELEASE -------------------->
+
+
+
+ </div><!-- Close w-sdc-form-column -->
+
+ <div class="w-sdc-form-column">
+
+ <!--------------------- RESOURCE TAGS -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.tags)}">
+ <label class="i-sdc-form-label">Tags</label>
+
+ <sdc-tags form-element="editForm" element-name="tags" max-tags="20" class="i-sdc-form-item-tags"
+ sdc-disabled="isViewMode()"
+ tags="component.tags"
+ pattern="validation.tagValidationPattern"
+ special-tag="component.name"></sdc-tags>
+
+ <div class="input-error" data-ng-show="validateField(editForm.tags)">
+ <span ng-show="editForm.tags.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span>
+ <span ng-show="editForm.tags.$error.nameExist" translate="NEW_SERVICE_RESOURCE_ERROR_TAG_NAME_EXIST"></span>
+ </div>
+ </div>
+ <!--------------------- RESOURCE TAGS -------------------->
+
+ <!--------------------- CONTACT ID -------------------->
+ <div class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.contactId)}">
+ <label class="i-sdc-form-label " data-ng-class="{'required':!component.isProduct()}" translate="GENERAL_LABEL_CONTACT_ID"></label>
+ <input class="i-sdc-form-input" type="text" data-ng-if="!component.isProduct()"
+ data-ng-model="component.contactId"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-required="!component.isProduct()"
+ name="contactId"
+ data-ng-pattern="validation.contactIdValidationPattern"
+ data-ng-model-options="{ debounce: 500 }"
+ data-tests-id="userId"
+ maxlength="50"
+ />
+ <input class="i-sdc-form-input" type="text" data-ng-if="component.isProduct()"
+ data-ng-model="component.contacts[0]"
+ data-ng-class="{'view-mode': isViewMode()}"
+ data-ng-required="!component.isProduct()"
+ name="contactId"
+ data-ng-pattern="validation.contactIdValidationPattern"
+ data-ng-model-options="{ debounce: 500 }"
+ data-tests-id="userId"
+ maxlength="50"
+ />
+
+ <div class="input-error" data-ng-show="validateField(editForm.contactId)">
+ <span ng-show="editForm.contactId.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED"></span>
+ <span ng-show="editForm.contactId.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID"></span>
+ </div>
+ </div>
+ <!--------------------- CONTACT ID -------------------->
+
+
+ </div><!-- Close w-sdc-form-column -->
+ </div>
+
+ </div><!-- Close w-sdc-form-section-container -->
+
+ </form>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/general/general.less b/catalog-ui/app/scripts/view-models/workspace/tabs/general/general.less
new file mode 100644
index 0000000000..1861d02e98
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/general/general.less
@@ -0,0 +1,64 @@
+.sdc-workspace-general-step {
+
+ .w-sdc-form {
+ padding: 0;
+
+ .i-sdc-form-file-upload{
+ input[type="button"] {
+ cursor: pointer;
+ display: block;
+ filter: alpha(opacity=0);
+ width: 100px;
+ height: 30px;
+ opacity: 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 0;
+ }
+
+ .file-upload-browse-btn {
+ .noselect;
+ .bg_n;
+ padding: 4px 6px;
+ cursor: pointer;
+ z-index: 999;
+ width: 100px;
+ height: 28px;
+ text-align: center;
+
+ &.disabled {
+ cursor: default;
+ }
+ }
+ }
+
+ .w-sdc-form-section-container {
+ text-align: center;
+ }
+
+ .i-sdc-form-item {
+ &.upload {
+ margin-top: 0;
+ width: auto;
+ padding: 10px;
+ }
+ }
+
+ .template-desc {
+ border: 1px dashed @border_color_f;
+ height: 130px;
+ overflow: hidden;
+ padding: 10px 6px 6px 6px;
+ margin-top: 10px;
+ }
+
+ .sdc-tag .tag {
+ max-width: 225px;
+ }
+
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view-model.ts
new file mode 100644
index 0000000000..a591641d0a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view-model.ts
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 4/4/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IIconsScope extends IWorkspaceViewModelScope {
+ icons : Array<string>;
+ iconSprite: string;
+ setComponentIcon(iconSrc:string): void;
+ }
+
+ export class IconsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.AvailableIconsService',
+ 'ComponentFactory',
+ '$state'
+ ];
+
+ constructor(private $scope:IIconsScope,
+ private availableIconsService:Services.AvailableIconsService,
+ private ComponentFactory:Sdc.Utils.ComponentFactory,
+ private $state:ng.ui.IStateService) {
+
+
+ this.initScope();
+ this.initIcons();
+ this.$scope.updateSelectedMenuItem();
+ this.$scope.iconSprite = this.$scope.component.iconSprite;
+
+ if (this.$scope.component.isResource()) {
+ this.initVendor();
+ }
+ }
+
+ private initialIcon:string = this.$scope.component.icon;
+ private initIcons = ():void => {
+
+ // For subcategories that where created by admin, there is no icons
+ this.$scope.icons = new Array<string>();
+ if (this.$scope.component.categories && this.$scope.component.categories.length > 0) {
+
+ _.forEach(this.$scope.component.categories, (category:Models.IMainCategory):void => {
+ if (category.icons) {
+ this.$scope.icons = this.$scope.icons.concat(category.icons);
+ }
+ if (category.subcategories) {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory):void => {
+ if (subcategory.icons) {
+ this.$scope.icons = this.$scope.icons.concat(subcategory.icons);
+ }
+ });
+ }
+ });
+ }
+
+ if (this.$scope.component.isResource()) {
+ let resourceType:string = this.$scope.component.getComponentSubType();
+ if (resourceType === 'VL') {
+ this.$scope.icons = ['vl'];
+ }
+ if (resourceType === 'CP') {
+ this.$scope.icons = ['cp'];
+ }
+ }
+
+ if (this.$scope.icons.length === 0) {
+ this.$scope.icons = this.availableIconsService.getIcons(this.$scope.component.componentType);
+ }
+ //we always add the defual icon to the list
+ this.$scope.icons.push('defaulticon');
+ };
+
+ private initVendor = ():void => {
+ let vendors:Array<string> = this.availableIconsService.getIcons(this.$scope.component.componentType).slice(5, 19);
+ let vendorName = this.$scope.component.vendorName.toLowerCase();
+ if ('at&t' === vendorName) {
+ vendorName = 'att';
+ }
+ if ('nokia' === vendorName) {
+ vendorName = 'nokiasiemens';
+ }
+
+ let vendor:string = _.find(vendors, (vendor:string)=> {
+ return vendor.replace(/[_]/g, '').toLowerCase() === vendorName;
+ });
+
+ if (vendor && this.$scope.icons.indexOf(vendor) === -1) {
+ this.$scope.icons.push(vendor);
+ }
+ };
+
+ private initScope():void {
+ this.$scope.icons = [];
+ this.$scope.setValidState(true);
+ //if(this.$scope.component.icon === Utils.Constants.DEFAULT_ICON){
+ // //this.$scope.setValidState(false);
+ //}
+
+ this.$scope.setComponentIcon = (iconSrc:string):void => {
+ this.$state.current.data.unsavedChanges = !this.$scope.isViewMode() && (iconSrc != this.initialIcon);
+ this.$scope.component.icon = iconSrc;
+ // this.$scope.setValidState(true);
+ };
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view.html
new file mode 100644
index 0000000000..aac14e0e84
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons-view.html
@@ -0,0 +1,26 @@
+<div class="workspace-icons">
+
+ <form novalidate class="w-sdc-form" name="iconForm">
+ <label class="i-sdc-form-label icons-label required">Icons</label>
+ <div class="selected-icon-container" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="i-sdc-form-item-suggested-icon large selected-icon {{iconSprite}} {{component.icon}}"
+ data-ng-class="{ 'disable': isViewMode() }"
+ ng-model="component.icon"
+ tooltips tooltip-content='{{component.icon | translate}}'
+ >
+ </div>
+ </div>
+ <div data-ng-class="{'view-mode': isViewMode()}" class="icons-text">Select one of the icons below for the asset</div>
+ <div class="i-sdc-form-item suggested-icons-container" data-ng-class="{'view-mode no-pointer-events' : isViewMode()}">
+ <div class ="suggested-icon-wrapper" ng-class="component.icon==='{{iconSrc}}' ? 'selected' : '' " data-ng-repeat="iconSrc in icons track by $index">
+ <div class="i-sdc-form-item-suggested-icon large {{iconSprite}} {{iconSrc}}" data-ng-class="component.isAlreadyCertified() || isViewMode() ? 'disable':'hand'"
+ ng-model="component.icon"
+ data-tests-id="{{iconSrc}} iconBox"
+ data-ng-click="!component.isAlreadyCertified() && setComponentIcon(iconSrc)"
+ tooltips tooltip-content='{{iconSrc | translate}}'
+ >
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons.less b/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons.less
new file mode 100644
index 0000000000..65f946f395
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/icons/icons.less
@@ -0,0 +1,65 @@
+.workspace-icons {
+
+ width: 89%;
+ display: inline-block;
+ text-align: center;
+ align-items: center;
+
+ .w-sdc-form {
+ padding-top: 0px;
+ padding-bottom: 0px;
+ .selected-icon-container {
+ text-align: left;
+ border: 1px solid #cfcfcf;
+ clear: both;
+ margin-bottom: 30px;
+ padding: 2px 0px 5px 5px;
+ .selected-icon {
+ margin: 8px 5px 0px 6px;
+ }
+ }
+
+ .suggested-icons-container {
+ text-align: left;
+ border: 1px solid #cfcfcf;
+ clear: both;
+ padding: 2px 0px 5px 5px;
+ height: 340px;
+ margin-bottom: 0px;
+
+ .suggested-icon-wrapper {
+ margin: 8px 5px 0px 6px;
+ display: inline-block;
+
+ &.selected {
+ border: 2px solid @main_color_a;
+ border-radius: 35px;
+ display: inline-block;
+ line-height: 0px;
+ padding: 3px;
+ }
+
+ }
+ .suggested-icon {
+ // margin: 8px 5px 0px 6px;
+ display: inline-block;
+ &.disable{
+ opacity: 0.4;
+ }
+ }
+ }
+
+ .icons-label {
+ float: left;
+ }
+
+ .icons-text {
+ text-align: left;
+ line-height: 32px;
+ padding-left: 10px;
+ width: 100%;
+ border: 1px solid #cfcfcf;
+ border-bottom: none;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts
new file mode 100644
index 0000000000..3a048c1879
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts
@@ -0,0 +1,150 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ export interface IInformationArtifactsScope extends IWorkspaceViewModelScope {
+ artifacts: Array<Models.ArtifactModel>;
+ tableHeadersList: Array<any>;
+ artifactType: string;
+ isResourceInstance:boolean;
+ downloadFile:Models.IFileDownload;
+ isLoading:boolean;
+ sortBy:string;
+ reverse:boolean;
+
+ getTitle(): string;
+ addOrUpdate(artifact:Models.ArtifactModel): void;
+ delete(artifact:Models.ArtifactModel): void;
+ download(artifact:Models.ArtifactModel): void;
+ clickArtifactName(artifact:any):void;
+ openEditEnvParametersModal(artifactResource:Models.ArtifactModel):void;
+ sort(sortBy:string): void;
+ showNoArtifactMessage():boolean;
+ }
+
+ export class InformationArtifactsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'Sdc.Services.SharingService',
+ '$state',
+ 'sdcConfig',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:IInformationArtifactsScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private sharingService:Sdc.Services.SharingService,
+ private $state:any,
+ private sdcConfig:Models.IAppConfigurtaion,
+ private ModalsHandler:Utils.ModalsHandler) {
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+
+ private getMappedObjects():any {
+ return {
+ normal: this.$scope.component.artifacts
+ };
+ }
+
+ private initScope = ():void => {
+ let self = this;
+ this.$scope.isLoading = false;
+ this.$scope.sortBy = 'artifactDisplayName';
+ this.$scope.reverse = false;
+ this.$scope.setValidState(true);
+ this.$scope.artifactType = 'normal';
+ this.$scope.getTitle = ():string => {
+ return this.$filter("resourceName")(this.$scope.component.name) + ' Artifacts';
+
+ };
+
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'artifactDisplayName'},
+ {title: 'Type', property: 'artifactType'}
+ ];
+
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.artifacts);
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+
+ this.$scope.addOrUpdate = (artifact:Models.ArtifactModel):void => {
+ artifact.artifactGroupType = 'INFORMATIONAL';
+ this.ModalsHandler.openWizardArtifactModal(artifact, this.$scope.component).then(() => {
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.artifacts);
+ });
+ };
+
+ this.$scope.showNoArtifactMessage = ():boolean => {
+ let artifacts:any = [];
+ artifacts = _.filter(this.$scope.artifacts, (artifact:Models.ArtifactModel)=> {
+ return artifact.esId;
+ });
+
+ if (artifacts.length === 0) {
+ return true;
+ }
+ return false;
+ };
+
+ this.$scope.delete = (artifact:Models.ArtifactModel):void => {
+
+ let onOk = ():void => {
+ this.$scope.isLoading = true;
+ let onSuccess = ():void => {
+ this.$scope.isLoading = false;
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.artifacts);
+ };
+
+ let onFailed = (error:any):void => {
+ console.log('Delete artifact returned error:', error);
+ this.$scope.isLoading = false;
+ };
+
+ this.$scope.component.deleteArtifact(artifact.uniqueId, artifact.artifactLabel).then(onSuccess, onFailed);
+ };
+
+ let title:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("ARTIFACT_VIEW_DELETE_MODAL_TEXT", "{'name': '" + artifact.artifactDisplayName + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+
+ this.$scope.clickArtifactName = (artifact:any) => {
+ if (!artifact.esId) {
+ this.$scope.addOrUpdate(artifact);
+ }
+
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html
new file mode 100644
index 0000000000..790117b2fd
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts-view.html
@@ -0,0 +1,57 @@
+<div class="workspace-information-artifact">
+ <div data-tests-id="add-information-artifact-button" ng-if="!isViewMode()"
+ data-ng-class="{'disabled': isDisableMode()}"
+ data-tests-id="addGrey" class="add-btn" data-ng-click="addOrUpdate({})" type="button">Add </div>
+ <div class="table-container-flex">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-show="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="showNoArtifactMessage()" class="no-row-text" data-ng-class="{'disabled': isDisableMode()}">
+ There are no information artifacts to display
+ </div>
+ <div data-ng-repeat-start="artifact in artifacts| orderBy:sortBy:reverse track by $index" data-tests-id="InformationalArtifactRow"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected}"
+ data-ng-if="artifact.esId">
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected">
+ <span class="sprite table-arrow" data-ng-class="{'opened': artifact.selected}" data-tests-id="{{artifact.artifactDisplayName}}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+
+ <div class="table-btn-col flex-item">
+ <button class="table-edit-btn" data-tests-id="edit_{{artifact.artifactDisplayName}}" data-ng-if="!isViewMode() && !artifact.isThirdParty()" data-ng-click="addOrUpdate(artifact)" data-ng-class="{'disabled': isDisableMode()}"></button>
+ <button class="table-delete-btn" data-tests-id="delete_{{artifact.artifactDisplayName}}" data-ng-if="!isViewMode() && !artifact.isThirdParty()" data-ng-click="delete(artifact)" data-ng-class="{'disabled': isDisableMode()}"> </button>
+ <button class="table-download-btn" download-artifact data-tests-id="download_{{artifact.artifactDisplayName}}"
+ data-ng-if="artifact.artifactName" component="component" artifact="artifact"></button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected" class="item-opened" data-tests-id="{{artifact.artifactDisplayName}}Description" data-ng-bind="artifact.description"></div>
+ <button class="add-button" data-ng-repeat="artifact in artifacts track by $index"
+ data-ng-show="!artifact.esId"
+ data-tests-id="{{artifact.artifactDisplayName}}"
+ ng-if="!isViewMode()"
+ data-ng-class="{'disabled': isDisableMode()}"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_HEAT"
+ translate-values="{'name': '{{artifact.artifactDisplayName}}'}"
+ data-ng-click="addOrUpdate(artifact)"></button>
+ <button class="add-button"
+ ng-if="!isViewMode()"
+ data-ng-class="{'disabled': isDisableMode()}"
+ translate="DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER"
+ data-ng-click="addOrUpdate({})"></button>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts.less b/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts.less
new file mode 100644
index 0000000000..d3fe14d945
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/information-artifacts/information-artifacts.less
@@ -0,0 +1,47 @@
+.workspace-information-artifact {
+ width: 93%;
+ display: inline-block;
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table{
+ height: 490px;
+ margin-bottom: 0;
+ }
+
+ .table-container-flex {
+ margin-top: 27px;
+
+ .item-opened{
+ word-wrap: break-word;
+ }
+
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 3;
+ padding-top: 10px;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 1;
+ }
+
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/inputs.less b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/inputs.less
new file mode 100644
index 0000000000..76a82c69ee
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/inputs.less
@@ -0,0 +1,286 @@
+.workspace-inputs {
+
+ .sdc-workspace-container .w-sdc-main-right-container .w-sdc-main-container-body-content {
+ padding: 25px 8% 0px 8%;
+ }
+
+ width: 100%;
+ display: flex;
+
+ .text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding-left: 15px;
+ }
+
+ .title-text {
+ color: @main_color_m;
+ .f-type._13_m;
+ .bold;
+ }
+
+ .title-blue-text {
+ color: @main_color_a;
+ .f-type._13_m;
+
+ &.property-name-text {
+ padding-right: 13px;
+ border-right: 1px solid rgba(120, 136, 148, 0.26);
+ flex-grow: 1;
+ }
+ }
+
+ .instance-name-text {
+ flex-grow: 1;
+ }
+
+ ng-include {
+ width: 45%;
+ }
+
+ .w-sdc-inputs-search {
+ padding: 10px 20px 20px 0;
+ white-space: nowrap;
+ position: relative;
+ width: 60%;
+ height: 64px;
+
+ .inputs-search-icon {
+ top: 9px;
+ right: 11px;
+ }
+
+ .magnification-white {
+ .sprite-new;
+ .search-white-icon;
+ .hand;
+ }
+
+ .search-icon-container {
+ width: 35px;
+ height: 30px;
+ background-color: @main_color_a;
+ white-space: nowrap;
+ float: right;
+ position: relative;
+ bottom: 31px;
+ right: 1px;
+ border-radius: 0px 4px 4px 0px;
+ .hand
+ }
+ }
+
+ .total-inputs-count {
+ width: 100%;
+ font-weight: bold;
+ text-align: left;
+ }
+
+ .new-input-button {
+ margin: 9px 0 0 0;
+ }
+
+ .w-sdc-inputs-search-input {
+ border: 1px solid @color_e;
+ .border-radius(4px);
+ height: 32px;
+ margin: 0;
+ padding: 0px 28px 3px 10px;
+ vertical-align: 4px;
+ width: 100%;
+ outline: none;
+ font-style: italic;
+ }
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .prop-to-input-button {
+ position: absolute;
+ top: 50%;
+ margin-right: -20px;
+ margin-bottom: -10px;
+
+ }
+
+ .property-row {
+ border-bottom: 1px solid @border_color_d;
+ padding-left: 10px;
+ .property-name-container {
+ display: flex;
+ flex-grow: 4;
+ }
+
+ .type-schema-container {
+ flex-grow: 1;
+ border-left: 1px solid @border_color_d;
+ text-align: left;
+ line-height: 30px;
+ text-transform: capitalize;
+ width: 10px;
+ }
+
+ }
+
+ .table-container-flex {
+
+ .flex-item {
+ line-height: 22px;
+ }
+ .expand-collapse-table-row {
+
+ &.expanded {
+ .flex-container {
+ .expand-collapse-inputs-table-icon {
+ transform: rotate(180deg);
+ left: 0px;
+ }
+ }
+ }
+
+ .data-row {
+ background: @tlv_color_u;
+ .hand;
+ align-items: center;
+ padding: 0 12px;
+ margin: 1px 0px 1px 0px;
+ min-height: 40px;
+ }
+
+ .data-row:hover {
+ .bg_j;
+ }
+
+ .input-row {
+ padding-left: 45px;
+ background: @tlv_color_t;
+ border: @main_color_o solid 1px;
+ align-items: center;
+ .hand;
+ margin: 1px 0px 1px 0px;
+
+ &.service-input-row {
+ background: @tlv_color_u;
+ &.new-input {
+ background: @tlv_color_v;
+ }
+ }
+ &.selected {
+ background-color: @tlv_color_v;
+ }
+ .flex-item {
+ min-height: 60px;
+ padding: 0 15px;
+ }
+ .input-check-box {
+ padding-right: 10px;
+ margin-top: 9px;
+ }
+ &>.title-text{
+ text-align: start;
+ padding: 4px 0 0 35px;
+ }
+
+ .expand-collapse-inputs-table-icon{
+ margin-top: 15px;
+ }
+
+ }
+
+ .input-row:hover {
+ .bg_j;
+ }
+
+
+ }
+ }
+
+ .table {
+ height: 640px;
+ margin-bottom: 0;
+ clear: both;
+
+ .empty-row {
+ padding: 3px;
+ }
+
+ .flex-item {
+ line-height: 22px;
+ }
+
+ .table-header {
+
+ line-height: 14px;
+ background-color: @main_color_a;
+ color: @main_color_p;
+ text-align: left;
+ padding: 7px 5px 7px 10px;
+ .f-type._14_m;
+ }
+ .head {
+ background-color: #e6e6e6;
+ }
+
+ .property-row:hover{
+ background-color: @func_color_r;
+ }
+
+ .body {
+ .scrollbar-container {
+ .perfect-scrollbar;
+ max-height: 610px;
+ }
+
+ .expand-collapse-inputs-table-icon {
+ .hand;
+ .sprite-new;
+ .arrow-up;
+ transition: .3s all;
+ position: relative;
+ left: 8px;
+ border: none !important;
+ padding: 0px 10px 0px 10px;
+ }
+
+ .table-col-text {
+ margin-left: 14px;
+ }
+ }
+ }
+
+ .inputs-header {
+ width: 100%;
+ position: relative;
+ bottom: 31px;
+ }
+
+ .inputs-tables-container {
+ width: 100%;
+ min-width: 100%;
+ display: flex;
+ }
+
+ .inputs-button-container {
+ width: 8%;
+ min-width: 8%;
+ display: flex;
+
+ .right-arrow-btn {
+ .sprite-new;
+ .blue-right-arrow-circle;
+ margin: auto;
+ cursor: pointer;
+ }
+ }
+
+ .table-container-flex {
+ margin-top: 27px;
+ width: 46%;
+ min-width: 46%;
+ display: inline-block;
+ float: left;
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts
new file mode 100644
index 0000000000..2dc1b1d9ff
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts
@@ -0,0 +1,145 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import Dictionary = Sdc.Utils.Dictionary;
+ import InputModel = Sdc.Models.InputModel;
+
+ export interface IInputsViewModelScope extends IWorkspaceViewModelScope {
+ InstanceInputsProperties:Models.InstanceInputsPropertiesMapData; //this is tha map object that hold the selected inputs and the inputs we already used
+ vfInstancesList: Array<Models.ComponentsInstances.ComponentInstance>;
+ component:Models.Components.Resource;
+
+ onArrowPressed():void;
+ getInputPropertiesForInstance(instanceId:string, instance:Models.ComponentsInstances.ComponentInstance): ng.IPromise<boolean> ;
+ loadInputPropertiesForInstance(instanceId:string, input:Models.InputModel): ng.IPromise<boolean> ;
+ loadInputInputs(input:Models.InputModel): ng.IPromise<boolean>;
+ }
+
+ export class ResourceInputsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$q'
+ ];
+
+ constructor(private $scope:IInputsViewModelScope, private $q: ng.IQService) {
+ this.initScope();
+ }
+
+ private initScope = (): void => {
+
+ this.$scope.InstanceInputsProperties = new Models.InstanceInputsPropertiesMapData();
+ this.$scope.vfInstancesList = this.$scope.component.componentInstances;
+
+ // Need to cast all inputs to InputModel for the search to work
+ let tmpInputs:Array<Models.InputModel> = new Array<Models.InputModel>();
+ _.each(this.$scope.component.inputs, (input):void => {
+ tmpInputs.push(new Models.InputModel(input));
+ });
+ this.$scope.component.inputs = tmpInputs;
+ // This function is not supported for resource
+ //this.$scope.component.getComponentInputs();
+
+ /*
+ * When clicking on instance input in the left or right table, this function will load all properties of the selected input
+ */
+ this.$scope.getInputPropertiesForInstance = (instanceId:string, instance:Models.ComponentsInstances.ComponentInstance): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+ instance.properties = this.$scope.component.componentInstancesProperties[instanceId];
+ deferred.resolve(true);
+ return deferred.promise;
+ };
+
+ /*
+ * When clicking on input in the right table, this function will load all inputs of the selected input
+ */
+ this.$scope.loadInputInputs = (input:Models.InputModel): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+
+ let onSuccess = () => { deferred.resolve(true); };
+ let onError = () => { deferred.resolve(false); };
+
+ if(!input.inputs) {
+ this.$scope.component.getResourceInputInputs(input.uniqueId).then(onSuccess, onError);
+ } else {
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ };
+
+ /*
+ * When clicking on instance input in the left or right table, this function will load all properties of the selected input
+ */
+ this.$scope.loadInputPropertiesForInstance = (instanceId:string, input:Models.InputModel): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+
+ let onSuccess = (properties:Array<Models.PropertyModel>) => {
+ input.properties = properties;
+ deferred.resolve(true);
+ };
+
+ let onError = () => {
+ deferred.resolve(false)
+ };
+
+ if(!input.properties) {
+ this.$scope.component.getComponentInstanceInputProperties(instanceId, input.uniqueId).then(onSuccess, onError);
+ } else {
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ };
+
+ /*
+ * When pressing the arrow, we create service inputs from the inputs selected
+ */
+ this.$scope.onArrowPressed = ():void => {
+ let onSuccess = (inputsCreated: Array<Models.InputModel>) => {
+
+ //disabled all the inputs in the left table
+ _.forEach(this.$scope.InstanceInputsProperties, (properties:Array<Models.PropertyModel>) => {
+ _.forEach(properties, (property:Models.PropertyModel) => {
+ property.isAlreadySelected = true;
+ });
+ });
+
+ // Adding color to the new inputs (right table)
+ _.forEach(inputsCreated, (input) => {
+ input.isNew = true;
+ });
+
+ // Removing color to the new inputs (right table)
+ setTimeout(() => {
+ _.forEach(inputsCreated, (input) => {
+ input.isNew = false;
+ });
+ this.$scope.$apply();
+ }, 3000);
+ };
+
+ this.$scope.component.createInputsFormInstances(this.$scope.InstanceInputsProperties).then(onSuccess);
+ };
+
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html
new file mode 100644
index 0000000000..7cdf5a2fa4
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view.html
@@ -0,0 +1,136 @@
+<div class="workspace-inputs">
+ <div class="table-container-flex">
+ <div class="w-sdc-inputs-search pull-left hideme">
+ <input type="text" class="w-sdc-inputs-search-input" placeholder="Search"/>
+ <div class="search-icon-container">
+ <span class="w-sdc-search-icon inputs-search-icon magnification-white"></span>
+ </div>
+ </div>
+ <div class="table">
+ <div class="table-header">VFC instances inputs</div>
+ <div class="body">
+ <div class="table-loader" ng-class="{'tlv-loader large loader': isLoading}"></div>
+ <perfect-scrollbar scroll-y-margin-offset="0" class="scrollbar-container">
+
+ <expand-collapse expanded-selector=".vf-instance-list.{{$index}}"
+ class="expand-collapse-table-row"
+ load-data-function="getInputPropertiesForInstance(instance.uniqueId, instance)"
+ is-close-on-init="true"
+ data-ng-repeat-start="instance in vfInstancesList track by $index">
+ <div class="flex-container data-row">
+ <div class="expand-collapse-inputs-table-icon"></div>
+ <div class="table-col-general flex-item text">
+ <span class="title-text">{{instance.name}}</span>
+ </div>
+ </div>
+
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="vf-instance-list {{$index}}">
+
+ <div class="empty-row" ng-if="instance.properties.length===0">No properties to display</div>
+
+ <div ng-repeat="property in instance.properties track by $index">
+ <div class="property-row flex-container">
+ <div class="flex-item text property-name-container">
+ <span class="title-blue-text property-name-text">{{property.name}}</span>
+ <span class="text instance-name-text">{{property.name}}</span>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span>{{property.type}}</span>
+ </div>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span>{{property.schema.property.type}} </span>
+ </div>
+ </div>
+ <!--<sdc-checkbox
+ class="type-schema-container input-check-box"
+ disabled ="property.isAlreadySelected"
+ sdc-checklist-model="InstanceInputsProperties[instance.uniqueId]"
+ sdc-checklist-value="property"
+ data-ng-click="$event.stopPropagation()"></sdc-checkbox>-->
+ </div>
+ </div>
+
+ </div>
+
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+
+ <div class="inputs-button-container pull-left">
+ <!--<div ng-click="onArrowPressed()" class="right-arrow-btn"></div>-->
+ </div>
+
+ <div class="table-container-flex">
+ <div class="w-sdc-inputs-search pull-left">
+ <input type="text" class="w-sdc-inputs-search-input" data-ng-model="search.filterTerm" placeholder="Search" data-ng-model-options="{debounce: 200}"/>
+ <div class="search-icon-container">
+ <span class="w-sdc-search-icon inputs-search-icon magnification-white"></span>
+ </div>
+ </div>
+ <div class="table">
+ <div class="body">
+ <div class="table-header">Resource instance inputs</div>
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <expand-collapse expanded-selector=".resource-inputs.{{$index}}"
+ class="expand-collapse-table-row"
+ load-data-function="loadInputPropertiesForInstance(resourceInput.uniqueId, resourceInput)"
+ is-close-on-init="true"
+ data-ng-repeat-start="resourceInput in component.inputs | filter:search track by $index ">
+ <div class="input-row service-input-row">
+ <div class="title-text">{{resourceInput.name}}</div>
+ <div class="flex-container" ng-class="resourceInput.isNew ? 'new-input': 'service-input-row'">
+ <div class="expand-collapse-inputs-table-icon"></div>
+ <div class="flex-item">
+ <div>
+ <span class="title-text">Description:</span>
+ <span>{{resourceInput.description}}</span>
+ </div>
+ </div>
+ <div class="flex-item ">
+ <div class="text">
+ <span class="title-text">VF Instance:</span>
+ <span>{{resourceInput.name}}</span>
+ </div>
+ <div class="text">
+ <span class="title-text">Type:</span>
+ <span>{{resourceInput.type}} </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="input-inputs-list resource-inputs {{$index}}">
+ <div class="empty-row" ng-if="resourceInput.properties.length===0">No properties to display</div>
+ <div ng-repeat="property in resourceInput.properties track by $index">
+ <div class="property-row flex-container">
+ <div class="flex-item text property-name-container">
+ <span
+ class="title-blue-text property-name-text">{{property.name}}</span>
+ <span class="text instance-name-text">{{property.name}}</span>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span>{{property.type}}</span>
+ </div>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span>{{property.schema.property.type}} </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less
new file mode 100644
index 0000000000..ebb32fbdb2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less
@@ -0,0 +1,9 @@
+.workspace-inputs {
+
+ .property-row {
+ .input-check-box {
+ text-align: center;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts
new file mode 100644
index 0000000000..6c8391720a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts
@@ -0,0 +1,246 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import IAngularEvent = angular.IAngularEvent;
+ import ComponentInstance = Sdc.Models.ComponentsInstances.ComponentInstance;
+
+
+ interface IServiceInputsViewModelScope extends IWorkspaceViewModelScope {
+
+ vfInstancesList: Array<ComponentInstance>;
+ selectedInputs:Array<Models.InputModel>;
+ instanceInputsMap:Models.InstancesInputsMapData; //this is tha map object that hold the selected inputs and the inputs we already used
+ component:Models.Components.Service;
+ sdcMenu:Models.IAppMenu;
+
+ onArrowPressed():void;
+ loadComponentInputs(): void;
+ loadInstanceInputs(instance:ComponentInstance): ng.IPromise<boolean> ;
+ loadInputPropertiesForInstance(instanceId:string, input:Models.InputModel): ng.IPromise<boolean> ;
+ loadInputInputs(input:Models.InputModel): ng.IPromise<boolean>;
+ deleteInput(input:Models.InputModel):void
+ }
+
+ export class ServiceInputsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$q',
+ 'ModalsHandler'
+ ];
+
+ constructor(private $scope:IServiceInputsViewModelScope,
+ private $q: ng.IQService,
+ private ModalsHandler: Sdc.Utils.ModalsHandler) {
+ this.initScope();
+ }
+
+ /*
+ * When loading the screen again, we need to disabled the inputs that already created on the service,
+ * we do that by comparing the service input name, to the instance name + '_' + the resource instance input name.
+ */
+ private disableEnableSelectedInputs = (instance: ComponentInstance): void => {
+
+ let alreadySelectedInput = new Array<Models.InputModel>();
+ _.forEach(instance.inputs, (input:Models.InputModel) => {
+ let expectedServiceInputName = instance.normalizedName + '_' + input.name;
+ let inputAlreadyInService: Models.InputModel = _.find(this.$scope.component.inputs, (serviceInput: Models.InputModel) => {
+ return serviceInput.name === expectedServiceInputName;
+ });
+ if(inputAlreadyInService) {
+ input.isAlreadySelected = true;
+ alreadySelectedInput.push(input);
+ } else {
+ input.isAlreadySelected = false;
+ }
+ });
+ this.$scope.instanceInputsMap[instance.uniqueId] = alreadySelectedInput;
+ };
+
+ private initScope = (): void => {
+
+ this.$scope.instanceInputsMap = new Models.InstancesInputsMapData();
+ this.$scope.isLoading = true;
+ this.$scope.selectedInputs = new Array<Models.InputModel>();
+
+ // Why do we need this? we call this later.
+ //this.$scope.component.getComponentInputs();
+
+ let onSuccess = (componentInstances:Array<ComponentInstance>) => {
+ console.log("component instances loaded: ", componentInstances);
+ this.$scope.vfInstancesList = componentInstances;
+ this.$scope.isLoading = false;
+ };
+
+ //This function will get al component instance for the left table - in future the instances will be filter according to search text
+ this.$scope.component.getComponentInstancesFilteredByInputsAndProperties().then(onSuccess);
+
+ // This function will get the service inputs for the right table
+ this.$scope.component.getComponentInputs();
+
+
+ /*
+ * When clicking on instance in the left table, this function will load all instance inputs
+ */
+ this.$scope.loadInstanceInputs = (instance:ComponentInstance): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+
+ let onSuccess = (inputs:Array<Models.InputModel>) => {
+ instance.inputs = inputs;
+ this.disableEnableSelectedInputs(instance);
+ deferred.resolve(true);
+ };
+
+ let onError = () => {
+ deferred.resolve(false);
+ };
+
+ if(!instance.inputs) {
+ this.$scope.component.getComponentInstanceInputs(instance.uniqueId, instance.componentUid).then(onSuccess, onError);
+ this.disableEnableSelectedInputs(instance);
+ } else {
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ };
+
+ /*
+ * When clicking on instance input in the left or right table, this function will load all properties of the selected input
+ */
+ this.$scope.loadInputPropertiesForInstance = (instanceId:string, input:Models.InputModel): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+
+ let onSuccess = (properties:Array<Models.PropertyModel>) => {
+ input.properties = properties;
+ deferred.resolve(true);
+ };
+
+ let onError = () => {
+ deferred.resolve(false)
+ };
+
+ if(!input.properties) {
+ this.$scope.component.getComponentInstanceInputProperties(instanceId, input.uniqueId).then(onSuccess, onError);
+ } else {
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ };
+
+ /*
+ * When clicking on input in the right table, this function will load all inputs of the selected input
+ */
+ this.$scope.loadInputInputs = (input:Models.InputModel): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+
+ let onSuccess = () => { deferred.resolve(true); };
+ let onError = () => { deferred.resolve(false); };
+
+ if(!input.inputs) { // Caching, if exists do not get it.
+ this.$scope.component.getServiceInputInputs(input.uniqueId).then(onSuccess, onError);
+ } else {
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ };
+
+ /*
+ * When pressing the arrow, we create service inputs from the inputs selected
+ */
+ this.$scope.onArrowPressed = ():void => {
+ let onSuccess = (inputsCreated: Array<Models.InputModel>) => {
+
+ //disabled all the inputs in the left table
+ _.forEach(this.$scope.instanceInputsMap, (inputs:Array<Models.InputModel>, instanceId:string) => {
+ _.forEach(inputs, (input:Models.InputModel) => {
+ input.isAlreadySelected = true;
+ });
+ });
+
+ this.addColorToItems(inputsCreated);
+ };
+
+ this.$scope.component.createInputsFormInstances(this.$scope.instanceInputsMap).then(onSuccess);
+ };
+
+ this.$scope.deleteInput = (input: Models.InputModel):void => {
+
+ var onDelete = ():void => {
+ var onSuccess = (deletedInput: Models.InputModel, componentInstanceId:string):void => {
+ // Remove from component.inputs the deleted input (service inputs)
+ var remainingServiceInputs:Array<Models.InputModel> = _.filter(this.$scope.component.inputs, (input:Models.InputModel):boolean => {
+ return input.uniqueId !== deletedInput.uniqueId;
+ });
+ this.$scope.component.inputs = remainingServiceInputs;
+
+ // Find the instance that contains the deleted input, and set disable|enable the deleted input
+ var deletedInputComponentInstance:ComponentInstance = _.find(this.$scope.vfInstancesList, (instanceWithChildToDelete:ComponentInstance):boolean => {
+ return instanceWithChildToDelete.uniqueId === componentInstanceId;
+ });
+ this.disableEnableSelectedInputs(deletedInputComponentInstance);
+ };
+
+ var onFailed = (error:any) : void => {
+ console.log("Error deleting input");
+ };
+
+ this.addColorToItems([input]);
+
+ // Get service inputs of input (so after delete we will know the component instance)
+ this.$scope.loadInputInputs(input).then((result:boolean):void=>{
+ if (result && input.inputs.length>0) {
+ var componentInstanceId:string = input.inputs[0].componentInstanceId;
+ this.$scope.component.deleteServiceInput(input.uniqueId).then((deletedInput: Models.InputModel):void => {
+ onSuccess(deletedInput, componentInstanceId);
+ }, onFailed);
+ }
+ });
+ };
+
+ // Get confirmation modal text from menu.json
+ var state = "deleteInput";
+ var title:string = this.$scope.sdcMenu.alertMessages[state].title;
+ var message:string = this.$scope.sdcMenu.alertMessages[state].message.format([input.name]);
+
+ // Open confirmation modal
+ this.ModalsHandler.openAlertModal(title, message).then(onDelete);
+ }
+ };
+
+ private addColorToItems = (inputsCreated:Array<Models.InputModel>):void => {
+
+ // Adding color to the new inputs (right table)
+ _.forEach(inputsCreated, (input) => {
+ input.isNew = true;
+ });
+
+ // Removing color to the new inputs (right table)
+ setTimeout(() => {
+ _.forEach(inputsCreated, (input) => {
+ input.isNew = false;
+ });
+ this.$scope.$apply();
+ }, 3000);
+ };
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html
new file mode 100644
index 0000000000..bf15a70322
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs-view.html
@@ -0,0 +1,205 @@
+<div class="workspace-inputs">
+ <div class="table-container-flex">
+ <div class="w-sdc-inputs-search pull-left hideme">
+ <input type="text" class="w-sdc-inputs-search-input" placeholder="Search"/>
+ <div class="search-icon-container">
+ <span class="w-sdc-search-icon inputs-search-icon magnification-white"></span>
+ </div>
+ </div>
+ <div class="table">
+ <div class="table-header">Resource instance inputs</div>
+ <div class="body">
+ <div class="table-loader" ng-class="{'tlv-loader large loader': isLoading}"></div>
+ <perfect-scrollbar scroll-y-margin-offset="0" class="scrollbar-container">
+
+ <expand-collapse expanded-selector=".vf-instance-list.{{$index}}"
+ class="expand-collapse-table-row"
+ load-data-function="loadInstanceInputs(instance)"
+ is-close-on-init="true"
+ data-ng-repeat-start="instance in vfInstancesList track by $index">
+ <div class="flex-container data-row">
+ <div class="expand-collapse-inputs-table-icon"></div>
+ <div class="table-col-general flex-item text" data-tests-id="inputs-vf-instance-{{$index}}">
+ <span class="title-text">{{instance.name}}</span>
+ </div>
+ </div>
+
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="vf-instance-list {{$index}}">
+
+ <expand-collapse expanded-selector=".input-list.{{$parent.$index}}-{{$index}}"
+ class="expand-collapse-table-row"
+ load-data-function="loadInputPropertiesForInstance(instance.uniqueId, input)"
+ is-close-on-init="true"
+ data-ng-repeat-start="input in instance.inputs track by $index">
+ <div class="input-row" ng-class="{'selected': selectedInput.uniqueId === input.uniqueId}">
+ <div class="title-text">{{input.name}}</div>
+ <div class="flex-container">
+ <div class="expand-collapse-inputs-table-icon"></div>
+ <div class="flex-item">
+
+ <div>
+ <span class="title-text">Description:</span>
+ <span tooltips tooltip-content="{{input.description}}">{{input.description}}</span>
+ </div>
+ </div>
+ <div class="flex-item ">
+ <div class="text">
+ <span class="title-text">VF Instance:</span>
+ <span tooltips tooltip-content="{{instance.name}}">{{instance.name}}</span>
+ </div>
+ <div class="text">
+ <span class="title-text">Type:</span>
+ <span tooltips tooltip-content="{{input.type}}">{{input.type}} </span>
+ </div>
+ </div>
+ <sdc-checkbox
+ class="input-check-box"
+ disabled ="input.isAlreadySelected || isViewMode()"
+ sdc-checklist-model="instanceInputsMap[instance.uniqueId]"
+ sdc-checklist-value="input"
+ data-tests-id="inputs-checkbox-{{$index}}"
+ data-ng-click=" $event.stopPropagation()"></sdc-checkbox>
+ </div>
+ </div>
+
+
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="input-list {{$parent.$index}}-{{$index}}">
+ <div class="empty-row" ng-if="input.properties.length===0">No properties to display</div>
+
+ <div ng-repeat="property in input.properties track by $index">
+ <div class="property-row flex-container">
+ <div class="flex-item text property-name-container">
+ <span class="title-blue-text property-name-text" tooltips tooltip-content="{{property.name}}">{{property.name}}</span>
+ <span class="text instance-name-text" tooltips tooltip-content="{{property.name}}">{{property.name}}</span>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span tooltips tooltip-content="{{property.type}}">{{property.type}}</span>
+ </div>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span tooltips tooltip-content="{{property.schema.property.type}}">{{property.schema.property.type}} </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+
+ <div class="inputs-button-container pull-left">
+ <div ng-click="onArrowPressed()" class="right-arrow-btn" data-tests-id="add-inputs-to-service-button"></div>
+ </div>
+
+ <div class="table-container-flex">
+ <div class="w-sdc-inputs-search pull-left">
+ <input type="text" class="w-sdc-inputs-search-input" data-ng-model="search.filterTerm" placeholder="Search" data-ng-model-options="{debounce: 200}"/>
+ <div class="search-icon-container">
+ <span class="w-sdc-search-icon inputs-search-icon magnification-white"></span>
+ </div>
+ </div>
+ <div class="table">
+ <div class="body">
+ <div class="table-header">Service Inputs</div>
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <expand-collapse expanded-selector=".service-inputs.{{$index}}"
+ class="expand-collapse-table-row"
+ load-data-function="loadInputInputs(serviceInput)"
+ is-close-on-init="true"
+ data-ng-repeat-start="serviceInput in component.inputs | filter:search track by $index ">
+ <div class="input-row service-input-row " data-tests-id="service-input-{{$index}}" ng-class="serviceInput.isNew ? 'new-input': ''">
+ <div class="title-text">{{serviceInput.name}}</div>
+ <div class="flex-container">
+ <div class="expand-collapse-inputs-table-icon"></div>
+ <div class="flex-item">
+ <div>
+ <span class="title-text">Description:</span>
+ <span tooltips tooltip-content="{{serviceInput.description}}">{{serviceInput.description}}</span>
+ </div>
+ </div>
+ <div class="flex-item ">
+ <div class="text">
+ <span class="title-text">VF Instance:</span>
+ <span tooltips tooltip-content="{{serviceInput.name}}">{{serviceInput.name}}</span>
+ </div>
+ <div class="text">
+ <span class="title-text">Type:</span>
+ <span tooltips tooltip-content="{{serviceInput.type}}">{{serviceInput.type}} </span>
+ </div>
+ </div>
+ <div class="delete">
+ <span class="sprite-new delete-icon remove-input-icon"
+ data-ng-class="{'disabled': isViewMode()}"
+ data-ng-click="deleteInput(serviceInput); $event.stopPropagation();"
+ data-tests-id="delete-input-{{$index}}"></span>
+ </div>
+ </div>
+ </div>
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="service-inputs {{$index}}">
+ <expand-collapse expanded-selector=".input-inputs-list.{{$parent.$index}}-{{$index}}"
+ class="expand-collapse-table-row"
+ load-data-function="loadInputPropertiesForInstance(input.componentInstanceId, input)"
+ is-close-on-init="true"
+ data-ng-repeat-start="input in serviceInput.inputs track by $index">
+ <div class="input-row">
+ <div class="title-text">{{input.name}}</div>
+ <div class="flex-container">
+ <div class="expand-collapse-inputs-table-icon"></div>
+ <div class="flex-item">
+ <div>
+ <span class="title-text">Description:</span>
+ <span tooltips tooltip-content="{{input.description}}">{{input.description}}</span>
+ </div>
+ </div>
+ <div class="flex-item ">
+ <div class="text">
+ <span class="title-text">VF Instance:</span>
+ <span tooltips tooltip-content="{{instance.componentInstanceName}}">{{instance.componentInstanceName}}</span>
+ </div>
+ <div class="text">
+ <span class="title-text">Type:</span>
+ <span tooltips tooltip-content="{{input.type}}">{{input.type}} </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </expand-collapse>
+
+ <div data-ng-repeat-end="" class="input-inputs-list {{$parent.$index}}-{{$index}}">
+ <div class="empty-row" ng-if="input.properties.length===0">No properties to display</div>
+ <div ng-repeat="property in input.properties track by $index">
+ <div class="property-row flex-container">
+ <div class="flex-item text property-name-container">
+ <span
+ class="title-blue-text property-name-text" tooltips tooltips-content="{{property.name}}">{{property.name}}</span>
+ <span class="text instance-name-text" tooltips tooltips-content="{{property.name}}">{{property.name}}</span>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span tooltips tooltips-content="{{property.type}}">{{property.type}}</span>
+ </div>
+ </div>
+ <div class="type-schema-container">
+ <div class="text">
+ <span tooltips tooltips-content="{{property.schema.property.type}}">{{property.schema.property.type}} </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs.less b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs.less
new file mode 100644
index 0000000000..11e613b56e
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/inputs/service-input/service-inputs.less
@@ -0,0 +1,54 @@
+.workspace-inputs {
+
+ .service-inputs-view {
+
+ .table-container-flex {
+ width:100% !important;
+ }
+
+ .table-loader {
+ position: relative;
+ top:215px;
+ }
+
+ }
+
+ .infinite-scroll {
+
+ overflow-y: scroll;
+ overflow-x: hidden;
+ max-height: 400px;
+ }
+
+ .class_with_css_props_leading_to_a_scroll {
+ height: 100%;
+ overflow-y: auto;
+ }
+
+ .table-container-flex {
+ .expand-collapse-table-row {
+ .service-input-row {
+ padding-left: 15px;
+ border: none;
+ border-bottom: rgba(120, 136, 148, 0.26) solid 1px;
+
+ .delete {
+ width: 50px;
+ padding: 0;
+ position: relative;
+ }
+
+ .remove-input-icon {
+ position: absolute;
+ top: 12px;
+ right: 18px;
+ }
+
+ .remove-input-icon:hover {
+ .delete-icon-hover;
+ }
+ }
+ }
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts
new file mode 100644
index 0000000000..2fab118378
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view-model.ts
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IManagementWorkflowViewModelScope extends IWorkspaceViewModelScope {
+ vendorModel:VendorModel;
+ }
+
+ export class VendorModel {
+ artifacts: Models.ArtifactGroupModel;
+ serviceID: string;
+ readonly: boolean;
+ sessionID: string;
+ requestID: string;
+ diagramType: string;
+ participants:Array<participant>;
+
+ constructor(artifacts: Models.ArtifactGroupModel, serviceID:string, readonly:boolean, sessionID:string,
+ requestID:string, diagramType:string, participants:Array<participant>){
+ this.artifacts = artifacts;
+ this.serviceID = serviceID;
+ this.readonly = readonly;
+ this.sessionID = sessionID;
+ this.requestID = requestID;
+ this.diagramType = diagramType;
+ this.participants = participants;
+ }
+ }
+
+ export class ManagementWorkflowViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'uuid4'
+ ];
+
+ constructor(private $scope:IManagementWorkflowViewModelScope,
+ private uuid4:any) {
+
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+
+ private static getParticipants():Array<participant> {
+ return [
+ {
+ "id": "1",
+ "name": "Customer"},
+ {
+ "id": "2",
+ "name": "CCD"
+ },
+ {
+ "id": "3",
+ "name": "Infrastructure"
+ },
+ {
+ "id": "4",
+ "name": "MSO"
+ },
+ {
+ "id": "5",
+ "name": "SDN-C"
+ },
+ {
+ "id": "6",
+ "name": "A&AI"
+ },
+ {
+ "id": "7",
+ "name": "APP-C"
+ },
+ {
+ "id": "8",
+ "name": "Cloud"
+ },
+ {
+ "id": "9",
+ "name": "DCAE"
+ },
+ {
+ "id": "10",
+ "name": "ALTS"
+ },
+ {
+ "id": "11",
+ "name": "VF"
+ }
+ ]
+ }
+
+
+ private initScope():void {
+ this.$scope.vendorModel = new VendorModel(
+ this.$scope.component.artifacts.filteredByType(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES.WORKFLOW),
+ this.$scope.component.uniqueId,
+ this.$scope.isViewMode(),
+ this.$scope.user.userId,
+ this.uuid4.generate(),
+ Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES.WORKFLOW,
+ ManagementWorkflowViewModel.getParticipants()
+ );
+
+ this.$scope.thirdParty = true;
+ this.$scope.setValidState(true);
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view.html
new file mode 100644
index 0000000000..bd196daec8
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/management-workflow/management-workflow-view.html
@@ -0,0 +1,3 @@
+<div class="workspace-management-workflow">
+ <punch-out name="'sequence-diagram'" data="vendorModel" user="user" on-event="onVendorEvent"></punch-out>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts
new file mode 100644
index 0000000000..064f1c5896
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts
@@ -0,0 +1,80 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface INetworkCallFlowViewModelScope extends IWorkspaceViewModelScope {
+ vendorMessageModel:VendorModel;
+ }
+
+ export class participant {
+ name:string;
+ id:string;
+
+ constructor(instance:Models.ComponentsInstances.ComponentInstance){
+ this.name = instance.name;
+ this.id = instance.uniqueId;
+ }
+ }
+
+
+ export class NetworkCallFlowViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'uuid4'
+ ];
+
+ constructor(private $scope:INetworkCallFlowViewModelScope,
+ private uuid4:any) {
+
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+ private getVFParticipantsFromInstances(instances:Array<Models.ComponentsInstances.ComponentInstance>):Array<participant> {
+ let participants = [];
+ _.forEach(instances,(instance)=> {
+ if(Utils.Constants.ResourceType.VF == instance.originType){
+ participants.push(new participant(instance));
+ }
+ });
+ return participants;
+ }
+
+
+ private initScope():void {
+ this.$scope.vendorMessageModel = new VendorModel(
+ this.$scope.component.artifacts.filteredByType(Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES.NETWORK_CALL_FLOW),
+ this.$scope.component.uniqueId,
+ this.$scope.isViewMode(),
+ this.$scope.user.userId,
+ this.uuid4.generate(),
+ Utils.Constants.ArtifactType.THIRD_PARTY_RESERVED_TYPES.NETWORK_CALL_FLOW,
+ this.getVFParticipantsFromInstances(this.$scope.component.componentInstances)
+ );
+
+ this.$scope.thirdParty = true;
+ this.$scope.setValidState(true);
+ }
+
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html
new file mode 100644
index 0000000000..6ce3e8e2b7
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/network-call-flow/network-call-flow-view.html
@@ -0,0 +1,3 @@
+<div class="workspace-network-call-flow">
+ <punch-out name="'sequence-diagram'" data="vendorMessageModel" user="user" on-event="onVendorEvent"></punch-out>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts
new file mode 100644
index 0000000000..faf77a5215
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view-model.ts
@@ -0,0 +1,134 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 4/7/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ export interface IProductHierarchyScope extends IWorkspaceViewModelScope {
+
+ categoriesOptions: Array<Models.IMainCategory>;
+ product:Models.Components.Product;
+ isLoading:boolean;
+ showDropDown:boolean;
+
+ onInputTextClicked():void;
+ onGroupSelected(category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup):void;
+ clickOutside():void;
+ deleteGroup(uniqueId:string):void;
+ }
+
+ export class ProductHierarchyViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'Sdc.Services.CacheService',
+ 'ComponentFactory',
+ '$state'
+ ];
+
+ constructor(private $scope:IProductHierarchyScope,
+ private cacheService:Sdc.Services.CacheService,
+ private ComponentFactory: Sdc.Utils.ComponentFactory,
+ private $state:ng.ui.IStateService) {
+
+
+ this.$scope.product = <Models.Components.Product>this.$scope.getComponent();
+ this.$scope.setValidState(true);
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+ private initCategories = () => {
+ this.$scope.categoriesOptions = angular.copy(this.cacheService.get('productCategories'));
+ let selectedGroup:Array<Models.IGroup> = [];
+ _.forEach(this.$scope.product.categories, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ selectedGroup = selectedGroup.concat(subcategory.groupings);
+ });
+ });
+ _.forEach(this.$scope.categoriesOptions, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ _.forEach(subcategory.groupings, (group:Models.ISubCategory) => {
+ let componentGroup:Models.IGroup = _.find(selectedGroup, (componentGroupObj) => {
+ return componentGroupObj.uniqueId == group.uniqueId;
+ });
+ if(componentGroup){
+ group.isDisabled = true;
+ }
+ });
+ });
+ });
+ };
+
+ private setFormValidation = ():void => {
+ //if(!this.$scope.product.categories || this.$scope.product.categories.length === 0){
+ // this.$scope.setValidState(false);
+ //}
+ //else{
+ this.$scope.setValidState(true);
+ // }
+
+ };
+
+ private initScope = ():void => {
+ this.$scope.isLoading= false;
+ this.$scope.showDropDown =false;
+ this.initCategories();
+ this.setFormValidation();
+
+ this.$scope.onGroupSelected = (category: Models.IMainCategory, subcategory: Models.ISubCategory, group: Models.IGroup):void => {
+ this.$scope.product.addGroup(category, subcategory, group);
+ this.$state.current.data.unsavedChanges = !this.$scope.isViewMode();
+ group.isDisabled = true;
+ this.$scope.showDropDown = false;
+ this.setFormValidation();
+ };
+
+ this.$scope.onInputTextClicked = ():void => {//just edit the component in place, no pop up nor server update ?
+ this.$scope.showDropDown = !this.$scope.showDropDown;
+ };
+
+ this.$scope.clickOutside = (): any => {
+ this.$scope.showDropDown = false;
+ };
+
+ this.$scope.deleteGroup = (uniqueId:string) : void => {
+ //delete group from component
+ this.$scope.product.deleteGroup(uniqueId);
+ this.$state.current.data.unsavedChanges = !this.$scope.isViewMode();
+ this.setFormValidation();
+ //enabled group
+ _.forEach(this.$scope.categoriesOptions, (category: Models.IMainCategory) => {
+ _.forEach(category.subcategories, (subcategory:Models.ISubCategory) => {
+ let groupObj:Models.IGroup = _.find (subcategory.groupings, (group) => {
+ return group.uniqueId === uniqueId;
+ });
+ if(groupObj){
+ groupObj.isDisabled = false;
+ }
+ });
+ });
+ }
+ };
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html
new file mode 100644
index 0000000000..2335ad7c74
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy-view.html
@@ -0,0 +1,40 @@
+<div class="workspace-hierarchy">
+ <div class="dropdown-container" clicked-outside="{onClickedOutside: 'clickOutside()', clickedOutsideEnable: 'true'}" >
+ <input placeholder="Add Group" data-ng-click="onInputTextClicked()" class="dropdown-input-text" data-ng-model="search.filterTerms" data-ng-disabled="isViewMode()" data-ng-class="{'view-mode': isViewMode()}" data-ng-model-options="{debounce: 200}"/>
+ <div data-ng-class="{'show': showDropDown}" class="dropdown-content" >
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="false" class="scrollbar-container">
+ <div ng-repeat="category in categoriesOptions track by $index">
+ <div ng-repeat="subcategory in category.subcategories track by $index">
+ <div class="dropdown-option" ng-show="!category.filteredGroup || category.filteredGroup.length > 0">
+ <div class="category-container">
+ <div class="category">{{category.name}}</div>
+ <div class="subcategory">{{subcategory.name}}</div>
+ </div>
+ <div class="groupings-container">
+ <div ng-init="group.filterTerms = group.name + ' ' + category.name + ' ' + subcategory.name"
+ ng-repeat="group in (category.filteredGroup = (subcategory.groupings | filter:search )) track by $index">
+ <div class="group" data-ng-disabled="group.isDisabled" data-ng-class="{'disabled-group': group.isDisabled}" ng-click="onGroupSelected(category, subcategory, group)">
+ <span >{{group.name}}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+ </div>
+ <div class="hierarchy-groups-container no-border-top" data-ng-class="{'view-mode': isViewMode()}">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div ng-if="!product.categories.length || product.categories.length === 0" class="no-groups-text" translate="NEW_PRODUCT_NO_CATEGORIES_TO_DISPLAY"></div>
+ <div ng-repeat="category in product.categories track by $index">
+ <div ng-repeat="subcategory in category.subcategories track by $index">
+ <div class="group-tag" ng-repeat="group in subcategory.groupings track by $index"
+ data-ng-init="tooltip = '<b>' + category.name + '</b><br />' + subcategory.name">
+ <sdc-tag sdc-disable="isViewMode()" data-on-delete="deleteGroup(uniqueId)" data-tag-data="{tag: group.name, tooltip: tooltip, id: group.uniqueId }"></sdc-tag>
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less b/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less
new file mode 100644
index 0000000000..c992558ed2
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less
@@ -0,0 +1,130 @@
+.workspace-hierarchy {
+ display: inline-block;
+ width: 93%;
+
+ .scrollbar-container{
+ max-height:400px;
+ .perfect-scrollbar;
+ }
+
+ .dropdown-container {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+
+ &:after{
+ top: 47%;
+ right: 1%;
+ border: solid transparent;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-color: rgba(0, 0, 0, 0);
+ border-top-color: black;
+ border-width: 4px;
+ margin-left: -4px;
+ }
+
+ .dropdown-input-text {
+ width: 100%;
+ padding: 4px 10px;
+ }
+
+ .dropdown-content {
+ .perfect-scrollbar;
+ border: 1px solid #d8d8d8;
+ display: none;
+ position: absolute;
+ overflow: hidden;
+ width: 100%;
+ .bg_c;
+ max-height: 400px;
+ z-index: 999999;
+
+ .dropdown-option {
+ border-bottom: 1px solid #d8d8d8;
+ display: inline-block;
+ width: 100%;
+ }
+
+ .category-container{
+ width: 250px;
+ float: left;
+ padding-left: 5px;
+
+ .category {
+ .bold;
+ padding: 3px 3px 2px 3px;
+ &:after{
+ .sprite;
+ .arrow-left;
+ content: '';
+ margin-left: 5px;
+ transform: rotate(180deg);
+ }
+ }
+ .subcategory {
+ padding-left: 3px;
+ }
+ }
+
+ .groupings-container{
+ display: inline-block;
+ width: 424px;
+ border-left: 1px solid #d8d8d8;
+ min-height: 55px;
+ .group{
+ padding: 3px 3px 3px 10px;
+ &:hover{
+ .hand;
+ .bg_n;
+ }
+ &.disabled-group {
+ opacity: 0.5;
+ &:hover{
+ cursor: auto;
+ .bg_c;
+ }
+ }
+ }
+ }
+
+ .seperator {
+ height: 1px;
+ width: 100%;
+ .bg_j;
+ margin: 5px 0px;
+ }
+ }
+ .show {
+ display: block;
+ }
+ }
+
+ .hierarchy-groups-container{
+ .b_9;
+ width: 100%;
+ border: 1px solid #d8d8d8;
+ height: 425px;
+ padding: 15px;
+ text-align: center;
+
+ .scrollbar-container {
+ z-index: 0;
+ }
+
+ .no-group-text{
+ text-align: center;
+ margin-top:25px;
+ a {
+ cursor: pointer;
+ }
+ }
+ .group-tag{
+ display: inline-block;
+ float: left;
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view-model.ts
new file mode 100644
index 0000000000..9b824bfca9
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view-model.ts
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+
+ interface IPropertiesViewModelScope extends IWorkspaceViewModelScope {
+ tableHeadersList: Array<any>;
+ reverse: boolean;
+ sortBy:string;
+ filteredProperties:any;
+
+ addOrUpdateProperty(property?:Models.PropertyModel): void;
+ delete(property:Models.PropertyModel): void;
+ sort(sortBy:string): void;
+ }
+
+ export class PropertiesViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ModalsHandler'
+ ];
+
+
+ constructor(private $scope:IPropertiesViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService,
+ private ModalsHandler:Utils.ModalsHandler) {
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+
+ private openEditPropertyModal = (property:Models.PropertyModel):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/property-form/property-form-view.html'),
+ controller: 'Sdc.ViewModels.PropertyFormViewModel',
+ size: 'sdc-l',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ property: ():Models.PropertyModel => {
+ return property;
+ },
+ component: ():Models.Components.Component => {
+ return <Models.Components.Component> this.$scope.component;
+ },
+ filteredProperties: ():Array<Models.PropertyModel> => {
+ return this.$scope.filteredProperties.properties;
+ }
+ }
+ };
+ this.$modal.open(modalOptions);
+ };
+
+ private initScope = ():void => {
+
+ //let self = this;
+ this.$scope.filteredProperties={properties:[]};
+ this.$scope.sortBy = 'name';
+ this.$scope.reverse = false;
+ this.$scope.setValidState(true);
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Type', property: 'type'},
+ {title: 'Schema', property: 'schema.property.type'},
+ {title: 'Description', property: 'description'},
+ ];
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+
+ this.$scope.addOrUpdateProperty = (property?:Models.PropertyModel):void => {
+ this.openEditPropertyModal(property ? property : new Models.PropertyModel());
+ };
+
+ this.$scope.delete = (property:Models.PropertyModel):void => {
+
+ let onOk = ():void => {
+ this.$scope.component.deleteProperty(property.uniqueId);
+ };
+ let title:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TITLE");
+ let message:string = this.$filter('translate')("PROPERTY_VIEW_DELETE_MODAL_TEXT", "{'name': '" + property.name + "'}");
+ this.ModalsHandler.openConfirmationModal(title, message, false).then(onOk);
+ };
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view.html
new file mode 100644
index 0000000000..e9a4c3879d
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties-view.html
@@ -0,0 +1,62 @@
+<div class="workspace-properties">
+ <div id="left-top-bar">
+ <span id="properties-count">Total Properties: {{component.properties.length}}</span>
+ <input id="search-by-name" type="search" placeholder="Search" data-ng-model-options="{debounce: 200}" data-ng-model="filterTerms"/>
+ <span class="sprite magnification-glass search-button"></span>
+ </div>
+ <div class="add-btn" data-tests-id="addGrey" ng-if="!isViewMode()"
+ data-ng-class="{'disabled': isDisableMode()}" data-ng-click="addOrUpdateProperty()">Add Property</div>
+ <div class="table-container-flex">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-if="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item" ng-if="!isViewMode()"><span class="delete-col-header"></span></div>
+ <!--div class="table-no-text-header head-row flex-item"></div-->
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="component.properties.length === 0" class="no-row-text" data-ng-class="{'disabled': isDisableMode()}">
+ There are no properties to display <br>
+ <span ng-if="!isViewMode()"> click <a data-ng-click="addOrUpdateProperty()">here</a> to add one </span>
+
+ </div>
+ <div data-ng-repeat="property in filteredProperties.properties=(component.properties | orderBy:sortBy:reverse | filter: {filterTerm:filterTerms}) track by $index"
+ data-tests-id="propertyRow" data-ng-class="{'selected': property.selected}"
+ class="flex-container data-row">
+
+ <div class="table-col-general flex-item text">
+ <a data-tests-id="{{property.name}}"
+ tooltips tooltip-content="{{property.name}}"
+ data-ng-click="addOrUpdateProperty(property); $event.stopPropagation();"
+ data-ng-class="{'disabled': isViewMode()}">{{property.name}}</a>
+
+ </div>
+
+ <div class="table-col-general flex-item text"
+ data-tests-id="{{property.type}}"
+ tooltips tooltip-content="{{property.type}}"
+ data-ng-bind="property.type">
+ </div>
+ <div class="table-col-general flex-item text"
+ data-tests-id="{{property.schema.property.type}}"
+ tooltips tooltip-content="{{property.schema.property.type}}"
+ data-ng-bind="property.schema.property.type">
+ </div>
+ <div class="table-col-general flex-item text">
+ <span tooltips tooltip-content="{{property.description}}" data-tests-id="{{property.description}}" data-ng-bind="property.description"></span>
+ </div>
+ <div class="table-btn-col flex-item" ng-if="!isViewMode()">
+ <button class="table-delete-btn" data-tests-id="delete_{{property.name}}" data-ng-if="property.parentUniqueId==component.uniqueId"
+ data-ng-click="delete(property); $event.stopPropagation();" data-ng-class="{'disabled': isViewMode()}"> </button>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties.less b/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties.less
new file mode 100644
index 0000000000..3e8d6c3fbd
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/properties/properties.less
@@ -0,0 +1,115 @@
+.workspace-properties {
+
+ width: 93%;
+ display: inline-block;
+
+ #left-top-bar{
+ float: left;
+ width: 155px;
+ ::-webkit-input-placeholder {
+ font-style: italic;
+ }
+ :-moz-placeholder {
+ font-style: italic;
+ }
+ ::-moz-placeholder {
+ font-style: italic;
+ }
+ :-ms-input-placeholder {
+ font-style: italic;
+ }
+
+ #properties-count{
+ font-weight: bold;
+ float: left;
+ }
+
+ #search-by-name{
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+ width: 245px;
+ height: 32px;
+ line-height: 32px;
+ border: 1px solid @main_color_o;
+ text-indent: 10px;
+ }
+
+ .search-button{
+ .hand;
+ cursor: pointer;
+ float: right;
+ position: relative;
+ top: -22px;
+ right: -80px;
+ }
+ }
+
+ .add-btn {
+ margin: 36px 0 0 0;
+ }
+
+ .delete-col-header{
+ .sprite;
+ .sprite.e-sdc-small-icon-delete;
+ }
+
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .table{
+ height:490px;
+ margin-bottom: 0;
+ }
+
+ .data-row{
+ .table-delete-btn{
+ display: none !important;
+ }
+ &:hover{
+ .table-delete-btn{
+ display: inline-block !important;
+ }
+ }
+ }
+
+ .table-container-flex {
+ margin-top: 27px;
+
+ .text{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ }
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+ a{
+ .hand
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 20;
+
+ }
+ .flex-item:nth-child(5) {
+ flex-grow: 3;
+ padding-top: 10px;
+ }
+
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts
new file mode 100644
index 0000000000..97a117e8b7
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by rcohen on 9/22/2016.
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import tree = d3.layout.tree;
+
+ export class SortTableDefined {
+ reverse:boolean;
+ sortByField:string;
+ }
+
+ interface IReqAndCapabilitiesViewModelScope extends IWorkspaceViewModelScope {
+ requirementsTableHeadersList: Array<any>;
+ capabilitiesTableHeadersList: Array<any>;
+ capabilityPropertiesTableHeadersList: Array<any>;
+ requirementsSortTableDefined: SortTableDefined;
+ capabilitiesSortTableDefined: SortTableDefined;
+ propertiesSortTableDefined: SortTableDefined;
+ requirements:Array<Models.Requirement>;
+ capabilities:Array<Models.Capability>;
+ mode:string;
+ filteredProperties:Array<Array<Models.PropertyModel>>;
+ searchText:string;
+
+ sort(sortBy:string, sortByTableDefined:SortTableDefined):void;
+ updateProperty(property:Models.PropertyModel, indexInFilteredProperties:number):void;
+ allCapabilitiesSelected(selected:boolean):void;
+ }
+
+ export class ReqAndCapabilitiesViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter',
+ '$modal',
+ '$templateCache',
+ 'ModalsHandler'
+ ];
+
+
+ constructor(private $scope:IReqAndCapabilitiesViewModelScope,
+ private $filter:ng.IFilterService,
+ private $modal:ng.ui.bootstrap.IModalService,
+ private $templateCache:ng.ITemplateCacheService) {
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+
+ private openEditPropertyModal = (property:Models.PropertyModel, indexInFilteredProperties:number):void => {
+ let viewModelsHtmlBasePath:string = '/app/scripts/view-models/';
+ //...because there is not be api
+ _.forEach(this.$scope.filteredProperties[indexInFilteredProperties],(prop:Models.PropertyModel)=>{
+ prop.readonly = true;
+ });
+ let modalOptions:ng.ui.bootstrap.IModalSettings = {
+ template: this.$templateCache.get(viewModelsHtmlBasePath + 'forms/property-form/property-form-view.html'),
+ controller: 'Sdc.ViewModels.PropertyFormViewModel',
+ size: 'sdc-l',
+ backdrop: 'static',
+ keyboard: false,
+ resolve: {
+ property: ():Models.PropertyModel => {
+ return property;
+ },
+ component: ():Models.Components.Component => {
+ return <Models.Components.Component> this.$scope.component;
+ },
+ filteredProperties: ():Array<Models.PropertyModel> => {
+ return this.$scope.filteredProperties[indexInFilteredProperties];
+ }
+ }
+ };
+ this.$modal.open(modalOptions);
+ };
+
+ private initScope = ():void => {
+
+ this.$scope.requirementsSortTableDefined = {
+ reverse: false,
+ sortByField: 'name'
+ };
+ this.$scope.capabilitiesSortTableDefined = {
+ reverse: false,
+ sortByField: 'name'
+ };
+ this.$scope.propertiesSortTableDefined = {
+ reverse: false,
+ sortByField: 'name'
+ };
+
+ this.$scope.setValidState(true);
+ this.$scope.requirementsTableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Capability', property: 'capability'},
+ {title: 'Node', property: 'node'},
+ {title: 'Relationship', property: 'relationship'},
+ {title: 'Connected To', property: ''},
+ {title: 'Occurrences', property: ''}
+ ];
+ this.$scope.capabilitiesTableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Type', property: 'type'},
+ {title: 'Description', property: ''},
+ {title: 'Valid Source', property: ''},
+ {title: 'Occurrences', property: ''}
+ ];
+ this.$scope.capabilityPropertiesTableHeadersList = [
+ {title: 'Name', property: 'name'},
+ {title: 'Type', property: 'type'},
+ {title: 'Schema', property: 'schema.property.type'},
+ {title: 'Description', property: 'description'},
+ ];
+ this.$scope.filteredProperties=[];
+
+ this.$scope.mode='requirements';
+ this.$scope.requirements=[];
+ _.forEach(this.$scope.component.requirements,(req:Array<Models.Requirement>,capName)=>{
+ this.$scope.requirements=this.$scope.requirements.concat(req);
+ });
+
+ this.$scope.capabilities=[];
+ _.forEach(this.$scope.component.capabilities,(cap:Array<Models.Capability>,capName)=>{
+ this.$scope.capabilities=this.$scope.capabilities.concat(cap);
+ });
+
+ this.$scope.sort = (sortBy:string, sortByTableDefined:SortTableDefined):void => {
+ sortByTableDefined.reverse = (sortByTableDefined.sortByField === sortBy) ? !sortByTableDefined.reverse : false;
+ sortByTableDefined.sortByField = sortBy;
+ };
+
+ this.$scope.updateProperty = (property:Models.PropertyModel, indexInFilteredProperties:number):void => {
+ this.openEditPropertyModal(property, indexInFilteredProperties);
+ };
+
+ this.$scope.allCapabilitiesSelected = (selected:boolean):void => {
+ _.forEach(this.$scope.capabilities,(cap:Models.Capability)=>{
+ cap.selected = selected;
+ });
+ };
+ }
+ }
+}
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html
new file mode 100644
index 0000000000..047768689a
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view.html
@@ -0,0 +1,144 @@
+<div class="workspace-req-and-cap">
+ <div class="tabs">
+ <button data-tests-id="req-tab" data-ng-click="mode='requirements';filterTerms='';" class="tlv-btn" data-ng-class="{'selected':mode=='requirements'}">Requirements({{requirements.length||'0'}})</button>
+ <button data-tests-id="cap-tab" data-ng-click="mode='capabilities';filterTerms='';" class="tlv-btn" data-ng-class="{'selected':mode=='capabilities'}">Capabilities({{capabilities.length||'0'}})</button>
+ </div>
+ <div class="expand-collapse-buttons" data-ng-if="mode=='capabilities'">
+ <span class="sprite-new expand-all" data-ng-click="allCapabilitiesSelected(true)"></span>
+ <span class="sprite-new collapse-all" data-ng-click="allCapabilitiesSelected(false)"></span>
+ </div>
+ <div class="search">
+ <input id="search-box" data-tests-id="search-box" type="search" placeholder="Search" data-ng-model-options="{debounce: 200}" data-ng-model="filterTerms"/>
+ <div class="search-icon-container">
+ <span class="search-icon sprite-new search-white-icon"></span>
+ </div>
+ </div>
+ <div class="table-container-flex requirements-table" data-ng-if="mode=='requirements'">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in requirementsTableHeadersList track by $index" data-ng-click="sort(header.property, requirementsSortTableDefined)">{{header.title}}
+ <span data-ng-if="requirementsSortTableDefined.sortByField === header.property" class="table-header-sort-arrow" data-ng-class="{'down': requirementsSortTableDefined.reverse, 'up':!requirementsSortTableDefined.reverse}"> </span>
+ </div>
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="requirements.length === 0" class="no-row-text">
+ There are no requirements to display
+
+ </div>
+ <div data-ng-repeat="req in requirements | orderBy:requirementsSortTableDefined.sortByField:requirementsSortTableDefined.reverse | filter: {filterTerm:filterTerms} track by $index"
+ class="flex-container data-row" data-tests-id="reqRow">
+
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{req.name}}" tooltips tooltip-content="{{req.name}}">{{req.name}}</span>
+ </div>
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{req.capability}}" tooltips tooltip-content="{{req.capability}}">{{req.capability.substring("tosca.capabilities.".length)}}</span>
+ </div>
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{req.node}}" tooltips tooltip-content="{{req.node}}">{{req.node.substring("tosca.nodes.".length)}}</span>
+ </div>
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{req.relationship}}" tooltips tooltip-content="{{req.relationship}}">{{req.relationship.substring("tosca.relationships.".length)}}</span>
+ </div>
+ <div class="table-col-general flex-item text" data-tests-id="{{}}" data-ng-bind=""></div>
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{req.minOccurrences}},{{req.maxOccurrences}}" tooltips tooltip-content="{{req.minOccurrences}},{{req.maxOccurrences}}">{{req.minOccurrences}},{{req.maxOccurrences}}</span>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+ <div class="table-container-flex capabilities-table" data-ng-if="mode=='capabilities'">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in capabilitiesTableHeadersList track by $index" data-ng-click="sort(header.property, capabilitiesSortTableDefined)">{{header.title}}
+ <span data-ng-if="capabilitiesSortTableDefined.sortByField === header.property" class="table-header-sort-arrow" data-ng-class="{'down': capabilitiesSortTableDefined.reverse, 'up':!capabilitiesSortTableDefined.reverse}"> </span>
+ </div>
+ </div>
+
+ <div class="body">
+ <perfect-scrollbar scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="capabilities.length === 0" class="no-row-text">
+ There are no capabilities to display
+
+ </div>
+ <div data-ng-repeat-start="capability in capabilities | orderBy:capabilitiesSortTableDefined.sortByField:capabilitiesSortTableDefined.reverse | filter: {filterTerm:filterTerms} track by $index"
+ class="flex-container data-row" data-ng-class="{'selected': capability.selected}"
+ data-ng-click="capability.selected = !capability.selected" data-tests-id="capabilities-table-row">
+
+ <div class="table-col-general flex-item text">
+ <span class="sprite-new arrow-up-small" data-ng-class="{'opened': capability.selected}"></span>
+ <span data-tests-id="{{capability.name}}" tooltips tooltip-content="{{capability.name}}">{{capability.name}}</span>
+ </div>
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{capability.type}}" tooltips tooltip-content="{{capability.type}}">{{capability.type.substring("tosca.capabilities.".length)}}</span>
+ </div>
+
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{capability.description}}" tooltips tooltip-content="{{capability.description}}">{{capability.description}}</span>
+ </div>
+
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{capability.validSourceTypes.join(',')}}" tooltips tooltip-content="{{capability.validSourceTypes.join(',')}}">{{capability.validSourceTypes.join(',')}}</span>
+ </div>
+
+ <div class="table-col-general flex-item text">
+ <span data-tests-id="{{capability.minOccurrences}},{{capability.maxOccurrences}}" tooltips tooltip-content="{{capability.minOccurrences}},{{capability.maxOccurrences}}">{{capability.minOccurrences}},{{capability.maxOccurrences}}</span>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="capability.selected" class="item-opened">
+ <p class="properties-title">Properties</p>
+ <div class="table-container-flex properties-table">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" data-ng-repeat="header in capabilityPropertiesTableHeadersList track by $index" data-ng-click="sort(header.property, propertiesSortTableDefined)">{{header.title}}
+ <span data-ng-if="propertiesSortTableDefined.sortByField === header.property" class="table-header-sort-arrow" data-ng-class="{'down': propertiesSortTableDefined.reverse, 'up':!propertiesSortTableDefined.reverse}"> </span>
+ </div>
+ </div>
+
+ <div class="body">
+ <div data-ng-if="capability.properties.length === 0" class="no-row-text">
+ There are no properties to display
+ </div>
+ <div data-ng-repeat="property in filteredProperties[$parent.$index]=(capability.properties | orderBy:propertiesSortTableDefined.sortByField:propertiesSortTableDefined.reverse) track by $index"
+ data-tests-id="propertyRow"
+ class="flex-container data-row">
+
+ <div class="table-col-general flex-item text">
+ <a data-tests-id="{{property.name}}"
+ tooltips tooltip-content="{{property.name}}"
+ data-ng-click="updateProperty(property, $parent.$index); $event.stopPropagation();"
+ data-ng-class="{'disabled': isViewMode()}">{{property.name}}</a>
+
+ </div>
+
+ <div class="table-col-general flex-item text"
+ data-tests-id="{{property.type}}"
+ tooltips tooltip-content="{{property.type}}"
+ data-ng-bind="property.type">
+ </div>
+ <div class="table-col-general flex-item text"
+ data-tests-id="{{property.schema.property.type}}"
+ tooltips tooltip-content="{{property.schema.property.type}}"
+ data-ng-bind="property.schema.property.type">
+ </div>
+ <div class="table-col-general flex-item text">
+ <span tooltips tooltip-content="{{property.description}}" data-tests-id="{{property.description}}" data-ng-bind="property.description"></span>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ </perfect-scrollbar>
+ </div>
+
+ </div>
+ </div>
+</div>
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less b/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less
new file mode 100644
index 0000000000..9b52fad411
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less
@@ -0,0 +1,196 @@
+.workspace-req-and-cap {
+
+ width: 93%;
+ display: inline-block;
+
+ .tabs{
+ float: left;
+ position: relative;
+ top: 6px;
+ button{
+ float: left;
+ width: 233px;
+ height: 38px;
+ background-color: @tlv_color_t;
+ border: 1px solid @main_color_o;
+ color: black;
+ &:nth-child(1){
+ border-radius: 10px 0 0 0;
+ }
+ &:nth-child(2){
+ border-radius: 0 10px 0 0;
+ }
+ &.selected{
+ background-color: @main_color_a;
+ border: 1px solid @main_color_a;
+ color: white;
+ }
+ }
+ }
+ .search{
+ margin-bottom: 12px;
+ float: right;
+ ::-webkit-input-placeholder {
+ font-style: italic;
+ }
+ :-moz-placeholder {
+ font-style: italic;
+ }
+ ::-moz-placeholder {
+ font-style: italic;
+ }
+ :-ms-input-placeholder {
+ font-style: italic;
+ }
+ #search-box{
+ -webkit-border-radius: 2px 0 0 2px;
+ -moz-border-radius: 2px 0 0 2px;
+ border-radius: 2px 0 0 2px;
+ width: 213px;
+ height: 32px;
+ line-height: 32px;
+ border: 1px solid @main_color_o;
+ text-indent: 10px;
+ float: left;
+ }
+ .search-icon-container{
+ background-color: @main_color_a;
+ height: 32px;
+ width: 32px;
+ border-radius: 0 2px 2px 0;
+ float: left;
+ .search-icon{
+ position: relative;
+ top: 9px;
+ }
+ }
+ }
+ .expand-collapse-buttons{
+ float: right;
+ width: 44px;
+ margin-left: 11px;
+ margin-top: 10px;
+ span{
+ vertical-align: bottom;
+ .hand;
+ }
+ }
+
+
+
+ .table{
+ height:490px;
+ margin-bottom: 0;
+ }
+
+ .arrow-up-small{
+ &.opened{
+ .arrow-up-small-hover;
+ }
+ }
+
+ .item-opened{
+ background-color: @tlv_color_t;
+ }
+
+ .properties-title{
+ margin:0;
+ font-weight: bold;
+ }
+
+ .table-container-flex {
+ margin-top: 10px;
+
+ .text{
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: inline-block;
+ white-space: nowrap;
+ }
+
+ &.requirements-table{
+ border-top: 4px solid @main_color_a;
+ .flex-item:nth-child(1) {
+ flex-grow: 20;
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 20;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 20;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 20;
+ }
+
+ .flex-item:nth-child(5) {
+ flex-grow: 20;
+ }
+
+ .flex-item:nth-child(6) {
+ flex-grow: 20;
+ }
+ }
+
+ &.capabilities-table{
+ border-top: 4px solid @main_color_a;
+ .selected{
+ .flex-item:nth-child(1) {
+ border-left: 4px solid @main_color_a;
+ padding-right: 11px;
+ }
+ }
+ .flex-item:nth-child(1) {
+ flex-grow: 10;
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 10;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 10;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 10;
+ }
+
+ .flex-item:nth-child(5) {
+ flex-grow: 10;
+ }
+
+ }
+
+ &.properties-table{
+ .table{
+ height: auto;
+ }
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+ a{
+ .hand
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(4) {
+ flex-grow: 20;
+
+ }
+ }
+
+ }
+
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts
new file mode 100644
index 0000000000..1e6bc04924
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="../../../../references"/>
+module Sdc.ViewModels {
+ 'use strict';
+ import ArtifactModel = Sdc.Models.ArtifactModel;
+
+ export interface IToscaArtifactsScope extends IWorkspaceViewModelScope {
+ artifacts: Array<Models.ArtifactModel>;
+ tableHeadersList: Array<any>;
+ artifactType: string;
+ downloadFile:Models.IFileDownload;
+ isLoading:boolean;
+ sortBy:string;
+ reverse:boolean;
+
+ getTitle(): string;
+ download(artifact:Models.ArtifactModel): void;
+ sort(sortBy:string): void;
+ showNoArtifactMessage():boolean;
+ }
+
+ export class ToscaArtifactsViewModel {
+
+ static '$inject' = [
+ '$scope',
+ '$filter'
+ ];
+
+ constructor(private $scope:IToscaArtifactsScope,
+ private $filter:ng.IFilterService) {
+ this.initScope();
+ this.$scope.updateSelectedMenuItem();
+ }
+
+ private initScope = ():void => {
+ let self = this;
+ this.$scope.isLoading = false;
+ this.$scope.sortBy = 'artifactDisplayName';
+ this.$scope.reverse = false;
+ this.$scope.setValidState(true);
+ this.$scope.artifactType = 'normal';
+ this.$scope.getTitle = ():string => {
+ return this.$filter("resourceName")(this.$scope.component.name) + ' Artifacts';
+
+ };
+
+ this.$scope.tableHeadersList = [
+ {title: 'Name', property: 'artifactDisplayName'},
+ {title: 'Type', property: 'artifactType'}
+ ];
+
+ this.$scope.artifacts = <ArtifactModel[]>_.values(this.$scope.component.toscaArtifacts);
+ this.$scope.sort = (sortBy:string):void => {
+ this.$scope.reverse = (this.$scope.sortBy === sortBy) ? !this.$scope.reverse : false;
+ this.$scope.sortBy = sortBy;
+ };
+
+
+
+ this.$scope.showNoArtifactMessage = ():boolean => {
+ if (this.$scope.artifacts.length === 0) {
+ return true;
+ }
+ return false;
+ };
+
+ }
+ }
+}
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html b/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html
new file mode 100644
index 0000000000..947b37db93
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view.html
@@ -0,0 +1,45 @@
+<div class="workspace-tosca-artifact">
+ <div class="table-container-flex">
+ <div class="table" data-ng-class="{'view-mode': isViewMode()}">
+ <div class="head flex-container">
+ <div class="table-header head-row hand flex-item" ng-repeat="header in tableHeadersList track by $index" data-ng-click="sort(header.property)">{{header.title}}
+ <span data-ng-show="sortBy === header.property" class="table-header-sort-arrow" data-ng-class="{'down': reverse, 'up':!reverse}"> </span>
+ </div>
+ <div class="table-no-text-header head-row flex-item"></div>
+ </div>
+ <div class="body">
+ <perfect-scrollbar suppress-scroll-x="true" scroll-y-margin-offset="0" include-padding="true" class="scrollbar-container">
+ <div data-ng-if="showNoArtifactMessage()" class="no-row-text" data-ng-class="{'disabled': isDisableMode()}">
+ There are no TOSCA artifacts to display
+ </div>
+ <div data-ng-repeat-start="artifact in artifacts| orderBy:sortBy:reverse track by $index"
+ class="flex-container data-row"
+ data-ng-class="{'selected': artifact.selected}">
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected">
+ <span class="sprite table-arrow" data-ng-class="{'opened': artifact.selected}" data-tests-id="{{artifact.artifactDisplayName}}"></span>
+ {{artifact.artifactDisplayName}}
+ </div>
+
+ <div class="table-col-general flex-item" data-ng-click="artifact.selected = !artifact.selected" data-tests-id="{{artifact.artifactType}}">
+ {{artifact.artifactType}}
+ </div>
+
+ <div class="table-btn-col flex-item download-icon-container">
+ <button class="table-download-btn tosca" download-artifact data-tests-id="download_{{artifact.artifactDisplayName}}"
+ data-ng-if="artifact.artifactName" component="component" artifact="artifact" show-loader="true"></button>
+ </div>
+ </div>
+ <div data-ng-repeat-end="" data-ng-if="artifact.selected" class="item-opened">
+ <div><span class="details-title">Label:</span> {{artifact.artifactLabel}}</div>
+ <div><span class="details-title">UUID:</span> {{artifact.uniqueId}}</div>
+ <div><span class="details-title">Description:</span> {{artifact.description}}</div>
+
+
+ </div>
+
+ </perfect-scrollbar>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less b/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less
new file mode 100644
index 0000000000..f792bb8c53
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less
@@ -0,0 +1,74 @@
+.workspace-tosca-artifact {
+ width: 93%;
+ display: inline-block;
+ .w-sdc-classic-btn {
+ float: right;
+ margin-bottom: 10px;
+ }
+
+ .details-title{
+ font-weight: bold;
+ margin-right: 5px;
+ }
+
+ .table{
+ height: 490px;
+ margin-bottom: 0;
+ }
+
+
+ .table-container-flex {
+ margin-top: 27px;
+
+ .item-opened{
+ word-wrap: break-word;
+ }
+
+
+ .flex-item:nth-child(1) {
+ flex-grow: 15;
+ .hand;
+ span.table-arrow {
+ margin-right: 7px;
+ }
+ }
+
+ .flex-item:nth-child(2) {
+ flex-grow: 6;
+ }
+
+ .flex-item:nth-child(3) {
+ flex-grow: 1;
+ }
+
+
+
+
+ .table-download-btn{
+ &.tosca{
+ margin-left: 0;
+ margin-top: 8px;
+ }
+ }
+
+ .download-icon-container{
+ position: relative;
+
+ .loader{
+ left: 60%;
+ top: 45px;
+ border: none;
+ background-color: transparent;
+ height: 0px;
+ width: 63px;
+ outline: none;
+
+ }
+ }
+
+
+ }
+
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/workspace-view-model.ts b/catalog-ui/app/scripts/view-models/workspace/workspace-view-model.ts
new file mode 100644
index 0000000000..a8523f24f5
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/workspace-view-model.ts
@@ -0,0 +1,703 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/**
+ * Created by obarda on 3/30/2016.
+ */
+/// <reference path="../../references"/>
+module Sdc.ViewModels {
+
+ 'use strict';
+ import Resource = Sdc.Models.Components.Resource;
+ import ResourceType = Sdc.Utils.Constants.ResourceType;
+
+ export interface IWorkspaceViewModelScope extends ng.IScope {
+
+ isLoading: boolean;
+ isCreateProgress: boolean;
+ component: Models.Components.Component;
+ originComponent: Models.Components.Component;
+ componentType: string;
+ importFile: any;
+ leftBarTabs: Utils.MenuItemGroup;
+ isNew: boolean;
+ isFromImport: boolean;
+ isValidForm: boolean;
+ mode: Utils.Constants.WorkspaceMode;
+ breadcrumbsModel: Array<Utils.MenuItemGroup>;
+ sdcMenu: Models.IAppMenu;
+ changeLifecycleStateButtons: any;
+ version: string;
+ versionsList: Array<any>;
+ changeVersion: any;
+ isComposition: boolean;
+ isDeployment: boolean;
+ $state: ng.ui.IStateService;
+ user: Models.IUserProperties;
+ thirdParty: boolean;
+ disabledButtons: boolean;
+ menuComponentTitle: string;
+ progressService: Sdc.Services.ProgressService;
+ progressMessage: string;
+ // leftPanelComponents:Array<Models.Components.Component>; //this is in order to load the left panel once, and not wait long time when moving to composition
+
+ showChangeStateButton(): boolean;
+ getComponent(): Sdc.Models.Components.Component;
+ setComponent(component: Sdc.Models.Components.Component): void;
+ onMenuItemPressed(state: string): ng.IPromise<boolean>;
+ save(): ng.IPromise<boolean>;
+ setValidState(isValid: boolean): void;
+ revert(): void;
+ changeLifecycleState(state: string): void;
+ enabledTabs(): void
+ isDesigner(): boolean;
+ isViewMode(): boolean;
+ isEditMode(): boolean;
+ isCreateMode(): boolean;
+ isDisableMode(): boolean;
+ showFullIcons(): boolean;
+ goToBreadcrumbHome(): void;
+ onVersionChanged(selectedId: string): void;
+ getLatestVersion(): void;
+ getStatus(): string;
+ showLifecycleIcon(): boolean;
+ updateSelectedMenuItem(): void;
+ uploadFileChangedInGeneralTab(): void;
+ updateMenuComponentName(ComponentName: string): void;
+ }
+
+ export class WorkspaceViewModel {
+
+ static '$inject' = [
+ '$scope',
+ 'injectComponent',
+ 'ComponentFactory',
+ '$state',
+ 'sdcMenu',
+ '$q',
+ 'MenuHandler',
+ 'Sdc.Services.CacheService',
+ 'ChangeLifecycleStateHandler',
+ 'ModalsHandler',
+ 'LeftPaletteLoaderService',
+ '$filter',
+ 'EventListenerService',
+ 'Sdc.Services.EntityService',
+ 'Notification',
+ '$stateParams',
+ 'Sdc.Services.ProgressService'
+ ];
+
+ constructor(private $scope: IWorkspaceViewModelScope,
+ private injectComponent: Models.Components.Component,
+ private ComponentFactory: Utils.ComponentFactory,
+ private $state: ng.ui.IStateService,
+ private sdcMenu: Models.IAppMenu,
+ private $q: ng.IQService,
+ private MenuHandler: Utils.MenuHandler,
+ private cacheService: Services.CacheService,
+ private ChangeLifecycleStateHandler: Sdc.Utils.ChangeLifecycleStateHandler,
+ private ModalsHandler: Sdc.Utils.ModalsHandler,
+ private LeftPaletteLoaderService: Services.Components.LeftPaletteLoaderService,
+ private $filter: ng.IFilterService,
+ private EventListenerService: Services.EventListenerService,
+ private EntityService: Sdc.Services.EntityService,
+ private Notification: any,
+ private $stateParams: any,
+ private progressService: Sdc.Services.ProgressService) {
+
+ this.initScope();
+ this.initAfterScope();
+ }
+
+ private role: string;
+ private components: Array<Models.Components.Component>;
+
+ private initViewMode = (): Utils.Constants.WorkspaceMode => {
+ let mode = Utils.Constants.WorkspaceMode.VIEW;
+
+ if (!this.$state.params['id']) { //&& !this.$state.params['vspComponent']
+ mode = Utils.Constants.WorkspaceMode.CREATE;
+ } else {
+ if (this.$scope.component.lifecycleState === Utils.Constants.ComponentState.NOT_CERTIFIED_CHECKOUT &&
+ this.$scope.component.lastUpdaterUserId === this.cacheService.get("user").userId) {
+ if (this.$scope.component.isProduct() && this.role == Utils.Constants.Role.PRODUCT_MANAGER) {
+ mode = Utils.Constants.WorkspaceMode.EDIT;
+ }
+ if ((this.$scope.component.isService() || this.$scope.component.isResource()) && this.role == Utils.Constants.Role.DESIGNER) {
+ mode = Utils.Constants.WorkspaceMode.EDIT;
+ }
+ }
+ }
+ return mode;
+ };
+
+ private initChangeLifecycleStateButtons = (): void => {
+ let state = this.$scope.component.isService() && (Utils.Constants.Role.OPS == this.role || Utils.Constants.Role.GOVERNOR == this.role) ? this.$scope.component.distributionStatus : this.$scope.component.lifecycleState;
+ this.$scope.changeLifecycleStateButtons = this.sdcMenu.roles[this.role].changeLifecycleStateButtons[state];
+ };
+
+ private isNeedSave = (): boolean => {
+ if (this.$scope.isEditMode() && //this is a workaround for onboarding - we need to get the artifact in order to avoid saving the vf when moving from their tabs
+ (this.$state.current.name === Utils.Constants.States.WORKSPACE_MANAGEMENT_WORKFLOW || this.$state.current.name === Utils.Constants.States.WORKSPACE_NETWORK_CALL_FLOW)) {
+ return true;
+ }
+ return this.$scope.isEditMode() &&
+ this.$state.current.data && this.$state.current.data.unsavedChanges;
+ };
+
+ private initScope = (): void => {
+
+ this.$scope.component = this.injectComponent;
+ this.$scope.menuComponentTitle = this.$scope.component.name;
+ this.$scope.disabledButtons = false;
+ this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component);
+ this.$scope.componentType = this.$scope.component.componentType;
+ this.$scope.version = this.cacheService.get('version');
+ this.$scope.user = this.cacheService.get("user");
+ this.role = this.$scope.user.role;
+ this.$scope.mode = this.initViewMode();
+ this.$scope.isValidForm = true;
+ this.initChangeLifecycleStateButtons();
+ this.initVersionObject();
+ this.$scope.$state = this.$state;
+ this.$scope.isLoading = false;
+ this.$scope.isComposition = (this.$state.current.name.indexOf(Utils.Constants.States.WORKSPACE_COMPOSITION) > -1);
+ this.$scope.isDeployment = (this.$state.current.name.indexOf(Utils.Constants.States.WORKSPACE_DEPLOYMENT) > -1);
+ this.$scope.progressService = this.progressService;
+
+ this.$scope.getComponent = (): Sdc.Models.Components.Component => {
+ return this.$scope.component;
+ };
+
+ this.$scope.updateMenuComponentName = (ComponentName: string): void => {
+ this.$scope.menuComponentTitle = ComponentName;
+ };
+
+ this.$scope.sdcMenu = this.sdcMenu;
+ // Will be called from each step after save to update the resource.
+ this.$scope.setComponent = (component: Sdc.Models.Components.Component): void => {
+ this.$scope.component = component;
+ };
+
+ this.$scope.uploadFileChangedInGeneralTab = (): void => {
+ // In case user select browse file, and in update mode, need to disable submit for testing and checkin buttons.
+ if (this.$scope.isEditMode() && this.$scope.component.isResource() && (<Resource>this.$scope.component).resourceType == ResourceType.VF) {
+ this.$scope.disabledButtons = true;
+ }
+ };
+
+ this.$scope.onMenuItemPressed = (state: string): ng.IPromise<boolean> => {
+ let deferred = this.$q.defer();
+ if (this.isNeedSave()) {
+ if (this.$scope.isValidForm) {
+ let onSuccess = (): void => {
+ this.$state.go(state, {
+ id: this.$scope.component.uniqueId,
+ type: this.$scope.component.componentType.toLowerCase(),
+ components: this.components
+ });
+ deferred.resolve(true);
+ };
+ this.$scope.save().then(onSuccess);
+ } else {
+ console.log('form is not valid');
+ deferred.reject(false);
+ }
+ } else {
+ this.$state.go(state, {
+ id: this.$scope.component.uniqueId,
+ type: this.$scope.component.componentType.toLowerCase(),
+ components: this.components
+ });
+ deferred.resolve(true);
+ }
+ return deferred.promise;
+ };
+
+ this.$scope.setValidState = (isValid: boolean): void => {
+ this.$scope.isValidForm = isValid;
+ };
+
+ this.$scope.onVersionChanged = (selectedId: string): void => {
+ this.$scope.isLoading = true;
+ if (this.$state.current.data && this.$state.current.data.unsavedChanges) {
+ this.$scope.changeVersion.selectedVersion = _.find(this.$scope.versionsList, {versionId: this.$scope.component.uniqueId});
+ }
+ this.$state.go(this.$state.current.name, {
+ id: selectedId,
+ type: this.$scope.componentType.toLowerCase(),
+ mode: Utils.Constants.WorkspaceMode.VIEW,
+ components: this.$state.params['components']
+ });
+
+ };
+
+ this.$scope.getLatestVersion = (): void => {
+ this.$scope.onVersionChanged(_.first(this.$scope.versionsList).versionId);
+ };
+
+ this.$scope.save = (state?: string): ng.IPromise<boolean> => {
+ this.EventListenerService.notifyObservers(Utils.Constants.EVENTS.ON_WORKSPACE_SAVE_BUTTON_CLICK);
+
+ this.progressService.initCreateComponentProgress(this.$scope.component.uniqueId);
+
+ let deferred = this.$q.defer();
+ let modalInstance: ng.ui.bootstrap.IModalServiceInstance;
+
+ let onFailed = () => {
+ this.EventListenerService.notifyObservers(Utils.Constants.EVENTS.ON_WORKSPACE_SAVE_BUTTON_ERROR);
+ this.progressService.deleteProgressValue(this.$scope.component.uniqueId);
+ modalInstance && modalInstance.close(); // Close the modal in case it is opened.
+ this.$scope.isCreateProgress = false;
+ this.$scope.isLoading = false; // stop the progress.
+
+ this.$scope.setValidState(true); // Set the form valid (if sent form is valid, the error from server).
+ if (!this.$scope.isCreateMode()) {
+ this.$scope.component = this.$scope.originComponent; // Set the component back to the original.
+ this.enableMenuItems(); // Enable the menu items (left tabs), so user can press on them.
+ this.$scope.disabledButtons = false; // Enable "submit for testing" & checking buttons.
+ }
+
+ deferred.reject(false);
+ };
+
+ let onSuccessCreate = (component: Models.Components.Component) => {
+
+ this.EventListenerService.notifyObservers(Utils.Constants.EVENTS.ON_WORKSPACE_SAVE_BUTTON_SUCCESS);
+ this.progressService.deleteProgressValue(this.$scope.component.uniqueId);
+ //update components for breadcrumbs
+ this.components.unshift(component);
+ this.$state.go(Utils.Constants.States.WORKSPACE_GENERAL, {
+ id: component.uniqueId,
+ type: component.componentType.toLowerCase(),
+ components: this.components
+ });
+
+ deferred.resolve(true);
+ };
+
+ let onSuccessUpdate = (component: Models.Components.Component) => {
+ this.$scope.isCreateProgress = false;
+ this.$scope.disabledButtons = false;
+ this.EventListenerService.notifyObservers(Utils.Constants.EVENTS.ON_WORKSPACE_SAVE_BUTTON_SUCCESS);
+ this.progressService.deleteProgressValue(this.$scope.component.uniqueId);
+
+ // Stop the circle loader.
+ this.$scope.isLoading = false;
+
+ component.tags = _.reject(component.tags, (item)=> {
+ return item === component.name
+ });
+
+ // Update the components
+ this.$scope.component = component;
+ this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component);
+
+ //update components for breadcrumbs
+ this.components.unshift(component);
+
+ // Enable left tags
+ this.$scope.enabledTabs();
+
+
+ if (this.$state.current.data) {
+ this.$state.current.data.unsavedChanges = false;
+ }
+
+ deferred.resolve(true);
+ };
+
+ if (this.$scope.isCreateMode()) {
+ this.$scope.progressMessage = "Creating Asset...";
+ // CREATE MODE
+ this.$scope.isCreateProgress = true;
+
+ // Start creating the component
+ this.ComponentFactory.createComponentOnServer(this.$scope.component).then(onSuccessCreate, onFailed);
+
+ // In case we import CSAR. Notify user that import VF will take long time (the create is performed in the background).
+ if (this.$scope.component.isResource() && (<Resource>this.$scope.component).csarUUID) {
+ this.Notification.info({
+ message: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_DESCRIPTION"),
+ title: this.$filter('translate')("IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_TITLE")
+ });
+ }
+ } else {
+ // UPDATE MODE
+ this.$scope.isCreateProgress = true;
+ this.$scope.progressMessage = "Updating Asset...";
+ this.disableMenuItems();
+
+
+ // Work around to change the csar version
+ if (this.cacheService.get(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG)) {
+ (<Resource>this.$scope.component).csarVersion = this.cacheService.get(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG);
+ this.cacheService.remove(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG);
+ }
+
+ this.$scope.component.updateComponent().then(onSuccessUpdate, onFailed);
+ }
+ return deferred.promise;
+ };
+
+ this.$scope.revert = (): void => {
+ //in state of import file leave the file in place
+ if (this.$scope.component.isResource() && (<Resource>this.$scope.component).importedFile) {
+ let tempFile: Sdc.Directives.FileUploadModel = (<Resource>this.$scope.component).importedFile;
+ this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent);
+ (<Resource>this.$scope.component).importedFile = tempFile;
+ } else {
+ this.$scope.component = this.ComponentFactory.createComponent(this.$scope.originComponent);
+ }
+
+ };
+
+ this.$scope.changeLifecycleState = (state: string): void => {
+ if (this.isNeedSave() && state !== 'deleteVersion') {
+ this.$scope.save().then(() => {
+ changeLifecycleState(state);
+ })
+ } else {
+ changeLifecycleState(state);
+ }
+ };
+
+ let defaultActionAfterChangeLifecycleState = (): void => {
+ if (this.$state.current.data && this.$state.current.data.unsavedChanges) {
+ this.$state.current.data.unsavedChanges = false;
+ }
+ this.$state.go('dashboard');
+ };
+
+ let changeLifecycleState = (state: string) => {
+ if ('monitor' === state) {
+ this.$state.go('workspace.distribution');
+ return;
+ }
+
+ let data = this.$scope.changeLifecycleStateButtons[state];
+ let onSuccess = (component: Models.Components.Component): void => {
+ //Updating the component from server response
+
+ //the server returns only metaData (small component) except checkout (Full component) ,so we update only the statuses of distribution & lifecycle
+ this.$scope.component.lifecycleState = component.lifecycleState;
+ this.$scope.component.distributionStatus = component.distributionStatus;
+
+ switch (data.url) {
+ case 'lifecycleState/CHECKOUT':
+ // only checkOut get the full component from server
+ this.$scope.component = component;
+ // Work around to change the csar version
+ if (this.cacheService.get(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG)) {
+ (<Resource>this.$scope.component).csarVersion = this.cacheService.get(Utils.Constants.CHANGE_COMPONENT_CSAR_VERSION_FLAG);
+ }
+
+ //when checking out a minor version uuid remains
+ let bcComponent: Sdc.Models.Components.Component = _.find(this.components, (item) => {
+ return item.uuid === component.uuid;
+ });
+ if (bcComponent) {
+ this.components[this.components.indexOf(bcComponent)] = component;
+ } else {
+ //when checking out a major(certified) version
+ this.components.unshift(component);
+ }
+
+ this.$state.go(this.$state.current.name, {
+ id: component.uniqueId,
+ type: component.componentType.toLowerCase(),
+ components: this.components
+ });
+ this.Notification.success({
+ message: this.$filter('translate')("CHECKOUT_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("CHECKOUT_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ case 'lifecycleState/CHECKIN':
+ defaultActionAfterChangeLifecycleState();
+ this.Notification.success({
+ message: this.$filter('translate')("CHECKIN_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("CHECKIN_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ case 'lifecycleState/UNDOCHECKOUT':
+ defaultActionAfterChangeLifecycleState();
+ this.Notification.success({
+ message: this.$filter('translate')("DELETE_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("DELETE_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ case 'lifecycleState/certificationRequest':
+ defaultActionAfterChangeLifecycleState();
+ this.Notification.success({
+ message: this.$filter('translate')("SUBMIT_FOR_TESTING_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("SUBMIT_FOR_TESTING_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ //Tester Role
+ case 'lifecycleState/failCertification':
+ defaultActionAfterChangeLifecycleState();
+ this.Notification.success({
+ message: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ case 'lifecycleState/certify':
+ defaultActionAfterChangeLifecycleState();
+ this.Notification.success({
+ message: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("ACCEPT_TESTING_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ //DE203504 Bug Fix Start
+ case 'lifecycleState/startCertification':
+ this.initChangeLifecycleStateButtons();
+ this.Notification.success({
+ message: this.$filter('translate')("START_TESTING_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("START_TESTING_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ case 'lifecycleState/cancelCertification':
+ this.initChangeLifecycleStateButtons();
+ this.Notification.success({
+ message: this.$filter('translate')("CANCEL_TESTING_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("CANCEL_TESTING_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ //Ops Role
+ case 'distribution/PROD/activate':
+ this.initChangeLifecycleStateButtons();
+ this.Notification.success({
+ message: this.$filter('translate')("DISTRIBUTE_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("DISTRIBUTE_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ //Governor Role
+ case 'distribution-state/reject':
+ this.initChangeLifecycleStateButtons();
+ this.Notification.success({
+ message: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("REJECT_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ case 'distribution-state/approve':
+ this.initChangeLifecycleStateButtons();
+ this.$state.go('catalog');
+ this.Notification.success({
+ message: this.$filter('translate')("APPROVE_SUCCESS_MESSAGE_TEXT"),
+ title: this.$filter('translate')("APPROVE_SUCCESS_MESSAGE_TITLE")
+ });
+ break;
+ //DE203504 Bug Fix End
+
+ default :
+ defaultActionAfterChangeLifecycleState();
+
+ }
+ if (data.url != 'lifecycleState/CHECKOUT') {
+ this.$scope.isLoading = false;
+ }
+ };
+ //this.$scope.isLoading = true;
+ this.ChangeLifecycleStateHandler.changeLifecycleState(this.$scope.component, data, this.$scope, onSuccess);
+ };
+
+ this.$scope.enabledTabs = (): void => {
+ this.$scope.leftBarTabs.menuItems.forEach((item: Utils.MenuItem) => {
+ item.isDisabled = false;
+ });
+ };
+
+ this.$scope.isViewMode = (): boolean => {
+ return this.$scope.mode === Utils.Constants.WorkspaceMode.VIEW;
+ };
+
+ this.$scope.isDesigner = (): boolean => {
+ return this.role == Utils.Constants.Role.DESIGNER;
+ };
+
+ this.$scope.isDisableMode = (): boolean => {
+ return this.$scope.mode === Utils.Constants.WorkspaceMode.VIEW && this.$scope.component.lifecycleState === Utils.Constants.ComponentState.NOT_CERTIFIED_CHECKIN;
+ };
+
+ this.$scope.showFullIcons = (): boolean => {
+ //we show revert and save icons only in general\icon view
+ return this.$state.current.name === Utils.Constants.States.WORKSPACE_GENERAL ||
+ this.$state.current.name === Utils.Constants.States.WORKSPACE_ICONS;
+ };
+
+ this.$scope.isCreateMode = (): boolean => {
+ return this.$scope.mode === Utils.Constants.WorkspaceMode.CREATE;
+ };
+
+ this.$scope.isEditMode = (): boolean => {
+ return this.$scope.mode === Utils.Constants.WorkspaceMode.EDIT;
+ };
+
+ this.$scope.goToBreadcrumbHome = (): void => {
+ let bcHome: Sdc.Utils.MenuItemGroup = this.$scope.breadcrumbsModel[0];
+ this.$state.go(bcHome.menuItems[bcHome.selectedIndex].state);
+ };
+
+ this.$scope.showLifecycleIcon = (): boolean => {
+ return this.role == Utils.Constants.Role.DESIGNER ||
+ this.role == Utils.Constants.Role.PRODUCT_MANAGER;
+ };
+
+ this.$scope.getStatus = (): string => {
+ if (this.$scope.isCreateMode()) {
+ return 'IN DESIGN';
+ }
+
+ return this.$scope.component.getStatus(this.sdcMenu);
+ };
+
+ this.initMenuItems();
+
+ this.$scope.showChangeStateButton = (): boolean => {
+ let result: boolean = true;
+ if (!this.$scope.component.isLatestVersion() && Utils.Constants.Role.OPS != this.role && Utils.Constants.Role.GOVERNOR != this.role) {
+ result = false;
+ }
+ if (this.role === Utils.Constants.Role.PRODUCT_MANAGER && !this.$scope.component.isProduct()) {
+ result = false;
+ }
+ if ((this.role === Utils.Constants.Role.DESIGNER || this.role === Utils.Constants.Role.TESTER)
+ && this.$scope.component.isProduct()) {
+ result = false;
+ }
+ if (Utils.Constants.ComponentState.NOT_CERTIFIED_CHECKOUT === this.$scope.component.lifecycleState && this.$scope.isViewMode()) {
+ result = false;
+ }
+ if (Utils.Constants.ComponentState.CERTIFIED != this.$scope.component.lifecycleState &&
+ (Utils.Constants.Role.OPS == this.role || Utils.Constants.Role.GOVERNOR == this.role)) {
+ result = false;
+ }
+ return result;
+ };
+
+ this.$scope.updateSelectedMenuItem = (): void => {
+ let selectedItem: Sdc.Utils.MenuItem = _.find(this.$scope.leftBarTabs.menuItems, (item: Sdc.Utils.MenuItem) => {
+ return item.state === this.$state.current.name;
+ });
+ this.$scope.leftBarTabs.selectedIndex = selectedItem ? this.$scope.leftBarTabs.menuItems.indexOf(selectedItem) : 0;
+ };
+
+ this.$scope.$watch('$state.current.name', (newVal: string): void => {
+ if (newVal) {
+ this.$scope.isComposition = (newVal.indexOf(Utils.Constants.States.WORKSPACE_COMPOSITION) > -1);
+ this.$scope.isDeployment = (newVal.indexOf(Utils.Constants.States.WORKSPACE_DEPLOYMENT) > -1);
+ }
+ });
+ };
+
+ private initAfterScope = (): void => {
+ // In case user select csar from the onboarding modal, need to disable checkout and submit for testing.
+ if (this.$state.params['disableButtons'] === true) {
+ this.$scope.uploadFileChangedInGeneralTab();
+ }
+ };
+
+ private initVersionObject = (): void => {
+ this.$scope.versionsList = (this.$scope.component.getAllVersionsAsSortedArray()).reverse();
+ this.$scope.changeVersion = {selectedVersion: _.find(this.$scope.versionsList, {versionId: this.$scope.component.uniqueId})};
+ };
+
+ private getNewComponentBreadcrumbItem = (): Utils.MenuItem => {
+ let text = "";
+ if (this.$scope.component.isResource() && (<Resource>this.$scope.component).isCsarComponent()) {
+ text = this.$scope.component.getComponentSubType() + ': ' + this.$scope.component.name;
+ } else {
+ text = 'Create new ' + this.$state.params['type'];
+ }
+ return new Utils.MenuItem(text, null, Utils.Constants.States.WORKSPACE_GENERAL, 'goToState', [this.$state.params]);
+ };
+
+ private updateMenuItemByRole = (menuItems: Array<Utils.MenuItem>, role: string) => {
+ let tempMenuItems: Array<Utils.MenuItem> = new Array<Utils.MenuItem>();
+ menuItems.forEach((item: Utils.MenuItem) => {
+ //remove item if role is disabled
+ if (!(item.disabledRoles && item.disabledRoles.indexOf(role) > -1)) {
+ tempMenuItems.push(item);
+ }
+ });
+ return tempMenuItems;
+ };
+
+ private initBreadcrumbs = () => {
+ this.components = this.cacheService.get('breadcrumbsComponents');
+ let breadcrumbsComponentsLvl = this.MenuHandler.generateBreadcrumbsModelFromComponents(this.components, this.$scope.component);
+
+ if (this.$scope.isCreateMode()) {
+ let createItem = this.getNewComponentBreadcrumbItem();
+ if (!breadcrumbsComponentsLvl.menuItems) {
+ breadcrumbsComponentsLvl.menuItems = [];
+ }
+ breadcrumbsComponentsLvl.menuItems.unshift(createItem);
+ breadcrumbsComponentsLvl.selectedIndex = 0;
+ }
+
+ this.$scope.breadcrumbsModel = [breadcrumbsComponentsLvl, this.$scope.leftBarTabs];
+ };
+
+ private initMenuItems() {
+
+ let inCreateMode = this.$scope.isCreateMode();
+ this.$scope.leftBarTabs = new Utils.MenuItemGroup();
+ this.$scope.leftBarTabs.menuItems = this.updateMenuItemByRole(this.sdcMenu.component_workspace_menu_option[this.$scope.component.getComponentSubType()], this.role);
+
+ this.$scope.leftBarTabs.menuItems.forEach((item: Utils.MenuItem) => {
+ item.params = [item.state];
+ item.callback = this.$scope.onMenuItemPressed;
+ item.isDisabled = (inCreateMode && Utils.Constants.States.WORKSPACE_GENERAL != item.state) ||
+ (Utils.Constants.States.WORKSPACE_DEPLOYMENT === item.state && this.$scope.component.groups.length === 0 && this.$scope.component.isResource());
+ });
+
+ if (this.cacheService.get('breadcrumbsComponents')) {
+ this.initBreadcrumbs();
+ } else {
+ let onSuccess = (components: Array<Models.Components.Component>) => {
+ this.cacheService.set('breadcrumbsComponents', components);
+ this.initBreadcrumbs();
+ };
+ this.EntityService.getCatalog().then(onSuccess); //getAllComponents() doesnt return components from catalog
+ }
+ }
+
+ private disableMenuItems() {
+ this.$scope.leftBarTabs.menuItems.forEach((item: Utils.MenuItem) => {
+ item.params = [item.state];
+ item.callback = this.$scope.onMenuItemPressed;
+ item.isDisabled = (Utils.Constants.States.WORKSPACE_GENERAL != item.state);
+ });
+ }
+
+ private enableMenuItems() {
+ this.$scope.leftBarTabs.menuItems.forEach((item: Utils.MenuItem) => {
+ item.params = [item.state];
+ item.callback = this.$scope.onMenuItemPressed;
+ item.isDisabled = false;
+ });
+ }
+
+ }
+}
+
+
diff --git a/catalog-ui/app/scripts/view-models/workspace/workspace-view.html b/catalog-ui/app/scripts/view-models/workspace/workspace-view.html
new file mode 100644
index 0000000000..118f7474be
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/workspace-view.html
@@ -0,0 +1,86 @@
+<div class="sdc-workspace-container">
+ <loader data-display="isLoading"></loader>
+
+ <!-- HEADER -->
+<!--
+ <ecomp-header menu-data="menuItems" version="{{version}}"></ecomp-header>
+-->
+
+ <div class="w-sdc-main-container">
+
+ <div class="w-sdc-left-sidebar" data-ng-if="!isComposition">
+ <div class="i-sdc-left-sidebar-item">
+ <expand-collapse-menu-box menu-items-group="leftBarTabs" menu-title="{{menuComponentTitle}}" parent-scope="this"> </expand-collapse-menu-box>
+ </div>
+ </div>
+
+ <div include-padding="true" class="w-sdc-main-right-container" data-ng-class="{'composition':isComposition}">
+
+ <!--<div class="w-sdc-main-right-container" data-ng-class="{'composition':isComposition}">-->
+ <loader data-display="isCreateProgress" relative="true"></loader>
+
+ <div class="sdc-workspace-top-bar">
+ <div class="version-container">
+
+
+ <span data-ng-if="!isCreateMode() && !component.isLatestVersion()" class="not-latest"></span>
+ <select class="version-selector" data-ng-if="!isCreateMode()" data-tests-id="versionHeader" data-ng-model="changeVersion.selectedVersion"
+ ng-options="'V'+version.versionNumber for version in versionsList" data-ng-change="onVersionChanged(changeVersion.selectedVersion.versionId)">
+ </select>
+ </div>
+
+ <div class="lifecycle-state">
+ <div data-ng-show="showLifecycleIcon()" class="lifecycle-state-icon" data-ng-class="{'in-design-status-icon': isCreateMode(), '{{sdcMenu.LifeCycleStatuses[component.lifecycleState].icon}}': !isCreateMode()}"></div>
+ <span class="lifecycle-state-text" data-tests-id="formlifecyclestate">{{getStatus()}}</span>
+ </div>
+
+
+ <div class="progress-container" >
+ <top-progress class="general-view-top-progress" progress-value="progressService.getProgressValue(component.uniqueId)" progress-message="progressMessage"></top-progress>
+ </div>
+
+ <div class="sdc-workspace-top-bar-buttons">
+
+ <span ng-if="!isCreateMode() && !component.isLatestVersion() && !showChangeStateButton()">Switch to the&nbsp;<a ng-click="getLatestVersion()">latest version</a></span>
+
+ <button ng-if="isDesigner() && !isCreateMode()"
+ data-ng-class="{'disabled' :!isValidForm || isDisableMode() || isViewMode()}"
+ ng-click="save()"
+ class="tlv-btn blue"
+ data-tests-id="create/save"
+ data-ng-show="showFullIcons()"
+ sdc-smart-tooltip="">Update</button>
+
+ <button ng-repeat="(key,button) in changeLifecycleStateButtons"
+ ng-click="changeLifecycleState(key)"
+ ng-if="showChangeStateButton() && key != 'deleteVersion'"
+ data-ng-disabled="isCreateMode() || button.disabled || disabledButtons || !isValidForm"
+ class="change-lifecycle-state-btn tlv-btn"
+ ng-class="$first ? 'outline green' : 'grey'"
+ data-tests-id="{{button.text | testsId}}">
+ {{button.text}}
+ </button>
+
+ <button ng-if="!isViewMode() && isCreateMode()" data-ng-disabled="!isValidForm || isDisableMode() || isLoading" ng-click="save()" class="tlv-btn outline green" data-tests-id="create/save">Create</button>
+
+ <span data-ng-if="isDesigner() && !isCreateMode() && component.lifecycleState === 'NOT_CERTIFIED_CHECKOUT'" sdc-smart-tooltip=""
+ data-ng-class="{'disabled' : !isValidForm || isDisableMode() || isViewMode()}" ng-click="changeLifecycleState('deleteVersion')"
+ class="sprite-new delete-btn" data-tests-id="delete_version" sdc-smart-tooltip="">Delete</span>
+
+ <span data-ng-if="isDesigner()" data-ng-class="{'disabled' :isDisableMode() || isViewMode()}" ng-click="revert()" class="sprite-new revert-btn" data-tests-id="revert"
+ data-ng-show="showFullIcons()" sdc-smart-tooltip="">Revert</span>
+
+ <span data-ng-if="isComposition && !component.isProduct()" class="sprite-new print-screen-btn" entity="component" print-graph-screen data-tests-id="printScreen"></span>
+ <span class="delimiter"></span>
+ <span class="sprite-new x-btn" data-ng-click="goToBreadcrumbHome()" sdc-smart-tooltip="">Close</span>
+
+ </div>
+ </div>
+
+ <div data-ng-if="component.creationDate && (!isComposition && !isDeployment)" class="sdc-asset-creation-info"><b>Created:</b>&nbsp;{{component.creationDate | date:'MM/dd/yyyy'}},&nbsp;{{component.creatorFullName}}&nbsp;|&nbsp;<b>Modifed:</b>&nbsp;{{component.lastUpdateDate | date:'MM/dd/yyyy'}}&nbsp;|&nbsp;<b>UUID:</b>&nbsp;{{component.uuid}}<b>&nbsp;Invariant UUID:</b>&nbsp;{{component.invariantUUID}}</div>
+
+ <div class="w-sdc-main-container-body-content" data-ng-class="{'third-party':thirdParty}" data-ui-view></div>
+ </div>
+ </div>
+ <top-nav search-bind="search.filterTerm" hide-search="true" menu-model="breadcrumbsModel" version="{{version}}"></top-nav>
+</div>
diff --git a/catalog-ui/app/scripts/view-models/workspace/workspace.less b/catalog-ui/app/scripts/view-models/workspace/workspace.less
new file mode 100644
index 0000000000..d8bff1b634
--- /dev/null
+++ b/catalog-ui/app/scripts/view-models/workspace/workspace.less
@@ -0,0 +1,144 @@
+.sdc-workspace-container {
+ .bg_p;
+
+ .add-btn {
+ .f-color.a;
+ .f-type._12_m;
+ .hand;
+ float: right;
+ margin-bottom: 15px;
+
+ &:before {
+ .sprite-new;
+ .plus-icon;
+ margin-right: 5px;
+ content: "";
+
+ }
+ &:hover {
+ .f-color.b;
+ &:before {
+ .sprite-new;
+ .plus-icon-hover;
+ }
+ }
+
+ }
+ .w-sdc-left-sidebar {
+ padding: 3px 3px 0px 0px;
+ background-color: @main_color_p;
+ box-shadow: 7px -3px 6px -8px @main_color_n;
+ z-index: 2;
+ }
+
+ .sdc-asset-creation-info {
+ .n_12_r;
+ float: right;
+ margin: 8px 20px 0 0;
+ }
+
+ .w-sdc-main-right-container {
+
+ padding: 0px 0px 0px 0px;
+ background-color: @main_color_p;
+ z-index: 1;
+
+ .sdc-workspace-top-bar {
+ height: @action_nav_height;
+ padding: 12px 10px 0px 50px;
+ border-bottom: 1px solid @main_color_o;
+ display: flex;
+ justify-content: space-between;
+
+ .version-container {
+
+ }
+
+ .progress-container {
+ flex-grow: 4;
+ z-index: 10000000;
+
+ .general-view-top-progress {
+ width: 30%;
+ margin: 0 auto;
+ }
+ }
+
+ .not-latest {
+ position: absolute;
+ left: 24px;
+ top: 20px;
+ .sprite-new;
+ .asdc-warning;
+ }
+
+ .sdc-workspace-top-bar-buttons {
+
+ > button, > span:not(.delimiter) {
+ margin-right: 10px;
+ vertical-align: middle;
+ .hand;
+
+ &.sprite-new {
+ text-indent: 100%;
+ }
+ &.disabled, &:hover.disabled {
+ pointer-events: none;
+ }
+ }
+ .delimiter {
+ height: 32px;
+ width: 1px;
+ background-color: #959595;
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 20px;
+ }
+
+ }
+
+ .lifecycle-state {
+ padding: 7px 0 0 10px;
+ margin: 2px 0 7px 10px;
+ border-left: 1px solid @main_color_o;
+ line-height: 15px;
+ font-family: @font-omnes-medium;
+ color: @main_color_m;
+
+ .lifecycle-state-icon {
+ .sprite-new;
+ }
+ .lifecycle-state-text {
+
+ font-weight: bold;
+ text-transform: uppercase;
+ vertical-align: top;
+ padding: 3px;
+ }
+ }
+
+ .version-selector {
+ // float:left;
+ background-color: transparent;
+ border: none;
+ margin-top: 6px;
+ }
+ }
+ .w-sdc-main-container-body-content {
+ height:100%;
+
+ text-align: center;
+ align-items: center;
+ padding: 40px 14% 20px 14%;
+ &.third-party {
+ text-align: left;
+ padding: 0;
+ position: absolute;
+ top: @action_nav_height;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ }
+ }
+ }
+}
diff --git a/catalog-ui/app/styles/animation.less b/catalog-ui/app/styles/animation.less
new file mode 100644
index 0000000000..014b33a454
--- /dev/null
+++ b/catalog-ui/app/styles/animation.less
@@ -0,0 +1,51 @@
+.animation_flex (@value){
+ -webkit-animation: @value;
+ -moz-animation: @value;
+ -o-animation: @value;
+ -ms-transition: @value;
+ animation: @value;
+}
+
+.animation (@name, @duration: 300ms, @delay: 0, @ease: ease) {
+ -webkit-animation: @name @duration @delay @ease;
+ -moz-animation: @name @duration @delay @ease;
+ -o-animation: @name @duration @delay @ease;
+ -ms-animation: @name @duration @delay @ease;
+ animation: @name @duration @delay @ease;
+}
+
+/* ============ flash ============ */
+.flash-frames () {
+ 0% { opacity: 1; }
+ 20% { opacity: 1; }
+ 50% { opacity: 0; }
+ 80% { opacity: 1; }
+ 100% { opacity: 1; }
+}
+@-webkit-keyframes flash {.flash-frames;} /* Safari, Chrome and Opera > 12.1 */
+@-moz-keyframes flash {.flash-frames;} /* Firefox < 16 */
+@-o-keyframes flash {.flash-frames;} /* Opera < 12.1 */
+@-ms-keyframes flash {.flash-frames;} /* Internet Explorer */
+@keyframes flash {.flash-frames;}
+
+/* ============ fadein ============ */
+.fadein-frames () {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+@-webkit-keyframes fadein {.fadein-frames;} /* Safari, Chrome and Opera > 12.1 */
+@-moz-keyframes fadein {.fadein-frames;} /* Firefox < 16 */
+@-o-keyframes fadein {.fadein-frames;} /* Opera < 12.1 */
+@-ms-keyframes fadein {.fadein-frames;} /* Internet Explorer */
+@keyframes fadein {.fadein-frames;}
+
+/* ============ fadeout ============ */
+.fadeout-frames () {
+ from { opacity: 1; }
+ to { opacity: 0; }
+}
+@-webkit-keyframes fadeout {.fadeout-frames;} /* Safari, Chrome and Opera > 12.1 */
+@-moz-keyframes fadeout {.fadeout-frames;} /* Firefox < 16 */
+@-o-keyframes fadeout {.fadeout-frames;} /* Opera < 12.1 */
+@-ms-keyframes fadeout {.fadeout-frames;} /* Internet Explorer */
+@keyframes fadeout {.fadeout-frames;}
diff --git a/catalog-ui/app/styles/app.less b/catalog-ui/app/styles/app.less
new file mode 100644
index 0000000000..cec5b86753
--- /dev/null
+++ b/catalog-ui/app/styles/app.less
@@ -0,0 +1,122 @@
+.sdc-main-container{
+ width: 100%;
+ height: 100%;
+}
+
+// injector:less
+@import 'animation.less';
+@import 'buttons.less';
+@import 'dark-header.less';
+@import 'fonts.less';
+@import 'form-elements.less';
+@import 'global.less';
+@import 'layout/header.less';
+@import 'layout/main.less';
+@import 'layout/sidebar.less';
+@import 'mixins.less';
+@import 'mixins_old.less';
+@import 'modal.less';
+@import 'scroller.less';
+@import 'sprite-old.less';
+@import 'sprite-product-icons.less';
+@import 'sprite-resource-icons.less';
+@import 'sprite-services-icons.less';
+@import 'sprite.less';
+@import 'table-flex.less';
+@import 'tlv-buttons.less';
+@import 'tlv-checkbox.less';
+@import 'tlv-loader.less';
+@import 'tlv-sprite.less';
+@import 'tooltips.less';
+@import 'variables-old.less';
+@import 'variables.less';
+@import 'welcome-sprite.less';
+@import 'welcome-style.less';
+@import '../scripts/directives/ecomp-header/ecomp-header.less';
+@import '../scripts/directives/edit-name-popover/edit-name-popover.less';
+@import '../scripts/directives/elements/checkbox/checkbox.less';
+@import '../scripts/directives/elements/radiobutton/radiobutton.less';
+@import '../scripts/directives/ellipsis/ellipsis-directive.less';
+@import '../scripts/directives/file-upload/file-upload.less';
+@import '../scripts/directives/graphs-v2/composition-graph/composition-graph.less';
+@import '../scripts/directives/graphs-v2/deployment-graph/deployment-graph.less';
+@import '../scripts/directives/graphs-v2/palette/palette.less';
+@import '../scripts/directives/graphs-v2/relation-menu/relation-menu.less';
+@import '../scripts/directives/info-tooltip/info-tooltip.less';
+@import '../scripts/directives/layout/top-nav/top-nav.less';
+@import '../scripts/directives/layout/top-progress/top-progress.less';
+@import '../scripts/directives/loader/loader-directive.less';
+@import '../scripts/directives/modal/sdc-modal.less';
+@import '../scripts/directives/page-scroller/page-scroller.less';
+@import '../scripts/directives/property-types/data-type-fields-structure/data-type-fields-structure.less';
+@import '../scripts/directives/property-types/type-list/type-list-directive.less';
+@import '../scripts/directives/property-types/type-map/type-map-directive.less';
+@import '../scripts/directives/sdc-tabs/sdc-single-tab/sdc-single-tab.less';
+@import '../scripts/directives/sdc-tabs/sdc-tabs.less';
+@import '../scripts/directives/structure-tree/structure-tree-directive.less';
+@import '../scripts/directives/tag/tag-directive.less';
+@import '../scripts/directives/tutorial/tutorial-directive.less';
+@import '../scripts/directives/user-header-details/user-header-details-directive.less';
+@import '../scripts/directives/utils/expand-collapse-menu-box/expand-collapse-menu-box.less';
+@import '../scripts/directives/utils/expand-collapse/expand-collapse.less';
+@import '../scripts/directives/utils/page-selector/page-selector.less';
+@import '../scripts/directives/utils/sdc-tags/sdc-tags.less';
+@import '../scripts/directives/utils/sdc_messages/sdc-messages.less';
+@import '../scripts/directives/utils/wizard_steps/sdc-wizard-steps.less';
+@import '../scripts/view-models/admin-dashboard/add-category-modal/add-category-modal-view.less';
+@import '../scripts/view-models/admin-dashboard/admin-dashboard.less';
+@import '../scripts/view-models/admin-dashboard/category-management/category-management.less';
+@import '../scripts/view-models/admin-dashboard/user-management/user-management.less';
+@import '../scripts/view-models/catalog/catalog.less';
+@import '../scripts/view-models/component-viewer/activity-log/activity-log-view.less';
+@import '../scripts/view-models/component-viewer/component-viewer.less';
+@import '../scripts/view-models/component-viewer/properties/properties-view.less';
+@import '../scripts/view-models/dashboard/dashboard.less';
+@import '../scripts/view-models/forms/artifact-form/artifact-form.less';
+@import '../scripts/view-models/forms/env-parameters-form/env-parameters-form.less';
+@import '../scripts/view-models/forms/property-form/property-form.less';
+@import '../scripts/view-models/forms/resource-instance-name-form/resource-instance-name.less';
+@import '../scripts/view-models/modals/confirmation-modal/confirmation-modal.less';
+@import '../scripts/view-models/modals/email-modal/email-modal.less';
+@import '../scripts/view-models/modals/error-modal/error.less';
+@import '../scripts/view-models/modals/message-modal/message-client-modal/client-message-modal.less';
+@import '../scripts/view-models/modals/message-modal/message-server-modal/server-message-modal.less';
+@import '../scripts/view-models/modals/onboarding-modal/onboarding-modal.less';
+@import '../scripts/view-models/onboard-vendor/onboard-vendor.less';
+@import '../scripts/view-models/preloading/preloading-view.less';
+@import '../scripts/view-models/support/support.less';
+@import '../scripts/view-models/tabs/general-tab.less';
+@import '../scripts/view-models/tabs/hierarchy/hierarchy.less';
+@import '../scripts/view-models/tutorial-end/tutorial-end.less';
+@import '../scripts/view-models/wizard/artifact-deployment-step/artifact-deployment-step.less';
+@import '../scripts/view-models/wizard/artifact-form-step/artifact-form-step.less';
+@import '../scripts/view-models/wizard/artifact-information-step/artifact-information-step.less';
+@import '../scripts/view-models/wizard/general-step/general-step.less';
+@import '../scripts/view-models/wizard/hierarchy-step/hierarchy-step.less';
+@import '../scripts/view-models/wizard/icons-step/icons-step.less';
+@import '../scripts/view-models/wizard/properties-step/properties-step.less';
+@import '../scripts/view-models/wizard/property-form/property-form.less';
+@import '../scripts/view-models/wizard/wizard-creation-base.less';
+@import '../scripts/view-models/workspace/tabs/activity-log/activity-log.less';
+@import '../scripts/view-models/workspace/tabs/attributes/attributes.less';
+@import '../scripts/view-models/workspace/tabs/composition/composition.less';
+@import '../scripts/view-models/workspace/tabs/composition/tabs/artifacts/artifacts.less';
+@import '../scripts/view-models/workspace/tabs/composition/tabs/details/details.less';
+@import '../scripts/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties.less';
+@import '../scripts/view-models/workspace/tabs/composition/tabs/relations/relations.less';
+@import '../scripts/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts.less';
+@import '../scripts/view-models/workspace/tabs/deployment/deployment.less';
+@import '../scripts/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal.less';
+@import '../scripts/view-models/workspace/tabs/distribution/distribution.less';
+@import '../scripts/view-models/workspace/tabs/general/general.less';
+@import '../scripts/view-models/workspace/tabs/icons/icons.less';
+@import '../scripts/view-models/workspace/tabs/information-artifacts/information-artifacts.less';
+@import '../scripts/view-models/workspace/tabs/inputs/inputs.less';
+@import '../scripts/view-models/workspace/tabs/inputs/resource-input/resource-inputs.less';
+@import '../scripts/view-models/workspace/tabs/inputs/service-input/service-inputs.less';
+@import '../scripts/view-models/workspace/tabs/product-hierarchy/product-hierarchy.less';
+@import '../scripts/view-models/workspace/tabs/properties/properties.less';
+@import '../scripts/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less';
+@import '../scripts/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less';
+@import '../scripts/view-models/workspace/workspace.less';
+// endinjector:less
diff --git a/catalog-ui/app/styles/buttons.less b/catalog-ui/app/styles/buttons.less
new file mode 100644
index 0000000000..7215d7a3d4
--- /dev/null
+++ b/catalog-ui/app/styles/buttons.less
@@ -0,0 +1,274 @@
+.w-sdc-btn {
+ .c_4;
+ .hand;
+ .border-radius(5px);
+ border: 0;
+ height: 38px;
+ line-height: 38px;
+ width: 195px;
+ margin: 0 6px;
+ padding: 0;
+ vertical-align: middle;
+ outline: none;
+ &:disabled { .bg_e; }
+}
+
+.w-sdc-btn-small {
+ .c_9;
+ .hand;
+ .border-radius(5px);
+ border: 0;
+ height: 30px;
+ line-height: 30px;
+ width: 71px;
+ margin: 0 6px;
+ padding: 0;
+ outline: none;
+ vertical-align: middle;
+ position: relative;
+ &:disabled { .bg_e; }
+}
+
+
+
+.w-sdc-btn-red-small{
+
+ .w-sdc-btn-small;
+ .bg_h;
+ &:hover:enabled { .bg_h_hover; }
+ padding-left: 15px;
+
+ span {
+ position: absolute;
+ top: 7px;
+ left: 10px;
+ }
+}
+
+.w-sdc-btn-green-small{
+ .w-sdc-btn-small;
+ .bg_z;
+ &:hover:enabled { .bg_z_hover; }
+
+}
+
+
+.w-sdc-btn-light-green {
+ .w-sdc-btn;
+ .bg_z;
+ &:hover:enabled { .bg_z_hover; }
+}
+
+.w-sdc-btn-green {
+ .w-sdc-btn;
+ .bg_d;
+ &:hover:enabled { .bg_d_hover; }
+}
+
+.w-sdc-btn-blue {
+ .w-sdc-btn;
+ .bg_a;
+ &:hover:enabled { .bg_a_hover; }
+}
+
+.w-sdc-btn-dark-gray {
+ .w-sdc-btn;
+ .bg_i;
+ &:hover:enabled { .bg_i_hover; }
+}
+
+.w-sdc-form-action {
+ .c_6;
+ //.bg_d;
+ background-color: #28bd6e; //TO DO: add to variables
+ .border-radius(5px);
+ .hand;
+ border: 0;
+ height: 38px;
+ line-height: 38px;
+ width: 264px;
+
+ &:hover { background-color: #23aa63; } //TO DO: add to variables
+ &:disabled { background-color: #93deb6; } //TO DO: add to variables
+}
+
+/*
+CLASSIC BUTTON
+Examples:
+-------------------------------------------------------------------
+<button class="w-sdc-classic-btn blue">Text 1</button>
+<button class="w-sdc-classic-btn green">Text 1</button>
+<button class="w-sdc-classic-btn gray">Text 1</button>
+<button class="w-sdc-classic-btn white">Text 1</button>
+<button class="w-sdc-classic-btn blue disabled">Text 5</button>
+*/
+.w-sdc-classic-btn {
+ .p_14_m;
+ text-align: center;
+ .border-radius(2px);
+ border-style: solid;
+ border-width: 1px;
+ width: 94px;
+ height: 30px;
+ display: block;
+ vertical-align: middle;
+ line-height: 30px;
+ cursor: pointer;
+ text-decoration: none;
+ box-sizing: border-box;
+ .noselect;
+ position: relative;
+
+ &:focus,
+ &:active:focus {
+ outline: 0 none;
+ }
+
+ &.primary {
+ border-color: @main_color_b;
+ background-color: @main_color_a;
+ color: @main_color_p;
+
+ &:hover {
+ background-color: #1ec2ff;
+ }
+ }
+
+ &.white {
+
+ }
+
+ &.grey {
+
+ }
+
+ &.positive {
+
+ }
+
+ &.negative {
+
+ }
+
+ &.blue {
+ .c_7;
+ ._w-sdc-classic-btn-configuration(#036698, #3b7b9b, #023c59); // Normal
+ &:hover { ._w-sdc-classic-btn-configuration(#036698, #458eb2, #023c59); } // Hover
+ &:active { ._w-sdc-classic-btn-configuration(#036698, #336c88, #023c59); } // Pressed
+ &:focus { &:before { ._w-sdc-classic-btn-focus(#ffffff, 0px); } } // Focus
+ &:disabled,
+ &.disabled {
+ .c_7;
+ opacity: 0.3;
+ ._w-sdc-classic-btn-configuration(#036698, #3b7b9b, #023c59); // Disabled
+ }
+ }
+
+ &.green {
+ .c_7;
+ ._w-sdc-classic-btn-configuration(#25a762, #28bd6e, #235600); // Normal
+ &:hover { ._w-sdc-classic-btn-configuration(#25a762, #2bcd77, #235600); } // Hover
+ &:active { ._w-sdc-classic-btn-configuration(#45a006, #24a862, #235600); } // Pressed
+ &:focus { &:before { ._w-sdc-classic-btn-focus(#ffffff, 0px); } } // Focus
+ &:disabled,
+ &.disabled {
+ .c_7;
+ opacity: 0.3;
+ ._w-sdc-classic-btn-configuration(#45a006, #28bd6e, #235600); // Disabled
+ }
+ }
+
+ &.gray {
+ .b_7;
+ ._w-sdc-classic-btn-configuration(#d8d8d8, #f6f6f6, #aaaaaa); // Normal
+ &:hover { ._w-sdc-classic-btn-configuration(#d8d8d8, #ececec, #aaaaaa); } // Hover
+ &:active { ._w-sdc-classic-btn-configuration(#d8d8d8, #d7d7d7, #aaaaaa); } // Pressed
+ &:focus { &:before { ._w-sdc-classic-btn-focus(#666666, -1px); } } // Focus
+ &:disabled,
+ &.disabled {
+ .b_7;
+ opacity: 0.3;
+ ._w-sdc-classic-btn-configuration(#d8d8d8, #f6f6f6, #aaaaaa); // Disabled
+ }
+ }
+
+ &.white {
+ .b_7;
+ ._w-sdc-classic-btn-configuration(#d8d8d8, #ffffff, #aaaaaa); // Normal
+ &:hover { ._w-sdc-classic-btn-configuration(#d8d8d8, #f6f6f6, #aaaaaa); } // Hover
+ &:active { ._w-sdc-classic-btn-configuration(#d8d8d8, #ececec, #aaaaaa); } // Pressed
+ &:focus { &:before { ._w-sdc-classic-btn-focus(#666666, -1px); } } // Focus
+ &:disabled,
+ &.disabled {
+ .b_7;
+ opacity: 0.3;
+ ._w-sdc-classic-btn-configuration(#d8d8d8, #ffffff, #aaaaaa); // Disabled
+ }
+ }
+}
+
+._w-sdc-classic-btn-configuration(@border-color; @background-color; @shadow-color) {
+ border-color: @border-color;
+ background-color: @background-color;
+ .box-shadow(0px 1px 0.99px 0.01px @shadow-color);
+}
+
+._w-sdc-classic-btn-focus(@border-color, @position) {
+ content: " ";
+ position: absolute;
+ top: @position;
+ left: @position;
+ right: @position;
+ bottom: -1px + @position;
+ border: 1px solid @border-color;
+ z-index: 1;
+ .border-radius(2px);
+}
+
+/*
+WIZARD STEP CIRCLE BUTTON
+Examples:
+-------------------------------------------------------------------
+<button class="w-sdc-wizard-step-btn">1</button>
+<button class="w-sdc-wizard-step-btn selected">2</button>
+<button class="w-sdc-wizard-step-btn valid">3</button>
+<button class="w-sdc-wizard-step-btn">4</button>
+<button class="w-sdc-wizard-step-btn disabled">5</button>
+*/
+.w-sdc-wizard-step-btn {
+ ._w-sdc-wizard-step-btn(18px);
+}
+._w-sdc-wizard-step-btn(@circle-radius) {
+ // I'm not using border, so the text inside will be vertical aligned.
+ .circle(@circle-radius * 2, #f6f6f6);
+ .noselect;
+ outline: 0 none;
+ position: relative;
+ vertical-align: top;
+ cursor: pointer;
+ vertical-align: middle;
+ border: none;
+ .box-shadow(inset 0px 0px 0px 1px #d8d8d8);
+ .b_17;
+
+ &:active { .box-shadow(none); background-color: #eaeaea;} // Pressed
+ &.disabled { color: #a8b3b9; cursor: default; }
+ &.disabled:active { .box-shadow(inset 0px 0px 0px 1px #d8d8d8); background-color: #f6f6f6; }
+ &.valid {
+ .circle(@circle-radius * 2, #ffffff);
+ .box-shadow(inset 0px 0px 0px 2px #28bd6e);
+ .b_17;
+ }
+ &.selected {
+ .c_17;
+ background-color: #3b7b9b;
+ .box-shadow(none);
+ &:active {background-color: #336c88;} // Pressed
+ }
+}
+
+
+
+
+
+
diff --git a/catalog-ui/app/styles/dark-header.less b/catalog-ui/app/styles/dark-header.less
new file mode 100644
index 0000000000..38b4175daa
--- /dev/null
+++ b/catalog-ui/app/styles/dark-header.less
@@ -0,0 +1,51 @@
+.w-sdc-dark-header {
+ .c_11;
+ .bg_i;
+ height: 94px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+
+ .w-sdc-dark-header-logo {
+ .hand;
+ height: 92px;
+ width: 240px;
+ padding: 28px 28px 28px 60px;
+ .flex-fixed(240px);
+
+ .w-sdc-dark-header-logo-icon {
+ display: inline-block;
+ margin-right: 8px;
+ vertical-align: -6px;
+ }
+ a.w-sdc-dark-header-logo-link {
+ .c_5;
+ text-decoration: none;
+ }
+ .w-sdc-dark-header-version{
+ .c_16;
+ .opacity(0.6);
+ }
+ }
+
+ .i-sdc-dark-header-caption {
+ min-width: 175px;
+ text-align: left;
+ cursor: default;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ flex-grow: 10;
+ padding-left: 27px;
+ white-space: nowrap;
+
+ .i-sdc-dark-header-caption-text {
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ }
+
+ .w-sdc-header-user-container {
+ .c_7;
+ }
+}
diff --git a/catalog-ui/app/styles/fonts.less b/catalog-ui/app/styles/fonts.less
new file mode 100644
index 0000000000..752f41f86a
--- /dev/null
+++ b/catalog-ui/app/styles/fonts.less
@@ -0,0 +1,72 @@
+//Omnes_ATT W02 Italic
+@font-face {
+ font-family: omnes-italic;
+ src: url('fonts/OmnesATT/Omnes_ATTW02Italic.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02Italic.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02Italic.ttf');
+}
+
+//Omnes_ATT W02 Light
+@font-face {
+ font-family: omnes-light;
+ src: url('fonts/OmnesATT/Omnes_ATTW02Light.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02Light.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02Light.ttf');
+}
+
+//Omnes_ATT W02 Light Italic
+@font-face {
+ font-family: omnes-light-italic;
+ src: url('fonts/OmnesATT/Omnes_ATTW02LightItalic.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02LightItalic.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02LightItalic.ttf');
+}
+
+//Omnes_ATT W02 Medium
+@font-face {
+ font-family: omnes-medium;
+ src: url('fonts/OmnesATT/Omnes_ATTW02Medium.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02Medium.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02Medium.ttf');
+}
+
+//Omnes_ATT W02 Medium Italic
+@font-face {
+ font-family: omnes-medium-italic;
+ src: url('fonts/OmnesATT/Omnes_ATTW02MediumItalic.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02MediumItalic.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02MediumItalic.ttf');
+}
+
+//Omnes_ATT W02 Regular
+@font-face {
+ font-family: omnes-regular;
+ src: url('fonts/OmnesATT/Omnes_ATTW02.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02.ttf');
+}
+
+//Omnes_ATT W02 Bold
+@font-face {
+ font-family: omnes-bold;
+ src: url('fonts/OmnesATT/Omnes_ATTW02Bold.eot');
+ src: url('fonts/OmnesATT/Omnes_ATTW02Bold.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/Omnes_ATTW02Bold.ttf');
+}
+
+//Omnes_ATT W02 Bold Italic
+@font-face {
+ font-family: omnes-bold-italic;
+ src: url('fonts/OmnesATT/OmnesATTW02BoldItalic.eot');
+ src: url('fonts/OmnesATT/OmnesATTW02BoldItalic.eot?#iefix') format('embedded-opentype'),
+ url('fonts/OmnesATT/OmnesATTW02BoldItalic.ttf');
+}
+
+
+//ClearviewATT Book
+@font-face {
+ font-family: clearview-book;
+ src: url('fonts/ClearviewATT/ClearviewATT-Bk.eot');
+ src: url('fonts/ClearviewATT/ClearviewATT-Bk.eot?#iefix') format('embedded-opentype'),
+ url('fonts/ClearviewATT/ClearviewATT-Bk.ttf');
+}
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.eot b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.eot
new file mode 100644
index 0000000000..83074fbe2b
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.svg b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.svg
new file mode 100644
index 0000000000..564f750d99
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.svg
@@ -0,0 +1,424 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[ClearviewATT Bd]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Copyright (c) Terminal Design, Inc, 2005. All rights reserved.]]></copyright>
+<trademark><![CDATA[Clearview is a trademark of Terminal Design, Inc.]]></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="544" id="ClearviewATT-Bd">
+<font-face font-family="ClearviewATT Bd" panose-1="2 11 5 6 3 5 0 2 0 4" ascent="750" descent="-250" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="484" d="M492 667V0H-8V667H492ZM31 606V65L217 336L31 606ZM453 67V605L268 336L453 67ZM61 35H424L242 299L61 35ZM242 373L419 630H65L242 373Z" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " horiz-adv-x="300" />
+<glyph unicode="!" horiz-adv-x="299" d="M228 667Q223 547 217 429T208 191H92Q89 311 84 429T73 667H228ZM242 61Q242 29 216 9T150 -12Q110 -12 84 8T57 61Q57 91 83 111T149 132Q189 132 215 112T242 61Z" />
+<glyph unicode="&quot;" horiz-adv-x="482" d="M405 448H312L288 700H435L405 448ZM165 448H71L47 700H194L165 448Z" />
+<glyph unicode="#" horiz-adv-x="701" d="M633 456H532L513 335H621L607 231H497L482 113H369L387 231H267L249 113H142L159 231H49L62 335H173L189 456H78L92 558H204L220 667H329L313 558H439L455 667H564L547 558H649L633 456ZM297 456L281 335H405L422 456H297Z" />
+<glyph unicode="$" horiz-adv-x="613" d="M551 209Q551 182 541 153T510 100T455 58T373 35V-46H241V36Q157 42 61 101L120 198Q172 164 216 149T306 134Q418 134 418 200Q418 226 402 240T350 269L207 319Q140 343 108 382T76 480Q76 510 86 538T116 588T168 625T242 645V722H374V644Q412 641 447 631T523 600L472 496Q434 518 394 529T309 541Q263 541 238 528T212 488Q212 465 228 452T293 422L407 383Q483 356 517 315T551 209Z" />
+<glyph unicode="%" horiz-adv-x="794" d="M330 530Q330 495 323 465T300 411T255 375T185 361Q143 361 115 374T71 411T47 464T40 530Q40 565 47 596T70 650T115 686T185 700Q227 700 255 687T299 650T323 596T330 530ZM241 532Q241 552 239 570T231 601T214 621T185 629Q166 629 155 622T138 601T130 570T128 531Q128 483 139 457T185 431Q220 431 230 458T241 532ZM203 0H126L588 700H664L203 0ZM754 163Q754 128 747 98T723 44T679 8T609 -6Q567 -6 539 7T495 44T471 97T464 163Q464 198 471 229T494 283T539 319T609 333Q651 333 678 320T723 283T747 229T754 163ZM665 165Q665 185 663 203T655 234T638 254T609 262Q590 262 579 255T562 234T554 203T552 164Q552 116 563 90T609 64Q644 64 654 91T665 165Z" />
+<glyph unicode="&amp;" horiz-adv-x="667" d="M309 -10Q253 -10 207 2T127 40T76 103T57 193Q57 227 67 255T96 307T138 348T190 381Q161 409 141 445T120 523Q120 553 132 581T168 631T228 666T313 680Q362 680 396 667T451 633T481 585T491 530Q491 474 454 437T357 367L482 201Q484 210 484 225T485 254Q485 278 483 304T477 347H603Q608 325 610 297T613 245Q613 210 604 173T569 108L650 0H501L474 34Q442 14 401 2T309 -10ZM312 98Q338 98 359 104T395 120L253 303Q230 285 215 260T199 203Q199 158 227 128T312 98ZM305 431Q340 457 358 478T376 529Q376 554 363 572T315 590Q284 590 267 574T250 528Q250 505 267 478T305 431Z" />
+<glyph unicode="&apos;" horiz-adv-x="241" d="M165 448H71L47 700H194L165 448Z" />
+<glyph unicode="(" horiz-adv-x="337" d="M178 -139Q116 -38 77 73T38 318Q38 385 48 443T76 557T120 665T178 775L278 749Q229 659 199 553T169 318Q169 190 199 86T278 -112L178 -139Z" />
+<glyph unicode=")" horiz-adv-x="337" d="M38 -112Q87 -18 117 86T147 318Q147 447 118 553T38 749L138 775Q169 719 195 666T239 557T268 444T278 318Q278 184 239 73T138 -139L38 -112Z" />
+<glyph unicode="*" horiz-adv-x="375" d="M243 531V529L319 458L232 399L185 490H183L135 399L49 458L124 529V531L16 543L50 639L146 593V594L127 694H240L222 595V594L317 639L351 544L243 531Z" />
+<glyph unicode="+" horiz-adv-x="613" d="M368 211V5H247V211H45V329H247V532H368V329H568V211H368Z" />
+<glyph unicode="&#x2c;" horiz-adv-x="259" d="M116 -132H20L70 120H221L116 -132Z" />
+<glyph unicode="-" horiz-adv-x="383" d="M48 195V327H335V195H48Z" />
+<glyph unicode="." horiz-adv-x="259" d="M222 61Q222 29 195 9T129 -12Q89 -12 63 8T36 61Q36 91 63 111T129 132Q168 132 195 112T222 61Z" />
+<glyph unicode="/" horiz-adv-x="573" d="M149 -115H22L418 700H546L149 -115Z" />
+<glyph unicode="0" horiz-adv-x="613" d="M568 333Q568 255 556 192T515 83T435 12T307 -13Q230 -13 180 12T99 82T57 191T45 333Q45 411 57 474T99 583T179 653T307 678Q384 678 434 653T514 583T556 475T568 333ZM425 333Q425 383 422 425T408 497T373 544T307 561Q265 561 241 545T206 498T191 426T188 333Q188 285 191 243T205 171T241 122T307 104Q349 104 372 121T407 169T422 242T425 333Z" />
+<glyph unicode="1" horiz-adv-x="613" d="M255 119V511H252L121 430L65 536L281 668H393V119H548V0H96V119H255Z" />
+<glyph unicode="2" horiz-adv-x="613" d="M82 0L61 118Q139 182 201 230T307 318T374 394T397 470Q397 505 375 531T310 558Q264 558 223 537T140 471L65 571Q96 601 125 621T185 654T247 672T317 678Q420 678 479 628T539 484Q539 442 525 400T477 313T384 221T235 119H552V0H82Z" />
+<glyph unicode="3" horiz-adv-x="613" d="M555 202Q555 155 540 116T492 48T409 3T291 -13Q226 -13 167 7T57 67L119 168Q159 138 202 120T285 102Q342 102 377 126T413 199Q413 250 387 268T310 287H212V400H260Q321 400 357 418T393 479Q393 493 389 507T373 533T342 552T291 559Q248 559 208 541T130 489L67 593Q98 616 126 632T183 659T241 673T307 678Q356 678 398 666T470 630T518 570T535 485Q535 437 518 405T461 352V350Q506 337 530 299T555 202Z" />
+<glyph unicode="4" horiz-adv-x="613" d="M480 133V0H342V133H73L40 239L335 666H480V252H572V133H480ZM342 503L188 252H346V503H342Z" />
+<glyph unicode="5" horiz-adv-x="613" d="M552 230Q552 114 485 51T282 -13Q216 -13 164 2T61 55L118 162Q155 135 197 119T283 102Q353 102 383 134T414 230Q414 292 383 320T289 348Q249 348 220 333T164 294L62 362L118 667H514V547H228L197 417L201 415Q232 439 263 449T332 460Q435 460 493 405T552 230Z" />
+<glyph unicode="6" horiz-adv-x="613" d="M564 226Q564 170 546 126T495 51T416 4T313 -13Q179 -13 114 70T48 312Q48 388 61 455T108 571T198 649T339 678Q393 678 438 666T530 624L467 519Q435 540 406 549T340 558Q262 558 224 517T184 385H188Q212 417 249 433T335 449Q387 449 429 434T502 389T548 318T564 226ZM423 223Q423 280 396 306T314 332Q260 332 228 305T196 220Q196 161 227 133T313 104Q423 104 423 223Z" />
+<glyph unicode="7" horiz-adv-x="613" d="M553 558Q486 504 446 455T383 348T354 220T347 50V0H200V33Q200 92 205 162T229 302T280 435T371 544V548H59V667H530L553 558H553Z" />
+<glyph unicode="8" horiz-adv-x="613" d="M562 200Q562 156 551 118T510 50T432 4T308 -13Q233 -13 183 4T104 50T62 117T50 200Q50 259 74 295T154 353V357Q106 380 87 413T68 496Q68 583 124 630T306 678Q544 678 544 496Q544 447 525 414T458 357V353Q512 333 537 296T562 200ZM407 484Q407 526 383 545T306 564Q253 564 229 545T205 484Q205 401 306 401Q407 401 407 484ZM423 198Q423 252 396 274T306 297Q248 297 217 276T186 197Q186 141 216 120T306 98Q365 98 394 119T423 198Z" />
+<glyph unicode="9" horiz-adv-x="613" d="M564 345Q564 175 496 82T279 -11Q219 -11 172 5T81 58L152 150Q170 136 185 127T214 113T244 105T279 102Q352 102 388 143T427 287H423Q395 252 361 235T277 218Q227 218 186 232T113 275T65 345T48 441Q48 496 66 539T117 614T196 661T299 678Q427 678 495 598T564 345ZM413 447Q413 499 388 530T301 561Q244 561 217 530T189 444Q189 396 216 366T300 336Q332 336 353 344T388 367T407 402T413 447Z" />
+<glyph unicode=":" horiz-adv-x="259" d="M220 407Q220 376 194 355T130 334Q92 334 66 355T40 407Q40 438 66 458T130 478Q168 478 194 458T220 407ZM220 61Q220 29 194 9T130 -12Q92 -12 66 8T40 61Q40 91 66 111T130 132Q168 132 194 112T220 61Z" />
+<glyph unicode=";" horiz-adv-x="259" d="M116 -132H20L69 120H220L116 -132ZM229 407Q229 376 204 355T140 334Q102 334 76 355T50 407Q50 438 76 458T140 478Q178 478 203 458T229 407Z" />
+<glyph unicode="&lt;" horiz-adv-x="613" d="M45 210V329L568 544V419L183 271L568 127V5L45 210Z" />
+<glyph unicode="=" horiz-adv-x="613" d="M45 333V450H568V333H45ZM45 101V218H568V101H45Z" />
+<glyph unicode="&gt;" horiz-adv-x="613" d="M45 5V127L430 271L45 419V544L568 329V210L45 5Z" />
+<glyph unicode="?" horiz-adv-x="545" d="M496 493Q496 431 468 388T376 314L336 295Q324 289 317 285T307 275T303 260T302 235V195H166V266Q166 288 168 303T178 330T199 350T233 369L276 389Q297 399 313 408T339 427T355 450T360 482Q360 520 332 540T255 561Q166 561 105 504L37 605Q67 625 94 639T150 662T208 675T274 679Q386 679 441 630T496 493ZM323 60Q323 29 297 9T231 -12Q192 -12 166 8T139 60Q139 90 165 110T230 131Q270 131 296 111T323 60Z" />
+<glyph unicode="@" horiz-adv-x="823" d="M766 299Q766 236 750 189T707 112T646 66T576 51Q547 51 521 66T488 114H487Q467 84 434 69T364 54Q304 54 270 95T236 209Q236 244 245 281T275 349T328 400T408 420Q437 420 457 411T494 380H495L499 410H603L567 141Q565 130 574 122T600 113Q616 113 635 123T670 155T697 214T708 304Q708 358 689 400T635 470T552 513T446 528Q378 528 319 510T217 454T150 361T125 230Q125 96 198 22T413 -53Q540 -53 623 9L656 -38Q599 -78 537 -93T402 -108Q331 -108 268 -86T158 -21T84 85T57 233Q57 315 86 380T167 491T289 561T442 586Q514 586 574 567T676 510T742 420T766 299ZM482 279Q485 307 475 324T427 341Q382 341 361 307T339 199Q339 132 399 132Q434 132 451 152T474 212L482 279Z" />
+<glyph unicode="A" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525Z" />
+<glyph unicode="B" horiz-adv-x="666" d="M615 181Q615 130 598 96T551 40T477 10T379 0H73V662Q133 669 195 672T317 675Q366 675 415 669T503 643T566 588T591 495Q591 433 562 401T482 353V349Q545 336 580 296T615 181ZM448 480Q448 523 421 543T320 563Q309 563 295 563T266 561T240 560T219 558V394Q242 392 265 392T324 392Q388 392 418 409T448 480ZM468 196Q468 230 455 249T420 278T375 290T328 292Q300 292 271 292T218 290V106Q246 104 269 104T327 104Q358 104 383 106T428 118T457 146T468 196Z" />
+<glyph unicode="C" horiz-adv-x="665" d="M640 96Q582 41 520 15T385 -11Q299 -11 237 14T135 85T76 195T56 338Q56 414 77 477T140 584T242 653T381 678Q456 678 517 652T636 567L547 474Q509 516 471 535T387 555Q300 555 254 503T208 337Q208 232 250 171T386 110Q434 110 476 130T561 193L640 96H640Z" />
+<glyph unicode="D" horiz-adv-x="704" d="M648 337Q648 250 624 187T556 82T447 20T303 0H73V662Q122 667 182 671T305 675Q380 675 443 656T551 595T622 490T648 337ZM496 340Q496 380 489 419T462 490T406 541T309 561Q288 561 265 560T219 557V117Q242 116 257 116T291 116Q339 116 377 125T441 160T482 229T496 340Z" />
+<glyph unicode="E" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73Z" />
+<glyph unicode="F" horiz-adv-x="555" d="M219 545V398H490V275H219V0H73V667H519V545H219Z" />
+<glyph unicode="G" horiz-adv-x="732" d="M388 100Q456 100 497 132T539 236V258H381V375H676V303Q676 228 660 170T608 71T517 11T383 -10Q315 -10 255 10T151 73T82 180T56 332Q56 421 82 486T153 594T257 657T382 678Q429 678 467 668T535 643T589 610T629 575L541 482Q503 520 466 537T388 555Q293 555 251 499T208 333Q208 279 217 236T248 163T304 117T388 100Z" />
+<glyph unicode="H" horiz-adv-x="694" d="M475 0V283H219V0H73V667H219V420H475V667H621V0H475Z" />
+<glyph unicode="I" horiz-adv-x="292" d="M73 0V667H219V0H73Z" />
+<glyph unicode="J" horiz-adv-x="490" d="M417 228Q417 176 410 132T380 56T320 5T218 -13Q161 -13 118 2T31 53L96 160Q126 131 150 121T204 111Q236 111 253 130T271 197V667H417V228H417Z" />
+<glyph unicode="K" horiz-adv-x="644" d="M451 0L270 279L219 223V0H73V667H219V389H223L428 667H612L380 376L637 0H451Z" />
+<glyph unicode="L" horiz-adv-x="543" d="M73 0V667H219V123H502V0H73Z" />
+<glyph unicode="M" horiz-adv-x="799" d="M585 0V427H581L399 169L218 425H214V0H73V667H200L399 383L598 667H726V0H585Z" />
+<glyph unicode="N" horiz-adv-x="731" d="M532 0L219 425H215V0H73V667H208L512 249H517V667H658V0H532Z" />
+<glyph unicode="O" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334Z" />
+<glyph unicode="P" horiz-adv-x="634" d="M592 461Q592 411 579 370T538 300T464 254T354 237H219V0H73V662Q116 667 181 671T311 675Q366 675 417 666T507 632T569 566T592 461ZM449 457Q449 514 417 536T312 558Q291 558 269 557T219 554V351H323Q348 351 371 353T411 366T438 398T449 457Z" />
+<glyph unicode="Q" horiz-adv-x="772" d="M716 334Q716 251 691 179T600 54V53Q620 48 632 40T657 24T686 9T730 -2L682 -103Q642 -102 609 -87T546 -55T491 -24T441 -9Q429 -9 419 -10T397 -12Q228 -12 142 76T56 334Q56 497 138 587T386 678Q551 678 633 588T716 334ZM564 334Q564 383 557 424T531 496T476 542T386 559Q330 559 296 543T242 496T215 425T208 334Q208 285 215 244T242 172T297 125T386 108Q441 108 475 125T529 172T556 243T564 334Z" />
+<glyph unicode="R" horiz-adv-x="645" d="M458 0L364 228Q358 243 351 248T328 253H219V0H73V663Q187 675 304 675Q355 675 406 667T497 637T563 575T588 471Q588 408 563 362T475 289V288Q484 285 490 275T500 255L611 0H458ZM446 463Q446 495 435 514T404 544T361 558T310 561Q286 561 264 560T219 556V367H327Q353 367 375 371T412 385T437 414T446 463Z" />
+<glyph unicode="S" horiz-adv-x="601" d="M553 195Q553 138 533 99T479 35T399 1T302 -10Q224 -10 161 8T33 71L97 175Q143 145 191 125T295 104Q315 104 335 107T372 118T399 142T410 182Q410 207 394 223T353 251T294 272T226 294Q193 305 162 320T107 357T68 412T53 489Q53 543 74 578T130 636T209 667T299 676Q362 676 418 661T530 617L478 505Q440 528 396 543T309 559Q287 559 268 557T233 547T209 527T200 494Q200 473 211 459T241 435T287 417T344 401Q380 391 417 378T485 341T534 282T553 195Z" />
+<glyph unicode="T" horiz-adv-x="549" d="M347 543V0H201V543H14V667H535V543H347Z" />
+<glyph unicode="U" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628Z" />
+<glyph unicode="V" horiz-adv-x="636" d="M393 0H243L15 667H169L318 170H322L469 667H621L393 0Z" />
+<glyph unicode="W" horiz-adv-x="1005" d="M788 0H632L503 473H498L367 0H204L21 667H182L291 177H295L431 667H579L708 177H712L829 667H984L788 0Z" />
+<glyph unicode="X" horiz-adv-x="649" d="M469 0L325 226H321L180 0H10L240 347L36 667H208L327 457H331L456 667H626L416 338L639 0H469Z" />
+<glyph unicode="Y" horiz-adv-x="646" d="M397 262V0H251V259L2 667H170L325 390L483 667H644L397 262Z" />
+<glyph unicode="Z" horiz-adv-x="607" d="M43 0V107L367 543H59V667H548V560L220 119H564V0H43Z" />
+<glyph unicode="[" horiz-adv-x="370" d="M71 -125V783H311V702H194V-45H311V-125H71Z" />
+<glyph unicode="\" horiz-adv-x="573" d="M409 -115L28 667H156L536 -115H409Z" />
+<glyph unicode="]" horiz-adv-x="370" d="M59 -125V-45H176V702H59V783H299V-125H59Z" />
+<glyph unicode="^" horiz-adv-x="433" d="M366 522L213 601L57 522L-4 594L211 705L429 594L366 522Z" />
+<glyph unicode="_" horiz-adv-x="484" d="M-8 -171V-84H492V-171H-8Z" />
+<glyph unicode="`" horiz-adv-x="348" d="M218 565L22 683L163 741L302 610L218 565Z" />
+<glyph unicode="a" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245Z" />
+<glyph unicode="b" horiz-adv-x="590" d="M548 256Q548 205 537 157T500 72T435 12T338 -11Q288 -11 249 8T192 75H188L167 0H63V700H204V447H208Q228 486 262 499T338 513Q394 513 434 492T499 436T536 354T548 256ZM405 254Q405 283 402 310T388 359T356 392T300 405Q255 405 229 385T202 316V206Q202 180 206 159T222 123T251 100T298 92Q334 92 355 106T387 144T401 196T405 254Z" />
+<glyph unicode="c" horiz-adv-x="519" d="M437 350Q410 378 380 391T310 404Q275 404 252 392T213 360T192 312T185 253Q185 221 191 193T213 143T251 110T310 97Q350 97 383 107T451 147L496 54Q451 22 405 5T301 -12Q180 -12 111 56T42 246Q42 308 60 357T112 441T194 494T305 513Q356 513 406 494T498 441L437 350Z" />
+<glyph unicode="d" horiz-adv-x="590" d="M411 0L402 67H398Q376 26 337 8T252 -11Q195 -11 155 11T90 71T54 156T42 256Q42 307 53 353T90 435T155 492T252 513Q291 513 324 501T382 455H386V700H527V0H411ZM388 316Q388 364 362 384T290 405Q254 405 234 393T202 359T188 311T185 254Q185 225 188 197T203 145T235 107T292 92Q348 92 368 123T388 206V316Z" />
+<glyph unicode="e" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404Z" />
+<glyph unicode="f" horiz-adv-x="365" d="M356 598Q343 600 331 601T301 602Q259 602 247 590T235 546V502H346V391H240V0H97V389H20V502H97V563Q97 601 104 628T132 672T187 697T276 705Q294 705 314 704T356 700V598H356Z" />
+<glyph unicode="g" horiz-adv-x="590" d="M527 8Q527 -64 512 -110T466 -181T394 -216T297 -226Q234 -226 181 -213T74 -170L115 -66Q159 -92 200 -105T293 -119Q343 -119 365 -101T387 -29V71H383Q357 32 329 18T255 3Q199 3 159 23T92 77T54 157T42 256Q42 307 53 354T90 436T155 492T252 513Q300 513 336 498T399 441H401L408 502H527V8ZM388 312Q388 361 363 383T290 405Q257 405 237 393T205 360T189 314T185 260Q185 232 189 205T205 157T237 122T290 109Q319 109 338 117T368 141T383 177T388 225V312Z" />
+<glyph unicode="h" horiz-adv-x="593" d="M389 0V281Q389 306 387 327T375 363T349 386T303 395Q204 395 204 300V0H63V700H204V453H205Q230 487 266 500T347 513Q398 513 433 499T489 457T520 390T530 303V0H389Z" />
+<glyph unicode="i" horiz-adv-x="267" d="M63 0V502H204V0H63ZM218 633Q218 601 195 583T133 564Q95 564 72 582T49 633Q49 665 72 684T133 703Q171 703 194 684T218 633Z" />
+<glyph unicode="j" horiz-adv-x="274" d="M224 633Q224 615 217 602T198 581T171 568T139 564Q123 564 108 568T81 580T62 602T55 633Q55 651 62 664T81 686T108 699T139 703Q155 703 170 699T198 687T217 665T224 633ZM211 -17Q211 -123 161 -174T5 -226Q-17 -226 -37 -225T-78 -221V-103Q-45 -106 -12 -106Q17 -106 33 -100T58 -80T68 -47T70 0V502H211V-17Z" />
+<glyph unicode="k" horiz-adv-x="555" d="M380 0L252 214L204 162V0H63V700H204V314H208L363 502H538L354 301L551 0H380Z" />
+<glyph unicode="l" horiz-adv-x="316" d="M288 0Q250 -6 186 -6Q116 -6 90 20T63 109V700H204V142Q204 119 211 114T239 109H288V0H288Z" />
+<glyph unicode="m" horiz-adv-x="876" d="M672 0V287Q672 312 669 332T658 366T634 387T596 395Q561 395 535 377T508 313V0H367V288Q367 313 364 333T353 366T330 387T290 395Q255 395 230 374T204 312V0H63V502H188L198 441H200Q213 468 243 490T330 513Q378 513 412 496T466 439Q495 481 531 497T619 513Q718 513 765 460T813 302V0H672Z" />
+<glyph unicode="n" horiz-adv-x="593" d="M389 0V281Q389 306 386 327T374 363T348 386T302 395Q204 395 204 300V0H63V502H185L194 442H196Q224 482 260 497T346 513Q398 513 433 499T489 457T520 390T530 303V0H389Z" />
+<glyph unicode="o" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251Z" />
+<glyph unicode="p" horiz-adv-x="590" d="M548 256Q548 202 537 154T499 69T432 11T331 -11Q308 -11 290 -8T258 2T231 22T206 53L204 54V-220H63V502H181L190 442H192Q219 481 253 497T336 513Q394 513 434 492T499 436T536 354T548 256ZM405 254Q405 284 402 311T388 359T356 392T299 405Q254 405 228 385T202 316V206Q202 180 206 159T222 123T251 100T297 92Q333 92 354 106T386 144T401 196T405 254Z" />
+<glyph unicode="q" horiz-adv-x="590" d="M486 -238Q458 -228 439 -215T409 -184T392 -143T387 -88V54H384Q361 16 329 3T254 -11Q196 -11 156 11T90 71T54 156T42 256Q42 307 53 354T90 436T155 492T252 513Q296 513 335 499T398 442H400L408 502H527V-67Q527 -97 536 -113T573 -144L486 -238ZM388 316Q388 364 363 384T290 405Q254 405 233 393T202 359T188 311T185 253Q185 224 188 196T203 144T236 107T292 92Q348 92 368 123T388 206V316Z" />
+<glyph unicode="r" horiz-adv-x="404" d="M345 388Q314 388 288 385T244 372T215 341T204 287V0H63V502H184L195 435H199Q220 473 252 492T343 512H378V388H345Z" />
+<glyph unicode="s" horiz-adv-x="492" d="M457 151Q457 67 403 28T250 -11Q131 -11 26 50L75 146Q117 123 161 108T249 92Q285 92 305 104T325 145Q325 160 317 168T295 183T262 192T222 201Q189 208 157 218T99 247T59 294T43 364Q43 395 55 422T93 469T158 501T250 513Q299 513 346 503T439 470L402 370Q370 387 332 397T261 408Q225 408 200 398T175 362Q175 348 184 340T209 326T246 316T290 307Q320 301 349 291T403 264T442 220T457 151Z" />
+<glyph unicode="t" horiz-adv-x="391" d="M353 -2Q324 -6 305 -7T266 -9Q215 -9 182 -1T130 25T103 72T96 145L97 389H15V502H98V635H237V502H353V389H237V164Q237 146 239 135T249 117T276 109T327 107Q335 107 339 107T353 108V-2H353Z" />
+<glyph unicode="u" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398Z" />
+<glyph unicode="v" horiz-adv-x="543" d="M352 0H192L11 502H161L272 130H277L387 502H532L352 0Z" />
+<glyph unicode="w" horiz-adv-x="796" d="M627 0H493L398 298H394L303 0H166L16 502H163L242 150H246L342 502H452L551 149H555L638 502H780L627 0Z" />
+<glyph unicode="x" horiz-adv-x="542" d="M373 0L271 174H267L168 0H6L185 264L27 502H186L275 348H279L369 502H516L356 267L536 0H373Z" />
+<glyph unicode="y" horiz-adv-x="549" d="M335 -21Q310 -83 290 -123T247 -186T192 -217T115 -226Q101 -226 83 -225T40 -221V-104Q47 -106 60 -106T96 -107Q116 -107 128 -104T150 -94T165 -76T179 -47L203 11L7 502H160L274 154H278L391 502H542L335 -21Z" />
+<glyph unicode="z" horiz-adv-x="482" d="M34 0V95L267 389H37V502H439V401L210 112H448V0H34Z" />
+<glyph unicode="{" horiz-adv-x="413" d="M270 -125Q230 -125 202 -119T156 -96T130 -53T121 15V190Q121 222 112 244T67 275V369Q102 376 111 399T121 454V646Q121 686 129 713T156 756T202 779T269 786H344V702H309Q284 702 271 698T252 683T246 657T245 618V430Q245 391 230 369T183 322Q199 310 211 299T230 276T241 249T245 214V42Q245 18 246 2T253 -24T272 -38T309 -42H344V-125H270Z" />
+<glyph unicode="|" horiz-adv-x="287" d="M92 -220V740H195V-220H92Z" />
+<glyph unicode="}" horiz-adv-x="413" d="M346 275Q310 267 301 245T292 190V15Q292 -26 284 -52T257 -95T211 -118T144 -125H69V-42H105Q129 -42 142 -38T161 -24T168 2T169 42V214Q169 234 172 249T183 276T201 299T229 322Q197 347 183 369T169 430V618Q169 641 168 657T161 683T142 697T105 702H69V786H144Q183 786 211 779T257 756T283 713T292 646V454Q292 422 301 399T346 369V275H346Z" />
+<glyph unicode="~" horiz-adv-x="478" d="M471 623Q452 578 417 556T336 534Q309 534 285 540T239 555T194 570T150 577Q128 577 113 569T83 533L2 569Q15 594 28 610T57 637T91 652T133 657Q159 657 183 650T230 635T276 620T319 613Q340 613 356 622T384 656L471 623H471Z" />
+<glyph unicode="&#xA0;" horiz-adv-x="320" />
+<glyph unicode="&#xA1;" horiz-adv-x="299" d="M71 -164Q76 -44 82 74T91 312H207Q210 192 215 74T226 -164H71ZM57 442Q57 474 83 494T149 515Q189 515 215 495T242 442Q242 412 216 392T150 371Q110 371 84 391T57 442Z" />
+<glyph unicode="&#xA2;" horiz-adv-x="613" d="M538 159Q505 127 471 107T396 79V0H265V78Q219 87 184 109T124 165T88 242T75 337Q75 439 123 505T265 590V667H396V590Q431 584 464 569T531 527L470 433Q438 462 409 474T342 487Q277 487 247 448T217 343Q217 268 246 225T341 181Q356 181 371 183T402 191T437 210T477 243L538 159H538Z" />
+<glyph unicode="&#xA3;" horiz-adv-x="613" d="M558 0H55V109H83Q127 109 149 136T172 214Q172 234 170 257T164 302H65V411H151Q144 442 139 468T134 523Q134 601 187 639T340 678Q449 678 540 634L514 513Q483 533 449 546T366 560Q313 560 294 542T274 489Q274 475 277 457T285 411H443V302H305Q317 246 317 208Q317 153 289 122V119H558V0Z" />
+<glyph unicode="&#xA4;" horiz-adv-x="613" d="M87 349Q88 378 97 405T121 455L41 535L113 607L195 525Q220 539 247 546T304 554Q334 554 362 546T415 523L499 607L571 535L489 452Q522 402 522 341Q522 310 514 282T490 230L571 149L499 76L417 158Q392 143 364 135T304 126Q274 126 246 134T193 157L113 77L41 149L119 228Q104 251 96 280T87 341V349ZM206 341Q206 321 213 303T234 272T266 250T305 242Q326 242 344 250T375 271T396 303T404 341Q404 383 376 412T305 441Q263 441 235 412T206 341Z" />
+<glyph unicode="&#xA5;" horiz-adv-x="613" d="M464 398H572V310H410L383 266V206H572V118H383V0H240V118H44V206H240V259L208 310H44V398H152L14 667H182L313 385L442 667H599L464 398Z" />
+<glyph unicode="&#xA6;" horiz-adv-x="287" d="M92 360V740H195V360H92ZM92 -220V160H195V-220H92Z" />
+<glyph unicode="&#xA7;" horiz-adv-x="496" d="M458 322Q458 273 430 243T338 201V198Q392 185 423 150T454 64Q454 37 444 11T409 -37T344 -71T245 -84Q182 -84 133 -70T29 -23L77 75Q173 18 244 18Q280 18 295 29T311 61Q311 77 300 88T255 112Q218 124 184 137T125 163Q77 187 56 214T34 283Q34 336 68 364T162 401V404Q106 420 74 453T41 532Q41 563 53 590T91 636T154 667T243 678Q299 678 345 668T439 637L400 530Q362 552 328 562T256 573Q224 573 204 566T183 536Q183 521 195 510T245 485Q295 469 325 458T382 434Q419 415 438 389T458 322ZM333 291Q333 314 314 328T246 343Q206 343 182 333T158 291Q158 271 176 255T246 239Q333 239 333 291Z" />
+<glyph unicode="&#xA8;" horiz-adv-x="348" d="M355 639Q355 610 335 590T286 569Q257 569 237 589T216 639Q216 668 236 688T286 708Q315 708 335 688T355 639ZM132 639Q132 610 112 590T63 569Q34 569 14 589T-6 639Q-6 668 14 688T63 708Q92 708 112 688T132 639Z" />
+<glyph unicode="&#xA9;" horiz-adv-x="795" d="M743 334Q743 262 716 200T642 90T533 16T398 -11Q326 -11 263 16T153 90T79 199T52 334Q52 406 79 468T153 578T263 652T398 679Q470 679 532 652T642 578T716 469T743 334ZM698 335Q698 397 675 451T611 547T516 611T398 635Q336 635 282 612T187 547T123 452T99 335Q99 273 122 219T187 125T282 61T398 37Q460 37 514 60T610 124T674 219T698 335ZM572 187Q537 154 498 137T411 119Q355 119 315 136T250 183T214 252T202 337Q202 435 255 492T409 549Q457 549 496 532T571 479L515 418Q470 471 413 471Q351 471 324 436T297 337Q297 270 324 234T412 198Q444 198 470 211T523 250L572 187Z" />
+<glyph unicode="&#xAA;" horiz-adv-x="415" d="M300 371Q263 378 252 406H250Q206 371 151 371Q105 371 79 395T52 464Q52 524 101 549T241 574V582Q241 601 235 610T197 619Q176 619 147 613T86 590L58 650Q96 669 131 677T205 686Q242 686 265 679T302 656T321 616T327 559V463Q327 449 332 442T355 424L300 371ZM241 524Q182 524 160 510T137 467Q137 449 147 438T176 427Q191 427 205 432T241 452V524Z" />
+<glyph unicode="&#xAB;" horiz-adv-x="499" d="M368 97L244 300L368 502L463 455L363 299L463 144L368 97ZM146 85L14 300L144 513L239 466L133 299L241 133L146 85Z" />
+<glyph unicode="&#xAC;" horiz-adv-x="613" d="M451 211H45V329H568V34H451V211Z" />
+<glyph unicode="&#xAE;" horiz-adv-x="451" d="M225 362Q189 362 158 375T103 411T66 465T52 531Q52 566 65 597T102 651T157 687T225 700Q261 700 292 687T348 651T385 597T399 531Q399 496 386 465T348 411T293 375T225 362ZM226 677Q195 677 168 666T121 634T89 588T77 532Q77 502 88 476T120 430T168 399T226 387Q256 387 283 398T331 429T363 475T375 532Q375 562 363 588T331 634T284 665T226 677ZM154 627Q172 629 190 629T225 629Q240 629 255 627T282 618T301 599T308 568Q308 535 279 519L313 439H259L234 503Q232 505 232 506T228 507H203V439H154V627ZM225 592Q218 592 212 592T202 591V542H229Q246 542 252 548T259 566Q259 579 252 585T225 592Z" />
+<glyph unicode="&#xAF;" horiz-adv-x="348" d="M12 587V674H335V587H12Z" />
+<glyph unicode="&#xB0;" horiz-adv-x="413" d="M361 532Q361 500 349 472T316 422T267 389T207 377Q175 377 147 389T98 422T65 471T52 532Q52 564 64 592T98 641T147 674T207 686Q239 686 267 674T316 641T349 592T361 532ZM317 532Q317 554 309 574T285 609T250 632T207 641Q184 641 164 633T129 609T106 574T97 532Q97 509 105 489T129 454T164 431T207 422Q230 422 250 430T285 454T308 489T317 532Z" />
+<glyph unicode="&#xB1;" horiz-adv-x="613" d="M368 323V157H247V323H45V440H247V623H368V440H568V323H368ZM45 0V112H568V0H45Z" />
+<glyph unicode="&#xB2;" horiz-adv-x="326" d="M38 366L25 439Q64 467 95 488T149 526T184 558T197 590Q197 604 188 614T159 625Q134 625 116 617T78 587L28 643Q63 674 95 687T168 700Q225 700 256 674T288 601Q288 581 281 562T256 524T208 483T132 438H292V366H38Z" />
+<glyph unicode="&#xB3;" horiz-adv-x="319" d="M285 463Q285 412 253 387T153 361Q118 361 85 370T20 404L57 464Q82 444 102 437T143 429Q171 429 185 440T199 468Q199 488 187 495T155 502H102V562H136Q159 562 173 571T188 598Q188 611 179 620T149 630Q126 630 104 621T65 595L24 654Q59 680 89 690T159 700Q213 700 244 677T276 602Q276 581 264 565T235 540V539Q259 532 272 512T285 463Z" />
+<glyph unicode="&#xB4;" horiz-adv-x="348" d="M130 565L46 610L185 742L326 683L130 565Z" />
+<glyph unicode="&#xB5;" horiz-adv-x="604" d="M404 0L395 58H392Q381 41 370 28T346 6T314 -7T269 -12Q264 -12 254 -11T233 -7T211 3T194 21V-220H69V502H211V239Q211 210 212 186T220 144T245 116T294 106Q322 106 340 113T368 132T381 164T385 207V502H527V0H404Z" />
+<glyph unicode="&#xB6;" horiz-adv-x="536" d="M335 0Q263 0 208 19T116 73T61 153T42 252Q42 304 60 349T116 429T208 482T335 502H463V0H335Z" />
+<glyph unicode="&#xB7;" horiz-adv-x="259" d="M221 242Q221 211 194 191T128 170Q88 170 62 190T35 242Q35 272 62 292T128 313Q167 313 194 293T221 242Z" />
+<glyph unicode="&#xB8;" horiz-adv-x="348" d="M283 -123Q283 -161 255 -187T170 -213Q144 -213 122 -205T82 -188L98 -140Q118 -150 132 -154T164 -159Q182 -159 196 -153T211 -126Q211 -92 168 -92Q144 -92 128 -100L101 -86L143 0H207L181 -47L182 -48Q187 -47 193 -47T206 -46Q241 -46 262 -66T283 -123Z" />
+<glyph unicode="&#xB9;" horiz-adv-x="301" d="M111 438V602H108L43 573L16 639L131 694H202V438H279V367H30V438H111Z" />
+<glyph unicode="&#xBA;" horiz-adv-x="415" d="M363 529Q363 456 324 414T207 371Q130 371 91 413T52 529Q52 563 61 592T89 642T137 674T207 686Q282 686 322 645T363 529ZM275 529Q275 574 260 594T207 615Q169 615 155 595T140 529Q140 478 156 459T208 439Q243 439 259 458T275 529Z" />
+<glyph unicode="&#xBB;" horiz-adv-x="499" d="M353 85L258 133L365 299L257 467L353 514L485 300L353 85ZM130 97L35 144L136 299L35 455L130 502L255 300L130 97Z" />
+<glyph unicode="&#xBC;" horiz-adv-x="798" d="M148 438V602H145L80 573L53 639L168 694H239V438H316V367H67V438H148ZM214 0H138L658 700H734L214 0ZM719 62V0H629V62H493L473 126L630 327H719V134H770V62H719ZM552 127H633V244L552 127Z" />
+<glyph unicode="&#xBD;" horiz-adv-x="771" d="M146 438V602H143L78 573L51 639L166 694H237V438H314V367H65V438H146ZM182 0H106L626 700H702L182 0ZM486 0L473 73Q512 101 543 122T597 159T632 191T645 223Q645 237 636 247T607 258Q582 258 564 250T526 220L476 276Q511 307 543 320T616 333Q673 333 704 307T736 234Q736 214 729 196T704 158T656 117T580 72H740V0H486Z" />
+<glyph unicode="&#xBE;" horiz-adv-x="752" d="M323 463Q323 412 291 387T191 361Q156 361 123 370T58 404L95 464Q120 444 140 437T181 429Q209 429 223 440T237 468Q237 488 225 495T193 502H140V562H174Q197 562 211 571T226 598Q226 611 217 620T187 630Q164 630 142 621T103 595L62 654Q97 680 127 690T197 700Q251 700 282 677T314 602Q314 581 302 565T273 540V539Q297 532 310 512T323 463ZM196 0H120L640 700H716L196 0ZM682 62V0H592V62H456L436 126L593 327H682V134H733V62H682ZM515 127H596V244L515 127Z" />
+<glyph unicode="&#xBF;" horiz-adv-x="545" d="M54 10Q54 72 82 115T174 189L214 208Q226 214 233 218T243 228T247 243T248 268V308H384V237Q384 215 382 200T372 173T351 153T317 134L274 114Q231 94 211 76T190 21Q190 -17 218 -37T295 -58Q384 -58 445 -1L513 -102Q483 -122 456 -136T400 -159T342 -172T276 -176Q164 -176 109 -127T54 10ZM227 443Q227 474 253 494T319 515Q358 515 384 495T411 443Q411 413 385 393T320 372Q280 372 254 392T227 443Z" />
+<glyph unicode="&#xC0;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM346 705L150 823L291 881L430 750L346 705Z" />
+<glyph unicode="&#xC1;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM346 705L262 750L401 882L542 823L346 705Z" />
+<glyph unicode="&#xC2;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM471 701L346 775L219 701L163 780L344 878L528 780L471 701Z" />
+<glyph unicode="&#xC3;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM535 795Q521 750 489 729T422 707Q399 707 381 713T346 728T315 743T285 750Q268 750 252 741T224 706L159 759Q180 809 207 827T271 846Q292 846 310 839T345 824T377 809T409 802Q426 802 442 811T468 846L535 795H535Z" />
+<glyph unicode="&#xC4;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM527 779Q527 750 507 730T458 709Q429 709 409 729T388 779Q388 808 408 828T458 848Q487 848 507 828T527 779ZM304 779Q304 750 284 730T235 709Q206 709 186 729T166 779Q166 808 186 828T235 848Q264 848 284 828T304 779Z" />
+<glyph unicode="&#xC5;" horiz-adv-x="701" d="M495 718Q495 686 482 659T447 614L689 0H533L480 140H217L163 0H12L247 613Q224 631 211 658T197 718Q197 749 208 775T240 821T288 850T346 861Q377 861 404 851T451 821T483 776T495 718ZM347 526H345L251 258H443L347 526ZM412 718Q412 748 394 767T347 786Q318 786 299 767T280 718Q280 688 299 669T347 650Q375 650 393 669T412 718Z" />
+<glyph unicode="&#xC6;" horiz-adv-x="939" d="M454 0V151H234L165 0H14L355 667H892V548H597V406H863V283H597V119H902V0H454ZM454 547H431L290 273H454V547Z" />
+<glyph unicode="&#xC7;" horiz-adv-x="665" d="M479 -126Q479 -164 451 -189T367 -215Q341 -215 318 -208T278 -190L294 -142Q313 -152 328 -157T361 -162Q378 -162 392 -155T407 -128Q407 -94 364 -94Q352 -94 343 -96T324 -102L297 -88L336 -9Q264 -2 211 26T124 99T73 205T56 338Q56 414 77 477T140 584T242 653T381 678Q456 678 517 652T636 567L547 474Q509 516 471 535T387 555Q300 555 254 503T208 337Q208 232 250 171T386 110Q434 110 476 130T561 193L640 96Q584 43 526 17T398 -11L377 -50L378 -51Q383 -50 389 -50T402 -49Q437 -49 458 -68T479 -126Z" />
+<glyph unicode="&#xC8;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM332 705L136 823L277 881L416 750L332 705Z" />
+<glyph unicode="&#xC9;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM297 705L213 750L352 882L493 823L297 705Z" />
+<glyph unicode="&#xCA;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM425 701L300 775L173 701L117 780L298 878L482 780L425 701Z" />
+<glyph unicode="&#xCB;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM481 779Q481 750 461 730T412 709Q383 709 363 729T342 779Q342 808 362 828T412 848Q441 848 461 828T481 779ZM258 779Q258 750 238 730T189 709Q160 709 140 729T120 779Q120 808 140 828T189 848Q218 848 238 828T258 779Z" />
+<glyph unicode="&#xCC;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM145 705L-51 823L90 881L229 750L145 705Z" />
+<glyph unicode="&#xCD;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM150 705L66 750L205 882L346 823L150 705Z" />
+<glyph unicode="&#xCE;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM278 701L153 775L26 701L-30 780L151 878L335 780L278 701Z" />
+<glyph unicode="&#xCF;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM328 779Q328 750 308 730T259 709Q230 709 210 729T189 779Q189 808 209 828T259 848Q288 848 308 828T328 779ZM105 779Q105 750 85 730T36 709Q7 709 -13 729T-33 779Q-33 808 -13 828T36 848Q65 848 85 828T105 779Z" />
+<glyph unicode="&#xD0;" horiz-adv-x="704" d="M648 338Q648 251 624 188T555 83T443 21T292 0H75V289H5V408H75V662Q132 667 190 670T305 674Q467 674 557 589T648 338ZM499 340Q499 386 490 426T460 496T402 543T310 560Q287 560 264 559T219 556V408H373V289H219V117Q237 116 255 116T291 116Q349 116 388 129T452 169T488 239T499 340Z" />
+<glyph unicode="&#xD1;" horiz-adv-x="731" d="M532 0L219 425H215V0H73V667H208L512 249H517V667H658V0H532ZM553 795Q539 750 507 729T440 707Q417 707 399 713T364 728T333 743T303 750Q286 750 270 741T242 706L177 759Q198 809 225 827T289 846Q310 846 328 839T363 824T395 809T427 802Q444 802 460 811T486 846L553 795H553Z" />
+<glyph unicode="&#xD2;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM423 709L227 827L368 885L507 754L423 709Z" />
+<glyph unicode="&#xD3;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM386 709L302 754L441 886L582 827L386 709Z" />
+<glyph unicode="&#xD4;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM518 701L393 775L266 701L210 780L391 878L575 780L518 701Z" />
+<glyph unicode="&#xD5;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM569 799Q555 754 523 733T456 711Q433 711 415 717T380 732T349 747T319 754Q302 754 286 745T258 710L193 763Q214 813 241 831T305 850Q326 850 344 843T379 828T411 813T443 806Q460 806 476 815T502 850L569 799H569Z" />
+<glyph unicode="&#xD6;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM569 779Q569 750 549 730T500 709Q471 709 451 729T430 779Q430 808 450 828T500 848Q529 848 549 828T569 779ZM346 779Q346 750 326 730T277 709Q248 709 228 729T208 779Q208 808 228 828T277 848Q306 848 326 828T346 779Z" />
+<glyph unicode="&#xD7;" horiz-adv-x="613" d="M551 430L389 269L552 106L470 23L307 186L143 23L60 106L224 269L62 430L145 513L307 351L469 513L551 430Z" />
+<glyph unicode="&#xD8;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q270 -10 192 38L131 -26L56 59L111 117Q56 199 56 334Q56 422 79 487T146 594T251 657T386 678Q446 678 497 665T590 623L653 690L733 612L667 542Q690 502 703 451T716 334ZM208 334Q208 273 218 230L499 528Q458 560 386 560Q335 560 301 545T246 502T217 431T208 334ZM564 334Q564 360 563 382T556 425L281 133Q322 108 386 108Q437 108 471 123T526 167T555 238T564 334Z" />
+<glyph unicode="&#xD9;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM349 705L153 823L294 881L433 750L349 705Z" />
+<glyph unicode="&#xDA;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM349 705L265 750L404 882L545 823L349 705Z" />
+<glyph unicode="&#xDB;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM477 701L352 775L225 701L169 780L350 878L534 780L477 701Z" />
+<glyph unicode="&#xDC;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM531 779Q531 750 511 730T462 709Q433 709 413 729T392 779Q392 808 412 828T462 848Q491 848 511 828T531 779ZM308 779Q308 750 288 730T239 709Q210 709 190 729T170 779Q170 808 190 828T239 848Q268 848 288 828T308 779Z" />
+<glyph unicode="&#xDD;" horiz-adv-x="646" d="M397 262V0H251V259L2 667H170L325 390L483 667H644L397 262ZM347 689L263 734L402 866L543 807L347 689Z" />
+<glyph unicode="&#xDE;" horiz-adv-x="632" d="M590 335Q590 289 577 249T535 180T461 133T351 116H216V0H73V667H215V550Q264 553 308 553Q365 553 416 544T505 509T567 442T590 335ZM446 336Q446 393 415 415T309 437Q288 437 266 436T216 433V230H326Q350 230 372 233T410 246T436 278T446 336Z" />
+<glyph unicode="&#xDF;" horiz-adv-x="589" d="M554 183Q554 129 536 92T490 32T428 -2T359 -13Q321 -13 293 -9T250 1L269 99Q280 96 295 94T321 92Q339 92 355 96T383 111T403 139T411 182Q411 200 405 219T384 255T341 287T272 310L256 393Q308 429 334 463T360 533Q360 565 342 585T280 606Q235 606 218 585T201 522V0H63V524Q63 563 71 596T103 653T169 691T278 705Q380 705 432 661T484 545Q484 502 466 461T402 387V386Q432 374 459 355T508 311T541 253T554 183Z" />
+<glyph unicode="&#xE0;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM283 565L87 683L228 741L367 610L283 565Z" />
+<glyph unicode="&#xE1;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM265 565L181 610L320 742L461 683L265 565Z" />
+<glyph unicode="&#xE2;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM402 559L277 633L150 559L94 638L275 736L459 638L402 559Z" />
+<glyph unicode="&#xE3;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM457 655Q443 610 411 589T344 567Q321 567 303 573T268 588T237 603T207 610Q190 610 174 601T146 566L81 619Q102 669 129 687T193 706Q214 706 232 699T267 684T299 669T331 662Q348 662 364 671T390 706L457 655H457Z" />
+<glyph unicode="&#xE4;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM454 639Q454 610 434 590T385 569Q356 569 336 589T315 639Q315 668 335 688T385 708Q414 708 434 688T454 639ZM231 639Q231 610 211 590T162 569Q133 569 113 589T93 639Q93 668 113 688T162 708Q191 708 211 688T231 639Z" />
+<glyph unicode="&#xE5;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM389 666Q389 643 381 624T357 590T322 568T277 560Q254 560 234 568T199 590T175 623T166 666Q166 714 198 743T277 773Q325 773 357 744T389 666ZM328 666Q328 689 314 703T278 718Q255 718 241 704T227 666Q227 643 241 629T278 614Q300 614 314 628T328 666Z" />
+<glyph unicode="&#xE6;" horiz-adv-x="891" d="M850 244Q850 237 850 229T848 203H493Q496 152 529 125T622 97Q660 97 696 106T771 138L817 43Q769 15 720 2T617 -11Q549 -11 499 10T414 69Q376 37 324 13T200 -12Q124 -12 80 28T36 141Q36 190 57 224T118 281T217 313T350 323V340Q350 356 348 368T339 389T316 401T276 406Q240 406 191 394T94 355L48 453Q109 484 166 498T288 513Q341 513 381 500T444 457Q477 487 519 500T606 514Q661 514 706 496T783 442T832 357T850 244ZM722 295Q717 348 691 379T608 411Q555 411 524 381T492 295H722ZM371 134Q360 153 356 176T351 226V242Q253 242 213 219T173 149Q173 117 192 97T243 77Q269 77 302 92T371 134Z" />
+<glyph unicode="&#xE7;" horiz-adv-x="519" d="M396 -128Q396 -166 368 -191T284 -217Q258 -217 235 -210T195 -193L211 -144Q230 -155 245 -159T278 -164Q295 -164 309 -157T324 -130Q324 -96 282 -96Q258 -96 242 -105L214 -90L254 -8Q153 3 98 70T42 246Q42 308 60 357T112 441T194 494T305 513Q356 513 406 494T498 441L437 350Q410 378 380 391T310 404Q275 404 251 392T212 360T191 312T184 253Q184 221 190 193T212 143T251 110T310 97Q350 97 383 107T451 147L496 54Q453 23 411 7T316 -12L294 -52L295 -53Q300 -52 306 -52T318 -51Q354 -51 375 -70T396 -128Z" />
+<glyph unicode="&#xE8;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM306 565L110 683L251 741L390 610L306 565Z" />
+<glyph unicode="&#xE9;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM294 565L210 610L349 742L490 683L294 565Z" />
+<glyph unicode="&#xEA;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM420 561L295 635L168 561L112 640L293 738L477 640L420 561Z" />
+<glyph unicode="&#xEB;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM480 639Q480 610 460 590T411 569Q382 569 362 589T341 639Q341 668 361 688T411 708Q440 708 460 688T480 639ZM257 639Q257 610 237 590T188 569Q159 569 139 589T119 639Q119 668 139 688T188 708Q217 708 237 688T257 639Z" />
+<glyph unicode="&#xEC;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM134 565L-62 683L79 741L218 610L134 565Z" />
+<glyph unicode="&#xED;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM134 565L50 610L189 742L330 683L134 565Z" />
+<glyph unicode="&#xEE;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM261 561L136 635L9 561L-47 640L134 738L318 640L261 561Z" />
+<glyph unicode="&#xEF;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM315 639Q315 610 295 590T246 569Q217 569 197 589T176 639Q176 668 196 688T246 708Q275 708 295 688T315 639ZM92 639Q92 610 72 590T23 569Q-6 569 -26 589T-46 639Q-46 668 -26 688T23 708Q52 708 72 688T92 639Z" />
+<glyph unicode="&#xF0;" horiz-adv-x="574" d="M456 611Q476 575 491 534T515 450T529 368T534 297Q534 148 471 70T277 -10Q224 -10 181 7T107 56T59 136T42 243Q42 356 96 417T254 478Q300 478 328 468T376 433H380Q378 462 368 496T339 562L228 517L195 588L296 629Q284 646 271 660T237 691L363 725Q367 721 373 715T387 701T400 688T409 677L481 710L516 639L456 611ZM394 240Q394 275 390 302T373 347T340 375T287 385Q255 385 235 376T203 348T187 303T182 243Q182 165 205 130T289 95Q349 95 371 131T394 240Z" />
+<glyph unicode="&#xF1;" horiz-adv-x="593" d="M389 0V281Q389 306 386 327T374 363T348 386T302 395Q204 395 204 300V0H63V502H185L194 442H196Q224 482 260 497T346 513Q398 513 433 499T489 457T520 390T530 303V0H389ZM489 655Q475 610 443 589T376 567Q353 567 335 573T300 588T269 603T239 610Q222 610 206 601T178 566L113 619Q134 669 161 687T225 706Q246 706 264 699T299 684T331 669T363 662Q380 662 396 671T422 706L489 655H489Z" />
+<glyph unicode="&#xF2;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM313 565L117 683L258 741L397 610L313 565Z" />
+<glyph unicode="&#xF3;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM304 565L220 610L359 742L500 683L304 565Z" />
+<glyph unicode="&#xF4;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM428 561L303 635L176 561L120 640L301 738L485 640L428 561Z" />
+<glyph unicode="&#xF5;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM490 655Q476 610 444 589T377 567Q354 567 336 573T301 588T270 603T240 610Q223 610 207 601T179 566L114 619Q135 669 162 687T226 706Q247 706 265 699T300 684T332 669T364 662Q381 662 397 671T423 706L490 655H490Z" />
+<glyph unicode="&#xF6;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM480 639Q480 610 460 590T411 569Q382 569 362 589T341 639Q341 668 361 688T411 708Q440 708 460 688T480 639ZM257 639Q257 610 237 590T188 569Q159 569 139 589T119 639Q119 668 139 688T188 708Q217 708 237 688T257 639Z" />
+<glyph unicode="&#xF7;" horiz-adv-x="613" d="M45 211V329H568V211H45ZM224 70Q224 104 247 128T306 152Q340 152 364 128T388 70Q388 35 364 11T306 -13Q271 -13 248 11T224 70ZM224 477Q224 511 247 535T306 559Q340 559 364 535T388 477Q388 442 364 418T306 394Q271 394 248 418T224 477Z" />
+<glyph unicode="&#xF8;" horiz-adv-x="601" d="M510 424Q536 390 547 346T559 251Q559 193 544 145T496 62T416 8T301 -11Q205 -11 143 33L91 -23L31 41L80 95Q60 127 51 166T42 251Q42 306 56 353T102 436T183 492T301 513Q384 513 441 482L498 539L563 480L510 424ZM360 379Q349 390 334 395T301 400Q236 400 211 362T185 251Q185 240 186 222T191 193L360 379ZM416 251Q416 268 414 289T406 324L224 129Q235 114 257 106T302 98Q365 98 390 136T416 251Z" />
+<glyph unicode="&#xF9;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM294 565L98 683L239 741L378 610L294 565Z" />
+<glyph unicode="&#xFA;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM284 565L200 610L339 742L480 683L284 565Z" />
+<glyph unicode="&#xFB;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM421 561L296 635L169 561L113 640L294 738L478 640L421 561Z" />
+<glyph unicode="&#xFC;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM474 639Q474 610 454 590T405 569Q376 569 356 589T335 639Q335 668 355 688T405 708Q434 708 454 688T474 639ZM251 639Q251 610 231 590T182 569Q153 569 133 589T113 639Q113 668 133 688T182 708Q211 708 231 688T251 639Z" />
+<glyph unicode="&#xFD;" horiz-adv-x="549" d="M335 -21Q310 -83 290 -123T247 -186T192 -217T115 -226Q101 -226 83 -225T40 -221V-104Q47 -106 60 -106T96 -107Q116 -107 128 -104T150 -94T165 -76T179 -47L203 11L7 502H160L274 154H278L391 502H542L335 -21ZM275 565L191 610L330 742L471 683L275 565Z" />
+<glyph unicode="&#xFE;" horiz-adv-x="590" d="M548 256Q548 197 533 148T490 64T420 9T326 -11Q284 -11 258 3T208 46H204V-220H63V700H194V450H198Q225 484 258 498T336 513Q388 513 428 495T494 442T534 361T548 256ZM405 255Q405 331 384 369T299 407Q249 407 227 386T204 318V209Q204 149 225 122T297 95Q357 95 381 136T405 255Z" />
+<glyph unicode="&#xFF;" horiz-adv-x="549" d="M335 -21Q310 -83 290 -123T247 -186T192 -217T115 -226Q101 -226 83 -225T40 -221V-104Q47 -106 60 -106T96 -107Q116 -107 128 -104T150 -94T165 -76T179 -47L203 11L7 502H160L274 154H278L391 502H542L335 -21ZM460 639Q460 610 440 590T391 569Q362 569 342 589T321 639Q321 668 341 688T391 708Q420 708 440 688T460 639ZM237 639Q237 610 217 590T168 569Q139 569 119 589T99 639Q99 668 119 688T168 708Q197 708 217 688T237 639Z" />
+<glyph unicode="&#x100;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM185 727V814H508V727H185Z" />
+<glyph unicode="&#x101;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM111 587V674H434V587H111Z" />
+<glyph unicode="&#x102;" horiz-adv-x="701" d="M533 0L480 140H217L163 0H12L269 670H425L689 0H533ZM348 525H344L251 258H443L348 525ZM519 846Q514 801 495 774T451 731T397 710T344 705Q320 705 292 710T240 730T197 773T173 846H270Q273 820 290 799T345 778Q381 778 400 799T421 846H519H519Z" />
+<glyph unicode="&#x103;" horiz-adv-x="562" d="M446 -13Q420 -6 400 7T368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 110 503 97T535 73L446 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245ZM441 706Q436 661 417 634T373 591T319 570T266 565Q242 565 214 570T162 590T119 633T95 706H192Q195 680 212 659T267 638Q303 638 322 659T343 706H441H441Z" />
+<glyph unicode="&#x104;" horiz-adv-x="701" d="M611 0Q566 -33 551 -59T536 -106Q536 -132 550 -144T584 -156Q604 -156 615 -151T636 -141L659 -219Q652 -222 641 -226T616 -233T587 -239T559 -241Q499 -241 469 -212T438 -132Q438 -91 465 -57T532 1L480 140H217L163 0H12L269 670H425L689 0H611ZM347 525H345L251 258H443L347 525Z" />
+<glyph unicode="&#x105;" horiz-adv-x="562" d="M445 -13Q410 -42 398 -64T386 -106Q386 -131 400 -143T433 -156Q453 -156 465 -151T486 -141L509 -219Q495 -226 466 -233T408 -241Q348 -241 318 -211T288 -131Q288 -87 319 -51T394 9Q378 21 368 47H366Q329 18 289 3T200 -12Q164 -12 134 -2T83 27T49 74T36 141Q36 203 67 239T146 294T248 318T350 323V354Q349 386 330 398T275 411Q226 411 179 397T92 359L48 453Q107 482 164 497T289 513Q344 513 382 502T445 467T480 404T491 311V135Q491 109 503 96T536 71L445 -13ZM353 245Q328 245 297 243T239 232T192 204T173 151Q173 119 192 101T243 82Q275 82 301 92T353 121V245Z" />
+<glyph unicode="&#x106;" horiz-adv-x="665" d="M640 96Q582 41 520 15T385 -11Q299 -11 237 14T135 85T76 195T56 338Q56 414 77 477T140 584T242 653T381 678Q456 678 517 652T636 567L547 474Q509 516 471 535T387 555Q300 555 254 503T208 337Q208 232 250 171T386 110Q434 110 476 130T561 193L640 96H640ZM362 710L278 755L417 887L558 828L362 710Z" />
+<glyph unicode="&#x107;" horiz-adv-x="519" d="M437 350Q410 378 380 391T310 404Q275 404 252 392T213 360T192 312T185 253Q185 221 191 193T213 143T251 110T310 97Q350 97 383 107T451 147L496 54Q451 22 405 5T301 -12Q180 -12 111 56T42 246Q42 308 60 357T112 441T194 494T305 513Q356 513 406 494T498 441L437 350ZM290 565L206 610L345 742L486 683L290 565Z" />
+<glyph unicode="&#x108;" horiz-adv-x="665" d="M640 96Q582 41 520 15T385 -11Q299 -11 237 14T135 85T76 195T56 338Q56 414 77 477T140 584T242 653T381 678Q456 678 517 652T636 567L547 474Q509 516 471 535T387 555Q300 555 254 503T208 337Q208 232 250 171T386 110Q434 110 476 130T561 193L640 96H640ZM507 704L382 778L255 704L199 783L380 881L564 783L507 704Z" />
+<glyph unicode="&#x109;" horiz-adv-x="519" d="M437 350Q410 378 380 391T310 404Q275 404 252 392T213 360T192 312T185 253Q185 221 191 193T213 143T251 110T310 97Q350 97 383 107T451 147L496 54Q451 22 405 5T301 -12Q180 -12 111 56T42 246Q42 308 60 357T112 441T194 494T305 513Q356 513 406 494T498 441L437 350ZM418 561L293 635L166 561L110 640L291 738L475 640L418 561Z" />
+<glyph unicode="&#x10A;" horiz-adv-x="665" d="M640 96Q582 41 520 15T385 -11Q299 -11 237 14T135 85T76 195T56 338Q56 414 77 477T140 584T242 653T381 678Q456 678 517 652T636 567L547 474Q509 516 471 535T387 555Q300 555 254 503T208 337Q208 232 250 171T386 110Q434 110 476 130T561 193L640 96H640ZM461 786Q461 754 439 735T378 716Q340 716 317 735T294 786Q294 818 317 836T378 855Q416 855 438 837T461 786Z" />
+<glyph unicode="&#x10B;" horiz-adv-x="519" d="M437 350Q410 378 380 391T310 404Q275 404 252 392T213 360T192 312T185 253Q185 221 191 193T213 143T251 110T310 97Q350 97 383 107T451 147L496 54Q451 22 405 5T301 -12Q180 -12 111 56T42 246Q42 308 60 357T112 441T194 494T305 513Q356 513 406 494T498 441L437 350ZM377 639Q377 607 355 588T294 569Q256 569 233 588T210 639Q210 671 233 689T294 708Q332 708 354 690T377 639Z" />
+<glyph unicode="&#x10C;" horiz-adv-x="665" d="M640 96Q582 41 520 15T385 -11Q299 -11 237 14T135 85T76 195T56 338Q56 414 77 477T140 584T242 653T381 678Q456 678 517 652T636 567L547 474Q509 516 471 535T387 555Q300 555 254 503T208 337Q208 232 250 171T386 110Q434 110 476 130T561 193L640 96H640ZM546 808L378 711L208 808L261 883L376 814L493 883L546 808Z" />
+<glyph unicode="&#x10D;" horiz-adv-x="519" d="M437 350Q410 378 380 391T310 404Q275 404 252 392T213 360T192 312T185 253Q185 221 191 193T213 143T251 110T310 97Q350 97 383 107T451 147L496 54Q451 22 405 5T301 -12Q180 -12 111 56T42 246Q42 308 60 357T112 441T194 494T305 513Q356 513 406 494T498 441L437 350ZM469 662L301 565L131 662L184 737L299 668L416 737L469 662Z" />
+<glyph unicode="&#x10E;" horiz-adv-x="704" d="M648 337Q648 250 624 187T556 82T447 20T303 0H73V662Q122 667 182 671T305 675Q380 675 443 656T551 595T622 490T648 337ZM496 340Q496 380 489 419T462 490T406 541T309 561Q288 561 265 560T219 557V117Q242 116 257 116T291 116Q339 116 377 125T441 160T482 229T496 340ZM474 802L306 705L136 802L189 877L304 808L421 877L474 802Z" />
+<glyph unicode="&#x10F;" horiz-adv-x="590" d="M411 0L402 67H398Q376 26 337 8T252 -11Q195 -11 155 11T90 71T54 156T42 256Q42 307 53 353T90 435T155 492T252 513Q291 513 324 501T382 455H386V700H527V0H411ZM388 316Q388 364 362 384T290 405Q254 405 234 393T202 359T188 311T185 254Q185 225 188 197T203 145T235 107T292 92Q348 92 368 123T388 206V316ZM665 518H571L608 700H746L665 518Z" />
+<glyph unicode="&#x110;" horiz-adv-x="704" d="M648 338Q648 251 624 188T555 83T443 21T292 0H75V289H5V408H75V662Q132 667 190 670T305 674Q467 674 557 589T648 338ZM499 340Q499 386 490 426T460 496T402 543T310 560Q287 560 264 559T219 556V408H373V289H219V117Q237 116 255 116T291 116Q349 116 388 129T452 169T488 239T499 340Z" />
+<glyph unicode="&#x111;" horiz-adv-x="590" d="M411 0L402 67H398Q376 26 337 8T252 -11Q195 -11 155 10T90 67T54 150T42 249Q42 300 53 344T90 423T155 476T252 496Q291 496 324 484T382 437H386V545H247V637H386V700H527V637H589V545H527V0H411ZM388 299Q388 347 362 367T290 388Q254 388 234 377T202 347T188 301T185 246Q185 217 188 190T203 141T235 106T292 92Q348 92 368 123T388 206V299Z" />
+<glyph unicode="&#x112;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM139 727V814H462V727H139Z" />
+<glyph unicode="&#x113;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM135 587V674H458V587H135Z" />
+<glyph unicode="&#x114;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM470 846Q465 801 446 774T402 731T348 710T295 705Q271 705 243 710T191 730T148 773T124 846H221Q224 820 241 799T296 778Q332 778 351 799T372 846H470H470Z" />
+<glyph unicode="&#x115;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM470 706Q465 661 446 634T402 591T348 570T295 565Q271 565 243 570T191 590T148 633T124 706H221Q224 680 241 659T296 638Q332 638 351 659T372 706H470H470Z" />
+<glyph unicode="&#x116;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM399 782Q399 750 377 731T316 712Q278 712 255 731T232 782Q232 814 255 832T316 851Q354 851 376 833T399 782Z" />
+<glyph unicode="&#x117;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM379 639Q379 607 357 588T296 569Q258 569 235 588T212 639Q212 671 235 689T296 708Q334 708 356 690T379 639Z" />
+<glyph unicode="&#x118;" horiz-adv-x="575" d="M481 0Q437 -33 422 -58T407 -106Q407 -131 421 -143T454 -156Q474 -156 486 -151T507 -141L530 -219Q516 -226 487 -233T429 -241Q369 -241 339 -212T309 -133Q309 -88 334 -56T400 0H73V667H528V545H219V404H499V280H219V119H538V0H481Z" />
+<glyph unicode="&#x119;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q488 33 472 25T441 9Q418 -4 404 -16T383 -41T373 -65T370 -87Q370 -112 384 -126T417 -141Q437 -141 448 -136T470 -125L493 -204Q486 -207 475 -210T450 -217T421 -223T392 -225Q332 -225 302 -195T272 -116Q272 -87 289 -60T329 -10L328 -9Q323 -10 315 -10T300 -11Q235 -11 187 7T106 60T58 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM405 292Q405 343 378 377T296 411Q266 411 245 402T209 376T188 338T181 292H405Z" />
+<glyph unicode="&#x11A;" horiz-adv-x="575" d="M73 0V667H528V545H219V404H499V280H219V119H538V0H73ZM469 802L301 705L131 802L184 877L299 808L416 877L469 802Z" />
+<glyph unicode="&#x11B;" horiz-adv-x="573" d="M532 247Q532 242 532 230T530 206H181Q181 152 213 124T311 95Q352 95 386 105T460 137L505 43Q456 14 408 2T304 -11Q239 -11 190 7T108 60T59 143T42 250Q42 322 64 372T121 454T202 500T294 514Q350 514 394 495T469 441T516 357T532 247ZM404 295Q404 346 378 380T296 414Q266 414 245 405T209 379T189 341T182 295H404ZM466 662L298 565L128 662L181 737L296 668L413 737L466 662Z" />
+<glyph unicode="&#x11C;" horiz-adv-x="732" d="M388 100Q456 100 497 132T539 236V258H381V375H676V303Q676 228 660 170T608 71T517 11T383 -10Q315 -10 255 10T151 73T82 180T56 332Q56 421 82 486T153 594T257 657T382 678Q429 678 467 668T535 643T589 610T629 575L541 482Q503 520 466 537T388 555Q293 555 251 499T208 333Q208 279 217 236T248 163T304 117T388 100ZM494 701L369 775L242 701L186 780L367 878L551 780L494 701Z" />
+<glyph unicode="&#x11D;" horiz-adv-x="590" d="M527 8Q527 -64 512 -110T466 -181T394 -216T297 -226Q234 -226 181 -213T74 -170L115 -66Q159 -92 200 -105T293 -119Q343 -119 365 -101T387 -29V71H383Q357 32 329 18T255 3Q199 3 159 23T92 77T54 157T42 256Q42 307 53 354T90 436T155 492T252 513Q300 513 336 498T399 441H401L408 502H527V8ZM388 312Q388 361 363 383T290 405Q257 405 237 393T205 360T189 314T185 260Q185 232 189 205T205 157T237 122T290 109Q319 109 338 117T368 141T383 177T388 225V312ZM418 561L293 635L166 561L110 640L291 738L475 640L418 561Z" />
+<glyph unicode="&#x11E;" horiz-adv-x="732" d="M388 100Q456 100 497 132T539 236V258H381V375H676V303Q676 228 660 170T608 71T517 11T383 -10Q315 -10 255 10T151 73T82 180T56 332Q56 421 82 486T153 594T257 657T382 678Q429 678 467 668T535 643T589 610T629 575L541 482Q503 520 466 537T388 555Q293 555 251 499T208 333Q208 279 217 236T248 163T304 117T388 100ZM554 855Q549 810 530 783T486 740T432 719T379 714Q355 714 327 719T275 739T232 782T208 855H305Q308 829 325 808T380 787Q416 787 435 808T456 855H554H554Z" />
+<glyph unicode="&#x11F;" horiz-adv-x="590" d="M527 8Q527 -64 512 -110T466 -181T394 -216T297 -226Q234 -226 181 -213T74 -170L115 -66Q159 -92 200 -105T293 -119Q343 -119 365 -101T387 -29V71H383Q357 32 329 18T255 3Q199 3 159 23T92 77T54 157T42 256Q42 307 53 354T90 436T155 492T252 513Q300 513 336 498T399 441H401L408 502H527V8ZM388 312Q388 361 363 383T290 405Q257 405 237 393T205 360T189 314T185 260Q185 232 189 205T205 157T237 122T290 109Q319 109 338 117T368 141T383 177T388 225V312ZM473 706Q468 661 449 634T405 591T351 570T298 565Q274 565 246 570T194 590T151 633T127 706H224Q227 680 244 659T299 638Q335 638 354 659T375 706H473H473Z" />
+<glyph unicode="&#x120;" horiz-adv-x="732" d="M388 100Q456 100 497 132T539 236V258H381V375H676V303Q676 228 660 170T608 71T517 11T383 -10Q315 -10 255 10T151 73T82 180T56 332Q56 421 82 486T153 594T257 657T382 678Q429 678 467 668T535 643T589 610T629 575L541 482Q503 520 466 537T388 555Q293 555 251 499T208 333Q208 279 217 236T248 163T304 117T388 100ZM462 786Q462 754 440 735T379 716Q341 716 318 735T295 786Q295 818 318 836T379 855Q417 855 439 837T462 786Z" />
+<glyph unicode="&#x121;" horiz-adv-x="590" d="M527 8Q527 -64 512 -110T466 -181T394 -216T297 -226Q234 -226 181 -213T74 -170L115 -66Q159 -92 200 -105T293 -119Q343 -119 365 -101T387 -29V71H383Q357 32 329 18T255 3Q199 3 159 23T92 77T54 157T42 256Q42 307 53 354T90 436T155 492T252 513Q300 513 336 498T399 441H401L408 502H527V8ZM388 312Q388 361 363 383T290 405Q257 405 237 393T205 360T189 314T185 260Q185 232 189 205T205 157T237 122T290 109Q319 109 338 117T368 141T383 177T388 225V312ZM380 639Q380 607 358 588T297 569Q259 569 236 588T213 639Q213 671 236 689T297 708Q335 708 357 690T380 639Z" />
+<glyph unicode="&#x122;" horiz-adv-x="732" d="M388 100Q456 100 497 132T539 236V258H381V375H676V303Q676 228 660 170T608 71T517 11T383 -10Q315 -10 255 10T151 73T82 180T56 332Q56 421 82 486T153 594T257 657T382 678Q429 678 467 668T535 643T589 610T629 575L541 482Q503 520 466 537T388 555Q293 555 251 499T208 333Q208 279 217 236T248 163T304 117T388 100ZM385 -226H289L322 -55H471L385 -226Z" />
+<glyph unicode="&#x123;" horiz-adv-x="590" d="M527 8Q527 -64 512 -110T466 -181T394 -216T297 -226Q234 -226 181 -213T74 -170L115 -66Q159 -92 200 -105T293 -119Q343 -119 365 -101T387 -29V71H383Q357 32 329 18T255 3Q199 3 159 23T92 77T54 157T42 256Q42 307 53 354T90 436T155 492T252 513Q300 513 336 498T399 441H401L408 502H527V8ZM388 312Q388 361 363 383T290 405Q257 405 237 393T205 360T189 314T185 260Q185 232 189 205T205 157T237 122T290 109Q319 109 338 117T368 141T383 177T388 225V312ZM311 730H400L369 568H222L311 730Z" />
+<glyph unicode="&#x124;" horiz-adv-x="694" d="M475 0V283H219V0H73V667H219V420H475V667H621V0H475ZM480 701L355 775L228 701L172 780L353 878L537 780L480 701Z" />
+<glyph unicode="&#x125;" horiz-adv-x="593" d="M389 0V281Q389 306 387 327T375 363T349 386T303 395Q204 395 204 300V0H63V700H204V453H205Q230 487 266 500T347 513Q398 513 433 499T489 457T520 390T530 303V0H389ZM261 709L136 783L9 709L-47 788L134 886L318 788L261 709Z" />
+<glyph unicode="&#x126;" horiz-adv-x="694" d="M475 0V283H219V0H73V476H-9V585H73V667H219V585H475V667H621V585H703V476H621V0H475ZM475 401V476H219V401H475Z" />
+<glyph unicode="&#x127;" horiz-adv-x="593" d="M204 547V432H208Q233 466 267 479T347 493Q398 493 433 478T489 436T520 369T530 282V0H388V260Q388 285 386 306T375 342T349 365T303 374Q204 374 204 280V0H63V547H1V637H63V700H204V637H364V547H204Z" />
+<glyph unicode="&#x128;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM332 795Q318 750 286 729T219 707Q196 707 178 713T143 728T112 743T82 750Q65 750 49 741T21 706L-44 759Q-23 809 4 827T68 846Q89 846 107 839T142 824T174 809T206 802Q223 802 239 811T265 846L332 795H332Z" />
+<glyph unicode="&#x129;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM321 655Q307 610 275 589T208 567Q185 567 167 573T132 588T101 603T71 610Q54 610 38 601T10 566L-55 619Q-34 669 -7 687T57 706Q78 706 96 699T131 684T163 669T195 662Q212 662 228 671T254 706L321 655H321Z" />
+<glyph unicode="&#x12A;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM-15 727V814H308V727H-15Z" />
+<glyph unicode="&#x12B;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM-30 587V674H293V587H-30Z" />
+<glyph unicode="&#x12C;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM318 846Q313 801 294 774T250 731T196 710T143 705Q119 705 91 710T39 730T-4 773T-28 846H69Q72 820 89 799T144 778Q180 778 199 799T220 846H318H318Z" />
+<glyph unicode="&#x12D;" horiz-adv-x="267" d="M63 0V502H204V0H63ZM308 706Q303 661 284 634T240 591T186 570T133 565Q109 565 81 570T29 590T-14 633T-38 706H59Q62 680 79 659T134 638Q170 638 189 659T210 706H308H308Z" />
+<glyph unicode="&#x12E;" horiz-adv-x="292" d="M182 0Q138 -33 123 -58T107 -106Q107 -131 121 -143T155 -156Q175 -156 186 -151T207 -141L230 -219Q223 -222 211 -226T185 -233T155 -239T126 -241Q66 -241 38 -211T9 -131Q9 -86 37 -55T104 0H73V667H219V0H182Z" />
+<glyph unicode="&#x12F;" horiz-adv-x="267" d="M172 0Q128 -33 113 -58T97 -106Q97 -131 111 -143T145 -156Q165 -156 176 -151T197 -141L220 -219Q206 -226 177 -233T120 -241Q59 -241 29 -211T-1 -131Q-1 -91 26 -57T93 0H63V502H204V0H172ZM218 633Q218 601 195 583T134 564Q96 564 73 582T50 633Q50 665 73 684T134 703Q172 703 195 684T218 633Z" />
+<glyph unicode="&#x130;" horiz-adv-x="292" d="M73 0V667H219V0H73ZM231 786Q231 754 209 735T148 716Q110 716 87 735T64 786Q64 818 87 836T148 855Q186 855 208 837T231 786Z" />
+<glyph unicode="&#x131;" horiz-adv-x="267" d="M63 0V502H204V0H63Z" />
+<glyph unicode="&#x132;" horiz-adv-x="731" d="M73 0V667H219V0H73ZM658 228Q658 176 651 132T621 56T561 5T459 -13Q402 -13 359 2T272 53L337 160Q367 131 391 121T445 111Q477 111 494 130T512 197V667H658V228Z" />
+<glyph unicode="&#x133;" horiz-adv-x="538" d="M65 0V502H206V0H65ZM220 633Q220 601 197 583T135 564Q97 564 74 582T51 633Q51 665 74 684T135 703Q173 703 196 684T220 633ZM488 633Q488 615 481 602T462 581T435 568T403 564Q387 564 372 568T345 580T326 602T319 633Q319 651 326 664T345 686T372 699T403 703Q419 703 434 699T462 687T481 665T488 633ZM475 -17Q475 -123 425 -174T269 -226Q247 -226 227 -225T186 -221V-103Q219 -106 252 -106Q281 -106 297 -100T322 -80T332 -47T334 0V502H475V-17Z" />
+<glyph unicode="&#x134;" horiz-adv-x="490" d="M417 228Q417 176 410 132T380 56T320 5T218 -13Q161 -13 118 2T31 53L96 160Q126 131 150 121T204 111Q236 111 253 130T271 197V667H417V228H417ZM469 701L344 775L217 701L161 780L342 878L526 780L469 701Z" />
+<glyph unicode="&#x135;" horiz-adv-x="274" d="M211 -17Q211 -226 5 -226Q-17 -226 -37 -225T-78 -221V-103Q-45 -106 -12 -106Q17 -106 33 -100T58 -80T68 -47T70 0V502H211V-17ZM269 561L144 635L17 561L-39 640L142 738L326 640L269 561Z" />
+<glyph unicode="&#x136;" horiz-adv-x="644" d="M451 0L270 279L219 223V0H73V667H219V389H223L428 667H612L380 376L637 0H451ZM323 -226H227L260 -55H409L323 -226Z" />
+<glyph unicode="&#x137;" horiz-adv-x="555" d="M380 0L252 214L204 162V0H63V700H204V314H208L363 502H538L354 301L551 0H380ZM283 -226H187L220 -55H369L283 -226Z" />
+<glyph unicode="&#x139;" horiz-adv-x="543" d="M73 0V667H219V123H502V0H73ZM150 705L66 750L205 882L346 823L150 705Z" />
+<glyph unicode="&#x13A;" horiz-adv-x="316" d="M288 0Q250 -6 186 -6Q116 -6 90 20T63 109V700H204V142Q204 119 211 114T239 109H288V0H288ZM147 735L63 780L202 912L343 853L147 735Z" />
+<glyph unicode="&#x13B;" horiz-adv-x="543" d="M73 0V667H219V123H502V0H73ZM271 -226H175L208 -55H357L271 -226Z" />
+<glyph unicode="&#x13C;" horiz-adv-x="316" d="M288 0Q250 -6 186 -6Q116 -6 90 20T63 109V700H204V142Q204 119 211 114T239 109H288V0H288ZM176 -226H80L113 -55H262L176 -226Z" />
+<glyph unicode="&#x13D;" horiz-adv-x="543" d="M73 0V667H219V123H502V0H73ZM462 518H368L405 700H543L462 518Z" />
+<glyph unicode="&#x13E;" horiz-adv-x="316" d="M288 0Q250 -6 186 -6Q116 -6 90 20T63 109V700H204V142Q204 119 211 114T239 109H288V0ZM352 518H258L295 700H433L352 518Z" />
+<glyph unicode="&#x13F;" horiz-adv-x="543" d="M73 0V667H219V123H502V0H73ZM526 323Q526 292 499 272T433 251Q393 251 367 271T340 323Q340 353 367 373T433 394Q472 394 499 374T526 323Z" />
+<glyph unicode="&#x140;" horiz-adv-x="443" d="M288 0Q250 -6 186 -6Q116 -6 90 20T63 109V700H204V142Q204 119 211 114T239 109H288V0ZM451 242Q451 211 424 191T358 170Q318 170 292 190T265 242Q265 272 292 292T358 313Q397 313 424 293T451 242Z" />
+<glyph unicode="&#x141;" horiz-adv-x="543" d="M73 0V269L7 228V341L73 383V667H219V474L395 585V466L219 354V123H502V0H73Z" />
+<glyph unicode="&#x142;" horiz-adv-x="316" d="M261 366V142Q261 119 268 114T296 109H345V-2Q325 -5 302 -7T243 -9Q208 -9 184 -3T146 18T126 55T120 109V282L63 244V357L120 396V700H261V475L356 537V428L261 366Z" />
+<glyph unicode="&#x143;" horiz-adv-x="731" d="M532 0L219 425H215V0H73V667H208L512 249H517V667H658V0H532ZM371 705L287 750L426 882L567 823L371 705Z" />
+<glyph unicode="&#x144;" horiz-adv-x="593" d="M389 0V281Q389 306 386 327T374 363T348 386T302 395Q204 395 204 300V0H63V502H185L194 442H196Q224 482 260 497T346 513Q398 513 433 499T489 457T520 390T530 303V0H389ZM307 565L223 610L362 742L503 683L307 565Z" />
+<glyph unicode="&#x145;" horiz-adv-x="731" d="M532 0L219 425H215V0H73V667H208L512 249H517V667H658V0H532ZM358 -226H262L295 -55H444L358 -226Z" />
+<glyph unicode="&#x146;" horiz-adv-x="593" d="M389 0V281Q389 306 386 327T374 363T348 386T302 395Q204 395 204 300V0H63V502H185L194 442H196Q224 482 260 497T346 513Q398 513 433 499T489 457T520 390T530 303V0H389ZM293 -226H197L230 -55H379L293 -226Z" />
+<glyph unicode="&#x147;" horiz-adv-x="731" d="M532 0L219 425H215V0H73V667H208L512 249H517V667H658V0H532ZM540 802L372 705L202 802L255 877L370 808L487 877L540 802Z" />
+<glyph unicode="&#x148;" horiz-adv-x="593" d="M389 0V281Q389 306 386 327T374 363T348 386T302 395Q204 395 204 300V0H63V502H185L194 442H196Q224 482 260 497T346 513Q398 513 433 499T489 457T520 390T530 303V0H389ZM473 662L305 565L135 662L188 737L303 668L420 737L473 662Z" />
+<glyph unicode="&#x14A;" horiz-adv-x="731" d="M422 -121H436Q474 -121 496 -100T518 -33V9L219 430H215V0H73V667H208L512 242H517V667H658V-29Q658 -136 608 -191T455 -246H422V-121Z" />
+<glyph unicode="&#x14B;" horiz-adv-x="593" d="M185 502L194 442H196Q224 482 260 497T346 513Q398 513 433 499T489 457T520 390T530 303V-17Q530 -127 480 -182T323 -238H289V-108H303Q388 -108 388 -16V281Q388 306 386 327T374 363T348 386T302 395Q204 395 204 300V0H63V502H185Z" />
+<glyph unicode="&#x14C;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM229 727V814H552V727H229Z" />
+<glyph unicode="&#x14D;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM140 587V674H463V587H140Z" />
+<glyph unicode="&#x14E;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM562 855Q557 810 538 783T494 740T440 719T387 714Q363 714 335 719T283 739T240 782T216 855H313Q316 829 333 808T388 787Q424 787 443 808T464 855H562H562Z" />
+<glyph unicode="&#x14F;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM475 706Q470 661 451 634T407 591T353 570T300 565Q276 565 248 570T196 590T153 633T129 706H226Q229 680 246 659T301 638Q337 638 356 659T377 706H475H475Z" />
+<glyph unicode="&#x150;" horiz-adv-x="772" d="M716 334Q716 245 692 180T625 73T521 11T386 -10Q312 -10 252 10T148 73T80 180T56 334Q56 422 79 487T146 594T251 657T386 678Q461 678 521 658T625 595T692 487T716 334ZM564 334Q564 388 556 430T526 501T471 545T386 560Q335 560 301 545T246 502T217 431T208 334Q208 280 216 238T246 167T301 123T386 108Q437 108 471 123T526 167T555 238T564 334ZM544 702L472 746L619 877L740 820L544 702ZM276 702L205 746L352 877L473 820L276 702Z" />
+<glyph unicode="&#x151;" horiz-adv-x="601" d="M42 251Q42 306 56 353T102 436T182 492T300 513Q428 513 493 444T559 251Q559 193 543 145T495 62T415 8T300 -11Q235 -11 187 8T106 61T58 144T42 251ZM185 251Q185 176 210 136T301 95Q365 95 390 135T416 251Q416 325 391 364T301 403Q236 403 211 364T185 251ZM464 555L392 599L539 730L660 673L464 555ZM196 555L125 599L272 730L393 673L196 555Z" />
+<glyph unicode="&#x152;" horiz-adv-x="957" d="M491 0Q466 -5 436 -7T378 -10Q304 -10 245 10T143 73T79 180T56 334Q56 422 78 487T143 594T244 657T378 678Q407 678 436 675T491 667H910V545H637V404H881V280H637V119H920V0H491ZM384 108Q419 108 445 113T491 126V540Q472 549 446 554T384 560Q333 560 299 545T245 502T217 431T208 334Q208 280 216 238T245 167T299 123T384 108Z" />
+<glyph unicode="&#x153;" horiz-adv-x="956" d="M915 244Q915 237 915 229T914 204H558Q564 97 688 97Q763 97 836 138L882 43Q834 16 786 3T682 -11Q619 -11 573 9T492 65Q427 -11 301 -11Q173 -11 108 58T42 251Q42 308 57 356T104 439T184 493T301 513Q362 513 409 495T489 438Q524 477 572 495T672 514Q727 514 772 495T849 441T898 356T915 244ZM788 295Q783 345 757 378T674 411Q620 411 590 381T558 295H788ZM415 251Q415 326 390 363T301 400Q236 400 211 363T185 251Q185 167 213 133T301 98Q360 98 387 132T415 251Z" />
+<glyph unicode="&#x154;" horiz-adv-x="645" d="M458 0L364 228Q358 243 351 248T328 253H219V0H73V663Q187 675 304 675Q355 675 406 667T497 637T563 575T588 471Q588 408 563 362T475 289V288Q484 285 490 275T500 255L611 0H458ZM446 463Q446 495 435 514T404 544T361 558T310 561Q286 561 264 560T219 556V367H327Q353 367 375 371T412 385T437 414T446 463ZM289 708L205 753L344 885L485 826L289 708Z" />
+<glyph unicode="&#x155;" horiz-adv-x="404" d="M345 388Q314 388 288 385T244 372T215 341T204 287V0H63V502H184L195 435H199Q220 473 252 492T343 512H378V388H345ZM214 565L130 610L269 742L410 683L214 565Z" />
+<glyph unicode="&#x156;" horiz-adv-x="645" d="M458 0L364 228Q358 243 351 248T328 253H219V0H73V663Q187 675 304 675Q355 675 406 667T497 637T563 575T588 471Q588 408 563 362T475 289V288Q484 285 490 275T500 255L611 0H458ZM446 463Q446 495 435 514T404 544T361 558T310 561Q286 561 264 560T219 556V367H327Q353 367 375 371T412 385T437 414T446 463ZM318 -226H222L255 -55H404L318 -226Z" />
+<glyph unicode="&#x157;" horiz-adv-x="404" d="M345 388Q314 388 288 385T244 372T215 341T204 287V0H63V502H184L195 435H199Q220 473 252 492T343 512H378V388H345ZM121 -226H25L58 -55H207L121 -226Z" />
+<glyph unicode="&#x158;" horiz-adv-x="645" d="M458 0L364 228Q358 243 351 248T328 253H219V0H73V663Q187 675 304 675Q355 675 406 667T497 637T563 575T588 471Q588 408 563 362T475 289V288Q484 285 490 275T500 255L611 0H458ZM446 463Q446 495 435 514T404 544T361 558T310 561Q286 561 264 560T219 556V367H327Q353 367 375 371T412 385T437 414T446 463ZM478 802L310 705L140 802L193 877L308 808L425 877L478 802Z" />
+<glyph unicode="&#x159;" horiz-adv-x="404" d="M345 388Q314 388 288 385T244 372T215 341T204 287V0H63V502H184L195 435H199Q220 473 252 492T343 512H378V388H345ZM381 662L213 565L43 662L96 737L211 668L328 737L381 662Z" />
+<glyph unicode="&#x15A;" horiz-adv-x="601" d="M553 195Q553 138 533 99T479 35T399 1T302 -10Q224 -10 161 8T33 71L97 175Q143 145 191 125T295 104Q315 104 335 107T372 118T399 142T410 182Q410 207 394 223T353 251T294 272T226 294Q193 305 162 320T107 357T68 412T53 489Q53 543 74 578T130 636T209 667T299 676Q362 676 418 661T530 617L478 505Q440 528 396 543T309 559Q287 559 268 557T233 547T209 527T200 494Q200 473 211 459T241 435T287 417T344 401Q380 391 417 378T485 341T534 282T553 195ZM296 709L212 754L351 886L492 827L296 709Z" />
+<glyph unicode="&#x15B;" horiz-adv-x="492" d="M457 151Q457 67 403 28T250 -11Q131 -11 26 50L75 146Q117 123 161 108T249 92Q285 92 305 104T325 145Q325 160 317 168T295 183T262 192T222 201Q189 208 157 218T99 247T59 294T43 364Q43 395 55 422T93 469T158 501T250 513Q299 513 346 503T439 470L402 370Q370 387 332 397T261 408Q225 408 200 398T175 362Q175 348 184 340T209 326T246 316T290 307Q320 301 349 291T403 264T442 220T457 151ZM254 565L170 610L309 742L450 683L254 565Z" />
+<glyph unicode="&#x15C;" horiz-adv-x="601" d="M553 195Q553 138 533 99T479 35T399 1T302 -10Q224 -10 161 8T33 71L97 175Q143 145 191 125T295 104Q315 104 335 107T372 118T399 142T410 182Q410 207 394 223T353 251T294 272T226 294Q193 305 162 320T107 357T68 412T53 489Q53 543 74 578T130 636T209 667T299 676Q362 676 418 661T530 617L478 505Q440 528 396 543T309 559Q287 559 268 557T233 547T209 527T200 494Q200 473 211 459T241 435T287 417T344 401Q380 391 417 378T485 341T534 282T553 195ZM432 701L307 775L180 701L124 780L305 878L489 780L432 701Z" />
+<glyph unicode="&#x15D;" horiz-adv-x="492" d="M457 151Q457 67 403 28T250 -11Q131 -11 26 50L75 146Q117 123 161 108T249 92Q285 92 305 104T325 145Q325 160 317 168T295 183T262 192T222 201Q189 208 157 218T99 247T59 294T43 364Q43 395 55 422T93 469T158 501T250 513Q299 513 346 503T439 470L402 370Q370 387 332 397T261 408Q225 408 200 398T175 362Q175 348 184 340T209 326T246 316T290 307Q320 301 349 291T403 264T442 220T457 151ZM389 561L264 635L137 561L81 640L262 738L446 640L389 561Z" />
+<glyph unicode="&#x15E;" horiz-adv-x="601" d="M411 -123Q411 -161 383 -187T298 -213Q272 -213 250 -205T210 -188L226 -140Q246 -150 260 -154T292 -159Q310 -159 324 -153T339 -126Q339 -92 296 -92Q272 -92 256 -100L229 -86L266 -9Q200 -5 146 14T33 71L97 175Q143 145 191 125T295 104Q315 104 335 107T372 118T399 142T410 182Q410 207 394 223T353 251T294 272T226 294Q193 305 162 320T107 357T68 412T53 489Q53 543 74 578T130 636T209 667T299 676Q362 676 418 661T530 617L478 505Q440 528 396 543T309 559Q287 559 268 557T233 547T209 527T200 494Q200 473 211 459T241 435T287 417T344 401Q380 391 417 378T485 341T534 282T553 195Q553 141 536 104T488 42T417 6T330 -9L309 -47L310 -48Q315 -47 321 -47T334 -46Q369 -46 390 -66T411 -123Z" />
+<glyph unicode="&#x15F;" horiz-adv-x="492" d="M354 -126Q354 -164 325 -190T241 -216Q215 -216 192 -209T152 -191L168 -143Q187 -153 202 -158T235 -163Q252 -163 266 -156T281 -129Q281 -95 239 -95Q226 -95 217 -97T199 -103L171 -89L210 -9Q161 -4 115 10T26 50L75 146Q117 123 161 108T249 92Q285 92 305 104T326 145Q326 158 318 167T296 182T262 194T222 204Q189 211 157 221T99 249T59 294T43 364Q43 395 55 422T93 469T158 501T250 513Q299 513 346 503T439 470L402 376Q370 394 332 404T261 414Q225 414 201 404T176 368Q176 354 185 346T209 331T246 320T290 310Q320 304 349 294T403 266T442 221T457 151Q457 72 409 34T273 -10L251 -51L252 -52Q257 -51 263 -51T275 -50Q311 -50 332 -69T354 -126Z" />
+<glyph unicode="&#x160;" horiz-adv-x="601" d="M553 195Q553 138 533 99T479 35T399 1T302 -10Q224 -10 161 8T33 71L97 175Q143 145 191 125T295 104Q315 104 335 107T372 118T399 142T410 182Q410 207 394 223T353 251T294 272T226 294Q193 305 162 320T107 357T68 412T53 489Q53 543 74 578T130 636T209 667T299 676Q362 676 418 661T530 617L478 505Q440 528 396 543T309 559Q287 559 268 557T233 547T209 527T200 494Q200 473 211 459T241 435T287 417T344 401Q380 391 417 378T485 341T534 282T553 195ZM474 802L306 705L136 802L189 877L304 808L421 877L474 802Z" />
+<glyph unicode="&#x161;" horiz-adv-x="492" d="M457 151Q457 67 403 28T250 -11Q131 -11 26 50L75 146Q117 123 161 108T249 92Q285 92 305 104T325 145Q325 160 317 168T295 183T262 192T222 201Q189 208 157 218T99 247T59 294T43 364Q43 395 55 422T93 469T158 501T250 513Q299 513 346 503T439 470L402 370Q370 387 332 397T261 408Q225 408 200 398T175 362Q175 348 184 340T209 326T246 316T290 307Q320 301 349 291T403 264T442 220T457 151ZM430 662L262 565L92 662L145 737L260 668L377 737L430 662Z" />
+<glyph unicode="&#x162;" horiz-adv-x="549" d="M347 543V0H201V543H14V667H535V543H347ZM254 -228H158L191 -57H340L254 -228Z" />
+<glyph unicode="&#x163;" horiz-adv-x="391" d="M353 -2Q324 -6 305 -7T266 -9Q215 -9 182 -1T130 25T103 72T96 145L97 389H15V502H98V635H237V502H353V389H237V164Q237 146 239 135T249 117T276 109T327 107Q335 107 339 107T353 108V-2H353ZM221 -226H125L158 -55H307L221 -226Z" />
+<glyph unicode="&#x164;" horiz-adv-x="549" d="M347 543V0H201V543H14V667H535V543H347ZM446 802L278 705L108 802L161 877L276 808L393 877L446 802Z" />
+<glyph unicode="&#x165;" horiz-adv-x="391" d="M353 -2Q324 -6 305 -7T266 -9Q215 -9 182 -1T130 25T103 72T96 145L97 389H15V502H98V635H237V502H353V389H237V164Q237 146 239 135T249 117T276 109T327 107Q335 107 339 107T353 108V-2ZM413 518H319L356 700H494L413 518Z" />
+<glyph unicode="&#x166;" horiz-adv-x="549" d="M347 289V0H201V289H43V402H201V543H14V667H535V543H347V402H505V289H347Z" />
+<glyph unicode="&#x167;" horiz-adv-x="391" d="M237 217V164Q237 146 239 135T249 117T276 109T327 107H353V-2Q324 -6 305 -7T266 -9Q215 -9 182 -1T130 25T104 72T96 145V217H15V323H97V389H15V502H98V635H237V502H353V389H237V323H353V217H237Z" />
+<glyph unicode="&#x168;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM536 795Q522 750 490 729T423 707Q400 707 382 713T347 728T316 743T286 750Q269 750 253 741T225 706L160 759Q181 809 208 827T272 846Q293 846 311 839T346 824T378 809T410 802Q427 802 443 811T469 846L536 795H536Z" />
+<glyph unicode="&#x169;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM479 655Q465 610 433 589T366 567Q343 567 325 573T290 588T259 603T229 610Q212 610 196 601T168 566L103 619Q124 669 151 687T215 706Q236 706 254 699T289 684T321 669T353 662Q370 662 386 671T412 706L479 655H479Z" />
+<glyph unicode="&#x16A;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM190 727V814H513V727H190Z" />
+<glyph unicode="&#x16B;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM129 587V674H452V587H129Z" />
+<glyph unicode="&#x16C;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM525 846Q520 801 501 774T457 731T403 710T350 705Q326 705 298 710T246 730T203 773T179 846H276Q279 820 296 799T351 778Q387 778 406 799T427 846H525H525Z" />
+<glyph unicode="&#x16D;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM467 706Q462 661 443 634T399 591T345 570T292 565Q268 565 240 570T188 590T145 633T121 706H218Q221 680 238 659T293 638Q329 638 348 659T369 706H467H467Z" />
+<glyph unicode="&#x16E;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM463 796Q463 773 455 754T431 720T396 698T351 690Q328 690 308 698T273 720T249 753T240 796Q240 844 272 873T351 903Q399 903 431 874T463 796ZM402 796Q402 819 388 833T352 848Q329 848 315 834T301 796Q301 773 315 759T352 744Q374 744 388 758T402 796Z" />
+<glyph unicode="&#x16F;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM404 668Q404 645 396 626T372 592T337 570T292 562Q269 562 249 570T214 592T190 625T181 668Q181 716 213 745T292 775Q340 775 372 746T404 668ZM343 668Q343 691 329 705T293 720Q270 720 256 706T242 668Q242 645 256 631T293 616Q315 616 329 630T343 668Z" />
+<glyph unicode="&#x170;" horiz-adv-x="701" d="M628 268Q628 202 614 150T566 62T480 8T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628ZM499 706L427 750L574 881L695 824L499 706ZM231 706L160 750L307 881L428 824L231 706Z" />
+<glyph unicode="&#x171;" horiz-adv-x="584" d="M398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H398ZM444 566L372 610L519 741L640 684L444 566ZM176 566L105 610L252 741L373 684L176 566Z" />
+<glyph unicode="&#x172;" horiz-adv-x="701" d="M628 268Q628 168 594 102T479 8Q435 -25 421 -50T406 -97Q406 -123 420 -135T454 -148Q474 -148 485 -143T506 -133L529 -211Q515 -217 486 -224T429 -232Q368 -232 338 -202T308 -123Q308 -90 326 -61T374 -10Q367 -11 362 -11T350 -11Q273 -11 220 7T135 62T88 150T73 268V667H219V290Q219 252 222 220T239 163T278 126T350 112Q396 112 422 125T461 163T478 219T482 290V667H628V268H628Z" />
+<glyph unicode="&#x173;" horiz-adv-x="584" d="M480 0Q439 -29 422 -56T405 -105Q405 -131 419 -143T453 -156Q473 -156 484 -151T505 -141L528 -219Q514 -226 485 -233T428 -241Q368 -241 338 -211T307 -130Q307 -89 333 -56T398 0L390 58H386Q364 25 329 7T242 -12Q200 -12 167 -3T110 29T75 90T63 188V502H204V234Q204 205 206 181T216 141T241 115T288 106Q345 106 362 132T380 207V502H521V0H480Z" />
+<glyph unicode="&#x174;" horiz-adv-x="1005" d="M788 0H632L503 473H498L367 0H204L21 667H182L291 177H295L431 667H579L708 177H712L829 667H984L788 0ZM628 701L503 775L376 701L320 780L501 878L685 780L628 701Z" />
+<glyph unicode="&#x175;" horiz-adv-x="796" d="M627 0H493L398 298H394L303 0H166L16 502H163L242 150H246L342 502H452L551 149H555L638 502H780L627 0ZM523 561L398 635L271 561L215 640L396 738L580 640L523 561Z" />
+<glyph unicode="&#x176;" horiz-adv-x="646" d="M397 262V0H251V259L2 667H170L325 390L483 667H644L397 262ZM453 701L328 775L201 701L145 780L326 878L510 780L453 701Z" />
+<glyph unicode="&#x177;" horiz-adv-x="549" d="M335 -21Q310 -83 290 -123T247 -186T192 -217T115 -226Q101 -226 83 -225T40 -221V-104Q47 -106 60 -106T96 -107Q116 -107 128 -104T150 -94T165 -76T179 -47L203 11L7 502H160L274 154H278L391 502H542L335 -21ZM404 561L279 635L152 561L96 640L277 738L461 640L404 561Z" />
+<glyph unicode="&#x178;" horiz-adv-x="646" d="M397 262V0H251V259L2 667H170L325 390L483 667H644L397 262ZM507 779Q507 750 487 730T438 709Q409 709 389 729T368 779Q368 808 388 828T438 848Q467 848 487 828T507 779ZM284 779Q284 750 264 730T215 709Q186 709 166 729T146 779Q146 808 166 828T215 848Q244 848 264 828T284 779Z" />
+<glyph unicode="&#x179;" horiz-adv-x="607" d="M43 0V107L367 543H59V667H548V560L220 119H564V0H43ZM298 705L214 750L353 882L494 823L298 705Z" />
+<glyph unicode="&#x17A;" horiz-adv-x="482" d="M34 0V95L267 389H37V502H439V401L210 112H448V0H34ZM232 565L148 610L287 742L428 683L232 565Z" />
+<glyph unicode="&#x17B;" horiz-adv-x="607" d="M43 0V107L367 543H59V667H548V560L220 119H564V0H43ZM397 786Q397 754 375 735T314 716Q276 716 253 735T230 786Q230 818 253 836T314 855Q352 855 374 837T397 786Z" />
+<glyph unicode="&#x17C;" horiz-adv-x="482" d="M34 0V95L267 389H37V502H439V401L210 112H448V0H34ZM335 639Q335 607 313 588T252 569Q214 569 191 588T168 639Q168 671 191 689T252 708Q290 708 312 690T335 639Z" />
+<glyph unicode="&#x17D;" horiz-adv-x="607" d="M43 0V107L367 543H59V667H548V560L220 119H564V0H43ZM480 802L312 705L142 802L195 877L310 808L427 877L480 802Z" />
+<glyph unicode="&#x17E;" horiz-adv-x="482" d="M34 0V95L267 389H37V502H439V401L210 112H448V0H34ZM424 662L256 565L86 662L139 737L254 668L371 737L424 662Z" />
+<glyph unicode="&#x192;" horiz-adv-x="613" d="M553 591Q541 592 534 593T511 594Q467 594 450 579T428 535L410 448H518L498 334H392L325 -7Q306 -100 262 -147T131 -195Q108 -195 85 -192T42 -182L60 -80Q74 -84 86 -85T119 -86Q156 -86 172 -51T198 43L251 334H174L194 448H271L290 553Q297 594 311 621T350 664T413 688T507 695Q524 695 537 695T571 694L553 591Z" />
+<glyph unicode="&#x1FC;" horiz-adv-x="939" d="M454 0V151H234L165 0H14L355 667H892V548H597V406H863V283H597V119H902V0H454ZM454 547H431L290 273H454V547ZM606 705L522 750L661 882L802 823L606 705Z" />
+<glyph unicode="&#x1FD;" horiz-adv-x="891" d="M850 244Q850 237 850 229T848 203H493Q496 152 529 125T622 97Q660 97 696 106T771 138L817 43Q769 15 720 2T617 -11Q549 -11 499 10T414 69Q376 37 324 13T200 -12Q124 -12 80 28T36 141Q36 190 57 224T118 281T217 313T350 323V340Q350 356 348 368T339 389T316 401T276 406Q240 406 191 394T94 355L48 453Q109 484 166 498T288 513Q341 513 381 500T444 457Q477 487 519 500T606 514Q661 514 706 496T783 442T832 357T850 244ZM722 295Q717 348 691 379T608 411Q555 411 524 381T492 295H722ZM371 134Q360 153 356 176T351 226V242Q253 242 213 219T173 149Q173 117 192 97T243 77Q269 77 302 92T371 134ZM466 565L382 610L521 742L662 683L466 565Z" />
+<glyph unicode="&#x1FE;" horiz-adv-x="785" d="M729 334Q729 244 704 179T634 72T528 10T393 -10Q338 -10 287 1T192 40L131 -26L56 59L112 118Q56 200 56 334Q56 424 81 489T151 596T258 658T393 678Q450 678 501 666T594 628L653 690L733 612L673 549Q702 508 715 454T729 334ZM577 334Q577 362 572 391T556 443L287 134Q332 108 393 108Q493 108 535 167T577 334ZM208 334Q208 304 212 276T226 225L500 534Q480 546 454 553T393 560Q343 560 308 545T250 502T218 431T208 334ZM373 709L289 754L428 886L569 827L373 709Z" />
+<glyph unicode="&#x1FF;" horiz-adv-x="601" d="M510 424Q536 390 547 346T559 251Q559 193 544 145T496 62T416 8T301 -11Q205 -11 143 33L91 -23L31 41L80 95Q60 127 51 166T42 251Q42 306 56 353T102 436T183 492T301 513Q384 513 441 482L498 539L563 480L510 424ZM360 379Q349 390 334 395T301 400Q236 400 211 362T185 251Q185 240 186 222T191 193L360 379ZM416 251Q416 268 414 289T406 324L224 129Q235 114 257 106T302 98Q365 98 390 136T416 251ZM299 565L215 610L354 742L495 683L299 565Z" />
+<glyph unicode="&#x218;" horiz-adv-x="601" d="M553 195Q553 138 533 99T479 35T399 1T302 -10Q224 -10 161 8T33 71L97 175Q143 145 191 125T295 104Q315 104 335 107T372 118T399 142T410 182Q410 207 394 223T353 251T294 272T226 294Q193 305 162 320T107 357T68 412T53 489Q53 543 74 578T130 636T209 667T299 676Q362 676 418 661T530 617L478 505Q440 528 396 543T309 559Q287 559 268 557T233 547T209 527T200 494Q200 473 211 459T241 435T287 417T344 401Q380 391 417 378T485 341T534 282T553 195ZM289 -226H193L226 -55H375L289 -226Z" />
+<glyph unicode="&#x219;" horiz-adv-x="492" d="M457 151Q457 67 403 28T250 -11Q131 -11 26 50L75 146Q117 123 161 108T249 92Q285 92 305 104T325 145Q325 160 317 168T295 183T262 192T222 201Q189 208 157 218T99 247T59 294T43 364Q43 395 55 422T93 469T158 501T250 513Q299 513 346 503T439 470L402 370Q370 387 332 397T261 408Q225 408 200 398T175 362Q175 348 184 340T209 326T246 316T290 307Q320 301 349 291T403 264T442 220T457 151ZM239 -226H143L176 -55H325L239 -226Z" />
+<glyph unicode="&#x237;" horiz-adv-x="274" d="M211 -17Q211 -226 5 -226Q-17 -226 -37 -225T-78 -221V-103Q-45 -106 -12 -106Q17 -106 33 -100T58 -80T68 -47T70 0V502H211V-17H211Z" />
+<glyph unicode="&#x2C6;" horiz-adv-x="348" d="M300 561L175 635L48 561L-8 640L173 738L357 640L300 561Z" />
+<glyph unicode="&#x2C7;" horiz-adv-x="348" d="M343 662L175 565L5 662L58 737L173 668L290 737L343 662Z" />
+<glyph unicode="&#x2D8;" horiz-adv-x="348" d="M347 706Q342 661 323 634T279 591T225 570T172 565Q148 565 120 570T68 590T25 633T1 706H98Q101 680 118 659T173 638Q209 638 228 659T249 706H347H347Z" />
+<glyph unicode="&#x2D9;" horiz-adv-x="348" d="M257 639Q257 607 235 588T174 569Q136 569 113 588T90 639Q90 671 113 689T174 708Q212 708 234 690T257 639Z" />
+<glyph unicode="&#x2DA;" horiz-adv-x="348" d="M285 668Q285 645 277 626T253 592T218 570T173 562Q150 562 130 570T95 592T71 625T62 668Q62 716 94 745T173 775Q221 775 253 746T285 668ZM224 668Q224 691 210 705T174 720Q151 720 137 706T123 668Q123 645 137 631T174 616Q196 616 210 630T224 668Z" />
+<glyph unicode="&#x2DB;" horiz-adv-x="348" d="M204 -24Q186 -40 173 -62T159 -105Q159 -131 173 -143T207 -156Q227 -156 238 -151T259 -141L282 -219Q268 -226 239 -233T182 -241Q122 -241 92 -211T61 -130Q61 -104 73 -81T104 -38T147 -3T198 22L235 1L204 -24Z" />
+<glyph unicode="&#x2DC;" horiz-adv-x="348" d="M362 655Q348 610 316 589T249 567Q226 567 208 573T173 588T142 603T112 610Q95 610 79 601T51 566L-14 619Q7 669 34 687T98 706Q119 706 137 699T172 684T204 669T236 662Q253 662 269 671T295 706L362 655H362Z" />
+<glyph unicode="&#x2DD;" horiz-adv-x="348" d="M261 566L189 610L336 741L457 684L261 566ZM-7 566L-78 610L69 741L190 684L-7 566Z" />
+<glyph unicode="&#x1E80;" horiz-adv-x="1005" d="M788 0H632L503 473H498L367 0H204L21 667H182L291 177H295L431 667H579L708 177H712L829 667H984L788 0ZM505 705L309 823L450 881L589 750L505 705Z" />
+<glyph unicode="&#x1E81;" horiz-adv-x="796" d="M627 0H493L398 298H394L303 0H166L16 502H163L242 150H246L342 502H452L551 149H555L638 502H780L627 0ZM400 565L204 683L345 741L484 610L400 565Z" />
+<glyph unicode="&#x1E82;" horiz-adv-x="1005" d="M788 0H632L503 473H498L367 0H204L21 667H182L291 177H295L431 667H579L708 177H712L829 667H984L788 0ZM509 705L425 750L564 882L705 823L509 705Z" />
+<glyph unicode="&#x1E83;" horiz-adv-x="796" d="M627 0H493L398 298H394L303 0H166L16 502H163L242 150H246L342 502H452L551 149H555L638 502H780L627 0ZM398 565L314 610L453 742L594 683L398 565Z" />
+<glyph unicode="&#x1E84;" horiz-adv-x="1005" d="M788 0H632L503 473H498L367 0H204L21 667H182L291 177H295L431 667H579L708 177H712L829 667H984L788 0ZM682 779Q682 750 662 730T613 709Q584 709 564 729T543 779Q543 808 563 828T613 848Q642 848 662 828T682 779ZM459 779Q459 750 439 730T390 709Q361 709 341 729T321 779Q321 808 341 828T390 848Q419 848 439 828T459 779Z" />
+<glyph unicode="&#x1E85;" horiz-adv-x="796" d="M627 0H493L398 298H394L303 0H166L16 502H163L242 150H246L342 502H452L551 149H555L638 502H780L627 0ZM577 639Q577 610 557 590T508 569Q479 569 459 589T438 639Q438 668 458 688T508 708Q537 708 557 688T577 639ZM354 639Q354 610 334 590T285 569Q256 569 236 589T216 639Q216 668 236 688T285 708Q314 708 334 688T354 639Z" />
+<glyph unicode="&#x1EF2;" horiz-adv-x="646" d="M397 262V0H251V259L2 667H170L325 390L483 667H644L397 262ZM322 705L126 823L267 881L406 750L322 705Z" />
+<glyph unicode="&#x1EF3;" horiz-adv-x="549" d="M335 -21Q310 -83 290 -123T247 -186T192 -217T115 -226Q101 -226 83 -225T40 -221V-104Q47 -106 60 -106T96 -107Q116 -107 128 -104T150 -94T165 -76T179 -47L203 11L7 502H160L274 154H278L391 502H542L335 -21ZM281 565L85 683L226 741L365 610L281 565Z" />
+<glyph unicode="&#x2013;" horiz-adv-x="588" d="M44 208V315H544V208H44Z" />
+<glyph unicode="&#x2014;" horiz-adv-x="866" d="M44 210V313H822V210H44Z" />
+<glyph unicode="&#x2018;" horiz-adv-x="272" d="M172 455H14L125 700H225L172 455Z" />
+<glyph unicode="&#x2019;" horiz-adv-x="272" d="M147 455H47L100 700H258L147 455Z" />
+<glyph unicode="&#x201A;" horiz-adv-x="272" d="M147 -178H47L100 67H258L147 -178Z" />
+<glyph unicode="&#x201C;" horiz-adv-x="516" d="M416 455H258L369 700H469L416 455ZM172 455H14L125 700H225L172 455Z" />
+<glyph unicode="&#x201D;" horiz-adv-x="516" d="M390 455H290L343 700H502L390 455ZM147 455H47L100 700H258L147 455Z" />
+<glyph unicode="&#x201E;" horiz-adv-x="516" d="M390 -179H290L343 67H502L390 -179ZM147 -179H47L100 67H258L147 -179Z" />
+<glyph unicode="&#x2020;" horiz-adv-x="385" d="M262 449V188H124V449H32V561H124V692H262V561H353V449H262Z" />
+<glyph unicode="&#x2021;" horiz-adv-x="385" d="M261 250V120H124V250H33V362H124V461H32V574H124V692H261V574H353V461H261V362H353V250H261Z" />
+<glyph unicode="&#x2022;" horiz-adv-x="517" d="M425 329Q425 294 412 264T377 212T324 176T258 163Q223 163 193 176T141 211T105 264T92 329Q92 363 105 393T140 446T193 482T258 495Q293 495 323 482T376 447T412 394T425 329Z" />
+<glyph unicode="&#x2026;" horiz-adv-x="837" d="M216 56Q216 27 192 8T132 -11Q95 -11 71 8T47 56Q47 84 71 102T131 121Q168 121 192 103T216 56ZM503 56Q503 27 479 8T419 -11Q382 -11 358 8T333 56Q333 84 358 102T418 121Q454 121 478 103T503 56ZM790 56Q790 27 766 8T706 -11Q670 -11 646 8T621 56Q621 84 645 102T705 121Q742 121 766 103T790 56Z" />
+<glyph unicode="&#x2030;" horiz-adv-x="1058" d="M301 547Q301 516 295 489T273 440T233 407T170 395Q132 395 107 407T67 439T46 487T40 547Q40 579 46 607T67 655T107 688T170 700Q208 700 233 688T273 655T294 606T301 547ZM221 549Q221 585 212 610T170 636Q137 636 129 611T120 548Q120 506 129 482T170 458Q202 458 211 482T221 549ZM203 0H126L588 700H664L203 0ZM686 147Q686 115 680 88T658 40T618 7T555 -5Q517 -5 492 7T452 39T431 87T425 147Q425 179 431 207T452 255T492 288T555 300Q592 300 617 288T658 255T679 206T686 147ZM606 148Q606 185 597 210T555 236Q522 236 513 211T504 148Q504 105 514 81T555 57Q587 57 596 81T606 148ZM1018 147Q1018 115 1012 88T990 40T950 7T887 -5Q849 -5 824 7T784 39T763 87T756 147Q756 179 762 207T784 255T824 288T887 300Q924 300 949 288T990 255T1011 206T1018 147ZM937 148Q937 185 929 210T887 236Q854 236 845 211T836 148Q836 105 845 81T887 57Q918 57 927 81T937 148Z" />
+<glyph unicode="&#x2039;" horiz-adv-x="270" d="M139 97L14 300L139 502L234 455L133 299L234 144L139 97Z" />
+<glyph unicode="&#x203A;" horiz-adv-x="270" d="M131 97L36 144L137 299L36 455L131 502L256 300L131 97Z" />
+<glyph unicode="&#x2044;" horiz-adv-x="131" d="M-156 0H-232L288 700H364L-156 0Z" />
+<glyph unicode="&#x2070;" horiz-adv-x="368" d="M329 530Q329 495 322 465T299 411T254 375T184 361Q142 361 114 374T70 411T46 464T39 530Q39 565 46 596T69 650T114 686T184 700Q226 700 254 687T298 650T322 596T329 530ZM240 532Q240 552 238 570T230 601T213 621T184 629Q166 629 155 622T137 601T129 570T127 531Q127 483 138 457T184 431Q219 431 229 458T240 532Z" />
+<glyph unicode="&#x2074;" horiz-adv-x="347" d="M264 429V367H174V429H38L18 493L175 694H264V501H315V429H264ZM97 494H178V611L97 494Z" />
+<glyph unicode="&#x2075;" horiz-adv-x="329" d="M295 480Q295 456 287 435T262 397T217 371T152 361Q116 361 88 369T30 398L72 458Q90 443 110 435T147 427Q178 427 192 441T207 479Q207 502 195 514T151 526Q133 526 119 521T94 503L33 541L63 694H276V622H126L115 565H116Q132 575 147 579T184 584Q238 584 266 559T295 480Z" />
+<glyph unicode="&#x2076;" horiz-adv-x="341" d="M307 479Q307 426 272 394T177 361Q109 361 74 401T39 522Q39 557 46 589T70 646T117 685T191 700Q223 700 246 694T295 670L258 612Q244 622 229 628T192 634Q157 634 144 612T129 557H130Q141 568 157 574T196 581Q246 581 276 558T307 479ZM224 476Q224 497 212 510T176 523Q154 523 142 510T129 476Q129 456 140 443T177 429Q200 429 212 442T224 476Z" />
+<glyph unicode="&#x2077;" horiz-adv-x="307" d="M284 627Q248 597 227 573T195 523T181 465T178 386V367H83V385Q83 409 86 440T100 503T128 566T174 621V623H15V694H269L284 627H284Z" />
+<glyph unicode="&#x2078;" horiz-adv-x="337" d="M303 464Q303 444 298 426T278 393T237 370T169 361Q127 361 101 369T60 392T40 425T34 464Q34 492 48 510T88 539V541Q64 552 54 569T43 610Q43 699 168 699Q233 699 263 678T294 610Q294 561 249 541V539Q274 528 288 510T303 464ZM211 602Q211 618 201 629T168 640Q147 640 137 629T127 602Q127 586 137 576T168 565Q189 565 200 575T211 602ZM217 467Q217 484 206 496T168 509Q145 509 133 498T120 467Q120 449 131 436T168 422Q192 422 204 435T217 467Z" />
+<glyph unicode="&#x2079;" horiz-adv-x="340" d="M301 534Q301 498 294 467T269 411T222 374T150 360Q118 360 95 367T44 391L82 448Q96 438 111 432T150 426Q186 426 199 448T213 505H211Q201 494 184 487T145 479Q95 479 65 502T34 582Q34 635 69 667T164 699Q231 699 266 659T301 534ZM212 586Q212 607 201 620T164 634Q141 634 129 621T117 587Q117 566 129 553T164 540Q186 540 199 552T212 586Z" />
+<glyph unicode="&#x2080;" horiz-adv-x="368" d="M329 48Q329 13 322 -17T299 -71T254 -107T184 -121Q142 -121 114 -108T70 -71T46 -18T39 48Q39 83 46 114T69 168T114 204T184 218Q226 218 254 205T298 168T322 114T329 48ZM240 50Q240 70 238 88T230 119T213 139T184 147Q166 147 155 140T137 119T129 88T127 49Q127 1 138 -25T184 -51Q219 -51 229 -24T240 50Z" />
+<glyph unicode="&#x2081;" horiz-adv-x="301" d="M111 -44V120H108L43 91L16 157L131 212H202V-44H279V-115H30V-44H111Z" />
+<glyph unicode="&#x2082;" horiz-adv-x="326" d="M38 -115L25 -42Q64 -14 95 7T149 44T184 76T197 108Q197 122 188 132T159 143Q134 143 116 135T78 105L28 161Q63 192 95 205T168 218Q225 218 256 192T288 119Q288 99 281 81T256 43T208 2T132 -43H292V-115H38Z" />
+<glyph unicode="&#x2083;" horiz-adv-x="319" d="M285 -19Q285 -70 253 -95T153 -121Q118 -121 85 -112T20 -78L57 -18Q82 -38 102 -45T143 -53Q171 -53 185 -42T199 -14Q199 6 187 13T155 20H102V80H136Q159 80 173 89T188 116Q188 129 179 138T149 148Q126 148 104 139T65 113L24 172Q59 198 89 208T159 218Q213 218 244 195T276 120Q276 99 264 83T235 58V57Q259 50 272 30T285 -19Z" />
+<glyph unicode="&#x2084;" horiz-adv-x="347" d="M264 -53V-115H174V-53H38L18 11L175 212H264V19H315V-53H264ZM97 12H178V129L97 12Z" />
+<glyph unicode="&#x2085;" horiz-adv-x="329" d="M295 -2Q295 -26 287 -47T262 -85T217 -111T152 -121Q116 -121 88 -113T30 -84L72 -24Q90 -39 110 -47T147 -55Q178 -55 192 -41T207 -3Q207 20 195 32T151 44Q133 44 119 39T94 21L33 59L63 212H276V140H126L115 83H116Q132 93 147 97T184 102Q238 102 266 77T295 -2Z" />
+<glyph unicode="&#x2086;" horiz-adv-x="341" d="M307 -3Q307 -56 272 -88T177 -121Q109 -121 74 -81T39 40Q39 75 46 107T70 164T117 203T191 218Q223 218 246 212T295 188L258 130Q244 140 229 146T192 152Q157 152 144 130T129 75H130Q141 86 157 92T196 99Q246 99 276 76T307 -3ZM224 -6Q224 15 212 28T176 41Q154 41 142 28T129 -6Q129 -26 140 -39T177 -53Q200 -53 212 -40T224 -6Z" />
+<glyph unicode="&#x2087;" horiz-adv-x="307" d="M284 145Q248 115 227 91T195 41T181 -17T178 -96V-115H83V-97Q83 -73 86 -42T100 21T128 84T174 139V141H15V212H269L284 145H284Z" />
+<glyph unicode="&#x2088;" horiz-adv-x="337" d="M303 -18Q303 -38 298 -56T278 -89T237 -112T169 -121Q127 -121 101 -113T60 -90T40 -57T34 -18Q34 10 48 28T88 57V59Q64 70 54 87T43 128Q43 217 168 217Q233 217 263 196T294 128Q294 79 249 59V57Q274 46 288 28T303 -18ZM211 120Q211 136 201 147T168 158Q147 158 137 147T127 120Q127 104 137 94T168 83Q189 83 200 93T211 120ZM217 -15Q217 2 206 14T168 27Q145 27 133 16T120 -15Q120 -33 131 -46T168 -60Q192 -60 204 -47T217 -15Z" />
+<glyph unicode="&#x2089;" horiz-adv-x="340" d="M301 53Q301 17 294 -15T269 -70T222 -107T150 -121Q118 -121 95 -114T44 -90L82 -33Q96 -43 111 -49T150 -55Q186 -55 199 -33T213 24H211Q201 13 184 6T145 -2Q95 -2 65 21T34 101Q34 154 69 186T164 218Q231 218 266 178T301 53ZM212 105Q212 125 201 139T164 153Q141 153 129 140T117 106Q117 85 129 72T164 59Q186 59 199 71T212 105Z" />
+<glyph unicode="&#x20AC;" horiz-adv-x="613" d="M94 443Q102 498 119 541T166 615T240 662T349 678Q386 678 417 672T475 653T529 621T583 575L503 489Q468 530 434 545T357 561Q294 561 268 533T232 443H460V367H228V337Q228 326 228 317T229 297H460V220H234Q238 197 244 177T264 140T300 114T358 104Q406 104 444 125T519 190L594 98Q540 42 482 15T353 -13Q285 -13 240 3T166 49T121 123T94 220H15L16 297H86Q85 307 85 317T85 338Q85 346 85 352T86 367H15L16 443H94Z" />
+<glyph unicode="&#x2122;" horiz-adv-x="708" d="M571 437L572 577L492 467L409 577L410 437H341V700H402L492 574L579 700H641V437H571ZM201 640V437H129V640H42V700H287V640H201Z" />
+<glyph unicode="&#x2153;" horiz-adv-x="771" d="M146 438V602H143L78 573L51 639L166 694H237V438H314V367H65V438H146ZM182 0H106L626 700H702L182 0ZM740 96Q740 45 708 20T608 -6Q573 -6 540 3T475 37L512 97Q537 77 557 70T598 62Q626 62 640 73T654 101Q654 121 642 128T610 135H557V195H591Q614 195 628 204T643 231Q643 244 634 253T604 263Q581 263 559 254T520 228L479 287Q514 313 544 323T614 333Q668 333 699 310T731 235Q731 214 719 198T690 173V172Q714 165 727 145T740 96Z" />
+<glyph unicode="&#x2154;" horiz-adv-x="771" d="M57 366L44 439Q83 467 114 488T168 526T203 558T216 590Q216 604 207 614T178 625Q153 625 135 617T97 587L47 643Q82 674 114 687T187 700Q244 700 275 674T307 601Q307 581 300 562T275 524T227 483T151 438H311V366H57ZM182 0H106L626 700H702L182 0ZM740 96Q740 45 708 20T608 -6Q573 -6 540 3T475 37L512 97Q537 77 557 70T598 62Q626 62 640 73T654 101Q654 121 642 128T610 135H557V195H591Q614 195 628 204T643 231Q643 244 634 253T604 263Q581 263 559 254T520 228L479 287Q514 313 544 323T614 333Q668 333 699 310T731 235Q731 214 719 198T690 173V172Q714 165 727 145T740 96Z" />
+<glyph unicode="&#x215B;" horiz-adv-x="771" d="M146 438V602H143L78 573L51 639L166 694H237V438H314V367H65V438H146ZM182 0H106L626 700H702L182 0ZM746 97Q746 77 741 59T721 26T680 3T612 -6Q570 -6 544 2T503 25T483 58T477 97Q477 125 491 143T531 172V174Q507 185 497 202T486 243Q486 332 611 332Q676 332 706 311T737 243Q737 194 692 174V172Q717 161 731 143T746 97ZM654 235Q654 251 644 262T611 273Q590 273 580 262T570 235Q570 219 580 209T611 198Q632 198 643 208T654 235ZM660 100Q660 117 649 129T611 142Q588 142 576 131T563 100Q563 82 574 69T611 55Q635 55 647 68T660 100Z" />
+<glyph unicode="&#x215C;" horiz-adv-x="771" d="M315 463Q315 412 283 387T183 361Q148 361 115 370T50 404L87 464Q112 444 132 437T173 429Q201 429 215 440T229 468Q229 488 217 495T185 502H132V562H166Q189 562 203 571T218 598Q218 611 209 620T179 630Q156 630 134 621T95 595L54 654Q89 680 119 690T189 700Q243 700 274 677T306 602Q306 581 294 565T265 540V539Q289 532 302 512T315 463ZM182 0H106L626 700H702L182 0ZM746 97Q746 77 741 59T721 26T680 3T612 -6Q570 -6 544 2T503 25T483 58T477 97Q477 125 491 143T531 172V174Q507 185 497 202T486 243Q486 332 611 332Q676 332 706 311T737 243Q737 194 692 174V172Q717 161 731 143T746 97ZM654 235Q654 251 644 262T611 273Q590 273 580 262T570 235Q570 219 580 209T611 198Q632 198 643 208T654 235ZM660 100Q660 117 649 129T611 142Q588 142 576 131T563 100Q563 82 574 69T611 55Q635 55 647 68T660 100Z" />
+<glyph unicode="&#x215D;" horiz-adv-x="771" d="M325 480Q325 456 317 435T292 397T247 371T182 361Q146 361 118 369T60 398L102 458Q120 443 140 435T177 427Q208 427 222 441T237 479Q237 502 225 514T181 526Q163 526 149 521T124 503L63 541L93 694H306V622H156L145 565H146Q162 575 177 579T214 584Q268 584 296 559T325 480ZM182 0H106L626 700H702L182 0ZM746 97Q746 77 741 59T721 26T680 3T612 -6Q570 -6 544 2T503 25T483 58T477 97Q477 125 491 143T531 172V174Q507 185 497 202T486 243Q486 332 611 332Q676 332 706 311T737 243Q737 194 692 174V172Q717 161 731 143T746 97ZM654 235Q654 251 644 262T611 273Q590 273 580 262T570 235Q570 219 580 209T611 198Q632 198 643 208T654 235ZM660 100Q660 117 649 129T611 142Q588 142 576 131T563 100Q563 82 574 69T611 55Q635 55 647 68T660 100Z" />
+<glyph unicode="&#x215E;" horiz-adv-x="771" d="M349 627Q313 597 292 573T260 523T246 465T243 386V367H148V385Q148 409 151 440T165 503T193 566T239 621V623H80V694H334L349 627ZM182 0H106L626 700H702L182 0ZM746 97Q746 77 741 59T721 26T680 3T612 -6Q570 -6 544 2T503 25T483 58T477 97Q477 125 491 143T531 172V174Q507 185 497 202T486 243Q486 332 611 332Q676 332 706 311T737 243Q737 194 692 174V172Q717 161 731 143T746 97ZM654 235Q654 251 644 262T611 273Q590 273 580 262T570 235Q570 219 580 209T611 198Q632 198 643 208T654 235ZM660 100Q660 117 649 129T611 142Q588 142 576 131T563 100Q563 82 574 69T611 55Q635 55 647 68T660 100Z" />
+<glyph unicode="&#x2212;" horiz-adv-x="613" d="M45 211V329H568V211H45Z" />
+<glyph unicode="&#x2260;" horiz-adv-x="613" d="M421 333L332 218H568V101H249L160 -17L71 46L114 101H45V218H198L286 333H45V450H370L464 575L553 513L504 450H568V333H421Z" />
+<glyph unicode="&#x2264;" horiz-adv-x="613" d="M45 314V434L568 616V490L192 375L568 266V143L45 314ZM45 0V112H568V0H45Z" />
+<glyph unicode="&#x2265;" horiz-adv-x="613" d="M45 143V266L421 375L45 490V616L568 434V314L45 143ZM45 0V112H568V0H45Z" />
+<glyph unicode="&#x24DF;" horiz-adv-x="795" d="M743 334Q743 262 716 200T642 90T533 16T398 -11Q326 -11 263 16T153 90T79 199T52 334Q52 406 79 468T153 578T263 652T398 679Q470 679 532 652T642 578T716 469T743 334ZM698 335Q698 397 675 451T611 547T516 611T398 635Q336 635 282 612T187 547T123 452T99 335Q99 273 122 219T187 125T282 61T398 37Q460 37 514 60T610 124T674 219T698 335ZM598 403Q598 341 561 303T437 264H362V119H256V531Q285 534 325 536T410 539Q500 539 549 507T598 403ZM494 399Q494 431 477 445T414 460Q401 460 390 460T362 458V337H413Q455 337 474 349T494 399Z" />
+<glyph unicode="&#xF6C3;" horiz-adv-x="348" d="M187 -226H91L124 -55H273L187 -226Z" />
+
+
+</font>
+</defs>
+<text x="40" y="40" font-family="ClearviewATT Bd" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="ClearviewATT Bd" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="ClearviewATT Bd" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="ClearviewATT Bd" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.ttf b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.ttf
new file mode 100644
index 0000000000..7bfb9d5cc2
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.woff b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.woff
new file mode 100644
index 0000000000..b1f00eeac9
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bd.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.eot b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.eot
new file mode 100644
index 0000000000..22646213e6
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.svg b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.svg
new file mode 100644
index 0000000000..0996bb5eec
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.svg
@@ -0,0 +1,425 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[ClearviewATT BdIt]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Copyright (c) Terminal Design, Inc, 2005. All rights reserved.]]></copyright>
+<trademark><![CDATA[Clearview is a trademark of Terminal Design, Inc.]]></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="545" id="ClearviewATT-BdIt">
+<font-face font-family="ClearviewATT BdIt" panose-1="2 11 5 6 3 5 0 2 0 4" ascent="750" descent="-250" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="484" d="M585 667L492 0H-8L85 667H585ZM116 606L40 65L264 336L116 606ZM462 67L538 605L315 336L462 67ZM65 35H428L284 299L65 35ZM294 373L507 630H153L294 373Z" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " horiz-adv-x="300" />
+<glyph unicode="!" horiz-adv-x="299" d="M321 667Q299 547 277 429T234 191H118Q132 310 143 428T166 667H321ZM251 70Q251 50 242 35T217 10T184 -6T148 -12Q133 -12 118 -9T91 3T72 23T64 52Q64 71 74 86T99 111T132 127T167 132Q181 132 196 129T223 118T243 98T251 70Z" />
+<glyph unicode="&quot;" horiz-adv-x="482" d="M467 448H374L386 700H533L467 448ZM227 448H133L145 700H292L227 448Z" />
+<glyph unicode="#" horiz-adv-x="701" d="M697 456H596L560 335H668L639 231H529L497 113H384L419 231H299L264 113H157L191 231H81L109 335H220L253 456H142L170 558H282L313 667H422L391 558H517L548 667H657L625 558H727L697 456ZM361 456L328 335H452L486 456H361Z" />
+<glyph unicode="$" horiz-adv-x="613" d="M582 234Q582 190 566 155T521 95T456 55T377 35L366 -46H234L246 36Q198 39 156 57T75 101L147 198Q187 169 230 152T324 134Q342 134 363 136T403 147T434 170T447 209Q447 234 425 250T368 279T294 304T220 336T164 384T141 458Q141 500 156 533T197 589T257 627T332 645L343 722H475L464 644Q502 641 537 629T607 600L541 496Q506 518 467 529T385 541Q370 541 352 539T317 531T290 512T279 479Q279 458 301 444T357 418T430 393T503 360T559 309T582 234Z" />
+<glyph unicode="%" horiz-adv-x="794" d="M407 574Q407 532 398 494T368 426T315 379T235 361Q111 361 111 486Q111 528 120 566T149 635T203 682T283 700Q346 700 376 668T407 574ZM245 431Q269 431 283 446T306 484T316 531T319 574Q319 598 309 613T273 629Q249 629 234 614T211 577T201 531T198 488Q198 463 207 447T245 431ZM203 0H126L686 700H762L203 0ZM779 207Q779 166 770 128T740 60T687 12T608 -6Q483 -6 483 120Q483 162 492 200T522 268T575 315T655 333Q718 333 748 301T779 207ZM617 64Q641 64 655 79T678 116T688 162T691 205Q691 229 682 245T645 262Q620 262 606 247T584 209T574 163T571 120Q571 95 580 80T617 64Z" />
+<glyph unicode="&amp;" horiz-adv-x="667" d="M303 -10Q262 -10 222 -2T151 25T100 76T81 157Q81 197 93 230T128 291T180 340T246 378Q223 403 207 435T191 501Q191 545 209 578T258 634T326 668T403 680Q434 680 463 674T516 652T553 613T567 553Q567 517 553 489T517 438T466 399T408 367L512 196Q522 222 525 256T528 329V347H651Q652 340 652 334T652 321Q652 257 638 205T582 111L650 0H501L476 37Q437 14 397 2T303 -10ZM325 95Q348 95 369 102T411 120L293 307Q261 288 243 256T225 191Q225 173 230 156T247 125T278 103T325 95ZM365 431Q380 441 395 452T423 476T443 505T451 541Q451 567 437 578T397 590Q365 590 344 571T323 519Q323 496 336 472T365 431Z" />
+<glyph unicode="&apos;" horiz-adv-x="241" d="M227 448H133L145 700H292L227 448Z" />
+<glyph unicode="(" horiz-adv-x="337" d="M158 -139Q120 -59 97 26T74 209Q74 287 88 360T130 503T197 640T286 775L383 749Q300 627 252 486T204 192Q204 106 220 32T262 -112L158 -139Z" />
+<glyph unicode=")" horiz-adv-x="337" d="M22 -112Q107 14 154 154T201 451Q201 536 186 609T143 749L246 775Q265 731 280 689T307 604T324 517T330 425Q330 271 274 131T118 -139L22 -112Z" />
+<glyph unicode="*" horiz-adv-x="375" d="M317 531L383 458L288 399L251 490L191 399L113 458L198 531L92 543L139 639L229 594L224 694H337L305 595L406 639L427 544L317 531Z" />
+<glyph unicode="+" horiz-adv-x="613" d="M397 211L368 5H247L276 211H74L91 329H293L321 532H442L414 329H614L597 211H397Z" />
+<glyph unicode="&#x2c;" horiz-adv-x="259" d="M97 -132H1L86 120H237L97 -132Z" />
+<glyph unicode="-" horiz-adv-x="383" d="M75 195L93 327H380L362 195H75Z" />
+<glyph unicode="." horiz-adv-x="259" d="M231 69Q231 49 221 34T196 9T163 -6T127 -12Q112 -12 97 -9T70 3T51 23T43 52Q43 71 53 86T78 111T111 127T147 132Q161 132 176 129T203 117T223 97T231 69Z" />
+<glyph unicode="/" horiz-adv-x="573" d="M132 -115H5L516 700H644L132 -115Z" />
+<glyph unicode="0" horiz-adv-x="613" d="M614 333Q603 255 582 192T525 83T436 12T305 -13Q243 -13 200 3T131 50T94 125T82 223Q82 249 84 276T91 333Q102 411 123 474T180 583T271 653T402 678Q464 678 506 662T574 615T611 542T623 445Q623 393 614 333ZM471 333Q476 365 479 393T482 447Q482 499 462 530T385 561Q343 561 317 545T275 498T250 426T234 333Q229 302 227 274T224 221Q224 168 244 136T321 104Q363 104 389 121T431 169T455 242T471 333Z" />
+<glyph unicode="1" horiz-adv-x="613" d="M271 119L326 511H321L181 430L140 536L374 668H486L409 119H564L548 0H96L112 119H271Z" />
+<glyph unicode="2" horiz-adv-x="613" d="M82 0L77 118Q163 180 234 228T357 319T436 401T464 484Q464 514 445 536T388 558Q342 558 298 537T206 471L145 571Q181 601 213 621T278 654T344 672T416 678Q513 678 561 633T609 510Q609 444 576 388T490 283T375 194T251 119H568L552 0H82Z" />
+<glyph unicode="3" horiz-adv-x="613" d="M585 234Q585 174 563 128T503 51T412 3T298 -13Q230 -13 171 7T66 67L142 168Q177 138 218 120T299 102Q326 102 351 108T397 128T430 165T442 221Q442 259 419 273T350 287H252L268 400H316Q346 400 372 404T418 420T449 448T461 492Q461 505 457 517T443 538T414 553T369 559Q326 559 284 541T198 489L150 593Q185 616 215 632T275 659T335 673T402 678Q447 678 484 668T548 638T590 588T605 517Q605 465 583 422T510 352V348Q585 325 585 234Z" />
+<glyph unicode="4" horiz-adv-x="613" d="M498 133L480 0H342L360 133H91L73 239L428 666H573L515 252H607L590 133H498ZM412 503L223 252H381L416 503H412Z" />
+<glyph unicode="5" horiz-adv-x="613" d="M588 283Q588 216 569 162T511 68T416 8T288 -13Q222 -13 168 2T68 55L140 162Q173 135 215 119T302 102Q373 102 410 142T448 256Q448 348 337 348Q297 348 266 333T205 294L112 362L211 667H607L590 547H304L255 417L259 415Q291 438 328 449T404 460Q440 460 473 451T531 420T572 365T588 283Z" />
+<glyph unicode="6" horiz-adv-x="613" d="M598 266Q598 203 576 152T516 64T425 7T311 -13Q194 -13 140 50T85 227Q85 270 91 312Q101 388 123 454T185 570T284 649T428 678Q480 678 527 666T617 624L539 519Q510 540 482 549T418 558Q375 558 344 546T292 512T257 457T238 385H242Q272 419 312 434T398 449Q441 449 478 438T541 403T583 346T598 266ZM327 104Q360 104 384 115T424 145T448 191T456 249Q456 296 430 314T360 332Q296 332 260 296T224 195Q224 147 253 126T327 104Z" />
+<glyph unicode="7" horiz-adv-x="613" d="M631 558Q556 504 509 455T431 348T384 220T354 50L347 0H200L204 33Q212 92 227 162T270 302T341 435T447 544L448 548H136L152 667H623L631 558H631Z" />
+<glyph unicode="8" horiz-adv-x="613" d="M592 232Q592 167 570 121T509 45T418 1T306 -13Q262 -13 220 -6T146 21T95 73T75 157Q75 230 104 278T203 353L204 357Q170 375 153 401T135 466Q135 526 157 566T215 631T300 667T401 678Q439 678 477 673T546 651T596 605T615 530Q615 467 590 426T508 357L507 353Q549 336 570 307T592 232ZM362 401Q411 401 443 424T476 500Q476 520 468 532T448 552T419 561T385 564Q334 564 303 543T272 467Q272 447 279 434T299 414T328 404T362 401ZM319 98Q384 98 418 126T452 222Q452 246 443 261T420 284T386 294T347 297Q284 297 248 270T211 175Q211 150 220 135T244 112T278 101T319 98Z" />
+<glyph unicode="9" horiz-adv-x="613" d="M619 437Q619 414 617 391T612 345Q601 271 580 206T520 93T425 17T284 -11Q228 -11 179 4T89 58L173 150Q203 124 228 113T293 102Q338 102 368 116T418 154T449 213T467 287H463Q430 252 393 235T307 218Q264 218 227 229T163 264T121 321T106 401Q106 463 128 514T189 601T280 658T394 678Q512 678 565 615T619 437ZM347 336Q414 336 445 373T477 475Q477 523 451 542T379 561Q317 561 283 522T249 423Q249 377 276 357T347 336Z" />
+<glyph unicode=":" horiz-adv-x="259" d="M277 415Q277 396 268 381T244 356T211 340T176 334Q162 334 148 338T122 350T103 370T96 398Q96 417 105 432T129 457T162 473T197 478Q211 478 225 475T251 463T270 443T277 415ZM229 70Q229 50 220 35T196 10T163 -6T128 -12Q114 -12 100 -8T74 4T55 24T47 52Q47 71 56 86T80 111T113 126T148 132Q162 132 176 129T202 118T221 98T229 70Z" />
+<glyph unicode=";" horiz-adv-x="259" d="M97 -132H1L85 120H236L97 -132ZM286 415Q286 396 277 381T253 356T221 340T186 334Q172 334 158 338T132 350T113 370T106 398Q106 417 115 432T139 457T172 473T207 478Q221 478 235 475T260 463T279 443T286 415Z" />
+<glyph unicode="&lt;" horiz-adv-x="613" d="M74 210L91 329L644 544L626 419L221 271L585 127L568 5L74 210Z" />
+<glyph unicode="=" horiz-adv-x="613" d="M91 333L108 450H631L614 333H91ZM59 101L75 218H598L582 101H59Z" />
+<glyph unicode="&gt;" horiz-adv-x="613" d="M45 5L62 127L468 271L103 419L121 544L614 329L597 210L45 5Z" />
+<glyph unicode="?" d="M567 519Q567 470 553 436T514 376T454 332T377 295Q365 290 358 286T346 275T340 260T335 235L329 195H193L203 266Q206 289 211 304T226 331T249 351T284 369Q306 379 331 388T377 412T413 445T428 494Q428 513 420 526T397 547T366 558T333 561Q246 561 175 504L122 605Q153 624 181 638T239 661T301 674T369 679Q409 679 445 671T508 643T551 594T567 519ZM332 69Q332 49 322 34T297 9T264 -6T229 -12Q215 -12 200 -9T173 3T154 22T146 51Q146 70 155 85T180 110T213 125T248 131Q262 131 277 128T304 117T324 97T332 69Z" />
+<glyph unicode="@" horiz-adv-x="823" d="M811 346Q811 297 797 245T754 149T682 79T583 51Q554 51 531 66T507 114H503Q479 85 444 70T371 54Q316 54 289 87T262 173Q262 216 276 260T316 340T381 397T467 420Q493 420 512 412T547 380H548L556 410H660Q658 403 652 381T638 330T620 267T603 205T590 157T585 135Q585 123 595 118T615 113Q650 113 676 138T719 200T744 276T753 342Q753 393 734 428T682 486T607 518T520 528Q439 528 372 504T256 433T180 321T153 173Q153 114 171 72T223 1T303 -40T405 -53Q463 -53 519 -38T624 9L650 -38Q589 -77 524 -92T386 -108Q321 -108 266 -90T170 -37T108 51T85 172Q85 266 119 342T213 473T353 556T524 586Q581 586 633 573T724 530T787 455T811 346ZM417 132Q455 132 474 154T503 212Q509 233 516 257T524 304Q524 327 509 334T474 341Q444 341 424 326T390 287T371 236T365 183Q365 132 417 132Z" />
+<glyph unicode="A" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525Z" />
+<glyph unicode="B" horiz-adv-x="666" d="M643 217Q643 160 624 119T571 51T488 13T379 0H73L166 662Q226 669 289 672T411 675Q457 675 502 670T582 648T640 603T662 526Q662 454 629 414T531 353V349Q585 338 614 306T643 217ZM386 392Q412 392 435 396T477 411T505 443T516 498Q516 532 491 547T399 563Q388 563 374 563T345 561T318 560T297 558L274 394Q297 392 323 392T386 392ZM348 104Q376 104 402 107T450 122T483 155T496 215Q496 243 484 258T453 281T412 290T369 292Q341 292 311 292T258 290L232 106Q260 104 287 104T348 104Z" />
+<glyph unicode="C" horiz-adv-x="665" d="M653 96Q593 46 527 18T383 -11Q249 -11 173 56T97 261Q97 347 122 423T195 555T314 645T476 678Q551 678 609 651T715 567L613 474Q584 512 549 533T465 555Q405 555 364 531T298 467T261 377T250 274Q250 198 285 154T401 110Q455 110 501 134T588 193L653 96H653Z" />
+<glyph unicode="D" horiz-adv-x="704" d="M700 408Q700 314 674 239T596 110T471 29T303 0H73L166 662Q224 667 282 671T399 675Q465 675 520 660T616 613T678 530T700 408ZM307 116Q365 116 406 130T475 172T519 242T543 340Q545 356 546 371T548 402Q548 561 387 561Q364 561 342 560T297 557L235 117Q253 116 271 116T307 116Z" />
+<glyph unicode="E" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73Z" />
+<glyph unicode="F" horiz-adv-x="555" d="M295 545L274 398H545L528 275H257L219 0H73L166 667H612L595 545H295Z" />
+<glyph unicode="G" horiz-adv-x="732" d="M402 100Q483 100 525 139T575 258H417L433 375H728L718 303Q707 228 683 170T618 72T519 11T381 -10Q316 -10 264 8T175 60T117 146T97 263Q97 348 123 424T198 556T317 645T477 678Q548 678 605 654T709 575L608 482Q580 514 546 534T466 555Q404 555 362 530T295 465T260 372T249 266Q249 188 284 144T402 100Z" />
+<glyph unicode="H" horiz-adv-x="694" d="M475 0L514 283H258L219 0H73L166 667H312L278 420H534L568 667H714L621 0H475Z" />
+<glyph unicode="I" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73Z" />
+<glyph unicode="J" horiz-adv-x="490" d="M449 228Q441 176 428 132T388 56T320 5T216 -13Q159 -13 118 2T38 53L118 160Q144 131 166 121T219 111Q251 111 271 130T298 197L364 667H510L449 228H449Z" />
+<glyph unicode="K" horiz-adv-x="644" d="M451 0L309 279L250 223L219 0H73L166 667H312L273 389H277L521 667H705L432 376L637 0H451Z" />
+<glyph unicode="L" horiz-adv-x="543" d="M73 0L166 667H312L236 123H519L502 0H73Z" />
+<glyph unicode="M" horiz-adv-x="799" d="M585 0L645 427H641L422 169L277 425H273L214 0H73L166 667H293L452 383L691 667H819L726 0H585Z" />
+<glyph unicode="N" horiz-adv-x="731" d="M532 0L278 425H274L215 0H73L166 667H301L546 249H551L610 667H751L658 0H532Z" />
+<glyph unicode="O" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108Z" />
+<glyph unicode="P" horiz-adv-x="634" d="M659 500Q659 376 590 307T393 237H252L219 0H73L166 662Q187 664 215 666T274 671T339 674T405 675Q456 675 502 667T583 639T638 585T659 500ZM378 351Q410 351 435 357T478 379T505 418T515 480Q515 524 486 541T390 558Q369 558 347 557T296 554L268 351H378Z" />
+<glyph unicode="Q" horiz-adv-x="772" d="M768 411Q768 360 759 310T731 213T681 127T607 57V53Q626 48 637 40T660 24T687 8T729 -2L667 -103Q628 -102 596 -87T537 -55T487 -24T439 -9Q431 -9 419 -10T395 -12Q327 -12 272 3T178 52T118 136T96 259Q96 349 121 426T194 559T315 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T554 172T590 244T610 334Q616 373 616 411Q616 489 579 524T464 559Q408 559 371 543T310 496T274 425T254 334Q252 317 250 299T248 263Q248 185 284 147T401 108Z" />
+<glyph unicode="R" horiz-adv-x="645" d="M458 0L396 228Q391 243 385 248T363 253H254L219 0H73L166 663Q281 675 398 675Q446 675 492 669T575 644T634 592T657 507Q657 472 649 438T623 376T578 325T515 292V288Q523 284 527 274T535 255L611 0H458ZM383 367Q410 367 433 372T474 391T502 427T512 482Q512 507 502 523T474 547T435 558T388 561Q364 561 342 560T297 556L270 367H383Z" />
+<glyph unicode="S" horiz-adv-x="601" d="M582 216Q582 153 560 110T499 41T411 2T305 -10Q230 -10 167 8T42 71L121 175Q163 144 209 124T309 104Q330 104 352 106T393 117T424 141T436 181Q436 210 413 228T354 260T278 290T201 327T143 381T119 465Q119 524 142 564T203 629T289 665T390 676Q510 676 616 617L548 505Q513 528 472 543T387 559Q368 559 347 557T309 547T280 527T268 492Q268 464 291 447T349 417T425 391T500 356T558 302T582 216Z" />
+<glyph unicode="T" horiz-adv-x="549" d="M423 543L347 0H201L277 543H90L107 667H628L611 543H423Z" />
+<glyph unicode="U" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665Z" />
+<glyph unicode="V" horiz-adv-x="636" d="M393 0H243L108 667H262L341 170H345L562 667H714L393 0Z" />
+<glyph unicode="W" horiz-adv-x="1005" d="M788 0H632L569 473H564L367 0H204L114 667H275L315 177H319L524 667H672L732 177H736L922 667H1077L788 0Z" />
+<glyph unicode="X" horiz-adv-x="649" d="M469 0L356 226H352L180 0H10L288 347L129 667H301L391 457H395L549 667H719L463 338L639 0H469Z" />
+<glyph unicode="Y" horiz-adv-x="646" d="M433 262L397 0H251L287 259L95 667H263L379 390L576 667H737L433 262Z" />
+<glyph unicode="Z" horiz-adv-x="607" d="M43 0L58 107L443 543H135L152 667H641L626 560L236 119H580L564 0H43Z" />
+<glyph unicode="[" horiz-adv-x="370" d="M53 -125L181 783H421L409 702H292L187 -45H304L293 -125H53Z" />
+<glyph unicode="\" horiz-adv-x="573" d="M392 -115L121 667H249L519 -115H392Z" />
+<glyph unicode="]" horiz-adv-x="370" d="M41 -125L52 -45H169L274 702H157L169 783H409L281 -125H41Z" />
+<glyph unicode="^" horiz-adv-x="433" d="M439 522L297 601L130 522L79 594L310 705L512 594L439 522Z" />
+<glyph unicode="_" horiz-adv-x="484" d="M-32 -171L-19 -84H480L467 -171H-32Z" />
+<glyph unicode="`" horiz-adv-x="348" d="M297 565L117 683L267 741L387 610L297 565Z" />
+<glyph unicode="a" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316Z" />
+<glyph unicode="b" horiz-adv-x="590" d="M588 316Q588 261 574 203T528 98T450 20T336 -11Q286 -11 250 8T202 75H198L167 0H63L161 700H302L266 447H270Q296 486 332 499T410 513Q458 513 491 498T546 456T578 393T588 316ZM310 92Q350 92 376 113T418 167T439 237T445 310Q445 330 441 347T427 377T400 397T356 405Q311 405 282 385T246 316L230 206Q228 198 228 190T228 174Q228 136 245 114T310 92Z" />
+<glyph unicode="c" horiz-adv-x="519" d="M486 350Q462 379 433 391T366 404Q326 404 298 387T251 343T225 282T217 213Q217 160 242 129T323 97Q367 97 401 109T471 147L503 54Q455 23 406 6T299 -12Q249 -12 207 1T135 39T89 104T72 196Q72 264 93 322T155 422T251 489T377 513Q428 513 475 494T559 441L486 350Z" />
+<glyph unicode="d" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q363 513 394 501T445 455H449L484 700H625L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316Z" />
+<glyph unicode="e" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445Z" />
+<glyph unicode="f" horiz-adv-x="365" d="M440 598Q430 600 416 601T385 602Q364 602 351 600T329 591T317 573T311 546L305 502H416L400 391H294L240 0H97L151 389H74L90 502H167L176 563Q181 601 192 628T226 672T285 697T375 705Q393 705 413 704T454 700L440 598H440Z" />
+<glyph unicode="g" horiz-adv-x="590" d="M525 -13Q515 -84 493 -126T439 -190T367 -219T279 -226Q205 -226 152 -213T50 -170L105 -66Q145 -92 185 -105T276 -119Q326 -119 350 -101T382 -29L396 71H392Q376 51 361 38T330 18T296 7T255 3Q207 3 173 18T116 59T84 121T73 200Q73 252 87 307T131 409T209 484T324 513Q372 513 403 500T455 444H459L475 502H597L525 -13ZM431 312Q438 361 416 383T346 405Q308 405 284 388T245 343T224 282T218 215Q218 193 222 174T236 141T262 118T305 109Q334 109 354 117T387 141T408 177T419 225L431 312Z" />
+<glyph unicode="h" horiz-adv-x="593" d="M389 0L428 281Q430 294 431 305T432 327Q432 359 417 377T358 395Q259 395 246 300L204 0H63L161 700H302L267 453H271Q300 487 337 500T419 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389Z" />
+<glyph unicode="i" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM307 643Q307 623 299 609T278 584T247 569T212 564Q183 564 160 578T136 624Q136 643 144 658T165 683T196 698T231 703Q259 703 283 689T307 643Z" />
+<glyph unicode="j" horiz-adv-x="274" d="M313 644Q313 624 305 609T284 584T254 569T218 564Q204 564 191 567T166 578T149 596T142 623Q142 643 150 658T171 683T202 698T237 703Q265 703 289 690T313 644ZM208 -17Q194 -120 137 -173T-26 -226Q-47 -226 -67 -225T-109 -221L-92 -103Q-59 -106 -26 -106Q3 -106 20 -99T48 -79T62 -46T70 0L140 502H281L208 -17Z" />
+<glyph unicode="k" horiz-adv-x="555" d="M380 0L282 214L226 162L204 0H63L161 700H302L248 314H252L433 502H608L396 301L551 0H380Z" />
+<glyph unicode="l" horiz-adv-x="316" d="M288 0Q249 -6 185 -6Q125 -6 100 12T75 75Q75 89 78 109L161 700H302L223 142Q220 119 227 114T254 109H303L288 0H288Z" />
+<glyph unicode="m" horiz-adv-x="876" d="M672 0L712 287Q715 308 715 326Q715 358 702 376T651 395Q616 395 587 377T551 313L508 0H367L407 288Q410 310 410 329Q410 361 397 378T345 395Q328 395 312 390T282 374T259 348T247 312L204 0H63L133 502H257L259 441H263Q280 468 313 490T402 513Q450 513 481 496T527 439Q562 481 601 497T691 513Q777 513 818 473T859 354Q859 342 858 329T855 302L813 0H672Z" />
+<glyph unicode="n" horiz-adv-x="593" d="M389 0L428 281Q430 293 431 303T432 324Q432 356 417 375T357 395Q259 395 246 300L204 0H63L133 502H254L256 442H260Q293 482 330 497T418 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389Z" />
+<glyph unicode="o" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403Z" />
+<glyph unicode="p" horiz-adv-x="590" d="M588 318Q588 263 574 205T530 98T450 20T329 -11Q282 -11 256 4T215 54H211L173 -220H32L133 502H250L252 442H256Q288 481 324 497T408 513Q457 513 491 498T546 457T578 395T588 318ZM309 92Q351 92 377 113T419 167T439 238T445 311Q445 331 441 348T427 378T399 398T355 405Q310 405 282 385T246 316L230 206Q228 197 228 189T228 173Q228 135 245 114T309 92Z" />
+<glyph unicode="q" horiz-adv-x="590" d="M452 -238Q408 -221 390 -196T371 -128Q371 -109 374 -88L394 54H390Q362 16 329 3T252 -11Q203 -11 169 5T114 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q368 513 404 499T458 442H462L478 502H597L517 -67Q512 -97 519 -113T552 -144L452 -238ZM432 316Q439 364 416 384T346 405Q304 405 278 385T238 335T220 268T215 197Q215 176 219 157T233 123T260 101T304 92Q360 92 384 123T416 206L432 316Z" />
+<glyph unicode="r" horiz-adv-x="404" d="M399 388Q368 388 342 385T296 372T262 341T244 287L204 0H63L133 502H253L256 435H260Q286 473 320 492T414 512H449L432 388H399Z" />
+<glyph unicode="s" horiz-adv-x="492" d="M480 167Q480 116 461 82T411 27T338 -2T252 -11Q195 -11 138 4T33 50L95 146Q133 123 174 108T261 92Q275 92 290 94T317 101T337 117T345 146Q345 164 327 174T280 193T219 211T158 237T111 279T92 347Q92 393 111 425T162 476T234 504T317 513Q365 513 414 503T505 470L454 370Q425 387 389 397T318 408Q306 408 290 407T259 402T235 388T225 362Q225 344 244 335T291 317T352 300T414 276T461 234T480 167Z" />
+<glyph unicode="t" horiz-adv-x="391" d="M352 -2Q323 -6 303 -7T264 -9Q222 -9 193 -4T146 14T120 46T112 94Q112 105 113 118T116 145L151 389H69L85 502H168L187 635H326L307 502H423L407 389H291L260 164Q257 146 257 135T266 117T291 109T342 107Q350 107 354 107T368 108L352 -2H352Z" />
+<glyph unicode="u" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398Z" />
+<glyph unicode="v" horiz-adv-x="543" d="M352 0H192L81 502H231L290 130H295L457 502H602L352 0Z" />
+<glyph unicode="w" horiz-adv-x="796" d="M627 0H493L439 298H435L303 0H166L86 502H233L263 150H267L412 502H522L571 149H575L708 502H850L627 0Z" />
+<glyph unicode="x" horiz-adv-x="542" d="M373 0L295 174H291L168 0H6L222 264L97 502H256L323 348H327L439 502H586L393 267L536 0H373Z" />
+<glyph unicode="y" horiz-adv-x="549" d="M332 -21Q299 -83 273 -123T220 -186T161 -217T83 -226Q69 -226 51 -225T8 -221L25 -104Q32 -106 44 -106T80 -107Q100 -107 113 -104T136 -94T154 -76T172 -47L204 11L77 502H230L295 154H299L461 502H612L332 -21Z" />
+<glyph unicode="z" horiz-adv-x="482" d="M34 0L47 95L321 389H91L107 502H509L495 401L225 112H463L448 0H34Z" />
+<glyph unicode="{" horiz-adv-x="413" d="M252 -125Q184 -125 152 -104T120 -27Q120 -8 123 15L147 190Q150 206 150 220Q150 241 141 255T105 275L118 369Q154 376 166 399T184 454L211 646Q217 686 229 713T261 756T311 779T379 786H454L442 702H407Q382 702 369 698T348 683T337 657T331 618L305 430Q300 391 282 369T228 322Q257 298 268 276T275 214L250 42Q248 28 247 18T246 -2Q246 -24 257 -33T303 -42H338L326 -125H252Z" />
+<glyph unicode="|" horiz-adv-x="287" d="M58 -200L190 740H293L161 -200H58Z" />
+<glyph unicode="}" horiz-adv-x="413" d="M384 275Q347 267 335 245T318 190L294 15Q288 -26 276 -52T244 -95T194 -118T126 -125H51L63 -42H99Q123 -42 136 -38T156 -24T167 2T174 42L199 214Q202 234 207 249T221 276T243 299T274 322Q245 347 235 369T229 430L255 618Q257 632 258 643T260 664Q260 684 249 693T203 702H167L179 786H254Q322 786 353 765T385 686Q385 668 382 646L355 454Q350 422 357 399T397 369L384 275H384Z" />
+<glyph unicode="~" horiz-adv-x="478" d="M558 623Q533 578 495 556T411 534Q384 534 361 540T317 555T274 570T231 577Q209 577 192 569T157 533L81 569Q98 594 114 610T147 637T183 652T225 657Q251 657 274 650T319 635T362 620T405 613Q426 613 443 622T476 656L558 623H558Z" />
+<glyph unicode="&#xA0;" horiz-adv-x="320" />
+<glyph unicode="&#xA1;" horiz-adv-x="299" d="M64 -164Q86 -44 108 74T151 312H267Q253 193 242 75T219 -164H64ZM134 433Q134 453 143 468T168 493T201 509T237 515Q252 515 267 512T294 500T313 480T321 451Q321 431 311 417T286 392T253 376T218 371Q204 371 189 374T162 385T142 405T134 433Z" />
+<glyph unicode="&#xA2;" horiz-adv-x="613" d="M560 159Q527 130 489 109T407 79L396 0H265L275 78Q194 94 156 150T118 285Q118 340 132 390T176 482T247 551T347 590L358 667H489L478 590Q514 584 545 567T605 527L530 433Q504 461 476 474T410 487Q369 487 341 471T295 429T270 369T262 298Q262 245 286 213T366 181Q411 181 444 197T511 243L560 159H560Z" />
+<glyph unicode="&#xA3;" horiz-adv-x="613" d="M558 0H55L70 109H98Q136 109 157 126T190 172T203 234T206 302H107L122 411H208Q206 430 205 446T204 480Q204 582 264 630T435 678Q544 678 629 634L586 513Q557 533 525 546T444 560Q387 560 364 537T340 456Q340 446 340 436T342 411H500L485 302H347Q348 289 348 278T349 255Q349 220 341 185T306 122L305 119H574L558 0Z" />
+<glyph unicode="&#xA4;" horiz-adv-x="613" d="M132 312Q132 351 146 387T184 455L116 535L198 607L268 525Q294 539 322 546T381 554Q438 554 488 523L584 607L646 535L552 452Q571 415 571 371Q571 332 558 297T522 230L591 149L509 76L439 158Q412 143 382 135T321 126Q265 126 215 157L123 77L61 149L151 228Q132 266 132 312ZM366 441Q344 441 324 432T287 407T262 371T252 328Q252 291 277 267T339 242Q361 242 381 251T418 276T443 312T452 355Q452 393 428 417T366 441Z" />
+<glyph unicode="&#xA5;" horiz-adv-x="613" d="M519 398H627L615 310H453L420 266L411 206H600L588 118H399L383 0H240L256 118H60L72 206H268L276 259L251 310H87L99 398H207L107 667H275L367 385L535 667H692L519 398Z" />
+<glyph unicode="&#xA6;" horiz-adv-x="287" d="M135 360L189 740H292L238 360H135ZM58 -200L111 180H214L161 -200H58Z" />
+<glyph unicode="&#xA7;" horiz-adv-x="496" d="M496 341Q496 309 486 285T457 244T413 217T358 201L357 198Q399 187 427 159T456 85Q456 38 435 6T381 -47T306 -75T225 -84Q164 -84 116 -71T17 -23L79 75Q114 52 154 35T238 18Q251 18 264 20T287 27T304 42T311 67Q311 83 293 95T247 118T188 140T128 169T83 209T64 265Q64 297 76 321T108 361T154 387T210 401V405Q190 411 171 420T138 444T115 476T106 517Q106 561 127 591T181 641T253 669T330 678Q381 678 427 669T520 637L466 530Q434 550 401 561T328 573Q317 573 304 572T278 567T258 554T250 530Q250 513 268 502T313 482T373 462T432 436T478 398T496 341ZM271 239Q286 239 303 241T334 250T357 268T366 300Q366 315 358 323T337 336T311 342T286 343Q271 343 255 341T224 333T200 316T190 285Q190 270 198 261T219 247T246 241T271 239Z" />
+<glyph unicode="&#xA8;" horiz-adv-x="348" d="M445 649Q445 633 439 619T421 593T396 576T365 569Q339 569 322 586T304 630Q304 646 311 660T329 684T355 701T385 708Q410 708 427 692T445 649ZM222 649Q222 633 216 619T198 593T173 576T142 569Q116 569 99 586T82 629Q82 645 88 659T106 684T132 701T162 708Q187 708 204 692T222 649Z" />
+<glyph unicode="&#xA9;" horiz-adv-x="795" d="M743 334Q743 262 716 200T642 90T533 16T398 -11Q326 -11 263 16T153 90T79 199T52 334Q52 406 79 468T153 578T263 652T398 679Q470 679 532 652T642 578T716 469T743 334ZM698 335Q698 397 675 451T611 547T516 611T398 635Q336 635 282 612T187 547T123 452T99 335Q99 273 122 219T187 125T282 61T398 37Q460 37 514 60T610 124T674 219T698 335ZM572 187Q537 154 498 137T411 119Q355 119 315 136T250 183T214 252T202 337Q202 435 255 492T409 549Q457 549 496 532T571 479L515 418Q470 471 413 471Q351 471 324 436T297 337Q297 270 324 234T412 198Q444 198 470 211T523 250L572 187Z" />
+<glyph unicode="&#xAA;" horiz-adv-x="415" d="M352 371Q337 374 325 382T309 406H305Q282 390 257 381T203 371Q167 371 142 388T116 446Q116 488 136 513T186 551T252 569T321 574Q322 580 323 586T324 597Q324 610 314 615T287 620Q246 620 214 609T168 590L149 650Q186 667 223 676T301 686Q325 686 344 683T378 671T400 645T408 601Q408 582 406 562T400 521T394 484T391 453Q391 442 398 436T414 424L352 371ZM314 524Q297 524 278 523T242 515T214 495T202 460Q202 427 236 427Q255 427 271 434T304 452L314 524Z" />
+<glyph unicode="&#xAB;" horiz-adv-x="499" d="M381 97L286 300L438 502L526 455L405 299L483 144L381 97ZM157 85L56 300L216 513L304 466L175 299L259 133L157 85Z" />
+<glyph unicode="&#xAC;" horiz-adv-x="613" d="M480 211H74L91 329H614L572 34H455L480 211Z" />
+<glyph unicode="&#xAD;" horiz-adv-x="383" d="M75 195L93 327H380L362 195H75Z" />
+<glyph unicode="&#xAE;" horiz-adv-x="451" d="M295 362Q259 362 228 375T173 411T136 465T122 531Q122 566 135 597T172 651T227 687T295 700Q331 700 362 687T418 651T455 597T469 531Q469 496 456 465T418 411T363 375T295 362ZM296 677Q265 677 238 666T191 634T159 588T147 532Q147 502 158 476T190 430T238 399T296 387Q326 387 353 398T401 429T433 475T445 532Q445 562 433 588T401 634T354 665T296 677ZM224 627Q242 629 260 629T295 629Q310 629 325 627T352 618T371 599T378 568Q378 535 349 519L383 439H329L304 503Q302 505 302 506T298 507H273V439H224V627ZM295 592Q288 592 282 592T272 591V542H299Q316 542 322 548T329 566Q329 579 322 585T295 592Z" />
+<glyph unicode="&#xAF;" horiz-adv-x="348" d="M94 587L106 674H429L417 587H94Z" />
+<glyph unicode="&#xB0;" horiz-adv-x="413" d="M437 551Q437 513 424 482T387 428T331 393T262 380Q202 380 163 416T124 513Q124 553 137 585T175 638T231 671T300 683Q360 683 398 648T437 551ZM266 422Q320 422 354 456T389 548Q389 593 365 617T297 641Q240 641 207 608T173 517Q173 472 197 447T266 422Z" />
+<glyph unicode="&#xB1;" horiz-adv-x="613" d="M413 323L390 157H269L292 323H90L106 440H308L334 623H455L429 440H629L613 323H413ZM45 0L60 112H583L568 0H45Z" />
+<glyph unicode="&#xB2;" horiz-adv-x="326" d="M89 366L86 439Q129 467 164 488T225 526T265 560T280 596Q280 608 272 616T246 625Q221 625 202 617T160 587L118 643Q158 674 192 687T266 700Q318 700 345 679T373 618Q373 584 356 557T313 509T255 470T193 438H353L343 366H89Z" />
+<glyph unicode="&#xB3;" horiz-adv-x="319" d="M351 478Q351 446 339 424T307 387T260 367T203 361Q168 361 136 370T76 404L122 464Q144 444 164 437T205 429Q215 429 225 431T245 439T259 454T265 475Q265 491 254 496T225 502H172L180 562H214Q237 562 254 572T272 602Q272 613 264 621T237 630Q214 630 191 621T148 595L115 654Q153 680 185 690T257 700Q305 700 333 681T361 621Q361 592 349 575T310 543V539Q351 527 351 478Z" />
+<glyph unicode="&#xB4;" horiz-adv-x="348" d="M209 565L131 610L289 742L421 683L209 565Z" />
+<glyph unicode="&#xB5;" horiz-adv-x="604" d="M405 0L403 58H398Q384 41 372 28T345 6T312 -7T267 -12Q262 -12 252 -11T231 -7T211 3T196 21L163 -220H38L139 502H281L244 239Q241 219 239 203T237 171Q237 141 251 124T308 106Q336 106 355 113T386 132T404 164T414 207L455 502H597L527 0H405Z" />
+<glyph unicode="&#xB6;" horiz-adv-x="536" d="M333 0Q282 0 235 11T152 48T94 114T72 212Q72 282 99 336T172 427T277 483T403 502H531L461 0H333Z" />
+<glyph unicode="&#xB7;" horiz-adv-x="259" d="M255 249Q255 230 245 215T220 190T186 175T151 170Q137 170 122 173T95 185T76 204T68 233Q68 252 77 267T102 292T135 308T171 313Q185 313 200 309T227 298T247 278T255 249Z" />
+<glyph unicode="&#xB8;" horiz-adv-x="348" d="M266 -108Q266 -135 257 -154T231 -187T192 -206T146 -213Q123 -213 99 -206T55 -188L78 -140Q94 -148 108 -153T141 -159Q161 -159 177 -151T193 -120Q193 -103 181 -98T155 -92Q130 -92 113 -100L88 -86L143 0H207L170 -53Q188 -47 204 -47Q230 -47 248 -62T266 -108Z" />
+<glyph unicode="&#xB9;" horiz-adv-x="301" d="M172 438L195 602H191L123 573L105 639L228 694H299L263 438H340L330 367H81L91 438H172Z" />
+<glyph unicode="&#xBA;" horiz-adv-x="415" d="M439 561Q439 519 427 485T392 425T336 385T259 371Q197 371 161 401T124 496Q124 538 136 572T170 632T226 672T303 686Q365 686 402 656T439 561ZM269 439Q294 439 310 449T335 477T347 515T351 557Q351 588 337 601T293 615Q268 615 252 605T227 578T215 540T211 498Q211 439 269 439Z" />
+<glyph unicode="&#xBB;" horiz-adv-x="499" d="M364 85L276 133L407 299L322 467L425 514L527 300L364 85ZM143 97L55 144L178 299L98 455L200 502L297 300L143 97Z" />
+<glyph unicode="&#xBC;" horiz-adv-x="798" d="M209 438L232 602H228L160 573L142 639L265 694H336L300 438H377L367 367H118L128 438H209ZM214 0H138L756 700H832L214 0ZM727 62L719 0H629L637 62H501L490 126L675 327H764L737 134H788L778 62H727ZM569 127H650L667 244L569 127Z" />
+<glyph unicode="&#xBD;" horiz-adv-x="771" d="M207 438L230 602H226L158 573L140 639L263 694H334L298 438H375L365 367H116L126 438H207ZM182 0H106L724 700H800L182 0ZM485 -1L482 72Q525 100 560 121T621 159T661 193T676 229Q676 241 668 249T642 258Q617 258 598 250T556 220L514 276Q554 307 588 320T662 333Q714 333 741 312T769 251Q769 217 752 190T709 142T651 103T589 71H749L739 -1H485Z" />
+<glyph unicode="&#xBE;" horiz-adv-x="752" d="M389 478Q389 446 377 424T345 387T298 367T241 361Q206 361 174 370T114 404L160 464Q182 444 202 437T243 429Q253 429 263 431T283 439T297 454T303 475Q303 491 292 496T263 502H210L218 562H252Q275 562 292 572T310 602Q310 613 302 621T275 630Q252 630 229 621T186 595L153 654Q191 680 223 690T295 700Q343 700 371 681T399 621Q399 592 387 575T348 543V539Q389 527 389 478ZM196 0H120L738 700H814L196 0ZM690 62L682 0H592L600 62H464L453 126L638 327H727L700 134H751L741 62H690ZM532 127H613L630 244L532 127Z" />
+<glyph unicode="&#xBF;" d="M82 -16Q82 33 96 67T135 127T195 171T272 208Q284 213 291 217T303 228T309 243T314 268L320 308H456L446 237Q443 214 438 199T423 172T400 152T365 134Q343 124 318 115T272 91T236 58T221 9Q221 -11 229 -23T252 -44T282 -55T316 -58Q403 -58 474 -1L527 -102Q496 -121 468 -135T410 -158T348 -171T280 -176Q240 -176 204 -168T141 -140T98 -91T82 -16ZM317 434Q317 453 327 468T352 494T385 509T420 515Q434 515 449 512T476 500T495 480T503 452Q503 433 494 418T469 393T436 378T401 372Q387 372 372 375T345 386T325 406T317 434Z" />
+<glyph unicode="&#xC0;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM444 705L264 823L414 881L534 750L444 705Z" />
+<glyph unicode="&#xC1;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM444 705L366 750L524 882L656 823L444 705Z" />
+<glyph unicode="&#xC2;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM568 701L454 775L316 701L271 780L466 878L636 780L568 701Z" />
+<glyph unicode="&#xC3;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM646 795Q625 750 590 729T520 707Q497 707 480 713T447 728T418 743T389 750Q372 750 354 741T322 706L264 759Q293 809 323 827T389 846Q410 846 427 839T460 824T490 809T521 802Q538 802 555 811T586 846L646 795H646Z" />
+<glyph unicode="&#xC4;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM636 789Q636 773 630 759T612 733T587 716T556 709Q530 709 513 726T495 770Q495 786 502 800T520 824T546 841T576 848Q601 848 618 832T636 789ZM413 789Q413 773 407 759T389 733T364 716T333 709Q307 709 290 726T273 769Q273 785 279 799T297 824T323 841T353 848Q378 848 395 832T413 789Z" />
+<glyph unicode="&#xC5;" horiz-adv-x="700" d="M597 737Q597 700 580 668T533 614L689 0H533L499 140H236L163 0H12L333 613Q314 629 305 653T296 703Q296 737 309 766T344 815T397 846T460 858Q487 858 512 851T555 828T585 790T597 737ZM421 526H417L287 258H479L421 526ZM440 650Q473 650 493 672T513 730Q513 757 496 771T454 786Q419 786 399 765T379 708Q379 682 396 666T440 650Z" />
+<glyph unicode="&#xC6;" horiz-adv-x="939" d="M454 0L475 151H255L165 0H14L448 667H985L969 548H674L654 406H920L902 283H636L613 119H918L902 0H454ZM530 547H507L328 273H492L530 547Z" />
+<glyph unicode="&#xC7;" horiz-adv-x="665" d="M462 -110Q462 -137 453 -156T427 -189T388 -208T342 -215Q319 -215 295 -208T251 -190L274 -142Q290 -150 304 -155T337 -161Q357 -161 373 -153T389 -122Q389 -105 377 -100T351 -94Q326 -94 309 -102L284 -88L334 -9Q222 2 160 68T97 261Q97 347 122 423T195 555T314 645T476 678Q551 678 609 651T715 567L613 474Q584 512 549 533T465 555Q405 555 364 531T298 467T261 377T250 274Q250 198 285 154T401 110Q455 110 501 134T588 193L653 96Q596 48 534 20T397 -11L366 -55Q384 -49 400 -49Q426 -49 444 -64T462 -110Z" />
+<glyph unicode="&#xC8;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM430 705L250 823L400 881L520 750L430 705Z" />
+<glyph unicode="&#xC9;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM395 705L317 750L475 882L607 823L395 705Z" />
+<glyph unicode="&#xCA;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM522 701L408 775L270 701L225 780L420 878L590 780L522 701Z" />
+<glyph unicode="&#xCB;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM590 789Q590 773 584 759T566 733T541 716T510 709Q484 709 467 726T449 770Q449 786 456 800T474 824T500 841T530 848Q555 848 572 832T590 789ZM367 789Q367 773 361 759T343 733T318 716T287 709Q261 709 244 726T227 769Q227 785 233 799T251 824T277 841T307 848Q332 848 349 832T367 789Z" />
+<glyph unicode="&#xCC;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM244 705L64 823L214 881L334 750L244 705Z" />
+<glyph unicode="&#xCD;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM248 705L170 750L328 882L460 823L248 705Z" />
+<glyph unicode="&#xCE;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM376 701L262 775L124 701L79 780L274 878L444 780L376 701Z" />
+<glyph unicode="&#xCF;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM438 789Q438 773 432 759T414 733T389 716T358 709Q332 709 315 726T297 770Q297 786 304 800T322 824T348 841T378 848Q403 848 420 832T438 789ZM215 789Q215 773 209 759T191 733T166 716T135 709Q109 709 92 726T75 769Q75 785 81 799T99 824T125 841T155 848Q180 848 197 832T215 789Z" />
+<glyph unicode="&#xD0;" horiz-adv-x="704" d="M700 407Q700 310 672 235T592 107T464 28T292 0H75L115 289H45L62 408H132L168 662Q226 667 283 670T399 674Q465 674 520 659T615 611T677 528T700 407ZM307 116Q367 116 408 130T478 171T522 241T546 340Q548 355 549 370T551 400Q551 560 388 560Q365 560 343 559T297 556L276 408H430L413 289H259L235 117Q253 116 271 116T307 116Z" />
+<glyph unicode="&#xD1;" horiz-adv-x="731" d="M532 0L278 425H274L215 0H73L166 667H301L546 249H551L610 667H751L658 0H532ZM664 795Q643 750 608 729T538 707Q515 707 498 713T465 728T436 743T407 750Q390 750 372 741T340 706L282 759Q311 809 341 827T407 846Q428 846 445 839T478 824T508 809T539 802Q556 802 573 811T604 846L664 795H664Z" />
+<glyph unicode="&#xD2;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM522 709L342 827L492 885L612 754L522 709Z" />
+<glyph unicode="&#xD3;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM485 709L407 754L565 886L697 827L485 709Z" />
+<glyph unicode="&#xD4;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM615 701L501 775L363 701L318 780L513 878L683 780L615 701Z" />
+<glyph unicode="&#xD5;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM681 799Q660 754 625 733T555 711Q532 711 515 717T482 732T453 747T424 754Q407 754 389 745T357 710L299 763Q328 813 358 831T424 850Q445 850 462 843T495 828T525 813T556 806Q573 806 590 815T621 850L681 799H681Z" />
+<glyph unicode="&#xD6;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM678 789Q678 773 672 759T654 733T629 716T598 709Q572 709 555 726T537 770Q537 786 544 800T562 824T588 841T618 848Q643 848 660 832T678 789ZM455 789Q455 773 449 759T431 733T406 716T375 709Q349 709 332 726T315 769Q315 785 321 799T339 824T365 841T395 848Q420 848 437 832T455 789Z" />
+<glyph unicode="&#xD7;" horiz-adv-x="613" d="M611 430L426 269L566 106L473 23L333 186L146 23L74 106L261 269L122 430L217 513L356 351L541 513L611 430Z" />
+<glyph unicode="&#xD8;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q327 -10 280 1T196 38L127 -26L64 59L126 117Q96 174 96 259Q96 349 121 425T195 558T316 646T481 678Q541 678 590 665T677 623L749 690L819 612L743 541Q768 485 768 411ZM248 263Q248 254 248 246T249 230L573 528Q535 560 464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 412 616 415T615 424L300 133Q335 108 401 108Z" />
+<glyph unicode="&#xD9;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM447 705L267 823L417 881L537 750L447 705Z" />
+<glyph unicode="&#xDA;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM447 705L369 750L527 882L659 823L447 705Z" />
+<glyph unicode="&#xDB;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM574 701L460 775L322 701L277 780L472 878L642 780L574 701Z" />
+<glyph unicode="&#xDC;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM640 789Q640 773 634 759T616 733T591 716T560 709Q534 709 517 726T499 770Q499 786 506 800T524 824T550 841T580 848Q605 848 622 832T640 789ZM417 789Q417 773 411 759T393 733T368 716T337 709Q311 709 294 726T277 769Q277 785 283 799T301 824T327 841T357 848Q382 848 399 832T417 789Z" />
+<glyph unicode="&#xDD;" horiz-adv-x="646" d="M433 262L397 0H251L287 259L95 667H263L379 390L576 667H737L433 262ZM443 689L365 734L523 866L655 807L443 689Z" />
+<glyph unicode="&#xDE;" horiz-adv-x="632" d="M640 376Q640 317 622 270T568 188T484 135T371 116H232L216 0H73L166 667H308L292 550Q341 553 385 553Q437 553 483 545T564 517T619 463T640 376ZM362 230Q426 230 460 259T495 359Q495 403 466 420T370 437Q349 437 327 436T276 433L248 230H362Z" />
+<glyph unicode="&#xDF;" horiz-adv-x="589" d="M581 200Q581 147 562 107T511 41T437 1T347 -13Q300 -13 250 1L282 99Q294 96 307 94T333 92Q382 92 409 121T437 200Q437 226 426 244T398 276T359 297T315 310L311 393Q331 406 353 422T393 457T423 499T435 548Q435 581 415 593T365 606Q321 606 301 585T274 522L201 0H63L136 524Q143 577 163 612T213 667T285 696T377 705Q414 705 447 698T506 673T547 629T562 562Q562 505 532 462T456 387V383Q510 359 545 311T581 200Z" />
+<glyph unicode="&#xE0;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM362 565L182 683L332 741L452 610L362 565Z" />
+<glyph unicode="&#xE1;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM344 565L266 610L424 742L556 683L344 565Z" />
+<glyph unicode="&#xE2;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM479 559L365 633L227 559L182 638L377 736L547 638L479 559Z" />
+<glyph unicode="&#xE3;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM549 655Q528 610 493 589T423 567Q400 567 383 573T350 588T321 603T292 610Q275 610 257 601T225 566L167 619Q196 669 226 687T292 706Q313 706 330 699T363 684T393 669T424 662Q441 662 458 671T489 706L549 655H549Z" />
+<glyph unicode="&#xE4;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM544 649Q544 633 538 619T520 593T495 576T464 569Q438 569 421 586T403 630Q403 646 410 660T428 684T454 701T484 708Q509 708 526 692T544 649ZM321 649Q321 633 315 619T297 593T272 576T241 569Q215 569 198 586T181 629Q181 645 187 659T205 684T231 701T261 708Q286 708 303 692T321 649Z" />
+<glyph unicode="&#xE5;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM482 682Q482 656 472 634T446 597T406 572T358 563Q317 563 287 586T257 652Q257 677 267 699T293 736T332 761T380 770Q400 770 418 765T451 748T473 720T482 682ZM367 614Q393 614 407 632T421 676Q421 696 409 707T374 718Q349 718 334 701T318 658Q318 638 331 626T367 614Z" />
+<glyph unicode="&#xE6;" horiz-adv-x="891" d="M888 298Q888 274 885 250T876 203H521L520 190Q520 164 529 147T554 118T591 102T635 97Q677 97 715 107T790 138L823 43Q773 17 722 3T615 -11Q561 -11 513 5T431 59Q415 45 390 32T336 9T273 -7T209 -13Q178 -13 150 -7T100 15T66 54T53 114Q53 183 86 224T171 287T282 316T395 323Q397 334 398 344T399 365Q399 380 393 388T378 400T357 405T333 406Q286 406 236 392T143 355L111 453Q172 482 232 497T360 513Q379 513 400 511T442 503T479 486T508 457Q544 486 588 500T678 514Q779 514 833 456T888 298ZM763 295Q763 411 665 411Q610 411 576 380T533 295H763ZM390 130Q385 144 383 160T380 190Q380 203 381 216T385 242Q358 242 324 240T261 227T212 195T192 137Q192 109 209 93T253 77Q296 77 330 93T390 130Z" />
+<glyph unicode="&#xE7;" horiz-adv-x="519" d="M378 -113Q378 -140 369 -159T343 -192T304 -211T258 -218Q235 -218 211 -211T167 -193L190 -145Q206 -153 220 -158T253 -164Q273 -164 289 -156T305 -125Q305 -108 293 -103T267 -97Q242 -97 225 -105L200 -91L252 -9Q171 2 122 52T72 196Q72 264 93 322T155 422T251 489T377 513Q428 513 475 494T559 441L486 350Q462 379 433 391T366 404Q326 404 298 387T251 343T225 282T217 213Q217 160 242 129T323 97Q367 97 401 109T471 147L503 54Q458 25 413 8T314 -12L282 -58Q300 -52 316 -52Q342 -52 360 -67T378 -113Z" />
+<glyph unicode="&#xE8;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM385 565L205 683L355 741L475 610L385 565Z" />
+<glyph unicode="&#xE9;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM373 565L295 610L453 742L585 683L373 565Z" />
+<glyph unicode="&#xEA;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM498 561L384 635L246 561L201 640L396 738L566 640L498 561Z" />
+<glyph unicode="&#xEB;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM570 649Q570 633 564 619T546 593T521 576T490 569Q464 569 447 586T429 630Q429 646 436 660T454 684T480 701T510 708Q535 708 552 692T570 649ZM347 649Q347 633 341 619T323 593T298 576T267 569Q241 569 224 586T207 629Q207 645 213 659T231 684T257 701T287 708Q312 708 329 692T347 649Z" />
+<glyph unicode="&#xEC;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM213 565L33 683L183 741L303 610L213 565Z" />
+<glyph unicode="&#xED;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM213 565L135 610L293 742L425 683L213 565Z" />
+<glyph unicode="&#xEE;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM339 561L225 635L87 561L42 640L237 738L407 640L339 561Z" />
+<glyph unicode="&#xEF;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM405 649Q405 633 399 619T381 593T356 576T325 569Q299 569 282 586T264 630Q264 646 271 660T289 684T315 701T345 708Q370 708 387 692T405 649ZM182 649Q182 633 176 619T158 593T133 576T102 569Q76 569 59 586T42 629Q42 645 48 659T66 684T92 701T122 708Q147 708 164 692T182 649Z" />
+<glyph unicode="&#xF0;" horiz-adv-x="574" d="M543 606Q566 555 573 498T581 384Q581 310 565 239T513 113T419 24T275 -10Q180 -10 126 43T72 192Q72 250 87 302T134 393T212 455T321 478Q337 478 353 477T385 471T413 457T436 433H441V449Q441 478 435 507T417 562L300 517L277 588L384 629Q373 647 362 662T334 691L464 725Q475 714 485 702T504 677L580 710L607 634L543 606ZM302 95Q343 95 368 113T407 160T426 224T431 293Q431 341 410 363T341 385Q300 385 275 367T235 321T217 258T212 189Q212 141 232 118T302 95Z" />
+<glyph unicode="&#xF1;" horiz-adv-x="593" d="M389 0L428 281Q430 293 431 303T432 324Q432 356 417 375T357 395Q259 395 246 300L204 0H63L133 502H254L256 442H260Q293 482 330 497T418 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389ZM581 655Q560 610 525 589T455 567Q432 567 415 573T382 588T353 603T324 610Q307 610 289 601T257 566L199 619Q228 669 258 687T324 706Q345 706 362 699T395 684T425 669T456 662Q473 662 490 671T521 706L581 655H581Z" />
+<glyph unicode="&#xF2;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM392 565L212 683L362 741L482 610L392 565Z" />
+<glyph unicode="&#xF3;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM383 565L305 610L463 742L595 683L383 565Z" />
+<glyph unicode="&#xF4;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM506 561L392 635L254 561L209 640L404 738L574 640L506 561Z" />
+<glyph unicode="&#xF5;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM582 655Q561 610 526 589T456 567Q433 567 416 573T383 588T354 603T325 610Q308 610 290 601T258 566L200 619Q229 669 259 687T325 706Q346 706 363 699T396 684T426 669T457 662Q474 662 491 671T522 706L582 655H582Z" />
+<glyph unicode="&#xF6;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM570 649Q570 633 564 619T546 593T521 576T490 569Q464 569 447 586T429 630Q429 646 436 660T454 684T480 701T510 708Q535 708 552 692T570 649ZM347 649Q347 633 341 619T323 593T298 576T267 569Q241 569 224 586T207 629Q207 645 213 659T231 684T257 701T287 708Q312 708 329 692T347 649Z" />
+<glyph unicode="&#xF7;" horiz-adv-x="613" d="M74 211L91 329H614L597 211H74ZM232 59Q232 77 240 94T261 124T291 144T327 152Q357 152 377 132T398 82Q398 63 391 46T370 16T340 -5T304 -13Q273 -13 253 7T232 59ZM290 464Q290 483 297 500T318 531T348 551T384 559Q414 559 435 539T456 488Q456 470 448 453T427 423T397 402T361 394Q330 394 310 413T290 464Z" />
+<glyph unicode="&#xF8;" horiz-adv-x="601" d="M569 424Q586 398 592 368T598 306Q598 237 578 179T520 79T426 13T299 -11Q259 -11 219 -2T147 33L87 -23L36 41L93 95Q82 118 77 143T72 195Q72 264 92 322T151 423T246 489T373 513Q408 513 443 507T508 482L573 539L630 480L569 424ZM413 379Q402 390 387 395T357 400Q316 400 289 384T246 340T223 279T216 209Q216 203 218 193L413 379ZM315 98Q356 98 383 115T425 161T447 224T454 294Q454 309 451 324L242 129Q252 112 274 105T315 98Z" />
+<glyph unicode="&#xF9;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM373 565L193 683L343 741L463 610L373 565Z" />
+<glyph unicode="&#xFA;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM363 565L285 610L443 742L575 683L363 565Z" />
+<glyph unicode="&#xFB;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM499 561L385 635L247 561L202 640L397 738L567 640L499 561Z" />
+<glyph unicode="&#xFC;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM564 649Q564 633 558 619T540 593T515 576T484 569Q458 569 441 586T423 630Q423 646 430 660T448 684T474 701T504 708Q529 708 546 692T564 649ZM341 649Q341 633 335 619T317 593T292 576T261 569Q235 569 218 586T201 629Q201 645 207 659T225 684T251 701T281 708Q306 708 323 692T341 649Z" />
+<glyph unicode="&#xFD;" horiz-adv-x="549" d="M332 -21Q299 -83 273 -123T220 -186T161 -217T83 -226Q69 -226 51 -225T8 -221L25 -104Q32 -106 44 -106T80 -107Q100 -107 113 -104T136 -94T154 -76T172 -47L204 11L77 502H230L295 154H299L461 502H612L332 -21ZM354 565L276 610L434 742L566 683L354 565Z" />
+<glyph unicode="&#xFE;" horiz-adv-x="590" d="M588 317Q588 260 572 202T523 96T441 19T324 -11Q282 -11 258 3T214 46H210L173 -220H32L161 700H292L257 450H261Q293 484 328 498T408 513Q499 513 543 461T588 317ZM310 95Q351 95 377 116T418 170T439 240T445 314Q445 361 426 384T356 407Q306 407 281 386T248 318L233 209Q230 189 230 173Q230 132 249 114T310 95Z" />
+<glyph unicode="&#xFF;" horiz-adv-x="549" d="M332 -21Q299 -83 273 -123T220 -186T161 -217T83 -226Q69 -226 51 -225T8 -221L25 -104Q32 -106 44 -106T80 -107Q100 -107 113 -104T136 -94T154 -76T172 -47L204 11L77 502H230L295 154H299L461 502H612L332 -21ZM550 649Q550 633 544 619T526 593T501 576T470 569Q444 569 427 586T409 630Q409 646 416 660T434 684T460 701T490 708Q515 708 532 692T550 649ZM327 649Q327 633 321 619T303 593T278 576T247 569Q221 569 204 586T187 629Q187 645 193 659T211 684T237 701T267 708Q292 708 309 692T327 649Z" />
+<glyph unicode="&#x100;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM286 727L298 814H621L609 727H286Z" />
+<glyph unicode="&#x101;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM193 587L205 674H528L516 587H193Z" />
+<glyph unicode="&#x102;" horiz-adv-x="701" d="M533 0L499 140H236L163 0H12L363 670H519L689 0H533ZM421 525H417L287 258H479L421 525ZM637 846Q626 801 603 774T552 731T495 710T442 705Q418 705 391 710T341 730T304 773T291 846H388Q387 820 401 799T453 778Q489 778 511 799T539 846H637H637Z" />
+<glyph unicode="&#x103;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM540 706Q529 661 506 634T455 591T398 570T345 565Q321 565 294 570T244 590T207 633T194 706H291Q290 680 304 659T356 638Q392 638 414 659T442 706H540H540Z" />
+<glyph unicode="&#x104;" horiz-adv-x="701" d="M611 0Q597 -9 581 -21T552 -48T529 -80T520 -116Q520 -136 531 -146T562 -156Q579 -156 590 -152T616 -141L628 -219Q607 -228 578 -234T525 -241Q481 -241 450 -221T418 -151Q418 -125 428 -103T454 -61T490 -26T532 1L499 140H236L163 0H12L363 670H519L689 0H611ZM421 525H417L287 258H479L421 525Z" />
+<glyph unicode="&#x105;" horiz-adv-x="590" d="M489 0Q475 -10 460 -22T431 -48T408 -79T399 -115Q399 -134 410 -145T441 -156Q458 -156 469 -152T495 -141L507 -219Q485 -228 456 -234T404 -241Q359 -241 328 -220T297 -150Q297 -125 306 -103T332 -61T368 -27T411 1V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q378 513 407 497T454 446H458L474 502H597L527 0H489ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316Z" />
+<glyph unicode="&#x106;" horiz-adv-x="665" d="M653 96Q593 46 527 18T383 -11Q249 -11 173 56T97 261Q97 347 122 423T195 555T314 645T476 678Q551 678 609 651T715 567L613 474Q584 512 549 533T465 555Q405 555 364 531T298 467T261 377T250 274Q250 198 285 154T401 110Q455 110 501 134T588 193L653 96H653ZM461 710L383 755L541 887L673 828L461 710Z" />
+<glyph unicode="&#x107;" horiz-adv-x="519" d="M486 350Q462 379 433 391T366 404Q326 404 298 387T251 343T225 282T217 213Q217 160 242 129T323 97Q367 97 401 109T471 147L503 54Q455 23 406 6T299 -12Q249 -12 207 1T135 39T89 104T72 196Q72 264 93 322T155 422T251 489T377 513Q428 513 475 494T559 441L486 350ZM369 565L291 610L449 742L581 683L369 565Z" />
+<glyph unicode="&#x108;" horiz-adv-x="665" d="M653 96Q593 46 527 18T383 -11Q249 -11 173 56T97 261Q97 347 122 423T195 555T314 645T476 678Q551 678 609 651T715 567L613 474Q584 512 549 533T465 555Q405 555 364 531T298 467T261 377T250 274Q250 198 285 154T401 110Q455 110 501 134T588 193L653 96H653ZM605 704L491 778L353 704L308 783L503 881L673 783L605 704Z" />
+<glyph unicode="&#x109;" horiz-adv-x="519" d="M486 350Q462 379 433 391T366 404Q326 404 298 387T251 343T225 282T217 213Q217 160 242 129T323 97Q367 97 401 109T471 147L503 54Q455 23 406 6T299 -12Q249 -12 207 1T135 39T89 104T72 196Q72 264 93 322T155 422T251 489T377 513Q428 513 475 494T559 441L486 350ZM496 561L382 635L244 561L199 640L394 738L564 640L496 561Z" />
+<glyph unicode="&#x10A;" horiz-adv-x="665" d="M653 96Q593 46 527 18T383 -11Q249 -11 173 56T97 261Q97 347 122 423T195 555T314 645T476 678Q551 678 609 651T715 567L613 474Q584 512 549 533T465 555Q405 555 364 531T298 467T261 377T250 274Q250 198 285 154T401 110Q455 110 501 134T588 193L653 96H653ZM571 797Q571 758 542 737T477 716Q463 716 450 719T426 730T409 748T402 775Q402 795 410 810T431 835T462 850T497 855Q525 855 548 842T571 797Z" />
+<glyph unicode="&#x10B;" horiz-adv-x="519" d="M486 350Q462 379 433 391T366 404Q326 404 298 387T251 343T225 282T217 213Q217 160 242 129T323 97Q367 97 401 109T471 147L503 54Q455 23 406 6T299 -12Q249 -12 207 1T135 39T89 104T72 196Q72 264 93 322T155 422T251 489T377 513Q428 513 475 494T559 441L486 350ZM467 650Q467 611 438 590T373 569Q359 569 346 572T322 583T305 601T298 628Q298 648 306 663T327 688T358 703T393 708Q421 708 444 695T467 650Z" />
+<glyph unicode="&#x10C;" horiz-adv-x="665" d="M653 96Q593 46 527 18T383 -11Q249 -11 173 56T97 261Q97 347 122 423T195 555T314 645T476 678Q551 678 609 651T715 567L613 474Q584 512 549 533T465 555Q405 555 364 531T298 467T261 377T250 274Q250 198 285 154T401 110Q455 110 501 134T588 193L653 96H653ZM659 808L477 711L321 808L384 883L489 814L616 883L659 808Z" />
+<glyph unicode="&#x10D;" horiz-adv-x="519" d="M486 350Q462 379 433 391T366 404Q326 404 298 387T251 343T225 282T217 213Q217 160 242 129T323 97Q367 97 401 109T471 147L503 54Q455 23 406 6T299 -12Q249 -12 207 1T135 39T89 104T72 196Q72 264 93 322T155 422T251 489T377 513Q428 513 475 494T559 441L486 350ZM562 662L380 565L224 662L287 737L392 668L519 737L562 662Z" />
+<glyph unicode="&#x10E;" horiz-adv-x="704" d="M700 408Q700 314 674 239T596 110T471 29T303 0H73L166 662Q224 667 282 671T399 675Q465 675 520 660T616 613T678 530T700 408ZM307 116Q365 116 406 130T475 172T519 242T543 340Q545 356 546 371T548 402Q548 561 387 561Q364 561 342 560T297 557L235 117Q253 116 271 116T307 116ZM586 802L404 705L248 802L311 877L416 808L543 877L586 802Z" />
+<glyph unicode="&#x10F;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 168 5T113 49T83 114T73 194Q73 247 86 303T130 406T208 483T324 513Q363 513 394 501T445 455H449L484 700H625L527 0H411ZM432 316Q439 364 415 384T346 405Q304 405 279 386T239 336T221 268T216 196Q216 175 220 156T233 123T260 101T304 92Q360 92 384 123T416 206L432 316ZM737 518H643L706 700H844L737 518Z" />
+<glyph unicode="&#x110;" horiz-adv-x="704" d="M700 407Q700 310 672 235T592 107T464 28T292 0H75L115 289H45L62 408H132L168 662Q226 667 283 670T399 674Q465 674 520 659T615 611T677 528T700 407ZM307 116Q373 116 419 136T495 194T538 283T551 400Q551 560 388 560Q365 560 343 559T297 556L276 408H430L413 289H259L235 117Q253 116 271 116T307 116Z" />
+<glyph unicode="&#x111;" horiz-adv-x="590" d="M411 0V67H407Q379 26 338 8T250 -11Q201 -11 167 4T112 47T82 110T72 188Q72 242 85 296T128 395T204 468T317 496Q356 496 389 484T443 437H447L462 545H323L336 637H475L484 700H625L616 637H678L665 545H603L527 0H411ZM430 299Q437 347 412 367T342 388Q301 388 276 370T238 324T219 260T214 190Q214 170 218 152T232 121T260 100T304 92Q360 92 384 123T416 206L430 299Z" />
+<glyph unicode="&#x112;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM240 727L252 814H575L563 727H240Z" />
+<glyph unicode="&#x113;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM217 587L229 674H552L540 587H217Z" />
+<glyph unicode="&#x114;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM588 846Q577 801 554 774T503 731T446 710T393 705Q369 705 342 710T292 730T255 773T242 846H339Q338 820 352 799T404 778Q440 778 462 799T490 846H588H588Z" />
+<glyph unicode="&#x115;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM569 706Q558 661 535 634T484 591T427 570T374 565Q350 565 323 570T273 590T236 633T223 706H320Q319 680 333 659T385 638Q421 638 443 659T471 706H569H569Z" />
+<glyph unicode="&#x116;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM509 793Q509 754 480 733T415 712Q401 712 388 715T364 726T347 744T340 771Q340 791 348 806T369 831T400 846T435 851Q463 851 486 838T509 793Z" />
+<glyph unicode="&#x117;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM469 650Q469 611 440 590T375 569Q361 569 348 572T324 583T307 601T300 628Q300 648 308 663T329 688T360 703T395 708Q423 708 446 695T469 650Z" />
+<glyph unicode="&#x118;" horiz-adv-x="575" d="M481 0Q467 -9 451 -21T422 -48T400 -80T391 -116Q391 -134 402 -145T432 -156Q449 -156 460 -152T487 -141L499 -219Q477 -229 448 -235T395 -241Q351 -241 320 -221T289 -153Q289 -98 322 -61T400 0H73L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H481Z" />
+<glyph unicode="&#x119;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q490 32 463 20T413 -8T373 -45T356 -96Q356 -114 366 -127T397 -141Q414 -141 426 -137T452 -125L464 -204Q442 -213 413 -219T360 -225Q315 -225 285 -205T254 -136Q254 -97 276 -67T327 -12L326 -9Q315 -11 305 -11T285 -11Q237 -11 198 2T132 42T89 107T73 196Q73 262 93 320T150 421T242 489T366 514Q464 514 517 457T570 303ZM446 292Q447 298 447 303T448 315Q448 360 423 385T353 411Q298 411 264 378T222 292H446Z" />
+<glyph unicode="&#x11A;" horiz-adv-x="575" d="M73 0L166 667H621L604 545H295L275 404H555L538 280H258L235 119H554L538 0H73ZM581 802L399 705L243 802L306 877L411 808L538 877L581 802Z" />
+<glyph unicode="&#x11B;" horiz-adv-x="573" d="M570 303Q570 254 558 206H209Q208 201 208 196T207 184Q207 158 216 141T242 113T280 99T324 95Q367 95 404 106T479 137L511 43Q460 16 410 3T302 -11Q251 -11 209 1T137 40T90 105T73 198Q73 264 93 321T151 422T243 489T366 514Q464 514 517 457T570 303ZM445 295Q446 301 446 307T447 321Q447 366 423 390T354 414Q298 414 265 382T223 295H445ZM559 662L377 565L221 662L284 737L389 668L516 737L559 662Z" />
+<glyph unicode="&#x11C;" horiz-adv-x="732" d="M402 100Q483 100 525 139T575 258H417L433 375H728L718 303Q707 228 683 170T618 72T519 11T381 -10Q316 -10 264 8T175 60T117 146T97 263Q97 348 123 424T198 556T317 645T477 678Q548 678 605 654T709 575L608 482Q580 514 546 534T466 555Q404 555 362 530T295 465T260 372T249 266Q249 188 284 144T402 100ZM591 701L477 775L339 701L294 780L489 878L659 780L591 701Z" />
+<glyph unicode="&#x11D;" horiz-adv-x="590" d="M525 -13Q515 -84 493 -126T439 -190T367 -219T279 -226Q205 -226 152 -213T50 -170L105 -66Q145 -92 185 -105T276 -119Q326 -119 350 -101T382 -29L396 71H392Q376 51 361 38T330 18T296 7T255 3Q207 3 173 18T116 59T84 121T73 200Q73 252 87 307T131 409T209 484T324 513Q372 513 403 500T455 444H459L475 502H597L525 -13ZM431 312Q438 361 416 383T346 405Q308 405 284 388T245 343T224 282T218 215Q218 193 222 174T236 141T262 118T305 109Q334 109 354 117T387 141T408 177T419 225L431 312ZM496 561L382 635L244 561L199 640L394 738L564 640L496 561Z" />
+<glyph unicode="&#x11E;" horiz-adv-x="732" d="M402 100Q483 100 525 139T575 258H417L433 375H728L718 303Q707 228 683 170T618 72T519 11T381 -10Q316 -10 264 8T175 60T117 146T97 263Q97 348 123 424T198 556T317 645T477 678Q548 678 605 654T709 575L608 482Q580 514 546 534T466 555Q404 555 362 530T295 465T260 372T249 266Q249 188 284 144T402 100ZM673 855Q662 810 639 783T588 740T531 719T478 714Q454 714 427 719T377 739T340 782T327 855H424Q423 829 437 808T489 787Q525 787 547 808T575 855H673H673Z" />
+<glyph unicode="&#x11F;" horiz-adv-x="590" d="M525 -13Q515 -84 493 -126T439 -190T367 -219T279 -226Q205 -226 152 -213T50 -170L105 -66Q145 -92 185 -105T276 -119Q326 -119 350 -101T382 -29L396 71H392Q376 51 361 38T330 18T296 7T255 3Q207 3 173 18T116 59T84 121T73 200Q73 252 87 307T131 409T209 484T324 513Q372 513 403 500T455 444H459L475 502H597L525 -13ZM431 312Q438 361 416 383T346 405Q308 405 284 388T245 343T224 282T218 215Q218 193 222 174T236 141T262 118T305 109Q334 109 354 117T387 141T408 177T419 225L431 312ZM572 706Q561 661 538 634T487 591T430 570T377 565Q353 565 326 570T276 590T239 633T226 706H323Q322 680 336 659T388 638Q424 638 446 659T474 706H572H572Z" />
+<glyph unicode="&#x120;" horiz-adv-x="732" d="M402 100Q483 100 525 139T575 258H417L433 375H728L718 303Q707 228 683 170T618 72T519 11T381 -10Q316 -10 264 8T175 60T117 146T97 263Q97 348 123 424T198 556T317 645T477 678Q548 678 605 654T709 575L608 482Q580 514 546 534T466 555Q404 555 362 530T295 465T260 372T249 266Q249 188 284 144T402 100ZM572 797Q572 758 543 737T478 716Q464 716 451 719T427 730T410 748T403 775Q403 795 411 810T432 835T463 850T498 855Q526 855 549 842T572 797Z" />
+<glyph unicode="&#x121;" horiz-adv-x="590" d="M525 -13Q515 -84 493 -126T439 -190T367 -219T279 -226Q205 -226 152 -213T50 -170L105 -66Q145 -92 185 -105T276 -119Q326 -119 350 -101T382 -29L396 71H392Q376 51 361 38T330 18T296 7T255 3Q207 3 173 18T116 59T84 121T73 200Q73 252 87 307T131 409T209 484T324 513Q372 513 403 500T455 444H459L475 502H597L525 -13ZM431 312Q438 361 416 383T346 405Q308 405 284 388T245 343T224 282T218 215Q218 193 222 174T236 141T262 118T305 109Q334 109 354 117T387 141T408 177T419 225L431 312ZM470 650Q470 611 441 590T376 569Q362 569 349 572T325 583T308 601T301 628Q301 648 309 663T330 688T361 703T396 708Q424 708 447 695T470 650Z" />
+<glyph unicode="&#x122;" horiz-adv-x="732" d="M402 100Q483 100 525 139T575 258H417L433 375H728L718 303Q707 228 683 170T618 72T519 11T381 -10Q316 -10 264 8T175 60T117 146T97 263Q97 348 123 424T198 556T317 645T477 678Q548 678 605 654T709 575L608 482Q580 514 546 534T466 555Q404 555 362 530T295 465T260 372T249 266Q249 188 284 144T402 100ZM353 -226H257L314 -55H463L353 -226Z" />
+<glyph unicode="&#x123;" horiz-adv-x="590" d="M525 -13Q515 -84 493 -126T439 -190T367 -219T279 -226Q205 -226 152 -213T50 -170L105 -66Q145 -92 185 -105T276 -119Q326 -119 350 -101T382 -29L396 71H392Q376 51 361 38T330 18T296 7T255 3Q207 3 173 18T116 59T84 121T73 200Q73 252 87 307T131 409T209 484T324 513Q372 513 403 500T455 444H459L475 502H597L525 -13ZM431 312Q438 361 416 383T346 405Q308 405 284 388T245 343T224 282T218 215Q218 193 222 174T236 141T262 118T305 109Q334 109 354 117T387 141T408 177T419 225L431 312ZM413 730H502L448 568H301L413 730Z" />
+<glyph unicode="&#x124;" horiz-adv-x="694" d="M475 0L514 283H258L219 0H73L166 667H312L278 420H534L568 667H714L621 0H475ZM577 701L463 775L325 701L280 780L475 878L645 780L577 701Z" />
+<glyph unicode="&#x125;" horiz-adv-x="593" d="M389 0L428 281Q430 294 431 305T432 327Q432 359 417 377T358 395Q259 395 246 300L204 0H63L161 700H302L267 453H271Q300 487 337 500T419 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389ZM360 709L246 783L108 709L63 788L258 886L428 788L360 709Z" />
+<glyph unicode="&#x126;" horiz-adv-x="694" d="M475 0L514 283H258L219 0H73L139 476H57L73 585H155L166 667H312L301 585H557L568 667H714L703 585H785L769 476H687L621 0H475ZM531 401L541 476H285L275 401H531Z" />
+<glyph unicode="&#x127;" horiz-adv-x="593" d="M280 547L264 432H268Q298 466 334 479T416 493Q502 493 537 452T573 340Q573 326 572 312T569 282L530 0H388L424 260Q426 273 427 285T428 308Q428 339 414 356T355 374Q256 374 243 280L204 0H63L139 547H77L90 637H152L161 700H302L293 637H453L440 547H280Z" />
+<glyph unicode="&#x128;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM444 795Q423 750 388 729T318 707Q295 707 278 713T245 728T216 743T187 750Q170 750 152 741T120 706L62 759Q91 809 121 827T187 846Q208 846 225 839T258 824T288 809T319 802Q336 802 353 811T384 846L444 795H444Z" />
+<glyph unicode="&#x129;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM413 655Q392 610 357 589T287 567Q264 567 247 573T214 588T185 603T156 610Q139 610 121 601T89 566L31 619Q60 669 90 687T156 706Q177 706 194 699T227 684T257 669T288 662Q305 662 322 671T353 706L413 655H413Z" />
+<glyph unicode="&#x12A;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM87 727L99 814H422L410 727H87Z" />
+<glyph unicode="&#x12B;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM52 587L64 674H387L375 587H52Z" />
+<glyph unicode="&#x12C;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM437 846Q426 801 403 774T352 731T295 710T242 705Q218 705 191 710T141 730T104 773T91 846H188Q187 820 201 799T253 778Q289 778 311 799T339 846H437H437Z" />
+<glyph unicode="&#x12D;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63ZM407 706Q396 661 373 634T322 591T265 570T212 565Q188 565 161 570T111 590T74 633T61 706H158Q157 680 171 659T223 638Q259 638 281 659T309 706H407H407Z" />
+<glyph unicode="&#x12E;" horiz-adv-x="292" d="M182 0Q168 -9 152 -21T123 -48T100 -80T91 -116Q91 -135 102 -145T133 -156Q150 -156 161 -152T187 -141L199 -219Q189 -224 175 -228T147 -235T118 -239T92 -241Q47 -241 18 -221T-11 -152Q-11 -124 -2 -102T23 -61T60 -27T104 0H73L166 667H312L219 0H182Z" />
+<glyph unicode="&#x12F;" horiz-adv-x="267" d="M172 0Q158 -9 142 -21T113 -48T90 -80T81 -116Q81 -135 92 -145T123 -156Q140 -156 151 -152T177 -141L189 -219Q167 -228 138 -234T86 -241Q42 -241 11 -221T-20 -151Q-20 -125 -10 -103T15 -61T51 -27T93 0H63L133 502H274L204 0H172ZM307 644Q307 604 278 584T213 564Q184 564 161 578T137 624Q137 643 145 658T166 683T197 698T232 703Q260 703 283 689T307 644Z" />
+<glyph unicode="&#x130;" horiz-adv-x="292" d="M73 0L166 667H312L219 0H73ZM342 797Q342 758 313 737T248 716Q234 716 221 719T197 730T180 748T173 775Q173 795 181 810T202 835T233 850T268 855Q296 855 319 842T342 797Z" />
+<glyph unicode="&#x131;" horiz-adv-x="267" d="M63 0L133 502H274L204 0H63Z" />
+<glyph unicode="&#x132;" horiz-adv-x="731" d="M73 0L166 667H312L219 0H73ZM690 228Q682 176 669 132T629 56T561 5T457 -13Q400 -13 359 2T279 53L359 160Q385 131 407 121T460 111Q492 111 512 130T539 197L605 667H751L690 228Z" />
+<glyph unicode="&#x133;" horiz-adv-x="538" d="M65 0L135 502H276L206 0H65ZM309 643Q309 623 301 609T280 584T249 569T214 564Q185 564 162 578T138 624Q138 643 146 658T167 683T198 698T233 703Q261 703 285 689T309 643ZM577 644Q577 624 569 609T548 584T518 569T482 564Q468 564 455 567T430 578T413 596T406 623Q406 643 414 658T435 683T466 698T501 703Q529 703 553 690T577 644ZM472 -17Q458 -120 401 -173T238 -226Q217 -226 197 -225T155 -221L172 -103Q205 -106 238 -106Q267 -106 284 -99T312 -79T326 -46T334 0L404 502H545L472 -17Z" />
+<glyph unicode="&#x134;" horiz-adv-x="490" d="M449 228Q441 176 428 132T388 56T320 5T216 -13Q159 -13 118 2T38 53L118 160Q144 131 166 121T219 111Q251 111 271 130T298 197L364 667H510L449 228H449ZM566 701L452 775L314 701L269 780L464 878L634 780L566 701Z" />
+<glyph unicode="&#x135;" horiz-adv-x="274" d="M208 -17Q178 -226 -26 -226Q-48 -226 -68 -225T-109 -221L-92 -103Q-59 -106 -26 -106Q2 -106 19 -100T47 -80T61 -47T70 0L140 502H281L208 -17ZM347 561L233 635L95 561L50 640L245 738L415 640L347 561Z" />
+<glyph unicode="&#x136;" horiz-adv-x="644" d="M451 0L309 279L250 223L219 0H73L166 667H312L273 389H277L521 667H705L432 376L637 0H451ZM291 -226H195L252 -55H401L291 -226Z" />
+<glyph unicode="&#x137;" horiz-adv-x="555" d="M380 0L282 214L226 162L204 0H63L161 700H302L248 314H252L433 502H608L396 301L551 0H380ZM251 -226H155L212 -55H361L251 -226Z" />
+<glyph unicode="&#x139;" horiz-adv-x="543" d="M73 0L166 667H312L236 123H519L502 0H73ZM248 705L170 750L328 882L460 823L248 705Z" />
+<glyph unicode="&#x13A;" horiz-adv-x="316" d="M288 0Q249 -6 185 -6Q125 -6 100 12T75 75Q75 89 78 109L161 700H302L223 142Q220 119 227 114T254 109H303L288 0H288ZM241 735L163 780L321 912L453 853L241 735Z" />
+<glyph unicode="&#x13B;" horiz-adv-x="543" d="M73 0L166 667H312L236 123H519L502 0H73ZM239 -226H143L200 -55H349L239 -226Z" />
+<glyph unicode="&#x13C;" horiz-adv-x="316" d="M288 0Q249 -6 185 -6Q125 -6 100 12T75 75Q75 89 78 109L161 700H302L223 142Q220 119 227 114T254 109H303L288 0H288ZM171 -226H75L132 -55H281L171 -226Z" />
+<glyph unicode="&#x13D;" horiz-adv-x="543" d="M73 0L166 667H312L236 123H519L502 0H73ZM534 518H440L503 700H641L534 518Z" />
+<glyph unicode="&#x13E;" horiz-adv-x="316" d="M288 0Q249 -6 185 -6Q125 -6 100 12T75 75Q75 89 78 109L161 700H302L223 142Q220 119 227 114T254 109H303L288 0ZM424 518H330L393 700H531L424 518Z" />
+<glyph unicode="&#x13F;" horiz-adv-x="543" d="M73 0L166 667H312L236 123H519L502 0H73ZM571 330Q571 311 561 296T536 271T502 256T467 251Q453 251 438 254T411 266T392 285T384 314Q384 333 393 348T418 373T451 389T487 394Q501 394 516 390T543 379T563 359T571 330Z" />
+<glyph unicode="&#x140;" horiz-adv-x="443" d="M288 0Q249 -6 185 -6Q125 -6 100 12T75 75Q75 89 78 109L161 700H302L223 142Q220 119 227 114T254 109H303L288 0ZM485 249Q485 230 475 215T450 190T416 175T381 170Q367 170 352 173T325 185T306 204T298 233Q298 252 307 267T332 292T365 308T401 313Q415 313 430 309T457 298T477 278T485 249Z" />
+<glyph unicode="&#x141;" horiz-adv-x="543" d="M74 0L111 269L40 228L55 341L127 383L167 667H313L286 474L478 585L461 466L269 354L236 123H519L503 0H74Z" />
+<glyph unicode="&#x142;" horiz-adv-x="316" d="M290 366L258 142Q255 119 261 114T289 109H338L322 -2Q302 -5 279 -7T219 -9Q159 -9 135 10T110 73Q110 89 113 109L137 282L75 244L91 357L153 396L196 700H337L305 475L409 537L394 428L290 366Z" />
+<glyph unicode="&#x143;" horiz-adv-x="731" d="M532 0L278 425H274L215 0H73L166 667H301L546 249H551L610 667H751L658 0H532ZM469 705L391 750L549 882L681 823L469 705Z" />
+<glyph unicode="&#x144;" horiz-adv-x="593" d="M389 0L428 281Q430 293 431 303T432 324Q432 356 417 375T357 395Q259 395 246 300L204 0H63L133 502H254L256 442H260Q293 482 330 497T418 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389ZM386 565L308 610L466 742L598 683L386 565Z" />
+<glyph unicode="&#x145;" horiz-adv-x="731" d="M532 0L278 425H274L215 0H73L166 667H301L546 249H551L610 667H751L658 0H532ZM326 -226H230L287 -55H436L326 -226Z" />
+<glyph unicode="&#x146;" horiz-adv-x="593" d="M389 0L428 281Q430 293 431 303T432 324Q432 356 417 375T357 395Q259 395 246 300L204 0H63L133 502H254L256 442H260Q293 482 330 497T418 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389ZM261 -226H165L222 -55H371L261 -226Z" />
+<glyph unicode="&#x147;" horiz-adv-x="731" d="M532 0L278 425H274L215 0H73L166 667H301L546 249H551L610 667H751L658 0H532ZM652 802L470 705L314 802L377 877L482 808L609 877L652 802Z" />
+<glyph unicode="&#x148;" horiz-adv-x="593" d="M389 0L428 281Q430 293 431 303T432 324Q432 356 417 375T357 395Q259 395 246 300L204 0H63L133 502H254L256 442H260Q293 482 330 497T418 513Q505 513 540 473T576 361Q576 347 575 333T572 303L530 0H389ZM566 662L384 565L228 662L291 737L396 668L523 737L566 662Z" />
+<glyph unicode="&#x14A;" horiz-adv-x="731" d="M404 -121H418Q456 -121 481 -100T513 -33L519 9L279 430H275L215 0H73L166 667H301L546 242H551L610 667H751L653 -29Q638 -136 581 -191T420 -246H387L404 -121Z" />
+<glyph unicode="&#x14B;" horiz-adv-x="593" d="M254 502L256 442H260Q293 482 330 497T418 513Q505 513 540 473T576 361Q576 347 575 333T572 303L527 -17Q512 -127 454 -182T289 -238H255L273 -108H287Q372 -108 385 -16L427 281Q429 293 430 304T431 326Q431 358 416 376T357 395Q259 395 246 300L204 0H63L133 502H254Z" />
+<glyph unicode="&#x14C;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM330 727L342 814H665L653 727H330Z" />
+<glyph unicode="&#x14D;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM222 587L234 674H557L545 587H222Z" />
+<glyph unicode="&#x14E;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM681 855Q670 810 647 783T596 740T539 719T486 714Q462 714 435 719T385 739T348 782T335 855H432Q431 829 445 808T497 787Q533 787 555 808T583 855H681H681Z" />
+<glyph unicode="&#x14F;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM574 706Q563 661 540 634T489 591T432 570T379 565Q355 565 328 570T278 590T241 633T228 706H325Q324 680 338 659T390 638Q426 638 448 659T476 706H574H574Z" />
+<glyph unicode="&#x150;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q318 -10 265 6T174 55T116 139T96 259Q96 349 121 425T195 558T316 646T481 678Q547 678 600 662T690 613T748 530T768 411ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 485 580 522T464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263Q248 184 284 146T401 108ZM642 702L576 746L742 877L855 820L642 702ZM374 702L309 746L475 877L588 820L374 702Z" />
+<glyph unicode="&#x151;" horiz-adv-x="601" d="M73 197Q73 266 93 324T151 424T245 489T372 513Q423 513 464 501T536 463T582 398T598 305Q598 236 578 178T519 78T425 13T298 -11Q196 -11 135 40T73 197ZM357 403Q314 403 287 385T244 337T222 272T216 200Q216 149 238 122T314 95Q356 95 383 114T426 162T448 228T455 300Q455 351 432 377T357 403ZM541 555L475 599L641 730L754 673L541 555ZM273 555L208 599L374 730L487 673L273 555Z" />
+<glyph unicode="&#x152;" horiz-adv-x="957" d="M491 0Q465 -5 435 -7T376 -10Q311 -10 259 6T171 54T116 137T96 255Q96 337 117 413T184 549T301 643T470 678Q499 678 529 675T584 667H1003L986 545H713L693 404H937L920 280H676L653 119H936L920 0H491ZM399 108Q434 108 461 113T508 126L566 540Q549 549 522 554T459 560Q396 560 356 534T291 466T258 370T248 261Q248 187 282 148T399 108Z" />
+<glyph unicode="&#x153;" horiz-adv-x="956" d="M953 298Q953 274 950 251T942 204H586L585 191Q585 164 594 146T619 117T656 102T701 97Q743 97 781 108T855 138L888 43Q838 17 787 3T680 -11Q628 -11 581 6T501 65Q460 24 408 7T299 -11Q248 -11 207 1T135 39T89 104T73 196Q73 266 93 324T151 424T245 489T373 513Q423 513 471 497T550 438Q589 476 639 495T744 514Q794 514 833 499T898 455T939 387T953 298ZM829 295Q829 350 807 380T731 411Q676 411 642 380T599 295H829ZM314 98Q356 98 383 116T426 164T448 229T454 301Q454 352 432 376T357 400Q314 400 287 383T244 337T222 273T216 201Q216 150 238 124T314 98Z" />
+<glyph unicode="&#x154;" horiz-adv-x="645" d="M458 0L396 228Q391 243 385 248T363 253H254L219 0H73L166 663Q281 675 398 675Q446 675 492 669T575 644T634 592T657 507Q657 472 649 438T623 376T578 325T515 292V288Q523 284 527 274T535 255L611 0H458ZM383 367Q410 367 433 372T474 391T502 427T512 482Q512 507 502 523T474 547T435 558T388 561Q364 561 342 560T297 556L270 367H383ZM388 708L310 753L468 885L600 826L388 708Z" />
+<glyph unicode="&#x155;" horiz-adv-x="404" d="M399 388Q368 388 342 385T296 372T262 341T244 287L204 0H63L133 502H253L256 435H260Q286 473 320 492T414 512H449L432 388H399ZM293 565L215 610L373 742L505 683L293 565Z" />
+<glyph unicode="&#x156;" horiz-adv-x="645" d="M458 0L396 228Q391 243 385 248T363 253H254L219 0H73L166 663Q281 675 398 675Q446 675 492 669T575 644T634 592T657 507Q657 472 649 438T623 376T578 325T515 292V288Q523 284 527 274T535 255L611 0H458ZM383 367Q410 367 433 372T474 391T502 427T512 482Q512 507 502 523T474 547T435 558T388 561Q364 561 342 560T297 556L270 367H383ZM286 -226H190L247 -55H396L286 -226Z" />
+<glyph unicode="&#x157;" horiz-adv-x="404" d="M399 388Q368 388 342 385T296 372T262 341T244 287L204 0H63L133 502H253L256 435H260Q286 473 320 492T414 512H449L432 388H399ZM89 -226H-7L50 -55H199L89 -226Z" />
+<glyph unicode="&#x158;" horiz-adv-x="645" d="M458 0L396 228Q391 243 385 248T363 253H254L219 0H73L166 663Q281 675 398 675Q446 675 492 669T575 644T634 592T657 507Q657 472 649 438T623 376T578 325T515 292V288Q523 284 527 274T535 255L611 0H458ZM383 367Q410 367 433 372T474 391T502 427T512 482Q512 507 502 523T474 547T435 558T388 561Q364 561 342 560T297 556L270 367H383ZM590 802L408 705L252 802L315 877L420 808L547 877L590 802Z" />
+<glyph unicode="&#x159;" horiz-adv-x="404" d="M399 388Q368 388 342 385T296 372T262 341T244 287L204 0H63L133 502H253L256 435H260Q286 473 320 492T414 512H449L432 388H399ZM474 662L292 565L136 662L199 737L304 668L431 737L474 662Z" />
+<glyph unicode="&#x15A;" horiz-adv-x="601" d="M582 216Q582 153 560 110T499 41T411 2T305 -10Q230 -10 167 8T42 71L121 175Q163 144 209 124T309 104Q330 104 352 106T393 117T424 141T436 181Q436 210 413 228T354 260T278 290T201 327T143 381T119 465Q119 524 142 564T203 629T289 665T390 676Q510 676 616 617L548 505Q513 528 472 543T387 559Q368 559 347 557T309 547T280 527T268 492Q268 464 291 447T349 417T425 391T500 356T558 302T582 216ZM395 709L317 754L475 886L607 827L395 709Z" />
+<glyph unicode="&#x15B;" horiz-adv-x="492" d="M480 167Q480 116 461 82T411 27T338 -2T252 -11Q195 -11 138 4T33 50L95 146Q133 123 174 108T261 92Q275 92 290 94T317 101T337 117T345 146Q345 164 327 174T280 193T219 211T158 237T111 279T92 347Q92 393 111 425T162 476T234 504T317 513Q365 513 414 503T505 470L454 370Q425 387 389 397T318 408Q306 408 290 407T259 402T235 388T225 362Q225 344 244 335T291 317T352 300T414 276T461 234T480 167ZM333 565L255 610L413 742L545 683L333 565Z" />
+<glyph unicode="&#x15C;" horiz-adv-x="601" d="M582 216Q582 153 560 110T499 41T411 2T305 -10Q230 -10 167 8T42 71L121 175Q163 144 209 124T309 104Q330 104 352 106T393 117T424 141T436 181Q436 210 413 228T354 260T278 290T201 327T143 381T119 465Q119 524 142 564T203 629T289 665T390 676Q510 676 616 617L548 505Q513 528 472 543T387 559Q368 559 347 557T309 547T280 527T268 492Q268 464 291 447T349 417T425 391T500 356T558 302T582 216ZM529 701L415 775L277 701L232 780L427 878L597 780L529 701Z" />
+<glyph unicode="&#x15D;" horiz-adv-x="492" d="M480 167Q480 116 461 82T411 27T338 -2T252 -11Q195 -11 138 4T33 50L95 146Q133 123 174 108T261 92Q275 92 290 94T317 101T337 117T345 146Q345 164 327 174T280 193T219 211T158 237T111 279T92 347Q92 393 111 425T162 476T234 504T317 513Q365 513 414 503T505 470L454 370Q425 387 389 397T318 408Q306 408 290 407T259 402T235 388T225 362Q225 344 244 335T291 317T352 300T414 276T461 234T480 167ZM467 561L353 635L215 561L170 640L365 738L535 640L467 561Z" />
+<glyph unicode="&#x15E;" horiz-adv-x="601" d="M397 -111Q397 -138 388 -157T362 -190T323 -209T277 -216Q254 -216 230 -209T186 -191L209 -143Q225 -151 239 -156T272 -162Q292 -162 308 -154T324 -123Q324 -106 312 -101T286 -95Q261 -95 244 -103L219 -89L270 -9Q206 -5 151 13T42 71L121 175Q163 144 209 124T309 104Q330 104 352 106T393 117T424 141T436 181Q436 210 413 228T354 260T278 290T201 327T143 381T119 465Q119 524 142 564T203 629T289 665T390 676Q510 676 616 617L548 505Q513 528 472 543T387 559Q368 559 347 557T309 547T280 527T268 492Q268 464 291 447T349 417T425 391T500 356T558 302T582 216Q582 157 563 116T509 48T430 8T333 -9L301 -56Q319 -50 335 -50Q361 -50 379 -65T397 -111Z" />
+<glyph unicode="&#x15F;" horiz-adv-x="492" d="M336 -112Q336 -139 327 -158T301 -191T262 -210T216 -217Q193 -217 169 -210T125 -192L148 -144Q164 -152 178 -157T211 -163Q231 -163 247 -155T263 -124Q263 -107 251 -102T225 -96Q200 -96 183 -104L158 -90L210 -9Q162 -4 117 10T33 50L95 146Q133 123 174 108T261 92Q275 92 290 94T317 101T337 117T345 146Q345 164 327 174T280 193T219 211T158 237T111 279T92 347Q92 393 111 425T162 476T234 504T317 513Q365 513 414 503T505 470L454 370Q425 387 389 397T318 408Q306 408 290 407T259 402T235 388T225 362Q225 344 244 335T291 317T352 300T414 276T461 234T480 167Q480 119 463 86T418 32T352 1T272 -10L240 -57Q258 -51 274 -51Q300 -51 318 -66T336 -112Z" />
+<glyph unicode="&#x160;" horiz-adv-x="601" d="M582 216Q582 153 560 110T499 41T411 2T305 -10Q230 -10 167 8T42 71L121 175Q163 144 209 124T309 104Q330 104 352 106T393 117T424 141T436 181Q436 210 413 228T354 260T278 290T201 327T143 381T119 465Q119 524 142 564T203 629T289 665T390 676Q510 676 616 617L548 505Q513 528 472 543T387 559Q368 559 347 557T309 547T280 527T268 492Q268 464 291 447T349 417T425 391T500 356T558 302T582 216ZM586 802L404 705L248 802L311 877L416 808L543 877L586 802Z" />
+<glyph unicode="&#x161;" horiz-adv-x="492" d="M480 167Q480 116 461 82T411 27T338 -2T252 -11Q195 -11 138 4T33 50L95 146Q133 123 174 108T261 92Q275 92 290 94T317 101T337 117T345 146Q345 164 327 174T280 193T219 211T158 237T111 279T92 347Q92 393 111 425T162 476T234 504T317 513Q365 513 414 503T505 470L454 370Q425 387 389 397T318 408Q306 408 290 407T259 402T235 388T225 362Q225 344 244 335T291 317T352 300T414 276T461 234T480 167ZM523 662L341 565L185 662L248 737L353 668L480 737L523 662Z" />
+<glyph unicode="&#x162;" horiz-adv-x="549" d="M423 543L347 0H201L277 543H90L107 667H628L611 543H423ZM221 -228H125L182 -57H331L221 -228Z" />
+<glyph unicode="&#x163;" horiz-adv-x="391" d="M352 -2Q323 -6 303 -7T264 -9Q222 -9 193 -4T146 14T120 46T112 94Q112 105 113 118T116 145L151 389H69L85 502H168L187 635H326L307 502H423L407 389H291L260 164Q257 146 257 135T266 117T291 109T342 107Q350 107 354 107T368 108L352 -2H352ZM189 -226H93L150 -55H299L189 -226Z" />
+<glyph unicode="&#x164;" horiz-adv-x="549" d="M423 543L347 0H201L277 543H90L107 667H628L611 543H423ZM558 802L376 705L220 802L283 877L388 808L515 877L558 802Z" />
+<glyph unicode="&#x165;" horiz-adv-x="391" d="M352 -2Q323 -6 303 -7T264 -9Q222 -9 193 -4T146 14T120 46T112 94Q112 105 113 118T116 145L151 389H69L85 502H168L187 635H326L307 502H423L407 389H291L260 164Q257 146 257 135T266 117T291 109T342 107Q350 107 354 107T368 108L352 -2ZM485 518H391L454 700H592L485 518Z" />
+<glyph unicode="&#x166;" horiz-adv-x="549" d="M387 289L347 0H201L241 289H83L99 402H257L277 543H90L107 667H628L611 543H423L403 402H561L545 289H387Z" />
+<glyph unicode="&#x167;" horiz-adv-x="391" d="M267 217L260 164Q257 146 257 135T266 117T291 109T342 107H368L352 -2Q323 -6 303 -7T264 -9Q221 -9 192 -4T145 14T120 46T112 95Q112 106 113 118T116 145L126 217H45L60 323H142L151 389H69L85 502H168L187 635H326L307 502H423L407 389H291L282 323H398L383 217H267Z" />
+<glyph unicode="&#x168;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM647 795Q626 750 591 729T521 707Q498 707 481 713T448 728T419 743T390 750Q373 750 355 741T323 706L265 759Q294 809 324 827T390 846Q411 846 428 839T461 824T491 809T522 802Q539 802 556 811T587 846L647 795H647Z" />
+<glyph unicode="&#x169;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM571 655Q550 610 515 589T445 567Q422 567 405 573T372 588T343 603T314 610Q297 610 279 601T247 566L189 619Q218 669 248 687T314 706Q335 706 352 699T385 684T415 669T446 662Q463 662 480 671T511 706L571 655H571Z" />
+<glyph unicode="&#x16A;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM291 727L303 814H626L614 727H291Z" />
+<glyph unicode="&#x16B;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM211 587L223 674H546L534 587H211Z" />
+<glyph unicode="&#x16C;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM643 846Q632 801 609 774T558 731T501 710T448 705Q424 705 397 710T347 730T310 773T297 846H394Q393 820 407 799T459 778Q495 778 517 799T545 846H643H643Z" />
+<glyph unicode="&#x16D;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM566 706Q555 661 532 634T481 591T424 570T371 565Q347 565 320 570T270 590T233 633T220 706H317Q316 680 330 659T382 638Q418 638 440 659T468 706H566H566Z" />
+<glyph unicode="&#x16E;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM574 812Q574 786 564 764T538 727T498 702T450 693Q409 693 379 716T349 782Q349 807 359 829T385 866T424 891T472 900Q492 900 510 895T543 878T565 850T574 812ZM459 744Q485 744 499 762T513 806Q513 826 501 837T466 848Q441 848 426 831T410 788Q410 768 423 756T459 744Z" />
+<glyph unicode="&#x16F;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM498 684Q498 658 488 636T462 599T422 574T374 565Q333 565 303 588T273 654Q273 679 283 701T309 738T348 763T396 772Q416 772 434 767T467 750T489 722T498 684ZM383 616Q409 616 423 634T437 678Q437 698 425 709T390 720Q365 720 350 703T334 660Q334 640 347 628T383 616Z" />
+<glyph unicode="&#x170;" horiz-adv-x="701" d="M665 268Q656 202 634 150T574 62T481 8T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665ZM597 706L531 750L697 881L810 824L597 706ZM329 706L264 750L430 881L543 824L329 706Z" />
+<glyph unicode="&#x171;" horiz-adv-x="584" d="M398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H398ZM523 566L457 610L623 741L736 684L523 566ZM255 566L190 610L356 741L469 684L255 566Z" />
+<glyph unicode="&#x172;" horiz-adv-x="701" d="M665 268Q651 168 608 102T480 8Q467 -1 452 -13T423 -39T400 -70T391 -107Q391 -127 403 -137T433 -148Q453 -148 465 -143T487 -133L499 -211Q484 -217 454 -224T396 -232Q341 -232 315 -208T289 -143Q289 -98 312 -68T372 -10Q365 -11 360 -11T348 -11Q282 -11 236 3T161 43T118 108T105 197Q105 214 106 231T110 268L166 667H312L259 290Q253 251 253 217Q253 193 258 174T275 141T309 120T365 112Q411 112 439 125T484 163T509 219T522 290L575 667H721L665 268H665Z" />
+<glyph unicode="&#x173;" horiz-adv-x="584" d="M478 0Q464 -10 449 -22T420 -48T397 -79T388 -115Q388 -134 399 -145T430 -156Q447 -156 458 -152T484 -141L496 -219Q474 -228 445 -234T393 -241Q348 -241 317 -220T286 -150Q286 -125 295 -103T320 -62T356 -28T398 0V58H394Q367 25 330 7T240 -12Q204 -12 175 -6T126 17T96 61T85 130Q85 143 86 157T89 188L133 502H274L236 234Q234 217 232 202T230 174Q230 142 244 124T302 106Q330 106 349 113T381 132T399 164T409 207L450 502H591L521 0H478Z" />
+<glyph unicode="&#x174;" horiz-adv-x="1005" d="M788 0H632L569 473H564L367 0H204L114 667H275L315 177H319L524 667H672L732 177H736L922 667H1077L788 0ZM725 701L611 775L473 701L428 780L623 878L793 780L725 701Z" />
+<glyph unicode="&#x175;" horiz-adv-x="796" d="M627 0H493L439 298H435L303 0H166L86 502H233L263 150H267L412 502H522L571 149H575L708 502H850L627 0ZM601 561L487 635L349 561L304 640L499 738L669 640L601 561Z" />
+<glyph unicode="&#x176;" horiz-adv-x="646" d="M433 262L397 0H251L287 259L95 667H263L379 390L576 667H737L433 262ZM550 701L436 775L298 701L253 780L448 878L618 780L550 701Z" />
+<glyph unicode="&#x177;" horiz-adv-x="549" d="M332 -21Q299 -83 273 -123T220 -186T161 -217T83 -226Q69 -226 51 -225T8 -221L25 -104Q32 -106 44 -106T80 -107Q100 -107 113 -104T136 -94T154 -76T172 -47L204 11L77 502H230L295 154H299L461 502H612L332 -21ZM482 561L368 635L230 561L185 640L380 738L550 640L482 561Z" />
+<glyph unicode="&#x178;" horiz-adv-x="646" d="M433 262L397 0H251L287 259L95 667H263L379 390L576 667H737L433 262ZM616 789Q616 773 610 759T592 733T567 716T536 709Q510 709 493 726T475 770Q475 786 482 800T500 824T526 841T556 848Q581 848 598 832T616 789ZM393 789Q393 773 387 759T369 733T344 716T313 709Q287 709 270 726T253 769Q253 785 259 799T277 824T303 841T333 848Q358 848 375 832T393 789Z" />
+<glyph unicode="&#x179;" horiz-adv-x="607" d="M43 0L58 107L443 543H135L152 667H641L626 560L236 119H580L564 0H43ZM396 705L318 750L476 882L608 823L396 705Z" />
+<glyph unicode="&#x17A;" horiz-adv-x="482" d="M34 0L47 95L321 389H91L107 502H509L495 401L225 112H463L448 0H34ZM311 565L233 610L391 742L523 683L311 565Z" />
+<glyph unicode="&#x17B;" horiz-adv-x="607" d="M43 0L58 107L443 543H135L152 667H641L626 560L236 119H580L564 0H43ZM507 797Q507 758 478 737T413 716Q399 716 386 719T362 730T345 748T338 775Q338 795 346 810T367 835T398 850T433 855Q461 855 484 842T507 797Z" />
+<glyph unicode="&#x17C;" horiz-adv-x="482" d="M34 0L47 95L321 389H91L107 502H509L495 401L225 112H463L448 0H34ZM425 650Q425 611 396 590T331 569Q317 569 304 572T280 583T263 601T256 628Q256 648 264 663T285 688T316 703T351 708Q379 708 402 695T425 650Z" />
+<glyph unicode="&#x17D;" horiz-adv-x="607" d="M43 0L58 107L443 543H135L152 667H641L626 560L236 119H580L564 0H43ZM592 802L410 705L254 802L317 877L422 808L549 877L592 802Z" />
+<glyph unicode="&#x17E;" horiz-adv-x="482" d="M34 0L47 95L321 389H91L107 502H509L495 401L225 112H463L448 0H34ZM517 662L335 565L179 662L242 737L347 668L474 737L517 662Z" />
+<glyph unicode="&#x192;" horiz-adv-x="613" d="M636 591Q624 592 617 593T594 594Q550 594 532 579T503 535L472 448H580L544 334H438L324 -7Q292 -100 241 -147T103 -195Q80 -195 57 -192T16 -182L48 -80Q62 -84 74 -85T106 -86Q143 -86 164 -51T204 43L297 334H220L256 448H333L367 553Q380 594 398 621T443 664T509 688T604 695Q621 695 634 695T668 694L636 591Z" />
+<glyph unicode="&#x1FC;" horiz-adv-x="939" d="M454 0L475 151H255L165 0H14L448 667H985L969 548H674L654 406H920L902 283H636L613 119H918L902 0H454ZM530 547H507L328 273H492L530 547ZM704 705L626 750L784 882L916 823L704 705Z" />
+<glyph unicode="&#x1FD;" horiz-adv-x="891" d="M888 298Q888 274 885 250T876 203H521L520 190Q520 164 529 147T554 118T591 102T635 97Q677 97 715 107T790 138L823 43Q773 17 722 3T615 -11Q561 -11 513 5T431 59Q415 45 390 32T336 9T273 -7T209 -13Q178 -13 150 -7T100 15T66 54T53 114Q53 183 86 224T171 287T282 316T395 323Q397 334 398 344T399 365Q399 380 393 388T378 400T357 405T333 406Q286 406 236 392T143 355L111 453Q172 482 232 497T360 513Q379 513 400 511T442 503T479 486T508 457Q544 486 588 500T678 514Q779 514 833 456T888 298ZM763 295Q763 411 665 411Q610 411 576 380T533 295H763ZM390 130Q385 144 383 160T380 190Q380 203 381 216T385 242Q358 242 324 240T261 227T212 195T192 137Q192 109 209 93T253 77Q296 77 330 93T390 130ZM545 565L467 610L625 742L757 683L545 565Z" />
+<glyph unicode="&#x1FE;" horiz-adv-x="772" d="M768 411Q768 321 744 244T671 110T550 22T384 -10Q327 -10 280 1T196 38L127 -26L64 59L126 117Q96 174 96 259Q96 349 121 425T195 558T316 646T481 678Q541 678 590 665T677 623L749 690L819 612L743 541Q768 485 768 411ZM248 263Q248 254 248 246T249 230L573 528Q535 560 464 560Q409 560 372 543T311 496T274 425T254 334Q248 298 248 263ZM401 108Q456 108 493 125T553 172T590 244T610 334Q616 370 616 407Q616 412 616 415T615 424L300 133Q335 108 401 108ZM472 709L394 754L552 886L684 827L472 709Z" />
+<glyph unicode="&#x1FF;" horiz-adv-x="601" d="M569 424Q586 398 592 368T598 306Q598 237 578 179T520 79T426 13T299 -11Q259 -11 219 -2T147 33L87 -23L36 41L93 95Q82 118 77 143T72 195Q72 264 92 322T151 423T246 489T373 513Q408 513 443 507T508 482L573 539L630 480L569 424ZM413 379Q402 390 387 395T357 400Q316 400 289 384T246 340T223 279T216 209Q216 203 218 193L413 379ZM315 98Q356 98 383 115T425 161T447 224T454 294Q454 309 451 324L242 129Q252 112 274 105T315 98ZM378 565L300 610L458 742L590 683L378 565Z" />
+<glyph unicode="&#x218;" horiz-adv-x="601" d="M582 216Q582 153 560 110T499 41T411 2T305 -10Q230 -10 167 8T42 71L121 175Q163 144 209 124T309 104Q330 104 352 106T393 117T424 141T436 181Q436 210 413 228T354 260T278 290T201 327T143 381T119 465Q119 524 142 564T203 629T289 665T390 676Q510 676 616 617L548 505Q513 528 472 543T387 559Q368 559 347 557T309 547T280 527T268 492Q268 464 291 447T349 417T425 391T500 356T558 302T582 216ZM257 -226H161L218 -55H367L257 -226Z" />
+<glyph unicode="&#x219;" horiz-adv-x="492" d="M480 167Q480 116 461 82T411 27T338 -2T252 -11Q195 -11 138 4T33 50L95 146Q133 123 174 108T261 92Q275 92 290 94T317 101T337 117T345 146Q345 164 327 174T280 193T219 211T158 237T111 279T92 347Q92 393 111 425T162 476T234 504T317 513Q365 513 414 503T505 470L454 370Q425 387 389 397T318 408Q306 408 290 407T259 402T235 388T225 362Q225 344 244 335T291 317T352 300T414 276T461 234T480 167ZM207 -226H111L168 -55H317L207 -226Z" />
+<glyph unicode="&#x237;" horiz-adv-x="274" d="M208 -17Q178 -226 -26 -226Q-48 -226 -68 -225T-109 -221L-92 -103Q-59 -106 -26 -106Q2 -106 19 -100T47 -80T61 -47T70 0L140 502H281L208 -17H208Z" />
+<glyph unicode="&#x2C6;" horiz-adv-x="348" d="M378 561L264 635L126 561L81 640L276 738L446 640L378 561Z" />
+<glyph unicode="&#x2C7;" horiz-adv-x="348" d="M436 662L254 565L98 662L161 737L266 668L393 737L436 662Z" />
+<glyph unicode="&#x2D8;" horiz-adv-x="348" d="M446 706Q435 661 412 634T361 591T304 570T251 565Q227 565 200 570T150 590T113 633T100 706H197Q196 680 210 659T262 638Q298 638 320 659T348 706H446H446Z" />
+<glyph unicode="&#x2D9;" horiz-adv-x="348" d="M347 650Q347 611 318 590T253 569Q239 569 226 572T202 583T185 601T178 628Q178 648 186 663T207 688T238 703T273 708Q301 708 324 695T347 650Z" />
+<glyph unicode="&#x2DA;" horiz-adv-x="348" d="M379 684Q379 658 369 636T343 599T303 574T255 565Q214 565 184 588T154 654Q154 679 164 701T190 738T229 763T277 772Q297 772 315 767T348 750T370 722T379 684ZM264 616Q290 616 304 634T318 678Q318 698 306 709T271 720Q246 720 231 703T215 660Q215 640 228 628T264 616Z" />
+<glyph unicode="&#x2DB;" horiz-adv-x="348" d="M235 1Q221 -9 205 -21T175 -47T152 -79T143 -115Q143 -134 154 -145T185 -156Q202 -156 213 -152T239 -141L251 -219Q229 -228 200 -234T148 -241Q103 -241 72 -220T41 -150Q41 -118 56 -91T94 -42T145 -4T201 22L235 1H235Z" />
+<glyph unicode="&#x2DC;" horiz-adv-x="348" d="M454 655Q433 610 398 589T328 567Q305 567 288 573T255 588T226 603T197 610Q180 610 162 601T130 566L72 619Q101 669 131 687T197 706Q218 706 235 699T268 684T298 669T329 662Q346 662 363 671T394 706L454 655H454Z" />
+<glyph unicode="&#x2DD;" horiz-adv-x="348" d="M340 566L274 610L440 741L553 684L340 566ZM72 566L7 610L173 741L286 684L72 566Z" />
+<glyph unicode="&#x1E80;" horiz-adv-x="1005" d="M788 0H632L569 473H564L367 0H204L114 667H275L315 177H319L524 667H672L732 177H736L922 667H1077L788 0ZM603 705L423 823L573 881L693 750L603 705Z" />
+<glyph unicode="&#x1E81;" horiz-adv-x="796" d="M627 0H493L439 298H435L303 0H166L86 502H233L263 150H267L412 502H522L571 149H575L708 502H850L627 0ZM479 565L299 683L449 741L569 610L479 565Z" />
+<glyph unicode="&#x1E82;" horiz-adv-x="1005" d="M788 0H632L569 473H564L367 0H204L114 667H275L315 177H319L524 667H672L732 177H736L922 667H1077L788 0ZM607 705L529 750L687 882L819 823L607 705Z" />
+<glyph unicode="&#x1E83;" horiz-adv-x="796" d="M627 0H493L439 298H435L303 0H166L86 502H233L263 150H267L412 502H522L571 149H575L708 502H850L627 0ZM477 565L399 610L557 742L689 683L477 565Z" />
+<glyph unicode="&#x1E84;" horiz-adv-x="1005" d="M788 0H632L569 473H564L367 0H204L114 667H275L315 177H319L524 667H672L732 177H736L922 667H1077L788 0ZM791 789Q791 773 785 759T767 733T742 716T711 709Q685 709 668 726T650 770Q650 786 657 800T675 824T701 841T731 848Q756 848 773 832T791 789ZM568 789Q568 773 562 759T544 733T519 716T488 709Q462 709 445 726T428 769Q428 785 434 799T452 824T478 841T508 848Q533 848 550 832T568 789Z" />
+<glyph unicode="&#x1E85;" horiz-adv-x="796" d="M627 0H493L439 298H435L303 0H166L86 502H233L263 150H267L412 502H522L571 149H575L708 502H850L627 0ZM667 649Q667 633 661 619T643 593T618 576T587 569Q561 569 544 586T526 630Q526 646 533 660T551 684T577 701T607 708Q632 708 649 692T667 649ZM444 649Q444 633 438 619T420 593T395 576T364 569Q338 569 321 586T304 629Q304 645 310 659T328 684T354 701T384 708Q409 708 426 692T444 649Z" />
+<glyph unicode="&#x1EF2;" horiz-adv-x="646" d="M433 262L397 0H251L287 259L95 667H263L379 390L576 667H737L433 262ZM420 705L240 823L390 881L510 750L420 705Z" />
+<glyph unicode="&#x1EF3;" horiz-adv-x="549" d="M332 -21Q299 -83 273 -123T220 -186T161 -217T83 -226Q69 -226 51 -225T8 -221L25 -104Q32 -106 44 -106T80 -107Q100 -107 113 -104T136 -94T154 -76T172 -47L204 11L77 502H230L295 154H299L461 502H612L332 -21ZM360 565L180 683L330 741L450 610L360 565Z" />
+<glyph unicode="&#x2013;" horiz-adv-x="588" d="M73 208L88 315H588L573 208H73Z" />
+<glyph unicode="&#x2014;" horiz-adv-x="866" d="M73 210L87 313H865L851 210H73Z" />
+<glyph unicode="&#x2018;" horiz-adv-x="272" d="M235 455H77L223 700H323L235 455Z" />
+<glyph unicode="&#x2019;" horiz-adv-x="272" d="M210 455H110L198 700H356L210 455Z" />
+<glyph unicode="&#x201A;" horiz-adv-x="272" d="M121 -178H21L109 67H267L121 -178Z" />
+<glyph unicode="&#x201C;" horiz-adv-x="516" d="M479 455H321L467 700H567L479 455ZM235 455H77L223 700H323L235 455Z" />
+<glyph unicode="&#x201D;" horiz-adv-x="516" d="M453 455H353L441 700H600L453 455ZM210 455H110L198 700H356L210 455Z" />
+<glyph unicode="&#x201E;" horiz-adv-x="516" d="M364 -179H264L352 67H511L364 -179ZM121 -179H21L109 67H267L121 -179Z" />
+<glyph unicode="&#x2020;" horiz-adv-x="385" d="M325 449L288 188H150L187 449H95L110 561H202L221 692H359L340 561H431L416 449H325Z" />
+<glyph unicode="&#x2021;" horiz-adv-x="385" d="M290 250L271 120H134L153 250H62L77 362H168L182 461H90L106 574H198L215 692H352L335 574H427L411 461H319L305 362H397L382 250H290Z" />
+<glyph unicode="&#x2022;" horiz-adv-x="517" d="M425 329Q425 294 412 264T377 212T324 176T258 163Q223 163 193 176T141 211T105 264T92 329Q92 363 105 393T140 446T193 482T258 495Q293 495 323 482T376 447T412 394T425 329Z" />
+<glyph unicode="&#x2026;" horiz-adv-x="837" d="M224 65Q224 47 216 33T194 9T163 -6T130 -11Q116 -11 103 -8T78 3T60 21T53 48Q53 66 62 79T85 102T116 116T148 121Q161 121 174 118T199 108T217 91T224 65ZM511 65Q511 47 503 33T481 9T450 -6T417 -11Q403 -11 390 -8T365 3T347 21T340 48Q340 66 349 79T372 102T403 116T435 121Q448 121 461 118T486 108T504 91T511 65ZM798 65Q798 47 790 33T768 9T737 -6T704 -11Q690 -11 677 -8T652 3T634 22T627 48Q627 66 636 79T659 102T690 116T722 121Q735 121 748 118T773 108T791 91T798 65Z" />
+<glyph unicode="&#x2030;" horiz-adv-x="1058" d="M380 587Q380 549 372 515T345 454T297 411T225 395Q168 395 141 424T113 509Q113 546 121 580T149 641T197 684T268 700Q380 700 380 587ZM234 458Q256 458 269 471T289 504T298 546T301 584Q301 607 293 621T259 636Q237 636 224 622T204 589T195 547T193 508Q193 486 201 472T234 458ZM203 0H126L686 700H762L203 0ZM709 187Q709 149 701 115T674 54T626 11T554 -5Q442 -5 442 107Q442 144 450 179T477 241T525 284T597 300Q709 300 709 187ZM563 57Q585 57 598 71T618 105T627 147T629 185Q629 207 621 221T588 236Q566 236 553 223T533 190T524 149T521 111Q521 89 529 73T563 57ZM1041 187Q1041 149 1033 115T1006 54T958 11T886 -5Q773 -5 773 107Q773 145 781 180T808 241T857 284T929 300Q1041 300 1041 187ZM895 57Q917 57 929 71T949 106T958 149T960 187Q960 208 952 222T920 236Q898 236 885 223T864 190T855 148T853 110Q853 87 861 72T895 57Z" />
+<glyph unicode="&#x2039;" horiz-adv-x="270" d="M152 97L56 300L209 502L297 455L175 299L254 144L152 97Z" />
+<glyph unicode="&#x203A;" horiz-adv-x="270" d="M144 97L56 144L179 299L99 455L201 502L298 300L144 97Z" />
+<glyph unicode="&#x2044;" horiz-adv-x="131" d="M-156 0H-232L386 700H462L-156 0Z" />
+<glyph unicode="&#x2070;" horiz-adv-x="368" d="M406 574Q406 532 397 494T367 426T314 379T234 361Q110 361 110 486Q110 528 119 566T148 635T202 682T282 700Q345 700 375 668T406 574ZM244 431Q268 431 282 446T305 484T315 531T318 574Q318 598 308 613T272 629Q248 629 233 614T210 577T200 531T197 488Q197 463 206 447T244 431Z" />
+<glyph unicode="&#x2074;" horiz-adv-x="347" d="M324 429L315 367H225L234 429H98L87 493L272 694H361L334 501H385L375 429H324ZM166 494H247L263 611L166 494Z" />
+<glyph unicode="&#x2075;" horiz-adv-x="329" d="M364 503Q364 438 322 400T210 361Q174 361 143 369T86 398L136 458Q169 427 211 427Q239 427 257 443T275 490Q275 507 264 516T225 526Q207 526 193 521T165 503L109 541L160 694H373L363 622H213L194 565L197 563Q212 573 232 578T273 584Q291 584 307 580T336 565T356 540T364 503Z" />
+<glyph unicode="&#x2076;" horiz-adv-x="341" d="M376 495Q376 463 365 438T333 396T286 370T228 361Q109 361 109 481Q109 524 118 563T148 633T202 682T283 700Q312 700 336 694T386 670L344 612Q316 634 281 634Q245 634 229 612T207 557H211Q241 581 278 581Q319 581 347 561T376 495ZM237 429Q261 429 276 444T292 483Q292 523 249 523Q224 523 210 508T195 469Q195 450 207 440T237 429Z" />
+<glyph unicode="&#x2077;" horiz-adv-x="307" d="M372 627Q331 597 306 573T266 523T244 465T232 386L230 367H135L137 385Q140 409 148 439T170 502T207 564T261 619V623H102L112 694H366L372 627H372Z" />
+<glyph unicode="&#x2078;" horiz-adv-x="337" d="M368 476Q368 443 356 421T323 386T275 367T219 361Q197 361 176 364T137 376T108 401T97 444Q97 481 113 502T163 538V542Q146 550 137 562T127 595Q127 625 139 645T170 677T214 694T265 699Q284 699 304 697T341 687T369 665T380 626Q380 566 324 542V538Q344 528 356 514T368 476ZM246 565Q265 565 280 576T295 607Q295 624 285 632T257 640Q236 640 223 628T210 595Q210 579 220 572T246 565ZM226 422Q249 422 265 436T282 474Q282 494 269 501T238 509Q214 509 199 496T184 458Q184 439 196 431T226 422Z" />
+<glyph unicode="&#x2079;" horiz-adv-x="340" d="M379 578Q379 535 371 496T342 426T290 378T209 360Q178 360 154 367T104 391L145 448Q174 426 210 426Q248 426 263 449T284 505H280Q266 491 249 485T212 479Q171 479 143 499T114 565Q114 597 125 622T157 664T204 690T262 699Q322 699 350 668T379 578ZM240 540Q265 540 280 555T295 596Q295 615 284 624T253 634Q228 634 214 618T199 578Q199 559 210 550T240 540Z" />
+<glyph unicode="&#x2080;" horiz-adv-x="368" d="M338 92Q338 50 329 12T299 -56T246 -103T166 -121Q42 -121 42 4Q42 46 51 84T80 153T134 200T214 218Q277 218 307 186T338 92ZM176 -51Q200 -51 214 -36T237 1T247 47T250 90Q250 114 241 130T204 147Q179 147 165 132T143 96T133 50T130 7Q130 -18 138 -34T176 -51Z" />
+<glyph unicode="&#x2081;" horiz-adv-x="301" d="M104 -44L127 120H123L55 91L38 157L160 212H231L195 -44H272L262 -115H13L23 -44H104Z" />
+<glyph unicode="&#x2082;" horiz-adv-x="326" d="M21 -116L18 -43Q61 -15 96 6T157 44T197 78T212 114Q212 126 204 134T178 143Q153 143 134 135T92 105L50 161Q90 192 124 205T198 218Q250 218 277 197T305 136Q305 102 288 75T245 27T187 -12T125 -44H285L275 -116H21Z" />
+<glyph unicode="&#x2083;" horiz-adv-x="319" d="M283 -4Q283 -36 271 -58T239 -95T192 -115T135 -121Q100 -121 68 -112T8 -78L54 -18Q76 -38 96 -45T137 -53Q147 -53 157 -51T177 -43T191 -28T197 -7Q197 9 186 14T157 20H104L112 80H146Q169 80 186 90T204 120Q204 131 196 139T169 148Q146 148 123 139T80 113L47 172Q85 198 117 208T189 218Q237 218 265 199T293 139Q293 110 281 93T242 61V57Q283 45 283 -4Z" />
+<glyph unicode="&#x2084;" horiz-adv-x="347" d="M256 -53L247 -115H157L166 -53H30L19 11L204 212H293L266 19H317L307 -53H256ZM98 12H179L196 129L98 12Z" />
+<glyph unicode="&#x2085;" horiz-adv-x="329" d="M296 21Q296 -44 254 -82T142 -121Q106 -121 75 -113T18 -84L68 -24Q101 -55 143 -55Q171 -55 189 -39T207 8Q207 25 196 34T157 44Q139 44 125 39T97 21L41 59L92 212H305L295 140H145L126 83L129 81Q144 91 164 96T205 102Q223 102 239 98T268 83T288 58T296 21Z" />
+<glyph unicode="&#x2086;" horiz-adv-x="341" d="M308 13Q308 -19 297 -44T265 -86T218 -112T160 -121Q41 -121 41 -1Q41 42 50 81T80 151T134 200T215 218Q244 218 268 212T318 188L276 130Q248 152 213 152Q177 152 161 130T139 75H143Q173 99 210 99Q251 99 279 79T308 13ZM169 -53Q193 -53 208 -38T224 1Q224 41 181 41Q156 41 142 26T127 -13Q127 -32 139 -42T169 -53Z" />
+<glyph unicode="&#x2087;" horiz-adv-x="307" d="M304 145Q263 115 238 91T198 41T176 -17T164 -96L162 -115H67L69 -97Q72 -73 80 -43T102 20T139 82T193 137V141H34L44 212H298L304 145H304Z" />
+<glyph unicode="&#x2088;" horiz-adv-x="337" d="M301 -6Q301 -39 289 -61T256 -96T208 -115T152 -121Q130 -121 109 -118T70 -106T41 -81T30 -38Q30 -1 46 20T96 56V60Q79 68 70 80T60 113Q60 143 72 163T103 195T147 212T198 217Q217 217 237 215T274 205T302 183T313 144Q313 84 257 60V56Q277 46 289 32T301 -6ZM179 83Q198 83 213 94T228 125Q228 142 218 150T190 158Q169 158 156 146T143 113Q143 97 153 90T179 83ZM159 -60Q182 -60 198 -46T215 -8Q215 12 202 19T171 27Q147 27 132 14T117 -24Q117 -43 129 -51T159 -60Z" />
+<glyph unicode="&#x2089;" horiz-adv-x="340" d="M311 97Q311 54 303 15T274 -55T222 -103T141 -121Q110 -121 86 -114T36 -90L77 -33Q106 -55 142 -55Q180 -55 195 -32T216 24H212Q198 10 181 4T144 -2Q103 -2 75 18T46 84Q46 116 57 141T89 183T136 209T194 218Q254 218 282 187T311 97ZM172 59Q197 59 212 74T227 115Q227 134 216 143T185 153Q160 153 146 137T131 97Q131 78 142 69T172 59Z" />
+<glyph unicode="&#x20AC;" horiz-adv-x="613" d="M156 443Q172 500 196 544T254 617T335 662T444 678Q480 678 510 672T566 653T615 621T663 575L571 489Q545 527 513 544T435 561Q403 561 380 554T341 532T313 495T294 443H522L511 367H279Q277 350 275 332T270 297H501L490 220H264Q265 192 270 171T288 134T320 112T372 104Q426 104 467 129T545 190L607 98Q552 48 489 18T351 -13Q288 -13 246 1T178 44T139 117T124 220H45L57 297H127Q129 314 131 333T137 367H66L78 443H156Z" />
+<glyph unicode="&#x2122;" horiz-adv-x="708" d="M632 437L653 577L557 467L490 577L471 437H402L439 700H500L572 574L677 700H739L702 437H632ZM290 640L262 437H190L218 640H131L140 700H385L376 640H290Z" />
+<glyph unicode="&#x2153;" horiz-adv-x="771" d="M207 438L230 602H226L158 573L140 639L263 694H334L298 438H375L365 367H116L126 438H207ZM182 0H106L724 700H800L182 0ZM755 111Q755 79 743 57T711 20T664 0T607 -6Q572 -6 540 3T480 37L526 97Q548 77 568 70T609 62Q619 62 629 64T649 72T663 87T669 108Q669 124 658 129T629 135H576L584 195H618Q641 195 658 205T676 235Q676 246 668 254T641 263Q618 263 595 254T552 228L519 287Q557 313 589 323T661 333Q709 333 737 314T765 254Q765 225 753 208T714 176V172Q755 160 755 111Z" />
+<glyph unicode="&#x2154;" horiz-adv-x="771" d="M108 366L105 439Q148 467 183 488T244 526T284 560T299 596Q299 608 291 616T265 625Q240 625 221 617T179 587L137 643Q177 674 211 687T285 700Q337 700 364 679T392 618Q392 584 375 557T332 509T274 470T212 438H372L362 366H108ZM182 0H106L724 700H800L182 0ZM755 111Q755 79 743 57T711 20T664 0T607 -6Q572 -6 540 3T480 37L526 97Q548 77 568 70T609 62Q619 62 629 64T649 72T663 87T669 108Q669 124 658 129T629 135H576L584 195H618Q641 195 658 205T676 235Q676 246 668 254T641 263Q618 263 595 254T552 228L519 287Q557 313 589 323T661 333Q709 333 737 314T765 254Q765 225 753 208T714 176V172Q755 160 755 111Z" />
+<glyph unicode="&#x215B;" horiz-adv-x="771" d="M207 438L230 602H226L158 573L140 639L263 694H334L298 438H375L365 367H116L126 438H207ZM182 0H106L724 700H800L182 0ZM760 109Q760 76 748 54T715 19T667 0T611 -6Q589 -6 568 -3T529 9T500 34T489 77Q489 114 505 135T555 171V175Q538 183 529 195T519 228Q519 258 531 278T562 310T606 327T657 332Q676 332 696 330T733 320T761 298T772 259Q772 199 716 175V171Q736 161 748 147T760 109ZM638 198Q657 198 672 209T687 240Q687 257 677 265T649 273Q628 273 615 261T602 228Q602 212 612 205T638 198ZM618 55Q641 55 657 69T674 107Q674 127 661 134T630 142Q606 142 591 129T576 91Q576 72 588 64T618 55Z" />
+<glyph unicode="&#x215C;" horiz-adv-x="771" d="M381 478Q381 446 369 424T337 387T290 367T233 361Q198 361 166 370T106 404L152 464Q174 444 194 437T235 429Q245 429 255 431T275 439T289 454T295 475Q295 491 284 496T255 502H202L210 562H244Q267 562 284 572T302 602Q302 613 294 621T267 630Q244 630 221 621T178 595L145 654Q183 680 215 690T287 700Q335 700 363 681T391 621Q391 592 379 575T340 543V539Q381 527 381 478ZM182 0H106L724 700H800L182 0ZM760 109Q760 76 748 54T715 19T667 0T611 -6Q589 -6 568 -3T529 9T500 34T489 77Q489 114 505 135T555 171V175Q538 183 529 195T519 228Q519 258 531 278T562 310T606 327T657 332Q676 332 696 330T733 320T761 298T772 259Q772 199 716 175V171Q736 161 748 147T760 109ZM638 198Q657 198 672 209T687 240Q687 257 677 265T649 273Q628 273 615 261T602 228Q602 212 612 205T638 198ZM618 55Q641 55 657 69T674 107Q674 127 661 134T630 142Q606 142 591 129T576 91Q576 72 588 64T618 55Z" />
+<glyph unicode="&#x215D;" horiz-adv-x="771" d="M394 503Q394 438 352 400T240 361Q204 361 173 369T116 398L166 458Q199 427 241 427Q269 427 287 443T305 490Q305 507 294 516T255 526Q237 526 223 521T195 503L139 541L190 694H403L393 622H243L224 565L227 563Q242 573 262 578T303 584Q321 584 337 580T366 565T386 540T394 503ZM182 0H106L724 700H800L182 0ZM760 109Q760 76 748 54T715 19T667 0T611 -6Q589 -6 568 -3T529 9T500 34T489 77Q489 114 505 135T555 171V175Q538 183 529 195T519 228Q519 258 531 278T562 310T606 327T657 332Q676 332 696 330T733 320T761 298T772 259Q772 199 716 175V171Q736 161 748 147T760 109ZM638 198Q657 198 672 209T687 240Q687 257 677 265T649 273Q628 273 615 261T602 228Q602 212 612 205T638 198ZM618 55Q641 55 657 69T674 107Q674 127 661 134T630 142Q606 142 591 129T576 91Q576 72 588 64T618 55Z" />
+<glyph unicode="&#x215E;" horiz-adv-x="771" d="M437 627Q396 597 371 573T331 523T309 465T297 386L295 367H200L202 385Q205 409 213 439T235 502T272 564T326 619V623H167L177 694H431L437 627ZM182 0H106L724 700H800L182 0ZM760 109Q760 76 748 54T715 19T667 0T611 -6Q589 -6 568 -3T529 9T500 34T489 77Q489 114 505 135T555 171V175Q538 183 529 195T519 228Q519 258 531 278T562 310T606 327T657 332Q676 332 696 330T733 320T761 298T772 259Q772 199 716 175V171Q736 161 748 147T760 109ZM638 198Q657 198 672 209T687 240Q687 257 677 265T649 273Q628 273 615 261T602 228Q602 212 612 205T638 198ZM618 55Q641 55 657 69T674 107Q674 127 661 134T630 142Q606 142 591 129T576 91Q576 72 588 64T618 55Z" />
+<glyph unicode="&#x2212;" horiz-adv-x="613" d="M74 211L91 329H614L597 211H74Z" />
+<glyph unicode="&#x2260;" horiz-adv-x="613" d="M467 333L362 218H598L582 101H263L157 -17L77 46L128 101H59L75 218H228L332 333H91L108 450H433L544 575L625 513L567 450H631L614 333H467Z" />
+<glyph unicode="&#x2264;" horiz-adv-x="613" d="M89 314L105 434L654 616L636 490L244 375L605 266L588 143L89 314ZM45 0L60 112H583L568 0H45Z" />
+<glyph unicode="&#x2265;" horiz-adv-x="613" d="M65 143L82 266L473 375L113 490L131 616L628 434L612 314L65 143ZM45 0L60 112H583L568 0H45Z" />
+<glyph unicode="&#x24DF;" horiz-adv-x="795" d="M743 334Q743 262 716 200T642 90T533 16T398 -11Q326 -11 263 16T153 90T79 199T52 334Q52 406 79 468T153 578T263 652T398 679Q470 679 532 652T642 578T716 469T743 334ZM698 335Q698 397 675 451T611 547T516 611T398 635Q336 635 282 612T187 547T123 452T99 335Q99 273 122 219T187 125T282 61T398 37Q460 37 514 60T610 124T674 219T698 335ZM598 403Q598 341 561 303T437 264H362V119H256V531Q285 534 325 536T410 539Q500 539 549 507T598 403ZM494 399Q494 431 477 445T414 460Q401 460 390 460T362 458V337H413Q455 337 474 349T494 399Z" />
+<glyph unicode="&#xF6C3;" horiz-adv-x="348" d="M155 -226H59L116 -55H265L155 -226Z" />
+
+
+</font>
+</defs>
+<text x="40" y="40" font-family="ClearviewATT BdIt" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="ClearviewATT BdIt" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="ClearviewATT BdIt" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="ClearviewATT BdIt" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.ttf b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.ttf
new file mode 100644
index 0000000000..69b3c834e6
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.woff b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.woff
new file mode 100644
index 0000000000..f2850a6f53
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BdIt.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.eot b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.eot
new file mode 100644
index 0000000000..a945fb3592
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.svg b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.svg
new file mode 100644
index 0000000000..279204943f
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.svg
@@ -0,0 +1,425 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[ClearviewATT Bk]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Copyright (c) Terminal Design, Inc, 2005. All rights reserved.]]></copyright>
+<trademark><![CDATA[Clearview is a trademark of Terminal Design, Inc.]]></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="508" id="ClearviewATT-Bk">
+<font-face font-family="ClearviewATT Bk" panose-1="2 11 5 6 3 5 0 2 0 4" ascent="750" descent="-250" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="484" d="M492 667V0H-8V667H492ZM31 606V65L217 336L31 606ZM453 67V605L268 336L453 67ZM61 35H424L242 299L61 35ZM242 373L419 630H65L242 373Z" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " horiz-adv-x="330" />
+<glyph unicode="!" horiz-adv-x="281" d="M192 667Q187 545 183 424T177 181H106Q100 424 91 667H192ZM204 46Q204 20 187 5T141 -11Q112 -11 95 4T77 46Q77 71 94 86T141 101Q169 101 186 86T204 46Z" />
+<glyph unicode="&quot;" horiz-adv-x="417" d="M325 495H266L248 700H346L325 495ZM137 495H78L60 700H158L137 495Z" />
+<glyph unicode="#" horiz-adv-x="623" d="M577 466H464L443 322H561L552 257H433L414 113H343L363 257H225L204 113H136L156 257H37L45 322H165L185 466H65L73 530H194L214 667H284L264 530H405L425 667H495L473 530H587L577 466ZM254 466L234 322H374L395 466H254Z" />
+<glyph unicode="$" horiz-adv-x="575" d="M504 197Q504 169 494 141T462 89T407 50T329 31V-47H245V31Q202 33 156 49T70 92L108 158Q200 96 286 96Q351 96 384 121T418 193Q418 232 395 255T329 298L213 347Q153 373 120 407T86 500Q86 524 94 549T120 595T170 631T246 648V723H330V648Q365 646 403 634T478 601L446 533Q409 556 371 569T289 582Q173 582 173 503Q173 487 178 475T194 453T225 433T272 410L369 369Q441 338 472 299T504 197Z" />
+<glyph unicode="%" horiz-adv-x="739" d="M308 530Q308 492 302 461T280 408T239 373T177 361Q141 361 116 373T76 407T54 461T47 530Q47 568 53 599T75 653T116 688T177 700Q214 700 239 688T279 654T301 600T308 530ZM247 531Q247 557 245 579T235 617T213 642T177 651Q154 651 141 642T120 617T111 579T108 530Q108 466 123 438T177 409Q215 409 231 438T247 531ZM179 0H123L552 700H607L179 0ZM692 163Q692 125 685 94T663 41T622 6T561 -6Q525 -6 500 6T460 40T438 94T431 163Q431 201 437 232T459 286T500 321T561 333Q597 333 622 321T662 287T685 233T692 163ZM630 164Q630 190 628 212T618 250T597 275T561 284Q538 284 525 275T504 250T495 212T492 163Q492 99 507 71T561 42Q599 42 614 71T630 164Z" />
+<glyph unicode="&amp;" horiz-adv-x="623" d="M290 -11Q230 -11 187 4T116 47T75 109T62 185Q62 221 73 250T104 302T149 345T203 379Q188 395 174 413T148 451T129 492T122 535Q122 562 132 588T163 634T216 666T292 679Q337 679 367 666T415 632T440 588T447 542Q447 510 436 485T404 440T358 402T303 367L460 151Q465 164 468 185T472 227Q472 239 471 253T468 281T464 309T459 331H538Q543 311 546 281T549 225Q549 192 542 158T515 97L586 0H491L456 47Q427 23 388 6T290 -11ZM290 54Q327 54 359 66T411 99L239 330Q221 317 204 303T175 271T154 235T146 192Q146 125 186 90T290 54ZM271 409Q319 441 345 471T372 539Q372 551 369 565T357 592T333 613T293 621Q248 621 225 597T201 538Q201 521 208 504T226 469T249 437T271 409Z" />
+<glyph unicode="&apos;" horiz-adv-x="218" d="M137 495H78L60 700H158L137 495Z" />
+<glyph unicode="(" horiz-adv-x="299" d="M180 -138Q148 -81 124 -27T83 83T58 196T49 317Q49 380 57 437T82 548T123 657T180 772L240 758Q190 654 163 546T135 317Q135 197 162 91T240 -123L180 -138Z" />
+<glyph unicode=")" horiz-adv-x="299" d="M59 -123Q110 -16 137 90T164 317Q164 438 137 546T59 758L119 772Q151 712 175 658T216 549T241 437T250 317Q250 254 242 196T217 83T176 -26T119 -138L59 -123Z" />
+<glyph unicode="*" horiz-adv-x="336" d="M203 546V544L268 479L208 435L163 517H162L117 436L57 479L122 544V546L28 560L52 629L136 590H137L122 681H200L187 591V590L271 630L296 561L203 546Z" />
+<glyph unicode="+" horiz-adv-x="575" d="M328 224V16H251V224H51V300H251V499H328V300H523V224H328Z" />
+<glyph unicode="&#x2c;" horiz-adv-x="210" d="M87 -119H27L66 97H171L87 -119Z" />
+<glyph unicode="-" horiz-adv-x="376" d="M52 219V300H324V219H52Z" />
+<glyph unicode="." horiz-adv-x="210" d="M168 46Q168 20 151 5T105 -11Q76 -11 59 4T42 46Q42 71 59 86T105 101Q133 101 150 86T168 46Z" />
+<glyph unicode="/" horiz-adv-x="512" d="M101 -115H20L416 700H498L101 -115Z" />
+<glyph unicode="0" horiz-adv-x="575" d="M522 334Q522 252 511 188T472 79T400 12T288 -12Q220 -12 175 11T103 78T65 186T53 334Q53 416 64 480T103 588T175 655T288 678Q355 678 400 655T472 588T510 480T522 334ZM432 334Q432 395 427 445T407 530T364 584T288 604Q240 604 211 585T167 531T148 446T143 334Q143 274 147 224T167 138T211 82T288 62Q335 62 363 81T407 137T427 223T432 334Z" />
+<glyph unicode="1" horiz-adv-x="575" d="M259 76V569H256L105 479L69 546L273 669H347V76H506V0H94V76H259Z" />
+<glyph unicode="2" horiz-adv-x="575" d="M81 0L68 81Q140 144 201 196T307 295T377 387T403 480Q403 501 398 522T380 562T345 591T290 603Q245 603 204 580T122 512L74 576Q128 631 179 654T295 678Q384 678 438 632T492 489Q492 442 476 397T422 303T322 199T170 76H507V0H81Z" />
+<glyph unicode="3" horiz-adv-x="575" d="M508 191Q508 150 496 114T456 49T384 5T276 -12Q213 -12 164 5T66 57L107 123Q147 92 189 77T273 61Q302 61 328 68T375 90T406 131T418 192Q418 256 387 283T280 311H205V384H236Q284 384 315 393T366 417T393 453T401 496Q401 544 371 573T275 603Q193 603 118 540L78 608Q132 645 179 661T285 678Q328 678 365 668T431 635T475 579T491 499Q491 446 467 410T394 355V353Q508 330 508 191Z" />
+<glyph unicode="4" horiz-adv-x="575" d="M437 143V0H347V143H70L46 218L339 665H437V219H528V143H437ZM348 564L137 219H352V564H348Z" />
+<glyph unicode="5" horiz-adv-x="575" d="M507 229Q507 165 490 120T442 45T365 2T264 -12Q207 -12 163 2T70 51L108 120Q148 91 186 76T265 61Q343 61 380 101T417 229Q417 311 382 347T277 384Q236 384 204 370T137 323L68 370L120 667H474V591H190L155 405L157 404Q189 431 223 443T299 456Q397 456 452 402T507 229Z" />
+<glyph unicode="6" horiz-adv-x="575" d="M520 218Q520 164 504 122T457 50T385 4T292 -12Q227 -12 182 8T108 67T67 162T54 293Q54 375 65 445T106 568T185 649T313 678Q401 678 476 633L438 565Q405 587 377 595T314 603Q273 603 241 590T187 547T153 468T140 346H142Q192 432 306 432Q406 432 463 375T520 218ZM430 215Q430 358 292 358Q227 358 187 319T147 209Q147 137 185 100T292 62Q358 62 394 100T430 215Z" />
+<glyph unicode="7" horiz-adv-x="575" d="M509 592Q443 536 404 484T343 371T314 237T307 65V0H214V30Q214 98 218 174T241 326T297 469T401 589V591H65V667H495L509 592H509Z" />
+<glyph unicode="8" horiz-adv-x="575" d="M519 191Q519 144 506 107T466 43T394 3T288 -12Q225 -12 181 2T109 43T68 106T55 191Q55 253 80 293T165 355V359Q117 378 95 415T72 503Q72 540 82 572T118 627T184 664T287 678Q393 678 447 635T501 503Q501 453 479 416T408 359V355Q465 337 492 295T519 191ZM413 499Q413 555 383 580T287 606Q220 606 190 581T160 499Q160 388 287 388Q413 388 413 499ZM429 189Q429 220 423 244T400 286T356 312T287 321Q253 321 227 315T182 293T155 251T145 186Q145 118 179 89T287 59Q358 59 393 88T429 189Z" />
+<glyph unicode="9" horiz-adv-x="575" d="M520 348Q520 161 459 75T265 -11Q215 -11 171 3T87 48L131 109Q166 83 195 72T265 61Q307 61 338 73T391 117T423 200T435 333H433Q410 286 368 261T272 235Q223 235 183 250T114 293T70 362T54 454Q54 507 71 549T118 619T191 663T283 678Q342 678 386 660T460 601T505 499T520 348ZM421 454Q421 526 386 565T284 604Q215 604 180 563T144 453Q144 387 180 349T284 310Q354 310 387 350T421 454Z" />
+<glyph unicode=":" horiz-adv-x="210" d="M168 404Q168 378 151 363T106 347Q78 347 61 362T44 404Q44 429 60 444T106 459Q134 459 151 444T168 404ZM168 46Q168 20 151 5T106 -11Q78 -11 61 4T44 46Q44 71 60 86T106 101Q134 101 151 86T168 46Z" />
+<glyph unicode=";" horiz-adv-x="210" d="M87 -119H27L65 97H167L87 -119ZM177 403Q177 377 160 362T115 346Q87 346 70 361T53 403Q53 429 70 443T115 458Q144 458 160 444T177 403Z" />
+<glyph unicode="&lt;" horiz-adv-x="575" d="M51 223V300L523 515V436L135 262L523 92V15L51 223Z" />
+<glyph unicode="=" horiz-adv-x="575" d="M52 319V394H523V319H52ZM52 132V207H523V132H52Z" />
+<glyph unicode="&gt;" horiz-adv-x="575" d="M51 15V92L440 262L51 436V515L523 300V223L51 15Z" />
+<glyph unicode="?" horiz-adv-x="515" d="M461 506Q461 444 434 402T342 329L290 305Q274 297 265 292T252 280T247 263T246 234V172H160V251Q160 274 162 289T172 315T192 335T226 354L276 377Q304 390 323 402T353 429T369 459T374 499Q374 550 341 577T247 604Q205 604 168 591T91 545L48 610Q101 646 149 662T257 678Q357 678 409 632T461 506ZM265 46Q265 20 248 5T202 -11Q173 -11 156 4T138 46Q138 71 155 86T202 101Q230 101 247 86T265 46Z" />
+<glyph unicode="@" horiz-adv-x="803" d="M741 294Q741 234 725 189T684 114T627 69T565 54Q536 54 516 70T491 115H486Q463 81 429 66T359 51Q298 51 265 90T232 204Q232 240 242 278T273 348T328 400T409 421Q441 421 462 411T500 377H504L509 413H582L546 142Q544 128 551 115T581 102Q594 102 613 111T651 144T682 204T695 297Q695 355 676 400T621 477T538 525T431 542Q353 542 294 517T194 448T133 348T112 229Q112 159 132 104T188 12T277 -45T396 -65Q459 -65 508 -50T599 -6L624 -43Q573 -78 516 -92T389 -107Q308 -107 247 -81T145 -9T83 99T62 232Q62 306 87 370T160 482T276 557T429 585Q491 585 547 568T647 514T715 423T741 294ZM494 282Q496 298 494 313T484 340T460 359T418 366Q362 366 332 324T302 199Q302 150 323 128T382 105Q428 105 452 133T484 207L494 282Z" />
+<glyph unicode="A" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582Z" />
+<glyph unicode="B" horiz-adv-x="636" d="M576 180Q576 141 565 108T527 51T459 14T357 0H86V663Q128 668 185 671T297 675Q340 675 385 670T466 647T526 594T549 498Q549 441 522 405T444 352V348Q506 337 541 294T576 180ZM460 493Q460 527 449 548T416 582T364 598T298 603Q272 603 239 602T177 597V380Q206 379 236 379T299 379Q335 379 364 382T415 398T448 432T460 493ZM486 187Q486 233 469 258T426 295T366 309T301 312Q260 312 233 312T176 311V70Q210 68 237 68T301 68Q338 68 372 71T431 85T471 121T486 187Z" />
+<glyph unicode="C" horiz-adv-x="632" d="M600 83Q545 33 488 11T365 -11Q290 -11 234 13T140 82T84 191T65 335Q65 404 82 466T137 575T231 650T368 678Q434 678 488 656T596 586L541 526Q498 566 459 583T371 601Q275 601 218 536T160 335Q160 282 170 234T204 148T268 88T368 65Q416 65 461 84T551 145L600 83H600Z" />
+<glyph unicode="D" horiz-adv-x="669" d="M604 338Q604 267 587 206T531 99T433 27T289 0H86V663Q127 668 179 671T288 675Q364 675 423 652T522 586T583 480T604 338ZM509 339Q509 384 500 430T467 515T400 578T290 602Q262 602 233 600T177 596V75Q203 74 229 74T273 74Q391 74 450 136T509 339Z" />
+<glyph unicode="E" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86Z" />
+<glyph unicode="F" horiz-adv-x="510" d="M177 590V387H448V309H177V0H86V667H477V590H177Z" />
+<glyph unicode="G" horiz-adv-x="698" d="M370 60Q458 60 503 107T549 235V277H373V352H633V278Q633 135 567 63T366 -10Q291 -10 235 16T141 88T84 196T65 333Q65 402 83 465T139 575T234 650T370 678Q445 678 499 652T593 591L539 530Q495 568 455 584T373 601Q313 601 272 580T206 522T171 436T160 333Q160 280 169 231T203 144T267 83T370 60Z" />
+<glyph unicode="H" horiz-adv-x="662" d="M485 0V314H177V0H86V667H177V399H485V667H576V0H485Z" />
+<glyph unicode="I" horiz-adv-x="263" d="M86 0V667H177V0H86Z" />
+<glyph unicode="J" horiz-adv-x="448" d="M362 177Q362 86 322 37T191 -12Q148 -12 112 -1T40 38L79 108Q108 84 131 75T182 66Q229 66 250 91T271 170V667H362V177H362Z" />
+<glyph unicode="K" horiz-adv-x="579" d="M448 0L246 321L177 242V0H86V667H177V352H179L428 667H542L314 384L561 0H448Z" />
+<glyph unicode="L" horiz-adv-x="498" d="M86 0V667H177V78H458V0H86Z" />
+<glyph unicode="M" horiz-adv-x="767" d="M593 0V518H589L383 198L178 516H174V0H86V667H170L383 337L596 667H681V0H593Z" />
+<glyph unicode="N" horiz-adv-x="701" d="M530 0L178 520H174V0H86V667H176L523 154H527V667H615V0H530Z" />
+<glyph unicode="O" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333Z" />
+<glyph unicode="P" horiz-adv-x="602" d="M550 466Q550 360 497 307T328 254H177V0H86V663Q134 668 187 671T295 675Q350 675 396 665T477 631T530 567T550 466ZM458 464Q458 503 448 529T418 572T368 594T295 601Q265 601 237 600T177 594V327H301Q338 327 367 332T417 352T447 393T458 464Z" />
+<glyph unicode="Q" horiz-adv-x="746" d="M681 334Q681 289 674 245T652 162T610 91T544 35V34Q561 29 574 21T601 4T631 -13T672 -26L644 -94Q611 -91 583 -77T531 -48T484 -20T439 -8Q425 -8 408 -9T377 -11Q295 -11 236 14T139 85T83 194T65 334Q65 409 83 472T138 580T234 652T373 678Q454 678 512 653T608 581T663 472T681 334ZM586 334Q586 389 576 438T542 523T477 581T373 603Q310 603 269 582T204 524T170 438T160 333Q160 278 170 229T205 144T270 86T373 64Q436 64 477 86T542 145T576 231T586 334Z" />
+<glyph unicode="R" horiz-adv-x="604" d="M459 0L372 240Q364 262 351 270T315 279H177V0H86V664Q139 670 185 672T286 675Q333 675 378 667T458 637T515 578T537 482Q537 419 513 373T425 304V303Q436 298 444 286T456 261L555 0H459ZM447 475Q447 514 435 538T401 577T350 596T288 602Q258 602 232 601T177 595V352H308Q340 352 366 358T410 378T437 415T447 475Z" />
+<glyph unicode="S" horiz-adv-x="539" d="M488 185Q488 87 428 38T269 -11Q202 -11 147 6T36 62L78 130Q117 101 163 82T263 62Q322 62 359 90T396 181Q396 212 381 232T341 268T285 296T220 322Q188 334 159 349T106 386T69 437T55 509Q55 555 73 587T120 639T187 668T266 677Q321 677 370 662T468 618L434 545Q398 571 357 587T269 603Q245 603 223 598T184 582T157 554T147 511Q147 485 158 467T189 436T233 412T288 391Q323 379 358 364T423 326T470 269T488 185Z" />
+<glyph unicode="T" horiz-adv-x="505" d="M298 588V0H207V588H19V667H486V588H298Z" />
+<glyph unicode="U" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583Z" />
+<glyph unicode="V" horiz-adv-x="576" d="M338 0H237L22 667H119L287 101H291L459 667H554L338 0Z" />
+<glyph unicode="W" horiz-adv-x="916" d="M712 0H608L458 538H454L307 0H196L30 667H130L256 105H261L413 667H505L657 105H661L789 667H886L712 0Z" />
+<glyph unicode="X" horiz-adv-x="564" d="M441 0L283 269H279L122 0H17L230 346L42 667H148L284 415H288L430 667H535L340 339L547 0H441Z" />
+<glyph unicode="Y" horiz-adv-x="572" d="M332 265V0H241V262L13 667H116L287 345L459 667H559L332 265Z" />
+<glyph unicode="Z" horiz-adv-x="558" d="M46 0V75L398 588H61V667H503V593L151 76H512V0H46Z" />
+<glyph unicode="[" horiz-adv-x="310" d="M58 -104V762H243V706H138V-48H243V-104H58Z" />
+<glyph unicode="\" horiz-adv-x="512" d="M403 -115L22 667H105L485 -115H403Z" />
+<glyph unicode="]" horiz-adv-x="310" d="M62 -104V-48H165V706H62V762H244V-104H62Z" />
+<glyph unicode="^" horiz-adv-x="366" d="M312 525L181 602L46 525L3 571L180 676L359 571L312 525Z" />
+<glyph unicode="_" horiz-adv-x="484" d="M-8 -144V-85H492V-144H-8Z" />
+<glyph unicode="`" horiz-adv-x="358" d="M235 565L61 674L149 714L284 595L235 565Z" />
+<glyph unicode="a" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269Z" />
+<glyph unicode="b" horiz-adv-x="564" d="M511 252Q511 201 501 154T467 70T405 11T311 -11Q256 -11 217 12T158 80H154L141 0H76V700H163V441H164Q187 483 226 498T310 514Q368 514 407 493T469 435T501 352T511 252ZM422 251Q422 287 418 322T401 384T361 429T290 446Q233 446 198 416T162 326V200Q162 170 169 143T193 97T233 66T289 54Q331 54 357 72T398 118T417 181T422 251Z" />
+<glyph unicode="c" horiz-adv-x="495" d="M427 390Q400 417 368 431T294 445Q250 445 221 429T175 385T150 324T142 252Q142 214 149 179T174 117T221 74T294 58Q335 58 367 70T433 112L463 51Q425 22 383 6T289 -11Q230 -11 186 8T113 61T68 143T53 248Q53 306 68 355T113 439T187 494T291 514Q339 514 383 497T465 451L427 390Z" />
+<glyph unicode="d" horiz-adv-x="564" d="M415 0L410 77H406Q383 32 344 11T254 -11Q197 -11 159 10T97 68T63 151T53 252Q53 303 62 350T95 434T156 492T254 514Q299 514 334 500T397 444H401V700H488V0H415ZM402 326Q402 386 367 416T274 446Q229 446 203 429T164 385T146 322T142 251Q142 217 147 182T166 119T207 72T276 54Q308 54 332 65T371 96T394 143T402 200V326Z" />
+<glyph unicode="e" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412Z" />
+<glyph unicode="f" horiz-adv-x="319" d="M305 634Q294 636 283 637T258 639Q235 639 222 636T201 626T192 605T189 571V502H297V432H192V0H104V431H27V502H104V576Q104 610 109 634T130 674T172 697T243 705Q257 705 273 704T305 700V634H305Z" />
+<glyph unicode="g" horiz-adv-x="564" d="M488 -33Q488 -95 474 -133T433 -191T371 -219T293 -226Q232 -226 183 -214T86 -175L113 -107Q156 -132 198 -145T291 -158Q351 -158 376 -137T401 -52V79H397Q371 33 338 15T257 -4Q201 -4 162 16T99 70T64 152T53 252Q53 303 62 350T95 434T156 492T254 514Q304 514 341 498T405 436H409L414 502H488V-33ZM402 325Q402 386 367 416T274 446Q230 446 204 429T165 385T147 324T142 253Q142 216 148 182T169 121T210 79T275 63Q308 63 331 74T371 105T394 151T402 208V325Z" />
+<glyph unicode="h" horiz-adv-x="564" d="M401 0V294Q401 327 399 354T385 400T353 429T293 440Q230 440 197 408T163 315V0H76V700H163V440H167Q188 478 226 496T316 514Q367 514 400 501T453 461T480 398T488 313V0H401Z" />
+<glyph unicode="i" horiz-adv-x="239" d="M76 0V502H163V0H76ZM174 626Q174 604 159 591T119 577Q95 577 80 590T65 626Q65 649 80 662T119 676Q145 676 159 663T174 626Z" />
+<glyph unicode="j" horiz-adv-x="245" d="M179 626Q179 602 163 590T124 577Q103 577 87 589T70 626Q70 652 86 664T124 676Q147 676 163 664T179 626ZM169 -39Q169 -128 130 -177T-1 -226Q-18 -226 -34 -225T-68 -222V-148Q-38 -151 -13 -151Q18 -151 37 -143T66 -120T79 -83T82 -31V502H169V-39Z" />
+<glyph unicode="k" horiz-adv-x="504" d="M380 0L236 247L163 162V0H76V700H163V263H167L360 502H470L299 304L485 0H380Z" />
+<glyph unicode="l" horiz-adv-x="272" d="M244 0Q219 -5 177 -5Q123 -5 100 17T76 97V700H163V119Q163 101 165 91T174 76T189 70T213 69H244V0H244Z" />
+<glyph unicode="m" horiz-adv-x="841" d="M678 0V304Q678 333 675 358T662 401T633 429T581 440Q525 440 495 412T464 326V0H377V308Q377 338 374 362T359 404T329 430T276 440Q250 440 229 431T194 406T171 369T163 323V0H76V502H152L160 438H162Q181 473 215 493T299 514Q347 514 383 496T437 434Q463 478 501 496T589 514Q639 514 672 501T726 461T756 398T765 313V0H678Z" />
+<glyph unicode="n" horiz-adv-x="564" d="M401 0V294Q401 327 398 354T383 400T351 429T292 440Q229 440 196 408T163 315V0H76V502H151L158 439H162Q211 514 314 514Q366 514 400 501T453 461T480 398T488 314V0H401Z" />
+<glyph unicode="o" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251Z" />
+<glyph unicode="p" horiz-adv-x="564" d="M511 252Q511 200 501 153T466 69T403 11T305 -11Q254 -11 221 9T165 66L163 67V-220H76V502H149L156 438H158Q184 480 222 497T308 514Q368 514 407 492T469 434T501 351T511 252ZM422 251Q422 287 418 322T400 384T360 429T288 446Q231 446 197 416T162 325V201Q162 170 169 144T192 97T231 66T287 54Q329 54 355 71T397 117T417 181T422 251Z" />
+<glyph unicode="q" horiz-adv-x="564" d="M481 -237Q437 -223 419 -198T401 -119V67H397Q374 25 340 7T256 -11Q198 -11 159 11T97 70T63 154T53 252Q53 303 62 350T95 434T156 492T254 514Q299 514 338 499T404 438H408L415 502H488V-101Q488 -132 494 -146T529 -173L481 -237ZM402 326Q402 385 368 415T274 446Q229 446 203 429T164 385T146 322T142 251Q142 216 147 181T167 118T208 72T276 54Q308 54 332 65T371 96T394 143T402 200V326Z" />
+<glyph unicode="r" horiz-adv-x="359" d="M317 436Q246 436 205 411T163 318V0H76V502H150L158 438H159Q182 477 220 495T313 513H339V436H317Z" />
+<glyph unicode="s" horiz-adv-x="450" d="M408 141Q408 101 395 73T357 26T300 -2T228 -11Q173 -11 126 2T35 42L67 106Q102 83 145 69T227 55Q269 55 297 73T325 136Q325 156 316 169T291 192T255 209T212 222Q180 231 151 242T99 271T63 314T49 379Q49 413 63 438T101 480T158 505T229 514Q273 514 313 504T392 475L367 409Q341 425 305 435T233 446Q212 446 194 443T162 431T140 409T132 376Q132 357 142 345T168 324T207 309T254 294Q282 285 309 274T359 246T394 204T408 141Z" />
+<glyph unicode="t" horiz-adv-x="352" d="M311 -1Q295 -3 279 -4T247 -6Q202 -6 174 2T131 27T110 68T105 126V431H22V502H106V640H192V502H311V431H192V139Q192 119 194 106T204 84T230 71T279 67Q288 67 294 67T311 68V-1H311Z" />
+<glyph unicode="u" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406Z" />
+<glyph unicode="v" horiz-adv-x="485" d="M296 0H191L20 502H113L244 77H248L375 502H465L296 0Z" />
+<glyph unicode="w" horiz-adv-x="747" d="M576 0H483L374 373H370L261 0H168L25 502H115L218 86H222L337 502H408L524 86H528L634 502H722L576 0Z" />
+<glyph unicode="x" horiz-adv-x="475" d="M361 0L240 207H236L115 0H15L187 265L35 502H134L240 320H244L349 502H442L292 268L460 0H361Z" />
+<glyph unicode="y" horiz-adv-x="495" d="M266 -65Q248 -113 233 -144T198 -193T156 -219T98 -226Q77 -226 44 -223V-149Q52 -151 62 -151T88 -152Q107 -152 120 -148T143 -132T163 -100T183 -48L207 18L18 502H112L251 111H255L384 502H477L266 -65Z" />
+<glyph unicode="z" horiz-adv-x="454" d="M39 0V68L308 431H45V502H407V431L139 71H415V0H39Z" />
+<glyph unicode="{" horiz-adv-x="348" d="M234 -104Q198 -104 176 -96T141 -70T123 -29T118 28V207Q118 239 109 261T64 292V361Q99 369 108 391T118 446V631Q118 662 123 687T140 729T175 755T232 764H281V706H255Q232 706 221 700T204 682T199 656T198 621V435Q198 415 196 401T187 374T171 350T144 327Q160 315 170 304T187 280T195 253T198 218V39Q198 15 199 -1T206 -28T223 -42T255 -47H281V-104H234Z" />
+<glyph unicode="|" horiz-adv-x="233" d="M82 -200V740H151V-200H82Z" />
+<glyph unicode="}" horiz-adv-x="348" d="M284 292Q249 284 239 262T229 207V28Q229 -4 224 -28T207 -70T172 -95T114 -104H67V-47H93Q114 -47 125 -43T142 -28T149 -2T150 39V218Q150 238 152 253T160 280T176 303T203 327Q187 339 177 350T160 373T152 400T150 435V621Q150 640 150 655T144 682T128 699T94 706H67V764H115Q149 764 171 755T206 729T224 687T229 631V446Q229 414 239 392T284 361V292H284Z" />
+<glyph unicode="~" horiz-adv-x="415" d="M405 622Q391 586 363 568T296 550Q273 550 252 556T211 569T170 583T130 590Q109 590 94 582T65 548L12 572Q34 612 59 628T123 644Q145 644 166 638T207 624T247 610T287 604Q307 604 322 613T349 644L405 622H405Z" />
+<glyph unicode="&#xA0;" horiz-adv-x="350" />
+<glyph unicode="&#xA1;" horiz-adv-x="281" d="M89 -163Q94 -41 98 80T104 323H175Q181 80 190 -163H89ZM77 458Q77 484 94 499T140 515Q169 515 186 500T204 458Q204 433 187 418T140 403Q112 403 95 418T77 458Z" />
+<glyph unicode="&#xA2;" horiz-adv-x="575" d="M494 148Q462 119 427 102T351 78V0H269V78Q219 84 183 106T124 164T90 244T79 340Q79 389 90 432T125 510T184 566T269 595V667H351V596Q390 592 425 577T492 537L454 475Q397 529 320 529Q278 529 249 515T202 475T176 415T168 342Q168 301 175 266T200 203T246 161T317 145Q352 145 385 158T456 204L494 148H494Z" />
+<glyph unicode="&#xA3;" horiz-adv-x="575" d="M512 0H63V71H83Q128 71 154 104T180 196Q180 224 175 258T161 325H77V394H150Q141 432 134 464T126 528Q126 601 173 639T307 678Q355 678 399 667T484 632L462 557Q433 577 399 590T322 603Q264 603 240 581T215 512Q215 484 221 455T234 394H385V325H247Q256 290 262 259T269 192Q269 152 257 124T225 79V76H512V0Z" />
+<glyph unicode="&#xA4;" horiz-adv-x="575" d="M82 344Q82 376 92 405T121 458L51 529L98 576L169 504Q194 522 223 532T286 542Q319 542 348 532T403 503L476 575L523 528L451 457Q490 406 490 340Q490 307 480 277T451 222L523 150L476 103L404 175Q379 157 349 147T286 137Q253 137 224 147T169 175L98 104L51 151L121 221Q82 271 82 340V344ZM159 340Q159 314 169 291T196 250T236 223T286 213Q313 213 336 223T376 250T404 290T414 340Q414 367 404 390T377 430T336 458T286 468Q260 468 237 458T196 431T169 390T159 340Z" />
+<glyph unicode="&#xA5;" horiz-adv-x="575" d="M390 362H514V302H355L335 267V202H514V142H335V0H245V142H64V202H245V262L222 302H64V362H186L29 667H132L291 342L448 667H546L390 362Z" />
+<glyph unicode="&#xA6;" horiz-adv-x="233" d="M82 335V740H151V335H82ZM82 -200V205H151V-200H82Z" />
+<glyph unicode="&#xA7;" horiz-adv-x="474" d="M425 302Q425 254 393 220T283 181V178Q349 157 388 121T427 25Q427 3 419 -21T391 -65T335 -99T244 -113Q191 -113 149 -102T60 -63L91 2Q168 -47 241 -47Q291 -47 313 -29T336 25Q336 52 320 70T256 110Q246 113 232 118T205 130T179 141T160 150Q100 178 74 208T47 284Q47 314 58 336T88 372T134 394T191 404V407Q165 416 141 429T99 459T69 497T58 545Q58 605 103 641T240 678Q284 678 322 670T401 644L377 574Q345 591 313 600T247 610Q227 610 210 607T178 597T156 578T148 547Q148 500 215 472Q248 460 274 449T332 423Q375 403 400 378T425 302ZM343 288Q343 303 339 316T322 340T290 356T236 362Q217 362 198 360T164 350T138 327T128 287Q128 278 131 265T145 241T178 221T236 213Q293 213 318 231T343 288Z" />
+<glyph unicode="&#xA8;" horiz-adv-x="358" d="M323 622Q323 602 308 587T273 572Q252 572 237 587T222 622Q222 643 237 658T273 673Q293 673 308 658T323 622ZM136 622Q136 602 121 587T85 572Q65 572 50 587T35 622Q35 643 50 658T85 673Q106 673 121 658T136 622Z" />
+<glyph unicode="&#xA9;" horiz-adv-x="866" d="M779 334Q779 262 752 200T678 91T568 17T433 -10Q361 -10 298 17T188 90T114 199T87 334Q87 406 114 468T188 577T298 651T433 678Q505 678 568 651T678 578T752 469T779 334ZM741 334Q741 397 718 453T653 552T555 618T433 643Q368 643 312 619T214 552T150 454T126 334Q126 271 150 216T216 118T314 52T433 28Q496 28 552 52T650 118T716 215T741 334ZM599 178Q565 148 527 131T442 114Q384 114 346 130T284 176T251 246T241 335Q241 385 252 425T289 494T352 537T444 552Q491 552 528 536T599 489L564 444Q539 470 510 484T445 498Q404 498 377 487T335 454T313 403T306 336Q306 298 312 267T334 214T376 181T443 169Q479 169 508 183T566 223L599 178Z" />
+<glyph unicode="&#xAA;" horiz-adv-x="390" d="M295 370Q264 380 257 403H255Q233 386 207 377T154 368Q112 368 86 391T59 460Q59 491 73 513T112 548T173 567T251 573V581Q251 610 243 622T200 634Q174 634 148 629T85 603L64 646Q101 665 133 673T202 682Q259 682 284 658T310 578V448Q310 431 313 425T332 410L295 370ZM251 536Q182 536 150 516T118 460Q118 437 132 423T168 409Q187 409 206 416T251 441V536Z" />
+<glyph unicode="&#xAB;" horiz-adv-x="454" d="M349 110L210 306L349 502L409 469L289 305L408 144L349 110ZM163 104L19 306L162 508L221 475L98 305L222 138L163 104Z" />
+<glyph unicode="&#xAC;" horiz-adv-x="575" d="M448 224H51V300H523V44H448V224Z" />
+<glyph unicode="&#xAD;" horiz-adv-x="376" d="M52 219V300H324V219H52Z" />
+<glyph unicode="&#xAE;" horiz-adv-x="453" d="M227 361Q191 361 159 374T103 411T66 464T52 530Q52 565 65 596T103 650T159 686T227 700Q263 700 294 687T350 650T387 596T401 530Q401 495 388 465T350 411T295 375T227 361ZM228 676Q196 676 169 665T121 633T89 587T77 531Q77 502 89 476T121 429T169 398T227 386Q257 386 284 397T332 428T365 474T377 531Q377 560 365 586T333 633T286 664T228 676ZM162 624Q182 626 197 627T228 628Q242 628 256 626T281 616T298 598T305 568Q305 551 297 539T275 519L309 439H266L240 503Q238 507 237 508T230 509H200V439H162V624ZM229 595Q221 595 214 595T200 594V539H231Q266 539 266 566Q266 595 229 595Z" />
+<glyph unicode="&#xAF;" horiz-adv-x="358" d="M29 589V646H327V589H29Z" />
+<glyph unicode="&#xB0;" horiz-adv-x="410" d="M348 539Q348 509 337 483T307 437T261 406T205 395Q175 395 149 406T104 436T73 482T62 539Q62 569 73 595T103 640T149 671T205 682Q235 682 261 671T306 641T337 595T348 539ZM311 539Q311 561 303 580T280 613T247 635T205 644Q183 644 164 636T131 613T108 580T99 539Q99 517 107 498T130 464T164 442T205 433Q250 433 280 463T311 539Z" />
+<glyph unicode="&#xB1;" horiz-adv-x="575" d="M328 282V114H251V282H51V357H251V546H328V357H523V282H328ZM51 0V73H523V0H51Z" />
+<glyph unicode="&#xB2;" horiz-adv-x="309" d="M41 367L32 421Q74 456 106 481T160 527T193 565T204 603Q204 620 194 634T154 649Q130 649 112 640T72 608L38 647Q67 676 96 688T160 700Q209 700 237 677T266 607Q266 587 260 569T235 529T185 480T101 417H270V367H41Z" />
+<glyph unicode="&#xB3;" horiz-adv-x="302" d="M263 460Q263 415 235 388T143 361Q111 361 83 369T27 399L53 442Q76 424 96 417T137 409Q166 409 184 422T203 464Q203 491 189 502T143 513H100V557H124Q162 557 178 571T194 606Q194 626 181 638T141 651Q119 651 98 644T60 619L32 661Q62 683 88 691T147 700Q195 700 224 678T254 608Q254 585 241 568T207 542V541Q263 527 263 460Z" />
+<glyph unicode="&#xB4;" horiz-adv-x="358" d="M123 565L74 596L211 717L297 675L123 565Z" />
+<glyph unicode="&#xB5;" horiz-adv-x="581" d="M417 0L409 63H406Q396 48 385 35T357 11T319 -5T268 -11Q255 -11 241 -10T212 -3T186 12T166 36V-220H87V502H175V238Q175 196 177 164T190 109T222 75T281 63Q316 63 339 72T377 99T397 140T403 194V502H491V0H417Z" />
+<glyph unicode="&#xB6;" horiz-adv-x="488" d="M335 0Q263 0 208 19T116 73T61 153T42 252Q42 304 60 349T116 429T208 482T335 502H402V0H335Z" />
+<glyph unicode="&#xB7;" horiz-adv-x="210" d="M167 238Q167 212 150 197T104 181Q76 181 59 196T41 238Q41 263 58 278T104 293Q133 293 150 278T167 238Z" />
+<glyph unicode="&#xB8;" horiz-adv-x="358" d="M274 -124Q274 -160 249 -183T174 -206Q150 -206 130 -199T92 -182L107 -147Q126 -157 140 -161T170 -166Q193 -166 208 -158T223 -125Q223 -102 209 -94T172 -86Q161 -86 153 -87T136 -92L118 -77L158 0H205L176 -51H177Q183 -51 189 -51T202 -50Q235 -50 254 -68T274 -124Z" />
+<glyph unicode="&#xB9;" horiz-adv-x="280" d="M117 416V628H114L45 590L25 635L129 694H178V416H257V367H34V416H117Z" />
+<glyph unicode="&#xBA;" horiz-adv-x="390" d="M338 525Q338 451 301 410T195 368Q126 368 89 409T52 525Q52 597 88 639T195 682Q264 682 301 641T338 525ZM278 525Q278 575 261 603T195 632Q147 632 130 604T113 525Q113 463 135 440T196 416Q234 416 256 439T278 525Z" />
+<glyph unicode="&#xBB;" horiz-adv-x="454" d="M291 104L232 138L355 305L231 475L291 508L435 306L291 104ZM104 110L45 144L165 305L44 469L104 502L244 306L104 110Z" />
+<glyph unicode="&#xBC;" horiz-adv-x="746" d="M153 416V628H150L81 590L61 635L165 694H214V416H293V367H70V416H153ZM192 0H139L627 700H680L192 0ZM662 67V0H600V67H460L445 114L596 327H662V117H713V67H662ZM501 114H602V267L501 114Z" />
+<glyph unicode="&#xBD;" horiz-adv-x="718" d="M145 416V628H142L73 590L53 635L157 694H206V416H285V367H62V416H145ZM153 0H100L588 700H641L153 0ZM449 0L440 54Q482 89 514 114T568 160T601 198T612 236Q612 253 602 267T562 282Q538 282 520 273T480 241L446 280Q475 309 504 321T568 333Q617 333 645 310T674 240Q674 220 668 202T643 162T593 113T509 50H678V0H449Z" />
+<glyph unicode="&#xBE;" horiz-adv-x="707" d="M298 460Q298 415 270 388T178 361Q146 361 118 369T62 399L88 442Q111 424 131 417T172 409Q201 409 219 422T238 464Q238 491 224 502T178 513H135V557H159Q197 557 213 571T229 606Q229 626 216 638T176 651Q154 651 133 644T95 619L67 661Q97 683 123 691T182 700Q230 700 259 678T289 608Q289 585 276 568T242 542V541Q298 527 298 460ZM173 0H120L608 700H661L173 0ZM630 67V0H568V67H428L413 114L564 327H630V117H681V67H630ZM469 114H570V267L469 114Z" />
+<glyph unicode="&#xBF;" horiz-adv-x="515" d="M72 -2Q72 59 99 101T191 175L243 199Q259 207 268 212T281 224T286 241T287 270V332H373V253Q373 230 371 215T361 189T341 169T307 150L257 127Q228 113 210 101T180 75T164 44T159 5Q159 -46 192 -73T286 -100Q328 -100 365 -87T442 -41L485 -106Q432 -142 384 -158T276 -174Q176 -174 124 -128T72 -2ZM268 458Q268 484 285 499T331 515Q360 515 377 500T395 458Q395 433 378 418T331 403Q303 403 286 418T268 458Z" />
+<glyph unicode="&#xC0;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM332 705L158 814L246 854L381 735L332 705Z" />
+<glyph unicode="&#xC1;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM309 705L260 736L397 857L483 815L309 705Z" />
+<glyph unicode="&#xC2;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM436 703L318 782L199 703L162 753L317 851L474 753L436 703Z" />
+<glyph unicode="&#xC3;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM483 781Q472 745 446 728T391 710Q373 710 355 716T319 729T285 743T254 750Q237 750 223 742T198 709L154 743Q172 784 195 799T247 814Q265 814 283 808T320 794T354 780T385 774Q402 774 416 782T438 814L483 781H483Z" />
+<glyph unicode="&#xC4;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM463 762Q463 742 448 727T413 712Q392 712 377 727T362 762Q362 783 377 798T413 813Q433 813 448 798T463 762ZM276 762Q276 742 261 727T225 712Q205 712 190 727T175 762Q175 783 190 798T225 813Q246 813 261 798T276 762Z" />
+<glyph unicode="&#xC5;" horiz-adv-x="642" d="M436 726Q436 695 422 671T384 632L620 0H522L465 161H174L116 0H22L253 631Q229 645 214 670T199 726Q199 751 208 772T234 808T271 832T318 841Q343 841 364 833T402 809T427 772T436 726ZM319 584H317L196 236H440L319 584ZM382 726Q382 754 364 772T318 790Q290 790 272 772T253 726Q253 697 271 679T318 660Q345 660 363 678T382 726Z" />
+<glyph unicode="&#xC6;" horiz-adv-x="898" d="M451 0V164H205L118 0H22L393 667H850V591H541V392H820V314H541V76H860V0H451ZM452 589H439L242 242H452V589Z" />
+<glyph unicode="&#xC7;" horiz-adv-x="632" d="M453 -127Q453 -165 426 -187T354 -209Q331 -209 309 -203T270 -185L286 -150Q302 -159 317 -164T351 -169Q371 -169 386 -161T401 -129Q401 -106 388 -98T352 -89Q330 -89 314 -95L296 -80L333 -10Q199 0 132 91T65 335Q65 404 82 466T137 575T231 650T368 678Q434 678 488 656T596 586L541 526Q498 566 459 583T371 601Q275 601 218 536T160 335Q160 282 170 234T204 148T268 88T368 65Q416 65 461 84T551 145L600 83Q547 35 494 13T379 -11L354 -55L355 -56Q360 -55 367 -55T380 -54Q413 -54 433 -72T453 -127Z" />
+<glyph unicode="&#xC8;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM329 705L155 814L243 854L378 735L329 705Z" />
+<glyph unicode="&#xC9;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM270 705L221 736L358 857L444 815L270 705Z" />
+<glyph unicode="&#xCA;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM405 703L287 782L168 703L131 753L286 851L443 753L405 703Z" />
+<glyph unicode="&#xCB;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM432 762Q432 742 417 727T382 712Q361 712 346 727T331 762Q331 783 346 798T382 813Q402 813 417 798T432 762ZM245 762Q245 742 230 727T194 712Q174 712 159 727T144 762Q144 783 159 798T194 813Q215 813 230 798T245 762Z" />
+<glyph unicode="&#xCC;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM145 705L-29 814L59 854L194 735L145 705Z" />
+<glyph unicode="&#xCD;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM123 705L74 736L211 857L297 815L123 705Z" />
+<glyph unicode="&#xCE;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM254 703L136 782L17 703L-20 753L135 851L292 753L254 703Z" />
+<glyph unicode="&#xCF;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM277 762Q277 742 262 727T227 712Q206 712 191 727T176 762Q176 783 191 798T227 813Q247 813 262 798T277 762ZM90 762Q90 742 75 727T39 712Q19 712 4 727T-11 762Q-11 783 4 798T39 813Q60 813 75 798T90 762Z" />
+<glyph unicode="&#xD0;" horiz-adv-x="669" d="M604 338Q604 268 587 207T531 99T432 27T284 0H87V318H23V393H87V663Q133 668 188 671T288 675Q368 675 427 651T526 583T584 476T604 338ZM510 339Q510 386 501 433T468 517T401 577T293 600Q263 600 233 599T177 594V393H333V318H177V75Q200 74 224 74T273 74Q401 74 455 141T510 339Z" />
+<glyph unicode="&#xD1;" horiz-adv-x="701" d="M530 0L178 520H174V0H86V667H176L523 154H527V667H615V0H530ZM522 781Q511 745 485 728T430 710Q412 710 394 716T358 729T324 743T293 750Q276 750 262 742T237 709L193 743Q211 784 234 799T286 814Q304 814 322 808T359 794T393 780T424 774Q441 774 455 782T477 814L522 781H522Z" />
+<glyph unicode="&#xD2;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM425 707L251 816L339 856L474 737L425 707Z" />
+<glyph unicode="&#xD3;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM358 707L309 738L446 859L532 817L358 707Z" />
+<glyph unicode="&#xD4;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM496 703L378 782L259 703L222 753L377 851L534 753L496 703Z" />
+<glyph unicode="&#xD5;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM534 783Q523 747 497 730T442 712Q424 712 406 718T370 731T336 745T305 752Q288 752 274 744T249 711L205 745Q223 786 246 801T298 816Q316 816 334 810T371 796T405 782T436 776Q453 776 467 784T489 816L534 783H534Z" />
+<glyph unicode="&#xD6;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM517 762Q517 742 502 727T467 712Q446 712 431 727T416 762Q416 783 431 798T467 813Q487 813 502 798T517 762ZM330 762Q330 742 315 727T279 712Q259 712 244 727T229 762Q229 783 244 798T279 813Q300 813 315 798T330 762Z" />
+<glyph unicode="&#xD7;" horiz-adv-x="575" d="M492 410L341 259L496 103L443 50L288 206L131 50L78 103L235 259L84 410L137 463L288 312L440 463L492 410Z" />
+<glyph unicode="&#xD8;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q314 -11 268 3T185 43L132 -24L78 25L133 94Q98 140 82 201T65 334Q65 406 83 468T138 577T234 651T373 678Q430 678 475 665T556 626L607 690L663 645L609 577Q646 530 663 469T681 334ZM586 333Q586 381 579 425T552 505L237 107Q261 87 294 75T373 63Q436 63 477 85T543 145T576 232T586 333ZM160 333Q160 288 167 245T191 167L506 563Q482 583 450 593T373 604Q310 604 269 582T204 522T170 435T160 333Z" />
+<glyph unicode="&#xD9;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM343 705L169 814L257 854L392 735L343 705Z" />
+<glyph unicode="&#xDA;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM329 705L280 736L417 857L503 815L329 705Z" />
+<glyph unicode="&#xDB;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM455 703L337 782L218 703L181 753L336 851L493 753L455 703Z" />
+<glyph unicode="&#xDC;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM480 762Q480 742 465 727T430 712Q409 712 394 727T379 762Q379 783 394 798T430 813Q450 813 465 798T480 762ZM293 762Q293 742 278 727T242 712Q222 712 207 727T192 762Q192 783 207 798T242 813Q263 813 278 798T293 762Z" />
+<glyph unicode="&#xDD;" horiz-adv-x="572" d="M332 265V0H241V262L13 667H116L287 345L459 667H559L332 265ZM289 697L240 728L377 849L463 807L289 697Z" />
+<glyph unicode="&#xDE;" horiz-adv-x="601" d="M549 340Q549 238 496 184T328 129H176V0H86V667H175V545Q208 547 236 548T295 550Q350 550 396 540T477 506T530 442T549 340ZM458 339Q458 379 448 405T418 446T367 468T294 475Q264 475 236 474T176 469V202H309Q345 203 372 208T419 229T448 270T458 339Z" />
+<glyph unicode="&#xDF;" horiz-adv-x="532" d="M490 172Q490 130 477 96T441 38T385 1T315 -12Q286 -12 261 -8T223 2L237 66Q249 61 264 58T296 55Q316 55 335 61T368 80T392 116T401 171Q401 228 366 268T243 339L232 396Q296 439 321 476T346 552Q346 569 342 585T328 613T300 633T254 641Q211 641 187 619T163 537V0H76V541Q76 579 85 609T115 661T170 693T255 705Q337 705 380 667T424 558Q424 510 402 471T322 391V387Q356 372 387 353T440 308T476 249T490 172Z" />
+<glyph unicode="&#xE0;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM292 565L118 674L206 714L341 595L292 565Z" />
+<glyph unicode="&#xE1;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM240 565L191 596L328 717L414 675L240 565Z" />
+<glyph unicode="&#xE2;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM380 562L262 641L143 562L106 612L261 710L418 612L380 562Z" />
+<glyph unicode="&#xE3;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM420 641Q409 605 383 588T328 570Q310 570 292 576T256 589T222 603T191 610Q174 610 160 602T135 569L91 603Q109 644 132 659T184 674Q202 674 220 668T257 654T291 640T322 634Q339 634 353 642T375 674L420 641H420Z" />
+<glyph unicode="&#xE4;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM409 622Q409 602 394 587T359 572Q338 572 323 587T308 622Q308 643 323 658T359 673Q379 673 394 658T409 622ZM222 622Q222 602 207 587T171 572Q151 572 136 587T121 622Q121 643 136 658T171 673Q192 673 207 658T222 622Z" />
+<glyph unicode="&#xE5;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM363 651Q363 611 336 585T268 558Q228 558 200 584T172 651Q172 691 199 717T268 744Q308 744 335 718T363 651ZM321 651Q321 674 306 689T268 705Q245 705 230 690T214 651Q214 628 229 613T268 597Q291 597 306 612T321 651Z" />
+<glyph unicode="&#xE6;" horiz-adv-x="858" d="M808 255Q808 250 808 242T806 222H453Q455 144 493 101T604 58Q641 58 679 70T751 109L783 48Q700 -11 603 -11Q537 -11 489 12T412 77Q371 40 320 15T202 -11Q133 -11 89 28T45 140Q45 192 68 227T132 283T232 313T362 322V347Q362 372 359 390T345 421T318 440T272 446Q227 446 179 431T90 388L59 452Q113 481 164 497T276 514Q328 514 369 497T425 430Q454 472 497 493T593 515Q642 515 682 498T750 448T793 367T808 255ZM727 283Q726 317 719 347T696 399T655 435T592 448Q532 448 492 406T452 283H727ZM384 125Q371 151 367 182T363 245V267Q245 267 188 238T131 144Q131 123 138 106T159 76T189 56T225 48Q263 48 304 69T384 125Z" />
+<glyph unicode="&#xE7;" horiz-adv-x="495" d="M376 -127Q376 -165 350 -187T277 -209Q254 -209 232 -203T194 -186L208 -151Q225 -160 240 -165T274 -170Q294 -170 309 -161T325 -129Q325 -107 311 -98T276 -89Q255 -89 238 -96L220 -80L256 -9Q205 -5 167 16T104 71T66 150T53 248Q53 306 68 355T113 439T187 494T291 514Q339 514 383 497T465 451L427 390Q400 417 368 431T294 445Q250 445 221 429T175 385T150 324T142 252Q142 214 149 179T174 117T221 74T294 58Q335 58 367 70T433 112L463 51Q427 23 389 7T302 -11L277 -57L278 -58Q283 -56 290 -56T302 -55Q336 -55 356 -73T376 -127Z" />
+<glyph unicode="&#xE8;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM304 565L130 674L218 714L353 595L304 565Z" />
+<glyph unicode="&#xE9;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM269 565L220 596L357 717L443 675L269 565Z" />
+<glyph unicode="&#xEA;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM400 563L282 642L163 563L126 613L281 711L438 613L400 563Z" />
+<glyph unicode="&#xEB;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM427 622Q427 602 412 587T377 572Q356 572 341 587T326 622Q326 643 341 658T377 673Q397 673 412 658T427 622ZM240 622Q240 602 225 587T189 572Q169 572 154 587T139 622Q139 643 154 658T189 673Q210 673 225 658T240 622Z" />
+<glyph unicode="&#xEC;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM142 565L-32 674L56 714L191 595L142 565Z" />
+<glyph unicode="&#xED;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM104 565L55 596L192 717L278 675L104 565Z" />
+<glyph unicode="&#xEE;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM241 563L123 642L4 563L-33 613L122 711L279 613L241 563Z" />
+<glyph unicode="&#xEF;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM266 622Q266 602 251 587T216 572Q195 572 180 587T165 622Q165 643 180 658T216 673Q236 673 251 658T266 622ZM79 622Q79 602 64 587T28 572Q8 572 -7 587T-22 622Q-22 643 -7 658T28 673Q49 673 64 658T79 622Z" />
+<glyph unicode="&#xF0;" horiz-adv-x="587" d="M420 616Q443 581 460 539T488 450T504 355T510 262Q510 201 498 151T458 65T387 10T283 -10Q228 -10 188 9T121 62T81 142T68 243Q68 294 78 338T111 414T174 466T272 485Q320 485 353 471T409 422H413Q406 464 389 506T350 584L230 524L205 575L319 632Q292 666 259 695L335 716Q346 706 362 691T387 664L466 708L491 656L420 616ZM422 233Q422 272 418 306T401 367T360 408T288 423Q243 423 217 408T178 367T160 310T156 243Q156 202 162 168T184 110T225 71T290 57Q331 57 357 72T397 113T417 169T422 233Z" />
+<glyph unicode="&#xF1;" horiz-adv-x="564" d="M401 0V294Q401 327 398 354T383 400T351 429T292 440Q229 440 196 408T163 315V0H76V502H151L158 439H162Q211 514 314 514Q366 514 400 501T453 461T480 398T488 314V0H401ZM450 641Q439 605 413 588T358 570Q340 570 322 576T286 589T252 603T221 610Q204 610 190 602T165 569L121 603Q139 644 162 659T214 674Q232 674 250 668T287 654T321 640T352 634Q369 634 383 642T405 674L450 641H450Z" />
+<glyph unicode="&#xF2;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM312 565L138 674L226 714L361 595L312 565Z" />
+<glyph unicode="&#xF3;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM273 565L224 596L361 717L447 675L273 565Z" />
+<glyph unicode="&#xF4;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM406 563L288 642L169 563L132 613L287 711L444 613L406 563Z" />
+<glyph unicode="&#xF5;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM453 641Q442 605 416 588T361 570Q343 570 325 576T289 589T255 603T224 610Q207 610 193 602T168 569L124 603Q142 644 165 659T217 674Q235 674 253 668T290 654T324 640T355 634Q372 634 386 642T408 674L453 641H453Z" />
+<glyph unicode="&#xF6;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM433 622Q433 602 418 587T383 572Q362 572 347 587T332 622Q332 643 347 658T383 673Q403 673 418 658T433 622ZM246 622Q246 602 231 587T195 572Q175 572 160 587T145 622Q145 643 160 658T195 673Q216 673 231 658T246 622Z" />
+<glyph unicode="&#xF7;" horiz-adv-x="575" d="M51 224V300H523V224H51ZM234 97Q234 120 250 135T289 151Q312 151 327 136T343 97Q343 74 328 58T289 42Q266 42 250 58T234 97ZM234 430Q234 453 250 468T289 484Q312 484 327 469T343 430Q343 407 328 391T289 375Q266 375 250 391T234 430Z" />
+<glyph unicode="&#xF8;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q368 514 424 475L477 538L522 499L467 434Q494 399 507 353T521 250Q521 195 507 148T465 65T392 9T286 -11Q236 -11 198 2T132 39L83 -18L41 22L94 85Q73 118 63 160T53 250ZM432 251Q432 284 428 314T412 369L182 99Q199 79 224 68T287 56Q329 56 357 71T401 113T425 175T432 251ZM142 251Q142 225 144 201T154 155L375 418Q342 445 287 445Q245 445 217 430T173 388T149 327T142 251Z" />
+<glyph unicode="&#xF9;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM284 565L110 674L198 714L333 595L284 565Z" />
+<glyph unicode="&#xFA;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM267 565L218 596L355 717L441 675L267 565Z" />
+<glyph unicode="&#xFB;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM399 563L281 642L162 563L125 613L280 711L437 613L399 563Z" />
+<glyph unicode="&#xFC;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM427 622Q427 602 412 587T377 572Q356 572 341 587T326 622Q326 643 341 658T377 673Q397 673 412 658T427 622ZM240 622Q240 602 225 587T189 572Q169 572 154 587T139 622Q139 643 154 658T189 673Q210 673 225 658T240 622Z" />
+<glyph unicode="&#xFD;" horiz-adv-x="495" d="M266 -65Q248 -113 233 -144T198 -193T156 -219T98 -226Q77 -226 44 -223V-149Q52 -151 62 -151T88 -152Q107 -152 120 -148T143 -132T163 -100T183 -48L207 18L18 502H112L251 111H255L384 502H477L266 -65ZM248 565L199 596L336 717L422 675L248 565Z" />
+<glyph unicode="&#xFE;" horiz-adv-x="564" d="M511 252Q511 197 499 150T461 67T396 11T302 -10Q253 -10 220 9T164 62L162 63V-220H76V700H157V443H159Q188 483 225 498T308 514Q365 514 404 494T466 438T500 354T511 252ZM422 252Q422 292 417 327T398 389T358 431T289 446Q229 446 196 416T163 326V202Q163 136 194 97T288 57Q327 57 353 72T394 114T416 176T422 252Z" />
+<glyph unicode="&#xFF;" horiz-adv-x="495" d="M266 -65Q248 -113 233 -144T198 -193T156 -219T98 -226Q77 -226 44 -223V-149Q52 -151 62 -151T88 -152Q107 -152 120 -148T143 -132T163 -100T183 -48L207 18L18 502H112L251 111H255L384 502H477L266 -65ZM399 622Q399 602 384 587T349 572Q328 572 313 587T298 622Q298 643 313 658T349 673Q369 673 384 658T399 622ZM212 622Q212 602 197 587T161 572Q141 572 126 587T111 622Q111 643 126 658T161 673Q182 673 197 658T212 622Z" />
+<glyph unicode="&#x100;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM168 729V786H466V729H168Z" />
+<glyph unicode="&#x101;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM111 589V646H409V589H111Z" />
+<glyph unicode="&#x102;" horiz-adv-x="642" d="M522 0L465 161H174L116 0H22L268 671H370L620 0H522ZM320 582H316L196 236H440L320 582ZM465 827Q462 792 448 769T412 731T367 711T318 705Q294 705 270 711T225 731T191 768T173 827H233Q237 796 257 775T318 754Q358 754 380 775T404 827H465H465Z" />
+<glyph unicode="&#x103;" horiz-adv-x="529" d="M430 -8Q407 0 393 13T373 54H369Q332 24 292 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q450 64 482 50L430 -8ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 99 157 74T225 49Q267 49 299 65T364 106V269ZM408 687Q405 652 391 629T355 591T310 571T261 565Q237 565 213 571T168 591T134 628T116 687H176Q180 656 200 635T261 614Q301 614 323 635T347 687H408H408Z" />
+<glyph unicode="&#x104;" horiz-adv-x="642" d="M571 0Q514 -35 497 -66T479 -123Q479 -153 496 -168T540 -183Q560 -183 573 -177T596 -166L614 -217Q602 -223 577 -230T527 -238Q469 -238 442 -211T415 -135Q415 -93 444 -57T521 4L465 161H174L116 0H22L268 671H370L620 0H571ZM319 582H317L196 236H440L319 582Z" />
+<glyph unicode="&#x105;" horiz-adv-x="529" d="M427 -7Q376 -42 360 -69T344 -121Q344 -149 361 -166T405 -183Q425 -183 439 -178T462 -167L479 -217Q468 -223 443 -230T392 -238Q335 -238 308 -208T280 -133Q280 -89 311 -52T394 10Q384 18 380 29T373 54H372Q335 24 293 7T203 -11Q169 -11 140 -1T90 28T57 76T45 140Q45 203 77 239T156 294T259 317T362 322V371Q360 413 337 430T269 448Q217 448 171 431T90 391L59 452Q111 481 163 497T277 514Q367 514 408 475T449 344V104Q449 81 459 67T485 43L427 -7ZM364 269Q332 269 292 267T216 252T156 215T131 145Q131 103 157 76T225 49Q267 49 299 65T364 106V269Z" />
+<glyph unicode="&#x106;" horiz-adv-x="632" d="M600 83Q545 33 488 11T365 -11Q290 -11 234 13T140 82T84 191T65 335Q65 404 82 466T137 575T231 650T368 678Q434 678 488 656T596 586L541 526Q498 566 459 583T371 601Q275 601 218 536T160 335Q160 282 170 234T204 148T268 88T368 65Q416 65 461 84T551 145L600 83H600ZM344 710L295 741L432 862L518 820L344 710Z" />
+<glyph unicode="&#x107;" horiz-adv-x="495" d="M427 390Q400 417 368 431T294 445Q250 445 221 429T175 385T150 324T142 252Q142 214 149 179T174 117T221 74T294 58Q335 58 367 70T433 112L463 51Q425 22 383 6T289 -11Q230 -11 186 8T113 61T68 143T53 248Q53 306 68 355T113 439T187 494T291 514Q339 514 383 497T465 451L427 390ZM265 565L216 596L353 717L439 675L265 565Z" />
+<glyph unicode="&#x108;" horiz-adv-x="632" d="M600 83Q545 33 488 11T365 -11Q290 -11 234 13T140 82T84 191T65 335Q65 404 82 466T137 575T231 650T368 678Q434 678 488 656T596 586L541 526Q498 566 459 583T371 601Q275 601 218 536T160 335Q160 282 170 234T204 148T268 88T368 65Q416 65 461 84T551 145L600 83H600ZM486 705L368 784L249 705L212 755L367 853L524 755L486 705Z" />
+<glyph unicode="&#x109;" horiz-adv-x="495" d="M427 390Q400 417 368 431T294 445Q250 445 221 429T175 385T150 324T142 252Q142 214 149 179T174 117T221 74T294 58Q335 58 367 70T433 112L463 51Q425 22 383 6T289 -11Q230 -11 186 8T113 61T68 143T53 248Q53 306 68 355T113 439T187 494T291 514Q339 514 383 497T465 451L427 390ZM402 563L284 642L165 563L128 613L283 711L440 613L402 563Z" />
+<glyph unicode="&#x10A;" horiz-adv-x="632" d="M600 83Q545 33 488 11T365 -11Q290 -11 234 13T140 82T84 191T65 335Q65 404 82 466T137 575T231 650T368 678Q434 678 488 656T596 586L541 526Q498 566 459 583T371 601Q275 601 218 536T160 335Q160 282 170 234T204 148T268 88T368 65Q416 65 461 84T551 145L600 83H600ZM424 770Q424 748 410 735T369 721Q345 721 330 734T315 770Q315 793 330 806T369 820Q395 820 409 807T424 770Z" />
+<glyph unicode="&#x10B;" horiz-adv-x="495" d="M427 390Q400 417 368 431T294 445Q250 445 221 429T175 385T150 324T142 252Q142 214 149 179T174 117T221 74T294 58Q335 58 367 70T433 112L463 51Q425 22 383 6T289 -11Q230 -11 186 8T113 61T68 143T53 248Q53 306 68 355T113 439T187 494T291 514Q339 514 383 497T465 451L427 390ZM339 629Q339 607 325 594T284 580Q260 580 245 593T230 629Q230 652 245 665T284 679Q310 679 324 666T339 629Z" />
+<glyph unicode="&#x10C;" horiz-adv-x="632" d="M600 83Q545 33 488 11T365 -11Q290 -11 234 13T140 82T84 191T65 335Q65 404 82 466T137 575T231 650T368 678Q434 678 488 656T596 586L541 526Q498 566 459 583T371 601Q275 601 218 536T160 335Q160 282 170 234T204 148T268 88T368 65Q416 65 461 84T551 145L600 83H600ZM510 811L366 713L222 811L257 859L365 783L474 859L510 811Z" />
+<glyph unicode="&#x10D;" horiz-adv-x="495" d="M427 390Q400 417 368 431T294 445Q250 445 221 429T175 385T150 324T142 252Q142 214 149 179T174 117T221 74T294 58Q335 58 367 70T433 112L463 51Q425 22 383 6T289 -11Q230 -11 186 8T113 61T68 143T53 248Q53 306 68 355T113 439T187 494T291 514Q339 514 383 497T465 451L427 390ZM434 663L290 565L146 663L181 711L289 635L398 711L434 663Z" />
+<glyph unicode="&#x10E;" horiz-adv-x="669" d="M604 338Q604 267 587 206T531 99T433 27T289 0H86V663Q127 668 179 671T288 675Q364 675 423 652T522 586T583 480T604 338ZM509 339Q509 384 500 430T467 515T400 578T290 602Q262 602 233 600T177 596V75Q203 74 229 74T273 74Q391 74 450 136T509 339ZM431 803L287 705L143 803L178 851L286 775L395 851L431 803Z" />
+<glyph unicode="&#x10F;" horiz-adv-x="564" d="M415 0L410 77H406Q383 32 344 11T254 -11Q197 -11 159 10T97 68T63 151T53 252Q53 303 62 350T95 434T156 492T254 514Q299 514 334 500T397 444H401V700H488V0H415ZM402 326Q402 386 367 416T274 446Q229 446 203 429T164 385T146 322T142 251Q142 217 147 182T166 119T207 72T276 54Q308 54 332 65T371 96T394 143T402 200V326ZM597 535H538L571 700H664L597 535Z" />
+<glyph unicode="&#x110;" horiz-adv-x="669" d="M604 338Q604 268 587 207T531 99T432 27T284 0H87V318H23V393H87V663Q133 668 188 671T288 675Q368 675 427 651T526 583T584 476T604 338ZM510 339Q510 386 501 433T468 517T401 577T293 600Q263 600 233 599T177 594V393H333V318H177V75Q200 74 224 74T273 74Q401 74 455 141T510 339Z" />
+<glyph unicode="&#x111;" horiz-adv-x="564" d="M415 0L410 77H406Q383 32 344 11T254 -11Q197 -11 159 10T97 66T63 149T53 249Q53 300 62 347T95 430T156 487T254 508Q299 508 336 494T400 437H401V570H243V628H401V700H488V628H555V570H488V0H415ZM402 319Q402 380 367 410T274 440Q229 440 203 424T164 380T146 319T142 248Q142 213 147 179T166 117T207 72T276 54Q308 54 332 65T371 96T394 143T402 200V319Z" />
+<glyph unicode="&#x112;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM138 729V786H436V729H138Z" />
+<glyph unicode="&#x113;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM133 589V646H431V589H133Z" />
+<glyph unicode="&#x114;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM433 827Q430 792 416 769T380 731T335 711T286 705Q262 705 238 711T193 731T159 768T141 827H201Q205 796 225 775T286 754Q326 754 348 775T372 827H433H433Z" />
+<glyph unicode="&#x115;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM429 687Q426 652 412 629T376 591T331 571T282 565Q258 565 234 571T189 591T155 628T137 687H197Q201 656 221 635T282 614Q322 614 344 635T368 687H429H429Z" />
+<glyph unicode="&#x116;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM358 768Q358 746 344 733T303 719Q279 719 264 732T249 768Q249 791 264 804T303 818Q329 818 343 805T358 768Z" />
+<glyph unicode="&#x117;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM338 629Q338 607 324 594T283 580Q259 580 244 593T229 629Q229 652 244 665T283 679Q309 679 323 666T338 629Z" />
+<glyph unicode="&#x118;" horiz-adv-x="534" d="M443 0Q386 -36 369 -65T351 -121Q351 -149 368 -166T412 -183Q432 -183 446 -178T469 -167L486 -217Q475 -223 450 -230T399 -238Q342 -238 315 -209T287 -134Q287 -91 313 -57T386 0H86V667H484V590H177V391H455V313H177V76H496V0H443Z" />
+<glyph unicode="&#x119;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q456 37 442 29T413 12Q383 -5 365 -20T337 -50T324 -78T320 -105Q320 -133 337 -151T381 -169Q401 -169 414 -164T438 -152L455 -203Q443 -209 418 -216T368 -224Q310 -224 283 -194T256 -119Q256 -88 273 -60T319 -9L318 -8Q311 -9 302 -10T286 -11Q170 -11 112 60T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM413 281Q413 314 406 344T383 397T343 435T284 449Q243 449 216 434T171 395T147 341T140 281H413Z" />
+<glyph unicode="&#x11A;" horiz-adv-x="534" d="M86 0V667H484V590H177V391H455V313H177V76H496V0H86ZM435 803L291 705L147 803L182 851L290 775L399 851L435 803Z" />
+<glyph unicode="&#x11B;" horiz-adv-x="543" d="M493 262Q493 257 493 244T492 223H140Q140 145 178 101T291 56Q329 56 364 67T440 108L471 48Q426 16 381 3T288 -11Q230 -11 186 7T113 60T68 142T53 251Q53 314 69 363T116 446T188 497T283 515Q333 515 372 497T438 445T479 365T493 262ZM412 282Q412 315 405 345T383 399T343 437T284 451Q243 451 215 436T171 397T147 343T140 282H412ZM426 663L282 565L138 663L173 711L281 635L390 711L426 663Z" />
+<glyph unicode="&#x11C;" horiz-adv-x="698" d="M370 60Q458 60 503 107T549 235V277H373V352H633V278Q633 135 567 63T366 -10Q291 -10 235 16T141 88T84 196T65 333Q65 402 83 465T139 575T234 650T370 678Q445 678 499 652T593 591L539 530Q495 568 455 584T373 601Q313 601 272 580T206 522T171 436T160 333Q160 280 169 231T203 144T267 83T370 60ZM480 703L362 782L243 703L206 753L361 851L518 753L480 703Z" />
+<glyph unicode="&#x11D;" horiz-adv-x="564" d="M488 -33Q488 -95 474 -133T433 -191T371 -219T293 -226Q232 -226 183 -214T86 -175L113 -107Q156 -132 198 -145T291 -158Q351 -158 376 -137T401 -52V79H397Q371 33 338 15T257 -4Q201 -4 162 16T99 70T64 152T53 252Q53 303 62 350T95 434T156 492T254 514Q304 514 341 498T405 436H409L414 502H488V-33ZM402 325Q402 386 367 416T274 446Q230 446 204 429T165 385T147 324T142 253Q142 216 148 182T169 121T210 79T275 63Q308 63 331 74T371 105T394 151T402 208V325ZM397 563L279 642L160 563L123 613L278 711L435 613L397 563Z" />
+<glyph unicode="&#x11E;" horiz-adv-x="698" d="M370 60Q458 60 503 107T549 235V277H373V352H633V278Q633 135 567 63T366 -10Q291 -10 235 16T141 88T84 196T65 333Q65 402 83 465T139 575T234 650T370 678Q445 678 499 652T593 591L539 530Q495 568 455 584T373 601Q313 601 272 580T206 522T171 436T160 333Q160 280 169 231T203 144T267 83T370 60ZM515 834Q512 799 498 776T462 738T417 718T368 712Q344 712 320 718T275 738T241 775T223 834H283Q287 803 307 782T368 761Q408 761 430 782T454 834H515H515Z" />
+<glyph unicode="&#x11F;" horiz-adv-x="564" d="M488 -33Q488 -95 474 -133T433 -191T371 -219T293 -226Q232 -226 183 -214T86 -175L113 -107Q156 -132 198 -145T291 -158Q351 -158 376 -137T401 -52V79H397Q371 33 338 15T257 -4Q201 -4 162 16T99 70T64 152T53 252Q53 303 62 350T95 434T156 492T254 514Q304 514 341 498T405 436H409L414 502H488V-33ZM402 325Q402 386 367 416T274 446Q230 446 204 429T165 385T147 324T142 253Q142 216 148 182T169 121T210 79T275 63Q308 63 331 74T371 105T394 151T402 208V325ZM432 687Q429 652 415 629T379 591T334 571T285 565Q261 565 237 571T192 591T158 628T140 687H200Q204 656 224 635T285 614Q325 614 347 635T371 687H432H432Z" />
+<glyph unicode="&#x120;" horiz-adv-x="698" d="M370 60Q458 60 503 107T549 235V277H373V352H633V278Q633 135 567 63T366 -10Q291 -10 235 16T141 88T84 196T65 333Q65 402 83 465T139 575T234 650T370 678Q445 678 499 652T593 591L539 530Q495 568 455 584T373 601Q313 601 272 580T206 522T171 436T160 333Q160 280 169 231T203 144T267 83T370 60ZM425 770Q425 748 411 735T370 721Q346 721 331 734T316 770Q316 793 331 806T370 820Q396 820 410 807T425 770Z" />
+<glyph unicode="&#x121;" horiz-adv-x="564" d="M488 -33Q488 -95 474 -133T433 -191T371 -219T293 -226Q232 -226 183 -214T86 -175L113 -107Q156 -132 198 -145T291 -158Q351 -158 376 -137T401 -52V79H397Q371 33 338 15T257 -4Q201 -4 162 16T99 70T64 152T53 252Q53 303 62 350T95 434T156 492T254 514Q304 514 341 498T405 436H409L414 502H488V-33ZM402 325Q402 386 367 416T274 446Q230 446 204 429T165 385T147 324T142 253Q142 216 148 182T169 121T210 79T275 63Q308 63 331 74T371 105T394 151T402 208V325ZM336 629Q336 607 322 594T281 580Q257 580 242 593T227 629Q227 652 242 665T281 679Q307 679 321 666T336 629Z" />
+<glyph unicode="&#x122;" horiz-adv-x="698" d="M370 60Q458 60 503 107T549 235V277H373V352H633V278Q633 135 567 63T366 -10Q291 -10 235 16T141 88T84 196T65 333Q65 402 83 465T139 575T234 650T370 678Q445 678 499 652T593 591L539 530Q495 568 455 584T373 601Q313 601 272 580T206 522T171 436T160 333Q160 280 169 231T203 144T267 83T370 60ZM361 -213H301L328 -59H425L361 -213Z" />
+<glyph unicode="&#x123;" horiz-adv-x="564" d="M488 -33Q488 -95 474 -133T433 -191T371 -219T293 -226Q232 -226 183 -214T86 -175L113 -107Q156 -132 198 -145T291 -158Q351 -158 376 -137T401 -52V79H397Q371 33 338 15T257 -4Q201 -4 162 16T99 70T64 152T53 252Q53 303 62 350T95 434T156 492T254 514Q304 514 341 498T405 436H409L414 502H488V-33ZM402 325Q402 386 367 416T274 446Q230 446 204 429T165 385T147 324T142 253Q142 216 148 182T169 121T210 79T275 63Q308 63 331 74T371 105T394 151T402 208V325ZM304 715H360L333 572H232L304 715Z" />
+<glyph unicode="&#x124;" horiz-adv-x="662" d="M485 0V314H177V0H86V667H177V399H485V667H576V0H485ZM457 703L339 782L220 703L183 753L338 851L495 753L457 703Z" />
+<glyph unicode="&#x125;" horiz-adv-x="564" d="M401 0V294Q401 327 399 354T385 400T353 429T293 440Q230 440 197 408T163 315V0H76V700H163V440H167Q188 478 226 496T316 514Q367 514 400 501T453 461T480 398T488 313V0H401ZM239 701L121 780L2 701L-35 751L120 849L277 751L239 701Z" />
+<glyph unicode="&#x126;" horiz-adv-x="662" d="M485 0V314H177V0H86V493H0V562H86V667H177V562H485V667H576V562H662V493H576V0H485ZM485 389V493H177V389H485Z" />
+<glyph unicode="&#x127;" horiz-adv-x="564" d="M163 570V430H167Q188 468 226 486T316 504Q367 504 400 490T453 449T480 384T488 299V0H400V283Q400 316 398 343T385 389T353 418T293 429Q230 429 197 397T163 305V0H76V570H9V627H76V700H163V627H335V570H163Z" />
+<glyph unicode="&#x128;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM296 781Q285 745 259 728T204 710Q186 710 168 716T132 729T98 743T67 750Q50 750 36 742T11 709L-33 743Q-15 784 8 799T60 814Q78 814 96 808T133 794T167 780T198 774Q215 774 229 782T251 814L296 781H296Z" />
+<glyph unicode="&#x129;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM286 641Q275 605 249 588T194 570Q176 570 158 576T122 589T88 603T57 610Q40 610 26 602T1 569L-43 603Q-25 644 -2 659T50 674Q68 674 86 668T123 654T157 640T188 634Q205 634 219 642T241 674L286 641H286Z" />
+<glyph unicode="&#x12A;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM-17 729V786H281V729H-17Z" />
+<glyph unicode="&#x12B;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM-30 589V646H268V589H-30Z" />
+<glyph unicode="&#x12C;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM278 827Q275 792 261 769T225 731T180 711T131 705Q107 705 83 711T38 731T4 768T-14 827H46Q50 796 70 775T131 754Q171 754 193 775T217 827H278H278Z" />
+<glyph unicode="&#x12D;" horiz-adv-x="239" d="M76 0V502H163V0H76ZM267 687Q264 652 250 629T214 591T169 571T120 565Q96 565 72 571T27 591T-7 628T-25 687H35Q39 656 59 635T120 614Q160 614 182 635T206 687H267H267Z" />
+<glyph unicode="&#x12E;" horiz-adv-x="263" d="M154 0Q97 -36 79 -65T61 -121Q61 -149 78 -166T122 -183Q142 -183 156 -178T179 -167L196 -217Q190 -220 180 -223T158 -230T133 -236T107 -238Q50 -238 24 -208T-3 -133Q-3 -89 27 -56T105 0H86V667H177V0H154Z" />
+<glyph unicode="&#x12F;" horiz-adv-x="239" d="M145 0Q88 -36 70 -65T52 -121Q52 -149 69 -166T113 -183Q133 -183 146 -178T170 -167L187 -217Q175 -223 150 -230T100 -238Q42 -238 15 -208T-12 -133Q-12 -92 17 -58T94 0H76V502H163V0H145ZM174 626Q174 604 159 591T119 577Q95 577 80 590T65 626Q65 649 80 662T119 676Q145 676 159 663T174 626Z" />
+<glyph unicode="&#x130;" horiz-adv-x="263" d="M86 0V667H177V0H86ZM187 770Q187 748 173 735T132 721Q108 721 93 734T78 770Q78 793 93 806T132 820Q158 820 172 807T187 770Z" />
+<glyph unicode="&#x131;" horiz-adv-x="239" d="M76 0V502H163V0H76Z" />
+<glyph unicode="&#x132;" horiz-adv-x="647" d="M86 0V667H177V0H86ZM561 177Q561 86 521 37T390 -12Q347 -12 311 -1T239 38L278 108Q307 84 330 75T381 66Q428 66 449 91T470 170V667H561V177Z" />
+<glyph unicode="&#x133;" horiz-adv-x="469" d="M77 0V502H164V0H77ZM175 626Q175 604 160 591T120 577Q96 577 81 590T66 626Q66 649 81 662T120 676Q146 676 160 663T175 626ZM403 626Q403 602 387 590T348 577Q327 577 311 589T294 626Q294 652 310 664T348 676Q371 676 387 664T403 626ZM393 -39Q393 -128 354 -177T223 -226Q206 -226 190 -225T156 -222V-148Q186 -151 211 -151Q242 -151 261 -143T290 -120T303 -83T306 -31V502H393V-39Z" />
+<glyph unicode="&#x134;" horiz-adv-x="448" d="M362 177Q362 86 322 37T191 -12Q148 -12 112 -1T40 38L79 108Q108 84 131 75T182 66Q229 66 250 91T271 170V667H362V177H362ZM435 703L317 782L198 703L161 753L316 851L473 753L435 703Z" />
+<glyph unicode="&#x135;" horiz-adv-x="245" d="M169 -39Q169 -129 130 -177T-1 -226Q-18 -226 -34 -225T-68 -222V-148Q-38 -151 -13 -151Q18 -151 37 -143T66 -120T79 -83T82 -31V502H169V-39ZM245 563L127 642L8 563L-29 613L126 711L283 613L245 563Z" />
+<glyph unicode="&#x136;" horiz-adv-x="579" d="M448 0L246 321L177 242V0H86V667H177V352H179L428 667H542L314 384L561 0H448ZM286 -213H226L253 -59H350L286 -213Z" />
+<glyph unicode="&#x137;" horiz-adv-x="504" d="M380 0L236 247L163 162V0H76V700H163V263H167L360 502H470L299 304L485 0H380ZM254 -213H194L221 -59H318L254 -213Z" />
+<glyph unicode="&#x139;" horiz-adv-x="498" d="M86 0V667H177V78H458V0H86ZM133 705L84 736L221 857L307 815L133 705Z" />
+<glyph unicode="&#x13A;" horiz-adv-x="272" d="M244 0Q219 -5 177 -5Q123 -5 100 17T76 97V700H163V119Q163 101 165 91T174 76T189 70T213 69H244V0H244ZM125 733L76 764L213 885L299 843L125 733Z" />
+<glyph unicode="&#x13B;" horiz-adv-x="498" d="M86 0V667H177V78H458V0H86ZM254 -213H194L221 -59H318L254 -213Z" />
+<glyph unicode="&#x13C;" horiz-adv-x="272" d="M244 0Q219 -5 177 -5Q123 -5 100 17T76 97V700H163V119Q163 101 165 91T174 76T189 70T213 69H244V0H244ZM152 -213H92L119 -59H216L152 -213Z" />
+<glyph unicode="&#x13D;" horiz-adv-x="498" d="M86 0V667H177V78H458V0H86ZM406 535H347L380 700H473L406 535Z" />
+<glyph unicode="&#x13E;" horiz-adv-x="272" d="M244 0Q219 -5 177 -5Q123 -5 100 17T76 97V700H163V119Q163 101 165 91T174 76T189 70T213 69H244V0ZM276 535H217L250 700H343L276 535Z" />
+<glyph unicode="&#x13F;" horiz-adv-x="498" d="M86 0V667H177V78H458V0H86ZM465 280Q465 254 448 239T402 223Q374 223 357 238T339 280Q339 305 356 320T402 335Q431 335 448 320T465 280Z" />
+<glyph unicode="&#x140;" horiz-adv-x="355" d="M244 0Q219 -5 177 -5Q123 -5 100 17T76 97V700H163V119Q163 101 165 91T174 76T189 70T213 69H244V0ZM370 238Q370 212 353 197T307 181Q279 181 262 196T244 238Q244 263 261 278T307 293Q336 293 353 278T370 238Z" />
+<glyph unicode="&#x141;" horiz-adv-x="498" d="M86 0V303L36 273V350L86 380V667H177V434L314 516V437L177 355V78H458V0H86Z" />
+<glyph unicode="&#x142;" horiz-adv-x="272" d="M163 365V119Q163 101 165 91T174 76T190 70T214 69H245V-1Q233 -3 217 -4T178 -6Q123 -6 100 16T76 97V315L29 285V368L76 399V700H163V446L263 506V425L163 365Z" />
+<glyph unicode="&#x143;" horiz-adv-x="701" d="M530 0L178 520H174V0H86V667H176L523 154H527V667H615V0H530ZM352 705L303 736L440 857L526 815L352 705Z" />
+<glyph unicode="&#x144;" horiz-adv-x="564" d="M401 0V294Q401 327 398 354T383 400T351 429T292 440Q229 440 196 408T163 315V0H76V502H151L158 439H162Q211 514 314 514Q366 514 400 501T453 461T480 398T488 314V0H401ZM277 565L228 596L365 717L451 675L277 565Z" />
+<glyph unicode="&#x145;" horiz-adv-x="701" d="M530 0L178 520H174V0H86V667H176L523 154H527V667H615V0H530ZM333 -213H273L300 -59H397L333 -213Z" />
+<glyph unicode="&#x146;" horiz-adv-x="564" d="M401 0V294Q401 327 398 354T383 400T351 429T292 440Q229 440 196 408T163 315V0H76V502H151L158 439H162Q211 514 314 514Q366 514 400 501T453 461T480 398T488 314V0H401ZM276 -213H216L243 -59H340L276 -213Z" />
+<glyph unicode="&#x147;" horiz-adv-x="701" d="M530 0L178 520H174V0H86V667H176L523 154H527V667H615V0H530ZM508 803L364 705L220 803L255 851L363 775L472 851L508 803Z" />
+<glyph unicode="&#x148;" horiz-adv-x="564" d="M401 0V294Q401 327 398 354T383 400T351 429T292 440Q229 440 196 408T163 315V0H76V502H151L158 439H162Q211 514 314 514Q366 514 400 501T453 461T480 398T488 314V0H401ZM435 663L291 565L147 663L182 711L290 635L399 711L435 663Z" />
+<glyph unicode="&#x14A;" horiz-adv-x="701" d="M432 -158H446Q468 -158 483 -150T508 -129T522 -99T527 -62V5L176 525H175V0H86V667H176L525 148H526V667H615V-60Q615 -143 578 -191T456 -239H432V-158Z" />
+<glyph unicode="&#x14B;" horiz-adv-x="564" d="M151 502L158 439H160Q209 514 314 514Q366 514 400 501T453 461T480 398T488 314V-23Q488 -115 449 -164T317 -214H293V-135H307Q334 -135 352 -127T381 -104T396 -69T401 -25V294Q401 327 398 354T383 400T351 429T292 440Q229 440 196 408T163 315V0H76V502H151Z" />
+<glyph unicode="&#x14C;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM229 729V786H527V729H229Z" />
+<glyph unicode="&#x14D;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM139 589V646H437V589H139Z" />
+<glyph unicode="&#x14E;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM523 834Q520 799 506 776T470 738T425 718T376 712Q352 712 328 718T283 738T249 775T231 834H291Q295 803 315 782T376 761Q416 761 438 782T462 834H523H523Z" />
+<glyph unicode="&#x14F;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM434 687Q431 652 417 629T381 591T336 571T287 565Q263 565 239 571T194 591T160 628T142 687H202Q206 656 226 635T287 614Q327 614 349 635T373 687H434H434Z" />
+<glyph unicode="&#x150;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q293 -11 235 15T139 87T83 196T65 334Q65 406 83 468T138 577T234 651T373 678Q453 678 511 652T607 580T663 470T681 334ZM586 333Q586 386 577 435T543 521T478 581T373 604Q310 604 269 582T204 522T170 435T160 333Q160 281 169 232T203 146T269 86T373 63Q436 63 477 85T543 145T576 232T586 333ZM505 696L459 723L600 844L680 806L505 696ZM275 696L230 723L371 844L451 806L275 696Z" />
+<glyph unicode="&#x151;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q346 514 390 495T463 440T507 357T521 250Q521 195 507 148T465 65T392 9T286 -11Q225 -11 181 9T109 64T67 146T53 250ZM142 251Q142 211 149 176T172 114T217 72T287 56Q329 56 357 71T401 113T425 175T432 251Q432 291 425 326T402 388T357 429T287 445Q245 445 217 430T173 388T149 327T142 251ZM429 562L383 589L524 710L604 672L429 562ZM199 562L154 589L295 710L375 672L199 562Z" />
+<glyph unicode="&#x152;" horiz-adv-x="894" d="M472 0Q449 -5 420 -8T364 -11Q284 -11 228 15T135 87T82 196T65 334Q65 406 81 468T134 577T227 651T364 678Q391 678 420 675T472 667H845V590H563V391H816V313H563V76H856V0H472ZM367 63Q398 63 425 67T472 79V587Q452 595 425 599T367 604Q304 604 264 582T201 522T169 435T160 333Q160 281 168 232T200 146T264 86T367 63Z" />
+<glyph unicode="&#x153;" horiz-adv-x="927" d="M877 255Q877 250 877 242T876 223H521Q523 143 561 101T674 58Q748 58 820 110L851 48Q811 19 766 4T672 -11Q597 -11 551 19T479 98Q450 41 402 15T287 -11Q175 -11 114 56T53 251Q53 373 112 443T287 514Q352 514 401 488T477 405Q506 458 552 486T662 515Q762 515 819 447T877 255ZM796 283Q795 313 788 342T766 395T726 433T663 448Q599 448 561 405T521 283H796ZM431 251Q431 293 424 328T400 389T356 428T287 442Q246 442 219 428T174 389T150 329T142 251Q142 146 181 102T287 58Q354 58 392 102T431 251Z" />
+<glyph unicode="&#x154;" horiz-adv-x="604" d="M459 0L372 240Q364 262 351 270T315 279H177V0H86V664Q139 670 185 672T286 675Q333 675 378 667T458 637T515 578T537 482Q537 419 513 373T425 304V303Q436 298 444 286T456 261L555 0H459ZM447 475Q447 514 435 538T401 577T350 596T288 602Q258 602 232 601T177 595V352H308Q340 352 366 358T410 378T437 415T447 475ZM271 707L222 738L359 859L445 817L271 707Z" />
+<glyph unicode="&#x155;" horiz-adv-x="359" d="M317 436Q246 436 205 411T163 318V0H76V502H150L158 438H159Q182 477 220 495T313 513H339V436H317ZM196 565L147 596L284 717L370 675L196 565Z" />
+<glyph unicode="&#x156;" horiz-adv-x="604" d="M459 0L372 240Q364 262 351 270T315 279H177V0H86V664Q139 670 185 672T286 675Q333 675 378 667T458 637T515 578T537 482Q537 419 513 373T425 304V303Q436 298 444 286T456 261L555 0H459ZM447 475Q447 514 435 538T401 577T350 596T288 602Q258 602 232 601T177 595V352H308Q340 352 366 358T410 378T437 415T447 475ZM292 -213H232L259 -59H356L292 -213Z" />
+<glyph unicode="&#x157;" horiz-adv-x="359" d="M317 436Q246 436 205 411T163 318V0H76V502H150L158 438H159Q182 477 220 495T313 513H339V436H317ZM136 -213H76L103 -59H200L136 -213Z" />
+<glyph unicode="&#x158;" horiz-adv-x="604" d="M459 0L372 240Q364 262 351 270T315 279H177V0H86V664Q139 670 185 672T286 675Q333 675 378 667T458 637T515 578T537 482Q537 419 513 373T425 304V303Q436 298 444 286T456 261L555 0H459ZM447 475Q447 514 435 538T401 577T350 596T288 602Q258 602 232 601T177 595V352H308Q340 352 366 358T410 378T437 415T447 475ZM434 803L290 705L146 803L181 851L289 775L398 851L434 803Z" />
+<glyph unicode="&#x159;" horiz-adv-x="359" d="M317 436Q246 436 205 411T163 318V0H76V502H150L158 438H159Q182 477 220 495T313 513H339V436H317ZM364 663L220 565L76 663L111 711L219 635L328 711L364 663Z" />
+<glyph unicode="&#x15A;" horiz-adv-x="539" d="M488 185Q488 87 428 38T269 -11Q202 -11 147 6T36 62L78 130Q117 101 163 82T263 62Q322 62 359 90T396 181Q396 212 381 232T341 268T285 296T220 322Q188 334 159 349T106 386T69 437T55 509Q55 555 73 587T120 639T187 668T266 677Q321 677 370 662T468 618L434 545Q398 571 357 587T269 603Q245 603 223 598T184 582T157 554T147 511Q147 485 158 467T189 436T233 412T288 391Q323 379 358 364T423 326T470 269T488 185ZM259 707L210 738L347 859L433 817L259 707Z" />
+<glyph unicode="&#x15B;" horiz-adv-x="450" d="M408 141Q408 101 395 73T357 26T300 -2T228 -11Q173 -11 126 2T35 42L67 106Q102 83 145 69T227 55Q269 55 297 73T325 136Q325 156 316 169T291 192T255 209T212 222Q180 231 151 242T99 271T63 314T49 379Q49 413 63 438T101 480T158 505T229 514Q273 514 313 504T392 475L367 409Q341 425 305 435T233 446Q212 446 194 443T162 431T140 409T132 376Q132 357 142 345T168 324T207 309T254 294Q282 285 309 274T359 246T394 204T408 141ZM210 565L161 596L298 717L384 675L210 565Z" />
+<glyph unicode="&#x15C;" horiz-adv-x="539" d="M488 185Q488 87 428 38T269 -11Q202 -11 147 6T36 62L78 130Q117 101 163 82T263 62Q322 62 359 90T396 181Q396 212 381 232T341 268T285 296T220 322Q188 334 159 349T106 386T69 437T55 509Q55 555 73 587T120 639T187 668T266 677Q321 677 370 662T468 618L434 545Q398 571 357 587T269 603Q245 603 223 598T184 582T157 554T147 511Q147 485 158 467T189 436T233 412T288 391Q323 379 358 364T423 326T470 269T488 185ZM390 703L272 782L153 703L116 753L271 851L428 753L390 703Z" />
+<glyph unicode="&#x15D;" horiz-adv-x="450" d="M408 141Q408 101 395 73T357 26T300 -2T228 -11Q173 -11 126 2T35 42L67 106Q102 83 145 69T227 55Q269 55 297 73T325 136Q325 156 316 169T291 192T255 209T212 222Q180 231 151 242T99 271T63 314T49 379Q49 413 63 438T101 480T158 505T229 514Q273 514 313 504T392 475L367 409Q341 425 305 435T233 446Q212 446 194 443T162 431T140 409T132 376Q132 357 142 345T168 324T207 309T254 294Q282 285 309 274T359 246T394 204T408 141ZM359 563L241 642L122 563L85 613L240 711L397 613L359 563Z" />
+<glyph unicode="&#x15E;" horiz-adv-x="539" d="M357 -128Q357 -166 331 -188T259 -210Q236 -210 214 -204T175 -187L190 -151Q207 -160 222 -165T256 -171Q276 -171 291 -162T306 -130Q306 -108 292 -99T257 -90Q246 -90 237 -92T219 -96L201 -81L238 -10Q181 -6 133 11T36 62L78 130Q117 101 162 82T263 62Q292 62 316 69T358 90T386 127T396 181Q396 212 381 232T341 268T284 296T219 322Q188 334 159 349T106 386T69 437T55 509Q55 555 72 587T119 639T187 668T266 677Q321 677 370 662T468 618L434 545Q398 571 357 587T268 603Q244 603 223 598T185 582T158 554T148 511Q148 485 159 467T189 436T234 412T288 391Q323 379 358 364T423 326T470 269T488 185Q488 90 433 42T284 -10L259 -57L260 -58Q265 -56 272 -56T284 -55Q318 -55 337 -73T357 -128Z" />
+<glyph unicode="&#x15F;" horiz-adv-x="450" d="M322 -127Q322 -165 295 -187T223 -209Q200 -209 178 -203T139 -186L154 -151Q170 -160 185 -165T220 -170Q239 -170 255 -161T271 -129Q271 -107 257 -98T221 -89Q210 -89 201 -91T183 -95L165 -80L202 -10Q154 -7 114 6T35 42L67 106Q102 83 145 69T227 55Q269 55 297 73T326 136Q326 157 317 171T291 195T255 212T212 226Q180 235 151 246T99 274T63 316T49 379Q49 413 63 438T101 480T158 505T229 514Q273 514 313 504T392 475L368 412Q341 428 305 439T234 450Q192 450 162 435T132 379Q132 360 142 347T168 325T207 308T254 294Q282 286 309 275T359 247T394 204T408 141Q408 66 364 30T248 -10L222 -57L223 -58Q228 -57 235 -57T247 -56Q281 -56 301 -73T322 -127Z" />
+<glyph unicode="&#x160;" horiz-adv-x="539" d="M488 185Q488 87 428 38T269 -11Q202 -11 147 6T36 62L78 130Q117 101 163 82T263 62Q322 62 359 90T396 181Q396 212 381 232T341 268T285 296T220 322Q188 334 159 349T106 386T69 437T55 509Q55 555 73 587T120 639T187 668T266 677Q321 677 370 662T468 618L434 545Q398 571 357 587T269 603Q245 603 223 598T184 582T157 554T147 511Q147 485 158 467T189 436T233 412T288 391Q323 379 358 364T423 326T470 269T488 185ZM414 803L270 705L126 803L161 851L269 775L378 851L414 803Z" />
+<glyph unicode="&#x161;" horiz-adv-x="450" d="M408 141Q408 101 395 73T357 26T300 -2T228 -11Q173 -11 126 2T35 42L67 106Q102 83 145 69T227 55Q269 55 297 73T325 136Q325 156 316 169T291 192T255 209T212 222Q180 231 151 242T99 271T63 314T49 379Q49 413 63 438T101 480T158 505T229 514Q273 514 313 504T392 475L367 409Q341 425 305 435T233 446Q212 446 194 443T162 431T140 409T132 376Q132 357 142 345T168 324T207 309T254 294Q282 285 309 274T359 246T394 204T408 141ZM380 663L236 565L92 663L127 711L235 635L344 711L380 663Z" />
+<glyph unicode="&#x162;" horiz-adv-x="505" d="M298 588V0H207V588H19V667H486V588H298ZM232 -214H172L199 -60H296L232 -214Z" />
+<glyph unicode="&#x163;" horiz-adv-x="352" d="M311 -1Q295 -3 279 -4T247 -6Q202 -6 174 2T131 27T110 68T105 126V431H22V502H106V640H192V502H311V431H192V139Q192 119 194 106T204 84T230 71T279 67Q288 67 294 67T311 68V-1H311ZM202 -213H142L169 -59H266L202 -213Z" />
+<glyph unicode="&#x164;" horiz-adv-x="505" d="M298 588V0H207V588H19V667H486V588H298ZM399 803L255 705L111 803L146 851L254 775L363 851L399 803Z" />
+<glyph unicode="&#x165;" horiz-adv-x="352" d="M311 -1Q295 -3 279 -4T247 -6Q202 -6 174 2T131 27T110 68T105 126V431H22V502H106V640H192V502H311V431H192V139Q192 119 194 106T204 84T230 71T279 67Q288 67 294 67T311 68V-1ZM341 535H282L315 700H408L341 535Z" />
+<glyph unicode="&#x166;" horiz-adv-x="505" d="M298 324V0H207V324H49V396H207V588H19V667H486V588H298V396H456V324H298Z" />
+<glyph unicode="&#x167;" horiz-adv-x="352" d="M192 270V139Q192 119 194 106T204 84T230 71T279 67H311V-1Q295 -3 279 -4T247 -6Q202 -6 174 2T131 27T110 68T105 126V270H22V338H105V431H22V502H106V640H192V502H311V431H192V338H311V270H192Z" />
+<glyph unicode="&#x168;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM498 781Q487 745 461 728T406 710Q388 710 370 716T334 729T300 743T269 750Q252 750 238 742T213 709L169 743Q187 784 210 799T262 814Q280 814 298 808T335 794T369 780T400 774Q417 774 431 782T453 814L498 781H498Z" />
+<glyph unicode="&#x169;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM439 641Q428 605 402 588T347 570Q329 570 311 576T275 589T241 603T210 610Q193 610 179 602T154 569L110 603Q128 644 151 659T203 674Q221 674 239 668T276 654T310 640T341 634Q358 634 372 642T394 674L439 641H439Z" />
+<glyph unicode="&#x16A;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM188 729V786H486V729H188Z" />
+<glyph unicode="&#x16B;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM127 589V646H425V589H127Z" />
+<glyph unicode="&#x16C;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM480 827Q477 792 463 769T427 731T382 711T333 705Q309 705 285 711T240 731T206 768T188 827H248Q252 796 272 775T333 754Q373 754 395 775T419 827H480H480Z" />
+<glyph unicode="&#x16D;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM427 687Q424 652 410 629T374 591T329 571T280 565Q256 565 232 571T187 591T153 628T135 687H195Q199 656 219 635T280 614Q320 614 342 635T366 687H427H427Z" />
+<glyph unicode="&#x16E;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM431 781Q431 741 404 715T336 688Q296 688 268 714T240 781Q240 821 267 847T336 874Q376 874 403 848T431 781ZM389 781Q389 804 374 819T336 835Q313 835 298 820T282 781Q282 758 297 743T336 727Q359 727 374 742T389 781Z" />
+<glyph unicode="&#x16F;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM374 657Q374 617 347 591T279 564Q239 564 211 590T183 657Q183 697 210 723T279 750Q319 750 346 724T374 657ZM332 657Q332 680 317 695T279 711Q256 711 241 696T225 657Q225 634 240 619T279 603Q302 603 317 618T332 657Z" />
+<glyph unicode="&#x170;" horiz-adv-x="669" d="M583 259Q583 120 525 55T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583ZM456 708L410 735L551 856L631 818L456 708ZM226 708L181 735L322 856L402 818L226 708Z" />
+<glyph unicode="&#x171;" horiz-adv-x="555" d="M406 0L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H406ZM407 568L361 595L502 716L582 678L407 568ZM177 568L132 595L273 716L353 678L177 568Z" />
+<glyph unicode="&#x172;" horiz-adv-x="669" d="M583 259Q583 156 552 95T451 7Q422 -11 405 -27T377 -59T363 -87T359 -114Q359 -143 376 -159T420 -176Q440 -176 453 -171T477 -160L493 -210Q482 -216 457 -223T407 -231Q349 -231 322 -201T295 -126Q295 -92 314 -62T368 -9Q360 -10 352 -10T334 -11Q202 -11 144 55T86 259V667H177V259Q177 216 184 181T208 120T256 80T334 65Q381 65 411 79T460 119T485 180T492 259V667H583V259H583Z" />
+<glyph unicode="&#x173;" horiz-adv-x="555" d="M454 0Q397 -36 380 -65T362 -121Q362 -149 379 -166T423 -183Q443 -183 456 -178T480 -167L496 -217Q485 -223 460 -230T410 -238Q352 -238 325 -208T298 -133Q298 -89 327 -54T406 4L398 63H395Q376 31 340 10T244 -11Q204 -11 173 -2T120 30T87 92T76 192V502H163V237Q163 195 166 163T179 108T211 74T269 62Q304 62 327 71T365 98T386 139T392 194V502H479V0H454Z" />
+<glyph unicode="&#x174;" horiz-adv-x="916" d="M712 0H608L458 538H454L307 0H196L30 667H130L256 105H261L413 667H505L657 105H661L789 667H886L712 0ZM577 703L459 782L340 703L303 753L458 851L615 753L577 703Z" />
+<glyph unicode="&#x175;" horiz-adv-x="747" d="M576 0H483L374 373H370L261 0H168L25 502H115L218 86H222L337 502H408L524 86H528L634 502H722L576 0ZM493 563L375 642L256 563L219 613L374 711L531 613L493 563Z" />
+<glyph unicode="&#x176;" horiz-adv-x="572" d="M332 265V0H241V262L13 667H116L287 345L459 667H559L332 265ZM407 703L289 782L170 703L133 753L288 851L445 753L407 703Z" />
+<glyph unicode="&#x177;" horiz-adv-x="495" d="M266 -65Q248 -113 233 -144T198 -193T156 -219T98 -226Q77 -226 44 -223V-149Q52 -151 62 -151T88 -152Q107 -152 120 -148T143 -132T163 -100T183 -48L207 18L18 502H112L251 111H255L384 502H477L266 -65ZM371 563L253 642L134 563L97 613L252 711L409 613L371 563Z" />
+<glyph unicode="&#x178;" horiz-adv-x="572" d="M332 265V0H241V262L13 667H116L287 345L459 667H559L332 265ZM433 762Q433 742 418 727T383 712Q362 712 347 727T332 762Q332 783 347 798T383 813Q403 813 418 798T433 762ZM246 762Q246 742 231 727T195 712Q175 712 160 727T145 762Q145 783 160 798T195 813Q216 813 231 798T246 762Z" />
+<glyph unicode="&#x179;" horiz-adv-x="558" d="M46 0V75L398 588H61V667H503V593L151 76H512V0H46ZM263 705L214 736L351 857L437 815L263 705Z" />
+<glyph unicode="&#x17A;" horiz-adv-x="454" d="M39 0V68L308 431H45V502H407V431L139 71H415V0H39ZM210 565L161 596L298 717L384 675L210 565Z" />
+<glyph unicode="&#x17B;" horiz-adv-x="558" d="M46 0V75L398 588H61V667H503V593L151 76H512V0H46ZM340 770Q340 748 326 735T285 721Q261 721 246 734T231 770Q231 793 246 806T285 820Q311 820 325 807T340 770Z" />
+<glyph unicode="&#x17C;" horiz-adv-x="454" d="M39 0V68L308 431H45V502H407V431L139 71H415V0H39ZM296 629Q296 607 282 594T241 580Q217 580 202 593T187 629Q187 652 202 665T241 679Q267 679 281 666T296 629Z" />
+<glyph unicode="&#x17D;" horiz-adv-x="558" d="M46 0V75L398 588H61V667H503V593L151 76H512V0H46ZM428 803L284 705L140 803L175 851L283 775L392 851L428 803Z" />
+<glyph unicode="&#x17E;" horiz-adv-x="454" d="M39 0V68L308 431H45V502H407V431L139 71H415V0H39ZM383 663L239 565L95 663L130 711L238 635L347 711L383 663Z" />
+<glyph unicode="&#x192;" horiz-adv-x="575" d="M514 613Q501 614 495 614T476 614Q441 614 419 601T388 548L364 429H471L458 356H352L281 -10Q273 -53 260 -87T225 -144T175 -180T105 -193Q91 -193 77 -192T48 -186L61 -119Q72 -121 82 -121T106 -122Q125 -122 140 -112T166 -83T185 -41T198 12L263 356H187L200 429H275L298 548Q305 586 316 611T349 653T401 675T481 682Q493 682 502 682T526 681L514 613Z" />
+<glyph unicode="&#x1FC;" horiz-adv-x="898" d="M451 0V164H205L118 0H22L393 667H850V591H541V392H820V314H541V76H860V0H451ZM452 589H439L242 242H452V589ZM586 705L537 736L674 857L760 815L586 705Z" />
+<glyph unicode="&#x1FD;" horiz-adv-x="858" d="M808 255Q808 250 808 242T806 222H453Q455 144 493 101T604 58Q641 58 679 70T751 109L783 48Q700 -11 603 -11Q537 -11 489 12T412 77Q371 40 320 15T202 -11Q133 -11 89 28T45 140Q45 192 68 227T132 283T232 313T362 322V347Q362 372 359 390T345 421T318 440T272 446Q227 446 179 431T90 388L59 452Q113 481 164 497T276 514Q328 514 369 497T425 430Q454 472 497 493T593 515Q642 515 682 498T750 448T793 367T808 255ZM727 283Q726 317 719 347T696 399T655 435T592 448Q532 448 492 406T452 283H727ZM384 125Q371 151 367 182T363 245V267Q245 267 188 238T131 144Q131 123 138 106T159 76T189 56T225 48Q263 48 304 69T384 125ZM446 565L397 596L534 717L620 675L446 565Z" />
+<glyph unicode="&#x1FE;" horiz-adv-x="746" d="M681 334Q681 259 663 196T607 87T511 15T373 -11Q314 -11 268 3T185 43L132 -24L78 25L133 94Q98 140 82 201T65 334Q65 406 83 468T138 577T234 651T373 678Q430 678 475 665T556 626L607 690L663 645L609 577Q646 530 663 469T681 334ZM586 333Q586 381 579 425T552 505L237 107Q261 87 294 75T373 63Q436 63 477 85T543 145T576 232T586 333ZM160 333Q160 288 167 245T191 167L506 563Q482 583 450 593T373 604Q310 604 269 582T204 522T170 435T160 333ZM352 707L303 738L440 859L526 817L352 707Z" />
+<glyph unicode="&#x1FF;" horiz-adv-x="574" d="M53 250Q53 305 66 353T107 437T179 493T286 514Q368 514 424 475L477 538L522 499L467 434Q494 399 507 353T521 250Q521 195 507 148T465 65T392 9T286 -11Q236 -11 198 2T132 39L83 -18L41 22L94 85Q73 118 63 160T53 250ZM432 251Q432 284 428 314T412 369L182 99Q199 79 224 68T287 56Q329 56 357 71T401 113T425 175T432 251ZM142 251Q142 225 144 201T154 155L375 418Q342 445 287 445Q245 445 217 430T173 388T149 327T142 251ZM280 565L231 596L368 717L454 675L280 565Z" />
+<glyph unicode="&#x218;" horiz-adv-x="539" d="M488 185Q488 87 428 38T269 -11Q202 -11 147 6T36 62L78 130Q117 101 163 82T263 62Q322 62 359 90T396 181Q396 212 381 232T341 268T285 296T220 322Q188 334 159 349T106 386T69 437T55 509Q55 555 73 587T120 639T187 668T266 677Q321 677 370 662T468 618L434 545Q398 571 357 587T269 603Q245 603 223 598T184 582T157 554T147 511Q147 485 158 467T189 436T233 412T288 391Q323 379 358 364T423 326T470 269T488 185ZM254 -213H194L221 -59H318L254 -213Z" />
+<glyph unicode="&#x219;" horiz-adv-x="450" d="M408 141Q408 101 395 73T357 26T300 -2T228 -11Q173 -11 126 2T35 42L67 106Q102 83 145 69T227 55Q269 55 297 73T325 136Q325 156 316 169T291 192T255 209T212 222Q180 231 151 242T99 271T63 314T49 379Q49 413 63 438T101 480T158 505T229 514Q273 514 313 504T392 475L367 409Q341 425 305 435T233 446Q212 446 194 443T162 431T140 409T132 376Q132 357 142 345T168 324T207 309T254 294Q282 285 309 274T359 246T394 204T408 141ZM210 -213H150L177 -59H274L210 -213Z" />
+<glyph unicode="&#x237;" horiz-adv-x="245" d="M169 -39Q169 -129 130 -177T-1 -226Q-18 -226 -34 -225T-68 -222V-148Q-38 -151 -13 -151Q18 -151 37 -143T66 -120T79 -83T82 -31V502H169V-39H169Z" />
+<glyph unicode="&#x2C6;" horiz-adv-x="358" d="M297 563L179 642L60 563L23 613L178 711L335 613L297 563Z" />
+<glyph unicode="&#x2C7;" horiz-adv-x="358" d="M323 663L179 565L35 663L70 711L178 635L287 711L323 663Z" />
+<glyph unicode="&#x2D8;" horiz-adv-x="358" d="M325 687Q322 652 308 629T272 591T227 571T178 565Q154 565 130 571T85 591T51 628T33 687H93Q97 656 117 635T178 614Q218 614 240 635T264 687H325H325Z" />
+<glyph unicode="&#x2D9;" horiz-adv-x="358" d="M233 629Q233 607 219 594T178 580Q154 580 139 593T124 629Q124 652 139 665T178 679Q204 679 218 666T233 629Z" />
+<glyph unicode="&#x2DA;" horiz-adv-x="358" d="M274 657Q274 617 247 591T179 564Q139 564 111 590T83 657Q83 697 110 723T179 750Q219 750 246 724T274 657ZM232 657Q232 680 217 695T179 711Q156 711 141 696T125 657Q125 634 140 619T179 603Q202 603 217 618T232 657Z" />
+<glyph unicode="&#x2DB;" horiz-adv-x="358" d="M196 -27Q173 -44 158 -68T142 -118Q142 -148 159 -165T203 -183Q223 -183 236 -177T260 -166L277 -217Q265 -223 240 -230T190 -238Q133 -238 106 -208T78 -130Q78 -106 88 -84T117 -43T160 -9T214 17L237 2L196 -27Z" />
+<glyph unicode="&#x2DC;" horiz-adv-x="358" d="M344 641Q333 605 307 588T252 570Q234 570 216 576T180 589T146 603T115 610Q98 610 84 602T59 569L15 603Q33 644 56 659T108 674Q126 674 144 668T181 654T215 640T246 634Q263 634 277 642T299 674L344 641H344Z" />
+<glyph unicode="&#x2DD;" horiz-adv-x="358" d="M237 568L191 595L332 716L412 678L237 568ZM7 568L-38 595L103 716L183 678L7 568Z" />
+<glyph unicode="&#x1E80;" horiz-adv-x="916" d="M712 0H608L458 538H454L307 0H196L30 667H130L256 105H261L413 667H505L657 105H661L789 667H886L712 0ZM478 705L304 814L392 854L527 735L478 705Z" />
+<glyph unicode="&#x1E81;" horiz-adv-x="747" d="M576 0H483L374 373H370L261 0H168L25 502H115L218 86H222L337 502H408L524 86H528L634 502H722L576 0ZM390 565L216 674L304 714L439 595L390 565Z" />
+<glyph unicode="&#x1E82;" horiz-adv-x="916" d="M712 0H608L458 538H454L307 0H196L30 667H130L256 105H261L413 667H505L657 105H661L789 667H886L712 0ZM441 705L392 736L529 857L615 815L441 705Z" />
+<glyph unicode="&#x1E83;" horiz-adv-x="747" d="M576 0H483L374 373H370L261 0H168L25 502H115L218 86H222L337 502H408L524 86H528L634 502H722L576 0ZM358 565L309 596L446 717L532 675L358 565Z" />
+<glyph unicode="&#x1E84;" horiz-adv-x="916" d="M712 0H608L458 538H454L307 0H196L30 667H130L256 105H261L413 667H505L657 105H661L789 667H886L712 0ZM603 762Q603 742 588 727T553 712Q532 712 517 727T502 762Q502 783 517 798T553 813Q573 813 588 798T603 762ZM416 762Q416 742 401 727T365 712Q345 712 330 727T315 762Q315 783 330 798T365 813Q386 813 401 798T416 762Z" />
+<glyph unicode="&#x1E85;" horiz-adv-x="747" d="M576 0H483L374 373H370L261 0H168L25 502H115L218 86H222L337 502H408L524 86H528L634 502H722L576 0ZM517 622Q517 602 502 587T467 572Q446 572 431 587T416 622Q416 643 431 658T467 673Q487 673 502 658T517 622ZM330 622Q330 602 315 587T279 572Q259 572 244 587T229 622Q229 643 244 658T279 673Q300 673 315 658T330 622Z" />
+<glyph unicode="&#x1EF2;" horiz-adv-x="572" d="M332 265V0H241V262L13 667H116L287 345L459 667H559L332 265ZM294 705L120 814L208 854L343 735L294 705Z" />
+<glyph unicode="&#x1EF3;" horiz-adv-x="495" d="M266 -65Q248 -113 233 -144T198 -193T156 -219T98 -226Q77 -226 44 -223V-149Q52 -151 62 -151T88 -152Q107 -152 120 -148T143 -132T163 -100T183 -48L207 18L18 502H112L251 111H255L384 502H477L266 -65ZM261 565L87 674L175 714L310 595L261 565Z" />
+<glyph unicode="&#x2013;" horiz-adv-x="598" d="M49 226V294H549V226H49Z" />
+<glyph unicode="&#x2014;" horiz-adv-x="872" d="M49 227V293H823V227H49Z" />
+<glyph unicode="&#x2018;" horiz-adv-x="215" d="M126 491H19L104 700H167L126 491Z" />
+<glyph unicode="&#x2019;" horiz-adv-x="215" d="M110 491H48L89 700H196L110 491Z" />
+<glyph unicode="&#x201A;" horiz-adv-x="215" d="M110 -153H48L89 56H196L110 -153Z" />
+<glyph unicode="&#x201C;" horiz-adv-x="403" d="M314 491H208L293 700H355L314 491ZM126 491H19L105 700H167L126 491Z" />
+<glyph unicode="&#x201D;" horiz-adv-x="403" d="M298 491H236L277 700H384L298 491ZM110 491H48L89 700H196L110 491Z" />
+<glyph unicode="&#x201E;" horiz-adv-x="403" d="M298 -154H236L277 56H384L298 -154ZM110 -154H48L89 56H196L110 -154Z" />
+<glyph unicode="&#x2020;" horiz-adv-x="349" d="M218 473V171H132V473H47V544H132V675H218V544H302V473H218Z" />
+<glyph unicode="&#x2021;" horiz-adv-x="349" d="M217 280V136H131V280H47V352H131V479H47V551H131V675H217V551H302V479H217V352H302V280H217Z" />
+<glyph unicode="&#x2022;" horiz-adv-x="521" d="M414 329Q414 297 402 269T369 221T320 188T260 176Q228 176 200 188T152 220T119 269T107 329Q107 361 119 389T151 437T200 470T260 482Q292 482 320 470T369 438T402 389T414 329Z" />
+<glyph unicode="&#x2026;" horiz-adv-x="701" d="M165 43Q165 19 149 5T106 -10Q80 -10 64 4T47 43Q47 67 63 81T106 96Q133 96 149 82T165 43ZM410 43Q410 19 394 5T351 -10Q324 -10 308 4T291 43Q291 67 307 81T350 96Q377 96 393 82T410 43ZM654 43Q654 19 638 5T595 -10Q569 -10 553 4T536 43Q536 67 552 81T595 96Q622 96 638 82T654 43Z" />
+<glyph unicode="&#x2030;" horiz-adv-x="999" d="M282 547Q282 513 276 485T256 437T220 406T164 395Q131 395 109 406T73 437T53 485T47 547Q47 616 73 658T164 700Q230 700 256 658T282 547ZM227 548Q227 571 225 591T216 625T197 648T164 656Q144 656 132 648T113 626T104 591T102 547Q102 490 115 464T164 438Q199 438 213 464T227 548ZM179 0H123L552 700H607L179 0ZM646 146Q646 77 620 36T527 -6Q494 -6 472 5T437 36T417 84T411 146Q411 216 436 257T527 299Q593 299 619 257T646 146ZM590 147Q590 170 588 190T579 224T560 247T527 255Q507 255 495 247T476 225T467 190T465 147Q465 89 479 63T527 37Q562 37 576 63T590 147ZM952 146Q952 112 946 84T926 36T890 5T835 -6Q802 -6 780 5T743 36T723 84T717 146Q717 216 743 257T835 299Q900 299 926 257T952 146ZM897 147Q897 170 895 190T886 224T867 247T835 255Q815 255 803 247T783 225T774 190T772 147Q772 89 786 63T835 37Q869 37 883 63T897 147Z" />
+<glyph unicode="&#x2039;" horiz-adv-x="263" d="M159 110L19 306L159 502L219 469L98 305L219 144L159 110Z" />
+<glyph unicode="&#x203A;" horiz-adv-x="263" d="M105 110L45 144L165 305L45 469L105 502L244 306L105 110Z" />
+<glyph unicode="&#x2044;" horiz-adv-x="118" d="M-162 0H-215L273 700H326L-162 0Z" />
+<glyph unicode="&#x2070;" horiz-adv-x="349" d="M305 530Q305 492 299 461T277 408T236 373T174 361Q138 361 113 373T73 407T51 461T44 530Q44 568 50 599T72 653T113 688T174 700Q211 700 236 688T276 654T298 600T305 530ZM244 531Q244 557 242 579T232 617T210 642T174 651Q152 651 139 642T118 617T108 579T105 530Q105 466 120 438T174 409Q212 409 228 438T244 531Z" />
+<glyph unicode="&#x2074;" horiz-adv-x="317" d="M238 434V367H176V434H36L21 481L172 694H238V484H289V434H238ZM77 481H178V634L77 481Z" />
+<glyph unicode="&#x2075;" horiz-adv-x="312" d="M273 479Q273 423 240 392T144 361Q112 361 88 368T37 395L66 437Q102 408 141 408Q212 408 212 479Q212 544 148 544Q129 544 114 538T84 516L39 546L65 694H252V643H109L95 564H96Q126 587 166 587Q217 587 245 561T273 479Z" />
+<glyph unicode="&#x2076;" horiz-adv-x="326" d="M287 476Q287 423 255 392T169 361Q104 361 74 400T44 514Q44 553 50 586T72 645T114 685T181 700Q207 700 227 694T270 675L245 633Q230 644 216 648T182 653Q144 653 126 627T105 542Q130 578 181 578Q230 578 258 553T287 476ZM230 473Q230 502 215 519T168 536Q140 536 124 519T107 472Q107 442 123 426T169 409Q198 409 214 426T230 473Z" />
+<glyph unicode="&#x2077;" horiz-adv-x="288" d="M262 642Q229 616 209 594T176 545T160 481T155 393V367H90V381Q90 415 93 450T106 519T137 584T192 642V644H22V694H252L262 642H262Z" />
+<glyph unicode="&#x2078;" horiz-adv-x="324" d="M285 461Q285 439 279 421T259 390T221 369T162 361Q93 361 66 389T39 461Q39 517 96 540V541Q49 562 49 611Q49 649 74 672T162 696Q274 696 274 611Q274 561 229 541V540Q285 517 285 461ZM217 606Q217 653 162 653Q107 653 107 606Q107 583 121 571T162 558Q189 558 203 570T217 606ZM226 462Q226 485 212 501T162 518Q134 518 116 505T98 461Q98 435 114 420T162 405Q192 405 209 419T226 462Z" />
+<glyph unicode="&#x2079;" horiz-adv-x="327" d="M283 535Q283 451 252 406T146 361Q118 361 97 367T53 388L79 429Q96 418 110 413T147 407Q187 407 206 433T225 522H223Q213 503 193 493T147 483Q97 483 68 508T39 587Q39 640 72 670T159 700Q222 700 252 660T283 535ZM220 589Q220 619 205 636T158 654Q129 654 113 636T97 590Q97 560 112 544T157 527Q186 527 203 543T220 589Z" />
+<glyph unicode="&#x2080;" horiz-adv-x="349" d="M305 48Q305 10 299 -21T277 -74T236 -109T174 -121Q138 -121 113 -109T73 -75T51 -21T44 48Q44 86 50 117T72 171T113 206T174 218Q211 218 236 206T276 172T298 118T305 48ZM244 49Q244 75 242 97T232 135T210 160T174 169Q152 169 139 160T118 135T108 97T105 48Q105 -16 120 -44T174 -73Q212 -73 228 -44T244 49Z" />
+<glyph unicode="&#x2081;" horiz-adv-x="280" d="M117 -66V146H114L45 108L25 153L129 212H178V-66H257V-115H34V-66H117Z" />
+<glyph unicode="&#x2082;" horiz-adv-x="309" d="M41 -115L32 -61Q74 -26 106 -1T160 45T193 83T204 121Q204 138 194 152T154 167Q130 167 112 158T72 126L38 165Q67 194 96 206T160 218Q209 218 237 195T266 125Q266 105 260 87T235 47T185 -2T101 -65H270V-115H41Z" />
+<glyph unicode="&#x2083;" horiz-adv-x="302" d="M263 -22Q263 -67 235 -94T143 -121Q111 -121 83 -113T27 -83L53 -40Q76 -58 96 -65T137 -73Q166 -73 184 -60T203 -18Q203 9 189 20T143 31H100V75H124Q162 75 178 89T194 124Q194 144 181 156T141 169Q119 169 98 162T60 137L32 179Q62 201 88 209T147 218Q195 218 224 196T254 126Q254 103 241 86T207 60V59Q263 45 263 -22Z" />
+<glyph unicode="&#x2084;" horiz-adv-x="317" d="M238 -48V-115H176V-48H36L21 -1L172 212H238V2H289V-48H238ZM77 -1H178V152L77 -1Z" />
+<glyph unicode="&#x2085;" horiz-adv-x="312" d="M273 -3Q273 -59 240 -90T144 -121Q112 -121 88 -114T37 -87L66 -45Q102 -74 141 -74Q212 -74 212 -3Q212 62 148 62Q129 62 114 56T84 34L39 64L65 212H252V161H109L95 82H96Q126 105 166 105Q217 105 245 79T273 -3Z" />
+<glyph unicode="&#x2086;" horiz-adv-x="326" d="M287 -6Q287 -59 255 -90T169 -121Q104 -121 74 -82T44 32Q44 71 50 104T72 163T114 203T181 218Q207 218 227 212T270 193L245 151Q230 162 216 166T182 171Q144 171 126 145T105 60Q130 96 181 96Q230 96 258 71T287 -6ZM230 -9Q230 20 215 37T168 54Q140 54 124 37T107 -10Q107 -40 123 -56T169 -73Q198 -73 214 -56T230 -9Z" />
+<glyph unicode="&#x2087;" horiz-adv-x="288" d="M262 160Q229 134 209 112T176 63T160 -1T155 -89V-115H90V-101Q90 -67 93 -32T106 37T137 102T192 160V162H22V212H252L262 160H262Z" />
+<glyph unicode="&#x2088;" horiz-adv-x="324" d="M285 -21Q285 -43 279 -61T259 -92T221 -113T162 -121Q93 -121 66 -93T39 -21Q39 35 96 58V59Q49 80 49 129Q49 167 74 190T162 214Q274 214 274 129Q274 79 229 59V58Q285 35 285 -21ZM217 124Q217 171 162 171Q107 171 107 124Q107 101 121 89T162 76Q189 76 203 88T217 124ZM226 -20Q226 3 212 19T162 36Q134 36 116 23T98 -21Q98 -47 114 -62T162 -77Q192 -77 209 -63T226 -20Z" />
+<glyph unicode="&#x2089;" horiz-adv-x="327" d="M283 53Q283 -31 252 -76T146 -121Q118 -121 97 -115T53 -93L79 -52Q96 -64 110 -69T147 -75Q187 -75 206 -49T225 41H223Q213 22 193 12T147 1Q97 1 68 27T39 106Q39 158 72 188T159 218Q222 218 252 178T283 53ZM220 108Q220 137 205 154T158 172Q129 172 113 154T97 108Q97 79 112 63T157 46Q186 46 203 62T220 108Z" />
+<glyph unicode="&#x20AC;" horiz-adv-x="575" d="M96 432Q103 483 116 528T155 606T223 659T328 678Q393 678 441 654T534 578L480 524Q448 568 412 586T331 604Q293 604 268 594T225 563T199 510T184 432H450V379H182Q181 368 181 358T181 335Q181 325 181 311T182 286H450V233H186Q189 207 194 178T215 122T257 79T330 62Q386 62 422 87T491 157L542 102Q500 50 450 19T327 -12Q267 -12 227 5T160 54T119 131T96 233H21L22 286H92Q91 296 91 310T90 335Q90 346 90 356T92 379H21L22 432H96Z" />
+<glyph unicode="&#x2122;" horiz-adv-x="638" d="M521 466V612L441 498L360 612V466H311V700H356L441 577L525 700H571V466H521ZM178 656V466H126V656H47V700H257V656H178Z" />
+<glyph unicode="&#x2153;" horiz-adv-x="718" d="M145 416V628H142L73 590L53 635L157 694H206V416H285V367H62V416H145ZM153 0H100L588 700H641L153 0ZM681 93Q681 48 653 21T561 -6Q529 -6 501 2T445 32L471 75Q494 57 514 50T555 42Q584 42 602 55T621 97Q621 124 607 135T561 146H518V190H542Q580 190 596 204T612 239Q612 259 599 271T559 284Q537 284 516 277T478 252L450 294Q480 316 506 324T565 333Q613 333 642 311T672 241Q672 218 659 201T625 175V174Q681 160 681 93Z" />
+<glyph unicode="&#x2154;" horiz-adv-x="718" d="M61 367L52 421Q94 456 126 481T180 527T213 565T224 603Q224 620 214 634T174 649Q150 649 132 640T92 608L58 647Q87 676 116 688T180 700Q229 700 257 677T286 607Q286 587 280 569T255 529T205 480T121 417H290V367H61ZM153 0H100L588 700H641L153 0ZM681 93Q681 48 653 21T561 -6Q529 -6 501 2T445 32L471 75Q494 57 514 50T555 42Q584 42 602 55T621 97Q621 124 607 135T561 146H518V190H542Q580 190 596 204T612 239Q612 259 599 271T559 284Q537 284 516 277T478 252L450 294Q480 316 506 324T565 333Q613 333 642 311T672 241Q672 218 659 201T625 175V174Q681 160 681 93Z" />
+<glyph unicode="&#x215B;" horiz-adv-x="718" d="M145 416V628H142L73 590L53 635L157 694H206V416H285V367H62V416H145ZM153 0H100L588 700H641L153 0ZM687 94Q687 72 681 54T661 23T623 2T564 -6Q495 -6 468 22T441 94Q441 150 498 173V174Q451 195 451 244Q451 282 476 305T564 329Q676 329 676 244Q676 194 631 174V173Q687 150 687 94ZM619 239Q619 286 564 286Q509 286 509 239Q509 216 523 204T564 191Q591 191 605 203T619 239ZM628 95Q628 118 614 134T564 151Q536 151 518 138T500 94Q500 68 516 53T564 38Q594 38 611 52T628 95Z" />
+<glyph unicode="&#x215C;" horiz-adv-x="718" d="M284 460Q284 415 256 388T164 361Q132 361 104 369T48 399L74 442Q97 424 117 417T158 409Q187 409 205 422T224 464Q224 491 210 502T164 513H121V557H145Q183 557 199 571T215 606Q215 626 202 638T162 651Q140 651 119 644T81 619L53 661Q83 683 109 691T168 700Q216 700 245 678T275 608Q275 585 262 568T228 542V541Q284 527 284 460ZM153 0H100L588 700H641L153 0ZM687 94Q687 72 681 54T661 23T623 2T564 -6Q495 -6 468 22T441 94Q441 150 498 173V174Q451 195 451 244Q451 282 476 305T564 329Q676 329 676 244Q676 194 631 174V173Q687 150 687 94ZM619 239Q619 286 564 286Q509 286 509 239Q509 216 523 204T564 191Q591 191 605 203T619 239ZM628 95Q628 118 614 134T564 151Q536 151 518 138T500 94Q500 68 516 53T564 38Q594 38 611 52T628 95Z" />
+<glyph unicode="&#x215D;" horiz-adv-x="718" d="M294 479Q294 423 261 392T165 361Q133 361 109 368T58 395L87 437Q123 408 162 408Q233 408 233 479Q233 544 169 544Q150 544 135 538T105 516L60 546L86 694H273V643H130L116 564H117Q147 587 187 587Q238 587 266 561T294 479ZM153 0H100L588 700H641L153 0ZM687 94Q687 72 681 54T661 23T623 2T564 -6Q495 -6 468 22T441 94Q441 150 498 173V174Q451 195 451 244Q451 282 476 305T564 329Q676 329 676 244Q676 194 631 174V173Q687 150 687 94ZM619 239Q619 286 564 286Q509 286 509 239Q509 216 523 204T564 191Q591 191 605 203T619 239ZM628 95Q628 118 614 134T564 151Q536 151 518 138T500 94Q500 68 516 53T564 38Q594 38 611 52T628 95Z" />
+<glyph unicode="&#x215E;" horiz-adv-x="718" d="M320 642Q287 616 267 594T234 545T218 481T213 393V367H148V381Q148 415 151 450T164 519T195 584T250 642V644H80V694H310L320 642ZM153 0H100L588 700H641L153 0ZM687 94Q687 72 681 54T661 23T623 2T564 -6Q495 -6 468 22T441 94Q441 150 498 173V174Q451 195 451 244Q451 282 476 305T564 329Q676 329 676 244Q676 194 631 174V173Q687 150 687 94ZM619 239Q619 286 564 286Q509 286 509 239Q509 216 523 204T564 191Q591 191 605 203T619 239ZM628 95Q628 118 614 134T564 151Q536 151 518 138T500 94Q500 68 516 53T564 38Q594 38 611 52T628 95Z" />
+<glyph unicode="&#x2212;" horiz-adv-x="575" d="M51 224V300H523V224H51Z" />
+<glyph unicode="&#x2260;" horiz-adv-x="575" d="M387 319L282 207H523V132H223L132 30L77 74L131 132H52V207H191L295 319H52V394H355L450 501L506 458L446 394H523V319H387Z" />
+<glyph unicode="&#x2264;" horiz-adv-x="575" d="M51 276V355L523 529V448L141 316L523 188V109L51 276ZM51 0V73H523V0H51Z" />
+<glyph unicode="&#x2265;" horiz-adv-x="575" d="M51 109V188L434 316L51 448V529L523 355V276L51 109ZM51 0V73H523V0H51Z" />
+<glyph unicode="&#x24DF;" horiz-adv-x="866" d="M779 334Q779 262 752 200T678 91T568 17T433 -10Q361 -10 298 17T188 90T114 199T87 334Q87 406 114 468T188 577T298 651T433 678Q505 678 568 651T678 578T752 469T779 334ZM741 334Q741 397 718 453T653 552T555 618T433 643Q368 643 312 619T214 552T150 454T126 334Q126 271 150 216T216 118T314 52T433 28Q496 28 552 52T650 118T716 215T741 334ZM623 415Q623 346 586 312T467 278H369V111H301V544Q333 548 371 549T449 551Q488 551 520 544T575 522T610 480T623 415ZM555 412Q555 463 529 481T448 499Q428 499 410 498T369 495V328H448Q472 328 491 331T525 343T547 368T555 412Z" />
+<glyph unicode="&#xF6C3;" horiz-adv-x="358" d="M181 -213H121L148 -59H245L181 -213Z" />
+
+
+</font>
+</defs>
+<text x="40" y="40" font-family="ClearviewATT Bk" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="ClearviewATT Bk" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="ClearviewATT Bk" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="ClearviewATT Bk" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.ttf b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.ttf
new file mode 100644
index 0000000000..b4390461c6
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.woff b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.woff
new file mode 100644
index 0000000000..5373567803
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Bk.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.eot b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.eot
new file mode 100644
index 0000000000..4ab6695752
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.svg b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.svg
new file mode 100644
index 0000000000..c2337a7f89
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.svg
@@ -0,0 +1,425 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[ClearviewATT BkIt]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Copyright (c) Terminal Design, Inc, 2005. All rights reserved.]]></copyright>
+<trademark><![CDATA[Clearview is a trademark of Terminal Design, Inc.]]></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="509" id="ClearviewATT-BkIt">
+<font-face font-family="ClearviewATT BkIt" panose-1="2 11 5 6 3 5 0 2 0 4" ascent="750" descent="-250" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="484" d="M585 667L492 0H-8L85 667H585ZM116 606L40 65L264 336L116 606ZM462 67L538 605L315 336L462 67ZM65 35H428L284 299L65 35ZM294 373L507 630H153L294 373Z" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " horiz-adv-x="330" />
+<glyph unicode="!" horiz-adv-x="281" d="M285 667Q263 545 243 424T202 181H131Q145 303 158 424T184 667H285ZM211 55Q211 25 190 7T139 -11Q116 -11 99 0T82 37Q82 67 104 84T155 101Q177 101 194 90T211 55Z" />
+<glyph unicode="&quot;" horiz-adv-x="417" d="M394 495H335L346 700H444L394 495ZM206 495H147L158 700H256L206 495Z" />
+<glyph unicode="#" horiz-adv-x="623" d="M642 466H529L488 322H606L588 257H469L429 113H358L399 257H261L219 113H151L192 257H73L90 322H210L250 466H130L147 530H268L307 667H377L338 530H479L518 667H588L547 530H661L642 466ZM319 466L279 322H419L460 466H319Z" />
+<glyph unicode="$" horiz-adv-x="575" d="M533 228Q533 185 517 150T474 89T411 48T333 31L322 -47H238L249 31Q204 33 161 50T82 92L130 158Q167 131 209 114T299 96Q326 96 352 102T399 121T433 155T446 207Q446 239 425 260T371 298T300 329T230 363T176 409T154 477Q154 516 169 547T209 599T267 633T337 648L347 723H431L421 648Q459 646 494 634T562 601L520 533Q451 582 370 582Q347 582 325 578T284 564T254 535T242 490Q242 465 263 448T317 415T387 385T457 350T511 300T533 228Z" />
+<glyph unicode="%" horiz-adv-x="739" d="M386 580Q386 541 379 502T353 432T304 381T227 361Q168 361 143 392T117 480Q117 519 124 558T150 628T198 680T275 700Q335 700 360 669T386 580ZM234 409Q264 409 282 427T309 473T322 530T325 582Q325 612 314 631T268 651Q238 651 220 633T193 588T181 532T178 480Q178 450 189 430T234 409ZM179 0H123L650 700H705L179 0ZM718 213Q718 174 711 135T685 65T637 14T560 -6Q500 -6 475 25T449 114Q449 152 456 191T482 261T531 313T607 333Q666 333 692 302T718 213ZM566 42Q596 42 613 60T641 106T654 162T657 214Q657 244 646 264T600 284Q570 284 552 266T525 221T513 165T510 113Q510 83 521 63T566 42Z" />
+<glyph unicode="&amp;" horiz-adv-x="623" d="M288 -11Q248 -11 212 -3T147 25T103 76T86 152Q86 195 99 229T136 290T190 339T256 379Q233 408 215 443T196 516Q196 555 212 585T256 636T317 668T387 679Q447 679 485 649T524 557Q524 521 508 493T468 443T413 403T353 369L481 151Q497 181 502 218T507 289Q507 300 507 310T505 331H584Q585 323 585 317T585 302Q585 278 582 251T572 198T554 146T524 100L586 0H491L462 47Q386 -11 288 -11ZM297 54Q330 54 364 65T424 99L283 332Q260 318 240 302T204 265T179 221T170 168Q170 110 206 82T297 54ZM328 409Q348 421 369 435T407 468T436 507T448 555Q448 587 430 604T380 621Q333 621 304 596T275 528Q275 496 292 466T328 409Z" />
+<glyph unicode="&apos;" horiz-adv-x="218" d="M206 495H147L158 700H256L206 495Z" />
+<glyph unicode="(" horiz-adv-x="299" d="M160 -138Q124 -51 104 30T84 201Q84 352 137 491T288 772L346 758Q268 629 219 486T170 185Q170 104 183 29T222 -123L160 -138Z" />
+<glyph unicode=")" horiz-adv-x="299" d="M41 -123Q120 7 169 151T218 453Q218 534 205 609T165 758L227 772Q263 681 283 601T303 433Q303 279 249 138T99 -138L41 -123Z" />
+<glyph unicode="*" horiz-adv-x="336" d="M279 544L335 479L269 435L235 517L178 436L124 479L198 546L106 560L140 629L219 590L216 681H295L270 591L359 630L374 561L279 544Z" />
+<glyph unicode="+" horiz-adv-x="575" d="M359 224L330 16H253L282 224H82L93 300H293L321 499H398L370 300H565L554 224H359Z" />
+<glyph unicode="&#x2c;" horiz-adv-x="210" d="M70 -119H10L79 97H184L70 -119Z" />
+<glyph unicode="-" horiz-adv-x="376" d="M82 219L94 300H366L354 219H82Z" />
+<glyph unicode="." horiz-adv-x="210" d="M175 55Q175 25 154 7T103 -11Q80 -11 64 0T47 37Q47 67 68 84T119 101Q141 101 158 90T175 55Z" />
+<glyph unicode="/" horiz-adv-x="512" d="M84 -115H3L514 700H596L84 -115Z" />
+<glyph unicode="0" horiz-adv-x="575" d="M578 456Q578 425 576 395T568 334Q559 270 543 208T495 97T413 18T286 -12Q230 -12 193 3T132 48T99 119T89 212Q89 243 91 273T99 334Q108 399 124 461T172 571T255 648T383 678Q438 678 475 663T535 618T568 548T578 456ZM296 62Q350 62 383 88T435 155T463 244T478 334Q483 365 486 395T489 458Q489 489 484 516T466 562T430 593T372 604Q316 604 283 578T231 512T204 425T189 334Q184 302 181 271T178 207Q178 175 183 149T201 103T237 73T296 62Z" />
+<glyph unicode="1" horiz-adv-x="575" d="M269 76L338 569H334L172 479L145 546L367 669H441L357 76H516L506 0H94L104 76H269Z" />
+<glyph unicode="2" horiz-adv-x="575" d="M82 0L79 81Q161 145 232 197T357 299T441 397T472 505Q472 523 467 541T451 572T421 594T374 603Q329 603 285 580T193 512L154 576Q216 631 270 654T390 678Q470 678 516 638T563 520Q563 450 525 388T430 270T304 166T174 76H517L507 0H82Z" />
+<glyph unicode="3" horiz-adv-x="575" d="M537 230Q537 171 517 126T462 50T379 4T274 -12Q216 -12 168 4T74 57L124 123Q156 94 196 78T281 61Q358 61 402 102T446 222Q446 252 436 269T407 296T364 308T313 311H248L258 384Q293 384 330 387T399 404T451 443T471 515Q471 540 462 557T438 584T402 598T359 603Q313 603 271 585T193 540L163 608Q216 642 266 660T380 678Q416 678 449 670T507 645T548 599T563 530Q563 467 533 422T443 355V351Q493 341 515 310T537 230Z" />
+<glyph unicode="4" horiz-adv-x="575" d="M457 143L437 0H347L367 143H90L76 218L432 665H530L467 219H558L548 143H457ZM427 564L167 219H382L431 564H427Z" />
+<glyph unicode="5" horiz-adv-x="575" d="M543 284Q543 216 526 162T474 69T388 9T267 -12Q213 -12 166 2T77 51L124 120Q156 94 193 78T273 61Q321 61 354 77T410 120T442 186T452 269Q452 330 421 357T330 384Q285 384 250 368T182 323L120 370L213 667H567L557 591H273L211 405L214 402Q249 428 284 442T363 456Q450 456 496 415T543 284Z" />
+<glyph unicode="6" horiz-adv-x="575" d="M553 259Q553 201 534 152T480 66T397 9T290 -12Q183 -12 136 41T88 199Q88 223 90 246T95 293Q105 363 123 432T177 555T268 644T408 678Q450 678 489 667T564 633L517 565Q488 585 461 594T398 603Q342 603 305 581T245 522T208 439T188 346H192Q224 391 268 411T366 432Q408 432 442 421T501 389T539 335T553 259ZM300 62Q341 62 371 76T422 116T452 176T462 248Q462 307 431 332T342 358Q303 358 272 344T219 306T186 250T174 179Q174 119 208 91T300 62Z" />
+<glyph unicode="7" horiz-adv-x="575" d="M592 592Q518 536 471 484T394 371T347 237T316 65L307 0H214L218 30Q227 98 242 174T287 325T363 467T484 587V591H148L158 667H588L592 592H592Z" />
+<glyph unicode="8" horiz-adv-x="575" d="M548 226Q548 166 529 121T474 47T391 3T286 -12Q244 -12 206 -5T140 21T95 71T78 150Q78 225 110 277T214 355L215 359Q177 375 159 404T140 475Q140 529 159 567T211 630T288 666T382 678Q418 678 452 672T514 651T557 608T574 538Q574 476 546 429T458 359L457 355Q503 340 525 307T548 226ZM341 388Q407 388 446 421T485 523Q485 549 476 565T451 590T415 602T372 606Q305 606 267 576T228 475Q228 449 237 432T262 406T298 392T341 388ZM295 59Q458 59 458 223Q458 253 448 272T421 302T381 317T332 321Q169 321 169 155Q169 125 179 107T206 78T246 63T295 59Z" />
+<glyph unicode="9" horiz-adv-x="575" d="M576 454Q576 427 574 401T568 348Q558 278 540 214T487 99T399 19T263 -11Q215 -11 172 2T93 48L146 109Q176 83 204 72T273 61Q332 61 369 85T428 149T462 236T481 333H477Q448 287 404 261T305 235Q220 235 167 280T114 414Q114 472 133 520T188 604T272 658T378 678Q433 678 471 663T532 618T565 548T576 454ZM327 310Q366 310 396 324T446 362T477 418T487 488Q487 545 456 574T368 604Q329 604 299 590T248 552T216 495T205 425Q205 367 238 339T327 310Z" />
+<glyph unicode=":" horiz-adv-x="210" d="M225 413Q225 383 204 365T154 347Q131 347 116 359T100 396Q100 425 121 442T170 459Q192 459 208 448T225 413ZM175 55Q175 25 154 7T104 -11Q81 -11 65 1T49 37Q49 67 70 84T120 101Q142 101 158 90T175 55Z" />
+<glyph unicode=";" horiz-adv-x="210" d="M70 -119H10L78 97H180L70 -119ZM234 413Q234 383 213 365T163 346Q140 346 124 358T108 394Q108 424 129 441T179 458Q201 458 217 448T234 413Z" />
+<glyph unicode="&lt;" horiz-adv-x="575" d="M82 223L93 300L595 515L584 436L171 262L535 92L525 15L82 223Z" />
+<glyph unicode="=" horiz-adv-x="575" d="M96 319L107 394H578L567 319H96ZM70 132L81 207H552L541 132H70Z" />
+<glyph unicode="&gt;" horiz-adv-x="575" d="M53 15L63 92L476 262L112 436L123 515L565 300L554 223L53 15Z" />
+<glyph unicode="?" horiz-adv-x="515" d="M534 531Q534 479 518 444T475 384T411 341T332 305Q315 298 306 293T291 280T283 263T278 234L270 172H184L195 251Q198 275 203 290T216 317T239 336T275 354Q305 367 335 379T390 409T429 452T445 517Q445 542 436 558T410 585T374 599T331 604Q284 604 245 588T167 545L133 610Q187 643 238 660T352 678Q388 678 420 670T478 645T519 600T534 531ZM269 53Q269 24 249 7T200 -11Q178 -11 162 0T145 36Q145 65 165 82T214 99Q236 99 252 88T269 53Z" />
+<glyph unicode="@" horiz-adv-x="803" d="M786 345Q786 301 772 250T731 156T664 83T572 54Q545 54 526 70T507 115H502Q476 85 441 68T366 51Q311 51 284 82T257 167Q257 211 272 256T314 338T381 398T468 421Q496 421 517 412T552 377H556L567 413H640Q637 404 631 381T616 329T598 267T582 206T569 157T564 131Q564 102 595 102Q608 102 620 107T642 120Q664 135 680 155T707 198T725 247T736 297Q739 318 739 339Q739 391 721 429T671 492T597 529T507 542Q427 542 360 514T243 435T167 316T139 168Q139 110 156 67T206 -6T284 -50T386 -65Q444 -65 496 -51T598 -6L617 -43Q560 -79 500 -93T373 -107Q307 -107 255 -89T166 -35T110 52T90 170Q90 260 122 336T211 467T345 554T511 585Q567 585 617 571T704 526T764 451T786 345ZM396 105Q443 105 472 135T513 207Q520 232 528 259T537 313Q537 345 517 355T469 366Q433 366 407 348T362 302T336 240T327 175Q327 105 396 105Z" />
+<glyph unicode="A" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582Z" />
+<glyph unicode="B" horiz-adv-x="636" d="M603 212Q603 160 586 121T537 54T461 14T362 0H86L179 663Q222 668 279 671T391 675Q431 675 472 671T546 653T600 610T621 532Q621 470 590 421T493 352L492 348Q545 338 574 304T603 212ZM356 379Q391 379 423 384T479 403T517 444T531 515Q531 542 520 559T490 586T443 599T382 603Q356 603 323 602T260 597L230 380Q259 379 291 379T356 379ZM314 68Q353 68 389 72T452 92T497 135T514 213Q514 249 499 269T459 298T404 310T344 312Q303 312 276 312T219 311L185 70Q219 68 248 68T314 68Z" />
+<glyph unicode="C" horiz-adv-x="632" d="M611 83Q555 38 495 14T363 -11Q234 -11 170 58T106 254Q106 335 128 411T194 547T306 642T463 678Q529 678 580 656T678 586L614 526Q580 563 543 582T455 601Q385 601 337 571T259 492T216 384T202 263Q202 219 211 183T242 121T296 80T377 65Q432 65 480 87T571 145L611 83H611Z" />
+<glyph unicode="D" horiz-adv-x="669" d="M656 411Q656 325 633 251T564 120T449 32T289 0H86L179 663Q230 668 280 671T382 675Q511 675 583 609T656 411ZM283 74Q349 74 396 92T476 144T528 227T556 339Q558 356 559 372T561 405Q561 498 516 550T374 602Q345 602 317 600T260 596L187 75Q211 74 235 74T283 74Z" />
+<glyph unicode="E" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86Z" />
+<glyph unicode="F" horiz-adv-x="510" d="M259 590L231 387H502L491 309H220L177 0H86L179 667H570L559 590H259Z" />
+<glyph unicode="G" horiz-adv-x="698" d="M378 60Q466 60 517 107T582 235L589 277H411L422 352H682L672 278Q652 135 575 63T364 -10Q298 -10 250 10T169 66T122 151T106 261Q106 338 129 413T197 547T309 642T465 678Q540 678 590 652T676 591L613 530Q575 568 537 584T457 601Q386 601 338 571T258 491T214 381T200 259Q200 218 209 182T238 118T293 76T378 60Z" />
+<glyph unicode="H" horiz-adv-x="662" d="M485 0L529 314H221L177 0H86L179 667H270L233 399H541L578 667H669L576 0H485Z" />
+<glyph unicode="I" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86Z" />
+<glyph unicode="J" horiz-adv-x="448" d="M386 177Q373 86 327 37T189 -12Q146 -12 112 -1T45 38L94 108Q119 84 141 75T191 66Q238 66 262 91T294 170L364 667H455L386 177H386Z" />
+<glyph unicode="K" horiz-adv-x="579" d="M448 0L291 321L211 242L177 0H86L179 667H270L226 352H230L521 667H635L367 384L561 0H448Z" />
+<glyph unicode="L" horiz-adv-x="498" d="M86 0L179 667H270L187 78H468L458 0H86Z" />
+<glyph unicode="M" horiz-adv-x="767" d="M593 0L665 518H661L410 198L250 516H246L174 0H86L179 667H263L430 337L689 667H774L681 0H593Z" />
+<glyph unicode="N" horiz-adv-x="701" d="M530 0L251 520H247L174 0H86L179 667H269L544 154H548L620 667H708L615 0H530Z" />
+<glyph unicode="O" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63Z" />
+<glyph unicode="P" horiz-adv-x="602" d="M618 509Q618 452 603 406T556 325T479 273T369 254H212L177 0H86L179 663Q228 668 281 671T389 675Q438 675 480 667T553 640T601 589T618 509ZM352 327Q393 327 425 335T479 364T513 416T525 497Q525 556 489 578T379 601Q349 601 321 600T260 594L222 327H352Z" />
+<glyph unicode="Q" horiz-adv-x="746" d="M733 415Q733 361 723 306T691 200T634 107T548 38V34Q565 29 577 21T602 4T630 -13T668 -26L630 -94Q599 -91 573 -77T524 -48T479 -20T437 -8Q426 -8 409 -9T375 -11Q244 -11 175 56T105 256Q105 340 127 416T195 551T308 643T468 678Q598 678 665 612T733 415ZM381 64Q454 64 503 95T582 175T625 287T638 411Q638 502 596 552T457 603Q384 603 335 573T256 494T214 384T201 260Q201 169 243 117T381 64Z" />
+<glyph unicode="R" horiz-adv-x="604" d="M459 0L405 240Q400 262 389 270T354 279H216L177 0H86L179 664Q233 670 279 672T380 675Q423 675 464 668T537 643T588 595T607 517Q607 482 599 449T574 389T530 340T467 307V303Q477 298 483 286T492 261L555 0H459ZM362 352Q434 352 474 388T515 502Q515 532 504 551T474 582T428 597T372 602Q342 602 316 601T260 595L226 352H362Z" />
+<glyph unicode="S" horiz-adv-x="539" d="M516 205Q516 150 497 110T443 42T364 2T267 -11Q202 -11 149 6T44 62L96 130Q134 98 177 80T271 62Q301 62 328 69T377 92T410 131T422 188Q422 222 400 244T345 285T273 319T201 358T146 411T124 489Q124 540 143 576T196 634T272 667T361 677Q466 677 554 618L510 545Q476 572 437 587T353 603Q329 603 305 598T261 582T229 552T217 505Q217 472 239 451T294 413T366 381T438 343T494 288T516 205Z" />
+<glyph unicode="T" horiz-adv-x="505" d="M380 588L298 0H207L289 588H101L112 667H579L568 588H380Z" />
+<glyph unicode="U" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619Z" />
+<glyph unicode="V" horiz-adv-x="576" d="M338 0H237L115 667H212L301 101H305L552 667H647L338 0Z" />
+<glyph unicode="W" horiz-adv-x="916" d="M712 0H608L533 538H529L307 0H196L123 667H223L270 105H275L506 667H598L671 105H675L882 667H979L712 0Z" />
+<glyph unicode="X" horiz-adv-x="564" d="M441 0L320 269H316L122 0H17L278 346L135 667H241L342 415H346L523 667H628L387 339L547 0H441Z" />
+<glyph unicode="Y" horiz-adv-x="572" d="M369 265L332 0H241L277 262L106 667H209L335 345L552 667H652L369 265Z" />
+<glyph unicode="Z" horiz-adv-x="558" d="M46 0L56 75L480 588H143L154 667H596L586 593L161 76H522L512 0H46Z" />
+<glyph unicode="[" horiz-adv-x="310" d="M43 -104L165 762H350L342 706H237L131 -48H236L228 -104H43Z" />
+<glyph unicode="\" horiz-adv-x="512" d="M386 -115L115 667H198L468 -115H386Z" />
+<glyph unicode="]" horiz-adv-x="310" d="M47 -104L55 -48H158L264 706H161L169 762H351L229 -104H47Z" />
+<glyph unicode="^" horiz-adv-x="366" d="M385 525L265 602L119 525L83 571L275 676L439 571L385 525Z" />
+<glyph unicode="_" horiz-adv-x="484" d="M-28 -144L-19 -85H480L471 -144H-28Z" />
+<glyph unicode="`" horiz-adv-x="358" d="M314 565L155 674L249 714L367 595L314 565Z" />
+<glyph unicode="a" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326Z" />
+<glyph unicode="b" horiz-adv-x="564" d="M551 324Q551 270 539 211T498 102T423 21T309 -11Q254 -11 218 12T169 80H165L141 0H76L174 700H261L224 441H228Q257 483 297 498T382 514Q430 514 462 499T514 459T542 399T551 324ZM296 54Q345 54 377 80T429 146T455 233T463 322Q463 347 458 370T441 409T407 436T352 446Q295 446 255 416T207 326L190 200Q188 192 188 184T188 168Q188 118 214 86T296 54Z" />
+<glyph unicode="c" horiz-adv-x="495" d="M481 390Q433 445 356 445Q307 445 272 423T215 367T183 289T173 203Q173 139 203 99T302 58Q345 58 379 72T448 112L470 51Q428 22 383 6T287 -11Q189 -11 136 42T83 194Q83 257 101 315T155 417T243 488T363 514Q409 514 451 498T528 451L481 390Z" />
+<glyph unicode="d" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 404 500T459 444H463L499 700H586L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326Z" />
+<glyph unicode="e" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451Z" />
+<glyph unicode="f" horiz-adv-x="319" d="M394 634Q383 636 372 637T347 639Q324 639 310 636T288 626T276 605T269 571L259 502H367L357 432H252L192 0H104L164 431H87L97 502H174L184 576Q189 610 198 634T224 674T269 697T342 705Q356 705 371 704T403 700L394 634H394Z" />
+<glyph unicode="g" horiz-adv-x="564" d="M482 -43Q473 -105 453 -141T405 -196T343 -220T273 -226Q148 -226 61 -175L97 -107Q137 -132 177 -145T268 -158Q298 -158 319 -154T356 -137T379 -105T393 -52L412 79H408Q376 33 340 15T256 -4Q209 -4 176 10T123 49T93 110T83 186Q83 239 94 297T134 403T208 482T326 514Q376 514 410 498T466 436H470L484 502H558L482 -43ZM447 325Q456 386 425 416T336 446Q286 446 255 423T205 363T180 283T173 196Q173 168 178 144T196 102T230 74T283 63Q316 63 341 74T385 105T415 151T431 208L447 325Z" />
+<glyph unicode="h" horiz-adv-x="564" d="M401 0L442 294Q444 311 446 327T448 356Q448 395 429 417T354 440Q291 440 253 408T207 315L163 0H76L174 700H261L224 440H228Q255 478 295 496T388 514Q472 514 504 479T536 378Q536 363 535 347T531 313L488 0H401Z" />
+<glyph unicode="i" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM262 636Q262 609 244 593T200 577Q180 577 166 587T151 619Q151 645 170 660T214 676Q234 676 248 667T262 636Z" />
+<glyph unicode="j" horiz-adv-x="245" d="M267 635Q267 608 249 593T205 577Q186 577 172 588T157 619Q157 645 175 660T219 676Q239 676 253 667T267 635ZM163 -39Q151 -126 106 -176T-32 -226Q-49 -226 -65 -225T-99 -222L-88 -148Q-61 -151 -34 -151Q-3 -151 17 -143T49 -119T67 -81T77 -31L152 502H239L163 -39Z" />
+<glyph unicode="k" horiz-adv-x="504" d="M380 0L270 247L185 162L163 0H76L174 700H261L199 263H203L430 502H540L341 304L485 0H380Z" />
+<glyph unicode="l" horiz-adv-x="272" d="M244 0Q232 -2 214 -3T176 -5Q130 -5 109 11T87 67Q87 74 87 81T89 97L174 700H261L179 119Q174 83 184 76T222 69H253L244 0H244Z" />
+<glyph unicode="m" horiz-adv-x="841" d="M678 0L720 304Q722 318 723 331T725 357Q725 395 708 417T642 440Q586 440 552 412T509 326L464 0H377L420 308Q422 322 423 334T424 357Q424 396 406 418T337 440Q311 440 289 431T250 406T222 369T208 323L163 0H76L146 502H221V438H225Q249 473 285 493T371 514Q419 514 452 496T497 434Q529 478 570 496T661 514Q744 514 778 476T813 371Q813 357 812 343T808 313L765 0H678Z" />
+<glyph unicode="n" horiz-adv-x="564" d="M401 0L442 294Q444 310 445 324T447 352Q447 372 443 388T428 416T399 434T353 440Q290 440 253 408T207 315L163 0H76L146 502H219V439H223Q282 514 386 514Q429 514 458 505T504 478T529 435T537 376Q537 362 536 347T532 314L488 0H401Z" />
+<glyph unicode="o" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445Z" />
+<glyph unicode="p" horiz-adv-x="564" d="M551 324Q551 267 539 208T497 100T420 20T303 -11Q252 -11 223 9T176 67H172L132 -220H45L146 502H217V438H221Q253 480 292 497T380 514Q429 514 461 499T514 459T542 399T551 324ZM294 54Q343 54 376 79T428 144T455 230T463 322Q463 348 458 370T440 410T406 436T350 446Q293 446 255 416T207 325L190 201Q188 193 188 185T188 168Q188 118 213 86T294 54Z" />
+<glyph unicode="q" horiz-adv-x="564" d="M447 -237Q412 -225 397 -207T381 -153Q381 -139 384 -119L410 67H406Q377 25 340 7T254 -11Q206 -11 174 4T121 46T92 108T83 184Q83 238 95 296T135 403T209 483T326 514Q371 514 408 499T465 438H469L485 502H558L473 -101Q468 -132 473 -146T504 -173L447 -237ZM447 326Q455 385 425 415T336 446Q285 446 253 422T203 361T179 278T172 190Q172 163 177 139T195 95T229 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326Z" />
+<glyph unicode="r" horiz-adv-x="365" d="M378 436Q307 436 262 411T207 318L163 0H76L146 502H219V438H223Q251 477 290 495T385 513H411L400 436H378Z" />
+<glyph unicode="s" horiz-adv-x="450" d="M429 157Q429 112 413 81T370 29T307 -1T230 -11Q179 -11 131 1T40 42L81 106Q113 83 153 69T234 55Q255 55 274 59T310 73T335 99T345 138Q345 164 327 180T282 207T223 229T164 255T118 295T100 360Q100 401 116 430T160 478T223 505T297 514Q339 514 380 505T458 475L424 409Q397 427 362 436T295 446Q277 446 257 443T221 433T194 411T183 374Q183 351 201 337T246 311T306 289T365 262T411 221T429 157Z" />
+<glyph unicode="t" horiz-adv-x="352" d="M310 -1Q294 -3 278 -4T246 -6Q208 -6 184 0T145 17T125 45T119 84Q119 103 122 126L165 431H82L92 502H176L195 640H281L262 502H381L371 431H252L211 139Q208 121 208 109Q208 86 223 77T288 67Q297 67 303 67T320 68L310 -1H310Z" />
+<glyph unicode="u" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406Z" />
+<glyph unicode="v" horiz-adv-x="485" d="M296 0H191L90 502H183L254 77H258L445 502H535L296 0Z" />
+<glyph unicode="w" horiz-adv-x="747" d="M576 0H483L426 373H422L261 0H168L95 502H185L230 86H234L407 502H478L536 86H540L704 502H792L576 0Z" />
+<glyph unicode="x" horiz-adv-x="475" d="M361 0L269 207H265L115 0H15L224 265L105 502H204L284 320H288L419 502H512L329 268L460 0H361Z" />
+<glyph unicode="y" horiz-adv-x="495" d="M256 -65Q232 -113 212 -144T170 -193T125 -219T66 -226Q45 -226 12 -223L23 -149Q31 -151 40 -151T66 -152Q85 -152 98 -148T124 -132T148 -100T176 -48L209 18L88 502H182L266 111H270L454 502H547L256 -65Z" />
+<glyph unicode="z" horiz-adv-x="454" d="M39 0L48 68L368 431H105L115 502H477L467 431L148 71H424L415 0H39Z" />
+<glyph unicode="{" horiz-adv-x="348" d="M219 -104Q161 -104 139 -82T117 -16Q117 -6 118 5T121 28L147 207Q152 239 145 261T105 292L114 361Q150 369 162 391T180 446L206 631Q210 662 218 687T242 729T281 755T339 764H388L380 706H354Q331 706 319 700T300 682T291 656T285 621L259 435Q256 415 252 401T239 374T219 350T189 327Q212 307 221 291T231 250Q231 236 228 218L203 39Q201 25 200 14T199 -7Q199 -29 209 -38T248 -47H274L266 -104H219Z" />
+<glyph unicode="|" horiz-adv-x="233" d="M52 -202L185 740H254L121 -202H52Z" />
+<glyph unicode="}" horiz-adv-x="348" d="M325 292Q288 284 276 262T258 207L232 28Q227 -4 219 -28T196 -70T158 -95T99 -104H52L60 -47H86Q107 -47 119 -43T138 -28T148 -2T155 39L180 218Q183 238 187 253T199 280T218 303T248 327Q226 346 217 362T208 402Q208 415 211 435L237 621Q239 634 240 645T242 666Q242 685 232 695T193 706H166L174 764H222Q278 764 299 741T321 675Q321 665 320 654T317 631L291 446Q286 414 294 392T334 361L325 292H325Z" />
+<glyph unicode="~" horiz-adv-x="415" d="M492 622Q472 586 442 568T373 550Q350 550 330 556T290 569T251 583T212 590Q191 590 175 582T142 548L92 572Q120 613 147 628T213 644Q235 644 255 638T294 624T332 610T371 604Q391 604 408 613T439 644L492 622H492Z" />
+<glyph unicode="&#xA0;" horiz-adv-x="350" />
+<glyph unicode="&#xA1;" horiz-adv-x="281" d="M82 -163Q104 -41 124 80T165 323H236Q222 201 209 80T183 -163H82ZM156 449Q156 479 177 497T228 515Q251 515 268 504T285 467Q285 437 263 420T212 403Q190 403 173 414T156 449Z" />
+<glyph unicode="&#xA2;" horiz-adv-x="575" d="M514 148Q480 121 442 103T361 78L351 0H269L279 78Q237 83 208 100T159 145T131 207T122 281Q122 336 135 389T177 485T249 557T352 595L362 667H444L434 596Q472 592 505 577T567 537L520 475Q470 529 394 529Q343 529 308 508T252 452T222 374T212 287Q212 255 219 229T241 185T279 156T337 145Q380 145 415 161T484 204L514 148H514Z" />
+<glyph unicode="&#xA3;" horiz-adv-x="575" d="M512 0H63L72 71H92Q125 71 148 86T184 126T204 183T210 248Q210 268 209 287T206 325H122L132 394H205Q202 421 200 444T197 491Q197 580 249 629T402 678Q450 678 493 667T572 632L540 557Q513 577 481 590T406 603Q340 603 312 573T284 480Q284 460 286 439T289 394H440L430 325H292Q295 303 297 283T299 242Q299 195 287 154T236 79L235 76H522L512 0Z" />
+<glyph unicode="&#xA4;" horiz-adv-x="575" d="M127 311Q127 352 142 390T185 458L125 529L178 576L239 504Q267 522 298 532T362 542Q392 542 421 533T473 503L556 575L597 528L515 457Q539 416 539 368Q539 327 524 290T482 222L544 150L490 103L428 175Q401 157 370 147T305 137Q275 137 246 146T193 175L112 104L72 151L152 221Q127 261 127 311ZM351 468Q322 468 296 456T249 424T217 378T205 322Q205 274 236 244T315 213Q344 213 370 225T417 257T450 303T462 358Q462 382 454 402T430 437T395 460T351 468Z" />
+<glyph unicode="&#xA5;" horiz-adv-x="575" d="M440 362H564L556 302H397L372 267L363 202H542L533 142H354L335 0H245L264 142H83L92 202H273L281 262L264 302H106L114 362H236L122 667H225L339 342L541 667H639L440 362Z" />
+<glyph unicode="&#xA6;" horiz-adv-x="233" d="M128 335L185 740H254L197 335H128ZM52 -202L109 203H178L121 -202H52Z" />
+<glyph unicode="&#xA7;" horiz-adv-x="474" d="M470 323Q470 287 457 261T422 218T371 192T309 181V177Q333 169 355 158T394 132T422 95T433 46Q433 3 415 -27T369 -76T303 -104T229 -113Q178 -113 137 -102T52 -63L92 2Q123 -20 160 -33T235 -47Q256 -47 275 -43T309 -29T332 -3T341 37Q341 61 322 78T275 108T214 135T152 165T105 206T86 265Q86 300 99 325T135 368T187 393T248 404L249 407Q229 414 209 425T172 451T145 485T134 529Q134 570 153 598T201 644T266 670T336 678Q378 678 416 670T492 644L458 574Q429 591 398 600T333 610Q316 610 297 607T261 597T235 575T224 539Q224 515 242 499T287 469T347 445T406 417T452 379T470 323ZM266 213Q289 213 310 217T348 230T375 257T385 302Q385 322 376 334T352 352T320 360T287 362Q265 362 244 359T206 346T179 319T168 275Q168 255 177 243T201 225T233 216T266 213Z" />
+<glyph unicode="&#xA8;" horiz-adv-x="358" d="M411 629Q411 606 394 589T353 572Q335 572 322 584T308 615Q308 638 326 655T367 673Q386 673 398 661T411 629ZM224 629Q224 606 206 589T165 572Q147 572 134 584T121 615Q121 638 138 655T179 673Q198 673 211 661T224 629Z" />
+<glyph unicode="&#xA9;" horiz-adv-x="866" d="M779 334Q779 262 752 200T678 91T568 17T433 -10Q361 -10 298 17T188 90T114 199T87 334Q87 406 114 468T188 577T298 651T433 678Q505 678 568 651T678 578T752 469T779 334ZM741 334Q741 397 718 453T653 552T555 618T433 643Q368 643 312 619T214 552T150 454T126 334Q126 271 150 216T216 118T314 52T433 28Q496 28 552 52T650 118T716 215T741 334ZM599 178Q565 148 527 131T442 114Q384 114 346 130T284 176T251 246T241 335Q241 385 252 425T289 494T352 537T444 552Q491 552 528 536T599 489L564 444Q539 470 510 484T445 498Q404 498 377 487T335 454T313 403T306 336Q306 298 312 267T334 214T376 181T443 169Q479 169 508 183T566 223L599 178Z" />
+<glyph unicode="&#xAA;" horiz-adv-x="390" d="M347 370Q334 374 325 381T313 403H309Q286 387 260 378T205 368Q169 368 146 386T122 443Q122 485 142 510T194 550T262 568T331 573Q332 581 333 588T334 602Q334 620 323 627T289 634Q247 634 216 623T169 603L154 646Q189 663 224 672T297 682Q339 682 366 667T393 605Q393 587 390 565T382 519T375 473T371 435Q371 425 376 420T389 410L347 370ZM326 536Q306 536 281 533T233 521T196 496T181 452Q181 432 193 421T225 409Q249 409 270 418T312 441L326 536Z" />
+<glyph unicode="&#xAB;" horiz-adv-x="454" d="M364 110L253 306L419 502L474 469L331 305L428 144L364 110ZM177 104L62 306L233 508L287 475L140 305L241 138L177 104Z" />
+<glyph unicode="&#xAC;" horiz-adv-x="575" d="M479 224H82L93 300H565L529 44H454L479 224Z" />
+<glyph unicode="&#xAD;" horiz-adv-x="376" d="M82 219L94 300H366L354 219H82Z" />
+<glyph unicode="&#xAE;" horiz-adv-x="453" d="M297 361Q261 361 229 374T173 411T136 464T122 530Q122 565 135 596T173 650T229 686T297 700Q333 700 364 687T420 650T457 596T471 530Q471 495 458 465T420 411T365 375T297 361ZM298 676Q266 676 239 665T191 633T159 587T147 531Q147 502 159 476T191 429T239 398T297 386Q327 386 354 397T402 428T435 474T447 531Q447 560 435 586T403 633T356 664T298 676ZM232 624Q252 626 267 627T298 628Q312 628 326 626T351 616T368 598T375 568Q375 551 367 539T345 519L379 439H336L310 503Q308 507 307 508T300 509H270V439H232V624ZM299 595Q291 595 284 595T270 594V539H301Q336 539 336 566Q336 595 299 595Z" />
+<glyph unicode="&#xAF;" horiz-adv-x="358" d="M111 589L119 646H417L409 589H111Z" />
+<glyph unicode="&#xB0;" horiz-adv-x="410" d="M426 559Q426 522 414 492T379 441T327 409T263 398Q236 398 213 407T172 432T144 470T134 519Q134 556 146 585T180 636T232 668T297 680Q325 680 349 671T390 646T416 608T426 559ZM265 433Q320 433 352 465T385 554Q385 593 363 618T295 644Q239 644 207 611T175 524Q175 485 200 459T265 433Z" />
+<glyph unicode="&#xB1;" horiz-adv-x="575" d="M367 282L344 114H267L290 282H90L101 357H301L327 546H404L378 357H573L562 282H367ZM51 0L61 73H533L523 0H51Z" />
+<glyph unicode="&#xB2;" horiz-adv-x="309" d="M93 367L91 421Q108 434 141 454T207 499T264 551T289 605Q289 619 280 634T245 649Q221 649 201 640T157 608L128 647Q162 676 192 688T258 700Q303 700 327 677T352 616Q352 583 334 556T288 506T226 461T159 417H328L321 367H93Z" />
+<glyph unicode="&#xB3;" horiz-adv-x="302" d="M328 478Q328 422 294 392T197 361Q165 361 136 369T83 399L115 442Q135 424 154 417T198 409Q226 409 247 425T269 475Q269 513 215 513H172L178 557H206Q216 557 229 559T253 568T271 585T279 612Q279 630 267 640T232 651Q210 651 188 644T146 619L124 661Q157 683 184 691T245 700Q288 700 314 682T340 625Q340 569 283 544V540Q328 528 328 478Z" />
+<glyph unicode="&#xB4;" horiz-adv-x="358" d="M202 565L157 596L311 717L391 675L202 565Z" />
+<glyph unicode="&#xB5;" horiz-adv-x="581" d="M417 0V63H412Q389 32 357 11T266 -11Q253 -11 239 -10T212 -3T189 12T171 36L135 -220H56L157 502H245L208 238Q204 213 202 191T200 151Q200 108 218 86T289 63Q359 63 390 99T430 194L473 502H561L491 0H417Z" />
+<glyph unicode="&#xB6;" horiz-adv-x="488" d="M321 0Q270 0 223 11T140 48T82 114T60 212Q60 282 87 336T160 427T265 483T391 502H458L388 0H321Z" />
+<glyph unicode="&#xB7;" horiz-adv-x="210" d="M201 247Q201 217 180 199T129 181Q106 181 90 193T73 229Q73 259 94 276T145 293Q167 293 184 282T201 247Z" />
+<glyph unicode="&#xB8;" horiz-adv-x="358" d="M257 -110Q257 -160 225 -183T148 -206Q124 -206 104 -200T66 -182L86 -147Q100 -155 115 -160T148 -166Q172 -166 189 -156T206 -118Q206 -99 192 -92T161 -85Q140 -85 123 -92L107 -77L158 0H205L165 -54Q173 -52 180 -51T196 -50Q220 -50 238 -63T257 -110Z" />
+<glyph unicode="&#xB9;" horiz-adv-x="280" d="M175 416L205 628H201L127 590L114 635L226 694H275L236 416H315L308 367H85L92 416H175Z" />
+<glyph unicode="&#xBA;" horiz-adv-x="390" d="M414 560Q414 521 404 486T372 425T320 384T246 368Q186 368 155 400T123 492Q123 531 133 565T165 626T217 667T290 682Q349 682 381 651T414 560ZM254 416Q284 416 303 429T333 463T349 509T354 560Q354 632 283 632Q253 632 234 620T204 587T188 542T184 492Q184 455 200 436T254 416Z" />
+<glyph unicode="&#xBB;" horiz-adv-x="454" d="M305 104L251 138L397 305L297 475L362 508L478 306L305 104ZM119 110L65 144L207 305L109 469L174 502L287 306L119 110Z" />
+<glyph unicode="&#xBC;" horiz-adv-x="746" d="M211 416L241 628H237L163 590L150 635L262 694H311L272 416H351L344 367H121L128 416H211ZM192 0H139L725 700H778L192 0ZM671 67L662 0H600L609 67H469L461 114L641 327H707L678 117H729L722 67H671ZM517 114H618L639 267L517 114Z" />
+<glyph unicode="&#xBD;" horiz-adv-x="718" d="M203 416L233 628H229L155 590L142 635L254 694H303L264 416H343L336 367H113L120 416H203ZM153 0H100L686 700H739L153 0ZM449 0L447 54Q464 67 497 87T563 132T620 184T645 238Q645 252 636 267T601 282Q577 282 557 273T513 241L484 280Q518 309 548 321T614 333Q659 333 683 310T708 249Q708 216 690 189T644 139T582 94T515 50H684L677 0H449Z" />
+<glyph unicode="&#xBE;" horiz-adv-x="707" d="M363 478Q363 422 329 392T232 361Q200 361 171 369T118 399L150 442Q170 424 189 417T233 409Q261 409 282 425T304 475Q304 513 250 513H207L213 557H241Q251 557 264 559T288 568T306 585T314 612Q314 630 302 640T267 651Q245 651 223 644T181 619L159 661Q192 683 219 691T280 700Q323 700 349 682T375 625Q375 569 318 544V540Q363 528 363 478ZM173 0H120L706 700H759L173 0ZM639 67L630 0H568L577 67H437L429 114L609 327H675L646 117H697L690 67H639ZM485 114H586L607 267L485 114Z" />
+<glyph unicode="&#xBF;" horiz-adv-x="515" d="M72 -27Q72 25 88 60T131 120T195 163T274 199Q290 206 300 211T315 224T323 241T328 270L336 332H422L411 253Q408 229 403 214T390 187T367 168T331 150Q301 137 271 125T216 95T177 52T161 -13Q161 -38 170 -54T196 -81T232 -95T275 -100Q322 -100 361 -84T439 -41L473 -106Q419 -139 368 -156T254 -174Q218 -174 186 -166T128 -141T87 -96T72 -27ZM337 451Q337 480 357 497T406 515Q428 515 444 504T461 468Q461 439 441 422T392 405Q370 405 354 416T337 451Z" />
+<glyph unicode="&#xC0;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM430 705L271 814L365 854L483 735L430 705Z" />
+<glyph unicode="&#xC1;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM407 705L362 736L516 857L596 815L407 705Z" />
+<glyph unicode="&#xC2;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM534 703L427 782L297 703L267 753L435 851L579 753L534 703Z" />
+<glyph unicode="&#xC3;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM592 781Q576 745 548 728T490 710Q472 710 455 716T420 729T388 743T358 750Q341 750 326 742T296 709L257 743Q281 784 306 799T360 814Q378 814 395 808T430 794T463 780T493 774Q510 774 525 782T551 814L592 781H592Z" />
+<glyph unicode="&#xC4;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM570 769Q570 746 553 729T512 712Q494 712 481 724T467 755Q467 778 485 795T526 813Q545 813 557 801T570 769ZM383 769Q383 746 365 729T324 712Q306 712 293 724T280 755Q280 778 297 795T338 813Q357 813 370 801T383 769Z" />
+<glyph unicode="&#xC5;" horiz-adv-x="643" d="M540 741Q540 706 522 676T473 632L621 0H523L488 161H197L117 0H23L342 631Q322 644 312 666T301 712Q301 739 311 762T338 802T379 828T429 838Q451 838 471 832T506 813T531 782T540 741ZM403 584H399L230 236H474L403 584ZM411 660Q443 660 464 682T486 736Q486 760 471 775T428 790Q393 790 374 768T355 715Q355 690 371 675T411 660Z" />
+<glyph unicode="&#xC6;" horiz-adv-x="898" d="M451 0L474 164H228L118 0H22L486 667H943L933 591H624L596 392H875L864 314H585L551 76H870L860 0H451ZM534 589H521L276 242H486L534 589Z" />
+<glyph unicode="&#xC7;" horiz-adv-x="632" d="M435 -113Q435 -163 403 -186T326 -209Q302 -209 282 -203T244 -185L264 -150Q278 -158 293 -163T326 -169Q350 -169 367 -159T384 -121Q384 -102 370 -95T339 -88Q318 -88 301 -95L285 -80L331 -10Q218 -2 162 67T106 254Q106 335 128 411T194 547T306 642T463 678Q529 678 580 656T678 586L614 526Q580 563 543 582T455 601Q385 601 337 571T259 492T216 384T202 263Q202 219 211 183T242 121T296 80T377 65Q432 65 480 87T571 145L611 83Q558 41 502 17T377 -10L343 -57Q351 -55 358 -54T374 -53Q398 -53 416 -66T435 -113Z" />
+<glyph unicode="&#xC8;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM427 705L268 814L362 854L480 735L427 705Z" />
+<glyph unicode="&#xC9;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM368 705L323 736L477 857L557 815L368 705Z" />
+<glyph unicode="&#xCA;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM503 703L396 782L266 703L236 753L404 851L548 753L503 703Z" />
+<glyph unicode="&#xCB;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM539 769Q539 746 522 729T481 712Q463 712 450 724T436 755Q436 778 454 795T495 813Q514 813 526 801T539 769ZM352 769Q352 746 334 729T293 712Q275 712 262 724T249 755Q249 778 266 795T307 813Q326 813 339 801T352 769Z" />
+<glyph unicode="&#xCC;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM244 705L85 814L179 854L297 735L244 705Z" />
+<glyph unicode="&#xCD;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM221 705L176 736L330 857L410 815L221 705Z" />
+<glyph unicode="&#xCE;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM353 703L246 782L116 703L86 753L254 851L398 753L353 703Z" />
+<glyph unicode="&#xCF;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM385 769Q385 746 368 729T327 712Q309 712 296 724T282 755Q282 778 300 795T341 813Q360 813 372 801T385 769ZM198 769Q198 746 180 729T139 712Q121 712 108 724T95 755Q95 778 112 795T153 813Q172 813 185 801T198 769Z" />
+<glyph unicode="&#xD0;" horiz-adv-x="669" d="M657 411Q657 324 633 249T562 119T445 32T284 0H87L131 318H67L78 393H142L180 663Q231 668 281 671T382 675Q510 675 583 609T657 411ZM283 74Q349 74 397 92T477 144T529 227T557 339Q559 356 560 373T562 407Q562 499 518 549T377 600Q318 600 260 594L232 393H388L377 318H221L187 75Q211 74 235 74T283 74Z" />
+<glyph unicode="&#xD1;" horiz-adv-x="701" d="M530 0L251 520H247L174 0H86L179 667H269L544 154H548L620 667H708L615 0H530ZM631 781Q615 745 587 728T529 710Q511 710 494 716T459 729T427 743T397 750Q380 750 365 742T335 709L296 743Q320 784 345 799T399 814Q417 814 434 808T469 794T502 780T532 774Q549 774 564 782T590 814L631 781H631Z" />
+<glyph unicode="&#xD2;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM523 707L364 816L458 856L576 737L523 707Z" />
+<glyph unicode="&#xD3;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM456 707L411 738L565 859L645 817L456 707Z" />
+<glyph unicode="&#xD4;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM594 703L487 782L357 703L327 753L495 851L639 753L594 703Z" />
+<glyph unicode="&#xD5;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM643 783Q627 747 599 730T541 712Q523 712 506 718T471 731T439 745T409 752Q392 752 377 744T347 711L308 745Q332 786 357 801T411 816Q429 816 446 810T481 796T514 782T544 776Q561 776 576 784T602 816L643 783H643Z" />
+<glyph unicode="&#xD6;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM624 769Q624 746 607 729T566 712Q548 712 535 724T521 755Q521 778 539 795T580 813Q599 813 611 801T624 769ZM437 769Q437 746 419 729T378 712Q360 712 347 724T334 755Q334 778 351 795T392 813Q411 813 424 801T437 769Z" />
+<glyph unicode="&#xD7;" horiz-adv-x="575" d="M549 410L377 259L510 103L450 50L316 206L138 50L92 103L271 259L141 410L202 463L331 312L505 463L549 410Z" />
+<glyph unicode="&#xD8;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q259 -11 191 43L128 -24L79 23L145 94Q105 158 105 258Q105 341 128 417T196 551T310 643T468 678Q578 678 644 626L702 688L753 645L690 577Q733 511 733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 466 622 505L251 108Q273 86 304 75T381 63ZM201 261Q201 207 214 168L584 562Q539 604 457 604Q385 604 336 574T257 494T214 384T201 261Z" />
+<glyph unicode="&#xD9;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM441 705L282 814L376 854L494 735L441 705Z" />
+<glyph unicode="&#xDA;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM427 705L382 736L536 857L616 815L427 705Z" />
+<glyph unicode="&#xDB;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM553 703L446 782L316 703L286 753L454 851L598 753L553 703Z" />
+<glyph unicode="&#xDC;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM587 769Q587 746 570 729T529 712Q511 712 498 724T484 755Q484 778 502 795T543 813Q562 813 574 801T587 769ZM400 769Q400 746 382 729T341 712Q323 712 310 724T297 755Q297 778 314 795T355 813Q374 813 387 801T400 769Z" />
+<glyph unicode="&#xDD;" horiz-adv-x="572" d="M369 265L332 0H241L277 262L106 667H209L335 345L552 667H652L369 265ZM386 697L341 728L495 849L575 807L386 697Z" />
+<glyph unicode="&#xDE;" horiz-adv-x="601" d="M599 384Q599 267 535 198T346 129H194L176 0H86L179 667H268L251 545Q285 547 313 548T372 550Q422 550 463 542T535 515T582 464T599 384ZM337 202Q422 202 465 242T508 374Q508 433 472 454T360 475Q330 475 302 474T241 469L204 202H337Z" />
+<glyph unicode="&#xDF;" horiz-adv-x="532" d="M516 197Q516 153 502 115T460 49T396 5T313 -12Q304 -12 292 -11T268 -9T244 -5T223 2L246 66Q274 55 303 55Q364 55 395 93T427 195Q427 226 415 248T384 288T340 317T290 339L287 396Q311 411 335 428T379 467T411 514T424 572Q424 610 402 625T344 641Q291 641 268 614T238 537L163 0H76L152 541Q165 630 215 667T354 705Q384 705 411 699T459 677T492 639T504 581Q504 547 494 520T467 469T427 427T376 391V387Q406 373 431 355T475 313T505 260T516 197Z" />
+<glyph unicode="&#xE0;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM371 565L212 674L306 714L424 595L371 565Z" />
+<glyph unicode="&#xE1;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM319 565L274 596L428 717L508 675L319 565Z" />
+<glyph unicode="&#xE2;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM458 562L351 641L221 562L191 612L359 710L503 612L458 562Z" />
+<glyph unicode="&#xE3;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM510 641Q494 605 466 588T408 570Q390 570 373 576T338 589T306 603T276 610Q259 610 244 602T214 569L175 603Q199 644 224 659T278 674Q296 674 313 668T348 654T381 640T411 634Q428 634 443 642T469 674L510 641H510Z" />
+<glyph unicode="&#xE4;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM497 629Q497 606 480 589T439 572Q421 572 408 584T394 615Q394 638 412 655T453 673Q472 673 484 661T497 629ZM310 629Q310 606 292 589T251 572Q233 572 220 584T207 615Q207 638 224 655T265 673Q284 673 297 661T310 629Z" />
+<glyph unicode="&#xE5;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM455 661Q455 617 425 589T351 560Q315 560 289 581T262 639Q262 661 270 680T293 713T326 734T368 742Q406 742 430 720T455 661ZM351 597Q376 597 393 613T411 659Q411 681 399 693T366 705Q340 705 323 688T306 642Q306 621 318 609T351 597Z" />
+<glyph unicode="&#xE6;" horiz-adv-x="858" d="M848 317Q848 294 846 270T837 222H484Q483 214 483 205T482 188Q482 125 515 92T612 58Q654 58 693 72T766 109L789 48Q747 21 700 5T601 -11Q547 -11 501 7T426 71Q377 32 322 11T203 -11Q173 -11 148 -4T103 20T73 59T62 114Q62 183 96 224T181 287T293 315T407 322Q409 337 411 352T413 382Q413 422 391 434T334 446Q286 446 236 430T144 388L122 452Q177 479 231 496T348 514Q371 514 393 511T435 498T467 473T485 430Q520 471 565 493T665 515Q711 515 745 501T802 460T836 398T848 317ZM766 283Q767 292 767 301T768 319Q768 376 742 412T654 448Q618 448 590 435T542 398T508 346T491 283H766ZM401 125Q392 153 392 185Q392 206 394 226T400 267Q365 267 322 263T241 245T176 204T150 129Q150 94 172 74T233 53Q283 53 327 75T401 125Z" />
+<glyph unicode="&#xE7;" horiz-adv-x="495" d="M357 -113Q357 -163 325 -186T248 -209Q224 -209 204 -203T166 -185L186 -150Q200 -158 215 -163T248 -169Q272 -169 289 -159T306 -121Q306 -102 292 -95T261 -88Q240 -88 223 -95L207 -80L254 -9Q171 -1 127 51T83 194Q83 257 101 315T155 417T243 488T363 514Q409 514 451 498T528 451L481 390Q433 445 356 445Q307 445 272 423T215 367T183 289T173 203Q173 139 203 99T302 58Q345 58 379 72T448 112L470 51Q430 24 389 8T299 -11L265 -57Q273 -55 280 -54T296 -53Q320 -53 338 -66T357 -113Z" />
+<glyph unicode="&#xE8;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM383 565L224 674L318 714L436 595L383 565Z" />
+<glyph unicode="&#xE9;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM348 565L303 596L457 717L537 675L348 565Z" />
+<glyph unicode="&#xEA;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM479 563L372 642L242 563L212 613L380 711L524 613L479 563Z" />
+<glyph unicode="&#xEB;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM515 629Q515 606 498 589T457 572Q439 572 426 584T412 615Q412 638 430 655T471 673Q490 673 502 661T515 629ZM328 629Q328 606 310 589T269 572Q251 572 238 584T225 615Q225 638 242 655T283 673Q302 673 315 661T328 629Z" />
+<glyph unicode="&#xEC;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM221 565L62 674L156 714L274 595L221 565Z" />
+<glyph unicode="&#xED;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM183 565L138 596L292 717L372 675L183 565Z" />
+<glyph unicode="&#xEE;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM320 563L213 642L83 563L53 613L221 711L365 613L320 563Z" />
+<glyph unicode="&#xEF;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM354 629Q354 606 337 589T296 572Q278 572 265 584T251 615Q251 638 269 655T310 673Q329 673 341 661T354 629ZM167 629Q167 606 149 589T108 572Q90 572 77 584T64 615Q64 638 81 655T122 673Q141 673 154 661T167 629Z" />
+<glyph unicode="&#xF0;" horiz-adv-x="587" d="M506 616Q534 563 544 501T554 378Q554 309 543 240T501 116T417 25T281 -10Q188 -10 143 43T98 185Q98 243 111 297T154 393T229 460T340 485Q381 485 414 473T468 422H472Q471 464 460 505T432 584L303 524L285 575L407 632Q396 649 383 665T356 695L435 716Q447 703 458 691T480 664L565 708L583 656L506 616ZM298 57Q348 57 379 81T429 142T454 223T461 308Q461 367 434 395T347 423Q304 423 276 409T231 370T204 313T190 243Q188 228 187 214T185 184Q185 126 210 92T298 57Z" />
+<glyph unicode="&#xF1;" horiz-adv-x="564" d="M401 0L442 294Q444 310 445 324T447 352Q447 372 443 388T428 416T399 434T353 440Q290 440 253 408T207 315L163 0H76L146 502H219V439H223Q282 514 386 514Q429 514 458 505T504 478T529 435T537 376Q537 362 536 347T532 314L488 0H401ZM540 641Q524 605 496 588T438 570Q420 570 403 576T368 589T336 603T306 610Q289 610 274 602T244 569L205 603Q229 644 254 659T308 674Q326 674 343 668T378 654T411 640T441 634Q458 634 473 642T499 674L540 641H540Z" />
+<glyph unicode="&#xF2;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM391 565L232 674L326 714L444 595L391 565Z" />
+<glyph unicode="&#xF3;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM352 565L307 596L461 717L541 675L352 565Z" />
+<glyph unicode="&#xF4;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM485 563L378 642L248 563L218 613L386 711L530 613L485 563Z" />
+<glyph unicode="&#xF5;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM543 641Q527 605 499 588T441 570Q423 570 406 576T371 589T339 603T309 610Q292 610 277 602T247 569L208 603Q232 644 257 659T311 674Q329 674 346 668T381 654T414 640T444 634Q461 634 476 642T502 674L543 641H543Z" />
+<glyph unicode="&#xF6;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM521 629Q521 606 504 589T463 572Q445 572 432 584T418 615Q418 638 436 655T477 673Q496 673 508 661T521 629ZM334 629Q334 606 316 589T275 572Q257 572 244 584T231 615Q231 638 248 655T289 673Q308 673 321 661T334 629Z" />
+<glyph unicode="&#xF7;" horiz-adv-x="575" d="M82 224L93 300H565L554 224H82ZM246 89Q246 115 265 133T310 151Q330 151 343 138T357 105Q357 80 338 61T294 42Q274 42 260 55T246 89ZM293 422Q293 448 312 466T357 484Q377 484 390 471T404 438Q404 413 385 394T341 375Q321 375 307 388T293 422Z" />
+<glyph unicode="&#xF8;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q400 514 434 504T493 473L557 538L597 499L530 431Q560 381 560 307Q560 244 543 186T491 85T405 15T284 -11Q192 -11 139 37L85 -18L49 22L107 81Q84 124 84 192ZM471 308Q471 324 469 338T464 365L198 96Q212 77 235 67T294 56Q345 56 379 79T434 139T462 220T471 308ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 170 176 151L436 415Q421 429 400 437T349 445Z" />
+<glyph unicode="&#xF9;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM363 565L204 674L298 714L416 595L363 565Z" />
+<glyph unicode="&#xFA;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM346 565L301 596L455 717L535 675L346 565Z" />
+<glyph unicode="&#xFB;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM478 563L371 642L241 563L211 613L379 711L523 613L478 563Z" />
+<glyph unicode="&#xFC;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM515 629Q515 606 498 589T457 572Q439 572 426 584T412 615Q412 638 430 655T471 673Q490 673 502 661T515 629ZM328 629Q328 606 310 589T269 572Q251 572 238 584T225 615Q225 638 242 655T283 673Q302 673 315 661T328 629Z" />
+<glyph unicode="&#xFD;" horiz-adv-x="495" d="M256 -65Q232 -113 212 -144T170 -193T125 -219T66 -226Q45 -226 12 -223L23 -149Q31 -151 40 -151T66 -152Q85 -152 98 -148T124 -132T148 -100T176 -48L209 18L88 502H182L266 111H270L454 502H547L256 -65ZM327 565L282 596L436 717L516 675L327 565Z" />
+<glyph unicode="&#xFE;" horiz-adv-x="564" d="M551 322Q551 265 537 206T493 99T416 21T300 -10Q251 -10 221 9T172 62L170 63L131 -220H45L174 700H255L219 443H223Q257 483 295 498T380 514Q427 514 459 500T512 460T542 399T551 322ZM296 57Q345 57 377 82T429 146T455 232T463 324Q463 351 458 373T440 412T406 437T351 446Q291 446 254 416T208 326L191 202Q189 194 189 186T189 170Q189 118 215 88T296 57Z" />
+<glyph unicode="&#xFF;" horiz-adv-x="495" d="M256 -65Q232 -113 212 -144T170 -193T125 -219T66 -226Q45 -226 12 -223L23 -149Q31 -151 40 -151T66 -152Q85 -152 98 -148T124 -132T148 -100T176 -48L209 18L88 502H182L266 111H270L454 502H547L256 -65ZM487 629Q487 606 470 589T429 572Q411 572 398 584T384 615Q384 638 402 655T443 673Q462 673 474 661T487 629ZM300 629Q300 606 282 589T241 572Q223 572 210 584T197 615Q197 638 214 655T255 673Q274 673 287 661T300 629Z" />
+<glyph unicode="&#x100;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM269 729L277 786H575L567 729H269Z" />
+<glyph unicode="&#x101;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM193 589L201 646H499L491 589H193Z" />
+<glyph unicode="&#x102;" horiz-adv-x="642" d="M522 0L487 161H196L116 0H22L362 671H464L620 0H522ZM401 582H397L229 236H473L401 582ZM580 827Q572 792 554 769T514 731T466 711T416 705Q392 705 369 711T327 731T298 768T288 827H348Q347 796 364 775T423 754Q463 754 487 775T519 827H580H580Z" />
+<glyph unicode="&#x103;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM504 687Q496 652 478 629T438 591T390 571T340 565Q316 565 293 571T251 591T222 628T212 687H272Q271 656 288 635T347 614Q387 614 411 635T443 687H504H504Z" />
+<glyph unicode="&#x104;" horiz-adv-x="642" d="M571 0Q509 -35 485 -64T460 -128Q460 -153 474 -168T514 -183Q534 -183 548 -177T572 -166L583 -217Q570 -223 544 -230T493 -238Q442 -238 418 -214T394 -151Q394 -123 404 -100T432 -58T473 -24T521 4L487 161H196L116 0H22L362 671H464L620 0H571ZM401 582H397L229 236H473L401 582Z" />
+<glyph unicode="&#x105;" horiz-adv-x="564" d="M466 0Q410 -35 383 -65T356 -130Q356 -155 371 -169T409 -183Q429 -183 443 -178T468 -166L478 -217Q465 -223 439 -230T388 -238Q337 -238 313 -214T289 -151Q289 -125 299 -102T326 -60T366 -25T415 4L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 405 500T462 444H466L483 502H558L488 0H466ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326Z" />
+<glyph unicode="&#x106;" horiz-adv-x="632" d="M611 83Q555 38 495 14T363 -11Q234 -11 170 58T106 254Q106 335 128 411T194 547T306 642T463 678Q529 678 580 656T678 586L614 526Q580 563 543 582T455 601Q385 601 337 571T259 492T216 384T202 263Q202 219 211 183T242 121T296 80T377 65Q432 65 480 87T571 145L611 83H611ZM443 710L398 741L552 862L632 820L443 710Z" />
+<glyph unicode="&#x107;" horiz-adv-x="495" d="M481 390Q433 445 356 445Q307 445 272 423T215 367T183 289T173 203Q173 139 203 99T302 58Q345 58 379 72T448 112L470 51Q428 22 383 6T287 -11Q189 -11 136 42T83 194Q83 257 101 315T155 417T243 488T363 514Q409 514 451 498T528 451L481 390ZM344 565L299 596L453 717L533 675L344 565Z" />
+<glyph unicode="&#x108;" horiz-adv-x="632" d="M611 83Q555 38 495 14T363 -11Q234 -11 170 58T106 254Q106 335 128 411T194 547T306 642T463 678Q529 678 580 656T678 586L614 526Q580 563 543 582T455 601Q385 601 337 571T259 492T216 384T202 263Q202 219 211 183T242 121T296 80T377 65Q432 65 480 87T571 145L611 83H611ZM584 705L477 784L347 705L317 755L485 853L629 755L584 705Z" />
+<glyph unicode="&#x109;" horiz-adv-x="495" d="M481 390Q433 445 356 445Q307 445 272 423T215 367T183 289T173 203Q173 139 203 99T302 58Q345 58 379 72T448 112L470 51Q428 22 383 6T287 -11Q189 -11 136 42T83 194Q83 257 101 315T155 417T243 488T363 514Q409 514 451 498T528 451L481 390ZM481 563L374 642L244 563L214 613L382 711L526 613L481 563Z" />
+<glyph unicode="&#x10A;" horiz-adv-x="632" d="M611 83Q555 38 495 14T363 -11Q234 -11 170 58T106 254Q106 335 128 411T194 547T306 642T463 678Q529 678 580 656T678 586L614 526Q580 563 543 582T455 601Q385 601 337 571T259 492T216 384T202 263Q202 219 211 183T242 121T296 80T377 65Q432 65 480 87T571 145L611 83H611ZM532 778Q532 751 514 736T469 721Q449 721 435 731T421 763Q421 789 439 804T483 820Q503 820 517 810T532 778Z" />
+<glyph unicode="&#x10B;" horiz-adv-x="495" d="M481 390Q433 445 356 445Q307 445 272 423T215 367T183 289T173 203Q173 139 203 99T302 58Q345 58 379 72T448 112L470 51Q428 22 383 6T287 -11Q189 -11 136 42T83 194Q83 257 101 315T155 417T243 488T363 514Q409 514 451 498T528 451L481 390ZM428 637Q428 610 410 595T365 580Q345 580 331 590T317 622Q317 648 335 663T379 679Q399 679 413 669T428 637Z" />
+<glyph unicode="&#x10C;" horiz-adv-x="632" d="M611 83Q555 38 495 14T363 -11Q234 -11 170 58T106 254Q106 335 128 411T194 547T306 642T463 678Q529 678 580 656T678 586L614 526Q580 563 543 582T455 601Q385 601 337 571T259 492T216 384T202 263Q202 219 211 183T242 121T296 80T377 65Q432 65 480 87T571 145L611 83H611ZM623 811L465 713L335 811L376 859L474 783L593 859L623 811Z" />
+<glyph unicode="&#x10D;" horiz-adv-x="495" d="M481 390Q433 445 356 445Q307 445 272 423T215 367T183 289T173 203Q173 139 203 99T302 58Q345 58 379 72T448 112L470 51Q428 22 383 6T287 -11Q189 -11 136 42T83 194Q83 257 101 315T155 417T243 488T363 514Q409 514 451 498T528 451L481 390ZM527 663L369 565L239 663L280 711L378 635L497 711L527 663Z" />
+<glyph unicode="&#x10E;" horiz-adv-x="669" d="M656 411Q656 325 633 251T564 120T449 32T289 0H86L179 663Q230 668 280 671T382 675Q511 675 583 609T656 411ZM283 74Q349 74 396 92T476 144T528 227T556 339Q558 356 559 372T561 405Q561 498 516 550T374 602Q345 602 317 600T260 596L187 75Q211 74 235 74T283 74ZM543 803L385 705L255 803L296 851L394 775L513 851L543 803Z" />
+<glyph unicode="&#x10F;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 4T121 45T92 106T83 182Q83 235 94 293T134 401T208 482T326 514Q371 514 404 500T459 444H463L499 700H586L488 0H415ZM447 326Q455 386 424 416T336 446Q284 446 252 421T202 358T178 275T172 188Q172 161 177 137T194 95T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L447 326ZM672 535H613L669 700H762L672 535Z" />
+<glyph unicode="&#x110;" horiz-adv-x="669" d="M657 411Q657 324 633 249T562 119T445 32T284 0H87L131 318H67L78 393H142L180 663Q231 668 281 671T382 675Q510 675 583 609T657 411ZM283 74Q349 74 397 92T477 144T529 227T557 339Q559 356 560 373T562 407Q562 499 518 549T377 600Q318 600 260 594L232 393H388L377 318H221L187 75Q211 74 235 74T283 74Z" />
+<glyph unicode="&#x111;" horiz-adv-x="564" d="M415 0L420 77H416Q387 32 345 11T252 -11Q205 -11 173 3T120 44T91 105T82 181Q82 234 93 291T132 397T206 476T322 508Q367 508 402 494T458 437H462L481 570H323L331 628H489L499 700H586L576 628H643L635 570H568L488 0H415ZM446 319Q454 380 422 410T332 440Q282 440 251 416T202 354T178 272T171 186Q171 160 176 136T194 94T228 65T283 54Q315 54 340 65T384 96T414 143T430 200L446 319Z" />
+<glyph unicode="&#x112;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM239 729L247 786H545L537 729H239Z" />
+<glyph unicode="&#x113;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM215 589L223 646H521L513 589H215Z" />
+<glyph unicode="&#x114;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM548 827Q540 792 522 769T482 731T434 711T384 705Q360 705 337 711T295 731T266 768T256 827H316Q315 796 332 775T391 754Q431 754 455 775T487 827H548H548Z" />
+<glyph unicode="&#x115;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM525 687Q517 652 499 629T459 591T411 571T361 565Q337 565 314 571T272 591T243 628T233 687H293Q292 656 309 635T368 614Q408 614 432 635T464 687H525H525Z" />
+<glyph unicode="&#x116;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM466 776Q466 749 448 734T403 719Q383 719 369 729T355 761Q355 787 373 802T417 818Q437 818 451 808T466 776Z" />
+<glyph unicode="&#x117;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM427 637Q427 610 409 595T364 580Q344 580 330 590T316 622Q316 648 334 663T378 679Q398 679 412 669T427 637Z" />
+<glyph unicode="&#x118;" horiz-adv-x="534" d="M443 0Q426 -10 407 -23T371 -52T343 -88T332 -131Q332 -155 347 -169T386 -183Q416 -183 445 -167L455 -217Q437 -226 411 -232T365 -238Q324 -238 295 -218T266 -154Q266 -126 276 -103T302 -60T341 -26T386 0H86L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H443Z" />
+<glyph unicode="&#x119;" horiz-adv-x="543" d="M533 317Q533 293 530 270T523 223H171Q168 203 168 184Q168 121 201 89T298 56Q343 56 380 70T455 108L477 48Q454 33 425 18T368 -15T323 -58T304 -115Q304 -139 318 -154T357 -169Q373 -169 387 -165T416 -152L426 -203Q407 -212 382 -218T336 -224Q294 -224 266 -204T237 -139Q237 -97 261 -66T317 -11L316 -8Q299 -11 284 -11Q186 -11 135 41T84 191Q84 253 101 311T151 415T236 487T355 515Q400 515 433 500T489 459T522 396T533 317ZM452 281Q455 300 455 319Q455 345 449 368T430 410T397 438T347 449Q309 449 280 436T230 400T197 346T179 281H452Z" />
+<glyph unicode="&#x11A;" horiz-adv-x="534" d="M86 0L179 667H577L566 590H259L231 391H509L498 313H220L187 76H506L496 0H86ZM547 803L389 705L259 803L300 851L398 775L517 851L547 803Z" />
+<glyph unicode="&#x11B;" horiz-adv-x="543" d="M533 317Q533 294 530 269T523 223H171Q168 202 168 184Q168 124 200 90T298 56Q336 56 373 67T455 108L477 48Q427 16 381 3T286 -11Q184 -11 134 43T84 192Q84 253 100 311T150 414T235 487T355 515Q443 515 488 461T533 317ZM451 282Q454 303 454 323Q454 350 448 373T430 413T397 441T347 451Q306 451 276 436T226 397T195 343T179 282H451ZM519 663L361 565L231 663L272 711L370 635L489 711L519 663Z" />
+<glyph unicode="&#x11C;" horiz-adv-x="698" d="M378 60Q466 60 517 107T582 235L589 277H411L422 352H682L672 278Q652 135 575 63T364 -10Q298 -10 250 10T169 66T122 151T106 261Q106 338 129 413T197 547T309 642T465 678Q540 678 590 652T676 591L613 530Q575 568 537 584T457 601Q386 601 338 571T258 491T214 381T200 259Q200 218 209 182T238 118T293 76T378 60ZM578 703L471 782L341 703L311 753L479 851L623 753L578 703Z" />
+<glyph unicode="&#x11D;" horiz-adv-x="564" d="M482 -43Q473 -105 453 -141T405 -196T343 -220T273 -226Q148 -226 61 -175L97 -107Q137 -132 177 -145T268 -158Q298 -158 319 -154T356 -137T379 -105T393 -52L412 79H408Q376 33 340 15T256 -4Q209 -4 176 10T123 49T93 110T83 186Q83 239 94 297T134 403T208 482T326 514Q376 514 410 498T466 436H470L484 502H558L482 -43ZM447 325Q456 386 425 416T336 446Q286 446 255 423T205 363T180 283T173 196Q173 168 178 144T196 102T230 74T283 63Q316 63 341 74T385 105T415 151T431 208L447 325ZM476 563L369 642L239 563L209 613L377 711L521 613L476 563Z" />
+<glyph unicode="&#x11E;" horiz-adv-x="698" d="M378 60Q466 60 517 107T582 235L589 277H411L422 352H682L672 278Q652 135 575 63T364 -10Q298 -10 250 10T169 66T122 151T106 261Q106 338 129 413T197 547T309 642T465 678Q540 678 590 652T676 591L613 530Q575 568 537 584T457 601Q386 601 338 571T258 491T214 381T200 259Q200 218 209 182T238 118T293 76T378 60ZM631 834Q623 799 605 776T565 738T517 718T467 712Q443 712 420 718T378 738T349 775T339 834H399Q398 803 415 782T474 761Q514 761 538 782T570 834H631H631Z" />
+<glyph unicode="&#x11F;" horiz-adv-x="564" d="M482 -43Q473 -105 453 -141T405 -196T343 -220T273 -226Q148 -226 61 -175L97 -107Q137 -132 177 -145T268 -158Q298 -158 319 -154T356 -137T379 -105T393 -52L412 79H408Q376 33 340 15T256 -4Q209 -4 176 10T123 49T93 110T83 186Q83 239 94 297T134 403T208 482T326 514Q376 514 410 498T466 436H470L484 502H558L482 -43ZM447 325Q456 386 425 416T336 446Q286 446 255 423T205 363T180 283T173 196Q173 168 178 144T196 102T230 74T283 63Q316 63 341 74T385 105T415 151T431 208L447 325ZM528 687Q520 652 502 629T462 591T414 571T364 565Q340 565 317 571T275 591T246 628T236 687H296Q295 656 312 635T371 614Q411 614 435 635T467 687H528H528Z" />
+<glyph unicode="&#x120;" horiz-adv-x="698" d="M378 60Q466 60 517 107T582 235L589 277H411L422 352H682L672 278Q652 135 575 63T364 -10Q298 -10 250 10T169 66T122 151T106 261Q106 338 129 413T197 547T309 642T465 678Q540 678 590 652T676 591L613 530Q575 568 537 584T457 601Q386 601 338 571T258 491T214 381T200 259Q200 218 209 182T238 118T293 76T378 60ZM533 778Q533 751 515 736T470 721Q450 721 436 731T422 763Q422 789 440 804T484 820Q504 820 518 810T533 778Z" />
+<glyph unicode="&#x121;" horiz-adv-x="564" d="M482 -43Q473 -105 453 -141T405 -196T343 -220T273 -226Q148 -226 61 -175L97 -107Q137 -132 177 -145T268 -158Q298 -158 319 -154T356 -137T379 -105T393 -52L412 79H408Q376 33 340 15T256 -4Q209 -4 176 10T123 49T93 110T83 186Q83 239 94 297T134 403T208 482T326 514Q376 514 410 498T466 436H470L484 502H558L482 -43ZM447 325Q456 386 425 416T336 446Q286 446 255 423T205 363T180 283T173 196Q173 168 178 144T196 102T230 74T283 63Q316 63 341 74T385 105T415 151T431 208L447 325ZM425 637Q425 610 407 595T362 580Q342 580 328 590T314 622Q314 648 332 663T376 679Q396 679 410 669T425 637Z" />
+<glyph unicode="&#x122;" horiz-adv-x="698" d="M378 60Q466 60 517 107T582 235L589 277H411L422 352H682L672 278Q652 135 575 63T364 -10Q298 -10 250 10T169 66T122 151T106 261Q106 338 129 413T197 547T309 642T465 678Q540 678 590 652T676 591L613 530Q575 568 537 584T457 601Q386 601 338 571T258 491T214 381T200 259Q200 218 209 182T238 118T293 76T378 60ZM331 -213H271L319 -59H416L331 -213Z" />
+<glyph unicode="&#x123;" horiz-adv-x="564" d="M482 -43Q473 -105 453 -141T405 -196T343 -220T273 -226Q148 -226 61 -175L97 -107Q137 -132 177 -145T268 -158Q298 -158 319 -154T356 -137T379 -105T393 -52L412 79H408Q376 33 340 15T256 -4Q209 -4 176 10T123 49T93 110T83 186Q83 239 94 297T134 403T208 482T326 514Q376 514 410 498T466 436H470L484 502H558L482 -43ZM447 325Q456 386 425 416T336 446Q286 446 255 423T205 363T180 283T173 196Q173 168 178 144T196 102T230 74T283 63Q316 63 341 74T385 105T415 151T431 208L447 325ZM404 715H460L413 572H312L404 715Z" />
+<glyph unicode="&#x124;" horiz-adv-x="662" d="M485 0L529 314H221L177 0H86L179 667H270L233 399H541L578 667H669L576 0H485ZM555 703L448 782L318 703L288 753L456 851L600 753L555 703Z" />
+<glyph unicode="&#x125;" horiz-adv-x="564" d="M401 0L442 294Q444 311 446 327T448 356Q448 395 429 417T354 440Q291 440 253 408T207 315L163 0H76L174 700H261L224 440H228Q255 478 295 496T388 514Q472 514 504 479T536 378Q536 363 535 347T531 313L488 0H401ZM338 701L231 780L101 701L71 751L239 849L383 751L338 701Z" />
+<glyph unicode="&#x126;" horiz-adv-x="662" d="M485 0L529 314H221L177 0H86L155 493H69L78 562H164L179 667H270L255 562H563L578 667H669L654 562H740L731 493H645L576 0H485ZM539 389L554 493H246L231 389H539Z" />
+<glyph unicode="&#x127;" horiz-adv-x="564" d="M243 570L223 430H227Q253 468 293 486T386 504Q470 504 502 466T535 362Q535 348 534 332T530 299L488 0H400L439 283Q441 300 443 317T445 348Q445 386 427 407T353 429Q290 429 252 397T205 305L163 0H76L156 570H89L97 627H164L174 700H261L251 627H423L415 570H243Z" />
+<glyph unicode="&#x128;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM406 781Q390 745 362 728T304 710Q286 710 269 716T234 729T202 743T172 750Q155 750 140 742T110 709L71 743Q95 784 120 799T174 814Q192 814 209 808T244 794T277 780T307 774Q324 774 339 782T365 814L406 781H406Z" />
+<glyph unicode="&#x129;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM376 641Q360 605 332 588T274 570Q256 570 239 576T204 589T172 603T142 610Q125 610 110 602T80 569L41 603Q65 644 90 659T144 674Q162 674 179 668T214 654T247 640T277 634Q294 634 309 642T335 674L376 641H376Z" />
+<glyph unicode="&#x12A;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM85 729L93 786H391L383 729H85Z" />
+<glyph unicode="&#x12B;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM52 589L60 646H358L350 589H52Z" />
+<glyph unicode="&#x12C;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM394 827Q386 792 368 769T328 731T280 711T230 705Q206 705 183 711T141 731T112 768T102 827H162Q161 796 178 775T237 754Q277 754 301 775T333 827H394H394Z" />
+<glyph unicode="&#x12D;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76ZM363 687Q355 652 337 629T297 591T249 571T199 565Q175 565 152 571T110 591T81 628T71 687H131Q130 656 147 635T206 614Q246 614 270 635T302 687H363H363Z" />
+<glyph unicode="&#x12E;" horiz-adv-x="263" d="M154 0Q137 -10 117 -23T81 -52T53 -89T42 -131Q42 -155 57 -169T96 -183Q126 -183 155 -167L165 -217Q147 -226 120 -232T73 -238Q31 -238 5 -218T-22 -153Q-22 -124 -11 -101T17 -59T58 -26T105 0H86L179 667H270L177 0H154Z" />
+<glyph unicode="&#x12F;" horiz-adv-x="239" d="M145 0Q128 -10 108 -23T72 -52T44 -89T33 -131Q33 -155 48 -169T87 -183Q103 -183 117 -179T146 -167L156 -217Q137 -226 112 -232T66 -238Q24 -238 -3 -218T-31 -152Q-31 -124 -20 -101T8 -59T48 -26T94 0H76L146 502H233L163 0H145ZM262 636Q262 609 244 593T200 577Q180 577 166 587T151 619Q151 645 170 660T214 676Q234 676 248 667T262 636Z" />
+<glyph unicode="&#x130;" horiz-adv-x="263" d="M86 0L179 667H270L177 0H86ZM296 778Q296 751 278 736T233 721Q213 721 199 731T185 763Q185 789 203 804T247 820Q267 820 281 810T296 778Z" />
+<glyph unicode="&#x131;" horiz-adv-x="239" d="M76 0L146 502H233L163 0H76Z" />
+<glyph unicode="&#x132;" horiz-adv-x="647" d="M86 0L179 667H270L177 0H86ZM585 177Q572 86 526 37T388 -12Q345 -12 311 -1T244 38L293 108Q318 84 340 75T390 66Q437 66 461 91T493 170L563 667H654L585 177Z" />
+<glyph unicode="&#x133;" horiz-adv-x="469" d="M77 0L147 502H234L164 0H77ZM263 636Q263 609 245 593T201 577Q181 577 167 587T152 619Q152 645 171 660T215 676Q235 676 249 667T263 636ZM491 635Q491 608 473 593T429 577Q410 577 396 588T381 619Q381 645 399 660T443 676Q463 676 477 667T491 635ZM387 -39Q375 -126 330 -176T192 -226Q175 -226 159 -225T125 -222L136 -148Q163 -151 190 -151Q221 -151 241 -143T273 -119T291 -81T301 -31L376 502H463L387 -39Z" />
+<glyph unicode="&#x134;" horiz-adv-x="448" d="M386 177Q373 86 327 37T189 -12Q146 -12 112 -1T45 38L94 108Q119 84 141 75T191 66Q238 66 262 91T294 170L364 667H455L386 177H386ZM533 703L426 782L296 703L266 753L434 851L578 753L533 703Z" />
+<glyph unicode="&#x135;" horiz-adv-x="245" d="M163 -39Q150 -129 105 -177T-32 -226Q-49 -226 -65 -225T-99 -222L-88 -148Q-60 -151 -34 -151Q-3 -151 16 -143T48 -120T66 -83T77 -31L152 502H239L163 -39ZM324 563L217 642L87 563L57 613L225 711L369 613L324 563Z" />
+<glyph unicode="&#x136;" horiz-adv-x="579" d="M448 0L291 321L211 242L177 0H86L179 667H270L226 352H230L521 667H635L367 384L561 0H448ZM256 -213H196L244 -59H341L256 -213Z" />
+<glyph unicode="&#x137;" horiz-adv-x="504" d="M380 0L270 247L185 162L163 0H76L174 700H261L199 263H203L430 502H540L341 304L485 0H380ZM224 -213H164L212 -59H309L224 -213Z" />
+<glyph unicode="&#x139;" horiz-adv-x="498" d="M86 0L179 667H270L187 78H468L458 0H86ZM231 705L186 736L340 857L420 815L231 705Z" />
+<glyph unicode="&#x13A;" horiz-adv-x="272" d="M244 0Q232 -2 214 -3T176 -5Q130 -5 109 11T87 67Q87 74 87 81T89 97L174 700H261L179 119Q174 83 184 76T222 69H253L244 0H244ZM227 733L182 764L336 885L416 843L227 733Z" />
+<glyph unicode="&#x13B;" horiz-adv-x="498" d="M86 0L179 667H270L187 78H468L458 0H86ZM224 -213H164L212 -59H309L224 -213Z" />
+<glyph unicode="&#x13C;" horiz-adv-x="272" d="M244 0Q232 -2 214 -3T176 -5Q130 -5 109 11T87 67Q87 74 87 81T89 97L174 700H261L179 119Q174 83 184 76T222 69H253L244 0H244ZM122 -213H62L110 -59H207L122 -213Z" />
+<glyph unicode="&#x13D;" horiz-adv-x="498" d="M86 0L179 667H270L187 78H468L458 0H86ZM481 535H422L478 700H571L481 535Z" />
+<glyph unicode="&#x13E;" horiz-adv-x="272" d="M244 0Q232 -2 214 -3T176 -5Q130 -5 109 11T87 67Q87 74 87 81T89 97L174 700H261L179 119Q174 83 184 76T222 69H253L244 0ZM351 535H292L348 700H441L351 535Z" />
+<glyph unicode="&#x13F;" horiz-adv-x="498" d="M86 0L179 667H270L187 78H468L458 0H86ZM504 289Q504 259 483 241T432 223Q409 223 393 235T376 271Q376 301 397 318T448 335Q470 335 487 324T504 289Z" />
+<glyph unicode="&#x140;" horiz-adv-x="355" d="M244 0Q232 -2 214 -3T176 -5Q130 -5 109 11T87 67Q87 74 87 81T89 97L174 700H261L179 119Q174 83 184 76T222 69H253L244 0ZM404 247Q404 217 383 199T332 181Q309 181 293 193T276 229Q276 259 297 276T348 293Q370 293 387 282T404 247Z" />
+<glyph unicode="&#x141;" horiz-adv-x="498" d="M86 0L128 303L74 273L85 350L139 380L179 667H270L237 434L386 516L375 437L226 355L187 78H468L458 0H86Z" />
+<glyph unicode="&#x142;" horiz-adv-x="272" d="M214 365L179 119Q176 101 177 91T184 76T199 70T223 69H254L244 -1Q232 -3 216 -4T177 -6Q130 -6 109 9T87 66Q87 73 87 81T89 97L120 315L69 285L80 368L132 399L174 700H261L225 446L334 506L322 425L214 365Z" />
+<glyph unicode="&#x143;" horiz-adv-x="701" d="M530 0L251 520H247L174 0H86L179 667H269L544 154H548L620 667H708L615 0H530ZM450 705L405 736L559 857L639 815L450 705Z" />
+<glyph unicode="&#x144;" horiz-adv-x="564" d="M401 0L442 294Q444 310 445 324T447 352Q447 372 443 388T428 416T399 434T353 440Q290 440 253 408T207 315L163 0H76L146 502H219V439H223Q282 514 386 514Q429 514 458 505T504 478T529 435T537 376Q537 362 536 347T532 314L488 0H401ZM356 565L311 596L465 717L545 675L356 565Z" />
+<glyph unicode="&#x145;" horiz-adv-x="701" d="M530 0L251 520H247L174 0H86L179 667H269L544 154H548L620 667H708L615 0H530ZM303 -213H243L291 -59H388L303 -213Z" />
+<glyph unicode="&#x146;" horiz-adv-x="564" d="M401 0L442 294Q444 310 445 324T447 352Q447 372 443 388T428 416T399 434T353 440Q290 440 253 408T207 315L163 0H76L146 502H219V439H223Q282 514 386 514Q429 514 458 505T504 478T529 435T537 376Q537 362 536 347T532 314L488 0H401ZM246 -213H186L234 -59H331L246 -213Z" />
+<glyph unicode="&#x147;" horiz-adv-x="701" d="M530 0L251 520H247L174 0H86L179 667H269L544 154H548L620 667H708L615 0H530ZM620 803L462 705L332 803L373 851L471 775L590 851L620 803Z" />
+<glyph unicode="&#x148;" horiz-adv-x="564" d="M401 0L442 294Q444 310 445 324T447 352Q447 372 443 388T428 416T399 434T353 440Q290 440 253 408T207 315L163 0H76L146 502H219V439H223Q282 514 386 514Q429 514 458 505T504 478T529 435T537 376Q537 362 536 347T532 314L488 0H401ZM528 663L370 565L240 663L281 711L379 635L498 711L528 663Z" />
+<glyph unicode="&#x14A;" horiz-adv-x="701" d="M409 -158H423Q467 -158 490 -129T518 -62L527 5L250 525H246L175 0H86L179 667H269L544 148H548L619 667H708L606 -60Q595 -143 551 -191T422 -239H398L409 -158Z" />
+<glyph unicode="&#x14B;" horiz-adv-x="564" d="M219 502V439H223Q282 514 386 514Q429 514 458 505T504 478T529 435T537 376Q537 362 536 347T532 314L484 -23Q471 -115 425 -164T286 -214H262L274 -135H288Q315 -135 334 -127T366 -104T386 -69T397 -25L442 294Q444 310 445 324T447 352Q447 372 443 388T428 416T399 434T353 440Q290 440 253 408T207 315L163 0H76L146 502H219Z" />
+<glyph unicode="&#x14C;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM330 729L338 786H636L628 729H330Z" />
+<glyph unicode="&#x14D;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM221 589L229 646H527L519 589H221Z" />
+<glyph unicode="&#x14E;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM639 834Q631 799 613 776T573 738T525 718T475 712Q451 712 428 718T386 738T357 775T347 834H407Q406 803 423 782T482 761Q522 761 546 782T578 834H639H639Z" />
+<glyph unicode="&#x14F;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM530 687Q522 652 504 629T464 591T416 571T366 565Q342 565 319 571T277 591T248 628T238 687H298Q297 656 314 635T373 614Q413 614 437 635T469 687H530H530Z" />
+<glyph unicode="&#x150;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q240 -11 173 59T105 258Q105 341 128 417T196 551T310 643T468 678Q598 678 665 609T733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 499 596 551T457 604Q385 604 336 574T257 494T214 384T201 261Q201 216 211 180T242 117T298 77T381 63ZM601 696L559 723L717 844L792 806L601 696ZM371 696L330 723L488 844L563 806L371 696Z" />
+<glyph unicode="&#x151;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q458 514 509 460T560 307Q560 244 543 186T491 85T405 15T284 -11Q185 -11 135 42T84 192ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 131 200 94T294 56Q345 56 379 79T434 139T462 220T471 308Q471 370 444 407T349 445ZM507 562L465 589L623 710L698 672L507 562ZM277 562L236 589L394 710L469 672L277 562Z" />
+<glyph unicode="&#x152;" horiz-adv-x="894" d="M472 0Q449 -5 419 -8T362 -11Q293 -11 245 8T165 62T120 145T105 252Q105 328 124 404T186 541T295 640T459 678Q486 678 514 675T565 667H938L927 590H645L617 391H870L859 313H606L573 76H866L856 0H472ZM375 63Q406 63 434 67T483 79L554 587Q535 595 509 599T451 604Q379 604 331 573T254 492T213 380T200 255Q200 215 208 180T236 119T290 78T375 63Z" />
+<glyph unicode="&#x153;" horiz-adv-x="927" d="M916 314Q916 291 914 269T907 223H552Q551 215 551 206T550 189Q550 124 583 91T682 58Q724 58 762 72T835 110L857 48Q815 21 768 5T670 -11Q609 -11 563 14T492 98Q456 41 404 15T285 -11Q187 -11 135 40T83 190Q83 254 100 312T151 416T237 487T359 514Q418 514 465 489T533 405Q568 456 619 485T734 515Q780 515 814 500T871 459T905 395T916 314ZM835 283Q836 290 836 298T837 315Q837 343 831 367T812 409T778 437T725 448Q689 448 661 435T612 399T578 346T560 283H835ZM295 58Q346 58 380 81T434 140T462 221T470 309Q470 371 443 406T349 442Q298 442 264 420T210 361T181 281T173 194Q173 131 201 95T295 58Z" />
+<glyph unicode="&#x154;" horiz-adv-x="604" d="M459 0L405 240Q400 262 389 270T354 279H216L177 0H86L179 664Q233 670 279 672T380 675Q423 675 464 668T537 643T588 595T607 517Q607 482 599 449T574 389T530 340T467 307V303Q477 298 483 286T492 261L555 0H459ZM362 352Q434 352 474 388T515 502Q515 532 504 551T474 582T428 597T372 602Q342 602 316 601T260 595L226 352H362ZM369 707L324 738L478 859L558 817L369 707Z" />
+<glyph unicode="&#x155;" horiz-adv-x="365" d="M378 436Q307 436 262 411T207 318L163 0H76L146 502H219V438H223Q251 477 290 495T385 513H411L400 436H378ZM275 565L230 596L384 717L464 675L275 565Z" />
+<glyph unicode="&#x156;" horiz-adv-x="604" d="M459 0L405 240Q400 262 389 270T354 279H216L177 0H86L179 664Q233 670 279 672T380 675Q423 675 464 668T537 643T588 595T607 517Q607 482 599 449T574 389T530 340T467 307V303Q477 298 483 286T492 261L555 0H459ZM362 352Q434 352 474 388T515 502Q515 532 504 551T474 582T428 597T372 602Q342 602 316 601T260 595L226 352H362ZM262 -213H202L250 -59H347L262 -213Z" />
+<glyph unicode="&#x157;" horiz-adv-x="365" d="M378 436Q307 436 262 411T207 318L163 0H76L146 502H219V438H223Q251 477 290 495T385 513H411L400 436H378ZM74 -213H14L62 -59H159L74 -213Z" />
+<glyph unicode="&#x158;" horiz-adv-x="604" d="M459 0L405 240Q400 262 389 270T354 279H216L177 0H86L179 664Q233 670 279 672T380 675Q423 675 464 668T537 643T588 595T607 517Q607 482 599 449T574 389T530 340T467 307V303Q477 298 483 286T492 261L555 0H459ZM362 352Q434 352 474 388T515 502Q515 532 504 551T474 582T428 597T372 602Q342 602 316 601T260 595L226 352H362ZM546 803L388 705L258 803L299 851L397 775L516 851L546 803Z" />
+<glyph unicode="&#x159;" horiz-adv-x="365" d="M378 436Q307 436 262 411T207 318L163 0H76L146 502H219V438H223Q251 477 290 495T385 513H411L400 436H378ZM436 663L278 565L148 663L189 711L287 635L406 711L436 663Z" />
+<glyph unicode="&#x15A;" horiz-adv-x="539" d="M516 205Q516 150 497 110T443 42T364 2T267 -11Q202 -11 149 6T44 62L96 130Q134 98 177 80T271 62Q301 62 328 69T377 92T410 131T422 188Q422 222 400 244T345 285T273 319T201 358T146 411T124 489Q124 540 143 576T196 634T272 667T361 677Q466 677 554 618L510 545Q476 572 437 587T353 603Q329 603 305 598T261 582T229 552T217 505Q217 472 239 451T294 413T366 381T438 343T494 288T516 205ZM357 707L312 738L466 859L546 817L357 707Z" />
+<glyph unicode="&#x15B;" horiz-adv-x="450" d="M429 157Q429 112 413 81T370 29T307 -1T230 -11Q179 -11 131 1T40 42L81 106Q113 83 153 69T234 55Q255 55 274 59T310 73T335 99T345 138Q345 164 327 180T282 207T223 229T164 255T118 295T100 360Q100 401 116 430T160 478T223 505T297 514Q339 514 380 505T458 475L424 409Q397 427 362 436T295 446Q277 446 257 443T221 433T194 411T183 374Q183 351 201 337T246 311T306 289T365 262T411 221T429 157ZM289 565L244 596L398 717L478 675L289 565Z" />
+<glyph unicode="&#x15C;" horiz-adv-x="539" d="M516 205Q516 150 497 110T443 42T364 2T267 -11Q202 -11 149 6T44 62L96 130Q134 98 177 80T271 62Q301 62 328 69T377 92T410 131T422 188Q422 222 400 244T345 285T273 319T201 358T146 411T124 489Q124 540 143 576T196 634T272 667T361 677Q466 677 554 618L510 545Q476 572 437 587T353 603Q329 603 305 598T261 582T229 552T217 505Q217 472 239 451T294 413T366 381T438 343T494 288T516 205ZM488 703L381 782L251 703L221 753L389 851L533 753L488 703Z" />
+<glyph unicode="&#x15D;" horiz-adv-x="450" d="M429 157Q429 112 413 81T370 29T307 -1T230 -11Q179 -11 131 1T40 42L81 106Q113 83 153 69T234 55Q255 55 274 59T310 73T335 99T345 138Q345 164 327 180T282 207T223 229T164 255T118 295T100 360Q100 401 116 430T160 478T223 505T297 514Q339 514 380 505T458 475L424 409Q397 427 362 436T295 446Q277 446 257 443T221 433T194 411T183 374Q183 351 201 337T246 311T306 289T365 262T411 221T429 157ZM438 563L331 642L201 563L171 613L339 711L483 613L438 563Z" />
+<glyph unicode="&#x15E;" horiz-adv-x="539" d="M339 -114Q339 -164 307 -187T230 -210Q206 -210 186 -204T148 -186L168 -151Q182 -159 197 -164T230 -170Q254 -170 271 -160T288 -122Q288 -103 274 -96T243 -89Q222 -89 205 -96L189 -81L236 -10Q182 -6 136 11T44 62L96 130Q134 98 177 80T271 62Q301 62 328 69T377 92T410 131T422 188Q422 222 400 244T345 285T273 319T201 358T146 411T124 489Q124 540 143 576T196 634T272 667T361 677Q466 677 554 618L510 545Q476 572 437 587T353 603Q329 603 305 598T261 582T229 552T217 505Q217 472 239 451T294 413T366 381T438 343T494 288T516 205Q516 152 498 112T449 46T374 5T282 -11L247 -58Q255 -56 262 -55T278 -54Q302 -54 320 -67T339 -114Z" />
+<glyph unicode="&#x15F;" horiz-adv-x="450" d="M304 -113Q304 -163 272 -186T195 -209Q171 -209 151 -203T113 -185L133 -150Q147 -158 162 -163T195 -169Q219 -169 236 -159T253 -121Q253 -102 239 -95T208 -88Q187 -88 170 -95L154 -80L200 -10Q157 -7 116 5T40 42L81 106Q113 83 153 69T234 55Q255 55 274 59T310 73T335 99T345 138Q345 164 327 180T282 207T223 229T164 255T118 295T100 360Q100 401 116 430T160 478T223 505T297 514Q339 514 380 505T458 475L424 409Q397 427 362 436T295 446Q277 446 257 443T221 433T194 411T183 374Q183 351 201 337T246 311T306 289T365 262T411 221T429 157Q429 115 415 84T376 33T318 3T246 -10L212 -57Q220 -55 227 -54T243 -53Q267 -53 285 -66T304 -113Z" />
+<glyph unicode="&#x160;" horiz-adv-x="539" d="M516 205Q516 150 497 110T443 42T364 2T267 -11Q202 -11 149 6T44 62L96 130Q134 98 177 80T271 62Q301 62 328 69T377 92T410 131T422 188Q422 222 400 244T345 285T273 319T201 358T146 411T124 489Q124 540 143 576T196 634T272 667T361 677Q466 677 554 618L510 545Q476 572 437 587T353 603Q329 603 305 598T261 582T229 552T217 505Q217 472 239 451T294 413T366 381T438 343T494 288T516 205ZM526 803L368 705L238 803L279 851L377 775L496 851L526 803Z" />
+<glyph unicode="&#x161;" horiz-adv-x="450" d="M429 157Q429 112 413 81T370 29T307 -1T230 -11Q179 -11 131 1T40 42L81 106Q113 83 153 69T234 55Q255 55 274 59T310 73T335 99T345 138Q345 164 327 180T282 207T223 229T164 255T118 295T100 360Q100 401 116 430T160 478T223 505T297 514Q339 514 380 505T458 475L424 409Q397 427 362 436T295 446Q277 446 257 443T221 433T194 411T183 374Q183 351 201 337T246 311T306 289T365 262T411 221T429 157ZM473 663L315 565L185 663L226 711L324 635L443 711L473 663Z" />
+<glyph unicode="&#x162;" horiz-adv-x="505" d="M380 588L298 0H207L289 588H101L112 667H579L568 588H380ZM201 -214H141L189 -60H286L201 -214Z" />
+<glyph unicode="&#x163;" horiz-adv-x="352" d="M310 -1Q294 -3 278 -4T246 -6Q208 -6 184 0T145 17T125 45T119 84Q119 103 122 126L165 431H82L92 502H176L195 640H281L262 502H381L371 431H252L211 139Q208 121 208 109Q208 86 223 77T288 67Q297 67 303 67T320 68L310 -1H310ZM172 -213H112L160 -59H257L172 -213Z" />
+<glyph unicode="&#x164;" horiz-adv-x="505" d="M380 588L298 0H207L289 588H101L112 667H579L568 588H380ZM511 803L353 705L223 803L264 851L362 775L481 851L511 803Z" />
+<glyph unicode="&#x165;" horiz-adv-x="352" d="M310 -1Q294 -3 278 -4T246 -6Q208 -6 184 0T145 17T125 45T119 84Q119 103 122 126L165 431H82L92 502H176L195 640H281L262 502H381L371 431H252L211 139Q208 121 208 109Q208 86 223 77T288 67Q297 67 303 67T320 68L310 -1ZM416 535H357L413 700H506L416 535Z" />
+<glyph unicode="&#x166;" horiz-adv-x="505" d="M343 324L298 0H207L252 324H94L104 396H262L289 588H101L112 667H579L568 588H380L353 396H511L501 324H343Z" />
+<glyph unicode="&#x167;" horiz-adv-x="352" d="M229 270L211 139Q208 121 208 109Q208 86 223 77T288 67H320L310 -1Q294 -3 278 -4T246 -6Q208 -6 184 0T145 17T125 45T119 84Q119 103 122 126L142 270H59L69 338H152L165 431H82L92 502H176L195 640H281L262 502H381L371 431H252L239 338H358L348 270H229Z" />
+<glyph unicode="&#x168;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM607 781Q591 745 563 728T505 710Q487 710 470 716T435 729T403 743T373 750Q356 750 341 742T311 709L272 743Q296 784 321 799T375 814Q393 814 410 808T445 794T478 780T508 774Q525 774 540 782T566 814L607 781H607Z" />
+<glyph unicode="&#x169;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM529 641Q513 605 485 588T427 570Q409 570 392 576T357 589T325 603T295 610Q278 610 263 602T233 569L194 603Q218 644 243 659T297 674Q315 674 332 668T367 654T400 640T430 634Q447 634 462 642T488 674L529 641H529Z" />
+<glyph unicode="&#x16A;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM289 729L297 786H595L587 729H289Z" />
+<glyph unicode="&#x16B;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM209 589L217 646H515L507 589H209Z" />
+<glyph unicode="&#x16C;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM595 827Q587 792 569 769T529 731T481 711T431 705Q407 705 384 711T342 731T313 768T303 827H363Q362 796 379 775T438 754Q478 754 502 775T534 827H595H595Z" />
+<glyph unicode="&#x16D;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM523 687Q515 652 497 629T457 591T409 571T359 565Q335 565 312 571T270 591T241 628T231 687H291Q290 656 307 635T366 614Q406 614 430 635T462 687H523H523Z" />
+<glyph unicode="&#x16E;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM541 791Q541 747 511 719T437 690Q401 690 375 711T348 769Q348 791 356 810T379 843T412 864T454 872Q492 872 516 850T541 791ZM437 727Q462 727 479 743T497 789Q497 811 485 823T452 835Q426 835 409 818T392 772Q392 751 404 739T437 727Z" />
+<glyph unicode="&#x16F;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM467 667Q467 623 437 595T363 566Q327 566 301 587T274 645Q274 667 282 686T305 719T338 740T380 748Q418 748 442 726T467 667ZM363 603Q388 603 405 619T423 665Q423 687 411 699T378 711Q352 711 335 694T318 648Q318 627 330 615T363 603Z" />
+<glyph unicode="&#x170;" horiz-adv-x="669" d="M619 259Q599 121 531 55T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619ZM554 708L512 735L670 856L745 818L554 708ZM324 708L283 735L441 856L516 818L324 708Z" />
+<glyph unicode="&#x171;" horiz-adv-x="555" d="M406 0V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H406ZM486 568L444 595L602 716L677 678L486 568ZM256 568L215 595L373 716L448 678L256 568Z" />
+<glyph unicode="&#x172;" horiz-adv-x="669" d="M619 259Q605 156 565 95T451 7Q434 -4 415 -17T380 -46T352 -81T341 -124Q341 -148 355 -162T395 -176Q415 -176 429 -171T454 -160L463 -210Q451 -216 425 -223T374 -231Q323 -231 299 -207T275 -146Q275 -102 301 -68T366 -9Q358 -10 350 -10T332 -11Q219 -11 168 36T116 184Q116 218 122 259L179 667H270L213 259Q211 243 210 228T208 199Q208 169 214 144T237 102T279 75T343 65Q390 65 422 79T476 119T510 180T528 259L585 667H676L619 259H619Z" />
+<glyph unicode="&#x173;" horiz-adv-x="555" d="M454 0Q392 -36 368 -66T343 -131Q343 -155 358 -169T397 -183Q417 -183 431 -178T456 -167L465 -217Q453 -223 427 -230T376 -238Q324 -238 301 -214T278 -152Q278 -124 288 -101T317 -59T358 -24T406 4V63H402Q379 31 341 10T242 -11Q208 -11 181 -5T136 17T107 60T97 128Q97 142 98 158T102 192L146 502H233L196 237Q192 213 190 192T188 153Q188 108 206 85T277 62Q347 62 379 98T419 194L462 502H549L479 0H454Z" />
+<glyph unicode="&#x174;" horiz-adv-x="916" d="M712 0H608L533 538H529L307 0H196L123 667H223L270 105H275L506 667H598L671 105H675L882 667H979L712 0ZM675 703L568 782L438 703L408 753L576 851L720 753L675 703Z" />
+<glyph unicode="&#x175;" horiz-adv-x="747" d="M576 0H483L426 373H422L261 0H168L95 502H185L230 86H234L407 502H478L536 86H540L704 502H792L576 0ZM572 563L465 642L335 563L305 613L473 711L617 613L572 563Z" />
+<glyph unicode="&#x176;" horiz-adv-x="572" d="M369 265L332 0H241L277 262L106 667H209L335 345L552 667H652L369 265ZM505 703L398 782L268 703L238 753L406 851L550 753L505 703Z" />
+<glyph unicode="&#x177;" horiz-adv-x="495" d="M256 -65Q232 -113 212 -144T170 -193T125 -219T66 -226Q45 -226 12 -223L23 -149Q31 -151 40 -151T66 -152Q85 -152 98 -148T124 -132T148 -100T176 -48L209 18L88 502H182L266 111H270L454 502H547L256 -65ZM450 563L343 642L213 563L183 613L351 711L495 613L450 563Z" />
+<glyph unicode="&#x178;" horiz-adv-x="572" d="M369 265L332 0H241L277 262L106 667H209L335 345L552 667H652L369 265ZM540 769Q540 746 523 729T482 712Q464 712 451 724T437 755Q437 778 455 795T496 813Q515 813 527 801T540 769ZM353 769Q353 746 335 729T294 712Q276 712 263 724T250 755Q250 778 267 795T308 813Q327 813 340 801T353 769Z" />
+<glyph unicode="&#x179;" horiz-adv-x="558" d="M46 0L56 75L480 588H143L154 667H596L586 593L161 76H522L512 0H46ZM361 705L316 736L470 857L550 815L361 705Z" />
+<glyph unicode="&#x17A;" horiz-adv-x="454" d="M39 0L48 68L368 431H105L115 502H477L467 431L148 71H424L415 0H39ZM289 565L244 596L398 717L478 675L289 565Z" />
+<glyph unicode="&#x17B;" horiz-adv-x="558" d="M46 0L56 75L480 588H143L154 667H596L586 593L161 76H522L512 0H46ZM448 778Q448 751 430 736T385 721Q365 721 351 731T337 763Q337 789 355 804T399 820Q419 820 433 810T448 778Z" />
+<glyph unicode="&#x17C;" horiz-adv-x="454" d="M39 0L48 68L368 431H105L115 502H477L467 431L148 71H424L415 0H39ZM385 637Q385 610 367 595T322 580Q302 580 288 590T274 622Q274 648 292 663T336 679Q356 679 370 669T385 637Z" />
+<glyph unicode="&#x17D;" horiz-adv-x="558" d="M46 0L56 75L480 588H143L154 667H596L586 593L161 76H522L512 0H46ZM540 803L382 705L252 803L293 851L391 775L510 851L540 803Z" />
+<glyph unicode="&#x17E;" horiz-adv-x="454" d="M39 0L48 68L368 431H105L115 502H477L467 431L148 71H424L415 0H39ZM476 663L318 565L188 663L229 711L327 635L446 711L476 663Z" />
+<glyph unicode="&#x192;" horiz-adv-x="575" d="M600 613Q587 614 581 614T562 614Q527 614 503 601T465 548L424 429H531L508 356H402L279 -10Q265 -53 247 -87T204 -144T149 -180T77 -193Q63 -193 49 -192T21 -186L44 -119Q54 -121 64 -121T88 -122Q107 -122 123 -112T153 -83T178 -41T199 12L313 356H237L260 429H335L375 548Q387 586 402 611T440 653T495 675T576 682Q588 682 597 682T621 681L600 613Z" />
+<glyph unicode="&#x1FC;" horiz-adv-x="898" d="M451 0L474 164H228L118 0H22L486 667H943L933 591H624L596 392H875L864 314H585L551 76H870L860 0H451ZM534 589H521L276 242H486L534 589ZM684 705L639 736L793 857L873 815L684 705Z" />
+<glyph unicode="&#x1FD;" horiz-adv-x="858" d="M848 317Q848 294 846 270T837 222H484Q483 214 483 205T482 188Q482 125 515 92T612 58Q654 58 693 72T766 109L789 48Q747 21 700 5T601 -11Q547 -11 501 7T426 71Q377 32 322 11T203 -11Q173 -11 148 -4T103 20T73 59T62 114Q62 183 96 224T181 287T293 315T407 322Q409 337 411 352T413 382Q413 422 391 434T334 446Q286 446 236 430T144 388L122 452Q177 479 231 496T348 514Q371 514 393 511T435 498T467 473T485 430Q520 471 565 493T665 515Q711 515 745 501T802 460T836 398T848 317ZM766 283Q767 292 767 301T768 319Q768 376 742 412T654 448Q618 448 590 435T542 398T508 346T491 283H766ZM401 125Q392 153 392 185Q392 206 394 226T400 267Q365 267 322 263T241 245T176 204T150 129Q150 94 172 74T233 53Q283 53 327 75T401 125ZM525 565L480 596L634 717L714 675L525 565Z" />
+<glyph unicode="&#x1FE;" horiz-adv-x="746" d="M733 411Q733 327 711 251T643 116T530 24T371 -11Q259 -11 191 43L128 -24L79 23L145 94Q105 158 105 258Q105 341 128 417T196 551T310 643T468 678Q578 678 644 626L702 688L753 645L690 577Q733 511 733 411ZM381 63Q453 63 502 94T581 174T625 284T638 408Q638 466 622 505L251 108Q273 86 304 75T381 63ZM201 261Q201 207 214 168L584 562Q539 604 457 604Q385 604 336 574T257 494T214 384T201 261ZM450 707L405 738L559 859L639 817L450 707Z" />
+<glyph unicode="&#x1FF;" horiz-adv-x="574" d="M84 192Q84 256 100 314T151 416T236 487T358 514Q400 514 434 504T493 473L557 538L597 499L530 431Q560 381 560 307Q560 244 543 186T491 85T405 15T284 -11Q192 -11 139 37L85 -18L49 22L107 81Q84 124 84 192ZM471 308Q471 324 469 338T464 365L198 96Q212 77 235 67T294 56Q345 56 379 79T434 139T462 220T471 308ZM349 445Q297 445 263 422T209 362T181 281T173 193Q173 170 176 151L436 415Q421 429 400 437T349 445ZM359 565L314 596L468 717L548 675L359 565Z" />
+<glyph unicode="&#x218;" horiz-adv-x="539" d="M516 205Q516 150 497 110T443 42T364 2T267 -11Q202 -11 149 6T44 62L96 130Q134 98 177 80T271 62Q301 62 328 69T377 92T410 131T422 188Q422 222 400 244T345 285T273 319T201 358T146 411T124 489Q124 540 143 576T196 634T272 667T361 677Q466 677 554 618L510 545Q476 572 437 587T353 603Q329 603 305 598T261 582T229 552T217 505Q217 472 239 451T294 413T366 381T438 343T494 288T516 205ZM224 -213H164L212 -59H309L224 -213Z" />
+<glyph unicode="&#x219;" horiz-adv-x="450" d="M429 157Q429 112 413 81T370 29T307 -1T230 -11Q179 -11 131 1T40 42L81 106Q113 83 153 69T234 55Q255 55 274 59T310 73T335 99T345 138Q345 164 327 180T282 207T223 229T164 255T118 295T100 360Q100 401 116 430T160 478T223 505T297 514Q339 514 380 505T458 475L424 409Q397 427 362 436T295 446Q277 446 257 443T221 433T194 411T183 374Q183 351 201 337T246 311T306 289T365 262T411 221T429 157ZM180 -213H120L168 -59H265L180 -213Z" />
+<glyph unicode="&#x237;" horiz-adv-x="245" d="M163 -39Q150 -129 105 -177T-32 -226Q-49 -226 -65 -225T-99 -222L-88 -148Q-60 -151 -34 -151Q-3 -151 16 -143T48 -120T66 -83T77 -31L152 502H239L163 -39H163Z" />
+<glyph unicode="&#x2C6;" horiz-adv-x="358" d="M376 563L269 642L139 563L109 613L277 711L421 613L376 563Z" />
+<glyph unicode="&#x2C7;" horiz-adv-x="358" d="M416 663L258 565L128 663L169 711L267 635L386 711L416 663Z" />
+<glyph unicode="&#x2D8;" horiz-adv-x="358" d="M421 687Q413 652 395 629T355 591T307 571T257 565Q233 565 210 571T168 591T139 628T129 687H189Q188 656 205 635T264 614Q304 614 328 635T360 687H421H421Z" />
+<glyph unicode="&#x2D9;" horiz-adv-x="358" d="M322 637Q322 610 304 595T259 580Q239 580 225 590T211 622Q211 648 229 663T273 679Q293 679 307 669T322 637Z" />
+<glyph unicode="&#x2DA;" horiz-adv-x="358" d="M367 667Q367 623 337 595T263 566Q227 566 201 587T174 645Q174 667 182 686T205 719T238 740T280 748Q318 748 342 726T367 667ZM263 603Q288 603 305 619T323 665Q323 687 311 699T278 711Q252 711 235 694T218 648Q218 627 230 615T263 603Z" />
+<glyph unicode="&#x2DB;" horiz-adv-x="358" d="M237 2Q219 -9 200 -21T163 -50T135 -86T124 -130Q124 -154 138 -168T177 -183Q194 -183 207 -179T236 -166L246 -217Q227 -226 202 -232T156 -238Q113 -238 85 -218T57 -151Q57 -118 71 -92T109 -44T160 -8T216 17L237 2H237Z" />
+<glyph unicode="&#x2DC;" horiz-adv-x="358" d="M434 641Q418 605 390 588T332 570Q314 570 297 576T262 589T230 603T200 610Q183 610 168 602T138 569L99 603Q123 644 148 659T202 674Q220 674 237 668T272 654T305 640T335 634Q352 634 367 642T393 674L434 641H434Z" />
+<glyph unicode="&#x2DD;" horiz-adv-x="358" d="M316 568L274 595L432 716L507 678L316 568ZM86 568L45 595L203 716L278 678L86 568Z" />
+<glyph unicode="&#x1E80;" horiz-adv-x="916" d="M712 0H608L533 538H529L307 0H196L123 667H223L270 105H275L506 667H598L671 105H675L882 667H979L712 0ZM576 705L417 814L511 854L629 735L576 705Z" />
+<glyph unicode="&#x1E81;" horiz-adv-x="747" d="M576 0H483L426 373H422L261 0H168L95 502H185L230 86H234L407 502H478L536 86H540L704 502H792L576 0ZM469 565L310 674L404 714L522 595L469 565Z" />
+<glyph unicode="&#x1E82;" horiz-adv-x="916" d="M712 0H608L533 538H529L307 0H196L123 667H223L270 105H275L506 667H598L671 105H675L882 667H979L712 0ZM539 705L494 736L648 857L728 815L539 705Z" />
+<glyph unicode="&#x1E83;" horiz-adv-x="747" d="M576 0H483L426 373H422L261 0H168L95 502H185L230 86H234L407 502H478L536 86H540L704 502H792L576 0ZM437 565L392 596L546 717L626 675L437 565Z" />
+<glyph unicode="&#x1E84;" horiz-adv-x="916" d="M712 0H608L533 538H529L307 0H196L123 667H223L270 105H275L506 667H598L671 105H675L882 667H979L712 0ZM710 769Q710 746 693 729T652 712Q634 712 621 724T607 755Q607 778 625 795T666 813Q685 813 697 801T710 769ZM523 769Q523 746 505 729T464 712Q446 712 433 724T420 755Q420 778 437 795T478 813Q497 813 510 801T523 769Z" />
+<glyph unicode="&#x1E85;" horiz-adv-x="747" d="M576 0H483L426 373H422L261 0H168L95 502H185L230 86H234L407 502H478L536 86H540L704 502H792L576 0ZM605 629Q605 606 588 589T547 572Q529 572 516 584T502 615Q502 638 520 655T561 673Q580 673 592 661T605 629ZM418 629Q418 606 400 589T359 572Q341 572 328 584T315 615Q315 638 332 655T373 673Q392 673 405 661T418 629Z" />
+<glyph unicode="&#x1EF2;" horiz-adv-x="572" d="M369 265L332 0H241L277 262L106 667H209L335 345L552 667H652L369 265ZM392 705L233 814L327 854L445 735L392 705Z" />
+<glyph unicode="&#x1EF3;" horiz-adv-x="495" d="M256 -65Q232 -113 212 -144T170 -193T125 -219T66 -226Q45 -226 12 -223L23 -149Q31 -151 40 -151T66 -152Q85 -152 98 -148T124 -132T148 -100T176 -48L209 18L88 502H182L266 111H270L454 502H547L256 -65ZM340 565L181 674L275 714L393 595L340 565Z" />
+<glyph unicode="&#x2013;" horiz-adv-x="598" d="M80 226L90 294H590L580 226H80Z" />
+<glyph unicode="&#x2014;" horiz-adv-x="872" d="M80 227L90 293H864L854 227H80Z" />
+<glyph unicode="&#x2018;" horiz-adv-x="215" d="M195 491H88L202 700H265L195 491Z" />
+<glyph unicode="&#x2019;" horiz-adv-x="215" d="M179 491H117L187 700H294L179 491Z" />
+<glyph unicode="&#x201A;" horiz-adv-x="215" d="M88 -153H26L96 56H203L88 -153Z" />
+<glyph unicode="&#x201C;" horiz-adv-x="403" d="M383 491H277L391 700H453L383 491ZM195 491H88L203 700H265L195 491Z" />
+<glyph unicode="&#x201D;" horiz-adv-x="403" d="M367 491H305L375 700H482L367 491ZM179 491H117L187 700H294L179 491Z" />
+<glyph unicode="&#x201E;" horiz-adv-x="403" d="M276 -154H214L284 56H391L276 -154ZM88 -154H26L96 56H203L88 -154Z" />
+<glyph unicode="&#x2020;" horiz-adv-x="349" d="M284 473L242 171H156L198 473H113L123 544H208L226 675H312L294 544H378L368 473H284Z" />
+<glyph unicode="&#x2021;" horiz-adv-x="349" d="M257 280L237 136H152L172 280H87L97 352H182L200 479H114L124 551H210L227 675H312L295 551H380L370 479H285L267 352H353L343 280H257Z" />
+<glyph unicode="&#x2022;" horiz-adv-x="521" d="M414 329Q414 297 402 269T369 221T320 188T260 176Q228 176 200 188T152 220T119 269T107 329Q107 361 119 389T151 437T200 470T260 482Q292 482 320 470T369 438T402 389T414 329Z" />
+<glyph unicode="&#x2026;" horiz-adv-x="701" d="M172 51Q172 22 152 6T104 -10Q84 -10 68 0T52 34Q52 63 71 79T119 96Q140 96 156 85T172 51ZM416 51Q416 22 396 6T349 -10Q328 -10 312 0T296 34Q296 63 315 79T363 96Q384 96 400 85T416 51ZM661 51Q661 22 640 6T593 -10Q573 -10 557 0T541 34Q541 63 560 79T608 96Q629 96 645 85T661 51Z" />
+<glyph unicode="&#x2030;" horiz-adv-x="999" d="M362 593Q362 558 355 523T332 460T288 413T219 395Q165 395 143 424T120 504Q120 538 127 573T150 636T194 682T262 700Q316 700 339 672T362 593ZM225 438Q252 438 268 454T293 495T304 545T307 592Q307 619 297 637T256 656Q229 656 213 640T189 600T178 550T175 503Q175 476 184 457T225 438ZM179 0H123L650 700H705L179 0ZM669 191Q669 156 663 121T640 57T596 12T526 -6Q473 -6 450 22T427 101Q427 135 434 170T457 234T501 281T569 299Q623 299 646 271T669 191ZM532 37Q559 37 575 54T599 95T611 145T614 192Q614 219 604 237T562 255Q536 255 520 239T496 199T484 149T481 102Q481 75 491 56T532 37ZM976 192Q976 157 969 122T946 59T902 12T834 -6Q780 -6 757 22T734 102Q734 137 740 172T763 236T807 281T877 299Q931 299 953 271T976 192ZM840 37Q866 37 882 54T906 95T918 145T921 192Q921 219 911 237T870 255Q843 255 827 239T803 199T791 149T788 102Q788 74 798 56T840 37Z" />
+<glyph unicode="&#x2039;" horiz-adv-x="263" d="M174 110L62 306L229 502L284 469L140 305L239 144L174 110Z" />
+<glyph unicode="&#x203A;" horiz-adv-x="263" d="M120 110L65 144L207 305L110 469L175 502L287 306L120 110Z" />
+<glyph unicode="&#x2044;" horiz-adv-x="118" d="M-162 0H-215L371 700H424L-162 0Z" />
+<glyph unicode="&#x2070;" horiz-adv-x="349" d="M383 580Q383 541 376 502T350 432T301 381T224 361Q165 361 140 392T114 480Q114 519 121 558T147 628T195 680T272 700Q332 700 357 669T383 580ZM231 409Q261 409 279 427T306 473T319 530T322 582Q322 612 311 631T265 651Q235 651 218 633T191 588T178 532T175 480Q175 450 186 430T231 409Z" />
+<glyph unicode="&#x2074;" horiz-adv-x="317" d="M298 434L289 367H227L236 434H96L88 481L269 694H335L306 484H357L349 434H298ZM144 481H245L267 634L144 481Z" />
+<glyph unicode="&#x2075;" horiz-adv-x="312" d="M342 504Q342 436 302 399T195 361Q164 361 140 368T92 395L127 437Q142 423 160 416T198 408Q239 408 259 431T280 495Q280 522 265 533T224 544Q185 544 156 516L116 546L162 694H349L342 643H199L174 564L177 561Q206 587 248 587Q290 587 316 568T342 504Z" />
+<glyph unicode="&#x2076;" horiz-adv-x="326" d="M356 497Q356 437 318 399T220 361Q164 361 139 389T113 472Q113 511 121 551T148 625T198 679T276 700Q325 700 365 675L334 633Q308 653 274 653Q250 653 234 643T207 618T190 582T181 542Q196 560 217 569T262 578Q302 578 329 560T356 497ZM226 409Q261 409 279 431T297 487Q297 513 282 524T243 536Q211 536 192 514T172 460Q172 434 186 422T226 409Z" />
+<glyph unicode="&#x2077;" horiz-adv-x="288" d="M352 642Q316 616 292 594T251 545T226 481T210 393L207 367H142L143 381Q145 414 153 448T177 517T218 582T282 640V644H112L119 694H349L352 642H352Z" />
+<glyph unicode="&#x2078;" horiz-adv-x="324" d="M350 474Q350 443 339 422T309 387T265 367T212 361Q191 361 171 364T136 377T112 401T102 442Q102 479 119 501T171 538V542Q133 559 133 597Q133 625 143 644T172 674T212 691T259 696Q277 696 295 694T327 684T351 663T360 627Q360 566 304 542V538Q326 528 338 514T350 474ZM239 558Q266 558 284 572T302 615Q302 637 288 645T253 653Q226 653 208 640T190 597Q190 575 204 567T239 558ZM218 405Q250 405 270 422T291 473Q291 499 274 508T234 518Q202 518 182 501T161 450Q161 424 178 415T218 405Z" />
+<glyph unicode="&#x2079;" horiz-adv-x="327" d="M362 585Q362 544 355 504T329 432T280 381T201 361Q175 361 152 367T108 389L139 430Q155 417 169 412T204 407Q230 407 246 417T273 443T289 480T298 523H294Q281 503 260 493T215 483Q173 483 147 502T120 566Q120 596 130 621T159 663T202 690T257 700Q314 700 338 670T362 585ZM231 528Q264 528 284 549T304 603Q304 629 290 641T250 654Q217 654 198 631T179 576Q179 552 193 540T231 528Z" />
+<glyph unicode="&#x2080;" horiz-adv-x="349" d="M315 99Q315 60 308 21T282 -50T233 -101T156 -121Q97 -121 72 -90T46 -2Q46 36 53 75T79 146T128 198T204 218Q264 218 289 187T315 99ZM163 -73Q193 -73 211 -55T238 -9T251 48T254 100Q254 130 243 149T197 169Q167 169 150 151T123 106T110 50T107 -2Q107 -32 118 -52T163 -73Z" />
+<glyph unicode="&#x2081;" horiz-adv-x="280" d="M107 -66L137 146H133L60 108L46 153L158 212H207L168 -66H247L240 -115H17L24 -66H107Z" />
+<glyph unicode="&#x2082;" horiz-adv-x="309" d="M25 -115L23 -61Q40 -48 73 -28T139 17T196 69T221 123Q221 137 212 152T177 167Q153 167 133 158T89 126L60 165Q94 194 124 206T190 218Q235 218 259 195T284 134Q284 101 266 74T220 24T158 -21T91 -65H260L253 -115H25Z" />
+<glyph unicode="&#x2083;" horiz-adv-x="302" d="M260 -4Q260 -60 226 -90T129 -121Q97 -121 68 -113T15 -83L47 -40Q67 -58 86 -65T130 -73Q158 -73 179 -57T201 -7Q201 31 147 31H104L110 75H138Q148 75 161 77T185 86T203 103T211 130Q211 148 199 158T164 169Q142 169 120 162T78 137L56 179Q89 201 116 209T177 218Q220 218 246 200T272 143Q272 87 215 62V58Q260 46 260 -4Z" />
+<glyph unicode="&#x2084;" horiz-adv-x="317" d="M231 -48L221 -115H159L169 -48H29L20 -1L201 212H267L238 2H289L282 -48H231ZM76 -1H177L199 152L76 -1Z" />
+<glyph unicode="&#x2085;" horiz-adv-x="312" d="M274 22Q274 -46 234 -83T127 -121Q96 -121 72 -114T24 -87L59 -45Q91 -74 130 -74Q171 -74 191 -51T212 13Q212 40 197 51T156 62Q117 62 88 34L48 64L94 212H281L274 161H131L106 82L109 79Q138 105 180 105Q222 105 248 86T274 22Z" />
+<glyph unicode="&#x2086;" horiz-adv-x="326" d="M288 15Q288 -45 250 -83T152 -121Q96 -121 71 -93T45 -10Q45 29 53 69T80 143T130 197T208 218Q257 218 297 193L266 151Q240 171 206 171Q182 171 166 161T139 136T122 100T113 60Q128 78 149 87T194 96Q234 96 261 78T288 15ZM158 -73Q193 -73 211 -51T229 5Q229 31 214 42T175 54Q143 54 124 32T104 -22Q104 -48 118 -60T158 -73Z" />
+<glyph unicode="&#x2087;" horiz-adv-x="288" d="M284 160Q248 134 224 112T183 63T158 -1T142 -89L139 -115H74L75 -101Q77 -68 85 -34T109 35T150 100T214 158V162H44L51 212H281L284 160H284Z" />
+<glyph unicode="&#x2088;" horiz-adv-x="324" d="M282 -8Q282 -39 271 -60T241 -95T197 -115T144 -121Q123 -121 103 -118T68 -105T44 -81T34 -40Q34 -3 51 19T103 56V60Q65 77 65 115Q65 143 75 162T104 192T144 209T191 214Q209 214 227 212T259 202T283 181T292 145Q292 84 236 60V56Q258 46 270 32T282 -8ZM171 76Q198 76 216 90T234 133Q234 155 220 163T185 171Q158 171 140 158T122 115Q122 93 136 85T171 76ZM150 -77Q182 -77 202 -60T223 -9Q223 17 206 26T166 36Q134 36 114 19T93 -32Q93 -58 110 -67T150 -77Z" />
+<glyph unicode="&#x2089;" horiz-adv-x="327" d="M294 103Q294 62 287 22T261 -50T212 -101T133 -121Q107 -121 84 -115T40 -93L71 -52Q87 -65 101 -70T136 -75Q162 -75 178 -65T205 -39T221 -2T230 41H226Q213 21 192 11T147 1Q105 1 79 20T52 84Q52 114 62 139T91 181T134 208T189 218Q246 218 270 188T294 103ZM163 46Q196 46 216 67T236 121Q236 147 222 159T182 172Q149 172 130 149T111 94Q111 70 125 58T163 46Z" />
+<glyph unicode="&#x20AC;" horiz-adv-x="575" d="M156 432Q171 486 191 531T243 609T317 660T423 678Q487 678 532 654T615 578L553 524Q528 565 496 584T415 604Q372 604 344 591T296 555T265 501T244 432H510L503 379H235Q230 356 227 333T222 286H490L482 233H218V214Q218 182 222 155T240 107T277 74T338 62Q396 62 436 88T513 157L556 102Q508 51 453 20T325 -12Q263 -12 225 6T165 56T136 134T128 233H53L62 286H132Q134 309 136 332T145 379H74L82 432H156Z" />
+<glyph unicode="&#x2122;" horiz-adv-x="638" d="M586 466L607 612L510 498L446 612L425 466H376L409 700H454L522 577L623 700H669L636 466H586ZM270 656L243 466H191L218 656H139L145 700H355L349 656H270Z" />
+<glyph unicode="&#x2153;" horiz-adv-x="718" d="M203 416L233 628H229L155 590L142 635L254 694H303L264 416H343L336 367H113L120 416H203ZM153 0H100L686 700H739L153 0ZM694 111Q694 55 660 25T563 -6Q531 -6 502 2T449 32L481 75Q501 57 520 50T564 42Q592 42 613 58T635 108Q635 146 581 146H538L544 190H572Q582 190 595 192T619 201T637 218T645 245Q645 263 633 273T598 284Q576 284 554 277T512 252L490 294Q523 316 550 324T611 333Q654 333 680 315T706 258Q706 202 649 177V173Q694 161 694 111Z" />
+<glyph unicode="&#x2154;" horiz-adv-x="718" d="M113 367L111 421Q128 434 161 454T227 499T284 551T309 605Q309 619 300 634T265 649Q241 649 221 640T177 608L148 647Q182 676 212 688T278 700Q323 700 347 677T372 616Q372 583 354 556T308 506T246 461T179 417H348L341 367H113ZM153 0H100L686 700H739L153 0ZM694 111Q694 55 660 25T563 -6Q531 -6 502 2T449 32L481 75Q501 57 520 50T564 42Q592 42 613 58T635 108Q635 146 581 146H538L544 190H572Q582 190 595 192T619 201T637 218T645 245Q645 263 633 273T598 284Q576 284 554 277T512 252L490 294Q523 316 550 324T611 333Q654 333 680 315T706 258Q706 202 649 177V173Q694 161 694 111Z" />
+<glyph unicode="&#x215B;" horiz-adv-x="718" d="M203 416L233 628H229L155 590L142 635L254 694H303L264 416H343L336 367H113L120 416H203ZM153 0H100L686 700H739L153 0ZM701 107Q701 76 690 55T660 20T616 0T563 -6Q542 -6 522 -3T487 10T463 34T453 75Q453 112 470 134T522 171V175Q484 192 484 230Q484 258 494 277T523 307T563 324T610 329Q628 329 646 327T678 317T702 296T711 260Q711 199 655 175V171Q677 161 689 147T701 107ZM590 191Q617 191 635 205T653 248Q653 270 639 278T604 286Q577 286 559 273T541 230Q541 208 555 200T590 191ZM569 38Q601 38 621 55T642 106Q642 132 625 141T585 151Q553 151 533 134T512 83Q512 57 529 48T569 38Z" />
+<glyph unicode="&#x215C;" horiz-adv-x="718" d="M349 478Q349 422 315 392T218 361Q186 361 157 369T104 399L136 442Q156 424 175 417T219 409Q247 409 268 425T290 475Q290 513 236 513H193L199 557H227Q237 557 250 559T274 568T292 585T300 612Q300 630 288 640T253 651Q231 651 209 644T167 619L145 661Q178 683 205 691T266 700Q309 700 335 682T361 625Q361 569 304 544V540Q349 528 349 478ZM153 0H100L686 700H739L153 0ZM701 107Q701 76 690 55T660 20T616 0T563 -6Q542 -6 522 -3T487 10T463 34T453 75Q453 112 470 134T522 171V175Q484 192 484 230Q484 258 494 277T523 307T563 324T610 329Q628 329 646 327T678 317T702 296T711 260Q711 199 655 175V171Q677 161 689 147T701 107ZM590 191Q617 191 635 205T653 248Q653 270 639 278T604 286Q577 286 559 273T541 230Q541 208 555 200T590 191ZM569 38Q601 38 621 55T642 106Q642 132 625 141T585 151Q553 151 533 134T512 83Q512 57 529 48T569 38Z" />
+<glyph unicode="&#x215D;" horiz-adv-x="718" d="M363 504Q363 436 323 399T216 361Q185 361 161 368T113 395L148 437Q163 423 181 416T219 408Q260 408 280 431T301 495Q301 522 286 533T245 544Q206 544 177 516L137 546L183 694H370L363 643H220L195 564L198 561Q227 587 269 587Q311 587 337 568T363 504ZM153 0H100L686 700H739L153 0ZM701 107Q701 76 690 55T660 20T616 0T563 -6Q542 -6 522 -3T487 10T463 34T453 75Q453 112 470 134T522 171V175Q484 192 484 230Q484 258 494 277T523 307T563 324T610 329Q628 329 646 327T678 317T702 296T711 260Q711 199 655 175V171Q677 161 689 147T701 107ZM590 191Q617 191 635 205T653 248Q653 270 639 278T604 286Q577 286 559 273T541 230Q541 208 555 200T590 191ZM569 38Q601 38 621 55T642 106Q642 132 625 141T585 151Q553 151 533 134T512 83Q512 57 529 48T569 38Z" />
+<glyph unicode="&#x215E;" horiz-adv-x="718" d="M410 642Q374 616 350 594T309 545T284 481T268 393L265 367H200L201 381Q203 414 211 448T235 517T276 582T340 640V644H170L177 694H407L410 642ZM153 0H100L686 700H739L153 0ZM701 107Q701 76 690 55T660 20T616 0T563 -6Q542 -6 522 -3T487 10T463 34T453 75Q453 112 470 134T522 171V175Q484 192 484 230Q484 258 494 277T523 307T563 324T610 329Q628 329 646 327T678 317T702 296T711 260Q711 199 655 175V171Q677 161 689 147T701 107ZM590 191Q617 191 635 205T653 248Q653 270 639 278T604 286Q577 286 559 273T541 230Q541 208 555 200T590 191ZM569 38Q601 38 621 55T642 106Q642 132 625 141T585 151Q553 151 533 134T512 83Q512 57 529 48T569 38Z" />
+<glyph unicode="&#x2212;" horiz-adv-x="575" d="M82 224L93 300H565L554 224H82Z" />
+<glyph unicode="&#x2260;" horiz-adv-x="575" d="M431 319L311 207H552L541 132H241L136 30L87 74L149 132H70L81 207H220L339 319H96L107 394H410L520 501L570 458L501 394H578L567 319H431Z" />
+<glyph unicode="&#x2264;" horiz-adv-x="575" d="M89 276L100 355L597 529L585 448L185 316L549 188L538 109L89 276ZM51 0L61 73H533L523 0H51Z" />
+<glyph unicode="&#x2265;" horiz-adv-x="575" d="M66 109L77 188L478 316L113 448L125 529L572 355L561 276L66 109ZM51 0L61 73H533L523 0H51Z" />
+<glyph unicode="&#x24DF;" horiz-adv-x="866" d="M779 334Q779 262 752 200T678 91T568 17T433 -10Q361 -10 298 17T188 90T114 199T87 334Q87 406 114 468T188 577T298 651T433 678Q505 678 568 651T678 578T752 469T779 334ZM741 334Q741 397 718 453T653 552T555 618T433 643Q368 643 312 619T214 552T150 454T126 334Q126 271 150 216T216 118T314 52T433 28Q496 28 552 52T650 118T716 215T741 334ZM623 415Q623 346 586 312T467 278H369V111H301V544Q333 548 371 549T449 551Q488 551 520 544T575 522T610 480T623 415ZM555 412Q555 463 529 481T448 499Q428 499 410 498T369 495V328H448Q472 328 491 331T525 343T547 368T555 412Z" />
+<glyph unicode="&#xF6C3;" horiz-adv-x="358" d="M151 -213H91L139 -59H236L151 -213Z" />
+
+
+</font>
+</defs>
+<text x="40" y="40" font-family="ClearviewATT BkIt" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="ClearviewATT BkIt" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="ClearviewATT BkIt" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="ClearviewATT BkIt" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.ttf b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.ttf
new file mode 100644
index 0000000000..7e7aa05778
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.woff b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.woff
new file mode 100644
index 0000000000..55fbfd6896
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-BkIt.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.eot b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.eot
new file mode 100644
index 0000000000..06a4bf9949
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.svg b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.svg
new file mode 100644
index 0000000000..40656e14f8
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.svg
@@ -0,0 +1,425 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[ClearviewATT Lt]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Copyright (c) Terminal Design, Inc, 2005. All rights reserved.]]></copyright>
+<trademark><![CDATA[Clearview is a trademark of Terminal Design, Inc.]]></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="489" id="ClearviewATT-Lt">
+<font-face font-family="ClearviewATT Lt" panose-1="2 11 5 6 3 5 0 2 0 4" ascent="750" descent="-250" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="484" d="M492 667V0H-8V667H492ZM31 606V65L217 336L31 606ZM453 67V605L268 336L453 67ZM61 35H424L242 299L61 35ZM242 373L419 630H65L242 373Z" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " horiz-adv-x="280" />
+<glyph unicode="!" horiz-adv-x="271" d="M173 667Q168 543 165 422T160 176H112Q110 300 107 421T100 667H173ZM184 38Q184 15 171 3T136 -10Q113 -10 100 2T87 38Q87 85 136 85Q159 85 171 73T184 38Z" />
+<glyph unicode="&quot;" horiz-adv-x="368" d="M284 518H244L228 700H301L284 518ZM123 518H82L67 700H140L123 518Z" />
+<glyph unicode="#" horiz-adv-x="582" d="M548 471H429L406 316H530L523 271H400L378 113H329L350 271H202L181 113H133L154 271H30L36 316H161L183 471H57L63 515H188L211 667H261L239 515H387L409 667H459L435 515H555L548 471ZM231 471L210 316H358L381 471H231Z" />
+<glyph unicode="$" horiz-adv-x="554" d="M480 191Q480 163 469 135T436 83T381 45T305 28V-47H246V29Q203 30 156 47T74 87L101 138Q144 109 189 93T275 76Q344 76 381 106T418 190Q418 213 411 230T390 262T358 288T317 312L215 362Q187 376 164 390T125 421T99 460T90 511Q90 531 97 554T122 599T170 634T248 650V723H308V651Q341 650 381 637T455 602L431 552Q396 576 359 589T278 603Q213 603 183 579T152 511Q152 492 157 479T176 454T209 431T260 404L349 362Q419 329 449 290T480 191Z" />
+<glyph unicode="%" horiz-adv-x="709" d="M296 530Q296 451 269 406T173 361Q105 361 78 406T50 530Q50 610 77 655T173 700Q241 700 268 655T296 530ZM249 531Q249 559 247 583T236 625T212 653T173 663Q149 663 134 653T111 626T100 584T97 530Q97 458 114 428T173 398Q213 398 231 428T249 531ZM167 0H121L533 700H577L167 0ZM659 163Q659 84 632 39T536 -6Q468 -6 441 39T413 163Q413 243 440 288T536 333Q604 333 631 288T659 163ZM612 164Q612 192 610 216T599 258T575 286T536 296Q512 296 497 286T474 259T463 217T460 163Q460 91 478 61T536 31Q576 31 594 61T612 164Z" />
+<glyph unicode="&amp;" horiz-adv-x="572" d="M271 -11Q209 -11 167 6T100 52T63 115T52 184Q52 220 64 249T97 302T144 345T198 381Q184 398 170 416T144 455T125 497T117 541Q117 566 126 591T153 635T201 666T271 678Q314 678 342 665T387 632T410 589T417 548Q417 515 406 490T374 443T328 405T273 370L442 133Q449 148 453 169T457 213Q457 245 451 275T440 323H497Q502 304 506 273T511 213Q511 181 504 149T479 92L545 0H478L439 53Q412 27 373 8T271 -11ZM271 35Q313 35 349 51T408 92L225 344Q204 329 184 313T147 277T122 235T112 185Q112 146 124 118T158 71T209 44T271 35ZM249 403Q303 437 332 468T362 544Q362 556 358 572T344 601T316 624T271 634Q246 634 228 627T199 608T182 579T176 543Q176 525 183 506T202 469T225 433T249 403Z" />
+<glyph unicode="&apos;" horiz-adv-x="207" d="M123 518H82L67 700H140L123 518Z" />
+<glyph unicode="(" horiz-adv-x="278" d="M178 -137Q146 -77 122 -22T83 88T60 198T52 316Q52 378 59 433T83 543T122 653T178 771L218 762Q167 651 141 542T115 317Q115 202 141 93T218 -128L178 -137Z" />
+<glyph unicode=")" horiz-adv-x="278" d="M52 -128Q104 -16 129 93T155 317Q155 433 129 542T52 762L92 771Q124 709 148 654T187 544T210 434T218 316Q218 254 211 199T188 88T148 -22T92 -137L52 -128Z" />
+<glyph unicode="*" horiz-adv-x="316" d="M182 554V552L241 490L194 454L152 531H150L107 455L60 490L120 552V554L33 569L53 624L131 588H132L119 674H179L168 589L169 588L247 625L267 570L182 554Z" />
+<glyph unicode="+" horiz-adv-x="554" d="M307 231V22H252V231H54V285H252V482H307V285H500V231H307Z" />
+<glyph unicode="&#x2c;" horiz-adv-x="184" d="M71 -112H30L63 85H145L71 -112Z" />
+<glyph unicode="-" horiz-adv-x="369" d="M52 232V286H317V232H52Z" />
+<glyph unicode="." horiz-adv-x="184" d="M139 38Q139 15 127 3T91 -10Q69 -10 57 2T44 38Q44 85 92 85Q139 85 139 38Z" />
+<glyph unicode="/" horiz-adv-x="479" d="M76 -115H19L414 700H473L76 -115Z" />
+<glyph unicode="0" horiz-adv-x="554" d="M498 334Q498 250 487 186T449 78T381 12T277 -11Q215 -11 173 11T104 76T67 184T56 334Q56 419 67 483T104 590T172 656T277 678Q339 678 381 656T449 591T486 483T498 334ZM434 335Q434 402 428 456T405 547T358 605T277 626Q226 626 195 606T148 548T126 456T120 335Q120 268 125 214T147 121T195 61T278 40Q327 40 358 61T405 120T428 213T434 335Z" />
+<glyph unicode="1" horiz-adv-x="554" d="M261 54V599H258L97 503L70 551L268 670H322V54H483V0H92V54H261Z" />
+<glyph unicode="2" horiz-adv-x="554" d="M80 0L71 62Q140 125 201 179T307 284T379 383T406 485Q406 507 401 531T382 577T344 611T280 625Q235 625 193 602T113 533L78 578Q127 631 175 654T283 678Q324 678 358 667T416 633T454 575T468 491Q468 442 450 395T393 297T290 186T136 54H484V0H80Z" />
+<glyph unicode="3" horiz-adv-x="554" d="M484 185Q484 147 473 112T437 50T370 6T268 -11Q206 -11 161 4T71 52L100 101Q141 69 182 55T266 40Q296 40 323 47T372 71T407 117T420 188Q420 224 412 249T386 291T338 316T264 324H201V376H223Q280 376 315 387T370 417T397 458T404 505Q404 563 369 594T267 626Q185 626 111 567L83 616Q132 649 176 663T273 678Q313 678 348 668T410 637T451 584T467 506Q467 451 440 412T360 356V354Q423 343 453 302T484 185Z" />
+<glyph unicode="4" horiz-adv-x="554" d="M414 148V0H351V148H68L49 207L340 665H414V202H505V148H414ZM353 596L110 202H354V596H353Z" />
+<glyph unicode="5" horiz-adv-x="554" d="M483 228Q483 162 466 116T419 42T347 2T254 -11Q203 -11 162 2T74 49L102 99Q144 67 180 54T255 40Q338 40 379 84T420 229Q420 322 382 362T270 403Q229 403 195 389T123 337L71 374L120 667H453V613H170L133 399H134Q168 428 202 441T281 454Q378 454 430 400T483 228Z" />
+<glyph unicode="6" horiz-adv-x="554" d="M497 214Q497 161 481 120T437 49T368 5T280 -11Q216 -11 173 8T105 65T68 157T57 283Q57 369 67 441T104 566T178 648T300 678Q339 678 377 668T447 638L422 589Q388 611 361 618T300 625Q258 625 225 611T167 562T130 470T116 326H118Q168 424 291 424Q388 424 442 368T497 214ZM434 211Q434 291 395 331T280 372Q245 372 216 360T166 327T134 274T122 203Q122 125 164 83T281 40Q353 40 393 83T434 211Z" />
+<glyph unicode="7" horiz-adv-x="554" d="M487 609Q422 552 383 498T321 382T292 245T285 73V0H221V29Q221 102 225 181T247 337T305 485T416 611V613H68V667H476L487 609H487Z" />
+<glyph unicode="8" horiz-adv-x="554" d="M496 187Q496 90 442 40T277 -11Q163 -11 110 39T57 187Q57 250 83 292T170 357V360Q122 377 98 417T74 506Q74 539 83 570T114 625T176 663T276 678Q375 678 426 636T478 506Q478 457 455 418T381 360V357Q440 340 468 295T496 187ZM416 507Q416 570 383 598T276 627Q202 627 170 599T137 507Q137 444 170 413T276 381Q416 381 416 507ZM434 185Q434 218 427 245T401 291T353 322T276 333Q240 333 211 326T162 301T130 254T119 180Q119 106 157 73T276 39Q354 39 394 72T434 185Z" />
+<glyph unicode="9" horiz-adv-x="554" d="M497 350Q497 252 483 183T440 71T365 9T257 -11Q213 -11 170 2T89 43L120 88Q154 63 184 52T257 40Q302 40 335 54T392 103T426 200T438 357H436Q416 303 371 274T269 244Q220 244 181 259T114 302T72 370T57 460Q57 513 73 553T119 622T188 664T275 678Q331 678 372 660T442 603T483 501T497 350ZM424 457Q424 538 384 582T275 626Q200 626 160 580T120 458Q120 382 161 339T275 296Q349 296 386 340T424 457Z" />
+<glyph unicode=":" horiz-adv-x="184" d="M140 402Q140 379 128 366T93 353Q70 353 58 366T45 402Q45 449 93 449Q140 449 140 402ZM140 38Q140 15 128 3T93 -10Q70 -10 58 2T45 38Q45 85 93 85Q140 85 140 38Z" />
+<glyph unicode=";" horiz-adv-x="184" d="M71 -112H30L62 85H140L71 -112ZM150 401Q150 378 138 365T102 352Q79 352 67 365T54 401Q54 448 102 448Q150 448 150 401Z" />
+<glyph unicode="&lt;" horiz-adv-x="554" d="M54 230V285L500 500V445L109 258L500 74V20L54 230Z" />
+<glyph unicode="=" horiz-adv-x="554" d="M55 312V366H500V312H55ZM55 147V201H500V147H55Z" />
+<glyph unicode="&gt;" horiz-adv-x="554" d="M54 20V74L444 258L54 445V500L500 285V230L54 20Z" />
+<glyph unicode="?" horiz-adv-x="499" d="M443 513Q443 450 417 410T324 337L266 310Q248 301 238 296T224 283T218 264T217 234V161H156V243Q156 266 158 281T168 308T188 328T222 347L276 371Q308 385 328 399T360 429T376 464T381 508Q381 565 345 595T243 626Q202 626 164 613T84 566L53 612Q102 647 148 662T248 678Q342 678 392 633T443 513ZM232 37Q232 -10 186 -10Q164 -10 152 2T139 37Q139 83 186 83Q232 83 232 37Z" />
+<glyph unicode="@" horiz-adv-x="807" d="M735 277Q735 218 719 174T679 100T625 56T567 41Q538 41 521 57T500 100H497Q472 64 435 49T363 34Q302 34 270 73T237 187Q237 223 247 261T279 332T335 385T417 406Q484 406 514 361H515L521 399H578L542 127Q540 113 546 97T579 81Q590 81 609 90T648 123T681 184T695 279Q695 339 676 386T621 466T538 516T431 534Q348 534 288 505T189 429T132 326T113 214Q113 142 133 86T190 -8T279 -66T394 -86Q457 -86 504 -72T594 -28L614 -60Q566 -93 512 -107T390 -121Q304 -121 244 -93T145 -17T90 91T72 217Q72 286 95 350T164 462T276 540T430 569Q486 569 541 553T639 501T708 410T735 277ZM507 269Q509 286 507 303T496 333T469 355T421 364Q360 364 325 318T290 184Q290 127 313 102T380 76Q432 76 460 109T496 190L507 269Z" />
+<glyph unicode="A" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611Z" />
+<glyph unicode="B" horiz-adv-x="620" d="M556 179Q556 146 547 114T515 56T450 16T346 0H92V664Q108 666 131 668T179 671T232 674T286 675Q326 675 368 671T446 650T504 597T527 499Q527 445 500 407T424 353V351Q485 340 520 294T556 179ZM463 499Q463 540 449 564T411 602T354 620T286 624Q258 624 225 623T155 617V373Q187 372 220 372T286 372Q324 372 356 375T412 392T449 430T463 499ZM491 182Q491 234 473 262T426 304T361 320T287 323Q263 323 246 323T214 323T185 323T155 321V52Q191 50 220 50T287 50Q328 50 365 53T430 69T474 108T491 182Z" />
+<glyph unicode="C" horiz-adv-x="614" d="M580 76Q526 28 472 9T354 -11Q214 -11 142 80T69 334Q69 398 84 460T135 570T225 648T361 678Q421 678 472 658T575 596L537 552Q492 590 451 607T362 625Q312 625 270 607T198 553T152 463T135 334Q135 281 145 229T180 136T249 68T359 42Q405 42 452 60T545 120L580 76H580Z" />
+<glyph unicode="D" horiz-adv-x="650" d="M581 338Q581 276 567 216T518 107T426 30T282 0H92V664Q129 669 176 672T279 675Q355 675 412 651T506 582T562 475T581 338ZM515 338Q515 386 505 436T469 528T398 596T280 623Q248 623 216 621T155 616V53H263Q393 53 454 123T515 338Z" />
+<glyph unicode="E" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92Z" />
+<glyph unicode="F" horiz-adv-x="485" d="M155 612V381H425V326H155V0H92V667H455V612H155Z" />
+<glyph unicode="G" horiz-adv-x="679" d="M360 40Q409 40 445 54T504 95T539 156T551 234V287H368V340H610V266Q610 128 544 59T357 -10Q278 -10 223 18T134 95T85 205T69 333Q69 392 83 453T132 565T223 646T364 678Q429 678 480 656T574 599L538 555Q491 593 450 609T365 625Q298 625 254 600T183 534T146 440T135 333Q135 280 144 228T179 134T248 66T360 40Z" />
+<glyph unicode="H" horiz-adv-x="645" d="M490 0V331H155V0H92V667H155V388H490V667H553V0H490Z" />
+<glyph unicode="I" horiz-adv-x="247" d="M92 0V667H155V0H92Z" />
+<glyph unicode="J" horiz-adv-x="425" d="M333 151Q333 66 291 28T177 -11Q141 -11 108 -2T44 30L70 81Q98 59 120 51T170 42Q225 42 247 70T270 156V667H333V151H333Z" />
+<glyph unicode="K" horiz-adv-x="545" d="M446 0L232 343L155 252V0H92V667H155V333H157L428 667H505L279 388L522 0H446Z" />
+<glyph unicode="L" horiz-adv-x="473" d="M92 0V667H155V55H436V0H92Z" />
+<glyph unicode="M" horiz-adv-x="750" d="M596 0V559H592L375 214L158 558H154V0H92V667H156L375 315L595 667H658V0H596Z" />
+<glyph unicode="N" horiz-adv-x="684" d="M530 0L158 560H154V0H92V667H159L526 110H530V667H592V0H530Z" />
+<glyph unicode="O" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333Z" />
+<glyph unicode="P" horiz-adv-x="585" d="M527 471Q527 362 475 313T315 263H155V0H92V664Q143 669 189 672T286 675Q341 675 385 665T461 632T510 569T527 471ZM463 467Q463 512 452 542T419 590T363 615T285 623Q250 623 220 621T155 615V315H289Q333 315 366 321T420 344T452 390T463 467Z" />
+<glyph unicode="Q" horiz-adv-x="732" d="M663 334Q663 287 656 241T632 154T586 80T515 25V24Q531 19 544 11T572 -6T602 -24T641 -38L624 -89Q595 -85 570 -72T523 -44T479 -19T438 -7Q422 -7 402 -9T366 -11Q285 -11 229 16T137 90T85 199T69 334Q69 405 85 468T137 577T229 651T366 678Q447 678 503 651T595 578T647 469T663 334ZM597 334Q597 392 586 444T548 537T477 602T366 626Q300 626 256 602T184 538T146 444T135 333Q135 275 146 222T185 129T256 65T366 41Q432 41 476 65T548 130T586 224T597 334Z" />
+<glyph unicode="R" horiz-adv-x="581" d="M459 0L375 246Q367 271 351 281T307 292H155V0H92V664Q117 667 139 669T183 672T227 674T276 675Q321 675 363 667T438 637T490 580T510 487Q510 424 486 379T399 312V311Q410 306 419 292T433 265L526 0H459ZM446 481Q446 523 433 550T398 594T344 616T277 623Q243 623 215 622T155 615V344H297Q369 344 407 374T446 481Z" />
+<glyph unicode="S" horiz-adv-x="506" d="M454 180Q454 89 402 39T252 -11Q191 -11 140 5T37 57L67 107Q103 79 147 60T246 40Q315 40 352 75T390 180Q390 215 375 238T336 279T280 309T216 336Q186 349 157 364T106 400T70 450T56 519Q56 561 72 591T114 640T175 669T248 678Q300 678 346 663T436 618L411 565Q376 593 336 609T247 626Q222 626 199 620T159 600T132 567T121 520Q121 491 132 471T162 436T206 409T259 386Q293 372 328 357T391 319T436 263T454 180Z" />
+<glyph unicode="T" horiz-adv-x="482" d="M272 612V0H209V612H21V667H461V612H272Z" />
+<glyph unicode="U" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560Z" />
+<glyph unicode="V" horiz-adv-x="545" d="M309 0H234L25 667H93L270 65H274L453 667H520L309 0Z" />
+<glyph unicode="W" horiz-adv-x="869" d="M673 0H594L435 571H431L276 0H192L34 667H103L236 68H240L403 667H466L632 68H636L768 667H835L673 0Z" />
+<glyph unicode="X" horiz-adv-x="519" d="M427 0L261 291H257L92 0H20L224 345L45 667H117L261 393H265L416 667H488L301 339L499 0H427Z" />
+<glyph unicode="Y" horiz-adv-x="533" d="M299 267V0H235V264L18 667H88L267 323L446 667H515L299 267Z" />
+<glyph unicode="Z" horiz-adv-x="532" d="M47 0V55L407 612H62V667H477V613L115 54H485V0H47Z" />
+<glyph unicode="[" horiz-adv-x="278" d="M78 -94V751H232V708H135V-50H232V-94H78Z" />
+<glyph unicode="\" horiz-adv-x="479" d="M399 -115L19 667H78L458 -115H399Z" />
+<glyph unicode="]" horiz-adv-x="278" d="M46 -94V-50H143V708H46V751H200V-94H46Z" />
+<glyph unicode="^" horiz-adv-x="331" d="M284 526L164 603L40 526L6 559L164 660L322 559L284 526Z" />
+<glyph unicode="_" horiz-adv-x="484" d="M-8 -130V-85H492V-130H-8Z" />
+<glyph unicode="`" horiz-adv-x="362" d="M243 565L81 669L141 701L274 587L243 565Z" />
+<glyph unicode="a" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281Z" />
+<glyph unicode="b" horiz-adv-x="550" d="M492 250Q492 200 482 153T450 70T390 11T297 -11Q239 -11 200 13T140 83H137L126 0H82V700H141V437H143Q166 480 208 497T295 515Q355 515 393 493T453 435T483 350T492 250ZM431 250Q431 289 427 328T407 398T362 448T284 467Q221 467 181 433T141 331V197Q141 164 150 135T178 83T222 48T284 35Q330 35 358 54T403 105T425 174T431 250Z" />
+<glyph unicode="c" horiz-adv-x="482" d="M422 411Q395 437 361 451T285 466Q237 466 206 448T155 399T127 330T119 251Q119 211 127 173T154 105T205 57T285 38Q326 38 358 51T424 94L446 50Q411 21 372 5T282 -11Q224 -11 182 9T113 64T72 146T58 249Q58 305 71 353T112 438T182 494T283 515Q330 515 371 499T448 456L422 411Z" />
+<glyph unicode="d" horiz-adv-x="550" d="M417 0L413 82H409Q360 -11 254 -11Q198 -11 161 10T100 66T68 149T58 250Q58 302 66 349T97 434T157 493T255 515Q302 515 340 499T407 438H409V700H468V0H417ZM409 331Q409 398 369 432T266 467Q217 467 188 448T144 398T124 328T119 250Q119 212 125 174T147 105T192 55T267 35Q301 35 327 48T372 83T399 135T409 197V331Z" />
+<glyph unicode="e" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416Z" />
+<glyph unicode="f" horiz-adv-x="296" d="M280 652Q269 655 260 656T237 658Q213 658 199 655T178 643T168 621T166 584V502H273V453H167V0H107V452H30V502H107V583Q107 614 111 637T128 675T164 697T227 705Q239 705 252 704T280 700V652H280Z" />
+<glyph unicode="g" horiz-adv-x="549" d="M467 -54Q467 -111 453 -145T415 -197T359 -220T291 -226Q231 -226 184 -214T91 -178L112 -128Q154 -153 197 -165T289 -178Q322 -178 344 -173T381 -155T401 -120T408 -64V83H406Q380 34 344 14T258 -7Q202 -7 164 13T102 67T68 149T58 250Q58 301 66 349T96 434T156 493T255 515Q306 515 345 498T412 433H413L417 502H467V-54ZM408 332Q408 399 368 433T265 467Q217 467 188 448T144 398T124 328T119 250Q119 208 126 170T150 103T195 57T267 39Q301 39 327 52T372 86T399 137T408 199V332Z" />
+<glyph unicode="h" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T390 419T354 451T288 463Q217 463 179 427T141 323V0H82V700H141V434H142Q161 474 202 494T300 515Q351 515 383 502T434 463T459 401T466 318V0H407Z" />
+<glyph unicode="i" horiz-adv-x="223" d="M82 0V502H141V0H82ZM151 623Q151 606 140 595T111 584Q94 584 83 595T72 623Q72 641 83 651T111 662Q130 662 140 652T151 623Z" />
+<glyph unicode="j" horiz-adv-x="229" d="M156 623Q156 604 145 594T116 584Q100 584 89 594T77 623Q77 642 88 652T116 662Q133 662 144 652T156 623ZM146 -51Q146 -91 139 -123T114 -178T67 -213T-4 -226Q-19 -226 -34 -225T-63 -222V-171Q-36 -174 -15 -174Q18 -174 38 -166T69 -141T83 -101T87 -47V502H146V-51Z" />
+<glyph unicode="k" horiz-adv-x="477" d="M379 0L228 264L141 162V0H82V700H141V237H144L358 502H434L271 305L451 0H379Z" />
+<glyph unicode="l" horiz-adv-x="251" d="M222 0Q214 -2 200 -3T172 -5Q125 -5 104 15T82 91V700H141V107Q141 86 144 74T154 57T172 50T200 49H222V0H222Z" />
+<glyph unicode="m" horiz-adv-x="822" d="M681 0V313Q681 344 678 371T664 419T631 451T572 463Q441 463 441 333V0H382V318Q382 351 378 377T362 423T328 452T269 463Q238 463 215 452T175 422T150 379T141 329V0H82V502H133L140 437H142Q164 476 200 495T282 515Q330 515 367 497T422 432Q445 477 485 496T573 515Q623 515 655 501T706 462T732 400T740 319V0H681Z" />
+<glyph unicode="n" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T388 419T351 451T286 463Q215 463 178 426T141 323V0H82V502H133L139 437H141Q190 515 297 515Q349 515 382 502T434 464T459 402T466 319V0H407Z" />
+<glyph unicode="o" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251Z" />
+<glyph unicode="p" horiz-adv-x="549" d="M491 250Q491 199 482 152T449 68T388 11T292 -11Q238 -11 202 12T143 73L141 74V-220H82V502H132L138 436H140Q166 479 205 497T292 515Q353 515 391 493T452 434T482 349T491 250ZM430 250Q430 290 426 328T406 398T362 448T282 467Q219 467 180 432T141 330V198Q141 165 150 135T177 83T221 48T281 35Q327 35 356 54T401 104T424 173T430 250Z" />
+<glyph unicode="q" horiz-adv-x="550" d="M478 -236Q459 -230 446 -223T424 -205T412 -176T408 -134V73H405Q358 -11 257 -11Q199 -11 161 11T100 69T68 153T58 250Q58 301 66 349T96 434T156 493T255 515Q300 515 342 498T411 435H412L418 502H467V-119Q467 -134 468 -145T473 -163T485 -177T507 -189L478 -236ZM408 331Q408 396 369 431T266 467Q216 467 187 448T143 398T124 328T120 250Q120 212 126 174T148 105T194 55T268 35Q302 35 328 48T372 83T399 134T408 197V331Z" />
+<glyph unicode="r" horiz-adv-x="343" d="M302 461Q227 461 184 432T141 333V0H82V502H132L138 440H139Q162 480 203 497T297 514H319V461H302Z" />
+<glyph unicode="s" horiz-adv-x="428" d="M383 136Q383 97 370 69T333 24T280 -2T216 -11Q164 -11 122 1T39 38L62 85Q94 63 136 50T216 36Q238 36 258 41T293 58T317 88T326 132Q326 156 316 172T289 200T250 220T206 236Q176 246 148 257T99 286T65 327T52 386Q52 422 67 446T105 486T158 508T218 515Q259 515 296 505T368 477L350 429Q325 444 290 455T219 467Q197 467 177 463T142 448T118 422T109 383Q109 362 119 347T147 321T187 301T235 285Q262 276 288 265T336 237T370 196T383 136Z" />
+<glyph unicode="t" horiz-adv-x="332" d="M290 -1Q280 -3 265 -4T236 -5Q194 -5 169 3T131 28T113 66T109 116V452H25V502H109V642H168V502H290V452H168V126Q168 106 170 91T180 66T206 52T253 47H290V-1H290Z" />
+<glyph unicode="u" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409Z" />
+<glyph unicode="v" horiz-adv-x="455" d="M267 0H190L24 502H87L228 50H232L368 502H431L267 0Z" />
+<glyph unicode="w" horiz-adv-x="720" d="M550 0H477L362 411H358L240 0H169L29 502H90L206 53H210L334 502H385L510 54H514L631 502H691L550 0Z" />
+<glyph unicode="x" horiz-adv-x="440" d="M354 0L224 224H220L88 0H19L188 266L38 502H106L222 305H226L339 502H403L259 268L421 0H354Z" />
+<glyph unicode="y" horiz-adv-x="466" d="M230 -88Q215 -128 202 -154T172 -197T136 -219T89 -226Q81 -226 69 -226T46 -224V-172Q55 -174 63 -174T83 -174Q102 -174 115 -169T139 -151T161 -113T185 -49L209 21L23 502H87L238 89H242L380 502H443L230 -88Z" />
+<glyph unicode="z" horiz-adv-x="441" d="M42 0V49L329 452H50V502H394V452L108 50H399V0H42Z" />
+<glyph unicode="{" horiz-adv-x="313" d="M231 -94Q198 -94 179 -84T149 -57T136 -16T133 35V216Q133 247 123 270T78 300V357Q113 365 123 387T133 442V624Q133 651 136 674T149 714T178 742T229 752H264V709H243Q221 709 210 702T195 682T190 655T190 622V437Q190 417 188 403T181 376T166 353T140 329Q156 317 166 306T181 283T188 255T190 220V37Q190 13 191 -3T198 -30T214 -45T244 -50H264V-94H231Z" />
+<glyph unicode="|" horiz-adv-x="216" d="M82 -200V740H134V-200H82Z" />
+<glyph unicode="}" horiz-adv-x="313" d="M235 300Q200 293 191 270T181 216V35Q181 7 178 -16T165 -57T135 -84T83 -94H49V-50H71Q90 -50 100 -46T116 -31T122 -4T123 37V220Q123 240 125 255T132 282T147 306T173 329Q157 341 147 352T132 376T125 402T123 437V622Q123 639 123 654T119 682T104 701T72 709H49V752H84Q116 752 135 742T165 715T178 674T181 624V442Q181 426 183 413T191 388T207 369T235 357V300H235Z" />
+<glyph unicode="~" horiz-adv-x="382" d="M371 622Q345 558 275 558Q254 558 235 564T196 577T157 591T119 597Q98 597 83 589T56 556L17 574Q37 609 59 623T117 638Q138 638 157 632T195 619T232 605T270 599Q290 599 305 608T330 638L371 622H371Z" />
+<glyph unicode="&#xA0;" horiz-adv-x="300" />
+<glyph unicode="&#xA1;" horiz-adv-x="271" d="M98 -162Q103 -38 106 83T111 329H159Q163 85 171 -162H98ZM87 467Q87 490 100 502T135 515Q158 515 171 503T184 467Q184 420 135 420Q112 420 100 432T87 467Z" />
+<glyph unicode="&#xA2;" horiz-adv-x="554" d="M472 142Q441 116 406 99T328 77V0H270V78Q218 83 182 105T124 163T91 244T80 341Q80 390 91 434T125 512T184 569T270 598V667H328V599Q368 597 403 582T471 542L445 497Q388 551 308 551Q261 551 230 534T179 488T151 422T143 342Q143 300 151 261T177 192T227 144T304 126Q342 126 376 140T444 184L472 142H472Z" />
+<glyph unicode="&#xA3;" horiz-adv-x="554" d="M488 0H66V52H83Q129 52 156 87T183 186Q183 218 176 259T159 336H82V385H148Q139 428 130 463T121 530Q121 602 166 640T290 678Q332 678 375 666T454 631L435 580Q406 600 373 612T298 625Q238 625 211 600T184 524Q184 489 192 454T208 385H355V336H217Q228 298 235 262T243 184Q243 138 229 107T192 57V54H488V0Z" />
+<glyph unicode="&#xA4;" horiz-adv-x="554" d="M79 342Q79 376 90 406T121 460L56 525L90 559L155 493Q180 513 210 524T276 536Q311 536 341 525T397 493L463 559L497 525L431 459Q473 409 473 339Q473 304 462 274T430 218L497 151L463 117L396 184Q371 164 341 154T276 143Q242 143 212 153T156 184L90 118L56 152L122 218Q101 242 90 273T79 339V342ZM134 340Q134 310 145 284T175 239T221 208T276 197Q306 197 331 208T376 238T407 284T418 340Q418 369 407 395T377 440T332 471T276 482Q247 482 221 471T176 441T145 395T134 340Z" />
+<glyph unicode="&#xA5;" horiz-adv-x="554" d="M352 343H484V298H326L309 267V199H484V155H309V0H247V155H73V199H247V264L228 298H73V343H202L36 667H106L279 320L450 667H518L352 343Z" />
+<glyph unicode="&#xA6;" horiz-adv-x="216" d="M82 323V740H134V323H82ZM82 -200V217H134V-200H82Z" />
+<glyph unicode="&#xA7;" horiz-adv-x="483" d="M415 291Q415 267 408 246T382 209T335 182T262 170V168Q335 142 378 105T421 5Q421 -15 415 -38T390 -81T338 -114T252 -128Q204 -128 165 -118T84 -84L106 -35Q138 -57 175 -68T247 -80Q304 -80 330 -58T357 6Q357 23 353 36T338 62T309 85T264 109Q258 111 247 116T223 126T201 136T186 143Q119 173 91 205T62 284Q62 317 74 339T107 376T155 398T214 406V408Q188 418 164 431T119 462T87 502T75 551Q75 609 118 643T246 678Q284 678 318 670T389 647L374 597Q344 611 313 620T250 629Q229 629 209 626T173 614T148 590T138 553Q138 524 158 502T208 465Q232 455 257 445T314 418Q338 407 356 397T388 372T408 339T415 291ZM356 286Q356 303 352 318T335 346T299 365T239 372Q221 372 200 370T161 359T132 333T120 285Q120 276 123 262T137 234T173 210T239 200Q303 200 329 220T356 286Z" />
+<glyph unicode="&#xA8;" horiz-adv-x="362" d="M306 614Q306 597 294 585T265 573Q248 573 236 585T224 614Q224 631 236 643T265 655Q282 655 294 643T306 614ZM137 614Q137 597 125 585T96 573Q79 573 67 585T55 614Q55 631 67 643T96 655Q113 655 125 643T137 614Z" />
+<glyph unicode="&#xA9;" horiz-adv-x="906" d="M799 334Q799 262 772 200T698 91T588 17T453 -10Q381 -10 318 17T208 90T134 199T107 334Q107 406 134 468T208 577T318 651T453 678Q525 678 588 651T698 578T772 469T799 334ZM766 334Q766 398 742 454T676 554T577 622T453 647Q386 647 329 622T230 554T165 455T141 334Q141 270 166 214T233 115T332 48T453 23Q517 23 573 47T673 114T741 213T766 334ZM614 173Q582 145 545 129T460 112Q402 112 364 128T304 173T272 243T263 334Q263 385 273 425T307 494T369 538T464 553Q510 553 546 538T615 494L592 458Q566 483 535 497T465 511Q418 511 389 499T343 463T320 408T313 335Q313 294 319 261T341 203T386 166T461 153Q499 153 530 168T591 210L614 173Z" />
+<glyph unicode="&#xAA;" horiz-adv-x="377" d="M288 370Q273 376 266 383T254 402H252Q230 385 203 376T150 366Q110 366 84 389T57 458Q57 491 72 512T113 547T175 566T251 572V581Q251 614 241 627T196 641Q169 641 144 636T80 610L63 643Q98 663 129 671T196 680Q245 680 271 659T297 588V440Q297 421 299 416T315 403L288 370ZM251 542Q176 542 140 520T103 457Q103 431 119 415T159 399Q180 399 202 407T251 435V542Z" />
+<glyph unicode="&#xAB;" horiz-adv-x="430" d="M339 117L192 309L339 502L381 476L250 309L380 144L339 117ZM171 114L21 309L170 505L212 479L80 309L212 141L171 114Z" />
+<glyph unicode="&#xAC;" horiz-adv-x="554" d="M446 231H54V285H500V49H446V231Z" />
+<glyph unicode="&#xAD;" horiz-adv-x="369" d="M52 232V286H317V232H52Z" />
+<glyph unicode="&#xAE;" horiz-adv-x="479" d="M226 361Q190 361 158 374T102 411T65 464T51 530Q51 565 64 596T102 650T158 686T226 700Q262 700 293 687T349 650T386 596T400 530Q400 495 387 465T349 411T294 375T226 361ZM227 676Q195 676 168 664T120 632T88 586T76 530Q76 501 88 475T121 429T169 397T226 385Q256 385 283 396T331 427T364 473T376 530Q376 559 365 585T333 632T285 664T227 676ZM164 623Q184 625 199 626T228 627Q256 627 279 615T302 568Q302 551 294 540T272 519L306 439H268L241 503Q239 508 238 509T230 510H197V439H164V623ZM229 596Q221 596 214 596T198 595V538H231Q247 538 258 545T269 566Q269 583 259 589T229 596Z" />
+<glyph unicode="&#xAF;" horiz-adv-x="362" d="M37 590V632H322V590H37Z" />
+<glyph unicode="&#xB0;" horiz-adv-x="389" d="M332 542Q332 513 321 488T292 445T248 415T195 404Q166 404 141 415T97 444T68 488T57 542Q57 570 67 595T97 639T141 669T195 680Q223 680 248 669T291 640T321 596T332 542ZM298 542Q298 563 290 582T268 615T235 637T195 645Q173 645 154 637T121 615T98 582T90 542Q90 521 98 502T120 469T154 447T195 439Q216 439 235 447T268 469T290 502T298 542Z" />
+<glyph unicode="&#xB1;" horiz-adv-x="554" d="M307 261V92H252V261H54V315H252V506H307V315H500V261H307ZM54 0V53H500V0H54Z" />
+<glyph unicode="&#xB2;" horiz-adv-x="299" d="M42 367L35 411Q79 450 111 477T165 527T196 568T207 610Q207 619 205 628T196 644T179 657T151 662Q128 662 109 652T69 619L42 649Q69 676 96 688T156 700Q201 700 227 678T254 611Q254 591 248 573T223 532T172 479T84 406H258V367H42Z" />
+<glyph unicode="&#xB3;" horiz-adv-x="292" d="M251 459Q251 418 225 390T137 361Q107 361 82 369T30 396L50 430Q72 413 92 406T134 398Q163 398 184 412T205 461Q205 492 189 505T136 519H99V555H117Q163 555 179 571T196 610Q196 662 136 662Q115 662 95 655T57 632L35 665Q63 685 87 692T141 700Q185 700 213 679T242 612Q242 588 229 570T193 543V542Q251 528 251 459Z" />
+<glyph unicode="&#xB4;" horiz-adv-x="362" d="M119 565L88 589L224 704L281 671L119 565Z" />
+<glyph unicode="&#xB5;" horiz-adv-x="568" d="M423 0L415 65H413Q404 50 392 37T362 13T321 -4T267 -11Q250 -11 233 -9T201 -1T173 16T151 44V-220H96V502H156V238Q156 190 159 153T174 91T209 54T273 41Q350 41 381 81T412 188V502H472V0H423Z" />
+<glyph unicode="&#xB6;" horiz-adv-x="473" d="M345 0Q273 0 218 19T126 73T71 153T52 252Q52 304 70 349T126 429T218 482T345 502H381V0H345Z" />
+<glyph unicode="&#xB7;" horiz-adv-x="184" d="M139 235Q139 212 127 200T91 187Q69 187 57 199T44 235Q44 282 92 282Q139 282 139 235Z" />
+<glyph unicode="&#xB8;" horiz-adv-x="362" d="M269 -124Q269 -160 245 -181T176 -203Q153 -203 134 -197T97 -179L111 -151Q129 -160 143 -164T173 -169Q197 -169 213 -160T229 -125Q229 -99 213 -91T173 -83Q163 -83 156 -84T139 -88L126 -72L165 0H204L173 -53H174H199Q231 -53 250 -70T269 -124Z" />
+<glyph unicode="&#xB9;" horiz-adv-x="268" d="M119 405V641H117L46 599L29 633L128 694H164V405H245V367H36V405H119Z" />
+<glyph unicode="&#xBA;" horiz-adv-x="377" d="M325 523Q325 448 290 407T189 366Q124 366 88 407T52 523Q52 598 88 639T189 680Q253 680 289 639T325 523ZM279 523Q279 575 260 607T189 640Q136 640 117 608T98 523Q98 456 122 431T189 405Q230 405 254 430T279 523Z" />
+<glyph unicode="&#xBB;" horiz-adv-x="429" d="M259 114L218 141L349 309L217 479L259 505L408 309L259 114ZM90 117L49 144L179 309L49 476L90 502L238 309L90 117Z" />
+<glyph unicode="&#xBC;" horiz-adv-x="718" d="M154 405V641H152L81 599L64 633L163 694H199V405H280V367H71V405H154ZM181 0H139L610 700H652L181 0ZM632 70V0H585V70H443L430 108L578 327H632V109H682V70H632ZM474 107H586V279L474 107Z" />
+<glyph unicode="&#xBD;" horiz-adv-x="690" d="M144 405V641H142L71 599L54 633L153 694H189V405H270V367H61V405H144ZM138 0H96L567 700H609L138 0ZM429 0L422 44Q466 83 498 110T552 160T583 201T594 243Q594 252 592 261T583 277T566 290T538 295Q515 295 496 285T456 252L429 282Q456 309 483 321T543 333Q588 333 614 311T641 244Q641 224 635 206T610 165T559 112T471 39H645V0H429Z" />
+<glyph unicode="&#xBE;" horiz-adv-x="682" d="M285 459Q285 418 259 390T171 361Q141 361 116 369T64 396L84 430Q106 413 126 406T168 398Q197 398 218 412T239 461Q239 492 223 505T170 519H133V555H151Q197 555 213 571T230 610Q230 662 170 662Q149 662 129 655T91 632L69 665Q97 685 121 692T175 700Q219 700 247 679T276 612Q276 588 263 570T227 543V542Q285 528 285 459ZM161 0H119L590 700H632L161 0ZM603 70V0H556V70H414L401 108L549 327H603V109H653V70H603ZM445 107H557V279L445 107Z" />
+<glyph unicode="&#xBF;" horiz-adv-x="499" d="M56 -8Q56 55 82 95T175 168L233 195Q251 204 261 209T275 222T281 241T282 271V344H343V262Q343 239 341 224T331 197T311 177T277 158L223 134Q191 120 171 106T139 76T123 41T118 -3Q118 -60 154 -90T256 -121Q297 -121 335 -108T415 -61L446 -107Q397 -142 351 -157T251 -173Q157 -173 107 -128T56 -8ZM267 468Q267 515 313 515Q335 515 347 503T360 468Q360 422 313 422Q267 422 267 468Z" />
+<glyph unicode="&#xC0;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM326 705L164 809L224 841L357 727L326 705Z" />
+<glyph unicode="&#xC1;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM291 705L260 729L396 844L453 811L291 705Z" />
+<glyph unicode="&#xC2;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM419 704L304 786L190 704L163 739L304 838L447 739L419 704Z" />
+<glyph unicode="&#xC3;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM458 774Q449 742 424 727T372 712Q356 712 339 718T305 731T273 744T244 750Q228 750 212 743T187 711L152 734Q168 770 191 783T240 796Q256 796 273 790T307 777T339 764T369 758Q386 758 400 766T422 796L458 774H458Z" />
+<glyph unicode="&#xC4;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM431 754Q431 737 419 725T390 713Q373 713 361 725T349 754Q349 771 361 783T390 795Q407 795 419 783T431 754ZM262 754Q262 737 250 725T221 713Q204 713 192 725T180 754Q180 771 192 783T221 795Q238 795 250 783T262 754Z" />
+<glyph unicode="&#xC5;" horiz-adv-x="613" d="M407 730Q407 700 392 677T353 642L585 0H518L458 171H153L93 0H28L257 641Q232 652 217 676T201 730Q201 751 209 769T231 801T264 823T305 831Q326 831 344 823T377 802T399 770T407 730ZM306 614H304L168 225H440L306 614ZM368 730Q368 756 350 774T305 793Q278 793 260 775T241 730Q241 702 259 684T305 665Q331 665 349 683T368 730Z" />
+<glyph unicode="&#xC6;" horiz-adv-x="868" d="M449 0V171H190L94 0H25L411 667H820V613H512V385H790V330H512V54H830V0H449ZM450 610H442L217 226H450V610Z" />
+<glyph unicode="&#xC7;" horiz-adv-x="614" d="M438 -127Q438 -165 413 -185T347 -205Q325 -205 304 -199T266 -183L281 -154Q295 -162 310 -167T346 -173Q366 -173 382 -164T398 -129Q398 -104 383 -95T345 -86Q326 -86 308 -91L295 -76L331 -10Q202 -2 136 87T69 334Q69 398 84 460T135 570T225 648T361 678Q421 678 472 658T575 596L537 552Q492 590 451 607T362 625Q312 625 270 607T198 553T152 463T135 334Q135 281 145 229T180 136T249 68T359 42Q405 42 452 60T545 120L580 76Q528 31 478 11T368 -11L341 -58L342 -59Q354 -56 368 -56Q400 -56 419 -74T438 -127Z" />
+<glyph unicode="&#xC8;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM327 705L165 809L225 841L358 727L327 705Z" />
+<glyph unicode="&#xC9;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM255 705L224 729L360 844L417 811L255 705Z" />
+<glyph unicode="&#xCA;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM394 704L279 786L165 704L138 739L279 838L422 739L394 704Z" />
+<glyph unicode="&#xCB;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM406 754Q406 737 394 725T365 713Q348 713 336 725T324 754Q324 771 336 783T365 795Q382 795 394 783T406 754ZM237 754Q237 737 225 725T196 713Q179 713 167 725T155 754Q155 771 167 783T196 795Q213 795 225 783T237 754Z" />
+<glyph unicode="&#xCC;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM144 705L-18 809L42 841L175 727L144 705Z" />
+<glyph unicode="&#xCD;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM109 705L78 729L214 844L271 811L109 705Z" />
+<glyph unicode="&#xCE;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM241 704L126 786L12 704L-15 739L126 838L269 739L241 704Z" />
+<glyph unicode="&#xCF;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM250 754Q250 737 238 725T209 713Q192 713 180 725T168 754Q168 771 180 783T209 795Q226 795 238 783T250 754ZM81 754Q81 737 69 725T40 713Q23 713 11 725T-1 754Q-1 771 11 783T40 795Q57 795 69 783T81 754Z" />
+<glyph unicode="&#xD0;" horiz-adv-x="650" d="M581 338Q581 276 567 216T518 107T425 30T279 0H92V332H32V385H92V664Q113 666 137 668T187 671T236 674T279 675Q358 675 415 650T509 579T563 473T581 338ZM516 338Q516 386 507 436T471 527T400 594T283 620Q250 620 217 619T155 614V385H311V332H155V53H263Q397 53 456 126T516 338Z" />
+<glyph unicode="&#xD1;" horiz-adv-x="684" d="M530 0L158 560H154V0H92V667H159L526 110H530V667H592V0H530ZM506 774Q497 742 472 727T420 712Q404 712 387 718T353 731T321 744T292 750Q276 750 260 743T235 711L200 734Q216 770 239 783T288 796Q304 796 321 790T355 777T387 764T417 758Q434 758 448 766T470 796L506 774H506Z" />
+<glyph unicode="&#xD2;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM425 706L263 810L323 842L456 728L425 706Z" />
+<glyph unicode="&#xD3;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM343 706L312 730L448 845L505 812L343 706Z" />
+<glyph unicode="&#xD4;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM485 704L370 786L256 704L229 739L370 838L513 739L485 704Z" />
+<glyph unicode="&#xD5;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM516 775Q507 743 482 728T430 713Q414 713 397 719T363 732T331 745T302 751Q286 751 270 744T245 712L210 735Q226 771 249 784T298 797Q314 797 331 791T365 778T397 765T427 759Q444 759 458 767T480 797L516 775H516Z" />
+<glyph unicode="&#xD6;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM490 754Q490 737 478 725T449 713Q432 713 420 725T408 754Q408 771 420 783T449 795Q466 795 478 783T490 754ZM321 754Q321 737 309 725T280 713Q263 713 251 725T239 754Q239 771 251 783T280 795Q297 795 309 783T321 754Z" />
+<glyph unicode="&#xD7;" horiz-adv-x="554" d="M461 400L315 254L467 102L429 64L277 216L124 64L87 102L240 254L94 399L132 438L278 292L424 438L461 400Z" />
+<glyph unicode="&#xD8;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q306 -11 261 4T181 45L132 -23L91 11L143 83Q103 132 86 197T69 334Q69 398 84 459T133 569T225 648T366 678Q421 678 464 665T540 628L588 694L626 661L578 595Q624 545 643 477T663 334ZM597 333Q597 390 585 446T542 545L216 93Q242 69 278 55T366 41Q436 41 480 67T551 136T587 230T597 333ZM135 333Q135 281 145 229T181 135L507 583Q481 603 447 615T366 627Q297 627 252 601T181 532T145 438T135 333Z" />
+<glyph unicode="&#xD9;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM339 705L177 809L237 841L370 727L339 705Z" />
+<glyph unicode="&#xDA;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM318 705L287 729L423 844L480 811L318 705Z" />
+<glyph unicode="&#xDB;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM443 704L328 786L214 704L187 739L328 838L471 739L443 704Z" />
+<glyph unicode="&#xDC;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM453 754Q453 737 441 725T412 713Q395 713 383 725T371 754Q371 771 383 783T412 795Q429 795 441 783T453 754ZM284 754Q284 737 272 725T243 713Q226 713 214 725T202 754Q202 771 214 783T243 795Q260 795 272 783T284 754Z" />
+<glyph unicode="&#xDD;" horiz-adv-x="533" d="M299 267V0H235V264L18 667H88L267 323L446 667H515L299 267ZM259 701L228 725L364 840L421 807L259 701Z" />
+<glyph unicode="&#xDE;" horiz-adv-x="585" d="M527 343Q527 237 476 187T315 136H155V0H92V667H154V542Q191 545 221 546T287 548Q342 548 386 538T461 505T510 442T527 343ZM463 340Q463 385 452 415T419 462T363 487T285 494Q250 494 220 493T155 487V188H299Q341 189 371 196T422 220T453 266T463 340Z" />
+<glyph unicode="&#xDF;" horiz-adv-x="502" d="M457 166Q457 130 447 98T415 42T364 3T293 -11Q268 -11 244 -7T209 2L220 48Q233 43 249 40T283 36Q304 36 324 43T360 65T385 104T395 165Q395 199 386 225T357 274T304 316T227 354L219 397Q255 420 278 441T314 482T333 521T339 562Q339 579 335 596T320 628T290 650T240 659Q219 659 201 654T170 637T149 602T141 545V0H82V550Q82 625 119 665T242 705Q315 705 354 670T393 565Q393 515 369 474T281 389V387Q317 371 349 352T405 306T443 245T457 166Z" />
+<glyph unicode="&#xE0;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM296 565L134 669L194 701L327 587L296 565Z" />
+<glyph unicode="&#xE1;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM226 565L195 589L331 704L388 671L226 565Z" />
+<glyph unicode="&#xE2;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM368 563L253 645L139 563L112 598L253 697L396 598L368 563Z" />
+<glyph unicode="&#xE3;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM402 634Q393 602 368 587T316 572Q300 572 283 578T249 591T217 604T188 610Q172 610 156 603T131 571L96 594Q112 630 135 643T184 656Q200 656 217 650T251 637T283 624T313 618Q330 618 344 626T366 656L402 634H402Z" />
+<glyph unicode="&#xE4;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM385 614Q385 597 373 585T344 573Q327 573 315 585T303 614Q303 631 315 643T344 655Q361 655 373 643T385 614ZM216 614Q216 597 204 585T175 573Q158 573 146 585T134 614Q134 631 146 643T175 655Q192 655 204 643T216 614Z" />
+<glyph unicode="&#xE5;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM349 642Q349 606 324 581T262 556Q225 556 200 581T174 642Q174 678 199 703T262 728Q298 728 323 703T349 642ZM316 642Q316 665 301 681T262 697Q239 697 223 681T207 642Q207 619 223 603T262 587Q285 587 300 603T316 642Z" />
+<glyph unicode="&#xE6;" horiz-adv-x="840" d="M786 261Q786 256 786 248T785 232H432Q433 140 474 89T594 38Q630 38 669 52T741 95L765 51Q727 20 684 5T595 -11Q529 -11 484 13T410 81Q368 41 318 15T203 -11Q170 -11 142 -1T94 28T61 75T49 139Q49 193 73 228T139 284T240 313T368 322V351Q368 410 348 438T269 466Q221 466 173 449T87 405L65 451Q116 480 163 497T269 515Q295 515 319 511T362 496T395 465T415 416Q442 464 485 489T585 515Q677 515 731 452T786 261ZM729 277Q728 314 721 348T697 409T653 451T583 467Q551 467 524 455T475 418T442 359T430 277H729ZM390 120Q376 150 373 185T368 255V280Q241 280 176 247T110 142Q110 119 118 99T141 64T175 41T215 32Q259 32 303 57T390 120Z" />
+<glyph unicode="&#xE7;" horiz-adv-x="482" d="M365 -127Q365 -165 340 -185T273 -205Q251 -205 230 -199T192 -183L206 -154Q221 -163 236 -168T272 -174Q292 -174 308 -164T325 -129Q325 -104 309 -95T272 -86Q252 -86 235 -91L222 -75L257 -10Q205 -6 168 15T106 71T70 151T58 249Q58 305 71 353T112 438T182 494T283 515Q330 515 371 499T448 456L422 411Q395 437 361 451T285 466Q237 466 206 448T155 399T127 330T119 251Q119 211 127 173T154 105T205 57T285 38Q326 38 358 51T424 94L446 50Q412 24 376 8T294 -11L267 -59L268 -60Q274 -59 281 -59T294 -58Q326 -58 345 -75T365 -127Z" />
+<glyph unicode="&#xE8;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM303 565L141 669L201 701L334 587L303 565Z" />
+<glyph unicode="&#xE9;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM255 565L224 589L360 704L417 671L255 565Z" />
+<glyph unicode="&#xEA;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM389 564L274 646L160 564L133 599L274 698L417 599L389 564Z" />
+<glyph unicode="&#xEB;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM399 614Q399 597 387 585T358 573Q341 573 329 585T317 614Q317 631 329 643T358 655Q375 655 387 643T399 614ZM230 614Q230 597 218 585T189 573Q172 573 160 585T148 614Q148 631 160 643T189 655Q206 655 218 643T230 614Z" />
+<glyph unicode="&#xEC;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM144 565L-18 669L42 701L175 587L144 565Z" />
+<glyph unicode="&#xED;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM87 565L56 589L192 704L249 671L87 565Z" />
+<glyph unicode="&#xEE;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM229 564L114 646L0 564L-27 599L114 698L257 599L229 564Z" />
+<glyph unicode="&#xEF;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM240 614Q240 597 228 585T199 573Q182 573 170 585T158 614Q158 631 170 643T199 655Q216 655 228 643T240 614ZM71 614Q71 597 59 585T30 573Q13 573 1 585T-11 614Q-11 631 1 643T30 655Q47 655 59 643T71 614Z" />
+<glyph unicode="&#xF0;" horiz-adv-x="532" d="M381 619Q406 582 423 543T452 459T468 364T474 253Q474 198 463 150T427 67T362 11T262 -10Q205 -10 166 11T103 67T69 149T58 247Q58 295 66 340T96 420T157 476T258 497Q306 497 341 481T401 426H405Q395 473 375 516T332 596L228 540L210 574L308 628Q294 645 278 664T246 698L299 711Q311 700 328 682T356 651L438 700L457 665L381 619ZM413 233Q413 274 410 313T391 383T346 433T264 452Q213 452 184 433T141 385T123 319T119 247Q119 206 126 168T149 101T194 55T267 38Q314 38 343 57T387 106T408 169T413 233Z" />
+<glyph unicode="&#xF1;" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T388 419T351 451T286 463Q215 463 178 426T141 323V0H82V502H133L139 437H141Q190 515 297 515Q349 515 382 502T434 464T459 402T466 319V0H407ZM430 634Q421 602 396 587T344 572Q328 572 311 578T277 591T245 604T216 610Q200 610 184 603T159 571L124 594Q140 630 163 643T212 656Q228 656 245 650T279 637T311 624T341 618Q358 618 372 626T394 656L430 634H430Z" />
+<glyph unicode="&#xF2;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM311 565L149 669L209 701L342 587L311 565Z" />
+<glyph unicode="&#xF3;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM256 565L225 589L361 704L418 671L256 565Z" />
+<glyph unicode="&#xF4;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM395 564L280 646L166 564L139 599L280 698L423 599L395 564Z" />
+<glyph unicode="&#xF5;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM435 634Q426 602 401 587T349 572Q333 572 316 578T282 591T250 604T221 610Q205 610 189 603T164 571L129 594Q145 630 168 643T217 656Q233 656 250 650T284 637T316 624T346 618Q363 618 377 626T399 656L435 634H435Z" />
+<glyph unicode="&#xF6;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM408 614Q408 597 396 585T367 573Q350 573 338 585T326 614Q326 631 338 643T367 655Q384 655 396 643T408 614ZM239 614Q239 597 227 585T198 573Q181 573 169 585T157 614Q157 631 169 643T198 655Q215 655 227 643T239 614Z" />
+<glyph unicode="&#xF7;" horiz-adv-x="554" d="M54 231V285H500V231H54ZM239 111Q239 128 250 139T279 151Q296 151 308 140T320 111Q320 94 308 83T279 71Q262 71 251 82T239 111ZM239 406Q239 423 250 434T279 446Q296 446 308 435T320 406Q320 389 308 378T279 366Q262 366 251 377T239 406Z" />
+<glyph unicode="&#xF8;" horiz-adv-x="559" d="M447 442Q479 400 490 351T502 251Q502 197 490 149T452 66T383 10T279 -11Q234 -11 196 2T128 48L78 -16L46 12L100 80Q76 118 67 162T58 251Q58 305 70 353T110 437T179 494T279 515Q362 515 413 472L465 537L501 509L447 442ZM380 428Q363 444 339 453T280 463Q232 463 201 446T152 399T127 331T120 251Q120 219 124 186T143 126L380 428ZM440 251Q440 286 433 327T406 398L163 92Q184 63 214 51T280 39Q327 39 358 56T407 103T432 170T440 251Z" />
+<glyph unicode="&#xF9;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM278 565L116 669L176 701L309 587L278 565Z" />
+<glyph unicode="&#xFA;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM258 565L227 589L363 704L420 671L258 565Z" />
+<glyph unicode="&#xFB;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM387 564L272 646L158 564L131 599L272 698L415 599L387 564Z" />
+<glyph unicode="&#xFC;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM402 614Q402 597 390 585T361 573Q344 573 332 585T320 614Q320 631 332 643T361 655Q378 655 390 643T402 614ZM233 614Q233 597 221 585T192 573Q175 573 163 585T151 614Q151 631 163 643T192 655Q209 655 221 643T233 614Z" />
+<glyph unicode="&#xFD;" horiz-adv-x="466" d="M230 -88Q215 -128 202 -154T172 -197T136 -219T89 -226Q81 -226 69 -226T46 -224V-172Q55 -174 63 -174T83 -174Q102 -174 115 -169T139 -151T161 -113T185 -49L209 21L23 502H87L238 89H242L380 502H443L230 -88ZM233 565L202 589L338 704L395 671L233 565Z" />
+<glyph unicode="&#xFE;" horiz-adv-x="549" d="M491 250Q491 197 480 150T445 68T383 11T289 -10Q235 -10 200 13T142 71L140 72V-220H82V700H138V440H140Q169 482 207 498T292 515Q352 515 390 494T451 435T482 350T491 250ZM430 251Q430 293 425 331T405 400T360 448T283 466Q218 466 180 432T141 330V199Q141 164 150 135T178 84T222 50T282 37Q326 37 355 55T400 103T423 171T430 251Z" />
+<glyph unicode="&#xFF;" horiz-adv-x="466" d="M230 -88Q215 -128 202 -154T172 -197T136 -219T89 -226Q81 -226 69 -226T46 -224V-172Q55 -174 63 -174T83 -174Q102 -174 115 -169T139 -151T161 -113T185 -49L209 21L23 502H87L238 89H242L380 502H443L230 -88ZM367 614Q367 597 355 585T326 573Q309 573 297 585T285 614Q285 631 297 643T326 655Q343 655 355 643T367 614ZM198 614Q198 597 186 585T157 573Q140 573 128 585T116 614Q116 631 128 643T157 655Q174 655 186 643T198 614Z" />
+<glyph unicode="&#x100;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM161 730V772H446V730H161Z" />
+<glyph unicode="&#x101;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM110 590V632H395V590H110Z" />
+<glyph unicode="&#x102;" horiz-adv-x="613" d="M518 0L458 171H153L93 0H28L268 671H343L585 0H518ZM306 611H304L168 225H440L306 611ZM438 818Q435 787 423 766T392 731T351 711T304 705Q280 705 258 711T217 730T187 765T172 818H214Q218 784 240 763T304 741Q346 741 369 762T395 818H438H438Z" />
+<glyph unicode="&#x103;" horiz-adv-x="511" d="M421 -6Q399 3 389 16T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 70 434 57T454 39L421 -6ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281ZM392 678Q389 647 377 626T346 591T305 571T258 565Q234 565 212 571T171 590T141 625T126 678H168Q172 644 194 623T258 601Q300 601 323 622T349 678H392H392Z" />
+<glyph unicode="&#x104;" horiz-adv-x="613" d="M551 0Q519 -18 500 -35T470 -69T455 -101T451 -131Q451 -164 470 -180T518 -196Q538 -196 552 -191T577 -179L591 -216Q581 -222 558 -229T512 -237Q455 -237 430 -210T404 -136Q404 -94 433 -57T516 5L458 171H153L93 0H28L268 671H343L585 0H551ZM306 611H304L168 225H440L306 611Z" />
+<glyph unicode="&#x105;" horiz-adv-x="511" d="M418 -3Q388 -22 370 -38T341 -70T327 -100T323 -129Q323 -158 341 -177T390 -196Q410 -196 425 -191T450 -180L463 -216Q452 -222 430 -229T384 -237Q328 -237 302 -207T276 -134Q276 -90 307 -53T394 10Q383 19 380 30T375 57H374Q337 26 295 8T204 -11Q171 -11 143 -1T94 28T61 76T49 139Q49 202 81 238T161 293T264 317T368 322V380Q365 426 341 446T265 467Q212 467 166 448T88 407L65 452Q113 480 162 497T270 515Q348 515 388 479T428 361V88Q428 67 436 52T458 29L418 -3ZM369 281Q334 281 289 278T204 261T137 219T109 141Q109 118 117 99T139 64T172 41T215 32Q262 32 297 51T369 98V281Z" />
+<glyph unicode="&#x106;" horiz-adv-x="614" d="M580 76Q526 28 472 9T354 -11Q214 -11 142 80T69 334Q69 398 84 460T135 570T225 648T361 678Q421 678 472 658T575 596L537 552Q492 590 451 607T362 625Q312 625 270 607T198 553T152 463T135 334Q135 281 145 229T180 136T249 68T359 42Q405 42 452 60T545 120L580 76H580ZM334 710L303 734L439 849L496 816L334 710Z" />
+<glyph unicode="&#x107;" horiz-adv-x="482" d="M422 411Q395 437 361 451T285 466Q237 466 206 448T155 399T127 330T119 251Q119 211 127 173T154 105T205 57T285 38Q326 38 358 51T424 94L446 50Q411 21 372 5T282 -11Q224 -11 182 9T113 64T72 146T58 249Q58 305 71 353T112 438T182 494T283 515Q330 515 371 499T448 456L422 411ZM251 565L220 589L356 704L413 671L251 565Z" />
+<glyph unicode="&#x108;" horiz-adv-x="614" d="M580 76Q526 28 472 9T354 -11Q214 -11 142 80T69 334Q69 398 84 460T135 570T225 648T361 678Q421 678 472 658T575 596L537 552Q492 590 451 607T362 625Q312 625 270 607T198 553T152 463T135 334Q135 281 145 229T180 136T249 68T359 42Q405 42 452 60T545 120L580 76H580ZM474 705L359 787L245 705L218 740L359 839L502 740L474 705Z" />
+<glyph unicode="&#x109;" horiz-adv-x="482" d="M422 411Q395 437 361 451T285 466Q237 466 206 448T155 399T127 330T119 251Q119 211 127 173T154 105T205 57T285 38Q326 38 358 51T424 94L446 50Q411 21 372 5T282 -11Q224 -11 182 9T113 64T72 146T58 249Q58 305 71 353T112 438T182 494T283 515Q330 515 371 499T448 456L422 411ZM393 564L278 646L164 564L137 599L278 698L421 599L393 564Z" />
+<glyph unicode="&#x10A;" horiz-adv-x="614" d="M580 76Q526 28 472 9T354 -11Q214 -11 142 80T69 334Q69 398 84 460T135 570T225 648T361 678Q421 678 472 658T575 596L537 552Q492 590 451 607T362 625Q312 625 270 607T198 553T152 463T135 334Q135 281 145 229T180 136T249 68T359 42Q405 42 452 60T545 120L580 76H580ZM404 762Q404 745 394 735T364 724Q347 724 337 734T326 762Q326 780 336 790T364 801Q383 801 393 791T404 762Z" />
+<glyph unicode="&#x10B;" horiz-adv-x="482" d="M422 411Q395 437 361 451T285 466Q237 466 206 448T155 399T127 330T119 251Q119 211 127 173T154 105T205 57T285 38Q326 38 358 51T424 94L446 50Q411 21 372 5T282 -11Q224 -11 182 9T113 64T72 146T58 249Q58 305 71 353T112 438T182 494T283 515Q330 515 371 499T448 456L422 411ZM319 624Q319 607 309 597T279 586Q262 586 252 596T241 624Q241 642 251 652T279 663Q298 663 308 653T319 624Z" />
+<glyph unicode="&#x10C;" horiz-adv-x="614" d="M580 76Q526 28 472 9T354 -11Q214 -11 142 80T69 334Q69 398 84 460T135 570T225 648T361 678Q421 678 472 658T575 596L537 552Q492 590 451 607T362 625Q312 625 270 607T198 553T152 463T135 334Q135 281 145 229T180 136T249 68T359 42Q405 42 452 60T545 120L580 76H580ZM491 812L359 714L228 812L255 846L359 766L464 846L491 812Z" />
+<glyph unicode="&#x10D;" horiz-adv-x="482" d="M422 411Q395 437 361 451T285 466Q237 466 206 448T155 399T127 330T119 251Q119 211 127 173T154 105T205 57T285 38Q326 38 358 51T424 94L446 50Q411 21 372 5T282 -11Q224 -11 182 9T113 64T72 146T58 249Q58 305 71 353T112 438T182 494T283 515Q330 515 371 499T448 456L422 411ZM415 663L283 565L152 663L179 697L283 617L388 697L415 663Z" />
+<glyph unicode="&#x10E;" horiz-adv-x="650" d="M581 338Q581 276 567 216T518 107T426 30T282 0H92V664Q129 669 176 672T279 675Q355 675 412 651T506 582T562 475T581 338ZM515 338Q515 386 505 436T469 528T398 596T280 623Q248 623 216 621T155 616V53H263Q393 53 454 123T515 338ZM409 803L277 705L146 803L173 837L277 757L382 837L409 803Z" />
+<glyph unicode="&#x10F;" horiz-adv-x="550" d="M417 0L413 82H409Q360 -11 254 -11Q198 -11 161 10T100 66T68 149T58 250Q58 302 66 349T97 434T157 493T255 515Q302 515 340 499T407 438H409V700H468V0H417ZM409 331Q409 398 369 432T266 467Q217 467 188 448T144 398T124 328T119 250Q119 212 125 174T147 105T192 55T267 35Q301 35 327 48T372 83T399 135T409 197V331ZM562 544H520L552 700H621L562 544Z" />
+<glyph unicode="&#x110;" horiz-adv-x="650" d="M581 338Q581 276 567 216T518 107T425 30T279 0H92V332H32V385H92V664Q113 666 137 668T187 671T236 674T279 675Q358 675 415 650T509 579T563 473T581 338ZM516 338Q516 386 507 436T471 527T400 594T283 620Q250 620 217 619T155 614V385H311V332H155V53H263Q397 53 456 126T516 338Z" />
+<glyph unicode="&#x111;" horiz-adv-x="550" d="M417 0L413 82H409Q360 -11 254 -11Q198 -11 161 9T100 65T68 148T58 248Q58 300 66 347T97 431T157 489T255 511Q302 511 340 496T407 435H408V581H240V623H408V700H468V623H537V581H468V0H417ZM409 327Q409 394 369 429T266 464Q217 464 188 445T144 396T124 327T119 249Q119 211 125 173T147 104T192 54T267 35Q301 35 327 48T372 83T399 135T409 197V327Z" />
+<glyph unicode="&#x112;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM137 730V772H422V730H137Z" />
+<glyph unicode="&#x113;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM131 590V632H416V590H131Z" />
+<glyph unicode="&#x114;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM414 818Q411 787 399 766T368 731T327 711T280 705Q256 705 234 711T193 730T163 765T148 818H190Q194 784 216 763T280 741Q322 741 345 762T371 818H414H414Z" />
+<glyph unicode="&#x115;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM408 678Q405 647 393 626T362 591T321 571T274 565Q250 565 228 571T187 590T157 625T142 678H184Q188 644 210 623T274 601Q316 601 339 622T365 678H408H408Z" />
+<glyph unicode="&#x116;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM337 761Q337 744 327 734T297 723Q280 723 270 733T259 761Q259 779 269 789T297 800Q316 800 326 790T337 761Z" />
+<glyph unicode="&#x117;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM316 624Q316 607 306 597T276 586Q259 586 249 596T238 624Q238 642 248 652T276 663Q295 663 305 653T316 624Z" />
+<glyph unicode="&#x118;" horiz-adv-x="511" d="M423 0Q391 -19 372 -36T341 -69T326 -100T322 -129Q322 -158 341 -177T390 -196Q410 -196 425 -191T449 -180L463 -216Q452 -222 429 -229T383 -237Q327 -237 302 -207T276 -135Q276 -93 303 -59T378 0H92V667H462V612H155V385H433V330H155V54H473V0H423Z" />
+<glyph unicode="&#x119;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q439 39 425 31T397 14Q364 -4 344 -21T313 -54T298 -85T294 -114Q294 -144 313 -163T362 -183Q382 -183 396 -178T421 -166L435 -202Q424 -208 401 -215T355 -223Q299 -223 273 -193T247 -120Q247 -88 264 -60T313 -9L312 -7Q304 -9 295 -10T278 -11Q168 -11 113 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 275Q416 311 409 346T385 408T342 452T277 469Q230 469 200 451T151 405T126 343T118 275H416Z" />
+<glyph unicode="&#x11A;" horiz-adv-x="511" d="M92 0V667H462V612H155V385H433V330H155V54H473V0H92ZM417 803L285 705L154 803L181 837L285 757L390 837L417 803Z" />
+<glyph unicode="&#x11B;" horiz-adv-x="527" d="M473 270Q473 265 473 252T472 232H118Q118 142 159 89T281 36Q316 36 352 48T430 93L453 50Q410 16 367 3T279 -11Q169 -11 114 59T58 251Q58 309 72 358T113 441T182 495T277 515Q324 515 360 497T422 447T460 369T473 270ZM416 276Q416 312 409 347T384 409T341 453T277 470Q230 470 200 452T151 406T126 344T119 276H416ZM404 663L272 565L141 663L168 697L272 617L377 697L404 663Z" />
+<glyph unicode="&#x11C;" horiz-adv-x="679" d="M360 40Q409 40 445 54T504 95T539 156T551 234V287H368V340H610V266Q610 128 544 59T357 -10Q278 -10 223 18T134 95T85 205T69 333Q69 392 83 453T132 565T223 646T364 678Q429 678 480 656T574 599L538 555Q491 593 450 609T365 625Q298 625 254 600T183 534T146 440T135 333Q135 280 144 228T179 134T248 66T360 40ZM472 704L357 786L243 704L216 739L357 838L500 739L472 704Z" />
+<glyph unicode="&#x11D;" horiz-adv-x="549" d="M467 -54Q467 -111 453 -145T415 -197T359 -220T291 -226Q231 -226 184 -214T91 -178L112 -128Q154 -153 197 -165T289 -178Q322 -178 344 -173T381 -155T401 -120T408 -64V83H406Q380 34 344 14T258 -7Q202 -7 164 13T102 67T68 149T58 250Q58 301 66 349T96 434T156 493T255 515Q306 515 345 498T412 433H413L417 502H467V-54ZM408 332Q408 399 368 433T265 467Q217 467 188 448T144 398T124 328T119 250Q119 208 126 170T150 103T195 57T267 39Q301 39 327 52T372 86T399 137T408 199V332ZM386 564L271 646L157 564L130 599L271 698L414 599L386 564Z" />
+<glyph unicode="&#x11E;" horiz-adv-x="679" d="M360 40Q409 40 445 54T504 95T539 156T551 234V287H368V340H610V266Q610 128 544 59T357 -10Q278 -10 223 18T134 95T85 205T69 333Q69 392 83 453T132 565T223 646T364 678Q429 678 480 656T574 599L538 555Q491 593 450 609T365 625Q298 625 254 600T183 534T146 440T135 333Q135 280 144 228T179 134T248 66T360 40ZM495 824Q492 793 480 772T449 737T408 717T361 711Q337 711 315 717T274 736T244 771T229 824H271Q275 790 297 769T361 747Q403 747 426 768T452 824H495H495Z" />
+<glyph unicode="&#x11F;" horiz-adv-x="549" d="M467 -54Q467 -111 453 -145T415 -197T359 -220T291 -226Q231 -226 184 -214T91 -178L112 -128Q154 -153 197 -165T289 -178Q322 -178 344 -173T381 -155T401 -120T408 -64V83H406Q380 34 344 14T258 -7Q202 -7 164 13T102 67T68 149T58 250Q58 301 66 349T96 434T156 493T255 515Q306 515 345 498T412 433H413L417 502H467V-54ZM408 332Q408 399 368 433T265 467Q217 467 188 448T144 398T124 328T119 250Q119 208 126 170T150 103T195 57T267 39Q301 39 327 52T372 86T399 137T408 199V332ZM411 678Q408 647 396 626T365 591T324 571T277 565Q253 565 231 571T190 590T160 625T145 678H187Q191 644 213 623T277 601Q319 601 342 622T368 678H411H411Z" />
+<glyph unicode="&#x120;" horiz-adv-x="679" d="M360 40Q409 40 445 54T504 95T539 156T551 234V287H368V340H610V266Q610 128 544 59T357 -10Q278 -10 223 18T134 95T85 205T69 333Q69 392 83 453T132 565T223 646T364 678Q429 678 480 656T574 599L538 555Q491 593 450 609T365 625Q298 625 254 600T183 534T146 440T135 333Q135 280 144 228T179 134T248 66T360 40ZM405 762Q405 745 395 735T365 724Q348 724 338 734T327 762Q327 780 337 790T365 801Q384 801 394 791T405 762Z" />
+<glyph unicode="&#x121;" horiz-adv-x="549" d="M467 -54Q467 -111 453 -145T415 -197T359 -220T291 -226Q231 -226 184 -214T91 -178L112 -128Q154 -153 197 -165T289 -178Q322 -178 344 -173T381 -155T401 -120T408 -64V83H406Q380 34 344 14T258 -7Q202 -7 164 13T102 67T68 149T58 250Q58 301 66 349T96 434T156 493T255 515Q306 515 345 498T412 433H413L417 502H467V-54ZM408 332Q408 399 368 433T265 467Q217 467 188 448T144 398T124 328T119 250Q119 208 126 170T150 103T195 57T267 39Q301 39 327 52T372 86T399 137T408 199V332ZM312 624Q312 607 302 597T272 586Q255 586 245 596T234 624Q234 642 244 652T272 663Q291 663 301 653T312 624Z" />
+<glyph unicode="&#x122;" horiz-adv-x="679" d="M360 40Q409 40 445 54T504 95T539 156T551 234V287H368V340H610V266Q610 128 544 59T357 -10Q278 -10 223 18T134 95T85 205T69 333Q69 392 83 453T132 565T223 646T364 678Q429 678 480 656T574 599L538 555Q491 593 450 609T365 625Q298 625 254 600T183 534T146 440T135 333Q135 280 144 228T179 134T248 66T360 40ZM347 -206H306L330 -61H399L347 -206Z" />
+<glyph unicode="&#x123;" horiz-adv-x="549" d="M467 -54Q467 -111 453 -145T415 -197T359 -220T291 -226Q231 -226 184 -214T91 -178L112 -128Q154 -153 197 -165T289 -178Q322 -178 344 -173T381 -155T401 -120T408 -64V83H406Q380 34 344 14T258 -7Q202 -7 164 13T102 67T68 149T58 250Q58 301 66 349T96 434T156 493T255 515Q306 515 345 498T412 433H413L417 502H467V-54ZM408 332Q408 399 368 433T265 467Q217 467 188 448T144 398T124 328T119 250Q119 208 126 170T150 103T195 57T267 39Q301 39 327 52T372 86T399 137T408 199V332ZM299 707H339L314 574H235L299 707Z" />
+<glyph unicode="&#x124;" horiz-adv-x="645" d="M490 0V331H155V0H92V667H155V388H490V667H553V0H490ZM445 704L330 786L216 704L189 739L330 838L473 739L445 704Z" />
+<glyph unicode="&#x125;" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T390 419T354 451T288 463Q217 463 179 427T141 323V0H82V700H141V434H142Q161 474 202 494T300 515Q351 515 383 502T434 463T459 401T466 318V0H407ZM227 697L112 779L-2 697L-29 732L112 831L255 732L227 697Z" />
+<glyph unicode="&#x126;" horiz-adv-x="645" d="M489 0V331H155V0H92V501H4V550H92V667H155V550H489V667H553V550H641V501H553V0H489ZM489 383V501H155V383H489Z" />
+<glyph unicode="&#x127;" horiz-adv-x="548" d="M141 580V430H142Q161 471 202 491T300 511Q351 511 383 498T434 460T459 398T466 315V0H406V297Q406 334 404 364T389 415T354 448T288 460Q217 460 179 423T141 319V0H82V580H13V622H82V700H141V622H320V580H141Z" />
+<glyph unicode="&#x128;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM277 774Q268 742 243 727T191 712Q175 712 158 718T124 731T92 744T63 750Q47 750 31 743T6 711L-29 734Q-13 770 10 783T59 796Q75 796 92 790T126 777T158 764T188 758Q205 758 219 766T241 796L277 774H277Z" />
+<glyph unicode="&#x129;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM268 634Q259 602 234 587T182 572Q166 572 149 578T115 591T83 604T54 610Q38 610 22 603T-3 571L-38 594Q-22 630 1 643T50 656Q66 656 83 650T117 637T149 624T179 618Q196 618 210 626T232 656L268 634H268Z" />
+<glyph unicode="&#x12A;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM-19 730V772H266V730H-19Z" />
+<glyph unicode="&#x12B;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM-31 590V632H254V590H-31Z" />
+<glyph unicode="&#x12C;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM257 818Q254 787 242 766T211 731T170 711T123 705Q99 705 77 711T36 730T6 765T-9 818H33Q37 784 59 763T123 741Q165 741 188 762T214 818H257H257Z" />
+<glyph unicode="&#x12D;" horiz-adv-x="223" d="M82 0V502H141V0H82ZM245 678Q242 647 230 626T199 591T158 571T111 565Q87 565 65 571T24 590T-6 625T-21 678H21Q25 644 47 623T111 601Q153 601 176 622T202 678H245H245Z" />
+<glyph unicode="&#x12E;" horiz-adv-x="247" d="M138 0Q107 -19 88 -36T57 -69T41 -100T37 -129Q37 -158 56 -177T105 -196Q125 -196 139 -191T164 -180L178 -216Q167 -222 144 -229T97 -237Q41 -237 16 -207T-10 -134Q-10 -91 21 -58T104 0H92V667H155V0H138Z" />
+<glyph unicode="&#x12F;" horiz-adv-x="223" d="M129 0Q97 -19 78 -36T47 -69T32 -100T28 -129Q28 -158 46 -177T95 -196Q115 -196 130 -191T155 -180L168 -216Q157 -222 135 -229T89 -237Q33 -237 7 -207T-19 -134Q-19 -92 12 -58T93 0H82V502H141V0H129ZM150 623Q150 606 139 595T110 584Q93 584 82 595T71 623Q71 641 82 651T110 662Q129 662 139 652T150 623Z" />
+<glyph unicode="&#x130;" horiz-adv-x="247" d="M92 0V667H155V0H92ZM164 762Q164 745 154 735T124 724Q107 724 97 734T86 762Q86 780 96 790T124 801Q143 801 153 791T164 762Z" />
+<glyph unicode="&#x131;" horiz-adv-x="223" d="M82 0V502H141V0H82Z" />
+<glyph unicode="&#x132;" horiz-adv-x="603" d="M92 0V667H155V0H92ZM511 151Q511 66 469 28T355 -11Q319 -11 286 -2T222 30L248 81Q276 59 298 51T348 42Q403 42 425 70T448 156V667H511V151Z" />
+<glyph unicode="&#x133;" horiz-adv-x="432" d="M82 0V502H141V0H82ZM151 623Q151 606 140 595T111 584Q94 584 83 595T72 623Q72 641 83 651T111 662Q130 662 140 652T151 623ZM359 623Q359 604 348 594T319 584Q303 584 292 594T280 623Q280 642 291 652T319 662Q336 662 347 652T359 623ZM349 -51Q349 -91 342 -123T317 -178T270 -213T199 -226Q184 -226 169 -225T140 -222V-171Q167 -174 188 -174Q221 -174 241 -166T272 -141T286 -101T290 -47V502H349V-51Z" />
+<glyph unicode="&#x134;" horiz-adv-x="425" d="M333 151Q333 66 291 28T177 -11Q141 -11 108 -2T44 30L70 81Q98 59 120 51T170 42Q225 42 247 70T270 156V667H333V151H333ZM417 704L302 786L188 704L161 739L302 838L445 739L417 704Z" />
+<glyph unicode="&#x135;" horiz-adv-x="229" d="M147 -51Q147 -91 140 -123T114 -178T67 -213T-4 -226Q-19 -226 -34 -225T-63 -222V-171Q-36 -174 -15 -174Q18 -174 38 -166T69 -141T83 -101T87 -47V502H147V-51ZM232 564L117 646L3 564L-24 599L117 698L260 599L232 564Z" />
+<glyph unicode="&#x136;" horiz-adv-x="545" d="M446 0L232 343L155 252V0H92V667H155V333H157L428 667H505L279 388L522 0H446ZM267 -206H226L250 -61H319L267 -206Z" />
+<glyph unicode="&#x137;" horiz-adv-x="477" d="M379 0L228 264L141 162V0H82V700H141V237H144L358 502H434L271 305L451 0H379ZM239 -206H198L222 -61H291L239 -206Z" />
+<glyph unicode="&#x139;" horiz-adv-x="473" d="M92 0V667H155V55H436V0H92ZM124 705L93 729L229 844L286 811L124 705Z" />
+<glyph unicode="&#x13A;" horiz-adv-x="251" d="M222 0Q214 -2 200 -3T172 -5Q125 -5 104 15T82 91V700H141V107Q141 86 144 74T154 57T172 50T200 49H222V0H222ZM117 732L86 756L222 871L279 838L117 732Z" />
+<glyph unicode="&#x13B;" horiz-adv-x="473" d="M92 0V667H155V55H436V0H92ZM244 -206H203L227 -61H296L244 -206Z" />
+<glyph unicode="&#x13C;" horiz-adv-x="251" d="M222 0Q214 -2 200 -3T172 -5Q125 -5 104 15T82 91V700H141V107Q141 86 144 74T154 57T172 50T200 49H222V0H222ZM138 -206H97L121 -61H190L138 -206Z" />
+<glyph unicode="&#x13D;" horiz-adv-x="473" d="M92 0V667H155V55H436V0H92ZM377 544H335L367 700H436L377 544Z" />
+<glyph unicode="&#x13E;" horiz-adv-x="251" d="M222 0Q214 -2 200 -3T172 -5Q125 -5 104 15T82 91V700H141V107Q141 86 144 74T154 57T172 50T200 49H222V0ZM237 544H195L227 700H296L237 544Z" />
+<glyph unicode="&#x13F;" horiz-adv-x="473" d="M92 0V667H155V55H436V0H92ZM434 257Q434 234 422 222T386 209Q364 209 352 221T339 257Q339 304 387 304Q434 304 434 257Z" />
+<glyph unicode="&#x140;" horiz-adv-x="302" d="M222 0Q214 -2 200 -3T172 -5Q125 -5 104 15T82 91V700H141V107Q141 86 144 74T154 57T172 50T200 49H222V0ZM318 235Q318 212 306 200T270 187Q248 187 236 199T223 235Q223 282 271 282Q318 282 318 235Z" />
+<glyph unicode="&#x141;" horiz-adv-x="473" d="M92 0V321L50 296V354L92 379V667H155V414L271 481V422L155 355V55H436V0H92Z" />
+<glyph unicode="&#x142;" horiz-adv-x="251" d="M141 364V107Q141 86 144 74T154 57T172 50T200 49H222V0Q213 -2 200 -3T173 -5Q125 -5 104 15T82 91V332L39 306V364L82 389V700H141V420L244 479V423L141 364Z" />
+<glyph unicode="&#x143;" horiz-adv-x="684" d="M530 0L158 560H154V0H92V667H159L526 110H530V667H592V0H530ZM342 705L311 729L447 844L504 811L342 705Z" />
+<glyph unicode="&#x144;" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T388 419T351 451T286 463Q215 463 178 426T141 323V0H82V502H133L139 437H141Q190 515 297 515Q349 515 382 502T434 464T459 402T466 319V0H407ZM261 565L230 589L366 704L423 671L261 565Z" />
+<glyph unicode="&#x145;" horiz-adv-x="684" d="M530 0L158 560H154V0H92V667H159L526 110H530V667H592V0H530ZM319 -206H278L302 -61H371L319 -206Z" />
+<glyph unicode="&#x146;" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T388 419T351 451T286 463Q215 463 178 426T141 323V0H82V502H133L139 437H141Q190 515 297 515Q349 515 382 502T434 464T459 402T466 319V0H407ZM267 -206H226L250 -61H319L267 -206Z" />
+<glyph unicode="&#x147;" horiz-adv-x="684" d="M530 0L158 560H154V0H92V667H159L526 110H530V667H592V0H530ZM491 803L359 705L228 803L255 837L359 757L464 837L491 803Z" />
+<glyph unicode="&#x148;" horiz-adv-x="548" d="M407 0V301Q407 338 404 368T388 419T351 451T286 463Q215 463 178 426T141 323V0H82V502H133L139 437H141Q190 515 297 515Q349 515 382 502T434 464T459 402T466 319V0H407ZM414 663L282 565L151 663L178 697L282 617L387 697L414 663Z" />
+<glyph unicode="&#x14A;" horiz-adv-x="684" d="M437 -179H450Q474 -179 489 -170T514 -147T527 -115T531 -79V2L155 572H154V0H92V667H159L529 103H531V667H592V-76Q592 -146 561 -191T456 -236H437V-179Z" />
+<glyph unicode="&#x14B;" horiz-adv-x="548" d="M133 502L139 437H141Q190 515 297 515Q349 515 382 502T434 464T459 402T466 319V-27Q466 -68 459 -100T433 -155T386 -190T314 -202H295V-148H308Q338 -148 357 -139T387 -113T403 -75T407 -30V301Q407 338 404 368T388 419T351 451T286 463Q215 463 178 426T141 323V0H82V502H133Z" />
+<glyph unicode="&#x14C;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM229 730V772H514V730H229Z" />
+<glyph unicode="&#x14D;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM137 590V632H422V590H137Z" />
+<glyph unicode="&#x14E;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM503 824Q500 793 488 772T457 737T416 717T369 711Q345 711 323 717T282 736T252 771T237 824H279Q283 790 305 769T369 747Q411 747 434 768T460 824H503H503Z" />
+<glyph unicode="&#x14F;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM413 678Q410 647 398 626T367 591T326 571T279 565Q255 565 233 571T192 590T162 625T147 678H189Q193 644 215 623T279 601Q321 601 344 622T370 678H413H413Z" />
+<glyph unicode="&#x150;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q283 -11 227 17T135 94T85 204T69 334Q69 398 84 459T133 569T225 648T366 678Q449 678 506 649T598 572T648 462T663 334ZM597 333Q597 385 587 437T551 532T481 600T366 627Q297 627 252 601T181 532T145 438T135 333Q135 282 145 230T181 136T252 68T366 41Q436 41 480 67T551 136T587 230T597 333ZM485 693L453 712L591 827L650 798L485 693ZM275 693L243 712L381 827L440 798L275 693Z" />
+<glyph unicode="&#x151;" horiz-adv-x="559" d="M58 250Q58 304 70 352T108 437T177 494T279 515Q337 515 379 495T448 439T488 355T501 250Q501 196 489 149T449 66T380 10T279 -11Q220 -11 179 9T110 65T71 148T58 250ZM120 251Q120 210 127 171T152 102T201 54T280 36Q327 36 357 54T406 102T431 171T439 251Q439 293 432 332T407 401T358 449T279 467Q232 467 201 449T152 401T127 332T120 251ZM410 566L378 585L516 700L575 671L410 566ZM200 566L168 585L306 700L365 671L200 566Z" />
+<glyph unicode="&#x152;" horiz-adv-x="853" d="M461 0Q440 -5 412 -8T357 -11Q274 -11 219 17T130 94T83 204T69 334Q69 398 82 459T129 569T217 648T357 678Q383 678 411 675T461 667H803V612H524V385H774V330H524V54H815V0H461ZM358 41Q387 41 414 44T461 56V610Q441 618 414 622T358 627Q289 627 246 601T178 532T144 438T135 333Q135 282 144 230T177 136T245 68T358 41Z" />
+<glyph unicode="&#x153;" horiz-adv-x="911" d="M857 261Q857 256 857 248T856 232H502Q503 138 544 88T667 38Q740 38 812 95L835 51Q798 22 755 6T666 -11Q626 -11 595 -2T541 25T500 64T471 114Q444 47 396 18T279 -11Q175 -11 117 54T58 251Q58 377 115 446T279 515Q346 515 395 484T470 387Q495 447 542 481T656 515Q750 515 803 449T857 261ZM800 277Q799 309 793 342T770 404T727 449T657 467Q587 467 545 417T502 277H800ZM439 251Q439 296 431 334T405 402T356 447T279 464Q234 464 203 448T154 402T127 335T119 251Q119 193 131 153T164 87T215 50T280 38Q316 38 345 49T395 86T427 152T439 251Z" />
+<glyph unicode="&#x154;" horiz-adv-x="581" d="M459 0L375 246Q367 271 351 281T307 292H155V0H92V664Q117 667 139 669T183 672T227 674T276 675Q321 675 363 667T438 637T490 580T510 487Q510 424 486 379T399 312V311Q410 306 419 292T433 265L526 0H459ZM446 481Q446 523 433 550T398 594T344 616T277 623Q243 623 215 622T155 615V344H297Q369 344 407 374T446 481ZM261 706L230 730L366 845L423 812L261 706Z" />
+<glyph unicode="&#x155;" horiz-adv-x="343" d="M302 461Q227 461 184 432T141 333V0H82V502H132L138 440H139Q162 480 203 497T297 514H319V461H302ZM187 565L156 589L292 704L349 671L187 565Z" />
+<glyph unicode="&#x156;" horiz-adv-x="581" d="M459 0L375 246Q367 271 351 281T307 292H155V0H92V664Q117 667 139 669T183 672T227 674T276 675Q321 675 363 667T438 637T490 580T510 487Q510 424 486 379T399 312V311Q410 306 419 292T433 265L526 0H459ZM446 481Q446 523 433 550T398 594T344 616T277 623Q243 623 215 622T155 615V344H297Q369 344 407 374T446 481ZM277 -206H236L260 -61H329L277 -206Z" />
+<glyph unicode="&#x157;" horiz-adv-x="343" d="M302 461Q227 461 184 432T141 333V0H82V502H132L138 440H139Q162 480 203 497T297 514H319V461H302ZM94 -206H53L77 -61H146L94 -206Z" />
+<glyph unicode="&#x158;" horiz-adv-x="581" d="M459 0L375 246Q367 271 351 281T307 292H155V0H92V664Q117 667 139 669T183 672T227 674T276 675Q321 675 363 667T438 637T490 580T510 487Q510 424 486 379T399 312V311Q410 306 419 292T433 265L526 0H459ZM446 481Q446 523 433 550T398 594T344 616T277 623Q243 623 215 622T155 615V344H297Q369 344 407 374T446 481ZM410 803L278 705L147 803L174 837L278 757L383 837L410 803Z" />
+<glyph unicode="&#x159;" horiz-adv-x="343" d="M302 461Q227 461 184 432T141 333V0H82V502H132L138 440H139Q162 480 203 497T297 514H319V461H302ZM322 663L190 565L59 663L86 697L190 617L295 697L322 663Z" />
+<glyph unicode="&#x15A;" horiz-adv-x="506" d="M454 180Q454 89 402 39T252 -11Q191 -11 140 5T37 57L67 107Q103 79 147 60T246 40Q315 40 352 75T390 180Q390 215 375 238T336 279T280 309T216 336Q186 349 157 364T106 400T70 450T56 519Q56 561 72 591T114 640T175 669T248 678Q300 678 346 663T436 618L411 565Q376 593 336 609T247 626Q222 626 199 620T159 600T132 567T121 520Q121 491 132 471T162 436T206 409T259 386Q293 372 328 357T391 319T436 263T454 180ZM240 706L209 730L345 845L402 812L240 706Z" />
+<glyph unicode="&#x15B;" horiz-adv-x="428" d="M383 136Q383 97 370 69T333 24T280 -2T216 -11Q164 -11 122 1T39 38L62 85Q94 63 136 50T216 36Q238 36 258 41T293 58T317 88T326 132Q326 156 316 172T289 200T250 220T206 236Q176 246 148 257T99 286T65 327T52 386Q52 422 67 446T105 486T158 508T218 515Q259 515 296 505T368 477L350 429Q325 444 290 455T219 467Q197 467 177 463T142 448T118 422T109 383Q109 362 119 347T147 321T187 301T235 285Q262 276 288 265T336 237T370 196T383 136ZM187 565L156 589L292 704L349 671L187 565Z" />
+<glyph unicode="&#x15C;" horiz-adv-x="506" d="M454 180Q454 89 402 39T252 -11Q191 -11 140 5T37 57L67 107Q103 79 147 60T246 40Q315 40 352 75T390 180Q390 215 375 238T336 279T280 309T216 336Q186 349 157 364T106 400T70 450T56 519Q56 561 72 591T114 640T175 669T248 678Q300 678 346 663T436 618L411 565Q376 593 336 609T247 626Q222 626 199 620T159 600T132 567T121 520Q121 491 132 471T162 436T206 409T259 386Q293 372 328 357T391 319T436 263T454 180ZM368 704L253 786L139 704L112 739L253 838L396 739L368 704Z" />
+<glyph unicode="&#x15D;" horiz-adv-x="428" d="M383 136Q383 97 370 69T333 24T280 -2T216 -11Q164 -11 122 1T39 38L62 85Q94 63 136 50T216 36Q238 36 258 41T293 58T317 88T326 132Q326 156 316 172T289 200T250 220T206 236Q176 246 148 257T99 286T65 327T52 386Q52 422 67 446T105 486T158 508T218 515Q259 515 296 505T368 477L350 429Q325 444 290 455T219 467Q197 467 177 463T142 448T118 422T109 383Q109 362 119 347T147 321T187 301T235 285Q262 276 288 265T336 237T370 196T383 136ZM344 564L229 646L115 564L88 599L229 698L372 599L344 564Z" />
+<glyph unicode="&#x15E;" horiz-adv-x="507" d="M332 -129Q332 -167 307 -187T241 -207Q218 -207 197 -201T160 -184L174 -155Q189 -164 204 -169T240 -174Q260 -174 275 -165T291 -130Q291 -105 276 -96T239 -87Q219 -87 202 -92L189 -77L225 -10Q172 -6 127 10T37 57L67 107Q103 79 147 60T246 40Q315 40 353 75T391 180Q391 214 376 237T336 278T280 308T216 336Q186 349 157 364T106 400T70 450T56 519Q56 561 72 591T114 640T175 669T248 678Q300 678 346 663T436 618L411 565Q376 593 336 609T247 626Q222 626 199 620T159 600T132 567T121 520Q121 491 132 471T163 436T207 409T259 386Q293 372 328 357T391 319T437 263T455 180Q455 92 406 42T262 -11L235 -59L236 -60Q248 -57 262 -57Q294 -57 313 -75T332 -129Z" />
+<glyph unicode="&#x15F;" horiz-adv-x="428" d="M305 -128Q305 -166 280 -186T213 -206Q191 -206 170 -200T132 -183L146 -155Q161 -163 176 -168T211 -174Q232 -174 248 -164T265 -129Q265 -104 249 -95T212 -86Q192 -86 174 -91L161 -76L197 -11Q150 -8 112 4T39 38L62 85Q94 63 136 50T216 36Q238 36 258 41T293 58T317 88T326 132Q326 156 316 173T289 201T250 221T206 237Q176 247 148 258T99 286T65 327T52 386Q52 422 67 446T105 486T158 508T218 515Q259 515 296 505T368 477L350 430Q325 446 291 457T219 468Q197 468 177 464T142 450T118 424T109 385Q109 364 119 349T147 322T187 303T235 286Q262 277 288 266T336 238T370 196T383 136Q383 100 372 73T340 29T292 2T235 -10L206 -61L207 -62Q219 -59 232 -59Q264 -59 284 -75T305 -128Z" />
+<glyph unicode="&#x160;" horiz-adv-x="506" d="M454 180Q454 89 402 39T252 -11Q191 -11 140 5T37 57L67 107Q103 79 147 60T246 40Q315 40 352 75T390 180Q390 215 375 238T336 279T280 309T216 336Q186 349 157 364T106 400T70 450T56 519Q56 561 72 591T114 640T175 669T248 678Q300 678 346 663T436 618L411 565Q376 593 336 609T247 626Q222 626 199 620T159 600T132 567T121 520Q121 491 132 471T162 436T206 409T259 386Q293 372 328 357T391 319T436 263T454 180ZM383 803L251 705L120 803L147 837L251 757L356 837L383 803Z" />
+<glyph unicode="&#x161;" horiz-adv-x="428" d="M383 136Q383 97 370 69T333 24T280 -2T216 -11Q164 -11 122 1T39 38L62 85Q94 63 136 50T216 36Q238 36 258 41T293 58T317 88T326 132Q326 156 316 172T289 200T250 220T206 236Q176 246 148 257T99 286T65 327T52 386Q52 422 67 446T105 486T158 508T218 515Q259 515 296 505T368 477L350 429Q325 444 290 455T219 467Q197 467 177 463T142 448T118 422T109 383Q109 362 119 347T147 321T187 301T235 285Q262 276 288 265T336 237T370 196T383 136ZM354 663L222 565L91 663L118 697L222 617L327 697L354 663Z" />
+<glyph unicode="&#x162;" horiz-adv-x="482" d="M272 612V0H209V612H21V667H461V612H272ZM220 -206H179L203 -61H272L220 -206Z" />
+<glyph unicode="&#x163;" horiz-adv-x="332" d="M290 -1Q280 -3 265 -4T236 -5Q194 -5 169 3T131 28T113 66T109 116V452H25V502H109V642H168V502H290V452H168V126Q168 106 170 91T180 66T206 52T253 47H290V-1H290ZM191 -206H150L174 -61H243L191 -206Z" />
+<glyph unicode="&#x164;" horiz-adv-x="482" d="M272 612V0H209V612H21V667H461V612H272ZM375 803L243 705L112 803L139 837L243 757L348 837L375 803Z" />
+<glyph unicode="&#x165;" horiz-adv-x="332" d="M290 -1Q280 -3 265 -4T236 -5Q194 -5 169 3T131 28T113 66T109 116V452H25V502H109V642H168V502H290V452H168V126Q168 106 170 91T180 66T206 52T253 47H290V-1ZM304 544H262L294 700H363L304 544Z" />
+<glyph unicode="&#x166;" horiz-adv-x="482" d="M272 342V0H209V342H51V392H209V612H21V667H461V612H272V392H430V342H272Z" />
+<glyph unicode="&#x167;" horiz-adv-x="332" d="M168 297V126Q168 106 170 91T180 66T206 52T253 47H290V-1Q279 -3 265 -4T236 -5Q194 -5 169 3T131 28T113 66T109 116V297H25V345H109V452H25V502H109V642H168V502H290V452H168V345H289V297H168Z" />
+<glyph unicode="&#x168;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM478 774Q469 742 444 727T392 712Q376 712 359 718T325 731T293 744T264 750Q248 750 232 743T207 711L172 734Q188 770 211 783T260 796Q276 796 293 790T327 777T359 764T389 758Q406 758 420 766T442 796L478 774H478Z" />
+<glyph unicode="&#x169;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM418 634Q409 602 384 587T332 572Q316 572 299 578T265 591T233 604T204 610Q188 610 172 603T147 571L112 594Q128 630 151 643T200 656Q216 656 233 650T267 637T299 624T329 618Q346 618 360 626T382 656L418 634H418Z" />
+<glyph unicode="&#x16A;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM186 730V772H471V730H186Z" />
+<glyph unicode="&#x16B;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM126 590V632H411V590H126Z" />
+<glyph unicode="&#x16C;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM457 818Q454 787 442 766T411 731T370 711T323 705Q299 705 277 711T236 730T206 765T191 818H233Q237 784 259 763T323 741Q365 741 388 762T414 818H457H457Z" />
+<glyph unicode="&#x16D;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM406 678Q403 647 391 626T360 591T319 571T272 565Q248 565 226 571T185 590T155 625T140 678H182Q186 644 208 623T272 601Q314 601 337 622T363 678H406H406Z" />
+<glyph unicode="&#x16E;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM414 773Q414 737 389 712T327 687Q290 687 265 712T239 773Q239 809 264 834T327 859Q363 859 388 834T414 773ZM381 773Q381 796 366 812T327 828Q304 828 288 812T272 773Q272 750 288 734T327 718Q350 718 365 734T381 773Z" />
+<glyph unicode="&#x16F;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM358 651Q358 615 333 590T271 565Q234 565 209 590T183 651Q183 687 208 712T271 737Q307 737 332 712T358 651ZM325 651Q325 674 310 690T271 706Q248 706 232 690T216 651Q216 628 232 612T271 596Q294 596 309 612T325 651Z" />
+<glyph unicode="&#x170;" horiz-adv-x="652" d="M560 255Q560 114 503 52T325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T488 160T497 242V667H560V255H560ZM433 709L401 728L539 843L598 814L433 709ZM223 709L191 728L329 843L388 814L223 709Z" />
+<glyph unicode="&#x171;" horiz-adv-x="540" d="M409 0L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H141V238Q141 189 144 152T160 90T195 53T259 40Q335 40 367 81T399 188V502H458V0H409ZM387 569L355 588L493 703L552 674L387 569ZM177 569L145 588L283 703L342 674L177 569Z" />
+<glyph unicode="&#x172;" horiz-adv-x="652" d="M560 255Q560 151 530 91T435 6Q403 -13 384 -30T353 -63T338 -94T334 -123Q334 -152 353 -171T402 -191Q422 -191 436 -186T461 -174L474 -210Q464 -216 441 -223T395 -231Q339 -231 313 -201T287 -128Q287 -93 307 -63T364 -8Q346 -11 325 -11Q204 -11 148 51T92 255V667H155V242Q155 197 163 161T192 98T245 57T326 42Q374 42 406 56T459 97T487 160T496 242V667H560V255H560Z" />
+<glyph unicode="&#x173;" horiz-adv-x="540" d="M441 0Q409 -19 390 -36T360 -69T345 -100T341 -129Q341 -158 360 -177T408 -196Q428 -196 443 -191T468 -180L481 -216Q471 -222 448 -229T402 -237Q346 -237 320 -207T294 -134Q294 -90 324 -54T409 5L401 66H399Q382 35 346 12T245 -11Q206 -11 176 -2T125 30T93 92T82 194V502H142V238Q142 189 145 152T161 90T196 53T259 40Q298 40 324 50T367 80T391 127T398 188V502H458V0H441Z" />
+<glyph unicode="&#x174;" horiz-adv-x="869" d="M673 0H594L435 571H431L276 0H192L34 667H103L236 68H240L403 667H466L632 68H636L768 667H835L673 0ZM550 704L435 786L321 704L294 739L435 838L578 739L550 704Z" />
+<glyph unicode="&#x175;" horiz-adv-x="720" d="M550 0H477L362 411H358L240 0H169L29 502H90L206 53H210L334 502H385L510 54H514L631 502H691L550 0ZM478 564L363 646L249 564L222 599L363 698L506 599L478 564Z" />
+<glyph unicode="&#x176;" horiz-adv-x="533" d="M299 267V0H235V264L18 667H88L267 323L446 667H515L299 267ZM383 704L268 786L154 704L127 739L268 838L411 739L383 704Z" />
+<glyph unicode="&#x177;" horiz-adv-x="466" d="M230 -88Q215 -128 202 -154T172 -197T136 -219T89 -226Q81 -226 69 -226T46 -224V-172Q55 -174 63 -174T83 -174Q102 -174 115 -169T139 -151T161 -113T185 -49L209 21L23 502H87L238 89H242L380 502H443L230 -88ZM354 564L239 646L125 564L98 599L239 698L382 599L354 564Z" />
+<glyph unicode="&#x178;" horiz-adv-x="533" d="M299 267V0H235V264L18 667H88L267 323L446 667H515L299 267ZM395 754Q395 737 383 725T354 713Q337 713 325 725T313 754Q313 771 325 783T354 795Q371 795 383 783T395 754ZM226 754Q226 737 214 725T185 713Q168 713 156 725T144 754Q144 771 156 783T185 795Q202 795 214 783T226 754Z" />
+<glyph unicode="&#x179;" horiz-adv-x="532" d="M47 0V55L407 612H62V667H477V613L115 54H485V0H47ZM245 705L214 729L350 844L407 811L245 705Z" />
+<glyph unicode="&#x17A;" horiz-adv-x="441" d="M42 0V49L329 452H50V502H394V452L108 50H399V0H42ZM199 565L168 589L304 704L361 671L199 565Z" />
+<glyph unicode="&#x17B;" horiz-adv-x="532" d="M47 0V55L407 612H62V667H477V613L115 54H485V0H47ZM310 762Q310 745 300 735T270 724Q253 724 243 734T232 762Q232 780 242 790T270 801Q289 801 299 791T310 762Z" />
+<glyph unicode="&#x17C;" horiz-adv-x="441" d="M42 0V49L329 452H50V502H394V452L108 50H399V0H42ZM267 624Q267 607 257 597T227 586Q210 586 200 596T189 624Q189 642 199 652T227 663Q246 663 256 653T267 624Z" />
+<glyph unicode="&#x17D;" horiz-adv-x="532" d="M47 0V55L407 612H62V667H477V613L115 54H485V0H47ZM401 803L269 705L138 803L165 837L269 757L374 837L401 803Z" />
+<glyph unicode="&#x17E;" horiz-adv-x="441" d="M42 0V49L329 452H50V502H394V452L108 50H399V0H42ZM362 663L230 565L99 663L126 697L230 617L335 697L362 663Z" />
+<glyph unicode="&#x192;" horiz-adv-x="554" d="M493 624Q481 625 475 625T457 625Q442 625 428 623T402 613T381 591T368 555L340 420H446L438 367H331L259 -11Q251 -52 238 -85T206 -142T158 -179T90 -192Q82 -192 72 -192T51 -189L61 -139Q70 -140 79 -140T99 -140Q118 -140 134 -129T163 -100T184 -56T198 -4L268 367H193L203 420H277L301 546Q308 582 318 606T347 646T394 668T467 675Q476 675 483 675T503 674L493 624Z" />
+<glyph unicode="&#x1FC;" horiz-adv-x="868" d="M449 0V171H190L94 0H25L411 667H820V613H512V385H790V330H512V54H830V0H449ZM450 610H442L217 226H450V610ZM576 705L545 729L681 844L738 811L576 705Z" />
+<glyph unicode="&#x1FD;" horiz-adv-x="840" d="M786 261Q786 256 786 248T785 232H432Q433 140 474 89T594 38Q630 38 669 52T741 95L765 51Q727 20 684 5T595 -11Q529 -11 484 13T410 81Q368 41 318 15T203 -11Q170 -11 142 -1T94 28T61 75T49 139Q49 193 73 228T139 284T240 313T368 322V351Q368 410 348 438T269 466Q221 466 173 449T87 405L65 451Q116 480 163 497T269 515Q295 515 319 511T362 496T395 465T415 416Q442 464 485 489T585 515Q677 515 731 452T786 261ZM729 277Q728 314 721 348T697 409T653 451T583 467Q551 467 524 455T475 418T442 359T430 277H729ZM390 120Q376 150 373 185T368 255V280Q241 280 176 247T110 142Q110 119 118 99T141 64T175 41T215 32Q259 32 303 57T390 120ZM435 565L404 589L540 704L597 671L435 565Z" />
+<glyph unicode="&#x1FE;" horiz-adv-x="732" d="M663 334Q663 267 648 205T597 94T506 18T366 -11Q306 -11 261 4T181 45L132 -23L91 11L143 83Q103 132 86 197T69 334Q69 398 84 459T133 569T225 648T366 678Q421 678 464 665T540 628L588 694L626 661L578 595Q624 545 643 477T663 334ZM597 333Q597 390 585 446T542 545L216 93Q242 69 278 55T366 41Q436 41 480 67T551 136T587 230T597 333ZM135 333Q135 281 145 229T181 135L507 583Q481 603 447 615T366 627Q297 627 252 601T181 532T145 438T135 333ZM342 706L311 730L447 845L504 812L342 706Z" />
+<glyph unicode="&#x1FF;" horiz-adv-x="559" d="M447 442Q479 400 490 351T502 251Q502 197 490 149T452 66T383 10T279 -11Q234 -11 196 2T128 48L78 -16L46 12L100 80Q76 118 67 162T58 251Q58 305 70 353T110 437T179 494T279 515Q362 515 413 472L465 537L501 509L447 442ZM380 428Q363 444 339 453T280 463Q232 463 201 446T152 399T127 331T120 251Q120 219 124 186T143 126L380 428ZM440 251Q440 286 433 327T406 398L163 92Q184 63 214 51T280 39Q327 39 358 56T407 103T432 170T440 251ZM270 565L239 589L375 704L432 671L270 565Z" />
+<glyph unicode="&#x218;" horiz-adv-x="506" d="M454 180Q454 89 402 39T252 -11Q191 -11 140 5T37 57L67 107Q103 79 147 60T246 40Q315 40 352 75T390 180Q390 215 375 238T336 279T280 309T216 336Q186 349 157 364T106 400T70 450T56 519Q56 561 72 591T114 640T175 669T248 678Q300 678 346 663T436 618L411 565Q376 593 336 609T247 626Q222 626 199 620T159 600T132 567T121 520Q121 491 132 471T162 436T206 409T259 386Q293 372 328 357T391 319T436 263T454 180ZM235 -206H194L218 -61H287L235 -206Z" />
+<glyph unicode="&#x219;" horiz-adv-x="428" d="M383 136Q383 97 370 69T333 24T280 -2T216 -11Q164 -11 122 1T39 38L62 85Q94 63 136 50T216 36Q238 36 258 41T293 58T317 88T326 132Q326 156 316 172T289 200T250 220T206 236Q176 246 148 257T99 286T65 327T52 386Q52 422 67 446T105 486T158 508T218 515Q259 515 296 505T368 477L350 429Q325 444 290 455T219 467Q197 467 177 463T142 448T118 422T109 383Q109 362 119 347T147 321T187 301T235 285Q262 276 288 265T336 237T370 196T383 136ZM201 -206H160L184 -61H253L201 -206Z" />
+<glyph unicode="&#x237;" horiz-adv-x="229" d="M147 -51Q147 -91 140 -123T114 -178T67 -213T-4 -226Q-19 -226 -34 -225T-63 -222V-171Q-36 -174 -15 -174Q18 -174 38 -166T69 -141T83 -101T87 -47V502H147V-51H147Z" />
+<glyph unicode="&#x2C6;" horiz-adv-x="362" d="M295 564L180 646L66 564L39 599L180 698L323 599L295 564Z" />
+<glyph unicode="&#x2C7;" horiz-adv-x="362" d="M312 663L180 565L49 663L76 697L180 617L285 697L312 663Z" />
+<glyph unicode="&#x2D8;" horiz-adv-x="362" d="M314 678Q311 647 299 626T268 591T227 571T180 565Q156 565 134 571T93 590T63 625T48 678H90Q94 644 116 623T180 601Q222 601 245 622T271 678H314H314Z" />
+<glyph unicode="&#x2D9;" horiz-adv-x="362" d="M220 624Q220 607 210 597T180 586Q163 586 153 596T142 624Q142 642 152 652T180 663Q199 663 209 653T220 624Z" />
+<glyph unicode="&#x2DA;" horiz-adv-x="362" d="M268 651Q268 615 243 590T181 565Q144 565 119 590T93 651Q93 687 118 712T181 737Q217 737 242 712T268 651ZM235 651Q235 674 220 690T181 706Q158 706 142 690T126 651Q126 628 142 612T181 596Q204 596 219 612T235 651Z" />
+<glyph unicode="&#x2DB;" horiz-adv-x="362" d="M191 -28Q165 -45 149 -70T133 -125Q133 -157 152 -176T201 -196Q221 -196 235 -191T259 -179L274 -216Q263 -222 240 -229T194 -237Q137 -237 112 -207T86 -130Q86 -83 123 -46T221 15L238 2L191 -28Z" />
+<glyph unicode="&#x2DC;" horiz-adv-x="362" d="M335 634Q326 602 301 587T249 572Q233 572 216 578T182 591T150 604T121 610Q105 610 89 603T64 571L29 594Q45 630 68 643T117 656Q133 656 150 650T184 637T216 624T246 618Q263 618 277 626T299 656L335 634H335Z" />
+<glyph unicode="&#x2DD;" horiz-adv-x="362" d="M224 569L192 588L330 703L389 674L224 569ZM14 569L-18 588L120 703L179 674L14 569Z" />
+<glyph unicode="&#x1E80;" horiz-adv-x="869" d="M673 0H594L435 571H431L276 0H192L34 667H103L236 68H240L403 667H466L632 68H636L768 667H835L673 0ZM463 705L301 809L361 841L494 727L463 705Z" />
+<glyph unicode="&#x1E81;" horiz-adv-x="720" d="M550 0H477L362 411H358L240 0H169L29 502H90L206 53H210L334 502H385L510 54H514L631 502H691L550 0ZM384 565L222 669L282 701L415 587L384 565Z" />
+<glyph unicode="&#x1E82;" horiz-adv-x="869" d="M673 0H594L435 571H431L276 0H192L34 667H103L236 68H240L403 667H466L632 68H636L768 667H835L673 0ZM406 705L375 729L511 844L568 811L406 705Z" />
+<glyph unicode="&#x1E83;" horiz-adv-x="720" d="M550 0H477L362 411H358L240 0H169L29 502H90L206 53H210L334 502H385L510 54H514L631 502H691L550 0ZM337 565L306 589L442 704L499 671L337 565Z" />
+<glyph unicode="&#x1E84;" horiz-adv-x="869" d="M673 0H594L435 571H431L276 0H192L34 667H103L236 68H240L403 667H466L632 68H636L768 667H835L673 0ZM563 754Q563 737 551 725T522 713Q505 713 493 725T481 754Q481 771 493 783T522 795Q539 795 551 783T563 754ZM394 754Q394 737 382 725T353 713Q336 713 324 725T312 754Q312 771 324 783T353 795Q370 795 382 783T394 754Z" />
+<glyph unicode="&#x1E85;" horiz-adv-x="720" d="M550 0H477L362 411H358L240 0H169L29 502H90L206 53H210L334 502H385L510 54H514L631 502H691L550 0ZM486 614Q486 597 474 585T445 573Q428 573 416 585T404 614Q404 631 416 643T445 655Q462 655 474 643T486 614ZM317 614Q317 597 305 585T276 573Q259 573 247 585T235 614Q235 631 247 643T276 655Q293 655 305 643T317 614Z" />
+<glyph unicode="&#x1EF2;" horiz-adv-x="533" d="M299 267V0H235V264L18 667H88L267 323L446 667H515L299 267ZM280 705L118 809L178 841L311 727L280 705Z" />
+<glyph unicode="&#x1EF3;" horiz-adv-x="466" d="M230 -88Q215 -128 202 -154T172 -197T136 -219T89 -226Q81 -226 69 -226T46 -224V-172Q55 -174 63 -174T83 -174Q102 -174 115 -169T139 -151T161 -113T185 -49L209 21L23 502H87L238 89H242L380 502H443L230 -88ZM250 565L88 669L148 701L281 587L250 565Z" />
+<glyph unicode="&#x2013;" horiz-adv-x="602" d="M51 235V283H551V235H51Z" />
+<glyph unicode="&#x2014;" horiz-adv-x="874" d="M51 236V282H823V236H51Z" />
+<glyph unicode="&#x2018;" horiz-adv-x="185" d="M102 509H21L93 700H137L102 509Z" />
+<glyph unicode="&#x2019;" horiz-adv-x="185" d="M91 509H48L83 700H164L91 509Z" />
+<glyph unicode="&#x201A;" horiz-adv-x="185" d="M91 -140H48L83 51H164L91 -140Z" />
+<glyph unicode="&#x201C;" horiz-adv-x="345" d="M263 509H182L255 700H297L263 509ZM101 509H21L94 700H136L101 509Z" />
+<glyph unicode="&#x201D;" horiz-adv-x="345" d="M250 509H208L242 700H324L250 509ZM91 509H48L83 700H164L91 509Z" />
+<glyph unicode="&#x201E;" horiz-adv-x="345" d="M250 -141H208L242 50H324L250 -141ZM91 -141H48L83 50H164L91 -141Z" />
+<glyph unicode="&#x2020;" horiz-adv-x="326" d="M193 486V162H134V486H52V535H134V666H193V535H274V486H193Z" />
+<glyph unicode="&#x2021;" horiz-adv-x="326" d="M191 295V144H133V295H52V347H133V488H52V539H133V666H191V539H273V488H191V347H274V295H191Z" />
+<glyph unicode="&#x2022;" horiz-adv-x="517" d="M405 329Q405 298 394 272T362 226T316 195T258 183Q227 183 201 194T155 225T124 271T112 329Q112 360 123 386T154 432T200 463T258 475Q289 475 315 464T362 433T393 387T405 329Z" />
+<glyph unicode="&#x2026;" horiz-adv-x="630" d="M139 37Q139 15 127 3T93 -10Q71 -10 59 2T47 37Q47 83 93 83Q139 83 139 37ZM361 37Q361 15 349 3T315 -10Q293 -10 281 2T269 37Q269 83 315 83Q361 83 361 37ZM584 37Q584 15 572 3T538 -10Q516 -10 504 2T492 37Q492 83 538 83Q584 83 584 37Z" />
+<glyph unicode="&#x2030;" horiz-adv-x="968" d="M271 547Q271 475 246 435T160 395Q100 395 75 435T50 547Q50 619 75 659T160 700Q221 700 246 660T271 547ZM229 548Q229 574 227 596T217 633T196 658T160 667Q138 667 125 658T105 633T95 595T92 547Q92 481 108 455T160 428Q197 428 213 455T229 548ZM167 0H121L533 700H577L167 0ZM624 146Q624 74 599 34T513 -6Q453 -6 428 34T403 146Q403 218 428 258T513 299Q574 299 599 259T624 146ZM581 147Q581 172 579 194T569 231T549 256T513 265Q491 265 478 256T458 232T448 194T445 146Q445 81 461 54T513 27Q549 27 565 54T581 147ZM918 146Q918 74 893 34T807 -6Q746 -6 721 34T696 146Q696 218 721 258T807 299Q868 299 893 259T918 146ZM875 147Q875 172 873 194T863 231T842 256T807 265Q785 265 772 256T752 232T742 194T739 146Q739 81 755 54T807 27Q843 27 859 54T875 147Z" />
+<glyph unicode="&#x2039;" horiz-adv-x="259" d="M169 117L21 309L169 502L210 476L80 309L210 144L169 117Z" />
+<glyph unicode="&#x203A;" horiz-adv-x="259" d="M90 117L50 144L179 309L49 476L90 502L238 309L90 117Z" />
+<glyph unicode="&#x2044;" horiz-adv-x="111" d="M-165 0H-207L264 700H306L-165 0Z" />
+<glyph unicode="&#x2070;" horiz-adv-x="338" d="M292 530Q292 451 265 406T169 361Q101 361 74 406T46 530Q46 610 73 655T169 700Q237 700 264 655T292 530ZM245 531Q245 559 243 583T232 625T208 653T169 663Q145 663 130 653T107 626T96 584T93 530Q93 458 111 428T169 398Q209 398 227 428T245 531Z" />
+<glyph unicode="&#x2074;" horiz-adv-x="300" d="M224 437V367H177V437H35L22 475L170 694H224V476H274V437H224ZM66 474H178V646L66 474Z" />
+<glyph unicode="&#x2075;" horiz-adv-x="302" d="M261 479Q261 419 229 390T140 361Q109 361 87 368T40 393L62 426Q82 411 100 405T138 398Q176 398 195 417T214 479Q214 554 146 554Q126 554 111 547T78 523L41 549L66 694H239V654H100L84 563H85Q101 577 118 582T157 588Q206 588 233 562T261 479Z" />
+<glyph unicode="&#x2076;" horiz-adv-x="317" d="M276 474Q276 422 246 392T164 361Q101 361 74 399T46 510Q46 550 52 585T72 645T112 685T175 700Q198 700 217 694T257 677L238 644Q222 655 208 659T176 663Q136 663 115 635T91 535H92Q117 577 173 577Q222 577 249 551T276 474ZM233 472Q233 505 216 524T163 543Q133 543 114 523T95 469Q95 435 113 417T164 398Q197 398 215 417T233 472Z" />
+<glyph unicode="&#x2077;" horiz-adv-x="277" d="M250 650Q219 627 199 605T166 555T148 489T142 396V367H93V379Q93 418 96 455T109 528T141 594T200 653V654H25V694H242L250 650H250Z" />
+<glyph unicode="&#x2078;" horiz-adv-x="316" d="M275 460Q275 414 248 388T158 361Q96 361 69 387T41 460Q41 519 99 540V541Q52 562 52 611Q52 646 75 670T158 695Q209 695 236 675T264 611Q264 562 218 541V540Q275 518 275 460ZM219 609Q219 660 158 660Q97 660 97 609Q97 555 158 555Q219 555 219 609ZM230 460Q230 485 215 503T158 522Q128 522 108 509T87 457Q87 396 158 396Q192 396 211 411T230 460Z" />
+<glyph unicode="&#x2079;" horiz-adv-x="320" d="M274 535Q274 444 243 403T143 361Q118 361 98 367T57 387L77 420Q94 407 108 402T145 397Q187 397 208 425T230 531H228Q218 508 196 497T147 485Q98 485 70 512T41 590Q41 642 73 671T156 700Q216 700 245 660T274 535ZM223 590Q223 625 206 644T155 664Q123 664 105 644T86 591Q86 557 103 539T153 520Q185 520 204 539T223 590Z" />
+<glyph unicode="&#x2080;" horiz-adv-x="338" d="M292 48Q292 -31 265 -76T169 -121Q101 -121 74 -76T46 48Q46 128 73 173T169 218Q237 218 264 173T292 48ZM245 49Q245 77 243 101T232 143T208 171T169 181Q145 181 130 171T107 144T96 102T93 48Q93 -24 111 -54T169 -84Q209 -84 227 -54T245 49Z" />
+<glyph unicode="&#x2081;" horiz-adv-x="268" d="M119 -77V159H117L46 117L29 151L128 212H164V-77H245V-115H36V-77H119Z" />
+<glyph unicode="&#x2082;" horiz-adv-x="299" d="M42 -115L35 -71Q79 -32 111 -5T165 45T196 86T207 128Q207 137 205 146T196 162T179 175T151 180Q128 180 109 170T69 137L42 167Q69 194 96 206T156 218Q201 218 227 196T254 129Q254 109 248 91T223 50T172 -3T84 -76H258V-115H42Z" />
+<glyph unicode="&#x2083;" horiz-adv-x="292" d="M251 -23Q251 -64 225 -92T137 -121Q107 -121 82 -113T30 -86L50 -52Q72 -69 92 -76T134 -84Q163 -84 184 -70T205 -21Q205 10 189 23T136 37H99V73H117Q163 73 179 89T196 128Q196 180 136 180Q115 180 95 173T57 150L35 183Q63 203 87 210T141 218Q185 218 213 197T242 130Q242 106 229 88T193 61V60Q251 46 251 -23Z" />
+<glyph unicode="&#x2084;" horiz-adv-x="300" d="M224 -45V-115H177V-45H35L22 -7L170 212H224V-6H274V-45H224ZM66 -8H178V164L66 -8Z" />
+<glyph unicode="&#x2085;" horiz-adv-x="302" d="M261 -3Q261 -63 229 -92T140 -121Q109 -121 87 -114T40 -89L62 -56Q82 -71 100 -77T138 -84Q176 -84 195 -65T214 -3Q214 72 146 72Q126 72 111 65T78 41L41 67L66 212H239V172H100L84 81H85Q101 95 118 100T157 106Q206 106 233 80T261 -3Z" />
+<glyph unicode="&#x2086;" horiz-adv-x="317" d="M276 -8Q276 -60 246 -90T164 -121Q101 -121 74 -83T46 28Q46 68 52 103T72 163T112 203T175 218Q198 218 217 212T257 195L238 162Q222 173 208 177T176 181Q136 181 115 153T91 53H92Q117 95 173 95Q222 95 249 69T276 -8ZM233 -10Q233 23 216 42T163 61Q133 61 114 41T95 -13Q95 -47 113 -65T164 -84Q197 -84 215 -65T233 -10Z" />
+<glyph unicode="&#x2087;" horiz-adv-x="277" d="M250 168Q219 145 199 123T166 73T148 7T142 -86V-115H93V-103Q93 -64 96 -27T109 46T141 112T200 171V172H25V212H242L250 168H250Z" />
+<glyph unicode="&#x2088;" horiz-adv-x="316" d="M275 -22Q275 -68 248 -94T158 -121Q96 -121 69 -95T41 -22Q41 37 99 58V59Q52 80 52 129Q52 164 75 188T158 213Q209 213 236 193T264 129Q264 80 218 59V58Q275 36 275 -22ZM219 127Q219 178 158 178Q97 178 97 127Q97 73 158 73Q219 73 219 127ZM230 -22Q230 3 215 21T158 40Q128 40 108 27T87 -25Q87 -86 158 -86Q192 -86 211 -71T230 -22Z" />
+<glyph unicode="&#x2089;" horiz-adv-x="320" d="M274 53Q274 -38 243 -79T143 -121Q118 -121 98 -115T57 -94L77 -62Q94 -75 108 -80T145 -85Q166 -85 182 -79T208 -57T224 -15T230 50H228Q218 27 196 15T147 3Q98 3 70 30T41 108Q41 161 73 189T156 218Q216 218 245 179T274 53ZM223 109Q223 144 206 163T155 182Q123 182 105 162T86 109Q86 75 103 57T153 39Q185 39 204 57T223 109Z" />
+<glyph unicode="&#x20AC;" horiz-adv-x="554" d="M97 426Q103 475 114 521T149 601T213 657T316 678Q376 678 422 654T509 580L468 542Q436 587 401 606T318 626Q277 626 249 615T203 579T174 516T159 426H444V385H157Q157 372 157 360T156 334Q156 324 156 308T158 281H444V239H160Q163 212 168 178T189 113T234 61T315 40Q374 40 410 66T476 140L515 105Q497 80 478 59T434 22T380 -2T312 -11Q256 -11 218 6T155 56T117 135T97 239H24V281H94Q93 290 93 307T92 334Q92 347 92 359T94 385H24V426H97Z" />
+<glyph unicode="&#x2122;" horiz-adv-x="596" d="M494 481V630L414 514L334 630V481H294V700H332L414 578L496 700H534V481H494ZM165 664V481H124V664H49V700H240V664H165Z" />
+<glyph unicode="&#x2153;" horiz-adv-x="690" d="M144 405V641H142L71 599L54 633L153 694H189V405H270V367H61V405H144ZM138 0H96L567 700H609L138 0ZM650 92Q650 51 624 23T536 -6Q506 -6 481 2T429 29L449 63Q471 46 491 39T533 31Q562 31 583 45T604 94Q604 125 588 138T535 152H498V188H516Q562 188 578 204T595 243Q595 295 535 295Q514 295 494 288T456 265L434 298Q462 318 486 325T540 333Q584 333 612 312T641 245Q641 221 628 203T592 176V175Q650 161 650 92Z" />
+<glyph unicode="&#x2154;" horiz-adv-x="690" d="M62 367L55 411Q99 450 131 477T185 527T216 568T227 610Q227 619 225 628T216 644T199 657T171 662Q148 662 129 652T89 619L62 649Q89 676 116 688T176 700Q221 700 247 678T274 611Q274 591 268 573T243 532T192 479T104 406H278V367H62ZM138 0H96L567 700H609L138 0ZM650 92Q650 51 624 23T536 -6Q506 -6 481 2T429 29L449 63Q471 46 491 39T533 31Q562 31 583 45T604 94Q604 125 588 138T535 152H498V188H516Q562 188 578 204T595 243Q595 295 535 295Q514 295 494 288T456 265L434 298Q462 318 486 325T540 333Q584 333 612 312T641 245Q641 221 628 203T592 176V175Q650 161 650 92Z" />
+<glyph unicode="&#x215B;" horiz-adv-x="690" d="M144 405V641H142L71 599L54 633L153 694H189V405H270V367H61V405H144ZM138 0H96L567 700H609L138 0ZM656 93Q656 47 629 21T539 -6Q477 -6 450 20T422 93Q422 152 480 173V174Q433 195 433 244Q433 279 456 303T539 328Q590 328 617 308T645 244Q645 195 599 174V173Q656 151 656 93ZM600 242Q600 293 539 293Q478 293 478 242Q478 188 539 188Q600 188 600 242ZM611 93Q611 118 596 136T539 155Q509 155 489 142T468 90Q468 29 539 29Q573 29 592 44T611 93Z" />
+<glyph unicode="&#x215C;" horiz-adv-x="690" d="M267 459Q267 418 241 390T153 361Q123 361 98 369T46 396L66 430Q88 413 108 406T150 398Q179 398 200 412T221 461Q221 492 205 505T152 519H115V555H133Q179 555 195 571T212 610Q212 662 152 662Q131 662 111 655T73 632L51 665Q79 685 103 692T157 700Q201 700 229 679T258 612Q258 588 245 570T209 543V542Q267 528 267 459ZM138 0H96L567 700H609L138 0ZM656 93Q656 47 629 21T539 -6Q477 -6 450 20T422 93Q422 152 480 173V174Q433 195 433 244Q433 279 456 303T539 328Q590 328 617 308T645 244Q645 195 599 174V173Q656 151 656 93ZM600 242Q600 293 539 293Q478 293 478 242Q478 188 539 188Q600 188 600 242ZM611 93Q611 118 596 136T539 155Q509 155 489 142T468 90Q468 29 539 29Q573 29 592 44T611 93Z" />
+<glyph unicode="&#x215D;" horiz-adv-x="690" d="M277 479Q277 419 245 390T156 361Q125 361 103 368T56 393L78 426Q98 411 116 405T154 398Q192 398 211 417T230 479Q230 554 162 554Q142 554 127 547T94 523L57 549L82 694H255V654H116L100 563H101Q117 577 134 582T173 588Q222 588 249 562T277 479ZM138 0H96L567 700H609L138 0ZM656 93Q656 47 629 21T539 -6Q477 -6 450 20T422 93Q422 152 480 173V174Q433 195 433 244Q433 279 456 303T539 328Q590 328 617 308T645 244Q645 195 599 174V173Q656 151 656 93ZM600 242Q600 293 539 293Q478 293 478 242Q478 188 539 188Q600 188 600 242ZM611 93Q611 118 596 136T539 155Q509 155 489 142T468 90Q468 29 539 29Q573 29 592 44T611 93Z" />
+<glyph unicode="&#x215E;" horiz-adv-x="690" d="M305 650Q274 627 254 605T221 555T203 489T197 396V367H148V379Q148 418 151 455T164 528T196 594T255 653V654H80V694H297L305 650ZM138 0H96L567 700H609L138 0ZM656 93Q656 47 629 21T539 -6Q477 -6 450 20T422 93Q422 152 480 173V174Q433 195 433 244Q433 279 456 303T539 328Q590 328 617 308T645 244Q645 195 599 174V173Q656 151 656 93ZM600 242Q600 293 539 293Q478 293 478 242Q478 188 539 188Q600 188 600 242ZM611 93Q611 118 596 136T539 155Q509 155 489 142T468 90Q468 29 539 29Q573 29 592 44T611 93Z" />
+<glyph unicode="&#x2212;" horiz-adv-x="554" d="M54 231V285H500V231H54Z" />
+<glyph unicode="&#x2260;" horiz-adv-x="554" d="M369 312L256 201H500V147H209L118 55L80 89L139 147H55V201H187L299 312H55V366H347L442 463L482 430L416 366H500V312H369Z" />
+<glyph unicode="&#x2264;" horiz-adv-x="554" d="M54 256V315L500 484V427L114 286L500 148V92L54 256ZM54 0V53H500V0H54Z" />
+<glyph unicode="&#x2265;" horiz-adv-x="554" d="M54 92V148L440 286L54 427V484L500 315V256L54 92ZM54 0V53H500V0H54Z" />
+<glyph unicode="&#x24DF;" horiz-adv-x="906" d="M799 334Q799 262 772 200T698 91T588 17T453 -10Q381 -10 318 17T208 90T134 199T107 334Q107 406 134 468T208 577T318 651T453 678Q525 678 588 651T698 578T772 469T799 334ZM766 334Q766 398 742 454T676 554T577 622T453 647Q386 647 329 622T230 554T165 455T141 334Q141 270 166 214T233 115T332 48T453 23Q517 23 573 47T673 114T741 213T766 334ZM637 421Q637 350 600 318T484 285H375V107H326V551Q360 555 397 556T471 557Q508 557 538 551T591 530T625 488T637 421ZM588 419Q588 449 581 468T558 499T521 515T468 519Q444 519 422 518T375 514V323H468Q494 323 516 326T554 339T579 368T588 419Z" />
+<glyph unicode="&#xF6C3;" horiz-adv-x="362" d="M177 -206H136L160 -61H229L177 -206Z" />
+
+
+</font>
+</defs>
+<text x="40" y="40" font-family="ClearviewATT Lt" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="ClearviewATT Lt" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="ClearviewATT Lt" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="ClearviewATT Lt" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.ttf b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.ttf
new file mode 100644
index 0000000000..c9a45c0e51
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.woff b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.woff
new file mode 100644
index 0000000000..521ee2e918
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-Lt.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.eot b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.eot
new file mode 100644
index 0000000000..06772919e7
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.svg b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.svg
new file mode 100644
index 0000000000..bff63e5524
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.svg
@@ -0,0 +1,424 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[ClearviewATT LtIt]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Copyright (c) Terminal Design, Inc, 2005. All rights reserved.]]></copyright>
+<trademark><![CDATA[Clearview is a trademark of Terminal Design, Inc.]]></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="490" id="ClearviewATT-LtIt">
+<font-face font-family="ClearviewATT LtIt" panose-1="2 11 5 6 3 5 0 2 0 4" ascent="750" descent="-250" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="484" d="M585 667L492 0H-8L85 667H585ZM116 606L40 65L264 336L116 606ZM462 67L538 605L315 336L462 67ZM65 35H428L284 299L65 35ZM294 373L507 630H153L294 373Z" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " horiz-adv-x="280" />
+<glyph unicode="!" horiz-adv-x="271" d="M266 667Q244 543 224 422T184 176H136Q151 300 165 421T193 667H266ZM190 47Q190 23 175 7T134 -10Q113 -10 102 0T91 29Q91 53 106 69T147 85Q190 85 190 47Z" />
+<glyph unicode="&quot;" horiz-adv-x="368" d="M356 518H316L326 700H399L356 518ZM195 518H154L165 700H238L195 518Z" />
+<glyph unicode="#" horiz-adv-x="582" d="M614 471H495L450 316H574L561 271H438L393 113H344L388 271H240L196 113H148L192 271H68L80 316H205L249 471H123L135 515H260L304 667H354L311 515H459L502 667H552L507 515H627L614 471ZM297 471L254 316H402L447 471H297Z" />
+<glyph unicode="$" horiz-adv-x="554" d="M508 221Q508 181 493 147T452 87T389 46T308 28L298 -47H239L250 29Q207 30 163 47T86 87L120 138Q158 109 201 93T285 76Q313 76 341 83T393 105T430 146T445 208Q445 246 422 269T360 312L265 362Q219 387 189 417T159 495Q159 530 173 558T211 607T269 638T339 650L349 723H409L399 651Q432 650 470 637T539 602L508 552Q477 576 442 589T362 603Q297 603 260 580T222 501Q222 472 244 451T316 404L399 362Q456 333 482 301T508 221Z" />
+<glyph unicode="%" horiz-adv-x="709" d="M374 583Q374 546 367 507T343 436T297 382T223 361Q166 361 143 392T120 477Q120 514 126 553T150 625T197 679T271 700Q328 700 351 668T374 583ZM228 398Q260 398 280 418T310 468T324 530T328 587Q328 620 316 641T266 663Q234 663 214 643T184 594T170 532T166 475Q166 442 178 420T228 398ZM167 0H121L631 700H675L167 0ZM685 216Q685 179 679 140T655 69T609 15T535 -6Q478 -6 455 26T431 112Q431 149 438 188T462 259T508 312T582 333Q639 333 662 301T685 216ZM540 31Q572 31 591 51T621 100T635 161T639 218Q639 251 627 273T577 296Q545 296 526 276T496 228T482 167T478 111Q478 78 490 55T540 31Z" />
+<glyph unicode="&amp;" horiz-adv-x="572" d="M269 -11Q214 -11 177 3T117 40T85 93T75 154Q75 196 89 229T128 290T184 339T251 381Q229 411 211 447T192 522Q192 557 205 586T241 635T296 667T366 678Q405 678 430 668T469 641T489 604T495 566Q495 530 480 501T441 449T386 406T325 370L460 133Q469 148 476 169T486 213Q489 238 489 264Q489 282 488 297T485 323H542Q543 314 543 302T544 276Q544 260 543 244T540 213Q535 181 524 149T491 92L545 0H478L446 53Q415 27 373 8T269 -11ZM275 35Q317 35 355 51T420 92L273 344Q247 328 223 310T179 270T148 220T136 156Q136 94 175 65T275 35ZM305 403Q325 415 348 430T392 464T425 506T439 559Q439 571 436 584T423 609T399 627T360 634Q309 634 280 607T251 531Q251 514 256 497T270 462T287 430T305 403Z" />
+<glyph unicode="&apos;" horiz-adv-x="207" d="M195 518H154L165 700H238L195 518Z" />
+<glyph unicode="(" horiz-adv-x="278" d="M158 -137Q123 -47 105 32T87 195Q87 351 139 491T286 771L325 762Q246 630 198 486T150 181Q150 103 162 27T200 -128L158 -137Z" />
+<glyph unicode=")" horiz-adv-x="278" d="M34 -128Q114 4 161 149T209 455Q209 533 197 609T159 762L200 771Q235 679 253 600T271 437Q271 282 220 141T72 -137L34 -128Z" />
+<glyph unicode="*" horiz-adv-x="316" d="M259 554L309 490L257 454L224 531L170 455L128 490L197 554L112 569L140 624L214 588L211 674H273L250 589L334 625L347 570L259 554Z" />
+<glyph unicode="+" horiz-adv-x="554" d="M339 231L310 22H255L284 231H86L94 285H292L319 482H374L347 285H540L532 231H339Z" />
+<glyph unicode="&#x2c;" horiz-adv-x="184" d="M55 -112H14L74 85H156L55 -112Z" />
+<glyph unicode="-" horiz-adv-x="369" d="M84 232L92 286H357L349 232H84Z" />
+<glyph unicode="." horiz-adv-x="184" d="M145 47Q145 22 130 6T89 -10Q71 -10 60 0T48 29Q48 55 62 70T103 85Q122 85 133 76T145 47Z" />
+<glyph unicode="/" horiz-adv-x="479" d="M59 -115H2L512 700H571L59 -115Z" />
+<glyph unicode="0" horiz-adv-x="554" d="M554 461Q554 415 550 363T534 258T503 157T452 71T378 12T275 -11Q222 -11 187 5T130 49T101 118T92 207Q92 253 96 306T112 411T143 512T194 597T269 656T372 678Q425 678 460 662T516 618T545 549T554 461ZM283 40Q329 40 362 62T419 120T456 201T479 294T489 386T492 465Q492 499 487 528T467 579T428 613T364 626Q317 626 283 604T226 546T189 464T168 371T158 278T155 198Q155 164 160 135T180 85T220 52T283 40Z" />
+<glyph unicode="1" horiz-adv-x="554" d="M268 54L345 599H341L167 503L147 551L362 670H416L329 54H490L483 0H92L99 54H268Z" />
+<glyph unicode="2" horiz-adv-x="554" d="M82 0L79 62Q109 86 150 117T236 184T323 259T400 341T455 426T476 514Q476 567 449 596T367 625Q341 625 316 617T269 597T226 568T187 533L159 578Q207 623 259 650T378 678Q450 678 495 641T540 526Q540 477 521 431T469 340T396 256T310 180T223 112T143 54H491L484 0H82Z" />
+<glyph unicode="3" horiz-adv-x="554" d="M513 227Q513 175 497 132T450 57T373 7T266 -11Q204 -11 162 4T78 52L114 101Q150 69 189 55T271 40Q358 40 403 90T449 223Q449 276 417 300T309 324H246L253 376H275Q312 376 348 383T412 407T458 453T476 527Q476 575 445 600T354 626Q273 626 190 567L169 616Q223 649 269 663T368 678Q404 678 435 670T490 645T527 601T540 537Q540 475 507 426T410 356L409 353Q462 344 487 314T513 227Z" />
+<glyph unicode="4" horiz-adv-x="554" d="M434 148L414 0H351L371 148H88L78 207L433 665H507L442 202H533L525 148H434ZM433 596L138 202H382L437 596H433Z" />
+<glyph unicode="5" horiz-adv-x="554" d="M519 284Q519 220 504 166T456 73T374 11T255 -11Q204 -11 163 2T80 49L115 99Q153 67 187 54T260 40Q311 40 348 57T408 105T443 179T455 275Q455 343 422 373T326 403Q285 403 249 389T170 337L123 374L213 667H546L539 613H256L189 399L192 396Q230 425 263 439T344 454Q429 454 474 413T519 284Z" />
+<glyph unicode="6" horiz-adv-x="554" d="M530 255Q530 204 514 156T465 71T386 12T278 -11Q174 -11 131 39T88 186Q88 208 90 232T96 283Q108 369 128 441T183 566T269 648T395 678Q434 678 470 668T536 638L504 589Q473 611 447 618T387 625Q345 625 310 611T246 562T196 470T161 326L165 325Q229 424 350 424Q437 424 483 380T530 255ZM286 40Q330 40 364 57T420 102T454 168T466 248Q466 372 332 372Q291 372 258 356T199 312T161 247T147 169Q147 106 183 73T286 40Z" />
+<glyph unicode="7" horiz-adv-x="554" d="M572 609Q499 552 452 498T375 382T326 245T295 73L285 0H221L225 29Q235 102 250 181T294 337T373 484T501 610L502 613H154L161 667H569L572 609H572Z" />
+<glyph unicode="8" horiz-adv-x="554" d="M524 221Q524 115 461 52T275 -11Q174 -11 127 28T80 145Q80 181 87 214T112 275T155 323T220 356V360Q180 375 162 406T143 479Q143 573 201 625T371 678Q460 678 505 644T551 540Q551 477 521 430T431 360V356Q479 342 501 307T524 221ZM329 381Q364 381 393 390T444 417T477 465T489 533Q489 582 459 604T364 627Q288 627 247 592T206 479Q206 430 236 406T329 381ZM281 39Q463 39 463 220Q463 245 456 266T433 302T389 325T322 333Q234 333 188 287T142 147Q142 90 176 65T281 39Z" />
+<glyph unicode="9" horiz-adv-x="554" d="M546 350Q532 252 508 183T449 71T365 9T255 -11Q211 -11 169 2T95 43L132 88Q162 63 191 52T262 40Q307 40 342 54T406 103T454 200T488 357H484Q456 303 408 274T303 244Q215 244 167 291T118 420Q118 475 136 522T187 604T266 658T370 678Q462 678 508 628T555 463Q555 413 546 350ZM316 296Q358 296 391 312T446 355T479 417T491 493Q491 558 457 592T362 626Q325 626 292 614T235 576T196 513T182 425Q182 364 216 330T316 296Z" />
+<glyph unicode=":" horiz-adv-x="184" d="M197 412Q197 386 183 370T142 353Q123 353 112 363T100 393Q100 419 115 434T156 449Q175 449 186 440T197 412ZM146 47Q146 22 132 6T91 -10Q72 -10 61 -1T49 29Q49 55 63 70T104 85Q123 85 134 76T146 47Z" />
+<glyph unicode=";" horiz-adv-x="184" d="M55 -112H14L73 85H151L55 -112ZM207 410Q207 385 192 369T151 352Q132 352 121 362T109 392Q109 418 123 433T164 448Q183 448 195 439T207 410Z" />
+<glyph unicode="&lt;" horiz-adv-x="554" d="M86 230L94 285L570 500L562 445L145 258L510 74L502 20L86 230Z" />
+<glyph unicode="=" horiz-adv-x="554" d="M98 312L106 366H551L543 312H98ZM75 147L83 201H528L520 147H75Z" />
+<glyph unicode="&gt;" horiz-adv-x="554" d="M56 20L64 74L480 258L116 445L124 500L540 285L532 230L56 20Z" />
+<glyph unicode="?" horiz-adv-x="499" d="M517 537Q517 479 499 443T452 384T390 346T326 318T275 285T249 234L239 161H178L190 243Q196 284 218 308T269 347T330 374T390 404T435 450T453 528Q453 555 443 574T416 604T376 621T330 626Q283 626 242 609T163 566L139 612Q186 642 236 660T343 678Q376 678 407 670T463 645T502 601T517 537ZM238 46Q238 22 224 6T184 -10Q167 -10 155 -1T143 27Q143 52 157 67T197 83Q215 83 226 74T238 46Z" />
+<glyph unicode="@" horiz-adv-x="807" d="M777 330Q777 285 764 234T725 140T661 69T572 41Q546 41 531 57T515 100H511Q485 68 447 51T367 34Q313 34 287 64T260 148Q260 193 275 239T317 322T384 382T474 406Q501 406 524 397T561 361H565L577 399H634Q632 390 625 367T610 313T592 250T575 188T562 138T557 112Q557 97 566 89T590 81Q626 81 653 106T699 168T727 247T737 323Q737 375 720 414T671 480T598 520T506 534Q425 534 358 504T242 422T166 301T139 153Q139 39 203 -23T381 -86Q438 -86 490 -72T590 -28L605 -60Q552 -93 493 -107T372 -121Q241 -121 169 -51T97 151Q97 239 128 315T214 447T344 536T509 569Q564 569 613 554T698 509T756 434T777 330ZM390 76Q442 76 473 107T522 190Q531 217 540 246T549 304Q549 339 526 351T472 364Q432 364 403 345T353 295T323 227T313 156Q313 118 332 97T390 76Z" />
+<glyph unicode="A" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611Z" />
+<glyph unicode="B" horiz-adv-x="620" d="M583 209Q583 157 566 118T518 53T443 13T346 0H92L185 664Q219 668 273 671T380 675Q417 675 456 672T527 655T579 613T600 535Q600 465 568 421T473 354V350Q526 341 554 304T583 209ZM344 372Q383 372 418 377T479 399T520 446T535 524Q535 556 522 576T487 606T436 620T373 624Q345 624 312 623T241 617L207 373Q239 372 275 372T344 372ZM300 50Q342 50 381 54T451 74T499 123T518 213Q518 253 502 275T459 308T400 321T332 323Q308 323 291 323T259 323T230 323T200 321L162 52Q198 50 230 50T300 50Z" />
+<glyph unicode="C" horiz-adv-x="614" d="M590 76Q529 28 472 9T352 -11Q231 -11 170 57T109 251Q109 328 129 404T193 541T301 640T456 678Q516 678 564 658T658 596L614 552Q574 590 536 607T449 625Q377 625 326 593T241 508T192 391T176 259Q176 216 185 177T215 107T273 60T364 42Q410 42 460 60T561 120L590 76H590Z" />
+<glyph unicode="D" horiz-adv-x="650" d="M633 412Q633 333 612 259T548 127T439 35T282 0H92L185 664Q223 669 270 672T373 675Q440 675 489 656T570 603T617 520T633 412ZM274 53Q352 53 407 80T498 156T550 268T567 407Q567 448 558 487T526 556T465 604T367 623Q335 623 303 621T241 616L162 53H274Z" />
+<glyph unicode="E" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92Z" />
+<glyph unicode="F" horiz-adv-x="485" d="M241 612L208 381H478L470 326H200L155 0H92L185 667H548L541 612H241Z" />
+<glyph unicode="G" horiz-adv-x="679" d="M365 40Q414 40 451 54T516 95T560 156T583 234L591 287H408L415 340H657L646 262Q626 124 551 57T355 -10Q287 -10 241 11T165 68T122 153T109 256Q109 332 130 407T195 543T304 640T459 678Q524 678 572 656T658 599L616 555Q574 593 535 609T452 625Q378 625 326 593T240 508T191 390T175 256Q175 213 184 174T214 105T272 58T365 40Z" />
+<glyph unicode="H" horiz-adv-x="645" d="M490 0L536 331H201L155 0H92L185 667H248L209 388H544L583 667H646L553 0H490Z" />
+<glyph unicode="I" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92Z" />
+<glyph unicode="J" horiz-adv-x="425" d="M354 151Q342 66 295 28T175 -11Q139 -11 108 -2T48 30L81 81Q106 59 127 51T175 42Q230 42 256 70T291 156L363 667H426L354 151H354Z" />
+<glyph unicode="K" horiz-adv-x="545" d="M446 0L280 343L190 252L155 0H92L185 667H248L201 333H205L521 667H598L333 388L522 0H446Z" />
+<glyph unicode="L" horiz-adv-x="473" d="M92 0L185 667H248L162 55H443L436 0H92Z" />
+<glyph unicode="M" horiz-adv-x="750" d="M596 0L674 559H670L405 214L236 558H232L154 0H92L185 667H249L419 315L688 667H751L658 0H596Z" />
+<glyph unicode="N" horiz-adv-x="684" d="M530 0L236 560H232L154 0H92L185 667H252L541 110H545L623 667H685L592 0H530Z" />
+<glyph unicode="O" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41Z" />
+<glyph unicode="P" horiz-adv-x="585" d="M596 516Q596 396 536 330T351 263H191L155 0H92L185 664Q236 669 283 672T380 675Q429 675 469 668T537 642T580 593T596 516ZM339 315Q437 315 484 359T531 507Q531 574 491 598T372 623Q337 623 307 621T241 615L199 315H339Z" />
+<glyph unicode="Q" horiz-adv-x="732" d="M715 415Q715 359 705 302T671 192T610 96T518 28V24Q533 19 546 10T572 -8T601 -25T635 -38L611 -89Q584 -85 561 -72T517 -44T476 -19T437 -7Q423 -7 404 -9T364 -11Q235 -11 172 59T109 255Q109 336 130 412T194 548T304 642T461 678Q589 678 652 609T715 415ZM371 41Q447 41 500 74T586 160T634 279T649 411Q649 460 638 499T603 567T542 610T453 626Q377 626 324 594T238 509T191 391T176 259Q176 210 187 170T221 102T282 57T371 41Z" />
+<glyph unicode="R" horiz-adv-x="581" d="M459 0L409 246Q404 273 390 282T348 292H196L155 0H92L185 664Q232 669 277 672T370 675Q409 675 446 669T514 646T562 599T581 523Q581 450 548 393T442 312V308Q453 302 460 290T470 265L526 0H459ZM345 344Q430 344 472 384T515 512Q515 547 503 568T469 602T421 618T364 623Q333 623 303 622T241 615L203 344H345Z" />
+<glyph unicode="S" horiz-adv-x="506" d="M481 201Q481 150 464 111T416 44T343 3T250 -11Q192 -11 142 5T45 57L82 107Q118 75 160 58T251 40Q287 40 317 49T369 77T403 124T416 189Q416 226 395 251T341 295T271 331T201 371T148 424T126 500Q126 546 144 580T192 635T261 667T343 678Q393 678 437 663T522 618L490 565Q423 626 334 626Q306 626 281 620T236 599T205 563T193 511Q193 476 214 453T268 411T337 376T406 336T459 281T481 201Z" />
+<glyph unicode="T" horiz-adv-x="482" d="M358 612L272 0H209L295 612H107L114 667H554L547 612H358Z" />
+<glyph unicode="U" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595Z" />
+<glyph unicode="V" horiz-adv-x="545" d="M309 0H234L118 667H186L279 65H283L546 667H613L309 0Z" />
+<glyph unicode="W" horiz-adv-x="869" d="M673 0H594L515 571H511L276 0H192L127 667H196L245 68H249L496 667H559L641 68H645L861 667H928L673 0Z" />
+<glyph unicode="X" horiz-adv-x="519" d="M427 0L301 291H297L92 0H20L272 345L138 667H210L316 393H320L509 667H581L348 339L499 0H427Z" />
+<glyph unicode="Y" horiz-adv-x="533" d="M336 267L299 0H235L272 264L111 667H181L312 323L539 667H608L336 267Z" />
+<glyph unicode="Z" horiz-adv-x="532" d="M47 0L54 55L493 612H148L155 667H570L563 613L122 54H492L485 0H47Z" />
+<glyph unicode="[" horiz-adv-x="278" d="M64 -94L183 751H337L331 708H234L127 -50H224L218 -94H64Z" />
+<glyph unicode="\" horiz-adv-x="479" d="M382 -115L112 667H171L441 -115H382Z" />
+<glyph unicode="]" horiz-adv-x="278" d="M32 -94L38 -50H135L242 708H145L151 751H305L186 -94H32Z" />
+<glyph unicode="^" horiz-adv-x="331" d="M357 526L248 603L113 526L84 559L256 660L400 559L357 526Z" />
+<glyph unicode="_" horiz-adv-x="484" d="M-26 -130L-19 -85H480L473 -130H-26Z" />
+<glyph unicode="`" horiz-adv-x="362" d="M322 565L175 669L239 701L356 587L322 565Z" />
+<glyph unicode="a" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331Z" />
+<glyph unicode="b" horiz-adv-x="550" d="M533 329Q533 275 522 215T482 105T409 22T295 -11Q237 -11 202 13T152 83H148L126 0H82L180 700H239L202 437H206Q235 481 278 498T367 515Q415 515 447 501T498 461T525 402T533 329ZM288 35Q342 35 377 63T434 135T463 230T472 327Q472 355 467 380T448 425T410 456T349 467Q286 467 242 433T187 331L168 197Q167 189 167 182T166 167Q166 139 173 115T196 73T234 45T288 35Z" />
+<glyph unicode="c" horiz-adv-x="482" d="M479 411Q456 437 424 451T350 466Q297 466 259 442T197 380T162 294T150 198Q150 166 157 137T180 86T223 51T290 38Q331 38 365 51T437 94L453 50Q413 21 372 5T280 -11Q230 -11 194 4T134 47T99 111T88 193Q88 248 103 305T150 409T233 485T355 515Q402 515 441 499T512 456L479 411Z" />
+<glyph unicode="d" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L507 700H566L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331Z" />
+<glyph unicode="e" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454Z" />
+<glyph unicode="f" horiz-adv-x="296" d="M371 652Q360 655 351 656T329 658Q305 658 291 655T268 643T255 621T248 584L236 502H343L336 453H230L167 0H107L170 452H93L100 502H177L188 583Q192 614 200 637T223 675T262 697T326 705Q338 705 351 704T378 700L371 652H371Z" />
+<glyph unicode="g" horiz-adv-x="549" d="M457 -65Q449 -122 430 -154T385 -202T328 -222T265 -226Q202 -226 155 -214T65 -178L94 -128Q132 -153 173 -165T263 -178Q296 -178 319 -173T359 -155T384 -120T399 -64L419 83H415Q383 34 345 14T257 -7Q210 -7 178 7T126 45T96 104T87 179Q87 230 97 289T135 398T208 482T327 515Q378 515 413 498T469 433H473L487 502H537L457 -65ZM454 332Q464 399 429 433T330 467Q275 467 240 440T185 372T157 281T149 188Q149 156 155 129T176 82T213 51T272 39Q306 39 334 52T383 86T417 137T435 199L454 332Z" />
+<glyph unicode="h" horiz-adv-x="548" d="M407 0L449 301Q452 321 454 339T456 373Q456 394 452 410T436 439T404 457T353 463Q282 463 239 427T186 323L141 0H82L180 700H239L201 434H205Q230 474 272 494T372 515Q413 515 440 507T485 482T509 441T516 385Q516 371 514 354T510 318L466 0H407Z" />
+<glyph unicode="i" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM238 630Q238 610 226 597T193 584Q178 584 169 593T159 617Q159 637 171 649T204 662Q238 662 238 630Z" />
+<glyph unicode="j" horiz-adv-x="229" d="M138 -57Q127 -137 88 -181T-35 -226Q-50 -226 -65 -225T-94 -222L-87 -171Q-60 -174 -39 -174Q-7 -174 14 -166T48 -141T69 -101T80 -47L157 502H216L138 -57ZM243 630Q243 610 231 597T198 584Q183 584 174 593T164 617Q164 637 176 649T209 662Q243 662 243 630Z" />
+<glyph unicode="k" horiz-adv-x="477" d="M379 0L265 264L163 162L141 0H82L180 700H239L174 237H178L428 502H504L313 305L451 0H379Z" />
+<glyph unicode="l" horiz-adv-x="251" d="M222 0Q213 -2 199 -3T171 -5Q124 -5 105 15T94 91L180 700H239L156 107Q153 86 154 74T162 57T179 50T206 49H228L222 0H222Z" />
+<glyph unicode="m" horiz-adv-x="822" d="M681 0L724 313Q726 329 727 344T729 373Q729 393 725 409T711 438T682 456T637 463Q505 463 487 333L441 0H382L426 318Q428 332 429 346T430 372Q430 414 410 438T334 463Q303 463 278 452T233 422T202 379T187 329L141 0H82L152 502H203L201 437H205Q232 476 270 495T354 515Q402 515 436 497T482 432Q512 477 554 496T645 515Q686 515 713 506T758 479T782 437T789 381Q789 367 788 352T784 319L740 0H681Z" />
+<glyph unicode="n" horiz-adv-x="548" d="M407 0L449 301Q455 338 455 368Q455 390 451 407T434 437T402 456T351 463Q280 463 238 426T186 323L141 0H82L152 502H203L200 437H204Q264 515 369 515Q411 515 439 507T484 482T509 442T516 387Q516 356 510 319L466 0H407Z" />
+<glyph unicode="o" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467Z" />
+<glyph unicode="p" horiz-adv-x="549" d="M532 328Q532 274 520 214T480 104T406 22T290 -11Q236 -11 204 12T155 74H151L110 -220H51L152 502H202L199 436H203Q235 479 275 497T364 515Q413 515 445 501T496 461T524 401T532 328ZM285 35Q340 35 376 63T434 135T463 231T472 329Q472 357 467 382T447 426T409 456T347 467Q284 467 240 432T187 330L168 198Q166 190 166 183T166 167Q166 139 173 115T195 73T232 45T285 35Z" />
+<glyph unicode="q" horiz-adv-x="550" d="M444 -236Q414 -227 400 -213T386 -167Q386 -152 389 -134L418 73H414Q384 30 346 10T255 -11Q207 -11 175 4T124 44T96 105T88 179Q88 232 98 291T136 400T209 482T327 515Q372 515 410 498T469 435H473L488 502H537L450 -119Q447 -137 447 -148Q447 -162 453 -171T480 -189L444 -236ZM454 331Q463 396 429 431T331 467Q275 467 240 440T184 372T157 281T150 186Q150 156 155 129T175 81T212 48T272 35Q306 35 334 48T383 83T417 134T435 197L454 331Z" />
+<glyph unicode="r" horiz-adv-x="343" d="M366 461Q291 461 244 432T187 333L141 0H82L152 502H202L199 440H203Q232 480 274 497T369 514H391L383 461H366Z" />
+<glyph unicode="s" horiz-adv-x="428" d="M404 152Q404 111 389 81T347 30T287 -1T214 -11Q167 -11 125 0T44 38L73 85Q104 62 143 49T221 36Q245 36 267 42T307 61T334 93T344 140Q344 169 326 186T282 217T224 241T167 268T122 307T104 368Q104 408 120 436T162 481T221 507T290 515Q328 515 364 505T435 477L410 429Q383 447 350 457T284 467Q262 467 240 463T201 448T173 420T162 377Q162 350 180 334T225 306T283 282T341 255T386 214T404 152Z" />
+<glyph unicode="t" horiz-adv-x="332" d="M289 -1Q279 -3 264 -4T235 -5Q201 -5 179 0T145 16T127 42T122 77Q122 95 125 116L172 452H88L95 502H179L199 642H258L238 502H360L353 452H231L185 126Q182 108 182 94Q182 70 197 59T259 47H296L289 -1H289Z" />
+<glyph unicode="u" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409Z" />
+<glyph unicode="v" horiz-adv-x="455" d="M267 0H190L94 502H157L235 50H239L438 502H501L267 0Z" />
+<glyph unicode="w" horiz-adv-x="720" d="M550 0H477L419 411H415L240 0H169L99 502H160L213 53H217L404 502H455L517 54H521L701 502H761L550 0Z" />
+<glyph unicode="x" horiz-adv-x="440" d="M354 0L255 224H251L88 0H19L225 266L108 502H176L264 305H268L409 502H473L296 268L421 0H354Z" />
+<glyph unicode="y" horiz-adv-x="466" d="M217 -88Q197 -128 180 -154T144 -197T105 -219T57 -226Q49 -226 37 -226T14 -224L21 -172Q30 -174 38 -174T58 -174Q77 -174 91 -169T118 -151T145 -113T178 -49L211 21L93 502H157L250 89H254L450 502H513L217 -88Z" />
+<glyph unicode="z" horiz-adv-x="441" d="M42 0L48 49L392 452H113L120 502H464L457 452L115 50H406L399 0H42Z" />
+<glyph unicode="{" horiz-adv-x="313" d="M217 -94Q167 -94 150 -72T133 -14Q133 -3 134 9T137 35L163 216Q168 247 161 270T120 300L128 357Q164 365 177 387T195 442L220 624Q224 651 230 674T248 714T281 742T334 752H369L363 709H342Q320 709 308 702T290 682T282 655T277 622L251 437Q248 417 245 403T234 376T215 353T186 329Q207 311 215 295T223 257Q223 241 220 220L195 37Q193 23 192 12T190 -9Q190 -31 200 -40T236 -50H256L250 -94H217Z" />
+<glyph unicode="|" horiz-adv-x="216" d="M51 -200L182 740H234L103 -200H51Z" />
+<glyph unicode="}" horiz-adv-x="313" d="M277 300Q241 293 229 270T211 216L185 35Q181 7 175 -16T156 -57T123 -84T69 -94H35L41 -50H63Q82 -50 93 -46T111 -31T121 -4T128 37L153 220Q156 240 160 255T171 282T189 306T219 329Q197 347 189 363T181 401Q181 416 184 437L210 622Q212 634 213 645T215 668Q215 686 206 697T171 709H148L154 752H189Q238 752 255 729T272 671Q272 660 271 649T268 624L243 442Q241 426 241 413T245 388T258 369T285 357L277 300H277Z" />
+<glyph unicode="~" horiz-adv-x="382" d="M458 622Q423 558 353 558Q332 558 313 564T276 577T240 591T202 597Q181 597 165 589T134 556L97 574Q122 609 146 623T206 638Q227 638 245 632T282 619T317 605T354 599Q374 599 390 608T419 638L458 622H458Z" />
+<glyph unicode="&#xA0;" horiz-adv-x="300" />
+<glyph unicode="&#xA1;" horiz-adv-x="271" d="M88 -162Q110 -38 130 83T170 329H218Q203 205 189 84T161 -162H88ZM164 458Q164 482 179 498T220 515Q241 515 252 505T263 476Q263 452 248 436T207 420Q164 420 164 458Z" />
+<glyph unicode="&#xA2;" horiz-adv-x="554" d="M491 142Q457 116 419 99T338 77L328 0H270L280 78Q237 82 207 99T158 142T131 202T122 277Q122 331 135 385T176 485T248 560T354 598L363 667H421L412 599Q451 597 484 582T547 542L514 497Q465 551 385 551Q331 551 294 528T233 466T198 380T187 283Q187 250 193 222T216 172T257 139T321 126Q396 126 469 184L491 142H491Z" />
+<glyph unicode="&#xA3;" horiz-adv-x="554" d="M488 0H66L73 52H90Q125 52 148 69T186 113T206 174T212 241Q212 291 206 336H129L136 385H202Q200 417 197 445T193 499Q193 579 242 628T385 678Q427 678 468 666T542 631L516 580Q490 600 459 612T385 625Q316 625 286 592T255 487Q255 461 257 436T262 385H409L402 336H264Q267 312 269 289T272 241Q272 216 269 191T258 141T236 95T200 57L199 54H495L488 0Z" />
+<glyph unicode="&#xA4;" horiz-adv-x="554" d="M124 312Q124 354 140 392T185 460L129 525L168 559L224 493Q252 513 284 524T351 536Q383 536 412 525T466 493L541 559L570 525L495 459Q509 439 515 416T522 369Q522 283 460 218L518 151L479 117L421 184Q394 164 362 154T296 143Q264 143 235 153T181 184L106 118L77 152L152 218Q138 238 131 262T124 312ZM343 482Q311 482 282 469T230 434T194 382T180 320Q180 294 189 272T215 233T254 207T303 197Q335 197 364 210T416 246T452 298T466 360Q466 386 457 408T431 447T392 473T343 482Z" />
+<glyph unicode="&#xA5;" horiz-adv-x="554" d="M400 343H532L525 298H367L346 267L336 199H511L505 155H330L309 0H247L268 155H94L100 199H274L284 264L269 298H114L121 343H250L129 667H199L323 320L543 667H611L400 343Z" />
+<glyph unicode="&#xA6;" horiz-adv-x="216" d="M123 323L182 740H234L175 323H123ZM51 -200L109 217H161L103 -200H51Z" />
+<glyph unicode="&#xA7;" horiz-adv-x="483" d="M452 316Q452 277 439 250T402 205T347 180T280 170V166Q304 157 329 145T373 117T405 78T418 26Q418 -15 402 -44T360 -91T300 -119T229 -128Q184 -128 145 -118T67 -84L96 -35Q125 -56 159 -68T230 -80Q255 -80 277 -75T317 -57T343 -26T353 23Q353 50 334 69T286 103T224 132T162 164T114 206T95 265Q95 302 109 328T148 371T202 396T266 406V410Q246 419 225 430T186 457T157 491T146 536Q146 575 163 602T208 646T269 670T336 678Q373 678 407 670T474 647L452 597Q424 611 395 620T333 629Q313 629 291 625T251 612T221 586T209 544Q209 515 227 496T272 463T330 436T389 408T434 371T452 316ZM262 200Q289 200 313 204T354 221T382 253T392 304Q392 326 383 339T358 360T323 370T286 372Q260 372 236 368T194 352T165 321T154 270Q154 248 164 234T190 213T225 203T262 200Z" />
+<glyph unicode="&#xA8;" horiz-adv-x="362" d="M392 620Q392 601 378 587T345 573Q330 573 320 583T310 609Q310 627 324 641T357 655Q372 655 382 645T392 620ZM223 619Q223 601 209 587T176 573Q161 573 151 583T141 609Q141 627 155 641T188 655Q203 655 213 645T223 619Z" />
+<glyph unicode="&#xA9;" horiz-adv-x="906" d="M799 334Q799 262 772 200T698 91T588 17T453 -10Q381 -10 318 17T208 90T134 199T107 334Q107 406 134 468T208 577T318 651T453 678Q525 678 588 651T698 578T772 469T799 334ZM766 334Q766 398 742 454T676 554T577 622T453 647Q386 647 329 622T230 554T165 455T141 334Q141 270 166 214T233 115T332 48T453 23Q517 23 573 47T673 114T741 213T766 334ZM614 173Q582 145 545 129T460 112Q402 112 364 128T304 173T272 243T263 334Q263 385 273 425T307 494T369 538T464 553Q510 553 546 538T615 494L592 458Q566 483 535 497T465 511Q418 511 389 499T343 463T320 408T313 335Q313 294 319 261T341 203T386 166T461 153Q499 153 530 168T591 210L614 173Z" />
+<glyph unicode="&#xAA;" horiz-adv-x="377" d="M340 370Q328 375 320 381T310 402H306Q283 386 256 376T201 366Q166 366 143 384T120 440Q120 482 140 508T192 548T261 567T331 572Q332 580 333 588T334 604Q334 625 321 633T281 642Q266 642 249 639T216 631T187 621T165 610L153 643Q186 660 219 670T291 680Q329 680 354 666T380 609Q380 586 377 563T369 516T361 470T357 424Q357 416 361 412T371 403L340 370ZM327 542Q304 542 276 539T224 525T183 496T166 447Q166 425 179 412T215 399Q241 399 265 410T312 435L327 542Z" />
+<glyph unicode="&#xAB;" horiz-adv-x="430" d="M355 117L235 309L409 502L447 476L293 309L400 144L355 117ZM187 114L64 309L240 505L279 479L123 309L231 141L187 114Z" />
+<glyph unicode="&#xAC;" horiz-adv-x="554" d="M478 231H86L94 285H540L506 49H452L478 231Z" />
+<glyph unicode="&#xAE;" horiz-adv-x="479" d="M226 361Q190 361 158 374T102 411T65 464T51 530Q51 565 64 596T102 650T158 686T226 700Q262 700 293 687T349 650T386 596T400 530Q400 495 387 465T349 411T294 375T226 361ZM227 676Q195 676 168 664T120 632T88 586T76 530Q76 501 88 475T121 429T169 397T226 385Q256 385 283 396T331 427T364 473T376 530Q376 559 365 585T333 632T285 664T227 676ZM164 623Q184 625 199 626T228 627Q256 627 279 615T302 568Q302 551 294 540T272 519L306 439H268L241 503Q239 508 238 509T230 510H197V439H164V623ZM229 596Q221 596 214 596T198 595V538H231Q247 538 258 545T269 566Q269 583 259 589T229 596Z" />
+<glyph unicode="&#xAF;" horiz-adv-x="362" d="M119 590L125 632H410L404 590H119Z" />
+<glyph unicode="&#xB0;" horiz-adv-x="389" d="M411 560Q411 528 399 500T365 452T314 419T251 407Q225 407 203 415T165 439T139 475T129 522Q129 553 140 581T172 631T223 664T290 677Q315 677 337 669T376 644T401 607T411 560ZM256 439Q310 439 342 473T374 556Q374 595 349 620T285 645Q258 645 236 636T198 610T174 573T165 528Q165 489 191 464T256 439Z" />
+<glyph unicode="&#xB1;" horiz-adv-x="554" d="M343 261L319 92H264L288 261H90L98 315H296L323 506H378L351 315H544L536 261H343ZM54 0L61 53H507L500 0H54Z" />
+<glyph unicode="&#xB2;" horiz-adv-x="299" d="M94 367L92 411Q111 426 145 449T212 500T269 558T293 616Q293 639 279 650T244 662Q217 662 196 649T155 619L133 649Q160 672 189 686T254 700Q289 700 314 682T340 626Q340 591 319 560T266 500T201 448T141 406H315L309 367H94Z" />
+<glyph unicode="&#xB3;" horiz-adv-x="292" d="M317 479Q317 420 280 391T187 361Q158 361 133 369T85 396L110 430Q127 415 146 407T189 398Q225 398 247 417T270 475Q270 490 264 499T249 512T227 518T202 520Q194 520 186 520T171 519L177 555Q195 555 213 556T247 564T272 584T282 620Q282 644 266 653T229 662Q183 662 145 632L128 665Q155 682 181 691T239 700Q275 700 302 684T329 627Q329 598 313 576T269 544V540Q317 528 317 479Z" />
+<glyph unicode="&#xB4;" horiz-adv-x="362" d="M198 565L170 589L322 704L375 671L198 565Z" />
+<glyph unicode="&#xB5;" horiz-adv-x="568" d="M423 0L424 65H420Q408 50 394 37T362 13T320 -4T265 -11Q248 -11 232 -9T201 -1T175 16T157 44L120 -220H65L166 502H226L189 238Q185 209 183 185T180 141Q180 91 200 66T278 41Q355 41 392 81T438 188L482 502H542L472 0H423Z" />
+<glyph unicode="&#xB6;" horiz-adv-x="473" d="M348 0Q297 0 250 11T167 48T109 114T87 212Q87 282 114 336T187 427T292 483T418 502H454L384 0H348Z" />
+<glyph unicode="&#xB7;" horiz-adv-x="184" d="M173 244Q173 219 158 203T117 187Q99 187 88 196T76 225Q76 251 90 266T131 282Q150 282 161 273T173 244Z" />
+<glyph unicode="&#xB8;" horiz-adv-x="362" d="M252 -110Q252 -157 223 -180T150 -203Q129 -203 109 -197T71 -179L89 -151Q103 -159 117 -164T149 -169Q175 -169 193 -158T212 -116Q212 -96 197 -89T163 -82Q141 -82 126 -88L115 -72L165 0H204L165 -53Q170 -52 175 -52T186 -52Q212 -52 232 -63T252 -110Z" />
+<glyph unicode="&#xB9;" horiz-adv-x="268" d="M175 405L209 641H205L130 599L117 633L225 694H261L220 405H301L296 367H87L92 405H175Z" />
+<glyph unicode="&#xBA;" horiz-adv-x="377" d="M401 558Q401 521 391 487T361 425T311 382T240 366Q183 366 153 398T122 488Q122 525 132 559T162 621T213 664T284 680Q341 680 371 647T401 558ZM245 405Q277 405 298 419T332 455T350 504T355 559Q355 598 337 619T278 640Q246 640 225 627T191 591T173 542T168 487Q168 447 186 426T245 405Z" />
+<glyph unicode="&#xBB;" horiz-adv-x="429" d="M275 114L237 141L392 309L284 479L329 505L451 309L275 114ZM106 117L69 144L222 309L115 476L160 502L281 309L106 117Z" />
+<glyph unicode="&#xBC;" horiz-adv-x="718" d="M210 405L244 641H240L165 599L152 633L260 694H296L255 405H336L331 367H122L127 405H210ZM181 0H139L708 700H750L181 0ZM641 70L632 0H585L594 70H452L445 108L623 327H677L647 109H697L691 70H641ZM489 107H601L625 279L489 107Z" />
+<glyph unicode="&#xBD;" horiz-adv-x="690" d="M200 405L234 641H230L155 599L142 633L250 694H286L245 405H326L321 367H112L117 405H200ZM138 0H96L665 700H707L138 0ZM430 0L428 44Q454 65 489 89T555 140T607 193T629 246Q629 266 619 280T579 295Q556 295 536 285T491 252L468 282Q499 309 527 321T589 333Q629 333 652 313T676 258Q676 225 656 195T605 136T540 84T476 39H650L645 0H430Z" />
+<glyph unicode="&#xBE;" horiz-adv-x="682" d="M351 479Q351 420 315 391T222 361Q193 361 168 369T120 396L144 430Q179 398 224 398Q259 398 282 417T305 474Q305 504 285 511T240 519H206L211 555Q228 555 246 556T281 564T307 583T317 619Q317 643 301 652T263 662Q218 662 180 632L162 665Q190 682 215 691T273 700Q291 700 307 697T335 685T355 662T363 628Q363 599 348 577T303 544V540Q351 528 351 479ZM161 0H119L688 700H730L161 0ZM612 70L603 0H556L565 70H423L416 108L594 327H648L618 109H668L662 70H612ZM460 107H572L596 279L460 107Z" />
+<glyph unicode="&#xBF;" horiz-adv-x="499" d="M81 -32Q81 26 99 62T146 121T208 159T272 187T323 220T349 271L359 344H420L408 262Q402 221 380 197T329 158T268 131T208 101T163 55T145 -23Q145 -50 155 -69T182 -99T222 -116T268 -121Q315 -121 356 -104T435 -61L459 -107Q412 -137 362 -155T255 -173Q222 -173 191 -165T135 -140T96 -97T81 -32ZM360 459Q360 483 374 499T414 515Q431 515 443 506T455 478Q455 453 441 438T401 422Q383 422 372 431T360 459Z" />
+<glyph unicode="&#xC0;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM424 705L277 809L341 841L458 727L424 705Z" />
+<glyph unicode="&#xC1;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM389 705L361 729L513 844L566 811L389 705Z" />
+<glyph unicode="&#xC2;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM517 704L413 786L288 704L266 739L421 838L550 739L517 704Z" />
+<glyph unicode="&#xC3;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM566 774Q552 742 525 727T471 712Q455 712 439 718T407 731T376 744T348 750Q332 750 315 743T286 711L254 734Q275 770 300 783T351 796Q367 796 383 790T415 777T445 764T474 758Q491 758 506 766T533 796L566 774H566Z" />
+<glyph unicode="&#xC4;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM536 760Q536 741 522 727T489 713Q474 713 464 723T454 749Q454 767 468 781T501 795Q516 795 526 785T536 760ZM367 759Q367 741 353 727T320 713Q305 713 295 723T285 749Q285 767 299 781T332 795Q347 795 357 785T367 759Z" />
+<glyph unicode="&#xC5;" horiz-adv-x="613" d="M510 744Q510 712 491 684T443 642L585 0H518L482 171H177L93 0H28L347 641Q325 652 314 672T302 715Q302 739 311 759T335 795T373 819T421 828Q462 828 486 805T510 744ZM393 614H389L199 225H471L393 614ZM398 665Q413 665 426 671T449 687T465 711T471 739Q471 762 455 777T416 793Q384 793 363 772T342 720Q342 696 358 681T398 665Z" />
+<glyph unicode="&#xC6;" horiz-adv-x="868" d="M449 0L473 171H214L94 0H25L504 667H913L906 613H598L566 385H844L836 330H558L519 54H837L830 0H449ZM535 610H527L248 226H481L535 610Z" />
+<glyph unicode="&#xC7;" horiz-adv-x="614" d="M421 -113Q421 -152 394 -178T318 -205Q296 -205 275 -199T240 -183L259 -154Q272 -162 286 -167T321 -173Q347 -173 363 -160T380 -120Q380 -100 366 -93T332 -86Q313 -86 295 -91L284 -76L329 -10Q220 -3 165 64T109 251Q109 329 129 405T193 542T301 640T456 678Q516 678 564 658T658 596L614 552Q574 590 536 607T449 625Q377 625 326 592T241 507T192 389T176 259Q176 216 185 177T215 107T273 60T364 42Q410 42 460 60T561 120L590 76Q532 31 479 11T366 -11L332 -58L334 -61Q347 -56 360 -56Q389 -56 405 -70T421 -113Z" />
+<glyph unicode="&#xC8;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM425 705L278 809L342 841L459 727L425 705Z" />
+<glyph unicode="&#xC9;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM353 705L325 729L477 844L530 811L353 705Z" />
+<glyph unicode="&#xCA;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM492 704L388 786L263 704L241 739L396 838L525 739L492 704Z" />
+<glyph unicode="&#xCB;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM511 760Q511 741 497 727T464 713Q449 713 439 723T429 749Q429 767 443 781T476 795Q491 795 501 785T511 760ZM342 759Q342 741 328 727T295 713Q280 713 270 723T260 749Q260 767 274 781T307 795Q322 795 332 785T342 759Z" />
+<glyph unicode="&#xCC;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM243 705L96 809L160 841L277 727L243 705Z" />
+<glyph unicode="&#xCD;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM207 705L179 729L331 844L384 811L207 705Z" />
+<glyph unicode="&#xCE;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM340 704L236 786L111 704L89 739L244 838L373 739L340 704Z" />
+<glyph unicode="&#xCF;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM356 760Q356 741 342 727T309 713Q294 713 284 723T274 749Q274 767 288 781T321 795Q336 795 346 785T356 760ZM187 759Q187 741 173 727T140 713Q125 713 115 723T105 749Q105 767 119 781T152 795Q167 795 177 785T187 759Z" />
+<glyph unicode="&#xD0;" horiz-adv-x="650" d="M633 413Q633 338 613 264T551 132T441 37T279 0H92L138 332H78L86 385H146L185 664Q206 666 231 668T281 671T330 674T373 675Q442 675 491 656T571 603T618 520T633 413ZM270 53Q351 53 408 81T500 157T552 270T568 409Q568 449 559 487T528 555T467 602T370 620Q337 620 304 619T241 614L209 385H365L357 332H201L162 53H270Z" />
+<glyph unicode="&#xD1;" horiz-adv-x="684" d="M530 0L236 560H232L154 0H92L185 667H252L541 110H545L623 667H685L592 0H530ZM614 774Q600 742 573 727T519 712Q503 712 487 718T455 731T424 744T396 750Q380 750 363 743T334 711L302 734Q323 770 348 783T399 796Q415 796 431 790T463 777T493 764T522 758Q539 758 554 766T581 796L614 774H614Z" />
+<glyph unicode="&#xD2;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM523 706L376 810L440 842L557 728L523 706Z" />
+<glyph unicode="&#xD3;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM441 706L413 730L565 845L618 812L441 706Z" />
+<glyph unicode="&#xD4;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM583 704L479 786L354 704L332 739L487 838L616 739L583 704Z" />
+<glyph unicode="&#xD5;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM624 775Q610 743 583 728T529 713Q513 713 497 719T465 732T434 745T406 751Q390 751 373 744T344 712L312 735Q333 771 358 784T409 797Q425 797 441 791T473 778T503 765T532 759Q549 759 564 767T591 797L624 775H624Z" />
+<glyph unicode="&#xD6;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM595 760Q595 741 581 727T548 713Q533 713 523 723T513 749Q513 767 527 781T560 795Q575 795 585 785T595 760ZM426 759Q426 741 412 727T379 713Q364 713 354 723T344 749Q344 767 358 781T391 795Q406 795 416 785T426 759Z" />
+<glyph unicode="&#xD7;" horiz-adv-x="554" d="M517 400L350 254L481 102L437 64L307 216L132 64L101 102L275 254L150 399L193 438L319 292L485 438L517 400Z" />
+<glyph unicode="&#xD8;" horiz-adv-x="732" d="M715 415Q715 334 695 257T632 121T523 25T367 -11Q319 -11 272 1T188 45L128 -23L95 12L156 81Q129 119 120 165T110 256Q110 337 131 413T196 548T306 642T462 678Q507 678 549 668T627 630L682 694L715 661L659 597Q691 560 703 512T715 415ZM372 41Q447 41 499 73T585 158T633 275T648 405Q648 443 642 483T614 554L232 91Q260 64 295 53T372 41ZM454 627Q380 627 328 595T242 511T193 395T177 265Q177 230 183 196T207 130L584 585Q556 609 523 618T454 627Z" />
+<glyph unicode="&#xD9;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM437 705L290 809L354 841L471 727L437 705Z" />
+<glyph unicode="&#xDA;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM416 705L388 729L540 844L593 811L416 705Z" />
+<glyph unicode="&#xDB;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM541 704L437 786L312 704L290 739L445 838L574 739L541 704Z" />
+<glyph unicode="&#xDC;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM558 760Q558 741 544 727T511 713Q496 713 486 723T476 749Q476 767 490 781T523 795Q538 795 548 785T558 760ZM389 759Q389 741 375 727T342 713Q327 713 317 723T307 749Q307 767 321 781T354 795Q369 795 379 785T389 759Z" />
+<glyph unicode="&#xDD;" horiz-adv-x="533" d="M336 267L299 0H235L272 264L111 667H181L312 323L539 667H608L336 267ZM357 701L329 725L481 840L534 807L357 701Z" />
+<glyph unicode="&#xDE;" horiz-adv-x="585" d="M578 389Q578 334 564 288T520 208T445 155T338 136H174L155 0H92L185 667H247L230 542Q267 545 298 546T364 548Q413 548 452 541T520 515T563 466T578 389ZM329 188Q424 190 468 234T513 380Q513 413 503 435T473 470T423 488T354 494Q319 494 289 493T223 487L181 188H329Z" />
+<glyph unicode="&#xDF;" horiz-adv-x="502" d="M483 195Q483 153 470 116T431 50T370 6T291 -11Q274 -11 250 -9T209 2L226 48Q241 43 256 40T288 36Q321 36 346 48T387 81T412 129T420 189Q420 222 408 247T375 293T329 327T276 354L274 397Q301 413 327 431T373 472T406 522T419 585Q419 626 395 642T332 659Q275 659 250 629T217 545L141 0H82L159 550Q181 705 341 705Q369 705 393 699T435 680T463 646T474 594Q474 557 464 528T434 473T390 428T335 389V385Q365 371 392 354T439 313T471 260T483 195Z" />
+<glyph unicode="&#xE0;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM375 565L228 669L292 701L409 587L375 565Z" />
+<glyph unicode="&#xE1;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM305 565L277 589L429 704L482 671L305 565Z" />
+<glyph unicode="&#xE2;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM446 563L342 645L217 563L195 598L350 697L479 598L446 563Z" />
+<glyph unicode="&#xE3;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM491 634Q477 602 450 587T396 572Q380 572 364 578T332 591T301 604T273 610Q257 610 240 603T211 571L179 594Q200 630 225 643T276 656Q292 656 308 650T340 637T370 624T399 618Q416 618 431 626T458 656L491 634H491Z" />
+<glyph unicode="&#xE4;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM471 620Q471 601 457 587T424 573Q409 573 399 583T389 609Q389 627 403 641T436 655Q451 655 461 645T471 620ZM302 619Q302 601 288 587T255 573Q240 573 230 583T220 609Q220 627 234 641T267 655Q282 655 292 645T302 619Z" />
+<glyph unicode="&#xE5;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM439 654Q439 634 432 617T411 587T379 566T339 558Q307 558 285 577T262 630Q262 650 270 667T291 698T323 718T363 726Q395 726 417 707T439 654ZM343 587Q371 587 388 605T406 650Q406 670 394 683T359 697Q330 697 313 679T295 634Q295 614 307 601T343 587Z" />
+<glyph unicode="&#xE6;" horiz-adv-x="840" d="M827 326Q827 303 825 279T817 232H464Q461 209 461 188Q461 120 494 79T599 38Q641 38 681 53T754 95L772 51Q733 22 688 6T593 -11Q539 -11 493 9T421 81Q373 41 319 15T201 -11Q143 -11 105 20T66 113Q66 159 82 192T124 249T186 287T260 309T338 319T413 322Q415 339 417 356T420 390Q420 435 398 450T334 466Q284 466 235 449T143 405L128 451Q179 479 230 497T341 515Q367 515 390 511T431 495T460 465T473 416Q506 463 553 489T657 515Q701 515 733 501T786 463T817 403T827 326ZM767 277Q770 301 770 325Q770 356 764 382T743 427T705 456T648 467Q609 467 578 451T524 407T487 346T468 277H767ZM407 116Q402 131 400 148T398 181Q398 230 407 280Q369 280 321 276T230 255T158 209T128 127Q128 80 153 56T219 32Q247 32 273 39T322 59T367 86T407 116Z" />
+<glyph unicode="&#xE7;" horiz-adv-x="482" d="M348 -114Q348 -153 321 -179T244 -205Q222 -205 201 -199T166 -183L184 -154Q198 -163 212 -168T247 -174Q257 -174 267 -172T287 -163T301 -146T307 -121Q307 -101 293 -94T259 -86Q239 -86 222 -91L211 -75L255 -10Q211 -7 180 9T128 53T98 115T88 192Q88 251 104 309T154 412T237 486T355 515Q402 515 441 499T512 456L479 411Q456 437 424 451T350 466Q299 466 261 443T199 383T162 297T150 198Q150 166 157 137T180 86T223 51T290 38Q331 38 365 51T437 94L453 50Q415 24 377 8T292 -11L258 -59L260 -62Q266 -60 272 -59T285 -58Q314 -58 331 -71T348 -114Z" />
+<glyph unicode="&#xE8;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM382 565L235 669L299 701L416 587L382 565Z" />
+<glyph unicode="&#xE9;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM334 565L306 589L458 704L511 671L334 565Z" />
+<glyph unicode="&#xEA;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM468 564L364 646L239 564L217 599L372 698L501 599L468 564Z" />
+<glyph unicode="&#xEB;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM485 620Q485 601 471 587T438 573Q423 573 413 583T403 609Q403 627 417 641T450 655Q465 655 475 645T485 620ZM316 619Q316 601 302 587T269 573Q254 573 244 583T234 609Q234 627 248 641T281 655Q296 655 306 645T316 619Z" />
+<glyph unicode="&#xEC;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM223 565L76 669L140 701L257 587L223 565Z" />
+<glyph unicode="&#xED;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM166 565L138 589L290 704L343 671L166 565Z" />
+<glyph unicode="&#xEE;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM308 564L204 646L79 564L57 599L212 698L341 599L308 564Z" />
+<glyph unicode="&#xEF;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM326 620Q326 601 312 587T279 573Q264 573 254 583T244 609Q244 627 258 641T291 655Q306 655 316 645T326 620ZM157 619Q157 601 143 587T110 573Q95 573 85 583T75 609Q75 627 89 641T122 655Q137 655 147 645T157 619Z" />
+<glyph unicode="&#xF0;" horiz-adv-x="532" d="M467 619Q496 565 508 509T520 392Q520 348 516 302T502 210T473 125T425 55T356 8T260 -10Q214 -10 182 4T128 44T97 105T87 184Q87 237 98 292T136 393T210 468T327 497Q371 497 405 483T460 426H464Q461 471 448 514T415 596L303 540L290 574L396 628Q384 646 371 663T344 698L398 711Q412 697 424 682T447 651L536 700L550 665L467 619ZM272 38Q327 38 362 66T418 136T445 228T453 322Q453 387 424 419T327 452Q273 452 238 428T183 364T156 278T148 186Q148 154 154 127T175 81T213 50T272 38Z" />
+<glyph unicode="&#xF1;" horiz-adv-x="548" d="M407 0L449 301Q455 338 455 368Q455 390 451 407T434 437T402 456T351 463Q280 463 238 426T186 323L141 0H82L152 502H203L200 437H204Q264 515 369 515Q411 515 439 507T484 482T509 442T516 387Q516 356 510 319L466 0H407ZM519 634Q505 602 478 587T424 572Q408 572 392 578T360 591T329 604T301 610Q285 610 268 603T239 571L207 594Q228 630 253 643T304 656Q320 656 336 650T368 637T398 624T427 618Q444 618 459 626T486 656L519 634H519Z" />
+<glyph unicode="&#xF2;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM390 565L243 669L307 701L424 587L390 565Z" />
+<glyph unicode="&#xF3;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM335 565L307 589L459 704L512 671L335 565Z" />
+<glyph unicode="&#xF4;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM474 564L370 646L245 564L223 599L378 698L507 599L474 564Z" />
+<glyph unicode="&#xF5;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM524 634Q510 602 483 587T429 572Q413 572 397 578T365 591T334 604T306 610Q290 610 273 603T244 571L212 594Q233 630 258 643T309 656Q325 656 341 650T373 637T403 624T432 618Q449 618 464 626T491 656L524 634H524Z" />
+<glyph unicode="&#xF6;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM494 620Q494 601 480 587T447 573Q432 573 422 583T412 609Q412 627 426 641T459 655Q474 655 484 645T494 620ZM325 619Q325 601 311 587T278 573Q263 573 253 583T243 609Q243 627 257 641T290 655Q305 655 315 645T325 619Z" />
+<glyph unicode="&#xF7;" horiz-adv-x="554" d="M86 231L94 285H540L532 231H86ZM254 105Q254 124 268 137T300 151Q315 151 325 142T335 117Q335 98 321 85T288 71Q273 71 264 81T254 105ZM295 399Q295 418 308 432T341 446Q356 446 366 437T377 411Q377 392 363 379T330 366Q316 366 306 375T295 399Z" />
+<glyph unicode="&#xF8;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q432 515 480 473L540 537L572 509L507 441Q540 390 540 311Q540 250 525 192T477 89T395 17T277 -11Q228 -11 193 3T133 45L75 -16L47 12L110 80Q88 124 88 190ZM478 312Q478 361 464 395L174 88Q190 63 216 50T285 36Q340 36 377 62T437 128T468 217T478 312ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 154 157 129L441 432Q424 449 401 458T344 467Z" />
+<glyph unicode="&#xF9;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM357 565L210 669L274 701L391 587L357 565Z" />
+<glyph unicode="&#xFA;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM337 565L309 589L461 704L514 671L337 565Z" />
+<glyph unicode="&#xFB;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM466 564L362 646L237 564L215 599L370 698L499 599L466 564Z" />
+<glyph unicode="&#xFC;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM488 620Q488 601 474 587T441 573Q426 573 416 583T406 609Q406 627 420 641T453 655Q468 655 478 645T488 620ZM319 619Q319 601 305 587T272 573Q257 573 247 583T237 609Q237 627 251 641T284 655Q299 655 309 645T319 619Z" />
+<glyph unicode="&#xFD;" horiz-adv-x="466" d="M217 -88Q197 -128 180 -154T144 -197T105 -219T57 -226Q49 -226 37 -226T14 -224L21 -172Q30 -174 38 -174T58 -174Q77 -174 91 -169T118 -151T145 -113T178 -49L211 21L93 502H157L250 89H254L450 502H513L217 -88ZM312 565L284 589L436 704L489 671L312 565Z" />
+<glyph unicode="&#xFE;" horiz-adv-x="549" d="M532 327Q532 271 520 212T478 103T403 22T287 -10Q233 -10 203 13T154 72H150L109 -220H51L180 700H236L199 440H203Q238 482 278 498T364 515Q412 515 444 501T496 461T524 401T532 327ZM287 37Q341 37 376 65T433 137T462 232T471 329Q471 358 466 383T446 426T409 455T348 466Q283 466 240 432T187 330L168 199Q167 191 167 184T166 169Q166 111 196 74T287 37Z" />
+<glyph unicode="&#xFF;" horiz-adv-x="466" d="M217 -88Q197 -128 180 -154T144 -197T105 -219T57 -226Q49 -226 37 -226T14 -224L21 -172Q30 -174 38 -174T58 -174Q77 -174 91 -169T118 -151T145 -113T178 -49L211 21L93 502H157L250 89H254L450 502H513L217 -88ZM453 620Q453 601 439 587T406 573Q391 573 381 583T371 609Q371 627 385 641T418 655Q433 655 443 645T453 620ZM284 619Q284 601 270 587T237 573Q222 573 212 583T202 609Q202 627 216 641T249 655Q264 655 274 645T284 619Z" />
+<glyph unicode="&#x100;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM262 730L268 772H553L547 730H262Z" />
+<glyph unicode="&#x101;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM192 590L198 632H483L477 590H192Z" />
+<glyph unicode="&#x102;" horiz-adv-x="613" d="M518 0L482 171H177L93 0H28L362 671H437L585 0H518ZM391 611H389L199 225H471L391 611ZM552 818Q544 787 529 766T494 731T450 711T402 705Q378 705 356 711T318 730T293 765T286 818H328Q326 784 345 763T407 741Q449 741 475 762T509 818H552H552Z" />
+<glyph unicode="&#x103;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM487 678Q479 647 464 626T429 591T385 571T337 565Q313 565 291 571T253 590T228 625T221 678H263Q261 644 280 623T342 601Q384 601 410 622T444 678H487H487Z" />
+<glyph unicode="&#x104;" horiz-adv-x="613" d="M551 0Q532 -11 511 -24T472 -55T443 -94T431 -143Q431 -170 447 -183T490 -196Q510 -196 525 -191T551 -179L560 -216Q549 -222 525 -229T478 -237Q427 -237 405 -216T383 -156Q383 -128 394 -104T423 -59T466 -23T516 5L482 171H177L93 0H28L362 671H437L585 0H551ZM392 611H387L199 225H471L392 611Z" />
+<glyph unicode="&#x105;" horiz-adv-x="550" d="M455 0L408 -28Q381 -44 358 -72T335 -138Q335 -165 351 -180T394 -196Q414 -196 429 -191T454 -179L464 -216Q453 -222 429 -229T381 -237Q331 -237 309 -213T287 -151Q287 -123 297 -100T326 -57T367 -23T417 4L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L484 502H539L468 0H455ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331Z" />
+<glyph unicode="&#x106;" horiz-adv-x="614" d="M590 76Q529 28 472 9T352 -11Q231 -11 170 57T109 251Q109 328 129 404T193 541T301 640T456 678Q516 678 564 658T658 596L614 552Q574 590 536 607T449 625Q377 625 326 593T241 508T192 391T176 259Q176 216 185 177T215 107T273 60T364 42Q410 42 460 60T561 120L590 76H590ZM433 710L405 734L557 849L610 816L433 710Z" />
+<glyph unicode="&#x107;" horiz-adv-x="482" d="M479 411Q456 437 424 451T350 466Q297 466 259 442T197 380T162 294T150 198Q150 166 157 137T180 86T223 51T290 38Q331 38 365 51T437 94L453 50Q413 21 372 5T280 -11Q230 -11 194 4T134 47T99 111T88 193Q88 248 103 305T150 409T233 485T355 515Q402 515 441 499T512 456L479 411ZM330 565L302 589L454 704L507 671L330 565Z" />
+<glyph unicode="&#x108;" horiz-adv-x="614" d="M590 76Q529 28 472 9T352 -11Q231 -11 170 57T109 251Q109 328 129 404T193 541T301 640T456 678Q516 678 564 658T658 596L614 552Q574 590 536 607T449 625Q377 625 326 593T241 508T192 391T176 259Q176 216 185 177T215 107T273 60T364 42Q410 42 460 60T561 120L590 76H590ZM572 705L468 787L343 705L321 740L476 839L605 740L572 705Z" />
+<glyph unicode="&#x109;" horiz-adv-x="482" d="M479 411Q456 437 424 451T350 466Q297 466 259 442T197 380T162 294T150 198Q150 166 157 137T180 86T223 51T290 38Q331 38 365 51T437 94L453 50Q413 21 372 5T280 -11Q230 -11 194 4T134 47T99 111T88 193Q88 248 103 305T150 409T233 485T355 515Q402 515 441 499T512 456L479 411ZM472 564L368 646L243 564L221 599L376 698L505 599L472 564Z" />
+<glyph unicode="&#x10A;" horiz-adv-x="614" d="M590 76Q529 28 472 9T352 -11Q231 -11 170 57T109 251Q109 328 129 404T193 541T301 640T456 678Q516 678 564 658T658 596L614 552Q574 590 536 607T449 625Q377 625 326 593T241 508T192 391T176 259Q176 216 185 177T215 107T273 60T364 42Q410 42 460 60T561 120L590 76H590ZM511 770Q511 750 499 737T465 724Q451 724 442 732T432 756Q432 775 444 788T476 801Q491 801 501 794T511 770Z" />
+<glyph unicode="&#x10B;" horiz-adv-x="482" d="M479 411Q456 437 424 451T350 466Q297 466 259 442T197 380T162 294T150 198Q150 166 157 137T180 86T223 51T290 38Q331 38 365 51T437 94L453 50Q413 21 372 5T280 -11Q230 -11 194 4T134 47T99 111T88 193Q88 248 103 305T150 409T233 485T355 515Q402 515 441 499T512 456L479 411ZM407 632Q407 612 395 599T361 586Q347 586 338 594T328 618Q328 637 340 650T372 663Q387 663 397 656T407 632Z" />
+<glyph unicode="&#x10C;" horiz-adv-x="614" d="M590 76Q529 28 472 9T352 -11Q231 -11 170 57T109 251Q109 328 129 404T193 541T301 640T456 678Q516 678 564 658T658 596L614 552Q574 590 536 607T449 625Q377 625 326 593T241 508T192 391T176 259Q176 216 185 177T215 107T273 60T364 42Q410 42 460 60T561 120L590 76H590ZM604 812L458 714L341 812L372 846L465 766L581 846L604 812Z" />
+<glyph unicode="&#x10D;" horiz-adv-x="482" d="M479 411Q456 437 424 451T350 466Q297 466 259 442T197 380T162 294T150 198Q150 166 157 137T180 86T223 51T290 38Q331 38 365 51T437 94L453 50Q413 21 372 5T280 -11Q230 -11 194 4T134 47T99 111T88 193Q88 248 103 305T150 409T233 485T355 515Q402 515 441 499T512 456L479 411ZM508 663L362 565L245 663L276 697L369 617L485 697L508 663Z" />
+<glyph unicode="&#x10E;" horiz-adv-x="650" d="M633 412Q633 333 612 259T548 127T439 35T282 0H92L185 664Q223 669 270 672T373 675Q440 675 489 656T570 603T617 520T633 412ZM274 53Q352 53 407 80T498 156T550 268T567 407Q567 448 558 487T526 556T465 604T367 623Q335 623 303 621T241 616L162 53H274ZM521 803L375 705L258 803L289 837L382 757L498 837L521 803Z" />
+<glyph unicode="&#x10F;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 177Q87 229 97 288T134 397T207 481T327 515Q374 515 408 499T466 438H470L507 700H566L468 0H417ZM455 331Q465 398 429 432T331 467Q274 467 239 440T183 372T156 280T149 184Q149 155 154 128T174 81T211 48T271 35Q305 35 333 48T383 83T418 135T436 197L455 331ZM638 544H596L650 700H719L638 544Z" />
+<glyph unicode="&#x110;" horiz-adv-x="650" d="M633 413Q633 331 612 256T546 124T436 34T279 0H92L138 332H78L86 385H146L185 664Q232 669 278 672T373 675Q501 675 567 608T633 413ZM270 53Q351 53 407 81T499 157T551 270T568 409Q568 507 521 563T370 620Q305 620 241 614L209 385H365L357 332H201L162 53H270Z" />
+<glyph unicode="&#x111;" horiz-adv-x="550" d="M417 0L424 82H420Q389 36 348 13T252 -11Q206 -11 175 3T124 43T96 102T87 178Q87 231 98 289T136 397T210 479T326 511Q373 511 408 496T465 435H469L489 581H321L327 623H495L506 700H566L555 623H624L618 581H549L468 0H417ZM454 327Q464 394 429 429T331 464Q275 464 240 438T184 371T156 280T148 184Q148 154 153 127T173 80T211 47T271 35Q305 35 333 48T383 83T418 135T436 197L454 327Z" />
+<glyph unicode="&#x112;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM238 730L244 772H529L523 730H238Z" />
+<glyph unicode="&#x113;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM213 590L219 632H504L498 590H213Z" />
+<glyph unicode="&#x114;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM528 818Q520 787 505 766T470 731T426 711T378 705Q354 705 332 711T294 730T269 765T262 818H304Q302 784 321 763T383 741Q425 741 451 762T485 818H528H528Z" />
+<glyph unicode="&#x115;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM503 678Q495 647 480 626T445 591T401 571T353 565Q329 565 307 571T269 590T244 625T237 678H279Q277 644 296 623T358 601Q400 601 426 622T460 678H503H503Z" />
+<glyph unicode="&#x116;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM444 769Q444 749 432 736T398 723Q384 723 375 731T365 755Q365 774 377 787T409 800Q424 800 434 793T444 769Z" />
+<glyph unicode="&#x117;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM404 632Q404 612 392 599T358 586Q344 586 335 594T325 618Q325 637 337 650T369 663Q384 663 394 656T404 632Z" />
+<glyph unicode="&#x118;" horiz-adv-x="511" d="M423 0Q404 -11 383 -24T344 -55T314 -94T302 -139Q302 -165 319 -180T362 -196Q393 -196 423 -180L432 -216Q415 -225 392 -231T349 -237Q308 -237 282 -218T256 -154Q256 -126 266 -103T293 -60T332 -26T378 0H92L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H423Z" />
+<glyph unicode="&#x119;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q443 39 428 31T398 14Q379 4 358 -9T318 -40T288 -78T276 -124Q276 -150 292 -166T336 -183Q356 -183 371 -178T397 -166L406 -202Q395 -208 371 -215T323 -223Q273 -223 251 -199T229 -139Q229 -101 251 -70T312 -11L311 -7Q303 -9 293 -10T276 -11Q181 -11 135 41T88 188Q88 203 89 218T93 251Q101 309 122 358T175 441T251 495T349 515Q431 515 472 463T514 326ZM454 275Q456 287 457 299T458 323Q458 353 452 379T432 426T396 457T342 469Q295 469 262 451T207 405T173 343T156 275H454Z" />
+<glyph unicode="&#x11A;" horiz-adv-x="511" d="M92 0L185 667H555L548 612H241L209 385H487L479 330H201L162 54H480L473 0H92ZM529 803L383 705L266 803L297 837L390 757L506 837L529 803Z" />
+<glyph unicode="&#x11B;" horiz-adv-x="527" d="M514 326Q514 303 512 279T504 232H150Q147 208 147 186Q147 116 181 76T286 36Q321 36 358 48T443 93L460 50Q412 16 367 3T277 -11Q182 -11 135 41T88 188Q88 243 102 301T148 406T228 484T349 515Q431 515 472 463T514 326ZM454 276Q456 288 457 300T458 324Q458 354 452 380T432 427T396 458T343 470Q296 470 263 452T208 406T174 344T157 276H454ZM497 663L351 565L234 663L265 697L358 617L474 697L497 663Z" />
+<glyph unicode="&#x11C;" horiz-adv-x="679" d="M365 40Q414 40 451 54T516 95T560 156T583 234L591 287H408L415 340H657L646 262Q626 124 551 57T355 -10Q287 -10 241 11T165 68T122 153T109 256Q109 332 130 407T195 543T304 640T459 678Q524 678 572 656T658 599L616 555Q574 593 535 609T452 625Q378 625 326 593T240 508T191 390T175 256Q175 213 184 174T214 105T272 58T365 40ZM570 704L466 786L341 704L319 739L474 838L603 739L570 704Z" />
+<glyph unicode="&#x11D;" horiz-adv-x="549" d="M457 -65Q449 -122 430 -154T385 -202T328 -222T265 -226Q202 -226 155 -214T65 -178L94 -128Q132 -153 173 -165T263 -178Q296 -178 319 -173T359 -155T384 -120T399 -64L419 83H415Q383 34 345 14T257 -7Q210 -7 178 7T126 45T96 104T87 179Q87 230 97 289T135 398T208 482T327 515Q378 515 413 498T469 433H473L487 502H537L457 -65ZM454 332Q464 399 429 433T330 467Q275 467 240 440T185 372T157 281T149 188Q149 156 155 129T176 82T213 51T272 39Q306 39 334 52T383 86T417 137T435 199L454 332ZM465 564L361 646L236 564L214 599L369 698L498 599L465 564Z" />
+<glyph unicode="&#x11E;" horiz-adv-x="679" d="M365 40Q414 40 451 54T516 95T560 156T583 234L591 287H408L415 340H657L646 262Q626 124 551 57T355 -10Q287 -10 241 11T165 68T122 153T109 256Q109 332 130 407T195 543T304 640T459 678Q524 678 572 656T658 599L616 555Q574 593 535 609T452 625Q378 625 326 593T240 508T191 390T175 256Q175 213 184 174T214 105T272 58T365 40ZM610 824Q602 793 587 772T552 737T508 717T460 711Q436 711 414 717T376 736T351 771T344 824H386Q384 790 403 769T465 747Q507 747 533 768T567 824H610H610Z" />
+<glyph unicode="&#x11F;" horiz-adv-x="549" d="M457 -65Q449 -122 430 -154T385 -202T328 -222T265 -226Q202 -226 155 -214T65 -178L94 -128Q132 -153 173 -165T263 -178Q296 -178 319 -173T359 -155T384 -120T399 -64L419 83H415Q383 34 345 14T257 -7Q210 -7 178 7T126 45T96 104T87 179Q87 230 97 289T135 398T208 482T327 515Q378 515 413 498T469 433H473L487 502H537L457 -65ZM454 332Q464 399 429 433T330 467Q275 467 240 440T185 372T157 281T149 188Q149 156 155 129T176 82T213 51T272 39Q306 39 334 52T383 86T417 137T435 199L454 332ZM506 678Q498 647 483 626T448 591T404 571T356 565Q332 565 310 571T272 590T247 625T240 678H282Q280 644 299 623T361 601Q403 601 429 622T463 678H506H506Z" />
+<glyph unicode="&#x120;" horiz-adv-x="679" d="M365 40Q414 40 451 54T516 95T560 156T583 234L591 287H408L415 340H657L646 262Q626 124 551 57T355 -10Q287 -10 241 11T165 68T122 153T109 256Q109 332 130 407T195 543T304 640T459 678Q524 678 572 656T658 599L616 555Q574 593 535 609T452 625Q378 625 326 593T240 508T191 390T175 256Q175 213 184 174T214 105T272 58T365 40ZM512 770Q512 750 500 737T466 724Q452 724 443 732T433 756Q433 775 445 788T477 801Q492 801 502 794T512 770Z" />
+<glyph unicode="&#x121;" horiz-adv-x="549" d="M457 -65Q449 -122 430 -154T385 -202T328 -222T265 -226Q202 -226 155 -214T65 -178L94 -128Q132 -153 173 -165T263 -178Q296 -178 319 -173T359 -155T384 -120T399 -64L419 83H415Q383 34 345 14T257 -7Q210 -7 178 7T126 45T96 104T87 179Q87 230 97 289T135 398T208 482T327 515Q378 515 413 498T469 433H473L487 502H537L457 -65ZM454 332Q464 399 429 433T330 467Q275 467 240 440T185 372T157 281T149 188Q149 156 155 129T176 82T213 51T272 39Q306 39 334 52T383 86T417 137T435 199L454 332ZM400 632Q400 612 388 599T354 586Q340 586 331 594T321 618Q321 637 333 650T365 663Q380 663 390 656T400 632Z" />
+<glyph unicode="&#x122;" horiz-adv-x="679" d="M365 40Q414 40 451 54T516 95T560 156T583 234L591 287H408L415 340H657L646 262Q626 124 551 57T355 -10Q287 -10 241 11T165 68T122 153T109 256Q109 332 130 407T195 543T304 640T459 678Q524 678 572 656T658 599L616 555Q574 593 535 609T452 625Q378 625 326 593T240 508T191 390T175 256Q175 213 184 174T214 105T272 58T365 40ZM318 -206H277L321 -61H390L318 -206Z" />
+<glyph unicode="&#x123;" horiz-adv-x="549" d="M457 -65Q449 -122 430 -154T385 -202T328 -222T265 -226Q202 -226 155 -214T65 -178L94 -128Q132 -153 173 -165T263 -178Q296 -178 319 -173T359 -155T384 -120T399 -64L419 83H415Q383 34 345 14T257 -7Q210 -7 178 7T126 45T96 104T87 179Q87 230 97 289T135 398T208 482T327 515Q378 515 413 498T469 433H473L487 502H537L457 -65ZM454 332Q464 399 429 433T330 467Q275 467 240 440T185 372T157 281T149 188Q149 156 155 129T176 82T213 51T272 39Q306 39 334 52T383 86T417 137T435 199L454 332ZM398 707H438L394 574H315L398 707Z" />
+<glyph unicode="&#x124;" horiz-adv-x="645" d="M490 0L536 331H201L155 0H92L185 667H248L209 388H544L583 667H646L553 0H490ZM543 704L439 786L314 704L292 739L447 838L576 739L543 704Z" />
+<glyph unicode="&#x125;" horiz-adv-x="548" d="M407 0L449 301Q452 321 454 339T456 373Q456 394 452 410T436 439T404 457T353 463Q282 463 239 427T186 323L141 0H82L180 700H239L201 434H205Q230 474 272 494T372 515Q413 515 440 507T485 482T509 441T516 385Q516 371 514 354T510 318L466 0H407ZM325 697L221 779L96 697L74 732L229 831L358 732L325 697Z" />
+<glyph unicode="&#x126;" horiz-adv-x="645" d="M489 0L535 331H201L155 0H92L162 501H74L81 550H169L185 667H248L232 550H566L582 667H646L630 550H718L711 501H623L553 0H489ZM542 383L559 501H225L208 383H542Z" />
+<glyph unicode="&#x127;" horiz-adv-x="548" d="M222 580L201 430H205Q230 471 272 491T371 511Q412 511 440 503T484 477T508 436T515 380Q515 365 514 349T510 315L466 0H406L447 297Q450 317 452 335T454 370Q454 391 450 407T434 436T402 454T352 460Q281 460 238 423T185 319L141 0H82L163 580H94L100 622H169L180 700H239L228 622H407L401 580H222Z" />
+<glyph unicode="&#x128;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM386 774Q372 742 345 727T291 712Q275 712 259 718T227 731T196 744T168 750Q152 750 135 743T106 711L74 734Q95 770 120 783T171 796Q187 796 203 790T235 777T265 764T294 758Q311 758 326 766T353 796L386 774H386Z" />
+<glyph unicode="&#x129;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM357 634Q343 602 316 587T262 572Q246 572 230 578T198 591T167 604T139 610Q123 610 106 603T77 571L45 594Q66 630 91 643T142 656Q158 656 174 650T206 637T236 624T265 618Q282 618 297 626T324 656L357 634H357Z" />
+<glyph unicode="&#x12A;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM83 730L89 772H374L368 730H83Z" />
+<glyph unicode="&#x12B;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM51 590L57 632H342L336 590H51Z" />
+<glyph unicode="&#x12C;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM372 818Q364 787 349 766T314 731T270 711T222 705Q198 705 176 711T138 730T113 765T106 818H148Q146 784 165 763T227 741Q269 741 295 762T329 818H372H372Z" />
+<glyph unicode="&#x12D;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82ZM340 678Q332 647 317 626T282 591T238 571T190 565Q166 565 144 571T106 590T81 625T74 678H116Q114 644 133 623T195 601Q237 601 263 622T297 678H340H340Z" />
+<glyph unicode="&#x12E;" horiz-adv-x="247" d="M138 0Q70 -38 44 -72T17 -139Q17 -165 33 -180T77 -196Q97 -196 112 -191T138 -180L147 -216Q136 -222 111 -229T63 -237Q14 -237 -7 -213T-29 -153Q-29 -125 -18 -102T12 -61T55 -27T104 0H92L185 667H248L155 0H138Z" />
+<glyph unicode="&#x12F;" horiz-adv-x="223" d="M129 0Q110 -11 89 -24T49 -55T20 -93T8 -139Q8 -165 24 -180T67 -196Q99 -196 129 -180L137 -216Q120 -225 97 -231T55 -237Q14 -237 -12 -217T-38 -153Q-38 -125 -26 -102T4 -60T46 -26T93 0H82L152 502H211L141 0H129ZM237 630Q237 610 225 597T192 584Q177 584 168 593T158 617Q158 637 170 649T203 662Q237 662 237 630Z" />
+<glyph unicode="&#x130;" horiz-adv-x="247" d="M92 0L185 667H248L155 0H92ZM272 770Q272 750 260 737T226 724Q212 724 203 732T193 756Q193 775 205 788T237 801Q252 801 262 794T272 770Z" />
+<glyph unicode="&#x131;" horiz-adv-x="223" d="M82 0L152 502H211L141 0H82Z" />
+<glyph unicode="&#x132;" horiz-adv-x="603" d="M92 0L185 667H248L155 0H92ZM532 151Q520 66 473 28T353 -11Q317 -11 286 -2T226 30L259 81Q284 59 305 51T353 42Q408 42 434 70T469 156L541 667H604L532 151Z" />
+<glyph unicode="&#x133;" horiz-adv-x="432" d="M82 0L152 502H211L141 0H82ZM238 630Q238 610 226 597T193 584Q178 584 169 593T159 617Q159 637 171 649T204 662Q238 662 238 630ZM341 -57Q330 -137 291 -181T168 -226Q153 -226 138 -225T109 -222L116 -171Q143 -174 164 -174Q196 -174 217 -166T251 -141T272 -101T283 -47L360 502H419L341 -57ZM446 630Q446 610 434 597T401 584Q386 584 377 593T367 617Q367 637 379 649T412 662Q446 662 446 630Z" />
+<glyph unicode="&#x134;" horiz-adv-x="425" d="M354 151Q342 66 295 28T175 -11Q139 -11 108 -2T48 30L81 81Q106 59 127 51T175 42Q230 42 256 70T291 156L363 667H426L354 151H354ZM515 704L411 786L286 704L264 739L419 838L548 739L515 704Z" />
+<glyph unicode="&#x135;" horiz-adv-x="229" d="M139 -51Q128 -131 89 -178T-35 -226Q-50 -226 -65 -225T-94 -222L-87 -171Q-60 -174 -39 -174Q-7 -174 14 -166T48 -141T69 -101T80 -47L157 502H217L139 -51ZM311 564L207 646L82 564L60 599L215 698L344 599L311 564Z" />
+<glyph unicode="&#x136;" horiz-adv-x="545" d="M446 0L280 343L190 252L155 0H92L185 667H248L201 333H205L521 667H598L333 388L522 0H446ZM238 -206H197L241 -61H310L238 -206Z" />
+<glyph unicode="&#x137;" horiz-adv-x="477" d="M379 0L265 264L163 162L141 0H82L180 700H239L174 237H178L428 502H504L313 305L451 0H379ZM210 -206H169L213 -61H282L210 -206Z" />
+<glyph unicode="&#x139;" horiz-adv-x="473" d="M92 0L185 667H248L162 55H443L436 0H92ZM222 705L194 729L346 844L399 811L222 705Z" />
+<glyph unicode="&#x13A;" horiz-adv-x="251" d="M222 0Q213 -2 199 -3T171 -5Q124 -5 105 15T94 91L180 700H239L156 107Q153 86 154 74T162 57T179 50T206 49H228L222 0H222ZM219 732L191 756L343 871L396 838L219 732Z" />
+<glyph unicode="&#x13B;" horiz-adv-x="473" d="M92 0L185 667H248L162 55H443L436 0H92ZM215 -206H174L218 -61H287L215 -206Z" />
+<glyph unicode="&#x13C;" horiz-adv-x="251" d="M222 0Q213 -2 199 -3T171 -5Q124 -5 105 15T94 91L180 700H239L156 107Q153 86 154 74T162 57T179 50T206 49H228L222 0H222ZM109 -206H68L112 -61H181L109 -206Z" />
+<glyph unicode="&#x13D;" horiz-adv-x="473" d="M92 0L185 667H248L162 55H443L436 0H92ZM453 544H411L465 700H534L453 544Z" />
+<glyph unicode="&#x13E;" horiz-adv-x="251" d="M222 0Q213 -2 199 -3T171 -5Q124 -5 105 15T94 91L180 700H239L156 107Q153 86 154 74T162 57T179 50T206 49H228L222 0ZM313 544H271L325 700H394L313 544Z" />
+<glyph unicode="&#x13F;" horiz-adv-x="473" d="M92 0L185 667H248L162 55H443L436 0H92ZM471 266Q471 241 456 225T415 209Q397 209 386 218T374 247Q374 273 388 288T429 304Q448 304 459 295T471 266Z" />
+<glyph unicode="&#x140;" horiz-adv-x="302" d="M222 0Q213 -2 199 -3T171 -5Q124 -5 105 15T94 91L180 700H239L156 107Q153 86 154 74T162 57T179 50T206 49H228L222 0ZM352 244Q352 219 337 203T296 187Q278 187 267 196T255 225Q255 251 269 266T310 282Q329 282 340 273T352 244Z" />
+<glyph unicode="&#x141;" horiz-adv-x="473" d="M92 0L137 321L91 296L99 354L145 379L185 667H248L213 414L338 481L330 422L204 355L162 55H443L436 0H92Z" />
+<glyph unicode="&#x142;" horiz-adv-x="251" d="M192 364L156 107Q153 86 154 74T162 57T179 50T206 49H228L222 0Q212 -2 199 -3T172 -5Q124 -5 105 15T94 91L128 332L82 306L90 364L136 389L180 700H239L200 420L311 479L303 423L192 364Z" />
+<glyph unicode="&#x143;" horiz-adv-x="684" d="M530 0L236 560H232L154 0H92L185 667H252L541 110H545L623 667H685L592 0H530ZM440 705L412 729L564 844L617 811L440 705Z" />
+<glyph unicode="&#x144;" horiz-adv-x="548" d="M407 0L449 301Q455 338 455 368Q455 390 451 407T434 437T402 456T351 463Q280 463 238 426T186 323L141 0H82L152 502H203L200 437H204Q264 515 369 515Q411 515 439 507T484 482T509 442T516 387Q516 356 510 319L466 0H407ZM340 565L312 589L464 704L517 671L340 565Z" />
+<glyph unicode="&#x145;" horiz-adv-x="684" d="M530 0L236 560H232L154 0H92L185 667H252L541 110H545L623 667H685L592 0H530ZM290 -206H249L293 -61H362L290 -206Z" />
+<glyph unicode="&#x146;" horiz-adv-x="548" d="M407 0L449 301Q455 338 455 368Q455 390 451 407T434 437T402 456T351 463Q280 463 238 426T186 323L141 0H82L152 502H203L200 437H204Q264 515 369 515Q411 515 439 507T484 482T509 442T516 387Q516 356 510 319L466 0H407ZM238 -206H197L241 -61H310L238 -206Z" />
+<glyph unicode="&#x147;" horiz-adv-x="684" d="M530 0L236 560H232L154 0H92L185 667H252L541 110H545L623 667H685L592 0H530ZM603 803L457 705L340 803L371 837L464 757L580 837L603 803Z" />
+<glyph unicode="&#x148;" horiz-adv-x="548" d="M407 0L449 301Q455 338 455 368Q455 390 451 407T434 437T402 456T351 463Q280 463 238 426T186 323L141 0H82L152 502H203L200 437H204Q264 515 369 515Q411 515 439 507T484 482T509 442T516 387Q516 356 510 319L466 0H407ZM507 663L361 565L244 663L275 697L368 617L484 697L507 663Z" />
+<glyph unicode="&#x14A;" horiz-adv-x="684" d="M411 -179H424Q448 -179 465 -170T493 -147T510 -115T519 -79L531 2L235 572H231L154 0H92L185 667H252L543 103H547L624 667H685L581 -76Q571 -146 533 -191T422 -236H403L411 -179Z" />
+<glyph unicode="&#x14B;" horiz-adv-x="548" d="M203 502L200 437H204Q264 515 369 515Q411 515 439 507T484 482T509 442T516 387Q516 356 510 319L462 -27Q451 -108 411 -155T285 -202H266L274 -148H287Q317 -148 337 -139T371 -113T391 -75T402 -30L449 301Q455 338 455 368Q455 390 451 407T434 437T402 456T351 463Q280 463 238 426T186 323L141 0H82L152 502H203Z" />
+<glyph unicode="&#x14C;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM330 730L336 772H621L615 730H330Z" />
+<glyph unicode="&#x14D;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM219 590L225 632H510L504 590H219Z" />
+<glyph unicode="&#x14E;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM618 824Q610 793 595 772T560 737T516 717T468 711Q444 711 422 717T384 736T359 771T352 824H394Q392 790 411 769T473 747Q515 747 541 768T575 824H618H618Z" />
+<glyph unicode="&#x14F;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM508 678Q500 647 485 626T450 591T406 571T358 565Q334 565 312 571T274 590T249 625T242 678H284Q282 644 301 623T363 601Q405 601 431 622T465 678H508H508Z" />
+<glyph unicode="&#x150;" horiz-adv-x="732" d="M715 412Q715 331 694 255T630 120T520 25T364 -11Q236 -11 173 61T109 256Q109 337 130 413T195 548T305 642T461 678Q588 678 651 607T715 412ZM371 41Q447 41 500 74T586 161T634 280T649 411Q649 459 638 499T604 567T543 611T454 627Q378 627 325 594T239 509T191 391T176 260Q176 212 187 172T221 102T282 57T371 41ZM581 693L552 712L706 827L761 798L581 693ZM371 693L342 712L496 827L551 798L371 693Z" />
+<glyph unicode="&#x151;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q399 515 435 501T494 459T528 395T540 311Q540 250 525 192T477 89T395 17T277 -11Q181 -11 135 43T88 190ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 155 157 127T181 79T222 47T285 36Q340 36 377 62T437 128T468 217T478 312Q478 346 471 374T448 423T407 455T344 467ZM488 566L459 585L613 700L668 671L488 566ZM278 566L249 585L403 700L458 671L278 566Z" />
+<glyph unicode="&#x152;" horiz-adv-x="853" d="M461 0Q439 -5 410 -8T355 -11Q285 -11 238 9T163 65T122 148T109 249Q109 323 126 399T184 537T290 638T452 678Q478 678 506 675T554 667H896L889 612H610L578 385H828L820 330H570L531 54H822L815 0H461ZM363 41Q392 41 420 44T468 56L546 610Q528 618 502 622T446 627Q369 627 317 593T234 506T189 385T175 252Q175 210 183 172T213 105T271 59T363 41Z" />
+<glyph unicode="&#x153;" horiz-adv-x="911" d="M898 322Q898 299 896 277T888 232H534Q531 208 531 187Q531 115 566 77T672 38Q745 38 825 95L842 51Q800 22 755 6T664 -11Q624 -11 595 -2T544 25T509 64T487 114Q450 47 398 18T277 -11Q186 -11 137 38T88 186Q88 246 103 305T150 410T231 486T351 515Q418 515 463 484T524 387Q557 447 609 481T728 515Q809 515 853 465T898 322ZM838 277Q841 298 841 320Q841 348 836 374T817 421T780 454T722 467Q652 467 603 417T540 277H838ZM474 251Q476 268 477 283T479 313Q479 346 472 373T449 421T407 453T344 464Q290 464 253 439T193 375T160 288T149 192Q149 151 159 122T187 74T230 47T285 38Q321 38 352 49T407 86T449 152T474 251Z" />
+<glyph unicode="&#x154;" horiz-adv-x="581" d="M459 0L409 246Q404 273 390 282T348 292H196L155 0H92L185 664Q232 669 277 672T370 675Q409 675 446 669T514 646T562 599T581 523Q581 450 548 393T442 312V308Q453 302 460 290T470 265L526 0H459ZM345 344Q430 344 472 384T515 512Q515 547 503 568T469 602T421 618T364 623Q333 623 303 622T241 615L203 344H345ZM359 706L331 730L483 845L536 812L359 706Z" />
+<glyph unicode="&#x155;" horiz-adv-x="343" d="M366 461Q291 461 244 432T187 333L141 0H82L152 502H202L199 440H203Q232 480 274 497T369 514H391L383 461H366ZM266 565L238 589L390 704L443 671L266 565Z" />
+<glyph unicode="&#x156;" horiz-adv-x="581" d="M459 0L409 246Q404 273 390 282T348 292H196L155 0H92L185 664Q232 669 277 672T370 675Q409 675 446 669T514 646T562 599T581 523Q581 450 548 393T442 312V308Q453 302 460 290T470 265L526 0H459ZM345 344Q430 344 472 384T515 512Q515 547 503 568T469 602T421 618T364 623Q333 623 303 622T241 615L203 344H345ZM248 -206H207L251 -61H320L248 -206Z" />
+<glyph unicode="&#x157;" horiz-adv-x="343" d="M366 461Q291 461 244 432T187 333L141 0H82L152 502H202L199 440H203Q232 480 274 497T369 514H391L383 461H366ZM65 -206H24L68 -61H137L65 -206Z" />
+<glyph unicode="&#x158;" horiz-adv-x="581" d="M459 0L409 246Q404 273 390 282T348 292H196L155 0H92L185 664Q232 669 277 672T370 675Q409 675 446 669T514 646T562 599T581 523Q581 450 548 393T442 312V308Q453 302 460 290T470 265L526 0H459ZM345 344Q430 344 472 384T515 512Q515 547 503 568T469 602T421 618T364 623Q333 623 303 622T241 615L203 344H345ZM522 803L376 705L259 803L290 837L383 757L499 837L522 803Z" />
+<glyph unicode="&#x159;" horiz-adv-x="343" d="M366 461Q291 461 244 432T187 333L141 0H82L152 502H202L199 440H203Q232 480 274 497T369 514H391L383 461H366ZM415 663L269 565L152 663L183 697L276 617L392 697L415 663Z" />
+<glyph unicode="&#x15A;" horiz-adv-x="506" d="M481 201Q481 150 464 111T416 44T343 3T250 -11Q192 -11 142 5T45 57L82 107Q118 75 160 58T251 40Q287 40 317 49T369 77T403 124T416 189Q416 226 395 251T341 295T271 331T201 371T148 424T126 500Q126 546 144 580T192 635T261 667T343 678Q393 678 437 663T522 618L490 565Q423 626 334 626Q306 626 281 620T236 599T205 563T193 511Q193 476 214 453T268 411T337 376T406 336T459 281T481 201ZM338 706L310 730L462 845L515 812L338 706Z" />
+<glyph unicode="&#x15B;" horiz-adv-x="428" d="M404 152Q404 111 389 81T347 30T287 -1T214 -11Q167 -11 125 0T44 38L73 85Q104 62 143 49T221 36Q245 36 267 42T307 61T334 93T344 140Q344 169 326 186T282 217T224 241T167 268T122 307T104 368Q104 408 120 436T162 481T221 507T290 515Q328 515 364 505T435 477L410 429Q383 447 350 457T284 467Q262 467 240 463T201 448T173 420T162 377Q162 350 180 334T225 306T283 282T341 255T386 214T404 152ZM266 565L238 589L390 704L443 671L266 565Z" />
+<glyph unicode="&#x15C;" horiz-adv-x="506" d="M481 201Q481 150 464 111T416 44T343 3T250 -11Q192 -11 142 5T45 57L82 107Q118 75 160 58T251 40Q287 40 317 49T369 77T403 124T416 189Q416 226 395 251T341 295T271 331T201 371T148 424T126 500Q126 546 144 580T192 635T261 667T343 678Q393 678 437 663T522 618L490 565Q423 626 334 626Q306 626 281 620T236 599T205 563T193 511Q193 476 214 453T268 411T337 376T406 336T459 281T481 201ZM466 704L362 786L237 704L215 739L370 838L499 739L466 704Z" />
+<glyph unicode="&#x15D;" horiz-adv-x="428" d="M404 152Q404 111 389 81T347 30T287 -1T214 -11Q167 -11 125 0T44 38L73 85Q104 62 143 49T221 36Q245 36 267 42T307 61T334 93T344 140Q344 169 326 186T282 217T224 241T167 268T122 307T104 368Q104 408 120 436T162 481T221 507T290 515Q328 515 364 505T435 477L410 429Q383 447 350 457T284 467Q262 467 240 463T201 448T173 420T162 377Q162 350 180 334T225 306T283 282T341 255T386 214T404 152ZM423 564L319 646L194 564L172 599L327 698L456 599L423 564Z" />
+<glyph unicode="&#x15E;" horiz-adv-x="506" d="M315 -114Q315 -161 286 -184T213 -207Q192 -207 172 -201T134 -183L152 -155Q166 -163 180 -168T212 -173Q238 -173 256 -162T275 -120Q275 -100 260 -93T226 -86Q204 -86 189 -92L178 -76L223 -10Q173 -6 130 9T45 57L82 107Q118 75 160 58T251 40Q287 40 317 49T369 77T403 124T416 189Q416 226 395 251T341 295T271 331T201 371T148 424T126 500Q126 546 144 580T192 635T261 667T343 678Q393 678 437 663T522 618L490 565Q423 626 334 626Q306 626 281 620T236 599T205 563T193 511Q193 476 214 453T268 411T337 376T406 336T459 281T481 201Q481 151 465 113T419 47T350 6T262 -11L228 -57Q233 -56 238 -56T249 -56Q275 -56 295 -67T315 -114Z" />
+<glyph unicode="&#x15F;" horiz-adv-x="428" d="M287 -114Q287 -161 258 -184T185 -207Q164 -207 144 -201T106 -183L124 -155Q138 -163 152 -168T184 -173Q210 -173 228 -162T247 -120Q247 -100 232 -93T198 -86Q176 -86 161 -92L150 -76L195 -10Q154 -8 117 3T44 38L73 85Q104 62 143 49T221 36Q245 36 267 42T307 61T334 93T344 140Q344 169 326 186T282 217T224 241T167 268T122 307T104 368Q104 408 120 436T162 481T221 507T290 515Q328 515 364 505T435 477L410 429Q383 447 350 457T284 467Q262 467 240 463T201 448T173 420T162 377Q162 350 180 334T225 306T283 282T341 255T386 214T404 152Q404 114 391 85T354 36T300 4T234 -10L200 -57Q205 -56 210 -56T221 -56Q247 -56 267 -67T287 -114Z" />
+<glyph unicode="&#x160;" horiz-adv-x="506" d="M481 201Q481 150 464 111T416 44T343 3T250 -11Q192 -11 142 5T45 57L82 107Q118 75 160 58T251 40Q287 40 317 49T369 77T403 124T416 189Q416 226 395 251T341 295T271 331T201 371T148 424T126 500Q126 546 144 580T192 635T261 667T343 678Q393 678 437 663T522 618L490 565Q423 626 334 626Q306 626 281 620T236 599T205 563T193 511Q193 476 214 453T268 411T337 376T406 336T459 281T481 201ZM495 803L349 705L232 803L263 837L356 757L472 837L495 803Z" />
+<glyph unicode="&#x161;" horiz-adv-x="428" d="M404 152Q404 111 389 81T347 30T287 -1T214 -11Q167 -11 125 0T44 38L73 85Q104 62 143 49T221 36Q245 36 267 42T307 61T334 93T344 140Q344 169 326 186T282 217T224 241T167 268T122 307T104 368Q104 408 120 436T162 481T221 507T290 515Q328 515 364 505T435 477L410 429Q383 447 350 457T284 467Q262 467 240 463T201 448T173 420T162 377Q162 350 180 334T225 306T283 282T341 255T386 214T404 152ZM447 663L301 565L184 663L215 697L308 617L424 697L447 663Z" />
+<glyph unicode="&#x162;" horiz-adv-x="482" d="M358 612L272 0H209L295 612H107L114 667H554L547 612H358ZM191 -206H150L194 -61H263L191 -206Z" />
+<glyph unicode="&#x163;" horiz-adv-x="332" d="M289 -1Q279 -3 264 -4T235 -5Q201 -5 179 0T145 16T127 42T122 77Q122 95 125 116L172 452H88L95 502H179L199 642H258L238 502H360L353 452H231L185 126Q182 108 182 94Q182 70 197 59T259 47H296L289 -1H289ZM162 -206H121L165 -61H234L162 -206Z" />
+<glyph unicode="&#x164;" horiz-adv-x="482" d="M358 612L272 0H209L295 612H107L114 667H554L547 612H358ZM487 803L341 705L224 803L255 837L348 757L464 837L487 803Z" />
+<glyph unicode="&#x165;" horiz-adv-x="332" d="M289 -1Q279 -3 264 -4T235 -5Q201 -5 179 0T145 16T127 42T122 77Q122 95 125 116L172 452H88L95 502H179L199 642H258L238 502H360L353 452H231L185 126Q182 108 182 94Q182 70 197 59T259 47H296L289 -1ZM380 544H338L392 700H461L380 544Z" />
+<glyph unicode="&#x166;" horiz-adv-x="482" d="M320 342L272 0H209L257 342H99L106 392H264L295 612H107L114 667H554L547 612H358L327 392H485L478 342H320Z" />
+<glyph unicode="&#x167;" horiz-adv-x="332" d="M209 297L185 126Q182 108 182 94Q182 70 197 59T259 47H296L289 -1Q278 -3 264 -4T235 -5Q201 -5 179 0T145 16T127 42T122 77Q122 95 125 116L150 297H66L73 345H157L172 452H88L95 502H179L199 642H258L238 502H360L353 452H231L216 345H337L330 297H209Z" />
+<glyph unicode="&#x168;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM586 774Q572 742 545 727T491 712Q475 712 459 718T427 731T396 744T368 750Q352 750 335 743T306 711L274 734Q295 770 320 783T371 796Q387 796 403 790T435 777T465 764T494 758Q511 758 526 766T553 796L586 774H586Z" />
+<glyph unicode="&#x169;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM507 634Q493 602 466 587T412 572Q396 572 380 578T348 591T317 604T289 610Q273 610 256 603T227 571L195 594Q216 630 241 643T292 656Q308 656 324 650T356 637T386 624T415 618Q432 618 447 626T474 656L507 634H507Z" />
+<glyph unicode="&#x16A;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM287 730L293 772H578L572 730H287Z" />
+<glyph unicode="&#x16B;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM208 590L214 632H499L493 590H208Z" />
+<glyph unicode="&#x16C;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM571 818Q563 787 548 766T513 731T469 711T421 705Q397 705 375 711T337 730T312 765T305 818H347Q345 784 364 763T426 741Q468 741 494 762T528 818H571H571Z" />
+<glyph unicode="&#x16D;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM501 678Q493 647 478 626T443 591T399 571T351 565Q327 565 305 571T267 590T242 625T235 678H277Q275 644 294 623T356 601Q398 601 424 622T458 678H501H501Z" />
+<glyph unicode="&#x16E;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM523 785Q523 765 516 748T495 718T463 697T423 689Q391 689 369 708T346 761Q346 781 354 798T375 829T407 849T447 857Q479 857 501 838T523 785ZM427 718Q455 718 472 736T490 781Q490 801 478 814T443 828Q414 828 397 810T379 765Q379 745 391 732T427 718Z" />
+<glyph unicode="&#x16F;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM450 663Q450 643 443 626T422 596T390 575T350 567Q318 567 296 586T273 639Q273 659 281 676T302 707T334 727T374 735Q406 735 428 716T450 663ZM354 596Q382 596 399 614T417 659Q417 679 405 692T370 706Q341 706 324 688T306 643Q306 623 318 610T354 596Z" />
+<glyph unicode="&#x170;" horiz-adv-x="652" d="M595 255Q575 114 509 52T323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 414 56T473 97T511 160T531 242L590 667H653L595 255H595ZM531 709L502 728L656 843L711 814L531 709ZM321 709L292 728L446 843L501 814L321 709Z" />
+<glyph unicode="&#x171;" horiz-adv-x="540" d="M409 0L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H211L174 238Q170 210 168 187T165 143Q165 92 186 66T264 40Q340 40 378 81T425 188L469 502H528L458 0H409ZM466 569L437 588L591 703L646 674L466 569ZM256 569L227 588L381 703L436 674L256 569Z" />
+<glyph unicode="&#x172;" horiz-adv-x="652" d="M595 255Q581 151 542 91T435 6Q401 -13 378 -30T341 -64T321 -98T315 -133Q315 -159 331 -175T375 -191Q395 -191 410 -186T436 -174L444 -210Q433 -216 409 -223T362 -231Q312 -231 290 -207T268 -147Q268 -101 295 -68T362 -8Q344 -11 323 -11Q220 -11 171 34T121 179Q121 213 127 255L185 667H248L189 242Q187 227 186 213T185 186Q185 120 217 81T331 42Q379 42 413 56T472 97T510 160T530 242L589 667H653L595 255H595Z" />
+<glyph unicode="&#x173;" horiz-adv-x="540" d="M441 0Q406 -19 383 -36T347 -70T327 -104T321 -139Q321 -165 337 -180T380 -196Q400 -196 415 -191T442 -180L450 -216Q439 -222 415 -229T368 -237Q318 -237 296 -213T274 -153Q274 -125 285 -101T315 -58T358 -23T409 5L410 66H406Q384 35 346 12T243 -11Q210 -11 184 -5T141 17T114 59T104 126Q104 141 105 157T109 194L152 502H212L175 238Q171 210 169 186T166 141Q166 90 186 65T264 40Q341 40 378 81T424 188L468 502H528L458 0H441Z" />
+<glyph unicode="&#x174;" horiz-adv-x="869" d="M673 0H594L515 571H511L276 0H192L127 667H196L245 68H249L496 667H559L641 68H645L861 667H928L673 0ZM648 704L544 786L419 704L397 739L552 838L681 739L648 704Z" />
+<glyph unicode="&#x175;" horiz-adv-x="720" d="M550 0H477L419 411H415L240 0H169L99 502H160L213 53H217L404 502H455L517 54H521L701 502H761L550 0ZM557 564L453 646L328 564L306 599L461 698L590 599L557 564Z" />
+<glyph unicode="&#x176;" horiz-adv-x="533" d="M336 267L299 0H235L272 264L111 667H181L312 323L539 667H608L336 267ZM481 704L377 786L252 704L230 739L385 838L514 739L481 704Z" />
+<glyph unicode="&#x177;" horiz-adv-x="466" d="M217 -88Q197 -128 180 -154T144 -197T105 -219T57 -226Q49 -226 37 -226T14 -224L21 -172Q30 -174 38 -174T58 -174Q77 -174 91 -169T118 -151T145 -113T178 -49L211 21L93 502H157L250 89H254L450 502H513L217 -88ZM433 564L329 646L204 564L182 599L337 698L466 599L433 564Z" />
+<glyph unicode="&#x178;" horiz-adv-x="533" d="M336 267L299 0H235L272 264L111 667H181L312 323L539 667H608L336 267ZM500 760Q500 741 486 727T453 713Q438 713 428 723T418 749Q418 767 432 781T465 795Q480 795 490 785T500 760ZM331 759Q331 741 317 727T284 713Q269 713 259 723T249 749Q249 767 263 781T296 795Q311 795 321 785T331 759Z" />
+<glyph unicode="&#x179;" horiz-adv-x="532" d="M47 0L54 55L493 612H148L155 667H570L563 613L122 54H492L485 0H47ZM343 705L315 729L467 844L520 811L343 705Z" />
+<glyph unicode="&#x17A;" horiz-adv-x="441" d="M42 0L48 49L392 452H113L120 502H464L457 452L115 50H406L399 0H42ZM278 565L250 589L402 704L455 671L278 565Z" />
+<glyph unicode="&#x17B;" horiz-adv-x="532" d="M47 0L54 55L493 612H148L155 667H570L563 613L122 54H492L485 0H47ZM417 770Q417 750 405 737T371 724Q357 724 348 732T338 756Q338 775 350 788T382 801Q397 801 407 794T417 770Z" />
+<glyph unicode="&#x17C;" horiz-adv-x="441" d="M42 0L48 49L392 452H113L120 502H464L457 452L115 50H406L399 0H42ZM355 632Q355 612 343 599T309 586Q295 586 286 594T276 618Q276 637 288 650T320 663Q335 663 345 656T355 632Z" />
+<glyph unicode="&#x17D;" horiz-adv-x="532" d="M47 0L54 55L493 612H148L155 667H570L563 613L122 54H492L485 0H47ZM513 803L367 705L250 803L281 837L374 757L490 837L513 803Z" />
+<glyph unicode="&#x17E;" horiz-adv-x="441" d="M42 0L48 49L392 452H113L120 502H464L457 452L115 50H406L399 0H42ZM455 663L309 565L192 663L223 697L316 617L432 697L455 663Z" />
+<glyph unicode="&#x192;" horiz-adv-x="554" d="M580 624Q568 625 562 625T544 625Q529 625 515 623T488 613T464 591T446 555L399 420H505L489 367H382L257 -11Q243 -52 226 -85T186 -142T133 -179T63 -192Q55 -192 45 -192T24 -189L41 -139Q50 -140 59 -140T79 -140Q98 -140 115 -129T148 -100T175 -56T197 -4L319 367H244L262 420H336L377 546Q389 582 402 607T437 647T487 668T561 675Q570 675 577 675T597 674L580 624Z" />
+<glyph unicode="&#x1FC;" horiz-adv-x="868" d="M449 0L473 171H214L94 0H25L504 667H913L906 613H598L566 385H844L836 330H558L519 54H837L830 0H449ZM535 610H527L248 226H481L535 610ZM674 705L646 729L798 844L851 811L674 705Z" />
+<glyph unicode="&#x1FD;" horiz-adv-x="840" d="M827 326Q827 303 825 279T817 232H464Q461 209 461 188Q461 120 494 79T599 38Q641 38 681 53T754 95L772 51Q733 22 688 6T593 -11Q539 -11 493 9T421 81Q373 41 319 15T201 -11Q143 -11 105 20T66 113Q66 159 82 192T124 249T186 287T260 309T338 319T413 322Q415 339 417 356T420 390Q420 435 398 450T334 466Q284 466 235 449T143 405L128 451Q179 479 230 497T341 515Q367 515 390 511T431 495T460 465T473 416Q506 463 553 489T657 515Q701 515 733 501T786 463T817 403T827 326ZM767 277Q770 301 770 325Q770 356 764 382T743 427T705 456T648 467Q609 467 578 451T524 407T487 346T468 277H767ZM407 116Q402 131 400 148T398 181Q398 230 407 280Q369 280 321 276T230 255T158 209T128 127Q128 80 153 56T219 32Q247 32 273 39T322 59T367 86T407 116ZM514 565L486 589L638 704L691 671L514 565Z" />
+<glyph unicode="&#x1FE;" horiz-adv-x="732" d="M715 415Q715 334 695 257T632 121T523 25T367 -11Q319 -11 272 1T188 45L128 -23L95 12L156 81Q129 119 120 165T110 256Q110 337 131 413T196 548T306 642T462 678Q507 678 549 668T627 630L682 694L715 661L659 597Q691 560 703 512T715 415ZM372 41Q447 41 499 73T585 158T633 275T648 405Q648 443 642 483T614 554L232 91Q260 64 295 53T372 41ZM454 627Q380 627 328 595T242 511T193 395T177 265Q177 230 183 196T207 130L584 585Q556 609 523 618T454 627ZM440 706L412 730L564 845L617 812L440 706Z" />
+<glyph unicode="&#x1FF;" horiz-adv-x="559" d="M88 190Q88 251 103 309T151 413T233 487T351 515Q432 515 480 473L540 537L572 509L507 441Q540 390 540 311Q540 250 525 192T477 89T395 17T277 -11Q228 -11 193 3T133 45L75 -16L47 12L110 80Q88 124 88 190ZM478 312Q478 361 464 395L174 88Q190 63 216 50T285 36Q340 36 377 62T437 128T468 217T478 312ZM344 467Q288 467 251 441T192 375T160 285T150 189Q150 154 157 129L441 432Q424 449 401 458T344 467ZM349 565L321 589L473 704L526 671L349 565Z" />
+<glyph unicode="&#x218;" horiz-adv-x="506" d="M481 201Q481 150 464 111T416 44T343 3T250 -11Q192 -11 142 5T45 57L82 107Q118 75 160 58T251 40Q287 40 317 49T369 77T403 124T416 189Q416 226 395 251T341 295T271 331T201 371T148 424T126 500Q126 546 144 580T192 635T261 667T343 678Q393 678 437 663T522 618L490 565Q423 626 334 626Q306 626 281 620T236 599T205 563T193 511Q193 476 214 453T268 411T337 376T406 336T459 281T481 201ZM206 -206H165L209 -61H278L206 -206Z" />
+<glyph unicode="&#x219;" horiz-adv-x="428" d="M404 152Q404 111 389 81T347 30T287 -1T214 -11Q167 -11 125 0T44 38L73 85Q104 62 143 49T221 36Q245 36 267 42T307 61T334 93T344 140Q344 169 326 186T282 217T224 241T167 268T122 307T104 368Q104 408 120 436T162 481T221 507T290 515Q328 515 364 505T435 477L410 429Q383 447 350 457T284 467Q262 467 240 463T201 448T173 420T162 377Q162 350 180 334T225 306T283 282T341 255T386 214T404 152ZM172 -206H131L175 -61H244L172 -206Z" />
+<glyph unicode="&#x237;" horiz-adv-x="229" d="M139 -51Q128 -131 89 -178T-35 -226Q-50 -226 -65 -225T-94 -222L-87 -171Q-60 -174 -39 -174Q-7 -174 14 -166T48 -141T69 -101T80 -47L157 502H217L139 -51H139Z" />
+<glyph unicode="&#x2C6;" horiz-adv-x="362" d="M374 564L270 646L145 564L123 599L278 698L407 599L374 564Z" />
+<glyph unicode="&#x2C7;" horiz-adv-x="362" d="M405 663L259 565L142 663L173 697L266 617L382 697L405 663Z" />
+<glyph unicode="&#x2D8;" horiz-adv-x="362" d="M409 678Q401 647 386 626T351 591T307 571T259 565Q235 565 213 571T175 590T150 625T143 678H185Q183 644 202 623T264 601Q306 601 332 622T366 678H409H409Z" />
+<glyph unicode="&#x2D9;" horiz-adv-x="362" d="M308 632Q308 612 296 599T262 586Q248 586 239 594T229 618Q229 637 241 650T273 663Q288 663 298 656T308 632Z" />
+<glyph unicode="&#x2DA;" horiz-adv-x="362" d="M360 663Q360 643 353 626T332 596T300 575T260 567Q228 567 206 586T183 639Q183 659 191 676T212 707T244 727T284 735Q316 735 338 716T360 663ZM264 596Q292 596 309 614T327 659Q327 679 315 692T280 706Q251 706 234 688T216 643Q216 623 228 610T264 596Z" />
+<glyph unicode="&#x2DB;" horiz-adv-x="362" d="M224 -6Q205 -17 186 -29T150 -57T124 -93T114 -138Q114 -164 130 -180T173 -196Q205 -196 233 -179L243 -216Q226 -225 203 -231T160 -237Q117 -237 92 -217T66 -151Q66 -118 80 -92T117 -46T168 -10T223 15L238 2L224 -6Z" />
+<glyph unicode="&#x2DC;" horiz-adv-x="362" d="M424 634Q410 602 383 587T329 572Q313 572 297 578T265 591T234 604T206 610Q190 610 173 603T144 571L112 594Q133 630 158 643T209 656Q225 656 241 650T273 637T303 624T332 618Q349 618 364 626T391 656L424 634H424Z" />
+<glyph unicode="&#x2DD;" horiz-adv-x="362" d="M303 569L274 588L428 703L483 674L303 569ZM93 569L64 588L218 703L273 674L93 569Z" />
+<glyph unicode="&#x1E80;" horiz-adv-x="869" d="M673 0H594L515 571H511L276 0H192L127 667H196L245 68H249L496 667H559L641 68H645L861 667H928L673 0ZM561 705L414 809L478 841L595 727L561 705Z" />
+<glyph unicode="&#x1E81;" horiz-adv-x="720" d="M550 0H477L419 411H415L240 0H169L99 502H160L213 53H217L404 502H455L517 54H521L701 502H761L550 0ZM463 565L316 669L380 701L497 587L463 565Z" />
+<glyph unicode="&#x1E82;" horiz-adv-x="869" d="M673 0H594L515 571H511L276 0H192L127 667H196L245 68H249L496 667H559L641 68H645L861 667H928L673 0ZM504 705L476 729L628 844L681 811L504 705Z" />
+<glyph unicode="&#x1E83;" horiz-adv-x="720" d="M550 0H477L419 411H415L240 0H169L99 502H160L213 53H217L404 502H455L517 54H521L701 502H761L550 0ZM416 565L388 589L540 704L593 671L416 565Z" />
+<glyph unicode="&#x1E84;" horiz-adv-x="869" d="M673 0H594L515 571H511L276 0H192L127 667H196L245 68H249L496 667H559L641 68H645L861 667H928L673 0ZM668 760Q668 741 654 727T621 713Q606 713 596 723T586 749Q586 767 600 781T633 795Q648 795 658 785T668 760ZM499 759Q499 741 485 727T452 713Q437 713 427 723T417 749Q417 767 431 781T464 795Q479 795 489 785T499 759Z" />
+<glyph unicode="&#x1E85;" horiz-adv-x="720" d="M550 0H477L419 411H415L240 0H169L99 502H160L213 53H217L404 502H455L517 54H521L701 502H761L550 0ZM572 620Q572 601 558 587T525 573Q510 573 500 583T490 609Q490 627 504 641T537 655Q552 655 562 645T572 620ZM403 619Q403 601 389 587T356 573Q341 573 331 583T321 609Q321 627 335 641T368 655Q383 655 393 645T403 619Z" />
+<glyph unicode="&#x1EF2;" horiz-adv-x="533" d="M336 267L299 0H235L272 264L111 667H181L312 323L539 667H608L336 267ZM378 705L231 809L295 841L412 727L378 705Z" />
+<glyph unicode="&#x1EF3;" horiz-adv-x="466" d="M217 -88Q197 -128 180 -154T144 -197T105 -219T57 -226Q49 -226 37 -226T14 -224L21 -172Q30 -174 38 -174T58 -174Q77 -174 91 -169T118 -151T145 -113T178 -49L211 21L93 502H157L250 89H254L450 502H513L217 -88ZM329 565L182 669L246 701L363 587L329 565Z" />
+<glyph unicode="&#x2013;" horiz-adv-x="602" d="M84 235L90 283H590L584 235H84Z" />
+<glyph unicode="&#x2014;" horiz-adv-x="874" d="M84 236L90 282H862L856 236H84Z" />
+<glyph unicode="&#x2018;" horiz-adv-x="185" d="M173 509H92L191 700H235L173 509Z" />
+<glyph unicode="&#x2019;" horiz-adv-x="185" d="M162 509H119L181 700H262L162 509Z" />
+<glyph unicode="&#x201A;" horiz-adv-x="185" d="M71 -140H28L90 51H171L71 -140Z" />
+<glyph unicode="&#x201C;" horiz-adv-x="345" d="M334 509H253L353 700H395L334 509ZM172 509H92L192 700H234L172 509Z" />
+<glyph unicode="&#x201D;" horiz-adv-x="345" d="M321 509H279L340 700H422L321 509ZM162 509H119L181 700H262L162 509Z" />
+<glyph unicode="&#x201E;" horiz-adv-x="345" d="M230 -141H188L249 50H331L230 -141ZM71 -141H28L90 50H171L71 -141Z" />
+<glyph unicode="&#x2020;" horiz-adv-x="326" d="M253 486L207 162H148L194 486H112L119 535H201L219 666H278L260 535H341L334 486H253Z" />
+<glyph unicode="&#x2021;" horiz-adv-x="326" d="M223 295L202 144H144L165 295H84L91 347H172L192 488H111L118 539H199L217 666H275L257 539H339L332 488H250L230 347H313L306 295H223Z" />
+<glyph unicode="&#x2022;" horiz-adv-x="517" d="M405 329Q405 298 394 272T362 226T316 195T258 183Q227 183 201 194T155 225T124 271T112 329Q112 360 123 386T154 432T200 463T258 475Q289 475 315 464T362 433T393 387T405 329Z" />
+<glyph unicode="&#x2026;" horiz-adv-x="630" d="M145 46Q145 22 131 6T91 -10Q73 -10 62 -1T51 28Q51 52 64 67T104 83Q122 83 133 74T145 46ZM367 46Q367 22 353 6T313 -10Q295 -10 284 -1T273 28Q273 52 287 67T326 83Q344 83 355 75T367 46ZM590 46Q590 22 576 6T536 -10Q518 -10 507 -1T496 28Q496 52 509 67T549 83Q567 83 578 75T590 46Z" />
+<glyph unicode="&#x2030;" horiz-adv-x="968" d="M351 596Q351 563 345 527T324 462T282 414T215 395Q163 395 143 424T123 501Q123 534 129 569T150 633T192 681T258 700Q309 700 330 672T351 596ZM220 428Q249 428 267 446T294 490T307 546T310 597Q310 627 299 647T253 667Q224 667 207 649T180 605T167 550T164 499Q164 469 174 449T220 428ZM167 0H121L631 700H675L167 0ZM648 195Q648 162 642 126T621 61T579 13T512 -6Q460 -6 440 23T420 100Q420 133 426 168T447 232T489 280T555 299Q606 299 627 271T648 195ZM516 27Q545 27 562 45T590 90T603 146T606 198Q606 227 594 246T550 265Q521 265 504 247T477 203T464 148T461 97Q461 67 471 47T516 27ZM942 195Q942 162 936 126T915 61T873 13T806 -6Q754 -6 734 23T713 100Q713 133 719 168T740 233T782 280T849 299Q900 299 921 271T942 195ZM810 27Q839 27 856 45T883 90T896 146T899 197Q899 226 888 245T844 265Q815 265 798 247T771 203T758 148T755 97Q755 67 765 47T810 27Z" />
+<glyph unicode="&#x2039;" horiz-adv-x="259" d="M185 117L64 309L239 502L276 476L123 309L230 144L185 117Z" />
+<glyph unicode="&#x203A;" horiz-adv-x="259" d="M106 117L70 144L222 309L115 476L160 502L281 309L106 117Z" />
+<glyph unicode="&#x2044;" horiz-adv-x="111" d="M-165 0H-207L362 700H404L-165 0Z" />
+<glyph unicode="&#x2070;" horiz-adv-x="338" d="M370 583Q370 546 363 507T339 436T293 382T219 361Q162 361 139 392T116 477Q116 514 122 553T146 625T193 679T267 700Q324 700 347 669T370 583ZM224 398Q256 398 276 418T306 468T320 530T324 587Q324 620 312 641T262 663Q230 663 210 643T180 594T166 533T162 476Q162 442 174 420T224 398Z" />
+<glyph unicode="&#x2074;" horiz-adv-x="300" d="M285 437L275 367H228L238 437H96L88 475L267 694H321L290 476H340L335 437H285ZM132 474H244L268 646L132 474Z" />
+<glyph unicode="&#x2075;" horiz-adv-x="302" d="M330 505Q330 475 322 449T297 404T255 373T197 361Q166 361 142 368T95 393L122 426Q139 411 157 405T198 398Q239 398 260 425T282 498Q282 554 224 554Q204 554 188 547T151 523L118 549L163 694H336L331 654H192L163 563L166 560Q184 574 201 581T240 588Q283 588 306 568T330 505Z" />
+<glyph unicode="&#x2076;" horiz-adv-x="317" d="M344 495Q344 466 335 442T308 399T268 371T215 361Q162 361 138 388T114 466Q114 476 115 487T118 510Q123 544 132 578T159 639T204 683T273 700Q315 700 352 677L328 644Q302 663 269 663Q242 663 224 652T194 622T176 580T166 535H170Q201 577 254 577Q295 577 319 558T344 495ZM220 398Q259 398 279 423T300 487Q300 516 284 529T239 543Q202 543 181 516T160 455Q160 426 176 412T220 398Z" />
+<glyph unicode="&#x2077;" horiz-adv-x="277" d="M341 650Q307 627 283 605T243 555T216 489T197 396L193 367H144L146 379Q151 418 159 455T183 526T224 592T291 650V654H116L122 694H339L341 650H341Z" />
+<glyph unicode="&#x2078;" horiz-adv-x="316" d="M341 473Q341 444 331 423T302 388T260 368T209 361Q189 361 170 364T137 377T113 401T104 440Q104 514 175 538V542Q137 559 137 597Q137 624 146 642T172 672T210 689T256 695Q273 695 290 692T320 682T342 661T351 627Q351 565 294 542V538Q317 529 329 514T341 473ZM236 555Q267 555 286 571T306 619Q306 644 290 652T251 660Q219 660 201 646T182 598Q182 573 198 564T236 555ZM214 396Q249 396 272 414T296 471Q296 500 277 511T231 522Q195 522 173 503T150 446Q150 417 169 407T214 396Z" />
+<glyph unicode="&#x2079;" horiz-adv-x="320" d="M353 588Q353 549 347 509T323 436T275 382T197 361Q173 361 152 367T111 388L136 420Q152 407 166 402T201 397Q230 397 249 409T279 440T296 483T305 532H301Q287 509 264 497T215 485Q174 485 148 505T122 569Q122 627 159 663T254 700Q309 700 331 670T353 588ZM226 521Q264 521 285 545T307 607Q307 636 292 650T248 664Q211 664 190 638T168 577Q168 549 183 535T226 521Z" />
+<glyph unicode="&#x2080;" horiz-adv-x="338" d="M302 101Q302 64 295 25T271 -46T225 -100T151 -121Q94 -121 71 -90T48 -5Q48 32 54 71T78 143T125 197T199 218Q256 218 279 187T302 101ZM157 -84Q189 -84 208 -64T238 -14T252 48T256 105Q256 138 244 159T194 181Q162 181 143 161T113 113T99 52T95 -4Q95 -37 107 -60T157 -84Z" />
+<glyph unicode="&#x2081;" horiz-adv-x="268" d="M108 -77L141 159H137L62 117L50 151L157 212H193L153 -77H234L228 -115H19L25 -77H108Z" />
+<glyph unicode="&#x2082;" horiz-adv-x="299" d="M27 -115L25 -71Q51 -50 86 -26T152 25T204 78T226 131Q226 151 216 165T176 180Q153 180 133 170T88 137L65 167Q96 194 124 206T186 218Q226 218 249 198T273 143Q273 110 253 80T202 21T137 -31T73 -76H247L242 -115H27Z" />
+<glyph unicode="&#x2083;" horiz-adv-x="292" d="M249 -3Q249 -62 213 -91T120 -121Q91 -121 66 -113T18 -86L42 -52Q77 -84 122 -84Q157 -84 180 -65T203 -8Q203 22 183 29T138 37H104L109 73Q126 73 144 74T179 82T205 101T215 137Q215 161 199 170T161 180Q116 180 78 150L60 183Q88 200 113 209T171 218Q189 218 205 215T233 203T253 180T261 146Q261 117 246 95T201 62V58Q249 46 249 -3Z" />
+<glyph unicode="&#x2084;" horiz-adv-x="300" d="M217 -45L207 -115H160L170 -45H28L21 -7L199 212H253L223 -6H273L267 -45H217ZM64 -8H176L201 164L64 -8Z" />
+<glyph unicode="&#x2085;" horiz-adv-x="302" d="M262 23Q262 -7 254 -33T229 -78T187 -109T129 -121Q98 -121 74 -114T27 -89L54 -56Q71 -71 89 -77T130 -84Q171 -84 192 -57T214 16Q214 72 156 72Q136 72 120 65T83 41L50 67L95 212H268L263 172H124L95 81L98 78Q116 92 133 99T172 106Q215 106 238 86T262 23Z" />
+<glyph unicode="&#x2086;" horiz-adv-x="317" d="M275 13Q275 -16 266 -40T239 -83T199 -111T146 -121Q93 -121 69 -94T45 -16Q45 -6 46 5T49 28Q54 62 63 96T90 157T135 201T204 218Q246 218 283 195L259 162Q233 181 200 181Q173 181 155 170T125 140T107 98T97 53H101Q132 95 185 95Q226 95 250 76T275 13ZM151 -84Q190 -84 210 -59T231 5Q231 34 215 47T170 61Q133 61 112 34T91 -27Q91 -56 107 -70T151 -84Z" />
+<glyph unicode="&#x2087;" horiz-adv-x="277" d="M273 168Q239 145 215 123T175 73T148 7T129 -86L125 -115H76L78 -103Q84 -64 92 -27T115 44T157 110T224 168V172H49L54 212H271L273 168H273Z" />
+<glyph unicode="&#x2088;" horiz-adv-x="316" d="M273 -9Q273 -38 263 -59T234 -94T192 -114T141 -121Q121 -121 102 -118T69 -105T45 -81T36 -42Q36 32 107 56V60Q69 77 69 115Q69 142 78 160T104 190T142 207T188 213Q205 213 222 210T252 200T274 179T283 145Q283 83 226 60V56Q249 47 261 32T273 -9ZM168 73Q199 73 218 89T238 137Q238 162 222 170T183 178Q151 178 133 164T114 116Q114 91 130 82T168 73ZM146 -86Q181 -86 204 -68T228 -11Q228 18 209 29T163 40Q127 40 105 21T82 -36Q82 -65 101 -75T146 -86Z" />
+<glyph unicode="&#x2089;" horiz-adv-x="320" d="M285 106Q285 67 279 27T255 -46T207 -100T129 -121Q105 -121 84 -115T43 -94L68 -62Q84 -75 98 -80T133 -85Q162 -85 181 -73T211 -42T228 1T237 50H233Q219 27 196 15T147 3Q106 3 80 23T54 87Q54 145 91 181T186 218Q241 218 263 188T285 106ZM158 39Q196 39 217 63T239 125Q239 154 224 168T180 182Q143 182 122 156T100 95Q100 67 115 53T158 39Z" />
+<glyph unicode="&#x20AC;" horiz-adv-x="554" d="M156 426Q169 478 188 524T236 604T307 658T411 678Q471 678 513 654T590 580L544 542Q520 584 487 605T405 626Q357 626 326 610T274 567T241 503T218 426H503L498 385H211Q206 359 203 333T197 281H483L477 239H193Q192 220 192 213T192 200Q192 167 197 138T217 87T257 53T320 40Q380 40 420 67T495 140L529 105Q484 51 433 20T310 -11Q254 -11 219 6T164 54T137 126T130 217V239H57L63 281H133Q135 307 138 333T148 385H78L83 426H156Z" />
+<glyph unicode="&#x2122;" horiz-adv-x="596" d="M561 481L582 630L486 514L422 630L401 481H361L392 700H430L495 578L594 700H632L601 481H561ZM258 664L232 481H191L217 664H142L147 700H338L333 664H258Z" />
+<glyph unicode="&#x2153;" horiz-adv-x="690" d="M200 405L234 641H230L155 599L142 633L250 694H286L245 405H326L321 367H112L117 405H200ZM138 0H96L665 700H707L138 0ZM664 112Q664 53 628 24T535 -6Q506 -6 481 2T433 29L457 63Q492 31 537 31Q572 31 595 50T618 107Q618 137 598 144T553 152H519L524 188Q541 188 559 189T594 197T620 216T630 252Q630 276 614 285T576 295Q531 295 493 265L475 298Q503 315 528 324T586 333Q604 333 620 330T648 318T668 295T676 261Q676 232 661 210T616 177V173Q664 161 664 112Z" />
+<glyph unicode="&#x2154;" horiz-adv-x="690" d="M115 367L113 411Q139 432 174 456T240 507T292 560T314 613Q314 633 304 647T264 662Q241 662 221 652T176 619L153 649Q184 676 212 688T274 700Q314 700 337 680T361 625Q361 592 341 562T290 503T225 451T161 406H335L330 367H115ZM138 0H96L665 700H707L138 0ZM664 112Q664 53 628 24T535 -6Q506 -6 481 2T433 29L457 63Q492 31 537 31Q572 31 595 50T618 107Q618 137 598 144T553 152H519L524 188Q541 188 559 189T594 197T620 216T630 252Q630 276 614 285T576 295Q531 295 493 265L475 298Q503 315 528 324T586 333Q604 333 620 330T648 318T668 295T676 261Q676 232 661 210T616 177V173Q664 161 664 112Z" />
+<glyph unicode="&#x215B;" horiz-adv-x="690" d="M200 405L234 641H230L155 599L142 633L250 694H286L245 405H326L321 367H112L117 405H200ZM138 0H96L665 700H707L138 0ZM670 106Q670 77 660 56T631 21T589 1T538 -6Q518 -6 499 -3T466 10T442 34T433 73Q433 147 504 171V175Q466 192 466 230Q466 257 475 275T501 305T539 322T585 328Q602 328 619 325T649 315T671 294T680 260Q680 198 623 175V171Q646 162 658 147T670 106ZM565 188Q596 188 615 204T635 252Q635 277 619 285T580 293Q548 293 530 279T511 231Q511 206 527 197T565 188ZM543 29Q578 29 601 47T625 104Q625 133 606 144T560 155Q524 155 502 136T479 79Q479 50 498 40T543 29Z" />
+<glyph unicode="&#x215C;" horiz-adv-x="690" d="M333 479Q333 420 297 391T204 361Q175 361 150 369T102 396L126 430Q161 398 206 398Q241 398 264 417T287 474Q287 504 267 511T222 519H188L193 555Q210 555 228 556T263 564T289 583T299 619Q299 643 283 652T245 662Q200 662 162 632L144 665Q172 682 197 691T255 700Q273 700 289 697T317 685T337 662T345 628Q345 599 330 577T285 544V540Q333 528 333 479ZM138 0H96L665 700H707L138 0ZM670 106Q670 77 660 56T631 21T589 1T538 -6Q518 -6 499 -3T466 10T442 34T433 73Q433 147 504 171V175Q466 192 466 230Q466 257 475 275T501 305T539 322T585 328Q602 328 619 325T649 315T671 294T680 260Q680 198 623 175V171Q646 162 658 147T670 106ZM565 188Q596 188 615 204T635 252Q635 277 619 285T580 293Q548 293 530 279T511 231Q511 206 527 197T565 188ZM543 29Q578 29 601 47T625 104Q625 133 606 144T560 155Q524 155 502 136T479 79Q479 50 498 40T543 29Z" />
+<glyph unicode="&#x215D;" horiz-adv-x="690" d="M346 505Q346 475 338 449T313 404T271 373T213 361Q182 361 158 368T111 393L138 426Q155 411 173 405T214 398Q255 398 276 425T298 498Q298 554 240 554Q220 554 204 547T167 523L134 549L179 694H352L347 654H208L179 563L182 560Q200 574 217 581T256 588Q299 588 322 568T346 505ZM138 0H96L665 700H707L138 0ZM670 106Q670 77 660 56T631 21T589 1T538 -6Q518 -6 499 -3T466 10T442 34T433 73Q433 147 504 171V175Q466 192 466 230Q466 257 475 275T501 305T539 322T585 328Q602 328 619 325T649 315T671 294T680 260Q680 198 623 175V171Q646 162 658 147T670 106ZM565 188Q596 188 615 204T635 252Q635 277 619 285T580 293Q548 293 530 279T511 231Q511 206 527 197T565 188ZM543 29Q578 29 601 47T625 104Q625 133 606 144T560 155Q524 155 502 136T479 79Q479 50 498 40T543 29Z" />
+<glyph unicode="&#x215E;" horiz-adv-x="690" d="M396 650Q362 627 338 605T298 555T271 489T252 396L248 367H199L201 379Q206 418 214 455T238 526T279 592T346 650V654H171L177 694H394L396 650ZM138 0H96L665 700H707L138 0ZM670 106Q670 77 660 56T631 21T589 1T538 -6Q518 -6 499 -3T466 10T442 34T433 73Q433 147 504 171V175Q466 192 466 230Q466 257 475 275T501 305T539 322T585 328Q602 328 619 325T649 315T671 294T680 260Q680 198 623 175V171Q646 162 658 147T670 106ZM565 188Q596 188 615 204T635 252Q635 277 619 285T580 293Q548 293 530 279T511 231Q511 206 527 197T565 188ZM543 29Q578 29 601 47T625 104Q625 133 606 144T560 155Q524 155 502 136T479 79Q479 50 498 40T543 29Z" />
+<glyph unicode="&#x2212;" horiz-adv-x="554" d="M86 231L94 285H540L532 231H86Z" />
+<glyph unicode="&#x2260;" horiz-adv-x="554" d="M412 312L284 201H528L520 147H229L125 55L92 89L159 147H75L83 201H215L342 312H98L106 366H398L507 463L542 430L467 366H551L543 312H412Z" />
+<glyph unicode="&#x2264;" horiz-adv-x="554" d="M89 256L98 315L568 484L560 427L154 286L520 148L512 92L89 256ZM54 0L61 53H507L500 0H54Z" />
+<glyph unicode="&#x2265;" horiz-adv-x="554" d="M66 92L74 148L480 286L114 427L122 484L544 315L535 256L66 92ZM54 0L61 53H507L500 0H54Z" />
+<glyph unicode="&#x24DF;" horiz-adv-x="906" d="M799 334Q799 262 772 200T698 91T588 17T453 -10Q381 -10 318 17T208 90T134 199T107 334Q107 406 134 468T208 577T318 651T453 678Q525 678 588 651T698 578T772 469T799 334ZM766 334Q766 398 742 454T676 554T577 622T453 647Q386 647 329 622T230 554T165 455T141 334Q141 270 166 214T233 115T332 48T453 23Q517 23 573 47T673 114T741 213T766 334ZM637 421Q637 350 600 318T484 285H375V107H326V551Q360 555 397 556T471 557Q508 557 538 551T591 530T625 488T637 421ZM588 419Q588 449 581 468T558 499T521 515T468 519Q444 519 422 518T375 514V323H468Q494 323 516 326T554 339T579 368T588 419Z" />
+<glyph unicode="&#xF6C3;" horiz-adv-x="362" d="M148 -206H107L151 -61H220L148 -206Z" />
+
+
+</font>
+</defs>
+<text x="40" y="40" font-family="ClearviewATT LtIt" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="ClearviewATT LtIt" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="ClearviewATT LtIt" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="ClearviewATT LtIt" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.ttf b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.ttf
new file mode 100644
index 0000000000..bd7ac47137
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.woff b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.woff
new file mode 100644
index 0000000000..7f8a274165
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/ClearviewATT/ClearviewATT-LtIt.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/AT&T Variation ID.tab b/catalog-ui/app/styles/fonts/OmnesATT/AT&T Variation ID.tab
new file mode 100644
index 0000000000..e0085fd9ac
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/AT&T Variation ID.tab
@@ -0,0 +1,9 @@
+ Variation ID tier
+602 24875 1243116 Omnes_ATT W02 Italic 1243117 38 CUS24875W02
+602 24872 1243119 Omnes_ATT W02 Light 1243120 38 CUS24872W02
+602 24873 1243122 Omnes_ATT W02 Light Italic 1243123 38 CUS24873W02
+602 24876 1243125 Omnes_ATT W02 Medium 1243126 38 CUS24876W02
+602 24877 1243128 Omnes_ATT W02 Medium Italic 1243129 38 CUS24877W02
+602 24874 1243131 Omnes_ATT W02 Regular 1243132 38 CUS24874W02
+602 26730 1418220 Omnes_ATT W02 Bold 1418221 38 CUS26730W02
+602 26731 1418223 Omnes_ATT W02 Bold Italic 1418224 38 CUS26731W02
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.eot b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.eot
new file mode 100644
index 0000000000..da1c1d1ebe
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.svg b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.svg
new file mode 100644
index 0000000000..916075aa05
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.svg
@@ -0,0 +1,3671 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Bold Italic]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated in 2010 by FontLab Studio. Copyright info pending.]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="570" id="OmnesATTW02BoldItalic">
+<font-face font-family="OmnesATT W02 Bold Italic" panose-1="2 0 8 0 0 0 0 9 0 0" ascent="909" descent="-240" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="500" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="170" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="297" d="M149 215Q118 215 106 229T94 276L106 647H357L212 274Q198 244 185 230T149 215ZM16 55Q16 75 23 103T48 149Q54 155 60 159T76 166T97 170T129 171Q171 171 190 156T210 108Q210 88 203 60T178 14Q166 2 150 -3T97 -8Q55 -8 36 7T16 55Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="483" d="M336 647H537L427 369Q418 347 408 338T373 329Q344 329 330 338T317 380L336 647ZM158 329Q129 329 114 338T99 380L110 647H312L210 369Q201 347 191 338T158 329Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="676" d="M95 54Q95 63 97 72T102 93L113 129H7L36 267H156L191 380H87L115 518H235L277 655Q280 662 295 662Q308 662 324 658T354 645T377 623T386 594Q386 575 379 555L368 518H464L507 655Q508 662 525 662Q538 662 554 658T583 645T606 623T615 594Q615 576 609 555L598 518H702L673 380H554L519 267H623L594 129H476L433 -7Q431 -15 415 -15Q402 -15 386 -11T357 3T334 24T325 54Q325 72 331 93L343 129H247L204 -7Q201 -15 186 -15Q173 -15 157 -11T128 3T105 24T95 54ZM392 265L428 382H319L283 265H392Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="572" d="M35 75Q-5 115 -5 162Q-5 184 4 200T24 228T46 244T58 247Q82 216 120 192T199 156L224 271Q198 283 171 298T121 334T85 385T71 456Q71 492 85 524T127 582T199 624T303 643L331 745H478L449 630Q482 621 507 607T553 573Q585 541 585 498Q585 472 575 453T552 422T528 405T514 402Q473 459 411 485L387 369Q415 357 443 342T493 306T529 258T543 193Q543 115 483 64T303 5L280 -98H130L154 15Q112 24 84 39T35 75ZM280 508Q254 508 246 497T237 474Q237 462 244 452T264 433L280 508ZM330 138Q356 143 365 151T375 171Q375 193 345 209L330 138Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="764" d="M35 53Q35 66 42 77T64 103L683 658Q686 661 698 657T723 643T747 621T758 592Q758 569 728 542L109 -14Q106 -16 95 -12T70 1T46 24T35 53ZM212 338Q174 338 145 349T96 378T66 422T56 477Q56 514 67 546T100 603T156 642T235 657Q274 657 303 646T352 617T382 573T392 518Q392 481 381 449T348 392T291 353T212 338ZM218 431Q247 431 262 454T277 512Q277 535 263 549T229 564Q201 564 185 541T169 483Q169 460 183 446T218 431ZM404 130Q404 167 415 199T448 256T504 295T583 310Q622 310 651 299T700 270T730 226T740 171Q740 134 729 102T696 45T639 6T560 -9Q522 -9 493 2T444 31T414 75T404 130ZM517 136Q517 113 531 99T566 84Q595 84 610 107T625 165Q625 188 611 202T577 217Q549 217 533 194T517 136Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="726" d="M2 162Q2 207 15 239T52 296T107 337T175 366Q160 385 151 410T142 464Q142 501 157 536T200 599T271 644T369 661Q411 661 448 650T512 617T554 567T570 501Q570 465 558 438T524 392T473 358T409 334L488 247Q520 278 542 314T575 392H725Q713 316 677 255T594 148L599 143Q616 124 634 113T688 99Q689 99 690 82T683 44T653 6T587 -11Q566 -11 550 -8T521 3T494 22T467 49L463 54Q418 22 364 5T242 -13Q183 -13 138 0T63 37T18 93T2 162ZM334 411Q375 423 397 440T420 490Q420 510 407 524T368 539Q342 539 326 520T309 474Q309 456 314 442T334 411ZM172 193Q172 163 194 142T263 120Q293 120 318 128T367 154L250 281Q172 255 172 193Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="258" d="M158 329Q129 329 114 338T99 380L110 647H312L210 369Q201 347 191 338T158 329Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="441" d="M20 153Q20 209 31 263T64 369T115 466T185 551Q233 599 290 629T414 659Q457 659 481 639T505 580Q505 567 501 555T492 533T482 517T475 511Q426 507 390 490T320 440Q292 411 271 377T237 304T217 227T210 148Q210 94 224 53T273 -23Q287 -37 304 -47T341 -65Q342 -65 344 -70T350 -82T355 -99T358 -118Q358 -153 332 -176T263 -200Q223 -200 188 -184T124 -137Q75 -89 48 -14T20 153Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="441" d="M-42 -52Q8 -48 44 -31T114 20Q142 48 163 82T197 155T217 233T224 312Q224 365 209 407T161 482Q147 496 130 506T92 525Q90 526 83 542T76 577Q76 612 102 635T170 659Q210 659 245 643T310 597Q359 548 386 473T413 306Q413 194 370 90T248 -92Q200 -140 143 -170T20 -200Q-23 -200 -47 -180T-71 -121Q-71 -108 -67 -96T-58 -74T-49 -58T-42 -52Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="433" d="M136 284Q117 294 113 309T123 352L161 423L95 455Q69 469 65 481T66 517L67 521Q73 544 85 551T127 554L204 540L214 613Q219 643 231 651T264 660H268Q288 660 301 652T319 613L328 540L403 554Q433 559 445 551T464 521L465 517Q471 494 467 482T436 455L369 423L408 352Q422 325 415 309T394 284L391 282Q378 272 362 271T323 290L265 349L208 290Q186 269 173 270T140 282L136 284Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="535" d="M180 184H83Q49 184 32 198T15 236Q15 250 20 269T38 301Q48 312 62 317T101 322H209L233 432Q241 470 261 487T305 505H321Q355 505 370 484T377 425L355 322H453Q488 322 504 308T521 270Q521 256 516 237T499 205Q489 195 475 190T436 184H326L302 71Q294 33 274 16T230 -1H214Q180 -1 165 20T157 79L180 184Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="266" d="M48 172Q49 174 72 175T124 170T176 146T200 96Q200 71 189 51T146 -2L42 -113Q33 -121 16 -121T-18 -113T-43 -92T-46 -59L48 172Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="469" d="M61 334H446L409 159H24L61 334Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="259" d="M-4 55Q-4 75 3 103T28 149Q34 155 40 159T56 166T77 170T109 171Q151 171 171 156T191 108Q191 88 184 60T158 14Q146 2 131 -3T78 -8Q35 -8 16 7T-4 55Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="382" d="M-128 -210L333 652H493L32 -210H-128Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="653" d="M31 222Q31 256 36 291T54 379Q87 528 169 594T390 661Q454 661 503 643T585 594T636 519T653 425Q653 391 648 356T630 268Q597 119 515 53T294 -14Q230 -14 181 3T99 52T48 127T31 222ZM229 228Q229 190 251 167T320 144Q367 144 390 173T427 261Q441 321 448 358T455 419Q455 457 433 480T364 503Q317 503 295 474T258 386Q243 326 236 289T229 228Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="420" d="M201 424Q181 411 156 411Q115 411 93 433Q70 456 70 495Q70 526 79 547T100 582T122 602T133 607Q147 595 168 589T210 583Q241 583 266 602T307 656H354Q399 656 418 634T427 562L307 0H112L201 424Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="577" d="M68 0Q36 0 21 28T5 96Q5 155 18 202T56 275Q75 294 99 308T150 333T210 352T279 369Q312 378 338 386T381 404T407 427T416 457Q416 512 338 512Q285 512 249 486T189 415Q188 414 172 416T137 426T102 450T86 495Q86 548 128 590Q161 623 213 642T338 661Q402 661 451 647T533 606T584 545T602 466Q602 412 587 375T540 311T460 266T348 232Q305 222 278 215T233 202T205 190T187 176Q182 171 178 166T172 153H530L498 0H68H68Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="585" d="M34 53Q-3 90 -3 136Q-3 175 16 197T57 229T99 239T119 236Q127 190 161 163T255 136Q306 136 333 156T361 212Q361 235 345 248T293 262H202L232 403H342Q380 403 395 418T412 455Q414 481 389 496T321 511Q290 511 268 504T229 485T202 459T186 431Q185 428 169 428T132 434T96 458T79 509Q79 532 88 552T114 589Q148 623 204 642T332 661Q463 661 531 615T600 490Q600 462 592 436T565 389T519 352T452 329Q503 309 526 275T550 195Q550 158 537 121T491 54T402 6T257 -13Q176 -13 120 5T34 53Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="628" d="M71 145Q68 145 60 153T43 176T28 210T21 252Q21 267 24 280T34 307T52 338T82 375L275 590Q291 609 305 622T336 642T377 653T436 656Q515 656 548 627T566 525L517 290H617L587 145H493L463 0H282L312 145H71ZM350 286L399 522L190 286H350Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="566" d="M25 48Q8 66 1 84T-6 123Q-6 147 4 165T28 196T52 215T66 219Q83 197 101 181T142 153T190 136T252 130Q300 130 329 154T358 214Q358 246 336 264T263 283Q223 283 196 273T151 252Q98 264 74 292T62 381L97 548Q109 601 139 624T219 647H590L556 489H248L227 397Q266 409 318 409Q433 409 485 361T538 232Q538 177 518 132T461 55T370 5T251 -13Q159 -13 105 4T25 48Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="622" d="M35 246Q35 274 40 316T59 404T95 495T151 574Q191 614 246 637T388 661Q458 661 514 642T599 594Q630 563 630 523Q630 490 614 472T579 444T543 434T525 434Q513 461 480 482T398 504Q338 504 302 469T246 366Q278 385 315 396T408 407Q448 407 482 396T542 364T582 313T597 244Q597 191 578 144T521 63T428 8T302 -12Q239 -12 189 7T105 60T53 141T35 246ZM227 236Q221 188 244 159T311 130Q355 130 380 156T405 220Q405 252 383 269T320 287Q291 287 270 278T229 254Q227 244 227 236Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="565" d="M66 72Q66 99 80 120T123 173L391 486H71L106 647H540Q544 647 553 638T573 614T590 579T597 536Q597 501 583 470T531 396L193 -9Q192 -11 172 -11T129 -3T86 21T66 72Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="621" d="M9 167Q9 225 48 271T171 337Q133 354 112 384T90 459Q90 504 111 541T169 605T256 646T365 661Q429 661 478 648T560 611T609 555T626 486Q626 423 591 382T488 322Q534 303 559 271T585 189Q585 143 565 106T506 43T413 2T290 -12Q155 -12 82 35T9 167ZM351 382Q389 382 415 403T441 459Q441 485 419 503T360 521Q322 521 296 499T270 444Q270 417 292 400T351 382ZM208 193Q208 163 231 144T298 125Q340 125 367 148T395 208Q395 237 372 255T305 274Q265 274 237 252T208 193Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="622" d="M53 49Q24 79 24 120Q24 150 38 168T71 196T104 208T120 210Q136 182 170 163T251 144Q312 144 347 176T401 268Q372 254 333 243T247 232Q205 232 170 243T110 275T70 327T56 399Q56 454 76 502T133 585T225 641T349 661Q416 661 466 642T551 589T603 507T621 403Q621 375 616 333T597 245T561 154T505 75Q465 35 410 11T268 -13Q222 -13 189 -9T130 3T87 22T53 49ZM331 352Q360 352 381 361T422 385Q424 390 425 397T426 409Q433 462 409 490T343 519Q299 519 273 492T247 424Q247 390 268 371T331 352Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="266" d="M141 273Q98 273 79 288T59 337Q59 357 66 384T91 431Q97 437 103 441T119 447T140 451T172 453Q214 453 234 438T254 389Q254 369 247 342T221 295Q209 284 194 279T141 273ZM-1 55Q-1 75 6 103T32 149Q38 155 44 159T60 166T81 170T113 171Q155 171 174 156T194 108Q194 88 187 60T162 14Q150 2 134 -3T81 -8Q39 -8 19 7T-1 55Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="274" d="M148 273Q106 273 86 288T66 337Q66 357 73 384T99 431Q111 442 126 447T179 453Q222 453 241 438T261 389Q261 369 254 342T229 295Q217 284 201 279T148 273ZM51 172Q52 174 75 175T127 170T179 146T203 96Q203 71 192 50T150 -2L46 -113Q37 -121 20 -121T-14 -113T-39 -92T-42 -59L51 172Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="453" d="M84 166Q62 179 46 202T30 255Q32 286 51 309T96 344L417 500Q420 501 427 494T441 474T453 445T458 410Q457 384 441 360T382 317L227 249L324 195Q365 172 379 146T391 95Q390 77 383 61T368 33T351 15T340 10L84 166H84Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="535" d="M135 300Q97 300 78 315T58 357Q58 364 59 374T64 397T73 420T87 439Q98 450 113 455T160 460H455Q493 460 512 445T531 403Q531 396 530 386T525 363T517 340T503 321Q492 310 477 305T430 300H135ZM81 48Q43 48 24 63T5 105Q5 112 6 122T11 145T19 168T33 187Q44 198 59 203T106 209H401Q439 209 458 193T478 151Q478 145 477 134T472 112T463 89T449 69Q438 58 423 53T376 48H81Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="453" d="M38 10Q35 9 28 16T14 36T2 65T-3 100Q-2 126 14 150T73 193L228 261L131 315Q90 338 76 364T64 415Q65 433 72 449T87 477T104 495T115 500L371 344Q393 331 409 308T425 255Q423 224 404 201T359 166L38 10H38Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="522" d="M133 214Q142 294 174 338T268 402Q292 411 308 417T333 430T346 444T350 462Q350 484 332 495T286 507Q241 507 210 486T163 427Q163 426 148 429T114 441T81 467T65 511Q65 535 75 556T103 596Q134 627 182 644T299 661Q355 661 401 648T481 612T532 555T550 481Q550 433 534 403T493 353T439 324T384 306Q349 295 324 276T285 214H133ZM109 55Q109 75 116 103T141 149Q147 155 153 159T169 166T190 170T222 171Q264 171 284 156T304 108Q304 88 297 60T271 14Q259 2 244 -3T191 -8Q148 -8 129 7T109 55Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="736" d="M47 280Q47 362 75 432T153 553T273 632T424 661Q491 661 547 642T645 588T708 504T731 395Q731 343 717 301T677 228T615 181T534 164Q476 164 450 184T415 237Q401 206 374 187T307 167Q260 167 232 197T203 281Q203 317 214 350T245 410T294 452T357 468Q383 468 406 456T441 420L449 463H564L531 305Q517 247 564 247Q608 247 634 282T661 391Q661 437 643 475T593 540T516 581T418 596Q352 596 297 573T202 508T139 409T116 284Q116 232 133 189T184 115T266 67T375 50Q423 50 464 66T536 107Q554 122 571 122Q582 122 590 116T603 102T610 87T612 79Q574 37 514 12T369 -14Q292 -14 233 6T132 65T69 158T47 280ZM315 301Q315 281 326 269T356 256Q378 256 395 271T417 313L424 347Q419 361 408 370T379 380Q349 380 332 356T315 301Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="660" d="M109 548Q120 601 151 624T231 647H438Q544 647 599 611T655 503Q655 441 624 400T516 339Q575 323 604 289T633 205Q633 159 618 121T567 56T474 15T332 0H-9L109 548ZM352 386Q449 386 449 459Q448 505 368 505H296L271 386H352ZM325 143Q381 143 406 161T431 221Q431 270 344 270H247L220 143H325Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="647" d="M40 277Q40 353 67 423T144 545T261 629T411 661Q496 661 546 640T626 590Q647 569 656 546T666 506Q666 467 646 444T602 410T557 397T535 397Q519 435 490 457T415 479Q379 479 349 465T296 426T260 368T247 298Q247 241 282 205T373 168Q458 168 504 236Q505 238 521 235T555 220T589 190T604 142Q604 103 564 63Q532 31 479 9T358 -13Q285 -13 226 7T126 65T63 157T40 277Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="682" d="M109 548Q120 601 151 624T231 647H365Q438 647 495 628T593 573T654 488T675 377Q675 303 651 235T576 114T449 31T271 0H-8L109 548ZM296 161Q340 161 373 177T430 221T465 282T477 354Q477 413 443 449T347 485H293L224 161H296Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="571" d="M109 548Q120 601 151 624T231 647H637L600 478H290L268 373H512L477 212H241L196 0H-8L109 548Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="724" d="M40 269Q40 348 68 419T146 544T267 629T423 661Q503 661 561 640T652 586Q689 547 689 504Q689 480 680 464T658 438T630 422T600 415T576 413T565 416Q545 447 511 468T429 490Q390 490 357 475T298 431T258 366T243 284Q241 218 278 178T378 137Q425 137 459 162Q473 173 483 185T502 224H359L391 371H636Q716 371 697 280L640 14Q639 10 622 3T579 -5Q544 -5 521 10T490 59Q461 24 417 6T314 -13Q250 -13 200 6T114 62T59 151T40 269Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="697" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L281 409H485L517 559Q528 607 550 629T615 652H645Q687 652 707 628T718 557L599 0H398L449 241H246L194 0H-8L112 561Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="528" d="M26 43Q-7 76 -7 115Q-7 149 6 170T35 204T65 219T80 221Q97 192 124 175T186 157Q230 157 254 183T292 269L372 647H573L485 234Q460 115 392 51T196 -13Q136 -13 94 2T26 43Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="650" d="M112 561Q122 608 144 630T210 652H240Q282 652 302 628T313 557L275 377L544 661Q545 662 564 658T606 643T648 613T667 565Q667 542 655 519T613 469L436 306L516 173Q539 131 560 115T612 97Q614 97 613 81T601 45T564 9T493 -7Q460 -7 437 1T395 25T362 65T332 121L252 273L194 0H-8L112 561Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="547" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L230 174H420Q461 174 482 157T504 112Q504 105 502 94T497 69T487 44T472 23Q461 12 445 6T394 0H-8L112 561Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="772" d="M110 556Q131 652 219 652H270Q310 652 326 639T348 594L398 339L561 602Q579 632 600 642T656 652H719Q811 652 791 555L672 0H484L561 365L454 174Q449 164 444 156T431 141T407 130T365 126Q335 126 319 130T296 141T288 157T285 176L257 357L180 0H-8L110 556Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="703" d="M111 557Q122 605 145 628T213 652H231Q263 652 282 640T320 596L481 304L554 647H741L623 89Q612 41 589 18T522 -5H510Q479 -5 460 7T422 51L255 354L180 0H-8L111 557Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="614" d="M109 548Q120 601 151 624T231 647H379Q506 647 572 598T639 449Q639 391 621 342T566 258T470 202T332 182H233L191 0H-10L109 548ZM343 328Q389 328 416 353T444 429Q444 496 357 496H296L261 328H343Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="730" d="M41 277Q41 352 66 421T138 543T255 629T413 661Q491 661 548 638T644 575T701 483T720 372Q720 304 698 241T633 125L634 124Q650 101 664 86T694 62Q695 61 692 49T678 22T646 -4T595 -16Q563 -16 542 -9T499 22Q429 -10 344 -10Q266 -10 209 13T115 75T59 167T41 277ZM240 291Q240 237 271 201T360 164Q376 164 391 167L343 237L473 332L510 277Q517 295 521 315T525 358Q525 412 494 448T404 485Q369 485 339 470T287 429T253 367T240 291Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="644" d="M109 548Q120 601 151 624T231 647H393Q527 647 591 598T655 453Q655 411 643 375T606 310T547 262T465 233L501 174Q514 153 525 140T546 118T569 105T597 99Q600 98 599 82T587 46T550 10T476 -7Q449 -7 428 -1T388 19T353 57T318 117L273 202H235L191 0H-9L109 548ZM350 344Q405 344 429 369T453 432Q453 496 370 496H295L262 344H350Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="583" d="M26 58Q-11 97 -11 140Q-11 163 -1 181T23 213T48 232T62 236Q78 216 100 199T149 168T204 147T261 139Q336 139 336 192Q336 214 308 225T239 252Q210 263 182 277T130 313T92 365T77 437Q77 480 93 520T142 592T228 642T354 661Q435 661 491 642T578 592Q610 558 610 518Q610 492 598 472T572 437T545 417T530 413Q498 455 451 481T352 508Q314 508 295 494T275 457Q275 434 306 422T383 392Q415 380 446 366T502 330T542 281T557 213Q557 165 540 124T486 52T395 4T263 -14Q175 -14 117 6T26 58Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="619" d="M258 473H156Q114 473 93 490T72 535Q72 542 73 554T79 578T89 603T103 624Q114 635 130 641T182 647H595Q637 647 658 630T680 585Q680 578 678 567T673 542T663 517T648 496Q637 485 621 479T569 473H459L358 0H157L258 473Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="671" d="M85 583Q84 590 94 601T123 624T164 644T210 652Q252 652 275 627T302 557L337 251L554 655Q554 656 565 656T594 652T630 643T666 625T694 598T705 557Q705 534 696 513T664 457L368 0H199L85 583Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="979" d="M106 599Q106 605 115 614T141 631T178 646T224 652Q275 652 296 622T316 546L310 256L440 522Q457 557 488 573T554 590Q602 590 628 571T655 511L666 252L837 654Q837 655 849 655T880 655T918 648T956 632T986 604T998 560Q998 544 992 523T965 467L726 0H528L511 338L339 0H141L106 599Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="661" d="M-6 86Q-6 131 48 181L225 353L72 647H290L373 449L559 658Q560 659 579 655T621 640T663 610T682 562Q682 516 628 467L452 300L509 196Q531 155 556 130T608 103Q611 103 609 86T593 47T552 8T478 -10Q456 -10 437 -5T401 13T369 50T340 111L305 206L117 -10Q116 -11 97 -7T55 8T13 38T-6 86Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="632" d="M228 255L46 647H260L351 405L527 659Q528 660 551 658T603 646T655 618T679 567Q679 548 667 523T627 468L428 253L373 0H173L228 255Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="591" d="M10 161L354 482H194Q90 482 90 552Q90 569 95 586T106 616T118 638T126 647H638L603 483L259 166H439Q544 166 544 96Q544 79 539 62T528 31T515 9T508 0H-24L10 161Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="406" d="M246 -46Q282 -46 301 -61T320 -101Q320 -114 314 -139T292 -180Q282 -190 268 -194T222 -199H-49L133 658H392Q428 658 447 643T466 603Q466 597 465 587T460 566T451 543T438 525Q428 514 414 510T369 505H278L160 -46H246Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="382" d="M68 652H221L313 -210H160L68 652Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="406" d="M121 -46L238 505H153Q116 505 98 520T79 560Q79 573 85 598T106 639Q117 649 131 653T176 658H447L265 -199H6Q-30 -199 -49 -184T-68 -144Q-68 -138 -67 -128T-62 -107T-53 -85T-40 -66Q-30 -55 -16 -51T30 -46H121Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="500" d="M196 438Q169 438 154 455T139 495Q139 521 156 545T203 591Q233 613 268 628T335 651Q353 645 377 629T423 591Q443 570 456 548T469 505Q469 476 451 457T403 438Q379 438 359 453T325 496Q322 503 319 511T312 528Q306 520 301 512T288 496Q268 468 246 453T196 438Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="516" d="M375 0Q408 0 425 -13T442 -49Q442 -61 437 -84T417 -120Q407 -130 395 -134T354 -139H6Q-27 -139 -44 -126T-61 -89Q-61 -83 -60 -74T-55 -55T-48 -35T-36 -19Q-26 -9 -14 -5T27 0H375Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="500" d="M212 579Q186 594 171 617T156 664Q156 698 178 721T237 745Q269 745 296 729Q321 714 341 689T376 639T397 594T400 567Q396 561 378 555T334 548T275 554T212 579Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="628" d="M138 697H329L274 438Q299 470 334 489T416 508Q456 508 491 494T551 453T591 388T606 300Q606 240 589 184T538 85T459 15T354 -12Q303 -12 259 11T192 80Q172 35 141 16T59 -4Q45 -4 33 -2T12 5T-2 12T-6 19L138 697ZM317 134Q342 134 361 145T394 175T414 218T421 270Q421 310 400 334T340 359Q313 359 289 346T247 311L235 256Q224 202 245 168T317 134Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="512" d="M18 214Q18 274 40 327T100 420T190 484T303 508Q368 508 412 491T483 447Q501 429 509 409T518 370Q518 335 500 316T460 289T420 280T400 283Q394 315 372 337T307 359Q281 359 260 348T223 319T199 278T190 230Q190 190 214 164T285 137Q347 137 380 193Q381 195 394 193T423 182T452 157T465 117Q465 78 430 45Q404 19 363 3T268 -13Q211 -13 165 2T87 46T36 118T18 214Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="640" d="M24 197Q24 257 41 313T92 412T171 482T276 508Q323 508 365 488T433 427L490 697H682L563 135Q552 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 406 16T375 89Q351 44 310 16T213 -12Q172 -12 138 2T78 43T39 109T24 197ZM208 224Q208 185 229 161T287 137Q324 137 353 163T393 238L406 299Q395 328 375 345T317 362Q290 362 270 351T236 320T215 276T208 224Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="416" d="M116 351H33L63 496H146L150 513Q162 570 183 608T233 668T294 700T365 709Q407 709 435 700T481 673Q502 652 502 622Q502 604 495 589T480 563T465 547T456 542Q446 553 430 560T393 567Q370 567 356 556T334 514L330 496H444L413 351H308L233 0H41L116 351Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="629" d="M-3 -61Q-3 -37 9 -18T36 13T64 30T79 33Q102 -5 136 -25T224 -46Q284 -46 314 -20T355 58L363 96Q334 61 298 45T213 28Q172 28 138 42T79 83T41 146T27 229Q27 281 44 331T94 420T175 484T285 508Q329 508 369 490T435 435L448 496H640L546 57Q532 -9 509 -57T447 -137T353 -184T221 -200Q181 -200 151 -195T97 -181T58 -161T29 -137Q-3 -106 -3 -61ZM208 249Q208 214 230 193T289 171Q331 171 360 194T397 254L408 307Q397 332 375 346T321 361Q294 361 273 352T238 327T216 291T208 249Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="611" d="M120 612Q131 664 151 684T208 704H228Q247 704 265 700T295 685T312 657T311 612L269 412Q296 455 339 481T436 508Q505 508 544 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L120 612Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="298" d="M225 552Q177 552 151 574T125 631Q125 678 155 705T231 733Q279 733 305 711T331 654Q331 608 301 580T225 552ZM143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="301" d="M240 552Q193 552 167 574T141 631Q141 678 171 705T247 733Q295 733 320 711T346 654Q346 608 316 580T240 552ZM116 496H308L197 -21Q177 -113 135 -157T9 -201Q-31 -201 -59 -192T-103 -166Q-125 -144 -125 -111Q-125 -95 -120 -81T-109 -57T-98 -41T-92 -35Q-84 -43 -68 -49T-38 -55Q-20 -55 -9 -44T10 -2L116 496Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="556" d="M138 697H329Q306 588 288 504Q280 467 273 433T260 371Q253 339 247 311L449 508Q450 509 466 506T502 492T537 462T553 412Q553 387 542 368T507 330L393 238L428 172Q438 149 447 134T466 109T488 96T516 90Q518 90 517 74T505 40T471 6T407 -10Q381 -10 363 -4T331 15T304 50T279 103L228 221Q225 205 221 187Q218 171 214 151T205 111Q194 63 182 0H-10L138 697Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="298" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L143 697H335L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="917" d="M93 488Q94 491 110 495T149 500Q167 500 184 495T216 479T239 451T252 408Q284 455 327 481T426 508Q477 508 515 480T565 403Q597 451 640 479T743 508Q777 508 803 496T849 464T878 415T888 353Q888 327 881 293T860 210Q850 175 844 147T837 104Q837 82 846 72T876 56Q877 56 873 46T857 22T822 -1T762 -12Q699 -12 672 14T644 86Q644 108 649 133T667 202Q676 234 681 257T686 293Q686 321 672 337T634 353Q614 353 598 344T565 313L499 0H307L364 270Q372 306 359 329T318 353Q297 353 281 344T249 316L182 0H-10L93 488Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="611" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q284 453 328 480T432 508Q504 508 543 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L93 488Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="628" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 404Q276 451 317 479T416 508Q456 508 491 494T551 453T591 388T606 300Q606 240 589 184T538 85T459 15T354 -12Q300 -12 260 9T197 72L142 -186H-50L93 488ZM317 134Q342 134 361 145T394 175T414 218T421 270Q421 310 400 334T340 359Q313 359 289 346T247 310L235 256Q224 202 245 168T317 134Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="629" d="M24 197Q24 257 41 313T92 412T171 482T276 508Q325 508 370 486T438 418Q458 462 489 481T570 500Q598 500 617 491T636 477L495 -186H303L354 56Q328 24 293 6T213 -12Q172 -12 138 2T78 43T39 109T24 197ZM208 224Q208 185 229 161T287 137Q324 137 353 163T393 238L406 299Q395 328 375 345T317 362Q290 362 270 351T236 320T215 276T208 224Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="465" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q273 452 308 480T389 508Q431 508 458 485T486 424Q486 389 473 366T443 328T414 308T399 303Q388 316 372 325T328 334Q287 334 263 299T224 201L181 0H-11L93 488Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="481" d="M18 54Q4 68 -4 84T-13 118Q-13 137 -5 152T13 178T32 193T43 196Q57 181 76 167T116 142T159 125T203 118Q249 118 249 146Q249 157 236 165T195 184Q169 194 144 206T99 234T67 274T54 332Q54 370 67 402T108 457T174 493T266 506Q330 506 380 488T461 440Q472 429 479 415T486 383Q486 360 477 344T456 317T435 301T423 298Q397 330 359 354T279 378Q255 378 243 370T231 349Q231 338 246 330T298 306Q328 294 355 281T402 251T435 211T447 155Q447 80 392 35T228 -11Q151 -11 99 7T18 54Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="433" d="M111 351H35L65 496H142L145 511Q158 576 189 606T285 637Q316 637 336 628T355 614L330 496H455L424 351H296L262 189Q255 157 265 143T298 128Q314 128 328 134T354 150Q357 153 369 140T382 99Q382 58 351 27Q312 -14 229 -14Q133 -14 95 37T75 180L111 351Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="568" d="M94 322Q90 350 84 368T68 398T48 414T24 421Q22 421 26 434T43 464T79 493T140 506Q191 506 221 476Q243 454 253 420T266 335L275 204L443 505Q443 506 464 504T509 492T554 463T575 413Q575 390 567 372T538 322L318 0H143L94 322Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="805" d="M94 329Q92 378 74 398T28 422Q26 422 29 435T43 464T77 493T136 506Q189 506 218 478Q256 439 256 339L255 202L352 407Q367 439 388 450T441 462Q485 462 510 446T538 389L547 200L686 505Q687 506 707 505T750 495T794 469T814 419Q814 402 808 386T785 340L592 0H435L408 242L271 0H115L94 329Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="556" d="M84 -11Q83 -12 67 -7T32 9T-3 40T-19 86Q-19 112 -4 134T37 176L169 270L39 495H240L309 349L454 507Q455 508 471 503T507 487T541 456T557 410Q557 384 543 362T502 320L378 219L399 184Q412 160 426 145T455 122T483 110T511 105Q513 105 510 87T493 48T452 10T380 -8Q359 -8 341 -4T307 12T277 44T247 98L228 140L84 -11H84Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="572" d="M-7 -38Q1 -45 17 -50T47 -55Q74 -55 98 -39T155 24L165 38Q130 49 123 96L94 322Q86 377 68 397T23 421Q21 421 25 434T41 464T77 493T139 506Q195 506 227 466T266 335L275 208L425 496H627L306 -45Q283 -81 261 -109T214 -158T160 -190T96 -201Q22 -201 -12 -168Q-36 -144 -36 -103Q-36 -90 -32 -78T-23 -57T-13 -43T-7 -38Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="516" d="M4 135L269 352H157Q66 352 66 413Q66 428 70 442T80 469T91 488T97 496H532L506 361L241 144H371Q462 144 462 83Q462 68 458 53T448 27T437 8T431 0H-22L4 135Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="497" d="M80 102Q60 115 48 126T28 149T17 172T14 201Q14 234 23 269T53 324Q59 330 65 334T80 343T102 352T135 363L165 374Q185 448 213 498T276 584Q315 623 361 641T456 659Q507 659 530 639T554 581Q554 567 550 555T540 533T529 519T524 514Q479 513 449 502T396 468Q370 442 353 399T320 292L189 229L291 156Q282 84 287 42T316 -25Q339 -48 392 -58Q394 -58 400 -70T406 -104Q406 -150 381 -175T301 -200Q261 -200 232 -188T175 -150Q140 -115 121 -56T104 86L80 102H80Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="290" d="M117 567Q128 614 150 636T211 659H240Q277 659 297 636T308 566L166 -107Q155 -154 133 -176T71 -199H43Q5 -199 -15 -176T-26 -106L117 567Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="497" d="M-36 -55Q9 -54 39 -43T92 -9Q118 17 135 60T168 167L299 230L197 304Q206 375 201 418T172 484Q149 507 96 517Q94 517 88 529T82 563Q82 609 108 634T187 659Q228 659 257 647T313 609Q348 574 367 515T384 374L408 357Q428 343 441 332T461 310T472 287T475 258Q475 225 465 191T435 136Q429 130 423 126T408 117T386 107T353 96L323 85Q303 11 275 -39T212 -125Q173 -164 127 -182T32 -200Q-19 -200 -42 -180T-66 -122Q-66 -108 -62 -96T-52 -74T-41 -60T-36 -55Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="499" d="M119 155Q94 155 80 170T64 206Q65 235 75 258T101 297T139 321T182 330Q208 330 227 323T260 305Q277 294 286 286T303 278Q313 278 320 287T336 308Q344 319 353 323T377 327Q402 327 416 312T432 276Q431 247 421 225T395 186T357 162T314 153Q287 153 269 161T238 178Q221 191 213 201T193 212Q183 212 178 202T165 177Q159 167 149 161T119 155Z" />
+<glyph unicode="&#xA0;" glyph-name="nbspace" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="297" d="M201 477Q159 477 139 492T119 540Q119 560 126 588T152 634Q158 640 164 644T179 651T200 655T232 656Q274 656 294 641T314 593Q314 573 307 545T281 499Q270 487 254 482T201 477ZM118 374Q131 404 144 418T181 433Q212 433 223 419T235 372L224 1H-28L118 374Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="541" d="M170 35Q104 60 67 112T29 246Q29 303 47 353T99 443T178 509T278 544L285 574Q296 627 315 647T365 667H370Q386 667 402 663T429 648T446 619T445 574L437 535Q489 519 521 487Q538 470 546 449T555 406Q555 370 535 352T491 327T447 321T425 325Q417 359 395 377L352 175Q383 186 401 217Q402 219 417 217T451 205T484 179T499 135Q499 118 492 102T468 69Q447 48 412 34T326 16L321 -11Q310 -63 291 -83T241 -103H236Q220 -103 204 -99T177 -84T160 -56T161 -11L170 35ZM250 368Q228 349 217 321T205 262Q205 234 217 212L250 368Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="609" d="M-12 54Q-12 60 -11 70T-6 92T3 113T16 131Q27 141 40 146T78 151H93L119 239H98Q75 239 61 251T47 284Q47 298 53 320T72 356Q81 365 93 369T133 374H162L185 451Q218 560 278 610T440 661Q540 661 590 611Q622 580 622 536Q622 504 607 485T574 457T541 445T525 443Q515 478 493 493T439 509Q403 509 380 488T339 410L328 374H430Q462 374 478 361T495 326Q495 320 494 311T489 292T482 273T470 257Q461 248 449 244T409 239H286L259 151H481Q576 151 576 87Q576 71 572 56T561 28T550 8T544 0H61Q26 0 7 15T-12 54Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="634" d="M70 196Q41 196 27 207T12 237Q12 268 30 286Q49 305 82 305H105Q107 313 108 322T112 342H101Q72 342 58 353T43 383Q43 414 61 432Q80 451 113 451H144Q158 484 175 514T214 566Q265 616 322 638T451 661Q511 661 559 644T635 600Q652 583 659 564T666 524Q666 490 651 470T618 439T585 425T568 424Q547 461 517 480T434 499Q403 499 385 491T349 464L338 451H452Q481 451 495 439T510 409Q510 378 492 360Q474 342 440 342H298Q296 332 295 323T291 305H421Q450 305 464 293T479 263Q479 232 461 214Q443 196 409 196H293Q318 149 390 149Q410 149 426 152T458 162T487 180T517 207Q519 209 531 204T556 188T581 161T592 124Q592 83 560 51Q531 22 480 5T369 -13Q255 -13 184 42T100 196H70H70Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="640" d="M82 101Q50 101 35 113T19 146Q19 162 23 176T38 200Q58 220 95 220H229L236 255H116Q84 255 68 267T52 300Q52 335 72 355Q92 375 129 375H196L94 580Q92 584 98 596T119 620T156 642T208 652Q239 652 261 633T298 571L350 425L532 659Q533 660 552 658T593 646T634 618T653 567Q653 548 641 522T599 468L505 375H557Q589 375 604 362T620 329Q620 313 616 300T601 275Q581 255 544 255H413L405 220H523Q555 220 571 208T587 175Q587 159 583 145T567 120Q548 101 510 101H380L377 86Q367 39 344 17T278 -5H273Q232 -5 212 18T201 89L203 101H82H82Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="601" d="M42 19Q55 4 77 -10T125 -34T180 -51T236 -58Q271 -58 287 -46T304 -16Q304 4 276 15T209 41Q179 51 145 63T83 94T35 137T16 199Q16 259 52 302T149 364Q127 381 115 403T102 456Q102 501 120 538T173 603T255 645T361 660Q442 660 492 643T568 600Q594 574 594 543Q594 519 583 502T559 473T534 457T520 453Q492 485 452 506T363 528Q295 528 295 488Q295 468 322 456T390 430Q421 420 454 408T515 378T561 335T579 274Q579 214 545 174T449 110Q470 94 483 72T497 16Q497 -26 481 -63T430 -128T343 -173T221 -190Q133 -190 81 -174T0 -130Q-27 -103 -27 -68Q-27 -47 -17 -31T5 -3T29 14T42 19ZM185 249Q185 232 194 221T218 200T254 184T299 170L340 159Q370 164 390 181T410 224Q410 240 402 251T378 271T343 286T300 300L254 314Q223 307 204 290T185 249Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="500" d="M439 541Q396 541 375 562T353 615Q353 661 381 688T449 716Q492 716 513 695T534 641Q534 596 506 569T439 541ZM213 541Q170 541 149 562T128 615Q128 661 156 688T223 716Q266 716 287 695T309 641Q309 596 281 569T213 541Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="747" d="M52 324Q52 397 76 459T146 566T253 636T390 661Q465 661 526 636T632 567T701 460T726 324Q726 250 702 188T633 81T526 11T389 -14Q314 -14 252 11T146 81T77 188T52 324ZM123 324Q123 265 142 216T197 130T281 74T390 53Q450 53 498 73T582 129T636 215T655 324Q655 383 636 432T581 518T497 574T389 594Q329 594 280 574T196 518T142 433T123 324ZM212 323Q212 363 226 396T264 454T323 492T399 506Q446 506 474 493T517 466Q525 457 530 448T534 423Q534 408 526 397T507 378T487 367T477 365Q461 384 445 392T404 400Q374 400 353 379T331 323Q331 288 352 267T405 245Q430 245 446 253T478 281Q479 282 488 279T507 269T526 250T535 223Q536 209 531 199T517 178Q503 164 475 151T399 138Q358 138 324 151T264 190T226 248T212 323Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="541" d="M195 245Q131 245 91 288T51 404Q51 453 66 499T110 582T177 640T264 662Q302 662 335 645T386 592Q402 626 427 640T491 655Q514 655 529 648T544 636L486 363Q478 327 484 311T510 288Q511 288 508 281T497 266T472 252T429 245Q381 245 358 268T333 328Q313 291 278 268T195 245ZM260 366Q292 366 316 387T348 447L358 494Q350 516 334 529T292 543Q271 543 254 534T226 508T208 472T201 430Q201 401 217 384T260 366ZM126 48Q126 66 132 91T156 133Q167 144 180 148T228 153Q266 153 284 140T302 96Q302 78 296 53T272 11Q261 0 248 -4T200 -9Q162 -9 144 4T126 48Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="554" d="M82 144Q70 162 60 180T42 214T30 241T26 256Q26 259 32 269T51 295T78 327T112 361Q144 390 177 412T240 434Q269 434 285 419T302 376Q302 349 283 326T232 283Q217 272 200 263T162 245Q175 238 187 230T211 210Q235 189 249 170T264 130Q264 101 244 83T192 64Q160 64 133 88T82 144ZM316 141Q305 159 296 176T281 209T272 235T268 250Q268 253 275 264T294 290T322 322T357 357Q389 386 424 409T489 432Q522 432 539 417T556 374Q556 346 535 322T481 277Q446 256 407 239Q419 231 429 224T450 208Q473 189 486 169T500 128Q500 99 480 80T423 60Q391 60 364 84T316 141Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="747" d="M52 324Q52 397 76 459T146 566T253 636T390 661Q465 661 526 636T632 567T701 460T726 324Q726 250 702 188T633 81T526 11T389 -14Q314 -14 252 11T146 81T77 188T52 324ZM123 324Q123 265 142 216T197 130T281 74T390 53Q450 53 498 73T582 129T636 215T655 324Q655 383 636 432T581 518T497 574T389 594Q329 594 280 574T196 518T142 433T123 324ZM236 438Q236 502 296 502H403Q557 502 557 384Q557 346 539 321T489 283L559 181Q561 178 556 172T541 159T518 147T492 141Q475 141 458 150T426 185L379 257H352V198Q352 172 340 158T301 144H285Q261 144 249 162T236 212V438ZM395 338Q441 338 441 376Q441 414 393 414H350V338H395Z" />
+<glyph unicode="&#xAF;" glyph-name="overscore" horiz-adv-x="500" d="M200 546Q167 546 150 559T133 595Q133 601 134 610T139 629T146 649T158 666Q168 675 180 679T221 684H456Q489 684 506 671T523 635Q523 623 518 601T498 564Q488 554 476 550T435 546H200H200Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="384" d="M218 336Q183 336 155 345T105 372T73 415T61 471Q61 504 71 537T104 597T162 641T247 658Q281 658 310 649T360 622T392 580T404 524Q404 491 394 458T361 397T303 353T218 336ZM225 426Q242 426 255 435T276 459T289 489T293 518Q293 543 278 555T240 568Q223 568 210 559T189 536T176 506T172 477Q172 452 187 439T225 426Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="600" d="M292 211Q259 211 244 232T236 291L249 353H157Q123 353 106 367T89 405Q89 419 94 438T112 470Q122 480 136 485T175 490H278L298 581Q306 619 326 636T370 653H386Q420 653 435 632T442 573L424 490H519Q554 490 570 476T587 438Q587 424 582 405T565 373Q555 363 541 358T502 353H395L380 284Q372 246 352 229T309 211H292ZM18 82Q18 96 23 116T41 148Q51 158 65 163T104 168H451Q486 168 503 154T520 116Q520 102 515 82T497 50Q487 40 473 35T434 30H86Q52 30 35 44T18 82Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="336" d="M51 285Q19 285 10 303T7 353Q12 383 22 404T47 441Q66 461 90 472T140 491Q175 501 196 509T217 536Q217 547 208 554T180 561Q153 561 137 545T113 508Q113 508 102 508T77 512T52 528T41 561Q41 578 49 592T69 618Q88 637 121 646T196 656Q261 656 300 627T339 546Q339 516 329 496T303 462T265 440T219 427Q187 419 162 411T120 387Q118 385 117 384T114 380H251Q312 380 312 340Q312 330 309 320T302 303T295 290T290 285H51H51Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="336" d="M149 278Q102 278 70 288T21 315Q10 326 6 337T2 361Q2 385 13 399T39 419T64 425T77 423Q80 400 97 386T147 371Q171 371 185 380T200 406Q200 426 171 426H161Q118 426 118 463Q118 471 121 482T132 500Q140 509 152 511T188 514H202Q215 514 221 520T229 535Q230 547 218 554T182 562Q148 562 133 548T112 518Q112 516 101 516T78 519T55 535T44 567Q44 581 49 593T64 614Q84 634 116 645T190 656Q265 656 304 629T344 559Q344 527 325 502T260 469Q315 446 315 394Q315 371 307 351T279 314T228 288T149 278Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="500" d="M235 567Q232 573 239 594T262 639T300 688T348 727Q375 742 408 742Q444 742 466 718T488 661Q488 637 472 614T429 576Q396 558 363 552T303 547T257 555T235 567Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="617" d="M542 599Q547 625 559 636T595 648H611Q634 648 644 635T649 595L533 45Q527 19 515 8T478 -4H463Q440 -4 430 9T425 49L542 599ZM311 265H271Q162 265 107 305T52 427Q52 475 68 515T116 585T196 630T309 647H437Q465 647 479 631T487 585L372 45Q367 19 354 8T318 -4H303Q280 -4 270 9T265 49L311 265Z" />
+<glyph unicode="&#xB7;" glyph-name="middot" horiz-adv-x="262" d="M115 158Q73 158 54 173T34 221Q34 241 41 269T66 315Q72 321 78 325T94 332T115 336T147 337Q189 337 208 322T228 274Q228 254 221 227T196 180Q184 168 168 163T115 158Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="500" d="M139 -39Q140 -38 159 -38T202 -43T245 -62T265 -100Q265 -125 250 -142T204 -179L120 -234Q112 -240 97 -238T69 -229T49 -211T50 -186L139 -39H139Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="336" d="M63 286Q38 286 25 296T13 324Q13 332 17 348T31 374Q38 382 47 385T79 389H115L146 519Q126 505 104 505Q78 505 66 519T53 557Q53 576 59 589T72 611T85 623T92 627Q111 611 139 611Q156 611 170 622T194 653H238Q294 653 279 594L229 389H260Q284 389 297 379T310 351Q310 343 306 327T291 300Q285 292 275 289T243 286H63H63Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="458" d="M249 244Q200 244 164 258T103 297T67 355T55 426Q55 471 69 513T112 588T183 641T279 661Q328 661 365 647T426 608T463 550T475 480Q475 435 460 393T417 317T346 264T249 244ZM253 359Q274 359 291 369T320 395T338 431T345 474Q345 505 326 526T276 547Q255 547 238 537T210 511T192 475T185 432Q185 400 204 380T253 359ZM95 48Q95 66 101 91T125 133Q136 144 149 148T197 153Q235 153 253 140T271 96Q271 78 265 53T241 11Q230 0 217 -4T169 -9Q131 -9 113 4T95 48Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="554" d="M64 61Q31 61 14 76T-4 119Q-4 147 17 171T71 216Q88 226 106 235T146 254Q131 262 117 272T91 294Q72 313 63 330T53 365Q53 394 73 413T130 433Q162 433 189 409T237 352Q248 334 257 317T272 284T281 258T285 243Q285 240 278 229T259 203T230 171T196 136Q164 106 129 84T64 61ZM250 117Q250 143 268 165T316 207Q332 218 351 228T391 248Q358 266 329 295Q310 315 300 331T289 363Q289 392 308 410T361 429Q393 429 420 405T470 349Q483 331 493 313T510 279T522 252T526 237Q526 234 520 224T502 198T474 166T440 132Q409 103 376 81T313 59Q284 59 267 74T250 117Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="792" d="M63 286Q38 286 25 296T13 324Q13 332 17 348T31 374Q38 382 47 385T79 389H115L146 519Q126 505 104 505Q78 505 66 519T53 557Q53 576 59 589T72 611T85 623T92 627Q111 611 139 611Q156 611 170 622T194 653H238Q294 653 279 594L229 389H260Q284 389 297 379T310 351Q310 343 306 327T291 300Q285 292 275 289T243 286H63ZM80 22Q84 34 91 45T115 72Q117 74 142 98T204 157T284 233T366 312Q385 331 409 355T461 405T517 458T573 512Q636 573 704 638Q707 641 716 639T733 630T747 612T748 584Q746 573 739 561T715 534Q713 532 688 508T625 449T545 371T464 292Q444 273 420 249T368 199T312 145T257 91Q195 30 127 -34Q123 -37 114 -35T96 -25T82 -6T80 22ZM467 139Q467 157 477 174T508 219L591 328Q600 340 608 347T626 359T651 366T688 368Q737 368 757 351T766 286L741 167H750Q771 167 782 158T793 135Q793 127 790 113T777 89Q771 83 763 80T736 77H722L717 53Q710 20 698 8T666 -5H651Q640 -5 631 -2T614 7T605 25T606 54L611 77H502Q500 77 494 81T482 94T472 113T467 139ZM636 164L663 291L563 164H636Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="792" d="M63 286Q38 286 25 296T13 324Q13 332 17 348T31 374Q38 382 47 385T79 389H115L146 519Q126 505 104 505Q78 505 66 519T53 557Q53 576 59 589T72 611T85 623T92 627Q111 611 139 611Q156 611 170 622T194 653H238Q294 653 279 594L229 389H260Q284 389 297 379T310 351Q310 343 306 327T291 300Q285 292 275 289T243 286H63ZM58 22Q62 34 69 45T93 72Q95 74 120 98T182 157T262 233T344 312Q363 331 387 355T439 405T495 458T551 512Q614 573 682 638Q685 641 694 639T711 630T725 612T726 584Q724 573 717 561T693 534Q691 532 666 508T603 449T523 371T442 292Q422 273 398 249T346 199T290 145T235 91Q173 30 105 -34Q101 -37 92 -35T74 -25T60 -6T58 22ZM463 68Q468 98 478 119T503 156Q522 176 546 187T596 206Q631 216 652 224T673 251Q673 262 664 269T636 276Q609 276 593 260T569 223Q569 223 558 223T533 227T508 243T497 276Q497 293 505 307T525 333Q544 352 577 361T652 371Q717 371 756 342T795 261Q795 231 785 211T759 177T721 155T675 142Q643 134 618 126T576 102Q574 100 573 99T570 95H707Q768 95 768 55Q768 45 765 35T758 18T751 5T746 0H507Q475 0 466 18T463 68Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="792" d="M149 278Q102 278 70 288T21 315Q10 326 6 337T2 361Q2 385 13 399T39 419T64 425T77 423Q80 400 97 386T147 371Q171 371 185 380T200 406Q200 426 171 426H161Q118 426 118 463Q118 471 121 482T132 500Q140 509 152 511T188 514H202Q215 514 221 520T229 535Q230 547 218 554T182 562Q148 562 133 548T112 518Q112 516 101 516T78 519T55 535T44 567Q44 581 49 593T64 614Q84 634 116 645T190 656Q265 656 304 629T344 559Q344 527 325 502T260 469Q315 446 315 394Q315 371 307 351T279 314T228 288T149 278ZM65 22Q69 34 76 45T100 72Q102 74 127 98T189 157T269 233T351 312Q370 331 394 355T446 405T502 458T558 512Q621 573 689 638Q692 641 701 639T718 630T732 612T733 584Q731 573 724 561T700 534Q698 532 673 508T610 449T530 371T449 292Q429 273 405 249T353 199T297 145T242 91Q180 30 112 -34Q108 -37 99 -35T81 -25T67 -6T65 22ZM467 139Q467 157 477 174T508 219L591 328Q600 340 608 347T626 359T651 366T688 368Q737 368 757 351T766 286L741 167H750Q771 167 782 158T793 135Q793 127 790 113T777 89Q771 83 763 80T736 77H722L717 53Q710 20 698 8T666 -5H651Q640 -5 631 -2T614 7T605 25T606 54L611 77H502Q500 77 494 81T482 94T472 113T467 139ZM636 164L663 291L563 164H636Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="522" d="M331 476Q289 476 269 491T249 540Q249 560 256 587T282 634Q288 640 294 644T309 650T330 654T362 656Q404 656 424 641T444 592Q444 572 437 545T411 498Q400 487 384 482T331 476ZM3 167Q3 215 19 245T60 294T114 323T169 342Q204 353 228 372T268 434H419Q411 353 379 310T285 245Q260 237 244 231T219 218T207 204T203 186Q203 163 221 152T266 141Q311 141 343 162T390 221Q390 221 405 219T438 207T472 181T487 137Q487 87 450 52Q418 20 370 3T253 -14Q197 -14 151 -1T72 36T21 93T3 167Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM297 734Q271 749 256 772T241 819Q241 853 263 876T322 900Q354 900 381 884Q406 869 426 844T461 794T482 749T485 722Q481 716 463 710T419 703T360 709T297 734Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM347 722Q344 728 351 749T374 794T412 843T460 882Q487 897 520 897Q556 897 578 873T600 816Q600 792 584 769T541 731Q508 713 475 707T415 702T369 710T347 722Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM326 696Q299 696 284 713T269 753Q269 779 286 803T333 849Q363 871 398 886T465 909Q483 903 507 887T553 849Q573 828 586 806T599 763Q599 734 581 715T533 696Q509 696 489 711T455 754Q452 761 449 769T442 786Q436 778 431 770T418 754Q398 726 376 711T326 696Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM306 697Q281 697 267 712T251 748Q252 777 262 800T288 839T326 863T369 872Q395 872 414 865T447 847Q464 836 473 828T490 820Q500 820 507 829T523 850Q531 861 540 865T564 869Q589 869 603 854T619 818Q618 789 608 767T582 728T544 704T501 695Q474 695 456 703T425 720Q408 733 400 743T380 754Q370 754 365 744T352 719Q346 709 336 703T306 697Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM554 696Q511 696 490 717T468 770Q468 816 496 843T564 871Q607 871 628 850T649 796Q649 751 621 724T554 696ZM328 696Q285 696 264 717T243 770Q243 816 271 843T338 871Q381 871 402 850T424 796Q424 751 396 724T328 696Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="692" d="M286 590Q296 606 309 617T344 636Q309 665 309 713Q309 740 319 765T347 809T392 839T449 850Q507 850 541 821T575 745Q575 709 558 678T510 628Q524 620 531 610T540 588L649 0H445L425 134H217L144 0H-64L286 590ZM437 676Q460 676 474 693T489 731Q489 749 478 761T447 773Q424 773 410 756T395 718Q395 700 406 688T437 676ZM419 278L388 464L279 278H419Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="895" d="M-5 77Q-5 103 4 123T35 179L283 585Q292 600 302 611T328 631T364 643T414 647H929L892 475H556L571 405H806L775 250H598L613 168H834L798 0H541Q488 0 464 19T433 85L425 134H217L139 -10Q138 -12 116 -12T66 -3T17 24T-5 77ZM419 278L387 464L279 278H419Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="647" d="M40 277Q40 353 67 423T144 545T261 629T411 661Q496 661 546 640T626 590Q647 569 656 546T666 506Q666 467 646 444T602 410T557 397T535 397Q519 435 490 457T415 479Q379 479 349 465T296 426T260 368T247 298Q247 241 282 205T373 168Q458 168 504 236Q505 238 521 235T555 220T589 190T604 142Q604 103 564 63Q532 31 479 9T358 -13Q285 -13 226 7T126 65T63 157T40 277ZM265 -39Q266 -38 285 -38T328 -43T371 -62T391 -100Q391 -125 376 -142T330 -179L246 -234Q238 -240 223 -238T195 -229T175 -211T176 -186L265 -39Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM309 734Q283 749 268 772T253 819Q253 853 275 876T334 900Q366 900 393 884Q418 869 438 844T473 794T494 749T497 722Q493 716 475 710T431 703T372 709T309 734Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM315 722Q312 728 319 749T342 794T380 843T428 882Q455 897 488 897Q524 897 546 873T568 816Q568 792 552 769T509 731Q476 713 443 707T383 702T337 710T315 722Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM302 696Q275 696 260 713T245 753Q245 779 262 803T309 849Q339 871 374 886T441 909Q459 903 483 887T529 849Q549 828 562 806T575 763Q575 734 557 715T509 696Q485 696 465 711T431 754Q428 761 425 769T418 786Q412 778 407 770T394 754Q374 726 352 711T302 696Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM521 696Q478 696 457 717T435 770Q435 816 463 843T531 871Q574 871 595 850T616 796Q616 751 588 724T521 696ZM295 696Q252 696 231 717T210 770Q210 816 238 843T305 871Q348 871 369 850T391 796Q391 751 363 724T295 696Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561ZM129 734Q103 749 88 772T73 819Q73 853 95 876T154 900Q186 900 213 884Q238 869 258 844T293 794T314 749T317 722Q313 716 295 710T251 703T192 709T129 734Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561ZM172 722Q169 728 176 749T199 794T237 843T285 882Q312 897 345 897Q381 897 403 873T425 816Q425 792 409 769T366 731Q333 713 300 707T240 702T194 710T172 722Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561ZM145 696Q118 696 103 713T88 753Q88 779 105 803T152 849Q182 871 217 886T284 909Q302 903 326 887T372 849Q392 828 405 806T418 763Q418 734 400 715T352 696Q328 696 308 711T274 754Q271 761 268 769T261 786Q255 778 250 770T237 754Q217 726 195 711T145 696Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561ZM367 696Q324 696 303 717T281 770Q281 816 309 843T377 871Q420 871 441 850T462 796Q462 751 434 724T367 696ZM141 696Q98 696 77 717T56 770Q56 816 84 843T151 871Q194 871 215 850T237 796Q237 751 209 724T141 696Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="724" d="M87 258H69Q46 258 32 270T17 303Q17 316 22 338T41 375Q50 383 62 387T97 392H116L149 548Q160 601 191 624T271 647H411Q484 647 541 628T637 573T696 488T717 377Q717 303 693 235T619 114T494 31T316 0H33L87 258ZM341 161Q385 161 418 177T473 221T507 282T518 354Q518 415 484 450T388 485H334L314 392H369Q399 392 415 379T432 344Q432 338 431 329T426 311T419 292T407 275Q398 266 386 262T346 258H286L265 161H341Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="703" d="M111 557Q122 605 145 628T213 652H231Q263 652 282 640T320 596L481 304L554 647H741L623 89Q612 41 589 18T522 -5H510Q479 -5 460 7T422 51L255 354L180 0H-8L111 557ZM319 697Q294 697 280 712T264 748Q265 777 275 800T301 839T339 863T382 872Q408 872 427 865T460 847Q477 836 486 828T503 820Q513 820 520 829T536 850Q544 861 553 865T577 869Q602 869 616 854T632 818Q631 789 621 767T595 728T557 704T514 695Q487 695 469 703T438 720Q421 733 413 743T393 754Q383 754 378 744T365 719Q359 709 349 703T319 697Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM325 734Q299 749 284 772T269 819Q269 853 291 876T350 900Q382 900 409 884Q434 869 454 844T489 794T510 749T513 722Q509 716 491 710T447 703T388 709T325 734Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM348 722Q345 728 352 749T375 794T413 843T461 882Q488 897 521 897Q557 897 579 873T601 816Q601 792 585 769T542 731Q509 713 476 707T416 702T370 710T348 722Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM332 696Q305 696 290 713T275 753Q275 779 292 803T339 849Q369 871 404 886T471 909Q489 903 513 887T559 849Q579 828 592 806T605 763Q605 734 587 715T539 696Q515 696 495 711T461 754Q458 761 455 769T448 786Q442 778 437 770T424 754Q404 726 382 711T332 696Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM313 697Q288 697 274 712T258 748Q259 777 269 800T295 839T333 863T376 872Q402 872 421 865T454 847Q471 836 480 828T497 820Q507 820 514 829T530 850Q538 861 547 865T571 869Q596 869 610 854T626 818Q625 789 615 767T589 728T551 704T508 695Q481 695 463 703T432 720Q415 733 407 743T387 754Q377 754 372 744T359 719Q353 709 343 703T313 697Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM562 696Q519 696 498 717T476 770Q476 816 504 843T572 871Q615 871 636 850T657 796Q657 751 629 724T562 696ZM336 696Q293 696 272 717T251 770Q251 816 279 843T346 871Q389 871 410 850T432 796Q432 751 404 724T336 696Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="565" d="M89 23Q78 23 66 30T45 48T29 71T23 92Q23 108 30 125T57 159L183 262L105 360Q85 386 85 411Q85 421 92 434T111 457T136 475T161 483Q191 483 217 450L292 352L416 456Q431 470 445 475T474 481Q485 481 497 474T518 456T534 433T540 412Q540 396 534 379T507 345L380 242L459 144Q480 119 480 93Q480 82 473 70T454 47T429 29T403 21Q372 21 347 54L271 152L148 48Q120 23 89 23Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="727" d="M-14 21Q-14 35 -7 46T14 69L79 127Q59 161 50 198T41 277Q41 352 66 421T138 543T254 629T411 661Q474 661 523 645T610 601L702 684Q705 686 716 683T740 671T762 651T772 622Q772 608 766 597T745 574L682 517Q718 452 718 372Q718 297 693 228T621 105T505 19T348 -13Q286 -13 238 2T152 45L57 -41Q54 -44 43 -41T19 -29T-4 -8T-14 21ZM469 474Q440 491 401 491Q364 491 333 475T280 432T244 368T231 289Q231 282 231 276T233 264L469 474ZM295 172Q321 158 358 158Q395 158 426 174T479 217T515 281T528 360Q528 365 528 370T526 380L295 172Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM313 731Q287 746 272 769T257 816Q257 850 279 873T338 897Q370 897 397 881Q422 866 442 841T477 791T498 746T501 719Q497 713 479 707T435 700T376 706T313 731Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM340 719Q337 725 344 746T367 791T405 840T453 879Q480 894 513 894Q549 894 571 870T593 813Q593 789 577 766T534 728Q501 710 468 704T408 699T362 707T340 719Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM324 693Q297 693 282 710T267 750Q267 776 284 800T331 846Q361 868 396 883T463 906Q481 900 505 884T551 846Q571 825 584 803T597 760Q597 731 579 712T531 693Q507 693 487 708T453 751Q450 758 447 766T440 783Q434 775 429 767T416 751Q396 723 374 708T324 693Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM545 693Q502 693 481 714T459 767Q459 813 487 840T555 868Q598 868 619 847T640 793Q640 748 612 721T545 693ZM319 693Q276 693 255 714T234 767Q234 813 262 840T329 868Q372 868 393 847T415 793Q415 748 387 721T319 693Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="632" d="M228 255L46 647H260L351 405L527 659Q528 660 551 658T603 646T655 618T679 567Q679 548 667 523T627 468L428 253L373 0H173L228 255ZM317 719Q314 725 321 746T344 791T382 840T430 879Q457 894 490 894Q526 894 548 870T570 813Q570 789 554 766T511 728Q478 710 445 704T385 699T339 707T317 719Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="623" d="M111 559Q121 606 144 629T211 652H247Q283 652 303 632T316 576L311 551H356Q487 551 553 502T619 359Q619 299 601 252T546 171T452 119T317 101H216L194 0H-8L111 559ZM328 247Q382 247 405 273T428 337Q427 369 407 384T343 400H278L247 247H328Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="625" d="M90 472Q103 536 126 580T183 653T266 693T378 706Q442 706 489 693T568 657T615 603T631 537Q631 472 593 434T484 384Q541 358 572 317T603 218Q603 168 587 126T541 53T473 5T386 -13Q341 -13 307 -2T255 28Q231 52 231 87Q231 108 239 123T258 149T278 163T288 167Q297 156 311 147T355 137Q384 137 403 156T422 207Q422 231 407 247T363 275Q340 286 323 302T306 353Q306 379 318 404T345 439Q399 439 421 455T444 509Q444 535 425 551T373 568Q340 568 317 545T283 470L182 0H-10L90 472Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM267 579Q241 594 226 617T211 664Q211 698 233 721T292 745Q324 745 351 729Q376 714 396 689T431 639T452 594T455 567Q451 561 433 555T389 548T330 554T267 579Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM290 567Q287 573 294 594T317 639T355 688T403 727Q430 742 463 742Q499 742 521 718T543 661Q543 637 527 614T484 576Q451 558 418 552T358 547T312 555T290 567Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM284 541Q257 541 242 558T227 598Q227 624 244 648T291 694Q321 716 356 731T423 754Q441 748 465 732T511 694Q531 673 544 651T557 608Q557 579 539 560T491 541Q467 541 447 556T413 599Q410 606 407 614T400 631Q394 623 389 615T376 599Q356 571 334 556T284 541Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM260 542Q235 542 221 557T205 593Q206 622 216 645T242 684T280 708T323 717Q349 717 368 710T401 692Q418 681 427 673T444 665Q454 665 461 674T477 695Q485 706 494 710T518 714Q543 714 557 699T573 663Q572 634 562 612T536 573T498 549T455 540Q428 540 410 548T379 565Q362 578 354 588T334 599Q324 599 319 589T306 564Q300 554 290 548T260 542Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM506 541Q463 541 442 562T420 615Q420 661 448 688T516 716Q559 716 580 695T601 641Q601 596 573 569T506 541ZM280 541Q237 541 216 562T195 615Q195 661 223 688T290 716Q333 716 354 695T376 641Q376 596 348 569T280 541Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM391 541Q333 541 300 571T266 650Q266 678 276 704T305 750T350 781T407 793Q465 793 498 763T532 684Q532 655 522 630T493 584T448 553T391 541ZM394 619Q417 619 431 636T446 674Q446 692 435 704T404 716Q381 716 367 699T352 661Q352 643 363 631T394 619Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="861" d="M-3 120Q-3 216 83 257T336 301H356V304Q362 332 344 351T278 371Q255 371 239 368T209 358T185 337T164 304Q163 302 147 302T111 307T75 328T59 374Q59 398 72 422T113 465T183 495T282 507Q347 507 389 488T455 436Q492 471 540 489T646 508Q749 508 802 466T855 353Q855 315 842 285T795 233T708 201T571 189Q552 189 533 189T491 191Q500 118 593 118Q637 118 671 137T725 184Q726 184 738 182T764 173T790 152T802 116Q802 90 795 75T771 44Q747 20 702 4T594 -13Q512 -13 463 11T390 78Q362 36 314 12T185 -13Q91 -13 44 21T-3 120ZM511 297Q523 296 533 296T554 297Q596 297 624 300T669 311T693 327T700 348Q700 363 685 375T638 388Q595 388 563 362T511 297ZM180 145Q180 127 194 116T235 105Q270 105 297 127T332 185L338 213H312Q238 211 209 194T180 145Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="512" d="M18 214Q18 274 40 327T100 420T190 484T303 508Q368 508 412 491T483 447Q501 429 509 409T518 370Q518 335 500 316T460 289T420 280T400 283Q394 315 372 337T307 359Q281 359 260 348T223 319T199 278T190 230Q190 190 214 164T285 137Q347 137 380 193Q381 195 394 193T423 182T452 157T465 117Q465 78 430 45Q404 19 363 3T268 -13Q211 -13 165 2T87 46T36 118T18 214ZM185 -39Q186 -38 205 -38T248 -43T291 -62T311 -100Q311 -125 296 -142T250 -179L166 -234Q158 -240 143 -238T115 -229T95 -211T96 -186L185 -39Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM249 579Q223 594 208 617T193 664Q193 698 215 721T274 745Q306 745 333 729Q358 714 378 689T413 639T434 594T437 567Q433 561 415 555T371 548T312 554T249 579Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM265 567Q262 573 269 594T292 639T330 688T378 727Q405 742 438 742Q474 742 496 718T518 661Q518 637 502 614T459 576Q426 558 393 552T333 547T287 555T265 567Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM243 541Q216 541 201 558T186 598Q186 624 203 648T250 694Q280 716 315 731T382 754Q400 748 424 732T470 694Q490 673 503 651T516 608Q516 579 498 560T450 541Q426 541 406 556T372 599Q369 606 366 614T359 631Q353 623 348 615T335 599Q315 571 293 556T243 541Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM465 541Q422 541 401 562T379 615Q379 661 407 688T475 716Q518 716 539 695T560 641Q560 596 532 569T465 541ZM239 541Q196 541 175 562T154 615Q154 661 182 688T249 716Q292 716 313 695T335 641Q335 596 307 569T239 541Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="286" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM100 579Q74 594 59 617T44 664Q44 698 66 721T125 745Q157 745 184 729Q209 714 229 689T264 639T285 594T288 567Q284 561 266 555T222 548T163 554T100 579Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="286" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM136 567Q133 573 140 594T163 639T201 688T249 727Q276 742 309 742Q345 742 367 718T389 661Q389 637 373 614T330 576Q297 558 264 552T204 547T158 555T136 567Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="286" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM110 541Q83 541 68 558T53 598Q53 624 70 648T117 694Q147 716 182 731T249 754Q267 748 291 732T337 694Q357 673 370 651T383 608Q383 579 365 560T317 541Q293 541 273 556T239 599Q236 606 233 614T226 631Q220 623 215 615T202 599Q182 571 160 556T110 541Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="286" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM332 541Q289 541 268 562T246 615Q246 661 274 688T342 716Q385 716 406 695T427 641Q427 596 399 569T332 541ZM106 541Q63 541 42 562T21 615Q21 661 49 688T116 716Q159 716 180 695T202 641Q202 596 174 569T106 541Z" />
+<glyph unicode="&#xF0;" glyph-name="eth" horiz-adv-x="617" d="M17 210Q17 260 34 304T82 382T156 434T252 453Q321 453 365 429T430 368Q432 422 422 463T391 536L228 480Q225 479 215 496T204 540Q204 562 214 582T255 612L288 623Q262 635 234 642T179 651Q177 651 181 664T198 692T235 720T299 733Q346 733 388 718T468 676L606 722Q609 723 619 706T630 662Q630 640 620 620T579 590L554 582Q584 532 599 471T610 339Q607 253 585 188T508 68Q472 32 422 10T299 -13Q239 -13 187 1T98 44T39 114T17 210ZM200 210Q200 172 223 149T286 126Q331 126 363 156Q398 191 410 265Q402 289 376 308T312 327Q287 327 266 318T231 293T208 256T200 210Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="611" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q284 453 328 480T432 508Q504 508 543 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L93 488ZM248 542Q223 542 209 557T193 593Q194 622 204 645T230 684T268 708T311 717Q337 717 356 710T389 692Q406 681 415 673T432 665Q442 665 449 674T465 695Q473 706 482 710T506 714Q531 714 545 699T561 663Q560 634 550 612T524 573T486 549T443 540Q416 540 398 548T367 565Q350 578 342 588T322 599Q312 599 307 589T294 564Q288 554 278 548T248 542Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM222 579Q196 594 181 617T166 664Q166 698 188 721T247 745Q279 745 306 729Q331 714 351 689T386 639T407 594T410 567Q406 561 388 555T344 548T285 554T222 579Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM257 567Q254 573 261 594T284 639T322 688T370 727Q397 742 430 742Q466 742 488 718T510 661Q510 637 494 614T451 576Q418 558 385 552T325 547T279 555T257 567Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM237 541Q210 541 195 558T180 598Q180 624 197 648T244 694Q274 716 309 731T376 754Q394 748 418 732T464 694Q484 673 497 651T510 608Q510 579 492 560T444 541Q420 541 400 556T366 599Q363 606 360 614T353 631Q347 623 342 615T329 599Q309 571 287 556T237 541Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM220 542Q195 542 181 557T165 593Q166 622 176 645T202 684T240 708T283 717Q309 717 328 710T361 692Q378 681 387 673T404 665Q414 665 421 674T437 695Q445 706 454 710T478 714Q503 714 517 699T533 663Q532 634 522 612T496 573T458 549T415 540Q388 540 370 548T339 565Q322 578 314 588T294 599Q284 599 279 589T266 564Q260 554 250 548T220 542Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM459 541Q416 541 395 562T373 615Q373 661 401 688T469 716Q512 716 533 695T554 641Q554 596 526 569T459 541ZM233 541Q190 541 169 562T148 615Q148 661 176 688T243 716Q286 716 307 695T329 641Q329 596 301 569T233 541Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="600" d="M331 376Q264 376 264 430Q264 447 269 469T290 507Q301 518 313 522T356 527Q424 527 424 473Q424 456 418 434T397 396Q387 385 375 381T331 376ZM111 184Q76 184 60 199T44 239Q44 251 48 268T64 297Q74 309 89 315T130 322H493Q528 322 544 307T560 268Q560 255 556 238T540 210Q529 197 515 191T474 184H111ZM180 35Q180 52 185 75T206 113Q217 123 229 128T273 133Q340 133 340 79Q340 62 335 40T314 2Q303 -9 291 -13T247 -18Q180 -18 180 35Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="566" d="M-30 1Q-30 24 -4 47L52 99Q21 152 21 214Q21 270 39 323T93 417T181 483T302 508Q344 508 379 498T443 469L531 554Q534 556 545 553T569 543T592 523T602 496Q602 472 577 447L517 390Q545 339 545 281Q545 225 527 172T473 78T385 12T264 -13Q223 -13 189 -4T127 23L42 -58Q39 -61 28 -58T3 -47T-20 -27T-30 1ZM345 361Q323 375 296 375Q269 375 247 362T209 328T184 279T175 223Q175 217 175 212T177 201L345 361ZM224 133Q243 121 270 121Q297 121 319 134T357 168T381 217T390 273Q390 277 390 281T389 289L224 133Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM247 579Q221 594 206 617T191 664Q191 698 213 721T272 745Q304 745 331 729Q356 714 376 689T411 639T432 594T435 567Q431 561 413 555T369 548T310 554T247 579Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM277 567Q274 573 281 594T304 639T342 688T390 727Q417 742 450 742Q486 742 508 718T530 661Q530 637 514 614T471 576Q438 558 405 552T345 547T299 555T277 567Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM256 541Q229 541 214 558T199 598Q199 624 216 648T263 694Q293 716 328 731T395 754Q413 748 437 732T483 694Q503 673 516 651T529 608Q529 579 511 560T463 541Q439 541 419 556T385 599Q382 606 379 614T372 631Q366 623 361 615T348 599Q328 571 306 556T256 541Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM478 541Q435 541 414 562T392 615Q392 661 420 688T488 716Q531 716 552 695T573 641Q573 596 545 569T478 541ZM252 541Q209 541 188 562T167 615Q167 661 195 688T262 716Q305 716 326 695T348 641Q348 596 320 569T252 541Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="572" d="M-7 -38Q1 -45 17 -50T47 -55Q74 -55 98 -39T155 24L165 38Q130 49 123 96L94 322Q86 377 68 397T23 421Q21 421 25 434T41 464T77 493T139 506Q195 506 227 466T266 335L275 208L425 496H627L306 -45Q283 -81 261 -109T214 -158T160 -190T96 -201Q22 -201 -12 -168Q-36 -144 -36 -103Q-36 -90 -32 -78T-23 -57T-13 -43T-7 -38ZM258 565Q255 571 262 592T285 637T323 686T371 725Q398 740 431 740Q467 740 489 716T511 659Q511 635 495 612T452 574Q419 556 386 550T326 545T280 553T258 565Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="628" d="M120 612Q131 664 151 684T208 704H228Q247 704 264 700T294 685T311 657T311 612L274 438Q299 470 334 489T416 508Q456 508 491 494T551 453T591 388T606 300Q606 240 589 184T538 85T459 15T354 -12Q306 -12 263 8T197 71L142 -186H-50L120 612ZM314 134Q340 134 360 145T394 176T415 221T422 273Q422 312 401 335T342 359Q314 359 290 347T247 311L235 253Q224 199 245 167T314 134Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="572" d="M-7 -38Q1 -45 17 -50T47 -55Q74 -55 98 -39T155 24L165 38Q130 49 123 96L94 322Q86 377 68 397T23 421Q21 421 25 434T41 464T77 493T139 506Q195 506 227 466T266 335L275 208L425 496H627L306 -45Q283 -81 261 -109T214 -158T160 -190T96 -201Q22 -201 -12 -168Q-36 -144 -36 -103Q-36 -90 -32 -78T-23 -57T-13 -43T-7 -38ZM456 541Q413 541 392 562T370 615Q370 661 398 688T466 716Q509 716 530 695T551 641Q551 596 523 569T456 541ZM230 541Q187 541 166 562T145 615Q145 661 173 688T240 716Q283 716 304 695T326 641Q326 596 298 569T230 541Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM317 690Q284 690 267 703T250 739Q250 745 251 754T256 773T263 793T275 810Q285 819 297 823T338 828H573Q606 828 623 815T640 779Q640 767 635 745T615 708Q605 698 593 694T552 690H317H317Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM270 546Q237 546 220 559T203 595Q203 601 204 610T209 629T216 649T228 666Q238 675 250 679T291 684H526Q559 684 576 671T593 635Q593 623 588 601T568 564Q558 554 546 550T505 546H270H270Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419ZM305 734Q271 768 271 811Q271 836 287 856T336 876Q360 871 375 858T402 830T423 803T446 791Q459 791 471 802T496 829T527 857T567 876Q602 876 619 859T637 818Q637 798 627 779T600 742Q574 716 534 701T446 685Q398 685 363 698T305 734Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 405 16T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219ZM260 590Q226 624 226 667Q226 692 242 712T291 732Q315 727 330 714T357 686T378 659T401 647Q414 647 426 658T451 685T482 713T522 732Q557 732 574 715T592 674Q592 654 582 635T555 598Q529 572 489 557T401 541Q353 541 318 554T260 590Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="692" d="M290 597Q298 610 308 620T333 638T369 649T420 653Q453 653 474 649T509 637T529 619T539 596L649 0H576Q535 -29 520 -47T505 -82Q505 -107 532 -107Q551 -107 562 -91Q575 -95 583 -106T592 -132Q592 -164 567 -185T489 -206Q441 -206 410 -182T378 -117Q378 -81 406 -49T486 0H445L425 134H217L144 0H-64L290 597ZM419 278L388 464L279 278H419Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="640" d="M21 186Q21 247 40 305T95 408T179 480T288 508Q333 508 372 489T434 431L448 496H640L563 135Q553 90 560 70T594 42Q594 42 592 38T586 27T573 13T553 0Q530 -16 515 -28T490 -51T477 -71T473 -89Q473 -114 500 -114Q519 -114 530 -98Q543 -102 551 -113T560 -139Q560 -171 535 -192T457 -213Q409 -213 378 -189T346 -124Q346 -88 374 -56T452 -7Q413 2 395 27T374 91Q348 45 304 17T201 -12Q161 -12 128 2T71 41T34 104T21 186ZM205 219Q205 182 226 160T281 137Q322 137 352 164T394 240L407 300Q396 329 376 345T322 362Q295 362 274 350T237 318T213 272T205 219Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="647" d="M40 277Q40 353 67 423T144 545T261 629T411 661Q496 661 546 640T626 590Q647 569 656 546T666 506Q666 467 646 444T602 410T557 397T535 397Q519 435 490 457T415 479Q379 479 349 465T296 426T260 368T247 298Q247 241 282 205T373 168Q458 168 504 236Q505 238 521 235T555 220T589 190T604 142Q604 103 564 63Q532 31 479 9T358 -13Q285 -13 226 7T126 65T63 157T40 277ZM318 711Q315 717 322 738T345 783T383 832T431 871Q458 886 491 886Q527 886 549 862T571 805Q571 781 555 758T512 720Q479 702 446 696T386 691T340 699T318 711Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="512" d="M18 214Q18 274 40 327T100 420T190 484T303 508Q368 508 412 491T483 447Q501 429 509 409T518 370Q518 335 500 316T460 289T420 280T400 283Q394 315 372 337T307 359Q281 359 260 348T223 319T199 278T190 230Q190 190 214 164T285 137Q347 137 380 193Q381 195 394 193T423 182T452 157T465 117Q465 78 430 45Q404 19 363 3T268 -13Q211 -13 165 2T87 46T36 118T18 214ZM231 567Q228 573 235 594T258 639T296 688T344 727Q371 742 404 742Q440 742 462 718T484 661Q484 637 468 614T425 576Q392 558 359 552T299 547T253 555T231 567Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="647" d="M40 277Q40 353 67 423T144 545T261 629T411 661Q496 661 546 640T626 590Q647 569 656 546T666 506Q666 467 646 444T602 410T557 397T535 397Q519 435 490 457T415 479Q379 479 349 465T296 426T260 368T247 298Q247 241 282 205T373 168Q458 168 504 236Q505 238 521 235T555 220T589 190T604 142Q604 103 564 63Q532 31 479 9T358 -13Q285 -13 226 7T126 65T63 157T40 277ZM328 685Q301 685 286 702T271 742Q271 768 288 792T335 838Q365 860 400 875T467 898Q485 892 509 876T555 838Q575 817 588 795T601 752Q601 723 583 704T535 685Q511 685 491 700T457 743Q454 750 451 758T444 775Q438 767 433 759T420 743Q400 715 378 700T328 685Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="512" d="M18 214Q18 274 40 327T100 420T190 484T303 508Q368 508 412 491T483 447Q501 429 509 409T518 370Q518 335 500 316T460 289T420 280T400 283Q394 315 372 337T307 359Q281 359 260 348T223 319T199 278T190 230Q190 190 214 164T285 137Q347 137 380 193Q381 195 394 193T423 182T452 157T465 117Q465 78 430 45Q404 19 363 3T268 -13Q211 -13 165 2T87 46T36 118T18 214ZM213 541Q186 541 171 558T156 598Q156 624 173 648T220 694Q250 716 285 731T352 754Q370 748 394 732T440 694Q460 673 473 651T486 608Q486 579 468 560T420 541Q396 541 376 556T342 599Q339 606 336 614T329 631Q323 623 318 615T305 599Q285 571 263 556T213 541Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="647" d="M40 277Q40 353 67 423T144 545T261 629T411 661Q496 661 546 640T626 590Q647 569 656 546T666 506Q666 467 646 444T602 410T557 397T535 397Q519 435 490 457T415 479Q379 479 349 465T296 426T260 368T247 298Q247 241 282 205T373 168Q458 168 504 236Q505 238 521 235T555 220T589 190T604 142Q604 103 564 63Q532 31 479 9T358 -13Q285 -13 226 7T126 65T63 157T40 277ZM429 685Q384 685 362 707T339 763Q339 811 368 839T439 868Q485 868 507 846T529 791Q529 767 521 748T500 714T468 693T429 685Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="512" d="M18 214Q18 274 40 327T100 420T190 484T303 508Q368 508 412 491T483 447Q501 429 509 409T518 370Q518 335 500 316T460 289T420 280T400 283Q394 315 372 337T307 359Q281 359 260 348T223 319T199 278T190 230Q190 190 214 164T285 137Q347 137 380 193Q381 195 394 193T423 182T452 157T465 117Q465 78 430 45Q404 19 363 3T268 -13Q211 -13 165 2T87 46T36 118T18 214ZM326 541Q281 541 259 563T236 619Q236 667 265 695T336 724Q382 724 404 702T426 647Q426 623 418 604T397 570T365 549T326 541Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="647" d="M40 277Q40 353 67 423T144 545T261 629T411 661Q496 661 546 640T626 590Q647 569 656 546T666 506Q666 467 646 444T602 410T557 397T535 397Q519 435 490 457T415 479Q379 479 349 465T296 426T260 368T247 298Q247 241 282 205T373 168Q458 168 504 236Q505 238 521 235T555 220T589 190T604 142Q604 103 564 63Q532 31 479 9T358 -13Q285 -13 226 7T126 65T63 157T40 277ZM417 680Q399 687 374 703T328 741Q308 762 296 784T283 827Q283 856 300 875T348 894Q378 894 403 870T439 804L463 836Q483 863 505 878T555 894Q582 894 597 877T613 836Q613 810 596 786T548 741Q518 720 484 704T417 680Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="512" d="M18 214Q18 274 40 327T100 420T190 484T303 508Q368 508 412 491T483 447Q501 429 509 409T518 370Q518 335 500 316T460 289T420 280T400 283Q394 315 372 337T307 359Q281 359 260 348T223 319T199 278T190 230Q190 190 214 164T285 137Q347 137 380 193Q381 195 394 193T423 182T452 157T465 117Q465 78 430 45Q404 19 363 3T268 -13Q211 -13 165 2T87 46T36 118T18 214ZM311 529Q293 536 268 552T222 590Q202 611 190 633T177 676Q177 705 194 724T242 743Q272 743 297 719T333 653L357 685Q377 712 399 727T449 743Q476 743 491 726T507 685Q507 659 490 635T442 590Q412 569 378 553T311 529Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="682" d="M109 548Q120 601 151 624T231 647H365Q438 647 495 628T593 573T654 488T675 377Q675 303 651 235T576 114T449 31T271 0H-8L109 548ZM296 161Q340 161 373 177T430 221T465 282T477 354Q477 413 443 449T347 485H293L224 161H296ZM391 673Q373 680 348 696T302 734Q282 755 270 777T257 820Q257 849 274 868T322 887Q352 887 377 863T413 797L437 829Q457 856 479 871T529 887Q556 887 571 870T587 829Q587 803 570 779T522 734Q492 713 458 697T391 673Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="867" d="M24 197Q24 257 41 313T92 412T171 482T276 508Q323 508 365 488T433 427L490 697H682L563 135Q552 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 406 16T375 89Q351 44 310 16T213 -12Q172 -12 138 2T78 43T39 109T24 197ZM767 693Q768 695 791 696T843 691T895 667T919 617Q919 592 908 571T866 519L760 406Q751 398 734 398T700 406T675 427T672 460L767 693ZM208 224Q208 185 229 161T287 137Q324 137 353 163T393 238L406 299Q395 328 375 345T317 362Q290 362 270 351T236 320T215 276T208 224Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="724" d="M87 258H69Q46 258 32 270T17 303Q17 316 22 338T41 375Q50 383 62 387T97 392H116L149 548Q160 601 191 624T271 647H411Q484 647 541 628T637 573T696 488T717 377Q717 303 693 235T619 114T494 31T316 0H33L87 258ZM341 161Q385 161 418 177T473 221T507 282T518 354Q518 413 486 445T391 478H333L314 392H369Q399 392 415 379T432 344Q432 338 431 329T426 311T419 292T407 275Q398 266 386 262T346 258H286L265 161H341Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="640" d="M24 197Q24 257 41 313T92 412T171 482T276 508Q323 508 365 488T433 427L459 553H376Q345 553 345 571Q345 579 349 594T359 615Q366 623 385 623H474L490 697H682L666 623H706Q737 623 737 605Q737 597 733 582T723 561Q716 553 697 553H651L563 135Q552 90 560 70T594 42Q594 42 591 34T577 15T546 -3T493 -12Q433 -12 406 16T375 89Q351 44 310 16T213 -12Q172 -12 138 2T78 43T39 109T24 197ZM208 224Q208 185 229 161T287 137Q324 137 353 163T393 238L406 299Q395 328 375 345T317 362Q290 362 270 351T236 320T215 276T208 224Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM300 690Q267 690 250 703T233 739Q233 745 234 754T239 773T246 793T258 810Q268 819 280 823T321 828H556Q589 828 606 815T623 779Q623 767 618 745T598 708Q588 698 576 694T535 690H300H300Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM216 546Q183 546 166 559T149 595Q149 601 150 610T155 629T162 649T174 666Q184 675 196 679T237 684H472Q505 684 522 671T539 635Q539 623 534 601T514 564Q504 554 492 550T451 546H216H216Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM273 734Q239 768 239 811Q239 836 255 856T304 876Q328 871 343 858T370 830T391 803T414 791Q427 791 439 802T464 829T495 857T535 876Q570 876 587 859T605 818Q605 798 595 779T568 742Q542 716 502 701T414 685Q366 685 331 698T273 734Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM195 590Q161 624 161 667Q161 692 177 712T226 732Q250 727 265 714T292 686T313 659T336 647Q349 647 361 658T386 685T417 713T457 732Q492 732 509 715T527 674Q527 654 517 635T490 598Q464 572 424 557T336 541Q288 541 253 554T195 590Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM413 685Q368 685 346 707T323 763Q323 811 352 839T423 868Q469 868 491 846T513 791Q513 767 505 748T484 714T452 693T413 685Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM349 541Q304 541 282 563T259 619Q259 667 288 695T359 724Q405 724 427 702T449 647Q449 623 441 604T420 570T388 549T349 541Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="615" d="M109 548Q120 601 151 624T231 647H650L614 481H294L278 403H527L497 252H247L228 168H555L519 0H497Q475 -14 461 -26T439 -48T427 -66T423 -84Q423 -109 450 -109Q469 -109 480 -93Q493 -97 501 -108T510 -134Q510 -166 485 -187T407 -208Q359 -208 328 -184T296 -119Q296 -81 325 -49T409 0H-9L109 548Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q438 28 417 17Q394 2 379 -10T354 -33T341 -53T337 -71Q337 -96 364 -96Q383 -96 394 -80Q407 -84 415 -95T424 -121Q424 -153 399 -174T321 -195Q273 -195 242 -171T210 -106Q210 -80 225 -56T268 -13Q203 -12 156 4T78 50T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="616" d="M109 548Q120 601 151 624T231 647H650L615 481H294L278 403H527L497 252H247L228 168H555L519 0H-9L109 548ZM395 673Q377 680 352 696T306 734Q286 755 274 777T261 820Q261 849 278 868T326 887Q356 887 381 863T417 797L441 829Q461 856 483 871T533 887Q560 887 575 870T591 829Q591 803 574 779T526 734Q496 713 462 697T395 673Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="544" d="M18 205Q18 273 42 329T109 424T208 486T330 508Q433 508 485 466T538 353Q538 315 525 285T478 233T391 201T254 189Q235 189 216 189T174 191Q183 118 276 118Q320 118 354 137T409 184Q409 184 421 182T447 173T473 152T485 116Q485 90 478 75T454 44Q430 20 385 4T273 -13Q207 -13 159 3T80 49T33 118T18 205ZM194 297Q206 296 217 296T238 297Q280 297 308 300T352 311T376 327T383 348Q383 363 368 375T321 388Q278 388 246 362T194 297ZM332 529Q314 536 289 552T243 590Q223 611 211 633T198 676Q198 705 215 724T263 743Q293 743 318 719T354 653L378 685Q398 712 420 727T470 743Q497 743 512 726T528 685Q528 659 511 635T463 590Q433 569 399 553T332 529Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="724" d="M40 269Q40 348 68 419T146 544T267 629T423 661Q503 661 561 640T652 586Q689 547 689 504Q689 480 680 464T658 438T630 422T600 415T576 413T565 416Q545 447 511 468T429 490Q390 490 357 475T298 431T258 366T243 284Q241 218 278 178T378 137Q425 137 459 162Q473 173 483 185T502 224H359L391 371H636Q716 371 697 280L640 14Q639 10 622 3T579 -5Q544 -5 521 10T490 59Q461 24 417 6T314 -13Q250 -13 200 6T114 62T59 151T40 269ZM335 685Q308 685 293 702T278 742Q278 768 295 792T342 838Q372 860 407 875T474 898Q492 892 516 876T562 838Q582 817 595 795T608 752Q608 723 590 704T542 685Q518 685 498 700T464 743Q461 750 458 758T451 775Q445 767 440 759T427 743Q407 715 385 700T335 685Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="629" d="M-3 -61Q-3 -37 9 -18T36 13T64 30T79 33Q102 -5 136 -25T224 -46Q284 -46 314 -20T355 58L363 96Q334 61 298 45T213 28Q172 28 138 42T79 83T41 146T27 229Q27 281 44 331T94 420T175 484T285 508Q329 508 369 490T435 435L448 496H640L546 57Q532 -9 509 -57T447 -137T353 -184T221 -200Q181 -200 151 -195T97 -181T58 -161T29 -137Q-3 -106 -3 -61ZM208 249Q208 214 230 193T289 171Q331 171 360 194T397 254L408 307Q397 332 375 346T321 361Q294 361 273 352T238 327T216 291T208 249ZM263 541Q236 541 221 558T206 598Q206 624 223 648T270 694Q300 716 335 731T402 754Q420 748 444 732T490 694Q510 673 523 651T536 608Q536 579 518 560T470 541Q446 541 426 556T392 599Q389 606 386 614T379 631Q373 623 368 615T355 599Q335 571 313 556T263 541Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="724" d="M40 269Q40 348 68 419T146 544T267 629T423 661Q503 661 561 640T652 586Q689 547 689 504Q689 480 680 464T658 438T630 422T600 415T576 413T565 416Q545 447 511 468T429 490Q390 490 357 475T298 431T258 366T243 284Q241 218 278 178T378 137Q425 137 459 162Q473 173 483 185T502 224H359L391 371H636Q716 371 697 280L640 14Q639 10 622 3T579 -5Q544 -5 521 10T490 59Q461 24 417 6T314 -13Q250 -13 200 6T114 62T59 151T40 269ZM306 734Q272 768 272 811Q272 836 288 856T337 876Q361 871 376 858T403 830T424 803T447 791Q460 791 472 802T497 829T528 857T568 876Q603 876 620 859T638 818Q638 798 628 779T601 742Q575 716 535 701T447 685Q399 685 364 698T306 734Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="629" d="M-3 -61Q-3 -37 9 -18T36 13T64 30T79 33Q102 -5 136 -25T224 -46Q284 -46 314 -20T355 58L363 96Q334 61 298 45T213 28Q172 28 138 42T79 83T41 146T27 229Q27 281 44 331T94 420T175 484T285 508Q329 508 369 490T435 435L448 496H640L546 57Q532 -9 509 -57T447 -137T353 -184T221 -200Q181 -200 151 -195T97 -181T58 -161T29 -137Q-3 -106 -3 -61ZM208 249Q208 214 230 193T289 171Q331 171 360 194T397 254L408 307Q397 332 375 346T321 361Q294 361 273 352T238 327T216 291T208 249ZM238 590Q204 624 204 667Q204 692 220 712T269 732Q293 727 308 714T335 686T356 659T379 647Q392 647 404 658T429 685T460 713T500 732Q535 732 552 715T570 674Q570 654 560 635T533 598Q507 572 467 557T379 541Q331 541 296 554T238 590Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="724" d="M40 269Q40 348 68 419T146 544T267 629T423 661Q503 661 561 640T652 586Q689 547 689 504Q689 480 680 464T658 438T630 422T600 415T576 413T565 416Q545 447 511 468T429 490Q390 490 357 475T298 431T258 366T243 284Q241 218 278 178T378 137Q425 137 459 162Q473 173 483 185T502 224H359L391 371H636Q716 371 697 280L640 14Q639 10 622 3T579 -5Q544 -5 521 10T490 59Q461 24 417 6T314 -13Q250 -13 200 6T114 62T59 151T40 269ZM449 685Q404 685 382 707T359 763Q359 811 388 839T459 868Q505 868 527 846T549 791Q549 767 541 748T520 714T488 693T449 685Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="629" d="M-3 -61Q-3 -37 9 -18T36 13T64 30T79 33Q102 -5 136 -25T224 -46Q284 -46 314 -20T355 58L363 96Q334 61 298 45T213 28Q172 28 138 42T79 83T41 146T27 229Q27 281 44 331T94 420T175 484T285 508Q329 508 369 490T435 435L448 496H640L546 57Q532 -9 509 -57T447 -137T353 -184T221 -200Q181 -200 151 -195T97 -181T58 -161T29 -137Q-3 -106 -3 -61ZM208 249Q208 214 230 193T289 171Q331 171 360 194T397 254L408 307Q397 332 375 346T321 361Q294 361 273 352T238 327T216 291T208 249ZM376 541Q331 541 309 563T286 619Q286 667 315 695T386 724Q432 724 454 702T476 647Q476 623 468 604T447 570T415 549T376 541Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="724" d="M40 269Q40 348 68 419T146 544T267 629T423 661Q503 661 561 640T652 586Q689 547 689 504Q689 480 680 464T658 438T630 422T600 415T576 413T565 416Q545 447 511 468T429 490Q390 490 357 475T298 431T258 366T243 284Q241 218 278 178T378 137Q425 137 459 162Q473 173 483 185T502 224H359L391 371H636Q716 371 697 280L640 14Q639 10 622 3T579 -5Q544 -5 521 10T490 59Q461 24 417 6T314 -13Q250 -13 200 6T114 62T59 151T40 269ZM308 -32Q309 -31 320 -30T348 -30T384 -35T420 -49T448 -72T459 -109Q459 -135 448 -155T405 -207L303 -306Q294 -314 277 -314T244 -307T218 -285T215 -252L308 -32H308Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="629" d="M291 598Q291 624 302 644T345 696L447 795Q456 803 473 803T506 795T532 774T535 741L442 521Q441 520 430 519T402 519T366 524T330 538T302 561T291 598ZM-3 -61Q-3 -37 9 -18T36 13T64 30T79 33Q102 -5 136 -25T224 -46Q284 -46 314 -20T355 58L363 96Q334 61 298 45T213 28Q172 28 138 42T79 83T41 146T27 229Q27 281 44 331T94 420T175 484T285 508Q329 508 369 490T435 435L448 496H640L546 57Q532 -9 509 -57T447 -137T353 -184T221 -200Q181 -200 151 -195T97 -181T58 -161T29 -137Q-3 -106 -3 -61ZM208 249Q208 214 230 193T289 171Q331 171 360 194T397 254L408 307Q397 332 375 346T321 361Q294 361 273 352T238 327T216 291T208 249Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="697" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L281 409H485L517 559Q528 607 550 629T615 652H645Q687 652 707 628T718 557L599 0H398L449 241H246L194 0H-8L112 561ZM335 685Q308 685 293 702T278 742Q278 768 295 792T342 838Q372 860 407 875T474 898Q492 892 516 876T562 838Q582 817 595 795T608 752Q608 723 590 704T542 685Q518 685 498 700T464 743Q461 750 458 758T451 775Q445 767 440 759T427 743Q407 715 385 700T335 685Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="611" d="M120 612Q131 664 151 684T208 704H228Q247 704 265 700T295 685T312 657T311 612L269 412Q296 455 339 481T436 508Q505 508 544 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L120 612ZM142 722Q115 722 100 739T85 779Q85 805 102 829T149 875Q179 897 214 912T281 935Q299 929 323 913T369 875Q389 854 402 832T415 789Q415 760 397 741T349 722Q325 722 305 737T271 780Q268 787 265 795T258 812Q252 804 247 796T234 780Q214 752 192 737T142 722Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="697" d="M94 480H50Q19 480 19 498Q19 506 23 520T33 541Q40 549 59 549H109L112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L311 549H515L517 559Q528 607 550 629T615 652H645Q687 652 707 628T718 557L716 549H762Q793 549 793 531Q793 523 789 509T779 488Q772 480 753 480H701L599 0H398L449 241H246L194 0H-8L94 480ZM485 409L500 480H296L281 409H485Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="611" d="M107 553H67Q36 553 36 571Q36 579 40 594T50 615Q57 623 76 623H122Q133 668 153 686T208 704H228Q246 704 263 700T292 687T310 662T313 623H397Q428 623 428 605Q428 597 424 582T414 561Q407 553 388 553H298L269 412Q296 455 339 481T436 508Q505 508 544 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L107 553Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561ZM127 690Q94 690 77 703T60 739Q60 745 61 754T66 773T73 793T85 810Q95 819 107 823T148 828H383Q416 828 433 815T450 779Q450 767 445 745T425 708Q415 698 403 694T362 690H127H127Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="286" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM84 546Q51 546 34 559T17 595Q17 601 18 610T23 629T30 649T42 666Q52 675 64 679T105 684H340Q373 684 390 671T407 635Q407 623 402 601T382 564Q372 554 360 550T319 546H84H84Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H161Q115 -31 99 -50T83 -87Q83 -112 110 -112Q129 -112 140 -96Q153 -100 161 -111T170 -137Q170 -169 145 -190T67 -211Q19 -211 -12 -187T-44 -122Q-44 -83 -11 -49T80 0H-1L118 561Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="298" d="M225 552Q177 552 151 574T125 631Q125 678 155 705T231 733Q279 733 305 711T331 654Q331 608 301 580T225 552ZM107 -9Q77 -4 58 9T30 43T20 88T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 41 242 29T217 5Q189 -13 171 -27T142 -52T127 -74T122 -93Q122 -118 149 -118Q168 -118 179 -102Q192 -106 200 -117T209 -143Q209 -175 184 -196T106 -217Q58 -217 27 -193T-5 -128Q-5 -90 24 -58T107 -9Z" />
+<glyph unicode="&#x130;" glyph-name="Idot" horiz-adv-x="304" d="M118 561Q129 608 151 630T217 652H247Q288 652 309 628T319 557L200 0H-1L118 561ZM251 685Q206 685 184 707T161 763Q161 811 190 839T261 868Q307 868 329 846T351 791Q351 767 343 748T322 714T290 693T251 685Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="286" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L101 496H293L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="528" d="M26 43Q-7 76 -7 115Q-7 149 6 170T35 204T65 219T80 221Q97 192 124 175T186 157Q230 157 254 183T292 269L372 647H573L485 234Q460 115 392 51T196 -13Q136 -13 94 2T26 43ZM374 685Q347 685 332 702T317 742Q317 768 334 792T381 838Q411 860 446 875T513 898Q531 892 555 876T601 838Q621 817 634 795T647 752Q647 723 629 704T581 685Q557 685 537 700T503 743Q500 750 497 758T490 775Q484 767 479 759T466 743Q446 715 424 700T374 685Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="301" d="M128 541Q101 541 86 558T71 598Q71 624 88 648T135 694Q165 716 200 731T267 754Q285 748 309 732T355 694Q375 673 388 651T401 608Q401 579 383 560T335 541Q311 541 291 556T257 599Q254 606 251 614T244 631Q238 623 233 615T220 599Q200 571 178 556T128 541ZM116 496H308L197 -21Q177 -113 135 -157T9 -201Q-31 -201 -59 -192T-103 -166Q-125 -144 -125 -111Q-125 -95 -120 -81T-109 -57T-98 -41T-92 -35Q-84 -43 -68 -49T-38 -55Q-20 -55 -9 -44T10 -2L116 496Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="650" d="M112 561Q122 608 144 630T210 652H240Q282 652 302 628T313 557L275 377L544 661Q545 662 564 658T606 643T648 613T667 565Q667 542 655 519T613 469L436 306L516 173Q539 131 560 115T612 97Q614 97 613 81T601 45T564 9T493 -7Q460 -7 437 1T395 25T362 65T332 121L252 273L194 0H-8L112 561ZM218 -32Q219 -31 230 -30T258 -30T294 -35T330 -49T358 -72T369 -109Q369 -135 358 -155T315 -207L213 -306Q204 -314 187 -314T154 -307T128 -285T125 -252L218 -32H218Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="556" d="M138 697H329Q306 588 288 504Q280 467 273 433T260 371Q253 339 247 311L449 508Q450 509 466 506T502 492T537 462T553 412Q553 387 542 368T507 330L393 238L428 172Q438 149 447 134T466 109T488 96T516 90Q518 90 517 74T505 40T471 6T407 -10Q381 -10 363 -4T331 15T304 50T279 103L228 221Q225 205 221 187Q218 171 214 151T205 111Q194 63 182 0H-10L138 697ZM165 -32Q166 -31 177 -30T205 -30T241 -35T277 -49T305 -72T316 -109Q316 -135 305 -155T262 -207L160 -306Q151 -314 134 -314T101 -307T75 -285T72 -252L165 -32H165Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="547" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L230 174H420Q461 174 482 157T504 112Q504 105 502 94T497 69T487 44T472 23Q461 12 445 6T394 0H-8L112 561ZM170 711Q167 717 174 738T197 783T235 832T283 871Q310 886 343 886Q379 886 401 862T423 805Q423 781 407 758T364 720Q331 702 298 696T238 691T192 699T170 711Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="298" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L143 697H335L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM175 759Q172 765 179 786T202 831T240 880T288 919Q315 934 348 934Q384 934 406 910T428 853Q428 829 412 806T369 768Q336 750 303 744T243 739T197 747T175 759Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="547" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L230 174H420Q461 174 482 157T504 112Q504 105 502 94T497 69T487 44T472 23Q461 12 445 6T394 0H-8L112 561ZM160 -32Q161 -31 172 -30T200 -30T236 -35T272 -49T300 -72T311 -109Q311 -135 300 -155T257 -207L155 -306Q146 -314 129 -314T96 -307T70 -285T67 -252L160 -32H160Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="298" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L143 697H335L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM37 -32Q38 -31 49 -30T77 -30T113 -35T149 -49T177 -72T188 -109Q188 -135 177 -155T134 -207L32 -306Q23 -314 6 -314T-27 -307T-53 -285T-56 -252L37 -32H37Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="675" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L230 174H420Q461 174 482 157T504 112Q504 105 502 94T497 69T487 44T472 23Q461 12 445 6T394 0H-8L112 561ZM590 651Q591 653 614 654T666 649T718 625T742 575Q742 550 731 529T689 477L583 364Q574 356 557 356T523 364T498 385T495 418L590 651Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="523" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L143 697H335L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM423 695Q424 697 447 698T499 693T551 669T575 619Q575 594 564 573T522 521L416 408Q407 400 390 400T356 408T331 429T328 462L423 695Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="742" d="M112 561Q123 608 145 630T210 652H239Q281 652 302 629T313 558L230 174H420Q461 174 482 157T504 112Q504 105 502 94T497 69T487 44T472 23Q461 12 445 6T394 0H-8L112 561ZM538 299Q538 319 545 347T570 393Q576 399 582 403T598 410T619 414T651 415Q693 415 712 400T732 352Q732 332 725 305T700 258Q688 246 672 241T619 236Q577 236 558 251T538 299Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="565" d="M143 -12Q101 -12 75 0T36 33T20 82T25 142L143 697H335L215 135Q205 90 213 70T248 42Q248 42 245 34T230 15T198 -3T143 -12ZM354 279Q354 299 361 327T386 373Q392 379 398 383T414 390T435 394T467 395Q509 395 528 380T548 332Q548 312 541 285T516 238Q504 226 488 221T435 216Q393 216 374 231T354 279Z" />
+<glyph unicode="&#x141;" glyph-name="Lslash" horiz-adv-x="606" d="M87 194L34 180Q32 179 28 184T18 201T10 227T6 261Q6 277 10 290T25 313T55 333T105 351L122 356L166 561Q177 608 199 630T264 652H293Q335 652 356 629T367 558L336 414L460 448Q464 449 475 427T487 367Q487 351 483 338T468 315T438 295T388 277L301 253L284 174H474Q515 174 536 157T558 112Q558 105 556 94T551 69T541 44T526 23Q515 12 499 6T448 0H46L87 194Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="401" d="M93 204L25 189Q24 189 21 195T13 211T6 234T3 259Q3 294 20 318T87 352L126 362L198 697H389L327 404L398 421Q399 421 402 415T409 398T416 376T419 351Q419 316 402 292T335 257L294 247L270 135Q260 90 268 70T302 42Q303 42 300 34T285 15T253 -3T198 -12Q156 -12 130 0T90 33T75 82T80 142L93 204Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="703" d="M111 557Q122 605 145 628T213 652H231Q263 652 282 640T320 596L481 304L554 647H741L623 89Q612 41 589 18T522 -5H510Q479 -5 460 7T422 51L255 354L180 0H-8L111 557ZM348 711Q345 717 352 738T375 783T413 832T461 871Q488 886 521 886Q557 886 579 862T601 805Q601 781 585 758T542 720Q509 702 476 696T416 691T370 699T348 711Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="611" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q284 453 328 480T432 508Q504 508 543 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L93 488ZM252 567Q249 573 256 594T279 639T317 688T365 727Q392 742 425 742Q461 742 483 718T505 661Q505 637 489 614T446 576Q413 558 380 552T320 547T274 555T252 567Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="703" d="M111 557Q122 605 145 628T213 652H231Q263 652 282 640T320 596L481 304L554 647H741L623 89Q612 41 589 18T522 -5H510Q479 -5 460 7T422 51L255 354L180 0H-8L111 557ZM237 -32Q238 -31 249 -30T277 -30T313 -35T349 -49T377 -72T388 -109Q388 -135 377 -155T334 -207L232 -306Q223 -314 206 -314T173 -307T147 -285T144 -252L237 -32H237Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="611" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q284 453 328 480T432 508Q504 508 543 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L93 488ZM195 -32Q196 -31 207 -30T235 -30T271 -35T307 -49T335 -72T346 -109Q346 -135 335 -155T292 -207L190 -306Q181 -314 164 -314T131 -307T105 -285T102 -252L195 -32H195Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="703" d="M111 557Q122 605 145 628T213 652H231Q263 652 282 640T320 596L481 304L554 647H741L623 89Q612 41 589 18T522 -5H510Q479 -5 460 7T422 51L255 354L180 0H-8L111 557ZM420 673Q402 680 377 696T331 734Q311 755 299 777T286 820Q286 849 303 868T351 887Q381 887 406 863T442 797L466 829Q486 856 508 871T558 887Q585 887 600 870T616 829Q616 803 599 779T551 734Q521 713 487 697T420 673Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="611" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q284 453 328 480T432 508Q504 508 543 465T583 353Q583 327 576 293T555 210Q545 175 539 147T532 104Q532 82 541 72T571 56Q572 56 568 46T552 22T517 -1T457 -12Q394 -12 367 14T339 86Q339 108 344 133T362 202Q371 234 376 257T381 293Q381 321 367 337T323 353Q298 353 281 343T248 310L182 0H-10L93 488ZM332 529Q314 536 289 552T243 590Q223 611 211 633T198 676Q198 705 215 724T263 743Q293 743 318 719T354 653L378 685Q398 712 420 727T470 743Q497 743 512 726T528 685Q528 659 511 635T463 590Q433 569 399 553T332 529Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM315 690Q282 690 265 703T248 739Q248 745 249 754T254 773T261 793T273 810Q283 819 295 823T336 828H571Q604 828 621 815T638 779Q638 767 633 745T613 708Q603 698 591 694T550 690H315H315Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM205 546Q172 546 155 559T138 595Q138 601 139 610T144 629T151 649T163 666Q173 675 185 679T226 684H461Q494 684 511 671T528 635Q528 623 523 601T503 564Q493 554 481 550T440 546H205H205Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM299 734Q265 768 265 811Q265 836 281 856T330 876Q354 871 369 858T396 830T417 803T440 791Q453 791 465 802T490 829T521 857T561 876Q596 876 613 859T631 818Q631 798 621 779T594 742Q568 716 528 701T440 685Q392 685 357 698T299 734Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM183 590Q149 624 149 667Q149 692 165 712T214 732Q238 727 253 714T280 686T301 659T324 647Q337 647 349 658T374 685T405 713T445 732Q480 732 497 715T515 674Q515 654 505 635T478 598Q452 572 412 557T324 541Q276 541 241 554T183 590Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="727" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q488 661 545 638T641 574T699 482T718 372Q718 297 693 228T621 105T505 19T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291ZM470 711Q467 717 474 737T495 783T531 832T578 872Q607 888 632 888Q668 888 693 867T718 814Q718 788 703 762T661 721Q629 703 597 697T537 692T492 699T470 711ZM223 711Q219 717 226 737T248 783T284 832T331 872Q360 888 385 888Q421 888 446 867T471 814Q471 788 456 762T414 721Q382 703 350 697T290 692T245 699T223 711Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="566" d="M21 214Q21 270 39 323T93 417T181 483T302 508Q363 508 408 490T484 442T530 370T545 281Q545 225 527 172T473 78T385 12T264 -13Q203 -13 158 5T82 54T36 126T21 214ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222ZM364 567Q361 573 368 593T389 639T425 688T472 728Q501 744 526 744Q562 744 587 723T612 670Q612 644 597 618T555 577Q523 559 491 553T431 548T386 555T364 567ZM117 567Q113 573 120 593T142 639T178 688T225 728Q254 744 279 744Q315 744 340 723T365 670Q365 644 350 618T308 577Q276 559 244 553T184 548T139 555T117 567Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1028" d="M41 277Q41 352 66 421T138 543T254 629T411 661Q466 661 516 647H1062L1027 475H701Q712 441 717 405H940L909 250H699Q694 228 684 206T664 168H969L932 0H449Q426 -6 401 -9T348 -13Q271 -13 213 10T117 74T60 166T41 277ZM240 291Q240 237 271 201T359 164Q393 164 422 179T473 220T507 282T520 358Q520 412 489 448T400 485Q366 485 337 470T287 429T253 367T240 291Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="923" d="M21 214Q21 270 39 323T92 417T178 483T296 508Q366 508 416 482T493 410Q532 456 585 482T713 508Q813 508 865 466T917 353Q917 315 904 285T858 233T770 201T633 189Q614 189 595 189T553 191Q562 118 655 118Q700 118 733 137T788 184Q788 184 800 182T827 173T853 152T865 116Q865 90 857 75T834 44Q809 20 764 4T653 -13Q583 -13 531 9T452 74Q417 35 369 11T257 -13Q198 -13 154 5T80 53T36 125T21 214ZM573 297Q585 296 595 296T617 297Q659 297 687 300T732 311T756 327T763 348Q763 363 748 375T701 388Q658 388 626 362T573 297ZM181 222Q181 181 205 155T268 128Q295 128 316 140T353 173T376 220T385 274Q385 314 361 340T298 367Q271 367 250 355T213 322T190 276T181 222Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="644" d="M109 548Q120 601 151 624T231 647H393Q527 647 591 598T655 453Q655 411 643 375T606 310T547 262T465 233L501 174Q514 153 525 140T546 118T569 105T597 99Q600 98 599 82T587 46T550 10T476 -7Q449 -7 428 -1T388 19T353 57T318 117L273 202H235L191 0H-9L109 548ZM350 344Q405 344 429 369T453 432Q453 496 370 496H295L262 344H350ZM296 711Q293 717 300 738T323 783T361 832T409 871Q436 886 469 886Q505 886 527 862T549 805Q549 781 533 758T490 720Q457 702 424 696T364 691T318 699T296 711Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="465" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q273 452 308 480T389 508Q431 508 458 485T486 424Q486 389 473 366T443 328T414 308T399 303Q388 316 372 325T328 334Q287 334 263 299T224 201L181 0H-11L93 488ZM196 567Q193 573 200 594T223 639T261 688T309 727Q336 742 369 742Q405 742 427 718T449 661Q449 637 433 614T390 576Q357 558 324 552T264 547T218 555T196 567Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="644" d="M109 548Q120 601 151 624T231 647H393Q527 647 591 598T655 453Q655 411 643 375T606 310T547 262T465 233L501 174Q514 153 525 140T546 118T569 105T597 99Q600 98 599 82T587 46T550 10T476 -7Q449 -7 428 -1T388 19T353 57T318 117L273 202H235L191 0H-9L109 548ZM350 344Q405 344 429 369T453 432Q453 496 370 496H295L262 344H350ZM219 -32Q220 -31 231 -30T259 -30T295 -35T331 -49T359 -72T370 -109Q370 -135 359 -155T316 -207L214 -306Q205 -314 188 -314T155 -307T129 -285T126 -252L219 -32H219Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="465" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q273 452 308 480T389 508Q431 508 458 485T486 424Q486 389 473 366T443 328T414 308T399 303Q388 316 372 325T328 334Q287 334 263 299T224 201L181 0H-11L93 488ZM11 -32Q12 -31 23 -30T51 -30T87 -35T123 -49T151 -72T162 -109Q162 -135 151 -155T108 -207L6 -306Q-3 -314 -20 -314T-53 -307T-79 -285T-82 -252L11 -32H11Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="644" d="M109 548Q120 601 151 624T231 647H393Q527 647 591 598T655 453Q655 411 643 375T606 310T547 262T465 233L501 174Q514 153 525 140T546 118T569 105T597 99Q600 98 599 82T587 46T550 10T476 -7Q449 -7 428 -1T388 19T353 57T318 117L273 202H235L191 0H-9L109 548ZM350 344Q405 344 429 369T453 432Q453 496 370 496H295L262 344H350ZM386 673Q368 680 343 696T297 734Q277 755 265 777T252 820Q252 849 269 868T317 887Q347 887 372 863T408 797L432 829Q452 856 474 871T524 887Q551 887 566 870T582 829Q582 803 565 779T517 734Q487 713 453 697T386 673Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="465" d="M93 488Q94 491 110 495T149 500Q186 500 216 479T252 406Q273 452 308 480T389 508Q431 508 458 485T486 424Q486 389 473 366T443 328T414 308T399 303Q388 316 372 325T328 334Q287 334 263 299T224 201L181 0H-11L93 488ZM282 529Q264 536 239 552T193 590Q173 611 161 633T148 676Q148 705 165 724T213 743Q243 743 268 719T304 653L328 685Q348 712 370 727T420 743Q447 743 462 726T478 685Q478 659 461 635T413 590Q383 569 349 553T282 529Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="583" d="M26 58Q-11 97 -11 140Q-11 163 -1 181T23 213T48 232T62 236Q78 216 100 199T149 168T204 147T261 139Q336 139 336 192Q336 214 308 225T239 252Q210 263 182 277T130 313T92 365T77 437Q77 480 93 520T142 592T228 642T354 661Q435 661 491 642T578 592Q610 558 610 518Q610 492 598 472T572 437T545 417T530 413Q498 455 451 481T352 508Q314 508 295 494T275 457Q275 434 306 422T383 392Q415 380 446 366T502 330T542 281T557 213Q557 165 540 124T486 52T395 4T263 -14Q175 -14 117 6T26 58ZM283 711Q280 717 287 738T310 783T348 832T396 871Q423 886 456 886Q492 886 514 862T536 805Q536 781 520 758T477 720Q444 702 411 696T351 691T305 699T283 711Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="481" d="M18 54Q4 68 -4 84T-13 118Q-13 137 -5 152T13 178T32 193T43 196Q57 181 76 167T116 142T159 125T203 118Q249 118 249 146Q249 157 236 165T195 184Q169 194 144 206T99 234T67 274T54 332Q54 370 67 402T108 457T174 493T266 506Q330 506 380 488T461 440Q472 429 479 415T486 383Q486 360 477 344T456 317T435 301T423 298Q397 330 359 354T279 378Q255 378 243 370T231 349Q231 338 246 330T298 306Q328 294 355 281T402 251T435 211T447 155Q447 80 392 35T228 -11Q151 -11 99 7T18 54ZM226 567Q223 573 230 594T253 639T291 688T339 727Q366 742 399 742Q435 742 457 718T479 661Q479 637 463 614T420 576Q387 558 354 552T294 547T248 555T226 567Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="583" d="M26 58Q-11 97 -11 140Q-11 163 -1 181T23 213T48 232T62 236Q78 216 100 199T149 168T204 147T261 139Q336 139 336 192Q336 214 308 225T239 252Q210 263 182 277T130 313T92 365T77 437Q77 480 93 520T142 592T228 642T354 661Q435 661 491 642T578 592Q610 558 610 518Q610 492 598 472T572 437T545 417T530 413Q498 455 451 481T352 508Q314 508 295 494T275 457Q275 434 306 422T383 392Q415 380 446 366T502 330T542 281T557 213Q557 165 540 124T486 52T395 4T263 -14Q175 -14 117 6T26 58ZM275 685Q248 685 233 702T218 742Q218 768 235 792T282 838Q312 860 347 875T414 898Q432 892 456 876T502 838Q522 817 535 795T548 752Q548 723 530 704T482 685Q458 685 438 700T404 743Q401 750 398 758T391 775Q385 767 380 759T367 743Q347 715 325 700T275 685Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="481" d="M18 54Q4 68 -4 84T-13 118Q-13 137 -5 152T13 178T32 193T43 196Q57 181 76 167T116 142T159 125T203 118Q249 118 249 146Q249 157 236 165T195 184Q169 194 144 206T99 234T67 274T54 332Q54 370 67 402T108 457T174 493T266 506Q330 506 380 488T461 440Q472 429 479 415T486 383Q486 360 477 344T456 317T435 301T423 298Q397 330 359 354T279 378Q255 378 243 370T231 349Q231 338 246 330T298 306Q328 294 355 281T402 251T435 211T447 155Q447 80 392 35T228 -11Q151 -11 99 7T18 54ZM211 541Q184 541 169 558T154 598Q154 624 171 648T218 694Q248 716 283 731T350 754Q368 748 392 732T438 694Q458 673 471 651T484 608Q484 579 466 560T418 541Q394 541 374 556T340 599Q337 606 334 614T327 631Q321 623 316 615T303 599Q283 571 261 556T211 541Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="583" d="M26 58Q-11 97 -11 140Q-11 163 -1 181T23 213T48 232T62 236Q78 216 100 199T149 168T204 147T261 139Q336 139 336 192Q336 214 308 225T239 252Q210 263 182 277T130 313T92 365T77 437Q77 480 93 520T142 592T228 642T354 661Q435 661 491 642T578 592Q610 558 610 518Q610 492 598 472T572 437T545 417T530 413Q498 455 451 481T352 508Q314 508 295 494T275 457Q275 434 306 422T383 392Q415 380 446 366T502 330T542 281T557 213Q557 165 540 124T486 52T395 4T263 -14Q175 -14 117 6T26 58ZM179 -39Q180 -38 199 -38T242 -43T285 -62T305 -100Q305 -125 290 -142T244 -179L160 -234Q152 -240 137 -238T109 -229T89 -211T90 -186L179 -39Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="481" d="M18 54Q4 68 -4 84T-13 118Q-13 137 -5 152T13 178T32 193T43 196Q57 181 76 167T116 142T159 125T203 118Q249 118 249 146Q249 157 236 165T195 184Q169 194 144 206T99 234T67 274T54 332Q54 370 67 402T108 457T174 493T266 506Q330 506 380 488T461 440Q472 429 479 415T486 383Q486 360 477 344T456 317T435 301T423 298Q397 330 359 354T279 378Q255 378 243 370T231 349Q231 338 246 330T298 306Q328 294 355 281T402 251T435 211T447 155Q447 80 392 35T228 -11Q151 -11 99 7T18 54ZM166 -39Q167 -38 186 -38T229 -43T272 -62T292 -100Q292 -125 277 -142T231 -179L147 -234Q139 -240 124 -238T96 -229T76 -211T77 -186L166 -39Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="583" d="M26 58Q-11 97 -11 140Q-11 163 -1 181T23 213T48 232T62 236Q78 216 100 199T149 168T204 147T261 139Q336 139 336 192Q336 214 308 225T239 252Q210 263 182 277T130 313T92 365T77 437Q77 480 93 520T142 592T228 642T354 661Q435 661 491 642T578 592Q610 558 610 518Q610 492 598 472T572 437T545 417T530 413Q498 455 451 481T352 508Q314 508 295 494T275 457Q275 434 306 422T383 392Q415 380 446 366T502 330T542 281T557 213Q557 165 540 124T486 52T395 4T263 -14Q175 -14 117 6T26 58ZM375 684Q357 691 332 707T286 745Q266 766 254 788T241 831Q241 860 258 879T306 898Q336 898 361 874T397 808L421 840Q441 867 463 882T513 898Q540 898 555 881T571 840Q571 814 554 790T506 745Q476 724 442 708T375 684Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="481" d="M18 54Q4 68 -4 84T-13 118Q-13 137 -5 152T13 178T32 193T43 196Q57 181 76 167T116 142T159 125T203 118Q249 118 249 146Q249 157 236 165T195 184Q169 194 144 206T99 234T67 274T54 332Q54 370 67 402T108 457T174 493T266 506Q330 506 380 488T461 440Q472 429 479 415T486 383Q486 360 477 344T456 317T435 301T423 298Q397 330 359 354T279 378Q255 378 243 370T231 349Q231 338 246 330T298 306Q328 294 355 281T402 251T435 211T447 155Q447 80 392 35T228 -11Q151 -11 99 7T18 54ZM308 529Q290 536 265 552T219 590Q199 611 187 633T174 676Q174 705 191 724T239 743Q269 743 294 719T330 653L354 685Q374 712 396 727T446 743Q473 743 488 726T504 685Q504 659 487 635T439 590Q409 569 375 553T308 529Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="619" d="M258 473H156Q114 473 93 490T72 535Q72 542 73 554T79 578T89 603T103 624Q114 635 130 641T182 647H595Q637 647 658 630T680 585Q680 578 678 567T673 542T663 517T648 496Q637 485 621 479T569 473H459L358 0H157L258 473ZM179 -32Q180 -31 191 -30T219 -30T255 -35T291 -49T319 -72T330 -109Q330 -135 319 -155T276 -207L174 -306Q165 -314 148 -314T115 -307T89 -285T86 -252L179 -32H179Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="433" d="M111 351H35L65 496H142L145 511Q158 576 189 606T285 637Q316 637 336 628T355 614L330 496H455L424 351H296L262 189Q255 157 265 143T298 128Q314 128 328 134T354 150Q357 153 369 140T382 99Q382 58 351 27Q312 -14 229 -14Q133 -14 95 37T75 180L111 351ZM132 -32Q133 -31 144 -30T172 -30T208 -35T244 -49T272 -72T283 -109Q283 -135 272 -155T229 -207L127 -306Q118 -314 101 -314T68 -307T42 -285T39 -252L132 -32H132Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="619" d="M258 473H156Q114 473 93 490T72 535Q72 542 73 554T79 578T89 603T103 624Q114 635 130 641T182 647H595Q637 647 658 630T680 585Q680 578 678 567T673 542T663 517T648 496Q637 485 621 479T569 473H459L358 0H157L258 473ZM393 673Q375 680 350 696T304 734Q284 755 272 777T259 820Q259 849 276 868T324 887Q354 887 379 863T415 797L439 829Q459 856 481 871T531 887Q558 887 573 870T589 829Q589 803 572 779T524 734Q494 713 460 697T393 673Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="433" d="M475 821Q476 823 499 824T551 819T603 795T627 745Q627 720 616 699T574 647L468 534Q459 526 442 526T408 534T383 555T380 588L475 821ZM111 351H35L65 496H142L145 511Q158 576 189 606T285 637Q316 637 336 628T355 614L330 496H455L424 351H296L262 189Q255 157 265 143T298 128Q314 128 328 134T354 150Q357 153 369 140T382 99Q382 58 351 27Q312 -14 229 -14Q133 -14 95 37T75 180L111 351Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM305 690Q272 690 255 703T238 739Q238 745 239 754T244 773T251 793T263 810Q273 819 285 823T326 828H561Q594 828 611 815T628 779Q628 767 623 745T603 708Q593 698 581 694T540 690H305H305Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM241 546Q208 546 191 559T174 595Q174 601 175 610T180 629T187 649T199 666Q209 675 221 679T262 684H497Q530 684 547 671T564 635Q564 623 559 601T539 564Q529 554 517 550T476 546H241H241Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM291 734Q257 768 257 811Q257 836 273 856T322 876Q346 871 361 858T388 830T409 803T432 791Q445 791 457 802T482 829T513 857T553 876Q588 876 605 859T623 818Q623 798 613 779T586 742Q560 716 520 701T432 685Q384 685 349 698T291 734Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM220 590Q186 624 186 667Q186 692 202 712T251 732Q275 727 290 714T317 686T338 659T361 647Q374 647 386 658T411 685T442 713T482 732Q517 732 534 715T552 674Q552 654 542 635T515 598Q489 572 449 557T361 541Q313 541 278 554T220 590Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM436 685Q378 685 345 715T311 794Q311 822 321 848T350 894T395 925T452 937Q510 937 543 907T577 828Q577 799 567 774T538 728T493 697T436 685ZM439 763Q462 763 476 780T491 818Q491 836 480 848T449 860Q426 860 412 843T397 805Q397 787 408 775T439 763Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM360 536Q302 536 269 566T235 645Q235 673 245 699T274 745T319 776T376 788Q434 788 467 758T501 679Q501 650 491 625T462 579T417 548T360 536ZM363 614Q386 614 400 631T415 669Q415 687 404 699T373 711Q350 711 336 694T321 656Q321 638 332 626T363 614Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="671" d="M106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q604 122 524 55T313 -13Q150 -13 84 68T51 307L106 561ZM461 711Q458 717 465 737T486 783T522 832T569 872Q598 888 623 888Q659 888 684 867T709 814Q709 788 694 762T652 721Q620 703 588 697T528 692T483 699T461 711ZM214 711Q210 717 217 737T239 783T275 832T322 872Q351 888 376 888Q412 888 437 867T462 814Q462 788 447 762T405 721Q373 703 341 697T281 692T236 699T214 711Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 42 565 34T550 15T519 -3T467 -12Q408 -12 380 14T348 86Q316 40 273 14T171 -13ZM391 567Q388 573 395 593T416 639T452 688T499 728Q528 744 553 744Q589 744 614 723T639 670Q639 644 624 618T582 577Q550 559 518 553T458 548T413 555T391 567ZM144 567Q140 573 147 593T169 639T205 688T252 728Q281 744 306 744Q342 744 367 723T392 670Q392 644 377 618T335 577Q303 559 271 553T211 548T166 555T144 567Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="671" d="M279 -94Q279 -49 321 -13H313Q150 -13 84 68T51 307L106 561Q116 608 138 630T204 652H234Q276 652 296 629T307 558L250 292Q236 229 256 196T334 162Q373 162 399 187T438 273L499 559Q510 607 532 629T598 652H624Q666 652 687 628T697 558L634 262Q597 90 480 25Q437 -4 422 -23T406 -59Q406 -84 433 -84Q452 -84 463 -68Q476 -72 484 -83T493 -109Q493 -141 468 -162T390 -183Q342 -183 311 -159T279 -94Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="615" d="M171 -13Q82 -13 46 48T32 214L92 496H284L226 234Q216 193 230 168T280 143Q305 143 322 153T355 186L421 496H613L536 135Q526 90 533 70T567 42Q568 41 561 27T531 1Q483 -31 466 -50T449 -88Q449 -113 476 -113Q495 -113 506 -97Q519 -101 527 -112T536 -138Q536 -170 511 -191T433 -212Q385 -212 354 -188T322 -123Q322 -88 349 -57T425 -7Q387 1 369 25T348 86Q316 40 273 14T171 -13Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="979" d="M106 599Q106 605 115 614T141 631T178 646T224 652Q275 652 296 622T316 546L310 256L440 522Q457 557 488 573T554 590Q602 590 628 571T655 511L666 252L837 654Q837 655 849 655T880 655T918 648T956 632T986 604T998 560Q998 544 992 523T965 467L726 0H528L511 338L339 0H141L106 599ZM477 682Q450 682 435 699T420 739Q420 765 437 789T484 835Q514 857 549 872T616 895Q634 889 658 873T704 835Q724 814 737 792T750 749Q750 720 732 701T684 682Q660 682 640 697T606 740Q603 747 600 755T593 772Q587 764 582 756T569 740Q549 712 527 697T477 682Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="805" d="M94 329Q92 378 74 398T28 422Q26 422 29 435T43 464T77 493T136 506Q189 506 218 478Q256 439 256 339L255 202L352 407Q367 439 388 450T441 462Q485 462 510 446T538 389L547 200L686 505Q687 506 707 505T750 495T794 469T814 419Q814 402 808 386T785 340L592 0H435L408 242L271 0H115L94 329ZM366 539Q339 539 324 556T309 596Q309 622 326 646T373 692Q403 714 438 729T505 752Q523 746 547 730T593 692Q613 671 626 649T639 606Q639 577 621 558T573 539Q549 539 529 554T495 597Q492 604 489 612T482 629Q476 621 471 613T458 597Q438 569 416 554T366 539Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="632" d="M228 255L46 647H260L351 405L527 659Q528 660 551 658T603 646T655 618T679 567Q679 548 667 523T627 468L428 253L373 0H173L228 255ZM284 685Q257 685 242 702T227 742Q227 768 244 792T291 838Q321 860 356 875T423 898Q441 892 465 876T511 838Q531 817 544 795T557 752Q557 723 539 704T491 685Q467 685 447 700T413 743Q410 750 407 758T400 775Q394 767 389 759T376 743Q356 715 334 700T284 685Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="572" d="M-7 -38Q1 -45 17 -50T47 -55Q74 -55 98 -39T155 24L165 38Q130 49 123 96L94 322Q86 377 68 397T23 421Q21 421 25 434T41 464T77 493T139 506Q195 506 227 466T266 335L275 208L425 496H627L306 -45Q283 -81 261 -109T214 -158T160 -190T96 -201Q22 -201 -12 -168Q-36 -144 -36 -103Q-36 -90 -32 -78T-23 -57T-13 -43T-7 -38ZM228 541Q201 541 186 558T171 598Q171 624 188 648T235 694Q265 716 300 731T367 754Q385 748 409 732T455 694Q475 673 488 651T501 608Q501 579 483 560T435 541Q411 541 391 556T357 599Q354 606 351 614T344 631Q338 623 333 615T320 599Q300 571 278 556T228 541Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="632" d="M228 255L46 647H260L351 405L527 659Q528 660 551 658T603 646T655 618T679 567Q679 548 667 523T627 468L428 253L373 0H173L228 255ZM516 693Q473 693 452 714T430 767Q430 813 458 840T526 868Q569 868 590 847T611 793Q611 748 583 721T516 693ZM290 693Q247 693 226 714T205 767Q205 813 233 840T300 868Q343 868 364 847T386 793Q386 748 358 721T290 693Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="591" d="M10 161L354 482H194Q90 482 90 552Q90 569 95 586T106 616T118 638T126 647H638L603 483L259 166H439Q544 166 544 96Q544 79 539 62T528 31T515 9T508 0H-24L10 161ZM265 711Q262 717 269 738T292 783T330 832T378 871Q405 886 438 886Q474 886 496 862T518 805Q518 781 502 758T459 720Q426 702 393 696T333 691T287 699T265 711Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="516" d="M4 135L269 352H157Q66 352 66 413Q66 428 70 442T80 469T91 488T97 496H532L506 361L241 144H371Q462 144 462 83Q462 68 458 53T448 27T437 8T431 0H-22L4 135ZM198 567Q195 573 202 594T225 639T263 688T311 727Q338 742 371 742Q407 742 429 718T451 661Q451 637 435 614T392 576Q359 558 326 552T266 547T220 555T198 567Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="591" d="M10 161L354 482H194Q90 482 90 552Q90 569 95 586T106 616T118 638T126 647H638L603 483L259 166H439Q544 166 544 96Q544 79 539 62T528 31T515 9T508 0H-24L10 161ZM374 685Q329 685 307 707T284 763Q284 811 313 839T384 868Q430 868 452 846T474 791Q474 767 466 748T445 714T413 693T374 685Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="516" d="M4 135L269 352H157Q66 352 66 413Q66 428 70 442T80 469T91 488T97 496H532L506 361L241 144H371Q462 144 462 83Q462 68 458 53T448 27T437 8T431 0H-22L4 135ZM304 541Q259 541 237 563T214 619Q214 667 243 695T314 724Q360 724 382 702T404 647Q404 623 396 604T375 570T343 549T304 541Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="591" d="M10 161L354 482H194Q90 482 90 552Q90 569 95 586T106 616T118 638T126 647H638L603 483L259 166H439Q544 166 544 96Q544 79 539 62T528 31T515 9T508 0H-24L10 161ZM361 684Q343 691 318 707T272 745Q252 766 240 788T227 831Q227 860 244 879T292 898Q322 898 347 874T383 808L407 840Q427 867 449 882T499 898Q526 898 541 881T557 840Q557 814 540 790T492 745Q462 724 428 708T361 684Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="516" d="M4 135L269 352H157Q66 352 66 413Q66 428 70 442T80 469T91 488T97 496H532L506 361L241 144H371Q462 144 462 83Q462 68 458 53T448 27T437 8T431 0H-22L4 135ZM300 529Q282 536 257 552T211 590Q191 611 179 633T166 676Q166 705 183 724T231 743Q261 743 286 719T322 653L346 685Q366 712 388 727T438 743Q465 743 480 726T496 685Q496 659 479 635T431 590Q401 569 367 553T300 529Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="441" d="M109 276H92Q60 276 42 290T24 327Q24 333 25 342T30 362T39 383T51 401Q60 411 73 415T116 420H139L148 462Q160 519 181 556T231 616T292 648T363 658Q405 658 433 648T480 618Q491 608 496 594T502 565Q502 546 495 530T479 502T462 484T453 479Q442 491 426 498T389 505Q366 505 352 494T330 452L323 420H372Q406 420 423 406T441 369Q441 363 440 354T435 334T427 313T415 295Q405 286 392 281T350 276H300L266 114Q258 72 245 46T215 5T177 -14T135 -19Q116 -19 101 -16T75 -7T59 3T53 10L109 276Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="583" d="M26 58Q-11 97 -11 140Q-11 163 -1 181T23 213T48 232T62 236Q78 216 100 199T149 168T204 147T261 139Q336 139 336 192Q336 214 308 225T239 252Q210 263 182 277T130 313T92 365T77 437Q77 480 93 520T142 592T228 642T354 661Q435 661 491 642T578 592Q610 558 610 518Q610 492 598 472T572 437T545 417T530 413Q498 455 451 481T352 508Q314 508 295 494T275 457Q275 434 306 422T383 392Q415 380 446 366T502 330T542 281T557 213Q557 165 540 124T486 52T395 4T263 -14Q175 -14 117 6T26 58ZM205 -32Q206 -31 217 -30T245 -30T281 -35T317 -49T345 -72T356 -109Q356 -135 345 -155T302 -207L200 -306Q191 -314 174 -314T141 -307T115 -285T112 -252L205 -32H205Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="481" d="M18 54Q4 68 -4 84T-13 118Q-13 137 -5 152T13 178T32 193T43 196Q57 181 76 167T116 142T159 125T203 118Q249 118 249 146Q249 157 236 165T195 184Q169 194 144 206T99 234T67 274T54 332Q54 370 67 402T108 457T174 493T266 506Q330 506 380 488T461 440Q472 429 479 415T486 383Q486 360 477 344T456 317T435 301T423 298Q397 330 359 354T279 378Q255 378 243 370T231 349Q231 338 246 330T298 306Q328 294 355 281T402 251T435 211T447 155Q447 80 392 35T228 -11Q151 -11 99 7T18 54ZM156 -32Q157 -31 168 -30T196 -30T232 -35T268 -49T296 -72T307 -109Q307 -135 296 -155T253 -207L151 -306Q142 -314 125 -314T92 -307T66 -285T63 -252L156 -32H156Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="500" d="M217 541Q190 541 175 558T160 598Q160 624 177 648T224 694Q254 716 289 731T356 754Q374 748 398 732T444 694Q464 673 477 651T490 608Q490 579 472 560T424 541Q400 541 380 556T346 599Q343 606 340 614T333 631Q327 623 322 615T309 599Q289 571 267 556T217 541Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="500" d="M312 529Q294 536 269 552T223 590Q203 611 191 633T178 676Q178 705 195 724T243 743Q273 743 298 719T334 653L358 685Q378 712 400 727T450 743Q477 743 492 726T508 685Q508 659 491 635T443 590Q413 569 379 553T312 529Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="500" d="M190 590Q156 624 156 667Q156 692 172 712T221 732Q245 727 260 714T287 686T308 659T331 647Q344 647 356 658T381 685T412 713T452 732Q487 732 504 715T522 674Q522 654 512 635T485 598Q459 572 419 557T331 541Q283 541 248 554T190 590Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="500" d="M326 541Q281 541 259 563T236 619Q236 667 265 695T336 724Q382 724 404 702T426 647Q426 623 418 604T397 570T365 549T326 541Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="500" d="M331 541Q273 541 240 571T206 650Q206 678 216 704T245 750T290 781T347 793Q405 793 438 763T472 684Q472 655 462 630T433 584T388 553T331 541ZM334 619Q357 619 371 636T386 674Q386 692 375 704T344 716Q321 716 307 699T292 661Q292 643 303 631T334 619Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="500" d="M74 -119Q74 -95 86 -73T122 -33T177 -3T251 12L296 14Q268 -4 250 -18T221 -43T206 -65T201 -84Q201 -109 228 -109Q247 -109 258 -93Q271 -97 279 -108T288 -134Q288 -166 263 -187T185 -208Q137 -208 106 -184T74 -119Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="499" d="M200 542Q175 542 161 557T145 593Q146 622 156 645T182 684T220 708T263 717Q289 717 308 710T341 692Q358 681 367 673T384 665Q394 665 401 674T417 695Q425 706 434 710T458 714Q483 714 497 699T513 663Q512 634 502 612T476 573T438 549T395 540Q368 540 350 548T319 565Q302 578 294 588T274 599Q264 599 259 589T246 564Q240 554 230 548T200 542Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="500" d="M352 567Q349 573 356 593T377 639T413 688T460 728Q489 744 514 744Q550 744 575 723T600 670Q600 644 585 618T543 577Q511 559 479 553T419 548T374 555T352 567ZM105 567Q101 573 108 593T130 639T166 688T213 728Q242 744 267 744Q303 744 328 723T353 670Q353 644 338 618T296 577Q264 559 232 553T172 548T127 555T105 567Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="747" d="M52 324Q52 397 76 459T146 566T253 636T390 661Q465 661 526 636T632 567T701 460T726 324Q726 250 702 188T633 81T526 11T389 -14Q314 -14 252 11T146 81T77 188T52 324ZM123 324Q123 265 142 216T197 130T281 74T390 53Q450 53 498 73T582 129T636 215T655 324Q655 383 636 432T581 518T497 574T389 594Q329 594 280 574T196 518T142 433T123 324ZM246 427Q246 491 306 491H400Q478 491 518 459T559 361Q559 232 405 232H363V187Q363 134 312 134H295Q246 134 246 187V427ZM400 317Q447 317 447 361Q447 381 435 393T397 405H361V317H400Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="575" d="M103 167Q65 167 46 182T26 224Q26 230 27 241T32 263T41 286T55 305Q66 316 81 321T128 327H473Q511 327 530 311T549 269Q549 263 548 252T543 230T535 207T521 188Q510 177 495 172T448 167H103H103Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="737" d="M103 167Q65 167 46 182T26 224Q26 230 27 241T32 263T41 286T55 305Q66 316 81 321T128 327H635Q673 327 692 311T711 269Q711 263 710 252T705 230T697 207T683 188Q672 177 657 172T610 167H103H103Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="256" d="M216 372Q215 370 192 369T140 374T88 398T64 448Q64 473 75 493T118 546L223 659Q231 667 248 667T282 659T308 638T311 605L216 372H216Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="256" d="M145 372Q136 364 119 364T85 372T60 393T57 426L152 659Q153 661 176 662T228 657T280 633T304 583Q304 558 293 537T251 485L145 372H145Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="266" d="M56 172Q57 174 80 175T132 170T184 146T208 96Q208 71 197 51T154 -2L49 -115Q40 -123 23 -123T-11 -115T-36 -94T-39 -61L56 172Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="489" d="M208 376Q207 374 185 373T136 378T87 402T65 452Q65 464 67 475T76 497T93 521T120 550L231 667Q239 675 256 675T289 667T314 646T316 613L208 376ZM302 448Q302 471 311 490T351 542L453 655Q461 663 478 663T511 656T538 635T541 602L449 368Q449 367 438 366T410 366T375 371T340 385T313 410T302 448Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="489" d="M149 376Q141 368 124 368T90 375T63 397T60 430L152 664Q152 665 163 666T191 666T227 661T262 647T289 622T300 583Q300 561 290 542T251 490L149 376ZM394 656Q394 657 405 658T432 658T466 653T500 641T526 617T537 580Q537 555 525 535T482 482L371 365Q363 357 346 357T312 364T287 386T286 419L394 656Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="490" d="M48 174Q49 176 72 176T122 171T172 146T195 94Q195 71 186 52T146 0L44 -114Q36 -122 19 -122T-15 -115T-42 -93T-45 -60L48 174ZM289 166Q290 168 312 169T361 164T410 140T432 90Q432 65 420 44T377 -8L266 -125Q258 -133 241 -133T208 -126T183 -104T181 -71L289 166Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="540" d="M176 203Q183 245 197 279T245 344L111 309Q95 304 83 307T64 319T52 340T48 366Q48 388 52 412T65 455T91 483T130 486L262 451L259 593Q257 628 276 644T342 661Q401 661 420 639T423 581L365 452L493 486Q509 491 521 488T541 476T553 455T557 429Q557 407 553 383T540 340T514 311T475 308L340 344Q358 318 359 289T350 218L268 -107Q260 -139 245 -147T208 -155H173Q147 -155 132 -145T122 -106L176 203Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="540" d="M48 15Q31 10 19 13T0 25T-12 46T-16 72Q-16 94 -12 118T1 161T27 189T66 192L169 166L179 218Q186 260 200 294T248 359L114 324Q98 319 86 322T67 334T55 355T51 381Q51 403 55 427T68 470T94 498T133 501L261 468L259 593Q257 628 276 644T342 661Q401 661 420 639T423 581L374 469L496 501Q512 506 524 503T544 491T556 470T560 444Q560 422 556 398T543 355T517 326T478 323L343 359Q361 333 362 304T353 233L337 168L430 192Q446 197 458 194T478 182T490 161T494 135Q494 113 490 89T477 46T451 17T412 14L305 42L268 -107Q260 -139 245 -147T208 -155H173Q147 -155 132 -145T122 -106L148 40L48 15H48Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="363" d="M182 101Q151 101 124 112T78 142T47 188T36 247Q36 278 47 305T77 353T124 384T182 396Q213 396 239 385T285 353T316 306T327 247Q327 217 316 190T285 144T239 113T182 101Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="776" d="M513 55Q513 75 520 103T545 149Q551 155 557 159T573 166T594 170T626 171Q668 171 688 156T708 108Q708 88 701 60T675 14Q663 2 648 -3T595 -8Q552 -8 533 7T513 55ZM254 55Q254 75 261 103T287 149Q299 161 314 166T367 171Q410 171 429 156T449 108Q449 88 442 60T417 14Q405 2 389 -3T336 -8Q294 -8 274 7T254 55ZM-4 55Q-4 75 3 103T28 149Q34 155 40 159T56 166T77 170T109 171Q151 171 171 156T191 108Q191 88 184 60T158 14Q146 2 131 -3T78 -8Q35 -8 16 7T-4 55Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1117" d="M35 53Q35 66 42 77T64 103L683 658Q686 661 698 657T723 643T747 621T758 592Q758 569 728 542L109 -14Q106 -16 95 -12T70 1T46 24T35 53ZM212 338Q174 338 145 349T96 378T66 422T56 477Q56 514 67 546T100 603T156 642T235 657Q274 657 303 646T352 617T382 573T392 518Q392 481 381 449T348 392T291 353T212 338ZM218 431Q247 431 262 454T277 512Q277 535 263 549T229 564Q201 564 185 541T169 483Q169 460 183 446T218 431ZM758 130Q758 167 769 199T802 256T858 295T937 310Q975 310 1004 299T1053 270T1083 226T1093 171Q1093 134 1082 102T1049 45T993 6T914 -9Q876 -9 847 2T798 31T768 75T758 130ZM404 130Q404 167 415 199T448 256T504 295T583 310Q622 310 651 299T700 270T730 226T740 171Q740 134 729 102T696 45T639 6T560 -9Q522 -9 493 2T444 31T414 75T404 130ZM871 136Q871 113 885 99T920 84Q949 84 964 107T979 165Q979 188 965 202T931 217Q902 217 887 194T871 136ZM517 136Q517 113 531 99T566 84Q595 84 610 107T625 165Q625 188 611 202T577 217Q549 217 533 194T517 136Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="323" d="M75 144Q64 162 55 179T39 212T29 238T26 253Q26 256 33 267T52 293T81 325T117 360Q151 392 188 415T256 439Q288 439 306 423T324 379Q324 349 303 324T247 279Q212 258 172 242Q188 233 203 222T231 198Q250 178 260 161T271 125Q271 95 250 75T191 55Q156 55 127 81T75 144Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="323" d="M66 55Q34 55 16 71T-2 115Q-2 144 19 169T75 214Q108 235 150 252Q134 261 119 272T92 296Q52 334 52 369Q52 399 73 419T131 439Q166 439 195 412T247 350Q258 332 267 315T283 282T293 256T297 241Q297 237 290 227T270 201T241 169T205 134Q171 102 134 79T66 55Z" />
+<glyph unicode="&#x2044;" glyph-name="fraction" horiz-adv-x="723" d="M13 49Q13 73 40 99L649 659Q653 663 667 661T696 650T723 630T735 601Q735 575 708 550L99 -10Q94 -14 80 -12T51 -1T25 20T13 49Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="634" d="M70 196Q41 196 27 207T12 237Q12 268 30 286Q49 305 82 305H105Q107 313 108 322T112 342H101Q72 342 58 353T43 383Q43 414 61 432Q80 451 113 451H144Q158 484 175 514T214 566Q265 616 322 638T451 661Q511 661 559 644T635 600Q652 583 659 564T666 524Q666 490 651 470T618 439T585 425T568 424Q547 461 517 480T434 499Q403 499 385 491T349 464L338 451H452Q481 451 495 439T510 409Q510 378 492 360Q474 342 440 342H298Q296 332 295 323T291 305H421Q450 305 464 293T479 263Q479 232 461 214Q443 196 409 196H293Q318 149 390 149Q410 149 426 152T458 162T487 180T517 207Q519 209 531 204T556 188T581 161T592 124Q592 83 560 51Q531 22 480 5T369 -13Q255 -13 184 42T100 196H70H70Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="715" d="M389 602Q389 627 398 639T431 652H464Q484 652 493 646T508 625L559 504L610 625Q617 642 626 647T653 652H685Q708 652 718 640T728 602V373Q728 349 719 337T687 324H670Q629 324 629 373V498L601 409Q599 400 593 394T559 388Q544 388 536 389T525 394T520 400T517 409L488 494V373Q488 349 480 337T448 324H431Q408 324 399 336T389 373V602ZM203 324Q160 324 160 373V556H111Q63 556 63 602V604Q63 647 111 647H313Q361 647 361 604V602Q361 556 313 556H265V373Q265 324 219 324H203Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="600" d="M105 178Q67 178 52 197T44 249L46 258Q54 290 76 309T137 328H498Q537 328 552 309T560 258L558 249Q550 217 528 198T467 178H105H105Z" />
+<glyph unicode="&#x2219;" glyph-name="uni2219" horiz-adv-x="262" d="M115 158Q73 158 54 173T34 221Q34 241 41 269T66 315Q72 321 78 325T94 332T115 336T147 337Q189 337 208 322T228 274Q228 254 221 227T196 180Q184 168 168 163T115 158Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="600" d="M123 59H76Q53 59 39 71T25 104Q25 118 31 140T50 177Q59 186 71 190T111 194H208L276 314H128Q105 314 91 326T77 358Q77 365 78 375T83 395T91 415T102 432Q111 440 123 444T163 449H342L414 576Q439 625 462 637T502 642L520 632Q537 624 538 602T513 530L467 449H514Q546 449 562 436T579 401Q579 395 578 386T573 367T566 348T554 331Q545 323 533 319T493 314H383L314 194H462Q494 194 510 181T527 146Q527 140 526 131T521 112T514 93T502 77Q493 68 481 64T441 59H247L177 -70Q151 -120 128 -132T90 -137L71 -127Q63 -123 58 -116T53 -96T58 -67T77 -24L123 59Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="600" d="M141 342Q118 353 107 368T95 412Q95 442 111 464T154 498L559 646Q562 647 569 641T583 622T596 594T602 561Q602 537 584 512T517 472L321 418L465 367Q508 350 522 323T535 272Q535 254 528 239T511 213T493 195T481 191L141 342ZM25 82Q25 96 30 116T48 148Q58 158 72 163T111 168H444Q478 168 495 154T512 116Q512 102 507 82T489 50Q479 40 465 35T426 30H94Q59 30 42 44T25 82Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="600" d="M103 195Q100 194 93 199T79 215T66 241T60 273Q60 285 64 298T77 323T103 345T145 362L346 423L197 476Q175 483 162 493T140 515T130 539T127 563Q127 580 134 595T151 622T169 639T181 644L521 492Q544 482 555 467T567 422Q567 392 551 370T508 336L103 195ZM25 82Q25 96 30 116T48 148Q58 158 72 163T111 168H444Q478 168 495 154T512 116Q512 102 507 82T489 50Q479 40 465 35T426 30H94Q59 30 42 44T25 82Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="713" d="M541 631Q541 678 571 705T647 733Q694 733 720 711T746 654Q746 608 716 580T640 552Q592 552 567 574T541 631ZM116 351H33L63 496H146L150 513Q162 570 183 608T233 668T294 700T365 709Q407 709 435 700T481 673Q502 652 502 622Q502 604 495 589T480 563T465 547T456 542Q446 553 430 560T393 567Q370 567 356 556T334 514L330 496H444L413 351H308L250 81Q240 33 218 15T162 -4H142Q124 -4 107 0T78 14T59 40T59 81L116 351ZM517 496H709L631 135Q621 90 629 70T663 42Q664 42 661 34T646 15T613 -3T558 -12Q516 -12 490 0T451 33T436 82T441 142L517 496Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="713" d="M116 351H33L63 496H146L150 513Q162 570 183 608T233 668T294 700T365 709Q407 709 435 700T481 673Q502 652 502 622Q502 604 495 589T480 563T465 547T456 542Q446 553 430 560T393 567Q370 567 356 556T334 514L330 496H444L413 351H308L250 81Q240 33 218 15T162 -4H142Q124 -4 107 0T78 14T59 40T59 81L116 351ZM558 697H750L631 135Q621 90 629 70T663 42Q664 42 661 34T646 15T613 -3T558 -12Q516 -12 490 0T451 33T436 82T441 142L558 697Z" />
+
+<hkern u1="&#xd0;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd0;" u2="." k="10" />
+<hkern u1="&#xd0;" u2="/" k="13" />
+<hkern u1="&#xd0;" u2="?" k="5" />
+<hkern u1="&#xd0;" u2="J" k="11" />
+<hkern u1="&#xd0;" u2="T" k="13" />
+<hkern u1="&#xd0;" u2="V" k="10" />
+<hkern u1="&#xd0;" u2="W" k="15" />
+<hkern u1="&#xd0;" u2="X" k="9" />
+<hkern u1="&#xd0;" u2="Y" k="11" />
+<hkern u1="&#xd0;" u2="Z" k="11" />
+<hkern u1="&#xd0;" u2="a" k="1" />
+<hkern u1="&#xd0;" u2="b" k="3" />
+<hkern u1="&#xd0;" u2="c" k="1" />
+<hkern u1="&#xd0;" u2="d" k="1" />
+<hkern u1="&#xd0;" u2="e" k="1" />
+<hkern u1="&#xd0;" u2="h" k="3" />
+<hkern u1="&#xd0;" u2="k" k="3" />
+<hkern u1="&#xd0;" u2="l" k="3" />
+<hkern u1="&#xd0;" u2="m" k="1" />
+<hkern u1="&#xd0;" u2="n" k="1" />
+<hkern u1="&#xd0;" u2="o" k="1" />
+<hkern u1="&#xd0;" u2="p" k="1" />
+<hkern u1="&#xd0;" u2="q" k="1" />
+<hkern u1="&#xd0;" u2="r" k="1" />
+<hkern u1="&#xd0;" u2="u" k="1" />
+<hkern u1="&#xd0;" u2="x" k="3" />
+<hkern u1="&#xd0;" u2="z" k="3" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd0;" u2="&#x153;" k="1" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd0;" u2="&#x178;" k="11" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd0;" u2="&#x131;" k="1" />
+<hkern u1="&#x141;" u2="&#xdd;" k="20" />
+<hkern u1="&#x141;" u2="@" k="13" />
+<hkern u1="&#x141;" u2="A" k="-5" />
+<hkern u1="&#x141;" u2="C" k="13" />
+<hkern u1="&#x141;" u2="G" k="13" />
+<hkern u1="&#x141;" u2="O" k="13" />
+<hkern u1="&#x141;" u2="Q" k="13" />
+<hkern u1="&#x141;" u2="T" k="30" />
+<hkern u1="&#x141;" u2="U" k="4" />
+<hkern u1="&#x141;" u2="V" k="23" />
+<hkern u1="&#x141;" u2="W" k="18" />
+<hkern u1="&#x141;" u2="Y" k="20" />
+<hkern u1="&#x141;" u2="\" k="13" />
+<hkern u1="&#x141;" u2="a" k="5" />
+<hkern u1="&#x141;" u2="c" k="5" />
+<hkern u1="&#x141;" u2="d" k="5" />
+<hkern u1="&#x141;" u2="e" k="5" />
+<hkern u1="&#x141;" u2="o" k="5" />
+<hkern u1="&#x141;" u2="q" k="5" />
+<hkern u1="&#x141;" u2="v" k="10" />
+<hkern u1="&#x141;" u2="w" k="3" />
+<hkern u1="&#x141;" u2="y" k="10" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-5" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-5" />
+<hkern u1="&#x141;" u2="&#xd6;" k="13" />
+<hkern u1="&#x141;" u2="&#xdc;" k="4" />
+<hkern u1="&#x141;" u2="&#xe7;" k="5" />
+<hkern u1="&#x141;" u2="&#xae;" k="13" />
+<hkern u1="&#x141;" u2="&#xa9;" k="13" />
+<hkern u1="&#x141;" u2="&#x2122;" k="18" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-5" />
+<hkern u1="&#x141;" u2="&#xd8;" k="13" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="13" />
+<hkern u1="&#x141;" u2="&#xe6;" k="5" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-5" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-5" />
+<hkern u1="&#x141;" u2="&#xd5;" k="13" />
+<hkern u1="&#x141;" u2="&#x152;" k="13" />
+<hkern u1="&#x141;" u2="&#x153;" k="5" />
+<hkern u1="&#x141;" u2="&#x201c;" k="25" />
+<hkern u1="&#x141;" u2="&#x201d;" k="15" />
+<hkern u1="&#x141;" u2="&#x2018;" k="25" />
+<hkern u1="&#x141;" u2="&#x2019;" k="15" />
+<hkern u1="&#x141;" u2="&#x178;" k="20" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-5" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-5" />
+<hkern u1="&#x141;" u2="&#xd3;" k="13" />
+<hkern u1="&#x141;" u2="&#xd4;" k="13" />
+<hkern u1="&#x141;" u2="&#xd2;" k="13" />
+<hkern u1="&#x141;" u2="&#xda;" k="4" />
+<hkern u1="&#x141;" u2="&#xdb;" k="4" />
+<hkern u1="&#x141;" u2="&#xd9;" k="4" />
+<hkern u1="&#xdd;" u2="&#x160;" k="-8" />
+<hkern u1="&#xdd;" u2="&#x161;" k="13" />
+<hkern u1="&#xdd;" u2="&amp;" k="8" />
+<hkern u1="&#xdd;" u2=")" k="-13" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="13" />
+<hkern u1="&#xdd;" u2="-" k="13" />
+<hkern u1="&#xdd;" u2="." k="13" />
+<hkern u1="&#xdd;" u2=":" k="8" />
+<hkern u1="&#xdd;" u2=";" k="8" />
+<hkern u1="&#xdd;" u2="@" k="8" />
+<hkern u1="&#xdd;" u2="A" k="8" />
+<hkern u1="&#xdd;" u2="C" k="1" />
+<hkern u1="&#xdd;" u2="G" k="1" />
+<hkern u1="&#xdd;" u2="J" k="18" />
+<hkern u1="&#xdd;" u2="O" k="1" />
+<hkern u1="&#xdd;" u2="Q" k="1" />
+<hkern u1="&#xdd;" u2="S" k="-8" />
+<hkern u1="&#xdd;" u2="T" k="-5" />
+<hkern u1="&#xdd;" u2="V" k="-8" />
+<hkern u1="&#xdd;" u2="X" k="-5" />
+<hkern u1="&#xdd;" u2="]" k="-13" />
+<hkern u1="&#xdd;" u2="a" k="14" />
+<hkern u1="&#xdd;" u2="c" k="14" />
+<hkern u1="&#xdd;" u2="d" k="14" />
+<hkern u1="&#xdd;" u2="e" k="14" />
+<hkern u1="&#xdd;" u2="f" k="3" />
+<hkern u1="&#xdd;" u2="g" k="10" />
+<hkern u1="&#xdd;" u2="m" k="10" />
+<hkern u1="&#xdd;" u2="n" k="10" />
+<hkern u1="&#xdd;" u2="o" k="14" />
+<hkern u1="&#xdd;" u2="p" k="10" />
+<hkern u1="&#xdd;" u2="q" k="14" />
+<hkern u1="&#xdd;" u2="r" k="10" />
+<hkern u1="&#xdd;" u2="s" k="13" />
+<hkern u1="&#xdd;" u2="t" k="5" />
+<hkern u1="&#xdd;" u2="u" k="10" />
+<hkern u1="&#xdd;" u2="v" k="5" />
+<hkern u1="&#xdd;" u2="w" k="3" />
+<hkern u1="&#xdd;" u2="x" k="5" />
+<hkern u1="&#xdd;" u2="y" k="5" />
+<hkern u1="&#xdd;" u2="z" k="8" />
+<hkern u1="&#xdd;" u2="}" k="-13" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="8" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="8" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="1" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="14" />
+<hkern u1="&#xdd;" u2="&#xae;" k="8" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="8" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="8" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="1" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="14" />
+<hkern u1="&#xdd;" u2="&#xab;" k="15" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="10" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="13" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="8" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="8" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="1" />
+<hkern u1="&#xdd;" u2="&#x152;" k="1" />
+<hkern u1="&#xdd;" u2="&#x153;" k="14" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="13" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="13" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="15" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="10" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="13" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="13" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="8" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="8" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="1" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="1" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="1" />
+<hkern u1="&#xdd;" u2="&#x131;" k="10" />
+<hkern u1="&#xde;" u2="&#xdd;" k="11" />
+<hkern u1="&#xde;" u2="&#x17d;" k="11" />
+<hkern u1="&#xde;" u2="&#x2c;" k="10" />
+<hkern u1="&#xde;" u2="." k="10" />
+<hkern u1="&#xde;" u2="/" k="13" />
+<hkern u1="&#xde;" u2="?" k="5" />
+<hkern u1="&#xde;" u2="J" k="11" />
+<hkern u1="&#xde;" u2="T" k="13" />
+<hkern u1="&#xde;" u2="V" k="10" />
+<hkern u1="&#xde;" u2="W" k="15" />
+<hkern u1="&#xde;" u2="X" k="9" />
+<hkern u1="&#xde;" u2="Y" k="11" />
+<hkern u1="&#xde;" u2="Z" k="11" />
+<hkern u1="&#xde;" u2="a" k="1" />
+<hkern u1="&#xde;" u2="b" k="3" />
+<hkern u1="&#xde;" u2="c" k="1" />
+<hkern u1="&#xde;" u2="d" k="1" />
+<hkern u1="&#xde;" u2="e" k="1" />
+<hkern u1="&#xde;" u2="h" k="3" />
+<hkern u1="&#xde;" u2="k" k="3" />
+<hkern u1="&#xde;" u2="l" k="3" />
+<hkern u1="&#xde;" u2="m" k="1" />
+<hkern u1="&#xde;" u2="n" k="1" />
+<hkern u1="&#xde;" u2="o" k="1" />
+<hkern u1="&#xde;" u2="p" k="1" />
+<hkern u1="&#xde;" u2="q" k="1" />
+<hkern u1="&#xde;" u2="r" k="1" />
+<hkern u1="&#xde;" u2="u" k="1" />
+<hkern u1="&#xde;" u2="x" k="3" />
+<hkern u1="&#xde;" u2="z" k="3" />
+<hkern u1="&#xde;" u2="&#xe7;" k="1" />
+<hkern u1="&#xde;" u2="&#xe6;" k="1" />
+<hkern u1="&#xde;" u2="&#x2026;" k="10" />
+<hkern u1="&#xde;" u2="&#x153;" k="1" />
+<hkern u1="&#xde;" u2="&#x201c;" k="5" />
+<hkern u1="&#xde;" u2="&#x2018;" k="5" />
+<hkern u1="&#xde;" u2="&#x178;" k="11" />
+<hkern u1="&#xde;" u2="&#x201a;" k="10" />
+<hkern u1="&#xde;" u2="&#x201e;" k="10" />
+<hkern u1="&#xde;" u2="&#x131;" k="1" />
+<hkern u1="&amp;" u2="&#xdd;" k="8" />
+<hkern u1="&amp;" u2="A" k="-10" />
+<hkern u1="&amp;" u2="J" k="-5" />
+<hkern u1="&amp;" u2="T" k="10" />
+<hkern u1="&amp;" u2="V" k="5" />
+<hkern u1="&amp;" u2="W" k="5" />
+<hkern u1="&amp;" u2="X" k="-5" />
+<hkern u1="&amp;" u2="Y" k="8" />
+<hkern u1="&amp;" u2="&#xc4;" k="-10" />
+<hkern u1="&amp;" u2="&#xc5;" k="-10" />
+<hkern u1="&amp;" u2="&#xc6;" k="-10" />
+<hkern u1="&amp;" u2="&#xc0;" k="-10" />
+<hkern u1="&amp;" u2="&#xc3;" k="-10" />
+<hkern u1="&amp;" u2="&#x178;" k="8" />
+<hkern u1="&amp;" u2="&#xc2;" k="-10" />
+<hkern u1="&amp;" u2="&#xc1;" k="-10" />
+<hkern u1="(" u2="&#xdd;" k="-13" />
+<hkern u1="(" u2="V" k="-8" />
+<hkern u1="(" u2="W" k="-3" />
+<hkern u1="(" u2="X" k="-10" />
+<hkern u1="(" u2="Y" k="-13" />
+<hkern u1="(" u2="g" k="-5" />
+<hkern u1="(" u2="j" k="-38" />
+<hkern u1="(" u2="&#x178;" k="-13" />
+<hkern u1="*" u2="A" k="15" />
+<hkern u1="*" u2="&#xc4;" k="15" />
+<hkern u1="*" u2="&#xc5;" k="15" />
+<hkern u1="*" u2="&#xc6;" k="15" />
+<hkern u1="*" u2="&#xc0;" k="15" />
+<hkern u1="*" u2="&#xc3;" k="15" />
+<hkern u1="*" u2="&#xc2;" k="15" />
+<hkern u1="*" u2="&#xc1;" k="15" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="13" />
+<hkern u1="&#x2c;" u2="0" k="10" />
+<hkern u1="&#x2c;" u2="1" k="15" />
+<hkern u1="&#x2c;" u2="4" k="13" />
+<hkern u1="&#x2c;" u2="6" k="8" />
+<hkern u1="&#x2c;" u2="8" k="4" />
+<hkern u1="&#x2c;" u2="9" k="3" />
+<hkern u1="&#x2c;" u2="A" k="-8" />
+<hkern u1="&#x2c;" u2="C" k="10" />
+<hkern u1="&#x2c;" u2="G" k="10" />
+<hkern u1="&#x2c;" u2="O" k="10" />
+<hkern u1="&#x2c;" u2="Q" k="10" />
+<hkern u1="&#x2c;" u2="T" k="15" />
+<hkern u1="&#x2c;" u2="U" k="3" />
+<hkern u1="&#x2c;" u2="V" k="15" />
+<hkern u1="&#x2c;" u2="W" k="5" />
+<hkern u1="&#x2c;" u2="Y" k="13" />
+<hkern u1="&#x2c;" u2="a" k="5" />
+<hkern u1="&#x2c;" u2="c" k="5" />
+<hkern u1="&#x2c;" u2="d" k="5" />
+<hkern u1="&#x2c;" u2="e" k="5" />
+<hkern u1="&#x2c;" u2="m" k="5" />
+<hkern u1="&#x2c;" u2="n" k="5" />
+<hkern u1="&#x2c;" u2="o" k="5" />
+<hkern u1="&#x2c;" u2="p" k="5" />
+<hkern u1="&#x2c;" u2="q" k="5" />
+<hkern u1="&#x2c;" u2="r" k="5" />
+<hkern u1="&#x2c;" u2="t" k="10" />
+<hkern u1="&#x2c;" u2="u" k="5" />
+<hkern u1="&#x2c;" u2="v" k="8" />
+<hkern u1="&#x2c;" u2="w" k="3" />
+<hkern u1="&#x2c;" u2="y" k="8" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="10" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="3" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="5" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="10" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="5" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="10" />
+<hkern u1="&#x2c;" u2="&#x152;" k="10" />
+<hkern u1="&#x2c;" u2="&#x153;" k="5" />
+<hkern u1="&#x2c;" u2="&#x178;" k="13" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-8" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="10" />
+<hkern u1="&#x2c;" u2="&#xda;" k="3" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="3" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="3" />
+<hkern u1="&#x2c;" u2="&#x131;" k="5" />
+<hkern u1="-" u2="&#xdd;" k="13" />
+<hkern u1="-" u2="&#x17d;" k="3" />
+<hkern u1="-" u2="1" k="5" />
+<hkern u1="-" u2="7" k="8" />
+<hkern u1="-" u2="A" k="-3" />
+<hkern u1="-" u2="T" k="18" />
+<hkern u1="-" u2="V" k="13" />
+<hkern u1="-" u2="W" k="10" />
+<hkern u1="-" u2="X" k="8" />
+<hkern u1="-" u2="Y" k="13" />
+<hkern u1="-" u2="Z" k="3" />
+<hkern u1="-" u2="a" k="3" />
+<hkern u1="-" u2="c" k="3" />
+<hkern u1="-" u2="d" k="3" />
+<hkern u1="-" u2="e" k="3" />
+<hkern u1="-" u2="o" k="3" />
+<hkern u1="-" u2="q" k="3" />
+<hkern u1="-" u2="v" k="3" />
+<hkern u1="-" u2="x" k="8" />
+<hkern u1="-" u2="y" k="3" />
+<hkern u1="-" u2="z" k="8" />
+<hkern u1="-" u2="&#xc4;" k="-3" />
+<hkern u1="-" u2="&#xc5;" k="-3" />
+<hkern u1="-" u2="&#xe7;" k="3" />
+<hkern u1="-" u2="&#xc6;" k="-3" />
+<hkern u1="-" u2="&#xe6;" k="3" />
+<hkern u1="-" u2="&#xc0;" k="-3" />
+<hkern u1="-" u2="&#xc3;" k="-3" />
+<hkern u1="-" u2="&#x153;" k="3" />
+<hkern u1="-" u2="&#x178;" k="13" />
+<hkern u1="-" u2="&#xc2;" k="-3" />
+<hkern u1="-" u2="&#xc1;" k="-3" />
+<hkern u1="." u2="&#xdd;" k="13" />
+<hkern u1="." u2="0" k="10" />
+<hkern u1="." u2="1" k="15" />
+<hkern u1="." u2="4" k="13" />
+<hkern u1="." u2="6" k="8" />
+<hkern u1="." u2="8" k="4" />
+<hkern u1="." u2="9" k="3" />
+<hkern u1="." u2="A" k="-8" />
+<hkern u1="." u2="C" k="10" />
+<hkern u1="." u2="G" k="10" />
+<hkern u1="." u2="O" k="10" />
+<hkern u1="." u2="Q" k="10" />
+<hkern u1="." u2="T" k="15" />
+<hkern u1="." u2="U" k="3" />
+<hkern u1="." u2="V" k="15" />
+<hkern u1="." u2="W" k="5" />
+<hkern u1="." u2="Y" k="13" />
+<hkern u1="." u2="a" k="5" />
+<hkern u1="." u2="c" k="5" />
+<hkern u1="." u2="d" k="5" />
+<hkern u1="." u2="e" k="5" />
+<hkern u1="." u2="m" k="5" />
+<hkern u1="." u2="n" k="5" />
+<hkern u1="." u2="o" k="5" />
+<hkern u1="." u2="p" k="5" />
+<hkern u1="." u2="q" k="5" />
+<hkern u1="." u2="r" k="5" />
+<hkern u1="." u2="t" k="10" />
+<hkern u1="." u2="u" k="5" />
+<hkern u1="." u2="v" k="8" />
+<hkern u1="." u2="w" k="3" />
+<hkern u1="." u2="y" k="8" />
+<hkern u1="." u2="&#xc4;" k="-8" />
+<hkern u1="." u2="&#xc5;" k="-8" />
+<hkern u1="." u2="&#xd6;" k="10" />
+<hkern u1="." u2="&#xdc;" k="3" />
+<hkern u1="." u2="&#xe7;" k="5" />
+<hkern u1="." u2="&#xc6;" k="-8" />
+<hkern u1="." u2="&#xd8;" k="10" />
+<hkern u1="." u2="&#xe6;" k="5" />
+<hkern u1="." u2="&#xc0;" k="-8" />
+<hkern u1="." u2="&#xc3;" k="-8" />
+<hkern u1="." u2="&#xd5;" k="10" />
+<hkern u1="." u2="&#x152;" k="10" />
+<hkern u1="." u2="&#x153;" k="5" />
+<hkern u1="." u2="&#x178;" k="13" />
+<hkern u1="." u2="&#xc2;" k="-8" />
+<hkern u1="." u2="&#xc1;" k="-8" />
+<hkern u1="." u2="&#xd3;" k="10" />
+<hkern u1="." u2="&#xd4;" k="10" />
+<hkern u1="." u2="&#xd2;" k="10" />
+<hkern u1="." u2="&#xda;" k="3" />
+<hkern u1="." u2="&#xdb;" k="3" />
+<hkern u1="." u2="&#xd9;" k="3" />
+<hkern u1="." u2="&#x131;" k="5" />
+<hkern u1="/" u2="1" k="-5" />
+<hkern u1="/" u2="4" k="5" />
+<hkern u1="/" u2="C" k="-3" />
+<hkern u1="/" u2="G" k="-3" />
+<hkern u1="/" u2="O" k="-3" />
+<hkern u1="/" u2="Q" k="-3" />
+<hkern u1="/" u2="a" k="3" />
+<hkern u1="/" u2="c" k="3" />
+<hkern u1="/" u2="d" k="3" />
+<hkern u1="/" u2="e" k="3" />
+<hkern u1="/" u2="g" k="3" />
+<hkern u1="/" u2="o" k="3" />
+<hkern u1="/" u2="q" k="3" />
+<hkern u1="/" u2="&#xd6;" k="-3" />
+<hkern u1="/" u2="&#xe7;" k="3" />
+<hkern u1="/" u2="&#xd8;" k="-3" />
+<hkern u1="/" u2="&#xe6;" k="3" />
+<hkern u1="/" u2="&#xd5;" k="-3" />
+<hkern u1="/" u2="&#x152;" k="-3" />
+<hkern u1="/" u2="&#x153;" k="3" />
+<hkern u1="/" u2="&#xd3;" k="-3" />
+<hkern u1="/" u2="&#xd4;" k="-3" />
+<hkern u1="/" u2="&#xd2;" k="-3" />
+<hkern u1="0" u2="&#x2c;" k="10" />
+<hkern u1="0" u2="." k="10" />
+<hkern u1="0" u2="7" k="5" />
+<hkern u1="0" u2="&#x2026;" k="10" />
+<hkern u1="0" u2="&#x201a;" k="10" />
+<hkern u1="0" u2="&#x201e;" k="10" />
+<hkern u1="2" u2="-" k="3" />
+<hkern u1="2" u2="4" k="8" />
+<hkern u1="2" u2="&#x2013;" k="3" />
+<hkern u1="2" u2="&#x2014;" k="3" />
+<hkern u1="3" u2="&#x2c;" k="5" />
+<hkern u1="3" u2="." k="5" />
+<hkern u1="3" u2="7" k="5" />
+<hkern u1="3" u2="&#x2026;" k="5" />
+<hkern u1="3" u2="&#x201a;" k="5" />
+<hkern u1="3" u2="&#x201e;" k="5" />
+<hkern u1="4" u2="7" k="3" />
+<hkern u1="4" u2="&#xb0;" k="10" />
+<hkern u1="4" u2="&#x2122;" k="13" />
+<hkern u1="5" u2="&#x2c;" k="3" />
+<hkern u1="5" u2="." k="3" />
+<hkern u1="5" u2="7" k="3" />
+<hkern u1="5" u2="&#x2026;" k="3" />
+<hkern u1="5" u2="&#x201a;" k="3" />
+<hkern u1="5" u2="&#x201e;" k="3" />
+<hkern u1="6" u2="&#x2c;" k="3" />
+<hkern u1="6" u2="." k="3" />
+<hkern u1="6" u2="7" k="3" />
+<hkern u1="6" u2="&#x2026;" k="3" />
+<hkern u1="6" u2="&#x201a;" k="3" />
+<hkern u1="6" u2="&#x201e;" k="3" />
+<hkern u1="7" u2="&#x2c;" k="20" />
+<hkern u1="7" u2="-" k="10" />
+<hkern u1="7" u2="." k="20" />
+<hkern u1="7" u2="0" k="3" />
+<hkern u1="7" u2="1" k="-5" />
+<hkern u1="7" u2="3" k="-5" />
+<hkern u1="7" u2="4" k="15" />
+<hkern u1="7" u2="5" k="-4" />
+<hkern u1="7" u2="9" k="-3" />
+<hkern u1="7" u2=":" k="5" />
+<hkern u1="7" u2=";" k="5" />
+<hkern u1="7" u2="&#xa2;" k="10" />
+<hkern u1="7" u2="&#x2026;" k="20" />
+<hkern u1="7" u2="&#x2013;" k="10" />
+<hkern u1="7" u2="&#x2014;" k="10" />
+<hkern u1="7" u2="&#x201a;" k="20" />
+<hkern u1="7" u2="&#x201e;" k="20" />
+<hkern u1="8" u2="&#x2c;" k="4" />
+<hkern u1="8" u2="." k="4" />
+<hkern u1="8" u2="7" k="4" />
+<hkern u1="8" u2="&#x2026;" k="4" />
+<hkern u1="8" u2="&#x201a;" k="4" />
+<hkern u1="8" u2="&#x201e;" k="4" />
+<hkern u1="9" u2="&#x2c;" k="8" />
+<hkern u1="9" u2="." k="8" />
+<hkern u1="9" u2="7" k="11" />
+<hkern u1="9" u2="&#x2026;" k="8" />
+<hkern u1="9" u2="&#x201a;" k="8" />
+<hkern u1="9" u2="&#x201e;" k="8" />
+<hkern u1=":" u2="&#xdd;" k="8" />
+<hkern u1=":" u2="1" k="8" />
+<hkern u1=":" u2="7" k="5" />
+<hkern u1=":" u2="T" k="13" />
+<hkern u1=":" u2="V" k="5" />
+<hkern u1=":" u2="W" k="5" />
+<hkern u1=":" u2="Y" k="8" />
+<hkern u1=":" u2="a" k="3" />
+<hkern u1=":" u2="c" k="3" />
+<hkern u1=":" u2="d" k="3" />
+<hkern u1=":" u2="e" k="3" />
+<hkern u1=":" u2="o" k="3" />
+<hkern u1=":" u2="q" k="3" />
+<hkern u1=":" u2="&#xe7;" k="3" />
+<hkern u1=":" u2="&#xe6;" k="3" />
+<hkern u1=":" u2="&#x153;" k="3" />
+<hkern u1=":" u2="&#x178;" k="8" />
+<hkern u1=";" u2="&#xdd;" k="8" />
+<hkern u1=";" u2="1" k="8" />
+<hkern u1=";" u2="7" k="5" />
+<hkern u1=";" u2="T" k="13" />
+<hkern u1=";" u2="V" k="5" />
+<hkern u1=";" u2="W" k="5" />
+<hkern u1=";" u2="Y" k="8" />
+<hkern u1=";" u2="a" k="3" />
+<hkern u1=";" u2="c" k="3" />
+<hkern u1=";" u2="d" k="3" />
+<hkern u1=";" u2="e" k="3" />
+<hkern u1=";" u2="o" k="3" />
+<hkern u1=";" u2="q" k="3" />
+<hkern u1=";" u2="&#xe7;" k="3" />
+<hkern u1=";" u2="&#xe6;" k="3" />
+<hkern u1=";" u2="&#x153;" k="3" />
+<hkern u1=";" u2="&#x178;" k="8" />
+<hkern u1="&gt;" u2="7" k="15" />
+<hkern u1="@" u2="&#xdd;" k="8" />
+<hkern u1="@" u2="A" k="3" />
+<hkern u1="@" u2="T" k="8" />
+<hkern u1="@" u2="W" k="5" />
+<hkern u1="@" u2="Y" k="8" />
+<hkern u1="@" u2="&#xc4;" k="3" />
+<hkern u1="@" u2="&#xc5;" k="3" />
+<hkern u1="@" u2="&#xc6;" k="3" />
+<hkern u1="@" u2="&#xc0;" k="3" />
+<hkern u1="@" u2="&#xc3;" k="3" />
+<hkern u1="@" u2="&#x178;" k="8" />
+<hkern u1="@" u2="&#xc2;" k="3" />
+<hkern u1="@" u2="&#xc1;" k="3" />
+<hkern u1="A" u2="&#x160;" k="-1" />
+<hkern u1="A" u2="&#xdd;" k="18" />
+<hkern u1="A" u2="*" k="15" />
+<hkern u1="A" u2="&#x2c;" k="-8" />
+<hkern u1="A" u2="-" k="-3" />
+<hkern u1="A" u2="." k="-8" />
+<hkern u1="A" u2="@" k="3" />
+<hkern u1="A" u2="A" k="-3" />
+<hkern u1="A" u2="C" k="50" />
+<hkern u1="A" u2="G" k="40" />
+<hkern u1="A" u2="J" k="3" />
+<hkern u1="A" u2="O" k="40" />
+<hkern u1="A" u2="Q" k="40" />
+<hkern u1="A" u2="S" k="-1" />
+<hkern u1="A" u2="T" k="83" />
+<hkern u1="A" u2="U" k="20" />
+<hkern u1="A" u2="V" k="88" />
+<hkern u1="A" u2="W" k="78" />
+<hkern u1="A" u2="Y" k="88" />
+<hkern u1="A" u2="a" k="1" />
+<hkern u1="A" u2="c" k="1" />
+<hkern u1="A" u2="d" k="1" />
+<hkern u1="A" u2="e" k="1" />
+<hkern u1="A" u2="m" k="1" />
+<hkern u1="A" u2="n" k="1" />
+<hkern u1="A" u2="o" k="1" />
+<hkern u1="A" u2="p" k="1" />
+<hkern u1="A" u2="q" k="1" />
+<hkern u1="A" u2="r" k="1" />
+<hkern u1="A" u2="t" k="1" />
+<hkern u1="A" u2="u" k="11" />
+<hkern u1="A" u2="v" k="48" />
+<hkern u1="A" u2="w" k="43" />
+<hkern u1="A" u2="x" k="-5" />
+<hkern u1="A" u2="y" k="18" />
+<hkern u1="A" u2="z" k="-3" />
+<hkern u1="A" u2="&#xc4;" k="-3" />
+<hkern u1="A" u2="&#xc5;" k="-3" />
+<hkern u1="A" u2="&#xd6;" k="10" />
+<hkern u1="A" u2="&#xe7;" k="1" />
+<hkern u1="A" u2="&#xae;" k="3" />
+<hkern u1="A" u2="&#xa9;" k="3" />
+<hkern u1="A" u2="&#x2122;" k="18" />
+<hkern u1="A" u2="&#xc6;" k="-3" />
+<hkern u1="A" u2="&#xd8;" k="10" />
+<hkern u1="A" u2="&#x3c0;" k="3" />
+<hkern u1="A" u2="&#xe6;" k="1" />
+<hkern u1="A" u2="&#x2026;" k="-8" />
+<hkern u1="A" u2="&#xc0;" k="-3" />
+<hkern u1="A" u2="&#xc3;" k="-3" />
+<hkern u1="A" u2="&#xd5;" k="10" />
+<hkern u1="A" u2="&#x152;" k="10" />
+<hkern u1="A" u2="&#x153;" k="1" />
+<hkern u1="A" u2="&#x2013;" k="-3" />
+<hkern u1="A" u2="&#x2014;" k="-3" />
+<hkern u1="A" u2="&#x201c;" k="15" />
+<hkern u1="A" u2="&#x201d;" k="8" />
+<hkern u1="A" u2="&#x2018;" k="15" />
+<hkern u1="A" u2="&#x2019;" k="8" />
+<hkern u1="A" u2="&#x178;" k="18" />
+<hkern u1="A" u2="&#x201a;" k="-8" />
+<hkern u1="A" u2="&#x201e;" k="-8" />
+<hkern u1="A" u2="&#xc2;" k="-3" />
+<hkern u1="A" u2="&#xc1;" k="-3" />
+<hkern u1="A" u2="&#xd3;" k="10" />
+<hkern u1="A" u2="&#xd4;" k="10" />
+<hkern u1="A" u2="&#xd2;" k="10" />
+<hkern u1="A" u2="&#x131;" k="1" />
+<hkern u1="B" u2="&#xdd;" k="5" />
+<hkern u1="B" u2="V" k="1" />
+<hkern u1="B" u2="W" k="5" />
+<hkern u1="B" u2="Y" k="5" />
+<hkern u1="B" u2="&#x178;" k="5" />
+<hkern u1="C" u2="&#x17d;" k="3" />
+<hkern u1="C" u2="T" k="5" />
+<hkern u1="C" u2="V" k="-5" />
+<hkern u1="C" u2="X" k="-3" />
+<hkern u1="C" u2="Z" k="3" />
+<hkern u1="C" u2="a" k="-1" />
+<hkern u1="C" u2="c" k="-1" />
+<hkern u1="C" u2="d" k="-1" />
+<hkern u1="C" u2="e" k="-1" />
+<hkern u1="C" u2="o" k="-1" />
+<hkern u1="C" u2="q" k="-1" />
+<hkern u1="C" u2="t" k="1" />
+<hkern u1="C" u2="v" k="1" />
+<hkern u1="C" u2="w" k="1" />
+<hkern u1="C" u2="y" k="1" />
+<hkern u1="C" u2="z" k="3" />
+<hkern u1="C" u2="&#xe7;" k="-1" />
+<hkern u1="C" u2="&#xe6;" k="-1" />
+<hkern u1="C" u2="&#x153;" k="-1" />
+<hkern u1="D" u2="&#xdd;" k="11" />
+<hkern u1="D" u2="&#x17d;" k="11" />
+<hkern u1="D" u2="&#x2c;" k="10" />
+<hkern u1="D" u2="." k="10" />
+<hkern u1="D" u2="/" k="13" />
+<hkern u1="D" u2="?" k="5" />
+<hkern u1="D" u2="J" k="11" />
+<hkern u1="D" u2="T" k="13" />
+<hkern u1="D" u2="V" k="10" />
+<hkern u1="D" u2="W" k="15" />
+<hkern u1="D" u2="X" k="9" />
+<hkern u1="D" u2="Y" k="11" />
+<hkern u1="D" u2="Z" k="11" />
+<hkern u1="D" u2="a" k="1" />
+<hkern u1="D" u2="b" k="3" />
+<hkern u1="D" u2="c" k="1" />
+<hkern u1="D" u2="d" k="1" />
+<hkern u1="D" u2="e" k="1" />
+<hkern u1="D" u2="h" k="3" />
+<hkern u1="D" u2="k" k="3" />
+<hkern u1="D" u2="l" k="3" />
+<hkern u1="D" u2="m" k="1" />
+<hkern u1="D" u2="n" k="1" />
+<hkern u1="D" u2="o" k="1" />
+<hkern u1="D" u2="p" k="1" />
+<hkern u1="D" u2="q" k="1" />
+<hkern u1="D" u2="r" k="1" />
+<hkern u1="D" u2="u" k="1" />
+<hkern u1="D" u2="x" k="3" />
+<hkern u1="D" u2="z" k="3" />
+<hkern u1="D" u2="&#xe7;" k="1" />
+<hkern u1="D" u2="&#xe6;" k="1" />
+<hkern u1="D" u2="&#x2026;" k="10" />
+<hkern u1="D" u2="&#x153;" k="1" />
+<hkern u1="D" u2="&#x201c;" k="5" />
+<hkern u1="D" u2="&#x2018;" k="5" />
+<hkern u1="D" u2="&#x178;" k="11" />
+<hkern u1="D" u2="&#x201a;" k="10" />
+<hkern u1="D" u2="&#x201e;" k="10" />
+<hkern u1="D" u2="&#x131;" k="1" />
+<hkern u1="E" u2="@" k="5" />
+<hkern u1="E" u2="T" k="-4" />
+<hkern u1="E" u2="a" k="6" />
+<hkern u1="E" u2="c" k="6" />
+<hkern u1="E" u2="d" k="6" />
+<hkern u1="E" u2="e" k="6" />
+<hkern u1="E" u2="f" k="3" />
+<hkern u1="E" u2="g" k="1" />
+<hkern u1="E" u2="o" k="6" />
+<hkern u1="E" u2="q" k="6" />
+<hkern u1="E" u2="v" k="5" />
+<hkern u1="E" u2="y" k="5" />
+<hkern u1="E" u2="&#xe7;" k="6" />
+<hkern u1="E" u2="&#xae;" k="5" />
+<hkern u1="E" u2="&#xa9;" k="5" />
+<hkern u1="E" u2="&#x3c0;" k="5" />
+<hkern u1="E" u2="&#xe6;" k="6" />
+<hkern u1="E" u2="&#x153;" k="6" />
+<hkern u1="F" u2="&#x161;" k="10" />
+<hkern u1="F" u2="&#xdd;" k="-6" />
+<hkern u1="F" u2="&amp;" k="10" />
+<hkern u1="F" u2="&#x2c;" k="23" />
+<hkern u1="F" u2="-" k="13" />
+<hkern u1="F" u2="." k="23" />
+<hkern u1="F" u2="/" k="20" />
+<hkern u1="F" u2=":" k="8" />
+<hkern u1="F" u2=";" k="8" />
+<hkern u1="F" u2="@" k="5" />
+<hkern u1="F" u2="A" k="20" />
+<hkern u1="F" u2="J" k="23" />
+<hkern u1="F" u2="T" k="-8" />
+<hkern u1="F" u2="V" k="-5" />
+<hkern u1="F" u2="W" k="-1" />
+<hkern u1="F" u2="X" k="-3" />
+<hkern u1="F" u2="Y" k="-6" />
+<hkern u1="F" u2="a" k="13" />
+<hkern u1="F" u2="b" k="3" />
+<hkern u1="F" u2="c" k="13" />
+<hkern u1="F" u2="d" k="13" />
+<hkern u1="F" u2="e" k="13" />
+<hkern u1="F" u2="f" k="5" />
+<hkern u1="F" u2="g" k="8" />
+<hkern u1="F" u2="h" k="3" />
+<hkern u1="F" u2="i" k="3" />
+<hkern u1="F" u2="j" k="3" />
+<hkern u1="F" u2="k" k="3" />
+<hkern u1="F" u2="l" k="3" />
+<hkern u1="F" u2="m" k="10" />
+<hkern u1="F" u2="n" k="10" />
+<hkern u1="F" u2="o" k="13" />
+<hkern u1="F" u2="p" k="10" />
+<hkern u1="F" u2="q" k="13" />
+<hkern u1="F" u2="r" k="10" />
+<hkern u1="F" u2="s" k="10" />
+<hkern u1="F" u2="t" k="5" />
+<hkern u1="F" u2="u" k="10" />
+<hkern u1="F" u2="v" k="5" />
+<hkern u1="F" u2="w" k="5" />
+<hkern u1="F" u2="x" k="10" />
+<hkern u1="F" u2="y" k="5" />
+<hkern u1="F" u2="z" k="10" />
+<hkern u1="F" u2="&#xc4;" k="10" />
+<hkern u1="F" u2="&#xc5;" k="10" />
+<hkern u1="F" u2="&#xe7;" k="13" />
+<hkern u1="F" u2="&#xae;" k="5" />
+<hkern u1="F" u2="&#xa9;" k="5" />
+<hkern u1="F" u2="&#xc6;" k="10" />
+<hkern u1="F" u2="&#x3c0;" k="5" />
+<hkern u1="F" u2="&#xe6;" k="13" />
+<hkern u1="F" u2="&#x2026;" k="23" />
+<hkern u1="F" u2="&#xc0;" k="10" />
+<hkern u1="F" u2="&#xc3;" k="10" />
+<hkern u1="F" u2="&#x153;" k="13" />
+<hkern u1="F" u2="&#x2013;" k="13" />
+<hkern u1="F" u2="&#x2014;" k="13" />
+<hkern u1="F" u2="&#x178;" k="-6" />
+<hkern u1="F" u2="&#x201a;" k="23" />
+<hkern u1="F" u2="&#x201e;" k="23" />
+<hkern u1="F" u2="&#xc2;" k="10" />
+<hkern u1="F" u2="&#xc1;" k="10" />
+<hkern u1="F" u2="&#x131;" k="10" />
+<hkern u1="G" u2="&#xdd;" k="4" />
+<hkern u1="G" u2="&#x2c;" k="-5" />
+<hkern u1="G" u2="." k="-5" />
+<hkern u1="G" u2="T" k="6" />
+<hkern u1="G" u2="V" k="3" />
+<hkern u1="G" u2="W" k="4" />
+<hkern u1="G" u2="Y" k="4" />
+<hkern u1="G" u2="t" k="3" />
+<hkern u1="G" u2="v" k="3" />
+<hkern u1="G" u2="y" k="3" />
+<hkern u1="G" u2="&#x2122;" k="8" />
+<hkern u1="G" u2="&#x2026;" k="-5" />
+<hkern u1="G" u2="&#x178;" k="4" />
+<hkern u1="G" u2="&#x201a;" k="-5" />
+<hkern u1="G" u2="&#x201e;" k="-5" />
+<hkern u1="H" u2="/" k="5" />
+<hkern u1="H" u2="v" k="-5" />
+<hkern u1="H" u2="y" k="-5" />
+<hkern u1="I" u2="/" k="5" />
+<hkern u1="I" u2="v" k="-5" />
+<hkern u1="I" u2="y" k="-5" />
+<hkern u1="J" u2="&#x2c;" k="3" />
+<hkern u1="J" u2="." k="3" />
+<hkern u1="J" u2="A" k="-5" />
+<hkern u1="J" u2="J" k="3" />
+<hkern u1="J" u2="v" k="-5" />
+<hkern u1="J" u2="y" k="-5" />
+<hkern u1="J" u2="&#xc4;" k="-5" />
+<hkern u1="J" u2="&#xc5;" k="-5" />
+<hkern u1="J" u2="&#xc6;" k="-5" />
+<hkern u1="J" u2="&#x2026;" k="3" />
+<hkern u1="J" u2="&#xc0;" k="-5" />
+<hkern u1="J" u2="&#xc3;" k="-5" />
+<hkern u1="J" u2="&#x201a;" k="3" />
+<hkern u1="J" u2="&#x201e;" k="3" />
+<hkern u1="J" u2="&#xc2;" k="-5" />
+<hkern u1="J" u2="&#xc1;" k="-5" />
+<hkern u1="K" u2="&#xf0;" k="5" />
+<hkern u1="K" u2="&#x160;" k="1" />
+<hkern u1="K" u2="&#xdd;" k="-4" />
+<hkern u1="K" u2="&#x17d;" k="1" />
+<hkern u1="K" u2="&amp;" k="5" />
+<hkern u1="K" u2="-" k="10" />
+<hkern u1="K" u2="@" k="8" />
+<hkern u1="K" u2="C" k="8" />
+<hkern u1="K" u2="G" k="8" />
+<hkern u1="K" u2="O" k="8" />
+<hkern u1="K" u2="Q" k="8" />
+<hkern u1="K" u2="S" k="1" />
+<hkern u1="K" u2="T" k="-1" />
+<hkern u1="K" u2="U" k="3" />
+<hkern u1="K" u2="V" k="-4" />
+<hkern u1="K" u2="W" k="3" />
+<hkern u1="K" u2="X" k="-3" />
+<hkern u1="K" u2="Y" k="-4" />
+<hkern u1="K" u2="Z" k="1" />
+<hkern u1="K" u2="a" k="5" />
+<hkern u1="K" u2="c" k="5" />
+<hkern u1="K" u2="d" k="5" />
+<hkern u1="K" u2="e" k="5" />
+<hkern u1="K" u2="f" k="1" />
+<hkern u1="K" u2="g" k="3" />
+<hkern u1="K" u2="o" k="5" />
+<hkern u1="K" u2="q" k="5" />
+<hkern u1="K" u2="t" k="3" />
+<hkern u1="K" u2="v" k="5" />
+<hkern u1="K" u2="y" k="5" />
+<hkern u1="K" u2="&#xd6;" k="8" />
+<hkern u1="K" u2="&#xdc;" k="3" />
+<hkern u1="K" u2="&#xe7;" k="5" />
+<hkern u1="K" u2="&#xae;" k="8" />
+<hkern u1="K" u2="&#xa9;" k="8" />
+<hkern u1="K" u2="&#xd8;" k="8" />
+<hkern u1="K" u2="&#x3c0;" k="8" />
+<hkern u1="K" u2="&#xe6;" k="5" />
+<hkern u1="K" u2="&#xd5;" k="8" />
+<hkern u1="K" u2="&#x152;" k="8" />
+<hkern u1="K" u2="&#x153;" k="5" />
+<hkern u1="K" u2="&#x2013;" k="10" />
+<hkern u1="K" u2="&#x2014;" k="10" />
+<hkern u1="K" u2="&#x178;" k="-4" />
+<hkern u1="K" u2="&#xd3;" k="8" />
+<hkern u1="K" u2="&#xd4;" k="8" />
+<hkern u1="K" u2="&#xd2;" k="8" />
+<hkern u1="K" u2="&#xda;" k="3" />
+<hkern u1="K" u2="&#xdb;" k="3" />
+<hkern u1="K" u2="&#xd9;" k="3" />
+<hkern u1="L" u2="&#xdd;" k="20" />
+<hkern u1="L" u2="@" k="13" />
+<hkern u1="L" u2="A" k="-5" />
+<hkern u1="L" u2="C" k="13" />
+<hkern u1="L" u2="G" k="13" />
+<hkern u1="L" u2="O" k="13" />
+<hkern u1="L" u2="Q" k="13" />
+<hkern u1="L" u2="T" k="130" />
+<hkern u1="L" u2="U" k="4" />
+<hkern u1="L" u2="V" k="23" />
+<hkern u1="L" u2="W" k="98" />
+<hkern u1="L" u2="Y" k="140" />
+<hkern u1="L" u2="\" k="13" />
+<hkern u1="L" u2="a" k="5" />
+<hkern u1="L" u2="c" k="5" />
+<hkern u1="L" u2="d" k="5" />
+<hkern u1="L" u2="e" k="5" />
+<hkern u1="L" u2="o" k="5" />
+<hkern u1="L" u2="q" k="5" />
+<hkern u1="L" u2="v" k="10" />
+<hkern u1="L" u2="w" k="3" />
+<hkern u1="L" u2="y" k="10" />
+<hkern u1="L" u2="&#xc4;" k="-5" />
+<hkern u1="L" u2="&#xc5;" k="-5" />
+<hkern u1="L" u2="&#xd6;" k="13" />
+<hkern u1="L" u2="&#xdc;" k="4" />
+<hkern u1="L" u2="&#xe7;" k="5" />
+<hkern u1="L" u2="&#xae;" k="13" />
+<hkern u1="L" u2="&#xa9;" k="13" />
+<hkern u1="L" u2="&#x2122;" k="18" />
+<hkern u1="L" u2="&#xc6;" k="-5" />
+<hkern u1="L" u2="&#xd8;" k="13" />
+<hkern u1="L" u2="&#x3c0;" k="13" />
+<hkern u1="L" u2="&#xe6;" k="5" />
+<hkern u1="L" u2="&#xc0;" k="-5" />
+<hkern u1="L" u2="&#xc3;" k="-5" />
+<hkern u1="L" u2="&#xd5;" k="13" />
+<hkern u1="L" u2="&#x152;" k="13" />
+<hkern u1="L" u2="&#x153;" k="5" />
+<hkern u1="L" u2="&#x201c;" k="25" />
+<hkern u1="L" u2="&#x201d;" k="15" />
+<hkern u1="L" u2="&#x2018;" k="25" />
+<hkern u1="L" u2="&#x2019;" k="15" />
+<hkern u1="L" u2="&#x178;" k="20" />
+<hkern u1="L" u2="&#xc2;" k="-5" />
+<hkern u1="L" u2="&#xc1;" k="-5" />
+<hkern u1="L" u2="&#xd3;" k="13" />
+<hkern u1="L" u2="&#xd4;" k="13" />
+<hkern u1="L" u2="&#xd2;" k="13" />
+<hkern u1="L" u2="&#xda;" k="4" />
+<hkern u1="L" u2="&#xdb;" k="4" />
+<hkern u1="L" u2="&#xd9;" k="4" />
+<hkern u1="M" u2="/" k="5" />
+<hkern u1="M" u2="v" k="-5" />
+<hkern u1="M" u2="y" k="-5" />
+<hkern u1="N" u2="/" k="5" />
+<hkern u1="N" u2="v" k="-5" />
+<hkern u1="N" u2="y" k="-5" />
+<hkern u1="O" u2="&#xdd;" k="11" />
+<hkern u1="O" u2="&#x17d;" k="11" />
+<hkern u1="O" u2="&#x2c;" k="10" />
+<hkern u1="O" u2="." k="10" />
+<hkern u1="O" u2="/" k="13" />
+<hkern u1="O" u2="?" k="5" />
+<hkern u1="O" u2="J" k="11" />
+<hkern u1="O" u2="T" k="13" />
+<hkern u1="O" u2="V" k="10" />
+<hkern u1="O" u2="W" k="15" />
+<hkern u1="O" u2="X" k="9" />
+<hkern u1="O" u2="Y" k="11" />
+<hkern u1="O" u2="Z" k="11" />
+<hkern u1="O" u2="a" k="1" />
+<hkern u1="O" u2="b" k="3" />
+<hkern u1="O" u2="c" k="1" />
+<hkern u1="O" u2="d" k="1" />
+<hkern u1="O" u2="e" k="1" />
+<hkern u1="O" u2="h" k="3" />
+<hkern u1="O" u2="k" k="3" />
+<hkern u1="O" u2="l" k="3" />
+<hkern u1="O" u2="m" k="1" />
+<hkern u1="O" u2="n" k="1" />
+<hkern u1="O" u2="o" k="1" />
+<hkern u1="O" u2="p" k="1" />
+<hkern u1="O" u2="q" k="1" />
+<hkern u1="O" u2="r" k="1" />
+<hkern u1="O" u2="u" k="1" />
+<hkern u1="O" u2="x" k="3" />
+<hkern u1="O" u2="z" k="3" />
+<hkern u1="O" u2="&#xe7;" k="1" />
+<hkern u1="O" u2="&#xe6;" k="1" />
+<hkern u1="O" u2="&#x2026;" k="10" />
+<hkern u1="O" u2="&#x153;" k="1" />
+<hkern u1="O" u2="&#x201c;" k="5" />
+<hkern u1="O" u2="&#x2018;" k="5" />
+<hkern u1="O" u2="&#x178;" k="11" />
+<hkern u1="O" u2="&#x201a;" k="10" />
+<hkern u1="O" u2="&#x201e;" k="10" />
+<hkern u1="O" u2="&#x131;" k="1" />
+<hkern u1="P" u2="&#xdd;" k="1" />
+<hkern u1="P" u2="&#x17d;" k="6" />
+<hkern u1="P" u2="&amp;" k="5" />
+<hkern u1="P" u2="&#x2c;" k="15" />
+<hkern u1="P" u2="." k="15" />
+<hkern u1="P" u2="A" k="9" />
+<hkern u1="P" u2="J" k="21" />
+<hkern u1="P" u2="V" k="-1" />
+<hkern u1="P" u2="W" k="4" />
+<hkern u1="P" u2="X" k="-1" />
+<hkern u1="P" u2="Y" k="1" />
+<hkern u1="P" u2="Z" k="6" />
+<hkern u1="P" u2="a" k="3" />
+<hkern u1="P" u2="c" k="3" />
+<hkern u1="P" u2="d" k="3" />
+<hkern u1="P" u2="e" k="3" />
+<hkern u1="P" u2="f" k="-3" />
+<hkern u1="P" u2="o" k="3" />
+<hkern u1="P" u2="q" k="3" />
+<hkern u1="P" u2="t" k="-3" />
+<hkern u1="P" u2="v" k="-4" />
+<hkern u1="P" u2="w" k="-3" />
+<hkern u1="P" u2="x" k="-1" />
+<hkern u1="P" u2="y" k="-4" />
+<hkern u1="P" u2="&#xc4;" k="9" />
+<hkern u1="P" u2="&#xc5;" k="9" />
+<hkern u1="P" u2="&#xe7;" k="3" />
+<hkern u1="P" u2="&#xc6;" k="9" />
+<hkern u1="P" u2="&#xe6;" k="3" />
+<hkern u1="P" u2="&#x2026;" k="15" />
+<hkern u1="P" u2="&#xc0;" k="9" />
+<hkern u1="P" u2="&#xc3;" k="9" />
+<hkern u1="P" u2="&#x153;" k="3" />
+<hkern u1="P" u2="&#x178;" k="1" />
+<hkern u1="P" u2="&#x201a;" k="15" />
+<hkern u1="P" u2="&#x201e;" k="15" />
+<hkern u1="P" u2="&#xc2;" k="9" />
+<hkern u1="P" u2="&#xc1;" k="9" />
+<hkern u1="Q" u2="&#xdd;" k="11" />
+<hkern u1="Q" u2="&#x17d;" k="11" />
+<hkern u1="Q" u2="&#x2c;" k="10" />
+<hkern u1="Q" u2="." k="10" />
+<hkern u1="Q" u2="/" k="8" />
+<hkern u1="Q" u2="?" k="5" />
+<hkern u1="Q" u2="J" k="11" />
+<hkern u1="Q" u2="T" k="13" />
+<hkern u1="Q" u2="V" k="10" />
+<hkern u1="Q" u2="W" k="15" />
+<hkern u1="Q" u2="X" k="9" />
+<hkern u1="Q" u2="Y" k="11" />
+<hkern u1="Q" u2="Z" k="11" />
+<hkern u1="Q" u2="a" k="1" />
+<hkern u1="Q" u2="b" k="3" />
+<hkern u1="Q" u2="c" k="1" />
+<hkern u1="Q" u2="d" k="1" />
+<hkern u1="Q" u2="e" k="1" />
+<hkern u1="Q" u2="h" k="3" />
+<hkern u1="Q" u2="k" k="3" />
+<hkern u1="Q" u2="l" k="3" />
+<hkern u1="Q" u2="m" k="1" />
+<hkern u1="Q" u2="n" k="1" />
+<hkern u1="Q" u2="o" k="1" />
+<hkern u1="Q" u2="p" k="1" />
+<hkern u1="Q" u2="q" k="1" />
+<hkern u1="Q" u2="r" k="1" />
+<hkern u1="Q" u2="u" k="1" />
+<hkern u1="Q" u2="x" k="3" />
+<hkern u1="Q" u2="z" k="3" />
+<hkern u1="Q" u2="&#xe7;" k="1" />
+<hkern u1="Q" u2="&#xe6;" k="1" />
+<hkern u1="Q" u2="&#x2026;" k="10" />
+<hkern u1="Q" u2="&#x153;" k="1" />
+<hkern u1="Q" u2="&#x201c;" k="5" />
+<hkern u1="Q" u2="&#x2018;" k="5" />
+<hkern u1="Q" u2="&#x178;" k="11" />
+<hkern u1="Q" u2="&#x201a;" k="10" />
+<hkern u1="Q" u2="&#x201e;" k="10" />
+<hkern u1="Q" u2="&#x131;" k="1" />
+<hkern u1="R" u2="&#xdd;" k="-4" />
+<hkern u1="R" u2="&amp;" k="-3" />
+<hkern u1="R" u2="A" k="-5" />
+<hkern u1="R" u2="J" k="3" />
+<hkern u1="R" u2="T" k="3" />
+<hkern u1="R" u2="V" k="-5" />
+<hkern u1="R" u2="X" k="-3" />
+<hkern u1="R" u2="Y" k="-4" />
+<hkern u1="R" u2="v" k="-8" />
+<hkern u1="R" u2="w" k="-3" />
+<hkern u1="R" u2="y" k="-8" />
+<hkern u1="R" u2="&#xc4;" k="-5" />
+<hkern u1="R" u2="&#xc5;" k="-5" />
+<hkern u1="R" u2="&#xc6;" k="-5" />
+<hkern u1="R" u2="&#xc0;" k="-5" />
+<hkern u1="R" u2="&#xc3;" k="-5" />
+<hkern u1="R" u2="&#x178;" k="-4" />
+<hkern u1="R" u2="&#xc2;" k="-5" />
+<hkern u1="R" u2="&#xc1;" k="-5" />
+<hkern u1="S" u2="&#xdd;" k="3" />
+<hkern u1="S" u2="A" k="-1" />
+<hkern u1="S" u2="V" k="3" />
+<hkern u1="S" u2="W" k="5" />
+<hkern u1="S" u2="X" k="-1" />
+<hkern u1="S" u2="Y" k="3" />
+<hkern u1="S" u2="&#xc4;" k="-1" />
+<hkern u1="S" u2="&#xc5;" k="-1" />
+<hkern u1="S" u2="&#xc6;" k="-1" />
+<hkern u1="S" u2="&#xc0;" k="-1" />
+<hkern u1="S" u2="&#xc3;" k="-1" />
+<hkern u1="S" u2="&#x178;" k="3" />
+<hkern u1="S" u2="&#xc2;" k="-1" />
+<hkern u1="S" u2="&#xc1;" k="-1" />
+<hkern u1="T" u2="&#x161;" k="25" />
+<hkern u1="T" u2="&#xdd;" k="-5" />
+<hkern u1="T" u2="&#x17d;" k="3" />
+<hkern u1="T" u2="&amp;" k="10" />
+<hkern u1="T" u2="&#x2c;" k="15" />
+<hkern u1="T" u2="-" k="18" />
+<hkern u1="T" u2="." k="15" />
+<hkern u1="T" u2=":" k="13" />
+<hkern u1="T" u2=";" k="13" />
+<hkern u1="T" u2="@" k="8" />
+<hkern u1="T" u2="A" k="43" />
+<hkern u1="T" u2="C" k="3" />
+<hkern u1="T" u2="G" k="3" />
+<hkern u1="T" u2="J" k="101" />
+<hkern u1="T" u2="O" k="3" />
+<hkern u1="T" u2="Q" k="3" />
+<hkern u1="T" u2="T" k="-3" />
+<hkern u1="T" u2="V" k="-5" />
+<hkern u1="T" u2="X" k="-3" />
+<hkern u1="T" u2="Y" k="-5" />
+<hkern u1="T" u2="Z" k="3" />
+<hkern u1="T" u2="\" k="-3" />
+<hkern u1="T" u2="a" k="24" />
+<hkern u1="T" u2="c" k="24" />
+<hkern u1="T" u2="d" k="24" />
+<hkern u1="T" u2="e" k="24" />
+<hkern u1="T" u2="f" k="3" />
+<hkern u1="T" u2="g" k="24" />
+<hkern u1="T" u2="m" k="20" />
+<hkern u1="T" u2="n" k="20" />
+<hkern u1="T" u2="o" k="24" />
+<hkern u1="T" u2="p" k="20" />
+<hkern u1="T" u2="q" k="24" />
+<hkern u1="T" u2="r" k="20" />
+<hkern u1="T" u2="s" k="25" />
+<hkern u1="T" u2="t" k="5" />
+<hkern u1="T" u2="u" k="20" />
+<hkern u1="T" u2="v" k="18" />
+<hkern u1="T" u2="w" k="15" />
+<hkern u1="T" u2="x" k="20" />
+<hkern u1="T" u2="y" k="18" />
+<hkern u1="T" u2="z" k="20" />
+<hkern u1="T" u2="&#xc4;" k="13" />
+<hkern u1="T" u2="&#xc5;" k="13" />
+<hkern u1="T" u2="&#xd6;" k="3" />
+<hkern u1="T" u2="&#xe7;" k="24" />
+<hkern u1="T" u2="&#xae;" k="8" />
+<hkern u1="T" u2="&#xa9;" k="8" />
+<hkern u1="T" u2="&#xc6;" k="63" />
+<hkern u1="T" u2="&#xd8;" k="3" />
+<hkern u1="T" u2="&#x3c0;" k="8" />
+<hkern u1="T" u2="&#xe6;" k="24" />
+<hkern u1="T" u2="&#xbf;" k="13" />
+<hkern u1="T" u2="&#xab;" k="28" />
+<hkern u1="T" u2="&#xbb;" k="10" />
+<hkern u1="T" u2="&#x2026;" k="15" />
+<hkern u1="T" u2="&#xc0;" k="13" />
+<hkern u1="T" u2="&#xc3;" k="13" />
+<hkern u1="T" u2="&#xd5;" k="3" />
+<hkern u1="T" u2="&#x152;" k="3" />
+<hkern u1="T" u2="&#x153;" k="24" />
+<hkern u1="T" u2="&#x2013;" k="18" />
+<hkern u1="T" u2="&#x2014;" k="18" />
+<hkern u1="T" u2="&#x178;" k="-5" />
+<hkern u1="T" u2="&#x2039;" k="28" />
+<hkern u1="T" u2="&#x203a;" k="10" />
+<hkern u1="T" u2="&#x201a;" k="15" />
+<hkern u1="T" u2="&#x201e;" k="15" />
+<hkern u1="T" u2="&#xc2;" k="13" />
+<hkern u1="T" u2="&#xc1;" k="13" />
+<hkern u1="T" u2="&#xd3;" k="3" />
+<hkern u1="T" u2="&#xd4;" k="3" />
+<hkern u1="T" u2="&#xd2;" k="3" />
+<hkern u1="T" u2="&#x131;" k="20" />
+<hkern u1="U" u2="&#x2c;" k="3" />
+<hkern u1="U" u2="." k="3" />
+<hkern u1="U" u2="A" k="-5" />
+<hkern u1="U" u2="J" k="3" />
+<hkern u1="U" u2="v" k="-5" />
+<hkern u1="U" u2="y" k="-5" />
+<hkern u1="U" u2="&#xc4;" k="-5" />
+<hkern u1="U" u2="&#xc5;" k="-5" />
+<hkern u1="U" u2="&#xc6;" k="-5" />
+<hkern u1="U" u2="&#x2026;" k="3" />
+<hkern u1="U" u2="&#xc0;" k="-5" />
+<hkern u1="U" u2="&#xc3;" k="-5" />
+<hkern u1="U" u2="&#x201a;" k="3" />
+<hkern u1="U" u2="&#x201e;" k="3" />
+<hkern u1="U" u2="&#xc2;" k="-5" />
+<hkern u1="U" u2="&#xc1;" k="-5" />
+<hkern u1="V" u2="&#x160;" k="-8" />
+<hkern u1="V" u2="&#x161;" k="10" />
+<hkern u1="V" u2="&#xdd;" k="-8" />
+<hkern u1="V" u2="&amp;" k="10" />
+<hkern u1="V" u2=")" k="-8" />
+<hkern u1="V" u2="&#x2c;" k="15" />
+<hkern u1="V" u2="-" k="15" />
+<hkern u1="V" u2="." k="15" />
+<hkern u1="V" u2=":" k="5" />
+<hkern u1="V" u2=";" k="5" />
+<hkern u1="V" u2="A" k="38" />
+<hkern u1="V" u2="J" k="73" />
+<hkern u1="V" u2="S" k="-8" />
+<hkern u1="V" u2="T" k="-5" />
+<hkern u1="V" u2="V" k="-5" />
+<hkern u1="V" u2="W" k="1" />
+<hkern u1="V" u2="X" k="-5" />
+<hkern u1="V" u2="Y" k="-8" />
+<hkern u1="V" u2="]" k="-8" />
+<hkern u1="V" u2="a" k="10" />
+<hkern u1="V" u2="b" k="3" />
+<hkern u1="V" u2="c" k="10" />
+<hkern u1="V" u2="d" k="10" />
+<hkern u1="V" u2="e" k="10" />
+<hkern u1="V" u2="f" k="3" />
+<hkern u1="V" u2="g" k="9" />
+<hkern u1="V" u2="h" k="3" />
+<hkern u1="V" u2="k" k="3" />
+<hkern u1="V" u2="l" k="3" />
+<hkern u1="V" u2="m" k="8" />
+<hkern u1="V" u2="n" k="8" />
+<hkern u1="V" u2="o" k="10" />
+<hkern u1="V" u2="p" k="8" />
+<hkern u1="V" u2="q" k="10" />
+<hkern u1="V" u2="r" k="8" />
+<hkern u1="V" u2="s" k="10" />
+<hkern u1="V" u2="t" k="3" />
+<hkern u1="V" u2="u" k="8" />
+<hkern u1="V" u2="v" k="3" />
+<hkern u1="V" u2="w" k="3" />
+<hkern u1="V" u2="x" k="6" />
+<hkern u1="V" u2="y" k="3" />
+<hkern u1="V" u2="}" k="-8" />
+<hkern u1="V" u2="&#xc4;" k="8" />
+<hkern u1="V" u2="&#xc5;" k="8" />
+<hkern u1="V" u2="&#xe7;" k="10" />
+<hkern u1="V" u2="&#xc6;" k="58" />
+<hkern u1="V" u2="&#xe6;" k="10" />
+<hkern u1="V" u2="&#xab;" k="13" />
+<hkern u1="V" u2="&#xbb;" k="5" />
+<hkern u1="V" u2="&#x2026;" k="15" />
+<hkern u1="V" u2="&#xc0;" k="8" />
+<hkern u1="V" u2="&#xc3;" k="8" />
+<hkern u1="V" u2="&#x153;" k="10" />
+<hkern u1="V" u2="&#x2013;" k="15" />
+<hkern u1="V" u2="&#x2014;" k="15" />
+<hkern u1="V" u2="&#x178;" k="-8" />
+<hkern u1="V" u2="&#x2039;" k="13" />
+<hkern u1="V" u2="&#x203a;" k="5" />
+<hkern u1="V" u2="&#x201a;" k="15" />
+<hkern u1="V" u2="&#x201e;" k="15" />
+<hkern u1="V" u2="&#xc2;" k="8" />
+<hkern u1="V" u2="&#xc1;" k="8" />
+<hkern u1="V" u2="&#x131;" k="8" />
+<hkern u1="W" u2="&#x160;" k="-5" />
+<hkern u1="W" u2="&#x161;" k="13" />
+<hkern u1="W" u2="&amp;" k="8" />
+<hkern u1="W" u2=")" k="-3" />
+<hkern u1="W" u2="&#x2c;" k="5" />
+<hkern u1="W" u2="-" k="10" />
+<hkern u1="W" u2="." k="5" />
+<hkern u1="W" u2=":" k="5" />
+<hkern u1="W" u2=";" k="5" />
+<hkern u1="W" u2="@" k="5" />
+<hkern u1="W" u2="A" k="38" />
+<hkern u1="W" u2="C" k="5" />
+<hkern u1="W" u2="G" k="5" />
+<hkern u1="W" u2="J" k="100" />
+<hkern u1="W" u2="O" k="5" />
+<hkern u1="W" u2="Q" k="5" />
+<hkern u1="W" u2="S" k="-5" />
+<hkern u1="W" u2="V" k="1" />
+<hkern u1="W" u2="X" k="3" />
+<hkern u1="W" u2="]" k="-3" />
+<hkern u1="W" u2="a" k="11" />
+<hkern u1="W" u2="b" k="3" />
+<hkern u1="W" u2="c" k="11" />
+<hkern u1="W" u2="d" k="11" />
+<hkern u1="W" u2="e" k="11" />
+<hkern u1="W" u2="f" k="4" />
+<hkern u1="W" u2="g" k="13" />
+<hkern u1="W" u2="h" k="3" />
+<hkern u1="W" u2="i" k="5" />
+<hkern u1="W" u2="j" k="5" />
+<hkern u1="W" u2="k" k="3" />
+<hkern u1="W" u2="l" k="3" />
+<hkern u1="W" u2="m" k="9" />
+<hkern u1="W" u2="n" k="9" />
+<hkern u1="W" u2="o" k="11" />
+<hkern u1="W" u2="p" k="9" />
+<hkern u1="W" u2="q" k="11" />
+<hkern u1="W" u2="r" k="9" />
+<hkern u1="W" u2="s" k="13" />
+<hkern u1="W" u2="t" k="8" />
+<hkern u1="W" u2="u" k="9" />
+<hkern u1="W" u2="v" k="8" />
+<hkern u1="W" u2="w" k="8" />
+<hkern u1="W" u2="x" k="8" />
+<hkern u1="W" u2="y" k="8" />
+<hkern u1="W" u2="z" k="8" />
+<hkern u1="W" u2="}" k="-3" />
+<hkern u1="W" u2="&#xc4;" k="8" />
+<hkern u1="W" u2="&#xc5;" k="8" />
+<hkern u1="W" u2="&#xd6;" k="5" />
+<hkern u1="W" u2="&#xe7;" k="11" />
+<hkern u1="W" u2="&#xae;" k="5" />
+<hkern u1="W" u2="&#xa9;" k="5" />
+<hkern u1="W" u2="&#xc6;" k="58" />
+<hkern u1="W" u2="&#xd8;" k="5" />
+<hkern u1="W" u2="&#x3c0;" k="5" />
+<hkern u1="W" u2="&#xe6;" k="11" />
+<hkern u1="W" u2="&#xab;" k="10" />
+<hkern u1="W" u2="&#xbb;" k="5" />
+<hkern u1="W" u2="&#x2026;" k="5" />
+<hkern u1="W" u2="&#xc0;" k="8" />
+<hkern u1="W" u2="&#xc3;" k="8" />
+<hkern u1="W" u2="&#xd5;" k="5" />
+<hkern u1="W" u2="&#x152;" k="5" />
+<hkern u1="W" u2="&#x153;" k="11" />
+<hkern u1="W" u2="&#x2013;" k="10" />
+<hkern u1="W" u2="&#x2014;" k="10" />
+<hkern u1="W" u2="&#x2039;" k="10" />
+<hkern u1="W" u2="&#x203a;" k="5" />
+<hkern u1="W" u2="&#x201a;" k="5" />
+<hkern u1="W" u2="&#x201e;" k="5" />
+<hkern u1="W" u2="&#xc2;" k="8" />
+<hkern u1="W" u2="&#xc1;" k="8" />
+<hkern u1="W" u2="&#xd3;" k="5" />
+<hkern u1="W" u2="&#xd4;" k="5" />
+<hkern u1="W" u2="&#xd2;" k="5" />
+<hkern u1="W" u2="&#x131;" k="9" />
+<hkern u1="X" u2="&#x160;" k="-1" />
+<hkern u1="X" u2="&#xdd;" k="-5" />
+<hkern u1="X" u2="&amp;" k="5" />
+<hkern u1="X" u2=")" k="-10" />
+<hkern u1="X" u2="-" k="8" />
+<hkern u1="X" u2="A" k="-5" />
+<hkern u1="X" u2="C" k="4" />
+<hkern u1="X" u2="G" k="4" />
+<hkern u1="X" u2="O" k="4" />
+<hkern u1="X" u2="Q" k="4" />
+<hkern u1="X" u2="S" k="-1" />
+<hkern u1="X" u2="T" k="-3" />
+<hkern u1="X" u2="V" k="-5" />
+<hkern u1="X" u2="W" k="3" />
+<hkern u1="X" u2="X" k="-3" />
+<hkern u1="X" u2="Y" k="-5" />
+<hkern u1="X" u2="]" k="-10" />
+<hkern u1="X" u2="a" k="3" />
+<hkern u1="X" u2="c" k="3" />
+<hkern u1="X" u2="d" k="3" />
+<hkern u1="X" u2="e" k="3" />
+<hkern u1="X" u2="f" k="3" />
+<hkern u1="X" u2="m" k="3" />
+<hkern u1="X" u2="n" k="3" />
+<hkern u1="X" u2="o" k="3" />
+<hkern u1="X" u2="p" k="3" />
+<hkern u1="X" u2="q" k="3" />
+<hkern u1="X" u2="r" k="3" />
+<hkern u1="X" u2="t" k="3" />
+<hkern u1="X" u2="u" k="3" />
+<hkern u1="X" u2="v" k="-3" />
+<hkern u1="X" u2="w" k="3" />
+<hkern u1="X" u2="y" k="-3" />
+<hkern u1="X" u2="}" k="-10" />
+<hkern u1="X" u2="&#xc4;" k="-5" />
+<hkern u1="X" u2="&#xc5;" k="-5" />
+<hkern u1="X" u2="&#xd6;" k="4" />
+<hkern u1="X" u2="&#xe7;" k="3" />
+<hkern u1="X" u2="&#xc6;" k="-5" />
+<hkern u1="X" u2="&#xd8;" k="4" />
+<hkern u1="X" u2="&#xe6;" k="3" />
+<hkern u1="X" u2="&#xc0;" k="-5" />
+<hkern u1="X" u2="&#xc3;" k="-5" />
+<hkern u1="X" u2="&#xd5;" k="4" />
+<hkern u1="X" u2="&#x152;" k="4" />
+<hkern u1="X" u2="&#x153;" k="3" />
+<hkern u1="X" u2="&#x2013;" k="8" />
+<hkern u1="X" u2="&#x2014;" k="8" />
+<hkern u1="X" u2="&#x178;" k="-5" />
+<hkern u1="X" u2="&#xc2;" k="-5" />
+<hkern u1="X" u2="&#xc1;" k="-5" />
+<hkern u1="X" u2="&#xd3;" k="4" />
+<hkern u1="X" u2="&#xd4;" k="4" />
+<hkern u1="X" u2="&#xd2;" k="4" />
+<hkern u1="X" u2="&#x131;" k="3" />
+<hkern u1="Y" u2="&#x160;" k="-8" />
+<hkern u1="Y" u2="&#x161;" k="13" />
+<hkern u1="Y" u2="&amp;" k="8" />
+<hkern u1="Y" u2=")" k="-13" />
+<hkern u1="Y" u2="&#x2c;" k="13" />
+<hkern u1="Y" u2="-" k="13" />
+<hkern u1="Y" u2="." k="13" />
+<hkern u1="Y" u2=":" k="8" />
+<hkern u1="Y" u2=";" k="8" />
+<hkern u1="Y" u2="@" k="8" />
+<hkern u1="Y" u2="A" k="38" />
+<hkern u1="Y" u2="C" k="1" />
+<hkern u1="Y" u2="G" k="1" />
+<hkern u1="Y" u2="J" k="83" />
+<hkern u1="Y" u2="O" k="1" />
+<hkern u1="Y" u2="Q" k="1" />
+<hkern u1="Y" u2="S" k="-8" />
+<hkern u1="Y" u2="T" k="-5" />
+<hkern u1="Y" u2="V" k="-8" />
+<hkern u1="Y" u2="X" k="-5" />
+<hkern u1="Y" u2="]" k="-13" />
+<hkern u1="Y" u2="a" k="14" />
+<hkern u1="Y" u2="c" k="14" />
+<hkern u1="Y" u2="d" k="14" />
+<hkern u1="Y" u2="e" k="14" />
+<hkern u1="Y" u2="f" k="3" />
+<hkern u1="Y" u2="g" k="10" />
+<hkern u1="Y" u2="m" k="10" />
+<hkern u1="Y" u2="n" k="10" />
+<hkern u1="Y" u2="o" k="14" />
+<hkern u1="Y" u2="p" k="10" />
+<hkern u1="Y" u2="q" k="14" />
+<hkern u1="Y" u2="r" k="10" />
+<hkern u1="Y" u2="s" k="13" />
+<hkern u1="Y" u2="t" k="5" />
+<hkern u1="Y" u2="u" k="10" />
+<hkern u1="Y" u2="v" k="5" />
+<hkern u1="Y" u2="w" k="3" />
+<hkern u1="Y" u2="x" k="5" />
+<hkern u1="Y" u2="y" k="5" />
+<hkern u1="Y" u2="z" k="8" />
+<hkern u1="Y" u2="}" k="-13" />
+<hkern u1="Y" u2="&#xc4;" k="8" />
+<hkern u1="Y" u2="&#xc5;" k="8" />
+<hkern u1="Y" u2="&#xd6;" k="1" />
+<hkern u1="Y" u2="&#xe7;" k="14" />
+<hkern u1="Y" u2="&#xae;" k="8" />
+<hkern u1="Y" u2="&#xa9;" k="8" />
+<hkern u1="Y" u2="&#xc6;" k="8" />
+<hkern u1="Y" u2="&#xd8;" k="1" />
+<hkern u1="Y" u2="&#x3c0;" k="8" />
+<hkern u1="Y" u2="&#xe6;" k="14" />
+<hkern u1="Y" u2="&#xab;" k="15" />
+<hkern u1="Y" u2="&#xbb;" k="10" />
+<hkern u1="Y" u2="&#x2026;" k="13" />
+<hkern u1="Y" u2="&#xc0;" k="8" />
+<hkern u1="Y" u2="&#xc3;" k="8" />
+<hkern u1="Y" u2="&#xd5;" k="1" />
+<hkern u1="Y" u2="&#x152;" k="1" />
+<hkern u1="Y" u2="&#x153;" k="14" />
+<hkern u1="Y" u2="&#x2013;" k="13" />
+<hkern u1="Y" u2="&#x2014;" k="13" />
+<hkern u1="Y" u2="&#x2039;" k="15" />
+<hkern u1="Y" u2="&#x203a;" k="10" />
+<hkern u1="Y" u2="&#x201a;" k="13" />
+<hkern u1="Y" u2="&#x201e;" k="13" />
+<hkern u1="Y" u2="&#xc2;" k="8" />
+<hkern u1="Y" u2="&#xc1;" k="8" />
+<hkern u1="Y" u2="&#xd3;" k="1" />
+<hkern u1="Y" u2="&#xd4;" k="1" />
+<hkern u1="Y" u2="&#xd2;" k="1" />
+<hkern u1="Y" u2="&#x131;" k="10" />
+<hkern u1="Z" u2="&#xf0;" k="8" />
+<hkern u1="Z" u2="-" k="13" />
+<hkern u1="Z" u2="A" k="3" />
+<hkern u1="Z" u2="C" k="11" />
+<hkern u1="Z" u2="G" k="11" />
+<hkern u1="Z" u2="J" k="3" />
+<hkern u1="Z" u2="O" k="11" />
+<hkern u1="Z" u2="Q" k="11" />
+<hkern u1="Z" u2="T" k="3" />
+<hkern u1="Z" u2="a" k="10" />
+<hkern u1="Z" u2="b" k="3" />
+<hkern u1="Z" u2="c" k="10" />
+<hkern u1="Z" u2="d" k="10" />
+<hkern u1="Z" u2="e" k="10" />
+<hkern u1="Z" u2="f" k="5" />
+<hkern u1="Z" u2="g" k="5" />
+<hkern u1="Z" u2="h" k="3" />
+<hkern u1="Z" u2="i" k="5" />
+<hkern u1="Z" u2="j" k="4" />
+<hkern u1="Z" u2="k" k="3" />
+<hkern u1="Z" u2="l" k="3" />
+<hkern u1="Z" u2="m" k="5" />
+<hkern u1="Z" u2="n" k="5" />
+<hkern u1="Z" u2="o" k="10" />
+<hkern u1="Z" u2="p" k="5" />
+<hkern u1="Z" u2="q" k="10" />
+<hkern u1="Z" u2="r" k="5" />
+<hkern u1="Z" u2="u" k="5" />
+<hkern u1="Z" u2="v" k="8" />
+<hkern u1="Z" u2="w" k="3" />
+<hkern u1="Z" u2="y" k="8" />
+<hkern u1="Z" u2="&#xc4;" k="3" />
+<hkern u1="Z" u2="&#xc5;" k="3" />
+<hkern u1="Z" u2="&#xd6;" k="11" />
+<hkern u1="Z" u2="&#xe7;" k="10" />
+<hkern u1="Z" u2="&#xc6;" k="3" />
+<hkern u1="Z" u2="&#xd8;" k="11" />
+<hkern u1="Z" u2="&#xe6;" k="10" />
+<hkern u1="Z" u2="&#xc0;" k="3" />
+<hkern u1="Z" u2="&#xc3;" k="3" />
+<hkern u1="Z" u2="&#xd5;" k="11" />
+<hkern u1="Z" u2="&#x152;" k="11" />
+<hkern u1="Z" u2="&#x153;" k="10" />
+<hkern u1="Z" u2="&#x2013;" k="13" />
+<hkern u1="Z" u2="&#x2014;" k="13" />
+<hkern u1="Z" u2="&#xc2;" k="3" />
+<hkern u1="Z" u2="&#xc1;" k="3" />
+<hkern u1="Z" u2="&#xd3;" k="11" />
+<hkern u1="Z" u2="&#xd4;" k="11" />
+<hkern u1="Z" u2="&#xd2;" k="11" />
+<hkern u1="Z" u2="&#x131;" k="5" />
+<hkern u1="[" u2="&#xdd;" k="-13" />
+<hkern u1="[" u2="V" k="-8" />
+<hkern u1="[" u2="W" k="-3" />
+<hkern u1="[" u2="X" k="-10" />
+<hkern u1="[" u2="Y" k="-13" />
+<hkern u1="[" u2="g" k="-5" />
+<hkern u1="[" u2="j" k="-38" />
+<hkern u1="[" u2="&#x178;" k="-13" />
+<hkern u1="\" u2="&#xdd;" k="15" />
+<hkern u1="\" u2="T" k="23" />
+<hkern u1="\" u2="V" k="18" />
+<hkern u1="\" u2="W" k="13" />
+<hkern u1="\" u2="Y" k="15" />
+<hkern u1="\" u2="a" k="3" />
+<hkern u1="\" u2="c" k="3" />
+<hkern u1="\" u2="d" k="3" />
+<hkern u1="\" u2="e" k="3" />
+<hkern u1="\" u2="j" k="-33" />
+<hkern u1="\" u2="o" k="3" />
+<hkern u1="\" u2="q" k="3" />
+<hkern u1="\" u2="&#xe7;" k="3" />
+<hkern u1="\" u2="&#xe6;" k="3" />
+<hkern u1="\" u2="&#x153;" k="3" />
+<hkern u1="\" u2="&#x178;" k="15" />
+<hkern u1="a" u2="?" k="3" />
+<hkern u1="a" u2="\" k="3" />
+<hkern u1="a" u2="t" k="3" />
+<hkern u1="a" u2="v" k="1" />
+<hkern u1="a" u2="y" k="1" />
+<hkern u1="a" u2="&#x2122;" k="5" />
+<hkern u1="a" u2="&#x201c;" k="8" />
+<hkern u1="a" u2="&#x2018;" k="8" />
+<hkern u1="b" u2="&#x161;" k="-1" />
+<hkern u1="b" u2="&#x2c;" k="5" />
+<hkern u1="b" u2="." k="5" />
+<hkern u1="b" u2="/" k="3" />
+<hkern u1="b" u2=":" k="3" />
+<hkern u1="b" u2=";" k="3" />
+<hkern u1="b" u2="?" k="5" />
+<hkern u1="b" u2="\" k="3" />
+<hkern u1="b" u2="s" k="-1" />
+<hkern u1="b" u2="t" k="3" />
+<hkern u1="b" u2="v" k="-1" />
+<hkern u1="b" u2="x" k="1" />
+<hkern u1="b" u2="y" k="-1" />
+<hkern u1="b" u2="z" k="4" />
+<hkern u1="b" u2="&#x2122;" k="8" />
+<hkern u1="b" u2="&#x2026;" k="5" />
+<hkern u1="b" u2="&#x201c;" k="10" />
+<hkern u1="b" u2="&#x2018;" k="10" />
+<hkern u1="b" u2="&#x201a;" k="5" />
+<hkern u1="b" u2="&#x201e;" k="5" />
+<hkern u1="d" u2="v" k="-3" />
+<hkern u1="d" u2="y" k="-3" />
+<hkern u1="e" u2="v" k="3" />
+<hkern u1="e" u2="x" k="4" />
+<hkern u1="e" u2="y" k="3" />
+<hkern u1="f" u2="&#xf0;" k="5" />
+<hkern u1="f" u2="&amp;" k="8" />
+<hkern u1="f" u2=")" k="-18" />
+<hkern u1="f" u2="*" k="-5" />
+<hkern u1="f" u2="&#x2c;" k="20" />
+<hkern u1="f" u2="-" k="8" />
+<hkern u1="f" u2="." k="20" />
+<hkern u1="f" u2="/" k="13" />
+<hkern u1="f" u2=":" k="3" />
+<hkern u1="f" u2=";" k="3" />
+<hkern u1="f" u2="?" k="-10" />
+<hkern u1="f" u2="\" k="-10" />
+<hkern u1="f" u2="]" k="-18" />
+<hkern u1="f" u2="a" k="3" />
+<hkern u1="f" u2="c" k="3" />
+<hkern u1="f" u2="d" k="3" />
+<hkern u1="f" u2="e" k="3" />
+<hkern u1="f" u2="m" k="1" />
+<hkern u1="f" u2="n" k="1" />
+<hkern u1="f" u2="o" k="3" />
+<hkern u1="f" u2="p" k="1" />
+<hkern u1="f" u2="q" k="3" />
+<hkern u1="f" u2="r" k="1" />
+<hkern u1="f" u2="u" k="1" />
+<hkern u1="f" u2="v" k="-5" />
+<hkern u1="f" u2="y" k="-5" />
+<hkern u1="f" u2="}" k="-18" />
+<hkern u1="f" u2="&#xe7;" k="3" />
+<hkern u1="f" u2="&#x2122;" k="-10" />
+<hkern u1="f" u2="&#xe6;" k="3" />
+<hkern u1="f" u2="&#xab;" k="13" />
+<hkern u1="f" u2="&#xbb;" k="5" />
+<hkern u1="f" u2="&#x2026;" k="20" />
+<hkern u1="f" u2="&#x153;" k="3" />
+<hkern u1="f" u2="&#x2013;" k="8" />
+<hkern u1="f" u2="&#x2014;" k="8" />
+<hkern u1="f" u2="&#x201c;" k="-8" />
+<hkern u1="f" u2="&#x201d;" k="-13" />
+<hkern u1="f" u2="&#x2018;" k="-8" />
+<hkern u1="f" u2="&#x2019;" k="-13" />
+<hkern u1="f" u2="&#x2039;" k="13" />
+<hkern u1="f" u2="&#x203a;" k="5" />
+<hkern u1="f" u2="&#x201a;" k="20" />
+<hkern u1="f" u2="&#x201e;" k="20" />
+<hkern u1="f" u2="&#x131;" k="1" />
+<hkern u1="g" u2=")" k="-5" />
+<hkern u1="g" u2="/" k="-10" />
+<hkern u1="g" u2=";" k="-5" />
+<hkern u1="g" u2="]" k="-5" />
+<hkern u1="g" u2="g" k="-3" />
+<hkern u1="g" u2="j" k="-13" />
+<hkern u1="g" u2="t" k="-3" />
+<hkern u1="g" u2="}" k="-5" />
+<hkern u1="g" u2="&#x201c;" k="-5" />
+<hkern u1="g" u2="&#x201d;" k="-10" />
+<hkern u1="g" u2="&#x2018;" k="-5" />
+<hkern u1="g" u2="&#x2019;" k="-10" />
+<hkern u1="h" u2="?" k="3" />
+<hkern u1="h" u2="\" k="3" />
+<hkern u1="h" u2="t" k="3" />
+<hkern u1="h" u2="v" k="1" />
+<hkern u1="h" u2="y" k="1" />
+<hkern u1="h" u2="&#x2122;" k="5" />
+<hkern u1="h" u2="&#x201c;" k="8" />
+<hkern u1="h" u2="&#x2018;" k="8" />
+<hkern u1="j" u2="j" k="-5" />
+<hkern u1="j" u2="v" k="-5" />
+<hkern u1="j" u2="y" k="-5" />
+<hkern u1="k" u2="a" k="3" />
+<hkern u1="k" u2="c" k="3" />
+<hkern u1="k" u2="d" k="3" />
+<hkern u1="k" u2="e" k="3" />
+<hkern u1="k" u2="g" k="5" />
+<hkern u1="k" u2="o" k="3" />
+<hkern u1="k" u2="q" k="3" />
+<hkern u1="k" u2="v" k="-3" />
+<hkern u1="k" u2="y" k="-3" />
+<hkern u1="k" u2="&#xe7;" k="3" />
+<hkern u1="k" u2="&#xe6;" k="3" />
+<hkern u1="k" u2="&#x153;" k="3" />
+<hkern u1="l" u2="v" k="-3" />
+<hkern u1="l" u2="y" k="-3" />
+<hkern u1="m" u2="?" k="3" />
+<hkern u1="m" u2="\" k="3" />
+<hkern u1="m" u2="t" k="3" />
+<hkern u1="m" u2="v" k="1" />
+<hkern u1="m" u2="y" k="1" />
+<hkern u1="m" u2="&#x2122;" k="5" />
+<hkern u1="m" u2="&#x201c;" k="8" />
+<hkern u1="m" u2="&#x2018;" k="8" />
+<hkern u1="n" u2="?" k="3" />
+<hkern u1="n" u2="\" k="3" />
+<hkern u1="n" u2="t" k="3" />
+<hkern u1="n" u2="v" k="1" />
+<hkern u1="n" u2="y" k="1" />
+<hkern u1="n" u2="&#x2122;" k="5" />
+<hkern u1="n" u2="&#x201c;" k="8" />
+<hkern u1="n" u2="&#x2018;" k="8" />
+<hkern u1="o" u2="&#x161;" k="-1" />
+<hkern u1="o" u2="&#x2c;" k="5" />
+<hkern u1="o" u2="." k="5" />
+<hkern u1="o" u2="/" k="3" />
+<hkern u1="o" u2=":" k="3" />
+<hkern u1="o" u2=";" k="3" />
+<hkern u1="o" u2="?" k="5" />
+<hkern u1="o" u2="\" k="3" />
+<hkern u1="o" u2="s" k="-1" />
+<hkern u1="o" u2="t" k="3" />
+<hkern u1="o" u2="v" k="-1" />
+<hkern u1="o" u2="x" k="1" />
+<hkern u1="o" u2="y" k="-1" />
+<hkern u1="o" u2="z" k="4" />
+<hkern u1="o" u2="&#x2122;" k="8" />
+<hkern u1="o" u2="&#x2026;" k="5" />
+<hkern u1="o" u2="&#x201c;" k="10" />
+<hkern u1="o" u2="&#x2018;" k="10" />
+<hkern u1="o" u2="&#x201a;" k="5" />
+<hkern u1="o" u2="&#x201e;" k="5" />
+<hkern u1="p" u2="&#x161;" k="-1" />
+<hkern u1="p" u2="&#x2c;" k="5" />
+<hkern u1="p" u2="." k="5" />
+<hkern u1="p" u2="/" k="3" />
+<hkern u1="p" u2=":" k="3" />
+<hkern u1="p" u2=";" k="3" />
+<hkern u1="p" u2="?" k="5" />
+<hkern u1="p" u2="\" k="3" />
+<hkern u1="p" u2="s" k="-1" />
+<hkern u1="p" u2="t" k="3" />
+<hkern u1="p" u2="v" k="-1" />
+<hkern u1="p" u2="x" k="1" />
+<hkern u1="p" u2="y" k="-1" />
+<hkern u1="p" u2="z" k="4" />
+<hkern u1="p" u2="&#x2122;" k="8" />
+<hkern u1="p" u2="&#x2026;" k="5" />
+<hkern u1="p" u2="&#x201c;" k="10" />
+<hkern u1="p" u2="&#x2018;" k="10" />
+<hkern u1="p" u2="&#x201a;" k="5" />
+<hkern u1="p" u2="&#x201e;" k="5" />
+<hkern u1="q" u2="j" k="-8" />
+<hkern u1="r" u2="&#x2c;" k="13" />
+<hkern u1="r" u2="-" k="5" />
+<hkern u1="r" u2="." k="13" />
+<hkern u1="r" u2="/" k="15" />
+<hkern u1="r" u2="?" k="-5" />
+<hkern u1="r" u2="f" k="-4" />
+<hkern u1="r" u2="g" k="3" />
+<hkern u1="r" u2="t" k="-5" />
+<hkern u1="r" u2="v" k="-10" />
+<hkern u1="r" u2="w" k="-8" />
+<hkern u1="r" u2="x" k="-3" />
+<hkern u1="r" u2="y" k="-10" />
+<hkern u1="r" u2="&#x2026;" k="13" />
+<hkern u1="r" u2="&#x2013;" k="5" />
+<hkern u1="r" u2="&#x2014;" k="5" />
+<hkern u1="r" u2="&#x201c;" k="-5" />
+<hkern u1="r" u2="&#x201d;" k="-10" />
+<hkern u1="r" u2="&#x2018;" k="-5" />
+<hkern u1="r" u2="&#x2019;" k="-10" />
+<hkern u1="r" u2="&#x201a;" k="13" />
+<hkern u1="r" u2="&#x201e;" k="13" />
+<hkern u1="t" u2="&#x2c;" k="-3" />
+<hkern u1="t" u2="." k="-3" />
+<hkern u1="t" u2="a" k="1" />
+<hkern u1="t" u2="c" k="1" />
+<hkern u1="t" u2="d" k="1" />
+<hkern u1="t" u2="e" k="1" />
+<hkern u1="t" u2="o" k="1" />
+<hkern u1="t" u2="q" k="1" />
+<hkern u1="t" u2="v" k="-4" />
+<hkern u1="t" u2="x" k="-1" />
+<hkern u1="t" u2="y" k="-4" />
+<hkern u1="t" u2="&#xe7;" k="1" />
+<hkern u1="t" u2="&#xe6;" k="1" />
+<hkern u1="t" u2="&#x2026;" k="-3" />
+<hkern u1="t" u2="&#x153;" k="1" />
+<hkern u1="t" u2="&#x201c;" k="-3" />
+<hkern u1="t" u2="&#x2018;" k="-3" />
+<hkern u1="t" u2="&#x201a;" k="-3" />
+<hkern u1="t" u2="&#x201e;" k="-3" />
+<hkern u1="u" u2="v" k="-3" />
+<hkern u1="u" u2="y" k="-3" />
+<hkern u1="v" u2="&#x2c;" k="13" />
+<hkern u1="v" u2="-" k="3" />
+<hkern u1="v" u2="." k="13" />
+<hkern u1="v" u2="a" k="3" />
+<hkern u1="v" u2="c" k="3" />
+<hkern u1="v" u2="d" k="3" />
+<hkern u1="v" u2="e" k="3" />
+<hkern u1="v" u2="o" k="3" />
+<hkern u1="v" u2="q" k="3" />
+<hkern u1="v" u2="v" k="-8" />
+<hkern u1="v" u2="w" k="-3" />
+<hkern u1="v" u2="y" k="-8" />
+<hkern u1="v" u2="&#xe7;" k="3" />
+<hkern u1="v" u2="&#xe6;" k="3" />
+<hkern u1="v" u2="&#x2026;" k="13" />
+<hkern u1="v" u2="&#x153;" k="3" />
+<hkern u1="v" u2="&#x2013;" k="3" />
+<hkern u1="v" u2="&#x2014;" k="3" />
+<hkern u1="v" u2="&#x201c;" k="-8" />
+<hkern u1="v" u2="&#x201d;" k="-8" />
+<hkern u1="v" u2="&#x2018;" k="-8" />
+<hkern u1="v" u2="&#x2019;" k="-8" />
+<hkern u1="v" u2="&#x201a;" k="13" />
+<hkern u1="v" u2="&#x201e;" k="13" />
+<hkern u1="w" u2="&#x2c;" k="8" />
+<hkern u1="w" u2="." k="8" />
+<hkern u1="w" u2="v" k="-3" />
+<hkern u1="w" u2="y" k="-3" />
+<hkern u1="w" u2="&#x2026;" k="8" />
+<hkern u1="w" u2="&#x201c;" k="-5" />
+<hkern u1="w" u2="&#x201d;" k="-5" />
+<hkern u1="w" u2="&#x2018;" k="-5" />
+<hkern u1="w" u2="&#x2019;" k="-5" />
+<hkern u1="w" u2="&#x201a;" k="8" />
+<hkern u1="w" u2="&#x201e;" k="8" />
+<hkern u1="x" u2="-" k="8" />
+<hkern u1="x" u2="a" k="4" />
+<hkern u1="x" u2="c" k="4" />
+<hkern u1="x" u2="d" k="4" />
+<hkern u1="x" u2="e" k="4" />
+<hkern u1="x" u2="o" k="4" />
+<hkern u1="x" u2="q" k="4" />
+<hkern u1="x" u2="v" k="-5" />
+<hkern u1="x" u2="y" k="-5" />
+<hkern u1="x" u2="&#xe7;" k="4" />
+<hkern u1="x" u2="&#xe6;" k="4" />
+<hkern u1="x" u2="&#xab;" k="13" />
+<hkern u1="x" u2="&#x153;" k="4" />
+<hkern u1="x" u2="&#x2013;" k="8" />
+<hkern u1="x" u2="&#x2014;" k="8" />
+<hkern u1="x" u2="&#x201c;" k="-3" />
+<hkern u1="x" u2="&#x201d;" k="-5" />
+<hkern u1="x" u2="&#x2018;" k="-3" />
+<hkern u1="x" u2="&#x2019;" k="-5" />
+<hkern u1="x" u2="&#x2039;" k="13" />
+<hkern u1="y" u2="&#x2c;" k="13" />
+<hkern u1="y" u2="-" k="3" />
+<hkern u1="y" u2="." k="13" />
+<hkern u1="y" u2="a" k="3" />
+<hkern u1="y" u2="c" k="3" />
+<hkern u1="y" u2="d" k="3" />
+<hkern u1="y" u2="e" k="3" />
+<hkern u1="y" u2="o" k="3" />
+<hkern u1="y" u2="q" k="3" />
+<hkern u1="y" u2="v" k="-8" />
+<hkern u1="y" u2="w" k="-3" />
+<hkern u1="y" u2="y" k="-8" />
+<hkern u1="y" u2="&#xe7;" k="3" />
+<hkern u1="y" u2="&#xe6;" k="3" />
+<hkern u1="y" u2="&#x2026;" k="13" />
+<hkern u1="y" u2="&#x153;" k="3" />
+<hkern u1="y" u2="&#x2013;" k="3" />
+<hkern u1="y" u2="&#x2014;" k="3" />
+<hkern u1="y" u2="&#x201c;" k="-8" />
+<hkern u1="y" u2="&#x201d;" k="-8" />
+<hkern u1="y" u2="&#x2018;" k="-8" />
+<hkern u1="y" u2="&#x2019;" k="-8" />
+<hkern u1="y" u2="&#x201a;" k="13" />
+<hkern u1="y" u2="&#x201e;" k="13" />
+<hkern u1="z" u2="-" k="8" />
+<hkern u1="z" u2="a" k="5" />
+<hkern u1="z" u2="c" k="5" />
+<hkern u1="z" u2="d" k="5" />
+<hkern u1="z" u2="e" k="5" />
+<hkern u1="z" u2="g" k="3" />
+<hkern u1="z" u2="m" k="4" />
+<hkern u1="z" u2="n" k="4" />
+<hkern u1="z" u2="o" k="5" />
+<hkern u1="z" u2="p" k="4" />
+<hkern u1="z" u2="q" k="5" />
+<hkern u1="z" u2="r" k="4" />
+<hkern u1="z" u2="u" k="4" />
+<hkern u1="z" u2="v" k="-3" />
+<hkern u1="z" u2="y" k="-3" />
+<hkern u1="z" u2="&#xe7;" k="5" />
+<hkern u1="z" u2="&#xe6;" k="5" />
+<hkern u1="z" u2="&#xab;" k="15" />
+<hkern u1="z" u2="&#x153;" k="5" />
+<hkern u1="z" u2="&#x2013;" k="8" />
+<hkern u1="z" u2="&#x2014;" k="8" />
+<hkern u1="z" u2="&#x2039;" k="15" />
+<hkern u1="z" u2="&#x131;" k="4" />
+<hkern u1="{" u2="&#xdd;" k="-13" />
+<hkern u1="{" u2="V" k="-8" />
+<hkern u1="{" u2="W" k="-3" />
+<hkern u1="{" u2="X" k="-10" />
+<hkern u1="{" u2="Y" k="-13" />
+<hkern u1="{" u2="g" k="-5" />
+<hkern u1="{" u2="j" k="-38" />
+<hkern u1="{" u2="&#x178;" k="-13" />
+<hkern u1="&#xc4;" u2="&#x160;" k="-1" />
+<hkern u1="&#xc4;" u2="&#xdd;" k="18" />
+<hkern u1="&#xc4;" u2="*" k="15" />
+<hkern u1="&#xc4;" u2="&#x2c;" k="-8" />
+<hkern u1="&#xc4;" u2="-" k="-3" />
+<hkern u1="&#xc4;" u2="." k="-8" />
+<hkern u1="&#xc4;" u2="@" k="3" />
+<hkern u1="&#xc4;" u2="A" k="-3" />
+<hkern u1="&#xc4;" u2="C" k="10" />
+<hkern u1="&#xc4;" u2="G" k="10" />
+<hkern u1="&#xc4;" u2="J" k="3" />
+<hkern u1="&#xc4;" u2="O" k="10" />
+<hkern u1="&#xc4;" u2="Q" k="10" />
+<hkern u1="&#xc4;" u2="S" k="-1" />
+<hkern u1="&#xc4;" u2="T" k="23" />
+<hkern u1="&#xc4;" u2="V" k="18" />
+<hkern u1="&#xc4;" u2="W" k="18" />
+<hkern u1="&#xc4;" u2="Y" k="18" />
+<hkern u1="&#xc4;" u2="a" k="1" />
+<hkern u1="&#xc4;" u2="c" k="1" />
+<hkern u1="&#xc4;" u2="d" k="1" />
+<hkern u1="&#xc4;" u2="e" k="1" />
+<hkern u1="&#xc4;" u2="m" k="1" />
+<hkern u1="&#xc4;" u2="n" k="1" />
+<hkern u1="&#xc4;" u2="o" k="1" />
+<hkern u1="&#xc4;" u2="p" k="1" />
+<hkern u1="&#xc4;" u2="q" k="1" />
+<hkern u1="&#xc4;" u2="r" k="1" />
+<hkern u1="&#xc4;" u2="t" k="1" />
+<hkern u1="&#xc4;" u2="u" k="1" />
+<hkern u1="&#xc4;" u2="v" k="8" />
+<hkern u1="&#xc4;" u2="w" k="3" />
+<hkern u1="&#xc4;" u2="x" k="-5" />
+<hkern u1="&#xc4;" u2="y" k="8" />
+<hkern u1="&#xc4;" u2="z" k="-3" />
+<hkern u1="&#xc4;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="10" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="1" />
+<hkern u1="&#xc4;" u2="&#xae;" k="3" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="3" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="18" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="10" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="3" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="1" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="10" />
+<hkern u1="&#xc4;" u2="&#x152;" k="10" />
+<hkern u1="&#xc4;" u2="&#x153;" k="1" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-3" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-3" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="15" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="8" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="15" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="8" />
+<hkern u1="&#xc4;" u2="&#x178;" k="18" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-8" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="10" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="10" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="10" />
+<hkern u1="&#xc4;" u2="&#x131;" k="1" />
+<hkern u1="&#xc5;" u2="&#x160;" k="-1" />
+<hkern u1="&#xc5;" u2="&#xdd;" k="18" />
+<hkern u1="&#xc5;" u2="*" k="15" />
+<hkern u1="&#xc5;" u2="&#x2c;" k="-8" />
+<hkern u1="&#xc5;" u2="-" k="-3" />
+<hkern u1="&#xc5;" u2="." k="-8" />
+<hkern u1="&#xc5;" u2="@" k="3" />
+<hkern u1="&#xc5;" u2="A" k="-3" />
+<hkern u1="&#xc5;" u2="C" k="10" />
+<hkern u1="&#xc5;" u2="G" k="10" />
+<hkern u1="&#xc5;" u2="J" k="3" />
+<hkern u1="&#xc5;" u2="O" k="10" />
+<hkern u1="&#xc5;" u2="Q" k="10" />
+<hkern u1="&#xc5;" u2="S" k="-1" />
+<hkern u1="&#xc5;" u2="T" k="23" />
+<hkern u1="&#xc5;" u2="V" k="18" />
+<hkern u1="&#xc5;" u2="W" k="18" />
+<hkern u1="&#xc5;" u2="Y" k="18" />
+<hkern u1="&#xc5;" u2="a" k="1" />
+<hkern u1="&#xc5;" u2="c" k="1" />
+<hkern u1="&#xc5;" u2="d" k="1" />
+<hkern u1="&#xc5;" u2="e" k="1" />
+<hkern u1="&#xc5;" u2="m" k="1" />
+<hkern u1="&#xc5;" u2="n" k="1" />
+<hkern u1="&#xc5;" u2="o" k="1" />
+<hkern u1="&#xc5;" u2="p" k="1" />
+<hkern u1="&#xc5;" u2="q" k="1" />
+<hkern u1="&#xc5;" u2="r" k="1" />
+<hkern u1="&#xc5;" u2="t" k="1" />
+<hkern u1="&#xc5;" u2="u" k="1" />
+<hkern u1="&#xc5;" u2="v" k="8" />
+<hkern u1="&#xc5;" u2="w" k="3" />
+<hkern u1="&#xc5;" u2="x" k="-5" />
+<hkern u1="&#xc5;" u2="y" k="8" />
+<hkern u1="&#xc5;" u2="z" k="-3" />
+<hkern u1="&#xc5;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="10" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="1" />
+<hkern u1="&#xc5;" u2="&#xae;" k="3" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="3" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="18" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="10" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="3" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="1" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="10" />
+<hkern u1="&#xc5;" u2="&#x152;" k="10" />
+<hkern u1="&#xc5;" u2="&#x153;" k="1" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-3" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-3" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="15" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="8" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="15" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="8" />
+<hkern u1="&#xc5;" u2="&#x178;" k="18" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-8" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="10" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="10" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="10" />
+<hkern u1="&#xc5;" u2="&#x131;" k="1" />
+<hkern u1="&#xc9;" u2="@" k="5" />
+<hkern u1="&#xc9;" u2="T" k="-4" />
+<hkern u1="&#xc9;" u2="a" k="6" />
+<hkern u1="&#xc9;" u2="c" k="6" />
+<hkern u1="&#xc9;" u2="d" k="6" />
+<hkern u1="&#xc9;" u2="e" k="6" />
+<hkern u1="&#xc9;" u2="f" k="3" />
+<hkern u1="&#xc9;" u2="g" k="1" />
+<hkern u1="&#xc9;" u2="o" k="6" />
+<hkern u1="&#xc9;" u2="q" k="6" />
+<hkern u1="&#xc9;" u2="v" k="5" />
+<hkern u1="&#xc9;" u2="y" k="5" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="6" />
+<hkern u1="&#xc9;" u2="&#xae;" k="5" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="5" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="5" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="6" />
+<hkern u1="&#xc9;" u2="&#x153;" k="6" />
+<hkern u1="&#xd1;" u2="/" k="5" />
+<hkern u1="&#xd1;" u2="v" k="-5" />
+<hkern u1="&#xd1;" u2="y" k="-5" />
+<hkern u1="&#xd6;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd6;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd6;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd6;" u2="." k="10" />
+<hkern u1="&#xd6;" u2="/" k="13" />
+<hkern u1="&#xd6;" u2="?" k="5" />
+<hkern u1="&#xd6;" u2="J" k="11" />
+<hkern u1="&#xd6;" u2="T" k="13" />
+<hkern u1="&#xd6;" u2="V" k="10" />
+<hkern u1="&#xd6;" u2="W" k="15" />
+<hkern u1="&#xd6;" u2="X" k="9" />
+<hkern u1="&#xd6;" u2="Y" k="11" />
+<hkern u1="&#xd6;" u2="Z" k="11" />
+<hkern u1="&#xd6;" u2="a" k="1" />
+<hkern u1="&#xd6;" u2="b" k="3" />
+<hkern u1="&#xd6;" u2="c" k="1" />
+<hkern u1="&#xd6;" u2="d" k="1" />
+<hkern u1="&#xd6;" u2="e" k="1" />
+<hkern u1="&#xd6;" u2="h" k="3" />
+<hkern u1="&#xd6;" u2="k" k="3" />
+<hkern u1="&#xd6;" u2="l" k="3" />
+<hkern u1="&#xd6;" u2="m" k="1" />
+<hkern u1="&#xd6;" u2="n" k="1" />
+<hkern u1="&#xd6;" u2="o" k="1" />
+<hkern u1="&#xd6;" u2="p" k="1" />
+<hkern u1="&#xd6;" u2="q" k="1" />
+<hkern u1="&#xd6;" u2="r" k="1" />
+<hkern u1="&#xd6;" u2="u" k="1" />
+<hkern u1="&#xd6;" u2="x" k="3" />
+<hkern u1="&#xd6;" u2="z" k="3" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd6;" u2="&#x153;" k="1" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd6;" u2="&#x178;" k="11" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd6;" u2="&#x131;" k="1" />
+<hkern u1="&#xdc;" u2="&#x2c;" k="3" />
+<hkern u1="&#xdc;" u2="." k="3" />
+<hkern u1="&#xdc;" u2="A" k="-5" />
+<hkern u1="&#xdc;" u2="J" k="3" />
+<hkern u1="&#xdc;" u2="v" k="-5" />
+<hkern u1="&#xdc;" u2="y" k="-5" />
+<hkern u1="&#xdc;" u2="&#xc4;" k="-5" />
+<hkern u1="&#xdc;" u2="&#xc5;" k="-5" />
+<hkern u1="&#xdc;" u2="&#xc6;" k="-5" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="3" />
+<hkern u1="&#xdc;" u2="&#xc0;" k="-5" />
+<hkern u1="&#xdc;" u2="&#xc3;" k="-5" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="3" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="3" />
+<hkern u1="&#xdc;" u2="&#xc2;" k="-5" />
+<hkern u1="&#xdc;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xe1;" u2="?" k="3" />
+<hkern u1="&#xe1;" u2="\" k="3" />
+<hkern u1="&#xe1;" u2="t" k="3" />
+<hkern u1="&#xe1;" u2="v" k="1" />
+<hkern u1="&#xe1;" u2="y" k="1" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="5" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="8" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="8" />
+<hkern u1="&#xe0;" u2="?" k="3" />
+<hkern u1="&#xe0;" u2="\" k="3" />
+<hkern u1="&#xe0;" u2="t" k="3" />
+<hkern u1="&#xe0;" u2="v" k="1" />
+<hkern u1="&#xe0;" u2="y" k="1" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="5" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="8" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="8" />
+<hkern u1="&#xe2;" u2="?" k="3" />
+<hkern u1="&#xe2;" u2="\" k="3" />
+<hkern u1="&#xe2;" u2="t" k="3" />
+<hkern u1="&#xe2;" u2="v" k="1" />
+<hkern u1="&#xe2;" u2="y" k="1" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="5" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="8" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="8" />
+<hkern u1="&#xe4;" u2="?" k="3" />
+<hkern u1="&#xe4;" u2="\" k="3" />
+<hkern u1="&#xe4;" u2="t" k="3" />
+<hkern u1="&#xe4;" u2="v" k="1" />
+<hkern u1="&#xe4;" u2="y" k="1" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="5" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="8" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="8" />
+<hkern u1="&#xe9;" u2="v" k="3" />
+<hkern u1="&#xe9;" u2="x" k="4" />
+<hkern u1="&#xe9;" u2="y" k="3" />
+<hkern u1="&#xe8;" u2="v" k="3" />
+<hkern u1="&#xe8;" u2="x" k="4" />
+<hkern u1="&#xe8;" u2="y" k="3" />
+<hkern u1="&#xea;" u2="v" k="3" />
+<hkern u1="&#xea;" u2="x" k="4" />
+<hkern u1="&#xea;" u2="y" k="3" />
+<hkern u1="&#xeb;" u2="v" k="3" />
+<hkern u1="&#xeb;" u2="x" k="4" />
+<hkern u1="&#xeb;" u2="y" k="3" />
+<hkern u1="&#xf1;" u2="?" k="3" />
+<hkern u1="&#xf1;" u2="\" k="3" />
+<hkern u1="&#xf1;" u2="t" k="3" />
+<hkern u1="&#xf1;" u2="v" k="1" />
+<hkern u1="&#xf1;" u2="y" k="1" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="5" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="8" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="8" />
+<hkern u1="&#xf3;" u2="&#x161;" k="-1" />
+<hkern u1="&#xf3;" u2="&#x2c;" k="5" />
+<hkern u1="&#xf3;" u2="." k="5" />
+<hkern u1="&#xf3;" u2="/" k="3" />
+<hkern u1="&#xf3;" u2=":" k="3" />
+<hkern u1="&#xf3;" u2=";" k="3" />
+<hkern u1="&#xf3;" u2="?" k="5" />
+<hkern u1="&#xf3;" u2="\" k="3" />
+<hkern u1="&#xf3;" u2="s" k="-1" />
+<hkern u1="&#xf3;" u2="t" k="3" />
+<hkern u1="&#xf3;" u2="v" k="-1" />
+<hkern u1="&#xf3;" u2="x" k="1" />
+<hkern u1="&#xf3;" u2="y" k="-1" />
+<hkern u1="&#xf3;" u2="z" k="4" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="8" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="5" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="10" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="10" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="5" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="5" />
+<hkern u1="&#xf4;" u2="&#x161;" k="-1" />
+<hkern u1="&#xf4;" u2="&#x2c;" k="5" />
+<hkern u1="&#xf4;" u2="." k="5" />
+<hkern u1="&#xf4;" u2="/" k="3" />
+<hkern u1="&#xf4;" u2=":" k="3" />
+<hkern u1="&#xf4;" u2=";" k="3" />
+<hkern u1="&#xf4;" u2="?" k="5" />
+<hkern u1="&#xf4;" u2="\" k="3" />
+<hkern u1="&#xf4;" u2="s" k="-1" />
+<hkern u1="&#xf4;" u2="t" k="3" />
+<hkern u1="&#xf4;" u2="v" k="-1" />
+<hkern u1="&#xf4;" u2="x" k="1" />
+<hkern u1="&#xf4;" u2="y" k="-1" />
+<hkern u1="&#xf4;" u2="z" k="4" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="8" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="5" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="10" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="10" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="5" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="5" />
+<hkern u1="&#xf6;" u2="&#x161;" k="-1" />
+<hkern u1="&#xf6;" u2="&#x2c;" k="5" />
+<hkern u1="&#xf6;" u2="." k="5" />
+<hkern u1="&#xf6;" u2="/" k="3" />
+<hkern u1="&#xf6;" u2=":" k="3" />
+<hkern u1="&#xf6;" u2=";" k="3" />
+<hkern u1="&#xf6;" u2="?" k="5" />
+<hkern u1="&#xf6;" u2="\" k="3" />
+<hkern u1="&#xf6;" u2="s" k="-1" />
+<hkern u1="&#xf6;" u2="t" k="3" />
+<hkern u1="&#xf6;" u2="v" k="-1" />
+<hkern u1="&#xf6;" u2="x" k="1" />
+<hkern u1="&#xf6;" u2="y" k="-1" />
+<hkern u1="&#xf6;" u2="z" k="4" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="8" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="5" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="10" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="10" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="5" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="5" />
+<hkern u1="&#xf5;" u2="&#x161;" k="-1" />
+<hkern u1="&#xf5;" u2="&#x2c;" k="5" />
+<hkern u1="&#xf5;" u2="." k="5" />
+<hkern u1="&#xf5;" u2="/" k="3" />
+<hkern u1="&#xf5;" u2=":" k="3" />
+<hkern u1="&#xf5;" u2=";" k="3" />
+<hkern u1="&#xf5;" u2="?" k="5" />
+<hkern u1="&#xf5;" u2="\" k="3" />
+<hkern u1="&#xf5;" u2="s" k="-1" />
+<hkern u1="&#xf5;" u2="t" k="3" />
+<hkern u1="&#xf5;" u2="v" k="-1" />
+<hkern u1="&#xf5;" u2="x" k="1" />
+<hkern u1="&#xf5;" u2="y" k="-1" />
+<hkern u1="&#xf5;" u2="z" k="4" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="8" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="5" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="10" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="10" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="5" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="5" />
+<hkern u1="&#xb0;" u2="4" k="16" />
+<hkern u1="&#xa3;" u2="1" k="-3" />
+<hkern u1="&#xa3;" u2="4" k="5" />
+<hkern u1="&#xdf;" u2="&#x161;" k="-1" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="5" />
+<hkern u1="&#xdf;" u2="." k="5" />
+<hkern u1="&#xdf;" u2="/" k="3" />
+<hkern u1="&#xdf;" u2=":" k="3" />
+<hkern u1="&#xdf;" u2=";" k="3" />
+<hkern u1="&#xdf;" u2="?" k="5" />
+<hkern u1="&#xdf;" u2="\" k="3" />
+<hkern u1="&#xdf;" u2="g" k="3" />
+<hkern u1="&#xdf;" u2="s" k="-1" />
+<hkern u1="&#xdf;" u2="t" k="3" />
+<hkern u1="&#xdf;" u2="v" k="-1" />
+<hkern u1="&#xdf;" u2="x" k="1" />
+<hkern u1="&#xdf;" u2="y" k="-1" />
+<hkern u1="&#xdf;" u2="z" k="4" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="8" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="5" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="10" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="10" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="5" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="5" />
+<hkern u1="&#xae;" u2="&#xdd;" k="8" />
+<hkern u1="&#xae;" u2="A" k="3" />
+<hkern u1="&#xae;" u2="T" k="8" />
+<hkern u1="&#xae;" u2="W" k="5" />
+<hkern u1="&#xae;" u2="Y" k="8" />
+<hkern u1="&#xae;" u2="&#xc4;" k="3" />
+<hkern u1="&#xae;" u2="&#xc5;" k="3" />
+<hkern u1="&#xae;" u2="&#xc6;" k="3" />
+<hkern u1="&#xae;" u2="&#xc0;" k="3" />
+<hkern u1="&#xae;" u2="&#xc3;" k="3" />
+<hkern u1="&#xae;" u2="&#x178;" k="8" />
+<hkern u1="&#xae;" u2="&#xc2;" k="3" />
+<hkern u1="&#xae;" u2="&#xc1;" k="3" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="8" />
+<hkern u1="&#xa9;" u2="A" k="3" />
+<hkern u1="&#xa9;" u2="T" k="8" />
+<hkern u1="&#xa9;" u2="W" k="5" />
+<hkern u1="&#xa9;" u2="Y" k="8" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="3" />
+<hkern u1="&#xa9;" u2="&#x178;" k="8" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="3" />
+<hkern u1="&#xc6;" u2="@" k="5" />
+<hkern u1="&#xc6;" u2="T" k="-4" />
+<hkern u1="&#xc6;" u2="a" k="6" />
+<hkern u1="&#xc6;" u2="c" k="6" />
+<hkern u1="&#xc6;" u2="d" k="6" />
+<hkern u1="&#xc6;" u2="e" k="6" />
+<hkern u1="&#xc6;" u2="f" k="3" />
+<hkern u1="&#xc6;" u2="g" k="1" />
+<hkern u1="&#xc6;" u2="o" k="6" />
+<hkern u1="&#xc6;" u2="q" k="6" />
+<hkern u1="&#xc6;" u2="v" k="5" />
+<hkern u1="&#xc6;" u2="y" k="5" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="6" />
+<hkern u1="&#xc6;" u2="&#xae;" k="5" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="5" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="5" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="6" />
+<hkern u1="&#xc6;" u2="&#x153;" k="6" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd8;" u2="." k="10" />
+<hkern u1="&#xd8;" u2="/" k="13" />
+<hkern u1="&#xd8;" u2="?" k="5" />
+<hkern u1="&#xd8;" u2="J" k="11" />
+<hkern u1="&#xd8;" u2="T" k="13" />
+<hkern u1="&#xd8;" u2="V" k="10" />
+<hkern u1="&#xd8;" u2="W" k="15" />
+<hkern u1="&#xd8;" u2="X" k="9" />
+<hkern u1="&#xd8;" u2="Y" k="11" />
+<hkern u1="&#xd8;" u2="Z" k="11" />
+<hkern u1="&#xd8;" u2="a" k="1" />
+<hkern u1="&#xd8;" u2="b" k="3" />
+<hkern u1="&#xd8;" u2="c" k="1" />
+<hkern u1="&#xd8;" u2="d" k="1" />
+<hkern u1="&#xd8;" u2="e" k="1" />
+<hkern u1="&#xd8;" u2="h" k="3" />
+<hkern u1="&#xd8;" u2="k" k="3" />
+<hkern u1="&#xd8;" u2="l" k="3" />
+<hkern u1="&#xd8;" u2="m" k="1" />
+<hkern u1="&#xd8;" u2="n" k="1" />
+<hkern u1="&#xd8;" u2="o" k="1" />
+<hkern u1="&#xd8;" u2="p" k="1" />
+<hkern u1="&#xd8;" u2="q" k="1" />
+<hkern u1="&#xd8;" u2="r" k="1" />
+<hkern u1="&#xd8;" u2="u" k="1" />
+<hkern u1="&#xd8;" u2="x" k="3" />
+<hkern u1="&#xd8;" u2="z" k="3" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd8;" u2="&#x153;" k="1" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd8;" u2="&#x178;" k="11" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd8;" u2="&#x131;" k="1" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="8" />
+<hkern u1="&#x3c0;" u2="A" k="3" />
+<hkern u1="&#x3c0;" u2="T" k="8" />
+<hkern u1="&#x3c0;" u2="W" k="5" />
+<hkern u1="&#x3c0;" u2="Y" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="3" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="3" />
+<hkern u1="&#xe6;" u2="v" k="3" />
+<hkern u1="&#xe6;" u2="x" k="4" />
+<hkern u1="&#xe6;" u2="y" k="3" />
+<hkern u1="&#xf8;" u2="&#x161;" k="-1" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="5" />
+<hkern u1="&#xf8;" u2="." k="5" />
+<hkern u1="&#xf8;" u2="/" k="3" />
+<hkern u1="&#xf8;" u2=":" k="3" />
+<hkern u1="&#xf8;" u2=";" k="3" />
+<hkern u1="&#xf8;" u2="?" k="5" />
+<hkern u1="&#xf8;" u2="\" k="3" />
+<hkern u1="&#xf8;" u2="s" k="-1" />
+<hkern u1="&#xf8;" u2="t" k="3" />
+<hkern u1="&#xf8;" u2="v" k="-1" />
+<hkern u1="&#xf8;" u2="x" k="1" />
+<hkern u1="&#xf8;" u2="y" k="-1" />
+<hkern u1="&#xf8;" u2="z" k="4" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="8" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="5" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="10" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="10" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="5" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="5" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="10" />
+<hkern u1="&#xbf;" u2="1" k="10" />
+<hkern u1="&#xbf;" u2="3" k="-3" />
+<hkern u1="&#xbf;" u2="7" k="8" />
+<hkern u1="&#xbf;" u2="C" k="5" />
+<hkern u1="&#xbf;" u2="G" k="5" />
+<hkern u1="&#xbf;" u2="O" k="5" />
+<hkern u1="&#xbf;" u2="Q" k="5" />
+<hkern u1="&#xbf;" u2="T" k="15" />
+<hkern u1="&#xbf;" u2="V" k="13" />
+<hkern u1="&#xbf;" u2="W" k="8" />
+<hkern u1="&#xbf;" u2="Y" k="10" />
+<hkern u1="&#xbf;" u2="v" k="8" />
+<hkern u1="&#xbf;" u2="w" k="5" />
+<hkern u1="&#xbf;" u2="x" k="3" />
+<hkern u1="&#xbf;" u2="y" k="8" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="5" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="5" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="5" />
+<hkern u1="&#xbf;" u2="&#x152;" k="5" />
+<hkern u1="&#xbf;" u2="&#x178;" k="10" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="5" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="5" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="5" />
+<hkern u1="&#x192;" u2="1" k="-8" />
+<hkern u1="&#xab;" u2="&#xdd;" k="10" />
+<hkern u1="&#xab;" u2="T" k="10" />
+<hkern u1="&#xab;" u2="V" k="5" />
+<hkern u1="&#xab;" u2="W" k="5" />
+<hkern u1="&#xab;" u2="Y" k="10" />
+<hkern u1="&#xab;" u2="&#x178;" k="10" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="13" />
+<hkern u1="&#xbb;" u2="T" k="28" />
+<hkern u1="&#xbb;" u2="V" k="13" />
+<hkern u1="&#xbb;" u2="W" k="10" />
+<hkern u1="&#xbb;" u2="Y" k="13" />
+<hkern u1="&#xbb;" u2="x" k="13" />
+<hkern u1="&#xbb;" u2="z" k="13" />
+<hkern u1="&#xbb;" u2="&#x178;" k="13" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="13" />
+<hkern u1="&#x2026;" u2="0" k="10" />
+<hkern u1="&#x2026;" u2="1" k="15" />
+<hkern u1="&#x2026;" u2="4" k="13" />
+<hkern u1="&#x2026;" u2="6" k="8" />
+<hkern u1="&#x2026;" u2="8" k="4" />
+<hkern u1="&#x2026;" u2="9" k="3" />
+<hkern u1="&#x2026;" u2="A" k="-8" />
+<hkern u1="&#x2026;" u2="C" k="10" />
+<hkern u1="&#x2026;" u2="G" k="10" />
+<hkern u1="&#x2026;" u2="O" k="10" />
+<hkern u1="&#x2026;" u2="Q" k="10" />
+<hkern u1="&#x2026;" u2="T" k="15" />
+<hkern u1="&#x2026;" u2="U" k="3" />
+<hkern u1="&#x2026;" u2="V" k="15" />
+<hkern u1="&#x2026;" u2="W" k="5" />
+<hkern u1="&#x2026;" u2="Y" k="13" />
+<hkern u1="&#x2026;" u2="a" k="5" />
+<hkern u1="&#x2026;" u2="c" k="5" />
+<hkern u1="&#x2026;" u2="d" k="5" />
+<hkern u1="&#x2026;" u2="e" k="5" />
+<hkern u1="&#x2026;" u2="m" k="5" />
+<hkern u1="&#x2026;" u2="n" k="5" />
+<hkern u1="&#x2026;" u2="o" k="5" />
+<hkern u1="&#x2026;" u2="p" k="5" />
+<hkern u1="&#x2026;" u2="q" k="5" />
+<hkern u1="&#x2026;" u2="r" k="5" />
+<hkern u1="&#x2026;" u2="t" k="10" />
+<hkern u1="&#x2026;" u2="u" k="5" />
+<hkern u1="&#x2026;" u2="v" k="8" />
+<hkern u1="&#x2026;" u2="w" k="3" />
+<hkern u1="&#x2026;" u2="y" k="8" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="10" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="3" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="5" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="10" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="5" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="10" />
+<hkern u1="&#x2026;" u2="&#x152;" k="10" />
+<hkern u1="&#x2026;" u2="&#x153;" k="5" />
+<hkern u1="&#x2026;" u2="&#x178;" k="13" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-8" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="10" />
+<hkern u1="&#x2026;" u2="&#xda;" k="3" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="3" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="3" />
+<hkern u1="&#x2026;" u2="&#x131;" k="5" />
+<hkern u1="&#xc0;" u2="&#x160;" k="-1" />
+<hkern u1="&#xc0;" u2="&#xdd;" k="18" />
+<hkern u1="&#xc0;" u2="*" k="15" />
+<hkern u1="&#xc0;" u2="&#x2c;" k="-8" />
+<hkern u1="&#xc0;" u2="-" k="-3" />
+<hkern u1="&#xc0;" u2="." k="-8" />
+<hkern u1="&#xc0;" u2="@" k="3" />
+<hkern u1="&#xc0;" u2="A" k="-3" />
+<hkern u1="&#xc0;" u2="C" k="10" />
+<hkern u1="&#xc0;" u2="G" k="10" />
+<hkern u1="&#xc0;" u2="J" k="3" />
+<hkern u1="&#xc0;" u2="O" k="10" />
+<hkern u1="&#xc0;" u2="Q" k="10" />
+<hkern u1="&#xc0;" u2="S" k="-1" />
+<hkern u1="&#xc0;" u2="T" k="23" />
+<hkern u1="&#xc0;" u2="V" k="18" />
+<hkern u1="&#xc0;" u2="W" k="18" />
+<hkern u1="&#xc0;" u2="Y" k="18" />
+<hkern u1="&#xc0;" u2="a" k="1" />
+<hkern u1="&#xc0;" u2="c" k="1" />
+<hkern u1="&#xc0;" u2="d" k="1" />
+<hkern u1="&#xc0;" u2="e" k="1" />
+<hkern u1="&#xc0;" u2="m" k="1" />
+<hkern u1="&#xc0;" u2="n" k="1" />
+<hkern u1="&#xc0;" u2="o" k="1" />
+<hkern u1="&#xc0;" u2="p" k="1" />
+<hkern u1="&#xc0;" u2="q" k="1" />
+<hkern u1="&#xc0;" u2="r" k="1" />
+<hkern u1="&#xc0;" u2="t" k="1" />
+<hkern u1="&#xc0;" u2="u" k="1" />
+<hkern u1="&#xc0;" u2="v" k="8" />
+<hkern u1="&#xc0;" u2="w" k="3" />
+<hkern u1="&#xc0;" u2="x" k="-5" />
+<hkern u1="&#xc0;" u2="y" k="8" />
+<hkern u1="&#xc0;" u2="z" k="-3" />
+<hkern u1="&#xc0;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xd6;" k="10" />
+<hkern u1="&#xc0;" u2="&#xe7;" k="1" />
+<hkern u1="&#xc0;" u2="&#xae;" k="3" />
+<hkern u1="&#xc0;" u2="&#xa9;" k="3" />
+<hkern u1="&#xc0;" u2="&#x2122;" k="18" />
+<hkern u1="&#xc0;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xd8;" k="10" />
+<hkern u1="&#xc0;" u2="&#x3c0;" k="3" />
+<hkern u1="&#xc0;" u2="&#xe6;" k="1" />
+<hkern u1="&#xc0;" u2="&#x2026;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="10" />
+<hkern u1="&#xc0;" u2="&#x152;" k="10" />
+<hkern u1="&#xc0;" u2="&#x153;" k="1" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-3" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-3" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="15" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="8" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="15" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="8" />
+<hkern u1="&#xc0;" u2="&#x178;" k="18" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-8" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="10" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="10" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="10" />
+<hkern u1="&#xc0;" u2="&#x131;" k="1" />
+<hkern u1="&#xc3;" u2="&#x160;" k="-1" />
+<hkern u1="&#xc3;" u2="&#xdd;" k="18" />
+<hkern u1="&#xc3;" u2="*" k="15" />
+<hkern u1="&#xc3;" u2="&#x2c;" k="-8" />
+<hkern u1="&#xc3;" u2="-" k="-3" />
+<hkern u1="&#xc3;" u2="." k="-8" />
+<hkern u1="&#xc3;" u2="@" k="3" />
+<hkern u1="&#xc3;" u2="A" k="-3" />
+<hkern u1="&#xc3;" u2="C" k="10" />
+<hkern u1="&#xc3;" u2="G" k="10" />
+<hkern u1="&#xc3;" u2="J" k="3" />
+<hkern u1="&#xc3;" u2="O" k="10" />
+<hkern u1="&#xc3;" u2="Q" k="10" />
+<hkern u1="&#xc3;" u2="S" k="-1" />
+<hkern u1="&#xc3;" u2="T" k="23" />
+<hkern u1="&#xc3;" u2="V" k="18" />
+<hkern u1="&#xc3;" u2="W" k="18" />
+<hkern u1="&#xc3;" u2="Y" k="18" />
+<hkern u1="&#xc3;" u2="a" k="1" />
+<hkern u1="&#xc3;" u2="c" k="1" />
+<hkern u1="&#xc3;" u2="d" k="1" />
+<hkern u1="&#xc3;" u2="e" k="1" />
+<hkern u1="&#xc3;" u2="m" k="1" />
+<hkern u1="&#xc3;" u2="n" k="1" />
+<hkern u1="&#xc3;" u2="o" k="1" />
+<hkern u1="&#xc3;" u2="p" k="1" />
+<hkern u1="&#xc3;" u2="q" k="1" />
+<hkern u1="&#xc3;" u2="r" k="1" />
+<hkern u1="&#xc3;" u2="t" k="1" />
+<hkern u1="&#xc3;" u2="u" k="1" />
+<hkern u1="&#xc3;" u2="v" k="8" />
+<hkern u1="&#xc3;" u2="w" k="3" />
+<hkern u1="&#xc3;" u2="x" k="-5" />
+<hkern u1="&#xc3;" u2="y" k="8" />
+<hkern u1="&#xc3;" u2="z" k="-3" />
+<hkern u1="&#xc3;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xd6;" k="10" />
+<hkern u1="&#xc3;" u2="&#xe7;" k="1" />
+<hkern u1="&#xc3;" u2="&#xae;" k="3" />
+<hkern u1="&#xc3;" u2="&#xa9;" k="3" />
+<hkern u1="&#xc3;" u2="&#x2122;" k="18" />
+<hkern u1="&#xc3;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xd8;" k="10" />
+<hkern u1="&#xc3;" u2="&#x3c0;" k="3" />
+<hkern u1="&#xc3;" u2="&#xe6;" k="1" />
+<hkern u1="&#xc3;" u2="&#x2026;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="10" />
+<hkern u1="&#xc3;" u2="&#x152;" k="10" />
+<hkern u1="&#xc3;" u2="&#x153;" k="1" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-3" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-3" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="15" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="8" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="15" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="8" />
+<hkern u1="&#xc3;" u2="&#x178;" k="18" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-8" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="10" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="10" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="10" />
+<hkern u1="&#xc3;" u2="&#x131;" k="1" />
+<hkern u1="&#xd5;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd5;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd5;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd5;" u2="." k="10" />
+<hkern u1="&#xd5;" u2="/" k="13" />
+<hkern u1="&#xd5;" u2="?" k="5" />
+<hkern u1="&#xd5;" u2="J" k="11" />
+<hkern u1="&#xd5;" u2="T" k="13" />
+<hkern u1="&#xd5;" u2="V" k="10" />
+<hkern u1="&#xd5;" u2="W" k="15" />
+<hkern u1="&#xd5;" u2="X" k="9" />
+<hkern u1="&#xd5;" u2="Y" k="11" />
+<hkern u1="&#xd5;" u2="Z" k="11" />
+<hkern u1="&#xd5;" u2="a" k="1" />
+<hkern u1="&#xd5;" u2="b" k="3" />
+<hkern u1="&#xd5;" u2="c" k="1" />
+<hkern u1="&#xd5;" u2="d" k="1" />
+<hkern u1="&#xd5;" u2="e" k="1" />
+<hkern u1="&#xd5;" u2="h" k="3" />
+<hkern u1="&#xd5;" u2="k" k="3" />
+<hkern u1="&#xd5;" u2="l" k="3" />
+<hkern u1="&#xd5;" u2="m" k="1" />
+<hkern u1="&#xd5;" u2="n" k="1" />
+<hkern u1="&#xd5;" u2="o" k="1" />
+<hkern u1="&#xd5;" u2="p" k="1" />
+<hkern u1="&#xd5;" u2="q" k="1" />
+<hkern u1="&#xd5;" u2="r" k="1" />
+<hkern u1="&#xd5;" u2="u" k="1" />
+<hkern u1="&#xd5;" u2="x" k="3" />
+<hkern u1="&#xd5;" u2="z" k="3" />
+<hkern u1="&#xd5;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd5;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd5;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd5;" u2="&#x153;" k="1" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd5;" u2="&#x178;" k="11" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd5;" u2="&#x131;" k="1" />
+<hkern u1="&#x152;" u2="@" k="5" />
+<hkern u1="&#x152;" u2="T" k="-4" />
+<hkern u1="&#x152;" u2="a" k="6" />
+<hkern u1="&#x152;" u2="c" k="6" />
+<hkern u1="&#x152;" u2="d" k="6" />
+<hkern u1="&#x152;" u2="e" k="6" />
+<hkern u1="&#x152;" u2="f" k="3" />
+<hkern u1="&#x152;" u2="g" k="1" />
+<hkern u1="&#x152;" u2="o" k="6" />
+<hkern u1="&#x152;" u2="q" k="6" />
+<hkern u1="&#x152;" u2="v" k="5" />
+<hkern u1="&#x152;" u2="y" k="5" />
+<hkern u1="&#x152;" u2="&#xe7;" k="6" />
+<hkern u1="&#x152;" u2="&#xae;" k="5" />
+<hkern u1="&#x152;" u2="&#xa9;" k="5" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="5" />
+<hkern u1="&#x152;" u2="&#xe6;" k="6" />
+<hkern u1="&#x152;" u2="&#x153;" k="6" />
+<hkern u1="&#x153;" u2="v" k="3" />
+<hkern u1="&#x153;" u2="x" k="4" />
+<hkern u1="&#x153;" u2="y" k="3" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="13" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="3" />
+<hkern u1="&#x2013;" u2="1" k="5" />
+<hkern u1="&#x2013;" u2="7" k="8" />
+<hkern u1="&#x2013;" u2="A" k="-3" />
+<hkern u1="&#x2013;" u2="T" k="18" />
+<hkern u1="&#x2013;" u2="V" k="13" />
+<hkern u1="&#x2013;" u2="W" k="10" />
+<hkern u1="&#x2013;" u2="X" k="8" />
+<hkern u1="&#x2013;" u2="Y" k="13" />
+<hkern u1="&#x2013;" u2="Z" k="3" />
+<hkern u1="&#x2013;" u2="a" k="3" />
+<hkern u1="&#x2013;" u2="c" k="3" />
+<hkern u1="&#x2013;" u2="d" k="3" />
+<hkern u1="&#x2013;" u2="e" k="3" />
+<hkern u1="&#x2013;" u2="o" k="3" />
+<hkern u1="&#x2013;" u2="q" k="3" />
+<hkern u1="&#x2013;" u2="v" k="3" />
+<hkern u1="&#x2013;" u2="x" k="8" />
+<hkern u1="&#x2013;" u2="y" k="3" />
+<hkern u1="&#x2013;" u2="z" k="8" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="3" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="3" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-3" />
+<hkern u1="&#x2013;" u2="&#x153;" k="3" />
+<hkern u1="&#x2013;" u2="&#x178;" k="13" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="13" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="3" />
+<hkern u1="&#x2014;" u2="1" k="5" />
+<hkern u1="&#x2014;" u2="7" k="8" />
+<hkern u1="&#x2014;" u2="A" k="-3" />
+<hkern u1="&#x2014;" u2="T" k="18" />
+<hkern u1="&#x2014;" u2="V" k="13" />
+<hkern u1="&#x2014;" u2="W" k="10" />
+<hkern u1="&#x2014;" u2="X" k="8" />
+<hkern u1="&#x2014;" u2="Y" k="13" />
+<hkern u1="&#x2014;" u2="Z" k="3" />
+<hkern u1="&#x2014;" u2="a" k="3" />
+<hkern u1="&#x2014;" u2="c" k="3" />
+<hkern u1="&#x2014;" u2="d" k="3" />
+<hkern u1="&#x2014;" u2="e" k="3" />
+<hkern u1="&#x2014;" u2="o" k="3" />
+<hkern u1="&#x2014;" u2="q" k="3" />
+<hkern u1="&#x2014;" u2="v" k="3" />
+<hkern u1="&#x2014;" u2="x" k="8" />
+<hkern u1="&#x2014;" u2="y" k="3" />
+<hkern u1="&#x2014;" u2="z" k="8" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="3" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="3" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-3" />
+<hkern u1="&#x2014;" u2="&#x153;" k="3" />
+<hkern u1="&#x2014;" u2="&#x178;" k="13" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-3" />
+<hkern u1="&#x201c;" u2="&#x161;" k="8" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-8" />
+<hkern u1="&#x201c;" u2="A" k="10" />
+<hkern u1="&#x201c;" u2="J" k="28" />
+<hkern u1="&#x201c;" u2="T" k="-3" />
+<hkern u1="&#x201c;" u2="V" k="-5" />
+<hkern u1="&#x201c;" u2="W" k="-5" />
+<hkern u1="&#x201c;" u2="X" k="-5" />
+<hkern u1="&#x201c;" u2="Y" k="-8" />
+<hkern u1="&#x201c;" u2="a" k="8" />
+<hkern u1="&#x201c;" u2="c" k="8" />
+<hkern u1="&#x201c;" u2="d" k="8" />
+<hkern u1="&#x201c;" u2="e" k="8" />
+<hkern u1="&#x201c;" u2="g" k="10" />
+<hkern u1="&#x201c;" u2="m" k="3" />
+<hkern u1="&#x201c;" u2="n" k="3" />
+<hkern u1="&#x201c;" u2="o" k="8" />
+<hkern u1="&#x201c;" u2="p" k="3" />
+<hkern u1="&#x201c;" u2="q" k="8" />
+<hkern u1="&#x201c;" u2="r" k="3" />
+<hkern u1="&#x201c;" u2="s" k="8" />
+<hkern u1="&#x201c;" u2="u" k="3" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="10" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="10" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="8" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="10" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="8" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="10" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="10" />
+<hkern u1="&#x201c;" u2="&#x153;" k="8" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-8" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="10" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="10" />
+<hkern u1="&#x201c;" u2="&#x131;" k="3" />
+<hkern u1="&#x201d;" u2="&#x160;" k="3" />
+<hkern u1="&#x201d;" u2="C" k="10" />
+<hkern u1="&#x201d;" u2="G" k="10" />
+<hkern u1="&#x201d;" u2="J" k="30" />
+<hkern u1="&#x201d;" u2="O" k="10" />
+<hkern u1="&#x201d;" u2="Q" k="10" />
+<hkern u1="&#x201d;" u2="S" k="3" />
+<hkern u1="&#x201d;" u2="a" k="13" />
+<hkern u1="&#x201d;" u2="c" k="13" />
+<hkern u1="&#x201d;" u2="d" k="13" />
+<hkern u1="&#x201d;" u2="e" k="13" />
+<hkern u1="&#x201d;" u2="o" k="13" />
+<hkern u1="&#x201d;" u2="q" k="13" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="10" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="13" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="10" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="13" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="10" />
+<hkern u1="&#x201d;" u2="&#x152;" k="10" />
+<hkern u1="&#x201d;" u2="&#x153;" k="13" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="10" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="10" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="10" />
+<hkern u1="&#x2018;" u2="&#x161;" k="8" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-8" />
+<hkern u1="&#x2018;" u2="A" k="10" />
+<hkern u1="&#x2018;" u2="J" k="28" />
+<hkern u1="&#x2018;" u2="T" k="-3" />
+<hkern u1="&#x2018;" u2="V" k="-5" />
+<hkern u1="&#x2018;" u2="W" k="-5" />
+<hkern u1="&#x2018;" u2="X" k="-5" />
+<hkern u1="&#x2018;" u2="Y" k="-8" />
+<hkern u1="&#x2018;" u2="a" k="8" />
+<hkern u1="&#x2018;" u2="c" k="8" />
+<hkern u1="&#x2018;" u2="d" k="8" />
+<hkern u1="&#x2018;" u2="e" k="8" />
+<hkern u1="&#x2018;" u2="g" k="10" />
+<hkern u1="&#x2018;" u2="m" k="3" />
+<hkern u1="&#x2018;" u2="n" k="3" />
+<hkern u1="&#x2018;" u2="o" k="8" />
+<hkern u1="&#x2018;" u2="p" k="3" />
+<hkern u1="&#x2018;" u2="q" k="8" />
+<hkern u1="&#x2018;" u2="r" k="3" />
+<hkern u1="&#x2018;" u2="s" k="8" />
+<hkern u1="&#x2018;" u2="u" k="3" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="10" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="10" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="8" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="10" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="8" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="10" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="10" />
+<hkern u1="&#x2018;" u2="&#x153;" k="8" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-8" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="10" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="10" />
+<hkern u1="&#x2018;" u2="&#x131;" k="3" />
+<hkern u1="&#x2019;" u2="&#x160;" k="3" />
+<hkern u1="&#x2019;" u2="C" k="10" />
+<hkern u1="&#x2019;" u2="G" k="10" />
+<hkern u1="&#x2019;" u2="J" k="30" />
+<hkern u1="&#x2019;" u2="O" k="10" />
+<hkern u1="&#x2019;" u2="Q" k="10" />
+<hkern u1="&#x2019;" u2="S" k="3" />
+<hkern u1="&#x2019;" u2="a" k="13" />
+<hkern u1="&#x2019;" u2="c" k="13" />
+<hkern u1="&#x2019;" u2="d" k="13" />
+<hkern u1="&#x2019;" u2="e" k="13" />
+<hkern u1="&#x2019;" u2="o" k="13" />
+<hkern u1="&#x2019;" u2="q" k="13" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="10" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="13" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="10" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="13" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="10" />
+<hkern u1="&#x2019;" u2="&#x152;" k="10" />
+<hkern u1="&#x2019;" u2="&#x153;" k="13" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="10" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="10" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="10" />
+<hkern u1="&#x178;" u2="&#x160;" k="-8" />
+<hkern u1="&#x178;" u2="&#x161;" k="13" />
+<hkern u1="&#x178;" u2="&amp;" k="8" />
+<hkern u1="&#x178;" u2=")" k="-13" />
+<hkern u1="&#x178;" u2="&#x2c;" k="13" />
+<hkern u1="&#x178;" u2="-" k="13" />
+<hkern u1="&#x178;" u2="." k="13" />
+<hkern u1="&#x178;" u2=":" k="8" />
+<hkern u1="&#x178;" u2=";" k="8" />
+<hkern u1="&#x178;" u2="@" k="8" />
+<hkern u1="&#x178;" u2="A" k="8" />
+<hkern u1="&#x178;" u2="C" k="1" />
+<hkern u1="&#x178;" u2="G" k="1" />
+<hkern u1="&#x178;" u2="J" k="18" />
+<hkern u1="&#x178;" u2="O" k="1" />
+<hkern u1="&#x178;" u2="Q" k="1" />
+<hkern u1="&#x178;" u2="S" k="-8" />
+<hkern u1="&#x178;" u2="T" k="-5" />
+<hkern u1="&#x178;" u2="V" k="-8" />
+<hkern u1="&#x178;" u2="X" k="-5" />
+<hkern u1="&#x178;" u2="]" k="-13" />
+<hkern u1="&#x178;" u2="a" k="14" />
+<hkern u1="&#x178;" u2="c" k="14" />
+<hkern u1="&#x178;" u2="d" k="14" />
+<hkern u1="&#x178;" u2="e" k="14" />
+<hkern u1="&#x178;" u2="f" k="3" />
+<hkern u1="&#x178;" u2="g" k="10" />
+<hkern u1="&#x178;" u2="m" k="10" />
+<hkern u1="&#x178;" u2="n" k="10" />
+<hkern u1="&#x178;" u2="o" k="14" />
+<hkern u1="&#x178;" u2="p" k="10" />
+<hkern u1="&#x178;" u2="q" k="14" />
+<hkern u1="&#x178;" u2="r" k="10" />
+<hkern u1="&#x178;" u2="s" k="13" />
+<hkern u1="&#x178;" u2="t" k="5" />
+<hkern u1="&#x178;" u2="u" k="10" />
+<hkern u1="&#x178;" u2="v" k="5" />
+<hkern u1="&#x178;" u2="w" k="3" />
+<hkern u1="&#x178;" u2="x" k="5" />
+<hkern u1="&#x178;" u2="y" k="5" />
+<hkern u1="&#x178;" u2="z" k="8" />
+<hkern u1="&#x178;" u2="}" k="-13" />
+<hkern u1="&#x178;" u2="&#xc4;" k="8" />
+<hkern u1="&#x178;" u2="&#xc5;" k="8" />
+<hkern u1="&#x178;" u2="&#xd6;" k="1" />
+<hkern u1="&#x178;" u2="&#xe7;" k="14" />
+<hkern u1="&#x178;" u2="&#xae;" k="8" />
+<hkern u1="&#x178;" u2="&#xa9;" k="8" />
+<hkern u1="&#x178;" u2="&#xc6;" k="8" />
+<hkern u1="&#x178;" u2="&#xd8;" k="1" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="8" />
+<hkern u1="&#x178;" u2="&#xe6;" k="14" />
+<hkern u1="&#x178;" u2="&#xab;" k="15" />
+<hkern u1="&#x178;" u2="&#xbb;" k="10" />
+<hkern u1="&#x178;" u2="&#x2026;" k="13" />
+<hkern u1="&#x178;" u2="&#xc0;" k="8" />
+<hkern u1="&#x178;" u2="&#xc3;" k="8" />
+<hkern u1="&#x178;" u2="&#xd5;" k="1" />
+<hkern u1="&#x178;" u2="&#x152;" k="1" />
+<hkern u1="&#x178;" u2="&#x153;" k="14" />
+<hkern u1="&#x178;" u2="&#x2013;" k="13" />
+<hkern u1="&#x178;" u2="&#x2014;" k="13" />
+<hkern u1="&#x178;" u2="&#x2039;" k="15" />
+<hkern u1="&#x178;" u2="&#x203a;" k="10" />
+<hkern u1="&#x178;" u2="&#x201a;" k="13" />
+<hkern u1="&#x178;" u2="&#x201e;" k="13" />
+<hkern u1="&#x178;" u2="&#xc2;" k="8" />
+<hkern u1="&#x178;" u2="&#xc1;" k="8" />
+<hkern u1="&#x178;" u2="&#xd3;" k="1" />
+<hkern u1="&#x178;" u2="&#xd4;" k="1" />
+<hkern u1="&#x178;" u2="&#xd2;" k="1" />
+<hkern u1="&#x178;" u2="&#x131;" k="10" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="10" />
+<hkern u1="&#x2039;" u2="T" k="10" />
+<hkern u1="&#x2039;" u2="V" k="5" />
+<hkern u1="&#x2039;" u2="W" k="5" />
+<hkern u1="&#x2039;" u2="Y" k="10" />
+<hkern u1="&#x2039;" u2="&#x178;" k="10" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="13" />
+<hkern u1="&#x203a;" u2="T" k="28" />
+<hkern u1="&#x203a;" u2="V" k="13" />
+<hkern u1="&#x203a;" u2="W" k="10" />
+<hkern u1="&#x203a;" u2="Y" k="13" />
+<hkern u1="&#x203a;" u2="x" k="13" />
+<hkern u1="&#x203a;" u2="z" k="13" />
+<hkern u1="&#x203a;" u2="&#x178;" k="13" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="13" />
+<hkern u1="&#x201a;" u2="0" k="10" />
+<hkern u1="&#x201a;" u2="1" k="15" />
+<hkern u1="&#x201a;" u2="4" k="13" />
+<hkern u1="&#x201a;" u2="6" k="8" />
+<hkern u1="&#x201a;" u2="8" k="4" />
+<hkern u1="&#x201a;" u2="9" k="3" />
+<hkern u1="&#x201a;" u2="A" k="-8" />
+<hkern u1="&#x201a;" u2="C" k="10" />
+<hkern u1="&#x201a;" u2="G" k="10" />
+<hkern u1="&#x201a;" u2="O" k="10" />
+<hkern u1="&#x201a;" u2="Q" k="10" />
+<hkern u1="&#x201a;" u2="T" k="15" />
+<hkern u1="&#x201a;" u2="U" k="3" />
+<hkern u1="&#x201a;" u2="V" k="15" />
+<hkern u1="&#x201a;" u2="W" k="5" />
+<hkern u1="&#x201a;" u2="Y" k="13" />
+<hkern u1="&#x201a;" u2="a" k="5" />
+<hkern u1="&#x201a;" u2="c" k="5" />
+<hkern u1="&#x201a;" u2="d" k="5" />
+<hkern u1="&#x201a;" u2="e" k="5" />
+<hkern u1="&#x201a;" u2="j" k="-8" />
+<hkern u1="&#x201a;" u2="m" k="5" />
+<hkern u1="&#x201a;" u2="n" k="5" />
+<hkern u1="&#x201a;" u2="o" k="5" />
+<hkern u1="&#x201a;" u2="p" k="5" />
+<hkern u1="&#x201a;" u2="q" k="5" />
+<hkern u1="&#x201a;" u2="r" k="5" />
+<hkern u1="&#x201a;" u2="t" k="10" />
+<hkern u1="&#x201a;" u2="u" k="5" />
+<hkern u1="&#x201a;" u2="v" k="8" />
+<hkern u1="&#x201a;" u2="w" k="3" />
+<hkern u1="&#x201a;" u2="y" k="8" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="10" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="3" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="5" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="10" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="5" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="10" />
+<hkern u1="&#x201a;" u2="&#x152;" k="10" />
+<hkern u1="&#x201a;" u2="&#x153;" k="5" />
+<hkern u1="&#x201a;" u2="&#x178;" k="13" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-8" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="10" />
+<hkern u1="&#x201a;" u2="&#xda;" k="3" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="3" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="3" />
+<hkern u1="&#x201a;" u2="&#x131;" k="5" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="13" />
+<hkern u1="&#x201e;" u2="0" k="10" />
+<hkern u1="&#x201e;" u2="1" k="15" />
+<hkern u1="&#x201e;" u2="4" k="13" />
+<hkern u1="&#x201e;" u2="6" k="8" />
+<hkern u1="&#x201e;" u2="8" k="4" />
+<hkern u1="&#x201e;" u2="9" k="3" />
+<hkern u1="&#x201e;" u2="A" k="-8" />
+<hkern u1="&#x201e;" u2="C" k="10" />
+<hkern u1="&#x201e;" u2="G" k="10" />
+<hkern u1="&#x201e;" u2="O" k="10" />
+<hkern u1="&#x201e;" u2="Q" k="10" />
+<hkern u1="&#x201e;" u2="T" k="15" />
+<hkern u1="&#x201e;" u2="U" k="3" />
+<hkern u1="&#x201e;" u2="V" k="15" />
+<hkern u1="&#x201e;" u2="W" k="5" />
+<hkern u1="&#x201e;" u2="Y" k="13" />
+<hkern u1="&#x201e;" u2="a" k="5" />
+<hkern u1="&#x201e;" u2="c" k="5" />
+<hkern u1="&#x201e;" u2="d" k="5" />
+<hkern u1="&#x201e;" u2="e" k="5" />
+<hkern u1="&#x201e;" u2="j" k="-8" />
+<hkern u1="&#x201e;" u2="m" k="5" />
+<hkern u1="&#x201e;" u2="n" k="5" />
+<hkern u1="&#x201e;" u2="o" k="5" />
+<hkern u1="&#x201e;" u2="p" k="5" />
+<hkern u1="&#x201e;" u2="q" k="5" />
+<hkern u1="&#x201e;" u2="r" k="5" />
+<hkern u1="&#x201e;" u2="t" k="10" />
+<hkern u1="&#x201e;" u2="u" k="5" />
+<hkern u1="&#x201e;" u2="v" k="8" />
+<hkern u1="&#x201e;" u2="w" k="3" />
+<hkern u1="&#x201e;" u2="y" k="8" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="3" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="5" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="10" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="5" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="10" />
+<hkern u1="&#x201e;" u2="&#x152;" k="10" />
+<hkern u1="&#x201e;" u2="&#x153;" k="5" />
+<hkern u1="&#x201e;" u2="&#x178;" k="13" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-8" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="10" />
+<hkern u1="&#x201e;" u2="&#xda;" k="3" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="3" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="3" />
+<hkern u1="&#x201e;" u2="&#x131;" k="5" />
+<hkern u1="&#xc2;" u2="&#x160;" k="-1" />
+<hkern u1="&#xc2;" u2="&#xdd;" k="18" />
+<hkern u1="&#xc2;" u2="*" k="15" />
+<hkern u1="&#xc2;" u2="&#x2c;" k="-8" />
+<hkern u1="&#xc2;" u2="-" k="-3" />
+<hkern u1="&#xc2;" u2="." k="-8" />
+<hkern u1="&#xc2;" u2="@" k="3" />
+<hkern u1="&#xc2;" u2="A" k="-3" />
+<hkern u1="&#xc2;" u2="C" k="10" />
+<hkern u1="&#xc2;" u2="G" k="10" />
+<hkern u1="&#xc2;" u2="J" k="3" />
+<hkern u1="&#xc2;" u2="O" k="10" />
+<hkern u1="&#xc2;" u2="Q" k="10" />
+<hkern u1="&#xc2;" u2="S" k="-1" />
+<hkern u1="&#xc2;" u2="T" k="23" />
+<hkern u1="&#xc2;" u2="V" k="18" />
+<hkern u1="&#xc2;" u2="W" k="18" />
+<hkern u1="&#xc2;" u2="Y" k="18" />
+<hkern u1="&#xc2;" u2="a" k="1" />
+<hkern u1="&#xc2;" u2="c" k="1" />
+<hkern u1="&#xc2;" u2="d" k="1" />
+<hkern u1="&#xc2;" u2="e" k="1" />
+<hkern u1="&#xc2;" u2="m" k="1" />
+<hkern u1="&#xc2;" u2="n" k="1" />
+<hkern u1="&#xc2;" u2="o" k="1" />
+<hkern u1="&#xc2;" u2="p" k="1" />
+<hkern u1="&#xc2;" u2="q" k="1" />
+<hkern u1="&#xc2;" u2="r" k="1" />
+<hkern u1="&#xc2;" u2="t" k="1" />
+<hkern u1="&#xc2;" u2="u" k="1" />
+<hkern u1="&#xc2;" u2="v" k="8" />
+<hkern u1="&#xc2;" u2="w" k="3" />
+<hkern u1="&#xc2;" u2="x" k="-5" />
+<hkern u1="&#xc2;" u2="y" k="8" />
+<hkern u1="&#xc2;" u2="z" k="-3" />
+<hkern u1="&#xc2;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xd6;" k="10" />
+<hkern u1="&#xc2;" u2="&#xe7;" k="1" />
+<hkern u1="&#xc2;" u2="&#xae;" k="3" />
+<hkern u1="&#xc2;" u2="&#xa9;" k="3" />
+<hkern u1="&#xc2;" u2="&#x2122;" k="18" />
+<hkern u1="&#xc2;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xd8;" k="10" />
+<hkern u1="&#xc2;" u2="&#x3c0;" k="3" />
+<hkern u1="&#xc2;" u2="&#xe6;" k="1" />
+<hkern u1="&#xc2;" u2="&#x2026;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xd5;" k="10" />
+<hkern u1="&#xc2;" u2="&#x152;" k="10" />
+<hkern u1="&#xc2;" u2="&#x153;" k="1" />
+<hkern u1="&#xc2;" u2="&#x2013;" k="-3" />
+<hkern u1="&#xc2;" u2="&#x2014;" k="-3" />
+<hkern u1="&#xc2;" u2="&#x201c;" k="15" />
+<hkern u1="&#xc2;" u2="&#x201d;" k="8" />
+<hkern u1="&#xc2;" u2="&#x2018;" k="15" />
+<hkern u1="&#xc2;" u2="&#x2019;" k="8" />
+<hkern u1="&#xc2;" u2="&#x178;" k="18" />
+<hkern u1="&#xc2;" u2="&#x201a;" k="-8" />
+<hkern u1="&#xc2;" u2="&#x201e;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="10" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="10" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="10" />
+<hkern u1="&#xc2;" u2="&#x131;" k="1" />
+<hkern u1="&#xca;" u2="@" k="5" />
+<hkern u1="&#xca;" u2="T" k="-4" />
+<hkern u1="&#xca;" u2="a" k="6" />
+<hkern u1="&#xca;" u2="c" k="6" />
+<hkern u1="&#xca;" u2="d" k="6" />
+<hkern u1="&#xca;" u2="e" k="6" />
+<hkern u1="&#xca;" u2="f" k="3" />
+<hkern u1="&#xca;" u2="g" k="1" />
+<hkern u1="&#xca;" u2="o" k="6" />
+<hkern u1="&#xca;" u2="q" k="6" />
+<hkern u1="&#xca;" u2="v" k="5" />
+<hkern u1="&#xca;" u2="y" k="5" />
+<hkern u1="&#xca;" u2="&#xe7;" k="6" />
+<hkern u1="&#xca;" u2="&#xae;" k="5" />
+<hkern u1="&#xca;" u2="&#xa9;" k="5" />
+<hkern u1="&#xca;" u2="&#x3c0;" k="5" />
+<hkern u1="&#xca;" u2="&#xe6;" k="6" />
+<hkern u1="&#xca;" u2="&#x153;" k="6" />
+<hkern u1="&#xc1;" u2="&#x160;" k="-1" />
+<hkern u1="&#xc1;" u2="&#xdd;" k="18" />
+<hkern u1="&#xc1;" u2="*" k="15" />
+<hkern u1="&#xc1;" u2="&#x2c;" k="-8" />
+<hkern u1="&#xc1;" u2="-" k="-3" />
+<hkern u1="&#xc1;" u2="." k="-8" />
+<hkern u1="&#xc1;" u2="@" k="3" />
+<hkern u1="&#xc1;" u2="A" k="-3" />
+<hkern u1="&#xc1;" u2="C" k="10" />
+<hkern u1="&#xc1;" u2="G" k="10" />
+<hkern u1="&#xc1;" u2="J" k="3" />
+<hkern u1="&#xc1;" u2="O" k="10" />
+<hkern u1="&#xc1;" u2="Q" k="10" />
+<hkern u1="&#xc1;" u2="S" k="-1" />
+<hkern u1="&#xc1;" u2="T" k="23" />
+<hkern u1="&#xc1;" u2="V" k="18" />
+<hkern u1="&#xc1;" u2="W" k="18" />
+<hkern u1="&#xc1;" u2="Y" k="18" />
+<hkern u1="&#xc1;" u2="a" k="1" />
+<hkern u1="&#xc1;" u2="c" k="1" />
+<hkern u1="&#xc1;" u2="d" k="1" />
+<hkern u1="&#xc1;" u2="e" k="1" />
+<hkern u1="&#xc1;" u2="m" k="1" />
+<hkern u1="&#xc1;" u2="n" k="1" />
+<hkern u1="&#xc1;" u2="o" k="1" />
+<hkern u1="&#xc1;" u2="p" k="1" />
+<hkern u1="&#xc1;" u2="q" k="1" />
+<hkern u1="&#xc1;" u2="r" k="1" />
+<hkern u1="&#xc1;" u2="t" k="1" />
+<hkern u1="&#xc1;" u2="u" k="1" />
+<hkern u1="&#xc1;" u2="v" k="8" />
+<hkern u1="&#xc1;" u2="w" k="3" />
+<hkern u1="&#xc1;" u2="x" k="-5" />
+<hkern u1="&#xc1;" u2="y" k="8" />
+<hkern u1="&#xc1;" u2="z" k="-3" />
+<hkern u1="&#xc1;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xd6;" k="10" />
+<hkern u1="&#xc1;" u2="&#xe7;" k="1" />
+<hkern u1="&#xc1;" u2="&#xae;" k="3" />
+<hkern u1="&#xc1;" u2="&#xa9;" k="3" />
+<hkern u1="&#xc1;" u2="&#x2122;" k="18" />
+<hkern u1="&#xc1;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xd8;" k="10" />
+<hkern u1="&#xc1;" u2="&#x3c0;" k="3" />
+<hkern u1="&#xc1;" u2="&#xe6;" k="1" />
+<hkern u1="&#xc1;" u2="&#x2026;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xd5;" k="10" />
+<hkern u1="&#xc1;" u2="&#x152;" k="10" />
+<hkern u1="&#xc1;" u2="&#x153;" k="1" />
+<hkern u1="&#xc1;" u2="&#x2013;" k="-3" />
+<hkern u1="&#xc1;" u2="&#x2014;" k="-3" />
+<hkern u1="&#xc1;" u2="&#x201c;" k="15" />
+<hkern u1="&#xc1;" u2="&#x201d;" k="8" />
+<hkern u1="&#xc1;" u2="&#x2018;" k="15" />
+<hkern u1="&#xc1;" u2="&#x2019;" k="8" />
+<hkern u1="&#xc1;" u2="&#x178;" k="18" />
+<hkern u1="&#xc1;" u2="&#x201a;" k="-8" />
+<hkern u1="&#xc1;" u2="&#x201e;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="10" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="10" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="10" />
+<hkern u1="&#xc1;" u2="&#x131;" k="1" />
+<hkern u1="&#xcb;" u2="@" k="5" />
+<hkern u1="&#xcb;" u2="T" k="-4" />
+<hkern u1="&#xcb;" u2="a" k="6" />
+<hkern u1="&#xcb;" u2="c" k="6" />
+<hkern u1="&#xcb;" u2="d" k="6" />
+<hkern u1="&#xcb;" u2="e" k="6" />
+<hkern u1="&#xcb;" u2="f" k="3" />
+<hkern u1="&#xcb;" u2="g" k="1" />
+<hkern u1="&#xcb;" u2="o" k="6" />
+<hkern u1="&#xcb;" u2="q" k="6" />
+<hkern u1="&#xcb;" u2="v" k="5" />
+<hkern u1="&#xcb;" u2="y" k="5" />
+<hkern u1="&#xcb;" u2="&#xe7;" k="6" />
+<hkern u1="&#xcb;" u2="&#xae;" k="5" />
+<hkern u1="&#xcb;" u2="&#xa9;" k="5" />
+<hkern u1="&#xcb;" u2="&#x3c0;" k="5" />
+<hkern u1="&#xcb;" u2="&#xe6;" k="6" />
+<hkern u1="&#xcb;" u2="&#x153;" k="6" />
+<hkern u1="&#xc8;" u2="@" k="5" />
+<hkern u1="&#xc8;" u2="T" k="-4" />
+<hkern u1="&#xc8;" u2="a" k="6" />
+<hkern u1="&#xc8;" u2="c" k="6" />
+<hkern u1="&#xc8;" u2="d" k="6" />
+<hkern u1="&#xc8;" u2="e" k="6" />
+<hkern u1="&#xc8;" u2="f" k="3" />
+<hkern u1="&#xc8;" u2="g" k="1" />
+<hkern u1="&#xc8;" u2="o" k="6" />
+<hkern u1="&#xc8;" u2="q" k="6" />
+<hkern u1="&#xc8;" u2="v" k="5" />
+<hkern u1="&#xc8;" u2="y" k="5" />
+<hkern u1="&#xc8;" u2="&#xe7;" k="6" />
+<hkern u1="&#xc8;" u2="&#xae;" k="5" />
+<hkern u1="&#xc8;" u2="&#xa9;" k="5" />
+<hkern u1="&#xc8;" u2="&#x3c0;" k="5" />
+<hkern u1="&#xc8;" u2="&#xe6;" k="6" />
+<hkern u1="&#xc8;" u2="&#x153;" k="6" />
+<hkern u1="&#xcd;" u2="/" k="5" />
+<hkern u1="&#xcd;" u2="v" k="-5" />
+<hkern u1="&#xcd;" u2="y" k="-5" />
+<hkern u1="&#xce;" u2="/" k="5" />
+<hkern u1="&#xce;" u2="v" k="-5" />
+<hkern u1="&#xce;" u2="y" k="-5" />
+<hkern u1="&#xcf;" u2="/" k="5" />
+<hkern u1="&#xcf;" u2="v" k="-5" />
+<hkern u1="&#xcf;" u2="y" k="-5" />
+<hkern u1="&#xcc;" u2="/" k="5" />
+<hkern u1="&#xcc;" u2="v" k="-5" />
+<hkern u1="&#xcc;" u2="y" k="-5" />
+<hkern u1="&#xd3;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd3;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd3;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd3;" u2="." k="10" />
+<hkern u1="&#xd3;" u2="/" k="13" />
+<hkern u1="&#xd3;" u2="?" k="5" />
+<hkern u1="&#xd3;" u2="J" k="11" />
+<hkern u1="&#xd3;" u2="T" k="13" />
+<hkern u1="&#xd3;" u2="V" k="10" />
+<hkern u1="&#xd3;" u2="W" k="15" />
+<hkern u1="&#xd3;" u2="X" k="9" />
+<hkern u1="&#xd3;" u2="Y" k="11" />
+<hkern u1="&#xd3;" u2="Z" k="11" />
+<hkern u1="&#xd3;" u2="a" k="1" />
+<hkern u1="&#xd3;" u2="b" k="3" />
+<hkern u1="&#xd3;" u2="c" k="1" />
+<hkern u1="&#xd3;" u2="d" k="1" />
+<hkern u1="&#xd3;" u2="e" k="1" />
+<hkern u1="&#xd3;" u2="h" k="3" />
+<hkern u1="&#xd3;" u2="k" k="3" />
+<hkern u1="&#xd3;" u2="l" k="3" />
+<hkern u1="&#xd3;" u2="m" k="1" />
+<hkern u1="&#xd3;" u2="n" k="1" />
+<hkern u1="&#xd3;" u2="o" k="1" />
+<hkern u1="&#xd3;" u2="p" k="1" />
+<hkern u1="&#xd3;" u2="q" k="1" />
+<hkern u1="&#xd3;" u2="r" k="1" />
+<hkern u1="&#xd3;" u2="u" k="1" />
+<hkern u1="&#xd3;" u2="x" k="3" />
+<hkern u1="&#xd3;" u2="z" k="3" />
+<hkern u1="&#xd3;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd3;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd3;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd3;" u2="&#x153;" k="1" />
+<hkern u1="&#xd3;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd3;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd3;" u2="&#x178;" k="11" />
+<hkern u1="&#xd3;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd3;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd3;" u2="&#x131;" k="1" />
+<hkern u1="&#xd4;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd4;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd4;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd4;" u2="." k="10" />
+<hkern u1="&#xd4;" u2="/" k="13" />
+<hkern u1="&#xd4;" u2="?" k="5" />
+<hkern u1="&#xd4;" u2="J" k="11" />
+<hkern u1="&#xd4;" u2="T" k="13" />
+<hkern u1="&#xd4;" u2="V" k="10" />
+<hkern u1="&#xd4;" u2="W" k="15" />
+<hkern u1="&#xd4;" u2="X" k="9" />
+<hkern u1="&#xd4;" u2="Y" k="11" />
+<hkern u1="&#xd4;" u2="Z" k="11" />
+<hkern u1="&#xd4;" u2="a" k="1" />
+<hkern u1="&#xd4;" u2="b" k="3" />
+<hkern u1="&#xd4;" u2="c" k="1" />
+<hkern u1="&#xd4;" u2="d" k="1" />
+<hkern u1="&#xd4;" u2="e" k="1" />
+<hkern u1="&#xd4;" u2="h" k="3" />
+<hkern u1="&#xd4;" u2="k" k="3" />
+<hkern u1="&#xd4;" u2="l" k="3" />
+<hkern u1="&#xd4;" u2="m" k="1" />
+<hkern u1="&#xd4;" u2="n" k="1" />
+<hkern u1="&#xd4;" u2="o" k="1" />
+<hkern u1="&#xd4;" u2="p" k="1" />
+<hkern u1="&#xd4;" u2="q" k="1" />
+<hkern u1="&#xd4;" u2="r" k="1" />
+<hkern u1="&#xd4;" u2="u" k="1" />
+<hkern u1="&#xd4;" u2="x" k="3" />
+<hkern u1="&#xd4;" u2="z" k="3" />
+<hkern u1="&#xd4;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd4;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd4;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd4;" u2="&#x153;" k="1" />
+<hkern u1="&#xd4;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd4;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd4;" u2="&#x178;" k="11" />
+<hkern u1="&#xd4;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd4;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd4;" u2="&#x131;" k="1" />
+<hkern u1="&#xd2;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd2;" u2="&#x17d;" k="11" />
+<hkern u1="&#xd2;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd2;" u2="." k="10" />
+<hkern u1="&#xd2;" u2="/" k="13" />
+<hkern u1="&#xd2;" u2="?" k="5" />
+<hkern u1="&#xd2;" u2="J" k="11" />
+<hkern u1="&#xd2;" u2="T" k="13" />
+<hkern u1="&#xd2;" u2="V" k="10" />
+<hkern u1="&#xd2;" u2="W" k="15" />
+<hkern u1="&#xd2;" u2="X" k="9" />
+<hkern u1="&#xd2;" u2="Y" k="11" />
+<hkern u1="&#xd2;" u2="Z" k="11" />
+<hkern u1="&#xd2;" u2="a" k="1" />
+<hkern u1="&#xd2;" u2="b" k="3" />
+<hkern u1="&#xd2;" u2="c" k="1" />
+<hkern u1="&#xd2;" u2="d" k="1" />
+<hkern u1="&#xd2;" u2="e" k="1" />
+<hkern u1="&#xd2;" u2="h" k="3" />
+<hkern u1="&#xd2;" u2="k" k="3" />
+<hkern u1="&#xd2;" u2="l" k="3" />
+<hkern u1="&#xd2;" u2="m" k="1" />
+<hkern u1="&#xd2;" u2="n" k="1" />
+<hkern u1="&#xd2;" u2="o" k="1" />
+<hkern u1="&#xd2;" u2="p" k="1" />
+<hkern u1="&#xd2;" u2="q" k="1" />
+<hkern u1="&#xd2;" u2="r" k="1" />
+<hkern u1="&#xd2;" u2="u" k="1" />
+<hkern u1="&#xd2;" u2="x" k="3" />
+<hkern u1="&#xd2;" u2="z" k="3" />
+<hkern u1="&#xd2;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd2;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd2;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd2;" u2="&#x153;" k="1" />
+<hkern u1="&#xd2;" u2="&#x201c;" k="5" />
+<hkern u1="&#xd2;" u2="&#x2018;" k="5" />
+<hkern u1="&#xd2;" u2="&#x178;" k="11" />
+<hkern u1="&#xd2;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd2;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd2;" u2="&#x131;" k="1" />
+<hkern u1="&#xda;" u2="&#x2c;" k="3" />
+<hkern u1="&#xda;" u2="." k="3" />
+<hkern u1="&#xda;" u2="A" k="-5" />
+<hkern u1="&#xda;" u2="J" k="3" />
+<hkern u1="&#xda;" u2="v" k="-5" />
+<hkern u1="&#xda;" u2="y" k="-5" />
+<hkern u1="&#xda;" u2="&#xc4;" k="-5" />
+<hkern u1="&#xda;" u2="&#xc5;" k="-5" />
+<hkern u1="&#xda;" u2="&#xc6;" k="-5" />
+<hkern u1="&#xda;" u2="&#x2026;" k="3" />
+<hkern u1="&#xda;" u2="&#xc0;" k="-5" />
+<hkern u1="&#xda;" u2="&#xc3;" k="-5" />
+<hkern u1="&#xda;" u2="&#x201a;" k="3" />
+<hkern u1="&#xda;" u2="&#x201e;" k="3" />
+<hkern u1="&#xda;" u2="&#xc2;" k="-5" />
+<hkern u1="&#xda;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xdb;" u2="&#x2c;" k="3" />
+<hkern u1="&#xdb;" u2="." k="3" />
+<hkern u1="&#xdb;" u2="A" k="-5" />
+<hkern u1="&#xdb;" u2="J" k="3" />
+<hkern u1="&#xdb;" u2="v" k="-5" />
+<hkern u1="&#xdb;" u2="y" k="-5" />
+<hkern u1="&#xdb;" u2="&#xc4;" k="-5" />
+<hkern u1="&#xdb;" u2="&#xc5;" k="-5" />
+<hkern u1="&#xdb;" u2="&#xc6;" k="-5" />
+<hkern u1="&#xdb;" u2="&#x2026;" k="3" />
+<hkern u1="&#xdb;" u2="&#xc0;" k="-5" />
+<hkern u1="&#xdb;" u2="&#xc3;" k="-5" />
+<hkern u1="&#xdb;" u2="&#x201a;" k="3" />
+<hkern u1="&#xdb;" u2="&#x201e;" k="3" />
+<hkern u1="&#xdb;" u2="&#xc2;" k="-5" />
+<hkern u1="&#xdb;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xd9;" u2="&#x2c;" k="3" />
+<hkern u1="&#xd9;" u2="." k="3" />
+<hkern u1="&#xd9;" u2="A" k="-5" />
+<hkern u1="&#xd9;" u2="J" k="3" />
+<hkern u1="&#xd9;" u2="v" k="-5" />
+<hkern u1="&#xd9;" u2="y" k="-5" />
+<hkern u1="&#xd9;" u2="&#xc4;" k="-5" />
+<hkern u1="&#xd9;" u2="&#xc5;" k="-5" />
+<hkern u1="&#xd9;" u2="&#xc6;" k="-5" />
+<hkern u1="&#xd9;" u2="&#x2026;" k="3" />
+<hkern u1="&#xd9;" u2="&#xc0;" k="-5" />
+<hkern u1="&#xd9;" u2="&#xc3;" k="-5" />
+<hkern u1="&#xd9;" u2="&#x201a;" k="3" />
+<hkern u1="&#xd9;" u2="&#x201e;" k="3" />
+<hkern u1="&#xd9;" u2="&#xc2;" k="-5" />
+<hkern u1="&#xd9;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xa4;" u2="4" k="5" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="OmnesATT W02 Bold Italic" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="OmnesATT W02 Bold Italic" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="OmnesATT W02 Bold Italic" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="OmnesATT W02 Bold Italic" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.ttf b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.ttf
new file mode 100644
index 0000000000..4f35e1f9b8
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.woff b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.woff
new file mode 100644
index 0000000000..1722dce99e
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/OmnesATTW02BoldItalic.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.eot
new file mode 100644
index 0000000000..39e1eae7c7
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.svg
new file mode 100644
index 0000000000..73923de097
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.svg
@@ -0,0 +1,3694 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated by RoboFog]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="552" id="Omnes_ATTW02">
+<font-face font-family="Omnes_ATT W02" panose-1="2 0 6 6 4 0 0 2 0 4" ascent="700" descent="-300" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="170" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="170" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="277" d="M134 178Q125 178 123 184T119 202L97 647H180L158 202Q157 191 155 185T142 178H134ZM133 -6Q89 -6 89 38V48Q89 69 100 81T133 93H143Q165 93 176 81T188 48V38Q188 -6 143 -6H133Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="387" d="M262 391Q253 391 250 397T245 417L226 647H308L289 417Q287 404 284 398T272 391H262ZM114 391Q105 391 102 397T97 417L78 647H160L141 417Q139 404 136 398T124 391H114Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="681" d="M147 -4Q127 -4 117 14T120 78L150 175H29V233H167L195 323Q207 363 223 414H103V472H240Q245 484 249 499Q253 511 257 527T267 560Q278 599 294 648Q294 649 297 650T306 651Q326 651 336 633T333 569L303 472H468L522 648Q522 649 525 650T534 651Q554 651 564 633T561 569L531 472H652V414H513L458 233H578V175H440L387 -1Q387 -2 384 -3T375 -4Q355 -4 345 14T348 78L378 175H213L159 -1Q159 -2 156 -3T147 -4ZM397 231L453 416H284L228 231H397Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="571" d="M252 -10Q193 -4 150 16T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q115 117 156 89T255 52V301Q220 310 187 323T127 356T85 407T69 481Q69 515 83 545T122 598T181 636T254 657V742H314V658Q371 653 412 635T477 593Q488 582 494 570T501 547Q501 536 495 528T482 515T468 507T461 506Q441 541 402 566T311 597V357Q348 348 384 335T450 301T496 248T514 170Q514 134 499 102T456 46T392 7T312 -11V-98H252V-10ZM308 51Q370 55 408 87T447 167Q447 194 436 213T406 247T362 270T308 288V51ZM258 596Q205 589 171 558T136 482Q136 432 169 409T258 370V596Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="762" d="M201 347Q169 347 142 358T95 390T64 438T52 496V506Q52 537 64 564T96 612T143 645T202 657Q233 657 260 645T308 613T339 566T351 508V498Q351 467 339 440T307 392T260 359T201 347ZM128 -8Q127 -9 122 -8T111 -4T101 5T96 20Q96 34 102 45T126 75L629 661Q630 662 635 662T646 658T656 648T661 633Q661 612 631 578L128 -8ZM560 -4Q528 -4 501 7T454 39T423 87T411 145V155Q411 186 423 213T455 261T502 294T561 306Q592 306 619 294T667 262T698 215T710 157V147Q710 116 698 89T666 41T619 8T560 -4ZM202 399Q241 399 266 426T292 495V509Q292 551 266 578T201 606Q162 606 137 579T111 509V495Q111 454 137 427T202 399ZM561 48Q600 48 625 75T651 144V158Q651 200 625 227T560 255Q521 255 496 228T470 158V144Q470 103 496 76T561 48Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="681" d="M264 -12Q214 -12 173 1T101 38T55 99T38 182Q38 257 84 302T209 371Q186 398 169 430T152 505Q152 536 164 564T198 613T250 646T317 659Q351 659 379 648T429 617T462 570T474 512Q474 453 434 414T316 351L489 192Q499 209 508 228T525 270T541 323T557 391H624Q618 359 609 327T588 263T563 203T534 152L673 24Q674 23 673 19T668 9T656 0T636 -4Q626 -4 614 -1T586 17L497 101Q449 47 391 18T264 -12ZM268 46Q326 46 372 72T455 141L246 333Q186 315 147 281T108 185Q108 119 153 83T268 46ZM275 389Q345 408 378 436T411 511Q411 550 385 576T316 603Q268 603 243 575T218 506Q218 471 233 444T275 389Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="239" d="M114 391Q105 391 102 397T97 417L78 647H160L141 417Q139 404 136 398T124 391H114Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="381" d="M332 -198Q299 -198 264 -181T193 -128Q138 -73 101 16T63 226Q63 284 73 336T102 433T143 514T193 577Q265 649 332 649Q346 649 354 644T362 625Q362 618 359 610T353 600Q301 589 260 555T190 470T146 357T131 227Q131 157 146 94T189 -19T259 -104T353 -149Q355 -150 358 -158T362 -174Q362 -188 354 -193T332 -198Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="381" d="M49 -198Q35 -198 27 -193T19 -174Q19 -167 22 -159T28 -149Q80 -138 121 -104T191 -20T235 94T250 227Q250 294 235 356T192 469T122 554T28 600Q26 601 23 609T19 625Q19 639 27 644T49 649Q116 649 188 577Q214 551 237 515T279 433T307 336T318 226Q318 105 281 16T188 -128Q153 -163 118 -180T49 -198Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="407" d="M96 352Q92 356 92 363T98 381L159 472L57 511Q39 518 39 529Q39 564 72 555L178 526L182 634Q183 650 188 654T205 659Q216 659 221 655T228 634L232 526L338 555Q371 564 371 529Q371 518 353 511L251 472L312 381Q319 370 319 363T314 352Q304 343 295 342T274 354L205 440L136 354Q125 340 116 341T96 352Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="569" d="M282 34Q269 34 262 40T254 65V239H93Q75 239 69 245T62 265V271Q62 284 68 290T93 297H254V460Q254 478 261 484T282 491H287Q300 491 307 485T315 460V297H476Q494 297 500 291T507 271V265Q507 252 501 246T476 239H315V65Q315 47 308 41T287 34H282Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="239" d="M63 -131Q61 -135 55 -136T42 -136T31 -129T30 -116L90 63Q97 80 103 89T129 98H134Q146 98 155 89T164 63V59Q164 51 162 40T148 11L63 -131Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="396" d="M64 272H332V212H64V272Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="239" d="M114 -6Q70 -6 70 38V48Q70 69 81 81T114 93H124Q146 93 157 81T169 48V38Q169 -6 124 -6H114Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="353" d="M267 647H335L78 -193H10L267 647Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="659" d="M328 -12Q268 -12 221 8T140 68T89 163T71 289V356Q71 426 89 482T140 577T221 638T329 659Q389 659 437 638T518 578T570 484T588 358V291Q588 221 570 165T518 70T436 9T328 -12ZM329 47Q420 47 470 111T520 291V354Q520 470 470 535T328 601Q238 601 189 537T139 356V293Q139 177 189 112T329 47Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="377" d="M183 541Q171 524 152 514T109 504Q72 504 52 524Q36 540 36 561Q36 578 46 588T57 598Q67 587 80 580T114 573Q146 573 168 596T192 651H221Q251 651 251 620V0H183V541Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="550" d="M100 0Q57 0 57 41V61Q57 113 74 151T119 219T183 269T257 310Q290 327 321 343T375 378T413 420T428 473Q428 531 388 564T278 597Q239 597 210 586T161 557T127 515T106 464Q106 462 99 462T83 465T67 477T60 500Q60 519 71 544T106 592Q133 619 177 639T283 660Q382 660 438 610T495 472Q495 430 479 399T435 343T373 300T300 262Q265 245 233 227T177 188T138 138T123 73V62H504V0H100Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="562" d="M262 -12Q194 -12 146 8T72 55Q52 75 42 94T32 130Q32 143 38 151T51 164T64 169T71 169Q92 109 143 78T263 47Q340 47 385 82T431 178Q431 244 387 278T257 313H172V371H268Q340 371 376 401T412 486Q412 540 373 570T263 601Q201 601 158 573T93 499Q92 497 86 499T73 505T60 517T54 536Q54 562 84 592Q110 618 156 638T263 659Q366 659 423 614T481 487Q481 380 374 347Q435 327 467 284T500 179Q500 90 437 39T262 -12Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="611" d="M388 184H103Q80 184 68 194T55 220V240Q55 254 62 266T83 295L340 612Q349 623 355 630T367 642T382 649T401 651Q431 651 442 640T454 607V246H569V184H454V0H388V184ZM390 245V585L117 245H390Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="558" d="M268 -12Q205 -12 156 8T78 56Q46 88 46 116Q46 127 52 134T65 146T79 152T86 153Q109 105 155 77T270 49Q348 49 395 88T442 195Q442 261 399 299T282 338Q225 338 186 325T117 290Q94 293 84 305T74 337V609Q74 625 85 636T112 647H473V585H139V367Q170 382 205 391T283 401Q334 401 376 387T447 346T493 282T510 198Q510 150 493 111T445 45T369 3T268 -12Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="605" d="M138 328Q174 362 224 383T329 405Q379 405 420 391T490 352T534 290T550 209V204Q550 157 533 118T486 50T414 5T323 -12Q260 -12 213 9T135 70T87 163T71 283V356Q71 427 89 483T140 578T221 638T328 659Q391 659 433 642T498 602Q511 589 517 577T523 553Q523 544 518 537T505 524T493 517T486 516Q460 554 423 577T328 601Q239 601 189 539T138 360V328ZM319 46Q355 46 385 57T438 88T472 136T484 198V204Q484 273 443 310T323 348Q296 348 270 341T219 322T175 294T139 259Q144 159 189 103T319 46Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="548" d="M169 -4Q160 -4 153 -1T141 8T134 17T132 24L438 585H45V647H474Q497 647 505 639T513 617V611Q513 598 495 564L205 20Q197 6 188 1T169 -4Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="599" d="M299 -12Q243 -12 199 1T123 38T74 95T57 169Q57 226 90 269T189 333Q131 352 104 390T76 481Q76 520 92 553T137 609T207 646T300 659Q405 659 463 612T522 483Q522 431 495 393T410 334Q476 314 508 271T541 170Q541 131 524 98T474 40T398 2T299 -12ZM300 362Q375 362 414 393T454 479Q454 534 414 568T299 602Q221 602 183 569T144 479Q144 426 184 394T300 362ZM300 46Q387 46 430 79T473 173Q473 232 428 267T299 303Q214 303 170 268T125 173Q125 117 170 82T300 46Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="605" d="M272 46Q370 46 418 104T467 287V308Q435 277 384 255T276 232Q226 232 185 246T115 285T71 347T55 428V433Q55 479 72 520T119 592T191 641T282 659Q341 659 388 639T467 581T517 488T534 364V291Q534 139 467 64T272 -12Q213 -12 170 4T103 44Q77 70 77 94Q77 103 82 110T94 123T107 130T114 131Q140 93 177 70T272 46ZM282 289Q337 289 387 315T467 379Q463 483 417 542T286 601Q250 601 220 589T167 554T133 503T121 439V433Q121 364 162 327T282 289Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="269" d="M129 334Q85 334 85 378V388Q85 409 96 421T129 433H139Q161 433 172 421T184 388V378Q184 334 139 334H129ZM129 -6Q85 -6 85 38V48Q85 69 96 81T129 93H139Q161 93 172 81T184 48V38Q184 -6 139 -6H129Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="269" d="M129 334Q85 334 85 378V388Q85 409 96 421T129 433H139Q161 433 172 421T184 388V378Q184 334 139 334H129ZM78 -131Q76 -135 70 -136T57 -136T46 -129T45 -116L105 63Q112 80 118 89T144 98H149Q161 98 170 89T179 63V59Q179 51 177 40T163 11L78 -131Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="498" d="M84 225Q74 230 69 237T64 268Q64 290 69 297T84 310L411 486Q413 487 422 478T433 455Q435 444 427 432T392 406L129 268L392 131Q418 117 426 105T433 81Q431 65 423 57T411 50L84 225Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="569" d="M103 346Q85 346 79 353T72 373V380Q72 393 78 400T103 407H466Q484 407 490 400T497 380V373Q497 360 491 353T466 346H103ZM103 119Q85 119 79 126T72 146V153Q72 166 78 173T103 180H466Q484 180 490 173T497 153V146Q497 133 491 126T466 119H103Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="497" d="M86 50Q83 48 75 56T64 81Q63 92 71 104T105 131L368 268L105 406Q79 420 71 432T64 455Q66 469 75 478T86 486L413 310Q423 305 428 298T433 268Q433 245 428 238T413 225L86 50Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="493" d="M192 177Q193 223 203 256T229 314T264 354T302 383Q318 393 332 403T357 426T374 453T381 489Q381 540 348 569T252 599Q213 599 186 587T141 556T114 512T100 463Q100 461 92 462T75 467T59 480T51 503Q51 522 62 549T98 600Q123 625 161 642T254 659Q299 659 335 647T396 612T434 560T448 495Q448 465 440 443T417 403T386 372T349 345Q330 332 312 318T280 284T257 239T248 177H192ZM213 -4Q193 -4 185 4T176 33V53Q176 74 184 82T213 90H233Q254 90 262 82T271 53V33Q271 13 263 5T233 -4H213Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="776" d="M403 -12Q326 -12 264 12T157 80T88 186T63 323Q63 397 87 458T155 564T259 634T392 659Q466 659 526 638T628 578T694 483T717 357Q717 263 681 213T575 163Q533 163 505 180T469 230Q452 199 426 182T363 165Q334 165 310 176T267 207T239 255T228 317Q228 351 238 378T266 426T310 457T365 468Q397 468 422 452T462 415V462H526V296Q526 257 539 235T588 212Q609 212 624 223T648 253T662 299T667 357Q667 415 648 462T592 542T505 593T392 611Q330 611 279 590T191 531T134 439T113 323Q113 261 134 208T194 117T286 58T403 36Q451 36 492 48T562 79Q570 84 577 87T596 91Q603 91 608 87T618 76T623 65T623 58Q590 31 532 10T403 -12ZM378 221Q412 221 437 243T462 302V350Q451 375 431 393T379 412Q341 412 318 386T295 317Q295 274 318 248T378 221Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="633" d="M92 609Q92 625 103 636T130 647H337Q440 647 494 604T548 477Q548 373 436 336Q508 320 542 278T576 179Q576 90 519 45T343 0H92V609ZM342 61Q429 61 467 89T506 182Q506 298 346 298H160V61H342ZM340 358Q403 358 440 389T478 474Q478 586 337 586H160V358H340Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="690" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T389 659Q428 659 461 652T520 634T567 609T602 581Q630 553 630 531Q630 518 624 510T611 497T597 491T590 490Q575 517 552 537T502 572T446 593T387 600Q331 600 285 580T206 523T154 436T135 324Q135 264 154 213T207 125T287 68T389 47Q425 47 458 56T518 81T566 121T598 174Q598 175 605 174T620 169T635 156T642 134Q642 122 632 104T604 68Q574 38 519 13T386 -12Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="702" d="M92 609Q92 625 103 636T130 647H283Q367 647 432 625T543 562T613 461T637 325Q637 248 613 188T542 86T429 22T278 0H92V609ZM282 60Q419 60 493 128T567 323Q567 385 548 434T492 517T403 569T283 587H160V60H282Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="563" d="M92 609Q92 625 103 636T130 647H519V585H160V341H438V281H160V0H92V609Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="736" d="M370 -12Q300 -12 244 13T148 82T87 188T65 322Q65 396 89 458T155 564T257 634T389 659Q465 659 518 635T599 584Q630 553 630 531Q630 517 624 509T611 496T597 492T589 492Q574 519 552 539T502 572T446 593T387 600Q331 600 285 580T205 524T154 436T135 324Q135 264 151 213T199 126T276 68T379 47Q423 47 460 60T526 96T573 150T597 218V269H382V330H607Q629 330 643 316T657 280V3Q657 1 651 -1T635 -4Q618 -4 608 8T597 61V97Q567 46 508 17T370 -12Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="709" d="M92 620Q92 651 121 651H130Q160 651 160 620V355H549V620Q549 651 578 651H587Q617 651 617 620V0H549V293H160V0H92V620Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="467" d="M197 -12Q163 -12 135 -3T85 21T53 51T41 82Q41 92 47 100T60 114T74 122T81 123Q96 90 126 69T197 47Q262 47 289 83T317 204V647H385V204Q385 93 340 41T197 -12Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="586" d="M92 620Q92 651 121 651H130Q160 651 160 620V339L456 630Q472 646 486 649T510 648T525 634T529 615L235 332L536 31Q537 20 531 11T513 -2T488 -4T462 12L160 323V0H92V620Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="550" d="M92 620Q92 651 121 651H130Q160 651 160 620V63H472Q503 63 503 34V29Q503 0 472 0H92V620Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="771" d="M92 608Q92 627 105 639T141 651H157Q183 651 193 639T216 600L386 223L557 600Q568 626 579 638T616 651H630Q652 651 665 639T679 608V0H614V573L444 204Q435 184 423 173T385 161Q359 161 347 172T326 204L157 573V0H92V608Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="705" d="M92 605Q92 624 101 637T129 651H146Q165 651 175 642T198 615L549 106V647H613V42Q613 22 604 9T578 -4H573Q554 -4 547 2T529 22L156 561V0H92V605Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="583" d="M92 609Q92 625 103 636T130 647H296Q410 647 472 596T534 451Q534 354 472 303T288 252H160V0H92V609ZM294 312Q379 312 421 347T464 449Q464 586 297 586H160V312H294Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 256 688 199T628 96L705 19Q706 18 704 13T697 3T684 -7T666 -12Q649 -12 632 5L584 54Q543 22 493 5T386 -12ZM388 47Q478 47 541 95L397 241L440 286L584 139Q610 174 624 220T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="597" d="M541 460Q541 369 484 321T327 272L533 28Q534 26 532 21T525 11T511 1T492 -4Q482 -4 472 1T450 20L240 272H159V0H92V609Q92 625 103 636T130 647H308Q422 647 481 601T541 460ZM314 330Q395 330 433 365T471 458Q471 586 310 586H159V330H314Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="571" d="M289 -12Q216 -12 163 10T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q118 111 167 79T288 47Q363 47 405 78T447 167Q447 199 433 220T393 255T336 279T269 297Q232 306 197 317T133 349T88 401T70 481Q70 519 86 551T132 608T200 645T287 659Q355 659 403 639T476 594Q501 569 501 547Q501 536 495 528T482 515T468 507T461 506Q438 546 393 573T288 600Q220 600 178 568T136 483Q136 452 148 432T182 399T234 378T302 360Q341 351 379 338T447 304T495 251T514 170Q514 129 498 96T453 38T382 1T289 -12Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="614" d="M273 585H70Q39 585 39 613V618Q39 647 70 647H544Q575 647 575 618V613Q575 585 544 585H341V0H273V585Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="630" d="M33 631Q31 634 40 641T63 650T91 647T113 620L316 72L520 620Q528 641 541 647T568 651T589 641T597 631L353 0H277L33 631Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="934" d="M70 636Q69 639 79 644T103 651T130 643T149 609L273 61L415 524Q421 542 432 552T469 562Q495 562 505 553T522 524L662 63L789 609Q795 634 807 643T833 651T855 645T864 636L708 0H618L470 495L313 0H226L70 636Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="612" d="M75 -4Q66 -4 59 0T48 10T41 21T41 28L266 327L49 647H123L308 374L404 504Q446 561 501 633Q510 645 518 649T534 654Q543 654 550 650T561 640T567 629T568 622L345 326L568 31Q569 29 568 24T562 14T550 4T532 -1Q522 -1 512 4T494 19L304 281L108 17Q99 5 91 1T75 -4Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="579" d="M255 269L25 647H100L290 331L479 627Q491 646 504 649T529 648T547 635T553 622L323 269V0H255V269Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="634" d="M69 72L481 585H101Q70 585 70 613V618Q70 647 101 647H556V578L142 62H543Q574 62 574 34V29Q574 0 543 0H69V72Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="353" d="M94 647H286Q304 647 310 641T317 621V614Q317 601 311 595T286 588H156V-138H286Q304 -138 310 -144T317 -164V-171Q317 -184 311 -190T286 -197H94V647Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="353" d="M17 647H85L342 -193H274L17 647Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="353" d="M259 -197H67Q49 -197 43 -191T36 -171V-164Q36 -151 42 -145T67 -138H197V588H67Q49 588 43 594T36 614V621Q36 634 42 640T67 647H259V-197Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="530" d="M156 446Q134 446 134 469Q134 485 150 507T186 549Q200 563 213 575T238 596T256 610T265 616Q266 616 273 611T292 596T316 576T341 552Q361 532 378 509T395 469Q395 446 374 446Q361 446 345 460T303 507L264 558L224 505Q203 478 186 462T156 446Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="495" d="M61 -62Q30 -62 30 -34V-29Q30 0 61 0H434Q465 0 465 -29V-34Q465 -62 434 -62H61Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="530" d="M336 551Q334 549 318 553T277 566T225 589T172 620Q135 645 135 670Q135 682 144 689T165 697Q187 697 211 679Q235 662 258 641T299 600T327 567T336 551Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="599" d="M320 -12Q288 -12 261 -5T211 15T173 42T146 75V0H82V674Q82 676 88 678T104 681Q121 681 134 669T147 618V402Q179 444 222 466T324 488Q371 488 412 471T483 421T531 342T549 239Q549 183 532 137T484 57T411 6T320 -12ZM315 45Q353 45 384 59T437 98T471 159T483 238Q483 280 471 315T436 376T384 416T317 431Q254 431 212 401T146 318V206Q146 172 159 143T196 92T250 58T315 45Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="548" d="M295 -12Q242 -12 198 7T120 59T69 139T50 239Q50 293 68 338T120 417T197 469T295 488Q356 488 397 469T465 424Q482 407 491 389T500 356Q500 345 494 339T479 329T465 324T457 325Q448 347 434 366T401 399T355 422T295 430Q257 430 224 416T167 377T130 316T116 239Q116 197 129 162T167 101T223 61T295 46Q329 46 355 54T400 76T434 110T457 151Q458 152 464 152T479 148T493 138T500 120Q500 106 491 88T463 50Q438 25 397 7T295 -12Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="599" d="M275 -12Q228 -12 187 5T116 55T68 134T50 237Q50 293 67 339T115 419T187 470T279 488Q343 488 387 464T452 409V674Q452 676 458 678T474 681Q491 681 504 669T517 618V0H453V74Q421 32 378 10T275 -12ZM282 45Q315 45 346 57T401 90T439 141T453 205V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 317T116 238Q116 196 128 161T163 100T215 60T282 45Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="360" d="M126 418H29V476H126V520Q126 602 161 645T266 689Q297 689 316 682T348 663Q361 650 361 633Q361 616 353 608T342 602Q331 613 311 622T266 631Q225 631 207 604T189 517V476H312V418H190V0H126V418Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="599" d="M291 -200Q225 -200 179 -181T107 -136Q81 -110 81 -88Q81 -75 86 -67T99 -53T112 -47T119 -47Q141 -88 182 -115T292 -142Q453 -142 453 24V84Q421 43 378 23T275 2Q225 2 184 19T113 67T67 143T50 243Q50 296 68 341T116 418T189 469T279 488Q342 488 387 465T453 411V476H518V24Q518 -89 459 -144T291 -200ZM282 58Q315 58 346 69T401 102T439 152T453 215V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 319T116 244Q116 159 161 109T282 58Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="575" d="M82 674Q82 676 88 678T104 681Q121 681 134 669T147 618V400Q172 439 212 463T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 398 386T304 427Q233 427 190 383T147 264V0H82V674Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM119 595Q77 595 77 638Q77 681 121 681Q163 681 163 638Q163 595 119 595Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="239" d="M21 -191Q-5 -191 -25 -184T-56 -166Q-69 -153 -69 -139Q-69 -123 -61 -112T-50 -103Q-39 -114 -22 -123T20 -133Q56 -133 71 -113T87 -44V476H152V-49Q152 -118 119 -154T21 -191ZM119 595Q77 595 77 638Q77 681 121 681Q163 681 163 638Q163 595 119 595Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="473" d="M82 674Q82 676 88 678T104 681Q121 681 134 669T147 618V258L339 454Q359 475 374 478T398 477T412 462T415 448L218 253L446 30Q446 25 442 16T429 0T405 -3T369 21L147 242V0H82V674Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="239" d="M87 674Q87 676 93 678T109 681Q126 681 139 669T152 618V0H87V674Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="885" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V405Q171 442 209 465T296 488Q353 488 393 463T454 386Q478 429 520 458T623 488Q714 488 760 438T807 287V0H742V283Q742 427 619 427Q574 427 536 401T477 331V0H412V278Q412 427 289 427Q244 427 206 401T147 331V0H82V473Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="575" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V402Q171 440 211 464T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 399 386T304 427Q248 427 208 399T147 326V0H82V473Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="599" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V402Q178 444 221 466T324 488Q371 488 412 471T483 421T531 342T549 239Q549 183 532 137T484 57T411 6T320 -12Q288 -12 261 -5T212 15T174 42T147 75V-193H82V473ZM315 45Q353 45 384 59T437 98T471 159T483 238Q483 280 471 315T436 376T384 416T317 431Q254 431 212 401T146 318V206Q146 172 159 143T196 92T250 58T315 45Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="599" d="M452 73Q420 32 377 10T275 -12Q228 -12 187 5T116 55T68 134T50 237Q50 293 67 339T115 419T187 470T279 488Q344 488 388 464T453 408V417Q453 456 465 468T495 480Q504 480 510 478T517 473V-193H452V73ZM282 45Q315 45 346 57T401 92T439 143T453 207V317Q430 370 389 400T284 431Q246 431 215 417T162 378T128 317T116 238Q116 196 128 161T163 100T215 60T282 45Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="368" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V388Q178 488 272 488Q305 488 323 475T341 439Q341 421 333 412T323 405Q312 414 299 419T265 425Q233 425 211 409T174 365T154 299T147 214V0H82V473Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="484" d="M242 -12Q179 -12 135 5T68 45Q46 67 46 85Q46 95 51 102T62 113T74 119T81 120Q105 87 144 65T244 43Q304 43 335 65T367 123Q367 144 358 158T331 181T287 197T227 211Q199 217 170 225T116 247T76 285T60 344Q60 376 73 402T110 448T167 477T242 488Q301 488 339 473T398 437Q418 417 418 397Q418 388 413 381T401 370T389 364T382 363Q362 394 327 413T242 433Q183 433 153 411T122 353Q122 315 155 299T255 269Q284 263 315 255T371 232T413 194T430 133Q430 67 380 28T242 -12Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="376" d="M245 -12Q175 -12 144 26T112 140V418H41V476H112V559Q112 577 120 583T141 590H148Q161 590 168 584T176 559V476H330V418H176V147Q176 94 191 69T247 44Q272 44 290 53T321 79Q323 81 333 74T343 47Q343 31 328 16Q316 4 295 -4T245 -12Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="503" d="M30 461Q30 465 38 472T59 480T85 473T109 438L252 59L396 438Q406 464 419 472T445 480T465 472T473 461L290 0H214L30 461Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="704" d="M39 461Q39 464 48 471T70 479T96 473T116 434L203 60L305 381Q310 395 319 401T354 408Q379 408 388 402T403 381L504 60L590 434Q597 464 609 473T634 480T656 471T665 461L545 0H465L354 345L240 0H160L39 461Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="495" d="M73 -4Q63 -4 57 0T46 9T40 18T39 24L208 237L42 452Q41 453 42 457T48 467T58 476T75 480Q92 480 102 471T124 447L250 277L373 446Q387 465 398 472T421 480Q431 480 437 476T448 467T454 458T455 452L287 237L452 24Q453 23 452 19T446 9T436 0T419 -4Q402 -4 392 5T370 29L244 198L122 30Q110 13 99 5T73 -4Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="502" d="M198 -197Q184 -197 174 -190T164 -180L244 24Q227 24 219 29T204 49L24 476H92L262 65L410 476H479L244 -154Q234 -180 223 -188T198 -197Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="503" d="M55 50L358 420H88Q70 420 63 426T55 445V451Q55 464 62 470T88 476H445V432L137 56H419Q437 56 444 50T452 31V25Q452 12 445 6T419 0H55V50Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="440" d="M379 -199Q344 -199 306 -179T234 -119T177 -17T151 129L86 164Q73 171 65 176T51 188T43 203T41 226Q41 240 43 248T50 263T64 275T86 288L152 323Q155 407 178 468T234 570T306 631T380 651Q406 651 406 623Q406 616 403 608T397 598Q347 587 314 563T259 499T229 408T217 289L98 226L216 163Q218 97 227 45T258 -47T312 -111T396 -146Q398 -147 401 -155T405 -171Q405 -199 379 -199Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="273" d="M133 -197Q120 -197 112 -191T104 -166V620Q104 638 112 644T133 651H140Q153 651 161 645T169 620V-166Q169 -184 161 -190T140 -197H133Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="440" d="M60 -199Q34 -199 34 -171Q34 -164 37 -156T43 -146Q93 -136 126 -111T181 -47T211 44T223 163L341 226L222 289Q220 355 211 407T180 499T126 562T42 598Q40 599 37 607T33 623Q33 651 59 651Q94 651 132 631T204 571T261 469T287 323L353 288Q366 281 374 276T388 264T395 249T398 226Q398 212 396 204T388 189T375 177T353 164L288 129Q285 45 262 -16T206 -118T134 -179T60 -199Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="529" d="M146 168Q121 168 121 194Q121 210 126 226T140 255T162 276T190 284Q214 284 254 263T338 204Q342 217 347 230T358 253T370 270T384 277Q409 277 409 251Q409 234 404 218T390 190T368 169T340 161Q316 161 276 182T192 241Q188 228 183 215T172 192T160 175T146 168Z" />
+<glyph unicode="&#xA0;" glyph-name="uni00A0" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="277" d="M143 469Q152 469 154 463T158 445L180 0H97L119 445Q120 456 122 462T135 469H143ZM144 653Q188 653 188 609V599Q188 578 177 566T144 554H134Q112 554 101 566T89 599V609Q89 653 134 653H144Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="569" d="M305 -98Q292 -98 285 -92T278 -67V30Q177 42 120 107T62 281Q62 332 78 375T122 452T191 507T278 534V626Q278 644 285 650T305 657H310Q323 657 330 651T337 626V536Q381 533 414 520T469 489T503 454T515 423Q515 412 510 404T498 391T486 384T479 384Q456 426 422 449T334 478V86Q443 92 486 190Q487 191 493 190T506 185T518 174T524 156Q524 140 512 119T476 79T417 45T337 28V-67Q337 -85 330 -91T310 -98H305ZM128 282Q128 200 170 150T281 89V477Q211 469 170 419T128 282Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="594" d="M76 263Q45 263 45 289V296Q45 323 76 323H166L194 476Q210 563 256 611T385 659Q427 659 458 646T509 614T539 573T549 531Q549 516 542 508T527 497T511 493T503 495Q492 545 463 573T385 601Q327 601 300 570T260 472L233 323H399Q430 323 430 296V289Q430 263 399 263H222L185 62H505Q536 62 536 33V28Q536 0 505 0H62Q31 0 31 28V34Q31 62 62 62H118L155 263H76Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="656" d="M82 376Q64 376 58 382T51 402V408Q51 421 57 427T82 434H152Q162 486 183 527T237 598T310 643T399 659Q446 659 483 646T546 615T585 575T599 536Q599 524 593 517T580 505T567 499T560 499Q534 548 495 575T395 602Q328 602 282 557T218 434H434Q452 434 458 428T465 408V402Q465 389 459 383T434 376H210Q209 369 209 362T209 348V276H434Q452 276 458 270T465 250V244Q465 231 459 225T434 218H217Q234 140 282 93T401 46Q463 46 501 75T563 158Q564 159 570 158T583 153T597 140T603 120Q603 102 589 79T549 36T486 2T404 -12Q301 -12 235 48T151 218H82Q64 218 58 224T51 244V250Q51 263 57 269T82 276H144V347Q144 355 144 361T145 376H82Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="599" d="M107 255Q89 255 83 261T76 281V287Q76 300 82 306T107 313H246L57 622Q56 624 59 628T68 638T83 647T103 651Q123 651 136 628L299 335L466 627Q482 651 500 651Q509 651 517 647T530 638T539 629T541 622L351 313H491Q509 313 515 307T522 287V281Q522 268 516 262T491 255H330V175H491Q509 175 515 169T522 149V143Q522 130 516 124T491 117H330V27Q330 -4 302 -4H295Q268 -4 268 27V117H107Q89 117 83 123T76 143V149Q76 162 82 168T107 175H268V255H107Z" />
+<glyph unicode="&#xA6;" glyph-name="brokenbar" horiz-adv-x="273" d="M133 299Q120 299 112 305T104 330V620Q104 638 112 644T133 651H140Q153 651 161 645T169 620V330Q169 312 161 306T140 299H133ZM133 -197Q120 -197 112 -191T104 -166V124Q104 142 112 148T133 155H140Q153 155 161 149T169 124V-166Q169 -184 161 -190T140 -197H133Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="550" d="M269 -160Q210 -160 168 -147T98 -115T58 -75T45 -38Q45 -28 50 -20T63 -7T76 1T82 2Q108 -46 152 -73T268 -100Q338 -100 378 -77T418 -12Q418 14 407 30T375 57T324 75T254 92Q218 100 183 110T119 138T72 181T54 248Q54 296 83 328T164 375Q123 392 98 420T73 501Q73 536 89 565T133 615T198 647T279 659Q331 659 368 647T430 618T466 581T478 548Q478 538 473 530T461 517T449 510T442 509Q415 551 377 575T279 599Q251 599 226 593T181 576T150 547T138 509Q138 464 179 442T297 403Q333 395 368 385T432 358T479 315T497 249Q497 201 468 169T390 121Q430 106 456 77T483 -7Q483 -42 468 -70T424 -118T356 -149T269 -160ZM269 346Q260 348 251 349T233 353Q207 350 186 340T149 317T126 287T117 255Q117 229 130 212T165 184T218 164T282 149L316 141Q341 143 362 152T400 176T425 207T434 242Q434 267 421 283T386 311T333 330T269 346Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="530" d="M350 548Q327 548 314 561T300 597Q300 620 313 633T348 646Q371 646 384 633T398 597Q398 574 385 561T350 548ZM182 548Q159 548 146 561T132 597Q132 620 145 633T180 646Q203 646 216 633T230 597Q230 574 217 561T182 548Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="787" d="M393 -12Q321 -12 261 13T157 82T89 187T65 322Q65 394 90 456T159 563T263 633T394 659Q466 659 526 634T629 565T697 460T722 325Q722 253 697 191T628 84T524 14T393 -12ZM395 36Q455 36 505 57T592 116T648 206T668 323Q668 385 647 437T589 528T502 589T392 611Q332 611 282 590T195 531T139 441T119 324Q119 262 140 210T198 119T285 58T395 36ZM402 144Q363 144 331 157T275 193T238 250T225 322Q225 360 238 393T276 450T332 487T401 501Q441 501 470 488T515 459Q534 440 534 421Q534 412 529 406T518 395T507 390T500 390Q486 416 461 430T400 445Q377 445 357 436T320 410T295 371T286 323Q286 296 295 274T320 235T357 209T404 200Q441 200 466 217T504 260Q505 261 510 260T522 255T534 245T539 229Q539 210 515 185Q500 169 471 157T402 144Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="457" d="M195 258Q127 258 89 285T51 363Q51 391 62 414T104 454T187 482T322 498Q322 549 297 577T216 605Q173 605 145 585T100 537Q100 536 95 537T84 541T72 551T67 567Q67 578 76 593T104 623T152 648T221 659Q304 659 343 616T383 500V270Q383 268 377 266T362 264Q347 264 335 274T323 320Q302 291 268 275T195 258ZM215 -6Q175 -6 175 34V42Q175 83 215 83H223Q264 83 264 42V34Q264 -6 223 -6H215ZM203 308Q228 308 250 316T288 337T313 366T323 401V455Q255 450 214 442T150 423T120 397T112 366Q112 339 136 324T203 308Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="537" d="M447 78Q433 78 413 89T372 117T330 155T293 194T266 225T256 243Q256 246 266 259T292 291T330 330T372 368T413 396T447 408H448Q459 408 465 401T472 385V383Q472 373 459 358T425 324T375 284T316 242Q347 221 375 201T424 162T459 128T472 103V100Q472 92 466 85T448 78H447ZM233 94Q213 94 184 115T127 163T80 214T60 243Q60 246 69 258T93 286T127 321T165 355T202 381T233 391H234Q243 391 249 385T255 370V369Q255 360 244 346T213 316T169 280T117 242Q144 223 169 205T213 170T243 139T255 116V115Q255 107 249 101T234 94H233Z" />
+<glyph unicode="&#xAC;" glyph-name="logicalnot" horiz-adv-x="600" d="M49 380H543V96H453V297H49V380Z" />
+<glyph unicode="&#xAD;" glyph-name="uni00AD" horiz-adv-x="396" d="M64 272H332V212H64V272Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="787" d="M393 -12Q321 -12 261 13T157 82T89 187T65 322Q65 394 90 456T159 563T263 633T394 659Q466 659 526 634T629 565T697 460T722 325Q722 253 697 191T628 84T524 14T393 -12ZM395 36Q455 36 505 57T592 116T648 206T668 323Q668 385 647 437T589 528T502 589T392 611Q332 611 282 590T195 531T139 441T119 324Q119 262 140 210T198 119T285 58T395 36ZM500 151Q492 151 481 155T459 173L371 283H328V179Q328 151 302 151H294Q268 151 268 183V464Q268 480 277 489T300 498H406Q471 498 505 472T540 391Q540 346 513 319T436 287L533 176Q535 173 526 162T500 151ZM327 333H407Q442 333 461 348T480 390Q480 447 404 447H327V333Z" />
+<glyph unicode="&#xAF;" glyph-name="uni00AF" horiz-adv-x="530" d="M161 568Q122 568 122 597V602Q122 616 132 623T161 630H369Q408 630 408 602V597Q408 568 369 568H161Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="438" d="M217 346Q184 346 157 357T109 389T78 438T66 499V508Q66 541 78 569T111 617T160 650T221 662Q254 662 281 651T329 619T360 570T372 509V500Q372 467 360 439T327 391T278 358T217 346ZM220 399Q262 399 289 427T317 499V508Q317 529 309 547T288 579T256 601T218 609Q176 609 149 581T121 509V500Q121 479 129 461T150 429T182 407T220 399Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="630" d="M143 411Q125 411 119 417T112 437V443Q112 456 118 462T143 469H284V612Q284 630 291 636T312 643H317Q330 643 337 637T345 612V469H486Q504 469 510 463T517 443V437Q517 424 511 418T486 411H345V257Q345 239 338 233T317 226H312Q299 226 292 232T284 257V411H143ZM133 119Q115 119 109 126T102 146V153Q102 166 108 173T133 180H496Q514 180 520 173T527 153V146Q527 133 521 126T496 119H133Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="366" d="M209 430Q190 421 172 413T140 395T118 371T108 339H290Q320 339 320 314V311Q320 285 290 285H93Q71 285 63 293T54 319V334Q54 362 63 382T87 418T123 444T165 467Q182 476 198 483T226 500T245 519T252 544Q252 570 234 585T182 601Q144 601 125 580T98 533Q98 532 91 532T76 534T61 543T54 562Q54 577 62 593T88 623T130 646T187 655Q248 655 281 626T315 545Q315 521 306 504T283 473T249 449T209 430Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="366" d="M315 387Q315 336 278 307T173 278Q140 278 115 286T73 306T47 332T38 359Q38 371 44 378T57 388T70 392T76 391Q86 361 111 345T172 329Q210 329 234 345T258 390Q258 419 236 434T175 450H151Q134 450 128 456T121 473V477Q121 499 150 499H182Q213 499 231 513T250 550Q250 575 228 589T172 604Q137 604 116 588T86 549Q86 548 80 548T67 552T55 562T49 581Q49 593 57 606T82 630T121 648T173 655Q238 655 272 630T306 556Q306 497 245 479Q315 456 315 387Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="530" d="M194 551Q192 553 202 567T231 600T272 640T319 679Q343 697 365 697Q377 697 386 690T395 670Q395 645 358 620Q333 603 305 589T253 566T213 553T194 551Z" />
+<glyph unicode="&#xB5;" glyph-name="uni00B5" horiz-adv-x="575" d="M267 -12Q220 -12 184 3Q161 13 143 27V-290H78V476H143V191Q145 127 176 89Q210 49 271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="648" d="M517 -4Q488 -4 488 27V620Q488 651 517 651H526Q556 651 556 620V27Q556 -4 526 -4H517ZM392 -4Q377 -4 370 5T362 27V290H283Q168 290 108 334T48 466Q48 555 105 601T278 648H392Q408 648 419 637T430 610V27Q430 -4 401 -4H392Z" />
+<glyph unicode="&#xB7;" glyph-name="uni00B7" horiz-adv-x="251" d="M120 193Q76 193 76 237V247Q76 268 87 280T120 292H130Q152 292 163 280T175 247V237Q175 193 130 193H120Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="530" d="M207 -225Q204 -228 197 -229T184 -228T175 -222T174 -209L234 -84Q242 -68 248 -59T274 -50H279Q293 -50 303 -57T314 -79V-83Q314 -91 307 -100T288 -124L207 -225Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="366" d="M93 285Q64 285 64 310V312Q64 339 93 339H165V567Q146 546 115 546Q94 546 77 557T60 589Q60 605 68 613T79 621Q94 604 119 604Q139 604 155 617T173 651H196Q225 651 225 620V339H285Q315 339 315 312V310Q315 285 285 285H93Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="509" d="M255 257Q213 257 177 272T115 315T74 378T59 457Q59 499 74 536T116 600T178 643T255 659Q297 659 332 644T394 601T435 538T450 459Q450 417 435 380T393 316T331 273T255 257ZM249 -6Q209 -6 209 34V42Q209 83 249 83H257Q298 83 298 42V34Q298 -6 257 -6H249ZM255 311Q284 311 309 322T352 353T380 399T390 458Q390 489 380 516T351 562T308 593T255 605Q226 605 201 594T158 563T130 516T119 458Q119 427 129 401T158 354T201 323T255 311Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="537" d="M303 95Q294 95 288 101T282 116V117Q282 126 293 140T324 170T368 206T420 244Q393 263 368 281T324 316T294 347T282 370V371Q282 379 288 385T303 392H304Q317 392 335 382T372 357T410 323T444 288T468 259T477 243Q477 240 468 228T444 200T410 165T372 131T335 105T304 95H303ZM89 78Q78 78 72 85T65 101V103Q65 113 78 128T112 162T162 202T221 244Q190 265 162 285T113 324T78 358T65 383V386Q65 394 71 401T89 408H90Q104 408 124 397T165 369T207 331T244 292T271 261T281 243Q281 240 271 227T245 195T207 156T165 118T124 90T90 78H89Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="822" d="M699 -3Q686 -3 680 5T673 27V95H527Q509 95 499 103T488 124V137Q489 147 493 154T505 175L629 338Q639 354 649 360T684 366Q710 366 720 357T730 324V146H768Q797 146 797 122V119Q797 95 768 95H730V27Q730 -3 703 -3H699ZM164 -5Q164 9 168 18T188 48Q195 58 217 84T269 147T333 223T397 302Q412 321 431 345T472 395T515 450T559 504Q608 566 660 632Q668 632 674 630Q679 628 684 624T689 610Q689 599 684 588T664 557Q657 548 635 521T582 458T519 381T455 302Q440 283 421 259T380 209T337 154T294 100Q246 38 194 -27Q193 -28 188 -28T178 -26T169 -18T164 -5ZM93 285Q64 285 64 310V312Q64 339 93 339H165V567Q146 546 115 546Q94 546 77 557T60 589Q60 605 68 613T79 621Q94 604 119 604Q139 604 155 617T173 651H196Q225 651 225 620V339H285Q315 339 315 312V310Q315 285 285 285H93ZM544 145H674L673 316L544 145Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="822" d="M665 145Q646 136 628 128T596 110T574 86T564 54H746Q776 54 776 29V26Q776 0 746 0H549Q527 0 519 8T510 34V49Q510 77 519 97T543 133T579 159T621 182Q638 191 654 198T682 215T701 234T708 259Q708 285 690 300T638 316Q600 316 581 295T554 248Q554 247 547 247T532 249T517 258T510 277Q510 292 518 308T544 338T586 361T643 370Q704 370 737 341T771 260Q771 236 762 219T739 188T705 164T665 145ZM134 -5Q134 9 138 18T158 48Q165 58 187 84T239 147T303 223T367 302Q382 321 401 345T442 395T485 450T529 504Q578 566 630 632Q638 632 644 630Q649 628 654 624T659 610Q659 599 654 588T634 557Q627 548 605 521T552 458T489 381T425 302Q410 283 391 259T350 209T307 154T264 100Q216 38 164 -27Q163 -28 158 -28T148 -26T139 -18T134 -5ZM93 285Q64 285 64 310V312Q64 339 93 339H165V567Q146 546 115 546Q94 546 77 557T60 589Q60 605 68 613T79 621Q94 604 119 604Q139 604 155 617T173 651H196Q225 651 225 620V339H285Q315 339 315 312V310Q315 285 285 285H93Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="822" d="M699 -3Q686 -3 680 5T673 27V95H527Q509 95 499 103T488 124V137Q489 147 493 154T505 175L629 338Q639 354 649 360T684 366Q710 366 720 357T730 324V146H768Q797 146 797 122V119Q797 95 768 95H730V27Q730 -3 703 -3H699ZM315 387Q315 336 278 307T173 278Q140 278 115 286T73 306T47 332T38 359Q38 371 44 378T57 388T70 392T76 391Q86 361 111 345T172 329Q210 329 234 345T258 390Q258 419 236 434T175 450H151Q134 450 128 456T121 473V477Q121 499 150 499H182Q213 499 231 513T250 550Q250 575 228 589T172 604Q137 604 116 588T86 549Q86 548 80 548T67 552T55 562T49 581Q49 593 57 606T82 630T121 648T173 655Q238 655 272 630T306 556Q306 497 245 479Q315 456 315 387ZM164 -5Q164 9 168 18T188 48Q195 58 217 84T269 147T333 223T397 302Q412 321 431 345T472 395T515 450T559 504Q608 566 660 632Q668 632 674 630Q679 628 684 624T689 610Q689 599 684 588T664 557Q657 548 635 521T582 458T519 381T455 302Q440 283 421 259T380 209T337 154T294 100Q246 38 194 -27Q193 -28 188 -28T178 -26T169 -18T164 -5ZM544 145H674L673 316L544 145Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="493" d="M260 557Q239 557 231 565T222 594V614Q222 634 230 642T260 651H280Q300 651 308 643T317 614V594Q317 573 309 565T280 557H260ZM239 -12Q194 -12 158 0T97 35T59 87T45 152Q45 182 53 204T76 244T108 275T144 302Q163 315 181 329T213 360T236 399T245 453Q245 461 253 465T273 470Q286 470 294 465T301 449Q299 408 289 379T264 329T229 292T191 264Q159 243 136 221T112 158Q112 107 145 78T241 48Q280 48 307 60T352 91T379 135T393 184Q393 185 401 185T418 180T434 167T442 144Q442 125 431 98T395 47Q370 22 332 5T239 -12Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM360 721Q358 719 342 723T301 736T249 759T196 790Q159 815 159 840Q159 852 168 859T189 867Q211 867 235 849Q259 832 282 811T323 770T351 737T360 721Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM268 721Q266 723 276 737T305 770T346 810T393 849Q417 867 439 867Q451 867 460 860T469 840Q469 815 432 790Q407 773 379 759T327 736T287 723T268 721Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM210 719Q188 719 188 742Q188 760 208 785T252 833T296 873T319 889Q321 889 341 873T386 834T429 785T449 742Q449 719 428 719Q411 719 387 744T318 831Q295 800 279 779T250 745T228 725T210 719Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM206 725Q181 725 181 751Q181 767 186 783T200 812T222 833T250 841Q274 841 314 820T398 761Q402 774 407 787T418 810T430 827T444 834Q469 834 469 808Q469 791 464 775T450 747T428 726T400 718Q376 718 336 739T252 798Q248 785 243 772T232 749T220 732T206 725Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM404 718Q381 718 368 731T354 767Q354 790 367 803T402 816Q425 816 438 803T452 767Q452 744 439 731T404 718ZM236 718Q213 718 200 731T186 767Q186 790 199 803T234 816Q257 816 270 803T284 767Q284 744 271 731T236 718Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="639" d="M435 719Q435 684 418 658T373 617L607 16Q609 12 597 4T568 -4Q556 -4 545 2T527 27L456 211H178L108 27Q101 9 91 3T69 -4Q54 -4 42 4T31 16L263 616Q234 630 218 657T202 719Q202 744 211 765T235 802T272 827T319 836Q344 836 365 827T402 803T426 766T435 719ZM200 271H434L317 577L200 271ZM318 647Q347 647 366 667T385 719Q385 750 366 771T318 792Q289 792 270 771T251 719Q251 689 270 668T318 647Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="874" d="M577 0Q551 0 540 9T521 38L457 211H178L108 27Q100 6 87 1T60 -3T39 6T31 16L264 617Q269 630 279 638T307 647H812V585H384Q391 568 398 550Q404 534 411 514T427 474Q445 425 470 363H741V301H495L587 62H818V0H577ZM435 271L318 576L201 271H435Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="690" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T389 659Q428 659 461 652T520 634T567 609T602 581Q630 553 630 531Q630 518 624 510T611 497T597 491T590 490Q575 517 552 537T502 572T446 593T387 600Q331 600 285 580T206 523T154 436T135 324Q135 264 154 213T207 125T287 68T389 47Q425 47 458 56T518 81T566 121T598 174Q598 175 605 174T620 169T635 156T642 134Q642 122 632 104T604 68Q574 38 519 13T386 -12ZM312 -225Q309 -228 302 -229T289 -228T280 -222T279 -209L339 -84Q347 -68 353 -59T379 -50H384Q398 -50 408 -57T419 -79V-83Q419 -91 412 -100T393 -124L312 -225Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM391 721Q389 719 373 723T332 736T280 759T227 790Q190 815 190 840Q190 852 199 859T220 867Q242 867 266 849Q290 832 313 811T354 770T382 737T391 721Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM242 721Q240 723 250 737T279 770T320 810T367 849Q391 867 413 867Q425 867 434 860T443 840Q443 815 406 790Q381 773 353 759T301 736T261 723T242 721Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM211 719Q189 719 189 742Q189 760 209 785T253 833T297 873T320 889Q322 889 342 873T387 834T430 785T450 742Q450 719 429 719Q412 719 388 744T319 831Q296 800 280 779T251 745T229 725T211 719Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM405 718Q382 718 369 731T355 767Q355 790 368 803T403 816Q426 816 439 803T453 767Q453 744 440 731T405 718ZM237 718Q214 718 201 731T187 767Q187 790 200 803T235 816Q258 816 271 803T285 767Q285 744 272 731T237 718Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM187 721Q185 719 169 723T128 736T76 759T23 790Q-14 815 -14 840Q-14 852 -5 859T16 867Q38 867 62 849Q86 832 109 811T150 770T178 737T187 721Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM85 721Q83 723 93 737T122 770T163 810T210 849Q234 867 256 867Q268 867 277 860T286 840Q286 815 249 790Q224 773 196 759T144 736T104 723T85 721Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM27 719Q5 719 5 742Q5 760 25 785T69 833T113 873T136 889Q138 889 158 873T203 834T246 785T266 742Q266 719 245 719Q228 719 204 744T135 831Q112 800 96 779T67 745T45 725T27 719Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM221 718Q198 718 185 731T171 767Q171 790 184 803T219 816Q242 816 255 803T269 767Q269 744 256 731T221 718ZM53 718Q30 718 17 731T3 767Q3 790 16 803T51 816Q74 816 87 803T101 767Q101 744 88 731T53 718Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="747" d="M62 294Q44 294 38 301T31 321V327Q31 340 37 347T62 354H127V609Q127 625 138 636T165 647H323Q406 647 472 625T585 562T657 460T682 325Q682 249 657 189T584 87T469 23T318 0H165Q149 0 138 11T127 38V294H62ZM327 62Q466 62 539 130T613 323Q613 447 539 516T328 585H195V354H409Q427 354 433 347T440 327V321Q440 308 434 301T409 294H195V62H327Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="705" d="M92 605Q92 624 101 637T129 651H146Q165 651 175 642T198 615L549 106V647H613V42Q613 22 604 9T578 -4H573Q554 -4 547 2T529 22L156 561V0H92V605ZM234 725Q209 725 209 751Q209 767 214 783T228 812T250 833T278 841Q302 841 342 820T426 761Q430 774 435 787T446 810T458 827T472 834Q497 834 497 808Q497 791 492 775T478 747T456 726T428 718Q404 718 364 739T280 798Q276 785 271 772T260 749T248 732T234 725Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM438 721Q436 719 420 723T379 736T327 759T274 790Q237 815 237 840Q237 852 246 859T267 867Q289 867 313 849Q337 832 360 811T401 770T429 737T438 721Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM336 721Q334 723 344 737T373 770T414 810T461 849Q485 867 507 867Q519 867 528 860T537 840Q537 815 500 790Q475 773 447 759T395 736T355 723T336 721Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM278 719Q256 719 256 742Q256 760 276 785T320 833T364 873T387 889Q389 889 409 873T454 834T497 785T517 742Q517 719 496 719Q479 719 455 744T386 831Q363 800 347 779T318 745T296 725T278 719Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM269 725Q244 725 244 751Q244 767 249 783T263 812T285 833T313 841Q337 841 377 820T461 761Q465 774 470 787T481 810T493 827T507 834Q532 834 532 808Q532 791 527 775T513 747T491 726T463 718Q439 718 399 739T315 798Q311 785 306 772T295 749T283 732T269 725Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM472 718Q449 718 436 731T422 767Q422 790 435 803T470 816Q493 816 506 803T520 767Q520 744 507 731T472 718ZM304 718Q281 718 268 731T254 767Q254 790 267 803T302 816Q325 816 338 803T352 767Q352 744 339 731T304 718Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="569" d="M118 94Q98 112 120 136L246 266L120 394Q98 418 118 436L122 440Q130 449 140 449T165 436L287 308L409 436Q423 450 433 450T452 440L456 436Q476 418 454 394L328 266L454 136Q476 112 456 94L452 90Q444 81 434 81T409 94L287 223L165 94Q151 80 141 80T122 90L118 94Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="774" d="M94 -42Q92 -44 87 -42T76 -35T66 -23T61 -6Q61 4 66 15T85 43L138 102Q103 146 84 201T65 322Q65 393 88 454T154 561T256 633T388 659Q447 659 496 642T586 593L667 683Q668 684 673 682T685 676T695 664T700 647Q700 637 695 626T676 598L632 549Q669 504 689 447T709 324Q709 253 686 192T620 85T518 14T386 -12Q325 -12 274 6T183 57L94 -42ZM388 50Q446 50 492 70T572 127T623 213T641 322Q641 428 584 502L227 105Q259 79 299 65T388 50ZM133 324Q133 273 146 229T186 149L542 545Q511 570 472 583T386 597Q328 597 282 577T202 519T151 432T133 324Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM394 711Q392 709 376 713T335 726T283 749T230 780Q193 805 193 830Q193 842 202 849T223 857Q245 857 269 839Q293 822 316 801T357 760T385 727T394 711Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM282 711Q280 713 290 727T319 760T360 800T407 839Q431 857 453 857Q465 857 474 850T483 830Q483 805 446 780Q421 763 393 749T341 726T301 713T282 711Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM224 719Q202 719 202 742Q202 760 222 785T266 833T310 873T333 889Q335 889 355 873T400 834T443 785T463 742Q463 719 442 719Q425 719 401 744T332 831Q309 800 293 779T264 745T242 725T224 719Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM418 718Q395 718 382 731T368 767Q368 790 381 803T416 816Q439 816 452 803T466 767Q466 744 453 731T418 718ZM250 718Q227 718 214 731T200 767Q200 790 213 803T248 816Q271 816 284 803T298 767Q298 744 285 731T250 718Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="579" d="M255 269L25 647H100L290 331L479 627Q491 646 504 649T529 648T547 635T553 622L323 269V0H255V269ZM228 711Q226 713 236 727T265 760T306 800T353 839Q377 857 399 857Q411 857 420 850T429 830Q429 805 392 780Q367 763 339 749T287 726T247 713T228 711Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="588" d="M121 -4Q92 -4 92 27V620Q92 651 121 651H130Q160 651 160 620V524H296Q410 524 472 473T534 328Q534 231 472 180T288 129H160V27Q160 -4 130 -4H121ZM160 463V189H294Q379 189 421 224T464 326Q464 463 297 463H160Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="556" d="M327 -12Q283 -12 254 -2T210 24Q194 40 194 56Q194 65 198 72T206 85T215 92T220 94Q238 74 262 60T329 45Q353 45 374 54T410 80T435 119T445 167Q445 192 439 216T415 260T369 298T292 327Q260 335 248 348T236 381Q236 396 242 407T253 422Q331 421 370 445T410 526Q410 574 377 602T285 630Q219 630 183 591T147 467V0H82V468Q82 577 137 633T286 689Q328 689 362 678T422 645T460 595T474 531Q474 392 312 380Q369 366 407 345T468 297T500 237T510 167Q510 130 496 98T457 41T399 2T327 -12Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM338 551Q336 549 320 553T279 566T227 589T174 620Q137 645 137 670Q137 682 146 689T167 697Q189 697 213 679Q237 662 260 641T301 600T329 567T338 551Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM196 551Q194 553 204 567T233 600T274 640T321 679Q345 697 367 697Q379 697 388 690T397 670Q397 645 360 620Q335 603 307 589T255 566T215 553T196 551Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM158 549Q136 549 136 572Q136 590 156 615T200 663T244 703T267 719Q269 719 289 703T334 664T377 615T397 572Q397 549 376 549Q359 549 335 574T266 661Q243 630 227 609T198 575T176 555T158 549Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM149 555Q124 555 124 581Q124 597 129 613T143 642T165 663T193 671Q217 671 257 650T341 591Q345 604 350 617T361 640T373 657T387 664Q412 664 412 638Q412 621 407 605T393 577T371 556T343 548Q319 548 279 569T195 628Q191 615 186 602T175 579T163 562T149 555Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM352 548Q329 548 316 561T302 597Q302 620 315 633T350 646Q373 646 386 633T400 597Q400 574 387 561T352 548ZM184 548Q161 548 148 561T134 597Q134 620 147 633T182 646Q205 646 218 633T232 597Q232 574 219 561T184 548Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM266 548Q240 548 219 557T183 581T159 618T150 665Q150 690 159 711T183 748T220 773T267 782Q292 782 313 773T350 749T374 712T383 665Q383 640 374 619T350 582T313 557T266 548ZM266 593Q295 593 314 613T333 665Q333 696 314 717T266 738Q237 738 218 717T199 665Q199 635 218 614T266 593Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="895" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 391 82 409T119 445T180 475T264 488Q330 488 372 460T434 382Q463 432 511 460T624 488Q674 488 715 471T785 423T830 347T846 248V242Q846 228 840 224T819 220H449Q451 182 465 150T503 95T559 59T630 46Q665 46 691 53T736 71T768 98T793 131Q794 132 800 131T813 126T825 114T831 94Q831 82 818 64T779 29T716 0T630 -12Q554 -12 501 20T421 110Q395 56 341 23T216 -11ZM222 43Q259 43 290 54T344 84T380 130T393 189V233L365 232Q242 227 180 203T118 122Q118 86 145 65T222 43ZM624 431Q590 431 561 420T509 388T471 339T451 276H777Q772 347 732 389T624 431Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="548" d="M295 -12Q242 -12 198 7T120 59T69 139T50 239Q50 293 68 338T120 417T197 469T295 488Q356 488 397 469T465 424Q482 407 491 389T500 356Q500 345 494 339T479 329T465 324T457 325Q448 347 434 366T401 399T355 422T295 430Q257 430 224 416T167 377T130 316T116 239Q116 197 129 162T167 101T223 61T295 46Q329 46 355 54T400 76T434 110T457 151Q458 152 464 152T479 148T493 138T500 120Q500 106 491 88T463 50Q438 25 397 7T295 -12ZM232 -225Q229 -228 222 -229T209 -228T200 -222T199 -209L259 -84Q267 -68 273 -59T299 -50H304Q318 -50 328 -57T339 -79V-83Q339 -91 332 -100T313 -124L232 -225Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM346 551Q344 549 328 553T287 566T235 589T182 620Q145 645 145 670Q145 682 154 689T175 697Q197 697 221 679Q245 662 268 641T309 600T337 567T346 551Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM219 551Q217 553 227 567T256 600T297 640T344 679Q368 697 390 697Q402 697 411 690T420 670Q420 645 383 620Q358 603 330 589T278 566T238 553T219 551Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM181 549Q159 549 159 572Q159 590 179 615T223 663T267 703T290 719Q292 719 312 703T357 664T400 615T420 572Q420 549 399 549Q382 549 358 574T289 661Q266 630 250 609T221 575T199 555T181 549Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM375 548Q352 548 339 561T325 597Q325 620 338 633T373 646Q396 646 409 633T423 597Q423 574 410 561T375 548ZM207 548Q184 548 171 561T157 597Q157 620 170 633T205 646Q228 646 241 633T255 597Q255 574 242 561T207 548Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM175 551Q173 549 157 553T116 566T64 589T11 620Q-26 645 -26 670Q-26 682 -17 689T4 697Q26 697 50 679Q74 662 97 641T138 600T166 567T175 551Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM73 551Q71 553 81 567T110 600T151 640T198 679Q222 697 244 697Q256 697 265 690T274 670Q274 645 237 620Q212 603 184 589T132 566T92 553T73 551Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM15 549Q-7 549 -7 572Q-7 590 13 615T57 663T101 703T124 719Q126 719 146 703T191 664T234 615T254 572Q254 549 233 549Q216 549 192 574T123 661Q100 630 84 609T55 575T33 555T15 549Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM209 548Q186 548 173 561T159 597Q159 620 172 633T207 646Q230 646 243 633T257 597Q257 574 244 561T209 548ZM41 548Q18 548 5 561T-9 597Q-9 620 4 633T39 646Q62 646 75 633T89 597Q89 574 76 561T41 548Z" />
+<glyph unicode="&#xF0;" glyph-name="null" horiz-adv-x="595" d="M220 491Q205 482 196 484T181 498L179 502Q172 513 174 522T192 540L293 595Q211 635 118 635Q115 635 115 643T120 662T136 681T167 690Q207 690 257 675T358 628L464 689Q480 698 489 696T504 682L506 679Q513 668 511 659T492 641L404 593Q432 569 456 537T497 466T525 379T535 274Q535 206 517 153T467 63T390 7T290 -12Q238 -12 195 3T122 48T75 118T58 213Q58 263 75 303T123 373T198 417T294 433Q322 433 348 427T397 409T438 383T468 353Q457 423 425 475T345 563L220 491ZM291 45Q334 45 367 61T422 106T456 174T468 258V286Q459 302 442 318T403 347T354 368T299 376Q213 376 169 334T124 214Q124 132 169 89T291 45Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="575" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V402Q171 440 211 464T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 399 386T304 427Q248 427 208 399T147 326V0H82V473ZM163 555Q138 555 138 581Q138 597 143 613T157 642T179 663T207 671Q231 671 271 650T355 591Q359 604 364 617T375 640T387 657T401 664Q426 664 426 638Q426 621 421 605T407 577T385 556T357 548Q333 548 293 569T209 628Q205 615 200 602T189 579T177 562T163 555Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM350 551Q348 549 332 553T291 566T239 589T186 620Q149 645 149 670Q149 682 158 689T179 697Q201 697 225 679Q249 662 272 641T313 600T341 567T350 551Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM248 551Q246 553 256 567T285 600T326 640T373 679Q397 697 419 697Q431 697 440 690T449 670Q449 645 412 620Q387 603 359 589T307 566T267 553T248 551Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM190 549Q168 549 168 572Q168 590 188 615T232 663T276 703T299 719Q301 719 321 703T366 664T409 615T429 572Q429 549 408 549Q391 549 367 574T298 661Q275 630 259 609T230 575T208 555T190 549Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM181 555Q156 555 156 581Q156 597 161 613T175 642T197 663T225 671Q249 671 289 650T373 591Q377 604 382 617T393 640T405 657T419 664Q444 664 444 638Q444 621 439 605T425 577T403 556T375 548Q351 548 311 569T227 628Q223 615 218 602T207 579T195 562T181 555Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM384 548Q361 548 348 561T334 597Q334 620 347 633T382 646Q405 646 418 633T432 597Q432 574 419 561T384 548ZM216 548Q193 548 180 561T166 597Q166 620 179 633T214 646Q237 646 250 633T264 597Q264 574 251 561T216 548Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="630" d="M309 379Q265 379 265 423V433Q265 454 276 466T309 478H319Q341 478 352 466T364 433V423Q364 379 319 379H309ZM309 59Q265 59 265 103V113Q265 134 276 146T309 158H319Q341 158 352 146T364 113V103Q364 59 319 59H309ZM133 238Q115 238 109 245T102 265V272Q102 285 108 292T133 299H496Q514 299 520 292T527 272V265Q527 252 521 245T496 238H133Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="592" d="M86 -36Q85 -37 80 -36T70 -30T61 -20T56 -6Q56 2 60 13T78 38L113 76Q87 108 73 149T58 237Q58 289 77 335T129 415T206 468T300 488Q342 488 378 476T445 440L509 511Q510 512 515 511T525 505T534 495T539 481Q539 473 535 462T517 437L485 402Q511 369 526 328T541 239Q541 186 522 141T470 61T393 8T299 -12Q257 -12 220 1T152 37L86 -36ZM404 392Q382 409 356 419T299 429Q261 429 229 415T174 375T137 314T124 238Q124 205 133 176T159 122L404 392ZM300 47Q338 47 370 61T425 101T462 162T475 238Q475 305 439 355L193 85Q240 47 300 47Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM338 551Q336 549 320 553T279 566T227 589T174 620Q137 645 137 670Q137 682 146 689T167 697Q189 697 213 679Q237 662 260 641T301 600T329 567T338 551Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM246 551Q244 553 254 567T283 600T324 640T371 679Q395 697 417 697Q429 697 438 690T447 670Q447 645 410 620Q385 603 357 589T305 566T265 553T246 551Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM180 549Q158 549 158 572Q158 590 178 615T222 663T266 703T289 719Q291 719 311 703T356 664T399 615T419 572Q419 549 398 549Q381 549 357 574T288 661Q265 630 249 609T220 575T198 555T180 549Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM375 548Q352 548 339 561T325 597Q325 620 338 633T373 646Q396 646 409 633T423 597Q423 574 410 561T375 548ZM207 548Q184 548 171 561T157 597Q157 620 170 633T205 646Q228 646 241 633T255 597Q255 574 242 561T207 548Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="502" d="M198 -197Q184 -197 174 -190T164 -180L244 24Q227 24 219 29T204 49L24 476H92L262 65L410 476H479L244 -154Q234 -180 223 -188T198 -197ZM200 541Q198 543 208 557T237 590T278 630T325 669Q349 687 371 687Q383 687 392 680T401 660Q401 635 364 610Q339 593 311 579T259 556T219 543T200 541Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="609" d="M320 -12Q288 -12 261 -5T212 16T174 45T147 79V-166Q147 -184 139 -190T118 -197H111Q98 -197 90 -191T82 -166V674Q82 676 88 678T104 681Q121 681 134 669T147 618V398Q179 441 221 464T324 488Q371 488 412 471T483 421T531 342T549 239Q549 183 532 137T484 57T411 6T320 -12ZM315 45Q353 45 384 59T437 98T471 159T483 238Q483 280 471 315T436 376T384 416T317 431Q254 431 212 401T147 320V189Q150 158 165 132T202 86T254 56T315 45Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="502" d="M198 -197Q184 -197 174 -190T164 -180L244 24Q227 24 219 29T204 49L24 476H92L262 65L410 476H479L244 -154Q234 -180 223 -188T198 -197ZM340 548Q317 548 304 561T290 597Q290 620 303 633T338 646Q361 646 374 633T388 597Q388 574 375 561T340 548ZM172 548Q149 548 136 561T122 597Q122 620 135 633T170 646Q193 646 206 633T220 597Q220 574 207 561T172 548Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM215 739Q176 739 176 768V773Q176 787 186 794T215 801H423Q462 801 462 773V768Q462 739 423 739H215Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM173 568Q134 568 134 597V602Q134 616 144 623T173 630H381Q420 630 420 602V597Q420 568 381 568H173Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H537L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434ZM320 719Q299 719 274 730T228 757T193 791T179 824Q179 853 208 853Q215 853 228 844T256 822T288 792T322 762Q337 776 353 792T384 821T411 844T432 853Q461 853 461 824Q461 809 447 791T411 756T365 730T320 719Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H394V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43ZM274 548Q253 548 228 559T182 586T147 620T133 653Q133 682 162 682Q169 682 182 673T210 651T242 621T276 591Q291 605 307 621T338 650T365 673T386 682Q415 682 415 653Q415 638 401 620T365 585T319 559T274 548Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="639" d="M268 628Q273 641 284 647T319 653Q343 653 354 647T370 628L613 0H591Q548 -32 534 -55T519 -98Q519 -115 529 -125T553 -136Q579 -136 592 -115Q603 -115 613 -123T624 -149Q624 -169 604 -181T552 -193Q510 -193 485 -171T459 -111Q459 -82 477 -53T536 3L456 211H179L98 0H25L268 628ZM434 271L318 576L201 271H434Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="535" d="M216 -11Q138 -11 95 25T52 122Q52 206 134 243T383 284H393V301Q393 363 359 395T259 428Q201 428 164 403T107 340Q107 340 101 341T89 345T76 357T70 378Q70 387 77 400T96 426Q119 449 161 468T264 488Q355 488 406 439T457 303V0H453Q406 -34 391 -58T375 -103Q375 -120 385 -130T409 -141Q435 -141 448 -120Q459 -120 469 -128T480 -154Q480 -174 460 -186T408 -198Q366 -198 341 -176T315 -116Q315 -87 333 -57T394 0V73Q366 34 320 12T216 -11ZM222 43Q259 43 290 54T344 86T380 132T393 189V233H380Q246 228 182 204T118 122Q118 86 145 65T222 43Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="690" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T389 659Q428 659 461 652T520 634T567 609T602 581Q630 553 630 531Q630 518 624 510T611 497T597 491T590 490Q575 517 552 537T502 572T446 593T387 600Q331 600 285 580T206 523T154 436T135 324Q135 264 154 213T207 125T287 68T389 47Q425 47 458 56T518 81T566 121T598 174Q598 175 605 174T620 169T635 156T642 134Q642 122 632 104T604 68Q574 38 519 13T386 -12ZM339 722Q337 724 347 738T376 771T417 811T464 850Q488 868 510 868Q522 868 531 861T540 841Q540 816 503 791Q478 774 450 760T398 737T358 724T339 722Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="548" d="M295 -12Q242 -12 198 7T120 59T69 139T50 239Q50 293 68 338T120 417T197 469T295 488Q356 488 397 469T465 424Q482 407 491 389T500 356Q500 345 494 339T479 329T465 324T457 325Q448 347 434 366T401 399T355 422T295 430Q257 430 224 416T167 377T130 316T116 239Q116 197 129 162T167 101T223 61T295 46Q329 46 355 54T400 76T434 110T457 151Q458 152 464 152T479 148T493 138T500 120Q500 106 491 88T463 50Q438 25 397 7T295 -12ZM244 551Q242 553 252 567T281 600T322 640T369 679Q393 697 415 697Q427 697 436 690T445 670Q445 645 408 620Q383 603 355 589T303 566T263 553T244 551Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="690" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T389 659Q428 659 461 652T520 634T567 609T602 581Q630 553 630 531Q630 518 624 510T611 497T597 491T590 490Q575 517 552 537T502 572T446 593T387 600Q331 600 285 580T206 523T154 436T135 324Q135 264 154 213T207 125T287 68T389 47Q425 47 458 56T518 81T566 121T598 174Q598 175 605 174T620 169T635 156T642 134Q642 122 632 104T604 68Q574 38 519 13T386 -12ZM266 720Q244 720 244 743Q244 761 264 786T308 834T352 874T375 890Q377 890 397 874T442 835T485 786T505 743Q505 720 484 720Q467 720 443 745T374 832Q351 801 335 780T306 746T284 726T266 720Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="548" d="M295 -12Q242 -12 198 7T120 59T69 139T50 239Q50 293 68 338T120 417T197 469T295 488Q356 488 397 469T465 424Q482 407 491 389T500 356Q500 345 494 339T479 329T465 324T457 325Q448 347 434 366T401 399T355 422T295 430Q257 430 224 416T167 377T130 316T116 239Q116 197 129 162T167 101T223 61T295 46Q329 46 355 54T400 76T434 110T457 151Q458 152 464 152T479 148T493 138T500 120Q500 106 491 88T463 50Q438 25 397 7T295 -12ZM195 549Q173 549 173 572Q173 590 193 615T237 663T281 703T304 719Q306 719 326 703T371 664T414 615T434 572Q434 549 413 549Q396 549 372 574T303 661Q280 630 264 609T235 575T213 555T195 549Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="690" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T389 659Q428 659 461 652T520 634T567 609T602 581Q630 553 630 531Q630 518 624 510T611 497T597 491T590 490Q575 517 552 537T502 572T446 593T387 600Q331 600 285 580T206 523T154 436T135 324Q135 264 154 213T207 125T287 68T389 47Q425 47 458 56T518 81T566 121T598 174Q598 175 605 174T620 169T635 156T642 134Q642 122 632 104T604 68Q574 38 519 13T386 -12ZM384 720Q358 720 344 734T329 772Q329 795 344 809T385 824Q410 824 424 810T439 772Q439 749 425 735T384 720Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="548" d="M295 -12Q242 -12 198 7T120 59T69 139T50 239Q50 293 68 338T120 417T197 469T295 488Q356 488 397 469T465 424Q482 407 491 389T500 356Q500 345 494 339T479 329T465 324T457 325Q448 347 434 366T401 399T355 422T295 430Q257 430 224 416T167 377T130 316T116 239Q116 197 129 162T167 101T223 61T295 46Q329 46 355 54T400 76T434 110T457 151Q458 152 464 152T479 148T493 138T500 120Q500 106 491 88T463 50Q438 25 397 7T295 -12ZM309 549Q283 549 269 563T254 601Q254 624 269 638T310 653Q335 653 349 639T364 601Q364 578 350 564T309 549Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="690" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T389 659Q428 659 461 652T520 634T567 609T602 581Q630 553 630 531Q630 518 624 510T611 497T597 491T590 490Q575 517 552 537T502 572T446 593T387 600Q331 600 285 580T206 523T154 436T135 324Q135 264 154 213T207 125T287 68T389 47Q425 47 458 56T518 81T566 121T598 174Q598 175 605 174T620 169T635 156T642 134Q642 122 632 104T604 68Q574 38 519 13T386 -12ZM499 874Q520 874 520 851Q520 833 501 808T457 760T413 720T390 704Q388 704 368 720T323 759T280 808T260 851Q260 874 281 874Q298 874 322 849T391 762Q435 823 458 848T499 874Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="548" d="M295 -12Q242 -12 198 7T120 59T69 139T50 239Q50 293 68 338T120 417T197 469T295 488Q356 488 397 469T465 424Q482 407 491 389T500 356Q500 345 494 339T479 329T465 324T457 325Q448 347 434 366T401 399T355 422T295 430Q257 430 224 416T167 377T130 316T116 239Q116 197 129 162T167 101T223 61T295 46Q329 46 355 54T400 76T434 110T457 151Q458 152 464 152T479 148T493 138T500 120Q500 106 491 88T463 50Q438 25 397 7T295 -12ZM413 703Q434 703 434 680Q434 662 415 637T371 589T327 549T304 533Q302 533 282 549T237 588T194 637T174 680Q174 703 195 703Q212 703 236 678T305 591Q349 652 372 677T413 703Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="702" d="M92 609Q92 625 103 636T130 647H283Q367 647 432 625T543 562T613 461T637 325Q637 248 613 188T542 86T429 22T278 0H92V609ZM282 60Q419 60 493 128T567 323Q567 385 548 434T492 517T403 569T283 587H160V60H282ZM430 874Q451 874 451 851Q451 833 432 808T388 760T344 720T321 704Q319 704 299 720T254 759T211 808T191 851Q191 874 212 874Q229 874 253 849T322 762Q366 823 389 848T430 874Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="772" d="M275 -12Q228 -12 187 5T116 55T68 134T50 237Q50 293 67 339T115 419T187 470T279 488Q343 488 387 464T452 409V674Q452 676 458 678T474 681Q491 681 504 669T517 618V0H453V74Q421 32 378 10T275 -12ZM282 45Q315 45 346 57T401 90T439 141T453 205V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 317T116 238Q116 196 128 161T163 100T215 60T282 45ZM638 459Q636 455 630 454T617 454T606 461T605 474L665 652Q672 670 678 679T704 688H709Q721 688 730 679T739 652V649Q739 627 723 600L638 459Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="747" d="M62 294Q44 294 38 301T31 321V327Q31 340 37 347T62 354H127V609Q127 625 138 636T165 647H323Q406 647 472 625T585 562T657 460T682 325Q682 249 657 189T584 87T469 23T318 0H165Q149 0 138 11T127 38V294H62ZM327 62Q466 62 539 130T613 323Q613 447 539 516T328 585H195V354H409Q427 354 433 347T440 327V321Q440 308 434 301T409 294H195V62H327Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="599" d="M275 -12Q228 -12 187 5T116 55T68 134T50 237Q50 293 67 339T115 419T187 470T279 488Q343 488 387 464T452 409V533H360Q321 533 321 561V566Q321 595 360 595H452V674Q452 676 458 678T474 681Q491 681 504 669T517 618V595H558Q597 595 597 566V561Q597 547 587 540T558 533H517V0H453V74Q421 32 378 10T275 -12ZM282 45Q315 45 346 57T401 90T439 141T453 205V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 317T116 238Q116 196 128 161T163 100T215 60T282 45Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM216 739Q177 739 177 768V773Q177 787 187 794T216 801H424Q463 801 463 773V768Q463 739 424 739H216Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM184 568Q145 568 145 597V602Q145 616 155 623T184 630H392Q431 630 431 602V597Q431 568 392 568H184Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM320 719Q299 719 274 730T228 757T193 791T179 824Q179 853 208 853Q215 853 228 844T256 822T288 792T322 762Q337 776 353 792T384 821T411 844T432 853Q461 853 461 824Q461 809 447 791T411 756T365 730T320 719Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM290 548Q269 548 244 559T198 586T163 620T149 653Q149 682 178 682Q185 682 198 673T226 651T258 621T292 591Q307 605 323 621T354 650T381 673T402 682Q431 682 431 653Q431 638 417 620T381 585T335 559T290 548Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM327 720Q301 720 287 734T272 772Q272 795 287 809T328 824Q353 824 367 810T382 772Q382 749 368 735T327 720Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM284 549Q258 549 244 563T229 601Q229 624 244 638T285 653Q310 653 324 639T339 601Q339 578 325 564T284 549Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H536Q493 -32 479 -55T464 -98Q464 -115 474 -125T498 -136Q524 -136 537 -115Q548 -115 558 -123T569 -149Q569 -169 549 -181T497 -193Q455 -193 430 -171T404 -111Q404 -83 421 -55T476 0H92V609Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q452 30 422 15Q375 -18 360 -42T344 -87Q344 -104 354 -114T378 -125Q404 -125 417 -104Q428 -104 438 -112T449 -138Q449 -158 429 -170T377 -182Q335 -182 310 -160T284 -100Q284 -77 295 -55T329 -10Q321 -11 313 -11T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="606" d="M92 609Q92 625 103 636T130 647H544V585H160V362H473V302H160V62H550V0H92V609ZM432 874Q453 874 453 851Q453 833 434 808T390 760T346 720T323 704Q321 704 301 720T256 759T213 808T193 851Q193 874 214 874Q231 874 255 849T324 762Q368 823 391 848T432 874Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="561" d="M296 -12Q242 -12 197 6T119 57T68 135T50 236Q50 290 68 336T118 416T194 469T290 488Q340 488 381 471T451 423T496 347T512 248V242Q512 228 506 224T485 220H115Q118 180 132 148T171 94T226 59T296 46Q331 46 357 53T402 71T434 98T459 131Q460 132 466 131T479 126T491 114T497 94Q497 75 471 49Q448 26 403 7T296 -12ZM290 431Q257 431 228 420T177 388T139 339T118 276H443Q438 347 398 389T290 431ZM392 703Q413 703 413 680Q413 662 394 637T350 589T306 549T283 533Q281 533 261 549T216 588T173 637T153 680Q153 703 174 703Q191 703 215 678T284 591Q328 652 351 677T392 703Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="736" d="M370 -12Q300 -12 244 13T148 82T87 188T65 322Q65 396 89 458T155 564T257 634T389 659Q465 659 518 635T599 584Q630 553 630 531Q630 517 624 509T611 496T597 492T589 492Q574 519 552 539T502 572T446 593T387 600Q331 600 285 580T205 524T154 436T135 324Q135 264 151 213T199 126T276 68T379 47Q423 47 460 60T526 96T573 150T597 218V269H382V330H607Q629 330 643 316T657 280V3Q657 1 651 -1T635 -4Q618 -4 608 8T597 61V97Q567 46 508 17T370 -12ZM267 720Q245 720 245 743Q245 761 265 786T309 834T353 874T376 890Q378 890 398 874T443 835T486 786T506 743Q506 720 485 720Q468 720 444 745T375 832Q352 801 336 780T307 746T285 726T267 720Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="599" d="M291 -200Q225 -200 179 -181T107 -136Q81 -110 81 -88Q81 -75 86 -67T99 -53T112 -47T119 -47Q141 -88 182 -115T292 -142Q453 -142 453 24V84Q421 43 378 23T275 2Q225 2 184 19T113 67T67 143T50 243Q50 296 68 341T116 418T189 469T279 488Q342 488 387 465T453 411V476H518V24Q518 -89 459 -144T291 -200ZM282 58Q315 58 346 69T401 102T439 152T453 215V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 319T116 244Q116 159 161 109T282 58ZM179 549Q157 549 157 572Q157 590 177 615T221 663T265 703T288 719Q290 719 310 703T355 664T398 615T418 572Q418 549 397 549Q380 549 356 574T287 661Q264 630 248 609T219 575T197 555T179 549Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="736" d="M370 -12Q300 -12 244 13T148 82T87 188T65 322Q65 396 89 458T155 564T257 634T389 659Q465 659 518 635T599 584Q630 553 630 531Q630 517 624 509T611 496T597 492T589 492Q574 519 552 539T502 572T446 593T387 600Q331 600 285 580T205 524T154 436T135 324Q135 264 151 213T199 126T276 68T379 47Q423 47 460 60T526 96T573 150T597 218V269H382V330H607Q629 330 643 316T657 280V3Q657 1 651 -1T635 -4Q618 -4 608 8T597 61V97Q567 46 508 17T370 -12ZM378 719Q357 719 332 730T286 757T251 791T237 824Q237 853 266 853Q273 853 286 844T314 822T346 792T380 762Q395 776 411 792T442 821T469 844T490 853Q519 853 519 824Q519 809 505 791T469 756T423 730T378 719Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="599" d="M291 -200Q225 -200 179 -181T107 -136Q81 -110 81 -88Q81 -75 86 -67T99 -53T112 -47T119 -47Q141 -88 182 -115T292 -142Q453 -142 453 24V84Q421 43 378 23T275 2Q225 2 184 19T113 67T67 143T50 243Q50 296 68 341T116 418T189 469T279 488Q342 488 387 465T453 411V476H518V24Q518 -89 459 -144T291 -200ZM282 58Q315 58 346 69T401 102T439 152T453 215V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 319T116 244Q116 159 161 109T282 58ZM287 548Q266 548 241 559T195 586T160 620T146 653Q146 682 175 682Q182 682 195 673T223 651T255 621T289 591Q304 605 320 621T351 650T378 673T399 682Q428 682 428 653Q428 638 414 620T378 585T332 559T287 548Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="736" d="M370 -12Q300 -12 244 13T148 82T87 188T65 322Q65 396 89 458T155 564T257 634T389 659Q465 659 518 635T599 584Q630 553 630 531Q630 517 624 509T611 496T597 492T589 492Q574 519 552 539T502 572T446 593T387 600Q331 600 285 580T205 524T154 436T135 324Q135 264 151 213T199 126T276 68T379 47Q423 47 460 60T526 96T573 150T597 218V269H382V330H607Q629 330 643 316T657 280V3Q657 1 651 -1T635 -4Q618 -4 608 8T597 61V97Q567 46 508 17T370 -12ZM382 720Q356 720 342 734T327 772Q327 795 342 809T383 824Q408 824 422 810T437 772Q437 749 423 735T382 720Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="599" d="M291 -200Q225 -200 179 -181T107 -136Q81 -110 81 -88Q81 -75 86 -67T99 -53T112 -47T119 -47Q141 -88 182 -115T292 -142Q453 -142 453 24V84Q421 43 378 23T275 2Q225 2 184 19T113 67T67 143T50 243Q50 296 68 341T116 418T189 469T279 488Q342 488 387 465T453 411V476H518V24Q518 -89 459 -144T291 -200ZM282 58Q315 58 346 69T401 102T439 152T453 215V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 319T116 244Q116 159 161 109T282 58ZM290 549Q264 549 250 563T235 601Q235 624 250 638T291 653Q316 653 330 639T345 601Q345 578 331 564T290 549Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="736" d="M370 -12Q300 -12 244 13T148 82T87 188T65 322Q65 396 89 458T155 564T257 634T389 659Q465 659 518 635T599 584Q630 553 630 531Q630 517 624 509T611 496T597 492T589 492Q574 519 552 539T502 572T446 593T387 600Q331 600 285 580T205 524T154 436T135 324Q135 264 151 213T199 126T276 68T379 47Q423 47 460 60T526 96T573 150T597 218V269H382V330H607Q629 330 643 316T657 280V3Q657 1 651 -1T635 -4Q618 -4 608 8T597 61V97Q567 46 508 17T370 -12ZM321 -225Q318 -228 311 -229T298 -228T289 -222T288 -209L348 -84Q356 -68 362 -59T388 -50H393Q407 -50 417 -57T428 -79V-83Q428 -91 421 -100T402 -124L321 -225Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="599" d="M291 -200Q225 -200 179 -181T107 -136Q81 -110 81 -88Q81 -75 86 -67T99 -53T112 -47T119 -47Q141 -88 182 -115T292 -142Q453 -142 453 24V84Q421 43 378 23T275 2Q225 2 184 19T113 67T67 143T50 243Q50 296 68 341T116 418T189 469T279 488Q342 488 387 465T453 411V476H518V24Q518 -89 459 -144T291 -200ZM282 58Q315 58 346 69T401 102T439 152T453 215V319Q430 371 389 401T284 431Q246 431 215 417T162 378T128 319T116 244Q116 159 161 109T282 58ZM317 694Q320 697 327 698T340 697T349 690T350 678L290 553Q282 537 276 528T250 519H245Q231 519 221 526T210 548V552Q210 560 217 569T236 593L317 694Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="709" d="M92 620Q92 651 121 651H130Q160 651 160 620V355H549V620Q549 651 578 651H587Q617 651 617 620V0H549V293H160V0H92V620ZM246 720Q224 720 224 743Q224 761 244 786T288 834T332 874T355 890Q357 890 377 874T422 835T465 786T485 743Q485 720 464 720Q447 720 423 745T354 832Q331 801 315 780T286 746T264 726T246 720Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="575" d="M82 674Q82 676 88 678T104 681Q121 681 134 669T147 618V400Q172 439 212 463T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 398 386T304 427Q233 427 190 383T147 264V0H82V674ZM2 720Q-20 720 -20 743Q-20 761 0 786T44 834T88 874T111 890Q113 890 133 874T178 835T221 786T241 743Q241 720 220 720Q203 720 179 745T110 832Q87 801 71 780T42 746T20 726T2 720Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="709" d="M67 460Q48 460 38 466T28 487V492Q28 506 38 512T67 518H92V620Q92 651 121 651H130Q160 651 160 620V518H549V620Q549 651 578 651H587Q617 651 617 620V518H641Q680 518 680 492V487Q680 472 670 466T641 460H617V0H549V293H160V0H92V460H67ZM549 355V460H160V355H549Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="575" d="M41 533Q2 533 2 562V567Q2 581 12 588T41 595H82V674Q82 676 88 678T104 681Q121 681 134 669T147 618V595H239Q278 595 278 567V562Q278 533 239 533H147V400Q172 439 212 463T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 398 386T304 427Q233 427 190 383T147 264V0H82V533H41Z" />
+<glyph unicode="&#x128;" glyph-name="Itilde" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM17 680Q-8 680 -8 706Q-8 722 -3 738T11 767T33 788T61 796Q85 796 125 775T209 716Q213 729 218 742T229 765T241 782T255 789Q280 789 280 763Q280 746 275 730T261 702T239 681T211 673Q187 673 147 694T63 753Q59 740 54 727T43 704T31 687T17 680Z" />
+<glyph unicode="&#x129;" glyph-name="itilde" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM1 555Q-24 555 -24 581Q-24 597 -19 613T-5 642T17 663T45 671Q69 671 109 650T193 591Q197 604 202 617T213 640T225 657T239 664Q264 664 264 638Q264 621 259 605T245 577T223 556T195 548Q171 548 131 569T47 628Q43 615 38 602T27 579T15 562T1 555Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM32 739Q-7 739 -7 768V773Q-7 787 3 794T32 801H240Q279 801 279 773V768Q279 739 240 739H32Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM15 568Q-24 568 -24 597V602Q-24 616 -14 623T15 630H223Q262 630 262 602V597Q262 568 223 568H15Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="272" d="M102 619Q102 633 109 642T126 651H146Q170 651 170 619V0H157Q110 -34 95 -58T79 -103Q79 -120 89 -130T113 -141Q139 -141 152 -120Q163 -120 173 -128T184 -154Q184 -174 164 -186T112 -198Q70 -198 45 -176T19 -116Q19 -86 38 -56T102 2V619Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="239" d="M140 0Q93 -34 78 -58T62 -103Q62 -120 72 -130T96 -141Q122 -141 135 -120Q146 -120 156 -128T167 -154Q167 -174 147 -186T95 -198Q53 -198 28 -176T2 -116Q2 -86 22 -55T87 3V449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H140ZM119 595Q77 595 77 638Q77 681 121 681Q163 681 163 638Q163 595 119 595Z" />
+<glyph unicode="&#x130;" glyph-name="Idotaccent" horiz-adv-x="272" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM136 720Q110 720 96 734T81 772Q81 795 96 809T137 824Q162 824 176 810T191 772Q191 749 177 735T136 720Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="239" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449Z" />
+<glyph unicode="&#x132;" glyph-name="IJ" horiz-adv-x="739" d="M102 620Q102 651 131 651H140Q170 651 170 620V0H102V620ZM469 -12Q435 -12 407 -3T357 21T325 51T313 82Q313 92 319 100T332 114T346 122T353 123Q368 90 398 69T469 47Q534 47 561 83T589 204V647H657V204Q657 93 612 41T469 -12Z" />
+<glyph unicode="&#x133;" glyph-name="ij" horiz-adv-x="478" d="M87 449Q87 467 95 473T116 480H123Q136 480 144 474T152 449V0H87V449ZM119 595Q77 595 77 638Q77 681 121 681Q163 681 163 638Q163 595 119 595ZM260 -191Q234 -191 214 -184T183 -166Q170 -153 170 -139Q170 -123 178 -112T189 -103Q200 -114 217 -123T259 -133Q295 -133 310 -113T326 -44V476H391V-49Q391 -118 358 -154T260 -191ZM358 595Q316 595 316 638Q316 681 360 681Q402 681 402 638Q402 595 358 595Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="467" d="M197 -12Q163 -12 135 -3T85 21T53 51T41 82Q41 92 47 100T60 114T74 122T81 123Q96 90 126 69T197 47Q262 47 289 83T317 204V647H385V204Q385 93 340 41T197 -12ZM236 720Q214 720 214 743Q214 761 234 786T278 834T322 874T345 890Q347 890 367 874T412 835T455 786T475 743Q475 720 454 720Q437 720 413 745T344 832Q321 801 305 780T276 746T254 726T236 720Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="239" d="M21 -191Q-5 -191 -25 -184T-56 -166Q-69 -153 -69 -139Q-69 -123 -61 -112T-50 -103Q-39 -114 -22 -123T20 -133Q56 -133 71 -113T87 -44V476H152V-49Q152 -118 119 -154T21 -191ZM10 549Q-12 549 -12 572Q-12 590 8 615T52 663T96 703T119 719Q121 719 141 703T186 664T229 615T249 572Q249 549 228 549Q211 549 187 574T118 661Q95 630 79 609T50 575T28 555T10 549Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="586" d="M92 620Q92 651 121 651H130Q160 651 160 620V339L456 630Q472 646 486 649T510 648T525 634T529 615L235 332L536 31Q537 20 531 11T513 -2T488 -4T462 12L160 323V0H92V620ZM225 -225Q222 -228 215 -229T202 -228T193 -222T192 -209L252 -84Q260 -68 266 -59T292 -50H297Q311 -50 321 -57T332 -79V-83Q332 -91 325 -100T306 -124L225 -225Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="473" d="M82 674Q82 676 88 678T104 681Q121 681 134 669T147 618V258L339 454Q359 475 374 478T398 477T412 462T415 448L218 253L446 30Q446 25 442 16T429 0T405 -3T369 21L147 242V0H82V674ZM184 -225Q181 -228 174 -229T161 -228T152 -222T151 -209L211 -84Q219 -68 225 -59T251 -50H256Q270 -50 280 -57T291 -79V-83Q291 -91 284 -100T265 -124L184 -225Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="550" d="M92 620Q92 651 121 651H130Q160 651 160 620V63H472Q503 63 503 34V29Q503 0 472 0H92V620ZM115 722Q113 724 123 738T152 771T193 811T240 850Q264 868 286 868Q298 868 307 861T316 841Q316 816 279 791Q254 774 226 760T174 737T134 724T115 722Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="239" d="M87 674Q87 676 93 678T109 681Q126 681 139 669T152 618V0H87V674ZM107 722Q105 724 115 738T144 771T185 811T232 850Q256 868 278 868Q290 868 299 861T308 841Q308 816 271 791Q246 774 218 760T166 737T126 724T107 722Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="550" d="M92 620Q92 651 121 651H130Q160 651 160 620V63H472Q503 63 503 34V29Q503 0 472 0H92V620ZM242 -225Q239 -228 232 -229T219 -228T210 -222T209 -209L269 -84Q277 -68 283 -59T309 -50H314Q328 -50 338 -57T349 -79V-83Q349 -91 342 -100T323 -124L242 -225Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="239" d="M87 674Q87 676 93 678T109 681Q126 681 139 669T152 618V0H87V674ZM55 -225Q52 -228 45 -229T32 -228T23 -222T22 -209L82 -84Q90 -68 96 -59T122 -50H127Q141 -50 151 -57T162 -79V-83Q162 -91 155 -100T136 -124L55 -225Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="586" d="M92 620Q92 651 121 651H130Q160 651 160 620V63H472Q503 63 503 34V29Q503 0 472 0H92V620ZM473 422Q471 418 465 417T452 417T441 424T440 437L500 615Q507 633 513 642T539 651H544Q556 651 565 642T574 615V612Q574 590 558 563L473 422Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="407" d="M87 674Q87 676 93 678T109 681Q126 681 139 669T152 618V0H87V674ZM273 454Q271 450 265 449T252 449T241 456T240 469L300 647Q307 665 313 674T339 683H344Q356 683 365 674T374 647V644Q374 622 358 595L273 454Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="609" d="M92 620Q92 651 121 651H130Q160 651 160 620V63H472Q503 63 503 34V29Q503 0 472 0H92V620ZM542 276Q498 276 498 320V330Q498 351 509 363T542 375H552Q574 375 585 363T597 330V320Q597 276 552 276H542Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="337" d="M87 674Q87 676 93 678T109 681Q126 681 139 669T152 618V0H87V674ZM263 256Q219 256 219 300V310Q219 331 230 343T263 355H273Q295 355 306 343T318 310V300Q318 256 273 256H263Z" />
+<glyph unicode="&#x141;" glyph-name="CR" horiz-adv-x="565" d="M46 211Q44 209 38 219T32 241Q32 253 41 263T77 282L127 297V620Q127 651 156 651H165Q195 651 195 620V318L397 380Q400 381 405 372T411 350Q411 338 401 328T366 309L195 257V62H497Q528 62 528 33V28Q528 0 497 0H165Q149 0 138 11T127 38V236L46 211Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="354" d="M45 217Q43 216 37 227T31 248Q31 259 40 269T75 287L141 307V674Q141 676 147 678T163 681Q180 681 193 669T206 618V327L309 359Q311 360 317 349T323 328Q323 317 314 307T279 289L206 266V27Q206 9 198 3T177 -4H170Q157 -4 149 2T141 27V246L45 217Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="705" d="M92 605Q92 624 101 637T129 651H146Q165 651 175 642T198 615L549 106V647H613V42Q613 22 604 9T578 -4H573Q554 -4 547 2T529 22L156 561V0H92V605ZM330 722Q328 724 338 738T367 771T408 811T455 850Q479 868 501 868Q513 868 522 861T531 841Q531 816 494 791Q469 774 441 760T389 737T349 724T330 722Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="575" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V402Q171 440 211 464T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 399 386T304 427Q248 427 208 399T147 326V0H82V473ZM250 551Q248 553 258 567T287 600T328 640T375 679Q399 697 421 697Q433 697 442 690T451 670Q451 645 414 620Q389 603 361 589T309 566T269 553T250 551Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="705" d="M92 605Q92 624 101 637T129 651H146Q165 651 175 642T198 615L549 106V647H613V42Q613 22 604 9T578 -4H573Q554 -4 547 2T529 22L156 561V0H92V605ZM294 -225Q291 -228 284 -229T271 -228T262 -222T261 -209L321 -84Q329 -68 335 -59T361 -50H366Q380 -50 390 -57T401 -79V-83Q401 -91 394 -100T375 -124L294 -225Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="575" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V402Q171 440 211 464T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 399 386T304 427Q248 427 208 399T147 326V0H82V473ZM226 -225Q223 -228 216 -229T203 -228T194 -222T193 -209L253 -84Q261 -68 267 -59T293 -50H298Q312 -50 322 -57T333 -79V-83Q333 -91 326 -100T307 -124L226 -225Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="705" d="M92 605Q92 624 101 637T129 651H146Q165 651 175 642T198 615L549 106V647H613V42Q613 22 604 9T578 -4H573Q554 -4 547 2T529 22L156 561V0H92V605ZM467 874Q488 874 488 851Q488 833 469 808T425 760T381 720T358 704Q356 704 336 720T291 759T248 808T228 851Q228 874 249 874Q266 874 290 849T359 762Q403 823 426 848T467 874Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="575" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V402Q171 440 211 464T308 488Q355 488 390 473T449 431T485 364T497 278V0H432V274Q432 346 399 386T304 427Q248 427 208 399T147 326V0H82V473ZM402 703Q423 703 423 680Q423 662 404 637T360 589T316 549T293 533Q291 533 271 549T226 588T183 637T163 680Q163 703 184 703Q201 703 225 678T294 591Q338 652 361 677T402 703Z" />
+<glyph unicode="&#x149;" glyph-name="napostrophe" horiz-adv-x="807" d="M74 432Q72 428 66 427T53 427T42 434T41 447L101 625Q108 643 114 652T140 661H145Q157 661 166 652T175 625V622Q175 600 159 573L74 432ZM314 473Q314 475 320 477T336 480Q353 480 365 468T378 417V402Q403 440 443 464T540 488Q587 488 622 473T681 431T717 364T729 278V0H664V274Q664 346 631 386T536 427Q480 427 440 399T379 326V0H314V473Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM299 739Q260 739 260 768V773Q260 787 270 794T299 801H507Q546 801 546 773V768Q546 739 507 739H299Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM203 568Q164 568 164 597V602Q164 616 174 623T203 630H411Q450 630 450 602V597Q450 568 411 568H203Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM394 719Q373 719 348 730T302 757T267 791T253 824Q253 853 282 853Q289 853 302 844T330 822T362 792T396 762Q411 776 427 792T458 821T485 844T506 853Q535 853 535 824Q535 809 521 791T485 756T439 730T394 719Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM306 548Q285 548 260 559T214 586T179 620T165 653Q165 682 194 682Q201 682 214 673T242 651T274 621T308 591Q323 605 339 621T370 650T397 673T418 682Q447 682 447 653Q447 638 433 620T397 585T351 559T306 548Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="774" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q459 659 518 635T619 566T685 460T709 324Q709 250 685 188T619 82T517 13T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47ZM433 722Q431 724 441 738T470 771T511 811T558 850Q582 868 604 868Q616 868 625 861T634 841Q634 816 597 791Q572 774 544 760T492 737T452 724T433 722ZM254 722Q252 724 262 738T291 771T332 811T379 850Q403 868 425 868Q437 868 446 861T455 841Q455 816 418 791Q393 774 365 760T313 737T273 724T254 722Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="592" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q349 488 394 469T471 417T522 337T541 237Q541 183 523 138T472 59T394 7T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM314 551Q312 553 322 567T351 600T392 640T439 679Q463 697 485 697Q497 697 506 690T515 670Q515 645 478 620Q453 603 425 589T373 566T333 553T314 551ZM135 551Q133 553 143 567T172 600T213 640T260 679Q284 697 306 697Q318 697 327 690T336 670Q336 645 299 620Q274 603 246 589T194 566T154 553T135 551Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1061" d="M386 -12Q315 -12 256 12T155 81T89 186T65 322Q65 396 89 458T155 564T257 634T388 659Q437 659 482 647H999V585H599Q645 545 673 489T707 362H928V302H708Q704 226 676 165T598 62H1005V0H478Q456 -5 433 -8T386 -12ZM388 47Q443 47 489 67T568 123T620 210T639 322Q639 382 620 433T568 521T488 579T386 600Q331 600 285 580T206 523T154 436T135 324Q135 263 154 212T206 125T286 68T388 47Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="985" d="M296 -12Q242 -12 197 7T120 59T69 139T50 239Q50 293 68 338T119 417T197 469T296 488Q368 488 423 454T507 361Q536 420 589 454T714 488Q764 488 805 471T875 423T920 347T936 248V242Q936 228 930 224T909 220H539Q541 182 555 150T593 95T649 59T720 46Q755 46 781 53T826 71T858 98T883 131Q884 132 890 131T903 126T915 114T921 94Q921 82 908 64T869 29T806 0T720 -12Q644 -12 589 20T504 112Q475 55 422 22T296 -12ZM296 45Q334 45 367 59T424 99T461 160T475 237Q475 279 462 314T424 376T368 416T296 431Q257 431 224 417T167 377T130 316T116 239Q116 197 129 162T167 100T224 60T296 45ZM714 431Q680 431 651 420T599 388T561 339T541 276H867Q862 347 822 389T714 431Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="597" d="M541 460Q541 369 484 321T327 272L533 28Q534 26 532 21T525 11T511 1T492 -4Q482 -4 472 1T450 20L240 272H159V0H92V609Q92 625 103 636T130 647H308Q422 647 481 601T541 460ZM314 330Q395 330 433 365T471 458Q471 586 310 586H159V330H314ZM243 722Q241 724 251 738T280 771T321 811T368 850Q392 868 414 868Q426 868 435 861T444 841Q444 816 407 791Q382 774 354 760T302 737T262 724T243 722Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="368" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V388Q178 488 272 488Q305 488 323 475T341 439Q341 421 333 412T323 405Q312 414 299 419T265 425Q233 425 211 409T174 365T154 299T147 214V0H82V473ZM149 551Q147 553 157 567T186 600T227 640T274 679Q298 697 320 697Q332 697 341 690T350 670Q350 645 313 620Q288 603 260 589T208 566T168 553T149 551Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="597" d="M541 460Q541 369 484 321T327 272L533 28Q534 26 532 21T525 11T511 1T492 -4Q482 -4 472 1T450 20L240 272H159V0H92V609Q92 625 103 636T130 647H308Q422 647 481 601T541 460ZM314 330Q395 330 433 365T471 458Q471 586 310 586H159V330H314ZM236 -225Q233 -228 226 -229T213 -228T204 -222T203 -209L263 -84Q271 -68 277 -59T303 -50H308Q322 -50 332 -57T343 -79V-83Q343 -91 336 -100T317 -124L236 -225Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="368" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V388Q178 488 272 488Q305 488 323 475T341 439Q341 421 333 412T323 405Q312 414 299 419T265 425Q233 425 211 409T174 365T154 299T147 214V0H82V473ZM43 -225Q40 -228 33 -229T20 -228T11 -222T10 -209L70 -84Q78 -68 84 -59T110 -50H115Q129 -50 139 -57T150 -79V-83Q150 -91 143 -100T124 -124L43 -225Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="597" d="M541 460Q541 369 484 321T327 272L533 28Q534 26 532 21T525 11T511 1T492 -4Q482 -4 472 1T450 20L240 272H159V0H92V609Q92 625 103 636T130 647H308Q422 647 481 601T541 460ZM314 330Q395 330 433 365T471 458Q471 586 310 586H159V330H314ZM398 874Q419 874 419 851Q419 833 400 808T356 760T312 720T289 704Q287 704 267 720T222 759T179 808T159 851Q159 874 180 874Q197 874 221 849T290 762Q334 823 357 848T398 874Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="368" d="M82 473Q82 475 88 477T104 480Q121 480 133 468T146 417V388Q178 488 272 488Q305 488 323 475T341 439Q341 421 333 412T323 405Q312 414 299 419T265 425Q233 425 211 409T174 365T154 299T147 214V0H82V473ZM322 703Q343 703 343 680Q343 662 324 637T280 589T236 549T213 533Q211 533 191 549T146 588T103 637T83 680Q83 703 104 703Q121 703 145 678T214 591Q258 652 281 677T322 703Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="571" d="M289 -12Q216 -12 163 10T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q118 111 167 79T288 47Q363 47 405 78T447 167Q447 199 433 220T393 255T336 279T269 297Q232 306 197 317T133 349T88 401T70 481Q70 519 86 551T132 608T200 645T287 659Q355 659 403 639T476 594Q501 569 501 547Q501 536 495 528T482 515T468 507T461 506Q438 546 393 573T288 600Q220 600 178 568T136 483Q136 452 148 432T182 399T234 378T302 360Q341 351 379 338T447 304T495 251T514 170Q514 129 498 96T453 38T382 1T289 -12ZM224 722Q222 724 232 738T261 771T302 811T349 850Q373 868 395 868Q407 868 416 861T425 841Q425 816 388 791Q363 774 335 760T283 737T243 724T224 722Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="484" d="M242 -12Q179 -12 135 5T68 45Q46 67 46 85Q46 95 51 102T62 113T74 119T81 120Q105 87 144 65T244 43Q304 43 335 65T367 123Q367 144 358 158T331 181T287 197T227 211Q199 217 170 225T116 247T76 285T60 344Q60 376 73 402T110 448T167 477T242 488Q301 488 339 473T398 437Q418 417 418 397Q418 388 413 381T401 370T389 364T382 363Q362 394 327 413T242 433Q183 433 153 411T122 353Q122 315 155 299T255 269Q284 263 315 255T371 232T413 194T430 133Q430 67 380 28T242 -12ZM191 551Q189 553 199 567T228 600T269 640T316 679Q340 697 362 697Q374 697 383 690T392 670Q392 645 355 620Q330 603 302 589T250 566T210 553T191 551Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="571" d="M289 -12Q216 -12 163 10T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q118 111 167 79T288 47Q363 47 405 78T447 167Q447 199 433 220T393 255T336 279T269 297Q232 306 197 317T133 349T88 401T70 481Q70 519 86 551T132 608T200 645T287 659Q355 659 403 639T476 594Q501 569 501 547Q501 536 495 528T482 515T468 507T461 506Q438 546 393 573T288 600Q220 600 178 568T136 483Q136 452 148 432T182 399T234 378T302 360Q341 351 379 338T447 304T495 251T514 170Q514 129 498 96T453 38T382 1T289 -12ZM186 720Q164 720 164 743Q164 761 184 786T228 834T272 874T295 890Q297 890 317 874T362 835T405 786T425 743Q425 720 404 720Q387 720 363 745T294 832Q271 801 255 780T226 746T204 726T186 720Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="484" d="M242 -12Q179 -12 135 5T68 45Q46 67 46 85Q46 95 51 102T62 113T74 119T81 120Q105 87 144 65T244 43Q304 43 335 65T367 123Q367 144 358 158T331 181T287 197T227 211Q199 217 170 225T116 247T76 285T60 344Q60 376 73 402T110 448T167 477T242 488Q301 488 339 473T398 437Q418 417 418 397Q418 388 413 381T401 370T389 364T382 363Q362 394 327 413T242 433Q183 433 153 411T122 353Q122 315 155 299T255 269Q284 263 315 255T371 232T413 194T430 133Q430 67 380 28T242 -12ZM135 549Q113 549 113 572Q113 590 133 615T177 663T221 703T244 719Q246 719 266 703T311 664T354 615T374 572Q374 549 353 549Q336 549 312 574T243 661Q220 630 204 609T175 575T153 555T135 549Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="571" d="M289 -12Q216 -12 163 10T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q118 111 167 79T288 47Q363 47 405 78T447 167Q447 199 433 220T393 255T336 279T269 297Q232 306 197 317T133 349T88 401T70 481Q70 519 86 551T132 608T200 645T287 659Q355 659 403 639T476 594Q501 569 501 547Q501 536 495 528T482 515T468 507T461 506Q438 546 393 573T288 600Q220 600 178 568T136 483Q136 452 148 432T182 399T234 378T302 360Q341 351 379 338T447 304T495 251T514 170Q514 129 498 96T453 38T382 1T289 -12ZM210 -225Q207 -228 200 -229T187 -228T178 -222T177 -209L237 -84Q245 -68 251 -59T277 -50H282Q296 -50 306 -57T317 -79V-83Q317 -91 310 -100T291 -124L210 -225Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="484" d="M242 -12Q179 -12 135 5T68 45Q46 67 46 85Q46 95 51 102T62 113T74 119T81 120Q105 87 144 65T244 43Q304 43 335 65T367 123Q367 144 358 158T331 181T287 197T227 211Q199 217 170 225T116 247T76 285T60 344Q60 376 73 402T110 448T167 477T242 488Q301 488 339 473T398 437Q418 417 418 397Q418 388 413 381T401 370T389 364T382 363Q362 394 327 413T242 433Q183 433 153 411T122 353Q122 315 155 299T255 269Q284 263 315 255T371 232T413 194T430 133Q430 67 380 28T242 -12ZM168 -225Q165 -228 158 -229T145 -228T136 -222T135 -209L195 -84Q203 -68 209 -59T235 -50H240Q254 -50 264 -57T275 -79V-83Q275 -91 268 -100T249 -124L168 -225Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="571" d="M289 -12Q216 -12 163 10T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q118 111 167 79T288 47Q363 47 405 78T447 167Q447 199 433 220T393 255T336 279T269 297Q232 306 197 317T133 349T88 401T70 481Q70 519 86 551T132 608T200 645T287 659Q355 659 403 639T476 594Q501 569 501 547Q501 536 495 528T482 515T468 507T461 506Q438 546 393 573T288 600Q220 600 178 568T136 483Q136 452 148 432T182 399T234 378T302 360Q341 351 379 338T447 304T495 251T514 170Q514 129 498 96T453 38T382 1T289 -12ZM398 873Q419 873 419 850Q419 832 400 807T356 759T312 719T289 703Q287 703 267 719T222 758T179 807T159 850Q159 873 180 873Q197 873 221 848T290 761Q334 822 357 847T398 873Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="484" d="M242 -12Q179 -12 135 5T68 45Q46 67 46 85Q46 95 51 102T62 113T74 119T81 120Q105 87 144 65T244 43Q304 43 335 65T367 123Q367 144 358 158T331 181T287 197T227 211Q199 217 170 225T116 247T76 285T60 344Q60 376 73 402T110 448T167 477T242 488Q301 488 339 473T398 437Q418 417 418 397Q418 388 413 381T401 370T389 364T382 363Q362 394 327 413T242 433Q183 433 153 411T122 353Q122 315 155 299T255 269Q284 263 315 255T371 232T413 194T430 133Q430 67 380 28T242 -12ZM344 703Q365 703 365 680Q365 662 346 637T302 589T258 549T235 533Q233 533 213 549T168 588T125 637T105 680Q105 703 126 703Q143 703 167 678T236 591Q280 652 303 677T344 703Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="614" d="M273 585H70Q39 585 39 613V618Q39 647 70 647H544Q575 647 575 618V613Q575 585 544 585H341V0H273V585ZM248 -225Q245 -228 238 -229T225 -228T216 -222T215 -209L275 -84Q283 -68 289 -59T315 -50H320Q334 -50 344 -57T355 -79V-83Q355 -91 348 -100T329 -124L248 -225Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="376" d="M245 -12Q175 -12 144 26T112 140V418H41V476H112V559Q112 577 120 583T141 590H148Q161 590 168 584T176 559V476H330V418H176V147Q176 94 191 69T247 44Q272 44 290 53T321 79Q323 81 333 74T343 47Q343 31 328 16Q316 4 295 -4T245 -12ZM149 -225Q146 -228 139 -229T126 -228T117 -222T116 -209L176 -84Q184 -68 190 -59T216 -50H221Q235 -50 245 -57T256 -79V-83Q256 -91 249 -100T230 -124L149 -225Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="614" d="M273 585H70Q39 585 39 613V618Q39 647 70 647H544Q575 647 575 618V613Q575 585 544 585H341V0H273V585ZM416 874Q437 874 437 851Q437 833 418 808T374 760T330 720T307 704Q305 704 285 720T240 759T197 808T177 851Q177 874 198 874Q215 874 239 849T308 762Q352 823 375 848T416 874Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="400" d="M245 -12Q175 -12 144 26T112 140V418H41V476H112V559Q112 577 120 583T141 590H148Q161 590 168 584T176 559V476H330V418H176V147Q176 94 191 69T247 44Q272 44 290 53T321 79Q323 81 333 74T343 47Q343 31 328 16Q316 4 295 -4T245 -12ZM253 534Q251 531 246 530T236 530T227 536T226 547L276 694Q281 709 287 716T308 724H312Q323 724 330 716T337 694V692Q337 674 324 651L253 534Z" />
+<glyph unicode="&#x166;" glyph-name="Tbar" horiz-adv-x="614" d="M203 302Q164 302 164 331V336Q164 350 174 357T203 364H273V585H70Q39 585 39 613V618Q39 647 70 647H544Q575 647 575 618V613Q575 585 544 585H341V364H411Q450 364 450 336V331Q450 302 411 302H341V0H273V302H203Z" />
+<glyph unicode="&#x167;" glyph-name="tbar" horiz-adv-x="376" d="M84 209Q45 209 45 238V243Q45 257 55 264T84 271H112V418H41V476H112V559Q112 577 120 583T141 590H148Q161 590 168 584T176 559V476H330V418H176V271H292Q331 271 331 243V238Q331 209 292 209H176V147Q176 94 191 69T247 44Q272 44 290 53T321 79Q323 81 333 74T343 47Q343 31 328 16Q316 4 295 -4T245 -12Q175 -12 144 26T112 140V209H84Z" />
+<glyph unicode="&#x168;" glyph-name="Utilde" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM215 680Q190 680 190 706Q190 722 195 738T209 767T231 788T259 796Q283 796 323 775T407 716Q411 729 416 742T427 765T439 782T453 789Q478 789 478 763Q478 746 473 730T459 702T437 681T409 673Q385 673 345 694T261 753Q257 740 252 727T241 704T229 687T215 680Z" />
+<glyph unicode="&#x169;" glyph-name="utilde" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM168 555Q143 555 143 581Q143 597 148 613T162 642T184 663T212 671Q236 671 276 650T360 591Q364 604 369 617T380 640T392 657T406 664Q431 664 431 638Q431 621 426 605T412 577T390 556T362 548Q338 548 298 569T214 628Q210 615 205 602T194 579T182 562T168 555Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM241 739Q202 739 202 768V773Q202 787 212 794T241 801H449Q488 801 488 773V768Q488 739 449 739H241Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM187 568Q148 568 148 597V602Q148 616 158 623T187 630H395Q434 630 434 602V597Q434 568 395 568H187Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM340 719Q319 719 294 730T248 757T213 791T199 824Q199 853 228 853Q235 853 248 844T276 822T308 792T342 762Q357 776 373 792T404 821T431 844T452 853Q481 853 481 824Q481 809 467 791T431 756T385 730T340 719Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM277 548Q256 548 231 559T185 586T150 620T136 653Q136 682 165 682Q172 682 185 673T213 651T245 621T279 591Q294 605 310 621T341 650T368 673T389 682Q418 682 418 653Q418 638 404 620T368 585T322 559T277 548Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM344 719Q318 719 297 728T261 752T237 789T228 836Q228 861 237 882T261 919T298 944T345 953Q370 953 391 944T428 920T452 883T461 836Q461 811 452 790T428 753T391 728T344 719ZM344 764Q373 764 392 784T411 836Q411 867 392 888T344 909Q315 909 296 888T277 836Q277 806 296 785T344 764Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM291 535Q265 535 244 544T208 568T184 605T175 652Q175 677 184 698T208 735T245 760T292 769Q317 769 338 760T375 736T399 699T408 652Q408 627 399 606T375 569T338 544T291 535ZM291 580Q320 580 339 600T358 652Q358 683 339 704T291 725Q262 725 243 704T224 652Q224 622 243 601T291 580Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 126 522 57T333 -12ZM389 722Q387 724 397 738T426 771T467 811T514 850Q538 868 560 868Q572 868 581 861T590 841Q590 816 553 791Q528 774 500 760T448 737T408 724T389 722ZM210 722Q208 724 218 738T247 771T288 811T335 850Q359 868 381 868Q393 868 402 861T411 841Q411 816 374 791Q349 774 321 760T269 737T229 724T210 722Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12ZM322 551Q320 553 330 567T359 600T400 640T447 679Q471 697 493 697Q505 697 514 690T523 670Q523 645 486 620Q461 603 433 589T381 566T341 553T322 551ZM143 551Q141 553 151 567T180 600T221 640T268 679Q292 697 314 697Q326 697 335 690T344 670Q344 645 307 620Q282 603 254 589T202 566T162 553T143 551Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="669" d="M333 -12Q210 -12 146 56T82 255V620Q82 651 111 651H120Q150 651 150 620V260Q150 49 335 49Q520 49 520 261V620Q520 651 548 651H560Q587 651 587 620V258Q587 94 486 28Q443 -4 428 -28T413 -71Q413 -88 423 -98T447 -109Q473 -109 486 -88Q497 -88 507 -96T518 -122Q518 -142 498 -154T446 -166Q404 -166 379 -144T353 -84Q353 -45 384 -8Q372 -10 360 -11T333 -12Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="575" d="M267 -12Q220 -12 185 3T126 45T90 112T78 198V476H143V202Q143 130 176 90T271 49Q327 49 367 77T428 150V476H493V3Q493 2 489 0Q443 -34 428 -58T412 -102Q412 -119 422 -129T446 -140Q472 -140 485 -119Q496 -119 506 -127T517 -153Q517 -173 497 -185T445 -197Q403 -197 378 -175T352 -115Q352 -84 373 -53T442 7Q436 14 433 26T429 59V74Q404 36 364 12T267 -12Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="934" d="M70 636Q69 639 79 644T103 651T130 643T149 609L273 61L415 524Q421 542 432 552T469 562Q495 562 505 553T522 524L662 63L789 609Q795 634 807 643T833 651T855 645T864 636L708 0H618L470 495L313 0H226L70 636ZM362 700Q340 700 340 723Q340 741 360 766T404 814T448 854T471 870Q473 870 493 854T538 815T581 766T601 723Q601 700 580 700Q563 700 539 725T470 812Q447 781 431 760T402 726T380 706T362 700Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="704" d="M39 461Q39 464 48 471T70 479T96 473T116 434L203 60L305 381Q310 395 319 401T354 408Q379 408 388 402T403 381L504 60L590 434Q597 464 609 473T634 480T656 471T665 461L545 0H465L354 345L240 0H160L39 461ZM246 539Q224 539 224 562Q224 580 244 605T288 653T332 693T355 709Q357 709 377 693T422 654T465 605T485 562Q485 539 464 539Q447 539 423 564T354 651Q331 620 315 599T286 565T264 545T246 539Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="579" d="M255 269L25 647H100L290 331L479 627Q491 646 504 649T529 648T547 635T553 622L323 269V0H255V269ZM185 720Q163 720 163 743Q163 761 183 786T227 834T271 874T294 890Q296 890 316 874T361 835T404 786T424 743Q424 720 403 720Q386 720 362 745T293 832Q270 801 254 780T225 746T203 726T185 720Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="502" d="M198 -197Q184 -197 174 -190T164 -180L244 24Q227 24 219 29T204 49L24 476H92L262 65L410 476H479L244 -154Q234 -180 223 -188T198 -197ZM152 549Q130 549 130 572Q130 590 150 615T194 663T238 703T261 719Q263 719 283 703T328 664T371 615T391 572Q391 549 370 549Q353 549 329 574T260 661Q237 630 221 609T192 575T170 555T152 549Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="579" d="M255 269L25 647H100L290 331L479 627Q491 646 504 649T529 648T547 635T553 622L323 269V0H255V269ZM374 718Q351 718 338 731T324 767Q324 790 337 803T372 816Q395 816 408 803T422 767Q422 744 409 731T374 718ZM206 718Q183 718 170 731T156 767Q156 790 169 803T204 816Q227 816 240 803T254 767Q254 744 241 731T206 718Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="634" d="M69 72L481 585H101Q70 585 70 613V618Q70 647 101 647H556V578L142 62H543Q574 62 574 34V29Q574 0 543 0H69V72ZM270 722Q268 724 278 738T307 771T348 811T395 850Q419 868 441 868Q453 868 462 861T471 841Q471 816 434 791Q409 774 381 760T329 737T289 724T270 722Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="503" d="M55 50L358 420H88Q70 420 63 426T55 445V451Q55 464 62 470T88 476H445V432L137 56H419Q437 56 444 50T452 31V25Q452 12 445 6T419 0H55V50ZM210 551Q208 553 218 567T247 600T288 640T335 679Q359 697 381 697Q393 697 402 690T411 670Q411 645 374 620Q349 603 321 589T269 566T229 553T210 551Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="634" d="M69 72L481 585H101Q70 585 70 613V618Q70 647 101 647H556V578L142 62H543Q574 62 574 34V29Q574 0 543 0H69V72ZM315 720Q289 720 275 734T260 772Q260 795 275 809T316 824Q341 824 355 810T370 772Q370 749 356 735T315 720Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="503" d="M55 50L358 420H88Q70 420 63 426T55 445V451Q55 464 62 470T88 476H445V432L137 56H419Q437 56 444 50T452 31V25Q452 12 445 6T419 0H55V50ZM261 549Q235 549 221 563T206 601Q206 624 221 638T262 653Q287 653 301 639T316 601Q316 578 302 564T261 549Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="634" d="M69 72L481 585H101Q70 585 70 613V618Q70 647 101 647H556V578L142 62H543Q574 62 574 34V29Q574 0 543 0H69V72ZM420 873Q441 873 441 850Q441 832 422 807T378 759T334 719T311 703Q309 703 289 719T244 758T201 807T181 850Q181 873 202 873Q219 873 243 848T312 761Q356 822 379 847T420 873Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="503" d="M55 50L358 420H88Q70 420 63 426T55 445V451Q55 464 62 470T88 476H445V432L137 56H419Q437 56 444 50T452 31V25Q452 12 445 6T419 0H55V50ZM355 703Q376 703 376 680Q376 662 357 637T313 589T269 549T246 533Q244 533 224 549T179 588T136 637T116 680Q116 703 137 703Q154 703 178 678T247 591Q291 652 314 677T355 703Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="413" d="M73 378Q55 378 49 384T42 404V410Q42 423 48 429T73 436H144L156 500Q170 577 207 618T307 659Q349 659 373 642T398 602Q398 593 394 586T386 573T377 565T373 563Q363 577 345 588T301 599Q266 599 248 573T219 489L209 436H329Q347 436 353 430T360 410V404Q360 391 354 385T329 378H200L140 50Q131 -4 98 -4Q84 -4 75 1T67 9L134 378H73Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="571" d="M289 -12Q216 -12 163 10T81 62Q52 91 52 115Q52 126 58 134T71 147T85 155T92 156Q118 111 167 79T288 47Q363 47 405 78T447 167Q447 199 433 220T393 255T336 279T269 297Q232 306 197 317T133 349T88 401T70 481Q70 519 86 551T132 608T200 645T287 659Q355 659 403 639T476 594Q501 569 501 547Q501 536 495 528T482 515T468 507T461 506Q438 546 393 573T288 600Q220 600 178 568T136 483Q136 452 148 432T182 399T234 378T302 360Q341 351 379 338T447 304T495 251T514 170Q514 129 498 96T453 38T382 1T289 -12ZM224 -225Q221 -228 214 -229T201 -228T192 -222T191 -209L251 -84Q259 -68 265 -59T291 -50H296Q310 -50 320 -57T331 -79V-83Q331 -91 324 -100T305 -124L224 -225Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="484" d="M242 -12Q179 -12 135 5T68 45Q46 67 46 85Q46 95 51 102T62 113T74 119T81 120Q105 87 144 65T244 43Q304 43 335 65T367 123Q367 144 358 158T331 181T287 197T227 211Q199 217 170 225T116 247T76 285T60 344Q60 376 73 402T110 448T167 477T242 488Q301 488 339 473T398 437Q418 417 418 397Q418 388 413 381T401 370T389 364T382 363Q362 394 327 413T242 433Q183 433 153 411T122 353Q122 315 155 299T255 269Q284 263 315 255T371 232T413 194T430 133Q430 67 380 28T242 -12ZM158 -225Q155 -228 148 -229T135 -228T126 -222T125 -209L185 -84Q193 -68 199 -59T225 -50H230Q244 -50 254 -57T265 -79V-83Q265 -91 258 -100T239 -124L158 -225Z" />
+<glyph unicode="&#x21A;" glyph-name="uni021A" horiz-adv-x="614" d="M273 585H70Q39 585 39 613V618Q39 647 70 647H544Q575 647 575 618V613Q575 585 544 585H341V0H273V585ZM248 -225Q245 -228 238 -229T225 -228T216 -222T215 -209L275 -84Q283 -68 289 -59T315 -50H320Q334 -50 344 -57T355 -79V-83Q355 -91 348 -100T329 -124L248 -225Z" />
+<glyph unicode="&#x21B;" glyph-name="uni021B" horiz-adv-x="376" d="M245 -12Q175 -12 144 26T112 140V418H41V476H112V559Q112 577 120 583T141 590H148Q161 590 168 584T176 559V476H330V418H176V147Q176 94 191 69T247 44Q272 44 290 53T321 79Q323 81 333 74T343 47Q343 31 328 16Q316 4 295 -4T245 -12ZM149 -225Q146 -228 139 -229T126 -228T117 -222T116 -209L176 -84Q184 -68 190 -59T216 -50H221Q235 -50 245 -57T256 -79V-83Q256 -91 249 -100T230 -124L149 -225Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="530" d="M156 549Q134 549 134 572Q134 590 154 615T198 663T242 703T265 719Q267 719 287 703T332 664T375 615T395 572Q395 549 374 549Q357 549 333 574T264 661Q241 630 225 609T196 575T174 555T156 549Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="530" d="M374 703Q395 703 395 680Q395 662 376 637T332 589T288 549T265 533Q263 533 243 549T198 588T155 637T135 680Q135 703 156 703Q173 703 197 678T266 591Q310 652 333 677T374 703Z" />
+<glyph unicode="&#x2C9;" glyph-name="overscore" horiz-adv-x="530" d="M161 568Q122 568 122 597V602Q122 616 132 623T161 630H369Q408 630 408 602V597Q408 568 369 568H161Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="530" d="M265 548Q244 548 219 559T173 586T138 620T124 653Q124 682 153 682Q160 682 173 673T201 651T233 621T267 591Q282 605 298 621T329 650T356 673T377 682Q406 682 406 653Q406 638 392 620T356 585T310 559T265 548Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="530" d="M265 549Q239 549 225 563T210 601Q210 624 225 638T266 653Q291 653 305 639T320 601Q320 578 306 564T265 549Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="530" d="M264 548Q238 548 217 557T181 581T157 618T148 665Q148 690 157 711T181 748T218 773T265 782Q290 782 311 773T348 749T372 712T381 665Q381 640 372 619T348 582T311 557T264 548ZM264 593Q293 593 312 613T331 665Q331 696 312 717T264 738Q235 738 216 717T197 665Q197 635 216 614T264 593Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="530" d="M327 0Q280 -35 264 -60T247 -104Q247 -121 257 -131T281 -142Q307 -142 320 -121Q331 -121 341 -129T352 -155Q352 -175 332 -187T280 -199Q238 -199 213 -177T187 -117Q187 -80 217 -43T317 26L327 0Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="530" d="M146 555Q121 555 121 581Q121 597 126 613T140 642T162 663T190 671Q214 671 254 650T338 591Q342 604 347 617T358 640T370 657T384 664Q409 664 409 638Q409 621 404 605T390 577T368 556T340 548Q316 548 276 569T192 628Q188 615 183 602T172 579T160 562T146 555Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="530" d="M294 551Q292 553 302 567T331 600T372 640T419 679Q443 697 465 697Q477 697 486 690T495 670Q495 645 458 620Q433 603 405 589T353 566T313 553T294 551ZM115 551Q113 553 123 567T152 600T193 640T240 679Q264 697 286 697Q298 697 307 690T316 670Q316 645 279 620Q254 603 226 589T174 566T134 553T115 551Z" />
+<glyph unicode="&#x394;" glyph-name="increment" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x3A9;" glyph-name="Omega" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x3BC;" glyph-name="mu" horiz-adv-x="575" d="M267 -12Q220 -12 184 3Q161 13 143 27V-290H78V476H143V191Q145 127 176 89Q210 49 271 49Q327 49 367 77T428 150V476H493V3Q493 1 487 -1T471 -4Q454 -4 442 8T429 59V74Q404 36 364 12T267 -12Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="787" d="M393 -12Q321 -12 261 13T157 82T89 187T65 322Q65 394 90 456T159 563T263 633T394 659Q466 659 526 634T629 565T697 460T722 325Q722 253 697 191T628 84T524 14T393 -12ZM395 36Q455 36 505 57T592 116T648 206T668 323Q668 385 647 437T589 528T502 589T392 611Q332 611 282 590T195 531T139 441T119 324Q119 262 140 210T198 119T285 58T395 36ZM306 139Q281 139 281 167V452Q281 467 290 476T313 486H409Q475 486 510 457T546 373Q546 317 512 287T412 257H341V167Q341 139 315 139H306ZM340 306H413Q449 306 468 323T487 372Q487 403 467 420T409 437H340V306Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="555" d="M91 213Q73 213 67 219T60 239V245Q60 258 66 264T91 271H464Q482 271 488 265T495 245V239Q495 226 489 220T464 213H91Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="723" d="M91 213Q73 213 67 219T60 239V245Q60 258 66 264T91 271H632Q650 271 656 265T663 245V239Q663 226 657 220T632 213H91Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="232" d="M88 426Q75 426 66 435T57 462V465Q57 473 60 484T74 514L158 655Q160 659 166 660T179 660T190 653T191 640L132 462Q125 445 118 436T92 426H88Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="232" d="M74 432Q72 428 66 427T53 427T42 434T41 447L101 625Q108 643 114 652T140 661H145Q157 661 166 652T175 625V622Q175 600 159 573L74 432Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="232" d="M64 -129Q62 -133 56 -134T43 -134T32 -127T31 -114L91 64Q98 82 104 91T130 100H135Q147 100 156 91T165 64V61Q165 39 149 12L64 -129Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="385" d="M241 426Q229 426 220 435T211 462V465Q211 487 227 514L312 655Q315 659 321 660T334 660T344 653T345 640L285 462Q279 443 272 435T246 426H241ZM87 426Q75 426 66 435T57 462V465Q57 487 73 514L158 655Q161 659 167 660T180 660T190 653T191 640L131 462Q125 443 118 435T92 426H87Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="385" d="M227 432Q225 428 219 427T206 427T195 434T194 447L254 625Q261 643 267 652T293 661H298Q310 661 319 652T328 625V622Q328 600 312 573L227 432ZM73 432Q71 428 65 427T52 427T41 434T40 447L100 625Q107 643 113 652T139 661H144Q156 661 165 652T174 625V622Q174 600 158 573L73 432Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="385" d="M217 -129Q215 -133 209 -134T196 -134T185 -127T184 -114L244 64Q251 82 257 91T283 100H288Q300 100 309 91T318 64V61Q318 39 302 12L217 -129ZM63 -129Q61 -133 55 -134T42 -134T31 -127T30 -114L90 64Q97 82 103 91T129 100H134Q146 100 155 91T164 64V61Q164 39 148 12L63 -129Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="540" d="M265 -160Q252 -160 249 -154T245 -137L230 298Q229 315 235 337T254 380L90 358Q63 354 53 364T43 396Q43 418 53 428T90 434L254 412L230 606Q226 633 236 643T270 653Q293 653 303 643T310 606L286 412L450 434Q477 438 487 428T497 396Q497 374 487 364T450 358L286 380Q299 360 305 338T310 298L295 -137Q294 -148 291 -154T275 -160H265Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="540" d="M265 -160Q252 -160 249 -154T245 -137L237 103L90 82Q63 78 53 88T43 120Q43 142 53 152T90 158L236 137L230 298Q229 315 235 338T256 380L90 358Q63 354 53 364T43 396Q43 418 53 428T90 434L254 412L230 606Q226 633 236 643T270 653Q293 653 303 643T310 606L286 412L450 434Q477 438 487 428T497 396Q497 374 487 364T450 358L284 380Q298 361 304 338T310 298L304 137L450 158Q477 162 487 152T497 120Q497 98 487 88T450 82L303 103L295 -137Q294 -148 291 -154T275 -160H265Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="354" d="M177 139Q156 139 137 147T105 169T83 202T75 242Q75 263 83 282T104 315T137 338T177 346Q198 346 217 338T249 316T271 282T279 242Q279 221 271 202T250 169T217 147T177 139Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="657" d="M532 -6Q488 -6 488 38V48Q488 69 499 81T532 93H542Q564 93 575 81T587 48V38Q587 -6 542 -6H532ZM323 -6Q279 -6 279 38V48Q279 69 290 81T323 93H333Q355 93 366 81T378 48V38Q378 -6 333 -6H323ZM114 -6Q70 -6 70 38V48Q70 69 81 81T114 93H124Q146 93 157 81T169 48V38Q169 -6 124 -6H114Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1116" d="M201 347Q169 347 142 358T95 390T64 438T52 496V506Q52 537 64 564T96 612T143 645T202 657Q233 657 260 645T308 613T339 566T351 508V498Q351 467 339 440T307 392T260 359T201 347ZM128 -8Q127 -9 122 -8T111 -4T101 5T96 20Q96 34 102 45T126 75L629 661Q630 662 635 662T646 658T656 648T661 633Q661 612 631 578L128 -8ZM914 -4Q882 -4 855 7T808 39T777 87T765 145V155Q765 186 777 213T809 261T856 294T915 306Q946 306 973 294T1021 262T1052 215T1064 157V147Q1064 116 1052 89T1020 41T973 8T914 -4ZM560 -4Q528 -4 501 7T454 39T423 87T411 145V155Q411 186 423 213T455 261T502 294T561 306Q592 306 619 294T667 262T698 215T710 157V147Q710 116 698 89T666 41T619 8T560 -4ZM202 399Q241 399 266 426T292 495V509Q292 551 266 578T201 606Q162 606 137 579T111 509V495Q111 454 137 427T202 399ZM915 48Q954 48 979 75T1005 144V158Q1005 200 979 227T914 255Q875 255 850 228T824 158V144Q824 103 850 76T915 48ZM561 48Q600 48 625 75T651 144V158Q651 200 625 227T560 255Q521 255 496 228T470 158V144Q470 103 496 76T561 48Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="342" d="M277 103V100Q277 92 271 85T253 78H252Q238 78 218 89T177 117T135 155T98 194T71 225T60 243Q60 246 70 259T97 291T135 330T177 368T218 396T252 408H253Q264 408 270 401T277 385V383Q277 372 264 357T230 324T180 284T121 242Q151 221 179 201T229 162T264 128T277 103Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="342" d="M66 103Q66 113 79 128T113 161T163 200T222 242Q191 263 163 284T114 323T79 357T66 383V385Q66 393 72 400T89 408H90Q104 408 124 397T165 368T207 331T244 292T271 260T282 243Q282 239 272 226T245 194T207 156T165 118T124 90T90 78H89Q79 78 73 85T66 100V103Z" />
+<glyph unicode="&#x2044;" glyph-name="uni2044" horiz-adv-x="655" d="M67 -8Q66 -9 61 -8T50 -4T40 5T35 20Q35 34 41 45T65 75L588 661Q589 662 594 662T605 658T615 648T620 633Q620 612 590 578L67 -8Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="656" d="M82 376Q64 376 58 382T51 402V408Q51 421 57 427T82 434H152Q162 486 183 527T237 598T310 643T399 659Q446 659 483 646T546 615T585 575T599 536Q599 524 593 517T580 505T567 499T560 499Q534 548 495 575T395 602Q328 602 282 557T218 434H434Q452 434 458 428T465 408V402Q465 389 459 383T434 376H210Q209 369 209 362T209 348V276H434Q452 276 458 270T465 250V244Q465 231 459 225T434 218H217Q234 140 282 93T401 46Q463 46 501 75T563 158Q564 159 570 158T583 153T597 140T603 120Q603 102 589 79T549 36T486 2T404 -12Q301 -12 235 48T151 218H82Q64 218 58 224T51 244V250Q51 263 57 269T82 276H144V347Q144 355 144 361T145 376H82Z" />
+<glyph unicode="&#x2113;" glyph-name="afii61289" horiz-adv-x="434" d="M199 341Q228 365 251 395T291 456T317 513T326 558Q326 584 315 584Q308 584 300 573Q291 561 278 539T250 486T222 419T199 341ZM44 138Q30 151 19 172T7 228Q25 237 33 240T49 246L59 251Q63 321 81 383T126 497T183 588T245 652Q285 683 327 683Q367 683 395 652T423 558Q423 509 402 463T349 375T276 294T196 223Q196 218 196 214T195 204Q195 158 207 133T237 107Q254 107 273 133T310 217Q322 220 336 220Q355 220 373 213T404 186Q397 149 382 114T345 52T293 9T228 -8Q206 -8 182 1T135 30T94 78T66 146L44 138Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="707" d="M373 341Q352 341 352 366V622Q352 651 378 651H403Q417 651 422 646T433 629L496 473L559 629Q564 641 570 646T589 651H612Q638 651 638 622V366Q638 341 616 341H609Q588 341 588 366V579L523 418Q521 412 517 408T495 404Q478 404 474 408T468 418L402 578V366Q402 341 382 341H373ZM60 600Q36 600 36 622V626Q36 648 60 648H274Q298 648 298 626V622Q298 600 274 600H194V366Q194 341 170 341H163Q141 341 141 366V600H60Z" />
+<glyph unicode="&#x2126;" glyph-name="uni2126" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x212E;" glyph-name="estimated" horiz-adv-x="830" d="M415 671Q492 671 560 645T681 573T763 463T793 324H185Q175 324 175 313V116Q223 65 286 38T415 10Q490 10 555 43T667 131H724Q667 62 585 26T415 -11Q340 -11 272 14T151 84T67 191T36 330Q36 403 66 465T147 574T268 645T415 671ZM415 650Q345 650 282 622T175 543V358Q175 347 185 347H646Q655 347 655 358V542Q610 594 546 622T415 650Z" />
+<glyph unicode="&#x2202;" glyph-name="partialdiff" horiz-adv-x="538" d="M303 481Q335 481 361 471T404 445V464Q404 499 399 528T381 578T343 610T282 622Q247 622 208 612L189 679Q271 710 332 710Q421 710 464 649T508 475Q508 370 481 283T409 133T311 35T201 0Q165 0 134 10T80 44T43 102T30 187Q30 256 55 310T120 403T208 461T303 481ZM205 105Q229 105 256 121T310 167T359 243T393 349Q382 361 360 373T309 386Q275 386 244 371T188 331T149 270T134 194Q134 157 150 131T205 105Z" />
+<glyph unicode="&#x2206;" glyph-name="uni2206" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x220F;" glyph-name="product" horiz-adv-x="634" d="M484 0V607H150V0H50V700H584V0H484Z" />
+<glyph unicode="&#x2211;" glyph-name="summation" horiz-adv-x="475" d="M110 80H437V0H25V96L209 325L25 554V650H437V570H115L288 353V300L110 80Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="630" d="M133 237Q115 237 109 244T102 264V271Q102 284 108 291T133 298H496Q514 298 520 291T527 271V264Q527 251 521 244T496 237H133Z" />
+<glyph unicode="&#x2215;" glyph-name="fraction" horiz-adv-x="655" d="M67 -8Q66 -9 61 -8T50 -4T40 5T35 20Q35 34 41 45T65 75L588 661Q589 662 594 662T605 658T615 648T620 633Q620 612 590 578L67 -8Z" />
+<glyph unicode="&#x2219;" glyph-name="middot" horiz-adv-x="251" d="M120 193Q76 193 76 237V247Q76 268 87 280T120 292H130Q152 292 163 280T175 247V237Q175 193 130 193H120Z" />
+<glyph unicode="&#x221A;" glyph-name="radical" horiz-adv-x="649" d="M256 501L369 171L566 700H649L386 0H283L136 433H30V501H256Z" />
+<glyph unicode="&#x221E;" glyph-name="infinity" horiz-adv-x="733" d="M703 271Q703 231 690 203T654 156T604 129T547 120Q518 120 493 128T447 150T405 181T366 217Q347 199 328 182T287 151T240 129T186 120Q157 120 129 129T79 156T44 203T30 271Q30 310 43 338T79 385T129 413T186 422Q215 422 240 414T286 392T327 361T366 325Q385 343 405 360T446 391T493 413T547 422Q576 422 604 414T654 387T689 340T703 271ZM187 222Q223 222 252 235T308 271Q282 292 253 306T187 320Q167 320 149 309T130 271Q130 244 148 233T187 222ZM546 320Q510 320 481 306T425 271Q451 249 480 236T546 222Q566 222 584 233T603 271Q603 298 585 309T546 320Z" />
+<glyph unicode="&#x222B;" glyph-name="integral'" horiz-adv-x="396" d="M164 596Q172 654 201 685Q228 714 271 723T375 724V648Q314 653 288 640T255 584L168 -32Q159 -95 128 -126Q105 -149 70 -155T-15 -155V-81Q22 -84 45 -75T74 -25L164 596Z" />
+<glyph unicode="&#x2248;" glyph-name="approxequal" horiz-adv-x="529" d="M146 134Q121 134 121 160Q121 176 126 192T140 221T162 242T190 250Q214 250 254 229T338 170Q342 183 347 196T358 219T370 236T384 243Q409 243 409 217Q409 200 404 184T390 156T368 135T340 127Q316 127 276 148T192 207Q188 194 183 181T172 158T160 141T146 134ZM146 268Q121 268 121 294Q121 310 126 326T140 355T162 376T190 384Q214 384 254 363T338 304Q342 317 347 330T358 353T370 370T384 377Q409 377 409 351Q409 334 404 318T390 290T368 269T340 261Q316 261 276 282T192 341Q188 328 183 315T172 292T160 275T146 268Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="630" d="M133 346Q115 346 109 353T102 373V380Q102 393 108 400T133 407H350L419 549Q427 565 436 567T457 565L463 562Q475 556 478 548T474 522L418 407H496Q514 407 520 400T527 380V373Q527 360 521 353T496 346H384L303 180H496Q514 180 520 173T527 153V146Q527 133 521 126T496 119H278L210 -22Q202 -39 193 -41T172 -38L166 -35Q154 -30 151 -21T155 5L210 119H133Q115 119 109 126T102 146V153Q102 166 108 173T133 180H245L326 346H133Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="630" d="M109 423Q109 445 114 452T129 465L496 626Q498 627 507 618T518 595Q522 584 510 570T477 546L173 423L477 301Q497 293 508 279T518 251Q516 235 508 227T496 220L129 380Q119 385 114 392T109 423ZM133 117Q115 117 109 124T102 144V151Q102 164 108 171T133 178H496Q514 178 520 171T527 151V144Q527 131 521 124T496 117H133Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="630" d="M111 598Q113 612 122 621T133 629L500 468Q510 463 515 456T520 426Q520 403 515 396T500 383L133 223Q130 221 122 229T111 254Q110 268 122 282T152 304L455 426L152 549Q132 558 120 572T111 598ZM133 120Q115 120 109 127T102 147V154Q102 167 108 174T133 181H496Q514 181 520 174T527 154V147Q527 134 521 127T496 120H133Z" />
+<glyph unicode="&#x25CA;" glyph-name="lozenge" horiz-adv-x="520" d="M30 388L226 776H294L490 388L294 0H226L30 388ZM370 388L260 616L150 388L260 159L370 388Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="569" d="M126 418H25V476H126V520Q126 602 161 645T266 689Q297 689 316 682T348 663Q361 650 361 633Q361 616 353 608T342 602Q331 613 311 622T266 631Q225 631 207 604T189 517V476H316V418H190V0H126V418ZM417 449Q417 467 425 473T446 480H453Q466 480 474 474T482 449V0H417V449ZM449 595Q407 595 407 638Q407 681 451 681Q493 681 493 638Q493 595 449 595Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="569" d="M126 418H25V476H126V520Q126 602 161 645T266 689Q297 689 316 682T348 663Q361 650 361 633Q361 616 353 608T342 602Q331 613 311 622T266 631Q225 631 207 604T189 517V476H316V418H190V0H126V418ZM417 674Q417 676 423 678T439 681Q456 681 469 669T482 618V0H417V674Z" />
+
+<hkern u1="&#x141;" u2="&#xdd;" k="80" />
+<hkern u1="&#x141;" u2="Y" k="80" />
+<hkern u1="&#x141;" u2="y" k="40" />
+<hkern u1="&#x141;" u2="@" k="50" />
+<hkern u1="&#x141;" u2="A" k="-20" />
+<hkern u1="&#x141;" u2="C" k="50" />
+<hkern u1="&#x141;" u2="G" k="50" />
+<hkern u1="&#x141;" u2="O" k="50" />
+<hkern u1="&#x141;" u2="Q" k="50" />
+<hkern u1="&#x141;" u2="T" k="100" />
+<hkern u1="&#x141;" u2="U" k="15" />
+<hkern u1="&#x141;" u2="V" k="90" />
+<hkern u1="&#x141;" u2="W" k="70" />
+<hkern u1="&#x141;" u2="\" k="50" />
+<hkern u1="&#x141;" u2="a" k="20" />
+<hkern u1="&#x141;" u2="c" k="20" />
+<hkern u1="&#x141;" u2="d" k="20" />
+<hkern u1="&#x141;" u2="e" k="20" />
+<hkern u1="&#x141;" u2="o" k="20" />
+<hkern u1="&#x141;" u2="q" k="20" />
+<hkern u1="&#x141;" u2="v" k="40" />
+<hkern u1="&#x141;" u2="w" k="10" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd6;" k="50" />
+<hkern u1="&#x141;" u2="&#xdc;" k="15" />
+<hkern u1="&#x141;" u2="&#xe7;" k="20" />
+<hkern u1="&#x141;" u2="&#xae;" k="50" />
+<hkern u1="&#x141;" u2="&#xa9;" k="50" />
+<hkern u1="&#x141;" u2="&#x2122;" k="70" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd8;" k="50" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="50" />
+<hkern u1="&#x141;" u2="&#xe6;" k="20" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd5;" k="50" />
+<hkern u1="&#x141;" u2="&#x152;" k="50" />
+<hkern u1="&#x141;" u2="&#x153;" k="20" />
+<hkern u1="&#x141;" u2="&#x201c;" k="100" />
+<hkern u1="&#x141;" u2="&#x201d;" k="60" />
+<hkern u1="&#x141;" u2="&#x2018;" k="100" />
+<hkern u1="&#x141;" u2="&#x2019;" k="60" />
+<hkern u1="&#x141;" u2="&#x178;" k="80" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd3;" k="50" />
+<hkern u1="&#x141;" u2="&#xd4;" k="50" />
+<hkern u1="&#x141;" u2="&#xd2;" k="50" />
+<hkern u1="&#x141;" u2="&#xda;" k="15" />
+<hkern u1="&#x141;" u2="&#xdb;" k="15" />
+<hkern u1="&#x141;" u2="&#xd9;" k="15" />
+<hkern u1="&#x160;" u2="&#xdd;" k="-10" />
+<hkern u1="&#x160;" u2="Y" k="-10" />
+<hkern u1="&#x160;" u2="A" k="-5" />
+<hkern u1="&#x160;" u2="V" k="-10" />
+<hkern u1="&#x160;" u2="X" k="-5" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-5" />
+<hkern u1="&#x160;" u2="&#x178;" k="-10" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-5" />
+<hkern u1="S" u2="&#xdd;" k="-10" />
+<hkern u1="S" u2="Y" k="-10" />
+<hkern u1="S" u2="A" k="-5" />
+<hkern u1="S" u2="V" k="-10" />
+<hkern u1="S" u2="X" k="-5" />
+<hkern u1="S" u2="&#xc4;" k="-5" />
+<hkern u1="S" u2="&#xc5;" k="-5" />
+<hkern u1="S" u2="&#xc6;" k="-5" />
+<hkern u1="S" u2="&#xc0;" k="-5" />
+<hkern u1="S" u2="&#xc3;" k="-5" />
+<hkern u1="S" u2="&#x178;" k="-10" />
+<hkern u1="S" u2="&#xc2;" k="-5" />
+<hkern u1="S" u2="&#xc1;" k="-5" />
+<hkern u1="&#xdd;" u2="&#x160;" k="-10" />
+<hkern u1="&#xdd;" u2="S" k="-10" />
+<hkern u1="&#xdd;" u2="&#x161;" k="50" />
+<hkern u1="&#xdd;" u2="s" k="50" />
+<hkern u1="&#xdd;" u2="y" k="20" />
+<hkern u1="&#xdd;" u2="z" k="30" />
+<hkern u1="&#xdd;" u2="&amp;" k="30" />
+<hkern u1="&#xdd;" u2=")" k="-50" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="50" />
+<hkern u1="&#xdd;" u2="-" k="50" />
+<hkern u1="&#xdd;" u2="." k="50" />
+<hkern u1="&#xdd;" u2=":" k="30" />
+<hkern u1="&#xdd;" u2=";" k="30" />
+<hkern u1="&#xdd;" u2="@" k="30" />
+<hkern u1="&#xdd;" u2="A" k="50" />
+<hkern u1="&#xdd;" u2="C" k="25" />
+<hkern u1="&#xdd;" u2="G" k="25" />
+<hkern u1="&#xdd;" u2="J" k="70" />
+<hkern u1="&#xdd;" u2="O" k="25" />
+<hkern u1="&#xdd;" u2="Q" k="25" />
+<hkern u1="&#xdd;" u2="T" k="-20" />
+<hkern u1="&#xdd;" u2="V" k="-30" />
+<hkern u1="&#xdd;" u2="X" k="-20" />
+<hkern u1="&#xdd;" u2="]" k="-50" />
+<hkern u1="&#xdd;" u2="a" k="55" />
+<hkern u1="&#xdd;" u2="c" k="55" />
+<hkern u1="&#xdd;" u2="d" k="55" />
+<hkern u1="&#xdd;" u2="e" k="55" />
+<hkern u1="&#xdd;" u2="f" k="10" />
+<hkern u1="&#xdd;" u2="g" k="40" />
+<hkern u1="&#xdd;" u2="m" k="40" />
+<hkern u1="&#xdd;" u2="n" k="40" />
+<hkern u1="&#xdd;" u2="o" k="55" />
+<hkern u1="&#xdd;" u2="p" k="40" />
+<hkern u1="&#xdd;" u2="q" k="55" />
+<hkern u1="&#xdd;" u2="r" k="40" />
+<hkern u1="&#xdd;" u2="t" k="20" />
+<hkern u1="&#xdd;" u2="u" k="40" />
+<hkern u1="&#xdd;" u2="v" k="20" />
+<hkern u1="&#xdd;" u2="w" k="10" />
+<hkern u1="&#xdd;" u2="x" k="20" />
+<hkern u1="&#xdd;" u2="}" k="-50" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="50" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="25" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="55" />
+<hkern u1="&#xdd;" u2="&#x131;" k="40" />
+<hkern u1="&#xdd;" u2="&#xae;" k="30" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="50" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="25" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="30" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="55" />
+<hkern u1="&#xdd;" u2="&#xab;" k="60" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="50" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="25" />
+<hkern u1="&#xdd;" u2="&#x152;" k="25" />
+<hkern u1="&#xdd;" u2="&#x153;" k="55" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="60" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="40" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="50" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="50" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="25" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="25" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="25" />
+<hkern u1="Y" u2="&#x160;" k="-10" />
+<hkern u1="Y" u2="S" k="-10" />
+<hkern u1="Y" u2="&#x161;" k="50" />
+<hkern u1="Y" u2="s" k="50" />
+<hkern u1="Y" u2="y" k="20" />
+<hkern u1="Y" u2="z" k="30" />
+<hkern u1="Y" u2="&amp;" k="30" />
+<hkern u1="Y" u2=")" k="-50" />
+<hkern u1="Y" u2="&#x2c;" k="50" />
+<hkern u1="Y" u2="-" k="50" />
+<hkern u1="Y" u2="." k="50" />
+<hkern u1="Y" u2=":" k="30" />
+<hkern u1="Y" u2=";" k="30" />
+<hkern u1="Y" u2="@" k="30" />
+<hkern u1="Y" u2="A" k="51" />
+<hkern u1="Y" u2="C" k="25" />
+<hkern u1="Y" u2="G" k="25" />
+<hkern u1="Y" u2="J" k="70" />
+<hkern u1="Y" u2="O" k="25" />
+<hkern u1="Y" u2="Q" k="25" />
+<hkern u1="Y" u2="T" k="-20" />
+<hkern u1="Y" u2="V" k="-30" />
+<hkern u1="Y" u2="X" k="-20" />
+<hkern u1="Y" u2="]" k="-50" />
+<hkern u1="Y" u2="a" k="55" />
+<hkern u1="Y" u2="c" k="55" />
+<hkern u1="Y" u2="d" k="55" />
+<hkern u1="Y" u2="e" k="55" />
+<hkern u1="Y" u2="f" k="10" />
+<hkern u1="Y" u2="g" k="40" />
+<hkern u1="Y" u2="m" k="40" />
+<hkern u1="Y" u2="n" k="40" />
+<hkern u1="Y" u2="o" k="55" />
+<hkern u1="Y" u2="p" k="40" />
+<hkern u1="Y" u2="q" k="55" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="t" k="20" />
+<hkern u1="Y" u2="u" k="40" />
+<hkern u1="Y" u2="v" k="20" />
+<hkern u1="Y" u2="w" k="10" />
+<hkern u1="Y" u2="x" k="20" />
+<hkern u1="Y" u2="}" k="-50" />
+<hkern u1="Y" u2="&#xc4;" k="50" />
+<hkern u1="Y" u2="&#xc5;" k="50" />
+<hkern u1="Y" u2="&#xd6;" k="25" />
+<hkern u1="Y" u2="&#xe7;" k="55" />
+<hkern u1="Y" u2="&#x131;" k="40" />
+<hkern u1="Y" u2="&#xae;" k="30" />
+<hkern u1="Y" u2="&#xa9;" k="30" />
+<hkern u1="Y" u2="&#xc6;" k="50" />
+<hkern u1="Y" u2="&#xd8;" k="25" />
+<hkern u1="Y" u2="&#x3c0;" k="30" />
+<hkern u1="Y" u2="&#xe6;" k="55" />
+<hkern u1="Y" u2="&#xab;" k="60" />
+<hkern u1="Y" u2="&#xbb;" k="40" />
+<hkern u1="Y" u2="&#x2026;" k="50" />
+<hkern u1="Y" u2="&#xc0;" k="50" />
+<hkern u1="Y" u2="&#xc3;" k="50" />
+<hkern u1="Y" u2="&#xd5;" k="25" />
+<hkern u1="Y" u2="&#x152;" k="25" />
+<hkern u1="Y" u2="&#x153;" k="55" />
+<hkern u1="Y" u2="&#x2013;" k="50" />
+<hkern u1="Y" u2="&#x2014;" k="50" />
+<hkern u1="Y" u2="&#x2039;" k="60" />
+<hkern u1="Y" u2="&#x203a;" k="40" />
+<hkern u1="Y" u2="&#x201a;" k="50" />
+<hkern u1="Y" u2="&#x201e;" k="50" />
+<hkern u1="Y" u2="&#xc2;" k="50" />
+<hkern u1="Y" u2="&#xc1;" k="50" />
+<hkern u1="Y" u2="&#xd3;" k="25" />
+<hkern u1="Y" u2="&#xd4;" k="25" />
+<hkern u1="Y" u2="&#xd2;" k="25" />
+<hkern u1="y" u2="&#x2c;" k="50" />
+<hkern u1="y" u2="-" k="10" />
+<hkern u1="y" u2="." k="50" />
+<hkern u1="y" u2="a" k="10" />
+<hkern u1="y" u2="c" k="10" />
+<hkern u1="y" u2="d" k="10" />
+<hkern u1="y" u2="e" k="10" />
+<hkern u1="y" u2="o" k="10" />
+<hkern u1="y" u2="q" k="10" />
+<hkern u1="y" u2="&#xe7;" k="10" />
+<hkern u1="y" u2="&#xe6;" k="10" />
+<hkern u1="y" u2="&#x2026;" k="50" />
+<hkern u1="y" u2="&#x153;" k="10" />
+<hkern u1="y" u2="&#x2013;" k="10" />
+<hkern u1="y" u2="&#x2014;" k="10" />
+<hkern u1="y" u2="&#x201c;" k="-30" />
+<hkern u1="y" u2="&#x201d;" k="-30" />
+<hkern u1="y" u2="&#x2018;" k="-30" />
+<hkern u1="y" u2="&#x2019;" k="-30" />
+<hkern u1="y" u2="&#x201a;" k="50" />
+<hkern u1="y" u2="&#x201e;" k="50" />
+<hkern u1="&#xde;" u2="&#xdd;" k="25" />
+<hkern u1="&#xde;" u2="Y" k="25" />
+<hkern u1="&#xde;" u2="&#x17d;" k="45" />
+<hkern u1="&#xde;" u2="Z" k="45" />
+<hkern u1="&#xde;" u2="z" k="10" />
+<hkern u1="&#xde;" u2="&#x2c;" k="40" />
+<hkern u1="&#xde;" u2="." k="40" />
+<hkern u1="&#xde;" u2="/" k="50" />
+<hkern u1="&#xde;" u2="?" k="20" />
+<hkern u1="&#xde;" u2="A" k="20" />
+<hkern u1="&#xde;" u2="J" k="45" />
+<hkern u1="&#xde;" u2="T" k="30" />
+<hkern u1="&#xde;" u2="V" k="20" />
+<hkern u1="&#xde;" u2="W" k="40" />
+<hkern u1="&#xde;" u2="X" k="25" />
+<hkern u1="&#xde;" u2="a" k="5" />
+<hkern u1="&#xde;" u2="b" k="10" />
+<hkern u1="&#xde;" u2="c" k="5" />
+<hkern u1="&#xde;" u2="d" k="5" />
+<hkern u1="&#xde;" u2="e" k="5" />
+<hkern u1="&#xde;" u2="h" k="10" />
+<hkern u1="&#xde;" u2="k" k="10" />
+<hkern u1="&#xde;" u2="l" k="10" />
+<hkern u1="&#xde;" u2="m" k="5" />
+<hkern u1="&#xde;" u2="n" k="5" />
+<hkern u1="&#xde;" u2="o" k="5" />
+<hkern u1="&#xde;" u2="p" k="5" />
+<hkern u1="&#xde;" u2="q" k="5" />
+<hkern u1="&#xde;" u2="r" k="5" />
+<hkern u1="&#xde;" u2="u" k="5" />
+<hkern u1="&#xde;" u2="x" k="10" />
+<hkern u1="&#xde;" u2="&#xc4;" k="20" />
+<hkern u1="&#xde;" u2="&#xc5;" k="20" />
+<hkern u1="&#xde;" u2="&#xe7;" k="5" />
+<hkern u1="&#xde;" u2="&#x131;" k="5" />
+<hkern u1="&#xde;" u2="&#xc6;" k="20" />
+<hkern u1="&#xde;" u2="&#xe6;" k="5" />
+<hkern u1="&#xde;" u2="&#x2026;" k="40" />
+<hkern u1="&#xde;" u2="&#xc0;" k="20" />
+<hkern u1="&#xde;" u2="&#xc3;" k="20" />
+<hkern u1="&#xde;" u2="&#x153;" k="5" />
+<hkern u1="&#xde;" u2="&#x201c;" k="20" />
+<hkern u1="&#xde;" u2="&#x2018;" k="20" />
+<hkern u1="&#xde;" u2="&#x178;" k="25" />
+<hkern u1="&#xde;" u2="&#x201a;" k="40" />
+<hkern u1="&#xde;" u2="&#x201e;" k="40" />
+<hkern u1="&#xde;" u2="&#xc2;" k="20" />
+<hkern u1="&#xde;" u2="&#xc1;" k="20" />
+<hkern u1="&#x17d;" u2="&#xf0;" k="30" />
+<hkern u1="&#x17d;" u2="y" k="30" />
+<hkern u1="&#x17d;" u2="-" k="50" />
+<hkern u1="&#x17d;" u2="A" k="10" />
+<hkern u1="&#x17d;" u2="C" k="45" />
+<hkern u1="&#x17d;" u2="G" k="45" />
+<hkern u1="&#x17d;" u2="J" k="10" />
+<hkern u1="&#x17d;" u2="O" k="45" />
+<hkern u1="&#x17d;" u2="Q" k="45" />
+<hkern u1="&#x17d;" u2="T" k="10" />
+<hkern u1="&#x17d;" u2="a" k="40" />
+<hkern u1="&#x17d;" u2="b" k="10" />
+<hkern u1="&#x17d;" u2="c" k="40" />
+<hkern u1="&#x17d;" u2="d" k="40" />
+<hkern u1="&#x17d;" u2="e" k="40" />
+<hkern u1="&#x17d;" u2="f" k="20" />
+<hkern u1="&#x17d;" u2="g" k="20" />
+<hkern u1="&#x17d;" u2="h" k="10" />
+<hkern u1="&#x17d;" u2="i" k="20" />
+<hkern u1="&#x17d;" u2="j" k="15" />
+<hkern u1="&#x17d;" u2="k" k="10" />
+<hkern u1="&#x17d;" u2="l" k="10" />
+<hkern u1="&#x17d;" u2="m" k="20" />
+<hkern u1="&#x17d;" u2="n" k="20" />
+<hkern u1="&#x17d;" u2="o" k="40" />
+<hkern u1="&#x17d;" u2="p" k="20" />
+<hkern u1="&#x17d;" u2="q" k="40" />
+<hkern u1="&#x17d;" u2="r" k="20" />
+<hkern u1="&#x17d;" u2="u" k="20" />
+<hkern u1="&#x17d;" u2="v" k="30" />
+<hkern u1="&#x17d;" u2="w" k="10" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="45" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="40" />
+<hkern u1="&#x17d;" u2="&#x131;" k="20" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="45" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="40" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="45" />
+<hkern u1="&#x17d;" u2="&#x152;" k="45" />
+<hkern u1="&#x17d;" u2="&#x153;" k="40" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="50" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="50" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="45" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="45" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="45" />
+<hkern u1="Z" u2="&#xf0;" k="30" />
+<hkern u1="Z" u2="y" k="30" />
+<hkern u1="Z" u2="-" k="50" />
+<hkern u1="Z" u2="A" k="10" />
+<hkern u1="Z" u2="C" k="45" />
+<hkern u1="Z" u2="G" k="45" />
+<hkern u1="Z" u2="J" k="10" />
+<hkern u1="Z" u2="O" k="45" />
+<hkern u1="Z" u2="Q" k="45" />
+<hkern u1="Z" u2="T" k="10" />
+<hkern u1="Z" u2="a" k="30" />
+<hkern u1="Z" u2="b" k="10" />
+<hkern u1="Z" u2="c" k="40" />
+<hkern u1="Z" u2="d" k="40" />
+<hkern u1="Z" u2="e" k="40" />
+<hkern u1="Z" u2="f" k="20" />
+<hkern u1="Z" u2="g" k="20" />
+<hkern u1="Z" u2="h" k="10" />
+<hkern u1="Z" u2="i" k="20" />
+<hkern u1="Z" u2="j" k="15" />
+<hkern u1="Z" u2="k" k="10" />
+<hkern u1="Z" u2="l" k="10" />
+<hkern u1="Z" u2="m" k="20" />
+<hkern u1="Z" u2="n" k="20" />
+<hkern u1="Z" u2="o" k="40" />
+<hkern u1="Z" u2="p" k="20" />
+<hkern u1="Z" u2="q" k="40" />
+<hkern u1="Z" u2="r" k="20" />
+<hkern u1="Z" u2="u" k="20" />
+<hkern u1="Z" u2="v" k="30" />
+<hkern u1="Z" u2="w" k="10" />
+<hkern u1="Z" u2="&#xc4;" k="10" />
+<hkern u1="Z" u2="&#xc5;" k="10" />
+<hkern u1="Z" u2="&#xd6;" k="45" />
+<hkern u1="Z" u2="&#xe7;" k="40" />
+<hkern u1="Z" u2="&#x131;" k="20" />
+<hkern u1="Z" u2="&#xc6;" k="10" />
+<hkern u1="Z" u2="&#xd8;" k="45" />
+<hkern u1="Z" u2="&#xe6;" k="30" />
+<hkern u1="Z" u2="&#xc0;" k="10" />
+<hkern u1="Z" u2="&#xc3;" k="10" />
+<hkern u1="Z" u2="&#xd5;" k="45" />
+<hkern u1="Z" u2="&#x152;" k="45" />
+<hkern u1="Z" u2="&#x153;" k="40" />
+<hkern u1="Z" u2="&#x2013;" k="50" />
+<hkern u1="Z" u2="&#x2014;" k="50" />
+<hkern u1="Z" u2="&#xc2;" k="10" />
+<hkern u1="Z" u2="&#xc1;" k="10" />
+<hkern u1="Z" u2="&#xd3;" k="45" />
+<hkern u1="Z" u2="&#xd4;" k="45" />
+<hkern u1="Z" u2="&#xd2;" k="45" />
+<hkern u1="z" u2="-" k="30" />
+<hkern u1="z" u2="a" k="20" />
+<hkern u1="z" u2="c" k="20" />
+<hkern u1="z" u2="d" k="20" />
+<hkern u1="z" u2="e" k="20" />
+<hkern u1="z" u2="g" k="10" />
+<hkern u1="z" u2="o" k="20" />
+<hkern u1="z" u2="q" k="20" />
+<hkern u1="z" u2="&#xe7;" k="20" />
+<hkern u1="z" u2="&#xe6;" k="20" />
+<hkern u1="z" u2="&#xab;" k="60" />
+<hkern u1="z" u2="&#x153;" k="20" />
+<hkern u1="z" u2="&#x2013;" k="30" />
+<hkern u1="z" u2="&#x2014;" k="30" />
+<hkern u1="z" u2="&#x2039;" k="60" />
+<hkern u1="&amp;" u2="&#xdd;" k="30" />
+<hkern u1="&amp;" u2="Y" k="30" />
+<hkern u1="&amp;" u2="A" k="-34" />
+<hkern u1="&amp;" u2="J" k="-20" />
+<hkern u1="&amp;" u2="T" k="70" />
+<hkern u1="&amp;" u2="V" k="20" />
+<hkern u1="&amp;" u2="W" k="20" />
+<hkern u1="&amp;" u2="X" k="-20" />
+<hkern u1="&amp;" u2="&#xc4;" k="-40" />
+<hkern u1="&amp;" u2="&#xc5;" k="-40" />
+<hkern u1="&amp;" u2="&#xc6;" k="-40" />
+<hkern u1="&amp;" u2="&#xc0;" k="-40" />
+<hkern u1="&amp;" u2="&#xc3;" k="-40" />
+<hkern u1="&amp;" u2="&#x178;" k="30" />
+<hkern u1="&amp;" u2="&#xc2;" k="-40" />
+<hkern u1="&amp;" u2="&#xc1;" k="-40" />
+<hkern u1="(" u2="&#xdd;" k="-50" />
+<hkern u1="(" u2="Y" k="-50" />
+<hkern u1="(" u2="V" k="-30" />
+<hkern u1="(" u2="W" k="-10" />
+<hkern u1="(" u2="X" k="-40" />
+<hkern u1="(" u2="g" k="-20" />
+<hkern u1="(" u2="j" k="-150" />
+<hkern u1="(" u2="&#x178;" k="-50" />
+<hkern u1="*" u2="A" k="60" />
+<hkern u1="*" u2="&#xc4;" k="60" />
+<hkern u1="*" u2="&#xc5;" k="60" />
+<hkern u1="*" u2="&#xc6;" k="60" />
+<hkern u1="*" u2="&#xc0;" k="60" />
+<hkern u1="*" u2="&#xc3;" k="60" />
+<hkern u1="*" u2="&#xc2;" k="60" />
+<hkern u1="*" u2="&#xc1;" k="60" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2c;" u2="Y" k="50" />
+<hkern u1="&#x2c;" u2="y" k="30" />
+<hkern u1="&#x2c;" u2="0" k="40" />
+<hkern u1="&#x2c;" u2="1" k="60" />
+<hkern u1="&#x2c;" u2="4" k="50" />
+<hkern u1="&#x2c;" u2="6" k="30" />
+<hkern u1="&#x2c;" u2="8" k="15" />
+<hkern u1="&#x2c;" u2="9" k="10" />
+<hkern u1="&#x2c;" u2="A" k="-30" />
+<hkern u1="&#x2c;" u2="C" k="40" />
+<hkern u1="&#x2c;" u2="G" k="40" />
+<hkern u1="&#x2c;" u2="O" k="40" />
+<hkern u1="&#x2c;" u2="Q" k="40" />
+<hkern u1="&#x2c;" u2="T" k="60" />
+<hkern u1="&#x2c;" u2="U" k="10" />
+<hkern u1="&#x2c;" u2="V" k="60" />
+<hkern u1="&#x2c;" u2="W" k="20" />
+<hkern u1="&#x2c;" u2="a" k="20" />
+<hkern u1="&#x2c;" u2="c" k="20" />
+<hkern u1="&#x2c;" u2="d" k="20" />
+<hkern u1="&#x2c;" u2="e" k="20" />
+<hkern u1="&#x2c;" u2="m" k="20" />
+<hkern u1="&#x2c;" u2="n" k="20" />
+<hkern u1="&#x2c;" u2="o" k="20" />
+<hkern u1="&#x2c;" u2="p" k="20" />
+<hkern u1="&#x2c;" u2="q" k="20" />
+<hkern u1="&#x2c;" u2="r" k="20" />
+<hkern u1="&#x2c;" u2="t" k="40" />
+<hkern u1="&#x2c;" u2="u" k="20" />
+<hkern u1="&#x2c;" u2="v" k="30" />
+<hkern u1="&#x2c;" u2="w" k="10" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2c;" u2="&#x131;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2c;" u2="&#x152;" k="40" />
+<hkern u1="&#x2c;" u2="&#x153;" k="20" />
+<hkern u1="&#x2c;" u2="&#x178;" k="50" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2c;" u2="&#xda;" k="10" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="10" />
+<hkern u1="-" u2="&#xdd;" k="50" />
+<hkern u1="-" u2="Y" k="50" />
+<hkern u1="-" u2="y" k="10" />
+<hkern u1="-" u2="&#x17d;" k="10" />
+<hkern u1="-" u2="Z" k="10" />
+<hkern u1="-" u2="z" k="30" />
+<hkern u1="-" u2="1" k="20" />
+<hkern u1="-" u2="7" k="30" />
+<hkern u1="-" u2="A" k="-10" />
+<hkern u1="-" u2="T" k="70" />
+<hkern u1="-" u2="V" k="50" />
+<hkern u1="-" u2="W" k="40" />
+<hkern u1="-" u2="X" k="30" />
+<hkern u1="-" u2="a" k="10" />
+<hkern u1="-" u2="c" k="10" />
+<hkern u1="-" u2="d" k="10" />
+<hkern u1="-" u2="e" k="10" />
+<hkern u1="-" u2="o" k="10" />
+<hkern u1="-" u2="q" k="10" />
+<hkern u1="-" u2="v" k="10" />
+<hkern u1="-" u2="x" k="30" />
+<hkern u1="-" u2="&#xc4;" k="-10" />
+<hkern u1="-" u2="&#xc5;" k="-10" />
+<hkern u1="-" u2="&#xe7;" k="10" />
+<hkern u1="-" u2="&#xc6;" k="-10" />
+<hkern u1="-" u2="&#xe6;" k="10" />
+<hkern u1="-" u2="&#xc0;" k="-10" />
+<hkern u1="-" u2="&#xc3;" k="-10" />
+<hkern u1="-" u2="&#x153;" k="10" />
+<hkern u1="-" u2="&#x178;" k="50" />
+<hkern u1="-" u2="&#xc2;" k="-10" />
+<hkern u1="-" u2="&#xc1;" k="-10" />
+<hkern u1="." u2="&#xdd;" k="50" />
+<hkern u1="." u2="Y" k="50" />
+<hkern u1="." u2="y" k="30" />
+<hkern u1="." u2="0" k="40" />
+<hkern u1="." u2="1" k="60" />
+<hkern u1="." u2="4" k="50" />
+<hkern u1="." u2="6" k="30" />
+<hkern u1="." u2="8" k="15" />
+<hkern u1="." u2="9" k="10" />
+<hkern u1="." u2="A" k="-30" />
+<hkern u1="." u2="C" k="40" />
+<hkern u1="." u2="G" k="40" />
+<hkern u1="." u2="O" k="40" />
+<hkern u1="." u2="Q" k="40" />
+<hkern u1="." u2="T" k="60" />
+<hkern u1="." u2="U" k="10" />
+<hkern u1="." u2="V" k="60" />
+<hkern u1="." u2="W" k="20" />
+<hkern u1="." u2="a" k="20" />
+<hkern u1="." u2="c" k="20" />
+<hkern u1="." u2="d" k="20" />
+<hkern u1="." u2="e" k="20" />
+<hkern u1="." u2="m" k="20" />
+<hkern u1="." u2="n" k="20" />
+<hkern u1="." u2="o" k="20" />
+<hkern u1="." u2="p" k="20" />
+<hkern u1="." u2="q" k="20" />
+<hkern u1="." u2="r" k="20" />
+<hkern u1="." u2="t" k="40" />
+<hkern u1="." u2="u" k="20" />
+<hkern u1="." u2="v" k="30" />
+<hkern u1="." u2="w" k="10" />
+<hkern u1="." u2="&#xc4;" k="-30" />
+<hkern u1="." u2="&#xc5;" k="-30" />
+<hkern u1="." u2="&#xd6;" k="40" />
+<hkern u1="." u2="&#xdc;" k="10" />
+<hkern u1="." u2="&#xe7;" k="20" />
+<hkern u1="." u2="&#x131;" k="20" />
+<hkern u1="." u2="&#xc6;" k="-30" />
+<hkern u1="." u2="&#xd8;" k="40" />
+<hkern u1="." u2="&#xe6;" k="20" />
+<hkern u1="." u2="&#xc0;" k="-30" />
+<hkern u1="." u2="&#xc3;" k="-30" />
+<hkern u1="." u2="&#xd5;" k="40" />
+<hkern u1="." u2="&#x152;" k="40" />
+<hkern u1="." u2="&#x153;" k="20" />
+<hkern u1="." u2="&#x178;" k="50" />
+<hkern u1="." u2="&#xc2;" k="-30" />
+<hkern u1="." u2="&#xc1;" k="-30" />
+<hkern u1="." u2="&#xd3;" k="40" />
+<hkern u1="." u2="&#xd4;" k="40" />
+<hkern u1="." u2="&#xd2;" k="40" />
+<hkern u1="." u2="&#xda;" k="10" />
+<hkern u1="." u2="&#xdb;" k="10" />
+<hkern u1="." u2="&#xd9;" k="10" />
+<hkern u1="/" u2="1" k="-20" />
+<hkern u1="/" u2="4" k="20" />
+<hkern u1="/" u2="C" k="-10" />
+<hkern u1="/" u2="G" k="-10" />
+<hkern u1="/" u2="O" k="-10" />
+<hkern u1="/" u2="Q" k="-10" />
+<hkern u1="/" u2="a" k="10" />
+<hkern u1="/" u2="c" k="10" />
+<hkern u1="/" u2="d" k="10" />
+<hkern u1="/" u2="e" k="10" />
+<hkern u1="/" u2="g" k="10" />
+<hkern u1="/" u2="o" k="10" />
+<hkern u1="/" u2="q" k="10" />
+<hkern u1="/" u2="&#xd6;" k="-10" />
+<hkern u1="/" u2="&#xe7;" k="10" />
+<hkern u1="/" u2="&#xd8;" k="-10" />
+<hkern u1="/" u2="&#xe6;" k="10" />
+<hkern u1="/" u2="&#xd5;" k="-10" />
+<hkern u1="/" u2="&#x152;" k="-10" />
+<hkern u1="/" u2="&#x153;" k="10" />
+<hkern u1="/" u2="&#xd3;" k="-10" />
+<hkern u1="/" u2="&#xd4;" k="-10" />
+<hkern u1="/" u2="&#xd2;" k="-10" />
+<hkern u1="0" u2="&#x2c;" k="40" />
+<hkern u1="0" u2="." k="40" />
+<hkern u1="0" u2="7" k="20" />
+<hkern u1="0" u2="&#x2026;" k="40" />
+<hkern u1="0" u2="&#x201a;" k="40" />
+<hkern u1="0" u2="&#x201e;" k="40" />
+<hkern u1="2" u2="-" k="10" />
+<hkern u1="2" u2="4" k="30" />
+<hkern u1="2" u2="&#x2013;" k="10" />
+<hkern u1="2" u2="&#x2014;" k="10" />
+<hkern u1="3" u2="&#x2c;" k="20" />
+<hkern u1="3" u2="." k="20" />
+<hkern u1="3" u2="7" k="20" />
+<hkern u1="3" u2="&#x2026;" k="20" />
+<hkern u1="3" u2="&#x201a;" k="20" />
+<hkern u1="3" u2="&#x201e;" k="20" />
+<hkern u1="4" u2="7" k="10" />
+<hkern u1="4" u2="&#xb0;" k="40" />
+<hkern u1="4" u2="&#x2122;" k="50" />
+<hkern u1="5" u2="&#x2c;" k="10" />
+<hkern u1="5" u2="." k="10" />
+<hkern u1="5" u2="7" k="10" />
+<hkern u1="5" u2="&#x2026;" k="10" />
+<hkern u1="5" u2="&#x201a;" k="10" />
+<hkern u1="5" u2="&#x201e;" k="10" />
+<hkern u1="6" u2="&#x2c;" k="10" />
+<hkern u1="6" u2="." k="10" />
+<hkern u1="6" u2="7" k="10" />
+<hkern u1="6" u2="&#x2026;" k="10" />
+<hkern u1="6" u2="&#x201a;" k="10" />
+<hkern u1="6" u2="&#x201e;" k="10" />
+<hkern u1="7" u2="&#x2c;" k="80" />
+<hkern u1="7" u2="-" k="40" />
+<hkern u1="7" u2="." k="80" />
+<hkern u1="7" u2="0" k="10" />
+<hkern u1="7" u2="1" k="-20" />
+<hkern u1="7" u2="3" k="-20" />
+<hkern u1="7" u2="4" k="60" />
+<hkern u1="7" u2="5" k="-15" />
+<hkern u1="7" u2="9" k="-10" />
+<hkern u1="7" u2=":" k="20" />
+<hkern u1="7" u2=";" k="20" />
+<hkern u1="7" u2="&#xa2;" k="40" />
+<hkern u1="7" u2="&#x2026;" k="80" />
+<hkern u1="7" u2="&#x2013;" k="40" />
+<hkern u1="7" u2="&#x2014;" k="40" />
+<hkern u1="7" u2="&#x201a;" k="80" />
+<hkern u1="7" u2="&#x201e;" k="80" />
+<hkern u1="8" u2="&#x2c;" k="15" />
+<hkern u1="8" u2="." k="15" />
+<hkern u1="8" u2="7" k="15" />
+<hkern u1="8" u2="&#x2026;" k="15" />
+<hkern u1="8" u2="&#x201a;" k="15" />
+<hkern u1="8" u2="&#x201e;" k="15" />
+<hkern u1="9" u2="&#x2c;" k="30" />
+<hkern u1="9" u2="." k="30" />
+<hkern u1="9" u2="7" k="45" />
+<hkern u1="9" u2="&#x2026;" k="30" />
+<hkern u1="9" u2="&#x201a;" k="30" />
+<hkern u1="9" u2="&#x201e;" k="30" />
+<hkern u1=":" u2="&#xdd;" k="30" />
+<hkern u1=":" u2="Y" k="30" />
+<hkern u1=":" u2="1" k="30" />
+<hkern u1=":" u2="7" k="20" />
+<hkern u1=":" u2="T" k="50" />
+<hkern u1=":" u2="V" k="20" />
+<hkern u1=":" u2="W" k="20" />
+<hkern u1=":" u2="a" k="10" />
+<hkern u1=":" u2="c" k="10" />
+<hkern u1=":" u2="d" k="10" />
+<hkern u1=":" u2="e" k="10" />
+<hkern u1=":" u2="o" k="10" />
+<hkern u1=":" u2="q" k="10" />
+<hkern u1=":" u2="&#xe7;" k="10" />
+<hkern u1=":" u2="&#xe6;" k="10" />
+<hkern u1=":" u2="&#x153;" k="10" />
+<hkern u1=":" u2="&#x178;" k="30" />
+<hkern u1=";" u2="&#xdd;" k="30" />
+<hkern u1=";" u2="Y" k="30" />
+<hkern u1=";" u2="1" k="30" />
+<hkern u1=";" u2="7" k="20" />
+<hkern u1=";" u2="T" k="50" />
+<hkern u1=";" u2="V" k="20" />
+<hkern u1=";" u2="W" k="20" />
+<hkern u1=";" u2="a" k="10" />
+<hkern u1=";" u2="c" k="10" />
+<hkern u1=";" u2="d" k="10" />
+<hkern u1=";" u2="e" k="10" />
+<hkern u1=";" u2="o" k="10" />
+<hkern u1=";" u2="q" k="10" />
+<hkern u1=";" u2="&#xe7;" k="10" />
+<hkern u1=";" u2="&#xe6;" k="10" />
+<hkern u1=";" u2="&#x153;" k="10" />
+<hkern u1=";" u2="&#x178;" k="30" />
+<hkern u1="&gt;" u2="7" k="60" />
+<hkern u1="@" u2="&#xdd;" k="30" />
+<hkern u1="@" u2="Y" k="30" />
+<hkern u1="@" u2="A" k="10" />
+<hkern u1="@" u2="T" k="30" />
+<hkern u1="@" u2="W" k="20" />
+<hkern u1="@" u2="&#xc4;" k="10" />
+<hkern u1="@" u2="&#xc5;" k="10" />
+<hkern u1="@" u2="&#xc6;" k="10" />
+<hkern u1="@" u2="&#xc0;" k="10" />
+<hkern u1="@" u2="&#xc3;" k="10" />
+<hkern u1="@" u2="&#x178;" k="30" />
+<hkern u1="@" u2="&#xc2;" k="10" />
+<hkern u1="@" u2="&#xc1;" k="10" />
+<hkern u1="A" u2="Y" k="51" />
+<hkern u1="A" u2="T" k="116" />
+<hkern u1="B" u2="V" k="5" />
+<hkern u1="B" u2="W" k="20" />
+<hkern u1="C" u2="&#xdd;" k="-20" />
+<hkern u1="C" u2="Y" k="-20" />
+<hkern u1="C" u2="y" k="5" />
+<hkern u1="C" u2="z" k="10" />
+<hkern u1="C" u2="V" k="-20" />
+<hkern u1="C" u2="X" k="-10" />
+<hkern u1="C" u2="a" k="-5" />
+<hkern u1="C" u2="c" k="-5" />
+<hkern u1="C" u2="d" k="-5" />
+<hkern u1="C" u2="e" k="-5" />
+<hkern u1="C" u2="o" k="-5" />
+<hkern u1="C" u2="q" k="-5" />
+<hkern u1="C" u2="t" k="5" />
+<hkern u1="C" u2="v" k="5" />
+<hkern u1="C" u2="w" k="5" />
+<hkern u1="C" u2="&#xe7;" k="-5" />
+<hkern u1="C" u2="&#xe6;" k="-5" />
+<hkern u1="C" u2="&#x153;" k="-5" />
+<hkern u1="C" u2="&#x178;" k="-20" />
+<hkern u1="D" u2="&#xdd;" k="25" />
+<hkern u1="D" u2="Y" k="25" />
+<hkern u1="D" u2="&#x17d;" k="45" />
+<hkern u1="D" u2="Z" k="45" />
+<hkern u1="D" u2="z" k="10" />
+<hkern u1="D" u2="&#x2c;" k="40" />
+<hkern u1="D" u2="." k="40" />
+<hkern u1="D" u2="/" k="50" />
+<hkern u1="D" u2="?" k="20" />
+<hkern u1="D" u2="A" k="20" />
+<hkern u1="D" u2="J" k="45" />
+<hkern u1="D" u2="T" k="30" />
+<hkern u1="D" u2="V" k="20" />
+<hkern u1="D" u2="W" k="40" />
+<hkern u1="D" u2="X" k="25" />
+<hkern u1="D" u2="a" k="5" />
+<hkern u1="D" u2="b" k="10" />
+<hkern u1="D" u2="c" k="5" />
+<hkern u1="D" u2="d" k="5" />
+<hkern u1="D" u2="e" k="5" />
+<hkern u1="D" u2="h" k="10" />
+<hkern u1="D" u2="k" k="10" />
+<hkern u1="D" u2="l" k="10" />
+<hkern u1="D" u2="m" k="5" />
+<hkern u1="D" u2="n" k="5" />
+<hkern u1="D" u2="o" k="5" />
+<hkern u1="D" u2="p" k="5" />
+<hkern u1="D" u2="q" k="5" />
+<hkern u1="D" u2="r" k="5" />
+<hkern u1="D" u2="u" k="5" />
+<hkern u1="D" u2="x" k="10" />
+<hkern u1="D" u2="&#xc4;" k="20" />
+<hkern u1="D" u2="&#xc5;" k="20" />
+<hkern u1="D" u2="&#xe7;" k="5" />
+<hkern u1="D" u2="&#x131;" k="5" />
+<hkern u1="D" u2="&#xc6;" k="20" />
+<hkern u1="D" u2="&#xe6;" k="5" />
+<hkern u1="D" u2="&#x2026;" k="40" />
+<hkern u1="D" u2="&#xc0;" k="20" />
+<hkern u1="D" u2="&#xc3;" k="20" />
+<hkern u1="D" u2="&#x153;" k="5" />
+<hkern u1="D" u2="&#x201c;" k="20" />
+<hkern u1="D" u2="&#x2018;" k="20" />
+<hkern u1="D" u2="&#x178;" k="25" />
+<hkern u1="D" u2="&#x201a;" k="40" />
+<hkern u1="D" u2="&#x201e;" k="40" />
+<hkern u1="D" u2="&#xc2;" k="20" />
+<hkern u1="D" u2="&#xc1;" k="20" />
+<hkern u1="E" u2="y" k="20" />
+<hkern u1="E" u2="@" k="20" />
+<hkern u1="E" u2="T" k="-15" />
+<hkern u1="E" u2="a" k="25" />
+<hkern u1="E" u2="c" k="25" />
+<hkern u1="E" u2="d" k="25" />
+<hkern u1="E" u2="e" k="25" />
+<hkern u1="E" u2="f" k="10" />
+<hkern u1="E" u2="g" k="5" />
+<hkern u1="E" u2="o" k="25" />
+<hkern u1="E" u2="q" k="25" />
+<hkern u1="E" u2="v" k="20" />
+<hkern u1="E" u2="&#xe7;" k="25" />
+<hkern u1="E" u2="&#xae;" k="20" />
+<hkern u1="E" u2="&#xa9;" k="20" />
+<hkern u1="E" u2="&#x3c0;" k="20" />
+<hkern u1="E" u2="&#xe6;" k="25" />
+<hkern u1="E" u2="&#x153;" k="25" />
+<hkern u1="F" u2="&#x161;" k="40" />
+<hkern u1="F" u2="s" k="40" />
+<hkern u1="F" u2="&#xdd;" k="-25" />
+<hkern u1="F" u2="Y" k="-25" />
+<hkern u1="F" u2="y" k="20" />
+<hkern u1="F" u2="z" k="40" />
+<hkern u1="F" u2="&amp;" k="40" />
+<hkern u1="F" u2="&#x2c;" k="90" />
+<hkern u1="F" u2="-" k="50" />
+<hkern u1="F" u2="." k="90" />
+<hkern u1="F" u2="/" k="80" />
+<hkern u1="F" u2=":" k="30" />
+<hkern u1="F" u2=";" k="30" />
+<hkern u1="F" u2="@" k="20" />
+<hkern u1="F" u2="A" k="50" />
+<hkern u1="F" u2="J" k="90" />
+<hkern u1="F" u2="T" k="-30" />
+<hkern u1="F" u2="V" k="-20" />
+<hkern u1="F" u2="W" k="-5" />
+<hkern u1="F" u2="X" k="-10" />
+<hkern u1="F" u2="a" k="50" />
+<hkern u1="F" u2="b" k="10" />
+<hkern u1="F" u2="c" k="50" />
+<hkern u1="F" u2="d" k="50" />
+<hkern u1="F" u2="e" k="50" />
+<hkern u1="F" u2="f" k="20" />
+<hkern u1="F" u2="g" k="30" />
+<hkern u1="F" u2="h" k="10" />
+<hkern u1="F" u2="i" k="10" />
+<hkern u1="F" u2="j" k="10" />
+<hkern u1="F" u2="k" k="10" />
+<hkern u1="F" u2="l" k="10" />
+<hkern u1="F" u2="m" k="40" />
+<hkern u1="F" u2="n" k="40" />
+<hkern u1="F" u2="o" k="50" />
+<hkern u1="F" u2="p" k="40" />
+<hkern u1="F" u2="q" k="50" />
+<hkern u1="F" u2="r" k="40" />
+<hkern u1="F" u2="t" k="20" />
+<hkern u1="F" u2="u" k="40" />
+<hkern u1="F" u2="v" k="20" />
+<hkern u1="F" u2="w" k="20" />
+<hkern u1="F" u2="x" k="40" />
+<hkern u1="F" u2="&#xc4;" k="50" />
+<hkern u1="F" u2="&#xc5;" k="50" />
+<hkern u1="F" u2="&#xe7;" k="50" />
+<hkern u1="F" u2="&#x131;" k="40" />
+<hkern u1="F" u2="&#xae;" k="20" />
+<hkern u1="F" u2="&#xa9;" k="20" />
+<hkern u1="F" u2="&#xc6;" k="50" />
+<hkern u1="F" u2="&#x3c0;" k="20" />
+<hkern u1="F" u2="&#xe6;" k="50" />
+<hkern u1="F" u2="&#x2026;" k="90" />
+<hkern u1="F" u2="&#xc0;" k="50" />
+<hkern u1="F" u2="&#xc3;" k="50" />
+<hkern u1="F" u2="&#x153;" k="50" />
+<hkern u1="F" u2="&#x2013;" k="50" />
+<hkern u1="F" u2="&#x2014;" k="50" />
+<hkern u1="F" u2="&#x178;" k="-25" />
+<hkern u1="F" u2="&#x201a;" k="90" />
+<hkern u1="F" u2="&#x201e;" k="90" />
+<hkern u1="F" u2="&#xc2;" k="50" />
+<hkern u1="F" u2="&#xc1;" k="50" />
+<hkern u1="G" u2="&#xdd;" k="15" />
+<hkern u1="G" u2="Y" k="15" />
+<hkern u1="G" u2="y" k="10" />
+<hkern u1="G" u2="&#x2c;" k="-20" />
+<hkern u1="G" u2="." k="-20" />
+<hkern u1="G" u2="T" k="25" />
+<hkern u1="G" u2="V" k="10" />
+<hkern u1="G" u2="W" k="15" />
+<hkern u1="G" u2="t" k="10" />
+<hkern u1="G" u2="v" k="10" />
+<hkern u1="G" u2="&#x2122;" k="30" />
+<hkern u1="G" u2="&#x2026;" k="-20" />
+<hkern u1="G" u2="&#x178;" k="15" />
+<hkern u1="G" u2="&#x201a;" k="-20" />
+<hkern u1="G" u2="&#x201e;" k="-20" />
+<hkern u1="H" u2="y" k="10" />
+<hkern u1="H" u2="/" k="20" />
+<hkern u1="H" u2="v" k="10" />
+<hkern u1="I" u2="y" k="10" />
+<hkern u1="I" u2="/" k="20" />
+<hkern u1="I" u2="v" k="10" />
+<hkern u1="J" u2="&#x2c;" k="10" />
+<hkern u1="J" u2="." k="10" />
+<hkern u1="J" u2="A" k="5" />
+<hkern u1="J" u2="J" k="10" />
+<hkern u1="J" u2="&#x2026;" k="10" />
+<hkern u1="J" u2="&#x201a;" k="10" />
+<hkern u1="J" u2="&#x201e;" k="10" />
+<hkern u1="K" u2="&#xf0;" k="20" />
+<hkern u1="K" u2="&#x160;" k="5" />
+<hkern u1="K" u2="S" k="5" />
+<hkern u1="K" u2="&#xdd;" k="-15" />
+<hkern u1="K" u2="Y" k="-15" />
+<hkern u1="K" u2="y" k="20" />
+<hkern u1="K" u2="&#x17d;" k="5" />
+<hkern u1="K" u2="Z" k="5" />
+<hkern u1="K" u2="&amp;" k="20" />
+<hkern u1="K" u2="-" k="40" />
+<hkern u1="K" u2="@" k="30" />
+<hkern u1="K" u2="C" k="30" />
+<hkern u1="K" u2="G" k="30" />
+<hkern u1="K" u2="O" k="30" />
+<hkern u1="K" u2="Q" k="30" />
+<hkern u1="K" u2="T" k="-5" />
+<hkern u1="K" u2="U" k="10" />
+<hkern u1="K" u2="V" k="-15" />
+<hkern u1="K" u2="W" k="10" />
+<hkern u1="K" u2="X" k="-10" />
+<hkern u1="K" u2="a" k="15" />
+<hkern u1="K" u2="c" k="20" />
+<hkern u1="K" u2="d" k="20" />
+<hkern u1="K" u2="e" k="20" />
+<hkern u1="K" u2="f" k="5" />
+<hkern u1="K" u2="g" k="10" />
+<hkern u1="K" u2="o" k="20" />
+<hkern u1="K" u2="q" k="20" />
+<hkern u1="K" u2="t" k="10" />
+<hkern u1="K" u2="v" k="20" />
+<hkern u1="K" u2="&#xd6;" k="30" />
+<hkern u1="K" u2="&#xdc;" k="10" />
+<hkern u1="K" u2="&#xe7;" k="20" />
+<hkern u1="K" u2="&#xae;" k="30" />
+<hkern u1="K" u2="&#xa9;" k="30" />
+<hkern u1="K" u2="&#xd8;" k="30" />
+<hkern u1="K" u2="&#x3c0;" k="30" />
+<hkern u1="K" u2="&#xe6;" k="20" />
+<hkern u1="K" u2="&#xd5;" k="30" />
+<hkern u1="K" u2="&#x152;" k="30" />
+<hkern u1="K" u2="&#x153;" k="20" />
+<hkern u1="K" u2="&#x2013;" k="40" />
+<hkern u1="K" u2="&#x2014;" k="40" />
+<hkern u1="K" u2="&#x178;" k="-15" />
+<hkern u1="K" u2="&#xd3;" k="30" />
+<hkern u1="K" u2="&#xd4;" k="30" />
+<hkern u1="K" u2="&#xd2;" k="30" />
+<hkern u1="K" u2="&#xda;" k="10" />
+<hkern u1="K" u2="&#xdb;" k="10" />
+<hkern u1="K" u2="&#xd9;" k="10" />
+<hkern u1="L" u2="&#xdd;" k="80" />
+<hkern u1="L" u2="Y" k="80" />
+<hkern u1="L" u2="y" k="40" />
+<hkern u1="L" u2="@" k="50" />
+<hkern u1="L" u2="A" k="-20" />
+<hkern u1="L" u2="C" k="50" />
+<hkern u1="L" u2="G" k="50" />
+<hkern u1="L" u2="O" k="50" />
+<hkern u1="L" u2="Q" k="50" />
+<hkern u1="L" u2="T" k="100" />
+<hkern u1="L" u2="U" k="15" />
+<hkern u1="L" u2="V" k="90" />
+<hkern u1="L" u2="W" k="70" />
+<hkern u1="L" u2="\" k="50" />
+<hkern u1="L" u2="a" k="20" />
+<hkern u1="L" u2="c" k="20" />
+<hkern u1="L" u2="d" k="20" />
+<hkern u1="L" u2="e" k="20" />
+<hkern u1="L" u2="o" k="20" />
+<hkern u1="L" u2="q" k="20" />
+<hkern u1="L" u2="v" k="40" />
+<hkern u1="L" u2="w" k="10" />
+<hkern u1="L" u2="&#xc4;" k="-20" />
+<hkern u1="L" u2="&#xc5;" k="-20" />
+<hkern u1="L" u2="&#xd6;" k="50" />
+<hkern u1="L" u2="&#xdc;" k="15" />
+<hkern u1="L" u2="&#xe7;" k="20" />
+<hkern u1="L" u2="&#xae;" k="50" />
+<hkern u1="L" u2="&#xa9;" k="50" />
+<hkern u1="L" u2="&#x2122;" k="70" />
+<hkern u1="L" u2="&#xc6;" k="-20" />
+<hkern u1="L" u2="&#xd8;" k="50" />
+<hkern u1="L" u2="&#x3c0;" k="50" />
+<hkern u1="L" u2="&#xe6;" k="20" />
+<hkern u1="L" u2="&#xc0;" k="-20" />
+<hkern u1="L" u2="&#xc3;" k="-20" />
+<hkern u1="L" u2="&#xd5;" k="50" />
+<hkern u1="L" u2="&#x152;" k="50" />
+<hkern u1="L" u2="&#x153;" k="20" />
+<hkern u1="L" u2="&#x201c;" k="100" />
+<hkern u1="L" u2="&#x201d;" k="60" />
+<hkern u1="L" u2="&#x2018;" k="100" />
+<hkern u1="L" u2="&#x2019;" k="60" />
+<hkern u1="L" u2="&#x178;" k="80" />
+<hkern u1="L" u2="&#xc2;" k="-20" />
+<hkern u1="L" u2="&#xc1;" k="-20" />
+<hkern u1="L" u2="&#xd3;" k="50" />
+<hkern u1="L" u2="&#xd4;" k="50" />
+<hkern u1="L" u2="&#xd2;" k="50" />
+<hkern u1="L" u2="&#xda;" k="15" />
+<hkern u1="L" u2="&#xdb;" k="15" />
+<hkern u1="L" u2="&#xd9;" k="15" />
+<hkern u1="M" u2="y" k="10" />
+<hkern u1="M" u2="/" k="20" />
+<hkern u1="M" u2="v" k="10" />
+<hkern u1="N" u2="y" k="10" />
+<hkern u1="N" u2="/" k="20" />
+<hkern u1="N" u2="v" k="10" />
+<hkern u1="O" u2="&#xdd;" k="25" />
+<hkern u1="O" u2="Y" k="25" />
+<hkern u1="O" u2="&#x17d;" k="45" />
+<hkern u1="O" u2="Z" k="45" />
+<hkern u1="O" u2="z" k="10" />
+<hkern u1="O" u2="&#x2c;" k="40" />
+<hkern u1="O" u2="." k="40" />
+<hkern u1="O" u2="/" k="50" />
+<hkern u1="O" u2="?" k="20" />
+<hkern u1="O" u2="A" k="20" />
+<hkern u1="O" u2="J" k="45" />
+<hkern u1="O" u2="T" k="30" />
+<hkern u1="O" u2="V" k="20" />
+<hkern u1="O" u2="W" k="40" />
+<hkern u1="O" u2="X" k="25" />
+<hkern u1="O" u2="a" k="5" />
+<hkern u1="O" u2="b" k="10" />
+<hkern u1="O" u2="c" k="5" />
+<hkern u1="O" u2="d" k="5" />
+<hkern u1="O" u2="e" k="5" />
+<hkern u1="O" u2="h" k="10" />
+<hkern u1="O" u2="k" k="10" />
+<hkern u1="O" u2="l" k="10" />
+<hkern u1="O" u2="m" k="5" />
+<hkern u1="O" u2="n" k="5" />
+<hkern u1="O" u2="o" k="5" />
+<hkern u1="O" u2="p" k="5" />
+<hkern u1="O" u2="q" k="5" />
+<hkern u1="O" u2="r" k="5" />
+<hkern u1="O" u2="u" k="5" />
+<hkern u1="O" u2="x" k="10" />
+<hkern u1="O" u2="&#xc4;" k="20" />
+<hkern u1="O" u2="&#xc5;" k="20" />
+<hkern u1="O" u2="&#xe7;" k="5" />
+<hkern u1="O" u2="&#x131;" k="5" />
+<hkern u1="O" u2="&#xc6;" k="20" />
+<hkern u1="O" u2="&#xe6;" k="5" />
+<hkern u1="O" u2="&#x2026;" k="40" />
+<hkern u1="O" u2="&#xc0;" k="20" />
+<hkern u1="O" u2="&#xc3;" k="20" />
+<hkern u1="O" u2="&#x153;" k="5" />
+<hkern u1="O" u2="&#x201c;" k="20" />
+<hkern u1="O" u2="&#x2018;" k="20" />
+<hkern u1="O" u2="&#x178;" k="25" />
+<hkern u1="O" u2="&#x201a;" k="40" />
+<hkern u1="O" u2="&#x201e;" k="40" />
+<hkern u1="O" u2="&#xc2;" k="20" />
+<hkern u1="O" u2="&#xc1;" k="20" />
+<hkern u1="P" u2="&#xdd;" k="-15" />
+<hkern u1="P" u2="Y" k="-15" />
+<hkern u1="P" u2="y" k="-15" />
+<hkern u1="P" u2="&#x17d;" k="25" />
+<hkern u1="P" u2="Z" k="25" />
+<hkern u1="P" u2="&amp;" k="20" />
+<hkern u1="P" u2="&#x2c;" k="60" />
+<hkern u1="P" u2="." k="60" />
+<hkern u1="P" u2="A" k="36" />
+<hkern u1="P" u2="J" k="85" />
+<hkern u1="P" u2="V" k="-25" />
+<hkern u1="P" u2="W" k="-5" />
+<hkern u1="P" u2="X" k="-5" />
+<hkern u1="P" u2="a" k="10" />
+<hkern u1="P" u2="c" k="10" />
+<hkern u1="P" u2="d" k="10" />
+<hkern u1="P" u2="e" k="10" />
+<hkern u1="P" u2="f" k="-10" />
+<hkern u1="P" u2="o" k="10" />
+<hkern u1="P" u2="q" k="10" />
+<hkern u1="P" u2="t" k="-10" />
+<hkern u1="P" u2="v" k="-15" />
+<hkern u1="P" u2="w" k="-10" />
+<hkern u1="P" u2="x" k="-5" />
+<hkern u1="P" u2="&#xc4;" k="35" />
+<hkern u1="P" u2="&#xc5;" k="35" />
+<hkern u1="P" u2="&#xe7;" k="10" />
+<hkern u1="P" u2="&#xc6;" k="35" />
+<hkern u1="P" u2="&#xe6;" k="10" />
+<hkern u1="P" u2="&#x2026;" k="60" />
+<hkern u1="P" u2="&#xc0;" k="35" />
+<hkern u1="P" u2="&#xc3;" k="35" />
+<hkern u1="P" u2="&#x153;" k="10" />
+<hkern u1="P" u2="&#x178;" k="-15" />
+<hkern u1="P" u2="&#x201a;" k="60" />
+<hkern u1="P" u2="&#x201e;" k="60" />
+<hkern u1="P" u2="&#xc2;" k="35" />
+<hkern u1="P" u2="&#xc1;" k="35" />
+<hkern u1="Q" u2="&#xdd;" k="25" />
+<hkern u1="Q" u2="Y" k="25" />
+<hkern u1="Q" u2="&#x17d;" k="45" />
+<hkern u1="Q" u2="Z" k="45" />
+<hkern u1="Q" u2="z" k="10" />
+<hkern u1="Q" u2="&#x2c;" k="40" />
+<hkern u1="Q" u2="/" k="30" />
+<hkern u1="Q" u2="?" k="20" />
+<hkern u1="Q" u2="A" k="20" />
+<hkern u1="Q" u2="J" k="45" />
+<hkern u1="Q" u2="T" k="30" />
+<hkern u1="Q" u2="V" k="20" />
+<hkern u1="Q" u2="W" k="40" />
+<hkern u1="Q" u2="X" k="25" />
+<hkern u1="Q" u2="a" k="5" />
+<hkern u1="Q" u2="b" k="10" />
+<hkern u1="Q" u2="c" k="5" />
+<hkern u1="Q" u2="d" k="5" />
+<hkern u1="Q" u2="e" k="5" />
+<hkern u1="Q" u2="h" k="10" />
+<hkern u1="Q" u2="k" k="10" />
+<hkern u1="Q" u2="l" k="10" />
+<hkern u1="Q" u2="m" k="5" />
+<hkern u1="Q" u2="n" k="5" />
+<hkern u1="Q" u2="o" k="5" />
+<hkern u1="Q" u2="p" k="5" />
+<hkern u1="Q" u2="q" k="5" />
+<hkern u1="Q" u2="r" k="5" />
+<hkern u1="Q" u2="u" k="5" />
+<hkern u1="Q" u2="x" k="10" />
+<hkern u1="Q" u2="&#xc4;" k="20" />
+<hkern u1="Q" u2="&#xc5;" k="20" />
+<hkern u1="Q" u2="&#xe7;" k="5" />
+<hkern u1="Q" u2="&#x131;" k="5" />
+<hkern u1="Q" u2="&#xc6;" k="20" />
+<hkern u1="Q" u2="&#xe6;" k="5" />
+<hkern u1="Q" u2="&#x2026;" k="40" />
+<hkern u1="Q" u2="&#xc0;" k="20" />
+<hkern u1="Q" u2="&#xc3;" k="20" />
+<hkern u1="Q" u2="&#x153;" k="5" />
+<hkern u1="Q" u2="&#x201c;" k="20" />
+<hkern u1="Q" u2="&#x2018;" k="20" />
+<hkern u1="Q" u2="&#x178;" k="25" />
+<hkern u1="Q" u2="&#x201a;" k="40" />
+<hkern u1="Q" u2="&#x201e;" k="40" />
+<hkern u1="Q" u2="&#xc2;" k="20" />
+<hkern u1="Q" u2="&#xc1;" k="20" />
+<hkern u1="R" u2="&#xdd;" k="-15" />
+<hkern u1="R" u2="Y" k="-15" />
+<hkern u1="R" u2="&amp;" k="-10" />
+<hkern u1="R" u2="J" k="10" />
+<hkern u1="R" u2="T" k="10" />
+<hkern u1="R" u2="V" k="-20" />
+<hkern u1="R" u2="X" k="-10" />
+<hkern u1="R" u2="&#x178;" k="-15" />
+<hkern u1="T" u2="&#x161;" k="100" />
+<hkern u1="T" u2="s" k="100" />
+<hkern u1="T" u2="&#xdd;" k="-20" />
+<hkern u1="T" u2="Y" k="-20" />
+<hkern u1="T" u2="y" k="70" />
+<hkern u1="T" u2="&#x17d;" k="10" />
+<hkern u1="T" u2="Z" k="10" />
+<hkern u1="T" u2="z" k="80" />
+<hkern u1="T" u2="&amp;" k="70" />
+<hkern u1="T" u2="&#x2c;" k="60" />
+<hkern u1="T" u2="-" k="70" />
+<hkern u1="T" u2="." k="60" />
+<hkern u1="T" u2=":" k="50" />
+<hkern u1="T" u2=";" k="50" />
+<hkern u1="T" u2="@" k="30" />
+<hkern u1="T" u2="A" k="66" />
+<hkern u1="T" u2="C" k="30" />
+<hkern u1="T" u2="G" k="30" />
+<hkern u1="T" u2="J" k="85" />
+<hkern u1="T" u2="O" k="30" />
+<hkern u1="T" u2="Q" k="30" />
+<hkern u1="T" u2="T" k="-10" />
+<hkern u1="T" u2="V" k="-20" />
+<hkern u1="T" u2="X" k="-10" />
+<hkern u1="T" u2="\" k="-10" />
+<hkern u1="T" u2="a" k="95" />
+<hkern u1="T" u2="c" k="95" />
+<hkern u1="T" u2="d" k="95" />
+<hkern u1="T" u2="e" k="95" />
+<hkern u1="T" u2="f" k="10" />
+<hkern u1="T" u2="g" k="95" />
+<hkern u1="T" u2="m" k="80" />
+<hkern u1="T" u2="n" k="80" />
+<hkern u1="T" u2="o" k="95" />
+<hkern u1="T" u2="p" k="80" />
+<hkern u1="T" u2="q" k="95" />
+<hkern u1="T" u2="r" k="80" />
+<hkern u1="T" u2="t" k="20" />
+<hkern u1="T" u2="u" k="80" />
+<hkern u1="T" u2="v" k="70" />
+<hkern u1="T" u2="w" k="60" />
+<hkern u1="T" u2="x" k="80" />
+<hkern u1="T" u2="&#xc4;" k="70" />
+<hkern u1="T" u2="&#xc5;" k="70" />
+<hkern u1="T" u2="&#xd6;" k="30" />
+<hkern u1="T" u2="&#xe7;" k="95" />
+<hkern u1="T" u2="&#x131;" k="80" />
+<hkern u1="T" u2="&#xae;" k="30" />
+<hkern u1="T" u2="&#xa9;" k="30" />
+<hkern u1="T" u2="&#xc6;" k="70" />
+<hkern u1="T" u2="&#xd8;" k="30" />
+<hkern u1="T" u2="&#x3c0;" k="30" />
+<hkern u1="T" u2="&#xe6;" k="95" />
+<hkern u1="T" u2="&#xbf;" k="50" />
+<hkern u1="T" u2="&#xab;" k="110" />
+<hkern u1="T" u2="&#xbb;" k="40" />
+<hkern u1="T" u2="&#x2026;" k="60" />
+<hkern u1="T" u2="&#xc0;" k="70" />
+<hkern u1="T" u2="&#xc3;" k="70" />
+<hkern u1="T" u2="&#xd5;" k="30" />
+<hkern u1="T" u2="&#x152;" k="30" />
+<hkern u1="T" u2="&#x153;" k="95" />
+<hkern u1="T" u2="&#x2013;" k="70" />
+<hkern u1="T" u2="&#x2014;" k="70" />
+<hkern u1="T" u2="&#x178;" k="-20" />
+<hkern u1="T" u2="&#x2039;" k="110" />
+<hkern u1="T" u2="&#x203a;" k="40" />
+<hkern u1="T" u2="&#x201a;" k="60" />
+<hkern u1="T" u2="&#x201e;" k="60" />
+<hkern u1="T" u2="&#xc2;" k="70" />
+<hkern u1="T" u2="&#xc1;" k="70" />
+<hkern u1="T" u2="&#xd3;" k="30" />
+<hkern u1="T" u2="&#xd4;" k="30" />
+<hkern u1="T" u2="&#xd2;" k="30" />
+<hkern u1="U" u2="&#x2c;" k="10" />
+<hkern u1="U" u2="." k="10" />
+<hkern u1="U" u2="A" k="5" />
+<hkern u1="U" u2="J" k="10" />
+<hkern u1="U" u2="&#x2026;" k="10" />
+<hkern u1="U" u2="&#x201a;" k="10" />
+<hkern u1="U" u2="&#x201e;" k="10" />
+<hkern u1="V" u2="&#x160;" k="-10" />
+<hkern u1="V" u2="S" k="-10" />
+<hkern u1="V" u2="&#x161;" k="40" />
+<hkern u1="V" u2="s" k="40" />
+<hkern u1="V" u2="&#xdd;" k="-30" />
+<hkern u1="V" u2="Y" k="-30" />
+<hkern u1="V" u2="y" k="10" />
+<hkern u1="V" u2="&amp;" k="40" />
+<hkern u1="V" u2=")" k="-30" />
+<hkern u1="V" u2="&#x2c;" k="60" />
+<hkern u1="V" u2="-" k="60" />
+<hkern u1="V" u2="." k="60" />
+<hkern u1="V" u2=":" k="20" />
+<hkern u1="V" u2=";" k="20" />
+<hkern u1="V" u2="A" k="57" />
+<hkern u1="V" u2="C" k="20" />
+<hkern u1="V" u2="G" k="20" />
+<hkern u1="V" u2="J" k="50" />
+<hkern u1="V" u2="O" k="20" />
+<hkern u1="V" u2="Q" k="20" />
+<hkern u1="V" u2="T" k="-20" />
+<hkern u1="V" u2="V" k="-20" />
+<hkern u1="V" u2="W" k="5" />
+<hkern u1="V" u2="X" k="-20" />
+<hkern u1="V" u2="]" k="-30" />
+<hkern u1="V" u2="a" k="40" />
+<hkern u1="V" u2="b" k="10" />
+<hkern u1="V" u2="c" k="40" />
+<hkern u1="V" u2="d" k="40" />
+<hkern u1="V" u2="e" k="40" />
+<hkern u1="V" u2="f" k="10" />
+<hkern u1="V" u2="g" k="35" />
+<hkern u1="V" u2="h" k="10" />
+<hkern u1="V" u2="k" k="10" />
+<hkern u1="V" u2="l" k="10" />
+<hkern u1="V" u2="m" k="30" />
+<hkern u1="V" u2="n" k="30" />
+<hkern u1="V" u2="o" k="40" />
+<hkern u1="V" u2="p" k="30" />
+<hkern u1="V" u2="q" k="40" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="t" k="10" />
+<hkern u1="V" u2="u" k="30" />
+<hkern u1="V" u2="v" k="10" />
+<hkern u1="V" u2="w" k="10" />
+<hkern u1="V" u2="x" k="25" />
+<hkern u1="V" u2="}" k="-30" />
+<hkern u1="V" u2="&#xc4;" k="50" />
+<hkern u1="V" u2="&#xc5;" k="50" />
+<hkern u1="V" u2="&#xd6;" k="20" />
+<hkern u1="V" u2="&#xe7;" k="40" />
+<hkern u1="V" u2="&#x131;" k="30" />
+<hkern u1="V" u2="&#xc6;" k="50" />
+<hkern u1="V" u2="&#xd8;" k="20" />
+<hkern u1="V" u2="&#xe6;" k="40" />
+<hkern u1="V" u2="&#xab;" k="50" />
+<hkern u1="V" u2="&#xbb;" k="20" />
+<hkern u1="V" u2="&#x2026;" k="60" />
+<hkern u1="V" u2="&#xc0;" k="50" />
+<hkern u1="V" u2="&#xc3;" k="50" />
+<hkern u1="V" u2="&#xd5;" k="20" />
+<hkern u1="V" u2="&#x152;" k="20" />
+<hkern u1="V" u2="&#x153;" k="40" />
+<hkern u1="V" u2="&#x2013;" k="60" />
+<hkern u1="V" u2="&#x2014;" k="60" />
+<hkern u1="V" u2="&#x178;" k="-30" />
+<hkern u1="V" u2="&#x2039;" k="50" />
+<hkern u1="V" u2="&#x203a;" k="20" />
+<hkern u1="V" u2="&#x201a;" k="60" />
+<hkern u1="V" u2="&#x201e;" k="60" />
+<hkern u1="V" u2="&#xc2;" k="50" />
+<hkern u1="V" u2="&#xc1;" k="50" />
+<hkern u1="V" u2="&#xd3;" k="20" />
+<hkern u1="V" u2="&#xd4;" k="20" />
+<hkern u1="V" u2="&#xd2;" k="20" />
+<hkern u1="W" u2="&#x161;" k="50" />
+<hkern u1="W" u2="s" k="50" />
+<hkern u1="W" u2="y" k="30" />
+<hkern u1="W" u2="z" k="30" />
+<hkern u1="W" u2="&amp;" k="30" />
+<hkern u1="W" u2=")" k="-10" />
+<hkern u1="W" u2="&#x2c;" k="20" />
+<hkern u1="W" u2="-" k="40" />
+<hkern u1="W" u2="." k="20" />
+<hkern u1="W" u2=":" k="20" />
+<hkern u1="W" u2=";" k="20" />
+<hkern u1="W" u2="@" k="20" />
+<hkern u1="W" u2="A" k="54" />
+<hkern u1="W" u2="C" k="40" />
+<hkern u1="W" u2="G" k="40" />
+<hkern u1="W" u2="J" k="40" />
+<hkern u1="W" u2="O" k="40" />
+<hkern u1="W" u2="Q" k="40" />
+<hkern u1="W" u2="V" k="5" />
+<hkern u1="W" u2="X" k="10" />
+<hkern u1="W" u2="]" k="-10" />
+<hkern u1="W" u2="a" k="45" />
+<hkern u1="W" u2="b" k="10" />
+<hkern u1="W" u2="c" k="45" />
+<hkern u1="W" u2="d" k="45" />
+<hkern u1="W" u2="e" k="45" />
+<hkern u1="W" u2="f" k="15" />
+<hkern u1="W" u2="g" k="50" />
+<hkern u1="W" u2="h" k="10" />
+<hkern u1="W" u2="i" k="20" />
+<hkern u1="W" u2="j" k="20" />
+<hkern u1="W" u2="k" k="10" />
+<hkern u1="W" u2="l" k="10" />
+<hkern u1="W" u2="m" k="35" />
+<hkern u1="W" u2="n" k="35" />
+<hkern u1="W" u2="o" k="45" />
+<hkern u1="W" u2="p" k="35" />
+<hkern u1="W" u2="q" k="45" />
+<hkern u1="W" u2="r" k="35" />
+<hkern u1="W" u2="t" k="30" />
+<hkern u1="W" u2="u" k="35" />
+<hkern u1="W" u2="v" k="30" />
+<hkern u1="W" u2="w" k="30" />
+<hkern u1="W" u2="x" k="30" />
+<hkern u1="W" u2="}" k="-10" />
+<hkern u1="W" u2="&#xc4;" k="50" />
+<hkern u1="W" u2="&#xc5;" k="50" />
+<hkern u1="W" u2="&#xd6;" k="40" />
+<hkern u1="W" u2="&#xe7;" k="45" />
+<hkern u1="W" u2="&#x131;" k="35" />
+<hkern u1="W" u2="&#xae;" k="20" />
+<hkern u1="W" u2="&#xa9;" k="20" />
+<hkern u1="W" u2="&#xc6;" k="50" />
+<hkern u1="W" u2="&#xd8;" k="40" />
+<hkern u1="W" u2="&#x3c0;" k="20" />
+<hkern u1="W" u2="&#xe6;" k="45" />
+<hkern u1="W" u2="&#xab;" k="40" />
+<hkern u1="W" u2="&#xbb;" k="20" />
+<hkern u1="W" u2="&#x2026;" k="20" />
+<hkern u1="W" u2="&#xc0;" k="50" />
+<hkern u1="W" u2="&#xc3;" k="50" />
+<hkern u1="W" u2="&#xd5;" k="40" />
+<hkern u1="W" u2="&#x152;" k="40" />
+<hkern u1="W" u2="&#x153;" k="45" />
+<hkern u1="W" u2="&#x2013;" k="40" />
+<hkern u1="W" u2="&#x2014;" k="40" />
+<hkern u1="W" u2="&#x2039;" k="40" />
+<hkern u1="W" u2="&#x203a;" k="20" />
+<hkern u1="W" u2="&#x201a;" k="20" />
+<hkern u1="W" u2="&#x201e;" k="20" />
+<hkern u1="W" u2="&#xc2;" k="50" />
+<hkern u1="W" u2="&#xc1;" k="50" />
+<hkern u1="W" u2="&#xd3;" k="40" />
+<hkern u1="W" u2="&#xd4;" k="40" />
+<hkern u1="W" u2="&#xd2;" k="40" />
+<hkern u1="X" u2="&#x160;" k="-5" />
+<hkern u1="X" u2="S" k="-5" />
+<hkern u1="X" u2="&#xdd;" k="-20" />
+<hkern u1="X" u2="Y" k="-20" />
+<hkern u1="X" u2="y" k="20" />
+<hkern u1="X" u2="&amp;" k="20" />
+<hkern u1="X" u2=")" k="-40" />
+<hkern u1="X" u2="-" k="30" />
+<hkern u1="X" u2="A" k="-10" />
+<hkern u1="X" u2="C" k="25" />
+<hkern u1="X" u2="G" k="25" />
+<hkern u1="X" u2="O" k="25" />
+<hkern u1="X" u2="Q" k="25" />
+<hkern u1="X" u2="T" k="-10" />
+<hkern u1="X" u2="V" k="-20" />
+<hkern u1="X" u2="W" k="10" />
+<hkern u1="X" u2="X" k="-10" />
+<hkern u1="X" u2="]" k="-40" />
+<hkern u1="X" u2="a" k="10" />
+<hkern u1="X" u2="c" k="10" />
+<hkern u1="X" u2="d" k="10" />
+<hkern u1="X" u2="e" k="10" />
+<hkern u1="X" u2="f" k="10" />
+<hkern u1="X" u2="m" k="10" />
+<hkern u1="X" u2="n" k="10" />
+<hkern u1="X" u2="o" k="10" />
+<hkern u1="X" u2="p" k="10" />
+<hkern u1="X" u2="q" k="10" />
+<hkern u1="X" u2="r" k="10" />
+<hkern u1="X" u2="t" k="10" />
+<hkern u1="X" u2="u" k="10" />
+<hkern u1="X" u2="v" k="20" />
+<hkern u1="X" u2="w" k="10" />
+<hkern u1="X" u2="}" k="-40" />
+<hkern u1="X" u2="&#xc4;" k="-10" />
+<hkern u1="X" u2="&#xc5;" k="-10" />
+<hkern u1="X" u2="&#xd6;" k="25" />
+<hkern u1="X" u2="&#xe7;" k="10" />
+<hkern u1="X" u2="&#x131;" k="10" />
+<hkern u1="X" u2="&#xc6;" k="-10" />
+<hkern u1="X" u2="&#xd8;" k="25" />
+<hkern u1="X" u2="&#xe6;" k="10" />
+<hkern u1="X" u2="&#xc0;" k="-10" />
+<hkern u1="X" u2="&#xc3;" k="-10" />
+<hkern u1="X" u2="&#xd5;" k="25" />
+<hkern u1="X" u2="&#x152;" k="25" />
+<hkern u1="X" u2="&#x153;" k="10" />
+<hkern u1="X" u2="&#x2013;" k="30" />
+<hkern u1="X" u2="&#x2014;" k="30" />
+<hkern u1="X" u2="&#x178;" k="-20" />
+<hkern u1="X" u2="&#xc2;" k="-10" />
+<hkern u1="X" u2="&#xc1;" k="-10" />
+<hkern u1="X" u2="&#xd3;" k="25" />
+<hkern u1="X" u2="&#xd4;" k="25" />
+<hkern u1="X" u2="&#xd2;" k="25" />
+<hkern u1="[" u2="&#xdd;" k="-50" />
+<hkern u1="[" u2="Y" k="-50" />
+<hkern u1="[" u2="V" k="-30" />
+<hkern u1="[" u2="W" k="-10" />
+<hkern u1="[" u2="X" k="-40" />
+<hkern u1="[" u2="g" k="-20" />
+<hkern u1="[" u2="j" k="-150" />
+<hkern u1="[" u2="&#x178;" k="-50" />
+<hkern u1="\" u2="&#xdd;" k="60" />
+<hkern u1="\" u2="Y" k="60" />
+<hkern u1="\" u2="T" k="90" />
+<hkern u1="\" u2="V" k="70" />
+<hkern u1="\" u2="W" k="50" />
+<hkern u1="\" u2="a" k="10" />
+<hkern u1="\" u2="c" k="10" />
+<hkern u1="\" u2="d" k="10" />
+<hkern u1="\" u2="e" k="10" />
+<hkern u1="\" u2="j" k="-130" />
+<hkern u1="\" u2="o" k="10" />
+<hkern u1="\" u2="q" k="10" />
+<hkern u1="\" u2="&#xe7;" k="10" />
+<hkern u1="\" u2="&#xe6;" k="10" />
+<hkern u1="\" u2="&#x153;" k="10" />
+<hkern u1="\" u2="&#x178;" k="60" />
+<hkern u1="a" u2="y" k="5" />
+<hkern u1="a" u2="?" k="10" />
+<hkern u1="a" u2="\" k="10" />
+<hkern u1="a" u2="t" k="10" />
+<hkern u1="a" u2="v" k="5" />
+<hkern u1="a" u2="&#x2122;" k="20" />
+<hkern u1="a" u2="&#x201c;" k="30" />
+<hkern u1="a" u2="&#x2018;" k="30" />
+<hkern u1="b" u2="y" k="10" />
+<hkern u1="b" u2="z" k="15" />
+<hkern u1="b" u2="&#x2c;" k="20" />
+<hkern u1="b" u2="." k="20" />
+<hkern u1="b" u2="/" k="10" />
+<hkern u1="b" u2=":" k="10" />
+<hkern u1="b" u2=";" k="10" />
+<hkern u1="b" u2="?" k="20" />
+<hkern u1="b" u2="\" k="10" />
+<hkern u1="b" u2="v" k="10" />
+<hkern u1="b" u2="x" k="15" />
+<hkern u1="b" u2="&#x2122;" k="30" />
+<hkern u1="b" u2="&#x2026;" k="20" />
+<hkern u1="b" u2="&#x201c;" k="40" />
+<hkern u1="b" u2="&#x2018;" k="40" />
+<hkern u1="b" u2="&#x201a;" k="20" />
+<hkern u1="b" u2="&#x201e;" k="20" />
+<hkern u1="e" u2="y" k="10" />
+<hkern u1="e" u2="v" k="10" />
+<hkern u1="e" u2="x" k="15" />
+<hkern u1="f" u2="&#xf0;" k="20" />
+<hkern u1="f" u2="&amp;" k="30" />
+<hkern u1="f" u2=")" k="-70" />
+<hkern u1="f" u2="*" k="-20" />
+<hkern u1="f" u2="&#x2c;" k="80" />
+<hkern u1="f" u2="-" k="30" />
+<hkern u1="f" u2="." k="80" />
+<hkern u1="f" u2="/" k="50" />
+<hkern u1="f" u2=":" k="10" />
+<hkern u1="f" u2=";" k="10" />
+<hkern u1="f" u2="?" k="-40" />
+<hkern u1="f" u2="\" k="-40" />
+<hkern u1="f" u2="]" k="-70" />
+<hkern u1="f" u2="a" k="20" />
+<hkern u1="f" u2="c" k="20" />
+<hkern u1="f" u2="d" k="20" />
+<hkern u1="f" u2="e" k="20" />
+<hkern u1="f" u2="m" k="5" />
+<hkern u1="f" u2="n" k="5" />
+<hkern u1="f" u2="o" k="20" />
+<hkern u1="f" u2="p" k="5" />
+<hkern u1="f" u2="q" k="20" />
+<hkern u1="f" u2="r" k="5" />
+<hkern u1="f" u2="u" k="5" />
+<hkern u1="f" u2="}" k="-70" />
+<hkern u1="f" u2="&#xe7;" k="20" />
+<hkern u1="f" u2="&#x131;" k="5" />
+<hkern u1="f" u2="&#x2122;" k="-40" />
+<hkern u1="f" u2="&#xe6;" k="20" />
+<hkern u1="f" u2="&#xab;" k="50" />
+<hkern u1="f" u2="&#xbb;" k="20" />
+<hkern u1="f" u2="&#x2026;" k="80" />
+<hkern u1="f" u2="&#x153;" k="20" />
+<hkern u1="f" u2="&#x2013;" k="30" />
+<hkern u1="f" u2="&#x2014;" k="30" />
+<hkern u1="f" u2="&#x201c;" k="-30" />
+<hkern u1="f" u2="&#x201d;" k="-50" />
+<hkern u1="f" u2="&#x2018;" k="-30" />
+<hkern u1="f" u2="&#x2019;" k="-50" />
+<hkern u1="f" u2="&#x2039;" k="50" />
+<hkern u1="f" u2="&#x203a;" k="20" />
+<hkern u1="f" u2="&#x201a;" k="80" />
+<hkern u1="f" u2="&#x201e;" k="80" />
+<hkern u1="h" u2="y" k="5" />
+<hkern u1="h" u2="?" k="10" />
+<hkern u1="h" u2="\" k="10" />
+<hkern u1="h" u2="t" k="10" />
+<hkern u1="h" u2="v" k="5" />
+<hkern u1="h" u2="&#x2122;" k="20" />
+<hkern u1="h" u2="&#x201c;" k="30" />
+<hkern u1="h" u2="&#x2018;" k="30" />
+<hkern u1="k" u2="a" k="10" />
+<hkern u1="k" u2="c" k="10" />
+<hkern u1="k" u2="d" k="10" />
+<hkern u1="k" u2="e" k="10" />
+<hkern u1="k" u2="g" k="20" />
+<hkern u1="k" u2="o" k="10" />
+<hkern u1="k" u2="q" k="10" />
+<hkern u1="k" u2="&#xe7;" k="10" />
+<hkern u1="k" u2="&#xe6;" k="10" />
+<hkern u1="k" u2="&#x153;" k="10" />
+<hkern u1="m" u2="y" k="5" />
+<hkern u1="m" u2="?" k="10" />
+<hkern u1="m" u2="\" k="10" />
+<hkern u1="m" u2="t" k="10" />
+<hkern u1="m" u2="v" k="5" />
+<hkern u1="m" u2="&#x2122;" k="20" />
+<hkern u1="m" u2="&#x201c;" k="30" />
+<hkern u1="m" u2="&#x2018;" k="30" />
+<hkern u1="n" u2="y" k="5" />
+<hkern u1="n" u2="?" k="10" />
+<hkern u1="n" u2="\" k="10" />
+<hkern u1="n" u2="t" k="10" />
+<hkern u1="n" u2="v" k="5" />
+<hkern u1="n" u2="&#x2122;" k="20" />
+<hkern u1="n" u2="&#x201c;" k="30" />
+<hkern u1="n" u2="&#x2018;" k="30" />
+<hkern u1="o" u2="y" k="10" />
+<hkern u1="o" u2="z" k="15" />
+<hkern u1="o" u2="&#x2c;" k="20" />
+<hkern u1="o" u2="." k="20" />
+<hkern u1="o" u2="/" k="10" />
+<hkern u1="o" u2=":" k="10" />
+<hkern u1="o" u2=";" k="10" />
+<hkern u1="o" u2="?" k="20" />
+<hkern u1="o" u2="\" k="10" />
+<hkern u1="o" u2="v" k="10" />
+<hkern u1="o" u2="x" k="15" />
+<hkern u1="o" u2="&#x2122;" k="30" />
+<hkern u1="o" u2="&#x2026;" k="20" />
+<hkern u1="o" u2="&#x201c;" k="40" />
+<hkern u1="o" u2="&#x2018;" k="40" />
+<hkern u1="o" u2="&#x201a;" k="20" />
+<hkern u1="o" u2="&#x201e;" k="20" />
+<hkern u1="p" u2="y" k="10" />
+<hkern u1="p" u2="z" k="15" />
+<hkern u1="p" u2="&#x2c;" k="20" />
+<hkern u1="p" u2="." k="20" />
+<hkern u1="p" u2="/" k="10" />
+<hkern u1="p" u2=":" k="10" />
+<hkern u1="p" u2=";" k="10" />
+<hkern u1="p" u2="?" k="20" />
+<hkern u1="p" u2="\" k="10" />
+<hkern u1="p" u2="v" k="10" />
+<hkern u1="p" u2="x" k="15" />
+<hkern u1="p" u2="&#x2122;" k="30" />
+<hkern u1="p" u2="&#x2026;" k="20" />
+<hkern u1="p" u2="&#x201c;" k="40" />
+<hkern u1="p" u2="&#x2018;" k="40" />
+<hkern u1="p" u2="&#x201a;" k="20" />
+<hkern u1="p" u2="&#x201e;" k="20" />
+<hkern u1="r" u2="&#x2c;" k="50" />
+<hkern u1="r" u2="-" k="20" />
+<hkern u1="r" u2="." k="50" />
+<hkern u1="r" u2="/" k="60" />
+<hkern u1="r" u2="?" k="-20" />
+<hkern u1="r" u2="a" k="5" />
+<hkern u1="r" u2="c" k="5" />
+<hkern u1="r" u2="d" k="5" />
+<hkern u1="r" u2="e" k="5" />
+<hkern u1="r" u2="f" k="-15" />
+<hkern u1="r" u2="g" k="10" />
+<hkern u1="r" u2="o" k="5" />
+<hkern u1="r" u2="q" k="5" />
+<hkern u1="r" u2="t" k="-20" />
+<hkern u1="r" u2="&#xe7;" k="5" />
+<hkern u1="r" u2="&#xe6;" k="5" />
+<hkern u1="r" u2="&#x2026;" k="50" />
+<hkern u1="r" u2="&#x153;" k="5" />
+<hkern u1="r" u2="&#x2013;" k="20" />
+<hkern u1="r" u2="&#x2014;" k="20" />
+<hkern u1="r" u2="&#x201c;" k="-20" />
+<hkern u1="r" u2="&#x201d;" k="-40" />
+<hkern u1="r" u2="&#x2018;" k="-20" />
+<hkern u1="r" u2="&#x2019;" k="-40" />
+<hkern u1="r" u2="&#x201a;" k="50" />
+<hkern u1="r" u2="&#x201e;" k="50" />
+<hkern u1="t" u2="&#x2c;" k="-10" />
+<hkern u1="t" u2="." k="-10" />
+<hkern u1="t" u2="a" k="5" />
+<hkern u1="t" u2="c" k="5" />
+<hkern u1="t" u2="d" k="5" />
+<hkern u1="t" u2="e" k="5" />
+<hkern u1="t" u2="o" k="5" />
+<hkern u1="t" u2="q" k="5" />
+<hkern u1="t" u2="&#xe7;" k="5" />
+<hkern u1="t" u2="&#xe6;" k="5" />
+<hkern u1="t" u2="&#x2026;" k="-10" />
+<hkern u1="t" u2="&#x153;" k="5" />
+<hkern u1="t" u2="&#x201c;" k="-10" />
+<hkern u1="t" u2="&#x2018;" k="-10" />
+<hkern u1="t" u2="&#x201a;" k="-10" />
+<hkern u1="t" u2="&#x201e;" k="-10" />
+<hkern u1="v" u2="&#x2c;" k="50" />
+<hkern u1="v" u2="-" k="10" />
+<hkern u1="v" u2="." k="50" />
+<hkern u1="v" u2="a" k="10" />
+<hkern u1="v" u2="c" k="10" />
+<hkern u1="v" u2="d" k="10" />
+<hkern u1="v" u2="e" k="10" />
+<hkern u1="v" u2="o" k="10" />
+<hkern u1="v" u2="q" k="10" />
+<hkern u1="v" u2="&#xe7;" k="10" />
+<hkern u1="v" u2="&#xe6;" k="10" />
+<hkern u1="v" u2="&#x2026;" k="50" />
+<hkern u1="v" u2="&#x153;" k="10" />
+<hkern u1="v" u2="&#x2013;" k="10" />
+<hkern u1="v" u2="&#x2014;" k="10" />
+<hkern u1="v" u2="&#x201c;" k="-30" />
+<hkern u1="v" u2="&#x201d;" k="-30" />
+<hkern u1="v" u2="&#x2018;" k="-30" />
+<hkern u1="v" u2="&#x2019;" k="-30" />
+<hkern u1="v" u2="&#x201a;" k="50" />
+<hkern u1="v" u2="&#x201e;" k="50" />
+<hkern u1="w" u2="&#x2c;" k="30" />
+<hkern u1="w" u2="." k="30" />
+<hkern u1="w" u2="&#x2026;" k="30" />
+<hkern u1="w" u2="&#x201c;" k="-20" />
+<hkern u1="w" u2="&#x201d;" k="-20" />
+<hkern u1="w" u2="&#x2018;" k="-20" />
+<hkern u1="w" u2="&#x2019;" k="-20" />
+<hkern u1="w" u2="&#x201a;" k="30" />
+<hkern u1="w" u2="&#x201e;" k="30" />
+<hkern u1="x" u2="-" k="30" />
+<hkern u1="x" u2="a" k="15" />
+<hkern u1="x" u2="c" k="15" />
+<hkern u1="x" u2="d" k="15" />
+<hkern u1="x" u2="e" k="15" />
+<hkern u1="x" u2="o" k="15" />
+<hkern u1="x" u2="q" k="15" />
+<hkern u1="x" u2="&#xe7;" k="15" />
+<hkern u1="x" u2="&#xe6;" k="15" />
+<hkern u1="x" u2="&#xab;" k="50" />
+<hkern u1="x" u2="&#x153;" k="15" />
+<hkern u1="x" u2="&#x2013;" k="30" />
+<hkern u1="x" u2="&#x2014;" k="30" />
+<hkern u1="x" u2="&#x201c;" k="-10" />
+<hkern u1="x" u2="&#x201d;" k="-20" />
+<hkern u1="x" u2="&#x2018;" k="-10" />
+<hkern u1="x" u2="&#x2019;" k="-20" />
+<hkern u1="x" u2="&#x2039;" k="50" />
+<hkern u1="{" u2="&#xdd;" k="-50" />
+<hkern u1="{" u2="Y" k="-50" />
+<hkern u1="{" u2="V" k="-30" />
+<hkern u1="{" u2="W" k="-10" />
+<hkern u1="{" u2="X" k="-40" />
+<hkern u1="{" u2="g" k="-20" />
+<hkern u1="{" u2="j" k="-150" />
+<hkern u1="{" u2="&#x178;" k="-50" />
+<hkern u1="&#xc4;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc4;" u2="S" k="-5" />
+<hkern u1="&#xc4;" u2="&#xdd;" k="50" />
+<hkern u1="&#xc4;" u2="Y" k="50" />
+<hkern u1="&#xc4;" u2="y" k="30" />
+<hkern u1="&#xc4;" u2="z" k="-10" />
+<hkern u1="&#xc4;" u2="*" k="60" />
+<hkern u1="&#xc4;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc4;" u2="-" k="-10" />
+<hkern u1="&#xc4;" u2="." k="-30" />
+<hkern u1="&#xc4;" u2="@" k="10" />
+<hkern u1="&#xc4;" u2="A" k="-10" />
+<hkern u1="&#xc4;" u2="C" k="20" />
+<hkern u1="&#xc4;" u2="G" k="20" />
+<hkern u1="&#xc4;" u2="J" k="-10" />
+<hkern u1="&#xc4;" u2="O" k="20" />
+<hkern u1="&#xc4;" u2="Q" k="20" />
+<hkern u1="&#xc4;" u2="T" k="70" />
+<hkern u1="&#xc4;" u2="V" k="50" />
+<hkern u1="&#xc4;" u2="W" k="50" />
+<hkern u1="&#xc4;" u2="X" k="-10" />
+<hkern u1="&#xc4;" u2="a" k="5" />
+<hkern u1="&#xc4;" u2="c" k="5" />
+<hkern u1="&#xc4;" u2="d" k="5" />
+<hkern u1="&#xc4;" u2="e" k="5" />
+<hkern u1="&#xc4;" u2="m" k="5" />
+<hkern u1="&#xc4;" u2="n" k="5" />
+<hkern u1="&#xc4;" u2="o" k="5" />
+<hkern u1="&#xc4;" u2="p" k="5" />
+<hkern u1="&#xc4;" u2="q" k="5" />
+<hkern u1="&#xc4;" u2="r" k="5" />
+<hkern u1="&#xc4;" u2="t" k="5" />
+<hkern u1="&#xc4;" u2="u" k="5" />
+<hkern u1="&#xc4;" u2="v" k="30" />
+<hkern u1="&#xc4;" u2="w" k="10" />
+<hkern u1="&#xc4;" u2="x" k="-20" />
+<hkern u1="&#xc4;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc4;" u2="&#x131;" k="5" />
+<hkern u1="&#xc4;" u2="&#xae;" k="10" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc4;" u2="&#x152;" k="20" />
+<hkern u1="&#xc4;" u2="&#x153;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc4;" u2="&#x178;" k="50" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc5;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc5;" u2="S" k="-5" />
+<hkern u1="&#xc5;" u2="&#xdd;" k="50" />
+<hkern u1="&#xc5;" u2="Y" k="50" />
+<hkern u1="&#xc5;" u2="y" k="30" />
+<hkern u1="&#xc5;" u2="z" k="-10" />
+<hkern u1="&#xc5;" u2="*" k="60" />
+<hkern u1="&#xc5;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc5;" u2="-" k="-10" />
+<hkern u1="&#xc5;" u2="." k="-30" />
+<hkern u1="&#xc5;" u2="@" k="10" />
+<hkern u1="&#xc5;" u2="A" k="-10" />
+<hkern u1="&#xc5;" u2="C" k="20" />
+<hkern u1="&#xc5;" u2="G" k="20" />
+<hkern u1="&#xc5;" u2="J" k="-10" />
+<hkern u1="&#xc5;" u2="O" k="20" />
+<hkern u1="&#xc5;" u2="Q" k="20" />
+<hkern u1="&#xc5;" u2="T" k="70" />
+<hkern u1="&#xc5;" u2="V" k="50" />
+<hkern u1="&#xc5;" u2="W" k="50" />
+<hkern u1="&#xc5;" u2="X" k="-10" />
+<hkern u1="&#xc5;" u2="a" k="5" />
+<hkern u1="&#xc5;" u2="c" k="5" />
+<hkern u1="&#xc5;" u2="d" k="5" />
+<hkern u1="&#xc5;" u2="e" k="5" />
+<hkern u1="&#xc5;" u2="m" k="5" />
+<hkern u1="&#xc5;" u2="n" k="5" />
+<hkern u1="&#xc5;" u2="o" k="5" />
+<hkern u1="&#xc5;" u2="p" k="5" />
+<hkern u1="&#xc5;" u2="q" k="5" />
+<hkern u1="&#xc5;" u2="r" k="5" />
+<hkern u1="&#xc5;" u2="t" k="5" />
+<hkern u1="&#xc5;" u2="u" k="5" />
+<hkern u1="&#xc5;" u2="v" k="30" />
+<hkern u1="&#xc5;" u2="w" k="10" />
+<hkern u1="&#xc5;" u2="x" k="-20" />
+<hkern u1="&#xc5;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc5;" u2="&#x131;" k="5" />
+<hkern u1="&#xc5;" u2="&#xae;" k="10" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc5;" u2="&#x152;" k="20" />
+<hkern u1="&#xc5;" u2="&#x153;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc5;" u2="&#x178;" k="50" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc9;" u2="y" k="20" />
+<hkern u1="&#xc9;" u2="@" k="20" />
+<hkern u1="&#xc9;" u2="T" k="-15" />
+<hkern u1="&#xc9;" u2="a" k="25" />
+<hkern u1="&#xc9;" u2="c" k="25" />
+<hkern u1="&#xc9;" u2="d" k="25" />
+<hkern u1="&#xc9;" u2="e" k="25" />
+<hkern u1="&#xc9;" u2="f" k="10" />
+<hkern u1="&#xc9;" u2="g" k="5" />
+<hkern u1="&#xc9;" u2="o" k="25" />
+<hkern u1="&#xc9;" u2="q" k="25" />
+<hkern u1="&#xc9;" u2="v" k="20" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc9;" u2="&#xae;" k="20" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc9;" u2="&#x153;" k="25" />
+<hkern u1="&#xd1;" u2="y" k="10" />
+<hkern u1="&#xd1;" u2="/" k="20" />
+<hkern u1="&#xd1;" u2="v" k="10" />
+<hkern u1="&#xd6;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd6;" u2="Y" k="25" />
+<hkern u1="&#xd6;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd6;" u2="Z" k="45" />
+<hkern u1="&#xd6;" u2="z" k="10" />
+<hkern u1="&#xd6;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd6;" u2="." k="40" />
+<hkern u1="&#xd6;" u2="/" k="50" />
+<hkern u1="&#xd6;" u2="?" k="20" />
+<hkern u1="&#xd6;" u2="A" k="20" />
+<hkern u1="&#xd6;" u2="J" k="45" />
+<hkern u1="&#xd6;" u2="T" k="30" />
+<hkern u1="&#xd6;" u2="V" k="20" />
+<hkern u1="&#xd6;" u2="W" k="40" />
+<hkern u1="&#xd6;" u2="X" k="25" />
+<hkern u1="&#xd6;" u2="a" k="5" />
+<hkern u1="&#xd6;" u2="b" k="10" />
+<hkern u1="&#xd6;" u2="c" k="5" />
+<hkern u1="&#xd6;" u2="d" k="5" />
+<hkern u1="&#xd6;" u2="e" k="5" />
+<hkern u1="&#xd6;" u2="h" k="10" />
+<hkern u1="&#xd6;" u2="k" k="10" />
+<hkern u1="&#xd6;" u2="l" k="10" />
+<hkern u1="&#xd6;" u2="m" k="5" />
+<hkern u1="&#xd6;" u2="n" k="5" />
+<hkern u1="&#xd6;" u2="o" k="5" />
+<hkern u1="&#xd6;" u2="p" k="5" />
+<hkern u1="&#xd6;" u2="q" k="5" />
+<hkern u1="&#xd6;" u2="r" k="5" />
+<hkern u1="&#xd6;" u2="u" k="5" />
+<hkern u1="&#xd6;" u2="x" k="10" />
+<hkern u1="&#xd6;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd6;" u2="&#x131;" k="5" />
+<hkern u1="&#xd6;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd6;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd6;" u2="&#x153;" k="5" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd6;" u2="&#x178;" k="25" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd6;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc1;" k="20" />
+<hkern u1="&#xdc;" u2="&#x2c;" k="10" />
+<hkern u1="&#xdc;" u2="." k="10" />
+<hkern u1="&#xdc;" u2="J" k="10" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="10" />
+<hkern u1="&#xe1;" u2="y" k="5" />
+<hkern u1="&#xe1;" u2="?" k="10" />
+<hkern u1="&#xe1;" u2="\" k="10" />
+<hkern u1="&#xe1;" u2="t" k="10" />
+<hkern u1="&#xe1;" u2="v" k="5" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe0;" u2="y" k="5" />
+<hkern u1="&#xe0;" u2="?" k="10" />
+<hkern u1="&#xe0;" u2="\" k="10" />
+<hkern u1="&#xe0;" u2="t" k="10" />
+<hkern u1="&#xe0;" u2="v" k="5" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe2;" u2="y" k="5" />
+<hkern u1="&#xe2;" u2="?" k="10" />
+<hkern u1="&#xe2;" u2="\" k="10" />
+<hkern u1="&#xe2;" u2="t" k="10" />
+<hkern u1="&#xe2;" u2="v" k="5" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe4;" u2="y" k="5" />
+<hkern u1="&#xe4;" u2="?" k="10" />
+<hkern u1="&#xe4;" u2="\" k="10" />
+<hkern u1="&#xe4;" u2="t" k="10" />
+<hkern u1="&#xe4;" u2="v" k="5" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe9;" u2="y" k="10" />
+<hkern u1="&#xe9;" u2="v" k="10" />
+<hkern u1="&#xe9;" u2="x" k="15" />
+<hkern u1="&#xe8;" u2="y" k="10" />
+<hkern u1="&#xe8;" u2="v" k="10" />
+<hkern u1="&#xe8;" u2="x" k="15" />
+<hkern u1="&#xea;" u2="y" k="10" />
+<hkern u1="&#xea;" u2="v" k="10" />
+<hkern u1="&#xea;" u2="x" k="15" />
+<hkern u1="&#xeb;" u2="y" k="10" />
+<hkern u1="&#xeb;" u2="v" k="10" />
+<hkern u1="&#xeb;" u2="x" k="15" />
+<hkern u1="&#xf1;" u2="y" k="5" />
+<hkern u1="&#xf1;" u2="?" k="10" />
+<hkern u1="&#xf1;" u2="\" k="10" />
+<hkern u1="&#xf1;" u2="t" k="10" />
+<hkern u1="&#xf1;" u2="v" k="5" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xf3;" u2="y" k="10" />
+<hkern u1="&#xf3;" u2="z" k="15" />
+<hkern u1="&#xf3;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf3;" u2="." k="20" />
+<hkern u1="&#xf3;" u2="/" k="10" />
+<hkern u1="&#xf3;" u2=":" k="10" />
+<hkern u1="&#xf3;" u2=";" k="10" />
+<hkern u1="&#xf3;" u2="?" k="20" />
+<hkern u1="&#xf3;" u2="\" k="10" />
+<hkern u1="&#xf3;" u2="v" k="10" />
+<hkern u1="&#xf3;" u2="x" k="15" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf4;" u2="y" k="10" />
+<hkern u1="&#xf4;" u2="z" k="15" />
+<hkern u1="&#xf4;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf4;" u2="." k="20" />
+<hkern u1="&#xf4;" u2="/" k="10" />
+<hkern u1="&#xf4;" u2=":" k="10" />
+<hkern u1="&#xf4;" u2=";" k="10" />
+<hkern u1="&#xf4;" u2="?" k="20" />
+<hkern u1="&#xf4;" u2="\" k="10" />
+<hkern u1="&#xf4;" u2="v" k="10" />
+<hkern u1="&#xf4;" u2="x" k="15" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf6;" u2="y" k="10" />
+<hkern u1="&#xf6;" u2="z" k="15" />
+<hkern u1="&#xf6;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf6;" u2="." k="20" />
+<hkern u1="&#xf6;" u2="/" k="10" />
+<hkern u1="&#xf6;" u2=":" k="10" />
+<hkern u1="&#xf6;" u2=";" k="10" />
+<hkern u1="&#xf6;" u2="?" k="20" />
+<hkern u1="&#xf6;" u2="\" k="10" />
+<hkern u1="&#xf6;" u2="v" k="10" />
+<hkern u1="&#xf6;" u2="x" k="15" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf5;" u2="y" k="10" />
+<hkern u1="&#xf5;" u2="z" k="15" />
+<hkern u1="&#xf5;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf5;" u2="." k="20" />
+<hkern u1="&#xf5;" u2="/" k="10" />
+<hkern u1="&#xf5;" u2=":" k="10" />
+<hkern u1="&#xf5;" u2=";" k="10" />
+<hkern u1="&#xf5;" u2="?" k="20" />
+<hkern u1="&#xf5;" u2="\" k="10" />
+<hkern u1="&#xf5;" u2="v" k="10" />
+<hkern u1="&#xf5;" u2="x" k="15" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="20" />
+<hkern u1="&#xb0;" u2="4" k="65" />
+<hkern u1="&#xa3;" u2="1" k="-10" />
+<hkern u1="&#xa3;" u2="4" k="20" />
+<hkern u1="&#xdf;" u2="y" k="10" />
+<hkern u1="&#xdf;" u2="z" k="15" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="20" />
+<hkern u1="&#xdf;" u2="." k="20" />
+<hkern u1="&#xdf;" u2="/" k="10" />
+<hkern u1="&#xdf;" u2=":" k="10" />
+<hkern u1="&#xdf;" u2=";" k="10" />
+<hkern u1="&#xdf;" u2="?" k="20" />
+<hkern u1="&#xdf;" u2="\" k="10" />
+<hkern u1="&#xdf;" u2="g" k="10" />
+<hkern u1="&#xdf;" u2="t" k="10" />
+<hkern u1="&#xdf;" u2="v" k="10" />
+<hkern u1="&#xdf;" u2="x" k="15" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="30" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="40" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="40" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="20" />
+<hkern u1="&#xae;" u2="&#xdd;" k="30" />
+<hkern u1="&#xae;" u2="Y" k="30" />
+<hkern u1="&#xae;" u2="A" k="10" />
+<hkern u1="&#xae;" u2="T" k="30" />
+<hkern u1="&#xae;" u2="W" k="20" />
+<hkern u1="&#xae;" u2="&#xc4;" k="10" />
+<hkern u1="&#xae;" u2="&#xc5;" k="10" />
+<hkern u1="&#xae;" u2="&#xc6;" k="10" />
+<hkern u1="&#xae;" u2="&#xc0;" k="10" />
+<hkern u1="&#xae;" u2="&#xc3;" k="10" />
+<hkern u1="&#xae;" u2="&#x178;" k="30" />
+<hkern u1="&#xae;" u2="&#xc2;" k="10" />
+<hkern u1="&#xae;" u2="&#xc1;" k="10" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="30" />
+<hkern u1="&#xa9;" u2="Y" k="30" />
+<hkern u1="&#xa9;" u2="A" k="10" />
+<hkern u1="&#xa9;" u2="T" k="30" />
+<hkern u1="&#xa9;" u2="W" k="20" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="10" />
+<hkern u1="&#xa9;" u2="&#x178;" k="30" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="10" />
+<hkern u1="&#xc6;" u2="y" k="20" />
+<hkern u1="&#xc6;" u2="@" k="20" />
+<hkern u1="&#xc6;" u2="T" k="-15" />
+<hkern u1="&#xc6;" u2="a" k="25" />
+<hkern u1="&#xc6;" u2="c" k="25" />
+<hkern u1="&#xc6;" u2="d" k="25" />
+<hkern u1="&#xc6;" u2="e" k="25" />
+<hkern u1="&#xc6;" u2="f" k="10" />
+<hkern u1="&#xc6;" u2="g" k="5" />
+<hkern u1="&#xc6;" u2="o" k="25" />
+<hkern u1="&#xc6;" u2="q" k="25" />
+<hkern u1="&#xc6;" u2="v" k="20" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc6;" u2="&#xae;" k="20" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc6;" u2="&#x153;" k="25" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd8;" u2="Y" k="25" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd8;" u2="Z" k="45" />
+<hkern u1="&#xd8;" u2="z" k="10" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd8;" u2="." k="40" />
+<hkern u1="&#xd8;" u2="/" k="50" />
+<hkern u1="&#xd8;" u2="?" k="20" />
+<hkern u1="&#xd8;" u2="A" k="20" />
+<hkern u1="&#xd8;" u2="J" k="45" />
+<hkern u1="&#xd8;" u2="T" k="30" />
+<hkern u1="&#xd8;" u2="V" k="20" />
+<hkern u1="&#xd8;" u2="W" k="40" />
+<hkern u1="&#xd8;" u2="X" k="25" />
+<hkern u1="&#xd8;" u2="a" k="5" />
+<hkern u1="&#xd8;" u2="b" k="10" />
+<hkern u1="&#xd8;" u2="c" k="5" />
+<hkern u1="&#xd8;" u2="d" k="5" />
+<hkern u1="&#xd8;" u2="e" k="5" />
+<hkern u1="&#xd8;" u2="h" k="10" />
+<hkern u1="&#xd8;" u2="k" k="10" />
+<hkern u1="&#xd8;" u2="l" k="10" />
+<hkern u1="&#xd8;" u2="m" k="5" />
+<hkern u1="&#xd8;" u2="n" k="5" />
+<hkern u1="&#xd8;" u2="o" k="5" />
+<hkern u1="&#xd8;" u2="p" k="5" />
+<hkern u1="&#xd8;" u2="q" k="5" />
+<hkern u1="&#xd8;" u2="r" k="5" />
+<hkern u1="&#xd8;" u2="u" k="5" />
+<hkern u1="&#xd8;" u2="x" k="10" />
+<hkern u1="&#xd8;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd8;" u2="&#x131;" k="5" />
+<hkern u1="&#xd8;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd8;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd8;" u2="&#x153;" k="5" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd8;" u2="&#x178;" k="25" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd8;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc1;" k="20" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="30" />
+<hkern u1="&#x3c0;" u2="Y" k="30" />
+<hkern u1="&#x3c0;" u2="A" k="10" />
+<hkern u1="&#x3c0;" u2="T" k="30" />
+<hkern u1="&#x3c0;" u2="W" k="20" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="10" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="10" />
+<hkern u1="&#xe6;" u2="y" k="10" />
+<hkern u1="&#xe6;" u2="v" k="10" />
+<hkern u1="&#xe6;" u2="x" k="15" />
+<hkern u1="&#xf8;" u2="y" k="10" />
+<hkern u1="&#xf8;" u2="z" k="15" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf8;" u2="." k="20" />
+<hkern u1="&#xf8;" u2="/" k="10" />
+<hkern u1="&#xf8;" u2=":" k="10" />
+<hkern u1="&#xf8;" u2=";" k="10" />
+<hkern u1="&#xf8;" u2="?" k="20" />
+<hkern u1="&#xf8;" u2="\" k="10" />
+<hkern u1="&#xf8;" u2="v" k="10" />
+<hkern u1="&#xf8;" u2="x" k="15" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="20" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="40" />
+<hkern u1="&#xbf;" u2="Y" k="40" />
+<hkern u1="&#xbf;" u2="y" k="30" />
+<hkern u1="&#xbf;" u2="1" k="40" />
+<hkern u1="&#xbf;" u2="3" k="-10" />
+<hkern u1="&#xbf;" u2="7" k="30" />
+<hkern u1="&#xbf;" u2="C" k="20" />
+<hkern u1="&#xbf;" u2="G" k="20" />
+<hkern u1="&#xbf;" u2="O" k="20" />
+<hkern u1="&#xbf;" u2="Q" k="20" />
+<hkern u1="&#xbf;" u2="T" k="60" />
+<hkern u1="&#xbf;" u2="V" k="50" />
+<hkern u1="&#xbf;" u2="W" k="30" />
+<hkern u1="&#xbf;" u2="v" k="30" />
+<hkern u1="&#xbf;" u2="w" k="20" />
+<hkern u1="&#xbf;" u2="x" k="10" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="20" />
+<hkern u1="&#xbf;" u2="&#x152;" k="20" />
+<hkern u1="&#xbf;" u2="&#x178;" k="40" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="20" />
+<hkern u1="&#x192;" u2="1" k="-30" />
+<hkern u1="&#xab;" u2="&#xdd;" k="40" />
+<hkern u1="&#xab;" u2="Y" k="40" />
+<hkern u1="&#xab;" u2="T" k="40" />
+<hkern u1="&#xab;" u2="V" k="20" />
+<hkern u1="&#xab;" u2="W" k="20" />
+<hkern u1="&#xab;" u2="&#x178;" k="40" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="50" />
+<hkern u1="&#xbb;" u2="Y" k="50" />
+<hkern u1="&#xbb;" u2="z" k="50" />
+<hkern u1="&#xbb;" u2="T" k="110" />
+<hkern u1="&#xbb;" u2="V" k="50" />
+<hkern u1="&#xbb;" u2="W" k="40" />
+<hkern u1="&#xbb;" u2="x" k="50" />
+<hkern u1="&#xbb;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2026;" u2="Y" k="50" />
+<hkern u1="&#x2026;" u2="y" k="30" />
+<hkern u1="&#x2026;" u2="0" k="40" />
+<hkern u1="&#x2026;" u2="1" k="60" />
+<hkern u1="&#x2026;" u2="4" k="50" />
+<hkern u1="&#x2026;" u2="6" k="30" />
+<hkern u1="&#x2026;" u2="8" k="15" />
+<hkern u1="&#x2026;" u2="9" k="10" />
+<hkern u1="&#x2026;" u2="A" k="-30" />
+<hkern u1="&#x2026;" u2="C" k="40" />
+<hkern u1="&#x2026;" u2="G" k="40" />
+<hkern u1="&#x2026;" u2="O" k="40" />
+<hkern u1="&#x2026;" u2="Q" k="40" />
+<hkern u1="&#x2026;" u2="T" k="60" />
+<hkern u1="&#x2026;" u2="U" k="10" />
+<hkern u1="&#x2026;" u2="V" k="60" />
+<hkern u1="&#x2026;" u2="W" k="20" />
+<hkern u1="&#x2026;" u2="a" k="20" />
+<hkern u1="&#x2026;" u2="c" k="20" />
+<hkern u1="&#x2026;" u2="d" k="20" />
+<hkern u1="&#x2026;" u2="e" k="20" />
+<hkern u1="&#x2026;" u2="m" k="20" />
+<hkern u1="&#x2026;" u2="n" k="20" />
+<hkern u1="&#x2026;" u2="o" k="20" />
+<hkern u1="&#x2026;" u2="p" k="20" />
+<hkern u1="&#x2026;" u2="q" k="20" />
+<hkern u1="&#x2026;" u2="r" k="20" />
+<hkern u1="&#x2026;" u2="t" k="40" />
+<hkern u1="&#x2026;" u2="u" k="20" />
+<hkern u1="&#x2026;" u2="v" k="30" />
+<hkern u1="&#x2026;" u2="w" k="10" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2026;" u2="&#x131;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2026;" u2="&#x152;" k="40" />
+<hkern u1="&#x2026;" u2="&#x153;" k="20" />
+<hkern u1="&#x2026;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2026;" u2="&#xda;" k="10" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="10" />
+<hkern u1="&#xc0;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc0;" u2="S" k="-5" />
+<hkern u1="&#xc0;" u2="&#xdd;" k="50" />
+<hkern u1="&#xc0;" u2="Y" k="50" />
+<hkern u1="&#xc0;" u2="y" k="30" />
+<hkern u1="&#xc0;" u2="z" k="-10" />
+<hkern u1="&#xc0;" u2="*" k="60" />
+<hkern u1="&#xc0;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc0;" u2="-" k="-10" />
+<hkern u1="&#xc0;" u2="." k="-30" />
+<hkern u1="&#xc0;" u2="@" k="10" />
+<hkern u1="&#xc0;" u2="A" k="-10" />
+<hkern u1="&#xc0;" u2="C" k="20" />
+<hkern u1="&#xc0;" u2="G" k="20" />
+<hkern u1="&#xc0;" u2="J" k="-10" />
+<hkern u1="&#xc0;" u2="O" k="20" />
+<hkern u1="&#xc0;" u2="Q" k="20" />
+<hkern u1="&#xc0;" u2="T" k="70" />
+<hkern u1="&#xc0;" u2="V" k="50" />
+<hkern u1="&#xc0;" u2="W" k="50" />
+<hkern u1="&#xc0;" u2="X" k="-10" />
+<hkern u1="&#xc0;" u2="a" k="5" />
+<hkern u1="&#xc0;" u2="c" k="5" />
+<hkern u1="&#xc0;" u2="d" k="5" />
+<hkern u1="&#xc0;" u2="e" k="5" />
+<hkern u1="&#xc0;" u2="m" k="5" />
+<hkern u1="&#xc0;" u2="n" k="5" />
+<hkern u1="&#xc0;" u2="o" k="5" />
+<hkern u1="&#xc0;" u2="p" k="5" />
+<hkern u1="&#xc0;" u2="q" k="5" />
+<hkern u1="&#xc0;" u2="r" k="5" />
+<hkern u1="&#xc0;" u2="t" k="5" />
+<hkern u1="&#xc0;" u2="u" k="5" />
+<hkern u1="&#xc0;" u2="v" k="30" />
+<hkern u1="&#xc0;" u2="w" k="10" />
+<hkern u1="&#xc0;" u2="x" k="-20" />
+<hkern u1="&#xc0;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc0;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc0;" u2="&#x131;" k="5" />
+<hkern u1="&#xc0;" u2="&#xae;" k="10" />
+<hkern u1="&#xc0;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc0;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc0;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc0;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc0;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc0;" u2="&#x152;" k="20" />
+<hkern u1="&#xc0;" u2="&#x153;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc0;" u2="&#x178;" k="50" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc3;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc3;" u2="S" k="-5" />
+<hkern u1="&#xc3;" u2="&#xdd;" k="50" />
+<hkern u1="&#xc3;" u2="Y" k="50" />
+<hkern u1="&#xc3;" u2="y" k="30" />
+<hkern u1="&#xc3;" u2="z" k="-10" />
+<hkern u1="&#xc3;" u2="*" k="60" />
+<hkern u1="&#xc3;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc3;" u2="-" k="-10" />
+<hkern u1="&#xc3;" u2="." k="-30" />
+<hkern u1="&#xc3;" u2="@" k="10" />
+<hkern u1="&#xc3;" u2="A" k="-10" />
+<hkern u1="&#xc3;" u2="C" k="20" />
+<hkern u1="&#xc3;" u2="G" k="20" />
+<hkern u1="&#xc3;" u2="J" k="-10" />
+<hkern u1="&#xc3;" u2="O" k="20" />
+<hkern u1="&#xc3;" u2="Q" k="20" />
+<hkern u1="&#xc3;" u2="T" k="70" />
+<hkern u1="&#xc3;" u2="V" k="50" />
+<hkern u1="&#xc3;" u2="W" k="50" />
+<hkern u1="&#xc3;" u2="X" k="-10" />
+<hkern u1="&#xc3;" u2="a" k="5" />
+<hkern u1="&#xc3;" u2="c" k="5" />
+<hkern u1="&#xc3;" u2="d" k="5" />
+<hkern u1="&#xc3;" u2="e" k="5" />
+<hkern u1="&#xc3;" u2="m" k="5" />
+<hkern u1="&#xc3;" u2="n" k="5" />
+<hkern u1="&#xc3;" u2="o" k="5" />
+<hkern u1="&#xc3;" u2="p" k="5" />
+<hkern u1="&#xc3;" u2="q" k="5" />
+<hkern u1="&#xc3;" u2="r" k="5" />
+<hkern u1="&#xc3;" u2="t" k="5" />
+<hkern u1="&#xc3;" u2="u" k="5" />
+<hkern u1="&#xc3;" u2="v" k="30" />
+<hkern u1="&#xc3;" u2="w" k="10" />
+<hkern u1="&#xc3;" u2="x" k="-20" />
+<hkern u1="&#xc3;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc3;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc3;" u2="&#x131;" k="5" />
+<hkern u1="&#xc3;" u2="&#xae;" k="10" />
+<hkern u1="&#xc3;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc3;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc3;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc3;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc3;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc3;" u2="&#x152;" k="20" />
+<hkern u1="&#xc3;" u2="&#x153;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc3;" u2="&#x178;" k="50" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="20" />
+<hkern u1="&#xd5;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd5;" u2="Y" k="25" />
+<hkern u1="&#xd5;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd5;" u2="Z" k="45" />
+<hkern u1="&#xd5;" u2="z" k="10" />
+<hkern u1="&#xd5;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd5;" u2="." k="40" />
+<hkern u1="&#xd5;" u2="/" k="50" />
+<hkern u1="&#xd5;" u2="?" k="20" />
+<hkern u1="&#xd5;" u2="A" k="20" />
+<hkern u1="&#xd5;" u2="J" k="45" />
+<hkern u1="&#xd5;" u2="T" k="30" />
+<hkern u1="&#xd5;" u2="V" k="20" />
+<hkern u1="&#xd5;" u2="W" k="40" />
+<hkern u1="&#xd5;" u2="X" k="25" />
+<hkern u1="&#xd5;" u2="a" k="5" />
+<hkern u1="&#xd5;" u2="b" k="10" />
+<hkern u1="&#xd5;" u2="c" k="5" />
+<hkern u1="&#xd5;" u2="d" k="5" />
+<hkern u1="&#xd5;" u2="e" k="5" />
+<hkern u1="&#xd5;" u2="h" k="10" />
+<hkern u1="&#xd5;" u2="k" k="10" />
+<hkern u1="&#xd5;" u2="l" k="10" />
+<hkern u1="&#xd5;" u2="m" k="5" />
+<hkern u1="&#xd5;" u2="n" k="5" />
+<hkern u1="&#xd5;" u2="o" k="5" />
+<hkern u1="&#xd5;" u2="p" k="5" />
+<hkern u1="&#xd5;" u2="q" k="5" />
+<hkern u1="&#xd5;" u2="r" k="5" />
+<hkern u1="&#xd5;" u2="u" k="5" />
+<hkern u1="&#xd5;" u2="x" k="10" />
+<hkern u1="&#xd5;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd5;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd5;" u2="&#x131;" k="5" />
+<hkern u1="&#xd5;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd5;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd5;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd5;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd5;" u2="&#x153;" k="5" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd5;" u2="&#x178;" k="25" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd5;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc1;" k="20" />
+<hkern u1="&#x152;" u2="y" k="20" />
+<hkern u1="&#x152;" u2="@" k="20" />
+<hkern u1="&#x152;" u2="T" k="-15" />
+<hkern u1="&#x152;" u2="a" k="25" />
+<hkern u1="&#x152;" u2="c" k="25" />
+<hkern u1="&#x152;" u2="d" k="25" />
+<hkern u1="&#x152;" u2="e" k="25" />
+<hkern u1="&#x152;" u2="f" k="10" />
+<hkern u1="&#x152;" u2="g" k="5" />
+<hkern u1="&#x152;" u2="o" k="25" />
+<hkern u1="&#x152;" u2="q" k="25" />
+<hkern u1="&#x152;" u2="v" k="20" />
+<hkern u1="&#x152;" u2="&#xe7;" k="25" />
+<hkern u1="&#x152;" u2="&#xae;" k="20" />
+<hkern u1="&#x152;" u2="&#xa9;" k="20" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="20" />
+<hkern u1="&#x152;" u2="&#xe6;" k="25" />
+<hkern u1="&#x152;" u2="&#x153;" k="25" />
+<hkern u1="&#x153;" u2="y" k="10" />
+<hkern u1="&#x153;" u2="v" k="10" />
+<hkern u1="&#x153;" u2="x" k="15" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2013;" u2="Y" k="50" />
+<hkern u1="&#x2013;" u2="y" k="10" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2013;" u2="Z" k="10" />
+<hkern u1="&#x2013;" u2="z" k="30" />
+<hkern u1="&#x2013;" u2="1" k="20" />
+<hkern u1="&#x2013;" u2="7" k="30" />
+<hkern u1="&#x2013;" u2="A" k="-10" />
+<hkern u1="&#x2013;" u2="T" k="70" />
+<hkern u1="&#x2013;" u2="V" k="50" />
+<hkern u1="&#x2013;" u2="W" k="40" />
+<hkern u1="&#x2013;" u2="X" k="30" />
+<hkern u1="&#x2013;" u2="a" k="10" />
+<hkern u1="&#x2013;" u2="c" k="10" />
+<hkern u1="&#x2013;" u2="d" k="10" />
+<hkern u1="&#x2013;" u2="e" k="10" />
+<hkern u1="&#x2013;" u2="o" k="10" />
+<hkern u1="&#x2013;" u2="q" k="10" />
+<hkern u1="&#x2013;" u2="v" k="10" />
+<hkern u1="&#x2013;" u2="x" k="30" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2013;" u2="&#x153;" k="10" />
+<hkern u1="&#x2013;" u2="&#x178;" k="50" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2014;" u2="Y" k="50" />
+<hkern u1="&#x2014;" u2="y" k="10" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2014;" u2="Z" k="10" />
+<hkern u1="&#x2014;" u2="z" k="30" />
+<hkern u1="&#x2014;" u2="1" k="20" />
+<hkern u1="&#x2014;" u2="7" k="30" />
+<hkern u1="&#x2014;" u2="A" k="-10" />
+<hkern u1="&#x2014;" u2="T" k="70" />
+<hkern u1="&#x2014;" u2="V" k="50" />
+<hkern u1="&#x2014;" u2="W" k="40" />
+<hkern u1="&#x2014;" u2="X" k="30" />
+<hkern u1="&#x2014;" u2="a" k="10" />
+<hkern u1="&#x2014;" u2="c" k="10" />
+<hkern u1="&#x2014;" u2="d" k="10" />
+<hkern u1="&#x2014;" u2="e" k="10" />
+<hkern u1="&#x2014;" u2="o" k="10" />
+<hkern u1="&#x2014;" u2="q" k="10" />
+<hkern u1="&#x2014;" u2="v" k="10" />
+<hkern u1="&#x2014;" u2="x" k="30" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2014;" u2="&#x153;" k="10" />
+<hkern u1="&#x2014;" u2="&#x178;" k="50" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x201c;" u2="&#x161;" k="30" />
+<hkern u1="&#x201c;" u2="s" k="30" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x201c;" u2="Y" k="-30" />
+<hkern u1="&#x201c;" u2="A" k="40" />
+<hkern u1="&#x201c;" u2="J" k="110" />
+<hkern u1="&#x201c;" u2="T" k="-10" />
+<hkern u1="&#x201c;" u2="V" k="-20" />
+<hkern u1="&#x201c;" u2="W" k="-20" />
+<hkern u1="&#x201c;" u2="X" k="-20" />
+<hkern u1="&#x201c;" u2="a" k="30" />
+<hkern u1="&#x201c;" u2="c" k="30" />
+<hkern u1="&#x201c;" u2="d" k="30" />
+<hkern u1="&#x201c;" u2="e" k="30" />
+<hkern u1="&#x201c;" u2="g" k="40" />
+<hkern u1="&#x201c;" u2="m" k="10" />
+<hkern u1="&#x201c;" u2="n" k="10" />
+<hkern u1="&#x201c;" u2="o" k="30" />
+<hkern u1="&#x201c;" u2="p" k="10" />
+<hkern u1="&#x201c;" u2="q" k="30" />
+<hkern u1="&#x201c;" u2="r" k="10" />
+<hkern u1="&#x201c;" u2="u" k="10" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="30" />
+<hkern u1="&#x201c;" u2="&#x131;" k="10" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="40" />
+<hkern u1="&#x201c;" u2="&#x153;" k="30" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-30" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="40" />
+<hkern u1="&#x201d;" u2="&#x160;" k="10" />
+<hkern u1="&#x201d;" u2="S" k="10" />
+<hkern u1="&#x201d;" u2="C" k="40" />
+<hkern u1="&#x201d;" u2="G" k="40" />
+<hkern u1="&#x201d;" u2="J" k="120" />
+<hkern u1="&#x201d;" u2="O" k="40" />
+<hkern u1="&#x201d;" u2="Q" k="40" />
+<hkern u1="&#x201d;" u2="a" k="50" />
+<hkern u1="&#x201d;" u2="c" k="50" />
+<hkern u1="&#x201d;" u2="d" k="50" />
+<hkern u1="&#x201d;" u2="e" k="50" />
+<hkern u1="&#x201d;" u2="o" k="50" />
+<hkern u1="&#x201d;" u2="q" k="50" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201d;" u2="&#x152;" k="40" />
+<hkern u1="&#x201d;" u2="&#x153;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2018;" u2="&#x161;" k="30" />
+<hkern u1="&#x2018;" u2="s" k="30" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x2018;" u2="Y" k="-30" />
+<hkern u1="&#x2018;" u2="A" k="40" />
+<hkern u1="&#x2018;" u2="J" k="110" />
+<hkern u1="&#x2018;" u2="T" k="-10" />
+<hkern u1="&#x2018;" u2="V" k="-20" />
+<hkern u1="&#x2018;" u2="W" k="-20" />
+<hkern u1="&#x2018;" u2="X" k="-20" />
+<hkern u1="&#x2018;" u2="a" k="30" />
+<hkern u1="&#x2018;" u2="c" k="30" />
+<hkern u1="&#x2018;" u2="d" k="30" />
+<hkern u1="&#x2018;" u2="e" k="30" />
+<hkern u1="&#x2018;" u2="g" k="40" />
+<hkern u1="&#x2018;" u2="m" k="10" />
+<hkern u1="&#x2018;" u2="n" k="10" />
+<hkern u1="&#x2018;" u2="o" k="30" />
+<hkern u1="&#x2018;" u2="p" k="10" />
+<hkern u1="&#x2018;" u2="q" k="30" />
+<hkern u1="&#x2018;" u2="r" k="10" />
+<hkern u1="&#x2018;" u2="u" k="10" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="30" />
+<hkern u1="&#x2018;" u2="&#x131;" k="10" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="40" />
+<hkern u1="&#x2018;" u2="&#x153;" k="30" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-30" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="40" />
+<hkern u1="&#x2019;" u2="&#x160;" k="10" />
+<hkern u1="&#x2019;" u2="S" k="10" />
+<hkern u1="&#x2019;" u2="C" k="40" />
+<hkern u1="&#x2019;" u2="G" k="40" />
+<hkern u1="&#x2019;" u2="J" k="120" />
+<hkern u1="&#x2019;" u2="O" k="40" />
+<hkern u1="&#x2019;" u2="Q" k="40" />
+<hkern u1="&#x2019;" u2="a" k="50" />
+<hkern u1="&#x2019;" u2="c" k="50" />
+<hkern u1="&#x2019;" u2="d" k="50" />
+<hkern u1="&#x2019;" u2="e" k="50" />
+<hkern u1="&#x2019;" u2="o" k="50" />
+<hkern u1="&#x2019;" u2="q" k="50" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2019;" u2="&#x152;" k="40" />
+<hkern u1="&#x2019;" u2="&#x153;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="40" />
+<hkern u1="&#x178;" u2="&#x160;" k="-10" />
+<hkern u1="&#x178;" u2="S" k="-10" />
+<hkern u1="&#x178;" u2="&#x161;" k="50" />
+<hkern u1="&#x178;" u2="s" k="50" />
+<hkern u1="&#x178;" u2="y" k="20" />
+<hkern u1="&#x178;" u2="z" k="30" />
+<hkern u1="&#x178;" u2="&amp;" k="30" />
+<hkern u1="&#x178;" u2=")" k="-50" />
+<hkern u1="&#x178;" u2="&#x2c;" k="50" />
+<hkern u1="&#x178;" u2="-" k="50" />
+<hkern u1="&#x178;" u2="." k="50" />
+<hkern u1="&#x178;" u2=":" k="30" />
+<hkern u1="&#x178;" u2=";" k="30" />
+<hkern u1="&#x178;" u2="@" k="30" />
+<hkern u1="&#x178;" u2="A" k="50" />
+<hkern u1="&#x178;" u2="C" k="25" />
+<hkern u1="&#x178;" u2="G" k="25" />
+<hkern u1="&#x178;" u2="J" k="70" />
+<hkern u1="&#x178;" u2="O" k="25" />
+<hkern u1="&#x178;" u2="Q" k="25" />
+<hkern u1="&#x178;" u2="T" k="-20" />
+<hkern u1="&#x178;" u2="V" k="-30" />
+<hkern u1="&#x178;" u2="X" k="-20" />
+<hkern u1="&#x178;" u2="]" k="-50" />
+<hkern u1="&#x178;" u2="a" k="55" />
+<hkern u1="&#x178;" u2="c" k="55" />
+<hkern u1="&#x178;" u2="d" k="55" />
+<hkern u1="&#x178;" u2="e" k="55" />
+<hkern u1="&#x178;" u2="f" k="10" />
+<hkern u1="&#x178;" u2="g" k="40" />
+<hkern u1="&#x178;" u2="m" k="40" />
+<hkern u1="&#x178;" u2="n" k="40" />
+<hkern u1="&#x178;" u2="o" k="55" />
+<hkern u1="&#x178;" u2="p" k="40" />
+<hkern u1="&#x178;" u2="q" k="55" />
+<hkern u1="&#x178;" u2="r" k="40" />
+<hkern u1="&#x178;" u2="t" k="20" />
+<hkern u1="&#x178;" u2="u" k="40" />
+<hkern u1="&#x178;" u2="v" k="20" />
+<hkern u1="&#x178;" u2="w" k="10" />
+<hkern u1="&#x178;" u2="x" k="20" />
+<hkern u1="&#x178;" u2="}" k="-50" />
+<hkern u1="&#x178;" u2="&#xc4;" k="50" />
+<hkern u1="&#x178;" u2="&#xc5;" k="50" />
+<hkern u1="&#x178;" u2="&#xd6;" k="25" />
+<hkern u1="&#x178;" u2="&#xe7;" k="55" />
+<hkern u1="&#x178;" u2="&#x131;" k="40" />
+<hkern u1="&#x178;" u2="&#xae;" k="30" />
+<hkern u1="&#x178;" u2="&#xa9;" k="30" />
+<hkern u1="&#x178;" u2="&#xc6;" k="50" />
+<hkern u1="&#x178;" u2="&#xd8;" k="25" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="30" />
+<hkern u1="&#x178;" u2="&#xe6;" k="55" />
+<hkern u1="&#x178;" u2="&#xab;" k="60" />
+<hkern u1="&#x178;" u2="&#xbb;" k="40" />
+<hkern u1="&#x178;" u2="&#x2026;" k="50" />
+<hkern u1="&#x178;" u2="&#xc0;" k="50" />
+<hkern u1="&#x178;" u2="&#xc3;" k="50" />
+<hkern u1="&#x178;" u2="&#xd5;" k="25" />
+<hkern u1="&#x178;" u2="&#x152;" k="25" />
+<hkern u1="&#x178;" u2="&#x153;" k="55" />
+<hkern u1="&#x178;" u2="&#x2013;" k="50" />
+<hkern u1="&#x178;" u2="&#x2014;" k="50" />
+<hkern u1="&#x178;" u2="&#x2039;" k="60" />
+<hkern u1="&#x178;" u2="&#x203a;" k="40" />
+<hkern u1="&#x178;" u2="&#x201a;" k="50" />
+<hkern u1="&#x178;" u2="&#x201e;" k="50" />
+<hkern u1="&#x178;" u2="&#xc2;" k="50" />
+<hkern u1="&#x178;" u2="&#xc1;" k="50" />
+<hkern u1="&#x178;" u2="&#xd3;" k="25" />
+<hkern u1="&#x178;" u2="&#xd4;" k="25" />
+<hkern u1="&#x178;" u2="&#xd2;" k="25" />
+<hkern u1="&#xa4;" u2="4" k="20" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2039;" u2="Y" k="40" />
+<hkern u1="&#x2039;" u2="T" k="40" />
+<hkern u1="&#x2039;" u2="V" k="20" />
+<hkern u1="&#x2039;" u2="W" k="20" />
+<hkern u1="&#x2039;" u2="&#x178;" k="40" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x203a;" u2="Y" k="50" />
+<hkern u1="&#x203a;" u2="z" k="50" />
+<hkern u1="&#x203a;" u2="T" k="110" />
+<hkern u1="&#x203a;" u2="V" k="50" />
+<hkern u1="&#x203a;" u2="W" k="40" />
+<hkern u1="&#x203a;" u2="x" k="50" />
+<hkern u1="&#x203a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201a;" u2="Y" k="50" />
+<hkern u1="&#x201a;" u2="y" k="30" />
+<hkern u1="&#x201a;" u2="0" k="40" />
+<hkern u1="&#x201a;" u2="1" k="60" />
+<hkern u1="&#x201a;" u2="4" k="50" />
+<hkern u1="&#x201a;" u2="6" k="30" />
+<hkern u1="&#x201a;" u2="8" k="15" />
+<hkern u1="&#x201a;" u2="9" k="10" />
+<hkern u1="&#x201a;" u2="A" k="-30" />
+<hkern u1="&#x201a;" u2="C" k="40" />
+<hkern u1="&#x201a;" u2="G" k="40" />
+<hkern u1="&#x201a;" u2="O" k="40" />
+<hkern u1="&#x201a;" u2="Q" k="40" />
+<hkern u1="&#x201a;" u2="T" k="60" />
+<hkern u1="&#x201a;" u2="U" k="10" />
+<hkern u1="&#x201a;" u2="V" k="60" />
+<hkern u1="&#x201a;" u2="W" k="20" />
+<hkern u1="&#x201a;" u2="a" k="20" />
+<hkern u1="&#x201a;" u2="c" k="20" />
+<hkern u1="&#x201a;" u2="d" k="20" />
+<hkern u1="&#x201a;" u2="e" k="20" />
+<hkern u1="&#x201a;" u2="j" k="-30" />
+<hkern u1="&#x201a;" u2="m" k="20" />
+<hkern u1="&#x201a;" u2="n" k="20" />
+<hkern u1="&#x201a;" u2="o" k="20" />
+<hkern u1="&#x201a;" u2="p" k="20" />
+<hkern u1="&#x201a;" u2="q" k="20" />
+<hkern u1="&#x201a;" u2="r" k="20" />
+<hkern u1="&#x201a;" u2="t" k="40" />
+<hkern u1="&#x201a;" u2="u" k="20" />
+<hkern u1="&#x201a;" u2="v" k="30" />
+<hkern u1="&#x201a;" u2="w" k="10" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201a;" u2="&#x131;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201a;" u2="&#x152;" k="40" />
+<hkern u1="&#x201a;" u2="&#x153;" k="20" />
+<hkern u1="&#x201a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="40" />
+<hkern u1="&#x201a;" u2="&#xda;" k="10" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201e;" u2="Y" k="50" />
+<hkern u1="&#x201e;" u2="y" k="30" />
+<hkern u1="&#x201e;" u2="0" k="40" />
+<hkern u1="&#x201e;" u2="1" k="60" />
+<hkern u1="&#x201e;" u2="4" k="50" />
+<hkern u1="&#x201e;" u2="6" k="30" />
+<hkern u1="&#x201e;" u2="8" k="15" />
+<hkern u1="&#x201e;" u2="9" k="10" />
+<hkern u1="&#x201e;" u2="A" k="-30" />
+<hkern u1="&#x201e;" u2="C" k="40" />
+<hkern u1="&#x201e;" u2="G" k="40" />
+<hkern u1="&#x201e;" u2="O" k="40" />
+<hkern u1="&#x201e;" u2="Q" k="40" />
+<hkern u1="&#x201e;" u2="T" k="60" />
+<hkern u1="&#x201e;" u2="U" k="10" />
+<hkern u1="&#x201e;" u2="V" k="60" />
+<hkern u1="&#x201e;" u2="W" k="20" />
+<hkern u1="&#x201e;" u2="a" k="20" />
+<hkern u1="&#x201e;" u2="c" k="20" />
+<hkern u1="&#x201e;" u2="d" k="20" />
+<hkern u1="&#x201e;" u2="e" k="20" />
+<hkern u1="&#x201e;" u2="j" k="-30" />
+<hkern u1="&#x201e;" u2="m" k="20" />
+<hkern u1="&#x201e;" u2="n" k="20" />
+<hkern u1="&#x201e;" u2="o" k="20" />
+<hkern u1="&#x201e;" u2="p" k="20" />
+<hkern u1="&#x201e;" u2="q" k="20" />
+<hkern u1="&#x201e;" u2="r" k="20" />
+<hkern u1="&#x201e;" u2="t" k="40" />
+<hkern u1="&#x201e;" u2="u" k="20" />
+<hkern u1="&#x201e;" u2="v" k="30" />
+<hkern u1="&#x201e;" u2="w" k="10" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201e;" u2="&#x131;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201e;" u2="&#x152;" k="40" />
+<hkern u1="&#x201e;" u2="&#x153;" k="20" />
+<hkern u1="&#x201e;" u2="&#x178;" k="50" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="40" />
+<hkern u1="&#x201e;" u2="&#xda;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="10" />
+<hkern u1="&#xc2;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc2;" u2="S" k="-5" />
+<hkern u1="&#xc2;" u2="&#xdd;" k="50" />
+<hkern u1="&#xc2;" u2="Y" k="50" />
+<hkern u1="&#xc2;" u2="y" k="30" />
+<hkern u1="&#xc2;" u2="z" k="-10" />
+<hkern u1="&#xc2;" u2="*" k="60" />
+<hkern u1="&#xc2;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc2;" u2="-" k="-10" />
+<hkern u1="&#xc2;" u2="." k="-30" />
+<hkern u1="&#xc2;" u2="@" k="10" />
+<hkern u1="&#xc2;" u2="A" k="-10" />
+<hkern u1="&#xc2;" u2="C" k="20" />
+<hkern u1="&#xc2;" u2="G" k="20" />
+<hkern u1="&#xc2;" u2="J" k="-10" />
+<hkern u1="&#xc2;" u2="O" k="20" />
+<hkern u1="&#xc2;" u2="Q" k="20" />
+<hkern u1="&#xc2;" u2="T" k="70" />
+<hkern u1="&#xc2;" u2="V" k="50" />
+<hkern u1="&#xc2;" u2="W" k="50" />
+<hkern u1="&#xc2;" u2="X" k="-10" />
+<hkern u1="&#xc2;" u2="a" k="5" />
+<hkern u1="&#xc2;" u2="c" k="5" />
+<hkern u1="&#xc2;" u2="d" k="5" />
+<hkern u1="&#xc2;" u2="e" k="5" />
+<hkern u1="&#xc2;" u2="m" k="5" />
+<hkern u1="&#xc2;" u2="n" k="5" />
+<hkern u1="&#xc2;" u2="o" k="5" />
+<hkern u1="&#xc2;" u2="p" k="5" />
+<hkern u1="&#xc2;" u2="q" k="5" />
+<hkern u1="&#xc2;" u2="r" k="5" />
+<hkern u1="&#xc2;" u2="t" k="5" />
+<hkern u1="&#xc2;" u2="u" k="5" />
+<hkern u1="&#xc2;" u2="v" k="30" />
+<hkern u1="&#xc2;" u2="w" k="10" />
+<hkern u1="&#xc2;" u2="x" k="-20" />
+<hkern u1="&#xc2;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc2;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc2;" u2="&#x131;" k="5" />
+<hkern u1="&#xc2;" u2="&#xae;" k="10" />
+<hkern u1="&#xc2;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc2;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc2;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc2;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc2;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc2;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc2;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc2;" u2="&#x152;" k="20" />
+<hkern u1="&#xc2;" u2="&#x153;" k="5" />
+<hkern u1="&#xc2;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc2;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc2;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc2;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc2;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc2;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc2;" u2="&#x178;" k="50" />
+<hkern u1="&#xc2;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc2;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc2;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="20" />
+<hkern u1="&#xca;" u2="y" k="20" />
+<hkern u1="&#xca;" u2="@" k="20" />
+<hkern u1="&#xca;" u2="T" k="-15" />
+<hkern u1="&#xca;" u2="a" k="25" />
+<hkern u1="&#xca;" u2="c" k="25" />
+<hkern u1="&#xca;" u2="d" k="25" />
+<hkern u1="&#xca;" u2="e" k="25" />
+<hkern u1="&#xca;" u2="f" k="10" />
+<hkern u1="&#xca;" u2="g" k="5" />
+<hkern u1="&#xca;" u2="o" k="25" />
+<hkern u1="&#xca;" u2="q" k="25" />
+<hkern u1="&#xca;" u2="v" k="20" />
+<hkern u1="&#xca;" u2="&#xe7;" k="25" />
+<hkern u1="&#xca;" u2="&#xae;" k="20" />
+<hkern u1="&#xca;" u2="&#xa9;" k="20" />
+<hkern u1="&#xca;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xca;" u2="&#xe6;" k="25" />
+<hkern u1="&#xca;" u2="&#x153;" k="25" />
+<hkern u1="&#xc1;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc1;" u2="S" k="-5" />
+<hkern u1="&#xc1;" u2="&#xdd;" k="50" />
+<hkern u1="&#xc1;" u2="Y" k="50" />
+<hkern u1="&#xc1;" u2="y" k="30" />
+<hkern u1="&#xc1;" u2="z" k="-10" />
+<hkern u1="&#xc1;" u2="*" k="60" />
+<hkern u1="&#xc1;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc1;" u2="-" k="-10" />
+<hkern u1="&#xc1;" u2="." k="-30" />
+<hkern u1="&#xc1;" u2="@" k="10" />
+<hkern u1="&#xc1;" u2="A" k="-10" />
+<hkern u1="&#xc1;" u2="C" k="20" />
+<hkern u1="&#xc1;" u2="G" k="20" />
+<hkern u1="&#xc1;" u2="J" k="-10" />
+<hkern u1="&#xc1;" u2="O" k="20" />
+<hkern u1="&#xc1;" u2="Q" k="20" />
+<hkern u1="&#xc1;" u2="T" k="70" />
+<hkern u1="&#xc1;" u2="V" k="50" />
+<hkern u1="&#xc1;" u2="W" k="50" />
+<hkern u1="&#xc1;" u2="X" k="-10" />
+<hkern u1="&#xc1;" u2="a" k="5" />
+<hkern u1="&#xc1;" u2="c" k="5" />
+<hkern u1="&#xc1;" u2="d" k="5" />
+<hkern u1="&#xc1;" u2="e" k="5" />
+<hkern u1="&#xc1;" u2="m" k="5" />
+<hkern u1="&#xc1;" u2="n" k="5" />
+<hkern u1="&#xc1;" u2="o" k="5" />
+<hkern u1="&#xc1;" u2="p" k="5" />
+<hkern u1="&#xc1;" u2="q" k="5" />
+<hkern u1="&#xc1;" u2="r" k="5" />
+<hkern u1="&#xc1;" u2="t" k="5" />
+<hkern u1="&#xc1;" u2="u" k="5" />
+<hkern u1="&#xc1;" u2="v" k="30" />
+<hkern u1="&#xc1;" u2="w" k="10" />
+<hkern u1="&#xc1;" u2="x" k="-20" />
+<hkern u1="&#xc1;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc1;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc1;" u2="&#x131;" k="5" />
+<hkern u1="&#xc1;" u2="&#xae;" k="10" />
+<hkern u1="&#xc1;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc1;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc1;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc1;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc1;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc1;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc1;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc1;" u2="&#x152;" k="20" />
+<hkern u1="&#xc1;" u2="&#x153;" k="5" />
+<hkern u1="&#xc1;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc1;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc1;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc1;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc1;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc1;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc1;" u2="&#x178;" k="50" />
+<hkern u1="&#xc1;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc1;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc1;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="20" />
+<hkern u1="&#xcb;" u2="y" k="20" />
+<hkern u1="&#xcb;" u2="@" k="20" />
+<hkern u1="&#xcb;" u2="T" k="-15" />
+<hkern u1="&#xcb;" u2="a" k="25" />
+<hkern u1="&#xcb;" u2="c" k="25" />
+<hkern u1="&#xcb;" u2="d" k="25" />
+<hkern u1="&#xcb;" u2="e" k="25" />
+<hkern u1="&#xcb;" u2="f" k="10" />
+<hkern u1="&#xcb;" u2="g" k="5" />
+<hkern u1="&#xcb;" u2="o" k="25" />
+<hkern u1="&#xcb;" u2="q" k="25" />
+<hkern u1="&#xcb;" u2="v" k="20" />
+<hkern u1="&#xcb;" u2="&#xe7;" k="25" />
+<hkern u1="&#xcb;" u2="&#xae;" k="20" />
+<hkern u1="&#xcb;" u2="&#xa9;" k="20" />
+<hkern u1="&#xcb;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xcb;" u2="&#xe6;" k="25" />
+<hkern u1="&#xcb;" u2="&#x153;" k="25" />
+<hkern u1="&#xc8;" u2="y" k="20" />
+<hkern u1="&#xc8;" u2="@" k="20" />
+<hkern u1="&#xc8;" u2="T" k="-15" />
+<hkern u1="&#xc8;" u2="a" k="25" />
+<hkern u1="&#xc8;" u2="c" k="25" />
+<hkern u1="&#xc8;" u2="d" k="25" />
+<hkern u1="&#xc8;" u2="e" k="25" />
+<hkern u1="&#xc8;" u2="f" k="10" />
+<hkern u1="&#xc8;" u2="g" k="5" />
+<hkern u1="&#xc8;" u2="o" k="25" />
+<hkern u1="&#xc8;" u2="q" k="25" />
+<hkern u1="&#xc8;" u2="v" k="20" />
+<hkern u1="&#xc8;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc8;" u2="&#xae;" k="20" />
+<hkern u1="&#xc8;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc8;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc8;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc8;" u2="&#x153;" k="25" />
+<hkern u1="&#xcd;" u2="y" k="10" />
+<hkern u1="&#xcd;" u2="/" k="20" />
+<hkern u1="&#xcd;" u2="v" k="10" />
+<hkern u1="&#xce;" u2="y" k="10" />
+<hkern u1="&#xce;" u2="/" k="20" />
+<hkern u1="&#xce;" u2="v" k="10" />
+<hkern u1="&#xcf;" u2="y" k="10" />
+<hkern u1="&#xcf;" u2="/" k="20" />
+<hkern u1="&#xcf;" u2="v" k="10" />
+<hkern u1="&#xcc;" u2="y" k="10" />
+<hkern u1="&#xcc;" u2="/" k="20" />
+<hkern u1="&#xcc;" u2="v" k="10" />
+<hkern u1="&#xd3;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd3;" u2="Y" k="25" />
+<hkern u1="&#xd3;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd3;" u2="Z" k="45" />
+<hkern u1="&#xd3;" u2="z" k="10" />
+<hkern u1="&#xd3;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd3;" u2="." k="40" />
+<hkern u1="&#xd3;" u2="/" k="50" />
+<hkern u1="&#xd3;" u2="?" k="20" />
+<hkern u1="&#xd3;" u2="A" k="20" />
+<hkern u1="&#xd3;" u2="J" k="45" />
+<hkern u1="&#xd3;" u2="T" k="30" />
+<hkern u1="&#xd3;" u2="V" k="20" />
+<hkern u1="&#xd3;" u2="W" k="40" />
+<hkern u1="&#xd3;" u2="X" k="25" />
+<hkern u1="&#xd3;" u2="a" k="5" />
+<hkern u1="&#xd3;" u2="b" k="10" />
+<hkern u1="&#xd3;" u2="c" k="5" />
+<hkern u1="&#xd3;" u2="d" k="5" />
+<hkern u1="&#xd3;" u2="e" k="5" />
+<hkern u1="&#xd3;" u2="h" k="10" />
+<hkern u1="&#xd3;" u2="k" k="10" />
+<hkern u1="&#xd3;" u2="l" k="10" />
+<hkern u1="&#xd3;" u2="m" k="5" />
+<hkern u1="&#xd3;" u2="n" k="5" />
+<hkern u1="&#xd3;" u2="o" k="5" />
+<hkern u1="&#xd3;" u2="p" k="5" />
+<hkern u1="&#xd3;" u2="q" k="5" />
+<hkern u1="&#xd3;" u2="r" k="5" />
+<hkern u1="&#xd3;" u2="u" k="5" />
+<hkern u1="&#xd3;" u2="x" k="10" />
+<hkern u1="&#xd3;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd3;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd3;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd3;" u2="&#x131;" k="5" />
+<hkern u1="&#xd3;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd3;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd3;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd3;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd3;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd3;" u2="&#x153;" k="5" />
+<hkern u1="&#xd3;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd3;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd3;" u2="&#x178;" k="25" />
+<hkern u1="&#xd3;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd3;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd3;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd3;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd4;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd4;" u2="Y" k="25" />
+<hkern u1="&#xd4;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd4;" u2="Z" k="45" />
+<hkern u1="&#xd4;" u2="z" k="10" />
+<hkern u1="&#xd4;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd4;" u2="." k="40" />
+<hkern u1="&#xd4;" u2="/" k="50" />
+<hkern u1="&#xd4;" u2="?" k="20" />
+<hkern u1="&#xd4;" u2="A" k="20" />
+<hkern u1="&#xd4;" u2="J" k="45" />
+<hkern u1="&#xd4;" u2="T" k="30" />
+<hkern u1="&#xd4;" u2="V" k="20" />
+<hkern u1="&#xd4;" u2="W" k="40" />
+<hkern u1="&#xd4;" u2="X" k="25" />
+<hkern u1="&#xd4;" u2="a" k="5" />
+<hkern u1="&#xd4;" u2="b" k="10" />
+<hkern u1="&#xd4;" u2="c" k="5" />
+<hkern u1="&#xd4;" u2="d" k="5" />
+<hkern u1="&#xd4;" u2="e" k="5" />
+<hkern u1="&#xd4;" u2="h" k="10" />
+<hkern u1="&#xd4;" u2="k" k="10" />
+<hkern u1="&#xd4;" u2="l" k="10" />
+<hkern u1="&#xd4;" u2="m" k="5" />
+<hkern u1="&#xd4;" u2="n" k="5" />
+<hkern u1="&#xd4;" u2="o" k="5" />
+<hkern u1="&#xd4;" u2="p" k="5" />
+<hkern u1="&#xd4;" u2="q" k="5" />
+<hkern u1="&#xd4;" u2="r" k="5" />
+<hkern u1="&#xd4;" u2="u" k="5" />
+<hkern u1="&#xd4;" u2="x" k="10" />
+<hkern u1="&#xd4;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd4;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd4;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd4;" u2="&#x131;" k="5" />
+<hkern u1="&#xd4;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd4;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd4;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd4;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd4;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd4;" u2="&#x153;" k="5" />
+<hkern u1="&#xd4;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd4;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd4;" u2="&#x178;" k="25" />
+<hkern u1="&#xd4;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd4;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd4;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd4;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd2;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd2;" u2="Y" k="25" />
+<hkern u1="&#xd2;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd2;" u2="Z" k="45" />
+<hkern u1="&#xd2;" u2="z" k="10" />
+<hkern u1="&#xd2;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd2;" u2="." k="40" />
+<hkern u1="&#xd2;" u2="/" k="50" />
+<hkern u1="&#xd2;" u2="?" k="20" />
+<hkern u1="&#xd2;" u2="A" k="20" />
+<hkern u1="&#xd2;" u2="J" k="45" />
+<hkern u1="&#xd2;" u2="T" k="30" />
+<hkern u1="&#xd2;" u2="V" k="20" />
+<hkern u1="&#xd2;" u2="W" k="40" />
+<hkern u1="&#xd2;" u2="X" k="25" />
+<hkern u1="&#xd2;" u2="a" k="5" />
+<hkern u1="&#xd2;" u2="b" k="10" />
+<hkern u1="&#xd2;" u2="c" k="5" />
+<hkern u1="&#xd2;" u2="d" k="5" />
+<hkern u1="&#xd2;" u2="e" k="5" />
+<hkern u1="&#xd2;" u2="h" k="10" />
+<hkern u1="&#xd2;" u2="k" k="10" />
+<hkern u1="&#xd2;" u2="l" k="10" />
+<hkern u1="&#xd2;" u2="m" k="5" />
+<hkern u1="&#xd2;" u2="n" k="5" />
+<hkern u1="&#xd2;" u2="o" k="5" />
+<hkern u1="&#xd2;" u2="p" k="5" />
+<hkern u1="&#xd2;" u2="q" k="5" />
+<hkern u1="&#xd2;" u2="r" k="5" />
+<hkern u1="&#xd2;" u2="u" k="5" />
+<hkern u1="&#xd2;" u2="x" k="10" />
+<hkern u1="&#xd2;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd2;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd2;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd2;" u2="&#x131;" k="5" />
+<hkern u1="&#xd2;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd2;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd2;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd2;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd2;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd2;" u2="&#x153;" k="5" />
+<hkern u1="&#xd2;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd2;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd2;" u2="&#x178;" k="25" />
+<hkern u1="&#xd2;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd2;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd2;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd2;" u2="&#xc1;" k="20" />
+<hkern u1="&#xda;" u2="&#x2c;" k="10" />
+<hkern u1="&#xda;" u2="." k="10" />
+<hkern u1="&#xda;" u2="J" k="10" />
+<hkern u1="&#xda;" u2="&#x2026;" k="10" />
+<hkern u1="&#xda;" u2="&#x201a;" k="10" />
+<hkern u1="&#xda;" u2="&#x201e;" k="10" />
+<hkern u1="&#xdb;" u2="&#x2c;" k="10" />
+<hkern u1="&#xdb;" u2="." k="10" />
+<hkern u1="&#xdb;" u2="J" k="10" />
+<hkern u1="&#xdb;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdb;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdb;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd9;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd9;" u2="." k="10" />
+<hkern u1="&#xd9;" u2="J" k="10" />
+<hkern u1="&#xd9;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd9;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd9;" u2="&#x201e;" k="10" />
+<hkern u1="&#xad;" u2="&#xdd;" k="50" />
+<hkern u1="&#xad;" u2="Y" k="50" />
+<hkern u1="&#xad;" u2="y" k="10" />
+<hkern u1="&#xad;" u2="&#x17d;" k="10" />
+<hkern u1="&#xad;" u2="Z" k="10" />
+<hkern u1="&#xad;" u2="z" k="30" />
+<hkern u1="&#xad;" u2="1" k="20" />
+<hkern u1="&#xad;" u2="7" k="30" />
+<hkern u1="&#xad;" u2="A" k="-10" />
+<hkern u1="&#xad;" u2="T" k="70" />
+<hkern u1="&#xad;" u2="V" k="50" />
+<hkern u1="&#xad;" u2="W" k="40" />
+<hkern u1="&#xad;" u2="X" k="30" />
+<hkern u1="&#xad;" u2="a" k="10" />
+<hkern u1="&#xad;" u2="c" k="10" />
+<hkern u1="&#xad;" u2="d" k="10" />
+<hkern u1="&#xad;" u2="e" k="10" />
+<hkern u1="&#xad;" u2="o" k="10" />
+<hkern u1="&#xad;" u2="q" k="10" />
+<hkern u1="&#xad;" u2="v" k="10" />
+<hkern u1="&#xad;" u2="x" k="30" />
+<hkern u1="&#xad;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe7;" k="10" />
+<hkern u1="&#xad;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe6;" k="10" />
+<hkern u1="&#xad;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xad;" u2="&#x153;" k="10" />
+<hkern u1="&#xad;" u2="&#x178;" k="50" />
+<hkern u1="&#xad;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xd0;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd0;" u2="Y" k="25" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd0;" u2="Z" k="45" />
+<hkern u1="&#xd0;" u2="z" k="10" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd0;" u2="." k="40" />
+<hkern u1="&#xd0;" u2="/" k="50" />
+<hkern u1="&#xd0;" u2="?" k="20" />
+<hkern u1="&#xd0;" u2="A" k="20" />
+<hkern u1="&#xd0;" u2="J" k="45" />
+<hkern u1="&#xd0;" u2="T" k="30" />
+<hkern u1="&#xd0;" u2="V" k="20" />
+<hkern u1="&#xd0;" u2="W" k="40" />
+<hkern u1="&#xd0;" u2="X" k="25" />
+<hkern u1="&#xd0;" u2="a" k="5" />
+<hkern u1="&#xd0;" u2="b" k="10" />
+<hkern u1="&#xd0;" u2="c" k="5" />
+<hkern u1="&#xd0;" u2="d" k="5" />
+<hkern u1="&#xd0;" u2="e" k="5" />
+<hkern u1="&#xd0;" u2="h" k="10" />
+<hkern u1="&#xd0;" u2="k" k="10" />
+<hkern u1="&#xd0;" u2="l" k="10" />
+<hkern u1="&#xd0;" u2="m" k="5" />
+<hkern u1="&#xd0;" u2="n" k="5" />
+<hkern u1="&#xd0;" u2="o" k="5" />
+<hkern u1="&#xd0;" u2="p" k="5" />
+<hkern u1="&#xd0;" u2="q" k="5" />
+<hkern u1="&#xd0;" u2="r" k="5" />
+<hkern u1="&#xd0;" u2="u" k="5" />
+<hkern u1="&#xd0;" u2="x" k="10" />
+<hkern u1="&#xd0;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd0;" u2="&#x131;" k="5" />
+<hkern u1="&#xd0;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd0;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd0;" u2="&#x153;" k="5" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd0;" u2="&#x178;" k="25" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd0;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc1;" k="20" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.ttf
new file mode 100644
index 0000000000..2342be4be5
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.woff
new file mode 100644
index 0000000000..0acf1f027f
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.eot
new file mode 100644
index 0000000000..0e5892d1e0
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.svg
new file mode 100644
index 0000000000..9670981b29
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.svg
@@ -0,0 +1,4365 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Bold]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated in 2010 by FontLab Studio. Copyright info pending.]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="569" id="Omnes_ATTW02Bold">
+<font-face font-family="Omnes_ATT W02 Bold" panose-1="2 0 8 0 0 0 0 0 0 0" ascent="909" descent="-233" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="500" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="170" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="297" d="M138 216Q119 216 108 230T90 276L24 647H274L208 276Q200 244 189 230T157 216H138ZM136 -8Q55 -8 55 73V89Q55 170 136 170H159Q242 170 242 89V73Q242 -8 159 -8H136Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="479" d="M339 330Q300 330 294 374L258 647H441L405 374Q398 330 360 330H339ZM118 330Q79 330 73 374L37 647H220L184 374Q178 330 139 330H118Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="698" d="M13 267H153L189 380H92V518H232L275 655Q276 662 293 662Q303 662 317 657T343 643T364 618T373 583Q373 568 367 547L358 518H461L504 655Q507 662 522 662Q533 662 547 657T573 643T594 618T603 583Q603 567 596 547L587 518H684V380H544L508 267H606V129H465L423 -7Q421 -15 405 -15Q394 -15 381 -10T354 5T333 30T324 65Q324 73 326 81T331 101L339 129H236L194 -7Q191 -15 176 -15Q165 -15 151 -10T125 5T104 30T95 65Q95 80 101 101L110 129H13V267ZM278 266H383L419 381H314L278 266Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="574" d="M210 8Q119 18 74 51T28 132Q28 151 37 168T56 198T76 216T88 221Q111 201 146 184T219 158V262Q187 270 154 283T94 318T50 373T32 455Q32 527 82 574T224 638V737H375V641Q456 631 495 601T535 525Q535 504 526 486T505 456T483 436T471 430Q426 470 365 489V387Q397 379 431 366T494 331T541 277T560 198Q560 122 508 75T360 11V-98H210V8ZM222 493Q203 492 192 483T180 456Q180 439 191 429T222 411V493ZM362 151Q402 153 402 190Q402 207 392 216T362 233V151Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="743" d="M148 -13Q146 -16 133 -13T104 -1T77 21T64 50Q64 71 89 99L593 658Q596 661 609 658T638 646T665 625T677 596Q677 573 653 546L148 -13ZM180 335Q100 335 53 377T6 492V499Q6 572 53 614T180 657Q259 657 306 615T353 500V493Q353 420 306 378T180 335ZM180 430Q201 430 215 444T230 488V504Q230 534 216 548T180 563Q157 563 144 549T130 504V488Q130 459 144 445T180 430ZM564 -12Q484 -12 437 30T389 145V152Q389 225 437 267T564 310Q643 310 690 268T737 153V146Q737 73 690 31T564 -12ZM564 83Q585 83 599 97T614 141V157Q614 187 600 201T564 216Q541 216 528 202T514 157V141Q514 111 528 97T564 83Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="711" d="M270 -14Q149 -14 83 36T16 181Q16 244 52 289T154 359Q126 385 110 417T94 484Q94 524 110 556T155 612T226 648T319 661Q364 661 401 650T465 617T505 565T520 499Q520 446 485 407T389 346L477 272Q488 294 498 321T522 390H687Q673 331 647 278T586 178L711 74Q712 73 706 60T688 32T655 4T608 -9Q585 -9 557 2T501 46L484 63Q443 27 391 7T270 -14ZM262 479Q262 457 272 441T301 407Q340 420 356 438T372 482Q372 507 357 523T318 539Q292 539 277 522T262 479ZM285 117Q311 117 336 128T383 158L237 284Q211 270 197 251T182 203Q182 163 210 140T285 117Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="258" d="M118 330Q79 330 73 374L37 647H220L184 374Q178 330 139 330H118H118Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="441" d="M342 -198Q283 -198 227 -165T128 -73T58 63T31 231Q31 319 57 397T127 533T227 625T342 659Q387 659 410 637T434 576Q434 563 430 552T419 533T408 521T400 515Q355 503 322 478T267 415T233 332T222 231Q222 122 266 46T400 -55Q402 -55 407 -59T419 -72T429 -91T434 -115Q434 -154 411 -176T342 -198Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="441" d="M99 -198Q55 -198 31 -176T7 -115Q7 -102 11 -91T22 -72T33 -60T41 -55Q130 -31 174 45T219 231Q219 285 208 331T175 415T119 477T41 515Q39 516 34 520T22 533T12 552T7 576Q7 614 31 636T99 659Q158 659 214 626T313 534T383 397T410 231Q410 141 384 63T314 -73T214 -164T99 -198Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="433" d="M89 284Q72 293 67 309T76 352L113 422L48 455Q22 469 18 481T19 517L20 521Q26 543 38 551T80 554L156 541L167 613Q172 643 184 651T217 660H221Q241 660 254 652T272 613L281 541L356 554Q386 559 399 550T417 521L418 517Q424 495 420 482T389 455L323 421L361 352Q375 325 369 310T347 284L344 282Q329 272 314 271T276 290L218 348L161 290Q139 269 125 270T93 282L89 284Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="535" d="M92 184Q54 184 35 202T16 249V258Q16 285 35 303T92 322H194V429Q194 467 212 486T260 505H275Q303 505 321 486T340 429V322H443Q482 322 500 304T519 258V249Q519 220 501 202T443 184H340V74Q340 35 322 17T275 -2H260Q231 -2 213 16T194 74V184H92H92Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="259" d="M106 -97Q93 -110 75 -112T40 -108T15 -87T9 -53L44 93Q48 113 53 128T68 152T92 167T130 172H151Q186 172 205 154T225 93V85Q225 61 215 39T182 -7L106 -97H106Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="469" d="M37 334H432V159H37V334Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="259" d="M116 -8Q35 -8 35 73V89Q35 170 116 170H139Q183 170 202 151T222 89V73Q222 -8 139 -8H116H116Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="382" d="M81 -217Q66 -217 50 -215T21 -205T-2 -184T-11 -149Q-11 -140 -10 -129T-3 -104L241 655Q242 657 254 659T283 662Q298 662 314 659T343 649T366 628T375 593Q375 585 374 574T367 549L123 -211Q123 -213 111 -215T81 -217Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="653" d="M327 -14Q256 -14 201 7T107 68T48 165T27 293V355Q27 425 47 481T106 578T200 639T327 661Q397 661 453 640T547 579T605 482T626 355V293Q626 222 606 166T547 69T453 8T327 -14ZM327 147Q425 147 425 275V373Q425 503 327 503Q228 503 228 373V276Q228 147 327 147Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="420" d="M164 0V422Q140 411 117 411Q73 411 45 439T16 511Q16 536 23 554T39 583T56 599T68 603Q83 593 98 588T131 583Q163 583 188 602T221 656H273Q361 656 361 563V0H164Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="577" d="M377 255Q318 232 277 212T217 160H552V1H149Q114 1 91 4T54 18T35 47T29 97V135Q29 177 47 210T95 270T159 316T227 351Q255 364 279 374T322 395T350 416T360 443Q360 505 277 505Q252 505 232 497T197 474T173 441T160 404Q160 403 140 403T96 412T51 438T31 493Q31 529 48 560T98 613T179 649T290 662Q419 662 486 607T554 453Q554 406 540 375T500 321T443 283T377 255Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="585" d="M551 480Q551 432 521 399T433 349Q496 329 528 288T561 187Q561 90 487 38T273 -14Q213 -14 165 -2T83 30T31 78T13 137Q13 174 31 194T71 222T111 231T131 228Q144 182 180 159T271 136Q319 136 345 154T372 203Q372 262 296 262H191V403H309Q335 403 350 416T366 452Q366 479 340 495T268 511Q236 511 214 503T176 483T153 455T140 425Q140 422 122 422T83 429T43 457T25 515Q25 546 42 573T91 619T169 650T272 661Q409 661 480 615T551 480Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="630" d="M328 0V141H120Q70 141 42 164T14 222V250Q15 273 22 293T46 342L206 590Q218 611 231 624T261 644T304 653T367 656Q443 656 476 628T510 526V286H612V141H510V0H328ZM184 282H336V522L184 282Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="566" d="M197 393Q220 401 245 405T297 409Q354 409 399 395T477 355T527 290T545 203Q545 153 526 113T471 45T384 2T268 -14Q206 -14 159 -4T79 25T31 65T14 112Q14 132 24 149T48 179T74 198T88 203Q123 166 168 147T263 128Q315 128 343 148T372 206Q372 283 263 283Q221 283 194 272T150 249Q94 250 64 275T34 361V536Q34 590 55 618T128 647H516V489H197V393Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="622" d="M357 505Q289 505 258 471T224 362Q250 385 291 400T388 416Q484 416 543 368T602 224V219Q602 167 582 124T526 51T438 3T326 -14Q256 -14 201 8T107 70T48 168T27 297V345Q27 418 48 476T110 576T209 639T340 661Q455 661 516 623T577 522Q577 493 563 476T531 451T498 440T482 439Q468 463 438 484T357 505ZM323 283Q296 283 272 274T228 247Q231 192 256 163T324 133Q361 133 386 152T411 207V210Q411 245 388 264T323 283Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="562" d="M198 -9Q174 -9 152 0T115 21T91 44T86 60L330 486H18V647H409Q454 647 480 639T521 616T539 583T544 544V533Q544 513 537 492T521 451T500 413T480 380L300 61Q277 22 251 7T198 -9Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="621" d="M309 -12Q172 -12 98 38T23 179Q23 292 148 332Q93 352 68 387T42 475Q42 517 61 552T116 612T202 650T312 664Q373 664 421 651T504 614T556 554T575 476Q575 373 471 333Q598 295 598 179Q598 135 578 100T521 39T430 1T309 -12ZM311 387Q348 387 372 406T396 455Q396 485 373 504T310 524Q272 524 249 505T225 455Q225 425 249 406T311 387ZM311 126Q355 126 379 146T404 199Q404 233 379 253T310 274Q267 274 242 254T217 199Q217 167 242 147T311 126Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="622" d="M260 141Q331 141 363 175T398 286Q372 263 330 247T234 231Q138 231 79 279T20 423V426Q20 478 40 521T96 596T183 644T296 661Q364 661 419 640T514 579T574 482T595 351V296Q595 144 513 65T277 -15Q160 -15 102 17T44 104Q44 133 52 152T72 181T92 195T104 198Q132 174 170 158T260 141ZM299 364Q325 364 349 373T393 400Q391 455 366 484T297 514Q260 514 235 493T210 438V437Q210 402 233 383T299 364Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="266" d="M120 274Q39 274 39 355V372Q39 453 120 453H143Q226 453 226 372V355Q226 274 143 274H120ZM120 -8Q39 -8 39 73V89Q39 170 120 170H143Q226 170 226 89V73Q226 -8 143 -8H120Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="266" d="M120 274Q39 274 39 355V372Q39 453 120 453H143Q226 453 226 372V355Q226 274 143 274H120ZM110 -97Q96 -110 78 -112T44 -108T19 -87T13 -53L47 93Q51 113 57 128T72 152T96 167T134 172H154Q190 172 209 154T229 93V85Q229 61 219 39T186 -7L110 -97Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="453" d="M225 255L350 193Q392 173 407 148T422 98Q422 80 416 64T402 35T387 15T376 10L87 166Q65 178 48 199T31 253Q31 285 48 307T87 342L376 500Q379 501 386 495T401 476T415 447T421 412Q422 387 407 362T350 316L225 255Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="535" d="M109 300Q71 300 52 321T33 372V388Q33 416 52 438T109 460H426Q464 460 483 438T502 388V372Q502 343 483 322T426 300H109ZM109 48Q71 48 52 70T33 121V137Q33 164 52 186T109 209H426Q464 209 483 187T502 137V121Q502 92 483 70T426 48H109Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="453" d="M31 98Q31 122 46 147T103 193L228 255L103 316Q61 336 46 361T32 412Q32 430 38 446T51 475T66 494T77 500L366 342Q388 330 405 308T422 253Q422 221 405 200T366 166L77 10Q74 8 67 15T52 34T38 63T31 98Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="522" d="M162 214Q159 256 166 287T187 342T220 381T263 406Q289 417 307 427T326 458Q326 481 308 494T257 508Q230 508 210 500T175 477T153 444T141 407Q141 406 121 406T78 413T34 439T14 496Q14 528 30 558T77 611T154 647T259 661Q377 661 442 610T507 471Q507 436 498 411T473 369T440 339T402 317Q387 309 373 301T346 282T324 254T312 214H162ZM230 -8Q149 -8 149 73V89Q149 170 230 170H253Q336 170 336 89V73Q336 -8 253 -8H230Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="736" d="M323 166Q291 166 267 178T226 210T201 257T192 316Q192 350 202 377T229 425T270 456T322 467Q348 467 368 456T404 427V462H519V304Q519 267 531 256T565 244Q577 244 590 250T613 270T629 308T636 365Q636 410 621 451T574 525T492 577T373 596Q313 596 263 576T178 519T122 433T102 324Q102 265 121 215T176 129T262 72T372 51Q430 51 475 69T552 117Q568 132 585 132Q595 132 603 126T617 112T624 98T626 90Q582 42 518 14T372 -14Q296 -14 234 11T126 80T57 187T32 324Q32 397 57 459T127 566T235 636T373 661Q455 661 517 638T621 574T684 481T705 367Q705 270 660 219T526 168Q484 168 456 184T415 228Q404 200 380 183T323 166ZM356 257Q377 257 390 269T404 302V348Q388 376 357 376Q332 376 318 360T303 316Q303 289 318 273T356 257Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="660" d="M46 536Q46 590 72 618T148 647H380Q495 647 554 605T614 485Q614 428 584 396T496 350Q569 333 603 290T637 187Q637 138 621 103T571 45T484 11T356 0H46V536ZM320 386Q408 386 408 448Q408 505 323 505H243V386H320ZM332 143Q385 143 408 158T431 209Q431 241 408 255T330 270H243V143H332Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="650" d="M368 -14Q292 -14 230 11T123 81T54 188T29 324Q29 397 53 459T123 566T230 636T369 661Q438 661 486 645T564 605T609 554T623 503Q623 471 606 451T567 420T527 406T508 405Q489 440 456 459T380 478Q349 478 323 467T277 436T246 388T235 324Q235 289 246 261T276 212T322 180T380 169Q427 169 460 189T510 247Q511 249 529 246T569 232T608 201T626 149Q626 125 612 97T567 44T487 3T368 -14Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="690" d="M46 536Q46 590 75 618T150 647H321Q401 647 464 625T572 561T641 460T665 327Q665 252 641 192T571 89T462 23T317 0H46V536ZM312 170Q383 170 425 210T467 326Q467 399 426 438T315 477H244V170H312Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="571" d="M46 0V536Q46 590 72 618T148 647H549V481H243V373H479V212H250V0H46Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="728" d="M624 -5Q594 -5 566 10T525 71Q499 33 452 11T337 -11Q267 -11 210 14T113 83T51 189T29 324Q29 397 54 459T126 566T238 636T383 661Q444 661 491 650T572 618T622 571T639 512Q639 485 624 466T591 437T557 422T539 421Q513 454 476 472T395 490Q360 490 331 478T279 442T244 387T231 314Q231 274 242 242T274 187T323 152T386 139Q439 139 472 161T513 224H360V371H622Q665 371 678 350T691 286V15Q691 10 671 3T624 -5Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="700" d="M46 559Q46 652 130 652H159Q247 652 247 559V409H454V559Q454 652 538 652H568Q655 652 655 559V0H454V241H247V0H46V559Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="538" d="M229 -14Q124 -14 70 20T15 115Q15 143 25 162T47 194T69 211T81 215Q129 156 199 156Q246 156 266 179T286 260V647H487V257Q487 117 422 52T229 -14Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="652" d="M46 559Q46 652 130 652H159Q246 652 246 559V367L402 584Q428 622 456 637T512 652Q537 652 559 642T596 619T620 594T626 577L445 329L635 69Q638 64 630 52T605 28T565 5T514 -5Q485 -5 458 9T404 62L246 281V0H46V559Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="552" d="M46 559Q46 652 130 652H159Q246 652 246 559V174H443Q535 174 535 91V88Q535 0 443 0H46V559Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="772" d="M46 556Q46 604 69 628T135 652H192Q212 652 226 650T250 641T267 624T281 598L386 339L492 598Q506 631 524 641T578 652H634Q726 652 726 556V0H538V365L470 175Q467 166 464 157T452 141T429 130T385 126Q357 126 342 130T319 140T308 156T301 175L234 357V0H46V556Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="703" d="M46 559Q46 606 68 629T128 652H147Q165 652 177 650T199 641T217 626T237 605L469 303V647H657V88Q657 41 636 18T576 -5H560Q542 -5 530 -3T508 5T490 18T471 39L234 346V0H46V559Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="614" d="M46 536Q46 590 72 618T148 647H319Q459 647 532 588T605 412Q605 296 534 239T325 182H247V0H46V536ZM244 328H317Q363 328 386 349T410 412Q410 453 386 474T314 496H244V328Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="733" d="M619 -14Q599 -14 575 -6T524 25Q489 6 450 -4T368 -14Q293 -14 230 11T122 81T51 188T25 324Q25 397 50 459T122 566T231 636T369 661Q444 661 506 636T613 567T683 460T708 324Q708 273 693 227T651 140L731 52Q732 50 725 40T704 17T668 -4T619 -14ZM369 164Q387 164 402 167L341 234L452 345L513 279Q518 302 518 324Q518 358 508 387T478 439T432 473T372 485Q337 485 310 473T263 439T234 388T223 323Q223 288 233 259T263 209T310 176T369 164Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="650" d="M509 -5Q486 -5 468 0T434 15T404 42T374 80L282 202H246V0H46V536Q46 590 72 618T148 647H348Q484 647 551 593T618 435Q618 364 583 315T478 245L624 64Q626 61 621 50T601 27T564 5T509 -5ZM243 344H327Q372 344 394 363T417 419Q417 495 325 495H243V344Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="588" d="M277 -14Q206 -14 157 -3T77 27T33 69T19 119Q19 137 28 155T50 186T72 208T85 214Q101 200 123 187T170 163T223 146T277 139Q324 139 345 152T367 190Q367 205 358 213T334 227T297 235T251 244Q210 252 172 266T102 303T53 361T34 446Q34 546 105 603T310 661Q374 661 420 651T496 624T540 584T554 538Q554 517 544 498T520 466T496 444T483 438Q451 468 405 488T312 508Q225 508 225 458Q225 444 234 437T259 425T297 417T345 408Q393 397 434 381T505 341T552 284T569 206Q569 100 497 43T277 -14Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="619" d="M102 473Q10 473 10 561V564Q10 647 102 647H517Q610 647 610 564V561Q610 473 517 473H412V0H211V473H102H102Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="674" d="M130 652Q168 652 197 627T241 559L342 237L442 559Q456 608 486 630T549 652Q573 652 596 644T636 625T662 602T668 583L424 0H255L6 583Q3 590 12 601T40 624T81 644T130 652Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="983" d="M30 582Q29 585 36 596T60 619T99 640T155 650Q170 650 187 645T219 628T246 596T265 543L319 264L387 517Q395 555 425 572T497 590Q535 590 565 571T604 517L672 264L726 546Q731 575 742 595T768 627T800 644T835 650Q865 650 887 641T924 620T946 597T952 582L784 0H589L493 338L399 0H205L30 582Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="671" d="M139 -9Q113 -9 90 0T48 23T21 48T14 65L208 336L2 647H223L346 446L424 578Q444 616 473 633T531 650Q557 650 580 641T622 619T649 594T656 577L473 321L656 66Q659 61 649 49T621 24T579 2T528 -8Q498 -8 470 8T418 65L332 208L246 64Q223 22 196 7T139 -9Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="639" d="M221 269L-16 647H203L327 430L414 581Q437 619 460 635T509 651Q543 651 568 641T608 617T629 591T633 575L421 253V0H221V269Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="621" d="M45 114L329 482H132Q88 482 66 499T44 560V569Q44 611 66 629T132 647H580V543L281 166H509Q553 166 575 148T597 87V79Q597 37 575 19T509 0H45V114Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="406" d="M46 658H300Q348 658 370 642T392 586V580Q392 539 370 522T300 505H223V-47H303Q351 -47 373 -63T395 -118V-124Q395 -165 373 -182T303 -199H46V658Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="382" d="M300 -217Q283 -217 271 -215T258 -211L14 549Q6 578 6 593Q6 614 15 627T38 648T68 659T99 662Q116 662 128 660T141 655L385 -104Q389 -118 391 -129T393 -149Q393 -170 384 -183T361 -204T331 -214T300 -217Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="406" d="M104 -199Q55 -199 33 -182T11 -124V-118Q11 -80 33 -64T104 -47H184V505H107Q58 505 36 522T14 580V586Q14 625 36 641T107 658H360V-199H104H104Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="500" d="M354 438Q323 438 296 462T250 530Q230 487 203 463T146 438Q118 438 102 454T85 496Q85 522 102 547T144 593T198 629T250 651Q273 645 302 630T356 593T398 547T415 496Q415 471 399 455T354 438Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="516" d="M84 -139Q46 -139 27 -121T8 -73V-65Q8 -37 27 -19T84 0H433Q471 0 490 -18T509 -65V-73Q509 -102 490 -120T433 -139H84H84Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="500" d="M141 577Q114 592 99 618T83 670Q83 702 108 723T170 744Q196 744 223 728Q248 713 269 689T306 639T328 594T332 567Q328 561 310 555T265 548T206 553T141 577Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="629" d="M380 -14Q329 -14 291 10T235 72V0H44V682Q44 687 66 695T118 704Q139 704 160 698T198 675T225 630T236 558V435Q259 468 296 487T380 506Q431 506 473 488T545 437T591 355T607 247Q607 190 591 142T545 60T473 6T380 -14ZM327 135Q372 135 398 165T425 246Q425 297 399 327T328 357Q295 357 271 341T235 298V223Q235 181 261 158T327 135Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="512" d="M286 -14Q227 -14 178 5T95 58T41 141T22 247Q22 303 41 350T96 432T179 486T286 505Q345 505 385 492T450 458T486 414T497 373Q497 344 482 327T447 301T412 290T395 291Q384 320 361 338T297 356Q252 356 223 326T194 247Q194 198 222 167T297 135Q335 135 359 153T395 201Q396 204 412 203T447 192T481 165T497 117Q497 99 486 76T449 33T383 0T286 -14Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="629" d="M248 -14Q197 -14 156 5T84 58T38 141T22 247Q22 307 38 354T84 436T156 488T250 506Q297 506 334 487T394 438V682Q394 687 416 695T467 704Q488 704 509 698T547 675T574 630T585 558V0H394V76Q374 34 337 10T248 -14ZM302 135Q340 135 366 157T394 218V300Q382 326 359 341T304 357Q259 357 232 328T204 246Q204 196 231 166T302 135Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="416" d="M10 496H101V510Q101 568 115 606T154 667T214 699T290 709Q365 709 398 682T432 618Q432 602 427 588T415 564T402 549T395 544Q384 554 369 560T334 567Q310 567 298 555T285 512V496H398V351H293V0H101V351H10V496Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="629" d="M293 -195Q233 -195 189 -184T115 -153T72 -108T57 -56Q57 -35 67 -19T90 7T114 23T127 26Q151 -5 186 -23T279 -41Q342 -41 371 -17T401 59V104Q374 69 335 49T244 28Q193 28 152 44T82 91T38 166T22 268Q22 321 38 364T85 439T156 488T248 506Q297 506 336 491T401 451V496H586V66Q586 -69 515 -132T293 -195ZM300 172Q345 172 372 193T401 250V315Q385 335 360 348T302 361Q257 361 231 335T204 266Q204 223 230 198T300 172Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="606" d="M236 417Q258 455 295 480T391 506Q439 506 472 489T527 444T557 380T567 303V0H375V273Q375 309 358 329T312 350Q275 350 256 329T236 269V0H44V682Q44 687 66 695T118 704Q139 704 160 698T198 675T225 630T236 558V417Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="286" d="M145 552Q97 552 69 576T41 643Q41 685 68 709T142 733Q190 733 218 709T246 643Q246 601 219 577T145 552ZM47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="286" d="M145 552Q97 552 69 576T41 643Q41 685 68 709T142 733Q190 733 218 709T246 643Q246 601 219 577T145 552ZM58 -198Q-7 -198 -42 -174T-78 -100Q-78 -85 -73 -72T-61 -49T-48 -34T-41 -29Q-33 -40 -20 -45T7 -50Q47 -50 47 -5V496H239V-15Q239 -103 193 -150T58 -198Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="561" d="M44 682Q44 687 66 695T118 704Q139 704 160 698T198 675T225 630T236 558V296L335 445Q355 477 383 490T436 503Q458 503 477 496T511 478T534 458T540 444L407 255L553 50Q555 46 547 37T523 17T488 0T447 -8Q421 -8 393 5T343 50L236 205V0H44V682Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="286" d="M47 682Q47 687 69 695T121 704Q142 704 163 698T201 675T228 630T239 558V0H47V682Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="909" d="M118 501Q134 501 151 497T183 483T209 457T228 415Q254 454 290 480T383 506Q447 506 484 479T537 407Q563 449 600 477T700 506Q748 506 780 491T833 451T861 390T870 316V0H678V282Q678 350 619 350Q598 350 581 340T553 314V0H361V280Q361 350 302 350Q281 350 264 340T236 314V0H44V479Q44 484 66 492T118 501Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="606" d="M118 501Q134 501 151 497T183 483T209 457T228 415Q254 455 292 480T391 506Q439 506 472 490T527 447T557 385T567 310V0H375V277Q375 311 358 330T311 350Q285 350 266 339T236 310V0H44V479Q44 484 66 492T118 501Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="629" d="M44 479Q44 484 66 492T118 501Q135 501 152 497T184 483T211 455T229 410Q246 454 285 480T380 506Q431 506 473 488T545 437T591 355T607 247Q607 190 591 142T545 60T473 6T380 -14Q329 -14 292 10T236 72V-186H44V479ZM327 135Q372 135 398 165T425 246Q425 297 399 327T328 357Q295 357 271 341T235 298V223Q235 181 261 158T327 135Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="629" d="M394 76Q374 34 337 10T248 -14Q197 -14 156 5T84 58T38 141T22 247Q22 307 38 354T84 436T156 488T250 506Q303 506 343 483T403 422Q410 445 422 460T448 484T479 497T512 501Q541 501 563 493T585 479V-186H394V76ZM302 135Q342 135 368 158T394 223V300Q382 326 359 341T304 357Q259 357 232 328T204 246Q204 196 231 166T302 135Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="467" d="M118 501Q134 501 151 497T183 483T210 456T228 413Q242 455 272 480T348 506Q396 506 424 479T453 403Q453 374 444 355T423 323T401 306T390 302Q378 313 360 320T321 328Q236 328 236 206V0H44V479Q44 484 66 492T118 501Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="492" d="M235 -11Q119 -11 66 19T13 102Q13 118 20 132T36 156T52 171T62 176Q88 152 128 135T220 118Q250 118 266 123T282 143Q282 161 260 166T201 180Q178 185 148 193T90 218T45 263T26 337Q26 375 41 406T85 459T154 493T244 505Q352 505 404 475T456 392Q456 375 449 361T431 336T413 320T403 315Q376 342 337 359T259 376Q231 376 216 371T200 349Q200 332 224 328T286 314Q310 309 341 301T400 275T448 229T468 157Q468 78 407 34T235 -11Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="439" d="M16 496H98V544Q98 590 120 613T184 637H200Q238 637 262 617T286 544V496H411V351H282V185Q282 135 324 135Q344 135 356 143T379 165Q380 167 387 163T401 150T415 127T421 95Q421 47 381 17T272 -14Q183 -14 141 30T98 164V351H16V496Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="571" d="M114 503Q145 503 172 485T215 415L286 184L360 415Q376 466 402 484T458 503Q479 503 498 496T533 479T556 460T562 447L371 0H201L8 447Q6 451 15 460T39 478T73 495T114 503Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="809" d="M10 447Q8 451 19 460T47 479T85 496T127 503Q140 503 154 500T180 488T201 466T214 430L256 194L319 402Q327 431 353 446T413 462Q446 462 471 447T504 402L559 204L601 430Q609 472 633 487T684 503Q705 503 726 496T763 480T790 461T799 447L652 0H485L405 250L327 0H160L10 447Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="556" d="M402 248L539 53Q542 49 534 40T510 20T473 1T426 -7Q397 -7 372 8T332 55L276 152L222 56Q191 -7 125 -7Q102 -7 81 1T45 19T21 39T15 53L152 248L16 444Q13 448 21 457T45 477T80 495T124 503Q153 503 179 490T222 441L277 343L333 441Q349 474 375 488T429 503Q452 503 473 495T510 477T534 457T540 444L402 248Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="570" d="M243 -188Q222 -188 203 -181T169 -165T146 -148T140 -135L222 36Q205 40 192 48T173 72L-9 496H195L290 199L379 496H579L345 -103Q326 -151 300 -169T243 -188Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="508" d="M23 118L237 352H101Q28 352 28 420V427Q28 460 46 478T101 496H477V380L257 145H415Q487 145 487 77V69Q487 1 415 1H23V118Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="497" d="M384 -200Q343 -200 304 -185T230 -137T172 -49T138 86L112 100Q83 115 65 127T36 153T22 186T18 230Q18 256 22 274T38 307T67 334T112 360L139 374Q148 453 172 507T230 596T304 644T384 659Q428 659 457 637T486 575Q486 560 482 549T472 531T461 519T453 515Q414 512 388 499T346 459T321 393T310 298L192 230L310 162Q313 107 321 67T345 1T387 -38T452 -55Q454 -55 459 -59T471 -71T481 -90T486 -116Q486 -154 457 -177T384 -200Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="290" d="M137 -199Q98 -199 74 -180T49 -107V567Q49 619 73 639T135 659H155Q194 659 217 640T241 567V-107Q241 -159 218 -179T157 -199H137H137Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="497" d="M113 -200Q69 -200 40 -177T11 -116Q11 -101 15 -90T26 -72T37 -60T45 -55Q84 -52 110 -39T152 1T176 67T187 162L305 230L187 298Q184 353 176 393T152 459T109 498T44 515Q42 515 37 519T26 530T16 549T11 575Q11 614 40 636T113 659Q153 659 193 645T266 596T324 508T358 374L385 360Q411 347 429 335T458 308T474 275T479 230Q479 204 475 186T461 154T432 127T385 100L359 86Q350 6 326 -48T267 -136T193 -185T113 -200Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="499" d="M325 154Q293 154 269 163T224 189Q216 197 209 202T197 208Q189 208 184 197T172 176Q156 155 129 155Q103 155 86 172T68 214Q68 269 99 299T174 329Q202 329 224 322T265 300Q276 292 285 284T302 275Q310 275 314 285T325 305Q341 327 369 327Q396 327 412 310T431 268Q431 213 400 184T325 154Z" />
+<glyph unicode="&#xA0;" glyph-name="nbspace" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="297" d="M138 478Q94 478 75 497T55 559V575Q55 656 138 656H161Q242 656 242 575V559Q242 478 161 478H138ZM89 372Q96 404 108 418T140 432H159Q178 432 189 418T207 372L273 1H23L89 372Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="523" d="M288 -103Q244 -103 227 -81T210 -11V25Q118 47 65 113T12 283Q12 382 65 448T208 538V574Q208 620 226 643T286 667H290Q333 667 350 645T368 574V544Q441 533 477 501T513 423Q513 396 498 379T465 353T430 341T413 341Q390 378 360 391V174Q393 187 415 226Q416 229 432 227T466 215T500 190T515 146Q515 100 479 67T370 22V-11Q370 -57 352 -80T292 -103H288ZM193 284Q193 237 217 207V360Q193 330 193 284Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="622" d="M106 239Q78 239 61 256T43 299V315Q43 341 61 357T106 374H145L160 454Q179 559 240 610T398 661Q493 661 547 624T602 522Q602 485 586 466T549 439T513 431T496 432Q489 468 469 488T407 509Q372 509 352 491T323 429L313 374H422Q451 374 468 358T486 315V299Q486 272 469 256T422 239H289L273 151H514Q560 151 580 135T600 81V72Q600 34 580 17T514 0H103Q56 0 37 17T17 72V81Q17 118 37 134T103 151H106L121 239H106H106Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="634" d="M78 338Q46 338 31 352T15 386V400Q15 418 30 432T78 447H104Q116 497 141 537T204 604T289 646T392 661Q498 661 557 623T616 526Q616 502 603 484T574 454T543 437T526 434Q500 466 472 482T396 499Q326 499 296 447H415Q448 447 463 433T478 400V386Q478 366 463 352T415 338H280V308H415Q448 308 463 294T478 261V247Q478 228 463 214T415 199H298Q313 173 338 161T397 148Q445 148 473 165T527 215Q529 217 543 212T574 196T604 166T617 123Q617 99 605 75T566 31T497 -1T394 -14Q281 -14 205 41T105 199H78Q46 199 31 213T15 247V261Q15 280 30 294T78 308H95V338H78H78Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="644" d="M108 255Q73 255 56 271T39 308V322Q39 343 56 359T108 375H176L42 571Q40 575 43 588T59 615T94 641T153 653Q184 653 208 638T249 584L323 433L402 584Q422 623 446 639T501 655Q532 655 552 643T585 616T601 588T603 571L470 375H537Q572 375 588 359T605 322V308Q605 287 589 271T537 255H412V220H537Q572 220 588 204T605 168V154Q605 132 589 117T537 101H412V84Q412 38 394 15T332 -9H312Q270 -9 253 14T235 84V101H108Q73 101 56 116T39 154V168Q39 188 56 204T108 220H235V255H108H108Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="601" d="M280 -196Q154 -196 95 -162T36 -71Q36 -55 44 -41T62 -16T81 0T93 4Q108 -10 131 -22T181 -43T234 -57T283 -62Q325 -62 343 -52T361 -21Q361 1 331 11T252 33Q216 41 176 52T101 83T45 133T23 208Q23 304 127 346Q94 364 73 393T52 470Q52 557 118 609T305 661Q372 661 416 650T487 621T523 583T534 543Q534 524 525 509T505 482T484 466T473 462Q440 492 400 510T317 529Q242 529 242 487Q242 476 251 468T278 454T319 441T371 429Q406 420 443 408T510 377T559 329T579 258Q579 163 477 120Q509 103 530 74T551 -4Q551 -47 533 -82T480 -143T395 -182T280 -196ZM415 224Q415 243 403 255T370 276T322 291T264 304Q226 296 206 278T186 239Q186 219 199 207T236 187T288 173T351 161Q383 169 399 185T415 224Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="500" d="M365 541Q323 541 299 563T275 629Q275 671 298 694T362 717Q403 717 427 694T451 629Q451 587 427 564T365 541ZM139 541Q97 541 73 563T49 629Q49 671 73 694T135 717Q177 717 201 694T226 629Q226 587 202 564T139 541Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="739" d="M370 -14Q295 -14 233 11T127 81T58 188T33 324Q33 397 57 459T126 566T233 636T370 661Q445 661 507 636T613 567T681 460T706 324Q706 250 682 188T613 81T507 11T370 -14ZM370 53Q430 53 479 73T563 129T617 215T636 324Q636 383 617 432T562 518T478 574T370 594Q310 594 261 574T177 518T122 433T103 324Q103 265 122 216T177 130T261 74T370 53ZM380 138Q338 138 304 151T245 190T206 248T192 323Q192 363 206 396T244 454T303 492T379 506Q426 506 454 493T497 466Q506 457 510 448T515 423Q515 408 507 397T488 378T468 367T457 365Q441 384 425 392T384 400Q354 400 333 379T312 323Q312 288 333 267T385 245Q411 245 427 253T458 281Q459 282 468 279T488 269T507 250T516 223Q517 209 512 199T497 178Q483 164 455 151T380 138Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="456" d="M149 248Q88 248 52 280T16 364Q16 395 27 418T67 458T145 483T271 493Q268 550 209 550Q191 550 179 545T157 531T142 514T132 495Q132 494 116 494T82 498T47 516T31 555Q31 602 76 631T219 661Q327 661 374 619T422 490V266Q422 260 404 255T363 249Q296 249 279 314Q261 282 227 265T149 248ZM209 343Q238 343 254 361T271 406V424Q212 422 187 412T161 379Q161 362 174 353T209 343ZM217 -9Q144 -9 144 64V79Q144 152 217 152H237Q276 152 294 135T312 79V64Q312 -9 237 -9H217Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="551" d="M452 55Q420 55 387 82T325 143T279 208T261 247Q261 252 269 268T292 305T325 350T365 393T408 426T452 439H454Q489 439 506 420T524 370V366Q524 348 511 331T480 297T440 268T401 247Q418 238 439 225T479 196T511 163T524 128V123Q524 94 507 75T454 55H452ZM204 74Q185 74 165 85T124 115T87 154T56 194T34 227T26 247Q26 254 43 281T86 340T144 395T204 420H206Q238 420 253 403T269 357V354Q269 338 258 322T230 292T194 266T159 247Q174 239 193 227T229 201T257 171T269 139V136Q269 109 254 92T206 74H204Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="739" d="M370 -14Q295 -14 233 11T127 81T58 188T33 324Q33 397 57 459T126 566T233 636T370 661Q445 661 507 636T613 567T681 460T706 324Q706 250 682 188T613 81T507 11T370 -14ZM370 53Q430 53 479 73T563 129T617 215T636 324Q636 383 617 432T562 518T478 574T370 594Q310 594 261 574T177 518T122 433T103 324Q103 265 122 216T177 130T261 74T370 53ZM472 141Q455 141 438 150T407 185L361 257H333V198Q333 172 320 158T282 144H266Q242 144 229 162T216 212V438Q216 470 231 486T276 502H384Q538 502 538 384Q538 348 521 322T469 283L540 181Q542 178 537 172T522 159T499 147T472 141ZM331 338H376Q422 338 422 376Q422 414 373 414H331V338Z" />
+<glyph unicode="&#xAF;" glyph-name="overscore" horiz-adv-x="500" d="M133 546Q97 546 76 563T55 611V619Q55 649 76 666T133 684H367Q403 684 424 667T445 619V611Q445 580 425 563T367 546H133H133Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="375" d="M188 335Q151 335 120 346T67 377T33 427T20 493V500Q20 537 32 566T67 615T121 647T189 658Q263 658 309 617T355 500V493Q355 419 309 377T188 335ZM189 425Q212 425 227 440T243 489V504Q243 537 227 553T188 569Q164 569 149 554T133 505V489Q133 457 149 441T189 425Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="600" d="M129 353Q91 353 72 371T53 418V426Q53 453 72 471T129 490H226V577Q226 615 244 634T292 653H307Q335 653 353 634T372 577V490H470Q509 490 527 472T546 426V418Q546 389 528 371T470 353H372V285Q372 246 354 228T307 209H292Q263 209 245 227T226 285V353H129ZM119 30Q81 30 62 48T43 95V104Q43 131 62 149T119 168H480Q519 168 537 150T556 104V95Q556 66 538 48T480 30H119Z" />
+<glyph unicode="&#xB2;" glyph-name="two.sups" horiz-adv-x="336" d="M223 427Q192 416 165 408T124 383H262Q321 383 321 337V335Q321 286 262 286H96Q51 286 36 297T21 345V369Q21 394 29 412T51 443T85 466T131 487Q163 501 183 509T204 531Q204 545 194 554T165 564Q138 564 123 545T104 505Q104 504 92 504T64 510T36 526T23 561Q23 604 61 630T172 656Q241 656 280 624T320 536Q320 510 312 492T291 462T260 442T223 427Z" />
+<glyph unicode="&#xB3;" glyph-name="three.sups" horiz-adv-x="336" d="M321 390Q321 336 281 307T160 277Q91 277 51 301T10 362Q10 386 21 398T47 415T73 421T86 419Q98 369 156 369Q179 369 192 378T206 401Q206 426 174 426H166Q135 426 123 436T110 466V473Q110 493 123 503T165 514H181Q191 514 197 519T204 534Q204 547 191 555T154 564Q136 564 125 559T106 547T95 531T89 515Q89 513 78 513T53 517T28 534T17 571Q17 609 55 632T159 656Q317 656 317 553Q317 498 255 479Q321 456 321 390Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="500" d="M167 567Q164 573 171 593T193 639T229 688T276 728Q303 744 329 744Q365 744 390 723T415 670Q415 644 400 618T358 577Q326 559 294 553T234 548T189 555T167 567Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="626" d="M518 -9Q472 -9 472 42V606Q472 656 518 656H534Q581 656 581 606V42Q581 -9 534 -9H518ZM359 -9Q336 -9 324 4T312 42V272H250Q135 272 74 319T13 461Q13 557 71 606T248 655H357Q387 655 403 639T420 594V42Q420 -9 374 -9H359Z" />
+<glyph unicode="&#xB7;" glyph-name="middot" horiz-adv-x="262" d="M118 158Q36 158 36 239V255Q36 336 118 336H141Q224 336 224 255V239Q224 158 141 158H118H118Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="500" d="M233 -221Q219 -230 201 -231T167 -229T142 -216T137 -193L171 -96Q175 -83 181 -73T196 -56T220 -46T258 -43H278Q314 -43 334 -56T354 -95V-100Q354 -117 343 -131T308 -162L233 -221H233Z" />
+<glyph unicode="&#xB9;" glyph-name="one.sups" horiz-adv-x="336" d="M85 285Q27 285 27 335V336Q27 389 85 389H125V513Q105 503 87 503Q61 503 42 520T23 564Q23 580 28 591T39 610T51 620T59 623Q68 618 75 615T94 612Q112 612 126 622T145 653H184Q240 653 240 594V389H263Q321 389 321 336V335Q321 285 263 285H85H85Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="476" d="M237 244Q188 244 149 260T81 304T38 370T22 453Q22 497 38 535T83 601T151 645T239 661Q287 661 327 645T395 601T439 535T455 453Q455 408 439 370T394 304T325 260T237 244ZM239 367Q273 367 296 392T320 453Q320 490 297 515T239 540Q204 540 181 515T157 453Q157 417 180 392T239 367ZM226 -9Q153 -9 153 64V79Q153 152 226 152H247Q321 152 321 79V64Q321 -9 247 -9H226Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="551" d="M97 55Q62 55 45 74T27 124V128Q27 146 40 163T71 196T111 225T150 247Q133 255 112 268T72 297T40 331T27 366V370Q27 401 44 420T97 439H99Q120 439 142 426T185 394T225 351T258 306T281 268T289 247Q289 242 281 226T259 189T226 144T186 101T142 68T99 55H97ZM344 74Q313 74 298 92T282 136V140Q282 156 293 171T321 201T357 227T392 247Q377 254 358 266T322 292T294 322T282 354V358Q282 385 297 402T344 420H347Q366 420 386 409T427 379T464 340T495 300T517 266T525 247Q525 242 517 228T496 194T465 154T427 115T387 86T347 74H344Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="792" d="M85 285Q27 285 27 335V336Q27 389 85 389H125V513Q105 503 87 503Q61 503 42 520T23 564Q23 580 28 591T39 610T51 620T59 623Q68 618 75 615T94 612Q112 612 126 622T145 653H184Q240 653 240 594V389H263Q321 389 321 336V335Q321 285 263 285H85ZM132 12Q133 24 138 35T155 62Q157 64 177 88T226 147T290 223T355 302Q370 321 389 345T430 396T475 451T519 507Q569 569 622 638Q624 641 633 641T652 635T669 620T677 594Q676 583 672 571T654 544Q652 542 632 518T582 459T518 381T453 302Q438 283 419 259T378 207T334 152T290 96Q241 33 188 -34Q185 -37 176 -36T157 -30T140 -14T132 12ZM675 -5Q624 -5 624 53V75H522Q491 75 474 89T456 125V141Q457 155 461 166T475 196L547 328Q554 341 561 349T580 361T608 366T649 368Q696 368 715 351T735 286V164H737Q767 164 779 155T791 122V119Q791 94 779 85T737 75H735V53Q735 -5 682 -5H675ZM555 162H631V295L555 162Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="792" d="M85 285Q27 285 27 335V336Q27 389 85 389H125V513Q105 503 87 503Q61 503 42 520T23 564Q23 580 28 591T39 610T51 620T59 623Q68 618 75 615T94 612Q112 612 126 622T145 653H184Q240 653 240 594V389H263Q321 389 321 336V335Q321 285 263 285H85ZM110 12Q111 24 116 35T133 62Q135 64 155 88T204 147T268 223T333 302Q348 321 367 345T408 396T453 451T497 507Q547 569 600 638Q602 641 611 641T630 635T647 620T655 594Q654 583 650 571T632 544Q630 542 610 518T560 459T496 381T431 302Q416 283 397 259T356 207T312 152T268 96Q219 33 166 -34Q163 -37 154 -36T135 -30T118 -14T110 12ZM679 142Q648 131 621 123T580 98H718Q777 98 777 52V50Q777 1 718 1H552Q507 1 492 12T477 60V84Q477 109 485 127T507 158T541 181T587 202Q619 216 639 224T660 246Q660 260 650 269T621 279Q594 279 579 260T560 220Q560 219 548 219T520 225T492 241T479 276Q479 319 517 345T628 371Q697 371 736 339T776 251Q776 225 768 207T747 177T716 157T679 142Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="792" d="M321 390Q321 336 281 307T160 277Q91 277 51 301T10 362Q10 386 21 398T47 415T73 421T86 419Q98 369 156 369Q179 369 192 378T206 401Q206 426 174 426H166Q135 426 123 436T110 466V473Q110 493 123 503T165 514H181Q191 514 197 519T204 534Q204 547 191 555T154 564Q136 564 125 559T106 547T95 531T89 515Q89 513 78 513T53 517T28 534T17 571Q17 609 55 632T159 656Q317 656 317 553Q317 498 255 479Q321 456 321 390ZM125 12Q126 24 131 35T148 62Q150 64 170 88T219 147T283 223T348 302Q363 321 382 345T423 396T468 451T512 507Q562 569 615 638Q617 641 626 641T645 635T662 620T670 594Q669 583 665 571T647 544Q645 542 625 518T575 459T511 381T446 302Q431 283 412 259T371 207T327 152T283 96Q234 33 181 -34Q178 -37 169 -36T150 -30T133 -14T125 12ZM675 -5Q624 -5 624 53V75H522Q491 75 474 89T456 125V141Q457 155 461 166T475 196L547 328Q554 341 561 349T580 361T608 366T649 368Q696 368 715 351T735 286V164H737Q767 164 779 155T791 122V119Q791 94 779 85T737 75H735V53Q735 -5 682 -5H675ZM555 162H631V295L555 162Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="522" d="M292 656Q374 656 374 575V559Q374 478 292 478H270Q186 478 186 559V575Q186 656 270 656H292ZM360 434Q363 392 356 361T335 306T301 267T259 242Q234 231 216 221T197 190Q197 167 215 154T266 140Q292 140 312 148T347 171T369 204T381 241Q381 241 390 242T414 241T444 235T475 220T498 194T508 152Q508 120 492 90T445 37T368 1T263 -13Q146 -13 81 38T16 177Q16 212 25 237T49 279T83 309T121 331Q136 339 150 347T177 366T199 394T211 434H360Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM227 732Q200 747 185 773T169 825Q169 857 194 878T256 899Q282 899 309 883Q334 868 355 844T392 794T414 749T418 722Q414 716 396 710T351 703T292 708T227 732Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM265 722Q262 728 269 748T291 794T327 843T374 883Q401 899 427 899Q463 899 488 878T513 825Q513 799 498 773T456 732Q424 714 392 708T332 703T287 710T265 722Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM447 696Q416 696 389 720T343 788Q323 745 296 721T239 696Q211 696 195 712T178 754Q178 780 195 805T237 851T291 887T343 909Q366 903 395 888T449 851T491 805T508 754Q508 729 492 713T447 696Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM421 696Q389 696 365 705T320 731Q312 739 305 744T293 750Q285 750 280 739T268 718Q252 697 225 697Q199 697 182 714T164 756Q164 811 195 841T270 871Q298 871 320 864T361 842Q372 834 381 826T398 817Q406 817 410 827T421 847Q437 869 465 869Q492 869 508 852T527 810Q527 755 496 726T421 696Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM453 696Q411 696 387 718T363 784Q363 826 386 849T450 872Q491 872 515 849T539 784Q539 742 515 719T453 696ZM227 696Q185 696 161 718T137 784Q137 826 161 849T223 872Q265 872 289 849T314 784Q314 742 290 719T227 696Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="688" d="M475 724Q475 694 463 670T428 631Q458 618 467 597L708 0H499L445 134H236L185 0H-15L216 597Q225 619 257 632Q213 663 213 724Q213 751 223 774T250 814T292 840T345 850Q373 850 397 841T438 815T465 775T475 724ZM344 676Q363 676 376 690T390 724Q390 745 377 759T344 773Q324 773 311 759T298 724Q298 704 311 690T344 676ZM270 278H409L340 466L270 278Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="895" d="M445 134H236L218 84Q199 31 170 11T110 -9Q88 -9 68 -2T34 15T11 35T6 53L210 583Q215 598 223 610T247 630T284 642T340 647H857V475H509L534 408H772V248H601L631 170H859V0H598Q572 0 552 3T515 16T486 43T462 89L445 134ZM270 278H409L339 466L270 278Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="650" d="M368 -14Q292 -14 230 11T123 81T54 188T29 324Q29 397 53 459T123 566T230 636T369 661Q438 661 486 645T564 605T609 554T623 503Q623 471 606 451T567 420T527 406T508 405Q489 440 456 459T380 478Q349 478 323 467T277 436T246 388T235 324Q235 289 246 261T276 212T322 180T380 169Q427 169 460 189T510 247Q511 249 529 246T569 232T608 201T626 149Q626 125 612 97T567 44T487 3T368 -14ZM334 -221Q320 -230 302 -231T268 -229T243 -216T238 -193L272 -96Q276 -83 282 -73T297 -56T321 -46T359 -43H379Q415 -43 435 -56T455 -95V-100Q455 -117 444 -131T409 -162L334 -221Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM227 732Q200 747 185 773T169 825Q169 857 194 878T256 899Q282 899 309 883Q334 868 355 844T392 794T414 749T418 722Q414 716 396 710T351 703T292 708T227 732Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM221 722Q218 728 225 748T247 794T283 843T330 883Q357 899 383 899Q419 899 444 878T469 825Q469 799 454 773T412 732Q380 714 348 708T288 703T243 710T221 722Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM425 696Q394 696 367 720T321 788Q301 745 274 721T217 696Q189 696 173 712T156 754Q156 780 173 805T215 851T269 887T321 909Q344 903 373 888T427 851T469 805T486 754Q486 729 470 713T425 696Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM436 696Q394 696 370 718T346 784Q346 826 369 849T433 872Q474 872 498 849T522 784Q522 742 498 719T436 696ZM210 696Q168 696 144 718T120 784Q120 826 144 849T206 872Q248 872 272 849T297 784Q297 742 273 719T210 696Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566ZM51 732Q24 747 9 773T-7 825Q-7 857 18 878T80 899Q106 899 133 883Q158 868 179 844T216 794T238 749T242 722Q238 716 220 710T175 703T116 708T51 732Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566ZM57 722Q54 728 61 748T83 794T119 843T166 883Q193 899 219 899Q255 899 280 878T305 825Q305 799 290 773T248 732Q216 714 184 708T124 703T79 710T57 722Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566ZM254 696Q223 696 196 720T150 788Q130 745 103 721T46 696Q18 696 2 712T-15 754Q-15 780 2 805T44 851T98 887T150 909Q173 903 202 888T256 851T298 805T315 754Q315 729 299 713T254 696Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566ZM265 696Q223 696 199 718T175 784Q175 826 198 849T262 872Q303 872 327 849T351 784Q351 742 327 719T265 696ZM39 696Q-3 696 -27 718T-51 784Q-51 826 -27 849T35 872Q77 872 101 849T126 784Q126 742 102 719T39 696Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="731" d="M67 257Q35 257 19 275T3 317V333Q3 356 19 374T67 392H84V536Q84 590 113 618T188 647H361Q440 647 504 625T613 561T682 460T706 327Q706 252 682 192T612 89T502 23T357 0H196Q173 0 153 8T117 32T93 67T84 110V257H67ZM353 171Q424 171 466 211T509 326Q509 399 468 437T356 476H283V392H349Q382 392 397 374T412 333V317Q412 293 397 275T349 257H283V171H353Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="703" d="M46 559Q46 606 68 629T128 652H147Q165 652 177 650T199 641T217 626T237 605L469 303V647H657V88Q657 41 636 18T576 -5H560Q542 -5 530 -3T508 5T490 18T471 39L234 346V0H46V559ZM427 696Q395 696 371 705T326 731Q318 739 311 744T299 750Q291 750 286 739T274 718Q258 697 231 697Q205 697 188 714T170 756Q170 811 201 841T276 871Q304 871 326 864T367 842Q378 834 387 826T404 817Q412 817 416 827T427 847Q443 869 471 869Q498 869 514 852T533 810Q533 755 502 726T427 696Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM253 732Q226 747 211 773T195 825Q195 857 220 878T282 899Q308 899 335 883Q360 868 381 844T418 794T440 749T444 722Q440 716 422 710T377 703T318 708T253 732Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM274 722Q271 728 278 748T300 794T336 843T383 883Q410 899 436 899Q472 899 497 878T522 825Q522 799 507 773T465 732Q433 714 401 708T341 703T296 710T274 722Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM471 696Q440 696 413 720T367 788Q347 745 320 721T263 696Q235 696 219 712T202 754Q202 780 219 805T261 851T315 887T367 909Q390 903 419 888T473 851T515 805T532 754Q532 729 516 713T471 696Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM443 696Q411 696 387 705T342 731Q334 739 327 744T315 750Q307 750 302 739T290 718Q274 697 247 697Q221 697 204 714T186 756Q186 811 217 841T292 871Q320 871 342 864T383 842Q394 834 403 826T420 817Q428 817 432 827T443 847Q459 869 487 869Q514 869 530 852T549 810Q549 755 518 726T443 696Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM482 696Q440 696 416 718T392 784Q392 826 415 849T479 872Q520 872 544 849T568 784Q568 742 544 719T482 696ZM256 696Q214 696 190 718T166 784Q166 826 190 849T252 872Q294 872 318 849T343 784Q343 742 319 719T256 696Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="565" d="M75 53Q55 73 54 99T82 153L185 252L82 351Q53 377 54 403T74 450L85 462Q105 483 131 483T185 453L283 350L380 453Q407 482 434 482T481 462L491 450Q510 430 511 404T483 351L381 252L483 153Q512 126 512 100T491 53L479 41Q461 23 434 22T380 50L283 153L185 50Q158 21 132 22T86 41L75 53Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="733" d="M110 -44Q107 -47 95 -45T69 -35T44 -15T33 14Q33 24 38 35T56 61L98 108Q64 151 46 205T27 324Q27 397 52 458T122 565T231 635T369 661Q422 661 468 648T553 612L621 686Q624 689 636 687T661 677T686 657T697 628Q697 618 692 607T675 581L637 539Q672 496 690 442T709 324Q709 251 684 189T614 82T506 12T368 -14Q314 -14 268 -1T181 35L110 -44ZM210 324Q210 281 224 247L434 480Q403 493 368 493Q294 493 252 447T210 324ZM369 156Q404 156 433 168T483 203T515 256T527 324Q527 366 512 401L302 169Q331 156 369 156Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM227 729Q200 744 185 770T169 822Q169 854 194 875T256 896Q282 896 309 880Q334 865 355 841T392 791T414 746T418 719Q414 713 396 707T351 700T292 705T227 729Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM261 719Q258 725 265 745T287 791T323 840T370 880Q397 896 423 896Q459 896 484 875T509 822Q509 796 494 770T452 729Q420 711 388 705T328 700T283 707T261 719Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM443 696Q412 696 385 720T339 788Q319 745 292 721T235 696Q207 696 191 712T174 754Q174 780 191 805T233 851T287 887T339 909Q362 903 391 888T445 851T487 805T504 754Q504 729 488 713T443 696Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM454 696Q412 696 388 718T364 784Q364 826 387 849T451 872Q492 872 516 849T540 784Q540 742 516 719T454 696ZM228 696Q186 696 162 718T138 784Q138 826 162 849T224 872Q266 872 290 849T315 784Q315 742 291 719T228 696Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="639" d="M221 269L-16 647H203L327 430L414 581Q437 619 460 635T509 651Q543 651 568 641T608 617T629 591T633 575L421 253V0H221V269ZM238 719Q235 725 242 745T264 791T300 840T347 880Q374 896 400 896Q436 896 461 875T486 822Q486 796 471 770T429 729Q397 711 365 705T305 700T260 707T238 719Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="623" d="M130 -5Q46 -5 46 88V559Q46 652 130 652H159Q246 652 246 559V551H315Q457 551 529 494T601 324Q601 101 321 101H246V88Q246 -5 159 -5H130ZM246 400V247H317Q410 247 410 323Q410 363 388 381T317 400H246Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="625" d="M573 532Q573 475 548 443T470 397Q540 369 577 316T614 194Q614 147 600 109T559 44T497 1T417 -14Q341 -14 303 12T265 85Q265 100 271 113T284 136T297 152T305 157Q316 147 330 140T370 133Q396 133 415 150T434 193Q434 225 413 244T347 278Q333 283 321 289T300 306T286 331T280 367Q280 383 285 396T296 419T307 434T312 439Q350 439 370 455T391 504Q391 534 371 551T317 568Q236 568 236 466V0H44V447Q44 585 109 646T320 708Q441 708 507 661T573 532Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM186 577Q159 592 144 618T128 670Q128 702 153 723T215 744Q241 744 268 728Q293 713 314 689T351 639T373 594T377 567Q373 561 355 555T310 548T251 553T186 577Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM197 567Q194 573 201 593T223 639T259 688T306 728Q333 744 359 744Q395 744 420 723T445 670Q445 644 430 618T388 577Q356 559 324 553T264 548T219 555T197 567Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM392 541Q361 541 334 565T288 633Q268 590 241 566T184 541Q156 541 140 557T123 599Q123 625 140 650T182 696T236 732T288 754Q311 748 340 733T394 696T436 650T453 599Q453 574 437 558T392 541Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM356 541Q324 541 300 550T255 576Q247 584 240 589T228 595Q220 595 215 584T203 563Q187 542 160 542Q134 542 117 559T99 601Q99 656 130 686T205 716Q233 716 255 709T296 687Q307 679 316 671T333 662Q341 662 345 672T356 692Q372 714 400 714Q427 714 443 697T462 655Q462 600 431 571T356 541Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM395 541Q353 541 329 563T305 629Q305 671 328 694T392 717Q433 717 457 694T481 629Q481 587 457 564T395 541ZM169 541Q127 541 103 563T79 629Q79 671 103 694T165 717Q207 717 231 694T256 629Q256 587 232 564T169 541Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM279 541Q219 541 184 576T149 667Q149 694 158 717T185 757T227 783T281 793Q310 793 334 784T375 757T401 717T411 667Q411 640 402 617T375 577T333 551T279 541ZM280 619Q300 619 313 633T326 667Q326 687 313 701T280 716Q260 716 247 702T234 667Q234 646 247 633T280 619Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="872" d="M188 -10Q150 -10 120 0T67 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 438 94 472T260 506Q377 506 434 451Q497 506 595 506Q652 506 699 488T780 436T833 358T852 259V247Q851 225 844 211T814 197H500Q509 158 539 139T615 119Q660 119 690 137T735 182Q735 183 749 181T779 170T810 146T824 102Q824 52 768 19T607 -14Q448 -14 389 89Q363 45 310 18T188 -10ZM593 381Q555 381 529 357T497 296H680Q678 334 656 357T593 381ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="512" d="M286 -14Q227 -14 178 5T95 58T41 141T22 247Q22 303 41 350T96 432T179 486T286 505Q345 505 385 492T450 458T486 414T497 373Q497 344 482 327T447 301T412 290T395 291Q384 320 361 338T297 356Q252 356 223 326T194 247Q194 198 222 167T297 135Q335 135 359 153T395 201Q396 204 412 203T447 192T481 165T497 117Q497 99 486 76T449 33T383 0T286 -14ZM258 -221Q244 -230 226 -231T192 -229T167 -216T162 -193L196 -96Q200 -83 206 -73T221 -56T245 -46T283 -43H303Q339 -43 359 -56T379 -95V-100Q379 -117 368 -131T333 -162L258 -221Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM172 577Q145 592 130 618T114 670Q114 702 139 723T201 744Q227 744 254 728Q279 713 300 689T337 639T359 594T363 567Q359 561 341 555T296 548T237 553T172 577Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM195 567Q192 573 199 593T221 639T257 688T304 728Q331 744 357 744Q393 744 418 723T443 670Q443 644 428 618T386 577Q354 559 322 553T262 548T217 555T195 567Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM389 541Q358 541 331 565T285 633Q265 590 238 566T181 541Q153 541 137 557T120 599Q120 625 137 650T179 696T233 732T285 754Q308 748 337 733T391 696T433 650T450 599Q450 574 434 558T389 541Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM400 541Q358 541 334 563T310 629Q310 671 333 694T397 717Q438 717 462 694T486 629Q486 587 462 564T400 541ZM174 541Q132 541 108 563T84 629Q84 671 108 694T170 717Q212 717 236 694T261 629Q261 587 237 564T174 541Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="286" d="M47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409ZM43 577Q16 592 1 618T-15 670Q-15 702 10 723T72 744Q98 744 125 728Q150 713 171 689T208 639T230 594T234 567Q230 561 212 555T167 548T108 553T43 577Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="286" d="M47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409ZM49 567Q46 573 53 593T75 639T111 688T158 728Q185 744 211 744Q247 744 272 723T297 670Q297 644 282 618T240 577Q208 559 176 553T116 548T71 555T49 567Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="286" d="M47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409ZM246 541Q215 541 188 565T142 633Q122 590 95 566T38 541Q10 541 -6 557T-23 599Q-23 625 -6 650T36 696T90 732T142 754Q165 748 194 733T248 696T290 650T307 599Q307 574 291 558T246 541Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="286" d="M47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409ZM257 541Q215 541 191 563T167 629Q167 671 190 694T254 717Q295 717 319 694T343 629Q343 587 319 564T257 541ZM31 541Q-11 541 -35 563T-59 629Q-59 671 -35 694T27 717Q69 717 93 694T118 629Q118 587 94 564T31 541Z" />
+<glyph unicode="&#xF0;" glyph-name="eth" horiz-adv-x="622" d="M221 505Q190 490 167 497T133 530L131 535Q122 561 129 581T168 618H169Q134 630 96 639Q92 640 96 655T115 689T159 722T235 737Q297 737 357 711L456 760Q487 775 507 765T542 735L546 730Q562 710 552 687T509 647L471 629Q530 569 564 486T599 308Q599 234 578 175T517 73T422 9T301 -14Q239 -14 188 2T101 50T44 128T24 234Q24 286 41 330T88 405T161 454T256 472Q308 472 345 451T404 397Q391 439 366 476T303 546L221 505ZM303 133Q349 133 376 161T403 236V289Q390 312 364 327T307 343Q260 343 233 313T205 238Q205 193 232 163T303 133Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="606" d="M118 501Q134 501 151 497T183 483T209 457T228 415Q254 455 292 480T391 506Q439 506 472 490T527 447T557 385T567 310V0H375V277Q375 311 358 330T311 350Q285 350 266 339T236 310V0H44V479Q44 484 66 492T118 501ZM384 541Q352 541 328 550T283 576Q275 584 268 589T256 595Q248 595 243 584T231 563Q215 542 188 542Q162 542 145 559T127 601Q127 656 158 686T233 716Q261 716 283 709T324 687Q335 679 344 671T361 662Q369 662 373 672T384 692Q400 714 428 714Q455 714 471 697T490 655Q490 600 459 571T384 541Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM179 577Q152 592 137 618T121 670Q121 702 146 723T208 744Q234 744 261 728Q286 713 307 689T344 639T366 594T370 567Q366 561 348 555T303 548T244 553T179 577Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM215 567Q212 573 219 593T241 639T277 688T324 728Q351 744 377 744Q413 744 438 723T463 670Q463 644 448 618T406 577Q374 559 342 553T282 548T237 555T215 567Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM397 541Q366 541 339 565T293 633Q273 590 246 566T189 541Q161 541 145 557T128 599Q128 625 145 650T187 696T241 732T293 754Q316 748 345 733T399 696T441 650T458 599Q458 574 442 558T397 541Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM369 541Q337 541 313 550T268 576Q260 584 253 589T241 595Q233 595 228 584T216 563Q200 542 173 542Q147 542 130 559T112 601Q112 656 143 686T218 716Q246 716 268 709T309 687Q320 679 329 671T346 662Q354 662 358 672T369 692Q385 714 413 714Q440 714 456 697T475 655Q475 600 444 571T369 541Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM408 541Q366 541 342 563T318 629Q318 671 341 694T405 717Q446 717 470 694T494 629Q494 587 470 564T408 541ZM182 541Q140 541 116 563T92 629Q92 671 116 694T178 717Q220 717 244 694T269 629Q269 587 245 564T182 541Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="600" d="M289 377Q222 377 222 445V459Q222 527 289 527H309Q378 527 378 459V445Q378 377 309 377H289ZM119 182Q81 182 62 200T43 249V258Q43 288 62 306T119 325H480Q519 325 537 307T556 258V249Q556 218 538 200T480 182H119ZM289 -18Q222 -18 222 50V64Q222 132 289 132H309Q378 132 378 64V50Q378 -18 309 -18H289Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="586" d="M82 -73Q79 -76 67 -74T41 -64T16 -44T5 -16Q5 4 28 30L77 84Q51 117 38 157T24 246Q24 301 44 348T100 431T186 486T296 506Q334 506 367 498T429 474L514 568Q517 571 529 569T554 559T579 541T590 513Q590 502 585 491T568 466L513 404Q564 338 564 247Q564 191 544 144T488 61T402 6T292 -14Q217 -14 162 16L82 -73ZM191 193Q202 205 214 218Q225 229 238 243T265 274Q298 309 339 355Q319 367 294 367Q243 367 212 333T180 246Q180 218 191 193ZM294 127Q319 127 340 136T376 161T399 198T408 246Q408 276 396 297L324 217Q292 182 252 138Q271 127 294 127Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM188 577Q161 592 146 618T130 670Q130 702 155 723T217 744Q243 744 270 728Q295 713 316 689T353 639T375 594T379 567Q375 561 357 555T312 548T253 553T188 577Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM227 567Q224 573 231 593T253 639T289 688T336 728Q363 744 389 744Q425 744 450 723T475 670Q475 644 460 618T418 577Q386 559 354 553T294 548T249 555T227 567Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM407 541Q376 541 349 565T303 633Q283 590 256 566T199 541Q171 541 155 557T138 599Q138 625 155 650T197 696T251 732T303 754Q326 748 355 733T409 696T451 650T468 599Q468 574 452 558T407 541Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM418 541Q376 541 352 563T328 629Q328 671 351 694T415 717Q456 717 480 694T504 629Q504 587 480 564T418 541ZM192 541Q150 541 126 563T102 629Q102 671 126 694T188 717Q230 717 254 694T279 629Q279 587 255 564T192 541Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="570" d="M243 -188Q222 -188 203 -181T169 -165T146 -148T140 -135L222 36Q205 40 192 48T173 72L-9 496H195L290 199L379 496H579L345 -103Q326 -151 300 -169T243 -188ZM209 565Q206 571 213 591T235 637T271 686T318 726Q345 742 371 742Q407 742 432 721T457 668Q457 642 442 616T400 575Q368 557 336 551T276 546T231 553T209 565Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="632" d="M380 -14Q335 -14 298 4T236 51V-101Q236 -153 212 -173T151 -193H131Q93 -193 69 -174T44 -101V682Q44 687 66 695T118 704Q139 704 160 698T198 675T225 630T236 558V430Q262 465 299 485T381 506Q432 506 473 488T545 435T591 353T607 245Q607 185 591 138T546 56T474 4T380 -14ZM327 135Q372 135 398 165T425 246Q425 297 399 327T328 357Q296 357 272 342T236 301V207Q241 172 266 154T327 135Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="570" d="M243 -188Q222 -188 203 -181T169 -165T146 -148T140 -135L222 36Q205 40 192 48T173 72L-9 496H195L290 199L379 496H579L345 -103Q326 -151 300 -169T243 -188ZM403 541Q361 541 337 563T313 629Q313 671 336 694T400 717Q441 717 465 694T489 629Q489 587 465 564T403 541ZM177 541Q135 541 111 563T87 629Q87 671 111 694T173 717Q215 717 239 694T264 629Q264 587 240 564T177 541Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM226 694Q190 694 169 711T148 759V767Q148 797 169 814T226 832H460Q496 832 517 815T538 767V759Q538 728 518 711T460 694H226H226Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM178 546Q142 546 121 563T100 611V619Q100 649 121 666T178 684H412Q448 684 469 667T490 619V611Q490 580 470 563T412 546H178H178Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H499L445 134ZM270 278H409L339 466L270 278ZM344 689Q301 689 263 703T198 742Q181 759 171 778T161 817Q161 842 179 861T229 880Q252 875 268 863T297 837T321 813T344 802Q357 802 367 812T391 836T420 862T459 880Q491 880 509 861T527 817Q527 777 491 744Q466 719 427 704T344 689Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107ZM292 541Q249 541 211 555T146 594Q129 611 119 630T109 669Q109 694 127 713T177 732Q200 727 216 715T245 689T269 665T292 654Q305 654 315 664T339 688T368 714T407 732Q439 732 457 713T475 669Q475 629 439 596Q414 571 375 556T292 541Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="688" d="M445 134H236L185 0H-15L216 597Q220 609 227 619T249 636T286 648T343 652Q377 652 399 648T435 637T456 619T467 597L708 0H640Q612 -27 601 -44T590 -75Q590 -104 618 -104Q639 -104 650 -86Q666 -88 676 -102T686 -135Q686 -165 663 -185T590 -205Q534 -205 502 -181T470 -111Q470 -78 494 -48T559 0H499L445 134ZM270 278H409L339 466L270 278Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="574" d="M186 -10Q148 -10 118 0T66 30T33 77T21 136Q21 221 93 262T325 304H344Q338 370 267 370Q244 370 227 364T199 349T180 327T167 304Q167 303 147 303T103 308T60 331T40 379Q40 437 97 471T278 506Q412 506 473 454T534 295V0H521Q486 -33 472 -52T458 -88Q458 -117 486 -117Q507 -117 518 -99Q534 -101 544 -115T554 -148Q554 -178 531 -198T458 -218Q402 -218 370 -194T338 -124Q338 -103 348 -84T375 -47T416 -18T468 0H345V61Q319 27 278 9T186 -10ZM261 107Q299 107 322 130T345 192V209H321Q263 207 233 195T203 153Q203 131 219 119T261 107Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="650" d="M368 -14Q292 -14 230 11T123 81T54 188T29 324Q29 397 53 459T123 566T230 636T369 661Q438 661 486 645T564 605T609 554T623 503Q623 471 606 451T567 420T527 406T508 405Q489 440 456 459T380 478Q349 478 323 467T277 436T246 388T235 324Q235 289 246 261T276 212T322 180T380 169Q427 169 460 189T510 247Q511 249 529 246T569 232T608 201T626 149Q626 125 612 97T567 44T487 3T368 -14ZM267 715Q264 721 271 741T293 787T329 836T376 876Q403 892 429 892Q465 892 490 871T515 818Q515 792 500 766T458 725Q426 707 394 701T334 696T289 703T267 715Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="512" d="M286 -14Q227 -14 178 5T95 58T41 141T22 247Q22 303 41 350T96 432T179 486T286 505Q345 505 385 492T450 458T486 414T497 373Q497 344 482 327T447 301T412 290T395 291Q384 320 361 338T297 356Q252 356 223 326T194 247Q194 198 222 167T297 135Q335 135 359 153T395 201Q396 204 412 203T447 192T481 165T497 117Q497 99 486 76T449 33T383 0T286 -14ZM188 567Q185 573 192 593T214 639T250 688T297 728Q324 744 350 744Q386 744 411 723T436 670Q436 644 421 618T379 577Q347 559 315 553T255 548T210 555T188 567Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="650" d="M368 -14Q292 -14 230 11T123 81T54 188T29 324Q29 397 53 459T123 566T230 636T369 661Q438 661 486 645T564 605T609 554T623 503Q623 471 606 451T567 420T527 406T508 405Q489 440 456 459T380 478Q349 478 323 467T277 436T246 388T235 324Q235 289 246 261T276 212T322 180T380 169Q427 169 460 189T510 247Q511 249 529 246T569 232T608 201T626 149Q626 125 612 97T567 44T487 3T368 -14ZM463 689Q432 689 405 713T359 781Q339 738 312 714T255 689Q227 689 211 705T194 747Q194 773 211 798T253 844T307 880T359 902Q382 896 411 881T465 844T507 798T524 747Q524 722 508 706T463 689Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="512" d="M286 -14Q227 -14 178 5T95 58T41 141T22 247Q22 303 41 350T96 432T179 486T286 505Q345 505 385 492T450 458T486 414T497 373Q497 344 482 327T447 301T412 290T395 291Q384 320 361 338T297 356Q252 356 223 326T194 247Q194 198 222 167T297 135Q335 135 359 153T395 201Q396 204 412 203T447 192T481 165T497 117Q497 99 486 76T449 33T383 0T286 -14ZM390 541Q359 541 332 565T286 633Q266 590 239 566T182 541Q154 541 138 557T121 599Q121 625 138 650T180 696T234 732T286 754Q309 748 338 733T392 696T434 650T451 599Q451 574 435 558T390 541Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="650" d="M368 -14Q292 -14 230 11T123 81T54 188T29 324Q29 397 53 459T123 566T230 636T369 661Q438 661 486 645T564 605T609 554T623 503Q623 471 606 451T567 420T527 406T508 405Q489 440 456 459T380 478Q349 478 323 467T277 436T246 388T235 324Q235 289 246 261T276 212T322 180T380 169Q427 169 460 189T510 247Q511 249 529 246T569 232T608 201T626 149Q626 125 612 97T567 44T487 3T368 -14ZM364 689Q320 689 295 714T269 780Q269 819 295 845T366 871Q410 871 435 845T461 780Q461 740 435 715T364 689Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="512" d="M286 -14Q227 -14 178 5T95 58T41 141T22 247Q22 303 41 350T96 432T179 486T286 505Q345 505 385 492T450 458T486 414T497 373Q497 344 482 327T447 301T412 290T395 291Q384 320 361 338T297 356Q252 356 223 326T194 247Q194 198 222 167T297 135Q335 135 359 153T395 201Q396 204 412 203T447 192T481 165T497 117Q497 99 486 76T449 33T383 0T286 -14ZM282 541Q238 541 213 566T187 632Q187 671 213 697T284 723Q328 723 353 697T379 632Q379 592 353 567T282 541Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="650" d="M368 -14Q292 -14 230 11T123 81T54 188T29 324Q29 397 53 459T123 566T230 636T369 661Q438 661 486 645T564 605T609 554T623 503Q623 471 606 451T567 420T527 406T508 405Q489 440 456 459T380 478Q349 478 323 467T277 436T246 388T235 324Q235 289 246 261T276 212T322 180T380 169Q427 169 460 189T510 247Q511 249 529 246T569 232T608 201T626 149Q626 125 612 97T567 44T487 3T368 -14ZM259 891Q289 891 316 866T363 797Q382 841 409 866T467 891Q495 891 511 875T528 833Q528 807 511 782T469 736T415 699T363 677Q340 683 311 698T257 735T215 782T198 833Q198 858 214 874T259 891Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="512" d="M286 -14Q227 -14 178 5T95 58T41 141T22 247Q22 303 41 350T96 432T179 486T286 505Q345 505 385 492T450 458T486 414T497 373Q497 344 482 327T447 301T412 290T395 291Q384 320 361 338T297 356Q252 356 223 326T194 247Q194 198 222 167T297 135Q335 135 359 153T395 201Q396 204 412 203T447 192T481 165T497 117Q497 99 486 76T449 33T383 0T286 -14ZM179 743Q209 743 236 718T283 649Q302 693 329 718T387 743Q415 743 431 727T448 685Q448 659 431 634T389 588T335 551T283 529Q260 535 231 550T177 587T135 634T118 685Q118 710 134 726T179 743Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="690" d="M46 536Q46 590 75 618T150 647H321Q401 647 464 625T572 561T641 460T665 327Q665 252 641 192T571 89T462 23T317 0H46V536ZM312 170Q383 170 425 210T467 326Q467 399 426 438T315 477H244V170H312ZM216 891Q246 891 273 866T320 797Q339 841 366 866T424 891Q452 891 468 875T485 833Q485 807 468 782T426 736T372 699T320 677Q297 683 268 698T214 735T172 782T155 833Q155 858 171 874T216 891Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="862" d="M248 -14Q197 -14 156 5T84 58T38 141T22 247Q22 307 38 354T84 436T156 488T250 506Q297 506 334 487T394 438V682Q394 687 416 695T467 704Q488 704 509 698T547 675T574 630T585 558V0H394V76Q374 34 337 10T248 -14ZM728 433Q715 419 697 417T662 421T637 442T631 477L666 622Q670 642 675 657T690 681T714 696T752 701H773Q808 701 827 683T847 622V615Q847 566 804 522L728 433ZM302 135Q342 135 368 158T394 222V300Q382 326 359 341T304 357Q259 357 232 328T204 246Q204 196 231 166T302 135Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="731" d="M67 257Q35 257 19 275T3 317V333Q3 356 19 374T67 392H84V536Q84 590 113 618T188 647H361Q440 647 504 625T613 561T682 460T706 327Q706 252 682 192T612 89T502 23T357 0H196Q173 0 153 8T117 32T93 67T84 110V257H67ZM353 171Q424 171 466 211T509 326Q509 399 468 437T356 476H283V392H349Q382 392 397 374T412 333V317Q412 293 397 275T349 257H283V171H353Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="629" d="M248 -14Q197 -14 156 5T84 58T38 141T22 247Q22 307 38 354T84 436T156 488T250 506Q297 506 334 487T394 438V552H333Q294 552 294 580V592Q294 621 333 621H394V682Q394 687 416 695T467 704Q483 704 499 700T531 687T558 661T577 621H611Q650 621 650 592V580Q650 566 640 559T611 552H585V0H394V76Q374 34 337 10T248 -14ZM302 135Q340 135 366 157T394 218V300Q382 326 359 341T304 357Q259 357 232 328T204 246Q204 196 231 166T302 135Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM197 694Q161 694 140 711T119 759V767Q119 797 140 814T197 832H431Q467 832 488 815T509 767V759Q509 728 489 711T431 694H197H197Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM171 546Q135 546 114 563T93 611V619Q93 649 114 666T171 684H405Q441 684 462 667T483 619V611Q483 580 463 563T405 546H171H171Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM322 689Q279 689 241 703T176 742Q159 759 149 778T139 817Q139 842 157 861T207 880Q230 875 246 863T275 837T299 813T322 802Q335 802 345 812T369 836T398 862T437 880Q469 880 487 861T505 817Q505 777 469 744Q444 719 405 704T322 689Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM288 541Q245 541 207 555T142 594Q125 611 115 630T105 669Q105 694 123 713T173 732Q196 727 212 715T241 689T265 665T288 654Q301 654 311 664T335 688T364 714T403 732Q435 732 453 713T471 669Q471 629 435 596Q410 571 371 556T288 541Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM323 689Q279 689 254 714T228 780Q228 819 254 845T325 871Q369 871 394 845T420 780Q420 740 394 715T323 689Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM283 541Q239 541 214 566T188 632Q188 671 214 697T285 723Q329 723 354 697T380 632Q380 592 354 567T283 541Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="616" d="M148 0Q102 0 74 25T46 101V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H545Q516 -28 505 -45T493 -77Q493 -106 521 -106Q542 -106 553 -88Q569 -90 579 -104T589 -137Q589 -167 566 -187T493 -207Q437 -207 405 -183T373 -113Q373 -78 399 -48T467 0H148H148Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 53 465 23Q431 -9 418 -28T404 -63Q404 -92 432 -92Q453 -92 464 -74Q480 -76 490 -90T500 -123Q500 -153 477 -173T404 -193Q348 -193 316 -169T284 -99Q284 -75 296 -53T332 -13Q324 -13 316 -13T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="616" d="M46 0V536Q46 590 72 618T148 647H581V475H246V408H496V248H246V170H582V0H46ZM217 891Q247 891 274 866T321 797Q340 841 367 866T425 891Q453 891 469 875T486 833Q486 807 469 782T427 736T373 699T321 677Q298 683 269 698T215 735T173 782T156 833Q156 858 172 874T217 891Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="563" d="M298 -14Q235 -14 184 3T97 54T42 134T22 242Q22 299 41 347T96 431T180 486T286 506Q343 506 390 488T472 436T525 358T544 259V247Q543 225 535 211T505 197H192Q201 158 231 139T307 119Q352 119 382 137T427 182Q427 183 440 181T470 170T501 146T515 102Q515 52 460 19T298 -14ZM284 381Q246 381 220 357T189 296H371Q370 334 347 357T284 381ZM185 743Q215 743 242 718T289 649Q308 693 335 718T393 743Q421 743 437 727T454 685Q454 659 437 634T395 588T341 551T289 529Q266 535 237 550T183 587T141 634T124 685Q124 710 140 726T185 743Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="728" d="M624 -5Q594 -5 566 10T525 71Q499 33 452 11T337 -11Q267 -11 210 14T113 83T51 189T29 324Q29 397 54 459T126 566T238 636T383 661Q444 661 491 650T572 618T622 571T639 512Q639 485 624 466T591 437T557 422T539 421Q513 454 476 472T395 490Q360 490 331 478T279 442T244 387T231 314Q231 274 242 242T274 187T323 152T386 139Q439 139 472 161T513 224H360V371H622Q665 371 678 350T691 286V15Q691 10 671 3T624 -5ZM472 689Q441 689 414 713T368 781Q348 738 321 714T264 689Q236 689 220 705T203 747Q203 773 220 798T262 844T316 880T368 902Q391 896 420 881T474 844T516 798T533 747Q533 722 517 706T472 689Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="629" d="M293 -195Q233 -195 189 -184T115 -153T72 -108T57 -56Q57 -35 67 -19T90 7T114 23T127 26Q151 -5 186 -23T279 -41Q342 -41 371 -17T401 59V104Q374 69 335 49T244 28Q193 28 152 44T82 91T38 166T22 268Q22 321 38 364T85 439T156 488T248 506Q297 506 336 491T401 451V496H586V66Q586 -69 515 -132T293 -195ZM300 172Q345 172 372 193T401 250V315Q385 335 360 348T302 361Q257 361 231 335T204 266Q204 223 230 198T300 172ZM424 541Q393 541 366 565T320 633Q300 590 273 566T216 541Q188 541 172 557T155 599Q155 625 172 650T214 696T268 732T320 754Q343 748 372 733T426 696T468 650T485 599Q485 574 469 558T424 541Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="728" d="M624 -5Q594 -5 566 10T525 71Q499 33 452 11T337 -11Q267 -11 210 14T113 83T51 189T29 324Q29 397 54 459T126 566T238 636T383 661Q444 661 491 650T572 618T622 571T639 512Q639 485 624 466T591 437T557 422T539 421Q513 454 476 472T395 490Q360 490 331 478T279 442T244 387T231 314Q231 274 242 242T274 187T323 152T386 139Q439 139 472 161T513 224H360V371H622Q665 371 678 350T691 286V15Q691 10 671 3T624 -5ZM366 689Q323 689 285 703T220 742Q203 759 193 778T183 817Q183 842 201 861T251 880Q274 875 290 863T319 837T343 813T366 802Q379 802 389 812T413 836T442 862T481 880Q513 880 531 861T549 817Q549 777 513 744Q488 719 449 704T366 689Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="629" d="M293 -195Q233 -195 189 -184T115 -153T72 -108T57 -56Q57 -35 67 -19T90 7T114 23T127 26Q151 -5 186 -23T279 -41Q342 -41 371 -17T401 59V104Q374 69 335 49T244 28Q193 28 152 44T82 91T38 166T22 268Q22 321 38 364T85 439T156 488T248 506Q297 506 336 491T401 451V496H586V66Q586 -69 515 -132T293 -195ZM300 172Q345 172 372 193T401 250V315Q385 335 360 348T302 361Q257 361 231 335T204 266Q204 223 230 198T300 172ZM326 541Q283 541 245 555T180 594Q163 611 153 630T143 669Q143 694 161 713T211 732Q234 727 250 715T279 689T303 665T326 654Q339 654 349 664T373 688T402 714T441 732Q473 732 491 713T509 669Q509 629 473 596Q448 571 409 556T326 541Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="728" d="M624 -5Q594 -5 566 10T525 71Q499 33 452 11T337 -11Q267 -11 210 14T113 83T51 189T29 324Q29 397 54 459T126 566T238 636T383 661Q444 661 491 650T572 618T622 571T639 512Q639 485 624 466T591 437T557 422T539 421Q513 454 476 472T395 490Q360 490 331 478T279 442T244 387T231 314Q231 274 242 242T274 187T323 152T386 139Q439 139 472 161T513 224H360V371H622Q665 371 678 350T691 286V15Q691 10 671 3T624 -5ZM369 689Q325 689 300 714T274 780Q274 819 300 845T371 871Q415 871 440 845T466 780Q466 740 440 715T369 689Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="629" d="M293 -195Q233 -195 189 -184T115 -153T72 -108T57 -56Q57 -35 67 -19T90 7T114 23T127 26Q151 -5 186 -23T279 -41Q342 -41 371 -17T401 59V104Q374 69 335 49T244 28Q193 28 152 44T82 91T38 166T22 268Q22 321 38 364T85 439T156 488T248 506Q297 506 336 491T401 451V496H586V66Q586 -69 515 -132T293 -195ZM300 172Q345 172 372 193T401 250V315Q385 335 360 348T302 361Q257 361 231 335T204 266Q204 223 230 198T300 172ZM310 541Q266 541 241 566T215 632Q215 671 241 697T312 723Q356 723 381 697T407 632Q407 592 381 567T310 541Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="728" d="M624 -5Q594 -5 566 10T525 71Q499 33 452 11T337 -11Q267 -11 210 14T113 83T51 189T29 324Q29 397 54 459T126 566T238 636T383 661Q444 661 491 650T572 618T622 571T639 512Q639 485 624 466T591 437T557 422T539 421Q513 454 476 472T395 490Q360 490 331 478T279 442T244 387T231 314Q231 274 242 242T274 187T323 152T386 139Q439 139 472 161T513 224H360V371H622Q665 371 678 350T691 286V15Q691 10 671 3T624 -5ZM367 -287Q353 -301 335 -303T301 -299T276 -278T270 -244L305 -111Q309 -92 314 -77T329 -53T353 -38T391 -33H412Q448 -33 467 -51T487 -110V-118Q487 -142 476 -163T442 -208L367 -287Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="629" d="M331 788Q344 801 362 803T397 800T422 779T428 745L393 612Q389 592 384 578T369 554T345 539T307 534H286Q250 534 231 552T211 611V619Q211 643 222 664T256 709L331 788ZM293 -195Q233 -195 189 -184T115 -153T72 -108T57 -56Q57 -35 67 -19T90 7T114 23T127 26Q151 -5 186 -23T279 -41Q342 -41 371 -17T401 59V104Q374 69 335 49T244 28Q193 28 152 44T82 91T38 166T22 268Q22 321 38 364T85 439T156 488T248 506Q297 506 336 491T401 451V496H586V66Q586 -69 515 -132T293 -195ZM300 172Q345 172 372 193T401 250V315Q385 335 360 348T302 361Q257 361 231 335T204 266Q204 223 230 198T300 172Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="700" d="M46 559Q46 652 130 652H159Q247 652 247 559V409H454V559Q454 652 538 652H568Q655 652 655 559V0H454V241H247V0H46V559ZM453 689Q422 689 395 713T349 781Q329 738 302 714T245 689Q217 689 201 705T184 747Q184 773 201 798T243 844T297 880T349 902Q372 896 401 881T455 844T497 798T514 747Q514 722 498 706T453 689Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="606" d="M236 417Q258 455 295 480T391 506Q439 506 472 489T527 444T557 380T567 303V0H375V273Q375 309 358 329T312 350Q275 350 256 329T236 269V0H44V682Q44 687 66 695T118 704Q139 704 160 698T198 675T225 630T236 558V417ZM230 712Q199 712 172 736T126 804Q106 761 79 737T22 712Q-6 712 -22 728T-39 770Q-39 796 -22 821T20 867T74 903T126 925Q149 919 178 904T232 867T274 821T291 770Q291 745 275 729T230 712Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="700" d="M10 477Q-29 477 -29 506V518Q-29 532 -19 539T10 546H46V559Q46 652 130 652H159Q247 652 247 559V546H454V559Q454 652 538 652H568Q655 652 655 559V546H695Q734 546 734 518V506Q734 477 695 477H655V0H454V241H247V0H46V477H10ZM454 409V477H247V409H454Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="606" d="M19 552Q-21 552 -21 581V593Q-21 621 19 621H44V682Q44 687 66 695T118 704Q134 704 150 700T182 687T209 661T228 621H296Q335 621 335 593V581Q335 552 296 552H236V417Q258 455 295 480T391 506Q439 506 472 489T527 444T557 380T567 303V0H375V273Q375 309 358 329T312 350Q275 350 256 329T236 269V0H44V552H19H19Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566ZM36 694Q0 694 -21 711T-42 759V767Q-42 797 -21 814T36 832H270Q306 832 327 815T348 767V759Q348 728 328 711T270 694H36H36Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="286" d="M47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409ZM26 546Q-10 546 -31 563T-52 611V619Q-52 649 -31 666T26 684H260Q296 684 317 667T338 619V611Q338 580 318 563T260 546H26H26Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H209Q180 -28 168 -46T156 -78Q156 -107 184 -107Q205 -107 216 -89Q232 -91 242 -105T252 -138Q252 -168 229 -188T156 -208Q100 -208 68 -184T36 -114Q36 -79 62 -48T133 0H53V566Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="286" d="M145 552Q97 552 69 576T41 643Q41 685 68 709T142 733Q190 733 218 709T246 643Q246 601 219 577T145 552ZM47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H198Q168 -29 156 -47T144 -79Q144 -108 172 -108Q193 -108 204 -90Q220 -92 230 -106T240 -139Q240 -169 217 -189T144 -209Q88 -209 56 -185T24 -115Q24 -80 51 -49T123 0H47V409Z" />
+<glyph unicode="&#x130;" glyph-name="Idot" horiz-adv-x="306" d="M53 566Q53 609 76 630T137 651H166Q205 651 229 630T253 566V0H53V566ZM152 689Q108 689 83 714T57 780Q57 819 83 845T154 871Q198 871 223 845T249 780Q249 740 223 715T152 689Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="286" d="M47 409Q47 461 71 481T133 501H153Q192 501 215 482T239 409V0H47V409Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="538" d="M229 -14Q124 -14 70 20T15 115Q15 143 25 162T47 194T69 211T81 215Q129 156 199 156Q246 156 266 179T286 260V647H487V257Q487 117 422 52T229 -14ZM487 689Q456 689 429 713T383 781Q363 738 336 714T279 689Q251 689 235 705T218 747Q218 773 235 798T277 844T331 880T383 902Q406 896 435 881T489 844T531 798T548 747Q548 722 532 706T487 689Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="286" d="M247 541Q216 541 189 565T143 633Q123 590 96 566T39 541Q11 541 -5 557T-22 599Q-22 625 -5 650T37 696T91 732T143 754Q166 748 195 733T249 696T291 650T308 599Q308 574 292 558T247 541ZM58 -198Q-7 -198 -42 -174T-78 -100Q-78 -85 -73 -72T-61 -49T-48 -34T-41 -29Q-33 -40 -20 -45T7 -50Q47 -50 47 -5V496H239V-15Q239 -103 193 -150T58 -198Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="652" d="M46 559Q46 652 130 652H159Q246 652 246 559V367L402 584Q428 622 456 637T512 652Q537 652 559 642T596 619T620 594T626 577L445 329L635 69Q638 64 630 52T605 28T565 5T514 -5Q485 -5 458 9T404 62L246 281V0H46V559ZM293 -287Q279 -301 261 -303T227 -299T202 -278T196 -244L231 -111Q235 -92 240 -77T255 -53T279 -38T317 -33H338Q374 -33 393 -51T413 -110V-118Q413 -142 402 -163T368 -208L293 -287Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="561" d="M44 682Q44 687 66 695T118 704Q139 704 160 698T198 675T225 630T236 558V296L335 445Q355 477 383 490T436 503Q458 503 477 496T511 478T534 458T540 444L407 255L553 50Q555 46 547 37T523 17T488 0T447 -8Q421 -8 393 5T343 50L236 205V0H44V682ZM263 -287Q249 -301 231 -303T197 -299T172 -278T166 -244L201 -111Q205 -92 210 -77T225 -53T249 -38T287 -33H308Q344 -33 363 -51T383 -110V-118Q383 -142 372 -163T338 -208L263 -287Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="552" d="M46 559Q46 652 130 652H159Q246 652 246 559V174H443Q535 174 535 91V88Q535 0 443 0H46V559ZM63 715Q60 721 67 741T89 787T125 836T172 876Q199 892 225 892Q261 892 286 871T311 818Q311 792 296 766T254 725Q222 707 190 701T130 696T85 703T63 715Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="286" d="M47 682Q47 687 69 695T121 704Q142 704 163 698T201 675T228 630T239 558V0H47V682ZM63 756Q60 762 67 782T89 828T125 877T172 917Q199 933 225 933Q261 933 286 912T311 859Q311 833 296 807T254 766Q222 748 190 742T130 737T85 744T63 756Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="552" d="M46 559Q46 652 130 652H159Q246 652 246 559V174H443Q535 174 535 91V88Q535 0 443 0H46V559ZM266 -287Q252 -301 234 -303T200 -299T175 -278T169 -244L204 -111Q208 -92 213 -77T228 -53T252 -38T290 -33H311Q347 -33 366 -51T386 -110V-118Q386 -142 375 -163T341 -208L266 -287Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="286" d="M47 682Q47 687 69 695T121 704Q142 704 163 698T201 675T228 630T239 558V0H47V682ZM120 -287Q106 -301 88 -303T54 -299T29 -278T23 -244L58 -111Q62 -92 67 -77T82 -53T106 -38T144 -33H165Q201 -33 220 -51T240 -110V-118Q240 -142 229 -163T195 -208L120 -287Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="552" d="M436 388Q423 374 405 372T370 376T345 397T339 432L374 577Q378 597 383 612T398 636T422 651T460 656H481Q516 656 535 638T555 577V570Q555 521 512 477L436 388ZM46 559Q46 652 130 652H159Q246 652 246 559V174H443Q535 174 535 91V88Q535 0 443 0H46V559Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="517" d="M385 438Q372 424 354 422T319 426T294 447T288 482L323 627Q327 647 332 662T347 686T371 701T409 706H430Q465 706 484 688T504 627V620Q504 571 461 527L385 438ZM47 682Q47 687 69 695T121 704Q142 704 163 698T201 675T228 630T239 558V0H47V682Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="751" d="M46 559Q46 652 130 652H159Q246 652 246 559V174H443Q535 174 535 91V88Q535 0 443 0H46V559ZM597 237Q515 237 515 318V334Q515 415 597 415H620Q703 415 703 334V318Q703 237 620 237H597Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="496" d="M47 682Q47 687 69 695T121 704Q142 704 163 698T201 675T228 630T239 558V0H47V682ZM379 232Q297 232 297 313V329Q297 410 379 410H402Q485 410 485 329V313Q485 232 402 232H379Z" />
+<glyph unicode="&#x141;" glyph-name="Lslash" horiz-adv-x="585" d="M41 182Q38 181 32 187T21 203T11 228T6 257Q6 271 8 283T19 306T43 326T84 345V559Q84 652 169 652H197Q285 652 285 559V407L414 447Q418 448 424 442T435 426T445 401T449 372Q449 357 445 344T431 319T401 297T351 277L285 256V174H479Q572 174 572 91V87Q572 0 479 0H187Q141 0 113 25T84 101V195L41 182H41Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="371" d="M41 183Q38 182 32 188T20 205T10 230T6 259Q6 272 8 284T20 308T45 329T89 348V682Q89 687 111 695T162 704Q183 704 204 698T242 675T269 630T280 558V407L332 423Q335 424 341 418T353 402T363 377T367 348Q367 319 351 297T280 256V77Q280 30 255 11T196 -8H176Q158 -8 143 -4T115 10T96 36T89 77V197L41 183H41Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="703" d="M46 559Q46 606 68 629T128 652H147Q165 652 177 650T199 641T217 626T237 605L469 303V647H657V88Q657 41 636 18T576 -5H560Q542 -5 530 -3T508 5T490 18T471 39L234 346V0H46V559ZM259 715Q256 721 263 741T285 787T321 836T368 876Q395 892 421 892Q457 892 482 871T507 818Q507 792 492 766T450 725Q418 707 386 701T326 696T281 703T259 715Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="606" d="M118 501Q134 501 151 497T183 483T209 457T228 415Q254 455 292 480T391 506Q439 506 472 490T527 447T557 385T567 310V0H375V277Q375 311 358 330T311 350Q285 350 266 339T236 310V0H44V479Q44 484 66 492T118 501ZM212 567Q209 573 216 593T238 639T274 688T321 728Q348 744 374 744Q410 744 435 723T460 670Q460 644 445 618T403 577Q371 559 339 553T279 548T234 555T212 567Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="703" d="M46 559Q46 606 68 629T128 652H147Q165 652 177 650T199 641T217 626T237 605L469 303V647H657V88Q657 41 636 18T576 -5H560Q542 -5 530 -3T508 5T490 18T471 39L234 346V0H46V559ZM331 -287Q317 -301 299 -303T265 -299T240 -278T234 -244L269 -111Q273 -92 278 -77T293 -53T317 -38T355 -33H376Q412 -33 431 -51T451 -110V-118Q451 -142 440 -163T406 -208L331 -287Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="606" d="M118 501Q134 501 151 497T183 483T209 457T228 415Q254 455 292 480T391 506Q439 506 472 490T527 447T557 385T567 310V0H375V277Q375 311 358 330T311 350Q285 350 266 339T236 310V0H44V479Q44 484 66 492T118 501ZM274 -287Q260 -301 242 -303T208 -299T183 -278T177 -244L212 -111Q216 -92 221 -77T236 -53T260 -38T298 -33H319Q355 -33 374 -51T394 -110V-118Q394 -142 383 -163T349 -208L274 -287Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="703" d="M46 559Q46 606 68 629T128 652H147Q165 652 177 650T199 641T217 626T237 605L469 303V647H657V88Q657 41 636 18T576 -5H560Q542 -5 530 -3T508 5T490 18T471 39L234 346V0H46V559ZM242 891Q272 891 299 866T346 797Q365 841 392 866T450 891Q478 891 494 875T511 833Q511 807 494 782T452 736T398 699T346 677Q323 683 294 698T240 735T198 782T181 833Q181 858 197 874T242 891Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="606" d="M118 501Q134 501 151 497T183 483T209 457T228 415Q254 455 292 480T391 506Q439 506 472 490T527 447T557 385T567 310V0H375V277Q375 311 358 330T311 350Q285 350 266 339T236 310V0H44V479Q44 484 66 492T118 501ZM185 743Q215 743 242 718T289 649Q308 693 335 718T393 743Q421 743 437 727T454 685Q454 659 437 634T395 588T341 551T289 529Q266 535 237 550T183 587T141 634T124 685Q124 710 140 726T185 743Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM258 694Q222 694 201 711T180 759V767Q180 797 201 814T258 832H492Q528 832 549 815T570 767V759Q570 728 550 711T492 694H258H258Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM178 546Q142 546 121 563T100 611V619Q100 649 121 666T178 684H412Q448 684 469 667T490 619V611Q490 580 470 563T412 546H178H178Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM367 689Q324 689 286 703T221 742Q204 759 194 778T184 817Q184 842 202 861T252 880Q275 875 291 863T320 837T344 813T367 802Q380 802 390 812T414 836T443 862T482 880Q514 880 532 861T550 817Q550 777 514 744Q489 719 450 704T367 689Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM295 541Q252 541 214 555T149 594Q132 611 122 630T112 669Q112 694 130 713T180 732Q203 727 219 715T248 689T272 665T295 654Q308 654 318 664T342 688T371 714T410 732Q442 732 460 713T478 669Q478 629 442 596Q417 571 378 556T295 541Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="733" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q442 661 504 636T612 567T682 460T708 324Q708 250 683 188T612 81T505 11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165ZM417 715Q414 721 421 741T443 787T479 836T526 876Q553 892 579 892Q615 892 640 871T665 818Q665 792 650 766T608 725Q576 707 544 701T484 696T439 703T417 715ZM170 715Q167 721 174 741T195 787T231 836T278 876Q307 892 332 892Q368 892 393 871T418 818Q418 792 403 766T361 725Q329 707 297 701T237 696T192 703T170 715Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="586" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q356 506 405 488T490 435T544 352T564 246Q564 187 545 139T490 57T404 5T292 -14ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133ZM307 567Q304 573 311 593T333 639T369 688T416 728Q443 744 469 744Q505 744 530 723T555 670Q555 644 540 618T498 577Q466 559 434 553T374 548T329 555T307 567ZM60 567Q57 573 64 593T85 639T121 688T168 728Q197 744 222 744Q258 744 283 723T308 670Q308 644 293 618T251 577Q219 559 187 553T127 548T82 555T60 567Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1030" d="M367 -14Q292 -14 229 11T121 81T51 188T25 324Q25 397 50 459T121 566T229 636T367 661Q396 661 421 658T472 647H992V475H667Q679 463 687 446T697 408H906V248H698Q696 225 688 205T666 170H993V0H472Q447 -8 421 -11T367 -14ZM367 165Q432 165 471 208T510 324Q510 396 471 440T367 484Q301 484 262 441T223 324Q223 252 262 209T367 165Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="942" d="M292 -14Q230 -14 181 4T96 57T41 139T22 247Q22 305 41 353T97 435T183 487T295 506Q353 506 400 486T479 429Q547 506 665 506Q722 506 769 488T851 436T904 358T923 259V247Q922 225 914 211T884 197H571Q580 158 610 139T686 119Q731 119 761 137T806 182Q806 183 819 181T849 170T880 146T894 102Q894 52 839 19T677 -14Q541 -14 475 60Q443 25 397 6T292 -14ZM663 381Q625 381 599 357T568 296H750Q749 334 726 357T663 381ZM293 133Q340 133 370 165T401 246Q401 297 371 329T293 361Q245 361 215 329T185 247Q185 196 215 165T293 133Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="650" d="M509 -5Q486 -5 468 0T434 15T404 42T374 80L282 202H246V0H46V536Q46 590 72 618T148 647H348Q484 647 551 593T618 435Q618 364 583 315T478 245L624 64Q626 61 621 50T601 27T564 5T509 -5ZM243 344H327Q372 344 394 363T417 419Q417 495 325 495H243V344ZM212 715Q209 721 216 741T238 787T274 836T321 876Q348 892 374 892Q410 892 435 871T460 818Q460 792 445 766T403 725Q371 707 339 701T279 696T234 703T212 715Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="467" d="M118 501Q134 501 151 497T183 483T210 456T228 413Q242 455 272 480T348 506Q396 506 424 479T453 403Q453 374 444 355T423 323T401 306T390 302Q378 313 360 320T321 328Q236 328 236 206V0H44V479Q44 484 66 492T118 501ZM160 567Q157 573 164 593T186 639T222 688T269 728Q296 744 322 744Q358 744 383 723T408 670Q408 644 393 618T351 577Q319 559 287 553T227 548T182 555T160 567Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="650" d="M509 -5Q486 -5 468 0T434 15T404 42T374 80L282 202H246V0H46V536Q46 590 72 618T148 647H348Q484 647 551 593T618 435Q618 364 583 315T478 245L624 64Q626 61 621 50T601 27T564 5T509 -5ZM243 344H327Q372 344 394 363T417 419Q417 495 325 495H243V344ZM300 -287Q286 -301 268 -303T234 -299T209 -278T203 -244L238 -111Q242 -92 247 -77T262 -53T286 -38T324 -33H345Q381 -33 400 -51T420 -110V-118Q420 -142 409 -163T375 -208L300 -287Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="467" d="M118 501Q134 501 151 497T183 483T210 456T228 413Q242 455 272 480T348 506Q396 506 424 479T453 403Q453 374 444 355T423 323T401 306T390 302Q378 313 360 320T321 328Q236 328 236 206V0H44V479Q44 484 66 492T118 501ZM113 -287Q99 -301 81 -303T47 -299T22 -278T16 -244L51 -111Q55 -92 60 -77T75 -53T99 -38T137 -33H158Q194 -33 213 -51T233 -110V-118Q233 -142 222 -163T188 -208L113 -287Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="650" d="M509 -5Q486 -5 468 0T434 15T404 42T374 80L282 202H246V0H46V536Q46 590 72 618T148 647H348Q484 647 551 593T618 435Q618 364 583 315T478 245L624 64Q626 61 621 50T601 27T564 5T509 -5ZM243 344H327Q372 344 394 363T417 419Q417 495 325 495H243V344ZM203 891Q233 891 260 866T307 797Q326 841 353 866T411 891Q439 891 455 875T472 833Q472 807 455 782T413 736T359 699T307 677Q284 683 255 698T201 735T159 782T142 833Q142 858 158 874T203 891Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="467" d="M118 501Q134 501 151 497T183 483T210 456T228 413Q242 455 272 480T348 506Q396 506 424 479T453 403Q453 374 444 355T423 323T401 306T390 302Q378 313 360 320T321 328Q236 328 236 206V0H44V479Q44 484 66 492T118 501ZM137 743Q167 743 194 718T241 649Q260 693 287 718T345 743Q373 743 389 727T406 685Q406 659 389 634T347 588T293 551T241 529Q218 535 189 550T135 587T93 634T76 685Q76 710 92 726T137 743Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="588" d="M277 -14Q206 -14 157 -3T77 27T33 69T19 119Q19 137 28 155T50 186T72 208T85 214Q101 200 123 187T170 163T223 146T277 139Q324 139 345 152T367 190Q367 205 358 213T334 227T297 235T251 244Q210 252 172 266T102 303T53 361T34 446Q34 546 105 603T310 661Q374 661 420 651T496 624T540 584T554 538Q554 517 544 498T520 466T496 444T483 438Q451 468 405 488T312 508Q225 508 225 458Q225 444 234 437T259 425T297 417T345 408Q393 397 434 381T505 341T552 284T569 206Q569 100 497 43T277 -14ZM196 715Q193 721 200 741T222 787T258 836T305 876Q332 892 358 892Q394 892 419 871T444 818Q444 792 429 766T387 725Q355 707 323 701T263 696T218 703T196 715Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="492" d="M235 -11Q119 -11 66 19T13 102Q13 118 20 132T36 156T52 171T62 176Q88 152 128 135T220 118Q250 118 266 123T282 143Q282 161 260 166T201 180Q178 185 148 193T90 218T45 263T26 337Q26 375 41 406T85 459T154 493T244 505Q352 505 404 475T456 392Q456 375 449 361T431 336T413 320T403 315Q376 342 337 359T259 376Q231 376 216 371T200 349Q200 332 224 328T286 314Q310 309 341 301T400 275T448 229T468 157Q468 78 407 34T235 -11ZM158 567Q155 573 162 593T184 639T220 688T267 728Q294 744 320 744Q356 744 381 723T406 670Q406 644 391 618T349 577Q317 559 285 553T225 548T180 555T158 567Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="588" d="M277 -14Q206 -14 157 -3T77 27T33 69T19 119Q19 137 28 155T50 186T72 208T85 214Q101 200 123 187T170 163T223 146T277 139Q324 139 345 152T367 190Q367 205 358 213T334 227T297 235T251 244Q210 252 172 266T102 303T53 361T34 446Q34 546 105 603T310 661Q374 661 420 651T496 624T540 584T554 538Q554 517 544 498T520 466T496 444T483 438Q451 468 405 488T312 508Q225 508 225 458Q225 444 234 437T259 425T297 417T345 408Q393 397 434 381T505 341T552 284T569 206Q569 100 497 43T277 -14ZM399 689Q368 689 341 713T295 781Q275 738 248 714T191 689Q163 689 147 705T130 747Q130 773 147 798T189 844T243 880T295 902Q318 896 347 881T401 844T443 798T460 747Q460 722 444 706T399 689Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="492" d="M235 -11Q119 -11 66 19T13 102Q13 118 20 132T36 156T52 171T62 176Q88 152 128 135T220 118Q250 118 266 123T282 143Q282 161 260 166T201 180Q178 185 148 193T90 218T45 263T26 337Q26 375 41 406T85 459T154 493T244 505Q352 505 404 475T456 392Q456 375 449 361T431 336T413 320T403 315Q376 342 337 359T259 376Q231 376 216 371T200 349Q200 332 224 328T286 314Q310 309 341 301T400 275T448 229T468 157Q468 78 407 34T235 -11ZM351 541Q320 541 293 565T247 633Q227 590 200 566T143 541Q115 541 99 557T82 599Q82 625 99 650T141 696T195 732T247 754Q270 748 299 733T353 696T395 650T412 599Q412 574 396 558T351 541Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="588" d="M277 -14Q206 -14 157 -3T77 27T33 69T19 119Q19 137 28 155T50 186T72 208T85 214Q101 200 123 187T170 163T223 146T277 139Q324 139 345 152T367 190Q367 205 358 213T334 227T297 235T251 244Q210 252 172 266T102 303T53 361T34 446Q34 546 105 603T310 661Q374 661 420 651T496 624T540 584T554 538Q554 517 544 498T520 466T496 444T483 438Q451 468 405 488T312 508Q225 508 225 458Q225 444 234 437T259 425T297 417T345 408Q393 397 434 381T505 341T552 284T569 206Q569 100 497 43T277 -14ZM264 -221Q250 -230 232 -231T198 -229T173 -216T168 -193L202 -96Q206 -83 212 -73T227 -56T251 -46T289 -43H309Q345 -43 365 -56T385 -95V-100Q385 -117 374 -131T339 -162L264 -221Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="492" d="M235 -11Q119 -11 66 19T13 102Q13 118 20 132T36 156T52 171T62 176Q88 152 128 135T220 118Q250 118 266 123T282 143Q282 161 260 166T201 180Q178 185 148 193T90 218T45 263T26 337Q26 375 41 406T85 459T154 493T244 505Q352 505 404 475T456 392Q456 375 449 361T431 336T413 320T403 315Q376 342 337 359T259 376Q231 376 216 371T200 349Q200 332 224 328T286 314Q310 309 341 301T400 275T448 229T468 157Q468 78 407 34T235 -11ZM201 -221Q187 -230 169 -231T135 -229T110 -216T105 -193L139 -96Q143 -83 149 -73T164 -56T188 -46T226 -43H246Q282 -43 302 -56T322 -95V-100Q322 -117 311 -131T276 -162L201 -221Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="588" d="M277 -14Q206 -14 157 -3T77 27T33 69T19 119Q19 137 28 155T50 186T72 208T85 214Q101 200 123 187T170 163T223 146T277 139Q324 139 345 152T367 190Q367 205 358 213T334 227T297 235T251 244Q210 252 172 266T102 303T53 361T34 446Q34 546 105 603T310 661Q374 661 420 651T496 624T540 584T554 538Q554 517 544 498T520 466T496 444T483 438Q451 468 405 488T312 508Q225 508 225 458Q225 444 234 437T259 425T297 417T345 408Q393 397 434 381T505 341T552 284T569 206Q569 100 497 43T277 -14ZM200 898Q230 898 257 873T304 804Q323 848 350 873T408 898Q436 898 452 882T469 840Q469 814 452 789T410 743T356 706T304 684Q281 690 252 705T198 742T156 789T139 840Q139 865 155 881T200 898Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="492" d="M235 -11Q119 -11 66 19T13 102Q13 118 20 132T36 156T52 171T62 176Q88 152 128 135T220 118Q250 118 266 123T282 143Q282 161 260 166T201 180Q178 185 148 193T90 218T45 263T26 337Q26 375 41 406T85 459T154 493T244 505Q352 505 404 475T456 392Q456 375 449 361T431 336T413 320T403 315Q376 342 337 359T259 376Q231 376 216 371T200 349Q200 332 224 328T286 314Q310 309 341 301T400 275T448 229T468 157Q468 78 407 34T235 -11ZM138 743Q168 743 195 718T242 649Q261 693 288 718T346 743Q374 743 390 727T407 685Q407 659 390 634T348 588T294 551T242 529Q219 535 190 550T136 587T94 634T77 685Q77 710 93 726T138 743Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="619" d="M102 473Q10 473 10 561V564Q10 647 102 647H517Q610 647 610 564V561Q610 473 517 473H412V0H211V473H102ZM292 -287Q278 -301 260 -303T226 -299T201 -278T195 -244L230 -111Q234 -92 239 -77T254 -53T278 -38T316 -33H337Q373 -33 392 -51T412 -110V-118Q412 -142 401 -163T367 -208L292 -287Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="439" d="M16 496H98V544Q98 590 120 613T184 637H200Q238 637 262 617T286 544V496H411V351H282V185Q282 135 324 135Q344 135 356 143T379 165Q380 167 387 163T401 150T415 127T421 95Q421 47 381 17T272 -14Q183 -14 141 30T98 164V351H16V496ZM223 -287Q209 -301 191 -303T157 -299T132 -278T126 -244L161 -111Q165 -92 170 -77T185 -53T209 -38T247 -33H268Q304 -33 323 -51T343 -110V-118Q343 -142 332 -163T298 -208L223 -287Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="619" d="M102 473Q10 473 10 561V564Q10 647 102 647H517Q610 647 610 564V561Q610 473 517 473H412V0H211V473H102H102ZM205 891Q235 891 262 866T309 797Q328 841 355 866T413 891Q441 891 457 875T474 833Q474 807 457 782T415 736T361 699T309 677Q286 683 257 698T203 735T161 782T144 833Q144 858 160 874T205 891Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="445" d="M415 546Q403 533 386 531T353 535T328 555T322 588L356 727Q360 746 365 760T379 784T402 798T438 803H458Q492 803 510 785T529 727V721Q529 673 488 632L415 546ZM16 496H98V544Q98 590 120 613T184 637H200Q238 637 262 617T286 544V496H411V351H282V185Q282 135 324 135Q344 135 356 143T379 165Q380 167 387 163T401 150T415 127T421 95Q421 47 381 17T272 -14Q183 -14 141 30T98 164V351H16V496Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM225 694Q189 694 168 711T147 759V767Q147 797 168 814T225 832H459Q495 832 516 815T537 767V759Q537 728 517 711T459 694H225H225Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM186 546Q150 546 129 563T108 611V619Q108 649 129 666T186 684H420Q456 684 477 667T498 619V611Q498 580 478 563T420 546H186H186Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM340 689Q297 689 259 703T194 742Q177 759 167 778T157 817Q157 842 175 861T225 880Q248 875 264 863T293 837T317 813T340 802Q353 802 363 812T387 836T416 862T455 880Q487 880 505 861T523 817Q523 777 487 744Q462 719 423 704T340 689Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM300 541Q257 541 219 555T154 594Q137 611 127 630T117 669Q117 694 135 713T185 732Q208 727 224 715T253 689T277 665T300 654Q313 654 323 664T347 688T376 714T415 732Q447 732 465 713T483 669Q483 629 447 596Q422 571 383 556T300 541Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM341 689Q281 689 246 724T211 815Q211 842 220 865T247 905T289 931T343 941Q372 941 396 932T437 905T463 865T473 815Q473 788 464 765T437 725T395 699T341 689ZM342 767Q362 767 375 781T388 815Q388 835 375 849T342 864Q322 864 309 850T296 815Q296 794 309 781T342 767Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM300 528Q240 528 205 563T170 654Q170 681 179 704T206 744T248 770T302 780Q331 780 355 771T396 744T422 704T432 654Q432 627 423 604T396 564T354 538T300 528ZM301 606Q321 606 334 620T347 654Q347 674 334 688T301 703Q281 703 268 689T255 654Q255 633 268 620T301 606Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 137 562 63T339 -11ZM377 715Q374 721 381 741T403 787T439 836T486 876Q513 892 539 892Q575 892 600 871T625 818Q625 792 610 766T568 725Q536 707 504 701T444 696T399 703T377 715ZM130 715Q127 721 134 741T155 787T191 836T238 876Q267 892 292 892Q328 892 353 871T378 818Q378 792 363 766T321 725Q289 707 257 701T197 696T152 703T130 715Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="606" d="M488 -5Q471 -5 455 -1T423 12T397 38T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 15 556 11T540 4T516 -2T488 -5ZM336 567Q333 573 340 593T362 639T398 688T445 728Q472 744 498 744Q534 744 559 723T584 670Q584 644 569 618T527 577Q495 559 463 553T403 548T358 555T336 567ZM89 567Q86 573 93 593T114 639T150 688T197 728Q226 744 251 744Q287 744 312 723T337 670Q337 644 322 618T280 577Q248 559 216 553T156 548T111 555T89 567Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="679" d="M339 -11Q43 -11 43 284V559Q43 606 66 629T131 652H156Q244 652 244 559V283Q244 163 340 163Q389 163 412 192T435 284V559Q435 607 458 629T523 652H549Q636 652 636 559V284Q636 119 540 45Q500 9 485 -12T469 -50Q469 -79 497 -79Q518 -79 529 -61Q545 -63 555 -77T565 -110Q565 -140 542 -160T469 -180Q413 -180 381 -156T349 -86Q349 -65 359 -46T386 -9Q375 -10 363 -10T339 -11Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="606" d="M368 -117Q368 -82 394 -51T464 -3Q437 2 413 19T378 78Q352 38 314 12T215 -14Q167 -14 134 2T79 46T49 108T39 182V496H231V216Q231 182 248 163T296 143Q321 143 340 154T370 182V496H562V17Q562 13 554 10Q517 -25 503 -45T488 -81Q488 -110 516 -110Q537 -110 548 -92Q564 -94 574 -108T584 -141Q584 -171 561 -191T488 -211Q432 -211 400 -187T368 -117Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="983" d="M30 582Q29 585 36 596T60 619T99 640T155 650Q170 650 187 645T219 628T246 596T265 543L319 264L387 517Q395 555 425 572T497 590Q535 590 565 571T604 517L672 264L726 546Q731 575 742 595T768 627T800 644T835 650Q865 650 887 641T924 620T946 597T952 582L784 0H589L493 338L399 0H205L30 582ZM596 684Q565 684 538 708T492 776Q472 733 445 709T388 684Q360 684 344 700T327 742Q327 768 344 793T386 839T440 875T492 897Q515 891 544 876T598 839T640 793T657 742Q657 717 641 701T596 684Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="809" d="M10 447Q8 451 19 460T47 479T85 496T127 503Q140 503 154 500T180 488T201 466T214 430L256 194L319 402Q327 431 353 446T413 462Q446 462 471 447T504 402L559 204L601 430Q609 472 633 487T684 503Q705 503 726 496T763 480T790 461T799 447L652 0H485L405 250L327 0H160L10 447ZM509 539Q478 539 451 563T405 631Q385 588 358 564T301 539Q273 539 257 555T240 597Q240 623 257 648T299 694T353 730T405 752Q428 746 457 731T511 694T553 648T570 597Q570 572 554 556T509 539Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="639" d="M221 269L-16 647H203L327 430L414 581Q437 619 460 635T509 651Q543 651 568 641T608 617T629 591T633 575L421 253V0H221V269ZM424 689Q393 689 366 713T320 781Q300 738 273 714T216 689Q188 689 172 705T155 747Q155 773 172 798T214 844T268 880T320 902Q343 896 372 881T426 844T468 798T485 747Q485 722 469 706T424 689Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="570" d="M243 -188Q222 -188 203 -181T169 -165T146 -148T140 -135L222 36Q205 40 192 48T173 72L-9 496H195L290 199L379 496H579L345 -103Q326 -151 300 -169T243 -188ZM392 541Q361 541 334 565T288 633Q268 590 241 566T184 541Q156 541 140 557T123 599Q123 625 140 650T182 696T236 732T288 754Q311 748 340 733T394 696T436 650T453 599Q453 574 437 558T392 541Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="639" d="M221 269L-16 647H203L327 430L414 581Q437 619 460 635T509 651Q543 651 568 641T608 617T629 591T633 575L421 253V0H221V269ZM434 696Q392 696 368 718T344 784Q344 826 367 849T431 872Q472 872 496 849T520 784Q520 742 496 719T434 696ZM208 696Q166 696 142 718T118 784Q118 826 142 849T204 872Q246 872 270 849T295 784Q295 742 271 719T208 696Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="621" d="M45 114L329 482H132Q88 482 66 499T44 560V569Q44 611 66 629T132 647H580V543L281 166H509Q553 166 575 148T597 87V79Q597 37 575 19T509 0H45V114ZM237 715Q234 721 241 741T263 787T299 836T346 876Q373 892 399 892Q435 892 460 871T485 818Q485 792 470 766T428 725Q396 707 364 701T304 696T259 703T237 715Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="508" d="M23 118L237 352H101Q28 352 28 420V427Q28 460 46 478T101 496H477V380L257 145H415Q487 145 487 77V69Q487 1 415 1H23V118ZM178 567Q175 573 182 593T204 639T240 688T287 728Q314 744 340 744Q376 744 401 723T426 670Q426 644 411 618T369 577Q337 559 305 553T245 548T200 555T178 567Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="621" d="M45 114L329 482H132Q88 482 66 499T44 560V569Q44 611 66 629T132 647H580V543L281 166H509Q553 166 575 148T597 87V79Q597 37 575 19T509 0H45V114ZM320 689Q276 689 251 714T225 780Q225 819 251 845T322 871Q366 871 391 845T417 780Q417 740 391 715T320 689Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="508" d="M23 118L237 352H101Q28 352 28 420V427Q28 460 46 478T101 496H477V380L257 145H415Q487 145 487 77V69Q487 1 415 1H23V118ZM263 541Q219 541 194 566T168 632Q168 671 194 697T265 723Q309 723 334 697T360 632Q360 592 334 567T263 541Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="621" d="M45 114L329 482H132Q88 482 66 499T44 560V569Q44 611 66 629T132 647H580V543L281 166H509Q553 166 575 148T597 87V79Q597 37 575 19T509 0H45V114ZM197 898Q227 898 254 873T301 804Q320 848 347 873T405 898Q433 898 449 882T466 840Q466 814 449 789T407 743T353 706T301 684Q278 690 249 705T195 742T153 789T136 840Q136 865 152 881T197 898Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="508" d="M23 118L237 352H101Q28 352 28 420V427Q28 460 46 478T101 496H477V380L257 145H415Q487 145 487 77V69Q487 1 415 1H23V118ZM143 743Q173 743 200 718T247 649Q266 693 293 718T351 743Q379 743 395 727T412 685Q412 659 395 634T353 588T299 551T247 529Q224 535 195 550T141 587T99 634T82 685Q82 710 98 726T143 743Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="496" d="M102 276Q25 276 25 345V355Q25 420 102 420H133L143 475Q152 527 170 562T212 618T268 649T335 658Q490 658 490 559Q490 539 482 523T465 496T447 480T438 475Q429 486 414 495T378 505Q355 505 343 494T326 453L319 420H355Q433 420 433 352V341Q433 276 355 276H300L272 114Q264 72 250 46T218 6T181 -14T145 -19Q125 -19 110 -16T83 -7T67 3T61 11L108 276H102H102Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="588" d="M277 -14Q206 -14 157 -3T77 27T33 69T19 119Q19 137 28 155T50 186T72 208T85 214Q101 200 123 187T170 163T223 146T277 139Q324 139 345 152T367 190Q367 205 358 213T334 227T297 235T251 244Q210 252 172 266T102 303T53 361T34 446Q34 546 105 603T310 661Q374 661 420 651T496 624T540 584T554 538Q554 517 544 498T520 466T496 444T483 438Q451 468 405 488T312 508Q225 508 225 458Q225 444 234 437T259 425T297 417T345 408Q393 397 434 381T505 341T552 284T569 206Q569 100 497 43T277 -14ZM264 -287Q250 -301 232 -303T198 -299T173 -278T167 -244L202 -111Q206 -92 211 -77T226 -53T250 -38T288 -33H309Q345 -33 364 -51T384 -110V-118Q384 -142 373 -163T339 -208L264 -287Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="492" d="M235 -11Q119 -11 66 19T13 102Q13 118 20 132T36 156T52 171T62 176Q88 152 128 135T220 118Q250 118 266 123T282 143Q282 161 260 166T201 180Q178 185 148 193T90 218T45 263T26 337Q26 375 41 406T85 459T154 493T244 505Q352 505 404 475T456 392Q456 375 449 361T431 336T413 320T403 315Q376 342 337 359T259 376Q231 376 216 371T200 349Q200 332 224 328T286 314Q310 309 341 301T400 275T448 229T468 157Q468 78 407 34T235 -11ZM201 -287Q187 -301 169 -303T135 -299T110 -278T104 -244L139 -111Q143 -92 148 -77T163 -53T187 -38T225 -33H246Q282 -33 301 -51T321 -110V-118Q321 -142 310 -163T276 -208L201 -287Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="500" d="M354 541Q323 541 296 565T250 633Q230 590 203 566T146 541Q118 541 102 557T85 599Q85 625 102 650T144 696T198 732T250 754Q273 748 302 733T356 696T398 650T415 599Q415 574 399 558T354 541Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="500" d="M146 743Q176 743 203 718T250 649Q269 693 296 718T354 743Q382 743 398 727T415 685Q415 659 398 634T356 588T302 551T250 529Q227 535 198 550T144 587T102 634T85 685Q85 710 101 726T146 743Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="500" d="M250 541Q207 541 169 555T104 594Q87 611 77 630T67 669Q67 694 85 713T135 732Q158 727 174 715T203 689T227 665T250 654Q263 654 273 664T297 688T326 714T365 732Q397 732 415 713T433 669Q433 629 397 596Q372 571 333 556T250 541Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="500" d="M249 541Q205 541 180 566T154 632Q154 671 180 697T251 723Q295 723 320 697T346 632Q346 592 320 567T249 541Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="500" d="M249 541Q189 541 154 576T119 667Q119 694 128 717T155 757T197 783T251 793Q280 793 304 784T345 757T371 717T381 667Q381 640 372 617T345 577T303 551T249 541ZM250 619Q270 619 283 633T296 667Q296 687 283 701T250 716Q230 716 217 702T204 667Q204 646 217 633T250 619Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="500" d="M355 20Q314 -18 298 -39T282 -77Q282 -106 310 -106Q331 -106 342 -88Q358 -90 368 -104T378 -137Q378 -167 355 -187T282 -207Q226 -207 194 -183T162 -113Q162 -91 173 -70T205 -31T252 -2T311 14L355 20H355Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="499" d="M325 541Q293 541 269 550T224 576Q216 584 209 589T197 595Q189 595 184 584T172 563Q156 542 129 542Q103 542 86 559T68 601Q68 656 99 686T174 716Q202 716 224 709T265 687Q276 679 285 671T302 662Q310 662 314 672T325 692Q341 714 369 714Q396 714 412 697T431 655Q431 600 400 571T325 541Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="500" d="M282 567Q279 573 286 593T308 639T344 688T391 728Q418 744 444 744Q480 744 505 723T530 670Q530 644 515 618T473 577Q441 559 409 553T349 548T304 555T282 567ZM35 567Q32 573 39 593T60 639T96 688T143 728Q172 744 197 744Q233 744 258 723T283 670Q283 644 268 618T226 577Q194 559 162 553T102 548T57 555T35 567Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="739" d="M370 -14Q295 -14 233 11T127 81T58 188T33 324Q33 397 57 459T126 566T233 636T370 661Q445 661 507 636T613 567T681 460T706 324Q706 250 682 188T613 81T507 11T370 -14ZM370 53Q430 53 479 73T563 129T617 215T636 324Q636 383 617 432T562 518T478 574T370 594Q310 594 261 574T177 518T122 433T103 324Q103 265 122 216T177 130T261 74T370 53ZM275 134Q226 134 226 187V427Q226 458 241 474T286 491H381Q459 491 499 459T539 361Q539 297 500 265T386 232H343V187Q343 134 293 134H275ZM342 317H381Q428 317 428 361Q428 381 416 393T378 405H342V317Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="576" d="M106 167Q68 167 49 188T30 239V255Q30 282 49 304T106 327H463Q501 327 520 305T539 255V239Q539 210 520 189T463 167H106H106Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="738" d="M106 167Q68 167 49 188T30 239V255Q30 282 49 304T106 327H632Q671 327 689 305T708 255V239Q708 210 690 189T632 167H106H106Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="256" d="M95 373Q60 373 41 391T21 452V459Q21 484 31 505T64 552L139 641Q152 654 171 656T206 653T232 632T238 597L202 452Q197 432 192 417T178 393T155 378T117 373H95H95Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="256" d="M116 390Q103 376 85 374T50 378T25 399T19 434L54 579Q58 599 63 614T78 638T102 653T140 658H161Q196 658 215 640T235 579V572Q235 523 192 479L116 390H116Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="256" d="M114 -96Q100 -110 82 -112T48 -108T23 -87T17 -53L51 93Q55 113 61 128T76 152T100 167T138 172H158Q194 172 213 154T233 93V86Q233 60 222 38T190 -7L114 -96H114Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="489" d="M328 373Q293 373 274 391T254 452V459Q254 508 297 552L373 641Q386 654 404 656T438 653T464 632T470 597L435 452Q431 432 426 417T411 392T387 378T349 373H328ZM95 373Q60 373 41 391T21 452V459Q21 485 31 507T64 552L139 641Q152 654 171 656T206 653T232 632T238 597L202 452Q197 432 192 417T178 392T155 378T117 373H95Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="489" d="M349 390Q336 376 318 374T283 378T256 399T251 434L287 579Q291 599 296 614T311 638T334 653T372 658H394Q429 658 448 640T468 579V572Q468 546 457 524T425 479L349 390ZM116 390Q103 376 85 374T50 378T25 399T19 434L54 579Q58 599 63 614T78 638T102 653T140 658H161Q196 658 215 640T235 579V572Q235 523 192 479L116 390Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="489" d="M347 -96Q334 -110 316 -112T281 -109T254 -88T249 -53L285 93Q289 113 294 128T309 152T332 167T369 172H392Q427 172 446 154T465 93V86Q465 60 455 38T422 -7L347 -96ZM113 -96Q100 -110 82 -112T48 -108T23 -87T17 -53L51 93Q55 113 60 128T75 152T99 167T137 172H158Q194 172 213 154T232 93V86Q232 37 189 -7L113 -96Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="540" d="M252 -155Q230 -155 215 -149T198 -110L184 218Q182 259 189 288T220 343L89 309Q71 304 58 309T36 326T23 357T18 398Q18 447 33 472T89 486L219 451L187 589Q168 661 270 661Q324 661 344 644T354 589L322 452L451 486Q469 491 482 486T505 469T518 438T523 398Q523 376 519 358T505 327T483 309T451 309L321 343Q345 317 352 288T357 218L343 -110Q341 -142 326 -148T289 -155H252H252Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="540" d="M252 -155Q230 -155 215 -149T198 -110L192 42L89 15Q49 4 34 29T18 104Q18 126 22 144T35 175T57 193T89 193L187 167L184 233Q182 274 189 303T221 358L89 324Q71 319 58 324T36 341T23 372T18 413Q18 462 33 487T89 501L218 467L187 589Q168 661 270 661Q324 661 344 644T354 589L322 467L451 501Q469 506 482 501T505 484T518 453T523 413Q523 391 519 373T505 342T483 324T451 324L320 358Q344 332 351 303T357 233L354 168L451 193Q469 198 482 193T505 176T518 145T523 104Q523 83 519 65T505 33T483 15T451 15L349 42L343 -110Q341 -142 326 -148T289 -155H252H252Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="363" d="M182 101Q150 101 124 112T78 142T47 188T36 247Q36 278 47 305T77 353T124 384T182 396Q213 396 239 385T285 353T316 306T327 247Q327 217 316 190T285 144T239 113T182 101Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="776" d="M633 -8Q552 -8 552 73V89Q552 170 633 170H656Q700 170 719 151T739 89V73Q739 -8 656 -8H633ZM375 -8Q293 -8 293 73V89Q293 170 375 170H398Q481 170 481 89V73Q481 -8 398 -8H375ZM116 -8Q35 -8 35 73V89Q35 170 116 170H139Q183 170 202 151T222 89V73Q222 -8 139 -8H116Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1124" d="M148 -13Q146 -16 133 -13T104 -1T77 21T64 50Q64 71 89 99L593 658Q596 661 609 658T638 646T665 625T677 596Q677 573 653 546L148 -13ZM180 335Q100 335 53 377T6 492V499Q6 572 53 614T180 657Q259 657 306 615T353 500V493Q353 420 306 378T180 335ZM180 430Q201 430 215 444T230 488V504Q230 534 216 548T180 563Q157 563 144 549T130 504V488Q130 459 144 445T180 430ZM945 -12Q865 -12 818 30T770 145V152Q770 225 818 267T945 310Q1024 310 1071 268T1118 153V146Q1118 73 1071 31T945 -12ZM564 -12Q484 -12 437 30T389 145V152Q389 225 437 267T564 310Q643 310 690 268T737 153V146Q737 73 690 31T564 -12ZM945 83Q966 83 980 97T995 141V157Q995 187 981 201T945 216Q922 216 909 202T895 157V141Q895 111 909 97T945 83ZM564 83Q585 83 599 97T614 141V157Q614 187 600 201T564 216Q541 216 528 202T514 157V141Q514 111 528 97T564 83Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="323" d="M296 366Q296 348 283 331T251 297T211 268T173 247Q189 238 210 225T251 196T283 163T296 128V123Q296 94 279 75T226 55H224Q192 55 158 82T94 143T45 208T26 247Q26 252 35 268T59 305T93 350T135 393T180 426T224 439H226Q261 439 278 420T296 370V366H296Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="323" d="M27 128Q27 146 39 163T71 196T111 225T149 247Q132 255 111 268T71 297T40 330T27 366V370Q27 400 44 419T97 439H99Q120 439 143 426T188 394T229 351T264 306T288 268T297 247Q297 242 288 226T264 189T230 144T188 101T143 68T99 55H97Q62 55 45 74T27 123V128H27Z" />
+<glyph unicode="&#x2044;" glyph-name="fraction" horiz-adv-x="723" d="M144 -10Q139 -15 124 -14T93 -5T64 14T51 44Q51 55 55 66T73 92L569 658Q573 663 588 662T619 654T648 635T661 605Q661 594 656 583T639 557L144 -10H144Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="634" d="M78 338Q46 338 31 352T15 386V400Q15 418 30 432T78 447H104Q116 497 141 537T204 604T289 646T392 661Q498 661 557 623T616 526Q616 502 603 484T574 454T543 437T526 434Q500 466 472 482T396 499Q326 499 296 447H415Q448 447 463 433T478 400V386Q478 366 463 352T415 338H280V308H415Q448 308 463 294T478 261V247Q478 228 463 214T415 199H298Q313 173 338 161T397 148Q445 148 473 165T527 215Q529 217 543 212T574 196T604 166T617 123Q617 99 605 75T566 31T497 -1T394 -14Q281 -14 205 41T105 199H78Q46 199 31 213T15 247V261Q15 280 30 294T78 308H95V338H78H78Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="700" d="M375 324Q334 324 334 373V602Q334 627 343 639T376 652H408Q428 652 437 646T453 625L504 504L555 625Q562 642 571 647T598 652H630Q673 652 673 602V373Q673 349 664 337T632 324H615Q574 324 574 373V498L546 409Q544 400 538 394T503 388Q488 388 481 389T470 394T465 400T462 409L433 494V373Q433 349 425 337T393 324H375ZM56 556Q8 556 8 602V604Q8 647 56 647H258Q306 647 306 604V602Q306 556 258 556H210V373Q210 324 163 324H148Q105 324 105 373V556H56Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="600" d="M119 178Q81 178 62 197T43 249V258Q43 288 62 308T119 328H480Q519 328 537 308T556 258V249Q556 217 538 198T480 178H119H119Z" />
+<glyph unicode="&#x2219;" glyph-name="uni2219" horiz-adv-x="262" d="M118 158Q36 158 36 239V255Q36 336 118 336H141Q224 336 224 255V239Q224 158 141 158H118H118Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="600" d="M119 311Q81 311 62 329T43 376V385Q43 412 62 430T119 449H299L340 568Q357 619 376 633T413 642L437 634Q455 628 461 606T452 531L423 449H480Q519 449 537 431T556 385V376Q556 347 538 329T480 311H366L326 197H480Q519 197 537 179T556 133V124Q556 95 538 77T480 59H286L245 -63Q228 -115 210 -129T173 -138L149 -130Q131 -124 124 -101T133 -27L162 59H119Q81 59 62 77T43 124V133Q43 160 62 178T119 197H219L259 311H119H119Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="600" d="M63 412Q63 446 75 466T113 497L501 640Q504 641 511 635T525 617T539 590T545 556Q546 531 531 507T475 470L293 413L475 359Q517 346 531 322T546 273Q546 255 540 239T527 212T512 194T501 189L113 328Q87 339 75 359T63 412ZM130 29Q92 29 73 47T54 95V103Q54 131 73 149T130 168H469Q508 168 526 150T545 103V95Q545 66 527 48T469 29H130Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="600" d="M55 557Q55 575 60 590T73 617T88 635T99 641L487 498Q537 477 537 413Q537 380 525 360T487 329L99 189Q96 187 89 193T74 212T60 240T54 274Q54 298 69 322T125 360L306 414L125 470Q84 484 69 508T55 557ZM131 30Q92 30 74 48T55 95V104Q55 131 73 149T131 168H470Q508 168 527 150T546 104V95Q546 66 527 48T470 30H131Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="702" d="M560 552Q512 552 485 576T457 643Q457 685 484 709T558 733Q606 733 633 709T661 643Q661 601 634 577T560 552ZM81 351Q3 351 3 420V431Q3 496 81 496H101V510Q101 568 115 606T154 667T214 699T290 709Q365 709 398 682T432 618Q432 602 427 588T415 564T402 549T395 544Q384 554 369 560T334 567Q310 567 298 555T285 512V496H327Q405 496 405 427V416Q405 351 327 351H293V0H101V351H81ZM463 409Q463 461 487 481T549 501H569Q607 501 631 482T655 409V0H463V409Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="702" d="M73 351Q38 351 21 370T3 420V431Q3 460 20 478T73 496H101V510Q101 568 115 606T154 667T214 699T290 709Q365 709 398 682T432 618Q432 602 427 588T415 564T402 549T395 544Q384 554 369 560T334 567Q310 567 298 555T285 512V496H335Q370 496 387 477T405 427V416Q405 387 388 369T335 351H293V0H101V351H73ZM463 682Q463 687 485 695T537 704Q558 704 579 698T617 675T644 630T655 558V0H463V682Z" />
+
+<hkern u1="&#xd0;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd0;" u2="." k="33" />
+<hkern u1="&#xd0;" u2="/" k="50" />
+<hkern u1="&#xd0;" u2="?" k="20" />
+<hkern u1="&#xd0;" u2="A" k="20" />
+<hkern u1="&#xd0;" u2="J" k="23" />
+<hkern u1="&#xd0;" u2="T" k="19" />
+<hkern u1="&#xd0;" u2="V" k="20" />
+<hkern u1="&#xd0;" u2="W" k="33" />
+<hkern u1="&#xd0;" u2="X" k="29" />
+<hkern u1="&#xd0;" u2="Y" k="18" />
+<hkern u1="&#xd0;" u2="Z" k="19" />
+<hkern u1="&#xd0;" u2="a" k="1" />
+<hkern u1="&#xd0;" u2="b" k="3" />
+<hkern u1="&#xd0;" u2="c" k="1" />
+<hkern u1="&#xd0;" u2="d" k="1" />
+<hkern u1="&#xd0;" u2="e" k="1" />
+<hkern u1="&#xd0;" u2="h" k="3" />
+<hkern u1="&#xd0;" u2="k" k="3" />
+<hkern u1="&#xd0;" u2="l" k="3" />
+<hkern u1="&#xd0;" u2="m" k="1" />
+<hkern u1="&#xd0;" u2="n" k="1" />
+<hkern u1="&#xd0;" u2="o" k="1" />
+<hkern u1="&#xd0;" u2="p" k="1" />
+<hkern u1="&#xd0;" u2="q" k="1" />
+<hkern u1="&#xd0;" u2="r" k="1" />
+<hkern u1="&#xd0;" u2="u" k="1" />
+<hkern u1="&#xd0;" u2="x" k="10" />
+<hkern u1="&#xd0;" u2="z" k="10" />
+<hkern u1="&#xd0;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd0;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd0;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd0;" u2="&#x153;" k="1" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd0;" u2="&#x178;" k="18" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd0;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd0;" u2="&#x131;" k="1" />
+<hkern u1="&#x141;" u2="&#xdd;" k="91" />
+<hkern u1="&#x141;" u2="&#x17d;" k="-4" />
+<hkern u1="&#x141;" u2="*" k="45" />
+<hkern u1="&#x141;" u2="?" k="53" />
+<hkern u1="&#x141;" u2="@" k="50" />
+<hkern u1="&#x141;" u2="A" k="-20" />
+<hkern u1="&#x141;" u2="C" k="31" />
+<hkern u1="&#x141;" u2="G" k="31" />
+<hkern u1="&#x141;" u2="J" k="-11" />
+<hkern u1="&#x141;" u2="O" k="31" />
+<hkern u1="&#x141;" u2="Q" k="31" />
+<hkern u1="&#x141;" u2="T" k="96" />
+<hkern u1="&#x141;" u2="U" k="15" />
+<hkern u1="&#x141;" u2="V" k="90" />
+<hkern u1="&#x141;" u2="W" k="70" />
+<hkern u1="&#x141;" u2="Y" k="91" />
+<hkern u1="&#x141;" u2="Z" k="-4" />
+<hkern u1="&#x141;" u2="\" k="50" />
+<hkern u1="&#x141;" u2="a" k="13" />
+<hkern u1="&#x141;" u2="c" k="13" />
+<hkern u1="&#x141;" u2="d" k="13" />
+<hkern u1="&#x141;" u2="e" k="13" />
+<hkern u1="&#x141;" u2="o" k="13" />
+<hkern u1="&#x141;" u2="q" k="13" />
+<hkern u1="&#x141;" u2="v" k="55" />
+<hkern u1="&#x141;" u2="w" k="25" />
+<hkern u1="&#x141;" u2="y" k="55" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd6;" k="31" />
+<hkern u1="&#x141;" u2="&#xdc;" k="15" />
+<hkern u1="&#x141;" u2="&#xe7;" k="13" />
+<hkern u1="&#x141;" u2="&#xae;" k="50" />
+<hkern u1="&#x141;" u2="&#xa9;" k="50" />
+<hkern u1="&#x141;" u2="&#x2122;" k="70" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd8;" k="31" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="50" />
+<hkern u1="&#x141;" u2="&#xe6;" k="13" />
+<hkern u1="&#x141;" u2="&#xab;" k="15" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd5;" k="31" />
+<hkern u1="&#x141;" u2="&#x152;" k="31" />
+<hkern u1="&#x141;" u2="&#x153;" k="13" />
+<hkern u1="&#x141;" u2="&#x201c;" k="100" />
+<hkern u1="&#x141;" u2="&#x201d;" k="68" />
+<hkern u1="&#x141;" u2="&#x2018;" k="100" />
+<hkern u1="&#x141;" u2="&#x2019;" k="68" />
+<hkern u1="&#x141;" u2="&#x178;" k="91" />
+<hkern u1="&#x141;" u2="&#x2039;" k="15" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd3;" k="31" />
+<hkern u1="&#x141;" u2="&#xd4;" k="31" />
+<hkern u1="&#x141;" u2="&#xd2;" k="31" />
+<hkern u1="&#x141;" u2="&#xda;" k="15" />
+<hkern u1="&#x141;" u2="&#xdb;" k="15" />
+<hkern u1="&#x141;" u2="&#xd9;" k="15" />
+<hkern u1="&#x160;" u2="&#xdd;" k="-10" />
+<hkern u1="&#x160;" u2="A" k="-5" />
+<hkern u1="&#x160;" u2="V" k="-6" />
+<hkern u1="&#x160;" u2="W" k="8" />
+<hkern u1="&#x160;" u2="X" k="-5" />
+<hkern u1="&#x160;" u2="Y" k="-10" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-5" />
+<hkern u1="&#x160;" u2="&#x178;" k="-10" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xdd;" u2="&#x160;" k="-10" />
+<hkern u1="&#xdd;" u2="&#x161;" k="54" />
+<hkern u1="&#xdd;" u2="&amp;" k="30" />
+<hkern u1="&#xdd;" u2=")" k="-50" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="50" />
+<hkern u1="&#xdd;" u2="-" k="50" />
+<hkern u1="&#xdd;" u2="." k="50" />
+<hkern u1="&#xdd;" u2="/" k="45" />
+<hkern u1="&#xdd;" u2=":" k="30" />
+<hkern u1="&#xdd;" u2=";" k="30" />
+<hkern u1="&#xdd;" u2="@" k="30" />
+<hkern u1="&#xdd;" u2="A" k="54" />
+<hkern u1="&#xdd;" u2="C" k="18" />
+<hkern u1="&#xdd;" u2="G" k="18" />
+<hkern u1="&#xdd;" u2="J" k="85" />
+<hkern u1="&#xdd;" u2="O" k="18" />
+<hkern u1="&#xdd;" u2="Q" k="18" />
+<hkern u1="&#xdd;" u2="S" k="-10" />
+<hkern u1="&#xdd;" u2="T" k="-28" />
+<hkern u1="&#xdd;" u2="V" k="-23" />
+<hkern u1="&#xdd;" u2="X" k="-13" />
+<hkern u1="&#xdd;" u2="]" k="-50" />
+<hkern u1="&#xdd;" u2="a" k="70" />
+<hkern u1="&#xdd;" u2="c" k="70" />
+<hkern u1="&#xdd;" u2="d" k="70" />
+<hkern u1="&#xdd;" u2="e" k="70" />
+<hkern u1="&#xdd;" u2="f" k="10" />
+<hkern u1="&#xdd;" u2="g" k="40" />
+<hkern u1="&#xdd;" u2="m" k="40" />
+<hkern u1="&#xdd;" u2="n" k="40" />
+<hkern u1="&#xdd;" u2="o" k="70" />
+<hkern u1="&#xdd;" u2="p" k="40" />
+<hkern u1="&#xdd;" u2="q" k="70" />
+<hkern u1="&#xdd;" u2="r" k="40" />
+<hkern u1="&#xdd;" u2="s" k="54" />
+<hkern u1="&#xdd;" u2="t" k="20" />
+<hkern u1="&#xdd;" u2="u" k="40" />
+<hkern u1="&#xdd;" u2="v" k="20" />
+<hkern u1="&#xdd;" u2="w" k="10" />
+<hkern u1="&#xdd;" u2="x" k="20" />
+<hkern u1="&#xdd;" u2="y" k="20" />
+<hkern u1="&#xdd;" u2="z" k="30" />
+<hkern u1="&#xdd;" u2="}" k="-50" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="54" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="54" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="18" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="70" />
+<hkern u1="&#xdd;" u2="&#xae;" k="30" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="54" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="18" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="30" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="70" />
+<hkern u1="&#xdd;" u2="&#xab;" k="60" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="54" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="54" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="18" />
+<hkern u1="&#xdd;" u2="&#x152;" k="18" />
+<hkern u1="&#xdd;" u2="&#x153;" k="70" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="60" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="40" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="50" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="54" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="54" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="18" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="18" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="18" />
+<hkern u1="&#xdd;" u2="&#x131;" k="40" />
+<hkern u1="&#xde;" u2="&#xdd;" k="18" />
+<hkern u1="&#xde;" u2="&#x17d;" k="19" />
+<hkern u1="&#xde;" u2="&#x2c;" k="33" />
+<hkern u1="&#xde;" u2="." k="33" />
+<hkern u1="&#xde;" u2="/" k="50" />
+<hkern u1="&#xde;" u2="?" k="20" />
+<hkern u1="&#xde;" u2="A" k="20" />
+<hkern u1="&#xde;" u2="J" k="23" />
+<hkern u1="&#xde;" u2="T" k="19" />
+<hkern u1="&#xde;" u2="V" k="20" />
+<hkern u1="&#xde;" u2="W" k="33" />
+<hkern u1="&#xde;" u2="X" k="29" />
+<hkern u1="&#xde;" u2="Y" k="18" />
+<hkern u1="&#xde;" u2="Z" k="19" />
+<hkern u1="&#xde;" u2="a" k="1" />
+<hkern u1="&#xde;" u2="b" k="3" />
+<hkern u1="&#xde;" u2="c" k="1" />
+<hkern u1="&#xde;" u2="d" k="1" />
+<hkern u1="&#xde;" u2="e" k="1" />
+<hkern u1="&#xde;" u2="h" k="3" />
+<hkern u1="&#xde;" u2="k" k="3" />
+<hkern u1="&#xde;" u2="l" k="3" />
+<hkern u1="&#xde;" u2="m" k="1" />
+<hkern u1="&#xde;" u2="n" k="1" />
+<hkern u1="&#xde;" u2="o" k="1" />
+<hkern u1="&#xde;" u2="p" k="1" />
+<hkern u1="&#xde;" u2="q" k="1" />
+<hkern u1="&#xde;" u2="r" k="1" />
+<hkern u1="&#xde;" u2="u" k="1" />
+<hkern u1="&#xde;" u2="x" k="10" />
+<hkern u1="&#xde;" u2="z" k="10" />
+<hkern u1="&#xde;" u2="&#xc4;" k="20" />
+<hkern u1="&#xde;" u2="&#xc5;" k="20" />
+<hkern u1="&#xde;" u2="&#xe7;" k="1" />
+<hkern u1="&#xde;" u2="&#xc6;" k="20" />
+<hkern u1="&#xde;" u2="&#xe6;" k="1" />
+<hkern u1="&#xde;" u2="&#x2026;" k="33" />
+<hkern u1="&#xde;" u2="&#xc0;" k="20" />
+<hkern u1="&#xde;" u2="&#xc3;" k="20" />
+<hkern u1="&#xde;" u2="&#x153;" k="1" />
+<hkern u1="&#xde;" u2="&#x201c;" k="20" />
+<hkern u1="&#xde;" u2="&#x2018;" k="20" />
+<hkern u1="&#xde;" u2="&#x178;" k="18" />
+<hkern u1="&#xde;" u2="&#x201a;" k="33" />
+<hkern u1="&#xde;" u2="&#x201e;" k="33" />
+<hkern u1="&#xde;" u2="&#xc2;" k="20" />
+<hkern u1="&#xde;" u2="&#xc1;" k="20" />
+<hkern u1="&#xde;" u2="&#x131;" k="1" />
+<hkern u1="&#x17d;" u2="&#xf0;" k="30" />
+<hkern u1="&#x17d;" u2="&#xfe;" k="8" />
+<hkern u1="&#x17d;" u2="-" k="20" />
+<hkern u1="&#x17d;" u2="A" k="10" />
+<hkern u1="&#x17d;" u2="C" k="15" />
+<hkern u1="&#x17d;" u2="G" k="15" />
+<hkern u1="&#x17d;" u2="J" k="-5" />
+<hkern u1="&#x17d;" u2="O" k="15" />
+<hkern u1="&#x17d;" u2="Q" k="15" />
+<hkern u1="&#x17d;" u2="T" k="-1" />
+<hkern u1="&#x17d;" u2="a" k="18" />
+<hkern u1="&#x17d;" u2="b" k="10" />
+<hkern u1="&#x17d;" u2="c" k="18" />
+<hkern u1="&#x17d;" u2="d" k="18" />
+<hkern u1="&#x17d;" u2="e" k="18" />
+<hkern u1="&#x17d;" u2="f" k="20" />
+<hkern u1="&#x17d;" u2="g" k="20" />
+<hkern u1="&#x17d;" u2="h" k="10" />
+<hkern u1="&#x17d;" u2="i" k="20" />
+<hkern u1="&#x17d;" u2="j" k="15" />
+<hkern u1="&#x17d;" u2="k" k="10" />
+<hkern u1="&#x17d;" u2="l" k="10" />
+<hkern u1="&#x17d;" u2="m" k="20" />
+<hkern u1="&#x17d;" u2="n" k="20" />
+<hkern u1="&#x17d;" u2="o" k="18" />
+<hkern u1="&#x17d;" u2="p" k="20" />
+<hkern u1="&#x17d;" u2="q" k="18" />
+<hkern u1="&#x17d;" u2="r" k="20" />
+<hkern u1="&#x17d;" u2="u" k="20" />
+<hkern u1="&#x17d;" u2="v" k="30" />
+<hkern u1="&#x17d;" u2="w" k="10" />
+<hkern u1="&#x17d;" u2="y" k="30" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="15" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="18" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="15" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="18" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="15" />
+<hkern u1="&#x17d;" u2="&#x152;" k="15" />
+<hkern u1="&#x17d;" u2="&#x153;" k="18" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="20" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="20" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="15" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="15" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="15" />
+<hkern u1="&#x17d;" u2="&#x131;" k="20" />
+<hkern u1="!" u2="a" k="8" />
+<hkern u1="!" u2="c" k="8" />
+<hkern u1="!" u2="d" k="8" />
+<hkern u1="!" u2="e" k="8" />
+<hkern u1="!" u2="o" k="8" />
+<hkern u1="!" u2="q" k="8" />
+<hkern u1="!" u2="&#xe7;" k="8" />
+<hkern u1="!" u2="&#xe6;" k="8" />
+<hkern u1="!" u2="&#x153;" k="8" />
+<hkern u1="&amp;" u2="&#xdd;" k="38" />
+<hkern u1="&amp;" u2="A" k="-20" />
+<hkern u1="&amp;" u2="J" k="-20" />
+<hkern u1="&amp;" u2="T" k="40" />
+<hkern u1="&amp;" u2="V" k="43" />
+<hkern u1="&amp;" u2="W" k="28" />
+<hkern u1="&amp;" u2="X" k="-20" />
+<hkern u1="&amp;" u2="Y" k="38" />
+<hkern u1="&amp;" u2="&#xc4;" k="-21" />
+<hkern u1="&amp;" u2="&#xc5;" k="-21" />
+<hkern u1="&amp;" u2="&#xc6;" k="-21" />
+<hkern u1="&amp;" u2="&#xc0;" k="-21" />
+<hkern u1="&amp;" u2="&#xc3;" k="-21" />
+<hkern u1="&amp;" u2="&#x178;" k="38" />
+<hkern u1="&amp;" u2="&#xc2;" k="-21" />
+<hkern u1="&amp;" u2="&#xc1;" k="-21" />
+<hkern u1="(" u2="&#xdd;" k="-50" />
+<hkern u1="(" u2="7" k="-15" />
+<hkern u1="(" u2="J" k="15" />
+<hkern u1="(" u2="V" k="-30" />
+<hkern u1="(" u2="W" k="-10" />
+<hkern u1="(" u2="X" k="-40" />
+<hkern u1="(" u2="Y" k="-50" />
+<hkern u1="(" u2="g" k="-20" />
+<hkern u1="(" u2="j" k="-150" />
+<hkern u1="(" u2="&#x178;" k="-50" />
+<hkern u1="*" u2="A" k="75" />
+<hkern u1="*" u2="J" k="53" />
+<hkern u1="*" u2="T" k="-11" />
+<hkern u1="*" u2="&#xc4;" k="75" />
+<hkern u1="*" u2="&#xc5;" k="75" />
+<hkern u1="*" u2="&#xc6;" k="75" />
+<hkern u1="*" u2="&#xc0;" k="75" />
+<hkern u1="*" u2="&#xc3;" k="75" />
+<hkern u1="*" u2="&#xc2;" k="75" />
+<hkern u1="*" u2="&#xc1;" k="75" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2c;" u2="0" k="18" />
+<hkern u1="&#x2c;" u2="1" k="86" />
+<hkern u1="&#x2c;" u2="2" k="-8" />
+<hkern u1="&#x2c;" u2="3" k="-8" />
+<hkern u1="&#x2c;" u2="4" k="13" />
+<hkern u1="&#x2c;" u2="5" k="-15" />
+<hkern u1="&#x2c;" u2="6" k="23" />
+<hkern u1="&#x2c;" u2="7" k="8" />
+<hkern u1="&#x2c;" u2="8" k="15" />
+<hkern u1="&#x2c;" u2="9" k="3" />
+<hkern u1="&#x2c;" u2="A" k="-30" />
+<hkern u1="&#x2c;" u2="C" k="33" />
+<hkern u1="&#x2c;" u2="G" k="33" />
+<hkern u1="&#x2c;" u2="O" k="33" />
+<hkern u1="&#x2c;" u2="Q" k="33" />
+<hkern u1="&#x2c;" u2="T" k="83" />
+<hkern u1="&#x2c;" u2="U" k="10" />
+<hkern u1="&#x2c;" u2="V" k="68" />
+<hkern u1="&#x2c;" u2="W" k="28" />
+<hkern u1="&#x2c;" u2="Y" k="50" />
+<hkern u1="&#x2c;" u2="a" k="20" />
+<hkern u1="&#x2c;" u2="c" k="20" />
+<hkern u1="&#x2c;" u2="d" k="20" />
+<hkern u1="&#x2c;" u2="e" k="20" />
+<hkern u1="&#x2c;" u2="f" k="30" />
+<hkern u1="&#x2c;" u2="m" k="20" />
+<hkern u1="&#x2c;" u2="n" k="20" />
+<hkern u1="&#x2c;" u2="o" k="20" />
+<hkern u1="&#x2c;" u2="p" k="20" />
+<hkern u1="&#x2c;" u2="q" k="20" />
+<hkern u1="&#x2c;" u2="r" k="20" />
+<hkern u1="&#x2c;" u2="t" k="40" />
+<hkern u1="&#x2c;" u2="u" k="20" />
+<hkern u1="&#x2c;" u2="v" k="30" />
+<hkern u1="&#x2c;" u2="w" k="10" />
+<hkern u1="&#x2c;" u2="y" k="30" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="33" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="33" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="33" />
+<hkern u1="&#x2c;" u2="&#x152;" k="33" />
+<hkern u1="&#x2c;" u2="&#x153;" k="20" />
+<hkern u1="&#x2c;" u2="&#x178;" k="50" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="33" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="33" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="33" />
+<hkern u1="&#x2c;" u2="&#xda;" k="10" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2c;" u2="&#x131;" k="20" />
+<hkern u1="-" u2="&#xdd;" k="50" />
+<hkern u1="-" u2="&#x17d;" k="10" />
+<hkern u1="-" u2="1" k="20" />
+<hkern u1="-" u2="7" k="30" />
+<hkern u1="-" u2="A" k="-10" />
+<hkern u1="-" u2="T" k="55" />
+<hkern u1="-" u2="V" k="50" />
+<hkern u1="-" u2="W" k="40" />
+<hkern u1="-" u2="X" k="30" />
+<hkern u1="-" u2="Y" k="50" />
+<hkern u1="-" u2="Z" k="10" />
+<hkern u1="-" u2="a" k="10" />
+<hkern u1="-" u2="c" k="10" />
+<hkern u1="-" u2="d" k="10" />
+<hkern u1="-" u2="e" k="10" />
+<hkern u1="-" u2="o" k="10" />
+<hkern u1="-" u2="q" k="10" />
+<hkern u1="-" u2="v" k="10" />
+<hkern u1="-" u2="x" k="30" />
+<hkern u1="-" u2="y" k="10" />
+<hkern u1="-" u2="z" k="15" />
+<hkern u1="-" u2="&#xc4;" k="-10" />
+<hkern u1="-" u2="&#xc5;" k="-10" />
+<hkern u1="-" u2="&#xe7;" k="10" />
+<hkern u1="-" u2="&#xc6;" k="-10" />
+<hkern u1="-" u2="&#xe6;" k="10" />
+<hkern u1="-" u2="&#xc0;" k="-10" />
+<hkern u1="-" u2="&#xc3;" k="-10" />
+<hkern u1="-" u2="&#x153;" k="10" />
+<hkern u1="-" u2="&#x178;" k="50" />
+<hkern u1="-" u2="&#xc2;" k="-10" />
+<hkern u1="-" u2="&#xc1;" k="-10" />
+<hkern u1="." u2="&#xdd;" k="50" />
+<hkern u1="." u2="0" k="18" />
+<hkern u1="." u2="1" k="86" />
+<hkern u1="." u2="2" k="-8" />
+<hkern u1="." u2="3" k="-8" />
+<hkern u1="." u2="4" k="13" />
+<hkern u1="." u2="5" k="-15" />
+<hkern u1="." u2="6" k="23" />
+<hkern u1="." u2="7" k="8" />
+<hkern u1="." u2="8" k="15" />
+<hkern u1="." u2="9" k="3" />
+<hkern u1="." u2="A" k="-30" />
+<hkern u1="." u2="C" k="33" />
+<hkern u1="." u2="G" k="33" />
+<hkern u1="." u2="O" k="33" />
+<hkern u1="." u2="Q" k="33" />
+<hkern u1="." u2="T" k="83" />
+<hkern u1="." u2="U" k="10" />
+<hkern u1="." u2="V" k="68" />
+<hkern u1="." u2="W" k="28" />
+<hkern u1="." u2="Y" k="50" />
+<hkern u1="." u2="a" k="20" />
+<hkern u1="." u2="c" k="20" />
+<hkern u1="." u2="d" k="20" />
+<hkern u1="." u2="e" k="20" />
+<hkern u1="." u2="f" k="30" />
+<hkern u1="." u2="m" k="20" />
+<hkern u1="." u2="n" k="20" />
+<hkern u1="." u2="o" k="20" />
+<hkern u1="." u2="p" k="20" />
+<hkern u1="." u2="q" k="20" />
+<hkern u1="." u2="r" k="20" />
+<hkern u1="." u2="t" k="40" />
+<hkern u1="." u2="u" k="20" />
+<hkern u1="." u2="v" k="30" />
+<hkern u1="." u2="w" k="10" />
+<hkern u1="." u2="y" k="30" />
+<hkern u1="." u2="&#xc4;" k="-30" />
+<hkern u1="." u2="&#xc5;" k="-30" />
+<hkern u1="." u2="&#xd6;" k="33" />
+<hkern u1="." u2="&#xdc;" k="10" />
+<hkern u1="." u2="&#xe7;" k="20" />
+<hkern u1="." u2="&#xc6;" k="-30" />
+<hkern u1="." u2="&#xd8;" k="33" />
+<hkern u1="." u2="&#xe6;" k="20" />
+<hkern u1="." u2="&#xc0;" k="-30" />
+<hkern u1="." u2="&#xc3;" k="-30" />
+<hkern u1="." u2="&#xd5;" k="33" />
+<hkern u1="." u2="&#x152;" k="33" />
+<hkern u1="." u2="&#x153;" k="20" />
+<hkern u1="." u2="&#x178;" k="50" />
+<hkern u1="." u2="&#xc2;" k="-30" />
+<hkern u1="." u2="&#xc1;" k="-30" />
+<hkern u1="." u2="&#xd3;" k="33" />
+<hkern u1="." u2="&#xd4;" k="33" />
+<hkern u1="." u2="&#xd2;" k="33" />
+<hkern u1="." u2="&#xda;" k="10" />
+<hkern u1="." u2="&#xdb;" k="10" />
+<hkern u1="." u2="&#xd9;" k="10" />
+<hkern u1="." u2="&#x131;" k="20" />
+<hkern u1="/" u2="&#xd0;" k="-8" />
+<hkern u1="/" u2="&#x141;" k="-8" />
+<hkern u1="/" u2="&#xde;" k="-8" />
+<hkern u1="/" u2="1" k="-20" />
+<hkern u1="/" u2="2" k="-11" />
+<hkern u1="/" u2="3" k="-15" />
+<hkern u1="/" u2="4" k="5" />
+<hkern u1="/" u2="5" k="-15" />
+<hkern u1="/" u2="7" k="-30" />
+<hkern u1="/" u2="B" k="-8" />
+<hkern u1="/" u2="C" k="-3" />
+<hkern u1="/" u2="D" k="-8" />
+<hkern u1="/" u2="E" k="-8" />
+<hkern u1="/" u2="F" k="-8" />
+<hkern u1="/" u2="G" k="-3" />
+<hkern u1="/" u2="H" k="-8" />
+<hkern u1="/" u2="I" k="-8" />
+<hkern u1="/" u2="J" k="38" />
+<hkern u1="/" u2="K" k="-8" />
+<hkern u1="/" u2="L" k="-8" />
+<hkern u1="/" u2="M" k="-8" />
+<hkern u1="/" u2="N" k="-8" />
+<hkern u1="/" u2="O" k="-3" />
+<hkern u1="/" u2="P" k="-8" />
+<hkern u1="/" u2="Q" k="-3" />
+<hkern u1="/" u2="R" k="-8" />
+<hkern u1="/" u2="a" k="10" />
+<hkern u1="/" u2="c" k="10" />
+<hkern u1="/" u2="d" k="10" />
+<hkern u1="/" u2="e" k="10" />
+<hkern u1="/" u2="g" k="18" />
+<hkern u1="/" u2="o" k="10" />
+<hkern u1="/" u2="q" k="10" />
+<hkern u1="/" u2="&#xc9;" k="-8" />
+<hkern u1="/" u2="&#xd1;" k="-8" />
+<hkern u1="/" u2="&#xd6;" k="-3" />
+<hkern u1="/" u2="&#xe7;" k="10" />
+<hkern u1="/" u2="&#xd8;" k="-3" />
+<hkern u1="/" u2="&#xe6;" k="10" />
+<hkern u1="/" u2="&#xd5;" k="-3" />
+<hkern u1="/" u2="&#x152;" k="-3" />
+<hkern u1="/" u2="&#x153;" k="10" />
+<hkern u1="/" u2="&#xca;" k="-8" />
+<hkern u1="/" u2="&#xcb;" k="-8" />
+<hkern u1="/" u2="&#xc8;" k="-8" />
+<hkern u1="/" u2="&#xcd;" k="-8" />
+<hkern u1="/" u2="&#xce;" k="-8" />
+<hkern u1="/" u2="&#xcf;" k="-8" />
+<hkern u1="/" u2="&#xcc;" k="-8" />
+<hkern u1="/" u2="&#xd3;" k="-3" />
+<hkern u1="/" u2="&#xd4;" k="-3" />
+<hkern u1="/" u2="&#xd2;" k="-3" />
+<hkern u1="0" u2="&#x2c;" k="18" />
+<hkern u1="0" u2="." k="18" />
+<hkern u1="0" u2="7" k="20" />
+<hkern u1="0" u2="&#x2026;" k="18" />
+<hkern u1="0" u2="&#x201a;" k="18" />
+<hkern u1="0" u2="&#x201e;" k="18" />
+<hkern u1="1" u2="&#x2c;" k="-8" />
+<hkern u1="1" u2="." k="-8" />
+<hkern u1="1" u2="7" k="-8" />
+<hkern u1="1" u2="&#x2026;" k="-8" />
+<hkern u1="1" u2="&#x201a;" k="-8" />
+<hkern u1="1" u2="&#x201e;" k="-8" />
+<hkern u1="2" u2="-" k="10" />
+<hkern u1="2" u2="&#x2013;" k="10" />
+<hkern u1="2" u2="&#x2014;" k="10" />
+<hkern u1="3" u2="&#x2c;" k="5" />
+<hkern u1="3" u2="." k="5" />
+<hkern u1="3" u2="/" k="15" />
+<hkern u1="3" u2="7" k="1" />
+<hkern u1="3" u2="&#x2026;" k="5" />
+<hkern u1="3" u2="&#x201a;" k="5" />
+<hkern u1="3" u2="&#x201e;" k="5" />
+<hkern u1="4" u2="&#x2c;" k="-15" />
+<hkern u1="4" u2="." k="-15" />
+<hkern u1="4" u2="/" k="8" />
+<hkern u1="4" u2="1" k="23" />
+<hkern u1="4" u2="7" k="18" />
+<hkern u1="4" u2="&#xb0;" k="10" />
+<hkern u1="4" u2="&#x2122;" k="43" />
+<hkern u1="4" u2="&#x2026;" k="-15" />
+<hkern u1="4" u2="&#x201c;" k="30" />
+<hkern u1="4" u2="&#x201d;" k="23" />
+<hkern u1="4" u2="&#x2018;" k="30" />
+<hkern u1="4" u2="&#x2019;" k="23" />
+<hkern u1="4" u2="&#x201a;" k="-15" />
+<hkern u1="4" u2="&#x201e;" k="-15" />
+<hkern u1="5" u2="&#x2c;" k="10" />
+<hkern u1="5" u2="." k="10" />
+<hkern u1="5" u2="/" k="8" />
+<hkern u1="5" u2="7" k="-5" />
+<hkern u1="5" u2="&#x2026;" k="10" />
+<hkern u1="5" u2="&#x201a;" k="10" />
+<hkern u1="5" u2="&#x201e;" k="10" />
+<hkern u1="6" u2="&#x2c;" k="3" />
+<hkern u1="6" u2="." k="3" />
+<hkern u1="6" u2="7" k="10" />
+<hkern u1="6" u2="&#x2026;" k="3" />
+<hkern u1="6" u2="&#x201a;" k="3" />
+<hkern u1="6" u2="&#x201e;" k="3" />
+<hkern u1="7" u2=")" k="-8" />
+<hkern u1="7" u2="&#x2c;" k="80" />
+<hkern u1="7" u2="-" k="40" />
+<hkern u1="7" u2="." k="80" />
+<hkern u1="7" u2="/" k="68" />
+<hkern u1="7" u2="0" k="6" />
+<hkern u1="7" u2="1" k="-20" />
+<hkern u1="7" u2="3" k="-16" />
+<hkern u1="7" u2="4" k="23" />
+<hkern u1="7" u2="5" k="-15" />
+<hkern u1="7" u2="6" k="4" />
+<hkern u1="7" u2="8" k="4" />
+<hkern u1="7" u2="9" k="-10" />
+<hkern u1="7" u2=":" k="20" />
+<hkern u1="7" u2=";" k="20" />
+<hkern u1="7" u2="?" k="-8" />
+<hkern u1="7" u2="]" k="-8" />
+<hkern u1="7" u2="}" k="-8" />
+<hkern u1="7" u2="&#xa2;" k="40" />
+<hkern u1="7" u2="&#x2026;" k="80" />
+<hkern u1="7" u2="&#x2013;" k="40" />
+<hkern u1="7" u2="&#x2014;" k="40" />
+<hkern u1="7" u2="&#x201a;" k="80" />
+<hkern u1="7" u2="&#x201e;" k="80" />
+<hkern u1="8" u2="&#x2c;" k="15" />
+<hkern u1="8" u2="." k="15" />
+<hkern u1="8" u2="7" k="15" />
+<hkern u1="8" u2="&#x2026;" k="15" />
+<hkern u1="8" u2="&#x201a;" k="15" />
+<hkern u1="8" u2="&#x201e;" k="15" />
+<hkern u1="9" u2="&#x2c;" k="23" />
+<hkern u1="9" u2="." k="23" />
+<hkern u1="9" u2="7" k="19" />
+<hkern u1="9" u2="&#x2026;" k="23" />
+<hkern u1="9" u2="&#x201a;" k="23" />
+<hkern u1="9" u2="&#x201e;" k="23" />
+<hkern u1=":" u2="&#xdd;" k="30" />
+<hkern u1=":" u2="1" k="30" />
+<hkern u1=":" u2="7" k="20" />
+<hkern u1=":" u2="T" k="50" />
+<hkern u1=":" u2="V" k="20" />
+<hkern u1=":" u2="W" k="20" />
+<hkern u1=":" u2="Y" k="30" />
+<hkern u1=":" u2="a" k="10" />
+<hkern u1=":" u2="c" k="10" />
+<hkern u1=":" u2="d" k="10" />
+<hkern u1=":" u2="e" k="10" />
+<hkern u1=":" u2="o" k="10" />
+<hkern u1=":" u2="q" k="10" />
+<hkern u1=":" u2="&#xe7;" k="10" />
+<hkern u1=":" u2="&#xe6;" k="10" />
+<hkern u1=":" u2="&#x153;" k="10" />
+<hkern u1=":" u2="&#x178;" k="30" />
+<hkern u1=";" u2="&#xdd;" k="30" />
+<hkern u1=";" u2="1" k="30" />
+<hkern u1=";" u2="7" k="20" />
+<hkern u1=";" u2="T" k="50" />
+<hkern u1=";" u2="V" k="20" />
+<hkern u1=";" u2="W" k="20" />
+<hkern u1=";" u2="Y" k="30" />
+<hkern u1=";" u2="a" k="10" />
+<hkern u1=";" u2="c" k="10" />
+<hkern u1=";" u2="d" k="10" />
+<hkern u1=";" u2="e" k="10" />
+<hkern u1=";" u2="o" k="10" />
+<hkern u1=";" u2="q" k="10" />
+<hkern u1=";" u2="&#xe7;" k="10" />
+<hkern u1=";" u2="&#xe6;" k="10" />
+<hkern u1=";" u2="&#x153;" k="10" />
+<hkern u1=";" u2="&#x178;" k="30" />
+<hkern u1="&gt;" u2="7" k="15" />
+<hkern u1="@" u2="&#xdd;" k="30" />
+<hkern u1="@" u2="3" k="8" />
+<hkern u1="@" u2="A" k="10" />
+<hkern u1="@" u2="T" k="30" />
+<hkern u1="@" u2="V" k="8" />
+<hkern u1="@" u2="W" k="20" />
+<hkern u1="@" u2="X" k="23" />
+<hkern u1="@" u2="Y" k="30" />
+<hkern u1="@" u2="g" k="-8" />
+<hkern u1="@" u2="&#xc4;" k="10" />
+<hkern u1="@" u2="&#xc5;" k="10" />
+<hkern u1="@" u2="&#xc6;" k="10" />
+<hkern u1="@" u2="&#xc0;" k="10" />
+<hkern u1="@" u2="&#xc3;" k="10" />
+<hkern u1="@" u2="&#x178;" k="30" />
+<hkern u1="@" u2="&#xc2;" k="10" />
+<hkern u1="@" u2="&#xc1;" k="10" />
+<hkern u1="A" u2="&#x160;" k="-4" />
+<hkern u1="A" u2="&#x161;" k="-11" />
+<hkern u1="A" u2="&#xdd;" k="41" />
+<hkern u1="A" u2="*" k="60" />
+<hkern u1="A" u2="&#x2c;" k="-23" />
+<hkern u1="A" u2="-" k="-8" />
+<hkern u1="A" u2="." k="-23" />
+<hkern u1="A" u2="?" k="23" />
+<hkern u1="A" u2="@" k="8" />
+<hkern u1="A" u2="A" k="-8" />
+<hkern u1="A" u2="C" k="15" />
+<hkern u1="A" u2="G" k="15" />
+<hkern u1="A" u2="J" k="-8" />
+<hkern u1="A" u2="O" k="15" />
+<hkern u1="A" u2="Q" k="15" />
+<hkern u1="A" u2="S" k="-4" />
+<hkern u1="A" u2="T" k="41" />
+<hkern u1="A" u2="V" k="60" />
+<hkern u1="A" u2="W" k="49" />
+<hkern u1="A" u2="X" k="-8" />
+<hkern u1="A" u2="Y" k="41" />
+<hkern u1="A" u2="a" k="-8" />
+<hkern u1="A" u2="c" k="4" />
+<hkern u1="A" u2="d" k="4" />
+<hkern u1="A" u2="e" k="4" />
+<hkern u1="A" u2="f" k="30" />
+<hkern u1="A" u2="g" k="11" />
+<hkern u1="A" u2="m" k="4" />
+<hkern u1="A" u2="n" k="4" />
+<hkern u1="A" u2="o" k="4" />
+<hkern u1="A" u2="p" k="4" />
+<hkern u1="A" u2="q" k="4" />
+<hkern u1="A" u2="r" k="4" />
+<hkern u1="A" u2="s" k="-11" />
+<hkern u1="A" u2="t" k="4" />
+<hkern u1="A" u2="u" k="4" />
+<hkern u1="A" u2="v" k="53" />
+<hkern u1="A" u2="w" k="38" />
+<hkern u1="A" u2="x" k="-8" />
+<hkern u1="A" u2="y" k="53" />
+<hkern u1="A" u2="z" k="-8" />
+<hkern u1="A" u2="&#xc4;" k="-8" />
+<hkern u1="A" u2="&#xc5;" k="-8" />
+<hkern u1="A" u2="&#xd6;" k="15" />
+<hkern u1="A" u2="&#xe7;" k="4" />
+<hkern u1="A" u2="&#xae;" k="8" />
+<hkern u1="A" u2="&#xa9;" k="8" />
+<hkern u1="A" u2="&#x2122;" k="53" />
+<hkern u1="A" u2="&#xc6;" k="-8" />
+<hkern u1="A" u2="&#xd8;" k="15" />
+<hkern u1="A" u2="&#x3c0;" k="8" />
+<hkern u1="A" u2="&#xe6;" k="4" />
+<hkern u1="A" u2="&#x2026;" k="-23" />
+<hkern u1="A" u2="&#xc0;" k="-8" />
+<hkern u1="A" u2="&#xc3;" k="-8" />
+<hkern u1="A" u2="&#xd5;" k="15" />
+<hkern u1="A" u2="&#x152;" k="15" />
+<hkern u1="A" u2="&#x153;" k="4" />
+<hkern u1="A" u2="&#x2013;" k="-8" />
+<hkern u1="A" u2="&#x2014;" k="-8" />
+<hkern u1="A" u2="&#x201c;" k="45" />
+<hkern u1="A" u2="&#x201d;" k="23" />
+<hkern u1="A" u2="&#x2018;" k="45" />
+<hkern u1="A" u2="&#x2019;" k="23" />
+<hkern u1="A" u2="&#x178;" k="41" />
+<hkern u1="A" u2="&#x201a;" k="-23" />
+<hkern u1="A" u2="&#x201e;" k="-23" />
+<hkern u1="A" u2="&#xc2;" k="-8" />
+<hkern u1="A" u2="&#xc1;" k="-8" />
+<hkern u1="A" u2="&#xd3;" k="15" />
+<hkern u1="A" u2="&#xd4;" k="15" />
+<hkern u1="A" u2="&#xd2;" k="15" />
+<hkern u1="A" u2="&#x131;" k="4" />
+<hkern u1="B" u2="V" k="5" />
+<hkern u1="B" u2="W" k="20" />
+<hkern u1="B" u2="a" k="-8" />
+<hkern u1="B" u2="c" k="-8" />
+<hkern u1="B" u2="d" k="-8" />
+<hkern u1="B" u2="e" k="-8" />
+<hkern u1="B" u2="o" k="-8" />
+<hkern u1="B" u2="q" k="-8" />
+<hkern u1="B" u2="v" k="11" />
+<hkern u1="B" u2="w" k="8" />
+<hkern u1="B" u2="y" k="11" />
+<hkern u1="B" u2="&#xe7;" k="-8" />
+<hkern u1="B" u2="&#xe6;" k="-8" />
+<hkern u1="B" u2="&#x153;" k="-8" />
+<hkern u1="C" u2="&#xdd;" k="-20" />
+<hkern u1="C" u2="C" k="8" />
+<hkern u1="C" u2="G" k="8" />
+<hkern u1="C" u2="O" k="8" />
+<hkern u1="C" u2="Q" k="8" />
+<hkern u1="C" u2="V" k="-20" />
+<hkern u1="C" u2="X" k="-10" />
+<hkern u1="C" u2="Y" k="-20" />
+<hkern u1="C" u2="a" k="-1" />
+<hkern u1="C" u2="c" k="-1" />
+<hkern u1="C" u2="d" k="-1" />
+<hkern u1="C" u2="e" k="-1" />
+<hkern u1="C" u2="o" k="-1" />
+<hkern u1="C" u2="q" k="-1" />
+<hkern u1="C" u2="t" k="5" />
+<hkern u1="C" u2="v" k="5" />
+<hkern u1="C" u2="w" k="5" />
+<hkern u1="C" u2="y" k="5" />
+<hkern u1="C" u2="z" k="10" />
+<hkern u1="C" u2="&#xd6;" k="8" />
+<hkern u1="C" u2="&#xe7;" k="-1" />
+<hkern u1="C" u2="&#xd8;" k="8" />
+<hkern u1="C" u2="&#xe6;" k="-1" />
+<hkern u1="C" u2="&#xd5;" k="8" />
+<hkern u1="C" u2="&#x152;" k="8" />
+<hkern u1="C" u2="&#x153;" k="-1" />
+<hkern u1="C" u2="&#x178;" k="-20" />
+<hkern u1="C" u2="&#xd3;" k="8" />
+<hkern u1="C" u2="&#xd4;" k="8" />
+<hkern u1="C" u2="&#xd2;" k="8" />
+<hkern u1="D" u2="&#xdd;" k="18" />
+<hkern u1="D" u2="&#x17d;" k="19" />
+<hkern u1="D" u2="&#x2c;" k="33" />
+<hkern u1="D" u2="." k="33" />
+<hkern u1="D" u2="/" k="50" />
+<hkern u1="D" u2="?" k="20" />
+<hkern u1="D" u2="A" k="20" />
+<hkern u1="D" u2="J" k="23" />
+<hkern u1="D" u2="T" k="19" />
+<hkern u1="D" u2="V" k="20" />
+<hkern u1="D" u2="W" k="33" />
+<hkern u1="D" u2="X" k="29" />
+<hkern u1="D" u2="Y" k="18" />
+<hkern u1="D" u2="Z" k="19" />
+<hkern u1="D" u2="a" k="1" />
+<hkern u1="D" u2="b" k="3" />
+<hkern u1="D" u2="c" k="1" />
+<hkern u1="D" u2="d" k="1" />
+<hkern u1="D" u2="e" k="1" />
+<hkern u1="D" u2="h" k="3" />
+<hkern u1="D" u2="k" k="3" />
+<hkern u1="D" u2="l" k="3" />
+<hkern u1="D" u2="m" k="1" />
+<hkern u1="D" u2="n" k="1" />
+<hkern u1="D" u2="o" k="1" />
+<hkern u1="D" u2="p" k="1" />
+<hkern u1="D" u2="q" k="1" />
+<hkern u1="D" u2="r" k="1" />
+<hkern u1="D" u2="u" k="1" />
+<hkern u1="D" u2="x" k="10" />
+<hkern u1="D" u2="z" k="10" />
+<hkern u1="D" u2="&#xc4;" k="20" />
+<hkern u1="D" u2="&#xc5;" k="20" />
+<hkern u1="D" u2="&#xe7;" k="1" />
+<hkern u1="D" u2="&#xc6;" k="20" />
+<hkern u1="D" u2="&#xe6;" k="1" />
+<hkern u1="D" u2="&#x2026;" k="33" />
+<hkern u1="D" u2="&#xc0;" k="20" />
+<hkern u1="D" u2="&#xc3;" k="20" />
+<hkern u1="D" u2="&#x153;" k="1" />
+<hkern u1="D" u2="&#x201c;" k="20" />
+<hkern u1="D" u2="&#x2018;" k="20" />
+<hkern u1="D" u2="&#x178;" k="18" />
+<hkern u1="D" u2="&#x201a;" k="33" />
+<hkern u1="D" u2="&#x201e;" k="33" />
+<hkern u1="D" u2="&#xc2;" k="20" />
+<hkern u1="D" u2="&#xc1;" k="20" />
+<hkern u1="D" u2="&#x131;" k="1" />
+<hkern u1="E" u2="&#xfe;" k="4" />
+<hkern u1="E" u2="@" k="20" />
+<hkern u1="E" u2="C" k="19" />
+<hkern u1="E" u2="G" k="19" />
+<hkern u1="E" u2="J" k="-4" />
+<hkern u1="E" u2="O" k="19" />
+<hkern u1="E" u2="Q" k="19" />
+<hkern u1="E" u2="T" k="-8" />
+<hkern u1="E" u2="V" k="-8" />
+<hkern u1="E" u2="W" k="4" />
+<hkern u1="E" u2="a" k="6" />
+<hkern u1="E" u2="b" k="4" />
+<hkern u1="E" u2="c" k="21" />
+<hkern u1="E" u2="d" k="21" />
+<hkern u1="E" u2="e" k="21" />
+<hkern u1="E" u2="f" k="10" />
+<hkern u1="E" u2="g" k="16" />
+<hkern u1="E" u2="h" k="4" />
+<hkern u1="E" u2="k" k="4" />
+<hkern u1="E" u2="l" k="4" />
+<hkern u1="E" u2="m" k="8" />
+<hkern u1="E" u2="n" k="8" />
+<hkern u1="E" u2="o" k="21" />
+<hkern u1="E" u2="p" k="8" />
+<hkern u1="E" u2="q" k="21" />
+<hkern u1="E" u2="r" k="8" />
+<hkern u1="E" u2="u" k="8" />
+<hkern u1="E" u2="v" k="20" />
+<hkern u1="E" u2="y" k="20" />
+<hkern u1="E" u2="&#xd6;" k="19" />
+<hkern u1="E" u2="&#xe7;" k="21" />
+<hkern u1="E" u2="&#xae;" k="20" />
+<hkern u1="E" u2="&#xa9;" k="20" />
+<hkern u1="E" u2="&#xd8;" k="19" />
+<hkern u1="E" u2="&#x3c0;" k="20" />
+<hkern u1="E" u2="&#xe6;" k="21" />
+<hkern u1="E" u2="&#xab;" k="15" />
+<hkern u1="E" u2="&#xd5;" k="19" />
+<hkern u1="E" u2="&#x152;" k="19" />
+<hkern u1="E" u2="&#x153;" k="21" />
+<hkern u1="E" u2="&#x2039;" k="15" />
+<hkern u1="E" u2="&#xd3;" k="19" />
+<hkern u1="E" u2="&#xd4;" k="19" />
+<hkern u1="E" u2="&#xd2;" k="19" />
+<hkern u1="E" u2="&#x131;" k="8" />
+<hkern u1="F" u2="&#x161;" k="40" />
+<hkern u1="F" u2="&#xdd;" k="-25" />
+<hkern u1="F" u2="&#xfe;" k="8" />
+<hkern u1="F" u2="&amp;" k="33" />
+<hkern u1="F" u2="&#x2c;" k="90" />
+<hkern u1="F" u2="-" k="35" />
+<hkern u1="F" u2="." k="90" />
+<hkern u1="F" u2="/" k="80" />
+<hkern u1="F" u2=":" k="30" />
+<hkern u1="F" u2=";" k="30" />
+<hkern u1="F" u2="@" k="20" />
+<hkern u1="F" u2="A" k="50" />
+<hkern u1="F" u2="C" k="15" />
+<hkern u1="F" u2="G" k="15" />
+<hkern u1="F" u2="J" k="90" />
+<hkern u1="F" u2="O" k="15" />
+<hkern u1="F" u2="Q" k="15" />
+<hkern u1="F" u2="T" k="-15" />
+<hkern u1="F" u2="V" k="-20" />
+<hkern u1="F" u2="W" k="-5" />
+<hkern u1="F" u2="X" k="-10" />
+<hkern u1="F" u2="Y" k="-25" />
+<hkern u1="F" u2="a" k="50" />
+<hkern u1="F" u2="b" k="10" />
+<hkern u1="F" u2="c" k="50" />
+<hkern u1="F" u2="d" k="50" />
+<hkern u1="F" u2="e" k="50" />
+<hkern u1="F" u2="f" k="20" />
+<hkern u1="F" u2="g" k="30" />
+<hkern u1="F" u2="h" k="10" />
+<hkern u1="F" u2="i" k="10" />
+<hkern u1="F" u2="j" k="10" />
+<hkern u1="F" u2="k" k="10" />
+<hkern u1="F" u2="l" k="10" />
+<hkern u1="F" u2="m" k="40" />
+<hkern u1="F" u2="n" k="40" />
+<hkern u1="F" u2="o" k="50" />
+<hkern u1="F" u2="p" k="40" />
+<hkern u1="F" u2="q" k="50" />
+<hkern u1="F" u2="r" k="40" />
+<hkern u1="F" u2="s" k="40" />
+<hkern u1="F" u2="t" k="20" />
+<hkern u1="F" u2="u" k="40" />
+<hkern u1="F" u2="v" k="20" />
+<hkern u1="F" u2="w" k="20" />
+<hkern u1="F" u2="x" k="40" />
+<hkern u1="F" u2="y" k="20" />
+<hkern u1="F" u2="z" k="40" />
+<hkern u1="F" u2="&#xc4;" k="50" />
+<hkern u1="F" u2="&#xc5;" k="50" />
+<hkern u1="F" u2="&#xd6;" k="15" />
+<hkern u1="F" u2="&#xe7;" k="50" />
+<hkern u1="F" u2="&#xae;" k="20" />
+<hkern u1="F" u2="&#xa9;" k="20" />
+<hkern u1="F" u2="&#x2122;" k="-15" />
+<hkern u1="F" u2="&#xc6;" k="50" />
+<hkern u1="F" u2="&#xd8;" k="15" />
+<hkern u1="F" u2="&#x3c0;" k="20" />
+<hkern u1="F" u2="&#xe6;" k="50" />
+<hkern u1="F" u2="&#xab;" k="26" />
+<hkern u1="F" u2="&#x2026;" k="90" />
+<hkern u1="F" u2="&#xc0;" k="50" />
+<hkern u1="F" u2="&#xc3;" k="50" />
+<hkern u1="F" u2="&#xd5;" k="15" />
+<hkern u1="F" u2="&#x152;" k="15" />
+<hkern u1="F" u2="&#x153;" k="50" />
+<hkern u1="F" u2="&#x2013;" k="35" />
+<hkern u1="F" u2="&#x2014;" k="35" />
+<hkern u1="F" u2="&#x178;" k="-25" />
+<hkern u1="F" u2="&#x2039;" k="26" />
+<hkern u1="F" u2="&#x201a;" k="90" />
+<hkern u1="F" u2="&#x201e;" k="90" />
+<hkern u1="F" u2="&#xc2;" k="50" />
+<hkern u1="F" u2="&#xc1;" k="50" />
+<hkern u1="F" u2="&#xd3;" k="15" />
+<hkern u1="F" u2="&#xd4;" k="15" />
+<hkern u1="F" u2="&#xd2;" k="15" />
+<hkern u1="F" u2="&#x131;" k="40" />
+<hkern u1="G" u2="&#xdd;" k="23" />
+<hkern u1="G" u2="&#x2c;" k="-20" />
+<hkern u1="G" u2="." k="-20" />
+<hkern u1="G" u2="T" k="25" />
+<hkern u1="G" u2="V" k="25" />
+<hkern u1="G" u2="W" k="4" />
+<hkern u1="G" u2="Y" k="23" />
+<hkern u1="G" u2="t" k="10" />
+<hkern u1="G" u2="v" k="18" />
+<hkern u1="G" u2="w" k="11" />
+<hkern u1="G" u2="y" k="18" />
+<hkern u1="G" u2="&#x2122;" k="30" />
+<hkern u1="G" u2="&#x2026;" k="-20" />
+<hkern u1="G" u2="&#x178;" k="23" />
+<hkern u1="G" u2="&#x201a;" k="-20" />
+<hkern u1="G" u2="&#x201e;" k="-20" />
+<hkern u1="H" u2="/" k="20" />
+<hkern u1="H" u2="v" k="10" />
+<hkern u1="H" u2="y" k="10" />
+<hkern u1="I" u2="/" k="20" />
+<hkern u1="I" u2="v" k="10" />
+<hkern u1="I" u2="y" k="10" />
+<hkern u1="J" u2="&#x2c;" k="10" />
+<hkern u1="J" u2="." k="10" />
+<hkern u1="J" u2="A" k="16" />
+<hkern u1="J" u2="J" k="10" />
+<hkern u1="J" u2="&#xc4;" k="15" />
+<hkern u1="J" u2="&#xc5;" k="15" />
+<hkern u1="J" u2="&#xc6;" k="15" />
+<hkern u1="J" u2="&#x2026;" k="10" />
+<hkern u1="J" u2="&#xc0;" k="15" />
+<hkern u1="J" u2="&#xc3;" k="15" />
+<hkern u1="J" u2="&#x201a;" k="10" />
+<hkern u1="J" u2="&#x201e;" k="10" />
+<hkern u1="J" u2="&#xc2;" k="15" />
+<hkern u1="J" u2="&#xc1;" k="15" />
+<hkern u1="K" u2="&#xf0;" k="20" />
+<hkern u1="K" u2="&#x160;" k="5" />
+<hkern u1="K" u2="&#xdd;" k="-4" />
+<hkern u1="K" u2="&#x17d;" k="5" />
+<hkern u1="K" u2="&amp;" k="20" />
+<hkern u1="K" u2="-" k="33" />
+<hkern u1="K" u2="@" k="30" />
+<hkern u1="K" u2="C" k="8" />
+<hkern u1="K" u2="G" k="8" />
+<hkern u1="K" u2="O" k="8" />
+<hkern u1="K" u2="Q" k="8" />
+<hkern u1="K" u2="S" k="5" />
+<hkern u1="K" u2="T" k="-5" />
+<hkern u1="K" u2="U" k="10" />
+<hkern u1="K" u2="V" k="4" />
+<hkern u1="K" u2="W" k="21" />
+<hkern u1="K" u2="X" k="-6" />
+<hkern u1="K" u2="Y" k="-4" />
+<hkern u1="K" u2="Z" k="5" />
+<hkern u1="K" u2="a" k="15" />
+<hkern u1="K" u2="c" k="20" />
+<hkern u1="K" u2="d" k="20" />
+<hkern u1="K" u2="e" k="20" />
+<hkern u1="K" u2="f" k="5" />
+<hkern u1="K" u2="g" k="10" />
+<hkern u1="K" u2="o" k="20" />
+<hkern u1="K" u2="q" k="20" />
+<hkern u1="K" u2="t" k="10" />
+<hkern u1="K" u2="v" k="31" />
+<hkern u1="K" u2="w" k="19" />
+<hkern u1="K" u2="x" k="8" />
+<hkern u1="K" u2="y" k="31" />
+<hkern u1="K" u2="&#xd6;" k="8" />
+<hkern u1="K" u2="&#xdc;" k="10" />
+<hkern u1="K" u2="&#xe7;" k="20" />
+<hkern u1="K" u2="&#xae;" k="30" />
+<hkern u1="K" u2="&#xa9;" k="30" />
+<hkern u1="K" u2="&#xd8;" k="8" />
+<hkern u1="K" u2="&#x3c0;" k="30" />
+<hkern u1="K" u2="&#xe6;" k="20" />
+<hkern u1="K" u2="&#xd5;" k="8" />
+<hkern u1="K" u2="&#x152;" k="8" />
+<hkern u1="K" u2="&#x153;" k="20" />
+<hkern u1="K" u2="&#x2013;" k="33" />
+<hkern u1="K" u2="&#x2014;" k="33" />
+<hkern u1="K" u2="&#x178;" k="-4" />
+<hkern u1="K" u2="&#xd3;" k="8" />
+<hkern u1="K" u2="&#xd4;" k="8" />
+<hkern u1="K" u2="&#xd2;" k="8" />
+<hkern u1="K" u2="&#xda;" k="10" />
+<hkern u1="K" u2="&#xdb;" k="10" />
+<hkern u1="K" u2="&#xd9;" k="10" />
+<hkern u1="L" u2="&#xdd;" k="91" />
+<hkern u1="L" u2="&#x17d;" k="-4" />
+<hkern u1="L" u2="*" k="45" />
+<hkern u1="L" u2="?" k="53" />
+<hkern u1="L" u2="@" k="50" />
+<hkern u1="L" u2="A" k="-20" />
+<hkern u1="L" u2="C" k="31" />
+<hkern u1="L" u2="G" k="31" />
+<hkern u1="L" u2="J" k="-11" />
+<hkern u1="L" u2="O" k="31" />
+<hkern u1="L" u2="Q" k="31" />
+<hkern u1="L" u2="T" k="96" />
+<hkern u1="L" u2="U" k="15" />
+<hkern u1="L" u2="V" k="90" />
+<hkern u1="L" u2="W" k="70" />
+<hkern u1="L" u2="Y" k="91" />
+<hkern u1="L" u2="Z" k="-4" />
+<hkern u1="L" u2="\" k="50" />
+<hkern u1="L" u2="a" k="1" />
+<hkern u1="L" u2="c" k="13" />
+<hkern u1="L" u2="d" k="13" />
+<hkern u1="L" u2="e" k="13" />
+<hkern u1="L" u2="o" k="13" />
+<hkern u1="L" u2="q" k="13" />
+<hkern u1="L" u2="v" k="55" />
+<hkern u1="L" u2="w" k="25" />
+<hkern u1="L" u2="y" k="55" />
+<hkern u1="L" u2="&#xc4;" k="-20" />
+<hkern u1="L" u2="&#xc5;" k="-20" />
+<hkern u1="L" u2="&#xd6;" k="31" />
+<hkern u1="L" u2="&#xdc;" k="15" />
+<hkern u1="L" u2="&#xe7;" k="13" />
+<hkern u1="L" u2="&#xae;" k="50" />
+<hkern u1="L" u2="&#xa9;" k="50" />
+<hkern u1="L" u2="&#x2122;" k="70" />
+<hkern u1="L" u2="&#xc6;" k="-20" />
+<hkern u1="L" u2="&#xd8;" k="31" />
+<hkern u1="L" u2="&#x3c0;" k="50" />
+<hkern u1="L" u2="&#xe6;" k="1" />
+<hkern u1="L" u2="&#xab;" k="15" />
+<hkern u1="L" u2="&#xc0;" k="-20" />
+<hkern u1="L" u2="&#xc3;" k="-20" />
+<hkern u1="L" u2="&#xd5;" k="31" />
+<hkern u1="L" u2="&#x152;" k="31" />
+<hkern u1="L" u2="&#x153;" k="13" />
+<hkern u1="L" u2="&#x201c;" k="100" />
+<hkern u1="L" u2="&#x201d;" k="68" />
+<hkern u1="L" u2="&#x2018;" k="100" />
+<hkern u1="L" u2="&#x2019;" k="68" />
+<hkern u1="L" u2="&#x178;" k="91" />
+<hkern u1="L" u2="&#x2039;" k="15" />
+<hkern u1="L" u2="&#xc2;" k="-20" />
+<hkern u1="L" u2="&#xc1;" k="-20" />
+<hkern u1="L" u2="&#xd3;" k="31" />
+<hkern u1="L" u2="&#xd4;" k="31" />
+<hkern u1="L" u2="&#xd2;" k="31" />
+<hkern u1="L" u2="&#xda;" k="15" />
+<hkern u1="L" u2="&#xdb;" k="15" />
+<hkern u1="L" u2="&#xd9;" k="15" />
+<hkern u1="M" u2="/" k="20" />
+<hkern u1="M" u2="v" k="10" />
+<hkern u1="M" u2="y" k="10" />
+<hkern u1="N" u2="/" k="20" />
+<hkern u1="N" u2="v" k="10" />
+<hkern u1="N" u2="y" k="10" />
+<hkern u1="O" u2="&#xdd;" k="18" />
+<hkern u1="O" u2="&#x17d;" k="19" />
+<hkern u1="O" u2="&#x2c;" k="33" />
+<hkern u1="O" u2="." k="33" />
+<hkern u1="O" u2="/" k="50" />
+<hkern u1="O" u2="?" k="20" />
+<hkern u1="O" u2="A" k="20" />
+<hkern u1="O" u2="J" k="23" />
+<hkern u1="O" u2="T" k="19" />
+<hkern u1="O" u2="V" k="20" />
+<hkern u1="O" u2="W" k="33" />
+<hkern u1="O" u2="X" k="29" />
+<hkern u1="O" u2="Y" k="18" />
+<hkern u1="O" u2="Z" k="19" />
+<hkern u1="O" u2="a" k="1" />
+<hkern u1="O" u2="b" k="3" />
+<hkern u1="O" u2="c" k="1" />
+<hkern u1="O" u2="d" k="1" />
+<hkern u1="O" u2="e" k="1" />
+<hkern u1="O" u2="h" k="3" />
+<hkern u1="O" u2="k" k="3" />
+<hkern u1="O" u2="l" k="3" />
+<hkern u1="O" u2="m" k="1" />
+<hkern u1="O" u2="n" k="1" />
+<hkern u1="O" u2="o" k="1" />
+<hkern u1="O" u2="p" k="1" />
+<hkern u1="O" u2="q" k="1" />
+<hkern u1="O" u2="r" k="1" />
+<hkern u1="O" u2="u" k="1" />
+<hkern u1="O" u2="x" k="10" />
+<hkern u1="O" u2="z" k="10" />
+<hkern u1="O" u2="&#xc4;" k="20" />
+<hkern u1="O" u2="&#xc5;" k="20" />
+<hkern u1="O" u2="&#xe7;" k="1" />
+<hkern u1="O" u2="&#xc6;" k="20" />
+<hkern u1="O" u2="&#xe6;" k="1" />
+<hkern u1="O" u2="&#x2026;" k="33" />
+<hkern u1="O" u2="&#xc0;" k="20" />
+<hkern u1="O" u2="&#xc3;" k="20" />
+<hkern u1="O" u2="&#x153;" k="1" />
+<hkern u1="O" u2="&#x201c;" k="20" />
+<hkern u1="O" u2="&#x2018;" k="20" />
+<hkern u1="O" u2="&#x178;" k="18" />
+<hkern u1="O" u2="&#x201a;" k="33" />
+<hkern u1="O" u2="&#x201e;" k="33" />
+<hkern u1="O" u2="&#xc2;" k="20" />
+<hkern u1="O" u2="&#xc1;" k="20" />
+<hkern u1="O" u2="&#x131;" k="1" />
+<hkern u1="P" u2="&#xdd;" k="-8" />
+<hkern u1="P" u2="&#x17d;" k="25" />
+<hkern u1="P" u2="&amp;" k="13" />
+<hkern u1="P" u2="*" k="-8" />
+<hkern u1="P" u2="&#x2c;" k="60" />
+<hkern u1="P" u2="." k="60" />
+<hkern u1="P" u2="A" k="39" />
+<hkern u1="P" u2="J" k="59" />
+<hkern u1="P" u2="T" k="-8" />
+<hkern u1="P" u2="V" k="-3" />
+<hkern u1="P" u2="W" k="6" />
+<hkern u1="P" u2="X" k="6" />
+<hkern u1="P" u2="Y" k="-8" />
+<hkern u1="P" u2="Z" k="25" />
+<hkern u1="P" u2="a" k="10" />
+<hkern u1="P" u2="c" k="10" />
+<hkern u1="P" u2="d" k="10" />
+<hkern u1="P" u2="e" k="10" />
+<hkern u1="P" u2="f" k="-3" />
+<hkern u1="P" u2="o" k="10" />
+<hkern u1="P" u2="q" k="10" />
+<hkern u1="P" u2="t" k="-3" />
+<hkern u1="P" u2="v" k="-15" />
+<hkern u1="P" u2="w" k="-10" />
+<hkern u1="P" u2="x" k="-5" />
+<hkern u1="P" u2="y" k="-15" />
+<hkern u1="P" u2="&#xc4;" k="39" />
+<hkern u1="P" u2="&#xc5;" k="39" />
+<hkern u1="P" u2="&#xe7;" k="10" />
+<hkern u1="P" u2="&#xc6;" k="39" />
+<hkern u1="P" u2="&#xe6;" k="10" />
+<hkern u1="P" u2="&#x2026;" k="60" />
+<hkern u1="P" u2="&#xc0;" k="39" />
+<hkern u1="P" u2="&#xc3;" k="39" />
+<hkern u1="P" u2="&#x153;" k="10" />
+<hkern u1="P" u2="&#x178;" k="-8" />
+<hkern u1="P" u2="&#x201a;" k="60" />
+<hkern u1="P" u2="&#x201e;" k="60" />
+<hkern u1="P" u2="&#xc2;" k="39" />
+<hkern u1="P" u2="&#xc1;" k="39" />
+<hkern u1="Q" u2="&#xdd;" k="18" />
+<hkern u1="Q" u2="&#x17d;" k="19" />
+<hkern u1="Q" u2="&#x2c;" k="-13" />
+<hkern u1="Q" u2="." k="-15" />
+<hkern u1="Q" u2="/" k="30" />
+<hkern u1="Q" u2="?" k="20" />
+<hkern u1="Q" u2="A" k="20" />
+<hkern u1="Q" u2="J" k="4" />
+<hkern u1="Q" u2="T" k="19" />
+<hkern u1="Q" u2="V" k="20" />
+<hkern u1="Q" u2="W" k="33" />
+<hkern u1="Q" u2="X" k="-9" />
+<hkern u1="Q" u2="Y" k="18" />
+<hkern u1="Q" u2="Z" k="19" />
+<hkern u1="Q" u2="a" k="1" />
+<hkern u1="Q" u2="b" k="3" />
+<hkern u1="Q" u2="c" k="1" />
+<hkern u1="Q" u2="d" k="1" />
+<hkern u1="Q" u2="e" k="1" />
+<hkern u1="Q" u2="h" k="3" />
+<hkern u1="Q" u2="k" k="3" />
+<hkern u1="Q" u2="l" k="3" />
+<hkern u1="Q" u2="m" k="1" />
+<hkern u1="Q" u2="n" k="1" />
+<hkern u1="Q" u2="o" k="1" />
+<hkern u1="Q" u2="p" k="1" />
+<hkern u1="Q" u2="q" k="1" />
+<hkern u1="Q" u2="r" k="1" />
+<hkern u1="Q" u2="u" k="1" />
+<hkern u1="Q" u2="x" k="10" />
+<hkern u1="Q" u2="z" k="10" />
+<hkern u1="Q" u2="&#xc4;" k="20" />
+<hkern u1="Q" u2="&#xc5;" k="20" />
+<hkern u1="Q" u2="&#xe7;" k="1" />
+<hkern u1="Q" u2="&#xc6;" k="20" />
+<hkern u1="Q" u2="&#xe6;" k="1" />
+<hkern u1="Q" u2="&#x2026;" k="33" />
+<hkern u1="Q" u2="&#xc0;" k="20" />
+<hkern u1="Q" u2="&#xc3;" k="20" />
+<hkern u1="Q" u2="&#x153;" k="1" />
+<hkern u1="Q" u2="&#x201c;" k="20" />
+<hkern u1="Q" u2="&#x2018;" k="20" />
+<hkern u1="Q" u2="&#x178;" k="18" />
+<hkern u1="Q" u2="&#x201a;" k="-13" />
+<hkern u1="Q" u2="&#x201e;" k="-13" />
+<hkern u1="Q" u2="&#xc2;" k="20" />
+<hkern u1="Q" u2="&#xc1;" k="20" />
+<hkern u1="Q" u2="&#x131;" k="1" />
+<hkern u1="R" u2="&#xdd;" k="-8" />
+<hkern u1="R" u2="&amp;" k="-10" />
+<hkern u1="R" u2="C" k="4" />
+<hkern u1="R" u2="G" k="4" />
+<hkern u1="R" u2="J" k="6" />
+<hkern u1="R" u2="O" k="4" />
+<hkern u1="R" u2="Q" k="4" />
+<hkern u1="R" u2="T" k="3" />
+<hkern u1="R" u2="V" k="-1" />
+<hkern u1="R" u2="W" k="11" />
+<hkern u1="R" u2="X" k="-10" />
+<hkern u1="R" u2="Y" k="-8" />
+<hkern u1="R" u2="&#xd6;" k="4" />
+<hkern u1="R" u2="&#xd8;" k="4" />
+<hkern u1="R" u2="&#xd5;" k="4" />
+<hkern u1="R" u2="&#x152;" k="4" />
+<hkern u1="R" u2="&#x178;" k="-8" />
+<hkern u1="R" u2="&#xd3;" k="4" />
+<hkern u1="R" u2="&#xd4;" k="4" />
+<hkern u1="R" u2="&#xd2;" k="4" />
+<hkern u1="S" u2="&#xdd;" k="-10" />
+<hkern u1="S" u2="A" k="-5" />
+<hkern u1="S" u2="V" k="-6" />
+<hkern u1="S" u2="W" k="8" />
+<hkern u1="S" u2="X" k="-5" />
+<hkern u1="S" u2="Y" k="-10" />
+<hkern u1="S" u2="&#xc4;" k="-5" />
+<hkern u1="S" u2="&#xc5;" k="-5" />
+<hkern u1="S" u2="&#xc6;" k="-5" />
+<hkern u1="S" u2="&#xc0;" k="-5" />
+<hkern u1="S" u2="&#xc3;" k="-5" />
+<hkern u1="S" u2="&#x178;" k="-10" />
+<hkern u1="S" u2="&#xc2;" k="-5" />
+<hkern u1="S" u2="&#xc1;" k="-5" />
+<hkern u1="T" u2="&#x161;" k="63" />
+<hkern u1="T" u2="&#xdd;" k="-28" />
+<hkern u1="T" u2="&#x17d;" k="6" />
+<hkern u1="T" u2="&amp;" k="40" />
+<hkern u1="T" u2="*" k="-11" />
+<hkern u1="T" u2="&#x2c;" k="83" />
+<hkern u1="T" u2="-" k="55" />
+<hkern u1="T" u2="." k="83" />
+<hkern u1="T" u2="/" k="38" />
+<hkern u1="T" u2=":" k="50" />
+<hkern u1="T" u2=";" k="50" />
+<hkern u1="T" u2="@" k="30" />
+<hkern u1="T" u2="A" k="58" />
+<hkern u1="T" u2="C" k="19" />
+<hkern u1="T" u2="G" k="19" />
+<hkern u1="T" u2="J" k="96" />
+<hkern u1="T" u2="O" k="19" />
+<hkern u1="T" u2="Q" k="19" />
+<hkern u1="T" u2="T" k="-10" />
+<hkern u1="T" u2="V" k="-20" />
+<hkern u1="T" u2="X" k="-10" />
+<hkern u1="T" u2="Y" k="-28" />
+<hkern u1="T" u2="Z" k="6" />
+<hkern u1="T" u2="\" k="-10" />
+<hkern u1="T" u2="a" k="58" />
+<hkern u1="T" u2="c" k="76" />
+<hkern u1="T" u2="d" k="76" />
+<hkern u1="T" u2="e" k="76" />
+<hkern u1="T" u2="f" k="10" />
+<hkern u1="T" u2="g" k="95" />
+<hkern u1="T" u2="m" k="35" />
+<hkern u1="T" u2="n" k="35" />
+<hkern u1="T" u2="o" k="76" />
+<hkern u1="T" u2="p" k="35" />
+<hkern u1="T" u2="q" k="76" />
+<hkern u1="T" u2="r" k="35" />
+<hkern u1="T" u2="s" k="63" />
+<hkern u1="T" u2="t" k="20" />
+<hkern u1="T" u2="u" k="35" />
+<hkern u1="T" u2="v" k="25" />
+<hkern u1="T" u2="w" k="23" />
+<hkern u1="T" u2="x" k="35" />
+<hkern u1="T" u2="y" k="25" />
+<hkern u1="T" u2="z" k="35" />
+<hkern u1="T" u2="&#xc4;" k="59" />
+<hkern u1="T" u2="&#xc5;" k="59" />
+<hkern u1="T" u2="&#xd6;" k="19" />
+<hkern u1="T" u2="&#xe7;" k="76" />
+<hkern u1="T" u2="&#xae;" k="30" />
+<hkern u1="T" u2="&#xa9;" k="30" />
+<hkern u1="T" u2="&#xc6;" k="59" />
+<hkern u1="T" u2="&#xd8;" k="19" />
+<hkern u1="T" u2="&#x3c0;" k="30" />
+<hkern u1="T" u2="&#xe6;" k="58" />
+<hkern u1="T" u2="&#xbf;" k="50" />
+<hkern u1="T" u2="&#xab;" k="110" />
+<hkern u1="T" u2="&#xbb;" k="40" />
+<hkern u1="T" u2="&#x2026;" k="83" />
+<hkern u1="T" u2="&#xc0;" k="59" />
+<hkern u1="T" u2="&#xc3;" k="59" />
+<hkern u1="T" u2="&#xd5;" k="19" />
+<hkern u1="T" u2="&#x152;" k="19" />
+<hkern u1="T" u2="&#x153;" k="76" />
+<hkern u1="T" u2="&#x2013;" k="55" />
+<hkern u1="T" u2="&#x2014;" k="55" />
+<hkern u1="T" u2="&#x178;" k="-28" />
+<hkern u1="T" u2="&#x2039;" k="110" />
+<hkern u1="T" u2="&#x203a;" k="40" />
+<hkern u1="T" u2="&#x201a;" k="83" />
+<hkern u1="T" u2="&#x201e;" k="83" />
+<hkern u1="T" u2="&#xc2;" k="59" />
+<hkern u1="T" u2="&#xc1;" k="59" />
+<hkern u1="T" u2="&#xd3;" k="19" />
+<hkern u1="T" u2="&#xd4;" k="19" />
+<hkern u1="T" u2="&#xd2;" k="19" />
+<hkern u1="T" u2="&#x131;" k="35" />
+<hkern u1="U" u2="&#x2c;" k="10" />
+<hkern u1="U" u2="." k="10" />
+<hkern u1="U" u2="A" k="16" />
+<hkern u1="U" u2="J" k="10" />
+<hkern u1="U" u2="&#xc4;" k="15" />
+<hkern u1="U" u2="&#xc5;" k="15" />
+<hkern u1="U" u2="&#xc6;" k="15" />
+<hkern u1="U" u2="&#x2026;" k="10" />
+<hkern u1="U" u2="&#xc0;" k="15" />
+<hkern u1="U" u2="&#xc3;" k="15" />
+<hkern u1="U" u2="&#x201a;" k="10" />
+<hkern u1="U" u2="&#x201e;" k="10" />
+<hkern u1="U" u2="&#xc2;" k="15" />
+<hkern u1="U" u2="&#xc1;" k="15" />
+<hkern u1="V" u2="&#x160;" k="-3" />
+<hkern u1="V" u2="&#x161;" k="40" />
+<hkern u1="V" u2="&#xdd;" k="-23" />
+<hkern u1="V" u2="&#xfe;" k="8" />
+<hkern u1="V" u2="&amp;" k="33" />
+<hkern u1="V" u2=")" k="-30" />
+<hkern u1="V" u2="&#x2c;" k="68" />
+<hkern u1="V" u2="-" k="60" />
+<hkern u1="V" u2="." k="68" />
+<hkern u1="V" u2=":" k="20" />
+<hkern u1="V" u2=";" k="20" />
+<hkern u1="V" u2="@" k="8" />
+<hkern u1="V" u2="A" k="74" />
+<hkern u1="V" u2="C" k="20" />
+<hkern u1="V" u2="G" k="20" />
+<hkern u1="V" u2="J" k="80" />
+<hkern u1="V" u2="O" k="20" />
+<hkern u1="V" u2="Q" k="20" />
+<hkern u1="V" u2="S" k="-3" />
+<hkern u1="V" u2="T" k="-20" />
+<hkern u1="V" u2="V" k="-9" />
+<hkern u1="V" u2="W" k="9" />
+<hkern u1="V" u2="X" k="-5" />
+<hkern u1="V" u2="Y" k="-23" />
+<hkern u1="V" u2="]" k="-30" />
+<hkern u1="V" u2="a" k="48" />
+<hkern u1="V" u2="b" k="10" />
+<hkern u1="V" u2="c" k="55" />
+<hkern u1="V" u2="d" k="55" />
+<hkern u1="V" u2="e" k="55" />
+<hkern u1="V" u2="f" k="10" />
+<hkern u1="V" u2="g" k="35" />
+<hkern u1="V" u2="h" k="10" />
+<hkern u1="V" u2="k" k="10" />
+<hkern u1="V" u2="l" k="10" />
+<hkern u1="V" u2="m" k="30" />
+<hkern u1="V" u2="n" k="30" />
+<hkern u1="V" u2="o" k="55" />
+<hkern u1="V" u2="p" k="30" />
+<hkern u1="V" u2="q" k="55" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="s" k="40" />
+<hkern u1="V" u2="t" k="10" />
+<hkern u1="V" u2="u" k="30" />
+<hkern u1="V" u2="v" k="10" />
+<hkern u1="V" u2="w" k="10" />
+<hkern u1="V" u2="x" k="25" />
+<hkern u1="V" u2="y" k="10" />
+<hkern u1="V" u2="}" k="-30" />
+<hkern u1="V" u2="&#xc4;" k="73" />
+<hkern u1="V" u2="&#xc5;" k="73" />
+<hkern u1="V" u2="&#xd6;" k="20" />
+<hkern u1="V" u2="&#xe7;" k="55" />
+<hkern u1="V" u2="&#xae;" k="8" />
+<hkern u1="V" u2="&#xa9;" k="8" />
+<hkern u1="V" u2="&#xc6;" k="73" />
+<hkern u1="V" u2="&#xd8;" k="20" />
+<hkern u1="V" u2="&#x3c0;" k="8" />
+<hkern u1="V" u2="&#xe6;" k="48" />
+<hkern u1="V" u2="&#xab;" k="50" />
+<hkern u1="V" u2="&#xbb;" k="20" />
+<hkern u1="V" u2="&#x2026;" k="68" />
+<hkern u1="V" u2="&#xc0;" k="73" />
+<hkern u1="V" u2="&#xc3;" k="73" />
+<hkern u1="V" u2="&#xd5;" k="20" />
+<hkern u1="V" u2="&#x152;" k="20" />
+<hkern u1="V" u2="&#x153;" k="55" />
+<hkern u1="V" u2="&#x2013;" k="60" />
+<hkern u1="V" u2="&#x2014;" k="60" />
+<hkern u1="V" u2="&#x178;" k="-23" />
+<hkern u1="V" u2="&#x2039;" k="50" />
+<hkern u1="V" u2="&#x203a;" k="20" />
+<hkern u1="V" u2="&#x201a;" k="68" />
+<hkern u1="V" u2="&#x201e;" k="68" />
+<hkern u1="V" u2="&#xc2;" k="73" />
+<hkern u1="V" u2="&#xc1;" k="73" />
+<hkern u1="V" u2="&#xd3;" k="20" />
+<hkern u1="V" u2="&#xd4;" k="20" />
+<hkern u1="V" u2="&#xd2;" k="20" />
+<hkern u1="V" u2="&#x131;" k="30" />
+<hkern u1="W" u2="&#x160;" k="11" />
+<hkern u1="W" u2="&#x161;" k="50" />
+<hkern u1="W" u2="&#xfe;" k="8" />
+<hkern u1="W" u2="&amp;" k="41" />
+<hkern u1="W" u2=")" k="-10" />
+<hkern u1="W" u2="&#x2c;" k="28" />
+<hkern u1="W" u2="-" k="40" />
+<hkern u1="W" u2="." k="28" />
+<hkern u1="W" u2=":" k="20" />
+<hkern u1="W" u2=";" k="20" />
+<hkern u1="W" u2="@" k="20" />
+<hkern u1="W" u2="A" k="62" />
+<hkern u1="W" u2="C" k="33" />
+<hkern u1="W" u2="G" k="33" />
+<hkern u1="W" u2="J" k="74" />
+<hkern u1="W" u2="O" k="33" />
+<hkern u1="W" u2="Q" k="33" />
+<hkern u1="W" u2="S" k="11" />
+<hkern u1="W" u2="V" k="9" />
+<hkern u1="W" u2="W" k="23" />
+<hkern u1="W" u2="X" k="18" />
+<hkern u1="W" u2="]" k="-10" />
+<hkern u1="W" u2="a" k="53" />
+<hkern u1="W" u2="b" k="10" />
+<hkern u1="W" u2="c" k="60" />
+<hkern u1="W" u2="d" k="60" />
+<hkern u1="W" u2="e" k="60" />
+<hkern u1="W" u2="f" k="15" />
+<hkern u1="W" u2="g" k="50" />
+<hkern u1="W" u2="h" k="10" />
+<hkern u1="W" u2="i" k="20" />
+<hkern u1="W" u2="j" k="20" />
+<hkern u1="W" u2="k" k="10" />
+<hkern u1="W" u2="l" k="10" />
+<hkern u1="W" u2="m" k="35" />
+<hkern u1="W" u2="n" k="35" />
+<hkern u1="W" u2="o" k="60" />
+<hkern u1="W" u2="p" k="35" />
+<hkern u1="W" u2="q" k="60" />
+<hkern u1="W" u2="r" k="35" />
+<hkern u1="W" u2="s" k="50" />
+<hkern u1="W" u2="t" k="30" />
+<hkern u1="W" u2="u" k="35" />
+<hkern u1="W" u2="v" k="30" />
+<hkern u1="W" u2="w" k="30" />
+<hkern u1="W" u2="x" k="30" />
+<hkern u1="W" u2="y" k="30" />
+<hkern u1="W" u2="z" k="30" />
+<hkern u1="W" u2="}" k="-10" />
+<hkern u1="W" u2="&#xc4;" k="61" />
+<hkern u1="W" u2="&#xc5;" k="61" />
+<hkern u1="W" u2="&#xd6;" k="33" />
+<hkern u1="W" u2="&#xe7;" k="60" />
+<hkern u1="W" u2="&#xae;" k="20" />
+<hkern u1="W" u2="&#xa9;" k="20" />
+<hkern u1="W" u2="&#xc6;" k="61" />
+<hkern u1="W" u2="&#xd8;" k="33" />
+<hkern u1="W" u2="&#x3c0;" k="20" />
+<hkern u1="W" u2="&#xe6;" k="53" />
+<hkern u1="W" u2="&#xab;" k="40" />
+<hkern u1="W" u2="&#xbb;" k="20" />
+<hkern u1="W" u2="&#x2026;" k="28" />
+<hkern u1="W" u2="&#xc0;" k="61" />
+<hkern u1="W" u2="&#xc3;" k="61" />
+<hkern u1="W" u2="&#xd5;" k="33" />
+<hkern u1="W" u2="&#x152;" k="33" />
+<hkern u1="W" u2="&#x153;" k="60" />
+<hkern u1="W" u2="&#x2013;" k="40" />
+<hkern u1="W" u2="&#x2014;" k="40" />
+<hkern u1="W" u2="&#x2039;" k="40" />
+<hkern u1="W" u2="&#x203a;" k="20" />
+<hkern u1="W" u2="&#x201a;" k="28" />
+<hkern u1="W" u2="&#x201e;" k="28" />
+<hkern u1="W" u2="&#xc2;" k="61" />
+<hkern u1="W" u2="&#xc1;" k="61" />
+<hkern u1="W" u2="&#xd3;" k="33" />
+<hkern u1="W" u2="&#xd4;" k="33" />
+<hkern u1="W" u2="&#xd2;" k="33" />
+<hkern u1="W" u2="&#x131;" k="35" />
+<hkern u1="X" u2="&#x160;" k="-9" />
+<hkern u1="X" u2="&#xdd;" k="-13" />
+<hkern u1="X" u2="&amp;" k="5" />
+<hkern u1="X" u2=")" k="-40" />
+<hkern u1="X" u2="-" k="30" />
+<hkern u1="X" u2="@" k="23" />
+<hkern u1="X" u2="A" k="-10" />
+<hkern u1="X" u2="C" k="29" />
+<hkern u1="X" u2="G" k="29" />
+<hkern u1="X" u2="O" k="29" />
+<hkern u1="X" u2="Q" k="29" />
+<hkern u1="X" u2="S" k="-9" />
+<hkern u1="X" u2="T" k="-10" />
+<hkern u1="X" u2="V" k="-5" />
+<hkern u1="X" u2="W" k="18" />
+<hkern u1="X" u2="X" k="-3" />
+<hkern u1="X" u2="Y" k="-13" />
+<hkern u1="X" u2="]" k="-40" />
+<hkern u1="X" u2="a" k="10" />
+<hkern u1="X" u2="c" k="10" />
+<hkern u1="X" u2="d" k="10" />
+<hkern u1="X" u2="e" k="10" />
+<hkern u1="X" u2="f" k="10" />
+<hkern u1="X" u2="m" k="10" />
+<hkern u1="X" u2="n" k="10" />
+<hkern u1="X" u2="o" k="10" />
+<hkern u1="X" u2="p" k="10" />
+<hkern u1="X" u2="q" k="10" />
+<hkern u1="X" u2="r" k="10" />
+<hkern u1="X" u2="t" k="10" />
+<hkern u1="X" u2="u" k="10" />
+<hkern u1="X" u2="v" k="20" />
+<hkern u1="X" u2="w" k="10" />
+<hkern u1="X" u2="y" k="20" />
+<hkern u1="X" u2="}" k="-40" />
+<hkern u1="X" u2="&#xc4;" k="-10" />
+<hkern u1="X" u2="&#xc5;" k="-10" />
+<hkern u1="X" u2="&#xd6;" k="29" />
+<hkern u1="X" u2="&#xe7;" k="10" />
+<hkern u1="X" u2="&#xae;" k="23" />
+<hkern u1="X" u2="&#xa9;" k="23" />
+<hkern u1="X" u2="&#xc6;" k="-10" />
+<hkern u1="X" u2="&#xd8;" k="29" />
+<hkern u1="X" u2="&#x3c0;" k="23" />
+<hkern u1="X" u2="&#xe6;" k="10" />
+<hkern u1="X" u2="&#xc0;" k="-10" />
+<hkern u1="X" u2="&#xc3;" k="-10" />
+<hkern u1="X" u2="&#xd5;" k="29" />
+<hkern u1="X" u2="&#x152;" k="29" />
+<hkern u1="X" u2="&#x153;" k="10" />
+<hkern u1="X" u2="&#x2013;" k="30" />
+<hkern u1="X" u2="&#x2014;" k="30" />
+<hkern u1="X" u2="&#x178;" k="-13" />
+<hkern u1="X" u2="&#xc2;" k="-10" />
+<hkern u1="X" u2="&#xc1;" k="-10" />
+<hkern u1="X" u2="&#xd3;" k="29" />
+<hkern u1="X" u2="&#xd4;" k="29" />
+<hkern u1="X" u2="&#xd2;" k="29" />
+<hkern u1="X" u2="&#x131;" k="10" />
+<hkern u1="Y" u2="&#x160;" k="-10" />
+<hkern u1="Y" u2="&#x161;" k="54" />
+<hkern u1="Y" u2="&amp;" k="30" />
+<hkern u1="Y" u2=")" k="-50" />
+<hkern u1="Y" u2="&#x2c;" k="50" />
+<hkern u1="Y" u2="-" k="50" />
+<hkern u1="Y" u2="." k="50" />
+<hkern u1="Y" u2="/" k="45" />
+<hkern u1="Y" u2=":" k="30" />
+<hkern u1="Y" u2=";" k="30" />
+<hkern u1="Y" u2="@" k="30" />
+<hkern u1="Y" u2="A" k="54" />
+<hkern u1="Y" u2="C" k="18" />
+<hkern u1="Y" u2="G" k="18" />
+<hkern u1="Y" u2="J" k="85" />
+<hkern u1="Y" u2="O" k="18" />
+<hkern u1="Y" u2="Q" k="18" />
+<hkern u1="Y" u2="S" k="-10" />
+<hkern u1="Y" u2="T" k="-28" />
+<hkern u1="Y" u2="V" k="-23" />
+<hkern u1="Y" u2="X" k="-13" />
+<hkern u1="Y" u2="]" k="-50" />
+<hkern u1="Y" u2="a" k="63" />
+<hkern u1="Y" u2="c" k="70" />
+<hkern u1="Y" u2="d" k="70" />
+<hkern u1="Y" u2="e" k="70" />
+<hkern u1="Y" u2="f" k="10" />
+<hkern u1="Y" u2="g" k="40" />
+<hkern u1="Y" u2="m" k="40" />
+<hkern u1="Y" u2="n" k="40" />
+<hkern u1="Y" u2="o" k="70" />
+<hkern u1="Y" u2="p" k="40" />
+<hkern u1="Y" u2="q" k="70" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="s" k="54" />
+<hkern u1="Y" u2="t" k="20" />
+<hkern u1="Y" u2="u" k="40" />
+<hkern u1="Y" u2="v" k="20" />
+<hkern u1="Y" u2="w" k="10" />
+<hkern u1="Y" u2="x" k="20" />
+<hkern u1="Y" u2="y" k="20" />
+<hkern u1="Y" u2="z" k="30" />
+<hkern u1="Y" u2="}" k="-50" />
+<hkern u1="Y" u2="&#xc4;" k="54" />
+<hkern u1="Y" u2="&#xc5;" k="54" />
+<hkern u1="Y" u2="&#xd6;" k="18" />
+<hkern u1="Y" u2="&#xe7;" k="70" />
+<hkern u1="Y" u2="&#xae;" k="30" />
+<hkern u1="Y" u2="&#xa9;" k="30" />
+<hkern u1="Y" u2="&#xc6;" k="54" />
+<hkern u1="Y" u2="&#xd8;" k="18" />
+<hkern u1="Y" u2="&#x3c0;" k="30" />
+<hkern u1="Y" u2="&#xe6;" k="63" />
+<hkern u1="Y" u2="&#xab;" k="60" />
+<hkern u1="Y" u2="&#xbb;" k="40" />
+<hkern u1="Y" u2="&#x2026;" k="50" />
+<hkern u1="Y" u2="&#xc0;" k="54" />
+<hkern u1="Y" u2="&#xc3;" k="54" />
+<hkern u1="Y" u2="&#xd5;" k="18" />
+<hkern u1="Y" u2="&#x152;" k="18" />
+<hkern u1="Y" u2="&#x153;" k="70" />
+<hkern u1="Y" u2="&#x2013;" k="50" />
+<hkern u1="Y" u2="&#x2014;" k="50" />
+<hkern u1="Y" u2="&#x2039;" k="60" />
+<hkern u1="Y" u2="&#x203a;" k="40" />
+<hkern u1="Y" u2="&#x201a;" k="50" />
+<hkern u1="Y" u2="&#x201e;" k="50" />
+<hkern u1="Y" u2="&#xc2;" k="54" />
+<hkern u1="Y" u2="&#xc1;" k="54" />
+<hkern u1="Y" u2="&#xd3;" k="18" />
+<hkern u1="Y" u2="&#xd4;" k="18" />
+<hkern u1="Y" u2="&#xd2;" k="18" />
+<hkern u1="Y" u2="&#x131;" k="40" />
+<hkern u1="Z" u2="&#xf0;" k="30" />
+<hkern u1="Z" u2="&#xfe;" k="8" />
+<hkern u1="Z" u2="-" k="20" />
+<hkern u1="Z" u2="A" k="10" />
+<hkern u1="Z" u2="C" k="15" />
+<hkern u1="Z" u2="G" k="15" />
+<hkern u1="Z" u2="J" k="-5" />
+<hkern u1="Z" u2="O" k="15" />
+<hkern u1="Z" u2="Q" k="15" />
+<hkern u1="Z" u2="T" k="-1" />
+<hkern u1="Z" u2="a" k="8" />
+<hkern u1="Z" u2="b" k="10" />
+<hkern u1="Z" u2="c" k="18" />
+<hkern u1="Z" u2="d" k="18" />
+<hkern u1="Z" u2="e" k="18" />
+<hkern u1="Z" u2="f" k="20" />
+<hkern u1="Z" u2="g" k="20" />
+<hkern u1="Z" u2="h" k="10" />
+<hkern u1="Z" u2="i" k="20" />
+<hkern u1="Z" u2="j" k="15" />
+<hkern u1="Z" u2="k" k="10" />
+<hkern u1="Z" u2="l" k="10" />
+<hkern u1="Z" u2="m" k="20" />
+<hkern u1="Z" u2="n" k="20" />
+<hkern u1="Z" u2="o" k="18" />
+<hkern u1="Z" u2="p" k="20" />
+<hkern u1="Z" u2="q" k="18" />
+<hkern u1="Z" u2="r" k="20" />
+<hkern u1="Z" u2="u" k="20" />
+<hkern u1="Z" u2="v" k="30" />
+<hkern u1="Z" u2="w" k="10" />
+<hkern u1="Z" u2="y" k="30" />
+<hkern u1="Z" u2="&#xc4;" k="10" />
+<hkern u1="Z" u2="&#xc5;" k="10" />
+<hkern u1="Z" u2="&#xd6;" k="15" />
+<hkern u1="Z" u2="&#xe7;" k="18" />
+<hkern u1="Z" u2="&#xc6;" k="10" />
+<hkern u1="Z" u2="&#xd8;" k="15" />
+<hkern u1="Z" u2="&#xe6;" k="8" />
+<hkern u1="Z" u2="&#xc0;" k="10" />
+<hkern u1="Z" u2="&#xc3;" k="10" />
+<hkern u1="Z" u2="&#xd5;" k="15" />
+<hkern u1="Z" u2="&#x152;" k="15" />
+<hkern u1="Z" u2="&#x153;" k="18" />
+<hkern u1="Z" u2="&#x2013;" k="20" />
+<hkern u1="Z" u2="&#x2014;" k="20" />
+<hkern u1="Z" u2="&#xc2;" k="10" />
+<hkern u1="Z" u2="&#xc1;" k="10" />
+<hkern u1="Z" u2="&#xd3;" k="15" />
+<hkern u1="Z" u2="&#xd4;" k="15" />
+<hkern u1="Z" u2="&#xd2;" k="15" />
+<hkern u1="Z" u2="&#x131;" k="20" />
+<hkern u1="[" u2="&#xdd;" k="-50" />
+<hkern u1="[" u2="7" k="-15" />
+<hkern u1="[" u2="J" k="15" />
+<hkern u1="[" u2="V" k="-30" />
+<hkern u1="[" u2="W" k="-10" />
+<hkern u1="[" u2="X" k="-40" />
+<hkern u1="[" u2="Y" k="-50" />
+<hkern u1="[" u2="g" k="-20" />
+<hkern u1="[" u2="j" k="-150" />
+<hkern u1="[" u2="&#x178;" k="-50" />
+<hkern u1="\" u2="&#xdd;" k="60" />
+<hkern u1="\" u2="T" k="90" />
+<hkern u1="\" u2="V" k="70" />
+<hkern u1="\" u2="W" k="50" />
+<hkern u1="\" u2="Y" k="60" />
+<hkern u1="\" u2="a" k="10" />
+<hkern u1="\" u2="c" k="10" />
+<hkern u1="\" u2="d" k="10" />
+<hkern u1="\" u2="e" k="10" />
+<hkern u1="\" u2="j" k="-130" />
+<hkern u1="\" u2="o" k="10" />
+<hkern u1="\" u2="q" k="10" />
+<hkern u1="\" u2="&#xe7;" k="10" />
+<hkern u1="\" u2="&#xe6;" k="10" />
+<hkern u1="\" u2="&#x153;" k="10" />
+<hkern u1="\" u2="&#x178;" k="60" />
+<hkern u1="a" u2="?" k="18" />
+<hkern u1="a" u2="\" k="10" />
+<hkern u1="a" u2="f" k="4" />
+<hkern u1="a" u2="t" k="10" />
+<hkern u1="a" u2="v" k="9" />
+<hkern u1="a" u2="y" k="9" />
+<hkern u1="a" u2="&#x2122;" k="20" />
+<hkern u1="a" u2="&#x201c;" k="30" />
+<hkern u1="a" u2="&#x2018;" k="30" />
+<hkern u1="b" u2="!" k="15" />
+<hkern u1="b" u2="&#x2c;" k="20" />
+<hkern u1="b" u2="." k="20" />
+<hkern u1="b" u2="/" k="10" />
+<hkern u1="b" u2=":" k="10" />
+<hkern u1="b" u2=";" k="10" />
+<hkern u1="b" u2="?" k="31" />
+<hkern u1="b" u2="\" k="10" />
+<hkern u1="b" u2="f" k="8" />
+<hkern u1="b" u2="t" k="11" />
+<hkern u1="b" u2="v" k="21" />
+<hkern u1="b" u2="w" k="11" />
+<hkern u1="b" u2="x" k="23" />
+<hkern u1="b" u2="y" k="21" />
+<hkern u1="b" u2="z" k="19" />
+<hkern u1="b" u2="&#x2122;" k="30" />
+<hkern u1="b" u2="&#x2026;" k="20" />
+<hkern u1="b" u2="&#x201c;" k="25" />
+<hkern u1="b" u2="&#x2018;" k="25" />
+<hkern u1="b" u2="&#x201a;" k="20" />
+<hkern u1="b" u2="&#x201e;" k="20" />
+<hkern u1="c" u2="&#xab;" k="15" />
+<hkern u1="c" u2="&#x2039;" k="15" />
+<hkern u1="e" u2="f" k="8" />
+<hkern u1="e" u2="t" k="4" />
+<hkern u1="e" u2="v" k="18" />
+<hkern u1="e" u2="w" k="8" />
+<hkern u1="e" u2="x" k="19" />
+<hkern u1="e" u2="y" k="18" />
+<hkern u1="f" u2="&#xf0;" k="20" />
+<hkern u1="f" u2="&#xfe;" k="-11" />
+<hkern u1="f" u2="!" k="-30" />
+<hkern u1="f" u2="&amp;" k="15" />
+<hkern u1="f" u2=")" k="-70" />
+<hkern u1="f" u2="*" k="-28" />
+<hkern u1="f" u2="&#x2c;" k="58" />
+<hkern u1="f" u2="-" k="15" />
+<hkern u1="f" u2="." k="58" />
+<hkern u1="f" u2="/" k="65" />
+<hkern u1="f" u2=":" k="-5" />
+<hkern u1="f" u2=";" k="-5" />
+<hkern u1="f" u2="?" k="-40" />
+<hkern u1="f" u2="@" k="-15" />
+<hkern u1="f" u2="\" k="-40" />
+<hkern u1="f" u2="]" k="-70" />
+<hkern u1="f" u2="a" k="16" />
+<hkern u1="f" u2="b" k="-11" />
+<hkern u1="f" u2="c" k="16" />
+<hkern u1="f" u2="d" k="16" />
+<hkern u1="f" u2="e" k="16" />
+<hkern u1="f" u2="f" k="-8" />
+<hkern u1="f" u2="g" k="8" />
+<hkern u1="f" u2="h" k="-11" />
+<hkern u1="f" u2="k" k="-11" />
+<hkern u1="f" u2="l" k="-11" />
+<hkern u1="f" u2="m" k="1" />
+<hkern u1="f" u2="n" k="1" />
+<hkern u1="f" u2="o" k="16" />
+<hkern u1="f" u2="p" k="1" />
+<hkern u1="f" u2="q" k="16" />
+<hkern u1="f" u2="r" k="1" />
+<hkern u1="f" u2="t" k="-8" />
+<hkern u1="f" u2="u" k="1" />
+<hkern u1="f" u2="}" k="-70" />
+<hkern u1="f" u2="&#xe7;" k="16" />
+<hkern u1="f" u2="&#xae;" k="-15" />
+<hkern u1="f" u2="&#xa9;" k="-15" />
+<hkern u1="f" u2="&#x2122;" k="-48" />
+<hkern u1="f" u2="&#x3c0;" k="-15" />
+<hkern u1="f" u2="&#xe6;" k="16" />
+<hkern u1="f" u2="&#xab;" k="13" />
+<hkern u1="f" u2="&#xbb;" k="-3" />
+<hkern u1="f" u2="&#x2026;" k="58" />
+<hkern u1="f" u2="&#x153;" k="16" />
+<hkern u1="f" u2="&#x2013;" k="15" />
+<hkern u1="f" u2="&#x2014;" k="15" />
+<hkern u1="f" u2="&#x201c;" k="-30" />
+<hkern u1="f" u2="&#x201d;" k="-50" />
+<hkern u1="f" u2="&#x2018;" k="-30" />
+<hkern u1="f" u2="&#x2019;" k="-50" />
+<hkern u1="f" u2="&#x2039;" k="13" />
+<hkern u1="f" u2="&#x203a;" k="-3" />
+<hkern u1="f" u2="&#x201a;" k="58" />
+<hkern u1="f" u2="&#x201e;" k="58" />
+<hkern u1="f" u2="&#x131;" k="1" />
+<hkern u1="g" u2=")" k="-20" />
+<hkern u1="g" u2="*" k="-30" />
+<hkern u1="g" u2="&#x2c;" k="-23" />
+<hkern u1="g" u2="/" k="-48" />
+<hkern u1="g" u2=";" k="-20" />
+<hkern u1="g" u2="?" k="-23" />
+<hkern u1="g" u2="@" k="-15" />
+<hkern u1="g" u2="]" k="-20" />
+<hkern u1="g" u2="g" k="-10" />
+<hkern u1="g" u2="j" k="-50" />
+<hkern u1="g" u2="t" k="-10" />
+<hkern u1="g" u2="}" k="-20" />
+<hkern u1="g" u2="&#xae;" k="-15" />
+<hkern u1="g" u2="&#xa9;" k="-15" />
+<hkern u1="g" u2="&#x3c0;" k="-15" />
+<hkern u1="g" u2="&#x201c;" k="-20" />
+<hkern u1="g" u2="&#x201d;" k="-40" />
+<hkern u1="g" u2="&#x2018;" k="-20" />
+<hkern u1="g" u2="&#x2019;" k="-40" />
+<hkern u1="h" u2="?" k="18" />
+<hkern u1="h" u2="\" k="10" />
+<hkern u1="h" u2="f" k="4" />
+<hkern u1="h" u2="t" k="10" />
+<hkern u1="h" u2="v" k="9" />
+<hkern u1="h" u2="y" k="9" />
+<hkern u1="h" u2="&#x2122;" k="20" />
+<hkern u1="h" u2="&#x201c;" k="30" />
+<hkern u1="h" u2="&#x2018;" k="30" />
+<hkern u1="k" u2="a" k="14" />
+<hkern u1="k" u2="c" k="14" />
+<hkern u1="k" u2="d" k="14" />
+<hkern u1="k" u2="e" k="14" />
+<hkern u1="k" u2="g" k="20" />
+<hkern u1="k" u2="o" k="14" />
+<hkern u1="k" u2="q" k="14" />
+<hkern u1="k" u2="v" k="4" />
+<hkern u1="k" u2="y" k="4" />
+<hkern u1="k" u2="&#xe7;" k="14" />
+<hkern u1="k" u2="&#xe6;" k="14" />
+<hkern u1="k" u2="&#xab;" k="30" />
+<hkern u1="k" u2="&#x153;" k="14" />
+<hkern u1="k" u2="&#x2039;" k="30" />
+<hkern u1="m" u2="?" k="18" />
+<hkern u1="m" u2="\" k="10" />
+<hkern u1="m" u2="f" k="4" />
+<hkern u1="m" u2="t" k="10" />
+<hkern u1="m" u2="v" k="9" />
+<hkern u1="m" u2="y" k="9" />
+<hkern u1="m" u2="&#x2122;" k="20" />
+<hkern u1="m" u2="&#x201c;" k="30" />
+<hkern u1="m" u2="&#x2018;" k="30" />
+<hkern u1="n" u2="?" k="18" />
+<hkern u1="n" u2="\" k="10" />
+<hkern u1="n" u2="f" k="4" />
+<hkern u1="n" u2="t" k="10" />
+<hkern u1="n" u2="v" k="9" />
+<hkern u1="n" u2="y" k="9" />
+<hkern u1="n" u2="&#x2122;" k="20" />
+<hkern u1="n" u2="&#x201c;" k="30" />
+<hkern u1="n" u2="&#x2018;" k="30" />
+<hkern u1="o" u2="!" k="15" />
+<hkern u1="o" u2="&#x2c;" k="20" />
+<hkern u1="o" u2="." k="20" />
+<hkern u1="o" u2="/" k="10" />
+<hkern u1="o" u2=":" k="10" />
+<hkern u1="o" u2=";" k="10" />
+<hkern u1="o" u2="?" k="31" />
+<hkern u1="o" u2="\" k="10" />
+<hkern u1="o" u2="f" k="8" />
+<hkern u1="o" u2="t" k="11" />
+<hkern u1="o" u2="v" k="21" />
+<hkern u1="o" u2="w" k="11" />
+<hkern u1="o" u2="x" k="23" />
+<hkern u1="o" u2="y" k="21" />
+<hkern u1="o" u2="z" k="19" />
+<hkern u1="o" u2="&#x2122;" k="30" />
+<hkern u1="o" u2="&#x2026;" k="20" />
+<hkern u1="o" u2="&#x201c;" k="25" />
+<hkern u1="o" u2="&#x2018;" k="25" />
+<hkern u1="o" u2="&#x201a;" k="20" />
+<hkern u1="o" u2="&#x201e;" k="20" />
+<hkern u1="p" u2="!" k="15" />
+<hkern u1="p" u2="&#x2c;" k="20" />
+<hkern u1="p" u2="." k="20" />
+<hkern u1="p" u2="/" k="10" />
+<hkern u1="p" u2=":" k="10" />
+<hkern u1="p" u2=";" k="10" />
+<hkern u1="p" u2="?" k="31" />
+<hkern u1="p" u2="\" k="10" />
+<hkern u1="p" u2="f" k="8" />
+<hkern u1="p" u2="t" k="11" />
+<hkern u1="p" u2="v" k="21" />
+<hkern u1="p" u2="w" k="11" />
+<hkern u1="p" u2="x" k="23" />
+<hkern u1="p" u2="y" k="21" />
+<hkern u1="p" u2="z" k="19" />
+<hkern u1="p" u2="&#x2122;" k="30" />
+<hkern u1="p" u2="&#x2026;" k="20" />
+<hkern u1="p" u2="&#x201c;" k="25" />
+<hkern u1="p" u2="&#x2018;" k="25" />
+<hkern u1="p" u2="&#x201a;" k="20" />
+<hkern u1="p" u2="&#x201e;" k="20" />
+<hkern u1="r" u2="&amp;" k="23" />
+<hkern u1="r" u2="*" k="-8" />
+<hkern u1="r" u2="&#x2c;" k="80" />
+<hkern u1="r" u2="-" k="13" />
+<hkern u1="r" u2="." k="80" />
+<hkern u1="r" u2="/" k="60" />
+<hkern u1="r" u2="?" k="-5" />
+<hkern u1="r" u2="a" k="9" />
+<hkern u1="r" u2="c" k="9" />
+<hkern u1="r" u2="d" k="9" />
+<hkern u1="r" u2="e" k="9" />
+<hkern u1="r" u2="f" k="-4" />
+<hkern u1="r" u2="g" k="10" />
+<hkern u1="r" u2="o" k="9" />
+<hkern u1="r" u2="q" k="9" />
+<hkern u1="r" u2="t" k="-5" />
+<hkern u1="r" u2="x" k="4" />
+<hkern u1="r" u2="&#xe7;" k="9" />
+<hkern u1="r" u2="&#xe6;" k="9" />
+<hkern u1="r" u2="&#x2026;" k="80" />
+<hkern u1="r" u2="&#x153;" k="9" />
+<hkern u1="r" u2="&#x2013;" k="13" />
+<hkern u1="r" u2="&#x2014;" k="13" />
+<hkern u1="r" u2="&#x201c;" k="-20" />
+<hkern u1="r" u2="&#x201d;" k="-40" />
+<hkern u1="r" u2="&#x2018;" k="-20" />
+<hkern u1="r" u2="&#x2019;" k="-40" />
+<hkern u1="r" u2="&#x201a;" k="80" />
+<hkern u1="r" u2="&#x201e;" k="80" />
+<hkern u1="s" u2="&#x161;" k="-4" />
+<hkern u1="s" u2="?" k="15" />
+<hkern u1="s" u2="g" k="8" />
+<hkern u1="s" u2="s" k="-4" />
+<hkern u1="t" u2="&#x2c;" k="-10" />
+<hkern u1="t" u2="." k="-10" />
+<hkern u1="t" u2="a" k="13" />
+<hkern u1="t" u2="c" k="13" />
+<hkern u1="t" u2="d" k="13" />
+<hkern u1="t" u2="e" k="13" />
+<hkern u1="t" u2="o" k="13" />
+<hkern u1="t" u2="q" k="13" />
+<hkern u1="t" u2="t" k="4" />
+<hkern u1="t" u2="v" k="11" />
+<hkern u1="t" u2="y" k="11" />
+<hkern u1="t" u2="&#xe7;" k="13" />
+<hkern u1="t" u2="&#xe6;" k="13" />
+<hkern u1="t" u2="&#x2026;" k="-10" />
+<hkern u1="t" u2="&#x153;" k="13" />
+<hkern u1="t" u2="&#x201c;" k="-10" />
+<hkern u1="t" u2="&#x2018;" k="-10" />
+<hkern u1="t" u2="&#x201a;" k="-10" />
+<hkern u1="t" u2="&#x201e;" k="-10" />
+<hkern u1="u" u2="?" k="8" />
+<hkern u1="v" u2="&#x2c;" k="50" />
+<hkern u1="v" u2="-" k="10" />
+<hkern u1="v" u2="." k="50" />
+<hkern u1="v" u2="a" k="21" />
+<hkern u1="v" u2="c" k="21" />
+<hkern u1="v" u2="d" k="21" />
+<hkern u1="v" u2="e" k="21" />
+<hkern u1="v" u2="o" k="21" />
+<hkern u1="v" u2="q" k="21" />
+<hkern u1="v" u2="v" k="11" />
+<hkern u1="v" u2="w" k="11" />
+<hkern u1="v" u2="x" k="15" />
+<hkern u1="v" u2="y" k="11" />
+<hkern u1="v" u2="&#xe7;" k="21" />
+<hkern u1="v" u2="&#xe6;" k="21" />
+<hkern u1="v" u2="&#xab;" k="23" />
+<hkern u1="v" u2="&#x2026;" k="50" />
+<hkern u1="v" u2="&#x153;" k="21" />
+<hkern u1="v" u2="&#x2013;" k="10" />
+<hkern u1="v" u2="&#x2014;" k="10" />
+<hkern u1="v" u2="&#x201c;" k="-30" />
+<hkern u1="v" u2="&#x201d;" k="-30" />
+<hkern u1="v" u2="&#x2018;" k="-30" />
+<hkern u1="v" u2="&#x2019;" k="-30" />
+<hkern u1="v" u2="&#x2039;" k="23" />
+<hkern u1="v" u2="&#x201a;" k="50" />
+<hkern u1="v" u2="&#x201e;" k="50" />
+<hkern u1="w" u2="&#x2c;" k="30" />
+<hkern u1="w" u2="." k="30" />
+<hkern u1="w" u2="a" k="11" />
+<hkern u1="w" u2="c" k="11" />
+<hkern u1="w" u2="d" k="11" />
+<hkern u1="w" u2="e" k="11" />
+<hkern u1="w" u2="o" k="11" />
+<hkern u1="w" u2="q" k="11" />
+<hkern u1="w" u2="v" k="11" />
+<hkern u1="w" u2="w" k="11" />
+<hkern u1="w" u2="x" k="11" />
+<hkern u1="w" u2="y" k="11" />
+<hkern u1="w" u2="&#xe7;" k="11" />
+<hkern u1="w" u2="&#xe6;" k="11" />
+<hkern u1="w" u2="&#xab;" k="15" />
+<hkern u1="w" u2="&#x2026;" k="30" />
+<hkern u1="w" u2="&#x153;" k="11" />
+<hkern u1="w" u2="&#x201c;" k="-20" />
+<hkern u1="w" u2="&#x201d;" k="-20" />
+<hkern u1="w" u2="&#x2018;" k="-20" />
+<hkern u1="w" u2="&#x2019;" k="-20" />
+<hkern u1="w" u2="&#x2039;" k="15" />
+<hkern u1="w" u2="&#x201a;" k="30" />
+<hkern u1="w" u2="&#x201e;" k="30" />
+<hkern u1="x" u2="-" k="30" />
+<hkern u1="x" u2="a" k="23" />
+<hkern u1="x" u2="c" k="23" />
+<hkern u1="x" u2="d" k="23" />
+<hkern u1="x" u2="e" k="23" />
+<hkern u1="x" u2="o" k="23" />
+<hkern u1="x" u2="q" k="23" />
+<hkern u1="x" u2="v" k="15" />
+<hkern u1="x" u2="w" k="11" />
+<hkern u1="x" u2="y" k="15" />
+<hkern u1="x" u2="&#xe7;" k="23" />
+<hkern u1="x" u2="&#xe6;" k="23" />
+<hkern u1="x" u2="&#xab;" k="50" />
+<hkern u1="x" u2="&#x153;" k="23" />
+<hkern u1="x" u2="&#x2013;" k="30" />
+<hkern u1="x" u2="&#x2014;" k="30" />
+<hkern u1="x" u2="&#x201c;" k="-10" />
+<hkern u1="x" u2="&#x201d;" k="-20" />
+<hkern u1="x" u2="&#x2018;" k="-10" />
+<hkern u1="x" u2="&#x2019;" k="-20" />
+<hkern u1="x" u2="&#x2039;" k="50" />
+<hkern u1="y" u2="&#x2c;" k="50" />
+<hkern u1="y" u2="-" k="10" />
+<hkern u1="y" u2="." k="50" />
+<hkern u1="y" u2="a" k="21" />
+<hkern u1="y" u2="c" k="21" />
+<hkern u1="y" u2="d" k="21" />
+<hkern u1="y" u2="e" k="21" />
+<hkern u1="y" u2="o" k="21" />
+<hkern u1="y" u2="q" k="21" />
+<hkern u1="y" u2="v" k="11" />
+<hkern u1="y" u2="w" k="11" />
+<hkern u1="y" u2="x" k="15" />
+<hkern u1="y" u2="y" k="11" />
+<hkern u1="y" u2="&#xe7;" k="21" />
+<hkern u1="y" u2="&#xe6;" k="21" />
+<hkern u1="y" u2="&#xab;" k="23" />
+<hkern u1="y" u2="&#x2026;" k="50" />
+<hkern u1="y" u2="&#x153;" k="21" />
+<hkern u1="y" u2="&#x2013;" k="10" />
+<hkern u1="y" u2="&#x2014;" k="10" />
+<hkern u1="y" u2="&#x201c;" k="-30" />
+<hkern u1="y" u2="&#x201d;" k="-30" />
+<hkern u1="y" u2="&#x2018;" k="-30" />
+<hkern u1="y" u2="&#x2019;" k="-30" />
+<hkern u1="y" u2="&#x2039;" k="23" />
+<hkern u1="y" u2="&#x201a;" k="50" />
+<hkern u1="y" u2="&#x201e;" k="50" />
+<hkern u1="z" u2="-" k="15" />
+<hkern u1="z" u2="a" k="20" />
+<hkern u1="z" u2="c" k="20" />
+<hkern u1="z" u2="d" k="20" />
+<hkern u1="z" u2="e" k="20" />
+<hkern u1="z" u2="g" k="3" />
+<hkern u1="z" u2="o" k="20" />
+<hkern u1="z" u2="q" k="20" />
+<hkern u1="z" u2="&#xe7;" k="20" />
+<hkern u1="z" u2="&#xe6;" k="20" />
+<hkern u1="z" u2="&#xab;" k="45" />
+<hkern u1="z" u2="&#x153;" k="20" />
+<hkern u1="z" u2="&#x2013;" k="15" />
+<hkern u1="z" u2="&#x2014;" k="15" />
+<hkern u1="z" u2="&#x2039;" k="45" />
+<hkern u1="{" u2="&#xdd;" k="-50" />
+<hkern u1="{" u2="7" k="-15" />
+<hkern u1="{" u2="J" k="15" />
+<hkern u1="{" u2="V" k="-30" />
+<hkern u1="{" u2="W" k="-10" />
+<hkern u1="{" u2="X" k="-40" />
+<hkern u1="{" u2="Y" k="-50" />
+<hkern u1="{" u2="g" k="-20" />
+<hkern u1="{" u2="j" k="-150" />
+<hkern u1="{" u2="&#x178;" k="-50" />
+<hkern u1="&#xc4;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc4;" u2="&#x161;" k="-11" />
+<hkern u1="&#xc4;" u2="&#xdd;" k="54" />
+<hkern u1="&#xc4;" u2="*" k="75" />
+<hkern u1="&#xc4;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc4;" u2="-" k="-10" />
+<hkern u1="&#xc4;" u2="." k="-30" />
+<hkern u1="&#xc4;" u2="?" k="23" />
+<hkern u1="&#xc4;" u2="@" k="10" />
+<hkern u1="&#xc4;" u2="A" k="-10" />
+<hkern u1="&#xc4;" u2="C" k="20" />
+<hkern u1="&#xc4;" u2="G" k="20" />
+<hkern u1="&#xc4;" u2="J" k="-10" />
+<hkern u1="&#xc4;" u2="O" k="20" />
+<hkern u1="&#xc4;" u2="Q" k="20" />
+<hkern u1="&#xc4;" u2="S" k="-5" />
+<hkern u1="&#xc4;" u2="T" k="59" />
+<hkern u1="&#xc4;" u2="V" k="73" />
+<hkern u1="&#xc4;" u2="W" k="61" />
+<hkern u1="&#xc4;" u2="X" k="-10" />
+<hkern u1="&#xc4;" u2="Y" k="54" />
+<hkern u1="&#xc4;" u2="a" k="5" />
+<hkern u1="&#xc4;" u2="c" k="5" />
+<hkern u1="&#xc4;" u2="d" k="5" />
+<hkern u1="&#xc4;" u2="e" k="5" />
+<hkern u1="&#xc4;" u2="f" k="30" />
+<hkern u1="&#xc4;" u2="g" k="11" />
+<hkern u1="&#xc4;" u2="m" k="5" />
+<hkern u1="&#xc4;" u2="n" k="5" />
+<hkern u1="&#xc4;" u2="o" k="5" />
+<hkern u1="&#xc4;" u2="p" k="5" />
+<hkern u1="&#xc4;" u2="q" k="5" />
+<hkern u1="&#xc4;" u2="r" k="5" />
+<hkern u1="&#xc4;" u2="s" k="-11" />
+<hkern u1="&#xc4;" u2="t" k="5" />
+<hkern u1="&#xc4;" u2="u" k="5" />
+<hkern u1="&#xc4;" u2="v" k="60" />
+<hkern u1="&#xc4;" u2="w" k="40" />
+<hkern u1="&#xc4;" u2="x" k="-13" />
+<hkern u1="&#xc4;" u2="y" k="60" />
+<hkern u1="&#xc4;" u2="z" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc4;" u2="&#xae;" k="10" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc4;" u2="&#x152;" k="20" />
+<hkern u1="&#xc4;" u2="&#x153;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc4;" u2="&#x178;" k="54" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc4;" u2="&#x131;" k="5" />
+<hkern u1="&#xc5;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc5;" u2="&#x161;" k="-11" />
+<hkern u1="&#xc5;" u2="&#xdd;" k="54" />
+<hkern u1="&#xc5;" u2="*" k="75" />
+<hkern u1="&#xc5;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc5;" u2="-" k="-10" />
+<hkern u1="&#xc5;" u2="." k="-30" />
+<hkern u1="&#xc5;" u2="?" k="23" />
+<hkern u1="&#xc5;" u2="@" k="10" />
+<hkern u1="&#xc5;" u2="A" k="-10" />
+<hkern u1="&#xc5;" u2="C" k="20" />
+<hkern u1="&#xc5;" u2="G" k="20" />
+<hkern u1="&#xc5;" u2="J" k="-10" />
+<hkern u1="&#xc5;" u2="O" k="20" />
+<hkern u1="&#xc5;" u2="Q" k="20" />
+<hkern u1="&#xc5;" u2="S" k="-5" />
+<hkern u1="&#xc5;" u2="T" k="59" />
+<hkern u1="&#xc5;" u2="V" k="73" />
+<hkern u1="&#xc5;" u2="W" k="61" />
+<hkern u1="&#xc5;" u2="X" k="-10" />
+<hkern u1="&#xc5;" u2="Y" k="54" />
+<hkern u1="&#xc5;" u2="a" k="5" />
+<hkern u1="&#xc5;" u2="c" k="5" />
+<hkern u1="&#xc5;" u2="d" k="5" />
+<hkern u1="&#xc5;" u2="e" k="5" />
+<hkern u1="&#xc5;" u2="f" k="30" />
+<hkern u1="&#xc5;" u2="g" k="11" />
+<hkern u1="&#xc5;" u2="m" k="5" />
+<hkern u1="&#xc5;" u2="n" k="5" />
+<hkern u1="&#xc5;" u2="o" k="5" />
+<hkern u1="&#xc5;" u2="p" k="5" />
+<hkern u1="&#xc5;" u2="q" k="5" />
+<hkern u1="&#xc5;" u2="r" k="5" />
+<hkern u1="&#xc5;" u2="s" k="-11" />
+<hkern u1="&#xc5;" u2="t" k="5" />
+<hkern u1="&#xc5;" u2="u" k="5" />
+<hkern u1="&#xc5;" u2="v" k="60" />
+<hkern u1="&#xc5;" u2="w" k="40" />
+<hkern u1="&#xc5;" u2="x" k="-13" />
+<hkern u1="&#xc5;" u2="y" k="60" />
+<hkern u1="&#xc5;" u2="z" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc5;" u2="&#xae;" k="10" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc5;" u2="&#x152;" k="20" />
+<hkern u1="&#xc5;" u2="&#x153;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc5;" u2="&#x178;" k="54" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc5;" u2="&#x131;" k="5" />
+<hkern u1="&#xc9;" u2="&#xfe;" k="4" />
+<hkern u1="&#xc9;" u2="@" k="20" />
+<hkern u1="&#xc9;" u2="C" k="19" />
+<hkern u1="&#xc9;" u2="G" k="19" />
+<hkern u1="&#xc9;" u2="J" k="-4" />
+<hkern u1="&#xc9;" u2="O" k="19" />
+<hkern u1="&#xc9;" u2="Q" k="19" />
+<hkern u1="&#xc9;" u2="T" k="-8" />
+<hkern u1="&#xc9;" u2="V" k="-8" />
+<hkern u1="&#xc9;" u2="W" k="4" />
+<hkern u1="&#xc9;" u2="a" k="21" />
+<hkern u1="&#xc9;" u2="b" k="4" />
+<hkern u1="&#xc9;" u2="c" k="21" />
+<hkern u1="&#xc9;" u2="d" k="21" />
+<hkern u1="&#xc9;" u2="e" k="21" />
+<hkern u1="&#xc9;" u2="f" k="10" />
+<hkern u1="&#xc9;" u2="g" k="16" />
+<hkern u1="&#xc9;" u2="h" k="4" />
+<hkern u1="&#xc9;" u2="k" k="4" />
+<hkern u1="&#xc9;" u2="l" k="4" />
+<hkern u1="&#xc9;" u2="m" k="8" />
+<hkern u1="&#xc9;" u2="n" k="8" />
+<hkern u1="&#xc9;" u2="o" k="21" />
+<hkern u1="&#xc9;" u2="p" k="8" />
+<hkern u1="&#xc9;" u2="q" k="21" />
+<hkern u1="&#xc9;" u2="r" k="8" />
+<hkern u1="&#xc9;" u2="u" k="8" />
+<hkern u1="&#xc9;" u2="v" k="20" />
+<hkern u1="&#xc9;" u2="y" k="20" />
+<hkern u1="&#xc9;" u2="&#xd6;" k="19" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="21" />
+<hkern u1="&#xc9;" u2="&#xae;" k="20" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc9;" u2="&#xd8;" k="19" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="21" />
+<hkern u1="&#xc9;" u2="&#xab;" k="15" />
+<hkern u1="&#xc9;" u2="&#xd5;" k="19" />
+<hkern u1="&#xc9;" u2="&#x152;" k="19" />
+<hkern u1="&#xc9;" u2="&#x153;" k="21" />
+<hkern u1="&#xc9;" u2="&#x2039;" k="15" />
+<hkern u1="&#xc9;" u2="&#xd3;" k="19" />
+<hkern u1="&#xc9;" u2="&#xd4;" k="19" />
+<hkern u1="&#xc9;" u2="&#xd2;" k="19" />
+<hkern u1="&#xc9;" u2="&#x131;" k="8" />
+<hkern u1="&#xd1;" u2="/" k="20" />
+<hkern u1="&#xd1;" u2="v" k="10" />
+<hkern u1="&#xd1;" u2="y" k="10" />
+<hkern u1="&#xd6;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd6;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd6;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd6;" u2="." k="33" />
+<hkern u1="&#xd6;" u2="/" k="50" />
+<hkern u1="&#xd6;" u2="?" k="20" />
+<hkern u1="&#xd6;" u2="A" k="20" />
+<hkern u1="&#xd6;" u2="J" k="23" />
+<hkern u1="&#xd6;" u2="T" k="19" />
+<hkern u1="&#xd6;" u2="V" k="20" />
+<hkern u1="&#xd6;" u2="W" k="33" />
+<hkern u1="&#xd6;" u2="X" k="29" />
+<hkern u1="&#xd6;" u2="Y" k="18" />
+<hkern u1="&#xd6;" u2="Z" k="19" />
+<hkern u1="&#xd6;" u2="a" k="1" />
+<hkern u1="&#xd6;" u2="b" k="3" />
+<hkern u1="&#xd6;" u2="c" k="1" />
+<hkern u1="&#xd6;" u2="d" k="1" />
+<hkern u1="&#xd6;" u2="e" k="1" />
+<hkern u1="&#xd6;" u2="h" k="3" />
+<hkern u1="&#xd6;" u2="k" k="3" />
+<hkern u1="&#xd6;" u2="l" k="3" />
+<hkern u1="&#xd6;" u2="m" k="1" />
+<hkern u1="&#xd6;" u2="n" k="1" />
+<hkern u1="&#xd6;" u2="o" k="1" />
+<hkern u1="&#xd6;" u2="p" k="1" />
+<hkern u1="&#xd6;" u2="q" k="1" />
+<hkern u1="&#xd6;" u2="r" k="1" />
+<hkern u1="&#xd6;" u2="u" k="1" />
+<hkern u1="&#xd6;" u2="x" k="10" />
+<hkern u1="&#xd6;" u2="z" k="10" />
+<hkern u1="&#xd6;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd6;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd6;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd6;" u2="&#x153;" k="1" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd6;" u2="&#x178;" k="18" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd6;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd6;" u2="&#x131;" k="1" />
+<hkern u1="&#xdc;" u2="&#x2c;" k="10" />
+<hkern u1="&#xdc;" u2="." k="10" />
+<hkern u1="&#xdc;" u2="A" k="15" />
+<hkern u1="&#xdc;" u2="J" k="10" />
+<hkern u1="&#xdc;" u2="&#xc4;" k="15" />
+<hkern u1="&#xdc;" u2="&#xc5;" k="15" />
+<hkern u1="&#xdc;" u2="&#xc6;" k="15" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdc;" u2="&#xc0;" k="15" />
+<hkern u1="&#xdc;" u2="&#xc3;" k="15" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="10" />
+<hkern u1="&#xdc;" u2="&#xc2;" k="15" />
+<hkern u1="&#xdc;" u2="&#xc1;" k="15" />
+<hkern u1="&#xe1;" u2="?" k="18" />
+<hkern u1="&#xe1;" u2="\" k="10" />
+<hkern u1="&#xe1;" u2="f" k="4" />
+<hkern u1="&#xe1;" u2="t" k="10" />
+<hkern u1="&#xe1;" u2="v" k="9" />
+<hkern u1="&#xe1;" u2="y" k="9" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe0;" u2="?" k="18" />
+<hkern u1="&#xe0;" u2="\" k="10" />
+<hkern u1="&#xe0;" u2="f" k="4" />
+<hkern u1="&#xe0;" u2="t" k="10" />
+<hkern u1="&#xe0;" u2="v" k="9" />
+<hkern u1="&#xe0;" u2="y" k="9" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe2;" u2="?" k="18" />
+<hkern u1="&#xe2;" u2="\" k="10" />
+<hkern u1="&#xe2;" u2="f" k="4" />
+<hkern u1="&#xe2;" u2="t" k="10" />
+<hkern u1="&#xe2;" u2="v" k="9" />
+<hkern u1="&#xe2;" u2="y" k="9" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe4;" u2="?" k="18" />
+<hkern u1="&#xe4;" u2="\" k="10" />
+<hkern u1="&#xe4;" u2="f" k="4" />
+<hkern u1="&#xe4;" u2="t" k="10" />
+<hkern u1="&#xe4;" u2="v" k="9" />
+<hkern u1="&#xe4;" u2="y" k="9" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe7;" u2="&#xab;" k="15" />
+<hkern u1="&#xe7;" u2="&#x2039;" k="15" />
+<hkern u1="&#xe9;" u2="f" k="8" />
+<hkern u1="&#xe9;" u2="t" k="4" />
+<hkern u1="&#xe9;" u2="v" k="18" />
+<hkern u1="&#xe9;" u2="w" k="8" />
+<hkern u1="&#xe9;" u2="x" k="19" />
+<hkern u1="&#xe9;" u2="y" k="18" />
+<hkern u1="&#xe8;" u2="f" k="8" />
+<hkern u1="&#xe8;" u2="t" k="4" />
+<hkern u1="&#xe8;" u2="v" k="18" />
+<hkern u1="&#xe8;" u2="w" k="8" />
+<hkern u1="&#xe8;" u2="x" k="19" />
+<hkern u1="&#xe8;" u2="y" k="18" />
+<hkern u1="&#xea;" u2="f" k="8" />
+<hkern u1="&#xea;" u2="t" k="4" />
+<hkern u1="&#xea;" u2="v" k="18" />
+<hkern u1="&#xea;" u2="w" k="8" />
+<hkern u1="&#xea;" u2="x" k="19" />
+<hkern u1="&#xea;" u2="y" k="18" />
+<hkern u1="&#xeb;" u2="f" k="8" />
+<hkern u1="&#xeb;" u2="t" k="4" />
+<hkern u1="&#xeb;" u2="v" k="18" />
+<hkern u1="&#xeb;" u2="w" k="8" />
+<hkern u1="&#xeb;" u2="x" k="19" />
+<hkern u1="&#xeb;" u2="y" k="18" />
+<hkern u1="&#xf1;" u2="?" k="18" />
+<hkern u1="&#xf1;" u2="\" k="10" />
+<hkern u1="&#xf1;" u2="f" k="4" />
+<hkern u1="&#xf1;" u2="t" k="10" />
+<hkern u1="&#xf1;" u2="v" k="9" />
+<hkern u1="&#xf1;" u2="y" k="9" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xf3;" u2="!" k="15" />
+<hkern u1="&#xf3;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf3;" u2="." k="20" />
+<hkern u1="&#xf3;" u2="/" k="10" />
+<hkern u1="&#xf3;" u2=":" k="10" />
+<hkern u1="&#xf3;" u2=";" k="10" />
+<hkern u1="&#xf3;" u2="?" k="31" />
+<hkern u1="&#xf3;" u2="\" k="10" />
+<hkern u1="&#xf3;" u2="f" k="8" />
+<hkern u1="&#xf3;" u2="t" k="11" />
+<hkern u1="&#xf3;" u2="v" k="21" />
+<hkern u1="&#xf3;" u2="w" k="11" />
+<hkern u1="&#xf3;" u2="x" k="23" />
+<hkern u1="&#xf3;" u2="y" k="21" />
+<hkern u1="&#xf3;" u2="z" k="19" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="25" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="25" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf4;" u2="!" k="15" />
+<hkern u1="&#xf4;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf4;" u2="." k="20" />
+<hkern u1="&#xf4;" u2="/" k="10" />
+<hkern u1="&#xf4;" u2=":" k="10" />
+<hkern u1="&#xf4;" u2=";" k="10" />
+<hkern u1="&#xf4;" u2="?" k="31" />
+<hkern u1="&#xf4;" u2="\" k="10" />
+<hkern u1="&#xf4;" u2="f" k="8" />
+<hkern u1="&#xf4;" u2="t" k="11" />
+<hkern u1="&#xf4;" u2="v" k="21" />
+<hkern u1="&#xf4;" u2="w" k="11" />
+<hkern u1="&#xf4;" u2="x" k="23" />
+<hkern u1="&#xf4;" u2="y" k="21" />
+<hkern u1="&#xf4;" u2="z" k="19" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="25" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="25" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf6;" u2="!" k="15" />
+<hkern u1="&#xf6;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf6;" u2="." k="20" />
+<hkern u1="&#xf6;" u2="/" k="10" />
+<hkern u1="&#xf6;" u2=":" k="10" />
+<hkern u1="&#xf6;" u2=";" k="10" />
+<hkern u1="&#xf6;" u2="?" k="31" />
+<hkern u1="&#xf6;" u2="\" k="10" />
+<hkern u1="&#xf6;" u2="f" k="8" />
+<hkern u1="&#xf6;" u2="t" k="11" />
+<hkern u1="&#xf6;" u2="v" k="21" />
+<hkern u1="&#xf6;" u2="w" k="11" />
+<hkern u1="&#xf6;" u2="x" k="23" />
+<hkern u1="&#xf6;" u2="y" k="21" />
+<hkern u1="&#xf6;" u2="z" k="19" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="25" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="25" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf5;" u2="!" k="15" />
+<hkern u1="&#xf5;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf5;" u2="." k="20" />
+<hkern u1="&#xf5;" u2="/" k="10" />
+<hkern u1="&#xf5;" u2=":" k="10" />
+<hkern u1="&#xf5;" u2=";" k="10" />
+<hkern u1="&#xf5;" u2="?" k="31" />
+<hkern u1="&#xf5;" u2="\" k="10" />
+<hkern u1="&#xf5;" u2="f" k="8" />
+<hkern u1="&#xf5;" u2="t" k="11" />
+<hkern u1="&#xf5;" u2="v" k="21" />
+<hkern u1="&#xf5;" u2="w" k="11" />
+<hkern u1="&#xf5;" u2="x" k="23" />
+<hkern u1="&#xf5;" u2="y" k="21" />
+<hkern u1="&#xf5;" u2="z" k="19" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="25" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="25" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="20" />
+<hkern u1="&#xb0;" u2="4" k="16" />
+<hkern u1="&#xa3;" u2="1" k="-10" />
+<hkern u1="&#xa3;" u2="4" k="20" />
+<hkern u1="&#xdf;" u2="!" k="15" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="20" />
+<hkern u1="&#xdf;" u2="." k="20" />
+<hkern u1="&#xdf;" u2="/" k="10" />
+<hkern u1="&#xdf;" u2=":" k="10" />
+<hkern u1="&#xdf;" u2=";" k="10" />
+<hkern u1="&#xdf;" u2="?" k="31" />
+<hkern u1="&#xdf;" u2="\" k="10" />
+<hkern u1="&#xdf;" u2="f" k="8" />
+<hkern u1="&#xdf;" u2="g" k="10" />
+<hkern u1="&#xdf;" u2="t" k="10" />
+<hkern u1="&#xdf;" u2="v" k="21" />
+<hkern u1="&#xdf;" u2="w" k="11" />
+<hkern u1="&#xdf;" u2="x" k="23" />
+<hkern u1="&#xdf;" u2="y" k="21" />
+<hkern u1="&#xdf;" u2="z" k="19" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="30" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="25" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="25" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="20" />
+<hkern u1="&#xae;" u2="&#xdd;" k="30" />
+<hkern u1="&#xae;" u2="3" k="8" />
+<hkern u1="&#xae;" u2="A" k="10" />
+<hkern u1="&#xae;" u2="T" k="30" />
+<hkern u1="&#xae;" u2="V" k="8" />
+<hkern u1="&#xae;" u2="W" k="20" />
+<hkern u1="&#xae;" u2="X" k="23" />
+<hkern u1="&#xae;" u2="Y" k="30" />
+<hkern u1="&#xae;" u2="g" k="-8" />
+<hkern u1="&#xae;" u2="&#xc4;" k="10" />
+<hkern u1="&#xae;" u2="&#xc5;" k="10" />
+<hkern u1="&#xae;" u2="&#xc6;" k="10" />
+<hkern u1="&#xae;" u2="&#xc0;" k="10" />
+<hkern u1="&#xae;" u2="&#xc3;" k="10" />
+<hkern u1="&#xae;" u2="&#x178;" k="30" />
+<hkern u1="&#xae;" u2="&#xc2;" k="10" />
+<hkern u1="&#xae;" u2="&#xc1;" k="10" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="30" />
+<hkern u1="&#xa9;" u2="3" k="8" />
+<hkern u1="&#xa9;" u2="A" k="10" />
+<hkern u1="&#xa9;" u2="T" k="30" />
+<hkern u1="&#xa9;" u2="V" k="8" />
+<hkern u1="&#xa9;" u2="W" k="20" />
+<hkern u1="&#xa9;" u2="X" k="23" />
+<hkern u1="&#xa9;" u2="Y" k="30" />
+<hkern u1="&#xa9;" u2="g" k="-8" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="10" />
+<hkern u1="&#xa9;" u2="&#x178;" k="30" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="10" />
+<hkern u1="&#xc6;" u2="&#xfe;" k="4" />
+<hkern u1="&#xc6;" u2="@" k="20" />
+<hkern u1="&#xc6;" u2="C" k="19" />
+<hkern u1="&#xc6;" u2="G" k="19" />
+<hkern u1="&#xc6;" u2="J" k="-4" />
+<hkern u1="&#xc6;" u2="O" k="19" />
+<hkern u1="&#xc6;" u2="Q" k="19" />
+<hkern u1="&#xc6;" u2="T" k="-8" />
+<hkern u1="&#xc6;" u2="V" k="-8" />
+<hkern u1="&#xc6;" u2="W" k="4" />
+<hkern u1="&#xc6;" u2="a" k="21" />
+<hkern u1="&#xc6;" u2="b" k="4" />
+<hkern u1="&#xc6;" u2="c" k="21" />
+<hkern u1="&#xc6;" u2="d" k="21" />
+<hkern u1="&#xc6;" u2="e" k="21" />
+<hkern u1="&#xc6;" u2="f" k="10" />
+<hkern u1="&#xc6;" u2="g" k="16" />
+<hkern u1="&#xc6;" u2="h" k="4" />
+<hkern u1="&#xc6;" u2="k" k="4" />
+<hkern u1="&#xc6;" u2="l" k="4" />
+<hkern u1="&#xc6;" u2="m" k="8" />
+<hkern u1="&#xc6;" u2="n" k="8" />
+<hkern u1="&#xc6;" u2="o" k="21" />
+<hkern u1="&#xc6;" u2="p" k="8" />
+<hkern u1="&#xc6;" u2="q" k="21" />
+<hkern u1="&#xc6;" u2="r" k="8" />
+<hkern u1="&#xc6;" u2="u" k="8" />
+<hkern u1="&#xc6;" u2="v" k="20" />
+<hkern u1="&#xc6;" u2="y" k="20" />
+<hkern u1="&#xc6;" u2="&#xd6;" k="19" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="21" />
+<hkern u1="&#xc6;" u2="&#xae;" k="20" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc6;" u2="&#xd8;" k="19" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="21" />
+<hkern u1="&#xc6;" u2="&#xab;" k="15" />
+<hkern u1="&#xc6;" u2="&#xd5;" k="19" />
+<hkern u1="&#xc6;" u2="&#x152;" k="19" />
+<hkern u1="&#xc6;" u2="&#x153;" k="21" />
+<hkern u1="&#xc6;" u2="&#x2039;" k="15" />
+<hkern u1="&#xc6;" u2="&#xd3;" k="19" />
+<hkern u1="&#xc6;" u2="&#xd4;" k="19" />
+<hkern u1="&#xc6;" u2="&#xd2;" k="19" />
+<hkern u1="&#xc6;" u2="&#x131;" k="8" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd8;" u2="." k="33" />
+<hkern u1="&#xd8;" u2="/" k="50" />
+<hkern u1="&#xd8;" u2="?" k="20" />
+<hkern u1="&#xd8;" u2="A" k="20" />
+<hkern u1="&#xd8;" u2="J" k="23" />
+<hkern u1="&#xd8;" u2="T" k="19" />
+<hkern u1="&#xd8;" u2="V" k="20" />
+<hkern u1="&#xd8;" u2="W" k="33" />
+<hkern u1="&#xd8;" u2="X" k="29" />
+<hkern u1="&#xd8;" u2="Y" k="18" />
+<hkern u1="&#xd8;" u2="Z" k="19" />
+<hkern u1="&#xd8;" u2="a" k="1" />
+<hkern u1="&#xd8;" u2="b" k="3" />
+<hkern u1="&#xd8;" u2="c" k="1" />
+<hkern u1="&#xd8;" u2="d" k="1" />
+<hkern u1="&#xd8;" u2="e" k="1" />
+<hkern u1="&#xd8;" u2="h" k="3" />
+<hkern u1="&#xd8;" u2="k" k="3" />
+<hkern u1="&#xd8;" u2="l" k="3" />
+<hkern u1="&#xd8;" u2="m" k="1" />
+<hkern u1="&#xd8;" u2="n" k="1" />
+<hkern u1="&#xd8;" u2="o" k="1" />
+<hkern u1="&#xd8;" u2="p" k="1" />
+<hkern u1="&#xd8;" u2="q" k="1" />
+<hkern u1="&#xd8;" u2="r" k="1" />
+<hkern u1="&#xd8;" u2="u" k="1" />
+<hkern u1="&#xd8;" u2="x" k="10" />
+<hkern u1="&#xd8;" u2="z" k="10" />
+<hkern u1="&#xd8;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd8;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd8;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd8;" u2="&#x153;" k="1" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd8;" u2="&#x178;" k="18" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd8;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd8;" u2="&#x131;" k="1" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="30" />
+<hkern u1="&#x3c0;" u2="3" k="8" />
+<hkern u1="&#x3c0;" u2="A" k="10" />
+<hkern u1="&#x3c0;" u2="T" k="30" />
+<hkern u1="&#x3c0;" u2="V" k="8" />
+<hkern u1="&#x3c0;" u2="W" k="20" />
+<hkern u1="&#x3c0;" u2="X" k="23" />
+<hkern u1="&#x3c0;" u2="Y" k="30" />
+<hkern u1="&#x3c0;" u2="g" k="-8" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="10" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="10" />
+<hkern u1="&#xe6;" u2="f" k="8" />
+<hkern u1="&#xe6;" u2="t" k="4" />
+<hkern u1="&#xe6;" u2="v" k="18" />
+<hkern u1="&#xe6;" u2="w" k="8" />
+<hkern u1="&#xe6;" u2="x" k="19" />
+<hkern u1="&#xe6;" u2="y" k="18" />
+<hkern u1="&#xf8;" u2="!" k="15" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf8;" u2="." k="20" />
+<hkern u1="&#xf8;" u2="/" k="10" />
+<hkern u1="&#xf8;" u2=":" k="10" />
+<hkern u1="&#xf8;" u2=";" k="10" />
+<hkern u1="&#xf8;" u2="?" k="31" />
+<hkern u1="&#xf8;" u2="\" k="10" />
+<hkern u1="&#xf8;" u2="f" k="8" />
+<hkern u1="&#xf8;" u2="t" k="11" />
+<hkern u1="&#xf8;" u2="v" k="21" />
+<hkern u1="&#xf8;" u2="w" k="11" />
+<hkern u1="&#xf8;" u2="x" k="23" />
+<hkern u1="&#xf8;" u2="y" k="21" />
+<hkern u1="&#xf8;" u2="z" k="19" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="25" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="25" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="20" />
+<hkern u1="&#xbf;" u2="&#x161;" k="-8" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="55" />
+<hkern u1="&#xbf;" u2="1" k="55" />
+<hkern u1="&#xbf;" u2="2" k="-15" />
+<hkern u1="&#xbf;" u2="3" k="-3" />
+<hkern u1="&#xbf;" u2="4" k="-8" />
+<hkern u1="&#xbf;" u2="7" k="38" />
+<hkern u1="&#xbf;" u2="C" k="20" />
+<hkern u1="&#xbf;" u2="G" k="20" />
+<hkern u1="&#xbf;" u2="O" k="20" />
+<hkern u1="&#xbf;" u2="Q" k="20" />
+<hkern u1="&#xbf;" u2="T" k="68" />
+<hkern u1="&#xbf;" u2="V" k="65" />
+<hkern u1="&#xbf;" u2="W" k="45" />
+<hkern u1="&#xbf;" u2="Y" k="55" />
+<hkern u1="&#xbf;" u2="a" k="-8" />
+<hkern u1="&#xbf;" u2="c" k="-8" />
+<hkern u1="&#xbf;" u2="d" k="-8" />
+<hkern u1="&#xbf;" u2="e" k="-8" />
+<hkern u1="&#xbf;" u2="o" k="-8" />
+<hkern u1="&#xbf;" u2="q" k="-8" />
+<hkern u1="&#xbf;" u2="s" k="-8" />
+<hkern u1="&#xbf;" u2="t" k="30" />
+<hkern u1="&#xbf;" u2="v" k="45" />
+<hkern u1="&#xbf;" u2="w" k="35" />
+<hkern u1="&#xbf;" u2="x" k="10" />
+<hkern u1="&#xbf;" u2="y" k="45" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="20" />
+<hkern u1="&#xbf;" u2="&#xe7;" k="-8" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="20" />
+<hkern u1="&#xbf;" u2="&#xe6;" k="-8" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="20" />
+<hkern u1="&#xbf;" u2="&#x152;" k="20" />
+<hkern u1="&#xbf;" u2="&#x153;" k="-8" />
+<hkern u1="&#xbf;" u2="&#x178;" k="55" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="20" />
+<hkern u1="&#xa1;" u2="&#xdd;" k="15" />
+<hkern u1="&#xa1;" u2="V" k="15" />
+<hkern u1="&#xa1;" u2="W" k="15" />
+<hkern u1="&#xa1;" u2="Y" k="15" />
+<hkern u1="&#xa1;" u2="f" k="15" />
+<hkern u1="&#xa1;" u2="v" k="30" />
+<hkern u1="&#xa1;" u2="w" k="19" />
+<hkern u1="&#xa1;" u2="y" k="30" />
+<hkern u1="&#xa1;" u2="&#x178;" k="15" />
+<hkern u1="&#x192;" u2="1" k="-30" />
+<hkern u1="&#xab;" u2="&#xdd;" k="40" />
+<hkern u1="&#xab;" u2="T" k="40" />
+<hkern u1="&#xab;" u2="V" k="20" />
+<hkern u1="&#xab;" u2="W" k="20" />
+<hkern u1="&#xab;" u2="Y" k="40" />
+<hkern u1="&#xab;" u2="f" k="-15" />
+<hkern u1="&#xab;" u2="&#x178;" k="40" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="50" />
+<hkern u1="&#xbb;" u2="T" k="110" />
+<hkern u1="&#xbb;" u2="V" k="50" />
+<hkern u1="&#xbb;" u2="W" k="40" />
+<hkern u1="&#xbb;" u2="Y" k="50" />
+<hkern u1="&#xbb;" u2="f" k="8" />
+<hkern u1="&#xbb;" u2="v" k="23" />
+<hkern u1="&#xbb;" u2="w" k="15" />
+<hkern u1="&#xbb;" u2="x" k="50" />
+<hkern u1="&#xbb;" u2="y" k="23" />
+<hkern u1="&#xbb;" u2="z" k="35" />
+<hkern u1="&#xbb;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2026;" u2="0" k="18" />
+<hkern u1="&#x2026;" u2="1" k="86" />
+<hkern u1="&#x2026;" u2="2" k="-8" />
+<hkern u1="&#x2026;" u2="3" k="-8" />
+<hkern u1="&#x2026;" u2="4" k="13" />
+<hkern u1="&#x2026;" u2="5" k="-15" />
+<hkern u1="&#x2026;" u2="6" k="23" />
+<hkern u1="&#x2026;" u2="7" k="8" />
+<hkern u1="&#x2026;" u2="8" k="15" />
+<hkern u1="&#x2026;" u2="9" k="3" />
+<hkern u1="&#x2026;" u2="A" k="-30" />
+<hkern u1="&#x2026;" u2="C" k="33" />
+<hkern u1="&#x2026;" u2="G" k="33" />
+<hkern u1="&#x2026;" u2="O" k="33" />
+<hkern u1="&#x2026;" u2="Q" k="33" />
+<hkern u1="&#x2026;" u2="T" k="83" />
+<hkern u1="&#x2026;" u2="U" k="10" />
+<hkern u1="&#x2026;" u2="V" k="68" />
+<hkern u1="&#x2026;" u2="W" k="28" />
+<hkern u1="&#x2026;" u2="Y" k="50" />
+<hkern u1="&#x2026;" u2="a" k="20" />
+<hkern u1="&#x2026;" u2="c" k="20" />
+<hkern u1="&#x2026;" u2="d" k="20" />
+<hkern u1="&#x2026;" u2="e" k="20" />
+<hkern u1="&#x2026;" u2="f" k="30" />
+<hkern u1="&#x2026;" u2="m" k="20" />
+<hkern u1="&#x2026;" u2="n" k="20" />
+<hkern u1="&#x2026;" u2="o" k="20" />
+<hkern u1="&#x2026;" u2="p" k="20" />
+<hkern u1="&#x2026;" u2="q" k="20" />
+<hkern u1="&#x2026;" u2="r" k="20" />
+<hkern u1="&#x2026;" u2="t" k="40" />
+<hkern u1="&#x2026;" u2="u" k="20" />
+<hkern u1="&#x2026;" u2="v" k="30" />
+<hkern u1="&#x2026;" u2="w" k="10" />
+<hkern u1="&#x2026;" u2="y" k="30" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="33" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="33" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="33" />
+<hkern u1="&#x2026;" u2="&#x152;" k="33" />
+<hkern u1="&#x2026;" u2="&#x153;" k="20" />
+<hkern u1="&#x2026;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="33" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="33" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="33" />
+<hkern u1="&#x2026;" u2="&#xda;" k="10" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2026;" u2="&#x131;" k="20" />
+<hkern u1="&#xc0;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc0;" u2="&#x161;" k="-11" />
+<hkern u1="&#xc0;" u2="&#xdd;" k="54" />
+<hkern u1="&#xc0;" u2="*" k="75" />
+<hkern u1="&#xc0;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc0;" u2="-" k="-10" />
+<hkern u1="&#xc0;" u2="." k="-30" />
+<hkern u1="&#xc0;" u2="?" k="23" />
+<hkern u1="&#xc0;" u2="@" k="10" />
+<hkern u1="&#xc0;" u2="A" k="-10" />
+<hkern u1="&#xc0;" u2="C" k="20" />
+<hkern u1="&#xc0;" u2="G" k="20" />
+<hkern u1="&#xc0;" u2="J" k="-10" />
+<hkern u1="&#xc0;" u2="O" k="20" />
+<hkern u1="&#xc0;" u2="Q" k="20" />
+<hkern u1="&#xc0;" u2="S" k="-5" />
+<hkern u1="&#xc0;" u2="T" k="59" />
+<hkern u1="&#xc0;" u2="V" k="73" />
+<hkern u1="&#xc0;" u2="W" k="61" />
+<hkern u1="&#xc0;" u2="X" k="-10" />
+<hkern u1="&#xc0;" u2="Y" k="54" />
+<hkern u1="&#xc0;" u2="a" k="5" />
+<hkern u1="&#xc0;" u2="c" k="5" />
+<hkern u1="&#xc0;" u2="d" k="5" />
+<hkern u1="&#xc0;" u2="e" k="5" />
+<hkern u1="&#xc0;" u2="f" k="30" />
+<hkern u1="&#xc0;" u2="g" k="11" />
+<hkern u1="&#xc0;" u2="m" k="5" />
+<hkern u1="&#xc0;" u2="n" k="5" />
+<hkern u1="&#xc0;" u2="o" k="5" />
+<hkern u1="&#xc0;" u2="p" k="5" />
+<hkern u1="&#xc0;" u2="q" k="5" />
+<hkern u1="&#xc0;" u2="r" k="5" />
+<hkern u1="&#xc0;" u2="s" k="-11" />
+<hkern u1="&#xc0;" u2="t" k="5" />
+<hkern u1="&#xc0;" u2="u" k="5" />
+<hkern u1="&#xc0;" u2="v" k="60" />
+<hkern u1="&#xc0;" u2="w" k="40" />
+<hkern u1="&#xc0;" u2="x" k="-13" />
+<hkern u1="&#xc0;" u2="y" k="60" />
+<hkern u1="&#xc0;" u2="z" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc0;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc0;" u2="&#xae;" k="10" />
+<hkern u1="&#xc0;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc0;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc0;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc0;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc0;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc0;" u2="&#x152;" k="20" />
+<hkern u1="&#xc0;" u2="&#x153;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc0;" u2="&#x178;" k="54" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc0;" u2="&#x131;" k="5" />
+<hkern u1="&#xc3;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc3;" u2="&#x161;" k="-11" />
+<hkern u1="&#xc3;" u2="&#xdd;" k="54" />
+<hkern u1="&#xc3;" u2="*" k="75" />
+<hkern u1="&#xc3;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc3;" u2="-" k="-10" />
+<hkern u1="&#xc3;" u2="." k="-30" />
+<hkern u1="&#xc3;" u2="?" k="23" />
+<hkern u1="&#xc3;" u2="@" k="10" />
+<hkern u1="&#xc3;" u2="A" k="-10" />
+<hkern u1="&#xc3;" u2="C" k="20" />
+<hkern u1="&#xc3;" u2="G" k="20" />
+<hkern u1="&#xc3;" u2="J" k="-10" />
+<hkern u1="&#xc3;" u2="O" k="20" />
+<hkern u1="&#xc3;" u2="Q" k="20" />
+<hkern u1="&#xc3;" u2="S" k="-5" />
+<hkern u1="&#xc3;" u2="T" k="59" />
+<hkern u1="&#xc3;" u2="V" k="73" />
+<hkern u1="&#xc3;" u2="W" k="61" />
+<hkern u1="&#xc3;" u2="X" k="-10" />
+<hkern u1="&#xc3;" u2="Y" k="54" />
+<hkern u1="&#xc3;" u2="a" k="5" />
+<hkern u1="&#xc3;" u2="c" k="5" />
+<hkern u1="&#xc3;" u2="d" k="5" />
+<hkern u1="&#xc3;" u2="e" k="5" />
+<hkern u1="&#xc3;" u2="f" k="30" />
+<hkern u1="&#xc3;" u2="g" k="11" />
+<hkern u1="&#xc3;" u2="m" k="5" />
+<hkern u1="&#xc3;" u2="n" k="5" />
+<hkern u1="&#xc3;" u2="o" k="5" />
+<hkern u1="&#xc3;" u2="p" k="5" />
+<hkern u1="&#xc3;" u2="q" k="5" />
+<hkern u1="&#xc3;" u2="r" k="5" />
+<hkern u1="&#xc3;" u2="s" k="-11" />
+<hkern u1="&#xc3;" u2="t" k="5" />
+<hkern u1="&#xc3;" u2="u" k="5" />
+<hkern u1="&#xc3;" u2="v" k="60" />
+<hkern u1="&#xc3;" u2="w" k="40" />
+<hkern u1="&#xc3;" u2="x" k="-13" />
+<hkern u1="&#xc3;" u2="y" k="60" />
+<hkern u1="&#xc3;" u2="z" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc3;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc3;" u2="&#xae;" k="10" />
+<hkern u1="&#xc3;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc3;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc3;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc3;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc3;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc3;" u2="&#x152;" k="20" />
+<hkern u1="&#xc3;" u2="&#x153;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc3;" u2="&#x178;" k="54" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc3;" u2="&#x131;" k="5" />
+<hkern u1="&#xd5;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd5;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd5;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd5;" u2="." k="33" />
+<hkern u1="&#xd5;" u2="/" k="50" />
+<hkern u1="&#xd5;" u2="?" k="20" />
+<hkern u1="&#xd5;" u2="A" k="20" />
+<hkern u1="&#xd5;" u2="J" k="23" />
+<hkern u1="&#xd5;" u2="T" k="19" />
+<hkern u1="&#xd5;" u2="V" k="20" />
+<hkern u1="&#xd5;" u2="W" k="33" />
+<hkern u1="&#xd5;" u2="X" k="29" />
+<hkern u1="&#xd5;" u2="Y" k="18" />
+<hkern u1="&#xd5;" u2="Z" k="19" />
+<hkern u1="&#xd5;" u2="a" k="1" />
+<hkern u1="&#xd5;" u2="b" k="3" />
+<hkern u1="&#xd5;" u2="c" k="1" />
+<hkern u1="&#xd5;" u2="d" k="1" />
+<hkern u1="&#xd5;" u2="e" k="1" />
+<hkern u1="&#xd5;" u2="h" k="3" />
+<hkern u1="&#xd5;" u2="k" k="3" />
+<hkern u1="&#xd5;" u2="l" k="3" />
+<hkern u1="&#xd5;" u2="m" k="1" />
+<hkern u1="&#xd5;" u2="n" k="1" />
+<hkern u1="&#xd5;" u2="o" k="1" />
+<hkern u1="&#xd5;" u2="p" k="1" />
+<hkern u1="&#xd5;" u2="q" k="1" />
+<hkern u1="&#xd5;" u2="r" k="1" />
+<hkern u1="&#xd5;" u2="u" k="1" />
+<hkern u1="&#xd5;" u2="x" k="10" />
+<hkern u1="&#xd5;" u2="z" k="10" />
+<hkern u1="&#xd5;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd5;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd5;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd5;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd5;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd5;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd5;" u2="&#x153;" k="1" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd5;" u2="&#x178;" k="18" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd5;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd5;" u2="&#x131;" k="1" />
+<hkern u1="&#x152;" u2="&#xfe;" k="4" />
+<hkern u1="&#x152;" u2="@" k="20" />
+<hkern u1="&#x152;" u2="C" k="19" />
+<hkern u1="&#x152;" u2="G" k="19" />
+<hkern u1="&#x152;" u2="J" k="-4" />
+<hkern u1="&#x152;" u2="O" k="19" />
+<hkern u1="&#x152;" u2="Q" k="19" />
+<hkern u1="&#x152;" u2="T" k="-8" />
+<hkern u1="&#x152;" u2="V" k="-8" />
+<hkern u1="&#x152;" u2="W" k="4" />
+<hkern u1="&#x152;" u2="a" k="21" />
+<hkern u1="&#x152;" u2="b" k="4" />
+<hkern u1="&#x152;" u2="c" k="21" />
+<hkern u1="&#x152;" u2="d" k="21" />
+<hkern u1="&#x152;" u2="e" k="21" />
+<hkern u1="&#x152;" u2="f" k="10" />
+<hkern u1="&#x152;" u2="g" k="16" />
+<hkern u1="&#x152;" u2="h" k="4" />
+<hkern u1="&#x152;" u2="k" k="4" />
+<hkern u1="&#x152;" u2="l" k="4" />
+<hkern u1="&#x152;" u2="m" k="8" />
+<hkern u1="&#x152;" u2="n" k="8" />
+<hkern u1="&#x152;" u2="o" k="21" />
+<hkern u1="&#x152;" u2="p" k="8" />
+<hkern u1="&#x152;" u2="q" k="21" />
+<hkern u1="&#x152;" u2="r" k="8" />
+<hkern u1="&#x152;" u2="u" k="8" />
+<hkern u1="&#x152;" u2="v" k="20" />
+<hkern u1="&#x152;" u2="y" k="20" />
+<hkern u1="&#x152;" u2="&#xd6;" k="19" />
+<hkern u1="&#x152;" u2="&#xe7;" k="21" />
+<hkern u1="&#x152;" u2="&#xae;" k="20" />
+<hkern u1="&#x152;" u2="&#xa9;" k="20" />
+<hkern u1="&#x152;" u2="&#xd8;" k="19" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="20" />
+<hkern u1="&#x152;" u2="&#xe6;" k="21" />
+<hkern u1="&#x152;" u2="&#xab;" k="15" />
+<hkern u1="&#x152;" u2="&#xd5;" k="19" />
+<hkern u1="&#x152;" u2="&#x152;" k="19" />
+<hkern u1="&#x152;" u2="&#x153;" k="21" />
+<hkern u1="&#x152;" u2="&#x2039;" k="15" />
+<hkern u1="&#x152;" u2="&#xd3;" k="19" />
+<hkern u1="&#x152;" u2="&#xd4;" k="19" />
+<hkern u1="&#x152;" u2="&#xd2;" k="19" />
+<hkern u1="&#x152;" u2="&#x131;" k="8" />
+<hkern u1="&#x153;" u2="f" k="8" />
+<hkern u1="&#x153;" u2="t" k="4" />
+<hkern u1="&#x153;" u2="v" k="18" />
+<hkern u1="&#x153;" u2="w" k="8" />
+<hkern u1="&#x153;" u2="x" k="19" />
+<hkern u1="&#x153;" u2="y" k="18" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2013;" u2="1" k="20" />
+<hkern u1="&#x2013;" u2="7" k="30" />
+<hkern u1="&#x2013;" u2="A" k="-10" />
+<hkern u1="&#x2013;" u2="T" k="55" />
+<hkern u1="&#x2013;" u2="V" k="50" />
+<hkern u1="&#x2013;" u2="W" k="40" />
+<hkern u1="&#x2013;" u2="X" k="30" />
+<hkern u1="&#x2013;" u2="Y" k="50" />
+<hkern u1="&#x2013;" u2="Z" k="10" />
+<hkern u1="&#x2013;" u2="a" k="10" />
+<hkern u1="&#x2013;" u2="c" k="10" />
+<hkern u1="&#x2013;" u2="d" k="10" />
+<hkern u1="&#x2013;" u2="e" k="10" />
+<hkern u1="&#x2013;" u2="o" k="10" />
+<hkern u1="&#x2013;" u2="q" k="10" />
+<hkern u1="&#x2013;" u2="v" k="10" />
+<hkern u1="&#x2013;" u2="x" k="30" />
+<hkern u1="&#x2013;" u2="y" k="10" />
+<hkern u1="&#x2013;" u2="z" k="15" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2013;" u2="&#x153;" k="10" />
+<hkern u1="&#x2013;" u2="&#x178;" k="50" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2014;" u2="1" k="20" />
+<hkern u1="&#x2014;" u2="7" k="30" />
+<hkern u1="&#x2014;" u2="A" k="-10" />
+<hkern u1="&#x2014;" u2="T" k="55" />
+<hkern u1="&#x2014;" u2="V" k="50" />
+<hkern u1="&#x2014;" u2="W" k="40" />
+<hkern u1="&#x2014;" u2="X" k="30" />
+<hkern u1="&#x2014;" u2="Y" k="50" />
+<hkern u1="&#x2014;" u2="Z" k="10" />
+<hkern u1="&#x2014;" u2="a" k="10" />
+<hkern u1="&#x2014;" u2="c" k="10" />
+<hkern u1="&#x2014;" u2="d" k="10" />
+<hkern u1="&#x2014;" u2="e" k="10" />
+<hkern u1="&#x2014;" u2="o" k="10" />
+<hkern u1="&#x2014;" u2="q" k="10" />
+<hkern u1="&#x2014;" u2="v" k="10" />
+<hkern u1="&#x2014;" u2="x" k="30" />
+<hkern u1="&#x2014;" u2="y" k="10" />
+<hkern u1="&#x2014;" u2="z" k="15" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2014;" u2="&#x153;" k="10" />
+<hkern u1="&#x2014;" u2="&#x178;" k="50" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x201c;" u2="&#x161;" k="30" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x201c;" u2="A" k="40" />
+<hkern u1="&#x201c;" u2="J" k="118" />
+<hkern u1="&#x201c;" u2="T" k="-10" />
+<hkern u1="&#x201c;" u2="V" k="-20" />
+<hkern u1="&#x201c;" u2="W" k="-20" />
+<hkern u1="&#x201c;" u2="X" k="-20" />
+<hkern u1="&#x201c;" u2="Y" k="-30" />
+<hkern u1="&#x201c;" u2="a" k="30" />
+<hkern u1="&#x201c;" u2="c" k="30" />
+<hkern u1="&#x201c;" u2="d" k="30" />
+<hkern u1="&#x201c;" u2="e" k="30" />
+<hkern u1="&#x201c;" u2="g" k="40" />
+<hkern u1="&#x201c;" u2="m" k="10" />
+<hkern u1="&#x201c;" u2="n" k="10" />
+<hkern u1="&#x201c;" u2="o" k="30" />
+<hkern u1="&#x201c;" u2="p" k="10" />
+<hkern u1="&#x201c;" u2="q" k="30" />
+<hkern u1="&#x201c;" u2="r" k="10" />
+<hkern u1="&#x201c;" u2="s" k="30" />
+<hkern u1="&#x201c;" u2="u" k="10" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="40" />
+<hkern u1="&#x201c;" u2="&#x153;" k="30" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-30" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="40" />
+<hkern u1="&#x201c;" u2="&#x131;" k="10" />
+<hkern u1="&#x201d;" u2="&#x160;" k="10" />
+<hkern u1="&#x201d;" u2="A" k="38" />
+<hkern u1="&#x201d;" u2="C" k="40" />
+<hkern u1="&#x201d;" u2="G" k="40" />
+<hkern u1="&#x201d;" u2="J" k="120" />
+<hkern u1="&#x201d;" u2="O" k="40" />
+<hkern u1="&#x201d;" u2="Q" k="40" />
+<hkern u1="&#x201d;" u2="S" k="10" />
+<hkern u1="&#x201d;" u2="a" k="50" />
+<hkern u1="&#x201d;" u2="c" k="50" />
+<hkern u1="&#x201d;" u2="d" k="50" />
+<hkern u1="&#x201d;" u2="e" k="50" />
+<hkern u1="&#x201d;" u2="o" k="50" />
+<hkern u1="&#x201d;" u2="q" k="50" />
+<hkern u1="&#x201d;" u2="&#xc4;" k="38" />
+<hkern u1="&#x201d;" u2="&#xc5;" k="38" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="50" />
+<hkern u1="&#x201d;" u2="&#xc6;" k="38" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="50" />
+<hkern u1="&#x201d;" u2="&#xc0;" k="38" />
+<hkern u1="&#x201d;" u2="&#xc3;" k="38" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201d;" u2="&#x152;" k="40" />
+<hkern u1="&#x201d;" u2="&#x153;" k="50" />
+<hkern u1="&#x201d;" u2="&#xc2;" k="38" />
+<hkern u1="&#x201d;" u2="&#xc1;" k="38" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2018;" u2="&#x161;" k="30" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x2018;" u2="A" k="40" />
+<hkern u1="&#x2018;" u2="J" k="118" />
+<hkern u1="&#x2018;" u2="T" k="-10" />
+<hkern u1="&#x2018;" u2="V" k="-20" />
+<hkern u1="&#x2018;" u2="W" k="-20" />
+<hkern u1="&#x2018;" u2="X" k="-20" />
+<hkern u1="&#x2018;" u2="Y" k="-30" />
+<hkern u1="&#x2018;" u2="a" k="30" />
+<hkern u1="&#x2018;" u2="c" k="30" />
+<hkern u1="&#x2018;" u2="d" k="30" />
+<hkern u1="&#x2018;" u2="e" k="30" />
+<hkern u1="&#x2018;" u2="g" k="40" />
+<hkern u1="&#x2018;" u2="m" k="10" />
+<hkern u1="&#x2018;" u2="n" k="10" />
+<hkern u1="&#x2018;" u2="o" k="30" />
+<hkern u1="&#x2018;" u2="p" k="10" />
+<hkern u1="&#x2018;" u2="q" k="30" />
+<hkern u1="&#x2018;" u2="r" k="10" />
+<hkern u1="&#x2018;" u2="s" k="30" />
+<hkern u1="&#x2018;" u2="u" k="10" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="40" />
+<hkern u1="&#x2018;" u2="&#x153;" k="30" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-30" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="40" />
+<hkern u1="&#x2018;" u2="&#x131;" k="10" />
+<hkern u1="&#x2019;" u2="&#x160;" k="10" />
+<hkern u1="&#x2019;" u2="A" k="38" />
+<hkern u1="&#x2019;" u2="C" k="40" />
+<hkern u1="&#x2019;" u2="G" k="40" />
+<hkern u1="&#x2019;" u2="J" k="120" />
+<hkern u1="&#x2019;" u2="O" k="40" />
+<hkern u1="&#x2019;" u2="Q" k="40" />
+<hkern u1="&#x2019;" u2="S" k="10" />
+<hkern u1="&#x2019;" u2="a" k="50" />
+<hkern u1="&#x2019;" u2="c" k="50" />
+<hkern u1="&#x2019;" u2="d" k="50" />
+<hkern u1="&#x2019;" u2="e" k="50" />
+<hkern u1="&#x2019;" u2="o" k="50" />
+<hkern u1="&#x2019;" u2="q" k="50" />
+<hkern u1="&#x2019;" u2="&#xc4;" k="38" />
+<hkern u1="&#x2019;" u2="&#xc5;" k="38" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="50" />
+<hkern u1="&#x2019;" u2="&#xc6;" k="38" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="50" />
+<hkern u1="&#x2019;" u2="&#xc0;" k="38" />
+<hkern u1="&#x2019;" u2="&#xc3;" k="38" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2019;" u2="&#x152;" k="40" />
+<hkern u1="&#x2019;" u2="&#x153;" k="50" />
+<hkern u1="&#x2019;" u2="&#xc2;" k="38" />
+<hkern u1="&#x2019;" u2="&#xc1;" k="38" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="40" />
+<hkern u1="&#x178;" u2="&#x160;" k="-10" />
+<hkern u1="&#x178;" u2="&#x161;" k="54" />
+<hkern u1="&#x178;" u2="&amp;" k="30" />
+<hkern u1="&#x178;" u2=")" k="-50" />
+<hkern u1="&#x178;" u2="&#x2c;" k="50" />
+<hkern u1="&#x178;" u2="-" k="50" />
+<hkern u1="&#x178;" u2="." k="50" />
+<hkern u1="&#x178;" u2="/" k="45" />
+<hkern u1="&#x178;" u2=":" k="30" />
+<hkern u1="&#x178;" u2=";" k="30" />
+<hkern u1="&#x178;" u2="@" k="30" />
+<hkern u1="&#x178;" u2="A" k="54" />
+<hkern u1="&#x178;" u2="C" k="18" />
+<hkern u1="&#x178;" u2="G" k="18" />
+<hkern u1="&#x178;" u2="J" k="85" />
+<hkern u1="&#x178;" u2="O" k="18" />
+<hkern u1="&#x178;" u2="Q" k="18" />
+<hkern u1="&#x178;" u2="S" k="-10" />
+<hkern u1="&#x178;" u2="T" k="-28" />
+<hkern u1="&#x178;" u2="V" k="-23" />
+<hkern u1="&#x178;" u2="X" k="-13" />
+<hkern u1="&#x178;" u2="]" k="-50" />
+<hkern u1="&#x178;" u2="a" k="70" />
+<hkern u1="&#x178;" u2="c" k="70" />
+<hkern u1="&#x178;" u2="d" k="70" />
+<hkern u1="&#x178;" u2="e" k="70" />
+<hkern u1="&#x178;" u2="f" k="10" />
+<hkern u1="&#x178;" u2="g" k="40" />
+<hkern u1="&#x178;" u2="m" k="40" />
+<hkern u1="&#x178;" u2="n" k="40" />
+<hkern u1="&#x178;" u2="o" k="70" />
+<hkern u1="&#x178;" u2="p" k="40" />
+<hkern u1="&#x178;" u2="q" k="70" />
+<hkern u1="&#x178;" u2="r" k="40" />
+<hkern u1="&#x178;" u2="s" k="54" />
+<hkern u1="&#x178;" u2="t" k="20" />
+<hkern u1="&#x178;" u2="u" k="40" />
+<hkern u1="&#x178;" u2="v" k="20" />
+<hkern u1="&#x178;" u2="w" k="10" />
+<hkern u1="&#x178;" u2="x" k="20" />
+<hkern u1="&#x178;" u2="y" k="20" />
+<hkern u1="&#x178;" u2="z" k="30" />
+<hkern u1="&#x178;" u2="}" k="-50" />
+<hkern u1="&#x178;" u2="&#xc4;" k="54" />
+<hkern u1="&#x178;" u2="&#xc5;" k="54" />
+<hkern u1="&#x178;" u2="&#xd6;" k="18" />
+<hkern u1="&#x178;" u2="&#xe7;" k="70" />
+<hkern u1="&#x178;" u2="&#xae;" k="30" />
+<hkern u1="&#x178;" u2="&#xa9;" k="30" />
+<hkern u1="&#x178;" u2="&#xc6;" k="54" />
+<hkern u1="&#x178;" u2="&#xd8;" k="18" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="30" />
+<hkern u1="&#x178;" u2="&#xe6;" k="70" />
+<hkern u1="&#x178;" u2="&#xab;" k="60" />
+<hkern u1="&#x178;" u2="&#xbb;" k="40" />
+<hkern u1="&#x178;" u2="&#x2026;" k="50" />
+<hkern u1="&#x178;" u2="&#xc0;" k="54" />
+<hkern u1="&#x178;" u2="&#xc3;" k="54" />
+<hkern u1="&#x178;" u2="&#xd5;" k="18" />
+<hkern u1="&#x178;" u2="&#x152;" k="18" />
+<hkern u1="&#x178;" u2="&#x153;" k="70" />
+<hkern u1="&#x178;" u2="&#x2013;" k="50" />
+<hkern u1="&#x178;" u2="&#x2014;" k="50" />
+<hkern u1="&#x178;" u2="&#x2039;" k="60" />
+<hkern u1="&#x178;" u2="&#x203a;" k="40" />
+<hkern u1="&#x178;" u2="&#x201a;" k="50" />
+<hkern u1="&#x178;" u2="&#x201e;" k="50" />
+<hkern u1="&#x178;" u2="&#xc2;" k="54" />
+<hkern u1="&#x178;" u2="&#xc1;" k="54" />
+<hkern u1="&#x178;" u2="&#xd3;" k="18" />
+<hkern u1="&#x178;" u2="&#xd4;" k="18" />
+<hkern u1="&#x178;" u2="&#xd2;" k="18" />
+<hkern u1="&#x178;" u2="&#x131;" k="40" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2039;" u2="T" k="40" />
+<hkern u1="&#x2039;" u2="V" k="20" />
+<hkern u1="&#x2039;" u2="W" k="20" />
+<hkern u1="&#x2039;" u2="Y" k="40" />
+<hkern u1="&#x2039;" u2="f" k="-15" />
+<hkern u1="&#x2039;" u2="&#x178;" k="40" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x203a;" u2="T" k="110" />
+<hkern u1="&#x203a;" u2="V" k="50" />
+<hkern u1="&#x203a;" u2="W" k="40" />
+<hkern u1="&#x203a;" u2="Y" k="50" />
+<hkern u1="&#x203a;" u2="f" k="8" />
+<hkern u1="&#x203a;" u2="v" k="23" />
+<hkern u1="&#x203a;" u2="w" k="15" />
+<hkern u1="&#x203a;" u2="x" k="50" />
+<hkern u1="&#x203a;" u2="y" k="23" />
+<hkern u1="&#x203a;" u2="z" k="35" />
+<hkern u1="&#x203a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201a;" u2="0" k="18" />
+<hkern u1="&#x201a;" u2="1" k="86" />
+<hkern u1="&#x201a;" u2="2" k="-8" />
+<hkern u1="&#x201a;" u2="3" k="-8" />
+<hkern u1="&#x201a;" u2="4" k="13" />
+<hkern u1="&#x201a;" u2="5" k="-15" />
+<hkern u1="&#x201a;" u2="6" k="23" />
+<hkern u1="&#x201a;" u2="7" k="8" />
+<hkern u1="&#x201a;" u2="8" k="15" />
+<hkern u1="&#x201a;" u2="9" k="3" />
+<hkern u1="&#x201a;" u2="A" k="-30" />
+<hkern u1="&#x201a;" u2="C" k="33" />
+<hkern u1="&#x201a;" u2="G" k="33" />
+<hkern u1="&#x201a;" u2="O" k="33" />
+<hkern u1="&#x201a;" u2="Q" k="33" />
+<hkern u1="&#x201a;" u2="T" k="83" />
+<hkern u1="&#x201a;" u2="U" k="10" />
+<hkern u1="&#x201a;" u2="V" k="68" />
+<hkern u1="&#x201a;" u2="W" k="28" />
+<hkern u1="&#x201a;" u2="Y" k="50" />
+<hkern u1="&#x201a;" u2="a" k="20" />
+<hkern u1="&#x201a;" u2="c" k="20" />
+<hkern u1="&#x201a;" u2="d" k="20" />
+<hkern u1="&#x201a;" u2="e" k="20" />
+<hkern u1="&#x201a;" u2="f" k="30" />
+<hkern u1="&#x201a;" u2="j" k="-30" />
+<hkern u1="&#x201a;" u2="m" k="20" />
+<hkern u1="&#x201a;" u2="n" k="20" />
+<hkern u1="&#x201a;" u2="o" k="20" />
+<hkern u1="&#x201a;" u2="p" k="20" />
+<hkern u1="&#x201a;" u2="q" k="20" />
+<hkern u1="&#x201a;" u2="r" k="20" />
+<hkern u1="&#x201a;" u2="t" k="40" />
+<hkern u1="&#x201a;" u2="u" k="20" />
+<hkern u1="&#x201a;" u2="v" k="30" />
+<hkern u1="&#x201a;" u2="w" k="10" />
+<hkern u1="&#x201a;" u2="y" k="30" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="33" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="33" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="33" />
+<hkern u1="&#x201a;" u2="&#x152;" k="33" />
+<hkern u1="&#x201a;" u2="&#x153;" k="20" />
+<hkern u1="&#x201a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="33" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="33" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="33" />
+<hkern u1="&#x201a;" u2="&#xda;" k="10" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201a;" u2="&#x131;" k="20" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201e;" u2="0" k="18" />
+<hkern u1="&#x201e;" u2="1" k="86" />
+<hkern u1="&#x201e;" u2="2" k="-8" />
+<hkern u1="&#x201e;" u2="3" k="-8" />
+<hkern u1="&#x201e;" u2="4" k="13" />
+<hkern u1="&#x201e;" u2="5" k="-15" />
+<hkern u1="&#x201e;" u2="6" k="23" />
+<hkern u1="&#x201e;" u2="7" k="8" />
+<hkern u1="&#x201e;" u2="8" k="15" />
+<hkern u1="&#x201e;" u2="9" k="3" />
+<hkern u1="&#x201e;" u2="A" k="-30" />
+<hkern u1="&#x201e;" u2="C" k="33" />
+<hkern u1="&#x201e;" u2="G" k="33" />
+<hkern u1="&#x201e;" u2="O" k="33" />
+<hkern u1="&#x201e;" u2="Q" k="33" />
+<hkern u1="&#x201e;" u2="T" k="83" />
+<hkern u1="&#x201e;" u2="U" k="10" />
+<hkern u1="&#x201e;" u2="V" k="68" />
+<hkern u1="&#x201e;" u2="W" k="28" />
+<hkern u1="&#x201e;" u2="Y" k="50" />
+<hkern u1="&#x201e;" u2="a" k="20" />
+<hkern u1="&#x201e;" u2="c" k="20" />
+<hkern u1="&#x201e;" u2="d" k="20" />
+<hkern u1="&#x201e;" u2="e" k="20" />
+<hkern u1="&#x201e;" u2="f" k="30" />
+<hkern u1="&#x201e;" u2="j" k="-30" />
+<hkern u1="&#x201e;" u2="m" k="20" />
+<hkern u1="&#x201e;" u2="n" k="20" />
+<hkern u1="&#x201e;" u2="o" k="20" />
+<hkern u1="&#x201e;" u2="p" k="20" />
+<hkern u1="&#x201e;" u2="q" k="20" />
+<hkern u1="&#x201e;" u2="r" k="20" />
+<hkern u1="&#x201e;" u2="t" k="40" />
+<hkern u1="&#x201e;" u2="u" k="20" />
+<hkern u1="&#x201e;" u2="v" k="30" />
+<hkern u1="&#x201e;" u2="w" k="10" />
+<hkern u1="&#x201e;" u2="y" k="30" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="33" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="33" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="33" />
+<hkern u1="&#x201e;" u2="&#x152;" k="33" />
+<hkern u1="&#x201e;" u2="&#x153;" k="20" />
+<hkern u1="&#x201e;" u2="&#x178;" k="50" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="33" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="33" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="33" />
+<hkern u1="&#x201e;" u2="&#xda;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201e;" u2="&#x131;" k="20" />
+<hkern u1="&#xc2;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc2;" u2="&#x161;" k="-11" />
+<hkern u1="&#xc2;" u2="&#xdd;" k="54" />
+<hkern u1="&#xc2;" u2="*" k="75" />
+<hkern u1="&#xc2;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc2;" u2="-" k="-10" />
+<hkern u1="&#xc2;" u2="." k="-30" />
+<hkern u1="&#xc2;" u2="?" k="23" />
+<hkern u1="&#xc2;" u2="@" k="10" />
+<hkern u1="&#xc2;" u2="A" k="-10" />
+<hkern u1="&#xc2;" u2="C" k="20" />
+<hkern u1="&#xc2;" u2="G" k="20" />
+<hkern u1="&#xc2;" u2="J" k="-10" />
+<hkern u1="&#xc2;" u2="O" k="20" />
+<hkern u1="&#xc2;" u2="Q" k="20" />
+<hkern u1="&#xc2;" u2="S" k="-5" />
+<hkern u1="&#xc2;" u2="T" k="59" />
+<hkern u1="&#xc2;" u2="V" k="73" />
+<hkern u1="&#xc2;" u2="W" k="61" />
+<hkern u1="&#xc2;" u2="X" k="-10" />
+<hkern u1="&#xc2;" u2="Y" k="54" />
+<hkern u1="&#xc2;" u2="a" k="5" />
+<hkern u1="&#xc2;" u2="c" k="5" />
+<hkern u1="&#xc2;" u2="d" k="5" />
+<hkern u1="&#xc2;" u2="e" k="5" />
+<hkern u1="&#xc2;" u2="f" k="30" />
+<hkern u1="&#xc2;" u2="g" k="11" />
+<hkern u1="&#xc2;" u2="m" k="5" />
+<hkern u1="&#xc2;" u2="n" k="5" />
+<hkern u1="&#xc2;" u2="o" k="5" />
+<hkern u1="&#xc2;" u2="p" k="5" />
+<hkern u1="&#xc2;" u2="q" k="5" />
+<hkern u1="&#xc2;" u2="r" k="5" />
+<hkern u1="&#xc2;" u2="s" k="-11" />
+<hkern u1="&#xc2;" u2="t" k="5" />
+<hkern u1="&#xc2;" u2="u" k="5" />
+<hkern u1="&#xc2;" u2="v" k="60" />
+<hkern u1="&#xc2;" u2="w" k="40" />
+<hkern u1="&#xc2;" u2="x" k="-13" />
+<hkern u1="&#xc2;" u2="y" k="60" />
+<hkern u1="&#xc2;" u2="z" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc2;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc2;" u2="&#xae;" k="10" />
+<hkern u1="&#xc2;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc2;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc2;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc2;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc2;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc2;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc2;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc2;" u2="&#x152;" k="20" />
+<hkern u1="&#xc2;" u2="&#x153;" k="5" />
+<hkern u1="&#xc2;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc2;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc2;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc2;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc2;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc2;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc2;" u2="&#x178;" k="54" />
+<hkern u1="&#xc2;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc2;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc2;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc2;" u2="&#x131;" k="5" />
+<hkern u1="&#xca;" u2="&#xfe;" k="4" />
+<hkern u1="&#xca;" u2="@" k="20" />
+<hkern u1="&#xca;" u2="C" k="19" />
+<hkern u1="&#xca;" u2="G" k="19" />
+<hkern u1="&#xca;" u2="J" k="-4" />
+<hkern u1="&#xca;" u2="O" k="19" />
+<hkern u1="&#xca;" u2="Q" k="19" />
+<hkern u1="&#xca;" u2="T" k="-8" />
+<hkern u1="&#xca;" u2="V" k="-8" />
+<hkern u1="&#xca;" u2="W" k="4" />
+<hkern u1="&#xca;" u2="a" k="21" />
+<hkern u1="&#xca;" u2="b" k="4" />
+<hkern u1="&#xca;" u2="c" k="21" />
+<hkern u1="&#xca;" u2="d" k="21" />
+<hkern u1="&#xca;" u2="e" k="21" />
+<hkern u1="&#xca;" u2="f" k="10" />
+<hkern u1="&#xca;" u2="g" k="16" />
+<hkern u1="&#xca;" u2="h" k="4" />
+<hkern u1="&#xca;" u2="k" k="4" />
+<hkern u1="&#xca;" u2="l" k="4" />
+<hkern u1="&#xca;" u2="m" k="8" />
+<hkern u1="&#xca;" u2="n" k="8" />
+<hkern u1="&#xca;" u2="o" k="21" />
+<hkern u1="&#xca;" u2="p" k="8" />
+<hkern u1="&#xca;" u2="q" k="21" />
+<hkern u1="&#xca;" u2="r" k="8" />
+<hkern u1="&#xca;" u2="u" k="8" />
+<hkern u1="&#xca;" u2="v" k="20" />
+<hkern u1="&#xca;" u2="y" k="20" />
+<hkern u1="&#xca;" u2="&#xd6;" k="19" />
+<hkern u1="&#xca;" u2="&#xe7;" k="21" />
+<hkern u1="&#xca;" u2="&#xae;" k="20" />
+<hkern u1="&#xca;" u2="&#xa9;" k="20" />
+<hkern u1="&#xca;" u2="&#xd8;" k="19" />
+<hkern u1="&#xca;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xca;" u2="&#xe6;" k="21" />
+<hkern u1="&#xca;" u2="&#xab;" k="15" />
+<hkern u1="&#xca;" u2="&#xd5;" k="19" />
+<hkern u1="&#xca;" u2="&#x152;" k="19" />
+<hkern u1="&#xca;" u2="&#x153;" k="21" />
+<hkern u1="&#xca;" u2="&#x2039;" k="15" />
+<hkern u1="&#xca;" u2="&#xd3;" k="19" />
+<hkern u1="&#xca;" u2="&#xd4;" k="19" />
+<hkern u1="&#xca;" u2="&#xd2;" k="19" />
+<hkern u1="&#xca;" u2="&#x131;" k="8" />
+<hkern u1="&#xc1;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc1;" u2="&#x161;" k="-11" />
+<hkern u1="&#xc1;" u2="&#xdd;" k="54" />
+<hkern u1="&#xc1;" u2="*" k="75" />
+<hkern u1="&#xc1;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc1;" u2="-" k="-10" />
+<hkern u1="&#xc1;" u2="." k="-30" />
+<hkern u1="&#xc1;" u2="?" k="23" />
+<hkern u1="&#xc1;" u2="@" k="10" />
+<hkern u1="&#xc1;" u2="A" k="-10" />
+<hkern u1="&#xc1;" u2="C" k="20" />
+<hkern u1="&#xc1;" u2="G" k="20" />
+<hkern u1="&#xc1;" u2="J" k="-10" />
+<hkern u1="&#xc1;" u2="O" k="20" />
+<hkern u1="&#xc1;" u2="Q" k="20" />
+<hkern u1="&#xc1;" u2="S" k="-5" />
+<hkern u1="&#xc1;" u2="T" k="59" />
+<hkern u1="&#xc1;" u2="V" k="73" />
+<hkern u1="&#xc1;" u2="W" k="61" />
+<hkern u1="&#xc1;" u2="X" k="-10" />
+<hkern u1="&#xc1;" u2="Y" k="54" />
+<hkern u1="&#xc1;" u2="a" k="5" />
+<hkern u1="&#xc1;" u2="c" k="5" />
+<hkern u1="&#xc1;" u2="d" k="5" />
+<hkern u1="&#xc1;" u2="e" k="5" />
+<hkern u1="&#xc1;" u2="f" k="30" />
+<hkern u1="&#xc1;" u2="g" k="11" />
+<hkern u1="&#xc1;" u2="m" k="5" />
+<hkern u1="&#xc1;" u2="n" k="5" />
+<hkern u1="&#xc1;" u2="o" k="5" />
+<hkern u1="&#xc1;" u2="p" k="5" />
+<hkern u1="&#xc1;" u2="q" k="5" />
+<hkern u1="&#xc1;" u2="r" k="5" />
+<hkern u1="&#xc1;" u2="s" k="-11" />
+<hkern u1="&#xc1;" u2="t" k="5" />
+<hkern u1="&#xc1;" u2="u" k="5" />
+<hkern u1="&#xc1;" u2="v" k="60" />
+<hkern u1="&#xc1;" u2="w" k="40" />
+<hkern u1="&#xc1;" u2="x" k="-13" />
+<hkern u1="&#xc1;" u2="y" k="60" />
+<hkern u1="&#xc1;" u2="z" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc1;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc1;" u2="&#xae;" k="10" />
+<hkern u1="&#xc1;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc1;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc1;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc1;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc1;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc1;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc1;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc1;" u2="&#x152;" k="20" />
+<hkern u1="&#xc1;" u2="&#x153;" k="5" />
+<hkern u1="&#xc1;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc1;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc1;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc1;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc1;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc1;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc1;" u2="&#x178;" k="54" />
+<hkern u1="&#xc1;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc1;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc1;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc1;" u2="&#x131;" k="5" />
+<hkern u1="&#xcb;" u2="&#xfe;" k="4" />
+<hkern u1="&#xcb;" u2="@" k="20" />
+<hkern u1="&#xcb;" u2="C" k="19" />
+<hkern u1="&#xcb;" u2="G" k="19" />
+<hkern u1="&#xcb;" u2="J" k="-4" />
+<hkern u1="&#xcb;" u2="O" k="19" />
+<hkern u1="&#xcb;" u2="Q" k="19" />
+<hkern u1="&#xcb;" u2="T" k="-8" />
+<hkern u1="&#xcb;" u2="V" k="-8" />
+<hkern u1="&#xcb;" u2="W" k="4" />
+<hkern u1="&#xcb;" u2="a" k="21" />
+<hkern u1="&#xcb;" u2="b" k="4" />
+<hkern u1="&#xcb;" u2="c" k="21" />
+<hkern u1="&#xcb;" u2="d" k="21" />
+<hkern u1="&#xcb;" u2="e" k="21" />
+<hkern u1="&#xcb;" u2="f" k="10" />
+<hkern u1="&#xcb;" u2="g" k="16" />
+<hkern u1="&#xcb;" u2="h" k="4" />
+<hkern u1="&#xcb;" u2="k" k="4" />
+<hkern u1="&#xcb;" u2="l" k="4" />
+<hkern u1="&#xcb;" u2="m" k="8" />
+<hkern u1="&#xcb;" u2="n" k="8" />
+<hkern u1="&#xcb;" u2="o" k="21" />
+<hkern u1="&#xcb;" u2="p" k="8" />
+<hkern u1="&#xcb;" u2="q" k="21" />
+<hkern u1="&#xcb;" u2="r" k="8" />
+<hkern u1="&#xcb;" u2="u" k="8" />
+<hkern u1="&#xcb;" u2="v" k="20" />
+<hkern u1="&#xcb;" u2="y" k="20" />
+<hkern u1="&#xcb;" u2="&#xd6;" k="19" />
+<hkern u1="&#xcb;" u2="&#xe7;" k="21" />
+<hkern u1="&#xcb;" u2="&#xae;" k="20" />
+<hkern u1="&#xcb;" u2="&#xa9;" k="20" />
+<hkern u1="&#xcb;" u2="&#xd8;" k="19" />
+<hkern u1="&#xcb;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xcb;" u2="&#xe6;" k="21" />
+<hkern u1="&#xcb;" u2="&#xab;" k="15" />
+<hkern u1="&#xcb;" u2="&#xd5;" k="19" />
+<hkern u1="&#xcb;" u2="&#x152;" k="19" />
+<hkern u1="&#xcb;" u2="&#x153;" k="21" />
+<hkern u1="&#xcb;" u2="&#x2039;" k="15" />
+<hkern u1="&#xcb;" u2="&#xd3;" k="19" />
+<hkern u1="&#xcb;" u2="&#xd4;" k="19" />
+<hkern u1="&#xcb;" u2="&#xd2;" k="19" />
+<hkern u1="&#xcb;" u2="&#x131;" k="8" />
+<hkern u1="&#xc8;" u2="&#xfe;" k="4" />
+<hkern u1="&#xc8;" u2="@" k="20" />
+<hkern u1="&#xc8;" u2="C" k="19" />
+<hkern u1="&#xc8;" u2="G" k="19" />
+<hkern u1="&#xc8;" u2="J" k="-4" />
+<hkern u1="&#xc8;" u2="O" k="19" />
+<hkern u1="&#xc8;" u2="Q" k="19" />
+<hkern u1="&#xc8;" u2="T" k="-8" />
+<hkern u1="&#xc8;" u2="V" k="-8" />
+<hkern u1="&#xc8;" u2="W" k="4" />
+<hkern u1="&#xc8;" u2="a" k="21" />
+<hkern u1="&#xc8;" u2="b" k="4" />
+<hkern u1="&#xc8;" u2="c" k="21" />
+<hkern u1="&#xc8;" u2="d" k="21" />
+<hkern u1="&#xc8;" u2="e" k="21" />
+<hkern u1="&#xc8;" u2="f" k="10" />
+<hkern u1="&#xc8;" u2="g" k="16" />
+<hkern u1="&#xc8;" u2="h" k="4" />
+<hkern u1="&#xc8;" u2="k" k="4" />
+<hkern u1="&#xc8;" u2="l" k="4" />
+<hkern u1="&#xc8;" u2="m" k="8" />
+<hkern u1="&#xc8;" u2="n" k="8" />
+<hkern u1="&#xc8;" u2="o" k="21" />
+<hkern u1="&#xc8;" u2="p" k="8" />
+<hkern u1="&#xc8;" u2="q" k="21" />
+<hkern u1="&#xc8;" u2="r" k="8" />
+<hkern u1="&#xc8;" u2="u" k="8" />
+<hkern u1="&#xc8;" u2="v" k="20" />
+<hkern u1="&#xc8;" u2="y" k="20" />
+<hkern u1="&#xc8;" u2="&#xd6;" k="19" />
+<hkern u1="&#xc8;" u2="&#xe7;" k="21" />
+<hkern u1="&#xc8;" u2="&#xae;" k="20" />
+<hkern u1="&#xc8;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc8;" u2="&#xd8;" k="19" />
+<hkern u1="&#xc8;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc8;" u2="&#xe6;" k="21" />
+<hkern u1="&#xc8;" u2="&#xab;" k="15" />
+<hkern u1="&#xc8;" u2="&#xd5;" k="19" />
+<hkern u1="&#xc8;" u2="&#x152;" k="19" />
+<hkern u1="&#xc8;" u2="&#x153;" k="21" />
+<hkern u1="&#xc8;" u2="&#x2039;" k="15" />
+<hkern u1="&#xc8;" u2="&#xd3;" k="19" />
+<hkern u1="&#xc8;" u2="&#xd4;" k="19" />
+<hkern u1="&#xc8;" u2="&#xd2;" k="19" />
+<hkern u1="&#xc8;" u2="&#x131;" k="8" />
+<hkern u1="&#xcd;" u2="/" k="20" />
+<hkern u1="&#xcd;" u2="v" k="10" />
+<hkern u1="&#xcd;" u2="y" k="10" />
+<hkern u1="&#xce;" u2="/" k="20" />
+<hkern u1="&#xce;" u2="v" k="10" />
+<hkern u1="&#xce;" u2="y" k="10" />
+<hkern u1="&#xcf;" u2="/" k="20" />
+<hkern u1="&#xcf;" u2="v" k="10" />
+<hkern u1="&#xcf;" u2="y" k="10" />
+<hkern u1="&#xcc;" u2="/" k="20" />
+<hkern u1="&#xcc;" u2="v" k="10" />
+<hkern u1="&#xcc;" u2="y" k="10" />
+<hkern u1="&#xd3;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd3;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd3;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd3;" u2="." k="33" />
+<hkern u1="&#xd3;" u2="/" k="50" />
+<hkern u1="&#xd3;" u2="?" k="20" />
+<hkern u1="&#xd3;" u2="A" k="20" />
+<hkern u1="&#xd3;" u2="J" k="23" />
+<hkern u1="&#xd3;" u2="T" k="19" />
+<hkern u1="&#xd3;" u2="V" k="20" />
+<hkern u1="&#xd3;" u2="W" k="33" />
+<hkern u1="&#xd3;" u2="X" k="29" />
+<hkern u1="&#xd3;" u2="Y" k="18" />
+<hkern u1="&#xd3;" u2="Z" k="19" />
+<hkern u1="&#xd3;" u2="a" k="1" />
+<hkern u1="&#xd3;" u2="b" k="3" />
+<hkern u1="&#xd3;" u2="c" k="1" />
+<hkern u1="&#xd3;" u2="d" k="1" />
+<hkern u1="&#xd3;" u2="e" k="1" />
+<hkern u1="&#xd3;" u2="h" k="3" />
+<hkern u1="&#xd3;" u2="k" k="3" />
+<hkern u1="&#xd3;" u2="l" k="3" />
+<hkern u1="&#xd3;" u2="m" k="1" />
+<hkern u1="&#xd3;" u2="n" k="1" />
+<hkern u1="&#xd3;" u2="o" k="1" />
+<hkern u1="&#xd3;" u2="p" k="1" />
+<hkern u1="&#xd3;" u2="q" k="1" />
+<hkern u1="&#xd3;" u2="r" k="1" />
+<hkern u1="&#xd3;" u2="u" k="1" />
+<hkern u1="&#xd3;" u2="x" k="10" />
+<hkern u1="&#xd3;" u2="z" k="10" />
+<hkern u1="&#xd3;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd3;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd3;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd3;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd3;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd3;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd3;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd3;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd3;" u2="&#x153;" k="1" />
+<hkern u1="&#xd3;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd3;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd3;" u2="&#x178;" k="18" />
+<hkern u1="&#xd3;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd3;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd3;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd3;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd3;" u2="&#x131;" k="1" />
+<hkern u1="&#xd4;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd4;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd4;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd4;" u2="." k="33" />
+<hkern u1="&#xd4;" u2="/" k="50" />
+<hkern u1="&#xd4;" u2="?" k="20" />
+<hkern u1="&#xd4;" u2="A" k="20" />
+<hkern u1="&#xd4;" u2="J" k="23" />
+<hkern u1="&#xd4;" u2="T" k="19" />
+<hkern u1="&#xd4;" u2="V" k="20" />
+<hkern u1="&#xd4;" u2="W" k="33" />
+<hkern u1="&#xd4;" u2="X" k="29" />
+<hkern u1="&#xd4;" u2="Y" k="18" />
+<hkern u1="&#xd4;" u2="Z" k="19" />
+<hkern u1="&#xd4;" u2="a" k="1" />
+<hkern u1="&#xd4;" u2="b" k="3" />
+<hkern u1="&#xd4;" u2="c" k="1" />
+<hkern u1="&#xd4;" u2="d" k="1" />
+<hkern u1="&#xd4;" u2="e" k="1" />
+<hkern u1="&#xd4;" u2="h" k="3" />
+<hkern u1="&#xd4;" u2="k" k="3" />
+<hkern u1="&#xd4;" u2="l" k="3" />
+<hkern u1="&#xd4;" u2="m" k="1" />
+<hkern u1="&#xd4;" u2="n" k="1" />
+<hkern u1="&#xd4;" u2="o" k="1" />
+<hkern u1="&#xd4;" u2="p" k="1" />
+<hkern u1="&#xd4;" u2="q" k="1" />
+<hkern u1="&#xd4;" u2="r" k="1" />
+<hkern u1="&#xd4;" u2="u" k="1" />
+<hkern u1="&#xd4;" u2="x" k="10" />
+<hkern u1="&#xd4;" u2="z" k="10" />
+<hkern u1="&#xd4;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd4;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd4;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd4;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd4;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd4;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd4;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd4;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd4;" u2="&#x153;" k="1" />
+<hkern u1="&#xd4;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd4;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd4;" u2="&#x178;" k="18" />
+<hkern u1="&#xd4;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd4;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd4;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd4;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd4;" u2="&#x131;" k="1" />
+<hkern u1="&#xd2;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd2;" u2="&#x17d;" k="19" />
+<hkern u1="&#xd2;" u2="&#x2c;" k="33" />
+<hkern u1="&#xd2;" u2="." k="33" />
+<hkern u1="&#xd2;" u2="/" k="50" />
+<hkern u1="&#xd2;" u2="?" k="20" />
+<hkern u1="&#xd2;" u2="A" k="20" />
+<hkern u1="&#xd2;" u2="J" k="23" />
+<hkern u1="&#xd2;" u2="T" k="19" />
+<hkern u1="&#xd2;" u2="V" k="20" />
+<hkern u1="&#xd2;" u2="W" k="33" />
+<hkern u1="&#xd2;" u2="X" k="29" />
+<hkern u1="&#xd2;" u2="Y" k="18" />
+<hkern u1="&#xd2;" u2="Z" k="19" />
+<hkern u1="&#xd2;" u2="a" k="1" />
+<hkern u1="&#xd2;" u2="b" k="3" />
+<hkern u1="&#xd2;" u2="c" k="1" />
+<hkern u1="&#xd2;" u2="d" k="1" />
+<hkern u1="&#xd2;" u2="e" k="1" />
+<hkern u1="&#xd2;" u2="h" k="3" />
+<hkern u1="&#xd2;" u2="k" k="3" />
+<hkern u1="&#xd2;" u2="l" k="3" />
+<hkern u1="&#xd2;" u2="m" k="1" />
+<hkern u1="&#xd2;" u2="n" k="1" />
+<hkern u1="&#xd2;" u2="o" k="1" />
+<hkern u1="&#xd2;" u2="p" k="1" />
+<hkern u1="&#xd2;" u2="q" k="1" />
+<hkern u1="&#xd2;" u2="r" k="1" />
+<hkern u1="&#xd2;" u2="u" k="1" />
+<hkern u1="&#xd2;" u2="x" k="10" />
+<hkern u1="&#xd2;" u2="z" k="10" />
+<hkern u1="&#xd2;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd2;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd2;" u2="&#xe7;" k="1" />
+<hkern u1="&#xd2;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd2;" u2="&#xe6;" k="1" />
+<hkern u1="&#xd2;" u2="&#x2026;" k="33" />
+<hkern u1="&#xd2;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd2;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd2;" u2="&#x153;" k="1" />
+<hkern u1="&#xd2;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd2;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd2;" u2="&#x178;" k="18" />
+<hkern u1="&#xd2;" u2="&#x201a;" k="33" />
+<hkern u1="&#xd2;" u2="&#x201e;" k="33" />
+<hkern u1="&#xd2;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd2;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd2;" u2="&#x131;" k="1" />
+<hkern u1="&#xda;" u2="&#x2c;" k="10" />
+<hkern u1="&#xda;" u2="." k="10" />
+<hkern u1="&#xda;" u2="A" k="15" />
+<hkern u1="&#xda;" u2="J" k="10" />
+<hkern u1="&#xda;" u2="&#xc4;" k="15" />
+<hkern u1="&#xda;" u2="&#xc5;" k="15" />
+<hkern u1="&#xda;" u2="&#xc6;" k="15" />
+<hkern u1="&#xda;" u2="&#x2026;" k="10" />
+<hkern u1="&#xda;" u2="&#xc0;" k="15" />
+<hkern u1="&#xda;" u2="&#xc3;" k="15" />
+<hkern u1="&#xda;" u2="&#x201a;" k="10" />
+<hkern u1="&#xda;" u2="&#x201e;" k="10" />
+<hkern u1="&#xda;" u2="&#xc2;" k="15" />
+<hkern u1="&#xda;" u2="&#xc1;" k="15" />
+<hkern u1="&#xdb;" u2="&#x2c;" k="10" />
+<hkern u1="&#xdb;" u2="." k="10" />
+<hkern u1="&#xdb;" u2="A" k="15" />
+<hkern u1="&#xdb;" u2="J" k="10" />
+<hkern u1="&#xdb;" u2="&#xc4;" k="15" />
+<hkern u1="&#xdb;" u2="&#xc5;" k="15" />
+<hkern u1="&#xdb;" u2="&#xc6;" k="15" />
+<hkern u1="&#xdb;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdb;" u2="&#xc0;" k="15" />
+<hkern u1="&#xdb;" u2="&#xc3;" k="15" />
+<hkern u1="&#xdb;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdb;" u2="&#x201e;" k="10" />
+<hkern u1="&#xdb;" u2="&#xc2;" k="15" />
+<hkern u1="&#xdb;" u2="&#xc1;" k="15" />
+<hkern u1="&#xd9;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd9;" u2="." k="10" />
+<hkern u1="&#xd9;" u2="A" k="15" />
+<hkern u1="&#xd9;" u2="J" k="10" />
+<hkern u1="&#xd9;" u2="&#xc4;" k="15" />
+<hkern u1="&#xd9;" u2="&#xc5;" k="15" />
+<hkern u1="&#xd9;" u2="&#xc6;" k="15" />
+<hkern u1="&#xd9;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd9;" u2="&#xc0;" k="15" />
+<hkern u1="&#xd9;" u2="&#xc3;" k="15" />
+<hkern u1="&#xd9;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd9;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd9;" u2="&#xc2;" k="15" />
+<hkern u1="&#xd9;" u2="&#xc1;" k="15" />
+<hkern u1="&#xa4;" u2="4" k="20" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02 Bold" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02 Bold" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02 Bold" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02 Bold" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.ttf
new file mode 100644
index 0000000000..94934087bb
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.woff
new file mode 100644
index 0000000000..1dfd28f299
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Bold.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.eot
new file mode 100644
index 0000000000..8723e66fa0
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.svg
new file mode 100644
index 0000000000..dbf33a9768
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.svg
@@ -0,0 +1,3799 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Italic]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated by RoboFog]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="554" id="Omnes_ATTW02Italic">
+<font-face font-family="Omnes_ATT W02 Italic" panose-1="2 0 6 6 4 0 0 9 0 4" ascent="700" descent="-300" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="170" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="170" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="281" d="M135 177Q120 177 116 183T114 205L184 647H273L152 197Q147 177 135 177ZM97 -6Q74 -6 62 3T49 28Q49 64 65 80Q78 93 102 93Q125 93 137 84T150 59Q150 23 134 7Q121 -6 97 -6Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="391" d="M310 390Q295 390 290 395T287 419L311 647H405L331 415Q324 390 310 390ZM161 390Q146 390 141 395T138 419L162 647H256L182 415Q175 390 161 390Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="659" d="M166 -1Q166 -3 156 -4T135 1T121 24T127 78L157 175H24Q24 176 25 179Q26 181 26 183T28 191L35 226Q35 228 35 229T36 231Q36 232 37 233H174L230 414H98Q98 415 99 418Q100 420 100 422T102 430L109 465Q109 467 109 468T110 470Q110 471 111 472H247L301 648Q301 649 311 650T332 646T346 623T340 569L310 472H475L529 648Q529 649 539 650T560 646T574 623T568 569L538 472H670Q670 471 669 468Q667 463 666 456L659 421Q658 418 658 416Q658 415 657 414H520L465 233H597Q597 232 596 229Q594 224 593 217L586 182Q585 179 585 177Q585 176 584 175H447L394 -1Q394 -3 384 -4T363 1T349 24T355 78L385 175H220L166 -1ZM404 231L460 416H291L235 231H404Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="567" d="M196 -5Q153 4 120 21T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q88 125 123 95T211 53L265 304Q236 316 210 331T162 366T129 414T116 480Q116 517 131 550T174 607T243 645T333 659H339L357 742H417L398 654Q440 646 470 631T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q495 531 464 558T382 595L330 350Q361 337 391 323T444 288T481 241T495 174Q495 132 480 98T437 40T366 2T269 -12H255L236 -98H176L196 -5ZM273 47Q351 47 388 79T426 168Q426 190 418 207T394 237T358 262T313 284L263 47H273ZM331 600H329Q297 600 271 592T225 568T196 532T185 487Q185 442 212 417T282 371L331 600Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="766" d="M232 347Q204 347 179 356T136 384T107 427T96 484Q96 518 108 549T143 605T195 643T262 657Q290 657 315 648T358 620T387 577T398 520Q398 485 386 454T351 399T299 361T232 347ZM69 -9Q68 -10 64 -9T55 -5T46 4T42 18Q42 31 49 41T78 72L734 662Q735 663 739 662T748 658T757 649T761 635Q761 622 754 612T725 581L69 -9ZM541 -4Q513 -4 488 5T445 33T416 76T405 133Q405 167 417 198T452 254T504 292T571 306Q599 306 624 297T667 269T696 226T707 169Q707 134 695 103T660 48T608 10T541 -4ZM236 398Q258 398 278 407T312 433T334 471T343 517Q343 555 319 580T258 606Q235 606 216 597T182 571T160 533T151 487Q151 449 176 424T236 398ZM545 47Q567 47 587 56T621 82T643 120T652 166Q652 204 628 229T567 255Q544 255 525 246T491 220T469 182T460 136Q460 98 485 73T545 47Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="685" d="M246 -12Q196 -12 157 1T90 38T49 95T35 167Q35 211 49 244T91 303T156 347T243 377Q229 399 220 426T211 485Q211 521 224 552T261 608T317 645T389 659Q420 659 447 649T493 621T523 578T534 521Q534 383 342 346L485 161Q523 208 546 263T578 391H639Q622 230 520 117L524 111Q551 76 573 60T631 43Q632 43 632 36T630 20T618 4T590 -4Q553 -4 531 14T482 67L476 74Q431 35 376 12T246 -12ZM309 390Q349 398 380 407T431 431T461 467T472 520Q472 558 447 580T383 603Q358 603 338 593T305 568T284 532T276 490Q276 460 283 438T309 390ZM253 46Q316 46 359 64T443 117L274 333Q191 315 146 276T100 169Q100 145 109 123T136 83T184 56T253 46Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="243" d="M161 390Q146 390 141 395T138 419L162 647H257L182 415Q175 390 161 390Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="385" d="M248 -200Q226 -200 201 -188T151 -150Q110 -109 83 -41T56 120Q56 179 67 239T101 355T156 462T230 555Q275 600 322 625T414 650Q431 650 438 643T447 625T445 607T435 595Q395 593 356 575T273 513Q235 475 208 429T161 331T133 225T124 120Q124 49 143 -9T203 -107Q234 -138 275 -150Q277 -151 279 -159T281 -176T272 -192T248 -200Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="385" d="M-32 -199Q-49 -199 -56 -192T-65 -174T-63 -156T-53 -144Q-13 -142 26 -124T109 -62Q146 -25 174 22T221 120T249 226T258 331Q258 402 239 460T179 558Q148 589 107 601Q105 602 103 610T101 627T110 643T134 651Q156 651 181 639T231 601Q272 560 299 492T326 331Q326 272 315 212T281 96T226 -11T152 -104Q107 -149 60 -174T-32 -199Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="411" d="M154 352Q150 356 150 363T156 381L217 472L115 511Q97 518 97 529Q97 564 130 555L236 526L240 634Q241 650 246 654T263 659Q274 659 279 655T286 634L290 526L396 555Q429 564 429 529Q429 518 411 511L309 472L370 381Q377 370 377 363T372 352Q362 343 353 342T332 354L263 440L194 354Q183 340 174 341T154 352Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="573" d="M244 34Q233 34 225 40T220 65L257 239H98Q69 239 69 255Q69 263 72 274T81 290Q88 297 105 297H269L304 460Q308 477 315 484T333 491H341Q352 491 360 485T365 460L330 297H489Q518 297 518 281Q518 273 515 262T506 246Q499 239 482 239H318L281 65Q277 48 270 41T252 34H244Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="243" d="M-11 -132Q-13 -135 -19 -134T-30 -129T-39 -121T-41 -112L69 98Q70 99 79 99T100 96T121 85T131 65Q131 54 127 44T112 20L-11 -132Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="400" d="M66 272H337V212H66V272Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="243" d="M78 -6Q55 -6 43 3T30 28Q30 64 46 80Q59 93 83 93Q106 93 118 84T131 59Q131 23 115 7Q102 -6 78 -6Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="357" d="M-83 -193L354 647H428L-9 -193H-83Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="663" d="M318 -12Q260 -12 216 5T141 54T96 129T81 223Q81 259 88 302T106 392Q119 445 139 493T192 578T271 636T382 658Q440 658 484 641T559 592T604 517T619 423Q619 387 612 344T594 254Q581 201 561 153T508 68T429 10T318 -12ZM318 46Q369 46 405 64T465 113T504 182T529 262Q540 306 545 348T551 423Q551 462 539 495T506 551T453 587T382 600Q331 600 295 582T235 533T196 464T171 384Q160 340 155 298T149 223Q149 184 161 151T194 95T247 59T318 46Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="381" d="M248 534Q233 519 213 512T175 504Q153 504 140 508T118 521Q106 533 106 552Q106 563 110 571T120 586T131 595T136 598Q144 587 158 580T193 573Q225 573 249 596T282 651H310Q325 651 331 642T334 616L203 0H136L248 534Z" />
+<glyph unicode="2" glyph-name="two" d="M136 162Q98 124 92 62H468L463 37Q461 31 461 25T458 13Q456 7 455 0H60Q46 0 35 14T24 46Q24 94 40 136T89 210Q127 248 177 271T284 315Q333 333 369 348T430 383T466 426T478 485Q478 533 440 565T333 597Q266 597 226 564T162 480Q161 478 155 479T142 484T128 496T122 519Q122 533 132 553T160 591Q187 618 231 638T337 659Q434 659 491 612T548 485Q548 434 531 399T482 337T406 291T306 251Q245 229 205 210T136 162Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="566" d="M242 -12Q177 -12 130 6T56 51Q33 74 24 96T15 132Q15 149 22 159T37 175T53 180T61 180Q73 116 120 82T246 47Q291 47 323 59T377 92T408 139T418 195Q418 249 380 281T266 313H184Q184 314 185 317Q186 319 186 321T188 329L195 364Q195 366 195 367T196 369Q196 370 197 371H279Q336 371 372 381T429 410T457 451T465 498Q465 521 455 540T425 572T381 593T327 601Q269 601 225 575T152 507Q151 506 146 507T134 513T123 524T118 540Q118 564 145 591Q174 621 220 640T330 659Q372 659 410 650T476 620T520 570T537 500Q537 471 528 444T501 394T454 356T387 334Q440 317 465 280T490 196Q490 161 477 124T435 57T358 8T242 -12Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="605" d="M369 184H66Q64 184 61 188T53 199T46 214T43 232Q43 249 50 262T77 295L404 612Q427 634 442 642T477 651Q509 651 519 638T524 602L448 246H568Q568 245 567 242Q565 237 564 229L556 191Q555 188 555 185V184H435L396 0H331L369 184ZM384 245L456 585L110 245H384Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="562" d="M171 378Q225 399 285 399Q336 399 376 386T445 349T489 292T504 219Q504 168 487 126T437 53T357 5T251 -12Q183 -12 135 7T60 55Q28 88 28 124Q28 136 34 144T49 157T64 163T71 163Q87 112 132 80T253 48Q294 48 327 60T383 94T420 147T433 215Q433 275 396 307T280 339Q229 339 195 329T134 304Q111 309 104 322T102 355L154 606Q158 625 169 636T201 647H563Q563 644 562 640Q561 637 560 634T558 625Q556 618 556 612T553 599Q551 592 550 585H215L171 378Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="609" d="M306 -12Q256 -12 215 3T144 47T98 119T81 219Q81 253 88 302T110 403T150 504T209 587Q226 604 244 617T284 640T333 654T396 659Q452 659 490 642T551 603Q567 586 573 571T580 545Q580 533 574 525T561 511T548 505T541 505Q518 554 478 577T389 601Q299 601 244 535T162 345Q191 368 233 386T337 404Q383 404 421 392T487 357T530 301T546 224Q546 179 529 137T480 61T404 8T306 -12ZM307 48Q348 48 380 61T435 98T470 153T482 220Q482 278 444 313T329 348Q267 348 226 328T150 278Q147 261 146 246T145 217Q145 136 186 92T307 48Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="552" d="M142 -4Q142 -4 130 -2T109 8T100 35T125 84L507 585H115Q115 586 116 590Q117 592 117 596T120 607Q121 619 125 633Q127 640 128 647H540Q541 647 546 643T557 632T567 615T572 593Q572 582 568 571T549 540L142 -4Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="603" d="M287 -12Q233 -12 189 0T114 34T65 89T47 161Q47 225 93 274T219 341Q177 358 155 389T132 466Q132 505 149 540T196 602T270 643T365 659Q410 659 449 648T516 615T561 565T578 499Q578 441 540 393T425 326Q476 305 503 269T530 186Q530 145 513 109T465 46T388 4T287 -12ZM352 362Q428 362 469 398T510 493Q510 544 468 573T356 602Q283 602 242 565T200 469Q200 418 241 390T352 362ZM289 45Q330 45 362 56T417 85T450 130T462 186Q462 238 415 270T284 303Q249 303 218 293T164 265T128 222T115 165Q115 111 162 78T289 45Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="609" d="M250 -12Q194 -12 156 5T95 44Q79 60 73 75T66 102Q66 114 72 122T85 136T98 142T105 142Q128 93 168 70T257 46Q345 46 399 109T482 292Q452 269 409 251T308 233Q211 233 155 280T99 418Q99 466 116 509T165 586T241 639T340 659Q390 659 431 644T502 600T548 528T565 428Q565 393 558 344T536 244T496 143T437 60Q420 43 402 30T362 7T313 -7T250 -12ZM316 289Q375 289 417 309T494 359Q501 397 501 430Q501 511 460 555T339 599Q298 599 265 586T210 548T175 492T163 422Q163 361 199 325T316 289Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="273" d="M165 334Q142 334 130 343T117 368Q117 404 133 420Q146 433 170 433Q193 433 205 424T218 399Q218 363 202 347Q189 334 165 334ZM93 -6Q70 -6 58 3T45 28Q45 64 61 80Q74 93 98 93Q121 93 133 84T146 59Q146 23 130 7Q117 -6 93 -6Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="273" d="M165 334Q142 334 130 343T117 368Q117 404 133 420Q146 433 170 433Q193 433 205 424T218 399Q218 363 202 347Q189 334 165 334ZM4 -132Q2 -135 -4 -134T-15 -129T-24 -121T-26 -112L84 98Q85 99 94 99T115 96T136 85T146 65Q146 54 142 44T127 20L4 -132Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="502" d="M93 222Q69 237 69 262Q69 279 77 292T111 318L460 486Q462 487 471 478T481 454Q482 443 473 432T438 407L136 265L367 131Q392 117 400 104T406 80Q404 64 395 56T382 50L93 222Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="573" d="M133 346Q102 346 102 362Q102 370 106 382T115 400Q122 407 140 407H500Q531 407 531 391Q531 383 527 371T518 353Q511 346 493 346H133ZM84 119Q53 119 53 135Q53 143 57 155T66 173Q73 180 91 180H451Q482 180 482 164Q482 156 478 144T469 126Q462 119 444 119H84Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="502" d="M55 50Q53 49 44 58T34 82Q33 93 42 104T77 129L379 271L148 405Q123 419 115 432T109 456Q111 472 120 480T133 486L422 314Q446 299 446 274Q446 257 438 244T404 218L55 50Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="497" d="M175 219Q181 246 190 265T214 300T251 328T304 351Q335 362 360 373T402 400T428 435T438 483Q438 507 429 528T403 564T361 589T304 599Q247 599 209 565T150 477Q150 475 143 476T128 482T114 493T107 513Q107 533 118 554T149 595Q177 623 215 641T308 659Q353 659 389 646T451 608T491 552T505 483Q505 441 490 411T452 360T400 326T347 305Q318 296 299 287T268 267T249 243T238 210L230 177H166L175 219ZM182 -6Q159 -6 147 3T134 28Q134 64 150 80Q163 93 187 93Q210 93 222 84T235 59Q235 23 219 7Q206 -6 182 -6Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="780" d="M404 473Q439 473 464 458T501 416L512 466H575L537 286Q529 251 540 232T585 212Q636 212 666 259T696 388Q696 437 677 478T625 549T544 595T441 612Q378 612 322 587T224 517T158 413T134 285Q134 230 153 185T208 106T292 55T400 36Q443 36 484 48T554 79Q562 84 569 87T588 91Q595 91 600 87T610 76T615 65T615 58Q582 31 524 10T395 -12Q324 -12 267 9T169 68T106 160T83 281Q83 360 111 429T188 549T303 629T446 659Q514 659 569 640T664 586T724 501T746 390Q746 283 700 223T577 163Q526 163 502 182T473 230Q435 169 358 169Q309 169 276 200T243 289Q243 323 255 356T288 415T339 457T404 473ZM375 221Q413 221 442 248T480 315L489 358Q481 387 461 404T409 421Q385 421 366 410T334 381T313 341T306 296Q306 261 326 241T375 221Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="637" d="M172 606Q178 647 219 647H403Q499 647 548 610T597 501Q597 444 567 399T460 330Q517 314 545 279T573 199Q573 159 560 123T518 60T440 16T320 0H43L172 606ZM325 61Q421 61 462 95T503 199Q503 245 468 271T362 298H175L124 61H325ZM367 358Q442 358 484 395T527 492Q524 586 396 586H236L187 358H367Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="684" d="M370 -12Q304 -12 250 7T158 63T99 150T78 267Q78 346 105 417T181 542T295 627T438 659Q510 659 556 638T631 588Q653 566 663 544T673 508Q673 495 667 487T653 474T638 468T628 469Q580 600 435 600Q374 600 322 574T232 504T172 402T150 277Q150 224 166 182T211 110T280 64T369 48Q439 48 491 76T576 153Q577 154 582 153T594 147T605 135T610 117Q610 93 581 64Q566 49 545 36T496 12T436 -5T370 -12Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="694" d="M172 606Q178 647 219 647H329Q480 647 564 577T649 378Q649 300 624 231T549 111T423 30T246 0H43L172 606ZM256 60Q339 60 399 85T499 152T558 250T577 368Q577 471 511 529T324 587H236L124 60H256Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="567" d="M172 606Q178 647 219 647H607Q606 646 606 643Q605 641 605 638T604 630Q602 622 600 610T596 592Q595 589 595 586Q595 585 594 585H236L184 341H461Q460 340 460 337Q459 335 459 332T458 325Q456 317 454 305T450 288Q449 285 449 283Q449 282 448 281H171L110 0H43L172 606Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="740" d="M368 -12Q303 -12 250 6T159 60T99 147T78 264Q78 342 105 413T180 539T296 626T443 659Q517 659 568 636T649 584Q666 567 675 548T685 515Q685 499 678 490T663 477T647 473T639 473Q620 527 568 563T442 599Q378 599 325 573T232 501T172 397T150 271Q150 217 166 175T210 105T279 62T369 47Q468 47 529 108Q543 122 554 136T574 168T591 210T606 269H392Q392 270 393 273Q394 275 394 277T396 285Q398 293 400 305T404 323Q404 325 404 326T405 328V330H633Q656 330 665 318T670 285L609 -1Q609 -4 595 -4Q576 -4 566 11T563 65L565 74Q541 39 491 14T368 -12Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="701" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L187 355H564L620 620Q624 636 631 643T654 651H663Q678 651 684 642T687 616L556 0H489L551 293H174L111 0H44L175 620Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="471" d="M175 -12Q130 -12 99 2T47 36Q33 50 28 60T23 80Q23 92 29 100T42 114T55 120T62 121Q77 88 107 68T176 47Q232 47 262 81T310 194L407 647H474L376 185Q354 84 306 36T175 -12Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="580" d="M467 -5Q454 -5 442 0T417 17T389 47T354 93L181 328L110 0H43L175 620Q179 637 186 644T209 651H218Q250 651 242 616L185 346L570 659Q571 660 581 656T597 641T599 615T565 575L256 328L417 116Q434 93 446 79T470 56T491 44T515 39Q516 39 516 32T512 17T498 2T467 -5Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="534" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L124 62H432Q463 62 463 45Q463 37 459 25T450 7Q444 0 424 0H43L175 620Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="775" d="M172 605Q177 626 190 638T227 651H243Q269 651 277 639T292 600L381 223L633 600Q653 630 666 640T702 651H720Q743 651 752 637T758 603L629 0H565L687 573L436 201Q421 179 407 170T373 161Q345 161 335 173T319 208L230 573L107 0H43L172 605Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="709" d="M172 605Q177 626 189 638T220 651H237Q254 651 262 642T280 615L522 101L639 647H702L572 37Q568 19 557 8T528 -4H521Q506 -4 497 5T479 32L227 566L106 0H43L172 605Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="587" d="M172 606Q178 647 219 647H367Q471 647 526 604T582 480Q582 370 516 311T324 252H165L110 0H43L172 606ZM337 312Q419 312 465 353T512 471Q512 586 363 586H236L178 312H337Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="778" d="M362 -11Q294 -11 242 10T153 68T97 157T78 269Q78 355 108 426T190 550T306 630T440 659Q507 659 560 638T649 580T705 491T724 379Q724 289 691 216T604 91L610 84Q629 60 644 48T676 33Q677 33 677 26T675 11T665 -5T643 -12Q620 -12 604 0T563 45L558 52Q513 21 464 5T362 -11ZM369 48Q408 48 446 61T519 99L406 246L454 285L564 140Q604 183 628 242T652 374Q652 420 638 461T595 533T526 582T433 600Q380 600 329 576T239 509T175 406T150 274Q150 227 164 186T207 115T275 66T369 48Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="601" d="M337 272L426 118Q448 79 467 61T521 43Q522 43 522 36T518 19T504 3T473 -5Q460 -5 448 -1T423 13T396 43T365 92L264 272H169L110 0H43L172 606Q178 647 219 647H377Q482 647 537 607T592 488Q592 385 527 329T348 272H337ZM350 330Q431 330 476 369T521 481Q521 533 483 560T373 587H236L181 330H350Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="567" d="M269 -12Q201 -12 149 9T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q93 113 143 80T273 47Q351 47 388 79T426 168Q426 193 415 212T385 245T340 272T284 296Q253 309 223 323T169 359T131 409T116 480Q116 517 131 550T174 607T243 645T333 659Q401 659 448 640T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q492 541 448 570T330 600Q298 600 271 592T225 568T196 532T185 487Q185 459 195 439T225 404T272 376T331 350Q361 337 390 323T443 289T480 242T495 174Q495 132 480 98T437 40T366 2T269 -12Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="618" d="M350 585H151Q120 585 120 602Q120 610 124 622T133 640Q139 647 159 647H628Q659 647 659 630Q659 622 655 610T646 592Q640 585 620 585H418L293 0H226L350 585Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="634" d="M118 631Q117 634 127 641T150 650T175 645T192 614L288 72L616 651Q616 651 628 651T650 644T662 618T644 566L314 0H239L118 631Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="938" d="M157 637Q157 641 168 646T192 652T216 642T227 609L235 61L478 529Q487 547 498 554T532 562Q561 562 571 550T583 518L624 63L885 649Q885 649 897 650T919 645T933 622T920 567L660 0H570L526 495L264 0H177L157 637Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="606" d="M491 -12Q457 -12 435 15T389 96L308 277L35 -12Q35 -12 24 -9T4 3T-4 27T22 68L278 328L141 647H213L329 366L606 659Q606 659 617 656T637 644T645 620T619 579L359 315L447 126Q459 100 468 84T488 59T509 47T537 44Q538 44 538 35T535 16T521 -3T491 -12Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="583" d="M265 272L110 647H181L308 331L581 653Q581 653 593 651T614 639T622 612T593 564L331 265L275 0H207L265 272Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="598" d="M13 48L15 61L521 585H170Q149 585 141 592T132 610Q132 621 138 634T147 647H622L612 599L609 586L103 62H475Q496 62 504 55T513 37Q513 26 507 13T498 0H2L13 48Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="357" d="M184 647H373Q402 647 402 631Q402 623 399 612T390 595Q383 588 366 588H233L79 -138H207Q236 -138 236 -154Q236 -162 233 -173T224 -190Q217 -197 200 -197H4L184 647Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="357" d="M107 647H173L252 -193H186L107 647Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="357" d="M167 -197H-22Q-51 -197 -51 -181Q-51 -173 -48 -162T-39 -145Q-32 -138 -15 -138H118L272 588H144Q115 588 115 604Q115 612 118 623T127 640Q134 647 151 647H347L167 -197Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="534" d="M206 446Q196 446 192 451T187 463Q187 480 204 500T254 549Q270 563 286 575T316 596T337 610T347 616Q348 616 354 611T369 596T388 576T409 552Q431 523 439 505T448 475Q448 461 441 454T424 446Q411 446 399 460T368 507L339 558L288 505Q262 478 241 462T206 446Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="499" d="M5 -62Q-26 -62 -26 -45Q-26 -37 -23 -25T-14 -8Q-6 0 12 0H381Q412 0 412 -17Q412 -25 409 -37T400 -54Q392 -62 374 -62H5Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="534" d="M405 551Q403 549 387 553T349 568T300 593T252 626Q235 640 229 651T222 675Q222 688 232 696T254 704Q265 704 276 698T299 683Q321 665 341 643T376 601T399 567T405 551Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="600" d="M292 -12Q229 -12 185 18T117 90L111 62Q103 25 91 11T59 -4Q49 -4 42 -1T34 4L177 677H242L182 399Q215 441 257 464T356 488Q394 488 429 475T492 435T535 369T552 277Q552 222 533 170T480 78T398 13T292 -12ZM296 45Q342 45 377 65T437 118T473 192T486 277Q486 314 475 342T444 390T400 420T348 431Q290 431 241 398T164 311L145 221Q139 188 146 156T173 100T224 60T296 45Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="552" d="M274 -12Q227 -12 186 3T114 46T66 116T48 209Q48 263 69 313T126 402T211 464T318 487Q378 487 418 470T483 427Q504 406 513 384T523 345Q523 329 516 321T499 309T483 305T475 308Q465 361 426 395T320 429Q274 429 237 411T172 362T130 292T115 213Q115 176 126 146T160 93T212 59T280 47Q336 47 373 72T435 137Q436 138 441 137T454 133T466 123T472 106Q472 83 441 52Q416 27 373 8T274 -12Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="603" d="M489 -8Q451 -8 434 14T422 78Q389 34 347 11T248 -12Q210 -12 175 1T112 41T69 107T52 199Q52 254 71 306T124 398T206 463T312 488Q373 488 417 462T488 391L549 677H614L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM256 45Q292 45 324 58T381 94T425 149T451 218L470 305Q450 364 410 397T308 431Q262 431 227 411T167 358T131 284T118 199Q118 162 129 134T160 86T204 56T256 45Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="364" d="M166 418H70Q70 419 71 422Q71 424 71 426T73 434L81 469Q81 471 81 472T82 474V476H178L191 534Q208 612 247 650T346 689Q377 689 397 682T430 661Q444 647 444 629Q444 620 441 614T433 603T424 596T419 595Q409 609 389 620T343 631Q309 631 287 607T252 525L241 476H365Q364 475 364 472Q363 470 363 467T362 460L354 425Q353 422 353 420V418H230L142 0H77L166 418Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="603" d="M229 -200Q165 -200 121 -182T51 -138Q33 -120 25 -104T17 -74Q17 -58 24 -48T40 -33T56 -27T64 -27Q78 -79 118 -110T235 -141Q301 -141 345 -103T407 14L423 88Q391 46 348 24T249 1Q211 1 176 14T112 54T68 120T51 212Q51 263 71 312T128 401T213 464T319 488Q379 488 423 461T489 393L507 476H571L471 8Q450 -91 390 -145T229 -200ZM257 58Q291 58 322 70T380 103T424 153T451 219L471 308Q454 365 413 398T315 431Q271 431 235 412T172 361T132 291T117 212Q117 175 128 147T159 99T204 69T257 58Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L171 650Q175 667 182 674T201 681H209Q222 681 231 675T236 650L182 397Q208 440 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM207 596Q163 596 163 629Q163 649 174 664T209 680Q253 680 253 647Q253 627 242 612T207 596Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="243" d="M-51 -203Q-82 -203 -102 -195T-134 -176Q-148 -162 -148 -148Q-148 -139 -144 -131T-135 -118T-126 -109T-121 -107Q-110 -123 -91 -134T-49 -145Q-20 -145 -2 -128T26 -62L140 476H205L88 -74Q74 -142 37 -172T-51 -203ZM207 596Q163 596 163 629Q163 649 174 664T209 680Q253 680 253 647Q253 627 242 612T207 596Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="475" d="M364 -8Q349 -8 338 -4T313 11T285 44T246 100L150 245L98 0H33L177 677H242L153 260L436 488Q437 489 447 485T462 470T464 444T431 406L224 242L309 117Q325 92 337 78T360 57T383 48T412 45Q413 45 413 37T409 19T395 1T364 -8Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L183 677H248L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="889" d="M771 -8Q741 -8 724 11T707 63Q707 84 714 118T733 190Q744 228 751 261T758 315Q758 367 730 397T656 427Q630 427 608 421T567 402T531 369T496 322L428 0H363L421 274Q428 308 425 336T408 384T374 415T326 427Q274 427 238 403T166 322L98 0H33L135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L184 408Q213 448 253 468T335 488Q392 488 430 460T483 384Q512 429 557 458T665 488Q701 488 730 476T781 442T813 391T825 327Q825 301 816 262T796 181Q785 144 779 116T772 73Q772 53 782 46T814 36Q815 36 815 29T811 14T798 -1T771 -8Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L182 398Q209 441 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="600" d="M135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L183 399Q216 441 257 464T356 488Q394 488 429 475T492 435T535 369T552 277Q552 222 533 170T480 78T398 13T292 -12Q259 -12 232 -3T182 20T143 52T117 90L57 -193H-8L135 477ZM296 45Q342 45 377 65T437 118T473 192T486 277Q486 314 475 342T444 390T400 420T348 431Q290 431 241 398T164 311L145 221Q139 188 146 156T173 100T224 60T296 45Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="603" d="M421 77Q388 35 347 12T248 -12Q210 -12 175 1T112 41T69 107T52 199Q52 254 71 306T124 398T206 463T312 488Q373 488 418 462T489 396L493 413Q501 450 513 465T545 480Q555 480 562 477T570 472L428 -193H363L421 77ZM256 45Q292 45 324 58T381 94T425 149T451 218L470 305Q450 364 414 397T308 431Q262 431 227 411T167 358T131 284T118 199Q118 162 129 134T160 86T204 56T256 45Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="372" d="M135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L181 392Q206 440 238 464T315 488Q343 488 363 476T384 443Q384 431 380 423T372 409T363 401T358 400Q348 411 334 418T296 426Q267 426 243 409T200 360T165 287T139 194L98 0H33L135 477Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="481" d="M222 -12Q163 -12 117 6T45 50Q34 61 28 72T21 95Q21 106 26 113T38 126T50 132T57 133Q80 95 122 69T226 43Q282 43 312 65T342 124Q342 157 312 174T228 209Q202 218 177 228T131 254T97 292T84 347Q84 378 97 403T134 448T190 477T262 488Q316 488 356 472T420 432Q444 408 444 384Q444 373 439 366T426 354T413 349T406 350Q387 387 347 410T257 433Q202 433 175 409T147 355Q147 320 179 302T265 264Q290 255 315 245T360 219T392 183T405 132Q405 66 357 27T222 -12Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="375" d="M208 -12Q141 -12 108 31T93 161L147 418H79Q79 419 80 422Q80 424 80 426T82 434L90 469Q90 471 90 472T91 474V476H159L169 523Q177 560 189 575T222 590Q232 590 239 587T247 582L224 476H372Q371 475 371 472Q370 470 370 467T369 460L361 425Q360 422 360 420V418H212L157 161Q143 97 160 71T218 44Q240 44 258 53T284 73Q286 75 292 66T299 43Q299 26 285 12Q274 1 255 -5T208 -12Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="497" d="M117 330Q113 359 107 378T92 408T70 423T40 428Q40 428 40 436T42 454T55 472T85 480Q117 480 138 459Q156 441 165 408T181 324L211 66L455 479Q456 480 467 480T487 472T498 449T481 401L237 0H160L117 330Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="729" d="M127 330Q127 360 123 379T110 410T88 425T58 431Q57 431 57 438T61 455T77 472T109 480Q138 480 157 461Q192 426 190 324L186 69L337 371Q349 395 362 401T396 408Q442 408 446 360L472 68L664 479Q664 479 675 479T696 472T709 449T697 401L502 0H421L384 329L214 0H126L127 330Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="487" d="M30 -9Q29 -10 19 -7T1 6T-5 31T24 73L190 236L65 476H141L237 272L446 485Q447 486 458 483T476 471T482 446T452 403L270 224L319 131Q333 104 344 89T366 65T391 53T424 49Q425 49 425 41T423 23T409 5T380 -4Q361 -4 346 1T317 21T287 60T252 126L223 187L30 -9H30Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="498" d="M9 -204Q-15 -204 -34 -199T-65 -181Q-77 -169 -77 -153Q-77 -145 -73 -139T-65 -127T-56 -120T-50 -119Q-40 -130 -23 -137T5 -145Q20 -145 35 -139T68 -117T109 -72T162 1L178 24Q161 26 153 37T143 67L115 330Q110 382 93 405T40 428Q40 428 40 436T42 454T57 472T90 480Q116 480 134 462Q170 426 179 324L203 59L457 476H528L225 -12Q189 -69 161 -106T108 -165T60 -195T9 -204Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="507" d="M10 49L406 420H138Q119 420 112 426T104 443Q104 453 109 464T117 476H490V427L94 56H371Q390 56 397 50T405 33Q405 23 400 12T392 0H10V49Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="444" d="M289 -200Q245 -200 202 -157Q155 -110 138 -35T132 129L74 171Q52 187 47 195T41 218Q41 229 44 245T60 274Q65 279 72 283T100 295L172 323Q193 416 224 473T296 571Q341 616 381 633T457 650Q470 650 479 643T489 621Q489 611 484 603T476 595Q441 593 402 574T335 527Q294 486 272 428T229 283L99 232L203 157Q171 -37 247 -112Q260 -125 276 -134T316 -150Q318 -151 320 -156T322 -168Q322 -182 314 -191T289 -200Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="277" d="M43 -197Q29 -197 23 -188T21 -162L187 620Q191 636 198 643T223 651H229Q243 651 249 642T251 616L85 -166Q81 -183 74 -190T49 -197H43Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="444" d="M-19 -200Q-32 -200 -41 -193T-51 -171Q-51 -161 -46 -153T-38 -145Q-3 -143 36 -124T103 -77Q144 -36 166 22T209 167L339 218L235 293Q266 486 191 562Q178 575 162 584T122 600Q120 601 118 606T116 618Q116 632 124 641T149 650Q193 650 236 607Q283 560 300 485T306 321L364 279Q386 263 391 255T397 232Q397 221 394 205T378 176Q373 171 366 167T338 155L266 127Q245 34 214 -23T142 -121Q97 -166 57 -183T-19 -200Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="534" d="M333 161Q309 161 271 182T194 241Q188 228 182 215T169 192T155 175T140 168Q128 168 123 174T117 188Q117 205 124 222T142 253T167 275T197 284Q221 284 259 263T336 204Q342 217 348 230T361 253T375 270T390 277Q402 277 407 271T413 257Q413 240 406 223T388 192T363 170T333 161Z" />
+<glyph unicode="&#xA0;" glyph-name="uni00A0" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="281" d="M167 450Q172 470 184 470Q199 470 203 464T205 442L135 0H46L167 450ZM217 554Q194 554 182 563T169 588Q169 624 185 640Q198 653 222 653Q245 653 257 644T270 619Q270 583 254 567Q241 554 217 554Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="573" d="M241 -98Q230 -98 221 -92T216 -67L238 39Q203 48 173 66T121 111T87 174T75 253Q75 307 95 357T152 445T237 507T343 531L363 626Q367 643 373 650T391 657H397Q408 657 417 651T422 626L401 526Q438 518 464 504T510 471Q531 450 540 428T550 389Q550 373 543 365T526 353T510 349T502 352Q494 396 465 427T387 470L306 90H307Q363 90 400 116T462 181Q463 182 468 181T481 177T493 167T499 150Q499 127 468 96Q443 71 400 52T301 32H296L275 -67Q271 -84 265 -91T247 -98H241ZM142 257Q142 197 172 155T254 97L333 473Q290 470 255 451T195 401T156 334T142 257Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="578" d="M41 0Q10 0 10 17Q10 25 14 37T23 55Q29 62 49 62H102L160 263H81Q51 263 51 280Q51 288 54 299T63 315Q71 323 88 323H177L209 436Q226 496 246 538T292 608T350 647T422 659Q467 659 497 646T548 613Q565 596 572 579T580 547Q580 530 573 521T558 508T542 503T534 504Q512 601 418 601Q394 601 374 592T336 563T304 510T274 427L244 323H409Q439 323 439 306Q439 298 436 287T427 271Q419 263 402 263H227L169 62H479Q500 62 508 55T517 37Q517 26 511 13T502 0H41Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="660" d="M116 376Q87 376 87 392Q87 400 90 411T99 427Q106 434 123 434H194Q210 475 231 512T282 579Q322 619 369 639T475 659Q527 659 563 643T625 602Q642 585 651 564T660 522Q660 507 653 498T638 485T622 479T614 480Q599 544 560 573T467 602Q425 602 391 587T328 543Q306 521 290 493T260 434H476Q505 434 505 418Q505 410 502 399T493 383Q486 376 469 376H240Q233 349 229 324T222 276H442Q471 276 471 260Q471 252 468 241T459 225Q452 218 435 218H220Q223 182 234 151T266 96T316 59T381 46Q431 46 470 69T545 144Q546 145 551 144T562 138T572 127T577 111Q577 86 545 54Q532 41 514 29T475 8T430 -6T383 -12Q327 -12 285 4T214 51T170 123T152 218H82Q53 218 53 234Q53 242 56 253T65 269Q72 276 89 276H154Q157 299 161 324T174 376H116Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="603" d="M116 255Q87 255 87 271Q87 279 90 290T99 306Q106 313 123 313H266L136 629Q134 634 146 642T175 651Q186 651 196 646T212 623L320 336L568 653Q568 653 574 652T588 648T602 637T609 617Q609 606 604 596T580 564L371 313H507Q536 313 536 297Q536 289 533 278T524 262Q517 255 500 255H336L319 175H478Q507 175 507 159Q507 151 504 140T495 124Q488 117 471 117H307L288 27Q284 10 275 3T251 -4H248Q234 -4 228 3T226 27L244 117H87Q58 117 58 133Q58 141 61 152T70 168Q77 175 94 175H256L273 255H116Z" />
+<glyph unicode="&#xA6;" glyph-name="brokenbar" horiz-adv-x="277" d="M43 -197Q29 -197 23 -188T21 -162L92 170Q96 186 103 193T128 201H134Q148 201 154 192T156 166L85 -166Q81 -183 74 -190T49 -197H43ZM138 253Q124 253 118 262T116 288L187 620Q191 636 198 643T223 651H229Q243 651 249 642T251 616L180 284Q176 267 169 260T144 253H138Z" />
+<glyph unicode="&#xA7;" glyph-name="section" d="M221 -160Q148 -160 99 -141T23 -95Q-8 -64 -8 -35Q-8 -22 -2 -13T12 1T26 8T33 9Q43 -15 59 -35T98 -70T152 -93T226 -102Q295 -102 330 -76T366 -5Q366 15 355 29T325 55T281 76T227 97Q197 108 167 120T114 149T77 189T62 246Q62 277 73 301T102 343T145 373T196 391Q168 409 150 435T132 501Q132 535 146 564T188 614T253 647T339 659Q407 659 449 641T516 598Q530 584 537 570T544 544Q544 532 538 524T524 510T510 503T502 503Q481 548 440 574T334 601Q269 601 234 575T198 510Q198 488 209 473T239 445T284 422T339 400Q369 388 397 376T448 348T485 309T499 256Q499 223 488 198T459 155T417 126T365 109Q394 92 413 67T432 3Q432 -70 377 -115T221 -160ZM304 137Q329 138 352 146T394 168T423 201T434 243Q434 264 423 279T394 305T350 326T297 346Q286 350 275 354T252 363Q226 360 204 352T164 330T137 300T127 260Q127 237 138 221T168 193T213 170T269 150L304 137Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="534" d="M426 548Q377 548 377 586Q377 609 389 626T428 644Q477 644 477 606Q477 583 465 566T426 548ZM258 548Q209 548 209 586Q209 609 221 626T260 644Q309 644 309 606Q309 583 297 566T258 548Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="791" d="M413 -12Q341 -12 281 13T177 82T109 187T85 322Q85 394 110 456T179 563T283 633T414 659Q486 659 546 634T649 565T717 460T742 325Q742 253 717 191T648 84T544 14T413 -12ZM415 36Q475 36 525 57T612 116T668 206T688 323Q688 385 667 437T609 528T522 589T412 611Q352 611 302 590T215 531T159 441T139 324Q139 262 160 210T218 119T305 58T415 36ZM422 144Q383 144 351 157T295 193T258 250T245 322Q245 360 258 393T296 450T352 487T421 501Q461 501 490 488T535 459Q554 440 554 421Q554 412 549 406T538 395T527 390T520 390Q506 416 481 430T420 445Q397 445 377 436T340 410T315 371T306 323Q306 296 315 274T340 235T377 209T424 200Q461 200 486 217T524 260Q525 261 530 260T542 255T554 245T559 229Q559 210 535 185Q520 169 491 157T422 144Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="521" d="M437 261Q403 261 390 282T381 340Q355 298 317 278T235 258Q204 258 177 268T128 299T95 350T83 420Q83 464 99 507T143 584T212 639T299 660Q346 660 380 639T431 582L434 593Q441 626 452 639T484 653Q493 653 500 650T507 645L444 351Q438 322 444 311T477 297Q477 297 477 292T474 279T462 267T437 261ZM247 311Q275 311 301 322T348 353T383 397T404 451L417 513Q404 557 375 582T298 608Q262 608 234 592T185 549T155 489T144 423Q144 369 174 340T247 311ZM199 -6Q156 -6 156 25Q156 55 170 71Q181 83 204 83Q247 83 247 52Q247 21 232 6Q220 -6 199 -6Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="541" d="M409 88Q389 88 368 105T324 150Q312 165 302 180T283 209T271 232T266 245Q266 247 275 256T298 279T332 309T371 340Q404 366 436 384T488 403Q499 403 505 396T511 379Q511 371 503 360T480 336T447 308T406 281Q387 270 367 257T325 232Q342 218 354 208T380 185Q410 157 423 142T436 111Q436 102 428 95T409 88ZM232 84Q212 84 187 103T136 154Q122 171 110 188T88 219T74 243T68 256Q68 261 94 286T160 343Q192 368 221 384T270 401Q281 401 287 394T293 377Q293 362 268 339T202 290Q185 279 167 267T127 243Q144 229 160 216T191 189Q226 157 241 140T258 107Q259 98 251 91T232 84Z" />
+<glyph unicode="&#xAC;" glyph-name="logicalnot" horiz-adv-x="600" d="M49 380H543V96H453V297H49V380Z" />
+<glyph unicode="&#xAD;" glyph-name="uni00AD" horiz-adv-x="400" d="M66 272H337V212H66V272Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="791" d="M413 -12Q341 -12 281 13T177 82T109 187T85 322Q85 394 110 456T179 563T283 633T414 659Q486 659 546 634T649 565T717 460T742 325Q742 253 717 191T648 84T544 14T413 -12ZM415 36Q475 36 525 57T612 116T668 206T688 323Q688 385 667 437T609 528T522 589T412 611Q352 611 302 590T215 531T159 441T139 324Q139 262 160 210T218 119T305 58T415 36ZM560 391Q560 364 551 345T527 314T494 295T456 288L553 176Q555 173 546 162T520 151Q512 151 501 155T479 173L392 283H348V179Q348 151 322 151H314Q288 151 288 183V464Q288 480 297 489T320 498H426Q491 498 525 472T560 391ZM427 333Q462 333 481 348T500 390Q500 447 424 447H347V333H427Z" />
+<glyph unicode="&#xAF;" glyph-name="uni00AF" horiz-adv-x="534" d="M231 568Q200 568 200 586Q200 594 203 605T212 622Q219 630 238 630H458Q489 630 489 612Q489 604 486 593T477 576Q470 568 451 568H231Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="422" d="M239 348Q210 348 185 357T142 385T114 428T103 484Q103 521 117 553T155 610T210 648T277 662Q306 662 331 653T374 625T402 582T413 526Q413 488 399 456T361 399T306 362T239 348ZM249 400Q271 400 290 409T325 435T348 474T357 522Q357 558 332 584T267 610Q245 610 226 601T191 575T168 536T159 488Q159 452 184 426T249 400Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="634" d="M183 411Q154 411 154 427Q154 435 157 446T166 462Q173 469 190 469H335L365 612Q369 629 376 636T394 643H402Q413 643 421 637T426 612L396 469H534Q563 469 563 453Q563 445 560 434T551 418Q544 411 527 411H383L353 267Q349 250 342 243T324 236H316Q305 236 297 242T292 267L322 411H183ZM113 119Q82 119 82 135Q82 143 86 155T95 173Q102 180 120 180H480Q511 180 511 164Q511 156 507 144T498 126Q491 119 473 119H113Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="370" d="M115 381Q98 364 94 338H269Q303 338 303 316Q303 305 297 295T290 285H61Q50 285 43 296T37 324Q39 352 48 374T75 414Q95 435 122 447T179 470Q228 487 255 502T282 550Q282 573 262 587T210 601Q176 601 156 584T123 540Q123 539 117 539T104 543T92 552T86 571Q86 582 92 594T109 617Q126 633 153 644T217 655Q275 655 309 628T343 553Q343 525 333 506T306 472T264 448T211 428Q178 417 155 408T115 381Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="370" d="M309 395Q309 374 302 354T277 316T231 289T162 278Q123 278 95 288T51 314Q38 326 34 338T29 359Q29 373 35 381T49 393T63 397T70 397Q75 365 98 347T163 329Q205 329 225 349T246 398Q246 421 229 435T178 450H157Q133 450 133 467Q133 472 135 480T142 492Q148 499 168 499H193Q217 499 232 503T257 516T269 534T273 555Q273 577 254 590T205 604Q172 604 150 589T116 553Q115 552 110 552T98 556T87 565T82 581Q82 600 99 615Q116 633 143 644T209 655Q234 655 257 648T298 629T325 600T336 563Q336 527 316 503T253 470Q309 450 309 395Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="534" d="M263 551Q262 553 275 567T309 600T356 641T407 677Q431 692 453 692Q466 692 474 684T482 664Q482 640 441 616Q413 600 383 587T326 565T282 553T263 551Z" />
+<glyph unicode="&#xB5;" glyph-name="uni00B5" horiz-adv-x="571" d="M52 -298Q7 -298 -9 -271T-14 -190L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37V30Q500 23 497 15T484 -1T455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 147 4Q118 17 100 40L51 -189Q43 -224 52 -238T99 -253V-260Q99 -267 95 -275T81 -291T52 -298Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="622" d="M451 -4Q419 -4 427 31L552 620Q556 636 563 643T586 651H595Q610 651 616 642T619 616L494 27Q490 10 483 3T460 -4H451ZM325 -4Q293 -4 301 31L356 290H301Q195 290 139 327T83 440Q83 482 96 520T139 586T217 630T334 647H453Q476 647 485 635T490 602L368 27Q364 10 357 3T334 -4H325Z" />
+<glyph unicode="&#xB7;" glyph-name="uni00B7" horiz-adv-x="255" d="M127 194Q104 194 92 203T79 228Q79 264 95 280Q108 293 132 293Q155 293 167 284T180 259Q180 223 164 207Q151 194 127 194Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="534" d="M127 -234Q124 -237 118 -236T107 -230T99 -222T97 -214L204 -47Q205 -46 214 -47T233 -52T253 -63T262 -83Q262 -105 240 -128L127 -234Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="370" d="M69 285Q41 285 44 307Q44 309 45 315T49 324Q56 339 79 339H148L199 569Q177 547 146 547Q123 547 111 558T98 587Q98 597 101 604T109 617T118 624T122 626Q127 618 137 611T164 603Q182 603 199 616T224 651H249Q277 651 271 620L208 339H271Q299 339 296 317Q296 315 295 309T290 300Q285 285 261 285H69Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="477" d="M265 257Q227 257 195 269T139 305T102 361T88 435Q88 479 104 519T148 591T216 640T303 659Q341 659 373 647T430 611T467 555T481 482Q481 437 465 397T421 325T352 276T265 257ZM192 -6Q149 -6 149 25Q149 55 163 71Q174 83 197 83Q240 83 240 52Q240 21 225 6Q213 -6 192 -6ZM268 310Q300 310 328 324T376 361T408 415T420 480Q420 507 411 530T385 570T347 596T300 606Q268 606 240 592T192 555T161 501T149 436Q149 409 158 386T183 346T221 320T268 310Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="541" d="M273 85Q262 85 256 92T250 109Q250 124 275 147T341 196Q358 207 376 219T416 243Q399 257 383 270T352 297Q317 329 302 346T285 379Q284 388 292 395T311 402Q331 402 356 383T407 332Q421 315 433 298T455 267T469 243T475 230Q475 225 449 200T383 143Q351 118 322 102T273 85ZM55 83Q44 83 38 90T32 107Q32 114 40 125T62 150T96 178T137 205Q156 216 176 229T218 254Q201 268 189 278T163 301Q133 329 120 344T107 375Q107 384 115 391T134 398Q154 398 175 381T219 336Q231 321 241 306T260 277T272 254T277 241Q277 239 268 230T245 207T211 177T172 146Q139 120 107 102T55 83Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="826" d="M673 -3Q661 -3 654 4T651 28L664 95H513Q511 95 503 106T494 133Q494 145 500 155T519 181L672 338Q686 354 697 360T730 366Q782 366 772 321L735 147H770Q795 147 795 130Q795 125 793 116T785 101Q781 98 777 97T763 95H724L709 27Q706 10 699 4T681 -3H673ZM732 632Q740 629 746 625Q751 621 754 615T756 600Q754 589 746 578T720 547Q711 538 683 511T617 448T537 371T457 292Q438 273 414 250T363 200T308 147T254 95Q192 36 127 -27Q125 -28 121 -27T111 -21T103 -10T101 5Q102 12 104 17T111 29T121 41T137 58Q146 68 173 94T238 157T318 233T399 312Q418 331 442 354T494 404T549 456T604 509Q666 568 732 632ZM69 285Q41 285 44 307Q44 309 45 315T49 324Q56 339 79 339H148L199 569Q177 547 146 547Q123 547 111 558T98 587Q98 597 101 604T109 617T118 624T122 626Q127 618 137 611T164 603Q182 603 199 616T224 651H249Q277 651 271 620L208 339H271Q299 339 296 317Q296 315 295 309T290 300Q285 285 261 285H69ZM550 146H678L714 316L550 146Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="826" d="M571 96Q554 79 550 53H725Q759 53 759 31Q759 20 753 10T746 0H517Q506 0 499 11T493 39Q495 67 504 89T531 129Q551 150 578 162T635 185Q684 202 711 217T738 265Q738 288 718 302T666 316Q632 316 612 299T579 255Q579 254 573 254T560 258T548 267T542 286Q542 297 548 309T565 332Q582 348 609 359T673 370Q731 370 765 343T799 268Q799 240 789 221T762 187T720 163T667 143Q634 132 611 123T571 96ZM702 632Q710 629 716 625Q721 621 724 615T726 600Q724 589 716 578T690 547Q681 538 653 511T587 448T507 371T427 292Q408 273 384 250T333 200T278 147T224 95Q162 36 97 -27Q95 -28 91 -27T81 -21T73 -10T71 5Q72 12 74 17T81 29T91 41T107 58Q116 68 143 94T208 157T288 233T369 312Q388 331 412 354T464 404T519 456T574 509Q636 568 702 632ZM69 285Q41 285 44 307Q44 309 45 315T49 324Q56 339 79 339H148L199 569Q177 547 146 547Q123 547 111 558T98 587Q98 597 101 604T109 617T118 624T122 626Q127 618 137 611T164 603Q182 603 199 616T224 651H249Q277 651 271 620L208 339H271Q299 339 296 317Q296 315 295 309T290 300Q285 285 261 285H69Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="826" d="M673 -3Q661 -3 654 4T651 28L664 95H513Q511 95 503 106T494 133Q494 145 500 155T519 181L672 338Q686 354 697 360T730 366Q782 366 772 321L735 147H770Q795 147 795 130Q795 125 793 116T785 101Q781 98 777 97T763 95H724L709 27Q706 10 699 4T681 -3H673ZM732 632Q740 629 746 625Q751 621 754 615T756 600Q754 589 746 578T720 547Q711 538 683 511T617 448T537 371T457 292Q438 273 414 250T363 200T308 147T254 95Q192 36 127 -27Q125 -28 121 -27T111 -21T103 -10T101 5Q102 12 104 17T111 29T121 41T137 58Q146 68 173 94T238 157T318 233T399 312Q418 331 442 354T494 404T549 456T604 509Q666 568 732 632ZM309 395Q309 374 302 354T277 316T231 289T162 278Q123 278 95 288T51 314Q38 326 34 338T29 359Q29 373 35 381T49 393T63 397T70 397Q75 365 98 347T163 329Q205 329 225 349T246 398Q246 421 229 435T178 450H157Q133 450 133 467Q133 472 135 480T142 492Q148 499 168 499H193Q217 499 232 503T257 516T269 534T273 555Q273 577 254 590T205 604Q172 604 150 589T116 553Q115 552 110 552T98 556T87 565T82 581Q82 600 99 615Q116 633 143 644T209 655Q234 655 257 648T298 629T325 600T336 563Q336 527 316 503T253 470Q309 450 309 395ZM550 146H678L714 316L550 146Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="497" d="M356 426Q350 399 341 380T317 345T280 317T227 294Q196 283 171 272T129 245T103 210T93 162Q93 138 102 117T128 81T170 56T227 46Q284 46 322 80T381 168Q381 169 388 168T403 163T417 152T424 132Q424 112 413 91T382 50Q354 22 316 4T223 -14Q178 -14 142 -1T80 37T40 93T26 162Q26 204 41 234T80 285T131 319T184 340Q213 349 232 358T263 378T281 402T293 435L301 468H365L356 426ZM349 651Q372 651 384 642T397 617Q397 581 381 565Q368 552 344 552Q321 552 309 561T296 586Q296 622 312 638Q325 651 349 651Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM449 721Q447 719 431 723T393 738T344 763T296 796Q279 810 273 821T266 845Q266 858 276 866T298 874Q309 874 320 868T343 853Q365 835 385 813T420 771T443 737T449 721Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM357 721Q356 723 369 737T403 770T450 811T501 847Q525 862 547 862Q560 862 568 854T576 834Q576 810 535 786Q507 770 477 757T420 735T376 723T357 721Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM291 719Q281 719 277 724T272 736Q272 753 289 773T339 822Q355 836 371 848T401 869T422 883T432 889Q433 889 439 884T454 869T473 849T494 825Q516 796 524 778T533 748Q533 734 526 727T509 719Q496 719 484 733T453 780L424 831L373 778Q347 751 326 735T291 719Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM478 718Q454 718 416 739T339 798Q333 785 327 772T314 749T300 732T285 725Q273 725 268 731T262 745Q262 762 269 779T287 810T312 832T342 841Q366 841 404 820T481 761Q487 774 493 787T506 810T520 827T535 834Q547 834 552 828T558 814Q558 797 551 780T533 749T508 727T478 718Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM490 718Q441 718 441 756Q441 779 453 796T492 814Q541 814 541 776Q541 753 529 736T490 718ZM322 718Q273 718 273 756Q273 779 285 796T324 814Q373 814 373 776Q373 753 361 736T322 718Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="643" d="M52 -3Q52 -3 45 -3T28 -1T12 9T5 33Q5 42 9 53T24 82L333 602L342 615Q318 628 305 650T291 702Q291 728 300 752T327 795T368 825T421 836Q470 836 500 808T530 735Q530 693 506 658T442 608Q445 602 446 593L562 17Q563 13 552 5T524 -3Q512 -3 502 5T488 34L455 211H175L52 -3ZM443 271L386 576L210 271H443ZM402 646Q420 646 434 653T458 673T473 700T479 731Q479 756 462 773T419 791Q401 791 387 784T363 764T348 737T342 706Q342 681 359 664T402 646Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="880" d="M52 -3Q52 -3 40 -3T17 4T5 29T24 82L339 612Q349 629 361 638T396 647H882Q881 646 881 643Q880 641 880 638T879 630Q877 622 875 610T871 592Q870 589 870 586Q870 585 869 585H448L493 362H756Q755 361 755 358Q754 356 754 353T753 346Q751 338 749 326T745 309Q744 306 744 304Q744 303 743 302H505L553 62H773Q772 61 772 58Q771 56 771 53T770 45Q768 37 766 25T762 7Q761 4 761 1Q761 0 760 0H541Q515 0 503 9T486 44L455 211H175L52 -3ZM443 271L386 576L210 271H443Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="684" d="M370 -12Q304 -12 250 7T158 63T99 150T78 267Q78 346 105 417T181 542T295 627T438 659Q510 659 556 638T631 588Q653 566 663 544T673 508Q673 495 667 487T653 474T638 468T628 469Q580 600 435 600Q374 600 322 574T232 504T172 402T150 277Q150 224 166 182T211 110T280 64T369 48Q439 48 491 76T576 153Q577 154 582 153T594 147T605 135T610 117Q610 93 581 64Q566 49 545 36T496 12T436 -5T370 -12ZM272 -234Q269 -237 263 -236T252 -230T244 -222T242 -214L349 -47Q350 -46 359 -47T378 -52T398 -63T407 -83Q407 -105 385 -128L272 -234Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM483 721Q481 719 465 723T427 738T378 763T330 796Q313 810 307 821T300 845Q300 858 310 866T332 874Q343 874 354 868T377 853Q399 835 419 813T454 771T477 737T483 721Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM361 721Q360 723 373 737T407 770T454 811T505 847Q529 862 551 862Q564 862 572 854T580 834Q580 810 539 786Q511 770 481 757T424 735T380 723T361 721Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM315 719Q305 719 301 724T296 736Q296 753 313 773T363 822Q379 836 395 848T425 869T446 883T456 889Q457 889 463 884T478 869T497 849T518 825Q540 796 548 778T557 748Q557 734 550 727T533 719Q520 719 508 733T477 780L448 831L397 778Q371 751 350 735T315 719Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM504 718Q455 718 455 756Q455 779 467 796T506 814Q555 814 555 776Q555 753 543 736T504 718ZM336 718Q287 718 287 756Q287 779 299 796T338 814Q387 814 387 776Q387 753 375 736T336 718Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM276 721Q274 719 258 723T220 738T171 763T123 796Q106 810 100 821T93 845Q93 858 103 866T125 874Q136 874 147 868T170 853Q192 835 212 813T247 771T270 737T276 721Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM194 721Q193 723 206 737T240 770T287 811T338 847Q362 862 384 862Q397 862 405 854T413 834Q413 810 372 786Q344 770 314 757T257 735T213 723T194 721Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM128 719Q118 719 114 724T109 736Q109 753 126 773T176 822Q192 836 208 848T238 869T259 883T269 889Q270 889 276 884T291 869T310 849T331 825Q353 796 361 778T370 748Q370 734 363 727T346 719Q333 719 321 733T290 780L261 831L210 778Q184 751 163 735T128 719Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM327 718Q278 718 278 756Q278 779 290 796T329 814Q378 814 378 776Q378 753 366 736T327 718ZM159 718Q110 718 110 756Q110 779 122 796T161 814Q210 814 210 776Q210 753 198 736T159 718Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="731" d="M127 0Q104 0 95 12T90 45L143 295H79Q50 295 50 311Q50 319 53 330T62 346Q69 353 86 353H155L209 606Q215 647 256 647H366Q517 647 601 577T686 378Q686 300 661 231T586 111T460 30T283 0H127ZM293 60Q376 60 436 85T536 152T595 250T614 368Q614 471 548 529T361 587H273L223 353H434Q463 353 463 337Q463 329 460 318T451 302Q444 295 427 295H211L161 60H293Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="709" d="M172 605Q177 626 189 638T220 651H237Q254 651 262 642T280 615L522 101L639 647H702L572 37Q568 19 557 8T528 -4H521Q506 -4 497 5T479 32L227 566L106 0H43L172 605ZM541 718Q517 718 479 739T402 798Q396 785 390 772T377 749T363 732T348 725Q336 725 331 731T325 745Q325 762 332 779T350 810T375 832T405 841Q429 841 467 820T544 761Q550 774 556 787T569 810T583 827T598 834Q610 834 615 828T621 814Q621 797 614 780T596 749T571 727T541 718Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM521 721Q519 719 503 723T465 738T416 763T368 796Q351 810 345 821T338 845Q338 858 348 866T370 874Q381 874 392 868T415 853Q437 835 457 813T492 771T515 737T521 721Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM379 721Q378 723 391 737T425 770T472 811T523 847Q547 862 569 862Q582 862 590 854T598 834Q598 810 557 786Q529 770 499 757T442 735T398 723T379 721Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM353 719Q343 719 339 724T334 736Q334 753 351 773T401 822Q417 836 433 848T463 869T484 883T494 889Q495 889 501 884T516 869T535 849T556 825Q578 796 586 778T595 748Q595 734 588 727T571 719Q558 719 546 733T515 780L486 831L435 778Q409 751 388 735T353 719Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM530 718Q506 718 468 739T391 798Q385 785 379 772T366 749T352 732T337 725Q325 725 320 731T314 745Q314 762 321 779T339 810T364 832T394 841Q418 841 456 820T533 761Q539 774 545 787T558 810T572 827T587 834Q599 834 604 828T610 814Q610 797 603 780T585 749T560 727T530 718Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM552 718Q503 718 503 756Q503 779 515 796T554 814Q603 814 603 776Q603 753 591 736T552 718ZM384 718Q335 718 335 756Q335 779 347 796T386 814Q435 814 435 776Q435 753 423 736T384 718Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="573" d="M413 79Q398 79 387 94L291 222L140 94Q128 83 116 83Q107 83 97 94T86 114Q86 129 100 140L254 271L158 400Q148 413 148 422Q148 431 157 441T179 451Q194 451 205 436L300 309L450 436Q462 447 474 447Q483 447 493 436T504 416Q504 401 490 390L337 260L434 130Q444 117 444 108Q444 99 435 89T413 79Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="766" d="M42 -32Q40 -33 36 -31T27 -24T18 -13T14 2Q14 16 20 28T43 54L118 120Q78 188 78 269Q78 355 108 426T189 550T304 630T436 659Q499 659 550 638T640 581L743 673Q744 674 749 672T758 665T767 653T771 639Q771 625 765 613T742 587L680 532Q701 498 712 460T724 379Q724 293 694 222T613 98T498 18T366 -11Q300 -11 248 11T157 71L42 -32ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 439 626 489L206 114Q235 83 276 66T373 48ZM150 274Q150 215 173 164L591 538Q563 567 523 583T429 600Q378 600 328 576T239 509T175 406T150 274Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM494 711Q492 709 476 713T438 728T389 753T341 786Q324 800 318 811T311 835Q311 848 321 856T343 864Q354 864 365 858T388 843Q410 825 430 803T465 761T488 727T494 711Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM352 711Q351 713 364 727T398 760T445 801T496 837Q520 852 542 852Q555 852 563 844T571 824Q571 800 530 776Q502 760 472 747T415 725T371 713T352 711Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM323 709Q313 709 309 714T304 726Q304 743 321 763T371 812Q387 826 403 838T433 859T454 873T464 879Q465 879 471 874T486 859T505 839T526 815Q548 786 556 768T565 738Q565 724 558 717T541 709Q528 709 516 723T485 770L456 821L405 768Q379 741 358 725T323 709Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM524 708Q475 708 475 746Q475 769 487 786T526 804Q575 804 575 766Q575 743 563 726T524 708ZM356 708Q307 708 307 746Q307 769 319 786T358 804Q407 804 407 766Q407 743 395 726T356 708Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="583" d="M265 272L110 647H181L308 331L581 653Q581 653 593 651T614 639T622 612T593 564L331 265L275 0H207L265 272ZM325 711Q324 713 337 727T371 760T418 801T469 837Q493 852 515 852Q528 852 536 844T544 824Q544 800 503 776Q475 760 445 747T388 725T344 713T325 711Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="592" d="M74 -4Q42 -4 50 31L175 620Q179 637 186 644T209 651H218Q250 651 242 616L222 524H341Q445 524 500 481T556 357Q556 247 490 188T298 129H138L117 27Q113 10 106 3T83 -4H74ZM311 189Q393 189 439 230T486 348Q486 463 337 463H209L151 189H311Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="560" d="M296 -12Q218 -12 181 25Q162 44 162 63Q162 74 167 82T178 96T189 104T196 105Q210 82 233 64T300 45Q327 45 351 56T394 87T422 134T433 196Q433 215 428 233T409 267T372 298T315 324Q289 333 280 343T270 368Q270 392 280 405T295 422Q386 422 430 447T474 536Q474 580 440 605T351 630Q294 630 255 591T196 462L99 0H34L134 468Q158 582 215 635T352 689Q439 689 489 649T539 540Q539 467 491 422T340 371Q374 363 403 348T453 312T487 263T499 202Q499 156 483 117T439 49T375 4T296 -12Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM409 551Q407 549 391 553T353 568T304 593T256 626Q239 640 233 651T226 675Q226 688 236 696T258 704Q269 704 280 698T303 683Q325 665 345 643T380 601T403 567T409 551Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM297 551Q296 553 309 567T343 600T390 641T441 677Q465 692 487 692Q500 692 508 684T516 664Q516 640 475 616Q447 600 417 587T360 565T316 553T297 551Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM251 549Q241 549 237 554T232 566Q232 583 249 603T299 652Q315 666 331 678T361 699T382 713T392 719Q393 719 399 714T414 699T433 679T454 655Q476 626 484 608T493 578Q493 564 486 557T469 549Q456 549 444 563T413 610L384 661L333 608Q307 581 286 565T251 549Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM438 548Q414 548 376 569T299 628Q293 615 287 602T274 579T260 562T245 555Q233 555 228 561T222 575Q222 592 229 609T247 640T272 662T302 671Q326 671 364 650T441 591Q447 604 453 617T466 640T480 657T495 664Q507 664 512 658T518 644Q518 627 511 610T493 579T468 557T438 548Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM450 548Q401 548 401 586Q401 609 413 626T452 644Q501 644 501 606Q501 583 489 566T450 548ZM282 548Q233 548 233 586Q233 609 245 626T284 644Q333 644 333 606Q333 583 321 566T282 548Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM372 550Q323 550 293 578T263 651Q263 677 272 701T299 744T340 774T393 785Q442 785 472 757T502 684Q502 658 493 634T466 591T424 561T372 550ZM374 595Q392 595 406 602T430 622T445 649T451 680Q451 705 434 722T391 740Q373 740 359 733T335 713T320 686T314 655Q314 630 331 613T374 595Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="882" d="M196 -12Q119 -12 75 20T30 107Q30 152 49 184T110 238T223 271T395 284H405L406 288Q420 354 387 391T278 429Q220 429 184 410T123 355Q123 355 119 356T109 362T99 374T95 390Q95 402 106 419T140 451T199 477T285 488Q358 488 405 458T464 369Q502 426 558 457T688 488Q769 488 813 454T858 362Q858 326 845 298T800 251T715 221T581 210Q528 210 455 216Q454 208 454 201T454 186Q454 123 490 84T597 44Q653 44 691 64T758 123Q759 124 764 123T776 118T787 108T792 92Q792 81 788 72T770 50Q738 18 696 3T595 -12Q517 -12 471 20T408 104Q378 50 325 19T196 -12ZM205 43Q238 43 267 53T319 82T359 126T383 180L395 233L366 232Q285 229 233 221T150 199T108 163T96 112Q96 82 126 63T205 43ZM685 431Q647 431 614 419T552 386T503 335T469 271Q499 267 525 266T575 265Q639 265 681 273T748 294T782 325T792 361Q792 387 766 409T685 431Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="552" d="M274 -12Q227 -12 186 3T114 46T66 116T48 209Q48 263 69 313T126 402T211 464T318 487Q378 487 418 470T483 427Q504 406 513 384T523 345Q523 329 516 321T499 309T483 305T475 308Q465 361 426 395T320 429Q274 429 237 411T172 362T130 292T115 213Q115 176 126 146T160 93T212 59T280 47Q336 47 373 72T435 137Q436 138 441 137T454 133T466 123T472 106Q472 83 441 52Q416 27 373 8T274 -12ZM176 -234Q173 -237 167 -236T156 -230T148 -222T146 -214L253 -47Q254 -46 263 -47T282 -52T302 -63T311 -83Q311 -105 289 -128L176 -234Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM400 551Q398 549 382 553T344 568T295 593T247 626Q230 640 224 651T217 675Q217 688 227 696T249 704Q260 704 271 698T294 683Q316 665 336 643T371 601T394 567T400 551Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM288 551Q287 553 300 567T334 600T381 641T432 677Q456 692 478 692Q491 692 499 684T507 664Q507 640 466 616Q438 600 408 587T351 565T307 553T288 551Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM252 549Q242 549 238 554T233 566Q233 583 250 603T300 652Q316 666 332 678T362 699T383 713T393 719Q394 719 400 714T415 699T434 679T455 655Q477 626 485 608T494 578Q494 564 487 557T470 549Q457 549 445 563T414 610L385 661L334 608Q308 581 287 565T252 549Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM451 548Q402 548 402 586Q402 609 414 626T453 644Q502 644 502 606Q502 583 490 566T451 548ZM283 548Q234 548 234 586Q234 609 246 626T285 644Q334 644 334 606Q334 583 322 566T283 548Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM239 551Q237 549 221 553T183 568T134 593T86 626Q69 640 63 651T56 675Q56 688 66 696T88 704Q99 704 110 698T133 683Q155 665 175 643T210 601T233 567T239 551Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM117 551Q116 553 129 567T163 600T210 641T261 677Q285 692 307 692Q320 692 328 684T336 664Q336 640 295 616Q267 600 237 587T180 565T136 553T117 551Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM81 549Q71 549 67 554T62 566Q62 583 79 603T129 652Q145 666 161 678T191 699T212 713T222 719Q223 719 229 714T244 699T263 679T284 655Q306 626 314 608T323 578Q323 564 316 557T299 549Q286 549 274 563T243 610L214 661L163 608Q137 581 116 565T81 549Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM280 548Q231 548 231 586Q231 609 243 626T282 644Q331 644 331 606Q331 583 319 566T280 548ZM112 548Q63 548 63 586Q63 609 75 626T114 644Q163 644 163 606Q163 583 151 566T112 548Z" />
+<glyph unicode="&#xF0;" glyph-name="null" horiz-adv-x="578" d="M231 474Q229 472 221 480T212 505Q212 515 217 525T246 547L334 595Q269 630 194 635Q192 635 192 643T196 662T211 680T242 689Q273 689 314 674T398 628L513 694Q515 696 523 688T532 663Q532 653 527 643T498 621L441 590Q468 563 490 529T525 452T541 360T532 253Q522 205 501 155T430 55Q401 26 358 7T261 -12Q213 -12 173 0T104 37T58 97T42 179Q42 229 59 274T110 352T189 406T294 426Q328 426 357 418T410 396T451 365T478 330Q481 407 455 465T383 562L231 474ZM259 44Q336 44 390 98Q419 127 437 171T465 263Q459 281 444 300T406 334T355 359T293 369Q244 369 209 354T151 313T118 253T107 178Q107 146 118 121T150 79T198 53T259 44Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L182 398Q209 441 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8ZM432 548Q408 548 370 569T293 628Q287 615 281 602T268 579T254 562T239 555Q227 555 222 561T216 575Q216 592 223 609T241 640T266 662T296 671Q320 671 358 650T435 591Q441 604 447 617T460 640T474 657T489 664Q501 664 506 658T512 644Q512 627 505 610T487 579T462 557T432 548Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM392 551Q390 549 374 553T336 568T287 593T239 626Q222 640 216 651T209 675Q209 688 219 696T241 704Q252 704 263 698T286 683Q308 665 328 643T363 601T386 567T392 551Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM278 551Q277 553 290 567T324 600T371 641T422 677Q446 692 468 692Q481 692 489 684T497 664Q497 640 456 616Q428 600 398 587T341 565T297 553T278 551Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM232 549Q222 549 218 554T213 566Q213 583 230 603T280 652Q296 666 312 678T342 699T363 713T373 719Q374 719 380 714T395 699T414 679T435 655Q457 626 465 608T474 578Q474 564 467 557T450 549Q437 549 425 563T394 610L365 661L314 608Q288 581 267 565T232 549Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM419 548Q395 548 357 569T280 628Q274 615 268 602T255 579T241 562T226 555Q214 555 209 561T203 575Q203 592 210 609T228 640T253 662T283 671Q307 671 345 650T422 591Q428 604 434 617T447 640T461 657T476 664Q488 664 493 658T499 644Q499 627 492 610T474 579T449 557T419 548Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM431 548Q382 548 382 586Q382 609 394 626T433 644Q482 644 482 606Q482 583 470 566T431 548ZM263 548Q214 548 214 586Q214 609 226 626T265 644Q314 644 314 606Q314 583 302 566T263 548Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="634" d="M354 379Q331 379 319 388T306 413Q306 449 322 465Q335 478 359 478Q382 478 394 469T407 444Q407 408 391 392Q378 379 354 379ZM286 59Q263 59 251 68T238 93Q238 129 254 145Q267 158 291 158Q314 158 326 149T339 124Q339 88 323 72Q310 59 286 59ZM135 238Q101 238 109 265L111 272Q115 286 122 292T148 299H511Q529 299 534 293T536 272L534 265Q530 251 523 245T498 238H135Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="584" d="M35 -26Q33 -27 29 -25T20 -19T12 -8T8 5Q8 13 14 24T34 48L82 90Q66 115 58 145T49 209Q49 264 69 315T125 404T210 465T317 488Q359 488 397 473T465 431L543 501Q544 502 548 500T558 494T566 483T570 470Q570 461 564 451T544 427L501 389Q518 363 527 333T536 267Q536 211 516 161T460 72T375 11T268 -12Q224 -12 185 3T117 47L35 -26ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 309 451 347L162 90Q205 45 272 45ZM115 211Q115 165 133 131L420 388Q399 408 372 419T313 431Q271 431 235 413T173 365T131 295T115 211Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM383 551Q381 549 365 553T327 568T278 593T230 626Q213 640 207 651T200 675Q200 688 210 696T232 704Q243 704 254 698T277 683Q299 665 319 643T354 601T377 567T383 551Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM281 551Q280 553 293 567T327 600T374 641T425 677Q449 692 471 692Q484 692 492 684T500 664Q500 640 459 616Q431 600 401 587T344 565T300 553T281 551Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM235 549Q225 549 221 554T216 566Q216 583 233 603T283 652Q299 666 315 678T345 699T366 713T376 719Q377 719 383 714T398 699T417 679T438 655Q460 626 468 608T477 578Q477 564 470 557T453 549Q440 549 428 563T397 610L368 661L317 608Q291 581 270 565T235 549Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM434 548Q385 548 385 586Q385 609 397 626T436 644Q485 644 485 606Q485 583 473 566T434 548ZM266 548Q217 548 217 586Q217 609 229 626T268 644Q317 644 317 606Q317 583 305 566T266 548Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="498" d="M9 -204Q-15 -204 -34 -199T-65 -181Q-77 -169 -77 -153Q-77 -145 -73 -139T-65 -127T-56 -120T-50 -119Q-40 -130 -23 -137T5 -145Q20 -145 35 -139T68 -117T109 -72T162 1L178 24Q161 26 153 37T143 67L115 330Q110 382 93 405T40 428Q40 428 40 436T42 454T57 472T90 480Q116 480 134 462Q170 426 179 324L203 59L457 476H528L225 -12Q189 -69 161 -106T108 -165T60 -195T9 -204ZM255 541Q254 543 267 557T301 590T348 631T399 667Q423 682 445 682Q458 682 466 674T474 654Q474 630 433 606Q405 590 375 577T318 555T274 543T255 541Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="600" d="M292 -12Q227 -12 183 19T118 95L63 -166Q59 -183 52 -190T33 -197H25Q12 -197 3 -191T-2 -166L171 650Q175 667 181 674T201 681H209Q221 681 230 675T236 650L181 394Q215 440 257 464T356 488Q394 488 429 475T492 435T535 369T552 277Q552 222 533 170T480 78T398 13T292 -12ZM348 431Q290 431 241 398T164 311L145 221Q139 188 146 156T173 100T224 60T296 45Q342 45 377 65T437 118T473 192T486 277Q486 314 475 342T444 390T400 420T348 431Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="498" d="M9 -204Q-15 -204 -34 -199T-65 -181Q-77 -169 -77 -153Q-77 -145 -73 -139T-65 -127T-56 -120T-50 -119Q-40 -130 -23 -137T5 -145Q20 -145 35 -139T68 -117T109 -72T162 1L178 24Q161 26 153 37T143 67L115 330Q110 382 93 405T40 428Q40 428 40 436T42 454T57 472T90 480Q116 480 134 462Q170 426 179 324L203 59L457 476H528L225 -12Q189 -69 161 -106T108 -165T60 -195T9 -204ZM393 548Q344 548 344 586Q344 609 356 626T395 644Q444 644 444 606Q444 583 432 566T393 548ZM225 548Q176 548 176 586Q176 609 188 626T227 644Q276 644 276 606Q276 583 264 566T225 548Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM306 733Q275 733 275 751Q275 759 278 770T287 787Q294 795 313 795H533Q564 795 564 777Q564 769 561 758T552 741Q545 733 526 733H306Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM265 568Q234 568 234 586Q234 594 237 605T246 622Q253 630 272 630H492Q523 630 523 612Q523 604 520 593T511 576Q504 568 485 568H265Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H494L455 211H175L54 0H-25ZM443 271L386 576L210 271H443ZM420 713Q396 713 373 723T330 749T299 784T287 820Q287 831 293 839T312 847Q328 847 352 828T419 756Q466 805 495 826T544 847Q555 847 562 840T569 823Q569 808 556 789T522 753T474 725T420 713Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="603" d="M489 -8Q450 -8 433 16T423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45ZM380 548Q356 548 333 558T290 584T259 619T247 655Q247 666 253 674T272 682Q288 682 312 663T379 591Q426 640 455 661T504 682Q515 682 522 675T529 658Q529 643 516 624T482 588T434 560T380 548Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="643" d="M-25 0L350 631Q358 644 367 649T393 654Q419 654 428 646T440 624L565 0H541Q510 -17 491 -32T460 -60T444 -86T439 -109Q439 -124 448 -133T472 -142Q494 -142 509 -124Q519 -126 527 -133T535 -154Q535 -174 517 -187T467 -200Q427 -200 402 -178T376 -121Q376 -86 404 -53T492 7L455 211H175L54 0H-25ZM443 271L386 576L210 271H443Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="603" d="M534 37Q534 37 534 32T533 20T525 6T509 -5Q478 -22 459 -37T428 -65T412 -91T407 -114Q407 -129 416 -138T440 -147Q462 -147 477 -129Q487 -131 495 -138T503 -159Q503 -179 485 -192T435 -205Q395 -205 370 -183T344 -126Q344 -92 370 -60T453 -1Q412 19 423 86Q390 37 342 13T240 -12Q201 -12 167 1T106 39T65 102T49 190Q49 245 69 298T125 394T210 462T319 488Q379 488 423 461T489 393L507 476H571L491 101Q483 66 492 52T534 37ZM248 45Q285 45 319 60T381 100T427 158T454 228L471 307Q453 364 416 397T315 431Q268 431 231 410T168 354T129 277T115 191Q115 156 125 129T154 83T197 55T248 45Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="684" d="M370 -12Q304 -12 250 7T158 63T99 150T78 267Q78 346 105 417T181 542T295 627T438 659Q510 659 556 638T631 588Q653 566 663 544T673 508Q673 495 667 487T653 474T638 468T628 469Q580 600 435 600Q374 600 322 574T232 504T172 402T150 277Q150 224 166 182T211 110T280 64T369 48Q439 48 491 76T576 153Q577 154 582 153T594 147T605 135T610 117Q610 93 581 64Q566 49 545 36T496 12T436 -5T370 -12ZM376 716Q375 718 388 732T422 765T469 806T520 842Q544 857 566 857Q579 857 587 849T595 829Q595 805 554 781Q526 765 496 752T439 730T395 718T376 716Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="552" d="M274 -12Q227 -12 186 3T114 46T66 116T48 209Q48 263 69 313T126 402T211 464T318 487Q378 487 418 470T483 427Q504 406 513 384T523 345Q523 329 516 321T499 309T483 305T475 308Q465 361 426 395T320 429Q274 429 237 411T172 362T130 292T115 213Q115 176 126 146T160 93T212 59T280 47Q336 47 373 72T435 137Q436 138 441 137T454 133T466 123T472 106Q472 83 441 52Q416 27 373 8T274 -12ZM262 551Q261 553 274 567T308 600T355 641T406 677Q430 692 452 692Q465 692 473 684T481 664Q481 640 440 616Q412 600 382 587T325 565T281 553T262 551Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="684" d="M370 -12Q304 -12 250 7T158 63T99 150T78 267Q78 346 105 417T181 542T295 627T438 659Q510 659 556 638T631 588Q653 566 663 544T673 508Q673 495 667 487T653 474T638 468T628 469Q580 600 435 600Q374 600 322 574T232 504T172 402T150 277Q150 224 166 182T211 110T280 64T369 48Q439 48 491 76T576 153Q577 154 582 153T594 147T605 135T610 117Q610 93 581 64Q566 49 545 36T496 12T436 -5T370 -12ZM342 714Q332 714 328 719T323 731Q323 748 340 768T390 817Q406 831 422 843T452 864T473 878T483 884Q484 884 490 879T505 864T524 844T545 820Q567 791 575 773T584 743Q584 729 577 722T560 714Q547 714 535 728T504 775L475 826L424 773Q398 746 377 730T342 714Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="552" d="M274 -12Q227 -12 186 3T114 46T66 116T48 209Q48 263 69 313T126 402T211 464T318 487Q378 487 418 470T483 427Q504 406 513 384T523 345Q523 329 516 321T499 309T483 305T475 308Q465 361 426 395T320 429Q274 429 237 411T172 362T130 292T115 213Q115 176 126 146T160 93T212 59T280 47Q336 47 373 72T435 137Q436 138 441 137T454 133T466 123T472 106Q472 83 441 52Q416 27 373 8T274 -12ZM228 549Q218 549 214 554T209 566Q209 583 226 603T276 652Q292 666 308 678T338 699T359 713T369 719Q370 719 376 714T391 699T410 679T431 655Q453 626 461 608T470 578Q470 564 463 557T446 549Q433 549 421 563T390 610L361 661L310 608Q284 581 263 565T228 549Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="684" d="M370 -12Q304 -12 250 7T158 63T99 150T78 267Q78 346 105 417T181 542T295 627T438 659Q510 659 556 638T631 588Q653 566 663 544T673 508Q673 495 667 487T653 474T638 468T628 469Q580 600 435 600Q374 600 322 574T232 504T172 402T150 277Q150 224 166 182T211 110T280 64T369 48Q439 48 491 76T576 153Q577 154 582 153T594 147T605 135T610 117Q610 93 581 64Q566 49 545 36T496 12T436 -5T370 -12ZM459 714Q405 714 405 755Q405 779 418 798T461 818Q515 818 515 777Q515 753 502 734T459 714Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="552" d="M274 -12Q227 -12 186 3T114 46T66 116T48 209Q48 263 69 313T126 402T211 464T318 487Q378 487 418 470T483 427Q504 406 513 384T523 345Q523 329 516 321T499 309T483 305T475 308Q465 361 426 395T320 429Q274 429 237 411T172 362T130 292T115 213Q115 176 126 146T160 93T212 59T280 47Q336 47 373 72T435 137Q436 138 441 137T454 133T466 123T472 106Q472 83 441 52Q416 27 373 8T274 -12ZM353 549Q299 549 299 590Q299 614 312 633T355 653Q409 653 409 612Q409 588 396 569T353 549Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="684" d="M370 -12Q304 -12 250 7T158 63T99 150T78 267Q78 346 105 417T181 542T295 627T438 659Q510 659 556 638T631 588Q653 566 663 544T673 508Q673 495 667 487T653 474T638 468T628 469Q580 600 435 600Q374 600 322 574T232 504T172 402T150 277Q150 224 166 182T211 110T280 64T369 48Q439 48 491 76T576 153Q577 154 582 153T594 147T605 135T610 117Q610 93 581 64Q566 49 545 36T496 12T436 -5T370 -12ZM444 698Q443 698 437 703T421 718T402 738T382 762Q360 791 352 809T343 839Q343 853 350 860T367 868Q383 868 400 843T452 756L503 809Q529 836 550 852T585 868Q595 868 599 863T604 851Q604 834 587 814T537 765Q520 751 504 739T475 718T454 704T444 698Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="552" d="M274 -12Q227 -12 186 3T114 46T66 116T48 209Q48 263 69 313T126 402T211 464T318 487Q378 487 418 470T483 427Q504 406 513 384T523 345Q523 329 516 321T499 309T483 305T475 308Q465 361 426 395T320 429Q274 429 237 411T172 362T130 292T115 213Q115 176 126 146T160 93T212 59T280 47Q336 47 373 72T435 137Q436 138 441 137T454 133T466 123T472 106Q472 83 441 52Q416 27 373 8T274 -12ZM342 533Q341 533 335 538T319 553T300 573T280 597Q258 626 250 644T241 674Q241 688 248 695T265 703Q281 703 298 678T350 591L401 644Q427 671 448 687T483 703Q493 703 497 698T502 686Q502 669 485 649T435 600Q418 586 402 574T373 553T352 539T342 533Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="694" d="M172 606Q178 647 219 647H329Q480 647 564 577T649 378Q649 300 624 231T549 111T423 30T246 0H43L172 606ZM256 60Q339 60 399 85T499 152T558 250T577 368Q577 471 511 529T324 587H236L124 60H256ZM406 698Q405 698 399 703T383 718T364 738T344 762Q322 791 314 809T305 839Q305 853 312 860T329 868Q345 868 362 843T414 756L465 809Q491 836 512 852T547 868Q557 868 561 863T566 851Q566 834 549 814T499 765Q482 751 466 739T437 718T416 704T406 698Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="770" d="M489 -8Q451 -8 434 14T422 78Q389 34 347 11T248 -12Q210 -12 175 1T112 41T69 107T52 199Q52 254 71 306T124 398T206 463T312 488Q373 488 417 462T488 391L549 677H614L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8ZM256 45Q292 45 324 58T381 94T425 149T451 218L470 305Q450 364 410 397T308 431Q262 431 227 411T167 358T131 284T118 199Q118 162 129 134T160 86T204 56T256 45ZM647 438Q645 435 639 436T628 441T619 449T617 458L733 678Q734 679 743 679T764 676T785 665T795 645Q795 634 791 624T776 600L647 438Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="731" d="M127 0Q104 0 95 12T90 45L143 295H79Q50 295 50 311Q50 319 53 330T62 346Q69 353 86 353H155L209 606Q215 647 256 647H366Q517 647 601 577T686 378Q686 300 661 231T586 111T460 30T283 0H127ZM293 60Q376 60 436 85T536 152T595 250T614 368Q614 471 548 529T361 587H273L223 353H434Q463 353 463 337Q463 329 460 318T451 302Q444 295 427 295H211L161 60H293Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="603" d="M647 628Q678 628 678 610Q678 602 675 591T666 574Q659 566 640 566H590L491 101Q483 66 492 52T534 37Q534 37 534 30T531 15T518 -1T489 -8Q451 -8 434 14T422 78Q389 34 347 11T248 -12Q210 -12 175 1T112 41T69 107T52 199Q52 254 71 306T124 398T206 463T312 488Q373 488 417 462T488 391L525 566H430Q399 566 399 584Q399 592 402 603T411 620Q418 628 437 628H538L549 677H614L603 628H647ZM256 45Q292 45 324 58T381 94T425 149T451 218L470 305Q450 364 410 397T308 431Q262 431 227 411T167 358T131 284T118 199Q118 162 129 134T160 86T204 56T256 45Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM321 733Q290 733 290 751Q290 759 293 770T302 787Q309 795 328 795H548Q579 795 579 777Q579 769 576 758T567 741Q560 733 541 733H321Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM233 568Q202 568 202 586Q202 594 205 605T214 622Q221 630 240 630H460Q491 630 491 612Q491 604 488 593T479 576Q472 568 453 568H233Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM426 713Q402 713 379 723T336 749T305 784T293 820Q293 831 299 839T318 847Q334 847 358 828T425 756Q472 805 501 826T550 847Q561 847 568 840T575 823Q575 808 562 789T528 753T480 725T426 713Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM348 548Q324 548 301 558T258 584T227 619T215 655Q215 666 221 674T240 682Q256 682 280 663T347 591Q394 640 423 661T472 682Q483 682 490 675T497 658Q497 643 484 624T450 588T402 560T348 548Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM425 714Q371 714 371 755Q371 779 384 798T427 818Q481 818 481 777Q481 753 468 734T425 714Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM358 549Q304 549 304 590Q304 614 317 633T360 653Q414 653 414 612Q414 588 401 569T358 549Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H489Q429 -33 409 -60T389 -108Q389 -123 398 -132T422 -141Q444 -141 459 -123Q469 -125 477 -132T485 -153Q485 -173 467 -186T417 -199Q377 -199 352 -177T326 -120Q326 -88 350 -57T425 0H43L172 606Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q403 26 372 11Q312 -22 293 -48T273 -96Q273 -111 282 -120T306 -129Q328 -129 343 -111Q353 -113 361 -120T369 -141Q369 -161 351 -174T301 -187Q261 -187 236 -165T210 -108Q210 -82 224 -58T270 -11Q265 -12 261 -12T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="610" d="M172 606Q176 625 187 636T219 647H634Q633 646 633 643Q632 641 632 638T631 630Q629 622 627 610T623 592Q621 586 621 586V585H236L188 362H501Q501 361 500 358Q498 353 497 346L489 309Q488 306 488 304V302H175L124 62H514Q514 61 513 58Q511 53 510 45L502 7Q501 4 501 1V0H43L172 606ZM411 698Q410 698 404 703T388 718T369 738T349 762Q327 791 319 809T310 839Q310 853 317 860T334 868Q350 868 367 843T419 756L470 809Q496 836 517 852T552 868Q562 868 566 863T571 851Q571 834 554 814T504 765Q487 751 471 739T442 718T421 704T411 698Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="539" d="M252 -12Q201 -12 163 2T98 43T59 104T45 182Q45 246 68 302T131 399T226 464T345 488Q426 488 470 454T515 362Q515 326 502 298T457 251T372 221T238 210Q185 210 112 216Q111 208 111 201T111 186Q111 123 147 84T254 44Q310 44 348 64T415 123Q416 124 421 123T433 118T444 108T449 92Q449 81 445 72T427 50Q395 18 353 3T252 -12ZM342 431Q304 431 271 419T209 386T160 335T126 271Q156 267 182 266T232 265Q296 265 338 273T405 294T439 325T449 361Q449 387 423 409T342 431ZM335 533Q334 533 328 538T312 553T293 573T273 597Q251 626 243 644T234 674Q234 688 241 695T258 703Q274 703 291 678T343 591L394 644Q420 671 441 687T476 703Q486 703 490 698T495 686Q495 669 478 649T428 600Q411 586 395 574T366 553T345 539T335 533Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="740" d="M368 -12Q303 -12 250 6T159 60T99 147T78 264Q78 342 105 413T180 539T296 626T443 659Q517 659 568 636T649 584Q666 567 675 548T685 515Q685 499 678 490T663 477T647 473T639 473Q620 527 568 563T442 599Q378 599 325 573T232 501T172 397T150 271Q150 217 166 175T210 105T279 62T369 47Q468 47 529 108Q543 122 554 136T574 168T591 210T606 269H392Q392 270 393 273Q394 275 394 277T396 285Q398 293 400 305T404 323Q404 325 404 326T405 328V330H633Q656 330 665 318T670 285L609 -1Q609 -4 595 -4Q576 -4 566 11T563 65L565 74Q541 39 491 14T368 -12ZM330 714Q320 714 316 719T311 731Q311 748 328 768T378 817Q394 831 410 843T440 864T461 878T471 884Q472 884 478 879T493 864T512 844T533 820Q555 791 563 773T572 743Q572 729 565 722T548 714Q535 714 523 728T492 775L463 826L412 773Q386 746 365 730T330 714Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="603" d="M229 -200Q165 -200 121 -182T51 -138Q33 -120 25 -104T17 -74Q17 -58 24 -48T40 -33T56 -27T64 -27Q78 -79 118 -110T235 -141Q301 -141 345 -103T407 14L423 88Q391 46 348 24T249 1Q211 1 176 14T112 54T68 120T51 212Q51 263 71 312T128 401T213 464T319 488Q379 488 423 461T489 393L507 476H571L471 8Q450 -91 390 -145T229 -200ZM257 58Q291 58 322 70T380 103T424 153T451 219L471 308Q454 365 413 398T315 431Q271 431 235 412T172 361T132 291T117 212Q117 175 128 147T159 99T204 69T257 58ZM239 549Q229 549 225 554T220 566Q220 583 237 603T287 652Q303 666 319 678T349 699T370 713T380 719Q381 719 387 714T402 699T421 679T442 655Q464 626 472 608T481 578Q481 564 474 557T457 549Q444 549 432 563T401 610L372 661L321 608Q295 581 274 565T239 549Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="740" d="M368 -12Q303 -12 250 6T159 60T99 147T78 264Q78 342 105 413T180 539T296 626T443 659Q517 659 568 636T649 584Q666 567 675 548T685 515Q685 499 678 490T663 477T647 473T639 473Q620 527 568 563T442 599Q378 599 325 573T232 501T172 397T150 271Q150 217 166 175T210 105T279 62T369 47Q468 47 529 108Q543 122 554 136T574 168T591 210T606 269H392Q392 270 393 273Q394 275 394 277T396 285Q398 293 400 305T404 323Q404 325 404 326T405 328V330H633Q656 330 665 318T670 285L609 -1Q609 -4 595 -4Q576 -4 566 11T563 65L565 74Q541 39 491 14T368 -12ZM449 713Q425 713 402 723T359 749T328 784T316 820Q316 831 322 839T341 847Q357 847 381 828T448 756Q495 805 524 826T573 847Q584 847 591 840T598 823Q598 808 585 789T551 753T503 725T449 713Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="603" d="M229 -200Q165 -200 121 -182T51 -138Q33 -120 25 -104T17 -74Q17 -58 24 -48T40 -33T56 -27T64 -27Q78 -79 118 -110T235 -141Q301 -141 345 -103T407 14L423 88Q391 46 348 24T249 1Q211 1 176 14T112 54T68 120T51 212Q51 263 71 312T128 401T213 464T319 488Q379 488 423 461T489 393L507 476H571L471 8Q450 -91 390 -145T229 -200ZM257 58Q291 58 322 70T380 103T424 153T451 219L471 308Q454 365 413 398T315 431Q271 431 235 412T172 361T132 291T117 212Q117 175 128 147T159 99T204 69T257 58ZM357 548Q333 548 310 558T267 584T236 619T224 655Q224 666 230 674T249 682Q265 682 289 663T356 591Q403 640 432 661T481 682Q492 682 499 675T506 658Q506 643 493 624T459 588T411 560T357 548Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="740" d="M368 -12Q303 -12 250 6T159 60T99 147T78 264Q78 342 105 413T180 539T296 626T443 659Q517 659 568 636T649 584Q666 567 675 548T685 515Q685 499 678 490T663 477T647 473T639 473Q620 527 568 563T442 599Q378 599 325 573T232 501T172 397T150 271Q150 217 166 175T210 105T279 62T369 47Q468 47 529 108Q543 122 554 136T574 168T591 210T606 269H392Q392 270 393 273Q394 275 394 277T396 285Q398 293 400 305T404 323Q404 325 404 326T405 328V330H633Q656 330 665 318T670 285L609 -1Q609 -4 595 -4Q576 -4 566 11T563 65L565 74Q541 39 491 14T368 -12ZM447 714Q393 714 393 755Q393 779 406 798T449 818Q503 818 503 777Q503 753 490 734T447 714Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="603" d="M229 -200Q165 -200 121 -182T51 -138Q33 -120 25 -104T17 -74Q17 -58 24 -48T40 -33T56 -27T64 -27Q78 -79 118 -110T235 -141Q301 -141 345 -103T407 14L423 88Q391 46 348 24T249 1Q211 1 176 14T112 54T68 120T51 212Q51 263 71 312T128 401T213 464T319 488Q379 488 423 461T489 393L507 476H571L471 8Q450 -91 390 -145T229 -200ZM257 58Q291 58 322 70T380 103T424 153T451 219L471 308Q454 365 413 398T315 431Q271 431 235 412T172 361T132 291T117 212Q117 175 128 147T159 99T204 69T257 58ZM355 549Q301 549 301 590Q301 614 314 633T357 653Q411 653 411 612Q411 588 398 569T355 549Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="740" d="M368 -12Q303 -12 250 6T159 60T99 147T78 264Q78 342 105 413T180 539T296 626T443 659Q517 659 568 636T649 584Q666 567 675 548T685 515Q685 499 678 490T663 477T647 473T639 473Q620 527 568 563T442 599Q378 599 325 573T232 501T172 397T150 271Q150 217 166 175T210 105T279 62T369 47Q468 47 529 108Q543 122 554 136T574 168T591 210T606 269H392Q392 270 393 273Q394 275 394 277T396 285Q398 293 400 305T404 323Q404 325 404 326T405 328V330H633Q656 330 665 318T670 285L609 -1Q609 -4 595 -4Q576 -4 566 11T563 65L565 74Q541 39 491 14T368 -12ZM244 -234Q241 -237 235 -236T224 -230T216 -222T214 -214L321 -47Q322 -46 331 -47T350 -52T370 -63T379 -83Q379 -105 357 -128L244 -234Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="603" d="M229 -200Q165 -200 121 -182T51 -138Q33 -120 25 -104T17 -74Q17 -58 24 -48T40 -33T56 -27T64 -27Q78 -79 118 -110T235 -141Q301 -141 345 -103T407 14L423 88Q391 46 348 24T249 1Q211 1 176 14T112 54T68 120T51 212Q51 263 71 312T128 401T213 464T319 488Q379 488 423 461T489 393L507 476H571L471 8Q450 -91 390 -145T229 -200ZM257 58Q291 58 322 70T380 103T424 153T451 219L471 308Q454 365 413 398T315 431Q271 431 235 412T172 361T132 291T117 212Q117 175 128 147T159 99T204 69T257 58ZM401 687Q404 690 410 689T421 683T429 675T431 667L324 500Q323 499 314 500T295 505T275 516T266 536Q266 558 288 581L401 687Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="701" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L187 355H564L620 620Q624 636 631 643T654 651H663Q678 651 684 642T687 616L556 0H489L551 293H174L111 0H44L175 620ZM321 714Q311 714 307 719T302 731Q302 748 319 768T369 817Q385 831 401 843T431 864T452 878T462 884Q463 884 469 879T484 864T503 844T524 820Q546 791 554 773T563 743Q563 729 556 722T539 714Q526 714 514 728T483 775L454 826L403 773Q377 746 356 730T321 714Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L171 650Q175 667 182 674T201 681H209Q222 681 231 675T236 650L182 397Q208 440 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8ZM101 714Q91 714 87 719T82 731Q82 748 99 768T149 817Q165 831 181 843T211 864T232 878T242 884Q243 884 249 879T264 864T283 844T304 820Q326 791 334 773T343 743Q343 729 336 722T319 714Q306 714 294 728T263 775L234 826L183 773Q157 746 136 730T101 714Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="701" d="M101 469Q70 469 70 487Q70 495 73 505T82 521Q89 529 108 529H155L175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L223 529H600L620 620Q624 636 631 643T654 651H663Q678 651 684 642T687 616L668 529H709Q740 529 740 511Q740 503 737 493T728 477Q721 469 702 469H655L556 0H489L551 293H174L111 0H44L143 469H101ZM564 355L588 469H211L187 355H564Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="571" d="M109 566Q78 566 78 584Q78 592 81 603T90 620Q97 628 116 628H166L171 650Q175 667 182 674T201 681H209Q222 681 231 675T236 650L231 628H326Q357 628 357 610Q357 602 354 591T345 574Q338 566 319 566H218L182 397Q208 440 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L153 566H109Z" />
+<glyph unicode="&#x128;" glyph-name="Itilde" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM309 673Q285 673 247 694T170 753Q164 740 158 727T145 704T131 687T116 680Q104 680 99 686T93 700Q93 717 100 734T118 765T143 787T173 796Q197 796 235 775T312 716Q318 729 324 742T337 765T351 782T366 789Q378 789 383 783T389 769Q389 752 382 735T364 704T339 682T309 673Z" />
+<glyph unicode="&#x129;" glyph-name="itilde" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM260 548Q236 548 198 569T121 628Q115 615 109 602T96 579T82 562T67 555Q55 555 50 561T44 575Q44 592 51 609T69 640T94 662T124 671Q148 671 186 650T263 591Q269 604 275 617T288 640T302 657T317 664Q329 664 334 658T340 644Q340 627 333 610T315 579T290 557T260 548Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM141 733Q110 733 110 751Q110 759 113 770T122 787Q129 795 148 795H368Q399 795 399 777Q399 769 396 758T387 741Q380 733 361 733H141Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM82 568Q51 568 51 586Q51 594 54 605T63 622Q70 630 89 630H309Q340 630 340 612Q340 604 337 593T328 576Q321 568 302 568H82Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H111Q79 -17 59 -32T26 -62T10 -88T5 -111Q5 -126 14 -135T38 -144Q60 -144 75 -126Q85 -128 93 -135T101 -156Q101 -176 83 -189T33 -202Q-7 -202 -32 -180T-58 -123Q-58 -88 -31 -55T56 4L187 619Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="243" d="M173 37Q173 37 173 32T170 19T161 5T142 -6Q113 -22 95 -37T65 -65T50 -90T46 -112Q46 -127 55 -136T79 -145Q101 -145 116 -127Q126 -129 134 -136T142 -157Q142 -177 124 -190T74 -203Q34 -203 9 -181T-17 -124Q-17 -91 8 -59T88 -1Q42 20 60 100L140 476H205L125 101Q117 66 126 52T173 37ZM207 596Q163 596 163 629Q163 649 174 664T209 680Q253 680 253 647Q253 627 242 612T207 596Z" />
+<glyph unicode="&#x130;" glyph-name="Idotaccent" horiz-adv-x="276" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM248 714Q194 714 194 755Q194 779 207 798T250 818Q304 818 304 777Q304 753 291 734T248 714Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8Z" />
+<glyph unicode="&#x132;" glyph-name="IJ" horiz-adv-x="747" d="M187 619Q194 651 221 651H230Q245 651 251 642T253 615L122 0H55L187 619ZM451 -12Q406 -12 375 2T323 36Q309 50 304 60T299 80Q299 92 305 100T318 114T331 120T338 121Q353 88 383 68T452 47Q508 47 538 81T586 194L683 647H750L652 185Q630 84 582 36T451 -12Z" />
+<glyph unicode="&#x133;" glyph-name="ij" horiz-adv-x="486" d="M126 -8Q81 -8 65 19T60 100L140 476H205L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM207 596Q163 596 163 629Q163 649 174 664T209 680Q253 680 253 647Q253 627 242 612T207 596ZM192 -203Q161 -203 141 -195T109 -176Q95 -162 95 -148Q95 -139 99 -131T108 -118T117 -109T122 -107Q133 -123 152 -134T194 -145Q223 -145 241 -128T269 -62L383 476H448L331 -74Q317 -142 280 -172T192 -203ZM450 596Q406 596 406 629Q406 649 417 664T452 680Q496 680 496 647Q496 627 485 612T450 596Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="471" d="M175 -12Q130 -12 99 2T47 36Q33 50 28 60T23 80Q23 92 29 100T42 114T55 120T62 121Q77 88 107 68T176 47Q232 47 262 81T310 194L407 647H474L376 185Q354 84 306 36T175 -12ZM345 714Q335 714 331 719T326 731Q326 748 343 768T393 817Q409 831 425 843T455 864T476 878T486 884Q487 884 493 879T508 864T527 844T548 820Q570 791 578 773T587 743Q587 729 580 722T563 714Q550 714 538 728T507 775L478 826L427 773Q401 746 380 730T345 714Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="243" d="M-51 -203Q-82 -203 -102 -195T-134 -176Q-148 -162 -148 -148Q-148 -139 -144 -131T-135 -118T-126 -109T-121 -107Q-110 -123 -91 -134T-49 -145Q-20 -145 -2 -128T26 -62L140 476H205L88 -74Q74 -142 37 -172T-51 -203ZM81 549Q71 549 67 554T62 566Q62 583 79 603T129 652Q145 666 161 678T191 699T212 713T222 719Q223 719 229 714T244 699T263 679T284 655Q306 626 314 608T323 578Q323 564 316 557T299 549Q286 549 274 563T243 610L214 661L163 608Q137 581 116 565T81 549Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="580" d="M467 -5Q454 -5 442 0T417 17T389 47T354 93L181 328L110 0H43L175 620Q179 637 186 644T209 651H218Q250 651 242 616L185 346L570 659Q571 660 581 656T597 641T599 615T565 575L256 328L417 116Q434 93 446 79T470 56T491 44T515 39Q516 39 516 32T512 17T498 2T467 -5ZM138 -234Q135 -237 129 -236T118 -230T110 -222T108 -214L215 -47Q216 -46 225 -47T244 -52T264 -63T273 -83Q273 -105 251 -128L138 -234Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="475" d="M364 -8Q349 -8 338 -4T313 11T285 44T246 100L150 245L98 0H33L177 677H242L153 260L436 488Q437 489 447 485T462 470T464 444T431 406L224 242L309 117Q325 92 337 78T360 57T383 48T412 45Q413 45 413 37T409 19T395 1T364 -8ZM93 -234Q90 -237 84 -236T73 -230T65 -222T63 -214L170 -47Q171 -46 180 -47T199 -52T219 -63T228 -83Q228 -105 206 -128L93 -234Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="534" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L124 62H432Q463 62 463 45Q463 37 459 25T450 7Q444 0 424 0H43L175 620ZM204 716Q203 718 216 732T250 765T297 806T348 842Q372 857 394 857Q407 857 415 849T423 829Q423 805 382 781Q354 765 324 752T267 730T223 718T204 716Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L183 677H248L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM194 716Q193 718 206 732T240 765T287 806T338 842Q362 857 384 857Q397 857 405 849T413 829Q413 805 372 781Q344 765 314 752T257 730T213 718T194 716Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="534" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L124 62H432Q463 62 463 45Q463 37 459 25T450 7Q444 0 424 0H43L175 620ZM141 -234Q138 -237 132 -236T121 -230T113 -222T111 -214L218 -47Q219 -46 228 -47T247 -52T267 -63T276 -83Q276 -105 254 -128L141 -234Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="243" d="M126 -8Q81 -8 65 19T60 100L183 677H248L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM-5 -234Q-8 -237 -14 -236T-25 -230T-33 -222T-35 -214L72 -47Q73 -46 82 -47T101 -52T121 -63T130 -83Q130 -105 108 -128L-5 -234Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="635" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L124 62H432Q463 62 463 45Q463 37 459 25T450 7Q444 0 424 0H43L175 620ZM512 422Q510 419 504 420T493 425T484 433T482 442L598 662Q599 663 608 663T629 660T650 649T660 629Q660 618 656 608T641 584L512 422Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="404" d="M126 -8Q81 -8 65 19T60 100L183 677H248L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM281 437Q279 434 273 435T262 440T253 448T251 457L367 677Q368 678 377 678T398 675T419 664T429 644Q429 633 425 623T410 599L281 437Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="626" d="M175 620Q179 636 186 643T209 651H218Q233 651 239 642T242 616L124 62H432Q463 62 463 45Q463 37 459 25T450 7Q444 0 424 0H43L175 620ZM559 276Q536 276 524 285T511 310Q511 346 527 362Q540 375 564 375Q587 375 599 366T612 341Q612 305 596 289Q583 276 559 276Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="402" d="M126 -8Q81 -8 65 19T60 100L183 677H248L125 101Q117 66 126 52T173 37Q173 37 173 30T169 15T155 -1T126 -8ZM310 195Q287 195 275 204T262 229Q262 265 278 281Q291 294 315 294Q338 294 350 285T363 260Q363 224 347 208Q334 195 310 195Z" />
+<glyph unicode="&#x141;" glyph-name="CR" horiz-adv-x="590" d="M126 0Q103 0 94 12T89 45L129 234L49 211Q47 209 41 219T35 241Q35 253 44 263T80 282L143 300L211 620Q215 636 222 643T245 651H254Q269 651 275 642T278 616L215 320L425 380Q428 381 433 372T439 350Q439 338 429 328T394 309L201 254L160 62H468Q499 62 499 45Q499 37 495 25T486 7Q480 0 460 0H126Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="358" d="M180 -8Q135 -8 119 19T114 100L145 244L49 217Q47 216 41 227T35 248Q35 259 44 269T79 287L159 310L231 650Q235 667 242 674T261 681H269Q282 681 291 675T296 650L228 329L332 359Q334 360 340 349T346 328Q346 317 337 307T302 289L214 264L179 101Q171 66 180 52T227 37Q227 37 227 30T223 15T209 -1T180 -8Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="709" d="M172 605Q177 626 189 638T220 651H237Q254 651 262 642T280 615L522 101L639 647H702L572 37Q568 19 557 8T528 -4H521Q506 -4 497 5T479 32L227 566L106 0H43L172 605ZM399 716Q398 718 411 732T445 765T492 806T543 842Q567 857 589 857Q602 857 610 849T618 829Q618 805 577 781Q549 765 519 752T462 730T418 718T399 716Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L182 398Q209 441 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8ZM277 551Q276 553 289 567T323 600T370 641T421 677Q445 692 467 692Q480 692 488 684T496 664Q496 640 455 616Q427 600 397 587T340 565T296 553T277 551Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="709" d="M172 605Q177 626 189 638T220 651H237Q254 651 262 642T280 615L522 101L639 647H702L572 37Q568 19 557 8T528 -4H521Q506 -4 497 5T479 32L227 566L106 0H43L172 605ZM204 -234Q201 -237 195 -236T184 -230T176 -222T174 -214L281 -47Q282 -46 291 -47T310 -52T330 -63T339 -83Q339 -105 317 -128L204 -234Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L182 398Q209 441 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8ZM159 -234Q156 -237 150 -236T139 -230T131 -222T129 -214L236 -47Q237 -46 246 -47T265 -52T285 -63T294 -83Q294 -105 272 -128L159 -234Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="709" d="M172 605Q177 626 189 638T220 651H237Q254 651 262 642T280 615L522 101L639 647H702L572 37Q568 19 557 8T528 -4H521Q506 -4 497 5T479 32L227 566L106 0H43L172 605ZM450 698Q449 698 443 703T427 718T408 738T388 762Q366 791 358 809T349 839Q349 853 356 860T373 868Q389 868 406 843T458 756L509 809Q535 836 556 852T591 868Q601 868 605 863T610 851Q610 834 593 814T543 765Q526 751 510 739T481 718T460 704T450 698Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="571" d="M453 -8Q423 -8 406 11T389 63Q389 84 396 118T415 190Q426 228 433 261T440 315Q440 367 412 397T331 427Q276 427 237 399T164 312L98 0H33L135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L182 398Q209 441 250 464T340 488Q379 488 410 476T462 442T495 391T507 327Q507 301 498 262T478 181Q467 144 461 116T454 73Q454 53 464 46T496 36Q497 36 497 29T493 14T480 -1T453 -8ZM352 533Q351 533 345 538T329 553T310 573T290 597Q268 626 260 644T251 674Q251 688 258 695T275 703Q291 703 308 678T360 591L411 644Q437 671 458 687T493 703Q503 703 507 698T512 686Q512 669 495 649T445 600Q428 586 412 574T383 553T362 539T352 533Z" />
+<glyph unicode="&#x149;" glyph-name="napostrophe" horiz-adv-x="807" d="M113 422Q111 419 105 420T94 425T85 433T83 442L199 662Q200 663 209 663T230 660T251 649T261 629Q261 618 257 608T242 584L113 422ZM689 -8Q659 -8 642 11T625 63Q625 84 632 118T651 190Q662 228 669 261T676 315Q676 367 648 397T567 427Q512 427 473 399T400 312L334 0H269L371 477Q371 480 385 480Q394 480 402 477T416 467T423 446T421 411L418 398Q445 441 486 464T576 488Q615 488 646 476T698 442T731 391T743 327Q743 301 734 262T714 181Q703 144 697 116T690 73Q690 53 700 46T732 36Q733 36 733 29T729 14T716 -1T689 -8Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM341 733Q310 733 310 751Q310 759 313 770T322 787Q329 795 348 795H568Q599 795 599 777Q599 769 596 758T587 741Q580 733 561 733H341Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM225 568Q194 568 194 586Q194 594 197 605T206 622Q213 630 232 630H452Q483 630 483 612Q483 604 480 593T471 576Q464 568 445 568H225Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM466 713Q442 713 419 723T376 749T345 784T333 820Q333 831 339 839T358 847Q374 847 398 828T465 756Q512 805 541 826T590 847Q601 847 608 840T615 823Q615 808 602 789T568 753T520 725T466 713Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM333 548Q309 548 286 558T243 584T212 619T200 655Q200 666 206 674T225 682Q241 682 265 663T332 591Q379 640 408 661T457 682Q468 682 475 675T482 658Q482 643 469 624T435 588T387 560T333 548Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="766" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q503 659 556 638T646 579T704 490T724 379Q724 293 694 222T613 98T498 18T366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48ZM501 716Q499 718 509 732T538 765T579 805T626 844Q650 862 672 862Q684 862 693 855T702 835Q702 810 665 785Q640 768 612 754T560 731T520 718T501 716ZM322 716Q320 718 330 732T359 765T400 805T447 844Q471 862 493 862Q505 862 514 855T523 835Q523 810 486 785Q461 768 433 754T381 731T341 718T322 716Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="584" d="M268 -12Q223 -12 183 4T113 49T66 119T49 209Q49 264 69 315T125 404T210 465T317 488Q362 488 402 472T472 427T519 357T536 267Q536 211 516 161T460 72T375 11T268 -12ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM370 551Q368 553 378 567T407 600T448 640T495 679Q519 697 541 697Q553 697 562 690T571 670Q571 645 534 620Q509 603 481 589T429 566T389 553T370 551ZM191 551Q189 553 199 567T228 600T269 640T316 679Q340 697 362 697Q374 697 383 690T392 670Q392 645 355 620Q330 603 302 589T250 566T210 553T191 551Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1055" d="M366 -11Q299 -11 246 10T156 69T98 158T78 269Q78 355 108 426T189 550T304 630T436 659Q483 659 524 647H1064Q1063 646 1063 643Q1062 641 1062 638T1061 630Q1059 622 1057 610T1053 592Q1052 589 1052 586Q1052 585 1051 585H636Q678 547 701 495T724 379V362H933Q932 361 932 358Q931 356 931 353T930 346Q928 338 926 326T922 309Q921 306 921 304Q921 303 920 302H717Q702 226 662 165T567 62H945Q944 61 944 58Q943 56 943 53T942 45Q940 37 938 25T934 7Q933 4 933 1Q933 0 932 0H445Q404 -11 366 -11ZM373 48Q424 48 474 72T563 139T627 242T652 374Q652 420 638 461T595 533T525 582T429 600Q378 600 328 576T239 509T175 406T150 274Q150 227 164 186T207 115T277 66T373 48Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="958" d="M518 343Q556 409 621 448T764 488Q845 488 889 454T934 362Q934 326 921 298T876 251T791 221T657 210Q604 210 531 216Q530 208 530 201T530 186Q530 123 566 84T673 44Q729 44 767 64T834 123Q835 124 840 123T852 118T863 108T868 92Q868 81 864 72T846 50Q814 18 772 3T671 -12Q598 -12 550 22T484 110Q449 56 393 22T266 -12Q221 -12 182 3T113 47T66 117T49 209Q49 264 69 315T124 404T208 465T314 488Q386 488 441 450T518 343ZM272 45Q314 45 350 63T412 111T454 181T470 265Q470 301 458 331T425 384T375 418T313 431Q271 431 235 413T173 365T131 295T115 211Q115 175 127 145T160 92T210 58T272 45ZM761 431Q723 431 690 419T628 386T579 335T545 271Q575 267 601 266T651 265Q715 265 757 273T824 294T858 325T868 361Q868 387 842 409T761 431Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="601" d="M337 272L426 118Q448 79 467 61T521 43Q522 43 522 36T518 19T504 3T473 -5Q460 -5 448 -1T423 13T396 43T365 92L264 272H169L110 0H43L172 606Q178 647 219 647H377Q482 647 537 607T592 488Q592 385 527 329T348 272H337ZM350 330Q431 330 476 369T521 481Q521 533 483 560T373 587H236L181 330H350ZM317 716Q316 718 329 732T363 765T410 806T461 842Q485 857 507 857Q520 857 528 849T536 829Q536 805 495 781Q467 765 437 752T380 730T336 718T317 716Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="372" d="M135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L181 392Q206 440 238 464T315 488Q343 488 363 476T384 443Q384 431 380 423T372 409T363 401T358 400Q348 411 334 418T296 426Q267 426 243 409T200 360T165 287T139 194L98 0H33L135 477ZM186 551Q185 553 198 567T232 600T279 641T330 677Q354 692 376 692Q389 692 397 684T405 664Q405 640 364 616Q336 600 306 587T249 565T205 553T186 551Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="601" d="M337 272L426 118Q448 79 467 61T521 43Q522 43 522 36T518 19T504 3T473 -5Q460 -5 448 -1T423 13T396 43T365 92L264 272H169L110 0H43L172 606Q178 647 219 647H377Q482 647 537 607T592 488Q592 385 527 329T348 272H337ZM350 330Q431 330 476 369T521 481Q521 533 483 560T373 587H236L181 330H350ZM154 -234Q151 -237 145 -236T134 -230T126 -222T124 -214L231 -47Q232 -46 241 -47T260 -52T280 -63T289 -83Q289 -105 267 -128L154 -234Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="372" d="M135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L181 392Q206 440 238 464T315 488Q343 488 363 476T384 443Q384 431 380 423T372 409T363 401T358 400Q348 411 334 418T296 426Q267 426 243 409T200 360T165 287T139 194L98 0H33L135 477ZM-40 -234Q-43 -237 -49 -236T-60 -230T-68 -222T-70 -214L37 -47Q38 -46 47 -47T66 -52T86 -63T95 -83Q95 -105 73 -128L-40 -234Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="601" d="M337 272L426 118Q448 79 467 61T521 43Q522 43 522 36T518 19T504 3T473 -5Q460 -5 448 -1T423 13T396 43T365 92L264 272H169L110 0H43L172 606Q178 647 219 647H377Q482 647 537 607T592 488Q592 385 527 329T348 272H337ZM350 330Q431 330 476 369T521 481Q521 533 483 560T373 587H236L181 330H350ZM378 698Q377 698 371 703T355 718T336 738T316 762Q294 791 286 809T277 839Q277 853 284 860T301 868Q317 868 334 843T386 756L437 809Q463 836 484 852T519 868Q529 868 533 863T538 851Q538 834 521 814T471 765Q454 751 438 739T409 718T388 704T378 698Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="372" d="M135 477Q135 480 149 480Q158 480 166 477T180 467T187 446T185 411L181 392Q206 440 238 464T315 488Q343 488 363 476T384 443Q384 431 380 423T372 409T363 401T358 400Q348 411 334 418T296 426Q267 426 243 409T200 360T165 287T139 194L98 0H33L135 477ZM252 533Q251 533 245 538T229 553T210 573T190 597Q168 626 160 644T151 674Q151 688 158 695T175 703Q191 703 208 678T260 591L311 644Q337 671 358 687T393 703Q403 703 407 698T412 686Q412 669 395 649T345 600Q328 586 312 574T283 553T262 539T252 533Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="567" d="M269 -12Q201 -12 149 9T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q93 113 143 80T273 47Q351 47 388 79T426 168Q426 193 415 212T385 245T340 272T284 296Q253 309 223 323T169 359T131 409T116 480Q116 517 131 550T174 607T243 645T333 659Q401 659 448 640T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q492 541 448 570T330 600Q298 600 271 592T225 568T196 532T185 487Q185 459 195 439T225 404T272 376T331 350Q361 337 390 323T443 289T480 242T495 174Q495 132 480 98T437 40T366 2T269 -12ZM311 716Q310 718 323 732T357 765T404 806T455 842Q479 857 501 857Q514 857 522 849T530 829Q530 805 489 781Q461 765 431 752T374 730T330 718T311 716Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="481" d="M222 -12Q163 -12 117 6T45 50Q34 61 28 72T21 95Q21 106 26 113T38 126T50 132T57 133Q80 95 122 69T226 43Q282 43 312 65T342 124Q342 157 312 174T228 209Q202 218 177 228T131 254T97 292T84 347Q84 378 97 403T134 448T190 477T262 488Q316 488 356 472T420 432Q444 408 444 384Q444 373 439 366T426 354T413 349T406 350Q387 387 347 410T257 433Q202 433 175 409T147 355Q147 320 179 302T265 264Q290 255 315 245T360 219T392 183T405 132Q405 66 357 27T222 -12ZM236 551Q235 553 248 567T282 600T329 641T380 677Q404 692 426 692Q439 692 447 684T455 664Q455 640 414 616Q386 600 356 587T299 565T255 553T236 551Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="567" d="M269 -12Q201 -12 149 9T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q93 113 143 80T273 47Q351 47 388 79T426 168Q426 193 415 212T385 245T340 272T284 296Q253 309 223 323T169 359T131 409T116 480Q116 517 131 550T174 607T243 645T333 659Q401 659 448 640T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q492 541 448 570T330 600Q298 600 271 592T225 568T196 532T185 487Q185 459 195 439T225 404T272 376T331 350Q361 337 390 323T443 289T480 242T495 174Q495 132 480 98T437 40T366 2T269 -12ZM264 714Q254 714 250 719T245 731Q245 748 262 768T312 817Q328 831 344 843T374 864T395 878T405 884Q406 884 412 879T427 864T446 844T467 820Q489 791 497 773T506 743Q506 729 499 722T482 714Q469 714 457 728T426 775L397 826L346 773Q320 746 299 730T264 714Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="481" d="M222 -12Q163 -12 117 6T45 50Q34 61 28 72T21 95Q21 106 26 113T38 126T50 132T57 133Q80 95 122 69T226 43Q282 43 312 65T342 124Q342 157 312 174T228 209Q202 218 177 228T131 254T97 292T84 347Q84 378 97 403T134 448T190 477T262 488Q316 488 356 472T420 432Q444 408 444 384Q444 373 439 366T426 354T413 349T406 350Q387 387 347 410T257 433Q202 433 175 409T147 355Q147 320 179 302T265 264Q290 255 315 245T360 219T392 183T405 132Q405 66 357 27T222 -12ZM182 549Q172 549 168 554T163 566Q163 583 180 603T230 652Q246 666 262 678T292 699T313 713T323 719Q324 719 330 714T345 699T364 679T385 655Q407 626 415 608T424 578Q424 564 417 557T400 549Q387 549 375 563T344 610L315 661L264 608Q238 581 217 565T182 549Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="567" d="M269 -12Q201 -12 149 9T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q93 113 143 80T273 47Q351 47 388 79T426 168Q426 193 415 212T385 245T340 272T284 296Q253 309 223 323T169 359T131 409T116 480Q116 517 131 550T174 607T243 645T333 659Q401 659 448 640T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q492 541 448 570T330 600Q298 600 271 592T225 568T196 532T185 487Q185 459 195 439T225 404T272 376T331 350Q361 337 390 323T443 289T480 242T495 174Q495 132 480 98T437 40T366 2T269 -12ZM143 -234Q140 -237 134 -236T123 -230T115 -222T113 -214L220 -47Q221 -46 230 -47T249 -52T269 -63T278 -83Q278 -105 256 -128L143 -234Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="481" d="M222 -12Q163 -12 117 6T45 50Q34 61 28 72T21 95Q21 106 26 113T38 126T50 132T57 133Q80 95 122 69T226 43Q282 43 312 65T342 124Q342 157 312 174T228 209Q202 218 177 228T131 254T97 292T84 347Q84 378 97 403T134 448T190 477T262 488Q316 488 356 472T420 432Q444 408 444 384Q444 373 439 366T426 354T413 349T406 350Q387 387 347 410T257 433Q202 433 175 409T147 355Q147 320 179 302T265 264Q290 255 315 245T360 219T392 183T405 132Q405 66 357 27T222 -12ZM100 -234Q97 -237 91 -236T80 -230T72 -222T70 -214L177 -47Q178 -46 187 -47T206 -52T226 -63T235 -83Q235 -105 213 -128L100 -234Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="567" d="M269 -12Q201 -12 149 9T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q93 113 143 80T273 47Q351 47 388 79T426 168Q426 193 415 212T385 245T340 272T284 296Q253 309 223 323T169 359T131 409T116 480Q116 517 131 550T174 607T243 645T333 659Q401 659 448 640T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q492 541 448 570T330 600Q298 600 271 592T225 568T196 532T185 487Q185 459 195 439T225 404T272 376T331 350Q361 337 390 323T443 289T480 242T495 174Q495 132 480 98T437 40T366 2T269 -12ZM363 703Q362 703 356 708T340 723T321 743T301 767Q279 796 271 814T262 844Q262 858 269 865T286 873Q302 873 319 848T371 761L422 814Q448 841 469 857T504 873Q514 873 518 868T523 856Q523 839 506 819T456 770Q439 756 423 744T394 723T373 709T363 703Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="481" d="M222 -12Q163 -12 117 6T45 50Q34 61 28 72T21 95Q21 106 26 113T38 126T50 132T57 133Q80 95 122 69T226 43Q282 43 312 65T342 124Q342 157 312 174T228 209Q202 218 177 228T131 254T97 292T84 347Q84 378 97 403T134 448T190 477T262 488Q316 488 356 472T420 432Q444 408 444 384Q444 373 439 366T426 354T413 349T406 350Q387 387 347 410T257 433Q202 433 175 409T147 355Q147 320 179 302T265 264Q290 255 315 245T360 219T392 183T405 132Q405 66 357 27T222 -12ZM290 533Q289 533 283 538T267 553T248 573T228 597Q206 626 198 644T189 674Q189 688 196 695T213 703Q229 703 246 678T298 591L349 644Q375 671 396 687T431 703Q441 703 445 698T450 686Q450 669 433 649T383 600Q366 586 350 574T321 553T300 539T290 533Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="618" d="M350 585H151Q120 585 120 602Q120 610 124 622T133 640Q139 647 159 647H628Q659 647 659 630Q659 622 655 610T646 592Q640 585 620 585H418L293 0H226L350 585ZM143 -234Q140 -237 134 -236T123 -230T115 -222T113 -214L220 -47Q221 -46 230 -47T249 -52T269 -63T278 -83Q278 -105 256 -128L143 -234Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="375" d="M208 -12Q141 -12 108 31T93 161L147 418H79Q79 419 80 422Q80 424 80 426T82 434L90 469Q90 471 90 472T91 474V476H159L169 523Q177 560 189 575T222 590Q232 590 239 587T247 582L224 476H372Q371 475 371 472Q370 470 370 467T369 460L361 425Q360 422 360 420V418H212L157 161Q143 97 160 71T218 44Q240 44 258 53T284 73Q286 75 292 66T299 43Q299 26 285 12Q274 1 255 -5T208 -12ZM61 -234Q58 -237 52 -236T41 -230T33 -222T31 -214L138 -47Q139 -46 148 -47T167 -52T187 -63T196 -83Q196 -105 174 -128L61 -234Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="618" d="M350 585H151Q120 585 120 602Q120 610 124 622T133 640Q139 647 159 647H628Q659 647 659 630Q659 622 655 610T646 592Q640 585 620 585H418L293 0H226L350 585ZM399 698Q398 698 392 703T376 718T357 738T337 762Q315 791 307 809T298 839Q298 853 305 860T322 868Q338 868 355 843T407 756L458 809Q484 836 505 852T540 868Q550 868 554 863T559 851Q559 834 542 814T492 765Q475 751 459 739T430 718T409 704T399 698Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="375" d="M208 -12Q141 -12 108 31T93 161L147 418H79Q79 419 80 422Q80 424 80 426T82 434L90 469Q90 471 90 472T91 474V476H159L169 523Q177 560 189 575T222 590Q232 590 239 587T247 582L224 476H372Q371 475 371 472Q370 470 370 467T369 460L361 425Q360 422 360 420V418H212L157 161Q143 97 160 71T218 44Q240 44 258 53T284 73Q286 75 292 66T299 43Q299 26 285 12Q274 1 255 -5T208 -12ZM306 515Q304 512 298 513T287 518T278 526T276 535L392 755Q393 756 402 756T423 753T444 742T454 722Q454 711 450 701T435 677L306 515Z" />
+<glyph unicode="&#x166;" glyph-name="Tbar" horiz-adv-x="618" d="M350 585H151Q120 585 120 602Q120 610 124 622T133 640Q139 647 159 647H628Q659 647 659 630Q659 622 655 610T646 592Q640 585 620 585H418L293 0H226L350 585ZM220 295Q189 295 189 313Q189 321 192 332T201 349Q208 357 227 357H447Q478 357 478 339Q478 331 475 320T466 303Q459 295 440 295H220Z" />
+<glyph unicode="&#x167;" glyph-name="tbar" horiz-adv-x="375" d="M208 -12Q141 -12 108 31T93 161L147 418H79Q79 419 80 422Q80 424 80 426T82 434L90 469Q90 471 90 472T91 474V476H159L169 523Q177 560 189 575T222 590Q232 590 239 587T247 582L224 476H372Q371 475 371 472Q370 470 370 467T369 460L361 425Q360 422 360 420V418H212L157 161Q143 97 160 71T218 44Q240 44 258 53T284 73Q286 75 292 66T299 43Q299 26 285 12Q274 1 255 -5T208 -12ZM70 222Q39 222 39 240Q39 248 42 259T51 276Q58 284 77 284H297Q328 284 328 266Q328 258 325 247T316 230Q309 222 290 222H70Z" />
+<glyph unicode="&#x168;" glyph-name="Utilde" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM507 673Q483 673 445 694T368 753Q362 740 356 727T343 704T329 687T314 680Q302 680 297 686T291 700Q291 717 298 734T316 765T341 787T371 796Q395 796 433 775T510 716Q516 729 522 742T535 765T549 782T564 789Q576 789 581 783T587 769Q587 752 580 735T562 704T537 682T507 673Z" />
+<glyph unicode="&#x169;" glyph-name="utilde" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM424 548Q400 548 362 569T285 628Q279 615 273 602T260 579T246 562T231 555Q219 555 214 561T208 575Q208 592 215 609T233 640T258 662T288 671Q312 671 350 650T427 591Q433 604 439 617T452 640T466 657T481 664Q493 664 498 658T504 644Q504 627 497 610T479 579T454 557T424 548Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM330 733Q299 733 299 751Q299 759 302 770T311 787Q318 795 337 795H557Q588 795 588 777Q588 769 585 758T576 741Q569 733 550 733H330Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM248 568Q217 568 217 586Q217 594 220 605T229 622Q236 630 255 630H475Q506 630 506 612Q506 604 503 593T494 576Q487 568 468 568H248Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM439 713Q415 713 392 723T349 749T318 784T306 820Q306 831 312 839T331 847Q347 847 371 828T438 756Q485 805 514 826T563 847Q574 847 581 840T588 823Q588 808 575 789T541 753T493 725T439 713Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM346 548Q322 548 299 558T256 584T225 619T213 655Q213 666 219 674T238 682Q254 682 278 663T345 591Q392 640 421 661T470 682Q481 682 488 675T495 658Q495 643 482 624T448 588T400 560T346 548Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM437 715Q388 715 358 743T328 816Q328 842 337 866T364 909T405 939T458 950Q507 950 537 922T567 849Q567 823 558 799T531 756T489 726T437 715ZM439 760Q457 760 471 767T495 787T510 814T516 845Q516 870 499 887T456 905Q438 905 424 898T400 878T385 851T379 820Q379 795 396 778T439 760Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM346 530Q297 530 267 558T237 631Q237 657 246 681T273 724T314 754T367 765Q416 765 446 737T476 664Q476 638 467 614T440 571T398 541T346 530ZM348 575Q366 575 380 582T404 602T419 629T425 660Q425 685 408 702T365 720Q347 720 333 713T309 693T294 666T288 635Q288 610 305 593T348 575Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q563 112 497 50T321 -12ZM479 716Q477 718 487 732T516 765T557 805T604 844Q628 862 650 862Q662 862 671 855T680 835Q680 810 643 785Q618 768 590 754T538 731T498 718T479 716ZM300 716Q298 718 308 732T337 765T378 805T425 844Q449 862 471 862Q483 862 492 855T501 835Q501 810 464 785Q439 768 411 754T359 731T319 718T300 716Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="571" d="M455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 30T497 15T484 -1T455 -8ZM373 551Q371 553 381 567T410 600T451 640T498 679Q522 697 544 697Q556 697 565 690T574 670Q574 645 537 620Q512 603 484 589T432 566T392 553T373 551ZM194 551Q192 553 202 567T231 600T272 640T319 679Q343 697 365 697Q377 697 386 690T395 670Q395 645 358 620Q333 603 305 589T253 566T213 553T194 551Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="673" d="M321 -12Q256 -12 207 7T127 63T88 155T95 285L166 620Q170 636 177 643T200 651H209Q224 651 230 642T233 616L162 280Q138 164 180 107T325 49Q483 49 525 247L604 620Q608 636 615 643T638 651H646Q661 651 667 642T670 616L589 233Q553 65 440 12Q384 -20 365 -46T346 -92Q346 -107 355 -116T379 -125Q401 -125 416 -107Q426 -109 434 -116T442 -137Q442 -157 424 -170T374 -183Q334 -183 309 -161T283 -104Q283 -80 296 -57T337 -11Q332 -11 329 -11T321 -12Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="571" d="M419 -1Q382 17 388 79Q362 36 320 12T230 -12Q182 -12 148 4T93 49T68 119T72 207L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37Q500 37 500 32T499 20T491 7T476 -4Q416 -38 397 -64T377 -112Q377 -127 386 -136T410 -145Q432 -145 447 -127Q457 -129 465 -136T473 -157Q473 -177 455 -190T405 -203Q365 -203 340 -181T314 -124Q314 -91 339 -59T419 -1Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="938" d="M157 637Q157 641 168 646T192 652T216 642T227 609L235 61L478 529Q487 547 498 554T532 562Q561 562 571 550T583 518L624 63L885 649Q885 649 897 650T919 645T933 622T920 567L660 0H570L526 495L264 0H177L157 637ZM450 704Q440 704 436 709T431 721Q431 738 448 758T498 807Q514 821 530 833T560 854T581 868T591 874Q592 874 598 869T613 854T632 834T653 810Q675 781 683 763T692 733Q692 719 685 712T668 704Q655 704 643 718T612 765L583 816L532 763Q506 736 485 720T450 704Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="729" d="M127 330Q127 360 123 379T110 410T88 425T58 431Q57 431 57 438T61 455T77 472T109 480Q138 480 157 461Q192 426 190 324L186 69L337 371Q349 395 362 401T396 408Q442 408 446 360L472 68L664 479Q664 479 675 479T696 472T709 449T697 401L502 0H421L384 329L214 0H126L127 330ZM314 539Q304 539 300 544T295 556Q295 573 312 593T362 642Q378 656 394 668T424 689T445 703T455 709Q456 709 462 704T477 689T496 669T517 645Q539 616 547 598T556 568Q556 554 549 547T532 539Q519 539 507 553T476 600L447 651L396 598Q370 571 349 555T314 539Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="583" d="M265 272L110 647H181L308 331L581 653Q581 653 593 651T614 639T622 612T593 564L331 265L275 0H207L265 272ZM270 714Q260 714 256 719T251 731Q251 748 268 768T318 817Q334 831 350 843T380 864T401 878T411 884Q412 884 418 879T433 864T452 844T473 820Q495 791 503 773T512 743Q512 729 505 722T488 714Q475 714 463 728T432 775L403 826L352 773Q326 746 305 730T270 714Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="498" d="M9 -204Q-15 -204 -34 -199T-65 -181Q-77 -169 -77 -153Q-77 -145 -73 -139T-65 -127T-56 -120T-50 -119Q-40 -130 -23 -137T5 -145Q20 -145 35 -139T68 -117T109 -72T162 1L178 24Q161 26 153 37T143 67L115 330Q110 382 93 405T40 428Q40 428 40 436T42 454T57 472T90 480Q116 480 134 462Q170 426 179 324L203 59L457 476H528L225 -12Q189 -69 161 -106T108 -165T60 -195T9 -204ZM199 549Q189 549 185 554T180 566Q180 583 197 603T247 652Q263 666 279 678T309 699T330 713T340 719Q341 719 347 714T362 699T381 679T402 655Q424 626 432 608T441 578Q441 564 434 557T417 549Q404 549 392 563T361 610L332 661L281 608Q255 581 234 565T199 549Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="583" d="M265 272L110 647H181L308 331L581 653Q581 653 593 651T614 639T622 612T593 564L331 265L275 0H207L265 272ZM467 708Q418 708 418 746Q418 769 430 786T469 804Q518 804 518 766Q518 743 506 726T467 708ZM299 708Q250 708 250 746Q250 769 262 786T301 804Q350 804 350 766Q350 743 338 726T299 708Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="598" d="M13 48L15 61L521 585H170Q149 585 141 592T132 610Q132 621 138 634T147 647H622L612 599L609 586L103 62H475Q496 62 504 55T513 37Q513 26 507 13T498 0H2L13 48ZM295 716Q294 718 307 732T341 765T388 806T439 842Q463 857 485 857Q498 857 506 849T514 829Q514 805 473 781Q445 765 415 752T358 730T314 718T295 716Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="507" d="M10 49L406 420H138Q119 420 112 426T104 443Q104 453 109 464T117 476H490V427L94 56H371Q390 56 397 50T405 33Q405 23 400 12T392 0H10V49ZM224 551Q223 553 236 567T270 600T317 641T368 677Q392 692 414 692Q427 692 435 684T443 664Q443 640 402 616Q374 600 344 587T287 565T243 553T224 551Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="598" d="M13 48L15 61L521 585H170Q149 585 141 592T132 610Q132 621 138 634T147 647H622L612 599L609 586L103 62H475Q496 62 504 55T513 37Q513 26 507 13T498 0H2L13 48ZM385 714Q331 714 331 755Q331 779 344 798T387 818Q441 818 441 777Q441 753 428 734T385 714Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="507" d="M10 49L406 420H138Q119 420 112 426T104 443Q104 453 109 464T117 476H490V427L94 56H371Q390 56 397 50T405 33Q405 23 400 12T392 0H10V49ZM301 549Q247 549 247 590Q247 614 260 633T303 653Q357 653 357 612Q357 588 344 569T301 549Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="598" d="M13 48L15 61L521 585H170Q149 585 141 592T132 610Q132 621 138 634T147 647H622L612 599L609 586L103 62H475Q496 62 504 55T513 37Q513 26 507 13T498 0H2L13 48ZM380 703Q379 703 373 708T357 723T338 743T318 767Q296 796 288 814T279 844Q279 858 286 865T303 873Q319 873 336 848T388 761L439 814Q465 841 486 857T521 873Q531 873 535 868T540 856Q540 839 523 819T473 770Q456 756 440 744T411 723T390 709T380 703Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="507" d="M10 49L406 420H138Q119 420 112 426T104 443Q104 453 109 464T117 476H490V427L94 56H371Q390 56 397 50T405 33Q405 23 400 12T392 0H10V49ZM299 533Q298 533 292 538T276 553T257 573T237 597Q215 626 207 644T198 674Q198 688 205 695T222 703Q238 703 255 678T307 591L358 644Q384 671 405 687T440 703Q450 703 454 698T459 686Q459 669 442 649T392 600Q375 586 359 574T330 553T309 539T299 533Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="364" d="M94 -4Q84 -4 76 1T68 8L147 378H83Q54 378 54 394Q54 402 57 413T66 429Q73 436 90 436H159L174 505Q191 583 230 621T329 659Q361 659 380 652T413 631Q427 617 427 599Q427 590 424 584T416 573T407 566T402 565Q392 579 372 590T326 601Q292 601 270 577T235 496L222 436H345Q374 436 374 420Q374 412 371 401T362 385Q355 378 338 378H210L142 50Q136 21 125 9T94 -4Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="567" d="M269 -12Q201 -12 149 9T65 61Q48 78 38 96T28 133Q28 146 34 154T48 166T63 170T70 170Q93 113 143 80T273 47Q351 47 388 79T426 168Q426 193 415 212T385 245T340 272T284 296Q253 309 223 323T169 359T131 409T116 480Q116 517 131 550T174 607T243 645T333 659Q401 659 448 640T521 595Q553 563 553 529Q553 516 547 509T534 497T520 492T512 493Q492 541 448 570T330 600Q298 600 271 592T225 568T196 532T185 487Q185 459 195 439T225 404T272 376T331 350Q361 337 390 323T443 289T480 242T495 174Q495 132 480 98T437 40T366 2T269 -12ZM157 -234Q154 -237 148 -236T137 -230T129 -222T127 -214L234 -47Q235 -46 244 -47T263 -52T283 -63T292 -83Q292 -105 270 -128L157 -234Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="481" d="M222 -12Q163 -12 117 6T45 50Q34 61 28 72T21 95Q21 106 26 113T38 126T50 132T57 133Q80 95 122 69T226 43Q282 43 312 65T342 124Q342 157 312 174T228 209Q202 218 177 228T131 254T97 292T84 347Q84 378 97 403T134 448T190 477T262 488Q316 488 356 472T420 432Q444 408 444 384Q444 373 439 366T426 354T413 349T406 350Q387 387 347 410T257 433Q202 433 175 409T147 355Q147 320 179 302T265 264Q290 255 315 245T360 219T392 183T405 132Q405 66 357 27T222 -12ZM114 -234Q111 -237 105 -236T94 -230T86 -222T84 -214L191 -47Q192 -46 201 -47T220 -52T240 -63T249 -83Q249 -105 227 -128L114 -234Z" />
+<glyph unicode="&#x21A;" glyph-name="uni021A" horiz-adv-x="618" d="M350 585H151Q120 585 120 602Q120 610 124 622T133 640Q139 647 159 647H628Q659 647 659 630Q659 622 655 610T646 592Q640 585 620 585H418L293 0H226L350 585ZM143 -234Q140 -237 134 -236T123 -230T115 -222T113 -214L220 -47Q221 -46 230 -47T249 -52T269 -63T278 -83Q278 -105 256 -128L143 -234Z" />
+<glyph unicode="&#x21B;" glyph-name="uni021B" horiz-adv-x="375" d="M208 -12Q141 -12 108 31T93 161L147 418H79Q79 419 80 422Q80 424 80 426T82 434L90 469Q90 471 90 472T91 474V476H159L169 523Q177 560 189 575T222 590Q232 590 239 587T247 582L224 476H372Q371 475 371 472Q370 470 370 467T369 460L361 425Q360 422 360 420V418H212L157 161Q143 97 160 71T218 44Q240 44 258 53T284 73Q286 75 292 66T299 43Q299 26 285 12Q274 1 255 -5T208 -12ZM61 -234Q58 -237 52 -236T41 -230T33 -222T31 -214L138 -47Q139 -46 148 -47T167 -52T187 -63T196 -83Q196 -105 174 -128L61 -234Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="534" d="M227 549Q217 549 213 554T208 566Q208 583 225 603T275 652Q291 666 307 678T337 699T358 713T368 719Q369 719 375 714T390 699T409 679T430 655Q452 626 460 608T469 578Q469 564 462 557T445 549Q432 549 420 563T389 610L360 661L309 608Q283 581 262 565T227 549Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="534" d="M333 533Q332 533 326 538T310 553T291 573T271 597Q249 626 241 644T232 674Q232 688 239 695T256 703Q272 703 289 678T341 591L392 644Q418 671 439 687T474 703Q484 703 488 698T493 686Q493 669 476 649T426 600Q409 586 393 574T364 553T343 539T333 533Z" />
+<glyph unicode="&#x2C9;" glyph-name="overscore" horiz-adv-x="534" d="M231 568Q200 568 200 586Q200 594 203 605T212 622Q219 630 238 630H458Q489 630 489 612Q489 604 486 593T477 576Q470 568 451 568H231Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="534" d="M346 548Q322 548 299 558T256 584T225 619T213 655Q213 666 219 674T238 682Q254 682 278 663T345 591Q392 640 421 661T470 682Q481 682 488 675T495 658Q495 643 482 624T448 588T400 560T346 548Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="534" d="M344 549Q290 549 290 590Q290 614 303 633T346 653Q400 653 400 612Q400 588 387 569T344 549Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="534" d="M348 550Q299 550 269 578T239 651Q239 677 248 701T275 744T316 774T369 785Q418 785 448 757T478 684Q478 658 469 634T442 591T400 561T348 550ZM350 595Q368 595 382 602T406 622T421 649T427 680Q427 705 410 722T367 740Q349 740 335 733T311 713T296 686T290 655Q290 630 307 613T350 595Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="534" d="M280 2Q248 -15 228 -30T195 -60T179 -86T174 -109Q174 -124 183 -133T207 -142Q229 -142 244 -124Q254 -126 262 -133T270 -154Q270 -174 252 -187T202 -200Q162 -200 137 -178T111 -121Q111 -80 149 -42T272 23L280 2Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="534" d="M414 548Q390 548 352 569T275 628Q269 615 263 602T250 579T236 562T221 555Q209 555 204 561T198 575Q198 592 205 609T223 640T248 662T278 671Q302 671 340 650T417 591Q423 604 429 617T442 640T456 657T471 664Q483 664 488 658T494 644Q494 627 487 610T469 579T444 557T414 548Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="534" d="M365 551Q363 553 373 567T402 600T443 640T490 679Q514 697 536 697Q548 697 557 690T566 670Q566 645 529 620Q504 603 476 589T424 566T384 553T365 551ZM186 551Q184 553 194 567T223 600T264 640T311 679Q335 697 357 697Q369 697 378 690T387 670Q387 645 350 620Q325 603 297 589T245 566T205 553T186 551Z" />
+<glyph unicode="&#x394;" glyph-name="increment" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x3A9;" glyph-name="Omega" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x3BC;" glyph-name="mu" horiz-adv-x="571" d="M52 -298Q7 -298 -9 -271T-14 -190L129 476H194L136 202Q122 134 148 92T239 49Q294 49 333 77T406 164L472 476H537L457 101Q449 66 458 52T500 37V30Q500 23 497 15T484 -1T455 -8Q417 -8 400 14T388 79Q362 36 320 12T230 -12Q182 -12 147 4Q118 17 100 40L51 -189Q43 -224 52 -238T99 -253V-260Q99 -267 95 -275T81 -291T52 -298Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="791" d="M413 -12Q341 -12 281 13T177 82T109 187T85 322Q85 394 110 456T179 563T283 633T414 659Q486 659 546 634T649 565T717 460T742 325Q742 253 717 191T648 84T544 14T413 -12ZM415 36Q475 36 525 57T612 116T668 206T688 323Q688 385 667 437T609 528T522 589T412 611Q352 611 302 590T215 531T159 441T139 324Q139 262 160 210T218 119T305 58T415 36ZM326 139Q301 139 301 167V452Q301 467 310 476T333 486H429Q495 486 530 457T566 373Q566 317 532 287T432 257H361V167Q361 139 335 139H326ZM433 306Q469 306 488 323T507 372Q507 403 487 420T429 437H360V306H433Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="559" d="M91 213Q62 213 62 229Q62 237 65 248T74 264Q81 271 98 271H471Q500 271 500 255Q500 247 497 236T488 220Q481 213 464 213H91Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="727" d="M91 213Q62 213 62 229Q62 237 65 248T74 264Q81 271 98 271H639Q668 271 668 255Q668 247 665 236T656 220Q649 213 632 213H91Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="236" d="M167 425Q166 423 157 423T136 427T115 438T105 458Q105 469 109 479T124 503L253 665Q255 668 261 667T272 662T281 654T283 645L167 425Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="236" d="M113 422Q111 419 105 420T94 425T85 433T83 442L199 662Q200 663 209 663T230 660T251 649T261 629Q261 618 257 608T242 584L113 422Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="243" d="M-17 -142Q-19 -145 -25 -144T-36 -139T-45 -131T-47 -122L69 98Q70 99 79 99T100 96T121 85T131 65Q131 54 127 44T112 20L-17 -142Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="389" d="M167 426Q166 424 157 424T136 428T115 439T105 459Q105 470 109 480T124 504L253 666Q255 669 261 668T272 663T281 655T283 646L167 426ZM320 425Q319 423 310 423T289 427T268 438T258 458Q258 469 262 479T277 503L406 665Q408 668 414 667T425 662T434 654T436 645L320 425Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="389" d="M113 422Q111 419 105 420T94 425T85 433T83 442L199 662Q200 663 209 663T230 660T251 649T261 629Q261 618 257 608T242 584L113 422ZM266 421Q264 418 258 419T247 424T238 432T236 441L352 661Q353 662 362 662T383 659T404 648T414 628Q414 617 410 607T395 583L266 421Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="396" d="M-17 -142Q-19 -145 -25 -144T-36 -139T-45 -131T-47 -122L69 98Q70 99 79 99T100 96T121 85T131 65Q131 54 127 44T112 20L-17 -142ZM136 -143Q134 -146 128 -145T117 -140T108 -132T106 -123L222 97Q223 98 232 98T253 95T274 84T284 64Q284 53 280 43T265 19L136 -143Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="544" d="M194 -160Q175 -160 170 -154T167 -137L245 298Q248 315 259 338T287 381L120 358Q97 355 88 362T78 385Q78 395 80 405T88 422T103 433T127 435L294 413L310 609Q312 633 322 643T354 653Q377 653 389 643T391 604L326 411L493 434Q516 437 525 430T535 407Q535 386 525 370T486 357L319 379Q327 359 328 337T325 298L218 -138Q213 -160 196 -160H194Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="544" d="M194 -160Q175 -160 170 -154T167 -137L210 104L62 82Q39 79 30 86T20 109Q20 119 22 129T30 146T45 157T69 159L216 138L245 298Q248 315 259 338T287 381L120 358Q97 355 88 362T78 385Q78 395 80 405T88 422T103 433T127 435L294 413L310 609Q312 633 322 643T354 653Q377 653 389 643T391 604L326 411L493 434Q516 437 525 430T535 407Q535 386 525 370T486 357L319 379Q327 359 328 337T325 298L285 136L435 158Q458 161 467 154T477 131Q477 110 467 94T428 81L277 102L218 -138Q213 -160 196 -160H194Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="358" d="M180 139Q159 139 140 147T108 169T86 202T78 242Q78 263 86 282T107 315T140 338T180 346Q201 346 220 338T252 316T274 282T282 242Q282 221 274 202T253 169T220 147T180 139Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="661" d="M496 -6Q473 -6 461 3T448 28Q448 64 464 80Q477 93 501 93Q524 93 536 84T549 59Q549 23 533 7Q520 -6 496 -6ZM287 -6Q264 -6 252 3T239 28Q239 64 255 80Q268 93 292 93Q315 93 327 84T340 59Q340 23 324 7Q311 -6 287 -6ZM78 -6Q55 -6 43 3T30 28Q30 64 46 80Q59 93 83 93Q106 93 118 84T131 59Q131 23 115 7Q102 -6 78 -6Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1110" d="M232 347Q204 347 179 356T136 384T107 427T96 484Q96 518 108 549T143 605T195 643T262 657Q290 657 315 648T358 620T387 577T398 520Q398 485 386 454T351 399T299 361T232 347ZM69 -9Q68 -10 64 -9T55 -5T46 4T42 18Q42 31 49 41T78 72L734 662Q735 663 739 662T748 658T757 649T761 635Q761 622 754 612T725 581L69 -9ZM885 -4Q857 -4 832 5T789 33T760 76T749 133Q749 167 761 198T796 254T848 292T915 306Q943 306 968 297T1011 269T1040 226T1051 169Q1051 134 1039 103T1004 48T952 10T885 -4ZM541 -4Q513 -4 488 5T445 33T416 76T405 133Q405 167 417 198T452 254T504 292T571 306Q599 306 624 297T667 269T696 226T707 169Q707 134 695 103T660 48T608 10T541 -4ZM236 398Q258 398 278 407T312 433T334 471T343 517Q343 555 319 580T258 606Q235 606 216 597T182 571T160 533T151 487Q151 449 176 424T236 398ZM889 47Q911 47 931 56T965 82T987 120T996 166Q996 204 972 229T911 255Q888 255 869 246T835 220T813 182T804 136Q804 98 829 73T889 47ZM545 47Q567 47 587 56T621 82T643 120T652 166Q652 204 628 229T567 255Q544 255 525 246T491 220T469 182T460 136Q460 98 485 73T545 47Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="346" d="M223 78Q203 78 180 97T130 148Q116 165 104 182T83 213T69 238T65 251Q66 253 75 262T98 285T131 314T170 345Q203 371 235 389T287 408Q298 408 304 401T310 384Q310 376 302 365T279 341T246 313T205 286Q186 275 166 262T124 237Q141 223 156 210T186 183Q221 151 235 134T250 101Q250 92 242 85T223 78Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="346" d="M63 78Q52 78 46 85T40 102Q40 109 48 120T70 145T104 173T145 200Q164 211 184 224T226 249Q209 263 194 276T164 303Q129 335 115 352T100 385Q100 394 108 401T127 408Q147 408 170 389T220 338Q233 321 245 304T267 273T281 248T285 235Q284 233 275 224T252 201T219 172T180 141Q147 115 115 97T63 78Z" />
+<glyph unicode="&#x2044;" glyph-name="uni2044" horiz-adv-x="659" d="M23 -8Q22 -9 18 -8T8 -4T-1 5T-5 18Q-5 32 4 46T30 75L676 661Q677 662 681 661T691 657T700 648T704 635Q704 621 695 607T669 578L23 -8Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="660" d="M116 376Q87 376 87 392Q87 400 90 411T99 427Q106 434 123 434H194Q210 475 231 512T282 579Q322 619 369 639T475 659Q527 659 563 643T625 602Q642 585 651 564T660 522Q660 507 653 498T638 485T622 479T614 480Q599 544 560 573T467 602Q425 602 391 587T328 543Q306 521 290 493T260 434H476Q505 434 505 418Q505 410 502 399T493 383Q486 376 469 376H240Q233 349 229 324T222 276H442Q471 276 471 260Q471 252 468 241T459 225Q452 218 435 218H220Q223 182 234 151T266 96T316 59T381 46Q431 46 470 69T545 144Q546 145 551 144T562 138T572 127T577 111Q577 86 545 54Q532 41 514 29T475 8T430 -6T383 -12Q327 -12 285 4T214 51T170 123T152 218H82Q53 218 53 234Q53 242 56 253T65 269Q72 276 89 276H154Q157 299 161 324T174 376H116Z" />
+<glyph unicode="&#x2113;" glyph-name="afii61289" horiz-adv-x="434" d="M199 341Q228 365 251 395T291 456T317 513T326 558Q326 584 315 584Q308 584 300 573Q291 561 278 539T250 486T222 419T199 341ZM44 138Q30 151 19 172T7 228Q25 237 33 240T49 246L59 251Q63 321 81 383T126 497T183 588T245 652Q285 683 327 683Q367 683 395 652T423 558Q423 509 402 463T349 375T276 294T196 223Q196 218 196 214T195 204Q195 158 207 133T237 107Q254 107 273 133T310 217Q322 220 336 220Q355 220 373 213T404 186Q397 149 382 114T345 52T293 9T228 -8Q206 -8 182 1T135 30T94 78T66 146L44 138Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="711" d="M437 341Q416 341 416 366V622Q416 651 442 651H467Q481 651 486 646T497 629L560 473L623 629Q628 641 634 646T653 651H676Q702 651 702 622V366Q702 341 680 341H673Q652 341 652 366V579L587 418Q585 412 581 408T559 404Q542 404 538 408T532 418L466 578V366Q466 341 446 341H437ZM227 341Q205 341 205 366V600H124Q100 600 100 622V626Q100 648 124 648H338Q362 648 362 626V622Q362 600 338 600H258V366Q258 341 234 341H227Z" />
+<glyph unicode="&#x2126;" glyph-name="uni2126" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x212E;" glyph-name="estimated" horiz-adv-x="830" d="M415 671Q492 671 560 645T681 573T763 463T793 324H185Q175 324 175 313V116Q223 65 286 38T415 10Q490 10 555 43T667 131H724Q667 62 585 26T415 -11Q340 -11 272 14T151 84T67 191T36 330Q36 403 66 465T147 574T268 645T415 671ZM415 650Q345 650 282 622T175 543V358Q175 347 185 347H646Q655 347 655 358V542Q610 594 546 622T415 650Z" />
+<glyph unicode="&#x2202;" glyph-name="partialdiff" horiz-adv-x="538" d="M303 481Q335 481 361 471T404 445V464Q404 499 399 528T381 578T343 610T282 622Q247 622 208 612L189 679Q271 710 332 710Q421 710 464 649T508 475Q508 370 481 283T409 133T311 35T201 0Q165 0 134 10T80 44T43 102T30 187Q30 256 55 310T120 403T208 461T303 481ZM205 105Q229 105 256 121T310 167T359 243T393 349Q382 361 360 373T309 386Q275 386 244 371T188 331T149 270T134 194Q134 157 150 131T205 105Z" />
+<glyph unicode="&#x2206;" glyph-name="uni2206" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x220F;" glyph-name="product" horiz-adv-x="634" d="M484 0V607H150V0H50V700H584V0H484Z" />
+<glyph unicode="&#x2211;" glyph-name="summation" horiz-adv-x="475" d="M110 80H437V0H25V96L209 325L25 554V650H437V570H115L288 353V300L110 80Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="634" d="M134 237Q116 237 111 243T109 264L111 271Q115 285 122 291T147 298H510Q528 298 534 292T536 271L534 264Q530 250 523 244T497 237H134Z" />
+<glyph unicode="&#x2215;" glyph-name="fraction" horiz-adv-x="659" d="M23 -8Q22 -9 18 -8T8 -4T-1 5T-5 18Q-5 32 4 46T30 75L676 661Q677 662 681 661T691 657T700 648T704 635Q704 621 695 607T669 578L23 -8Z" />
+<glyph unicode="&#x2219;" glyph-name="middot" horiz-adv-x="255" d="M127 194Q104 194 92 203T79 228Q79 264 95 280Q108 293 132 293Q155 293 167 284T180 259Q180 223 164 207Q151 194 127 194Z" />
+<glyph unicode="&#x221A;" glyph-name="radical" horiz-adv-x="649" d="M256 501L369 171L566 700H649L386 0H283L136 433H30V501H256Z" />
+<glyph unicode="&#x221E;" glyph-name="infinity" horiz-adv-x="733" d="M703 271Q703 231 690 203T654 156T604 129T547 120Q518 120 493 128T447 150T405 181T366 217Q347 199 328 182T287 151T240 129T186 120Q157 120 129 129T79 156T44 203T30 271Q30 310 43 338T79 385T129 413T186 422Q215 422 240 414T286 392T327 361T366 325Q385 343 405 360T446 391T493 413T547 422Q576 422 604 414T654 387T689 340T703 271ZM187 222Q223 222 252 235T308 271Q282 292 253 306T187 320Q167 320 149 309T130 271Q130 244 148 233T187 222ZM546 320Q510 320 481 306T425 271Q451 249 480 236T546 222Q566 222 584 233T603 271Q603 298 585 309T546 320Z" />
+<glyph unicode="&#x222B;" glyph-name="integral'" horiz-adv-x="396" d="M164 596Q172 654 201 685Q228 714 271 723T375 724V648Q314 653 288 640T255 584L168 -32Q159 -95 128 -126Q105 -149 70 -155T-15 -155V-81Q22 -84 45 -75T74 -25L164 596Z" />
+<glyph unicode="&#x2248;" glyph-name="approxequal" horiz-adv-x="534" d="M339 252Q315 252 277 273T200 332Q194 319 188 306T175 283T161 266T146 259Q134 259 129 265T123 279Q123 296 130 313T148 344T173 366T203 375Q227 375 265 354T342 295Q348 308 354 321T367 344T381 361T396 368Q408 368 413 362T419 348Q419 331 412 314T394 283T369 261T339 252ZM315 81Q291 81 253 102T176 161Q170 148 164 135T151 112T137 95T122 88Q110 88 105 94T99 108Q99 125 106 142T124 173T149 195T179 204Q203 204 241 183T318 124Q324 137 330 150T343 173T357 190T372 197Q384 197 389 191T395 177Q395 160 388 143T370 112T345 90T315 81Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="634" d="M162 346Q131 346 131 362Q131 370 135 382T144 400Q151 407 169 407H387L487 549Q498 565 508 568T528 565L533 562Q544 555 545 547T536 522L455 407H529Q560 407 560 391Q560 383 556 371T547 353Q540 346 522 346H408L291 180H480Q511 180 511 164Q511 156 507 144T498 126Q491 119 473 119H254L156 -22Q145 -39 135 -41T115 -38L110 -35Q99 -28 98 -20T107 5L186 119H113Q82 119 82 135Q82 143 86 155T95 173Q102 180 120 180H233L350 346H162Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="634" d="M480 321Q505 307 513 294T519 270Q517 254 508 246T495 240L172 392Q148 407 148 432Q148 449 156 462T190 488L579 626Q581 627 590 618T600 594Q601 583 593 572T557 547L222 434L480 321ZM113 119Q82 119 82 135Q82 143 86 155T95 173Q102 180 120 180H480Q511 180 511 164Q511 156 507 144T498 126Q491 119 473 119H113Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="634" d="M113 273Q112 284 120 296T156 320L491 432L233 546Q206 556 199 571T194 597Q196 613 205 621T218 627L541 475Q555 470 560 459T565 435Q565 418 557 404T523 379L134 241Q132 240 123 249T113 273ZM113 119Q82 119 82 135Q82 143 86 155T95 173Q102 180 120 180H480Q511 180 511 164Q511 156 507 144T498 126Q491 119 473 119H113Z" />
+<glyph unicode="&#x25CA;" glyph-name="lozenge" horiz-adv-x="520" d="M30 388L226 776H294L490 388L294 0H226L30 388ZM370 388L260 616L150 388L260 159L370 388Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="573" d="M166 418H70Q70 419 71 422Q71 424 71 426T73 434L81 469Q81 471 81 472T82 474V476H178L191 534Q208 612 247 650T346 689Q377 689 397 682T430 661Q444 647 444 629Q444 620 441 614T433 603T424 596T419 595Q409 609 389 620T343 631Q309 631 287 607T252 525L241 476H365Q364 475 364 472Q363 470 363 467T362 460L354 425Q353 422 353 420V418H230L142 0H77L166 418ZM456 -8Q411 -8 395 19T390 100L470 476H535L455 101Q447 66 456 52T503 37Q503 37 503 30T499 15T485 -1T456 -8ZM537 596Q493 596 493 629Q493 649 504 664T539 680Q583 680 583 647Q583 627 572 612T537 596Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="573" d="M166 418H70Q70 419 71 422Q71 424 71 426T73 434L81 469Q81 471 81 472T82 474V476H178L191 534Q208 612 247 650T346 689Q377 689 397 682T430 661Q444 647 444 629Q444 620 441 614T433 603T424 596T419 595Q409 609 389 620T343 631Q309 631 287 607T252 525L241 476H365Q364 475 364 472Q363 470 363 467T362 460L354 425Q353 422 353 420V418H230L142 0H77L166 418ZM456 -8Q411 -8 395 19T390 100L513 677H578L455 101Q447 66 456 52T503 37Q503 37 503 30T499 15T485 -1T456 -8Z" />
+
+<hkern u1="&#x141;" u2="&#xdd;" k="80" />
+<hkern u1="&#x141;" u2="@" k="50" />
+<hkern u1="&#x141;" u2="A" k="-20" />
+<hkern u1="&#x141;" u2="C" k="50" />
+<hkern u1="&#x141;" u2="G" k="50" />
+<hkern u1="&#x141;" u2="O" k="50" />
+<hkern u1="&#x141;" u2="Q" k="50" />
+<hkern u1="&#x141;" u2="T" k="120" />
+<hkern u1="&#x141;" u2="U" k="15" />
+<hkern u1="&#x141;" u2="V" k="90" />
+<hkern u1="&#x141;" u2="W" k="70" />
+<hkern u1="&#x141;" u2="Y" k="80" />
+<hkern u1="&#x141;" u2="\" k="50" />
+<hkern u1="&#x141;" u2="a" k="20" />
+<hkern u1="&#x141;" u2="c" k="20" />
+<hkern u1="&#x141;" u2="d" k="20" />
+<hkern u1="&#x141;" u2="e" k="20" />
+<hkern u1="&#x141;" u2="o" k="20" />
+<hkern u1="&#x141;" u2="q" k="20" />
+<hkern u1="&#x141;" u2="v" k="40" />
+<hkern u1="&#x141;" u2="w" k="10" />
+<hkern u1="&#x141;" u2="y" k="40" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd6;" k="50" />
+<hkern u1="&#x141;" u2="&#xdc;" k="15" />
+<hkern u1="&#x141;" u2="&#xe7;" k="20" />
+<hkern u1="&#x141;" u2="&#xae;" k="50" />
+<hkern u1="&#x141;" u2="&#xa9;" k="50" />
+<hkern u1="&#x141;" u2="&#x2122;" k="70" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd8;" k="50" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="50" />
+<hkern u1="&#x141;" u2="&#xe6;" k="20" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd5;" k="50" />
+<hkern u1="&#x141;" u2="&#x152;" k="50" />
+<hkern u1="&#x141;" u2="&#x153;" k="20" />
+<hkern u1="&#x141;" u2="&#x201c;" k="100" />
+<hkern u1="&#x141;" u2="&#x201d;" k="60" />
+<hkern u1="&#x141;" u2="&#x2018;" k="100" />
+<hkern u1="&#x141;" u2="&#x2019;" k="60" />
+<hkern u1="&#x141;" u2="&#x178;" k="80" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd3;" k="50" />
+<hkern u1="&#x141;" u2="&#xd4;" k="50" />
+<hkern u1="&#x141;" u2="&#xd2;" k="50" />
+<hkern u1="&#x141;" u2="&#xda;" k="15" />
+<hkern u1="&#x141;" u2="&#xdb;" k="15" />
+<hkern u1="&#x141;" u2="&#xd9;" k="15" />
+<hkern u1="&#x160;" u2="&#xdd;" k="10" />
+<hkern u1="&#x160;" u2="A" k="-5" />
+<hkern u1="&#x160;" u2="V" k="10" />
+<hkern u1="&#x160;" u2="W" k="20" />
+<hkern u1="&#x160;" u2="X" k="-5" />
+<hkern u1="&#x160;" u2="Y" k="10" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-5" />
+<hkern u1="&#x160;" u2="&#x178;" k="10" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xdd;" u2="&#x160;" k="-30" />
+<hkern u1="&#xdd;" u2="&#x161;" k="50" />
+<hkern u1="&#xdd;" u2="&amp;" k="30" />
+<hkern u1="&#xdd;" u2=")" k="-50" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="50" />
+<hkern u1="&#xdd;" u2="-" k="50" />
+<hkern u1="&#xdd;" u2="." k="50" />
+<hkern u1="&#xdd;" u2=":" k="30" />
+<hkern u1="&#xdd;" u2=";" k="30" />
+<hkern u1="&#xdd;" u2="@" k="30" />
+<hkern u1="&#xdd;" u2="A" k="30" />
+<hkern u1="&#xdd;" u2="C" k="5" />
+<hkern u1="&#xdd;" u2="G" k="5" />
+<hkern u1="&#xdd;" u2="J" k="70" />
+<hkern u1="&#xdd;" u2="O" k="5" />
+<hkern u1="&#xdd;" u2="Q" k="5" />
+<hkern u1="&#xdd;" u2="S" k="-30" />
+<hkern u1="&#xdd;" u2="T" k="-20" />
+<hkern u1="&#xdd;" u2="V" k="-30" />
+<hkern u1="&#xdd;" u2="X" k="-20" />
+<hkern u1="&#xdd;" u2="]" k="-50" />
+<hkern u1="&#xdd;" u2="a" k="55" />
+<hkern u1="&#xdd;" u2="c" k="55" />
+<hkern u1="&#xdd;" u2="d" k="55" />
+<hkern u1="&#xdd;" u2="e" k="55" />
+<hkern u1="&#xdd;" u2="f" k="10" />
+<hkern u1="&#xdd;" u2="g" k="40" />
+<hkern u1="&#xdd;" u2="m" k="40" />
+<hkern u1="&#xdd;" u2="n" k="40" />
+<hkern u1="&#xdd;" u2="o" k="55" />
+<hkern u1="&#xdd;" u2="p" k="40" />
+<hkern u1="&#xdd;" u2="q" k="55" />
+<hkern u1="&#xdd;" u2="r" k="40" />
+<hkern u1="&#xdd;" u2="s" k="50" />
+<hkern u1="&#xdd;" u2="t" k="20" />
+<hkern u1="&#xdd;" u2="u" k="40" />
+<hkern u1="&#xdd;" u2="v" k="20" />
+<hkern u1="&#xdd;" u2="w" k="10" />
+<hkern u1="&#xdd;" u2="x" k="20" />
+<hkern u1="&#xdd;" u2="y" k="20" />
+<hkern u1="&#xdd;" u2="z" k="30" />
+<hkern u1="&#xdd;" u2="}" k="-50" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="30" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="5" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="55" />
+<hkern u1="&#xdd;" u2="&#xae;" k="30" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="30" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="5" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="30" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="55" />
+<hkern u1="&#xdd;" u2="&#xab;" k="60" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="30" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="5" />
+<hkern u1="&#xdd;" u2="&#x152;" k="5" />
+<hkern u1="&#xdd;" u2="&#x153;" k="55" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="60" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="40" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="50" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="30" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="5" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="5" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="5" />
+<hkern u1="&#xdd;" u2="&#x131;" k="40" />
+<hkern u1="&#xde;" u2="&#xdd;" k="45" />
+<hkern u1="&#xde;" u2="&#x17d;" k="45" />
+<hkern u1="&#xde;" u2="&#x2c;" k="40" />
+<hkern u1="&#xde;" u2="." k="40" />
+<hkern u1="&#xde;" u2="/" k="50" />
+<hkern u1="&#xde;" u2="?" k="20" />
+<hkern u1="&#xde;" u2="J" k="45" />
+<hkern u1="&#xde;" u2="T" k="50" />
+<hkern u1="&#xde;" u2="V" k="40" />
+<hkern u1="&#xde;" u2="W" k="60" />
+<hkern u1="&#xde;" u2="X" k="35" />
+<hkern u1="&#xde;" u2="Y" k="45" />
+<hkern u1="&#xde;" u2="Z" k="45" />
+<hkern u1="&#xde;" u2="a" k="5" />
+<hkern u1="&#xde;" u2="b" k="10" />
+<hkern u1="&#xde;" u2="c" k="5" />
+<hkern u1="&#xde;" u2="d" k="5" />
+<hkern u1="&#xde;" u2="e" k="5" />
+<hkern u1="&#xde;" u2="h" k="10" />
+<hkern u1="&#xde;" u2="k" k="10" />
+<hkern u1="&#xde;" u2="l" k="10" />
+<hkern u1="&#xde;" u2="m" k="5" />
+<hkern u1="&#xde;" u2="n" k="5" />
+<hkern u1="&#xde;" u2="o" k="5" />
+<hkern u1="&#xde;" u2="p" k="5" />
+<hkern u1="&#xde;" u2="q" k="5" />
+<hkern u1="&#xde;" u2="r" k="5" />
+<hkern u1="&#xde;" u2="u" k="5" />
+<hkern u1="&#xde;" u2="x" k="10" />
+<hkern u1="&#xde;" u2="z" k="10" />
+<hkern u1="&#xde;" u2="&#xe7;" k="5" />
+<hkern u1="&#xde;" u2="&#xe6;" k="5" />
+<hkern u1="&#xde;" u2="&#x2026;" k="40" />
+<hkern u1="&#xde;" u2="&#x153;" k="5" />
+<hkern u1="&#xde;" u2="&#x201c;" k="20" />
+<hkern u1="&#xde;" u2="&#x2018;" k="20" />
+<hkern u1="&#xde;" u2="&#x178;" k="45" />
+<hkern u1="&#xde;" u2="&#x201a;" k="40" />
+<hkern u1="&#xde;" u2="&#x201e;" k="40" />
+<hkern u1="&#xde;" u2="&#x131;" k="5" />
+<hkern u1="&#x17d;" u2="&#xf0;" k="30" />
+<hkern u1="&#x17d;" u2="-" k="50" />
+<hkern u1="&#x17d;" u2="A" k="10" />
+<hkern u1="&#x17d;" u2="C" k="45" />
+<hkern u1="&#x17d;" u2="G" k="45" />
+<hkern u1="&#x17d;" u2="J" k="10" />
+<hkern u1="&#x17d;" u2="O" k="45" />
+<hkern u1="&#x17d;" u2="Q" k="45" />
+<hkern u1="&#x17d;" u2="T" k="10" />
+<hkern u1="&#x17d;" u2="a" k="40" />
+<hkern u1="&#x17d;" u2="b" k="10" />
+<hkern u1="&#x17d;" u2="c" k="40" />
+<hkern u1="&#x17d;" u2="d" k="40" />
+<hkern u1="&#x17d;" u2="e" k="40" />
+<hkern u1="&#x17d;" u2="f" k="20" />
+<hkern u1="&#x17d;" u2="g" k="20" />
+<hkern u1="&#x17d;" u2="h" k="10" />
+<hkern u1="&#x17d;" u2="i" k="20" />
+<hkern u1="&#x17d;" u2="j" k="15" />
+<hkern u1="&#x17d;" u2="k" k="10" />
+<hkern u1="&#x17d;" u2="l" k="10" />
+<hkern u1="&#x17d;" u2="m" k="20" />
+<hkern u1="&#x17d;" u2="n" k="20" />
+<hkern u1="&#x17d;" u2="o" k="40" />
+<hkern u1="&#x17d;" u2="p" k="20" />
+<hkern u1="&#x17d;" u2="q" k="40" />
+<hkern u1="&#x17d;" u2="r" k="20" />
+<hkern u1="&#x17d;" u2="u" k="20" />
+<hkern u1="&#x17d;" u2="v" k="30" />
+<hkern u1="&#x17d;" u2="w" k="10" />
+<hkern u1="&#x17d;" u2="y" k="30" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="45" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="40" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="45" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="40" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="45" />
+<hkern u1="&#x17d;" u2="&#x152;" k="45" />
+<hkern u1="&#x17d;" u2="&#x153;" k="40" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="50" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="50" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="45" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="45" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="45" />
+<hkern u1="&#x17d;" u2="&#x131;" k="20" />
+<hkern u1="&amp;" u2="&#xdd;" k="30" />
+<hkern u1="&amp;" u2="A" k="-40" />
+<hkern u1="&amp;" u2="J" k="-20" />
+<hkern u1="&amp;" u2="T" k="110" />
+<hkern u1="&amp;" u2="V" k="20" />
+<hkern u1="&amp;" u2="W" k="20" />
+<hkern u1="&amp;" u2="X" k="-20" />
+<hkern u1="&amp;" u2="Y" k="30" />
+<hkern u1="&amp;" u2="&#xc4;" k="-40" />
+<hkern u1="&amp;" u2="&#xc5;" k="-40" />
+<hkern u1="&amp;" u2="&#xc6;" k="-40" />
+<hkern u1="&amp;" u2="&#xc0;" k="-40" />
+<hkern u1="&amp;" u2="&#xc3;" k="-40" />
+<hkern u1="&amp;" u2="&#x178;" k="30" />
+<hkern u1="&amp;" u2="&#xc2;" k="-40" />
+<hkern u1="&amp;" u2="&#xc1;" k="-40" />
+<hkern u1="(" u2="&#xdd;" k="-50" />
+<hkern u1="(" u2="V" k="-30" />
+<hkern u1="(" u2="W" k="-10" />
+<hkern u1="(" u2="X" k="-40" />
+<hkern u1="(" u2="Y" k="-50" />
+<hkern u1="(" u2="g" k="-20" />
+<hkern u1="(" u2="j" k="-150" />
+<hkern u1="(" u2="&#x178;" k="-50" />
+<hkern u1="*" u2="A" k="60" />
+<hkern u1="*" u2="&#xc4;" k="60" />
+<hkern u1="*" u2="&#xc5;" k="60" />
+<hkern u1="*" u2="&#xc6;" k="60" />
+<hkern u1="*" u2="&#xc0;" k="60" />
+<hkern u1="*" u2="&#xc3;" k="60" />
+<hkern u1="*" u2="&#xc2;" k="60" />
+<hkern u1="*" u2="&#xc1;" k="60" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2c;" u2="0" k="40" />
+<hkern u1="&#x2c;" u2="1" k="60" />
+<hkern u1="&#x2c;" u2="4" k="50" />
+<hkern u1="&#x2c;" u2="6" k="30" />
+<hkern u1="&#x2c;" u2="8" k="15" />
+<hkern u1="&#x2c;" u2="9" k="10" />
+<hkern u1="&#x2c;" u2="A" k="-30" />
+<hkern u1="&#x2c;" u2="C" k="40" />
+<hkern u1="&#x2c;" u2="G" k="40" />
+<hkern u1="&#x2c;" u2="O" k="40" />
+<hkern u1="&#x2c;" u2="Q" k="40" />
+<hkern u1="&#x2c;" u2="T" k="60" />
+<hkern u1="&#x2c;" u2="U" k="10" />
+<hkern u1="&#x2c;" u2="V" k="60" />
+<hkern u1="&#x2c;" u2="W" k="20" />
+<hkern u1="&#x2c;" u2="Y" k="50" />
+<hkern u1="&#x2c;" u2="a" k="20" />
+<hkern u1="&#x2c;" u2="c" k="20" />
+<hkern u1="&#x2c;" u2="d" k="20" />
+<hkern u1="&#x2c;" u2="e" k="20" />
+<hkern u1="&#x2c;" u2="m" k="20" />
+<hkern u1="&#x2c;" u2="n" k="20" />
+<hkern u1="&#x2c;" u2="o" k="20" />
+<hkern u1="&#x2c;" u2="p" k="20" />
+<hkern u1="&#x2c;" u2="q" k="20" />
+<hkern u1="&#x2c;" u2="r" k="20" />
+<hkern u1="&#x2c;" u2="t" k="40" />
+<hkern u1="&#x2c;" u2="u" k="20" />
+<hkern u1="&#x2c;" u2="v" k="30" />
+<hkern u1="&#x2c;" u2="w" k="10" />
+<hkern u1="&#x2c;" u2="y" k="30" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2c;" u2="&#x152;" k="40" />
+<hkern u1="&#x2c;" u2="&#x153;" k="20" />
+<hkern u1="&#x2c;" u2="&#x178;" k="50" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2c;" u2="&#xda;" k="10" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2c;" u2="&#x131;" k="20" />
+<hkern u1="-" u2="&#xdd;" k="50" />
+<hkern u1="-" u2="&#x17d;" k="10" />
+<hkern u1="-" u2="1" k="20" />
+<hkern u1="-" u2="7" k="30" />
+<hkern u1="-" u2="A" k="-10" />
+<hkern u1="-" u2="T" k="70" />
+<hkern u1="-" u2="V" k="50" />
+<hkern u1="-" u2="W" k="40" />
+<hkern u1="-" u2="X" k="30" />
+<hkern u1="-" u2="Y" k="50" />
+<hkern u1="-" u2="Z" k="10" />
+<hkern u1="-" u2="a" k="10" />
+<hkern u1="-" u2="c" k="10" />
+<hkern u1="-" u2="d" k="10" />
+<hkern u1="-" u2="e" k="10" />
+<hkern u1="-" u2="o" k="10" />
+<hkern u1="-" u2="q" k="10" />
+<hkern u1="-" u2="v" k="10" />
+<hkern u1="-" u2="x" k="30" />
+<hkern u1="-" u2="y" k="10" />
+<hkern u1="-" u2="z" k="30" />
+<hkern u1="-" u2="&#xc4;" k="-10" />
+<hkern u1="-" u2="&#xc5;" k="-10" />
+<hkern u1="-" u2="&#xe7;" k="10" />
+<hkern u1="-" u2="&#xc6;" k="-10" />
+<hkern u1="-" u2="&#xe6;" k="10" />
+<hkern u1="-" u2="&#xc0;" k="-10" />
+<hkern u1="-" u2="&#xc3;" k="-10" />
+<hkern u1="-" u2="&#x153;" k="10" />
+<hkern u1="-" u2="&#x178;" k="50" />
+<hkern u1="-" u2="&#xc2;" k="-10" />
+<hkern u1="-" u2="&#xc1;" k="-10" />
+<hkern u1="." u2="&#xdd;" k="50" />
+<hkern u1="." u2="0" k="40" />
+<hkern u1="." u2="1" k="60" />
+<hkern u1="." u2="4" k="50" />
+<hkern u1="." u2="6" k="30" />
+<hkern u1="." u2="8" k="15" />
+<hkern u1="." u2="9" k="10" />
+<hkern u1="." u2="A" k="-30" />
+<hkern u1="." u2="C" k="40" />
+<hkern u1="." u2="G" k="40" />
+<hkern u1="." u2="O" k="40" />
+<hkern u1="." u2="Q" k="40" />
+<hkern u1="." u2="T" k="60" />
+<hkern u1="." u2="U" k="10" />
+<hkern u1="." u2="V" k="60" />
+<hkern u1="." u2="W" k="20" />
+<hkern u1="." u2="Y" k="50" />
+<hkern u1="." u2="a" k="20" />
+<hkern u1="." u2="c" k="20" />
+<hkern u1="." u2="d" k="20" />
+<hkern u1="." u2="e" k="20" />
+<hkern u1="." u2="m" k="20" />
+<hkern u1="." u2="n" k="20" />
+<hkern u1="." u2="o" k="20" />
+<hkern u1="." u2="p" k="20" />
+<hkern u1="." u2="q" k="20" />
+<hkern u1="." u2="r" k="20" />
+<hkern u1="." u2="t" k="40" />
+<hkern u1="." u2="u" k="20" />
+<hkern u1="." u2="v" k="30" />
+<hkern u1="." u2="w" k="10" />
+<hkern u1="." u2="y" k="30" />
+<hkern u1="." u2="&#xc4;" k="-30" />
+<hkern u1="." u2="&#xc5;" k="-30" />
+<hkern u1="." u2="&#xd6;" k="40" />
+<hkern u1="." u2="&#xdc;" k="10" />
+<hkern u1="." u2="&#xe7;" k="20" />
+<hkern u1="." u2="&#xc6;" k="-30" />
+<hkern u1="." u2="&#xd8;" k="40" />
+<hkern u1="." u2="&#xe6;" k="20" />
+<hkern u1="." u2="&#xc0;" k="-30" />
+<hkern u1="." u2="&#xc3;" k="-30" />
+<hkern u1="." u2="&#xd5;" k="40" />
+<hkern u1="." u2="&#x152;" k="40" />
+<hkern u1="." u2="&#x153;" k="20" />
+<hkern u1="." u2="&#x178;" k="50" />
+<hkern u1="." u2="&#xc2;" k="-30" />
+<hkern u1="." u2="&#xc1;" k="-30" />
+<hkern u1="." u2="&#xd3;" k="40" />
+<hkern u1="." u2="&#xd4;" k="40" />
+<hkern u1="." u2="&#xd2;" k="40" />
+<hkern u1="." u2="&#xda;" k="10" />
+<hkern u1="." u2="&#xdb;" k="10" />
+<hkern u1="." u2="&#xd9;" k="10" />
+<hkern u1="." u2="&#x131;" k="20" />
+<hkern u1="/" u2="1" k="-20" />
+<hkern u1="/" u2="4" k="20" />
+<hkern u1="/" u2="C" k="-10" />
+<hkern u1="/" u2="G" k="-10" />
+<hkern u1="/" u2="O" k="-10" />
+<hkern u1="/" u2="Q" k="-10" />
+<hkern u1="/" u2="a" k="10" />
+<hkern u1="/" u2="c" k="10" />
+<hkern u1="/" u2="d" k="10" />
+<hkern u1="/" u2="e" k="10" />
+<hkern u1="/" u2="g" k="10" />
+<hkern u1="/" u2="o" k="10" />
+<hkern u1="/" u2="q" k="10" />
+<hkern u1="/" u2="&#xd6;" k="-10" />
+<hkern u1="/" u2="&#xe7;" k="10" />
+<hkern u1="/" u2="&#xd8;" k="-10" />
+<hkern u1="/" u2="&#xe6;" k="10" />
+<hkern u1="/" u2="&#xd5;" k="-10" />
+<hkern u1="/" u2="&#x152;" k="-10" />
+<hkern u1="/" u2="&#x153;" k="10" />
+<hkern u1="/" u2="&#xd3;" k="-10" />
+<hkern u1="/" u2="&#xd4;" k="-10" />
+<hkern u1="/" u2="&#xd2;" k="-10" />
+<hkern u1="0" u2="&#x2c;" k="40" />
+<hkern u1="0" u2="." k="40" />
+<hkern u1="0" u2="7" k="20" />
+<hkern u1="0" u2="&#x2026;" k="40" />
+<hkern u1="0" u2="&#x201a;" k="40" />
+<hkern u1="0" u2="&#x201e;" k="40" />
+<hkern u1="2" u2="-" k="10" />
+<hkern u1="2" u2="4" k="30" />
+<hkern u1="2" u2="&#x2013;" k="10" />
+<hkern u1="2" u2="&#x2014;" k="10" />
+<hkern u1="3" u2="&#x2c;" k="20" />
+<hkern u1="3" u2="." k="20" />
+<hkern u1="3" u2="7" k="20" />
+<hkern u1="3" u2="&#x2026;" k="20" />
+<hkern u1="3" u2="&#x201a;" k="20" />
+<hkern u1="3" u2="&#x201e;" k="20" />
+<hkern u1="4" u2="7" k="10" />
+<hkern u1="4" u2="&#xb0;" k="40" />
+<hkern u1="4" u2="&#x2122;" k="50" />
+<hkern u1="5" u2="&#x2c;" k="10" />
+<hkern u1="5" u2="." k="10" />
+<hkern u1="5" u2="7" k="10" />
+<hkern u1="5" u2="&#x2026;" k="10" />
+<hkern u1="5" u2="&#x201a;" k="10" />
+<hkern u1="5" u2="&#x201e;" k="10" />
+<hkern u1="6" u2="&#x2c;" k="10" />
+<hkern u1="6" u2="." k="10" />
+<hkern u1="6" u2="7" k="10" />
+<hkern u1="6" u2="&#x2026;" k="10" />
+<hkern u1="6" u2="&#x201a;" k="10" />
+<hkern u1="6" u2="&#x201e;" k="10" />
+<hkern u1="7" u2="&#x2c;" k="80" />
+<hkern u1="7" u2="-" k="40" />
+<hkern u1="7" u2="." k="80" />
+<hkern u1="7" u2="0" k="10" />
+<hkern u1="7" u2="1" k="-20" />
+<hkern u1="7" u2="3" k="-20" />
+<hkern u1="7" u2="4" k="60" />
+<hkern u1="7" u2="5" k="-15" />
+<hkern u1="7" u2="9" k="-10" />
+<hkern u1="7" u2=":" k="20" />
+<hkern u1="7" u2=";" k="20" />
+<hkern u1="7" u2="&#xa2;" k="40" />
+<hkern u1="7" u2="&#x2026;" k="80" />
+<hkern u1="7" u2="&#x2013;" k="40" />
+<hkern u1="7" u2="&#x2014;" k="40" />
+<hkern u1="7" u2="&#x201a;" k="80" />
+<hkern u1="7" u2="&#x201e;" k="80" />
+<hkern u1="8" u2="&#x2c;" k="15" />
+<hkern u1="8" u2="." k="15" />
+<hkern u1="8" u2="7" k="15" />
+<hkern u1="8" u2="&#x2026;" k="15" />
+<hkern u1="8" u2="&#x201a;" k="15" />
+<hkern u1="8" u2="&#x201e;" k="15" />
+<hkern u1="9" u2="&#x2c;" k="30" />
+<hkern u1="9" u2="." k="30" />
+<hkern u1="9" u2="7" k="45" />
+<hkern u1="9" u2="&#x2026;" k="30" />
+<hkern u1="9" u2="&#x201a;" k="30" />
+<hkern u1="9" u2="&#x201e;" k="30" />
+<hkern u1=":" u2="&#xdd;" k="30" />
+<hkern u1=":" u2="1" k="30" />
+<hkern u1=":" u2="7" k="20" />
+<hkern u1=":" u2="T" k="50" />
+<hkern u1=":" u2="V" k="20" />
+<hkern u1=":" u2="W" k="20" />
+<hkern u1=":" u2="Y" k="30" />
+<hkern u1=":" u2="a" k="10" />
+<hkern u1=":" u2="c" k="10" />
+<hkern u1=":" u2="d" k="10" />
+<hkern u1=":" u2="e" k="10" />
+<hkern u1=":" u2="o" k="10" />
+<hkern u1=":" u2="q" k="10" />
+<hkern u1=":" u2="&#xe7;" k="10" />
+<hkern u1=":" u2="&#xe6;" k="10" />
+<hkern u1=":" u2="&#x153;" k="10" />
+<hkern u1=":" u2="&#x178;" k="30" />
+<hkern u1=";" u2="&#xdd;" k="30" />
+<hkern u1=";" u2="1" k="30" />
+<hkern u1=";" u2="7" k="20" />
+<hkern u1=";" u2="T" k="50" />
+<hkern u1=";" u2="V" k="20" />
+<hkern u1=";" u2="W" k="20" />
+<hkern u1=";" u2="Y" k="30" />
+<hkern u1=";" u2="a" k="10" />
+<hkern u1=";" u2="c" k="10" />
+<hkern u1=";" u2="d" k="10" />
+<hkern u1=";" u2="e" k="10" />
+<hkern u1=";" u2="o" k="10" />
+<hkern u1=";" u2="q" k="10" />
+<hkern u1=";" u2="&#xe7;" k="10" />
+<hkern u1=";" u2="&#xe6;" k="10" />
+<hkern u1=";" u2="&#x153;" k="10" />
+<hkern u1=";" u2="&#x178;" k="30" />
+<hkern u1="&gt;" u2="7" k="60" />
+<hkern u1="@" u2="&#xdd;" k="30" />
+<hkern u1="@" u2="A" k="10" />
+<hkern u1="@" u2="T" k="30" />
+<hkern u1="@" u2="W" k="20" />
+<hkern u1="@" u2="Y" k="30" />
+<hkern u1="@" u2="&#xc4;" k="10" />
+<hkern u1="@" u2="&#xc5;" k="10" />
+<hkern u1="@" u2="&#xc6;" k="10" />
+<hkern u1="@" u2="&#xc0;" k="10" />
+<hkern u1="@" u2="&#xc3;" k="10" />
+<hkern u1="@" u2="&#x178;" k="30" />
+<hkern u1="@" u2="&#xc2;" k="10" />
+<hkern u1="@" u2="&#xc1;" k="10" />
+<hkern u1="A" u2="&#x160;" k="-5" />
+<hkern u1="A" u2="&#xdd;" k="70" />
+<hkern u1="A" u2="*" k="60" />
+<hkern u1="A" u2="&#x2c;" k="-30" />
+<hkern u1="A" u2="-" k="-10" />
+<hkern u1="A" u2="." k="-30" />
+<hkern u1="A" u2="@" k="10" />
+<hkern u1="A" u2="A" k="-10" />
+<hkern u1="A" u2="C" k="40" />
+<hkern u1="A" u2="G" k="40" />
+<hkern u1="A" u2="J" k="10" />
+<hkern u1="A" u2="O" k="40" />
+<hkern u1="A" u2="Q" k="40" />
+<hkern u1="A" u2="S" k="-5" />
+<hkern u1="A" u2="T" k="130" />
+<hkern u1="A" u2="V" k="70" />
+<hkern u1="A" u2="W" k="70" />
+<hkern u1="A" u2="Y" k="70" />
+<hkern u1="A" u2="a" k="5" />
+<hkern u1="A" u2="c" k="5" />
+<hkern u1="A" u2="d" k="5" />
+<hkern u1="A" u2="e" k="5" />
+<hkern u1="A" u2="m" k="5" />
+<hkern u1="A" u2="n" k="5" />
+<hkern u1="A" u2="o" k="5" />
+<hkern u1="A" u2="p" k="5" />
+<hkern u1="A" u2="q" k="5" />
+<hkern u1="A" u2="r" k="5" />
+<hkern u1="A" u2="t" k="5" />
+<hkern u1="A" u2="u" k="5" />
+<hkern u1="A" u2="v" k="30" />
+<hkern u1="A" u2="w" k="10" />
+<hkern u1="A" u2="x" k="-20" />
+<hkern u1="A" u2="y" k="30" />
+<hkern u1="A" u2="z" k="-10" />
+<hkern u1="A" u2="&#xc4;" k="-10" />
+<hkern u1="A" u2="&#xc5;" k="-10" />
+<hkern u1="A" u2="&#xd6;" k="40" />
+<hkern u1="A" u2="&#xe7;" k="5" />
+<hkern u1="A" u2="&#xae;" k="10" />
+<hkern u1="A" u2="&#xa9;" k="10" />
+<hkern u1="A" u2="&#x2122;" k="70" />
+<hkern u1="A" u2="&#xc6;" k="-10" />
+<hkern u1="A" u2="&#xd8;" k="40" />
+<hkern u1="A" u2="&#x3c0;" k="10" />
+<hkern u1="A" u2="&#xe6;" k="5" />
+<hkern u1="A" u2="&#x2026;" k="-30" />
+<hkern u1="A" u2="&#xc0;" k="-10" />
+<hkern u1="A" u2="&#xc3;" k="-10" />
+<hkern u1="A" u2="&#xd5;" k="40" />
+<hkern u1="A" u2="&#x152;" k="40" />
+<hkern u1="A" u2="&#x153;" k="5" />
+<hkern u1="A" u2="&#x2013;" k="-10" />
+<hkern u1="A" u2="&#x2014;" k="-10" />
+<hkern u1="A" u2="&#x201c;" k="60" />
+<hkern u1="A" u2="&#x201d;" k="30" />
+<hkern u1="A" u2="&#x2018;" k="60" />
+<hkern u1="A" u2="&#x2019;" k="30" />
+<hkern u1="A" u2="&#x178;" k="70" />
+<hkern u1="A" u2="&#x201a;" k="-30" />
+<hkern u1="A" u2="&#x201e;" k="-30" />
+<hkern u1="A" u2="&#xc2;" k="-10" />
+<hkern u1="A" u2="&#xc1;" k="-10" />
+<hkern u1="A" u2="&#xd3;" k="40" />
+<hkern u1="A" u2="&#xd4;" k="40" />
+<hkern u1="A" u2="&#xd2;" k="40" />
+<hkern u1="A" u2="&#x131;" k="5" />
+<hkern u1="B" u2="&#xdd;" k="20" />
+<hkern u1="B" u2="V" k="5" />
+<hkern u1="B" u2="W" k="20" />
+<hkern u1="B" u2="Y" k="20" />
+<hkern u1="B" u2="&#x178;" k="20" />
+<hkern u1="C" u2="&#x17d;" k="10" />
+<hkern u1="C" u2="T" k="20" />
+<hkern u1="C" u2="V" k="-20" />
+<hkern u1="C" u2="X" k="-10" />
+<hkern u1="C" u2="Z" k="10" />
+<hkern u1="C" u2="a" k="-5" />
+<hkern u1="C" u2="c" k="-5" />
+<hkern u1="C" u2="d" k="-5" />
+<hkern u1="C" u2="e" k="-5" />
+<hkern u1="C" u2="o" k="-5" />
+<hkern u1="C" u2="q" k="-5" />
+<hkern u1="C" u2="t" k="5" />
+<hkern u1="C" u2="v" k="5" />
+<hkern u1="C" u2="w" k="5" />
+<hkern u1="C" u2="y" k="5" />
+<hkern u1="C" u2="z" k="10" />
+<hkern u1="C" u2="&#xe7;" k="-5" />
+<hkern u1="C" u2="&#xe6;" k="-5" />
+<hkern u1="C" u2="&#x153;" k="-5" />
+<hkern u1="D" u2="&#xdd;" k="45" />
+<hkern u1="D" u2="&#x17d;" k="45" />
+<hkern u1="D" u2="&#x2c;" k="40" />
+<hkern u1="D" u2="." k="40" />
+<hkern u1="D" u2="/" k="50" />
+<hkern u1="D" u2="?" k="20" />
+<hkern u1="D" u2="J" k="45" />
+<hkern u1="D" u2="T" k="50" />
+<hkern u1="D" u2="V" k="40" />
+<hkern u1="D" u2="W" k="60" />
+<hkern u1="D" u2="X" k="35" />
+<hkern u1="D" u2="Y" k="45" />
+<hkern u1="D" u2="Z" k="45" />
+<hkern u1="D" u2="a" k="5" />
+<hkern u1="D" u2="b" k="10" />
+<hkern u1="D" u2="c" k="5" />
+<hkern u1="D" u2="d" k="5" />
+<hkern u1="D" u2="e" k="5" />
+<hkern u1="D" u2="h" k="10" />
+<hkern u1="D" u2="k" k="10" />
+<hkern u1="D" u2="l" k="10" />
+<hkern u1="D" u2="m" k="5" />
+<hkern u1="D" u2="n" k="5" />
+<hkern u1="D" u2="o" k="5" />
+<hkern u1="D" u2="p" k="5" />
+<hkern u1="D" u2="q" k="5" />
+<hkern u1="D" u2="r" k="5" />
+<hkern u1="D" u2="u" k="5" />
+<hkern u1="D" u2="x" k="10" />
+<hkern u1="D" u2="z" k="10" />
+<hkern u1="D" u2="&#xe7;" k="5" />
+<hkern u1="D" u2="&#xe6;" k="5" />
+<hkern u1="D" u2="&#x2026;" k="40" />
+<hkern u1="D" u2="&#x153;" k="5" />
+<hkern u1="D" u2="&#x201c;" k="20" />
+<hkern u1="D" u2="&#x2018;" k="20" />
+<hkern u1="D" u2="&#x178;" k="45" />
+<hkern u1="D" u2="&#x201a;" k="40" />
+<hkern u1="D" u2="&#x201e;" k="40" />
+<hkern u1="D" u2="&#x131;" k="5" />
+<hkern u1="E" u2="@" k="20" />
+<hkern u1="E" u2="T" k="-15" />
+<hkern u1="E" u2="a" k="25" />
+<hkern u1="E" u2="c" k="25" />
+<hkern u1="E" u2="d" k="25" />
+<hkern u1="E" u2="e" k="25" />
+<hkern u1="E" u2="f" k="10" />
+<hkern u1="E" u2="g" k="5" />
+<hkern u1="E" u2="o" k="25" />
+<hkern u1="E" u2="q" k="25" />
+<hkern u1="E" u2="v" k="20" />
+<hkern u1="E" u2="y" k="20" />
+<hkern u1="E" u2="&#xe7;" k="25" />
+<hkern u1="E" u2="&#xae;" k="20" />
+<hkern u1="E" u2="&#xa9;" k="20" />
+<hkern u1="E" u2="&#x3c0;" k="20" />
+<hkern u1="E" u2="&#xe6;" k="25" />
+<hkern u1="E" u2="&#x153;" k="25" />
+<hkern u1="F" u2="&#x161;" k="40" />
+<hkern u1="F" u2="&#xdd;" k="-25" />
+<hkern u1="F" u2="&amp;" k="40" />
+<hkern u1="F" u2="&#x2c;" k="90" />
+<hkern u1="F" u2="-" k="50" />
+<hkern u1="F" u2="." k="90" />
+<hkern u1="F" u2="/" k="80" />
+<hkern u1="F" u2=":" k="30" />
+<hkern u1="F" u2=";" k="30" />
+<hkern u1="F" u2="@" k="20" />
+<hkern u1="F" u2="A" k="40" />
+<hkern u1="F" u2="J" k="90" />
+<hkern u1="F" u2="T" k="-30" />
+<hkern u1="F" u2="V" k="-20" />
+<hkern u1="F" u2="W" k="-5" />
+<hkern u1="F" u2="X" k="-10" />
+<hkern u1="F" u2="Y" k="-25" />
+<hkern u1="F" u2="a" k="50" />
+<hkern u1="F" u2="b" k="10" />
+<hkern u1="F" u2="c" k="50" />
+<hkern u1="F" u2="d" k="50" />
+<hkern u1="F" u2="e" k="50" />
+<hkern u1="F" u2="f" k="20" />
+<hkern u1="F" u2="g" k="30" />
+<hkern u1="F" u2="h" k="10" />
+<hkern u1="F" u2="i" k="10" />
+<hkern u1="F" u2="j" k="10" />
+<hkern u1="F" u2="k" k="10" />
+<hkern u1="F" u2="l" k="10" />
+<hkern u1="F" u2="m" k="40" />
+<hkern u1="F" u2="n" k="40" />
+<hkern u1="F" u2="o" k="50" />
+<hkern u1="F" u2="p" k="40" />
+<hkern u1="F" u2="q" k="50" />
+<hkern u1="F" u2="r" k="40" />
+<hkern u1="F" u2="s" k="40" />
+<hkern u1="F" u2="t" k="20" />
+<hkern u1="F" u2="u" k="40" />
+<hkern u1="F" u2="v" k="20" />
+<hkern u1="F" u2="w" k="20" />
+<hkern u1="F" u2="x" k="40" />
+<hkern u1="F" u2="y" k="20" />
+<hkern u1="F" u2="z" k="40" />
+<hkern u1="F" u2="&#xc4;" k="40" />
+<hkern u1="F" u2="&#xc5;" k="40" />
+<hkern u1="F" u2="&#xe7;" k="50" />
+<hkern u1="F" u2="&#xae;" k="20" />
+<hkern u1="F" u2="&#xa9;" k="20" />
+<hkern u1="F" u2="&#xc6;" k="40" />
+<hkern u1="F" u2="&#x3c0;" k="20" />
+<hkern u1="F" u2="&#xe6;" k="50" />
+<hkern u1="F" u2="&#x2026;" k="90" />
+<hkern u1="F" u2="&#xc0;" k="40" />
+<hkern u1="F" u2="&#xc3;" k="40" />
+<hkern u1="F" u2="&#x153;" k="50" />
+<hkern u1="F" u2="&#x2013;" k="50" />
+<hkern u1="F" u2="&#x2014;" k="50" />
+<hkern u1="F" u2="&#x178;" k="-25" />
+<hkern u1="F" u2="&#x201a;" k="90" />
+<hkern u1="F" u2="&#x201e;" k="90" />
+<hkern u1="F" u2="&#xc2;" k="40" />
+<hkern u1="F" u2="&#xc1;" k="40" />
+<hkern u1="F" u2="&#x131;" k="40" />
+<hkern u1="G" u2="&#xdd;" k="15" />
+<hkern u1="G" u2="&#x2c;" k="-20" />
+<hkern u1="G" u2="." k="-20" />
+<hkern u1="G" u2="T" k="25" />
+<hkern u1="G" u2="V" k="10" />
+<hkern u1="G" u2="W" k="15" />
+<hkern u1="G" u2="Y" k="15" />
+<hkern u1="G" u2="t" k="10" />
+<hkern u1="G" u2="v" k="10" />
+<hkern u1="G" u2="y" k="10" />
+<hkern u1="G" u2="&#x2122;" k="30" />
+<hkern u1="G" u2="&#x2026;" k="-20" />
+<hkern u1="G" u2="&#x178;" k="15" />
+<hkern u1="G" u2="&#x201a;" k="-20" />
+<hkern u1="G" u2="&#x201e;" k="-20" />
+<hkern u1="H" u2="/" k="20" />
+<hkern u1="H" u2="v" k="-20" />
+<hkern u1="H" u2="y" k="-20" />
+<hkern u1="I" u2="/" k="20" />
+<hkern u1="I" u2="v" k="-20" />
+<hkern u1="I" u2="y" k="-20" />
+<hkern u1="J" u2="&#x2c;" k="10" />
+<hkern u1="J" u2="." k="10" />
+<hkern u1="J" u2="A" k="-20" />
+<hkern u1="J" u2="J" k="10" />
+<hkern u1="J" u2="v" k="-20" />
+<hkern u1="J" u2="y" k="-20" />
+<hkern u1="J" u2="&#xc4;" k="-20" />
+<hkern u1="J" u2="&#xc5;" k="-20" />
+<hkern u1="J" u2="&#xc6;" k="-20" />
+<hkern u1="J" u2="&#x2026;" k="10" />
+<hkern u1="J" u2="&#xc0;" k="-20" />
+<hkern u1="J" u2="&#xc3;" k="-20" />
+<hkern u1="J" u2="&#x201a;" k="10" />
+<hkern u1="J" u2="&#x201e;" k="10" />
+<hkern u1="J" u2="&#xc2;" k="-20" />
+<hkern u1="J" u2="&#xc1;" k="-20" />
+<hkern u1="K" u2="&#xf0;" k="20" />
+<hkern u1="K" u2="&#x160;" k="5" />
+<hkern u1="K" u2="&#xdd;" k="-15" />
+<hkern u1="K" u2="&#x17d;" k="5" />
+<hkern u1="K" u2="&amp;" k="20" />
+<hkern u1="K" u2="-" k="40" />
+<hkern u1="K" u2="@" k="30" />
+<hkern u1="K" u2="C" k="30" />
+<hkern u1="K" u2="G" k="30" />
+<hkern u1="K" u2="O" k="30" />
+<hkern u1="K" u2="Q" k="30" />
+<hkern u1="K" u2="S" k="5" />
+<hkern u1="K" u2="T" k="-5" />
+<hkern u1="K" u2="U" k="10" />
+<hkern u1="K" u2="V" k="-15" />
+<hkern u1="K" u2="W" k="10" />
+<hkern u1="K" u2="X" k="-10" />
+<hkern u1="K" u2="Y" k="-15" />
+<hkern u1="K" u2="Z" k="5" />
+<hkern u1="K" u2="a" k="20" />
+<hkern u1="K" u2="c" k="20" />
+<hkern u1="K" u2="d" k="20" />
+<hkern u1="K" u2="e" k="20" />
+<hkern u1="K" u2="f" k="5" />
+<hkern u1="K" u2="g" k="10" />
+<hkern u1="K" u2="o" k="20" />
+<hkern u1="K" u2="q" k="20" />
+<hkern u1="K" u2="t" k="10" />
+<hkern u1="K" u2="v" k="20" />
+<hkern u1="K" u2="y" k="20" />
+<hkern u1="K" u2="&#xd6;" k="30" />
+<hkern u1="K" u2="&#xdc;" k="10" />
+<hkern u1="K" u2="&#xe7;" k="20" />
+<hkern u1="K" u2="&#xae;" k="30" />
+<hkern u1="K" u2="&#xa9;" k="30" />
+<hkern u1="K" u2="&#xd8;" k="30" />
+<hkern u1="K" u2="&#x3c0;" k="30" />
+<hkern u1="K" u2="&#xe6;" k="20" />
+<hkern u1="K" u2="&#xd5;" k="30" />
+<hkern u1="K" u2="&#x152;" k="30" />
+<hkern u1="K" u2="&#x153;" k="20" />
+<hkern u1="K" u2="&#x2013;" k="40" />
+<hkern u1="K" u2="&#x2014;" k="40" />
+<hkern u1="K" u2="&#x178;" k="-15" />
+<hkern u1="K" u2="&#xd3;" k="30" />
+<hkern u1="K" u2="&#xd4;" k="30" />
+<hkern u1="K" u2="&#xd2;" k="30" />
+<hkern u1="K" u2="&#xda;" k="10" />
+<hkern u1="K" u2="&#xdb;" k="10" />
+<hkern u1="K" u2="&#xd9;" k="10" />
+<hkern u1="L" u2="&#xdd;" k="80" />
+<hkern u1="L" u2="@" k="50" />
+<hkern u1="L" u2="A" k="-20" />
+<hkern u1="L" u2="C" k="50" />
+<hkern u1="L" u2="G" k="50" />
+<hkern u1="L" u2="O" k="50" />
+<hkern u1="L" u2="Q" k="50" />
+<hkern u1="L" u2="T" k="120" />
+<hkern u1="L" u2="U" k="15" />
+<hkern u1="L" u2="V" k="90" />
+<hkern u1="L" u2="W" k="70" />
+<hkern u1="L" u2="Y" k="80" />
+<hkern u1="L" u2="\" k="50" />
+<hkern u1="L" u2="a" k="20" />
+<hkern u1="L" u2="c" k="20" />
+<hkern u1="L" u2="d" k="20" />
+<hkern u1="L" u2="e" k="20" />
+<hkern u1="L" u2="o" k="20" />
+<hkern u1="L" u2="q" k="20" />
+<hkern u1="L" u2="v" k="40" />
+<hkern u1="L" u2="w" k="10" />
+<hkern u1="L" u2="y" k="40" />
+<hkern u1="L" u2="&#xc4;" k="-20" />
+<hkern u1="L" u2="&#xc5;" k="-20" />
+<hkern u1="L" u2="&#xd6;" k="50" />
+<hkern u1="L" u2="&#xdc;" k="15" />
+<hkern u1="L" u2="&#xe7;" k="20" />
+<hkern u1="L" u2="&#xae;" k="50" />
+<hkern u1="L" u2="&#xa9;" k="50" />
+<hkern u1="L" u2="&#x2122;" k="70" />
+<hkern u1="L" u2="&#xc6;" k="-20" />
+<hkern u1="L" u2="&#xd8;" k="50" />
+<hkern u1="L" u2="&#x3c0;" k="50" />
+<hkern u1="L" u2="&#xe6;" k="20" />
+<hkern u1="L" u2="&#xc0;" k="-20" />
+<hkern u1="L" u2="&#xc3;" k="-20" />
+<hkern u1="L" u2="&#xd5;" k="50" />
+<hkern u1="L" u2="&#x152;" k="50" />
+<hkern u1="L" u2="&#x153;" k="20" />
+<hkern u1="L" u2="&#x201c;" k="100" />
+<hkern u1="L" u2="&#x201d;" k="60" />
+<hkern u1="L" u2="&#x2018;" k="100" />
+<hkern u1="L" u2="&#x2019;" k="60" />
+<hkern u1="L" u2="&#x178;" k="80" />
+<hkern u1="L" u2="&#xc2;" k="-20" />
+<hkern u1="L" u2="&#xc1;" k="-20" />
+<hkern u1="L" u2="&#xd3;" k="50" />
+<hkern u1="L" u2="&#xd4;" k="50" />
+<hkern u1="L" u2="&#xd2;" k="50" />
+<hkern u1="L" u2="&#xda;" k="15" />
+<hkern u1="L" u2="&#xdb;" k="15" />
+<hkern u1="L" u2="&#xd9;" k="15" />
+<hkern u1="M" u2="/" k="20" />
+<hkern u1="M" u2="v" k="-20" />
+<hkern u1="M" u2="y" k="-20" />
+<hkern u1="N" u2="/" k="20" />
+<hkern u1="N" u2="v" k="-20" />
+<hkern u1="N" u2="y" k="-20" />
+<hkern u1="O" u2="&#xdd;" k="45" />
+<hkern u1="O" u2="&#x17d;" k="45" />
+<hkern u1="O" u2="&#x2c;" k="40" />
+<hkern u1="O" u2="." k="40" />
+<hkern u1="O" u2="/" k="50" />
+<hkern u1="O" u2="?" k="20" />
+<hkern u1="O" u2="J" k="45" />
+<hkern u1="O" u2="T" k="50" />
+<hkern u1="O" u2="V" k="40" />
+<hkern u1="O" u2="W" k="60" />
+<hkern u1="O" u2="X" k="35" />
+<hkern u1="O" u2="Y" k="45" />
+<hkern u1="O" u2="Z" k="45" />
+<hkern u1="O" u2="a" k="5" />
+<hkern u1="O" u2="b" k="10" />
+<hkern u1="O" u2="c" k="5" />
+<hkern u1="O" u2="d" k="5" />
+<hkern u1="O" u2="e" k="5" />
+<hkern u1="O" u2="h" k="10" />
+<hkern u1="O" u2="k" k="10" />
+<hkern u1="O" u2="l" k="10" />
+<hkern u1="O" u2="m" k="5" />
+<hkern u1="O" u2="n" k="5" />
+<hkern u1="O" u2="o" k="5" />
+<hkern u1="O" u2="p" k="5" />
+<hkern u1="O" u2="q" k="5" />
+<hkern u1="O" u2="r" k="5" />
+<hkern u1="O" u2="u" k="5" />
+<hkern u1="O" u2="x" k="10" />
+<hkern u1="O" u2="z" k="10" />
+<hkern u1="O" u2="&#xe7;" k="5" />
+<hkern u1="O" u2="&#xe6;" k="5" />
+<hkern u1="O" u2="&#x2026;" k="40" />
+<hkern u1="O" u2="&#x153;" k="5" />
+<hkern u1="O" u2="&#x201c;" k="20" />
+<hkern u1="O" u2="&#x2018;" k="20" />
+<hkern u1="O" u2="&#x178;" k="45" />
+<hkern u1="O" u2="&#x201a;" k="40" />
+<hkern u1="O" u2="&#x201e;" k="40" />
+<hkern u1="O" u2="&#x131;" k="5" />
+<hkern u1="P" u2="&#xdd;" k="5" />
+<hkern u1="P" u2="&#x17d;" k="25" />
+<hkern u1="P" u2="&amp;" k="20" />
+<hkern u1="P" u2="&#x2c;" k="60" />
+<hkern u1="P" u2="." k="60" />
+<hkern u1="P" u2="A" k="35" />
+<hkern u1="P" u2="J" k="85" />
+<hkern u1="P" u2="V" k="-5" />
+<hkern u1="P" u2="W" k="15" />
+<hkern u1="P" u2="X" k="-5" />
+<hkern u1="P" u2="Y" k="5" />
+<hkern u1="P" u2="Z" k="25" />
+<hkern u1="P" u2="a" k="10" />
+<hkern u1="P" u2="c" k="10" />
+<hkern u1="P" u2="d" k="10" />
+<hkern u1="P" u2="e" k="10" />
+<hkern u1="P" u2="f" k="-10" />
+<hkern u1="P" u2="o" k="10" />
+<hkern u1="P" u2="q" k="10" />
+<hkern u1="P" u2="t" k="-10" />
+<hkern u1="P" u2="v" k="-15" />
+<hkern u1="P" u2="w" k="-10" />
+<hkern u1="P" u2="x" k="-5" />
+<hkern u1="P" u2="y" k="-15" />
+<hkern u1="P" u2="&#xc4;" k="35" />
+<hkern u1="P" u2="&#xc5;" k="35" />
+<hkern u1="P" u2="&#xe7;" k="10" />
+<hkern u1="P" u2="&#xc6;" k="35" />
+<hkern u1="P" u2="&#xe6;" k="10" />
+<hkern u1="P" u2="&#x2026;" k="60" />
+<hkern u1="P" u2="&#xc0;" k="35" />
+<hkern u1="P" u2="&#xc3;" k="35" />
+<hkern u1="P" u2="&#x153;" k="10" />
+<hkern u1="P" u2="&#x178;" k="5" />
+<hkern u1="P" u2="&#x201a;" k="60" />
+<hkern u1="P" u2="&#x201e;" k="60" />
+<hkern u1="P" u2="&#xc2;" k="35" />
+<hkern u1="P" u2="&#xc1;" k="35" />
+<hkern u1="Q" u2="&#xdd;" k="45" />
+<hkern u1="Q" u2="&#x17d;" k="45" />
+<hkern u1="Q" u2="&#x2c;" k="40" />
+<hkern u1="Q" u2="." k="40" />
+<hkern u1="Q" u2="/" k="30" />
+<hkern u1="Q" u2="?" k="20" />
+<hkern u1="Q" u2="J" k="45" />
+<hkern u1="Q" u2="T" k="50" />
+<hkern u1="Q" u2="V" k="40" />
+<hkern u1="Q" u2="W" k="60" />
+<hkern u1="Q" u2="X" k="35" />
+<hkern u1="Q" u2="Y" k="45" />
+<hkern u1="Q" u2="Z" k="45" />
+<hkern u1="Q" u2="a" k="5" />
+<hkern u1="Q" u2="b" k="10" />
+<hkern u1="Q" u2="c" k="5" />
+<hkern u1="Q" u2="d" k="5" />
+<hkern u1="Q" u2="e" k="5" />
+<hkern u1="Q" u2="h" k="10" />
+<hkern u1="Q" u2="k" k="10" />
+<hkern u1="Q" u2="l" k="10" />
+<hkern u1="Q" u2="m" k="5" />
+<hkern u1="Q" u2="n" k="5" />
+<hkern u1="Q" u2="o" k="5" />
+<hkern u1="Q" u2="p" k="5" />
+<hkern u1="Q" u2="q" k="5" />
+<hkern u1="Q" u2="r" k="5" />
+<hkern u1="Q" u2="u" k="5" />
+<hkern u1="Q" u2="x" k="10" />
+<hkern u1="Q" u2="z" k="10" />
+<hkern u1="Q" u2="&#xe7;" k="5" />
+<hkern u1="Q" u2="&#xe6;" k="5" />
+<hkern u1="Q" u2="&#x2026;" k="40" />
+<hkern u1="Q" u2="&#x153;" k="5" />
+<hkern u1="Q" u2="&#x201c;" k="20" />
+<hkern u1="Q" u2="&#x2018;" k="20" />
+<hkern u1="Q" u2="&#x178;" k="45" />
+<hkern u1="Q" u2="&#x201a;" k="40" />
+<hkern u1="Q" u2="&#x201e;" k="40" />
+<hkern u1="Q" u2="&#x131;" k="5" />
+<hkern u1="R" u2="&#xdd;" k="-15" />
+<hkern u1="R" u2="&amp;" k="-10" />
+<hkern u1="R" u2="A" k="-20" />
+<hkern u1="R" u2="J" k="10" />
+<hkern u1="R" u2="T" k="10" />
+<hkern u1="R" u2="V" k="-20" />
+<hkern u1="R" u2="X" k="-10" />
+<hkern u1="R" u2="Y" k="-15" />
+<hkern u1="R" u2="v" k="-30" />
+<hkern u1="R" u2="w" k="-10" />
+<hkern u1="R" u2="y" k="-30" />
+<hkern u1="R" u2="&#xc4;" k="-20" />
+<hkern u1="R" u2="&#xc5;" k="-20" />
+<hkern u1="R" u2="&#xc6;" k="-20" />
+<hkern u1="R" u2="&#xc0;" k="-20" />
+<hkern u1="R" u2="&#xc3;" k="-20" />
+<hkern u1="R" u2="&#x178;" k="-15" />
+<hkern u1="R" u2="&#xc2;" k="-20" />
+<hkern u1="R" u2="&#xc1;" k="-20" />
+<hkern u1="S" u2="&#xdd;" k="10" />
+<hkern u1="S" u2="A" k="-5" />
+<hkern u1="S" u2="V" k="10" />
+<hkern u1="S" u2="W" k="20" />
+<hkern u1="S" u2="X" k="-5" />
+<hkern u1="S" u2="Y" k="10" />
+<hkern u1="S" u2="&#xc4;" k="-5" />
+<hkern u1="S" u2="&#xc5;" k="-5" />
+<hkern u1="S" u2="&#xc6;" k="-5" />
+<hkern u1="S" u2="&#xc0;" k="-5" />
+<hkern u1="S" u2="&#xc3;" k="-5" />
+<hkern u1="S" u2="&#x178;" k="10" />
+<hkern u1="S" u2="&#xc2;" k="-5" />
+<hkern u1="S" u2="&#xc1;" k="-5" />
+<hkern u1="T" u2="&#x161;" k="100" />
+<hkern u1="T" u2="&#xdd;" k="-20" />
+<hkern u1="T" u2="&#x17d;" k="10" />
+<hkern u1="T" u2="&amp;" k="80" />
+<hkern u1="T" u2="&#x2c;" k="60" />
+<hkern u1="T" u2="-" k="70" />
+<hkern u1="T" u2="." k="60" />
+<hkern u1="T" u2=":" k="50" />
+<hkern u1="T" u2=";" k="50" />
+<hkern u1="T" u2="@" k="30" />
+<hkern u1="T" u2="A" k="70" />
+<hkern u1="T" u2="C" k="10" />
+<hkern u1="T" u2="G" k="10" />
+<hkern u1="T" u2="J" k="85" />
+<hkern u1="T" u2="O" k="10" />
+<hkern u1="T" u2="Q" k="10" />
+<hkern u1="T" u2="T" k="-10" />
+<hkern u1="T" u2="V" k="-20" />
+<hkern u1="T" u2="X" k="-10" />
+<hkern u1="T" u2="Y" k="-20" />
+<hkern u1="T" u2="Z" k="10" />
+<hkern u1="T" u2="\" k="-10" />
+<hkern u1="T" u2="a" k="95" />
+<hkern u1="T" u2="c" k="95" />
+<hkern u1="T" u2="d" k="95" />
+<hkern u1="T" u2="e" k="95" />
+<hkern u1="T" u2="f" k="10" />
+<hkern u1="T" u2="g" k="95" />
+<hkern u1="T" u2="m" k="80" />
+<hkern u1="T" u2="n" k="80" />
+<hkern u1="T" u2="o" k="95" />
+<hkern u1="T" u2="p" k="80" />
+<hkern u1="T" u2="q" k="95" />
+<hkern u1="T" u2="r" k="80" />
+<hkern u1="T" u2="s" k="100" />
+<hkern u1="T" u2="t" k="20" />
+<hkern u1="T" u2="u" k="80" />
+<hkern u1="T" u2="v" k="70" />
+<hkern u1="T" u2="w" k="60" />
+<hkern u1="T" u2="x" k="80" />
+<hkern u1="T" u2="y" k="70" />
+<hkern u1="T" u2="z" k="80" />
+<hkern u1="T" u2="&#xc4;" k="50" />
+<hkern u1="T" u2="&#xc5;" k="50" />
+<hkern u1="T" u2="&#xd6;" k="10" />
+<hkern u1="T" u2="&#xe7;" k="95" />
+<hkern u1="T" u2="&#xae;" k="30" />
+<hkern u1="T" u2="&#xa9;" k="30" />
+<hkern u1="T" u2="&#xc6;" k="50" />
+<hkern u1="T" u2="&#xd8;" k="10" />
+<hkern u1="T" u2="&#x3c0;" k="30" />
+<hkern u1="T" u2="&#xe6;" k="95" />
+<hkern u1="T" u2="&#xbf;" k="50" />
+<hkern u1="T" u2="&#xab;" k="110" />
+<hkern u1="T" u2="&#xbb;" k="40" />
+<hkern u1="T" u2="&#x2026;" k="60" />
+<hkern u1="T" u2="&#xc0;" k="50" />
+<hkern u1="T" u2="&#xc3;" k="50" />
+<hkern u1="T" u2="&#xd5;" k="10" />
+<hkern u1="T" u2="&#x152;" k="10" />
+<hkern u1="T" u2="&#x153;" k="95" />
+<hkern u1="T" u2="&#x2013;" k="70" />
+<hkern u1="T" u2="&#x2014;" k="70" />
+<hkern u1="T" u2="&#x178;" k="-20" />
+<hkern u1="T" u2="&#x2039;" k="110" />
+<hkern u1="T" u2="&#x203a;" k="40" />
+<hkern u1="T" u2="&#x201a;" k="60" />
+<hkern u1="T" u2="&#x201e;" k="60" />
+<hkern u1="T" u2="&#xc2;" k="50" />
+<hkern u1="T" u2="&#xc1;" k="50" />
+<hkern u1="T" u2="&#xd3;" k="10" />
+<hkern u1="T" u2="&#xd4;" k="10" />
+<hkern u1="T" u2="&#xd2;" k="10" />
+<hkern u1="T" u2="&#x131;" k="80" />
+<hkern u1="U" u2="&#x2c;" k="10" />
+<hkern u1="U" u2="." k="10" />
+<hkern u1="U" u2="A" k="-20" />
+<hkern u1="U" u2="J" k="10" />
+<hkern u1="U" u2="v" k="-20" />
+<hkern u1="U" u2="y" k="-20" />
+<hkern u1="U" u2="&#xc4;" k="-20" />
+<hkern u1="U" u2="&#xc5;" k="-20" />
+<hkern u1="U" u2="&#xc6;" k="-20" />
+<hkern u1="U" u2="&#x2026;" k="10" />
+<hkern u1="U" u2="&#xc0;" k="-20" />
+<hkern u1="U" u2="&#xc3;" k="-20" />
+<hkern u1="U" u2="&#x201a;" k="10" />
+<hkern u1="U" u2="&#x201e;" k="10" />
+<hkern u1="U" u2="&#xc2;" k="-20" />
+<hkern u1="U" u2="&#xc1;" k="-20" />
+<hkern u1="V" u2="&#x160;" k="-30" />
+<hkern u1="V" u2="&#x161;" k="40" />
+<hkern u1="V" u2="&#xdd;" k="-30" />
+<hkern u1="V" u2="&amp;" k="40" />
+<hkern u1="V" u2=")" k="-30" />
+<hkern u1="V" u2="&#x2c;" k="60" />
+<hkern u1="V" u2="-" k="60" />
+<hkern u1="V" u2="." k="60" />
+<hkern u1="V" u2=":" k="20" />
+<hkern u1="V" u2=";" k="20" />
+<hkern u1="V" u2="A" k="30" />
+<hkern u1="V" u2="J" k="50" />
+<hkern u1="V" u2="S" k="-30" />
+<hkern u1="V" u2="T" k="-20" />
+<hkern u1="V" u2="V" k="-20" />
+<hkern u1="V" u2="W" k="5" />
+<hkern u1="V" u2="X" k="-20" />
+<hkern u1="V" u2="Y" k="-30" />
+<hkern u1="V" u2="]" k="-30" />
+<hkern u1="V" u2="a" k="40" />
+<hkern u1="V" u2="b" k="10" />
+<hkern u1="V" u2="c" k="40" />
+<hkern u1="V" u2="d" k="40" />
+<hkern u1="V" u2="e" k="40" />
+<hkern u1="V" u2="f" k="10" />
+<hkern u1="V" u2="g" k="35" />
+<hkern u1="V" u2="h" k="10" />
+<hkern u1="V" u2="k" k="10" />
+<hkern u1="V" u2="l" k="10" />
+<hkern u1="V" u2="m" k="30" />
+<hkern u1="V" u2="n" k="30" />
+<hkern u1="V" u2="o" k="40" />
+<hkern u1="V" u2="p" k="30" />
+<hkern u1="V" u2="q" k="40" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="s" k="40" />
+<hkern u1="V" u2="t" k="10" />
+<hkern u1="V" u2="u" k="30" />
+<hkern u1="V" u2="v" k="10" />
+<hkern u1="V" u2="w" k="10" />
+<hkern u1="V" u2="x" k="25" />
+<hkern u1="V" u2="y" k="10" />
+<hkern u1="V" u2="}" k="-30" />
+<hkern u1="V" u2="&#xc4;" k="30" />
+<hkern u1="V" u2="&#xc5;" k="30" />
+<hkern u1="V" u2="&#xe7;" k="40" />
+<hkern u1="V" u2="&#xc6;" k="30" />
+<hkern u1="V" u2="&#xe6;" k="40" />
+<hkern u1="V" u2="&#xab;" k="50" />
+<hkern u1="V" u2="&#xbb;" k="20" />
+<hkern u1="V" u2="&#x2026;" k="60" />
+<hkern u1="V" u2="&#xc0;" k="30" />
+<hkern u1="V" u2="&#xc3;" k="30" />
+<hkern u1="V" u2="&#x153;" k="40" />
+<hkern u1="V" u2="&#x2013;" k="60" />
+<hkern u1="V" u2="&#x2014;" k="60" />
+<hkern u1="V" u2="&#x178;" k="-30" />
+<hkern u1="V" u2="&#x2039;" k="50" />
+<hkern u1="V" u2="&#x203a;" k="20" />
+<hkern u1="V" u2="&#x201a;" k="60" />
+<hkern u1="V" u2="&#x201e;" k="60" />
+<hkern u1="V" u2="&#xc2;" k="30" />
+<hkern u1="V" u2="&#xc1;" k="30" />
+<hkern u1="V" u2="&#x131;" k="30" />
+<hkern u1="W" u2="&#x160;" k="-20" />
+<hkern u1="W" u2="&#x161;" k="50" />
+<hkern u1="W" u2="&amp;" k="30" />
+<hkern u1="W" u2=")" k="-10" />
+<hkern u1="W" u2="&#x2c;" k="20" />
+<hkern u1="W" u2="-" k="40" />
+<hkern u1="W" u2="." k="20" />
+<hkern u1="W" u2=":" k="20" />
+<hkern u1="W" u2=";" k="20" />
+<hkern u1="W" u2="@" k="20" />
+<hkern u1="W" u2="A" k="30" />
+<hkern u1="W" u2="C" k="20" />
+<hkern u1="W" u2="G" k="20" />
+<hkern u1="W" u2="J" k="40" />
+<hkern u1="W" u2="O" k="20" />
+<hkern u1="W" u2="Q" k="20" />
+<hkern u1="W" u2="S" k="-20" />
+<hkern u1="W" u2="V" k="5" />
+<hkern u1="W" u2="X" k="10" />
+<hkern u1="W" u2="]" k="-10" />
+<hkern u1="W" u2="a" k="45" />
+<hkern u1="W" u2="b" k="10" />
+<hkern u1="W" u2="c" k="45" />
+<hkern u1="W" u2="d" k="45" />
+<hkern u1="W" u2="e" k="45" />
+<hkern u1="W" u2="f" k="15" />
+<hkern u1="W" u2="g" k="50" />
+<hkern u1="W" u2="h" k="10" />
+<hkern u1="W" u2="i" k="20" />
+<hkern u1="W" u2="j" k="20" />
+<hkern u1="W" u2="k" k="10" />
+<hkern u1="W" u2="l" k="10" />
+<hkern u1="W" u2="m" k="35" />
+<hkern u1="W" u2="n" k="35" />
+<hkern u1="W" u2="o" k="45" />
+<hkern u1="W" u2="p" k="35" />
+<hkern u1="W" u2="q" k="45" />
+<hkern u1="W" u2="r" k="35" />
+<hkern u1="W" u2="s" k="50" />
+<hkern u1="W" u2="t" k="30" />
+<hkern u1="W" u2="u" k="35" />
+<hkern u1="W" u2="v" k="30" />
+<hkern u1="W" u2="w" k="30" />
+<hkern u1="W" u2="x" k="30" />
+<hkern u1="W" u2="y" k="30" />
+<hkern u1="W" u2="z" k="30" />
+<hkern u1="W" u2="}" k="-10" />
+<hkern u1="W" u2="&#xc4;" k="30" />
+<hkern u1="W" u2="&#xc5;" k="30" />
+<hkern u1="W" u2="&#xd6;" k="20" />
+<hkern u1="W" u2="&#xe7;" k="45" />
+<hkern u1="W" u2="&#xae;" k="20" />
+<hkern u1="W" u2="&#xa9;" k="20" />
+<hkern u1="W" u2="&#xc6;" k="30" />
+<hkern u1="W" u2="&#xd8;" k="20" />
+<hkern u1="W" u2="&#x3c0;" k="20" />
+<hkern u1="W" u2="&#xe6;" k="45" />
+<hkern u1="W" u2="&#xab;" k="40" />
+<hkern u1="W" u2="&#xbb;" k="20" />
+<hkern u1="W" u2="&#x2026;" k="20" />
+<hkern u1="W" u2="&#xc0;" k="30" />
+<hkern u1="W" u2="&#xc3;" k="30" />
+<hkern u1="W" u2="&#xd5;" k="20" />
+<hkern u1="W" u2="&#x152;" k="20" />
+<hkern u1="W" u2="&#x153;" k="45" />
+<hkern u1="W" u2="&#x2013;" k="40" />
+<hkern u1="W" u2="&#x2014;" k="40" />
+<hkern u1="W" u2="&#x2039;" k="40" />
+<hkern u1="W" u2="&#x203a;" k="20" />
+<hkern u1="W" u2="&#x201a;" k="20" />
+<hkern u1="W" u2="&#x201e;" k="20" />
+<hkern u1="W" u2="&#xc2;" k="30" />
+<hkern u1="W" u2="&#xc1;" k="30" />
+<hkern u1="W" u2="&#xd3;" k="20" />
+<hkern u1="W" u2="&#xd4;" k="20" />
+<hkern u1="W" u2="&#xd2;" k="20" />
+<hkern u1="W" u2="&#x131;" k="35" />
+<hkern u1="X" u2="&#x160;" k="-5" />
+<hkern u1="X" u2="&#xdd;" k="-20" />
+<hkern u1="X" u2="&amp;" k="20" />
+<hkern u1="X" u2=")" k="-40" />
+<hkern u1="X" u2="-" k="30" />
+<hkern u1="X" u2="A" k="-20" />
+<hkern u1="X" u2="C" k="15" />
+<hkern u1="X" u2="G" k="15" />
+<hkern u1="X" u2="O" k="15" />
+<hkern u1="X" u2="Q" k="15" />
+<hkern u1="X" u2="S" k="-5" />
+<hkern u1="X" u2="T" k="-10" />
+<hkern u1="X" u2="V" k="-20" />
+<hkern u1="X" u2="W" k="10" />
+<hkern u1="X" u2="X" k="-10" />
+<hkern u1="X" u2="Y" k="-20" />
+<hkern u1="X" u2="]" k="-40" />
+<hkern u1="X" u2="a" k="10" />
+<hkern u1="X" u2="c" k="10" />
+<hkern u1="X" u2="d" k="10" />
+<hkern u1="X" u2="e" k="10" />
+<hkern u1="X" u2="f" k="10" />
+<hkern u1="X" u2="m" k="10" />
+<hkern u1="X" u2="n" k="10" />
+<hkern u1="X" u2="o" k="10" />
+<hkern u1="X" u2="p" k="10" />
+<hkern u1="X" u2="q" k="10" />
+<hkern u1="X" u2="r" k="10" />
+<hkern u1="X" u2="t" k="10" />
+<hkern u1="X" u2="u" k="10" />
+<hkern u1="X" u2="v" k="-10" />
+<hkern u1="X" u2="w" k="10" />
+<hkern u1="X" u2="y" k="-10" />
+<hkern u1="X" u2="}" k="-40" />
+<hkern u1="X" u2="&#xc4;" k="-20" />
+<hkern u1="X" u2="&#xc5;" k="-20" />
+<hkern u1="X" u2="&#xd6;" k="15" />
+<hkern u1="X" u2="&#xe7;" k="10" />
+<hkern u1="X" u2="&#xc6;" k="-20" />
+<hkern u1="X" u2="&#xd8;" k="15" />
+<hkern u1="X" u2="&#xe6;" k="10" />
+<hkern u1="X" u2="&#xc0;" k="-20" />
+<hkern u1="X" u2="&#xc3;" k="-20" />
+<hkern u1="X" u2="&#xd5;" k="15" />
+<hkern u1="X" u2="&#x152;" k="15" />
+<hkern u1="X" u2="&#x153;" k="10" />
+<hkern u1="X" u2="&#x2013;" k="30" />
+<hkern u1="X" u2="&#x2014;" k="30" />
+<hkern u1="X" u2="&#x178;" k="-20" />
+<hkern u1="X" u2="&#xc2;" k="-20" />
+<hkern u1="X" u2="&#xc1;" k="-20" />
+<hkern u1="X" u2="&#xd3;" k="15" />
+<hkern u1="X" u2="&#xd4;" k="15" />
+<hkern u1="X" u2="&#xd2;" k="15" />
+<hkern u1="X" u2="&#x131;" k="10" />
+<hkern u1="Y" u2="&#x160;" k="-30" />
+<hkern u1="Y" u2="&#x161;" k="50" />
+<hkern u1="Y" u2="&amp;" k="30" />
+<hkern u1="Y" u2=")" k="-50" />
+<hkern u1="Y" u2="&#x2c;" k="50" />
+<hkern u1="Y" u2="-" k="50" />
+<hkern u1="Y" u2="." k="50" />
+<hkern u1="Y" u2=":" k="30" />
+<hkern u1="Y" u2=";" k="30" />
+<hkern u1="Y" u2="@" k="30" />
+<hkern u1="Y" u2="A" k="30" />
+<hkern u1="Y" u2="C" k="5" />
+<hkern u1="Y" u2="G" k="5" />
+<hkern u1="Y" u2="J" k="70" />
+<hkern u1="Y" u2="O" k="5" />
+<hkern u1="Y" u2="Q" k="5" />
+<hkern u1="Y" u2="S" k="-30" />
+<hkern u1="Y" u2="T" k="-20" />
+<hkern u1="Y" u2="V" k="-30" />
+<hkern u1="Y" u2="X" k="-20" />
+<hkern u1="Y" u2="]" k="-50" />
+<hkern u1="Y" u2="a" k="55" />
+<hkern u1="Y" u2="c" k="55" />
+<hkern u1="Y" u2="d" k="55" />
+<hkern u1="Y" u2="e" k="55" />
+<hkern u1="Y" u2="f" k="10" />
+<hkern u1="Y" u2="g" k="40" />
+<hkern u1="Y" u2="m" k="40" />
+<hkern u1="Y" u2="n" k="40" />
+<hkern u1="Y" u2="o" k="55" />
+<hkern u1="Y" u2="p" k="40" />
+<hkern u1="Y" u2="q" k="55" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="s" k="50" />
+<hkern u1="Y" u2="t" k="20" />
+<hkern u1="Y" u2="u" k="40" />
+<hkern u1="Y" u2="v" k="20" />
+<hkern u1="Y" u2="w" k="10" />
+<hkern u1="Y" u2="x" k="20" />
+<hkern u1="Y" u2="y" k="20" />
+<hkern u1="Y" u2="z" k="30" />
+<hkern u1="Y" u2="}" k="-50" />
+<hkern u1="Y" u2="&#xc4;" k="30" />
+<hkern u1="Y" u2="&#xc5;" k="30" />
+<hkern u1="Y" u2="&#xd6;" k="5" />
+<hkern u1="Y" u2="&#xe7;" k="55" />
+<hkern u1="Y" u2="&#xae;" k="30" />
+<hkern u1="Y" u2="&#xa9;" k="30" />
+<hkern u1="Y" u2="&#xc6;" k="30" />
+<hkern u1="Y" u2="&#xd8;" k="5" />
+<hkern u1="Y" u2="&#x3c0;" k="30" />
+<hkern u1="Y" u2="&#xe6;" k="55" />
+<hkern u1="Y" u2="&#xab;" k="60" />
+<hkern u1="Y" u2="&#xbb;" k="40" />
+<hkern u1="Y" u2="&#x2026;" k="50" />
+<hkern u1="Y" u2="&#xc0;" k="30" />
+<hkern u1="Y" u2="&#xc3;" k="30" />
+<hkern u1="Y" u2="&#xd5;" k="5" />
+<hkern u1="Y" u2="&#x152;" k="5" />
+<hkern u1="Y" u2="&#x153;" k="55" />
+<hkern u1="Y" u2="&#x2013;" k="50" />
+<hkern u1="Y" u2="&#x2014;" k="50" />
+<hkern u1="Y" u2="&#x2039;" k="60" />
+<hkern u1="Y" u2="&#x203a;" k="40" />
+<hkern u1="Y" u2="&#x201a;" k="50" />
+<hkern u1="Y" u2="&#x201e;" k="50" />
+<hkern u1="Y" u2="&#xc2;" k="30" />
+<hkern u1="Y" u2="&#xc1;" k="30" />
+<hkern u1="Y" u2="&#xd3;" k="5" />
+<hkern u1="Y" u2="&#xd4;" k="5" />
+<hkern u1="Y" u2="&#xd2;" k="5" />
+<hkern u1="Y" u2="&#x131;" k="40" />
+<hkern u1="Z" u2="&#xf0;" k="30" />
+<hkern u1="Z" u2="-" k="50" />
+<hkern u1="Z" u2="A" k="10" />
+<hkern u1="Z" u2="C" k="45" />
+<hkern u1="Z" u2="G" k="45" />
+<hkern u1="Z" u2="J" k="10" />
+<hkern u1="Z" u2="O" k="45" />
+<hkern u1="Z" u2="Q" k="45" />
+<hkern u1="Z" u2="T" k="10" />
+<hkern u1="Z" u2="a" k="40" />
+<hkern u1="Z" u2="b" k="10" />
+<hkern u1="Z" u2="c" k="40" />
+<hkern u1="Z" u2="d" k="40" />
+<hkern u1="Z" u2="e" k="40" />
+<hkern u1="Z" u2="f" k="20" />
+<hkern u1="Z" u2="g" k="20" />
+<hkern u1="Z" u2="h" k="10" />
+<hkern u1="Z" u2="i" k="20" />
+<hkern u1="Z" u2="j" k="15" />
+<hkern u1="Z" u2="k" k="10" />
+<hkern u1="Z" u2="l" k="10" />
+<hkern u1="Z" u2="m" k="20" />
+<hkern u1="Z" u2="n" k="20" />
+<hkern u1="Z" u2="o" k="40" />
+<hkern u1="Z" u2="p" k="20" />
+<hkern u1="Z" u2="q" k="40" />
+<hkern u1="Z" u2="r" k="20" />
+<hkern u1="Z" u2="u" k="20" />
+<hkern u1="Z" u2="v" k="30" />
+<hkern u1="Z" u2="w" k="10" />
+<hkern u1="Z" u2="y" k="30" />
+<hkern u1="Z" u2="&#xc4;" k="10" />
+<hkern u1="Z" u2="&#xc5;" k="10" />
+<hkern u1="Z" u2="&#xd6;" k="45" />
+<hkern u1="Z" u2="&#xe7;" k="40" />
+<hkern u1="Z" u2="&#xc6;" k="10" />
+<hkern u1="Z" u2="&#xd8;" k="45" />
+<hkern u1="Z" u2="&#xe6;" k="40" />
+<hkern u1="Z" u2="&#xc0;" k="10" />
+<hkern u1="Z" u2="&#xc3;" k="10" />
+<hkern u1="Z" u2="&#xd5;" k="45" />
+<hkern u1="Z" u2="&#x152;" k="45" />
+<hkern u1="Z" u2="&#x153;" k="40" />
+<hkern u1="Z" u2="&#x2013;" k="50" />
+<hkern u1="Z" u2="&#x2014;" k="50" />
+<hkern u1="Z" u2="&#xc2;" k="10" />
+<hkern u1="Z" u2="&#xc1;" k="10" />
+<hkern u1="Z" u2="&#xd3;" k="45" />
+<hkern u1="Z" u2="&#xd4;" k="45" />
+<hkern u1="Z" u2="&#xd2;" k="45" />
+<hkern u1="Z" u2="&#x131;" k="20" />
+<hkern u1="[" u2="&#xdd;" k="-50" />
+<hkern u1="[" u2="V" k="-30" />
+<hkern u1="[" u2="W" k="-10" />
+<hkern u1="[" u2="X" k="-40" />
+<hkern u1="[" u2="Y" k="-50" />
+<hkern u1="[" u2="g" k="-20" />
+<hkern u1="[" u2="j" k="-150" />
+<hkern u1="[" u2="&#x178;" k="-50" />
+<hkern u1="\" u2="&#xdd;" k="60" />
+<hkern u1="\" u2="T" k="90" />
+<hkern u1="\" u2="V" k="70" />
+<hkern u1="\" u2="W" k="50" />
+<hkern u1="\" u2="Y" k="60" />
+<hkern u1="\" u2="a" k="10" />
+<hkern u1="\" u2="c" k="10" />
+<hkern u1="\" u2="d" k="10" />
+<hkern u1="\" u2="e" k="10" />
+<hkern u1="\" u2="j" k="-130" />
+<hkern u1="\" u2="o" k="10" />
+<hkern u1="\" u2="q" k="10" />
+<hkern u1="\" u2="&#xe7;" k="10" />
+<hkern u1="\" u2="&#xe6;" k="10" />
+<hkern u1="\" u2="&#x153;" k="10" />
+<hkern u1="\" u2="&#x178;" k="60" />
+<hkern u1="a" u2="?" k="10" />
+<hkern u1="a" u2="\" k="10" />
+<hkern u1="a" u2="t" k="10" />
+<hkern u1="a" u2="v" k="5" />
+<hkern u1="a" u2="y" k="5" />
+<hkern u1="a" u2="&#x2122;" k="20" />
+<hkern u1="a" u2="&#x201c;" k="30" />
+<hkern u1="a" u2="&#x2018;" k="30" />
+<hkern u1="b" u2="&#x161;" k="-5" />
+<hkern u1="b" u2="&#x2c;" k="20" />
+<hkern u1="b" u2="." k="20" />
+<hkern u1="b" u2="/" k="10" />
+<hkern u1="b" u2=":" k="10" />
+<hkern u1="b" u2=";" k="10" />
+<hkern u1="b" u2="?" k="20" />
+<hkern u1="b" u2="\" k="10" />
+<hkern u1="b" u2="s" k="-5" />
+<hkern u1="b" u2="t" k="10" />
+<hkern u1="b" u2="v" k="-5" />
+<hkern u1="b" u2="x" k="5" />
+<hkern u1="b" u2="y" k="-5" />
+<hkern u1="b" u2="z" k="15" />
+<hkern u1="b" u2="&#x2122;" k="30" />
+<hkern u1="b" u2="&#x2026;" k="20" />
+<hkern u1="b" u2="&#x201c;" k="40" />
+<hkern u1="b" u2="&#x2018;" k="40" />
+<hkern u1="b" u2="&#x201a;" k="20" />
+<hkern u1="b" u2="&#x201e;" k="20" />
+<hkern u1="d" u2="v" k="-10" />
+<hkern u1="d" u2="y" k="-10" />
+<hkern u1="e" u2="v" k="10" />
+<hkern u1="e" u2="x" k="15" />
+<hkern u1="e" u2="y" k="10" />
+<hkern u1="f" u2="&#xf0;" k="20" />
+<hkern u1="f" u2="&amp;" k="30" />
+<hkern u1="f" u2=")" k="-70" />
+<hkern u1="f" u2="*" k="-20" />
+<hkern u1="f" u2="&#x2c;" k="80" />
+<hkern u1="f" u2="-" k="30" />
+<hkern u1="f" u2="." k="80" />
+<hkern u1="f" u2="/" k="50" />
+<hkern u1="f" u2=":" k="10" />
+<hkern u1="f" u2=";" k="10" />
+<hkern u1="f" u2="?" k="-40" />
+<hkern u1="f" u2="\" k="-40" />
+<hkern u1="f" u2="]" k="-70" />
+<hkern u1="f" u2="a" k="10" />
+<hkern u1="f" u2="c" k="10" />
+<hkern u1="f" u2="d" k="10" />
+<hkern u1="f" u2="e" k="10" />
+<hkern u1="f" u2="m" k="5" />
+<hkern u1="f" u2="n" k="5" />
+<hkern u1="f" u2="o" k="10" />
+<hkern u1="f" u2="p" k="5" />
+<hkern u1="f" u2="q" k="10" />
+<hkern u1="f" u2="r" k="5" />
+<hkern u1="f" u2="u" k="5" />
+<hkern u1="f" u2="v" k="-20" />
+<hkern u1="f" u2="y" k="-20" />
+<hkern u1="f" u2="}" k="-70" />
+<hkern u1="f" u2="&#xe7;" k="10" />
+<hkern u1="f" u2="&#x2122;" k="-40" />
+<hkern u1="f" u2="&#xe6;" k="10" />
+<hkern u1="f" u2="&#xab;" k="50" />
+<hkern u1="f" u2="&#xbb;" k="20" />
+<hkern u1="f" u2="&#x2026;" k="80" />
+<hkern u1="f" u2="&#x153;" k="10" />
+<hkern u1="f" u2="&#x2013;" k="30" />
+<hkern u1="f" u2="&#x2014;" k="30" />
+<hkern u1="f" u2="&#x201c;" k="-30" />
+<hkern u1="f" u2="&#x201d;" k="-50" />
+<hkern u1="f" u2="&#x2018;" k="-30" />
+<hkern u1="f" u2="&#x2019;" k="-50" />
+<hkern u1="f" u2="&#x2039;" k="50" />
+<hkern u1="f" u2="&#x203a;" k="20" />
+<hkern u1="f" u2="&#x201a;" k="80" />
+<hkern u1="f" u2="&#x201e;" k="80" />
+<hkern u1="f" u2="&#x131;" k="5" />
+<hkern u1="g" u2=")" k="-20" />
+<hkern u1="g" u2="/" k="-40" />
+<hkern u1="g" u2=";" k="-20" />
+<hkern u1="g" u2="]" k="-20" />
+<hkern u1="g" u2="g" k="-10" />
+<hkern u1="g" u2="j" k="-50" />
+<hkern u1="g" u2="t" k="-10" />
+<hkern u1="g" u2="}" k="-20" />
+<hkern u1="g" u2="&#x201c;" k="-20" />
+<hkern u1="g" u2="&#x201d;" k="-40" />
+<hkern u1="g" u2="&#x2018;" k="-20" />
+<hkern u1="g" u2="&#x2019;" k="-40" />
+<hkern u1="h" u2="?" k="10" />
+<hkern u1="h" u2="\" k="10" />
+<hkern u1="h" u2="t" k="10" />
+<hkern u1="h" u2="v" k="5" />
+<hkern u1="h" u2="y" k="5" />
+<hkern u1="h" u2="&#x2122;" k="20" />
+<hkern u1="h" u2="&#x201c;" k="30" />
+<hkern u1="h" u2="&#x2018;" k="30" />
+<hkern u1="j" u2="j" k="-20" />
+<hkern u1="j" u2="v" k="-20" />
+<hkern u1="j" u2="y" k="-20" />
+<hkern u1="k" u2="a" k="10" />
+<hkern u1="k" u2="c" k="10" />
+<hkern u1="k" u2="d" k="10" />
+<hkern u1="k" u2="e" k="10" />
+<hkern u1="k" u2="g" k="20" />
+<hkern u1="k" u2="o" k="10" />
+<hkern u1="k" u2="q" k="10" />
+<hkern u1="k" u2="v" k="-10" />
+<hkern u1="k" u2="y" k="-10" />
+<hkern u1="k" u2="&#xe7;" k="10" />
+<hkern u1="k" u2="&#xe6;" k="10" />
+<hkern u1="k" u2="&#x153;" k="10" />
+<hkern u1="l" u2="v" k="-10" />
+<hkern u1="l" u2="y" k="-10" />
+<hkern u1="m" u2="?" k="10" />
+<hkern u1="m" u2="\" k="10" />
+<hkern u1="m" u2="t" k="10" />
+<hkern u1="m" u2="v" k="5" />
+<hkern u1="m" u2="y" k="5" />
+<hkern u1="m" u2="&#x2122;" k="20" />
+<hkern u1="m" u2="&#x201c;" k="30" />
+<hkern u1="m" u2="&#x2018;" k="30" />
+<hkern u1="n" u2="?" k="10" />
+<hkern u1="n" u2="\" k="10" />
+<hkern u1="n" u2="t" k="10" />
+<hkern u1="n" u2="v" k="5" />
+<hkern u1="n" u2="y" k="5" />
+<hkern u1="n" u2="&#x2122;" k="20" />
+<hkern u1="n" u2="&#x201c;" k="30" />
+<hkern u1="n" u2="&#x2018;" k="30" />
+<hkern u1="o" u2="&#x161;" k="-5" />
+<hkern u1="o" u2="&#x2c;" k="20" />
+<hkern u1="o" u2="." k="20" />
+<hkern u1="o" u2="/" k="10" />
+<hkern u1="o" u2=":" k="10" />
+<hkern u1="o" u2=";" k="10" />
+<hkern u1="o" u2="?" k="20" />
+<hkern u1="o" u2="\" k="10" />
+<hkern u1="o" u2="s" k="-5" />
+<hkern u1="o" u2="t" k="10" />
+<hkern u1="o" u2="v" k="-5" />
+<hkern u1="o" u2="x" k="5" />
+<hkern u1="o" u2="y" k="-5" />
+<hkern u1="o" u2="z" k="15" />
+<hkern u1="o" u2="&#x2122;" k="30" />
+<hkern u1="o" u2="&#x2026;" k="20" />
+<hkern u1="o" u2="&#x201c;" k="40" />
+<hkern u1="o" u2="&#x2018;" k="40" />
+<hkern u1="o" u2="&#x201a;" k="20" />
+<hkern u1="o" u2="&#x201e;" k="20" />
+<hkern u1="p" u2="&#x161;" k="-5" />
+<hkern u1="p" u2="&#x2c;" k="20" />
+<hkern u1="p" u2="." k="20" />
+<hkern u1="p" u2="/" k="10" />
+<hkern u1="p" u2=":" k="10" />
+<hkern u1="p" u2=";" k="10" />
+<hkern u1="p" u2="?" k="20" />
+<hkern u1="p" u2="\" k="10" />
+<hkern u1="p" u2="s" k="-5" />
+<hkern u1="p" u2="t" k="10" />
+<hkern u1="p" u2="v" k="-5" />
+<hkern u1="p" u2="x" k="5" />
+<hkern u1="p" u2="y" k="-5" />
+<hkern u1="p" u2="z" k="15" />
+<hkern u1="p" u2="&#x2122;" k="30" />
+<hkern u1="p" u2="&#x2026;" k="20" />
+<hkern u1="p" u2="&#x201c;" k="40" />
+<hkern u1="p" u2="&#x2018;" k="40" />
+<hkern u1="p" u2="&#x201a;" k="20" />
+<hkern u1="p" u2="&#x201e;" k="20" />
+<hkern u1="q" u2="j" k="-30" />
+<hkern u1="r" u2="&#x2c;" k="50" />
+<hkern u1="r" u2="-" k="20" />
+<hkern u1="r" u2="." k="50" />
+<hkern u1="r" u2="/" k="60" />
+<hkern u1="r" u2="?" k="-20" />
+<hkern u1="r" u2="f" k="-15" />
+<hkern u1="r" u2="g" k="10" />
+<hkern u1="r" u2="t" k="-20" />
+<hkern u1="r" u2="v" k="-40" />
+<hkern u1="r" u2="w" k="-30" />
+<hkern u1="r" u2="x" k="-10" />
+<hkern u1="r" u2="y" k="-40" />
+<hkern u1="r" u2="&#x2026;" k="50" />
+<hkern u1="r" u2="&#x2013;" k="20" />
+<hkern u1="r" u2="&#x2014;" k="20" />
+<hkern u1="r" u2="&#x201c;" k="-20" />
+<hkern u1="r" u2="&#x201d;" k="-40" />
+<hkern u1="r" u2="&#x2018;" k="-20" />
+<hkern u1="r" u2="&#x2019;" k="-40" />
+<hkern u1="r" u2="&#x201a;" k="50" />
+<hkern u1="r" u2="&#x201e;" k="50" />
+<hkern u1="t" u2="&#x2c;" k="-10" />
+<hkern u1="t" u2="." k="-10" />
+<hkern u1="t" u2="a" k="5" />
+<hkern u1="t" u2="c" k="5" />
+<hkern u1="t" u2="d" k="5" />
+<hkern u1="t" u2="e" k="5" />
+<hkern u1="t" u2="o" k="5" />
+<hkern u1="t" u2="q" k="5" />
+<hkern u1="t" u2="v" k="-15" />
+<hkern u1="t" u2="x" k="-5" />
+<hkern u1="t" u2="y" k="-15" />
+<hkern u1="t" u2="&#xe7;" k="5" />
+<hkern u1="t" u2="&#xe6;" k="5" />
+<hkern u1="t" u2="&#x2026;" k="-10" />
+<hkern u1="t" u2="&#x153;" k="5" />
+<hkern u1="t" u2="&#x201c;" k="-10" />
+<hkern u1="t" u2="&#x2018;" k="-10" />
+<hkern u1="t" u2="&#x201a;" k="-10" />
+<hkern u1="t" u2="&#x201e;" k="-10" />
+<hkern u1="u" u2="v" k="-10" />
+<hkern u1="u" u2="y" k="-10" />
+<hkern u1="v" u2="&#x2c;" k="50" />
+<hkern u1="v" u2="-" k="10" />
+<hkern u1="v" u2="." k="50" />
+<hkern u1="v" u2="a" k="10" />
+<hkern u1="v" u2="c" k="10" />
+<hkern u1="v" u2="d" k="10" />
+<hkern u1="v" u2="e" k="10" />
+<hkern u1="v" u2="o" k="10" />
+<hkern u1="v" u2="q" k="10" />
+<hkern u1="v" u2="v" k="-30" />
+<hkern u1="v" u2="w" k="-10" />
+<hkern u1="v" u2="y" k="-30" />
+<hkern u1="v" u2="&#xe7;" k="10" />
+<hkern u1="v" u2="&#xe6;" k="10" />
+<hkern u1="v" u2="&#x2026;" k="50" />
+<hkern u1="v" u2="&#x153;" k="10" />
+<hkern u1="v" u2="&#x2013;" k="10" />
+<hkern u1="v" u2="&#x2014;" k="10" />
+<hkern u1="v" u2="&#x201c;" k="-30" />
+<hkern u1="v" u2="&#x201d;" k="-30" />
+<hkern u1="v" u2="&#x2018;" k="-30" />
+<hkern u1="v" u2="&#x2019;" k="-30" />
+<hkern u1="v" u2="&#x201a;" k="50" />
+<hkern u1="v" u2="&#x201e;" k="50" />
+<hkern u1="w" u2="&#x2c;" k="30" />
+<hkern u1="w" u2="." k="30" />
+<hkern u1="w" u2="v" k="-10" />
+<hkern u1="w" u2="y" k="-10" />
+<hkern u1="w" u2="&#x2026;" k="30" />
+<hkern u1="w" u2="&#x201c;" k="-20" />
+<hkern u1="w" u2="&#x201d;" k="-20" />
+<hkern u1="w" u2="&#x2018;" k="-20" />
+<hkern u1="w" u2="&#x2019;" k="-20" />
+<hkern u1="w" u2="&#x201a;" k="30" />
+<hkern u1="w" u2="&#x201e;" k="30" />
+<hkern u1="x" u2="-" k="30" />
+<hkern u1="x" u2="a" k="15" />
+<hkern u1="x" u2="c" k="15" />
+<hkern u1="x" u2="d" k="15" />
+<hkern u1="x" u2="e" k="15" />
+<hkern u1="x" u2="o" k="15" />
+<hkern u1="x" u2="q" k="15" />
+<hkern u1="x" u2="v" k="-20" />
+<hkern u1="x" u2="y" k="-20" />
+<hkern u1="x" u2="&#xe7;" k="15" />
+<hkern u1="x" u2="&#xe6;" k="15" />
+<hkern u1="x" u2="&#xab;" k="50" />
+<hkern u1="x" u2="&#x153;" k="15" />
+<hkern u1="x" u2="&#x2013;" k="30" />
+<hkern u1="x" u2="&#x2014;" k="30" />
+<hkern u1="x" u2="&#x201c;" k="-10" />
+<hkern u1="x" u2="&#x201d;" k="-20" />
+<hkern u1="x" u2="&#x2018;" k="-10" />
+<hkern u1="x" u2="&#x2019;" k="-20" />
+<hkern u1="x" u2="&#x2039;" k="50" />
+<hkern u1="y" u2="&#x2c;" k="50" />
+<hkern u1="y" u2="-" k="10" />
+<hkern u1="y" u2="." k="50" />
+<hkern u1="y" u2="a" k="10" />
+<hkern u1="y" u2="c" k="10" />
+<hkern u1="y" u2="d" k="10" />
+<hkern u1="y" u2="e" k="10" />
+<hkern u1="y" u2="o" k="10" />
+<hkern u1="y" u2="q" k="10" />
+<hkern u1="y" u2="v" k="-30" />
+<hkern u1="y" u2="w" k="-10" />
+<hkern u1="y" u2="y" k="-30" />
+<hkern u1="y" u2="&#xe7;" k="10" />
+<hkern u1="y" u2="&#xe6;" k="10" />
+<hkern u1="y" u2="&#x2026;" k="50" />
+<hkern u1="y" u2="&#x153;" k="10" />
+<hkern u1="y" u2="&#x2013;" k="10" />
+<hkern u1="y" u2="&#x2014;" k="10" />
+<hkern u1="y" u2="&#x201c;" k="-30" />
+<hkern u1="y" u2="&#x201d;" k="-30" />
+<hkern u1="y" u2="&#x2018;" k="-30" />
+<hkern u1="y" u2="&#x2019;" k="-30" />
+<hkern u1="y" u2="&#x201a;" k="50" />
+<hkern u1="y" u2="&#x201e;" k="50" />
+<hkern u1="z" u2="-" k="30" />
+<hkern u1="z" u2="a" k="20" />
+<hkern u1="z" u2="c" k="20" />
+<hkern u1="z" u2="d" k="20" />
+<hkern u1="z" u2="e" k="20" />
+<hkern u1="z" u2="g" k="10" />
+<hkern u1="z" u2="m" k="15" />
+<hkern u1="z" u2="n" k="15" />
+<hkern u1="z" u2="o" k="20" />
+<hkern u1="z" u2="p" k="15" />
+<hkern u1="z" u2="q" k="20" />
+<hkern u1="z" u2="r" k="15" />
+<hkern u1="z" u2="u" k="15" />
+<hkern u1="z" u2="v" k="-10" />
+<hkern u1="z" u2="y" k="-10" />
+<hkern u1="z" u2="&#xe7;" k="20" />
+<hkern u1="z" u2="&#xe6;" k="20" />
+<hkern u1="z" u2="&#xab;" k="60" />
+<hkern u1="z" u2="&#x153;" k="20" />
+<hkern u1="z" u2="&#x2013;" k="30" />
+<hkern u1="z" u2="&#x2014;" k="30" />
+<hkern u1="z" u2="&#x2039;" k="60" />
+<hkern u1="z" u2="&#x131;" k="15" />
+<hkern u1="{" u2="&#xdd;" k="-50" />
+<hkern u1="{" u2="V" k="-30" />
+<hkern u1="{" u2="W" k="-10" />
+<hkern u1="{" u2="X" k="-40" />
+<hkern u1="{" u2="Y" k="-50" />
+<hkern u1="{" u2="g" k="-20" />
+<hkern u1="{" u2="j" k="-150" />
+<hkern u1="{" u2="&#x178;" k="-50" />
+<hkern u1="&#xc4;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc4;" u2="&#xdd;" k="70" />
+<hkern u1="&#xc4;" u2="*" k="60" />
+<hkern u1="&#xc4;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc4;" u2="-" k="-10" />
+<hkern u1="&#xc4;" u2="." k="-30" />
+<hkern u1="&#xc4;" u2="@" k="10" />
+<hkern u1="&#xc4;" u2="A" k="-10" />
+<hkern u1="&#xc4;" u2="C" k="40" />
+<hkern u1="&#xc4;" u2="G" k="40" />
+<hkern u1="&#xc4;" u2="J" k="10" />
+<hkern u1="&#xc4;" u2="O" k="40" />
+<hkern u1="&#xc4;" u2="Q" k="40" />
+<hkern u1="&#xc4;" u2="S" k="-5" />
+<hkern u1="&#xc4;" u2="T" k="90" />
+<hkern u1="&#xc4;" u2="V" k="70" />
+<hkern u1="&#xc4;" u2="W" k="70" />
+<hkern u1="&#xc4;" u2="Y" k="70" />
+<hkern u1="&#xc4;" u2="a" k="5" />
+<hkern u1="&#xc4;" u2="c" k="5" />
+<hkern u1="&#xc4;" u2="d" k="5" />
+<hkern u1="&#xc4;" u2="e" k="5" />
+<hkern u1="&#xc4;" u2="m" k="5" />
+<hkern u1="&#xc4;" u2="n" k="5" />
+<hkern u1="&#xc4;" u2="o" k="5" />
+<hkern u1="&#xc4;" u2="p" k="5" />
+<hkern u1="&#xc4;" u2="q" k="5" />
+<hkern u1="&#xc4;" u2="r" k="5" />
+<hkern u1="&#xc4;" u2="t" k="5" />
+<hkern u1="&#xc4;" u2="u" k="5" />
+<hkern u1="&#xc4;" u2="v" k="30" />
+<hkern u1="&#xc4;" u2="w" k="10" />
+<hkern u1="&#xc4;" u2="x" k="-20" />
+<hkern u1="&#xc4;" u2="y" k="30" />
+<hkern u1="&#xc4;" u2="z" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="40" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc4;" u2="&#xae;" k="10" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="40" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="40" />
+<hkern u1="&#xc4;" u2="&#x152;" k="40" />
+<hkern u1="&#xc4;" u2="&#x153;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc4;" u2="&#x178;" k="70" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="40" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="40" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="40" />
+<hkern u1="&#xc4;" u2="&#x131;" k="5" />
+<hkern u1="&#xc5;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc5;" u2="&#xdd;" k="70" />
+<hkern u1="&#xc5;" u2="*" k="60" />
+<hkern u1="&#xc5;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc5;" u2="-" k="-10" />
+<hkern u1="&#xc5;" u2="." k="-30" />
+<hkern u1="&#xc5;" u2="@" k="10" />
+<hkern u1="&#xc5;" u2="A" k="-10" />
+<hkern u1="&#xc5;" u2="C" k="40" />
+<hkern u1="&#xc5;" u2="G" k="40" />
+<hkern u1="&#xc5;" u2="J" k="10" />
+<hkern u1="&#xc5;" u2="O" k="40" />
+<hkern u1="&#xc5;" u2="Q" k="40" />
+<hkern u1="&#xc5;" u2="S" k="-5" />
+<hkern u1="&#xc5;" u2="T" k="90" />
+<hkern u1="&#xc5;" u2="V" k="70" />
+<hkern u1="&#xc5;" u2="W" k="70" />
+<hkern u1="&#xc5;" u2="Y" k="70" />
+<hkern u1="&#xc5;" u2="a" k="5" />
+<hkern u1="&#xc5;" u2="c" k="5" />
+<hkern u1="&#xc5;" u2="d" k="5" />
+<hkern u1="&#xc5;" u2="e" k="5" />
+<hkern u1="&#xc5;" u2="m" k="5" />
+<hkern u1="&#xc5;" u2="n" k="5" />
+<hkern u1="&#xc5;" u2="o" k="5" />
+<hkern u1="&#xc5;" u2="p" k="5" />
+<hkern u1="&#xc5;" u2="q" k="5" />
+<hkern u1="&#xc5;" u2="r" k="5" />
+<hkern u1="&#xc5;" u2="t" k="5" />
+<hkern u1="&#xc5;" u2="u" k="5" />
+<hkern u1="&#xc5;" u2="v" k="30" />
+<hkern u1="&#xc5;" u2="w" k="10" />
+<hkern u1="&#xc5;" u2="x" k="-20" />
+<hkern u1="&#xc5;" u2="y" k="30" />
+<hkern u1="&#xc5;" u2="z" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="40" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc5;" u2="&#xae;" k="10" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="40" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="40" />
+<hkern u1="&#xc5;" u2="&#x152;" k="40" />
+<hkern u1="&#xc5;" u2="&#x153;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc5;" u2="&#x178;" k="70" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="40" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="40" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="40" />
+<hkern u1="&#xc5;" u2="&#x131;" k="5" />
+<hkern u1="&#xc9;" u2="@" k="20" />
+<hkern u1="&#xc9;" u2="T" k="-15" />
+<hkern u1="&#xc9;" u2="a" k="25" />
+<hkern u1="&#xc9;" u2="c" k="25" />
+<hkern u1="&#xc9;" u2="d" k="25" />
+<hkern u1="&#xc9;" u2="e" k="25" />
+<hkern u1="&#xc9;" u2="f" k="10" />
+<hkern u1="&#xc9;" u2="g" k="5" />
+<hkern u1="&#xc9;" u2="o" k="25" />
+<hkern u1="&#xc9;" u2="q" k="25" />
+<hkern u1="&#xc9;" u2="v" k="20" />
+<hkern u1="&#xc9;" u2="y" k="20" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc9;" u2="&#xae;" k="20" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc9;" u2="&#x153;" k="25" />
+<hkern u1="&#xd1;" u2="/" k="20" />
+<hkern u1="&#xd1;" u2="v" k="-20" />
+<hkern u1="&#xd1;" u2="y" k="-20" />
+<hkern u1="&#xd6;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd6;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd6;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd6;" u2="." k="40" />
+<hkern u1="&#xd6;" u2="/" k="50" />
+<hkern u1="&#xd6;" u2="?" k="20" />
+<hkern u1="&#xd6;" u2="J" k="45" />
+<hkern u1="&#xd6;" u2="T" k="50" />
+<hkern u1="&#xd6;" u2="V" k="40" />
+<hkern u1="&#xd6;" u2="W" k="60" />
+<hkern u1="&#xd6;" u2="X" k="35" />
+<hkern u1="&#xd6;" u2="Y" k="45" />
+<hkern u1="&#xd6;" u2="Z" k="45" />
+<hkern u1="&#xd6;" u2="a" k="5" />
+<hkern u1="&#xd6;" u2="b" k="10" />
+<hkern u1="&#xd6;" u2="c" k="5" />
+<hkern u1="&#xd6;" u2="d" k="5" />
+<hkern u1="&#xd6;" u2="e" k="5" />
+<hkern u1="&#xd6;" u2="h" k="10" />
+<hkern u1="&#xd6;" u2="k" k="10" />
+<hkern u1="&#xd6;" u2="l" k="10" />
+<hkern u1="&#xd6;" u2="m" k="5" />
+<hkern u1="&#xd6;" u2="n" k="5" />
+<hkern u1="&#xd6;" u2="o" k="5" />
+<hkern u1="&#xd6;" u2="p" k="5" />
+<hkern u1="&#xd6;" u2="q" k="5" />
+<hkern u1="&#xd6;" u2="r" k="5" />
+<hkern u1="&#xd6;" u2="u" k="5" />
+<hkern u1="&#xd6;" u2="x" k="10" />
+<hkern u1="&#xd6;" u2="z" k="10" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd6;" u2="&#x153;" k="5" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd6;" u2="&#x178;" k="45" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd6;" u2="&#x131;" k="5" />
+<hkern u1="&#xdc;" u2="&#x2c;" k="10" />
+<hkern u1="&#xdc;" u2="." k="10" />
+<hkern u1="&#xdc;" u2="A" k="-20" />
+<hkern u1="&#xdc;" u2="J" k="10" />
+<hkern u1="&#xdc;" u2="v" k="-20" />
+<hkern u1="&#xdc;" u2="y" k="-20" />
+<hkern u1="&#xdc;" u2="&#xc4;" k="-20" />
+<hkern u1="&#xdc;" u2="&#xc5;" k="-20" />
+<hkern u1="&#xdc;" u2="&#xc6;" k="-20" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdc;" u2="&#xc0;" k="-20" />
+<hkern u1="&#xdc;" u2="&#xc3;" k="-20" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="10" />
+<hkern u1="&#xdc;" u2="&#xc2;" k="-20" />
+<hkern u1="&#xdc;" u2="&#xc1;" k="-20" />
+<hkern u1="&#xe1;" u2="?" k="10" />
+<hkern u1="&#xe1;" u2="\" k="10" />
+<hkern u1="&#xe1;" u2="t" k="10" />
+<hkern u1="&#xe1;" u2="v" k="5" />
+<hkern u1="&#xe1;" u2="y" k="5" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe0;" u2="?" k="10" />
+<hkern u1="&#xe0;" u2="\" k="10" />
+<hkern u1="&#xe0;" u2="t" k="10" />
+<hkern u1="&#xe0;" u2="v" k="5" />
+<hkern u1="&#xe0;" u2="y" k="5" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe2;" u2="?" k="10" />
+<hkern u1="&#xe2;" u2="\" k="10" />
+<hkern u1="&#xe2;" u2="t" k="10" />
+<hkern u1="&#xe2;" u2="v" k="5" />
+<hkern u1="&#xe2;" u2="y" k="5" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe4;" u2="?" k="10" />
+<hkern u1="&#xe4;" u2="\" k="10" />
+<hkern u1="&#xe4;" u2="t" k="10" />
+<hkern u1="&#xe4;" u2="v" k="5" />
+<hkern u1="&#xe4;" u2="y" k="5" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe9;" u2="v" k="10" />
+<hkern u1="&#xe9;" u2="x" k="15" />
+<hkern u1="&#xe9;" u2="y" k="10" />
+<hkern u1="&#xe8;" u2="v" k="10" />
+<hkern u1="&#xe8;" u2="x" k="15" />
+<hkern u1="&#xe8;" u2="y" k="10" />
+<hkern u1="&#xea;" u2="v" k="10" />
+<hkern u1="&#xea;" u2="x" k="15" />
+<hkern u1="&#xea;" u2="y" k="10" />
+<hkern u1="&#xeb;" u2="v" k="10" />
+<hkern u1="&#xeb;" u2="x" k="15" />
+<hkern u1="&#xeb;" u2="y" k="10" />
+<hkern u1="&#xf1;" u2="?" k="10" />
+<hkern u1="&#xf1;" u2="\" k="10" />
+<hkern u1="&#xf1;" u2="t" k="10" />
+<hkern u1="&#xf1;" u2="v" k="5" />
+<hkern u1="&#xf1;" u2="y" k="5" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xf3;" u2="&#x161;" k="-5" />
+<hkern u1="&#xf3;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf3;" u2="." k="20" />
+<hkern u1="&#xf3;" u2="/" k="10" />
+<hkern u1="&#xf3;" u2=":" k="10" />
+<hkern u1="&#xf3;" u2=";" k="10" />
+<hkern u1="&#xf3;" u2="?" k="20" />
+<hkern u1="&#xf3;" u2="\" k="10" />
+<hkern u1="&#xf3;" u2="s" k="-5" />
+<hkern u1="&#xf3;" u2="t" k="10" />
+<hkern u1="&#xf3;" u2="v" k="-5" />
+<hkern u1="&#xf3;" u2="x" k="5" />
+<hkern u1="&#xf3;" u2="y" k="-5" />
+<hkern u1="&#xf3;" u2="z" k="15" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf4;" u2="&#x161;" k="-5" />
+<hkern u1="&#xf4;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf4;" u2="." k="20" />
+<hkern u1="&#xf4;" u2="/" k="10" />
+<hkern u1="&#xf4;" u2=":" k="10" />
+<hkern u1="&#xf4;" u2=";" k="10" />
+<hkern u1="&#xf4;" u2="?" k="20" />
+<hkern u1="&#xf4;" u2="\" k="10" />
+<hkern u1="&#xf4;" u2="s" k="-5" />
+<hkern u1="&#xf4;" u2="t" k="10" />
+<hkern u1="&#xf4;" u2="v" k="-5" />
+<hkern u1="&#xf4;" u2="x" k="5" />
+<hkern u1="&#xf4;" u2="y" k="-5" />
+<hkern u1="&#xf4;" u2="z" k="15" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf6;" u2="&#x161;" k="-5" />
+<hkern u1="&#xf6;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf6;" u2="." k="20" />
+<hkern u1="&#xf6;" u2="/" k="10" />
+<hkern u1="&#xf6;" u2=":" k="10" />
+<hkern u1="&#xf6;" u2=";" k="10" />
+<hkern u1="&#xf6;" u2="?" k="20" />
+<hkern u1="&#xf6;" u2="\" k="10" />
+<hkern u1="&#xf6;" u2="s" k="-5" />
+<hkern u1="&#xf6;" u2="t" k="10" />
+<hkern u1="&#xf6;" u2="v" k="-5" />
+<hkern u1="&#xf6;" u2="x" k="5" />
+<hkern u1="&#xf6;" u2="y" k="-5" />
+<hkern u1="&#xf6;" u2="z" k="15" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf5;" u2="&#x161;" k="-5" />
+<hkern u1="&#xf5;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf5;" u2="." k="20" />
+<hkern u1="&#xf5;" u2="/" k="10" />
+<hkern u1="&#xf5;" u2=":" k="10" />
+<hkern u1="&#xf5;" u2=";" k="10" />
+<hkern u1="&#xf5;" u2="?" k="20" />
+<hkern u1="&#xf5;" u2="\" k="10" />
+<hkern u1="&#xf5;" u2="s" k="-5" />
+<hkern u1="&#xf5;" u2="t" k="10" />
+<hkern u1="&#xf5;" u2="v" k="-5" />
+<hkern u1="&#xf5;" u2="x" k="5" />
+<hkern u1="&#xf5;" u2="y" k="-5" />
+<hkern u1="&#xf5;" u2="z" k="15" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="20" />
+<hkern u1="&#xb0;" u2="4" k="65" />
+<hkern u1="&#xa3;" u2="1" k="-10" />
+<hkern u1="&#xa3;" u2="4" k="20" />
+<hkern u1="&#xdf;" u2="&#x161;" k="-5" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="20" />
+<hkern u1="&#xdf;" u2="." k="20" />
+<hkern u1="&#xdf;" u2="/" k="10" />
+<hkern u1="&#xdf;" u2=":" k="10" />
+<hkern u1="&#xdf;" u2=";" k="10" />
+<hkern u1="&#xdf;" u2="?" k="20" />
+<hkern u1="&#xdf;" u2="\" k="10" />
+<hkern u1="&#xdf;" u2="g" k="10" />
+<hkern u1="&#xdf;" u2="s" k="-5" />
+<hkern u1="&#xdf;" u2="t" k="10" />
+<hkern u1="&#xdf;" u2="v" k="-5" />
+<hkern u1="&#xdf;" u2="x" k="5" />
+<hkern u1="&#xdf;" u2="y" k="-5" />
+<hkern u1="&#xdf;" u2="z" k="15" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="30" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="40" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="40" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="20" />
+<hkern u1="&#xae;" u2="&#xdd;" k="30" />
+<hkern u1="&#xae;" u2="A" k="10" />
+<hkern u1="&#xae;" u2="T" k="30" />
+<hkern u1="&#xae;" u2="W" k="20" />
+<hkern u1="&#xae;" u2="Y" k="30" />
+<hkern u1="&#xae;" u2="&#xc4;" k="10" />
+<hkern u1="&#xae;" u2="&#xc5;" k="10" />
+<hkern u1="&#xae;" u2="&#xc6;" k="10" />
+<hkern u1="&#xae;" u2="&#xc0;" k="10" />
+<hkern u1="&#xae;" u2="&#xc3;" k="10" />
+<hkern u1="&#xae;" u2="&#x178;" k="30" />
+<hkern u1="&#xae;" u2="&#xc2;" k="10" />
+<hkern u1="&#xae;" u2="&#xc1;" k="10" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="30" />
+<hkern u1="&#xa9;" u2="A" k="10" />
+<hkern u1="&#xa9;" u2="T" k="30" />
+<hkern u1="&#xa9;" u2="W" k="20" />
+<hkern u1="&#xa9;" u2="Y" k="30" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="10" />
+<hkern u1="&#xa9;" u2="&#x178;" k="30" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="10" />
+<hkern u1="&#xc6;" u2="@" k="20" />
+<hkern u1="&#xc6;" u2="T" k="-15" />
+<hkern u1="&#xc6;" u2="a" k="25" />
+<hkern u1="&#xc6;" u2="c" k="25" />
+<hkern u1="&#xc6;" u2="d" k="25" />
+<hkern u1="&#xc6;" u2="e" k="25" />
+<hkern u1="&#xc6;" u2="f" k="10" />
+<hkern u1="&#xc6;" u2="g" k="5" />
+<hkern u1="&#xc6;" u2="o" k="25" />
+<hkern u1="&#xc6;" u2="q" k="25" />
+<hkern u1="&#xc6;" u2="v" k="20" />
+<hkern u1="&#xc6;" u2="y" k="20" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc6;" u2="&#xae;" k="20" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc6;" u2="&#x153;" k="25" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd8;" u2="." k="40" />
+<hkern u1="&#xd8;" u2="/" k="50" />
+<hkern u1="&#xd8;" u2="?" k="20" />
+<hkern u1="&#xd8;" u2="J" k="45" />
+<hkern u1="&#xd8;" u2="T" k="50" />
+<hkern u1="&#xd8;" u2="V" k="40" />
+<hkern u1="&#xd8;" u2="W" k="60" />
+<hkern u1="&#xd8;" u2="X" k="35" />
+<hkern u1="&#xd8;" u2="Y" k="45" />
+<hkern u1="&#xd8;" u2="Z" k="45" />
+<hkern u1="&#xd8;" u2="a" k="5" />
+<hkern u1="&#xd8;" u2="b" k="10" />
+<hkern u1="&#xd8;" u2="c" k="5" />
+<hkern u1="&#xd8;" u2="d" k="5" />
+<hkern u1="&#xd8;" u2="e" k="5" />
+<hkern u1="&#xd8;" u2="h" k="10" />
+<hkern u1="&#xd8;" u2="k" k="10" />
+<hkern u1="&#xd8;" u2="l" k="10" />
+<hkern u1="&#xd8;" u2="m" k="5" />
+<hkern u1="&#xd8;" u2="n" k="5" />
+<hkern u1="&#xd8;" u2="o" k="5" />
+<hkern u1="&#xd8;" u2="p" k="5" />
+<hkern u1="&#xd8;" u2="q" k="5" />
+<hkern u1="&#xd8;" u2="r" k="5" />
+<hkern u1="&#xd8;" u2="u" k="5" />
+<hkern u1="&#xd8;" u2="x" k="10" />
+<hkern u1="&#xd8;" u2="z" k="10" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd8;" u2="&#x153;" k="5" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd8;" u2="&#x178;" k="45" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd8;" u2="&#x131;" k="5" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="30" />
+<hkern u1="&#x3c0;" u2="A" k="10" />
+<hkern u1="&#x3c0;" u2="T" k="30" />
+<hkern u1="&#x3c0;" u2="W" k="20" />
+<hkern u1="&#x3c0;" u2="Y" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="10" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="10" />
+<hkern u1="&#xe6;" u2="v" k="10" />
+<hkern u1="&#xe6;" u2="x" k="15" />
+<hkern u1="&#xe6;" u2="y" k="10" />
+<hkern u1="&#xf8;" u2="&#x161;" k="-5" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf8;" u2="." k="20" />
+<hkern u1="&#xf8;" u2="/" k="10" />
+<hkern u1="&#xf8;" u2=":" k="10" />
+<hkern u1="&#xf8;" u2=";" k="10" />
+<hkern u1="&#xf8;" u2="?" k="20" />
+<hkern u1="&#xf8;" u2="\" k="10" />
+<hkern u1="&#xf8;" u2="s" k="-5" />
+<hkern u1="&#xf8;" u2="t" k="10" />
+<hkern u1="&#xf8;" u2="v" k="-5" />
+<hkern u1="&#xf8;" u2="x" k="5" />
+<hkern u1="&#xf8;" u2="y" k="-5" />
+<hkern u1="&#xf8;" u2="z" k="15" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="20" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="40" />
+<hkern u1="&#xbf;" u2="1" k="40" />
+<hkern u1="&#xbf;" u2="3" k="-10" />
+<hkern u1="&#xbf;" u2="7" k="30" />
+<hkern u1="&#xbf;" u2="C" k="20" />
+<hkern u1="&#xbf;" u2="G" k="20" />
+<hkern u1="&#xbf;" u2="O" k="20" />
+<hkern u1="&#xbf;" u2="Q" k="20" />
+<hkern u1="&#xbf;" u2="T" k="60" />
+<hkern u1="&#xbf;" u2="V" k="50" />
+<hkern u1="&#xbf;" u2="W" k="30" />
+<hkern u1="&#xbf;" u2="Y" k="40" />
+<hkern u1="&#xbf;" u2="v" k="30" />
+<hkern u1="&#xbf;" u2="w" k="20" />
+<hkern u1="&#xbf;" u2="x" k="10" />
+<hkern u1="&#xbf;" u2="y" k="30" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="20" />
+<hkern u1="&#xbf;" u2="&#x152;" k="20" />
+<hkern u1="&#xbf;" u2="&#x178;" k="40" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="20" />
+<hkern u1="&#x192;" u2="1" k="-30" />
+<hkern u1="&#xab;" u2="&#xdd;" k="40" />
+<hkern u1="&#xab;" u2="T" k="40" />
+<hkern u1="&#xab;" u2="V" k="20" />
+<hkern u1="&#xab;" u2="W" k="20" />
+<hkern u1="&#xab;" u2="Y" k="40" />
+<hkern u1="&#xab;" u2="&#x178;" k="40" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="50" />
+<hkern u1="&#xbb;" u2="T" k="110" />
+<hkern u1="&#xbb;" u2="V" k="50" />
+<hkern u1="&#xbb;" u2="W" k="40" />
+<hkern u1="&#xbb;" u2="Y" k="50" />
+<hkern u1="&#xbb;" u2="x" k="50" />
+<hkern u1="&#xbb;" u2="z" k="50" />
+<hkern u1="&#xbb;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2026;" u2="0" k="40" />
+<hkern u1="&#x2026;" u2="1" k="60" />
+<hkern u1="&#x2026;" u2="4" k="50" />
+<hkern u1="&#x2026;" u2="6" k="30" />
+<hkern u1="&#x2026;" u2="8" k="15" />
+<hkern u1="&#x2026;" u2="9" k="10" />
+<hkern u1="&#x2026;" u2="A" k="-30" />
+<hkern u1="&#x2026;" u2="C" k="40" />
+<hkern u1="&#x2026;" u2="G" k="40" />
+<hkern u1="&#x2026;" u2="O" k="40" />
+<hkern u1="&#x2026;" u2="Q" k="40" />
+<hkern u1="&#x2026;" u2="T" k="60" />
+<hkern u1="&#x2026;" u2="U" k="10" />
+<hkern u1="&#x2026;" u2="V" k="60" />
+<hkern u1="&#x2026;" u2="W" k="20" />
+<hkern u1="&#x2026;" u2="Y" k="50" />
+<hkern u1="&#x2026;" u2="a" k="20" />
+<hkern u1="&#x2026;" u2="c" k="20" />
+<hkern u1="&#x2026;" u2="d" k="20" />
+<hkern u1="&#x2026;" u2="e" k="20" />
+<hkern u1="&#x2026;" u2="m" k="20" />
+<hkern u1="&#x2026;" u2="n" k="20" />
+<hkern u1="&#x2026;" u2="o" k="20" />
+<hkern u1="&#x2026;" u2="p" k="20" />
+<hkern u1="&#x2026;" u2="q" k="20" />
+<hkern u1="&#x2026;" u2="r" k="20" />
+<hkern u1="&#x2026;" u2="t" k="40" />
+<hkern u1="&#x2026;" u2="u" k="20" />
+<hkern u1="&#x2026;" u2="v" k="30" />
+<hkern u1="&#x2026;" u2="w" k="10" />
+<hkern u1="&#x2026;" u2="y" k="30" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2026;" u2="&#x152;" k="40" />
+<hkern u1="&#x2026;" u2="&#x153;" k="20" />
+<hkern u1="&#x2026;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2026;" u2="&#xda;" k="10" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2026;" u2="&#x131;" k="20" />
+<hkern u1="&#xc0;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc0;" u2="&#xdd;" k="70" />
+<hkern u1="&#xc0;" u2="*" k="60" />
+<hkern u1="&#xc0;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc0;" u2="-" k="-10" />
+<hkern u1="&#xc0;" u2="." k="-30" />
+<hkern u1="&#xc0;" u2="@" k="10" />
+<hkern u1="&#xc0;" u2="A" k="-10" />
+<hkern u1="&#xc0;" u2="C" k="40" />
+<hkern u1="&#xc0;" u2="G" k="40" />
+<hkern u1="&#xc0;" u2="J" k="10" />
+<hkern u1="&#xc0;" u2="O" k="40" />
+<hkern u1="&#xc0;" u2="Q" k="40" />
+<hkern u1="&#xc0;" u2="S" k="-5" />
+<hkern u1="&#xc0;" u2="T" k="90" />
+<hkern u1="&#xc0;" u2="V" k="70" />
+<hkern u1="&#xc0;" u2="W" k="70" />
+<hkern u1="&#xc0;" u2="Y" k="70" />
+<hkern u1="&#xc0;" u2="a" k="5" />
+<hkern u1="&#xc0;" u2="c" k="5" />
+<hkern u1="&#xc0;" u2="d" k="5" />
+<hkern u1="&#xc0;" u2="e" k="5" />
+<hkern u1="&#xc0;" u2="m" k="5" />
+<hkern u1="&#xc0;" u2="n" k="5" />
+<hkern u1="&#xc0;" u2="o" k="5" />
+<hkern u1="&#xc0;" u2="p" k="5" />
+<hkern u1="&#xc0;" u2="q" k="5" />
+<hkern u1="&#xc0;" u2="r" k="5" />
+<hkern u1="&#xc0;" u2="t" k="5" />
+<hkern u1="&#xc0;" u2="u" k="5" />
+<hkern u1="&#xc0;" u2="v" k="30" />
+<hkern u1="&#xc0;" u2="w" k="10" />
+<hkern u1="&#xc0;" u2="x" k="-20" />
+<hkern u1="&#xc0;" u2="y" k="30" />
+<hkern u1="&#xc0;" u2="z" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd6;" k="40" />
+<hkern u1="&#xc0;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc0;" u2="&#xae;" k="10" />
+<hkern u1="&#xc0;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc0;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc0;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd8;" k="40" />
+<hkern u1="&#xc0;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc0;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="40" />
+<hkern u1="&#xc0;" u2="&#x152;" k="40" />
+<hkern u1="&#xc0;" u2="&#x153;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc0;" u2="&#x178;" k="70" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="40" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="40" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="40" />
+<hkern u1="&#xc0;" u2="&#x131;" k="5" />
+<hkern u1="&#xc3;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc3;" u2="&#xdd;" k="70" />
+<hkern u1="&#xc3;" u2="*" k="60" />
+<hkern u1="&#xc3;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc3;" u2="-" k="-10" />
+<hkern u1="&#xc3;" u2="." k="-30" />
+<hkern u1="&#xc3;" u2="@" k="10" />
+<hkern u1="&#xc3;" u2="A" k="-10" />
+<hkern u1="&#xc3;" u2="C" k="40" />
+<hkern u1="&#xc3;" u2="G" k="40" />
+<hkern u1="&#xc3;" u2="J" k="10" />
+<hkern u1="&#xc3;" u2="O" k="40" />
+<hkern u1="&#xc3;" u2="Q" k="40" />
+<hkern u1="&#xc3;" u2="S" k="-5" />
+<hkern u1="&#xc3;" u2="T" k="90" />
+<hkern u1="&#xc3;" u2="V" k="70" />
+<hkern u1="&#xc3;" u2="W" k="70" />
+<hkern u1="&#xc3;" u2="Y" k="70" />
+<hkern u1="&#xc3;" u2="a" k="5" />
+<hkern u1="&#xc3;" u2="c" k="5" />
+<hkern u1="&#xc3;" u2="d" k="5" />
+<hkern u1="&#xc3;" u2="e" k="5" />
+<hkern u1="&#xc3;" u2="m" k="5" />
+<hkern u1="&#xc3;" u2="n" k="5" />
+<hkern u1="&#xc3;" u2="o" k="5" />
+<hkern u1="&#xc3;" u2="p" k="5" />
+<hkern u1="&#xc3;" u2="q" k="5" />
+<hkern u1="&#xc3;" u2="r" k="5" />
+<hkern u1="&#xc3;" u2="t" k="5" />
+<hkern u1="&#xc3;" u2="u" k="5" />
+<hkern u1="&#xc3;" u2="v" k="30" />
+<hkern u1="&#xc3;" u2="w" k="10" />
+<hkern u1="&#xc3;" u2="x" k="-20" />
+<hkern u1="&#xc3;" u2="y" k="30" />
+<hkern u1="&#xc3;" u2="z" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd6;" k="40" />
+<hkern u1="&#xc3;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc3;" u2="&#xae;" k="10" />
+<hkern u1="&#xc3;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc3;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc3;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd8;" k="40" />
+<hkern u1="&#xc3;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc3;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="40" />
+<hkern u1="&#xc3;" u2="&#x152;" k="40" />
+<hkern u1="&#xc3;" u2="&#x153;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc3;" u2="&#x178;" k="70" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="40" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="40" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="40" />
+<hkern u1="&#xc3;" u2="&#x131;" k="5" />
+<hkern u1="&#xd5;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd5;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd5;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd5;" u2="." k="40" />
+<hkern u1="&#xd5;" u2="/" k="50" />
+<hkern u1="&#xd5;" u2="?" k="20" />
+<hkern u1="&#xd5;" u2="J" k="45" />
+<hkern u1="&#xd5;" u2="T" k="50" />
+<hkern u1="&#xd5;" u2="V" k="40" />
+<hkern u1="&#xd5;" u2="W" k="60" />
+<hkern u1="&#xd5;" u2="X" k="35" />
+<hkern u1="&#xd5;" u2="Y" k="45" />
+<hkern u1="&#xd5;" u2="Z" k="45" />
+<hkern u1="&#xd5;" u2="a" k="5" />
+<hkern u1="&#xd5;" u2="b" k="10" />
+<hkern u1="&#xd5;" u2="c" k="5" />
+<hkern u1="&#xd5;" u2="d" k="5" />
+<hkern u1="&#xd5;" u2="e" k="5" />
+<hkern u1="&#xd5;" u2="h" k="10" />
+<hkern u1="&#xd5;" u2="k" k="10" />
+<hkern u1="&#xd5;" u2="l" k="10" />
+<hkern u1="&#xd5;" u2="m" k="5" />
+<hkern u1="&#xd5;" u2="n" k="5" />
+<hkern u1="&#xd5;" u2="o" k="5" />
+<hkern u1="&#xd5;" u2="p" k="5" />
+<hkern u1="&#xd5;" u2="q" k="5" />
+<hkern u1="&#xd5;" u2="r" k="5" />
+<hkern u1="&#xd5;" u2="u" k="5" />
+<hkern u1="&#xd5;" u2="x" k="10" />
+<hkern u1="&#xd5;" u2="z" k="10" />
+<hkern u1="&#xd5;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd5;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd5;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd5;" u2="&#x153;" k="5" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd5;" u2="&#x178;" k="45" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd5;" u2="&#x131;" k="5" />
+<hkern u1="&#x152;" u2="@" k="20" />
+<hkern u1="&#x152;" u2="T" k="-15" />
+<hkern u1="&#x152;" u2="a" k="25" />
+<hkern u1="&#x152;" u2="c" k="25" />
+<hkern u1="&#x152;" u2="d" k="25" />
+<hkern u1="&#x152;" u2="e" k="25" />
+<hkern u1="&#x152;" u2="f" k="10" />
+<hkern u1="&#x152;" u2="g" k="5" />
+<hkern u1="&#x152;" u2="o" k="25" />
+<hkern u1="&#x152;" u2="q" k="25" />
+<hkern u1="&#x152;" u2="v" k="20" />
+<hkern u1="&#x152;" u2="y" k="20" />
+<hkern u1="&#x152;" u2="&#xe7;" k="25" />
+<hkern u1="&#x152;" u2="&#xae;" k="20" />
+<hkern u1="&#x152;" u2="&#xa9;" k="20" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="20" />
+<hkern u1="&#x152;" u2="&#xe6;" k="25" />
+<hkern u1="&#x152;" u2="&#x153;" k="25" />
+<hkern u1="&#x153;" u2="v" k="10" />
+<hkern u1="&#x153;" u2="x" k="15" />
+<hkern u1="&#x153;" u2="y" k="10" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2013;" u2="1" k="20" />
+<hkern u1="&#x2013;" u2="7" k="30" />
+<hkern u1="&#x2013;" u2="A" k="-10" />
+<hkern u1="&#x2013;" u2="T" k="70" />
+<hkern u1="&#x2013;" u2="V" k="50" />
+<hkern u1="&#x2013;" u2="W" k="40" />
+<hkern u1="&#x2013;" u2="X" k="30" />
+<hkern u1="&#x2013;" u2="Y" k="50" />
+<hkern u1="&#x2013;" u2="Z" k="10" />
+<hkern u1="&#x2013;" u2="a" k="10" />
+<hkern u1="&#x2013;" u2="c" k="10" />
+<hkern u1="&#x2013;" u2="d" k="10" />
+<hkern u1="&#x2013;" u2="e" k="10" />
+<hkern u1="&#x2013;" u2="o" k="10" />
+<hkern u1="&#x2013;" u2="q" k="10" />
+<hkern u1="&#x2013;" u2="v" k="10" />
+<hkern u1="&#x2013;" u2="x" k="30" />
+<hkern u1="&#x2013;" u2="y" k="10" />
+<hkern u1="&#x2013;" u2="z" k="30" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2013;" u2="&#x153;" k="10" />
+<hkern u1="&#x2013;" u2="&#x178;" k="50" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2014;" u2="1" k="20" />
+<hkern u1="&#x2014;" u2="7" k="30" />
+<hkern u1="&#x2014;" u2="A" k="-10" />
+<hkern u1="&#x2014;" u2="T" k="70" />
+<hkern u1="&#x2014;" u2="V" k="50" />
+<hkern u1="&#x2014;" u2="W" k="40" />
+<hkern u1="&#x2014;" u2="X" k="30" />
+<hkern u1="&#x2014;" u2="Y" k="50" />
+<hkern u1="&#x2014;" u2="Z" k="10" />
+<hkern u1="&#x2014;" u2="a" k="10" />
+<hkern u1="&#x2014;" u2="c" k="10" />
+<hkern u1="&#x2014;" u2="d" k="10" />
+<hkern u1="&#x2014;" u2="e" k="10" />
+<hkern u1="&#x2014;" u2="o" k="10" />
+<hkern u1="&#x2014;" u2="q" k="10" />
+<hkern u1="&#x2014;" u2="v" k="10" />
+<hkern u1="&#x2014;" u2="x" k="30" />
+<hkern u1="&#x2014;" u2="y" k="10" />
+<hkern u1="&#x2014;" u2="z" k="30" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2014;" u2="&#x153;" k="10" />
+<hkern u1="&#x2014;" u2="&#x178;" k="50" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x201c;" u2="&#x161;" k="30" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x201c;" u2="A" k="40" />
+<hkern u1="&#x201c;" u2="J" k="110" />
+<hkern u1="&#x201c;" u2="T" k="-10" />
+<hkern u1="&#x201c;" u2="V" k="-20" />
+<hkern u1="&#x201c;" u2="W" k="-20" />
+<hkern u1="&#x201c;" u2="X" k="-20" />
+<hkern u1="&#x201c;" u2="Y" k="-30" />
+<hkern u1="&#x201c;" u2="a" k="30" />
+<hkern u1="&#x201c;" u2="c" k="30" />
+<hkern u1="&#x201c;" u2="d" k="30" />
+<hkern u1="&#x201c;" u2="e" k="30" />
+<hkern u1="&#x201c;" u2="g" k="40" />
+<hkern u1="&#x201c;" u2="m" k="10" />
+<hkern u1="&#x201c;" u2="n" k="10" />
+<hkern u1="&#x201c;" u2="o" k="30" />
+<hkern u1="&#x201c;" u2="p" k="10" />
+<hkern u1="&#x201c;" u2="q" k="30" />
+<hkern u1="&#x201c;" u2="r" k="10" />
+<hkern u1="&#x201c;" u2="s" k="30" />
+<hkern u1="&#x201c;" u2="u" k="10" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="40" />
+<hkern u1="&#x201c;" u2="&#x153;" k="30" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-30" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="40" />
+<hkern u1="&#x201c;" u2="&#x131;" k="10" />
+<hkern u1="&#x201d;" u2="&#x160;" k="10" />
+<hkern u1="&#x201d;" u2="C" k="40" />
+<hkern u1="&#x201d;" u2="G" k="40" />
+<hkern u1="&#x201d;" u2="J" k="120" />
+<hkern u1="&#x201d;" u2="O" k="40" />
+<hkern u1="&#x201d;" u2="Q" k="40" />
+<hkern u1="&#x201d;" u2="S" k="10" />
+<hkern u1="&#x201d;" u2="a" k="50" />
+<hkern u1="&#x201d;" u2="c" k="50" />
+<hkern u1="&#x201d;" u2="d" k="50" />
+<hkern u1="&#x201d;" u2="e" k="50" />
+<hkern u1="&#x201d;" u2="o" k="50" />
+<hkern u1="&#x201d;" u2="q" k="50" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201d;" u2="&#x152;" k="40" />
+<hkern u1="&#x201d;" u2="&#x153;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2018;" u2="&#x161;" k="30" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x2018;" u2="A" k="40" />
+<hkern u1="&#x2018;" u2="J" k="110" />
+<hkern u1="&#x2018;" u2="T" k="-10" />
+<hkern u1="&#x2018;" u2="V" k="-20" />
+<hkern u1="&#x2018;" u2="W" k="-20" />
+<hkern u1="&#x2018;" u2="X" k="-20" />
+<hkern u1="&#x2018;" u2="Y" k="-30" />
+<hkern u1="&#x2018;" u2="a" k="30" />
+<hkern u1="&#x2018;" u2="c" k="30" />
+<hkern u1="&#x2018;" u2="d" k="30" />
+<hkern u1="&#x2018;" u2="e" k="30" />
+<hkern u1="&#x2018;" u2="g" k="40" />
+<hkern u1="&#x2018;" u2="m" k="10" />
+<hkern u1="&#x2018;" u2="n" k="10" />
+<hkern u1="&#x2018;" u2="o" k="30" />
+<hkern u1="&#x2018;" u2="p" k="10" />
+<hkern u1="&#x2018;" u2="q" k="30" />
+<hkern u1="&#x2018;" u2="r" k="10" />
+<hkern u1="&#x2018;" u2="s" k="30" />
+<hkern u1="&#x2018;" u2="u" k="10" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="40" />
+<hkern u1="&#x2018;" u2="&#x153;" k="30" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-30" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="40" />
+<hkern u1="&#x2018;" u2="&#x131;" k="10" />
+<hkern u1="&#x2019;" u2="&#x160;" k="10" />
+<hkern u1="&#x2019;" u2="C" k="40" />
+<hkern u1="&#x2019;" u2="G" k="40" />
+<hkern u1="&#x2019;" u2="J" k="120" />
+<hkern u1="&#x2019;" u2="O" k="40" />
+<hkern u1="&#x2019;" u2="Q" k="40" />
+<hkern u1="&#x2019;" u2="S" k="10" />
+<hkern u1="&#x2019;" u2="a" k="50" />
+<hkern u1="&#x2019;" u2="c" k="50" />
+<hkern u1="&#x2019;" u2="d" k="50" />
+<hkern u1="&#x2019;" u2="e" k="50" />
+<hkern u1="&#x2019;" u2="o" k="50" />
+<hkern u1="&#x2019;" u2="q" k="50" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2019;" u2="&#x152;" k="40" />
+<hkern u1="&#x2019;" u2="&#x153;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="40" />
+<hkern u1="&#x178;" u2="&#x160;" k="-30" />
+<hkern u1="&#x178;" u2="&#x161;" k="50" />
+<hkern u1="&#x178;" u2="&amp;" k="30" />
+<hkern u1="&#x178;" u2=")" k="-50" />
+<hkern u1="&#x178;" u2="&#x2c;" k="50" />
+<hkern u1="&#x178;" u2="-" k="50" />
+<hkern u1="&#x178;" u2="." k="50" />
+<hkern u1="&#x178;" u2=":" k="30" />
+<hkern u1="&#x178;" u2=";" k="30" />
+<hkern u1="&#x178;" u2="@" k="30" />
+<hkern u1="&#x178;" u2="A" k="30" />
+<hkern u1="&#x178;" u2="C" k="5" />
+<hkern u1="&#x178;" u2="G" k="5" />
+<hkern u1="&#x178;" u2="J" k="70" />
+<hkern u1="&#x178;" u2="O" k="5" />
+<hkern u1="&#x178;" u2="Q" k="5" />
+<hkern u1="&#x178;" u2="S" k="-30" />
+<hkern u1="&#x178;" u2="T" k="-20" />
+<hkern u1="&#x178;" u2="V" k="-30" />
+<hkern u1="&#x178;" u2="X" k="-20" />
+<hkern u1="&#x178;" u2="]" k="-50" />
+<hkern u1="&#x178;" u2="a" k="55" />
+<hkern u1="&#x178;" u2="c" k="55" />
+<hkern u1="&#x178;" u2="d" k="55" />
+<hkern u1="&#x178;" u2="e" k="55" />
+<hkern u1="&#x178;" u2="f" k="10" />
+<hkern u1="&#x178;" u2="g" k="40" />
+<hkern u1="&#x178;" u2="m" k="40" />
+<hkern u1="&#x178;" u2="n" k="40" />
+<hkern u1="&#x178;" u2="o" k="55" />
+<hkern u1="&#x178;" u2="p" k="40" />
+<hkern u1="&#x178;" u2="q" k="55" />
+<hkern u1="&#x178;" u2="r" k="40" />
+<hkern u1="&#x178;" u2="s" k="50" />
+<hkern u1="&#x178;" u2="t" k="20" />
+<hkern u1="&#x178;" u2="u" k="40" />
+<hkern u1="&#x178;" u2="v" k="20" />
+<hkern u1="&#x178;" u2="w" k="10" />
+<hkern u1="&#x178;" u2="x" k="20" />
+<hkern u1="&#x178;" u2="y" k="20" />
+<hkern u1="&#x178;" u2="z" k="30" />
+<hkern u1="&#x178;" u2="}" k="-50" />
+<hkern u1="&#x178;" u2="&#xc4;" k="30" />
+<hkern u1="&#x178;" u2="&#xc5;" k="30" />
+<hkern u1="&#x178;" u2="&#xd6;" k="5" />
+<hkern u1="&#x178;" u2="&#xe7;" k="55" />
+<hkern u1="&#x178;" u2="&#xae;" k="30" />
+<hkern u1="&#x178;" u2="&#xa9;" k="30" />
+<hkern u1="&#x178;" u2="&#xc6;" k="30" />
+<hkern u1="&#x178;" u2="&#xd8;" k="5" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="30" />
+<hkern u1="&#x178;" u2="&#xe6;" k="55" />
+<hkern u1="&#x178;" u2="&#xab;" k="60" />
+<hkern u1="&#x178;" u2="&#xbb;" k="40" />
+<hkern u1="&#x178;" u2="&#x2026;" k="50" />
+<hkern u1="&#x178;" u2="&#xc0;" k="30" />
+<hkern u1="&#x178;" u2="&#xc3;" k="30" />
+<hkern u1="&#x178;" u2="&#xd5;" k="5" />
+<hkern u1="&#x178;" u2="&#x152;" k="5" />
+<hkern u1="&#x178;" u2="&#x153;" k="55" />
+<hkern u1="&#x178;" u2="&#x2013;" k="50" />
+<hkern u1="&#x178;" u2="&#x2014;" k="50" />
+<hkern u1="&#x178;" u2="&#x2039;" k="60" />
+<hkern u1="&#x178;" u2="&#x203a;" k="40" />
+<hkern u1="&#x178;" u2="&#x201a;" k="50" />
+<hkern u1="&#x178;" u2="&#x201e;" k="50" />
+<hkern u1="&#x178;" u2="&#xc2;" k="30" />
+<hkern u1="&#x178;" u2="&#xc1;" k="30" />
+<hkern u1="&#x178;" u2="&#xd3;" k="5" />
+<hkern u1="&#x178;" u2="&#xd4;" k="5" />
+<hkern u1="&#x178;" u2="&#xd2;" k="5" />
+<hkern u1="&#x178;" u2="&#x131;" k="40" />
+<hkern u1="&#xa4;" u2="4" k="20" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2039;" u2="T" k="40" />
+<hkern u1="&#x2039;" u2="V" k="20" />
+<hkern u1="&#x2039;" u2="W" k="20" />
+<hkern u1="&#x2039;" u2="Y" k="40" />
+<hkern u1="&#x2039;" u2="&#x178;" k="40" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x203a;" u2="T" k="110" />
+<hkern u1="&#x203a;" u2="V" k="50" />
+<hkern u1="&#x203a;" u2="W" k="40" />
+<hkern u1="&#x203a;" u2="Y" k="50" />
+<hkern u1="&#x203a;" u2="x" k="50" />
+<hkern u1="&#x203a;" u2="z" k="50" />
+<hkern u1="&#x203a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201a;" u2="0" k="40" />
+<hkern u1="&#x201a;" u2="1" k="60" />
+<hkern u1="&#x201a;" u2="4" k="50" />
+<hkern u1="&#x201a;" u2="6" k="30" />
+<hkern u1="&#x201a;" u2="8" k="15" />
+<hkern u1="&#x201a;" u2="9" k="10" />
+<hkern u1="&#x201a;" u2="A" k="-30" />
+<hkern u1="&#x201a;" u2="C" k="40" />
+<hkern u1="&#x201a;" u2="G" k="40" />
+<hkern u1="&#x201a;" u2="O" k="40" />
+<hkern u1="&#x201a;" u2="Q" k="40" />
+<hkern u1="&#x201a;" u2="T" k="60" />
+<hkern u1="&#x201a;" u2="U" k="10" />
+<hkern u1="&#x201a;" u2="V" k="60" />
+<hkern u1="&#x201a;" u2="W" k="20" />
+<hkern u1="&#x201a;" u2="Y" k="50" />
+<hkern u1="&#x201a;" u2="a" k="20" />
+<hkern u1="&#x201a;" u2="c" k="20" />
+<hkern u1="&#x201a;" u2="d" k="20" />
+<hkern u1="&#x201a;" u2="e" k="20" />
+<hkern u1="&#x201a;" u2="j" k="-30" />
+<hkern u1="&#x201a;" u2="m" k="20" />
+<hkern u1="&#x201a;" u2="n" k="20" />
+<hkern u1="&#x201a;" u2="o" k="20" />
+<hkern u1="&#x201a;" u2="p" k="20" />
+<hkern u1="&#x201a;" u2="q" k="20" />
+<hkern u1="&#x201a;" u2="r" k="20" />
+<hkern u1="&#x201a;" u2="t" k="40" />
+<hkern u1="&#x201a;" u2="u" k="20" />
+<hkern u1="&#x201a;" u2="v" k="30" />
+<hkern u1="&#x201a;" u2="w" k="10" />
+<hkern u1="&#x201a;" u2="y" k="30" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201a;" u2="&#x152;" k="40" />
+<hkern u1="&#x201a;" u2="&#x153;" k="20" />
+<hkern u1="&#x201a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="40" />
+<hkern u1="&#x201a;" u2="&#xda;" k="10" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201a;" u2="&#x131;" k="20" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201e;" u2="0" k="40" />
+<hkern u1="&#x201e;" u2="1" k="60" />
+<hkern u1="&#x201e;" u2="4" k="50" />
+<hkern u1="&#x201e;" u2="6" k="30" />
+<hkern u1="&#x201e;" u2="8" k="15" />
+<hkern u1="&#x201e;" u2="9" k="10" />
+<hkern u1="&#x201e;" u2="A" k="-30" />
+<hkern u1="&#x201e;" u2="C" k="40" />
+<hkern u1="&#x201e;" u2="G" k="40" />
+<hkern u1="&#x201e;" u2="O" k="40" />
+<hkern u1="&#x201e;" u2="Q" k="40" />
+<hkern u1="&#x201e;" u2="T" k="60" />
+<hkern u1="&#x201e;" u2="U" k="10" />
+<hkern u1="&#x201e;" u2="V" k="60" />
+<hkern u1="&#x201e;" u2="W" k="20" />
+<hkern u1="&#x201e;" u2="Y" k="50" />
+<hkern u1="&#x201e;" u2="a" k="20" />
+<hkern u1="&#x201e;" u2="c" k="20" />
+<hkern u1="&#x201e;" u2="d" k="20" />
+<hkern u1="&#x201e;" u2="e" k="20" />
+<hkern u1="&#x201e;" u2="j" k="-30" />
+<hkern u1="&#x201e;" u2="m" k="20" />
+<hkern u1="&#x201e;" u2="n" k="20" />
+<hkern u1="&#x201e;" u2="o" k="20" />
+<hkern u1="&#x201e;" u2="p" k="20" />
+<hkern u1="&#x201e;" u2="q" k="20" />
+<hkern u1="&#x201e;" u2="r" k="20" />
+<hkern u1="&#x201e;" u2="t" k="40" />
+<hkern u1="&#x201e;" u2="u" k="20" />
+<hkern u1="&#x201e;" u2="v" k="30" />
+<hkern u1="&#x201e;" u2="w" k="10" />
+<hkern u1="&#x201e;" u2="y" k="30" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201e;" u2="&#x152;" k="40" />
+<hkern u1="&#x201e;" u2="&#x153;" k="20" />
+<hkern u1="&#x201e;" u2="&#x178;" k="50" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="40" />
+<hkern u1="&#x201e;" u2="&#xda;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201e;" u2="&#x131;" k="20" />
+<hkern u1="&#xc2;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc2;" u2="&#xdd;" k="70" />
+<hkern u1="&#xc2;" u2="*" k="60" />
+<hkern u1="&#xc2;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc2;" u2="-" k="-10" />
+<hkern u1="&#xc2;" u2="." k="-30" />
+<hkern u1="&#xc2;" u2="@" k="10" />
+<hkern u1="&#xc2;" u2="A" k="-10" />
+<hkern u1="&#xc2;" u2="C" k="40" />
+<hkern u1="&#xc2;" u2="G" k="40" />
+<hkern u1="&#xc2;" u2="J" k="10" />
+<hkern u1="&#xc2;" u2="O" k="40" />
+<hkern u1="&#xc2;" u2="Q" k="40" />
+<hkern u1="&#xc2;" u2="S" k="-5" />
+<hkern u1="&#xc2;" u2="T" k="90" />
+<hkern u1="&#xc2;" u2="V" k="70" />
+<hkern u1="&#xc2;" u2="W" k="70" />
+<hkern u1="&#xc2;" u2="Y" k="70" />
+<hkern u1="&#xc2;" u2="a" k="5" />
+<hkern u1="&#xc2;" u2="c" k="5" />
+<hkern u1="&#xc2;" u2="d" k="5" />
+<hkern u1="&#xc2;" u2="e" k="5" />
+<hkern u1="&#xc2;" u2="m" k="5" />
+<hkern u1="&#xc2;" u2="n" k="5" />
+<hkern u1="&#xc2;" u2="o" k="5" />
+<hkern u1="&#xc2;" u2="p" k="5" />
+<hkern u1="&#xc2;" u2="q" k="5" />
+<hkern u1="&#xc2;" u2="r" k="5" />
+<hkern u1="&#xc2;" u2="t" k="5" />
+<hkern u1="&#xc2;" u2="u" k="5" />
+<hkern u1="&#xc2;" u2="v" k="30" />
+<hkern u1="&#xc2;" u2="w" k="10" />
+<hkern u1="&#xc2;" u2="x" k="-20" />
+<hkern u1="&#xc2;" u2="y" k="30" />
+<hkern u1="&#xc2;" u2="z" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd6;" k="40" />
+<hkern u1="&#xc2;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc2;" u2="&#xae;" k="10" />
+<hkern u1="&#xc2;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc2;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc2;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd8;" k="40" />
+<hkern u1="&#xc2;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc2;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc2;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc2;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd5;" k="40" />
+<hkern u1="&#xc2;" u2="&#x152;" k="40" />
+<hkern u1="&#xc2;" u2="&#x153;" k="5" />
+<hkern u1="&#xc2;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc2;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc2;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc2;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc2;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc2;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc2;" u2="&#x178;" k="70" />
+<hkern u1="&#xc2;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc2;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc2;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="40" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="40" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="40" />
+<hkern u1="&#xc2;" u2="&#x131;" k="5" />
+<hkern u1="&#xca;" u2="@" k="20" />
+<hkern u1="&#xca;" u2="T" k="-15" />
+<hkern u1="&#xca;" u2="a" k="25" />
+<hkern u1="&#xca;" u2="c" k="25" />
+<hkern u1="&#xca;" u2="d" k="25" />
+<hkern u1="&#xca;" u2="e" k="25" />
+<hkern u1="&#xca;" u2="f" k="10" />
+<hkern u1="&#xca;" u2="g" k="5" />
+<hkern u1="&#xca;" u2="o" k="25" />
+<hkern u1="&#xca;" u2="q" k="25" />
+<hkern u1="&#xca;" u2="v" k="20" />
+<hkern u1="&#xca;" u2="y" k="20" />
+<hkern u1="&#xca;" u2="&#xe7;" k="25" />
+<hkern u1="&#xca;" u2="&#xae;" k="20" />
+<hkern u1="&#xca;" u2="&#xa9;" k="20" />
+<hkern u1="&#xca;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xca;" u2="&#xe6;" k="25" />
+<hkern u1="&#xca;" u2="&#x153;" k="25" />
+<hkern u1="&#xc1;" u2="&#x160;" k="-5" />
+<hkern u1="&#xc1;" u2="&#xdd;" k="70" />
+<hkern u1="&#xc1;" u2="*" k="60" />
+<hkern u1="&#xc1;" u2="&#x2c;" k="-30" />
+<hkern u1="&#xc1;" u2="-" k="-10" />
+<hkern u1="&#xc1;" u2="." k="-30" />
+<hkern u1="&#xc1;" u2="@" k="10" />
+<hkern u1="&#xc1;" u2="A" k="-10" />
+<hkern u1="&#xc1;" u2="C" k="40" />
+<hkern u1="&#xc1;" u2="G" k="40" />
+<hkern u1="&#xc1;" u2="J" k="10" />
+<hkern u1="&#xc1;" u2="O" k="40" />
+<hkern u1="&#xc1;" u2="Q" k="40" />
+<hkern u1="&#xc1;" u2="S" k="-5" />
+<hkern u1="&#xc1;" u2="T" k="90" />
+<hkern u1="&#xc1;" u2="V" k="70" />
+<hkern u1="&#xc1;" u2="W" k="70" />
+<hkern u1="&#xc1;" u2="Y" k="70" />
+<hkern u1="&#xc1;" u2="a" k="5" />
+<hkern u1="&#xc1;" u2="c" k="5" />
+<hkern u1="&#xc1;" u2="d" k="5" />
+<hkern u1="&#xc1;" u2="e" k="5" />
+<hkern u1="&#xc1;" u2="m" k="5" />
+<hkern u1="&#xc1;" u2="n" k="5" />
+<hkern u1="&#xc1;" u2="o" k="5" />
+<hkern u1="&#xc1;" u2="p" k="5" />
+<hkern u1="&#xc1;" u2="q" k="5" />
+<hkern u1="&#xc1;" u2="r" k="5" />
+<hkern u1="&#xc1;" u2="t" k="5" />
+<hkern u1="&#xc1;" u2="u" k="5" />
+<hkern u1="&#xc1;" u2="v" k="30" />
+<hkern u1="&#xc1;" u2="w" k="10" />
+<hkern u1="&#xc1;" u2="x" k="-20" />
+<hkern u1="&#xc1;" u2="y" k="30" />
+<hkern u1="&#xc1;" u2="z" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd6;" k="40" />
+<hkern u1="&#xc1;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc1;" u2="&#xae;" k="10" />
+<hkern u1="&#xc1;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc1;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc1;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd8;" k="40" />
+<hkern u1="&#xc1;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc1;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc1;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc1;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd5;" k="40" />
+<hkern u1="&#xc1;" u2="&#x152;" k="40" />
+<hkern u1="&#xc1;" u2="&#x153;" k="5" />
+<hkern u1="&#xc1;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc1;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc1;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc1;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc1;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc1;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc1;" u2="&#x178;" k="70" />
+<hkern u1="&#xc1;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc1;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc1;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="40" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="40" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="40" />
+<hkern u1="&#xc1;" u2="&#x131;" k="5" />
+<hkern u1="&#xcb;" u2="@" k="20" />
+<hkern u1="&#xcb;" u2="T" k="-15" />
+<hkern u1="&#xcb;" u2="a" k="25" />
+<hkern u1="&#xcb;" u2="c" k="25" />
+<hkern u1="&#xcb;" u2="d" k="25" />
+<hkern u1="&#xcb;" u2="e" k="25" />
+<hkern u1="&#xcb;" u2="f" k="10" />
+<hkern u1="&#xcb;" u2="g" k="5" />
+<hkern u1="&#xcb;" u2="o" k="25" />
+<hkern u1="&#xcb;" u2="q" k="25" />
+<hkern u1="&#xcb;" u2="v" k="20" />
+<hkern u1="&#xcb;" u2="y" k="20" />
+<hkern u1="&#xcb;" u2="&#xe7;" k="25" />
+<hkern u1="&#xcb;" u2="&#xae;" k="20" />
+<hkern u1="&#xcb;" u2="&#xa9;" k="20" />
+<hkern u1="&#xcb;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xcb;" u2="&#xe6;" k="25" />
+<hkern u1="&#xcb;" u2="&#x153;" k="25" />
+<hkern u1="&#xc8;" u2="@" k="20" />
+<hkern u1="&#xc8;" u2="T" k="-15" />
+<hkern u1="&#xc8;" u2="a" k="25" />
+<hkern u1="&#xc8;" u2="c" k="25" />
+<hkern u1="&#xc8;" u2="d" k="25" />
+<hkern u1="&#xc8;" u2="e" k="25" />
+<hkern u1="&#xc8;" u2="f" k="10" />
+<hkern u1="&#xc8;" u2="g" k="5" />
+<hkern u1="&#xc8;" u2="o" k="25" />
+<hkern u1="&#xc8;" u2="q" k="25" />
+<hkern u1="&#xc8;" u2="v" k="20" />
+<hkern u1="&#xc8;" u2="y" k="20" />
+<hkern u1="&#xc8;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc8;" u2="&#xae;" k="20" />
+<hkern u1="&#xc8;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc8;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc8;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc8;" u2="&#x153;" k="25" />
+<hkern u1="&#xcd;" u2="/" k="20" />
+<hkern u1="&#xcd;" u2="v" k="-20" />
+<hkern u1="&#xcd;" u2="y" k="-20" />
+<hkern u1="&#xce;" u2="/" k="20" />
+<hkern u1="&#xce;" u2="v" k="-20" />
+<hkern u1="&#xce;" u2="y" k="-20" />
+<hkern u1="&#xcf;" u2="/" k="20" />
+<hkern u1="&#xcf;" u2="v" k="-20" />
+<hkern u1="&#xcf;" u2="y" k="-20" />
+<hkern u1="&#xcc;" u2="/" k="20" />
+<hkern u1="&#xcc;" u2="v" k="-20" />
+<hkern u1="&#xcc;" u2="y" k="-20" />
+<hkern u1="&#xd3;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd3;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd3;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd3;" u2="." k="40" />
+<hkern u1="&#xd3;" u2="/" k="50" />
+<hkern u1="&#xd3;" u2="?" k="20" />
+<hkern u1="&#xd3;" u2="J" k="45" />
+<hkern u1="&#xd3;" u2="T" k="50" />
+<hkern u1="&#xd3;" u2="V" k="40" />
+<hkern u1="&#xd3;" u2="W" k="60" />
+<hkern u1="&#xd3;" u2="X" k="35" />
+<hkern u1="&#xd3;" u2="Y" k="45" />
+<hkern u1="&#xd3;" u2="Z" k="45" />
+<hkern u1="&#xd3;" u2="a" k="5" />
+<hkern u1="&#xd3;" u2="b" k="10" />
+<hkern u1="&#xd3;" u2="c" k="5" />
+<hkern u1="&#xd3;" u2="d" k="5" />
+<hkern u1="&#xd3;" u2="e" k="5" />
+<hkern u1="&#xd3;" u2="h" k="10" />
+<hkern u1="&#xd3;" u2="k" k="10" />
+<hkern u1="&#xd3;" u2="l" k="10" />
+<hkern u1="&#xd3;" u2="m" k="5" />
+<hkern u1="&#xd3;" u2="n" k="5" />
+<hkern u1="&#xd3;" u2="o" k="5" />
+<hkern u1="&#xd3;" u2="p" k="5" />
+<hkern u1="&#xd3;" u2="q" k="5" />
+<hkern u1="&#xd3;" u2="r" k="5" />
+<hkern u1="&#xd3;" u2="u" k="5" />
+<hkern u1="&#xd3;" u2="x" k="10" />
+<hkern u1="&#xd3;" u2="z" k="10" />
+<hkern u1="&#xd3;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd3;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd3;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd3;" u2="&#x153;" k="5" />
+<hkern u1="&#xd3;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd3;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd3;" u2="&#x178;" k="45" />
+<hkern u1="&#xd3;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd3;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd3;" u2="&#x131;" k="5" />
+<hkern u1="&#xd4;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd4;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd4;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd4;" u2="." k="40" />
+<hkern u1="&#xd4;" u2="/" k="50" />
+<hkern u1="&#xd4;" u2="?" k="20" />
+<hkern u1="&#xd4;" u2="J" k="45" />
+<hkern u1="&#xd4;" u2="T" k="50" />
+<hkern u1="&#xd4;" u2="V" k="40" />
+<hkern u1="&#xd4;" u2="W" k="60" />
+<hkern u1="&#xd4;" u2="X" k="35" />
+<hkern u1="&#xd4;" u2="Y" k="45" />
+<hkern u1="&#xd4;" u2="Z" k="45" />
+<hkern u1="&#xd4;" u2="a" k="5" />
+<hkern u1="&#xd4;" u2="b" k="10" />
+<hkern u1="&#xd4;" u2="c" k="5" />
+<hkern u1="&#xd4;" u2="d" k="5" />
+<hkern u1="&#xd4;" u2="e" k="5" />
+<hkern u1="&#xd4;" u2="h" k="10" />
+<hkern u1="&#xd4;" u2="k" k="10" />
+<hkern u1="&#xd4;" u2="l" k="10" />
+<hkern u1="&#xd4;" u2="m" k="5" />
+<hkern u1="&#xd4;" u2="n" k="5" />
+<hkern u1="&#xd4;" u2="o" k="5" />
+<hkern u1="&#xd4;" u2="p" k="5" />
+<hkern u1="&#xd4;" u2="q" k="5" />
+<hkern u1="&#xd4;" u2="r" k="5" />
+<hkern u1="&#xd4;" u2="u" k="5" />
+<hkern u1="&#xd4;" u2="x" k="10" />
+<hkern u1="&#xd4;" u2="z" k="10" />
+<hkern u1="&#xd4;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd4;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd4;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd4;" u2="&#x153;" k="5" />
+<hkern u1="&#xd4;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd4;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd4;" u2="&#x178;" k="45" />
+<hkern u1="&#xd4;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd4;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd4;" u2="&#x131;" k="5" />
+<hkern u1="&#xd2;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd2;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd2;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd2;" u2="." k="40" />
+<hkern u1="&#xd2;" u2="/" k="50" />
+<hkern u1="&#xd2;" u2="?" k="20" />
+<hkern u1="&#xd2;" u2="J" k="45" />
+<hkern u1="&#xd2;" u2="T" k="50" />
+<hkern u1="&#xd2;" u2="V" k="40" />
+<hkern u1="&#xd2;" u2="W" k="60" />
+<hkern u1="&#xd2;" u2="X" k="35" />
+<hkern u1="&#xd2;" u2="Y" k="45" />
+<hkern u1="&#xd2;" u2="Z" k="45" />
+<hkern u1="&#xd2;" u2="a" k="5" />
+<hkern u1="&#xd2;" u2="b" k="10" />
+<hkern u1="&#xd2;" u2="c" k="5" />
+<hkern u1="&#xd2;" u2="d" k="5" />
+<hkern u1="&#xd2;" u2="e" k="5" />
+<hkern u1="&#xd2;" u2="h" k="10" />
+<hkern u1="&#xd2;" u2="k" k="10" />
+<hkern u1="&#xd2;" u2="l" k="10" />
+<hkern u1="&#xd2;" u2="m" k="5" />
+<hkern u1="&#xd2;" u2="n" k="5" />
+<hkern u1="&#xd2;" u2="o" k="5" />
+<hkern u1="&#xd2;" u2="p" k="5" />
+<hkern u1="&#xd2;" u2="q" k="5" />
+<hkern u1="&#xd2;" u2="r" k="5" />
+<hkern u1="&#xd2;" u2="u" k="5" />
+<hkern u1="&#xd2;" u2="x" k="10" />
+<hkern u1="&#xd2;" u2="z" k="10" />
+<hkern u1="&#xd2;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd2;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd2;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd2;" u2="&#x153;" k="5" />
+<hkern u1="&#xd2;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd2;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd2;" u2="&#x178;" k="45" />
+<hkern u1="&#xd2;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd2;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd2;" u2="&#x131;" k="5" />
+<hkern u1="&#xda;" u2="&#x2c;" k="10" />
+<hkern u1="&#xda;" u2="." k="10" />
+<hkern u1="&#xda;" u2="A" k="-20" />
+<hkern u1="&#xda;" u2="J" k="10" />
+<hkern u1="&#xda;" u2="v" k="-20" />
+<hkern u1="&#xda;" u2="y" k="-20" />
+<hkern u1="&#xda;" u2="&#xc4;" k="-20" />
+<hkern u1="&#xda;" u2="&#xc5;" k="-20" />
+<hkern u1="&#xda;" u2="&#xc6;" k="-20" />
+<hkern u1="&#xda;" u2="&#x2026;" k="10" />
+<hkern u1="&#xda;" u2="&#xc0;" k="-20" />
+<hkern u1="&#xda;" u2="&#xc3;" k="-20" />
+<hkern u1="&#xda;" u2="&#x201a;" k="10" />
+<hkern u1="&#xda;" u2="&#x201e;" k="10" />
+<hkern u1="&#xda;" u2="&#xc2;" k="-20" />
+<hkern u1="&#xda;" u2="&#xc1;" k="-20" />
+<hkern u1="&#xdb;" u2="&#x2c;" k="10" />
+<hkern u1="&#xdb;" u2="." k="10" />
+<hkern u1="&#xdb;" u2="A" k="-20" />
+<hkern u1="&#xdb;" u2="J" k="10" />
+<hkern u1="&#xdb;" u2="v" k="-20" />
+<hkern u1="&#xdb;" u2="y" k="-20" />
+<hkern u1="&#xdb;" u2="&#xc4;" k="-20" />
+<hkern u1="&#xdb;" u2="&#xc5;" k="-20" />
+<hkern u1="&#xdb;" u2="&#xc6;" k="-20" />
+<hkern u1="&#xdb;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdb;" u2="&#xc0;" k="-20" />
+<hkern u1="&#xdb;" u2="&#xc3;" k="-20" />
+<hkern u1="&#xdb;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdb;" u2="&#x201e;" k="10" />
+<hkern u1="&#xdb;" u2="&#xc2;" k="-20" />
+<hkern u1="&#xdb;" u2="&#xc1;" k="-20" />
+<hkern u1="&#xd9;" u2="&#x2c;" k="10" />
+<hkern u1="&#xd9;" u2="." k="10" />
+<hkern u1="&#xd9;" u2="A" k="-20" />
+<hkern u1="&#xd9;" u2="J" k="10" />
+<hkern u1="&#xd9;" u2="v" k="-20" />
+<hkern u1="&#xd9;" u2="y" k="-20" />
+<hkern u1="&#xd9;" u2="&#xc4;" k="-20" />
+<hkern u1="&#xd9;" u2="&#xc5;" k="-20" />
+<hkern u1="&#xd9;" u2="&#xc6;" k="-20" />
+<hkern u1="&#xd9;" u2="&#x2026;" k="10" />
+<hkern u1="&#xd9;" u2="&#xc0;" k="-20" />
+<hkern u1="&#xd9;" u2="&#xc3;" k="-20" />
+<hkern u1="&#xd9;" u2="&#x201a;" k="10" />
+<hkern u1="&#xd9;" u2="&#x201e;" k="10" />
+<hkern u1="&#xd9;" u2="&#xc2;" k="-20" />
+<hkern u1="&#xd9;" u2="&#xc1;" k="-20" />
+<hkern u1="&#xad;" u2="&#xdd;" k="50" />
+<hkern u1="&#xad;" u2="&#x17d;" k="10" />
+<hkern u1="&#xad;" u2="1" k="20" />
+<hkern u1="&#xad;" u2="7" k="30" />
+<hkern u1="&#xad;" u2="A" k="-10" />
+<hkern u1="&#xad;" u2="T" k="70" />
+<hkern u1="&#xad;" u2="V" k="50" />
+<hkern u1="&#xad;" u2="W" k="40" />
+<hkern u1="&#xad;" u2="X" k="30" />
+<hkern u1="&#xad;" u2="Y" k="50" />
+<hkern u1="&#xad;" u2="Z" k="10" />
+<hkern u1="&#xad;" u2="a" k="10" />
+<hkern u1="&#xad;" u2="c" k="10" />
+<hkern u1="&#xad;" u2="d" k="10" />
+<hkern u1="&#xad;" u2="e" k="10" />
+<hkern u1="&#xad;" u2="o" k="10" />
+<hkern u1="&#xad;" u2="q" k="10" />
+<hkern u1="&#xad;" u2="v" k="10" />
+<hkern u1="&#xad;" u2="x" k="30" />
+<hkern u1="&#xad;" u2="y" k="10" />
+<hkern u1="&#xad;" u2="z" k="30" />
+<hkern u1="&#xad;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe7;" k="10" />
+<hkern u1="&#xad;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe6;" k="10" />
+<hkern u1="&#xad;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xad;" u2="&#x153;" k="10" />
+<hkern u1="&#xad;" u2="&#x178;" k="50" />
+<hkern u1="&#xad;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xd0;" u2="&#xdd;" k="45" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd0;" u2="." k="40" />
+<hkern u1="&#xd0;" u2="/" k="50" />
+<hkern u1="&#xd0;" u2="?" k="20" />
+<hkern u1="&#xd0;" u2="J" k="45" />
+<hkern u1="&#xd0;" u2="T" k="50" />
+<hkern u1="&#xd0;" u2="V" k="40" />
+<hkern u1="&#xd0;" u2="W" k="60" />
+<hkern u1="&#xd0;" u2="X" k="35" />
+<hkern u1="&#xd0;" u2="Y" k="45" />
+<hkern u1="&#xd0;" u2="Z" k="45" />
+<hkern u1="&#xd0;" u2="a" k="5" />
+<hkern u1="&#xd0;" u2="b" k="10" />
+<hkern u1="&#xd0;" u2="c" k="5" />
+<hkern u1="&#xd0;" u2="d" k="5" />
+<hkern u1="&#xd0;" u2="e" k="5" />
+<hkern u1="&#xd0;" u2="h" k="10" />
+<hkern u1="&#xd0;" u2="k" k="10" />
+<hkern u1="&#xd0;" u2="l" k="10" />
+<hkern u1="&#xd0;" u2="m" k="5" />
+<hkern u1="&#xd0;" u2="n" k="5" />
+<hkern u1="&#xd0;" u2="o" k="5" />
+<hkern u1="&#xd0;" u2="p" k="5" />
+<hkern u1="&#xd0;" u2="q" k="5" />
+<hkern u1="&#xd0;" u2="r" k="5" />
+<hkern u1="&#xd0;" u2="u" k="5" />
+<hkern u1="&#xd0;" u2="x" k="10" />
+<hkern u1="&#xd0;" u2="z" k="10" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd0;" u2="&#x153;" k="5" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd0;" u2="&#x178;" k="45" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd0;" u2="&#x131;" k="5" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02 Italic" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02 Italic" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02 Italic" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02 Italic" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.ttf
new file mode 100644
index 0000000000..4ef76763bd
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.woff
new file mode 100644
index 0000000000..750d90d387
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Italic.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.eot
new file mode 100644
index 0000000000..280d111fe4
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.svg
new file mode 100644
index 0000000000..85f39f5008
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.svg
@@ -0,0 +1,3872 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Light]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated in 2010 by FontLab Studio. Copyright info pending.]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="543" id="Omnes_ATTW02Light">
+<font-face font-family="Omnes_ATT W02 Light" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="700" descent="-300" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="176" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="176" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="262" d="M128 173Q117 173 117 193L101 647H161L146 193Q145 183 144 178T134 173H128ZM127 -5Q93 -5 93 30V37Q93 72 127 72H135Q169 72 169 37V30Q169 -5 135 -5H127Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="362" d="M109 410Q102 410 100 415T96 430L83 647H143L129 430Q128 420 126 415T117 410H109ZM244 410Q237 410 235 415T232 430L218 647H278L264 430Q263 420 261 415T252 410H244Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="673" d="M140 -3Q126 -3 118 10T121 57L160 184H34V227H172L202 323Q215 365 232 420H108V463H245L301 648Q303 650 311 650Q325 650 333 637T330 590L291 463H467L523 648Q525 650 533 650Q547 650 555 637T552 590L513 463H639V420H500L441 227H565V184H428L372 -1Q370 -3 362 -3Q348 -3 340 10T343 57L382 184H206L150 -1Q148 -3 140 -3ZM396 226L455 421H277L218 226H396Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="565" d="M280 -96Q260 -96 260 -95V-8Q199 -3 156 16T87 61Q70 78 63 93T56 120Q56 128 60 134T70 143T80 149T86 150Q94 130 109 112T145 77T196 51T263 37V309Q227 318 193 330T131 361T88 410T72 483Q72 518 86 548T126 601T186 638T262 655V739Q262 740 282 740H287Q306 740 306 739V656Q362 652 402 634T465 593Q478 581 485 567T493 542Q493 534 489 528T479 519T469 513T463 512Q446 548 407 577T304 611V350Q342 341 378 329T444 296T490 245T508 168Q508 131 493 100T451 45T387 8T305 -9V-95Q305 -96 285 -96H280ZM265 610Q202 606 162 572T122 483Q122 454 132 434T162 401T207 378T265 360V610ZM302 37Q337 38 366 48T415 75T447 115T459 166Q459 198 447 219T412 256T362 281T302 299V37Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="738" d="M200 358Q169 358 143 369T98 400T67 446T56 503V510Q56 540 67 566T97 613T143 644T200 656Q231 656 257 645T302 614T332 568T343 512V504Q343 474 332 448T302 401T256 370T200 358ZM118 -6Q117 -7 113 -6T105 -3T98 4T94 15Q94 26 98 34T116 56L617 657Q618 658 622 657T630 654T637 648T641 636Q641 621 618 595L118 -6ZM538 -5Q508 -5 482 6T436 37T406 83T395 140V147Q395 177 406 203T436 250T482 281T539 293Q569 293 595 282T641 251T671 205T682 149V141Q682 111 671 85T641 38T595 7T538 -5ZM200 397Q244 397 272 427T300 502V512Q300 534 293 553T272 587T240 610T199 618Q156 618 128 588T100 512V502Q100 457 128 427T200 397ZM539 34Q583 34 610 64T638 139V149Q638 194 610 224T538 255Q494 255 467 225T439 149V139Q439 94 467 64T539 34Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="671" d="M260 -10Q211 -10 171 3T102 40T57 98T41 177Q41 216 54 247T91 301T147 342T218 370Q192 399 174 432T156 507Q156 538 167 565T199 613T249 645T313 657Q346 657 373 646T421 616T452 570T463 515Q463 483 452 458T418 413T366 379T297 355L488 176Q511 215 530 265T564 387H613Q607 355 598 323T576 259T550 199T521 147L662 18Q664 16 659 7T635 -3Q627 -3 619 -1T598 13L494 109Q449 54 390 22T260 -10ZM263 33Q326 33 376 62T463 139L246 342Q214 332 186 319T138 286T105 240T93 179Q93 111 139 72T263 33ZM267 384Q337 401 377 430T417 514Q417 535 410 553T389 586T356 608T312 616Q260 616 232 585T204 508Q204 473 221 443T267 384Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="226" d="M109 410Q102 410 100 415T96 430L83 647H143L129 430Q128 420 126 415T117 410H109Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="381" d="M334 -195Q310 -195 274 -180T200 -126Q143 -69 108 21T72 228Q72 284 82 335T109 432T149 514T200 579Q238 617 274 633T334 650Q356 650 356 633Q356 628 354 622T349 614Q298 603 256 569T184 483T138 366T122 229Q122 155 138 90T184 -28T256 -114T349 -159Q351 -160 353 -166T356 -178Q356 -195 334 -195Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="381" d="M47 -195Q25 -195 25 -178Q25 -173 27 -167T32 -159Q83 -148 125 -114T197 -28T243 89T259 229Q259 301 243 366T197 482T125 568T32 614Q30 615 28 621T25 633Q25 650 47 650Q72 650 107 634T181 579Q208 552 231 515T272 432T299 336T309 228Q309 112 274 22T181 -126Q143 -164 108 -179T47 -195Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="407" d="M105 372Q97 378 106 394L171 487L64 526Q51 531 51 540Q51 565 75 559L185 527L188 640Q188 652 192 655T205 659Q213 659 217 656T222 640L225 527L334 559Q359 565 359 540Q359 532 345 526L239 487L303 394Q313 378 305 372Q297 365 290 364T275 374L205 463L135 374Q127 363 120 364T105 372Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="550" d="M273 38Q264 38 259 42T253 61V245H82Q68 245 63 250T58 264V269Q58 278 63 283T82 288H253V460Q253 483 273 483H277Q298 483 298 460V288H469Q492 288 492 269V264Q492 245 469 245H298V61Q298 47 293 43T277 38H273Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="221" d="M56 -129Q54 -132 50 -133T40 -133T32 -128T31 -118L90 51Q95 65 100 71T119 78H123Q132 78 138 72T145 52V49Q145 43 144 35T134 13L56 -129Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="381" d="M60 270H320V226H60V270Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="221" d="M107 -5Q72 -5 72 30V37Q72 72 107 72H114Q149 72 149 37V30Q149 -5 114 -5H107Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="347" d="M273 647H323L66 -193H16L273 647Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="655" d="M326 -10Q268 -10 222 11T142 71T92 166T74 291V355Q74 424 91 480T142 575T222 636T327 657Q385 657 432 636T512 576T563 481T581 356V293Q581 224 563 168T512 73T432 12T326 -10ZM327 33Q375 33 412 51T476 102T517 184T531 293V353Q531 413 517 461T476 544T412 596T327 615Q280 615 242 597T178 545T138 463T124 355V294Q124 234 138 186T178 103T242 51T327 33Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="366" d="M186 564Q174 544 153 533T105 521Q68 521 48 541Q36 553 36 569Q36 582 43 589T52 596Q60 585 74 578T109 571Q143 571 166 594T192 650H213Q236 650 236 626V0H186V564Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="544" d="M92 0Q58 0 58 32V55Q58 108 75 146T120 214T186 265T262 307Q297 325 328 341T384 377T422 421T436 478Q436 537 395 574T276 612Q235 612 204 601T151 570T115 525T93 471Q93 469 88 469T76 472T64 480T59 497Q59 511 70 538T107 591Q135 619 177 638T279 658Q328 658 366 645T431 608T471 551T485 477Q485 436 470 406T429 353T368 310T293 272Q256 254 222 235T163 191T122 136T107 63V46H496V0H92Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="552" d="M258 -10Q192 -10 145 10T72 57Q51 78 42 97T32 129Q32 139 36 145T45 154T55 158T61 158Q78 104 130 69T259 34Q343 34 391 72T440 177Q440 246 393 283T258 320H169V363H265Q339 363 379 396T420 488Q420 545 378 580T259 615Q195 615 148 585T81 510Q80 509 76 510T66 515T56 524T52 538Q52 547 60 561T81 589Q109 616 154 636T259 657Q359 657 415 612T471 488Q471 431 439 395T350 345Q415 328 453 286T491 177Q491 134 475 100T429 41T355 3T258 -10Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="597" d="M388 193H88Q71 193 62 200T52 219V234Q52 245 57 254T73 276L352 622Q365 639 373 645T398 652Q420 652 428 644T437 619V238H555V193H437V0H388V193ZM390 238V604L97 238H390Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="554" d="M269 -10Q206 -10 158 10T80 59Q66 73 58 88T49 114Q49 122 53 127T63 136T73 141T79 141Q102 94 151 65T271 35Q313 35 346 46T404 78T441 129T454 197Q454 232 442 260T407 309T352 341T281 352Q179 352 110 302Q77 306 77 338V617Q77 630 85 638T107 647H466V602H125V359Q160 379 199 388T281 398Q331 398 372 384T442 344T488 281T504 199Q504 150 488 112T441 46T367 5T269 -10Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="604" d="M123 299Q152 339 204 368T327 397Q376 397 416 383T485 344T529 284T545 205V201Q545 155 529 117T482 50T411 6T321 -10Q263 -10 217 10T140 68T91 160T74 282V355Q74 427 91 483T141 577T220 636T325 657Q385 657 426 640T490 600Q504 586 510 572T517 549Q517 542 513 537T504 528T495 522T490 521Q469 559 429 587T325 615Q231 615 177 549T123 358V299ZM318 33Q357 33 390 45T446 78T483 130T496 197V201Q496 273 451 314T322 355Q289 355 259 347T203 324T157 290T125 248Q133 144 184 89T318 33ZM123 262V265V262Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="533" d="M162 -3Q149 -3 141 6T135 17L441 602H45V647H467Q484 647 490 641T496 625V620Q496 610 482 584L189 16Q184 5 177 1T162 -3Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="596" d="M298 -10Q242 -10 198 3T123 40T75 97T58 171Q58 228 96 273T208 336Q142 352 111 391T79 484Q79 522 94 553T138 608T207 644T298 657Q401 657 458 610T516 485Q516 432 485 393T388 336Q463 320 500 275T537 171Q537 132 520 99T472 41T396 4T298 -10ZM299 357Q379 357 422 390T466 483Q466 541 423 578T298 615Q215 615 172 579T129 483Q129 426 173 392T299 357ZM299 33Q392 33 439 70T487 173Q487 236 438 275T298 314Q207 314 158 275T108 173Q108 111 158 72T299 33Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="604" d="M277 33Q377 33 429 95T481 289V338Q467 319 447 301T401 270T344 248T277 240Q227 240 187 254T119 293T75 353T59 433V439Q59 485 75 524T122 593T193 640T283 657Q338 657 384 638T462 582T512 491T530 365V292Q530 142 464 66T277 -10Q218 -10 176 6T109 47Q93 64 87 78T80 102Q80 109 84 114T93 123T102 129T108 130Q128 89 169 61T277 33ZM281 282Q314 282 345 290T401 314T447 348T480 390Q473 499 422 557T285 615Q246 615 214 602T158 566T121 511T108 443V437Q108 364 153 323T281 282Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="245" d="M119 349Q84 349 84 383V391Q84 407 93 416T119 426H126Q143 426 152 417T161 391V383Q161 349 126 349H119ZM119 -5Q84 -5 84 30V37Q84 72 119 72H126Q161 72 161 37V30Q161 -5 126 -5H119Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="245" d="M119 349Q84 349 84 383V391Q84 407 93 416T119 426H126Q143 426 152 417T161 391V383Q161 349 126 349H119ZM68 -129Q66 -132 62 -133T52 -133T44 -128T43 -118L102 51Q107 65 112 71T131 78H135Q144 78 150 72T157 52V49Q157 43 156 35T146 13L68 -129Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="491" d="M77 234Q69 239 66 244T62 266Q62 282 65 287T77 298L404 479Q406 480 413 474T421 457Q422 449 416 440T391 420L109 266L391 113Q410 102 416 93T421 76Q420 65 414 59T404 53L77 234Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="550" d="M92 349Q69 349 69 369V374Q69 394 92 394H458Q472 394 476 389T481 374V369Q481 360 477 355T458 349H92ZM92 128Q69 128 69 148V153Q69 173 92 173H458Q472 173 476 168T481 153V148Q481 138 477 133T458 128H92Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="490" d="M85 53Q83 52 77 58T69 76Q68 84 74 93T99 113L381 266L99 420Q80 431 74 440T69 457Q70 467 76 473T85 479L412 298Q420 293 424 288T428 266Q428 250 424 245T412 234L85 53Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="488" d="M196 174Q197 221 207 254T234 310T269 350T309 379Q325 390 340 401T367 425T386 455T393 494Q393 548 356 580T253 613Q212 613 183 601T133 569T102 524T87 472Q86 471 81 471T69 475T57 485T51 502Q51 515 61 544T98 599Q124 624 162 640T254 657Q297 657 332 645T391 612T429 562T443 498Q443 469 435 447T412 408T381 377T344 351Q324 337 305 323T271 288T247 241T238 174H196ZM213 -3Q184 -3 184 26V41Q184 57 190 63T213 70H227Q244 70 250 64T257 41V26Q257 10 251 4T227 -3H213Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="765" d="M397 -10Q322 -10 260 14T154 82T86 187T62 323Q62 396 86 457T153 563T255 632T387 657Q460 657 519 636T621 576T685 481T708 356Q708 267 674 217T574 167Q534 167 508 188T475 244Q461 209 433 189T363 168Q334 168 310 179T267 210T239 257T229 317Q229 350 239 377T267 424T310 455T365 466Q401 466 428 448T470 405V461H517V297Q517 252 532 228T583 204Q606 204 622 215T649 247T665 296T670 356Q670 416 650 465T593 549T504 603T387 622Q323 622 270 600T179 538T120 443T99 323Q99 258 121 204T182 110T276 48T397 26Q449 26 494 40T569 74Q575 79 581 81T595 84Q605 84 611 73T615 59Q598 46 575 34T524 12T464 -4T397 -10ZM374 210Q415 210 442 237T470 304V353Q458 383 434 404T375 425Q332 425 305 395T278 317Q278 270 305 240T374 210Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="625" d="M94 617Q94 630 102 638T123 647H328Q432 647 485 605T538 482Q538 369 414 337Q492 323 530 282T568 177Q568 86 511 43T335 0H94V617ZM334 45Q428 45 472 76T516 179Q516 309 336 309H144V45H334ZM331 353Q405 353 445 387T486 479Q486 602 328 602H144V353H331Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="687" d="M383 -10Q311 -10 252 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q460 657 511 634T591 581Q608 564 617 547T626 520Q626 511 622 505T612 495T602 490T596 490Q570 544 514 579T383 614Q323 614 274 593T190 532T135 440T115 323Q115 260 134 207T189 116T275 55T385 33Q430 33 466 44T529 75T575 120T603 172Q604 173 609 172T620 168T631 159T636 143Q636 132 626 110T592 66Q563 37 510 14T383 -10Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="692" d="M94 617Q94 630 102 638T123 647H276Q360 647 425 625T536 562T605 460T629 325Q629 249 605 189T534 87T422 23T271 0H94V617ZM274 44Q420 44 498 118T577 324Q577 389 557 440T498 528T403 583T276 603H144V44H274Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="554" d="M94 617Q94 630 102 638T123 647H512V602H144V333H422V289H144V0H94V617Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="724" d="M368 -10Q298 -10 242 15T146 84T85 189T64 322Q64 395 87 456T152 562T252 632T382 657Q456 657 507 634T586 583Q604 565 612 549T621 524Q621 513 617 507T607 498T597 495T591 495Q579 520 559 541T511 579T451 604T380 614Q321 614 273 593T189 532T135 440T115 323Q115 261 132 208T183 116T265 55T374 33Q422 33 462 47T533 88T582 151T605 229V273H381V318H612Q629 318 639 308T649 280V2Q649 1 645 -1T633 -3Q620 -3 613 6T605 46V126Q579 63 517 27T368 -10Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="702" d="M94 627Q94 650 115 650H122Q144 650 144 627V347H558V627Q558 650 579 650H586Q608 650 608 627V0H558V301H144V0H94V627Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="466" d="M200 -10Q161 -10 132 1T82 27T51 60T40 88Q40 95 44 101T53 112T63 117T69 118Q84 81 118 57T200 33Q270 33 301 73T333 203V647H383V203Q383 94 339 42T200 -10Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="562" d="M94 627Q94 650 115 650H122Q144 650 144 627V337L451 635Q463 647 473 649T491 648T502 638T505 624L199 332L514 23Q515 15 510 8T497 -2T479 -3T459 9L144 325V0H94V627Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="530" d="M94 627Q94 650 115 650H122Q144 650 144 627V46H461Q484 46 484 25V21Q484 0 461 0H94V627Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="765" d="M94 618Q94 632 103 641T129 650H141Q161 650 168 642T185 613L383 221L582 613Q590 633 598 641T625 650H635Q652 650 661 641T671 618V0H623V593L425 206Q419 192 410 184T382 175Q363 175 354 183T339 206L141 593V0H94V618Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="703" d="M94 616Q94 630 100 640T121 650H134Q148 650 156 643T173 623L563 77V647H610V30Q610 16 603 7T584 -3H580Q566 -3 560 2T546 18L141 585V0H94V616Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="576" d="M94 617Q94 630 102 638T123 647H289Q404 647 465 597T527 453Q527 358 466 307T284 256H144V0H94V617ZM288 300Q382 300 428 339T475 451Q475 525 429 563T290 602H144V300H288Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 255 679 196T617 92L694 14Q696 12 689 2T665 -9Q653 -9 639 5L584 61Q543 27 493 9T382 -10ZM383 33Q434 33 476 48T552 91L399 246L430 279L584 123Q616 161 633 212T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="590" d="M288 277L528 21Q530 18 522 8T498 -3Q482 -3 467 15L224 277H143V0H94V617Q94 630 102 638T123 647H298Q413 647 472 601T531 463Q531 373 473 325T310 277H288ZM303 320Q393 320 436 358T479 461Q479 530 435 566T300 602H143V320H303Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="565" d="M288 -10Q216 -10 166 11T87 60Q70 77 63 92T55 119Q55 127 59 133T69 143T79 148T85 149Q95 128 112 107T153 70T211 43T287 33Q367 33 412 68T458 166Q458 201 443 224T402 262T343 287T272 306Q234 315 199 326T135 357T89 407T72 483Q72 521 88 553T132 608T199 644T283 657Q349 657 394 638T464 595Q478 581 485 567T492 543Q492 535 488 529T478 519T468 513T462 512Q453 532 438 550T400 583T348 605T283 614Q248 614 218 605T167 579T133 538T121 484Q121 451 134 430T171 394T227 370T297 352Q336 343 373 331T441 298T489 246T507 168Q507 128 492 95T447 39T378 3T288 -10Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="599" d="M275 602H61Q38 602 38 622V626Q38 647 61 647H538Q562 647 562 626V622Q562 602 538 602H325V0H275V602Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="623" d="M31 635Q30 638 37 643T53 649T73 647T90 626L312 53L535 626Q541 642 551 646T570 650T586 643T592 635L340 0H284L31 635Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="912" d="M66 639Q66 642 73 646T91 650T110 643T125 617L260 45L417 518Q422 532 430 539T458 547Q478 547 485 540T497 518L653 46L791 617Q795 636 804 643T823 650T839 646T846 639L687 0H620L458 497L290 0H225L66 639Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="598" d="M63 -3Q50 -3 43 7T38 21L270 329L47 647H101L301 364L505 636Q517 652 530 652Q543 652 550 642T555 629L328 328L558 23Q560 20 553 10T532 -1Q524 -1 517 2T504 14L298 295L88 13Q76 -3 63 -3Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="568" d="M259 277L24 647H79L284 322L489 632Q498 646 508 648T526 648T539 639T544 629L309 277V0H259V277Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="615" d="M61 53L488 602H89Q66 602 66 622V626Q66 647 89 647H543V597L115 46H533Q557 46 557 25V21Q557 0 533 0H61V53Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="352" d="M99 647H287Q301 647 305 642T310 628V623Q310 613 306 609T287 604H145V-149H287Q301 -149 305 -154T310 -169V-174Q310 -183 306 -188T287 -193H99V647Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="347" d="M23 647H73L330 -193H280L23 647Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="352" d="M65 -193Q51 -193 47 -188T42 -174V-169Q42 -159 46 -154T65 -149H207V604H65Q51 604 47 608T42 623V628Q42 637 46 642T65 647H253V-193H65Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="530" d="M161 450Q145 450 145 467Q145 479 158 497T196 544Q225 576 244 592T265 609Q267 609 286 593T332 546Q357 517 370 498T384 467Q384 450 369 450Q359 450 346 462T304 513L264 566L224 511Q199 478 185 464T161 450Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="482" d="M51 -46Q28 -46 28 -25V-21Q28 0 51 0H431Q454 0 454 -21V-25Q454 -46 431 -46H51Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="530" d="M334 547Q332 545 320 548T289 559T244 581T192 615Q169 632 162 641T154 661Q154 670 160 675T175 681Q183 681 193 677T220 659Q246 639 267 620T304 585T327 559T334 547Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="596" d="M316 -10Q280 -10 250 -1T197 23T157 57T131 96V0H84V673Q84 678 100 678Q113 678 122 669T132 631V376Q161 424 207 453T319 483Q366 483 407 465T478 415T526 338T544 238Q544 183 527 138T480 59T407 8T316 -10ZM312 32Q353 32 387 47T445 89T482 154T496 238Q496 282 482 319T444 383T387 425T314 441Q245 441 200 406T131 312V203Q131 167 145 136T183 82T241 46T312 32Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="542" d="M293 -10Q240 -10 196 9T120 61T70 139T52 238Q52 290 70 335T120 413T196 464T293 483Q353 483 393 465T457 422Q476 403 484 385T493 355Q493 347 489 342T479 334T468 331T462 331Q454 352 440 372T406 407T358 432T293 441Q251 441 216 426T155 383T115 318T100 238Q100 194 114 157T153 92T214 49T293 33Q330 33 358 42T406 66T440 101T462 142Q463 143 468 143T478 140T488 132T493 118Q493 107 485 89T456 50Q432 26 392 8T293 -10Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="596" d="M277 -10Q229 -10 188 8T117 58T69 135T52 237Q52 291 69 336T116 414T189 465T280 483Q314 483 343 475T396 452T437 420T464 381V673Q464 678 480 678Q493 678 502 669T512 631V0H464V96Q438 48 392 19T277 -10ZM282 32Q320 32 353 45T411 82T450 136T464 202V313Q442 371 397 406T283 441Q243 441 210 426T152 384T114 320T100 238Q100 193 113 155T151 90T208 48T282 32Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="341" d="M127 430H28V473H127V524Q127 605 161 646T259 688Q288 688 307 681T338 662Q349 651 349 637Q349 625 343 619T335 614Q324 628 303 637T259 646Q216 646 195 617T173 521V473H298V430H174V0H127V430Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="596" d="M291 -197Q227 -197 183 -179T112 -134Q98 -120 91 -106T83 -82Q83 -73 87 -67T96 -57T105 -52T111 -52Q120 -72 135 -90T173 -123T224 -145T292 -154Q464 -154 464 26V107Q438 61 392 34T277 6Q227 6 186 23T115 71T69 146T52 244Q52 296 69 339T118 415T190 465T280 483Q350 483 397 454T464 384V473H512V26Q512 -86 454 -141T291 -197ZM282 47Q319 47 352 59T410 94T449 147T464 213V314Q442 372 398 406T283 441Q243 441 210 427T152 386T114 323T100 245Q100 200 113 164T150 101T207 61T282 47Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="571" d="M84 673Q84 678 100 678Q113 678 122 669T132 631V380Q153 426 197 454T300 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 348 406 393T297 438Q262 438 232 426T179 393T145 340T132 272V0H84V673Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM112 601Q80 601 80 634Q80 668 114 668Q147 668 147 634Q147 601 112 601Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="226" d="M15 -190Q-11 -190 -29 -184T-58 -167Q-68 -157 -68 -147Q-68 -136 -62 -128T-54 -120Q-43 -131 -26 -139T15 -148Q89 -148 89 -50V473H137V-53Q137 -119 106 -154T15 -190ZM112 601Q80 601 80 634Q80 668 114 668Q147 668 147 634Q147 601 112 601Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="459" d="M84 673Q84 678 100 678Q113 678 122 669T132 631V255L348 457Q363 472 374 474T392 474T402 464T405 453L185 252L433 22Q433 18 430 11T420 0T402 -2T376 16L132 244V0H84V673Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="226" d="M89 673Q89 678 105 678Q118 678 127 669T137 631V0H89V673Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="886" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V387Q154 431 196 457T289 483Q352 483 393 453T452 372Q474 422 518 452T624 483Q712 483 759 431T806 279V0H758V276Q758 354 723 396T621 438Q573 438 531 410T469 328V0H421V287Q421 358 387 398T284 438Q237 438 195 409T132 329V0H84V471Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="571" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V385Q155 428 198 455T301 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 347 406 392T299 438Q239 438 197 408T132 328V0H84V471Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="596" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V376Q160 424 206 453T319 483Q366 483 407 465T478 415T526 338T544 238Q544 183 527 138T480 59T407 8T316 -10Q280 -10 251 -1T198 23T158 57T132 96V-190H84V471ZM312 32Q353 32 387 47T445 89T482 154T496 238Q496 282 482 319T444 383T387 425T314 441Q245 441 200 406T131 312V203Q131 167 145 136T183 82T241 46T312 32Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="596" d="M464 95Q438 48 392 19T277 -10Q229 -10 188 8T117 58T69 135T52 237Q52 291 69 336T116 414T189 465T280 483Q315 483 344 475T397 452T437 419T464 380V428Q464 457 473 466T495 476Q502 476 507 474T512 471V-190H464V95ZM282 32Q320 32 353 45T411 82T450 136T464 203V311Q442 370 397 405T283 441Q243 441 210 426T152 384T114 320T100 238Q100 193 113 155T151 90T208 48T282 32Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="358" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V369Q146 429 180 456T262 483Q290 483 309 472T328 443Q328 429 322 423T314 418Q305 426 291 431T257 437Q193 437 163 379T132 225V0H84V471Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="474" d="M237 -10Q174 -10 133 6T69 45Q56 58 51 67T46 84Q46 91 50 96T58 105T67 109T72 110Q94 76 135 53T239 30Q304 30 339 56T375 122Q375 146 365 161T335 187T288 204T228 218Q199 224 169 231T115 253T76 289T60 346Q60 376 72 401T108 445T164 473T237 483Q293 483 331 468T390 431Q408 413 408 396Q408 389 404 384T396 375T387 371T382 371Q364 402 326 422T237 443Q175 443 141 418T106 353Q106 330 116 316T144 291T189 274T248 260Q278 254 309 246T365 224T406 187T422 129Q422 67 373 29T237 -10Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="362" d="M237 -10Q174 -10 144 26T113 137V430H35V473H113V565Q113 579 119 584T134 589H140Q149 589 154 584T160 565V473H317V430H160V142Q160 83 178 57T239 31Q265 31 284 42T316 72Q318 74 325 68T332 48Q332 42 329 34T316 18Q305 6 286 -2T237 -10Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="491" d="M30 462Q30 466 36 471T52 476T71 471T89 444L246 43L404 444Q411 464 421 470T440 476T455 470T461 462L274 0H218L30 462Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="694" d="M44 463Q44 465 51 470T67 476T86 470T101 441L198 44L312 379Q315 390 322 395T349 400Q367 400 374 395T386 379L499 44L595 441Q600 463 609 470T628 476T644 470T651 463L529 0H470L349 353L225 0H166L44 463Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="485" d="M64 -3Q50 -3 44 6T39 18L213 237L42 455Q40 457 46 466T66 476Q79 476 86 470T102 452L244 267L384 451Q395 465 403 470T420 476Q434 476 440 467T445 455L272 237L443 18Q445 16 439 7T419 -3Q406 -3 399 3T383 21L240 208L100 22Q91 10 83 4T64 -3Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="488" d="M182 -193Q172 -193 164 -188T157 -180L241 26Q228 26 222 30T211 45L25 473H75L254 56L413 473H464L216 -161Q209 -181 201 -187T182 -193Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="490" d="M51 37L370 432H78Q64 432 59 436T54 450V455Q54 464 59 468T78 473H434V440L112 41H416Q441 41 441 23V18Q441 0 416 0H51V37Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="424" d="M369 -196Q346 -196 313 -180T246 -130Q208 -92 181 -25T149 138L74 182Q55 192 48 200T41 228Q41 238 42 244T48 256T58 265T74 274L149 318Q153 412 180 480T246 586Q280 620 313 636T369 652Q388 652 388 631Q388 626 386 620T381 612Q284 585 243 506T197 293L83 228L197 163Q201 28 242 -51T381 -157Q383 -158 385 -164T388 -176Q388 -196 369 -196Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="260" d="M127 -193Q118 -193 112 -189T106 -170V627Q106 641 112 645T127 650H133Q142 650 148 646T154 627V-170Q154 -184 148 -188T133 -193H127Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="424" d="M54 -196Q35 -196 35 -176Q35 -171 37 -165T42 -157Q139 -130 180 -51T226 163L340 228L226 293Q222 427 181 506T42 612Q40 613 38 619T35 631Q35 652 54 652Q77 652 110 636T177 586Q215 548 242 480T274 318L349 274Q358 269 364 265T375 256T380 245T382 228Q382 208 375 200T349 182L274 138Q270 43 243 -24T177 -130Q143 -164 110 -180T54 -196Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="529" d="M325 167Q300 167 282 179T248 208Q233 223 223 230T202 237Q187 237 180 226T168 195Q165 184 161 179T147 173Q138 173 129 179Q132 224 150 251T203 278Q229 278 247 266T280 237Q295 221 305 214T327 207Q341 207 348 218T361 249Q364 260 368 266T382 272Q392 272 399 265Q397 220 379 194T325 167Z" />
+<glyph unicode="&#xA0;" glyph-name="uni00A0" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="262" d="M134 474Q145 474 145 454L161 0H101L116 454Q117 464 118 469T128 474H134ZM135 652Q169 652 169 617V610Q169 575 135 575H127Q93 575 93 610V617Q93 652 127 652H135Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="560" d="M298 -98Q289 -98 284 -94T278 -75V33Q229 37 189 56T120 108T76 183T60 281Q60 332 76 375T122 452T191 505T278 530V634Q278 648 283 652T298 657H302Q312 657 317 653T322 634V531Q375 528 412 511T473 469Q488 454 496 439T504 416Q504 408 500 402T492 393T483 388T477 387Q457 429 420 456T320 488V75Q383 77 423 107T482 183Q483 184 487 183T497 179T507 171T511 157Q511 146 502 129T474 93Q451 70 413 52T322 32V-75Q322 -89 317 -93T302 -98H298ZM280 488Q242 485 211 469T157 427T122 363T109 281Q109 237 121 202T157 140T211 97T280 77V488Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="590" d="M61 0Q38 0 38 21V25Q38 46 61 46H123L164 268H74Q51 268 51 288V293Q51 313 74 313H172L200 463Q217 559 261 609T388 659Q429 659 458 647T507 614Q525 595 534 573T543 531Q543 520 538 514T527 506T515 503T508 503Q501 555 471 585T388 616Q324 616 294 578T248 460L221 313H404Q427 313 427 293V288Q427 268 404 268H213L172 46H509Q532 46 532 24V21Q532 0 509 0H61Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="655" d="M402 -10Q299 -10 235 52T153 226H75Q61 226 56 230T51 245V249Q51 259 56 263T75 268H148Q147 276 147 284T147 302V347Q147 356 147 365T148 384H75Q61 384 56 388T51 403V407Q51 417 56 422T75 427H154Q163 480 184 522T236 595T309 641T398 657Q457 657 497 639T561 596Q578 578 586 561T595 530Q595 522 591 517T581 508T571 503T565 503Q556 525 542 545T507 581T459 606T395 615Q320 615 270 565T202 427H433Q456 427 456 407V403Q456 384 433 384H196Q195 375 195 366T195 347V300Q195 292 195 284T196 268H433Q456 268 456 249V245Q456 226 433 226H202Q219 138 270 86T400 33Q436 33 464 42T512 68T547 105T570 151Q571 152 575 151T585 147T595 138T600 123Q600 109 591 90T563 53Q537 27 497 9T402 -10Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="583" d="M288 -3Q268 -3 268 20V123H95Q72 123 72 142V147Q72 166 95 166H268V265H95Q72 265 72 284V288Q72 307 95 307H253L49 629Q47 632 53 638T69 647T89 649T108 633L291 326L477 632Q486 647 496 649T515 648T529 638T533 629L329 307H487Q510 307 510 288V284Q510 265 487 265H314V166H487Q510 166 510 147V142Q510 123 487 123H314V20Q314 -3 293 -3H288Z" />
+<glyph unicode="&#xA6;" glyph-name="brokenbar" horiz-adv-x="260" d="M127 -193Q118 -193 112 -189T106 -170V177Q106 191 112 195T127 200H133Q142 200 148 196T154 177V-170Q154 -184 148 -188T133 -193H127ZM127 257Q118 257 112 261T106 280V627Q106 641 112 645T127 650H133Q142 650 148 646T154 627V280Q154 266 148 262T133 257H127Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="531" d="M261 -157Q192 -157 147 -139T79 -97Q62 -80 55 -66T48 -41Q48 -33 52 -27T61 -18T70 -12T75 -11Q97 -56 141 -84T260 -113Q332 -113 374 -86T417 -11Q417 18 405 36T370 66T317 86T248 103Q212 111 177 121T114 147T69 189T52 253Q52 301 84 332T172 378Q127 394 99 422T70 505Q70 539 85 567T127 615T190 646T268 657Q331 657 371 640T435 599Q448 585 454 572T461 550Q461 542 457 537T449 527T440 522T434 521Q412 560 373 586T268 613Q237 613 210 606T162 586T130 553T118 511Q118 484 130 466T164 435T216 413T284 396Q320 388 355 378T417 352T462 310T480 247Q480 199 449 167T364 120Q385 113 403 103T435 77T457 41T465 -8Q465 -41 451 -68T409 -115T345 -146T261 -157ZM308 136Q335 139 358 148T398 172T424 205T434 242Q434 269 421 287T384 317T329 337T263 354L225 362Q197 359 174 349T134 325T108 293T98 257Q98 230 111 212T148 182T203 161T269 145L308 136Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="530" d="M345 545Q307 545 307 583Q307 621 344 621Q361 621 371 611T382 583Q382 565 372 555T345 545ZM186 545Q169 545 159 555T148 583Q148 601 158 611T185 621Q223 621 223 583Q223 565 213 555T186 545Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="779" d="M389 -10Q317 -10 258 14T155 83T88 188T64 322Q64 394 88 455T156 561T260 631T390 657Q462 657 522 633T625 564T692 459T716 325Q716 253 692 192T623 86T519 16T389 -10ZM391 26Q454 26 506 48T596 109T655 203T676 324Q676 388 655 443T595 537T504 599T389 622Q326 622 274 600T184 539T125 444T104 323Q104 258 125 204T185 110T275 48T391 26ZM398 145Q359 145 327 158T272 194T237 250T224 322Q224 360 237 392T273 449T328 486T397 500Q436 500 464 488T508 459Q529 438 529 419Q529 413 526 409T518 401T509 397T504 396Q492 423 465 441T396 459Q369 459 346 449T306 420T280 376T270 323Q270 293 279 268T306 225T347 197T400 186Q446 186 471 207T508 254Q509 255 513 254T521 250T530 243T534 231Q534 224 528 212T508 184Q493 169 465 157T398 145Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="446" d="M189 262Q125 262 88 289T50 365Q50 393 62 415T106 454T191 480T328 493V503Q328 556 300 586T214 617Q167 617 138 597T91 547Q91 547 87 547T79 550T70 558T66 571Q66 586 89 611Q107 628 138 642T217 657Q295 657 334 615T373 503V272Q373 270 369 269T358 267Q346 267 338 275T329 309V340Q311 305 274 284T189 262ZM215 -5Q183 -5 183 27V33Q183 65 215 65H221Q253 65 253 33V27Q253 -5 221 -5H215ZM195 299Q223 299 247 308T290 332T318 367T329 409V460Q257 457 212 450T141 430T106 402T96 367Q96 335 122 317T195 299Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="518" d="M434 83Q420 83 397 99T334 149Q292 185 269 211T245 242Q245 246 268 271T333 333Q372 367 396 384T434 401Q442 401 447 396T453 383Q453 372 433 353T366 298Q346 282 328 269T290 241Q310 227 329 214T368 183Q413 148 433 130T453 100Q453 93 448 88T434 83ZM235 94Q222 94 201 109T143 155Q103 189 81 213T59 242Q59 246 81 270T143 328Q180 360 201 374T235 389Q251 389 251 372Q251 364 234 347T175 297Q156 282 139 269T101 241Q121 227 140 213T179 183Q219 151 235 135T251 111Q251 94 235 94Z" />
+<glyph unicode="&#xAC;" glyph-name="logicalnot" horiz-adv-x="600" d="M49 380H543V96H453V297H49V380Z" />
+<glyph unicode="&#xAD;" glyph-name="uni00AD" horiz-adv-x="381" d="M60 270H320V226H60V270Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="779" d="M389 -10Q317 -10 258 14T155 83T88 188T64 322Q64 394 88 455T156 561T260 631T390 657Q462 657 522 633T625 564T692 459T716 325Q716 253 692 192T623 86T519 16T389 -10ZM391 26Q454 26 506 48T596 109T655 203T676 324Q676 388 655 443T595 537T504 599T389 622Q326 622 274 600T184 539T125 444T104 323Q104 258 125 204T185 110T275 48T391 26ZM528 395Q528 349 498 322T408 294L525 172Q527 169 520 161T501 153Q494 153 486 156T470 169L360 291H317V174Q317 153 298 153H291Q272 153 272 176V472Q272 499 297 499H398Q461 499 494 473T528 395ZM399 328Q441 328 462 345T484 395Q484 461 397 461H316V328H399Z" />
+<glyph unicode="&#xAF;" glyph-name="uni00AF" horiz-adv-x="530" d="M163 567Q134 567 134 588V592Q134 612 163 612H367Q396 612 396 592V588Q396 567 367 567H163Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="412" d="M204 370Q174 370 149 380T105 409T77 454T66 510V518Q66 548 77 574T107 618T152 648T208 659Q238 659 263 649T307 619T335 575T346 519V511Q346 481 335 455T305 411T260 381T204 370ZM206 409Q248 409 276 438T305 510V518Q305 539 297 558T276 590T244 612T205 620Q163 620 135 591T107 519V511Q107 490 114 471T135 439T167 417T206 409Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="630" d="M337 450H482Q496 450 500 446T505 431V426Q505 417 501 412T482 407H337V253Q337 239 332 235T317 230H313Q303 230 298 234T292 253V407H147Q124 407 124 426V431Q124 450 147 450H292V596Q292 610 297 615T313 620H317Q326 620 331 615T337 596V450ZM132 128Q118 128 114 133T109 148V153Q109 163 113 168T132 173H498Q521 173 521 153V148Q521 128 498 128H132Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="366" d="M98 325H293Q315 325 315 307V304Q315 285 293 285H88Q71 285 65 291T58 311V327Q58 355 67 376T92 412T128 440T171 463Q190 472 206 480T235 499T255 520T263 548Q263 577 243 595T183 614Q141 614 120 592T90 540Q90 539 85 539T73 541T62 548T57 562Q57 573 65 590T91 621Q108 635 131 644T187 654Q246 654 277 626T309 549Q309 525 301 508T278 479T245 456T204 435Q183 426 164 416T131 394T107 365T98 327V325Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="366" d="M310 386Q310 336 274 308T174 279Q147 279 126 285T89 301Q66 314 54 330T42 358Q42 367 46 372T55 379T65 382T70 382Q79 353 105 335T173 316Q215 316 241 336T268 388Q268 420 243 438T176 457H144Q122 457 122 474V477Q122 493 144 493H181Q216 493 237 509T258 553Q258 582 235 599T173 616Q137 616 113 599T80 559Q80 559 76 559T66 562T57 570T53 583Q53 594 62 606T88 629Q104 640 126 647T174 654Q235 654 267 628T300 557Q300 525 283 506T236 478Q271 468 290 445T310 386Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="530" d="M196 547Q195 548 203 558T226 585T262 620T310 659Q327 672 337 676T355 681Q364 681 370 676T377 661Q377 651 369 642T339 615Q312 595 287 582T242 560T210 548T196 547Z" />
+<glyph unicode="&#xB5;" glyph-name="uni00B5" horiz-adv-x="571" d="M80 473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10Q222 -10 187 5T128 47V-260H80V473Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="614" d="M492 -3Q470 -3 470 20V627Q470 650 492 650H498Q520 650 520 627V20Q520 -3 498 -3H492ZM391 -3Q380 -3 375 3T369 20V304H280Q168 304 109 346T50 473Q50 558 106 603T275 648H388Q402 648 410 640T418 618V20Q418 -3 397 -3H391Z" />
+<glyph unicode="&#xB7;" glyph-name="uni00B7" horiz-adv-x="236" d="M114 210Q80 210 80 244V252Q80 287 114 287H121Q156 287 156 252V244Q156 210 121 210H114Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="530" d="M209 -213Q207 -216 202 -216T192 -216T184 -211T184 -201L246 -74Q251 -62 256 -55T276 -48H280Q290 -48 297 -54T305 -70V-73Q305 -79 300 -86T286 -105L209 -213Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="366" d="M89 285Q67 285 67 303V305Q67 325 89 325H172V582Q163 570 149 564T117 557Q106 557 95 560T77 571Q65 580 65 594Q65 605 71 611T80 617Q92 600 120 600Q142 600 159 613T178 650H195Q217 650 217 627V325H290Q312 325 312 305V303Q312 285 290 285H89Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="494" d="M247 261Q206 261 171 276T110 317T70 380T55 458Q55 500 70 536T111 599T172 641T248 657Q289 657 324 642T384 601T424 538T439 460Q439 418 425 382T384 319T323 277T247 261ZM243 -5Q212 -5 212 27V33Q212 65 243 65H250Q282 65 282 33V27Q282 -5 250 -5H243ZM247 301Q279 301 306 313T353 346T384 397T395 459Q395 492 384 521T353 571T306 605T248 617Q216 617 189 605T142 572T111 521T100 459Q100 426 111 397T142 347T188 314T247 301Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="518" d="M84 83Q76 83 71 88T66 101Q66 112 86 131T152 186L228 243Q208 257 189 270T150 300Q105 335 86 354T66 384Q66 391 71 396T84 401Q98 401 121 385T184 335Q226 298 249 273T273 242Q273 237 250 212T185 150Q146 116 122 100T84 83ZM283 95Q267 95 267 111Q267 120 284 137T343 187Q362 202 379 215T417 243Q397 257 378 271T339 301Q300 333 284 349T267 373Q267 389 283 389Q296 389 317 375T375 329Q415 294 437 270T460 242Q460 237 438 214T375 155Q338 124 317 110T283 95Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="822" d="M89 285Q67 285 67 303V305Q67 325 89 325H172V582Q163 570 149 564T117 557Q106 557 95 560T77 571Q65 580 65 594Q65 605 71 611T80 617Q92 600 120 600Q142 600 159 613T178 650H195Q217 650 217 627V325H290Q312 325 312 305V303Q312 285 290 285H89ZM680 616Q680 608 676 599T661 576Q656 569 632 540T575 470T505 384T438 302Q418 277 391 243T334 173T277 100T226 36T189 -9T175 -27Q173 -29 163 -26T153 -11Q153 0 156 7T171 29Q176 36 200 65T258 135T328 220T395 302Q410 321 429 345T470 395T513 450T557 504Q606 566 658 632Q658 632 661 632T669 631T676 626T680 616ZM700 -2Q681 -2 681 20V102H522Q508 102 500 108T492 123V133Q493 141 496 147T506 162L647 345Q655 357 663 361T689 366Q708 366 715 359T723 335V140H770Q791 140 791 122V120Q791 102 770 102H723V20Q723 -2 703 -2H700ZM534 139H682L681 329L534 139Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="822" d="M554 40H749Q771 40 771 22V19Q771 0 749 0H544Q527 0 521 6T514 26V42Q514 70 523 91T548 127T584 155T627 178Q646 187 662 195T691 214T711 235T719 263Q719 292 699 310T639 329Q597 329 576 307T546 255Q546 254 541 254T529 256T518 263T513 277Q513 288 521 305T547 336Q564 350 587 359T643 369Q702 369 733 341T765 264Q765 240 757 223T734 194T701 171T660 150Q639 141 620 131T587 109T563 80T554 42V40ZM660 616Q660 608 656 599T641 576Q636 569 612 540T555 470T485 384T418 302Q398 277 371 243T314 173T257 100T206 36T169 -9T155 -27Q153 -29 143 -26T133 -11Q133 0 136 7T151 29Q156 36 180 65T238 135T308 220T375 302Q390 321 409 345T450 395T493 450T537 504Q586 566 638 632Q638 632 641 632T649 631T656 626T660 616ZM89 285Q67 285 67 303V305Q67 325 89 325H172V582Q163 570 149 564T117 557Q106 557 95 560T77 571Q65 580 65 594Q65 605 71 611T80 617Q92 600 120 600Q142 600 159 613T178 650H195Q217 650 217 627V325H290Q312 325 312 305V303Q312 285 290 285H89Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="822" d="M700 -2Q681 -2 681 20V102H522Q508 102 500 108T492 123V133Q493 141 496 147T506 162L647 345Q655 357 663 361T689 366Q708 366 715 359T723 335V140H770Q791 140 791 122V120Q791 102 770 102H723V20Q723 -2 703 -2H700ZM310 386Q310 336 274 308T174 279Q147 279 126 285T89 301Q66 314 54 330T42 358Q42 367 46 372T55 379T65 382T70 382Q79 353 105 335T173 316Q215 316 241 336T268 388Q268 420 243 438T176 457H144Q122 457 122 474V477Q122 493 144 493H181Q216 493 237 509T258 553Q258 582 235 599T173 616Q137 616 113 599T80 559Q80 559 76 559T66 562T57 570T53 583Q53 594 62 606T88 629Q104 640 126 647T174 654Q235 654 267 628T300 557Q300 525 283 506T236 478Q271 468 290 445T310 386ZM680 616Q680 608 676 599T661 576Q656 569 632 540T575 470T505 384T438 302Q418 277 391 243T334 173T277 100T226 36T189 -9T175 -27Q173 -29 163 -26T153 -11Q153 0 156 7T171 29Q176 36 200 65T258 135T328 220T395 302Q410 321 429 345T470 395T513 450T557 504Q606 566 658 632Q658 632 661 632T669 631T676 626T680 616ZM534 139H682L681 329L534 139Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="488" d="M261 577Q244 577 238 583T231 606V621Q231 637 237 643T261 650H275Q304 650 304 621V606Q304 590 298 584T275 577H261ZM235 -10Q190 -10 155 2T95 36T58 87T45 148Q45 178 53 200T76 239T108 270T144 296Q164 309 183 323T217 356T241 400T250 461Q250 469 256 472T271 475Q292 475 292 458Q291 416 281 386T254 334T218 297T179 268Q162 257 147 246T121 222T102 191T95 152Q95 101 131 68T237 34Q277 34 306 46T355 78T386 123T401 175Q402 176 407 176T419 172T431 163T437 146Q437 132 427 104T390 49Q364 23 327 7T235 -10Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM353 718Q351 716 339 719T308 730T263 752T211 786Q188 803 181 812T173 832Q173 841 179 846T194 852Q202 852 212 848T239 830Q265 810 286 791T323 756T346 730T353 718Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM263 721Q262 722 270 732T293 759T329 794T377 833Q394 846 404 850T422 855Q431 855 437 850T444 835Q444 825 436 816T406 789Q379 769 354 756T309 734T277 722T263 721Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM209 712Q193 712 193 729Q193 740 206 758T244 805Q273 837 292 853T313 870Q315 870 334 854T380 807Q405 778 418 760T432 729Q432 712 417 712Q407 712 394 724T352 774L312 827L272 773Q247 739 233 726T209 712Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM377 717Q352 717 334 729T300 758Q285 773 275 780T254 787Q239 787 232 776T220 745Q217 734 213 728T199 722Q191 722 181 729Q184 774 202 801T255 828Q281 828 299 816T332 786Q347 771 357 764T379 757Q393 757 400 768T413 799Q416 810 420 816T434 822Q442 822 451 815Q449 770 431 744T377 717Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM393 716Q355 716 355 754Q355 792 392 792Q409 792 419 782T430 754Q430 736 420 726T393 716ZM234 716Q217 716 207 726T196 754Q196 772 206 782T233 792Q271 792 271 754Q271 736 261 726T234 716Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="627" d="M272 624Q242 635 224 661T206 722Q206 769 236 800T314 831Q362 831 391 800T421 722Q421 687 403 661T354 624L597 12Q598 9 591 4T574 -2T554 0T538 21L457 224H168L87 21Q81 5 71 1T52 -3T36 4T30 12L272 624ZM313 647Q344 647 364 668T384 722Q384 754 364 776T313 798Q282 798 262 776T242 722Q242 690 262 669T313 647ZM441 268L313 597L184 268H441Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="874" d="M575 0Q556 0 548 7T533 28L458 224H167L87 21Q81 5 71 1T52 -3T36 4T30 12L272 624Q277 634 284 640T305 647H806V602H361Q368 583 376 563Q383 546 392 524T410 478Q431 423 459 355H736V309H478L582 46H820V0H575ZM441 268L313 595L184 268H441Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="687" d="M383 -10Q311 -10 252 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q460 657 511 634T591 581Q608 564 617 547T626 520Q626 511 622 505T612 495T602 490T596 490Q570 544 514 579T383 614Q323 614 274 593T190 532T135 440T115 323Q115 260 134 207T189 116T275 55T385 33Q430 33 466 44T529 75T575 120T603 172Q604 173 609 172T620 168T631 159T636 143Q636 132 626 110T592 66Q563 37 510 14T383 -10ZM308 -213Q306 -216 301 -216T291 -216T283 -211T283 -201L345 -74Q350 -62 355 -55T375 -48H379Q389 -48 396 -54T404 -70V-73Q404 -79 399 -86T385 -105L308 -213Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM382 721Q380 719 368 722T337 733T292 755T240 789Q217 806 210 815T202 835Q202 844 208 849T223 855Q231 855 241 851T268 833Q294 813 315 794T352 759T375 733T382 721Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM246 721Q245 722 253 732T276 759T312 794T360 833Q377 846 387 850T405 855Q414 855 420 850T427 835Q427 825 419 816T389 789Q362 769 337 756T292 734T260 722T246 721Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM213 719Q197 719 197 736Q197 747 210 765T248 812Q277 844 296 860T317 877Q319 877 338 861T384 814Q409 785 422 767T436 736Q436 719 421 719Q411 719 398 731T356 781L316 834L276 780Q251 746 237 733T213 719Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM395 719Q357 719 357 757Q357 795 394 795Q411 795 421 785T432 757Q432 739 422 729T395 719ZM236 719Q219 719 209 729T198 757Q198 775 208 785T235 795Q273 795 273 757Q273 739 263 729T236 719Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM176 718Q174 716 162 719T131 730T86 752T34 786Q11 803 4 812T-4 832Q-4 841 2 846T17 852Q25 852 35 848T62 830Q88 810 109 791T146 756T169 730T176 718Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM82 718Q81 719 89 729T112 756T148 791T196 830Q213 843 223 847T241 852Q250 852 256 847T263 832Q263 822 255 813T225 786Q198 766 173 753T128 731T96 719T82 718Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM25 716Q9 716 9 733Q9 744 22 762T60 809Q89 841 108 857T129 874Q131 874 150 858T196 811Q221 782 234 764T248 733Q248 716 233 716Q223 716 210 728T168 778L128 831L88 777Q63 743 49 730T25 716Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM209 719Q171 719 171 757Q171 795 208 795Q225 795 235 785T246 757Q246 739 236 729T209 719ZM50 719Q33 719 23 729T12 757Q12 775 22 785T49 795Q87 795 87 757Q87 739 77 729T50 719Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="741" d="M166 0Q153 0 145 8T136 30V302H60Q46 302 42 307T37 321V326Q37 335 41 340T60 346H136V617Q136 630 144 638T166 647H322Q405 647 471 625T583 561T653 460T678 325Q678 250 653 190T581 87T468 23T317 0H166ZM323 46Q469 46 548 119T627 324Q627 388 607 439T549 527T454 582T326 602H186V346H413Q436 346 436 326V321Q436 302 413 302H186V46H323Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="703" d="M94 616Q94 630 100 640T121 650H134Q148 650 156 643T173 623L563 77V647H610V30Q610 16 603 7T584 -3H580Q566 -3 560 2T546 18L141 585V0H94V616ZM416 713Q391 713 373 725T339 754Q324 769 314 776T293 783Q278 783 271 772T259 741Q256 730 252 724T238 718Q230 718 220 725Q223 770 241 797T294 824Q320 824 338 812T371 782Q386 767 396 760T418 753Q432 753 439 764T452 795Q455 806 459 812T473 818Q481 818 490 811Q488 766 470 740T416 713Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM424 721Q422 719 410 722T379 733T334 755T282 789Q259 806 252 815T244 835Q244 844 250 849T265 855Q273 855 283 851T310 833Q336 813 357 794T394 759T417 733T424 721Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM335 721Q334 722 342 732T365 759T401 794T449 833Q466 846 476 850T494 855Q503 855 509 850T516 835Q516 825 508 816T478 789Q451 769 426 756T381 734T349 722T335 721Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM276 719Q260 719 260 736Q260 747 273 765T311 812Q340 844 359 860T380 877Q382 877 401 861T447 814Q472 785 485 767T499 736Q499 719 484 719Q474 719 461 731T419 781L379 834L339 780Q314 746 300 733T276 719Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM440 718Q415 718 397 730T363 759Q348 774 338 781T317 788Q302 788 295 777T283 746Q280 735 276 729T262 723Q254 723 244 730Q247 775 265 802T318 829Q344 829 362 817T395 787Q410 772 420 765T442 758Q456 758 463 769T476 800Q479 811 483 817T497 823Q505 823 514 816Q512 771 494 745T440 718Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM460 719Q422 719 422 757Q422 795 459 795Q476 795 486 785T497 757Q497 739 487 729T460 719ZM301 719Q284 719 274 729T263 757Q263 775 273 785T300 795Q338 795 338 757Q338 739 328 729T301 719Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="550" d="M110 94Q104 100 103 108T111 126L247 265L111 403Q102 412 103 420T110 434L113 437Q119 443 126 444T145 434L277 295L409 434Q420 445 427 444T441 437L444 434Q450 428 450 420T442 403L307 265L442 126Q458 108 444 94L441 91Q435 85 428 84T409 94L277 234L145 94Q134 83 127 84T113 91L110 94Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="765" d="M86 -39Q85 -40 81 -39T73 -34T65 -25T61 -13Q61 -6 65 3T80 24L142 95Q104 140 84 197T64 322Q64 392 87 453T151 560T251 631T381 657Q441 657 491 639T580 589L659 680Q660 681 664 680T672 675T680 666T684 654Q684 646 680 638T665 617L613 557Q654 512 675 453T697 324Q697 254 674 193T610 87T510 16T379 -10Q318 -10 267 8T175 61Q168 53 161 45Q154 38 146 29T130 11L86 -39ZM547 554Q515 581 473 596T380 612Q319 612 270 590T187 529T133 437T114 323Q114 208 177 130L547 554ZM381 35Q442 35 491 57T574 118T628 209T647 323Q647 382 630 433T578 522Q475 403 393 310L207 98Q241 68 285 52T381 35Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM383 710Q381 708 369 711T338 722T293 744T241 778Q218 795 211 804T203 824Q203 833 209 838T224 844Q232 844 242 840T269 822Q295 802 316 783T353 748T376 722T383 710Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM279 710Q278 711 286 721T309 748T345 783T393 822Q410 835 420 839T438 844Q447 844 453 839T460 824Q460 814 452 805T422 778Q395 758 370 745T325 723T293 711T279 710Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM224 715Q208 715 208 732Q208 743 221 761T259 808Q288 840 307 856T328 873Q330 873 349 857T395 810Q420 781 433 763T447 732Q447 715 432 715Q422 715 409 727T367 777L327 830L287 776Q262 742 248 729T224 715Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM408 716Q370 716 370 754Q370 792 407 792Q424 792 434 782T445 754Q445 736 435 726T408 716ZM249 716Q232 716 222 726T211 754Q211 772 221 782T248 792Q286 792 286 754Q286 736 276 726T249 716Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="568" d="M259 277L24 647H79L284 322L489 632Q498 646 508 648T526 648T539 639T544 629L309 277V0H259V277ZM227 708Q226 709 234 719T257 746T293 781T341 820Q358 833 368 837T386 842Q395 842 401 837T408 822Q408 812 400 803T370 776Q343 756 318 743T273 721T241 709T227 708Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="582" d="M115 -3Q94 -3 94 20V627Q94 650 115 650H122Q144 650 144 627V521H290Q405 521 466 470T527 328Q527 234 467 184T285 133H144V20Q144 -3 122 -3H115ZM289 177Q383 177 429 215T475 326Q475 398 429 437T290 476H144V177H289Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="544" d="M319 -10Q275 -10 246 1T200 28Q190 38 186 47T181 62Q181 76 190 84T201 90Q217 67 244 50T320 32Q347 32 370 42T410 70T436 113T446 167Q446 196 438 222T409 269T356 308T275 336Q251 342 242 351T233 376Q233 387 238 395T246 406Q327 408 368 435T410 524Q410 576 375 609T277 642Q208 642 170 599T132 462V0H84V462Q84 574 136 629T278 685Q318 685 351 673T408 640T444 590T457 527Q457 460 417 421T289 375Q346 362 385 342T448 295T483 237T494 167Q494 129 481 97T444 41T388 4T319 -10Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM331 547Q329 545 317 548T286 559T241 581T189 615Q166 632 159 641T151 661Q151 670 157 675T172 681Q180 681 190 677T217 659Q243 639 264 620T301 585T324 559T331 547Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM196 547Q195 548 203 558T226 585T262 620T310 659Q327 672 337 676T355 681Q364 681 370 676T377 661Q377 651 369 642T339 615Q312 595 287 582T242 560T210 548T196 547Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM159 545Q143 545 143 562Q143 573 156 591T194 638Q223 670 242 686T263 703Q265 703 284 687T330 640Q355 611 368 593T382 562Q382 545 367 545Q357 545 344 557T302 607L262 660L222 606Q197 572 183 559T159 545Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM323 544Q298 544 280 556T246 585Q231 600 221 607T200 614Q185 614 178 603T166 572Q163 561 159 555T145 549Q137 549 127 556Q130 601 148 628T201 655Q227 655 245 643T278 613Q293 598 303 591T325 584Q339 584 346 595T359 626Q362 637 366 643T380 649Q388 649 397 642Q395 597 377 571T323 544Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM344 545Q306 545 306 583Q306 621 343 621Q360 621 370 611T381 583Q381 565 371 555T344 545ZM185 545Q168 545 158 555T147 583Q147 601 157 611T184 621Q222 621 222 583Q222 565 212 555T185 545Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM264 544Q216 544 187 574T157 652Q157 699 187 730T265 761Q313 761 342 730T372 652Q372 606 342 575T264 544ZM264 577Q295 577 315 598T335 652Q335 684 315 706T264 728Q233 728 213 706T193 652Q193 620 213 599T264 577Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="897" d="M216 -10Q140 -10 96 25T52 120Q52 160 72 189T134 236T240 264T394 276H404V297Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q333 483 377 450T435 354Q460 414 511 448T631 483Q679 483 718 466T786 419T831 346T847 251V247Q847 236 842 233T826 229H445Q446 185 460 149T500 88T561 48T638 33Q674 33 701 41T748 62T782 93T805 128Q806 129 810 128T820 124T829 115T833 100Q833 92 826 79T803 49Q781 27 739 9T638 -10Q558 -10 503 28T425 131Q415 99 396 73T349 29T287 0T216 -10ZM220 30Q259 30 292 42T350 75T389 125T404 190V238H392Q314 236 259 229T169 208T118 172T101 120Q101 78 133 54T220 30ZM631 441Q595 441 564 429T508 393T468 339T447 270H797Q793 347 748 394T631 441Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="542" d="M293 -10Q240 -10 196 9T120 61T70 139T52 238Q52 290 70 335T120 413T196 464T293 483Q353 483 393 465T457 422Q476 403 484 385T493 355Q493 347 489 342T479 334T468 331T462 331Q454 352 440 372T406 407T358 432T293 441Q251 441 216 426T155 383T115 318T100 238Q100 194 114 157T153 92T214 49T293 33Q330 33 358 42T406 66T440 101T462 142Q463 143 468 143T478 140T488 132T493 118Q493 107 485 89T456 50Q432 26 392 8T293 -10ZM230 -213Q228 -216 223 -216T213 -216T205 -211T205 -201L267 -74Q272 -62 277 -55T297 -48H301Q311 -48 318 -54T326 -70V-73Q326 -79 321 -86T307 -105L230 -213Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM336 547Q334 545 322 548T291 559T246 581T194 615Q171 632 164 641T156 661Q156 670 162 675T177 681Q185 681 195 677T222 659Q248 639 269 620T306 585T329 559T336 547Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM227 547Q226 548 234 558T257 585T293 620T341 659Q358 672 368 676T386 681Q395 681 401 676T408 661Q408 651 400 642T370 615Q343 595 318 582T273 560T241 548T227 547Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM181 545Q165 545 165 562Q165 573 178 591T216 638Q245 670 264 686T285 703Q287 703 306 687T352 640Q377 611 390 593T404 562Q404 545 389 545Q379 545 366 557T324 607L284 660L244 606Q219 572 205 559T181 545Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM365 545Q327 545 327 583Q327 621 364 621Q381 621 391 611T402 583Q402 565 392 555T365 545ZM206 545Q189 545 179 555T168 583Q168 601 178 611T205 621Q243 621 243 583Q243 565 233 555T206 545Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM165 547Q163 545 151 548T120 559T75 581T23 615Q0 632 -7 641T-15 661Q-15 670 -9 675T6 681Q14 681 24 677T51 659Q77 639 98 620T135 585T158 559T165 547Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM68 547Q67 548 75 558T98 585T134 620T182 659Q199 672 209 676T227 681Q236 681 242 676T249 661Q249 651 241 642T211 615Q184 595 159 582T114 560T82 548T68 547Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM12 542Q-4 542 -4 559Q-4 570 9 588T47 635Q76 667 95 683T116 700Q118 700 137 684T183 637Q208 608 221 590T235 559Q235 542 220 542Q210 542 197 554T155 604L115 657L75 603Q50 569 36 556T12 542Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM196 545Q158 545 158 583Q158 621 195 621Q212 621 222 611T233 583Q233 565 223 555T196 545ZM37 545Q20 545 10 555T-1 583Q-1 601 9 611T36 621Q74 621 74 583Q74 565 64 555T37 545Z" />
+<glyph unicode="&#xF0;" glyph-name="null" horiz-adv-x="583" d="M285 -10Q234 -10 192 5T120 48T74 118T57 210Q57 260 74 300T121 368T194 411T288 426Q320 426 349 418T402 396T445 364T475 327Q467 408 431 469T339 571L208 495Q188 484 180 500L178 504Q167 519 188 532L300 594Q257 618 209 631T111 646Q109 646 109 652T112 665T124 679T147 686Q164 686 187 683T236 671T292 651T349 619L463 685Q475 692 481 690T493 680L495 678Q500 670 498 663T484 649L383 593Q412 569 438 537T483 466T513 378T524 272Q524 205 507 153T459 64T384 9T285 -10ZM285 32Q334 32 369 50T428 100T463 176T475 275Q466 295 449 314T407 349T353 374T291 384Q203 384 155 340T106 211Q106 125 155 79T285 32Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="571" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V385Q155 428 198 455T301 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 347 406 392T299 438Q239 438 197 408T132 328V0H84V471ZM342 544Q317 544 299 556T265 585Q250 600 240 607T219 614Q204 614 197 603T185 572Q182 561 178 555T164 549Q156 549 146 556Q149 601 167 628T220 655Q246 655 264 643T297 613Q312 598 322 591T344 584Q358 584 365 595T378 626Q381 637 385 643T399 649Q407 649 416 642Q414 597 396 571T342 544Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM340 547Q338 545 326 548T295 559T250 581T198 615Q175 632 168 641T160 661Q160 670 166 675T181 681Q189 681 199 677T226 659Q252 639 273 620T310 585T333 559T340 547Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM247 547Q246 548 254 558T277 585T313 620T361 659Q378 672 388 676T406 681Q415 681 421 676T428 661Q428 651 420 642T390 615Q363 595 338 582T293 560T261 548T247 547Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM190 545Q174 545 174 562Q174 573 187 591T225 638Q254 670 273 686T294 703Q296 703 315 687T361 640Q386 611 399 593T413 562Q413 545 398 545Q388 545 375 557T333 607L293 660L253 606Q228 572 214 559T190 545Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM355 544Q330 544 312 556T278 585Q263 600 253 607T232 614Q217 614 210 603T198 572Q195 561 191 555T177 549Q169 549 159 556Q162 601 180 628T233 655Q259 655 277 643T310 613Q325 598 335 591T357 584Q371 584 378 595T391 626Q394 637 398 643T412 649Q420 649 429 642Q427 597 409 571T355 544Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM374 545Q336 545 336 583Q336 621 373 621Q390 621 400 611T411 583Q411 565 401 555T374 545ZM215 545Q198 545 188 555T177 583Q177 601 187 611T214 621Q252 621 252 583Q252 565 242 555T215 545Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="630" d="M128 244Q105 244 105 264V269Q105 289 128 289H501Q515 289 519 284T524 269V264Q524 255 520 250T501 244H128ZM311 375Q276 375 276 410V417Q276 452 311 452H318Q353 452 353 417V410Q353 375 318 375H311ZM311 81Q276 81 276 116V123Q276 158 311 158H318Q353 158 353 123V116Q353 81 318 81H311Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="588" d="M79 -32Q77 -34 67 -28T56 -10Q56 -4 59 4T73 23L117 71Q89 103 73 145T57 236Q57 287 75 332T126 410T202 463T297 483Q340 483 378 470T446 431L511 504Q513 506 523 500T533 482Q533 475 530 467T516 448L475 403Q504 370 519 328T535 237Q535 185 517 140T466 62T391 9T296 -10Q252 -10 214 3T146 42L79 -32ZM415 396Q391 416 361 428T296 440Q254 440 220 425T160 382T120 317T106 237Q106 199 117 165T150 105L415 396ZM297 33Q338 33 373 48T433 91T473 156T487 236Q487 274 475 308T441 369L176 77Q201 57 231 45T297 33Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM335 544Q333 542 321 545T290 556T245 578T193 612Q170 629 163 638T155 658Q155 667 161 672T176 678Q184 678 194 674T221 656Q247 636 268 617T305 582T328 556T335 544Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM243 544Q242 545 250 555T273 582T309 617T357 656Q374 669 384 673T402 678Q411 678 417 673T424 658Q424 648 416 639T386 612Q359 592 334 579T289 557T257 545T243 544Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM183 542Q167 542 167 559Q167 570 180 588T218 635Q247 667 266 683T287 700Q289 700 308 684T354 637Q379 608 392 590T406 559Q406 542 391 542Q381 542 368 554T326 604L286 657L246 603Q221 569 207 556T183 542Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM367 542Q329 542 329 580Q329 618 366 618Q383 618 393 608T404 580Q404 562 394 552T367 542ZM208 542Q191 542 181 552T170 580Q170 598 180 608T207 618Q245 618 245 580Q245 562 235 552T208 542Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="488" d="M182 -193Q172 -193 164 -188T157 -180L241 26Q228 26 222 30T211 45L25 473H75L254 56L413 473H464L216 -161Q209 -181 201 -187T182 -193ZM199 534Q198 535 206 545T229 572T265 607T313 646Q330 659 340 663T358 668Q367 668 373 663T380 648Q380 638 372 629T342 602Q315 582 290 569T245 547T213 535T199 534Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="603" d="M106 -193Q84 -193 84 -170V673Q84 678 100 678Q113 678 122 669T132 631V376Q160 424 206 453T319 483Q366 483 407 465T478 415T526 338T544 238Q544 183 527 138T480 59T407 8T316 -10Q280 -10 251 -1T198 23T158 57T132 96V-170Q132 -183 126 -188T111 -193H106ZM312 32Q353 32 387 47T445 89T482 154T496 238Q496 282 482 319T444 383T387 425T314 441Q245 441 200 406T131 312V203Q131 167 145 136T183 82T241 46T312 32Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="488" d="M182 -193Q172 -193 164 -188T157 -180L241 26Q228 26 222 30T211 45L25 473H75L254 56L413 473H464L216 -161Q209 -181 201 -187T182 -193ZM326 545Q288 545 288 583Q288 621 325 621Q342 621 352 611T363 583Q363 565 353 555T326 545ZM167 545Q150 545 140 555T129 583Q129 601 139 611T166 621Q204 621 204 583Q204 565 194 555T167 545Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM211 741Q182 741 182 762V766Q182 786 211 786H415Q444 786 444 766V762Q444 741 415 741H211Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM166 567Q137 567 137 588V592Q137 612 166 612H370Q399 612 399 592V588Q399 567 370 567H166Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H545L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441ZM314 719Q282 719 257 730T215 759Q199 776 192 793T184 820Q184 831 190 839T213 847Q226 809 252 786T314 762Q350 762 376 785T415 847Q432 847 438 839T444 820Q444 810 437 793T413 759Q396 742 371 731T314 719Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H404V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30ZM266 545Q234 545 209 556T167 585Q151 602 144 619T136 646Q136 657 142 665T165 673Q178 635 204 612T266 588Q302 588 328 611T367 673Q384 673 390 665T396 646Q396 636 389 619T365 585Q348 568 323 557T266 545Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="627" d="M276 633Q280 642 288 647T313 652Q331 652 339 647T351 633L601 0H584Q538 -32 521 -58T504 -105Q504 -124 515 -136T544 -148Q571 -148 584 -129Q592 -129 600 -135T608 -154Q608 -170 589 -180T543 -190Q506 -190 483 -170T460 -114Q460 -85 479 -55T544 3L457 224H168L79 0H26L276 633ZM441 268L313 595L184 268H441Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="531" d="M216 -10Q140 -10 96 25T52 120Q52 200 133 236T394 275H404V299Q404 366 366 402T259 439Q197 439 158 413T98 349Q98 349 94 350T84 354T75 362T71 377Q71 385 78 398T98 423Q121 446 161 464T262 483Q351 483 401 435T451 300V0H446Q398 -33 380 -59T362 -108Q362 -127 373 -139T402 -151Q429 -151 442 -132Q450 -132 458 -138T466 -157Q466 -173 447 -183T401 -193Q364 -193 341 -173T318 -117Q318 -88 338 -58T404 1V94Q381 48 332 19T216 -10ZM220 30Q259 30 292 42T350 76T389 127T404 190V237H391Q314 235 259 228T169 207T118 172T101 120Q101 78 133 54T220 30Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="687" d="M383 -10Q311 -10 252 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q460 657 511 634T591 581Q608 564 617 547T626 520Q626 511 622 505T612 495T602 490T596 490Q570 544 514 579T383 614Q323 614 274 593T190 532T135 440T115 323Q115 260 134 207T189 116T275 55T385 33Q430 33 466 44T529 75T575 120T603 172Q604 173 609 172T620 168T631 159T636 143Q636 132 626 110T592 66Q563 37 510 14T383 -10ZM338 721Q337 722 345 732T368 759T404 794T452 833Q469 846 479 850T497 855Q506 855 512 850T519 835Q519 825 511 816T481 789Q454 769 429 756T384 734T352 722T338 721Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="542" d="M293 -10Q240 -10 196 9T120 61T70 139T52 238Q52 290 70 335T120 413T196 464T293 483Q353 483 393 465T457 422Q476 403 484 385T493 355Q493 347 489 342T479 334T468 331T462 331Q454 352 440 372T406 407T358 432T293 441Q251 441 216 426T155 383T115 318T100 238Q100 194 114 157T153 92T214 49T293 33Q330 33 358 42T406 66T440 101T462 142Q463 143 468 143T478 140T488 132T493 118Q493 107 485 89T456 50Q432 26 392 8T293 -10ZM251 547Q250 548 258 558T281 585T317 620T365 659Q382 672 392 676T410 681Q419 681 425 676T432 661Q432 651 424 642T394 615Q367 595 342 582T297 560T265 548T251 547Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="687" d="M383 -10Q311 -10 252 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q460 657 511 634T591 581Q608 564 617 547T626 520Q626 511 622 505T612 495T602 490T596 490Q570 544 514 579T383 614Q323 614 274 593T190 532T135 440T115 323Q115 260 134 207T189 116T275 55T385 33Q430 33 466 44T529 75T575 120T603 172Q604 173 609 172T620 168T631 159T636 143Q636 132 626 110T592 66Q563 37 510 14T383 -10ZM267 719Q251 719 251 736Q251 747 264 765T302 812Q331 844 350 860T371 877Q373 877 392 861T438 814Q463 785 476 767T490 736Q490 719 475 719Q465 719 452 731T410 781L370 834L330 780Q305 746 291 733T267 719Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="542" d="M293 -10Q240 -10 196 9T120 61T70 139T52 238Q52 290 70 335T120 413T196 464T293 483Q353 483 393 465T457 422Q476 403 484 385T493 355Q493 347 489 342T479 334T468 331T462 331Q454 352 440 372T406 407T358 432T293 441Q251 441 216 426T155 383T115 318T100 238Q100 194 114 157T153 92T214 49T293 33Q330 33 358 42T406 66T440 101T462 142Q463 143 468 143T478 140T488 132T493 118Q493 107 485 89T456 50Q432 26 392 8T293 -10ZM193 545Q177 545 177 562Q177 573 190 591T228 638Q257 670 276 686T297 703Q299 703 318 687T364 640Q389 611 402 593T416 562Q416 545 401 545Q391 545 378 557T336 607L296 660L256 606Q231 572 217 559T193 545Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="687" d="M383 -10Q311 -10 252 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q460 657 511 634T591 581Q608 564 617 547T626 520Q626 511 622 505T612 495T602 490T596 490Q570 544 514 579T383 614Q323 614 274 593T190 532T135 440T115 323Q115 260 134 207T189 116T275 55T385 33Q430 33 466 44T529 75T575 120T603 172Q604 173 609 172T620 168T631 159T636 143Q636 132 626 110T592 66Q563 37 510 14T383 -10ZM377 720Q357 720 347 731T336 760Q336 777 347 788T378 799Q397 799 408 788T419 760Q419 743 408 732T377 720Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="542" d="M293 -10Q240 -10 196 9T120 61T70 139T52 238Q52 290 70 335T120 413T196 464T293 483Q353 483 393 465T457 422Q476 403 484 385T493 355Q493 347 489 342T479 334T468 331T462 331Q454 352 440 372T406 407T358 432T293 441Q251 441 216 426T155 383T115 318T100 238Q100 194 114 157T153 92T214 49T293 33Q330 33 358 42T406 66T440 101T462 142Q463 143 468 143T478 140T488 132T493 118Q493 107 485 89T456 50Q432 26 392 8T293 -10ZM300 546Q280 546 270 557T259 586Q259 603 270 614T301 625Q320 625 331 614T342 586Q342 569 331 558T300 546Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="687" d="M383 -10Q311 -10 252 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q460 657 511 634T591 581Q608 564 617 547T626 520Q626 511 622 505T612 495T602 490T596 490Q570 544 514 579T383 614Q323 614 274 593T190 532T135 440T115 323Q115 260 134 207T189 116T275 55T385 33Q430 33 466 44T529 75T575 120T603 172Q604 173 609 172T620 168T631 159T636 143Q636 132 626 110T592 66Q563 37 510 14T383 -10ZM383 703Q381 703 362 719T316 766Q291 795 278 813T264 844Q264 861 279 861Q289 861 302 849T344 799L384 746L424 800Q449 833 463 847T487 861Q503 861 503 844Q503 833 490 815T452 768Q424 736 405 720T383 703Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="542" d="M293 -10Q240 -10 196 9T120 61T70 139T52 238Q52 290 70 335T120 413T196 464T293 483Q353 483 393 465T457 422Q476 403 484 385T493 355Q493 347 489 342T479 334T468 331T462 331Q454 352 440 372T406 407T358 432T293 441Q251 441 216 426T155 383T115 318T100 238Q100 194 114 157T153 92T214 49T293 33Q330 33 358 42T406 66T440 101T462 142Q463 143 468 143T478 140T488 132T493 118Q493 107 485 89T456 50Q432 26 392 8T293 -10ZM298 529Q296 529 277 545T231 592Q206 621 193 639T179 670Q179 687 194 687Q204 687 217 675T259 625L299 572L339 626Q364 659 378 673T402 687Q418 687 418 670Q418 659 405 641T367 594Q339 562 320 546T298 529Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="692" d="M94 617Q94 630 102 638T123 647H276Q360 647 425 625T536 562T605 460T629 325Q629 249 605 189T534 87T422 23T271 0H94V617ZM274 44Q420 44 498 118T577 324Q577 389 557 440T498 528T403 583T276 603H144V44H274ZM303 703Q301 703 282 719T236 766Q211 795 198 813T184 844Q184 861 199 861Q209 861 222 849T264 799L304 746L344 800Q369 833 383 847T407 861Q423 861 423 844Q423 833 410 815T372 768Q344 736 325 720T303 703Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="751" d="M277 -10Q229 -10 188 8T117 58T69 135T52 237Q52 291 69 336T116 414T189 465T280 483Q314 483 343 475T396 452T437 420T464 381V673Q464 678 480 678Q493 678 502 669T512 631V0H464V96Q438 48 392 19T277 -10ZM282 32Q320 32 353 45T411 82T450 136T464 202V313Q442 371 397 406T283 441Q243 441 210 426T152 384T114 320T100 238Q100 193 113 155T151 90T208 48T282 32ZM620 470Q618 467 613 466T604 466T596 472T595 482L654 657Q659 671 664 677T683 684H687Q696 684 702 677T709 658V655Q709 649 707 641T697 619L620 470Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="741" d="M166 0Q153 0 145 8T136 30V302H60Q46 302 42 307T37 321V326Q37 335 41 340T60 346H136V617Q136 630 144 638T166 647H322Q405 647 471 625T583 561T653 460T678 325Q678 250 653 190T581 87T468 23T317 0H166ZM323 46Q469 46 548 119T627 324Q627 388 607 439T549 527T454 582T326 602H186V346H413Q436 346 436 326V321Q436 302 413 302H186V46H323Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="596" d="M277 -10Q229 -10 188 8T117 58T69 135T52 237Q52 291 69 336T116 414T189 465T280 483Q314 483 343 475T396 452T437 420T464 381V539H349Q320 539 320 560V563Q320 585 349 585H464V673Q464 678 480 678Q493 678 502 669T512 631V585H562Q591 585 591 563V560Q591 539 562 539H512V0H464V96Q438 48 392 19T277 -10ZM282 32Q320 32 353 45T411 82T450 136T464 202V313Q442 371 397 406T283 441Q243 441 210 426T152 384T114 320T100 238Q100 193 113 155T151 90T208 48T282 32Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM212 741Q183 741 183 762V766Q183 786 212 786H416Q445 786 445 766V762Q445 741 416 741H212Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM184 567Q155 567 155 588V592Q155 612 184 612H388Q417 612 417 592V588Q417 567 388 567H184Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM315 719Q283 719 258 730T216 759Q200 776 193 793T185 820Q185 831 191 839T214 847Q227 809 253 786T315 762Q351 762 377 785T416 847Q433 847 439 839T445 820Q445 810 438 793T414 759Q397 742 372 731T315 719Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM290 545Q258 545 233 556T191 585Q175 602 168 619T160 646Q160 657 166 665T189 673Q202 635 228 612T290 588Q326 588 352 611T391 673Q408 673 414 665T420 646Q420 636 413 619T389 585Q372 568 347 557T290 545Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM321 720Q301 720 291 731T280 760Q280 777 291 788T322 799Q341 799 352 788T363 760Q363 743 352 732T321 720Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM285 546Q265 546 255 557T244 586Q244 603 255 614T286 625Q305 625 316 614T327 586Q327 569 316 558T285 546Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H529Q483 -32 466 -58T449 -105Q449 -124 460 -136T489 -148Q516 -148 529 -129Q537 -129 545 -135T553 -154Q553 -170 534 -180T488 -190Q451 -190 428 -170T405 -114Q405 -85 423 -56T484 0H94V617Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q453 41 441 33T415 17Q367 -16 349 -42T331 -91Q331 -110 342 -122T371 -134Q398 -134 411 -115Q419 -115 427 -121T435 -140Q435 -156 416 -166T370 -176Q333 -176 310 -156T287 -100Q287 -77 299 -54T338 -7Q317 -10 294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="594" d="M94 617Q94 630 102 638T123 647H532V602H144V354H449V310H144V46H541V0H94V617ZM317 703Q315 703 296 719T250 766Q225 795 212 813T198 844Q198 861 213 861Q223 861 236 849T278 799L318 746L358 800Q383 833 397 847T421 861Q437 861 437 844Q437 833 424 815T386 768Q358 736 339 720T317 703Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="555" d="M294 -10Q240 -10 195 8T119 59T70 137T52 235Q52 288 69 333T118 412T193 464T288 483Q337 483 377 467T445 420T489 347T505 252V247Q505 236 500 233T484 230H99Q100 186 114 150T154 88T215 48T294 33Q331 33 358 41T406 62T440 93T463 128Q464 129 468 128T478 124T487 115T491 100Q491 92 484 79T461 49Q439 27 397 9T294 -10ZM288 441Q251 441 220 429T164 394T123 340T101 271H455Q453 309 441 340T407 393T355 428T288 441ZM283 529Q281 529 262 545T216 592Q191 621 178 639T164 670Q164 687 179 687Q189 687 202 675T244 625L284 572L324 626Q349 659 363 673T387 687Q403 687 403 670Q403 659 390 641T352 594Q324 562 305 546T283 529Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="724" d="M368 -10Q298 -10 242 15T146 84T85 189T64 322Q64 395 87 456T152 562T252 632T382 657Q456 657 507 634T586 583Q604 565 612 549T621 524Q621 513 617 507T607 498T597 495T591 495Q579 520 559 541T511 579T451 604T380 614Q321 614 273 593T189 532T135 440T115 323Q115 261 132 208T183 116T265 55T374 33Q422 33 462 47T533 88T582 151T605 229V273H381V318H612Q629 318 639 308T649 280V2Q649 1 645 -1T633 -3Q620 -3 613 6T605 46V126Q579 63 517 27T368 -10ZM266 719Q250 719 250 736Q250 747 263 765T301 812Q330 844 349 860T370 877Q372 877 391 861T437 814Q462 785 475 767T489 736Q489 719 474 719Q464 719 451 731T409 781L369 834L329 780Q304 746 290 733T266 719Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="596" d="M291 -197Q227 -197 183 -179T112 -134Q98 -120 91 -106T83 -82Q83 -73 87 -67T96 -57T105 -52T111 -52Q120 -72 135 -90T173 -123T224 -145T292 -154Q464 -154 464 26V107Q438 61 392 34T277 6Q227 6 186 23T115 71T69 146T52 244Q52 296 69 339T118 415T190 465T280 483Q350 483 397 454T464 384V473H512V26Q512 -86 454 -141T291 -197ZM282 47Q319 47 352 59T410 94T449 147T464 213V314Q442 372 398 406T283 441Q243 441 210 427T152 386T114 323T100 245Q100 200 113 164T150 101T207 61T282 47ZM185 545Q169 545 169 562Q169 573 182 591T220 638Q249 670 268 686T289 703Q291 703 310 687T356 640Q381 611 394 593T408 562Q408 545 393 545Q383 545 370 557T328 607L288 660L248 606Q223 572 209 559T185 545Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="724" d="M368 -10Q298 -10 242 15T146 84T85 189T64 322Q64 395 87 456T152 562T252 632T382 657Q456 657 507 634T586 583Q604 565 612 549T621 524Q621 513 617 507T607 498T597 495T591 495Q579 520 559 541T511 579T451 604T380 614Q321 614 273 593T189 532T135 440T115 323Q115 261 132 208T183 116T265 55T374 33Q422 33 462 47T533 88T582 151T605 229V273H381V318H612Q629 318 639 308T649 280V2Q649 1 645 -1T633 -3Q620 -3 613 6T605 46V126Q579 63 517 27T368 -10ZM374 719Q342 719 317 730T275 759Q259 776 252 793T244 820Q244 831 250 839T273 847Q286 809 312 786T374 762Q410 762 436 785T475 847Q492 847 498 839T504 820Q504 810 497 793T473 759Q456 742 431 731T374 719Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="596" d="M291 -197Q227 -197 183 -179T112 -134Q98 -120 91 -106T83 -82Q83 -73 87 -67T96 -57T105 -52T111 -52Q120 -72 135 -90T173 -123T224 -145T292 -154Q464 -154 464 26V107Q438 61 392 34T277 6Q227 6 186 23T115 71T69 146T52 244Q52 296 69 339T118 415T190 465T280 483Q350 483 397 454T464 384V473H512V26Q512 -86 454 -141T291 -197ZM282 47Q319 47 352 59T410 94T449 147T464 213V314Q442 372 398 406T283 441Q243 441 210 427T152 386T114 323T100 245Q100 200 113 164T150 101T207 61T282 47ZM288 545Q256 545 231 556T189 585Q173 602 166 619T158 646Q158 657 164 665T187 673Q200 635 226 612T288 588Q324 588 350 611T389 673Q406 673 412 665T418 646Q418 636 411 619T387 585Q370 568 345 557T288 545Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="724" d="M368 -10Q298 -10 242 15T146 84T85 189T64 322Q64 395 87 456T152 562T252 632T382 657Q456 657 507 634T586 583Q604 565 612 549T621 524Q621 513 617 507T607 498T597 495T591 495Q579 520 559 541T511 579T451 604T380 614Q321 614 273 593T189 532T135 440T115 323Q115 261 132 208T183 116T265 55T374 33Q422 33 462 47T533 88T582 151T605 229V273H381V318H612Q629 318 639 308T649 280V2Q649 1 645 -1T633 -3Q620 -3 613 6T605 46V126Q579 63 517 27T368 -10ZM376 720Q356 720 346 731T335 760Q335 777 346 788T377 799Q396 799 407 788T418 760Q418 743 407 732T376 720Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="596" d="M291 -197Q227 -197 183 -179T112 -134Q98 -120 91 -106T83 -82Q83 -73 87 -67T96 -57T105 -52T111 -52Q120 -72 135 -90T173 -123T224 -145T292 -154Q464 -154 464 26V107Q438 61 392 34T277 6Q227 6 186 23T115 71T69 146T52 244Q52 296 69 339T118 415T190 465T280 483Q350 483 397 454T464 384V473H512V26Q512 -86 454 -141T291 -197ZM282 47Q319 47 352 59T410 94T449 147T464 213V314Q442 372 398 406T283 441Q243 441 210 427T152 386T114 323T100 245Q100 200 113 164T150 101T207 61T282 47ZM291 546Q271 546 261 557T250 586Q250 603 261 614T292 625Q311 625 322 614T333 586Q333 569 322 558T291 546Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="724" d="M368 -10Q298 -10 242 15T146 84T85 189T64 322Q64 395 87 456T152 562T252 632T382 657Q456 657 507 634T586 583Q604 565 612 549T621 524Q621 513 617 507T607 498T597 495T591 495Q579 520 559 541T511 579T451 604T380 614Q321 614 273 593T189 532T135 440T115 323Q115 261 132 208T183 116T265 55T374 33Q422 33 462 47T533 88T582 151T605 229V273H381V318H612Q629 318 639 308T649 280V2Q649 1 645 -1T633 -3Q620 -3 613 6T605 46V126Q579 63 517 27T368 -10ZM319 -270Q317 -273 312 -274T303 -274T295 -269T294 -259L356 -94Q361 -80 366 -74T385 -67H389Q398 -67 404 -73T411 -93V-96Q411 -102 409 -110T399 -133L319 -270Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="596" d="M291 -197Q227 -197 183 -179T112 -134Q98 -120 91 -106T83 -82Q83 -73 87 -67T96 -57T105 -52T111 -52Q120 -72 135 -90T173 -123T224 -145T292 -154Q464 -154 464 26V107Q438 61 392 34T277 6Q227 6 186 23T115 71T69 146T52 244Q52 296 69 339T118 415T190 465T280 483Q350 483 397 454T464 384V473H512V26Q512 -86 454 -141T291 -197ZM282 47Q319 47 352 59T410 94T449 147T464 213V314Q442 372 398 406T283 441Q243 441 210 427T152 386T114 323T100 245Q100 200 113 164T150 101T207 61T282 47ZM322 735Q324 738 329 739T338 739T346 734T347 724L285 559Q280 545 275 539T256 532H252Q243 532 237 538T230 558V561Q230 567 232 575T242 598L322 735Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="702" d="M94 627Q94 650 115 650H122Q144 650 144 627V347H558V627Q558 650 579 650H586Q608 650 608 627V0H558V301H144V0H94V627ZM247 719Q231 719 231 736Q231 747 244 765T282 812Q311 844 330 860T351 877Q353 877 372 861T418 814Q443 785 456 767T470 736Q470 719 455 719Q445 719 432 731T390 781L350 834L310 780Q285 746 271 733T247 719Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="571" d="M84 673Q84 678 100 678Q113 678 122 669T132 631V380Q153 426 197 454T300 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 348 406 393T297 438Q262 438 232 426T179 393T145 340T132 272V0H84V673ZM1 719Q-15 719 -15 736Q-15 747 -2 765T36 812Q65 844 84 860T105 877Q107 877 126 861T172 814Q197 785 210 767T224 736Q224 719 209 719Q199 719 186 731T144 781L104 834L64 780Q39 746 25 733T1 719Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="702" d="M54 470Q25 470 25 490V494Q25 513 54 513H94V627Q94 650 115 650H122Q144 650 144 627V513H558V627Q558 650 579 650H586Q608 650 608 627V513H647Q676 513 676 494V490Q676 470 647 470H608V0H558V301H144V0H94V470H54ZM558 347V470H144V347H558Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="571" d="M34 539Q5 539 5 560V564Q5 585 34 585H84V673Q84 678 100 678Q113 678 122 669T132 631V585H247Q276 585 276 564V560Q276 539 247 539H132V380Q153 426 197 454T300 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 348 406 393T297 438Q262 438 232 426T179 393T145 340T132 272V0H84V539H34Z" />
+<glyph unicode="&#x128;" glyph-name="Itilde" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM190 669Q165 669 147 681T113 710Q98 725 88 732T67 739Q52 739 45 728T33 697Q30 686 26 680T12 674Q4 674 -6 681Q-3 726 15 753T68 780Q94 780 112 768T145 738Q160 723 170 716T192 709Q206 709 213 720T226 751Q229 762 233 768T247 774Q255 774 264 767Q262 722 244 696T190 669Z" />
+<glyph unicode="&#x129;" glyph-name="itilde" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM174 544Q149 544 131 556T97 585Q82 600 72 607T51 614Q36 614 29 603T17 572Q14 561 10 555T-4 549Q-12 549 -22 556Q-19 601 -1 628T52 655Q78 655 96 643T129 613Q144 598 154 591T176 584Q190 584 197 595T210 626Q213 637 217 643T231 649Q239 649 248 642Q246 597 228 571T174 544Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM27 741Q-2 741 -2 762V766Q-2 786 27 786H231Q260 786 260 766V762Q260 741 231 741H27Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM11 567Q-18 567 -18 588V592Q-18 612 11 612H215Q244 612 244 592V588Q244 567 215 567H11Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="257" d="M104 629Q104 650 125 650H131Q143 650 148 644T154 629V0H144Q96 -33 78 -59T60 -108Q60 -127 71 -139T100 -151Q127 -151 140 -132Q148 -132 156 -138T164 -157Q164 -173 145 -183T99 -193Q62 -193 39 -173T16 -117Q16 -87 36 -57T104 2V629Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="226" d="M127 0Q79 -33 61 -59T43 -108Q43 -127 54 -139T83 -151Q110 -151 123 -132Q131 -132 139 -138T147 -157Q147 -173 128 -183T82 -193Q45 -193 22 -173T-1 -117Q-1 -87 20 -56T89 4V453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H127ZM112 601Q80 601 80 634Q80 668 114 668Q147 668 147 634Q147 601 112 601Z" />
+<glyph unicode="&#x130;" glyph-name="Idotaccent" horiz-adv-x="257" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM130 719Q110 719 100 730T89 759Q89 776 100 787T131 798Q150 798 161 787T172 759Q172 742 161 731T130 719Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="226" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453Z" />
+<glyph unicode="&#x132;" glyph-name="IJ" horiz-adv-x="723" d="M104 627Q104 650 125 650H132Q154 650 154 627V0H104V627ZM457 -10Q418 -10 389 1T339 27T308 60T297 88Q297 95 301 101T310 112T320 117T326 118Q341 81 375 57T457 33Q527 33 558 73T590 203V647H640V203Q640 94 596 42T457 -10Z" />
+<glyph unicode="&#x133;" glyph-name="ij" horiz-adv-x="452" d="M89 453Q89 467 95 471T110 476H116Q125 476 131 472T137 453V0H89V453ZM112 601Q80 601 80 634Q80 668 114 668Q147 668 147 634Q147 601 112 601ZM241 -190Q215 -190 197 -184T168 -167Q158 -157 158 -147Q158 -136 164 -128T172 -120Q183 -131 200 -139T241 -148Q315 -148 315 -50V473H363V-53Q363 -119 332 -154T241 -190ZM338 601Q306 601 306 634Q306 668 340 668Q373 668 373 634Q373 601 338 601Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="466" d="M200 -10Q161 -10 132 1T82 27T51 60T40 88Q40 95 44 101T53 112T63 117T69 118Q84 81 118 57T200 33Q270 33 301 73T333 203V647H383V203Q383 94 339 42T200 -10ZM249 719Q233 719 233 736Q233 747 246 765T284 812Q313 844 332 860T353 877Q355 877 374 861T420 814Q445 785 458 767T472 736Q472 719 457 719Q447 719 434 731T392 781L352 834L312 780Q287 746 273 733T249 719Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="226" d="M15 -190Q-11 -190 -29 -184T-58 -167Q-68 -157 -68 -147Q-68 -136 -62 -128T-54 -120Q-43 -131 -26 -139T15 -148Q89 -148 89 -50V473H137V-53Q137 -119 106 -154T15 -190ZM9 545Q-7 545 -7 562Q-7 573 6 591T44 638Q73 670 92 686T113 703Q115 703 134 687T180 640Q205 611 218 593T232 562Q232 545 217 545Q207 545 194 557T152 607L112 660L72 606Q47 572 33 559T9 545Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="562" d="M94 627Q94 650 115 650H122Q144 650 144 627V337L451 635Q463 647 473 649T491 648T502 638T505 624L199 332L514 23Q515 15 510 8T497 -2T479 -3T459 9L144 325V0H94V627ZM214 -270Q212 -273 207 -274T198 -274T190 -269T189 -259L251 -94Q256 -80 261 -74T280 -67H284Q293 -67 299 -73T306 -93V-96Q306 -102 304 -110T294 -133L214 -270Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="459" d="M84 673Q84 678 100 678Q113 678 122 669T132 631V255L348 457Q363 472 374 474T392 474T402 464T405 453L185 252L433 22Q433 18 430 11T420 0T402 -2T376 16L132 244V0H84V673ZM173 -270Q171 -273 166 -274T157 -274T149 -269T148 -259L210 -94Q215 -80 220 -74T239 -67H243Q252 -67 258 -73T265 -93V-96Q265 -102 263 -110T253 -133L173 -270Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="530" d="M94 627Q94 650 115 650H122Q144 650 144 627V46H461Q484 46 484 25V21Q484 0 461 0H94V627ZM110 721Q109 722 117 732T140 759T176 794T224 833Q241 846 251 850T269 855Q278 855 284 850T291 835Q291 825 283 816T253 789Q226 769 201 756T156 734T124 722T110 721Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="226" d="M89 673Q89 678 105 678Q118 678 127 669T137 631V0H89V673ZM104 721Q103 722 111 732T134 759T170 794T218 833Q235 846 245 850T263 855Q272 855 278 850T285 835Q285 825 277 816T247 789Q220 769 195 756T150 734T118 722T104 721Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="530" d="M94 627Q94 650 115 650H122Q144 650 144 627V46H461Q484 46 484 25V21Q484 0 461 0H94V627ZM230 -270Q228 -273 223 -274T214 -274T206 -269T205 -259L267 -94Q272 -80 277 -74T296 -67H300Q309 -67 315 -73T322 -93V-96Q322 -102 320 -110T310 -133L230 -270Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="226" d="M89 673Q89 678 105 678Q118 678 127 669T137 631V0H89V673ZM49 -270Q47 -273 42 -274T33 -274T25 -269T24 -259L86 -94Q91 -80 96 -74T115 -67H119Q128 -67 134 -73T141 -93V-96Q141 -102 139 -110T129 -133L49 -270Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="580" d="M455 436Q453 433 448 432T439 432T431 438T430 448L489 623Q494 637 499 643T518 650H522Q531 650 537 643T544 624V621Q544 615 542 607T532 585L455 436ZM94 627Q94 650 115 650H122Q144 650 144 627V46H461Q484 46 484 25V21Q484 0 461 0H94V627Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="384" d="M253 464Q251 461 246 460T237 460T229 466T228 476L287 651Q292 665 297 671T316 678H320Q329 678 335 671T342 652V649Q342 643 340 635T330 613L253 464ZM89 673Q89 678 105 678Q118 678 127 669T137 631V0H89V673Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="585" d="M94 627Q94 650 115 650H122Q144 650 144 627V46H461Q484 46 484 25V21Q484 0 461 0H94V627ZM535 286Q501 286 501 320V328Q501 363 535 363H542Q577 363 577 328V320Q577 286 542 286H535Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="300" d="M89 673Q89 678 105 678Q118 678 127 669T137 631V0H89V673ZM239 266Q205 266 205 300V308Q205 343 239 343H246Q281 343 281 308V300Q281 266 246 266H239Z" />
+<glyph unicode="&#x141;" glyph-name="CR" horiz-adv-x="557" d="M165 0Q151 0 143 8T135 30V246L48 218Q46 217 42 224T37 240Q37 249 45 257T73 271L135 291V627Q135 650 156 650H163Q185 650 185 627V307L394 374Q396 375 400 368T404 352Q404 343 396 335T368 321L276 291Q259 285 243 280T213 270Q198 265 185 261V46H501Q524 46 524 24V21Q524 0 501 0H165Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="346" d="M168 -3Q146 -3 146 20V257L45 225Q43 225 39 233T35 248Q35 256 42 264T70 278L146 302V673Q146 675 151 676T163 678Q175 678 184 669T194 631V317L303 351Q305 351 309 343T313 328Q313 320 306 312T278 298Q254 291 236 285L194 271V20Q194 -3 173 -3H168Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="703" d="M94 616Q94 630 100 640T121 650H134Q148 650 156 643T173 623L563 77V647H610V30Q610 16 603 7T584 -3H580Q566 -3 560 2T546 18L141 585V0H94V616ZM319 721Q318 722 326 732T349 759T385 794T433 833Q450 846 460 850T478 855Q487 855 493 850T500 835Q500 825 492 816T462 789Q435 769 410 756T365 734T333 722T319 721Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="571" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V385Q155 428 198 455T301 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 347 406 392T299 438Q239 438 197 408T132 328V0H84V471ZM245 547Q244 548 252 558T275 585T311 620T359 659Q376 672 386 676T404 681Q413 681 419 676T426 661Q426 651 418 642T388 615Q361 595 336 582T291 560T259 548T245 547Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="703" d="M94 616Q94 630 100 640T121 650H134Q148 650 156 643T173 623L563 77V647H610V30Q610 16 603 7T584 -3H580Q566 -3 560 2T546 18L141 585V0H94V616ZM294 -270Q292 -273 287 -274T278 -274T270 -269T269 -259L331 -94Q336 -80 341 -74T360 -67H364Q373 -67 379 -73T386 -93V-96Q386 -102 384 -110T374 -133L294 -270Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="571" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V385Q155 428 198 455T301 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 347 406 392T299 438Q239 438 197 408T132 328V0H84V471ZM222 -270Q220 -273 215 -274T206 -274T198 -269T197 -259L259 -94Q264 -80 269 -74T288 -67H292Q301 -67 307 -73T314 -93V-96Q314 -102 312 -110T302 -133L222 -270Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="703" d="M94 616Q94 630 100 640T121 650H134Q148 650 156 643T173 623L563 77V647H610V30Q610 16 603 7T584 -3H580Q566 -3 560 2T546 18L141 585V0H94V616ZM356 703Q354 703 335 719T289 766Q264 795 251 813T237 844Q237 861 252 861Q262 861 275 849T317 799L357 746L397 800Q422 833 436 847T460 861Q476 861 476 844Q476 833 463 815T425 768Q397 736 378 720T356 703Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="571" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V385Q155 428 198 455T301 483Q348 483 383 468T443 425T479 359T491 272V0H443V269Q443 347 406 392T299 438Q239 438 197 408T132 328V0H84V471ZM288 529Q286 529 267 545T221 592Q196 621 183 639T169 670Q169 687 184 687Q194 687 207 675T249 625L289 572L329 626Q354 659 368 673T392 687Q408 687 408 670Q408 659 395 641T357 594Q329 562 310 546T288 529Z" />
+<glyph unicode="&#x149;" glyph-name="napostrophe" horiz-adv-x="793" d="M74 443Q72 440 67 439T58 439T50 445T49 455L108 630Q113 644 118 650T137 657H141Q150 657 156 650T163 631V628Q163 622 161 614T151 592L74 443ZM306 471Q306 472 310 474T322 476Q335 476 344 467T353 428V385Q377 428 420 455T523 483Q570 483 605 468T665 425T701 359T713 272V0H665V269Q665 347 628 392T521 438Q461 438 419 408T354 328V0H306V471Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM295 741Q266 741 266 762V766Q266 786 295 786H499Q528 786 528 766V762Q528 741 499 741H295Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM200 567Q171 567 171 588V592Q171 612 200 612H404Q433 612 433 592V588Q433 567 404 567H200Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM390 719Q358 719 333 730T291 759Q275 776 268 793T260 820Q260 831 266 839T289 847Q302 809 328 786T390 762Q426 762 452 785T491 847Q508 847 514 839T520 820Q520 810 513 793T489 759Q472 742 447 731T390 719Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM302 545Q270 545 245 556T203 585Q187 602 180 619T172 646Q172 657 178 665T201 673Q214 635 240 612T302 588Q338 588 364 611T403 673Q420 673 426 665T432 646Q432 636 425 619T401 585Q384 568 359 557T302 545Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="765" d="M382 -10Q311 -10 253 14T152 83T87 188T64 322Q64 395 87 456T153 562T254 632T384 657Q455 657 513 632T613 563T678 458T701 324Q701 251 678 190T613 85T512 15T382 -10ZM383 33Q443 33 492 55T576 115T630 207T650 323Q650 385 631 438T576 531T491 592T382 614Q322 614 273 592T189 532T135 440T115 323Q115 260 134 207T189 116T274 55T383 33ZM425 721Q424 722 431 732T454 759T490 794T538 833Q555 846 565 850T584 855Q592 855 598 850T605 835Q605 825 597 816T567 789Q540 769 515 756T471 734T439 722T425 721ZM262 721Q261 722 269 732T292 759T328 794T376 833Q393 846 403 850T421 855Q429 855 435 850T442 835Q442 825 435 816T404 789Q377 769 352 756T308 734T276 722T262 721Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="588" d="M294 -10Q241 -10 197 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T294 483Q346 483 390 464T467 412T517 334T535 235Q535 182 517 138T467 60T391 9T294 -10ZM294 32Q336 32 371 47T432 90T472 154T487 235Q487 278 473 316T433 381T372 425T294 441Q252 441 217 426T155 383T115 318T100 237Q100 193 114 156T155 91T216 48T294 32ZM323 547Q322 548 329 558T352 585T388 620T436 659Q453 672 463 676T482 681Q490 681 496 676T503 661Q503 651 495 642T465 615Q438 595 413 582T369 560T337 548T323 547ZM160 547Q159 548 167 558T190 585T226 620T274 659Q291 672 301 676T319 681Q327 681 333 676T340 661Q340 651 333 642T302 615Q275 595 250 582T206 560T174 548T160 547Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1047" d="M379 -9Q309 -9 251 15T152 84T87 188T64 322Q64 395 87 456T153 561T253 631T381 656Q423 656 461 647H984V602H563Q620 562 655 499T696 354H912V310H697Q694 223 659 156T563 46H993V0H459Q421 -9 379 -9ZM381 34Q439 34 488 55T572 115T626 207T646 323Q646 385 627 438T572 530T487 591T380 613Q321 613 273 592T189 531T135 439T115 323Q115 260 134 207T189 116T273 56T381 34Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="981" d="M292 -10Q239 -10 195 9T120 61T70 139T52 237Q52 290 70 335T120 413T196 464T292 483Q368 483 424 442T506 334Q529 402 585 442T716 483Q764 483 803 466T871 419T915 346T931 252V247Q931 236 926 233T910 230H529Q530 186 544 150T583 88T644 48T722 33Q758 33 785 41T832 62T866 93T889 128Q890 129 894 128T904 124T913 115T917 100Q917 92 910 79T887 49Q865 27 823 9T722 -10Q642 -10 585 29T504 136Q480 70 424 30T292 -10ZM292 32Q333 32 367 47T427 90T467 154T482 236Q482 279 468 316T428 381T368 425T292 441Q251 441 216 426T155 383T115 318T100 237Q100 193 114 156T154 91T215 48T292 32ZM716 441Q680 441 649 429T593 394T553 340T531 271H881Q879 309 867 340T833 393T782 428T716 441Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="590" d="M288 277L528 21Q530 18 522 8T498 -3Q482 -3 467 15L224 277H143V0H94V617Q94 630 102 638T123 647H298Q413 647 472 601T531 463Q531 373 473 325T310 277H288ZM303 320Q393 320 436 358T479 461Q479 530 435 566T300 602H143V320H303ZM240 721Q239 722 247 732T270 759T306 794T354 833Q371 846 381 850T399 855Q408 855 414 850T421 835Q421 825 413 816T383 789Q356 769 331 756T286 734T254 722T240 721Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="358" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V369Q146 429 180 456T262 483Q290 483 309 472T328 443Q328 429 322 423T314 418Q305 426 291 431T257 437Q193 437 163 379T132 225V0H84V471ZM150 547Q149 548 157 558T180 585T216 620T264 659Q281 672 291 676T309 681Q318 681 324 676T331 661Q331 651 323 642T293 615Q266 595 241 582T196 560T164 548T150 547Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="590" d="M288 277L528 21Q530 18 522 8T498 -3Q482 -3 467 15L224 277H143V0H94V617Q94 630 102 638T123 647H298Q413 647 472 601T531 463Q531 373 473 325T310 277H288ZM303 320Q393 320 436 358T479 461Q479 530 435 566T300 602H143V320H303ZM233 -270Q231 -273 226 -274T217 -274T209 -269T208 -259L270 -94Q275 -80 280 -74T299 -67H303Q312 -67 318 -73T325 -93V-96Q325 -102 323 -110T313 -133L233 -270Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="358" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V369Q146 429 180 456T262 483Q290 483 309 472T328 443Q328 429 322 423T314 418Q305 426 291 431T257 437Q193 437 163 379T132 225V0H84V471ZM40 -270Q38 -273 33 -274T24 -274T16 -269T15 -259L77 -94Q82 -80 87 -74T106 -67H110Q119 -67 125 -73T132 -93V-96Q132 -102 130 -110T120 -133L40 -270Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="590" d="M288 277L528 21Q530 18 522 8T498 -3Q482 -3 467 15L224 277H143V0H94V617Q94 630 102 638T123 647H298Q413 647 472 601T531 463Q531 373 473 325T310 277H288ZM303 320Q393 320 436 358T479 461Q479 530 435 566T300 602H143V320H303ZM282 703Q280 703 261 719T215 766Q190 795 177 813T163 844Q163 861 178 861Q188 861 201 849T243 799L283 746L323 800Q348 833 362 847T386 861Q402 861 402 844Q402 833 389 815T351 768Q323 736 304 720T282 703Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="358" d="M84 471Q84 472 88 474T100 476Q113 476 122 467T131 428V369Q146 429 180 456T262 483Q290 483 309 472T328 443Q328 429 322 423T314 418Q305 426 291 431T257 437Q193 437 163 379T132 225V0H84V471ZM201 529Q199 529 180 545T134 592Q109 621 96 639T82 670Q82 687 97 687Q107 687 120 675T162 625L202 572L242 626Q267 659 281 673T305 687Q321 687 321 670Q321 659 308 641T270 594Q242 562 223 546T201 529Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="565" d="M288 -10Q216 -10 166 11T87 60Q70 77 63 92T55 119Q55 127 59 133T69 143T79 148T85 149Q95 128 112 107T153 70T211 43T287 33Q367 33 412 68T458 166Q458 201 443 224T402 262T343 287T272 306Q234 315 199 326T135 357T89 407T72 483Q72 521 88 553T132 608T199 644T283 657Q349 657 394 638T464 595Q478 581 485 567T492 543Q492 535 488 529T478 519T468 513T462 512Q453 532 438 550T400 583T348 605T283 614Q248 614 218 605T167 579T133 538T121 484Q121 451 134 430T171 394T227 370T297 352Q336 343 373 331T441 298T489 246T507 168Q507 128 492 95T447 39T378 3T288 -10ZM226 721Q225 722 233 732T256 759T292 794T340 833Q357 846 367 850T385 855Q394 855 400 850T407 835Q407 825 399 816T369 789Q342 769 317 756T272 734T240 722T226 721Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="474" d="M237 -10Q174 -10 133 6T69 45Q56 58 51 67T46 84Q46 91 50 96T58 105T67 109T72 110Q94 76 135 53T239 30Q304 30 339 56T375 122Q375 146 365 161T335 187T288 204T228 218Q199 224 169 231T115 253T76 289T60 346Q60 376 72 401T108 445T164 473T237 483Q293 483 331 468T390 431Q408 413 408 396Q408 389 404 384T396 375T387 371T382 371Q364 402 326 422T237 443Q175 443 141 418T106 353Q106 330 116 316T144 291T189 274T248 260Q278 254 309 246T365 224T406 187T422 129Q422 67 373 29T237 -10ZM188 547Q187 548 195 558T218 585T254 620T302 659Q319 672 329 676T347 681Q356 681 362 676T369 661Q369 651 361 642T331 615Q304 595 279 582T234 560T202 548T188 547Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="565" d="M288 -10Q216 -10 166 11T87 60Q70 77 63 92T55 119Q55 127 59 133T69 143T79 148T85 149Q95 128 112 107T153 70T211 43T287 33Q367 33 412 68T458 166Q458 201 443 224T402 262T343 287T272 306Q234 315 199 326T135 357T89 407T72 483Q72 521 88 553T132 608T199 644T283 657Q349 657 394 638T464 595Q478 581 485 567T492 543Q492 535 488 529T478 519T468 513T462 512Q453 532 438 550T400 583T348 605T283 614Q248 614 218 605T167 579T133 538T121 484Q121 451 134 430T171 394T227 370T297 352Q336 343 373 331T441 298T489 246T507 168Q507 128 492 95T447 39T378 3T288 -10ZM187 719Q171 719 171 736Q171 747 184 765T222 812Q251 844 270 860T291 877Q293 877 312 861T358 814Q383 785 396 767T410 736Q410 719 395 719Q385 719 372 731T330 781L290 834L250 780Q225 746 211 733T187 719Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="474" d="M237 -10Q174 -10 133 6T69 45Q56 58 51 67T46 84Q46 91 50 96T58 105T67 109T72 110Q94 76 135 53T239 30Q304 30 339 56T375 122Q375 146 365 161T335 187T288 204T228 218Q199 224 169 231T115 253T76 289T60 346Q60 376 72 401T108 445T164 473T237 483Q293 483 331 468T390 431Q408 413 408 396Q408 389 404 384T396 375T387 371T382 371Q364 402 326 422T237 443Q175 443 141 418T106 353Q106 330 116 316T144 291T189 274T248 260Q278 254 309 246T365 224T406 187T422 129Q422 67 373 29T237 -10ZM137 545Q121 545 121 562Q121 573 134 591T172 638Q201 670 220 686T241 703Q243 703 262 687T308 640Q333 611 346 593T360 562Q360 545 345 545Q335 545 322 557T280 607L240 660L200 606Q175 572 161 559T137 545Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="565" d="M288 -10Q216 -10 166 11T87 60Q70 77 63 92T55 119Q55 127 59 133T69 143T79 148T85 149Q95 128 112 107T153 70T211 43T287 33Q367 33 412 68T458 166Q458 201 443 224T402 262T343 287T272 306Q234 315 199 326T135 357T89 407T72 483Q72 521 88 553T132 608T199 644T283 657Q349 657 394 638T464 595Q478 581 485 567T492 543Q492 535 488 529T478 519T468 513T462 512Q453 532 438 550T400 583T348 605T283 614Q248 614 218 605T167 579T133 538T121 484Q121 451 134 430T171 394T227 370T297 352Q336 343 373 331T441 298T489 246T507 168Q507 128 492 95T447 39T378 3T288 -10ZM214 -213Q212 -216 207 -216T197 -216T189 -211T189 -201L251 -74Q256 -62 261 -55T281 -48H285Q295 -48 302 -54T310 -70V-73Q310 -79 305 -86T291 -105L214 -213Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="474" d="M237 -10Q174 -10 133 6T69 45Q56 58 51 67T46 84Q46 91 50 96T58 105T67 109T72 110Q94 76 135 53T239 30Q304 30 339 56T375 122Q375 146 365 161T335 187T288 204T228 218Q199 224 169 231T115 253T76 289T60 346Q60 376 72 401T108 445T164 473T237 483Q293 483 331 468T390 431Q408 413 408 396Q408 389 404 384T396 375T387 371T382 371Q364 402 326 422T237 443Q175 443 141 418T106 353Q106 330 116 316T144 291T189 274T248 260Q278 254 309 246T365 224T406 187T422 129Q422 67 373 29T237 -10ZM164 -213Q162 -216 157 -216T147 -216T139 -211T139 -201L201 -74Q206 -62 211 -55T231 -48H235Q245 -48 252 -54T260 -70V-73Q260 -79 255 -86T241 -105L164 -213Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="565" d="M288 -10Q216 -10 166 11T87 60Q70 77 63 92T55 119Q55 127 59 133T69 143T79 148T85 149Q95 128 112 107T153 70T211 43T287 33Q367 33 412 68T458 166Q458 201 443 224T402 262T343 287T272 306Q234 315 199 326T135 357T89 407T72 483Q72 521 88 553T132 608T199 644T283 657Q349 657 394 638T464 595Q478 581 485 567T492 543Q492 535 488 529T478 519T468 513T462 512Q453 532 438 550T400 583T348 605T283 614Q248 614 218 605T167 579T133 538T121 484Q121 451 134 430T171 394T227 370T297 352Q336 343 373 331T441 298T489 246T507 168Q507 128 492 95T447 39T378 3T288 -10ZM284 703Q282 703 263 719T217 766Q192 795 179 813T165 844Q165 861 180 861Q190 861 203 849T245 799L285 746L325 800Q350 833 364 847T388 861Q404 861 404 844Q404 833 391 815T353 768Q325 736 306 720T284 703Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="474" d="M237 -10Q174 -10 133 6T69 45Q56 58 51 67T46 84Q46 91 50 96T58 105T67 109T72 110Q94 76 135 53T239 30Q304 30 339 56T375 122Q375 146 365 161T335 187T288 204T228 218Q199 224 169 231T115 253T76 289T60 346Q60 376 72 401T108 445T164 473T237 483Q293 483 331 468T390 431Q408 413 408 396Q408 389 404 384T396 375T387 371T382 371Q364 402 326 422T237 443Q175 443 141 418T106 353Q106 330 116 316T144 291T189 274T248 260Q278 254 309 246T365 224T406 187T422 129Q422 67 373 29T237 -10ZM232 529Q230 529 211 545T165 592Q140 621 127 639T113 670Q113 687 128 687Q138 687 151 675T193 625L233 572L273 626Q298 659 312 673T336 687Q352 687 352 670Q352 659 339 641T301 594Q273 562 254 546T232 529Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="599" d="M275 602H61Q38 602 38 622V626Q38 647 61 647H538Q562 647 562 626V622Q562 602 538 602H325V0H275V602ZM240 -270Q238 -273 233 -274T224 -274T216 -269T215 -259L277 -94Q282 -80 287 -74T306 -67H310Q319 -67 325 -73T332 -93V-96Q332 -102 330 -110T320 -133L240 -270Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="362" d="M237 -10Q174 -10 144 26T113 137V430H35V473H113V565Q113 579 119 584T134 589H140Q149 589 154 584T160 565V473H317V430H160V142Q160 83 178 57T239 31Q265 31 284 42T316 72Q318 74 325 68T332 48Q332 42 329 34T316 18Q305 6 286 -2T237 -10ZM149 -270Q147 -273 142 -274T133 -274T125 -269T124 -259L186 -94Q191 -80 196 -74T215 -67H219Q228 -67 234 -73T241 -93V-96Q241 -102 239 -110T229 -133L149 -270Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="599" d="M275 602H61Q38 602 38 622V626Q38 647 61 647H538Q562 647 562 626V622Q562 602 538 602H325V0H275V602ZM299 703Q297 703 278 719T232 766Q207 795 194 813T180 844Q180 861 195 861Q205 861 218 849T260 799L300 746L340 800Q365 833 379 847T403 861Q419 861 419 844Q419 833 406 815T368 768Q340 736 321 720T299 703Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="391" d="M237 -10Q174 -10 144 26T113 137V430H35V473H113V565Q113 579 119 584T134 589H140Q149 589 154 584T160 565V473H317V430H160V142Q160 83 178 57T239 31Q265 31 284 42T316 72Q318 74 325 68T332 48Q332 42 329 34T316 18Q305 6 286 -2T237 -10ZM236 522Q234 519 230 519T222 519T215 524T214 533L266 687Q271 699 275 704T292 710H295Q303 710 309 705T315 688V685Q315 680 314 672T304 653L236 522Z" />
+<glyph unicode="&#x166;" glyph-name="Tbar" horiz-adv-x="599" d="M275 602H61Q38 602 38 622V626Q38 647 61 647H538Q562 647 562 626V622Q562 602 538 602H325V0H275V602ZM197 314Q168 314 168 335V339Q168 359 197 359H401Q430 359 430 339V335Q430 314 401 314H197Z" />
+<glyph unicode="&#x167;" glyph-name="tbar" horiz-adv-x="362" d="M237 -10Q174 -10 144 26T113 137V430H35V473H113V565Q113 579 119 584T134 589H140Q149 589 154 584T160 565V473H317V430H160V142Q160 83 178 57T239 31Q265 31 284 42T316 72Q318 74 325 68T332 48Q332 42 329 34T316 18Q305 6 286 -2T237 -10ZM59 233Q30 233 30 254V258Q30 278 59 278H263Q292 278 292 258V254Q292 233 263 233H59Z" />
+<glyph unicode="&#x168;" glyph-name="Utilde" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM391 669Q366 669 348 681T314 710Q299 725 289 732T268 739Q253 739 246 728T234 697Q231 686 227 680T213 674Q205 674 195 681Q198 726 216 753T269 780Q295 780 313 768T346 738Q361 723 371 716T393 709Q407 709 414 720T427 751Q430 762 434 768T448 774Q456 774 465 767Q463 722 445 696T391 669Z" />
+<glyph unicode="&#x169;" glyph-name="utilde" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM346 544Q321 544 303 556T269 585Q254 600 244 607T223 614Q208 614 201 603T189 572Q186 561 182 555T168 549Q160 549 150 556Q153 601 171 628T224 655Q250 655 268 643T301 613Q316 598 326 591T348 584Q362 584 369 595T382 626Q385 637 389 643T403 649Q411 649 420 642Q418 597 400 571T346 544Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM238 741Q209 741 209 762V766Q209 786 238 786H442Q471 786 471 766V762Q471 741 442 741H238Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM188 567Q159 567 159 588V592Q159 612 188 612H392Q421 612 421 592V588Q421 567 392 567H188Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM334 719Q302 719 277 730T235 759Q219 776 212 793T204 820Q204 831 210 839T233 847Q246 809 272 786T334 762Q370 762 396 785T435 847Q452 847 458 839T464 820Q464 810 457 793T433 759Q416 742 391 731T334 719Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM276 545Q244 545 219 556T177 585Q161 602 154 619T146 646Q146 657 152 665T175 673Q188 635 214 612T276 588Q312 588 338 611T377 673Q394 673 400 665T406 646Q406 636 399 619T375 585Q358 568 333 557T276 545Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM337 718Q289 718 260 748T230 826Q230 873 260 904T338 935Q386 935 415 904T445 826Q445 780 415 749T337 718ZM337 751Q368 751 388 772T408 826Q408 858 388 880T337 902Q306 902 286 880T266 826Q266 794 286 773T337 751Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM287 535Q239 535 210 565T180 643Q180 690 210 721T288 752Q336 752 365 721T395 643Q395 597 365 566T287 535ZM287 568Q318 568 338 589T358 643Q358 675 338 697T287 719Q256 719 236 697T216 643Q216 611 236 590T287 568Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 124 514 57T329 -10ZM382 721Q381 722 388 732T411 759T447 794T495 833Q512 846 522 850T541 855Q549 855 555 850T562 835Q562 825 554 816T524 789Q497 769 472 756T428 734T396 722T382 721ZM219 721Q218 722 226 732T249 759T285 794T333 833Q350 846 360 850T378 855Q386 855 392 850T399 835Q399 825 392 816T361 789Q334 769 309 756T265 734T233 722T219 721Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10ZM320 547Q319 548 326 558T349 585T385 620T433 659Q450 672 460 676T479 681Q487 681 493 676T500 661Q500 651 492 642T462 615Q435 595 410 582T366 560T334 548T320 547ZM157 547Q156 548 164 558T187 585T223 620T271 659Q288 672 298 676T316 681Q324 681 330 676T337 661Q337 651 330 642T299 615Q272 595 247 582T203 560T171 548T157 547Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="661" d="M329 -10Q208 -10 146 56T84 252V627Q84 650 105 650H112Q134 650 134 627V256Q134 35 331 35Q528 35 528 256V627Q528 650 549 650H558Q577 650 577 627V254Q577 169 550 112T471 23Q426 -9 410 -34T393 -80Q393 -99 404 -111T433 -123Q460 -123 473 -104Q481 -104 489 -110T497 -129Q497 -145 478 -155T432 -165Q395 -165 372 -145T349 -89Q349 -68 358 -47T390 -5Q376 -7 361 -8T329 -10Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="571" d="M269 -10Q222 -10 187 5T128 48T92 114T80 201V473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1Q434 -34 417 -60T399 -108Q399 -127 410 -139T439 -151Q466 -151 479 -132Q487 -132 495 -138T503 -157Q503 -173 484 -183T438 -193Q401 -193 378 -173T355 -117Q355 -86 376 -55T449 6Q440 16 440 45V88Q416 46 373 18T269 -10Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="912" d="M66 639Q66 642 73 646T91 650T110 643T125 617L260 45L417 518Q422 532 430 539T458 547Q478 547 485 540T497 518L653 46L791 617Q795 636 804 643T823 650T839 646T846 639L687 0H620L458 497L290 0H225L66 639ZM355 705Q339 705 339 722Q339 733 352 751T390 798Q419 830 438 846T459 863Q461 863 480 847T526 800Q551 771 564 753T578 722Q578 705 563 705Q553 705 540 717T498 767L458 820L418 766Q393 732 379 719T355 705Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="694" d="M44 463Q44 465 51 470T67 476T86 470T101 441L198 44L312 379Q315 390 322 395T349 400Q367 400 374 395T386 379L499 44L595 441Q600 463 609 470T628 476T644 470T651 463L529 0H470L349 353L225 0H166L44 463ZM245 538Q229 538 229 555Q229 566 242 584T280 631Q309 663 328 679T349 696Q351 696 370 680T416 633Q441 604 454 586T468 555Q468 538 453 538Q443 538 430 550T388 600L348 653L308 599Q283 565 269 552T245 538Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="568" d="M259 277L24 647H79L284 322L489 632Q498 646 508 648T526 648T539 639T544 629L309 277V0H259V277ZM182 719Q166 719 166 736Q166 747 179 765T217 812Q246 844 265 860T286 877Q288 877 307 861T353 814Q378 785 391 767T405 736Q405 719 390 719Q380 719 367 731T325 781L285 834L245 780Q220 746 206 733T182 719Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="488" d="M182 -193Q172 -193 164 -188T157 -180L241 26Q228 26 222 30T211 45L25 473H75L254 56L413 473H464L216 -161Q209 -181 201 -187T182 -193ZM151 545Q135 545 135 562Q135 573 148 591T186 638Q215 670 234 686T255 703Q257 703 276 687T322 640Q347 611 360 593T374 562Q374 545 359 545Q349 545 336 557T294 607L254 660L214 606Q189 572 175 559T151 545Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="568" d="M259 277L24 647H79L284 322L489 632Q498 646 508 648T526 648T539 639T544 629L309 277V0H259V277ZM363 719Q325 719 325 757Q325 795 362 795Q379 795 389 785T400 757Q400 739 390 729T363 719ZM204 719Q187 719 177 729T166 757Q166 775 176 785T203 795Q241 795 241 757Q241 739 231 729T204 719Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="615" d="M61 53L488 602H89Q66 602 66 622V626Q66 647 89 647H543V597L115 46H533Q557 46 557 25V21Q557 0 533 0H61V53ZM264 721Q263 722 271 732T294 759T330 794T378 833Q395 846 405 850T423 855Q432 855 438 850T445 835Q445 825 437 816T407 789Q380 769 355 756T310 734T278 722T264 721Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="490" d="M51 37L370 432H78Q64 432 59 436T54 450V455Q54 464 59 468T78 473H434V440L112 41H416Q441 41 441 23V18Q441 0 416 0H51V37ZM209 547Q208 548 216 558T239 585T275 620T323 659Q340 672 350 676T368 681Q377 681 383 676T390 661Q390 651 382 642T352 615Q325 595 300 582T255 560T223 548T209 547Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="615" d="M61 53L488 602H89Q66 602 66 622V626Q66 647 89 647H543V597L115 46H533Q557 46 557 25V21Q557 0 533 0H61V53ZM303 720Q283 720 273 731T262 760Q262 777 273 788T304 799Q323 799 334 788T345 760Q345 743 334 732T303 720Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="490" d="M51 37L370 432H78Q64 432 59 436T54 450V455Q54 464 59 468T78 473H434V440L112 41H416Q441 41 441 23V18Q441 0 416 0H51V37ZM249 546Q229 546 219 557T208 586Q208 603 219 614T250 625Q269 625 280 614T291 586Q291 569 280 558T249 546Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="615" d="M61 53L488 602H89Q66 602 66 622V626Q66 647 89 647H543V597L115 46H533Q557 46 557 25V21Q557 0 533 0H61V53ZM301 703Q299 703 280 719T234 766Q209 795 196 813T182 844Q182 861 197 861Q207 861 220 849T262 799L302 746L342 800Q367 833 381 847T405 861Q421 861 421 844Q421 833 408 815T370 768Q342 736 323 720T301 703Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="490" d="M51 37L370 432H78Q64 432 59 436T54 450V455Q54 464 59 468T78 473H434V440L112 41H416Q441 41 441 23V18Q441 0 416 0H51V37ZM240 529Q238 529 219 545T173 592Q148 621 135 639T121 670Q121 687 136 687Q146 687 159 675T201 625L241 572L281 626Q306 659 320 673T344 687Q360 687 360 670Q360 659 347 641T309 594Q281 562 262 546T240 529Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="405" d="M90 -3Q79 -3 73 1T67 7L136 387H61Q47 387 43 392T38 406V411Q38 420 42 425T61 430H143L155 497Q169 577 207 617T304 657Q329 657 347 650T377 632Q390 619 390 607Q390 594 381 586T371 578Q361 593 342 603T300 613Q261 613 237 584T202 489L191 430H319Q333 430 338 425T343 411V406Q343 397 338 392T319 387H184L121 39Q115 -3 90 -3Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="565" d="M288 -10Q216 -10 166 11T87 60Q70 77 63 92T55 119Q55 127 59 133T69 143T79 148T85 149Q95 128 112 107T153 70T211 43T287 33Q367 33 412 68T458 166Q458 201 443 224T402 262T343 287T272 306Q234 315 199 326T135 357T89 407T72 483Q72 521 88 553T132 608T199 644T283 657Q349 657 394 638T464 595Q478 581 485 567T492 543Q492 535 488 529T478 519T468 513T462 512Q453 532 438 550T400 583T348 605T283 614Q248 614 218 605T167 579T133 538T121 484Q121 451 134 430T171 394T227 370T297 352Q336 343 373 331T441 298T489 246T507 168Q507 128 492 95T447 39T378 3T288 -10ZM222 -270Q220 -273 215 -274T206 -274T198 -269T197 -259L259 -94Q264 -80 269 -74T288 -67H292Q301 -67 307 -73T314 -93V-96Q314 -102 312 -110T302 -133L222 -270Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="474" d="M237 -10Q174 -10 133 6T69 45Q56 58 51 67T46 84Q46 91 50 96T58 105T67 109T72 110Q94 76 135 53T239 30Q304 30 339 56T375 122Q375 146 365 161T335 187T288 204T228 218Q199 224 169 231T115 253T76 289T60 346Q60 376 72 401T108 445T164 473T237 483Q293 483 331 468T390 431Q408 413 408 396Q408 389 404 384T396 375T387 371T382 371Q364 402 326 422T237 443Q175 443 141 418T106 353Q106 330 116 316T144 291T189 274T248 260Q278 254 309 246T365 224T406 187T422 129Q422 67 373 29T237 -10ZM159 -270Q157 -273 152 -274T143 -274T135 -269T134 -259L196 -94Q201 -80 206 -74T225 -67H229Q238 -67 244 -73T251 -93V-96Q251 -102 249 -110T239 -133L159 -270Z" />
+<glyph unicode="&#x21A;" glyph-name="uni021A" horiz-adv-x="599" d="M275 602H61Q38 602 38 622V626Q38 647 61 647H538Q562 647 562 626V622Q562 602 538 602H325V0H275V602ZM240 -270Q238 -273 233 -274T224 -274T216 -269T215 -259L277 -94Q282 -80 287 -74T306 -67H310Q319 -67 325 -73T332 -93V-96Q332 -102 330 -110T320 -133L240 -270Z" />
+<glyph unicode="&#x21B;" glyph-name="uni021B" horiz-adv-x="362" d="M237 -10Q174 -10 144 26T113 137V430H35V473H113V565Q113 579 119 584T134 589H140Q149 589 154 584T160 565V473H317V430H160V142Q160 83 178 57T239 31Q265 31 284 42T316 72Q318 74 325 68T332 48Q332 42 329 34T316 18Q305 6 286 -2T237 -10ZM149 -270Q147 -273 142 -274T133 -274T125 -269T124 -259L186 -94Q191 -80 196 -74T215 -67H219Q228 -67 234 -73T241 -93V-96Q241 -102 239 -110T229 -133L149 -270Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="530" d="M161 545Q145 545 145 562Q145 573 158 591T196 638Q225 670 244 686T265 703Q267 703 286 687T332 640Q357 611 370 593T384 562Q384 545 369 545Q359 545 346 557T304 607L264 660L224 606Q199 572 185 559T161 545Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="530" d="M265 529Q263 529 244 545T198 592Q173 621 160 639T146 670Q146 687 161 687Q171 687 184 675T226 625L266 572L306 626Q331 659 345 673T369 687Q385 687 385 670Q385 659 372 641T334 594Q306 562 287 546T265 529Z" />
+<glyph unicode="&#x2C9;" glyph-name="overscore" horiz-adv-x="530" d="M163 567Q134 567 134 588V592Q134 612 163 612H367Q396 612 396 592V588Q396 567 367 567H163Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="530" d="M265 545Q233 545 208 556T166 585Q150 602 143 619T135 646Q135 657 141 665T164 673Q177 635 203 612T265 588Q301 588 327 611T366 673Q383 673 389 665T395 646Q395 636 388 619T364 585Q347 568 322 557T265 545Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="530" d="M265 546Q245 546 235 557T224 586Q224 603 235 614T266 625Q285 625 296 614T307 586Q307 569 296 558T265 546Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="530" d="M264 544Q216 544 187 574T157 652Q157 699 187 730T265 761Q313 761 342 730T372 652Q372 606 342 575T264 544ZM264 577Q295 577 315 598T335 652Q335 684 315 706T264 728Q233 728 213 706T193 652Q193 620 213 599T264 577Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="530" d="M326 1Q277 -33 259 -59T240 -108Q240 -127 251 -139T280 -151Q307 -151 320 -132Q328 -132 336 -138T344 -157Q344 -173 325 -183T279 -193Q242 -193 219 -173T196 -117Q196 -82 224 -47T318 21L326 1Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="530" d="M325 544Q300 544 282 556T248 585Q233 600 223 607T202 614Q187 614 180 603T168 572Q165 561 161 555T147 549Q139 549 129 556Q132 601 150 628T203 655Q229 655 247 643T280 613Q295 598 305 591T327 584Q341 584 348 595T361 626Q364 637 368 643T382 649Q390 649 399 642Q397 597 379 571T325 544Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="530" d="M287 547Q286 548 293 558T316 585T352 620T400 659Q417 672 427 676T446 681Q454 681 460 676T467 661Q467 651 459 642T429 615Q402 595 377 582T333 560T301 548T287 547ZM124 547Q123 548 131 558T154 585T190 620T238 659Q255 672 265 676T283 681Q291 681 297 676T304 661Q304 651 297 642T266 615Q239 595 214 582T170 560T138 548T124 547Z" />
+<glyph unicode="&#x394;" glyph-name="increment" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x3A9;" glyph-name="Omega" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x3BC;" glyph-name="mu" horiz-adv-x="571" d="M80 473H128V204Q128 126 165 81T272 35Q332 35 374 65T439 146V473H487V2Q487 1 482 -1T470 -3Q458 -3 449 6T440 45V88Q416 46 373 18T269 -10Q222 -10 187 5T128 47V-260H80V473Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="779" d="M389 -10Q317 -10 258 14T155 83T88 188T64 322Q64 394 88 455T156 561T260 631T390 657Q462 657 522 633T625 564T692 459T716 325Q716 253 692 192T623 86T519 16T389 -10ZM391 26Q454 26 506 48T596 109T655 203T676 324Q676 388 655 443T595 537T504 599T389 622Q326 622 274 600T184 539T125 444T104 323Q104 258 125 204T185 110T275 48T391 26ZM304 139Q286 139 286 160V459Q286 470 292 477T310 485H404Q468 485 502 457T537 375Q537 321 504 293T406 264H330V160Q330 139 311 139H304ZM407 300Q450 300 472 319T494 374Q494 409 472 428T404 448H330V300H407Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="531" d="M80 227Q57 227 57 246V250Q57 270 80 270H451Q465 270 469 265T474 250V246Q474 236 470 232T451 227H80Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="695" d="M80 227Q57 227 57 246V250Q57 270 80 270H614Q628 270 633 265T638 250V246Q638 236 633 232T614 227H80Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="222" d="M82 439Q72 439 66 446T59 465V468Q59 474 61 482T71 504L148 653Q149 656 154 657T164 657T172 652T173 642L115 466Q110 453 105 446T85 439H82Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="222" d="M74 443Q72 440 67 439T58 439T50 445T49 455L108 630Q113 644 118 650T137 657H141Q150 657 156 650T163 631V628Q163 622 161 614T151 592L74 443Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="216" d="M57 -134Q55 -137 51 -138T41 -138T33 -133T32 -123L91 52Q96 66 101 72T120 79H124Q133 79 139 73T146 53V51Q146 44 144 36T134 14L57 -134Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="360" d="M220 439Q211 439 204 446T197 465V468Q197 474 199 482T210 504L286 653Q288 656 293 657T302 657T310 652T311 642L253 466Q248 453 243 446T224 439H220ZM82 439Q73 439 66 446T59 465V468Q59 474 61 482T71 504L148 653Q149 656 154 657T164 657T172 652T173 642L115 466Q110 453 105 446T86 439H82Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="360" d="M212 443Q210 440 206 439T196 439T188 445T187 455L246 630Q251 644 256 650T275 657H279Q288 657 294 650T301 631V628Q301 622 300 614T290 592L212 443ZM73 443Q71 440 67 439T57 439T49 445T48 455L107 630Q112 644 117 650T136 657H140Q149 657 155 650T162 631V628Q162 622 160 614T150 592L73 443Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="355" d="M196 -134Q194 -137 189 -138T180 -138T172 -133T171 -123L230 52Q235 66 240 72T259 79H263Q272 79 278 73T285 53V51Q285 44 283 36T273 14L196 -134ZM56 -134Q54 -137 50 -138T41 -138T33 -133T32 -123L90 52Q95 66 100 72T119 79H123Q132 79 138 73T145 53V51Q145 44 144 36T134 14L56 -134Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="527" d="M261 -156Q249 -156 246 -149T242 -131L238 316Q238 331 241 347T249 380L75 372Q55 370 48 375T41 396Q41 413 48 418T75 421L248 412L238 617Q236 637 241 644T264 652Q281 652 286 645T290 617L280 412L452 421Q472 423 479 418T487 396Q487 380 480 375T452 372L278 380Q283 364 286 348T290 316L286 -131Q286 -142 282 -149T266 -156H261Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="527" d="M261 -156Q249 -156 246 -149T242 -131L239 106L75 98Q55 96 48 101T41 122Q41 138 48 143T75 146L239 138L238 316Q238 331 241 347T249 380L75 372Q55 370 48 375T41 396Q41 413 48 418T75 421L248 412L238 617Q236 637 241 644T264 652Q281 652 286 645T290 617L280 412L452 421Q472 423 479 418T487 396Q487 380 480 375T452 372L278 380Q283 364 286 348T290 316L288 138L452 146Q472 148 479 143T487 122Q487 106 480 101T452 98L288 106L286 -131Q286 -142 282 -149T266 -156H261Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="330" d="M165 160Q128 160 103 185T78 248Q78 285 103 310T165 336Q202 336 227 311T252 248Q252 211 227 186T165 160Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="604" d="M489 -5Q455 -5 455 30V37Q455 72 489 72H497Q532 72 532 37V30Q532 -5 497 -5H489ZM298 -5Q264 -5 264 30V37Q264 72 298 72H305Q340 72 340 37V30Q340 -5 305 -5H298ZM107 -5Q72 -5 72 30V37Q72 72 107 72H114Q149 72 149 37V30Q149 -5 114 -5H107Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1071" d="M200 358Q169 358 143 369T98 400T67 446T56 503V510Q56 540 67 566T97 613T143 644T200 656Q231 656 257 645T302 614T332 568T343 512V504Q343 474 332 448T302 401T256 370T200 358ZM118 -6Q117 -7 113 -6T105 -3T98 4T94 15Q94 26 98 34T116 56L617 657Q618 658 622 657T630 654T637 648T641 636Q641 621 618 595L118 -6ZM874 -5Q844 -5 818 6T772 37T742 83T731 140V147Q731 177 742 203T772 250T818 281T875 293Q905 293 931 282T977 251T1007 205T1018 149V141Q1018 111 1007 85T977 38T931 7T874 -5ZM538 -5Q508 -5 482 6T436 37T406 83T395 140V147Q395 177 406 203T436 250T482 281T539 293Q569 293 595 282T641 251T671 205T682 149V141Q682 111 671 85T641 38T595 7T538 -5ZM200 397Q244 397 272 427T300 502V512Q300 534 293 553T272 587T240 610T199 618Q156 618 128 588T100 512V502Q100 457 128 427T200 397ZM875 34Q919 34 946 64T974 139V149Q974 194 946 224T874 255Q830 255 803 225T775 149V139Q775 94 803 64T875 34ZM539 34Q583 34 610 64T638 139V149Q638 194 610 224T538 255Q494 255 467 225T439 149V139Q439 94 467 64T539 34Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="332" d="M248 83Q234 83 211 99T148 149Q106 185 83 211T59 242Q59 246 82 271T147 333Q186 368 210 384T248 401Q256 401 261 396T267 383Q267 372 247 353T180 298L104 241Q124 227 143 214T182 183Q227 148 247 130T267 100Q267 93 262 88T248 83Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="332" d="M84 83Q76 83 71 88T66 101Q66 112 86 131T152 186L228 243Q208 257 189 270T150 300Q105 335 86 354T66 384Q66 391 71 396T84 401Q98 401 121 385T184 335Q226 298 249 273T273 242Q273 237 250 212T185 150Q146 116 122 100T84 83Z" />
+<glyph unicode="&#x2044;" glyph-name="uni2044" horiz-adv-x="649" d="M60 -7Q58 -9 48 -4T37 13Q37 24 41 32T59 55L589 659Q590 660 594 659T602 656T609 650T613 638Q613 621 590 597L60 -7Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="655" d="M402 -10Q299 -10 235 52T153 226H75Q61 226 56 230T51 245V249Q51 259 56 263T75 268H148Q147 276 147 284T147 302V347Q147 356 147 365T148 384H75Q61 384 56 388T51 403V407Q51 417 56 422T75 427H154Q163 480 184 522T236 595T309 641T398 657Q457 657 497 639T561 596Q578 578 586 561T595 530Q595 522 591 517T581 508T571 503T565 503Q556 525 542 545T507 581T459 606T395 615Q320 615 270 565T202 427H433Q456 427 456 407V403Q456 384 433 384H196Q195 375 195 366T195 347V300Q195 292 195 284T196 268H433Q456 268 456 249V245Q456 226 433 226H202Q219 138 270 86T400 33Q436 33 464 42T512 68T547 105T570 151Q571 152 575 151T585 147T595 138T600 123Q600 109 591 90T563 53Q537 27 497 9T402 -10Z" />
+<glyph unicode="&#x2113;" glyph-name="afii61289" horiz-adv-x="434" d="M199 341Q228 365 251 395T291 456T317 513T326 558Q326 584 315 584Q308 584 300 573Q291 561 278 539T250 486T222 419T199 341ZM44 138Q30 151 19 172T7 228Q25 237 33 240T49 246L59 251Q63 321 81 383T126 497T183 588T245 652Q285 683 327 683Q367 683 395 652T423 558Q423 509 402 463T349 375T276 294T196 223Q196 218 196 214T195 204Q195 158 207 133T237 107Q254 107 273 133T310 217Q322 220 336 220Q355 220 373 213T404 186Q397 149 382 114T345 52T293 9T228 -8Q206 -8 182 1T135 30T94 78T66 146L44 138Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="683" d="M355 342Q339 342 339 361V628Q339 650 359 650H377Q387 650 391 646T400 633L480 465L559 633Q564 643 568 646T582 650H599Q619 650 619 628V361Q619 342 602 342H597Q581 342 581 361V597L501 425Q499 420 496 417T479 414Q466 414 463 417T458 425L377 596V361Q377 342 362 342H355ZM157 342Q140 342 140 361V612H50Q32 612 32 628V631Q32 648 50 648H270Q288 648 288 631V628Q288 612 270 612H180V361Q180 342 162 342H157Z" />
+<glyph unicode="&#x2126;" glyph-name="uni2126" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x212E;" glyph-name="estimated" horiz-adv-x="830" d="M415 671Q492 671 560 645T681 573T763 463T793 324H185Q175 324 175 313V116Q223 65 286 38T415 10Q490 10 555 43T667 131H724Q667 62 585 26T415 -11Q340 -11 272 14T151 84T67 191T36 330Q36 403 66 465T147 574T268 645T415 671ZM415 650Q345 650 282 622T175 543V358Q175 347 185 347H646Q655 347 655 358V542Q610 594 546 622T415 650Z" />
+<glyph unicode="&#x2202;" glyph-name="partialdiff" horiz-adv-x="538" d="M303 481Q335 481 361 471T404 445V464Q404 499 399 528T381 578T343 610T282 622Q247 622 208 612L189 679Q271 710 332 710Q421 710 464 649T508 475Q508 370 481 283T409 133T311 35T201 0Q165 0 134 10T80 44T43 102T30 187Q30 256 55 310T120 403T208 461T303 481ZM205 105Q229 105 256 121T310 167T359 243T393 349Q382 361 360 373T309 386Q275 386 244 371T188 331T149 270T134 194Q134 157 150 131T205 105Z" />
+<glyph unicode="&#x2206;" glyph-name="uni2206" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x220F;" glyph-name="product" horiz-adv-x="634" d="M484 0V607H150V0H50V700H584V0H484Z" />
+<glyph unicode="&#x2211;" glyph-name="summation" horiz-adv-x="475" d="M110 80H437V0H25V96L209 325L25 554V650H437V570H115L288 353V300L110 80Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="630" d="M128 244Q105 244 105 263V269Q105 288 128 288H501Q515 288 519 283T524 269V263Q524 254 520 249T501 244H128Z" />
+<glyph unicode="&#x2215;" glyph-name="fraction" horiz-adv-x="649" d="M60 -7Q58 -9 48 -4T37 13Q37 24 41 32T59 55L589 659Q590 660 594 659T602 656T609 650T613 638Q613 621 590 597L60 -7Z" />
+<glyph unicode="&#x2219;" glyph-name="middot" horiz-adv-x="236" d="M114 210Q80 210 80 244V252Q80 287 114 287H121Q156 287 156 252V244Q156 210 121 210H114Z" />
+<glyph unicode="&#x221A;" glyph-name="radical" horiz-adv-x="649" d="M256 501L369 171L566 700H649L386 0H283L136 433H30V501H256Z" />
+<glyph unicode="&#x221E;" glyph-name="infinity" horiz-adv-x="733" d="M703 271Q703 231 690 203T654 156T604 129T547 120Q518 120 493 128T447 150T405 181T366 217Q347 199 328 182T287 151T240 129T186 120Q157 120 129 129T79 156T44 203T30 271Q30 310 43 338T79 385T129 413T186 422Q215 422 240 414T286 392T327 361T366 325Q385 343 405 360T446 391T493 413T547 422Q576 422 604 414T654 387T689 340T703 271ZM187 222Q223 222 252 235T308 271Q282 292 253 306T187 320Q167 320 149 309T130 271Q130 244 148 233T187 222ZM546 320Q510 320 481 306T425 271Q451 249 480 236T546 222Q566 222 584 233T603 271Q603 298 585 309T546 320Z" />
+<glyph unicode="&#x222B;" glyph-name="integral'" horiz-adv-x="396" d="M164 596Q172 654 201 685Q228 714 271 723T375 724V648Q314 653 288 640T255 584L168 -32Q159 -95 128 -126Q105 -149 70 -155T-15 -155V-81Q22 -84 45 -75T74 -25L164 596Z" />
+<glyph unicode="&#x2248;" glyph-name="approxequal" horiz-adv-x="529" d="M325 121Q300 121 282 133T248 162Q233 177 223 184T202 191Q187 191 180 180T168 149Q165 138 161 133T147 127Q138 127 129 133Q132 178 150 205T203 232Q229 232 247 220T280 191Q295 175 305 168T327 161Q341 161 348 172T361 203Q364 214 368 220T382 226Q392 226 399 219Q397 174 379 148T325 121ZM325 267Q300 267 282 279T248 308Q233 323 223 330T202 337Q187 337 180 326T168 295Q165 284 161 279T147 273Q138 273 129 279Q132 324 150 351T203 378Q229 378 247 366T280 337Q295 321 305 314T327 307Q341 307 348 318T361 349Q364 360 368 366T382 372Q392 372 399 365Q397 320 379 294T325 267Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="630" d="M132 349Q118 349 114 354T109 369V374Q109 384 113 389T132 394H355L419 524Q430 544 447 536L452 534Q460 529 463 523T460 504L405 394H498Q521 394 521 374V369Q521 349 498 349H380L293 173H498Q521 173 521 153V148Q521 128 498 128H274L207 -11Q200 -24 194 -26T179 -24L174 -21Q155 -13 166 9L224 128H132Q118 128 114 133T109 148V153Q109 163 113 168T132 173H250L336 349H132Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="630" d="M484 286Q499 279 507 269T514 249Q513 238 507 232T498 226L132 384Q124 389 121 394T117 417Q117 433 120 438T132 449L498 607Q500 608 506 602T514 585Q517 577 508 566T484 548L163 417L484 286ZM132 127Q118 127 114 132T109 146V152Q109 161 113 166T132 171H498Q521 171 521 152V146Q521 127 498 127H132Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="630" d="M115 251Q114 261 123 272T145 288L466 419L145 551Q130 558 121 568T115 587Q116 598 123 604T132 610L497 451Q505 446 509 441T513 419Q513 402 509 397T497 386L132 228Q129 227 123 233T115 251ZM132 129Q118 129 114 134T109 149V154Q109 163 113 168T132 174H498Q521 174 521 154V149Q521 129 498 129H132Z" />
+<glyph unicode="&#x25CA;" glyph-name="lozenge" horiz-adv-x="520" d="M30 388L226 776H294L490 388L294 0H226L30 388ZM370 388L260 616L150 388L260 159L370 388Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="537" d="M127 430H26V473H127V524Q127 605 161 646T259 688Q288 688 307 681T338 662Q349 651 349 637Q349 625 343 619T335 614Q324 628 303 637T259 646Q216 646 195 617T173 521V473H301V430H174V0H127V430ZM400 453Q400 467 406 471T421 476H427Q436 476 442 472T448 453V0H400V453ZM424 601Q391 601 391 634Q391 668 426 668Q458 668 458 634Q458 601 424 601Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="537" d="M127 430H26V473H127V524Q127 605 161 646T259 688Q288 688 307 681T338 662Q349 651 349 637Q349 625 343 619T335 614Q324 628 303 637T259 646Q216 646 195 617T173 521V473H301V430H174V0H127V430ZM400 673Q400 675 405 676T417 678Q429 678 438 669T448 631V0H400V673Z" />
+
+<hkern u1="&#x141;" u2="&#xdd;" k="80" />
+<hkern u1="&#x141;" u2="@" k="50" />
+<hkern u1="&#x141;" u2="A" k="-20" />
+<hkern u1="&#x141;" u2="C" k="50" />
+<hkern u1="&#x141;" u2="G" k="50" />
+<hkern u1="&#x141;" u2="O" k="50" />
+<hkern u1="&#x141;" u2="Q" k="50" />
+<hkern u1="&#x141;" u2="T" k="100" />
+<hkern u1="&#x141;" u2="U" k="15" />
+<hkern u1="&#x141;" u2="V" k="90" />
+<hkern u1="&#x141;" u2="W" k="70" />
+<hkern u1="&#x141;" u2="Y" k="80" />
+<hkern u1="&#x141;" u2="\" k="50" />
+<hkern u1="&#x141;" u2="a" k="20" />
+<hkern u1="&#x141;" u2="c" k="20" />
+<hkern u1="&#x141;" u2="d" k="20" />
+<hkern u1="&#x141;" u2="e" k="20" />
+<hkern u1="&#x141;" u2="o" k="20" />
+<hkern u1="&#x141;" u2="q" k="20" />
+<hkern u1="&#x141;" u2="v" k="40" />
+<hkern u1="&#x141;" u2="w" k="10" />
+<hkern u1="&#x141;" u2="y" k="40" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd6;" k="50" />
+<hkern u1="&#x141;" u2="&#xdc;" k="15" />
+<hkern u1="&#x141;" u2="&#xe7;" k="20" />
+<hkern u1="&#x141;" u2="&#xae;" k="50" />
+<hkern u1="&#x141;" u2="&#xa9;" k="50" />
+<hkern u1="&#x141;" u2="&#x2122;" k="70" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd8;" k="50" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="50" />
+<hkern u1="&#x141;" u2="&#xe6;" k="20" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd5;" k="50" />
+<hkern u1="&#x141;" u2="&#x152;" k="50" />
+<hkern u1="&#x141;" u2="&#x153;" k="20" />
+<hkern u1="&#x141;" u2="&#x201c;" k="100" />
+<hkern u1="&#x141;" u2="&#x201d;" k="60" />
+<hkern u1="&#x141;" u2="&#x2018;" k="100" />
+<hkern u1="&#x141;" u2="&#x2019;" k="60" />
+<hkern u1="&#x141;" u2="&#x178;" k="80" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd3;" k="50" />
+<hkern u1="&#x141;" u2="&#xd4;" k="50" />
+<hkern u1="&#x141;" u2="&#xd2;" k="50" />
+<hkern u1="&#x141;" u2="&#xda;" k="15" />
+<hkern u1="&#x141;" u2="&#xdb;" k="15" />
+<hkern u1="&#x141;" u2="&#xd9;" k="15" />
+<hkern u1="&#x160;" u2="&#xdd;" k="-10" />
+<hkern u1="&#x160;" u2="A" k="-5" />
+<hkern u1="&#x160;" u2="V" k="-10" />
+<hkern u1="&#x160;" u2="X" k="-5" />
+<hkern u1="&#x160;" u2="Y" k="-10" />
+<hkern u1="&#x160;" u2="v" k="3" />
+<hkern u1="&#x160;" u2="y" k="3" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-5" />
+<hkern u1="&#x160;" u2="&#x178;" k="-10" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xdd;" u2="&#x160;" k="-10" />
+<hkern u1="&#xdd;" u2="&#x161;" k="50" />
+<hkern u1="&#xdd;" u2="&amp;" k="30" />
+<hkern u1="&#xdd;" u2=")" k="-50" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="50" />
+<hkern u1="&#xdd;" u2="-" k="50" />
+<hkern u1="&#xdd;" u2="." k="50" />
+<hkern u1="&#xdd;" u2=":" k="30" />
+<hkern u1="&#xdd;" u2=";" k="30" />
+<hkern u1="&#xdd;" u2="@" k="30" />
+<hkern u1="&#xdd;" u2="A" k="38" />
+<hkern u1="&#xdd;" u2="C" k="25" />
+<hkern u1="&#xdd;" u2="G" k="25" />
+<hkern u1="&#xdd;" u2="J" k="73" />
+<hkern u1="&#xdd;" u2="O" k="25" />
+<hkern u1="&#xdd;" u2="Q" k="25" />
+<hkern u1="&#xdd;" u2="S" k="-10" />
+<hkern u1="&#xdd;" u2="T" k="-20" />
+<hkern u1="&#xdd;" u2="V" k="-30" />
+<hkern u1="&#xdd;" u2="X" k="-20" />
+<hkern u1="&#xdd;" u2="]" k="-50" />
+<hkern u1="&#xdd;" u2="a" k="55" />
+<hkern u1="&#xdd;" u2="c" k="55" />
+<hkern u1="&#xdd;" u2="d" k="55" />
+<hkern u1="&#xdd;" u2="e" k="55" />
+<hkern u1="&#xdd;" u2="f" k="10" />
+<hkern u1="&#xdd;" u2="g" k="40" />
+<hkern u1="&#xdd;" u2="m" k="40" />
+<hkern u1="&#xdd;" u2="n" k="40" />
+<hkern u1="&#xdd;" u2="o" k="55" />
+<hkern u1="&#xdd;" u2="p" k="40" />
+<hkern u1="&#xdd;" u2="q" k="55" />
+<hkern u1="&#xdd;" u2="r" k="40" />
+<hkern u1="&#xdd;" u2="s" k="50" />
+<hkern u1="&#xdd;" u2="t" k="20" />
+<hkern u1="&#xdd;" u2="u" k="40" />
+<hkern u1="&#xdd;" u2="v" k="20" />
+<hkern u1="&#xdd;" u2="w" k="10" />
+<hkern u1="&#xdd;" u2="x" k="20" />
+<hkern u1="&#xdd;" u2="y" k="20" />
+<hkern u1="&#xdd;" u2="z" k="30" />
+<hkern u1="&#xdd;" u2="}" k="-50" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="38" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="38" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="25" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="55" />
+<hkern u1="&#xdd;" u2="&#xae;" k="30" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="38" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="25" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="30" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="55" />
+<hkern u1="&#xdd;" u2="&#xab;" k="60" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="38" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="38" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="25" />
+<hkern u1="&#xdd;" u2="&#x152;" k="25" />
+<hkern u1="&#xdd;" u2="&#x153;" k="55" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="60" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="40" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="50" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="38" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="38" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="25" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="25" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="25" />
+<hkern u1="&#xdd;" u2="&#x131;" k="40" />
+<hkern u1="&#xde;" u2="&#xdd;" k="25" />
+<hkern u1="&#xde;" u2="&#x17d;" k="45" />
+<hkern u1="&#xde;" u2="&#x2c;" k="40" />
+<hkern u1="&#xde;" u2="." k="40" />
+<hkern u1="&#xde;" u2="/" k="50" />
+<hkern u1="&#xde;" u2="?" k="20" />
+<hkern u1="&#xde;" u2="A" k="17" />
+<hkern u1="&#xde;" u2="J" k="45" />
+<hkern u1="&#xde;" u2="T" k="32" />
+<hkern u1="&#xde;" u2="V" k="19" />
+<hkern u1="&#xde;" u2="W" k="33" />
+<hkern u1="&#xde;" u2="X" k="25" />
+<hkern u1="&#xde;" u2="Y" k="25" />
+<hkern u1="&#xde;" u2="Z" k="45" />
+<hkern u1="&#xde;" u2="a" k="5" />
+<hkern u1="&#xde;" u2="b" k="10" />
+<hkern u1="&#xde;" u2="c" k="5" />
+<hkern u1="&#xde;" u2="d" k="5" />
+<hkern u1="&#xde;" u2="e" k="5" />
+<hkern u1="&#xde;" u2="h" k="10" />
+<hkern u1="&#xde;" u2="k" k="10" />
+<hkern u1="&#xde;" u2="l" k="10" />
+<hkern u1="&#xde;" u2="m" k="5" />
+<hkern u1="&#xde;" u2="n" k="5" />
+<hkern u1="&#xde;" u2="o" k="5" />
+<hkern u1="&#xde;" u2="p" k="5" />
+<hkern u1="&#xde;" u2="q" k="5" />
+<hkern u1="&#xde;" u2="r" k="5" />
+<hkern u1="&#xde;" u2="u" k="5" />
+<hkern u1="&#xde;" u2="v" k="-5" />
+<hkern u1="&#xde;" u2="x" k="9" />
+<hkern u1="&#xde;" u2="y" k="-5" />
+<hkern u1="&#xde;" u2="z" k="10" />
+<hkern u1="&#xde;" u2="&#xc4;" k="17" />
+<hkern u1="&#xde;" u2="&#xc5;" k="17" />
+<hkern u1="&#xde;" u2="&#xe7;" k="5" />
+<hkern u1="&#xde;" u2="&#xc6;" k="17" />
+<hkern u1="&#xde;" u2="&#xe6;" k="5" />
+<hkern u1="&#xde;" u2="&#x2026;" k="40" />
+<hkern u1="&#xde;" u2="&#xc0;" k="17" />
+<hkern u1="&#xde;" u2="&#xc3;" k="17" />
+<hkern u1="&#xde;" u2="&#x153;" k="5" />
+<hkern u1="&#xde;" u2="&#x201c;" k="20" />
+<hkern u1="&#xde;" u2="&#x2018;" k="20" />
+<hkern u1="&#xde;" u2="&#x178;" k="25" />
+<hkern u1="&#xde;" u2="&#x201a;" k="40" />
+<hkern u1="&#xde;" u2="&#x201e;" k="40" />
+<hkern u1="&#xde;" u2="&#xc2;" k="17" />
+<hkern u1="&#xde;" u2="&#xc1;" k="17" />
+<hkern u1="&#xde;" u2="&#x131;" k="5" />
+<hkern u1="&#x17d;" u2="&#xf0;" k="30" />
+<hkern u1="&#x17d;" u2="-" k="50" />
+<hkern u1="&#x17d;" u2="A" k="7" />
+<hkern u1="&#x17d;" u2="C" k="45" />
+<hkern u1="&#x17d;" u2="G" k="45" />
+<hkern u1="&#x17d;" u2="J" k="10" />
+<hkern u1="&#x17d;" u2="O" k="45" />
+<hkern u1="&#x17d;" u2="Q" k="45" />
+<hkern u1="&#x17d;" u2="T" k="10" />
+<hkern u1="&#x17d;" u2="a" k="40" />
+<hkern u1="&#x17d;" u2="b" k="10" />
+<hkern u1="&#x17d;" u2="c" k="40" />
+<hkern u1="&#x17d;" u2="d" k="40" />
+<hkern u1="&#x17d;" u2="e" k="40" />
+<hkern u1="&#x17d;" u2="f" k="20" />
+<hkern u1="&#x17d;" u2="g" k="20" />
+<hkern u1="&#x17d;" u2="h" k="10" />
+<hkern u1="&#x17d;" u2="i" k="20" />
+<hkern u1="&#x17d;" u2="j" k="15" />
+<hkern u1="&#x17d;" u2="k" k="10" />
+<hkern u1="&#x17d;" u2="l" k="10" />
+<hkern u1="&#x17d;" u2="m" k="20" />
+<hkern u1="&#x17d;" u2="n" k="20" />
+<hkern u1="&#x17d;" u2="o" k="40" />
+<hkern u1="&#x17d;" u2="p" k="20" />
+<hkern u1="&#x17d;" u2="q" k="40" />
+<hkern u1="&#x17d;" u2="r" k="20" />
+<hkern u1="&#x17d;" u2="u" k="20" />
+<hkern u1="&#x17d;" u2="v" k="30" />
+<hkern u1="&#x17d;" u2="w" k="10" />
+<hkern u1="&#x17d;" u2="y" k="30" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="7" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="7" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="45" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="40" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="7" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="45" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="40" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="7" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="7" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="45" />
+<hkern u1="&#x17d;" u2="&#x152;" k="45" />
+<hkern u1="&#x17d;" u2="&#x153;" k="40" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="50" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="50" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="7" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="7" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="45" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="45" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="45" />
+<hkern u1="&#x17d;" u2="&#x131;" k="20" />
+<hkern u1="&amp;" u2="&#xdd;" k="30" />
+<hkern u1="&amp;" u2="A" k="-40" />
+<hkern u1="&amp;" u2="J" k="-20" />
+<hkern u1="&amp;" u2="T" k="70" />
+<hkern u1="&amp;" u2="V" k="20" />
+<hkern u1="&amp;" u2="W" k="20" />
+<hkern u1="&amp;" u2="X" k="-20" />
+<hkern u1="&amp;" u2="Y" k="30" />
+<hkern u1="&amp;" u2="&#xc4;" k="-40" />
+<hkern u1="&amp;" u2="&#xc5;" k="-40" />
+<hkern u1="&amp;" u2="&#xc6;" k="-40" />
+<hkern u1="&amp;" u2="&#xc0;" k="-40" />
+<hkern u1="&amp;" u2="&#xc3;" k="-40" />
+<hkern u1="&amp;" u2="&#x178;" k="30" />
+<hkern u1="&amp;" u2="&#xc2;" k="-40" />
+<hkern u1="&amp;" u2="&#xc1;" k="-40" />
+<hkern u1="(" u2="&#xdd;" k="-50" />
+<hkern u1="(" u2="V" k="-30" />
+<hkern u1="(" u2="W" k="-10" />
+<hkern u1="(" u2="X" k="-40" />
+<hkern u1="(" u2="Y" k="-50" />
+<hkern u1="(" u2="g" k="-20" />
+<hkern u1="(" u2="j" k="-150" />
+<hkern u1="(" u2="&#x178;" k="-50" />
+<hkern u1="*" u2="A" k="60" />
+<hkern u1="*" u2="&#xc4;" k="60" />
+<hkern u1="*" u2="&#xc5;" k="60" />
+<hkern u1="*" u2="&#xc6;" k="60" />
+<hkern u1="*" u2="&#xc0;" k="60" />
+<hkern u1="*" u2="&#xc3;" k="60" />
+<hkern u1="*" u2="&#xc2;" k="60" />
+<hkern u1="*" u2="&#xc1;" k="60" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2c;" u2="0" k="40" />
+<hkern u1="&#x2c;" u2="1" k="60" />
+<hkern u1="&#x2c;" u2="4" k="50" />
+<hkern u1="&#x2c;" u2="6" k="30" />
+<hkern u1="&#x2c;" u2="8" k="15" />
+<hkern u1="&#x2c;" u2="9" k="10" />
+<hkern u1="&#x2c;" u2="A" k="-30" />
+<hkern u1="&#x2c;" u2="C" k="40" />
+<hkern u1="&#x2c;" u2="G" k="40" />
+<hkern u1="&#x2c;" u2="O" k="40" />
+<hkern u1="&#x2c;" u2="Q" k="40" />
+<hkern u1="&#x2c;" u2="T" k="60" />
+<hkern u1="&#x2c;" u2="U" k="10" />
+<hkern u1="&#x2c;" u2="V" k="60" />
+<hkern u1="&#x2c;" u2="W" k="20" />
+<hkern u1="&#x2c;" u2="Y" k="50" />
+<hkern u1="&#x2c;" u2="a" k="20" />
+<hkern u1="&#x2c;" u2="c" k="20" />
+<hkern u1="&#x2c;" u2="d" k="20" />
+<hkern u1="&#x2c;" u2="e" k="20" />
+<hkern u1="&#x2c;" u2="m" k="20" />
+<hkern u1="&#x2c;" u2="n" k="20" />
+<hkern u1="&#x2c;" u2="o" k="20" />
+<hkern u1="&#x2c;" u2="p" k="20" />
+<hkern u1="&#x2c;" u2="q" k="20" />
+<hkern u1="&#x2c;" u2="r" k="20" />
+<hkern u1="&#x2c;" u2="t" k="40" />
+<hkern u1="&#x2c;" u2="u" k="20" />
+<hkern u1="&#x2c;" u2="v" k="30" />
+<hkern u1="&#x2c;" u2="w" k="10" />
+<hkern u1="&#x2c;" u2="y" k="30" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2c;" u2="&#x152;" k="40" />
+<hkern u1="&#x2c;" u2="&#x153;" k="20" />
+<hkern u1="&#x2c;" u2="&#x178;" k="50" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2c;" u2="&#xda;" k="10" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2c;" u2="&#x131;" k="20" />
+<hkern u1="-" u2="&#xdd;" k="50" />
+<hkern u1="-" u2="&#x17d;" k="10" />
+<hkern u1="-" u2="1" k="20" />
+<hkern u1="-" u2="7" k="30" />
+<hkern u1="-" u2="A" k="-10" />
+<hkern u1="-" u2="T" k="70" />
+<hkern u1="-" u2="V" k="50" />
+<hkern u1="-" u2="W" k="40" />
+<hkern u1="-" u2="X" k="30" />
+<hkern u1="-" u2="Y" k="50" />
+<hkern u1="-" u2="Z" k="10" />
+<hkern u1="-" u2="a" k="10" />
+<hkern u1="-" u2="c" k="10" />
+<hkern u1="-" u2="d" k="10" />
+<hkern u1="-" u2="e" k="10" />
+<hkern u1="-" u2="o" k="10" />
+<hkern u1="-" u2="q" k="10" />
+<hkern u1="-" u2="v" k="10" />
+<hkern u1="-" u2="x" k="30" />
+<hkern u1="-" u2="y" k="10" />
+<hkern u1="-" u2="z" k="30" />
+<hkern u1="-" u2="&#xc4;" k="-10" />
+<hkern u1="-" u2="&#xc5;" k="-10" />
+<hkern u1="-" u2="&#xe7;" k="10" />
+<hkern u1="-" u2="&#xc6;" k="-10" />
+<hkern u1="-" u2="&#xe6;" k="10" />
+<hkern u1="-" u2="&#xc0;" k="-10" />
+<hkern u1="-" u2="&#xc3;" k="-10" />
+<hkern u1="-" u2="&#x153;" k="10" />
+<hkern u1="-" u2="&#x178;" k="50" />
+<hkern u1="-" u2="&#xc2;" k="-10" />
+<hkern u1="-" u2="&#xc1;" k="-10" />
+<hkern u1="." u2="&#xdd;" k="50" />
+<hkern u1="." u2="0" k="40" />
+<hkern u1="." u2="1" k="60" />
+<hkern u1="." u2="4" k="50" />
+<hkern u1="." u2="6" k="30" />
+<hkern u1="." u2="8" k="15" />
+<hkern u1="." u2="9" k="10" />
+<hkern u1="." u2="A" k="-30" />
+<hkern u1="." u2="C" k="40" />
+<hkern u1="." u2="G" k="40" />
+<hkern u1="." u2="O" k="40" />
+<hkern u1="." u2="Q" k="40" />
+<hkern u1="." u2="T" k="60" />
+<hkern u1="." u2="U" k="10" />
+<hkern u1="." u2="V" k="60" />
+<hkern u1="." u2="W" k="20" />
+<hkern u1="." u2="Y" k="50" />
+<hkern u1="." u2="a" k="20" />
+<hkern u1="." u2="c" k="20" />
+<hkern u1="." u2="d" k="20" />
+<hkern u1="." u2="e" k="20" />
+<hkern u1="." u2="m" k="20" />
+<hkern u1="." u2="n" k="20" />
+<hkern u1="." u2="o" k="20" />
+<hkern u1="." u2="p" k="20" />
+<hkern u1="." u2="q" k="20" />
+<hkern u1="." u2="r" k="20" />
+<hkern u1="." u2="t" k="40" />
+<hkern u1="." u2="u" k="20" />
+<hkern u1="." u2="v" k="30" />
+<hkern u1="." u2="w" k="10" />
+<hkern u1="." u2="y" k="30" />
+<hkern u1="." u2="&#xc4;" k="-30" />
+<hkern u1="." u2="&#xc5;" k="-30" />
+<hkern u1="." u2="&#xd6;" k="40" />
+<hkern u1="." u2="&#xdc;" k="10" />
+<hkern u1="." u2="&#xe7;" k="20" />
+<hkern u1="." u2="&#xc6;" k="-30" />
+<hkern u1="." u2="&#xd8;" k="40" />
+<hkern u1="." u2="&#xe6;" k="20" />
+<hkern u1="." u2="&#xc0;" k="-30" />
+<hkern u1="." u2="&#xc3;" k="-30" />
+<hkern u1="." u2="&#xd5;" k="40" />
+<hkern u1="." u2="&#x152;" k="40" />
+<hkern u1="." u2="&#x153;" k="20" />
+<hkern u1="." u2="&#x178;" k="50" />
+<hkern u1="." u2="&#xc2;" k="-30" />
+<hkern u1="." u2="&#xc1;" k="-30" />
+<hkern u1="." u2="&#xd3;" k="40" />
+<hkern u1="." u2="&#xd4;" k="40" />
+<hkern u1="." u2="&#xd2;" k="40" />
+<hkern u1="." u2="&#xda;" k="10" />
+<hkern u1="." u2="&#xdb;" k="10" />
+<hkern u1="." u2="&#xd9;" k="10" />
+<hkern u1="." u2="&#x131;" k="20" />
+<hkern u1="/" u2="1" k="-20" />
+<hkern u1="/" u2="4" k="20" />
+<hkern u1="/" u2="C" k="-10" />
+<hkern u1="/" u2="G" k="-10" />
+<hkern u1="/" u2="O" k="-10" />
+<hkern u1="/" u2="Q" k="-10" />
+<hkern u1="/" u2="a" k="10" />
+<hkern u1="/" u2="c" k="10" />
+<hkern u1="/" u2="d" k="10" />
+<hkern u1="/" u2="e" k="10" />
+<hkern u1="/" u2="g" k="10" />
+<hkern u1="/" u2="o" k="10" />
+<hkern u1="/" u2="q" k="10" />
+<hkern u1="/" u2="&#xd6;" k="-10" />
+<hkern u1="/" u2="&#xe7;" k="10" />
+<hkern u1="/" u2="&#xd8;" k="-10" />
+<hkern u1="/" u2="&#xe6;" k="10" />
+<hkern u1="/" u2="&#xd5;" k="-10" />
+<hkern u1="/" u2="&#x152;" k="-10" />
+<hkern u1="/" u2="&#x153;" k="10" />
+<hkern u1="/" u2="&#xd3;" k="-10" />
+<hkern u1="/" u2="&#xd4;" k="-10" />
+<hkern u1="/" u2="&#xd2;" k="-10" />
+<hkern u1="0" u2="&#x2c;" k="40" />
+<hkern u1="0" u2="." k="40" />
+<hkern u1="0" u2="7" k="20" />
+<hkern u1="0" u2="&#x2026;" k="40" />
+<hkern u1="0" u2="&#x201a;" k="40" />
+<hkern u1="0" u2="&#x201e;" k="40" />
+<hkern u1="2" u2="-" k="10" />
+<hkern u1="2" u2="4" k="30" />
+<hkern u1="2" u2="&#x2013;" k="10" />
+<hkern u1="2" u2="&#x2014;" k="10" />
+<hkern u1="3" u2="&#x2c;" k="20" />
+<hkern u1="3" u2="." k="20" />
+<hkern u1="3" u2="7" k="20" />
+<hkern u1="3" u2="&#x2026;" k="20" />
+<hkern u1="3" u2="&#x201a;" k="20" />
+<hkern u1="3" u2="&#x201e;" k="20" />
+<hkern u1="4" u2="7" k="10" />
+<hkern u1="4" u2="&#xb0;" k="40" />
+<hkern u1="4" u2="&#x2122;" k="50" />
+<hkern u1="5" u2="&#x2c;" k="10" />
+<hkern u1="5" u2="." k="10" />
+<hkern u1="5" u2="7" k="10" />
+<hkern u1="5" u2="&#x2026;" k="10" />
+<hkern u1="5" u2="&#x201a;" k="10" />
+<hkern u1="5" u2="&#x201e;" k="10" />
+<hkern u1="6" u2="&#x2c;" k="10" />
+<hkern u1="6" u2="." k="10" />
+<hkern u1="6" u2="7" k="10" />
+<hkern u1="6" u2="&#x2026;" k="10" />
+<hkern u1="6" u2="&#x201a;" k="10" />
+<hkern u1="6" u2="&#x201e;" k="10" />
+<hkern u1="7" u2="&#x2c;" k="80" />
+<hkern u1="7" u2="-" k="40" />
+<hkern u1="7" u2="." k="80" />
+<hkern u1="7" u2="0" k="10" />
+<hkern u1="7" u2="1" k="-20" />
+<hkern u1="7" u2="3" k="-20" />
+<hkern u1="7" u2="4" k="60" />
+<hkern u1="7" u2="5" k="-15" />
+<hkern u1="7" u2="9" k="-10" />
+<hkern u1="7" u2=":" k="20" />
+<hkern u1="7" u2=";" k="20" />
+<hkern u1="7" u2="&#xa2;" k="40" />
+<hkern u1="7" u2="&#x2026;" k="80" />
+<hkern u1="7" u2="&#x2013;" k="40" />
+<hkern u1="7" u2="&#x2014;" k="40" />
+<hkern u1="7" u2="&#x201a;" k="80" />
+<hkern u1="7" u2="&#x201e;" k="80" />
+<hkern u1="8" u2="&#x2c;" k="15" />
+<hkern u1="8" u2="." k="15" />
+<hkern u1="8" u2="7" k="15" />
+<hkern u1="8" u2="&#x2026;" k="15" />
+<hkern u1="8" u2="&#x201a;" k="15" />
+<hkern u1="8" u2="&#x201e;" k="15" />
+<hkern u1="9" u2="&#x2c;" k="30" />
+<hkern u1="9" u2="." k="30" />
+<hkern u1="9" u2="7" k="45" />
+<hkern u1="9" u2="&#x2026;" k="30" />
+<hkern u1="9" u2="&#x201a;" k="30" />
+<hkern u1="9" u2="&#x201e;" k="30" />
+<hkern u1=":" u2="&#xdd;" k="30" />
+<hkern u1=":" u2="1" k="30" />
+<hkern u1=":" u2="7" k="20" />
+<hkern u1=":" u2="T" k="50" />
+<hkern u1=":" u2="V" k="20" />
+<hkern u1=":" u2="W" k="20" />
+<hkern u1=":" u2="Y" k="30" />
+<hkern u1=":" u2="a" k="10" />
+<hkern u1=":" u2="c" k="10" />
+<hkern u1=":" u2="d" k="10" />
+<hkern u1=":" u2="e" k="10" />
+<hkern u1=":" u2="o" k="10" />
+<hkern u1=":" u2="q" k="10" />
+<hkern u1=":" u2="&#xe7;" k="10" />
+<hkern u1=":" u2="&#xe6;" k="10" />
+<hkern u1=":" u2="&#x153;" k="10" />
+<hkern u1=":" u2="&#x178;" k="30" />
+<hkern u1=";" u2="&#xdd;" k="30" />
+<hkern u1=";" u2="1" k="30" />
+<hkern u1=";" u2="7" k="20" />
+<hkern u1=";" u2="T" k="50" />
+<hkern u1=";" u2="V" k="20" />
+<hkern u1=";" u2="W" k="20" />
+<hkern u1=";" u2="Y" k="30" />
+<hkern u1=";" u2="a" k="10" />
+<hkern u1=";" u2="c" k="10" />
+<hkern u1=";" u2="d" k="10" />
+<hkern u1=";" u2="e" k="10" />
+<hkern u1=";" u2="o" k="10" />
+<hkern u1=";" u2="q" k="10" />
+<hkern u1=";" u2="&#xe7;" k="10" />
+<hkern u1=";" u2="&#xe6;" k="10" />
+<hkern u1=";" u2="&#x153;" k="10" />
+<hkern u1=";" u2="&#x178;" k="30" />
+<hkern u1="&gt;" u2="7" k="60" />
+<hkern u1="@" u2="&#xdd;" k="30" />
+<hkern u1="@" u2="A" k="10" />
+<hkern u1="@" u2="T" k="30" />
+<hkern u1="@" u2="W" k="20" />
+<hkern u1="@" u2="Y" k="30" />
+<hkern u1="@" u2="&#xc4;" k="10" />
+<hkern u1="@" u2="&#xc5;" k="10" />
+<hkern u1="@" u2="&#xc6;" k="10" />
+<hkern u1="@" u2="&#xc0;" k="10" />
+<hkern u1="@" u2="&#xc3;" k="10" />
+<hkern u1="@" u2="&#x178;" k="30" />
+<hkern u1="@" u2="&#xc2;" k="10" />
+<hkern u1="@" u2="&#xc1;" k="10" />
+<hkern u1="A" u2="&#x160;" k="-5" />
+<hkern u1="A" u2="&#xdd;" k="38" />
+<hkern u1="A" u2="&#x17d;" k="-3" />
+<hkern u1="A" u2="*" k="60" />
+<hkern u1="A" u2="&#x2c;" k="-30" />
+<hkern u1="A" u2="-" k="-10" />
+<hkern u1="A" u2="." k="-30" />
+<hkern u1="A" u2="@" k="10" />
+<hkern u1="A" u2="A" k="-13" />
+<hkern u1="A" u2="C" k="17" />
+<hkern u1="A" u2="G" k="17" />
+<hkern u1="A" u2="J" k="-10" />
+<hkern u1="A" u2="O" k="17" />
+<hkern u1="A" u2="Q" k="17" />
+<hkern u1="A" u2="S" k="-5" />
+<hkern u1="A" u2="T" k="114" />
+<hkern u1="A" u2="V" k="44" />
+<hkern u1="A" u2="W" k="44" />
+<hkern u1="A" u2="X" k="-13" />
+<hkern u1="A" u2="Y" k="38" />
+<hkern u1="A" u2="Z" k="-3" />
+<hkern u1="A" u2="a" k="-5" />
+<hkern u1="A" u2="c" k="4" />
+<hkern u1="A" u2="d" k="4" />
+<hkern u1="A" u2="e" k="4" />
+<hkern u1="A" u2="m" k="5" />
+<hkern u1="A" u2="n" k="5" />
+<hkern u1="A" u2="o" k="4" />
+<hkern u1="A" u2="p" k="5" />
+<hkern u1="A" u2="q" k="4" />
+<hkern u1="A" u2="r" k="5" />
+<hkern u1="A" u2="t" k="5" />
+<hkern u1="A" u2="u" k="5" />
+<hkern u1="A" u2="v" k="30" />
+<hkern u1="A" u2="w" k="10" />
+<hkern u1="A" u2="x" k="-20" />
+<hkern u1="A" u2="y" k="30" />
+<hkern u1="A" u2="z" k="-10" />
+<hkern u1="A" u2="&#xc4;" k="-13" />
+<hkern u1="A" u2="&#xc5;" k="-13" />
+<hkern u1="A" u2="&#xd6;" k="17" />
+<hkern u1="A" u2="&#xe7;" k="4" />
+<hkern u1="A" u2="&#xae;" k="10" />
+<hkern u1="A" u2="&#xa9;" k="10" />
+<hkern u1="A" u2="&#x2122;" k="70" />
+<hkern u1="A" u2="&#xc6;" k="-13" />
+<hkern u1="A" u2="&#xd8;" k="17" />
+<hkern u1="A" u2="&#x3c0;" k="10" />
+<hkern u1="A" u2="&#xe6;" k="4" />
+<hkern u1="A" u2="&#x2026;" k="-30" />
+<hkern u1="A" u2="&#xc0;" k="-13" />
+<hkern u1="A" u2="&#xc3;" k="-13" />
+<hkern u1="A" u2="&#xd5;" k="17" />
+<hkern u1="A" u2="&#x152;" k="17" />
+<hkern u1="A" u2="&#x153;" k="4" />
+<hkern u1="A" u2="&#x2013;" k="-10" />
+<hkern u1="A" u2="&#x2014;" k="-10" />
+<hkern u1="A" u2="&#x201c;" k="60" />
+<hkern u1="A" u2="&#x201d;" k="30" />
+<hkern u1="A" u2="&#x2018;" k="60" />
+<hkern u1="A" u2="&#x2019;" k="30" />
+<hkern u1="A" u2="&#x178;" k="38" />
+<hkern u1="A" u2="&#x201a;" k="-30" />
+<hkern u1="A" u2="&#x201e;" k="-30" />
+<hkern u1="A" u2="&#xc2;" k="-13" />
+<hkern u1="A" u2="&#xc1;" k="-13" />
+<hkern u1="A" u2="&#xd3;" k="17" />
+<hkern u1="A" u2="&#xd4;" k="17" />
+<hkern u1="A" u2="&#xd2;" k="17" />
+<hkern u1="A" u2="&#x131;" k="5" />
+<hkern u1="B" u2="V" k="5" />
+<hkern u1="B" u2="W" k="20" />
+<hkern u1="C" u2="&#xdd;" k="-20" />
+<hkern u1="C" u2="V" k="-20" />
+<hkern u1="C" u2="X" k="-10" />
+<hkern u1="C" u2="Y" k="-20" />
+<hkern u1="C" u2="a" k="-5" />
+<hkern u1="C" u2="c" k="-5" />
+<hkern u1="C" u2="d" k="-5" />
+<hkern u1="C" u2="e" k="-5" />
+<hkern u1="C" u2="o" k="-5" />
+<hkern u1="C" u2="q" k="-5" />
+<hkern u1="C" u2="t" k="5" />
+<hkern u1="C" u2="v" k="5" />
+<hkern u1="C" u2="w" k="5" />
+<hkern u1="C" u2="y" k="5" />
+<hkern u1="C" u2="z" k="10" />
+<hkern u1="C" u2="&#xe7;" k="-5" />
+<hkern u1="C" u2="&#xe6;" k="-5" />
+<hkern u1="C" u2="&#x153;" k="-5" />
+<hkern u1="C" u2="&#x178;" k="-20" />
+<hkern u1="D" u2="&#xdd;" k="25" />
+<hkern u1="D" u2="&#x17d;" k="45" />
+<hkern u1="D" u2="&#x2c;" k="40" />
+<hkern u1="D" u2="." k="40" />
+<hkern u1="D" u2="/" k="50" />
+<hkern u1="D" u2="?" k="20" />
+<hkern u1="D" u2="A" k="17" />
+<hkern u1="D" u2="J" k="45" />
+<hkern u1="D" u2="T" k="32" />
+<hkern u1="D" u2="V" k="19" />
+<hkern u1="D" u2="W" k="33" />
+<hkern u1="D" u2="X" k="25" />
+<hkern u1="D" u2="Y" k="25" />
+<hkern u1="D" u2="Z" k="45" />
+<hkern u1="D" u2="a" k="5" />
+<hkern u1="D" u2="b" k="10" />
+<hkern u1="D" u2="c" k="5" />
+<hkern u1="D" u2="d" k="5" />
+<hkern u1="D" u2="e" k="5" />
+<hkern u1="D" u2="h" k="10" />
+<hkern u1="D" u2="k" k="10" />
+<hkern u1="D" u2="l" k="10" />
+<hkern u1="D" u2="m" k="5" />
+<hkern u1="D" u2="n" k="5" />
+<hkern u1="D" u2="o" k="5" />
+<hkern u1="D" u2="p" k="5" />
+<hkern u1="D" u2="q" k="5" />
+<hkern u1="D" u2="r" k="5" />
+<hkern u1="D" u2="u" k="5" />
+<hkern u1="D" u2="v" k="-5" />
+<hkern u1="D" u2="x" k="9" />
+<hkern u1="D" u2="y" k="-5" />
+<hkern u1="D" u2="z" k="10" />
+<hkern u1="D" u2="&#xc4;" k="17" />
+<hkern u1="D" u2="&#xc5;" k="17" />
+<hkern u1="D" u2="&#xe7;" k="5" />
+<hkern u1="D" u2="&#xc6;" k="17" />
+<hkern u1="D" u2="&#xe6;" k="5" />
+<hkern u1="D" u2="&#x2026;" k="40" />
+<hkern u1="D" u2="&#xc0;" k="17" />
+<hkern u1="D" u2="&#xc3;" k="17" />
+<hkern u1="D" u2="&#x153;" k="5" />
+<hkern u1="D" u2="&#x201c;" k="20" />
+<hkern u1="D" u2="&#x2018;" k="20" />
+<hkern u1="D" u2="&#x178;" k="25" />
+<hkern u1="D" u2="&#x201a;" k="40" />
+<hkern u1="D" u2="&#x201e;" k="40" />
+<hkern u1="D" u2="&#xc2;" k="17" />
+<hkern u1="D" u2="&#xc1;" k="17" />
+<hkern u1="D" u2="&#x131;" k="5" />
+<hkern u1="E" u2="@" k="20" />
+<hkern u1="E" u2="C" k="6" />
+<hkern u1="E" u2="G" k="6" />
+<hkern u1="E" u2="O" k="6" />
+<hkern u1="E" u2="Q" k="6" />
+<hkern u1="E" u2="T" k="-15" />
+<hkern u1="E" u2="a" k="25" />
+<hkern u1="E" u2="c" k="25" />
+<hkern u1="E" u2="d" k="25" />
+<hkern u1="E" u2="e" k="25" />
+<hkern u1="E" u2="f" k="10" />
+<hkern u1="E" u2="g" k="5" />
+<hkern u1="E" u2="o" k="25" />
+<hkern u1="E" u2="q" k="25" />
+<hkern u1="E" u2="v" k="20" />
+<hkern u1="E" u2="y" k="20" />
+<hkern u1="E" u2="&#xd6;" k="6" />
+<hkern u1="E" u2="&#xe7;" k="25" />
+<hkern u1="E" u2="&#xae;" k="20" />
+<hkern u1="E" u2="&#xa9;" k="20" />
+<hkern u1="E" u2="&#xd8;" k="6" />
+<hkern u1="E" u2="&#x3c0;" k="20" />
+<hkern u1="E" u2="&#xe6;" k="25" />
+<hkern u1="E" u2="&#xd5;" k="6" />
+<hkern u1="E" u2="&#x152;" k="6" />
+<hkern u1="E" u2="&#x153;" k="25" />
+<hkern u1="E" u2="&#xd3;" k="6" />
+<hkern u1="E" u2="&#xd4;" k="6" />
+<hkern u1="E" u2="&#xd2;" k="6" />
+<hkern u1="F" u2="&#x161;" k="40" />
+<hkern u1="F" u2="&#xdd;" k="-25" />
+<hkern u1="F" u2="&amp;" k="40" />
+<hkern u1="F" u2="&#x2c;" k="90" />
+<hkern u1="F" u2="-" k="50" />
+<hkern u1="F" u2="." k="90" />
+<hkern u1="F" u2="/" k="80" />
+<hkern u1="F" u2=":" k="30" />
+<hkern u1="F" u2=";" k="30" />
+<hkern u1="F" u2="@" k="20" />
+<hkern u1="F" u2="A" k="50" />
+<hkern u1="F" u2="J" k="90" />
+<hkern u1="F" u2="T" k="-30" />
+<hkern u1="F" u2="V" k="-20" />
+<hkern u1="F" u2="W" k="-5" />
+<hkern u1="F" u2="X" k="-10" />
+<hkern u1="F" u2="Y" k="-25" />
+<hkern u1="F" u2="a" k="53" />
+<hkern u1="F" u2="b" k="10" />
+<hkern u1="F" u2="c" k="53" />
+<hkern u1="F" u2="d" k="53" />
+<hkern u1="F" u2="e" k="53" />
+<hkern u1="F" u2="f" k="20" />
+<hkern u1="F" u2="g" k="30" />
+<hkern u1="F" u2="h" k="10" />
+<hkern u1="F" u2="i" k="10" />
+<hkern u1="F" u2="j" k="10" />
+<hkern u1="F" u2="k" k="10" />
+<hkern u1="F" u2="l" k="10" />
+<hkern u1="F" u2="m" k="40" />
+<hkern u1="F" u2="n" k="40" />
+<hkern u1="F" u2="o" k="53" />
+<hkern u1="F" u2="p" k="40" />
+<hkern u1="F" u2="q" k="53" />
+<hkern u1="F" u2="r" k="40" />
+<hkern u1="F" u2="s" k="40" />
+<hkern u1="F" u2="t" k="20" />
+<hkern u1="F" u2="u" k="40" />
+<hkern u1="F" u2="v" k="20" />
+<hkern u1="F" u2="w" k="20" />
+<hkern u1="F" u2="x" k="40" />
+<hkern u1="F" u2="y" k="20" />
+<hkern u1="F" u2="z" k="40" />
+<hkern u1="F" u2="&#xc4;" k="50" />
+<hkern u1="F" u2="&#xc5;" k="50" />
+<hkern u1="F" u2="&#xe7;" k="53" />
+<hkern u1="F" u2="&#xae;" k="20" />
+<hkern u1="F" u2="&#xa9;" k="20" />
+<hkern u1="F" u2="&#xc6;" k="50" />
+<hkern u1="F" u2="&#x3c0;" k="20" />
+<hkern u1="F" u2="&#xe6;" k="53" />
+<hkern u1="F" u2="&#x2026;" k="90" />
+<hkern u1="F" u2="&#xc0;" k="50" />
+<hkern u1="F" u2="&#xc3;" k="50" />
+<hkern u1="F" u2="&#x153;" k="53" />
+<hkern u1="F" u2="&#x2013;" k="50" />
+<hkern u1="F" u2="&#x2014;" k="50" />
+<hkern u1="F" u2="&#x178;" k="-25" />
+<hkern u1="F" u2="&#x201a;" k="90" />
+<hkern u1="F" u2="&#x201e;" k="90" />
+<hkern u1="F" u2="&#xc2;" k="50" />
+<hkern u1="F" u2="&#xc1;" k="50" />
+<hkern u1="F" u2="&#x131;" k="40" />
+<hkern u1="G" u2="&#xdd;" k="12" />
+<hkern u1="G" u2="&#x17d;" k="-2" />
+<hkern u1="G" u2="&#x2c;" k="-20" />
+<hkern u1="G" u2="." k="-20" />
+<hkern u1="G" u2="T" k="25" />
+<hkern u1="G" u2="V" k="7" />
+<hkern u1="G" u2="W" k="12" />
+<hkern u1="G" u2="Y" k="12" />
+<hkern u1="G" u2="Z" k="-2" />
+<hkern u1="G" u2="t" k="10" />
+<hkern u1="G" u2="v" k="10" />
+<hkern u1="G" u2="y" k="10" />
+<hkern u1="G" u2="&#x2122;" k="30" />
+<hkern u1="G" u2="&#x2026;" k="-20" />
+<hkern u1="G" u2="&#x178;" k="12" />
+<hkern u1="G" u2="&#x201a;" k="-20" />
+<hkern u1="G" u2="&#x201e;" k="-20" />
+<hkern u1="H" u2="/" k="20" />
+<hkern u1="H" u2="v" k="10" />
+<hkern u1="H" u2="y" k="10" />
+<hkern u1="I" u2="/" k="20" />
+<hkern u1="I" u2="v" k="10" />
+<hkern u1="I" u2="y" k="10" />
+<hkern u1="J" u2="&#x2c;" k="10" />
+<hkern u1="J" u2="." k="10" />
+<hkern u1="J" u2="J" k="10" />
+<hkern u1="J" u2="&#x2026;" k="10" />
+<hkern u1="J" u2="&#x201a;" k="10" />
+<hkern u1="J" u2="&#x201e;" k="10" />
+<hkern u1="K" u2="&#xf0;" k="20" />
+<hkern u1="K" u2="&#x160;" k="5" />
+<hkern u1="K" u2="&#xdd;" k="-14" />
+<hkern u1="K" u2="&#x17d;" k="5" />
+<hkern u1="K" u2="&amp;" k="20" />
+<hkern u1="K" u2="-" k="40" />
+<hkern u1="K" u2="@" k="30" />
+<hkern u1="K" u2="C" k="30" />
+<hkern u1="K" u2="G" k="30" />
+<hkern u1="K" u2="O" k="30" />
+<hkern u1="K" u2="Q" k="30" />
+<hkern u1="K" u2="S" k="5" />
+<hkern u1="K" u2="T" k="-5" />
+<hkern u1="K" u2="U" k="10" />
+<hkern u1="K" u2="V" k="-12" />
+<hkern u1="K" u2="W" k="12" />
+<hkern u1="K" u2="X" k="-7" />
+<hkern u1="K" u2="Y" k="-14" />
+<hkern u1="K" u2="Z" k="5" />
+<hkern u1="K" u2="a" k="20" />
+<hkern u1="K" u2="c" k="23" />
+<hkern u1="K" u2="d" k="23" />
+<hkern u1="K" u2="e" k="23" />
+<hkern u1="K" u2="f" k="5" />
+<hkern u1="K" u2="g" k="10" />
+<hkern u1="K" u2="o" k="23" />
+<hkern u1="K" u2="q" k="23" />
+<hkern u1="K" u2="t" k="10" />
+<hkern u1="K" u2="u" k="6" />
+<hkern u1="K" u2="v" k="20" />
+<hkern u1="K" u2="y" k="20" />
+<hkern u1="K" u2="&#xd6;" k="30" />
+<hkern u1="K" u2="&#xdc;" k="10" />
+<hkern u1="K" u2="&#xe7;" k="23" />
+<hkern u1="K" u2="&#xae;" k="30" />
+<hkern u1="K" u2="&#xa9;" k="30" />
+<hkern u1="K" u2="&#xd8;" k="30" />
+<hkern u1="K" u2="&#x3c0;" k="30" />
+<hkern u1="K" u2="&#xe6;" k="23" />
+<hkern u1="K" u2="&#xd5;" k="30" />
+<hkern u1="K" u2="&#x152;" k="30" />
+<hkern u1="K" u2="&#x153;" k="23" />
+<hkern u1="K" u2="&#x2013;" k="40" />
+<hkern u1="K" u2="&#x2014;" k="40" />
+<hkern u1="K" u2="&#x178;" k="-14" />
+<hkern u1="K" u2="&#xd3;" k="30" />
+<hkern u1="K" u2="&#xd4;" k="30" />
+<hkern u1="K" u2="&#xd2;" k="30" />
+<hkern u1="K" u2="&#xda;" k="10" />
+<hkern u1="K" u2="&#xdb;" k="10" />
+<hkern u1="K" u2="&#xd9;" k="10" />
+<hkern u1="L" u2="&#xdd;" k="80" />
+<hkern u1="L" u2="@" k="50" />
+<hkern u1="L" u2="A" k="-20" />
+<hkern u1="L" u2="C" k="50" />
+<hkern u1="L" u2="G" k="50" />
+<hkern u1="L" u2="O" k="50" />
+<hkern u1="L" u2="Q" k="50" />
+<hkern u1="L" u2="T" k="100" />
+<hkern u1="L" u2="U" k="15" />
+<hkern u1="L" u2="V" k="90" />
+<hkern u1="L" u2="W" k="70" />
+<hkern u1="L" u2="Y" k="80" />
+<hkern u1="L" u2="\" k="50" />
+<hkern u1="L" u2="a" k="20" />
+<hkern u1="L" u2="c" k="20" />
+<hkern u1="L" u2="d" k="20" />
+<hkern u1="L" u2="e" k="20" />
+<hkern u1="L" u2="o" k="20" />
+<hkern u1="L" u2="q" k="20" />
+<hkern u1="L" u2="u" k="6" />
+<hkern u1="L" u2="v" k="40" />
+<hkern u1="L" u2="w" k="10" />
+<hkern u1="L" u2="y" k="40" />
+<hkern u1="L" u2="&#xc4;" k="-20" />
+<hkern u1="L" u2="&#xc5;" k="-20" />
+<hkern u1="L" u2="&#xd6;" k="50" />
+<hkern u1="L" u2="&#xdc;" k="15" />
+<hkern u1="L" u2="&#xe7;" k="20" />
+<hkern u1="L" u2="&#xae;" k="50" />
+<hkern u1="L" u2="&#xa9;" k="50" />
+<hkern u1="L" u2="&#x2122;" k="70" />
+<hkern u1="L" u2="&#xc6;" k="-20" />
+<hkern u1="L" u2="&#xd8;" k="50" />
+<hkern u1="L" u2="&#x3c0;" k="50" />
+<hkern u1="L" u2="&#xe6;" k="20" />
+<hkern u1="L" u2="&#xc0;" k="-20" />
+<hkern u1="L" u2="&#xc3;" k="-20" />
+<hkern u1="L" u2="&#xd5;" k="50" />
+<hkern u1="L" u2="&#x152;" k="50" />
+<hkern u1="L" u2="&#x153;" k="20" />
+<hkern u1="L" u2="&#x201c;" k="100" />
+<hkern u1="L" u2="&#x201d;" k="60" />
+<hkern u1="L" u2="&#x2018;" k="100" />
+<hkern u1="L" u2="&#x2019;" k="60" />
+<hkern u1="L" u2="&#x178;" k="80" />
+<hkern u1="L" u2="&#xc2;" k="-20" />
+<hkern u1="L" u2="&#xc1;" k="-20" />
+<hkern u1="L" u2="&#xd3;" k="50" />
+<hkern u1="L" u2="&#xd4;" k="50" />
+<hkern u1="L" u2="&#xd2;" k="50" />
+<hkern u1="L" u2="&#xda;" k="15" />
+<hkern u1="L" u2="&#xdb;" k="15" />
+<hkern u1="L" u2="&#xd9;" k="15" />
+<hkern u1="M" u2="/" k="20" />
+<hkern u1="M" u2="v" k="10" />
+<hkern u1="M" u2="y" k="10" />
+<hkern u1="N" u2="/" k="20" />
+<hkern u1="N" u2="v" k="10" />
+<hkern u1="N" u2="y" k="10" />
+<hkern u1="O" u2="&#xdd;" k="25" />
+<hkern u1="O" u2="&#x17d;" k="45" />
+<hkern u1="O" u2="&#x2c;" k="40" />
+<hkern u1="O" u2="." k="40" />
+<hkern u1="O" u2="/" k="50" />
+<hkern u1="O" u2="?" k="20" />
+<hkern u1="O" u2="A" k="17" />
+<hkern u1="O" u2="J" k="45" />
+<hkern u1="O" u2="T" k="32" />
+<hkern u1="O" u2="V" k="19" />
+<hkern u1="O" u2="W" k="33" />
+<hkern u1="O" u2="X" k="25" />
+<hkern u1="O" u2="Y" k="25" />
+<hkern u1="O" u2="Z" k="45" />
+<hkern u1="O" u2="a" k="5" />
+<hkern u1="O" u2="b" k="10" />
+<hkern u1="O" u2="c" k="5" />
+<hkern u1="O" u2="d" k="5" />
+<hkern u1="O" u2="e" k="5" />
+<hkern u1="O" u2="h" k="10" />
+<hkern u1="O" u2="k" k="10" />
+<hkern u1="O" u2="l" k="10" />
+<hkern u1="O" u2="m" k="5" />
+<hkern u1="O" u2="n" k="5" />
+<hkern u1="O" u2="o" k="5" />
+<hkern u1="O" u2="p" k="5" />
+<hkern u1="O" u2="q" k="5" />
+<hkern u1="O" u2="r" k="5" />
+<hkern u1="O" u2="u" k="5" />
+<hkern u1="O" u2="v" k="-5" />
+<hkern u1="O" u2="x" k="9" />
+<hkern u1="O" u2="y" k="-5" />
+<hkern u1="O" u2="z" k="10" />
+<hkern u1="O" u2="&#xc4;" k="17" />
+<hkern u1="O" u2="&#xc5;" k="17" />
+<hkern u1="O" u2="&#xe7;" k="5" />
+<hkern u1="O" u2="&#xc6;" k="17" />
+<hkern u1="O" u2="&#xe6;" k="5" />
+<hkern u1="O" u2="&#x2026;" k="40" />
+<hkern u1="O" u2="&#xc0;" k="17" />
+<hkern u1="O" u2="&#xc3;" k="17" />
+<hkern u1="O" u2="&#x153;" k="5" />
+<hkern u1="O" u2="&#x201c;" k="20" />
+<hkern u1="O" u2="&#x2018;" k="20" />
+<hkern u1="O" u2="&#x178;" k="25" />
+<hkern u1="O" u2="&#x201a;" k="40" />
+<hkern u1="O" u2="&#x201e;" k="40" />
+<hkern u1="O" u2="&#xc2;" k="17" />
+<hkern u1="O" u2="&#xc1;" k="17" />
+<hkern u1="O" u2="&#x131;" k="5" />
+<hkern u1="P" u2="&#xdd;" k="-15" />
+<hkern u1="P" u2="&#x17d;" k="25" />
+<hkern u1="P" u2="&amp;" k="20" />
+<hkern u1="P" u2="&#x2c;" k="60" />
+<hkern u1="P" u2="." k="60" />
+<hkern u1="P" u2="A" k="35" />
+<hkern u1="P" u2="J" k="85" />
+<hkern u1="P" u2="V" k="-25" />
+<hkern u1="P" u2="W" k="-5" />
+<hkern u1="P" u2="X" k="-5" />
+<hkern u1="P" u2="Y" k="-15" />
+<hkern u1="P" u2="Z" k="25" />
+<hkern u1="P" u2="a" k="10" />
+<hkern u1="P" u2="c" k="10" />
+<hkern u1="P" u2="d" k="10" />
+<hkern u1="P" u2="e" k="10" />
+<hkern u1="P" u2="f" k="-10" />
+<hkern u1="P" u2="o" k="10" />
+<hkern u1="P" u2="q" k="10" />
+<hkern u1="P" u2="t" k="-10" />
+<hkern u1="P" u2="v" k="-18" />
+<hkern u1="P" u2="w" k="-9" />
+<hkern u1="P" u2="x" k="-8" />
+<hkern u1="P" u2="y" k="-18" />
+<hkern u1="P" u2="&#xc4;" k="35" />
+<hkern u1="P" u2="&#xc5;" k="35" />
+<hkern u1="P" u2="&#xe7;" k="10" />
+<hkern u1="P" u2="&#xc6;" k="35" />
+<hkern u1="P" u2="&#xe6;" k="10" />
+<hkern u1="P" u2="&#x2026;" k="60" />
+<hkern u1="P" u2="&#xc0;" k="35" />
+<hkern u1="P" u2="&#xc3;" k="35" />
+<hkern u1="P" u2="&#x153;" k="10" />
+<hkern u1="P" u2="&#x178;" k="-15" />
+<hkern u1="P" u2="&#x201a;" k="60" />
+<hkern u1="P" u2="&#x201e;" k="60" />
+<hkern u1="P" u2="&#xc2;" k="35" />
+<hkern u1="P" u2="&#xc1;" k="35" />
+<hkern u1="Q" u2="&#xdd;" k="25" />
+<hkern u1="Q" u2="&#x17d;" k="45" />
+<hkern u1="Q" u2="&#x2c;" k="40" />
+<hkern u1="Q" u2="." k="12" />
+<hkern u1="Q" u2="/" k="30" />
+<hkern u1="Q" u2="?" k="20" />
+<hkern u1="Q" u2="A" k="17" />
+<hkern u1="Q" u2="J" k="45" />
+<hkern u1="Q" u2="T" k="32" />
+<hkern u1="Q" u2="V" k="19" />
+<hkern u1="Q" u2="W" k="33" />
+<hkern u1="Q" u2="X" k="25" />
+<hkern u1="Q" u2="Y" k="25" />
+<hkern u1="Q" u2="Z" k="45" />
+<hkern u1="Q" u2="a" k="5" />
+<hkern u1="Q" u2="b" k="10" />
+<hkern u1="Q" u2="c" k="5" />
+<hkern u1="Q" u2="d" k="5" />
+<hkern u1="Q" u2="e" k="5" />
+<hkern u1="Q" u2="h" k="10" />
+<hkern u1="Q" u2="k" k="10" />
+<hkern u1="Q" u2="l" k="10" />
+<hkern u1="Q" u2="m" k="5" />
+<hkern u1="Q" u2="n" k="5" />
+<hkern u1="Q" u2="o" k="5" />
+<hkern u1="Q" u2="p" k="5" />
+<hkern u1="Q" u2="q" k="5" />
+<hkern u1="Q" u2="r" k="5" />
+<hkern u1="Q" u2="u" k="5" />
+<hkern u1="Q" u2="v" k="-5" />
+<hkern u1="Q" u2="x" k="9" />
+<hkern u1="Q" u2="y" k="-5" />
+<hkern u1="Q" u2="z" k="10" />
+<hkern u1="Q" u2="&#xc4;" k="17" />
+<hkern u1="Q" u2="&#xc5;" k="17" />
+<hkern u1="Q" u2="&#xe7;" k="5" />
+<hkern u1="Q" u2="&#xc6;" k="17" />
+<hkern u1="Q" u2="&#xe6;" k="5" />
+<hkern u1="Q" u2="&#x2026;" k="40" />
+<hkern u1="Q" u2="&#xc0;" k="17" />
+<hkern u1="Q" u2="&#xc3;" k="17" />
+<hkern u1="Q" u2="&#x153;" k="5" />
+<hkern u1="Q" u2="&#x201c;" k="20" />
+<hkern u1="Q" u2="&#x2018;" k="20" />
+<hkern u1="Q" u2="&#x178;" k="25" />
+<hkern u1="Q" u2="&#x201a;" k="40" />
+<hkern u1="Q" u2="&#x201e;" k="40" />
+<hkern u1="Q" u2="&#xc2;" k="17" />
+<hkern u1="Q" u2="&#xc1;" k="17" />
+<hkern u1="Q" u2="&#x131;" k="5" />
+<hkern u1="R" u2="&#xdd;" k="-15" />
+<hkern u1="R" u2="&amp;" k="-10" />
+<hkern u1="R" u2="J" k="10" />
+<hkern u1="R" u2="T" k="10" />
+<hkern u1="R" u2="V" k="-20" />
+<hkern u1="R" u2="X" k="-10" />
+<hkern u1="R" u2="Y" k="-15" />
+<hkern u1="R" u2="&#x178;" k="-15" />
+<hkern u1="S" u2="&#xdd;" k="-10" />
+<hkern u1="S" u2="A" k="-5" />
+<hkern u1="S" u2="V" k="-10" />
+<hkern u1="S" u2="X" k="-5" />
+<hkern u1="S" u2="Y" k="-10" />
+<hkern u1="S" u2="v" k="3" />
+<hkern u1="S" u2="y" k="3" />
+<hkern u1="S" u2="&#xc4;" k="-5" />
+<hkern u1="S" u2="&#xc5;" k="-5" />
+<hkern u1="S" u2="&#xc6;" k="-5" />
+<hkern u1="S" u2="&#xc0;" k="-5" />
+<hkern u1="S" u2="&#xc3;" k="-5" />
+<hkern u1="S" u2="&#x178;" k="-10" />
+<hkern u1="S" u2="&#xc2;" k="-5" />
+<hkern u1="S" u2="&#xc1;" k="-5" />
+<hkern u1="T" u2="&#x161;" k="100" />
+<hkern u1="T" u2="&#xdd;" k="-20" />
+<hkern u1="T" u2="&#x17d;" k="10" />
+<hkern u1="T" u2="&amp;" k="70" />
+<hkern u1="T" u2="&#x2c;" k="60" />
+<hkern u1="T" u2="-" k="70" />
+<hkern u1="T" u2="." k="60" />
+<hkern u1="T" u2=":" k="50" />
+<hkern u1="T" u2=";" k="50" />
+<hkern u1="T" u2="@" k="30" />
+<hkern u1="T" u2="A" k="64" />
+<hkern u1="T" u2="C" k="32" />
+<hkern u1="T" u2="G" k="32" />
+<hkern u1="T" u2="J" k="88" />
+<hkern u1="T" u2="O" k="32" />
+<hkern u1="T" u2="Q" k="32" />
+<hkern u1="T" u2="T" k="-10" />
+<hkern u1="T" u2="V" k="-20" />
+<hkern u1="T" u2="X" k="-10" />
+<hkern u1="T" u2="Y" k="-20" />
+<hkern u1="T" u2="Z" k="10" />
+<hkern u1="T" u2="\" k="-10" />
+<hkern u1="T" u2="a" k="95" />
+<hkern u1="T" u2="c" k="95" />
+<hkern u1="T" u2="d" k="95" />
+<hkern u1="T" u2="e" k="95" />
+<hkern u1="T" u2="f" k="10" />
+<hkern u1="T" u2="g" k="95" />
+<hkern u1="T" u2="m" k="80" />
+<hkern u1="T" u2="n" k="80" />
+<hkern u1="T" u2="o" k="95" />
+<hkern u1="T" u2="p" k="80" />
+<hkern u1="T" u2="q" k="95" />
+<hkern u1="T" u2="r" k="80" />
+<hkern u1="T" u2="s" k="100" />
+<hkern u1="T" u2="t" k="20" />
+<hkern u1="T" u2="u" k="80" />
+<hkern u1="T" u2="v" k="73" />
+<hkern u1="T" u2="w" k="63" />
+<hkern u1="T" u2="x" k="80" />
+<hkern u1="T" u2="y" k="73" />
+<hkern u1="T" u2="z" k="80" />
+<hkern u1="T" u2="&#xc4;" k="64" />
+<hkern u1="T" u2="&#xc5;" k="64" />
+<hkern u1="T" u2="&#xd6;" k="32" />
+<hkern u1="T" u2="&#xe7;" k="95" />
+<hkern u1="T" u2="&#xae;" k="30" />
+<hkern u1="T" u2="&#xa9;" k="30" />
+<hkern u1="T" u2="&#xc6;" k="64" />
+<hkern u1="T" u2="&#xd8;" k="32" />
+<hkern u1="T" u2="&#x3c0;" k="30" />
+<hkern u1="T" u2="&#xe6;" k="95" />
+<hkern u1="T" u2="&#xbf;" k="50" />
+<hkern u1="T" u2="&#xab;" k="110" />
+<hkern u1="T" u2="&#xbb;" k="40" />
+<hkern u1="T" u2="&#x2026;" k="60" />
+<hkern u1="T" u2="&#xc0;" k="64" />
+<hkern u1="T" u2="&#xc3;" k="64" />
+<hkern u1="T" u2="&#xd5;" k="32" />
+<hkern u1="T" u2="&#x152;" k="32" />
+<hkern u1="T" u2="&#x153;" k="95" />
+<hkern u1="T" u2="&#x2013;" k="70" />
+<hkern u1="T" u2="&#x2014;" k="70" />
+<hkern u1="T" u2="&#x178;" k="-20" />
+<hkern u1="T" u2="&#x2039;" k="110" />
+<hkern u1="T" u2="&#x203a;" k="40" />
+<hkern u1="T" u2="&#x201a;" k="60" />
+<hkern u1="T" u2="&#x201e;" k="60" />
+<hkern u1="T" u2="&#xc2;" k="64" />
+<hkern u1="T" u2="&#xc1;" k="64" />
+<hkern u1="T" u2="&#xd3;" k="32" />
+<hkern u1="T" u2="&#xd4;" k="32" />
+<hkern u1="T" u2="&#xd2;" k="32" />
+<hkern u1="T" u2="&#x131;" k="80" />
+<hkern u1="U" u2="&#x2c;" k="10" />
+<hkern u1="U" u2="." k="10" />
+<hkern u1="U" u2="J" k="10" />
+<hkern u1="U" u2="&#x2026;" k="10" />
+<hkern u1="U" u2="&#x201a;" k="10" />
+<hkern u1="U" u2="&#x201e;" k="10" />
+<hkern u1="V" u2="&#x160;" k="-10" />
+<hkern u1="V" u2="&#x161;" k="40" />
+<hkern u1="V" u2="&#xdd;" k="-30" />
+<hkern u1="V" u2="&amp;" k="40" />
+<hkern u1="V" u2=")" k="-30" />
+<hkern u1="V" u2="&#x2c;" k="60" />
+<hkern u1="V" u2="-" k="60" />
+<hkern u1="V" u2="." k="60" />
+<hkern u1="V" u2=":" k="20" />
+<hkern u1="V" u2=";" k="20" />
+<hkern u1="V" u2="A" k="44" />
+<hkern u1="V" u2="C" k="19" />
+<hkern u1="V" u2="G" k="19" />
+<hkern u1="V" u2="J" k="56" />
+<hkern u1="V" u2="O" k="19" />
+<hkern u1="V" u2="Q" k="19" />
+<hkern u1="V" u2="S" k="-10" />
+<hkern u1="V" u2="T" k="-20" />
+<hkern u1="V" u2="V" k="-20" />
+<hkern u1="V" u2="W" k="5" />
+<hkern u1="V" u2="X" k="-20" />
+<hkern u1="V" u2="Y" k="-30" />
+<hkern u1="V" u2="]" k="-30" />
+<hkern u1="V" u2="a" k="40" />
+<hkern u1="V" u2="b" k="10" />
+<hkern u1="V" u2="c" k="40" />
+<hkern u1="V" u2="d" k="40" />
+<hkern u1="V" u2="e" k="40" />
+<hkern u1="V" u2="f" k="10" />
+<hkern u1="V" u2="g" k="35" />
+<hkern u1="V" u2="h" k="10" />
+<hkern u1="V" u2="k" k="10" />
+<hkern u1="V" u2="l" k="10" />
+<hkern u1="V" u2="m" k="30" />
+<hkern u1="V" u2="n" k="30" />
+<hkern u1="V" u2="o" k="40" />
+<hkern u1="V" u2="p" k="30" />
+<hkern u1="V" u2="q" k="40" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="s" k="40" />
+<hkern u1="V" u2="t" k="10" />
+<hkern u1="V" u2="u" k="30" />
+<hkern u1="V" u2="v" k="10" />
+<hkern u1="V" u2="w" k="10" />
+<hkern u1="V" u2="x" k="25" />
+<hkern u1="V" u2="y" k="10" />
+<hkern u1="V" u2="}" k="-30" />
+<hkern u1="V" u2="&#xc4;" k="44" />
+<hkern u1="V" u2="&#xc5;" k="44" />
+<hkern u1="V" u2="&#xd6;" k="19" />
+<hkern u1="V" u2="&#xe7;" k="40" />
+<hkern u1="V" u2="&#xc6;" k="44" />
+<hkern u1="V" u2="&#xd8;" k="19" />
+<hkern u1="V" u2="&#xe6;" k="40" />
+<hkern u1="V" u2="&#xab;" k="50" />
+<hkern u1="V" u2="&#xbb;" k="20" />
+<hkern u1="V" u2="&#x2026;" k="60" />
+<hkern u1="V" u2="&#xc0;" k="44" />
+<hkern u1="V" u2="&#xc3;" k="44" />
+<hkern u1="V" u2="&#xd5;" k="19" />
+<hkern u1="V" u2="&#x152;" k="19" />
+<hkern u1="V" u2="&#x153;" k="40" />
+<hkern u1="V" u2="&#x2013;" k="60" />
+<hkern u1="V" u2="&#x2014;" k="60" />
+<hkern u1="V" u2="&#x178;" k="-30" />
+<hkern u1="V" u2="&#x2039;" k="50" />
+<hkern u1="V" u2="&#x203a;" k="20" />
+<hkern u1="V" u2="&#x201a;" k="60" />
+<hkern u1="V" u2="&#x201e;" k="60" />
+<hkern u1="V" u2="&#xc2;" k="44" />
+<hkern u1="V" u2="&#xc1;" k="44" />
+<hkern u1="V" u2="&#xd3;" k="19" />
+<hkern u1="V" u2="&#xd4;" k="19" />
+<hkern u1="V" u2="&#xd2;" k="19" />
+<hkern u1="V" u2="&#x131;" k="30" />
+<hkern u1="W" u2="&#x161;" k="50" />
+<hkern u1="W" u2="&amp;" k="30" />
+<hkern u1="W" u2=")" k="-10" />
+<hkern u1="W" u2="&#x2c;" k="20" />
+<hkern u1="W" u2="-" k="40" />
+<hkern u1="W" u2="." k="20" />
+<hkern u1="W" u2=":" k="20" />
+<hkern u1="W" u2=";" k="20" />
+<hkern u1="W" u2="@" k="20" />
+<hkern u1="W" u2="A" k="44" />
+<hkern u1="W" u2="C" k="33" />
+<hkern u1="W" u2="G" k="33" />
+<hkern u1="W" u2="J" k="46" />
+<hkern u1="W" u2="O" k="33" />
+<hkern u1="W" u2="Q" k="33" />
+<hkern u1="W" u2="V" k="5" />
+<hkern u1="W" u2="X" k="10" />
+<hkern u1="W" u2="]" k="-10" />
+<hkern u1="W" u2="a" k="45" />
+<hkern u1="W" u2="b" k="10" />
+<hkern u1="W" u2="c" k="45" />
+<hkern u1="W" u2="d" k="45" />
+<hkern u1="W" u2="e" k="45" />
+<hkern u1="W" u2="f" k="15" />
+<hkern u1="W" u2="g" k="50" />
+<hkern u1="W" u2="h" k="10" />
+<hkern u1="W" u2="i" k="20" />
+<hkern u1="W" u2="j" k="20" />
+<hkern u1="W" u2="k" k="10" />
+<hkern u1="W" u2="l" k="10" />
+<hkern u1="W" u2="m" k="35" />
+<hkern u1="W" u2="n" k="35" />
+<hkern u1="W" u2="o" k="45" />
+<hkern u1="W" u2="p" k="35" />
+<hkern u1="W" u2="q" k="45" />
+<hkern u1="W" u2="r" k="35" />
+<hkern u1="W" u2="s" k="50" />
+<hkern u1="W" u2="t" k="30" />
+<hkern u1="W" u2="u" k="35" />
+<hkern u1="W" u2="v" k="30" />
+<hkern u1="W" u2="w" k="30" />
+<hkern u1="W" u2="x" k="30" />
+<hkern u1="W" u2="y" k="30" />
+<hkern u1="W" u2="z" k="30" />
+<hkern u1="W" u2="}" k="-10" />
+<hkern u1="W" u2="&#xc4;" k="44" />
+<hkern u1="W" u2="&#xc5;" k="44" />
+<hkern u1="W" u2="&#xd6;" k="33" />
+<hkern u1="W" u2="&#xe7;" k="45" />
+<hkern u1="W" u2="&#xae;" k="20" />
+<hkern u1="W" u2="&#xa9;" k="20" />
+<hkern u1="W" u2="&#xc6;" k="44" />
+<hkern u1="W" u2="&#xd8;" k="33" />
+<hkern u1="W" u2="&#x3c0;" k="20" />
+<hkern u1="W" u2="&#xe6;" k="45" />
+<hkern u1="W" u2="&#xab;" k="40" />
+<hkern u1="W" u2="&#xbb;" k="20" />
+<hkern u1="W" u2="&#x2026;" k="20" />
+<hkern u1="W" u2="&#xc0;" k="44" />
+<hkern u1="W" u2="&#xc3;" k="44" />
+<hkern u1="W" u2="&#xd5;" k="33" />
+<hkern u1="W" u2="&#x152;" k="33" />
+<hkern u1="W" u2="&#x153;" k="45" />
+<hkern u1="W" u2="&#x2013;" k="40" />
+<hkern u1="W" u2="&#x2014;" k="40" />
+<hkern u1="W" u2="&#x2039;" k="40" />
+<hkern u1="W" u2="&#x203a;" k="20" />
+<hkern u1="W" u2="&#x201a;" k="20" />
+<hkern u1="W" u2="&#x201e;" k="20" />
+<hkern u1="W" u2="&#xc2;" k="44" />
+<hkern u1="W" u2="&#xc1;" k="44" />
+<hkern u1="W" u2="&#xd3;" k="33" />
+<hkern u1="W" u2="&#xd4;" k="33" />
+<hkern u1="W" u2="&#xd2;" k="33" />
+<hkern u1="W" u2="&#x131;" k="35" />
+<hkern u1="X" u2="&#x160;" k="-5" />
+<hkern u1="X" u2="&#xdd;" k="-20" />
+<hkern u1="X" u2="&amp;" k="20" />
+<hkern u1="X" u2=")" k="-40" />
+<hkern u1="X" u2="-" k="30" />
+<hkern u1="X" u2="A" k="-13" />
+<hkern u1="X" u2="C" k="25" />
+<hkern u1="X" u2="G" k="25" />
+<hkern u1="X" u2="O" k="25" />
+<hkern u1="X" u2="Q" k="25" />
+<hkern u1="X" u2="S" k="-5" />
+<hkern u1="X" u2="T" k="-10" />
+<hkern u1="X" u2="V" k="-20" />
+<hkern u1="X" u2="W" k="10" />
+<hkern u1="X" u2="X" k="-10" />
+<hkern u1="X" u2="Y" k="-20" />
+<hkern u1="X" u2="]" k="-40" />
+<hkern u1="X" u2="a" k="10" />
+<hkern u1="X" u2="c" k="10" />
+<hkern u1="X" u2="d" k="10" />
+<hkern u1="X" u2="e" k="10" />
+<hkern u1="X" u2="f" k="10" />
+<hkern u1="X" u2="m" k="10" />
+<hkern u1="X" u2="n" k="10" />
+<hkern u1="X" u2="o" k="10" />
+<hkern u1="X" u2="p" k="10" />
+<hkern u1="X" u2="q" k="10" />
+<hkern u1="X" u2="r" k="10" />
+<hkern u1="X" u2="t" k="10" />
+<hkern u1="X" u2="u" k="10" />
+<hkern u1="X" u2="v" k="20" />
+<hkern u1="X" u2="w" k="10" />
+<hkern u1="X" u2="y" k="20" />
+<hkern u1="X" u2="}" k="-40" />
+<hkern u1="X" u2="&#xc4;" k="-13" />
+<hkern u1="X" u2="&#xc5;" k="-13" />
+<hkern u1="X" u2="&#xd6;" k="25" />
+<hkern u1="X" u2="&#xe7;" k="10" />
+<hkern u1="X" u2="&#xc6;" k="-13" />
+<hkern u1="X" u2="&#xd8;" k="25" />
+<hkern u1="X" u2="&#xe6;" k="10" />
+<hkern u1="X" u2="&#xc0;" k="-13" />
+<hkern u1="X" u2="&#xc3;" k="-13" />
+<hkern u1="X" u2="&#xd5;" k="25" />
+<hkern u1="X" u2="&#x152;" k="25" />
+<hkern u1="X" u2="&#x153;" k="10" />
+<hkern u1="X" u2="&#x2013;" k="30" />
+<hkern u1="X" u2="&#x2014;" k="30" />
+<hkern u1="X" u2="&#x178;" k="-20" />
+<hkern u1="X" u2="&#xc2;" k="-13" />
+<hkern u1="X" u2="&#xc1;" k="-13" />
+<hkern u1="X" u2="&#xd3;" k="25" />
+<hkern u1="X" u2="&#xd4;" k="25" />
+<hkern u1="X" u2="&#xd2;" k="25" />
+<hkern u1="X" u2="&#x131;" k="10" />
+<hkern u1="Y" u2="&#x160;" k="-10" />
+<hkern u1="Y" u2="&#x161;" k="50" />
+<hkern u1="Y" u2="&amp;" k="30" />
+<hkern u1="Y" u2=")" k="-50" />
+<hkern u1="Y" u2="&#x2c;" k="50" />
+<hkern u1="Y" u2="-" k="50" />
+<hkern u1="Y" u2="." k="50" />
+<hkern u1="Y" u2=":" k="30" />
+<hkern u1="Y" u2=";" k="30" />
+<hkern u1="Y" u2="@" k="30" />
+<hkern u1="Y" u2="A" k="38" />
+<hkern u1="Y" u2="C" k="25" />
+<hkern u1="Y" u2="G" k="25" />
+<hkern u1="Y" u2="J" k="73" />
+<hkern u1="Y" u2="O" k="25" />
+<hkern u1="Y" u2="Q" k="25" />
+<hkern u1="Y" u2="S" k="-10" />
+<hkern u1="Y" u2="T" k="-20" />
+<hkern u1="Y" u2="V" k="-30" />
+<hkern u1="Y" u2="X" k="-20" />
+<hkern u1="Y" u2="]" k="-50" />
+<hkern u1="Y" u2="a" k="55" />
+<hkern u1="Y" u2="c" k="55" />
+<hkern u1="Y" u2="d" k="55" />
+<hkern u1="Y" u2="e" k="55" />
+<hkern u1="Y" u2="f" k="10" />
+<hkern u1="Y" u2="g" k="40" />
+<hkern u1="Y" u2="m" k="40" />
+<hkern u1="Y" u2="n" k="40" />
+<hkern u1="Y" u2="o" k="55" />
+<hkern u1="Y" u2="p" k="40" />
+<hkern u1="Y" u2="q" k="55" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="s" k="50" />
+<hkern u1="Y" u2="t" k="20" />
+<hkern u1="Y" u2="u" k="40" />
+<hkern u1="Y" u2="v" k="20" />
+<hkern u1="Y" u2="w" k="10" />
+<hkern u1="Y" u2="x" k="20" />
+<hkern u1="Y" u2="y" k="20" />
+<hkern u1="Y" u2="z" k="30" />
+<hkern u1="Y" u2="}" k="-50" />
+<hkern u1="Y" u2="&#xc4;" k="38" />
+<hkern u1="Y" u2="&#xc5;" k="38" />
+<hkern u1="Y" u2="&#xd6;" k="25" />
+<hkern u1="Y" u2="&#xe7;" k="55" />
+<hkern u1="Y" u2="&#xae;" k="30" />
+<hkern u1="Y" u2="&#xa9;" k="30" />
+<hkern u1="Y" u2="&#xc6;" k="38" />
+<hkern u1="Y" u2="&#xd8;" k="25" />
+<hkern u1="Y" u2="&#x3c0;" k="30" />
+<hkern u1="Y" u2="&#xe6;" k="55" />
+<hkern u1="Y" u2="&#xab;" k="60" />
+<hkern u1="Y" u2="&#xbb;" k="40" />
+<hkern u1="Y" u2="&#x2026;" k="50" />
+<hkern u1="Y" u2="&#xc0;" k="38" />
+<hkern u1="Y" u2="&#xc3;" k="38" />
+<hkern u1="Y" u2="&#xd5;" k="25" />
+<hkern u1="Y" u2="&#x152;" k="25" />
+<hkern u1="Y" u2="&#x153;" k="55" />
+<hkern u1="Y" u2="&#x2013;" k="50" />
+<hkern u1="Y" u2="&#x2014;" k="50" />
+<hkern u1="Y" u2="&#x2039;" k="60" />
+<hkern u1="Y" u2="&#x203a;" k="40" />
+<hkern u1="Y" u2="&#x201a;" k="50" />
+<hkern u1="Y" u2="&#x201e;" k="50" />
+<hkern u1="Y" u2="&#xc2;" k="38" />
+<hkern u1="Y" u2="&#xc1;" k="38" />
+<hkern u1="Y" u2="&#xd3;" k="25" />
+<hkern u1="Y" u2="&#xd4;" k="25" />
+<hkern u1="Y" u2="&#xd2;" k="25" />
+<hkern u1="Y" u2="&#x131;" k="40" />
+<hkern u1="Z" u2="&#xf0;" k="30" />
+<hkern u1="Z" u2="-" k="50" />
+<hkern u1="Z" u2="A" k="7" />
+<hkern u1="Z" u2="C" k="45" />
+<hkern u1="Z" u2="G" k="45" />
+<hkern u1="Z" u2="J" k="10" />
+<hkern u1="Z" u2="O" k="45" />
+<hkern u1="Z" u2="Q" k="45" />
+<hkern u1="Z" u2="T" k="10" />
+<hkern u1="Z" u2="a" k="33" />
+<hkern u1="Z" u2="b" k="10" />
+<hkern u1="Z" u2="c" k="40" />
+<hkern u1="Z" u2="d" k="40" />
+<hkern u1="Z" u2="e" k="40" />
+<hkern u1="Z" u2="f" k="20" />
+<hkern u1="Z" u2="g" k="20" />
+<hkern u1="Z" u2="h" k="10" />
+<hkern u1="Z" u2="i" k="20" />
+<hkern u1="Z" u2="j" k="15" />
+<hkern u1="Z" u2="k" k="10" />
+<hkern u1="Z" u2="l" k="10" />
+<hkern u1="Z" u2="m" k="20" />
+<hkern u1="Z" u2="n" k="20" />
+<hkern u1="Z" u2="o" k="40" />
+<hkern u1="Z" u2="p" k="20" />
+<hkern u1="Z" u2="q" k="40" />
+<hkern u1="Z" u2="r" k="20" />
+<hkern u1="Z" u2="u" k="20" />
+<hkern u1="Z" u2="v" k="30" />
+<hkern u1="Z" u2="w" k="10" />
+<hkern u1="Z" u2="y" k="30" />
+<hkern u1="Z" u2="&#xc4;" k="7" />
+<hkern u1="Z" u2="&#xc5;" k="7" />
+<hkern u1="Z" u2="&#xd6;" k="45" />
+<hkern u1="Z" u2="&#xe7;" k="40" />
+<hkern u1="Z" u2="&#xc6;" k="7" />
+<hkern u1="Z" u2="&#xd8;" k="45" />
+<hkern u1="Z" u2="&#xe6;" k="33" />
+<hkern u1="Z" u2="&#xc0;" k="7" />
+<hkern u1="Z" u2="&#xc3;" k="7" />
+<hkern u1="Z" u2="&#xd5;" k="45" />
+<hkern u1="Z" u2="&#x152;" k="45" />
+<hkern u1="Z" u2="&#x153;" k="40" />
+<hkern u1="Z" u2="&#x2013;" k="50" />
+<hkern u1="Z" u2="&#x2014;" k="50" />
+<hkern u1="Z" u2="&#xc2;" k="7" />
+<hkern u1="Z" u2="&#xc1;" k="7" />
+<hkern u1="Z" u2="&#xd3;" k="45" />
+<hkern u1="Z" u2="&#xd4;" k="45" />
+<hkern u1="Z" u2="&#xd2;" k="45" />
+<hkern u1="Z" u2="&#x131;" k="20" />
+<hkern u1="[" u2="&#xdd;" k="-50" />
+<hkern u1="[" u2="V" k="-30" />
+<hkern u1="[" u2="W" k="-10" />
+<hkern u1="[" u2="X" k="-40" />
+<hkern u1="[" u2="Y" k="-50" />
+<hkern u1="[" u2="g" k="-20" />
+<hkern u1="[" u2="j" k="-150" />
+<hkern u1="[" u2="&#x178;" k="-50" />
+<hkern u1="\" u2="&#xdd;" k="60" />
+<hkern u1="\" u2="T" k="90" />
+<hkern u1="\" u2="V" k="70" />
+<hkern u1="\" u2="W" k="50" />
+<hkern u1="\" u2="Y" k="60" />
+<hkern u1="\" u2="a" k="10" />
+<hkern u1="\" u2="c" k="10" />
+<hkern u1="\" u2="d" k="10" />
+<hkern u1="\" u2="e" k="10" />
+<hkern u1="\" u2="j" k="-130" />
+<hkern u1="\" u2="o" k="10" />
+<hkern u1="\" u2="q" k="10" />
+<hkern u1="\" u2="&#xe7;" k="10" />
+<hkern u1="\" u2="&#xe6;" k="10" />
+<hkern u1="\" u2="&#x153;" k="10" />
+<hkern u1="\" u2="&#x178;" k="60" />
+<hkern u1="a" u2="?" k="10" />
+<hkern u1="a" u2="\" k="10" />
+<hkern u1="a" u2="t" k="10" />
+<hkern u1="a" u2="v" k="5" />
+<hkern u1="a" u2="y" k="5" />
+<hkern u1="a" u2="&#x2122;" k="20" />
+<hkern u1="a" u2="&#x201c;" k="30" />
+<hkern u1="a" u2="&#x2018;" k="30" />
+<hkern u1="b" u2="&#x161;" k="2" />
+<hkern u1="b" u2="&#x2c;" k="20" />
+<hkern u1="b" u2="." k="20" />
+<hkern u1="b" u2="/" k="10" />
+<hkern u1="b" u2=":" k="10" />
+<hkern u1="b" u2=";" k="10" />
+<hkern u1="b" u2="?" k="20" />
+<hkern u1="b" u2="\" k="10" />
+<hkern u1="b" u2="f" k="2" />
+<hkern u1="b" u2="g" k="-2" />
+<hkern u1="b" u2="s" k="2" />
+<hkern u1="b" u2="v" k="10" />
+<hkern u1="b" u2="w" k="3" />
+<hkern u1="b" u2="x" k="15" />
+<hkern u1="b" u2="y" k="10" />
+<hkern u1="b" u2="z" k="15" />
+<hkern u1="b" u2="&#x2122;" k="30" />
+<hkern u1="b" u2="&#x2026;" k="20" />
+<hkern u1="b" u2="&#x201c;" k="40" />
+<hkern u1="b" u2="&#x2018;" k="40" />
+<hkern u1="b" u2="&#x201a;" k="20" />
+<hkern u1="b" u2="&#x201e;" k="20" />
+<hkern u1="e" u2="v" k="10" />
+<hkern u1="e" u2="x" k="15" />
+<hkern u1="e" u2="y" k="10" />
+<hkern u1="f" u2="&#xf0;" k="20" />
+<hkern u1="f" u2="&amp;" k="30" />
+<hkern u1="f" u2=")" k="-70" />
+<hkern u1="f" u2="*" k="-20" />
+<hkern u1="f" u2="&#x2c;" k="80" />
+<hkern u1="f" u2="-" k="30" />
+<hkern u1="f" u2="." k="80" />
+<hkern u1="f" u2="/" k="50" />
+<hkern u1="f" u2=":" k="10" />
+<hkern u1="f" u2=";" k="10" />
+<hkern u1="f" u2="?" k="-40" />
+<hkern u1="f" u2="\" k="-40" />
+<hkern u1="f" u2="]" k="-70" />
+<hkern u1="f" u2="a" k="20" />
+<hkern u1="f" u2="c" k="20" />
+<hkern u1="f" u2="d" k="20" />
+<hkern u1="f" u2="e" k="20" />
+<hkern u1="f" u2="g" k="5" />
+<hkern u1="f" u2="m" k="5" />
+<hkern u1="f" u2="n" k="5" />
+<hkern u1="f" u2="o" k="20" />
+<hkern u1="f" u2="p" k="5" />
+<hkern u1="f" u2="q" k="20" />
+<hkern u1="f" u2="r" k="5" />
+<hkern u1="f" u2="u" k="5" />
+<hkern u1="f" u2="v" k="-3" />
+<hkern u1="f" u2="y" k="-3" />
+<hkern u1="f" u2="}" k="-70" />
+<hkern u1="f" u2="&#xe7;" k="20" />
+<hkern u1="f" u2="&#x2122;" k="-46" />
+<hkern u1="f" u2="&#xe6;" k="20" />
+<hkern u1="f" u2="&#xab;" k="50" />
+<hkern u1="f" u2="&#xbb;" k="20" />
+<hkern u1="f" u2="&#x2026;" k="80" />
+<hkern u1="f" u2="&#x153;" k="20" />
+<hkern u1="f" u2="&#x2013;" k="30" />
+<hkern u1="f" u2="&#x2014;" k="30" />
+<hkern u1="f" u2="&#x201c;" k="-30" />
+<hkern u1="f" u2="&#x201d;" k="-50" />
+<hkern u1="f" u2="&#x2018;" k="-30" />
+<hkern u1="f" u2="&#x2019;" k="-50" />
+<hkern u1="f" u2="&#x2039;" k="50" />
+<hkern u1="f" u2="&#x203a;" k="20" />
+<hkern u1="f" u2="&#x201a;" k="80" />
+<hkern u1="f" u2="&#x201e;" k="80" />
+<hkern u1="f" u2="&#x131;" k="5" />
+<hkern u1="h" u2="?" k="10" />
+<hkern u1="h" u2="\" k="10" />
+<hkern u1="h" u2="t" k="10" />
+<hkern u1="h" u2="v" k="5" />
+<hkern u1="h" u2="y" k="5" />
+<hkern u1="h" u2="&#x2122;" k="20" />
+<hkern u1="h" u2="&#x201c;" k="30" />
+<hkern u1="h" u2="&#x2018;" k="30" />
+<hkern u1="k" u2="a" k="12" />
+<hkern u1="k" u2="c" k="12" />
+<hkern u1="k" u2="d" k="12" />
+<hkern u1="k" u2="e" k="12" />
+<hkern u1="k" u2="g" k="14" />
+<hkern u1="k" u2="o" k="12" />
+<hkern u1="k" u2="q" k="12" />
+<hkern u1="k" u2="v" k="-5" />
+<hkern u1="k" u2="w" k="-2" />
+<hkern u1="k" u2="y" k="-5" />
+<hkern u1="k" u2="&#xe7;" k="12" />
+<hkern u1="k" u2="&#xe6;" k="12" />
+<hkern u1="k" u2="&#x153;" k="12" />
+<hkern u1="m" u2="?" k="10" />
+<hkern u1="m" u2="\" k="10" />
+<hkern u1="m" u2="t" k="10" />
+<hkern u1="m" u2="v" k="5" />
+<hkern u1="m" u2="y" k="5" />
+<hkern u1="m" u2="&#x2122;" k="20" />
+<hkern u1="m" u2="&#x201c;" k="30" />
+<hkern u1="m" u2="&#x2018;" k="30" />
+<hkern u1="n" u2="?" k="10" />
+<hkern u1="n" u2="\" k="10" />
+<hkern u1="n" u2="t" k="10" />
+<hkern u1="n" u2="v" k="5" />
+<hkern u1="n" u2="y" k="5" />
+<hkern u1="n" u2="&#x2122;" k="20" />
+<hkern u1="n" u2="&#x201c;" k="30" />
+<hkern u1="n" u2="&#x2018;" k="30" />
+<hkern u1="o" u2="&#x161;" k="2" />
+<hkern u1="o" u2="&#x2c;" k="20" />
+<hkern u1="o" u2="." k="20" />
+<hkern u1="o" u2="/" k="10" />
+<hkern u1="o" u2=":" k="10" />
+<hkern u1="o" u2=";" k="10" />
+<hkern u1="o" u2="?" k="20" />
+<hkern u1="o" u2="\" k="10" />
+<hkern u1="o" u2="f" k="2" />
+<hkern u1="o" u2="g" k="-2" />
+<hkern u1="o" u2="s" k="2" />
+<hkern u1="o" u2="v" k="10" />
+<hkern u1="o" u2="w" k="3" />
+<hkern u1="o" u2="x" k="15" />
+<hkern u1="o" u2="y" k="10" />
+<hkern u1="o" u2="z" k="15" />
+<hkern u1="o" u2="&#x2122;" k="30" />
+<hkern u1="o" u2="&#x2026;" k="20" />
+<hkern u1="o" u2="&#x201c;" k="40" />
+<hkern u1="o" u2="&#x2018;" k="40" />
+<hkern u1="o" u2="&#x201a;" k="20" />
+<hkern u1="o" u2="&#x201e;" k="20" />
+<hkern u1="p" u2="&#x161;" k="2" />
+<hkern u1="p" u2="&#x2c;" k="20" />
+<hkern u1="p" u2="." k="20" />
+<hkern u1="p" u2="/" k="10" />
+<hkern u1="p" u2=":" k="10" />
+<hkern u1="p" u2=";" k="10" />
+<hkern u1="p" u2="?" k="20" />
+<hkern u1="p" u2="\" k="10" />
+<hkern u1="p" u2="f" k="2" />
+<hkern u1="p" u2="g" k="-2" />
+<hkern u1="p" u2="s" k="2" />
+<hkern u1="p" u2="v" k="10" />
+<hkern u1="p" u2="w" k="3" />
+<hkern u1="p" u2="x" k="15" />
+<hkern u1="p" u2="y" k="10" />
+<hkern u1="p" u2="z" k="15" />
+<hkern u1="p" u2="&#x2122;" k="30" />
+<hkern u1="p" u2="&#x2026;" k="20" />
+<hkern u1="p" u2="&#x201c;" k="40" />
+<hkern u1="p" u2="&#x2018;" k="40" />
+<hkern u1="p" u2="&#x201a;" k="20" />
+<hkern u1="p" u2="&#x201e;" k="20" />
+<hkern u1="r" u2="&#x2c;" k="50" />
+<hkern u1="r" u2="-" k="20" />
+<hkern u1="r" u2="." k="50" />
+<hkern u1="r" u2="/" k="60" />
+<hkern u1="r" u2="?" k="-20" />
+<hkern u1="r" u2="a" k="8" />
+<hkern u1="r" u2="c" k="8" />
+<hkern u1="r" u2="d" k="8" />
+<hkern u1="r" u2="e" k="8" />
+<hkern u1="r" u2="f" k="-15" />
+<hkern u1="r" u2="g" k="10" />
+<hkern u1="r" u2="o" k="8" />
+<hkern u1="r" u2="q" k="8" />
+<hkern u1="r" u2="t" k="-20" />
+<hkern u1="r" u2="&#xe7;" k="8" />
+<hkern u1="r" u2="&#xe6;" k="8" />
+<hkern u1="r" u2="&#x2026;" k="50" />
+<hkern u1="r" u2="&#x153;" k="8" />
+<hkern u1="r" u2="&#x2013;" k="20" />
+<hkern u1="r" u2="&#x2014;" k="20" />
+<hkern u1="r" u2="&#x201c;" k="-20" />
+<hkern u1="r" u2="&#x201d;" k="-40" />
+<hkern u1="r" u2="&#x2018;" k="-20" />
+<hkern u1="r" u2="&#x2019;" k="-40" />
+<hkern u1="r" u2="&#x201a;" k="50" />
+<hkern u1="r" u2="&#x201e;" k="50" />
+<hkern u1="s" u2="a" k="-2" />
+<hkern u1="s" u2="c" k="-2" />
+<hkern u1="s" u2="d" k="-2" />
+<hkern u1="s" u2="e" k="-2" />
+<hkern u1="s" u2="o" k="-2" />
+<hkern u1="s" u2="q" k="-2" />
+<hkern u1="s" u2="&#xe7;" k="-2" />
+<hkern u1="s" u2="&#xe6;" k="-2" />
+<hkern u1="s" u2="&#x153;" k="-2" />
+<hkern u1="t" u2="&#x2c;" k="-10" />
+<hkern u1="t" u2="." k="-10" />
+<hkern u1="t" u2="a" k="8" />
+<hkern u1="t" u2="c" k="8" />
+<hkern u1="t" u2="d" k="8" />
+<hkern u1="t" u2="e" k="8" />
+<hkern u1="t" u2="o" k="8" />
+<hkern u1="t" u2="q" k="8" />
+<hkern u1="t" u2="z" k="-2" />
+<hkern u1="t" u2="&#xe7;" k="8" />
+<hkern u1="t" u2="&#xe6;" k="8" />
+<hkern u1="t" u2="&#x2026;" k="-10" />
+<hkern u1="t" u2="&#x153;" k="8" />
+<hkern u1="t" u2="&#x201c;" k="-10" />
+<hkern u1="t" u2="&#x2018;" k="-10" />
+<hkern u1="t" u2="&#x201a;" k="-10" />
+<hkern u1="t" u2="&#x201e;" k="-10" />
+<hkern u1="v" u2="&#x2c;" k="50" />
+<hkern u1="v" u2="-" k="10" />
+<hkern u1="v" u2="." k="50" />
+<hkern u1="v" u2="a" k="10" />
+<hkern u1="v" u2="c" k="10" />
+<hkern u1="v" u2="d" k="10" />
+<hkern u1="v" u2="e" k="10" />
+<hkern u1="v" u2="f" k="-3" />
+<hkern u1="v" u2="g" k="-2" />
+<hkern u1="v" u2="o" k="10" />
+<hkern u1="v" u2="q" k="10" />
+<hkern u1="v" u2="t" k="-3" />
+<hkern u1="v" u2="&#xe7;" k="10" />
+<hkern u1="v" u2="&#xe6;" k="10" />
+<hkern u1="v" u2="&#x2026;" k="50" />
+<hkern u1="v" u2="&#x153;" k="10" />
+<hkern u1="v" u2="&#x2013;" k="10" />
+<hkern u1="v" u2="&#x2014;" k="10" />
+<hkern u1="v" u2="&#x201c;" k="-30" />
+<hkern u1="v" u2="&#x201d;" k="-30" />
+<hkern u1="v" u2="&#x2018;" k="-30" />
+<hkern u1="v" u2="&#x2019;" k="-30" />
+<hkern u1="v" u2="&#x201a;" k="50" />
+<hkern u1="v" u2="&#x201e;" k="50" />
+<hkern u1="w" u2="&#x2c;" k="30" />
+<hkern u1="w" u2="." k="30" />
+<hkern u1="w" u2="a" k="3" />
+<hkern u1="w" u2="c" k="3" />
+<hkern u1="w" u2="d" k="3" />
+<hkern u1="w" u2="e" k="3" />
+<hkern u1="w" u2="o" k="3" />
+<hkern u1="w" u2="q" k="3" />
+<hkern u1="w" u2="t" k="-2" />
+<hkern u1="w" u2="&#xe7;" k="3" />
+<hkern u1="w" u2="&#xe6;" k="3" />
+<hkern u1="w" u2="&#x2026;" k="30" />
+<hkern u1="w" u2="&#x153;" k="3" />
+<hkern u1="w" u2="&#x201c;" k="-20" />
+<hkern u1="w" u2="&#x201d;" k="-20" />
+<hkern u1="w" u2="&#x2018;" k="-20" />
+<hkern u1="w" u2="&#x2019;" k="-20" />
+<hkern u1="w" u2="&#x201a;" k="30" />
+<hkern u1="w" u2="&#x201e;" k="30" />
+<hkern u1="x" u2="-" k="30" />
+<hkern u1="x" u2="a" k="15" />
+<hkern u1="x" u2="c" k="15" />
+<hkern u1="x" u2="d" k="15" />
+<hkern u1="x" u2="e" k="15" />
+<hkern u1="x" u2="g" k="-3" />
+<hkern u1="x" u2="o" k="15" />
+<hkern u1="x" u2="q" k="15" />
+<hkern u1="x" u2="&#xe7;" k="15" />
+<hkern u1="x" u2="&#xe6;" k="15" />
+<hkern u1="x" u2="&#xab;" k="50" />
+<hkern u1="x" u2="&#x153;" k="15" />
+<hkern u1="x" u2="&#x2013;" k="30" />
+<hkern u1="x" u2="&#x2014;" k="30" />
+<hkern u1="x" u2="&#x201c;" k="-10" />
+<hkern u1="x" u2="&#x201d;" k="-20" />
+<hkern u1="x" u2="&#x2018;" k="-10" />
+<hkern u1="x" u2="&#x2019;" k="-20" />
+<hkern u1="x" u2="&#x2039;" k="50" />
+<hkern u1="y" u2="&#x2c;" k="50" />
+<hkern u1="y" u2="-" k="10" />
+<hkern u1="y" u2="." k="50" />
+<hkern u1="y" u2="a" k="10" />
+<hkern u1="y" u2="c" k="10" />
+<hkern u1="y" u2="d" k="10" />
+<hkern u1="y" u2="e" k="10" />
+<hkern u1="y" u2="f" k="-3" />
+<hkern u1="y" u2="g" k="-2" />
+<hkern u1="y" u2="o" k="10" />
+<hkern u1="y" u2="q" k="10" />
+<hkern u1="y" u2="t" k="-3" />
+<hkern u1="y" u2="&#xe7;" k="10" />
+<hkern u1="y" u2="&#xe6;" k="10" />
+<hkern u1="y" u2="&#x2026;" k="50" />
+<hkern u1="y" u2="&#x153;" k="10" />
+<hkern u1="y" u2="&#x2013;" k="10" />
+<hkern u1="y" u2="&#x2014;" k="10" />
+<hkern u1="y" u2="&#x201c;" k="-30" />
+<hkern u1="y" u2="&#x201d;" k="-30" />
+<hkern u1="y" u2="&#x2018;" k="-30" />
+<hkern u1="y" u2="&#x2019;" k="-30" />
+<hkern u1="y" u2="&#x201a;" k="50" />
+<hkern u1="y" u2="&#x201e;" k="50" />
+<hkern u1="z" u2="-" k="30" />
+<hkern u1="z" u2="a" k="20" />
+<hkern u1="z" u2="c" k="20" />
+<hkern u1="z" u2="d" k="20" />
+<hkern u1="z" u2="e" k="20" />
+<hkern u1="z" u2="g" k="9" />
+<hkern u1="z" u2="o" k="20" />
+<hkern u1="z" u2="q" k="20" />
+<hkern u1="z" u2="t" k="-2" />
+<hkern u1="z" u2="&#xe7;" k="20" />
+<hkern u1="z" u2="&#xe6;" k="20" />
+<hkern u1="z" u2="&#xab;" k="60" />
+<hkern u1="z" u2="&#x153;" k="20" />
+<hkern u1="z" u2="&#x2013;" k="30" />
+<hkern u1="z" u2="&#x2014;" k="30" />
+<hkern u1="z" u2="&#x2039;" k="60" />
+<hkern u1="{" u2="&#xdd;" k="-50" />
+<hkern u1="{" u2="V" k="-30" />
+<hkern u1="{" u2="W" k="-10" />
+<hkern u1="{" u2="X" k="-40" />
+<hkern u1="{" u2="Y" k="-50" />
+<hkern u1="{" u2="g" k="-20" />
+<hkern u1="{" u2="j" k="-150" />
+<hkern u1="{" u2="&#x178;" k="-50" />
+<hkern u1="&#xc4;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc4;" u2="&#xdd;" k="35" />
+<hkern u1="&#xc4;" u2="*" k="42" />
+<hkern u1="&#xc4;" u2="&#x2c;" k="-21" />
+<hkern u1="&#xc4;" u2="-" k="-7" />
+<hkern u1="&#xc4;" u2="." k="-21" />
+<hkern u1="&#xc4;" u2="@" k="7" />
+<hkern u1="&#xc4;" u2="A" k="-7" />
+<hkern u1="&#xc4;" u2="C" k="14" />
+<hkern u1="&#xc4;" u2="G" k="14" />
+<hkern u1="&#xc4;" u2="J" k="-7" />
+<hkern u1="&#xc4;" u2="O" k="14" />
+<hkern u1="&#xc4;" u2="Q" k="14" />
+<hkern u1="&#xc4;" u2="S" k="-4" />
+<hkern u1="&#xc4;" u2="T" k="49" />
+<hkern u1="&#xc4;" u2="V" k="35" />
+<hkern u1="&#xc4;" u2="W" k="35" />
+<hkern u1="&#xc4;" u2="X" k="-7" />
+<hkern u1="&#xc4;" u2="Y" k="35" />
+<hkern u1="&#xc4;" u2="a" k="4" />
+<hkern u1="&#xc4;" u2="c" k="4" />
+<hkern u1="&#xc4;" u2="d" k="4" />
+<hkern u1="&#xc4;" u2="e" k="4" />
+<hkern u1="&#xc4;" u2="m" k="4" />
+<hkern u1="&#xc4;" u2="n" k="4" />
+<hkern u1="&#xc4;" u2="o" k="4" />
+<hkern u1="&#xc4;" u2="p" k="4" />
+<hkern u1="&#xc4;" u2="q" k="4" />
+<hkern u1="&#xc4;" u2="r" k="4" />
+<hkern u1="&#xc4;" u2="t" k="4" />
+<hkern u1="&#xc4;" u2="u" k="4" />
+<hkern u1="&#xc4;" u2="v" k="21" />
+<hkern u1="&#xc4;" u2="w" k="7" />
+<hkern u1="&#xc4;" u2="x" k="-14" />
+<hkern u1="&#xc4;" u2="y" k="21" />
+<hkern u1="&#xc4;" u2="z" k="-7" />
+<hkern u1="&#xc4;" u2="&#xc4;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="14" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc4;" u2="&#xae;" k="7" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="7" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="49" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="14" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="7" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-21" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="14" />
+<hkern u1="&#xc4;" u2="&#x152;" k="14" />
+<hkern u1="&#xc4;" u2="&#x153;" k="4" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-7" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-7" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="42" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="21" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="42" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="21" />
+<hkern u1="&#xc4;" u2="&#x178;" k="35" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-21" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-21" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-7" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="14" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="14" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="14" />
+<hkern u1="&#xc4;" u2="&#x131;" k="4" />
+<hkern u1="&#xc5;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc5;" u2="&#xdd;" k="35" />
+<hkern u1="&#xc5;" u2="*" k="42" />
+<hkern u1="&#xc5;" u2="&#x2c;" k="-21" />
+<hkern u1="&#xc5;" u2="-" k="-7" />
+<hkern u1="&#xc5;" u2="." k="-21" />
+<hkern u1="&#xc5;" u2="@" k="7" />
+<hkern u1="&#xc5;" u2="A" k="-7" />
+<hkern u1="&#xc5;" u2="C" k="14" />
+<hkern u1="&#xc5;" u2="G" k="14" />
+<hkern u1="&#xc5;" u2="J" k="-7" />
+<hkern u1="&#xc5;" u2="O" k="14" />
+<hkern u1="&#xc5;" u2="Q" k="14" />
+<hkern u1="&#xc5;" u2="S" k="-4" />
+<hkern u1="&#xc5;" u2="T" k="49" />
+<hkern u1="&#xc5;" u2="V" k="35" />
+<hkern u1="&#xc5;" u2="W" k="35" />
+<hkern u1="&#xc5;" u2="X" k="-7" />
+<hkern u1="&#xc5;" u2="Y" k="35" />
+<hkern u1="&#xc5;" u2="a" k="4" />
+<hkern u1="&#xc5;" u2="c" k="4" />
+<hkern u1="&#xc5;" u2="d" k="4" />
+<hkern u1="&#xc5;" u2="e" k="4" />
+<hkern u1="&#xc5;" u2="m" k="4" />
+<hkern u1="&#xc5;" u2="n" k="4" />
+<hkern u1="&#xc5;" u2="o" k="4" />
+<hkern u1="&#xc5;" u2="p" k="4" />
+<hkern u1="&#xc5;" u2="q" k="4" />
+<hkern u1="&#xc5;" u2="r" k="4" />
+<hkern u1="&#xc5;" u2="t" k="4" />
+<hkern u1="&#xc5;" u2="u" k="4" />
+<hkern u1="&#xc5;" u2="v" k="21" />
+<hkern u1="&#xc5;" u2="w" k="7" />
+<hkern u1="&#xc5;" u2="x" k="-14" />
+<hkern u1="&#xc5;" u2="y" k="21" />
+<hkern u1="&#xc5;" u2="z" k="-7" />
+<hkern u1="&#xc5;" u2="&#xc4;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xc5;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="14" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc5;" u2="&#xae;" k="7" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="7" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="49" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="14" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="7" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-21" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="14" />
+<hkern u1="&#xc5;" u2="&#x152;" k="14" />
+<hkern u1="&#xc5;" u2="&#x153;" k="4" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-7" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-7" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="42" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="21" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="42" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="21" />
+<hkern u1="&#xc5;" u2="&#x178;" k="35" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-21" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-21" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-7" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="14" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="14" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="14" />
+<hkern u1="&#xc5;" u2="&#x131;" k="4" />
+<hkern u1="&#xc9;" u2="@" k="14" />
+<hkern u1="&#xc9;" u2="T" k="-11" />
+<hkern u1="&#xc9;" u2="a" k="18" />
+<hkern u1="&#xc9;" u2="c" k="18" />
+<hkern u1="&#xc9;" u2="d" k="18" />
+<hkern u1="&#xc9;" u2="e" k="18" />
+<hkern u1="&#xc9;" u2="f" k="7" />
+<hkern u1="&#xc9;" u2="g" k="4" />
+<hkern u1="&#xc9;" u2="o" k="18" />
+<hkern u1="&#xc9;" u2="q" k="18" />
+<hkern u1="&#xc9;" u2="v" k="14" />
+<hkern u1="&#xc9;" u2="y" k="14" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="18" />
+<hkern u1="&#xc9;" u2="&#xae;" k="14" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="14" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="14" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="18" />
+<hkern u1="&#xc9;" u2="&#x153;" k="18" />
+<hkern u1="&#xd1;" u2="/" k="14" />
+<hkern u1="&#xd1;" u2="v" k="7" />
+<hkern u1="&#xd1;" u2="y" k="7" />
+<hkern u1="&#xd6;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd6;" u2="&#x17d;" k="31" />
+<hkern u1="&#xd6;" u2="&#x2c;" k="28" />
+<hkern u1="&#xd6;" u2="." k="28" />
+<hkern u1="&#xd6;" u2="/" k="35" />
+<hkern u1="&#xd6;" u2="?" k="14" />
+<hkern u1="&#xd6;" u2="A" k="14" />
+<hkern u1="&#xd6;" u2="J" k="31" />
+<hkern u1="&#xd6;" u2="T" k="21" />
+<hkern u1="&#xd6;" u2="V" k="14" />
+<hkern u1="&#xd6;" u2="W" k="28" />
+<hkern u1="&#xd6;" u2="X" k="18" />
+<hkern u1="&#xd6;" u2="Y" k="18" />
+<hkern u1="&#xd6;" u2="Z" k="31" />
+<hkern u1="&#xd6;" u2="a" k="4" />
+<hkern u1="&#xd6;" u2="b" k="7" />
+<hkern u1="&#xd6;" u2="c" k="4" />
+<hkern u1="&#xd6;" u2="d" k="4" />
+<hkern u1="&#xd6;" u2="e" k="4" />
+<hkern u1="&#xd6;" u2="h" k="7" />
+<hkern u1="&#xd6;" u2="k" k="7" />
+<hkern u1="&#xd6;" u2="l" k="7" />
+<hkern u1="&#xd6;" u2="m" k="4" />
+<hkern u1="&#xd6;" u2="n" k="4" />
+<hkern u1="&#xd6;" u2="o" k="4" />
+<hkern u1="&#xd6;" u2="p" k="4" />
+<hkern u1="&#xd6;" u2="q" k="4" />
+<hkern u1="&#xd6;" u2="r" k="4" />
+<hkern u1="&#xd6;" u2="u" k="4" />
+<hkern u1="&#xd6;" u2="x" k="7" />
+<hkern u1="&#xd6;" u2="z" k="7" />
+<hkern u1="&#xd6;" u2="&#xc4;" k="14" />
+<hkern u1="&#xd6;" u2="&#xc5;" k="14" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd6;" u2="&#xc6;" k="14" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="28" />
+<hkern u1="&#xd6;" u2="&#xc0;" k="14" />
+<hkern u1="&#xd6;" u2="&#xc3;" k="14" />
+<hkern u1="&#xd6;" u2="&#x153;" k="4" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="14" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="14" />
+<hkern u1="&#xd6;" u2="&#x178;" k="18" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="28" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="28" />
+<hkern u1="&#xd6;" u2="&#xc2;" k="14" />
+<hkern u1="&#xd6;" u2="&#xc1;" k="14" />
+<hkern u1="&#xd6;" u2="&#x131;" k="4" />
+<hkern u1="&#xdc;" u2="&#x2c;" k="7" />
+<hkern u1="&#xdc;" u2="." k="7" />
+<hkern u1="&#xdc;" u2="J" k="7" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="7" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="7" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="7" />
+<hkern u1="&#xe1;" u2="?" k="7" />
+<hkern u1="&#xe1;" u2="\" k="7" />
+<hkern u1="&#xe1;" u2="t" k="7" />
+<hkern u1="&#xe1;" u2="v" k="4" />
+<hkern u1="&#xe1;" u2="y" k="4" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="14" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="21" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="21" />
+<hkern u1="&#xe0;" u2="?" k="7" />
+<hkern u1="&#xe0;" u2="\" k="7" />
+<hkern u1="&#xe0;" u2="t" k="7" />
+<hkern u1="&#xe0;" u2="v" k="4" />
+<hkern u1="&#xe0;" u2="y" k="4" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="14" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="21" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="21" />
+<hkern u1="&#xe2;" u2="?" k="7" />
+<hkern u1="&#xe2;" u2="\" k="7" />
+<hkern u1="&#xe2;" u2="t" k="7" />
+<hkern u1="&#xe2;" u2="v" k="4" />
+<hkern u1="&#xe2;" u2="y" k="4" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="14" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="21" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="21" />
+<hkern u1="&#xe4;" u2="?" k="7" />
+<hkern u1="&#xe4;" u2="\" k="7" />
+<hkern u1="&#xe4;" u2="t" k="7" />
+<hkern u1="&#xe4;" u2="v" k="4" />
+<hkern u1="&#xe4;" u2="y" k="4" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="14" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="21" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="21" />
+<hkern u1="&#xe9;" u2="v" k="7" />
+<hkern u1="&#xe9;" u2="x" k="11" />
+<hkern u1="&#xe9;" u2="y" k="7" />
+<hkern u1="&#xe8;" u2="v" k="7" />
+<hkern u1="&#xe8;" u2="x" k="11" />
+<hkern u1="&#xe8;" u2="y" k="7" />
+<hkern u1="&#xea;" u2="v" k="7" />
+<hkern u1="&#xea;" u2="x" k="11" />
+<hkern u1="&#xea;" u2="y" k="7" />
+<hkern u1="&#xeb;" u2="v" k="7" />
+<hkern u1="&#xeb;" u2="x" k="11" />
+<hkern u1="&#xeb;" u2="y" k="7" />
+<hkern u1="&#xf1;" u2="?" k="7" />
+<hkern u1="&#xf1;" u2="\" k="7" />
+<hkern u1="&#xf1;" u2="t" k="7" />
+<hkern u1="&#xf1;" u2="v" k="4" />
+<hkern u1="&#xf1;" u2="y" k="4" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="14" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="21" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="21" />
+<hkern u1="&#xf3;" u2="&#x2c;" k="14" />
+<hkern u1="&#xf3;" u2="." k="14" />
+<hkern u1="&#xf3;" u2="/" k="7" />
+<hkern u1="&#xf3;" u2=":" k="7" />
+<hkern u1="&#xf3;" u2=";" k="7" />
+<hkern u1="&#xf3;" u2="?" k="14" />
+<hkern u1="&#xf3;" u2="\" k="7" />
+<hkern u1="&#xf3;" u2="v" k="7" />
+<hkern u1="&#xf3;" u2="x" k="11" />
+<hkern u1="&#xf3;" u2="y" k="7" />
+<hkern u1="&#xf3;" u2="z" k="11" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="21" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="14" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="28" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="28" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="14" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="14" />
+<hkern u1="&#xf4;" u2="&#x2c;" k="14" />
+<hkern u1="&#xf4;" u2="." k="14" />
+<hkern u1="&#xf4;" u2="/" k="7" />
+<hkern u1="&#xf4;" u2=":" k="7" />
+<hkern u1="&#xf4;" u2=";" k="7" />
+<hkern u1="&#xf4;" u2="?" k="14" />
+<hkern u1="&#xf4;" u2="\" k="7" />
+<hkern u1="&#xf4;" u2="v" k="7" />
+<hkern u1="&#xf4;" u2="x" k="11" />
+<hkern u1="&#xf4;" u2="y" k="7" />
+<hkern u1="&#xf4;" u2="z" k="11" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="21" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="14" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="28" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="28" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="14" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="14" />
+<hkern u1="&#xf6;" u2="&#x2c;" k="14" />
+<hkern u1="&#xf6;" u2="." k="14" />
+<hkern u1="&#xf6;" u2="/" k="7" />
+<hkern u1="&#xf6;" u2=":" k="7" />
+<hkern u1="&#xf6;" u2=";" k="7" />
+<hkern u1="&#xf6;" u2="?" k="14" />
+<hkern u1="&#xf6;" u2="\" k="7" />
+<hkern u1="&#xf6;" u2="v" k="7" />
+<hkern u1="&#xf6;" u2="x" k="11" />
+<hkern u1="&#xf6;" u2="y" k="7" />
+<hkern u1="&#xf6;" u2="z" k="11" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="21" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="14" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="28" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="28" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="14" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="14" />
+<hkern u1="&#xf5;" u2="&#x2c;" k="14" />
+<hkern u1="&#xf5;" u2="." k="14" />
+<hkern u1="&#xf5;" u2="/" k="7" />
+<hkern u1="&#xf5;" u2=":" k="7" />
+<hkern u1="&#xf5;" u2=";" k="7" />
+<hkern u1="&#xf5;" u2="?" k="14" />
+<hkern u1="&#xf5;" u2="\" k="7" />
+<hkern u1="&#xf5;" u2="v" k="7" />
+<hkern u1="&#xf5;" u2="x" k="11" />
+<hkern u1="&#xf5;" u2="y" k="7" />
+<hkern u1="&#xf5;" u2="z" k="11" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="21" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="14" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="28" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="28" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="14" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="14" />
+<hkern u1="&#xb0;" u2="4" k="65" />
+<hkern u1="&#xa3;" u2="1" k="-10" />
+<hkern u1="&#xa3;" u2="4" k="20" />
+<hkern u1="&#xdf;" u2="&#x161;" k="2" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="20" />
+<hkern u1="&#xdf;" u2="." k="20" />
+<hkern u1="&#xdf;" u2="/" k="10" />
+<hkern u1="&#xdf;" u2=":" k="10" />
+<hkern u1="&#xdf;" u2=";" k="10" />
+<hkern u1="&#xdf;" u2="?" k="20" />
+<hkern u1="&#xdf;" u2="\" k="10" />
+<hkern u1="&#xdf;" u2="f" k="2" />
+<hkern u1="&#xdf;" u2="g" k="10" />
+<hkern u1="&#xdf;" u2="s" k="2" />
+<hkern u1="&#xdf;" u2="t" k="10" />
+<hkern u1="&#xdf;" u2="v" k="10" />
+<hkern u1="&#xdf;" u2="w" k="3" />
+<hkern u1="&#xdf;" u2="x" k="15" />
+<hkern u1="&#xdf;" u2="y" k="10" />
+<hkern u1="&#xdf;" u2="z" k="15" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="30" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="40" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="40" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="20" />
+<hkern u1="&#xae;" u2="&#xdd;" k="30" />
+<hkern u1="&#xae;" u2="A" k="10" />
+<hkern u1="&#xae;" u2="T" k="30" />
+<hkern u1="&#xae;" u2="W" k="20" />
+<hkern u1="&#xae;" u2="Y" k="30" />
+<hkern u1="&#xae;" u2="&#xc4;" k="10" />
+<hkern u1="&#xae;" u2="&#xc5;" k="10" />
+<hkern u1="&#xae;" u2="&#xc6;" k="10" />
+<hkern u1="&#xae;" u2="&#xc0;" k="10" />
+<hkern u1="&#xae;" u2="&#xc3;" k="10" />
+<hkern u1="&#xae;" u2="&#x178;" k="30" />
+<hkern u1="&#xae;" u2="&#xc2;" k="10" />
+<hkern u1="&#xae;" u2="&#xc1;" k="10" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="30" />
+<hkern u1="&#xa9;" u2="A" k="10" />
+<hkern u1="&#xa9;" u2="T" k="30" />
+<hkern u1="&#xa9;" u2="W" k="20" />
+<hkern u1="&#xa9;" u2="Y" k="30" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="10" />
+<hkern u1="&#xa9;" u2="&#x178;" k="30" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="10" />
+<hkern u1="&#xc6;" u2="@" k="20" />
+<hkern u1="&#xc6;" u2="C" k="6" />
+<hkern u1="&#xc6;" u2="G" k="6" />
+<hkern u1="&#xc6;" u2="O" k="6" />
+<hkern u1="&#xc6;" u2="Q" k="6" />
+<hkern u1="&#xc6;" u2="T" k="-15" />
+<hkern u1="&#xc6;" u2="a" k="25" />
+<hkern u1="&#xc6;" u2="c" k="25" />
+<hkern u1="&#xc6;" u2="d" k="25" />
+<hkern u1="&#xc6;" u2="e" k="25" />
+<hkern u1="&#xc6;" u2="f" k="10" />
+<hkern u1="&#xc6;" u2="g" k="5" />
+<hkern u1="&#xc6;" u2="o" k="25" />
+<hkern u1="&#xc6;" u2="q" k="25" />
+<hkern u1="&#xc6;" u2="v" k="20" />
+<hkern u1="&#xc6;" u2="y" k="20" />
+<hkern u1="&#xc6;" u2="&#xd6;" k="6" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="25" />
+<hkern u1="&#xc6;" u2="&#xae;" k="20" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc6;" u2="&#xd8;" k="6" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="25" />
+<hkern u1="&#xc6;" u2="&#xd5;" k="6" />
+<hkern u1="&#xc6;" u2="&#x152;" k="6" />
+<hkern u1="&#xc6;" u2="&#x153;" k="25" />
+<hkern u1="&#xc6;" u2="&#xd3;" k="6" />
+<hkern u1="&#xc6;" u2="&#xd4;" k="6" />
+<hkern u1="&#xc6;" u2="&#xd2;" k="6" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd8;" u2="." k="40" />
+<hkern u1="&#xd8;" u2="/" k="50" />
+<hkern u1="&#xd8;" u2="?" k="20" />
+<hkern u1="&#xd8;" u2="A" k="17" />
+<hkern u1="&#xd8;" u2="J" k="45" />
+<hkern u1="&#xd8;" u2="T" k="32" />
+<hkern u1="&#xd8;" u2="V" k="19" />
+<hkern u1="&#xd8;" u2="W" k="33" />
+<hkern u1="&#xd8;" u2="X" k="25" />
+<hkern u1="&#xd8;" u2="Y" k="25" />
+<hkern u1="&#xd8;" u2="Z" k="45" />
+<hkern u1="&#xd8;" u2="a" k="5" />
+<hkern u1="&#xd8;" u2="b" k="10" />
+<hkern u1="&#xd8;" u2="c" k="5" />
+<hkern u1="&#xd8;" u2="d" k="5" />
+<hkern u1="&#xd8;" u2="e" k="5" />
+<hkern u1="&#xd8;" u2="h" k="10" />
+<hkern u1="&#xd8;" u2="k" k="10" />
+<hkern u1="&#xd8;" u2="l" k="10" />
+<hkern u1="&#xd8;" u2="m" k="5" />
+<hkern u1="&#xd8;" u2="n" k="5" />
+<hkern u1="&#xd8;" u2="o" k="5" />
+<hkern u1="&#xd8;" u2="p" k="5" />
+<hkern u1="&#xd8;" u2="q" k="5" />
+<hkern u1="&#xd8;" u2="r" k="5" />
+<hkern u1="&#xd8;" u2="u" k="5" />
+<hkern u1="&#xd8;" u2="v" k="-5" />
+<hkern u1="&#xd8;" u2="x" k="9" />
+<hkern u1="&#xd8;" u2="y" k="-5" />
+<hkern u1="&#xd8;" u2="z" k="10" />
+<hkern u1="&#xd8;" u2="&#xc4;" k="17" />
+<hkern u1="&#xd8;" u2="&#xc5;" k="17" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd8;" u2="&#xc6;" k="17" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd8;" u2="&#xc0;" k="17" />
+<hkern u1="&#xd8;" u2="&#xc3;" k="17" />
+<hkern u1="&#xd8;" u2="&#x153;" k="5" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd8;" u2="&#x178;" k="25" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd8;" u2="&#xc2;" k="17" />
+<hkern u1="&#xd8;" u2="&#xc1;" k="17" />
+<hkern u1="&#xd8;" u2="&#x131;" k="5" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="30" />
+<hkern u1="&#x3c0;" u2="A" k="10" />
+<hkern u1="&#x3c0;" u2="T" k="30" />
+<hkern u1="&#x3c0;" u2="W" k="20" />
+<hkern u1="&#x3c0;" u2="Y" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="10" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="10" />
+<hkern u1="&#xe6;" u2="v" k="10" />
+<hkern u1="&#xe6;" u2="x" k="15" />
+<hkern u1="&#xe6;" u2="y" k="10" />
+<hkern u1="&#xf8;" u2="&#x161;" k="2" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="20" />
+<hkern u1="&#xf8;" u2="." k="20" />
+<hkern u1="&#xf8;" u2="/" k="10" />
+<hkern u1="&#xf8;" u2=":" k="10" />
+<hkern u1="&#xf8;" u2=";" k="10" />
+<hkern u1="&#xf8;" u2="?" k="20" />
+<hkern u1="&#xf8;" u2="\" k="10" />
+<hkern u1="&#xf8;" u2="f" k="2" />
+<hkern u1="&#xf8;" u2="g" k="-2" />
+<hkern u1="&#xf8;" u2="s" k="2" />
+<hkern u1="&#xf8;" u2="v" k="10" />
+<hkern u1="&#xf8;" u2="w" k="3" />
+<hkern u1="&#xf8;" u2="x" k="15" />
+<hkern u1="&#xf8;" u2="y" k="10" />
+<hkern u1="&#xf8;" u2="z" k="15" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="40" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="40" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="20" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="40" />
+<hkern u1="&#xbf;" u2="1" k="40" />
+<hkern u1="&#xbf;" u2="3" k="-10" />
+<hkern u1="&#xbf;" u2="7" k="30" />
+<hkern u1="&#xbf;" u2="C" k="20" />
+<hkern u1="&#xbf;" u2="G" k="20" />
+<hkern u1="&#xbf;" u2="O" k="20" />
+<hkern u1="&#xbf;" u2="Q" k="20" />
+<hkern u1="&#xbf;" u2="T" k="60" />
+<hkern u1="&#xbf;" u2="V" k="50" />
+<hkern u1="&#xbf;" u2="W" k="30" />
+<hkern u1="&#xbf;" u2="Y" k="40" />
+<hkern u1="&#xbf;" u2="v" k="30" />
+<hkern u1="&#xbf;" u2="w" k="20" />
+<hkern u1="&#xbf;" u2="x" k="10" />
+<hkern u1="&#xbf;" u2="y" k="30" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="20" />
+<hkern u1="&#xbf;" u2="&#x152;" k="20" />
+<hkern u1="&#xbf;" u2="&#x178;" k="40" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="20" />
+<hkern u1="&#x192;" u2="1" k="-30" />
+<hkern u1="&#xab;" u2="&#xdd;" k="40" />
+<hkern u1="&#xab;" u2="T" k="40" />
+<hkern u1="&#xab;" u2="V" k="20" />
+<hkern u1="&#xab;" u2="W" k="20" />
+<hkern u1="&#xab;" u2="Y" k="40" />
+<hkern u1="&#xab;" u2="&#x178;" k="40" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="50" />
+<hkern u1="&#xbb;" u2="T" k="110" />
+<hkern u1="&#xbb;" u2="V" k="50" />
+<hkern u1="&#xbb;" u2="W" k="40" />
+<hkern u1="&#xbb;" u2="Y" k="50" />
+<hkern u1="&#xbb;" u2="x" k="50" />
+<hkern u1="&#xbb;" u2="z" k="50" />
+<hkern u1="&#xbb;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2026;" u2="0" k="40" />
+<hkern u1="&#x2026;" u2="1" k="60" />
+<hkern u1="&#x2026;" u2="4" k="50" />
+<hkern u1="&#x2026;" u2="6" k="30" />
+<hkern u1="&#x2026;" u2="8" k="15" />
+<hkern u1="&#x2026;" u2="9" k="10" />
+<hkern u1="&#x2026;" u2="A" k="-30" />
+<hkern u1="&#x2026;" u2="C" k="40" />
+<hkern u1="&#x2026;" u2="G" k="40" />
+<hkern u1="&#x2026;" u2="O" k="40" />
+<hkern u1="&#x2026;" u2="Q" k="40" />
+<hkern u1="&#x2026;" u2="T" k="60" />
+<hkern u1="&#x2026;" u2="U" k="10" />
+<hkern u1="&#x2026;" u2="V" k="60" />
+<hkern u1="&#x2026;" u2="W" k="20" />
+<hkern u1="&#x2026;" u2="Y" k="50" />
+<hkern u1="&#x2026;" u2="a" k="20" />
+<hkern u1="&#x2026;" u2="c" k="20" />
+<hkern u1="&#x2026;" u2="d" k="20" />
+<hkern u1="&#x2026;" u2="e" k="20" />
+<hkern u1="&#x2026;" u2="m" k="20" />
+<hkern u1="&#x2026;" u2="n" k="20" />
+<hkern u1="&#x2026;" u2="o" k="20" />
+<hkern u1="&#x2026;" u2="p" k="20" />
+<hkern u1="&#x2026;" u2="q" k="20" />
+<hkern u1="&#x2026;" u2="r" k="20" />
+<hkern u1="&#x2026;" u2="t" k="40" />
+<hkern u1="&#x2026;" u2="u" k="20" />
+<hkern u1="&#x2026;" u2="v" k="30" />
+<hkern u1="&#x2026;" u2="w" k="10" />
+<hkern u1="&#x2026;" u2="y" k="30" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2026;" u2="&#x152;" k="40" />
+<hkern u1="&#x2026;" u2="&#x153;" k="20" />
+<hkern u1="&#x2026;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2026;" u2="&#xda;" k="10" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2026;" u2="&#x131;" k="20" />
+<hkern u1="&#xc0;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc0;" u2="&#xdd;" k="35" />
+<hkern u1="&#xc0;" u2="*" k="42" />
+<hkern u1="&#xc0;" u2="&#x2c;" k="-21" />
+<hkern u1="&#xc0;" u2="-" k="-7" />
+<hkern u1="&#xc0;" u2="." k="-21" />
+<hkern u1="&#xc0;" u2="@" k="7" />
+<hkern u1="&#xc0;" u2="A" k="-7" />
+<hkern u1="&#xc0;" u2="C" k="14" />
+<hkern u1="&#xc0;" u2="G" k="14" />
+<hkern u1="&#xc0;" u2="J" k="-7" />
+<hkern u1="&#xc0;" u2="O" k="14" />
+<hkern u1="&#xc0;" u2="Q" k="14" />
+<hkern u1="&#xc0;" u2="S" k="-4" />
+<hkern u1="&#xc0;" u2="T" k="49" />
+<hkern u1="&#xc0;" u2="V" k="35" />
+<hkern u1="&#xc0;" u2="W" k="35" />
+<hkern u1="&#xc0;" u2="X" k="-7" />
+<hkern u1="&#xc0;" u2="Y" k="35" />
+<hkern u1="&#xc0;" u2="a" k="4" />
+<hkern u1="&#xc0;" u2="c" k="4" />
+<hkern u1="&#xc0;" u2="d" k="4" />
+<hkern u1="&#xc0;" u2="e" k="4" />
+<hkern u1="&#xc0;" u2="m" k="4" />
+<hkern u1="&#xc0;" u2="n" k="4" />
+<hkern u1="&#xc0;" u2="o" k="4" />
+<hkern u1="&#xc0;" u2="p" k="4" />
+<hkern u1="&#xc0;" u2="q" k="4" />
+<hkern u1="&#xc0;" u2="r" k="4" />
+<hkern u1="&#xc0;" u2="t" k="4" />
+<hkern u1="&#xc0;" u2="u" k="4" />
+<hkern u1="&#xc0;" u2="v" k="21" />
+<hkern u1="&#xc0;" u2="w" k="7" />
+<hkern u1="&#xc0;" u2="x" k="-14" />
+<hkern u1="&#xc0;" u2="y" k="21" />
+<hkern u1="&#xc0;" u2="z" k="-7" />
+<hkern u1="&#xc0;" u2="&#xc4;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xc5;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xd6;" k="14" />
+<hkern u1="&#xc0;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc0;" u2="&#xae;" k="7" />
+<hkern u1="&#xc0;" u2="&#xa9;" k="7" />
+<hkern u1="&#xc0;" u2="&#x2122;" k="49" />
+<hkern u1="&#xc0;" u2="&#xc6;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xd8;" k="14" />
+<hkern u1="&#xc0;" u2="&#x3c0;" k="7" />
+<hkern u1="&#xc0;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc0;" u2="&#x2026;" k="-21" />
+<hkern u1="&#xc0;" u2="&#xc0;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="14" />
+<hkern u1="&#xc0;" u2="&#x152;" k="14" />
+<hkern u1="&#xc0;" u2="&#x153;" k="4" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-7" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-7" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="42" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="21" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="42" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="21" />
+<hkern u1="&#xc0;" u2="&#x178;" k="35" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-21" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-21" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-7" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="14" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="14" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="14" />
+<hkern u1="&#xc0;" u2="&#x131;" k="4" />
+<hkern u1="&#xc3;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc3;" u2="&#xdd;" k="35" />
+<hkern u1="&#xc3;" u2="*" k="42" />
+<hkern u1="&#xc3;" u2="&#x2c;" k="-21" />
+<hkern u1="&#xc3;" u2="-" k="-7" />
+<hkern u1="&#xc3;" u2="." k="-21" />
+<hkern u1="&#xc3;" u2="@" k="7" />
+<hkern u1="&#xc3;" u2="A" k="-7" />
+<hkern u1="&#xc3;" u2="C" k="14" />
+<hkern u1="&#xc3;" u2="G" k="14" />
+<hkern u1="&#xc3;" u2="J" k="-7" />
+<hkern u1="&#xc3;" u2="O" k="14" />
+<hkern u1="&#xc3;" u2="Q" k="14" />
+<hkern u1="&#xc3;" u2="S" k="-4" />
+<hkern u1="&#xc3;" u2="T" k="49" />
+<hkern u1="&#xc3;" u2="V" k="35" />
+<hkern u1="&#xc3;" u2="W" k="35" />
+<hkern u1="&#xc3;" u2="X" k="-7" />
+<hkern u1="&#xc3;" u2="Y" k="35" />
+<hkern u1="&#xc3;" u2="a" k="4" />
+<hkern u1="&#xc3;" u2="c" k="4" />
+<hkern u1="&#xc3;" u2="d" k="4" />
+<hkern u1="&#xc3;" u2="e" k="4" />
+<hkern u1="&#xc3;" u2="m" k="4" />
+<hkern u1="&#xc3;" u2="n" k="4" />
+<hkern u1="&#xc3;" u2="o" k="4" />
+<hkern u1="&#xc3;" u2="p" k="4" />
+<hkern u1="&#xc3;" u2="q" k="4" />
+<hkern u1="&#xc3;" u2="r" k="4" />
+<hkern u1="&#xc3;" u2="t" k="4" />
+<hkern u1="&#xc3;" u2="u" k="4" />
+<hkern u1="&#xc3;" u2="v" k="21" />
+<hkern u1="&#xc3;" u2="w" k="7" />
+<hkern u1="&#xc3;" u2="x" k="-14" />
+<hkern u1="&#xc3;" u2="y" k="21" />
+<hkern u1="&#xc3;" u2="z" k="-7" />
+<hkern u1="&#xc3;" u2="&#xc4;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xc5;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xd6;" k="14" />
+<hkern u1="&#xc3;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc3;" u2="&#xae;" k="7" />
+<hkern u1="&#xc3;" u2="&#xa9;" k="7" />
+<hkern u1="&#xc3;" u2="&#x2122;" k="49" />
+<hkern u1="&#xc3;" u2="&#xc6;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xd8;" k="14" />
+<hkern u1="&#xc3;" u2="&#x3c0;" k="7" />
+<hkern u1="&#xc3;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc3;" u2="&#x2026;" k="-21" />
+<hkern u1="&#xc3;" u2="&#xc0;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xc3;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="14" />
+<hkern u1="&#xc3;" u2="&#x152;" k="14" />
+<hkern u1="&#xc3;" u2="&#x153;" k="4" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-7" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-7" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="42" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="21" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="42" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="21" />
+<hkern u1="&#xc3;" u2="&#x178;" k="35" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-21" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-21" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-7" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="14" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="14" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="14" />
+<hkern u1="&#xc3;" u2="&#x131;" k="4" />
+<hkern u1="&#xd5;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd5;" u2="&#x17d;" k="31" />
+<hkern u1="&#xd5;" u2="&#x2c;" k="28" />
+<hkern u1="&#xd5;" u2="." k="28" />
+<hkern u1="&#xd5;" u2="/" k="35" />
+<hkern u1="&#xd5;" u2="?" k="14" />
+<hkern u1="&#xd5;" u2="A" k="14" />
+<hkern u1="&#xd5;" u2="J" k="31" />
+<hkern u1="&#xd5;" u2="T" k="21" />
+<hkern u1="&#xd5;" u2="V" k="14" />
+<hkern u1="&#xd5;" u2="W" k="28" />
+<hkern u1="&#xd5;" u2="X" k="18" />
+<hkern u1="&#xd5;" u2="Y" k="18" />
+<hkern u1="&#xd5;" u2="Z" k="31" />
+<hkern u1="&#xd5;" u2="a" k="4" />
+<hkern u1="&#xd5;" u2="b" k="7" />
+<hkern u1="&#xd5;" u2="c" k="4" />
+<hkern u1="&#xd5;" u2="d" k="4" />
+<hkern u1="&#xd5;" u2="e" k="4" />
+<hkern u1="&#xd5;" u2="h" k="7" />
+<hkern u1="&#xd5;" u2="k" k="7" />
+<hkern u1="&#xd5;" u2="l" k="7" />
+<hkern u1="&#xd5;" u2="m" k="4" />
+<hkern u1="&#xd5;" u2="n" k="4" />
+<hkern u1="&#xd5;" u2="o" k="4" />
+<hkern u1="&#xd5;" u2="p" k="4" />
+<hkern u1="&#xd5;" u2="q" k="4" />
+<hkern u1="&#xd5;" u2="r" k="4" />
+<hkern u1="&#xd5;" u2="u" k="4" />
+<hkern u1="&#xd5;" u2="x" k="7" />
+<hkern u1="&#xd5;" u2="z" k="7" />
+<hkern u1="&#xd5;" u2="&#xc4;" k="14" />
+<hkern u1="&#xd5;" u2="&#xc5;" k="14" />
+<hkern u1="&#xd5;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd5;" u2="&#xc6;" k="14" />
+<hkern u1="&#xd5;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd5;" u2="&#x2026;" k="28" />
+<hkern u1="&#xd5;" u2="&#xc0;" k="14" />
+<hkern u1="&#xd5;" u2="&#xc3;" k="14" />
+<hkern u1="&#xd5;" u2="&#x153;" k="4" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="14" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="14" />
+<hkern u1="&#xd5;" u2="&#x178;" k="18" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="28" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="28" />
+<hkern u1="&#xd5;" u2="&#xc2;" k="14" />
+<hkern u1="&#xd5;" u2="&#xc1;" k="14" />
+<hkern u1="&#xd5;" u2="&#x131;" k="4" />
+<hkern u1="&#x152;" u2="@" k="20" />
+<hkern u1="&#x152;" u2="C" k="6" />
+<hkern u1="&#x152;" u2="G" k="6" />
+<hkern u1="&#x152;" u2="O" k="6" />
+<hkern u1="&#x152;" u2="Q" k="6" />
+<hkern u1="&#x152;" u2="T" k="-15" />
+<hkern u1="&#x152;" u2="a" k="25" />
+<hkern u1="&#x152;" u2="c" k="25" />
+<hkern u1="&#x152;" u2="d" k="25" />
+<hkern u1="&#x152;" u2="e" k="25" />
+<hkern u1="&#x152;" u2="f" k="10" />
+<hkern u1="&#x152;" u2="g" k="5" />
+<hkern u1="&#x152;" u2="o" k="25" />
+<hkern u1="&#x152;" u2="q" k="25" />
+<hkern u1="&#x152;" u2="v" k="20" />
+<hkern u1="&#x152;" u2="y" k="20" />
+<hkern u1="&#x152;" u2="&#xd6;" k="6" />
+<hkern u1="&#x152;" u2="&#xe7;" k="25" />
+<hkern u1="&#x152;" u2="&#xae;" k="20" />
+<hkern u1="&#x152;" u2="&#xa9;" k="20" />
+<hkern u1="&#x152;" u2="&#xd8;" k="6" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="20" />
+<hkern u1="&#x152;" u2="&#xe6;" k="25" />
+<hkern u1="&#x152;" u2="&#xd5;" k="6" />
+<hkern u1="&#x152;" u2="&#x152;" k="6" />
+<hkern u1="&#x152;" u2="&#x153;" k="25" />
+<hkern u1="&#x152;" u2="&#xd3;" k="6" />
+<hkern u1="&#x152;" u2="&#xd4;" k="6" />
+<hkern u1="&#x152;" u2="&#xd2;" k="6" />
+<hkern u1="&#x153;" u2="v" k="10" />
+<hkern u1="&#x153;" u2="x" k="15" />
+<hkern u1="&#x153;" u2="y" k="10" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2013;" u2="1" k="20" />
+<hkern u1="&#x2013;" u2="7" k="30" />
+<hkern u1="&#x2013;" u2="A" k="-10" />
+<hkern u1="&#x2013;" u2="T" k="70" />
+<hkern u1="&#x2013;" u2="V" k="50" />
+<hkern u1="&#x2013;" u2="W" k="40" />
+<hkern u1="&#x2013;" u2="X" k="30" />
+<hkern u1="&#x2013;" u2="Y" k="50" />
+<hkern u1="&#x2013;" u2="Z" k="10" />
+<hkern u1="&#x2013;" u2="a" k="10" />
+<hkern u1="&#x2013;" u2="c" k="10" />
+<hkern u1="&#x2013;" u2="d" k="10" />
+<hkern u1="&#x2013;" u2="e" k="10" />
+<hkern u1="&#x2013;" u2="o" k="10" />
+<hkern u1="&#x2013;" u2="q" k="10" />
+<hkern u1="&#x2013;" u2="v" k="10" />
+<hkern u1="&#x2013;" u2="x" k="30" />
+<hkern u1="&#x2013;" u2="y" k="10" />
+<hkern u1="&#x2013;" u2="z" k="30" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2013;" u2="&#x153;" k="10" />
+<hkern u1="&#x2013;" u2="&#x178;" k="50" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="50" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="10" />
+<hkern u1="&#x2014;" u2="1" k="20" />
+<hkern u1="&#x2014;" u2="7" k="30" />
+<hkern u1="&#x2014;" u2="A" k="-10" />
+<hkern u1="&#x2014;" u2="T" k="70" />
+<hkern u1="&#x2014;" u2="V" k="50" />
+<hkern u1="&#x2014;" u2="W" k="40" />
+<hkern u1="&#x2014;" u2="X" k="30" />
+<hkern u1="&#x2014;" u2="Y" k="50" />
+<hkern u1="&#x2014;" u2="Z" k="10" />
+<hkern u1="&#x2014;" u2="a" k="10" />
+<hkern u1="&#x2014;" u2="c" k="10" />
+<hkern u1="&#x2014;" u2="d" k="10" />
+<hkern u1="&#x2014;" u2="e" k="10" />
+<hkern u1="&#x2014;" u2="o" k="10" />
+<hkern u1="&#x2014;" u2="q" k="10" />
+<hkern u1="&#x2014;" u2="v" k="10" />
+<hkern u1="&#x2014;" u2="x" k="30" />
+<hkern u1="&#x2014;" u2="y" k="10" />
+<hkern u1="&#x2014;" u2="z" k="30" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="10" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-10" />
+<hkern u1="&#x2014;" u2="&#x153;" k="10" />
+<hkern u1="&#x2014;" u2="&#x178;" k="50" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x201c;" u2="&#x161;" k="30" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x201c;" u2="A" k="40" />
+<hkern u1="&#x201c;" u2="J" k="110" />
+<hkern u1="&#x201c;" u2="T" k="-10" />
+<hkern u1="&#x201c;" u2="V" k="-20" />
+<hkern u1="&#x201c;" u2="W" k="-20" />
+<hkern u1="&#x201c;" u2="X" k="-20" />
+<hkern u1="&#x201c;" u2="Y" k="-30" />
+<hkern u1="&#x201c;" u2="a" k="30" />
+<hkern u1="&#x201c;" u2="c" k="30" />
+<hkern u1="&#x201c;" u2="d" k="30" />
+<hkern u1="&#x201c;" u2="e" k="30" />
+<hkern u1="&#x201c;" u2="g" k="40" />
+<hkern u1="&#x201c;" u2="m" k="10" />
+<hkern u1="&#x201c;" u2="n" k="10" />
+<hkern u1="&#x201c;" u2="o" k="30" />
+<hkern u1="&#x201c;" u2="p" k="10" />
+<hkern u1="&#x201c;" u2="q" k="30" />
+<hkern u1="&#x201c;" u2="r" k="10" />
+<hkern u1="&#x201c;" u2="s" k="30" />
+<hkern u1="&#x201c;" u2="u" k="10" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="40" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="30" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="40" />
+<hkern u1="&#x201c;" u2="&#x153;" k="30" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-30" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="40" />
+<hkern u1="&#x201c;" u2="&#x131;" k="10" />
+<hkern u1="&#x201d;" u2="&#x160;" k="10" />
+<hkern u1="&#x201d;" u2="C" k="40" />
+<hkern u1="&#x201d;" u2="G" k="40" />
+<hkern u1="&#x201d;" u2="J" k="120" />
+<hkern u1="&#x201d;" u2="O" k="40" />
+<hkern u1="&#x201d;" u2="Q" k="40" />
+<hkern u1="&#x201d;" u2="S" k="10" />
+<hkern u1="&#x201d;" u2="a" k="50" />
+<hkern u1="&#x201d;" u2="c" k="50" />
+<hkern u1="&#x201d;" u2="d" k="50" />
+<hkern u1="&#x201d;" u2="e" k="50" />
+<hkern u1="&#x201d;" u2="o" k="50" />
+<hkern u1="&#x201d;" u2="q" k="50" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201d;" u2="&#x152;" k="40" />
+<hkern u1="&#x201d;" u2="&#x153;" k="50" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2018;" u2="&#x161;" k="30" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-30" />
+<hkern u1="&#x2018;" u2="A" k="40" />
+<hkern u1="&#x2018;" u2="J" k="110" />
+<hkern u1="&#x2018;" u2="T" k="-10" />
+<hkern u1="&#x2018;" u2="V" k="-20" />
+<hkern u1="&#x2018;" u2="W" k="-20" />
+<hkern u1="&#x2018;" u2="X" k="-20" />
+<hkern u1="&#x2018;" u2="Y" k="-30" />
+<hkern u1="&#x2018;" u2="a" k="30" />
+<hkern u1="&#x2018;" u2="c" k="30" />
+<hkern u1="&#x2018;" u2="d" k="30" />
+<hkern u1="&#x2018;" u2="e" k="30" />
+<hkern u1="&#x2018;" u2="g" k="40" />
+<hkern u1="&#x2018;" u2="m" k="10" />
+<hkern u1="&#x2018;" u2="n" k="10" />
+<hkern u1="&#x2018;" u2="o" k="30" />
+<hkern u1="&#x2018;" u2="p" k="10" />
+<hkern u1="&#x2018;" u2="q" k="30" />
+<hkern u1="&#x2018;" u2="r" k="10" />
+<hkern u1="&#x2018;" u2="s" k="30" />
+<hkern u1="&#x2018;" u2="u" k="10" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="40" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="30" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="40" />
+<hkern u1="&#x2018;" u2="&#x153;" k="30" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-30" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="40" />
+<hkern u1="&#x2018;" u2="&#x131;" k="10" />
+<hkern u1="&#x2019;" u2="&#x160;" k="10" />
+<hkern u1="&#x2019;" u2="C" k="40" />
+<hkern u1="&#x2019;" u2="G" k="40" />
+<hkern u1="&#x2019;" u2="J" k="120" />
+<hkern u1="&#x2019;" u2="O" k="40" />
+<hkern u1="&#x2019;" u2="Q" k="40" />
+<hkern u1="&#x2019;" u2="S" k="10" />
+<hkern u1="&#x2019;" u2="a" k="50" />
+<hkern u1="&#x2019;" u2="c" k="50" />
+<hkern u1="&#x2019;" u2="d" k="50" />
+<hkern u1="&#x2019;" u2="e" k="50" />
+<hkern u1="&#x2019;" u2="o" k="50" />
+<hkern u1="&#x2019;" u2="q" k="50" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="40" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="40" />
+<hkern u1="&#x2019;" u2="&#x152;" k="40" />
+<hkern u1="&#x2019;" u2="&#x153;" k="50" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="40" />
+<hkern u1="&#x178;" u2="&#x160;" k="-10" />
+<hkern u1="&#x178;" u2="&#x161;" k="50" />
+<hkern u1="&#x178;" u2="&amp;" k="30" />
+<hkern u1="&#x178;" u2=")" k="-50" />
+<hkern u1="&#x178;" u2="&#x2c;" k="50" />
+<hkern u1="&#x178;" u2="-" k="50" />
+<hkern u1="&#x178;" u2="." k="50" />
+<hkern u1="&#x178;" u2=":" k="30" />
+<hkern u1="&#x178;" u2=";" k="30" />
+<hkern u1="&#x178;" u2="@" k="30" />
+<hkern u1="&#x178;" u2="A" k="38" />
+<hkern u1="&#x178;" u2="C" k="25" />
+<hkern u1="&#x178;" u2="G" k="25" />
+<hkern u1="&#x178;" u2="J" k="73" />
+<hkern u1="&#x178;" u2="O" k="25" />
+<hkern u1="&#x178;" u2="Q" k="25" />
+<hkern u1="&#x178;" u2="S" k="-10" />
+<hkern u1="&#x178;" u2="T" k="-20" />
+<hkern u1="&#x178;" u2="V" k="-30" />
+<hkern u1="&#x178;" u2="X" k="-20" />
+<hkern u1="&#x178;" u2="]" k="-50" />
+<hkern u1="&#x178;" u2="a" k="55" />
+<hkern u1="&#x178;" u2="c" k="55" />
+<hkern u1="&#x178;" u2="d" k="55" />
+<hkern u1="&#x178;" u2="e" k="55" />
+<hkern u1="&#x178;" u2="f" k="10" />
+<hkern u1="&#x178;" u2="g" k="40" />
+<hkern u1="&#x178;" u2="m" k="40" />
+<hkern u1="&#x178;" u2="n" k="40" />
+<hkern u1="&#x178;" u2="o" k="55" />
+<hkern u1="&#x178;" u2="p" k="40" />
+<hkern u1="&#x178;" u2="q" k="55" />
+<hkern u1="&#x178;" u2="r" k="40" />
+<hkern u1="&#x178;" u2="s" k="50" />
+<hkern u1="&#x178;" u2="t" k="20" />
+<hkern u1="&#x178;" u2="u" k="40" />
+<hkern u1="&#x178;" u2="v" k="20" />
+<hkern u1="&#x178;" u2="w" k="10" />
+<hkern u1="&#x178;" u2="x" k="20" />
+<hkern u1="&#x178;" u2="y" k="20" />
+<hkern u1="&#x178;" u2="z" k="30" />
+<hkern u1="&#x178;" u2="}" k="-50" />
+<hkern u1="&#x178;" u2="&#xc4;" k="38" />
+<hkern u1="&#x178;" u2="&#xc5;" k="38" />
+<hkern u1="&#x178;" u2="&#xd6;" k="25" />
+<hkern u1="&#x178;" u2="&#xe7;" k="55" />
+<hkern u1="&#x178;" u2="&#xae;" k="30" />
+<hkern u1="&#x178;" u2="&#xa9;" k="30" />
+<hkern u1="&#x178;" u2="&#xc6;" k="38" />
+<hkern u1="&#x178;" u2="&#xd8;" k="25" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="30" />
+<hkern u1="&#x178;" u2="&#xe6;" k="55" />
+<hkern u1="&#x178;" u2="&#xab;" k="60" />
+<hkern u1="&#x178;" u2="&#xbb;" k="40" />
+<hkern u1="&#x178;" u2="&#x2026;" k="50" />
+<hkern u1="&#x178;" u2="&#xc0;" k="38" />
+<hkern u1="&#x178;" u2="&#xc3;" k="38" />
+<hkern u1="&#x178;" u2="&#xd5;" k="25" />
+<hkern u1="&#x178;" u2="&#x152;" k="25" />
+<hkern u1="&#x178;" u2="&#x153;" k="55" />
+<hkern u1="&#x178;" u2="&#x2013;" k="50" />
+<hkern u1="&#x178;" u2="&#x2014;" k="50" />
+<hkern u1="&#x178;" u2="&#x2039;" k="60" />
+<hkern u1="&#x178;" u2="&#x203a;" k="40" />
+<hkern u1="&#x178;" u2="&#x201a;" k="50" />
+<hkern u1="&#x178;" u2="&#x201e;" k="50" />
+<hkern u1="&#x178;" u2="&#xc2;" k="38" />
+<hkern u1="&#x178;" u2="&#xc1;" k="38" />
+<hkern u1="&#x178;" u2="&#xd3;" k="25" />
+<hkern u1="&#x178;" u2="&#xd4;" k="25" />
+<hkern u1="&#x178;" u2="&#xd2;" k="25" />
+<hkern u1="&#x178;" u2="&#x131;" k="40" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2039;" u2="T" k="40" />
+<hkern u1="&#x2039;" u2="V" k="20" />
+<hkern u1="&#x2039;" u2="W" k="20" />
+<hkern u1="&#x2039;" u2="Y" k="40" />
+<hkern u1="&#x2039;" u2="&#x178;" k="40" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x203a;" u2="T" k="110" />
+<hkern u1="&#x203a;" u2="V" k="50" />
+<hkern u1="&#x203a;" u2="W" k="40" />
+<hkern u1="&#x203a;" u2="Y" k="50" />
+<hkern u1="&#x203a;" u2="x" k="50" />
+<hkern u1="&#x203a;" u2="z" k="50" />
+<hkern u1="&#x203a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201a;" u2="0" k="40" />
+<hkern u1="&#x201a;" u2="1" k="60" />
+<hkern u1="&#x201a;" u2="4" k="50" />
+<hkern u1="&#x201a;" u2="6" k="30" />
+<hkern u1="&#x201a;" u2="8" k="15" />
+<hkern u1="&#x201a;" u2="9" k="10" />
+<hkern u1="&#x201a;" u2="A" k="-30" />
+<hkern u1="&#x201a;" u2="C" k="40" />
+<hkern u1="&#x201a;" u2="G" k="40" />
+<hkern u1="&#x201a;" u2="O" k="40" />
+<hkern u1="&#x201a;" u2="Q" k="40" />
+<hkern u1="&#x201a;" u2="T" k="60" />
+<hkern u1="&#x201a;" u2="U" k="10" />
+<hkern u1="&#x201a;" u2="V" k="60" />
+<hkern u1="&#x201a;" u2="W" k="20" />
+<hkern u1="&#x201a;" u2="Y" k="50" />
+<hkern u1="&#x201a;" u2="a" k="20" />
+<hkern u1="&#x201a;" u2="c" k="20" />
+<hkern u1="&#x201a;" u2="d" k="20" />
+<hkern u1="&#x201a;" u2="e" k="20" />
+<hkern u1="&#x201a;" u2="j" k="-30" />
+<hkern u1="&#x201a;" u2="m" k="20" />
+<hkern u1="&#x201a;" u2="n" k="20" />
+<hkern u1="&#x201a;" u2="o" k="20" />
+<hkern u1="&#x201a;" u2="p" k="20" />
+<hkern u1="&#x201a;" u2="q" k="20" />
+<hkern u1="&#x201a;" u2="r" k="20" />
+<hkern u1="&#x201a;" u2="t" k="40" />
+<hkern u1="&#x201a;" u2="u" k="20" />
+<hkern u1="&#x201a;" u2="v" k="30" />
+<hkern u1="&#x201a;" u2="w" k="10" />
+<hkern u1="&#x201a;" u2="y" k="30" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201a;" u2="&#x152;" k="40" />
+<hkern u1="&#x201a;" u2="&#x153;" k="20" />
+<hkern u1="&#x201a;" u2="&#x178;" k="50" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="40" />
+<hkern u1="&#x201a;" u2="&#xda;" k="10" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201a;" u2="&#x131;" k="20" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="50" />
+<hkern u1="&#x201e;" u2="0" k="40" />
+<hkern u1="&#x201e;" u2="1" k="60" />
+<hkern u1="&#x201e;" u2="4" k="50" />
+<hkern u1="&#x201e;" u2="6" k="30" />
+<hkern u1="&#x201e;" u2="8" k="15" />
+<hkern u1="&#x201e;" u2="9" k="10" />
+<hkern u1="&#x201e;" u2="A" k="-30" />
+<hkern u1="&#x201e;" u2="C" k="40" />
+<hkern u1="&#x201e;" u2="G" k="40" />
+<hkern u1="&#x201e;" u2="O" k="40" />
+<hkern u1="&#x201e;" u2="Q" k="40" />
+<hkern u1="&#x201e;" u2="T" k="60" />
+<hkern u1="&#x201e;" u2="U" k="10" />
+<hkern u1="&#x201e;" u2="V" k="60" />
+<hkern u1="&#x201e;" u2="W" k="20" />
+<hkern u1="&#x201e;" u2="Y" k="50" />
+<hkern u1="&#x201e;" u2="a" k="20" />
+<hkern u1="&#x201e;" u2="c" k="20" />
+<hkern u1="&#x201e;" u2="d" k="20" />
+<hkern u1="&#x201e;" u2="e" k="20" />
+<hkern u1="&#x201e;" u2="j" k="-30" />
+<hkern u1="&#x201e;" u2="m" k="20" />
+<hkern u1="&#x201e;" u2="n" k="20" />
+<hkern u1="&#x201e;" u2="o" k="20" />
+<hkern u1="&#x201e;" u2="p" k="20" />
+<hkern u1="&#x201e;" u2="q" k="20" />
+<hkern u1="&#x201e;" u2="r" k="20" />
+<hkern u1="&#x201e;" u2="t" k="40" />
+<hkern u1="&#x201e;" u2="u" k="20" />
+<hkern u1="&#x201e;" u2="v" k="30" />
+<hkern u1="&#x201e;" u2="w" k="10" />
+<hkern u1="&#x201e;" u2="y" k="30" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="40" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="10" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="40" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="40" />
+<hkern u1="&#x201e;" u2="&#x152;" k="40" />
+<hkern u1="&#x201e;" u2="&#x153;" k="20" />
+<hkern u1="&#x201e;" u2="&#x178;" k="50" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="40" />
+<hkern u1="&#x201e;" u2="&#xda;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201e;" u2="&#x131;" k="20" />
+<hkern u1="&#xc2;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc2;" u2="&#xdd;" k="35" />
+<hkern u1="&#xc2;" u2="*" k="42" />
+<hkern u1="&#xc2;" u2="&#x2c;" k="-21" />
+<hkern u1="&#xc2;" u2="-" k="-7" />
+<hkern u1="&#xc2;" u2="." k="-21" />
+<hkern u1="&#xc2;" u2="@" k="7" />
+<hkern u1="&#xc2;" u2="A" k="-7" />
+<hkern u1="&#xc2;" u2="C" k="14" />
+<hkern u1="&#xc2;" u2="G" k="14" />
+<hkern u1="&#xc2;" u2="J" k="-7" />
+<hkern u1="&#xc2;" u2="O" k="14" />
+<hkern u1="&#xc2;" u2="Q" k="14" />
+<hkern u1="&#xc2;" u2="S" k="-4" />
+<hkern u1="&#xc2;" u2="T" k="49" />
+<hkern u1="&#xc2;" u2="V" k="35" />
+<hkern u1="&#xc2;" u2="W" k="35" />
+<hkern u1="&#xc2;" u2="X" k="-7" />
+<hkern u1="&#xc2;" u2="Y" k="35" />
+<hkern u1="&#xc2;" u2="a" k="4" />
+<hkern u1="&#xc2;" u2="c" k="4" />
+<hkern u1="&#xc2;" u2="d" k="4" />
+<hkern u1="&#xc2;" u2="e" k="4" />
+<hkern u1="&#xc2;" u2="m" k="4" />
+<hkern u1="&#xc2;" u2="n" k="4" />
+<hkern u1="&#xc2;" u2="o" k="4" />
+<hkern u1="&#xc2;" u2="p" k="4" />
+<hkern u1="&#xc2;" u2="q" k="4" />
+<hkern u1="&#xc2;" u2="r" k="4" />
+<hkern u1="&#xc2;" u2="t" k="4" />
+<hkern u1="&#xc2;" u2="u" k="4" />
+<hkern u1="&#xc2;" u2="v" k="21" />
+<hkern u1="&#xc2;" u2="w" k="7" />
+<hkern u1="&#xc2;" u2="x" k="-14" />
+<hkern u1="&#xc2;" u2="y" k="21" />
+<hkern u1="&#xc2;" u2="z" k="-7" />
+<hkern u1="&#xc2;" u2="&#xc4;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xc5;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xd6;" k="14" />
+<hkern u1="&#xc2;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc2;" u2="&#xae;" k="7" />
+<hkern u1="&#xc2;" u2="&#xa9;" k="7" />
+<hkern u1="&#xc2;" u2="&#x2122;" k="49" />
+<hkern u1="&#xc2;" u2="&#xc6;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xd8;" k="14" />
+<hkern u1="&#xc2;" u2="&#x3c0;" k="7" />
+<hkern u1="&#xc2;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc2;" u2="&#x2026;" k="-21" />
+<hkern u1="&#xc2;" u2="&#xc0;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xc3;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xd5;" k="14" />
+<hkern u1="&#xc2;" u2="&#x152;" k="14" />
+<hkern u1="&#xc2;" u2="&#x153;" k="4" />
+<hkern u1="&#xc2;" u2="&#x2013;" k="-7" />
+<hkern u1="&#xc2;" u2="&#x2014;" k="-7" />
+<hkern u1="&#xc2;" u2="&#x201c;" k="42" />
+<hkern u1="&#xc2;" u2="&#x201d;" k="21" />
+<hkern u1="&#xc2;" u2="&#x2018;" k="42" />
+<hkern u1="&#xc2;" u2="&#x2019;" k="21" />
+<hkern u1="&#xc2;" u2="&#x178;" k="35" />
+<hkern u1="&#xc2;" u2="&#x201a;" k="-21" />
+<hkern u1="&#xc2;" u2="&#x201e;" k="-21" />
+<hkern u1="&#xc2;" u2="&#xc2;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-7" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="14" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="14" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="14" />
+<hkern u1="&#xc2;" u2="&#x131;" k="4" />
+<hkern u1="&#xca;" u2="@" k="14" />
+<hkern u1="&#xca;" u2="T" k="-11" />
+<hkern u1="&#xca;" u2="a" k="18" />
+<hkern u1="&#xca;" u2="c" k="18" />
+<hkern u1="&#xca;" u2="d" k="18" />
+<hkern u1="&#xca;" u2="e" k="18" />
+<hkern u1="&#xca;" u2="f" k="7" />
+<hkern u1="&#xca;" u2="g" k="4" />
+<hkern u1="&#xca;" u2="o" k="18" />
+<hkern u1="&#xca;" u2="q" k="18" />
+<hkern u1="&#xca;" u2="v" k="14" />
+<hkern u1="&#xca;" u2="y" k="14" />
+<hkern u1="&#xca;" u2="&#xe7;" k="18" />
+<hkern u1="&#xca;" u2="&#xae;" k="14" />
+<hkern u1="&#xca;" u2="&#xa9;" k="14" />
+<hkern u1="&#xca;" u2="&#x3c0;" k="14" />
+<hkern u1="&#xca;" u2="&#xe6;" k="18" />
+<hkern u1="&#xca;" u2="&#x153;" k="18" />
+<hkern u1="&#xc1;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc1;" u2="&#xdd;" k="35" />
+<hkern u1="&#xc1;" u2="*" k="42" />
+<hkern u1="&#xc1;" u2="&#x2c;" k="-21" />
+<hkern u1="&#xc1;" u2="-" k="-7" />
+<hkern u1="&#xc1;" u2="." k="-21" />
+<hkern u1="&#xc1;" u2="@" k="7" />
+<hkern u1="&#xc1;" u2="A" k="-7" />
+<hkern u1="&#xc1;" u2="C" k="14" />
+<hkern u1="&#xc1;" u2="G" k="14" />
+<hkern u1="&#xc1;" u2="J" k="-7" />
+<hkern u1="&#xc1;" u2="O" k="14" />
+<hkern u1="&#xc1;" u2="Q" k="14" />
+<hkern u1="&#xc1;" u2="S" k="-4" />
+<hkern u1="&#xc1;" u2="T" k="49" />
+<hkern u1="&#xc1;" u2="V" k="35" />
+<hkern u1="&#xc1;" u2="W" k="35" />
+<hkern u1="&#xc1;" u2="X" k="-7" />
+<hkern u1="&#xc1;" u2="Y" k="35" />
+<hkern u1="&#xc1;" u2="a" k="4" />
+<hkern u1="&#xc1;" u2="c" k="4" />
+<hkern u1="&#xc1;" u2="d" k="4" />
+<hkern u1="&#xc1;" u2="e" k="4" />
+<hkern u1="&#xc1;" u2="m" k="4" />
+<hkern u1="&#xc1;" u2="n" k="4" />
+<hkern u1="&#xc1;" u2="o" k="4" />
+<hkern u1="&#xc1;" u2="p" k="4" />
+<hkern u1="&#xc1;" u2="q" k="4" />
+<hkern u1="&#xc1;" u2="r" k="4" />
+<hkern u1="&#xc1;" u2="t" k="4" />
+<hkern u1="&#xc1;" u2="u" k="4" />
+<hkern u1="&#xc1;" u2="v" k="21" />
+<hkern u1="&#xc1;" u2="w" k="7" />
+<hkern u1="&#xc1;" u2="x" k="-14" />
+<hkern u1="&#xc1;" u2="y" k="21" />
+<hkern u1="&#xc1;" u2="z" k="-7" />
+<hkern u1="&#xc1;" u2="&#xc4;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xc5;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xd6;" k="14" />
+<hkern u1="&#xc1;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc1;" u2="&#xae;" k="7" />
+<hkern u1="&#xc1;" u2="&#xa9;" k="7" />
+<hkern u1="&#xc1;" u2="&#x2122;" k="49" />
+<hkern u1="&#xc1;" u2="&#xc6;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xd8;" k="14" />
+<hkern u1="&#xc1;" u2="&#x3c0;" k="7" />
+<hkern u1="&#xc1;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc1;" u2="&#x2026;" k="-21" />
+<hkern u1="&#xc1;" u2="&#xc0;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xc3;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xd5;" k="14" />
+<hkern u1="&#xc1;" u2="&#x152;" k="14" />
+<hkern u1="&#xc1;" u2="&#x153;" k="4" />
+<hkern u1="&#xc1;" u2="&#x2013;" k="-7" />
+<hkern u1="&#xc1;" u2="&#x2014;" k="-7" />
+<hkern u1="&#xc1;" u2="&#x201c;" k="42" />
+<hkern u1="&#xc1;" u2="&#x201d;" k="21" />
+<hkern u1="&#xc1;" u2="&#x2018;" k="42" />
+<hkern u1="&#xc1;" u2="&#x2019;" k="21" />
+<hkern u1="&#xc1;" u2="&#x178;" k="35" />
+<hkern u1="&#xc1;" u2="&#x201a;" k="-21" />
+<hkern u1="&#xc1;" u2="&#x201e;" k="-21" />
+<hkern u1="&#xc1;" u2="&#xc2;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xc1;" k="-7" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="14" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="14" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="14" />
+<hkern u1="&#xc1;" u2="&#x131;" k="4" />
+<hkern u1="&#xcb;" u2="@" k="14" />
+<hkern u1="&#xcb;" u2="T" k="-11" />
+<hkern u1="&#xcb;" u2="a" k="18" />
+<hkern u1="&#xcb;" u2="c" k="18" />
+<hkern u1="&#xcb;" u2="d" k="18" />
+<hkern u1="&#xcb;" u2="e" k="18" />
+<hkern u1="&#xcb;" u2="f" k="7" />
+<hkern u1="&#xcb;" u2="g" k="4" />
+<hkern u1="&#xcb;" u2="o" k="18" />
+<hkern u1="&#xcb;" u2="q" k="18" />
+<hkern u1="&#xcb;" u2="v" k="14" />
+<hkern u1="&#xcb;" u2="y" k="14" />
+<hkern u1="&#xcb;" u2="&#xe7;" k="18" />
+<hkern u1="&#xcb;" u2="&#xae;" k="14" />
+<hkern u1="&#xcb;" u2="&#xa9;" k="14" />
+<hkern u1="&#xcb;" u2="&#x3c0;" k="14" />
+<hkern u1="&#xcb;" u2="&#xe6;" k="18" />
+<hkern u1="&#xcb;" u2="&#x153;" k="18" />
+<hkern u1="&#xc8;" u2="@" k="14" />
+<hkern u1="&#xc8;" u2="T" k="-11" />
+<hkern u1="&#xc8;" u2="a" k="18" />
+<hkern u1="&#xc8;" u2="c" k="18" />
+<hkern u1="&#xc8;" u2="d" k="18" />
+<hkern u1="&#xc8;" u2="e" k="18" />
+<hkern u1="&#xc8;" u2="f" k="7" />
+<hkern u1="&#xc8;" u2="g" k="4" />
+<hkern u1="&#xc8;" u2="o" k="18" />
+<hkern u1="&#xc8;" u2="q" k="18" />
+<hkern u1="&#xc8;" u2="v" k="14" />
+<hkern u1="&#xc8;" u2="y" k="14" />
+<hkern u1="&#xc8;" u2="&#xe7;" k="18" />
+<hkern u1="&#xc8;" u2="&#xae;" k="14" />
+<hkern u1="&#xc8;" u2="&#xa9;" k="14" />
+<hkern u1="&#xc8;" u2="&#x3c0;" k="14" />
+<hkern u1="&#xc8;" u2="&#xe6;" k="18" />
+<hkern u1="&#xc8;" u2="&#x153;" k="18" />
+<hkern u1="&#xcd;" u2="/" k="14" />
+<hkern u1="&#xcd;" u2="v" k="7" />
+<hkern u1="&#xcd;" u2="y" k="7" />
+<hkern u1="&#xce;" u2="/" k="14" />
+<hkern u1="&#xce;" u2="v" k="7" />
+<hkern u1="&#xce;" u2="y" k="7" />
+<hkern u1="&#xcf;" u2="/" k="14" />
+<hkern u1="&#xcf;" u2="v" k="7" />
+<hkern u1="&#xcf;" u2="y" k="7" />
+<hkern u1="&#xcc;" u2="/" k="14" />
+<hkern u1="&#xcc;" u2="v" k="7" />
+<hkern u1="&#xcc;" u2="y" k="7" />
+<hkern u1="&#xd3;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd3;" u2="&#x17d;" k="31" />
+<hkern u1="&#xd3;" u2="&#x2c;" k="28" />
+<hkern u1="&#xd3;" u2="." k="28" />
+<hkern u1="&#xd3;" u2="/" k="35" />
+<hkern u1="&#xd3;" u2="?" k="14" />
+<hkern u1="&#xd3;" u2="A" k="14" />
+<hkern u1="&#xd3;" u2="J" k="31" />
+<hkern u1="&#xd3;" u2="T" k="21" />
+<hkern u1="&#xd3;" u2="V" k="14" />
+<hkern u1="&#xd3;" u2="W" k="28" />
+<hkern u1="&#xd3;" u2="X" k="18" />
+<hkern u1="&#xd3;" u2="Y" k="18" />
+<hkern u1="&#xd3;" u2="Z" k="31" />
+<hkern u1="&#xd3;" u2="a" k="4" />
+<hkern u1="&#xd3;" u2="b" k="7" />
+<hkern u1="&#xd3;" u2="c" k="4" />
+<hkern u1="&#xd3;" u2="d" k="4" />
+<hkern u1="&#xd3;" u2="e" k="4" />
+<hkern u1="&#xd3;" u2="h" k="7" />
+<hkern u1="&#xd3;" u2="k" k="7" />
+<hkern u1="&#xd3;" u2="l" k="7" />
+<hkern u1="&#xd3;" u2="m" k="4" />
+<hkern u1="&#xd3;" u2="n" k="4" />
+<hkern u1="&#xd3;" u2="o" k="4" />
+<hkern u1="&#xd3;" u2="p" k="4" />
+<hkern u1="&#xd3;" u2="q" k="4" />
+<hkern u1="&#xd3;" u2="r" k="4" />
+<hkern u1="&#xd3;" u2="u" k="4" />
+<hkern u1="&#xd3;" u2="x" k="7" />
+<hkern u1="&#xd3;" u2="z" k="7" />
+<hkern u1="&#xd3;" u2="&#xc4;" k="14" />
+<hkern u1="&#xd3;" u2="&#xc5;" k="14" />
+<hkern u1="&#xd3;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd3;" u2="&#xc6;" k="14" />
+<hkern u1="&#xd3;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd3;" u2="&#x2026;" k="28" />
+<hkern u1="&#xd3;" u2="&#xc0;" k="14" />
+<hkern u1="&#xd3;" u2="&#xc3;" k="14" />
+<hkern u1="&#xd3;" u2="&#x153;" k="4" />
+<hkern u1="&#xd3;" u2="&#x201c;" k="14" />
+<hkern u1="&#xd3;" u2="&#x2018;" k="14" />
+<hkern u1="&#xd3;" u2="&#x178;" k="18" />
+<hkern u1="&#xd3;" u2="&#x201a;" k="28" />
+<hkern u1="&#xd3;" u2="&#x201e;" k="28" />
+<hkern u1="&#xd3;" u2="&#xc2;" k="14" />
+<hkern u1="&#xd3;" u2="&#xc1;" k="14" />
+<hkern u1="&#xd3;" u2="&#x131;" k="4" />
+<hkern u1="&#xd4;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd4;" u2="&#x17d;" k="31" />
+<hkern u1="&#xd4;" u2="&#x2c;" k="28" />
+<hkern u1="&#xd4;" u2="." k="28" />
+<hkern u1="&#xd4;" u2="/" k="35" />
+<hkern u1="&#xd4;" u2="?" k="14" />
+<hkern u1="&#xd4;" u2="A" k="14" />
+<hkern u1="&#xd4;" u2="J" k="31" />
+<hkern u1="&#xd4;" u2="T" k="21" />
+<hkern u1="&#xd4;" u2="V" k="14" />
+<hkern u1="&#xd4;" u2="W" k="28" />
+<hkern u1="&#xd4;" u2="X" k="18" />
+<hkern u1="&#xd4;" u2="Y" k="18" />
+<hkern u1="&#xd4;" u2="Z" k="31" />
+<hkern u1="&#xd4;" u2="a" k="4" />
+<hkern u1="&#xd4;" u2="b" k="7" />
+<hkern u1="&#xd4;" u2="c" k="4" />
+<hkern u1="&#xd4;" u2="d" k="4" />
+<hkern u1="&#xd4;" u2="e" k="4" />
+<hkern u1="&#xd4;" u2="h" k="7" />
+<hkern u1="&#xd4;" u2="k" k="7" />
+<hkern u1="&#xd4;" u2="l" k="7" />
+<hkern u1="&#xd4;" u2="m" k="4" />
+<hkern u1="&#xd4;" u2="n" k="4" />
+<hkern u1="&#xd4;" u2="o" k="4" />
+<hkern u1="&#xd4;" u2="p" k="4" />
+<hkern u1="&#xd4;" u2="q" k="4" />
+<hkern u1="&#xd4;" u2="r" k="4" />
+<hkern u1="&#xd4;" u2="u" k="4" />
+<hkern u1="&#xd4;" u2="x" k="7" />
+<hkern u1="&#xd4;" u2="z" k="7" />
+<hkern u1="&#xd4;" u2="&#xc4;" k="14" />
+<hkern u1="&#xd4;" u2="&#xc5;" k="14" />
+<hkern u1="&#xd4;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd4;" u2="&#xc6;" k="14" />
+<hkern u1="&#xd4;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd4;" u2="&#x2026;" k="28" />
+<hkern u1="&#xd4;" u2="&#xc0;" k="14" />
+<hkern u1="&#xd4;" u2="&#xc3;" k="14" />
+<hkern u1="&#xd4;" u2="&#x153;" k="4" />
+<hkern u1="&#xd4;" u2="&#x201c;" k="14" />
+<hkern u1="&#xd4;" u2="&#x2018;" k="14" />
+<hkern u1="&#xd4;" u2="&#x178;" k="18" />
+<hkern u1="&#xd4;" u2="&#x201a;" k="28" />
+<hkern u1="&#xd4;" u2="&#x201e;" k="28" />
+<hkern u1="&#xd4;" u2="&#xc2;" k="14" />
+<hkern u1="&#xd4;" u2="&#xc1;" k="14" />
+<hkern u1="&#xd4;" u2="&#x131;" k="4" />
+<hkern u1="&#xd2;" u2="&#xdd;" k="18" />
+<hkern u1="&#xd2;" u2="&#x17d;" k="31" />
+<hkern u1="&#xd2;" u2="&#x2c;" k="28" />
+<hkern u1="&#xd2;" u2="." k="28" />
+<hkern u1="&#xd2;" u2="/" k="35" />
+<hkern u1="&#xd2;" u2="?" k="14" />
+<hkern u1="&#xd2;" u2="A" k="14" />
+<hkern u1="&#xd2;" u2="J" k="31" />
+<hkern u1="&#xd2;" u2="T" k="21" />
+<hkern u1="&#xd2;" u2="V" k="14" />
+<hkern u1="&#xd2;" u2="W" k="28" />
+<hkern u1="&#xd2;" u2="X" k="18" />
+<hkern u1="&#xd2;" u2="Y" k="18" />
+<hkern u1="&#xd2;" u2="Z" k="31" />
+<hkern u1="&#xd2;" u2="a" k="4" />
+<hkern u1="&#xd2;" u2="b" k="7" />
+<hkern u1="&#xd2;" u2="c" k="4" />
+<hkern u1="&#xd2;" u2="d" k="4" />
+<hkern u1="&#xd2;" u2="e" k="4" />
+<hkern u1="&#xd2;" u2="h" k="7" />
+<hkern u1="&#xd2;" u2="k" k="7" />
+<hkern u1="&#xd2;" u2="l" k="7" />
+<hkern u1="&#xd2;" u2="m" k="4" />
+<hkern u1="&#xd2;" u2="n" k="4" />
+<hkern u1="&#xd2;" u2="o" k="4" />
+<hkern u1="&#xd2;" u2="p" k="4" />
+<hkern u1="&#xd2;" u2="q" k="4" />
+<hkern u1="&#xd2;" u2="r" k="4" />
+<hkern u1="&#xd2;" u2="u" k="4" />
+<hkern u1="&#xd2;" u2="x" k="7" />
+<hkern u1="&#xd2;" u2="z" k="7" />
+<hkern u1="&#xd2;" u2="&#xc4;" k="14" />
+<hkern u1="&#xd2;" u2="&#xc5;" k="14" />
+<hkern u1="&#xd2;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd2;" u2="&#xc6;" k="14" />
+<hkern u1="&#xd2;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd2;" u2="&#x2026;" k="28" />
+<hkern u1="&#xd2;" u2="&#xc0;" k="14" />
+<hkern u1="&#xd2;" u2="&#xc3;" k="14" />
+<hkern u1="&#xd2;" u2="&#x153;" k="4" />
+<hkern u1="&#xd2;" u2="&#x201c;" k="14" />
+<hkern u1="&#xd2;" u2="&#x2018;" k="14" />
+<hkern u1="&#xd2;" u2="&#x178;" k="18" />
+<hkern u1="&#xd2;" u2="&#x201a;" k="28" />
+<hkern u1="&#xd2;" u2="&#x201e;" k="28" />
+<hkern u1="&#xd2;" u2="&#xc2;" k="14" />
+<hkern u1="&#xd2;" u2="&#xc1;" k="14" />
+<hkern u1="&#xd2;" u2="&#x131;" k="4" />
+<hkern u1="&#xda;" u2="&#x2c;" k="7" />
+<hkern u1="&#xda;" u2="." k="7" />
+<hkern u1="&#xda;" u2="J" k="7" />
+<hkern u1="&#xda;" u2="&#x2026;" k="7" />
+<hkern u1="&#xda;" u2="&#x201a;" k="7" />
+<hkern u1="&#xda;" u2="&#x201e;" k="7" />
+<hkern u1="&#xdb;" u2="&#x2c;" k="7" />
+<hkern u1="&#xdb;" u2="." k="7" />
+<hkern u1="&#xdb;" u2="J" k="7" />
+<hkern u1="&#xdb;" u2="&#x2026;" k="7" />
+<hkern u1="&#xdb;" u2="&#x201a;" k="7" />
+<hkern u1="&#xdb;" u2="&#x201e;" k="7" />
+<hkern u1="&#xd9;" u2="&#x2c;" k="7" />
+<hkern u1="&#xd9;" u2="." k="7" />
+<hkern u1="&#xd9;" u2="J" k="7" />
+<hkern u1="&#xd9;" u2="&#x2026;" k="7" />
+<hkern u1="&#xd9;" u2="&#x201a;" k="7" />
+<hkern u1="&#xd9;" u2="&#x201e;" k="7" />
+<hkern u1="&#xa4;" u2="4" k="20" />
+<hkern u1="&#xad;" u2="&#xdd;" k="50" />
+<hkern u1="&#xad;" u2="&#x17d;" k="10" />
+<hkern u1="&#xad;" u2="1" k="20" />
+<hkern u1="&#xad;" u2="7" k="30" />
+<hkern u1="&#xad;" u2="A" k="-10" />
+<hkern u1="&#xad;" u2="T" k="70" />
+<hkern u1="&#xad;" u2="V" k="50" />
+<hkern u1="&#xad;" u2="W" k="40" />
+<hkern u1="&#xad;" u2="X" k="30" />
+<hkern u1="&#xad;" u2="Y" k="50" />
+<hkern u1="&#xad;" u2="Z" k="10" />
+<hkern u1="&#xad;" u2="a" k="10" />
+<hkern u1="&#xad;" u2="c" k="10" />
+<hkern u1="&#xad;" u2="d" k="10" />
+<hkern u1="&#xad;" u2="e" k="10" />
+<hkern u1="&#xad;" u2="o" k="10" />
+<hkern u1="&#xad;" u2="q" k="10" />
+<hkern u1="&#xad;" u2="v" k="10" />
+<hkern u1="&#xad;" u2="x" k="30" />
+<hkern u1="&#xad;" u2="y" k="10" />
+<hkern u1="&#xad;" u2="z" k="30" />
+<hkern u1="&#xad;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe7;" k="10" />
+<hkern u1="&#xad;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe6;" k="10" />
+<hkern u1="&#xad;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xad;" u2="&#x153;" k="10" />
+<hkern u1="&#xad;" u2="&#x178;" k="50" />
+<hkern u1="&#xad;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xd0;" u2="&#xdd;" k="25" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="45" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="40" />
+<hkern u1="&#xd0;" u2="." k="40" />
+<hkern u1="&#xd0;" u2="/" k="50" />
+<hkern u1="&#xd0;" u2="?" k="20" />
+<hkern u1="&#xd0;" u2="A" k="17" />
+<hkern u1="&#xd0;" u2="J" k="45" />
+<hkern u1="&#xd0;" u2="T" k="32" />
+<hkern u1="&#xd0;" u2="V" k="19" />
+<hkern u1="&#xd0;" u2="W" k="33" />
+<hkern u1="&#xd0;" u2="X" k="25" />
+<hkern u1="&#xd0;" u2="Y" k="25" />
+<hkern u1="&#xd0;" u2="Z" k="45" />
+<hkern u1="&#xd0;" u2="a" k="5" />
+<hkern u1="&#xd0;" u2="b" k="10" />
+<hkern u1="&#xd0;" u2="c" k="5" />
+<hkern u1="&#xd0;" u2="d" k="5" />
+<hkern u1="&#xd0;" u2="e" k="5" />
+<hkern u1="&#xd0;" u2="h" k="10" />
+<hkern u1="&#xd0;" u2="k" k="10" />
+<hkern u1="&#xd0;" u2="l" k="10" />
+<hkern u1="&#xd0;" u2="m" k="5" />
+<hkern u1="&#xd0;" u2="n" k="5" />
+<hkern u1="&#xd0;" u2="o" k="5" />
+<hkern u1="&#xd0;" u2="p" k="5" />
+<hkern u1="&#xd0;" u2="q" k="5" />
+<hkern u1="&#xd0;" u2="r" k="5" />
+<hkern u1="&#xd0;" u2="u" k="5" />
+<hkern u1="&#xd0;" u2="v" k="-5" />
+<hkern u1="&#xd0;" u2="x" k="9" />
+<hkern u1="&#xd0;" u2="y" k="-5" />
+<hkern u1="&#xd0;" u2="z" k="10" />
+<hkern u1="&#xd0;" u2="&#xc4;" k="17" />
+<hkern u1="&#xd0;" u2="&#xc5;" k="17" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="5" />
+<hkern u1="&#xd0;" u2="&#xc6;" k="17" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="5" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="40" />
+<hkern u1="&#xd0;" u2="&#xc0;" k="17" />
+<hkern u1="&#xd0;" u2="&#xc3;" k="17" />
+<hkern u1="&#xd0;" u2="&#x153;" k="5" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd0;" u2="&#x178;" k="25" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="40" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="40" />
+<hkern u1="&#xd0;" u2="&#xc2;" k="17" />
+<hkern u1="&#xd0;" u2="&#xc1;" k="17" />
+<hkern u1="&#xd0;" u2="&#x131;" k="5" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02 Light" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02 Light" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02 Light" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02 Light" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.ttf
new file mode 100644
index 0000000000..273761ab3d
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.woff
new file mode 100644
index 0000000000..b12120a86e
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Light.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.eot
new file mode 100644
index 0000000000..2da6a99ec6
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.svg
new file mode 100644
index 0000000000..ab6936ceeb
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.svg
@@ -0,0 +1,3030 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Light Italic]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated in 2010 by FontLab Studio. Copyright info pending.]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="546" id="Omnes_ATTW02LightItalic">
+<font-face font-family="Omnes_ATT W02 Light Italic" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="700" descent="-300" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="176" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="176" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="266" d="M125 172Q114 172 111 176T109 193L189 647H254L138 187Q135 172 125 172ZM88 -5Q51 -5 51 22Q51 50 64 62Q74 72 92 72Q129 72 129 46Q129 18 117 5Q107 -5 88 -5Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="366" d="M293 409Q282 409 278 413T276 431L304 647H373L309 428Q303 409 293 409ZM157 409Q146 409 142 413T140 431L168 647H237L173 428Q167 409 157 409Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="659" d="M155 -1Q155 -2 148 -3T133 0T122 18T126 57L165 184H31Q32 185 32 187Q32 192 33 196L39 222Q39 224 39 224T40 226V227H178L238 420H105Q106 421 106 423Q106 428 107 432L113 458Q113 459 113 460T114 462V463H250L307 648Q307 649 314 650T329 647T340 629T336 590L297 463H472L529 648Q529 649 536 650T551 647T562 629T558 590L519 463H652Q651 462 651 460Q651 458 651 456T650 451L644 425Q643 423 643 421V420H505L446 227H579Q578 226 578 224Q578 222 578 220T577 215L571 190Q570 188 570 185V184H433L377 -1Q377 -2 370 -3T355 0T344 18T348 57L387 184H212L155 -1ZM401 226L461 421H283L223 226H401Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="564" d="M207 -5Q159 3 125 21T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q78 116 115 83T217 38L276 311Q246 323 218 337T169 372T134 418T121 482Q121 519 136 551T178 606T245 643T334 657H348L367 742H411L392 653Q436 645 466 630T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q503 541 470 570T380 609L324 345Q357 332 387 318T440 283T476 236T490 172Q490 91 433 41T269 -10H250L231 -98H187L207 -5ZM272 33Q356 33 398 69T440 167Q440 192 430 211T403 245T362 272T311 296L256 34Q260 34 264 34T272 33ZM342 613Q338 614 331 614Q295 614 266 605T216 578T184 537T172 487Q172 462 180 443T204 409T241 383T288 360L342 613Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="742" d="M235 358Q209 358 186 367T145 393T117 435T106 489Q106 522 118 552T151 606T202 642T266 656Q293 656 316 647T357 621T385 579T395 525Q395 492 383 462T350 408T299 372T235 358ZM62 -7Q60 -9 51 -4T42 13Q42 23 47 31T69 54L718 658Q720 660 728 655T737 638Q737 629 732 621T710 598L62 -7ZM514 -5Q488 -5 465 4T423 30T395 72T384 126Q384 159 396 189T429 243T480 279T544 293Q571 293 594 284T635 258T663 216T673 162Q673 129 661 99T628 45T578 9T514 -5ZM238 396Q263 396 284 406T320 434T345 474T354 523Q354 565 328 591T263 618Q238 618 217 608T181 580T156 540T147 491Q147 449 174 423T238 396ZM517 33Q541 33 562 43T599 71T624 111T633 160Q633 202 606 228T541 255Q517 255 496 245T459 217T434 177T425 128Q425 86 452 60T517 33Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="675" d="M240 -10Q192 -10 154 3T90 39T50 95T36 165Q36 249 91 300T252 375Q235 400 225 428T215 488Q215 522 227 552T262 606T316 643T385 657Q448 657 486 620T524 525Q524 451 473 410T325 352L481 147Q527 200 550 257T580 377H625Q619 308 590 241T507 114L520 95Q548 58 570 45T621 33Q622 33 622 27T621 15T612 2T588 -4Q557 -4 536 12T488 63L474 82Q428 40 370 15T240 -10ZM300 385Q343 392 376 402T432 428T466 467T478 524Q478 565 452 590T380 616Q353 616 332 606T295 578T271 538T263 491Q263 461 272 437T300 385ZM246 33Q309 33 358 54T449 114L275 342Q178 324 131 279T84 167Q84 140 94 116T124 73T174 44T246 33Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="230" d="M157 409Q146 409 142 413T140 431L168 647H238L173 428Q167 409 157 409Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="385" d="M249 -197Q232 -197 208 -185T157 -147Q115 -105 90 -38T64 120Q64 182 76 243T110 360T165 467T237 557Q284 604 332 627T418 651Q431 651 436 646T443 633T441 619T434 610Q392 608 352 588T269 526Q232 489 204 442T155 342T125 232T114 120Q114 45 134 -15T195 -115Q213 -133 230 -143T269 -160Q271 -161 272 -167T273 -179T267 -191T249 -197Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="385" d="M-36 -196Q-48 -196 -53 -191T-60 -178T-58 -164T-51 -155Q-9 -153 31 -133T114 -71Q150 -34 179 13T227 113T257 222T268 335Q268 410 248 470T188 570Q170 588 153 598T114 615Q112 616 111 621T110 634T116 646T134 652Q151 652 175 640T226 602Q267 560 292 493T318 335Q318 273 306 212T272 95T217 -12T145 -102Q98 -149 50 -172T-36 -196Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="411" d="M167 372Q159 378 169 394L233 487L126 526Q113 531 113 540Q113 565 138 559L247 527L250 640Q251 652 255 655T267 659Q275 659 279 656T284 640L287 527L396 559Q421 565 421 540Q421 532 407 526L301 487L365 394Q375 378 367 372Q360 365 353 364T337 374L267 463L197 374Q189 363 182 364T167 372Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="554" d="M235 38Q227 38 221 42T218 61L257 245H88Q66 245 66 257Q66 262 68 270T75 283Q82 288 93 288H266L303 460Q307 483 324 483H330Q338 483 344 479T348 460L311 288H480Q502 288 502 276Q502 271 500 263T493 250Q488 245 474 245H302L263 61Q258 38 241 38H235Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="225" d="M-18 -130Q-20 -132 -24 -131T-33 -128T-39 -122T-40 -115L64 78Q65 79 72 79T87 76T102 68T109 53Q109 45 106 38T95 19L-18 -130Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="385" d="M60 226Q60 227 61 229Q61 231 63 239L68 264Q68 266 68 267T69 269V270H330Q329 269 329 267Q329 265 329 263T328 258L322 232Q321 230 321 227V226H60Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="225" d="M68 -5Q31 -5 31 22Q31 50 43 62Q53 72 72 72Q109 72 109 46Q109 18 96 5Q86 -5 68 -5Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="351" d="M-76 -193L361 647H415L-22 -193H-76Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="656" d="M313 -10Q256 -10 213 7T141 56T98 130T83 224Q83 259 89 296T108 387Q121 445 143 494T198 580T275 636T380 657Q437 657 480 640T552 591T595 516T610 423Q610 388 604 351T585 259Q571 202 550 153T496 67T418 11T313 -10ZM314 33Q364 33 401 51T464 100T508 174T537 265Q549 316 554 354T560 423Q560 465 548 500T513 560T457 600T380 614Q330 614 293 596T229 547T185 473T156 381Q144 330 139 293T133 224Q133 182 145 147T180 87T237 47T314 33Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="370" d="M138 0L256 558Q242 541 220 531T176 521Q153 521 140 525T118 538Q108 548 108 563Q108 579 118 588T130 596Q137 585 151 578T189 571Q221 571 247 594T282 650H303Q326 650 320 623L188 0H138Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="545" d="M126 169Q103 146 90 116T74 46H456L447 0H52Q41 0 33 11T24 35Q24 86 41 129T91 204Q129 242 178 266T284 310Q336 329 374 346T436 383T473 428T485 488Q485 542 444 577T329 612Q259 612 217 577T152 491Q151 490 147 490T137 494T127 503T122 519Q122 530 132 550T162 591Q189 618 231 637T331 657Q427 657 481 611T536 488Q536 441 521 408T475 350T401 304T300 263Q241 242 200 222T126 169Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="553" d="M238 -10Q177 -10 130 8T57 53Q33 77 24 100T14 137Q14 149 19 156T30 168T42 172T48 172Q52 143 67 118T106 73T164 44T241 33Q286 33 320 46T378 81T413 132T425 193Q425 250 386 285T268 320H185L194 363H279Q334 363 370 374T429 403T461 447T471 500Q471 525 460 546T429 582T382 606T322 615Q263 615 216 588T143 522Q142 521 138 522T130 526T122 535T118 546Q118 565 142 589Q171 618 217 637T325 657Q367 657 403 647T466 617T508 568T524 502Q524 439 484 393T365 336Q421 321 449 283T477 194Q477 157 463 121T420 55T346 8T238 -10Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="594" d="M334 0L374 193H60Q58 193 51 204T43 228Q43 241 49 251T69 276L424 623Q441 639 452 645T478 652Q502 652 509 642T513 615L432 238H553L544 193H423L382 0H334ZM385 238L463 604L92 238H385Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="555" d="M157 371Q187 385 217 390T280 396Q330 396 370 384T438 348T481 292T496 220Q496 172 480 131T432 58T355 8T251 -10Q188 -10 142 8T66 56Q47 76 39 94T31 126Q31 135 35 141T46 150T57 155T63 155Q77 103 126 69T253 34Q298 34 333 48T393 88T431 146T444 217Q444 280 402 316T276 352Q230 352 195 343T130 317Q113 321 108 330T105 356L159 615Q167 647 197 647H556L547 602H205L157 371Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="605" d="M302 -10Q254 -10 214 4T145 46T100 117T83 217Q83 248 89 296T111 397T150 500T210 586Q244 620 284 638T390 657Q444 657 481 641T541 602Q558 585 565 569T572 543Q572 534 568 528T558 518T548 513T542 513Q525 559 484 587T385 615Q286 615 228 539T143 323Q172 352 218 374T331 396Q377 396 415 384T480 350T522 295T537 221Q537 175 520 133T471 60T397 9T302 -10ZM303 34Q345 34 379 48T438 87T476 145T490 218Q490 279 448 317T325 355Q259 355 212 331T134 272Q132 257 131 243T130 216Q130 126 176 80T303 34Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="534" d="M124 -3Q123 -3 115 -2T100 6T93 25T111 61L513 602H115L124 647H537Q538 647 542 644T550 636T557 624T561 607Q561 599 558 590T543 567L124 -3Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="600" d="M280 -10Q227 -10 184 2T111 36T64 89T47 158Q47 192 60 222T99 277T159 318T238 342Q187 357 161 389T135 469Q135 508 152 542T199 601T272 642T366 657Q411 657 449 646T514 615T557 565T573 501Q573 471 562 443T530 393T478 354T404 330Q467 312 496 273T526 186Q526 144 509 109T459 47T382 5T280 -10ZM347 357Q430 357 476 395T523 497Q523 551 479 583T359 615Q319 615 287 604T232 574T197 529T185 471Q185 418 228 388T347 357ZM282 32Q372 32 424 75T476 186Q476 241 428 277T289 314Q247 314 212 303T152 271T112 223T97 161Q97 104 146 68T282 32Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="605" d="M249 -10Q194 -10 157 7T97 46Q80 63 73 79T66 106Q66 115 70 121T79 131T89 135T95 135Q113 89 154 61T255 33Q353 33 411 106T497 314Q468 285 421 263T310 241Q214 241 159 287T104 421Q104 469 121 512T170 587T245 638T340 657Q388 657 428 643T497 601T542 530T559 430Q559 399 553 351T531 251T492 148T431 62Q397 27 356 9T249 -10ZM316 282Q379 282 427 306T506 365Q512 401 512 431Q512 521 466 567T339 613Q297 613 263 599T203 560T165 500T151 424Q151 358 191 320T316 282Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="249" d="M155 349Q118 349 118 376Q118 402 130 416Q140 426 159 426Q196 426 196 399Q196 371 183 359Q173 349 155 349ZM80 -5Q43 -5 43 22Q43 50 55 62Q65 72 84 72Q121 72 121 46Q121 18 108 5Q98 -5 80 -5Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="249" d="M-6 -130Q-8 -132 -12 -131T-21 -128T-27 -122T-28 -115L76 78Q77 79 84 79T99 76T114 68T121 53Q121 45 118 38T107 19L-6 -130ZM155 349Q118 349 118 376Q118 402 130 416Q140 426 159 426Q196 426 196 399Q196 371 183 359Q173 349 155 349Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="495" d="M86 232Q68 242 68 262Q68 274 74 284T99 304L454 479Q456 480 462 474T470 456Q470 448 464 440T437 421L117 264L363 112Q381 102 387 93T392 75Q390 64 383 58T374 53L86 232Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="554" d="M122 349Q99 349 99 361Q99 367 101 376T108 389Q113 394 128 394H490Q514 394 514 382Q514 376 511 368T504 355Q498 349 485 349H122ZM74 128Q51 128 51 140Q51 146 54 154T61 168Q68 173 80 173H443Q466 173 466 161Q466 155 464 146T456 133Q452 128 437 128H74Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="495" d="M53 53Q51 52 45 59T38 77Q37 85 43 93T70 112L391 269L144 420Q126 431 120 440T116 457Q118 469 124 475T134 479L422 301Q440 289 440 271Q440 258 434 248T408 229L53 53Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="492" d="M182 210Q187 235 195 254T217 289T254 318T315 344Q378 366 414 398T451 489Q451 543 413 578T307 613Q244 613 202 578T141 488Q140 487 135 488T124 492T114 500T109 515Q109 529 120 551T153 595Q180 622 219 639T310 657Q353 657 388 645T448 610T486 556T500 489Q500 449 487 420T451 369T401 334T345 310Q311 298 290 288T257 265T238 238T228 204L221 174H174L182 210ZM178 -5Q140 -5 140 22Q140 50 153 62Q163 72 181 72Q219 72 219 46Q219 20 206 5Q196 -5 178 -5Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="769" d="M400 470Q440 470 467 450T504 401L517 463H563L526 287Q518 247 531 226T580 204Q607 204 629 218T666 258T690 317T699 389Q699 441 680 483T627 557T544 605T438 622Q370 622 312 595T211 521T144 413T119 282Q119 226 138 180T193 99T279 45T393 26Q442 26 487 40T561 74Q567 79 573 81T587 84Q597 84 603 73T607 59Q591 46 568 34T517 12T456 -4T390 -10Q318 -10 261 11T164 70T103 162T81 279Q81 356 108 424T184 545T299 627T442 657Q509 657 563 638T656 585T716 501T737 391Q737 343 726 302T693 231T641 184T574 167Q528 167 505 187T478 238Q439 171 360 171Q310 171 277 203T243 292Q243 325 254 357T287 414T337 454T400 470ZM372 209Q393 209 411 217T445 238T470 269T484 307L494 354Q488 387 466 409T403 431Q377 431 357 420T321 389T298 346T290 297Q290 256 313 233T372 209Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="627" d="M175 616Q180 647 212 647H391Q488 647 536 611T585 504Q585 474 577 447T551 397T505 358T435 332Q495 319 528 285T561 198Q561 157 548 121T506 58T428 16T309 0H45L175 616ZM312 45Q419 45 464 84T510 198Q510 252 469 280T352 309H160L104 45H312ZM356 353Q401 353 434 364T489 394T523 440T534 498Q531 602 386 602H223L169 353H356Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="681" d="M365 -10Q300 -10 248 9T158 64T100 151T79 266Q79 345 106 416T180 540T293 625T434 657Q505 657 550 636T623 587Q646 564 656 540T666 501Q666 491 662 485T652 476T641 471T633 472Q615 538 565 576T431 614Q366 614 312 587T217 513T155 404T132 273Q132 218 149 174T196 98T270 51T365 34Q405 34 438 43T499 69T545 106T579 150Q580 151 584 150T592 146T600 138T604 124Q604 115 596 99T568 63Q538 34 485 12T365 -10Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="682" d="M176 616Q181 647 212 647H319Q471 647 554 576T638 376Q638 297 613 229T537 109T413 29T239 0H45L176 616ZM247 44Q332 44 395 69T501 139T564 243T585 369Q585 479 516 541T316 603H223L105 44H247Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="557" d="M176 616Q181 647 212 647H598L589 602H223L166 333H444L435 289H156L94 0H45L176 616Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="725" d="M363 -10Q301 -10 249 8T160 62T101 150T80 268Q80 344 106 414T179 538T292 624T437 657Q509 657 558 635T634 585Q653 566 662 546T672 511Q672 499 667 492T656 482T645 479T638 479Q631 506 614 530T570 573T510 602T436 613Q369 613 313 585T217 511T155 402T132 273Q132 215 149 170T197 95T270 49T364 33Q414 33 456 49T529 97Q546 113 558 130T579 167T596 212T611 273H389L398 318H630Q666 318 658 283L598 0Q598 -3 587 -3Q573 -3 566 8T564 49L574 98Q548 52 494 21T363 -10Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="694" d="M178 627Q184 650 203 650H210Q233 650 228 624L169 347H572L631 627Q637 650 656 650H663Q687 650 681 624L548 0H499L562 301H159L95 0H46L178 627Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="470" d="M176 -10Q131 -10 99 4T46 38Q31 54 25 66T19 87Q19 96 23 102T32 112T42 117T48 117Q62 80 96 57T176 33Q238 33 273 71T326 192L423 647H472L374 185Q353 85 305 38T176 -10Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="559" d="M456 -4Q444 -4 434 0T412 13T388 39T356 78L165 329L94 0H45L178 627Q183 650 203 650H210Q233 650 228 624L168 342L559 656Q560 656 567 653T579 643T581 623T556 594L220 329L402 96Q418 75 430 62T452 42T472 32T494 29Q495 29 495 24T492 13T481 1T456 -4Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="520" d="M178 627Q184 650 203 650H210Q233 650 228 624L105 46H419Q442 46 442 33Q442 27 440 19T432 5Q428 0 412 0H45L178 627Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="769" d="M176 616Q184 650 217 650H228Q247 650 253 641T264 613L378 221L661 613Q676 635 685 642T712 650H725Q742 650 749 640T753 615L622 0H575L701 593L419 204Q408 188 398 182T372 175Q352 175 345 184T333 210L219 593L92 0H45L176 616Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="704" d="M176 616Q184 650 211 650H224Q236 650 242 643T256 622L527 73L650 647H696L564 27Q561 13 553 5T532 -3H526Q515 -3 509 4T494 25L217 588L91 0H45L176 616Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="578" d="M176 616Q181 647 212 647H357Q463 647 518 604T573 480Q573 374 507 315T316 256H149L94 0H45L176 616ZM325 300Q419 300 470 346T522 474Q522 537 481 569T354 602H223L159 300H325Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="770" d="M360 -10Q294 -10 242 11T154 69T98 157T79 269Q79 353 109 424T188 547T303 628T438 657Q504 657 556 636T644 578T700 490T719 378Q719 287 685 212T592 85L601 74Q623 46 638 37T671 25Q672 25 672 20T671 8T662 -3T643 -9Q622 -9 607 2T567 45L558 56Q514 24 464 7T360 -10ZM365 34Q410 34 451 48T530 90L406 250L441 278L563 121Q610 166 638 231T666 375Q666 425 651 468T605 544T532 595T433 614Q375 614 321 589T224 517T157 409T132 273Q132 223 148 179T194 103T267 53T365 34Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="593" d="M299 277L418 97Q430 78 440 66T460 46T481 35T508 32Q509 32 509 27T507 14T496 2T470 -4Q458 -4 448 -1T426 11T403 36T374 78L243 277H154L94 0H45L176 616Q181 647 212 647H366Q471 647 525 607T580 490Q580 390 516 334T335 277H299ZM336 320Q432 320 480 365T528 485Q528 542 486 572T363 603H223L163 320H336Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="564" d="M269 -10Q200 -10 149 11T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q72 132 89 110T132 70T193 43T272 33Q356 33 398 69T440 167Q440 195 428 215T396 251T348 279T289 305Q257 318 227 332T174 367T136 415T121 482Q121 519 136 551T178 606T245 643T334 657Q401 657 446 638T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q509 527 494 547T455 582T402 605T332 614Q296 614 267 605T216 578T184 537T172 487Q172 458 183 437T215 400T263 371T324 345Q355 332 385 318T438 284T476 237T490 172Q490 91 433 41T269 -10Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="603" d="M355 602H144Q121 602 121 614Q121 620 123 628T131 642Q135 647 151 647H624Q647 647 647 635Q647 629 645 621T637 607Q635 603 630 603T617 602H405L277 0H227L355 602Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="625" d="M118 635Q118 638 125 643T141 649T160 646T172 622L281 53L627 650Q628 650 636 650T652 645T661 626T648 588L300 0H244L118 635Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="910" d="M154 640Q154 643 162 647T179 651T197 644T205 619L221 45L476 521Q483 535 491 541T517 547Q538 547 545 538T555 513L611 46L879 648Q879 649 888 649T905 646T915 628T905 588L638 2Q638 1 633 1T622 0T611 0T606 0Q605 0 600 0T588 0T576 0T570 2L512 497L243 0H178L154 640Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="595" d="M497 -9Q469 -9 449 12T406 83L307 291L21 -9Q21 -9 13 -7T-2 2T-8 20T11 50L285 329L140 647H194L322 357L606 656Q607 656 615 654T629 645T635 627T616 597L345 319L448 105Q459 81 468 67T488 45T509 35T534 33Q535 33 535 27T533 12T522 -2T497 -9Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="569" d="M271 279L108 647H162L303 322L584 651Q585 652 593 650T608 642T614 622T593 586L320 274L262 0H212L271 279Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="591" d="M7 34L9 44L542 602H165Q136 602 136 620Q136 628 141 637T147 647H617L610 613L608 603L74 46H471Q500 46 500 27Q500 19 495 10T488 0H0L7 34Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="356" d="M189 647H375Q397 647 397 635Q397 630 395 622T388 609Q383 604 369 604H225L65 -149H206Q228 -149 228 -161Q228 -167 226 -175T219 -188Q212 -193 200 -193H10L189 647Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="351" d="M114 647H162L240 -193H192L114 647Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="356" d="M163 -193H-23Q-45 -193 -45 -181Q-45 -176 -43 -168T-36 -155Q-30 -149 -17 -149H127L287 604H146Q124 604 124 615Q124 621 126 629T133 642Q138 647 152 647H342L163 -193Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="534" d="M211 450Q197 450 197 463Q197 475 212 493T263 544Q298 576 321 592T346 609Q348 609 363 593T399 546Q422 512 429 496T437 471Q437 461 432 456T419 450Q409 450 399 462T369 513L340 566Q326 552 316 540T288 511Q256 478 239 464T211 450Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="486" d="M-3 -46Q-26 -46 -26 -33Q-26 -28 -24 -19T-17 -6Q-12 0 3 0H379Q402 0 402 -13Q402 -18 400 -26T393 -40Q387 -46 373 -46H-3Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="534" d="M402 547Q400 545 389 548T359 561T317 584T270 619Q250 636 244 645T238 665Q238 674 245 680T261 686Q269 686 279 681T304 661Q328 641 346 621T378 585T397 559T402 547Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="598" d="M290 -10Q254 -10 225 0T172 26T132 65T107 112L93 46Q87 19 78 8T55 -3Q47 -3 42 -1T36 3L179 674H227L164 382Q196 428 241 455T346 483Q386 483 422 470T485 429T529 363T545 273Q545 219 526 168T474 78T393 14T290 -10ZM293 32Q340 32 378 52T442 107T483 184T497 273Q497 312 485 343T451 396T402 429T341 441Q309 441 280 431T227 404T183 363T150 312L129 218Q123 184 131 151T160 92T214 49T293 32Z" />
+<glyph unicode="c" glyph-name="c" d="M273 -10Q227 -10 187 4T117 46T70 114T53 206Q53 260 73 310T128 398T212 460T316 483Q374 483 412 466T474 424Q495 403 504 380T514 341Q514 330 509 324T497 314T485 311T478 313Q474 339 462 362T430 402T381 430T317 440Q270 440 231 421T163 369T118 295T102 209Q102 168 115 136T151 81T206 46T277 33Q336 33 375 61T437 127Q438 128 442 127T452 124T461 116T465 104Q465 84 434 52Q410 29 369 10T273 -10Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="600" d="M496 -7Q462 -7 444 17T436 90Q403 42 359 16T255 -10Q215 -10 179 4T116 44T73 110T57 200Q57 254 75 305T128 395T209 459T312 483Q382 483 428 449T495 365L561 674H609L485 95Q476 54 488 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM261 32Q298 32 331 46T391 83T436 138T461 205L481 296Q467 359 424 400T309 441Q262 441 224 421T160 366T119 289T105 200Q105 161 117 130T151 77T200 44T261 32Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="345" d="M79 0L170 430H73L81 473H179L192 531Q206 600 241 642T339 685Q368 685 387 678T419 658Q431 646 431 631Q431 618 423 611T412 606Q403 622 383 632T336 643Q293 643 271 611T237 524L225 473H351L342 430H217L126 0H79Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="598" d="M225 -197Q162 -197 120 -180T53 -137Q34 -118 27 -101T19 -72Q19 -60 24 -53T35 -42T47 -38T54 -38Q66 -86 107 -119T229 -153Q383 -153 419 18L438 105Q407 59 361 32T254 5Q215 5 180 18T116 58T72 123T55 212Q55 264 74 312T129 399T211 460T315 483Q350 483 379 474T431 448T470 411T494 366L518 473H564L467 14Q445 -88 386 -142T225 -197ZM260 47Q297 47 330 60T390 95T435 147T461 211L480 298Q469 361 425 401T312 441Q266 441 228 422T162 370T119 296T104 212Q104 173 117 143T151 91T201 59T260 47Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="569" d="M175 654Q179 676 199 676H206Q217 676 220 669T222 649L163 378Q192 426 237 454T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L175 654Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="230" d="M200 601Q166 601 166 627Q166 643 175 655T201 667Q235 667 235 641Q235 625 226 613T200 601ZM122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="230" d="M200 601Q166 601 166 627Q166 643 175 655T201 667Q235 667 235 641Q235 625 226 613T200 601ZM-58 -199Q-88 -199 -107 -191T-137 -173Q-148 -162 -148 -151Q-148 -137 -139 -128T-128 -121Q-117 -135 -99 -145T-56 -156Q-25 -156 -5 -136T27 -65L142 473H189L73 -73Q59 -139 25 -169T-58 -199Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="461" d="M363 -7Q350 -7 340 -4T318 9T292 36T258 83L136 246L83 0H35L179 674H227L138 257L433 482Q434 483 441 480T453 469T454 450T430 422L190 244L304 97Q320 74 331 62T353 43T375 36T401 34Q401 34 401 28T399 14T388 0T363 -7Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L185 674H232L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="890" d="M784 -7Q757 -7 741 10T724 59Q724 79 730 110T748 181Q760 230 767 263T774 320Q774 376 744 407T664 438Q603 438 562 408T488 320L420 0H372L432 282Q439 316 435 345T416 394T379 426T328 438Q269 438 228 409T152 323L83 0H35L137 473Q137 476 147 476Q161 476 170 466T173 424L166 389Q197 434 240 458T334 483Q391 483 430 452T479 368Q508 418 555 450T670 483Q704 483 732 472T780 440T812 391T824 328Q824 302 816 264T794 175Q783 136 777 108T771 66Q771 43 783 36T816 26Q816 26 816 21T814 10T804 -2T784 -7Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="569" d="M462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L137 473Q137 476 147 476Q161 476 170 466T173 424L163 379Q193 427 237 455T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="598" d="M137 473Q137 476 147 476Q161 476 170 466T173 424L164 382Q196 428 241 455T346 483Q386 483 422 470T485 429T529 363T545 273Q545 219 526 168T474 78T393 14T290 -10Q253 -10 223 0T169 28T130 67T107 112L43 -190H-5L137 473ZM293 32Q340 32 378 52T442 107T483 184T497 273Q497 312 485 343T451 396T402 429T341 441Q309 441 280 431T227 404T183 363T150 312L129 218Q123 184 131 151T160 92T214 49T293 32Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="600" d="M437 91Q405 45 360 18T255 -10Q215 -10 179 4T116 44T73 110T57 200Q57 254 75 305T128 395T209 459T312 483Q347 483 376 474T429 449T469 413T496 368L508 426Q514 454 523 465T547 476Q555 476 560 474T565 470L424 -190H377L437 91ZM261 32Q298 32 331 46T391 83T436 138T461 205L481 296Q467 359 427 400T309 441Q262 441 224 421T160 366T119 289T105 200Q105 161 117 130T151 77T200 44T261 32Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="362" d="M137 473Q137 476 147 476Q161 476 170 466T173 424L163 375Q189 431 223 457T302 483Q331 483 351 471T372 442Q372 425 363 418T352 411Q343 422 328 430T289 438Q260 438 235 422T190 376T155 304T128 209L84 0H36L137 473Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="475" d="M218 -10Q161 -10 117 7T47 51Q36 62 29 73T22 94Q22 102 26 107T34 116T43 121T49 122Q68 85 112 58T221 30Q284 30 318 55T352 122Q352 141 343 154T318 177T279 196T230 214Q203 223 177 233T131 259T98 295T85 348Q85 376 97 401T132 444T186 472T257 483Q311 483 351 467T415 427Q438 402 438 383Q438 375 434 370T425 361T416 357T410 358Q393 395 351 419T253 443Q223 443 201 436T163 416T140 387T132 354Q132 334 142 319T169 293T209 273T258 254Q284 245 309 235T354 210T386 176T398 127Q398 65 351 28T218 -10Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="362" d="M203 -10Q139 -10 109 31T95 156L153 430H77L85 473H162L176 539Q181 567 190 578T215 589Q222 589 227 587T233 583L210 473H362L353 430H201L142 156Q128 90 146 61T210 31Q232 31 250 40T278 63Q280 64 285 58T290 40Q290 27 278 15Q253 -10 203 -10Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="488" d="M114 343Q106 399 89 419T40 438Q40 438 40 444T41 457T51 471T73 477Q102 477 122 457Q137 442 146 413T161 339L200 48L461 475Q462 476 470 476T485 470T493 452T480 418L219 0H162L114 343Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="713" d="M126 341Q126 371 122 390T109 420T88 435T60 440Q59 440 59 446T61 458T73 471T97 477Q124 477 142 459Q173 428 172 338L169 50L343 372Q352 390 362 395T387 400Q421 400 425 363L459 50L663 475Q663 476 671 476T686 471T696 454T687 418L482 0H422L379 342L190 0H125L126 341Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="489" d="M393 -4Q378 -4 365 0T340 17T314 50T284 104L234 200L29 -7Q28 -7 20 -5T7 4T3 23T24 53L210 236L88 473H144L245 263L460 480Q461 480 469 478T482 469T486 450T465 420L269 227L332 109Q345 85 355 71T377 49T400 39T429 37Q429 37 429 31T428 17T417 3T393 -4Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="487" d="M3 -200Q-43 -200 -64 -177Q-73 -168 -73 -156Q-73 -144 -64 -137T-53 -131Q-44 -142 -29 -149T0 -156Q14 -156 28 -151T58 -132T93 -95T138 -34L178 26Q155 29 152 58L113 343Q105 396 88 417T39 438Q39 438 39 444T41 457T51 471T76 477Q101 477 118 459Q133 444 144 415T160 339L196 52L459 473Q459 473 467 473T485 473T502 472T510 471L184 -44Q155 -91 132 -121T87 -168T46 -193T3 -200Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="494" d="M7 37L418 432H129Q103 432 103 449Q103 456 107 464T113 473H480V437L68 41H368Q394 41 394 24Q394 17 390 9T384 0H7V37Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="428" d="M279 -197Q262 -197 242 -187T199 -153Q152 -105 135 -29T131 138L66 188Q49 201 44 210T43 238Q44 246 46 251T52 261T64 270T85 280L168 318Q179 366 193 404T222 473T255 526T292 568Q338 614 381 632T450 651Q461 651 466 646T472 633T470 619T463 610Q426 604 388 585T321 536Q282 496 256 436T210 288L84 234L183 158Q151 -38 232 -120Q249 -136 264 -145T299 -160Q301 -161 302 -167T303 -179T297 -191T279 -197Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="264" d="M39 -193Q16 -193 22 -167L191 627Q195 650 217 650H222Q244 650 238 624L69 -170Q65 -193 43 -193H39Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="428" d="M-27 -197Q-39 -197 -44 -192T-50 -179T-48 -165T-41 -156Q-4 -150 34 -131T102 -82Q141 -42 167 18T213 166L339 220L240 296Q256 393 244 463T190 573Q174 590 159 599T124 614Q122 615 120 620T119 633T126 645T143 651Q160 651 180 641T223 607Q271 559 288 483T292 316L357 266Q374 253 379 244T380 216Q378 208 377 202T371 192T359 183T338 174L255 136Q244 88 230 50T201 -19T167 -72T130 -114Q85 -160 42 -178T-27 -197Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="533" d="M319 167Q294 167 278 179T247 208Q233 223 224 230T204 237Q190 237 181 226T165 195Q158 173 141 173Q133 173 124 179Q132 224 153 251T210 278Q235 278 251 266T282 237Q295 221 304 214T325 207Q339 207 348 218T364 249Q368 260 373 266T387 272Q397 272 404 265Q396 220 375 194T319 167Z" />
+<glyph unicode="&#xA0;" glyph-name="uni00A0" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="266" d="M212 575Q175 575 175 602Q175 629 188 642Q198 652 216 652Q253 652 253 625Q253 597 241 585Q231 575 212 575ZM91 -3Q72 -3 63 2T59 28L166 460Q170 475 180 475Q191 475 194 471T195 454L119 22Q117 6 110 2T91 -3Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="560" d="M228 -98Q220 -98 214 -94T210 -75L222 -17L234 41Q199 49 170 66T119 110T85 172T73 251Q73 305 93 355T148 443T232 504T338 527L361 634Q365 657 381 657H386Q394 657 400 653T404 634L381 524Q420 518 447 504T494 469Q515 448 524 425T534 386Q534 374 529 368T518 359T505 356T498 358Q492 404 460 439T371 483L285 78Q288 78 291 78T297 77Q357 77 396 105T458 171Q459 172 463 172T472 169T481 161T485 149Q485 140 478 127T454 97Q431 74 390 55T293 35Q288 35 285 35T277 36L254 -75Q249 -98 233 -98H228ZM331 485Q284 483 246 463T181 411T138 337T123 254Q123 187 156 143T246 84L331 485Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="580" d="M36 0Q13 0 13 13Q13 19 16 27T23 40Q28 46 43 46H105L169 268H82Q59 268 59 281Q59 286 61 294T68 307Q75 313 87 313H182L217 435Q234 495 254 537T299 607T355 646T427 659Q468 659 497 647T546 615Q562 598 569 580T577 551Q577 539 572 532T561 522T550 519T543 519Q532 567 501 591T424 616Q368 616 331 574T265 429L231 313H415Q438 313 438 300Q438 295 436 287T429 274Q421 268 409 268H219L154 46H481Q497 46 503 41T509 27Q509 19 505 10T498 0H36Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="656" d="M110 384Q88 384 88 396Q88 401 90 409T97 421Q103 427 116 427H193Q209 469 231 508T284 579Q323 618 368 637T471 657Q522 657 557 642T617 601Q634 584 643 563T652 524Q652 513 647 507T636 497T625 493T619 494Q605 551 567 583T466 615Q421 615 385 599T318 552Q292 526 274 494T242 427H474Q495 427 495 415Q495 409 493 401T486 389Q482 384 468 384H228Q219 352 214 322T206 268H440Q461 268 461 256Q461 251 459 243T452 231Q448 226 434 226H205Q206 183 218 148T253 87T306 47T377 33Q432 33 474 60T545 133Q546 134 549 133T557 128T565 121T569 109Q569 87 536 54Q510 28 468 9T379 -10Q272 -10 215 53T155 226H76Q54 226 54 237Q54 243 56 251T63 263Q68 268 82 268H157Q159 293 164 322T179 384H110Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="587" d="M241 -3Q220 -3 225 20L247 123H77Q55 123 55 135Q55 140 57 148T64 161Q71 166 82 166H256L276 265H106Q85 265 85 276Q85 282 87 290T94 302Q98 307 112 307H271L130 634Q130 637 136 642T152 649T171 647T187 628L311 325L572 651Q573 652 581 651T596 643T602 623T581 586L348 307H503Q524 307 524 296Q524 290 522 282T515 270Q511 265 497 265H323L302 166H473Q494 166 494 154Q494 149 492 141T485 128Q481 123 467 123H293L271 20Q266 -3 244 -3H241Z" />
+<glyph unicode="&#xA6;" glyph-name="brokenbar" horiz-adv-x="264" d="M39 -193Q16 -193 22 -167L95 177Q99 200 121 200H126Q148 200 142 174L69 -170Q65 -193 43 -193H39ZM135 257Q112 257 118 283L191 627Q195 650 217 650H222Q244 650 238 624L165 280Q161 257 139 257H135Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="535" d="M213 -157Q144 -157 98 -139T27 -96Q10 -78 2 -62T-6 -33Q-6 -24 -2 -17T8 -6T19 -1T25 0Q32 -23 47 -44T85 -80T140 -105T216 -114Q289 -114 327 -83T366 -3Q366 19 355 35T325 63T280 86T223 107Q191 118 162 130T110 159T74 198T60 252Q60 282 71 305T102 346T148 375T202 391Q170 409 150 434T130 501Q130 534 144 562T184 612T246 645T329 657Q395 657 436 638T498 598Q513 583 520 567T528 541Q528 532 524 526T513 515T502 510T496 511Q480 555 438 585T325 615Q290 615 263 607T218 583T189 549T179 507Q179 484 190 468T220 438T266 415T323 392Q354 380 382 368T433 340T468 303T482 251Q482 220 471 196T440 154T395 125T341 110Q373 93 394 68T415 3Q415 -32 402 -61T362 -112T299 -145T213 -157ZM294 131Q321 132 346 140T391 163T422 198T434 242Q434 264 423 280T393 309T348 332T292 353Q280 357 268 361T245 370Q218 367 193 359T149 337T119 305T108 262Q108 238 119 221T149 191T196 167T254 146Q265 143 274 139T294 131Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="534" d="M419 545Q382 545 382 574Q382 592 391 605T421 619Q458 619 458 590Q458 572 449 559T419 545ZM260 545Q223 545 223 574Q223 592 232 605T261 619Q299 619 299 590Q299 572 289 559T260 545Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="783" d="M409 -10Q337 -10 278 14T175 83T108 188T84 322Q84 394 108 455T176 561T280 631T410 657Q482 657 542 633T645 564T712 459T736 325Q736 253 712 192T643 86T539 16T409 -10ZM411 26Q474 26 526 48T616 109T675 203T696 324Q696 388 675 443T615 537T524 599T409 622Q346 622 294 600T204 539T145 444T124 323Q124 258 145 204T205 110T295 48T411 26ZM419 145Q380 145 348 158T293 194T258 250T245 322Q245 360 258 392T294 449T348 486T417 500Q456 500 484 488T528 459Q539 448 544 438T549 419Q549 413 546 409T538 401T529 397T524 396Q512 423 486 441T417 459Q390 459 367 449T327 420T300 376T290 323Q290 293 299 268T326 225T367 197T420 186Q466 186 492 207T528 254Q529 255 533 254T541 250T550 243T554 231Q554 224 548 212T528 184Q513 169 485 157T419 145Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="503" d="M441 265Q411 265 397 285T391 348V349Q365 306 326 284T241 262Q209 262 181 273T132 305T99 356T86 425Q86 468 101 510T145 585T211 638T296 658Q351 658 387 631T437 563L446 607Q451 632 459 642T483 652Q490 652 495 650T500 646L436 347Q430 316 438 305T470 292Q470 292 470 288T468 279T459 269T441 265ZM249 301Q280 301 307 312T355 343T390 388T411 440L425 509Q417 557 385 588T295 619Q257 619 227 602T176 558T143 497T131 427Q131 368 165 335T249 301ZM195 -5Q161 -5 161 20Q161 43 172 56Q180 65 198 65Q232 65 232 41Q232 15 220 5Q211 -5 195 -5Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="522" d="M399 90Q385 90 367 104T317 154Q286 189 270 213T253 243Q253 247 282 272T359 332Q405 365 432 381T473 397Q481 397 485 392T490 379Q490 356 385 289Q362 275 341 262T297 234Q315 220 328 208T358 180Q376 163 388 152T407 132T416 119T419 107Q419 100 413 95T399 90ZM226 87Q212 87 192 103T136 157Q102 195 84 220T65 251Q65 254 89 276T162 334Q207 367 232 381T271 396Q279 396 283 391T288 378Q288 367 266 348T193 296Q171 282 152 269T109 242Q127 227 142 214T176 183Q196 164 209 152T230 131T241 116T245 104Q246 97 240 92T226 87Z" />
+<glyph unicode="&#xAC;" glyph-name="logicalnot" horiz-adv-x="600" d="M49 380H543V96H453V297H49V380Z" />
+<glyph unicode="&#xAD;" glyph-name="uni00AD" horiz-adv-x="385" d="M60 226Q60 227 61 229Q61 231 63 239L68 264Q68 266 68 267T69 269V270H330Q329 269 329 267Q329 265 329 263T328 258L322 232Q321 230 321 227V226H60Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="783" d="M409 -10Q337 -10 278 14T175 83T108 188T84 322Q84 394 108 455T176 561T280 631T410 657Q482 657 542 633T645 564T712 459T736 325Q736 253 712 192T643 86T539 16T409 -10ZM411 26Q474 26 526 48T616 109T675 203T696 324Q696 388 675 443T615 537T524 599T409 622Q346 622 294 600T204 539T145 444T124 323Q124 258 145 204T205 110T295 48T411 26ZM428 295L545 172Q547 169 540 161T521 153Q515 153 507 156T490 169L381 291H337V174Q337 153 318 153H312Q293 153 293 176V472Q293 499 317 499H418Q482 499 515 473T549 395Q549 368 540 349T514 318T477 301T433 295H428ZM420 328Q462 328 483 345T504 395Q504 461 417 461H336V328H420Z" />
+<glyph unicode="&#xAF;" glyph-name="uni00AF" horiz-adv-x="534" d="M233 567Q210 567 210 580Q210 585 212 593T219 606Q222 610 227 611T239 612H451Q475 612 475 599Q475 594 473 586T466 572Q463 569 458 568T446 567H233Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="402" d="M236 371Q182 371 147 405T111 496Q111 529 123 559T158 611T208 646T270 659Q324 659 359 626T395 535Q395 502 383 472T348 420T298 384T236 371ZM243 410Q266 410 286 420T321 446T344 485T353 532Q353 570 328 595T263 621Q240 621 220 611T185 585T162 546T153 499Q153 461 178 436T243 410Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="634" d="M344 260Q339 237 322 237H316Q308 237 302 241T299 260L330 407H186Q164 407 164 419Q164 424 166 432T173 444Q177 450 191 450H338L370 596Q376 620 391 620H398Q406 620 412 615T415 596L384 450H527Q549 450 549 438Q549 433 547 425T540 413Q534 407 522 407H375L344 260ZM113 128Q90 128 90 140Q90 146 92 154T99 167Q103 173 118 173H482Q505 173 505 161Q505 155 502 147T495 134Q490 128 476 128H113Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="370" d="M108 383Q85 360 82 324H270Q296 324 296 308Q296 300 292 293T286 285H59Q51 285 45 293T40 314Q43 370 79 408Q99 428 126 441T183 465Q210 474 230 482T263 501T284 523T291 554Q291 580 270 597T211 614Q175 614 153 596T119 550Q119 549 115 549T105 551T95 559T91 573Q91 581 97 593T114 617Q131 633 156 643T216 654Q272 654 304 628T336 556Q336 530 327 512T301 479T260 454T206 433Q174 422 150 411T108 383Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="370" d="M163 279Q127 279 100 289T56 315Q43 328 38 340T33 361Q33 372 37 378T48 387T59 390T64 390Q68 359 93 338T164 317Q209 317 233 340T257 396Q257 422 238 439T181 457H153Q135 457 135 469Q135 473 137 479T142 488Q147 493 161 493H193Q244 493 263 512T283 559Q283 584 262 600T207 616Q174 616 150 601T113 565Q112 565 108 565T100 568T92 574T88 586Q88 600 103 614Q120 632 147 643T210 654Q235 654 256 648T294 629T320 601T330 565Q330 527 308 504T242 472Q272 463 287 443T303 394Q303 373 295 352T270 315T227 289T163 279Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="534" d="M265 547Q264 548 273 559T300 585T342 620T394 657Q411 670 421 673T440 677Q449 677 455 671T461 657Q461 647 452 638T419 612Q389 594 362 581T313 559T279 548T265 547Z" />
+<glyph unicode="&#xB5;" glyph-name="uni00B5" horiz-adv-x="569" d="M56 -267Q19 -267 2 -243T-5 -166L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27V21Q497 16 494 10T484 -1T463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48L91 60L43 -165Q35 -206 47 -219T90 -233V-238Q91 -244 88 -250T78 -261T56 -267Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="597" d="M431 -3Q420 -3 416 4T414 23L542 627Q545 639 550 644T567 650H573Q597 650 591 624L463 20Q457 -3 438 -3H431ZM329 -3Q318 -3 314 4T312 23L371 304H305Q201 304 147 340T92 448Q92 489 105 525T147 589T222 631T336 647H458Q494 647 486 612L361 20Q355 -3 336 -3H329Z" />
+<glyph unicode="&#xB7;" glyph-name="uni00B7" horiz-adv-x="240" d="M121 211Q84 211 84 237Q84 265 97 278Q106 288 125 288Q162 288 162 261Q162 233 149 221Q139 211 121 211Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="534" d="M112 -219Q110 -222 106 -221T97 -217T91 -211T90 -204Q119 -160 142 -125Q152 -111 161 -97T178 -71Q187 -58 194 -46Q195 -45 201 -46T216 -50T230 -58T237 -73Q237 -90 220 -108L112 -219Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="370" d="M64 285Q43 285 46 301Q46 303 46 307T49 314Q54 325 72 325H153L209 584Q198 572 182 565T150 558Q129 558 117 567T104 592Q104 607 112 614T122 621Q127 612 136 606T163 599Q183 599 202 613T229 650H248Q268 650 264 627L197 325H273Q282 325 287 321T291 309Q291 307 290 303T287 296Q283 285 265 285H64Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="473" d="M264 261Q226 261 195 273T140 309T103 364T90 435Q90 480 106 520T150 591T218 639T302 657Q340 657 372 645T427 609T464 554T477 484Q477 439 461 399T416 328T348 279T264 261ZM190 -5Q156 -5 156 20Q156 43 167 56Q175 65 193 65Q227 65 227 41Q227 15 215 5Q206 -5 190 -5ZM267 300Q300 300 330 314T382 354T418 412T431 482Q431 511 421 536T394 579T352 607T299 618Q265 618 236 604T184 564T148 506T135 436Q135 407 145 382T172 339T214 311T267 300Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="522" d="M254 88Q245 88 241 93T237 106Q237 117 258 136T331 188Q353 202 372 215T415 242Q398 256 382 270T348 301Q328 320 315 332T294 353T283 368T279 379Q279 386 284 391T298 396Q312 396 332 381T388 327Q422 289 440 264T459 233Q459 229 435 207T362 149Q317 117 293 103T254 88ZM51 87Q43 87 39 92T34 104Q34 128 139 194Q162 209 183 222T227 250Q209 264 196 276T166 304Q148 321 136 332T117 352T108 365T105 377Q105 384 111 389T125 394Q139 394 157 380T207 330Q238 295 254 270T271 240Q271 237 243 212T165 151Q119 119 92 103T51 87Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="826" d="M64 285Q43 285 46 301Q46 303 46 307T49 314Q54 325 72 325H153L209 584Q198 572 182 565T150 558Q129 558 117 567T104 592Q104 607 112 614T122 621Q127 612 136 606T163 599Q183 599 202 613T229 650H248Q268 650 264 627L197 325H273Q282 325 287 321T291 309Q291 307 290 303T287 296Q283 285 265 285H64ZM114 -20Q113 -21 110 -20T103 -16T97 -8T95 3Q97 14 102 21T122 43Q126 47 143 64T186 106T242 159T303 217T361 271T406 315Q419 328 441 349T489 395T544 448T601 503Q667 566 743 638Q749 637 753 633Q757 630 760 626T761 614Q759 606 754 598T734 575Q730 570 712 553T669 512T612 458T551 399T494 344T449 301Q435 288 414 267T367 221T312 169T255 114Q189 51 114 -20ZM673 -2Q664 -2 659 3T657 21L674 102H511Q509 102 503 110T497 130Q497 139 501 147T516 167L692 345Q703 357 711 361T736 366Q774 366 767 332L726 140H771Q790 140 790 128Q790 124 788 117T782 106Q778 102 765 102H718L700 20Q698 7 693 3T679 -2H673ZM685 140L724 329L539 140H685Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="826" d="M94 -20Q93 -21 90 -20T83 -16T77 -8T75 3Q77 14 82 21T102 43Q106 47 123 64T166 106T222 159T283 217T341 271T386 315Q399 328 421 349T469 395T524 448T581 503Q647 566 723 638Q729 637 733 633Q737 630 740 626T741 614Q739 606 734 598T714 575Q710 570 692 553T649 512T592 458T531 399T474 344T429 301Q415 288 394 267T347 221T292 169T235 114Q169 51 94 -20ZM564 98Q541 75 538 39H726Q752 39 752 23Q752 15 748 8T742 0H515Q507 0 501 8T496 29Q499 85 535 123Q555 143 582 156T639 180Q666 189 686 197T719 216T740 238T747 269Q747 295 726 312T667 329Q631 329 609 311T575 265Q575 264 571 264T561 266T551 274T547 288Q547 296 553 308T570 332Q587 348 612 358T672 369Q728 369 760 343T792 271Q792 245 783 227T757 194T716 169T662 148Q630 137 606 126T564 98ZM64 285Q43 285 46 301Q46 303 46 307T49 314Q54 325 72 325H153L209 584Q198 572 182 565T150 558Q129 558 117 567T104 592Q104 607 112 614T122 621Q127 612 136 606T163 599Q183 599 202 613T229 650H248Q268 650 264 627L197 325H273Q282 325 287 321T291 309Q291 307 290 303T287 296Q283 285 265 285H64Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="826" d="M673 -2Q664 -2 659 3T657 21L674 102H511Q509 102 503 110T497 130Q497 139 501 147T516 167L692 345Q703 357 711 361T736 366Q774 366 767 332L726 140H771Q790 140 790 128Q790 124 788 117T782 106Q778 102 765 102H718L700 20Q698 7 693 3T679 -2H673ZM114 -20Q113 -21 110 -20T103 -16T97 -8T95 3Q97 14 102 21T122 43Q126 47 143 64T186 106T242 159T303 217T361 271T406 315Q419 328 441 349T489 395T544 448T601 503Q667 566 743 638Q749 637 753 633Q757 630 760 626T761 614Q759 606 754 598T734 575Q730 570 712 553T669 512T612 458T551 399T494 344T449 301Q435 288 414 267T367 221T312 169T255 114Q189 51 114 -20ZM163 279Q127 279 100 289T56 315Q43 328 38 340T33 361Q33 372 37 378T48 387T59 390T64 390Q68 359 93 338T164 317Q209 317 233 340T257 396Q257 422 238 439T181 457H153Q135 457 135 469Q135 473 137 479T142 488Q147 493 161 493H193Q244 493 263 512T283 559Q283 584 262 600T207 616Q174 616 150 601T113 565Q112 565 108 565T100 568T92 574T88 586Q88 600 103 614Q120 632 147 643T210 654Q235 654 256 648T294 629T320 601T330 565Q330 527 308 504T242 472Q272 463 287 443T303 394Q303 373 295 352T270 315T227 289T163 279ZM685 140L724 329L539 140H685Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="492" d="M346 573Q308 573 308 600Q308 628 321 640Q331 650 349 650Q387 650 387 624Q387 598 374 583Q364 573 346 573ZM217 -12Q173 -12 138 1T79 36T41 90T27 157Q27 197 40 226T75 277T124 312T180 336Q214 348 237 357T273 376T292 396T298 417Q298 447 265 453Q264 453 264 457T267 465T275 472T290 476Q313 476 329 460T345 418Q345 379 315 353T211 302Q179 291 154 278T112 248T86 208T76 157Q76 103 114 68T220 33Q282 33 324 68T386 158Q387 159 392 158T403 154T413 146T418 131Q418 116 407 94T374 51Q347 24 308 6T217 -12Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM434 721Q432 719 421 722T391 735T349 758T302 793Q282 810 276 819T270 839Q270 848 277 854T293 860Q301 860 311 855T336 835Q360 815 378 795T410 759T429 733T434 721Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM356 721Q355 722 364 733T391 759T433 794T485 831Q502 844 512 847T531 851Q540 851 546 845T552 831Q552 821 543 812T510 786Q480 768 453 755T404 733T370 722T356 721Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM291 719Q277 719 277 731Q277 743 292 761T343 812Q378 844 401 860T426 877Q428 877 443 861T479 814Q502 780 509 764T516 740Q516 729 511 724T499 719Q489 719 479 731T449 781Q441 795 435 807T420 834L368 780Q336 746 319 733T291 719Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM460 718Q435 718 419 730T388 759Q374 774 365 781T345 788Q330 788 321 777T306 746Q298 723 282 723Q274 723 265 730Q273 775 294 802T351 829Q376 829 392 816T423 787Q436 772 445 765T466 758Q480 758 489 769T505 800Q509 811 514 817T528 823Q536 823 545 816Q537 771 516 745T460 718Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM481 719Q444 719 444 748Q444 766 453 779T483 793Q520 793 520 764Q520 746 511 733T481 719ZM322 719Q285 719 285 748Q285 766 294 779T323 793Q361 793 361 764Q361 746 351 733T322 719Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="631" d="M33 -2Q33 -2 24 -2T8 3T-1 22T13 60L343 611L351 622Q324 633 310 656T296 706Q296 731 305 753T330 793T368 820T416 831Q464 831 490 803T517 737Q517 695 491 661T426 617Q427 615 428 611T429 604L551 13Q551 10 544 5T528 -1T509 2T497 26L457 225H166L33 -2ZM449 269L384 595L192 269H449ZM398 646Q416 646 430 653T455 673T472 701T478 734Q478 758 462 777T415 797Q397 797 382 790T357 770T340 742T334 709Q334 685 351 666T398 646Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="878" d="M33 -2Q32 -2 23 -2T7 3T-2 22T12 60L348 620Q356 633 365 640T392 647H880V644Q880 643 878 635Q877 629 875 621T873 607Q872 604 872 603Q872 602 871 602H430L481 354H752L749 342L744 315Q743 313 743 311Q743 310 742 310H490L544 46H772Q771 45 771 43Q771 41 771 39T770 33Q769 27 767 19T764 5Q764 3 764 3T763 1V0H536Q517 0 508 7T495 33L457 224H166L33 -2ZM448 268L384 595L191 268H448Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="681" d="M365 -10Q300 -10 248 9T158 64T100 151T79 266Q79 345 106 416T180 540T293 625T434 657Q505 657 550 636T623 587Q646 564 656 540T666 501Q666 491 662 485T652 476T641 471T633 472Q615 538 565 576T431 614Q366 614 312 587T217 513T155 404T132 273Q132 218 149 174T196 98T270 51T365 34Q405 34 438 43T499 69T545 106T579 150Q580 151 584 150T592 146T600 138T604 124Q604 115 596 99T568 63Q538 34 485 12T365 -10ZM246 -219Q244 -222 240 -221T231 -217T225 -211T224 -204Q253 -160 276 -125Q286 -111 295 -97T312 -71Q321 -58 328 -46Q329 -45 335 -46T350 -50T364 -58T371 -73Q371 -90 354 -108L246 -219Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM463 721Q461 719 450 722T420 735T378 758T331 793Q311 810 305 819T299 839Q299 848 306 854T322 860Q330 860 340 855T365 835Q389 815 407 795T439 759T458 733T463 721Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM355 721Q354 722 363 733T390 759T432 794T484 831Q501 844 511 847T530 851Q539 851 545 845T551 831Q551 821 542 812T509 786Q479 768 452 755T403 733T369 722T355 721Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM310 719Q296 719 296 731Q296 743 311 761T362 812Q397 844 420 860T445 877Q447 877 462 861T498 814Q521 780 528 764T535 740Q535 729 530 724T518 719Q508 719 498 731T468 781Q460 795 454 807T439 834L387 780Q355 746 338 733T310 719Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM495 719Q458 719 458 748Q458 766 467 779T497 793Q534 793 534 764Q534 746 525 733T495 719ZM336 719Q299 719 299 748Q299 766 308 779T337 793Q375 793 375 764Q375 746 365 733T336 719Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM266 721Q264 719 253 722T223 735T181 758T134 793Q114 810 108 819T102 839Q102 848 109 854T125 860Q133 860 143 855T168 835Q192 815 210 795T242 759T261 733T266 721Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM192 721Q191 722 200 733T227 759T269 794T321 831Q338 844 348 847T367 851Q376 851 382 845T388 831Q388 821 379 812T346 786Q316 768 289 755T240 733T206 722T192 721Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM127 719Q113 719 113 731Q113 743 128 761T179 812Q214 844 237 860T262 877Q264 877 279 861T315 814Q338 780 345 764T352 740Q352 729 347 724T335 719Q325 719 315 731T285 781Q277 795 271 807T256 834L204 780Q172 746 155 733T127 719Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM316 719Q279 719 279 748Q279 766 288 779T318 793Q355 793 355 764Q355 746 346 733T316 719ZM157 719Q120 719 120 748Q120 766 129 779T158 793Q196 793 196 764Q196 746 186 733T157 719Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="726" d="M125 0Q89 0 97 35L153 302H78Q56 302 56 314Q56 319 58 327T65 340Q70 345 84 345H162L220 616Q225 647 256 647H363Q515 647 598 576T682 376Q682 297 657 229T581 109T457 29T283 0H125ZM291 44Q376 44 439 69T545 139T608 243T629 369Q629 479 560 541T360 603H267L212 345H436Q458 345 458 333Q458 328 456 320T449 307Q442 302 431 302H203L148 44H291Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="704" d="M176 616Q184 650 211 650H224Q236 650 242 643T256 622L527 73L650 647H696L564 27Q561 13 553 5T532 -3H526Q515 -3 509 4T494 25L217 588L91 0H45L176 616ZM522 718Q497 718 481 730T450 759Q436 774 427 781T407 788Q392 788 383 777T368 746Q360 723 344 723Q336 723 327 730Q335 775 356 802T413 829Q438 829 454 816T485 787Q498 772 507 765T528 758Q542 758 551 769T567 800Q571 811 576 817T590 823Q598 823 607 816Q599 771 578 745T522 718Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM509 721Q507 719 496 722T466 735T424 758T377 793Q357 810 351 819T345 839Q345 848 352 854T368 860Q376 860 386 855T411 835Q435 815 453 795T485 759T504 733T509 721Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM387 721Q386 722 395 733T422 759T464 794T516 831Q533 844 543 847T562 851Q571 851 577 845T583 831Q583 821 574 812T541 786Q511 768 484 755T435 733T401 722T387 721Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM357 719Q343 719 343 731Q343 743 358 761T409 812Q444 844 467 860T492 877Q494 877 509 861T545 814Q568 780 575 764T582 740Q582 729 577 724T565 719Q555 719 545 731T515 781Q507 795 501 807T486 834L434 780Q402 746 385 733T357 719Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM510 718Q485 718 469 730T438 759Q424 774 415 781T395 788Q380 788 371 777T356 746Q348 723 332 723Q324 723 315 730Q323 775 344 802T401 829Q426 829 442 816T473 787Q486 772 495 765T516 758Q530 758 539 769T555 800Q559 811 564 817T578 823Q586 823 595 816Q587 771 566 745T510 718Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM545 719Q508 719 508 748Q508 766 517 779T547 793Q584 793 584 764Q584 746 575 733T545 719ZM386 719Q349 719 349 748Q349 766 358 779T387 793Q425 793 425 764Q425 746 415 733T386 719Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="573" d="M292 235L133 99Q124 90 115 91T100 99T94 115T104 133L265 271L163 409Q156 419 157 428T165 442T181 446T198 435L299 299L457 434Q466 443 475 442T490 434T495 418T486 400L326 263L429 124Q436 114 435 105T427 91T411 87T394 98L292 235Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="761" d="M39 -29Q37 -31 28 -24T19 -5Q19 14 41 35L126 111Q103 145 91 185T79 269Q79 353 108 424T187 547T301 628T435 657Q499 657 550 636T639 578L740 670Q742 672 751 664T761 645Q761 634 756 626T739 606L669 542Q693 508 706 467T719 378Q719 294 689 223T610 101T496 20T363 -10Q297 -10 244 13T153 75L39 -29ZM603 546Q574 578 531 596T430 614Q373 614 320 589T224 517T157 409T132 273Q132 236 140 204T165 144L603 546ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 453 629 510L190 107Q219 73 264 54T368 34Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM481 711Q479 709 468 712T438 725T396 748T349 783Q329 800 323 809T317 829Q317 838 324 844T340 850Q348 850 358 845T383 825Q407 805 425 785T457 749T476 723T481 711Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM362 711Q361 712 370 723T397 749T439 784T491 821Q508 834 518 837T537 841Q546 841 552 835T558 821Q558 811 549 802T516 776Q486 758 459 745T410 723T376 712T362 711Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM326 709Q312 709 312 721Q312 733 327 751T378 802Q413 834 436 850T461 867Q463 867 478 851T514 804Q537 770 544 754T551 730Q551 719 546 714T534 709Q524 709 514 721T484 771Q476 785 470 797T455 824L403 770Q371 736 354 723T326 709Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM515 709Q478 709 478 738Q478 756 487 769T517 783Q554 783 554 754Q554 736 545 723T515 709ZM356 709Q319 709 319 738Q319 756 328 769T357 783Q395 783 395 754Q395 736 385 723T356 709Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="569" d="M271 279L108 647H162L303 322L584 651Q585 652 593 650T608 642T614 622T593 586L320 274L262 0H212L271 279ZM328 708Q327 709 336 720T363 746T405 781T457 818Q474 831 484 834T503 838Q512 838 518 832T524 818Q524 808 515 799T482 773Q452 755 425 742T376 720T342 709T328 708Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="585" d="M68 -3Q44 -3 50 23L178 627Q183 650 203 650H210Q233 650 228 624L205 521H332Q438 521 492 478T547 355Q547 253 482 193T291 133H123L99 20Q95 -3 74 -3H68ZM300 177Q397 177 446 223T496 348Q496 410 455 443T329 476H196L132 177H300Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="548" d="M289 -10Q247 -10 219 0T173 27Q160 41 156 52T151 71Q151 79 154 85T162 95T171 100T176 101Q188 75 214 54T292 32Q323 32 349 44T394 78T424 129T435 193Q435 239 404 275T301 335Q281 342 274 350T266 368Q266 385 273 395T285 408Q382 408 428 438T474 536Q474 584 439 613T345 642Q286 642 244 600T182 463L84 0H36L136 467Q160 580 215 632T346 685Q429 685 475 645T522 539Q522 464 473 420T318 370Q393 350 438 306T483 197Q483 154 469 116T428 50T367 6T289 -10Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM404 547Q402 545 391 548T361 561T319 584T272 619Q252 636 246 645T240 665Q240 674 247 680T263 686Q271 686 281 681T306 661Q330 641 348 621T380 585T399 559T404 547Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM303 547Q302 548 311 559T338 585T380 620T432 657Q449 670 459 673T478 677Q487 677 493 671T499 657Q499 647 490 638T457 612Q427 594 400 581T351 559T317 548T303 547Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM252 545Q238 545 238 557Q238 569 253 587T304 638Q339 670 362 686T387 703Q389 703 404 687T440 640Q463 606 470 590T477 566Q477 555 472 550T460 545Q450 545 440 557T410 607Q402 621 396 633T381 660L329 606Q297 572 280 559T252 545Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM419 544Q394 544 378 556T347 585Q333 600 324 607T304 614Q289 614 280 603T265 572Q257 549 241 549Q233 549 224 556Q232 601 253 628T310 655Q335 655 351 642T382 613Q395 598 404 591T425 584Q439 584 448 595T464 626Q468 637 473 643T487 649Q495 649 504 642Q496 597 475 571T419 544Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM440 545Q403 545 403 574Q403 592 412 605T442 619Q479 619 479 590Q479 572 470 559T440 545ZM281 545Q244 545 244 574Q244 592 253 605T282 619Q320 619 320 590Q320 572 310 559T281 545Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM367 544Q319 544 293 572T266 638Q266 663 275 685T300 725T338 752T386 763Q434 763 460 735T487 669Q487 644 478 622T453 582T414 554T367 544ZM368 578Q386 578 400 585T426 605T443 634T449 667Q449 691 432 710T385 730Q367 730 353 723T327 703T310 674T304 641Q304 617 321 598T368 578Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="884" d="M196 -10Q121 -10 76 21T30 108Q30 167 65 202Q102 240 182 257T399 275H414L416 284Q431 359 394 399T278 440Q220 440 182 420T120 365Q119 364 109 370T99 391Q99 407 121 428Q142 449 181 466T283 483Q358 483 406 449T460 347Q496 410 555 446T692 483Q769 483 811 449T854 364Q854 325 837 296T774 248T650 223T450 226Q447 205 447 184Q447 116 486 74T601 31Q658 31 698 52T766 113Q767 114 771 113T780 109T788 101T792 90Q792 81 787 73T769 51Q709 -10 599 -10Q518 -10 472 27T413 124Q385 61 329 26T196 -10ZM203 30Q236 30 267 40T323 71T367 119T394 183L406 237H379Q315 235 270 232T192 221T138 204T103 180Q79 155 79 112Q79 75 113 53T203 30ZM690 441Q649 441 613 428T547 391T495 335T460 266Q566 258 633 263T738 283T791 318T806 363Q806 392 777 416T690 441Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" d="M273 -10Q227 -10 187 4T117 46T70 114T53 206Q53 260 73 310T128 398T212 460T316 483Q374 483 412 466T474 424Q495 403 504 380T514 341Q514 330 509 324T497 314T485 311T478 313Q474 339 462 362T430 402T381 430T317 440Q270 440 231 421T163 369T118 295T102 209Q102 168 115 136T151 81T206 46T277 33Q336 33 375 61T437 127Q438 128 442 127T452 124T461 116T465 104Q465 84 434 52Q410 29 369 10T273 -10ZM157 -219Q155 -222 151 -221T142 -217T136 -211T135 -204Q164 -160 187 -125Q197 -111 206 -97T223 -71Q232 -58 239 -46Q240 -45 246 -46T261 -50T275 -58T282 -73Q282 -90 265 -108L157 -219Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM397 547Q395 545 384 548T354 561T312 584T265 619Q245 636 239 645T233 665Q233 674 240 680T256 686Q264 686 274 681T299 661Q323 641 341 621T373 585T392 559T397 547Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM298 547Q297 548 306 559T333 585T375 620T427 657Q444 670 454 673T473 677Q482 677 488 671T494 657Q494 647 485 638T452 612Q422 594 395 581T346 559T312 548T298 547Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM257 545Q243 545 243 557Q243 569 258 587T309 638Q344 670 367 686T392 703Q394 703 409 687T445 640Q468 606 475 590T482 566Q482 555 477 550T465 545Q455 545 445 557T415 607Q407 621 401 633T386 660L334 606Q302 572 285 559T257 545Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM444 545Q407 545 407 574Q407 592 416 605T446 619Q483 619 483 590Q483 572 474 559T444 545ZM285 545Q248 545 248 574Q248 592 257 605T286 619Q324 619 324 590Q324 572 314 559T285 545Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM221 547Q219 545 208 548T178 561T136 584T89 619Q69 636 63 645T57 665Q57 674 64 680T80 686Q88 686 98 681T123 661Q147 641 165 621T197 585T216 559T221 547Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM124 547Q123 548 132 559T159 585T201 620T253 657Q270 670 280 673T299 677Q308 677 314 671T320 657Q320 647 311 638T278 612Q248 594 221 581T172 559T138 548T124 547Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM79 545Q65 545 65 557Q65 569 80 587T131 638Q166 670 189 686T214 703Q216 703 231 687T267 640Q290 606 297 590T304 566Q304 555 299 550T287 545Q277 545 267 557T237 607Q229 621 223 633T208 660L156 606Q124 572 107 559T79 545Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM267 545Q230 545 230 574Q230 592 239 605T269 619Q306 619 306 590Q306 572 297 559T267 545ZM108 545Q71 545 71 574Q71 592 80 605T109 619Q147 619 147 590Q147 572 137 559T108 545Z" />
+<glyph unicode="&#xF0;" glyph-name="null" horiz-adv-x="572" d="M258 -10Q212 -10 173 2T105 39T60 98T44 178Q44 228 61 272T111 349T188 401T290 421Q326 421 357 412T413 387T456 352T484 313Q485 321 485 329T485 346Q485 418 459 473T387 569L231 482Q229 480 223 486T217 505Q217 512 221 519T245 537L351 594Q313 620 271 631T192 646Q190 646 190 652T193 665T205 679T228 685Q257 685 304 670T397 618L524 687Q526 689 532 683T538 665Q538 657 534 650T510 632L430 590Q475 545 503 484T532 342Q532 286 517 224T468 110T383 24T258 -10ZM257 31Q306 31 343 50T406 100T449 174T474 262Q468 281 452 302T411 340T355 368T289 379Q239 379 203 364T141 321T104 256T92 177Q92 142 104 115T139 70T191 41T257 31Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="569" d="M462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L137 473Q137 476 147 476Q161 476 170 466T173 424L163 379Q193 427 237 455T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7ZM415 544Q390 544 374 556T343 585Q329 600 320 607T300 614Q285 614 276 603T261 572Q253 549 237 549Q229 549 220 556Q228 601 249 628T306 655Q331 655 347 642T378 613Q391 598 400 591T421 584Q435 584 444 595T460 626Q464 637 469 643T483 649Q491 649 500 642Q492 597 471 571T415 544Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM384 547Q382 545 371 548T341 561T299 584T252 619Q232 636 226 645T220 665Q220 674 227 680T243 686Q251 686 261 681T286 661Q310 641 328 621T360 585T379 559T384 547Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM287 547Q286 548 295 559T322 585T364 620T416 657Q433 670 443 673T462 677Q471 677 477 671T483 657Q483 647 474 638T441 612Q411 594 384 581T335 559T301 548T287 547Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM240 545Q226 545 226 557Q226 569 241 587T292 638Q327 670 350 686T375 703Q377 703 392 687T428 640Q451 606 458 590T465 566Q465 555 460 550T448 545Q438 545 428 557T398 607Q390 621 384 633T369 660L317 606Q285 572 268 559T240 545Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM407 544Q382 544 366 556T335 585Q321 600 312 607T292 614Q277 614 268 603T253 572Q245 549 229 549Q221 549 212 556Q220 601 241 628T298 655Q323 655 339 642T370 613Q383 598 392 591T413 584Q427 584 436 595T452 626Q456 637 461 643T475 649Q483 649 492 642Q484 597 463 571T407 544Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM428 545Q391 545 391 574Q391 592 400 605T430 619Q467 619 467 590Q467 572 458 559T428 545ZM269 545Q232 545 232 574Q232 592 241 605T270 619Q308 619 308 590Q308 572 298 559T269 545Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="634" d="M352 375Q314 375 314 402Q314 429 327 442Q337 452 355 452Q393 452 393 425Q393 398 380 385Q370 375 352 375ZM289 81Q252 81 252 108Q252 136 265 148Q274 158 293 158Q330 158 330 132Q330 104 317 91Q307 81 289 81ZM131 244Q106 244 112 264L113 269Q116 280 121 284T141 289H514Q528 289 531 285T533 269L531 264Q529 254 524 249T504 244H131Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="587" d="M33 -22Q31 -24 22 -17T12 1Q12 7 17 15T33 33L90 84Q54 137 54 207Q54 263 74 313T130 401T214 461T318 483Q363 483 402 467T471 422L550 494Q552 496 561 489T570 471Q570 464 566 457T550 438L497 391Q514 365 524 334T534 266Q534 210 514 160T457 72T373 12T270 -10Q224 -10 185 6T116 52L33 -22ZM437 390Q414 414 383 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 182 109 158T127 114L437 390ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 318 460 360L149 84Q172 60 203 46T273 32Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM378 544Q376 542 365 545T335 558T293 581T246 616Q226 633 220 642T214 662Q214 671 221 677T237 683Q245 683 255 678T280 658Q304 638 322 618T354 582T373 556T378 544Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM285 544Q284 545 293 556T320 582T362 617T414 654Q431 667 441 670T460 674Q469 674 475 668T481 654Q481 644 472 635T439 609Q409 591 382 578T333 556T299 545T285 544Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM240 542Q226 542 226 554Q226 566 241 584T292 635Q327 667 350 683T375 700Q377 700 392 684T428 637Q451 603 458 587T465 563Q465 552 460 547T448 542Q438 542 428 554T398 604Q390 618 384 630T369 657L317 603Q285 569 268 556T240 542Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM428 542Q391 542 391 571Q391 589 400 602T430 616Q467 616 467 587Q467 569 458 556T428 542ZM269 542Q232 542 232 571Q232 589 241 602T270 616Q308 616 308 587Q308 569 298 556T269 542Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="487" d="M3 -200Q-43 -200 -64 -177Q-73 -168 -73 -156Q-73 -144 -64 -137T-53 -131Q-44 -142 -29 -149T0 -156Q14 -156 28 -151T58 -132T93 -95T138 -34L178 26Q155 29 152 58L113 343Q105 396 88 417T39 438Q39 438 39 444T41 457T51 471T76 477Q101 477 118 459Q133 444 144 415T160 339L196 52L459 473Q459 473 467 473T485 473T502 472T510 471L184 -44Q155 -91 132 -121T87 -168T46 -193T3 -200ZM255 534Q254 535 263 546T290 572T332 607T384 644Q401 657 411 660T430 664Q439 664 445 658T451 644Q451 634 442 625T409 599Q379 581 352 568T303 546T269 535T255 534Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="598" d="M19 -196Q9 -196 3 -192T-1 -173L175 655Q179 678 197 678H203Q212 678 218 674T223 655L164 382Q196 428 241 455T346 483Q386 483 422 470T485 429T529 363T545 273Q545 219 526 168T474 78T393 14T290 -10Q253 -10 223 0T169 28T130 67T107 112L47 -173Q42 -196 25 -196H19ZM293 32Q340 32 378 52T442 107T483 184T497 273Q497 312 485 343T451 396T402 429T341 441Q309 441 280 431T227 404T183 363T150 312L129 218Q123 184 131 151T160 92T214 49T293 32Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="487" d="M3 -200Q-43 -200 -64 -177Q-73 -168 -73 -156Q-73 -144 -64 -137T-53 -131Q-44 -142 -29 -149T0 -156Q14 -156 28 -151T58 -132T93 -95T138 -34L178 26Q155 29 152 58L113 343Q105 396 88 417T39 438Q39 438 39 444T41 457T51 471T76 477Q101 477 118 459Q133 444 144 415T160 339L196 52L459 473Q459 473 467 473T485 473T502 472T510 471L184 -44Q155 -91 132 -121T87 -168T46 -193T3 -200ZM382 545Q345 545 345 574Q345 592 354 605T384 619Q421 619 421 590Q421 572 412 559T382 545ZM223 545Q186 545 186 574Q186 592 195 605T224 619Q262 619 262 590Q262 572 252 559T223 545Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM304 736Q281 736 281 749Q281 754 283 762T290 775Q293 779 298 780T310 781H522Q546 781 546 768Q546 763 544 755T537 741Q534 738 529 737T517 736H304Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM265 567Q242 567 242 580Q242 585 244 593T251 606Q254 610 259 611T271 612H483Q507 612 507 599Q507 594 505 586T498 572Q495 569 490 568T478 567H265Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H501L457 224H166L34 0H-24ZM448 268L384 595L191 268H448ZM415 714Q382 714 359 724T321 748Q304 765 297 783T290 815Q290 826 296 834T320 842Q329 803 353 780T416 757Q454 757 480 780T521 842Q538 842 544 834T551 818Q551 807 542 789T517 755Q498 736 472 725T415 714Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="600" d="M496 -7Q461 -7 444 18T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32ZM380 545Q347 545 324 555T286 579Q269 596 262 614T255 646Q255 657 261 665T285 673Q294 634 318 611T381 588Q419 588 445 611T486 673Q503 673 509 665T516 649Q516 638 507 620T482 586Q463 567 437 556T380 545Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="631" d="M-24 0L357 635Q362 644 369 648T389 652Q408 652 415 646T424 629L553 0H537Q472 -32 449 -61T426 -114Q426 -131 437 -141T465 -151Q486 -151 502 -135Q510 -136 515 -141T521 -157Q521 -173 504 -183T461 -194Q426 -194 403 -174T380 -122Q380 -88 408 -55T500 5L457 224H166L34 0H-24ZM448 268L384 595L191 268H448Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="600" d="M529 27Q529 27 529 23T529 14T523 3T510 -5Q446 -37 424 -65T401 -118Q401 -135 412 -145T440 -155Q461 -155 477 -139Q485 -140 490 -145T496 -161Q496 -177 479 -187T436 -198Q401 -198 378 -178T355 -126Q355 -94 381 -61T467 -2Q446 6 437 30T437 94V96Q404 44 355 17T250 -10Q209 -10 174 3T112 43T70 106T54 193Q54 248 73 300T128 392T211 458T317 483Q352 483 381 474T433 448T472 411T496 366L519 473H566L485 95Q477 54 489 41T529 27ZM256 32Q295 32 329 47T391 87T437 144T463 212L482 298Q469 360 428 400T314 441Q266 441 227 420T161 363T118 284T103 194Q103 156 115 126T147 75T196 43T256 32Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="681" d="M365 -10Q300 -10 248 9T158 64T100 151T79 266Q79 345 106 416T180 540T293 625T434 657Q505 657 550 636T623 587Q646 564 656 540T666 501Q666 491 662 485T652 476T641 471T633 472Q615 538 565 576T431 614Q366 614 312 587T217 513T155 404T132 273Q132 218 149 174T196 98T270 51T365 34Q405 34 438 43T499 69T545 106T579 150Q580 151 584 150T592 146T600 138T604 124Q604 115 596 99T568 63Q538 34 485 12T365 -10ZM379 716Q378 717 387 728T414 754T456 789T508 826Q525 839 535 842T554 846Q563 846 569 840T575 826Q575 816 566 807T533 781Q503 763 476 750T427 728T393 717T379 716Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" d="M273 -10Q227 -10 187 4T117 46T70 114T53 206Q53 260 73 310T128 398T212 460T316 483Q374 483 412 466T474 424Q495 403 504 380T514 341Q514 330 509 324T497 314T485 311T478 313Q474 339 462 362T430 402T381 430T317 440Q270 440 231 421T163 369T118 295T102 209Q102 168 115 136T151 81T206 46T277 33Q336 33 375 61T437 127Q438 128 442 127T452 124T461 116T465 104Q465 84 434 52Q410 29 369 10T273 -10ZM266 547Q265 548 274 559T301 585T343 620T395 657Q412 670 422 673T441 677Q450 677 456 671T462 657Q462 647 453 638T420 612Q390 594 363 581T314 559T280 548T266 547Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="681" d="M365 -10Q300 -10 248 9T158 64T100 151T79 266Q79 345 106 416T180 540T293 625T434 657Q505 657 550 636T623 587Q646 564 656 540T666 501Q666 491 662 485T652 476T641 471T633 472Q615 538 565 576T431 614Q366 614 312 587T217 513T155 404T132 273Q132 218 149 174T196 98T270 51T365 34Q405 34 438 43T499 69T545 106T579 150Q580 151 584 150T592 146T600 138T604 124Q604 115 596 99T568 63Q538 34 485 12T365 -10ZM344 714Q330 714 330 726Q330 738 345 756T396 807Q431 839 454 855T479 872Q481 872 496 856T532 809Q555 775 562 759T569 735Q569 724 564 719T552 714Q542 714 532 726T502 776Q494 790 488 802T473 829L421 775Q389 741 372 728T344 714Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" d="M273 -10Q227 -10 187 4T117 46T70 114T53 206Q53 260 73 310T128 398T212 460T316 483Q374 483 412 466T474 424Q495 403 504 380T514 341Q514 330 509 324T497 314T485 311T478 313Q474 339 462 362T430 402T381 430T317 440Q270 440 231 421T163 369T118 295T102 209Q102 168 115 136T151 81T206 46T277 33Q336 33 375 61T437 127Q438 128 442 127T452 124T461 116T465 104Q465 84 434 52Q410 29 369 10T273 -10ZM231 545Q217 545 217 557Q217 569 232 587T283 638Q318 670 341 686T366 703Q368 703 383 687T419 640Q442 606 449 590T456 566Q456 555 451 550T439 545Q429 545 419 557T389 607Q381 621 375 633T360 660L308 606Q276 572 259 559T231 545Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="681" d="M365 -10Q300 -10 248 9T158 64T100 151T79 266Q79 345 106 416T180 540T293 625T434 657Q505 657 550 636T623 587Q646 564 656 540T666 501Q666 491 662 485T652 476T641 471T633 472Q615 538 565 576T431 614Q366 614 312 587T217 513T155 404T132 273Q132 218 149 174T196 98T270 51T365 34Q405 34 438 43T499 69T545 106T579 150Q580 151 584 150T592 146T600 138T604 124Q604 115 596 99T568 63Q538 34 485 12T365 -10ZM457 715Q416 715 416 746Q416 765 426 779T458 794Q500 794 500 763Q500 744 489 730T457 715Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" d="M273 -10Q227 -10 187 4T117 46T70 114T53 206Q53 260 73 310T128 398T212 460T316 483Q374 483 412 466T474 424Q495 403 504 380T514 341Q514 330 509 324T497 314T485 311T478 313Q474 339 462 362T430 402T381 430T317 440Q270 440 231 421T163 369T118 295T102 209Q102 168 115 136T151 81T206 46T277 33Q336 33 375 61T437 127Q438 128 442 127T452 124T461 116T465 104Q465 84 434 52Q410 29 369 10T273 -10ZM346 546Q305 546 305 577Q305 596 315 610T347 625Q389 625 389 594Q389 575 378 561T346 546Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="681" d="M365 -10Q300 -10 248 9T158 64T100 151T79 266Q79 345 106 416T180 540T293 625T434 657Q505 657 550 636T623 587Q646 564 656 540T666 501Q666 491 662 485T652 476T641 471T633 472Q615 538 565 576T431 614Q366 614 312 587T217 513T155 404T132 273Q132 218 149 174T196 98T270 51T365 34Q405 34 438 43T499 69T545 106T579 150Q580 151 584 150T592 146T600 138T604 124Q604 115 596 99T568 63Q538 34 485 12T365 -10ZM444 698Q442 698 427 714T391 761Q368 795 361 811T353 835Q353 846 358 851T371 856Q381 856 391 844T421 794Q429 780 435 768T450 741Q464 755 474 766T502 795Q534 828 551 842T579 856Q593 856 593 843Q593 831 578 813T527 763Q492 731 469 715T444 698Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" d="M273 -10Q227 -10 187 4T117 46T70 114T53 206Q53 260 73 310T128 398T212 460T316 483Q374 483 412 466T474 424Q495 403 504 380T514 341Q514 330 509 324T497 314T485 311T478 313Q474 339 462 362T430 402T381 430T317 440Q270 440 231 421T163 369T118 295T102 209Q102 168 115 136T151 81T206 46T277 33Q336 33 375 61T437 127Q438 128 442 127T452 124T461 116T465 104Q465 84 434 52Q410 29 369 10T273 -10ZM336 529Q334 529 319 545T283 592Q260 626 253 642T245 666Q245 677 250 682T263 687Q273 687 283 675T313 625Q321 611 327 599T342 572Q356 586 366 597T394 626Q426 659 443 673T471 687Q485 687 485 674Q485 662 470 644T419 594Q384 562 361 546T336 529Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="682" d="M176 616Q181 647 212 647H319Q471 647 554 576T638 376Q638 297 613 229T537 109T413 29T239 0H45L176 616ZM247 44Q332 44 395 69T501 139T564 243T585 369Q585 479 516 541T316 603H223L105 44H247ZM395 698Q393 698 378 714T342 761Q319 795 312 811T304 835Q304 846 309 851T322 856Q332 856 342 844T372 794Q380 780 386 768T401 741Q415 755 425 766T453 795Q485 828 502 842T530 856Q544 856 544 843Q544 831 529 813T478 763Q443 731 420 715T395 698Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="767" d="M496 -7Q462 -7 444 17T436 90Q403 42 359 16T255 -10Q215 -10 179 4T116 44T73 110T57 200Q57 254 75 305T128 395T209 459T312 483Q382 483 428 449T495 365L561 674H609L485 95Q476 54 488 41T529 27Q529 27 529 22T527 10T517 -1T496 -7ZM261 32Q298 32 331 46T391 83T436 138T461 205L481 296Q467 359 424 400T309 441Q262 441 224 421T160 366T119 289T105 200Q105 161 117 130T151 77T200 44T261 32ZM647 454Q645 452 641 453T632 456T625 463T624 470L734 676Q735 677 742 677T757 674T772 666T779 651Q779 643 776 635T765 616L647 454Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="726" d="M125 0Q89 0 97 35L153 302H78Q56 302 56 314Q56 319 58 327T65 340Q70 345 84 345H162L220 616Q225 647 256 647H363Q515 647 598 576T682 376Q682 297 657 229T581 109T457 29T283 0H125ZM291 44Q376 44 439 69T545 139T608 243T629 369Q629 479 560 541T360 603H267L212 345H436Q458 345 458 333Q458 328 456 320T449 307Q442 302 431 302H203L148 44H291Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="600" d="M647 609Q670 609 670 595Q670 590 668 582T661 569Q656 563 641 563H585L485 95Q476 54 488 41T529 27Q529 27 529 22T527 10T517 -1T496 -7Q462 -7 444 17T436 90Q403 42 359 16T255 -10Q215 -10 179 4T116 44T73 110T57 200Q57 254 75 305T128 395T209 459T312 483Q382 483 428 449T495 365L537 563H420Q397 563 397 576Q397 582 399 590T406 603Q412 609 426 609H547L561 674H609L595 609H647ZM261 32Q298 32 331 46T391 83T436 138T461 205L481 296Q467 359 424 400T309 441Q262 441 224 421T160 366T119 289T105 200Q105 161 117 130T151 77T200 44T261 32Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM311 736Q288 736 288 749Q288 754 290 762T297 775Q300 779 305 780T317 781H529Q553 781 553 768Q553 763 551 755T544 741Q541 738 536 737T524 736H311Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM238 567Q215 567 215 580Q215 585 217 593T224 606Q227 610 232 611T244 612H456Q480 612 480 599Q480 594 478 586T471 572Q468 569 463 568T451 567H238Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM418 714Q385 714 362 724T324 748Q307 765 300 783T293 815Q293 826 299 834T323 842Q332 803 356 780T419 757Q457 757 483 780T524 842Q541 842 547 834T554 818Q554 807 545 789T520 755Q501 736 475 725T418 714Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM350 545Q317 545 294 555T256 579Q239 596 232 614T225 646Q225 657 231 665T255 673Q264 634 288 611T351 588Q389 588 415 611T456 673Q473 673 479 665T486 649Q486 638 477 620T452 586Q433 567 407 556T350 545Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM413 715Q372 715 372 746Q372 765 382 779T414 794Q456 794 456 763Q456 744 445 730T413 715Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM351 546Q310 546 310 577Q310 596 320 610T352 625Q394 625 394 594Q394 575 383 561T351 546Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H481Q417 -32 395 -60T372 -113Q372 -130 383 -140T411 -150Q432 -150 448 -134Q456 -135 461 -140T467 -156Q467 -172 450 -182T407 -193Q372 -193 349 -173T326 -121Q326 -89 350 -58T431 0H45L176 616Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q401 27 368 11Q305 -21 283 -49T260 -101Q260 -118 271 -128T299 -138Q320 -138 336 -122Q344 -123 349 -128T355 -144Q355 -160 338 -170T295 -181Q260 -181 237 -161T214 -109Q214 -83 230 -58T282 -9Q275 -9 268 -9T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="595" d="M176 616Q183 647 212 647H615L606 602H223L170 354H471L462 310H161L105 46H498L488 0H45L176 616ZM403 698Q401 698 386 714T350 761Q327 795 320 811T312 835Q312 846 317 851T330 856Q340 856 350 844T380 794Q388 780 394 768T409 741Q423 755 433 766T461 795Q493 828 510 842T538 856Q552 856 552 843Q552 831 537 813T486 763Q451 731 428 715T403 698Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="539" d="M254 -10Q204 -10 167 4T103 43T64 104T51 181Q51 244 73 299T136 395T229 459T346 483Q424 483 467 449T510 364Q510 332 498 306T454 260T369 231T235 220Q206 220 174 221T103 227Q100 206 100 184Q100 116 139 74T256 31Q314 31 354 52T422 113Q423 114 427 113T435 109T443 101T447 90Q447 81 443 73T424 51Q394 20 353 5T254 -10ZM343 441Q302 441 266 428T200 391T148 337T113 268Q147 263 176 262T231 260Q300 260 344 269T415 292T451 324T461 363Q461 392 432 416T343 441ZM333 529Q331 529 316 545T280 592Q257 626 250 642T242 666Q242 677 247 682T260 687Q270 687 280 675T310 625Q318 611 324 599T339 572Q353 586 363 597T391 626Q423 659 440 673T468 687Q482 687 482 674Q482 662 467 644T416 594Q381 562 358 546T333 529Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="725" d="M363 -10Q301 -10 249 8T160 62T101 150T80 268Q80 344 106 414T179 538T292 624T437 657Q509 657 558 635T634 585Q653 566 662 546T672 511Q672 499 667 492T656 482T645 479T638 479Q631 506 614 530T570 573T510 602T436 613Q369 613 313 585T217 511T155 402T132 273Q132 215 149 170T197 95T270 49T364 33Q414 33 456 49T529 97Q546 113 558 130T579 167T596 212T611 273H389L398 318H630Q666 318 658 283L598 0Q598 -3 587 -3Q573 -3 566 8T564 49L574 98Q548 52 494 21T363 -10ZM332 714Q318 714 318 726Q318 738 333 756T384 807Q419 839 442 855T467 872Q469 872 484 856T520 809Q543 775 550 759T557 735Q557 724 552 719T540 714Q530 714 520 726T490 776Q482 790 476 802T461 829L409 775Q377 741 360 728T332 714Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="598" d="M225 -197Q162 -197 120 -180T53 -137Q34 -118 27 -101T19 -72Q19 -60 24 -53T35 -42T47 -38T54 -38Q66 -86 107 -119T229 -153Q383 -153 419 18L438 105Q407 59 361 32T254 5Q215 5 180 18T116 58T72 123T55 212Q55 264 74 312T129 399T211 460T315 483Q350 483 379 474T431 448T470 411T494 366L518 473H564L467 14Q445 -88 386 -142T225 -197ZM260 47Q297 47 330 60T390 95T435 147T461 211L480 298Q469 361 425 401T312 441Q266 441 228 422T162 370T119 296T104 212Q104 173 117 143T151 91T201 59T260 47ZM247 545Q233 545 233 557Q233 569 248 587T299 638Q334 670 357 686T382 703Q384 703 399 687T435 640Q458 606 465 590T472 566Q472 555 467 550T455 545Q445 545 435 557T405 607Q397 621 391 633T376 660L324 606Q292 572 275 559T247 545Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="725" d="M363 -10Q301 -10 249 8T160 62T101 150T80 268Q80 344 106 414T179 538T292 624T437 657Q509 657 558 635T634 585Q653 566 662 546T672 511Q672 499 667 492T656 482T645 479T638 479Q631 506 614 530T570 573T510 602T436 613Q369 613 313 585T217 511T155 402T132 273Q132 215 149 170T197 95T270 49T364 33Q414 33 456 49T529 97Q546 113 558 130T579 167T596 212T611 273H389L398 318H630Q666 318 658 283L598 0Q598 -3 587 -3Q573 -3 566 8T564 49L574 98Q548 52 494 21T363 -10ZM449 714Q416 714 393 724T355 748Q338 765 331 783T324 815Q324 826 330 834T354 842Q363 803 387 780T450 757Q488 757 514 780T555 842Q572 842 578 834T585 818Q585 807 576 789T551 755Q532 736 506 725T449 714Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="598" d="M225 -197Q162 -197 120 -180T53 -137Q34 -118 27 -101T19 -72Q19 -60 24 -53T35 -42T47 -38T54 -38Q66 -86 107 -119T229 -153Q383 -153 419 18L438 105Q407 59 361 32T254 5Q215 5 180 18T116 58T72 123T55 212Q55 264 74 312T129 399T211 460T315 483Q350 483 379 474T431 448T470 411T494 366L518 473H564L467 14Q445 -88 386 -142T225 -197ZM260 47Q297 47 330 60T390 95T435 147T461 211L480 298Q469 361 425 401T312 441Q266 441 228 422T162 370T119 296T104 212Q104 173 117 143T151 91T201 59T260 47ZM363 545Q330 545 307 555T269 579Q252 596 245 614T238 646Q238 657 244 665T268 673Q277 634 301 611T364 588Q402 588 428 611T469 673Q486 673 492 665T499 649Q499 638 490 620T465 586Q446 567 420 556T363 545Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="725" d="M363 -10Q301 -10 249 8T160 62T101 150T80 268Q80 344 106 414T179 538T292 624T437 657Q509 657 558 635T634 585Q653 566 662 546T672 511Q672 499 667 492T656 482T645 479T638 479Q631 506 614 530T570 573T510 602T436 613Q369 613 313 585T217 511T155 402T132 273Q132 215 149 170T197 95T270 49T364 33Q414 33 456 49T529 97Q546 113 558 130T579 167T596 212T611 273H389L398 318H630Q666 318 658 283L598 0Q598 -3 587 -3Q573 -3 566 8T564 49L574 98Q548 52 494 21T363 -10ZM442 715Q401 715 401 746Q401 765 411 779T443 794Q485 794 485 763Q485 744 474 730T442 715Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="598" d="M225 -197Q162 -197 120 -180T53 -137Q34 -118 27 -101T19 -72Q19 -60 24 -53T35 -42T47 -38T54 -38Q66 -86 107 -119T229 -153Q383 -153 419 18L438 105Q407 59 361 32T254 5Q215 5 180 18T116 58T72 123T55 212Q55 264 74 312T129 399T211 460T315 483Q350 483 379 474T431 448T470 411T494 366L518 473H564L467 14Q445 -88 386 -142T225 -197ZM260 47Q297 47 330 60T390 95T435 147T461 211L480 298Q469 361 425 401T312 441Q266 441 228 422T162 370T119 296T104 212Q104 173 117 143T151 91T201 59T260 47ZM351 546Q310 546 310 577Q310 596 320 610T352 625Q394 625 394 594Q394 575 383 561T351 546Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="725" d="M363 -10Q301 -10 249 8T160 62T101 150T80 268Q80 344 106 414T179 538T292 624T437 657Q509 657 558 635T634 585Q653 566 662 546T672 511Q672 499 667 492T656 482T645 479T638 479Q631 506 614 530T570 573T510 602T436 613Q369 613 313 585T217 511T155 402T132 273Q132 215 149 170T197 95T270 49T364 33Q414 33 456 49T529 97Q546 113 558 130T579 167T596 212T611 273H389L398 318H630Q666 318 658 283L598 0Q598 -3 587 -3Q573 -3 566 8T564 49L574 98Q548 52 494 21T363 -10ZM243 -267Q241 -269 237 -268T228 -265T221 -259T220 -252L327 -63Q328 -62 335 -62T350 -65T365 -73T372 -88Q372 -96 369 -104T358 -123L243 -267Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="598" d="M225 -197Q162 -197 120 -180T53 -137Q34 -118 27 -101T19 -72Q19 -60 24 -53T35 -42T47 -38T54 -38Q66 -86 107 -119T229 -153Q383 -153 419 18L438 105Q407 59 361 32T254 5Q215 5 180 18T116 58T72 123T55 212Q55 264 74 312T129 399T211 460T315 483Q350 483 379 474T431 448T470 411T494 366L518 473H564L467 14Q445 -88 386 -142T225 -197ZM260 47Q297 47 330 60T390 95T435 147T461 211L480 298Q469 361 425 401T312 441Q266 441 228 422T162 370T119 296T104 212Q104 173 117 143T151 91T201 59T260 47ZM407 717Q409 719 413 718T422 715T428 709T430 702L323 513Q322 512 315 512T300 515T285 523T278 538Q278 546 281 554T292 573L407 717Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="694" d="M178 627Q184 650 203 650H210Q233 650 228 624L169 347H572L631 627Q637 650 656 650H663Q687 650 681 624L548 0H499L562 301H159L95 0H46L178 627ZM326 714Q312 714 312 726Q312 738 327 756T378 807Q413 839 436 855T461 872Q463 872 478 856T514 809Q537 775 544 759T551 735Q551 724 546 719T534 714Q524 714 514 726T484 776Q476 790 470 802T455 829L403 775Q371 741 354 728T326 714Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="569" d="M175 654Q179 676 199 676H206Q217 676 220 669T222 649L163 378Q192 426 237 454T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L175 654ZM209 735Q195 735 195 747Q195 759 210 777T261 828Q296 860 319 876T344 893Q346 893 361 877T397 830Q420 796 427 780T434 756Q434 745 429 740T417 735Q407 735 397 747T367 797Q359 811 353 823T338 850L286 796Q254 762 237 749T209 735Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="694" d="M91 472Q67 472 67 485Q67 499 76 510Q79 514 84 515T96 516H154L178 627Q184 650 203 650H210Q233 650 228 624L205 516H607L631 627Q637 650 656 650H663Q687 650 681 624L658 516H710Q733 516 733 503Q733 498 731 491T724 478Q719 472 704 472H648L548 0H499L562 301H159L95 0H46L145 472H91ZM572 347L598 472H195L169 347H572Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="569" d="M462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L155 563H102Q79 563 79 576Q79 582 81 590T88 603Q94 609 108 609H165L179 674H227L213 609H329Q353 609 353 595Q353 590 351 582T344 569Q341 565 336 564T324 563H203L163 378Q192 426 237 454T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7Z" />
+<glyph unicode="&#x128;" glyph-name="Itilde" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM290 669Q265 669 249 681T218 710Q204 725 195 732T175 739Q160 739 151 728T136 697Q128 674 112 674Q104 674 95 681Q103 726 124 753T181 780Q206 780 222 767T253 738Q266 723 275 716T296 709Q310 709 319 720T335 751Q339 762 344 768T358 774Q366 774 375 767Q367 722 346 696T290 669Z" />
+<glyph unicode="&#x129;" glyph-name="itilde" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM246 544Q221 544 205 556T174 585Q160 600 151 607T131 614Q116 614 107 603T92 572Q84 549 68 549Q60 549 51 556Q59 601 80 628T137 655Q162 655 178 642T209 613Q222 598 231 591T252 584Q266 584 275 595T291 626Q295 637 300 643T314 649Q322 649 331 642Q323 597 302 571T246 544Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM136 736Q113 736 113 749Q113 754 115 762T122 775Q125 779 130 780T142 781H354Q378 781 378 768Q378 763 376 755T369 741Q366 738 361 737T349 736H136Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM78 567Q55 567 55 580Q55 585 57 593T64 606Q67 610 72 611T84 612H296Q320 612 320 599Q320 594 318 586T311 572Q308 569 303 568T291 567H78Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H98Q32 -33 9 -62T-15 -115Q-15 -132 -4 -142T24 -152Q45 -152 61 -136Q69 -137 74 -142T80 -158Q80 -174 63 -184T20 -195Q-15 -195 -38 -175T-61 -123Q-61 -90 -34 -57T56 3L190 628Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="230" d="M156 27Q156 27 156 23T155 13T147 2T132 -6Q72 -38 51 -65T29 -116Q29 -133 40 -143T68 -153Q89 -153 105 -137Q113 -138 118 -143T124 -159Q124 -175 107 -185T64 -196Q29 -196 6 -176T-17 -124Q-17 -92 8 -60T91 -1Q69 7 61 30T61 94L142 473H189L109 95Q101 54 113 41T156 27ZM200 601Q166 601 166 627Q166 643 175 655T201 667Q235 667 235 641Q235 625 226 613T200 601Z" />
+<glyph unicode="&#x130;" glyph-name="Idotaccent" horiz-adv-x="261" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM240 715Q199 715 199 746Q199 765 209 779T241 794Q283 794 283 763Q283 744 272 730T240 715Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7Z" />
+<glyph unicode="&#x132;" glyph-name="IJ" horiz-adv-x="731" d="M190 628Q194 650 214 650H221Q232 650 236 643T239 623L106 0H56L190 628ZM437 -10Q392 -10 360 4T307 38Q292 54 286 66T280 87Q280 96 284 102T293 112T303 117T309 117Q323 80 357 57T437 33Q499 33 534 71T587 192L684 647H733L635 185Q614 85 566 38T437 -10Z" />
+<glyph unicode="&#x133;" glyph-name="ij" horiz-adv-x="460" d="M200 601Q166 601 166 627Q166 643 175 655T201 667Q235 667 235 641Q235 625 226 613T200 601ZM122 -7Q85 -7 68 17T61 94L142 473H189L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM430 601Q396 601 396 627Q396 643 405 655T431 667Q465 667 465 641Q465 625 456 613T430 601ZM172 -199Q142 -199 123 -191T93 -173Q82 -162 82 -151Q82 -137 91 -128T102 -121Q113 -135 131 -145T174 -156Q205 -156 225 -136T257 -65L372 473H419L303 -73Q289 -139 255 -169T172 -199Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="470" d="M176 -10Q131 -10 99 4T46 38Q31 54 25 66T19 87Q19 96 23 102T32 112T42 117T48 117Q62 80 96 57T176 33Q238 33 273 71T326 192L423 647H472L374 185Q353 85 305 38T176 -10ZM356 714Q342 714 342 726Q342 738 357 756T408 807Q443 839 466 855T491 872Q493 872 508 856T544 809Q567 775 574 759T581 735Q581 724 576 719T564 714Q554 714 544 726T514 776Q506 790 500 802T485 829L433 775Q401 741 384 728T356 714Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="230" d="M79 545Q65 545 65 557Q65 569 80 587T131 638Q166 670 189 686T214 703Q216 703 231 687T267 640Q290 606 297 590T304 566Q304 555 299 550T287 545Q277 545 267 557T237 607Q229 621 223 633T208 660L156 606Q124 572 107 559T79 545ZM-58 -199Q-88 -199 -107 -191T-137 -173Q-148 -162 -148 -151Q-148 -137 -139 -128T-128 -121Q-117 -135 -99 -145T-56 -156Q-25 -156 -5 -136T27 -65L141 472H145Q147 472 148 472T152 473H163H169Q170 473 179 473T189 472L73 -73Q59 -139 25 -169T-58 -199Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="559" d="M456 -4Q444 -4 434 0T412 13T388 39T356 78L165 329L94 0H45L178 627Q183 650 203 650H210Q233 650 228 624L168 342L559 656Q560 656 567 653T579 643T581 623T556 594L220 329L402 96Q418 75 430 62T452 42T472 32T494 29Q495 29 495 24T492 13T481 1T456 -4ZM127 -267Q125 -269 121 -268T112 -265T105 -259T104 -252L211 -63Q212 -62 219 -62T234 -65T249 -73T256 -88Q256 -96 253 -104T242 -123L127 -267Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="461" d="M363 -7Q350 -7 340 -4T318 9T292 36T258 83L136 246L83 0H35L179 674H227L138 257L433 482Q434 483 441 480T453 469T454 450T430 422L190 244L304 97Q320 74 331 62T353 43T375 36T401 34Q401 34 401 28T399 14T388 0T363 -7ZM86 -267Q84 -269 80 -268T71 -265T64 -259T63 -252L170 -63Q171 -62 178 -62T193 -65T208 -73T215 -88Q215 -96 212 -104T201 -123L86 -267Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="520" d="M178 627Q184 650 203 650H210Q233 650 228 624L105 46H419Q442 46 442 33Q442 27 440 19T432 5Q428 0 412 0H45L178 627ZM205 716Q204 717 213 728T240 754T282 789T334 826Q351 839 361 842T380 846Q389 846 395 840T401 826Q401 816 392 807T359 781Q329 763 302 750T253 728T219 717T205 716Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L185 674H232L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM195 716Q194 717 203 728T230 754T272 789T324 826Q341 839 351 842T370 846Q379 846 385 840T391 826Q391 816 382 807T349 781Q319 763 292 750T243 728T209 717T195 716Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="520" d="M178 627Q184 650 203 650H210Q233 650 228 624L105 46H419Q442 46 442 33Q442 27 440 19T432 5Q428 0 412 0H45L178 627ZM130 -267Q128 -269 124 -268T115 -265T108 -259T107 -252L214 -63Q215 -62 222 -62T237 -65T252 -73T259 -88Q259 -96 256 -104T245 -123L130 -267Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="230" d="M122 -7Q85 -7 68 17T61 94L185 674H232L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM-11 -267Q-13 -269 -17 -268T-26 -265T-33 -259T-34 -252L73 -63Q74 -62 81 -62T96 -65T111 -73T118 -88Q118 -96 115 -104T104 -123L-11 -267Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="616" d="M495 436Q493 434 489 435T480 438T473 445T472 452L582 658Q583 659 590 659T605 656T620 648T627 633Q627 625 624 617T613 598L495 436ZM178 627Q184 650 203 650H210Q233 650 228 624L105 46H419Q442 46 442 33Q442 27 440 19T432 5Q428 0 412 0H45L178 627Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="392" d="M272 453Q270 451 266 452T257 455T250 462T249 469L359 675Q360 676 367 676T382 673T397 665T404 650Q404 642 401 634T390 615L272 453ZM122 -7Q85 -7 68 17T61 94L185 674H232L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="606" d="M178 627Q184 650 203 650H210Q233 650 228 624L105 46H419Q442 46 442 33Q442 27 440 19T432 5Q428 0 412 0H45L178 627ZM534 281Q497 281 497 307Q497 335 510 348Q519 358 538 358Q575 358 575 331Q575 303 562 291Q552 281 534 281Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="368" d="M122 -7Q85 -7 68 17T61 94L185 674H232L109 95Q101 54 113 41T156 27Q156 27 156 22T154 10T144 -1T122 -7ZM289 224Q252 224 252 250Q252 278 265 291Q274 301 293 301Q330 301 330 274Q330 246 317 234Q307 224 289 224Z" />
+<glyph unicode="&#x141;" glyph-name="CR" horiz-adv-x="576" d="M123 0Q87 0 95 35L139 244L51 217Q49 216 45 223T40 239Q40 248 48 256T76 270L149 293L220 627Q226 650 245 650H252Q275 650 270 624L202 308L416 372Q418 373 422 366T426 350Q426 341 418 333T390 319L192 259L147 46H461Q484 46 484 33Q484 27 482 19T474 5Q470 0 454 0H123Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="350" d="M179 -7Q142 -7 125 17T118 94L153 255L49 225Q47 225 43 233T39 248Q39 256 46 264T75 278L163 304L238 655Q242 678 260 678H266Q275 678 281 674T286 655L214 318L325 351Q327 351 331 343T336 328Q336 320 329 312T300 298L204 270L166 95Q158 54 170 41T214 27Q214 27 214 22T211 10T201 -1T179 -7Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="704" d="M176 616Q184 650 211 650H224Q236 650 242 643T256 622L527 73L650 647H696L564 27Q561 13 553 5T532 -3H526Q515 -3 509 4T494 25L217 588L91 0H45L176 616ZM401 716Q400 717 409 728T436 754T478 789T530 826Q547 839 557 842T576 846Q585 846 591 840T597 826Q597 816 588 807T555 781Q525 763 498 750T449 728T415 717T401 716Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="569" d="M462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L137 473Q137 476 147 476Q161 476 170 466T173 424L163 379Q193 427 237 455T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7ZM279 547Q278 548 287 559T314 585T356 620T408 657Q425 670 435 673T454 677Q463 677 469 671T475 657Q475 647 466 638T433 612Q403 594 376 581T327 559T293 548T279 547Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="704" d="M176 616Q184 650 211 650H224Q236 650 242 643T256 622L527 73L650 647H696L564 27Q561 13 553 5T532 -3H526Q515 -3 509 4T494 25L217 588L91 0H45L176 616ZM201 -267Q199 -269 195 -268T186 -265T179 -259T178 -252L285 -63Q286 -62 293 -62T308 -65T323 -73T330 -88Q330 -96 327 -104T316 -123L201 -267Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="569" d="M462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L137 473Q137 476 147 476Q161 476 170 466T173 424L163 379Q193 427 237 455T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7ZM152 -267Q150 -269 146 -268T137 -265T130 -259T129 -252L236 -63Q237 -62 244 -62T259 -65T274 -73T281 -88Q281 -96 278 -104T267 -123L152 -267Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="704" d="M176 616Q184 650 211 650H224Q236 650 242 643T256 622L527 73L650 647H696L564 27Q561 13 553 5T532 -3H526Q515 -3 509 4T494 25L217 588L91 0H45L176 616ZM445 698Q443 698 428 714T392 761Q369 795 362 811T354 835Q354 846 359 851T372 856Q382 856 392 844T422 794Q430 780 436 768T451 741Q465 755 475 766T503 795Q535 828 552 842T580 856Q594 856 594 843Q594 831 579 813T528 763Q493 731 470 715T445 698Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="569" d="M462 -7Q436 -7 419 10T402 59Q402 79 408 110T426 181Q438 226 445 260T452 318Q452 375 421 406T333 438Q270 438 227 405T150 313L83 0H35L137 473Q137 476 147 476Q161 476 170 466T173 424L163 379Q193 427 237 455T339 483Q414 483 457 440T501 326Q501 299 493 261T473 175Q462 136 456 108T450 66Q450 43 462 36T494 26Q495 26 495 21T492 10T482 -2T462 -7ZM348 529Q346 529 331 545T295 592Q272 626 265 642T257 666Q257 677 262 682T275 687Q285 687 295 675T325 625Q333 611 339 599T354 572Q368 586 378 597T406 626Q438 659 455 673T483 687Q497 687 497 674Q497 662 482 644T431 594Q396 562 373 546T348 529Z" />
+<glyph unicode="&#x149;" glyph-name="napostrophe" horiz-adv-x="795" d="M117 436Q115 434 111 435T102 438T95 445T94 452L204 658Q205 659 212 659T227 656T242 648T249 633Q249 625 246 617T235 598L117 436ZM688 -7Q662 -7 645 10T628 59Q628 79 634 110T652 181Q664 226 671 260T678 318Q678 375 647 406T559 438Q496 438 453 405T376 313L309 0H261L363 473Q363 476 373 476Q387 476 396 466T399 424L389 379Q419 427 463 455T565 483Q640 483 683 440T727 326Q727 299 719 261T699 175Q688 136 682 108T676 66Q676 43 688 36T720 26Q721 26 721 21T718 10T708 -2T688 -7Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM347 736Q324 736 324 749Q324 754 326 762T333 775Q336 779 341 780T353 781H565Q589 781 589 768Q589 763 587 755T580 741Q577 738 572 737T560 736H347Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM233 567Q210 567 210 580Q210 585 212 593T219 606Q222 610 227 611T239 612H451Q475 612 475 599Q475 594 473 586T466 572Q463 569 458 568T446 567H233Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM468 714Q435 714 412 724T374 748Q357 765 350 783T343 815Q343 826 349 834T373 842Q382 803 406 780T469 757Q507 757 533 780T574 842Q591 842 597 834T604 818Q604 807 595 789T570 755Q551 736 525 725T468 714Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM340 545Q307 545 284 555T246 579Q229 596 222 614T215 646Q215 657 221 665T245 673Q254 634 278 611T341 588Q379 588 405 611T446 673Q463 673 469 665T476 649Q476 638 467 620T442 586Q423 567 397 556T340 545Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="761" d="M363 -10Q298 -10 246 11T156 69T99 158T79 269Q79 353 108 424T187 547T301 628T435 657Q501 657 553 636T642 578T699 489T719 378Q719 294 689 223T610 101T496 20T363 -10ZM368 34Q425 34 479 59T574 130T641 238T666 375Q666 425 650 468T604 544T530 595T430 614Q373 614 320 589T224 517T157 409T132 273Q132 223 148 179T194 103T268 53T368 34ZM498 716Q497 717 505 727T528 753T565 788T614 828Q632 841 642 845T660 850Q669 850 675 845T682 830Q682 821 674 811T643 784Q615 764 590 751T544 729T512 717T498 716ZM336 716Q334 717 342 727T365 753T403 788T452 828Q469 841 479 845T498 850Q506 850 512 845T519 830Q519 821 511 811T480 784Q452 764 427 751T382 729T350 717T336 716Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="587" d="M270 -10Q224 -10 185 6T116 51T71 119T54 207Q54 263 74 313T130 401T214 461T318 483Q364 483 403 467T472 422T517 354T534 266Q534 210 514 160T457 72T373 12T270 -10ZM273 32Q316 32 354 50T422 100T468 174T485 265Q485 303 473 335T438 390T384 427T315 441Q272 441 234 423T166 373T119 299T102 208Q102 170 115 138T150 83T204 46T273 32ZM377 547Q376 548 384 558T407 584T444 619T493 659Q511 672 521 676T539 681Q548 681 554 676T561 661Q561 652 553 642T522 615Q494 595 469 582T423 560T391 548T377 547ZM215 547Q213 548 221 558T244 584T282 619T331 659Q348 672 358 676T377 681Q385 681 391 676T398 661Q398 652 390 642T359 615Q331 595 306 582T261 560T229 548T215 547Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1044" d="M361 -8Q296 -8 244 13T155 71T99 159T79 270Q79 354 108 424T187 546T300 627T432 656Q470 656 505 647H1054Q1053 646 1053 644Q1053 642 1053 640T1052 635L1046 607L1045 602H595Q651 569 682 511T714 378V354H922Q921 353 921 351Q921 349 921 347T920 342L914 315Q913 313 913 311V310H708Q701 265 684 225T643 152T589 91T524 46H939Q938 45 938 43Q938 41 938 39T937 33L931 5Q930 3 930 1V0H427Q392 -8 361 -8ZM366 35Q423 35 476 60T570 131T636 239T661 375Q661 425 646 468T600 544T527 594T427 613Q370 613 317 588T223 517T157 409T132 273Q132 223 147 180T193 104T266 54T366 35Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="960" d="M266 -10Q221 -10 183 5T116 49T71 116T54 205Q54 262 74 312T129 401T211 461T315 483Q353 483 386 472T446 439T491 388T517 321Q534 357 560 386T618 437T689 471T768 483Q845 483 887 449T930 364Q930 332 918 306T875 261T794 231T666 220Q635 220 601 221T526 226Q523 205 523 184Q523 116 562 74T677 31Q734 31 774 52T842 113Q843 114 847 113T855 109T863 101T867 90Q867 81 863 73T845 51Q785 -10 675 -10Q597 -10 550 28T489 130Q457 67 399 29T266 -10ZM271 32Q314 32 352 50T419 101T464 175T481 267Q481 304 469 336T434 391T381 428T314 441Q270 441 231 423T164 372T119 298T102 206Q102 168 115 137T150 82T204 45T271 32ZM765 441Q725 441 689 428T624 391T572 336T537 268Q572 264 603 263T661 261Q727 261 769 270T837 293T872 325T882 363Q882 378 875 392T853 417T816 434T765 441Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="593" d="M299 277L418 97Q430 78 440 66T460 46T481 35T508 32Q509 32 509 27T507 14T496 2T470 -4Q458 -4 448 -1T426 11T403 36T374 78L243 277H154L94 0H45L176 616Q181 647 212 647H366Q471 647 525 607T580 490Q580 390 516 334T335 277H299ZM336 320Q432 320 480 365T528 485Q528 542 486 572T363 603H223L163 320H336ZM317 716Q316 717 325 728T352 754T394 789T446 826Q463 839 473 842T492 846Q501 846 507 840T513 826Q513 816 504 807T471 781Q441 763 414 750T365 728T331 717T317 716Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="362" d="M137 473Q137 476 147 476Q161 476 170 466T173 424L163 375Q189 431 223 457T302 483Q331 483 351 471T372 442Q372 425 363 418T352 411Q343 422 328 430T289 438Q260 438 235 422T190 376T155 304T128 209L84 0H36L137 473ZM191 547Q190 548 199 559T226 585T268 620T320 657Q337 670 347 673T366 677Q375 677 381 671T387 657Q387 647 378 638T345 612Q315 594 288 581T239 559T205 548T191 547Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="593" d="M299 277L418 97Q430 78 440 66T460 46T481 35T508 32Q509 32 509 27T507 14T496 2T470 -4Q458 -4 448 -1T426 11T403 36T374 78L243 277H154L94 0H45L176 616Q181 647 212 647H366Q471 647 525 607T580 490Q580 390 516 334T335 277H299ZM336 320Q432 320 480 365T528 485Q528 542 486 572T363 603H223L163 320H336ZM148 -267Q146 -269 142 -268T133 -265T126 -259T125 -252L232 -63Q233 -62 240 -62T255 -65T270 -73T277 -88Q277 -96 274 -104T263 -123L148 -267Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="362" d="M137 473Q137 476 147 476Q161 476 170 466T173 424L163 375Q189 431 223 457T302 483Q331 483 351 471T372 442Q372 425 363 418T352 411Q343 422 328 430T289 438Q260 438 235 422T190 376T155 304T128 209L84 0H36L137 473ZM-49 -267Q-51 -269 -55 -268T-64 -265T-71 -259T-72 -252L35 -63Q36 -62 43 -62T58 -65T73 -73T80 -88Q80 -96 77 -104T66 -123L-49 -267Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="593" d="M299 277L418 97Q430 78 440 66T460 46T481 35T508 32Q509 32 509 27T507 14T496 2T470 -4Q458 -4 448 -1T426 11T403 36T374 78L243 277H154L94 0H45L176 616Q181 647 212 647H366Q471 647 525 607T580 490Q580 390 516 334T335 277H299ZM336 320Q432 320 480 365T528 485Q528 542 486 572T363 603H223L163 320H336ZM372 698Q370 698 355 714T319 761Q296 795 289 811T281 835Q281 846 286 851T299 856Q309 856 319 844T349 794Q357 780 363 768T378 741Q392 755 402 766T430 795Q462 828 479 842T507 856Q521 856 521 843Q521 831 506 813T455 763Q420 731 397 715T372 698Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="362" d="M137 473Q137 476 147 476Q161 476 170 466T173 424L163 375Q189 431 223 457T302 483Q331 483 351 471T372 442Q372 425 363 418T352 411Q343 422 328 430T289 438Q260 438 235 422T190 376T155 304T128 209L84 0H36L137 473ZM247 529Q245 529 230 545T194 592Q171 626 164 642T156 666Q156 677 161 682T174 687Q184 687 194 675T224 625Q232 611 238 599T253 572Q267 586 277 597T305 626Q337 659 354 673T382 687Q396 687 396 674Q396 662 381 644T330 594Q295 562 272 546T247 529Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="564" d="M269 -10Q200 -10 149 11T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q72 132 89 110T132 70T193 43T272 33Q356 33 398 69T440 167Q440 195 428 215T396 251T348 279T289 305Q257 318 227 332T174 367T136 415T121 482Q121 519 136 551T178 606T245 643T334 657Q401 657 446 638T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q509 527 494 547T455 582T402 605T332 614Q296 614 267 605T216 578T184 537T172 487Q172 458 183 437T215 400T263 371T324 345Q355 332 385 318T438 284T476 237T490 172Q490 91 433 41T269 -10ZM315 716Q314 717 323 728T350 754T392 789T444 826Q461 839 471 842T490 846Q499 846 505 840T511 826Q511 816 502 807T469 781Q439 763 412 750T363 728T329 717T315 716Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="475" d="M218 -10Q161 -10 117 7T47 51Q36 62 29 73T22 94Q22 102 26 107T34 116T43 121T49 122Q68 85 112 58T221 30Q284 30 318 55T352 122Q352 141 343 154T318 177T279 196T230 214Q203 223 177 233T131 259T98 295T85 348Q85 376 97 401T132 444T186 472T257 483Q311 483 351 467T415 427Q438 402 438 383Q438 375 434 370T425 361T416 357T410 358Q393 395 351 419T253 443Q223 443 201 436T163 416T140 387T132 354Q132 334 142 319T169 293T209 273T258 254Q284 245 309 235T354 210T386 176T398 127Q398 65 351 28T218 -10ZM235 547Q234 548 243 559T270 585T312 620T364 657Q381 670 391 673T410 677Q419 677 425 671T431 657Q431 647 422 638T389 612Q359 594 332 581T283 559T249 548T235 547Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="564" d="M269 -10Q200 -10 149 11T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q72 132 89 110T132 70T193 43T272 33Q356 33 398 69T440 167Q440 195 428 215T396 251T348 279T289 305Q257 318 227 332T174 367T136 415T121 482Q121 519 136 551T178 606T245 643T334 657Q401 657 446 638T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q509 527 494 547T455 582T402 605T332 614Q296 614 267 605T216 578T184 537T172 487Q172 458 183 437T215 400T263 371T324 345Q355 332 385 318T438 284T476 237T490 172Q490 91 433 41T269 -10ZM268 714Q254 714 254 726Q254 738 269 756T320 807Q355 839 378 855T403 872Q405 872 420 856T456 809Q479 775 486 759T493 735Q493 724 488 719T476 714Q466 714 456 726T426 776Q418 790 412 802T397 829L345 775Q313 741 296 728T268 714Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="475" d="M218 -10Q161 -10 117 7T47 51Q36 62 29 73T22 94Q22 102 26 107T34 116T43 121T49 122Q68 85 112 58T221 30Q284 30 318 55T352 122Q352 141 343 154T318 177T279 196T230 214Q203 223 177 233T131 259T98 295T85 348Q85 376 97 401T132 444T186 472T257 483Q311 483 351 467T415 427Q438 402 438 383Q438 375 434 370T425 361T416 357T410 358Q393 395 351 419T253 443Q223 443 201 436T163 416T140 387T132 354Q132 334 142 319T169 293T209 273T258 254Q284 245 309 235T354 210T386 176T398 127Q398 65 351 28T218 -10ZM189 545Q175 545 175 557Q175 569 190 587T241 638Q276 670 299 686T324 703Q326 703 341 687T377 640Q400 606 407 590T414 566Q414 555 409 550T397 545Q387 545 377 557T347 607Q339 621 333 633T318 660L266 606Q234 572 217 559T189 545Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="564" d="M269 -10Q200 -10 149 11T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q72 132 89 110T132 70T193 43T272 33Q356 33 398 69T440 167Q440 195 428 215T396 251T348 279T289 305Q257 318 227 332T174 367T136 415T121 482Q121 519 136 551T178 606T245 643T334 657Q401 657 446 638T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q509 527 494 547T455 582T402 605T332 614Q296 614 267 605T216 578T184 537T172 487Q172 458 183 437T215 400T263 371T324 345Q355 332 385 318T438 284T476 237T490 172Q490 91 433 41T269 -10ZM131 -219Q129 -222 125 -221T116 -217T110 -211T109 -204Q138 -160 161 -125Q171 -111 180 -97T197 -71Q206 -58 213 -46Q214 -45 220 -46T235 -50T249 -58T256 -73Q256 -90 239 -108L131 -219Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="475" d="M218 -10Q161 -10 117 7T47 51Q36 62 29 73T22 94Q22 102 26 107T34 116T43 121T49 122Q68 85 112 58T221 30Q284 30 318 55T352 122Q352 141 343 154T318 177T279 196T230 214Q203 223 177 233T131 259T98 295T85 348Q85 376 97 401T132 444T186 472T257 483Q311 483 351 467T415 427Q438 402 438 383Q438 375 434 370T425 361T416 357T410 358Q393 395 351 419T253 443Q223 443 201 436T163 416T140 387T132 354Q132 334 142 319T169 293T209 273T258 254Q284 245 309 235T354 210T386 176T398 127Q398 65 351 28T218 -10ZM87 -219Q85 -222 81 -221T72 -217T66 -211T65 -204Q94 -160 117 -125Q127 -111 136 -97T153 -71Q162 -58 169 -46Q170 -45 176 -46T191 -50T205 -58T212 -73Q212 -90 195 -108L87 -219Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="564" d="M269 -10Q200 -10 149 11T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q72 132 89 110T132 70T193 43T272 33Q356 33 398 69T440 167Q440 195 428 215T396 251T348 279T289 305Q257 318 227 332T174 367T136 415T121 482Q121 519 136 551T178 606T245 643T334 657Q401 657 446 638T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q509 527 494 547T455 582T402 605T332 614Q296 614 267 605T216 578T184 537T172 487Q172 458 183 437T215 400T263 371T324 345Q355 332 385 318T438 284T476 237T490 172Q490 91 433 41T269 -10ZM361 703Q359 703 344 719T308 766Q285 800 278 816T270 840Q270 851 275 856T288 861Q298 861 308 849T338 799Q346 785 352 773T367 746Q381 760 391 771T419 800Q451 833 468 847T496 861Q510 861 510 848Q510 836 495 818T444 768Q409 736 386 720T361 703Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="475" d="M218 -10Q161 -10 117 7T47 51Q36 62 29 73T22 94Q22 102 26 107T34 116T43 121T49 122Q68 85 112 58T221 30Q284 30 318 55T352 122Q352 141 343 154T318 177T279 196T230 214Q203 223 177 233T131 259T98 295T85 348Q85 376 97 401T132 444T186 472T257 483Q311 483 351 467T415 427Q438 402 438 383Q438 375 434 370T425 361T416 357T410 358Q393 395 351 419T253 443Q223 443 201 436T163 416T140 387T132 354Q132 334 142 319T169 293T209 273T258 254Q284 245 309 235T354 210T386 176T398 127Q398 65 351 28T218 -10ZM289 529Q287 529 272 545T236 592Q213 626 206 642T198 666Q198 677 203 682T216 687Q226 687 236 675T266 625Q274 611 280 599T295 572Q309 586 319 597T347 626Q379 659 396 673T424 687Q438 687 438 674Q438 662 423 644T372 594Q337 562 314 546T289 529Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="603" d="M355 602H144Q121 602 121 614Q121 620 123 628T131 642Q135 647 151 647H624Q647 647 647 635Q647 629 645 621T637 607Q635 603 630 603T617 602H405L277 0H227L355 602ZM135 -267Q133 -269 129 -268T120 -265T113 -259T112 -252L219 -63Q220 -62 227 -62T242 -65T257 -73T264 -88Q264 -96 261 -104T250 -123L135 -267Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="362" d="M203 -10Q139 -10 109 31T95 156L153 430H77L85 473H162L176 539Q181 567 190 578T215 589Q222 589 227 587T233 583L210 473H362L353 430H201L142 156Q128 90 146 61T210 31Q232 31 250 40T278 63Q280 64 285 58T290 40Q290 27 278 15Q253 -10 203 -10ZM55 -267Q53 -269 49 -268T40 -265T33 -259T32 -252L139 -63Q140 -62 147 -62T162 -65T177 -73T184 -88Q184 -96 181 -104T170 -123L55 -267Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="603" d="M355 602H144Q121 602 121 614Q121 620 123 628T131 642Q135 647 151 647H624Q647 647 647 635Q647 629 645 621T637 607Q635 603 630 603T617 602H405L277 0H227L355 602ZM391 698Q389 698 374 714T338 761Q315 795 308 811T300 835Q300 846 305 851T318 856Q328 856 338 844T368 794Q376 780 382 768T397 741Q411 755 421 766T449 795Q481 828 498 842T526 856Q540 856 540 843Q540 831 525 813T474 763Q439 731 416 715T391 698Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="362" d="M203 -10Q139 -10 109 31T95 156L153 430H77L85 473H162L176 539Q181 567 190 578T215 589Q222 589 227 587T233 583L210 473H362L353 430H201L142 156Q128 90 146 61T210 31Q232 31 250 40T278 63Q280 64 285 58T290 40Q290 27 278 15Q253 -10 203 -10ZM291 509Q289 507 285 508T276 511T269 518T268 525L378 731Q379 732 386 732T401 729T416 721T423 706Q423 698 420 690T409 671L291 509Z" />
+<glyph unicode="&#x166;" glyph-name="Tbar" horiz-adv-x="603" d="M355 602H144Q121 602 121 614Q121 620 123 628T131 642Q135 647 151 647H624Q647 647 647 635Q647 629 645 621T637 607Q635 603 630 603T617 602H405L277 0H227L355 602ZM219 322Q196 322 196 335Q196 340 198 348T205 361Q208 365 213 366T225 367H437Q461 367 461 354Q461 349 459 341T452 327Q449 324 444 323T432 322H219Z" />
+<glyph unicode="&#x167;" glyph-name="tbar" horiz-adv-x="362" d="M203 -10Q139 -10 109 31T95 156L153 430H77L85 473H162L176 539Q181 567 190 578T215 589Q222 589 227 587T233 583L210 473H362L353 430H201L142 156Q128 90 146 61T210 31Q232 31 250 40T278 63Q280 64 285 58T290 40Q290 27 278 15Q253 -10 203 -10ZM60 235Q37 235 37 248Q37 253 39 261T46 274Q49 278 54 279T66 280H278Q302 280 302 267Q302 262 300 254T293 240Q290 237 285 236T273 235H60Z" />
+<glyph unicode="&#x168;" glyph-name="Utilde" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM486 669Q461 669 445 681T414 710Q400 725 391 732T371 739Q356 739 347 728T332 697Q324 674 308 674Q300 674 291 681Q299 726 320 753T377 780Q402 780 418 767T449 738Q462 723 471 716T492 709Q506 709 515 720T531 751Q535 762 540 768T554 774Q562 774 571 767Q563 722 542 696T486 669Z" />
+<glyph unicode="&#x169;" glyph-name="utilde" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM410 544Q385 544 369 556T338 585Q324 600 315 607T295 614Q280 614 271 603T256 572Q248 549 232 549Q224 549 215 556Q223 601 244 628T301 655Q326 655 342 642T373 613Q386 598 395 591T416 584Q430 584 439 595T455 626Q459 637 464 643T478 649Q486 649 495 642Q487 597 466 571T410 544Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM328 736Q305 736 305 749Q305 754 307 762T314 775Q317 779 322 780T334 781H546Q570 781 570 768Q570 763 568 755T561 741Q558 738 553 737T541 736H328Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM246 567Q223 567 223 580Q223 585 225 593T232 606Q235 610 240 611T252 612H464Q488 612 488 599Q488 594 486 586T479 572Q476 569 471 568T459 567H246Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM436 714Q403 714 380 724T342 748Q325 765 318 783T311 815Q311 826 317 834T341 842Q350 803 374 780T437 757Q475 757 501 780T542 842Q559 842 565 834T572 818Q572 807 563 789T538 755Q519 736 493 725T436 714Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM344 545Q311 545 288 555T250 579Q233 596 226 614T219 646Q219 657 225 665T249 673Q258 634 282 611T345 588Q383 588 409 611T450 673Q467 673 473 665T480 649Q480 638 471 620T446 586Q427 567 401 556T344 545Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM435 704Q387 704 361 732T334 798Q334 823 343 845T368 885T406 912T454 923Q502 923 528 895T555 829Q555 804 546 782T521 742T482 714T435 704ZM436 738Q454 738 468 745T494 765T511 794T517 827Q517 851 500 870T453 890Q435 890 421 883T395 863T378 834T372 801Q372 777 389 758T436 738Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM346 530Q298 530 272 558T245 624Q245 649 254 671T279 711T317 738T365 749Q413 749 439 721T466 655Q466 630 457 608T432 568T393 540T346 530ZM347 564Q365 564 379 571T405 591T422 620T428 653Q428 677 411 696T364 716Q346 716 332 709T306 689T289 660T283 627Q283 603 300 584T347 564Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q553 111 489 51T319 -10ZM470 716Q469 717 477 727T500 753T537 788T586 828Q604 841 614 845T632 850Q641 850 647 845T654 830Q654 821 646 811T615 784Q587 764 562 751T516 729T484 717T470 716ZM308 716Q306 717 314 727T337 753T375 788T424 828Q441 841 451 845T470 850Q478 850 484 845T491 830Q491 821 483 811T452 784Q424 764 399 751T354 729T322 717T308 716Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="569" d="M463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 22T494 10T484 -1T463 -7ZM372 547Q371 548 379 558T402 584T439 619T488 659Q506 672 516 676T534 681Q543 681 549 676T556 661Q556 652 548 642T517 615Q489 595 464 582T418 560T386 548T372 547ZM210 547Q208 548 216 558T239 584T277 619T326 659Q343 672 353 676T372 681Q380 681 386 676T393 661Q393 652 385 642T354 615Q326 595 301 582T256 560T224 548T210 547Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="665" d="M319 -10Q255 -10 206 8T128 62T89 152T95 280L169 627Q172 639 177 644T194 650H201Q224 650 218 624L145 276Q119 153 166 94T322 35Q406 35 458 86T531 239L614 627Q618 650 639 650H645Q668 650 662 624L579 229Q544 69 438 15Q373 -17 350 -46T326 -99Q326 -116 337 -126T365 -136Q386 -136 402 -120Q410 -121 415 -126T421 -142Q421 -158 404 -168T361 -179Q326 -179 303 -159T280 -107Q280 -82 295 -57T345 -9Q338 -9 332 -9T319 -10Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="569" d="M325 -124Q325 -92 350 -60T433 -1Q413 7 405 29T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48T72 118T76 212L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27Q496 27 496 23T495 14T489 3T475 -5Q414 -37 393 -65T371 -116Q371 -133 382 -143T410 -153Q431 -153 447 -137Q455 -138 460 -143T466 -159Q466 -175 449 -185T406 -196Q371 -196 348 -176T325 -124Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="910" d="M154 640Q154 643 162 647T179 651T197 644T205 619L221 45L476 521Q483 535 491 541T517 547Q538 547 545 538T555 513L611 46L879 648Q879 649 888 649T905 646T915 628T905 588L638 2Q638 1 633 1T622 0T611 0T606 0Q605 0 600 0T588 0T576 0T570 2L512 497L243 0H178L154 640ZM441 704Q427 704 427 716Q427 728 442 746T493 797Q528 829 551 845T576 862Q578 862 593 846T629 799Q652 765 659 749T666 725Q666 714 661 709T649 704Q639 704 629 716T599 766Q591 780 585 792T570 819L518 765Q486 731 469 718T441 704Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="713" d="M126 341Q126 371 122 390T109 420T88 435T60 440Q59 440 59 446T61 458T73 471T97 477Q124 477 142 459Q173 428 172 338L169 50L343 372Q352 390 362 395T387 400Q421 400 425 363L459 50L663 475Q663 476 671 476T686 471T696 454T687 418L482 0H422L379 342L190 0H125L126 341ZM309 532Q295 532 295 544Q295 556 310 574T361 625Q396 657 419 673T444 690Q446 690 461 674T497 627Q520 593 527 577T534 553Q534 542 529 537T517 532Q507 532 497 544T467 594Q459 608 453 620T438 647L386 593Q354 559 337 546T309 532Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="569" d="M271 279L108 647H162L303 322L584 651Q585 652 593 650T608 642T614 622T593 586L320 274L262 0H212L271 279ZM268 714Q254 714 254 726Q254 738 269 756T320 807Q355 839 378 855T403 872Q405 872 420 856T456 809Q479 775 486 759T493 735Q493 724 488 719T476 714Q466 714 456 726T426 776Q418 790 412 802T397 829L345 775Q313 741 296 728T268 714Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="487" d="M3 -200Q-43 -200 -64 -177Q-73 -168 -73 -156Q-73 -144 -64 -137T-53 -131Q-44 -142 -29 -149T0 -156Q14 -156 28 -151T58 -132T93 -95T138 -34L178 26Q155 29 152 58L113 343Q105 396 88 417T39 438Q39 438 39 444T41 457T51 471T76 477Q101 477 118 459Q133 444 144 415T160 339L196 52L459 473Q459 473 467 473T485 473T502 472T510 471L184 -44Q155 -91 132 -121T87 -168T46 -193T3 -200ZM197 545Q183 545 183 557Q183 569 198 587T249 638Q284 670 307 686T332 703Q334 703 349 687T385 640Q408 606 415 590T422 566Q422 555 417 550T405 545Q395 545 385 557T355 607Q347 621 341 633T326 660L274 606Q242 572 225 559T197 545Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="569" d="M271 279L108 647H162L303 322L584 651Q585 652 593 650T608 642T614 622T593 586L320 274L262 0H212L271 279ZM453 709Q416 709 416 738Q416 756 425 769T455 783Q492 783 492 754Q492 736 483 723T453 709ZM294 709Q257 709 257 738Q257 756 266 769T295 783Q333 783 333 754Q333 736 323 723T294 709Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="591" d="M7 34L9 44L542 602H165Q136 602 136 620Q136 628 141 637T147 647H617L610 613L608 603L74 46H471Q500 46 500 27Q500 19 495 10T488 0H0L7 34ZM308 716Q307 717 316 728T343 754T385 789T437 826Q454 839 464 842T483 846Q492 846 498 840T504 826Q504 816 495 807T462 781Q432 763 405 750T356 728T322 717T308 716Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="494" d="M7 37L418 432H129Q103 432 103 449Q103 456 107 464T113 473H480V437L68 41H368Q394 41 394 24Q394 17 390 9T384 0H7V37ZM231 547Q230 548 239 559T266 585T308 620T360 657Q377 670 387 673T406 677Q415 677 421 671T427 657Q427 647 418 638T385 612Q355 594 328 581T279 559T245 548T231 547Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="591" d="M7 34L9 44L542 602H165Q136 602 136 620Q136 628 141 637T147 647H617L610 613L608 603L74 46H471Q500 46 500 27Q500 19 495 10T488 0H0L7 34ZM383 715Q342 715 342 746Q342 765 352 779T384 794Q426 794 426 763Q426 744 415 730T383 715Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="494" d="M7 37L418 432H129Q103 432 103 449Q103 456 107 464T113 473H480V437L68 41H368Q394 41 394 24Q394 17 390 9T384 0H7V37ZM297 546Q256 546 256 577Q256 596 266 610T298 625Q340 625 340 594Q340 575 329 561T297 546Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="591" d="M7 34L9 44L542 602H165Q136 602 136 620Q136 628 141 637T147 647H617L610 613L608 603L74 46H471Q500 46 500 27Q500 19 495 10T488 0H0L7 34ZM362 703Q360 703 345 719T309 766Q286 800 279 816T271 840Q271 851 276 856T289 861Q299 861 309 849T339 799Q347 785 353 773T368 746Q382 760 392 771T420 800Q452 833 469 847T497 861Q511 861 511 848Q511 836 496 818T445 768Q410 736 387 720T362 703Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="494" d="M7 37L418 432H129Q103 432 103 449Q103 456 107 464T113 473H480V437L68 41H368Q394 41 394 24Q394 17 390 9T384 0H7V37ZM295 529Q293 529 278 545T242 592Q219 626 212 642T204 666Q204 677 209 682T222 687Q232 687 242 675T272 625Q280 611 286 599T301 572Q315 586 325 597T353 626Q385 659 402 673T430 687Q444 687 444 674Q444 662 429 644T378 594Q343 562 320 546T295 529Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="372" d="M91 -3Q83 -3 77 0T72 6L153 387H76Q54 387 54 399Q54 404 56 412T63 425Q68 430 82 430H162L178 503Q192 572 227 614T325 657Q355 657 374 650T406 630Q418 618 418 603Q418 590 410 583T399 578Q389 594 369 604T323 615Q279 615 257 583T223 496L209 430H339Q361 430 361 418Q361 413 359 405T352 393Q346 387 333 387H200L127 38Q122 15 114 6T91 -3Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="564" d="M269 -10Q200 -10 149 11T69 61Q52 78 42 96T32 131Q32 140 36 146T47 155T58 158T63 158Q72 132 89 110T132 70T193 43T272 33Q356 33 398 69T440 167Q440 195 428 215T396 251T348 279T289 305Q257 318 227 332T174 367T136 415T121 482Q121 519 136 551T178 606T245 643T334 657Q401 657 446 638T516 594Q532 578 540 562T548 532Q548 523 544 517T534 508T523 505T517 505Q509 527 494 547T455 582T402 605T332 614Q296 614 267 605T216 578T184 537T172 487Q172 458 183 437T215 400T263 371T324 345Q355 332 385 318T438 284T476 237T490 172Q490 91 433 41T269 -10ZM149 -267Q147 -269 143 -268T134 -265T127 -259T126 -252L233 -63Q234 -62 241 -62T256 -65T271 -73T278 -88Q278 -96 275 -104T264 -123L149 -267Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="475" d="M218 -10Q161 -10 117 7T47 51Q36 62 29 73T22 94Q22 102 26 107T34 116T43 121T49 122Q68 85 112 58T221 30Q284 30 318 55T352 122Q352 141 343 154T318 177T279 196T230 214Q203 223 177 233T131 259T98 295T85 348Q85 376 97 401T132 444T186 472T257 483Q311 483 351 467T415 427Q438 402 438 383Q438 375 434 370T425 361T416 357T410 358Q393 395 351 419T253 443Q223 443 201 436T163 416T140 387T132 354Q132 334 142 319T169 293T209 273T258 254Q284 245 309 235T354 210T386 176T398 127Q398 65 351 28T218 -10ZM105 -267Q103 -269 99 -268T90 -265T83 -259T82 -252L189 -63Q190 -62 197 -62T212 -65T227 -73T234 -88Q234 -96 231 -104T220 -123L105 -267Z" />
+<glyph unicode="&#x21A;" glyph-name="uni021A" horiz-adv-x="603" d="M355 602H144Q121 602 121 614Q121 620 123 628T131 642Q135 647 151 647H624Q647 647 647 635Q647 629 645 621T637 607Q635 603 630 603T617 602H405L277 0H227L355 602ZM135 -267Q133 -269 129 -268T120 -265T113 -259T112 -252L219 -63Q220 -62 227 -62T242 -65T257 -73T264 -88Q264 -96 261 -104T250 -123L135 -267Z" />
+<glyph unicode="&#x21B;" glyph-name="uni021B" horiz-adv-x="362" d="M203 -10Q139 -10 109 31T95 156L153 430H77L85 473H162L176 539Q181 567 190 578T215 589Q222 589 227 587T233 583L210 473H362L353 430H201L142 156Q128 90 146 61T210 31Q232 31 250 40T278 63Q280 64 285 58T290 40Q290 27 278 15Q253 -10 203 -10ZM55 -267Q53 -269 49 -268T40 -265T33 -259T32 -252L139 -63Q140 -62 147 -62T162 -65T177 -73T184 -88Q184 -96 181 -104T170 -123L55 -267Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="534" d="M231 545Q217 545 217 557Q217 569 232 587T283 638Q318 670 341 686T366 703Q368 703 383 687T419 640Q442 606 449 590T456 566Q456 555 451 550T439 545Q429 545 419 557T389 607Q381 621 375 633T360 660L308 606Q276 572 259 559T231 545Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="534" d="M331 529Q329 529 314 545T278 592Q255 626 248 642T240 666Q240 677 245 682T258 687Q268 687 278 675T308 625Q316 611 322 599T337 572Q351 586 361 597T389 626Q421 659 438 673T466 687Q480 687 480 674Q480 662 465 644T414 594Q379 562 356 546T331 529Z" />
+<glyph unicode="&#x2C9;" glyph-name="overscore" horiz-adv-x="534" d="M233 567Q210 567 210 580Q210 585 212 593T219 606Q222 610 227 611T239 612H451Q475 612 475 599Q475 594 473 586T466 572Q463 569 458 568T446 567H233Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="534" d="M348 545Q315 545 292 555T254 579Q237 596 230 614T223 646Q223 657 229 665T253 673Q262 634 286 611T349 588Q387 588 413 611T454 673Q471 673 477 665T484 649Q484 638 475 620T450 586Q431 567 405 556T348 545Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="534" d="M341 546Q300 546 300 577Q300 596 310 610T342 625Q384 625 384 594Q384 575 373 561T341 546Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="534" d="M346 544Q298 544 272 572T245 638Q245 663 254 685T279 725T317 752T365 763Q413 763 439 735T466 669Q466 644 457 622T432 582T393 554T346 544ZM347 578Q365 578 379 585T405 605T422 634T428 667Q428 691 411 710T364 730Q346 730 332 723T306 703T289 674T283 641Q283 617 300 598T347 578Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="534" d="M279 1Q213 -32 190 -61T166 -114Q166 -131 177 -141T205 -151Q226 -151 242 -135Q250 -136 255 -141T261 -157Q261 -173 244 -183T201 -194Q166 -194 143 -174T120 -122Q120 -84 156 -47T273 18L279 1Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="533" d="M398 544Q373 544 357 556T326 585Q312 600 303 607T283 614Q268 614 259 603T244 572Q236 549 220 549Q212 549 203 556Q211 601 232 628T289 655Q314 655 330 642T361 613Q374 598 383 591T404 584Q418 584 427 595T443 626Q447 637 452 643T466 649Q474 649 483 642Q475 597 454 571T398 544Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="534" d="M356 547Q355 548 363 558T386 584T423 619T472 659Q490 672 500 676T518 681Q527 681 533 676T540 661Q540 652 532 642T501 615Q473 595 448 582T402 560T370 548T356 547ZM194 547Q192 548 200 558T223 584T261 619T310 659Q327 672 337 676T356 681Q364 681 370 676T377 661Q377 652 369 642T338 615Q310 595 285 582T240 560T208 548T194 547Z" />
+<glyph unicode="&#x394;" glyph-name="increment" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x3A9;" glyph-name="Omega" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x3BC;" glyph-name="mu" horiz-adv-x="569" d="M56 -267Q19 -267 2 -243T-5 -166L131 473H179L123 209Q105 125 139 80T241 35Q300 35 341 65T418 155L485 473H533L453 95Q444 54 456 41T496 27V21Q497 16 494 10T484 -1T463 -7Q429 -7 412 16T404 90Q375 42 332 16T234 -10Q189 -10 155 5T99 48L91 60L43 -165Q35 -206 47 -219T90 -233V-238Q91 -244 88 -250T78 -261T56 -267Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="783" d="M409 -10Q337 -10 278 14T175 83T108 188T84 322Q84 394 108 455T176 561T280 631T410 657Q482 657 542 633T645 564T712 459T736 325Q736 253 712 192T643 86T539 16T409 -10ZM411 26Q474 26 526 48T616 109T675 203T696 324Q696 388 675 443T615 537T524 599T409 622Q346 622 294 600T204 539T145 444T124 323Q124 258 145 204T205 110T295 48T411 26ZM325 139Q306 139 306 160V459Q306 470 313 477T331 485H424Q489 485 523 457T558 375Q558 321 524 293T426 264H351V160Q351 139 331 139H325ZM427 300Q470 300 492 319T514 374Q514 409 492 428T424 448H350V300H427Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="535" d="M83 227Q61 227 61 239Q61 244 63 252T70 264Q76 270 88 270H458Q480 270 480 258Q480 252 478 244T471 232Q466 227 453 227H83Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="699" d="M83 227Q61 227 61 239Q61 244 63 252T70 264Q76 270 88 270H622Q643 270 643 258Q643 252 641 244T634 232Q630 227 616 227H83Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="226" d="M154 438Q153 437 146 437T131 440T115 448T108 463Q108 471 111 479T123 498L241 660Q243 662 247 661T256 658T262 651T264 644L154 438Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="226" d="M117 436Q115 434 111 435T102 438T95 445T94 452L204 658Q205 659 212 659T227 656T242 648T249 633Q249 625 246 617T235 598L117 436Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="225" d="M-23 -144Q-25 -146 -29 -145T-38 -142T-45 -135T-46 -128L64 78Q65 79 72 79T87 76T102 68T109 53Q109 45 106 38T95 19L-23 -144Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="364" d="M154 439Q153 438 146 438T131 441T115 449T108 464Q108 472 111 480T123 499L241 661Q243 663 247 662T256 659T262 652T264 645L154 439ZM292 438Q292 437 285 437T269 440T254 448T247 463Q247 471 250 479T262 498L380 660Q381 662 385 661T394 658T401 651T402 644L292 438Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="364" d="M117 436Q115 434 111 435T102 438T95 445T94 452L204 658Q205 659 212 659T227 656T242 648T249 633Q249 625 246 617T235 598L117 436ZM255 435Q253 433 249 434T240 437T234 444T233 451Q264 508 288 554Q298 573 307 592T325 625Q334 642 342 657Q343 658 350 658T365 655T381 647T388 632Q388 624 385 616T373 597L255 435Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="364" d="M-23 -144Q-25 -146 -29 -145T-38 -142T-45 -135T-46 -128L64 78Q65 79 72 79T87 76T102 68T109 53Q109 45 106 38T95 19L-23 -144ZM115 -145Q113 -147 109 -146T101 -143T94 -136T93 -129L203 77Q203 78 210 78T226 75T241 67T248 52Q248 37 233 18L115 -145Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="531" d="M183 -156Q169 -156 166 -151T164 -138L255 309Q258 323 265 344T281 381L108 372Q91 370 84 373T76 388Q76 404 83 413T112 421L288 413L320 619Q322 637 327 644T349 652Q383 652 373 616L319 411L493 421Q510 423 517 420T524 404Q524 389 517 379T488 371L313 380Q313 363 312 343T307 309L208 -139Q205 -156 192 -156H183Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="531" d="M183 -156Q169 -156 166 -151T164 -138L214 107L50 98Q18 94 18 114Q18 129 25 139T54 147L220 138L255 309Q258 323 265 344T281 381L108 372Q91 370 84 373T76 388Q76 404 83 413T112 421L288 413L320 619Q322 637 327 644T349 652Q383 652 373 616L319 411L493 421Q510 423 517 420T524 404Q524 389 517 379T488 371L313 380Q313 363 312 343T307 309L269 137L435 146Q467 150 467 130Q467 115 460 105T431 97L263 106L208 -139Q205 -156 192 -156H183Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="334" d="M170 160Q133 160 108 185T83 248Q83 285 108 310T170 336Q206 336 231 311T256 248Q256 211 231 186T170 160Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="608" d="M450 -5Q413 -5 413 22Q413 50 426 62Q436 72 454 72Q491 72 491 46Q491 18 479 5Q469 -5 450 -5ZM259 -5Q222 -5 222 22Q222 50 235 62Q245 72 263 72Q300 72 300 46Q300 18 287 5Q278 -5 259 -5ZM68 -5Q31 -5 31 22Q31 50 43 62Q53 72 72 72Q109 72 109 46Q109 18 96 5Q86 -5 68 -5Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1071" d="M235 358Q209 358 186 367T145 393T117 435T106 489Q106 522 118 552T151 606T202 642T266 656Q293 656 316 647T357 621T385 579T395 525Q395 492 383 462T350 408T299 372T235 358ZM62 -7Q60 -9 51 -4T42 13Q42 23 47 31T69 54L718 658Q720 660 728 655T737 638Q737 629 732 621T710 598L62 -7ZM843 -5Q817 -5 794 4T752 30T724 72T713 126Q713 159 725 189T758 243T809 279T873 293Q900 293 923 284T964 258T992 216T1002 162Q1002 129 990 99T957 45T907 9T843 -5ZM514 -5Q488 -5 465 4T423 30T395 72T384 126Q384 159 396 189T429 243T480 279T544 293Q571 293 594 284T635 258T663 216T673 162Q673 129 661 99T628 45T578 9T514 -5ZM238 396Q263 396 284 406T320 434T345 474T354 523Q354 565 328 591T263 618Q238 618 217 608T181 580T156 540T147 491Q147 449 174 423T238 396ZM846 33Q870 33 891 43T928 71T953 111T962 160Q962 202 935 228T870 255Q846 255 825 245T788 217T763 177T754 128Q754 86 781 60T846 33ZM517 33Q541 33 562 43T599 71T624 111T633 160Q633 202 606 228T541 255Q517 255 496 245T459 217T434 177T425 128Q425 86 452 60T517 33Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="336" d="M219 83Q205 83 186 99T132 153Q99 190 81 216T63 247Q64 251 92 276T169 336Q215 369 242 385T283 401Q291 401 295 396T300 383Q300 359 195 293Q172 278 151 265T107 238Q125 223 140 209T173 179Q193 160 205 148T225 127T236 112T239 100Q239 93 234 88T219 83Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="336" d="M57 83Q49 83 45 88T40 101Q40 124 144 191Q168 206 188 219T232 246Q215 261 200 274T167 305Q127 342 114 357T100 384Q100 391 106 396T120 401Q134 401 154 385T207 331Q241 293 259 267T277 236Q275 233 247 208T170 148Q125 115 98 99T57 83Z" />
+<glyph unicode="&#x2044;" glyph-name="uni2044" horiz-adv-x="653" d="M15 -7Q13 -9 6 -7T-4 3T-4 23T20 54L678 659Q680 660 686 658T696 648T696 628T672 597L15 -7Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="656" d="M110 384Q88 384 88 396Q88 401 90 409T97 421Q103 427 116 427H193Q209 469 231 508T284 579Q323 618 368 637T471 657Q522 657 557 642T617 601Q634 584 643 563T652 524Q652 513 647 507T636 497T625 493T619 494Q605 551 567 583T466 615Q421 615 385 599T318 552Q292 526 274 494T242 427H474Q495 427 495 415Q495 409 493 401T486 389Q482 384 468 384H228Q219 352 214 322T206 268H440Q461 268 461 256Q461 251 459 243T452 231Q448 226 434 226H205Q206 183 218 148T253 87T306 47T377 33Q432 33 474 60T545 133Q546 134 549 133T557 128T565 121T569 109Q569 87 536 54Q510 28 468 9T379 -10Q272 -10 215 53T155 226H76Q54 226 54 237Q54 243 56 251T63 263Q68 268 82 268H157Q159 293 164 322T179 384H110Z" />
+<glyph unicode="&#x2113;" glyph-name="afii61289" horiz-adv-x="434" d="M199 341Q228 365 251 395T291 456T317 513T326 558Q326 584 315 584Q308 584 300 573Q291 561 278 539T250 486T222 419T199 341ZM44 138Q30 151 19 172T7 228Q25 237 33 240T49 246L59 251Q63 321 81 383T126 497T183 588T245 652Q285 683 327 683Q367 683 395 652T423 558Q423 509 402 463T349 375T276 294T196 223Q196 218 196 214T195 204Q195 158 207 133T237 107Q254 107 273 133T310 217Q322 220 336 220Q355 220 373 213T404 186Q397 149 382 114T345 52T293 9T228 -8Q206 -8 182 1T135 30T94 78T66 146L44 138Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="687" d="M416 342Q400 342 400 361V628Q400 650 420 650H438Q448 650 452 646T461 633L541 465L620 633Q625 643 629 646T643 650H660Q680 650 680 628V361Q680 342 663 342H658Q642 342 642 361V597L562 425Q560 420 557 417T540 414Q527 414 524 417T519 425L438 596V361Q438 342 423 342H416ZM218 342Q201 342 201 361V612H111Q93 612 93 628V631Q93 648 111 648H331Q349 648 349 631V628Q349 612 331 612H241V361Q241 342 223 342H218Z" />
+<glyph unicode="&#x2126;" glyph-name="uni2126" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x212E;" glyph-name="estimated" horiz-adv-x="830" d="M415 671Q492 671 560 645T681 573T763 463T793 324H185Q175 324 175 313V116Q223 65 286 38T415 10Q490 10 555 43T667 131H724Q667 62 585 26T415 -11Q340 -11 272 14T151 84T67 191T36 330Q36 403 66 465T147 574T268 645T415 671ZM415 650Q345 650 282 622T175 543V358Q175 347 185 347H646Q655 347 655 358V542Q610 594 546 622T415 650Z" />
+<glyph unicode="&#x2202;" glyph-name="partialdiff" horiz-adv-x="538" d="M303 481Q335 481 361 471T404 445V464Q404 499 399 528T381 578T343 610T282 622Q247 622 208 612L189 679Q271 710 332 710Q421 710 464 649T508 475Q508 370 481 283T409 133T311 35T201 0Q165 0 134 10T80 44T43 102T30 187Q30 256 55 310T120 403T208 461T303 481ZM205 105Q229 105 256 121T310 167T359 243T393 349Q382 361 360 373T309 386Q275 386 244 371T188 331T149 270T134 194Q134 157 150 131T205 105Z" />
+<glyph unicode="&#x2206;" glyph-name="uni2206" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x220F;" glyph-name="product" horiz-adv-x="634" d="M484 0V607H150V0H50V700H584V0H484Z" />
+<glyph unicode="&#x2211;" glyph-name="summation" horiz-adv-x="475" d="M110 80H437V0H25V96L209 325L25 554V650H437V570H115L288 353V300L110 80Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="634" d="M131 244Q106 244 112 263L113 269Q116 279 121 283T140 288H513Q527 288 531 284T533 269L531 263Q527 244 504 244H131Z" />
+<glyph unicode="&#x2215;" glyph-name="fraction" horiz-adv-x="653" d="M15 -7Q13 -9 6 -7T-4 3T-4 23T20 54L678 659Q680 660 686 658T696 648T696 628T672 597L15 -7Z" />
+<glyph unicode="&#x2219;" glyph-name="middot" horiz-adv-x="240" d="M121 211Q84 211 84 237Q84 265 97 278Q106 288 125 288Q162 288 162 261Q162 233 149 221Q139 211 121 211Z" />
+<glyph unicode="&#x221A;" glyph-name="radical" horiz-adv-x="649" d="M256 501L369 171L566 700H649L386 0H283L136 433H30V501H256Z" />
+<glyph unicode="&#x221E;" glyph-name="infinity" horiz-adv-x="733" d="M703 271Q703 231 690 203T654 156T604 129T547 120Q518 120 493 128T447 150T405 181T366 217Q347 199 328 182T287 151T240 129T186 120Q157 120 129 129T79 156T44 203T30 271Q30 310 43 338T79 385T129 413T186 422Q215 422 240 414T286 392T327 361T366 325Q385 343 405 360T446 391T493 413T547 422Q576 422 604 414T654 387T689 340T703 271ZM187 222Q223 222 252 235T308 271Q282 292 253 306T187 320Q167 320 149 309T130 271Q130 244 148 233T187 222ZM546 320Q510 320 481 306T425 271Q451 249 480 236T546 222Q566 222 584 233T603 271Q603 298 585 309T546 320Z" />
+<glyph unicode="&#x222B;" glyph-name="integral'" horiz-adv-x="396" d="M164 596Q172 654 201 685Q228 714 271 723T375 724V648Q314 653 288 640T255 584L168 -32Q159 -95 128 -126Q105 -149 70 -155T-15 -155V-81Q22 -84 45 -75T74 -25L164 596Z" />
+<glyph unicode="&#x2248;" glyph-name="approxequal" horiz-adv-x="533" d="M310 114Q285 114 269 126T238 155Q224 170 215 177T195 184Q181 184 172 173T156 142Q149 120 132 120Q124 120 115 126Q123 171 144 198T201 225Q226 225 242 213T273 184Q286 168 295 161T316 154Q330 154 339 165T355 196Q359 207 364 213T378 219Q388 219 395 212Q387 167 366 141T310 114ZM351 267Q326 267 310 279T279 308Q265 323 256 330T236 337Q222 337 213 326T197 295Q190 273 173 273Q165 273 156 279Q164 324 185 351T242 378Q267 378 283 366T314 337Q327 321 336 314T357 307Q371 307 380 318T396 349Q400 360 405 366T419 372Q429 372 436 365Q428 320 407 294T351 267Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="634" d="M160 349Q137 349 137 361Q137 367 140 375T146 388Q152 394 165 394H389L490 537Q493 540 500 538Q515 538 520 526T511 494L440 394H529Q552 394 552 382Q552 376 550 368T543 355Q537 349 524 349H405L281 173H482Q505 173 505 161Q505 155 502 147T495 134Q490 128 476 128H252L147 -24Q146 -26 143 -26T137 -26Q122 -25 117 -13T126 19L202 128H113Q90 128 90 140Q90 146 92 154T99 167Q103 173 118 173H237L362 349H160Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="634" d="M487 300Q506 289 512 280T516 262Q514 251 507 245T498 240L173 393Q153 405 155 423Q155 436 161 446T186 465L577 607Q579 608 585 602T593 584Q594 576 588 568T561 549L208 424L487 300ZM113 128Q90 128 90 140Q90 146 92 154T99 167Q103 173 118 173H482Q505 173 505 161Q505 155 502 147T495 134Q490 128 476 128H113Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="634" d="M223 548Q203 556 198 567T194 586Q196 597 202 603T212 608L537 455Q548 451 551 443T555 425Q555 413 549 402T524 383L133 241Q131 240 125 246T117 264Q117 272 123 281T149 299L502 424L223 548ZM113 128Q90 128 90 140Q90 146 92 154T99 167Q103 173 118 173H482Q505 173 505 161Q505 155 502 147T495 134Q490 128 476 128H113Z" />
+<glyph unicode="&#x25CA;" glyph-name="lozenge" horiz-adv-x="520" d="M30 388L226 776H294L490 388L294 0H226L30 388ZM370 388L260 616L150 388L260 159L370 388Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="541" d="M170 430H73V433Q73 434 75 442L80 468Q80 470 80 470T81 472V473H179L192 531Q206 600 241 642T339 685Q368 685 387 678T419 658Q431 646 431 631Q431 618 423 611T412 606Q403 622 383 632T336 643Q293 643 271 611T237 524L225 473H351Q350 472 350 470Q350 468 350 466T349 461L343 436Q342 434 342 431V430H217L126 0H79L170 430ZM433 -7Q396 -7 379 17T372 94L453 473H501L420 95Q412 54 424 41T468 27Q468 27 468 22T465 10T455 -1T433 -7ZM512 601Q477 601 477 627Q477 643 486 655T513 667Q547 667 547 641Q547 625 538 613T512 601Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="541" d="M170 430H73V433Q73 434 75 442L80 468Q80 470 80 470T81 472V473H179L192 531Q206 600 241 642T339 685Q368 685 387 678T419 658Q431 646 431 631Q431 618 423 611T412 606Q403 622 383 632T336 643Q293 643 271 611T237 524L225 473H351Q350 472 350 470Q350 468 350 466T349 461L343 436Q342 434 342 431V430H217L126 0H79L170 430ZM433 -7Q396 -7 379 17T372 94L496 674H544L420 95Q412 54 424 41T468 27Q468 27 468 22T465 10T455 -1T433 -7Z" />
+
+<hkern u1="&#x141;" u2="&#xdd;" k="24" />
+<hkern u1="&#x141;" u2="@" k="15" />
+<hkern u1="&#x141;" u2="A" k="-6" />
+<hkern u1="&#x141;" u2="C" k="15" />
+<hkern u1="&#x141;" u2="G" k="15" />
+<hkern u1="&#x141;" u2="O" k="15" />
+<hkern u1="&#x141;" u2="Q" k="15" />
+<hkern u1="&#x141;" u2="T" k="39" />
+<hkern u1="&#x141;" u2="U" k="5" />
+<hkern u1="&#x141;" u2="V" k="27" />
+<hkern u1="&#x141;" u2="W" k="21" />
+<hkern u1="&#x141;" u2="Y" k="24" />
+<hkern u1="&#x141;" u2="\" k="15" />
+<hkern u1="&#x141;" u2="a" k="6" />
+<hkern u1="&#x141;" u2="c" k="6" />
+<hkern u1="&#x141;" u2="d" k="6" />
+<hkern u1="&#x141;" u2="e" k="6" />
+<hkern u1="&#x141;" u2="o" k="6" />
+<hkern u1="&#x141;" u2="q" k="6" />
+<hkern u1="&#x141;" u2="v" k="12" />
+<hkern u1="&#x141;" u2="w" k="3" />
+<hkern u1="&#x141;" u2="y" k="12" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-6" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-6" />
+<hkern u1="&#x141;" u2="&#xd6;" k="15" />
+<hkern u1="&#x141;" u2="&#xdc;" k="5" />
+<hkern u1="&#x141;" u2="&#xe7;" k="6" />
+<hkern u1="&#x141;" u2="&#xae;" k="15" />
+<hkern u1="&#x141;" u2="&#xa9;" k="15" />
+<hkern u1="&#x141;" u2="&#x2122;" k="21" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-6" />
+<hkern u1="&#x141;" u2="&#xd8;" k="15" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="15" />
+<hkern u1="&#x141;" u2="&#xe6;" k="6" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-6" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-6" />
+<hkern u1="&#x141;" u2="&#xd5;" k="15" />
+<hkern u1="&#x141;" u2="&#x152;" k="15" />
+<hkern u1="&#x141;" u2="&#x153;" k="6" />
+<hkern u1="&#x141;" u2="&#x201c;" k="30" />
+<hkern u1="&#x141;" u2="&#x201d;" k="18" />
+<hkern u1="&#x141;" u2="&#x2018;" k="30" />
+<hkern u1="&#x141;" u2="&#x2019;" k="18" />
+<hkern u1="&#x141;" u2="&#x178;" k="24" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-6" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-6" />
+<hkern u1="&#x141;" u2="&#xd3;" k="15" />
+<hkern u1="&#x141;" u2="&#xd4;" k="15" />
+<hkern u1="&#x141;" u2="&#xd2;" k="15" />
+<hkern u1="&#x141;" u2="&#xda;" k="5" />
+<hkern u1="&#x141;" u2="&#xdb;" k="5" />
+<hkern u1="&#x141;" u2="&#xd9;" k="5" />
+<hkern u1="&#x160;" u2="&#xdd;" k="3" />
+<hkern u1="&#x160;" u2="A" k="-12" />
+<hkern u1="&#x160;" u2="V" k="3" />
+<hkern u1="&#x160;" u2="W" k="6" />
+<hkern u1="&#x160;" u2="X" k="-2" />
+<hkern u1="&#x160;" u2="Y" k="3" />
+<hkern u1="&#x160;" u2="v" k="-3" />
+<hkern u1="&#x160;" u2="y" k="-3" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-12" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-12" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-12" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-12" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-12" />
+<hkern u1="&#x160;" u2="&#x178;" k="3" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-12" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-12" />
+<hkern u1="&#xde;" u2="&#xdd;" k="11" />
+<hkern u1="&#xde;" u2="&#x17d;" k="14" />
+<hkern u1="&#xde;" u2="&#x2c;" k="12" />
+<hkern u1="&#xde;" u2="." k="12" />
+<hkern u1="&#xde;" u2="/" k="15" />
+<hkern u1="&#xde;" u2="?" k="6" />
+<hkern u1="&#xde;" u2="J" k="14" />
+<hkern u1="&#xde;" u2="T" k="15" />
+<hkern u1="&#xde;" u2="V" k="12" />
+<hkern u1="&#xde;" u2="W" k="12" />
+<hkern u1="&#xde;" u2="X" k="8" />
+<hkern u1="&#xde;" u2="Y" k="11" />
+<hkern u1="&#xde;" u2="Z" k="14" />
+<hkern u1="&#xde;" u2="a" k="2" />
+<hkern u1="&#xde;" u2="b" k="3" />
+<hkern u1="&#xde;" u2="c" k="2" />
+<hkern u1="&#xde;" u2="d" k="2" />
+<hkern u1="&#xde;" u2="e" k="2" />
+<hkern u1="&#xde;" u2="h" k="3" />
+<hkern u1="&#xde;" u2="k" k="3" />
+<hkern u1="&#xde;" u2="l" k="3" />
+<hkern u1="&#xde;" u2="m" k="2" />
+<hkern u1="&#xde;" u2="n" k="2" />
+<hkern u1="&#xde;" u2="o" k="2" />
+<hkern u1="&#xde;" u2="p" k="2" />
+<hkern u1="&#xde;" u2="q" k="2" />
+<hkern u1="&#xde;" u2="r" k="2" />
+<hkern u1="&#xde;" u2="u" k="2" />
+<hkern u1="&#xde;" u2="v" k="-3" />
+<hkern u1="&#xde;" u2="w" k="-2" />
+<hkern u1="&#xde;" u2="x" k="3" />
+<hkern u1="&#xde;" u2="y" k="-3" />
+<hkern u1="&#xde;" u2="z" k="3" />
+<hkern u1="&#xde;" u2="&#xe7;" k="2" />
+<hkern u1="&#xde;" u2="&#xe6;" k="2" />
+<hkern u1="&#xde;" u2="&#x2026;" k="12" />
+<hkern u1="&#xde;" u2="&#x153;" k="2" />
+<hkern u1="&#xde;" u2="&#x201c;" k="6" />
+<hkern u1="&#xde;" u2="&#x2018;" k="6" />
+<hkern u1="&#xde;" u2="&#x178;" k="11" />
+<hkern u1="&#xde;" u2="&#x201a;" k="12" />
+<hkern u1="&#xde;" u2="&#x201e;" k="12" />
+<hkern u1="&#xde;" u2="&#x131;" k="2" />
+<hkern u1="&#x17d;" u2="&#xf0;" k="9" />
+<hkern u1="&#x17d;" u2="-" k="15" />
+<hkern u1="&#x17d;" u2="A" k="3" />
+<hkern u1="&#x17d;" u2="C" k="14" />
+<hkern u1="&#x17d;" u2="G" k="14" />
+<hkern u1="&#x17d;" u2="J" k="3" />
+<hkern u1="&#x17d;" u2="O" k="14" />
+<hkern u1="&#x17d;" u2="Q" k="14" />
+<hkern u1="&#x17d;" u2="T" k="3" />
+<hkern u1="&#x17d;" u2="V" k="-2" />
+<hkern u1="&#x17d;" u2="a" k="12" />
+<hkern u1="&#x17d;" u2="b" k="3" />
+<hkern u1="&#x17d;" u2="c" k="12" />
+<hkern u1="&#x17d;" u2="d" k="12" />
+<hkern u1="&#x17d;" u2="e" k="12" />
+<hkern u1="&#x17d;" u2="f" k="6" />
+<hkern u1="&#x17d;" u2="g" k="6" />
+<hkern u1="&#x17d;" u2="h" k="3" />
+<hkern u1="&#x17d;" u2="i" k="6" />
+<hkern u1="&#x17d;" u2="j" k="5" />
+<hkern u1="&#x17d;" u2="k" k="3" />
+<hkern u1="&#x17d;" u2="l" k="3" />
+<hkern u1="&#x17d;" u2="m" k="6" />
+<hkern u1="&#x17d;" u2="n" k="6" />
+<hkern u1="&#x17d;" u2="o" k="12" />
+<hkern u1="&#x17d;" u2="p" k="6" />
+<hkern u1="&#x17d;" u2="q" k="12" />
+<hkern u1="&#x17d;" u2="r" k="6" />
+<hkern u1="&#x17d;" u2="u" k="6" />
+<hkern u1="&#x17d;" u2="v" k="9" />
+<hkern u1="&#x17d;" u2="w" k="3" />
+<hkern u1="&#x17d;" u2="y" k="9" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="3" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="3" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="14" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="12" />
+<hkern u1="&#x17d;" u2="&#x2122;" k="-15" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="3" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="14" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="12" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="3" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="3" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="14" />
+<hkern u1="&#x17d;" u2="&#x152;" k="14" />
+<hkern u1="&#x17d;" u2="&#x153;" k="12" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="15" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="15" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="3" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="3" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="14" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="14" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="14" />
+<hkern u1="&#x17d;" u2="&#x131;" k="6" />
+<hkern u1="&amp;" u2="&#xdd;" k="9" />
+<hkern u1="&amp;" u2="A" k="-12" />
+<hkern u1="&amp;" u2="J" k="-6" />
+<hkern u1="&amp;" u2="T" k="92" />
+<hkern u1="&amp;" u2="V" k="6" />
+<hkern u1="&amp;" u2="W" k="6" />
+<hkern u1="&amp;" u2="X" k="-6" />
+<hkern u1="&amp;" u2="Y" k="9" />
+<hkern u1="&amp;" u2="&#xc4;" k="-12" />
+<hkern u1="&amp;" u2="&#xc5;" k="-12" />
+<hkern u1="&amp;" u2="&#xc6;" k="-12" />
+<hkern u1="&amp;" u2="&#xc0;" k="-12" />
+<hkern u1="&amp;" u2="&#xc3;" k="-12" />
+<hkern u1="&amp;" u2="&#x178;" k="9" />
+<hkern u1="&amp;" u2="&#xc2;" k="-12" />
+<hkern u1="&amp;" u2="&#xc1;" k="-12" />
+<hkern u1="(" u2="&#xdd;" k="-15" />
+<hkern u1="(" u2="V" k="-9" />
+<hkern u1="(" u2="W" k="-3" />
+<hkern u1="(" u2="X" k="-12" />
+<hkern u1="(" u2="Y" k="-15" />
+<hkern u1="(" u2="g" k="-6" />
+<hkern u1="(" u2="j" k="-45" />
+<hkern u1="(" u2="&#x178;" k="-15" />
+<hkern u1="*" u2="A" k="18" />
+<hkern u1="*" u2="&#xc4;" k="18" />
+<hkern u1="*" u2="&#xc5;" k="18" />
+<hkern u1="*" u2="&#xc6;" k="18" />
+<hkern u1="*" u2="&#xc0;" k="18" />
+<hkern u1="*" u2="&#xc3;" k="18" />
+<hkern u1="*" u2="&#xc2;" k="18" />
+<hkern u1="*" u2="&#xc1;" k="18" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="15" />
+<hkern u1="&#x2c;" u2="0" k="12" />
+<hkern u1="&#x2c;" u2="1" k="18" />
+<hkern u1="&#x2c;" u2="4" k="15" />
+<hkern u1="&#x2c;" u2="6" k="9" />
+<hkern u1="&#x2c;" u2="8" k="5" />
+<hkern u1="&#x2c;" u2="9" k="3" />
+<hkern u1="&#x2c;" u2="A" k="-9" />
+<hkern u1="&#x2c;" u2="C" k="12" />
+<hkern u1="&#x2c;" u2="G" k="12" />
+<hkern u1="&#x2c;" u2="O" k="12" />
+<hkern u1="&#x2c;" u2="Q" k="12" />
+<hkern u1="&#x2c;" u2="T" k="18" />
+<hkern u1="&#x2c;" u2="U" k="3" />
+<hkern u1="&#x2c;" u2="V" k="18" />
+<hkern u1="&#x2c;" u2="W" k="6" />
+<hkern u1="&#x2c;" u2="Y" k="15" />
+<hkern u1="&#x2c;" u2="a" k="6" />
+<hkern u1="&#x2c;" u2="c" k="6" />
+<hkern u1="&#x2c;" u2="d" k="6" />
+<hkern u1="&#x2c;" u2="e" k="6" />
+<hkern u1="&#x2c;" u2="m" k="6" />
+<hkern u1="&#x2c;" u2="n" k="6" />
+<hkern u1="&#x2c;" u2="o" k="6" />
+<hkern u1="&#x2c;" u2="p" k="6" />
+<hkern u1="&#x2c;" u2="q" k="6" />
+<hkern u1="&#x2c;" u2="r" k="6" />
+<hkern u1="&#x2c;" u2="t" k="12" />
+<hkern u1="&#x2c;" u2="u" k="6" />
+<hkern u1="&#x2c;" u2="v" k="9" />
+<hkern u1="&#x2c;" u2="w" k="3" />
+<hkern u1="&#x2c;" u2="y" k="9" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="12" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="3" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="6" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="12" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="6" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="12" />
+<hkern u1="&#x2c;" u2="&#x152;" k="12" />
+<hkern u1="&#x2c;" u2="&#x153;" k="6" />
+<hkern u1="&#x2c;" u2="&#x178;" k="15" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-9" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="12" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="12" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="12" />
+<hkern u1="&#x2c;" u2="&#xda;" k="3" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="3" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="3" />
+<hkern u1="&#x2c;" u2="&#x131;" k="6" />
+<hkern u1="-" u2="&#xdd;" k="15" />
+<hkern u1="-" u2="&#x17d;" k="3" />
+<hkern u1="-" u2="1" k="6" />
+<hkern u1="-" u2="7" k="9" />
+<hkern u1="-" u2="A" k="-3" />
+<hkern u1="-" u2="T" k="21" />
+<hkern u1="-" u2="V" k="15" />
+<hkern u1="-" u2="W" k="12" />
+<hkern u1="-" u2="X" k="9" />
+<hkern u1="-" u2="Y" k="15" />
+<hkern u1="-" u2="Z" k="3" />
+<hkern u1="-" u2="a" k="3" />
+<hkern u1="-" u2="c" k="3" />
+<hkern u1="-" u2="d" k="3" />
+<hkern u1="-" u2="e" k="3" />
+<hkern u1="-" u2="o" k="3" />
+<hkern u1="-" u2="q" k="3" />
+<hkern u1="-" u2="v" k="3" />
+<hkern u1="-" u2="x" k="9" />
+<hkern u1="-" u2="y" k="3" />
+<hkern u1="-" u2="z" k="9" />
+<hkern u1="-" u2="&#xc4;" k="-3" />
+<hkern u1="-" u2="&#xc5;" k="-3" />
+<hkern u1="-" u2="&#xe7;" k="3" />
+<hkern u1="-" u2="&#xc6;" k="-3" />
+<hkern u1="-" u2="&#xe6;" k="3" />
+<hkern u1="-" u2="&#xc0;" k="-3" />
+<hkern u1="-" u2="&#xc3;" k="-3" />
+<hkern u1="-" u2="&#x153;" k="3" />
+<hkern u1="-" u2="&#x178;" k="15" />
+<hkern u1="-" u2="&#xc2;" k="-3" />
+<hkern u1="-" u2="&#xc1;" k="-3" />
+<hkern u1="." u2="&#xdd;" k="15" />
+<hkern u1="." u2="0" k="12" />
+<hkern u1="." u2="1" k="18" />
+<hkern u1="." u2="4" k="15" />
+<hkern u1="." u2="6" k="9" />
+<hkern u1="." u2="8" k="5" />
+<hkern u1="." u2="9" k="3" />
+<hkern u1="." u2="A" k="-9" />
+<hkern u1="." u2="C" k="12" />
+<hkern u1="." u2="G" k="12" />
+<hkern u1="." u2="O" k="12" />
+<hkern u1="." u2="Q" k="12" />
+<hkern u1="." u2="T" k="18" />
+<hkern u1="." u2="U" k="3" />
+<hkern u1="." u2="V" k="18" />
+<hkern u1="." u2="W" k="6" />
+<hkern u1="." u2="Y" k="15" />
+<hkern u1="." u2="a" k="6" />
+<hkern u1="." u2="c" k="6" />
+<hkern u1="." u2="d" k="6" />
+<hkern u1="." u2="e" k="6" />
+<hkern u1="." u2="m" k="6" />
+<hkern u1="." u2="n" k="6" />
+<hkern u1="." u2="o" k="6" />
+<hkern u1="." u2="p" k="6" />
+<hkern u1="." u2="q" k="6" />
+<hkern u1="." u2="r" k="6" />
+<hkern u1="." u2="t" k="12" />
+<hkern u1="." u2="u" k="6" />
+<hkern u1="." u2="v" k="9" />
+<hkern u1="." u2="w" k="3" />
+<hkern u1="." u2="y" k="9" />
+<hkern u1="." u2="&#xc4;" k="-9" />
+<hkern u1="." u2="&#xc5;" k="-9" />
+<hkern u1="." u2="&#xd6;" k="12" />
+<hkern u1="." u2="&#xdc;" k="3" />
+<hkern u1="." u2="&#xe7;" k="6" />
+<hkern u1="." u2="&#xc6;" k="-9" />
+<hkern u1="." u2="&#xd8;" k="12" />
+<hkern u1="." u2="&#xe6;" k="6" />
+<hkern u1="." u2="&#xc0;" k="-9" />
+<hkern u1="." u2="&#xc3;" k="-9" />
+<hkern u1="." u2="&#xd5;" k="12" />
+<hkern u1="." u2="&#x152;" k="12" />
+<hkern u1="." u2="&#x153;" k="6" />
+<hkern u1="." u2="&#x178;" k="15" />
+<hkern u1="." u2="&#xc2;" k="-9" />
+<hkern u1="." u2="&#xc1;" k="-9" />
+<hkern u1="." u2="&#xd3;" k="12" />
+<hkern u1="." u2="&#xd4;" k="12" />
+<hkern u1="." u2="&#xd2;" k="12" />
+<hkern u1="." u2="&#xda;" k="3" />
+<hkern u1="." u2="&#xdb;" k="3" />
+<hkern u1="." u2="&#xd9;" k="3" />
+<hkern u1="." u2="&#x131;" k="6" />
+<hkern u1="/" u2="1" k="-6" />
+<hkern u1="/" u2="4" k="6" />
+<hkern u1="/" u2="C" k="-3" />
+<hkern u1="/" u2="G" k="-3" />
+<hkern u1="/" u2="O" k="-3" />
+<hkern u1="/" u2="Q" k="-3" />
+<hkern u1="/" u2="a" k="3" />
+<hkern u1="/" u2="c" k="3" />
+<hkern u1="/" u2="d" k="3" />
+<hkern u1="/" u2="e" k="3" />
+<hkern u1="/" u2="g" k="3" />
+<hkern u1="/" u2="o" k="3" />
+<hkern u1="/" u2="q" k="3" />
+<hkern u1="/" u2="&#xd6;" k="-3" />
+<hkern u1="/" u2="&#xe7;" k="3" />
+<hkern u1="/" u2="&#xd8;" k="-3" />
+<hkern u1="/" u2="&#xe6;" k="3" />
+<hkern u1="/" u2="&#xd5;" k="-3" />
+<hkern u1="/" u2="&#x152;" k="-3" />
+<hkern u1="/" u2="&#x153;" k="3" />
+<hkern u1="/" u2="&#xd3;" k="-3" />
+<hkern u1="/" u2="&#xd4;" k="-3" />
+<hkern u1="/" u2="&#xd2;" k="-3" />
+<hkern u1="0" u2="&#x2c;" k="12" />
+<hkern u1="0" u2="." k="12" />
+<hkern u1="0" u2="7" k="6" />
+<hkern u1="0" u2="&#x2026;" k="12" />
+<hkern u1="0" u2="&#x201a;" k="12" />
+<hkern u1="0" u2="&#x201e;" k="12" />
+<hkern u1="2" u2="-" k="3" />
+<hkern u1="2" u2="4" k="9" />
+<hkern u1="2" u2="&#x2013;" k="3" />
+<hkern u1="2" u2="&#x2014;" k="3" />
+<hkern u1="3" u2="&#x2c;" k="6" />
+<hkern u1="3" u2="." k="6" />
+<hkern u1="3" u2="7" k="6" />
+<hkern u1="3" u2="&#x2026;" k="6" />
+<hkern u1="3" u2="&#x201a;" k="6" />
+<hkern u1="3" u2="&#x201e;" k="6" />
+<hkern u1="4" u2="7" k="3" />
+<hkern u1="4" u2="&#xb0;" k="12" />
+<hkern u1="4" u2="&#x2122;" k="15" />
+<hkern u1="5" u2="&#x2c;" k="3" />
+<hkern u1="5" u2="." k="3" />
+<hkern u1="5" u2="7" k="3" />
+<hkern u1="5" u2="&#x2026;" k="3" />
+<hkern u1="5" u2="&#x201a;" k="3" />
+<hkern u1="5" u2="&#x201e;" k="3" />
+<hkern u1="6" u2="&#x2c;" k="3" />
+<hkern u1="6" u2="." k="3" />
+<hkern u1="6" u2="7" k="3" />
+<hkern u1="6" u2="&#x2026;" k="3" />
+<hkern u1="6" u2="&#x201a;" k="3" />
+<hkern u1="6" u2="&#x201e;" k="3" />
+<hkern u1="7" u2="&#x2c;" k="24" />
+<hkern u1="7" u2="-" k="12" />
+<hkern u1="7" u2="." k="24" />
+<hkern u1="7" u2="0" k="3" />
+<hkern u1="7" u2="1" k="-6" />
+<hkern u1="7" u2="3" k="-6" />
+<hkern u1="7" u2="4" k="18" />
+<hkern u1="7" u2="5" k="-5" />
+<hkern u1="7" u2="9" k="-3" />
+<hkern u1="7" u2=":" k="6" />
+<hkern u1="7" u2=";" k="6" />
+<hkern u1="7" u2="&#xa2;" k="12" />
+<hkern u1="7" u2="&#x2122;" k="-12" />
+<hkern u1="7" u2="&#x2026;" k="24" />
+<hkern u1="7" u2="&#x2013;" k="12" />
+<hkern u1="7" u2="&#x2014;" k="12" />
+<hkern u1="7" u2="&#x201a;" k="24" />
+<hkern u1="7" u2="&#x201e;" k="24" />
+<hkern u1="8" u2="&#x2c;" k="5" />
+<hkern u1="8" u2="." k="5" />
+<hkern u1="8" u2="7" k="5" />
+<hkern u1="8" u2="&#x2026;" k="5" />
+<hkern u1="8" u2="&#x201a;" k="5" />
+<hkern u1="8" u2="&#x201e;" k="5" />
+<hkern u1="9" u2="&#x2c;" k="9" />
+<hkern u1="9" u2="." k="9" />
+<hkern u1="9" u2="7" k="14" />
+<hkern u1="9" u2="&#x2026;" k="9" />
+<hkern u1="9" u2="&#x201a;" k="9" />
+<hkern u1="9" u2="&#x201e;" k="9" />
+<hkern u1=":" u2="&#xdd;" k="9" />
+<hkern u1=":" u2="1" k="9" />
+<hkern u1=":" u2="7" k="6" />
+<hkern u1=":" u2="T" k="15" />
+<hkern u1=":" u2="V" k="6" />
+<hkern u1=":" u2="W" k="6" />
+<hkern u1=":" u2="Y" k="9" />
+<hkern u1=":" u2="a" k="3" />
+<hkern u1=":" u2="c" k="3" />
+<hkern u1=":" u2="d" k="3" />
+<hkern u1=":" u2="e" k="3" />
+<hkern u1=":" u2="o" k="3" />
+<hkern u1=":" u2="q" k="3" />
+<hkern u1=":" u2="&#xe7;" k="3" />
+<hkern u1=":" u2="&#xe6;" k="3" />
+<hkern u1=":" u2="&#x153;" k="3" />
+<hkern u1=":" u2="&#x178;" k="9" />
+<hkern u1=";" u2="&#xdd;" k="9" />
+<hkern u1=";" u2="1" k="9" />
+<hkern u1=";" u2="7" k="6" />
+<hkern u1=";" u2="T" k="15" />
+<hkern u1=";" u2="V" k="6" />
+<hkern u1=";" u2="W" k="6" />
+<hkern u1=";" u2="Y" k="9" />
+<hkern u1=";" u2="a" k="3" />
+<hkern u1=";" u2="c" k="3" />
+<hkern u1=";" u2="d" k="3" />
+<hkern u1=";" u2="e" k="3" />
+<hkern u1=";" u2="o" k="3" />
+<hkern u1=";" u2="q" k="3" />
+<hkern u1=";" u2="&#xe7;" k="3" />
+<hkern u1=";" u2="&#xe6;" k="3" />
+<hkern u1=";" u2="&#x153;" k="3" />
+<hkern u1=";" u2="&#x178;" k="9" />
+<hkern u1="&gt;" u2="7" k="18" />
+<hkern u1="@" u2="&#xdd;" k="9" />
+<hkern u1="@" u2="A" k="3" />
+<hkern u1="@" u2="T" k="9" />
+<hkern u1="@" u2="W" k="6" />
+<hkern u1="@" u2="Y" k="9" />
+<hkern u1="@" u2="&#xc4;" k="3" />
+<hkern u1="@" u2="&#xc5;" k="3" />
+<hkern u1="@" u2="&#xc6;" k="3" />
+<hkern u1="@" u2="&#xc0;" k="3" />
+<hkern u1="@" u2="&#xc3;" k="3" />
+<hkern u1="@" u2="&#x178;" k="9" />
+<hkern u1="@" u2="&#xc2;" k="3" />
+<hkern u1="@" u2="&#xc1;" k="3" />
+<hkern u1="A" u2="&#x160;" k="3" />
+<hkern u1="A" u2="&#xdd;" k="18" />
+<hkern u1="A" u2="*" k="18" />
+<hkern u1="A" u2="&#x2c;" k="-9" />
+<hkern u1="A" u2="-" k="-3" />
+<hkern u1="A" u2="." k="-9" />
+<hkern u1="A" u2="@" k="3" />
+<hkern u1="A" u2="A" k="-3" />
+<hkern u1="A" u2="C" k="12" />
+<hkern u1="A" u2="G" k="12" />
+<hkern u1="A" u2="J" k="3" />
+<hkern u1="A" u2="O" k="12" />
+<hkern u1="A" u2="Q" k="12" />
+<hkern u1="A" u2="S" k="3" />
+<hkern u1="A" u2="T" k="134" />
+<hkern u1="A" u2="V" k="21" />
+<hkern u1="A" u2="W" k="21" />
+<hkern u1="A" u2="X" k="-6" />
+<hkern u1="A" u2="Y" k="18" />
+<hkern u1="A" u2="a" k="2" />
+<hkern u1="A" u2="c" k="2" />
+<hkern u1="A" u2="d" k="2" />
+<hkern u1="A" u2="e" k="2" />
+<hkern u1="A" u2="m" k="2" />
+<hkern u1="A" u2="n" k="2" />
+<hkern u1="A" u2="o" k="2" />
+<hkern u1="A" u2="p" k="2" />
+<hkern u1="A" u2="q" k="2" />
+<hkern u1="A" u2="r" k="2" />
+<hkern u1="A" u2="t" k="2" />
+<hkern u1="A" u2="u" k="2" />
+<hkern u1="A" u2="v" k="3" />
+<hkern u1="A" u2="w" k="3" />
+<hkern u1="A" u2="x" k="-6" />
+<hkern u1="A" u2="y" k="3" />
+<hkern u1="A" u2="z" k="-6" />
+<hkern u1="A" u2="&#xc4;" k="-3" />
+<hkern u1="A" u2="&#xc5;" k="-3" />
+<hkern u1="A" u2="&#xd6;" k="12" />
+<hkern u1="A" u2="&#xe7;" k="2" />
+<hkern u1="A" u2="&#xae;" k="3" />
+<hkern u1="A" u2="&#xa9;" k="3" />
+<hkern u1="A" u2="&#x2122;" k="21" />
+<hkern u1="A" u2="&#xc6;" k="-3" />
+<hkern u1="A" u2="&#xd8;" k="12" />
+<hkern u1="A" u2="&#x3c0;" k="3" />
+<hkern u1="A" u2="&#xe6;" k="2" />
+<hkern u1="A" u2="&#x2026;" k="-9" />
+<hkern u1="A" u2="&#xc0;" k="-3" />
+<hkern u1="A" u2="&#xc3;" k="-3" />
+<hkern u1="A" u2="&#xd5;" k="12" />
+<hkern u1="A" u2="&#x152;" k="12" />
+<hkern u1="A" u2="&#x153;" k="2" />
+<hkern u1="A" u2="&#x2013;" k="-3" />
+<hkern u1="A" u2="&#x2014;" k="-3" />
+<hkern u1="A" u2="&#x201c;" k="18" />
+<hkern u1="A" u2="&#x201d;" k="9" />
+<hkern u1="A" u2="&#x2018;" k="18" />
+<hkern u1="A" u2="&#x2019;" k="9" />
+<hkern u1="A" u2="&#x178;" k="18" />
+<hkern u1="A" u2="&#x201a;" k="-9" />
+<hkern u1="A" u2="&#x201e;" k="-9" />
+<hkern u1="A" u2="&#xc2;" k="-3" />
+<hkern u1="A" u2="&#xc1;" k="-3" />
+<hkern u1="A" u2="&#xd3;" k="12" />
+<hkern u1="A" u2="&#xd4;" k="12" />
+<hkern u1="A" u2="&#xd2;" k="12" />
+<hkern u1="A" u2="&#x131;" k="2" />
+<hkern u1="B" u2="&#xdd;" k="6" />
+<hkern u1="B" u2="V" k="2" />
+<hkern u1="B" u2="W" k="6" />
+<hkern u1="B" u2="Y" k="6" />
+<hkern u1="B" u2="&#x178;" k="6" />
+<hkern u1="C" u2="&#x17d;" k="3" />
+<hkern u1="C" u2="T" k="6" />
+<hkern u1="C" u2="V" k="-6" />
+<hkern u1="C" u2="X" k="-3" />
+<hkern u1="C" u2="Z" k="3" />
+<hkern u1="C" u2="a" k="-2" />
+<hkern u1="C" u2="c" k="-2" />
+<hkern u1="C" u2="d" k="-2" />
+<hkern u1="C" u2="e" k="-2" />
+<hkern u1="C" u2="o" k="-2" />
+<hkern u1="C" u2="q" k="-2" />
+<hkern u1="C" u2="t" k="2" />
+<hkern u1="C" u2="v" k="2" />
+<hkern u1="C" u2="w" k="2" />
+<hkern u1="C" u2="y" k="2" />
+<hkern u1="C" u2="z" k="3" />
+<hkern u1="C" u2="&#xe7;" k="-2" />
+<hkern u1="C" u2="&#xe6;" k="-2" />
+<hkern u1="C" u2="&#x153;" k="-2" />
+<hkern u1="D" u2="&#xdd;" k="11" />
+<hkern u1="D" u2="&#x17d;" k="14" />
+<hkern u1="D" u2="&#x2c;" k="12" />
+<hkern u1="D" u2="." k="12" />
+<hkern u1="D" u2="/" k="15" />
+<hkern u1="D" u2="?" k="6" />
+<hkern u1="D" u2="J" k="14" />
+<hkern u1="D" u2="T" k="15" />
+<hkern u1="D" u2="V" k="12" />
+<hkern u1="D" u2="W" k="12" />
+<hkern u1="D" u2="X" k="8" />
+<hkern u1="D" u2="Y" k="11" />
+<hkern u1="D" u2="Z" k="14" />
+<hkern u1="D" u2="a" k="2" />
+<hkern u1="D" u2="b" k="3" />
+<hkern u1="D" u2="c" k="2" />
+<hkern u1="D" u2="d" k="2" />
+<hkern u1="D" u2="e" k="2" />
+<hkern u1="D" u2="h" k="3" />
+<hkern u1="D" u2="k" k="3" />
+<hkern u1="D" u2="l" k="3" />
+<hkern u1="D" u2="m" k="2" />
+<hkern u1="D" u2="n" k="2" />
+<hkern u1="D" u2="o" k="2" />
+<hkern u1="D" u2="p" k="2" />
+<hkern u1="D" u2="q" k="2" />
+<hkern u1="D" u2="r" k="2" />
+<hkern u1="D" u2="u" k="2" />
+<hkern u1="D" u2="v" k="-3" />
+<hkern u1="D" u2="w" k="-2" />
+<hkern u1="D" u2="x" k="3" />
+<hkern u1="D" u2="y" k="-3" />
+<hkern u1="D" u2="z" k="3" />
+<hkern u1="D" u2="&#xe7;" k="2" />
+<hkern u1="D" u2="&#xe6;" k="2" />
+<hkern u1="D" u2="&#x2026;" k="12" />
+<hkern u1="D" u2="&#x153;" k="2" />
+<hkern u1="D" u2="&#x201c;" k="6" />
+<hkern u1="D" u2="&#x2018;" k="6" />
+<hkern u1="D" u2="&#x178;" k="11" />
+<hkern u1="D" u2="&#x201a;" k="12" />
+<hkern u1="D" u2="&#x201e;" k="12" />
+<hkern u1="D" u2="&#x131;" k="2" />
+<hkern u1="E" u2="&#x161;" k="6" />
+<hkern u1="E" u2="@" k="6" />
+<hkern u1="E" u2="T" k="-5" />
+<hkern u1="E" u2="a" k="11" />
+<hkern u1="E" u2="c" k="11" />
+<hkern u1="E" u2="d" k="11" />
+<hkern u1="E" u2="e" k="11" />
+<hkern u1="E" u2="f" k="3" />
+<hkern u1="E" u2="g" k="2" />
+<hkern u1="E" u2="m" k="6" />
+<hkern u1="E" u2="n" k="6" />
+<hkern u1="E" u2="o" k="11" />
+<hkern u1="E" u2="p" k="6" />
+<hkern u1="E" u2="q" k="11" />
+<hkern u1="E" u2="r" k="6" />
+<hkern u1="E" u2="s" k="6" />
+<hkern u1="E" u2="t" k="6" />
+<hkern u1="E" u2="u" k="6" />
+<hkern u1="E" u2="v" k="6" />
+<hkern u1="E" u2="y" k="6" />
+<hkern u1="E" u2="&#xe7;" k="11" />
+<hkern u1="E" u2="&#xae;" k="6" />
+<hkern u1="E" u2="&#xa9;" k="6" />
+<hkern u1="E" u2="&#x3c0;" k="6" />
+<hkern u1="E" u2="&#xe6;" k="11" />
+<hkern u1="E" u2="&#x153;" k="11" />
+<hkern u1="E" u2="&#x131;" k="6" />
+<hkern u1="F" u2="&#x161;" k="12" />
+<hkern u1="F" u2="&#xdd;" k="-8" />
+<hkern u1="F" u2="&amp;" k="12" />
+<hkern u1="F" u2="&#x2c;" k="27" />
+<hkern u1="F" u2="-" k="15" />
+<hkern u1="F" u2="." k="27" />
+<hkern u1="F" u2="/" k="24" />
+<hkern u1="F" u2=":" k="9" />
+<hkern u1="F" u2=";" k="9" />
+<hkern u1="F" u2="@" k="6" />
+<hkern u1="F" u2="A" k="12" />
+<hkern u1="F" u2="J" k="27" />
+<hkern u1="F" u2="T" k="-9" />
+<hkern u1="F" u2="V" k="-6" />
+<hkern u1="F" u2="W" k="-2" />
+<hkern u1="F" u2="X" k="-3" />
+<hkern u1="F" u2="Y" k="-8" />
+<hkern u1="F" u2="a" k="15" />
+<hkern u1="F" u2="b" k="3" />
+<hkern u1="F" u2="c" k="15" />
+<hkern u1="F" u2="d" k="15" />
+<hkern u1="F" u2="e" k="15" />
+<hkern u1="F" u2="f" k="6" />
+<hkern u1="F" u2="g" k="9" />
+<hkern u1="F" u2="h" k="3" />
+<hkern u1="F" u2="i" k="3" />
+<hkern u1="F" u2="j" k="3" />
+<hkern u1="F" u2="k" k="3" />
+<hkern u1="F" u2="l" k="3" />
+<hkern u1="F" u2="m" k="12" />
+<hkern u1="F" u2="n" k="12" />
+<hkern u1="F" u2="o" k="15" />
+<hkern u1="F" u2="p" k="12" />
+<hkern u1="F" u2="q" k="15" />
+<hkern u1="F" u2="r" k="12" />
+<hkern u1="F" u2="s" k="12" />
+<hkern u1="F" u2="t" k="6" />
+<hkern u1="F" u2="u" k="12" />
+<hkern u1="F" u2="v" k="-9" />
+<hkern u1="F" u2="w" k="6" />
+<hkern u1="F" u2="x" k="12" />
+<hkern u1="F" u2="y" k="-9" />
+<hkern u1="F" u2="z" k="12" />
+<hkern u1="F" u2="&#xc4;" k="12" />
+<hkern u1="F" u2="&#xc5;" k="12" />
+<hkern u1="F" u2="&#xe7;" k="15" />
+<hkern u1="F" u2="&#xae;" k="6" />
+<hkern u1="F" u2="&#xa9;" k="6" />
+<hkern u1="F" u2="&#x2122;" k="-15" />
+<hkern u1="F" u2="&#xc6;" k="12" />
+<hkern u1="F" u2="&#x3c0;" k="6" />
+<hkern u1="F" u2="&#xe6;" k="15" />
+<hkern u1="F" u2="&#xab;" k="9" />
+<hkern u1="F" u2="&#xbb;" k="9" />
+<hkern u1="F" u2="&#x2026;" k="27" />
+<hkern u1="F" u2="&#xc0;" k="12" />
+<hkern u1="F" u2="&#xc3;" k="12" />
+<hkern u1="F" u2="&#x153;" k="15" />
+<hkern u1="F" u2="&#x2013;" k="15" />
+<hkern u1="F" u2="&#x2014;" k="15" />
+<hkern u1="F" u2="&#x178;" k="-8" />
+<hkern u1="F" u2="&#x2039;" k="9" />
+<hkern u1="F" u2="&#x203a;" k="9" />
+<hkern u1="F" u2="&#x201a;" k="27" />
+<hkern u1="F" u2="&#x201e;" k="27" />
+<hkern u1="F" u2="&#xc2;" k="12" />
+<hkern u1="F" u2="&#xc1;" k="12" />
+<hkern u1="F" u2="&#x131;" k="12" />
+<hkern u1="G" u2="&#x141;" k="-5" />
+<hkern u1="G" u2="&#xdd;" k="3" />
+<hkern u1="G" u2="&#xde;" k="-5" />
+<hkern u1="G" u2="&#x2c;" k="-6" />
+<hkern u1="G" u2="." k="-6" />
+<hkern u1="G" u2="A" k="-5" />
+<hkern u1="G" u2="B" k="-5" />
+<hkern u1="G" u2="C" k="-3" />
+<hkern u1="G" u2="D" k="-5" />
+<hkern u1="G" u2="E" k="-5" />
+<hkern u1="G" u2="F" k="-5" />
+<hkern u1="G" u2="G" k="-3" />
+<hkern u1="G" u2="H" k="-5" />
+<hkern u1="G" u2="I" k="-5" />
+<hkern u1="G" u2="K" k="-5" />
+<hkern u1="G" u2="L" k="-5" />
+<hkern u1="G" u2="M" k="-5" />
+<hkern u1="G" u2="N" k="-5" />
+<hkern u1="G" u2="O" k="-3" />
+<hkern u1="G" u2="P" k="-5" />
+<hkern u1="G" u2="Q" k="-3" />
+<hkern u1="G" u2="R" k="-5" />
+<hkern u1="G" u2="T" k="8" />
+<hkern u1="G" u2="V" k="3" />
+<hkern u1="G" u2="W" k="2" />
+<hkern u1="G" u2="X" k="-3" />
+<hkern u1="G" u2="Y" k="3" />
+<hkern u1="G" u2="a" k="-5" />
+<hkern u1="G" u2="c" k="-5" />
+<hkern u1="G" u2="d" k="-5" />
+<hkern u1="G" u2="e" k="-5" />
+<hkern u1="G" u2="m" k="-5" />
+<hkern u1="G" u2="n" k="-5" />
+<hkern u1="G" u2="o" k="-5" />
+<hkern u1="G" u2="p" k="-5" />
+<hkern u1="G" u2="q" k="-5" />
+<hkern u1="G" u2="r" k="-5" />
+<hkern u1="G" u2="t" k="3" />
+<hkern u1="G" u2="u" k="-5" />
+<hkern u1="G" u2="v" k="-3" />
+<hkern u1="G" u2="y" k="-3" />
+<hkern u1="G" u2="&#xc4;" k="-5" />
+<hkern u1="G" u2="&#xc5;" k="-5" />
+<hkern u1="G" u2="&#xc9;" k="-5" />
+<hkern u1="G" u2="&#xd1;" k="-5" />
+<hkern u1="G" u2="&#xd6;" k="-3" />
+<hkern u1="G" u2="&#xe7;" k="-5" />
+<hkern u1="G" u2="&#x2122;" k="9" />
+<hkern u1="G" u2="&#xc6;" k="-5" />
+<hkern u1="G" u2="&#xd8;" k="-3" />
+<hkern u1="G" u2="&#xe6;" k="-5" />
+<hkern u1="G" u2="&#x2026;" k="-6" />
+<hkern u1="G" u2="&#xc0;" k="-5" />
+<hkern u1="G" u2="&#xc3;" k="-5" />
+<hkern u1="G" u2="&#xd5;" k="-3" />
+<hkern u1="G" u2="&#x152;" k="-3" />
+<hkern u1="G" u2="&#x153;" k="-5" />
+<hkern u1="G" u2="&#x178;" k="3" />
+<hkern u1="G" u2="&#x201a;" k="-6" />
+<hkern u1="G" u2="&#x201e;" k="-6" />
+<hkern u1="G" u2="&#xc2;" k="-5" />
+<hkern u1="G" u2="&#xca;" k="-5" />
+<hkern u1="G" u2="&#xc1;" k="-5" />
+<hkern u1="G" u2="&#xcb;" k="-5" />
+<hkern u1="G" u2="&#xc8;" k="-5" />
+<hkern u1="G" u2="&#xcd;" k="-5" />
+<hkern u1="G" u2="&#xce;" k="-5" />
+<hkern u1="G" u2="&#xcf;" k="-5" />
+<hkern u1="G" u2="&#xcc;" k="-5" />
+<hkern u1="G" u2="&#xd3;" k="-3" />
+<hkern u1="G" u2="&#xd4;" k="-3" />
+<hkern u1="G" u2="&#xd2;" k="-3" />
+<hkern u1="G" u2="&#x131;" k="-5" />
+<hkern u1="G" u2="&#xd0;" k="-5" />
+<hkern u1="H" u2="/" k="6" />
+<hkern u1="H" u2="v" k="-6" />
+<hkern u1="H" u2="y" k="-6" />
+<hkern u1="I" u2="/" k="6" />
+<hkern u1="I" u2="v" k="-6" />
+<hkern u1="I" u2="y" k="-6" />
+<hkern u1="J" u2="&#x2c;" k="3" />
+<hkern u1="J" u2="." k="3" />
+<hkern u1="J" u2="A" k="-6" />
+<hkern u1="J" u2="J" k="3" />
+<hkern u1="J" u2="v" k="-6" />
+<hkern u1="J" u2="y" k="-6" />
+<hkern u1="J" u2="&#xc4;" k="-6" />
+<hkern u1="J" u2="&#xc5;" k="-6" />
+<hkern u1="J" u2="&#xc6;" k="-6" />
+<hkern u1="J" u2="&#x2026;" k="3" />
+<hkern u1="J" u2="&#xc0;" k="-6" />
+<hkern u1="J" u2="&#xc3;" k="-6" />
+<hkern u1="J" u2="&#x201a;" k="3" />
+<hkern u1="J" u2="&#x201e;" k="3" />
+<hkern u1="J" u2="&#xc2;" k="-6" />
+<hkern u1="J" u2="&#xc1;" k="-6" />
+<hkern u1="K" u2="&#xf0;" k="6" />
+<hkern u1="K" u2="&#x160;" k="2" />
+<hkern u1="K" u2="&#xdd;" k="-5" />
+<hkern u1="K" u2="&#x17d;" k="2" />
+<hkern u1="K" u2="&amp;" k="6" />
+<hkern u1="K" u2="-" k="12" />
+<hkern u1="K" u2="@" k="9" />
+<hkern u1="K" u2="C" k="6" />
+<hkern u1="K" u2="G" k="6" />
+<hkern u1="K" u2="O" k="6" />
+<hkern u1="K" u2="Q" k="6" />
+<hkern u1="K" u2="S" k="2" />
+<hkern u1="K" u2="T" k="-2" />
+<hkern u1="K" u2="U" k="3" />
+<hkern u1="K" u2="V" k="-5" />
+<hkern u1="K" u2="W" k="3" />
+<hkern u1="K" u2="X" k="-3" />
+<hkern u1="K" u2="Y" k="-5" />
+<hkern u1="K" u2="Z" k="2" />
+<hkern u1="K" u2="a" k="9" />
+<hkern u1="K" u2="c" k="9" />
+<hkern u1="K" u2="d" k="9" />
+<hkern u1="K" u2="e" k="9" />
+<hkern u1="K" u2="f" k="2" />
+<hkern u1="K" u2="g" k="3" />
+<hkern u1="K" u2="o" k="9" />
+<hkern u1="K" u2="q" k="9" />
+<hkern u1="K" u2="t" k="3" />
+<hkern u1="K" u2="v" k="6" />
+<hkern u1="K" u2="y" k="6" />
+<hkern u1="K" u2="&#xd6;" k="6" />
+<hkern u1="K" u2="&#xdc;" k="3" />
+<hkern u1="K" u2="&#xe7;" k="9" />
+<hkern u1="K" u2="&#xae;" k="9" />
+<hkern u1="K" u2="&#xa9;" k="9" />
+<hkern u1="K" u2="&#xd8;" k="6" />
+<hkern u1="K" u2="&#x3c0;" k="9" />
+<hkern u1="K" u2="&#xe6;" k="9" />
+<hkern u1="K" u2="&#xd5;" k="6" />
+<hkern u1="K" u2="&#x152;" k="6" />
+<hkern u1="K" u2="&#x153;" k="9" />
+<hkern u1="K" u2="&#x2013;" k="12" />
+<hkern u1="K" u2="&#x2014;" k="12" />
+<hkern u1="K" u2="&#x178;" k="-5" />
+<hkern u1="K" u2="&#xd3;" k="6" />
+<hkern u1="K" u2="&#xd4;" k="6" />
+<hkern u1="K" u2="&#xd2;" k="6" />
+<hkern u1="K" u2="&#xda;" k="3" />
+<hkern u1="K" u2="&#xdb;" k="3" />
+<hkern u1="K" u2="&#xd9;" k="3" />
+<hkern u1="L" u2="&#xdd;" k="24" />
+<hkern u1="L" u2="@" k="15" />
+<hkern u1="L" u2="A" k="-6" />
+<hkern u1="L" u2="C" k="15" />
+<hkern u1="L" u2="G" k="15" />
+<hkern u1="L" u2="O" k="15" />
+<hkern u1="L" u2="Q" k="15" />
+<hkern u1="L" u2="T" k="39" />
+<hkern u1="L" u2="U" k="5" />
+<hkern u1="L" u2="V" k="27" />
+<hkern u1="L" u2="W" k="21" />
+<hkern u1="L" u2="Y" k="24" />
+<hkern u1="L" u2="\" k="15" />
+<hkern u1="L" u2="a" k="6" />
+<hkern u1="L" u2="c" k="6" />
+<hkern u1="L" u2="d" k="6" />
+<hkern u1="L" u2="e" k="6" />
+<hkern u1="L" u2="o" k="6" />
+<hkern u1="L" u2="q" k="6" />
+<hkern u1="L" u2="v" k="12" />
+<hkern u1="L" u2="w" k="3" />
+<hkern u1="L" u2="y" k="12" />
+<hkern u1="L" u2="&#xc4;" k="-6" />
+<hkern u1="L" u2="&#xc5;" k="-6" />
+<hkern u1="L" u2="&#xd6;" k="15" />
+<hkern u1="L" u2="&#xdc;" k="5" />
+<hkern u1="L" u2="&#xe7;" k="6" />
+<hkern u1="L" u2="&#xae;" k="15" />
+<hkern u1="L" u2="&#xa9;" k="15" />
+<hkern u1="L" u2="&#x2122;" k="21" />
+<hkern u1="L" u2="&#xc6;" k="-6" />
+<hkern u1="L" u2="&#xd8;" k="15" />
+<hkern u1="L" u2="&#x3c0;" k="15" />
+<hkern u1="L" u2="&#xe6;" k="6" />
+<hkern u1="L" u2="&#xc0;" k="-6" />
+<hkern u1="L" u2="&#xc3;" k="-6" />
+<hkern u1="L" u2="&#xd5;" k="15" />
+<hkern u1="L" u2="&#x152;" k="15" />
+<hkern u1="L" u2="&#x153;" k="6" />
+<hkern u1="L" u2="&#x201c;" k="30" />
+<hkern u1="L" u2="&#x201d;" k="18" />
+<hkern u1="L" u2="&#x2018;" k="30" />
+<hkern u1="L" u2="&#x2019;" k="18" />
+<hkern u1="L" u2="&#x178;" k="24" />
+<hkern u1="L" u2="&#xc2;" k="-6" />
+<hkern u1="L" u2="&#xc1;" k="-6" />
+<hkern u1="L" u2="&#xd3;" k="15" />
+<hkern u1="L" u2="&#xd4;" k="15" />
+<hkern u1="L" u2="&#xd2;" k="15" />
+<hkern u1="L" u2="&#xda;" k="5" />
+<hkern u1="L" u2="&#xdb;" k="5" />
+<hkern u1="L" u2="&#xd9;" k="5" />
+<hkern u1="M" u2="/" k="6" />
+<hkern u1="M" u2="v" k="-6" />
+<hkern u1="M" u2="y" k="-6" />
+<hkern u1="N" u2="/" k="6" />
+<hkern u1="N" u2="v" k="-6" />
+<hkern u1="N" u2="y" k="-6" />
+<hkern u1="O" u2="&#xdd;" k="11" />
+<hkern u1="O" u2="&#x17d;" k="14" />
+<hkern u1="O" u2="&#x2c;" k="12" />
+<hkern u1="O" u2="." k="12" />
+<hkern u1="O" u2="/" k="15" />
+<hkern u1="O" u2="?" k="6" />
+<hkern u1="O" u2="J" k="14" />
+<hkern u1="O" u2="T" k="15" />
+<hkern u1="O" u2="V" k="12" />
+<hkern u1="O" u2="W" k="12" />
+<hkern u1="O" u2="X" k="8" />
+<hkern u1="O" u2="Y" k="11" />
+<hkern u1="O" u2="Z" k="14" />
+<hkern u1="O" u2="a" k="2" />
+<hkern u1="O" u2="b" k="3" />
+<hkern u1="O" u2="c" k="2" />
+<hkern u1="O" u2="d" k="2" />
+<hkern u1="O" u2="e" k="2" />
+<hkern u1="O" u2="h" k="3" />
+<hkern u1="O" u2="k" k="3" />
+<hkern u1="O" u2="l" k="3" />
+<hkern u1="O" u2="m" k="2" />
+<hkern u1="O" u2="n" k="2" />
+<hkern u1="O" u2="o" k="2" />
+<hkern u1="O" u2="p" k="2" />
+<hkern u1="O" u2="q" k="2" />
+<hkern u1="O" u2="r" k="2" />
+<hkern u1="O" u2="u" k="2" />
+<hkern u1="O" u2="v" k="-3" />
+<hkern u1="O" u2="w" k="-2" />
+<hkern u1="O" u2="x" k="3" />
+<hkern u1="O" u2="y" k="-3" />
+<hkern u1="O" u2="z" k="3" />
+<hkern u1="O" u2="&#xe7;" k="2" />
+<hkern u1="O" u2="&#xe6;" k="2" />
+<hkern u1="O" u2="&#x2026;" k="12" />
+<hkern u1="O" u2="&#x153;" k="2" />
+<hkern u1="O" u2="&#x201c;" k="6" />
+<hkern u1="O" u2="&#x2018;" k="6" />
+<hkern u1="O" u2="&#x178;" k="11" />
+<hkern u1="O" u2="&#x201a;" k="12" />
+<hkern u1="O" u2="&#x201e;" k="12" />
+<hkern u1="O" u2="&#x131;" k="2" />
+<hkern u1="P" u2="&#xdd;" k="2" />
+<hkern u1="P" u2="&#x17d;" k="8" />
+<hkern u1="P" u2="&amp;" k="6" />
+<hkern u1="P" u2="&#x2c;" k="18" />
+<hkern u1="P" u2="." k="18" />
+<hkern u1="P" u2="A" k="11" />
+<hkern u1="P" u2="C" k="-3" />
+<hkern u1="P" u2="G" k="-3" />
+<hkern u1="P" u2="J" k="26" />
+<hkern u1="P" u2="O" k="-3" />
+<hkern u1="P" u2="Q" k="-3" />
+<hkern u1="P" u2="V" k="-2" />
+<hkern u1="P" u2="W" k="5" />
+<hkern u1="P" u2="X" k="-2" />
+<hkern u1="P" u2="Y" k="2" />
+<hkern u1="P" u2="Z" k="8" />
+<hkern u1="P" u2="a" k="3" />
+<hkern u1="P" u2="c" k="3" />
+<hkern u1="P" u2="d" k="3" />
+<hkern u1="P" u2="e" k="3" />
+<hkern u1="P" u2="f" k="-3" />
+<hkern u1="P" u2="o" k="3" />
+<hkern u1="P" u2="q" k="3" />
+<hkern u1="P" u2="t" k="-3" />
+<hkern u1="P" u2="v" k="-17" />
+<hkern u1="P" u2="w" k="-6" />
+<hkern u1="P" u2="x" k="-2" />
+<hkern u1="P" u2="y" k="-17" />
+<hkern u1="P" u2="&#xc4;" k="11" />
+<hkern u1="P" u2="&#xc5;" k="11" />
+<hkern u1="P" u2="&#xd6;" k="-3" />
+<hkern u1="P" u2="&#xe7;" k="3" />
+<hkern u1="P" u2="&#xc6;" k="11" />
+<hkern u1="P" u2="&#xd8;" k="-3" />
+<hkern u1="P" u2="&#xe6;" k="3" />
+<hkern u1="P" u2="&#x2026;" k="18" />
+<hkern u1="P" u2="&#xc0;" k="11" />
+<hkern u1="P" u2="&#xc3;" k="11" />
+<hkern u1="P" u2="&#xd5;" k="-3" />
+<hkern u1="P" u2="&#x152;" k="-3" />
+<hkern u1="P" u2="&#x153;" k="3" />
+<hkern u1="P" u2="&#x178;" k="2" />
+<hkern u1="P" u2="&#x201a;" k="18" />
+<hkern u1="P" u2="&#x201e;" k="18" />
+<hkern u1="P" u2="&#xc2;" k="11" />
+<hkern u1="P" u2="&#xc1;" k="11" />
+<hkern u1="P" u2="&#xd3;" k="-3" />
+<hkern u1="P" u2="&#xd4;" k="-3" />
+<hkern u1="P" u2="&#xd2;" k="-3" />
+<hkern u1="Q" u2="&#xdd;" k="11" />
+<hkern u1="Q" u2="&#x17d;" k="14" />
+<hkern u1="Q" u2="&#x2c;" k="12" />
+<hkern u1="Q" u2="." k="12" />
+<hkern u1="Q" u2="/" k="9" />
+<hkern u1="Q" u2="?" k="6" />
+<hkern u1="Q" u2="J" k="14" />
+<hkern u1="Q" u2="T" k="15" />
+<hkern u1="Q" u2="V" k="12" />
+<hkern u1="Q" u2="W" k="12" />
+<hkern u1="Q" u2="X" k="8" />
+<hkern u1="Q" u2="Y" k="11" />
+<hkern u1="Q" u2="Z" k="14" />
+<hkern u1="Q" u2="a" k="2" />
+<hkern u1="Q" u2="b" k="3" />
+<hkern u1="Q" u2="c" k="2" />
+<hkern u1="Q" u2="d" k="2" />
+<hkern u1="Q" u2="e" k="2" />
+<hkern u1="Q" u2="h" k="3" />
+<hkern u1="Q" u2="k" k="3" />
+<hkern u1="Q" u2="l" k="3" />
+<hkern u1="Q" u2="m" k="2" />
+<hkern u1="Q" u2="n" k="2" />
+<hkern u1="Q" u2="o" k="2" />
+<hkern u1="Q" u2="p" k="2" />
+<hkern u1="Q" u2="q" k="2" />
+<hkern u1="Q" u2="r" k="2" />
+<hkern u1="Q" u2="u" k="2" />
+<hkern u1="Q" u2="v" k="-3" />
+<hkern u1="Q" u2="w" k="-2" />
+<hkern u1="Q" u2="x" k="3" />
+<hkern u1="Q" u2="y" k="-3" />
+<hkern u1="Q" u2="z" k="3" />
+<hkern u1="Q" u2="&#xe7;" k="2" />
+<hkern u1="Q" u2="&#xe6;" k="2" />
+<hkern u1="Q" u2="&#x2026;" k="12" />
+<hkern u1="Q" u2="&#x153;" k="2" />
+<hkern u1="Q" u2="&#x201c;" k="6" />
+<hkern u1="Q" u2="&#x2018;" k="6" />
+<hkern u1="Q" u2="&#x178;" k="11" />
+<hkern u1="Q" u2="&#x201a;" k="12" />
+<hkern u1="Q" u2="&#x201e;" k="12" />
+<hkern u1="Q" u2="&#x131;" k="2" />
+<hkern u1="R" u2="&#xdd;" k="-5" />
+<hkern u1="R" u2="&amp;" k="-3" />
+<hkern u1="R" u2="A" k="-6" />
+<hkern u1="R" u2="J" k="3" />
+<hkern u1="R" u2="T" k="3" />
+<hkern u1="R" u2="V" k="-3" />
+<hkern u1="R" u2="X" k="-3" />
+<hkern u1="R" u2="Y" k="-5" />
+<hkern u1="R" u2="v" k="-9" />
+<hkern u1="R" u2="w" k="-5" />
+<hkern u1="R" u2="y" k="-9" />
+<hkern u1="R" u2="&#xc4;" k="-6" />
+<hkern u1="R" u2="&#xc5;" k="-6" />
+<hkern u1="R" u2="&#xc6;" k="-6" />
+<hkern u1="R" u2="&#xc0;" k="-6" />
+<hkern u1="R" u2="&#xc3;" k="-6" />
+<hkern u1="R" u2="&#x178;" k="-5" />
+<hkern u1="R" u2="&#xc2;" k="-6" />
+<hkern u1="R" u2="&#xc1;" k="-6" />
+<hkern u1="S" u2="&#xdd;" k="3" />
+<hkern u1="S" u2="A" k="-12" />
+<hkern u1="S" u2="V" k="3" />
+<hkern u1="S" u2="W" k="6" />
+<hkern u1="S" u2="X" k="-2" />
+<hkern u1="S" u2="Y" k="3" />
+<hkern u1="S" u2="v" k="-3" />
+<hkern u1="S" u2="y" k="-3" />
+<hkern u1="S" u2="&#xc4;" k="-12" />
+<hkern u1="S" u2="&#xc5;" k="-12" />
+<hkern u1="S" u2="&#xc6;" k="-12" />
+<hkern u1="S" u2="&#xc0;" k="-12" />
+<hkern u1="S" u2="&#xc3;" k="-12" />
+<hkern u1="S" u2="&#x178;" k="3" />
+<hkern u1="S" u2="&#xc2;" k="-12" />
+<hkern u1="S" u2="&#xc1;" k="-12" />
+<hkern u1="T" u2="&#x161;" k="30" />
+<hkern u1="T" u2="&#xdd;" k="-9" />
+<hkern u1="T" u2="&#x17d;" k="3" />
+<hkern u1="T" u2="&amp;" k="52" />
+<hkern u1="T" u2="&#x2c;" k="18" />
+<hkern u1="T" u2="-" k="21" />
+<hkern u1="T" u2="." k="18" />
+<hkern u1="T" u2=":" k="15" />
+<hkern u1="T" u2=";" k="15" />
+<hkern u1="T" u2="@" k="9" />
+<hkern u1="T" u2="A" k="65" />
+<hkern u1="T" u2="C" k="3" />
+<hkern u1="T" u2="G" k="3" />
+<hkern u1="T" u2="J" k="26" />
+<hkern u1="T" u2="O" k="3" />
+<hkern u1="T" u2="Q" k="3" />
+<hkern u1="T" u2="T" k="-3" />
+<hkern u1="T" u2="V" k="-9" />
+<hkern u1="T" u2="W" k="-3" />
+<hkern u1="T" u2="X" k="-3" />
+<hkern u1="T" u2="Y" k="-9" />
+<hkern u1="T" u2="Z" k="3" />
+<hkern u1="T" u2="\" k="-3" />
+<hkern u1="T" u2="a" k="29" />
+<hkern u1="T" u2="c" k="29" />
+<hkern u1="T" u2="d" k="29" />
+<hkern u1="T" u2="e" k="29" />
+<hkern u1="T" u2="f" k="3" />
+<hkern u1="T" u2="g" k="29" />
+<hkern u1="T" u2="m" k="24" />
+<hkern u1="T" u2="n" k="24" />
+<hkern u1="T" u2="o" k="29" />
+<hkern u1="T" u2="p" k="24" />
+<hkern u1="T" u2="q" k="29" />
+<hkern u1="T" u2="r" k="24" />
+<hkern u1="T" u2="s" k="30" />
+<hkern u1="T" u2="t" k="6" />
+<hkern u1="T" u2="u" k="24" />
+<hkern u1="T" u2="v" k="21" />
+<hkern u1="T" u2="w" k="18" />
+<hkern u1="T" u2="x" k="24" />
+<hkern u1="T" u2="y" k="21" />
+<hkern u1="T" u2="z" k="24" />
+<hkern u1="T" u2="&#xc4;" k="15" />
+<hkern u1="T" u2="&#xc5;" k="15" />
+<hkern u1="T" u2="&#xd6;" k="3" />
+<hkern u1="T" u2="&#xe7;" k="29" />
+<hkern u1="T" u2="&#xae;" k="9" />
+<hkern u1="T" u2="&#xa9;" k="9" />
+<hkern u1="T" u2="&#x2122;" k="-15" />
+<hkern u1="T" u2="&#xc6;" k="15" />
+<hkern u1="T" u2="&#xd8;" k="3" />
+<hkern u1="T" u2="&#x3c0;" k="9" />
+<hkern u1="T" u2="&#xe6;" k="29" />
+<hkern u1="T" u2="&#xbf;" k="15" />
+<hkern u1="T" u2="&#xab;" k="33" />
+<hkern u1="T" u2="&#xbb;" k="12" />
+<hkern u1="T" u2="&#x2026;" k="18" />
+<hkern u1="T" u2="&#xc0;" k="15" />
+<hkern u1="T" u2="&#xc3;" k="15" />
+<hkern u1="T" u2="&#xd5;" k="3" />
+<hkern u1="T" u2="&#x152;" k="3" />
+<hkern u1="T" u2="&#x153;" k="29" />
+<hkern u1="T" u2="&#x2013;" k="21" />
+<hkern u1="T" u2="&#x2014;" k="21" />
+<hkern u1="T" u2="&#x178;" k="-9" />
+<hkern u1="T" u2="&#x2039;" k="33" />
+<hkern u1="T" u2="&#x203a;" k="12" />
+<hkern u1="T" u2="&#x201a;" k="18" />
+<hkern u1="T" u2="&#x201e;" k="18" />
+<hkern u1="T" u2="&#xc2;" k="15" />
+<hkern u1="T" u2="&#xc1;" k="15" />
+<hkern u1="T" u2="&#xd3;" k="3" />
+<hkern u1="T" u2="&#xd4;" k="3" />
+<hkern u1="T" u2="&#xd2;" k="3" />
+<hkern u1="T" u2="&#x131;" k="24" />
+<hkern u1="U" u2="&#x2c;" k="3" />
+<hkern u1="U" u2="." k="3" />
+<hkern u1="U" u2="A" k="-6" />
+<hkern u1="U" u2="J" k="3" />
+<hkern u1="U" u2="v" k="-6" />
+<hkern u1="U" u2="y" k="-6" />
+<hkern u1="U" u2="&#xc4;" k="-6" />
+<hkern u1="U" u2="&#xc5;" k="-6" />
+<hkern u1="U" u2="&#xc6;" k="-6" />
+<hkern u1="U" u2="&#x2026;" k="3" />
+<hkern u1="U" u2="&#xc0;" k="-6" />
+<hkern u1="U" u2="&#xc3;" k="-6" />
+<hkern u1="U" u2="&#x201a;" k="3" />
+<hkern u1="U" u2="&#x201e;" k="3" />
+<hkern u1="U" u2="&#xc2;" k="-6" />
+<hkern u1="U" u2="&#xc1;" k="-6" />
+<hkern u1="V" u2="&#x160;" k="-9" />
+<hkern u1="V" u2="&#x161;" k="12" />
+<hkern u1="V" u2="&#xdd;" k="-11" />
+<hkern u1="V" u2="&#x17d;" k="-2" />
+<hkern u1="V" u2="&amp;" k="12" />
+<hkern u1="V" u2=")" k="-9" />
+<hkern u1="V" u2="&#x2c;" k="18" />
+<hkern u1="V" u2="-" k="18" />
+<hkern u1="V" u2="." k="18" />
+<hkern u1="V" u2=":" k="6" />
+<hkern u1="V" u2=";" k="6" />
+<hkern u1="V" u2="A" k="9" />
+<hkern u1="V" u2="J" k="15" />
+<hkern u1="V" u2="S" k="-9" />
+<hkern u1="V" u2="T" k="-9" />
+<hkern u1="V" u2="V" k="-9" />
+<hkern u1="V" u2="W" k="2" />
+<hkern u1="V" u2="X" k="-6" />
+<hkern u1="V" u2="Y" k="-11" />
+<hkern u1="V" u2="Z" k="-2" />
+<hkern u1="V" u2="]" k="-9" />
+<hkern u1="V" u2="a" k="9" />
+<hkern u1="V" u2="b" k="3" />
+<hkern u1="V" u2="c" k="9" />
+<hkern u1="V" u2="d" k="9" />
+<hkern u1="V" u2="e" k="9" />
+<hkern u1="V" u2="f" k="3" />
+<hkern u1="V" u2="g" k="11" />
+<hkern u1="V" u2="h" k="3" />
+<hkern u1="V" u2="k" k="3" />
+<hkern u1="V" u2="l" k="3" />
+<hkern u1="V" u2="m" k="6" />
+<hkern u1="V" u2="n" k="6" />
+<hkern u1="V" u2="o" k="9" />
+<hkern u1="V" u2="p" k="6" />
+<hkern u1="V" u2="q" k="9" />
+<hkern u1="V" u2="r" k="6" />
+<hkern u1="V" u2="s" k="12" />
+<hkern u1="V" u2="t" k="3" />
+<hkern u1="V" u2="u" k="6" />
+<hkern u1="V" u2="v" k="-6" />
+<hkern u1="V" u2="w" k="-3" />
+<hkern u1="V" u2="x" k="8" />
+<hkern u1="V" u2="y" k="-6" />
+<hkern u1="V" u2="}" k="-9" />
+<hkern u1="V" u2="&#xc4;" k="9" />
+<hkern u1="V" u2="&#xc5;" k="9" />
+<hkern u1="V" u2="&#xe7;" k="9" />
+<hkern u1="V" u2="&#x2122;" k="-18" />
+<hkern u1="V" u2="&#xc6;" k="9" />
+<hkern u1="V" u2="&#xe6;" k="9" />
+<hkern u1="V" u2="&#xab;" k="15" />
+<hkern u1="V" u2="&#xbb;" k="6" />
+<hkern u1="V" u2="&#x2026;" k="18" />
+<hkern u1="V" u2="&#xc0;" k="9" />
+<hkern u1="V" u2="&#xc3;" k="9" />
+<hkern u1="V" u2="&#x153;" k="9" />
+<hkern u1="V" u2="&#x2013;" k="18" />
+<hkern u1="V" u2="&#x2014;" k="18" />
+<hkern u1="V" u2="&#x178;" k="-11" />
+<hkern u1="V" u2="&#x2039;" k="15" />
+<hkern u1="V" u2="&#x203a;" k="6" />
+<hkern u1="V" u2="&#x201a;" k="18" />
+<hkern u1="V" u2="&#x201e;" k="18" />
+<hkern u1="V" u2="&#xc2;" k="9" />
+<hkern u1="V" u2="&#xc1;" k="9" />
+<hkern u1="V" u2="&#x131;" k="6" />
+<hkern u1="W" u2="&#x160;" k="-6" />
+<hkern u1="W" u2="&#x161;" k="15" />
+<hkern u1="W" u2="&#xdd;" k="-2" />
+<hkern u1="W" u2="&#x17d;" k="-2" />
+<hkern u1="W" u2="&amp;" k="9" />
+<hkern u1="W" u2=")" k="-3" />
+<hkern u1="W" u2="&#x2c;" k="6" />
+<hkern u1="W" u2="-" k="12" />
+<hkern u1="W" u2="." k="6" />
+<hkern u1="W" u2=":" k="6" />
+<hkern u1="W" u2=";" k="6" />
+<hkern u1="W" u2="@" k="6" />
+<hkern u1="W" u2="A" k="9" />
+<hkern u1="W" u2="C" k="6" />
+<hkern u1="W" u2="G" k="6" />
+<hkern u1="W" u2="J" k="12" />
+<hkern u1="W" u2="O" k="6" />
+<hkern u1="W" u2="Q" k="6" />
+<hkern u1="W" u2="S" k="-6" />
+<hkern u1="W" u2="T" k="-3" />
+<hkern u1="W" u2="V" k="2" />
+<hkern u1="W" u2="W" k="8" />
+<hkern u1="W" u2="X" k="3" />
+<hkern u1="W" u2="Y" k="-2" />
+<hkern u1="W" u2="Z" k="-2" />
+<hkern u1="W" u2="]" k="-3" />
+<hkern u1="W" u2="a" k="9" />
+<hkern u1="W" u2="b" k="3" />
+<hkern u1="W" u2="c" k="9" />
+<hkern u1="W" u2="d" k="9" />
+<hkern u1="W" u2="e" k="9" />
+<hkern u1="W" u2="f" k="5" />
+<hkern u1="W" u2="g" k="15" />
+<hkern u1="W" u2="h" k="3" />
+<hkern u1="W" u2="i" k="6" />
+<hkern u1="W" u2="j" k="6" />
+<hkern u1="W" u2="k" k="3" />
+<hkern u1="W" u2="l" k="3" />
+<hkern u1="W" u2="m" k="8" />
+<hkern u1="W" u2="n" k="8" />
+<hkern u1="W" u2="o" k="9" />
+<hkern u1="W" u2="p" k="8" />
+<hkern u1="W" u2="q" k="9" />
+<hkern u1="W" u2="r" k="8" />
+<hkern u1="W" u2="s" k="15" />
+<hkern u1="W" u2="t" k="9" />
+<hkern u1="W" u2="u" k="8" />
+<hkern u1="W" u2="v" k="-8" />
+<hkern u1="W" u2="w" k="9" />
+<hkern u1="W" u2="x" k="9" />
+<hkern u1="W" u2="y" k="-8" />
+<hkern u1="W" u2="z" k="9" />
+<hkern u1="W" u2="}" k="-3" />
+<hkern u1="W" u2="&#xc4;" k="9" />
+<hkern u1="W" u2="&#xc5;" k="9" />
+<hkern u1="W" u2="&#xd6;" k="6" />
+<hkern u1="W" u2="&#xe7;" k="9" />
+<hkern u1="W" u2="&#xae;" k="6" />
+<hkern u1="W" u2="&#xa9;" k="6" />
+<hkern u1="W" u2="&#x2122;" k="-15" />
+<hkern u1="W" u2="&#xc6;" k="9" />
+<hkern u1="W" u2="&#xd8;" k="6" />
+<hkern u1="W" u2="&#x3c0;" k="6" />
+<hkern u1="W" u2="&#xe6;" k="9" />
+<hkern u1="W" u2="&#xab;" k="12" />
+<hkern u1="W" u2="&#xbb;" k="6" />
+<hkern u1="W" u2="&#x2026;" k="6" />
+<hkern u1="W" u2="&#xc0;" k="9" />
+<hkern u1="W" u2="&#xc3;" k="9" />
+<hkern u1="W" u2="&#xd5;" k="6" />
+<hkern u1="W" u2="&#x152;" k="6" />
+<hkern u1="W" u2="&#x153;" k="9" />
+<hkern u1="W" u2="&#x2013;" k="12" />
+<hkern u1="W" u2="&#x2014;" k="12" />
+<hkern u1="W" u2="&#x178;" k="-2" />
+<hkern u1="W" u2="&#x2039;" k="12" />
+<hkern u1="W" u2="&#x203a;" k="6" />
+<hkern u1="W" u2="&#x201a;" k="6" />
+<hkern u1="W" u2="&#x201e;" k="6" />
+<hkern u1="W" u2="&#xc2;" k="9" />
+<hkern u1="W" u2="&#xc1;" k="9" />
+<hkern u1="W" u2="&#xd3;" k="6" />
+<hkern u1="W" u2="&#xd4;" k="6" />
+<hkern u1="W" u2="&#xd2;" k="6" />
+<hkern u1="W" u2="&#x131;" k="8" />
+<hkern u1="X" u2="&#x160;" k="-2" />
+<hkern u1="X" u2="&#xdd;" k="-6" />
+<hkern u1="X" u2="&amp;" k="6" />
+<hkern u1="X" u2=")" k="-12" />
+<hkern u1="X" u2="-" k="9" />
+<hkern u1="X" u2="A" k="-17" />
+<hkern u1="X" u2="C" k="5" />
+<hkern u1="X" u2="G" k="5" />
+<hkern u1="X" u2="O" k="5" />
+<hkern u1="X" u2="Q" k="5" />
+<hkern u1="X" u2="S" k="-2" />
+<hkern u1="X" u2="T" k="-3" />
+<hkern u1="X" u2="V" k="-6" />
+<hkern u1="X" u2="W" k="3" />
+<hkern u1="X" u2="X" k="-14" />
+<hkern u1="X" u2="Y" k="-6" />
+<hkern u1="X" u2="]" k="-12" />
+<hkern u1="X" u2="a" k="3" />
+<hkern u1="X" u2="c" k="3" />
+<hkern u1="X" u2="d" k="3" />
+<hkern u1="X" u2="e" k="3" />
+<hkern u1="X" u2="f" k="3" />
+<hkern u1="X" u2="m" k="3" />
+<hkern u1="X" u2="n" k="3" />
+<hkern u1="X" u2="o" k="3" />
+<hkern u1="X" u2="p" k="3" />
+<hkern u1="X" u2="q" k="3" />
+<hkern u1="X" u2="r" k="3" />
+<hkern u1="X" u2="t" k="3" />
+<hkern u1="X" u2="u" k="3" />
+<hkern u1="X" u2="v" k="-3" />
+<hkern u1="X" u2="w" k="3" />
+<hkern u1="X" u2="y" k="-3" />
+<hkern u1="X" u2="}" k="-12" />
+<hkern u1="X" u2="&#xc4;" k="-17" />
+<hkern u1="X" u2="&#xc5;" k="-17" />
+<hkern u1="X" u2="&#xd6;" k="5" />
+<hkern u1="X" u2="&#xe7;" k="3" />
+<hkern u1="X" u2="&#x2122;" k="-15" />
+<hkern u1="X" u2="&#xc6;" k="-17" />
+<hkern u1="X" u2="&#xd8;" k="5" />
+<hkern u1="X" u2="&#xe6;" k="3" />
+<hkern u1="X" u2="&#xc0;" k="-17" />
+<hkern u1="X" u2="&#xc3;" k="-17" />
+<hkern u1="X" u2="&#xd5;" k="5" />
+<hkern u1="X" u2="&#x152;" k="5" />
+<hkern u1="X" u2="&#x153;" k="3" />
+<hkern u1="X" u2="&#x2013;" k="9" />
+<hkern u1="X" u2="&#x2014;" k="9" />
+<hkern u1="X" u2="&#x178;" k="-6" />
+<hkern u1="X" u2="&#xc2;" k="-17" />
+<hkern u1="X" u2="&#xc1;" k="-17" />
+<hkern u1="X" u2="&#xd3;" k="5" />
+<hkern u1="X" u2="&#xd4;" k="5" />
+<hkern u1="X" u2="&#xd2;" k="5" />
+<hkern u1="X" u2="&#x131;" k="3" />
+<hkern u1="Y" u2="&#x160;" k="-9" />
+<hkern u1="Y" u2="&#x161;" k="15" />
+<hkern u1="Y" u2="&#xdd;" k="-9" />
+<hkern u1="Y" u2="&amp;" k="9" />
+<hkern u1="Y" u2=")" k="-15" />
+<hkern u1="Y" u2="&#x2c;" k="15" />
+<hkern u1="Y" u2="-" k="15" />
+<hkern u1="Y" u2="." k="15" />
+<hkern u1="Y" u2=":" k="9" />
+<hkern u1="Y" u2=";" k="9" />
+<hkern u1="Y" u2="@" k="9" />
+<hkern u1="Y" u2="A" k="6" />
+<hkern u1="Y" u2="C" k="-2" />
+<hkern u1="Y" u2="G" k="-2" />
+<hkern u1="Y" u2="J" k="21" />
+<hkern u1="Y" u2="O" k="-2" />
+<hkern u1="Y" u2="Q" k="-2" />
+<hkern u1="Y" u2="S" k="-9" />
+<hkern u1="Y" u2="T" k="-9" />
+<hkern u1="Y" u2="V" k="-11" />
+<hkern u1="Y" u2="W" k="-2" />
+<hkern u1="Y" u2="X" k="-6" />
+<hkern u1="Y" u2="Y" k="-9" />
+<hkern u1="Y" u2="]" k="-15" />
+<hkern u1="Y" u2="a" k="17" />
+<hkern u1="Y" u2="c" k="17" />
+<hkern u1="Y" u2="d" k="17" />
+<hkern u1="Y" u2="e" k="17" />
+<hkern u1="Y" u2="f" k="3" />
+<hkern u1="Y" u2="g" k="12" />
+<hkern u1="Y" u2="m" k="12" />
+<hkern u1="Y" u2="n" k="12" />
+<hkern u1="Y" u2="o" k="17" />
+<hkern u1="Y" u2="p" k="12" />
+<hkern u1="Y" u2="q" k="17" />
+<hkern u1="Y" u2="r" k="12" />
+<hkern u1="Y" u2="s" k="15" />
+<hkern u1="Y" u2="t" k="6" />
+<hkern u1="Y" u2="u" k="12" />
+<hkern u1="Y" u2="v" k="6" />
+<hkern u1="Y" u2="w" k="3" />
+<hkern u1="Y" u2="x" k="6" />
+<hkern u1="Y" u2="y" k="6" />
+<hkern u1="Y" u2="z" k="9" />
+<hkern u1="Y" u2="}" k="-15" />
+<hkern u1="Y" u2="&#xc4;" k="6" />
+<hkern u1="Y" u2="&#xc5;" k="6" />
+<hkern u1="Y" u2="&#xd6;" k="-2" />
+<hkern u1="Y" u2="&#xe7;" k="17" />
+<hkern u1="Y" u2="&#xae;" k="9" />
+<hkern u1="Y" u2="&#xa9;" k="9" />
+<hkern u1="Y" u2="&#x2122;" k="-18" />
+<hkern u1="Y" u2="&#xc6;" k="6" />
+<hkern u1="Y" u2="&#xd8;" k="-2" />
+<hkern u1="Y" u2="&#x3c0;" k="9" />
+<hkern u1="Y" u2="&#xe6;" k="17" />
+<hkern u1="Y" u2="&#xab;" k="18" />
+<hkern u1="Y" u2="&#xbb;" k="12" />
+<hkern u1="Y" u2="&#x2026;" k="15" />
+<hkern u1="Y" u2="&#xc0;" k="6" />
+<hkern u1="Y" u2="&#xc3;" k="6" />
+<hkern u1="Y" u2="&#xd5;" k="-2" />
+<hkern u1="Y" u2="&#x152;" k="-2" />
+<hkern u1="Y" u2="&#x153;" k="17" />
+<hkern u1="Y" u2="&#x2013;" k="15" />
+<hkern u1="Y" u2="&#x2014;" k="15" />
+<hkern u1="Y" u2="&#x178;" k="-9" />
+<hkern u1="Y" u2="&#x2039;" k="18" />
+<hkern u1="Y" u2="&#x203a;" k="12" />
+<hkern u1="Y" u2="&#x201a;" k="15" />
+<hkern u1="Y" u2="&#x201e;" k="15" />
+<hkern u1="Y" u2="&#xc2;" k="6" />
+<hkern u1="Y" u2="&#xc1;" k="6" />
+<hkern u1="Y" u2="&#xd3;" k="-2" />
+<hkern u1="Y" u2="&#xd4;" k="-2" />
+<hkern u1="Y" u2="&#xd2;" k="-2" />
+<hkern u1="Y" u2="&#x131;" k="12" />
+<hkern u1="Z" u2="&#xf0;" k="9" />
+<hkern u1="Z" u2="-" k="15" />
+<hkern u1="Z" u2="A" k="3" />
+<hkern u1="Z" u2="C" k="14" />
+<hkern u1="Z" u2="G" k="14" />
+<hkern u1="Z" u2="J" k="3" />
+<hkern u1="Z" u2="O" k="14" />
+<hkern u1="Z" u2="Q" k="14" />
+<hkern u1="Z" u2="T" k="3" />
+<hkern u1="Z" u2="V" k="-2" />
+<hkern u1="Z" u2="a" k="12" />
+<hkern u1="Z" u2="b" k="3" />
+<hkern u1="Z" u2="c" k="12" />
+<hkern u1="Z" u2="d" k="12" />
+<hkern u1="Z" u2="e" k="12" />
+<hkern u1="Z" u2="f" k="6" />
+<hkern u1="Z" u2="g" k="6" />
+<hkern u1="Z" u2="h" k="3" />
+<hkern u1="Z" u2="i" k="6" />
+<hkern u1="Z" u2="j" k="5" />
+<hkern u1="Z" u2="k" k="3" />
+<hkern u1="Z" u2="l" k="3" />
+<hkern u1="Z" u2="m" k="6" />
+<hkern u1="Z" u2="n" k="6" />
+<hkern u1="Z" u2="o" k="12" />
+<hkern u1="Z" u2="p" k="6" />
+<hkern u1="Z" u2="q" k="12" />
+<hkern u1="Z" u2="r" k="6" />
+<hkern u1="Z" u2="u" k="6" />
+<hkern u1="Z" u2="v" k="9" />
+<hkern u1="Z" u2="w" k="3" />
+<hkern u1="Z" u2="y" k="9" />
+<hkern u1="Z" u2="&#xc4;" k="3" />
+<hkern u1="Z" u2="&#xc5;" k="3" />
+<hkern u1="Z" u2="&#xd6;" k="14" />
+<hkern u1="Z" u2="&#xe7;" k="12" />
+<hkern u1="Z" u2="&#x2122;" k="-15" />
+<hkern u1="Z" u2="&#xc6;" k="3" />
+<hkern u1="Z" u2="&#xd8;" k="14" />
+<hkern u1="Z" u2="&#xe6;" k="12" />
+<hkern u1="Z" u2="&#xc0;" k="3" />
+<hkern u1="Z" u2="&#xc3;" k="3" />
+<hkern u1="Z" u2="&#xd5;" k="14" />
+<hkern u1="Z" u2="&#x152;" k="14" />
+<hkern u1="Z" u2="&#x153;" k="12" />
+<hkern u1="Z" u2="&#x2013;" k="15" />
+<hkern u1="Z" u2="&#x2014;" k="15" />
+<hkern u1="Z" u2="&#xc2;" k="3" />
+<hkern u1="Z" u2="&#xc1;" k="3" />
+<hkern u1="Z" u2="&#xd3;" k="14" />
+<hkern u1="Z" u2="&#xd4;" k="14" />
+<hkern u1="Z" u2="&#xd2;" k="14" />
+<hkern u1="Z" u2="&#x131;" k="6" />
+<hkern u1="[" u2="&#xdd;" k="-15" />
+<hkern u1="[" u2="V" k="-9" />
+<hkern u1="[" u2="W" k="-3" />
+<hkern u1="[" u2="X" k="-12" />
+<hkern u1="[" u2="Y" k="-15" />
+<hkern u1="[" u2="g" k="-6" />
+<hkern u1="[" u2="j" k="-45" />
+<hkern u1="[" u2="&#x178;" k="-15" />
+<hkern u1="\" u2="&#xdd;" k="18" />
+<hkern u1="\" u2="T" k="27" />
+<hkern u1="\" u2="V" k="21" />
+<hkern u1="\" u2="W" k="15" />
+<hkern u1="\" u2="Y" k="18" />
+<hkern u1="\" u2="a" k="3" />
+<hkern u1="\" u2="c" k="3" />
+<hkern u1="\" u2="d" k="3" />
+<hkern u1="\" u2="e" k="3" />
+<hkern u1="\" u2="j" k="-39" />
+<hkern u1="\" u2="o" k="3" />
+<hkern u1="\" u2="q" k="3" />
+<hkern u1="\" u2="&#xe7;" k="3" />
+<hkern u1="\" u2="&#xe6;" k="3" />
+<hkern u1="\" u2="&#x153;" k="3" />
+<hkern u1="\" u2="&#x178;" k="18" />
+<hkern u1="a" u2="?" k="3" />
+<hkern u1="a" u2="\" k="3" />
+<hkern u1="a" u2="t" k="3" />
+<hkern u1="a" u2="v" k="2" />
+<hkern u1="a" u2="y" k="2" />
+<hkern u1="a" u2="z" k="-2" />
+<hkern u1="a" u2="&#x2122;" k="6" />
+<hkern u1="a" u2="&#x201c;" k="9" />
+<hkern u1="a" u2="&#x2018;" k="9" />
+<hkern u1="b" u2="&#x161;" k="-2" />
+<hkern u1="b" u2="&#x2c;" k="6" />
+<hkern u1="b" u2="." k="6" />
+<hkern u1="b" u2="/" k="3" />
+<hkern u1="b" u2=":" k="3" />
+<hkern u1="b" u2=";" k="3" />
+<hkern u1="b" u2="?" k="6" />
+<hkern u1="b" u2="\" k="3" />
+<hkern u1="b" u2="s" k="-2" />
+<hkern u1="b" u2="t" k="3" />
+<hkern u1="b" u2="v" k="2" />
+<hkern u1="b" u2="w" k="6" />
+<hkern u1="b" u2="x" k="2" />
+<hkern u1="b" u2="y" k="2" />
+<hkern u1="b" u2="z" k="5" />
+<hkern u1="b" u2="&#x2122;" k="9" />
+<hkern u1="b" u2="&#x2026;" k="6" />
+<hkern u1="b" u2="&#x201c;" k="12" />
+<hkern u1="b" u2="&#x2018;" k="12" />
+<hkern u1="b" u2="&#x201a;" k="6" />
+<hkern u1="b" u2="&#x201e;" k="6" />
+<hkern u1="d" u2="v" k="-3" />
+<hkern u1="d" u2="y" k="-3" />
+<hkern u1="e" u2="v" k="2" />
+<hkern u1="e" u2="x" k="5" />
+<hkern u1="e" u2="y" k="2" />
+<hkern u1="f" u2="&#xf0;" k="6" />
+<hkern u1="f" u2="&amp;" k="9" />
+<hkern u1="f" u2=")" k="-21" />
+<hkern u1="f" u2="*" k="-6" />
+<hkern u1="f" u2="&#x2c;" k="24" />
+<hkern u1="f" u2="-" k="9" />
+<hkern u1="f" u2="." k="24" />
+<hkern u1="f" u2="/" k="15" />
+<hkern u1="f" u2=":" k="3" />
+<hkern u1="f" u2=";" k="3" />
+<hkern u1="f" u2="?" k="-12" />
+<hkern u1="f" u2="@" k="-9" />
+<hkern u1="f" u2="\" k="-12" />
+<hkern u1="f" u2="]" k="-21" />
+<hkern u1="f" u2="a" k="3" />
+<hkern u1="f" u2="c" k="3" />
+<hkern u1="f" u2="d" k="3" />
+<hkern u1="f" u2="e" k="3" />
+<hkern u1="f" u2="m" k="2" />
+<hkern u1="f" u2="n" k="2" />
+<hkern u1="f" u2="o" k="3" />
+<hkern u1="f" u2="p" k="2" />
+<hkern u1="f" u2="q" k="3" />
+<hkern u1="f" u2="r" k="2" />
+<hkern u1="f" u2="u" k="2" />
+<hkern u1="f" u2="v" k="-12" />
+<hkern u1="f" u2="w" k="-3" />
+<hkern u1="f" u2="y" k="-12" />
+<hkern u1="f" u2="}" k="-21" />
+<hkern u1="f" u2="&#xe7;" k="3" />
+<hkern u1="f" u2="&#xae;" k="-9" />
+<hkern u1="f" u2="&#xa9;" k="-9" />
+<hkern u1="f" u2="&#x2122;" k="-27" />
+<hkern u1="f" u2="&#x3c0;" k="-9" />
+<hkern u1="f" u2="&#xe6;" k="3" />
+<hkern u1="f" u2="&#xab;" k="15" />
+<hkern u1="f" u2="&#xbb;" k="6" />
+<hkern u1="f" u2="&#x2026;" k="24" />
+<hkern u1="f" u2="&#x153;" k="3" />
+<hkern u1="f" u2="&#x2013;" k="9" />
+<hkern u1="f" u2="&#x2014;" k="9" />
+<hkern u1="f" u2="&#x201c;" k="-9" />
+<hkern u1="f" u2="&#x201d;" k="-15" />
+<hkern u1="f" u2="&#x2018;" k="-9" />
+<hkern u1="f" u2="&#x2019;" k="-15" />
+<hkern u1="f" u2="&#x2039;" k="15" />
+<hkern u1="f" u2="&#x203a;" k="6" />
+<hkern u1="f" u2="&#x201a;" k="24" />
+<hkern u1="f" u2="&#x201e;" k="24" />
+<hkern u1="f" u2="&#x131;" k="2" />
+<hkern u1="g" u2=")" k="-6" />
+<hkern u1="g" u2="/" k="-12" />
+<hkern u1="g" u2=";" k="-6" />
+<hkern u1="g" u2="]" k="-6" />
+<hkern u1="g" u2="g" k="-3" />
+<hkern u1="g" u2="j" k="-15" />
+<hkern u1="g" u2="t" k="-3" />
+<hkern u1="g" u2="}" k="-6" />
+<hkern u1="g" u2="&#x201c;" k="-6" />
+<hkern u1="g" u2="&#x201d;" k="-12" />
+<hkern u1="g" u2="&#x2018;" k="-6" />
+<hkern u1="g" u2="&#x2019;" k="-12" />
+<hkern u1="h" u2="?" k="3" />
+<hkern u1="h" u2="\" k="3" />
+<hkern u1="h" u2="t" k="3" />
+<hkern u1="h" u2="v" k="2" />
+<hkern u1="h" u2="y" k="2" />
+<hkern u1="h" u2="z" k="-2" />
+<hkern u1="h" u2="&#x2122;" k="6" />
+<hkern u1="h" u2="&#x201c;" k="9" />
+<hkern u1="h" u2="&#x2018;" k="9" />
+<hkern u1="i" u2="v" k="-2" />
+<hkern u1="i" u2="w" k="-2" />
+<hkern u1="i" u2="y" k="-2" />
+<hkern u1="j" u2="j" k="-6" />
+<hkern u1="j" u2="v" k="-6" />
+<hkern u1="j" u2="y" k="-6" />
+<hkern u1="k" u2="a" k="3" />
+<hkern u1="k" u2="c" k="3" />
+<hkern u1="k" u2="d" k="3" />
+<hkern u1="k" u2="e" k="3" />
+<hkern u1="k" u2="g" k="6" />
+<hkern u1="k" u2="o" k="3" />
+<hkern u1="k" u2="q" k="3" />
+<hkern u1="k" u2="v" k="-8" />
+<hkern u1="k" u2="x" k="-3" />
+<hkern u1="k" u2="y" k="-8" />
+<hkern u1="k" u2="&#xe7;" k="3" />
+<hkern u1="k" u2="&#xe6;" k="3" />
+<hkern u1="k" u2="&#x153;" k="3" />
+<hkern u1="l" u2="v" k="-3" />
+<hkern u1="l" u2="y" k="-3" />
+<hkern u1="m" u2="?" k="3" />
+<hkern u1="m" u2="\" k="3" />
+<hkern u1="m" u2="t" k="3" />
+<hkern u1="m" u2="v" k="2" />
+<hkern u1="m" u2="y" k="2" />
+<hkern u1="m" u2="z" k="-2" />
+<hkern u1="m" u2="&#x2122;" k="6" />
+<hkern u1="m" u2="&#x201c;" k="9" />
+<hkern u1="m" u2="&#x2018;" k="9" />
+<hkern u1="n" u2="?" k="3" />
+<hkern u1="n" u2="\" k="3" />
+<hkern u1="n" u2="t" k="3" />
+<hkern u1="n" u2="v" k="2" />
+<hkern u1="n" u2="y" k="2" />
+<hkern u1="n" u2="z" k="-2" />
+<hkern u1="n" u2="&#x2122;" k="6" />
+<hkern u1="n" u2="&#x201c;" k="9" />
+<hkern u1="n" u2="&#x2018;" k="9" />
+<hkern u1="o" u2="&#x161;" k="-2" />
+<hkern u1="o" u2="&#x2c;" k="6" />
+<hkern u1="o" u2="." k="6" />
+<hkern u1="o" u2="/" k="3" />
+<hkern u1="o" u2=":" k="3" />
+<hkern u1="o" u2=";" k="3" />
+<hkern u1="o" u2="?" k="6" />
+<hkern u1="o" u2="\" k="3" />
+<hkern u1="o" u2="s" k="-2" />
+<hkern u1="o" u2="t" k="3" />
+<hkern u1="o" u2="v" k="2" />
+<hkern u1="o" u2="w" k="6" />
+<hkern u1="o" u2="x" k="2" />
+<hkern u1="o" u2="y" k="2" />
+<hkern u1="o" u2="z" k="5" />
+<hkern u1="o" u2="&#x2122;" k="9" />
+<hkern u1="o" u2="&#x2026;" k="6" />
+<hkern u1="o" u2="&#x201c;" k="12" />
+<hkern u1="o" u2="&#x2018;" k="12" />
+<hkern u1="o" u2="&#x201a;" k="6" />
+<hkern u1="o" u2="&#x201e;" k="6" />
+<hkern u1="p" u2="&#x161;" k="-2" />
+<hkern u1="p" u2="&#x2c;" k="6" />
+<hkern u1="p" u2="." k="6" />
+<hkern u1="p" u2="/" k="3" />
+<hkern u1="p" u2=":" k="3" />
+<hkern u1="p" u2=";" k="3" />
+<hkern u1="p" u2="?" k="6" />
+<hkern u1="p" u2="\" k="3" />
+<hkern u1="p" u2="s" k="-2" />
+<hkern u1="p" u2="t" k="3" />
+<hkern u1="p" u2="v" k="2" />
+<hkern u1="p" u2="w" k="6" />
+<hkern u1="p" u2="x" k="2" />
+<hkern u1="p" u2="y" k="2" />
+<hkern u1="p" u2="z" k="5" />
+<hkern u1="p" u2="&#x2122;" k="9" />
+<hkern u1="p" u2="&#x2026;" k="6" />
+<hkern u1="p" u2="&#x201c;" k="12" />
+<hkern u1="p" u2="&#x2018;" k="12" />
+<hkern u1="p" u2="&#x201a;" k="6" />
+<hkern u1="p" u2="&#x201e;" k="6" />
+<hkern u1="q" u2="j" k="-9" />
+<hkern u1="r" u2="&#x2c;" k="15" />
+<hkern u1="r" u2="-" k="6" />
+<hkern u1="r" u2="." k="15" />
+<hkern u1="r" u2="/" k="18" />
+<hkern u1="r" u2="?" k="-6" />
+<hkern u1="r" u2="f" k="-5" />
+<hkern u1="r" u2="g" k="3" />
+<hkern u1="r" u2="t" k="-6" />
+<hkern u1="r" u2="v" k="-12" />
+<hkern u1="r" u2="w" k="-9" />
+<hkern u1="r" u2="x" k="-3" />
+<hkern u1="r" u2="y" k="-12" />
+<hkern u1="r" u2="z" k="3" />
+<hkern u1="r" u2="&#x2026;" k="15" />
+<hkern u1="r" u2="&#x2013;" k="6" />
+<hkern u1="r" u2="&#x2014;" k="6" />
+<hkern u1="r" u2="&#x201c;" k="-6" />
+<hkern u1="r" u2="&#x201d;" k="-12" />
+<hkern u1="r" u2="&#x2018;" k="-6" />
+<hkern u1="r" u2="&#x2019;" k="-12" />
+<hkern u1="r" u2="&#x201a;" k="15" />
+<hkern u1="r" u2="&#x201e;" k="15" />
+<hkern u1="s" u2="v" k="-2" />
+<hkern u1="s" u2="y" k="-2" />
+<hkern u1="t" u2="&#x2c;" k="-3" />
+<hkern u1="t" u2="." k="-3" />
+<hkern u1="t" u2="a" k="2" />
+<hkern u1="t" u2="c" k="2" />
+<hkern u1="t" u2="d" k="2" />
+<hkern u1="t" u2="e" k="2" />
+<hkern u1="t" u2="o" k="2" />
+<hkern u1="t" u2="q" k="2" />
+<hkern u1="t" u2="v" k="-8" />
+<hkern u1="t" u2="w" k="-3" />
+<hkern u1="t" u2="x" k="-2" />
+<hkern u1="t" u2="y" k="-8" />
+<hkern u1="t" u2="z" k="-3" />
+<hkern u1="t" u2="&#xe7;" k="2" />
+<hkern u1="t" u2="&#xe6;" k="2" />
+<hkern u1="t" u2="&#x2026;" k="-3" />
+<hkern u1="t" u2="&#x153;" k="2" />
+<hkern u1="t" u2="&#x201c;" k="-3" />
+<hkern u1="t" u2="&#x2018;" k="-3" />
+<hkern u1="t" u2="&#x201a;" k="-3" />
+<hkern u1="t" u2="&#x201e;" k="-3" />
+<hkern u1="u" u2="v" k="-3" />
+<hkern u1="u" u2="y" k="-3" />
+<hkern u1="v" u2="&#x161;" k="-2" />
+<hkern u1="v" u2="&#x2c;" k="15" />
+<hkern u1="v" u2="-" k="3" />
+<hkern u1="v" u2="." k="15" />
+<hkern u1="v" u2="a" k="-2" />
+<hkern u1="v" u2="c" k="-2" />
+<hkern u1="v" u2="d" k="-2" />
+<hkern u1="v" u2="e" k="-2" />
+<hkern u1="v" u2="o" k="-2" />
+<hkern u1="v" u2="q" k="-2" />
+<hkern u1="v" u2="s" k="-2" />
+<hkern u1="v" u2="t" k="-3" />
+<hkern u1="v" u2="v" k="-12" />
+<hkern u1="v" u2="w" k="-6" />
+<hkern u1="v" u2="y" k="-12" />
+<hkern u1="v" u2="z" k="-3" />
+<hkern u1="v" u2="&#xe7;" k="-2" />
+<hkern u1="v" u2="&#xe6;" k="-2" />
+<hkern u1="v" u2="&#x2026;" k="15" />
+<hkern u1="v" u2="&#x153;" k="-2" />
+<hkern u1="v" u2="&#x2013;" k="3" />
+<hkern u1="v" u2="&#x2014;" k="3" />
+<hkern u1="v" u2="&#x201c;" k="-9" />
+<hkern u1="v" u2="&#x201d;" k="-9" />
+<hkern u1="v" u2="&#x2018;" k="-9" />
+<hkern u1="v" u2="&#x2019;" k="-9" />
+<hkern u1="v" u2="&#x201a;" k="15" />
+<hkern u1="v" u2="&#x201e;" k="15" />
+<hkern u1="w" u2="&#x2c;" k="9" />
+<hkern u1="w" u2="." k="9" />
+<hkern u1="w" u2="v" k="-6" />
+<hkern u1="w" u2="y" k="-6" />
+<hkern u1="w" u2="&#x2026;" k="9" />
+<hkern u1="w" u2="&#x201c;" k="-6" />
+<hkern u1="w" u2="&#x201d;" k="-6" />
+<hkern u1="w" u2="&#x2018;" k="-6" />
+<hkern u1="w" u2="&#x2019;" k="-6" />
+<hkern u1="w" u2="&#x201a;" k="9" />
+<hkern u1="w" u2="&#x201e;" k="9" />
+<hkern u1="x" u2="-" k="9" />
+<hkern u1="x" u2="a" k="5" />
+<hkern u1="x" u2="c" k="5" />
+<hkern u1="x" u2="d" k="5" />
+<hkern u1="x" u2="e" k="5" />
+<hkern u1="x" u2="o" k="5" />
+<hkern u1="x" u2="q" k="5" />
+<hkern u1="x" u2="v" k="-9" />
+<hkern u1="x" u2="x" k="-3" />
+<hkern u1="x" u2="y" k="-9" />
+<hkern u1="x" u2="z" k="-8" />
+<hkern u1="x" u2="&#xe7;" k="5" />
+<hkern u1="x" u2="&#xe6;" k="5" />
+<hkern u1="x" u2="&#xab;" k="15" />
+<hkern u1="x" u2="&#x153;" k="5" />
+<hkern u1="x" u2="&#x2013;" k="9" />
+<hkern u1="x" u2="&#x2014;" k="9" />
+<hkern u1="x" u2="&#x201c;" k="-3" />
+<hkern u1="x" u2="&#x201d;" k="-6" />
+<hkern u1="x" u2="&#x2018;" k="-3" />
+<hkern u1="x" u2="&#x2019;" k="-6" />
+<hkern u1="x" u2="&#x2039;" k="15" />
+<hkern u1="y" u2="&#x161;" k="-2" />
+<hkern u1="y" u2="&#x2c;" k="15" />
+<hkern u1="y" u2="-" k="3" />
+<hkern u1="y" u2="." k="15" />
+<hkern u1="y" u2="a" k="-2" />
+<hkern u1="y" u2="c" k="-2" />
+<hkern u1="y" u2="d" k="-2" />
+<hkern u1="y" u2="e" k="-2" />
+<hkern u1="y" u2="o" k="-2" />
+<hkern u1="y" u2="q" k="-2" />
+<hkern u1="y" u2="s" k="-2" />
+<hkern u1="y" u2="t" k="-3" />
+<hkern u1="y" u2="v" k="-12" />
+<hkern u1="y" u2="w" k="-6" />
+<hkern u1="y" u2="y" k="-12" />
+<hkern u1="y" u2="z" k="-3" />
+<hkern u1="y" u2="&#xe7;" k="-2" />
+<hkern u1="y" u2="&#xe6;" k="-2" />
+<hkern u1="y" u2="&#x2026;" k="15" />
+<hkern u1="y" u2="&#x153;" k="-2" />
+<hkern u1="y" u2="&#x2013;" k="3" />
+<hkern u1="y" u2="&#x2014;" k="3" />
+<hkern u1="y" u2="&#x201c;" k="-9" />
+<hkern u1="y" u2="&#x201d;" k="-9" />
+<hkern u1="y" u2="&#x2018;" k="-9" />
+<hkern u1="y" u2="&#x2019;" k="-9" />
+<hkern u1="y" u2="&#x201a;" k="15" />
+<hkern u1="y" u2="&#x201e;" k="15" />
+<hkern u1="z" u2="-" k="9" />
+<hkern u1="z" u2="a" k="6" />
+<hkern u1="z" u2="c" k="6" />
+<hkern u1="z" u2="d" k="6" />
+<hkern u1="z" u2="e" k="6" />
+<hkern u1="z" u2="g" k="3" />
+<hkern u1="z" u2="m" k="5" />
+<hkern u1="z" u2="n" k="5" />
+<hkern u1="z" u2="o" k="6" />
+<hkern u1="z" u2="p" k="5" />
+<hkern u1="z" u2="q" k="6" />
+<hkern u1="z" u2="r" k="5" />
+<hkern u1="z" u2="t" k="-2" />
+<hkern u1="z" u2="u" k="5" />
+<hkern u1="z" u2="v" k="-9" />
+<hkern u1="z" u2="y" k="-9" />
+<hkern u1="z" u2="&#xe7;" k="6" />
+<hkern u1="z" u2="&#xe6;" k="6" />
+<hkern u1="z" u2="&#xab;" k="18" />
+<hkern u1="z" u2="&#x153;" k="6" />
+<hkern u1="z" u2="&#x2013;" k="9" />
+<hkern u1="z" u2="&#x2014;" k="9" />
+<hkern u1="z" u2="&#x2039;" k="18" />
+<hkern u1="z" u2="&#x131;" k="5" />
+<hkern u1="{" u2="&#xdd;" k="-15" />
+<hkern u1="{" u2="V" k="-9" />
+<hkern u1="{" u2="W" k="-3" />
+<hkern u1="{" u2="X" k="-12" />
+<hkern u1="{" u2="Y" k="-15" />
+<hkern u1="{" u2="g" k="-6" />
+<hkern u1="{" u2="j" k="-45" />
+<hkern u1="{" u2="&#x178;" k="-15" />
+<hkern u1="&#xb0;" u2="4" k="20" />
+<hkern u1="&#xa3;" u2="1" k="-3" />
+<hkern u1="&#xa3;" u2="4" k="6" />
+<hkern u1="&#xdf;" u2="&#x161;" k="-2" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="6" />
+<hkern u1="&#xdf;" u2="." k="6" />
+<hkern u1="&#xdf;" u2="/" k="3" />
+<hkern u1="&#xdf;" u2=":" k="3" />
+<hkern u1="&#xdf;" u2=";" k="3" />
+<hkern u1="&#xdf;" u2="?" k="6" />
+<hkern u1="&#xdf;" u2="\" k="3" />
+<hkern u1="&#xdf;" u2="g" k="3" />
+<hkern u1="&#xdf;" u2="s" k="-2" />
+<hkern u1="&#xdf;" u2="t" k="3" />
+<hkern u1="&#xdf;" u2="v" k="2" />
+<hkern u1="&#xdf;" u2="w" k="6" />
+<hkern u1="&#xdf;" u2="x" k="2" />
+<hkern u1="&#xdf;" u2="y" k="2" />
+<hkern u1="&#xdf;" u2="z" k="5" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="9" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="6" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="12" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="12" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="6" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="6" />
+<hkern u1="&#xae;" u2="&#xdd;" k="9" />
+<hkern u1="&#xae;" u2="A" k="3" />
+<hkern u1="&#xae;" u2="T" k="9" />
+<hkern u1="&#xae;" u2="W" k="6" />
+<hkern u1="&#xae;" u2="Y" k="9" />
+<hkern u1="&#xae;" u2="&#xc4;" k="3" />
+<hkern u1="&#xae;" u2="&#xc5;" k="3" />
+<hkern u1="&#xae;" u2="&#xc6;" k="3" />
+<hkern u1="&#xae;" u2="&#xc0;" k="3" />
+<hkern u1="&#xae;" u2="&#xc3;" k="3" />
+<hkern u1="&#xae;" u2="&#x178;" k="9" />
+<hkern u1="&#xae;" u2="&#xc2;" k="3" />
+<hkern u1="&#xae;" u2="&#xc1;" k="3" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="9" />
+<hkern u1="&#xa9;" u2="A" k="3" />
+<hkern u1="&#xa9;" u2="T" k="9" />
+<hkern u1="&#xa9;" u2="W" k="6" />
+<hkern u1="&#xa9;" u2="Y" k="9" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="3" />
+<hkern u1="&#xa9;" u2="&#x178;" k="9" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="3" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="3" />
+<hkern u1="&#xc6;" u2="&#x161;" k="6" />
+<hkern u1="&#xc6;" u2="@" k="6" />
+<hkern u1="&#xc6;" u2="T" k="-5" />
+<hkern u1="&#xc6;" u2="a" k="11" />
+<hkern u1="&#xc6;" u2="c" k="11" />
+<hkern u1="&#xc6;" u2="d" k="11" />
+<hkern u1="&#xc6;" u2="e" k="11" />
+<hkern u1="&#xc6;" u2="f" k="3" />
+<hkern u1="&#xc6;" u2="g" k="2" />
+<hkern u1="&#xc6;" u2="m" k="6" />
+<hkern u1="&#xc6;" u2="n" k="6" />
+<hkern u1="&#xc6;" u2="o" k="11" />
+<hkern u1="&#xc6;" u2="p" k="6" />
+<hkern u1="&#xc6;" u2="q" k="11" />
+<hkern u1="&#xc6;" u2="r" k="6" />
+<hkern u1="&#xc6;" u2="s" k="6" />
+<hkern u1="&#xc6;" u2="t" k="6" />
+<hkern u1="&#xc6;" u2="u" k="6" />
+<hkern u1="&#xc6;" u2="v" k="6" />
+<hkern u1="&#xc6;" u2="y" k="6" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="11" />
+<hkern u1="&#xc6;" u2="&#xae;" k="6" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="6" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="6" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="11" />
+<hkern u1="&#xc6;" u2="&#x153;" k="11" />
+<hkern u1="&#xc6;" u2="&#x131;" k="6" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="14" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="12" />
+<hkern u1="&#xd8;" u2="." k="12" />
+<hkern u1="&#xd8;" u2="/" k="15" />
+<hkern u1="&#xd8;" u2="?" k="6" />
+<hkern u1="&#xd8;" u2="J" k="14" />
+<hkern u1="&#xd8;" u2="T" k="15" />
+<hkern u1="&#xd8;" u2="V" k="12" />
+<hkern u1="&#xd8;" u2="W" k="12" />
+<hkern u1="&#xd8;" u2="X" k="8" />
+<hkern u1="&#xd8;" u2="Y" k="11" />
+<hkern u1="&#xd8;" u2="Z" k="14" />
+<hkern u1="&#xd8;" u2="a" k="2" />
+<hkern u1="&#xd8;" u2="b" k="3" />
+<hkern u1="&#xd8;" u2="c" k="2" />
+<hkern u1="&#xd8;" u2="d" k="2" />
+<hkern u1="&#xd8;" u2="e" k="2" />
+<hkern u1="&#xd8;" u2="h" k="3" />
+<hkern u1="&#xd8;" u2="k" k="3" />
+<hkern u1="&#xd8;" u2="l" k="3" />
+<hkern u1="&#xd8;" u2="m" k="2" />
+<hkern u1="&#xd8;" u2="n" k="2" />
+<hkern u1="&#xd8;" u2="o" k="2" />
+<hkern u1="&#xd8;" u2="p" k="2" />
+<hkern u1="&#xd8;" u2="q" k="2" />
+<hkern u1="&#xd8;" u2="r" k="2" />
+<hkern u1="&#xd8;" u2="u" k="2" />
+<hkern u1="&#xd8;" u2="v" k="-3" />
+<hkern u1="&#xd8;" u2="w" k="-2" />
+<hkern u1="&#xd8;" u2="x" k="3" />
+<hkern u1="&#xd8;" u2="y" k="-3" />
+<hkern u1="&#xd8;" u2="z" k="3" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="2" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="2" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="12" />
+<hkern u1="&#xd8;" u2="&#x153;" k="2" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="6" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="6" />
+<hkern u1="&#xd8;" u2="&#x178;" k="11" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="12" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="12" />
+<hkern u1="&#xd8;" u2="&#x131;" k="2" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="9" />
+<hkern u1="&#x3c0;" u2="A" k="3" />
+<hkern u1="&#x3c0;" u2="T" k="9" />
+<hkern u1="&#x3c0;" u2="W" k="6" />
+<hkern u1="&#x3c0;" u2="Y" k="9" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="3" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="9" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="3" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="3" />
+<hkern u1="&#xe6;" u2="v" k="2" />
+<hkern u1="&#xe6;" u2="x" k="5" />
+<hkern u1="&#xe6;" u2="y" k="2" />
+<hkern u1="&#xf8;" u2="&#x161;" k="-2" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="6" />
+<hkern u1="&#xf8;" u2="." k="6" />
+<hkern u1="&#xf8;" u2="/" k="3" />
+<hkern u1="&#xf8;" u2=":" k="3" />
+<hkern u1="&#xf8;" u2=";" k="3" />
+<hkern u1="&#xf8;" u2="?" k="6" />
+<hkern u1="&#xf8;" u2="\" k="3" />
+<hkern u1="&#xf8;" u2="s" k="-2" />
+<hkern u1="&#xf8;" u2="t" k="3" />
+<hkern u1="&#xf8;" u2="v" k="2" />
+<hkern u1="&#xf8;" u2="w" k="6" />
+<hkern u1="&#xf8;" u2="x" k="2" />
+<hkern u1="&#xf8;" u2="y" k="2" />
+<hkern u1="&#xf8;" u2="z" k="5" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="9" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="6" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="12" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="12" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="6" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="6" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="12" />
+<hkern u1="&#xbf;" u2="1" k="12" />
+<hkern u1="&#xbf;" u2="3" k="-3" />
+<hkern u1="&#xbf;" u2="7" k="9" />
+<hkern u1="&#xbf;" u2="C" k="6" />
+<hkern u1="&#xbf;" u2="G" k="6" />
+<hkern u1="&#xbf;" u2="O" k="6" />
+<hkern u1="&#xbf;" u2="Q" k="6" />
+<hkern u1="&#xbf;" u2="T" k="18" />
+<hkern u1="&#xbf;" u2="V" k="15" />
+<hkern u1="&#xbf;" u2="W" k="9" />
+<hkern u1="&#xbf;" u2="Y" k="12" />
+<hkern u1="&#xbf;" u2="v" k="9" />
+<hkern u1="&#xbf;" u2="w" k="6" />
+<hkern u1="&#xbf;" u2="x" k="3" />
+<hkern u1="&#xbf;" u2="y" k="9" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="6" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="6" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="6" />
+<hkern u1="&#xbf;" u2="&#x152;" k="6" />
+<hkern u1="&#xbf;" u2="&#x178;" k="12" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="6" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="6" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="6" />
+<hkern u1="&#x192;" u2="1" k="-9" />
+<hkern u1="&#xab;" u2="&#xdd;" k="12" />
+<hkern u1="&#xab;" u2="T" k="12" />
+<hkern u1="&#xab;" u2="V" k="6" />
+<hkern u1="&#xab;" u2="W" k="6" />
+<hkern u1="&#xab;" u2="Y" k="12" />
+<hkern u1="&#xab;" u2="&#x178;" k="12" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="15" />
+<hkern u1="&#xbb;" u2="T" k="33" />
+<hkern u1="&#xbb;" u2="V" k="15" />
+<hkern u1="&#xbb;" u2="W" k="12" />
+<hkern u1="&#xbb;" u2="Y" k="15" />
+<hkern u1="&#xbb;" u2="x" k="15" />
+<hkern u1="&#xbb;" u2="z" k="15" />
+<hkern u1="&#xbb;" u2="&#x178;" k="15" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="15" />
+<hkern u1="&#x2026;" u2="0" k="12" />
+<hkern u1="&#x2026;" u2="1" k="18" />
+<hkern u1="&#x2026;" u2="4" k="15" />
+<hkern u1="&#x2026;" u2="6" k="9" />
+<hkern u1="&#x2026;" u2="8" k="5" />
+<hkern u1="&#x2026;" u2="9" k="3" />
+<hkern u1="&#x2026;" u2="A" k="-9" />
+<hkern u1="&#x2026;" u2="C" k="12" />
+<hkern u1="&#x2026;" u2="G" k="12" />
+<hkern u1="&#x2026;" u2="O" k="12" />
+<hkern u1="&#x2026;" u2="Q" k="12" />
+<hkern u1="&#x2026;" u2="T" k="18" />
+<hkern u1="&#x2026;" u2="U" k="3" />
+<hkern u1="&#x2026;" u2="V" k="18" />
+<hkern u1="&#x2026;" u2="W" k="6" />
+<hkern u1="&#x2026;" u2="Y" k="15" />
+<hkern u1="&#x2026;" u2="a" k="6" />
+<hkern u1="&#x2026;" u2="c" k="6" />
+<hkern u1="&#x2026;" u2="d" k="6" />
+<hkern u1="&#x2026;" u2="e" k="6" />
+<hkern u1="&#x2026;" u2="m" k="6" />
+<hkern u1="&#x2026;" u2="n" k="6" />
+<hkern u1="&#x2026;" u2="o" k="6" />
+<hkern u1="&#x2026;" u2="p" k="6" />
+<hkern u1="&#x2026;" u2="q" k="6" />
+<hkern u1="&#x2026;" u2="r" k="6" />
+<hkern u1="&#x2026;" u2="t" k="12" />
+<hkern u1="&#x2026;" u2="u" k="6" />
+<hkern u1="&#x2026;" u2="v" k="9" />
+<hkern u1="&#x2026;" u2="w" k="3" />
+<hkern u1="&#x2026;" u2="y" k="9" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="12" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="3" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="6" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="12" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="6" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="12" />
+<hkern u1="&#x2026;" u2="&#x152;" k="12" />
+<hkern u1="&#x2026;" u2="&#x153;" k="6" />
+<hkern u1="&#x2026;" u2="&#x178;" k="15" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-9" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="12" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="12" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="12" />
+<hkern u1="&#x2026;" u2="&#xda;" k="3" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="3" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="3" />
+<hkern u1="&#x2026;" u2="&#x131;" k="6" />
+<hkern u1="&#x152;" u2="&#x161;" k="6" />
+<hkern u1="&#x152;" u2="@" k="6" />
+<hkern u1="&#x152;" u2="T" k="-5" />
+<hkern u1="&#x152;" u2="a" k="11" />
+<hkern u1="&#x152;" u2="c" k="11" />
+<hkern u1="&#x152;" u2="d" k="11" />
+<hkern u1="&#x152;" u2="e" k="11" />
+<hkern u1="&#x152;" u2="f" k="3" />
+<hkern u1="&#x152;" u2="g" k="2" />
+<hkern u1="&#x152;" u2="m" k="6" />
+<hkern u1="&#x152;" u2="n" k="6" />
+<hkern u1="&#x152;" u2="o" k="11" />
+<hkern u1="&#x152;" u2="p" k="6" />
+<hkern u1="&#x152;" u2="q" k="11" />
+<hkern u1="&#x152;" u2="r" k="6" />
+<hkern u1="&#x152;" u2="s" k="6" />
+<hkern u1="&#x152;" u2="t" k="6" />
+<hkern u1="&#x152;" u2="u" k="6" />
+<hkern u1="&#x152;" u2="v" k="6" />
+<hkern u1="&#x152;" u2="y" k="6" />
+<hkern u1="&#x152;" u2="&#xe7;" k="11" />
+<hkern u1="&#x152;" u2="&#xae;" k="6" />
+<hkern u1="&#x152;" u2="&#xa9;" k="6" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="6" />
+<hkern u1="&#x152;" u2="&#xe6;" k="11" />
+<hkern u1="&#x152;" u2="&#x153;" k="11" />
+<hkern u1="&#x152;" u2="&#x131;" k="6" />
+<hkern u1="&#x153;" u2="v" k="2" />
+<hkern u1="&#x153;" u2="x" k="5" />
+<hkern u1="&#x153;" u2="y" k="2" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="15" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="3" />
+<hkern u1="&#x2013;" u2="1" k="6" />
+<hkern u1="&#x2013;" u2="7" k="9" />
+<hkern u1="&#x2013;" u2="A" k="-3" />
+<hkern u1="&#x2013;" u2="T" k="21" />
+<hkern u1="&#x2013;" u2="V" k="15" />
+<hkern u1="&#x2013;" u2="W" k="12" />
+<hkern u1="&#x2013;" u2="X" k="9" />
+<hkern u1="&#x2013;" u2="Y" k="15" />
+<hkern u1="&#x2013;" u2="Z" k="3" />
+<hkern u1="&#x2013;" u2="a" k="3" />
+<hkern u1="&#x2013;" u2="c" k="3" />
+<hkern u1="&#x2013;" u2="d" k="3" />
+<hkern u1="&#x2013;" u2="e" k="3" />
+<hkern u1="&#x2013;" u2="o" k="3" />
+<hkern u1="&#x2013;" u2="q" k="3" />
+<hkern u1="&#x2013;" u2="v" k="3" />
+<hkern u1="&#x2013;" u2="x" k="9" />
+<hkern u1="&#x2013;" u2="y" k="3" />
+<hkern u1="&#x2013;" u2="z" k="9" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="3" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="3" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-3" />
+<hkern u1="&#x2013;" u2="&#x153;" k="3" />
+<hkern u1="&#x2013;" u2="&#x178;" k="15" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-3" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="15" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="3" />
+<hkern u1="&#x2014;" u2="1" k="6" />
+<hkern u1="&#x2014;" u2="7" k="9" />
+<hkern u1="&#x2014;" u2="A" k="-3" />
+<hkern u1="&#x2014;" u2="T" k="21" />
+<hkern u1="&#x2014;" u2="V" k="15" />
+<hkern u1="&#x2014;" u2="W" k="12" />
+<hkern u1="&#x2014;" u2="X" k="9" />
+<hkern u1="&#x2014;" u2="Y" k="15" />
+<hkern u1="&#x2014;" u2="Z" k="3" />
+<hkern u1="&#x2014;" u2="a" k="3" />
+<hkern u1="&#x2014;" u2="c" k="3" />
+<hkern u1="&#x2014;" u2="d" k="3" />
+<hkern u1="&#x2014;" u2="e" k="3" />
+<hkern u1="&#x2014;" u2="o" k="3" />
+<hkern u1="&#x2014;" u2="q" k="3" />
+<hkern u1="&#x2014;" u2="v" k="3" />
+<hkern u1="&#x2014;" u2="x" k="9" />
+<hkern u1="&#x2014;" u2="y" k="3" />
+<hkern u1="&#x2014;" u2="z" k="9" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="3" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="3" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-3" />
+<hkern u1="&#x2014;" u2="&#x153;" k="3" />
+<hkern u1="&#x2014;" u2="&#x178;" k="15" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-3" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-3" />
+<hkern u1="&#x201c;" u2="&#x161;" k="9" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-9" />
+<hkern u1="&#x201c;" u2="A" k="12" />
+<hkern u1="&#x201c;" u2="J" k="33" />
+<hkern u1="&#x201c;" u2="T" k="-3" />
+<hkern u1="&#x201c;" u2="V" k="-6" />
+<hkern u1="&#x201c;" u2="W" k="-6" />
+<hkern u1="&#x201c;" u2="X" k="-6" />
+<hkern u1="&#x201c;" u2="Y" k="-9" />
+<hkern u1="&#x201c;" u2="a" k="9" />
+<hkern u1="&#x201c;" u2="c" k="9" />
+<hkern u1="&#x201c;" u2="d" k="9" />
+<hkern u1="&#x201c;" u2="e" k="9" />
+<hkern u1="&#x201c;" u2="g" k="12" />
+<hkern u1="&#x201c;" u2="m" k="3" />
+<hkern u1="&#x201c;" u2="n" k="3" />
+<hkern u1="&#x201c;" u2="o" k="9" />
+<hkern u1="&#x201c;" u2="p" k="3" />
+<hkern u1="&#x201c;" u2="q" k="9" />
+<hkern u1="&#x201c;" u2="r" k="3" />
+<hkern u1="&#x201c;" u2="s" k="9" />
+<hkern u1="&#x201c;" u2="u" k="3" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="12" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="12" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="9" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="12" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="9" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="12" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="12" />
+<hkern u1="&#x201c;" u2="&#x153;" k="9" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-9" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="12" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="12" />
+<hkern u1="&#x201c;" u2="&#x131;" k="3" />
+<hkern u1="&#x201d;" u2="&#x160;" k="3" />
+<hkern u1="&#x201d;" u2="C" k="12" />
+<hkern u1="&#x201d;" u2="G" k="12" />
+<hkern u1="&#x201d;" u2="J" k="36" />
+<hkern u1="&#x201d;" u2="O" k="12" />
+<hkern u1="&#x201d;" u2="Q" k="12" />
+<hkern u1="&#x201d;" u2="S" k="3" />
+<hkern u1="&#x201d;" u2="a" k="15" />
+<hkern u1="&#x201d;" u2="c" k="15" />
+<hkern u1="&#x201d;" u2="d" k="15" />
+<hkern u1="&#x201d;" u2="e" k="15" />
+<hkern u1="&#x201d;" u2="o" k="15" />
+<hkern u1="&#x201d;" u2="q" k="15" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="12" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="15" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="12" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="15" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="12" />
+<hkern u1="&#x201d;" u2="&#x152;" k="12" />
+<hkern u1="&#x201d;" u2="&#x153;" k="15" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="12" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="12" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="12" />
+<hkern u1="&#x2018;" u2="&#x161;" k="9" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-9" />
+<hkern u1="&#x2018;" u2="A" k="12" />
+<hkern u1="&#x2018;" u2="J" k="33" />
+<hkern u1="&#x2018;" u2="T" k="-3" />
+<hkern u1="&#x2018;" u2="V" k="-6" />
+<hkern u1="&#x2018;" u2="W" k="-6" />
+<hkern u1="&#x2018;" u2="X" k="-6" />
+<hkern u1="&#x2018;" u2="Y" k="-9" />
+<hkern u1="&#x2018;" u2="a" k="9" />
+<hkern u1="&#x2018;" u2="c" k="9" />
+<hkern u1="&#x2018;" u2="d" k="9" />
+<hkern u1="&#x2018;" u2="e" k="9" />
+<hkern u1="&#x2018;" u2="g" k="12" />
+<hkern u1="&#x2018;" u2="m" k="3" />
+<hkern u1="&#x2018;" u2="n" k="3" />
+<hkern u1="&#x2018;" u2="o" k="9" />
+<hkern u1="&#x2018;" u2="p" k="3" />
+<hkern u1="&#x2018;" u2="q" k="9" />
+<hkern u1="&#x2018;" u2="r" k="3" />
+<hkern u1="&#x2018;" u2="s" k="9" />
+<hkern u1="&#x2018;" u2="u" k="3" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="12" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="12" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="9" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="12" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="9" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="12" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="12" />
+<hkern u1="&#x2018;" u2="&#x153;" k="9" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-9" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="12" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="12" />
+<hkern u1="&#x2018;" u2="&#x131;" k="3" />
+<hkern u1="&#x2019;" u2="&#x160;" k="3" />
+<hkern u1="&#x2019;" u2="C" k="12" />
+<hkern u1="&#x2019;" u2="G" k="12" />
+<hkern u1="&#x2019;" u2="J" k="36" />
+<hkern u1="&#x2019;" u2="O" k="12" />
+<hkern u1="&#x2019;" u2="Q" k="12" />
+<hkern u1="&#x2019;" u2="S" k="3" />
+<hkern u1="&#x2019;" u2="a" k="15" />
+<hkern u1="&#x2019;" u2="c" k="15" />
+<hkern u1="&#x2019;" u2="d" k="15" />
+<hkern u1="&#x2019;" u2="e" k="15" />
+<hkern u1="&#x2019;" u2="o" k="15" />
+<hkern u1="&#x2019;" u2="q" k="15" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="12" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="15" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="12" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="15" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="12" />
+<hkern u1="&#x2019;" u2="&#x152;" k="12" />
+<hkern u1="&#x2019;" u2="&#x153;" k="15" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="12" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="12" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="12" />
+<hkern u1="&#x178;" u2="&#x160;" k="-9" />
+<hkern u1="&#x178;" u2="&#x161;" k="15" />
+<hkern u1="&#x178;" u2="&#xdd;" k="-9" />
+<hkern u1="&#x178;" u2="&amp;" k="9" />
+<hkern u1="&#x178;" u2=")" k="-15" />
+<hkern u1="&#x178;" u2="&#x2c;" k="15" />
+<hkern u1="&#x178;" u2="-" k="15" />
+<hkern u1="&#x178;" u2="." k="15" />
+<hkern u1="&#x178;" u2=":" k="9" />
+<hkern u1="&#x178;" u2=";" k="9" />
+<hkern u1="&#x178;" u2="@" k="9" />
+<hkern u1="&#x178;" u2="A" k="6" />
+<hkern u1="&#x178;" u2="C" k="-2" />
+<hkern u1="&#x178;" u2="G" k="-2" />
+<hkern u1="&#x178;" u2="J" k="21" />
+<hkern u1="&#x178;" u2="O" k="-2" />
+<hkern u1="&#x178;" u2="Q" k="-2" />
+<hkern u1="&#x178;" u2="S" k="-9" />
+<hkern u1="&#x178;" u2="T" k="-9" />
+<hkern u1="&#x178;" u2="V" k="-11" />
+<hkern u1="&#x178;" u2="W" k="-2" />
+<hkern u1="&#x178;" u2="X" k="-6" />
+<hkern u1="&#x178;" u2="Y" k="-9" />
+<hkern u1="&#x178;" u2="]" k="-15" />
+<hkern u1="&#x178;" u2="a" k="17" />
+<hkern u1="&#x178;" u2="c" k="17" />
+<hkern u1="&#x178;" u2="d" k="17" />
+<hkern u1="&#x178;" u2="e" k="17" />
+<hkern u1="&#x178;" u2="f" k="3" />
+<hkern u1="&#x178;" u2="g" k="12" />
+<hkern u1="&#x178;" u2="m" k="12" />
+<hkern u1="&#x178;" u2="n" k="12" />
+<hkern u1="&#x178;" u2="o" k="17" />
+<hkern u1="&#x178;" u2="p" k="12" />
+<hkern u1="&#x178;" u2="q" k="17" />
+<hkern u1="&#x178;" u2="r" k="12" />
+<hkern u1="&#x178;" u2="s" k="15" />
+<hkern u1="&#x178;" u2="t" k="6" />
+<hkern u1="&#x178;" u2="u" k="12" />
+<hkern u1="&#x178;" u2="v" k="6" />
+<hkern u1="&#x178;" u2="w" k="3" />
+<hkern u1="&#x178;" u2="x" k="6" />
+<hkern u1="&#x178;" u2="y" k="6" />
+<hkern u1="&#x178;" u2="z" k="9" />
+<hkern u1="&#x178;" u2="}" k="-15" />
+<hkern u1="&#x178;" u2="&#xc4;" k="6" />
+<hkern u1="&#x178;" u2="&#xc5;" k="6" />
+<hkern u1="&#x178;" u2="&#xd6;" k="-2" />
+<hkern u1="&#x178;" u2="&#xe7;" k="17" />
+<hkern u1="&#x178;" u2="&#xae;" k="9" />
+<hkern u1="&#x178;" u2="&#xa9;" k="9" />
+<hkern u1="&#x178;" u2="&#x2122;" k="-18" />
+<hkern u1="&#x178;" u2="&#xc6;" k="6" />
+<hkern u1="&#x178;" u2="&#xd8;" k="-2" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="9" />
+<hkern u1="&#x178;" u2="&#xe6;" k="17" />
+<hkern u1="&#x178;" u2="&#xab;" k="18" />
+<hkern u1="&#x178;" u2="&#xbb;" k="12" />
+<hkern u1="&#x178;" u2="&#x2026;" k="15" />
+<hkern u1="&#x178;" u2="&#xc0;" k="6" />
+<hkern u1="&#x178;" u2="&#xc3;" k="6" />
+<hkern u1="&#x178;" u2="&#xd5;" k="-2" />
+<hkern u1="&#x178;" u2="&#x152;" k="-2" />
+<hkern u1="&#x178;" u2="&#x153;" k="17" />
+<hkern u1="&#x178;" u2="&#x2013;" k="15" />
+<hkern u1="&#x178;" u2="&#x2014;" k="15" />
+<hkern u1="&#x178;" u2="&#x178;" k="-9" />
+<hkern u1="&#x178;" u2="&#x2039;" k="18" />
+<hkern u1="&#x178;" u2="&#x203a;" k="12" />
+<hkern u1="&#x178;" u2="&#x201a;" k="15" />
+<hkern u1="&#x178;" u2="&#x201e;" k="15" />
+<hkern u1="&#x178;" u2="&#xc2;" k="6" />
+<hkern u1="&#x178;" u2="&#xc1;" k="6" />
+<hkern u1="&#x178;" u2="&#xd3;" k="-2" />
+<hkern u1="&#x178;" u2="&#xd4;" k="-2" />
+<hkern u1="&#x178;" u2="&#xd2;" k="-2" />
+<hkern u1="&#x178;" u2="&#x131;" k="12" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="12" />
+<hkern u1="&#x2039;" u2="T" k="12" />
+<hkern u1="&#x2039;" u2="V" k="6" />
+<hkern u1="&#x2039;" u2="W" k="6" />
+<hkern u1="&#x2039;" u2="Y" k="12" />
+<hkern u1="&#x2039;" u2="&#x178;" k="12" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="15" />
+<hkern u1="&#x203a;" u2="T" k="33" />
+<hkern u1="&#x203a;" u2="V" k="15" />
+<hkern u1="&#x203a;" u2="W" k="12" />
+<hkern u1="&#x203a;" u2="Y" k="15" />
+<hkern u1="&#x203a;" u2="x" k="15" />
+<hkern u1="&#x203a;" u2="z" k="15" />
+<hkern u1="&#x203a;" u2="&#x178;" k="15" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="15" />
+<hkern u1="&#x201a;" u2="0" k="12" />
+<hkern u1="&#x201a;" u2="1" k="18" />
+<hkern u1="&#x201a;" u2="4" k="15" />
+<hkern u1="&#x201a;" u2="6" k="9" />
+<hkern u1="&#x201a;" u2="8" k="5" />
+<hkern u1="&#x201a;" u2="9" k="3" />
+<hkern u1="&#x201a;" u2="A" k="-9" />
+<hkern u1="&#x201a;" u2="C" k="12" />
+<hkern u1="&#x201a;" u2="G" k="12" />
+<hkern u1="&#x201a;" u2="O" k="12" />
+<hkern u1="&#x201a;" u2="Q" k="12" />
+<hkern u1="&#x201a;" u2="T" k="18" />
+<hkern u1="&#x201a;" u2="U" k="3" />
+<hkern u1="&#x201a;" u2="V" k="18" />
+<hkern u1="&#x201a;" u2="W" k="6" />
+<hkern u1="&#x201a;" u2="Y" k="15" />
+<hkern u1="&#x201a;" u2="a" k="6" />
+<hkern u1="&#x201a;" u2="c" k="6" />
+<hkern u1="&#x201a;" u2="d" k="6" />
+<hkern u1="&#x201a;" u2="e" k="6" />
+<hkern u1="&#x201a;" u2="j" k="-9" />
+<hkern u1="&#x201a;" u2="m" k="6" />
+<hkern u1="&#x201a;" u2="n" k="6" />
+<hkern u1="&#x201a;" u2="o" k="6" />
+<hkern u1="&#x201a;" u2="p" k="6" />
+<hkern u1="&#x201a;" u2="q" k="6" />
+<hkern u1="&#x201a;" u2="r" k="6" />
+<hkern u1="&#x201a;" u2="t" k="12" />
+<hkern u1="&#x201a;" u2="u" k="6" />
+<hkern u1="&#x201a;" u2="v" k="9" />
+<hkern u1="&#x201a;" u2="w" k="3" />
+<hkern u1="&#x201a;" u2="y" k="9" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="12" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="3" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="6" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="12" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="6" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="12" />
+<hkern u1="&#x201a;" u2="&#x152;" k="12" />
+<hkern u1="&#x201a;" u2="&#x153;" k="6" />
+<hkern u1="&#x201a;" u2="&#x178;" k="15" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-9" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="12" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="12" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="12" />
+<hkern u1="&#x201a;" u2="&#xda;" k="3" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="3" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="3" />
+<hkern u1="&#x201a;" u2="&#x131;" k="6" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="15" />
+<hkern u1="&#x201e;" u2="0" k="12" />
+<hkern u1="&#x201e;" u2="1" k="18" />
+<hkern u1="&#x201e;" u2="4" k="15" />
+<hkern u1="&#x201e;" u2="6" k="9" />
+<hkern u1="&#x201e;" u2="8" k="5" />
+<hkern u1="&#x201e;" u2="9" k="3" />
+<hkern u1="&#x201e;" u2="A" k="-9" />
+<hkern u1="&#x201e;" u2="C" k="12" />
+<hkern u1="&#x201e;" u2="G" k="12" />
+<hkern u1="&#x201e;" u2="O" k="12" />
+<hkern u1="&#x201e;" u2="Q" k="12" />
+<hkern u1="&#x201e;" u2="T" k="18" />
+<hkern u1="&#x201e;" u2="U" k="3" />
+<hkern u1="&#x201e;" u2="V" k="18" />
+<hkern u1="&#x201e;" u2="W" k="6" />
+<hkern u1="&#x201e;" u2="Y" k="15" />
+<hkern u1="&#x201e;" u2="a" k="6" />
+<hkern u1="&#x201e;" u2="c" k="6" />
+<hkern u1="&#x201e;" u2="d" k="6" />
+<hkern u1="&#x201e;" u2="e" k="6" />
+<hkern u1="&#x201e;" u2="j" k="-9" />
+<hkern u1="&#x201e;" u2="m" k="6" />
+<hkern u1="&#x201e;" u2="n" k="6" />
+<hkern u1="&#x201e;" u2="o" k="6" />
+<hkern u1="&#x201e;" u2="p" k="6" />
+<hkern u1="&#x201e;" u2="q" k="6" />
+<hkern u1="&#x201e;" u2="r" k="6" />
+<hkern u1="&#x201e;" u2="t" k="12" />
+<hkern u1="&#x201e;" u2="u" k="6" />
+<hkern u1="&#x201e;" u2="v" k="9" />
+<hkern u1="&#x201e;" u2="w" k="3" />
+<hkern u1="&#x201e;" u2="y" k="9" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="12" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="3" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="6" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="12" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="6" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="12" />
+<hkern u1="&#x201e;" u2="&#x152;" k="12" />
+<hkern u1="&#x201e;" u2="&#x153;" k="6" />
+<hkern u1="&#x201e;" u2="&#x178;" k="15" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-9" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="12" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="12" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="12" />
+<hkern u1="&#x201e;" u2="&#xda;" k="3" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="3" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="3" />
+<hkern u1="&#x201e;" u2="&#x131;" k="6" />
+<hkern u1="&#xa4;" u2="4" k="6" />
+<hkern u1="&#xad;" u2="&#xdd;" k="15" />
+<hkern u1="&#xad;" u2="&#x17d;" k="3" />
+<hkern u1="&#xad;" u2="1" k="6" />
+<hkern u1="&#xad;" u2="7" k="9" />
+<hkern u1="&#xad;" u2="A" k="-3" />
+<hkern u1="&#xad;" u2="T" k="21" />
+<hkern u1="&#xad;" u2="V" k="15" />
+<hkern u1="&#xad;" u2="W" k="12" />
+<hkern u1="&#xad;" u2="X" k="9" />
+<hkern u1="&#xad;" u2="Y" k="15" />
+<hkern u1="&#xad;" u2="Z" k="3" />
+<hkern u1="&#xad;" u2="a" k="3" />
+<hkern u1="&#xad;" u2="c" k="3" />
+<hkern u1="&#xad;" u2="d" k="3" />
+<hkern u1="&#xad;" u2="e" k="3" />
+<hkern u1="&#xad;" u2="o" k="3" />
+<hkern u1="&#xad;" u2="q" k="3" />
+<hkern u1="&#xad;" u2="v" k="3" />
+<hkern u1="&#xad;" u2="x" k="9" />
+<hkern u1="&#xad;" u2="y" k="3" />
+<hkern u1="&#xad;" u2="z" k="9" />
+<hkern u1="&#xad;" u2="&#xc4;" k="-3" />
+<hkern u1="&#xad;" u2="&#xc5;" k="-3" />
+<hkern u1="&#xad;" u2="&#xe7;" k="3" />
+<hkern u1="&#xad;" u2="&#xc6;" k="-3" />
+<hkern u1="&#xad;" u2="&#xe6;" k="3" />
+<hkern u1="&#xad;" u2="&#xc0;" k="-3" />
+<hkern u1="&#xad;" u2="&#xc3;" k="-3" />
+<hkern u1="&#xad;" u2="&#x153;" k="3" />
+<hkern u1="&#xad;" u2="&#x178;" k="15" />
+<hkern u1="&#xad;" u2="&#xc2;" k="-3" />
+<hkern u1="&#xad;" u2="&#xc1;" k="-3" />
+<hkern u1="&#xd0;" u2="&#xdd;" k="11" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="14" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="12" />
+<hkern u1="&#xd0;" u2="." k="12" />
+<hkern u1="&#xd0;" u2="/" k="15" />
+<hkern u1="&#xd0;" u2="?" k="6" />
+<hkern u1="&#xd0;" u2="J" k="14" />
+<hkern u1="&#xd0;" u2="T" k="15" />
+<hkern u1="&#xd0;" u2="V" k="12" />
+<hkern u1="&#xd0;" u2="W" k="12" />
+<hkern u1="&#xd0;" u2="X" k="8" />
+<hkern u1="&#xd0;" u2="Y" k="11" />
+<hkern u1="&#xd0;" u2="Z" k="14" />
+<hkern u1="&#xd0;" u2="a" k="2" />
+<hkern u1="&#xd0;" u2="b" k="3" />
+<hkern u1="&#xd0;" u2="c" k="2" />
+<hkern u1="&#xd0;" u2="d" k="2" />
+<hkern u1="&#xd0;" u2="e" k="2" />
+<hkern u1="&#xd0;" u2="h" k="3" />
+<hkern u1="&#xd0;" u2="k" k="3" />
+<hkern u1="&#xd0;" u2="l" k="3" />
+<hkern u1="&#xd0;" u2="m" k="2" />
+<hkern u1="&#xd0;" u2="n" k="2" />
+<hkern u1="&#xd0;" u2="o" k="2" />
+<hkern u1="&#xd0;" u2="p" k="2" />
+<hkern u1="&#xd0;" u2="q" k="2" />
+<hkern u1="&#xd0;" u2="r" k="2" />
+<hkern u1="&#xd0;" u2="u" k="2" />
+<hkern u1="&#xd0;" u2="v" k="-3" />
+<hkern u1="&#xd0;" u2="w" k="-2" />
+<hkern u1="&#xd0;" u2="x" k="3" />
+<hkern u1="&#xd0;" u2="y" k="-3" />
+<hkern u1="&#xd0;" u2="z" k="3" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="2" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="2" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="12" />
+<hkern u1="&#xd0;" u2="&#x153;" k="2" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="6" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="6" />
+<hkern u1="&#xd0;" u2="&#x178;" k="11" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="12" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="12" />
+<hkern u1="&#xd0;" u2="&#x131;" k="2" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02 Light Italic" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02 Light Italic" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02 Light Italic" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02 Light Italic" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.ttf
new file mode 100644
index 0000000000..5153cee682
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.woff
new file mode 100644
index 0000000000..cf9993a1d6
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02LightItalic.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.eot
new file mode 100644
index 0000000000..37c715f5be
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.svg
new file mode 100644
index 0000000000..ad72d0be51
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.svg
@@ -0,0 +1,2473 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Medium]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated in 2010 by FontLab Studio. Copyright info pending.]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="565" id="Omnes_ATTW02Medium">
+<font-face font-family="Omnes_ATT W02 Medium" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="700" descent="-300" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="170" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="170" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="290" d="M139 188Q128 188 123 196T115 222L81 647H210L175 222Q173 205 168 197T150 188H139ZM138 -7Q84 -7 84 47V59Q84 114 138 114H151Q206 114 206 59V47Q206 -7 151 -7H138Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="419" d="M119 375Q107 375 102 382T95 406L63 647H187L156 406Q154 390 149 383T132 375H119ZM287 375Q275 375 270 382T263 406L231 647H355L324 406Q322 390 317 383T300 375H287Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="693" d="M34 242H168L218 405H109V484H242L293 650Q293 654 306 654Q313 654 321 651T337 642T349 626T354 601Q354 589 346 563L321 484H470L521 650Q523 654 535 654Q549 654 565 642T582 601Q582 589 574 563L550 484H659V405H525L475 242H584V163H451L401 -3Q399 -7 387 -7Q373 -7 357 5T340 46Q340 53 342 63T347 84L371 163H223L172 -3Q172 -7 159 -7Q152 -7 144 -4T128 5T116 21T111 46Q111 60 119 84L143 163H34V242ZM245 240H397L448 407H296L245 240Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="580" d="M245 -6Q199 -1 163 12T102 43T64 81T50 120Q50 133 56 143T71 161T87 171T95 173Q118 139 157 114T249 80V291Q216 299 183 311T124 344T82 395T66 471Q66 506 79 536T116 589T173 629T247 651V747H334V653Q377 649 410 637T467 610T502 576T514 541Q514 527 507 517T492 499T476 489T468 486Q446 517 410 539T329 568V365Q364 356 399 344T463 310T509 258T527 180Q527 105 474 55T332 -5V-103H245V-6ZM426 176Q426 217 398 237T326 272V79Q372 87 399 113T426 176ZM164 473Q164 435 187 416T252 384V566Q212 557 188 532T164 473Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="765" d="M199 344Q166 344 138 355T88 387T56 435T44 495V504Q44 536 56 564T89 612T138 645T200 657Q234 657 262 646T312 614T344 566T356 506V497Q356 465 344 437T311 389T261 356T199 344ZM137 -9Q135 -11 128 -10T113 -3T98 9T91 28Q91 42 97 53T120 81L623 660Q624 661 631 660T647 655T662 642T669 623Q669 612 663 599T641 569L137 -9ZM565 -6Q531 -6 503 5T453 37T421 85T409 145V154Q409 186 421 214T454 262T504 295T566 307Q599 307 627 296T677 264T709 216T721 156V147Q721 115 709 87T676 39T627 6T565 -6ZM200 407Q235 407 257 431T279 493V508Q279 546 257 570T199 595Q164 595 142 571T120 508V493Q120 455 142 431T200 407ZM566 57Q600 57 622 81T645 143V158Q645 196 623 220T565 245Q530 245 508 221T486 158V143Q486 105 508 81T566 57Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="697" d="M270 -13Q217 -13 174 0T101 37T53 98T36 182Q36 254 79 299T198 368Q174 394 158 426T141 499Q141 533 154 562T191 613T248 647T322 660Q359 660 389 649T442 617T477 569T490 508Q490 451 451 412T339 350L491 214Q501 230 512 252T535 300T555 351T571 401H657Q652 372 641 340T615 274T583 211T551 159L687 37Q688 36 686 30T678 16T660 2T632 -5Q619 -5 603 0T567 25L498 91Q453 41 396 14T270 -13ZM277 65Q326 65 367 87T440 145L246 320Q195 303 164 273T132 190Q132 130 172 98T277 65ZM234 499Q234 467 247 443T286 394Q348 411 376 437T405 503Q405 538 382 562T321 586Q278 586 256 561T234 499Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="252" d="M119 375Q107 375 102 382T95 406L63 647H187L156 406Q154 390 149 383T132 375H119Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="405" d="M339 -198Q292 -198 243 -168T152 -82T85 52T58 227Q58 320 84 398T152 533T242 620T339 652Q385 652 385 612Q385 600 379 590T370 577Q320 566 281 534T214 455T173 350T159 228Q159 162 173 104T214 -2T280 -80T370 -124Q372 -125 378 -135T385 -158Q385 -198 339 -198Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="405" d="M66 -198Q20 -198 20 -158Q20 -146 26 -136T35 -124Q85 -112 124 -81T191 -2T232 103T246 228Q246 291 232 349T191 454T125 533T35 577Q33 579 27 589T20 612Q20 652 66 652Q112 652 162 621T253 533T320 399T347 227Q347 131 321 53T253 -81T162 -167T66 -198Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="422" d="M102 331Q91 337 89 346T96 373L148 458L58 496Q41 504 38 511T38 533L39 537Q43 549 51 553T77 555L174 531L182 628Q184 648 191 653T211 659H215Q227 659 234 654T244 628L250 531L347 555Q365 558 373 553T386 537L387 533Q391 519 388 512T367 496L275 458L328 373Q338 356 335 347T322 331L319 329Q310 322 301 322T278 337L212 414L147 337Q133 323 125 323T105 329L102 331Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="568" d="M97 224Q73 224 64 234T54 261V267Q54 284 63 294T97 304H242V452Q242 475 252 485T280 495H288Q305 495 315 485T326 452V304H471Q495 304 504 294T514 267V261Q514 244 505 234T471 224H326V67Q326 44 316 34T288 24H280Q263 24 253 34T242 67V224H97Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="252" d="M78 -122Q73 -128 64 -129T45 -128T31 -118T28 -99L82 71Q89 94 98 106T133 118H142Q161 118 172 106T184 71V66Q184 53 180 39T161 6L78 -122Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="423" d="M56 289H367V198H56V289Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="252" d="M119 -7Q65 -7 65 47V59Q65 114 119 114H132Q187 114 187 59V47Q187 -7 132 -7H119Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="369" d="M1 -202Q7 -183 12 -165Q17 -151 21 -137T27 -116L266 654H359Q352 634 347 617Q342 603 338 589T332 568L94 -202H1Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="665" d="M332 -13Q269 -13 219 8T135 68T82 164T63 290V356Q63 426 81 482T135 578T220 639T332 660Q395 660 445 639T530 579T583 483T602 357V292Q602 222 584 166T530 70T445 9T332 -13ZM332 74Q413 74 456 129T499 287V359Q499 462 455 518T332 575Q253 575 210 520T167 361V288Q167 185 210 130T332 74Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="396" d="M182 506Q155 479 115 479Q80 479 58 498T35 548Q35 561 39 570T48 587T58 597T64 599Q75 588 89 582T123 576Q155 576 177 598T204 652H239Q284 652 284 605V0H182V506Z" />
+<glyph unicode="2" glyph-name="two" d="M325 260Q289 244 258 228T202 191T164 146T149 88H515V0H117Q83 0 68 12T53 56V81Q53 131 70 168T116 233T181 282T253 321Q285 337 314 351T365 382T401 419T414 465Q414 516 380 544T282 572Q246 572 220 562T174 535T143 496T124 448Q124 447 114 447T90 451T67 467T56 498Q56 523 71 551T115 604T188 644T289 660Q395 660 455 609T515 467Q515 423 499 392T457 337T396 295T325 260Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="576" d="M520 181Q520 90 454 39T269 -13Q210 -13 166 1T91 37T46 84T31 132Q31 152 40 163T60 180T81 185T91 185Q110 129 157 100T269 71Q338 71 378 101T419 185Q419 299 271 299H177V379H283Q343 379 373 405T404 477Q404 524 368 550T268 577Q205 577 167 549T110 479Q109 477 100 478T79 484T59 501T50 530Q50 551 65 573T109 615T178 647T269 660Q381 660 442 615T504 485Q504 380 394 347Q455 327 487 284T520 181Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="624" d="M376 173H111Q82 173 65 186T48 221V243Q48 260 55 273T77 307L308 606Q318 619 326 628T343 642T365 650T396 652Q438 652 455 637T473 585V257H589V173H473V0H376V173ZM141 255H380V568L141 255Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="568" d="M158 374Q187 387 220 395T291 403Q343 403 386 389T459 348T506 284T523 199Q523 151 506 112T456 45T377 2T272 -13Q219 -13 177 -1T104 30T58 71T41 115Q41 128 48 138T64 155T81 165T90 166Q117 121 163 96T272 70Q344 70 385 104T427 198Q427 257 389 290T281 323Q228 323 192 311T130 279Q67 283 67 343V589Q67 615 81 631T120 647H493V559H158V374Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="617" d="M340 575Q255 575 210 519T165 357V337Q197 366 244 387T349 408Q398 408 438 395T508 356T552 295T568 213V208Q568 160 550 120T501 50T425 4T328 -13Q263 -13 214 8T131 69T80 164T63 287V353Q63 425 82 481T136 578T222 639T335 660Q386 660 424 649T489 622T528 585T541 545Q541 530 533 520T516 504T498 496T489 495Q466 530 431 552T340 575ZM327 331Q280 331 238 310T166 257Q171 168 211 119T324 69Q388 69 428 105T469 200V206Q469 266 433 298T327 331Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="560" d="M37 555V647H461Q481 647 493 643T513 632T522 617T525 598V590Q525 573 516 554T495 515L234 31Q223 10 209 3T181 -5Q168 -5 157 0T138 12T126 25T124 34L411 555H37Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="613" d="M306 -12Q247 -12 200 1T120 38T70 96T52 172Q52 229 85 270T183 333Q126 352 99 389T71 479Q71 519 88 552T135 610T209 647T307 660Q416 660 478 612T540 481Q540 429 513 392T429 334Q495 314 527 272T560 172Q560 131 542 98T491 40T410 2T306 -12ZM307 67Q382 67 420 97T459 180Q459 232 419 263T306 295Q233 295 193 264T153 180Q153 130 193 99T307 67ZM307 369Q372 369 407 396T442 473Q442 521 407 551T306 581Q238 581 204 552T170 473Q170 426 206 398T307 369Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="617" d="M452 302Q421 273 373 253T269 232Q169 232 110 283T50 427V431Q50 479 68 520T117 593T193 641T290 659Q351 659 400 639T483 580T536 487T554 361V292Q554 140 483 64T277 -13Q226 -13 188 -3T124 24T85 59T72 97Q72 111 78 121T93 138T107 147T115 149Q141 114 179 93T273 71Q364 71 408 123T453 288L452 302ZM290 309Q337 309 379 330T450 383Q447 475 408 526T293 578Q261 578 235 567T190 538T160 494T149 439V434Q149 374 185 342T290 309Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="276" d="M131 318Q77 318 77 372V384Q77 438 131 438H144Q199 438 199 384V372Q199 318 144 318H131ZM131 -7Q77 -7 77 47V59Q77 114 131 114H144Q199 114 199 59V47Q199 -7 144 -7H131Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="276" d="M131 318Q77 318 77 372V384Q77 438 131 438H144Q199 438 199 384V372Q199 318 144 318H131ZM90 -122Q85 -128 76 -129T57 -128T43 -118T40 -99L94 71Q101 94 110 106T145 118H154Q173 118 184 106T196 71V66Q196 53 192 39T173 6L90 -122Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="494" d="M385 148Q415 132 425 116T434 85Q432 64 421 51T406 39L89 209Q75 217 67 228T59 264Q59 289 67 300T89 319L406 490Q407 490 411 487T420 477T429 463T434 444Q435 429 425 413T385 382L157 264L385 148Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="568" d="M109 334Q85 334 76 345T66 373V382Q66 399 75 410T109 421H459Q483 421 492 410T502 382V373Q502 356 493 345T459 334H109ZM109 100Q85 100 76 111T66 139V149Q66 166 75 177T109 188H459Q483 188 492 177T502 149V139Q502 122 493 111T459 100H109Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="493" d="M59 85Q58 100 68 116T108 148L335 264L108 382Q79 397 69 413T59 444Q61 464 72 477T88 490L404 319Q418 311 426 300T434 264Q434 239 426 228T404 209L88 39Q83 37 72 50T59 85Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="509" d="M188 197V198Q188 199 188 201T188 209Q192 255 207 286T242 338T284 375T326 405T357 437T370 481Q370 524 341 549T257 575Q221 575 196 564T154 535T128 494T115 448Q115 447 104 447T80 453T56 469T45 501Q45 523 57 550T96 602T163 642T259 659Q308 659 346 647T412 612T453 558T468 489Q468 448 454 420T417 372T370 334T322 299T286 256T271 197H188ZM225 -7Q171 -7 171 47V59Q171 114 225 114H239Q294 114 294 59V47Q294 -7 239 -7H225Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="773" d="M356 165Q326 165 302 176T260 208T232 256T222 317Q222 351 232 378T260 426T303 457T358 468Q389 468 413 454T452 419V466H528V298Q528 260 541 240T586 220Q624 220 643 258T663 359Q663 414 645 459T591 538T506 589T391 607Q330 607 279 586T192 528T135 438T114 323Q114 261 135 210T194 120T284 61T399 40Q449 40 491 54T563 89Q571 95 579 98T597 102Q605 102 611 97T622 86T627 73T628 66Q592 34 532 11T399 -13Q322 -13 260 11T153 80T84 186T59 323Q59 397 83 459T152 565T257 635T391 660Q467 660 527 639T630 578T695 483T718 360Q718 265 679 215T566 164Q524 164 496 181T459 228Q443 199 417 182T356 165ZM376 231Q407 231 429 250T451 302V351Q441 374 423 388T377 402Q342 402 322 379T301 317Q301 278 322 255T376 231Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="648" d="M84 589Q84 615 99 631T139 647H352Q458 647 514 604T570 479Q570 426 541 392T456 340Q528 323 562 281T596 181Q596 90 537 45T350 0H84V589ZM343 83Q421 83 455 107T490 189Q490 291 346 291H186V83H343ZM339 365Q396 365 429 392T463 467Q463 564 337 564H186V365H339Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="687" d="M385 -13Q313 -13 253 11T150 80T83 186T59 322Q59 396 83 458T150 565T254 635T388 660Q449 660 494 645T570 609T616 564T632 523Q632 505 623 494T603 476T583 467T572 467Q544 516 494 541T389 567Q340 567 299 550T229 500T183 423T166 324Q166 270 183 226T230 149T301 98T391 80Q457 80 507 110T578 193Q579 195 589 194T611 186T632 168T642 138Q642 120 626 94T578 45T498 4T385 -13Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="707" d="M84 589Q84 615 100 631T139 647H297Q379 647 444 625T555 562T624 461T648 325Q648 249 624 189T553 87T441 23T292 0H84V589ZM294 89Q413 89 478 150T544 324Q544 435 479 496T295 558H186V89H294Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="573" d="M84 589Q84 615 99 631T139 647H536V556H186V350H458V263H188V0H84V589Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="742" d="M636 -4Q625 -4 615 -1T597 12T585 37T580 79V91Q550 43 495 16T365 -12Q295 -12 239 13T142 82T81 188T59 322Q59 396 83 458T151 565T256 635T391 660Q450 660 495 646T572 612T620 568T636 526Q636 508 628 497T609 480T590 473T580 473Q550 521 499 546T393 571Q343 571 301 553T229 501T182 422T165 321Q165 267 180 221T223 142T293 90T385 71Q424 71 457 81T516 111T558 157T579 216V257H375V341H615Q643 341 656 325T670 282V6Q670 4 660 0T636 -4Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="715" d="M84 604Q84 651 127 651H142Q187 651 187 604V369H528V604Q528 651 571 651H586Q631 651 631 604V0H528V279H187V0H84V604Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="494" d="M210 -13Q171 -13 139 -4T85 21T50 54T38 91Q38 106 45 117T60 135T76 145T85 147Q102 115 132 96T202 76Q262 76 287 108T313 219V647H416V218Q416 100 366 44T210 -13Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="612" d="M84 604Q84 651 127 651H142Q187 651 187 604V344L446 618Q463 638 476 644T503 651Q519 651 530 645T548 630T557 614T559 605L301 331L566 41Q567 39 565 32T555 17T536 3T507 -4Q490 -4 477 2T451 25L187 313V0H84V604Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="558" d="M84 604Q84 651 127 651H142Q187 651 187 604V93H468Q516 93 516 49V45Q516 0 468 0H84V604Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="779" d="M84 594Q84 620 100 635T143 651H170Q200 651 212 639T237 599L390 254L544 599Q556 628 568 639T610 651H635Q663 651 679 636T696 594V0H598V518L455 196Q447 176 435 164T389 152Q372 152 362 155T344 164T332 178T323 196L182 515V0H84V594Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="712" d="M84 593Q84 619 96 635T133 651H150Q173 651 185 642T212 612L532 158V647H629V54Q629 27 617 12T581 -4H573Q550 -4 540 3T518 27L181 504V0H84V593Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="599" d="M84 589Q84 615 99 631T139 647H306Q427 647 492 594T557 441Q557 338 493 286T302 233H187V0H84V589ZM186 316H304Q379 316 416 347T454 439Q454 562 305 562H186V316Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="771" d="M657 -13Q643 -13 628 -5T597 20L572 45Q533 17 486 2T385 -13Q313 -13 253 11T150 80T82 186T58 322Q58 396 82 458T150 565T254 635T387 660Q459 660 519 636T622 567T689 460T713 324Q713 261 694 206T639 107L716 28Q717 26 714 20T703 6T684 -7T657 -13ZM387 78Q457 78 510 111L383 241L446 301L571 173Q611 233 611 322Q611 376 595 421T548 499T477 550T386 569Q336 569 295 551T225 501T179 423T162 324Q162 270 178 225T225 147T296 96T387 78Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="619" d="M501 -4Q481 -4 466 5T434 36L257 253H186V0H84V589Q84 615 99 631T139 647H323Q442 647 503 599T565 453Q565 370 516 321T372 263L561 38Q563 36 560 29T549 15T529 2T501 -4ZM185 334H321Q393 334 427 364T461 448Q461 562 318 562H185V334Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="584" d="M290 -13Q231 -13 186 -1T110 31T63 73T47 116Q47 129 54 139T69 157T85 168T94 171Q122 130 172 101T289 72Q357 72 393 98T430 173Q430 200 417 218T382 248T330 267T268 283Q230 292 194 304T129 337T82 390T64 472Q64 513 81 547T128 607T202 646T297 660Q351 660 392 649T462 620T504 583T519 545Q519 531 512 520T496 502T480 491T471 488Q445 525 400 550T298 575Q237 575 201 548T164 476Q164 449 175 433T206 406T255 388T317 373Q358 364 397 350T466 314T514 260T533 180Q533 90 469 39T290 -13Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="623" d="M83 555Q35 555 35 599V604Q35 647 83 647H541Q588 647 588 604V599Q588 555 541 555H364V0H260V555H83Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="650" d="M91 651Q111 651 127 640T151 604L327 111L503 604Q512 630 527 640T560 651Q572 651 583 647T603 638T616 627T620 618L376 0H275L30 618Q28 621 33 627T46 638T67 647T91 651Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="955" d="M63 622Q62 626 78 638T124 651Q144 651 160 638T184 591L289 115L412 522Q418 545 434 557T480 569Q510 569 525 557T548 522L669 117L776 592Q783 624 797 637T833 651Q847 651 858 647T876 638T888 629T891 622L732 0H615L480 453L339 0H224L63 622Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="636" d="M26 647H144L320 387L484 618Q496 637 509 645T537 653Q550 653 561 648T581 635T593 620T595 610L384 324L595 40Q597 37 593 30T581 16T561 3T535 -3Q520 -3 505 5T478 31L315 264L149 30Q136 10 123 3T96 -5Q83 -5 72 0T52 13T40 28T38 38L251 325L26 647Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="603" d="M250 265L24 647H139L302 351L468 615Q480 635 493 643T519 651Q536 651 548 646T568 633T579 618T581 609L353 265V0H250V265Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="639" d="M63 92L445 557H112Q66 557 66 599V605Q66 647 112 647H569V561L185 90H538Q584 90 584 48V42Q584 0 538 0H63V92Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="375" d="M85 650H294Q320 650 330 641T341 612V605Q341 585 331 576T294 566H178V-114H294Q320 -114 331 -123T342 -152V-158Q342 -178 331 -188T294 -198H85V650Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="369" d="M368 -202H275L37 568Q35 574 31 588T22 617Q17 634 10 654H103L342 -116Q344 -122 348 -136T357 -165Q362 -183 368 -202Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="375" d="M81 -198Q55 -198 44 -188T33 -158V-152Q33 -132 44 -123T81 -114H197V566H82Q56 566 45 575T34 605V612Q34 632 45 641T82 650H290V-198H81Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="530" d="M157 444Q143 444 134 453T125 476Q125 496 144 521T187 569T234 607T265 625Q272 623 295 608T342 570T385 522T404 476Q404 462 396 453T373 444Q352 444 327 469T264 551Q226 494 202 469T157 444Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="509" d="M71 -82Q28 -82 28 -44V-39Q28 -21 39 -11T71 0H438Q459 0 470 -10T481 -39V-44Q481 -82 438 -82H71Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="530" d="M168 609Q125 635 125 670Q125 687 138 698T170 709Q195 709 218 692Q242 676 265 654T305 610T331 574T339 555Q337 552 320 553T278 561T224 579T168 609Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="615" d="M340 -13Q279 -13 237 12T174 72V0H76V676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602V406Q204 447 245 470T343 493Q391 493 432 475T504 424T551 345T568 241Q568 184 551 138T504 58T432 6T340 -13ZM322 69Q390 69 431 116T472 240Q472 277 461 308T431 363T384 398T324 411Q268 411 231 384T174 311V204Q175 175 187 150T220 107T266 79T322 69Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="546" d="M297 -13Q243 -13 197 6T118 58T66 138T47 241Q47 296 66 342T118 421T197 474T297 493Q351 493 389 479T453 444T491 400T503 360Q503 345 494 336T475 321T455 315T444 316Q427 356 394 383T299 410Q265 410 236 398T186 363T153 309T141 241Q141 204 153 173T186 119T236 83T299 70Q360 70 393 97T444 164Q445 166 454 165T474 160T494 145T503 119Q503 102 491 80T454 37T389 2T297 -13Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="615" d="M272 -13Q224 -13 183 5T111 56T64 136T47 240Q47 297 64 343T111 423T183 475T275 493Q334 493 376 470T440 414V676Q440 678 451 682T476 687Q488 687 499 684T519 671T533 645T539 602V0H441V80Q412 36 371 12T272 -13ZM291 69Q321 69 348 79T396 108T429 153T441 210V312Q422 358 386 384T293 411Q225 411 184 365T143 240Q143 202 154 171T184 117T231 82T291 69Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="383" d="M23 481H123V517Q123 608 163 651T276 694Q332 694 358 675T384 629Q384 618 381 610T373 597T365 588T360 586Q349 597 330 605T288 614Q252 614 236 591T219 516V481H343V400H221V0H123V400H23V481Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="615" d="M295 -199Q242 -199 202 -187T134 -157T93 -118T79 -79Q79 -64 86 -54T101 -37T116 -29T125 -28Q148 -66 187 -90T292 -115Q440 -115 440 33V96Q411 54 370 32T271 9Q221 9 180 26T110 74T64 149T47 250Q47 303 64 347T112 424T184 475T275 493Q333 493 376 471T442 417V481H539V35Q539 -84 477 -141T295 -199ZM291 88Q320 88 346 97T393 124T426 166T441 219V318Q421 362 384 387T293 412Q259 412 232 400T185 367T154 315T143 250Q143 176 183 132T291 88Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="591" d="M175 405Q199 443 238 468T334 493Q382 493 417 478T475 434T509 368T520 285V0H421V274Q421 337 392 371T310 406Q248 406 212 369T175 265V0H76V676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602V405Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM130 584Q102 584 87 598T71 639Q71 666 86 680T131 695Q189 695 189 639Q189 584 130 584Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="260" d="M35 -200Q-13 -200 -40 -182T-67 -136Q-67 -126 -64 -117T-56 -102T-49 -93T-44 -90Q-33 -101 -17 -109T20 -118Q52 -118 66 -100T80 -41V481H179V-47Q179 -121 143 -160T35 -200ZM130 584Q102 584 87 598T71 639Q71 666 86 680T131 695Q189 695 189 639Q189 584 130 584Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="504" d="M76 676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602V265L342 452Q357 471 372 478T401 486Q414 486 424 481T441 469T450 456T452 447L277 254L479 35Q480 33 477 27T466 13T449 1T425 -5Q411 -5 397 2T366 29L175 235V0H76V676Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="260" d="M80 676Q80 678 91 682T116 687Q128 687 139 684T159 671T173 645T179 602V0H80V676Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="899" d="M112 486Q133 486 152 472T174 412Q199 447 236 470T323 493Q382 493 421 467T480 392Q505 434 546 463T647 493Q739 493 783 441T828 295V0H729V283Q729 406 623 406Q585 406 553 384T501 326V0H402V279Q402 406 296 406Q258 406 226 384T175 326V0H76V475Q76 477 86 481T112 486Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="591" d="M112 486Q134 486 153 471T174 408Q199 445 238 469T334 493Q382 493 417 478T475 435T509 370T520 287V0H421V275Q421 336 392 371T310 406Q262 406 228 383T175 322V0H76V475Q76 477 86 481T112 486Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="615" d="M76 475Q76 477 86 481T112 486Q123 486 134 483T153 470T168 444T174 402Q202 445 243 469T343 493Q391 493 432 475T504 424T551 345T568 241Q568 184 551 138T504 58T432 6T340 -13Q280 -13 238 14T175 77V-190H76V475ZM322 69Q390 69 431 116T472 240Q472 277 461 308T431 363T384 398T324 411Q268 411 231 384T174 311V210Q174 180 186 154T218 109T265 80T322 69Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="615" d="M440 78Q411 35 370 11T272 -13Q224 -13 183 5T111 56T64 136T47 240Q47 297 64 343T111 423T183 475T275 493Q335 493 377 469T441 412Q442 434 448 448T462 471T481 483T503 486Q518 486 528 482T539 475V-191H440V78ZM291 69Q321 69 348 80T396 110T429 155T441 211V311Q422 356 386 383T293 411Q225 411 184 365T143 240Q143 202 154 171T184 117T231 82T291 69Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="402" d="M112 486Q123 486 134 483T153 470T168 444T174 402Q206 493 296 493Q334 493 354 476T375 429Q375 415 371 405T361 389T351 380T345 378Q334 387 319 393T284 399Q255 399 234 386T200 347T181 288T175 212V0H76V475Q76 477 86 481T112 486Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="494" d="M244 -12Q194 -12 156 -3T93 22T54 55T41 90Q41 102 46 110T59 125T72 133T80 135Q104 104 144 84T242 63Q293 63 320 80T348 128Q348 146 340 158T316 177T277 191T224 203Q197 209 168 217T113 240T72 279T55 342Q55 410 107 451T247 493Q293 493 327 484T385 461T420 430T432 396Q432 384 426 375T413 360T399 352T392 350Q369 380 333 399T251 418Q200 418 174 400T147 352Q147 335 155 325T178 307T215 293T267 281Q295 275 326 267T383 243T426 203T444 139Q444 70 391 29T244 -12Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="401" d="M34 481H112V555Q112 581 124 591T156 602H166Q186 602 197 593T209 555V481H360V400H208V157Q208 112 222 90T272 68Q295 68 312 77T340 102Q341 103 345 101T355 94T364 80T368 60Q368 32 339 10T256 -13Q181 -13 147 27T112 146V400H34V481Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="529" d="M389 427Q402 462 416 474T450 486Q462 486 471 482T488 473T498 464T501 457L316 0H215L28 457Q26 462 41 474T80 486Q101 486 115 474T143 427L264 88L389 427Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="740" d="M35 457Q34 459 39 464T52 474T72 482T94 486Q110 486 125 475T146 433L221 96L313 387Q318 405 332 413T374 422Q401 422 415 414T434 387L523 98L597 433Q602 464 617 475T648 486Q659 486 669 482T688 473T700 464T705 457L576 0H475L372 320L266 0H165L35 457Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="519" d="M479 32Q480 30 477 24T467 12T450 0T425 -5Q405 -5 391 5T364 36L257 188L153 37Q127 -5 91 -5Q78 -5 68 0T50 12T39 24T37 32L196 240L39 450Q38 452 41 458T51 470T68 481T92 486Q112 486 126 476T154 445L261 292L366 445Q380 468 396 477T427 486Q440 486 451 481T469 470T479 457T482 450L323 240L479 32Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="528" d="M214 -195Q203 -195 194 -192T177 -184T165 -175T162 -168L242 27Q225 29 216 34T200 55L18 481H120L273 98L409 481H510L275 -140Q263 -172 247 -183T214 -195Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="512" d="M50 66L330 402H95Q52 402 52 438V445Q52 481 95 481H457V418L173 80H422Q465 80 465 43V37Q465 0 422 0H50V66Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="463" d="M384 -199Q348 -199 309 -181T237 -124T180 -26T152 117L97 147Q80 156 69 163T51 178T42 198T39 227Q39 244 42 255T51 275T69 291T97 307L152 337Q157 419 180 479T237 577T310 634T385 653Q406 653 418 642T431 610Q431 597 425 587T416 576Q369 567 338 545T286 488T257 403T246 291L127 227L245 163Q247 100 256 51T285 -33T336 -91T415 -122Q418 -123 424 -133T431 -156Q431 -176 418 -187T384 -199Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="286" d="M138 -198Q118 -198 106 -188T93 -150V606Q93 633 105 643T138 653H148Q168 653 180 643T192 606V-150Q192 -177 180 -187T148 -198H138Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="463" d="M78 -199Q57 -199 45 -188T32 -156Q32 -143 38 -133T47 -122Q94 -113 125 -91T177 -34T206 51T217 163L335 227L217 291Q215 354 206 403T177 487T126 545T47 576Q44 577 38 587T31 610Q31 630 43 641T77 653Q113 653 152 635T225 578T281 479T310 337L366 307Q382 298 393 291T411 276T421 256T424 227Q424 210 422 199T412 179T394 163T366 147L311 117Q306 34 283 -25T226 -123T154 -180T78 -199Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="529" d="M146 165Q129 165 120 174T111 200Q111 219 117 236T133 267T158 288T189 296Q216 296 254 277T333 221Q341 247 354 268T384 290Q401 290 410 281T419 254Q419 235 413 218T397 188T372 167T341 159Q313 159 275 178T197 234Q188 208 176 187T146 165Z" />
+<glyph unicode="&#xA0;" glyph-name="uni00A0" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="290" d="M151 459Q162 459 167 451T175 425L209 0H80L115 425Q117 442 122 450T140 459H151ZM152 654Q206 654 206 600V588Q206 533 152 533H139Q84 533 84 588V600Q84 654 139 654H152Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" d="M304 -99Q284 -99 274 -89T264 -52V29Q165 43 109 108T53 282Q53 332 68 375T111 451T177 505T263 535V612Q263 638 273 649T304 660H309Q329 660 339 649T349 612V538Q391 534 423 522T476 493T507 458T518 423Q518 408 510 398T493 381T475 373T465 373Q445 409 417 430T345 456V108Q433 118 471 200Q472 201 481 200T500 193T518 178T526 153Q526 135 515 115T481 76T426 44T350 26V-52Q350 -78 340 -88T309 -99H304ZM149 282Q149 214 181 171T268 113V453Q213 440 181 396T149 282Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="609" d="M88 257Q48 257 48 292V301Q48 318 59 327T88 337H164L189 470Q206 561 256 610T392 660Q436 660 469 648T523 617T556 575T567 529Q567 508 558 497T537 482T515 477T505 478Q495 524 469 550T395 576Q343 576 318 548T281 460L258 337H409Q449 337 449 301V292Q449 257 409 257H244L213 86H511Q557 86 557 46V40Q557 0 511 0H77Q31 0 31 40V47Q31 86 77 86H119L150 257H88Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="658" d="M85 366Q63 366 54 374T45 398V406Q45 420 54 428T85 437H143Q164 540 232 599T401 659Q450 659 488 647T553 617T594 576T608 533Q608 518 600 508T583 491T565 483T555 482Q529 527 493 550T399 574Q340 574 300 538T242 437H433Q455 437 463 429T472 406V398Q472 383 464 375T433 366H232V285H433Q455 285 463 276T472 253V245Q472 231 464 222T433 213H242Q259 148 301 111T404 73Q462 73 497 99T557 173Q558 175 566 173T585 165T603 147T611 121Q611 101 598 78T558 35T493 1T405 -13Q299 -13 231 46T142 213H85Q63 213 54 222T45 245V253Q45 267 54 276T85 285H135V366H85Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="619" d="M111 255Q88 255 79 264T70 288V296Q70 311 79 320T111 329H233L57 608Q55 611 58 618T70 632T90 646T120 652Q152 652 170 616L309 361L453 615Q464 635 477 643T504 652Q519 652 530 646T549 633T559 618T561 608L386 329H507Q530 329 539 320T548 296V288Q548 273 539 264T507 255H356V187H507Q530 187 539 178T548 154V146Q548 131 539 122T507 113H356V42Q356 19 347 7T314 -5H303Q263 -5 263 42V113H111Q88 113 79 122T70 146V154Q70 169 79 178T111 187H263V255H111Z" />
+<glyph unicode="&#xA6;" glyph-name="brokenbar" horiz-adv-x="286" d="M138 252Q118 252 106 262T93 300V606Q93 633 105 643T138 653H148Q168 653 180 643T192 606V300Q192 273 180 263T148 252H138ZM138 -198Q118 -198 106 -188T93 -150V156Q93 183 105 193T138 203H148Q168 203 180 193T192 156V-150Q192 -177 180 -187T148 -198H138Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="572" d="M276 -170Q216 -170 173 -158T102 -128T61 -88T47 -47Q47 -35 53 -26T67 -9T81 1T89 3Q116 -40 163 -65T276 -90Q339 -90 373 -70T407 -14Q407 8 397 21T367 44T320 61T257 76Q221 84 185 94T118 123T69 168T50 237Q50 285 78 317T156 367Q118 384 95 413T71 493Q71 530 87 560T133 613T202 647T290 659Q346 659 385 648T449 619T485 582T497 547Q497 534 491 524T477 508T462 498T454 496Q425 535 386 557T293 580Q242 580 206 561T170 503Q170 464 210 445T321 410Q356 401 392 391T457 363T504 319T523 251Q523 203 495 171T418 121Q456 104 480 76T505 -6Q505 -43 489 -73T443 -124T371 -158T276 -170ZM269 335Q259 336 246 340Q197 332 168 306T139 251Q139 227 152 211T189 185T242 167T306 152L331 146Q376 153 404 179T433 237Q433 261 420 276T385 302T333 320T269 335Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="530" d="M358 546Q329 546 313 561T297 605Q297 634 313 649T356 665Q383 665 399 650T416 605Q416 577 400 562T358 546ZM174 546Q147 546 131 561T114 605Q114 634 130 649T172 665Q201 665 217 650T233 605Q233 577 217 562T174 546Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="782" d="M391 -13Q318 -13 258 12T153 81T85 187T60 322Q60 395 85 457T154 564T259 634T392 660Q465 660 525 635T629 566T697 460T722 325Q722 252 697 190T628 83T524 13T391 -13ZM392 41Q452 41 502 61T588 119T643 208T663 323Q663 384 643 436T586 525T499 584T390 606Q330 606 280 586T194 528T139 439T119 324Q119 263 139 211T196 122T283 63T392 41ZM400 142Q361 142 328 155T271 192T234 249T220 322Q220 361 234 394T272 451T328 488T399 502Q441 502 470 489T514 461Q533 442 533 421Q533 411 527 403T514 390T500 383T493 383Q463 433 400 433Q379 433 361 425T328 402T305 367T297 323Q297 299 305 279T328 243T361 220T403 212Q437 212 460 226T496 266Q497 267 503 266T517 259T531 246T537 227Q537 208 514 183Q500 168 471 155T400 142Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="465" d="M187 255Q121 255 84 284T46 363Q46 392 57 415T99 455T180 482T312 497Q312 542 290 566T218 590Q177 590 152 571T113 526Q113 525 105 526T87 530T69 542T61 564Q61 578 70 595T100 626T151 650T224 660Q314 660 355 617T397 497V269Q397 266 388 263T366 260Q346 260 332 271T315 318Q295 289 261 272T187 255ZM219 -7Q170 -7 170 42V52Q170 101 219 101H230Q280 101 280 52V42Q280 -7 230 -7H219ZM209 317Q231 317 250 324T283 343T305 370T313 402V447Q256 443 220 436T164 420T136 397T129 369Q129 345 150 331T209 317Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="549" d="M452 72Q428 72 395 96T333 152T282 210T261 244Q261 248 270 262T296 295T332 335T374 375T415 404T452 416H454Q471 416 480 406T490 381V378Q490 366 477 350T443 317T396 280T343 243Q370 225 396 207T443 171T477 138T490 110V106Q490 92 481 82T454 72H452ZM229 89Q214 89 196 99T158 126T121 161T88 197T64 227T55 244Q55 247 64 260T87 290T120 327T158 362T195 388T229 399H231Q245 399 254 390T263 367V365Q263 354 252 340T222 309T180 276T132 243Q156 227 179 211T221 178T251 148T263 122V121Q263 107 254 98T231 89H229Z" />
+<glyph unicode="&#xAC;" glyph-name="logicalnot" horiz-adv-x="600" d="M49 380H543V96H453V297H49V380Z" />
+<glyph unicode="&#xAD;" glyph-name="uni00AD" horiz-adv-x="423" d="M56 289H367V198H56V289Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="782" d="M391 -13Q318 -13 258 12T153 81T85 187T60 322Q60 395 85 457T154 564T259 634T392 660Q465 660 525 635T629 566T697 460T722 325Q722 252 697 190T628 83T524 13T391 -13ZM392 41Q452 41 502 61T588 119T643 208T663 323Q663 384 643 436T586 525T499 584T390 606Q330 606 280 586T194 528T139 439T119 324Q119 263 139 211T196 122T283 63T392 41ZM497 148Q486 148 473 154T449 176L373 276H333V184Q333 149 301 149H290Q274 149 266 160T258 191V457Q258 499 298 499H404Q472 499 507 472T543 389Q543 347 519 320T450 285L539 177Q540 175 537 171T529 161T515 152T497 148ZM332 334H403Q435 334 451 348T468 386Q468 438 400 438H332V334Z" />
+<glyph unicode="&#xAF;" glyph-name="uni00AF" horiz-adv-x="530" d="M158 562Q108 562 108 601V607Q108 625 121 634T158 644H372Q422 644 422 607V601Q422 562 372 562H158Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="429" d="M213 343Q179 343 151 354T102 386T70 435T58 497V506Q58 540 70 568T103 617T153 649T216 661Q250 661 278 650T327 618T359 569T371 507V498Q371 464 359 436T326 387T276 355T213 343ZM216 406Q252 406 276 431T301 496V507Q301 547 276 572T214 598Q177 598 153 573T128 508V497Q128 457 154 432T216 406Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="630" d="M143 395Q120 395 110 405T100 432V438Q100 455 110 465T143 475H273V603Q273 626 283 636T311 646H318Q335 646 345 636T356 603V475H486Q509 475 519 465T529 438V432Q529 415 519 405T486 395H356V264Q356 241 346 231T318 221H311Q294 221 284 231T273 264V395H143ZM133 95Q110 95 100 105T90 132V140Q90 157 100 167T133 177H496Q519 177 529 167T539 140V132Q539 115 529 105T496 95H133Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="366" d="M217 429Q198 421 181 414T150 398T127 378T115 351H286Q324 351 324 320V317Q324 285 286 285H98Q69 285 59 294T49 326V343Q49 370 58 390T82 424T117 450T160 472Q194 489 218 502T243 541Q243 564 227 577T181 591Q146 591 128 571T104 526Q104 525 96 525T77 528T59 539T50 562Q50 578 59 594T85 624T128 646T187 655Q250 655 285 626T320 543Q320 518 312 500T289 470T256 448T217 429Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="366" d="M321 388Q321 336 283 307T173 278Q140 278 115 285T71 305T44 331T35 360Q35 375 42 383T58 396T74 400T83 398Q91 370 114 355T172 340Q206 340 227 354T248 393Q248 418 230 431T179 444H159Q139 444 131 451T122 471V476Q122 503 158 503H186Q211 503 226 514T242 546Q242 567 222 580T171 593Q136 593 118 577T91 540Q91 539 84 539T67 543T51 555T44 578Q44 607 78 631T173 655Q242 655 277 629T313 555Q313 497 251 479Q321 456 321 388Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="530" d="M191 555Q189 558 198 573T225 610T264 653T311 692Q337 709 359 709Q378 709 391 698T404 670Q404 653 394 638T362 609Q335 591 306 580T252 561T210 553T191 555Z" />
+<glyph unicode="&#xB5;" glyph-name="uni00B5" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 174 3Q173 4 171 5V-312H72V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="650" d="M521 -5Q488 -5 488 31V616Q488 652 521 652H532Q567 652 567 616V31Q567 -5 532 -5H521ZM387 -5Q370 -5 362 5T353 31V285H278Q163 285 103 330T43 465Q43 556 101 603T274 650H387Q407 650 419 638T431 606V31Q431 -5 398 -5H387Z" />
+<glyph unicode="&#xB7;" glyph-name="uni00B7" horiz-adv-x="262" d="M123 184Q69 184 69 237V249Q69 304 123 304H137Q192 304 192 249V237Q192 184 137 184H123Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="530" d="M218 -224Q212 -229 202 -230T183 -229T169 -220T168 -205L221 -87Q229 -68 238 -58T274 -48H283Q303 -48 316 -57T329 -83V-88Q329 -98 321 -108T297 -134L218 -224Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="366" d="M95 285Q58 285 58 317V318Q58 352 95 352H158V551Q138 534 111 534Q89 534 72 547T54 582Q54 592 57 599T65 612T73 620T78 622Q86 614 94 610T116 606Q136 606 151 618T170 651H197Q233 651 233 613V352H283Q321 352 321 318V317Q321 285 283 285H95Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="508" d="M254 254Q210 254 173 269T110 312T68 376T53 456Q53 498 68 535T111 600T175 644T255 660Q298 660 335 645T398 602T440 537T455 457Q455 414 440 377T397 313T334 270T254 254ZM247 -7Q198 -7 198 42V52Q198 101 247 101H258Q308 101 308 52V42Q308 -7 258 -7H247ZM255 326Q281 326 303 336T341 363T366 405T375 457Q375 485 366 509T341 550T302 578T255 588Q229 588 207 578T168 551T142 509T133 457Q133 430 142 406T168 364T207 336T255 326Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="549" d="M318 89Q303 89 295 98T286 121V123Q286 134 297 148T327 179T369 212T416 245Q392 261 369 277T328 310T298 340T286 366V367Q286 381 294 390T318 399H319Q334 399 352 389T390 362T428 327T461 291T485 260T494 244Q494 240 485 228T462 198T429 161T391 126T353 100T319 89H318ZM95 72Q78 72 69 82T59 107V110Q59 122 72 138T106 171T153 208T206 245Q179 263 153 281T106 317T72 350T59 378V382Q59 396 68 406T95 416H96Q112 416 132 405T174 375T216 336T252 296T277 263T287 244Q287 240 278 226T252 193T216 153T175 113T133 84T96 72H95Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="822" d="M95 285Q58 285 58 317V318Q58 352 95 352H158V551Q138 534 111 534Q89 534 72 547T54 582Q54 592 57 599T65 612T73 620T78 622Q86 614 94 610T116 606Q136 606 151 618T170 651H197Q233 651 233 613V352H283Q321 352 321 318V317Q321 285 283 285H95ZM160 0Q160 14 164 23T183 52Q189 59 210 85T262 147T326 223T390 302Q405 321 424 345T465 395T508 450T552 505Q601 567 654 634Q655 635 660 635T672 632T684 623T690 606Q690 595 685 583T665 554Q659 546 638 520T587 458T523 381T459 302Q444 283 425 259T384 208T340 153T297 99Q248 37 196 -29Q194 -31 189 -31T177 -27T165 -17T160 0ZM697 -4Q680 -4 672 6T664 34V90H530Q508 90 496 99T483 124V138Q484 149 488 158T501 181L611 335Q622 355 635 360T679 366Q710 366 722 355T735 314V151H764Q799 151 799 122V119Q799 90 764 90H735V34Q735 -4 701 -4H697ZM552 150H667L666 310L552 150Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="822" d="M673 144Q654 136 637 129T606 113T583 93T571 66H742Q780 66 780 35V32Q780 0 742 0H554Q525 0 515 9T505 41V58Q505 85 514 105T538 139T573 165T616 187Q650 204 674 217T699 256Q699 279 683 292T637 306Q602 306 584 286T560 241Q560 240 552 240T533 243T515 254T506 277Q506 293 515 309T541 339T584 361T643 370Q706 370 741 341T776 258Q776 233 768 215T745 185T712 163T673 144ZM95 285Q58 285 58 317V318Q58 352 95 352H158V551Q138 534 111 534Q89 534 72 547T54 582Q54 592 57 599T65 612T73 620T78 622Q86 614 94 610T116 606Q136 606 151 618T170 651H197Q233 651 233 613V352H283Q321 352 321 318V317Q321 285 283 285H95ZM132 0Q132 14 136 23T155 52Q161 59 182 85T234 147T298 223T362 302Q377 321 396 345T437 395T480 450T524 505Q573 567 626 634Q627 635 632 635T644 632T656 623T662 606Q662 595 657 583T637 554Q631 546 610 520T559 458T495 381T431 302Q416 283 397 259T356 208T312 153T269 99Q220 37 168 -29Q166 -31 161 -31T149 -27T137 -17T132 0Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="822" d="M697 -4Q680 -4 672 6T664 34V90H530Q508 90 496 99T483 124V138Q484 149 488 158T501 181L611 335Q622 355 635 360T679 366Q710 366 722 355T735 314V151H764Q799 151 799 122V119Q799 90 764 90H735V34Q735 -4 701 -4H697ZM321 388Q321 336 283 307T173 278Q140 278 115 285T71 305T44 331T35 360Q35 375 42 383T58 396T74 400T83 398Q91 370 114 355T172 340Q206 340 227 354T248 393Q248 418 230 431T179 444H159Q139 444 131 451T122 471V476Q122 503 158 503H186Q211 503 226 514T242 546Q242 567 222 580T171 593Q136 593 118 577T91 540Q91 539 84 539T67 543T51 555T44 578Q44 607 78 631T173 655Q242 655 277 629T313 555Q313 497 251 479Q321 456 321 388ZM158 0Q158 14 162 23T181 52Q187 59 208 85T260 147T324 223T388 302Q403 321 422 345T463 395T506 450T550 505Q599 567 652 634Q653 635 658 635T670 632T682 623T688 606Q688 595 683 583T663 554Q657 546 636 520T585 458T521 381T457 302Q442 283 423 259T382 208T338 153T295 99Q246 37 194 -29Q192 -31 187 -31T175 -27T163 -17T158 0ZM552 150H667L666 310L552 150Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="509" d="M321 450V448V438Q317 392 302 361T267 309T225 272T183 242T152 210T139 166Q139 123 168 98T252 72Q288 72 313 83T355 112T381 153T394 199Q394 200 405 200T429 194T453 178T464 146Q464 123 452 96T413 44T346 4T250 -12Q201 -12 163 0T97 35T56 89T41 158Q41 199 55 227T92 275T139 313T187 348T223 391T238 450H321ZM284 654Q338 654 338 600V588Q338 533 284 533H270Q215 533 215 588V600Q215 654 270 654H284Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM208 775Q165 801 165 836Q165 853 178 864T210 875Q235 875 258 858Q282 842 305 820T345 776T371 740T379 721Q377 718 360 719T318 727T264 745T208 775Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM271 721Q269 724 278 739T305 776T344 819T391 858Q417 875 439 875Q458 875 471 864T484 836Q484 819 474 804T442 775Q415 757 386 746T332 727T290 719T271 721Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM221 713Q207 713 198 722T189 745Q189 765 208 790T251 838T298 876T329 894Q336 892 359 877T406 839T449 791T468 745Q468 731 460 722T437 713Q416 713 391 738T328 820Q290 763 266 738T221 713Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM215 718Q198 718 189 727T180 753Q180 772 186 789T202 820T227 841T258 849Q285 849 323 830T402 774Q410 800 423 821T453 843Q470 843 479 834T488 807Q488 788 482 771T466 741T441 720T410 712Q382 712 344 731T266 787Q257 761 245 740T215 718Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM422 712Q393 712 377 727T361 771Q361 800 377 815T420 831Q447 831 463 816T480 771Q480 743 464 728T422 712ZM238 712Q211 712 195 727T178 771Q178 800 194 815T236 831Q265 831 281 816T297 771Q297 743 281 728T238 712Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="660" d="M450 720Q450 688 436 663T397 622L399 620L632 26Q633 23 629 18T616 8T598 -1T575 -5Q558 -5 542 5T514 43L457 190H198L141 42Q131 15 116 5T84 -5Q61 -5 44 7T28 26L258 620Q258 621 260 623Q236 637 223 662T209 720Q209 745 218 767T243 805T281 831T330 840Q383 840 416 806T450 720ZM223 273H432L327 549L223 273ZM329 655Q355 655 372 673T390 720Q390 749 373 768T329 787Q303 787 285 768T267 720Q267 693 285 674T329 655Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="888" d="M457 190H198L141 42Q130 15 116 5T84 -5Q61 -5 44 7T28 26L254 608Q260 625 274 636T320 647H785Q833 647 833 604V598Q833 555 785 555H421Q426 542 432 527Q437 514 443 498T456 465Q471 425 491 375H711Q759 375 759 334V329Q759 287 711 287H527L603 91H791Q838 91 838 50V45Q838 0 791 0H587Q554 0 537 11T509 52L457 190ZM223 273H432L327 547L223 273Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="687" d="M385 -13Q313 -13 253 11T150 80T83 186T59 322Q59 396 83 458T150 565T254 635T388 660Q449 660 494 645T570 609T616 564T632 523Q632 505 623 494T603 476T583 467T572 467Q544 516 494 541T389 567Q340 567 299 550T229 500T183 423T166 324Q166 270 183 226T230 149T301 98T391 80Q457 80 507 110T578 193Q579 195 589 194T611 186T632 168T642 138Q642 120 626 94T578 45T498 4T385 -13ZM322 -224Q316 -229 306 -230T287 -229T273 -220T272 -205L325 -87Q333 -68 342 -58T378 -48H387Q407 -48 420 -57T433 -83V-88Q433 -98 425 -108T401 -134L322 -224Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM231 775Q188 801 188 836Q188 853 201 864T233 875Q258 875 281 858Q305 842 328 820T368 776T394 740T402 721Q400 718 383 719T341 727T287 745T231 775Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM240 721Q238 724 247 739T274 776T313 819T360 858Q386 875 408 875Q427 875 440 864T453 836Q453 819 443 804T411 775Q384 757 355 746T301 727T259 719T240 721Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM216 713Q202 713 193 722T184 745Q184 765 203 790T246 838T293 876T324 894Q331 892 354 877T401 839T444 791T463 745Q463 731 455 722T432 713Q411 713 386 738T323 820Q285 763 261 738T216 713Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM417 712Q388 712 372 727T356 771Q356 800 372 815T415 831Q442 831 458 816T475 771Q475 743 459 728T417 712ZM233 712Q206 712 190 727T173 771Q173 800 189 815T231 831Q260 831 276 816T292 771Q292 743 276 728T233 712Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM35 775Q-8 801 -8 836Q-8 853 5 864T37 875Q62 875 85 858Q109 842 132 820T172 776T198 740T206 721Q204 718 187 719T145 727T91 745T35 775Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM82 721Q80 724 89 739T116 776T155 819T202 858Q228 875 250 875Q269 875 282 864T295 836Q295 819 285 804T253 775Q226 757 197 746T143 727T101 719T82 721Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM36 713Q22 713 13 722T4 745Q4 765 23 790T66 838T113 876T144 894Q151 892 174 877T221 839T264 791T283 745Q283 731 275 722T252 713Q231 713 206 738T143 820Q105 763 81 738T36 713Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM237 712Q208 712 192 727T176 771Q176 800 192 815T235 831Q262 831 278 816T295 771Q295 743 279 728T237 712ZM53 712Q26 712 10 727T-7 771Q-7 800 9 815T51 831Q80 831 96 816T112 771Q112 743 96 728T53 712Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="751" d="M67 284Q45 284 37 294T28 320V329Q28 344 36 354T67 364H120V589Q120 615 136 631T175 647H337Q419 647 485 625T597 561T667 460T692 325Q692 250 667 190T595 87T482 23T332 0H177Q153 0 137 17T120 57V284H67ZM338 91Q459 91 524 151T589 324Q589 434 524 495T339 556H222V364H397Q419 364 428 354T437 329V320Q437 304 428 294T397 284H222V91H338Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="712" d="M84 593Q84 619 96 635T133 651H150Q173 651 185 642T212 612L532 158V647H629V54Q629 27 617 12T581 -4H573Q550 -4 540 3T518 27L181 504V0H84V593ZM237 718Q220 718 211 727T202 753Q202 772 208 789T224 820T249 841T280 849Q307 849 345 830T424 774Q432 800 445 821T475 843Q492 843 501 834T510 807Q510 788 504 771T488 741T463 720T432 712Q404 712 366 731T288 787Q279 761 267 740T237 718Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM272 775Q229 801 229 836Q229 853 242 864T274 875Q299 875 322 858Q346 842 369 820T409 776T435 740T443 721Q441 718 424 719T382 727T328 745T272 775Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM323 721Q321 724 330 739T357 776T396 819T443 858Q469 875 491 875Q510 875 523 864T536 836Q536 819 526 804T494 775Q467 757 438 746T384 727T342 719T323 721Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM277 713Q263 713 254 722T245 745Q245 765 264 790T307 838T354 876T385 894Q392 892 415 877T462 839T505 791T524 745Q524 731 516 722T493 713Q472 713 447 738T384 820Q346 763 322 738T277 713Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM267 718Q250 718 241 727T232 753Q232 772 238 789T254 820T279 841T310 849Q337 849 375 830T454 774Q462 800 475 821T505 843Q522 843 531 834T540 807Q540 788 534 771T518 741T493 720T462 712Q434 712 396 731T318 787Q309 761 297 740T267 718Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM478 712Q449 712 433 727T417 771Q417 800 433 815T476 831Q503 831 519 816T536 771Q536 743 520 728T478 712ZM294 712Q267 712 251 727T234 771Q234 800 250 815T292 831Q321 831 337 816T353 771Q353 743 337 728T294 712Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="576" d="M110 83Q98 94 98 108T114 140L234 262L114 382Q97 399 98 414T110 440L116 446Q127 458 142 458T174 441L290 319L405 441Q422 459 437 459T464 446L469 440Q481 429 482 414T466 382L346 262L466 140Q483 123 482 109T469 83L463 77Q452 66 437 65T405 82L290 204L174 82Q157 64 142 65T116 77L110 83Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="771" d="M102 -43Q100 -45 93 -43T78 -35T64 -21T57 -1Q57 9 62 20T81 48L131 103Q96 147 78 202T59 322Q59 394 83 455T150 562T254 634T387 660Q444 660 493 644T582 599L659 684Q660 686 667 684T682 676T696 662T703 642Q703 632 699 621T680 593L637 546Q673 502 693 446T713 324Q713 252 689 191T622 84T518 13T385 -13Q326 -13 276 3T186 50L102 -43ZM387 78Q439 78 481 96T553 147T599 224T615 322Q615 411 570 476L250 120Q278 100 312 89T387 78ZM157 324Q157 280 168 243T199 174L518 528Q490 548 457 558T385 569Q333 569 291 551T219 500T173 422T157 324Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM233 767Q190 793 190 828Q190 845 203 856T235 867Q260 867 283 850Q307 834 330 812T370 768T396 732T404 713Q402 710 385 711T343 719T289 737T233 767Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM280 713Q278 716 287 731T314 768T353 811T400 850Q426 867 448 867Q467 867 480 856T493 828Q493 811 483 796T451 767Q424 749 395 738T341 719T299 711T280 713Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM230 713Q216 713 207 722T198 745Q198 765 217 790T260 838T307 876T338 894Q345 892 368 877T415 839T458 791T477 745Q477 731 469 722T446 713Q425 713 400 738T337 820Q299 763 275 738T230 713Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM431 712Q402 712 386 727T370 771Q370 800 386 815T429 831Q456 831 472 816T489 771Q489 743 473 728T431 712ZM247 712Q220 712 204 727T187 771Q187 800 203 815T245 831Q274 831 290 816T306 771Q306 743 290 728T247 712Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="603" d="M250 265L24 647H139L302 351L468 615Q480 635 493 643T519 651Q536 651 548 646T568 633T579 618T581 609L353 265V0H250V265ZM235 713Q233 716 242 731T269 768T308 811T355 850Q381 867 403 867Q422 867 435 856T448 828Q448 811 438 796T406 767Q379 749 350 738T296 719T254 711T235 713Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="605" d="M127 -4Q84 -4 84 43V604Q84 651 127 651H142Q187 651 187 604V531H305Q426 531 491 479T556 327Q556 226 492 174T301 121H187V43Q187 -4 142 -4H127ZM187 446V204H304Q379 204 416 235T454 325Q454 446 306 446H187Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="582" d="M504 531Q504 402 361 384Q410 371 444 351T500 303T532 244T542 174Q542 135 528 101T488 41T429 2T355 -13Q286 -13 252 11T217 64Q217 74 221 83T231 98T241 108T247 111Q263 93 284 81T344 68Q365 68 383 76T416 99T438 133T446 174Q446 196 440 217T419 256T377 288T311 314Q280 323 266 337T252 377Q252 397 262 412T273 427Q340 426 374 447T409 520Q409 564 379 588T298 613Q238 613 207 578T175 467V0H76V462Q76 580 133 637T299 694Q346 694 384 683T448 650T489 598T504 531Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM181 609Q138 635 138 670Q138 687 151 698T183 709Q208 709 231 692Q255 676 278 654T318 610T344 574T352 555Q350 552 333 553T291 561T237 579T181 609Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM200 555Q198 558 207 573T234 610T273 653T320 692Q346 709 368 709Q387 709 400 698T413 670Q413 653 403 638T371 609Q344 591 315 580T261 561T219 553T200 555Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM168 547Q154 547 145 556T136 579Q136 599 155 624T198 672T245 710T276 728Q283 726 306 711T353 673T396 625T415 579Q415 565 407 556T384 547Q363 547 338 572T275 654Q237 597 213 572T168 547Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM156 552Q139 552 130 561T121 587Q121 606 127 623T143 654T168 675T199 683Q226 683 264 664T343 608Q351 634 364 655T394 677Q411 677 420 668T429 641Q429 622 423 605T407 575T382 554T351 546Q323 546 285 565T207 621Q198 595 186 574T156 552Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM367 546Q338 546 322 561T306 605Q306 634 322 649T365 665Q392 665 408 650T425 605Q425 577 409 562T367 546ZM183 546Q156 546 140 561T123 605Q123 634 139 649T181 665Q210 665 226 650T242 605Q242 577 226 562T183 546Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM273 546Q246 546 224 555T187 580T162 617T153 665Q153 690 162 712T187 750T225 776T274 785Q328 785 361 751T394 665Q394 640 385 618T360 580T322 555T273 546ZM273 600Q300 600 317 618T335 665Q335 694 317 713T273 732Q247 732 230 713T212 665Q212 638 229 619T273 600Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="897" d="M213 -11Q135 -11 92 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 413Q210 413 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T117 452T179 481T267 493Q330 493 373 470T439 405Q469 447 515 470T620 493Q672 493 715 476T788 426T835 350T852 251V243Q851 227 845 221T822 214H466Q469 181 482 154T516 107T567 76T630 65Q661 65 685 71T728 89T759 114T782 145Q782 146 790 144T807 137T825 122T833 96Q833 80 820 61T780 26T716 -2T628 -13Q550 -13 497 17T416 104Q390 53 337 21T213 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM620 418Q560 418 519 380T468 281H755Q751 344 716 381T620 418Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="546" d="M297 -13Q243 -13 197 6T118 58T66 138T47 241Q47 296 66 342T118 421T197 474T297 493Q351 493 389 479T453 444T491 400T503 360Q503 345 494 336T475 321T455 315T444 316Q427 356 394 383T299 410Q265 410 236 398T186 363T153 309T141 241Q141 204 153 173T186 119T236 83T299 70Q360 70 393 97T444 164Q445 166 454 165T474 160T494 145T503 119Q503 102 491 80T454 37T389 2T297 -13ZM243 -224Q237 -229 227 -230T208 -229T194 -220T193 -205L246 -87Q254 -68 263 -58T299 -48H308Q328 -48 341 -57T354 -83V-88Q354 -98 346 -108T322 -134L243 -224Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM183 609Q140 635 140 670Q140 687 153 698T185 709Q210 709 233 692Q257 676 280 654T320 610T346 574T354 555Q352 552 335 553T293 561T239 579T183 609Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM216 555Q214 558 223 573T250 610T289 653T336 692Q362 709 384 709Q403 709 416 698T429 670Q429 653 419 638T387 609Q360 591 331 580T277 561T235 553T216 555Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM184 547Q170 547 161 556T152 579Q152 599 171 624T214 672T261 710T292 728Q299 726 322 711T369 673T412 625T431 579Q431 565 423 556T400 547Q379 547 354 572T291 654Q253 597 229 572T184 547Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM385 546Q356 546 340 561T324 605Q324 634 340 649T383 665Q410 665 426 650T443 605Q443 577 427 562T385 546ZM201 546Q174 546 158 561T141 605Q141 634 157 649T199 665Q228 665 244 650T260 605Q260 577 244 562T201 546Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM24 609Q-19 635 -19 670Q-19 687 -6 698T26 709Q51 709 74 692Q98 676 121 654T161 610T187 574T195 555Q193 552 176 553T134 561T80 579T24 609Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM71 555Q69 558 78 573T105 610T144 653T191 692Q217 709 239 709Q258 709 271 698T284 670Q284 653 274 638T242 609Q215 591 186 580T132 561T90 553T71 555Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM25 547Q11 547 2 556T-7 579Q-7 599 12 624T55 672T102 710T133 728Q140 726 163 711T210 673T253 625T272 579Q272 565 264 556T241 547Q220 547 195 572T132 654Q94 597 70 572T25 547Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM226 546Q197 546 181 561T165 605Q165 634 181 649T224 665Q251 665 267 650T284 605Q284 577 268 562T226 546ZM42 546Q15 546 -1 561T-18 605Q-18 634 -2 649T40 665Q69 665 85 650T101 605Q101 577 85 562T42 546Z" />
+<glyph unicode="&#xF0;" glyph-name="null" horiz-adv-x="610" d="M224 495Q205 484 192 488T172 506L170 511Q162 525 166 537T190 561L264 601Q195 630 116 636Q113 636 114 646T122 669T146 692T189 702Q228 702 272 689T361 650L466 708Q486 719 498 715T518 696L521 693Q540 664 500 643L425 603Q482 551 519 472T556 283Q556 213 537 158T484 65T403 7T297 -13Q242 -13 197 3T120 49T71 122T53 219Q53 269 70 310T118 381T192 427T288 443Q345 443 389 419T456 362Q445 425 414 474T337 558L224 495ZM298 68Q335 68 364 82T414 120T444 179T455 252V285Q446 300 431 315T396 341T353 360T305 367Q230 367 190 329T150 220Q150 148 190 108T298 68Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="591" d="M112 486Q134 486 153 471T174 408Q199 445 238 469T334 493Q382 493 417 478T475 435T509 370T520 287V0H421V275Q421 336 392 371T310 406Q262 406 228 383T175 322V0H76V475Q76 477 86 481T112 486ZM174 552Q157 552 148 561T139 587Q139 606 145 623T161 654T186 675T217 683Q244 683 282 664T361 608Q369 634 382 655T412 677Q429 677 438 668T447 641Q447 622 441 605T425 575T400 554T369 546Q341 546 303 565T225 621Q216 595 204 574T174 552Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM188 609Q145 635 145 670Q145 687 158 698T190 709Q215 709 238 692Q262 676 285 654T325 610T351 574T359 555Q357 552 340 553T298 561T244 579T188 609Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM243 555Q241 558 250 573T277 610T316 653T363 692Q389 709 411 709Q430 709 443 698T456 670Q456 653 446 638T414 609Q387 591 358 580T304 561T262 553T243 555Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM193 547Q179 547 170 556T161 579Q161 599 180 624T223 672T270 710T301 728Q308 726 331 711T378 673T421 625T440 579Q440 565 432 556T409 547Q388 547 363 572T300 654Q262 597 238 572T193 547Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM183 552Q166 552 157 561T148 587Q148 606 154 623T170 654T195 675T226 683Q253 683 291 664T370 608Q378 634 391 655T421 677Q438 677 447 668T456 641Q456 622 450 605T434 575T409 554T378 546Q350 546 312 565T234 621Q225 595 213 574T183 552Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM394 546Q365 546 349 561T333 605Q333 634 349 649T392 665Q419 665 435 650T452 605Q452 577 436 562T394 546ZM210 546Q183 546 167 561T150 605Q150 634 166 649T208 665Q237 665 253 650T269 605Q269 577 253 562T210 546Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="630" d="M308 378Q257 378 257 429V440Q257 464 270 477T308 491H320Q346 491 359 478T372 440V429Q372 378 320 378H308ZM308 38Q257 38 257 89V100Q257 151 308 151H320Q372 151 372 100V89Q372 38 320 38H308ZM133 223Q110 223 100 233T90 261V268Q90 285 100 295T133 306H496Q519 306 529 296T539 268V261Q539 243 529 233T496 223H133Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="598" d="M89 -46Q87 -48 81 -46T66 -39T52 -27T46 -9Q46 0 51 11T69 36L107 78Q81 110 67 151T53 239Q53 292 72 338T125 419T204 473T303 493Q344 493 379 481T445 449L514 526Q515 527 522 526T537 519T551 507T557 489Q557 481 552 470T535 445L496 403Q522 370 536 329T551 241Q551 188 532 142T479 61T400 7T301 -13Q260 -13 225 -2T159 31L89 -46ZM302 68Q336 68 365 81T416 117T449 171T461 240Q461 297 432 340L213 99Q232 85 254 77T302 68ZM391 382Q352 412 302 412Q267 412 238 399T188 363T155 309T143 240Q143 212 150 187T172 141L391 382Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM182 609Q139 635 139 670Q139 687 152 698T184 709Q209 709 232 692Q256 676 279 654T319 610T345 574T353 555Q351 552 334 553T292 561T238 579T182 609Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM245 555Q243 558 252 573T279 610T318 653T365 692Q391 709 413 709Q432 709 445 698T458 670Q458 653 448 638T416 609Q389 591 360 580T306 561T264 553T245 555Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM188 547Q174 547 165 556T156 579Q156 599 175 624T218 672T265 710T296 728Q303 726 326 711T373 673T416 625T435 579Q435 565 427 556T404 547Q383 547 358 572T295 654Q257 597 233 572T188 547Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM390 546Q361 546 345 561T329 605Q329 634 345 649T388 665Q415 665 431 650T448 605Q448 577 432 562T390 546ZM206 546Q179 546 163 561T146 605Q146 634 162 649T204 665Q233 665 249 650T265 605Q265 577 249 562T206 546Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="528" d="M214 -195Q203 -195 194 -192T177 -184T165 -175T162 -168L242 27Q225 29 216 34T200 55L18 481H120L273 98L409 481H510L275 -140Q263 -172 247 -183T214 -195ZM206 547Q204 550 213 565T240 602T279 645T326 684Q352 701 374 701Q393 701 406 690T419 662Q419 645 409 630T377 601Q350 583 321 572T267 553T225 545T206 547Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="623" d="M340 -13Q282 -13 240 12T175 71V-149Q175 -176 163 -186T131 -196H120Q100 -196 88 -186T76 -149V676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602V402Q204 444 246 468T343 493Q391 493 432 475T504 424T551 345T568 241Q568 184 551 137T504 57T432 5T340 -13ZM322 69Q390 69 431 116T472 240Q472 277 461 308T431 363T384 398T324 411Q269 411 232 385T175 314V192Q178 165 191 143T224 104T269 78T322 69Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="528" d="M214 -195Q203 -195 194 -192T177 -184T165 -175T162 -168L242 27Q225 29 216 34T200 55L18 481H120L273 98L409 481H510L275 -140Q263 -172 247 -183T214 -195ZM361 546Q332 546 316 561T300 605Q300 634 316 649T359 665Q386 665 402 650T419 605Q419 577 403 562T361 546ZM177 546Q150 546 134 561T117 605Q117 634 133 649T175 665Q204 665 220 650T236 605Q236 577 220 562T177 546Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM222 727Q172 727 172 766V772Q172 790 185 799T222 809H436Q486 809 486 772V766Q486 727 436 727H222Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM178 562Q128 562 128 601V607Q128 625 141 634T178 644H392Q442 644 442 607V601Q442 562 392 562H178Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H531L457 190ZM223 273H431L327 547L223 273ZM330 711Q305 711 279 722T230 749T193 785T178 822Q178 839 188 849T216 860Q225 860 238 852T268 830T300 801T332 769Q346 785 361 801T392 830T421 851T444 860Q462 860 472 850T482 822Q482 804 467 785T430 749T381 722T330 711Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60ZM283 546Q258 546 232 557T183 584T146 620T131 657Q131 674 141 684T169 695Q178 695 191 687T221 665T253 636T285 604Q299 620 314 636T345 665T374 686T397 695Q415 695 425 685T435 657Q435 639 420 620T383 584T334 557T283 546Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="660" d="M457 190H198L125 0H20L258 620Q264 636 279 644T329 653Q365 653 379 645T400 620L642 0H606Q568 -31 555 -53T541 -92Q541 -108 550 -117T574 -127Q599 -127 611 -107Q623 -108 633 -118T644 -145Q644 -168 624 -182T566 -196Q520 -196 493 -174T465 -111Q465 -82 484 -53T542 0H531L457 190ZM223 273H431L327 547L223 273Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="553" d="M212 -11Q134 -11 91 26T48 126Q48 210 127 248T371 289H384V299Q384 354 354 383T265 412Q210 412 177 388T127 330Q127 330 118 330T97 335T76 350T66 378Q66 395 79 415T118 452T182 481T272 493Q375 493 428 443T482 301V0H475Q432 -34 417 -57T401 -99Q401 -115 410 -124T434 -134Q459 -134 471 -114Q483 -115 493 -125T504 -152Q504 -175 484 -189T426 -203Q380 -203 353 -181T325 -118Q325 -86 347 -55T413 0H385V14V72Q357 33 312 11T212 -11ZM236 60Q268 60 295 69T342 96T373 137T384 190V227L357 226Q251 222 198 201T145 130Q145 98 169 79T236 60Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="687" d="M385 -13Q313 -13 253 11T150 80T83 186T59 322Q59 396 83 458T150 565T254 635T388 660Q449 660 494 645T570 609T616 564T632 523Q632 505 623 494T603 476T583 467T572 467Q544 516 494 541T389 567Q340 567 299 550T229 500T183 423T166 324Q166 270 183 226T230 149T301 98T391 80Q457 80 507 110T578 193Q579 195 589 194T611 186T632 168T642 138Q642 120 626 94T578 45T498 4T385 -13ZM324 720Q322 723 331 738T358 775T397 818T444 857Q470 874 492 874Q511 874 524 863T537 835Q537 818 527 803T495 774Q468 756 439 745T385 726T343 718T324 720Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="546" d="M297 -13Q243 -13 197 6T118 58T66 138T47 241Q47 296 66 342T118 421T197 474T297 493Q351 493 389 479T453 444T491 400T503 360Q503 345 494 336T475 321T455 315T444 316Q427 356 394 383T299 410Q265 410 236 398T186 363T153 309T141 241Q141 204 153 173T186 119T236 83T299 70Q360 70 393 97T444 164Q445 166 454 165T474 160T494 145T503 119Q503 102 491 80T454 37T389 2T297 -13ZM233 555Q231 558 240 573T267 610T306 653T353 692Q379 709 401 709Q420 709 433 698T446 670Q446 653 436 638T404 609Q377 591 348 580T294 561T252 553T233 555Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="687" d="M385 -13Q313 -13 253 11T150 80T83 186T59 322Q59 396 83 458T150 565T254 635T388 660Q449 660 494 645T570 609T616 564T632 523Q632 505 623 494T603 476T583 467T572 467Q544 516 494 541T389 567Q340 567 299 550T229 500T183 423T166 324Q166 270 183 226T230 149T301 98T391 80Q457 80 507 110T578 193Q579 195 589 194T611 186T632 168T642 138Q642 120 626 94T578 45T498 4T385 -13ZM266 712Q252 712 243 721T234 744Q234 764 253 789T296 837T343 875T374 893Q381 891 404 876T451 838T494 790T513 744Q513 730 505 721T482 712Q461 712 436 737T373 819Q335 762 311 737T266 712Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="546" d="M297 -13Q243 -13 197 6T118 58T66 138T47 241Q47 296 66 342T118 421T197 474T297 493Q351 493 389 479T453 444T491 400T503 360Q503 345 494 336T475 321T455 315T444 316Q427 356 394 383T299 410Q265 410 236 398T186 363T153 309T141 241Q141 204 153 173T186 119T236 83T299 70Q360 70 393 97T444 164Q445 166 454 165T474 160T494 145T503 119Q503 102 491 80T454 37T389 2T297 -13ZM195 547Q181 547 172 556T163 579Q163 599 182 624T225 672T272 710T303 728Q310 726 333 711T380 673T423 625T442 579Q442 565 434 556T411 547Q390 547 365 572T302 654Q264 597 240 572T195 547Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="687" d="M385 -13Q313 -13 253 11T150 80T83 186T59 322Q59 396 83 458T150 565T254 635T388 660Q449 660 494 645T570 609T616 564T632 523Q632 505 623 494T603 476T583 467T572 467Q544 516 494 541T389 567Q340 567 299 550T229 500T183 423T166 324Q166 270 183 226T230 149T301 98T391 80Q457 80 507 110T578 193Q579 195 589 194T611 186T632 168T642 138Q642 120 626 94T578 45T498 4T385 -13ZM383 712Q352 712 335 729T317 774Q317 801 335 819T384 837Q414 837 431 819T449 774Q449 747 432 730T383 712Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="546" d="M297 -13Q243 -13 197 6T118 58T66 138T47 241Q47 296 66 342T118 421T197 474T297 493Q351 493 389 479T453 444T491 400T503 360Q503 345 494 336T475 321T455 315T444 316Q427 356 394 383T299 410Q265 410 236 398T186 363T153 309T141 241Q141 204 153 173T186 119T236 83T299 70Q360 70 393 97T444 164Q445 166 454 165T474 160T494 145T503 119Q503 102 491 80T454 37T389 2T297 -13ZM306 547Q275 547 258 564T240 609Q240 636 258 654T307 672Q337 672 354 654T372 609Q372 582 355 565T306 547Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="687" d="M385 -13Q313 -13 253 11T150 80T83 186T59 322Q59 396 83 458T150 565T254 635T388 660Q449 660 494 645T570 609T616 564T632 523Q632 505 623 494T603 476T583 467T572 467Q544 516 494 541T389 567Q340 567 299 550T229 500T183 423T166 324Q166 270 183 226T230 149T301 98T391 80Q457 80 507 110T578 193Q579 195 589 194T611 186T632 168T642 138Q642 120 626 94T578 45T498 4T385 -13ZM494 879Q508 879 516 870T525 846Q525 826 506 801T463 754T416 715T386 697Q378 698 356 714T309 753T266 801T247 846Q247 860 255 869T278 879Q299 879 323 854T386 771Q424 828 448 853T494 879Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="546" d="M297 -13Q243 -13 197 6T118 58T66 138T47 241Q47 296 66 342T118 421T197 474T297 493Q351 493 389 479T453 444T491 400T503 360Q503 345 494 336T475 321T455 315T444 316Q427 356 394 383T299 410Q265 410 236 398T186 363T153 309T141 241Q141 204 153 173T186 119T236 83T299 70Q360 70 393 97T444 164Q445 166 454 165T474 160T494 145T503 119Q503 102 491 80T454 37T389 2T297 -13ZM410 714Q424 714 432 705T441 681Q441 661 422 636T379 589T332 550T302 532Q294 533 272 549T225 588T182 636T163 681Q163 695 171 704T194 714Q215 714 239 689T302 606Q340 663 364 688T410 714Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="707" d="M84 589Q84 615 100 631T139 647H297Q379 647 444 625T555 562T624 461T648 325Q648 249 624 189T553 87T441 23T292 0H84V589ZM294 89Q413 89 478 150T544 324Q544 435 479 496T295 558H186V89H294ZM432 879Q446 879 454 870T463 846Q463 826 444 801T401 754T354 715T324 697Q316 698 294 714T247 753T204 801T185 846Q185 860 193 869T216 879Q237 879 261 854T324 771Q362 828 386 853T432 879Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="804" d="M272 -13Q224 -13 183 5T111 56T64 136T47 240Q47 297 64 343T111 423T183 475T275 493Q334 493 376 470T440 414V676Q440 678 451 682T476 687Q488 687 499 684T519 671T533 645T539 602V0H441V80Q412 36 371 12T272 -13ZM291 69Q321 69 348 79T396 108T429 153T441 210V312Q422 358 386 384T293 411Q225 411 184 365T143 240Q143 202 154 171T184 117T231 82T291 69ZM665 452Q660 445 650 444T631 445T617 455T615 474L668 644Q676 668 685 679T720 691H729Q748 691 759 679T771 644V640Q771 610 748 579L665 452Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="751" d="M67 284Q45 284 37 294T28 320V329Q28 344 36 354T67 364H120V589Q120 615 136 631T175 647H337Q419 647 485 625T597 561T667 460T692 325Q692 250 667 190T595 87T482 23T332 0H177Q153 0 137 17T120 57V284H67ZM338 91Q459 91 524 151T589 324Q589 434 524 495T339 556H222V364H397Q419 364 428 354T437 329V320Q437 304 428 294T397 284H222V91H338Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="615" d="M272 -13Q224 -13 183 5T111 56T64 136T47 240Q47 297 64 343T111 423T183 475T275 493Q334 493 376 470T440 414V538H357Q318 538 318 566V573Q318 602 357 602H440V676Q440 678 451 682T476 687Q488 687 499 684T519 671T533 645T539 602H576Q615 602 615 573V566Q615 552 605 545T576 538H539V0H441V80Q412 36 371 12T272 -13ZM291 69Q321 69 348 79T396 108T429 153T441 210V312Q422 358 386 384T293 411Q225 411 184 365T143 240Q143 202 154 171T184 117T231 82T291 69Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM215 727Q165 727 165 766V772Q165 790 178 799T215 809H429Q479 809 479 772V766Q479 727 429 727H215Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM185 562Q135 562 135 601V607Q135 625 148 634T185 644H399Q449 644 449 607V601Q449 562 399 562H185Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM324 711Q299 711 273 722T224 749T187 785T172 822Q172 839 182 849T210 860Q219 860 232 852T262 830T294 801T326 769Q340 785 355 801T386 830T415 851T438 860Q456 860 466 850T476 822Q476 804 461 785T424 749T375 722T324 711Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM293 546Q268 546 242 557T193 584T156 620T141 657Q141 674 151 684T179 695Q188 695 201 687T231 665T263 636T295 604Q309 620 324 636T355 665T384 686T407 695Q425 695 435 685T445 657Q445 639 430 620T393 584T344 557T293 546Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM330 712Q299 712 282 729T264 774Q264 801 282 819T331 837Q361 837 378 819T396 774Q396 747 379 730T330 712Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM288 547Q257 547 240 564T222 609Q222 636 240 654T289 672Q319 672 336 654T354 609Q354 582 337 565T288 547Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H541Q503 -32 489 -53T475 -93Q475 -109 484 -118T508 -128Q533 -128 545 -108Q557 -109 567 -119T578 -146Q578 -169 558 -183T500 -197Q454 -197 427 -175T399 -112Q399 -82 418 -53T477 0H84V589Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 77 487 55T433 14Q392 -18 378 -40T363 -81Q363 -97 372 -106T396 -116Q421 -116 433 -96Q445 -97 455 -107T466 -134Q466 -157 446 -171T388 -185Q342 -185 315 -163T287 -100Q287 -77 298 -54T333 -11Q325 -12 317 -12T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="617" d="M84 589Q84 615 99 631T139 647H562V555H187V374H488V287H187V91H567V0H84V589ZM434 879Q448 879 456 870T465 846Q465 826 446 801T403 754T356 715T326 697Q318 698 296 714T249 753T206 801T187 846Q187 860 195 869T218 879Q239 879 263 854T326 771Q364 828 388 853T434 879Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="570" d="M301 -13Q244 -13 197 5T117 56T66 135T47 238Q47 293 65 339T117 420T195 473T293 493Q345 493 387 476T460 426T507 350T524 251V243Q524 227 518 221T494 214H139Q142 181 155 154T189 107T240 76T303 65Q334 65 358 71T401 89T432 114T454 145Q455 146 463 144T480 137T498 122T506 96Q506 80 493 61T453 26T388 -2T301 -13ZM292 418Q262 418 237 408T191 380T158 336T140 281H428Q424 344 388 381T292 418ZM396 714Q410 714 418 705T427 681Q427 661 408 636T365 589T318 550T288 532Q280 533 258 549T211 588T168 636T149 681Q149 695 157 704T180 714Q201 714 225 689T288 606Q326 663 350 688T396 714Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="742" d="M636 -4Q625 -4 615 -1T597 12T585 37T580 79V91Q550 43 495 16T365 -12Q295 -12 239 13T142 82T81 188T59 322Q59 396 83 458T151 565T256 635T391 660Q450 660 495 646T572 612T620 568T636 526Q636 508 628 497T609 480T590 473T580 473Q550 521 499 546T393 571Q343 571 301 553T229 501T182 422T165 321Q165 267 180 221T223 142T293 90T385 71Q424 71 457 81T516 111T558 157T579 216V257H375V341H615Q643 341 656 325T670 282V6Q670 4 660 0T636 -4ZM270 712Q256 712 247 721T238 744Q238 764 257 789T300 837T347 875T378 893Q385 891 408 876T455 838T498 790T517 744Q517 730 509 721T486 712Q465 712 440 737T377 819Q339 762 315 737T270 712Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="615" d="M295 -199Q242 -199 202 -187T134 -157T93 -118T79 -79Q79 -64 86 -54T101 -37T116 -29T125 -28Q148 -66 187 -90T292 -115Q440 -115 440 33V96Q411 54 370 32T271 9Q221 9 180 26T110 74T64 149T47 250Q47 303 64 347T112 424T184 475T275 493Q333 493 376 471T442 417V481H539V35Q539 -84 477 -141T295 -199ZM291 88Q320 88 346 97T393 124T426 166T441 219V318Q421 362 384 387T293 412Q259 412 232 400T185 367T154 315T143 250Q143 176 183 132T291 88ZM192 547Q178 547 169 556T160 579Q160 599 179 624T222 672T269 710T300 728Q307 726 330 711T377 673T420 625T439 579Q439 565 431 556T408 547Q387 547 362 572T299 654Q261 597 237 572T192 547Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="742" d="M636 -4Q625 -4 615 -1T597 12T585 37T580 79V91Q550 43 495 16T365 -12Q295 -12 239 13T142 82T81 188T59 322Q59 396 83 458T151 565T256 635T391 660Q450 660 495 646T572 612T620 568T636 526Q636 508 628 497T609 480T590 473T580 473Q550 521 499 546T393 571Q343 571 301 553T229 501T182 422T165 321Q165 267 180 221T223 142T293 90T385 71Q424 71 457 81T516 111T558 157T579 216V257H375V341H615Q643 341 656 325T670 282V6Q670 4 660 0T636 -4ZM378 711Q353 711 327 722T278 749T241 785T226 822Q226 839 236 849T264 860Q273 860 286 852T316 830T348 801T380 769Q394 785 409 801T440 830T469 851T492 860Q510 860 520 850T530 822Q530 804 515 785T478 749T429 722T378 711Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="615" d="M295 -199Q242 -199 202 -187T134 -157T93 -118T79 -79Q79 -64 86 -54T101 -37T116 -29T125 -28Q148 -66 187 -90T292 -115Q440 -115 440 33V96Q411 54 370 32T271 9Q221 9 180 26T110 74T64 149T47 250Q47 303 64 347T112 424T184 475T275 493Q333 493 376 471T442 417V481H539V35Q539 -84 477 -141T295 -199ZM291 88Q320 88 346 97T393 124T426 166T441 219V318Q421 362 384 387T293 412Q259 412 232 400T185 367T154 315T143 250Q143 176 183 132T291 88ZM301 546Q276 546 250 557T201 584T164 620T149 657Q149 674 159 684T187 695Q196 695 209 687T239 665T271 636T303 604Q317 620 332 636T363 665T392 686T415 695Q433 695 443 685T453 657Q453 639 438 620T401 584T352 557T301 546Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="742" d="M636 -4Q625 -4 615 -1T597 12T585 37T580 79V91Q550 43 495 16T365 -12Q295 -12 239 13T142 82T81 188T59 322Q59 396 83 458T151 565T256 635T391 660Q450 660 495 646T572 612T620 568T636 526Q636 508 628 497T609 480T590 473T580 473Q550 521 499 546T393 571Q343 571 301 553T229 501T182 422T165 321Q165 267 180 221T223 142T293 90T385 71Q424 71 457 81T516 111T558 157T579 216V257H375V341H615Q643 341 656 325T670 282V6Q670 4 660 0T636 -4ZM382 712Q351 712 334 729T316 774Q316 801 334 819T383 837Q413 837 430 819T448 774Q448 747 431 730T382 712Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="615" d="M295 -199Q242 -199 202 -187T134 -157T93 -118T79 -79Q79 -64 86 -54T101 -37T116 -29T125 -28Q148 -66 187 -90T292 -115Q440 -115 440 33V96Q411 54 370 32T271 9Q221 9 180 26T110 74T64 149T47 250Q47 303 64 347T112 424T184 475T275 493Q333 493 376 471T442 417V481H539V35Q539 -84 477 -141T295 -199ZM291 88Q320 88 346 97T393 124T426 166T441 219V318Q421 362 384 387T293 412Q259 412 232 400T185 367T154 315T143 250Q143 176 183 132T291 88ZM299 547Q268 547 251 564T233 609Q233 636 251 654T300 672Q330 672 347 654T365 609Q365 582 348 565T299 547Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="742" d="M636 -4Q625 -4 615 -1T597 12T585 37T580 79V91Q550 43 495 16T365 -12Q295 -12 239 13T142 82T81 188T59 322Q59 396 83 458T151 565T256 635T391 660Q450 660 495 646T572 612T620 568T636 526Q636 508 628 497T609 480T590 473T580 473Q550 521 499 546T393 571Q343 571 301 553T229 501T182 422T165 321Q165 267 180 221T223 142T293 90T385 71Q424 71 457 81T516 111T558 157T579 216V257H375V341H615Q643 341 656 325T670 282V6Q670 4 660 0T636 -4ZM337 -242Q331 -248 322 -249T303 -247T289 -237T287 -218L340 -91Q348 -69 357 -57T393 -45H402Q422 -45 435 -56T448 -87V-92Q448 -105 440 -117T416 -146L337 -242Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="615" d="M295 -199Q242 -199 202 -187T134 -157T93 -118T79 -79Q79 -64 86 -54T101 -37T116 -29T125 -28Q148 -66 187 -90T292 -115Q440 -115 440 33V96Q411 54 370 32T271 9Q221 9 180 26T110 74T64 149T47 250Q47 303 64 347T112 424T184 475T275 493Q333 493 376 471T442 417V481H539V35Q539 -84 477 -141T295 -199ZM291 88Q320 88 346 97T393 124T426 166T441 219V318Q421 362 384 387T293 412Q259 412 232 400T185 367T154 315T143 250Q143 176 183 132T291 88ZM317 719Q323 725 332 726T351 724T365 714T367 695L314 568Q306 546 297 534T261 522H252Q232 522 219 533T206 564V569Q206 582 214 594T238 623L317 719Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="715" d="M84 604Q84 651 127 651H142Q187 651 187 604V369H528V604Q528 651 571 651H586Q631 651 631 604V0H528V279H187V0H84V604ZM249 712Q235 712 226 721T217 744Q217 764 236 789T279 837T326 875T357 893Q364 891 387 876T434 838T477 790T496 744Q496 730 488 721T465 712Q444 712 419 737T356 819Q318 762 294 737T249 712Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="591" d="M175 405Q199 443 238 468T334 493Q382 493 417 478T475 434T509 368T520 285V0H421V274Q421 337 392 371T310 406Q248 406 212 369T175 265V0H76V676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602V405ZM11 718Q-3 718 -12 727T-21 750Q-21 770 -2 795T41 843T88 881T119 899Q126 897 149 882T196 844T239 796T258 750Q258 736 250 727T227 718Q206 718 181 743T118 825Q80 768 56 743T11 718Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="715" d="M39 466Q17 471 17 492V499Q17 518 39 523V604Q39 651 82 651H97Q142 651 142 604V525H483V604Q483 651 526 651H541Q586 651 586 604V525H659Q698 525 698 499V492Q698 477 688 471T659 465H586V0H483V279H142V0H39V466ZM483 369V465H142V369H483Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="591" d="M258 602Q297 602 297 574V567Q297 538 258 538H175V405Q199 443 238 468T334 493Q382 493 417 478T475 434T509 368T520 285V0H421V274Q421 337 392 371T310 406Q248 406 212 369T175 265V0H76V538H39Q0 538 0 567V574Q0 588 10 595T39 602H76V676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602H258Z" />
+<glyph unicode="&#x128;" glyph-name="Itilde" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM26 677Q9 677 0 686T-9 712Q-9 731 -3 748T13 779T38 800T69 808Q96 808 134 789T213 733Q221 759 234 780T264 802Q281 802 290 793T299 766Q299 747 293 730T277 700T252 679T221 671Q193 671 155 690T77 746Q68 720 56 699T26 677Z" />
+<glyph unicode="&#x129;" glyph-name="itilde" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM11 552Q-6 552 -15 561T-24 587Q-24 606 -18 623T-2 654T23 675T54 683Q81 683 119 664T198 608Q206 634 219 655T249 677Q266 677 275 668T284 641Q284 622 278 605T262 575T237 554T206 546Q178 546 140 565T62 621Q53 595 41 574T11 552Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM38 727Q-12 727 -12 766V772Q-12 790 1 799T38 809H252Q302 809 302 772V766Q302 727 252 727H38Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM23 562Q-27 562 -27 601V607Q-27 625 -14 634T23 644H237Q287 644 287 607V601Q287 562 237 562H23Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="289" d="M93 608Q93 626 103 638T131 651H155Q174 651 185 640T196 608V0H175Q133 -33 119 -56T104 -97Q104 -113 113 -122T137 -132Q162 -132 174 -112Q186 -113 196 -123T207 -150Q207 -173 187 -187T129 -201Q83 -201 56 -179T28 -116Q28 -85 49 -55T113 0H93V608Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H159Q117 -33 103 -56T88 -97Q88 -113 97 -122T121 -132Q146 -132 158 -112Q170 -113 180 -123T191 -150Q191 -173 171 -187T113 -201Q67 -201 40 -179T12 -116Q12 -85 33 -55T97 0H80V438ZM130 584Q102 584 87 598T71 639Q71 666 86 680T131 695Q189 695 189 639Q189 584 130 584Z" />
+<glyph unicode="&#x130;" glyph-name="Idotaccent" horiz-adv-x="289" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM145 712Q114 712 97 729T79 774Q79 801 97 819T146 837Q176 837 193 819T211 774Q211 747 194 730T145 712Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="260" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438Z" />
+<glyph unicode="&#x132;" glyph-name="IJ" horiz-adv-x="783" d="M93 604Q93 651 136 651H151Q196 651 196 604V0H93V604ZM499 -13Q460 -13 428 -4T374 21T339 54T327 91Q327 106 334 117T349 135T365 145T374 147Q391 115 421 96T491 76Q551 76 576 108T602 219V647H705V218Q705 100 655 44T499 -13Z" />
+<glyph unicode="&#x133;" glyph-name="ij" horiz-adv-x="520" d="M80 438Q80 465 92 475T125 486H135Q155 486 167 476T179 438V0H80V438ZM130 584Q102 584 87 598T71 639Q71 666 86 680T131 695Q189 695 189 639Q189 584 130 584ZM295 -200Q247 -200 220 -182T193 -136Q193 -126 196 -117T204 -102T211 -93T216 -90Q227 -101 243 -109T280 -118Q312 -118 326 -100T340 -41V481H439V-47Q439 -121 403 -160T295 -200ZM390 584Q362 584 347 598T331 639Q331 666 346 680T391 695Q449 695 449 639Q449 584 390 584Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="494" d="M210 -13Q171 -13 139 -4T85 21T50 54T38 91Q38 106 45 117T60 135T76 145T85 147Q102 115 132 96T202 76Q262 76 287 108T313 219V647H416V218Q416 100 366 44T210 -13ZM251 712Q237 712 228 721T219 744Q219 764 238 789T281 837T328 875T359 893Q366 891 389 876T436 838T479 790T498 744Q498 730 490 721T467 712Q446 712 421 737T358 819Q320 762 296 737T251 712Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="260" d="M35 -200Q-13 -200 -40 -182T-67 -136Q-67 -126 -64 -117T-56 -102T-49 -93T-44 -90Q-33 -101 -17 -109T20 -118Q52 -118 66 -100T80 -41V481H179V-47Q179 -121 143 -160T35 -200ZM22 547Q8 547 -1 556T-10 579Q-10 599 9 624T52 672T99 710T130 728Q137 726 160 711T207 673T250 625T269 579Q269 565 261 556T238 547Q217 547 192 572T129 654Q91 597 67 572T22 547Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="612" d="M84 604Q84 651 127 651H142Q187 651 187 604V344L446 618Q463 638 476 644T503 651Q519 651 530 645T548 630T557 614T559 605L301 331L566 41Q567 39 565 32T555 17T536 3T507 -4Q490 -4 477 2T451 25L187 313V0H84V604ZM247 -242Q241 -248 232 -249T213 -247T199 -237T197 -218L250 -91Q258 -69 267 -57T303 -45H312Q332 -45 345 -56T358 -87V-92Q358 -105 350 -117T326 -146L247 -242Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="504" d="M76 676Q76 678 86 682T112 687Q123 687 134 684T155 671T169 645T175 602V265L342 452Q357 471 372 478T401 486Q414 486 424 481T441 469T450 456T452 447L277 254L479 35Q480 33 477 27T466 13T449 1T425 -5Q411 -5 397 2T366 29L175 235V0H76V676ZM210 -242Q204 -248 195 -249T176 -247T162 -237T160 -218L213 -91Q221 -69 230 -57T266 -45H275Q295 -45 308 -56T321 -87V-92Q321 -105 313 -117T289 -146L210 -242Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="558" d="M84 604Q84 651 127 651H142Q187 651 187 604V93H468Q516 93 516 49V45Q516 0 468 0H84V604ZM106 720Q104 723 113 738T140 775T179 818T226 857Q252 874 274 874Q293 874 306 863T319 835Q319 818 309 803T277 774Q250 756 221 745T167 726T125 718T106 720Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="260" d="M80 676Q80 678 91 682T116 687Q128 687 139 684T159 671T173 645T179 602V0H80V676ZM100 731Q98 734 107 749T134 786T173 829T220 868Q246 885 268 885Q287 885 300 874T313 846Q313 829 303 814T271 785Q244 767 215 756T161 737T119 729T100 731Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="558" d="M84 604Q84 651 127 651H142Q187 651 187 604V93H468Q516 93 516 49V45Q516 0 468 0H84V604ZM252 -242Q246 -248 237 -249T218 -247T204 -237T202 -218L255 -91Q263 -69 272 -57T308 -45H317Q337 -45 350 -56T363 -87V-92Q363 -105 355 -117T331 -146L252 -242Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="260" d="M80 676Q80 678 91 682T116 687Q128 687 139 684T159 671T173 645T179 602V0H80V676ZM77 -242Q71 -248 62 -249T43 -247T29 -237T27 -218L80 -91Q88 -69 97 -57T133 -45H142Q162 -45 175 -56T188 -87V-92Q188 -105 180 -117T156 -146L77 -242Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="615" d="M499 413Q494 406 484 405T465 406T451 416T449 435L502 605Q510 629 519 640T554 652H563Q582 652 593 640T605 605V601Q605 571 582 540L499 413ZM84 604Q84 651 127 651H142Q187 651 187 604V93H468Q516 93 516 49V45Q516 0 468 0H84V604Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="444" d="M306 450Q301 443 291 442T272 443T258 453T256 472L309 642Q317 666 326 677T361 689H370Q389 689 400 677T412 642V638Q412 608 389 577L306 450ZM80 676Q80 678 91 682T116 687Q128 687 139 684T159 671T173 645T179 602V0H80V676Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="655" d="M84 604Q84 651 127 651H142Q187 651 187 604V93H468Q516 93 516 49V45Q516 0 468 0H84V604ZM568 266Q514 266 514 319V331Q514 386 568 386H582Q637 386 637 331V319Q637 266 582 266H568Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="387" d="M80 676Q80 678 91 682T116 687Q128 687 139 684T159 671T173 645T179 602V0H80V676ZM297 250Q243 250 243 303V315Q243 370 297 370H311Q366 370 366 315V303Q366 250 311 250H297Z" />
+<glyph unicode="&#x141;" glyph-name="CR" horiz-adv-x="578" d="M49 203Q45 201 37 214T29 245Q29 262 40 276T88 301L120 311V604Q120 651 163 651H178Q223 651 223 604V342L405 398Q410 399 417 386T425 356Q425 339 413 325T366 300L223 256V92H496Q544 92 544 48V44Q544 0 496 0H175Q150 0 135 15T120 57V224L49 203Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="366" d="M48 208Q44 207 36 221T28 251Q28 267 39 280T86 305L131 318V676Q131 678 142 682T167 687Q179 687 190 684T210 671T224 645T230 602V349L319 376Q323 377 331 363T339 333Q339 317 327 304T280 279L230 263V42Q230 15 218 5T186 -5H176Q156 -5 144 5T131 42V233L48 208Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="712" d="M84 593Q84 619 96 635T133 651H150Q173 651 185 642T212 612L532 158V647H629V54Q629 27 617 12T581 -4H573Q550 -4 540 3T518 27L181 504V0H84V593ZM315 720Q313 723 322 738T349 775T388 818T435 857Q461 874 483 874Q502 874 515 863T528 835Q528 818 518 803T486 774Q459 756 430 745T376 726T334 718T315 720Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="591" d="M112 486Q134 486 153 471T174 408Q199 445 238 469T334 493Q382 493 417 478T475 435T509 370T520 287V0H421V275Q421 336 392 371T310 406Q262 406 228 383T175 322V0H76V475Q76 477 86 481T112 486ZM244 555Q242 558 251 573T278 610T317 653T364 692Q390 709 412 709Q431 709 444 698T457 670Q457 653 447 638T415 609Q388 591 359 580T305 561T263 553T244 555Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="712" d="M84 593Q84 619 96 635T133 651H150Q173 651 185 642T212 612L532 158V647H629V54Q629 27 617 12T581 -4H573Q550 -4 540 3T518 27L181 504V0H84V593ZM307 -242Q301 -248 292 -249T273 -247T259 -237T257 -218L310 -91Q318 -69 327 -57T363 -45H372Q392 -45 405 -56T418 -87V-92Q418 -105 410 -117T386 -146L307 -242Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="591" d="M112 486Q134 486 153 471T174 408Q199 445 238 469T334 493Q382 493 417 478T475 435T509 370T520 287V0H421V275Q421 336 392 371T310 406Q262 406 228 383T175 322V0H76V475Q76 477 86 481T112 486ZM242 -242Q236 -248 227 -249T208 -247T194 -237T192 -218L245 -91Q253 -69 262 -57T298 -45H307Q327 -45 340 -56T353 -87V-92Q353 -105 345 -117T321 -146L242 -242Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="712" d="M84 593Q84 619 96 635T133 651H150Q173 651 185 642T212 612L532 158V647H629V54Q629 27 617 12T581 -4H573Q550 -4 540 3T518 27L181 504V0H84V593ZM466 879Q480 879 488 870T497 846Q497 826 478 801T435 754T388 715T358 697Q350 698 328 714T281 753T238 801T219 846Q219 860 227 869T250 879Q271 879 295 854T358 771Q396 828 420 853T466 879Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="591" d="M112 486Q134 486 153 471T174 408Q199 445 238 469T334 493Q382 493 417 478T475 435T509 370T520 287V0H421V275Q421 336 392 371T310 406Q262 406 228 383T175 322V0H76V475Q76 477 86 481T112 486ZM404 714Q418 714 426 705T435 681Q435 661 416 636T373 589T326 550T296 532Q288 533 266 549T219 588T176 636T157 681Q157 695 165 704T188 714Q209 714 233 689T296 606Q334 663 358 688T404 714Z" />
+<glyph unicode="&#x149;" glyph-name="napostrophe" horiz-adv-x="837" d="M89 421Q84 414 74 413T55 414T41 424T39 443L92 613Q100 637 109 648T144 660H153Q172 660 183 648T195 613V609Q195 579 172 548L89 421ZM358 486Q380 486 399 471T420 408Q445 445 484 469T580 493Q628 493 663 478T721 435T755 370T766 287V0H667V275Q667 336 638 371T556 406Q508 406 474 383T421 322V0H322V475Q322 477 332 481T358 486Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM292 727Q242 727 242 766V772Q242 790 255 799T292 809H506Q556 809 556 772V766Q556 727 506 727H292Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM201 562Q151 562 151 601V607Q151 625 164 634T201 644H415Q465 644 465 607V601Q465 562 415 562H201Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM391 711Q366 711 340 722T291 749T254 785T239 822Q239 839 249 849T277 860Q286 860 299 852T329 830T361 801T393 769Q407 785 422 801T453 830T482 851T505 860Q523 860 533 850T543 822Q543 804 528 785T491 749T442 722T391 711Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM307 546Q282 546 256 557T207 584T170 620T155 657Q155 674 165 684T193 695Q202 695 215 687T245 665T277 636T309 604Q323 620 338 636T369 665T398 686T421 695Q439 695 449 685T459 657Q459 639 444 620T407 584T358 557T307 546Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="771" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q458 660 518 636T622 567T689 460T713 324Q713 250 689 188T621 82T517 12T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78ZM433 720Q431 723 440 738T467 775T506 818T553 857Q579 874 601 874Q620 874 633 863T646 835Q646 818 636 803T604 774Q577 756 548 745T494 726T452 718T433 720ZM236 720Q234 723 243 738T269 775T309 818T356 857Q382 874 404 874Q423 874 436 863T449 835Q449 818 439 803T407 774Q380 756 351 745T297 726T255 718T236 720Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="598" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q355 493 401 474T480 422T532 341T551 239Q551 184 533 138T481 58T401 6T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM316 555Q314 558 323 573T350 610T389 653T436 692Q462 709 484 709Q503 709 516 698T529 670Q529 653 519 638T487 609Q460 591 431 580T377 561T335 553T316 555ZM119 555Q117 558 126 573T152 610T192 653T239 692Q265 709 287 709Q306 709 319 698T332 670Q332 653 322 638T290 609Q263 591 234 580T180 561T138 553T119 555Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1061" d="M385 -13Q313 -13 253 11T149 80T82 186T58 322Q58 396 82 458T150 565T254 635T386 660Q412 660 436 657T483 647H958Q1006 647 1006 604V598Q1006 555 958 555H621Q658 523 680 478T708 374H884Q932 374 932 333V330Q932 287 884 287H709Q706 225 683 176T620 91H964Q1011 91 1011 50V45Q1011 0 964 0H483Q460 -6 436 -9T385 -13ZM386 78Q435 78 476 96T546 146T592 223T609 322Q609 376 593 421T546 499T475 550T385 569Q336 569 295 551T225 501T179 424T162 324Q162 270 178 225T225 147T296 96T386 78Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="982" d="M299 -13Q243 -13 197 5T117 58T66 138T47 241Q47 296 65 342T117 422T197 474T300 493Q368 493 420 463T503 382Q534 434 586 463T705 493Q757 493 799 476T872 426T919 350T936 251V243Q936 227 930 221T906 214H551Q554 181 567 154T601 107T652 76T715 65Q746 65 770 71T813 89T844 114T866 145Q867 146 875 144T892 137T910 122T918 96Q918 80 905 61T865 26T800 -2T713 -13Q639 -13 585 15T500 95Q469 45 418 16T299 -13ZM299 68Q333 68 362 81T413 117T447 171T459 239Q459 276 447 308T414 363T364 399T299 412Q264 412 235 399T184 364T150 310T138 241Q138 204 150 173T184 118T235 81T299 68ZM704 418Q674 418 649 408T603 380T570 336T552 281H840Q836 344 800 381T704 418Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="619" d="M501 -4Q481 -4 466 5T434 36L257 253H186V0H84V589Q84 615 99 631T139 647H323Q442 647 503 599T565 453Q565 370 516 321T372 263L561 38Q563 36 560 29T549 15T529 2T501 -4ZM185 334H321Q393 334 427 364T461 448Q461 562 318 562H185V334ZM239 720Q237 723 246 738T273 775T312 818T359 857Q385 874 407 874Q426 874 439 863T452 835Q452 818 442 803T410 774Q383 756 354 745T300 726T258 718T239 720Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="402" d="M112 486Q123 486 134 483T153 470T168 444T174 402Q206 493 296 493Q334 493 354 476T375 429Q375 415 371 405T361 389T351 380T345 378Q334 387 319 393T284 399Q255 399 234 386T200 347T181 288T175 212V0H76V475Q76 477 86 481T112 486ZM156 555Q154 558 163 573T190 610T229 653T276 692Q302 709 324 709Q343 709 356 698T369 670Q369 653 359 638T327 609Q300 591 271 580T217 561T175 553T156 555Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="619" d="M501 -4Q481 -4 466 5T434 36L257 253H186V0H84V589Q84 615 99 631T139 647H323Q442 647 503 599T565 453Q565 370 516 321T372 263L561 38Q563 36 560 29T549 15T529 2T501 -4ZM185 334H321Q393 334 427 364T461 448Q461 562 318 562H185V334ZM257 -242Q251 -248 242 -249T223 -247T209 -237T207 -218L260 -91Q268 -69 277 -57T313 -45H322Q342 -45 355 -56T368 -87V-92Q368 -105 360 -117T336 -146L257 -242Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="402" d="M112 486Q123 486 134 483T153 470T168 444T174 402Q206 493 296 493Q334 493 354 476T375 429Q375 415 371 405T361 389T351 380T345 378Q334 387 319 393T284 399Q255 399 234 386T200 347T181 288T175 212V0H76V475Q76 477 86 481T112 486ZM66 -242Q60 -248 51 -249T32 -247T18 -237T16 -218L69 -91Q77 -69 86 -57T122 -45H131Q151 -45 164 -56T177 -87V-92Q177 -105 169 -117T145 -146L66 -242Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="619" d="M501 -4Q481 -4 466 5T434 36L257 253H186V0H84V589Q84 615 99 631T139 647H323Q442 647 503 599T565 453Q565 370 516 321T372 263L561 38Q563 36 560 29T549 15T529 2T501 -4ZM185 334H321Q393 334 427 364T461 448Q461 562 318 562H185V334ZM405 879Q419 879 427 870T436 846Q436 826 417 801T374 754T327 715T297 697Q289 698 267 714T220 753T177 801T158 846Q158 860 166 869T189 879Q210 879 234 854T297 771Q335 828 359 853T405 879Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="402" d="M112 486Q123 486 134 483T153 470T168 444T174 402Q206 493 296 493Q334 493 354 476T375 429Q375 415 371 405T361 389T351 380T345 378Q334 387 319 393T284 399Q255 399 234 386T200 347T181 288T175 212V0H76V475Q76 477 86 481T112 486ZM333 714Q347 714 355 705T364 681Q364 661 345 636T302 589T255 550T225 532Q217 533 195 549T148 588T105 636T86 681Q86 695 94 704T117 714Q138 714 162 689T225 606Q263 663 287 688T333 714Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="584" d="M290 -13Q231 -13 186 -1T110 31T63 73T47 116Q47 129 54 139T69 157T85 168T94 171Q122 130 172 101T289 72Q357 72 393 98T430 173Q430 200 417 218T382 248T330 267T268 283Q230 292 194 304T129 337T82 390T64 472Q64 513 81 547T128 607T202 646T297 660Q351 660 392 649T462 620T504 583T519 545Q519 531 512 520T496 502T480 491T471 488Q445 525 400 550T298 575Q237 575 201 548T164 476Q164 449 175 433T206 406T255 388T317 373Q358 364 397 350T466 314T514 260T533 180Q533 90 469 39T290 -13ZM220 720Q218 723 227 738T254 775T293 818T340 857Q366 874 388 874Q407 874 420 863T433 835Q433 818 423 803T391 774Q364 756 335 745T281 726T239 718T220 720Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="494" d="M244 -12Q194 -12 156 -3T93 22T54 55T41 90Q41 102 46 110T59 125T72 133T80 135Q104 104 144 84T242 63Q293 63 320 80T348 128Q348 146 340 158T316 177T277 191T224 203Q197 209 168 217T113 240T72 279T55 342Q55 410 107 451T247 493Q293 493 327 484T385 461T420 430T432 396Q432 384 426 375T413 360T399 352T392 350Q369 380 333 399T251 418Q200 418 174 400T147 352Q147 335 155 325T178 307T215 293T267 281Q295 275 326 267T383 243T426 203T444 139Q444 70 391 29T244 -12ZM187 555Q185 558 194 573T221 610T260 653T307 692Q333 709 355 709Q374 709 387 698T400 670Q400 653 390 638T358 609Q331 591 302 580T248 561T206 553T187 555Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="584" d="M290 -13Q231 -13 186 -1T110 31T63 73T47 116Q47 129 54 139T69 157T85 168T94 171Q122 130 172 101T289 72Q357 72 393 98T430 173Q430 200 417 218T382 248T330 267T268 283Q230 292 194 304T129 337T82 390T64 472Q64 513 81 547T128 607T202 646T297 660Q351 660 392 649T462 620T504 583T519 545Q519 531 512 520T496 502T480 491T471 488Q445 525 400 550T298 575Q237 575 201 548T164 476Q164 449 175 433T206 406T255 388T317 373Q358 364 397 350T466 314T514 260T533 180Q533 90 469 39T290 -13ZM191 712Q177 712 168 721T159 744Q159 764 178 789T221 837T268 875T299 893Q306 891 329 876T376 838T419 790T438 744Q438 730 430 721T407 712Q386 712 361 737T298 819Q260 762 236 737T191 712Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="494" d="M244 -12Q194 -12 156 -3T93 22T54 55T41 90Q41 102 46 110T59 125T72 133T80 135Q104 104 144 84T242 63Q293 63 320 80T348 128Q348 146 340 158T316 177T277 191T224 203Q197 209 168 217T113 240T72 279T55 342Q55 410 107 451T247 493Q293 493 327 484T385 461T420 430T432 396Q432 384 426 375T413 360T399 352T392 350Q369 380 333 399T251 418Q200 418 174 400T147 352Q147 335 155 325T178 307T215 293T267 281Q295 275 326 267T383 243T426 203T444 139Q444 70 391 29T244 -12ZM141 547Q127 547 118 556T109 579Q109 599 128 624T171 672T218 710T249 728Q256 726 279 711T326 673T369 625T388 579Q388 565 380 556T357 547Q336 547 311 572T248 654Q210 597 186 572T141 547Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="584" d="M290 -13Q231 -13 186 -1T110 31T63 73T47 116Q47 129 54 139T69 157T85 168T94 171Q122 130 172 101T289 72Q357 72 393 98T430 173Q430 200 417 218T382 248T330 267T268 283Q230 292 194 304T129 337T82 390T64 472Q64 513 81 547T128 607T202 646T297 660Q351 660 392 649T462 620T504 583T519 545Q519 531 512 520T496 502T480 491T471 488Q445 525 400 550T298 575Q237 575 201 548T164 476Q164 449 175 433T206 406T255 388T317 373Q358 364 397 350T466 314T514 260T533 180Q533 90 469 39T290 -13ZM228 -224Q222 -229 212 -230T193 -229T179 -220T178 -205L231 -87Q239 -68 248 -58T284 -48H293Q313 -48 326 -57T339 -83V-88Q339 -98 331 -108T307 -134L228 -224Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="494" d="M244 -12Q194 -12 156 -3T93 22T54 55T41 90Q41 102 46 110T59 125T72 133T80 135Q104 104 144 84T242 63Q293 63 320 80T348 128Q348 146 340 158T316 177T277 191T224 203Q197 209 168 217T113 240T72 279T55 342Q55 410 107 451T247 493Q293 493 327 484T385 461T420 430T432 396Q432 384 426 375T413 360T399 352T392 350Q369 380 333 399T251 418Q200 418 174 400T147 352Q147 335 155 325T178 307T215 293T267 281Q295 275 326 267T383 243T426 203T444 139Q444 70 391 29T244 -12ZM181 -224Q175 -229 165 -230T146 -229T132 -220T131 -205L184 -87Q192 -68 201 -58T237 -48H246Q266 -48 279 -57T292 -83V-88Q292 -98 284 -108T260 -134L181 -224Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="584" d="M290 -13Q231 -13 186 -1T110 31T63 73T47 116Q47 129 54 139T69 157T85 168T94 171Q122 130 172 101T289 72Q357 72 393 98T430 173Q430 200 417 218T382 248T330 267T268 283Q230 292 194 304T129 337T82 390T64 472Q64 513 81 547T128 607T202 646T297 660Q351 660 392 649T462 620T504 583T519 545Q519 531 512 520T496 502T480 491T471 488Q445 525 400 550T298 575Q237 575 201 548T164 476Q164 449 175 433T206 406T255 388T317 373Q358 364 397 350T466 314T514 260T533 180Q533 90 469 39T290 -13ZM405 880Q419 880 427 871T436 847Q436 827 417 802T374 755T327 716T297 698Q289 699 267 715T220 754T177 802T158 847Q158 861 166 870T189 880Q210 880 234 855T297 772Q335 829 359 854T405 880Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="494" d="M244 -12Q194 -12 156 -3T93 22T54 55T41 90Q41 102 46 110T59 125T72 133T80 135Q104 104 144 84T242 63Q293 63 320 80T348 128Q348 146 340 158T316 177T277 191T224 203Q197 209 168 217T113 240T72 279T55 342Q55 410 107 451T247 493Q293 493 327 484T385 461T420 430T432 396Q432 384 426 375T413 360T399 352T392 350Q369 380 333 399T251 418Q200 418 174 400T147 352Q147 335 155 325T178 307T215 293T267 281Q295 275 326 267T383 243T426 203T444 139Q444 70 391 29T244 -12ZM349 714Q363 714 371 705T380 681Q380 661 361 636T318 589T271 550T241 532Q233 533 211 549T164 588T121 636T102 681Q102 695 110 704T133 714Q154 714 178 689T241 606Q279 663 303 688T349 714Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="623" d="M83 555Q35 555 35 599V604Q35 647 83 647H541Q588 647 588 604V599Q588 555 541 555H364V0H260V555H83ZM263 -242Q257 -248 248 -249T229 -247T215 -237T213 -218L266 -91Q274 -69 283 -57T319 -45H328Q348 -45 361 -56T374 -87V-92Q374 -105 366 -117T342 -146L263 -242Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="401" d="M34 481H112V555Q112 581 124 591T156 602H166Q186 602 197 593T209 555V481H360V400H208V157Q208 112 222 90T272 68Q295 68 312 77T340 102Q341 103 345 101T355 94T364 80T368 60Q368 32 339 10T256 -13Q181 -13 147 27T112 146V400H34V481ZM173 -242Q167 -248 158 -249T139 -247T125 -237T123 -218L176 -91Q184 -69 193 -57T229 -45H238Q258 -45 271 -56T284 -87V-92Q284 -105 276 -117T252 -146L173 -242Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="623" d="M83 555Q35 555 35 599V604Q35 647 83 647H541Q588 647 588 604V599Q588 555 541 555H364V0H260V555H83ZM419 879Q433 879 441 870T450 846Q450 826 431 801T388 754T341 715T311 697Q303 698 281 714T234 753T191 801T172 846Q172 860 180 869T203 879Q224 879 248 854T311 771Q349 828 373 853T419 879Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="420" d="M34 481H112V555Q112 581 124 591T156 602H166Q186 602 197 593T209 555V481H360V400H208V157Q208 112 222 90T272 68Q295 68 312 77T340 102Q341 103 345 101T355 94T364 80T368 60Q368 32 339 10T256 -13Q181 -13 147 27T112 146V400H34V481ZM299 538Q294 532 286 531T270 532T258 541T256 557L302 704Q308 725 316 735T347 745H354Q371 745 381 735T391 704V701Q391 674 371 648L299 538Z" />
+<glyph unicode="&#x166;" glyph-name="Tbar" horiz-adv-x="623" d="M83 555Q35 555 35 599V604Q35 647 83 647H541Q588 647 588 604V599Q588 555 541 555H364V0H260V555H83ZM204 296Q154 296 154 335V341Q154 359 167 368T204 378H418Q468 378 468 341V335Q468 296 418 296H204Z" />
+<glyph unicode="&#x167;" glyph-name="tbar" horiz-adv-x="401" d="M34 481H112V555Q112 581 124 591T156 602H166Q186 602 197 593T209 555V481H360V400H208V157Q208 112 222 90T272 68Q295 68 312 77T340 102Q341 103 345 101T355 94T364 80T368 60Q368 32 339 10T256 -13Q181 -13 147 27T112 146V400H34V481ZM84 209Q34 209 34 248V254Q34 272 47 281T84 291H298Q348 291 348 254V248Q348 209 298 209H84Z" />
+<glyph unicode="&#x168;" glyph-name="Utilde" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM221 677Q204 677 195 686T186 712Q186 731 192 748T208 779T233 800T264 808Q291 808 329 789T408 733Q416 759 429 780T459 802Q476 802 485 793T494 766Q494 747 488 730T472 700T447 679T416 671Q388 671 350 690T272 746Q263 720 251 699T221 677Z" />
+<glyph unicode="&#x169;" glyph-name="utilde" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM176 552Q159 552 150 561T141 587Q141 606 147 623T163 654T188 675T219 683Q246 683 284 664T363 608Q371 634 384 655T414 677Q431 677 440 668T449 641Q449 622 443 605T427 575T402 554T371 546Q343 546 305 565T227 621Q218 595 206 574T176 552Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM241 727Q191 727 191 766V772Q191 790 204 799T241 809H455Q505 809 505 772V766Q505 727 455 727H241Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM191 562Q141 562 141 601V607Q141 625 154 634T191 644H405Q455 644 455 607V601Q455 562 405 562H191Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM344 711Q319 711 293 722T244 749T207 785T192 822Q192 839 202 849T230 860Q239 860 252 852T282 830T314 801T346 769Q360 785 375 801T406 830T435 851T458 860Q476 860 486 850T496 822Q496 804 481 785T444 749T395 722T344 711Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM287 546Q262 546 236 557T187 584T150 620T135 657Q135 674 145 684T173 695Q182 695 195 687T225 665T257 636T289 604Q303 620 318 636T349 665T378 686T401 695Q419 695 429 685T439 657Q439 639 424 620T387 584T338 557T287 546Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM347 711Q320 711 298 720T261 745T236 782T227 830Q227 855 236 877T261 915T299 941T348 950Q402 950 435 916T468 830Q468 805 459 783T434 745T396 720T347 711ZM347 765Q374 765 391 783T409 830Q409 859 391 878T347 897Q321 897 304 878T286 830Q286 803 303 784T347 765Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM297 533Q270 533 248 542T211 567T186 604T177 652Q177 677 186 699T211 737T249 763T298 772Q352 772 385 738T418 652Q418 627 409 605T384 567T346 542T297 533ZM297 587Q324 587 341 605T359 652Q359 681 341 700T297 719Q271 719 254 700T236 652Q236 625 253 606T297 587Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 129 537 59T339 -12ZM390 720Q388 723 397 738T424 775T463 818T510 857Q536 874 558 874Q577 874 590 863T603 835Q603 818 593 803T561 774Q534 756 505 745T451 726T409 718T390 720ZM193 720Q191 723 200 738T226 775T266 818T313 857Q339 874 361 874Q380 874 393 863T406 835Q406 818 396 803T364 774Q337 756 308 745T254 726T212 718T193 720Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4ZM330 555Q328 558 337 573T364 610T403 653T450 692Q476 709 498 709Q517 709 530 698T543 670Q543 653 533 638T501 609Q474 591 445 580T391 561T349 553T330 555ZM133 555Q131 558 140 573T166 610T206 653T253 692Q279 709 301 709Q320 709 333 698T346 670Q346 653 336 638T304 609Q277 591 248 580T194 561T152 553T133 555Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="680" d="M339 -12Q209 -12 143 57T76 263V604Q76 651 120 651H134Q179 651 179 604V266Q179 79 340 79Q501 79 501 267V604Q501 651 545 651H561Q604 651 604 604V265Q604 181 578 122T501 30Q460 -2 446 -25T431 -66Q431 -82 440 -91T464 -101Q489 -101 501 -81Q513 -82 523 -92T534 -119Q534 -142 514 -156T456 -170Q410 -170 383 -148T355 -85Q355 -45 388 -9Q365 -12 339 -12Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="591" d="M514 5Q469 -30 453 -54T436 -97Q436 -113 445 -122T469 -132Q494 -132 506 -112Q518 -113 528 -123T539 -150Q539 -173 519 -187T461 -201Q415 -201 388 -179T360 -116Q360 -84 382 -53T449 2Q436 9 427 25T417 72Q391 35 352 12T257 -12Q209 -12 175 3T117 46T83 111T72 194V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 513 5H514Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="955" d="M63 622Q62 626 78 638T124 651Q144 651 160 638T184 591L289 115L412 522Q418 545 434 557T480 569Q510 569 525 557T548 522L669 117L776 592Q783 624 797 637T833 651Q847 651 858 647T876 638T888 629T891 622L732 0H615L480 453L339 0H224L63 622ZM372 696Q358 696 349 705T340 728Q340 748 359 773T402 821T449 859T480 877Q487 875 510 860T557 822T600 774T619 728Q619 714 611 705T588 696Q567 696 542 721T479 803Q441 746 417 721T372 696Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="740" d="M35 457Q34 459 39 464T52 474T72 482T94 486Q110 486 125 475T146 433L221 96L313 387Q318 405 332 413T374 422Q401 422 415 414T434 387L523 98L597 433Q602 464 617 475T648 486Q659 486 669 482T688 473T700 464T705 457L576 0H475L372 320L266 0H165L35 457ZM264 539Q250 539 241 548T232 571Q232 591 251 616T294 664T341 702T372 720Q379 718 402 703T449 665T492 617T511 571Q511 557 503 548T480 539Q459 539 434 564T371 646Q333 589 309 564T264 539Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="603" d="M250 265L24 647H139L302 351L468 615Q480 635 493 643T519 651Q536 651 548 646T568 633T579 618T581 609L353 265V0H250V265ZM197 712Q183 712 174 721T165 744Q165 764 184 789T227 837T274 875T305 893Q312 891 335 876T382 838T425 790T444 744Q444 730 436 721T413 712Q392 712 367 737T304 819Q266 762 242 737T197 712Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="528" d="M214 -195Q203 -195 194 -192T177 -184T165 -175T162 -168L242 27Q225 29 216 34T200 55L18 481H120L273 98L409 481H510L275 -140Q263 -172 247 -183T214 -195ZM164 547Q150 547 141 556T132 579Q132 599 151 624T194 672T241 710T272 728Q279 726 302 711T349 673T392 625T411 579Q411 565 403 556T380 547Q359 547 334 572T271 654Q233 597 209 572T164 547Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="603" d="M250 265L24 647H139L302 351L468 615Q480 635 493 643T519 651Q536 651 548 646T568 633T579 618T581 609L353 265V0H250V265ZM394 712Q365 712 349 727T333 771Q333 800 349 815T392 831Q419 831 435 816T452 771Q452 743 436 728T394 712ZM210 712Q183 712 167 727T150 771Q150 800 166 815T208 831Q237 831 253 816T269 771Q269 743 253 728T210 712Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="639" d="M63 92L445 557H112Q66 557 66 599V605Q66 647 112 647H569V561L185 90H538Q584 90 584 48V42Q584 0 538 0H63V92ZM265 720Q263 723 272 738T299 775T338 818T385 857Q411 874 433 874Q452 874 465 863T478 835Q478 818 468 803T436 774Q409 756 380 745T326 726T284 718T265 720Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="512" d="M50 66L330 402H95Q52 402 52 438V445Q52 481 95 481H457V418L173 80H422Q465 80 465 43V37Q465 0 422 0H50V66ZM205 555Q203 558 212 573T239 610T278 653T325 692Q351 709 373 709Q392 709 405 698T418 670Q418 653 408 638T376 609Q349 591 320 580T266 561T224 553T205 555Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="639" d="M63 92L445 557H112Q66 557 66 599V605Q66 647 112 647H569V561L185 90H538Q584 90 584 48V42Q584 0 538 0H63V92ZM320 712Q289 712 272 729T254 774Q254 801 272 819T321 837Q351 837 368 819T386 774Q386 747 369 730T320 712Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="512" d="M50 66L330 402H95Q52 402 52 438V445Q52 481 95 481H457V418L173 80H422Q465 80 465 43V37Q465 0 422 0H50V66ZM265 547Q234 547 217 564T199 609Q199 636 217 654T266 672Q296 672 313 654T331 609Q331 582 314 565T265 547Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="639" d="M63 92L445 557H112Q66 557 66 599V605Q66 647 112 647H569V561L185 90H538Q584 90 584 48V42Q584 0 538 0H63V92ZM420 880Q434 880 442 871T451 847Q451 827 432 802T389 755T342 716T312 698Q304 699 282 715T235 754T192 802T173 847Q173 861 181 870T204 880Q225 880 249 855T312 772Q350 829 374 854T420 880Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="512" d="M50 66L330 402H95Q52 402 52 438V445Q52 481 95 481H457V418L173 80H422Q465 80 465 43V37Q465 0 422 0H50V66ZM359 714Q373 714 381 705T390 681Q390 661 371 636T328 589T281 550T251 532Q243 533 221 549T174 588T131 636T112 681Q112 695 120 704T143 714Q164 714 188 689T251 606Q289 663 313 688T359 714Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="443" d="M85 351Q61 351 51 360T41 388V395Q41 413 51 422T85 432H145L156 493Q171 578 212 618T318 659Q369 659 398 640T427 591Q427 579 422 569T411 553T400 543T394 540Q385 553 368 563T325 574Q294 574 277 552T251 479L242 432H340Q363 432 373 423T383 394V387Q383 369 373 360T340 351H231L179 67Q171 25 154 9T114 -8Q94 -8 81 -1T69 9L130 351H85Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="584" d="M290 -13Q231 -13 186 -1T110 31T63 73T47 116Q47 129 54 139T69 157T85 168T94 171Q122 130 172 101T289 72Q357 72 393 98T430 173Q430 200 417 218T382 248T330 267T268 283Q230 292 194 304T129 337T82 390T64 472Q64 513 81 547T128 607T202 646T297 660Q351 660 392 649T462 620T504 583T519 545Q519 531 512 520T496 502T480 491T471 488Q445 525 400 550T298 575Q237 575 201 548T164 476Q164 449 175 433T206 406T255 388T317 373Q358 364 397 350T466 314T514 260T533 180Q533 90 469 39T290 -13ZM238 -242Q232 -248 223 -249T204 -247T190 -237T188 -218L241 -91Q249 -69 258 -57T294 -45H303Q323 -45 336 -56T349 -87V-92Q349 -105 341 -117T317 -146L238 -242Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="494" d="M244 -12Q194 -12 156 -3T93 22T54 55T41 90Q41 102 46 110T59 125T72 133T80 135Q104 104 144 84T242 63Q293 63 320 80T348 128Q348 146 340 158T316 177T277 191T224 203Q197 209 168 217T113 240T72 279T55 342Q55 410 107 451T247 493Q293 493 327 484T385 461T420 430T432 396Q432 384 426 375T413 360T399 352T392 350Q369 380 333 399T251 418Q200 418 174 400T147 352Q147 335 155 325T178 307T215 293T267 281Q295 275 326 267T383 243T426 203T444 139Q444 70 391 29T244 -12ZM174 -242Q168 -248 159 -249T140 -247T126 -237T124 -218L177 -91Q185 -69 194 -57T230 -45H239Q259 -45 272 -56T285 -87V-92Q285 -105 277 -117T253 -146L174 -242Z" />
+<glyph unicode="&#x21A;" glyph-name="uni021A" horiz-adv-x="623" d="M83 555Q35 555 35 599V604Q35 647 83 647H541Q588 647 588 604V599Q588 555 541 555H364V0H260V555H83ZM263 -242Q257 -248 248 -249T229 -247T215 -237T213 -218L266 -91Q274 -69 283 -57T319 -45H328Q348 -45 361 -56T374 -87V-92Q374 -105 366 -117T342 -146L263 -242Z" />
+<glyph unicode="&#x21B;" glyph-name="uni021B" horiz-adv-x="401" d="M34 481H112V555Q112 581 124 591T156 602H166Q186 602 197 593T209 555V481H360V400H208V157Q208 112 222 90T272 68Q295 68 312 77T340 102Q341 103 345 101T355 94T364 80T368 60Q368 32 339 10T256 -13Q181 -13 147 27T112 146V400H34V481ZM173 -242Q167 -248 158 -249T139 -247T125 -237T123 -218L176 -91Q184 -69 193 -57T229 -45H238Q258 -45 271 -56T284 -87V-92Q284 -105 276 -117T252 -146L173 -242Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="530" d="M157 547Q143 547 134 556T125 579Q125 599 144 624T187 672T234 710T265 728Q272 726 295 711T342 673T385 625T404 579Q404 565 396 556T373 547Q352 547 327 572T264 654Q226 597 202 572T157 547Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="530" d="M373 714Q387 714 395 705T404 681Q404 661 385 636T342 589T295 550T265 532Q257 533 235 549T188 588T145 636T126 681Q126 695 134 704T157 714Q178 714 202 689T265 606Q303 663 327 688T373 714Z" />
+<glyph unicode="&#x2C9;" glyph-name="overscore" horiz-adv-x="530" d="M158 562Q108 562 108 601V607Q108 625 121 634T158 644H372Q422 644 422 607V601Q422 562 372 562H158Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="530" d="M265 546Q240 546 214 557T165 584T128 620T113 657Q113 674 123 684T151 695Q160 695 173 687T203 665T235 636T267 604Q281 620 296 636T327 665T356 686T379 695Q397 695 407 685T417 657Q417 639 402 620T365 584T316 557T265 546Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="530" d="M265 547Q234 547 217 564T199 609Q199 636 217 654T266 672Q296 672 313 654T331 609Q331 582 314 565T265 547Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="530" d="M264 546Q237 546 215 555T178 580T153 617T144 665Q144 690 153 712T178 750T216 776T265 785Q319 785 352 751T385 665Q385 640 376 618T351 580T313 555T264 546ZM264 600Q291 600 308 618T326 665Q326 694 308 713T264 732Q238 732 221 713T203 665Q203 638 220 619T264 600Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="530" d="M338 5Q293 -30 277 -54T260 -97Q260 -113 269 -122T293 -132Q318 -132 330 -112Q342 -113 352 -123T363 -150Q363 -173 343 -187T285 -201Q239 -201 212 -179T184 -116Q184 -77 218 -40T319 23L338 5Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="530" d="M146 552Q129 552 120 561T111 587Q111 606 117 623T133 654T158 675T189 683Q216 683 254 664T333 608Q341 634 354 655T384 677Q401 677 410 668T419 641Q419 622 413 605T397 575T372 554T341 546Q313 546 275 565T197 621Q188 595 176 574T146 552Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="530" d="M295 555Q293 558 302 573T329 610T368 653T415 692Q441 709 463 709Q482 709 495 698T508 670Q508 653 498 638T466 609Q439 591 410 580T356 561T314 553T295 555ZM98 555Q96 558 105 573T131 610T171 653T218 692Q244 709 266 709Q285 709 298 698T311 670Q311 653 301 638T269 609Q242 591 213 580T159 561T117 553T98 555Z" />
+<glyph unicode="&#x394;" glyph-name="increment" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x3A9;" glyph-name="Omega" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x3BC;" glyph-name="mu" horiz-adv-x="591" d="M480 -4Q469 -4 458 -1T438 10T424 34T417 72Q391 35 352 12T257 -12Q209 -12 174 3Q173 4 171 5V-312H72V481H170V206Q170 143 200 109T285 74Q330 74 364 97T417 159V481H515V7Q515 5 505 1T480 -4Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="782" d="M391 -13Q318 -13 258 12T153 81T85 187T60 322Q60 395 85 457T154 564T259 634T392 660Q465 660 525 635T629 566T697 460T722 325Q722 252 697 190T628 83T524 13T391 -13ZM392 41Q452 41 502 61T588 119T643 208T663 323Q663 384 643 436T586 525T499 584T390 606Q330 606 280 586T194 528T139 439T119 324Q119 263 139 211T196 122T283 63T392 41ZM302 138Q270 138 270 172V445Q270 465 281 476T310 487H405Q474 487 511 457T548 370Q548 312 513 281T409 250H346V172Q346 138 313 138H302ZM344 309H408Q441 309 458 325T475 369Q475 397 458 412T405 428H344V309Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="569" d="M99 201Q76 201 66 211T56 239V248Q56 265 66 275T99 286H468Q491 286 501 276T511 248V239Q511 222 501 212T468 201H99Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="735" d="M99 201Q76 201 66 211T56 239V248Q56 265 66 275T99 286H636Q659 286 669 276T679 248V239Q679 222 669 212T636 201H99Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="246" d="M94 412Q75 412 63 424T51 459V463Q51 476 56 490T75 524L157 651Q162 658 172 659T191 658T205 648T207 629L155 459Q147 436 138 424T103 412H94Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="246" d="M89 421Q84 414 74 413T55 414T41 424T39 443L92 613Q100 637 109 648T144 660H153Q172 660 183 648T195 613V609Q195 579 172 548L89 421Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="246" d="M81 -120Q76 -127 66 -128T47 -127T33 -117T31 -98L84 72Q92 96 101 107T136 119H145Q164 119 175 107T187 72V68Q187 38 164 7L81 -120Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="421" d="M268 412Q250 412 238 424T226 459V463Q226 492 250 524L332 651Q338 658 347 659T365 658T379 648T382 629L329 459Q322 435 313 424T277 412H268ZM93 412Q75 412 63 424T51 459V463Q51 478 57 493T75 524L157 651Q163 658 172 659T191 658T205 648T207 629L154 459Q147 435 138 424T103 412H93Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="421" d="M264 421Q259 414 249 413T230 414T216 424T213 443L267 613Q274 637 283 648T318 660H328Q346 660 357 648T369 613V609Q369 580 346 548L264 421ZM88 421Q83 414 74 413T55 414T41 424T38 443L92 613Q99 637 108 648T143 660H152Q171 660 182 648T194 613V609Q194 579 171 548L88 421Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="421" d="M256 -120Q251 -127 241 -128T222 -127T208 -117T205 -98L259 72Q266 96 275 107T310 119H320Q338 119 349 107T361 72V68Q361 39 338 7L256 -120ZM80 -120Q75 -127 66 -128T47 -127T33 -117T30 -98L84 72Q91 96 100 107T135 119H144Q163 119 174 107T186 72V68Q186 38 163 7L80 -120Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="548" d="M266 -159Q250 -159 244 -153T236 -130L222 277Q220 300 226 324T249 370L94 345Q65 339 53 354T40 396Q40 425 51 439T94 448L249 422L222 601Q217 631 230 643T274 655Q306 655 318 643T326 601L300 423L454 448Q483 453 495 439T508 396Q508 369 496 354T454 345L299 370Q315 349 321 325T326 277L312 -130Q310 -146 304 -152T283 -159H266Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="548" d="M266 -159Q250 -159 244 -153T236 -130L229 87L94 64Q63 58 52 72T40 116Q40 143 52 158T94 167L227 145L222 281Q220 304 227 329T251 374L94 349Q65 343 53 358T40 400Q40 429 51 443T94 452L248 427L222 601Q217 631 230 643T274 655Q306 655 318 643T326 601L300 427L454 452Q483 457 495 443T508 400Q508 373 496 358T454 349L298 374Q314 354 321 329T326 281L321 145L454 167Q483 173 495 158T508 116Q508 88 496 74T454 64L319 87L312 -130Q310 -146 304 -152T283 -159H266Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="364" d="M182 129Q158 129 138 138T102 162T78 198T69 243Q69 267 77 288T101 325T137 350T182 359Q206 359 227 350T263 325T287 288T296 243Q296 219 287 199T263 163T227 138T182 129Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="697" d="M563 -7Q509 -7 509 47V59Q509 114 563 114H576Q632 114 632 59V47Q632 -7 576 -7H563ZM341 -7Q287 -7 287 47V59Q287 114 341 114H354Q409 114 409 59V47Q409 -7 354 -7H341ZM119 -7Q65 -7 65 47V59Q65 114 119 114H132Q187 114 187 59V47Q187 -7 132 -7H119Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1126" d="M199 344Q166 344 138 355T88 387T56 435T44 495V504Q44 536 56 564T89 612T138 645T200 657Q234 657 262 646T312 614T344 566T356 506V497Q356 465 344 437T311 389T261 356T199 344ZM137 -9Q135 -11 128 -10T113 -3T98 9T91 28Q91 42 97 53T120 81L623 660Q624 661 631 660T647 655T662 642T669 623Q669 612 663 599T641 569L137 -9ZM926 -6Q892 -6 864 5T814 37T782 85T770 145V154Q770 186 782 214T815 262T865 295T927 307Q961 307 989 296T1038 264T1070 216T1082 156V147Q1082 115 1070 87T1037 39T988 6T926 -6ZM565 -6Q531 -6 503 5T453 37T421 85T409 145V154Q409 186 421 214T454 262T504 295T566 307Q599 307 627 296T677 264T709 216T721 156V147Q721 115 709 87T676 39T627 6T565 -6ZM200 407Q235 407 257 431T279 493V508Q279 546 257 570T199 595Q164 595 142 571T120 508V493Q120 455 142 431T200 407ZM927 57Q962 57 984 81T1006 143V158Q1006 196 984 220T926 245Q891 245 869 221T847 158V143Q847 105 869 81T927 57ZM566 57Q600 57 622 81T645 143V158Q645 196 623 220T565 245Q530 245 508 221T486 158V143Q486 105 508 81T566 57Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="345" d="M286 110V106Q286 92 277 82T250 72H248Q232 72 212 83T170 113T128 152T91 192T65 225T55 244Q55 248 65 262T91 295T127 335T169 375T211 404T248 416H250Q267 416 276 406T286 381V378Q286 366 273 350T239 316T192 280T139 243Q166 225 192 207T239 171T273 138T286 110Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="345" d="M60 110Q60 122 73 137T106 170T153 206T207 243Q180 261 154 279T107 316T73 350T60 378V381Q60 395 69 405T95 416H96Q112 416 133 405T175 375T217 336T254 295T280 262T290 244Q290 240 280 226T254 192T218 152T176 113T133 84T96 72H95Q78 72 69 82T60 106V110Z" />
+<glyph unicode="&#x2044;" glyph-name="uni2044" horiz-adv-x="681" d="M91 -8Q89 -10 82 -9T66 -4T50 7T43 26Q43 40 49 51T71 79L587 660Q589 662 596 662T612 657T628 645T635 625Q635 615 629 602T607 572L91 -8Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="658" d="M85 366Q63 366 54 374T45 398V406Q45 420 54 428T85 437H143Q164 540 232 599T401 659Q450 659 488 647T553 617T594 576T608 533Q608 518 600 508T583 491T565 483T555 482Q529 527 493 550T399 574Q340 574 300 538T242 437H433Q455 437 463 429T472 406V398Q472 383 464 375T433 366H232V285H433Q455 285 463 276T472 253V245Q472 231 464 222T433 213H242Q259 148 301 111T404 73Q462 73 497 99T557 173Q558 175 566 173T585 165T603 147T611 121Q611 101 598 78T558 35T493 1T405 -13Q299 -13 231 46T142 213H85Q63 213 54 222T45 245V253Q45 267 54 276T85 285H135V366H85Z" />
+<glyph unicode="&#x2113;" glyph-name="afii61289" horiz-adv-x="434" d="M199 341Q228 365 251 395T291 456T317 513T326 558Q326 584 315 584Q308 584 300 573Q291 561 278 539T250 486T222 419T199 341ZM44 138Q30 151 19 172T7 228Q25 237 33 240T49 246L59 251Q63 321 81 383T126 497T183 588T245 652Q285 683 327 683Q367 683 395 652T423 558Q423 509 402 463T349 375T276 294T196 223Q196 218 196 214T195 204Q195 158 207 133T237 107Q254 107 273 133T310 217Q322 220 336 220Q355 220 373 213T404 186Q397 149 382 114T345 52T293 9T228 -8Q206 -8 182 1T135 30T94 78T66 146L44 138Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="713" d="M378 336Q351 336 351 368V617Q351 651 381 651H408Q424 651 430 646T442 628L502 481L562 628Q567 642 574 646T595 651H621Q651 651 651 617V368Q651 353 645 345T624 336H615Q588 336 588 368V557L533 416Q531 409 526 405T501 400Q481 400 477 404T470 416L414 556V368Q414 353 409 345T389 336H378ZM63 588Q32 588 32 617V620Q32 648 63 648H274Q304 648 304 620V617Q304 588 274 588H202V368Q202 336 172 336H163Q135 336 135 368V588H63Z" />
+<glyph unicode="&#x2126;" glyph-name="uni2126" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x212E;" glyph-name="estimated" horiz-adv-x="830" d="M415 671Q492 671 560 645T681 573T763 463T793 324H185Q175 324 175 313V116Q223 65 286 38T415 10Q490 10 555 43T667 131H724Q667 62 585 26T415 -11Q340 -11 272 14T151 84T67 191T36 330Q36 403 66 465T147 574T268 645T415 671ZM415 650Q345 650 282 622T175 543V358Q175 347 185 347H646Q655 347 655 358V542Q610 594 546 622T415 650Z" />
+<glyph unicode="&#x2202;" glyph-name="partialdiff" horiz-adv-x="538" d="M303 481Q335 481 361 471T404 445V464Q404 499 399 528T381 578T343 610T282 622Q247 622 208 612L189 679Q271 710 332 710Q421 710 464 649T508 475Q508 370 481 283T409 133T311 35T201 0Q165 0 134 10T80 44T43 102T30 187Q30 256 55 310T120 403T208 461T303 481ZM205 105Q229 105 256 121T310 167T359 243T393 349Q382 361 360 373T309 386Q275 386 244 371T188 331T149 270T134 194Q134 157 150 131T205 105Z" />
+<glyph unicode="&#x2206;" glyph-name="uni2206" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x220F;" glyph-name="product" horiz-adv-x="634" d="M484 0V607H150V0H50V700H584V0H484Z" />
+<glyph unicode="&#x2211;" glyph-name="summation" horiz-adv-x="475" d="M110 80H437V0H25V96L209 325L25 554V650H437V570H115L288 353V300L110 80Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="630" d="M133 221Q110 221 100 231T90 260V267Q90 285 100 295T133 306H496Q519 306 529 296T539 267V260Q539 242 529 232T496 221H133Z" />
+<glyph unicode="&#x2215;" glyph-name="fraction" horiz-adv-x="681" d="M91 -8Q89 -10 82 -9T66 -4T50 7T43 26Q43 40 49 51T71 79L587 660Q589 662 596 662T612 657T628 645T635 625Q635 615 629 602T607 572L91 -8Z" />
+<glyph unicode="&#x2219;" glyph-name="middot" horiz-adv-x="262" d="M123 184Q69 184 69 237V249Q69 304 123 304H137Q192 304 192 249V237Q192 184 137 184H123Z" />
+<glyph unicode="&#x221A;" glyph-name="radical" horiz-adv-x="649" d="M256 501L369 171L566 700H649L386 0H283L136 433H30V501H256Z" />
+<glyph unicode="&#x221E;" glyph-name="infinity" horiz-adv-x="733" d="M703 271Q703 231 690 203T654 156T604 129T547 120Q518 120 493 128T447 150T405 181T366 217Q347 199 328 182T287 151T240 129T186 120Q157 120 129 129T79 156T44 203T30 271Q30 310 43 338T79 385T129 413T186 422Q215 422 240 414T286 392T327 361T366 325Q385 343 405 360T446 391T493 413T547 422Q576 422 604 414T654 387T689 340T703 271ZM187 222Q223 222 252 235T308 271Q282 292 253 306T187 320Q167 320 149 309T130 271Q130 244 148 233T187 222ZM546 320Q510 320 481 306T425 271Q451 249 480 236T546 222Q566 222 584 233T603 271Q603 298 585 309T546 320Z" />
+<glyph unicode="&#x222B;" glyph-name="integral'" horiz-adv-x="396" d="M164 596Q172 654 201 685Q228 714 271 723T375 724V648Q314 653 288 640T255 584L168 -32Q159 -95 128 -126Q105 -149 70 -155T-15 -155V-81Q22 -84 45 -75T74 -25L164 596Z" />
+<glyph unicode="&#x2248;" glyph-name="approxequal" horiz-adv-x="529" d="M146 89Q129 89 120 98T111 124Q111 143 117 160T133 191T158 212T189 220Q216 220 254 201T333 145Q341 171 354 192T384 214Q401 214 410 205T419 178Q419 159 413 142T397 112T372 91T341 83Q313 83 275 102T197 158Q188 132 176 111T146 89ZM146 265Q129 265 120 274T111 300Q111 319 117 336T133 367T158 388T189 396Q216 396 254 377T333 321Q341 347 354 368T384 390Q401 390 410 381T419 354Q419 335 413 318T397 288T372 267T341 259Q313 259 275 278T197 334Q188 308 176 287T146 265Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="630" d="M133 337Q110 337 100 347T90 374V381Q90 398 100 408T133 418H341L402 554Q413 580 424 585T449 585L460 581Q474 575 478 563T472 524L424 418H496Q519 418 529 408T539 381V374Q539 357 529 347T496 337H382L313 185H496Q519 185 529 175T539 148V140Q539 123 529 113T496 103H282L223 -33Q212 -59 201 -65T176 -65L165 -60Q151 -55 147 -42T153 -3L199 103H133Q110 103 100 113T90 140V148Q90 165 100 175T133 185H242L311 337H133Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="630" d="M101 420Q101 445 108 456T129 474L501 630Q505 631 516 618T529 585Q532 570 519 553T480 526L205 420L480 316Q506 307 518 291T529 257Q528 236 517 223T501 212L129 366Q115 373 108 383T101 420ZM136 94Q113 94 103 104T93 131V138Q93 155 103 165T136 175H493Q516 175 526 165T536 138V131Q536 114 526 104T493 94H136Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="630" d="M100 587Q101 597 105 605T113 620T122 629T128 632L501 476Q515 469 522 459T529 422Q529 397 522 386T501 369L128 214Q124 212 113 225T100 259Q99 276 112 293T149 319L425 422L149 528Q123 538 110 555T100 587ZM136 96Q113 96 103 106T93 133V141Q93 158 103 168T136 178H493Q516 178 526 168T536 141V133Q536 116 526 106T493 96H136Z" />
+<glyph unicode="&#x25CA;" glyph-name="lozenge" horiz-adv-x="520" d="M30 388L226 776H294L490 388L294 0H226L30 388ZM370 388L260 616L150 388L260 159L370 388Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="612" d="M23 481H123V517Q123 608 163 651T276 694Q332 694 358 675T384 629Q384 618 381 610T373 597T365 588T360 586Q349 597 330 605T288 614Q252 614 236 591T219 516V481H343V400H221V0H123V400H23V481ZM433 438Q433 465 445 475T477 486H488Q508 486 520 476T532 438V0H433V438ZM483 584Q455 584 440 598T424 639Q424 666 439 680T483 695Q542 695 542 639Q542 612 527 598T483 584Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="612" d="M23 481H123V517Q123 608 163 651T276 694Q332 694 358 675T384 629Q384 618 381 610T373 597T365 588T360 586Q349 597 330 605T288 614Q252 614 236 591T219 516V481H343V400H221V0H123V400H23V481ZM433 676Q433 678 444 682T469 687Q481 687 492 684T512 671T526 645T532 602V0H433V676Z" />
+
+<hkern u1="&#x141;" u2="&#xdd;" k="83" />
+<hkern u1="&#x141;" u2="&#x17d;" k="-1" />
+<hkern u1="&#x141;" u2="*" k="12" />
+<hkern u1="&#x141;" u2="?" k="14" />
+<hkern u1="&#x141;" u2="@" k="50" />
+<hkern u1="&#x141;" u2="A" k="-20" />
+<hkern u1="&#x141;" u2="C" k="45" />
+<hkern u1="&#x141;" u2="G" k="45" />
+<hkern u1="&#x141;" u2="J" k="-3" />
+<hkern u1="&#x141;" u2="O" k="45" />
+<hkern u1="&#x141;" u2="Q" k="45" />
+<hkern u1="&#x141;" u2="T" k="99" />
+<hkern u1="&#x141;" u2="U" k="15" />
+<hkern u1="&#x141;" u2="V" k="90" />
+<hkern u1="&#x141;" u2="W" k="70" />
+<hkern u1="&#x141;" u2="Y" k="83" />
+<hkern u1="&#x141;" u2="Z" k="-1" />
+<hkern u1="&#x141;" u2="\" k="50" />
+<hkern u1="&#x141;" u2="a" k="18" />
+<hkern u1="&#x141;" u2="c" k="18" />
+<hkern u1="&#x141;" u2="d" k="18" />
+<hkern u1="&#x141;" u2="e" k="18" />
+<hkern u1="&#x141;" u2="o" k="18" />
+<hkern u1="&#x141;" u2="q" k="18" />
+<hkern u1="&#x141;" u2="v" k="44" />
+<hkern u1="&#x141;" u2="w" k="14" />
+<hkern u1="&#x141;" u2="y" k="44" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd6;" k="45" />
+<hkern u1="&#x141;" u2="&#xdc;" k="15" />
+<hkern u1="&#x141;" u2="&#xe7;" k="18" />
+<hkern u1="&#x141;" u2="&#xae;" k="50" />
+<hkern u1="&#x141;" u2="&#xa9;" k="50" />
+<hkern u1="&#x141;" u2="&#x2122;" k="70" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd8;" k="45" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="50" />
+<hkern u1="&#x141;" u2="&#xe6;" k="18" />
+<hkern u1="&#x141;" u2="&#xab;" k="4" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd5;" k="45" />
+<hkern u1="&#x141;" u2="&#x152;" k="45" />
+<hkern u1="&#x141;" u2="&#x153;" k="18" />
+<hkern u1="&#x141;" u2="&#x201c;" k="100" />
+<hkern u1="&#x141;" u2="&#x201d;" k="62" />
+<hkern u1="&#x141;" u2="&#x2018;" k="100" />
+<hkern u1="&#x141;" u2="&#x2019;" k="62" />
+<hkern u1="&#x141;" u2="&#x178;" k="83" />
+<hkern u1="&#x141;" u2="&#x2039;" k="4" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-20" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-20" />
+<hkern u1="&#x141;" u2="&#xd3;" k="45" />
+<hkern u1="&#x141;" u2="&#xd4;" k="45" />
+<hkern u1="&#x141;" u2="&#xd2;" k="45" />
+<hkern u1="&#x141;" u2="&#xda;" k="15" />
+<hkern u1="&#x141;" u2="&#xdb;" k="15" />
+<hkern u1="&#x141;" u2="&#xd9;" k="15" />
+<hkern u1="&#x160;" u2="&#xdd;" k="-10" />
+<hkern u1="&#x160;" u2="A" k="-5" />
+<hkern u1="&#x160;" u2="V" k="-9" />
+<hkern u1="&#x160;" u2="W" k="2" />
+<hkern u1="&#x160;" u2="X" k="-5" />
+<hkern u1="&#x160;" u2="Y" k="-10" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-5" />
+<hkern u1="&#x160;" u2="&#x178;" k="-10" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-5" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-5" />
+<hkern u1="&#xdd;" u2="&amp;" k="30" />
+<hkern u1="&#xdd;" u2=")" k="-50" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="50" />
+<hkern u1="&#xdd;" u2="-" k="50" />
+<hkern u1="&#xdd;" u2="." k="50" />
+<hkern u1="&#xdd;" u2="/" k="12" />
+<hkern u1="&#xdd;" u2=":" k="30" />
+<hkern u1="&#xdd;" u2=";" k="30" />
+<hkern u1="&#xdd;" u2="@" k="30" />
+<hkern u1="&#xdd;" u2="A" k="51" />
+<hkern u1="&#xdd;" u2="C" k="23" />
+<hkern u1="&#xdd;" u2="G" k="23" />
+<hkern u1="&#xdd;" u2="J" k="74" />
+<hkern u1="&#xdd;" u2="O" k="23" />
+<hkern u1="&#xdd;" u2="Q" k="23" />
+<hkern u1="&#xdd;" u2="S" k="-10" />
+<hkern u1="&#xdd;" u2="T" k="-22" />
+<hkern u1="&#xdd;" u2="V" k="-28" />
+<hkern u1="&#xdd;" u2="X" k="-18" />
+<hkern u1="&#xdd;" u2="]" k="-50" />
+<hkern u1="&#xdd;" u2="a" k="59" />
+<hkern u1="&#xdd;" u2="c" k="59" />
+<hkern u1="&#xdd;" u2="d" k="59" />
+<hkern u1="&#xdd;" u2="e" k="59" />
+<hkern u1="&#xdd;" u2="f" k="10" />
+<hkern u1="&#xdd;" u2="g" k="40" />
+<hkern u1="&#xdd;" u2="m" k="40" />
+<hkern u1="&#xdd;" u2="n" k="40" />
+<hkern u1="&#xdd;" u2="o" k="59" />
+<hkern u1="&#xdd;" u2="p" k="40" />
+<hkern u1="&#xdd;" u2="q" k="59" />
+<hkern u1="&#xdd;" u2="r" k="40" />
+<hkern u1="&#xdd;" u2="s" k="51" />
+<hkern u1="&#xdd;" u2="t" k="20" />
+<hkern u1="&#xdd;" u2="u" k="40" />
+<hkern u1="&#xdd;" u2="v" k="20" />
+<hkern u1="&#xdd;" u2="w" k="10" />
+<hkern u1="&#xdd;" u2="x" k="20" />
+<hkern u1="&#xdd;" u2="y" k="20" />
+<hkern u1="&#xdd;" u2="z" k="30" />
+<hkern u1="&#xdd;" u2="}" k="-50" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="51" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="51" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="23" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="59" />
+<hkern u1="&#xdd;" u2="&#xae;" k="30" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="30" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="51" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="23" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="30" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="59" />
+<hkern u1="&#xdd;" u2="&#xab;" k="60" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="51" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="51" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="23" />
+<hkern u1="&#xdd;" u2="&#x152;" k="23" />
+<hkern u1="&#xdd;" u2="&#x153;" k="59" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="50" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="60" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="40" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="50" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="50" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="51" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="51" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="23" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="23" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="23" />
+<hkern u1="&#xdd;" u2="&#x131;" k="40" />
+<hkern u1="&#xde;" u2="&#x17d;" k="38" />
+<hkern u1="&#xde;" u2="&#x2c;" k="38" />
+<hkern u1="&#xde;" u2="." k="38" />
+<hkern u1="&#xde;" u2="/" k="50" />
+<hkern u1="&#xde;" u2="?" k="20" />
+<hkern u1="&#xde;" u2="A" k="20" />
+<hkern u1="&#xde;" u2="J" k="39" />
+<hkern u1="&#xde;" u2="T" k="27" />
+<hkern u1="&#xde;" u2="V" k="20" />
+<hkern u1="&#xde;" u2="W" k="38" />
+<hkern u1="&#xde;" u2="X" k="26" />
+<hkern u1="&#xde;" u2="Y" k="23" />
+<hkern u1="&#xde;" u2="Z" k="38" />
+<hkern u1="&#xde;" u2="a" k="4" />
+<hkern u1="&#xde;" u2="b" k="8" />
+<hkern u1="&#xde;" u2="c" k="4" />
+<hkern u1="&#xde;" u2="d" k="4" />
+<hkern u1="&#xde;" u2="e" k="4" />
+<hkern u1="&#xde;" u2="h" k="8" />
+<hkern u1="&#xde;" u2="k" k="8" />
+<hkern u1="&#xde;" u2="l" k="8" />
+<hkern u1="&#xde;" u2="m" k="4" />
+<hkern u1="&#xde;" u2="n" k="4" />
+<hkern u1="&#xde;" u2="o" k="4" />
+<hkern u1="&#xde;" u2="p" k="4" />
+<hkern u1="&#xde;" u2="q" k="4" />
+<hkern u1="&#xde;" u2="r" k="4" />
+<hkern u1="&#xde;" u2="u" k="4" />
+<hkern u1="&#xde;" u2="x" k="10" />
+<hkern u1="&#xde;" u2="z" k="10" />
+<hkern u1="&#xde;" u2="&#xc4;" k="20" />
+<hkern u1="&#xde;" u2="&#xc5;" k="20" />
+<hkern u1="&#xde;" u2="&#xe7;" k="4" />
+<hkern u1="&#xde;" u2="&#xc6;" k="20" />
+<hkern u1="&#xde;" u2="&#xe6;" k="4" />
+<hkern u1="&#xde;" u2="&#x2026;" k="38" />
+<hkern u1="&#xde;" u2="&#xc0;" k="20" />
+<hkern u1="&#xde;" u2="&#xc3;" k="20" />
+<hkern u1="&#xde;" u2="&#x153;" k="4" />
+<hkern u1="&#xde;" u2="&#x201c;" k="20" />
+<hkern u1="&#xde;" u2="&#x2018;" k="20" />
+<hkern u1="&#xde;" u2="&#x178;" k="23" />
+<hkern u1="&#xde;" u2="&#x201a;" k="38" />
+<hkern u1="&#xde;" u2="&#x201e;" k="38" />
+<hkern u1="&#xde;" u2="&#xc2;" k="20" />
+<hkern u1="&#xde;" u2="&#xc1;" k="20" />
+<hkern u1="&#xde;" u2="&#x131;" k="4" />
+<hkern u1="&#x17d;" u2="-" k="42" />
+<hkern u1="&#x17d;" u2="A" k="10" />
+<hkern u1="&#x17d;" u2="C" k="37" />
+<hkern u1="&#x17d;" u2="G" k="37" />
+<hkern u1="&#x17d;" u2="J" k="6" />
+<hkern u1="&#x17d;" u2="O" k="37" />
+<hkern u1="&#x17d;" u2="Q" k="37" />
+<hkern u1="&#x17d;" u2="T" k="7" />
+<hkern u1="&#x17d;" u2="a" k="34" />
+<hkern u1="&#x17d;" u2="b" k="10" />
+<hkern u1="&#x17d;" u2="c" k="34" />
+<hkern u1="&#x17d;" u2="d" k="34" />
+<hkern u1="&#x17d;" u2="e" k="34" />
+<hkern u1="&#x17d;" u2="f" k="20" />
+<hkern u1="&#x17d;" u2="g" k="20" />
+<hkern u1="&#x17d;" u2="h" k="10" />
+<hkern u1="&#x17d;" u2="i" k="20" />
+<hkern u1="&#x17d;" u2="j" k="15" />
+<hkern u1="&#x17d;" u2="k" k="10" />
+<hkern u1="&#x17d;" u2="l" k="10" />
+<hkern u1="&#x17d;" u2="m" k="20" />
+<hkern u1="&#x17d;" u2="n" k="20" />
+<hkern u1="&#x17d;" u2="o" k="34" />
+<hkern u1="&#x17d;" u2="p" k="20" />
+<hkern u1="&#x17d;" u2="q" k="34" />
+<hkern u1="&#x17d;" u2="r" k="20" />
+<hkern u1="&#x17d;" u2="u" k="20" />
+<hkern u1="&#x17d;" u2="v" k="30" />
+<hkern u1="&#x17d;" u2="w" k="10" />
+<hkern u1="&#x17d;" u2="y" k="30" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="37" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="34" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="37" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="34" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="37" />
+<hkern u1="&#x17d;" u2="&#x152;" k="37" />
+<hkern u1="&#x17d;" u2="&#x153;" k="34" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="42" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="42" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="10" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="10" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="37" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="37" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="37" />
+<hkern u1="&#x17d;" u2="&#x131;" k="20" />
+<hkern u1="!" u2="a" k="2" />
+<hkern u1="!" u2="c" k="2" />
+<hkern u1="!" u2="d" k="2" />
+<hkern u1="!" u2="e" k="2" />
+<hkern u1="!" u2="o" k="2" />
+<hkern u1="!" u2="q" k="2" />
+<hkern u1="!" u2="&#xe7;" k="2" />
+<hkern u1="!" u2="&#xe6;" k="2" />
+<hkern u1="!" u2="&#x153;" k="2" />
+<hkern u1="&amp;" u2="&#xdd;" k="32" />
+<hkern u1="&amp;" u2="A" k="-30" />
+<hkern u1="&amp;" u2="J" k="-20" />
+<hkern u1="&amp;" u2="T" k="70" />
+<hkern u1="&amp;" u2="V" k="26" />
+<hkern u1="&amp;" u2="W" k="22" />
+<hkern u1="&amp;" u2="X" k="-20" />
+<hkern u1="&amp;" u2="Y" k="32" />
+<hkern u1="&amp;" u2="&#xc4;" k="-35" />
+<hkern u1="&amp;" u2="&#xc5;" k="-35" />
+<hkern u1="&amp;" u2="&#xc6;" k="-35" />
+<hkern u1="&amp;" u2="&#xc0;" k="-35" />
+<hkern u1="&amp;" u2="&#xc3;" k="-35" />
+<hkern u1="&amp;" u2="&#x178;" k="32" />
+<hkern u1="&amp;" u2="&#xc2;" k="-35" />
+<hkern u1="&amp;" u2="&#xc1;" k="-35" />
+<hkern u1="(" u2="7" k="-4" />
+<hkern u1="(" u2="J" k="4" />
+<hkern u1="(" u2="V" k="-30" />
+<hkern u1="(" u2="W" k="-10" />
+<hkern u1="(" u2="X" k="-40" />
+<hkern u1="(" u2="Y" k="-50" />
+<hkern u1="(" u2="g" k="-20" />
+<hkern u1="(" u2="j" k="-150" />
+<hkern u1="(" u2="&#x178;" k="-50" />
+<hkern u1="*" u2="A" k="64" />
+<hkern u1="*" u2="J" k="14" />
+<hkern u1="*" u2="T" k="-3" />
+<hkern u1="*" u2="&#xc4;" k="64" />
+<hkern u1="*" u2="&#xc5;" k="64" />
+<hkern u1="*" u2="&#xc6;" k="64" />
+<hkern u1="*" u2="&#xc0;" k="64" />
+<hkern u1="*" u2="&#xc3;" k="64" />
+<hkern u1="*" u2="&#xc2;" k="64" />
+<hkern u1="*" u2="&#xc1;" k="64" />
+<hkern u1="&#x2c;" u2="0" k="34" />
+<hkern u1="&#x2c;" u2="1" k="67" />
+<hkern u1="&#x2c;" u2="2" k="-2" />
+<hkern u1="&#x2c;" u2="3" k="-2" />
+<hkern u1="&#x2c;" u2="4" k="40" />
+<hkern u1="&#x2c;" u2="5" k="-4" />
+<hkern u1="&#x2c;" u2="6" k="28" />
+<hkern u1="&#x2c;" u2="7" k="2" />
+<hkern u1="&#x2c;" u2="8" k="15" />
+<hkern u1="&#x2c;" u2="9" k="8" />
+<hkern u1="&#x2c;" u2="A" k="-30" />
+<hkern u1="&#x2c;" u2="C" k="38" />
+<hkern u1="&#x2c;" u2="G" k="38" />
+<hkern u1="&#x2c;" u2="O" k="38" />
+<hkern u1="&#x2c;" u2="Q" k="38" />
+<hkern u1="&#x2c;" u2="T" k="66" />
+<hkern u1="&#x2c;" u2="U" k="10" />
+<hkern u1="&#x2c;" u2="V" k="62" />
+<hkern u1="&#x2c;" u2="W" k="22" />
+<hkern u1="&#x2c;" u2="Y" k="50" />
+<hkern u1="&#x2c;" u2="a" k="20" />
+<hkern u1="&#x2c;" u2="c" k="20" />
+<hkern u1="&#x2c;" u2="d" k="20" />
+<hkern u1="&#x2c;" u2="e" k="20" />
+<hkern u1="&#x2c;" u2="f" k="8" />
+<hkern u1="&#x2c;" u2="m" k="20" />
+<hkern u1="&#x2c;" u2="n" k="20" />
+<hkern u1="&#x2c;" u2="o" k="20" />
+<hkern u1="&#x2c;" u2="p" k="20" />
+<hkern u1="&#x2c;" u2="q" k="20" />
+<hkern u1="&#x2c;" u2="r" k="20" />
+<hkern u1="&#x2c;" u2="t" k="40" />
+<hkern u1="&#x2c;" u2="u" k="20" />
+<hkern u1="&#x2c;" u2="v" k="30" />
+<hkern u1="&#x2c;" u2="w" k="10" />
+<hkern u1="&#x2c;" u2="y" k="30" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="38" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="10" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="38" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="20" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="38" />
+<hkern u1="&#x2c;" u2="&#x152;" k="38" />
+<hkern u1="&#x2c;" u2="&#x153;" k="20" />
+<hkern u1="&#x2c;" u2="&#x178;" k="50" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="38" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="38" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="38" />
+<hkern u1="&#x2c;" u2="&#xda;" k="10" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2c;" u2="&#x131;" k="20" />
+<hkern u1="-" u2="1" k="20" />
+<hkern u1="-" u2="7" k="30" />
+<hkern u1="-" u2="A" k="-10" />
+<hkern u1="-" u2="T" k="66" />
+<hkern u1="-" u2="V" k="50" />
+<hkern u1="-" u2="W" k="40" />
+<hkern u1="-" u2="X" k="30" />
+<hkern u1="-" u2="Y" k="50" />
+<hkern u1="-" u2="Z" k="10" />
+<hkern u1="-" u2="a" k="10" />
+<hkern u1="-" u2="c" k="10" />
+<hkern u1="-" u2="d" k="10" />
+<hkern u1="-" u2="e" k="10" />
+<hkern u1="-" u2="o" k="10" />
+<hkern u1="-" u2="q" k="10" />
+<hkern u1="-" u2="v" k="10" />
+<hkern u1="-" u2="x" k="30" />
+<hkern u1="-" u2="y" k="10" />
+<hkern u1="-" u2="z" k="26" />
+<hkern u1="-" u2="&#xc4;" k="-10" />
+<hkern u1="-" u2="&#xc5;" k="-10" />
+<hkern u1="-" u2="&#xe7;" k="10" />
+<hkern u1="-" u2="&#xc6;" k="-10" />
+<hkern u1="-" u2="&#xe6;" k="10" />
+<hkern u1="-" u2="&#xc0;" k="-10" />
+<hkern u1="-" u2="&#xc3;" k="-10" />
+<hkern u1="-" u2="&#x153;" k="10" />
+<hkern u1="-" u2="&#x178;" k="50" />
+<hkern u1="-" u2="&#xc2;" k="-10" />
+<hkern u1="-" u2="&#xc1;" k="-10" />
+<hkern u1="." u2="0" k="34" />
+<hkern u1="." u2="1" k="67" />
+<hkern u1="." u2="2" k="-2" />
+<hkern u1="." u2="3" k="-2" />
+<hkern u1="." u2="4" k="40" />
+<hkern u1="." u2="5" k="-4" />
+<hkern u1="." u2="6" k="28" />
+<hkern u1="." u2="7" k="2" />
+<hkern u1="." u2="8" k="15" />
+<hkern u1="." u2="9" k="8" />
+<hkern u1="." u2="A" k="-30" />
+<hkern u1="." u2="C" k="38" />
+<hkern u1="." u2="G" k="38" />
+<hkern u1="." u2="O" k="38" />
+<hkern u1="." u2="Q" k="38" />
+<hkern u1="." u2="T" k="66" />
+<hkern u1="." u2="U" k="10" />
+<hkern u1="." u2="V" k="62" />
+<hkern u1="." u2="W" k="22" />
+<hkern u1="." u2="Y" k="50" />
+<hkern u1="." u2="a" k="20" />
+<hkern u1="." u2="c" k="20" />
+<hkern u1="." u2="d" k="20" />
+<hkern u1="." u2="e" k="20" />
+<hkern u1="." u2="f" k="8" />
+<hkern u1="." u2="m" k="20" />
+<hkern u1="." u2="n" k="20" />
+<hkern u1="." u2="o" k="20" />
+<hkern u1="." u2="p" k="20" />
+<hkern u1="." u2="q" k="20" />
+<hkern u1="." u2="r" k="20" />
+<hkern u1="." u2="t" k="40" />
+<hkern u1="." u2="u" k="20" />
+<hkern u1="." u2="v" k="30" />
+<hkern u1="." u2="w" k="10" />
+<hkern u1="." u2="y" k="30" />
+<hkern u1="." u2="&#xc4;" k="-30" />
+<hkern u1="." u2="&#xc5;" k="-30" />
+<hkern u1="." u2="&#xd6;" k="38" />
+<hkern u1="." u2="&#xdc;" k="10" />
+<hkern u1="." u2="&#xe7;" k="20" />
+<hkern u1="." u2="&#xc6;" k="-30" />
+<hkern u1="." u2="&#xd8;" k="38" />
+<hkern u1="." u2="&#xe6;" k="20" />
+<hkern u1="." u2="&#xc0;" k="-30" />
+<hkern u1="." u2="&#xc3;" k="-30" />
+<hkern u1="." u2="&#xd5;" k="38" />
+<hkern u1="." u2="&#x152;" k="38" />
+<hkern u1="." u2="&#x153;" k="20" />
+<hkern u1="." u2="&#x178;" k="50" />
+<hkern u1="." u2="&#xc2;" k="-30" />
+<hkern u1="." u2="&#xc1;" k="-30" />
+<hkern u1="." u2="&#xd3;" k="38" />
+<hkern u1="." u2="&#xd4;" k="38" />
+<hkern u1="." u2="&#xd2;" k="38" />
+<hkern u1="." u2="&#xda;" k="10" />
+<hkern u1="." u2="&#xdb;" k="10" />
+<hkern u1="." u2="&#xd9;" k="10" />
+<hkern u1="." u2="&#x131;" k="20" />
+<hkern u1="/" u2="1" k="-20" />
+<hkern u1="/" u2="2" k="-3" />
+<hkern u1="/" u2="3" k="-4" />
+<hkern u1="/" u2="4" k="16" />
+<hkern u1="/" u2="5" k="-4" />
+<hkern u1="/" u2="7" k="-8" />
+<hkern u1="/" u2="B" k="-2" />
+<hkern u1="/" u2="C" k="-8" />
+<hkern u1="/" u2="D" k="-2" />
+<hkern u1="/" u2="E" k="-2" />
+<hkern u1="/" u2="F" k="-2" />
+<hkern u1="/" u2="G" k="-8" />
+<hkern u1="/" u2="H" k="-2" />
+<hkern u1="/" u2="I" k="-2" />
+<hkern u1="/" u2="J" k="10" />
+<hkern u1="/" u2="K" k="-2" />
+<hkern u1="/" u2="L" k="-2" />
+<hkern u1="/" u2="M" k="-2" />
+<hkern u1="/" u2="N" k="-2" />
+<hkern u1="/" u2="O" k="-8" />
+<hkern u1="/" u2="P" k="-2" />
+<hkern u1="/" u2="Q" k="-8" />
+<hkern u1="/" u2="R" k="-2" />
+<hkern u1="/" u2="a" k="10" />
+<hkern u1="/" u2="c" k="10" />
+<hkern u1="/" u2="d" k="10" />
+<hkern u1="/" u2="e" k="10" />
+<hkern u1="/" u2="g" k="12" />
+<hkern u1="/" u2="o" k="10" />
+<hkern u1="/" u2="q" k="10" />
+<hkern u1="/" u2="&#xc9;" k="-2" />
+<hkern u1="/" u2="&#xd1;" k="-2" />
+<hkern u1="/" u2="&#xd6;" k="-8" />
+<hkern u1="/" u2="&#xe7;" k="10" />
+<hkern u1="/" u2="&#xd8;" k="-8" />
+<hkern u1="/" u2="&#xe6;" k="10" />
+<hkern u1="/" u2="&#xd5;" k="-8" />
+<hkern u1="/" u2="&#x152;" k="-8" />
+<hkern u1="/" u2="&#x153;" k="10" />
+<hkern u1="/" u2="&#xca;" k="-2" />
+<hkern u1="/" u2="&#xcb;" k="-2" />
+<hkern u1="/" u2="&#xc8;" k="-2" />
+<hkern u1="/" u2="&#xcd;" k="-2" />
+<hkern u1="/" u2="&#xce;" k="-2" />
+<hkern u1="/" u2="&#xcf;" k="-2" />
+<hkern u1="/" u2="&#xcc;" k="-2" />
+<hkern u1="/" u2="&#xd3;" k="-8" />
+<hkern u1="/" u2="&#xd4;" k="-8" />
+<hkern u1="/" u2="&#xd2;" k="-8" />
+<hkern u1="0" u2="7" k="20" />
+<hkern u1="0" u2="&#x2026;" k="34" />
+<hkern u1="0" u2="&#x201a;" k="34" />
+<hkern u1="0" u2="&#x201e;" k="34" />
+<hkern u1="1" u2="7" k="-2" />
+<hkern u1="1" u2="&#x2026;" k="-2" />
+<hkern u1="1" u2="&#x201a;" k="-2" />
+<hkern u1="1" u2="&#x201e;" k="-2" />
+<hkern u1="2" u2="4" k="22" />
+<hkern u1="2" u2="&#x2013;" k="10" />
+<hkern u1="2" u2="&#x2014;" k="10" />
+<hkern u1="3" u2="7" k="15" />
+<hkern u1="3" u2="&#x2026;" k="16" />
+<hkern u1="3" u2="&#x201a;" k="16" />
+<hkern u1="3" u2="&#x201e;" k="16" />
+<hkern u1="4" u2="7" k="12" />
+<hkern u1="4" u2="&#xb0;" k="32" />
+<hkern u1="4" u2="&#x2122;" k="48" />
+<hkern u1="4" u2="&#x2026;" k="-4" />
+<hkern u1="4" u2="&#x201c;" k="8" />
+<hkern u1="4" u2="&#x201d;" k="6" />
+<hkern u1="4" u2="&#x2018;" k="8" />
+<hkern u1="4" u2="&#x2019;" k="6" />
+<hkern u1="4" u2="&#x201a;" k="-4" />
+<hkern u1="4" u2="&#x201e;" k="-4" />
+<hkern u1="5" u2="7" k="6" />
+<hkern u1="5" u2="&#x2026;" k="10" />
+<hkern u1="5" u2="&#x201a;" k="10" />
+<hkern u1="5" u2="&#x201e;" k="10" />
+<hkern u1="6" u2="7" k="10" />
+<hkern u1="6" u2="&#x2026;" k="8" />
+<hkern u1="6" u2="&#x201a;" k="8" />
+<hkern u1="6" u2="&#x201e;" k="8" />
+<hkern u1="7" u2="8" k="1" />
+<hkern u1="7" u2="9" k="-10" />
+<hkern u1="7" u2=":" k="20" />
+<hkern u1="7" u2=";" k="20" />
+<hkern u1="7" u2="?" k="-2" />
+<hkern u1="7" u2="]" k="-2" />
+<hkern u1="7" u2="}" k="-2" />
+<hkern u1="7" u2="&#xa2;" k="40" />
+<hkern u1="7" u2="&#x2026;" k="80" />
+<hkern u1="7" u2="&#x2013;" k="40" />
+<hkern u1="7" u2="&#x2014;" k="40" />
+<hkern u1="7" u2="&#x201a;" k="80" />
+<hkern u1="7" u2="&#x201e;" k="80" />
+<hkern u1="8" u2="&#x2026;" k="15" />
+<hkern u1="8" u2="&#x201a;" k="15" />
+<hkern u1="8" u2="&#x201e;" k="15" />
+<hkern u1="9" u2="&#x2026;" k="28" />
+<hkern u1="9" u2="&#x201a;" k="28" />
+<hkern u1="9" u2="&#x201e;" k="28" />
+<hkern u1=":" u2="T" k="50" />
+<hkern u1=":" u2="V" k="20" />
+<hkern u1=":" u2="W" k="20" />
+<hkern u1=":" u2="Y" k="30" />
+<hkern u1=":" u2="a" k="10" />
+<hkern u1=":" u2="c" k="10" />
+<hkern u1=":" u2="d" k="10" />
+<hkern u1=":" u2="e" k="10" />
+<hkern u1=":" u2="o" k="10" />
+<hkern u1=":" u2="q" k="10" />
+<hkern u1=":" u2="&#xe7;" k="10" />
+<hkern u1=":" u2="&#xe6;" k="10" />
+<hkern u1=":" u2="&#x153;" k="10" />
+<hkern u1=":" u2="&#x178;" k="30" />
+<hkern u1=";" u2="T" k="50" />
+<hkern u1=";" u2="V" k="20" />
+<hkern u1=";" u2="W" k="20" />
+<hkern u1=";" u2="Y" k="30" />
+<hkern u1=";" u2="a" k="10" />
+<hkern u1=";" u2="c" k="10" />
+<hkern u1=";" u2="d" k="10" />
+<hkern u1=";" u2="e" k="10" />
+<hkern u1=";" u2="o" k="10" />
+<hkern u1=";" u2="q" k="10" />
+<hkern u1=";" u2="&#xe7;" k="10" />
+<hkern u1=";" u2="&#xe6;" k="10" />
+<hkern u1=";" u2="&#x153;" k="10" />
+<hkern u1=";" u2="&#x178;" k="30" />
+<hkern u1="@" u2="A" k="10" />
+<hkern u1="@" u2="T" k="30" />
+<hkern u1="@" u2="V" k="2" />
+<hkern u1="@" u2="W" k="20" />
+<hkern u1="@" u2="X" k="6" />
+<hkern u1="@" u2="Y" k="30" />
+<hkern u1="@" u2="g" k="-2" />
+<hkern u1="@" u2="&#xc4;" k="10" />
+<hkern u1="@" u2="&#xc5;" k="10" />
+<hkern u1="@" u2="&#xc6;" k="10" />
+<hkern u1="@" u2="&#xc0;" k="10" />
+<hkern u1="@" u2="&#xc3;" k="10" />
+<hkern u1="@" u2="&#x178;" k="30" />
+<hkern u1="@" u2="&#xc2;" k="10" />
+<hkern u1="@" u2="&#xc1;" k="10" />
+<hkern u1="A" u2="C" k="4" />
+<hkern u1="A" u2="G" k="4" />
+<hkern u1="A" u2="J" k="-2" />
+<hkern u1="A" u2="O" k="4" />
+<hkern u1="A" u2="Q" k="4" />
+<hkern u1="A" u2="S" k="-1" />
+<hkern u1="A" u2="T" k="104" />
+<hkern u1="A" u2="V" k="16" />
+<hkern u1="A" u2="W" k="13" />
+<hkern u1="A" u2="X" k="-2" />
+<hkern u1="A" u2="Y" k="52" />
+<hkern u1="A" u2="a" k="-2" />
+<hkern u1="A" u2="c" k="1" />
+<hkern u1="A" u2="d" k="1" />
+<hkern u1="A" u2="e" k="1" />
+<hkern u1="A" u2="f" k="8" />
+<hkern u1="A" u2="g" k="3" />
+<hkern u1="A" u2="m" k="1" />
+<hkern u1="A" u2="n" k="1" />
+<hkern u1="A" u2="o" k="1" />
+<hkern u1="A" u2="p" k="1" />
+<hkern u1="A" u2="q" k="1" />
+<hkern u1="A" u2="r" k="1" />
+<hkern u1="A" u2="s" k="-3" />
+<hkern u1="A" u2="t" k="1" />
+<hkern u1="A" u2="u" k="1" />
+<hkern u1="A" u2="v" k="14" />
+<hkern u1="A" u2="w" k="10" />
+<hkern u1="A" u2="x" k="-2" />
+<hkern u1="A" u2="y" k="14" />
+<hkern u1="A" u2="z" k="-2" />
+<hkern u1="A" u2="&#xc4;" k="-2" />
+<hkern u1="A" u2="&#xc5;" k="-2" />
+<hkern u1="A" u2="&#xd6;" k="4" />
+<hkern u1="A" u2="&#xe7;" k="1" />
+<hkern u1="A" u2="&#xae;" k="2" />
+<hkern u1="A" u2="&#xa9;" k="2" />
+<hkern u1="A" u2="&#x2122;" k="14" />
+<hkern u1="A" u2="&#xc6;" k="-2" />
+<hkern u1="A" u2="&#xd8;" k="4" />
+<hkern u1="A" u2="&#x3c0;" k="2" />
+<hkern u1="A" u2="&#xe6;" k="1" />
+<hkern u1="A" u2="&#x2026;" k="-6" />
+<hkern u1="A" u2="&#xc0;" k="-2" />
+<hkern u1="A" u2="&#xc3;" k="-2" />
+<hkern u1="A" u2="&#xd5;" k="4" />
+<hkern u1="A" u2="&#x152;" k="4" />
+<hkern u1="A" u2="&#x153;" k="1" />
+<hkern u1="A" u2="&#x2013;" k="-2" />
+<hkern u1="A" u2="&#x2014;" k="-2" />
+<hkern u1="A" u2="&#x201c;" k="12" />
+<hkern u1="A" u2="&#x201d;" k="6" />
+<hkern u1="A" u2="&#x2018;" k="12" />
+<hkern u1="A" u2="&#x2019;" k="6" />
+<hkern u1="A" u2="&#x178;" k="11" />
+<hkern u1="A" u2="&#x201a;" k="-6" />
+<hkern u1="A" u2="&#x201e;" k="-6" />
+<hkern u1="A" u2="&#xc2;" k="-2" />
+<hkern u1="A" u2="&#xc1;" k="-2" />
+<hkern u1="A" u2="&#xd3;" k="4" />
+<hkern u1="A" u2="&#xd4;" k="4" />
+<hkern u1="A" u2="&#xd2;" k="4" />
+<hkern u1="A" u2="&#x131;" k="1" />
+<hkern u1="B" u2="V" k="5" />
+<hkern u1="B" u2="W" k="20" />
+<hkern u1="B" u2="a" k="-2" />
+<hkern u1="B" u2="c" k="-2" />
+<hkern u1="B" u2="d" k="-2" />
+<hkern u1="B" u2="e" k="-2" />
+<hkern u1="B" u2="o" k="-2" />
+<hkern u1="B" u2="q" k="-2" />
+<hkern u1="B" u2="v" k="3" />
+<hkern u1="B" u2="w" k="2" />
+<hkern u1="B" u2="y" k="3" />
+<hkern u1="B" u2="&#xe7;" k="-2" />
+<hkern u1="B" u2="&#xe6;" k="-2" />
+<hkern u1="B" u2="&#x153;" k="-2" />
+<hkern u1="C" u2="G" k="2" />
+<hkern u1="C" u2="O" k="2" />
+<hkern u1="C" u2="Q" k="2" />
+<hkern u1="C" u2="V" k="-20" />
+<hkern u1="C" u2="X" k="-10" />
+<hkern u1="C" u2="Y" k="-20" />
+<hkern u1="C" u2="a" k="-4" />
+<hkern u1="C" u2="c" k="-4" />
+<hkern u1="C" u2="d" k="-4" />
+<hkern u1="C" u2="e" k="-4" />
+<hkern u1="C" u2="o" k="-4" />
+<hkern u1="C" u2="q" k="-4" />
+<hkern u1="C" u2="t" k="5" />
+<hkern u1="C" u2="v" k="5" />
+<hkern u1="C" u2="w" k="5" />
+<hkern u1="C" u2="y" k="5" />
+<hkern u1="C" u2="z" k="10" />
+<hkern u1="C" u2="&#xd6;" k="2" />
+<hkern u1="C" u2="&#xe7;" k="-4" />
+<hkern u1="C" u2="&#xd8;" k="2" />
+<hkern u1="C" u2="&#xe6;" k="-4" />
+<hkern u1="C" u2="&#xd5;" k="2" />
+<hkern u1="C" u2="&#x152;" k="2" />
+<hkern u1="C" u2="&#x153;" k="-4" />
+<hkern u1="C" u2="&#x178;" k="-20" />
+<hkern u1="C" u2="&#xd3;" k="2" />
+<hkern u1="C" u2="&#xd4;" k="2" />
+<hkern u1="C" u2="&#xd2;" k="2" />
+<hkern u1="D" u2="J" k="39" />
+<hkern u1="D" u2="T" k="27" />
+<hkern u1="D" u2="V" k="20" />
+<hkern u1="D" u2="W" k="38" />
+<hkern u1="D" u2="X" k="26" />
+<hkern u1="D" u2="Y" k="23" />
+<hkern u1="D" u2="Z" k="38" />
+<hkern u1="D" u2="a" k="4" />
+<hkern u1="D" u2="b" k="8" />
+<hkern u1="D" u2="c" k="4" />
+<hkern u1="D" u2="d" k="4" />
+<hkern u1="D" u2="e" k="4" />
+<hkern u1="D" u2="h" k="8" />
+<hkern u1="D" u2="k" k="8" />
+<hkern u1="D" u2="l" k="8" />
+<hkern u1="D" u2="m" k="4" />
+<hkern u1="D" u2="n" k="4" />
+<hkern u1="D" u2="o" k="4" />
+<hkern u1="D" u2="p" k="4" />
+<hkern u1="D" u2="q" k="4" />
+<hkern u1="D" u2="r" k="4" />
+<hkern u1="D" u2="u" k="4" />
+<hkern u1="D" u2="x" k="10" />
+<hkern u1="D" u2="z" k="10" />
+<hkern u1="D" u2="&#xc4;" k="20" />
+<hkern u1="D" u2="&#xc5;" k="20" />
+<hkern u1="D" u2="&#xe7;" k="4" />
+<hkern u1="D" u2="&#xc6;" k="20" />
+<hkern u1="D" u2="&#xe6;" k="4" />
+<hkern u1="D" u2="&#x2026;" k="38" />
+<hkern u1="D" u2="&#xc0;" k="20" />
+<hkern u1="D" u2="&#xc3;" k="20" />
+<hkern u1="D" u2="&#x153;" k="4" />
+<hkern u1="D" u2="&#x201c;" k="20" />
+<hkern u1="D" u2="&#x2018;" k="20" />
+<hkern u1="D" u2="&#x178;" k="23" />
+<hkern u1="D" u2="&#x201a;" k="38" />
+<hkern u1="D" u2="&#x201e;" k="38" />
+<hkern u1="D" u2="&#xc2;" k="20" />
+<hkern u1="D" u2="&#xc1;" k="20" />
+<hkern u1="D" u2="&#x131;" k="4" />
+<hkern u1="E" u2="G" k="5" />
+<hkern u1="E" u2="J" k="-1" />
+<hkern u1="E" u2="O" k="5" />
+<hkern u1="E" u2="Q" k="5" />
+<hkern u1="E" u2="T" k="-13" />
+<hkern u1="E" u2="V" k="-2" />
+<hkern u1="E" u2="W" k="1" />
+<hkern u1="E" u2="a" k="20" />
+<hkern u1="E" u2="b" k="1" />
+<hkern u1="E" u2="c" k="24" />
+<hkern u1="E" u2="d" k="24" />
+<hkern u1="E" u2="e" k="24" />
+<hkern u1="E" u2="f" k="10" />
+<hkern u1="E" u2="g" k="8" />
+<hkern u1="E" u2="h" k="1" />
+<hkern u1="E" u2="k" k="1" />
+<hkern u1="E" u2="l" k="1" />
+<hkern u1="E" u2="m" k="2" />
+<hkern u1="E" u2="n" k="2" />
+<hkern u1="E" u2="o" k="24" />
+<hkern u1="E" u2="p" k="2" />
+<hkern u1="E" u2="q" k="24" />
+<hkern u1="E" u2="r" k="2" />
+<hkern u1="E" u2="u" k="2" />
+<hkern u1="E" u2="v" k="20" />
+<hkern u1="E" u2="y" k="20" />
+<hkern u1="E" u2="&#xd6;" k="5" />
+<hkern u1="E" u2="&#xe7;" k="24" />
+<hkern u1="E" u2="&#xae;" k="20" />
+<hkern u1="E" u2="&#xa9;" k="20" />
+<hkern u1="E" u2="&#xd8;" k="5" />
+<hkern u1="E" u2="&#x3c0;" k="20" />
+<hkern u1="E" u2="&#xe6;" k="24" />
+<hkern u1="E" u2="&#xab;" k="4" />
+<hkern u1="E" u2="&#xd5;" k="5" />
+<hkern u1="E" u2="&#x152;" k="5" />
+<hkern u1="E" u2="&#x153;" k="24" />
+<hkern u1="E" u2="&#x2039;" k="4" />
+<hkern u1="E" u2="&#xd3;" k="5" />
+<hkern u1="E" u2="&#xd4;" k="5" />
+<hkern u1="E" u2="&#xd2;" k="5" />
+<hkern u1="E" u2="&#x131;" k="2" />
+<hkern u1="F" u2="&amp;" k="38" />
+<hkern u1="F" u2="G" k="4" />
+<hkern u1="F" u2="J" k="90" />
+<hkern u1="F" u2="O" k="4" />
+<hkern u1="F" u2="Q" k="4" />
+<hkern u1="F" u2="T" k="-26" />
+<hkern u1="F" u2="V" k="-20" />
+<hkern u1="F" u2="W" k="-5" />
+<hkern u1="F" u2="X" k="-10" />
+<hkern u1="F" u2="Y" k="-25" />
+<hkern u1="F" u2="a" k="50" />
+<hkern u1="F" u2="b" k="10" />
+<hkern u1="F" u2="c" k="50" />
+<hkern u1="F" u2="d" k="50" />
+<hkern u1="F" u2="e" k="50" />
+<hkern u1="F" u2="f" k="20" />
+<hkern u1="F" u2="g" k="30" />
+<hkern u1="F" u2="h" k="10" />
+<hkern u1="F" u2="i" k="10" />
+<hkern u1="F" u2="j" k="10" />
+<hkern u1="F" u2="k" k="10" />
+<hkern u1="F" u2="l" k="10" />
+<hkern u1="F" u2="m" k="40" />
+<hkern u1="F" u2="n" k="40" />
+<hkern u1="F" u2="o" k="50" />
+<hkern u1="F" u2="p" k="40" />
+<hkern u1="F" u2="q" k="50" />
+<hkern u1="F" u2="r" k="40" />
+<hkern u1="F" u2="s" k="40" />
+<hkern u1="F" u2="t" k="20" />
+<hkern u1="F" u2="u" k="40" />
+<hkern u1="F" u2="v" k="20" />
+<hkern u1="F" u2="w" k="20" />
+<hkern u1="F" u2="x" k="40" />
+<hkern u1="F" u2="y" k="20" />
+<hkern u1="F" u2="z" k="40" />
+<hkern u1="F" u2="&#xc4;" k="50" />
+<hkern u1="F" u2="&#xc5;" k="50" />
+<hkern u1="F" u2="&#xd6;" k="4" />
+<hkern u1="F" u2="&#xe7;" k="50" />
+<hkern u1="F" u2="&#xae;" k="20" />
+<hkern u1="F" u2="&#xa9;" k="20" />
+<hkern u1="F" u2="&#x2122;" k="-4" />
+<hkern u1="F" u2="&#xc6;" k="50" />
+<hkern u1="F" u2="&#xd8;" k="4" />
+<hkern u1="F" u2="&#x3c0;" k="20" />
+<hkern u1="F" u2="&#xe6;" k="50" />
+<hkern u1="F" u2="&#xab;" k="7" />
+<hkern u1="F" u2="&#x2026;" k="90" />
+<hkern u1="F" u2="&#xc0;" k="50" />
+<hkern u1="F" u2="&#xc3;" k="50" />
+<hkern u1="F" u2="&#xd5;" k="4" />
+<hkern u1="F" u2="&#x152;" k="4" />
+<hkern u1="F" u2="&#x153;" k="50" />
+<hkern u1="F" u2="&#x2013;" k="46" />
+<hkern u1="F" u2="&#x2014;" k="46" />
+<hkern u1="F" u2="&#x178;" k="-25" />
+<hkern u1="F" u2="&#x2039;" k="7" />
+<hkern u1="F" u2="&#x201a;" k="90" />
+<hkern u1="F" u2="&#x201e;" k="90" />
+<hkern u1="F" u2="&#xc2;" k="50" />
+<hkern u1="F" u2="&#xc1;" k="50" />
+<hkern u1="F" u2="&#xd3;" k="4" />
+<hkern u1="F" u2="&#xd4;" k="4" />
+<hkern u1="F" u2="&#xd2;" k="4" />
+<hkern u1="F" u2="&#x131;" k="40" />
+<hkern u1="G" u2="T" k="25" />
+<hkern u1="G" u2="V" k="14" />
+<hkern u1="G" u2="W" k="12" />
+<hkern u1="G" u2="Y" k="17" />
+<hkern u1="G" u2="t" k="10" />
+<hkern u1="G" u2="v" k="12" />
+<hkern u1="G" u2="w" k="3" />
+<hkern u1="G" u2="y" k="12" />
+<hkern u1="G" u2="&#x2122;" k="30" />
+<hkern u1="G" u2="&#x2026;" k="-20" />
+<hkern u1="G" u2="&#x178;" k="17" />
+<hkern u1="G" u2="&#x201a;" k="-20" />
+<hkern u1="G" u2="&#x201e;" k="-20" />
+<hkern u1="H" u2="v" k="10" />
+<hkern u1="H" u2="y" k="10" />
+<hkern u1="I" u2="v" k="10" />
+<hkern u1="I" u2="y" k="10" />
+<hkern u1="J" u2="&#xc4;" k="4" />
+<hkern u1="J" u2="&#xc5;" k="4" />
+<hkern u1="J" u2="&#xc6;" k="4" />
+<hkern u1="J" u2="&#x2026;" k="10" />
+<hkern u1="J" u2="&#xc0;" k="4" />
+<hkern u1="J" u2="&#xc3;" k="4" />
+<hkern u1="J" u2="&#x201a;" k="10" />
+<hkern u1="J" u2="&#x201e;" k="10" />
+<hkern u1="J" u2="&#xc2;" k="4" />
+<hkern u1="J" u2="&#xc1;" k="4" />
+<hkern u1="K" u2="&amp;" k="20" />
+<hkern u1="K" u2="O" k="24" />
+<hkern u1="K" u2="Q" k="24" />
+<hkern u1="K" u2="S" k="5" />
+<hkern u1="K" u2="T" k="-5" />
+<hkern u1="K" u2="U" k="10" />
+<hkern u1="K" u2="V" k="-10" />
+<hkern u1="K" u2="W" k="13" />
+<hkern u1="K" u2="X" k="-9" />
+<hkern u1="K" u2="Y" k="-12" />
+<hkern u1="K" u2="Z" k="5" />
+<hkern u1="K" u2="a" k="15" />
+<hkern u1="K" u2="c" k="20" />
+<hkern u1="K" u2="d" k="20" />
+<hkern u1="K" u2="e" k="20" />
+<hkern u1="K" u2="f" k="5" />
+<hkern u1="K" u2="g" k="10" />
+<hkern u1="K" u2="o" k="20" />
+<hkern u1="K" u2="q" k="20" />
+<hkern u1="K" u2="t" k="10" />
+<hkern u1="K" u2="v" k="23" />
+<hkern u1="K" u2="w" k="5" />
+<hkern u1="K" u2="x" k="2" />
+<hkern u1="K" u2="y" k="23" />
+<hkern u1="K" u2="&#xd6;" k="24" />
+<hkern u1="K" u2="&#xdc;" k="10" />
+<hkern u1="K" u2="&#xe7;" k="20" />
+<hkern u1="K" u2="&#xae;" k="30" />
+<hkern u1="K" u2="&#xa9;" k="30" />
+<hkern u1="K" u2="&#xd8;" k="24" />
+<hkern u1="K" u2="&#x3c0;" k="30" />
+<hkern u1="K" u2="&#xe6;" k="20" />
+<hkern u1="K" u2="&#xd5;" k="24" />
+<hkern u1="K" u2="&#x152;" k="24" />
+<hkern u1="K" u2="&#x153;" k="20" />
+<hkern u1="K" u2="&#x2013;" k="38" />
+<hkern u1="K" u2="&#x2014;" k="38" />
+<hkern u1="K" u2="&#x178;" k="-12" />
+<hkern u1="K" u2="&#xd3;" k="24" />
+<hkern u1="K" u2="&#xd4;" k="24" />
+<hkern u1="K" u2="&#xd2;" k="24" />
+<hkern u1="K" u2="&#xda;" k="10" />
+<hkern u1="K" u2="&#xdb;" k="10" />
+<hkern u1="K" u2="&#xd9;" k="10" />
+<hkern u1="L" u2="O" k="45" />
+<hkern u1="L" u2="Q" k="45" />
+<hkern u1="L" u2="T" k="99" />
+<hkern u1="L" u2="U" k="15" />
+<hkern u1="L" u2="V" k="90" />
+<hkern u1="L" u2="W" k="70" />
+<hkern u1="L" u2="Y" k="83" />
+<hkern u1="L" u2="Z" k="-1" />
+<hkern u1="L" u2="\" k="50" />
+<hkern u1="L" u2="a" k="15" />
+<hkern u1="L" u2="c" k="18" />
+<hkern u1="L" u2="d" k="18" />
+<hkern u1="L" u2="e" k="18" />
+<hkern u1="L" u2="o" k="18" />
+<hkern u1="L" u2="q" k="18" />
+<hkern u1="L" u2="v" k="44" />
+<hkern u1="L" u2="w" k="14" />
+<hkern u1="L" u2="y" k="44" />
+<hkern u1="L" u2="&#xc4;" k="-20" />
+<hkern u1="L" u2="&#xc5;" k="-20" />
+<hkern u1="L" u2="&#xd6;" k="45" />
+<hkern u1="L" u2="&#xdc;" k="15" />
+<hkern u1="L" u2="&#xe7;" k="18" />
+<hkern u1="L" u2="&#xae;" k="50" />
+<hkern u1="L" u2="&#xa9;" k="50" />
+<hkern u1="L" u2="&#x2122;" k="70" />
+<hkern u1="L" u2="&#xc6;" k="-20" />
+<hkern u1="L" u2="&#xd8;" k="45" />
+<hkern u1="L" u2="&#x3c0;" k="50" />
+<hkern u1="L" u2="&#xe6;" k="15" />
+<hkern u1="L" u2="&#xab;" k="4" />
+<hkern u1="L" u2="&#xc0;" k="-20" />
+<hkern u1="L" u2="&#xc3;" k="-20" />
+<hkern u1="L" u2="&#xd5;" k="45" />
+<hkern u1="L" u2="&#x152;" k="45" />
+<hkern u1="L" u2="&#x153;" k="18" />
+<hkern u1="L" u2="&#x201c;" k="100" />
+<hkern u1="L" u2="&#x201d;" k="62" />
+<hkern u1="L" u2="&#x2018;" k="100" />
+<hkern u1="L" u2="&#x2019;" k="62" />
+<hkern u1="L" u2="&#x178;" k="83" />
+<hkern u1="L" u2="&#x2039;" k="4" />
+<hkern u1="L" u2="&#xc2;" k="-20" />
+<hkern u1="L" u2="&#xc1;" k="-20" />
+<hkern u1="L" u2="&#xd3;" k="45" />
+<hkern u1="L" u2="&#xd4;" k="45" />
+<hkern u1="L" u2="&#xd2;" k="45" />
+<hkern u1="L" u2="&#xda;" k="15" />
+<hkern u1="L" u2="&#xdb;" k="15" />
+<hkern u1="L" u2="&#xd9;" k="15" />
+<hkern u1="M" u2="v" k="10" />
+<hkern u1="M" u2="y" k="10" />
+<hkern u1="N" u2="v" k="10" />
+<hkern u1="N" u2="y" k="10" />
+<hkern u1="O" u2="T" k="27" />
+<hkern u1="O" u2="V" k="20" />
+<hkern u1="O" u2="W" k="38" />
+<hkern u1="O" u2="X" k="26" />
+<hkern u1="O" u2="Y" k="23" />
+<hkern u1="O" u2="Z" k="38" />
+<hkern u1="O" u2="a" k="4" />
+<hkern u1="O" u2="b" k="8" />
+<hkern u1="O" u2="c" k="4" />
+<hkern u1="O" u2="d" k="4" />
+<hkern u1="O" u2="e" k="4" />
+<hkern u1="O" u2="h" k="8" />
+<hkern u1="O" u2="k" k="8" />
+<hkern u1="O" u2="l" k="8" />
+<hkern u1="O" u2="m" k="4" />
+<hkern u1="O" u2="n" k="4" />
+<hkern u1="O" u2="o" k="4" />
+<hkern u1="O" u2="p" k="4" />
+<hkern u1="O" u2="q" k="4" />
+<hkern u1="O" u2="r" k="4" />
+<hkern u1="O" u2="u" k="4" />
+<hkern u1="O" u2="x" k="10" />
+<hkern u1="O" u2="z" k="10" />
+<hkern u1="O" u2="&#xc4;" k="20" />
+<hkern u1="O" u2="&#xc5;" k="20" />
+<hkern u1="O" u2="&#xe7;" k="4" />
+<hkern u1="O" u2="&#xc6;" k="20" />
+<hkern u1="O" u2="&#xe6;" k="4" />
+<hkern u1="O" u2="&#x2026;" k="38" />
+<hkern u1="O" u2="&#xc0;" k="20" />
+<hkern u1="O" u2="&#xc3;" k="20" />
+<hkern u1="O" u2="&#x153;" k="4" />
+<hkern u1="O" u2="&#x201c;" k="20" />
+<hkern u1="O" u2="&#x2018;" k="20" />
+<hkern u1="O" u2="&#x178;" k="23" />
+<hkern u1="O" u2="&#x201a;" k="38" />
+<hkern u1="O" u2="&#x201e;" k="38" />
+<hkern u1="O" u2="&#xc2;" k="20" />
+<hkern u1="O" u2="&#xc1;" k="20" />
+<hkern u1="O" u2="&#x131;" k="4" />
+<hkern u1="P" u2="&amp;" k="18" />
+<hkern u1="P" u2="T" k="-2" />
+<hkern u1="P" u2="V" k="-19" />
+<hkern u1="P" u2="W" k="-2" />
+<hkern u1="P" u2="X" k="-2" />
+<hkern u1="P" u2="Y" k="-13" />
+<hkern u1="P" u2="Z" k="25" />
+<hkern u1="P" u2="a" k="10" />
+<hkern u1="P" u2="c" k="10" />
+<hkern u1="P" u2="d" k="10" />
+<hkern u1="P" u2="e" k="10" />
+<hkern u1="P" u2="f" k="-8" />
+<hkern u1="P" u2="o" k="10" />
+<hkern u1="P" u2="q" k="10" />
+<hkern u1="P" u2="t" k="-8" />
+<hkern u1="P" u2="v" k="-15" />
+<hkern u1="P" u2="w" k="-10" />
+<hkern u1="P" u2="x" k="-5" />
+<hkern u1="P" u2="y" k="-15" />
+<hkern u1="P" u2="&#xc4;" k="36" />
+<hkern u1="P" u2="&#xc5;" k="36" />
+<hkern u1="P" u2="&#xe7;" k="10" />
+<hkern u1="P" u2="&#xc6;" k="36" />
+<hkern u1="P" u2="&#xe6;" k="10" />
+<hkern u1="P" u2="&#x2026;" k="60" />
+<hkern u1="P" u2="&#xc0;" k="36" />
+<hkern u1="P" u2="&#xc3;" k="36" />
+<hkern u1="P" u2="&#x153;" k="10" />
+<hkern u1="P" u2="&#x178;" k="-13" />
+<hkern u1="P" u2="&#x201a;" k="60" />
+<hkern u1="P" u2="&#x201e;" k="60" />
+<hkern u1="P" u2="&#xc2;" k="36" />
+<hkern u1="P" u2="&#xc1;" k="36" />
+<hkern u1="Q" u2="T" k="27" />
+<hkern u1="Q" u2="V" k="20" />
+<hkern u1="Q" u2="W" k="38" />
+<hkern u1="Q" u2="X" k="16" />
+<hkern u1="Q" u2="Y" k="23" />
+<hkern u1="Q" u2="Z" k="38" />
+<hkern u1="Q" u2="a" k="4" />
+<hkern u1="Q" u2="b" k="8" />
+<hkern u1="Q" u2="c" k="4" />
+<hkern u1="Q" u2="d" k="4" />
+<hkern u1="Q" u2="e" k="4" />
+<hkern u1="Q" u2="h" k="8" />
+<hkern u1="Q" u2="k" k="8" />
+<hkern u1="Q" u2="l" k="8" />
+<hkern u1="Q" u2="m" k="4" />
+<hkern u1="Q" u2="n" k="4" />
+<hkern u1="Q" u2="o" k="4" />
+<hkern u1="Q" u2="p" k="4" />
+<hkern u1="Q" u2="q" k="4" />
+<hkern u1="Q" u2="r" k="4" />
+<hkern u1="Q" u2="u" k="4" />
+<hkern u1="Q" u2="x" k="10" />
+<hkern u1="Q" u2="z" k="10" />
+<hkern u1="Q" u2="&#xc4;" k="20" />
+<hkern u1="Q" u2="&#xc5;" k="20" />
+<hkern u1="Q" u2="&#xe7;" k="4" />
+<hkern u1="Q" u2="&#xc6;" k="20" />
+<hkern u1="Q" u2="&#xe6;" k="4" />
+<hkern u1="Q" u2="&#x2026;" k="38" />
+<hkern u1="Q" u2="&#xc0;" k="20" />
+<hkern u1="Q" u2="&#xc3;" k="20" />
+<hkern u1="Q" u2="&#x153;" k="4" />
+<hkern u1="Q" u2="&#x201c;" k="20" />
+<hkern u1="Q" u2="&#x2018;" k="20" />
+<hkern u1="Q" u2="&#x178;" k="23" />
+<hkern u1="Q" u2="&#x201a;" k="26" />
+<hkern u1="Q" u2="&#x201e;" k="26" />
+<hkern u1="Q" u2="&#xc2;" k="20" />
+<hkern u1="Q" u2="&#xc1;" k="20" />
+<hkern u1="Q" u2="&#x131;" k="4" />
+<hkern u1="R" u2="&amp;" k="-10" />
+<hkern u1="R" u2="T" k="8" />
+<hkern u1="R" u2="V" k="-15" />
+<hkern u1="R" u2="W" k="3" />
+<hkern u1="R" u2="X" k="-10" />
+<hkern u1="R" u2="Y" k="-13" />
+<hkern u1="R" u2="&#xd6;" k="1" />
+<hkern u1="R" u2="&#xd8;" k="1" />
+<hkern u1="R" u2="&#xd5;" k="1" />
+<hkern u1="R" u2="&#x152;" k="1" />
+<hkern u1="R" u2="&#x178;" k="-13" />
+<hkern u1="R" u2="&#xd3;" k="1" />
+<hkern u1="R" u2="&#xd4;" k="1" />
+<hkern u1="R" u2="&#xd2;" k="1" />
+<hkern u1="S" u2="V" k="-9" />
+<hkern u1="S" u2="W" k="2" />
+<hkern u1="S" u2="X" k="-5" />
+<hkern u1="S" u2="Y" k="-10" />
+<hkern u1="S" u2="&#xc4;" k="-5" />
+<hkern u1="S" u2="&#xc5;" k="-5" />
+<hkern u1="S" u2="&#xc6;" k="-5" />
+<hkern u1="S" u2="&#xc0;" k="-5" />
+<hkern u1="S" u2="&#xc3;" k="-5" />
+<hkern u1="S" u2="&#x178;" k="-10" />
+<hkern u1="S" u2="&#xc2;" k="-5" />
+<hkern u1="S" u2="&#xc1;" k="-5" />
+<hkern u1="T" u2="&amp;" k="70" />
+<hkern u1="T" u2="V" k="-20" />
+<hkern u1="T" u2="X" k="-10" />
+<hkern u1="T" u2="Y" k="-22" />
+<hkern u1="T" u2="Z" k="9" />
+<hkern u1="T" u2="\" k="-10" />
+<hkern u1="T" u2="a" k="85" />
+<hkern u1="T" u2="c" k="90" />
+<hkern u1="T" u2="d" k="90" />
+<hkern u1="T" u2="e" k="90" />
+<hkern u1="T" u2="f" k="10" />
+<hkern u1="T" u2="g" k="95" />
+<hkern u1="T" u2="m" k="68" />
+<hkern u1="T" u2="n" k="68" />
+<hkern u1="T" u2="o" k="90" />
+<hkern u1="T" u2="p" k="68" />
+<hkern u1="T" u2="q" k="90" />
+<hkern u1="T" u2="r" k="68" />
+<hkern u1="T" u2="s" k="90" />
+<hkern u1="T" u2="t" k="20" />
+<hkern u1="T" u2="u" k="68" />
+<hkern u1="T" u2="v" k="58" />
+<hkern u1="T" u2="w" k="50" />
+<hkern u1="T" u2="x" k="68" />
+<hkern u1="T" u2="y" k="58" />
+<hkern u1="T" u2="z" k="68" />
+<hkern u1="T" u2="&#xc4;" k="67" />
+<hkern u1="T" u2="&#xc5;" k="67" />
+<hkern u1="T" u2="&#xd6;" k="27" />
+<hkern u1="T" u2="&#xe7;" k="90" />
+<hkern u1="T" u2="&#xae;" k="30" />
+<hkern u1="T" u2="&#xa9;" k="30" />
+<hkern u1="T" u2="&#xc6;" k="67" />
+<hkern u1="T" u2="&#xd8;" k="27" />
+<hkern u1="T" u2="&#x3c0;" k="30" />
+<hkern u1="T" u2="&#xe6;" k="85" />
+<hkern u1="T" u2="&#xbf;" k="50" />
+<hkern u1="T" u2="&#xab;" k="110" />
+<hkern u1="T" u2="&#xbb;" k="40" />
+<hkern u1="T" u2="&#x2026;" k="66" />
+<hkern u1="T" u2="&#xc0;" k="67" />
+<hkern u1="T" u2="&#xc3;" k="67" />
+<hkern u1="T" u2="&#xd5;" k="27" />
+<hkern u1="T" u2="&#x152;" k="27" />
+<hkern u1="T" u2="&#x153;" k="90" />
+<hkern u1="T" u2="&#x2013;" k="66" />
+<hkern u1="T" u2="&#x2014;" k="66" />
+<hkern u1="T" u2="&#x178;" k="-22" />
+<hkern u1="T" u2="&#x2039;" k="110" />
+<hkern u1="T" u2="&#x203a;" k="40" />
+<hkern u1="T" u2="&#x201a;" k="66" />
+<hkern u1="T" u2="&#x201e;" k="66" />
+<hkern u1="T" u2="&#xc2;" k="67" />
+<hkern u1="T" u2="&#xc1;" k="67" />
+<hkern u1="T" u2="&#xd3;" k="27" />
+<hkern u1="T" u2="&#xd4;" k="27" />
+<hkern u1="T" u2="&#xd2;" k="27" />
+<hkern u1="T" u2="&#x131;" k="68" />
+<hkern u1="U" u2="&#xc4;" k="4" />
+<hkern u1="U" u2="&#xc5;" k="4" />
+<hkern u1="U" u2="&#xc6;" k="4" />
+<hkern u1="U" u2="&#x2026;" k="10" />
+<hkern u1="U" u2="&#xc0;" k="4" />
+<hkern u1="U" u2="&#xc3;" k="4" />
+<hkern u1="U" u2="&#x201a;" k="10" />
+<hkern u1="U" u2="&#x201e;" k="10" />
+<hkern u1="U" u2="&#xc2;" k="4" />
+<hkern u1="U" u2="&#xc1;" k="4" />
+<hkern u1="V" u2="&amp;" k="38" />
+<hkern u1="V" u2="W" k="6" />
+<hkern u1="V" u2="X" k="-16" />
+<hkern u1="V" u2="Y" k="-28" />
+<hkern u1="V" u2="]" k="-30" />
+<hkern u1="V" u2="a" k="42" />
+<hkern u1="V" u2="b" k="10" />
+<hkern u1="V" u2="c" k="44" />
+<hkern u1="V" u2="d" k="44" />
+<hkern u1="V" u2="e" k="44" />
+<hkern u1="V" u2="f" k="10" />
+<hkern u1="V" u2="g" k="35" />
+<hkern u1="V" u2="h" k="10" />
+<hkern u1="V" u2="k" k="10" />
+<hkern u1="V" u2="l" k="10" />
+<hkern u1="V" u2="m" k="30" />
+<hkern u1="V" u2="n" k="30" />
+<hkern u1="V" u2="o" k="44" />
+<hkern u1="V" u2="p" k="30" />
+<hkern u1="V" u2="q" k="44" />
+<hkern u1="V" u2="r" k="30" />
+<hkern u1="V" u2="s" k="40" />
+<hkern u1="V" u2="t" k="10" />
+<hkern u1="V" u2="u" k="30" />
+<hkern u1="V" u2="v" k="10" />
+<hkern u1="V" u2="w" k="10" />
+<hkern u1="V" u2="x" k="25" />
+<hkern u1="V" u2="y" k="10" />
+<hkern u1="V" u2="}" k="-30" />
+<hkern u1="V" u2="&#xc4;" k="56" />
+<hkern u1="V" u2="&#xc5;" k="56" />
+<hkern u1="V" u2="&#xd6;" k="20" />
+<hkern u1="V" u2="&#xe7;" k="44" />
+<hkern u1="V" u2="&#xae;" k="2" />
+<hkern u1="V" u2="&#xa9;" k="2" />
+<hkern u1="V" u2="&#xc6;" k="56" />
+<hkern u1="V" u2="&#xd8;" k="20" />
+<hkern u1="V" u2="&#x3c0;" k="2" />
+<hkern u1="V" u2="&#xe6;" k="42" />
+<hkern u1="V" u2="&#xab;" k="50" />
+<hkern u1="V" u2="&#xbb;" k="20" />
+<hkern u1="V" u2="&#x2026;" k="62" />
+<hkern u1="V" u2="&#xc0;" k="56" />
+<hkern u1="V" u2="&#xc3;" k="56" />
+<hkern u1="V" u2="&#xd5;" k="20" />
+<hkern u1="V" u2="&#x152;" k="20" />
+<hkern u1="V" u2="&#x153;" k="44" />
+<hkern u1="V" u2="&#x2013;" k="60" />
+<hkern u1="V" u2="&#x2014;" k="60" />
+<hkern u1="V" u2="&#x178;" k="-28" />
+<hkern u1="V" u2="&#x2039;" k="50" />
+<hkern u1="V" u2="&#x203a;" k="20" />
+<hkern u1="V" u2="&#x201a;" k="62" />
+<hkern u1="V" u2="&#x201e;" k="62" />
+<hkern u1="V" u2="&#xc2;" k="56" />
+<hkern u1="V" u2="&#xc1;" k="56" />
+<hkern u1="V" u2="&#xd3;" k="20" />
+<hkern u1="V" u2="&#xd4;" k="20" />
+<hkern u1="V" u2="&#xd2;" k="20" />
+<hkern u1="V" u2="&#x131;" k="30" />
+<hkern u1="W" u2="&amp;" k="33" />
+<hkern u1="W" u2="X" k="12" />
+<hkern u1="W" u2="]" k="-10" />
+<hkern u1="W" u2="a" k="47" />
+<hkern u1="W" u2="b" k="10" />
+<hkern u1="W" u2="c" k="49" />
+<hkern u1="W" u2="d" k="49" />
+<hkern u1="W" u2="e" k="49" />
+<hkern u1="W" u2="f" k="15" />
+<hkern u1="W" u2="g" k="50" />
+<hkern u1="W" u2="h" k="10" />
+<hkern u1="W" u2="i" k="20" />
+<hkern u1="W" u2="j" k="20" />
+<hkern u1="W" u2="k" k="10" />
+<hkern u1="W" u2="l" k="10" />
+<hkern u1="W" u2="m" k="35" />
+<hkern u1="W" u2="n" k="35" />
+<hkern u1="W" u2="o" k="49" />
+<hkern u1="W" u2="p" k="35" />
+<hkern u1="W" u2="q" k="49" />
+<hkern u1="W" u2="r" k="35" />
+<hkern u1="W" u2="s" k="50" />
+<hkern u1="W" u2="t" k="30" />
+<hkern u1="W" u2="u" k="35" />
+<hkern u1="W" u2="v" k="30" />
+<hkern u1="W" u2="w" k="30" />
+<hkern u1="W" u2="x" k="30" />
+<hkern u1="W" u2="y" k="30" />
+<hkern u1="W" u2="z" k="30" />
+<hkern u1="W" u2="}" k="-10" />
+<hkern u1="W" u2="&#xc4;" k="53" />
+<hkern u1="W" u2="&#xc5;" k="53" />
+<hkern u1="W" u2="&#xd6;" k="38" />
+<hkern u1="W" u2="&#xe7;" k="49" />
+<hkern u1="W" u2="&#xae;" k="20" />
+<hkern u1="W" u2="&#xa9;" k="20" />
+<hkern u1="W" u2="&#xc6;" k="53" />
+<hkern u1="W" u2="&#xd8;" k="38" />
+<hkern u1="W" u2="&#x3c0;" k="20" />
+<hkern u1="W" u2="&#xe6;" k="47" />
+<hkern u1="W" u2="&#xab;" k="40" />
+<hkern u1="W" u2="&#xbb;" k="20" />
+<hkern u1="W" u2="&#x2026;" k="22" />
+<hkern u1="W" u2="&#xc0;" k="53" />
+<hkern u1="W" u2="&#xc3;" k="53" />
+<hkern u1="W" u2="&#xd5;" k="38" />
+<hkern u1="W" u2="&#x152;" k="38" />
+<hkern u1="W" u2="&#x153;" k="49" />
+<hkern u1="W" u2="&#x2013;" k="40" />
+<hkern u1="W" u2="&#x2014;" k="40" />
+<hkern u1="W" u2="&#x2039;" k="40" />
+<hkern u1="W" u2="&#x203a;" k="20" />
+<hkern u1="W" u2="&#x201a;" k="22" />
+<hkern u1="W" u2="&#x201e;" k="22" />
+<hkern u1="W" u2="&#xc2;" k="53" />
+<hkern u1="W" u2="&#xc1;" k="53" />
+<hkern u1="W" u2="&#xd3;" k="38" />
+<hkern u1="W" u2="&#xd4;" k="38" />
+<hkern u1="W" u2="&#xd2;" k="38" />
+<hkern u1="W" u2="&#x131;" k="35" />
+<hkern u1="X" u2="&amp;" k="16" />
+<hkern u1="X" u2="Y" k="-18" />
+<hkern u1="X" u2="]" k="-40" />
+<hkern u1="X" u2="a" k="10" />
+<hkern u1="X" u2="c" k="10" />
+<hkern u1="X" u2="d" k="10" />
+<hkern u1="X" u2="e" k="10" />
+<hkern u1="X" u2="f" k="10" />
+<hkern u1="X" u2="m" k="10" />
+<hkern u1="X" u2="n" k="10" />
+<hkern u1="X" u2="o" k="10" />
+<hkern u1="X" u2="p" k="10" />
+<hkern u1="X" u2="q" k="10" />
+<hkern u1="X" u2="r" k="10" />
+<hkern u1="X" u2="t" k="10" />
+<hkern u1="X" u2="u" k="10" />
+<hkern u1="X" u2="v" k="20" />
+<hkern u1="X" u2="w" k="10" />
+<hkern u1="X" u2="y" k="20" />
+<hkern u1="X" u2="}" k="-40" />
+<hkern u1="X" u2="&#xc4;" k="-10" />
+<hkern u1="X" u2="&#xc5;" k="-10" />
+<hkern u1="X" u2="&#xd6;" k="26" />
+<hkern u1="X" u2="&#xe7;" k="10" />
+<hkern u1="X" u2="&#xae;" k="6" />
+<hkern u1="X" u2="&#xa9;" k="6" />
+<hkern u1="X" u2="&#xc6;" k="-10" />
+<hkern u1="X" u2="&#xd8;" k="26" />
+<hkern u1="X" u2="&#x3c0;" k="6" />
+<hkern u1="X" u2="&#xe6;" k="10" />
+<hkern u1="X" u2="&#xc0;" k="-10" />
+<hkern u1="X" u2="&#xc3;" k="-10" />
+<hkern u1="X" u2="&#xd5;" k="26" />
+<hkern u1="X" u2="&#x152;" k="26" />
+<hkern u1="X" u2="&#x153;" k="10" />
+<hkern u1="X" u2="&#x2013;" k="30" />
+<hkern u1="X" u2="&#x2014;" k="30" />
+<hkern u1="X" u2="&#x178;" k="-18" />
+<hkern u1="X" u2="&#xc2;" k="-10" />
+<hkern u1="X" u2="&#xc1;" k="-10" />
+<hkern u1="X" u2="&#xd3;" k="26" />
+<hkern u1="X" u2="&#xd4;" k="26" />
+<hkern u1="X" u2="&#xd2;" k="26" />
+<hkern u1="X" u2="&#x131;" k="10" />
+<hkern u1="Y" u2="&amp;" k="30" />
+<hkern u1="Y" u2="]" k="-50" />
+<hkern u1="Y" u2="a" k="57" />
+<hkern u1="Y" u2="c" k="59" />
+<hkern u1="Y" u2="d" k="59" />
+<hkern u1="Y" u2="e" k="59" />
+<hkern u1="Y" u2="f" k="10" />
+<hkern u1="Y" u2="g" k="40" />
+<hkern u1="Y" u2="m" k="40" />
+<hkern u1="Y" u2="n" k="40" />
+<hkern u1="Y" u2="o" k="59" />
+<hkern u1="Y" u2="p" k="40" />
+<hkern u1="Y" u2="q" k="59" />
+<hkern u1="Y" u2="r" k="40" />
+<hkern u1="Y" u2="s" k="51" />
+<hkern u1="Y" u2="t" k="20" />
+<hkern u1="Y" u2="u" k="40" />
+<hkern u1="Y" u2="v" k="20" />
+<hkern u1="Y" u2="w" k="10" />
+<hkern u1="Y" u2="x" k="20" />
+<hkern u1="Y" u2="y" k="20" />
+<hkern u1="Y" u2="z" k="30" />
+<hkern u1="Y" u2="}" k="-50" />
+<hkern u1="Y" u2="&#xc4;" k="51" />
+<hkern u1="Y" u2="&#xc5;" k="51" />
+<hkern u1="Y" u2="&#xd6;" k="23" />
+<hkern u1="Y" u2="&#xe7;" k="59" />
+<hkern u1="Y" u2="&#xae;" k="30" />
+<hkern u1="Y" u2="&#xa9;" k="30" />
+<hkern u1="Y" u2="&#xc6;" k="51" />
+<hkern u1="Y" u2="&#xd8;" k="23" />
+<hkern u1="Y" u2="&#x3c0;" k="30" />
+<hkern u1="Y" u2="&#xe6;" k="57" />
+<hkern u1="Y" u2="&#xab;" k="60" />
+<hkern u1="Y" u2="&#xbb;" k="40" />
+<hkern u1="Y" u2="&#x2026;" k="50" />
+<hkern u1="Y" u2="&#xc0;" k="51" />
+<hkern u1="Y" u2="&#xc3;" k="51" />
+<hkern u1="Y" u2="&#xd5;" k="23" />
+<hkern u1="Y" u2="&#x152;" k="23" />
+<hkern u1="Y" u2="&#x153;" k="59" />
+<hkern u1="Y" u2="&#x2013;" k="50" />
+<hkern u1="Y" u2="&#x2014;" k="50" />
+<hkern u1="Y" u2="&#x2039;" k="60" />
+<hkern u1="Y" u2="&#x203a;" k="40" />
+<hkern u1="Y" u2="&#x201a;" k="50" />
+<hkern u1="Y" u2="&#x201e;" k="50" />
+<hkern u1="Y" u2="&#xc2;" k="51" />
+<hkern u1="Y" u2="&#xc1;" k="51" />
+<hkern u1="Y" u2="&#xd3;" k="23" />
+<hkern u1="Y" u2="&#xd4;" k="23" />
+<hkern u1="Y" u2="&#xd2;" k="23" />
+<hkern u1="Y" u2="&#x131;" k="40" />
+<hkern u1="Z" u2="a" k="24" />
+<hkern u1="Z" u2="b" k="10" />
+<hkern u1="Z" u2="c" k="34" />
+<hkern u1="Z" u2="d" k="34" />
+<hkern u1="Z" u2="e" k="34" />
+<hkern u1="Z" u2="f" k="20" />
+<hkern u1="Z" u2="g" k="20" />
+<hkern u1="Z" u2="h" k="10" />
+<hkern u1="Z" u2="i" k="20" />
+<hkern u1="Z" u2="j" k="15" />
+<hkern u1="Z" u2="k" k="10" />
+<hkern u1="Z" u2="l" k="10" />
+<hkern u1="Z" u2="m" k="20" />
+<hkern u1="Z" u2="n" k="20" />
+<hkern u1="Z" u2="o" k="34" />
+<hkern u1="Z" u2="p" k="20" />
+<hkern u1="Z" u2="q" k="34" />
+<hkern u1="Z" u2="r" k="20" />
+<hkern u1="Z" u2="u" k="20" />
+<hkern u1="Z" u2="v" k="30" />
+<hkern u1="Z" u2="w" k="10" />
+<hkern u1="Z" u2="y" k="30" />
+<hkern u1="Z" u2="&#xc4;" k="10" />
+<hkern u1="Z" u2="&#xc5;" k="10" />
+<hkern u1="Z" u2="&#xd6;" k="37" />
+<hkern u1="Z" u2="&#xe7;" k="34" />
+<hkern u1="Z" u2="&#xc6;" k="10" />
+<hkern u1="Z" u2="&#xd8;" k="37" />
+<hkern u1="Z" u2="&#xe6;" k="24" />
+<hkern u1="Z" u2="&#xc0;" k="10" />
+<hkern u1="Z" u2="&#xc3;" k="10" />
+<hkern u1="Z" u2="&#xd5;" k="37" />
+<hkern u1="Z" u2="&#x152;" k="37" />
+<hkern u1="Z" u2="&#x153;" k="34" />
+<hkern u1="Z" u2="&#x2013;" k="42" />
+<hkern u1="Z" u2="&#x2014;" k="42" />
+<hkern u1="Z" u2="&#xc2;" k="10" />
+<hkern u1="Z" u2="&#xc1;" k="10" />
+<hkern u1="Z" u2="&#xd3;" k="37" />
+<hkern u1="Z" u2="&#xd4;" k="37" />
+<hkern u1="Z" u2="&#xd2;" k="37" />
+<hkern u1="Z" u2="&#x131;" k="20" />
+<hkern u1="[" u2="g" k="-20" />
+<hkern u1="[" u2="j" k="-150" />
+<hkern u1="[" u2="&#x178;" k="-50" />
+<hkern u1="\" u2="a" k="10" />
+<hkern u1="\" u2="c" k="10" />
+<hkern u1="\" u2="d" k="10" />
+<hkern u1="\" u2="e" k="10" />
+<hkern u1="\" u2="j" k="-130" />
+<hkern u1="\" u2="o" k="10" />
+<hkern u1="\" u2="q" k="10" />
+<hkern u1="\" u2="&#xe7;" k="10" />
+<hkern u1="\" u2="&#xe6;" k="10" />
+<hkern u1="\" u2="&#x153;" k="10" />
+<hkern u1="\" u2="&#x178;" k="60" />
+<hkern u1="a" u2="f" k="1" />
+<hkern u1="a" u2="t" k="10" />
+<hkern u1="a" u2="v" k="6" />
+<hkern u1="a" u2="y" k="6" />
+<hkern u1="a" u2="&#x2122;" k="20" />
+<hkern u1="a" u2="&#x201c;" k="30" />
+<hkern u1="a" u2="&#x2018;" k="30" />
+<hkern u1="b" u2="f" k="2" />
+<hkern u1="b" u2="t" k="3" />
+<hkern u1="b" u2="v" k="13" />
+<hkern u1="b" u2="w" k="3" />
+<hkern u1="b" u2="x" k="17" />
+<hkern u1="b" u2="y" k="13" />
+<hkern u1="b" u2="z" k="16" />
+<hkern u1="b" u2="&#x2122;" k="30" />
+<hkern u1="b" u2="&#x2026;" k="20" />
+<hkern u1="b" u2="&#x201c;" k="36" />
+<hkern u1="b" u2="&#x2018;" k="36" />
+<hkern u1="b" u2="&#x201a;" k="20" />
+<hkern u1="b" u2="&#x201e;" k="20" />
+<hkern u1="c" u2="&#xab;" k="4" />
+<hkern u1="c" u2="&#x2039;" k="4" />
+<hkern u1="e" u2="f" k="2" />
+<hkern u1="e" u2="t" k="1" />
+<hkern u1="e" u2="v" k="12" />
+<hkern u1="e" u2="w" k="2" />
+<hkern u1="e" u2="x" k="16" />
+<hkern u1="e" u2="y" k="12" />
+<hkern u1="f" u2="&amp;" k="26" />
+<hkern u1="f" u2="g" k="2" />
+<hkern u1="f" u2="h" k="-3" />
+<hkern u1="f" u2="k" k="-3" />
+<hkern u1="f" u2="l" k="-3" />
+<hkern u1="f" u2="m" k="4" />
+<hkern u1="f" u2="n" k="4" />
+<hkern u1="f" u2="o" k="19" />
+<hkern u1="f" u2="p" k="4" />
+<hkern u1="f" u2="q" k="19" />
+<hkern u1="f" u2="r" k="4" />
+<hkern u1="f" u2="t" k="-2" />
+<hkern u1="f" u2="u" k="4" />
+<hkern u1="f" u2="}" k="-70" />
+<hkern u1="f" u2="&#xe7;" k="19" />
+<hkern u1="f" u2="&#xae;" k="-4" />
+<hkern u1="f" u2="&#xa9;" k="-4" />
+<hkern u1="f" u2="&#x2122;" k="-42" />
+<hkern u1="f" u2="&#x3c0;" k="-4" />
+<hkern u1="f" u2="&#xe6;" k="19" />
+<hkern u1="f" u2="&#xab;" k="40" />
+<hkern u1="f" u2="&#xbb;" k="14" />
+<hkern u1="f" u2="&#x2026;" k="74" />
+<hkern u1="f" u2="&#x153;" k="19" />
+<hkern u1="f" u2="&#x2013;" k="26" />
+<hkern u1="f" u2="&#x2014;" k="26" />
+<hkern u1="f" u2="&#x201c;" k="-30" />
+<hkern u1="f" u2="&#x201d;" k="-50" />
+<hkern u1="f" u2="&#x2018;" k="-30" />
+<hkern u1="f" u2="&#x2019;" k="-50" />
+<hkern u1="f" u2="&#x2039;" k="40" />
+<hkern u1="f" u2="&#x203a;" k="14" />
+<hkern u1="f" u2="&#x201a;" k="74" />
+<hkern u1="f" u2="&#x201e;" k="74" />
+<hkern u1="f" u2="&#x131;" k="4" />
+<hkern u1="h" u2="t" k="10" />
+<hkern u1="h" u2="v" k="6" />
+<hkern u1="h" u2="y" k="6" />
+<hkern u1="h" u2="&#x2122;" k="20" />
+<hkern u1="h" u2="&#x201c;" k="30" />
+<hkern u1="h" u2="&#x2018;" k="30" />
+<hkern u1="k" u2="o" k="11" />
+<hkern u1="k" u2="q" k="11" />
+<hkern u1="k" u2="v" k="1" />
+<hkern u1="k" u2="y" k="1" />
+<hkern u1="k" u2="&#xe7;" k="11" />
+<hkern u1="k" u2="&#xe6;" k="11" />
+<hkern u1="k" u2="&#xab;" k="8" />
+<hkern u1="k" u2="&#x153;" k="11" />
+<hkern u1="k" u2="&#x2039;" k="8" />
+<hkern u1="m" u2="t" k="10" />
+<hkern u1="m" u2="v" k="6" />
+<hkern u1="m" u2="y" k="6" />
+<hkern u1="m" u2="&#x2122;" k="20" />
+<hkern u1="m" u2="&#x201c;" k="30" />
+<hkern u1="m" u2="&#x2018;" k="30" />
+<hkern u1="n" u2="t" k="10" />
+<hkern u1="n" u2="v" k="6" />
+<hkern u1="n" u2="y" k="6" />
+<hkern u1="n" u2="&#x2122;" k="20" />
+<hkern u1="n" u2="&#x201c;" k="30" />
+<hkern u1="n" u2="&#x2018;" k="30" />
+<hkern u1="o" u2="t" k="3" />
+<hkern u1="o" u2="v" k="13" />
+<hkern u1="o" u2="w" k="3" />
+<hkern u1="o" u2="x" k="17" />
+<hkern u1="o" u2="y" k="13" />
+<hkern u1="o" u2="z" k="16" />
+<hkern u1="o" u2="&#x2122;" k="30" />
+<hkern u1="o" u2="&#x2026;" k="20" />
+<hkern u1="o" u2="&#x201c;" k="36" />
+<hkern u1="o" u2="&#x2018;" k="36" />
+<hkern u1="o" u2="&#x201a;" k="20" />
+<hkern u1="o" u2="&#x201e;" k="20" />
+<hkern u1="p" u2="t" k="3" />
+<hkern u1="p" u2="v" k="13" />
+<hkern u1="p" u2="w" k="3" />
+<hkern u1="p" u2="x" k="17" />
+<hkern u1="p" u2="y" k="13" />
+<hkern u1="p" u2="z" k="16" />
+<hkern u1="p" u2="&#x2122;" k="30" />
+<hkern u1="p" u2="&#x2026;" k="20" />
+<hkern u1="p" u2="&#x201c;" k="36" />
+<hkern u1="p" u2="&#x2018;" k="36" />
+<hkern u1="p" u2="&#x201a;" k="20" />
+<hkern u1="p" u2="&#x201e;" k="20" />
+<hkern u1="r" u2="&amp;" k="6" />
+<hkern u1="r" u2="t" k="-16" />
+<hkern u1="r" u2="x" k="1" />
+<hkern u1="r" u2="&#xe7;" k="6" />
+<hkern u1="r" u2="&#xe6;" k="6" />
+<hkern u1="r" u2="&#x2026;" k="58" />
+<hkern u1="r" u2="&#x153;" k="6" />
+<hkern u1="r" u2="&#x2013;" k="18" />
+<hkern u1="r" u2="&#x2014;" k="18" />
+<hkern u1="r" u2="&#x201c;" k="-20" />
+<hkern u1="r" u2="&#x201d;" k="-40" />
+<hkern u1="r" u2="&#x2018;" k="-20" />
+<hkern u1="r" u2="&#x2019;" k="-40" />
+<hkern u1="r" u2="&#x201a;" k="58" />
+<hkern u1="r" u2="&#x201e;" k="58" />
+<hkern u1="t" u2="v" k="3" />
+<hkern u1="t" u2="y" k="3" />
+<hkern u1="t" u2="&#xe7;" k="7" />
+<hkern u1="t" u2="&#xe6;" k="7" />
+<hkern u1="t" u2="&#x2026;" k="-10" />
+<hkern u1="t" u2="&#x153;" k="7" />
+<hkern u1="t" u2="&#x201c;" k="-10" />
+<hkern u1="t" u2="&#x2018;" k="-10" />
+<hkern u1="t" u2="&#x201a;" k="-10" />
+<hkern u1="t" u2="&#x201e;" k="-10" />
+<hkern u1="v" u2="w" k="3" />
+<hkern u1="v" u2="x" k="4" />
+<hkern u1="v" u2="y" k="3" />
+<hkern u1="v" u2="&#xe7;" k="13" />
+<hkern u1="v" u2="&#xe6;" k="13" />
+<hkern u1="v" u2="&#xab;" k="6" />
+<hkern u1="v" u2="&#x2026;" k="50" />
+<hkern u1="v" u2="&#x153;" k="13" />
+<hkern u1="v" u2="&#x2013;" k="10" />
+<hkern u1="v" u2="&#x2014;" k="10" />
+<hkern u1="v" u2="&#x201c;" k="-30" />
+<hkern u1="v" u2="&#x201d;" k="-30" />
+<hkern u1="v" u2="&#x2018;" k="-30" />
+<hkern u1="v" u2="&#x2019;" k="-30" />
+<hkern u1="v" u2="&#x2039;" k="6" />
+<hkern u1="v" u2="&#x201a;" k="50" />
+<hkern u1="v" u2="&#x201e;" k="50" />
+<hkern u1="w" u2="x" k="3" />
+<hkern u1="w" u2="y" k="3" />
+<hkern u1="w" u2="&#xe7;" k="3" />
+<hkern u1="w" u2="&#xe6;" k="3" />
+<hkern u1="w" u2="&#xab;" k="4" />
+<hkern u1="w" u2="&#x2026;" k="30" />
+<hkern u1="w" u2="&#x153;" k="3" />
+<hkern u1="w" u2="&#x201c;" k="-20" />
+<hkern u1="w" u2="&#x201d;" k="-20" />
+<hkern u1="w" u2="&#x2018;" k="-20" />
+<hkern u1="w" u2="&#x2019;" k="-20" />
+<hkern u1="w" u2="&#x2039;" k="4" />
+<hkern u1="w" u2="&#x201a;" k="30" />
+<hkern u1="w" u2="&#x201e;" k="30" />
+<hkern u1="x" u2="y" k="4" />
+<hkern u1="x" u2="&#xe7;" k="17" />
+<hkern u1="x" u2="&#xe6;" k="17" />
+<hkern u1="x" u2="&#xab;" k="50" />
+<hkern u1="x" u2="&#x153;" k="17" />
+<hkern u1="x" u2="&#x2013;" k="30" />
+<hkern u1="x" u2="&#x2014;" k="30" />
+<hkern u1="x" u2="&#x201c;" k="-10" />
+<hkern u1="x" u2="&#x201d;" k="-20" />
+<hkern u1="x" u2="&#x2018;" k="-10" />
+<hkern u1="x" u2="&#x2019;" k="-20" />
+<hkern u1="x" u2="&#x2039;" k="50" />
+<hkern u1="y" u2="&#xe7;" k="13" />
+<hkern u1="y" u2="&#xe6;" k="13" />
+<hkern u1="y" u2="&#xab;" k="6" />
+<hkern u1="y" u2="&#x2026;" k="50" />
+<hkern u1="y" u2="&#x153;" k="13" />
+<hkern u1="y" u2="&#x2013;" k="10" />
+<hkern u1="y" u2="&#x2014;" k="10" />
+<hkern u1="y" u2="&#x201c;" k="-30" />
+<hkern u1="y" u2="&#x201d;" k="-30" />
+<hkern u1="y" u2="&#x2018;" k="-30" />
+<hkern u1="y" u2="&#x2019;" k="-30" />
+<hkern u1="y" u2="&#x2039;" k="6" />
+<hkern u1="y" u2="&#x201a;" k="50" />
+<hkern u1="y" u2="&#x201e;" k="50" />
+<hkern u1="z" u2="&#xe7;" k="20" />
+<hkern u1="z" u2="&#xe6;" k="20" />
+<hkern u1="z" u2="&#xab;" k="56" />
+<hkern u1="z" u2="&#x153;" k="20" />
+<hkern u1="z" u2="&#x2013;" k="26" />
+<hkern u1="z" u2="&#x2014;" k="26" />
+<hkern u1="z" u2="&#x2039;" k="56" />
+<hkern u1="{" u2="&#x178;" k="-50" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc4;" u2="&#xae;" k="10" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc4;" u2="&#x152;" k="20" />
+<hkern u1="&#xc4;" u2="&#x153;" k="5" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc4;" u2="&#x178;" k="51" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc4;" u2="&#x131;" k="5" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="20" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="5" />
+<hkern u1="&#xc5;" u2="&#xae;" k="10" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="10" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="70" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="20" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="10" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc5;" u2="&#x152;" k="20" />
+<hkern u1="&#xc5;" u2="&#x153;" k="5" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc5;" u2="&#x178;" k="51" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc5;" u2="&#x131;" k="5" />
+<hkern u1="&#xc9;" u2="&#xd6;" k="5" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="24" />
+<hkern u1="&#xc9;" u2="&#xae;" k="20" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="20" />
+<hkern u1="&#xc9;" u2="&#xd8;" k="5" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="24" />
+<hkern u1="&#xc9;" u2="&#xab;" k="4" />
+<hkern u1="&#xc9;" u2="&#xd5;" k="5" />
+<hkern u1="&#xc9;" u2="&#x152;" k="5" />
+<hkern u1="&#xc9;" u2="&#x153;" k="24" />
+<hkern u1="&#xc9;" u2="&#x2039;" k="4" />
+<hkern u1="&#xc9;" u2="&#xd3;" k="5" />
+<hkern u1="&#xc9;" u2="&#xd4;" k="5" />
+<hkern u1="&#xc9;" u2="&#xd2;" k="5" />
+<hkern u1="&#xc9;" u2="&#x131;" k="2" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd6;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="38" />
+<hkern u1="&#xd6;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd6;" u2="&#x153;" k="4" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd6;" u2="&#x178;" k="23" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="38" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="38" />
+<hkern u1="&#xd6;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd6;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd6;" u2="&#x131;" k="4" />
+<hkern u1="&#xdc;" u2="&#xc6;" k="4" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="10" />
+<hkern u1="&#xdc;" u2="&#xc0;" k="4" />
+<hkern u1="&#xdc;" u2="&#xc3;" k="4" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="10" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="10" />
+<hkern u1="&#xdc;" u2="&#xc2;" k="4" />
+<hkern u1="&#xdc;" u2="&#xc1;" k="4" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="20" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="30" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="30" />
+<hkern u1="&#xe7;" u2="&#xab;" k="4" />
+<hkern u1="&#xe7;" u2="&#x2039;" k="4" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="20" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="30" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="30" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="36" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="36" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="36" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="36" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="36" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="36" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="20" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="30" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="36" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="36" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="20" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="30" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="36" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="36" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="20" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="20" />
+<hkern u1="&#xae;" u2="&#xc6;" k="10" />
+<hkern u1="&#xae;" u2="&#xc0;" k="10" />
+<hkern u1="&#xae;" u2="&#xc3;" k="10" />
+<hkern u1="&#xae;" u2="&#x178;" k="30" />
+<hkern u1="&#xae;" u2="&#xc2;" k="10" />
+<hkern u1="&#xae;" u2="&#xc1;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="10" />
+<hkern u1="&#xa9;" u2="&#x178;" k="30" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="10" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="10" />
+<hkern u1="&#xc6;" u2="&#xd8;" k="5" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="20" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="24" />
+<hkern u1="&#xc6;" u2="&#xab;" k="4" />
+<hkern u1="&#xc6;" u2="&#xd5;" k="5" />
+<hkern u1="&#xc6;" u2="&#x152;" k="5" />
+<hkern u1="&#xc6;" u2="&#x153;" k="24" />
+<hkern u1="&#xc6;" u2="&#x2039;" k="4" />
+<hkern u1="&#xc6;" u2="&#xd3;" k="5" />
+<hkern u1="&#xc6;" u2="&#xd4;" k="5" />
+<hkern u1="&#xc6;" u2="&#xd2;" k="5" />
+<hkern u1="&#xc6;" u2="&#x131;" k="2" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="38" />
+<hkern u1="&#xd8;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd8;" u2="&#x153;" k="4" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd8;" u2="&#x178;" k="23" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="38" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="38" />
+<hkern u1="&#xd8;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd8;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd8;" u2="&#x131;" k="4" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="10" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="30" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="10" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="10" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="36" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="36" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="20" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="20" />
+<hkern u1="&#xbf;" u2="&#x152;" k="20" />
+<hkern u1="&#xbf;" u2="&#x153;" k="-2" />
+<hkern u1="&#xbf;" u2="&#x178;" k="44" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="20" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="20" />
+<hkern u1="&#xa1;" u2="&#x178;" k="4" />
+<hkern u1="&#xab;" u2="&#x178;" k="40" />
+<hkern u1="&#xbb;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="38" />
+<hkern u1="&#x2026;" u2="&#x152;" k="38" />
+<hkern u1="&#x2026;" u2="&#x153;" k="20" />
+<hkern u1="&#x2026;" u2="&#x178;" k="50" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="38" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="38" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="38" />
+<hkern u1="&#x2026;" u2="&#xda;" k="10" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="10" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="10" />
+<hkern u1="&#x2026;" u2="&#x131;" k="20" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc0;" u2="&#x152;" k="20" />
+<hkern u1="&#xc0;" u2="&#x153;" k="5" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc0;" u2="&#x178;" k="51" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc0;" u2="&#x131;" k="5" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="20" />
+<hkern u1="&#xc3;" u2="&#x152;" k="20" />
+<hkern u1="&#xc3;" u2="&#x153;" k="5" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-10" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="60" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="30" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="60" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="30" />
+<hkern u1="&#xc3;" u2="&#x178;" k="51" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-30" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-30" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc3;" u2="&#x131;" k="5" />
+<hkern u1="&#xd5;" u2="&#x153;" k="4" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd5;" u2="&#x178;" k="23" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="38" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="38" />
+<hkern u1="&#xd5;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd5;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd5;" u2="&#x131;" k="4" />
+<hkern u1="&#x152;" u2="&#x153;" k="24" />
+<hkern u1="&#x152;" u2="&#x2039;" k="4" />
+<hkern u1="&#x152;" u2="&#xd3;" k="5" />
+<hkern u1="&#x152;" u2="&#xd4;" k="5" />
+<hkern u1="&#x152;" u2="&#xd2;" k="5" />
+<hkern u1="&#x152;" u2="&#x131;" k="2" />
+<hkern u1="&#x2013;" u2="&#x178;" k="50" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x2014;" u2="&#x178;" k="50" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-10" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-10" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-30" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="40" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="40" />
+<hkern u1="&#x201c;" u2="&#x131;" k="10" />
+<hkern u1="&#x201d;" u2="&#xc2;" k="10" />
+<hkern u1="&#x201d;" u2="&#xc1;" k="10" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="40" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-30" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="40" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="40" />
+<hkern u1="&#x2018;" u2="&#x131;" k="10" />
+<hkern u1="&#x2019;" u2="&#xc2;" k="10" />
+<hkern u1="&#x2019;" u2="&#xc1;" k="10" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="40" />
+<hkern u1="&#x178;" u2="&amp;" k="30" />
+<hkern u1="&#x178;" u2="&#x2039;" k="60" />
+<hkern u1="&#x178;" u2="&#x203a;" k="40" />
+<hkern u1="&#x178;" u2="&#x201a;" k="50" />
+<hkern u1="&#x178;" u2="&#x201e;" k="50" />
+<hkern u1="&#x178;" u2="&#xc2;" k="51" />
+<hkern u1="&#x178;" u2="&#xc1;" k="51" />
+<hkern u1="&#x178;" u2="&#xd3;" k="23" />
+<hkern u1="&#x178;" u2="&#xd4;" k="23" />
+<hkern u1="&#x178;" u2="&#xd2;" k="23" />
+<hkern u1="&#x178;" u2="&#x131;" k="40" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="38" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="38" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="38" />
+<hkern u1="&#x201a;" u2="&#xda;" k="10" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201a;" u2="&#x131;" k="20" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-30" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="38" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="38" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="38" />
+<hkern u1="&#x201e;" u2="&#xda;" k="10" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="10" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="10" />
+<hkern u1="&#x201e;" u2="&#x131;" k="20" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc2;" u2="&#x131;" k="5" />
+<hkern u1="&#xca;" u2="&#xd3;" k="5" />
+<hkern u1="&#xca;" u2="&#xd4;" k="5" />
+<hkern u1="&#xca;" u2="&#xd2;" k="5" />
+<hkern u1="&#xca;" u2="&#x131;" k="2" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="20" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="20" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="20" />
+<hkern u1="&#xc1;" u2="&#x131;" k="5" />
+<hkern u1="&#xcb;" u2="&#xd3;" k="5" />
+<hkern u1="&#xcb;" u2="&#xd4;" k="5" />
+<hkern u1="&#xcb;" u2="&#xd2;" k="5" />
+<hkern u1="&#xcb;" u2="&#x131;" k="2" />
+<hkern u1="&#xc8;" u2="&#xd3;" k="5" />
+<hkern u1="&#xc8;" u2="&#xd4;" k="5" />
+<hkern u1="&#xc8;" u2="&#xd2;" k="5" />
+<hkern u1="&#xc8;" u2="&#x131;" k="2" />
+<hkern u1="&#xd3;" u2="&#x131;" k="4" />
+<hkern u1="&#xd4;" u2="&#x131;" k="4" />
+<hkern u1="&#xd2;" u2="&#x131;" k="4" />
+<hkern u1="&#xad;" u2="1" k="20" />
+<hkern u1="&#xad;" u2="7" k="30" />
+<hkern u1="&#xad;" u2="A" k="-10" />
+<hkern u1="&#xad;" u2="T" k="66" />
+<hkern u1="&#xad;" u2="V" k="50" />
+<hkern u1="&#xad;" u2="W" k="40" />
+<hkern u1="&#xad;" u2="X" k="30" />
+<hkern u1="&#xad;" u2="Y" k="50" />
+<hkern u1="&#xad;" u2="Z" k="10" />
+<hkern u1="&#xad;" u2="a" k="10" />
+<hkern u1="&#xad;" u2="c" k="10" />
+<hkern u1="&#xad;" u2="d" k="10" />
+<hkern u1="&#xad;" u2="e" k="10" />
+<hkern u1="&#xad;" u2="o" k="10" />
+<hkern u1="&#xad;" u2="q" k="10" />
+<hkern u1="&#xad;" u2="v" k="10" />
+<hkern u1="&#xad;" u2="x" k="30" />
+<hkern u1="&#xad;" u2="y" k="10" />
+<hkern u1="&#xad;" u2="z" k="26" />
+<hkern u1="&#xad;" u2="&#xc4;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc5;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe7;" k="10" />
+<hkern u1="&#xad;" u2="&#xc6;" k="-10" />
+<hkern u1="&#xad;" u2="&#xe6;" k="10" />
+<hkern u1="&#xad;" u2="&#xc0;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc3;" k="-10" />
+<hkern u1="&#xad;" u2="&#x153;" k="10" />
+<hkern u1="&#xad;" u2="&#x178;" k="50" />
+<hkern u1="&#xad;" u2="&#xc2;" k="-10" />
+<hkern u1="&#xad;" u2="&#xc1;" k="-10" />
+<hkern u1="&#xd0;" u2="&#xdd;" k="23" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="38" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="38" />
+<hkern u1="&#xd0;" u2="." k="38" />
+<hkern u1="&#xd0;" u2="/" k="50" />
+<hkern u1="&#xd0;" u2="?" k="20" />
+<hkern u1="&#xd0;" u2="A" k="20" />
+<hkern u1="&#xd0;" u2="J" k="39" />
+<hkern u1="&#xd0;" u2="T" k="27" />
+<hkern u1="&#xd0;" u2="V" k="20" />
+<hkern u1="&#xd0;" u2="W" k="38" />
+<hkern u1="&#xd0;" u2="X" k="26" />
+<hkern u1="&#xd0;" u2="Y" k="23" />
+<hkern u1="&#xd0;" u2="Z" k="38" />
+<hkern u1="&#xd0;" u2="a" k="4" />
+<hkern u1="&#xd0;" u2="b" k="8" />
+<hkern u1="&#xd0;" u2="c" k="4" />
+<hkern u1="&#xd0;" u2="d" k="4" />
+<hkern u1="&#xd0;" u2="e" k="4" />
+<hkern u1="&#xd0;" u2="h" k="8" />
+<hkern u1="&#xd0;" u2="k" k="8" />
+<hkern u1="&#xd0;" u2="l" k="8" />
+<hkern u1="&#xd0;" u2="m" k="4" />
+<hkern u1="&#xd0;" u2="n" k="4" />
+<hkern u1="&#xd0;" u2="o" k="4" />
+<hkern u1="&#xd0;" u2="p" k="4" />
+<hkern u1="&#xd0;" u2="q" k="4" />
+<hkern u1="&#xd0;" u2="r" k="4" />
+<hkern u1="&#xd0;" u2="u" k="4" />
+<hkern u1="&#xd0;" u2="x" k="10" />
+<hkern u1="&#xd0;" u2="z" k="10" />
+<hkern u1="&#xd0;" u2="&#xc4;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc5;" k="20" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd0;" u2="&#xc6;" k="20" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="38" />
+<hkern u1="&#xd0;" u2="&#xc0;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc3;" k="20" />
+<hkern u1="&#xd0;" u2="&#x153;" k="4" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="20" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="20" />
+<hkern u1="&#xd0;" u2="&#x178;" k="23" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="38" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="38" />
+<hkern u1="&#xd0;" u2="&#xc2;" k="20" />
+<hkern u1="&#xd0;" u2="&#xc1;" k="20" />
+<hkern u1="&#xd0;" u2="&#x131;" k="4" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02 Medium" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02 Medium" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02 Medium" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02 Medium" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.ttf
new file mode 100644
index 0000000000..2740425412
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.woff
new file mode 100644
index 0000000000..5cff69add1
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02Medium.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.eot b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.eot
new file mode 100644
index 0000000000..84ba4acc6b
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.eot
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.svg b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.svg
new file mode 100644
index 0000000000..eca8f73836
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.svg
@@ -0,0 +1,3837 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg" >
+<metadata>
+<version>1.0</version>
+<id><![CDATA[Omnes_ATT W02 Medium Italic]]></id>
+<vendor>Monotype Imaging Inc.</vendor>
+<credits>
+<name>Fonts.com WebFonts</name>
+<URL>http://webfonts.fonts.com</URL>
+<role>Home of the Web fonts</role>
+</credits>
+<license>
+<URL>http://webfonts.fonts.com/Legal</URL>
+</license>
+<copyright><![CDATA[Generated in 2010 by FontLab Studio. Copyright info pending.]]></copyright>
+<trademark></trademark>
+<licensee>
+<name></name>
+</licensee>
+</metadata>
+<defs >
+<font horiz-adv-x="567" id="Omnes_ATTW02MediumItalic">
+<font-face font-family="Omnes_ATT W02 Medium Italic" panose-1="0 0 0 0 0 0 0 0 0 0" ascent="700" descent="-300" units-per-em="1000" alphabetic="0">
+</font-face>
+<missing-glyph horiz-adv-x="170" />
+
+<glyph unicode="&#xA;" />
+<glyph unicode="&#xD;" />
+<glyph unicode=" " glyph-name="space" horiz-adv-x="170" />
+<glyph unicode="!" glyph-name="exclam" horiz-adv-x="294" d="M143 187Q124 187 118 195T113 225L169 649H299L172 216Q167 201 161 194T143 187ZM102 -7Q45 -7 45 35Q45 54 49 70T65 98Q73 106 84 110T114 114Q171 114 171 72Q171 53 166 37T150 9Q142 1 132 -3T102 -7Z" />
+<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="425" d="M165 374Q146 374 138 380T132 409L161 648H280L194 403Q189 388 183 381T165 374ZM331 374Q313 374 306 380T300 409L330 648H449L361 403Q351 374 331 374Z" />
+<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="673" d="M167 -7Q152 -7 135 4T117 43Q117 58 125 82L150 163H21Q21 165 22 169Q23 172 24 177T27 188L36 232Q38 238 38 240V242H174L224 405H98Q98 407 99 411Q100 414 100 419T103 430L112 474Q113 476 113 478T114 482Q114 484 115 484H248L299 650Q301 654 313 654Q328 654 345 643T363 604Q363 591 355 565L330 484H477L528 650Q528 654 541 654Q557 654 574 643T591 604Q591 591 583 565L558 484H687Q686 481 685 477Q684 474 684 470T681 459L672 415Q671 412 671 411T670 407V405H534L484 242H610Q610 239 609 235Q608 232 607 228T605 217L596 173Q595 170 595 169T594 165L593 163H459L409 -3Q409 -5 405 -6T395 -7Q380 -7 363 4T346 43Q346 51 347 61T353 82L378 163H232L181 -3Q179 -7 167 -7ZM405 240L456 407H303L252 240H405Z" />
+<glyph unicode="$" glyph-name="dollar" horiz-adv-x="577" d="M191 1Q150 9 118 25T63 65Q25 103 25 141Q25 156 32 166T47 182T63 190T72 190Q92 148 129 120T212 80L258 295Q231 307 205 321T158 356T125 405T112 471Q112 508 126 541T169 598T238 638T332 654L352 747H439L417 646Q455 638 484 624T535 589Q551 573 559 556T567 521Q567 505 560 494T544 477T527 469T518 469Q500 504 469 529T396 566L351 355Q381 342 409 328T460 293T497 246T511 182Q511 141 497 107T453 47T380 8T276 -6L257 -104H170L191 1ZM404 176Q404 208 383 228T329 264L288 74Q348 77 376 105T404 176ZM213 478Q213 445 231 425T281 387L320 573Q269 568 241 542T213 478Z" />
+<glyph unicode="%" glyph-name="percent" horiz-adv-x="774" d="M231 345Q169 345 130 382T90 482Q90 517 102 549T136 605T189 643T259 657Q322 657 361 620T401 519Q401 484 389 453T355 397T301 359T231 345ZM84 -10Q82 -12 76 -10T63 -3T51 9T45 27Q45 40 52 51T79 80L725 661Q726 662 732 661T746 654T759 642T765 624Q765 611 758 600T730 571L84 -10ZM551 -5Q488 -5 449 32T409 132Q409 167 421 199T455 255T509 293T579 307Q642 307 681 270T720 169Q720 134 708 103T674 47T621 9T551 -5ZM236 407Q256 407 273 415T303 439T323 474T330 516Q330 550 309 572T255 595Q235 595 218 587T187 563T167 528T160 486Q160 452 182 430T236 407ZM555 57Q575 57 592 65T622 89T642 124T649 166Q649 200 628 222T574 245Q554 245 537 237T507 213T487 178T480 136Q480 102 502 80T555 57Z" />
+<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="705" d="M249 -12Q196 -12 156 1T88 38T46 94T31 166Q31 210 45 243T85 302T147 344T229 374Q215 395 206 421T197 479Q197 515 210 548T249 606T309 645T388 660Q422 660 451 650T502 620T536 575T548 516Q548 445 501 404T364 343L490 184Q527 226 549 275T579 369H671Q653 300 624 240T544 125L549 119Q573 89 594 74T651 58Q652 58 652 48T649 26T632 4T594 -6Q556 -6 533 12T483 62L477 69Q432 31 378 10T249 -12ZM320 396Q391 410 427 434T463 512Q463 545 441 565T383 586Q361 586 344 578T314 555T296 523T289 486Q289 459 296 439T320 396ZM260 66Q314 66 353 81T427 127L272 319Q201 302 163 268T124 175Q124 153 132 134T156 99T199 75T260 66Z" />
+<glyph unicode="&apos;" glyph-name="quotesingle" horiz-adv-x="256" d="M165 374Q146 374 138 380T132 409L161 648H280L194 403Q189 388 183 381T165 374Z" />
+<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="409" d="M257 -200Q230 -200 202 -187T148 -147Q105 -104 78 -34T51 129Q51 187 62 245T96 359T150 463T223 554Q268 599 318 625T418 652Q441 652 454 641T468 610Q468 596 460 585T450 573Q408 570 370 553T290 493Q255 458 229 415T186 323T160 225T151 127Q151 61 169 7T226 -85Q258 -115 297 -127Q299 -128 302 -136T306 -155Q306 -175 294 -187T257 -200Z" />
+<glyph unicode=")" glyph-name="parenright" horiz-adv-x="409" d="M-14 -199Q-36 -199 -49 -188T-63 -157Q-63 -143 -55 -132T-45 -119Q-3 -117 35 -99T115 -40Q150 -5 176 38T219 130T244 228T253 326Q253 392 235 446T179 538Q148 568 108 581Q106 582 103 590T99 608Q99 628 111 640T148 653Q204 653 257 600Q299 557 326 487T354 324Q354 266 343 208T309 94T255 -10T182 -101Q137 -146 87 -172T-14 -199Z" />
+<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="426" d="M157 331Q146 337 144 346T152 373L206 460L113 496Q96 504 93 511T94 533L95 537Q98 549 106 553T133 555L231 530L238 628Q240 648 247 653T266 659H270Q282 659 289 654T299 628L305 530L402 555Q421 558 429 554T441 537L442 533Q446 519 443 512T422 496L330 459L384 373Q393 356 390 347T378 331L375 329Q366 322 357 322T333 337L267 416L202 337Q189 323 181 323T161 329L157 331Z" />
+<glyph unicode="+" glyph-name="plus" horiz-adv-x="572" d="M240 25Q223 25 213 35T208 69L241 224H99Q59 224 59 250Q59 259 63 272T74 293Q85 304 108 304H258L290 453Q295 476 305 485T330 495H340Q357 495 367 485T373 451L341 304H484Q523 304 523 278Q523 269 520 256T509 235Q503 230 496 227T474 224H325L291 67Q286 44 276 35T251 25H240Z" />
+<glyph unicode="&#x2c;" glyph-name="comma" horiz-adv-x="258" d="M8 -127Q4 -131 -5 -130T-22 -125T-36 -113T-38 -98L68 118Q69 119 82 119T112 115T141 101T154 73Q154 58 148 46T126 14L8 -127Z" />
+<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="427" d="M48 198Q49 202 50 208Q51 217 56 236L59 250Q61 258 62 265T65 278Q66 284 67 289H378Q377 284 376 278Q375 273 374 266T370 250L367 236Q362 217 361 208Q360 202 359 198H48Z" />
+<glyph unicode="." glyph-name="period" horiz-adv-x="256" d="M82 -7Q25 -7 25 35Q25 54 30 70T46 98Q54 106 64 110T94 114Q151 114 151 72Q151 53 147 37T131 9Q123 1 112 -3T82 -7Z" />
+<glyph unicode="/" glyph-name="slash" horiz-adv-x="373" d="M-91 -202L354 652H453Q443 631 434 613Q426 597 418 582T406 558L8 -202H-91Z" />
+<glyph unicode="0" glyph-name="zero" horiz-adv-x="669" d="M316 -13Q256 -13 211 4T135 53T88 128T72 223Q72 258 78 299T97 388Q111 447 133 496T191 582T274 638T389 659Q449 659 494 642T570 593T617 518T633 423Q633 388 626 347T608 258Q594 199 572 150T514 64T431 8T316 -13ZM323 72Q367 72 397 87T449 129T483 189T506 262Q518 310 524 351T530 422Q530 490 491 532T382 574Q338 574 308 559T256 517T222 457T199 385Q187 337 181 295T175 224Q175 156 214 114T323 72Z" />
+<glyph unicode="1" glyph-name="one" horiz-adv-x="400" d="M240 505Q226 492 209 486T174 479Q133 479 116 498Q101 511 101 537Q101 553 107 565T120 585T133 597T140 600Q159 576 202 576Q234 576 258 598T293 652H326Q375 652 363 602L236 0H134L240 505Z" />
+<glyph unicode="2" glyph-name="two" horiz-adv-x="569" d="M47 0Q33 0 22 14T11 46Q11 94 28 141T78 220Q97 239 117 253T162 281T217 305T287 329Q323 341 355 353T412 382T451 422T466 477Q466 520 433 547T339 574Q276 574 237 543T174 463Q173 461 164 462T145 469T126 484T117 513Q117 530 127 551T156 591Q185 620 231 640T342 660Q394 660 436 647T507 611T551 554T567 480Q567 428 550 392T502 330T425 284T322 246Q288 235 263 226T217 208T182 189T154 166Q123 133 117 86H496Q495 82 494 77Q493 72 492 66T488 51Q486 42 485 34T481 17Q479 8 478 0H47Z" />
+<glyph unicode="3" glyph-name="three" horiz-adv-x="580" d="M250 -12Q181 -12 131 6T55 52Q32 74 24 95T15 133Q15 156 25 169T47 189T70 196T81 195Q92 135 135 103T253 71Q292 71 321 81T369 109T397 150T407 200Q407 245 375 272T278 299H192L198 326Q200 335 202 348T207 369Q208 372 208 373T209 377V379H300Q347 379 377 388T424 412T448 446T455 486Q455 507 446 523T420 552T380 570T330 577Q271 577 230 551T166 487Q165 485 157 486T138 492T120 507T112 532Q112 547 119 561T141 590Q172 622 220 641T335 660Q383 660 424 650T495 619T541 568T558 497Q558 468 549 441T522 392T476 355T409 333Q461 315 485 278T510 196Q510 160 497 123T454 56T374 7T250 -12Z" />
+<glyph unicode="4" glyph-name="four" horiz-adv-x="620" d="M358 174H72Q70 174 65 179T55 193T46 213T42 237Q42 258 51 274T83 316L374 606Q387 619 397 628T418 642T441 649T471 652Q516 652 532 635T540 581L471 258H586Q586 255 585 251Q584 247 583 242T581 231L571 184Q569 176 569 176L568 174H455L418 0H322L358 174ZM379 256L445 568L136 256H379Z" />
+<glyph unicode="5" glyph-name="five" horiz-adv-x="572" d="M190 383Q240 402 298 402Q404 402 461 353T518 222Q518 170 500 127T447 53T365 5T256 -12Q181 -12 132 6T55 53Q23 86 23 124Q23 139 30 150T47 168T65 177T74 178Q95 129 139 100T257 70Q330 70 373 110T417 215Q417 267 384 295T280 324Q232 324 200 314T143 290Q83 303 96 362L143 590Q155 647 210 647H577Q576 643 575 639Q574 635 573 630T571 617Q569 607 567 597T562 579Q559 569 558 559H228L190 383Z" />
+<glyph unicode="6" glyph-name="six" horiz-adv-x="621" d="M309 -12Q256 -12 213 4T138 50T90 125T73 226Q73 259 79 306T101 403T140 501T198 583Q233 619 278 639T398 660Q458 660 501 642T568 601Q584 584 591 569T598 539Q598 521 590 510T571 493T551 486T541 486Q521 529 483 552T396 575Q314 575 264 518T189 351Q219 373 259 389T360 405Q405 405 442 393T507 359T549 304T564 229Q564 182 546 139T495 62T415 8T309 -12ZM313 70Q349 70 377 82T425 114T455 161T466 220Q466 271 432 301T331 332Q278 332 242 315T176 272Q173 258 172 246T171 222Q169 150 206 110T313 70Z" />
+<glyph unicode="7" glyph-name="seven" horiz-adv-x="564" d="M160 -5Q160 -6 150 -6T127 -1T105 14T95 43Q95 60 103 73T129 108L477 555H105Q105 559 107 565Q108 570 110 577T114 593Q116 601 117 611T121 629Q123 639 125 647H545Q547 647 553 642T566 627T578 606T583 578Q583 560 576 544T549 502L160 -5Z" />
+<glyph unicode="8" glyph-name="eight" horiz-adv-x="617" d="M292 -12Q235 -12 189 0T110 35T59 90T41 163Q41 225 85 273T211 340Q170 357 148 388T125 464Q125 505 143 540T193 602T271 644T369 659Q419 659 460 647T532 614T578 562T595 496Q595 436 558 390T446 325Q496 305 522 270T549 187Q549 145 531 109T480 46T400 4T292 -12ZM356 367Q422 367 459 399T496 484Q496 529 459 554T362 580Q298 580 261 547T223 462Q223 418 259 393T356 367ZM296 66Q368 66 408 102T449 192Q449 238 408 266T294 295Q263 295 236 286T188 261T156 223T144 172Q144 125 185 96T296 66Z" />
+<glyph unicode="9" glyph-name="nine" horiz-adv-x="621" d="M259 -12Q194 -12 153 4T88 45Q73 61 66 77T59 107Q59 124 67 135T85 152T103 159T113 160Q134 117 173 95T260 72Q341 72 390 127T465 286Q435 265 393 249T296 233Q250 233 213 244T148 278T107 335T92 413Q92 462 110 507T161 585T242 639T347 659Q456 659 520 597T584 421Q584 388 578 341T556 244T518 146T460 64Q442 46 423 32T380 9T327 -6T259 -12ZM324 306Q375 306 412 323T479 366Q485 397 485 424Q487 497 451 537T345 578Q309 578 280 566T231 533T201 484T190 422Q190 368 222 337T324 306Z" />
+<glyph unicode=":" glyph-name="colon" horiz-adv-x="258" d="M165 318Q108 318 108 360Q108 378 112 395T128 423Q136 431 147 434T177 438Q234 438 234 396Q234 378 230 361T214 333Q206 325 195 322T165 318ZM94 -7Q37 -7 37 35Q37 54 42 70T58 98Q66 106 76 110T106 114Q163 114 163 72Q163 53 159 37T143 9Q135 1 124 -3T94 -7Z" />
+<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="282" d="M165 318Q108 318 108 360Q108 378 112 395T128 423Q136 431 147 434T177 438Q234 438 234 396Q234 378 230 361T214 333Q206 325 195 322T165 318ZM20 -127Q16 -131 7 -130T-10 -125T-24 -113T-26 -98L80 118Q81 119 94 119T124 115T153 101T166 73Q166 58 160 46T138 14L20 -127Z" />
+<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="498" d="M95 207Q81 216 72 229T63 260Q64 280 75 296T112 325L453 490Q454 490 458 487T467 477T475 461T479 442Q480 427 469 412T428 383L165 261L360 148Q389 131 398 115T406 84Q405 73 401 65T391 50T382 41T375 39L95 207Z" />
+<glyph unicode="=" glyph-name="equal" horiz-adv-x="572" d="M138 334Q95 334 95 361Q95 370 99 386T112 410Q122 421 150 421H492Q536 421 536 394Q536 384 531 368T518 344Q513 339 505 337T481 334H138ZM88 100Q45 100 45 127Q45 137 49 153T62 177Q67 182 75 185T100 188H442Q485 188 485 161Q485 151 481 135T468 111Q463 105 455 103T430 100H88Z" />
+<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="498" d="M55 39Q53 39 49 42T41 52T33 68T29 87Q28 102 39 117T80 146L343 268L148 381Q119 398 110 414T102 445Q103 455 107 464T117 479T126 488T133 490L413 322Q427 313 436 300T445 269Q444 249 433 233T396 204L55 39Z" />
+<glyph unicode="?" glyph-name="question" horiz-adv-x="513" d="M162 183Q162 186 164 192Q166 197 167 206T173 232Q182 275 209 310T299 367Q328 377 350 387T388 410T411 439T419 479Q419 520 389 548T304 576Q250 576 213 545T158 466Q158 464 149 465T129 472T109 486T100 512Q100 533 111 554T141 595Q170 624 211 642T310 660Q358 660 397 647T463 609T506 553T521 482Q521 438 506 408T467 358T415 326T361 305Q310 288 288 265T261 223Q259 217 258 210T255 197Q253 190 252 183H162ZM189 -7Q132 -7 132 35Q132 54 136 70T152 98Q160 106 171 110T201 114Q258 114 258 72Q258 53 253 37T237 9Q229 1 219 -3T189 -7Z" />
+<glyph unicode="@" glyph-name="at" horiz-adv-x="777" d="M393 -13Q321 -13 263 8T163 67T100 160T78 281Q78 360 106 429T183 550T300 630T445 660Q512 660 567 641T663 586T724 502T746 391Q746 338 734 296T698 225T643 179T570 163Q517 163 493 183T462 232Q425 168 349 168Q301 168 269 199T237 287Q237 321 249 354T282 414T332 456T396 472Q427 472 452 458T490 418Q494 432 497 441T502 456Q504 463 505 467H577L540 291Q532 258 542 240T584 221Q633 221 662 265T691 389Q691 437 673 477T621 547T541 592T439 608Q375 608 320 583T223 515T158 412T134 285Q134 231 153 186T206 109T290 58T398 40Q442 40 483 53T554 86Q562 92 569 95T588 99Q596 99 602 94T613 83T618 71T619 64Q584 32 526 10T393 -13ZM374 230Q408 230 434 254T468 314L476 355Q468 380 451 395T405 410Q384 410 367 400T338 374T320 338T313 297Q313 266 330 248T374 230Z" />
+<glyph unicode="A" glyph-name="A" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441Z" />
+<glyph unicode="B" glyph-name="B" horiz-adv-x="652" d="M160 590Q169 647 227 647H417Q516 647 566 610T617 501Q617 443 587 399T479 332Q537 316 565 282T593 201Q593 159 580 123T535 59T453 16T328 0H34L160 590ZM329 83Q415 83 451 113T488 205Q488 245 457 268T362 291H199L154 83H329ZM367 365Q435 365 473 397T511 483Q508 564 393 564H257L214 365H367Z" />
+<glyph unicode="C" glyph-name="C" horiz-adv-x="683" d="M371 -12Q303 -12 248 7T154 63T94 152T72 270Q72 348 99 419T175 543T290 627T435 659Q510 659 558 638T634 588Q656 567 666 545T676 507Q676 487 666 475T644 457T621 450T608 450Q564 568 434 568Q380 568 334 545T253 483T200 393T180 283Q180 236 194 199T234 135T296 95T375 80Q438 80 485 105T561 175Q562 176 570 174T588 166T605 150T613 124Q613 96 581 64Q551 34 496 11T371 -12Z" />
+<glyph unicode="D" glyph-name="D" horiz-adv-x="700" d="M160 590Q169 647 227 647H343Q493 647 576 577T660 378Q660 301 635 232T560 112T434 30T257 0H34L160 590ZM272 89Q344 89 397 111T486 171T538 258T555 364Q555 455 498 506T334 558H255L156 89H272Z" />
+<glyph unicode="E" glyph-name="E" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590Z" />
+<glyph unicode="F" glyph-name="F" horiz-adv-x="577" d="M160 590Q169 647 227 647H625Q617 612 614 594T608 567L606 556H255L211 350H485Q484 343 482 336Q480 330 479 322T476 306Q472 287 466 263H194L139 0H35L160 590Z" />
+<glyph unicode="G" glyph-name="G" horiz-adv-x="745" d="M595 -4Q570 -4 555 12T546 69Q518 33 470 11T358 -12Q294 -12 241 7T151 61T93 148T72 265Q72 343 99 414T176 540T293 627T442 660Q517 660 571 637T654 584Q672 567 681 548T691 512Q691 491 681 479T659 462T636 457T624 458Q605 506 557 538T443 570Q386 570 338 547T254 483T199 388T179 274Q179 177 232 124T376 71Q424 71 458 84T518 122Q530 134 539 144T555 169T569 204T583 257H382L400 341H638Q694 341 682 284L622 3Q622 1 614 -1T595 -4Z" />
+<glyph unicode="H" glyph-name="H" horiz-adv-x="709" d="M163 604Q168 628 179 639T214 651H228Q251 651 261 638T265 601L217 369H547L597 604Q602 628 613 639T648 651H663Q685 651 695 638T700 600L571 0H469L528 279H198L137 0H34L163 604Z" />
+<glyph unicode="I" glyph-name="I" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0Z" />
+<glyph unicode="J" glyph-name="J" horiz-adv-x="495" d="M185 -12Q136 -12 102 2T46 38Q32 52 26 64T20 89Q20 107 27 119T44 138T62 147T71 148Q87 116 116 96T183 76Q236 76 265 108T310 214L403 647H505L410 198Q387 92 334 40T185 -12Z" />
+<glyph unicode="K" glyph-name="K" horiz-adv-x="608" d="M478 -6Q460 -6 445 0T416 19T387 52T353 100L204 313L138 0H35L163 604Q168 629 179 640T214 651H228Q251 651 261 638T265 600L213 354L568 660Q568 660 577 658T596 650T614 635T623 610Q623 593 614 579T582 547L308 322L448 131Q464 109 476 95T499 72T520 59T545 54Q546 54 546 45T540 24T520 4T478 -6Z" />
+<glyph unicode="L" glyph-name="L" horiz-adv-x="546" d="M163 604Q168 628 179 639T214 651H228Q251 651 261 638T265 601L157 92H433Q478 92 478 63Q478 53 474 37T460 11Q455 5 446 3T420 0H34L163 604Z" />
+<glyph unicode="M" glyph-name="M" horiz-adv-x="783" d="M160 592Q166 620 183 635T229 651H255Q284 651 295 639T311 598L390 254L618 600Q628 615 636 625T653 641T671 649T694 651H724Q753 651 764 634T771 590L645 0H548L658 518L445 194Q431 173 418 163T375 152Q339 152 329 165T314 199L242 515L131 0H34L160 592Z" />
+<glyph unicode="N" glyph-name="N" horiz-adv-x="716" d="M160 592Q172 651 223 651H240Q260 651 272 642T295 610L516 155L621 647H717L590 51Q579 -4 531 -4H523Q503 -4 492 5T468 37L239 509L131 0H35L160 592Z" />
+<glyph unicode="O" glyph-name="O" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79Z" />
+<glyph unicode="P" glyph-name="P" horiz-adv-x="603" d="M160 590Q169 647 227 647H375Q485 647 543 602T602 472Q602 360 534 297T331 233H188L137 0H34L160 590ZM343 316Q415 316 456 353T498 460Q498 510 466 536T366 562H257L205 316H343Z" />
+<glyph unicode="Q" glyph-name="Q" horiz-adv-x="774" d="M635 -13Q607 -13 590 -2T547 43Q505 17 459 3T362 -11Q291 -11 238 10T147 70T92 159T73 271Q73 354 102 425T180 548T296 630T437 660Q507 660 561 639T652 579T708 489T727 377Q727 293 698 223T617 100L621 95Q639 71 654 59T685 41Q686 41 686 33T680 14T665 -4T635 -13ZM371 79Q435 79 490 114L391 247Q391 247 401 256T423 276T445 297T456 305L556 173Q587 211 605 260T623 370Q623 411 610 447T573 511T512 553T430 569Q382 569 337 548T256 488T199 396T178 278Q178 237 191 201T228 137T289 95T371 79Z" />
+<glyph unicode="R" glyph-name="R" horiz-adv-x="621" d="M613 479Q613 379 551 321T376 260L451 133Q463 113 472 99T492 76T516 63T546 58Q547 58 547 48T541 26T521 4T478 -6Q461 -6 447 -2T418 14T390 46T357 99L271 253H191L138 0H35L160 590Q169 647 227 647H386Q498 647 555 605T613 479ZM355 334Q429 334 468 369T507 468Q507 515 474 539T377 563H256L207 334H355Z" />
+<glyph unicode="S" glyph-name="S" horiz-adv-x="580" d="M272 -12Q199 -12 145 8T59 60Q42 78 32 96T22 135Q22 150 29 161T46 178T63 187T72 188Q98 136 149 104T274 72Q342 72 374 99T407 174Q407 196 397 211T369 239T328 262T277 284Q247 296 217 311T163 347T125 397T110 468Q110 507 125 542T170 603T243 644T343 659Q415 659 464 641T541 594Q573 562 573 526Q573 509 566 498T549 481T531 472T521 472Q498 519 453 547T340 575Q283 575 248 548T213 479Q213 455 223 439T251 409T294 385T349 361Q380 349 410 335T463 300T501 252T516 184Q516 141 501 105T455 43T378 3T272 -12Z" />
+<glyph unicode="T" glyph-name="T" horiz-adv-x="627" d="M330 555H157Q112 555 112 584Q112 594 116 610T130 636Q135 642 144 644T170 647H624Q669 647 669 618Q669 608 664 592T651 566Q645 560 637 558T611 555H433L315 0H213L330 555Z" />
+<glyph unicode="U" glyph-name="U" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12Z" />
+<glyph unicode="V" glyph-name="V" horiz-adv-x="653" d="M114 618Q113 621 118 627T131 638T151 647T175 651Q195 651 208 638T226 599L306 120L604 652Q604 653 615 653T641 648T667 632T679 599Q679 587 674 574T654 537L334 0H233L114 618Z" />
+<glyph unicode="W" glyph-name="W" horiz-adv-x="958" d="M148 627Q148 633 165 642T204 652Q230 652 243 636T255 592L260 113L472 527Q483 550 499 559T542 569Q576 569 590 555T607 516L640 113L877 650Q877 651 889 651T916 648T943 633T956 600Q956 589 952 576T937 540L683 0H565L527 453L287 0H171L148 627Z" />
+<glyph unicode="X" glyph-name="X" horiz-adv-x="630" d="M495 -11Q458 -11 433 17T383 105L316 259L64 -11Q64 -12 54 -10T33 -2T13 14T3 41Q3 66 36 98L269 332L121 647H230L346 385L601 659Q601 659 611 657T632 650T652 634T662 607Q662 582 629 549L392 312L470 145Q482 118 492 102T512 76T535 64T562 60Q563 60 563 49T557 25T537 0T495 -11Z" />
+<glyph unicode="Y" glyph-name="Y" horiz-adv-x="605" d="M258 267L88 647H196L322 351L578 655Q578 655 588 654T609 647T630 632T640 603Q640 589 633 575T605 538L360 262L305 0H201L258 267Z" />
+<glyph unicode="Z" glyph-name="Z" horiz-adv-x="605" d="M1 0Q3 8 5 19Q7 27 9 38T14 61Q19 83 28 98T60 136L487 557H181Q125 557 125 594Q125 603 128 612T134 629T141 642T146 647H627Q625 638 623 628Q621 619 619 608T614 586Q609 564 601 549T569 511L143 90H470Q526 90 526 53Q526 44 523 35T517 18T510 5T505 0H1Z" />
+<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="379" d="M-6 -198Q-5 -191 -2 -181Q0 -173 2 -161T9 -131L163 593Q165 607 167 617T172 635L175 650H383Q424 650 424 624Q424 615 420 600T407 576Q402 571 394 569T371 566H249L105 -113H222Q263 -113 263 -140Q263 -149 259 -164T247 -187Q241 -192 233 -195T210 -198H-6Z" />
+<glyph unicode="\" glyph-name="backslash" horiz-adv-x="373" d="M101 653H190L274 -203H186L101 653Z" />
+<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="379" d="M379 650Q377 643 375 633Q373 625 371 613T364 583L210 -141Q207 -155 205 -165T201 -183L198 -198H-10Q-51 -198 -51 -172Q-51 -163 -47 -148T-34 -124Q-29 -119 -21 -117T2 -114H124L268 565H151Q110 565 110 592Q110 601 114 616T126 639Q132 644 140 647T163 650H379Z" />
+<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="534" d="M208 444Q194 444 187 452T179 472Q179 491 196 512T245 560Q261 573 277 584T308 605T332 619T348 625Q355 623 376 605T417 562Q439 536 448 517T458 483Q458 465 448 455T423 444Q407 444 393 458T361 504Q356 514 350 525T336 550Q325 537 315 526T293 503Q268 475 247 460T208 444Z" />
+<glyph unicode="_" glyph-name="underscore" horiz-adv-x="513" d="M10 -82Q-31 -82 -31 -57Q-31 -48 -27 -34T-15 -11Q-9 -5 -2 -3T21 0H384Q425 0 425 -26Q425 -34 421 -49T409 -72Q403 -77 396 -79T373 -82H10Z" />
+<glyph unicode="`" glyph-name="grave" horiz-adv-x="534" d="M408 555Q405 552 389 553T350 562T299 582T246 613Q209 640 209 672Q209 690 222 702T254 715Q266 715 279 710T303 695Q325 678 345 655T380 611T402 574T408 555Z" />
+<glyph unicode="a" glyph-name="a" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70Z" />
+<glyph unicode="b" glyph-name="b" horiz-adv-x="617" d="M313 -12Q254 -12 210 15T143 88V85Q132 36 114 16T64 -4Q49 -4 38 1T28 8L169 690H268L211 405Q241 447 281 470T377 493Q415 493 450 480T512 440T555 374T571 283Q571 227 553 174T500 80T419 13T313 -12ZM350 412Q301 412 259 385T191 314L174 230Q168 198 174 169T197 118T242 83T306 69Q346 69 377 87T429 134T462 200T473 275Q473 307 463 332T437 375T398 402T350 412Z" />
+<glyph unicode="c" glyph-name="c" horiz-adv-x="550" d="M277 -12Q227 -12 185 3T111 46T62 116T44 210Q44 266 65 317T123 407T210 469T318 493Q379 493 420 476T487 432Q507 412 516 391T526 352Q526 331 516 320T494 303T471 298T460 301Q451 349 416 379T321 410Q280 410 247 394T190 350T153 288T140 217Q140 152 179 112T286 71Q335 71 368 93T425 152Q426 153 434 152T451 146T467 132T475 109Q475 82 443 50Q417 25 375 7T277 -12Z" />
+<glyph unicode="d" glyph-name="d" horiz-adv-x="622" d="M495 -9Q450 -9 430 15T413 83Q382 37 341 13T243 -12Q204 -12 169 1T108 42T65 108T49 198Q49 254 67 307T120 401T201 468T307 493Q364 493 408 469T477 400L538 690H635L515 110Q506 72 514 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM269 70Q300 70 328 81T378 113T417 161T440 223L457 303Q439 354 408 383T315 413Q274 413 243 395T190 348T157 281T146 206Q146 174 156 149T183 106T222 79T269 70Z" />
+<glyph unicode="e" glyph-name="e" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419Z" />
+<glyph unicode="f" glyph-name="f" horiz-adv-x="387" d="M157 400H58Q58 403 59 407Q60 410 60 414T63 426Q65 434 68 448T73 471Q73 473 73 475T74 479Q74 481 75 481H174L185 528Q203 616 247 655T356 694Q420 694 448 664Q464 648 464 627Q464 616 460 607T450 592T439 583T433 581Q423 595 404 604T361 614Q330 614 310 593T278 522L269 481H398Q397 478 396 474Q395 471 395 467T392 456L383 410Q382 407 382 406T381 402V400H255L171 0H72L157 400Z" />
+<glyph unicode="g" glyph-name="g" horiz-adv-x="619" d="M231 -200Q163 -200 120 -182T50 -138Q32 -120 24 -103T16 -71Q16 -52 24 -40T43 -20T62 -11T72 -11Q89 -59 127 -87T237 -116Q301 -116 342 -81T398 26L412 94Q380 52 339 30T244 8Q205 8 170 21T108 61T65 127T49 216Q49 268 68 317T123 406T207 469T314 493Q369 493 412 468T478 401L497 481H593L496 21Q473 -86 409 -143T231 -200ZM270 88Q300 88 328 98T379 127T418 171T441 228L458 308Q444 356 410 384T321 412Q282 412 250 396T194 352T159 291T146 222Q146 190 156 166T183 124T222 97T270 88Z" />
+<glyph unicode="h" glyph-name="h" horiz-adv-x="591" d="M210 401Q236 444 278 468T370 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L160 647Q165 671 176 682T210 694H224Q270 694 258 644L210 401Z" />
+<glyph unicode="i" glyph-name="i" horiz-adv-x="267" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM216 584Q187 584 172 596T157 630Q157 656 173 675T219 694Q248 694 263 682T278 649Q278 622 262 603T216 584Z" />
+<glyph unicode="j" glyph-name="j" horiz-adv-x="268" d="M-30 -202Q-64 -202 -86 -194T-121 -173Q-137 -157 -137 -138Q-137 -127 -133 -118T-124 -102T-114 -91T-109 -88Q-99 -102 -81 -111T-41 -121Q-15 -121 1 -105T26 -46L138 481H236L122 -60Q106 -135 68 -168T-30 -202ZM220 584Q191 584 177 596T162 630Q162 656 178 675T224 694Q253 694 267 682T282 649Q282 622 266 603T220 584Z" />
+<glyph unicode="k" glyph-name="k" horiz-adv-x="506" d="M380 -9Q362 -9 349 -5T322 12T294 45T259 101L175 239Q171 220 167 201Q159 166 150 119Q139 66 125 0H26L169 690H268L183 274L444 493Q444 494 452 492T469 485T486 469T494 443Q494 416 456 386L274 241L345 132Q360 108 371 94T392 72T415 61T444 57Q445 57 445 47T439 24T420 2T380 -9Z" />
+<glyph unicode="l" glyph-name="l" horiz-adv-x="267" d="M135 -9Q80 -9 62 23T55 111L176 690H274L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9Z" />
+<glyph unicode="m" glyph-name="m" horiz-adv-x="905" d="M773 -9Q735 -9 715 12T695 69Q695 90 702 121T720 193Q730 229 736 260T743 309Q743 355 719 381T655 407Q611 407 580 387T519 321L452 0H353L410 273Q423 333 400 370T328 407Q284 407 254 387T193 320L125 0H26L128 480Q130 485 153 485Q165 485 176 482T195 470T207 447T208 410Q237 451 277 472T364 493Q419 493 457 465T509 389Q539 435 583 464T690 493Q725 493 754 481T803 448T835 397T846 334Q846 308 838 270T818 189Q807 152 801 124T794 81Q794 61 804 52T835 41Q836 41 835 33T827 16T808 -1T773 -9Z" />
+<glyph unicode="n" glyph-name="n" horiz-adv-x="591" d="M153 485Q165 485 176 482T196 469T208 443T208 403Q236 445 277 469T369 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L128 480Q130 485 153 485Z" />
+<glyph unicode="o" glyph-name="o" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67Z" />
+<glyph unicode="p" glyph-name="p" horiz-adv-x="617" d="M128 480Q130 485 153 485Q166 485 177 481T197 468T208 442T207 400Q238 445 279 469T377 493Q415 493 450 480T512 440T555 374T571 283Q571 227 553 174T500 80T419 13T313 -12Q250 -12 207 17T144 89L85 -190H-13L128 480ZM350 412Q301 412 260 385T192 314L174 230Q168 198 174 169T197 118T242 83T306 69Q346 69 377 87T429 134T462 200T473 275Q473 307 463 332T437 375T398 402T350 412Z" />
+<glyph unicode="q" glyph-name="q" horiz-adv-x="619" d="M408 75Q376 33 337 11T243 -12Q204 -12 169 1T108 42T65 108T49 198Q49 254 67 307T120 401T201 468T307 493Q364 493 408 469T478 400Q489 447 506 466T556 485Q571 485 581 480T592 473L451 -190H352L408 75ZM269 70Q300 70 328 81T378 113T417 161T440 223L457 303Q439 354 408 383T315 413Q274 413 243 395T190 348T157 281T146 206Q146 174 156 149T183 106T222 79T269 70Z" />
+<glyph unicode="r" glyph-name="r" horiz-adv-x="406" d="M153 485Q165 485 176 481T196 468T208 442T207 400Q231 445 263 469T339 493Q371 493 393 479T416 438Q416 420 410 407T395 387T381 376T373 374Q363 385 348 393T309 401Q282 401 260 386T220 344T189 279T166 196L125 0H26L128 480Q130 485 153 485Z" />
+<glyph unicode="s" glyph-name="s" horiz-adv-x="490" d="M228 -12Q165 -12 117 6T42 51Q31 62 24 75T17 101Q17 114 23 124T36 140T50 148T58 150Q82 114 124 89T224 63Q272 63 297 81T322 130Q322 157 296 171T224 202Q198 211 173 222T127 249T94 287T81 343Q81 376 94 403T131 451T190 482T268 493Q324 493 367 477T435 434Q460 410 460 384Q460 370 454 360T439 344T423 336T415 336Q395 371 355 394T267 418Q220 418 197 399T174 353Q174 324 202 309T278 275Q305 265 330 254T376 228T408 191T421 138Q421 70 371 29T228 -12Z" />
+<glyph unicode="t" glyph-name="t" horiz-adv-x="400" d="M218 -13Q143 -13 109 32T93 166L142 400H64Q64 403 65 407Q66 410 66 414T69 426Q71 434 74 448T79 471Q79 473 79 475T80 479Q80 481 81 481H159L167 520Q176 565 193 583T243 602Q259 602 270 597T280 590L257 481H406Q405 478 404 474Q403 471 403 467T400 456L391 410Q390 407 390 406T389 402V400H239L189 168Q178 113 193 90T244 66Q264 66 281 75T307 94Q309 96 317 86T326 58Q326 35 307 16Q294 3 272 -5T218 -13Z" />
+<glyph unicode="u" glyph-name="u" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9Z" />
+<glyph unicode="v" glyph-name="v" horiz-adv-x="525" d="M115 328Q107 385 90 405T40 426Q39 426 40 435T47 456T66 477T104 487Q141 487 165 464Q184 444 193 411T208 327L233 103L456 486Q456 487 467 487T490 482T513 467T524 438Q524 425 519 413T501 380L263 0H161L115 328Z" />
+<glyph unicode="w" glyph-name="w" horiz-adv-x="758" d="M123 330Q122 387 105 406T55 429Q53 429 54 438T61 458T82 478T121 487Q156 487 178 465Q213 430 212 328L209 104L346 381Q359 407 374 414T413 422Q442 422 457 410T475 368L497 103L674 486Q674 486 684 486T707 482T729 468T742 440Q744 429 740 417T725 385L530 0H430L397 306L235 0H127L123 330Z" />
+<glyph unicode="x" glyph-name="x" horiz-adv-x="523" d="M61 -10Q61 -10 53 -8T34 0T16 15T7 40Q7 56 15 69T44 100L195 244L74 481H181L269 291L464 491Q465 491 473 489T492 481T510 466T518 441Q518 425 510 412T481 381L317 224L357 145Q370 119 381 103T404 79T429 68T460 64Q461 64 461 53T455 30T435 6T396 -5Q378 -5 362 0T331 19T301 57T269 120L244 177L61 -10Z" />
+<glyph unicode="y" glyph-name="y" horiz-adv-x="527" d="M37 -203Q-21 -203 -46 -177Q-62 -162 -62 -140Q-62 -131 -58 -123T-49 -108T-40 -99T-34 -97Q-25 -108 -8 -114T21 -121Q35 -121 49 -116T80 -96T117 -57T165 7L179 28Q148 34 142 75L114 328Q108 381 90 403T40 426Q39 426 39 435T46 456T67 477T108 487Q150 487 174 450T207 327L227 99L447 481H546L251 -21Q195 -116 145 -159T37 -203Z" />
+<glyph unicode="z" glyph-name="z" horiz-adv-x="518" d="M10 0Q11 7 13 16Q15 23 16 32T20 50Q24 68 30 81T57 113L374 402H148Q98 402 98 435Q98 442 100 450T106 465T112 476T116 481H502Q500 473 498 465Q496 458 495 449T491 431Q487 413 481 400T454 369L138 79H375Q425 79 425 46Q425 38 423 30T417 16T411 5T407 0H10Z" />
+<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="467" d="M297 -200Q244 -200 199 -155Q156 -111 138 -41T129 117L80 153Q54 172 46 183T38 213Q38 230 43 251T63 287Q70 294 79 299T114 313L175 337Q195 425 226 480T295 574Q338 618 380 635T461 652Q484 652 497 641T511 610Q511 596 503 585T493 573Q456 571 419 555T356 511Q319 475 298 421T258 285L128 231L231 157Q203 -22 270 -89Q283 -101 299 -110T341 -125Q343 -126 346 -133T349 -151Q349 -174 336 -187T297 -200Z" />
+<glyph unicode="|" glyph-name="bar" horiz-adv-x="290" d="M47 -198Q27 -198 18 -185T13 -147L173 606Q178 630 189 641T224 653H236Q256 653 266 640T271 603L111 -150Q106 -175 94 -186T59 -198H47Z" />
+<glyph unicode="}" glyph-name="braceright" horiz-adv-x="467" d="M-1 -200Q-24 -200 -37 -189T-50 -158Q-50 -143 -43 -132T-33 -121Q5 -119 41 -102T105 -59Q141 -22 162 32T203 167L333 221L229 296Q257 474 190 541Q167 565 120 578Q118 579 115 585T111 603Q111 626 124 639T164 652Q216 652 261 607Q305 564 322 494T331 335L380 300Q407 281 414 270T422 239Q422 222 417 201T398 165Q391 158 382 153T347 139L286 116Q265 28 234 -28T165 -122Q122 -165 80 -182T-1 -200Z" />
+<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="534" d="M333 159Q306 159 270 178T198 234Q186 208 171 187T139 165Q123 165 115 174T107 194Q107 213 115 231T135 264T164 287T197 296Q224 296 260 277T332 221Q344 247 359 268T391 290Q407 290 415 281T423 261Q423 241 415 223T395 191T366 168T333 159Z" />
+<glyph unicode="&#xA0;" glyph-name="uni00A0" horiz-adv-x="170" />
+<glyph unicode="&#xA1;" glyph-name="exclamdown" horiz-adv-x="294" d="M188 460Q207 460 213 452T218 422L162 -2H32L159 431Q164 446 170 453T188 460ZM229 654Q286 654 286 612Q286 593 282 577T266 549Q258 541 247 537T217 533Q160 533 160 575Q160 594 165 610T181 638Q189 646 199 650T229 654Z" />
+<glyph unicode="&#xA2;" glyph-name="cent" horiz-adv-x="574" d="M244 -99Q227 -99 214 -90T206 -52L224 37Q154 57 111 111T67 251Q67 306 87 356T142 445T225 508T330 536L347 612Q352 639 362 649T389 660H394Q411 660 424 650T433 612L415 529Q449 521 474 507T517 475Q537 455 546 434T556 393Q556 372 545 361T521 346T497 341T486 345Q478 382 455 408T394 446L322 110Q367 113 398 134T450 191Q451 192 459 191T477 184T495 170T503 146Q503 134 496 120T472 89Q448 64 406 46T308 27L292 -52Q286 -78 276 -88T250 -99H244ZM163 258Q163 210 185 174T247 121L317 450Q282 444 254 426T205 382T174 323T163 258Z" />
+<glyph unicode="&#xA3;" glyph-name="sterling" horiz-adv-x="595" d="M51 0Q9 0 9 27Q9 36 13 51T26 75Q36 86 61 86H104L154 257H90Q54 257 54 281Q54 290 58 304T70 326Q76 332 83 334T105 337H177L207 440Q224 499 245 540T293 608T354 647T431 660Q477 660 510 647T564 612Q581 596 588 578T596 544Q596 523 587 512T567 494T546 487T536 488Q525 533 498 554T428 576Q405 576 387 569T352 543T323 496T296 422L271 337H419Q458 337 458 311Q458 303 455 289T443 267Q437 262 430 260T408 257H247L198 86H484Q513 86 525 77T537 50Q537 33 529 17T518 0H51Z" />
+<glyph unicode="&#xA4;" glyph-name="currency" horiz-adv-x="662" d="M117 367Q80 367 80 390Q80 399 83 410T93 428Q103 438 125 438H185Q200 477 220 512T268 575Q311 618 360 639T473 660Q527 660 566 644T632 601Q649 584 657 563T666 522Q666 502 657 490T637 472T617 465T606 465Q589 522 553 548T463 574Q424 574 394 561T338 522Q321 505 309 484T285 438H474Q511 438 511 416Q511 406 508 395T497 377Q487 367 466 367H260Q254 344 250 323T244 284H441Q478 284 478 261Q478 251 474 240T464 222Q454 212 433 212H242Q250 152 286 113T388 73Q436 73 472 94T542 161Q543 162 550 160T565 151T579 136T586 114Q586 85 554 53Q526 26 481 7T384 -12Q272 -12 210 49T142 212H83Q47 212 47 235Q47 244 50 255T60 274Q72 284 92 284H145Q148 303 152 323T162 367H117Z" />
+<glyph unicode="&#xA5;" glyph-name="yen" horiz-adv-x="622" d="M120 255Q82 255 82 279Q82 289 85 300T96 319Q106 329 129 329H253L129 616Q128 619 132 624T144 636T163 646T188 651Q204 651 217 642T239 609L333 360L563 655Q563 655 573 654T594 648T615 632T625 604Q625 591 618 576T590 538L409 329H525Q563 329 563 306Q563 296 560 284T549 265Q539 255 516 255H361L346 187H495Q533 187 533 163Q533 153 530 142T519 123Q509 113 486 113H331L316 43Q311 17 298 7T263 -4H259Q238 -4 229 7T224 44L238 113H90Q52 113 52 136Q52 146 55 158T66 177Q76 187 99 187H254L268 255H120Z" />
+<glyph unicode="&#xA6;" glyph-name="brokenbar" horiz-adv-x="290" d="M143 256Q123 256 114 269T109 307L173 606Q178 630 189 641T224 653H236Q256 653 266 640T271 603L207 304Q202 279 190 268T155 256H143ZM47 -198Q27 -198 18 -185T13 -147L77 152Q82 176 93 187T128 199H140Q160 199 170 186T175 149L111 -150Q106 -175 94 -186T59 -198H47Z" />
+<glyph unicode="&#xA7;" glyph-name="section" horiz-adv-x="576" d="M226 -168Q148 -168 98 -150T21 -104Q5 -88 -1 -73T-8 -44Q-8 -29 -1 -18T15 0T31 10T40 12Q62 -31 110 -60T233 -90Q293 -90 323 -68T354 -8Q354 9 344 21T317 44T276 63T227 82Q197 92 166 104T110 134T70 175T54 233Q54 264 64 288T93 332T135 364T188 384Q162 401 146 426T129 489Q129 526 144 557T188 611T258 646T349 659Q421 659 465 641T534 599Q548 585 555 570T562 544Q562 529 555 518T538 500T520 491T511 490Q488 531 447 556T346 581Q289 581 259 560T228 504Q228 486 238 473T265 448T306 428T357 408Q387 397 417 385T471 356T510 316T525 261Q525 229 515 204T486 160T444 129T392 109Q419 93 436 69T454 6Q454 -32 439 -63T395 -118T324 -155T226 -168ZM318 143Q341 144 361 151T398 171T423 201T432 238Q432 258 422 272T394 296T353 316T302 334Q291 338 280 341T257 350Q211 343 179 319T147 257Q147 236 157 221T186 195T228 174T281 155L318 143Z" />
+<glyph unicode="&#xA8;" glyph-name="dieresis" horiz-adv-x="534" d="M434 546Q405 546 390 558T375 594Q375 623 392 643T438 663Q467 663 482 651T497 615Q497 586 480 566T434 546ZM251 546Q222 546 207 558T192 594Q192 623 209 643T255 663Q284 663 298 651T313 615Q313 586 297 566T251 546Z" />
+<glyph unicode="&#xA9;" glyph-name="copyright" horiz-adv-x="788" d="M411 -13Q338 -13 278 12T174 81T106 187T81 322Q81 395 106 457T175 564T279 634T412 660Q485 660 545 635T649 566T717 460T742 325Q742 252 717 190T648 83T544 13T411 -13ZM413 41Q473 41 523 61T608 119T664 208T684 323Q684 384 664 436T607 525T520 584T410 606Q350 606 300 586T215 528T159 439T139 324Q139 263 159 211T216 122T303 63T413 41ZM420 142Q381 142 348 155T291 192T254 249T241 322Q241 361 255 394T293 451T349 488T420 502Q462 502 491 489T535 461Q553 441 553 421Q553 411 547 403T534 390T520 383T513 383Q483 433 420 433Q399 433 381 425T348 402T326 367T317 323Q317 299 325 279T348 243T381 220T423 212Q457 212 480 226T516 266Q517 267 523 266T537 259T551 246T557 227Q557 206 535 183Q520 168 491 155T420 142Z" />
+<glyph unicode="&#xAA;" glyph-name="ordfeminine" horiz-adv-x="535" d="M439 257Q402 257 386 278T371 334Q347 296 310 275T229 254Q197 254 170 265T123 296T91 347T79 416Q79 461 95 505T139 583T207 639T294 660Q338 660 372 641T423 586Q431 623 447 638T490 654Q503 654 512 650T521 643L460 354Q453 324 459 311T490 295Q491 295 490 289T484 276T469 263T439 257ZM255 326Q280 326 303 336T344 362T375 401T394 450L406 509Q394 547 369 569T301 591Q269 591 244 577T201 538T174 485T164 425Q164 378 190 352T255 326ZM204 -7Q153 -7 153 31Q153 48 157 62T171 87Q179 95 188 98T215 102Q266 102 266 64Q266 47 262 33T247 7Q239 0 230 -3T204 -7Z" />
+<glyph unicode="&#xAB;" glyph-name="guillemotleft" horiz-adv-x="553" d="M226 79Q203 79 178 99T126 151Q112 168 101 185T80 217T66 242T61 256Q61 259 68 267T87 289T115 317T152 348Q184 374 214 392T266 410Q283 410 291 401T300 377Q300 358 276 334T213 287Q197 277 179 266T141 244Q172 222 202 193Q234 164 248 147T264 113Q265 99 254 89T226 79ZM417 80Q394 80 371 99T326 148Q314 163 304 179T287 209T275 233T271 246Q271 249 279 258T302 282T334 312T372 344Q405 371 437 391T493 411Q510 411 519 402T528 378Q528 358 499 331T427 278Q410 267 391 256T351 234Q383 211 405 190Q432 164 445 148T458 116Q458 101 447 91T417 80Z" />
+<glyph unicode="&#xAC;" glyph-name="logicalnot" horiz-adv-x="600" d="M49 380H543V96H453V297H49V380Z" />
+<glyph unicode="&#xAD;" glyph-name="uni00AD" horiz-adv-x="427" d="M48 198Q49 202 50 208Q51 217 56 236L59 250Q61 258 62 265T65 278Q66 284 67 289H378Q377 284 376 278Q375 273 374 266T370 250L367 236Q362 217 361 208Q360 202 359 198H48Z" />
+<glyph unicode="&#xAE;" glyph-name="registered" horiz-adv-x="788" d="M411 -13Q338 -13 278 12T174 81T106 187T81 322Q81 395 106 457T175 564T279 634T412 660Q485 660 545 635T649 566T717 460T742 325Q742 252 717 190T648 83T544 13T411 -13ZM413 41Q473 41 523 61T608 119T664 208T684 323Q684 384 664 436T607 525T520 584T410 606Q350 606 300 586T215 528T159 439T139 324Q139 263 159 211T216 122T303 63T413 41ZM564 389Q564 341 536 316T470 285L559 177Q560 175 558 171T549 161T535 152T517 148Q506 148 493 154T469 176L393 276H354V184Q354 149 321 149H311Q295 149 287 160T279 191V457Q279 477 289 488T318 499H424Q493 499 528 472T564 389ZM423 334Q455 334 472 348T489 386Q489 438 420 438H352V334H423Z" />
+<glyph unicode="&#xAF;" glyph-name="uni00AF" horiz-adv-x="534" d="M227 562Q187 562 187 588Q187 597 190 611T202 634Q212 644 238 644H462Q503 644 503 618Q503 610 499 596T487 573Q477 562 451 562H227Z" />
+<glyph unicode="&#xB0;" glyph-name="degree" horiz-adv-x="421" d="M238 345Q177 345 137 381T96 480Q96 516 109 549T145 607T201 646T273 661Q303 661 329 652T374 624T404 581T415 525Q415 489 402 457T366 399T310 360T238 345ZM247 407Q268 407 285 416T316 442T336 478T344 521Q344 554 322 576T264 599Q243 599 226 590T195 564T175 528T167 485Q167 452 189 430T247 407Z" />
+<glyph unicode="&#xB1;" glyph-name="plusminus" horiz-adv-x="634" d="M181 395Q141 395 141 421Q141 430 145 443T156 464Q167 475 190 475H325L352 604Q357 627 367 636T392 646H402Q419 646 429 636T435 602L408 475H535Q574 475 574 449Q574 440 571 427T559 406Q548 395 525 395H391L365 271Q360 248 349 239T324 229H314Q297 229 287 239T282 273L308 395H181ZM110 95Q69 95 69 121Q69 131 73 145T85 166Q96 177 120 177H477Q518 177 518 151Q518 141 514 127T502 106Q491 95 467 95H110Z" />
+<glyph unicode="&#xB2;" glyph-name="twosuperior" horiz-adv-x="370" d="M121 383Q107 369 103 349H269Q310 349 310 322Q310 309 303 297T295 285H63Q46 285 39 298T33 332Q39 386 72 421Q92 442 118 454T173 476Q195 483 213 490T243 504T262 522T269 546Q269 566 252 578T206 590Q174 590 155 573T125 531Q125 531 118 531T102 534T86 545T79 568Q79 580 85 593T103 617Q120 634 149 644T216 655Q276 655 311 627T346 551Q346 522 336 503T309 469T269 446T218 428Q185 417 161 408T121 383Z" />
+<glyph unicode="&#xB3;" glyph-name="threesuperior" horiz-adv-x="370" d="M315 395Q315 374 307 353T281 316T234 289T163 278Q121 278 92 288T48 314Q35 326 31 337T26 360Q26 377 33 386T50 400T68 405T76 404Q81 374 103 357T163 340Q201 340 219 357T238 400Q238 420 224 432T181 444H163Q134 444 134 466Q134 472 136 480T144 494Q149 500 156 501T178 503H200Q239 503 252 517T266 550Q266 569 249 581T203 593Q170 593 150 578T119 544Q119 543 112 543T97 546T83 557T76 577Q76 597 94 615Q112 633 141 644T208 655Q266 655 304 630T343 562Q343 527 323 503T260 470Q288 459 301 440T315 395Z" />
+<glyph unicode="&#xB4;" glyph-name="acute" horiz-adv-x="534" d="M260 555Q258 558 269 574T301 611T346 653T396 690Q420 705 445 705Q465 705 476 693T488 663Q488 647 476 633T442 605Q413 588 382 577T324 560T280 553T260 555Z" />
+<glyph unicode="&#xB5;" glyph-name="uni00B5" horiz-adv-x="592" d="M73 -263Q18 -263 0 -231T-7 -143L123 481H222L161 192Q154 144 174 112Q197 74 254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 138 4Q131 7 124 11L92 -144Q84 -182 92 -198T135 -216Q136 -216 135 -223T128 -239T109 -255T73 -263Z" />
+<glyph unicode="&#xB6;" glyph-name="paragraph" horiz-adv-x="630" d="M459 -4Q442 -4 434 6T431 36L554 614Q561 650 593 650H604Q640 650 632 610L509 32Q504 12 495 4T469 -4H459ZM324 -4Q307 -4 299 6T296 36L348 283H297Q191 283 135 321T79 437Q79 480 93 518T137 585T216 630T332 647H453Q477 647 488 634T494 597L374 32Q369 12 360 4T334 -4H324Z" />
+<glyph unicode="&#xB7;" glyph-name="uni00B7" horiz-adv-x="266" d="M128 184Q71 184 71 226Q71 245 76 261T92 289Q100 297 110 301T140 305Q197 305 197 263Q197 244 193 228T177 200Q169 192 158 188T128 184Z" />
+<glyph unicode="&#xB8;" glyph-name="cedilla" horiz-adv-x="534" d="M130 -234Q125 -238 117 -236T102 -230T90 -219T89 -207L191 -45Q192 -44 204 -44T230 -49T255 -63T267 -88Q267 -103 260 -115T235 -142L130 -234Z" />
+<glyph unicode="&#xB9;" glyph-name="onesuperior" horiz-adv-x="370" d="M72 285Q38 285 40 311Q40 315 42 323T49 337Q54 345 61 348T84 352H143L190 557Q167 536 139 536Q116 536 104 548T91 579Q91 591 95 600T104 615T113 623T119 626Q125 618 135 612T162 605Q179 605 196 617T221 651H251Q285 651 278 613L218 352H273Q307 352 304 326Q304 322 302 314T295 300Q288 285 261 285H72Z" />
+<glyph unicode="&#xBA;" glyph-name="ordmasculine" horiz-adv-x="481" d="M265 254Q224 254 191 267T134 303T97 360T84 433Q84 477 99 518T143 590T212 641T301 660Q342 660 375 647T433 611T470 554T484 481Q484 436 469 395T424 323T355 273T265 254ZM190 -7Q139 -7 139 31Q139 48 143 62T157 87Q165 95 175 98T202 102Q253 102 253 64Q253 47 249 33T234 7Q226 0 217 -3T190 -7ZM268 323Q297 323 322 335T365 369T393 419T404 478Q404 502 396 522T374 558T340 581T298 590Q269 590 244 578T201 544T173 494T163 435Q163 411 171 391T193 355T227 332T268 323Z" />
+<glyph unicode="&#xBB;" glyph-name="guillemotright" horiz-adv-x="553" d="M288 78Q272 78 264 87T255 111Q255 130 279 154T342 201Q358 211 376 222T414 244Q397 256 382 268T352 294Q320 323 306 340T291 375Q290 389 301 399T329 409Q352 409 377 389T428 337Q441 320 453 303T474 271T488 246T493 232Q493 229 486 221T467 199T439 171T403 140Q371 114 341 96T288 78ZM62 77Q45 77 36 86T27 110Q27 130 56 157T127 210Q145 220 164 231T203 254Q186 266 174 276T150 298Q122 324 110 340T97 372Q97 387 108 397T137 407Q160 407 183 388T228 340Q240 324 250 308T267 279T279 255T284 242Q284 239 276 230T253 206T221 176T183 143Q150 117 117 97T62 77Z" />
+<glyph unicode="&#xBC;" glyph-name="onequarter" horiz-adv-x="826" d="M72 285Q38 285 40 311Q40 315 42 323T49 337Q54 345 61 348T84 352H143L190 557Q167 536 139 536Q116 536 104 548T91 579Q91 591 95 600T104 615T113 623T119 626Q125 618 135 612T162 605Q179 605 196 617T221 651H251Q285 651 278 613L218 352H273Q307 352 304 326Q304 322 302 314T295 300Q288 285 261 285H72ZM729 634Q730 635 735 633T747 627T757 615T759 596Q757 585 749 573T723 544Q715 536 688 510T624 448T544 371T463 292Q444 273 420 250T368 200T313 147T259 94Q197 34 132 -29Q130 -31 124 -29T112 -22T102 -9T100 10Q103 24 110 33T136 62Q143 69 170 95T234 157T313 233T395 312Q414 331 438 354T490 404T546 457T601 510Q663 570 729 634ZM672 -4Q657 -4 648 4T644 35L655 90H515Q512 90 502 102T491 135Q491 149 498 161T521 191L655 335Q671 353 683 359T723 366Q756 366 769 354T775 312L741 152H769Q799 152 799 131Q799 125 796 115T787 98Q783 94 778 92T760 90H728L716 34Q711 12 704 4T682 -4H672ZM559 151H672L705 309L559 151Z" />
+<glyph unicode="&#xBD;" glyph-name="onehalf" horiz-adv-x="826" d="M577 98Q563 84 559 64H725Q766 64 766 37Q766 24 759 12T751 0H519Q502 0 495 13T489 47Q495 101 528 136Q548 157 574 169T629 191Q651 198 669 205T699 219T718 237T725 261Q725 281 708 293T662 305Q630 305 611 288T581 246Q581 246 574 246T558 249T542 260T535 283Q535 295 541 308T559 332Q576 349 605 359T672 370Q732 370 767 342T802 266Q802 237 792 218T765 184T725 161T674 143Q641 132 617 123T577 98ZM701 634Q702 635 707 633T719 627T729 615T731 596Q729 585 721 573T695 544Q687 536 660 510T596 448T516 371T435 292Q416 273 392 250T340 200T285 147T231 94Q169 34 104 -29Q102 -31 96 -29T84 -22T74 -9T72 10Q75 24 82 33T108 62Q115 69 142 95T206 157T285 233T367 312Q386 331 410 354T462 404T518 457T573 510Q635 570 701 634ZM72 285Q38 285 40 311Q40 315 42 323T49 337Q54 345 61 348T84 352H143L190 557Q167 536 139 536Q116 536 104 548T91 579Q91 591 95 600T104 615T113 623T119 626Q125 618 135 612T162 605Q179 605 196 617T221 651H251Q285 651 278 613L218 352H273Q307 352 304 326Q304 322 302 314T295 300Q288 285 261 285H72Z" />
+<glyph unicode="&#xBE;" glyph-name="threequarters" horiz-adv-x="826" d="M672 -4Q657 -4 648 4T644 35L655 90H515Q512 90 502 102T491 135Q491 149 498 161T521 191L655 335Q671 353 683 359T723 366Q756 366 769 354T775 312L741 152H769Q799 152 799 131Q799 125 796 115T787 98Q783 94 778 92T760 90H728L716 34Q711 12 704 4T682 -4H672ZM315 395Q315 374 307 353T281 316T234 289T163 278Q121 278 92 288T48 314Q35 326 31 337T26 360Q26 377 33 386T50 400T68 405T76 404Q81 374 103 357T163 340Q201 340 219 357T238 400Q238 420 224 432T181 444H163Q134 444 134 466Q134 472 136 480T144 494Q149 500 156 501T178 503H200Q239 503 252 517T266 550Q266 569 249 581T203 593Q170 593 150 578T119 544Q119 543 112 543T97 546T83 557T76 577Q76 597 94 615Q112 633 141 644T208 655Q266 655 304 630T343 562Q343 527 323 503T260 470Q288 459 301 440T315 395ZM725 634Q726 635 731 633T743 627T753 615T755 596Q753 585 745 573T719 544Q711 536 684 510T620 448T540 371T459 292Q440 273 416 250T364 200T309 147T255 94Q193 34 128 -29Q126 -31 120 -29T108 -22T98 -9T96 10Q99 24 106 33T132 62Q139 69 166 95T230 157T309 233T391 312Q410 331 434 354T486 404T542 457T597 510Q659 570 725 634ZM559 151H672L705 309L559 151Z" />
+<glyph unicode="&#xBF;" glyph-name="questiondown" horiz-adv-x="513" d="M384 462Q384 459 382 453Q379 441 373 413Q364 369 337 334T247 278Q218 268 196 258T158 235T135 206T127 166Q127 125 157 97T242 69Q296 69 333 100T388 179Q388 180 397 179T417 173T437 159T446 133Q446 112 435 91T405 50Q376 21 335 3T236 -15Q188 -15 149 -2T83 36T40 92T25 163Q25 207 40 237T79 287T131 319T185 340Q236 357 258 380T285 422Q286 428 287 435T291 448Q293 455 294 462H384ZM357 652Q414 652 414 610Q414 591 410 575T394 547Q386 539 375 535T345 531Q288 531 288 573Q288 592 293 608T309 636Q317 644 327 648T357 652Z" />
+<glyph unicode="&#xC0;" glyph-name="Agrave" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM463 721Q460 718 444 719T405 728T354 748T301 779Q264 806 264 838Q264 856 277 868T309 881Q321 881 334 876T358 861Q380 844 400 821T435 777T457 740T463 721Z" />
+<glyph unicode="&#xC1;" glyph-name="Aacute" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM359 721Q357 724 368 740T400 777T445 819T495 856Q519 871 544 871Q564 871 575 859T587 829Q587 813 575 799T541 771Q512 754 481 743T423 726T379 719T359 721Z" />
+<glyph unicode="&#xC2;" glyph-name="Acircumflex" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM305 713Q291 713 284 721T276 741Q276 760 293 781T342 829Q358 842 374 853T405 874T429 888T445 894Q452 892 473 874T514 831Q536 805 545 786T555 752Q555 734 545 724T520 713Q504 713 490 727T458 773Q453 783 447 794T433 819Q422 806 412 795T390 772Q365 744 344 729T305 713Z" />
+<glyph unicode="&#xC3;" glyph-name="Atilde" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM489 712Q462 712 426 731T354 787Q342 761 327 740T295 718Q279 718 271 727T263 747Q263 766 271 784T291 817T320 840T353 849Q380 849 416 830T488 774Q500 800 515 821T547 843Q563 843 571 834T579 814Q579 794 571 776T551 744T522 721T489 712Z" />
+<glyph unicode="&#xC4;" glyph-name="Adieresis" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM510 712Q481 712 466 724T451 760Q451 789 468 809T514 829Q543 829 558 817T573 781Q573 752 556 732T510 712ZM327 712Q298 712 283 724T268 760Q268 789 285 809T331 829Q360 829 374 817T389 781Q389 752 373 732T327 712Z" />
+<glyph unicode="&#xC5;" glyph-name="Aring" horiz-adv-x="665" d="M-33 0L325 599Q325 600 326 601L339 622Q339 622 341 624Q300 652 300 705Q300 732 310 756T337 799T379 829T433 840Q484 840 515 812T547 738Q547 699 526 666T471 615L476 592H475L593 0H488L451 190H191L82 0H-33ZM416 654Q432 654 445 660T467 678T481 703T486 731Q486 754 471 770T431 786Q415 786 402 780T380 762T366 737T361 709Q361 686 376 670T416 654ZM441 273L391 546L233 273H441Z" />
+<glyph unicode="&#xC6;" glyph-name="AE" horiz-adv-x="893" d="M80 -5Q80 -6 69 -6T44 -2T19 13T7 45Q7 59 12 72T31 108L329 605Q341 625 357 636T405 647H864Q909 647 909 618Q909 608 904 592T891 567Q885 560 877 558T851 556H481L518 374H740Q782 374 782 347Q782 338 778 322T766 298Q760 293 752 290T728 287H535L574 91H762Q807 91 807 63Q807 52 802 36T789 11Q778 0 749 0H546Q513 0 497 11T476 55L451 190H191L80 -5ZM441 273L391 546L233 273H441Z" />
+<glyph unicode="&#xC7;" glyph-name="Ccedilla" horiz-adv-x="683" d="M371 -12Q303 -12 248 7T154 63T94 152T72 270Q72 348 99 419T175 543T290 627T435 659Q510 659 558 638T634 588Q656 567 666 545T676 507Q676 487 666 475T644 457T621 450T608 450Q564 568 434 568Q380 568 334 545T253 483T200 393T180 283Q180 236 194 199T234 135T296 95T375 80Q438 80 485 105T561 175Q562 176 570 174T588 166T605 150T613 124Q613 96 581 64Q551 34 496 11T371 -12ZM270 -234Q265 -238 257 -236T242 -230T230 -219T229 -207L331 -45Q332 -44 344 -44T370 -49T395 -63T407 -88Q407 -103 400 -115T375 -142L270 -234Z" />
+<glyph unicode="&#xC8;" glyph-name="Egrave" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM491 721Q488 718 472 719T433 728T382 748T329 779Q292 806 292 838Q292 856 305 868T337 881Q349 881 362 876T386 861Q408 844 428 821T463 777T485 740T491 721Z" />
+<glyph unicode="&#xC9;" glyph-name="Eacute" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM353 721Q351 724 362 740T394 777T439 819T489 856Q513 871 538 871Q558 871 569 859T581 829Q581 813 569 799T535 771Q506 754 475 743T417 726T373 719T353 721Z" />
+<glyph unicode="&#xCA;" glyph-name="Ecircumflex" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM316 713Q302 713 295 721T287 741Q287 760 304 781T353 829Q369 842 385 853T416 874T440 888T456 894Q463 892 484 874T525 831Q547 805 556 786T566 752Q566 734 556 724T531 713Q515 713 501 727T469 773Q464 783 458 794T444 819Q433 806 423 795T401 772Q376 744 355 729T316 713Z" />
+<glyph unicode="&#xCB;" glyph-name="Edieresis" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM513 712Q484 712 469 724T454 760Q454 789 471 809T517 829Q546 829 561 817T576 781Q576 752 559 732T513 712ZM330 712Q301 712 286 724T271 760Q271 789 288 809T334 829Q363 829 377 817T392 781Q392 752 376 732T330 712Z" />
+<glyph unicode="&#xCC;" glyph-name="Igrave" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM292 721Q289 718 273 719T234 728T183 748T130 779Q93 806 93 838Q93 856 106 868T138 881Q150 881 163 876T187 861Q209 844 229 821T264 777T286 740T292 721Z" />
+<glyph unicode="&#xCD;" glyph-name="Iacute" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM193 721Q191 724 202 740T234 777T279 819T329 856Q353 871 378 871Q398 871 409 859T421 829Q421 813 409 799T375 771Q346 754 315 743T257 726T213 719T193 721Z" />
+<glyph unicode="&#xCE;" glyph-name="Icircumflex" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM138 713Q124 713 117 721T109 741Q109 760 126 781T175 829Q191 842 207 853T238 874T262 888T278 894Q285 892 306 874T347 831Q369 805 378 786T388 752Q388 734 378 724T353 713Q337 713 323 727T291 773Q286 783 280 794T266 819Q255 806 245 795T223 772Q198 744 177 729T138 713Z" />
+<glyph unicode="&#xCF;" glyph-name="Idieresis" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM343 712Q314 712 299 724T284 760Q284 789 301 809T347 829Q376 829 391 817T406 781Q406 752 389 732T343 712ZM160 712Q131 712 116 724T101 760Q101 789 118 809T164 829Q193 829 207 817T222 781Q222 752 206 732T160 712Z" />
+<glyph unicode="&#xD0;" glyph-name="Eth" horiz-adv-x="739" d="M140 0Q72 0 86 67L133 285H81Q46 285 46 309Q46 318 49 332T61 354Q66 359 73 361T93 363H149L198 590Q207 647 265 647H382Q533 647 616 577T700 378Q700 301 675 232T600 112T473 30T296 0H140ZM311 89Q383 89 436 111T525 171T577 258T594 364Q594 455 537 506T374 558H293L252 363H421Q459 363 459 339Q459 331 456 317T444 295Q439 290 432 288T410 285H235L194 89H311Z" />
+<glyph unicode="&#xD1;" glyph-name="Ntilde" horiz-adv-x="716" d="M160 592Q172 651 223 651H240Q260 651 272 642T295 610L516 155L621 647H717L590 51Q579 -4 531 -4H523Q503 -4 492 5T468 37L239 509L131 0H35L160 592ZM539 712Q512 712 476 731T404 787Q392 761 377 740T345 718Q329 718 321 727T313 747Q313 766 321 784T341 817T370 840T403 849Q430 849 466 830T538 774Q550 800 565 821T597 843Q613 843 621 834T629 814Q629 794 621 776T601 744T572 721T539 712Z" />
+<glyph unicode="&#xD2;" glyph-name="Ograve" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM523 721Q520 718 504 719T465 728T414 748T361 779Q324 806 324 838Q324 856 337 868T369 881Q381 881 394 876T418 861Q440 844 460 821T495 777T517 740T523 721Z" />
+<glyph unicode="&#xD3;" glyph-name="Oacute" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM375 721Q373 724 384 740T416 777T461 819T511 856Q535 871 560 871Q580 871 591 859T603 829Q603 813 591 799T557 771Q528 754 497 743T439 726T395 719T375 721Z" />
+<glyph unicode="&#xD4;" glyph-name="Ocircumflex" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM352 713Q338 713 331 721T323 741Q323 760 340 781T389 829Q405 842 421 853T452 874T476 888T492 894Q499 892 520 874T561 831Q583 805 592 786T602 752Q602 734 592 724T567 713Q551 713 537 727T505 773Q500 783 494 794T480 819Q469 806 459 795T437 772Q412 744 391 729T352 713Z" />
+<glyph unicode="&#xD5;" glyph-name="Otilde" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM529 712Q502 712 466 731T394 787Q382 761 367 740T335 718Q319 718 311 727T303 747Q303 766 311 784T331 817T360 840T393 849Q420 849 456 830T528 774Q540 800 555 821T587 843Q603 843 611 834T619 814Q619 794 611 776T591 744T562 721T529 712Z" />
+<glyph unicode="&#xD6;" glyph-name="Odieresis" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM559 712Q530 712 515 724T500 760Q500 789 517 809T563 829Q592 829 607 817T622 781Q622 752 605 732T559 712ZM376 712Q347 712 332 724T317 760Q317 789 334 809T380 829Q409 829 423 817T438 781Q438 752 422 732T376 712Z" />
+<glyph unicode="&#xD7;" glyph-name="multiply" horiz-adv-x="580" d="M415 64Q395 64 381 83L290 203L147 82Q131 67 113 67Q107 67 100 71T88 82T78 96T74 108Q74 129 93 145L240 269L148 389Q136 405 136 419Q136 431 150 445T179 459Q198 459 213 440L302 320L445 441Q461 456 479 456Q485 456 492 452T504 441T514 427T518 415Q518 405 514 396T499 378L353 255L445 134Q458 117 458 104Q458 92 444 78T415 64Z" />
+<glyph unicode="&#xD8;" glyph-name="Oslash" horiz-adv-x="765" d="M50 -34Q48 -36 42 -33T29 -25T17 -11T11 7Q11 21 18 32T40 58L113 122Q73 190 73 271Q73 354 102 425T180 548T295 630T434 660Q497 660 548 641T637 586L737 676Q739 677 745 675T758 666T770 652T776 634Q776 620 769 609T747 583L685 528Q727 460 727 377Q727 294 698 223T620 100T505 19T366 -11Q301 -11 250 9T160 64L50 -34ZM374 77Q421 77 466 98T546 159T602 252T623 370Q623 420 605 461L233 128Q258 104 293 91T374 77ZM176 278Q176 228 192 189L564 522Q539 545 505 558T426 571Q378 571 333 550T253 489T197 396T176 278Z" />
+<glyph unicode="&#xD9;" glyph-name="Ugrave" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM500 713Q497 710 481 711T442 720T391 740T338 771Q301 798 301 830Q301 848 314 860T346 873Q358 873 371 868T395 853Q417 836 437 813T472 769T494 732T500 713Z" />
+<glyph unicode="&#xDA;" glyph-name="Uacute" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM353 713Q351 716 362 732T394 769T439 811T489 848Q513 863 538 863Q558 863 569 851T581 821Q581 805 569 791T535 763Q506 746 475 735T417 718T373 711T353 713Z" />
+<glyph unicode="&#xDB;" glyph-name="Ucircumflex" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM328 705Q314 705 307 713T299 733Q299 752 316 773T365 821Q381 834 397 845T428 866T452 880T468 886Q475 884 496 866T537 823Q559 797 568 778T578 744Q578 726 568 716T543 705Q527 705 513 719T481 765Q476 775 470 786T456 811Q445 798 435 787T413 764Q388 736 367 721T328 705Z" />
+<glyph unicode="&#xDC;" glyph-name="Udieresis" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM534 704Q505 704 490 716T475 752Q475 781 492 801T538 821Q567 821 582 809T597 773Q597 744 580 724T534 704ZM351 704Q322 704 307 716T292 752Q292 781 309 801T355 821Q384 821 398 809T413 773Q413 744 397 724T351 704Z" />
+<glyph unicode="&#xDD;" glyph-name="Yacute" horiz-adv-x="605" d="M258 267L88 647H196L322 351L578 655Q578 655 588 654T609 647T630 632T640 603Q640 589 633 575T605 538L360 262L305 0H201L258 267ZM327 713Q325 716 336 732T368 769T413 811T463 848Q487 863 512 863Q532 863 543 851T555 821Q555 805 543 791T509 763Q480 746 449 735T391 718T347 711T327 713Z" />
+<glyph unicode="&#xDE;" glyph-name="Thorn" horiz-adv-x="609" d="M81 -4Q59 -4 49 8T44 47L162 604Q168 628 179 639T214 651H230Q251 651 261 639T266 605L250 531H350Q461 531 519 487T577 357Q577 245 509 183T308 121H163L146 39Q141 16 130 6T96 -4H81ZM320 204Q395 204 435 241T475 345Q475 446 343 446H232L181 204H320Z" />
+<glyph unicode="&#xDF;" glyph-name="germandbls" horiz-adv-x="586" d="M324 -12Q243 -12 205 26Q194 37 190 47T185 69Q185 83 191 93T204 110T218 119T225 121Q237 101 258 85T319 69Q343 69 364 78T401 105T426 146T435 199Q435 234 413 262T332 311Q307 321 296 332T284 364Q284 389 294 405T313 427Q394 427 432 449T471 529Q471 567 441 590T361 613Q311 613 276 578T224 464L126 0H27L127 469Q152 587 211 640T363 694Q461 694 514 651T568 539Q568 468 523 425T383 374Q448 356 489 313T531 206Q531 159 515 119T471 50T405 5T324 -12Z" />
+<glyph unicode="&#xE0;" glyph-name="agrave" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM425 555Q422 552 406 553T367 562T316 582T263 613Q226 640 226 672Q226 690 239 702T271 715Q283 715 296 710T320 695Q342 678 362 655T397 611T419 574T425 555Z" />
+<glyph unicode="&#xE1;" glyph-name="aacute" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM299 555Q297 558 308 574T340 611T385 653T435 690Q459 705 484 705Q504 705 515 693T527 663Q527 647 515 633T481 605Q452 588 421 577T363 560T319 553T299 555Z" />
+<glyph unicode="&#xE2;" glyph-name="acircumflex" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM264 547Q250 547 243 555T235 575Q235 594 252 615T301 663Q317 676 333 687T364 708T388 722T404 728Q411 726 432 708T473 665Q495 639 504 620T514 586Q514 568 504 558T479 547Q463 547 449 561T417 607Q412 617 406 628T392 653Q381 640 371 629T349 606Q324 578 303 563T264 547Z" />
+<glyph unicode="&#xE3;" glyph-name="atilde" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM447 546Q420 546 384 565T312 621Q300 595 285 574T253 552Q237 552 229 561T221 581Q221 600 229 618T249 651T278 674T311 683Q338 683 374 664T446 608Q458 634 473 655T505 677Q521 677 529 668T537 648Q537 628 529 610T509 578T480 555T447 546Z" />
+<glyph unicode="&#xE4;" glyph-name="adieresis" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM469 546Q440 546 425 558T410 594Q410 623 427 643T473 663Q502 663 517 651T532 615Q532 586 515 566T469 546ZM286 546Q257 546 242 558T227 594Q227 623 244 643T290 663Q319 663 333 651T348 615Q348 586 332 566T286 546Z" />
+<glyph unicode="&#xE5;" glyph-name="aring" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM381 548Q330 548 299 577T268 651Q268 678 278 702T305 745T347 775T401 787Q452 787 483 758T514 684Q514 657 504 633T477 589T435 559T381 548ZM383 601Q416 601 435 625T454 678Q454 701 439 717T398 734Q382 734 369 728T347 710T333 685T328 657Q328 634 343 618T383 601Z" />
+<glyph unicode="&#xE6;" glyph-name="ae" horiz-adv-x="885" d="M198 -12Q117 -12 72 20T26 110Q26 156 45 189T108 243T219 275T384 288H396L397 292Q408 349 380 381T283 413Q228 413 195 396T138 341Q138 341 131 342T114 348T98 362T90 386Q90 401 101 420T137 455T199 482T289 493Q360 493 406 466T466 387Q504 438 558 465T681 493Q768 493 815 457T862 360Q862 324 849 295T803 246T718 215T583 204Q557 204 529 205T468 209V191Q468 134 501 99T600 64Q653 64 690 83T754 139Q755 140 762 139T777 133T792 120T799 98Q799 83 794 73T775 48Q745 19 702 4T599 -12Q520 -12 474 18T408 97Q378 46 326 17T198 -12ZM218 59Q246 59 272 68T318 93T353 132T374 181L384 228L356 227Q287 225 242 218T170 197T134 165T123 121Q123 94 148 77T218 59ZM677 419Q644 419 614 408T560 379T516 334T484 278Q510 275 532 274T574 273Q632 273 670 280T731 298T763 325T772 357Q772 381 749 400T677 419Z" />
+<glyph unicode="&#xE7;" glyph-name="ccedilla" horiz-adv-x="550" d="M277 -12Q227 -12 185 3T111 46T62 116T44 210Q44 266 65 317T123 407T210 469T318 493Q379 493 420 476T487 432Q507 412 516 391T526 352Q526 331 516 320T494 303T471 298T460 301Q451 349 416 379T321 410Q280 410 247 394T190 350T153 288T140 217Q140 152 179 112T286 71Q335 71 368 93T425 152Q426 153 434 152T451 146T467 132T475 109Q475 82 443 50Q417 25 375 7T277 -12ZM178 -234Q173 -238 165 -236T150 -230T138 -219T137 -207L239 -45Q240 -44 252 -44T278 -49T303 -63T315 -88Q315 -103 308 -115T283 -142L178 -234Z" />
+<glyph unicode="&#xE8;" glyph-name="egrave" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM414 555Q411 552 395 553T356 562T305 582T252 613Q215 640 215 672Q215 690 228 702T260 715Q272 715 285 710T309 695Q331 678 351 655T386 611T408 574T414 555Z" />
+<glyph unicode="&#xE9;" glyph-name="eacute" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM286 555Q284 558 295 574T327 611T372 653T422 690Q446 705 471 705Q491 705 502 693T514 663Q514 647 502 633T468 605Q439 588 408 577T350 560T306 553T286 555Z" />
+<glyph unicode="&#xEA;" glyph-name="ecircumflex" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM254 547Q240 547 233 555T225 575Q225 594 242 615T291 663Q307 676 323 687T354 708T378 722T394 728Q401 726 422 708T463 665Q485 639 494 620T504 586Q504 568 494 558T469 547Q453 547 439 561T407 607Q402 617 396 628T382 653Q371 640 361 629T339 606Q314 578 293 563T254 547Z" />
+<glyph unicode="&#xEB;" glyph-name="edieresis" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM459 546Q430 546 415 558T400 594Q400 623 417 643T463 663Q492 663 507 651T522 615Q522 586 505 566T459 546ZM276 546Q247 546 232 558T217 594Q217 623 234 643T280 663Q309 663 323 651T338 615Q338 586 322 566T276 546Z" />
+<glyph unicode="&#xEC;" glyph-name="igrave" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM257 555Q254 552 238 553T199 562T148 582T95 613Q58 640 58 672Q58 690 71 702T103 715Q115 715 128 710T152 695Q174 678 194 655T229 611T251 574T257 555Z" />
+<glyph unicode="&#xED;" glyph-name="iacute" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM127 555Q125 558 136 574T168 611T213 653T263 690Q287 705 312 705Q332 705 343 693T355 663Q355 647 343 633T309 605Q280 588 249 577T191 560T147 553T127 555Z" />
+<glyph unicode="&#xEE;" glyph-name="icircumflex" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM94 547Q80 547 73 555T65 575Q65 594 82 615T131 663Q147 676 163 687T194 708T218 722T234 728Q241 726 262 708T303 665Q325 639 334 620T344 586Q344 568 334 558T309 547Q293 547 279 561T247 607Q242 617 236 628T222 653Q211 640 201 629T179 606Q154 578 133 563T94 547Z" />
+<glyph unicode="&#xEF;" glyph-name="idieresis" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM299 546Q270 546 255 558T240 594Q240 623 257 643T303 663Q332 663 347 651T362 615Q362 586 345 566T299 546ZM116 546Q87 546 72 558T57 594Q57 623 74 643T120 663Q149 663 163 651T178 615Q178 586 162 566T116 546Z" />
+<glyph unicode="&#xF0;" glyph-name="null" horiz-adv-x="597" d="M235 476Q232 474 223 484T214 514Q214 528 221 540T253 564L329 601Q268 634 195 639Q193 639 194 648T201 670T222 691T262 701Q297 701 339 686T421 641L542 701Q545 703 554 693T563 663Q563 649 556 637T524 613L476 590Q500 563 519 530T549 458T563 373T557 276Q549 218 528 164T455 58Q424 27 380 8T276 -12Q225 -12 182 1T107 39T58 102T40 187Q40 237 57 281T107 360T185 413T287 433Q356 433 403 405T470 340Q473 411 452 464T392 556L235 476ZM271 66Q306 66 334 78T387 113Q413 139 430 178T455 263Q449 280 436 297T402 327T357 349T303 358Q260 358 229 345T177 308T146 253T136 186Q136 129 174 98T271 66Z" />
+<glyph unicode="&#xF1;" glyph-name="ntilde" horiz-adv-x="591" d="M153 485Q165 485 176 482T196 469T208 443T208 403Q236 445 277 469T369 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L128 480Q130 485 153 485ZM440 546Q413 546 377 565T305 621Q293 595 278 574T246 552Q230 552 222 561T214 581Q214 600 222 618T242 651T271 674T304 683Q331 683 367 664T439 608Q451 634 466 655T498 677Q514 677 522 668T530 648Q530 628 522 610T502 578T473 555T440 546Z" />
+<glyph unicode="&#xF2;" glyph-name="ograve" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM402 555Q399 552 383 553T344 562T293 582T240 613Q203 640 203 672Q203 690 216 702T248 715Q260 715 273 710T297 695Q319 678 339 655T374 611T396 574T402 555Z" />
+<glyph unicode="&#xF3;" glyph-name="oacute" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM277 555Q275 558 286 574T318 611T363 653T413 690Q437 705 462 705Q482 705 493 693T505 663Q505 647 493 633T459 605Q430 588 399 577T341 560T297 553T277 555Z" />
+<glyph unicode="&#xF4;" glyph-name="ocircumflex" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM238 547Q224 547 217 555T209 575Q209 594 226 615T275 663Q291 676 307 687T338 708T362 722T378 728Q385 726 406 708T447 665Q469 639 478 620T488 586Q488 568 478 558T453 547Q437 547 423 561T391 607Q386 617 380 628T366 653Q355 640 345 629T323 606Q298 578 277 563T238 547Z" />
+<glyph unicode="&#xF5;" glyph-name="otilde" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM423 546Q396 546 360 565T288 621Q276 595 261 574T229 552Q213 552 205 561T197 581Q197 600 205 618T225 651T254 674T287 683Q314 683 350 664T422 608Q434 634 449 655T481 677Q497 677 505 668T513 648Q513 628 505 610T485 578T456 555T423 546Z" />
+<glyph unicode="&#xF6;" glyph-name="odieresis" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM443 546Q414 546 399 558T384 594Q384 623 401 643T447 663Q476 663 491 651T506 615Q506 586 489 566T443 546ZM260 546Q231 546 216 558T201 594Q201 623 218 643T264 663Q293 663 307 651T322 615Q322 586 306 566T260 546Z" />
+<glyph unicode="&#xF7;" glyph-name="divide" horiz-adv-x="634" d="M352 378Q299 378 299 417Q299 435 303 450T318 476Q326 484 336 487T363 491Q416 491 416 452Q416 434 412 419T397 393Q389 385 379 382T352 378ZM280 38Q227 38 227 78Q227 95 231 110T246 136Q254 144 264 147T291 151Q344 151 344 112Q344 94 340 79T325 53Q317 45 307 42T280 38ZM133 224Q90 224 96 258Q96 261 98 268T103 279Q108 292 118 298T148 305H511Q534 305 542 297T547 271Q547 267 545 260T540 250Q535 237 526 231T496 224H133Z" />
+<glyph unicode="&#xF8;" glyph-name="oslash" horiz-adv-x="588" d="M41 -35Q39 -37 33 -35T20 -27T8 -13T2 4Q2 13 8 24T28 48L78 92Q63 118 55 147T46 210Q46 266 65 317T121 407T207 469T318 493Q360 493 397 479T464 441L544 515Q546 516 552 514T565 507T577 494T583 477Q583 467 577 456T557 432L510 389Q543 336 543 271Q543 215 524 164T468 74T382 12T271 -12Q228 -12 191 1T124 40L41 -35ZM276 65Q314 65 346 81T402 126T439 190T453 267Q453 301 440 332L183 101Q221 65 276 65ZM136 214Q136 178 149 149L405 381Q386 398 363 407T313 416Q275 416 243 400T187 355T150 291T136 214Z" />
+<glyph unicode="&#xF9;" glyph-name="ugrave" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM402 555Q399 552 383 553T344 562T293 582T240 613Q203 640 203 672Q203 690 216 702T248 715Q260 715 273 710T297 695Q319 678 339 655T374 611T396 574T402 555Z" />
+<glyph unicode="&#xFA;" glyph-name="uacute" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM284 555Q282 558 293 574T325 611T370 653T420 690Q444 705 469 705Q489 705 500 693T512 663Q512 647 500 633T466 605Q437 588 406 577T348 560T304 553T284 555Z" />
+<glyph unicode="&#xFB;" glyph-name="ucircumflex" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM245 547Q231 547 224 555T216 575Q216 594 233 615T282 663Q298 676 314 687T345 708T369 722T385 728Q392 726 413 708T454 665Q476 639 485 620T495 586Q495 568 485 558T460 547Q444 547 430 561T398 607Q393 617 387 628T373 653Q362 640 352 629T330 606Q305 578 284 563T245 547Z" />
+<glyph unicode="&#xFC;" glyph-name="udieresis" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM450 546Q421 546 406 558T391 594Q391 623 408 643T454 663Q483 663 498 651T513 615Q513 586 496 566T450 546ZM267 546Q238 546 223 558T208 594Q208 623 225 643T271 663Q300 663 314 651T329 615Q329 586 313 566T267 546Z" />
+<glyph unicode="&#xFD;" glyph-name="yacute" horiz-adv-x="527" d="M37 -203Q-21 -203 -46 -177Q-62 -162 -62 -140Q-62 -131 -58 -123T-49 -108T-40 -99T-34 -97Q-25 -108 -8 -114T21 -121Q35 -121 49 -116T80 -96T117 -57T165 7L179 28Q148 34 142 75L114 328Q108 381 90 403T40 426Q39 426 39 435T46 456T67 477T108 487Q150 487 174 450T207 327L227 99L447 481H546L251 -21Q195 -116 145 -159T37 -203ZM260 547Q258 550 269 566T301 603T346 645T396 682Q420 697 445 697Q465 697 476 685T488 655Q488 639 476 625T442 597Q413 580 382 569T324 552T280 545T260 547Z" />
+<glyph unicode="&#xFE;" glyph-name="thorn" horiz-adv-x="617" d="M313 -12Q253 -12 209 16T143 89L93 -149Q88 -175 78 -185T48 -196H37Q17 -196 3 -186T-5 -149L162 640Q168 666 178 676T207 687H219Q238 687 252 677T261 640L211 405Q241 447 281 470T377 493Q415 493 450 480T512 440T555 374T571 283Q571 227 553 174T500 80T419 13T313 -12ZM351 412Q301 412 259 385T191 314L174 230Q168 198 174 169T197 118T242 82T305 69Q345 69 376 87T429 134T462 200T473 276Q473 308 463 333T437 376T398 403T351 412Z" />
+<glyph unicode="&#xFF;" glyph-name="ydieresis" horiz-adv-x="527" d="M37 -203Q-21 -203 -46 -177Q-62 -162 -62 -140Q-62 -131 -58 -123T-49 -108T-40 -99T-34 -97Q-25 -108 -8 -114T21 -121Q35 -121 49 -116T80 -96T117 -57T165 7L179 28Q148 34 142 75L114 328Q108 381 90 403T40 426Q39 426 39 435T46 456T67 477T108 487Q150 487 174 450T207 327L227 99L447 481H546L251 -21Q195 -116 145 -159T37 -203ZM415 546Q386 546 371 558T356 594Q356 623 373 643T419 663Q448 663 463 651T478 615Q478 586 461 566T415 546ZM232 546Q203 546 188 558T173 594Q173 623 190 643T236 663Q265 663 279 651T294 615Q294 586 278 566T232 546Z" />
+<glyph unicode="&#x100;" glyph-name="Amacron" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM313 721Q273 721 273 747Q273 756 276 770T288 793Q298 803 324 803H548Q589 803 589 777Q589 769 585 755T573 732Q563 721 537 721H313Z" />
+<glyph unicode="&#x101;" glyph-name="amacron" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM270 562Q230 562 230 588Q230 597 233 611T245 634Q255 644 281 644H505Q546 644 546 618Q546 610 542 596T530 573Q520 562 494 562H270Z" />
+<glyph unicode="&#x102;" glyph-name="Abreve" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H488L451 190H191L82 0H-33ZM441 273L391 546L233 273H441ZM431 705Q404 705 378 715T332 742T299 778T287 817Q287 831 296 842T322 854Q342 854 368 834T430 763Q473 811 503 832T556 854Q572 854 582 845T592 821Q592 804 578 784T542 747T490 717T431 705Z" />
+<glyph unicode="&#x103;" glyph-name="abreve" horiz-adv-x="622" d="M495 -9Q448 -9 429 17T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70ZM389 546Q362 546 336 556T290 583T257 619T245 658Q245 672 254 683T280 695Q300 695 326 675T388 604Q431 652 461 673T514 695Q530 695 540 686T550 662Q550 645 536 625T500 588T448 558T389 546Z" />
+<glyph unicode="&#x104;" glyph-name="Aogonek" horiz-adv-x="665" d="M-33 0L339 622Q349 639 362 646T405 654Q441 654 454 644T471 616L593 0H555Q499 -32 480 -57T461 -102Q461 -133 492 -133Q513 -133 527 -116Q538 -118 546 -126T554 -149Q554 -172 534 -187T477 -202Q435 -202 408 -180T381 -120Q381 -86 407 -54T488 1L451 190H191L82 0H-33ZM441 273L391 546L233 273H441Z" />
+<glyph unicode="&#x105;" glyph-name="aogonek" horiz-adv-x="622" d="M554 38Q554 38 554 34T551 22T542 8T526 -3Q468 -36 449 -61T429 -107Q429 -138 460 -138Q481 -138 495 -121Q506 -123 514 -131T522 -154Q522 -177 502 -192T445 -207Q403 -207 376 -185T349 -125Q349 -90 376 -58T458 -3Q432 7 421 31T414 90Q383 40 336 14T234 -12Q195 -12 161 1T101 40T61 103T46 189Q46 246 66 300T121 397T206 466T315 493Q371 493 414 468T478 400Q483 423 487 438T493 464Q496 475 497 481H593L515 110Q507 72 515 57T554 38ZM261 70Q294 70 324 82T378 117T418 168T442 231L458 306Q443 355 410 384T321 413Q280 413 247 394T191 345T156 276T144 198Q144 137 178 104T261 70Z" />
+<glyph unicode="&#x106;" glyph-name="Cacute" horiz-adv-x="683" d="M371 -12Q303 -12 248 7T154 63T94 152T72 270Q72 348 99 419T175 543T290 627T435 659Q510 659 558 638T634 588Q656 567 666 545T676 507Q676 487 666 475T644 457T621 450T608 450Q564 568 434 568Q380 568 334 545T253 483T200 393T180 283Q180 236 194 199T234 135T296 95T375 80Q438 80 485 105T561 175Q562 176 570 174T588 166T605 150T613 124Q613 96 581 64Q551 34 496 11T371 -12ZM365 714Q363 717 374 733T406 770T451 812T501 849Q525 864 550 864Q570 864 581 852T593 822Q593 806 581 792T547 764Q518 747 487 736T429 719T385 712T365 714Z" />
+<glyph unicode="&#x107;" glyph-name="cacute" horiz-adv-x="550" d="M277 -12Q227 -12 185 3T111 46T62 116T44 210Q44 266 65 317T123 407T210 469T318 493Q379 493 420 476T487 432Q507 412 516 391T526 352Q526 331 516 320T494 303T471 298T460 301Q451 349 416 379T321 410Q280 410 247 394T190 350T153 288T140 217Q140 152 179 112T286 71Q335 71 368 93T425 152Q426 153 434 152T451 146T467 132T475 109Q475 82 443 50Q417 25 375 7T277 -12ZM258 555Q256 558 267 574T299 611T344 653T394 690Q418 705 443 705Q463 705 474 693T486 663Q486 647 474 633T440 605Q411 588 380 577T322 560T278 553T258 555Z" />
+<glyph unicode="&#x108;" glyph-name="Ccircumflex" horiz-adv-x="683" d="M371 -12Q303 -12 248 7T154 63T94 152T72 270Q72 348 99 419T175 543T290 627T435 659Q510 659 558 638T634 588Q656 567 666 545T676 507Q676 487 666 475T644 457T621 450T608 450Q564 568 434 568Q380 568 334 545T253 483T200 393T180 283Q180 236 194 199T234 135T296 95T375 80Q438 80 485 105T561 175Q562 176 570 174T588 166T605 150T613 124Q613 96 581 64Q551 34 496 11T371 -12ZM343 706Q329 706 322 714T314 734Q314 753 331 774T380 822Q396 835 412 846T443 867T467 881T483 887Q490 885 511 867T552 824Q574 798 583 779T593 745Q593 727 583 717T558 706Q542 706 528 720T496 766Q491 776 485 787T471 812Q460 799 450 788T428 765Q403 737 382 722T343 706Z" />
+<glyph unicode="&#x109;" glyph-name="ccircumflex" horiz-adv-x="550" d="M277 -12Q227 -12 185 3T111 46T62 116T44 210Q44 266 65 317T123 407T210 469T318 493Q379 493 420 476T487 432Q507 412 516 391T526 352Q526 331 516 320T494 303T471 298T460 301Q451 349 416 379T321 410Q280 410 247 394T190 350T153 288T140 217Q140 152 179 112T286 71Q335 71 368 93T425 152Q426 153 434 152T451 146T467 132T475 109Q475 82 443 50Q417 25 375 7T277 -12ZM229 547Q215 547 208 555T200 575Q200 594 217 615T266 663Q282 676 298 687T329 708T353 722T369 728Q376 726 397 708T438 665Q460 639 469 620T479 586Q479 568 469 558T444 547Q428 547 414 561T382 607Q377 617 371 628T357 653Q346 640 336 629T314 606Q289 578 268 563T229 547Z" />
+<glyph unicode="&#x10A;" glyph-name="Cdotaccent" horiz-adv-x="683" d="M371 -12Q303 -12 248 7T154 63T94 152T72 270Q72 348 99 419T175 543T290 627T435 659Q510 659 558 638T634 588Q656 567 666 545T676 507Q676 487 666 475T644 457T621 450T608 450Q564 568 434 568Q380 568 334 545T253 483T200 393T180 283Q180 236 194 199T234 135T296 95T375 80Q438 80 485 105T561 175Q562 176 570 174T588 166T605 150T613 124Q613 96 581 64Q551 34 496 11T371 -12ZM455 706Q423 706 407 720T391 757Q391 787 409 809T459 831Q491 831 506 817T522 780Q522 750 505 728T455 706Z" />
+<glyph unicode="&#x10B;" glyph-name="cdotaccent" horiz-adv-x="550" d="M277 -12Q227 -12 185 3T111 46T62 116T44 210Q44 266 65 317T123 407T210 469T318 493Q379 493 420 476T487 432Q507 412 516 391T526 352Q526 331 516 320T494 303T471 298T460 301Q451 349 416 379T321 410Q280 410 247 394T190 350T153 288T140 217Q140 152 179 112T286 71Q335 71 368 93T425 152Q426 153 434 152T451 146T467 132T475 109Q475 82 443 50Q417 25 375 7T277 -12ZM350 547Q318 547 302 561T286 598Q286 628 304 650T354 672Q386 672 401 658T417 621Q417 591 400 569T350 547Z" />
+<glyph unicode="&#x10C;" glyph-name="Ccaron" horiz-adv-x="683" d="M371 -12Q303 -12 248 7T154 63T94 152T72 270Q72 348 99 419T175 543T290 627T435 659Q510 659 558 638T634 588Q656 567 666 545T676 507Q676 487 666 475T644 457T621 450T608 450Q564 568 434 568Q380 568 334 545T253 483T200 393T180 283Q180 236 194 199T234 135T296 95T375 80Q438 80 485 105T561 175Q562 176 570 174T588 166T605 150T613 124Q613 96 581 64Q551 34 496 11T371 -12ZM441 693Q434 695 413 713T372 756Q350 782 341 810T331 851Q331 870 341 872T366 875Q395 875 428 814Q433 805 439 794T453 768L496 816Q521 843 542 859T581 875Q595 875 602 867T610 847Q610 827 593 806T544 758Q528 745 512 734T481 714T457 700T441 693Z" />
+<glyph unicode="&#x10D;" glyph-name="ccaron" horiz-adv-x="550" d="M277 -12Q227 -12 185 3T111 46T62 116T44 210Q44 266 65 317T123 407T210 469T318 493Q379 493 420 476T487 432Q507 412 516 391T526 352Q526 331 516 320T494 303T471 298T460 301Q451 349 416 379T321 410Q280 410 247 394T190 350T153 288T140 217Q140 152 179 112T286 71Q335 71 368 93T425 152Q426 153 434 152T451 146T467 132T475 109Q475 82 443 50Q417 25 375 7T277 -12ZM338 532Q331 534 310 552T269 595Q247 621 238 649T228 690Q228 709 238 711T263 714Q292 714 325 653Q330 644 336 633T350 607L393 655Q418 682 439 698T478 714Q492 714 499 706T507 686Q507 666 490 645T441 597Q425 584 409 573T378 553T354 539T338 532Z" />
+<glyph unicode="&#x10E;" glyph-name="Dcaron" horiz-adv-x="700" d="M160 590Q169 647 227 647H343Q493 647 576 577T660 378Q660 301 635 232T560 112T434 30T257 0H34L160 590ZM272 89Q344 89 397 111T486 171T538 258T555 364Q555 455 498 506T334 558H255L156 89H272ZM406 691Q399 693 378 711T337 754Q315 780 306 808T296 849Q296 868 306 870T331 873Q360 873 393 812Q398 803 404 792T418 766L461 814Q486 841 507 857T546 873Q560 873 567 865T575 845Q575 825 558 804T509 756Q493 743 477 732T446 712T422 698T406 691Z" />
+<glyph unicode="&#x10F;" glyph-name="dcaron" horiz-adv-x="805" d="M495 -9Q450 -9 430 15T413 83Q382 37 341 13T243 -12Q204 -12 169 1T108 42T65 108T49 198Q49 254 67 307T120 401T201 468T307 493Q364 493 408 469T477 400L538 690H635L515 110Q506 72 514 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM269 70Q300 70 328 81T378 113T417 161T440 223L457 303Q439 354 408 383T315 413Q274 413 243 395T190 348T157 281T146 206Q146 174 156 149T183 106T222 79T269 70ZM681 429Q677 425 668 426T651 431T638 443T636 458L746 681Q747 683 760 683T790 679T819 665T832 637Q832 622 826 609T804 578L681 429Z" />
+<glyph unicode="&#x110;" glyph-name="Dcroat" horiz-adv-x="739" d="M140 0Q72 0 86 67L133 285H81Q46 285 46 309Q46 318 49 332T61 354Q66 359 73 361T93 363H149L198 590Q207 647 265 647H382Q533 647 616 577T700 378Q700 301 675 232T600 112T473 30T296 0H140ZM311 89Q383 89 436 111T525 171T577 258T594 364Q594 455 537 506T374 558H293L252 363H421Q459 363 459 339Q459 331 456 317T444 295Q439 290 432 288T410 285H235L194 89H311Z" />
+<glyph unicode="&#x111;" glyph-name="dcroat" horiz-adv-x="622" d="M495 -9Q450 -9 430 15T413 83Q382 37 341 13T243 -12Q204 -12 169 1T108 42T65 108T49 198Q49 254 67 307T120 401T201 468T307 493Q364 493 408 469T477 400L511 563H420Q389 563 389 581Q389 589 393 601T402 619Q409 627 428 627H525L538 690H635L622 627H667Q698 627 698 609Q698 601 695 589T686 571Q679 563 660 563H608L515 110Q506 72 514 57T554 38Q555 38 554 31T548 15T530 -1T495 -9ZM269 70Q300 70 328 81T378 113T417 161T440 223L457 303Q439 354 408 383T315 413Q274 413 243 395T190 348T157 281T146 206Q146 174 156 149T183 106T222 79T269 70Z" />
+<glyph unicode="&#x112;" glyph-name="Emacron" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM319 721Q279 721 279 747Q279 756 282 770T294 793Q304 803 330 803H554Q595 803 595 777Q595 769 591 755T579 732Q569 721 543 721H319Z" />
+<glyph unicode="&#x113;" glyph-name="emacron" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM232 562Q192 562 192 588Q192 597 195 611T207 634Q217 644 243 644H467Q508 644 508 618Q508 610 504 596T492 573Q482 562 456 562H232Z" />
+<glyph unicode="&#x114;" glyph-name="Ebreve" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM426 705Q399 705 373 715T327 742T294 778T282 817Q282 831 291 842T317 854Q337 854 363 834T425 763Q468 811 498 832T551 854Q567 854 577 845T587 821Q587 804 573 784T537 747T485 717T426 705Z" />
+<glyph unicode="&#x115;" glyph-name="ebreve" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM349 546Q322 546 296 556T250 583T217 619T205 658Q205 672 214 683T240 695Q260 695 286 675T348 604Q391 652 421 673T474 695Q490 695 500 686T510 662Q510 645 496 625T460 588T408 558T349 546Z" />
+<glyph unicode="&#x116;" glyph-name="Edotaccent" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM426 706Q394 706 378 720T362 757Q362 787 380 809T430 831Q462 831 477 817T493 780Q493 750 476 728T426 706Z" />
+<glyph unicode="&#x117;" glyph-name="edotaccent" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM360 547Q328 547 312 561T296 598Q296 628 314 650T364 672Q396 672 411 658T427 621Q427 591 410 569T360 547Z" />
+<glyph unicode="&#x118;" glyph-name="Eogonek" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H497Q441 -32 422 -57T403 -102Q403 -133 434 -133Q455 -133 469 -116Q480 -118 488 -126T496 -149Q496 -172 476 -187T419 -202Q377 -202 350 -180T323 -120Q323 -86 349 -55T427 0H34L160 590Z" />
+<glyph unicode="&#x119;" glyph-name="eogonek" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q422 31 398 18Q367 1 347 -14T316 -42T300 -67T295 -89Q295 -120 326 -120Q347 -120 361 -103Q372 -105 380 -113T388 -136Q388 -159 368 -174T311 -189Q269 -189 242 -167T215 -107Q215 -81 230 -57T274 -12H262ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419Z" />
+<glyph unicode="&#x11A;" glyph-name="Ecaron" horiz-adv-x="621" d="M160 590Q171 647 227 647H650Q649 641 647 633Q645 626 644 618T641 601Q637 581 631 556H256L217 374H521L503 287H198L157 91H538L519 0H34L160 590ZM411 691Q404 693 383 711T342 754Q320 780 311 808T301 849Q301 868 311 870T336 873Q365 873 398 812Q403 803 409 792T423 766L466 814Q491 841 512 857T551 873Q565 873 572 865T580 845Q580 825 563 804T514 756Q498 743 482 732T451 712T427 698T411 691Z" />
+<glyph unicode="&#x11B;" glyph-name="ecaron" horiz-adv-x="549" d="M262 -12Q207 -12 166 3T98 45T56 108T42 188Q42 254 65 309T130 406T226 470T345 493Q432 493 479 457T526 360Q526 324 513 295T467 246T382 215T247 204Q221 204 193 205T132 209V191Q132 134 165 99T264 64Q317 64 354 83T418 139Q419 140 426 139T441 133T456 120T463 98Q463 83 458 73T439 48Q409 19 366 4T262 -12ZM341 419Q308 419 278 408T224 379T180 334T148 278Q174 275 196 274T238 273Q296 273 334 280T395 298T427 325T436 357Q436 381 413 400T341 419ZM338 532Q331 534 310 552T269 595Q247 621 238 649T228 690Q228 709 238 711T263 714Q292 714 325 653Q330 644 336 633T350 607L393 655Q418 682 439 698T478 714Q492 714 499 706T507 686Q507 666 490 645T441 597Q425 584 409 573T378 553T354 539T338 532Z" />
+<glyph unicode="&#x11C;" glyph-name="Gcircumflex" horiz-adv-x="745" d="M595 -4Q570 -4 555 12T546 69Q518 33 470 11T358 -12Q294 -12 241 7T151 61T93 148T72 265Q72 343 99 414T176 540T293 627T442 660Q517 660 571 637T654 584Q672 567 681 548T691 512Q691 491 681 479T659 462T636 457T624 458Q605 506 557 538T443 570Q386 570 338 547T254 483T199 388T179 274Q179 177 232 124T376 71Q424 71 458 84T518 122Q530 134 539 144T555 169T569 204T583 257H382L400 341H638Q694 341 682 284L622 3Q622 1 614 -1T595 -4ZM336 706Q322 706 315 714T307 734Q307 753 324 774T373 822Q389 835 405 846T436 867T460 881T476 887Q483 885 504 867T545 824Q567 798 576 779T586 745Q586 727 576 717T551 706Q535 706 521 720T489 766Q484 776 478 787T464 812Q453 799 443 788T421 765Q396 737 375 722T336 706Z" />
+<glyph unicode="&#x11D;" glyph-name="gcircumflex" horiz-adv-x="619" d="M231 -200Q163 -200 120 -182T50 -138Q32 -120 24 -103T16 -71Q16 -52 24 -40T43 -20T62 -11T72 -11Q89 -59 127 -87T237 -116Q301 -116 342 -81T398 26L412 94Q380 52 339 30T244 8Q205 8 170 21T108 61T65 127T49 216Q49 268 68 317T123 406T207 469T314 493Q369 493 412 468T478 401L497 481H593L496 21Q473 -86 409 -143T231 -200ZM270 88Q300 88 328 98T379 127T418 171T441 228L458 308Q444 356 410 384T321 412Q282 412 250 396T194 352T159 291T146 222Q146 190 156 166T183 124T222 97T270 88ZM250 547Q236 547 229 555T221 575Q221 594 238 615T287 663Q303 676 319 687T350 708T374 722T390 728Q397 726 418 708T459 665Q481 639 490 620T500 586Q500 568 490 558T465 547Q449 547 435 561T403 607Q398 617 392 628T378 653Q367 640 357 629T335 606Q310 578 289 563T250 547Z" />
+<glyph unicode="&#x11E;" glyph-name="Gbreve" horiz-adv-x="745" d="M595 -4Q570 -4 555 12T546 69Q518 33 470 11T358 -12Q294 -12 241 7T151 61T93 148T72 265Q72 343 99 414T176 540T293 627T442 660Q517 660 571 637T654 584Q672 567 681 548T691 512Q691 491 681 479T659 462T636 457T624 458Q605 506 557 538T443 570Q386 570 338 547T254 483T199 388T179 274Q179 177 232 124T376 71Q424 71 458 84T518 122Q530 134 539 144T555 169T569 204T583 257H382L400 341H638Q694 341 682 284L622 3Q622 1 614 -1T595 -4ZM452 705Q425 705 399 715T353 742T320 778T308 817Q308 831 317 842T343 854Q363 854 389 834T451 763Q494 811 524 832T577 854Q593 854 603 845T613 821Q613 804 599 784T563 747T511 717T452 705Z" />
+<glyph unicode="&#x11F;" glyph-name="gbreve" horiz-adv-x="619" d="M231 -200Q163 -200 120 -182T50 -138Q32 -120 24 -103T16 -71Q16 -52 24 -40T43 -20T62 -11T72 -11Q89 -59 127 -87T237 -116Q301 -116 342 -81T398 26L412 94Q380 52 339 30T244 8Q205 8 170 21T108 61T65 127T49 216Q49 268 68 317T123 406T207 469T314 493Q369 493 412 468T478 401L497 481H593L496 21Q473 -86 409 -143T231 -200ZM270 88Q300 88 328 98T379 127T418 171T441 228L458 308Q444 356 410 384T321 412Q282 412 250 396T194 352T159 291T146 222Q146 190 156 166T183 124T222 97T270 88ZM367 546Q340 546 314 556T268 583T235 619T223 658Q223 672 232 683T258 695Q278 695 304 675T366 604Q409 652 439 673T492 695Q508 695 518 686T528 662Q528 645 514 625T478 588T426 558T367 546Z" />
+<glyph unicode="&#x120;" glyph-name="Gdotaccent" horiz-adv-x="745" d="M595 -4Q570 -4 555 12T546 69Q518 33 470 11T358 -12Q294 -12 241 7T151 61T93 148T72 265Q72 343 99 414T176 540T293 627T442 660Q517 660 571 637T654 584Q672 567 681 548T691 512Q691 491 681 479T659 462T636 457T624 458Q605 506 557 538T443 570Q386 570 338 547T254 483T199 388T179 274Q179 177 232 124T376 71Q424 71 458 84T518 122Q530 134 539 144T555 169T569 204T583 257H382L400 341H638Q694 341 682 284L622 3Q622 1 614 -1T595 -4ZM452 706Q420 706 404 720T388 757Q388 787 406 809T456 831Q488 831 503 817T519 780Q519 750 502 728T452 706Z" />
+<glyph unicode="&#x121;" glyph-name="gdotaccent" horiz-adv-x="619" d="M231 -200Q163 -200 120 -182T50 -138Q32 -120 24 -103T16 -71Q16 -52 24 -40T43 -20T62 -11T72 -11Q89 -59 127 -87T237 -116Q301 -116 342 -81T398 26L412 94Q380 52 339 30T244 8Q205 8 170 21T108 61T65 127T49 216Q49 268 68 317T123 406T207 469T314 493Q369 493 412 468T478 401L497 481H593L496 21Q473 -86 409 -143T231 -200ZM270 88Q300 88 328 98T379 127T418 171T441 228L458 308Q444 356 410 384T321 412Q282 412 250 396T194 352T159 291T146 222Q146 190 156 166T183 124T222 97T270 88ZM365 547Q333 547 317 561T301 598Q301 628 319 650T369 672Q401 672 416 658T432 621Q432 591 415 569T365 547Z" />
+<glyph unicode="&#x122;" glyph-name="Gcommaaccent" horiz-adv-x="745" d="M595 -4Q570 -4 555 12T546 69Q518 33 470 11T358 -12Q294 -12 241 7T151 61T93 148T72 265Q72 343 99 414T176 540T293 627T442 660Q517 660 571 637T654 584Q672 567 681 548T691 512Q691 491 681 479T659 462T636 457T624 458Q605 506 557 538T443 570Q386 570 338 547T254 483T199 388T179 274Q179 177 232 124T376 71Q424 71 458 84T518 122Q530 134 539 144T555 169T569 204T583 257H382L400 341H638Q694 341 682 284L622 3Q622 1 614 -1T595 -4ZM264 -253Q259 -258 251 -257T234 -250T220 -238T218 -224L321 -43Q322 -42 335 -42T363 -47T391 -62T404 -90Q404 -106 398 -119T374 -149L264 -253Z" />
+<glyph unicode="&#x123;" glyph-name="gcommaaccent" horiz-adv-x="619" d="M231 -200Q163 -200 120 -182T50 -138Q32 -120 24 -103T16 -71Q16 -52 24 -40T43 -20T62 -11T72 -11Q89 -59 127 -87T237 -116Q301 -116 342 -81T398 26L412 94Q380 52 339 30T244 8Q205 8 170 21T108 61T65 127T49 216Q49 268 68 317T123 406T207 469T314 493Q369 493 412 468T478 401L497 481H593L496 21Q473 -86 409 -143T231 -200ZM270 88Q300 88 328 98T379 127T418 171T441 228L458 308Q444 356 410 384T321 412Q282 412 250 396T194 352T159 291T146 222Q146 190 156 166T183 124T222 97T270 88ZM408 715Q412 719 421 718T438 712T452 700T454 686L351 505Q350 503 337 504T309 509T281 524T268 552Q268 568 274 581T298 611L408 715Z" />
+<glyph unicode="&#x124;" glyph-name="Hcircumflex" horiz-adv-x="709" d="M163 604Q168 628 179 639T214 651H228Q251 651 261 638T265 601L217 369H547L597 604Q602 628 613 639T648 651H663Q685 651 695 638T700 600L571 0H469L528 279H198L137 0H34L163 604ZM329 706Q315 706 308 714T300 734Q300 753 317 774T366 822Q382 835 398 846T429 867T453 881T469 887Q476 885 497 867T538 824Q560 798 569 779T579 745Q579 727 569 717T544 706Q528 706 514 720T482 766Q477 776 471 787T457 812Q446 799 436 788T414 765Q389 737 368 722T329 706Z" />
+<glyph unicode="&#x125;" glyph-name="hcircumflex" horiz-adv-x="591" d="M210 401Q236 444 278 468T370 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L160 647Q165 671 176 682T210 694H224Q270 694 258 644L210 401ZM117 716Q103 716 96 724T88 744Q88 763 105 784T154 832Q170 845 186 856T217 877T241 891T257 897Q264 895 285 877T326 834Q348 808 357 789T367 755Q367 737 357 727T332 716Q316 716 302 730T270 776Q265 786 259 797T245 822Q234 809 224 798T202 775Q177 747 156 732T117 716Z" />
+<glyph unicode="&#x126;" glyph-name="Hbar" horiz-adv-x="709" d="M92 472Q61 472 61 490Q61 498 64 509T73 526Q80 534 99 534H148L163 604Q168 628 179 639T214 651H228Q251 651 261 638T265 601L251 534H582L597 604Q602 628 613 639T648 651H663Q685 651 695 638T700 600L686 534H728Q759 534 759 516Q759 508 755 497T746 480Q739 472 720 472H672L571 0H469L528 279H198L137 0H34L135 472H92ZM547 369L569 472H238L217 369H547Z" />
+<glyph unicode="&#x127;" glyph-name="hbar" horiz-adv-x="591" d="M102 563Q71 563 71 581Q71 589 75 601T84 619Q91 627 110 627H156L169 690Q170 695 183 693T214 690H226Q246 690 257 692T268 690L255 627H349Q380 627 380 609Q380 601 377 589T368 571Q361 563 342 563H242L210 401Q236 444 278 468T370 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H68Q48 0 37 -2T26 0L142 563H102Z" />
+<glyph unicode="&#x128;" glyph-name="Itilde" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM325 671Q298 671 262 690T190 746Q178 720 163 699T131 677Q115 677 107 686T99 706Q99 725 107 743T127 776T156 799T189 808Q216 808 252 789T324 733Q336 759 351 780T383 802Q399 802 407 793T415 773Q415 753 407 735T387 703T358 680T325 671Z" />
+<glyph unicode="&#x129;" glyph-name="itilde" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM281 546Q254 546 218 565T146 621Q134 595 119 574T87 552Q71 552 63 561T55 581Q55 600 63 618T83 651T112 674T145 683Q172 683 208 664T280 608Q292 634 307 655T339 677Q355 677 363 668T371 648Q371 628 363 610T343 578T314 555T281 546Z" />
+<glyph unicode="&#x12A;" glyph-name="Imacron" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM142 721Q102 721 102 747Q102 756 105 770T117 793Q127 803 153 803H377Q418 803 418 777Q418 769 414 755T402 732Q392 721 366 721H142Z" />
+<glyph unicode="&#x12B;" glyph-name="imacron" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM87 562Q47 562 47 588Q47 597 50 611T62 634Q72 644 98 644H322Q363 644 363 618Q363 610 359 596T347 573Q337 562 311 562H87Z" />
+<glyph unicode="&#x12E;" glyph-name="Iogonek" horiz-adv-x="293" d="M147 0H128Q70 -33 51 -58T31 -104Q31 -135 62 -135Q83 -135 97 -118Q108 -120 116 -128T124 -151Q124 -174 104 -189T47 -204Q5 -204 -22 -182T-49 -122Q-49 -87 -22 -55T60 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0Z" />
+<glyph unicode="&#x12F;" glyph-name="iogonek" horiz-adv-x="267" d="M197 38Q197 38 197 33T193 22T184 8T165 -4Q109 -36 90 -61T71 -106Q71 -137 102 -137Q123 -137 137 -120Q148 -122 156 -130T164 -153Q164 -176 144 -191T87 -206Q45 -206 18 -184T-9 -124Q-9 -90 17 -58T96 -3Q64 8 55 37T55 111L134 481H232L154 110Q146 72 154 56T197 38ZM216 584Q187 584 172 596T157 630Q157 656 173 675T219 694Q248 694 263 682T278 649Q278 622 262 603T216 584Z" />
+<glyph unicode="&#x130;" glyph-name="Idotaccent" horiz-adv-x="293" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM254 706Q222 706 206 720T190 757Q190 787 208 809T258 831Q290 831 305 817T321 780Q321 750 304 728T254 706Z" />
+<glyph unicode="&#x131;" glyph-name="dotlessi" horiz-adv-x="264" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9Z" />
+<glyph unicode="&#x132;" glyph-name="IJ" horiz-adv-x="788" d="M147 0H44L173 604Q178 628 189 639T224 651H238Q261 651 271 638T275 601L147 0ZM478 -12Q429 -12 395 2T339 38Q325 52 319 64T313 89Q313 107 320 119T337 138T355 147T364 148Q380 116 409 96T476 76Q529 76 558 108T603 214L696 647H798L703 198Q680 92 627 40T478 -12Z" />
+<glyph unicode="&#x133;" glyph-name="ij" horiz-adv-x="535" d="M135 -9Q80 -9 62 23T55 111L134 481H232L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM216 584Q187 584 172 596T157 630Q157 656 173 675T219 694Q248 694 263 682T278 649Q278 622 262 603T216 584ZM237 -202Q203 -202 181 -194T146 -173Q130 -157 130 -138Q130 -127 134 -118T143 -102T153 -91T158 -88Q168 -102 186 -111T226 -121Q252 -121 268 -105T293 -46L405 481H503L389 -60Q373 -135 335 -168T237 -202ZM487 584Q458 584 444 596T429 630Q429 656 445 675T491 694Q520 694 534 682T549 649Q549 622 533 603T487 584Z" />
+<glyph unicode="&#x134;" glyph-name="Jcircumflex" horiz-adv-x="495" d="M185 -12Q136 -12 102 2T46 38Q32 52 26 64T20 89Q20 107 27 119T44 138T62 147T71 148Q87 116 116 96T183 76Q236 76 265 108T310 214L403 647H505L410 198Q387 92 334 40T185 -12ZM357 706Q343 706 336 714T328 734Q328 753 345 774T394 822Q410 835 426 846T457 867T481 881T497 887Q504 885 525 867T566 824Q588 798 597 779T607 745Q607 727 597 717T572 706Q556 706 542 720T510 766Q505 776 499 787T485 812Q474 799 464 788T442 765Q417 737 396 722T357 706Z" />
+<glyph unicode="&#x135;" glyph-name="jcircumflex" horiz-adv-x="268" d="M-30 -202Q-64 -202 -86 -194T-121 -173Q-137 -157 -137 -138Q-137 -127 -133 -118T-124 -102T-114 -91T-109 -88Q-99 -102 -81 -111T-41 -121Q-15 -121 1 -105T26 -46L138 481H236L122 -60Q106 -135 68 -168T-30 -202ZM99 547Q85 547 78 555T70 575Q70 594 87 615T136 663Q152 676 168 687T199 708T223 722T239 728Q246 726 267 708T308 665Q330 639 339 620T349 586Q349 568 339 558T314 547Q298 547 284 561T252 607Q247 617 241 628T227 653Q216 640 206 629T184 606Q159 578 138 563T99 547Z" />
+<glyph unicode="&#x136;" glyph-name="Kcommaaccent" horiz-adv-x="608" d="M478 -6Q460 -6 445 0T416 19T387 52T353 100L204 313L138 0H35L163 604Q168 629 179 640T214 651H228Q251 651 261 638T265 600L213 354L568 660Q568 660 577 658T596 650T614 635T623 610Q623 593 614 579T582 547L308 322L448 131Q464 109 476 95T499 72T520 59T545 54Q546 54 546 45T540 24T520 4T478 -6ZM163 -253Q158 -258 150 -257T133 -250T119 -238T117 -224L220 -43Q221 -42 234 -42T262 -47T290 -62T303 -90Q303 -106 297 -119T273 -149L163 -253Z" />
+<glyph unicode="&#x137;" glyph-name="kcommaaccent" horiz-adv-x="506" d="M380 -9Q362 -9 349 -5T322 12T294 45T259 101L175 239Q171 220 167 201Q159 166 150 119Q139 66 125 0H26L169 690H268L183 274L444 493Q444 494 452 492T469 485T486 469T494 443Q494 416 456 386L274 241L345 132Q360 108 371 94T392 72T415 61T444 57Q445 57 445 47T439 24T420 2T380 -9ZM116 -253Q111 -258 103 -257T86 -250T72 -238T70 -224L173 -43Q174 -42 187 -42T215 -47T243 -62T256 -90Q256 -106 250 -119T226 -149L116 -253Z" />
+<glyph unicode="&#x139;" glyph-name="Lacute" horiz-adv-x="546" d="M163 604Q168 628 179 639T214 651H228Q251 651 261 638T265 601L157 92H433Q478 92 478 63Q478 53 474 37T460 11Q455 5 446 3T420 0H34L163 604ZM200 714Q198 717 209 733T241 770T286 812T336 849Q360 864 385 864Q405 864 416 852T428 822Q428 806 416 792T382 764Q353 747 322 736T264 719T220 712T200 714Z" />
+<glyph unicode="&#x13A;" glyph-name="lacute" horiz-adv-x="267" d="M135 -9Q80 -9 62 23T55 111L176 690H274L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM194 727Q192 730 203 746T235 783T280 825T330 862Q354 877 379 877Q399 877 410 865T422 835Q422 819 410 805T376 777Q347 760 316 749T258 732T214 725T194 727Z" />
+<glyph unicode="&#x13B;" glyph-name="Lcommaaccent" horiz-adv-x="546" d="M146 604Q151 628 162 639T197 651H211Q234 651 244 638T248 601L140 92H416Q461 92 461 63Q461 53 457 37T443 11Q438 5 429 3T403 0H17L146 604ZM150 -253Q145 -258 137 -257T120 -250T106 -238T104 -224L207 -43Q208 -42 221 -42T249 -47T277 -62T290 -90Q290 -106 284 -119T260 -149L150 -253Z" />
+<glyph unicode="&#x13C;" glyph-name="lcommaaccent" horiz-adv-x="267" d="M135 -9Q80 -9 62 23T55 111L176 690H274L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM10 -253Q5 -258 -3 -257T-20 -250T-34 -238T-36 -224L67 -43Q68 -42 81 -42T109 -47T137 -62T150 -90Q150 -106 144 -119T120 -149L10 -253Z" />
+<glyph unicode="&#x13D;" glyph-name="Lcaron" horiz-adv-x="655" d="M535 407Q531 403 522 404T505 409T492 421T490 436L600 659Q601 661 614 661T644 657T673 643T686 615Q686 600 680 587T658 556L535 407ZM146 604Q151 628 162 639T197 651H211Q234 651 244 638T248 601L140 92H416Q461 92 461 63Q461 53 457 37T443 11Q438 5 429 3T403 0H17L146 604Z" />
+<glyph unicode="&#x13E;" glyph-name="lcaron" horiz-adv-x="445" d="M321 429Q317 425 308 426T291 431T278 443T276 458L386 681Q387 683 400 683T430 679T459 665T472 637Q472 622 466 609T444 578L321 429ZM135 -9Q80 -9 62 23T55 111L176 690H274L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9Z" />
+<glyph unicode="&#x13F;" glyph-name="Ldot" horiz-adv-x="666" d="M146 604Q151 628 162 639T197 651H211Q234 651 244 638T248 601L140 92H416Q461 92 461 63Q461 53 457 37T443 11Q438 5 429 3T403 0H17L146 604ZM579 265Q522 265 522 307Q522 326 527 342T543 370Q551 378 561 382T591 386Q648 386 648 344Q648 325 644 309T628 281Q620 273 609 269T579 265Z" />
+<glyph unicode="&#x140;" glyph-name="ldot" horiz-adv-x="455" d="M135 -9Q80 -9 62 23T55 111L176 690H274L154 110Q146 72 154 56T197 38Q198 38 197 31T190 15T171 -1T135 -9ZM347 200Q290 200 290 242Q290 261 295 277T311 305Q319 313 329 317T359 321Q416 321 416 279Q416 260 412 244T396 216Q388 208 377 204T347 200Z" />
+<glyph unicode="&#x141;" glyph-name="CR" horiz-adv-x="603" d="M143 0Q75 0 89 67L122 223L49 203Q47 201 40 214T32 246Q32 263 44 276T91 300L142 315L203 604Q209 628 220 639T255 651H269Q291 651 301 638T306 601L252 345L439 398Q442 399 449 387T456 354Q456 337 444 324T397 300L232 254L198 92H474Q519 92 519 63Q519 53 514 37T501 11Q496 5 487 3T461 0H143Z" />
+<glyph unicode="&#x142;" glyph-name="lslash" horiz-adv-x="378" d="M189 -9Q134 -9 116 23T109 111L136 233L47 209Q46 209 44 213T38 223T33 236T31 251Q31 268 42 282T86 304L155 324L222 640Q227 666 238 676T267 687H279Q299 687 312 677T321 640L259 349L354 375Q355 375 357 372T363 362T368 349T370 334Q370 317 359 303T315 280L240 259L208 110Q200 72 208 56T252 38Q252 38 251 31T244 15T225 -1T189 -9Z" />
+<glyph unicode="&#x143;" glyph-name="Nacute" horiz-adv-x="716" d="M160 592Q172 651 223 651H240Q260 651 272 642T295 610L516 155L621 647H717L590 51Q579 -4 531 -4H523Q503 -4 492 5T468 37L239 509L131 0H35L160 592ZM390 714Q388 717 399 733T431 770T476 812T526 849Q550 864 575 864Q595 864 606 852T618 822Q618 806 606 792T572 764Q543 747 512 736T454 719T410 712T390 714Z" />
+<glyph unicode="&#x144;" glyph-name="nacute" horiz-adv-x="591" d="M153 485Q165 485 176 482T196 469T208 443T208 403Q236 445 277 469T369 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L128 480Q130 485 153 485ZM275 555Q273 558 284 574T316 611T361 653T411 690Q435 705 460 705Q480 705 491 693T503 663Q503 647 491 633T457 605Q428 588 397 577T339 560T295 553T275 555Z" />
+<glyph unicode="&#x145;" glyph-name="Ncommaaccent" horiz-adv-x="716" d="M160 592Q172 651 223 651H240Q260 651 272 642T295 610L516 155L621 647H717L590 51Q579 -4 531 -4H523Q503 -4 492 5T468 37L239 509L131 0H35L160 592ZM216 -253Q211 -258 203 -257T186 -250T172 -238T170 -224L273 -43Q274 -42 287 -42T315 -47T343 -62T356 -90Q356 -106 350 -119T326 -149L216 -253Z" />
+<glyph unicode="&#x146;" glyph-name="ncommaaccent" horiz-adv-x="591" d="M153 485Q165 485 176 482T196 469T208 443T208 403Q236 445 277 469T369 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L128 480Q130 485 153 485ZM172 -253Q167 -258 159 -257T142 -250T128 -238T126 -224L229 -43Q230 -42 243 -42T271 -47T299 -62T312 -90Q312 -106 306 -119T282 -149L172 -253Z" />
+<glyph unicode="&#x147;" glyph-name="Ncaron" horiz-adv-x="716" d="M160 592Q172 651 223 651H240Q260 651 272 642T295 610L516 155L621 647H717L590 51Q579 -4 531 -4H523Q503 -4 492 5T468 37L239 509L131 0H35L160 592ZM446 691Q439 693 418 711T377 754Q355 780 346 808T336 849Q336 868 346 870T371 873Q400 873 433 812Q438 803 444 792T458 766L501 814Q526 841 547 857T586 873Q600 873 607 865T615 845Q615 825 598 804T549 756Q533 743 517 732T486 712T462 698T446 691Z" />
+<glyph unicode="&#x148;" glyph-name="ncaron" horiz-adv-x="591" d="M153 485Q165 485 176 482T196 469T208 443T208 403Q236 445 277 469T369 493Q408 493 438 481T489 448T521 398T532 334Q532 308 524 270T503 189Q492 152 486 124T479 81Q479 61 489 52T520 41Q521 41 520 33T513 16T494 -1T459 -9Q420 -9 400 12T380 69Q380 90 387 121T405 193Q416 229 422 260T429 309Q429 355 405 381T333 407Q286 407 253 384T191 311L125 0H26L128 480Q130 485 153 485ZM351 532Q344 534 323 552T282 595Q260 621 251 649T241 690Q241 709 251 711T276 714Q305 714 338 653Q343 644 349 633T363 607L406 655Q431 682 452 698T491 714Q505 714 512 706T520 686Q520 666 503 645T454 597Q438 584 422 573T391 553T367 539T351 532Z" />
+<glyph unicode="&#x149;" glyph-name="napostrophe" horiz-adv-x="841" d="M126 409Q122 405 113 406T96 411T83 423T81 438L191 661Q192 663 205 663T235 659T264 645T277 617Q277 602 271 589T249 558L126 409ZM403 485Q415 485 426 482T446 469T458 443T458 403Q486 445 527 469T619 493Q658 493 688 481T739 448T771 398T782 334Q782 308 774 270T753 189Q742 152 736 124T729 81Q729 61 739 52T770 41Q771 41 770 33T763 16T744 -1T709 -9Q670 -9 650 12T630 69Q630 90 637 121T655 193Q666 229 672 260T679 309Q679 355 655 381T583 407Q536 407 503 384T441 311L375 0H276L378 480Q380 485 403 485Z" />
+<glyph unicode="&#x14C;" glyph-name="Omacron" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM338 721Q298 721 298 747Q298 756 301 770T313 793Q323 803 349 803H573Q614 803 614 777Q614 769 610 755T598 732Q588 721 562 721H338Z" />
+<glyph unicode="&#x14D;" glyph-name="omacron" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM224 562Q184 562 184 588Q184 597 187 611T199 634Q209 644 235 644H459Q500 644 500 618Q500 610 496 596T484 573Q474 562 448 562H224Z" />
+<glyph unicode="&#x14E;" glyph-name="Obreve" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM463 705Q436 705 410 715T364 742T331 778T319 817Q319 831 328 842T354 854Q374 854 400 834T462 763Q505 811 535 832T588 854Q604 854 614 845T624 821Q624 804 610 784T574 747T522 717T463 705Z" />
+<glyph unicode="&#x14F;" glyph-name="obreve" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM335 546Q308 546 282 556T236 583T203 619T191 658Q191 672 200 683T226 695Q246 695 272 675T334 604Q377 652 407 673T460 695Q476 695 486 686T496 662Q496 645 482 625T446 588T394 558T335 546Z" />
+<glyph unicode="&#x150;" glyph-name="Ohungarumlaut" horiz-adv-x="765" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q503 660 557 638T649 578T707 488T727 377Q727 294 698 223T620 100T505 19T366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79ZM497 714Q495 717 504 732T531 769T571 812T618 851Q643 868 666 868Q684 868 697 857T711 829Q711 794 668 768Q641 750 612 739T558 720T516 712T497 714ZM300 714Q298 717 307 732T334 769T373 812T420 851Q446 868 468 868Q487 868 500 857T513 829Q513 812 503 797T471 768Q444 750 415 739T361 720T319 712T300 714Z" />
+<glyph unicode="&#x151;" glyph-name="ohungarumlaut" horiz-adv-x="588" d="M271 -12Q221 -12 180 4T109 50T63 121T46 210Q46 266 65 317T121 407T207 469T318 493Q367 493 408 477T480 431T526 360T543 271Q543 215 524 164T468 74T382 12T271 -12ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM373 555Q371 558 380 573T407 610T447 653T494 692Q519 709 542 709Q560 709 573 698T587 670Q587 635 544 609Q517 591 488 580T434 561T392 553T373 555ZM176 555Q174 558 183 573T210 610T249 653T296 692Q322 709 344 709Q363 709 376 698T389 670Q389 653 379 638T347 609Q320 591 291 580T237 561T195 553T176 555Z" />
+<glyph unicode="&#x152;" glyph-name="OE" horiz-adv-x="1057" d="M366 -11Q296 -11 242 11T150 71T93 160T73 271Q73 354 102 425T180 548T295 630T434 660Q459 660 482 657T527 647H1034Q1079 647 1079 618Q1079 608 1074 592T1061 567Q1055 560 1047 558T1021 556H666Q695 519 711 474T727 377V374H905Q947 374 947 347Q947 338 943 322T931 298Q925 293 917 290T893 287H716Q703 228 675 179T607 91H922Q967 91 967 63Q967 52 962 36T949 11Q938 0 909 0H448Q406 -11 366 -11ZM374 79Q420 79 464 100T544 160T600 252T621 370Q621 411 608 447T571 511T510 553T426 569Q379 569 335 548T255 488T199 396T178 278Q178 237 191 201T229 137T290 95T374 79Z" />
+<glyph unicode="&#x153;" glyph-name="oe" horiz-adv-x="958" d="M516 361Q554 422 615 457T755 493Q841 493 887 457T934 360Q934 324 921 295T876 246T790 215T655 204Q629 204 601 205T540 209V191Q540 134 573 99T673 64Q726 64 762 83T826 139Q827 140 834 139T849 133T865 120T872 98Q872 83 867 73T847 48Q818 19 775 4T671 -12Q599 -12 550 18T480 100Q445 50 391 19T268 -12Q219 -12 179 4T109 49T63 119T46 210Q46 266 65 317T120 407T204 469T314 493Q386 493 439 459T516 361ZM275 67Q313 67 345 83T401 127T438 191T452 267Q452 299 442 326T413 372T369 403T314 414Q276 414 244 398T188 354T151 290T137 214Q137 182 147 155T176 109T220 78T275 67ZM749 419Q716 419 687 408T632 379T588 334T556 278Q582 275 604 274T646 273Q704 273 742 280T803 298T835 325T844 357Q844 381 821 400T749 419Z" />
+<glyph unicode="&#x154;" glyph-name="Racute" horiz-adv-x="621" d="M613 479Q613 379 551 321T376 260L451 133Q463 113 472 99T492 76T516 63T546 58Q547 58 547 48T541 26T521 4T478 -6Q461 -6 447 -2T418 14T390 46T357 99L271 253H191L138 0H35L160 590Q169 647 227 647H386Q498 647 555 605T613 479ZM355 334Q429 334 468 369T507 468Q507 515 474 539T377 563H256L207 334H355ZM316 714Q314 717 325 733T357 770T402 812T452 849Q476 864 501 864Q521 864 532 852T544 822Q544 806 532 792T498 764Q469 747 438 736T380 719T336 712T316 714Z" />
+<glyph unicode="&#x155;" glyph-name="racute" horiz-adv-x="406" d="M153 485Q165 485 176 481T196 468T208 442T207 400Q231 445 263 469T339 493Q371 493 393 479T416 438Q416 420 410 407T395 387T381 376T373 374Q363 385 348 393T309 401Q282 401 260 386T220 344T189 279T166 196L125 0H26L128 480Q130 485 153 485ZM193 555Q191 558 202 574T234 611T279 653T329 690Q353 705 378 705Q398 705 409 693T421 663Q421 647 409 633T375 605Q346 588 315 577T257 560T213 553T193 555Z" />
+<glyph unicode="&#x156;" glyph-name="Rcommaaccent" horiz-adv-x="621" d="M613 479Q613 379 551 321T376 260L451 133Q463 113 472 99T492 76T516 63T546 58Q547 58 547 48T541 26T521 4T478 -6Q461 -6 447 -2T418 14T390 46T357 99L271 253H191L138 0H35L160 590Q169 647 227 647H386Q498 647 555 605T613 479ZM355 334Q429 334 468 369T507 468Q507 515 474 539T377 563H256L207 334H355ZM175 -253Q170 -258 162 -257T145 -250T131 -238T129 -224L232 -43Q233 -42 246 -42T274 -47T302 -62T315 -90Q315 -106 309 -119T285 -149L175 -253Z" />
+<glyph unicode="&#x157;" glyph-name="rcommaaccent" horiz-adv-x="406" d="M153 485Q165 485 176 481T196 468T208 442T207 400Q231 445 263 469T339 493Q371 493 393 479T416 438Q416 420 410 407T395 387T381 376T373 374Q363 385 348 393T309 401Q282 401 260 386T220 344T189 279T166 196L125 0H26L128 480Q130 485 153 485ZM-22 -253Q-27 -258 -35 -257T-52 -250T-66 -238T-68 -224L35 -43Q36 -42 49 -42T77 -47T105 -62T118 -90Q118 -106 112 -119T88 -149L-22 -253Z" />
+<glyph unicode="&#x158;" glyph-name="Rcaron" horiz-adv-x="621" d="M613 479Q613 379 551 321T376 260L451 133Q463 113 472 99T492 76T516 63T546 58Q547 58 547 48T541 26T521 4T478 -6Q461 -6 447 -2T418 14T390 46T357 99L271 253H191L138 0H35L160 590Q169 647 227 647H386Q498 647 555 605T613 479ZM355 334Q429 334 468 369T507 468Q507 515 474 539T377 563H256L207 334H355ZM384 691Q377 693 356 711T315 754Q293 780 284 808T274 849Q274 868 284 870T309 873Q338 873 371 812Q376 803 382 792T396 766L439 814Q464 841 485 857T524 873Q538 873 545 865T553 845Q553 825 536 804T487 756Q471 743 455 732T424 712T400 698T384 691Z" />
+<glyph unicode="&#x159;" glyph-name="rcaron" horiz-adv-x="406" d="M153 485Q165 485 176 481T196 468T208 442T207 400Q231 445 263 469T339 493Q371 493 393 479T416 438Q416 420 410 407T395 387T381 376T373 374Q363 385 348 393T309 401Q282 401 260 386T220 344T189 279T166 196L125 0H26L128 480Q130 485 153 485ZM265 532Q258 534 237 552T196 595Q174 621 165 649T155 690Q155 709 165 711T190 714Q219 714 252 653Q257 644 263 633T277 607L320 655Q345 682 366 698T405 714Q419 714 426 706T434 686Q434 666 417 645T368 597Q352 584 336 573T305 553T281 539T265 532Z" />
+<glyph unicode="&#x15A;" glyph-name="Sacute" horiz-adv-x="580" d="M272 -12Q199 -12 145 8T59 60Q42 78 32 96T22 135Q22 150 29 161T46 178T63 187T72 188Q98 136 149 104T274 72Q342 72 374 99T407 174Q407 196 397 211T369 239T328 262T277 284Q247 296 217 311T163 347T125 397T110 468Q110 507 125 542T170 603T243 644T343 659Q415 659 464 641T541 594Q573 562 573 526Q573 509 566 498T549 481T531 472T521 472Q498 519 453 547T340 575Q283 575 248 548T213 479Q213 455 223 439T251 409T294 385T349 361Q380 349 410 335T463 300T501 252T516 184Q516 141 501 105T455 43T378 3T272 -12ZM308 714Q306 717 317 733T349 770T394 812T444 849Q468 864 493 864Q513 864 524 852T536 822Q536 806 524 792T490 764Q461 747 430 736T372 719T328 712T308 714Z" />
+<glyph unicode="&#x15B;" glyph-name="sacute" horiz-adv-x="490" d="M228 -12Q165 -12 117 6T42 51Q31 62 24 75T17 101Q17 114 23 124T36 140T50 148T58 150Q82 114 124 89T224 63Q272 63 297 81T322 130Q322 157 296 171T224 202Q198 211 173 222T127 249T94 287T81 343Q81 376 94 403T131 451T190 482T268 493Q324 493 367 477T435 434Q460 410 460 384Q460 370 454 360T439 344T423 336T415 336Q395 371 355 394T267 418Q220 418 197 399T174 353Q174 324 202 309T278 275Q305 265 330 254T376 228T408 191T421 138Q421 70 371 29T228 -12ZM238 555Q236 558 247 574T279 611T324 653T374 690Q398 705 423 705Q443 705 454 693T466 663Q466 647 454 633T420 605Q391 588 360 577T302 560T258 553T238 555Z" />
+<glyph unicode="&#x15C;" glyph-name="Scircumflex" horiz-adv-x="580" d="M272 -12Q199 -12 145 8T59 60Q42 78 32 96T22 135Q22 150 29 161T46 178T63 187T72 188Q98 136 149 104T274 72Q342 72 374 99T407 174Q407 196 397 211T369 239T328 262T277 284Q247 296 217 311T163 347T125 397T110 468Q110 507 125 542T170 603T243 644T343 659Q415 659 464 641T541 594Q573 562 573 526Q573 509 566 498T549 481T531 472T521 472Q498 519 453 547T340 575Q283 575 248 548T213 479Q213 455 223 439T251 409T294 385T349 361Q380 349 410 335T463 300T501 252T516 184Q516 141 501 105T455 43T378 3T272 -12ZM271 706Q257 706 250 714T242 734Q242 753 259 774T308 822Q324 835 340 846T371 867T395 881T411 887Q418 885 439 867T480 824Q502 798 511 779T521 745Q521 727 511 717T486 706Q470 706 456 720T424 766Q419 776 413 787T399 812Q388 799 378 788T356 765Q331 737 310 722T271 706Z" />
+<glyph unicode="&#x15D;" glyph-name="scircumflex" horiz-adv-x="490" d="M228 -12Q165 -12 117 6T42 51Q31 62 24 75T17 101Q17 114 23 124T36 140T50 148T58 150Q82 114 124 89T224 63Q272 63 297 81T322 130Q322 157 296 171T224 202Q198 211 173 222T127 249T94 287T81 343Q81 376 94 403T131 451T190 482T268 493Q324 493 367 477T435 434Q460 410 460 384Q460 370 454 360T439 344T423 336T415 336Q395 371 355 394T267 418Q220 418 197 399T174 353Q174 324 202 309T278 275Q305 265 330 254T376 228T408 191T421 138Q421 70 371 29T228 -12ZM195 547Q181 547 174 555T166 575Q166 594 183 615T232 663Q248 676 264 687T295 708T319 722T335 728Q342 726 363 708T404 665Q426 639 435 620T445 586Q445 568 435 558T410 547Q394 547 380 561T348 607Q343 617 337 628T323 653Q312 640 302 629T280 606Q255 578 234 563T195 547Z" />
+<glyph unicode="&#x15E;" glyph-name="Scedilla" horiz-adv-x="580" d="M272 -12Q199 -12 145 8T59 60Q42 78 32 96T22 135Q22 150 29 161T46 178T63 187T72 188Q98 136 149 104T274 72Q342 72 374 99T407 174Q407 196 397 211T369 239T328 262T277 284Q247 296 217 311T163 347T125 397T110 468Q110 507 125 542T170 603T243 644T343 659Q415 659 464 641T541 594Q573 562 573 526Q573 509 566 498T549 481T531 472T521 472Q498 519 453 547T340 575Q283 575 248 548T213 479Q213 455 223 439T251 409T294 385T349 361Q380 349 410 335T463 300T501 252T516 184Q516 141 501 105T455 43T378 3T272 -12ZM152 -234Q147 -238 139 -236T124 -230T112 -219T111 -207L213 -45Q214 -44 226 -44T252 -49T277 -63T289 -88Q289 -103 282 -115T257 -142L152 -234Z" />
+<glyph unicode="&#x15F;" glyph-name="scedilla" horiz-adv-x="490" d="M228 -12Q165 -12 117 6T42 51Q31 62 24 75T17 101Q17 114 23 124T36 140T50 148T58 150Q82 114 124 89T224 63Q272 63 297 81T322 130Q322 157 296 171T224 202Q198 211 173 222T127 249T94 287T81 343Q81 376 94 403T131 451T190 482T268 493Q324 493 367 477T435 434Q460 410 460 384Q460 370 454 360T439 344T423 336T415 336Q395 371 355 394T267 418Q220 418 197 399T174 353Q174 324 202 309T278 275Q305 265 330 254T376 228T408 191T421 138Q421 70 371 29T228 -12ZM118 -234Q113 -238 105 -236T90 -230T78 -219T77 -207L179 -45Q180 -44 192 -44T218 -49T243 -63T255 -88Q255 -103 248 -115T223 -142L118 -234Z" />
+<glyph unicode="&#x160;" glyph-name="Scaron" horiz-adv-x="580" d="M272 -12Q199 -12 145 8T59 60Q42 78 32 96T22 135Q22 150 29 161T46 178T63 187T72 188Q98 136 149 104T274 72Q342 72 374 99T407 174Q407 196 397 211T369 239T328 262T277 284Q247 296 217 311T163 347T125 397T110 468Q110 507 125 542T170 603T243 644T343 659Q415 659 464 641T541 594Q573 562 573 526Q573 509 566 498T549 481T531 472T521 472Q498 519 453 547T340 575Q283 575 248 548T213 479Q213 455 223 439T251 409T294 385T349 361Q380 349 410 335T463 300T501 252T516 184Q516 141 501 105T455 43T378 3T272 -12ZM371 698Q364 700 343 718T302 761Q280 787 271 815T261 856Q261 875 271 877T296 880Q325 880 358 819Q363 810 369 799T383 773L426 821Q451 848 472 864T511 880Q525 880 532 872T540 852Q540 832 523 811T474 763Q458 750 442 739T411 719T387 705T371 698Z" />
+<glyph unicode="&#x161;" glyph-name="scaron" horiz-adv-x="490" d="M228 -12Q165 -12 117 6T42 51Q31 62 24 75T17 101Q17 114 23 124T36 140T50 148T58 150Q82 114 124 89T224 63Q272 63 297 81T322 130Q322 157 296 171T224 202Q198 211 173 222T127 249T94 287T81 343Q81 376 94 403T131 451T190 482T268 493Q324 493 367 477T435 434Q460 410 460 384Q460 370 454 360T439 344T423 336T415 336Q395 371 355 394T267 418Q220 418 197 399T174 353Q174 324 202 309T278 275Q305 265 330 254T376 228T408 191T421 138Q421 70 371 29T228 -12ZM300 532Q293 534 272 552T231 595Q209 621 200 649T190 690Q190 709 200 711T225 714Q254 714 287 653Q292 644 298 633T312 607L355 655Q380 682 401 698T440 714Q454 714 461 706T469 686Q469 666 452 645T403 597Q387 584 371 573T340 553T316 539T300 532Z" />
+<glyph unicode="&#x162;" glyph-name="Tcommaaccent" horiz-adv-x="627" d="M330 555H157Q112 555 112 584Q112 594 116 610T130 636Q135 642 144 644T170 647H624Q669 647 669 618Q669 608 664 592T651 566Q645 560 637 558T611 555H433L315 0H213L330 555ZM156 -253Q151 -258 143 -257T126 -250T112 -238T110 -224L213 -43Q214 -42 227 -42T255 -47T283 -62T296 -90Q296 -106 290 -119T266 -149L156 -253Z" />
+<glyph unicode="&#x163;" glyph-name="tcommaaccent" horiz-adv-x="400" d="M218 -13Q143 -13 109 32T93 166L142 400H64Q64 403 65 407Q66 410 66 414T69 426Q71 434 74 448T79 471Q79 473 79 475T80 479Q80 481 81 481H159L167 520Q176 565 193 583T243 602Q259 602 270 597T280 590L257 481H406Q405 478 404 474Q403 471 403 467T400 456L391 410Q390 407 390 406T389 402V400H239L189 168Q178 113 193 90T244 66Q264 66 281 75T307 94Q309 96 317 86T326 58Q326 35 307 16Q294 3 272 -5T218 -13ZM84 -253Q79 -258 71 -257T54 -250T40 -238T38 -224L141 -43Q142 -42 155 -42T183 -47T211 -62T224 -90Q224 -106 218 -119T194 -149L84 -253Z" />
+<glyph unicode="&#x164;" glyph-name="Tcaron" horiz-adv-x="627" d="M330 555H157Q112 555 112 584Q112 594 116 610T130 636Q135 642 144 644T170 647H624Q669 647 669 618Q669 608 664 592T651 566Q645 560 637 558T611 555H433L315 0H213L330 555ZM402 691Q395 693 374 711T333 754Q311 780 302 808T292 849Q292 868 302 870T327 873Q356 873 389 812Q394 803 400 792T414 766L457 814Q482 841 503 857T542 873Q556 873 563 865T571 845Q571 825 554 804T505 756Q489 743 473 732T442 712T418 698T402 691Z" />
+<glyph unicode="&#x165;" glyph-name="tcaron" horiz-adv-x="400" d="M218 -13Q143 -13 109 32T93 166L142 400H64Q64 403 65 407Q66 410 66 414T69 426Q71 434 74 448T79 471Q79 473 79 475T80 479Q80 481 81 481H159L167 520Q176 565 193 583T243 602Q259 602 270 597T280 590L257 481H406Q405 478 404 474Q403 471 403 467T400 456L391 410Q390 407 390 406T389 402V400H239L189 168Q178 113 193 90T244 66Q264 66 281 75T307 94Q309 96 317 86T326 58Q326 35 307 16Q294 3 272 -5T218 -13ZM353 520Q349 516 340 517T323 522T310 534T308 549L418 772Q419 774 432 774T462 770T491 756T504 728Q504 713 498 700T476 669L353 520Z" />
+<glyph unicode="&#x166;" glyph-name="Tbar" horiz-adv-x="627" d="M219 310Q179 310 179 336Q179 345 182 359T194 382Q204 392 230 392H295L330 555H157Q112 555 112 584Q112 594 116 610T130 636Q135 642 144 644T170 647H624Q669 647 669 618Q669 608 664 592T651 566Q645 560 637 558T611 555H433L398 392H454Q495 392 495 366Q495 358 491 344T479 321Q469 310 443 310H381L315 0H213L278 310H219Z" />
+<glyph unicode="&#x167;" glyph-name="tbar" horiz-adv-x="400" d="M61 213Q21 213 21 239Q21 248 24 262T36 285Q46 295 72 295H120L142 400H64Q64 403 65 407Q66 410 66 414T69 426Q71 434 74 448T79 471Q79 473 79 475T80 479Q80 481 81 481H159L167 520Q176 565 193 583T243 602Q259 602 270 597T280 590L257 481H406Q405 478 404 474Q403 471 403 467T400 456L391 410L389 405Q389 404 389 402V400H239L216 295H296Q337 295 337 269Q337 261 333 247T321 224Q311 213 285 213H198L189 168Q178 113 193 90T244 66Q264 66 281 75T307 94Q309 96 317 86T326 58Q326 35 307 16Q294 3 272 -5T218 -13Q143 -13 109 32T93 166L103 213H61Z" />
+<glyph unicode="&#x168;" glyph-name="Utilde" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM509 671Q482 671 446 690T374 746Q362 720 347 699T315 677Q299 677 291 686T283 706Q283 725 291 743T311 776T340 799T373 808Q400 808 436 789T508 733Q520 759 535 780T567 802Q583 802 591 793T599 773Q599 753 591 735T571 703T542 680T509 671Z" />
+<glyph unicode="&#x169;" glyph-name="utilde" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM439 546Q412 546 376 565T304 621Q292 595 277 574T245 552Q229 552 221 561T213 581Q213 600 221 618T241 651T270 674T303 683Q330 683 366 664T438 608Q450 634 465 655T497 677Q513 677 521 668T529 648Q529 628 521 610T501 578T472 555T439 546Z" />
+<glyph unicode="&#x16A;" glyph-name="Umacron" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM327 721Q287 721 287 747Q287 756 290 770T302 793Q312 803 338 803H562Q603 803 603 777Q603 769 599 755T587 732Q577 721 551 721H327Z" />
+<glyph unicode="&#x16B;" glyph-name="umacron" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM250 562Q210 562 210 588Q210 597 213 611T225 634Q235 644 261 644H485Q526 644 526 618Q526 610 522 596T510 573Q500 562 474 562H250Z" />
+<glyph unicode="&#x16C;" glyph-name="Ubreve" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM441 705Q414 705 388 715T342 742T309 778T297 817Q297 831 306 842T332 854Q352 854 378 834T440 763Q483 811 513 832T566 854Q582 854 592 845T602 821Q602 804 588 784T552 747T500 717T441 705Z" />
+<glyph unicode="&#x16D;" glyph-name="ubreve" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM354 546Q327 546 301 556T255 583T222 619T210 658Q210 672 219 683T245 695Q265 695 291 675T353 604Q396 652 426 673T479 695Q495 695 505 686T515 662Q515 645 501 625T465 588T413 558T354 546Z" />
+<glyph unicode="&#x16E;" glyph-name="Uring" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM441 707Q390 707 359 736T328 810Q328 837 338 861T365 904T407 934T461 946Q512 946 543 917T574 843Q574 816 564 792T537 748T495 718T441 707ZM443 760Q476 760 495 784T514 837Q514 860 499 876T458 893Q442 893 429 887T407 869T393 844T388 816Q388 793 403 777T443 760Z" />
+<glyph unicode="&#x16F;" glyph-name="uring" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM354 532Q303 532 272 561T241 635Q241 662 251 686T278 729T320 759T374 771Q425 771 456 742T487 668Q487 641 477 617T450 573T408 543T354 532ZM356 585Q389 585 408 609T427 662Q427 685 412 701T371 718Q355 718 342 712T320 694T306 669T301 641Q301 618 316 602T356 585Z" />
+<glyph unicode="&#x170;" glyph-name="Uhungarumlaut" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q552 -12 323 -12ZM478 714Q476 717 485 732T512 769T552 812T599 851Q624 868 647 868Q665 868 678 857T692 829Q692 794 649 768Q622 750 593 739T539 720T497 712T478 714ZM281 714Q279 717 288 732T315 769T354 812T401 851Q427 868 449 868Q468 868 481 857T494 829Q494 812 484 797T452 768Q425 750 396 739T342 720T300 712T281 714Z" />
+<glyph unicode="&#x171;" glyph-name="uhungarumlaut" horiz-adv-x="592" d="M463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9ZM382 555Q380 558 389 573T416 610T456 653T503 692Q528 709 551 709Q569 709 582 698T596 670Q596 635 553 609Q526 591 497 580T443 561T401 553T382 555ZM185 555Q183 558 192 573T219 610T258 653T305 692Q331 709 353 709Q372 709 385 698T398 670Q398 653 388 638T356 609Q329 591 300 580T246 561T204 553T185 555Z" />
+<glyph unicode="&#x172;" glyph-name="Uogonek" horiz-adv-x="682" d="M323 -12Q184 -12 120 64T88 291L154 604Q160 628 171 639T206 651H220Q243 651 252 638T257 600L190 283Q169 181 205 130T332 79Q469 79 506 254L581 604Q586 628 597 639T632 651H645Q667 651 677 638T682 601L606 241Q570 77 460 18Q432 2 414 -12T386 -38T371 -62T367 -83Q367 -114 398 -114Q419 -114 433 -97Q444 -99 452 -107T460 -130Q460 -153 440 -168T383 -183Q341 -183 314 -161T287 -101Q287 -78 299 -55T338 -12H323Z" />
+<glyph unicode="&#x173;" glyph-name="uogonek" horiz-adv-x="592" d="M504 2Q502 1 502 1L497 -2Q440 -35 421 -60T401 -105Q401 -136 432 -136Q453 -136 467 -119Q478 -121 486 -129T494 -152Q494 -175 474 -190T417 -205Q375 -205 348 -183T321 -123Q321 -89 346 -58T425 -3Q376 15 381 80Q353 36 312 12T219 -12Q172 -12 139 4T86 49T61 119T66 209L123 481H222L165 210Q151 150 174 112T254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 521 26T504 2Z" />
+<glyph unicode="&#x174;" glyph-name="Wcircumflex" horiz-adv-x="958" d="M148 627Q148 633 165 642T204 652Q230 652 243 636T255 592L260 113L472 527Q483 550 499 559T542 569Q576 569 590 555T607 516L640 113L877 650Q877 651 889 651T916 648T943 633T956 600Q956 589 952 576T937 540L683 0H565L527 453L287 0H171L148 627ZM462 698Q448 698 441 706T433 726Q433 745 450 766T499 814Q515 827 531 838T562 859T586 873T602 879Q609 877 630 859T671 816Q693 790 702 771T712 737Q712 719 702 709T677 698Q661 698 647 712T615 758Q610 768 604 779T590 804Q579 791 569 780T547 757Q522 729 501 714T462 698Z" />
+<glyph unicode="&#x175;" glyph-name="wcircumflex" horiz-adv-x="758" d="M123 330Q122 387 105 406T55 429Q53 429 54 438T61 458T82 478T121 487Q156 487 178 465Q213 430 212 328L209 104L346 381Q359 407 374 414T413 422Q442 422 457 410T475 368L497 103L674 486Q674 486 684 486T707 482T729 468T742 440Q744 429 740 417T725 385L530 0H430L397 306L235 0H127L123 330ZM332 539Q318 539 311 547T303 567Q303 586 320 607T369 655Q385 668 401 679T432 700T456 714T472 720Q479 718 500 700T541 657Q563 631 572 612T582 578Q582 560 572 550T547 539Q531 539 517 553T485 599Q480 609 474 620T460 645Q449 632 439 621T417 598Q392 570 371 555T332 539Z" />
+<glyph unicode="&#x176;" glyph-name="Ycircumflex" horiz-adv-x="605" d="M258 267L88 647H196L322 351L578 655Q578 655 588 654T609 647T630 632T640 603Q640 589 633 575T605 538L360 262L305 0H201L258 267ZM278 706Q264 706 257 714T249 734Q249 753 266 774T315 822Q331 835 347 846T378 867T402 881T418 887Q425 885 446 867T487 824Q509 798 518 779T528 745Q528 727 518 717T493 706Q477 706 463 720T431 766Q426 776 420 787T406 812Q395 799 385 788T363 765Q338 737 317 722T278 706Z" />
+<glyph unicode="&#x177;" glyph-name="ycircumflex" horiz-adv-x="527" d="M37 -203Q-21 -203 -46 -177Q-62 -162 -62 -140Q-62 -131 -58 -123T-49 -108T-40 -99T-34 -97Q-25 -108 -8 -114T21 -121Q35 -121 49 -116T80 -96T117 -57T165 7L179 28Q148 34 142 75L114 328Q108 381 90 403T40 426Q39 426 39 435T46 456T67 477T108 487Q150 487 174 450T207 327L227 99L447 481H546L251 -21Q195 -116 145 -159T37 -203ZM212 547Q198 547 191 555T183 575Q183 594 200 615T249 663Q265 676 281 687T312 708T336 722T352 728Q359 726 380 708T421 665Q443 639 452 620T462 586Q462 568 452 558T427 547Q411 547 397 561T365 607Q360 617 354 628T340 653Q329 640 319 629T297 606Q272 578 251 563T212 547Z" />
+<glyph unicode="&#x178;" glyph-name="Ydieresis" horiz-adv-x="605" d="M258 267L88 647H196L322 351L578 655Q578 655 588 654T609 647T630 632T640 603Q640 589 633 575T605 538L360 262L305 0H201L258 267ZM484 704Q455 704 440 716T425 752Q425 781 442 801T488 821Q517 821 532 809T547 773Q547 744 530 724T484 704ZM301 704Q272 704 257 716T242 752Q242 781 259 801T305 821Q334 821 348 809T363 773Q363 744 347 724T301 704Z" />
+<glyph unicode="&#x179;" glyph-name="Zacute" horiz-adv-x="605" d="M1 0Q3 8 5 19Q7 27 9 38T14 61Q19 83 28 98T60 136L487 557H181Q125 557 125 594Q125 603 128 612T134 629T141 642T146 647H627Q625 638 623 628Q621 619 619 608T614 586Q609 564 601 549T569 511L143 90H470Q526 90 526 53Q526 44 523 35T517 18T510 5T505 0H1ZM291 714Q289 717 300 733T332 770T377 812T427 849Q451 864 476 864Q496 864 507 852T519 822Q519 806 507 792T473 764Q444 747 413 736T355 719T311 712T291 714Z" />
+<glyph unicode="&#x17A;" glyph-name="zacute" horiz-adv-x="518" d="M10 0Q11 7 13 16Q15 23 16 32T20 50Q24 68 30 81T57 113L374 402H148Q98 402 98 435Q98 442 100 450T106 465T112 476T116 481H502Q500 473 498 465Q496 458 495 449T491 431Q487 413 481 400T454 369L138 79H375Q425 79 425 46Q425 38 423 30T417 16T411 5T407 0H10ZM222 555Q220 558 231 574T263 611T308 653T358 690Q382 705 407 705Q427 705 438 693T450 663Q450 647 438 633T404 605Q375 588 344 577T286 560T242 553T222 555Z" />
+<glyph unicode="&#x17B;" glyph-name="Zdotaccent" horiz-adv-x="605" d="M1 0Q3 8 5 19Q7 27 9 38T14 61Q19 83 28 98T60 136L487 557H181Q125 557 125 594Q125 603 128 612T134 629T141 642T146 647H627Q625 638 623 628Q621 619 619 608T614 586Q609 564 601 549T569 511L143 90H470Q526 90 526 53Q526 44 523 35T517 18T510 5T505 0H1ZM387 706Q355 706 339 720T323 757Q323 787 341 809T391 831Q423 831 438 817T454 780Q454 750 437 728T387 706Z" />
+<glyph unicode="&#x17C;" glyph-name="zdotaccent" horiz-adv-x="518" d="M10 0Q11 7 13 16Q15 23 16 32T20 50Q24 68 30 81T57 113L374 402H148Q98 402 98 435Q98 442 100 450T106 465T112 476T116 481H502Q500 473 498 465Q496 458 495 449T491 431Q487 413 481 400T454 369L138 79H375Q425 79 425 46Q425 38 423 30T417 16T411 5T407 0H10ZM307 547Q275 547 259 561T243 598Q243 628 261 650T311 672Q343 672 358 658T374 621Q374 591 357 569T307 547Z" />
+<glyph unicode="&#x17D;" glyph-name="Zcaron" horiz-adv-x="605" d="M1 0Q3 8 5 19Q7 27 9 38T14 61Q19 83 28 98T60 136L487 557H181Q125 557 125 594Q125 603 128 612T134 629T141 642T146 647H627Q625 638 623 628Q621 619 619 608T614 586Q609 564 601 549T569 511L143 90H470Q526 90 526 53Q526 44 523 35T517 18T510 5T505 0H1ZM379 698Q372 700 351 718T310 761Q288 787 279 815T269 856Q269 875 279 877T304 880Q333 880 366 819Q371 810 377 799T391 773L434 821Q459 848 480 864T519 880Q533 880 540 872T548 852Q548 832 531 811T482 763Q466 750 450 739T419 719T395 705T379 698Z" />
+<glyph unicode="&#x17E;" glyph-name="zcaron" horiz-adv-x="518" d="M10 0Q11 7 13 16Q15 23 16 32T20 50Q24 68 30 81T57 113L374 402H148Q98 402 98 435Q98 442 100 450T106 465T112 476T116 481H502Q500 473 498 465Q496 458 495 449T491 431Q487 413 481 400T454 369L138 79H375Q425 79 425 46Q425 38 423 30T417 16T411 5T407 0H10ZM304 532Q297 534 276 552T235 595Q213 621 204 649T194 690Q194 709 204 711T229 714Q258 714 291 653Q296 644 302 633T316 607L359 655Q384 682 405 698T444 714Q458 714 465 706T473 686Q473 666 456 645T407 597Q391 584 375 573T344 553T320 539T304 532Z" />
+<glyph unicode="&#x192;" glyph-name="florin" horiz-adv-x="394" d="M109 -8Q92 -8 80 -1T68 9L141 351H90Q51 351 51 376Q51 385 54 399T66 422Q71 427 79 429T101 432H158L172 493Q190 581 234 620T343 659Q377 659 399 651T435 628Q452 611 452 590Q452 579 448 570T437 554T426 544T420 542Q410 556 391 565T347 575Q316 575 296 555T265 484L253 432H357Q396 432 396 406Q396 398 393 384T381 361Q376 356 368 354T346 351H239L180 67Q171 23 153 8T109 -8Z" />
+<glyph unicode="&#x218;" glyph-name="Scommaaccent" horiz-adv-x="580" d="M272 -12Q199 -12 145 8T59 60Q42 78 32 96T22 135Q22 150 29 161T46 178T63 187T72 188Q98 136 149 104T274 72Q342 72 374 99T407 174Q407 196 397 211T369 239T328 262T277 284Q247 296 217 311T163 347T125 397T110 468Q110 507 125 542T170 603T243 644T343 659Q415 659 464 641T541 594Q573 562 573 526Q573 509 566 498T549 481T531 472T521 472Q498 519 453 547T340 575Q283 575 248 548T213 479Q213 455 223 439T251 409T294 385T349 361Q380 349 410 335T463 300T501 252T516 184Q516 141 501 105T455 43T378 3T272 -12ZM173 -253Q168 -258 160 -257T143 -250T129 -238T127 -224L230 -43Q231 -42 244 -42T272 -47T300 -62T313 -90Q313 -106 307 -119T283 -149L173 -253Z" />
+<glyph unicode="&#x219;" glyph-name="scommaaccent" horiz-adv-x="490" d="M228 -12Q165 -12 117 6T42 51Q31 62 24 75T17 101Q17 114 23 124T36 140T50 148T58 150Q82 114 124 89T224 63Q272 63 297 81T322 130Q322 157 296 171T224 202Q198 211 173 222T127 249T94 287T81 343Q81 376 94 403T131 451T190 482T268 493Q324 493 367 477T435 434Q460 410 460 384Q460 370 454 360T439 344T423 336T415 336Q395 371 355 394T267 418Q220 418 197 399T174 353Q174 324 202 309T278 275Q305 265 330 254T376 228T408 191T421 138Q421 70 371 29T228 -12ZM129 -253Q124 -258 116 -257T99 -250T85 -238T83 -224L186 -43Q187 -42 200 -42T228 -47T256 -62T269 -90Q269 -106 263 -119T239 -149L129 -253Z" />
+<glyph unicode="&#x21A;" glyph-name="uni021A" horiz-adv-x="627" d="M330 555H157Q112 555 112 584Q112 594 116 610T130 636Q135 642 144 644T170 647H624Q669 647 669 618Q669 608 664 592T651 566Q645 560 637 558T611 555H433L315 0H213L330 555ZM156 -253Q151 -258 143 -257T126 -250T112 -238T110 -224L213 -43Q214 -42 227 -42T255 -47T283 -62T296 -90Q296 -106 290 -119T266 -149L156 -253Z" />
+<glyph unicode="&#x21B;" glyph-name="uni021B" horiz-adv-x="400" d="M218 -13Q143 -13 109 32T93 166L142 400H64Q64 403 65 407Q66 410 66 414T69 426Q71 434 74 448T79 471Q79 473 79 475T80 479Q80 481 81 481H159L167 520Q176 565 193 583T243 602Q259 602 270 597T280 590L257 481H406Q405 478 404 474Q403 471 403 467T400 456L391 410Q390 407 390 406T389 402V400H239L189 168Q178 113 193 90T244 66Q264 66 281 75T307 94Q309 96 317 86T326 58Q326 35 307 16Q294 3 272 -5T218 -13ZM84 -253Q79 -258 71 -257T54 -250T40 -238T38 -224L141 -43Q142 -42 155 -42T183 -47T211 -62T224 -90Q224 -106 218 -119T194 -149L84 -253Z" />
+<glyph unicode="&#x2C6;" glyph-name="circumflex" horiz-adv-x="534" d="M229 547Q215 547 208 555T200 575Q200 594 217 615T266 663Q282 676 298 687T329 708T353 722T369 728Q376 726 397 708T438 665Q460 639 469 620T479 586Q479 568 469 558T444 547Q428 547 414 561T382 607Q377 617 371 628T357 653Q346 640 336 629T314 606Q289 578 268 563T229 547Z" />
+<glyph unicode="&#x2C7;" glyph-name="caron" horiz-adv-x="534" d="M332 532Q325 534 304 552T263 595Q241 621 232 649T222 690Q222 709 232 711T257 714Q286 714 319 653Q324 644 330 633T344 607L387 655Q412 682 433 698T472 714Q486 714 493 706T501 686Q501 666 484 645T435 597Q419 584 403 573T372 553T348 539T332 532Z" />
+<glyph unicode="&#x2C9;" glyph-name="overscore" horiz-adv-x="534" d="M227 562Q187 562 187 588Q187 597 190 611T202 634Q212 644 238 644H462Q503 644 503 618Q503 610 499 596T487 573Q477 562 451 562H227Z" />
+<glyph unicode="&#x2D8;" glyph-name="breve" horiz-adv-x="534" d="M346 546Q319 546 293 556T247 583T214 619T202 658Q202 672 211 683T237 695Q257 695 283 675T345 604Q388 652 418 673T471 695Q487 695 497 686T507 662Q507 645 493 625T457 588T405 558T346 546Z" />
+<glyph unicode="&#x2D9;" glyph-name="dotaccent" horiz-adv-x="534" d="M344 547Q312 547 296 561T280 598Q280 628 298 650T348 672Q380 672 395 658T411 621Q411 591 394 569T344 547Z" />
+<glyph unicode="&#x2DA;" glyph-name="ring" horiz-adv-x="534" d="M348 548Q297 548 266 577T235 651Q235 678 245 702T272 745T314 775T368 787Q419 787 450 758T481 684Q481 657 471 633T444 589T402 559T348 548ZM350 601Q383 601 402 625T421 678Q421 701 406 717T365 734Q349 734 336 728T314 710T300 685T295 657Q295 634 310 618T350 601Z" />
+<glyph unicode="&#x2DB;" glyph-name="ogonek" horiz-adv-x="534" d="M289 5Q258 -12 238 -27T207 -55T191 -80T186 -102Q186 -133 217 -133Q238 -133 252 -116Q263 -118 271 -126T279 -149Q279 -172 259 -187T202 -202Q160 -202 133 -180T106 -120Q106 -78 147 -39T271 20L289 5Z" />
+<glyph unicode="&#x2DC;" glyph-name="tilde" horiz-adv-x="534" d="M414 546Q387 546 351 565T279 621Q267 595 252 574T220 552Q204 552 196 561T188 581Q188 600 196 618T216 651T245 674T278 683Q305 683 341 664T413 608Q425 634 440 655T472 677Q488 677 496 668T504 648Q504 628 496 610T476 578T447 555T414 546Z" />
+<glyph unicode="&#x2DD;" glyph-name="hungarumlaut" horiz-adv-x="534" d="M366 555Q364 558 373 573T400 610T440 653T487 692Q512 709 535 709Q553 709 566 698T580 670Q580 635 537 609Q510 591 481 580T427 561T385 553T366 555ZM169 555Q167 558 176 573T203 610T242 653T289 692Q315 709 337 709Q356 709 369 698T382 670Q382 653 372 638T340 609Q313 591 284 580T230 561T188 553T169 555Z" />
+<glyph unicode="&#x394;" glyph-name="increment" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x3A9;" glyph-name="Omega" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x3BC;" glyph-name="mu" horiz-adv-x="592" d="M73 -263Q18 -263 0 -231T-7 -143L123 481H222L161 192Q154 144 174 112Q197 74 254 74Q302 74 335 98T397 170L463 481H561L483 110Q475 72 483 57T522 38Q523 38 522 31T516 15T498 -1T463 -9Q418 -9 399 14T381 80Q353 36 312 12T219 -12Q172 -12 138 4Q131 7 124 11L92 -144Q84 -182 92 -198T135 -216Q136 -216 135 -223T128 -239T109 -255T73 -263Z" />
+<glyph unicode="&#x3C0;" glyph-name="pi" horiz-adv-x="788" d="M411 -13Q338 -13 278 12T174 81T106 187T81 322Q81 395 106 457T175 564T279 634T412 660Q485 660 545 635T649 566T717 460T742 325Q742 252 717 190T648 83T544 13T411 -13ZM413 41Q473 41 523 61T608 119T664 208T684 323Q684 384 664 436T607 525T520 584T410 606Q350 606 300 586T215 528T159 439T139 324Q139 263 159 211T216 122T303 63T413 41ZM322 138Q291 138 291 172V445Q291 465 301 476T330 487H426Q495 487 532 457T569 370Q569 312 533 281T429 250H366V172Q366 138 333 138H322ZM429 309Q461 309 478 325T496 369Q496 397 478 412T425 428H365V309H429Z" />
+<glyph unicode="&#x2013;" glyph-name="endash" horiz-adv-x="572" d="M99 201Q57 201 57 228Q57 237 61 252T73 275Q79 280 87 283T110 286H476Q518 286 518 259Q518 250 514 235T501 211Q496 206 488 204T464 201H99Z" />
+<glyph unicode="&#x2014;" glyph-name="emdash" horiz-adv-x="739" d="M99 201Q57 201 57 228Q57 237 61 252T73 275Q79 280 87 283T110 286H642Q684 286 684 259Q684 250 680 235T668 211Q662 206 654 204T631 201H99Z" />
+<glyph unicode="&#x2018;" glyph-name="quoteleft" horiz-adv-x="250" d="M185 411Q184 409 171 409T141 413T112 427T99 455Q99 470 105 483T127 514L250 663Q254 667 262 666T279 661T293 649T295 634L185 411Z" />
+<glyph unicode="&#x2019;" glyph-name="quoteright" horiz-adv-x="250" d="M126 409Q122 405 113 406T96 411T83 423T81 438L191 661Q192 663 205 663T235 659T264 645T277 617Q277 602 271 589T249 558L126 409Z" />
+<glyph unicode="&#x201A;" glyph-name="quotesinglbase" horiz-adv-x="258" d="M5 -135Q1 -139 -8 -138T-25 -133T-38 -121T-40 -106L70 118Q71 119 84 119T114 115T143 101T156 73Q156 58 150 46T128 14L5 -135Z" />
+<glyph unicode="&#x201C;" glyph-name="quotedblleft" horiz-adv-x="425" d="M182 413Q181 411 168 411T140 415T112 429T99 457Q99 472 105 485T127 516L252 666Q256 670 264 669T281 664T295 652T296 637L182 413ZM359 410Q358 408 345 408T316 412T287 427T274 455Q274 469 279 482T301 513L423 662Q427 666 436 665T453 660T467 648T469 633L359 410Z" />
+<glyph unicode="&#x201D;" glyph-name="quotedblright" horiz-adv-x="425" d="M127 410Q123 406 114 407T97 412T83 424T81 439L191 662Q192 664 205 664T234 660T263 645T276 617Q276 603 271 590T249 559L127 410ZM298 406Q294 402 286 403T269 408T255 420T254 435L368 660Q369 661 382 661T410 657T438 643T451 615Q451 600 445 587T423 556L298 406Z" />
+<glyph unicode="&#x201E;" glyph-name="quotedblbase" horiz-adv-x="430" d="M4 -134Q0 -138 -9 -137T-26 -132T-40 -120T-42 -105L68 118Q69 120 82 120T111 116T140 101T153 73Q153 59 147 46T126 15L4 -134ZM175 -138Q171 -142 162 -141T145 -136T132 -124T131 -109L244 115Q245 117 258 117T287 113T315 99T328 71Q328 56 322 43T299 12L175 -138Z" />
+<glyph unicode="&#x2020;" glyph-name="dagger" horiz-adv-x="552" d="M193 -159Q172 -159 165 -152T160 -129L231 273Q235 296 246 322T280 371L122 345Q96 340 85 350T74 380Q74 393 77 407T86 431T104 446T132 449L290 423L301 605Q302 655 355 655Q388 655 402 642T404 598L341 422L498 448Q524 453 534 443T545 413Q545 399 543 386T533 362T515 346T488 344L329 370Q340 348 341 324T336 277L236 -130Q229 -159 204 -159H193Z" />
+<glyph unicode="&#x2021;" glyph-name="daggerdbl" horiz-adv-x="552" d="M193 -159Q172 -159 165 -152T160 -129L198 87L63 64Q37 59 26 69T15 99Q15 112 17 126T27 150T45 165T73 168L208 145L232 277Q236 300 247 326T281 375L123 349Q97 344 86 354T75 384Q75 397 78 411T87 435T105 450T133 453L290 428L301 605Q302 655 355 655Q388 655 402 642T404 598L343 426L498 452Q524 457 535 447T546 417Q546 403 544 390T534 366T516 350T488 348L330 374Q341 352 341 328T337 281L303 145L438 167Q464 172 475 162T486 132Q486 118 484 105T474 81T456 66T428 63L289 86L236 -130Q229 -159 204 -159H193Z" />
+<glyph unicode="&#x2022;" glyph-name="bullet" horiz-adv-x="368" d="M185 129Q161 129 140 138T104 162T80 198T71 243Q71 267 80 288T104 325T140 350T185 359Q209 359 230 350T266 325T290 288T299 243Q299 219 290 199T266 163T230 138T185 129Z" />
+<glyph unicode="&#x2026;" glyph-name="ellipsis" horiz-adv-x="701" d="M527 -7Q470 -7 470 35Q470 54 474 70T490 98Q498 106 509 110T539 114Q596 114 596 72Q596 53 591 37T575 9Q567 1 557 -3T527 -7ZM305 -7Q248 -7 248 35Q248 54 252 70T268 98Q276 106 287 110T317 114Q374 114 374 72Q374 53 369 37T353 9Q345 1 335 -3T305 -7ZM82 -7Q25 -7 25 35Q25 54 30 70T46 98Q54 106 64 110T94 114Q151 114 151 72Q151 53 147 37T131 9Q123 1 112 -3T82 -7Z" />
+<glyph unicode="&#x2030;" glyph-name="perthousand" horiz-adv-x="1121" d="M231 345Q169 345 130 382T90 482Q90 517 102 549T136 605T189 643T259 657Q322 657 361 620T401 519Q401 484 389 453T355 397T301 359T231 345ZM84 -10Q82 -12 76 -10T63 -3T51 9T45 27Q45 40 52 51T79 80L725 661Q726 662 732 661T746 654T759 642T765 624Q765 611 758 600T730 571L84 -10ZM897 -5Q835 -5 796 32T756 132Q756 167 768 199T802 255T855 293T925 307Q988 307 1027 270T1067 169Q1067 134 1055 103T1021 47T967 9T897 -5ZM551 -5Q488 -5 449 32T409 132Q409 167 421 199T455 255T509 293T579 307Q642 307 681 270T720 169Q720 134 708 103T674 47T621 9T551 -5ZM236 407Q256 407 273 415T303 439T323 474T330 516Q330 550 309 572T255 595Q235 595 218 587T187 563T167 528T160 486Q160 452 182 430T236 407ZM902 57Q922 57 939 65T969 89T989 124T996 166Q996 200 975 222T921 245Q901 245 884 237T853 213T833 178T826 136Q826 102 848 80T902 57ZM555 57Q575 57 592 65T622 89T642 124T649 166Q649 200 628 222T574 245Q554 245 537 237T507 213T487 178T480 136Q480 102 502 80T555 57Z" />
+<glyph unicode="&#x2039;" glyph-name="guilsinglleft" horiz-adv-x="349" d="M219 72Q196 72 171 93T120 147Q107 164 96 181T76 213T63 238T59 251Q60 254 68 263T90 287T122 317T160 349Q194 376 227 396T283 416Q300 416 309 407T318 383Q318 363 290 336T221 284Q203 273 183 261T141 238Q174 215 202 187Q233 158 246 141T260 107Q260 93 249 83T219 72Z" />
+<glyph unicode="&#x203A;" glyph-name="guilsinglright" horiz-adv-x="349" d="M68 72Q51 72 42 81T33 105Q33 125 61 152T131 204Q149 215 169 227T210 250Q193 262 178 274T149 301Q119 330 106 347T92 381Q92 395 103 405T133 416Q156 416 181 395T232 341Q245 324 256 307T275 276T288 251T293 237Q292 234 284 225T261 201T229 171T191 139Q158 112 125 92T68 72Z" />
+<glyph unicode="&#x2044;" glyph-name="uni2044" horiz-adv-x="685" d="M48 -8Q46 -10 39 -9T24 -4T10 8T4 26Q4 40 13 53T37 81L673 660Q675 662 682 661T697 655T711 643T717 626Q717 612 708 599T684 571L48 -8Z" />
+<glyph unicode="&#x20AC;" glyph-name="Euro" horiz-adv-x="662" d="M117 367Q80 367 80 390Q80 399 83 410T93 428Q103 438 125 438H185Q200 477 220 512T268 575Q311 618 360 639T473 660Q527 660 566 644T632 601Q649 584 657 563T666 522Q666 502 657 490T637 472T617 465T606 465Q589 522 553 548T463 574Q424 574 394 561T338 522Q321 505 309 484T285 438H474Q511 438 511 416Q511 406 508 395T497 377Q487 367 466 367H260Q254 344 250 323T244 284H441Q478 284 478 261Q478 251 474 240T464 222Q454 212 433 212H242Q250 152 286 113T388 73Q436 73 472 94T542 161Q543 162 550 160T565 151T579 136T586 114Q586 85 554 53Q526 26 481 7T384 -12Q272 -12 210 49T142 212H83Q47 212 47 235Q47 244 50 255T60 274Q72 284 92 284H145Q148 303 152 323T162 367H117Z" />
+<glyph unicode="&#x2113;" glyph-name="afii61289" horiz-adv-x="434" d="M199 341Q228 365 251 395T291 456T317 513T326 558Q326 584 315 584Q308 584 300 573Q291 561 278 539T250 486T222 419T199 341ZM44 138Q30 151 19 172T7 228Q25 237 33 240T49 246L59 251Q63 321 81 383T126 497T183 588T245 652Q285 683 327 683Q367 683 395 652T423 558Q423 509 402 463T349 375T276 294T196 223Q196 218 196 214T195 204Q195 158 207 133T237 107Q254 107 273 133T310 217Q322 220 336 220Q355 220 373 213T404 186Q397 149 382 114T345 52T293 9T228 -8Q206 -8 182 1T135 30T94 78T66 146L44 138Z" />
+<glyph unicode="&#x2122;" glyph-name="trademark" horiz-adv-x="721" d="M440 336Q413 336 413 368V617Q413 651 444 651H471Q486 651 492 646T504 628L564 481L624 628Q630 642 637 646T658 651H683Q713 651 713 617V368Q713 353 707 345T686 336H677Q650 336 650 368V557L595 416Q593 409 589 405T563 400Q543 400 539 404T533 416L476 556V368Q476 353 471 345T451 336H440ZM225 336Q198 336 198 368V588H125Q95 588 95 617V620Q95 648 125 648H336Q366 648 366 620V617Q366 588 336 588H264V368Q264 336 234 336H225Z" />
+<glyph unicode="&#x2126;" glyph-name="uni2126" horiz-adv-x="760" d="M380 602Q318 602 272 585T196 537T150 467T135 380Q135 335 149 294T188 216T249 148T325 93V0H51V93H188Q114 153 72 225T30 380Q30 446 54 504T124 605T235 674T380 700Q460 700 525 675T635 606T705 504T730 380Q730 297 688 225T572 93H709V0H435V93Q476 117 511 148T571 215T611 293T625 380Q625 426 610 466T565 537T488 584T380 602Z" />
+<glyph unicode="&#x212E;" glyph-name="estimated" horiz-adv-x="830" d="M415 671Q492 671 560 645T681 573T763 463T793 324H185Q175 324 175 313V116Q223 65 286 38T415 10Q490 10 555 43T667 131H724Q667 62 585 26T415 -11Q340 -11 272 14T151 84T67 191T36 330Q36 403 66 465T147 574T268 645T415 671ZM415 650Q345 650 282 622T175 543V358Q175 347 185 347H646Q655 347 655 358V542Q610 594 546 622T415 650Z" />
+<glyph unicode="&#x2202;" glyph-name="partialdiff" horiz-adv-x="538" d="M303 481Q335 481 361 471T404 445V464Q404 499 399 528T381 578T343 610T282 622Q247 622 208 612L189 679Q271 710 332 710Q421 710 464 649T508 475Q508 370 481 283T409 133T311 35T201 0Q165 0 134 10T80 44T43 102T30 187Q30 256 55 310T120 403T208 461T303 481ZM205 105Q229 105 256 121T310 167T359 243T393 349Q382 361 360 373T309 386Q275 386 244 371T188 331T149 270T134 194Q134 157 150 131T205 105Z" />
+<glyph unicode="&#x2206;" glyph-name="uni2206" horiz-adv-x="649" d="M293 700H396L633 0H30L293 700ZM140 73H465L310 528L140 73Z" />
+<glyph unicode="&#x220F;" glyph-name="product" horiz-adv-x="634" d="M484 0V607H150V0H50V700H584V0H484Z" />
+<glyph unicode="&#x2211;" glyph-name="summation" horiz-adv-x="475" d="M110 80H437V0H25V96L209 325L25 554V650H437V570H115L288 353V300L110 80Z" />
+<glyph unicode="&#x2212;" glyph-name="minus" horiz-adv-x="634" d="M131 221Q87 221 96 260L98 267Q103 286 114 296T149 306H511Q535 306 543 296T547 267L545 260Q540 241 529 231T493 221H131Z" />
+<glyph unicode="&#x2215;" glyph-name="fraction" horiz-adv-x="685" d="M48 -8Q46 -10 39 -9T24 -4T10 8T4 26Q4 40 13 53T37 81L673 660Q675 662 682 661T697 655T711 643T717 626Q717 612 708 599T684 571L48 -8Z" />
+<glyph unicode="&#x2219;" glyph-name="middot" horiz-adv-x="266" d="M128 184Q71 184 71 226Q71 245 76 261T92 289Q100 297 110 301T140 305Q197 305 197 263Q197 244 193 228T177 200Q169 192 158 188T128 184Z" />
+<glyph unicode="&#x221A;" glyph-name="radical" horiz-adv-x="649" d="M256 501L369 171L566 700H649L386 0H283L136 433H30V501H256Z" />
+<glyph unicode="&#x221E;" glyph-name="infinity" horiz-adv-x="733" d="M703 271Q703 231 690 203T654 156T604 129T547 120Q518 120 493 128T447 150T405 181T366 217Q347 199 328 182T287 151T240 129T186 120Q157 120 129 129T79 156T44 203T30 271Q30 310 43 338T79 385T129 413T186 422Q215 422 240 414T286 392T327 361T366 325Q385 343 405 360T446 391T493 413T547 422Q576 422 604 414T654 387T689 340T703 271ZM187 222Q223 222 252 235T308 271Q282 292 253 306T187 320Q167 320 149 309T130 271Q130 244 148 233T187 222ZM546 320Q510 320 481 306T425 271Q451 249 480 236T546 222Q566 222 584 233T603 271Q603 298 585 309T546 320Z" />
+<glyph unicode="&#x222B;" glyph-name="integral'" horiz-adv-x="396" d="M164 596Q172 654 201 685Q228 714 271 723T375 724V648Q314 653 288 640T255 584L168 -32Q159 -95 128 -126Q105 -149 70 -155T-15 -155V-81Q22 -84 45 -75T74 -25L164 596Z" />
+<glyph unicode="&#x2248;" glyph-name="approxequal" horiz-adv-x="534" d="M329 106Q302 106 266 125T194 181Q182 155 167 134T135 112Q119 112 111 121T103 141Q103 160 111 178T131 211T160 234T193 243Q220 243 256 224T328 168Q340 194 355 215T387 237Q403 237 411 228T419 208Q419 188 411 170T391 138T362 115T329 106ZM355 249Q328 249 292 268T220 324Q208 298 193 277T161 255Q145 255 137 264T129 284Q129 303 137 321T157 354T186 377T219 386Q246 386 282 367T354 311Q366 337 381 358T413 380Q429 380 437 371T445 351Q445 331 437 313T417 281T388 258T355 249Z" />
+<glyph unicode="&#x2260;" glyph-name="notequal" horiz-adv-x="634" d="M157 337Q140 337 131 343T121 361Q121 371 125 386T137 408Q147 418 172 418H381L472 556Q487 581 500 586T526 585L534 581Q546 573 548 562T534 524L464 418H529Q570 418 570 394Q570 385 566 370T553 347Q544 337 519 337H405L302 184H480Q520 184 520 159Q520 150 516 136T504 113Q494 103 469 103H255L166 -35Q151 -61 138 -66T113 -64L104 -59Q92 -52 90 -40T104 -3L172 103H108Q71 103 71 127Q71 137 75 151T88 174Q97 184 122 184H231L334 337H157Z" />
+<glyph unicode="&#x2264;" glyph-name="lessequal" horiz-adv-x="634" d="M480 333Q510 318 519 302T528 270Q527 260 523 252T513 237T503 229T496 227L168 379Q154 387 146 398T138 427Q138 447 148 463T185 491L578 631Q580 632 584 629T592 619T601 604T605 585Q606 571 595 556T551 527L249 430L480 333ZM112 95Q71 95 71 121Q71 131 75 145T87 166Q98 177 122 177H475Q516 177 516 151Q516 141 512 127T500 106Q489 95 465 95H112Z" />
+<glyph unicode="&#x2265;" glyph-name="greaterequal" horiz-adv-x="634" d="M103 273Q103 288 113 303T158 331L461 430L228 527Q212 533 203 540T188 556T182 572T181 588Q183 609 195 621T213 631L540 480Q557 473 563 461T570 432Q570 412 560 396T524 368L130 229Q128 228 124 231T116 240T107 254T103 273ZM112 95Q71 95 71 121Q71 131 75 145T87 166Q98 177 122 177H475Q516 177 516 151Q516 141 512 127T500 106Q489 95 465 95H112Z" />
+<glyph unicode="&#x25CA;" glyph-name="lozenge" horiz-adv-x="520" d="M30 388L226 776H294L490 388L294 0H226L30 388ZM370 388L260 616L150 388L260 159L370 388Z" />
+<glyph unicode="&#xFB01;" glyph-name="fi" horiz-adv-x="619" d="M157 400H58Q58 403 59 407Q60 410 60 414T63 426Q65 434 68 448T73 471Q73 473 73 475T74 479Q74 481 75 481H174L185 528Q203 616 247 655T356 694Q420 694 448 664Q464 648 464 627Q464 616 460 607T450 592T439 583T433 581Q423 595 404 604T361 614Q330 614 310 593T278 522L269 481H398Q397 478 396 474Q395 471 395 467T392 456L383 410Q382 407 382 406T381 402V400H255L171 0H72L157 400ZM488 -9Q433 -9 415 23T408 111L487 481H585L507 110Q499 72 507 56T550 38Q551 38 550 31T543 15T524 -1T488 -9ZM569 584Q540 584 525 596T510 630Q510 656 526 675T572 694Q601 694 616 682T631 649Q631 622 615 603T569 584Z" />
+<glyph unicode="&#xFB02;" glyph-name="fl" horiz-adv-x="619" d="M157 400H58Q58 403 59 407Q60 410 60 414T63 426Q65 434 68 448T73 471Q73 473 73 475T74 479Q74 481 75 481H174L185 528Q203 616 247 655T356 694Q420 694 448 664Q464 648 464 627Q464 616 460 607T450 592T439 583T433 581Q423 595 404 604T361 614Q330 614 310 593T278 522L269 481H398Q397 478 396 474Q395 471 395 467T392 456L383 410Q382 407 382 406T381 402V400H255L171 0H72L157 400ZM487 -9Q432 -9 414 23T407 111L529 683H627L506 110Q498 72 506 56T549 38Q550 38 549 31T542 15T523 -1T487 -9Z" />
+
+<hkern u1="&#x141;" u2="&#xdd;" k="64" />
+<hkern u1="&#x141;" u2="@" k="40" />
+<hkern u1="&#x141;" u2="A" k="-16" />
+<hkern u1="&#x141;" u2="C" k="40" />
+<hkern u1="&#x141;" u2="G" k="40" />
+<hkern u1="&#x141;" u2="O" k="40" />
+<hkern u1="&#x141;" u2="Q" k="40" />
+<hkern u1="&#x141;" u2="T" k="96" />
+<hkern u1="&#x141;" u2="U" k="12" />
+<hkern u1="&#x141;" u2="V" k="72" />
+<hkern u1="&#x141;" u2="W" k="56" />
+<hkern u1="&#x141;" u2="Y" k="64" />
+<hkern u1="&#x141;" u2="\" k="40" />
+<hkern u1="&#x141;" u2="a" k="16" />
+<hkern u1="&#x141;" u2="c" k="16" />
+<hkern u1="&#x141;" u2="d" k="16" />
+<hkern u1="&#x141;" u2="e" k="16" />
+<hkern u1="&#x141;" u2="o" k="16" />
+<hkern u1="&#x141;" u2="q" k="16" />
+<hkern u1="&#x141;" u2="v" k="32" />
+<hkern u1="&#x141;" u2="w" k="8" />
+<hkern u1="&#x141;" u2="y" k="32" />
+<hkern u1="&#x141;" u2="&#xc4;" k="-16" />
+<hkern u1="&#x141;" u2="&#xc5;" k="-16" />
+<hkern u1="&#x141;" u2="&#xd6;" k="40" />
+<hkern u1="&#x141;" u2="&#xdc;" k="12" />
+<hkern u1="&#x141;" u2="&#xe7;" k="16" />
+<hkern u1="&#x141;" u2="&#xae;" k="40" />
+<hkern u1="&#x141;" u2="&#xa9;" k="40" />
+<hkern u1="&#x141;" u2="&#x2122;" k="56" />
+<hkern u1="&#x141;" u2="&#xc6;" k="-16" />
+<hkern u1="&#x141;" u2="&#xd8;" k="40" />
+<hkern u1="&#x141;" u2="&#x3c0;" k="40" />
+<hkern u1="&#x141;" u2="&#xe6;" k="16" />
+<hkern u1="&#x141;" u2="&#xc0;" k="-16" />
+<hkern u1="&#x141;" u2="&#xc3;" k="-16" />
+<hkern u1="&#x141;" u2="&#xd5;" k="40" />
+<hkern u1="&#x141;" u2="&#x152;" k="40" />
+<hkern u1="&#x141;" u2="&#x153;" k="16" />
+<hkern u1="&#x141;" u2="&#x201c;" k="80" />
+<hkern u1="&#x141;" u2="&#x201d;" k="48" />
+<hkern u1="&#x141;" u2="&#x2018;" k="80" />
+<hkern u1="&#x141;" u2="&#x2019;" k="48" />
+<hkern u1="&#x141;" u2="&#x178;" k="64" />
+<hkern u1="&#x141;" u2="&#xc2;" k="-16" />
+<hkern u1="&#x141;" u2="&#xc1;" k="-16" />
+<hkern u1="&#x141;" u2="&#xd3;" k="40" />
+<hkern u1="&#x141;" u2="&#xd4;" k="40" />
+<hkern u1="&#x141;" u2="&#xd2;" k="40" />
+<hkern u1="&#x141;" u2="&#xda;" k="12" />
+<hkern u1="&#x141;" u2="&#xdb;" k="12" />
+<hkern u1="&#x141;" u2="&#xd9;" k="12" />
+<hkern u1="&#x160;" u2="&#xdd;" k="8" />
+<hkern u1="&#x160;" u2="A" k="-4" />
+<hkern u1="&#x160;" u2="V" k="8" />
+<hkern u1="&#x160;" u2="W" k="16" />
+<hkern u1="&#x160;" u2="X" k="-4" />
+<hkern u1="&#x160;" u2="Y" k="8" />
+<hkern u1="&#x160;" u2="&#xc4;" k="-4" />
+<hkern u1="&#x160;" u2="&#xc5;" k="-4" />
+<hkern u1="&#x160;" u2="&#xc6;" k="-4" />
+<hkern u1="&#x160;" u2="&#xc0;" k="-4" />
+<hkern u1="&#x160;" u2="&#xc3;" k="-4" />
+<hkern u1="&#x160;" u2="&#x178;" k="8" />
+<hkern u1="&#x160;" u2="&#xc2;" k="-4" />
+<hkern u1="&#x160;" u2="&#xc1;" k="-4" />
+<hkern u1="&#xdd;" u2="&#x160;" k="-24" />
+<hkern u1="&#xdd;" u2="&#x161;" k="40" />
+<hkern u1="&#xdd;" u2="&amp;" k="24" />
+<hkern u1="&#xdd;" u2=")" k="-40" />
+<hkern u1="&#xdd;" u2="&#x2c;" k="40" />
+<hkern u1="&#xdd;" u2="-" k="40" />
+<hkern u1="&#xdd;" u2="." k="40" />
+<hkern u1="&#xdd;" u2=":" k="24" />
+<hkern u1="&#xdd;" u2=";" k="24" />
+<hkern u1="&#xdd;" u2="@" k="24" />
+<hkern u1="&#xdd;" u2="A" k="24" />
+<hkern u1="&#xdd;" u2="C" k="4" />
+<hkern u1="&#xdd;" u2="G" k="4" />
+<hkern u1="&#xdd;" u2="J" k="56" />
+<hkern u1="&#xdd;" u2="O" k="4" />
+<hkern u1="&#xdd;" u2="Q" k="4" />
+<hkern u1="&#xdd;" u2="S" k="-24" />
+<hkern u1="&#xdd;" u2="T" k="-16" />
+<hkern u1="&#xdd;" u2="V" k="-24" />
+<hkern u1="&#xdd;" u2="X" k="-16" />
+<hkern u1="&#xdd;" u2="]" k="-40" />
+<hkern u1="&#xdd;" u2="a" k="44" />
+<hkern u1="&#xdd;" u2="c" k="44" />
+<hkern u1="&#xdd;" u2="d" k="44" />
+<hkern u1="&#xdd;" u2="e" k="44" />
+<hkern u1="&#xdd;" u2="f" k="8" />
+<hkern u1="&#xdd;" u2="g" k="32" />
+<hkern u1="&#xdd;" u2="m" k="32" />
+<hkern u1="&#xdd;" u2="n" k="32" />
+<hkern u1="&#xdd;" u2="o" k="44" />
+<hkern u1="&#xdd;" u2="p" k="32" />
+<hkern u1="&#xdd;" u2="q" k="44" />
+<hkern u1="&#xdd;" u2="r" k="32" />
+<hkern u1="&#xdd;" u2="s" k="40" />
+<hkern u1="&#xdd;" u2="t" k="16" />
+<hkern u1="&#xdd;" u2="u" k="32" />
+<hkern u1="&#xdd;" u2="v" k="16" />
+<hkern u1="&#xdd;" u2="w" k="8" />
+<hkern u1="&#xdd;" u2="x" k="16" />
+<hkern u1="&#xdd;" u2="y" k="16" />
+<hkern u1="&#xdd;" u2="z" k="24" />
+<hkern u1="&#xdd;" u2="}" k="-40" />
+<hkern u1="&#xdd;" u2="&#xc4;" k="24" />
+<hkern u1="&#xdd;" u2="&#xc5;" k="24" />
+<hkern u1="&#xdd;" u2="&#xd6;" k="4" />
+<hkern u1="&#xdd;" u2="&#xe7;" k="44" />
+<hkern u1="&#xdd;" u2="&#xae;" k="24" />
+<hkern u1="&#xdd;" u2="&#xa9;" k="24" />
+<hkern u1="&#xdd;" u2="&#xc6;" k="24" />
+<hkern u1="&#xdd;" u2="&#xd8;" k="4" />
+<hkern u1="&#xdd;" u2="&#x3c0;" k="24" />
+<hkern u1="&#xdd;" u2="&#xe6;" k="44" />
+<hkern u1="&#xdd;" u2="&#xab;" k="48" />
+<hkern u1="&#xdd;" u2="&#xbb;" k="32" />
+<hkern u1="&#xdd;" u2="&#x2026;" k="40" />
+<hkern u1="&#xdd;" u2="&#xc0;" k="24" />
+<hkern u1="&#xdd;" u2="&#xc3;" k="24" />
+<hkern u1="&#xdd;" u2="&#xd5;" k="4" />
+<hkern u1="&#xdd;" u2="&#x152;" k="4" />
+<hkern u1="&#xdd;" u2="&#x153;" k="44" />
+<hkern u1="&#xdd;" u2="&#x2013;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2014;" k="40" />
+<hkern u1="&#xdd;" u2="&#x2039;" k="48" />
+<hkern u1="&#xdd;" u2="&#x203a;" k="32" />
+<hkern u1="&#xdd;" u2="&#x201a;" k="40" />
+<hkern u1="&#xdd;" u2="&#x201e;" k="40" />
+<hkern u1="&#xdd;" u2="&#xc2;" k="24" />
+<hkern u1="&#xdd;" u2="&#xc1;" k="24" />
+<hkern u1="&#xdd;" u2="&#xd3;" k="4" />
+<hkern u1="&#xdd;" u2="&#xd4;" k="4" />
+<hkern u1="&#xdd;" u2="&#xd2;" k="4" />
+<hkern u1="&#xdd;" u2="&#x131;" k="32" />
+<hkern u1="&#xde;" u2="&#xdd;" k="36" />
+<hkern u1="&#xde;" u2="&#x17d;" k="36" />
+<hkern u1="&#xde;" u2="&#x2c;" k="32" />
+<hkern u1="&#xde;" u2="." k="32" />
+<hkern u1="&#xde;" u2="/" k="40" />
+<hkern u1="&#xde;" u2="?" k="16" />
+<hkern u1="&#xde;" u2="J" k="36" />
+<hkern u1="&#xde;" u2="T" k="40" />
+<hkern u1="&#xde;" u2="V" k="32" />
+<hkern u1="&#xde;" u2="W" k="48" />
+<hkern u1="&#xde;" u2="X" k="28" />
+<hkern u1="&#xde;" u2="Y" k="36" />
+<hkern u1="&#xde;" u2="Z" k="36" />
+<hkern u1="&#xde;" u2="a" k="4" />
+<hkern u1="&#xde;" u2="b" k="8" />
+<hkern u1="&#xde;" u2="c" k="4" />
+<hkern u1="&#xde;" u2="d" k="4" />
+<hkern u1="&#xde;" u2="e" k="4" />
+<hkern u1="&#xde;" u2="h" k="8" />
+<hkern u1="&#xde;" u2="k" k="8" />
+<hkern u1="&#xde;" u2="l" k="8" />
+<hkern u1="&#xde;" u2="m" k="4" />
+<hkern u1="&#xde;" u2="n" k="4" />
+<hkern u1="&#xde;" u2="o" k="4" />
+<hkern u1="&#xde;" u2="p" k="4" />
+<hkern u1="&#xde;" u2="q" k="4" />
+<hkern u1="&#xde;" u2="r" k="4" />
+<hkern u1="&#xde;" u2="u" k="4" />
+<hkern u1="&#xde;" u2="x" k="8" />
+<hkern u1="&#xde;" u2="z" k="8" />
+<hkern u1="&#xde;" u2="&#xe7;" k="4" />
+<hkern u1="&#xde;" u2="&#xe6;" k="4" />
+<hkern u1="&#xde;" u2="&#x2026;" k="32" />
+<hkern u1="&#xde;" u2="&#x153;" k="4" />
+<hkern u1="&#xde;" u2="&#x201c;" k="16" />
+<hkern u1="&#xde;" u2="&#x2018;" k="16" />
+<hkern u1="&#xde;" u2="&#x178;" k="36" />
+<hkern u1="&#xde;" u2="&#x201a;" k="32" />
+<hkern u1="&#xde;" u2="&#x201e;" k="32" />
+<hkern u1="&#xde;" u2="&#x131;" k="4" />
+<hkern u1="&#x17d;" u2="&#xf0;" k="24" />
+<hkern u1="&#x17d;" u2="-" k="40" />
+<hkern u1="&#x17d;" u2="A" k="8" />
+<hkern u1="&#x17d;" u2="C" k="36" />
+<hkern u1="&#x17d;" u2="G" k="36" />
+<hkern u1="&#x17d;" u2="J" k="8" />
+<hkern u1="&#x17d;" u2="O" k="36" />
+<hkern u1="&#x17d;" u2="Q" k="36" />
+<hkern u1="&#x17d;" u2="T" k="8" />
+<hkern u1="&#x17d;" u2="a" k="32" />
+<hkern u1="&#x17d;" u2="b" k="8" />
+<hkern u1="&#x17d;" u2="c" k="32" />
+<hkern u1="&#x17d;" u2="d" k="32" />
+<hkern u1="&#x17d;" u2="e" k="32" />
+<hkern u1="&#x17d;" u2="f" k="16" />
+<hkern u1="&#x17d;" u2="g" k="16" />
+<hkern u1="&#x17d;" u2="h" k="8" />
+<hkern u1="&#x17d;" u2="i" k="16" />
+<hkern u1="&#x17d;" u2="j" k="12" />
+<hkern u1="&#x17d;" u2="k" k="8" />
+<hkern u1="&#x17d;" u2="l" k="8" />
+<hkern u1="&#x17d;" u2="m" k="16" />
+<hkern u1="&#x17d;" u2="n" k="16" />
+<hkern u1="&#x17d;" u2="o" k="32" />
+<hkern u1="&#x17d;" u2="p" k="16" />
+<hkern u1="&#x17d;" u2="q" k="32" />
+<hkern u1="&#x17d;" u2="r" k="16" />
+<hkern u1="&#x17d;" u2="u" k="16" />
+<hkern u1="&#x17d;" u2="v" k="24" />
+<hkern u1="&#x17d;" u2="w" k="8" />
+<hkern u1="&#x17d;" u2="y" k="24" />
+<hkern u1="&#x17d;" u2="&#xc4;" k="8" />
+<hkern u1="&#x17d;" u2="&#xc5;" k="8" />
+<hkern u1="&#x17d;" u2="&#xd6;" k="36" />
+<hkern u1="&#x17d;" u2="&#xe7;" k="32" />
+<hkern u1="&#x17d;" u2="&#xc6;" k="8" />
+<hkern u1="&#x17d;" u2="&#xd8;" k="36" />
+<hkern u1="&#x17d;" u2="&#xe6;" k="32" />
+<hkern u1="&#x17d;" u2="&#xc0;" k="8" />
+<hkern u1="&#x17d;" u2="&#xc3;" k="8" />
+<hkern u1="&#x17d;" u2="&#xd5;" k="36" />
+<hkern u1="&#x17d;" u2="&#x152;" k="36" />
+<hkern u1="&#x17d;" u2="&#x153;" k="32" />
+<hkern u1="&#x17d;" u2="&#x2013;" k="40" />
+<hkern u1="&#x17d;" u2="&#x2014;" k="40" />
+<hkern u1="&#x17d;" u2="&#xc2;" k="8" />
+<hkern u1="&#x17d;" u2="&#xc1;" k="8" />
+<hkern u1="&#x17d;" u2="&#xd3;" k="36" />
+<hkern u1="&#x17d;" u2="&#xd4;" k="36" />
+<hkern u1="&#x17d;" u2="&#xd2;" k="36" />
+<hkern u1="&#x17d;" u2="&#x131;" k="16" />
+<hkern u1="&amp;" u2="&#xdd;" k="24" />
+<hkern u1="&amp;" u2="A" k="-32" />
+<hkern u1="&amp;" u2="J" k="-16" />
+<hkern u1="&amp;" u2="T" k="120" />
+<hkern u1="&amp;" u2="V" k="16" />
+<hkern u1="&amp;" u2="W" k="16" />
+<hkern u1="&amp;" u2="X" k="-16" />
+<hkern u1="&amp;" u2="Y" k="24" />
+<hkern u1="&amp;" u2="&#xc4;" k="-32" />
+<hkern u1="&amp;" u2="&#xc5;" k="-32" />
+<hkern u1="&amp;" u2="&#xc6;" k="-32" />
+<hkern u1="&amp;" u2="&#xc0;" k="-32" />
+<hkern u1="&amp;" u2="&#xc3;" k="-32" />
+<hkern u1="&amp;" u2="&#x178;" k="24" />
+<hkern u1="&amp;" u2="&#xc2;" k="-32" />
+<hkern u1="&amp;" u2="&#xc1;" k="-32" />
+<hkern u1="(" u2="&#xdd;" k="-40" />
+<hkern u1="(" u2="V" k="-24" />
+<hkern u1="(" u2="W" k="-8" />
+<hkern u1="(" u2="X" k="-32" />
+<hkern u1="(" u2="Y" k="-40" />
+<hkern u1="(" u2="g" k="-16" />
+<hkern u1="(" u2="j" k="-120" />
+<hkern u1="(" u2="&#x178;" k="-40" />
+<hkern u1="*" u2="A" k="48" />
+<hkern u1="*" u2="&#xc4;" k="48" />
+<hkern u1="*" u2="&#xc5;" k="48" />
+<hkern u1="*" u2="&#xc6;" k="48" />
+<hkern u1="*" u2="&#xc0;" k="48" />
+<hkern u1="*" u2="&#xc3;" k="48" />
+<hkern u1="*" u2="&#xc2;" k="48" />
+<hkern u1="*" u2="&#xc1;" k="48" />
+<hkern u1="&#x2c;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2c;" u2="0" k="32" />
+<hkern u1="&#x2c;" u2="1" k="48" />
+<hkern u1="&#x2c;" u2="4" k="40" />
+<hkern u1="&#x2c;" u2="6" k="24" />
+<hkern u1="&#x2c;" u2="8" k="12" />
+<hkern u1="&#x2c;" u2="9" k="8" />
+<hkern u1="&#x2c;" u2="A" k="-24" />
+<hkern u1="&#x2c;" u2="C" k="32" />
+<hkern u1="&#x2c;" u2="G" k="32" />
+<hkern u1="&#x2c;" u2="O" k="32" />
+<hkern u1="&#x2c;" u2="Q" k="32" />
+<hkern u1="&#x2c;" u2="T" k="48" />
+<hkern u1="&#x2c;" u2="U" k="8" />
+<hkern u1="&#x2c;" u2="V" k="48" />
+<hkern u1="&#x2c;" u2="W" k="16" />
+<hkern u1="&#x2c;" u2="Y" k="40" />
+<hkern u1="&#x2c;" u2="a" k="16" />
+<hkern u1="&#x2c;" u2="c" k="16" />
+<hkern u1="&#x2c;" u2="d" k="16" />
+<hkern u1="&#x2c;" u2="e" k="16" />
+<hkern u1="&#x2c;" u2="m" k="16" />
+<hkern u1="&#x2c;" u2="n" k="16" />
+<hkern u1="&#x2c;" u2="o" k="16" />
+<hkern u1="&#x2c;" u2="p" k="16" />
+<hkern u1="&#x2c;" u2="q" k="16" />
+<hkern u1="&#x2c;" u2="r" k="16" />
+<hkern u1="&#x2c;" u2="t" k="32" />
+<hkern u1="&#x2c;" u2="u" k="16" />
+<hkern u1="&#x2c;" u2="v" k="24" />
+<hkern u1="&#x2c;" u2="w" k="8" />
+<hkern u1="&#x2c;" u2="y" k="24" />
+<hkern u1="&#x2c;" u2="&#xc4;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xc5;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xd6;" k="32" />
+<hkern u1="&#x2c;" u2="&#xdc;" k="8" />
+<hkern u1="&#x2c;" u2="&#xe7;" k="16" />
+<hkern u1="&#x2c;" u2="&#xc6;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xd8;" k="32" />
+<hkern u1="&#x2c;" u2="&#xe6;" k="16" />
+<hkern u1="&#x2c;" u2="&#xc0;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xc3;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xd5;" k="32" />
+<hkern u1="&#x2c;" u2="&#x152;" k="32" />
+<hkern u1="&#x2c;" u2="&#x153;" k="16" />
+<hkern u1="&#x2c;" u2="&#x178;" k="40" />
+<hkern u1="&#x2c;" u2="&#xc2;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xc1;" k="-24" />
+<hkern u1="&#x2c;" u2="&#xd3;" k="32" />
+<hkern u1="&#x2c;" u2="&#xd4;" k="32" />
+<hkern u1="&#x2c;" u2="&#xd2;" k="32" />
+<hkern u1="&#x2c;" u2="&#xda;" k="8" />
+<hkern u1="&#x2c;" u2="&#xdb;" k="8" />
+<hkern u1="&#x2c;" u2="&#xd9;" k="8" />
+<hkern u1="&#x2c;" u2="&#x131;" k="16" />
+<hkern u1="-" u2="&#xdd;" k="40" />
+<hkern u1="-" u2="&#x17d;" k="8" />
+<hkern u1="-" u2="1" k="16" />
+<hkern u1="-" u2="7" k="24" />
+<hkern u1="-" u2="A" k="-8" />
+<hkern u1="-" u2="T" k="56" />
+<hkern u1="-" u2="V" k="40" />
+<hkern u1="-" u2="W" k="32" />
+<hkern u1="-" u2="X" k="24" />
+<hkern u1="-" u2="Y" k="40" />
+<hkern u1="-" u2="Z" k="8" />
+<hkern u1="-" u2="a" k="8" />
+<hkern u1="-" u2="c" k="8" />
+<hkern u1="-" u2="d" k="8" />
+<hkern u1="-" u2="e" k="8" />
+<hkern u1="-" u2="o" k="8" />
+<hkern u1="-" u2="q" k="8" />
+<hkern u1="-" u2="v" k="8" />
+<hkern u1="-" u2="x" k="24" />
+<hkern u1="-" u2="y" k="8" />
+<hkern u1="-" u2="z" k="24" />
+<hkern u1="-" u2="&#xc4;" k="-8" />
+<hkern u1="-" u2="&#xc5;" k="-8" />
+<hkern u1="-" u2="&#xe7;" k="8" />
+<hkern u1="-" u2="&#xc6;" k="-8" />
+<hkern u1="-" u2="&#xe6;" k="8" />
+<hkern u1="-" u2="&#xc0;" k="-8" />
+<hkern u1="-" u2="&#xc3;" k="-8" />
+<hkern u1="-" u2="&#x153;" k="8" />
+<hkern u1="-" u2="&#x178;" k="40" />
+<hkern u1="-" u2="&#xc2;" k="-8" />
+<hkern u1="-" u2="&#xc1;" k="-8" />
+<hkern u1="." u2="&#xdd;" k="40" />
+<hkern u1="." u2="0" k="32" />
+<hkern u1="." u2="1" k="48" />
+<hkern u1="." u2="4" k="40" />
+<hkern u1="." u2="6" k="24" />
+<hkern u1="." u2="8" k="12" />
+<hkern u1="." u2="9" k="8" />
+<hkern u1="." u2="A" k="-24" />
+<hkern u1="." u2="C" k="32" />
+<hkern u1="." u2="G" k="32" />
+<hkern u1="." u2="O" k="32" />
+<hkern u1="." u2="Q" k="32" />
+<hkern u1="." u2="T" k="48" />
+<hkern u1="." u2="U" k="8" />
+<hkern u1="." u2="V" k="48" />
+<hkern u1="." u2="W" k="16" />
+<hkern u1="." u2="Y" k="40" />
+<hkern u1="." u2="a" k="16" />
+<hkern u1="." u2="c" k="16" />
+<hkern u1="." u2="d" k="16" />
+<hkern u1="." u2="e" k="16" />
+<hkern u1="." u2="m" k="16" />
+<hkern u1="." u2="n" k="16" />
+<hkern u1="." u2="o" k="16" />
+<hkern u1="." u2="p" k="16" />
+<hkern u1="." u2="q" k="16" />
+<hkern u1="." u2="r" k="16" />
+<hkern u1="." u2="t" k="32" />
+<hkern u1="." u2="u" k="16" />
+<hkern u1="." u2="v" k="24" />
+<hkern u1="." u2="w" k="8" />
+<hkern u1="." u2="y" k="24" />
+<hkern u1="." u2="&#xc4;" k="-24" />
+<hkern u1="." u2="&#xc5;" k="-24" />
+<hkern u1="." u2="&#xd6;" k="32" />
+<hkern u1="." u2="&#xdc;" k="8" />
+<hkern u1="." u2="&#xe7;" k="16" />
+<hkern u1="." u2="&#xc6;" k="-24" />
+<hkern u1="." u2="&#xd8;" k="32" />
+<hkern u1="." u2="&#xe6;" k="16" />
+<hkern u1="." u2="&#xc0;" k="-24" />
+<hkern u1="." u2="&#xc3;" k="-24" />
+<hkern u1="." u2="&#xd5;" k="32" />
+<hkern u1="." u2="&#x152;" k="32" />
+<hkern u1="." u2="&#x153;" k="16" />
+<hkern u1="." u2="&#x178;" k="40" />
+<hkern u1="." u2="&#xc2;" k="-24" />
+<hkern u1="." u2="&#xc1;" k="-24" />
+<hkern u1="." u2="&#xd3;" k="32" />
+<hkern u1="." u2="&#xd4;" k="32" />
+<hkern u1="." u2="&#xd2;" k="32" />
+<hkern u1="." u2="&#xda;" k="8" />
+<hkern u1="." u2="&#xdb;" k="8" />
+<hkern u1="." u2="&#xd9;" k="8" />
+<hkern u1="." u2="&#x131;" k="16" />
+<hkern u1="/" u2="1" k="-16" />
+<hkern u1="/" u2="4" k="16" />
+<hkern u1="/" u2="C" k="-8" />
+<hkern u1="/" u2="G" k="-8" />
+<hkern u1="/" u2="O" k="-8" />
+<hkern u1="/" u2="Q" k="-8" />
+<hkern u1="/" u2="a" k="8" />
+<hkern u1="/" u2="c" k="8" />
+<hkern u1="/" u2="d" k="8" />
+<hkern u1="/" u2="e" k="8" />
+<hkern u1="/" u2="g" k="8" />
+<hkern u1="/" u2="o" k="8" />
+<hkern u1="/" u2="q" k="8" />
+<hkern u1="/" u2="&#xd6;" k="-8" />
+<hkern u1="/" u2="&#xe7;" k="8" />
+<hkern u1="/" u2="&#xd8;" k="-8" />
+<hkern u1="/" u2="&#xe6;" k="8" />
+<hkern u1="/" u2="&#xd5;" k="-8" />
+<hkern u1="/" u2="&#x152;" k="-8" />
+<hkern u1="/" u2="&#x153;" k="8" />
+<hkern u1="/" u2="&#xd3;" k="-8" />
+<hkern u1="/" u2="&#xd4;" k="-8" />
+<hkern u1="/" u2="&#xd2;" k="-8" />
+<hkern u1="0" u2="&#x2c;" k="32" />
+<hkern u1="0" u2="." k="32" />
+<hkern u1="0" u2="7" k="16" />
+<hkern u1="0" u2="&#x2026;" k="32" />
+<hkern u1="0" u2="&#x201a;" k="32" />
+<hkern u1="0" u2="&#x201e;" k="32" />
+<hkern u1="2" u2="-" k="8" />
+<hkern u1="2" u2="4" k="24" />
+<hkern u1="2" u2="&#x2013;" k="8" />
+<hkern u1="2" u2="&#x2014;" k="8" />
+<hkern u1="3" u2="&#x2c;" k="16" />
+<hkern u1="3" u2="." k="16" />
+<hkern u1="3" u2="7" k="16" />
+<hkern u1="3" u2="&#x2026;" k="16" />
+<hkern u1="3" u2="&#x201a;" k="16" />
+<hkern u1="3" u2="&#x201e;" k="16" />
+<hkern u1="4" u2="7" k="8" />
+<hkern u1="4" u2="&#xb0;" k="32" />
+<hkern u1="4" u2="&#x2122;" k="40" />
+<hkern u1="5" u2="&#x2c;" k="8" />
+<hkern u1="5" u2="." k="8" />
+<hkern u1="5" u2="7" k="8" />
+<hkern u1="5" u2="&#x2026;" k="8" />
+<hkern u1="5" u2="&#x201a;" k="8" />
+<hkern u1="5" u2="&#x201e;" k="8" />
+<hkern u1="6" u2="&#x2c;" k="8" />
+<hkern u1="6" u2="." k="8" />
+<hkern u1="6" u2="7" k="8" />
+<hkern u1="6" u2="&#x2026;" k="8" />
+<hkern u1="6" u2="&#x201a;" k="8" />
+<hkern u1="6" u2="&#x201e;" k="8" />
+<hkern u1="7" u2="&#x2c;" k="64" />
+<hkern u1="7" u2="-" k="32" />
+<hkern u1="7" u2="." k="64" />
+<hkern u1="7" u2="0" k="8" />
+<hkern u1="7" u2="1" k="-16" />
+<hkern u1="7" u2="3" k="-16" />
+<hkern u1="7" u2="4" k="48" />
+<hkern u1="7" u2="5" k="-12" />
+<hkern u1="7" u2="9" k="-8" />
+<hkern u1="7" u2=":" k="16" />
+<hkern u1="7" u2=";" k="16" />
+<hkern u1="7" u2="&#xa2;" k="32" />
+<hkern u1="7" u2="&#x2026;" k="64" />
+<hkern u1="7" u2="&#x2013;" k="32" />
+<hkern u1="7" u2="&#x2014;" k="32" />
+<hkern u1="7" u2="&#x201a;" k="64" />
+<hkern u1="7" u2="&#x201e;" k="64" />
+<hkern u1="8" u2="&#x2c;" k="12" />
+<hkern u1="8" u2="." k="12" />
+<hkern u1="8" u2="7" k="12" />
+<hkern u1="8" u2="&#x2026;" k="12" />
+<hkern u1="8" u2="&#x201a;" k="12" />
+<hkern u1="8" u2="&#x201e;" k="12" />
+<hkern u1="9" u2="&#x2c;" k="24" />
+<hkern u1="9" u2="." k="24" />
+<hkern u1="9" u2="7" k="36" />
+<hkern u1="9" u2="&#x2026;" k="24" />
+<hkern u1="9" u2="&#x201a;" k="24" />
+<hkern u1="9" u2="&#x201e;" k="24" />
+<hkern u1=":" u2="&#xdd;" k="40" />
+<hkern u1=":" u2="0" k="32" />
+<hkern u1=":" u2="1" k="48" />
+<hkern u1=":" u2="4" k="40" />
+<hkern u1=":" u2="6" k="24" />
+<hkern u1=":" u2="8" k="12" />
+<hkern u1=":" u2="9" k="8" />
+<hkern u1=":" u2="A" k="-24" />
+<hkern u1=":" u2="C" k="32" />
+<hkern u1=":" u2="G" k="32" />
+<hkern u1=":" u2="O" k="32" />
+<hkern u1=":" u2="Q" k="32" />
+<hkern u1=":" u2="T" k="48" />
+<hkern u1=":" u2="U" k="8" />
+<hkern u1=":" u2="V" k="48" />
+<hkern u1=":" u2="W" k="16" />
+<hkern u1=":" u2="Y" k="40" />
+<hkern u1=":" u2="a" k="16" />
+<hkern u1=":" u2="c" k="16" />
+<hkern u1=":" u2="d" k="16" />
+<hkern u1=":" u2="e" k="16" />
+<hkern u1=":" u2="m" k="16" />
+<hkern u1=":" u2="n" k="16" />
+<hkern u1=":" u2="o" k="16" />
+<hkern u1=":" u2="p" k="16" />
+<hkern u1=":" u2="q" k="16" />
+<hkern u1=":" u2="r" k="16" />
+<hkern u1=":" u2="t" k="32" />
+<hkern u1=":" u2="u" k="16" />
+<hkern u1=":" u2="v" k="24" />
+<hkern u1=":" u2="w" k="8" />
+<hkern u1=":" u2="y" k="24" />
+<hkern u1=":" u2="&#xc4;" k="-24" />
+<hkern u1=":" u2="&#xc5;" k="-24" />
+<hkern u1=":" u2="&#xd6;" k="32" />
+<hkern u1=":" u2="&#xdc;" k="8" />
+<hkern u1=":" u2="&#xe7;" k="16" />
+<hkern u1=":" u2="&#xc6;" k="-24" />
+<hkern u1=":" u2="&#xd8;" k="32" />
+<hkern u1=":" u2="&#xe6;" k="16" />
+<hkern u1=":" u2="&#xc0;" k="-24" />
+<hkern u1=":" u2="&#xc3;" k="-24" />
+<hkern u1=":" u2="&#xd5;" k="32" />
+<hkern u1=":" u2="&#x152;" k="32" />
+<hkern u1=":" u2="&#x153;" k="16" />
+<hkern u1=":" u2="&#x178;" k="40" />
+<hkern u1=":" u2="&#xc2;" k="-24" />
+<hkern u1=":" u2="&#xc1;" k="-24" />
+<hkern u1=":" u2="&#xd3;" k="32" />
+<hkern u1=":" u2="&#xd4;" k="32" />
+<hkern u1=":" u2="&#xd2;" k="32" />
+<hkern u1=":" u2="&#xda;" k="8" />
+<hkern u1=":" u2="&#xdb;" k="8" />
+<hkern u1=":" u2="&#xd9;" k="8" />
+<hkern u1=":" u2="&#x131;" k="16" />
+<hkern u1=";" u2="&#xdd;" k="24" />
+<hkern u1=";" u2="1" k="24" />
+<hkern u1=";" u2="7" k="16" />
+<hkern u1=";" u2="T" k="40" />
+<hkern u1=";" u2="V" k="16" />
+<hkern u1=";" u2="W" k="16" />
+<hkern u1=";" u2="Y" k="24" />
+<hkern u1=";" u2="a" k="8" />
+<hkern u1=";" u2="c" k="8" />
+<hkern u1=";" u2="d" k="8" />
+<hkern u1=";" u2="e" k="8" />
+<hkern u1=";" u2="o" k="8" />
+<hkern u1=";" u2="q" k="8" />
+<hkern u1=";" u2="&#xe7;" k="8" />
+<hkern u1=";" u2="&#xe6;" k="8" />
+<hkern u1=";" u2="&#x153;" k="8" />
+<hkern u1=";" u2="&#x178;" k="24" />
+<hkern u1="&gt;" u2="7" k="48" />
+<hkern u1="@" u2="&#xdd;" k="24" />
+<hkern u1="@" u2="A" k="8" />
+<hkern u1="@" u2="T" k="24" />
+<hkern u1="@" u2="W" k="16" />
+<hkern u1="@" u2="Y" k="24" />
+<hkern u1="@" u2="&#xc4;" k="8" />
+<hkern u1="@" u2="&#xc5;" k="8" />
+<hkern u1="@" u2="&#xc6;" k="8" />
+<hkern u1="@" u2="&#xc0;" k="8" />
+<hkern u1="@" u2="&#xc3;" k="8" />
+<hkern u1="@" u2="&#x178;" k="24" />
+<hkern u1="@" u2="&#xc2;" k="8" />
+<hkern u1="@" u2="&#xc1;" k="8" />
+<hkern u1="A" u2="&#x160;" k="-4" />
+<hkern u1="A" u2="&#xdd;" k="56" />
+<hkern u1="A" u2="*" k="48" />
+<hkern u1="A" u2="&#x2c;" k="-24" />
+<hkern u1="A" u2="-" k="-8" />
+<hkern u1="A" u2="." k="-24" />
+<hkern u1="A" u2="@" k="8" />
+<hkern u1="A" u2="A" k="-8" />
+<hkern u1="A" u2="C" k="32" />
+<hkern u1="A" u2="G" k="32" />
+<hkern u1="A" u2="J" k="8" />
+<hkern u1="A" u2="O" k="32" />
+<hkern u1="A" u2="Q" k="32" />
+<hkern u1="A" u2="S" k="-4" />
+<hkern u1="A" u2="T" k="112" />
+<hkern u1="A" u2="V" k="56" />
+<hkern u1="A" u2="W" k="56" />
+<hkern u1="A" u2="Y" k="56" />
+<hkern u1="A" u2="a" k="4" />
+<hkern u1="A" u2="c" k="4" />
+<hkern u1="A" u2="d" k="4" />
+<hkern u1="A" u2="e" k="4" />
+<hkern u1="A" u2="m" k="4" />
+<hkern u1="A" u2="n" k="4" />
+<hkern u1="A" u2="o" k="4" />
+<hkern u1="A" u2="p" k="4" />
+<hkern u1="A" u2="q" k="4" />
+<hkern u1="A" u2="r" k="4" />
+<hkern u1="A" u2="t" k="4" />
+<hkern u1="A" u2="u" k="4" />
+<hkern u1="A" u2="v" k="24" />
+<hkern u1="A" u2="w" k="8" />
+<hkern u1="A" u2="x" k="-16" />
+<hkern u1="A" u2="y" k="24" />
+<hkern u1="A" u2="z" k="-8" />
+<hkern u1="A" u2="&#xc4;" k="-8" />
+<hkern u1="A" u2="&#xc5;" k="-8" />
+<hkern u1="A" u2="&#xd6;" k="32" />
+<hkern u1="A" u2="&#xe7;" k="4" />
+<hkern u1="A" u2="&#xae;" k="8" />
+<hkern u1="A" u2="&#xa9;" k="8" />
+<hkern u1="A" u2="&#x2122;" k="56" />
+<hkern u1="A" u2="&#xc6;" k="-8" />
+<hkern u1="A" u2="&#xd8;" k="32" />
+<hkern u1="A" u2="&#x3c0;" k="8" />
+<hkern u1="A" u2="&#xe6;" k="4" />
+<hkern u1="A" u2="&#x2026;" k="-24" />
+<hkern u1="A" u2="&#xc0;" k="-8" />
+<hkern u1="A" u2="&#xc3;" k="-8" />
+<hkern u1="A" u2="&#xd5;" k="32" />
+<hkern u1="A" u2="&#x152;" k="32" />
+<hkern u1="A" u2="&#x153;" k="4" />
+<hkern u1="A" u2="&#x2013;" k="-8" />
+<hkern u1="A" u2="&#x2014;" k="-8" />
+<hkern u1="A" u2="&#x201c;" k="48" />
+<hkern u1="A" u2="&#x201d;" k="24" />
+<hkern u1="A" u2="&#x2018;" k="48" />
+<hkern u1="A" u2="&#x2019;" k="24" />
+<hkern u1="A" u2="&#x178;" k="56" />
+<hkern u1="A" u2="&#x201a;" k="-24" />
+<hkern u1="A" u2="&#x201e;" k="-24" />
+<hkern u1="A" u2="&#xc2;" k="-8" />
+<hkern u1="A" u2="&#xc1;" k="-8" />
+<hkern u1="A" u2="&#xd3;" k="32" />
+<hkern u1="A" u2="&#xd4;" k="32" />
+<hkern u1="A" u2="&#xd2;" k="32" />
+<hkern u1="A" u2="&#x131;" k="4" />
+<hkern u1="B" u2="&#xdd;" k="16" />
+<hkern u1="B" u2="V" k="4" />
+<hkern u1="B" u2="W" k="16" />
+<hkern u1="B" u2="Y" k="16" />
+<hkern u1="B" u2="&#x178;" k="16" />
+<hkern u1="C" u2="&#x17d;" k="8" />
+<hkern u1="C" u2="T" k="16" />
+<hkern u1="C" u2="V" k="-16" />
+<hkern u1="C" u2="X" k="-8" />
+<hkern u1="C" u2="Z" k="8" />
+<hkern u1="C" u2="a" k="-4" />
+<hkern u1="C" u2="c" k="-4" />
+<hkern u1="C" u2="d" k="-4" />
+<hkern u1="C" u2="e" k="-4" />
+<hkern u1="C" u2="o" k="-4" />
+<hkern u1="C" u2="q" k="-4" />
+<hkern u1="C" u2="t" k="4" />
+<hkern u1="C" u2="v" k="4" />
+<hkern u1="C" u2="w" k="4" />
+<hkern u1="C" u2="y" k="4" />
+<hkern u1="C" u2="z" k="8" />
+<hkern u1="C" u2="&#xe7;" k="-4" />
+<hkern u1="C" u2="&#xe6;" k="-4" />
+<hkern u1="C" u2="&#x153;" k="-4" />
+<hkern u1="D" u2="&#xdd;" k="36" />
+<hkern u1="D" u2="&#x17d;" k="36" />
+<hkern u1="D" u2="&#x2c;" k="32" />
+<hkern u1="D" u2="." k="32" />
+<hkern u1="D" u2="/" k="40" />
+<hkern u1="D" u2="?" k="16" />
+<hkern u1="D" u2="J" k="36" />
+<hkern u1="D" u2="T" k="40" />
+<hkern u1="D" u2="V" k="32" />
+<hkern u1="D" u2="W" k="48" />
+<hkern u1="D" u2="X" k="28" />
+<hkern u1="D" u2="Y" k="36" />
+<hkern u1="D" u2="Z" k="36" />
+<hkern u1="D" u2="a" k="4" />
+<hkern u1="D" u2="b" k="8" />
+<hkern u1="D" u2="c" k="4" />
+<hkern u1="D" u2="d" k="4" />
+<hkern u1="D" u2="e" k="4" />
+<hkern u1="D" u2="h" k="8" />
+<hkern u1="D" u2="k" k="8" />
+<hkern u1="D" u2="l" k="8" />
+<hkern u1="D" u2="m" k="4" />
+<hkern u1="D" u2="n" k="4" />
+<hkern u1="D" u2="o" k="4" />
+<hkern u1="D" u2="p" k="4" />
+<hkern u1="D" u2="q" k="4" />
+<hkern u1="D" u2="r" k="4" />
+<hkern u1="D" u2="u" k="4" />
+<hkern u1="D" u2="x" k="8" />
+<hkern u1="D" u2="z" k="8" />
+<hkern u1="D" u2="&#xe7;" k="4" />
+<hkern u1="D" u2="&#xe6;" k="4" />
+<hkern u1="D" u2="&#x2026;" k="32" />
+<hkern u1="D" u2="&#x153;" k="4" />
+<hkern u1="D" u2="&#x201c;" k="16" />
+<hkern u1="D" u2="&#x2018;" k="16" />
+<hkern u1="D" u2="&#x178;" k="36" />
+<hkern u1="D" u2="&#x201a;" k="32" />
+<hkern u1="D" u2="&#x201e;" k="32" />
+<hkern u1="D" u2="&#x131;" k="4" />
+<hkern u1="E" u2="@" k="16" />
+<hkern u1="E" u2="T" k="-12" />
+<hkern u1="E" u2="a" k="20" />
+<hkern u1="E" u2="c" k="20" />
+<hkern u1="E" u2="d" k="20" />
+<hkern u1="E" u2="e" k="20" />
+<hkern u1="E" u2="f" k="8" />
+<hkern u1="E" u2="g" k="4" />
+<hkern u1="E" u2="o" k="20" />
+<hkern u1="E" u2="q" k="20" />
+<hkern u1="E" u2="v" k="16" />
+<hkern u1="E" u2="y" k="16" />
+<hkern u1="E" u2="&#xe7;" k="20" />
+<hkern u1="E" u2="&#xae;" k="16" />
+<hkern u1="E" u2="&#xa9;" k="16" />
+<hkern u1="E" u2="&#x3c0;" k="16" />
+<hkern u1="E" u2="&#xe6;" k="20" />
+<hkern u1="E" u2="&#x153;" k="20" />
+<hkern u1="F" u2="&#x161;" k="32" />
+<hkern u1="F" u2="&#xdd;" k="-20" />
+<hkern u1="F" u2="&amp;" k="32" />
+<hkern u1="F" u2="&#x2c;" k="72" />
+<hkern u1="F" u2="-" k="40" />
+<hkern u1="F" u2="." k="72" />
+<hkern u1="F" u2="/" k="64" />
+<hkern u1="F" u2=":" k="24" />
+<hkern u1="F" u2=";" k="24" />
+<hkern u1="F" u2="@" k="16" />
+<hkern u1="F" u2="A" k="32" />
+<hkern u1="F" u2="J" k="72" />
+<hkern u1="F" u2="T" k="-24" />
+<hkern u1="F" u2="V" k="-16" />
+<hkern u1="F" u2="W" k="-4" />
+<hkern u1="F" u2="X" k="-8" />
+<hkern u1="F" u2="Y" k="-20" />
+<hkern u1="F" u2="a" k="40" />
+<hkern u1="F" u2="b" k="8" />
+<hkern u1="F" u2="c" k="40" />
+<hkern u1="F" u2="d" k="40" />
+<hkern u1="F" u2="e" k="40" />
+<hkern u1="F" u2="f" k="16" />
+<hkern u1="F" u2="g" k="24" />
+<hkern u1="F" u2="h" k="8" />
+<hkern u1="F" u2="i" k="8" />
+<hkern u1="F" u2="j" k="8" />
+<hkern u1="F" u2="k" k="8" />
+<hkern u1="F" u2="l" k="8" />
+<hkern u1="F" u2="m" k="32" />
+<hkern u1="F" u2="n" k="32" />
+<hkern u1="F" u2="o" k="40" />
+<hkern u1="F" u2="p" k="32" />
+<hkern u1="F" u2="q" k="40" />
+<hkern u1="F" u2="r" k="32" />
+<hkern u1="F" u2="s" k="32" />
+<hkern u1="F" u2="t" k="16" />
+<hkern u1="F" u2="u" k="32" />
+<hkern u1="F" u2="v" k="16" />
+<hkern u1="F" u2="w" k="16" />
+<hkern u1="F" u2="x" k="32" />
+<hkern u1="F" u2="y" k="16" />
+<hkern u1="F" u2="z" k="32" />
+<hkern u1="F" u2="&#xc4;" k="32" />
+<hkern u1="F" u2="&#xc5;" k="32" />
+<hkern u1="F" u2="&#xe7;" k="40" />
+<hkern u1="F" u2="&#xae;" k="16" />
+<hkern u1="F" u2="&#xa9;" k="16" />
+<hkern u1="F" u2="&#xc6;" k="32" />
+<hkern u1="F" u2="&#x3c0;" k="16" />
+<hkern u1="F" u2="&#xe6;" k="40" />
+<hkern u1="F" u2="&#x2026;" k="72" />
+<hkern u1="F" u2="&#xc0;" k="32" />
+<hkern u1="F" u2="&#xc3;" k="32" />
+<hkern u1="F" u2="&#x153;" k="40" />
+<hkern u1="F" u2="&#x2013;" k="40" />
+<hkern u1="F" u2="&#x2014;" k="40" />
+<hkern u1="F" u2="&#x178;" k="-20" />
+<hkern u1="F" u2="&#x201a;" k="72" />
+<hkern u1="F" u2="&#x201e;" k="72" />
+<hkern u1="F" u2="&#xc2;" k="32" />
+<hkern u1="F" u2="&#xc1;" k="32" />
+<hkern u1="F" u2="&#x131;" k="32" />
+<hkern u1="G" u2="&#xdd;" k="12" />
+<hkern u1="G" u2="&#x2c;" k="-16" />
+<hkern u1="G" u2="." k="-16" />
+<hkern u1="G" u2="T" k="20" />
+<hkern u1="G" u2="V" k="8" />
+<hkern u1="G" u2="W" k="12" />
+<hkern u1="G" u2="Y" k="12" />
+<hkern u1="G" u2="t" k="8" />
+<hkern u1="G" u2="v" k="8" />
+<hkern u1="G" u2="y" k="8" />
+<hkern u1="G" u2="&#x2122;" k="24" />
+<hkern u1="G" u2="&#x2026;" k="-16" />
+<hkern u1="G" u2="&#x178;" k="12" />
+<hkern u1="G" u2="&#x201a;" k="-16" />
+<hkern u1="G" u2="&#x201e;" k="-16" />
+<hkern u1="H" u2="/" k="16" />
+<hkern u1="H" u2="v" k="-16" />
+<hkern u1="H" u2="y" k="-16" />
+<hkern u1="I" u2="/" k="16" />
+<hkern u1="I" u2="v" k="-16" />
+<hkern u1="I" u2="y" k="-16" />
+<hkern u1="J" u2="&#x2c;" k="8" />
+<hkern u1="J" u2="." k="8" />
+<hkern u1="J" u2="A" k="-16" />
+<hkern u1="J" u2="J" k="8" />
+<hkern u1="J" u2="v" k="-16" />
+<hkern u1="J" u2="y" k="-16" />
+<hkern u1="J" u2="&#xc4;" k="-16" />
+<hkern u1="J" u2="&#xc5;" k="-16" />
+<hkern u1="J" u2="&#xc6;" k="-16" />
+<hkern u1="J" u2="&#x2026;" k="8" />
+<hkern u1="J" u2="&#xc0;" k="-16" />
+<hkern u1="J" u2="&#xc3;" k="-16" />
+<hkern u1="J" u2="&#x201a;" k="8" />
+<hkern u1="J" u2="&#x201e;" k="8" />
+<hkern u1="J" u2="&#xc2;" k="-16" />
+<hkern u1="J" u2="&#xc1;" k="-16" />
+<hkern u1="K" u2="&#xf0;" k="16" />
+<hkern u1="K" u2="&#x160;" k="4" />
+<hkern u1="K" u2="&#xdd;" k="-12" />
+<hkern u1="K" u2="&#x17d;" k="4" />
+<hkern u1="K" u2="&amp;" k="16" />
+<hkern u1="K" u2="-" k="32" />
+<hkern u1="K" u2="@" k="24" />
+<hkern u1="K" u2="C" k="24" />
+<hkern u1="K" u2="G" k="24" />
+<hkern u1="K" u2="O" k="24" />
+<hkern u1="K" u2="Q" k="24" />
+<hkern u1="K" u2="S" k="4" />
+<hkern u1="K" u2="T" k="-4" />
+<hkern u1="K" u2="U" k="8" />
+<hkern u1="K" u2="V" k="-12" />
+<hkern u1="K" u2="W" k="8" />
+<hkern u1="K" u2="X" k="-8" />
+<hkern u1="K" u2="Y" k="-12" />
+<hkern u1="K" u2="Z" k="4" />
+<hkern u1="K" u2="a" k="16" />
+<hkern u1="K" u2="c" k="16" />
+<hkern u1="K" u2="d" k="16" />
+<hkern u1="K" u2="e" k="16" />
+<hkern u1="K" u2="f" k="4" />
+<hkern u1="K" u2="g" k="8" />
+<hkern u1="K" u2="o" k="16" />
+<hkern u1="K" u2="q" k="16" />
+<hkern u1="K" u2="t" k="8" />
+<hkern u1="K" u2="v" k="16" />
+<hkern u1="K" u2="y" k="16" />
+<hkern u1="K" u2="&#xd6;" k="24" />
+<hkern u1="K" u2="&#xdc;" k="8" />
+<hkern u1="K" u2="&#xe7;" k="16" />
+<hkern u1="K" u2="&#xae;" k="24" />
+<hkern u1="K" u2="&#xa9;" k="24" />
+<hkern u1="K" u2="&#xd8;" k="24" />
+<hkern u1="K" u2="&#x3c0;" k="24" />
+<hkern u1="K" u2="&#xe6;" k="16" />
+<hkern u1="K" u2="&#xd5;" k="24" />
+<hkern u1="K" u2="&#x152;" k="24" />
+<hkern u1="K" u2="&#x153;" k="16" />
+<hkern u1="K" u2="&#x2013;" k="32" />
+<hkern u1="K" u2="&#x2014;" k="32" />
+<hkern u1="K" u2="&#x178;" k="-12" />
+<hkern u1="K" u2="&#xd3;" k="24" />
+<hkern u1="K" u2="&#xd4;" k="24" />
+<hkern u1="K" u2="&#xd2;" k="24" />
+<hkern u1="K" u2="&#xda;" k="8" />
+<hkern u1="K" u2="&#xdb;" k="8" />
+<hkern u1="K" u2="&#xd9;" k="8" />
+<hkern u1="L" u2="&#xdd;" k="64" />
+<hkern u1="L" u2="@" k="40" />
+<hkern u1="L" u2="A" k="-16" />
+<hkern u1="L" u2="C" k="40" />
+<hkern u1="L" u2="G" k="40" />
+<hkern u1="L" u2="O" k="40" />
+<hkern u1="L" u2="Q" k="40" />
+<hkern u1="L" u2="T" k="96" />
+<hkern u1="L" u2="U" k="12" />
+<hkern u1="L" u2="V" k="72" />
+<hkern u1="L" u2="W" k="56" />
+<hkern u1="L" u2="Y" k="64" />
+<hkern u1="L" u2="\" k="40" />
+<hkern u1="L" u2="a" k="16" />
+<hkern u1="L" u2="c" k="16" />
+<hkern u1="L" u2="d" k="16" />
+<hkern u1="L" u2="e" k="16" />
+<hkern u1="L" u2="o" k="16" />
+<hkern u1="L" u2="q" k="16" />
+<hkern u1="L" u2="v" k="32" />
+<hkern u1="L" u2="w" k="8" />
+<hkern u1="L" u2="y" k="32" />
+<hkern u1="L" u2="&#xc4;" k="-16" />
+<hkern u1="L" u2="&#xc5;" k="-16" />
+<hkern u1="L" u2="&#xd6;" k="40" />
+<hkern u1="L" u2="&#xdc;" k="12" />
+<hkern u1="L" u2="&#xe7;" k="16" />
+<hkern u1="L" u2="&#xae;" k="40" />
+<hkern u1="L" u2="&#xa9;" k="40" />
+<hkern u1="L" u2="&#x2122;" k="56" />
+<hkern u1="L" u2="&#xc6;" k="-16" />
+<hkern u1="L" u2="&#xd8;" k="40" />
+<hkern u1="L" u2="&#x3c0;" k="40" />
+<hkern u1="L" u2="&#xe6;" k="16" />
+<hkern u1="L" u2="&#xc0;" k="-16" />
+<hkern u1="L" u2="&#xc3;" k="-16" />
+<hkern u1="L" u2="&#xd5;" k="40" />
+<hkern u1="L" u2="&#x152;" k="40" />
+<hkern u1="L" u2="&#x153;" k="16" />
+<hkern u1="L" u2="&#x201c;" k="80" />
+<hkern u1="L" u2="&#x201d;" k="48" />
+<hkern u1="L" u2="&#x2018;" k="80" />
+<hkern u1="L" u2="&#x2019;" k="48" />
+<hkern u1="L" u2="&#x178;" k="64" />
+<hkern u1="L" u2="&#xc2;" k="-16" />
+<hkern u1="L" u2="&#xc1;" k="-16" />
+<hkern u1="L" u2="&#xd3;" k="40" />
+<hkern u1="L" u2="&#xd4;" k="40" />
+<hkern u1="L" u2="&#xd2;" k="40" />
+<hkern u1="L" u2="&#xda;" k="12" />
+<hkern u1="L" u2="&#xdb;" k="12" />
+<hkern u1="L" u2="&#xd9;" k="12" />
+<hkern u1="M" u2="/" k="16" />
+<hkern u1="M" u2="v" k="-16" />
+<hkern u1="M" u2="y" k="-16" />
+<hkern u1="N" u2="/" k="16" />
+<hkern u1="N" u2="v" k="-16" />
+<hkern u1="N" u2="y" k="-16" />
+<hkern u1="O" u2="&#xdd;" k="36" />
+<hkern u1="O" u2="&#x17d;" k="36" />
+<hkern u1="O" u2="&#x2c;" k="32" />
+<hkern u1="O" u2="." k="32" />
+<hkern u1="O" u2="/" k="40" />
+<hkern u1="O" u2="?" k="16" />
+<hkern u1="O" u2="J" k="36" />
+<hkern u1="O" u2="T" k="40" />
+<hkern u1="O" u2="V" k="32" />
+<hkern u1="O" u2="W" k="48" />
+<hkern u1="O" u2="X" k="28" />
+<hkern u1="O" u2="Y" k="36" />
+<hkern u1="O" u2="Z" k="36" />
+<hkern u1="O" u2="a" k="4" />
+<hkern u1="O" u2="b" k="8" />
+<hkern u1="O" u2="c" k="4" />
+<hkern u1="O" u2="d" k="4" />
+<hkern u1="O" u2="e" k="4" />
+<hkern u1="O" u2="h" k="8" />
+<hkern u1="O" u2="k" k="8" />
+<hkern u1="O" u2="l" k="8" />
+<hkern u1="O" u2="m" k="4" />
+<hkern u1="O" u2="n" k="4" />
+<hkern u1="O" u2="o" k="4" />
+<hkern u1="O" u2="p" k="4" />
+<hkern u1="O" u2="q" k="4" />
+<hkern u1="O" u2="r" k="4" />
+<hkern u1="O" u2="u" k="4" />
+<hkern u1="O" u2="x" k="8" />
+<hkern u1="O" u2="z" k="8" />
+<hkern u1="O" u2="&#xe7;" k="4" />
+<hkern u1="O" u2="&#xe6;" k="4" />
+<hkern u1="O" u2="&#x2026;" k="32" />
+<hkern u1="O" u2="&#x153;" k="4" />
+<hkern u1="O" u2="&#x201c;" k="16" />
+<hkern u1="O" u2="&#x2018;" k="16" />
+<hkern u1="O" u2="&#x178;" k="36" />
+<hkern u1="O" u2="&#x201a;" k="32" />
+<hkern u1="O" u2="&#x201e;" k="32" />
+<hkern u1="O" u2="&#x131;" k="4" />
+<hkern u1="P" u2="&#xdd;" k="4" />
+<hkern u1="P" u2="&#x17d;" k="20" />
+<hkern u1="P" u2="&amp;" k="16" />
+<hkern u1="P" u2="&#x2c;" k="48" />
+<hkern u1="P" u2="." k="48" />
+<hkern u1="P" u2="A" k="28" />
+<hkern u1="P" u2="J" k="68" />
+<hkern u1="P" u2="V" k="-4" />
+<hkern u1="P" u2="W" k="12" />
+<hkern u1="P" u2="X" k="-4" />
+<hkern u1="P" u2="Y" k="4" />
+<hkern u1="P" u2="Z" k="20" />
+<hkern u1="P" u2="a" k="8" />
+<hkern u1="P" u2="c" k="8" />
+<hkern u1="P" u2="d" k="8" />
+<hkern u1="P" u2="e" k="8" />
+<hkern u1="P" u2="f" k="-8" />
+<hkern u1="P" u2="o" k="8" />
+<hkern u1="P" u2="q" k="8" />
+<hkern u1="P" u2="t" k="-8" />
+<hkern u1="P" u2="v" k="-12" />
+<hkern u1="P" u2="w" k="-8" />
+<hkern u1="P" u2="x" k="-4" />
+<hkern u1="P" u2="y" k="-12" />
+<hkern u1="P" u2="&#xc4;" k="28" />
+<hkern u1="P" u2="&#xc5;" k="28" />
+<hkern u1="P" u2="&#xe7;" k="8" />
+<hkern u1="P" u2="&#xc6;" k="28" />
+<hkern u1="P" u2="&#xe6;" k="8" />
+<hkern u1="P" u2="&#x2026;" k="48" />
+<hkern u1="P" u2="&#xc0;" k="28" />
+<hkern u1="P" u2="&#xc3;" k="28" />
+<hkern u1="P" u2="&#x153;" k="8" />
+<hkern u1="P" u2="&#x178;" k="4" />
+<hkern u1="P" u2="&#x201a;" k="48" />
+<hkern u1="P" u2="&#x201e;" k="48" />
+<hkern u1="P" u2="&#xc2;" k="28" />
+<hkern u1="P" u2="&#xc1;" k="28" />
+<hkern u1="Q" u2="&#xdd;" k="36" />
+<hkern u1="Q" u2="&#x17d;" k="36" />
+<hkern u1="Q" u2="&#x2c;" k="32" />
+<hkern u1="Q" u2="." k="32" />
+<hkern u1="Q" u2="/" k="24" />
+<hkern u1="Q" u2="?" k="16" />
+<hkern u1="Q" u2="J" k="36" />
+<hkern u1="Q" u2="T" k="40" />
+<hkern u1="Q" u2="V" k="32" />
+<hkern u1="Q" u2="W" k="48" />
+<hkern u1="Q" u2="X" k="28" />
+<hkern u1="Q" u2="Y" k="36" />
+<hkern u1="Q" u2="Z" k="36" />
+<hkern u1="Q" u2="a" k="4" />
+<hkern u1="Q" u2="b" k="8" />
+<hkern u1="Q" u2="c" k="4" />
+<hkern u1="Q" u2="d" k="4" />
+<hkern u1="Q" u2="e" k="4" />
+<hkern u1="Q" u2="h" k="8" />
+<hkern u1="Q" u2="k" k="8" />
+<hkern u1="Q" u2="l" k="8" />
+<hkern u1="Q" u2="m" k="4" />
+<hkern u1="Q" u2="n" k="4" />
+<hkern u1="Q" u2="o" k="4" />
+<hkern u1="Q" u2="p" k="4" />
+<hkern u1="Q" u2="q" k="4" />
+<hkern u1="Q" u2="r" k="4" />
+<hkern u1="Q" u2="u" k="4" />
+<hkern u1="Q" u2="x" k="8" />
+<hkern u1="Q" u2="z" k="8" />
+<hkern u1="Q" u2="&#xe7;" k="4" />
+<hkern u1="Q" u2="&#xe6;" k="4" />
+<hkern u1="Q" u2="&#x2026;" k="32" />
+<hkern u1="Q" u2="&#x153;" k="4" />
+<hkern u1="Q" u2="&#x201c;" k="16" />
+<hkern u1="Q" u2="&#x2018;" k="16" />
+<hkern u1="Q" u2="&#x178;" k="36" />
+<hkern u1="Q" u2="&#x201a;" k="32" />
+<hkern u1="Q" u2="&#x201e;" k="32" />
+<hkern u1="Q" u2="&#x131;" k="4" />
+<hkern u1="R" u2="&#xdd;" k="-12" />
+<hkern u1="R" u2="&amp;" k="-8" />
+<hkern u1="R" u2="A" k="-16" />
+<hkern u1="R" u2="J" k="8" />
+<hkern u1="R" u2="T" k="8" />
+<hkern u1="R" u2="V" k="-16" />
+<hkern u1="R" u2="X" k="-8" />
+<hkern u1="R" u2="Y" k="-12" />
+<hkern u1="R" u2="v" k="-24" />
+<hkern u1="R" u2="w" k="-8" />
+<hkern u1="R" u2="y" k="-24" />
+<hkern u1="R" u2="&#xc4;" k="-16" />
+<hkern u1="R" u2="&#xc5;" k="-16" />
+<hkern u1="R" u2="&#xc6;" k="-16" />
+<hkern u1="R" u2="&#xc0;" k="-16" />
+<hkern u1="R" u2="&#xc3;" k="-16" />
+<hkern u1="R" u2="&#x178;" k="-12" />
+<hkern u1="R" u2="&#xc2;" k="-16" />
+<hkern u1="R" u2="&#xc1;" k="-16" />
+<hkern u1="S" u2="&#xdd;" k="8" />
+<hkern u1="S" u2="A" k="-4" />
+<hkern u1="S" u2="V" k="8" />
+<hkern u1="S" u2="W" k="16" />
+<hkern u1="S" u2="X" k="-4" />
+<hkern u1="S" u2="Y" k="8" />
+<hkern u1="S" u2="&#xc4;" k="-4" />
+<hkern u1="S" u2="&#xc5;" k="-4" />
+<hkern u1="S" u2="&#xc6;" k="-4" />
+<hkern u1="S" u2="&#xc0;" k="-4" />
+<hkern u1="S" u2="&#xc3;" k="-4" />
+<hkern u1="S" u2="&#x178;" k="8" />
+<hkern u1="S" u2="&#xc2;" k="-4" />
+<hkern u1="S" u2="&#xc1;" k="-4" />
+<hkern u1="T" u2="&#x161;" k="80" />
+<hkern u1="T" u2="&#xdd;" k="-16" />
+<hkern u1="T" u2="&#x17d;" k="8" />
+<hkern u1="T" u2="&amp;" k="50" />
+<hkern u1="T" u2="&#x2c;" k="48" />
+<hkern u1="T" u2="-" k="56" />
+<hkern u1="T" u2="." k="48" />
+<hkern u1="T" u2=":" k="40" />
+<hkern u1="T" u2=";" k="40" />
+<hkern u1="T" u2="@" k="24" />
+<hkern u1="T" u2="A" k="40" />
+<hkern u1="T" u2="C" k="8" />
+<hkern u1="T" u2="G" k="8" />
+<hkern u1="T" u2="J" k="68" />
+<hkern u1="T" u2="O" k="8" />
+<hkern u1="T" u2="Q" k="8" />
+<hkern u1="T" u2="T" k="-8" />
+<hkern u1="T" u2="V" k="-16" />
+<hkern u1="T" u2="X" k="-8" />
+<hkern u1="T" u2="Y" k="-16" />
+<hkern u1="T" u2="Z" k="8" />
+<hkern u1="T" u2="\" k="-8" />
+<hkern u1="T" u2="a" k="76" />
+<hkern u1="T" u2="c" k="76" />
+<hkern u1="T" u2="d" k="76" />
+<hkern u1="T" u2="e" k="76" />
+<hkern u1="T" u2="f" k="8" />
+<hkern u1="T" u2="g" k="76" />
+<hkern u1="T" u2="m" k="64" />
+<hkern u1="T" u2="n" k="64" />
+<hkern u1="T" u2="o" k="76" />
+<hkern u1="T" u2="p" k="64" />
+<hkern u1="T" u2="q" k="76" />
+<hkern u1="T" u2="r" k="64" />
+<hkern u1="T" u2="s" k="80" />
+<hkern u1="T" u2="t" k="16" />
+<hkern u1="T" u2="u" k="64" />
+<hkern u1="T" u2="v" k="56" />
+<hkern u1="T" u2="w" k="48" />
+<hkern u1="T" u2="x" k="64" />
+<hkern u1="T" u2="y" k="56" />
+<hkern u1="T" u2="z" k="64" />
+<hkern u1="T" u2="&#xc4;" k="40" />
+<hkern u1="T" u2="&#xc5;" k="40" />
+<hkern u1="T" u2="&#xd6;" k="8" />
+<hkern u1="T" u2="&#xe7;" k="76" />
+<hkern u1="T" u2="&#xae;" k="24" />
+<hkern u1="T" u2="&#xa9;" k="24" />
+<hkern u1="T" u2="&#xc6;" k="40" />
+<hkern u1="T" u2="&#xd8;" k="8" />
+<hkern u1="T" u2="&#x3c0;" k="24" />
+<hkern u1="T" u2="&#xe6;" k="76" />
+<hkern u1="T" u2="&#xbf;" k="40" />
+<hkern u1="T" u2="&#xab;" k="88" />
+<hkern u1="T" u2="&#xbb;" k="32" />
+<hkern u1="T" u2="&#x2026;" k="48" />
+<hkern u1="T" u2="&#xc0;" k="40" />
+<hkern u1="T" u2="&#xc3;" k="40" />
+<hkern u1="T" u2="&#xd5;" k="8" />
+<hkern u1="T" u2="&#x152;" k="8" />
+<hkern u1="T" u2="&#x153;" k="76" />
+<hkern u1="T" u2="&#x2013;" k="56" />
+<hkern u1="T" u2="&#x2014;" k="56" />
+<hkern u1="T" u2="&#x178;" k="-16" />
+<hkern u1="T" u2="&#x2039;" k="88" />
+<hkern u1="T" u2="&#x203a;" k="32" />
+<hkern u1="T" u2="&#x201a;" k="48" />
+<hkern u1="T" u2="&#x201e;" k="48" />
+<hkern u1="T" u2="&#xc2;" k="40" />
+<hkern u1="T" u2="&#xc1;" k="40" />
+<hkern u1="T" u2="&#xd3;" k="8" />
+<hkern u1="T" u2="&#xd4;" k="8" />
+<hkern u1="T" u2="&#xd2;" k="8" />
+<hkern u1="T" u2="&#x131;" k="64" />
+<hkern u1="U" u2="&#x2c;" k="8" />
+<hkern u1="U" u2="." k="8" />
+<hkern u1="U" u2="A" k="-16" />
+<hkern u1="U" u2="J" k="8" />
+<hkern u1="U" u2="v" k="-16" />
+<hkern u1="U" u2="y" k="-16" />
+<hkern u1="U" u2="&#xc4;" k="-16" />
+<hkern u1="U" u2="&#xc5;" k="-16" />
+<hkern u1="U" u2="&#xc6;" k="-16" />
+<hkern u1="U" u2="&#x2026;" k="8" />
+<hkern u1="U" u2="&#xc0;" k="-16" />
+<hkern u1="U" u2="&#xc3;" k="-16" />
+<hkern u1="U" u2="&#x201a;" k="8" />
+<hkern u1="U" u2="&#x201e;" k="8" />
+<hkern u1="U" u2="&#xc2;" k="-16" />
+<hkern u1="U" u2="&#xc1;" k="-16" />
+<hkern u1="V" u2="&#x160;" k="-24" />
+<hkern u1="V" u2="&#x161;" k="32" />
+<hkern u1="V" u2="&#xdd;" k="-24" />
+<hkern u1="V" u2="&amp;" k="32" />
+<hkern u1="V" u2=")" k="-24" />
+<hkern u1="V" u2="&#x2c;" k="48" />
+<hkern u1="V" u2="-" k="48" />
+<hkern u1="V" u2="." k="48" />
+<hkern u1="V" u2=":" k="16" />
+<hkern u1="V" u2=";" k="16" />
+<hkern u1="V" u2="A" k="24" />
+<hkern u1="V" u2="J" k="40" />
+<hkern u1="V" u2="S" k="-24" />
+<hkern u1="V" u2="T" k="-16" />
+<hkern u1="V" u2="V" k="-16" />
+<hkern u1="V" u2="W" k="4" />
+<hkern u1="V" u2="X" k="-16" />
+<hkern u1="V" u2="Y" k="-24" />
+<hkern u1="V" u2="]" k="-24" />
+<hkern u1="V" u2="a" k="32" />
+<hkern u1="V" u2="b" k="8" />
+<hkern u1="V" u2="c" k="32" />
+<hkern u1="V" u2="d" k="32" />
+<hkern u1="V" u2="e" k="32" />
+<hkern u1="V" u2="f" k="8" />
+<hkern u1="V" u2="g" k="28" />
+<hkern u1="V" u2="h" k="8" />
+<hkern u1="V" u2="k" k="8" />
+<hkern u1="V" u2="l" k="8" />
+<hkern u1="V" u2="m" k="24" />
+<hkern u1="V" u2="n" k="24" />
+<hkern u1="V" u2="o" k="32" />
+<hkern u1="V" u2="p" k="24" />
+<hkern u1="V" u2="q" k="32" />
+<hkern u1="V" u2="r" k="24" />
+<hkern u1="V" u2="s" k="32" />
+<hkern u1="V" u2="t" k="8" />
+<hkern u1="V" u2="u" k="24" />
+<hkern u1="V" u2="v" k="8" />
+<hkern u1="V" u2="w" k="8" />
+<hkern u1="V" u2="x" k="20" />
+<hkern u1="V" u2="y" k="8" />
+<hkern u1="V" u2="}" k="-24" />
+<hkern u1="V" u2="&#xc4;" k="24" />
+<hkern u1="V" u2="&#xc5;" k="24" />
+<hkern u1="V" u2="&#xe7;" k="32" />
+<hkern u1="V" u2="&#xc6;" k="24" />
+<hkern u1="V" u2="&#xe6;" k="32" />
+<hkern u1="V" u2="&#xab;" k="40" />
+<hkern u1="V" u2="&#xbb;" k="16" />
+<hkern u1="V" u2="&#x2026;" k="48" />
+<hkern u1="V" u2="&#xc0;" k="24" />
+<hkern u1="V" u2="&#xc3;" k="24" />
+<hkern u1="V" u2="&#x153;" k="32" />
+<hkern u1="V" u2="&#x2013;" k="48" />
+<hkern u1="V" u2="&#x2014;" k="48" />
+<hkern u1="V" u2="&#x178;" k="-24" />
+<hkern u1="V" u2="&#x2039;" k="40" />
+<hkern u1="V" u2="&#x203a;" k="16" />
+<hkern u1="V" u2="&#x201a;" k="48" />
+<hkern u1="V" u2="&#x201e;" k="48" />
+<hkern u1="V" u2="&#xc2;" k="24" />
+<hkern u1="V" u2="&#xc1;" k="24" />
+<hkern u1="V" u2="&#x131;" k="24" />
+<hkern u1="W" u2="&#x160;" k="-16" />
+<hkern u1="W" u2="&#x161;" k="40" />
+<hkern u1="W" u2="&amp;" k="24" />
+<hkern u1="W" u2=")" k="-8" />
+<hkern u1="W" u2="&#x2c;" k="16" />
+<hkern u1="W" u2="-" k="32" />
+<hkern u1="W" u2="." k="16" />
+<hkern u1="W" u2=":" k="16" />
+<hkern u1="W" u2=";" k="16" />
+<hkern u1="W" u2="@" k="16" />
+<hkern u1="W" u2="A" k="24" />
+<hkern u1="W" u2="C" k="16" />
+<hkern u1="W" u2="G" k="16" />
+<hkern u1="W" u2="J" k="32" />
+<hkern u1="W" u2="O" k="16" />
+<hkern u1="W" u2="Q" k="16" />
+<hkern u1="W" u2="S" k="-16" />
+<hkern u1="W" u2="V" k="4" />
+<hkern u1="W" u2="X" k="8" />
+<hkern u1="W" u2="]" k="-8" />
+<hkern u1="W" u2="a" k="36" />
+<hkern u1="W" u2="b" k="8" />
+<hkern u1="W" u2="c" k="36" />
+<hkern u1="W" u2="d" k="36" />
+<hkern u1="W" u2="e" k="36" />
+<hkern u1="W" u2="f" k="12" />
+<hkern u1="W" u2="g" k="40" />
+<hkern u1="W" u2="h" k="8" />
+<hkern u1="W" u2="i" k="16" />
+<hkern u1="W" u2="j" k="16" />
+<hkern u1="W" u2="k" k="8" />
+<hkern u1="W" u2="l" k="8" />
+<hkern u1="W" u2="m" k="28" />
+<hkern u1="W" u2="n" k="28" />
+<hkern u1="W" u2="o" k="36" />
+<hkern u1="W" u2="p" k="28" />
+<hkern u1="W" u2="q" k="36" />
+<hkern u1="W" u2="r" k="28" />
+<hkern u1="W" u2="s" k="40" />
+<hkern u1="W" u2="t" k="24" />
+<hkern u1="W" u2="u" k="28" />
+<hkern u1="W" u2="v" k="24" />
+<hkern u1="W" u2="w" k="24" />
+<hkern u1="W" u2="x" k="24" />
+<hkern u1="W" u2="y" k="24" />
+<hkern u1="W" u2="z" k="24" />
+<hkern u1="W" u2="}" k="-8" />
+<hkern u1="W" u2="&#xc4;" k="24" />
+<hkern u1="W" u2="&#xc5;" k="24" />
+<hkern u1="W" u2="&#xd6;" k="16" />
+<hkern u1="W" u2="&#xe7;" k="36" />
+<hkern u1="W" u2="&#xae;" k="16" />
+<hkern u1="W" u2="&#xa9;" k="16" />
+<hkern u1="W" u2="&#xc6;" k="24" />
+<hkern u1="W" u2="&#xd8;" k="16" />
+<hkern u1="W" u2="&#x3c0;" k="16" />
+<hkern u1="W" u2="&#xe6;" k="36" />
+<hkern u1="W" u2="&#xab;" k="32" />
+<hkern u1="W" u2="&#xbb;" k="16" />
+<hkern u1="W" u2="&#x2026;" k="16" />
+<hkern u1="W" u2="&#xc0;" k="24" />
+<hkern u1="W" u2="&#xc3;" k="24" />
+<hkern u1="W" u2="&#xd5;" k="16" />
+<hkern u1="W" u2="&#x152;" k="16" />
+<hkern u1="W" u2="&#x153;" k="36" />
+<hkern u1="W" u2="&#x2013;" k="32" />
+<hkern u1="W" u2="&#x2014;" k="32" />
+<hkern u1="W" u2="&#x2039;" k="32" />
+<hkern u1="W" u2="&#x203a;" k="16" />
+<hkern u1="W" u2="&#x201a;" k="16" />
+<hkern u1="W" u2="&#x201e;" k="16" />
+<hkern u1="W" u2="&#xc2;" k="24" />
+<hkern u1="W" u2="&#xc1;" k="24" />
+<hkern u1="W" u2="&#xd3;" k="16" />
+<hkern u1="W" u2="&#xd4;" k="16" />
+<hkern u1="W" u2="&#xd2;" k="16" />
+<hkern u1="W" u2="&#x131;" k="28" />
+<hkern u1="X" u2="&#x160;" k="-4" />
+<hkern u1="X" u2="&#xdd;" k="-16" />
+<hkern u1="X" u2="&amp;" k="16" />
+<hkern u1="X" u2=")" k="-32" />
+<hkern u1="X" u2="-" k="24" />
+<hkern u1="X" u2="A" k="-16" />
+<hkern u1="X" u2="C" k="12" />
+<hkern u1="X" u2="G" k="12" />
+<hkern u1="X" u2="O" k="12" />
+<hkern u1="X" u2="Q" k="12" />
+<hkern u1="X" u2="S" k="-4" />
+<hkern u1="X" u2="T" k="-8" />
+<hkern u1="X" u2="V" k="-16" />
+<hkern u1="X" u2="W" k="8" />
+<hkern u1="X" u2="X" k="-8" />
+<hkern u1="X" u2="Y" k="-16" />
+<hkern u1="X" u2="]" k="-32" />
+<hkern u1="X" u2="a" k="8" />
+<hkern u1="X" u2="c" k="8" />
+<hkern u1="X" u2="d" k="8" />
+<hkern u1="X" u2="e" k="8" />
+<hkern u1="X" u2="f" k="8" />
+<hkern u1="X" u2="m" k="8" />
+<hkern u1="X" u2="n" k="8" />
+<hkern u1="X" u2="o" k="8" />
+<hkern u1="X" u2="p" k="8" />
+<hkern u1="X" u2="q" k="8" />
+<hkern u1="X" u2="r" k="8" />
+<hkern u1="X" u2="t" k="8" />
+<hkern u1="X" u2="u" k="8" />
+<hkern u1="X" u2="v" k="-8" />
+<hkern u1="X" u2="w" k="8" />
+<hkern u1="X" u2="y" k="-8" />
+<hkern u1="X" u2="}" k="-32" />
+<hkern u1="X" u2="&#xc4;" k="-16" />
+<hkern u1="X" u2="&#xc5;" k="-16" />
+<hkern u1="X" u2="&#xd6;" k="12" />
+<hkern u1="X" u2="&#xe7;" k="8" />
+<hkern u1="X" u2="&#xc6;" k="-16" />
+<hkern u1="X" u2="&#xd8;" k="12" />
+<hkern u1="X" u2="&#xe6;" k="8" />
+<hkern u1="X" u2="&#xc0;" k="-16" />
+<hkern u1="X" u2="&#xc3;" k="-16" />
+<hkern u1="X" u2="&#xd5;" k="12" />
+<hkern u1="X" u2="&#x152;" k="12" />
+<hkern u1="X" u2="&#x153;" k="8" />
+<hkern u1="X" u2="&#x2013;" k="24" />
+<hkern u1="X" u2="&#x2014;" k="24" />
+<hkern u1="X" u2="&#x178;" k="-16" />
+<hkern u1="X" u2="&#xc2;" k="-16" />
+<hkern u1="X" u2="&#xc1;" k="-16" />
+<hkern u1="X" u2="&#xd3;" k="12" />
+<hkern u1="X" u2="&#xd4;" k="12" />
+<hkern u1="X" u2="&#xd2;" k="12" />
+<hkern u1="X" u2="&#x131;" k="8" />
+<hkern u1="Y" u2="&#x160;" k="-24" />
+<hkern u1="Y" u2="&#x161;" k="40" />
+<hkern u1="Y" u2="&amp;" k="24" />
+<hkern u1="Y" u2=")" k="-40" />
+<hkern u1="Y" u2="&#x2c;" k="40" />
+<hkern u1="Y" u2="-" k="40" />
+<hkern u1="Y" u2="." k="40" />
+<hkern u1="Y" u2=":" k="24" />
+<hkern u1="Y" u2=";" k="24" />
+<hkern u1="Y" u2="@" k="24" />
+<hkern u1="Y" u2="A" k="24" />
+<hkern u1="Y" u2="C" k="4" />
+<hkern u1="Y" u2="G" k="4" />
+<hkern u1="Y" u2="J" k="56" />
+<hkern u1="Y" u2="O" k="4" />
+<hkern u1="Y" u2="Q" k="4" />
+<hkern u1="Y" u2="S" k="-24" />
+<hkern u1="Y" u2="T" k="-16" />
+<hkern u1="Y" u2="V" k="-24" />
+<hkern u1="Y" u2="X" k="-16" />
+<hkern u1="Y" u2="]" k="-40" />
+<hkern u1="Y" u2="a" k="44" />
+<hkern u1="Y" u2="c" k="44" />
+<hkern u1="Y" u2="d" k="44" />
+<hkern u1="Y" u2="e" k="44" />
+<hkern u1="Y" u2="f" k="8" />
+<hkern u1="Y" u2="g" k="32" />
+<hkern u1="Y" u2="m" k="32" />
+<hkern u1="Y" u2="n" k="32" />
+<hkern u1="Y" u2="o" k="44" />
+<hkern u1="Y" u2="p" k="32" />
+<hkern u1="Y" u2="q" k="44" />
+<hkern u1="Y" u2="r" k="32" />
+<hkern u1="Y" u2="s" k="40" />
+<hkern u1="Y" u2="t" k="16" />
+<hkern u1="Y" u2="u" k="32" />
+<hkern u1="Y" u2="v" k="16" />
+<hkern u1="Y" u2="w" k="8" />
+<hkern u1="Y" u2="x" k="16" />
+<hkern u1="Y" u2="y" k="16" />
+<hkern u1="Y" u2="z" k="24" />
+<hkern u1="Y" u2="}" k="-40" />
+<hkern u1="Y" u2="&#xc4;" k="24" />
+<hkern u1="Y" u2="&#xc5;" k="24" />
+<hkern u1="Y" u2="&#xd6;" k="4" />
+<hkern u1="Y" u2="&#xe7;" k="44" />
+<hkern u1="Y" u2="&#xae;" k="24" />
+<hkern u1="Y" u2="&#xa9;" k="24" />
+<hkern u1="Y" u2="&#xc6;" k="24" />
+<hkern u1="Y" u2="&#xd8;" k="4" />
+<hkern u1="Y" u2="&#x3c0;" k="24" />
+<hkern u1="Y" u2="&#xe6;" k="44" />
+<hkern u1="Y" u2="&#xab;" k="48" />
+<hkern u1="Y" u2="&#xbb;" k="32" />
+<hkern u1="Y" u2="&#x2026;" k="40" />
+<hkern u1="Y" u2="&#xc0;" k="24" />
+<hkern u1="Y" u2="&#xc3;" k="24" />
+<hkern u1="Y" u2="&#xd5;" k="4" />
+<hkern u1="Y" u2="&#x152;" k="4" />
+<hkern u1="Y" u2="&#x153;" k="44" />
+<hkern u1="Y" u2="&#x2013;" k="40" />
+<hkern u1="Y" u2="&#x2014;" k="40" />
+<hkern u1="Y" u2="&#x2039;" k="48" />
+<hkern u1="Y" u2="&#x203a;" k="32" />
+<hkern u1="Y" u2="&#x201a;" k="40" />
+<hkern u1="Y" u2="&#x201e;" k="40" />
+<hkern u1="Y" u2="&#xc2;" k="24" />
+<hkern u1="Y" u2="&#xc1;" k="24" />
+<hkern u1="Y" u2="&#xd3;" k="4" />
+<hkern u1="Y" u2="&#xd4;" k="4" />
+<hkern u1="Y" u2="&#xd2;" k="4" />
+<hkern u1="Y" u2="&#x131;" k="32" />
+<hkern u1="Z" u2="&#xf0;" k="24" />
+<hkern u1="Z" u2="-" k="40" />
+<hkern u1="Z" u2="A" k="8" />
+<hkern u1="Z" u2="C" k="36" />
+<hkern u1="Z" u2="G" k="36" />
+<hkern u1="Z" u2="J" k="8" />
+<hkern u1="Z" u2="O" k="36" />
+<hkern u1="Z" u2="Q" k="36" />
+<hkern u1="Z" u2="T" k="8" />
+<hkern u1="Z" u2="a" k="32" />
+<hkern u1="Z" u2="b" k="8" />
+<hkern u1="Z" u2="c" k="32" />
+<hkern u1="Z" u2="d" k="32" />
+<hkern u1="Z" u2="e" k="32" />
+<hkern u1="Z" u2="f" k="16" />
+<hkern u1="Z" u2="g" k="16" />
+<hkern u1="Z" u2="h" k="8" />
+<hkern u1="Z" u2="i" k="16" />
+<hkern u1="Z" u2="j" k="12" />
+<hkern u1="Z" u2="k" k="8" />
+<hkern u1="Z" u2="l" k="8" />
+<hkern u1="Z" u2="m" k="16" />
+<hkern u1="Z" u2="n" k="16" />
+<hkern u1="Z" u2="o" k="32" />
+<hkern u1="Z" u2="p" k="16" />
+<hkern u1="Z" u2="q" k="32" />
+<hkern u1="Z" u2="r" k="16" />
+<hkern u1="Z" u2="u" k="16" />
+<hkern u1="Z" u2="v" k="24" />
+<hkern u1="Z" u2="w" k="8" />
+<hkern u1="Z" u2="y" k="24" />
+<hkern u1="Z" u2="&#xc4;" k="8" />
+<hkern u1="Z" u2="&#xc5;" k="8" />
+<hkern u1="Z" u2="&#xd6;" k="36" />
+<hkern u1="Z" u2="&#xe7;" k="32" />
+<hkern u1="Z" u2="&#xc6;" k="8" />
+<hkern u1="Z" u2="&#xd8;" k="36" />
+<hkern u1="Z" u2="&#xe6;" k="32" />
+<hkern u1="Z" u2="&#xc0;" k="8" />
+<hkern u1="Z" u2="&#xc3;" k="8" />
+<hkern u1="Z" u2="&#xd5;" k="36" />
+<hkern u1="Z" u2="&#x152;" k="36" />
+<hkern u1="Z" u2="&#x153;" k="32" />
+<hkern u1="Z" u2="&#x2013;" k="40" />
+<hkern u1="Z" u2="&#x2014;" k="40" />
+<hkern u1="Z" u2="&#xc2;" k="8" />
+<hkern u1="Z" u2="&#xc1;" k="8" />
+<hkern u1="Z" u2="&#xd3;" k="36" />
+<hkern u1="Z" u2="&#xd4;" k="36" />
+<hkern u1="Z" u2="&#xd2;" k="36" />
+<hkern u1="Z" u2="&#x131;" k="16" />
+<hkern u1="[" u2="&#xdd;" k="-40" />
+<hkern u1="[" u2="V" k="-24" />
+<hkern u1="[" u2="W" k="-8" />
+<hkern u1="[" u2="X" k="-32" />
+<hkern u1="[" u2="Y" k="-40" />
+<hkern u1="[" u2="g" k="-16" />
+<hkern u1="[" u2="j" k="-120" />
+<hkern u1="[" u2="&#x178;" k="-40" />
+<hkern u1="\" u2="&#xdd;" k="48" />
+<hkern u1="\" u2="T" k="72" />
+<hkern u1="\" u2="V" k="56" />
+<hkern u1="\" u2="W" k="40" />
+<hkern u1="\" u2="Y" k="48" />
+<hkern u1="\" u2="a" k="8" />
+<hkern u1="\" u2="c" k="8" />
+<hkern u1="\" u2="d" k="8" />
+<hkern u1="\" u2="e" k="8" />
+<hkern u1="\" u2="j" k="-104" />
+<hkern u1="\" u2="o" k="8" />
+<hkern u1="\" u2="q" k="8" />
+<hkern u1="\" u2="&#xe7;" k="8" />
+<hkern u1="\" u2="&#xe6;" k="8" />
+<hkern u1="\" u2="&#x153;" k="8" />
+<hkern u1="\" u2="&#x178;" k="48" />
+<hkern u1="a" u2="?" k="8" />
+<hkern u1="a" u2="\" k="8" />
+<hkern u1="a" u2="t" k="8" />
+<hkern u1="a" u2="v" k="4" />
+<hkern u1="a" u2="y" k="4" />
+<hkern u1="a" u2="&#x2122;" k="16" />
+<hkern u1="a" u2="&#x201c;" k="24" />
+<hkern u1="a" u2="&#x2018;" k="24" />
+<hkern u1="b" u2="&#x161;" k="-4" />
+<hkern u1="b" u2="&#x2c;" k="16" />
+<hkern u1="b" u2="." k="16" />
+<hkern u1="b" u2="/" k="8" />
+<hkern u1="b" u2=":" k="8" />
+<hkern u1="b" u2=";" k="8" />
+<hkern u1="b" u2="?" k="16" />
+<hkern u1="b" u2="\" k="8" />
+<hkern u1="b" u2="s" k="-4" />
+<hkern u1="b" u2="t" k="8" />
+<hkern u1="b" u2="v" k="-4" />
+<hkern u1="b" u2="x" k="4" />
+<hkern u1="b" u2="y" k="-4" />
+<hkern u1="b" u2="z" k="12" />
+<hkern u1="b" u2="&#x2122;" k="24" />
+<hkern u1="b" u2="&#x2026;" k="16" />
+<hkern u1="b" u2="&#x201c;" k="32" />
+<hkern u1="b" u2="&#x2018;" k="32" />
+<hkern u1="b" u2="&#x201a;" k="16" />
+<hkern u1="b" u2="&#x201e;" k="16" />
+<hkern u1="d" u2="v" k="-8" />
+<hkern u1="d" u2="y" k="-8" />
+<hkern u1="e" u2="v" k="8" />
+<hkern u1="e" u2="x" k="12" />
+<hkern u1="e" u2="y" k="8" />
+<hkern u1="f" u2="&#xf0;" k="16" />
+<hkern u1="f" u2="&amp;" k="24" />
+<hkern u1="f" u2=")" k="-56" />
+<hkern u1="f" u2="*" k="-16" />
+<hkern u1="f" u2="&#x2c;" k="64" />
+<hkern u1="f" u2="-" k="24" />
+<hkern u1="f" u2="." k="64" />
+<hkern u1="f" u2="/" k="40" />
+<hkern u1="f" u2=":" k="8" />
+<hkern u1="f" u2=";" k="8" />
+<hkern u1="f" u2="?" k="-32" />
+<hkern u1="f" u2="\" k="-32" />
+<hkern u1="f" u2="]" k="-56" />
+<hkern u1="f" u2="a" k="8" />
+<hkern u1="f" u2="c" k="8" />
+<hkern u1="f" u2="d" k="8" />
+<hkern u1="f" u2="e" k="8" />
+<hkern u1="f" u2="m" k="4" />
+<hkern u1="f" u2="n" k="4" />
+<hkern u1="f" u2="o" k="8" />
+<hkern u1="f" u2="p" k="4" />
+<hkern u1="f" u2="q" k="8" />
+<hkern u1="f" u2="r" k="4" />
+<hkern u1="f" u2="u" k="4" />
+<hkern u1="f" u2="v" k="-16" />
+<hkern u1="f" u2="y" k="-16" />
+<hkern u1="f" u2="}" k="-56" />
+<hkern u1="f" u2="&#xe7;" k="8" />
+<hkern u1="f" u2="&#x2122;" k="-32" />
+<hkern u1="f" u2="&#xe6;" k="8" />
+<hkern u1="f" u2="&#xab;" k="40" />
+<hkern u1="f" u2="&#xbb;" k="16" />
+<hkern u1="f" u2="&#x2026;" k="64" />
+<hkern u1="f" u2="&#x153;" k="8" />
+<hkern u1="f" u2="&#x2013;" k="24" />
+<hkern u1="f" u2="&#x2014;" k="24" />
+<hkern u1="f" u2="&#x201c;" k="-24" />
+<hkern u1="f" u2="&#x201d;" k="-40" />
+<hkern u1="f" u2="&#x2018;" k="-24" />
+<hkern u1="f" u2="&#x2019;" k="-40" />
+<hkern u1="f" u2="&#x2039;" k="40" />
+<hkern u1="f" u2="&#x203a;" k="16" />
+<hkern u1="f" u2="&#x201a;" k="64" />
+<hkern u1="f" u2="&#x201e;" k="64" />
+<hkern u1="f" u2="&#x131;" k="4" />
+<hkern u1="g" u2=")" k="-16" />
+<hkern u1="g" u2="/" k="-32" />
+<hkern u1="g" u2=";" k="-16" />
+<hkern u1="g" u2="]" k="-16" />
+<hkern u1="g" u2="g" k="-8" />
+<hkern u1="g" u2="j" k="-40" />
+<hkern u1="g" u2="t" k="-8" />
+<hkern u1="g" u2="}" k="-16" />
+<hkern u1="g" u2="&#x201c;" k="-16" />
+<hkern u1="g" u2="&#x201d;" k="-32" />
+<hkern u1="g" u2="&#x2018;" k="-16" />
+<hkern u1="g" u2="&#x2019;" k="-32" />
+<hkern u1="h" u2="?" k="8" />
+<hkern u1="h" u2="\" k="8" />
+<hkern u1="h" u2="t" k="8" />
+<hkern u1="h" u2="v" k="4" />
+<hkern u1="h" u2="y" k="4" />
+<hkern u1="h" u2="&#x2122;" k="16" />
+<hkern u1="h" u2="&#x201c;" k="24" />
+<hkern u1="h" u2="&#x2018;" k="24" />
+<hkern u1="j" u2="j" k="-16" />
+<hkern u1="j" u2="v" k="-16" />
+<hkern u1="j" u2="y" k="-16" />
+<hkern u1="k" u2="a" k="8" />
+<hkern u1="k" u2="c" k="8" />
+<hkern u1="k" u2="d" k="8" />
+<hkern u1="k" u2="e" k="8" />
+<hkern u1="k" u2="g" k="16" />
+<hkern u1="k" u2="o" k="8" />
+<hkern u1="k" u2="q" k="8" />
+<hkern u1="k" u2="v" k="-8" />
+<hkern u1="k" u2="y" k="-8" />
+<hkern u1="k" u2="&#xe7;" k="8" />
+<hkern u1="k" u2="&#xe6;" k="8" />
+<hkern u1="k" u2="&#x153;" k="8" />
+<hkern u1="l" u2="v" k="-8" />
+<hkern u1="l" u2="y" k="-8" />
+<hkern u1="m" u2="?" k="8" />
+<hkern u1="m" u2="\" k="8" />
+<hkern u1="m" u2="t" k="8" />
+<hkern u1="m" u2="v" k="4" />
+<hkern u1="m" u2="y" k="4" />
+<hkern u1="m" u2="&#x2122;" k="16" />
+<hkern u1="m" u2="&#x201c;" k="24" />
+<hkern u1="m" u2="&#x2018;" k="24" />
+<hkern u1="n" u2="?" k="8" />
+<hkern u1="n" u2="\" k="8" />
+<hkern u1="n" u2="t" k="8" />
+<hkern u1="n" u2="v" k="4" />
+<hkern u1="n" u2="y" k="4" />
+<hkern u1="n" u2="&#x2122;" k="16" />
+<hkern u1="n" u2="&#x201c;" k="24" />
+<hkern u1="n" u2="&#x2018;" k="24" />
+<hkern u1="o" u2="&#x161;" k="-4" />
+<hkern u1="o" u2="&#x2c;" k="16" />
+<hkern u1="o" u2="." k="16" />
+<hkern u1="o" u2="/" k="8" />
+<hkern u1="o" u2=":" k="8" />
+<hkern u1="o" u2=";" k="8" />
+<hkern u1="o" u2="?" k="16" />
+<hkern u1="o" u2="\" k="8" />
+<hkern u1="o" u2="s" k="-4" />
+<hkern u1="o" u2="t" k="8" />
+<hkern u1="o" u2="v" k="-4" />
+<hkern u1="o" u2="x" k="4" />
+<hkern u1="o" u2="y" k="-4" />
+<hkern u1="o" u2="z" k="12" />
+<hkern u1="o" u2="&#x2122;" k="24" />
+<hkern u1="o" u2="&#x2026;" k="16" />
+<hkern u1="o" u2="&#x201c;" k="32" />
+<hkern u1="o" u2="&#x2018;" k="32" />
+<hkern u1="o" u2="&#x201a;" k="16" />
+<hkern u1="o" u2="&#x201e;" k="16" />
+<hkern u1="p" u2="&#x161;" k="-4" />
+<hkern u1="p" u2="&#x2c;" k="16" />
+<hkern u1="p" u2="." k="16" />
+<hkern u1="p" u2="/" k="8" />
+<hkern u1="p" u2=":" k="8" />
+<hkern u1="p" u2=";" k="8" />
+<hkern u1="p" u2="?" k="16" />
+<hkern u1="p" u2="\" k="8" />
+<hkern u1="p" u2="s" k="-4" />
+<hkern u1="p" u2="t" k="8" />
+<hkern u1="p" u2="v" k="-4" />
+<hkern u1="p" u2="x" k="4" />
+<hkern u1="p" u2="y" k="-4" />
+<hkern u1="p" u2="z" k="12" />
+<hkern u1="p" u2="&#x2122;" k="24" />
+<hkern u1="p" u2="&#x2026;" k="16" />
+<hkern u1="p" u2="&#x201c;" k="32" />
+<hkern u1="p" u2="&#x2018;" k="32" />
+<hkern u1="p" u2="&#x201a;" k="16" />
+<hkern u1="p" u2="&#x201e;" k="16" />
+<hkern u1="q" u2="j" k="-24" />
+<hkern u1="r" u2="&#x2c;" k="40" />
+<hkern u1="r" u2="-" k="16" />
+<hkern u1="r" u2="." k="40" />
+<hkern u1="r" u2="/" k="48" />
+<hkern u1="r" u2="?" k="-16" />
+<hkern u1="r" u2="f" k="-12" />
+<hkern u1="r" u2="g" k="8" />
+<hkern u1="r" u2="t" k="-16" />
+<hkern u1="r" u2="v" k="-32" />
+<hkern u1="r" u2="w" k="-24" />
+<hkern u1="r" u2="x" k="-8" />
+<hkern u1="r" u2="y" k="-32" />
+<hkern u1="r" u2="&#x2026;" k="40" />
+<hkern u1="r" u2="&#x2013;" k="16" />
+<hkern u1="r" u2="&#x2014;" k="16" />
+<hkern u1="r" u2="&#x201c;" k="-16" />
+<hkern u1="r" u2="&#x201d;" k="-32" />
+<hkern u1="r" u2="&#x2018;" k="-16" />
+<hkern u1="r" u2="&#x2019;" k="-32" />
+<hkern u1="r" u2="&#x201a;" k="40" />
+<hkern u1="r" u2="&#x201e;" k="40" />
+<hkern u1="t" u2="&#x2c;" k="-8" />
+<hkern u1="t" u2="." k="-8" />
+<hkern u1="t" u2="a" k="4" />
+<hkern u1="t" u2="c" k="4" />
+<hkern u1="t" u2="d" k="4" />
+<hkern u1="t" u2="e" k="4" />
+<hkern u1="t" u2="o" k="4" />
+<hkern u1="t" u2="q" k="4" />
+<hkern u1="t" u2="v" k="-12" />
+<hkern u1="t" u2="x" k="-4" />
+<hkern u1="t" u2="y" k="-12" />
+<hkern u1="t" u2="&#xe7;" k="4" />
+<hkern u1="t" u2="&#xe6;" k="4" />
+<hkern u1="t" u2="&#x2026;" k="-8" />
+<hkern u1="t" u2="&#x153;" k="4" />
+<hkern u1="t" u2="&#x201c;" k="-8" />
+<hkern u1="t" u2="&#x2018;" k="-8" />
+<hkern u1="t" u2="&#x201a;" k="-8" />
+<hkern u1="t" u2="&#x201e;" k="-8" />
+<hkern u1="u" u2="v" k="-8" />
+<hkern u1="u" u2="y" k="-8" />
+<hkern u1="v" u2="&#x2c;" k="40" />
+<hkern u1="v" u2="-" k="8" />
+<hkern u1="v" u2="." k="40" />
+<hkern u1="v" u2="a" k="8" />
+<hkern u1="v" u2="c" k="8" />
+<hkern u1="v" u2="d" k="8" />
+<hkern u1="v" u2="e" k="8" />
+<hkern u1="v" u2="o" k="8" />
+<hkern u1="v" u2="q" k="8" />
+<hkern u1="v" u2="v" k="-24" />
+<hkern u1="v" u2="w" k="-8" />
+<hkern u1="v" u2="y" k="-24" />
+<hkern u1="v" u2="&#xe7;" k="8" />
+<hkern u1="v" u2="&#xe6;" k="8" />
+<hkern u1="v" u2="&#x2026;" k="40" />
+<hkern u1="v" u2="&#x153;" k="8" />
+<hkern u1="v" u2="&#x2013;" k="8" />
+<hkern u1="v" u2="&#x2014;" k="8" />
+<hkern u1="v" u2="&#x201c;" k="-24" />
+<hkern u1="v" u2="&#x201d;" k="-24" />
+<hkern u1="v" u2="&#x2018;" k="-24" />
+<hkern u1="v" u2="&#x2019;" k="-24" />
+<hkern u1="v" u2="&#x201a;" k="40" />
+<hkern u1="v" u2="&#x201e;" k="40" />
+<hkern u1="w" u2="&#x2c;" k="24" />
+<hkern u1="w" u2="." k="24" />
+<hkern u1="w" u2="v" k="-8" />
+<hkern u1="w" u2="y" k="-8" />
+<hkern u1="w" u2="&#x2026;" k="24" />
+<hkern u1="w" u2="&#x201c;" k="-16" />
+<hkern u1="w" u2="&#x201d;" k="-16" />
+<hkern u1="w" u2="&#x2018;" k="-16" />
+<hkern u1="w" u2="&#x2019;" k="-16" />
+<hkern u1="w" u2="&#x201a;" k="24" />
+<hkern u1="w" u2="&#x201e;" k="24" />
+<hkern u1="x" u2="-" k="24" />
+<hkern u1="x" u2="a" k="12" />
+<hkern u1="x" u2="c" k="12" />
+<hkern u1="x" u2="d" k="12" />
+<hkern u1="x" u2="e" k="12" />
+<hkern u1="x" u2="o" k="12" />
+<hkern u1="x" u2="q" k="12" />
+<hkern u1="x" u2="v" k="-16" />
+<hkern u1="x" u2="y" k="-16" />
+<hkern u1="x" u2="&#xe7;" k="12" />
+<hkern u1="x" u2="&#xe6;" k="12" />
+<hkern u1="x" u2="&#xab;" k="40" />
+<hkern u1="x" u2="&#x153;" k="12" />
+<hkern u1="x" u2="&#x2013;" k="24" />
+<hkern u1="x" u2="&#x2014;" k="24" />
+<hkern u1="x" u2="&#x201c;" k="-8" />
+<hkern u1="x" u2="&#x201d;" k="-16" />
+<hkern u1="x" u2="&#x2018;" k="-8" />
+<hkern u1="x" u2="&#x2019;" k="-16" />
+<hkern u1="x" u2="&#x2039;" k="40" />
+<hkern u1="y" u2="&#x2c;" k="40" />
+<hkern u1="y" u2="-" k="8" />
+<hkern u1="y" u2="." k="40" />
+<hkern u1="y" u2="a" k="8" />
+<hkern u1="y" u2="c" k="8" />
+<hkern u1="y" u2="d" k="8" />
+<hkern u1="y" u2="e" k="8" />
+<hkern u1="y" u2="o" k="8" />
+<hkern u1="y" u2="q" k="8" />
+<hkern u1="y" u2="v" k="-24" />
+<hkern u1="y" u2="w" k="-8" />
+<hkern u1="y" u2="y" k="-24" />
+<hkern u1="y" u2="&#xe7;" k="8" />
+<hkern u1="y" u2="&#xe6;" k="8" />
+<hkern u1="y" u2="&#x2026;" k="40" />
+<hkern u1="y" u2="&#x153;" k="8" />
+<hkern u1="y" u2="&#x2013;" k="8" />
+<hkern u1="y" u2="&#x2014;" k="8" />
+<hkern u1="y" u2="&#x201c;" k="-24" />
+<hkern u1="y" u2="&#x201d;" k="-24" />
+<hkern u1="y" u2="&#x2018;" k="-24" />
+<hkern u1="y" u2="&#x2019;" k="-24" />
+<hkern u1="y" u2="&#x201a;" k="40" />
+<hkern u1="y" u2="&#x201e;" k="40" />
+<hkern u1="z" u2="-" k="24" />
+<hkern u1="z" u2="a" k="16" />
+<hkern u1="z" u2="c" k="16" />
+<hkern u1="z" u2="d" k="16" />
+<hkern u1="z" u2="e" k="16" />
+<hkern u1="z" u2="g" k="8" />
+<hkern u1="z" u2="m" k="12" />
+<hkern u1="z" u2="n" k="12" />
+<hkern u1="z" u2="o" k="16" />
+<hkern u1="z" u2="p" k="12" />
+<hkern u1="z" u2="q" k="16" />
+<hkern u1="z" u2="r" k="12" />
+<hkern u1="z" u2="u" k="12" />
+<hkern u1="z" u2="v" k="-8" />
+<hkern u1="z" u2="y" k="-8" />
+<hkern u1="z" u2="&#xe7;" k="16" />
+<hkern u1="z" u2="&#xe6;" k="16" />
+<hkern u1="z" u2="&#xab;" k="48" />
+<hkern u1="z" u2="&#x153;" k="16" />
+<hkern u1="z" u2="&#x2013;" k="24" />
+<hkern u1="z" u2="&#x2014;" k="24" />
+<hkern u1="z" u2="&#x2039;" k="48" />
+<hkern u1="z" u2="&#x131;" k="12" />
+<hkern u1="{" u2="&#xdd;" k="-40" />
+<hkern u1="{" u2="V" k="-24" />
+<hkern u1="{" u2="W" k="-8" />
+<hkern u1="{" u2="X" k="-32" />
+<hkern u1="{" u2="Y" k="-40" />
+<hkern u1="{" u2="g" k="-16" />
+<hkern u1="{" u2="j" k="-120" />
+<hkern u1="{" u2="&#x178;" k="-40" />
+<hkern u1="&#xc4;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc4;" u2="&#xdd;" k="56" />
+<hkern u1="&#xc4;" u2="*" k="48" />
+<hkern u1="&#xc4;" u2="&#x2c;" k="-24" />
+<hkern u1="&#xc4;" u2="-" k="-8" />
+<hkern u1="&#xc4;" u2="." k="-24" />
+<hkern u1="&#xc4;" u2="@" k="8" />
+<hkern u1="&#xc4;" u2="A" k="-8" />
+<hkern u1="&#xc4;" u2="C" k="32" />
+<hkern u1="&#xc4;" u2="G" k="32" />
+<hkern u1="&#xc4;" u2="J" k="8" />
+<hkern u1="&#xc4;" u2="O" k="32" />
+<hkern u1="&#xc4;" u2="Q" k="32" />
+<hkern u1="&#xc4;" u2="S" k="-4" />
+<hkern u1="&#xc4;" u2="T" k="72" />
+<hkern u1="&#xc4;" u2="V" k="56" />
+<hkern u1="&#xc4;" u2="W" k="56" />
+<hkern u1="&#xc4;" u2="Y" k="56" />
+<hkern u1="&#xc4;" u2="a" k="4" />
+<hkern u1="&#xc4;" u2="c" k="4" />
+<hkern u1="&#xc4;" u2="d" k="4" />
+<hkern u1="&#xc4;" u2="e" k="4" />
+<hkern u1="&#xc4;" u2="m" k="4" />
+<hkern u1="&#xc4;" u2="n" k="4" />
+<hkern u1="&#xc4;" u2="o" k="4" />
+<hkern u1="&#xc4;" u2="p" k="4" />
+<hkern u1="&#xc4;" u2="q" k="4" />
+<hkern u1="&#xc4;" u2="r" k="4" />
+<hkern u1="&#xc4;" u2="t" k="4" />
+<hkern u1="&#xc4;" u2="u" k="4" />
+<hkern u1="&#xc4;" u2="v" k="24" />
+<hkern u1="&#xc4;" u2="w" k="8" />
+<hkern u1="&#xc4;" u2="x" k="-16" />
+<hkern u1="&#xc4;" u2="y" k="24" />
+<hkern u1="&#xc4;" u2="z" k="-8" />
+<hkern u1="&#xc4;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xd6;" k="32" />
+<hkern u1="&#xc4;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc4;" u2="&#xae;" k="8" />
+<hkern u1="&#xc4;" u2="&#xa9;" k="8" />
+<hkern u1="&#xc4;" u2="&#x2122;" k="56" />
+<hkern u1="&#xc4;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xd8;" k="32" />
+<hkern u1="&#xc4;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xc4;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc4;" u2="&#x2026;" k="-24" />
+<hkern u1="&#xc4;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xd5;" k="32" />
+<hkern u1="&#xc4;" u2="&#x152;" k="32" />
+<hkern u1="&#xc4;" u2="&#x153;" k="4" />
+<hkern u1="&#xc4;" u2="&#x2013;" k="-8" />
+<hkern u1="&#xc4;" u2="&#x2014;" k="-8" />
+<hkern u1="&#xc4;" u2="&#x201c;" k="48" />
+<hkern u1="&#xc4;" u2="&#x201d;" k="24" />
+<hkern u1="&#xc4;" u2="&#x2018;" k="48" />
+<hkern u1="&#xc4;" u2="&#x2019;" k="24" />
+<hkern u1="&#xc4;" u2="&#x178;" k="56" />
+<hkern u1="&#xc4;" u2="&#x201a;" k="-24" />
+<hkern u1="&#xc4;" u2="&#x201e;" k="-24" />
+<hkern u1="&#xc4;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xc4;" u2="&#xd3;" k="32" />
+<hkern u1="&#xc4;" u2="&#xd4;" k="32" />
+<hkern u1="&#xc4;" u2="&#xd2;" k="32" />
+<hkern u1="&#xc4;" u2="&#x131;" k="4" />
+<hkern u1="&#xc5;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc5;" u2="&#xdd;" k="56" />
+<hkern u1="&#xc5;" u2="*" k="48" />
+<hkern u1="&#xc5;" u2="&#x2c;" k="-24" />
+<hkern u1="&#xc5;" u2="-" k="-8" />
+<hkern u1="&#xc5;" u2="." k="-24" />
+<hkern u1="&#xc5;" u2="@" k="8" />
+<hkern u1="&#xc5;" u2="A" k="-8" />
+<hkern u1="&#xc5;" u2="C" k="32" />
+<hkern u1="&#xc5;" u2="G" k="32" />
+<hkern u1="&#xc5;" u2="J" k="8" />
+<hkern u1="&#xc5;" u2="O" k="32" />
+<hkern u1="&#xc5;" u2="Q" k="32" />
+<hkern u1="&#xc5;" u2="S" k="-4" />
+<hkern u1="&#xc5;" u2="T" k="72" />
+<hkern u1="&#xc5;" u2="V" k="56" />
+<hkern u1="&#xc5;" u2="W" k="56" />
+<hkern u1="&#xc5;" u2="Y" k="56" />
+<hkern u1="&#xc5;" u2="a" k="4" />
+<hkern u1="&#xc5;" u2="c" k="4" />
+<hkern u1="&#xc5;" u2="d" k="4" />
+<hkern u1="&#xc5;" u2="e" k="4" />
+<hkern u1="&#xc5;" u2="m" k="4" />
+<hkern u1="&#xc5;" u2="n" k="4" />
+<hkern u1="&#xc5;" u2="o" k="4" />
+<hkern u1="&#xc5;" u2="p" k="4" />
+<hkern u1="&#xc5;" u2="q" k="4" />
+<hkern u1="&#xc5;" u2="r" k="4" />
+<hkern u1="&#xc5;" u2="t" k="4" />
+<hkern u1="&#xc5;" u2="u" k="4" />
+<hkern u1="&#xc5;" u2="v" k="24" />
+<hkern u1="&#xc5;" u2="w" k="8" />
+<hkern u1="&#xc5;" u2="x" k="-16" />
+<hkern u1="&#xc5;" u2="y" k="24" />
+<hkern u1="&#xc5;" u2="z" k="-8" />
+<hkern u1="&#xc5;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xd6;" k="32" />
+<hkern u1="&#xc5;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc5;" u2="&#xae;" k="8" />
+<hkern u1="&#xc5;" u2="&#xa9;" k="8" />
+<hkern u1="&#xc5;" u2="&#x2122;" k="56" />
+<hkern u1="&#xc5;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xd8;" k="32" />
+<hkern u1="&#xc5;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xc5;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc5;" u2="&#x2026;" k="-24" />
+<hkern u1="&#xc5;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xd5;" k="32" />
+<hkern u1="&#xc5;" u2="&#x152;" k="32" />
+<hkern u1="&#xc5;" u2="&#x153;" k="4" />
+<hkern u1="&#xc5;" u2="&#x2013;" k="-8" />
+<hkern u1="&#xc5;" u2="&#x2014;" k="-8" />
+<hkern u1="&#xc5;" u2="&#x201c;" k="48" />
+<hkern u1="&#xc5;" u2="&#x201d;" k="24" />
+<hkern u1="&#xc5;" u2="&#x2018;" k="48" />
+<hkern u1="&#xc5;" u2="&#x2019;" k="24" />
+<hkern u1="&#xc5;" u2="&#x178;" k="56" />
+<hkern u1="&#xc5;" u2="&#x201a;" k="-24" />
+<hkern u1="&#xc5;" u2="&#x201e;" k="-24" />
+<hkern u1="&#xc5;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xc5;" u2="&#xd3;" k="32" />
+<hkern u1="&#xc5;" u2="&#xd4;" k="32" />
+<hkern u1="&#xc5;" u2="&#xd2;" k="32" />
+<hkern u1="&#xc5;" u2="&#x131;" k="4" />
+<hkern u1="&#xc9;" u2="@" k="16" />
+<hkern u1="&#xc9;" u2="T" k="-12" />
+<hkern u1="&#xc9;" u2="a" k="20" />
+<hkern u1="&#xc9;" u2="c" k="20" />
+<hkern u1="&#xc9;" u2="d" k="20" />
+<hkern u1="&#xc9;" u2="e" k="20" />
+<hkern u1="&#xc9;" u2="f" k="8" />
+<hkern u1="&#xc9;" u2="g" k="4" />
+<hkern u1="&#xc9;" u2="o" k="20" />
+<hkern u1="&#xc9;" u2="q" k="20" />
+<hkern u1="&#xc9;" u2="v" k="16" />
+<hkern u1="&#xc9;" u2="y" k="16" />
+<hkern u1="&#xc9;" u2="&#xe7;" k="20" />
+<hkern u1="&#xc9;" u2="&#xae;" k="16" />
+<hkern u1="&#xc9;" u2="&#xa9;" k="16" />
+<hkern u1="&#xc9;" u2="&#x3c0;" k="16" />
+<hkern u1="&#xc9;" u2="&#xe6;" k="20" />
+<hkern u1="&#xc9;" u2="&#x153;" k="20" />
+<hkern u1="&#xd1;" u2="/" k="16" />
+<hkern u1="&#xd1;" u2="v" k="-16" />
+<hkern u1="&#xd1;" u2="y" k="-16" />
+<hkern u1="&#xd6;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd6;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd6;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd6;" u2="." k="32" />
+<hkern u1="&#xd6;" u2="/" k="40" />
+<hkern u1="&#xd6;" u2="?" k="16" />
+<hkern u1="&#xd6;" u2="J" k="36" />
+<hkern u1="&#xd6;" u2="T" k="40" />
+<hkern u1="&#xd6;" u2="V" k="32" />
+<hkern u1="&#xd6;" u2="W" k="48" />
+<hkern u1="&#xd6;" u2="X" k="28" />
+<hkern u1="&#xd6;" u2="Y" k="36" />
+<hkern u1="&#xd6;" u2="Z" k="36" />
+<hkern u1="&#xd6;" u2="a" k="4" />
+<hkern u1="&#xd6;" u2="b" k="8" />
+<hkern u1="&#xd6;" u2="c" k="4" />
+<hkern u1="&#xd6;" u2="d" k="4" />
+<hkern u1="&#xd6;" u2="e" k="4" />
+<hkern u1="&#xd6;" u2="h" k="8" />
+<hkern u1="&#xd6;" u2="k" k="8" />
+<hkern u1="&#xd6;" u2="l" k="8" />
+<hkern u1="&#xd6;" u2="m" k="4" />
+<hkern u1="&#xd6;" u2="n" k="4" />
+<hkern u1="&#xd6;" u2="o" k="4" />
+<hkern u1="&#xd6;" u2="p" k="4" />
+<hkern u1="&#xd6;" u2="q" k="4" />
+<hkern u1="&#xd6;" u2="r" k="4" />
+<hkern u1="&#xd6;" u2="u" k="4" />
+<hkern u1="&#xd6;" u2="x" k="8" />
+<hkern u1="&#xd6;" u2="z" k="8" />
+<hkern u1="&#xd6;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd6;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd6;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd6;" u2="&#x153;" k="4" />
+<hkern u1="&#xd6;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd6;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd6;" u2="&#x178;" k="36" />
+<hkern u1="&#xd6;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd6;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd6;" u2="&#x131;" k="4" />
+<hkern u1="&#xdc;" u2="&#x2c;" k="8" />
+<hkern u1="&#xdc;" u2="." k="8" />
+<hkern u1="&#xdc;" u2="A" k="-16" />
+<hkern u1="&#xdc;" u2="J" k="8" />
+<hkern u1="&#xdc;" u2="v" k="-16" />
+<hkern u1="&#xdc;" u2="y" k="-16" />
+<hkern u1="&#xdc;" u2="&#xc4;" k="-16" />
+<hkern u1="&#xdc;" u2="&#xc5;" k="-16" />
+<hkern u1="&#xdc;" u2="&#xc6;" k="-16" />
+<hkern u1="&#xdc;" u2="&#x2026;" k="8" />
+<hkern u1="&#xdc;" u2="&#xc0;" k="-16" />
+<hkern u1="&#xdc;" u2="&#xc3;" k="-16" />
+<hkern u1="&#xdc;" u2="&#x201a;" k="8" />
+<hkern u1="&#xdc;" u2="&#x201e;" k="8" />
+<hkern u1="&#xdc;" u2="&#xc2;" k="-16" />
+<hkern u1="&#xdc;" u2="&#xc1;" k="-16" />
+<hkern u1="&#xe1;" u2="?" k="8" />
+<hkern u1="&#xe1;" u2="\" k="8" />
+<hkern u1="&#xe1;" u2="t" k="8" />
+<hkern u1="&#xe1;" u2="v" k="4" />
+<hkern u1="&#xe1;" u2="y" k="4" />
+<hkern u1="&#xe1;" u2="&#x2122;" k="16" />
+<hkern u1="&#xe1;" u2="&#x201c;" k="24" />
+<hkern u1="&#xe1;" u2="&#x2018;" k="24" />
+<hkern u1="&#xe0;" u2="?" k="8" />
+<hkern u1="&#xe0;" u2="\" k="8" />
+<hkern u1="&#xe0;" u2="t" k="8" />
+<hkern u1="&#xe0;" u2="v" k="4" />
+<hkern u1="&#xe0;" u2="y" k="4" />
+<hkern u1="&#xe0;" u2="&#x2122;" k="16" />
+<hkern u1="&#xe0;" u2="&#x201c;" k="24" />
+<hkern u1="&#xe0;" u2="&#x2018;" k="24" />
+<hkern u1="&#xe2;" u2="?" k="8" />
+<hkern u1="&#xe2;" u2="\" k="8" />
+<hkern u1="&#xe2;" u2="t" k="8" />
+<hkern u1="&#xe2;" u2="v" k="4" />
+<hkern u1="&#xe2;" u2="y" k="4" />
+<hkern u1="&#xe2;" u2="&#x2122;" k="16" />
+<hkern u1="&#xe2;" u2="&#x201c;" k="24" />
+<hkern u1="&#xe2;" u2="&#x2018;" k="24" />
+<hkern u1="&#xe4;" u2="?" k="8" />
+<hkern u1="&#xe4;" u2="\" k="8" />
+<hkern u1="&#xe4;" u2="t" k="8" />
+<hkern u1="&#xe4;" u2="v" k="4" />
+<hkern u1="&#xe4;" u2="y" k="4" />
+<hkern u1="&#xe4;" u2="&#x2122;" k="16" />
+<hkern u1="&#xe4;" u2="&#x201c;" k="24" />
+<hkern u1="&#xe4;" u2="&#x2018;" k="24" />
+<hkern u1="&#xe9;" u2="v" k="8" />
+<hkern u1="&#xe9;" u2="x" k="12" />
+<hkern u1="&#xe9;" u2="y" k="8" />
+<hkern u1="&#xe8;" u2="v" k="8" />
+<hkern u1="&#xe8;" u2="x" k="12" />
+<hkern u1="&#xe8;" u2="y" k="8" />
+<hkern u1="&#xea;" u2="v" k="8" />
+<hkern u1="&#xea;" u2="x" k="12" />
+<hkern u1="&#xea;" u2="y" k="8" />
+<hkern u1="&#xeb;" u2="v" k="8" />
+<hkern u1="&#xeb;" u2="x" k="12" />
+<hkern u1="&#xeb;" u2="y" k="8" />
+<hkern u1="&#xf1;" u2="?" k="8" />
+<hkern u1="&#xf1;" u2="\" k="8" />
+<hkern u1="&#xf1;" u2="t" k="8" />
+<hkern u1="&#xf1;" u2="v" k="4" />
+<hkern u1="&#xf1;" u2="y" k="4" />
+<hkern u1="&#xf1;" u2="&#x2122;" k="16" />
+<hkern u1="&#xf1;" u2="&#x201c;" k="24" />
+<hkern u1="&#xf1;" u2="&#x2018;" k="24" />
+<hkern u1="&#xf3;" u2="&#x161;" k="-4" />
+<hkern u1="&#xf3;" u2="&#x2c;" k="16" />
+<hkern u1="&#xf3;" u2="." k="16" />
+<hkern u1="&#xf3;" u2="/" k="8" />
+<hkern u1="&#xf3;" u2=":" k="8" />
+<hkern u1="&#xf3;" u2=";" k="8" />
+<hkern u1="&#xf3;" u2="?" k="16" />
+<hkern u1="&#xf3;" u2="\" k="8" />
+<hkern u1="&#xf3;" u2="s" k="-4" />
+<hkern u1="&#xf3;" u2="t" k="8" />
+<hkern u1="&#xf3;" u2="v" k="-4" />
+<hkern u1="&#xf3;" u2="x" k="4" />
+<hkern u1="&#xf3;" u2="y" k="-4" />
+<hkern u1="&#xf3;" u2="z" k="12" />
+<hkern u1="&#xf3;" u2="&#x2122;" k="24" />
+<hkern u1="&#xf3;" u2="&#x2026;" k="16" />
+<hkern u1="&#xf3;" u2="&#x201c;" k="32" />
+<hkern u1="&#xf3;" u2="&#x2018;" k="32" />
+<hkern u1="&#xf3;" u2="&#x201a;" k="16" />
+<hkern u1="&#xf3;" u2="&#x201e;" k="16" />
+<hkern u1="&#xf4;" u2="&#x161;" k="-4" />
+<hkern u1="&#xf4;" u2="&#x2c;" k="16" />
+<hkern u1="&#xf4;" u2="." k="16" />
+<hkern u1="&#xf4;" u2="/" k="8" />
+<hkern u1="&#xf4;" u2=":" k="8" />
+<hkern u1="&#xf4;" u2=";" k="8" />
+<hkern u1="&#xf4;" u2="?" k="16" />
+<hkern u1="&#xf4;" u2="\" k="8" />
+<hkern u1="&#xf4;" u2="s" k="-4" />
+<hkern u1="&#xf4;" u2="t" k="8" />
+<hkern u1="&#xf4;" u2="v" k="-4" />
+<hkern u1="&#xf4;" u2="x" k="4" />
+<hkern u1="&#xf4;" u2="y" k="-4" />
+<hkern u1="&#xf4;" u2="z" k="12" />
+<hkern u1="&#xf4;" u2="&#x2122;" k="24" />
+<hkern u1="&#xf4;" u2="&#x2026;" k="16" />
+<hkern u1="&#xf4;" u2="&#x201c;" k="32" />
+<hkern u1="&#xf4;" u2="&#x2018;" k="32" />
+<hkern u1="&#xf4;" u2="&#x201a;" k="16" />
+<hkern u1="&#xf4;" u2="&#x201e;" k="16" />
+<hkern u1="&#xf6;" u2="&#x161;" k="-4" />
+<hkern u1="&#xf6;" u2="&#x2c;" k="16" />
+<hkern u1="&#xf6;" u2="." k="16" />
+<hkern u1="&#xf6;" u2="/" k="8" />
+<hkern u1="&#xf6;" u2=":" k="8" />
+<hkern u1="&#xf6;" u2=";" k="8" />
+<hkern u1="&#xf6;" u2="?" k="16" />
+<hkern u1="&#xf6;" u2="\" k="8" />
+<hkern u1="&#xf6;" u2="s" k="-4" />
+<hkern u1="&#xf6;" u2="t" k="8" />
+<hkern u1="&#xf6;" u2="v" k="-4" />
+<hkern u1="&#xf6;" u2="x" k="4" />
+<hkern u1="&#xf6;" u2="y" k="-4" />
+<hkern u1="&#xf6;" u2="z" k="12" />
+<hkern u1="&#xf6;" u2="&#x2122;" k="24" />
+<hkern u1="&#xf6;" u2="&#x2026;" k="16" />
+<hkern u1="&#xf6;" u2="&#x201c;" k="32" />
+<hkern u1="&#xf6;" u2="&#x2018;" k="32" />
+<hkern u1="&#xf6;" u2="&#x201a;" k="16" />
+<hkern u1="&#xf6;" u2="&#x201e;" k="16" />
+<hkern u1="&#xf5;" u2="&#x161;" k="-4" />
+<hkern u1="&#xf5;" u2="&#x2c;" k="16" />
+<hkern u1="&#xf5;" u2="." k="16" />
+<hkern u1="&#xf5;" u2="/" k="8" />
+<hkern u1="&#xf5;" u2=":" k="8" />
+<hkern u1="&#xf5;" u2=";" k="8" />
+<hkern u1="&#xf5;" u2="?" k="16" />
+<hkern u1="&#xf5;" u2="\" k="8" />
+<hkern u1="&#xf5;" u2="s" k="-4" />
+<hkern u1="&#xf5;" u2="t" k="8" />
+<hkern u1="&#xf5;" u2="v" k="-4" />
+<hkern u1="&#xf5;" u2="x" k="4" />
+<hkern u1="&#xf5;" u2="y" k="-4" />
+<hkern u1="&#xf5;" u2="z" k="12" />
+<hkern u1="&#xf5;" u2="&#x2122;" k="24" />
+<hkern u1="&#xf5;" u2="&#x2026;" k="16" />
+<hkern u1="&#xf5;" u2="&#x201c;" k="32" />
+<hkern u1="&#xf5;" u2="&#x2018;" k="32" />
+<hkern u1="&#xf5;" u2="&#x201a;" k="16" />
+<hkern u1="&#xf5;" u2="&#x201e;" k="16" />
+<hkern u1="&#xb0;" u2="4" k="52" />
+<hkern u1="&#xa3;" u2="1" k="-8" />
+<hkern u1="&#xa3;" u2="4" k="16" />
+<hkern u1="&#xdf;" u2="&#x161;" k="-4" />
+<hkern u1="&#xdf;" u2="&#x2c;" k="16" />
+<hkern u1="&#xdf;" u2="." k="16" />
+<hkern u1="&#xdf;" u2="/" k="8" />
+<hkern u1="&#xdf;" u2=":" k="8" />
+<hkern u1="&#xdf;" u2=";" k="8" />
+<hkern u1="&#xdf;" u2="?" k="16" />
+<hkern u1="&#xdf;" u2="\" k="8" />
+<hkern u1="&#xdf;" u2="g" k="8" />
+<hkern u1="&#xdf;" u2="s" k="-4" />
+<hkern u1="&#xdf;" u2="t" k="8" />
+<hkern u1="&#xdf;" u2="v" k="-4" />
+<hkern u1="&#xdf;" u2="x" k="4" />
+<hkern u1="&#xdf;" u2="y" k="-4" />
+<hkern u1="&#xdf;" u2="z" k="12" />
+<hkern u1="&#xdf;" u2="&#x2122;" k="24" />
+<hkern u1="&#xdf;" u2="&#x2026;" k="16" />
+<hkern u1="&#xdf;" u2="&#x201c;" k="32" />
+<hkern u1="&#xdf;" u2="&#x2018;" k="32" />
+<hkern u1="&#xdf;" u2="&#x201a;" k="16" />
+<hkern u1="&#xdf;" u2="&#x201e;" k="16" />
+<hkern u1="&#xae;" u2="&#xdd;" k="24" />
+<hkern u1="&#xae;" u2="A" k="8" />
+<hkern u1="&#xae;" u2="T" k="24" />
+<hkern u1="&#xae;" u2="W" k="16" />
+<hkern u1="&#xae;" u2="Y" k="24" />
+<hkern u1="&#xae;" u2="&#xc4;" k="8" />
+<hkern u1="&#xae;" u2="&#xc5;" k="8" />
+<hkern u1="&#xae;" u2="&#xc6;" k="8" />
+<hkern u1="&#xae;" u2="&#xc0;" k="8" />
+<hkern u1="&#xae;" u2="&#xc3;" k="8" />
+<hkern u1="&#xae;" u2="&#x178;" k="24" />
+<hkern u1="&#xae;" u2="&#xc2;" k="8" />
+<hkern u1="&#xae;" u2="&#xc1;" k="8" />
+<hkern u1="&#xa9;" u2="&#xdd;" k="24" />
+<hkern u1="&#xa9;" u2="A" k="8" />
+<hkern u1="&#xa9;" u2="T" k="24" />
+<hkern u1="&#xa9;" u2="W" k="16" />
+<hkern u1="&#xa9;" u2="Y" k="24" />
+<hkern u1="&#xa9;" u2="&#xc4;" k="8" />
+<hkern u1="&#xa9;" u2="&#xc5;" k="8" />
+<hkern u1="&#xa9;" u2="&#xc6;" k="8" />
+<hkern u1="&#xa9;" u2="&#xc0;" k="8" />
+<hkern u1="&#xa9;" u2="&#xc3;" k="8" />
+<hkern u1="&#xa9;" u2="&#x178;" k="24" />
+<hkern u1="&#xa9;" u2="&#xc2;" k="8" />
+<hkern u1="&#xa9;" u2="&#xc1;" k="8" />
+<hkern u1="&#xc6;" u2="@" k="16" />
+<hkern u1="&#xc6;" u2="T" k="-12" />
+<hkern u1="&#xc6;" u2="a" k="20" />
+<hkern u1="&#xc6;" u2="c" k="20" />
+<hkern u1="&#xc6;" u2="d" k="20" />
+<hkern u1="&#xc6;" u2="e" k="20" />
+<hkern u1="&#xc6;" u2="f" k="8" />
+<hkern u1="&#xc6;" u2="g" k="4" />
+<hkern u1="&#xc6;" u2="o" k="20" />
+<hkern u1="&#xc6;" u2="q" k="20" />
+<hkern u1="&#xc6;" u2="v" k="16" />
+<hkern u1="&#xc6;" u2="y" k="16" />
+<hkern u1="&#xc6;" u2="&#xe7;" k="20" />
+<hkern u1="&#xc6;" u2="&#xae;" k="16" />
+<hkern u1="&#xc6;" u2="&#xa9;" k="16" />
+<hkern u1="&#xc6;" u2="&#x3c0;" k="16" />
+<hkern u1="&#xc6;" u2="&#xe6;" k="20" />
+<hkern u1="&#xc6;" u2="&#x153;" k="20" />
+<hkern u1="&#xd8;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd8;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd8;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd8;" u2="." k="32" />
+<hkern u1="&#xd8;" u2="/" k="40" />
+<hkern u1="&#xd8;" u2="?" k="16" />
+<hkern u1="&#xd8;" u2="J" k="36" />
+<hkern u1="&#xd8;" u2="T" k="40" />
+<hkern u1="&#xd8;" u2="V" k="32" />
+<hkern u1="&#xd8;" u2="W" k="48" />
+<hkern u1="&#xd8;" u2="X" k="28" />
+<hkern u1="&#xd8;" u2="Y" k="36" />
+<hkern u1="&#xd8;" u2="Z" k="36" />
+<hkern u1="&#xd8;" u2="a" k="4" />
+<hkern u1="&#xd8;" u2="b" k="8" />
+<hkern u1="&#xd8;" u2="c" k="4" />
+<hkern u1="&#xd8;" u2="d" k="4" />
+<hkern u1="&#xd8;" u2="e" k="4" />
+<hkern u1="&#xd8;" u2="h" k="8" />
+<hkern u1="&#xd8;" u2="k" k="8" />
+<hkern u1="&#xd8;" u2="l" k="8" />
+<hkern u1="&#xd8;" u2="m" k="4" />
+<hkern u1="&#xd8;" u2="n" k="4" />
+<hkern u1="&#xd8;" u2="o" k="4" />
+<hkern u1="&#xd8;" u2="p" k="4" />
+<hkern u1="&#xd8;" u2="q" k="4" />
+<hkern u1="&#xd8;" u2="r" k="4" />
+<hkern u1="&#xd8;" u2="u" k="4" />
+<hkern u1="&#xd8;" u2="x" k="8" />
+<hkern u1="&#xd8;" u2="z" k="8" />
+<hkern u1="&#xd8;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd8;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd8;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd8;" u2="&#x153;" k="4" />
+<hkern u1="&#xd8;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd8;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd8;" u2="&#x178;" k="36" />
+<hkern u1="&#xd8;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd8;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd8;" u2="&#x131;" k="4" />
+<hkern u1="&#x3c0;" u2="&#xdd;" k="24" />
+<hkern u1="&#x3c0;" u2="A" k="8" />
+<hkern u1="&#x3c0;" u2="T" k="24" />
+<hkern u1="&#x3c0;" u2="W" k="16" />
+<hkern u1="&#x3c0;" u2="Y" k="24" />
+<hkern u1="&#x3c0;" u2="&#xc4;" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc5;" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc6;" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc0;" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc3;" k="8" />
+<hkern u1="&#x3c0;" u2="&#x178;" k="24" />
+<hkern u1="&#x3c0;" u2="&#xc2;" k="8" />
+<hkern u1="&#x3c0;" u2="&#xc1;" k="8" />
+<hkern u1="&#xe6;" u2="v" k="8" />
+<hkern u1="&#xe6;" u2="x" k="12" />
+<hkern u1="&#xe6;" u2="y" k="8" />
+<hkern u1="&#xf8;" u2="&#x161;" k="-4" />
+<hkern u1="&#xf8;" u2="&#x2c;" k="16" />
+<hkern u1="&#xf8;" u2="." k="16" />
+<hkern u1="&#xf8;" u2="/" k="8" />
+<hkern u1="&#xf8;" u2=":" k="8" />
+<hkern u1="&#xf8;" u2=";" k="8" />
+<hkern u1="&#xf8;" u2="?" k="16" />
+<hkern u1="&#xf8;" u2="\" k="8" />
+<hkern u1="&#xf8;" u2="s" k="-4" />
+<hkern u1="&#xf8;" u2="t" k="8" />
+<hkern u1="&#xf8;" u2="v" k="-4" />
+<hkern u1="&#xf8;" u2="x" k="4" />
+<hkern u1="&#xf8;" u2="y" k="-4" />
+<hkern u1="&#xf8;" u2="z" k="12" />
+<hkern u1="&#xf8;" u2="&#x2122;" k="24" />
+<hkern u1="&#xf8;" u2="&#x2026;" k="16" />
+<hkern u1="&#xf8;" u2="&#x201c;" k="32" />
+<hkern u1="&#xf8;" u2="&#x2018;" k="32" />
+<hkern u1="&#xf8;" u2="&#x201a;" k="16" />
+<hkern u1="&#xf8;" u2="&#x201e;" k="16" />
+<hkern u1="&#xbf;" u2="&#xdd;" k="32" />
+<hkern u1="&#xbf;" u2="1" k="32" />
+<hkern u1="&#xbf;" u2="3" k="-8" />
+<hkern u1="&#xbf;" u2="7" k="24" />
+<hkern u1="&#xbf;" u2="C" k="16" />
+<hkern u1="&#xbf;" u2="G" k="16" />
+<hkern u1="&#xbf;" u2="O" k="16" />
+<hkern u1="&#xbf;" u2="Q" k="16" />
+<hkern u1="&#xbf;" u2="T" k="48" />
+<hkern u1="&#xbf;" u2="V" k="40" />
+<hkern u1="&#xbf;" u2="W" k="24" />
+<hkern u1="&#xbf;" u2="Y" k="32" />
+<hkern u1="&#xbf;" u2="v" k="24" />
+<hkern u1="&#xbf;" u2="w" k="16" />
+<hkern u1="&#xbf;" u2="x" k="8" />
+<hkern u1="&#xbf;" u2="y" k="24" />
+<hkern u1="&#xbf;" u2="&#xd6;" k="16" />
+<hkern u1="&#xbf;" u2="&#xd8;" k="16" />
+<hkern u1="&#xbf;" u2="&#xd5;" k="16" />
+<hkern u1="&#xbf;" u2="&#x152;" k="16" />
+<hkern u1="&#xbf;" u2="&#x178;" k="32" />
+<hkern u1="&#xbf;" u2="&#xd3;" k="16" />
+<hkern u1="&#xbf;" u2="&#xd4;" k="16" />
+<hkern u1="&#xbf;" u2="&#xd2;" k="16" />
+<hkern u1="&#x192;" u2="1" k="-24" />
+<hkern u1="&#xab;" u2="&#xdd;" k="32" />
+<hkern u1="&#xab;" u2="T" k="32" />
+<hkern u1="&#xab;" u2="V" k="16" />
+<hkern u1="&#xab;" u2="W" k="16" />
+<hkern u1="&#xab;" u2="Y" k="32" />
+<hkern u1="&#xab;" u2="&#x178;" k="32" />
+<hkern u1="&#xbb;" u2="&#xdd;" k="40" />
+<hkern u1="&#xbb;" u2="T" k="88" />
+<hkern u1="&#xbb;" u2="V" k="40" />
+<hkern u1="&#xbb;" u2="W" k="32" />
+<hkern u1="&#xbb;" u2="Y" k="40" />
+<hkern u1="&#xbb;" u2="x" k="40" />
+<hkern u1="&#xbb;" u2="z" k="40" />
+<hkern u1="&#xbb;" u2="&#x178;" k="40" />
+<hkern u1="&#x2026;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2026;" u2="0" k="32" />
+<hkern u1="&#x2026;" u2="1" k="48" />
+<hkern u1="&#x2026;" u2="4" k="40" />
+<hkern u1="&#x2026;" u2="6" k="24" />
+<hkern u1="&#x2026;" u2="8" k="12" />
+<hkern u1="&#x2026;" u2="9" k="8" />
+<hkern u1="&#x2026;" u2="A" k="-24" />
+<hkern u1="&#x2026;" u2="C" k="32" />
+<hkern u1="&#x2026;" u2="G" k="32" />
+<hkern u1="&#x2026;" u2="O" k="32" />
+<hkern u1="&#x2026;" u2="Q" k="32" />
+<hkern u1="&#x2026;" u2="T" k="48" />
+<hkern u1="&#x2026;" u2="U" k="8" />
+<hkern u1="&#x2026;" u2="V" k="48" />
+<hkern u1="&#x2026;" u2="W" k="16" />
+<hkern u1="&#x2026;" u2="Y" k="40" />
+<hkern u1="&#x2026;" u2="a" k="16" />
+<hkern u1="&#x2026;" u2="c" k="16" />
+<hkern u1="&#x2026;" u2="d" k="16" />
+<hkern u1="&#x2026;" u2="e" k="16" />
+<hkern u1="&#x2026;" u2="m" k="16" />
+<hkern u1="&#x2026;" u2="n" k="16" />
+<hkern u1="&#x2026;" u2="o" k="16" />
+<hkern u1="&#x2026;" u2="p" k="16" />
+<hkern u1="&#x2026;" u2="q" k="16" />
+<hkern u1="&#x2026;" u2="r" k="16" />
+<hkern u1="&#x2026;" u2="t" k="32" />
+<hkern u1="&#x2026;" u2="u" k="16" />
+<hkern u1="&#x2026;" u2="v" k="24" />
+<hkern u1="&#x2026;" u2="w" k="8" />
+<hkern u1="&#x2026;" u2="y" k="24" />
+<hkern u1="&#x2026;" u2="&#xc4;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xc5;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xd6;" k="32" />
+<hkern u1="&#x2026;" u2="&#xdc;" k="8" />
+<hkern u1="&#x2026;" u2="&#xe7;" k="16" />
+<hkern u1="&#x2026;" u2="&#xc6;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xd8;" k="32" />
+<hkern u1="&#x2026;" u2="&#xe6;" k="16" />
+<hkern u1="&#x2026;" u2="&#xc0;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xc3;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xd5;" k="32" />
+<hkern u1="&#x2026;" u2="&#x152;" k="32" />
+<hkern u1="&#x2026;" u2="&#x153;" k="16" />
+<hkern u1="&#x2026;" u2="&#x178;" k="40" />
+<hkern u1="&#x2026;" u2="&#xc2;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xc1;" k="-24" />
+<hkern u1="&#x2026;" u2="&#xd3;" k="32" />
+<hkern u1="&#x2026;" u2="&#xd4;" k="32" />
+<hkern u1="&#x2026;" u2="&#xd2;" k="32" />
+<hkern u1="&#x2026;" u2="&#xda;" k="8" />
+<hkern u1="&#x2026;" u2="&#xdb;" k="8" />
+<hkern u1="&#x2026;" u2="&#xd9;" k="8" />
+<hkern u1="&#x2026;" u2="&#x131;" k="16" />
+<hkern u1="&#xc0;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc0;" u2="&#xdd;" k="56" />
+<hkern u1="&#xc0;" u2="*" k="48" />
+<hkern u1="&#xc0;" u2="&#x2c;" k="-24" />
+<hkern u1="&#xc0;" u2="-" k="-8" />
+<hkern u1="&#xc0;" u2="." k="-24" />
+<hkern u1="&#xc0;" u2="@" k="8" />
+<hkern u1="&#xc0;" u2="A" k="-8" />
+<hkern u1="&#xc0;" u2="C" k="32" />
+<hkern u1="&#xc0;" u2="G" k="32" />
+<hkern u1="&#xc0;" u2="J" k="8" />
+<hkern u1="&#xc0;" u2="O" k="32" />
+<hkern u1="&#xc0;" u2="Q" k="32" />
+<hkern u1="&#xc0;" u2="S" k="-4" />
+<hkern u1="&#xc0;" u2="T" k="72" />
+<hkern u1="&#xc0;" u2="V" k="56" />
+<hkern u1="&#xc0;" u2="W" k="56" />
+<hkern u1="&#xc0;" u2="Y" k="56" />
+<hkern u1="&#xc0;" u2="a" k="4" />
+<hkern u1="&#xc0;" u2="c" k="4" />
+<hkern u1="&#xc0;" u2="d" k="4" />
+<hkern u1="&#xc0;" u2="e" k="4" />
+<hkern u1="&#xc0;" u2="m" k="4" />
+<hkern u1="&#xc0;" u2="n" k="4" />
+<hkern u1="&#xc0;" u2="o" k="4" />
+<hkern u1="&#xc0;" u2="p" k="4" />
+<hkern u1="&#xc0;" u2="q" k="4" />
+<hkern u1="&#xc0;" u2="r" k="4" />
+<hkern u1="&#xc0;" u2="t" k="4" />
+<hkern u1="&#xc0;" u2="u" k="4" />
+<hkern u1="&#xc0;" u2="v" k="24" />
+<hkern u1="&#xc0;" u2="w" k="8" />
+<hkern u1="&#xc0;" u2="x" k="-16" />
+<hkern u1="&#xc0;" u2="y" k="24" />
+<hkern u1="&#xc0;" u2="z" k="-8" />
+<hkern u1="&#xc0;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xd6;" k="32" />
+<hkern u1="&#xc0;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc0;" u2="&#xae;" k="8" />
+<hkern u1="&#xc0;" u2="&#xa9;" k="8" />
+<hkern u1="&#xc0;" u2="&#x2122;" k="56" />
+<hkern u1="&#xc0;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xd8;" k="32" />
+<hkern u1="&#xc0;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xc0;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc0;" u2="&#x2026;" k="-24" />
+<hkern u1="&#xc0;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xd5;" k="32" />
+<hkern u1="&#xc0;" u2="&#x152;" k="32" />
+<hkern u1="&#xc0;" u2="&#x153;" k="4" />
+<hkern u1="&#xc0;" u2="&#x2013;" k="-8" />
+<hkern u1="&#xc0;" u2="&#x2014;" k="-8" />
+<hkern u1="&#xc0;" u2="&#x201c;" k="48" />
+<hkern u1="&#xc0;" u2="&#x201d;" k="24" />
+<hkern u1="&#xc0;" u2="&#x2018;" k="48" />
+<hkern u1="&#xc0;" u2="&#x2019;" k="24" />
+<hkern u1="&#xc0;" u2="&#x178;" k="56" />
+<hkern u1="&#xc0;" u2="&#x201a;" k="-24" />
+<hkern u1="&#xc0;" u2="&#x201e;" k="-24" />
+<hkern u1="&#xc0;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xc0;" u2="&#xd3;" k="32" />
+<hkern u1="&#xc0;" u2="&#xd4;" k="32" />
+<hkern u1="&#xc0;" u2="&#xd2;" k="32" />
+<hkern u1="&#xc0;" u2="&#x131;" k="4" />
+<hkern u1="&#xc3;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc3;" u2="&#xdd;" k="56" />
+<hkern u1="&#xc3;" u2="*" k="48" />
+<hkern u1="&#xc3;" u2="&#x2c;" k="-24" />
+<hkern u1="&#xc3;" u2="-" k="-8" />
+<hkern u1="&#xc3;" u2="." k="-24" />
+<hkern u1="&#xc3;" u2="@" k="8" />
+<hkern u1="&#xc3;" u2="A" k="-8" />
+<hkern u1="&#xc3;" u2="C" k="32" />
+<hkern u1="&#xc3;" u2="G" k="32" />
+<hkern u1="&#xc3;" u2="J" k="8" />
+<hkern u1="&#xc3;" u2="O" k="32" />
+<hkern u1="&#xc3;" u2="Q" k="32" />
+<hkern u1="&#xc3;" u2="S" k="-4" />
+<hkern u1="&#xc3;" u2="T" k="72" />
+<hkern u1="&#xc3;" u2="V" k="56" />
+<hkern u1="&#xc3;" u2="W" k="56" />
+<hkern u1="&#xc3;" u2="Y" k="56" />
+<hkern u1="&#xc3;" u2="a" k="4" />
+<hkern u1="&#xc3;" u2="c" k="4" />
+<hkern u1="&#xc3;" u2="d" k="4" />
+<hkern u1="&#xc3;" u2="e" k="4" />
+<hkern u1="&#xc3;" u2="m" k="4" />
+<hkern u1="&#xc3;" u2="n" k="4" />
+<hkern u1="&#xc3;" u2="o" k="4" />
+<hkern u1="&#xc3;" u2="p" k="4" />
+<hkern u1="&#xc3;" u2="q" k="4" />
+<hkern u1="&#xc3;" u2="r" k="4" />
+<hkern u1="&#xc3;" u2="t" k="4" />
+<hkern u1="&#xc3;" u2="u" k="4" />
+<hkern u1="&#xc3;" u2="v" k="24" />
+<hkern u1="&#xc3;" u2="w" k="8" />
+<hkern u1="&#xc3;" u2="x" k="-16" />
+<hkern u1="&#xc3;" u2="y" k="24" />
+<hkern u1="&#xc3;" u2="z" k="-8" />
+<hkern u1="&#xc3;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xd6;" k="32" />
+<hkern u1="&#xc3;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc3;" u2="&#xae;" k="8" />
+<hkern u1="&#xc3;" u2="&#xa9;" k="8" />
+<hkern u1="&#xc3;" u2="&#x2122;" k="56" />
+<hkern u1="&#xc3;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xd8;" k="32" />
+<hkern u1="&#xc3;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xc3;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc3;" u2="&#x2026;" k="-24" />
+<hkern u1="&#xc3;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xd5;" k="32" />
+<hkern u1="&#xc3;" u2="&#x152;" k="32" />
+<hkern u1="&#xc3;" u2="&#x153;" k="4" />
+<hkern u1="&#xc3;" u2="&#x2013;" k="-8" />
+<hkern u1="&#xc3;" u2="&#x2014;" k="-8" />
+<hkern u1="&#xc3;" u2="&#x201c;" k="48" />
+<hkern u1="&#xc3;" u2="&#x201d;" k="24" />
+<hkern u1="&#xc3;" u2="&#x2018;" k="48" />
+<hkern u1="&#xc3;" u2="&#x2019;" k="24" />
+<hkern u1="&#xc3;" u2="&#x178;" k="56" />
+<hkern u1="&#xc3;" u2="&#x201a;" k="-24" />
+<hkern u1="&#xc3;" u2="&#x201e;" k="-24" />
+<hkern u1="&#xc3;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xc3;" u2="&#xd3;" k="32" />
+<hkern u1="&#xc3;" u2="&#xd4;" k="32" />
+<hkern u1="&#xc3;" u2="&#xd2;" k="32" />
+<hkern u1="&#xc3;" u2="&#x131;" k="4" />
+<hkern u1="&#xd5;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd5;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd5;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd5;" u2="." k="32" />
+<hkern u1="&#xd5;" u2="/" k="40" />
+<hkern u1="&#xd5;" u2="?" k="16" />
+<hkern u1="&#xd5;" u2="J" k="36" />
+<hkern u1="&#xd5;" u2="T" k="40" />
+<hkern u1="&#xd5;" u2="V" k="32" />
+<hkern u1="&#xd5;" u2="W" k="48" />
+<hkern u1="&#xd5;" u2="X" k="28" />
+<hkern u1="&#xd5;" u2="Y" k="36" />
+<hkern u1="&#xd5;" u2="Z" k="36" />
+<hkern u1="&#xd5;" u2="a" k="4" />
+<hkern u1="&#xd5;" u2="b" k="8" />
+<hkern u1="&#xd5;" u2="c" k="4" />
+<hkern u1="&#xd5;" u2="d" k="4" />
+<hkern u1="&#xd5;" u2="e" k="4" />
+<hkern u1="&#xd5;" u2="h" k="8" />
+<hkern u1="&#xd5;" u2="k" k="8" />
+<hkern u1="&#xd5;" u2="l" k="8" />
+<hkern u1="&#xd5;" u2="m" k="4" />
+<hkern u1="&#xd5;" u2="n" k="4" />
+<hkern u1="&#xd5;" u2="o" k="4" />
+<hkern u1="&#xd5;" u2="p" k="4" />
+<hkern u1="&#xd5;" u2="q" k="4" />
+<hkern u1="&#xd5;" u2="r" k="4" />
+<hkern u1="&#xd5;" u2="u" k="4" />
+<hkern u1="&#xd5;" u2="x" k="8" />
+<hkern u1="&#xd5;" u2="z" k="8" />
+<hkern u1="&#xd5;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd5;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd5;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd5;" u2="&#x153;" k="4" />
+<hkern u1="&#xd5;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd5;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd5;" u2="&#x178;" k="36" />
+<hkern u1="&#xd5;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd5;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd5;" u2="&#x131;" k="4" />
+<hkern u1="&#x152;" u2="@" k="16" />
+<hkern u1="&#x152;" u2="T" k="-12" />
+<hkern u1="&#x152;" u2="a" k="20" />
+<hkern u1="&#x152;" u2="c" k="20" />
+<hkern u1="&#x152;" u2="d" k="20" />
+<hkern u1="&#x152;" u2="e" k="20" />
+<hkern u1="&#x152;" u2="f" k="8" />
+<hkern u1="&#x152;" u2="g" k="4" />
+<hkern u1="&#x152;" u2="o" k="20" />
+<hkern u1="&#x152;" u2="q" k="20" />
+<hkern u1="&#x152;" u2="v" k="16" />
+<hkern u1="&#x152;" u2="y" k="16" />
+<hkern u1="&#x152;" u2="&#xe7;" k="20" />
+<hkern u1="&#x152;" u2="&#xae;" k="16" />
+<hkern u1="&#x152;" u2="&#xa9;" k="16" />
+<hkern u1="&#x152;" u2="&#x3c0;" k="16" />
+<hkern u1="&#x152;" u2="&#xe6;" k="20" />
+<hkern u1="&#x152;" u2="&#x153;" k="20" />
+<hkern u1="&#x153;" u2="v" k="8" />
+<hkern u1="&#x153;" u2="x" k="12" />
+<hkern u1="&#x153;" u2="y" k="8" />
+<hkern u1="&#x2013;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2013;" u2="&#x17d;" k="8" />
+<hkern u1="&#x2013;" u2="1" k="16" />
+<hkern u1="&#x2013;" u2="7" k="24" />
+<hkern u1="&#x2013;" u2="A" k="-8" />
+<hkern u1="&#x2013;" u2="T" k="56" />
+<hkern u1="&#x2013;" u2="V" k="40" />
+<hkern u1="&#x2013;" u2="W" k="32" />
+<hkern u1="&#x2013;" u2="X" k="24" />
+<hkern u1="&#x2013;" u2="Y" k="40" />
+<hkern u1="&#x2013;" u2="Z" k="8" />
+<hkern u1="&#x2013;" u2="a" k="8" />
+<hkern u1="&#x2013;" u2="c" k="8" />
+<hkern u1="&#x2013;" u2="d" k="8" />
+<hkern u1="&#x2013;" u2="e" k="8" />
+<hkern u1="&#x2013;" u2="o" k="8" />
+<hkern u1="&#x2013;" u2="q" k="8" />
+<hkern u1="&#x2013;" u2="v" k="8" />
+<hkern u1="&#x2013;" u2="x" k="24" />
+<hkern u1="&#x2013;" u2="y" k="8" />
+<hkern u1="&#x2013;" u2="z" k="24" />
+<hkern u1="&#x2013;" u2="&#xc4;" k="-8" />
+<hkern u1="&#x2013;" u2="&#xc5;" k="-8" />
+<hkern u1="&#x2013;" u2="&#xe7;" k="8" />
+<hkern u1="&#x2013;" u2="&#xc6;" k="-8" />
+<hkern u1="&#x2013;" u2="&#xe6;" k="8" />
+<hkern u1="&#x2013;" u2="&#xc0;" k="-8" />
+<hkern u1="&#x2013;" u2="&#xc3;" k="-8" />
+<hkern u1="&#x2013;" u2="&#x153;" k="8" />
+<hkern u1="&#x2013;" u2="&#x178;" k="40" />
+<hkern u1="&#x2013;" u2="&#xc2;" k="-8" />
+<hkern u1="&#x2013;" u2="&#xc1;" k="-8" />
+<hkern u1="&#x2014;" u2="&#xdd;" k="40" />
+<hkern u1="&#x2014;" u2="&#x17d;" k="8" />
+<hkern u1="&#x2014;" u2="1" k="16" />
+<hkern u1="&#x2014;" u2="7" k="24" />
+<hkern u1="&#x2014;" u2="A" k="-8" />
+<hkern u1="&#x2014;" u2="T" k="56" />
+<hkern u1="&#x2014;" u2="V" k="40" />
+<hkern u1="&#x2014;" u2="W" k="32" />
+<hkern u1="&#x2014;" u2="X" k="24" />
+<hkern u1="&#x2014;" u2="Y" k="40" />
+<hkern u1="&#x2014;" u2="Z" k="8" />
+<hkern u1="&#x2014;" u2="a" k="8" />
+<hkern u1="&#x2014;" u2="c" k="8" />
+<hkern u1="&#x2014;" u2="d" k="8" />
+<hkern u1="&#x2014;" u2="e" k="8" />
+<hkern u1="&#x2014;" u2="o" k="8" />
+<hkern u1="&#x2014;" u2="q" k="8" />
+<hkern u1="&#x2014;" u2="v" k="8" />
+<hkern u1="&#x2014;" u2="x" k="24" />
+<hkern u1="&#x2014;" u2="y" k="8" />
+<hkern u1="&#x2014;" u2="z" k="24" />
+<hkern u1="&#x2014;" u2="&#xc4;" k="-8" />
+<hkern u1="&#x2014;" u2="&#xc5;" k="-8" />
+<hkern u1="&#x2014;" u2="&#xe7;" k="8" />
+<hkern u1="&#x2014;" u2="&#xc6;" k="-8" />
+<hkern u1="&#x2014;" u2="&#xe6;" k="8" />
+<hkern u1="&#x2014;" u2="&#xc0;" k="-8" />
+<hkern u1="&#x2014;" u2="&#xc3;" k="-8" />
+<hkern u1="&#x2014;" u2="&#x153;" k="8" />
+<hkern u1="&#x2014;" u2="&#x178;" k="40" />
+<hkern u1="&#x2014;" u2="&#xc2;" k="-8" />
+<hkern u1="&#x2014;" u2="&#xc1;" k="-8" />
+<hkern u1="&#x201c;" u2="&#x161;" k="24" />
+<hkern u1="&#x201c;" u2="&#xdd;" k="-24" />
+<hkern u1="&#x201c;" u2="A" k="32" />
+<hkern u1="&#x201c;" u2="J" k="88" />
+<hkern u1="&#x201c;" u2="T" k="-8" />
+<hkern u1="&#x201c;" u2="V" k="-16" />
+<hkern u1="&#x201c;" u2="W" k="-16" />
+<hkern u1="&#x201c;" u2="X" k="-16" />
+<hkern u1="&#x201c;" u2="Y" k="-24" />
+<hkern u1="&#x201c;" u2="a" k="24" />
+<hkern u1="&#x201c;" u2="c" k="24" />
+<hkern u1="&#x201c;" u2="d" k="24" />
+<hkern u1="&#x201c;" u2="e" k="24" />
+<hkern u1="&#x201c;" u2="g" k="32" />
+<hkern u1="&#x201c;" u2="m" k="8" />
+<hkern u1="&#x201c;" u2="n" k="8" />
+<hkern u1="&#x201c;" u2="o" k="24" />
+<hkern u1="&#x201c;" u2="p" k="8" />
+<hkern u1="&#x201c;" u2="q" k="24" />
+<hkern u1="&#x201c;" u2="r" k="8" />
+<hkern u1="&#x201c;" u2="s" k="24" />
+<hkern u1="&#x201c;" u2="u" k="8" />
+<hkern u1="&#x201c;" u2="&#xc4;" k="32" />
+<hkern u1="&#x201c;" u2="&#xc5;" k="32" />
+<hkern u1="&#x201c;" u2="&#xe7;" k="24" />
+<hkern u1="&#x201c;" u2="&#xc6;" k="32" />
+<hkern u1="&#x201c;" u2="&#xe6;" k="24" />
+<hkern u1="&#x201c;" u2="&#xc0;" k="32" />
+<hkern u1="&#x201c;" u2="&#xc3;" k="32" />
+<hkern u1="&#x201c;" u2="&#x153;" k="24" />
+<hkern u1="&#x201c;" u2="&#x178;" k="-24" />
+<hkern u1="&#x201c;" u2="&#xc2;" k="32" />
+<hkern u1="&#x201c;" u2="&#xc1;" k="32" />
+<hkern u1="&#x201c;" u2="&#x131;" k="8" />
+<hkern u1="&#x201d;" u2="&#x160;" k="8" />
+<hkern u1="&#x201d;" u2="C" k="32" />
+<hkern u1="&#x201d;" u2="G" k="32" />
+<hkern u1="&#x201d;" u2="J" k="96" />
+<hkern u1="&#x201d;" u2="O" k="32" />
+<hkern u1="&#x201d;" u2="Q" k="32" />
+<hkern u1="&#x201d;" u2="S" k="8" />
+<hkern u1="&#x201d;" u2="a" k="40" />
+<hkern u1="&#x201d;" u2="c" k="40" />
+<hkern u1="&#x201d;" u2="d" k="40" />
+<hkern u1="&#x201d;" u2="e" k="40" />
+<hkern u1="&#x201d;" u2="o" k="40" />
+<hkern u1="&#x201d;" u2="q" k="40" />
+<hkern u1="&#x201d;" u2="&#xd6;" k="32" />
+<hkern u1="&#x201d;" u2="&#xe7;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd8;" k="32" />
+<hkern u1="&#x201d;" u2="&#xe6;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd5;" k="32" />
+<hkern u1="&#x201d;" u2="&#x152;" k="32" />
+<hkern u1="&#x201d;" u2="&#x153;" k="40" />
+<hkern u1="&#x201d;" u2="&#xd3;" k="32" />
+<hkern u1="&#x201d;" u2="&#xd4;" k="32" />
+<hkern u1="&#x201d;" u2="&#xd2;" k="32" />
+<hkern u1="&#x2018;" u2="&#x161;" k="24" />
+<hkern u1="&#x2018;" u2="&#xdd;" k="-24" />
+<hkern u1="&#x2018;" u2="A" k="32" />
+<hkern u1="&#x2018;" u2="J" k="88" />
+<hkern u1="&#x2018;" u2="T" k="-8" />
+<hkern u1="&#x2018;" u2="V" k="-16" />
+<hkern u1="&#x2018;" u2="W" k="-16" />
+<hkern u1="&#x2018;" u2="X" k="-16" />
+<hkern u1="&#x2018;" u2="Y" k="-24" />
+<hkern u1="&#x2018;" u2="a" k="24" />
+<hkern u1="&#x2018;" u2="c" k="24" />
+<hkern u1="&#x2018;" u2="d" k="24" />
+<hkern u1="&#x2018;" u2="e" k="24" />
+<hkern u1="&#x2018;" u2="g" k="32" />
+<hkern u1="&#x2018;" u2="m" k="8" />
+<hkern u1="&#x2018;" u2="n" k="8" />
+<hkern u1="&#x2018;" u2="o" k="24" />
+<hkern u1="&#x2018;" u2="p" k="8" />
+<hkern u1="&#x2018;" u2="q" k="24" />
+<hkern u1="&#x2018;" u2="r" k="8" />
+<hkern u1="&#x2018;" u2="s" k="24" />
+<hkern u1="&#x2018;" u2="u" k="8" />
+<hkern u1="&#x2018;" u2="&#xc4;" k="32" />
+<hkern u1="&#x2018;" u2="&#xc5;" k="32" />
+<hkern u1="&#x2018;" u2="&#xe7;" k="24" />
+<hkern u1="&#x2018;" u2="&#xc6;" k="32" />
+<hkern u1="&#x2018;" u2="&#xe6;" k="24" />
+<hkern u1="&#x2018;" u2="&#xc0;" k="32" />
+<hkern u1="&#x2018;" u2="&#xc3;" k="32" />
+<hkern u1="&#x2018;" u2="&#x153;" k="24" />
+<hkern u1="&#x2018;" u2="&#x178;" k="-24" />
+<hkern u1="&#x2018;" u2="&#xc2;" k="32" />
+<hkern u1="&#x2018;" u2="&#xc1;" k="32" />
+<hkern u1="&#x2018;" u2="&#x131;" k="8" />
+<hkern u1="&#x2019;" u2="&#x160;" k="8" />
+<hkern u1="&#x2019;" u2="C" k="32" />
+<hkern u1="&#x2019;" u2="G" k="32" />
+<hkern u1="&#x2019;" u2="J" k="96" />
+<hkern u1="&#x2019;" u2="O" k="32" />
+<hkern u1="&#x2019;" u2="Q" k="32" />
+<hkern u1="&#x2019;" u2="S" k="8" />
+<hkern u1="&#x2019;" u2="a" k="40" />
+<hkern u1="&#x2019;" u2="c" k="40" />
+<hkern u1="&#x2019;" u2="d" k="40" />
+<hkern u1="&#x2019;" u2="e" k="40" />
+<hkern u1="&#x2019;" u2="o" k="40" />
+<hkern u1="&#x2019;" u2="q" k="40" />
+<hkern u1="&#x2019;" u2="&#xd6;" k="32" />
+<hkern u1="&#x2019;" u2="&#xe7;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd8;" k="32" />
+<hkern u1="&#x2019;" u2="&#xe6;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd5;" k="32" />
+<hkern u1="&#x2019;" u2="&#x152;" k="32" />
+<hkern u1="&#x2019;" u2="&#x153;" k="40" />
+<hkern u1="&#x2019;" u2="&#xd3;" k="32" />
+<hkern u1="&#x2019;" u2="&#xd4;" k="32" />
+<hkern u1="&#x2019;" u2="&#xd2;" k="32" />
+<hkern u1="&#x178;" u2="&#x160;" k="-24" />
+<hkern u1="&#x178;" u2="&#x161;" k="40" />
+<hkern u1="&#x178;" u2="&amp;" k="24" />
+<hkern u1="&#x178;" u2=")" k="-40" />
+<hkern u1="&#x178;" u2="&#x2c;" k="40" />
+<hkern u1="&#x178;" u2="-" k="40" />
+<hkern u1="&#x178;" u2="." k="40" />
+<hkern u1="&#x178;" u2=":" k="24" />
+<hkern u1="&#x178;" u2=";" k="24" />
+<hkern u1="&#x178;" u2="@" k="24" />
+<hkern u1="&#x178;" u2="A" k="24" />
+<hkern u1="&#x178;" u2="C" k="4" />
+<hkern u1="&#x178;" u2="G" k="4" />
+<hkern u1="&#x178;" u2="J" k="56" />
+<hkern u1="&#x178;" u2="O" k="4" />
+<hkern u1="&#x178;" u2="Q" k="4" />
+<hkern u1="&#x178;" u2="S" k="-24" />
+<hkern u1="&#x178;" u2="T" k="-16" />
+<hkern u1="&#x178;" u2="V" k="-24" />
+<hkern u1="&#x178;" u2="X" k="-16" />
+<hkern u1="&#x178;" u2="]" k="-40" />
+<hkern u1="&#x178;" u2="a" k="44" />
+<hkern u1="&#x178;" u2="c" k="44" />
+<hkern u1="&#x178;" u2="d" k="44" />
+<hkern u1="&#x178;" u2="e" k="44" />
+<hkern u1="&#x178;" u2="f" k="8" />
+<hkern u1="&#x178;" u2="g" k="32" />
+<hkern u1="&#x178;" u2="m" k="32" />
+<hkern u1="&#x178;" u2="n" k="32" />
+<hkern u1="&#x178;" u2="o" k="44" />
+<hkern u1="&#x178;" u2="p" k="32" />
+<hkern u1="&#x178;" u2="q" k="44" />
+<hkern u1="&#x178;" u2="r" k="32" />
+<hkern u1="&#x178;" u2="s" k="40" />
+<hkern u1="&#x178;" u2="t" k="16" />
+<hkern u1="&#x178;" u2="u" k="32" />
+<hkern u1="&#x178;" u2="v" k="16" />
+<hkern u1="&#x178;" u2="w" k="8" />
+<hkern u1="&#x178;" u2="x" k="16" />
+<hkern u1="&#x178;" u2="y" k="16" />
+<hkern u1="&#x178;" u2="z" k="24" />
+<hkern u1="&#x178;" u2="}" k="-40" />
+<hkern u1="&#x178;" u2="&#xc4;" k="24" />
+<hkern u1="&#x178;" u2="&#xc5;" k="24" />
+<hkern u1="&#x178;" u2="&#xd6;" k="4" />
+<hkern u1="&#x178;" u2="&#xe7;" k="44" />
+<hkern u1="&#x178;" u2="&#xae;" k="24" />
+<hkern u1="&#x178;" u2="&#xa9;" k="24" />
+<hkern u1="&#x178;" u2="&#xc6;" k="24" />
+<hkern u1="&#x178;" u2="&#xd8;" k="4" />
+<hkern u1="&#x178;" u2="&#x3c0;" k="24" />
+<hkern u1="&#x178;" u2="&#xe6;" k="44" />
+<hkern u1="&#x178;" u2="&#xab;" k="48" />
+<hkern u1="&#x178;" u2="&#xbb;" k="32" />
+<hkern u1="&#x178;" u2="&#x2026;" k="40" />
+<hkern u1="&#x178;" u2="&#xc0;" k="24" />
+<hkern u1="&#x178;" u2="&#xc3;" k="24" />
+<hkern u1="&#x178;" u2="&#xd5;" k="4" />
+<hkern u1="&#x178;" u2="&#x152;" k="4" />
+<hkern u1="&#x178;" u2="&#x153;" k="44" />
+<hkern u1="&#x178;" u2="&#x2013;" k="40" />
+<hkern u1="&#x178;" u2="&#x2014;" k="40" />
+<hkern u1="&#x178;" u2="&#x2039;" k="48" />
+<hkern u1="&#x178;" u2="&#x203a;" k="32" />
+<hkern u1="&#x178;" u2="&#x201a;" k="40" />
+<hkern u1="&#x178;" u2="&#x201e;" k="40" />
+<hkern u1="&#x178;" u2="&#xc2;" k="24" />
+<hkern u1="&#x178;" u2="&#xc1;" k="24" />
+<hkern u1="&#x178;" u2="&#xd3;" k="4" />
+<hkern u1="&#x178;" u2="&#xd4;" k="4" />
+<hkern u1="&#x178;" u2="&#xd2;" k="4" />
+<hkern u1="&#x178;" u2="&#x131;" k="32" />
+<hkern u1="&#x2039;" u2="&#xdd;" k="32" />
+<hkern u1="&#x2039;" u2="T" k="32" />
+<hkern u1="&#x2039;" u2="V" k="16" />
+<hkern u1="&#x2039;" u2="W" k="16" />
+<hkern u1="&#x2039;" u2="Y" k="32" />
+<hkern u1="&#x2039;" u2="&#x178;" k="32" />
+<hkern u1="&#x203a;" u2="&#xdd;" k="40" />
+<hkern u1="&#x203a;" u2="T" k="88" />
+<hkern u1="&#x203a;" u2="V" k="40" />
+<hkern u1="&#x203a;" u2="W" k="32" />
+<hkern u1="&#x203a;" u2="Y" k="40" />
+<hkern u1="&#x203a;" u2="x" k="40" />
+<hkern u1="&#x203a;" u2="z" k="40" />
+<hkern u1="&#x203a;" u2="&#x178;" k="40" />
+<hkern u1="&#x201a;" u2="&#xdd;" k="40" />
+<hkern u1="&#x201a;" u2="0" k="32" />
+<hkern u1="&#x201a;" u2="1" k="48" />
+<hkern u1="&#x201a;" u2="4" k="40" />
+<hkern u1="&#x201a;" u2="6" k="24" />
+<hkern u1="&#x201a;" u2="8" k="12" />
+<hkern u1="&#x201a;" u2="9" k="8" />
+<hkern u1="&#x201a;" u2="A" k="-24" />
+<hkern u1="&#x201a;" u2="C" k="32" />
+<hkern u1="&#x201a;" u2="G" k="32" />
+<hkern u1="&#x201a;" u2="O" k="32" />
+<hkern u1="&#x201a;" u2="Q" k="32" />
+<hkern u1="&#x201a;" u2="T" k="48" />
+<hkern u1="&#x201a;" u2="U" k="8" />
+<hkern u1="&#x201a;" u2="V" k="48" />
+<hkern u1="&#x201a;" u2="W" k="16" />
+<hkern u1="&#x201a;" u2="Y" k="40" />
+<hkern u1="&#x201a;" u2="a" k="16" />
+<hkern u1="&#x201a;" u2="c" k="16" />
+<hkern u1="&#x201a;" u2="d" k="16" />
+<hkern u1="&#x201a;" u2="e" k="16" />
+<hkern u1="&#x201a;" u2="j" k="-24" />
+<hkern u1="&#x201a;" u2="m" k="16" />
+<hkern u1="&#x201a;" u2="n" k="16" />
+<hkern u1="&#x201a;" u2="o" k="16" />
+<hkern u1="&#x201a;" u2="p" k="16" />
+<hkern u1="&#x201a;" u2="q" k="16" />
+<hkern u1="&#x201a;" u2="r" k="16" />
+<hkern u1="&#x201a;" u2="t" k="32" />
+<hkern u1="&#x201a;" u2="u" k="16" />
+<hkern u1="&#x201a;" u2="v" k="24" />
+<hkern u1="&#x201a;" u2="w" k="8" />
+<hkern u1="&#x201a;" u2="y" k="24" />
+<hkern u1="&#x201a;" u2="&#xc4;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xc5;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xd6;" k="32" />
+<hkern u1="&#x201a;" u2="&#xdc;" k="8" />
+<hkern u1="&#x201a;" u2="&#xe7;" k="16" />
+<hkern u1="&#x201a;" u2="&#xc6;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xd8;" k="32" />
+<hkern u1="&#x201a;" u2="&#xe6;" k="16" />
+<hkern u1="&#x201a;" u2="&#xc0;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xc3;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xd5;" k="32" />
+<hkern u1="&#x201a;" u2="&#x152;" k="32" />
+<hkern u1="&#x201a;" u2="&#x153;" k="16" />
+<hkern u1="&#x201a;" u2="&#x178;" k="40" />
+<hkern u1="&#x201a;" u2="&#xc2;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xc1;" k="-24" />
+<hkern u1="&#x201a;" u2="&#xd3;" k="32" />
+<hkern u1="&#x201a;" u2="&#xd4;" k="32" />
+<hkern u1="&#x201a;" u2="&#xd2;" k="32" />
+<hkern u1="&#x201a;" u2="&#xda;" k="8" />
+<hkern u1="&#x201a;" u2="&#xdb;" k="8" />
+<hkern u1="&#x201a;" u2="&#xd9;" k="8" />
+<hkern u1="&#x201a;" u2="&#x131;" k="16" />
+<hkern u1="&#x201e;" u2="&#xdd;" k="40" />
+<hkern u1="&#x201e;" u2="0" k="32" />
+<hkern u1="&#x201e;" u2="1" k="48" />
+<hkern u1="&#x201e;" u2="4" k="40" />
+<hkern u1="&#x201e;" u2="6" k="24" />
+<hkern u1="&#x201e;" u2="8" k="12" />
+<hkern u1="&#x201e;" u2="9" k="8" />
+<hkern u1="&#x201e;" u2="A" k="-24" />
+<hkern u1="&#x201e;" u2="C" k="32" />
+<hkern u1="&#x201e;" u2="G" k="32" />
+<hkern u1="&#x201e;" u2="O" k="32" />
+<hkern u1="&#x201e;" u2="Q" k="32" />
+<hkern u1="&#x201e;" u2="T" k="48" />
+<hkern u1="&#x201e;" u2="U" k="8" />
+<hkern u1="&#x201e;" u2="V" k="48" />
+<hkern u1="&#x201e;" u2="W" k="16" />
+<hkern u1="&#x201e;" u2="Y" k="40" />
+<hkern u1="&#x201e;" u2="a" k="16" />
+<hkern u1="&#x201e;" u2="c" k="16" />
+<hkern u1="&#x201e;" u2="d" k="16" />
+<hkern u1="&#x201e;" u2="e" k="16" />
+<hkern u1="&#x201e;" u2="j" k="-24" />
+<hkern u1="&#x201e;" u2="m" k="16" />
+<hkern u1="&#x201e;" u2="n" k="16" />
+<hkern u1="&#x201e;" u2="o" k="16" />
+<hkern u1="&#x201e;" u2="p" k="16" />
+<hkern u1="&#x201e;" u2="q" k="16" />
+<hkern u1="&#x201e;" u2="r" k="16" />
+<hkern u1="&#x201e;" u2="t" k="32" />
+<hkern u1="&#x201e;" u2="u" k="16" />
+<hkern u1="&#x201e;" u2="v" k="24" />
+<hkern u1="&#x201e;" u2="w" k="8" />
+<hkern u1="&#x201e;" u2="y" k="24" />
+<hkern u1="&#x201e;" u2="&#xc4;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xc5;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xd6;" k="32" />
+<hkern u1="&#x201e;" u2="&#xdc;" k="8" />
+<hkern u1="&#x201e;" u2="&#xe7;" k="16" />
+<hkern u1="&#x201e;" u2="&#xc6;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xd8;" k="32" />
+<hkern u1="&#x201e;" u2="&#xe6;" k="16" />
+<hkern u1="&#x201e;" u2="&#xc0;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xc3;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xd5;" k="32" />
+<hkern u1="&#x201e;" u2="&#x152;" k="32" />
+<hkern u1="&#x201e;" u2="&#x153;" k="16" />
+<hkern u1="&#x201e;" u2="&#x178;" k="40" />
+<hkern u1="&#x201e;" u2="&#xc2;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xc1;" k="-24" />
+<hkern u1="&#x201e;" u2="&#xd3;" k="32" />
+<hkern u1="&#x201e;" u2="&#xd4;" k="32" />
+<hkern u1="&#x201e;" u2="&#xd2;" k="32" />
+<hkern u1="&#x201e;" u2="&#xda;" k="8" />
+<hkern u1="&#x201e;" u2="&#xdb;" k="8" />
+<hkern u1="&#x201e;" u2="&#xd9;" k="8" />
+<hkern u1="&#x201e;" u2="&#x131;" k="16" />
+<hkern u1="&#xc2;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc2;" u2="&#xdd;" k="56" />
+<hkern u1="&#xc2;" u2="*" k="48" />
+<hkern u1="&#xc2;" u2="&#x2c;" k="-24" />
+<hkern u1="&#xc2;" u2="-" k="-8" />
+<hkern u1="&#xc2;" u2="." k="-24" />
+<hkern u1="&#xc2;" u2="@" k="8" />
+<hkern u1="&#xc2;" u2="A" k="-8" />
+<hkern u1="&#xc2;" u2="C" k="32" />
+<hkern u1="&#xc2;" u2="G" k="32" />
+<hkern u1="&#xc2;" u2="J" k="8" />
+<hkern u1="&#xc2;" u2="O" k="32" />
+<hkern u1="&#xc2;" u2="Q" k="32" />
+<hkern u1="&#xc2;" u2="S" k="-4" />
+<hkern u1="&#xc2;" u2="T" k="72" />
+<hkern u1="&#xc2;" u2="V" k="56" />
+<hkern u1="&#xc2;" u2="W" k="56" />
+<hkern u1="&#xc2;" u2="Y" k="56" />
+<hkern u1="&#xc2;" u2="a" k="4" />
+<hkern u1="&#xc2;" u2="c" k="4" />
+<hkern u1="&#xc2;" u2="d" k="4" />
+<hkern u1="&#xc2;" u2="e" k="4" />
+<hkern u1="&#xc2;" u2="m" k="4" />
+<hkern u1="&#xc2;" u2="n" k="4" />
+<hkern u1="&#xc2;" u2="o" k="4" />
+<hkern u1="&#xc2;" u2="p" k="4" />
+<hkern u1="&#xc2;" u2="q" k="4" />
+<hkern u1="&#xc2;" u2="r" k="4" />
+<hkern u1="&#xc2;" u2="t" k="4" />
+<hkern u1="&#xc2;" u2="u" k="4" />
+<hkern u1="&#xc2;" u2="v" k="24" />
+<hkern u1="&#xc2;" u2="w" k="8" />
+<hkern u1="&#xc2;" u2="x" k="-16" />
+<hkern u1="&#xc2;" u2="y" k="24" />
+<hkern u1="&#xc2;" u2="z" k="-8" />
+<hkern u1="&#xc2;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xd6;" k="32" />
+<hkern u1="&#xc2;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc2;" u2="&#xae;" k="8" />
+<hkern u1="&#xc2;" u2="&#xa9;" k="8" />
+<hkern u1="&#xc2;" u2="&#x2122;" k="56" />
+<hkern u1="&#xc2;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xd8;" k="32" />
+<hkern u1="&#xc2;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xc2;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc2;" u2="&#x2026;" k="-24" />
+<hkern u1="&#xc2;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xd5;" k="32" />
+<hkern u1="&#xc2;" u2="&#x152;" k="32" />
+<hkern u1="&#xc2;" u2="&#x153;" k="4" />
+<hkern u1="&#xc2;" u2="&#x2013;" k="-8" />
+<hkern u1="&#xc2;" u2="&#x2014;" k="-8" />
+<hkern u1="&#xc2;" u2="&#x201c;" k="48" />
+<hkern u1="&#xc2;" u2="&#x201d;" k="24" />
+<hkern u1="&#xc2;" u2="&#x2018;" k="48" />
+<hkern u1="&#xc2;" u2="&#x2019;" k="24" />
+<hkern u1="&#xc2;" u2="&#x178;" k="56" />
+<hkern u1="&#xc2;" u2="&#x201a;" k="-24" />
+<hkern u1="&#xc2;" u2="&#x201e;" k="-24" />
+<hkern u1="&#xc2;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xc2;" u2="&#xd3;" k="32" />
+<hkern u1="&#xc2;" u2="&#xd4;" k="32" />
+<hkern u1="&#xc2;" u2="&#xd2;" k="32" />
+<hkern u1="&#xc2;" u2="&#x131;" k="4" />
+<hkern u1="&#xca;" u2="@" k="16" />
+<hkern u1="&#xca;" u2="T" k="-12" />
+<hkern u1="&#xca;" u2="a" k="20" />
+<hkern u1="&#xca;" u2="c" k="20" />
+<hkern u1="&#xca;" u2="d" k="20" />
+<hkern u1="&#xca;" u2="e" k="20" />
+<hkern u1="&#xca;" u2="f" k="8" />
+<hkern u1="&#xca;" u2="g" k="4" />
+<hkern u1="&#xca;" u2="o" k="20" />
+<hkern u1="&#xca;" u2="q" k="20" />
+<hkern u1="&#xca;" u2="v" k="16" />
+<hkern u1="&#xca;" u2="y" k="16" />
+<hkern u1="&#xca;" u2="&#xe7;" k="20" />
+<hkern u1="&#xca;" u2="&#xae;" k="16" />
+<hkern u1="&#xca;" u2="&#xa9;" k="16" />
+<hkern u1="&#xca;" u2="&#x3c0;" k="16" />
+<hkern u1="&#xca;" u2="&#xe6;" k="20" />
+<hkern u1="&#xca;" u2="&#x153;" k="20" />
+<hkern u1="&#xc1;" u2="&#x160;" k="-4" />
+<hkern u1="&#xc1;" u2="&#xdd;" k="56" />
+<hkern u1="&#xc1;" u2="*" k="48" />
+<hkern u1="&#xc1;" u2="&#x2c;" k="-24" />
+<hkern u1="&#xc1;" u2="-" k="-8" />
+<hkern u1="&#xc1;" u2="." k="-24" />
+<hkern u1="&#xc1;" u2="@" k="8" />
+<hkern u1="&#xc1;" u2="A" k="-8" />
+<hkern u1="&#xc1;" u2="C" k="32" />
+<hkern u1="&#xc1;" u2="G" k="32" />
+<hkern u1="&#xc1;" u2="J" k="8" />
+<hkern u1="&#xc1;" u2="O" k="32" />
+<hkern u1="&#xc1;" u2="Q" k="32" />
+<hkern u1="&#xc1;" u2="S" k="-4" />
+<hkern u1="&#xc1;" u2="T" k="72" />
+<hkern u1="&#xc1;" u2="V" k="56" />
+<hkern u1="&#xc1;" u2="W" k="56" />
+<hkern u1="&#xc1;" u2="Y" k="56" />
+<hkern u1="&#xc1;" u2="a" k="4" />
+<hkern u1="&#xc1;" u2="c" k="4" />
+<hkern u1="&#xc1;" u2="d" k="4" />
+<hkern u1="&#xc1;" u2="e" k="4" />
+<hkern u1="&#xc1;" u2="m" k="4" />
+<hkern u1="&#xc1;" u2="n" k="4" />
+<hkern u1="&#xc1;" u2="o" k="4" />
+<hkern u1="&#xc1;" u2="p" k="4" />
+<hkern u1="&#xc1;" u2="q" k="4" />
+<hkern u1="&#xc1;" u2="r" k="4" />
+<hkern u1="&#xc1;" u2="t" k="4" />
+<hkern u1="&#xc1;" u2="u" k="4" />
+<hkern u1="&#xc1;" u2="v" k="24" />
+<hkern u1="&#xc1;" u2="w" k="8" />
+<hkern u1="&#xc1;" u2="x" k="-16" />
+<hkern u1="&#xc1;" u2="y" k="24" />
+<hkern u1="&#xc1;" u2="z" k="-8" />
+<hkern u1="&#xc1;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xd6;" k="32" />
+<hkern u1="&#xc1;" u2="&#xe7;" k="4" />
+<hkern u1="&#xc1;" u2="&#xae;" k="8" />
+<hkern u1="&#xc1;" u2="&#xa9;" k="8" />
+<hkern u1="&#xc1;" u2="&#x2122;" k="56" />
+<hkern u1="&#xc1;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xd8;" k="32" />
+<hkern u1="&#xc1;" u2="&#x3c0;" k="8" />
+<hkern u1="&#xc1;" u2="&#xe6;" k="4" />
+<hkern u1="&#xc1;" u2="&#x2026;" k="-24" />
+<hkern u1="&#xc1;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xd5;" k="32" />
+<hkern u1="&#xc1;" u2="&#x152;" k="32" />
+<hkern u1="&#xc1;" u2="&#x153;" k="4" />
+<hkern u1="&#xc1;" u2="&#x2013;" k="-8" />
+<hkern u1="&#xc1;" u2="&#x2014;" k="-8" />
+<hkern u1="&#xc1;" u2="&#x201c;" k="48" />
+<hkern u1="&#xc1;" u2="&#x201d;" k="24" />
+<hkern u1="&#xc1;" u2="&#x2018;" k="48" />
+<hkern u1="&#xc1;" u2="&#x2019;" k="24" />
+<hkern u1="&#xc1;" u2="&#x178;" k="56" />
+<hkern u1="&#xc1;" u2="&#x201a;" k="-24" />
+<hkern u1="&#xc1;" u2="&#x201e;" k="-24" />
+<hkern u1="&#xc1;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xc1;" u2="&#xd3;" k="32" />
+<hkern u1="&#xc1;" u2="&#xd4;" k="32" />
+<hkern u1="&#xc1;" u2="&#xd2;" k="32" />
+<hkern u1="&#xc1;" u2="&#x131;" k="4" />
+<hkern u1="&#xcb;" u2="@" k="16" />
+<hkern u1="&#xcb;" u2="T" k="-12" />
+<hkern u1="&#xcb;" u2="a" k="20" />
+<hkern u1="&#xcb;" u2="c" k="20" />
+<hkern u1="&#xcb;" u2="d" k="20" />
+<hkern u1="&#xcb;" u2="e" k="20" />
+<hkern u1="&#xcb;" u2="f" k="8" />
+<hkern u1="&#xcb;" u2="g" k="4" />
+<hkern u1="&#xcb;" u2="o" k="20" />
+<hkern u1="&#xcb;" u2="q" k="20" />
+<hkern u1="&#xcb;" u2="v" k="16" />
+<hkern u1="&#xcb;" u2="y" k="16" />
+<hkern u1="&#xcb;" u2="&#xe7;" k="20" />
+<hkern u1="&#xcb;" u2="&#xae;" k="16" />
+<hkern u1="&#xcb;" u2="&#xa9;" k="16" />
+<hkern u1="&#xcb;" u2="&#x3c0;" k="16" />
+<hkern u1="&#xcb;" u2="&#xe6;" k="20" />
+<hkern u1="&#xcb;" u2="&#x153;" k="20" />
+<hkern u1="&#xc8;" u2="@" k="16" />
+<hkern u1="&#xc8;" u2="T" k="-12" />
+<hkern u1="&#xc8;" u2="a" k="20" />
+<hkern u1="&#xc8;" u2="c" k="20" />
+<hkern u1="&#xc8;" u2="d" k="20" />
+<hkern u1="&#xc8;" u2="e" k="20" />
+<hkern u1="&#xc8;" u2="f" k="8" />
+<hkern u1="&#xc8;" u2="g" k="4" />
+<hkern u1="&#xc8;" u2="o" k="20" />
+<hkern u1="&#xc8;" u2="q" k="20" />
+<hkern u1="&#xc8;" u2="v" k="16" />
+<hkern u1="&#xc8;" u2="y" k="16" />
+<hkern u1="&#xc8;" u2="&#xe7;" k="20" />
+<hkern u1="&#xc8;" u2="&#xae;" k="16" />
+<hkern u1="&#xc8;" u2="&#xa9;" k="16" />
+<hkern u1="&#xc8;" u2="&#x3c0;" k="16" />
+<hkern u1="&#xc8;" u2="&#xe6;" k="20" />
+<hkern u1="&#xc8;" u2="&#x153;" k="20" />
+<hkern u1="&#xcd;" u2="/" k="16" />
+<hkern u1="&#xcd;" u2="v" k="-16" />
+<hkern u1="&#xcd;" u2="y" k="-16" />
+<hkern u1="&#xce;" u2="/" k="16" />
+<hkern u1="&#xce;" u2="v" k="-16" />
+<hkern u1="&#xce;" u2="y" k="-16" />
+<hkern u1="&#xcf;" u2="/" k="16" />
+<hkern u1="&#xcf;" u2="v" k="-16" />
+<hkern u1="&#xcf;" u2="y" k="-16" />
+<hkern u1="&#xcc;" u2="/" k="16" />
+<hkern u1="&#xcc;" u2="v" k="-16" />
+<hkern u1="&#xcc;" u2="y" k="-16" />
+<hkern u1="&#xd3;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd3;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd3;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd3;" u2="." k="32" />
+<hkern u1="&#xd3;" u2="/" k="40" />
+<hkern u1="&#xd3;" u2="?" k="16" />
+<hkern u1="&#xd3;" u2="J" k="36" />
+<hkern u1="&#xd3;" u2="T" k="40" />
+<hkern u1="&#xd3;" u2="V" k="32" />
+<hkern u1="&#xd3;" u2="W" k="48" />
+<hkern u1="&#xd3;" u2="X" k="28" />
+<hkern u1="&#xd3;" u2="Y" k="36" />
+<hkern u1="&#xd3;" u2="Z" k="36" />
+<hkern u1="&#xd3;" u2="a" k="4" />
+<hkern u1="&#xd3;" u2="b" k="8" />
+<hkern u1="&#xd3;" u2="c" k="4" />
+<hkern u1="&#xd3;" u2="d" k="4" />
+<hkern u1="&#xd3;" u2="e" k="4" />
+<hkern u1="&#xd3;" u2="h" k="8" />
+<hkern u1="&#xd3;" u2="k" k="8" />
+<hkern u1="&#xd3;" u2="l" k="8" />
+<hkern u1="&#xd3;" u2="m" k="4" />
+<hkern u1="&#xd3;" u2="n" k="4" />
+<hkern u1="&#xd3;" u2="o" k="4" />
+<hkern u1="&#xd3;" u2="p" k="4" />
+<hkern u1="&#xd3;" u2="q" k="4" />
+<hkern u1="&#xd3;" u2="r" k="4" />
+<hkern u1="&#xd3;" u2="u" k="4" />
+<hkern u1="&#xd3;" u2="x" k="8" />
+<hkern u1="&#xd3;" u2="z" k="8" />
+<hkern u1="&#xd3;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd3;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd3;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd3;" u2="&#x153;" k="4" />
+<hkern u1="&#xd3;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd3;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd3;" u2="&#x178;" k="36" />
+<hkern u1="&#xd3;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd3;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd3;" u2="&#x131;" k="4" />
+<hkern u1="&#xd4;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd4;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd4;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd4;" u2="." k="32" />
+<hkern u1="&#xd4;" u2="/" k="40" />
+<hkern u1="&#xd4;" u2="?" k="16" />
+<hkern u1="&#xd4;" u2="J" k="36" />
+<hkern u1="&#xd4;" u2="T" k="40" />
+<hkern u1="&#xd4;" u2="V" k="32" />
+<hkern u1="&#xd4;" u2="W" k="48" />
+<hkern u1="&#xd4;" u2="X" k="28" />
+<hkern u1="&#xd4;" u2="Y" k="36" />
+<hkern u1="&#xd4;" u2="Z" k="36" />
+<hkern u1="&#xd4;" u2="a" k="4" />
+<hkern u1="&#xd4;" u2="b" k="8" />
+<hkern u1="&#xd4;" u2="c" k="4" />
+<hkern u1="&#xd4;" u2="d" k="4" />
+<hkern u1="&#xd4;" u2="e" k="4" />
+<hkern u1="&#xd4;" u2="h" k="8" />
+<hkern u1="&#xd4;" u2="k" k="8" />
+<hkern u1="&#xd4;" u2="l" k="8" />
+<hkern u1="&#xd4;" u2="m" k="4" />
+<hkern u1="&#xd4;" u2="n" k="4" />
+<hkern u1="&#xd4;" u2="o" k="4" />
+<hkern u1="&#xd4;" u2="p" k="4" />
+<hkern u1="&#xd4;" u2="q" k="4" />
+<hkern u1="&#xd4;" u2="r" k="4" />
+<hkern u1="&#xd4;" u2="u" k="4" />
+<hkern u1="&#xd4;" u2="x" k="8" />
+<hkern u1="&#xd4;" u2="z" k="8" />
+<hkern u1="&#xd4;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd4;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd4;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd4;" u2="&#x153;" k="4" />
+<hkern u1="&#xd4;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd4;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd4;" u2="&#x178;" k="36" />
+<hkern u1="&#xd4;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd4;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd4;" u2="&#x131;" k="4" />
+<hkern u1="&#xd2;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd2;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd2;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd2;" u2="." k="32" />
+<hkern u1="&#xd2;" u2="/" k="40" />
+<hkern u1="&#xd2;" u2="?" k="16" />
+<hkern u1="&#xd2;" u2="J" k="36" />
+<hkern u1="&#xd2;" u2="T" k="40" />
+<hkern u1="&#xd2;" u2="V" k="32" />
+<hkern u1="&#xd2;" u2="W" k="48" />
+<hkern u1="&#xd2;" u2="X" k="28" />
+<hkern u1="&#xd2;" u2="Y" k="36" />
+<hkern u1="&#xd2;" u2="Z" k="36" />
+<hkern u1="&#xd2;" u2="a" k="4" />
+<hkern u1="&#xd2;" u2="b" k="8" />
+<hkern u1="&#xd2;" u2="c" k="4" />
+<hkern u1="&#xd2;" u2="d" k="4" />
+<hkern u1="&#xd2;" u2="e" k="4" />
+<hkern u1="&#xd2;" u2="h" k="8" />
+<hkern u1="&#xd2;" u2="k" k="8" />
+<hkern u1="&#xd2;" u2="l" k="8" />
+<hkern u1="&#xd2;" u2="m" k="4" />
+<hkern u1="&#xd2;" u2="n" k="4" />
+<hkern u1="&#xd2;" u2="o" k="4" />
+<hkern u1="&#xd2;" u2="p" k="4" />
+<hkern u1="&#xd2;" u2="q" k="4" />
+<hkern u1="&#xd2;" u2="r" k="4" />
+<hkern u1="&#xd2;" u2="u" k="4" />
+<hkern u1="&#xd2;" u2="x" k="8" />
+<hkern u1="&#xd2;" u2="z" k="8" />
+<hkern u1="&#xd2;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd2;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd2;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd2;" u2="&#x153;" k="4" />
+<hkern u1="&#xd2;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd2;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd2;" u2="&#x178;" k="36" />
+<hkern u1="&#xd2;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd2;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd2;" u2="&#x131;" k="4" />
+<hkern u1="&#xda;" u2="&#x2c;" k="8" />
+<hkern u1="&#xda;" u2="." k="8" />
+<hkern u1="&#xda;" u2="A" k="-16" />
+<hkern u1="&#xda;" u2="J" k="8" />
+<hkern u1="&#xda;" u2="v" k="-16" />
+<hkern u1="&#xda;" u2="y" k="-16" />
+<hkern u1="&#xda;" u2="&#xc4;" k="-16" />
+<hkern u1="&#xda;" u2="&#xc5;" k="-16" />
+<hkern u1="&#xda;" u2="&#xc6;" k="-16" />
+<hkern u1="&#xda;" u2="&#x2026;" k="8" />
+<hkern u1="&#xda;" u2="&#xc0;" k="-16" />
+<hkern u1="&#xda;" u2="&#xc3;" k="-16" />
+<hkern u1="&#xda;" u2="&#x201a;" k="8" />
+<hkern u1="&#xda;" u2="&#x201e;" k="8" />
+<hkern u1="&#xda;" u2="&#xc2;" k="-16" />
+<hkern u1="&#xda;" u2="&#xc1;" k="-16" />
+<hkern u1="&#xdb;" u2="&#x2c;" k="8" />
+<hkern u1="&#xdb;" u2="." k="8" />
+<hkern u1="&#xdb;" u2="A" k="-16" />
+<hkern u1="&#xdb;" u2="J" k="8" />
+<hkern u1="&#xdb;" u2="v" k="-16" />
+<hkern u1="&#xdb;" u2="y" k="-16" />
+<hkern u1="&#xdb;" u2="&#xc4;" k="-16" />
+<hkern u1="&#xdb;" u2="&#xc5;" k="-16" />
+<hkern u1="&#xdb;" u2="&#xc6;" k="-16" />
+<hkern u1="&#xdb;" u2="&#x2026;" k="8" />
+<hkern u1="&#xdb;" u2="&#xc0;" k="-16" />
+<hkern u1="&#xdb;" u2="&#xc3;" k="-16" />
+<hkern u1="&#xdb;" u2="&#x201a;" k="8" />
+<hkern u1="&#xdb;" u2="&#x201e;" k="8" />
+<hkern u1="&#xdb;" u2="&#xc2;" k="-16" />
+<hkern u1="&#xdb;" u2="&#xc1;" k="-16" />
+<hkern u1="&#xd9;" u2="&#x2c;" k="8" />
+<hkern u1="&#xd9;" u2="." k="8" />
+<hkern u1="&#xd9;" u2="A" k="-16" />
+<hkern u1="&#xd9;" u2="J" k="8" />
+<hkern u1="&#xd9;" u2="v" k="-16" />
+<hkern u1="&#xd9;" u2="y" k="-16" />
+<hkern u1="&#xd9;" u2="&#xc4;" k="-16" />
+<hkern u1="&#xd9;" u2="&#xc5;" k="-16" />
+<hkern u1="&#xd9;" u2="&#xc6;" k="-16" />
+<hkern u1="&#xd9;" u2="&#x2026;" k="8" />
+<hkern u1="&#xd9;" u2="&#xc0;" k="-16" />
+<hkern u1="&#xd9;" u2="&#xc3;" k="-16" />
+<hkern u1="&#xd9;" u2="&#x201a;" k="8" />
+<hkern u1="&#xd9;" u2="&#x201e;" k="8" />
+<hkern u1="&#xd9;" u2="&#xc2;" k="-16" />
+<hkern u1="&#xd9;" u2="&#xc1;" k="-16" />
+<hkern u1="&#xa4;" u2="4" k="16" />
+<hkern u1="&#xad;" u2="&#xdd;" k="40" />
+<hkern u1="&#xad;" u2="&#x17d;" k="8" />
+<hkern u1="&#xad;" u2="1" k="16" />
+<hkern u1="&#xad;" u2="7" k="24" />
+<hkern u1="&#xad;" u2="A" k="-8" />
+<hkern u1="&#xad;" u2="T" k="56" />
+<hkern u1="&#xad;" u2="V" k="40" />
+<hkern u1="&#xad;" u2="W" k="32" />
+<hkern u1="&#xad;" u2="X" k="24" />
+<hkern u1="&#xad;" u2="Y" k="40" />
+<hkern u1="&#xad;" u2="Z" k="8" />
+<hkern u1="&#xad;" u2="a" k="8" />
+<hkern u1="&#xad;" u2="c" k="8" />
+<hkern u1="&#xad;" u2="d" k="8" />
+<hkern u1="&#xad;" u2="e" k="8" />
+<hkern u1="&#xad;" u2="o" k="8" />
+<hkern u1="&#xad;" u2="q" k="8" />
+<hkern u1="&#xad;" u2="v" k="8" />
+<hkern u1="&#xad;" u2="x" k="24" />
+<hkern u1="&#xad;" u2="y" k="8" />
+<hkern u1="&#xad;" u2="z" k="24" />
+<hkern u1="&#xad;" u2="&#xc4;" k="-8" />
+<hkern u1="&#xad;" u2="&#xc5;" k="-8" />
+<hkern u1="&#xad;" u2="&#xe7;" k="8" />
+<hkern u1="&#xad;" u2="&#xc6;" k="-8" />
+<hkern u1="&#xad;" u2="&#xe6;" k="8" />
+<hkern u1="&#xad;" u2="&#xc0;" k="-8" />
+<hkern u1="&#xad;" u2="&#xc3;" k="-8" />
+<hkern u1="&#xad;" u2="&#x153;" k="8" />
+<hkern u1="&#xad;" u2="&#x178;" k="40" />
+<hkern u1="&#xad;" u2="&#xc2;" k="-8" />
+<hkern u1="&#xad;" u2="&#xc1;" k="-8" />
+<hkern u1="&#xd0;" u2="&#xdd;" k="36" />
+<hkern u1="&#xd0;" u2="&#x17d;" k="36" />
+<hkern u1="&#xd0;" u2="&#x2c;" k="32" />
+<hkern u1="&#xd0;" u2="." k="32" />
+<hkern u1="&#xd0;" u2="/" k="40" />
+<hkern u1="&#xd0;" u2="?" k="16" />
+<hkern u1="&#xd0;" u2="J" k="36" />
+<hkern u1="&#xd0;" u2="T" k="40" />
+<hkern u1="&#xd0;" u2="V" k="32" />
+<hkern u1="&#xd0;" u2="W" k="48" />
+<hkern u1="&#xd0;" u2="X" k="28" />
+<hkern u1="&#xd0;" u2="Y" k="36" />
+<hkern u1="&#xd0;" u2="Z" k="36" />
+<hkern u1="&#xd0;" u2="a" k="4" />
+<hkern u1="&#xd0;" u2="b" k="8" />
+<hkern u1="&#xd0;" u2="c" k="4" />
+<hkern u1="&#xd0;" u2="d" k="4" />
+<hkern u1="&#xd0;" u2="e" k="4" />
+<hkern u1="&#xd0;" u2="h" k="8" />
+<hkern u1="&#xd0;" u2="k" k="8" />
+<hkern u1="&#xd0;" u2="l" k="8" />
+<hkern u1="&#xd0;" u2="m" k="4" />
+<hkern u1="&#xd0;" u2="n" k="4" />
+<hkern u1="&#xd0;" u2="o" k="4" />
+<hkern u1="&#xd0;" u2="p" k="4" />
+<hkern u1="&#xd0;" u2="q" k="4" />
+<hkern u1="&#xd0;" u2="r" k="4" />
+<hkern u1="&#xd0;" u2="u" k="4" />
+<hkern u1="&#xd0;" u2="x" k="8" />
+<hkern u1="&#xd0;" u2="z" k="8" />
+<hkern u1="&#xd0;" u2="&#xe7;" k="4" />
+<hkern u1="&#xd0;" u2="&#xe6;" k="4" />
+<hkern u1="&#xd0;" u2="&#x2026;" k="32" />
+<hkern u1="&#xd0;" u2="&#x153;" k="4" />
+<hkern u1="&#xd0;" u2="&#x201c;" k="16" />
+<hkern u1="&#xd0;" u2="&#x2018;" k="16" />
+<hkern u1="&#xd0;" u2="&#x178;" k="36" />
+<hkern u1="&#xd0;" u2="&#x201a;" k="32" />
+<hkern u1="&#xd0;" u2="&#x201e;" k="32" />
+<hkern u1="&#xd0;" u2="&#x131;" k="4" />
+
+</font>
+</defs>
+<text x="40" y="40" font-family="Omnes_ATT W02 Medium Italic" font-size="30" fill="#933" >ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz AÃÀÂÄÅÃÆ CÇ Dà EÉÈÊË I à Ì ÃŽ à NÑ</text>
+<text x="40" y="80" font-family="Omnes_ATT W02 Medium Italic" font-size="30" fill="#933" >OÓÒÔÖÕØŒ SÅ  UÚÙÛÜ Yß ZŽ Þ aáàâäåãæ cç dð eéèêë i ı í ì î ï nñ oóòôöõøœ sšß uúùûü yýÿ zž</text>
+<text x="40" y="120" font-family="Omnes_ATT W02 Medium Italic" font-size="30" fill="#933" >þ 1234567890 ½ ¼ ¾ % ‰ $¢£¥ƒ€¤ † ‡ § ¶ # ^~µ +×± &lt; = &gt; ÷¬ !¡?¿ &quot; &amp; &apos; * ° . , : ; () [ \ ] {} / |</text>
+<text x="40" y="160" font-family="Omnes_ATT W02 Medium Italic" font-size="30" fill="#933" >¦ _ ‚ „ … ‹› «» ‘ ’ “ †• ­ - – — @ © ® ™ ªº ¹²³ ´ ` ˆ ˜ ¨ ¯ · ¸</text>
+</svg>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.ttf b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.ttf
new file mode 100644
index 0000000000..46bfd531b0
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.ttf
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.woff b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.woff
new file mode 100644
index 0000000000..20419ed3b3
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/Omnes_ATTW02MediumItalic.woff
Binary files differ
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/demo-async.htm b/catalog-ui/app/styles/fonts/OmnesATT/demo-async.htm
new file mode 100644
index 0000000000..f7a43e6a34
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/demo-async.htm
@@ -0,0 +1,169 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Webfonts Demo</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <style type="text/css">
+ body, textarea { font-family:Arial, Sans-Serif; line-height:1.6em; font-size:13px; }
+ h1 { font-size:3em; }
+ h2, h3 { color:#999; }
+ .content { width:780px; margin: 20px auto 40px; }
+ .demo { font-weight:bold; font-style:normal; color: #999 }
+ .fontdisplay {font-size:2em;line-height:150%;margin: 10px 40px 0px 0px;}
+ .guidelines { color:#333; }
+ pre { padding:0 0 0 20px; }
+ hr { border:0; border-bottom:1px solid #537094; margin:20px 0; }
+ .link { color: #537094;text-decoration:none; font-weight:bold;}
+ .uc {text-transform:uppercase}
+ </style>
+ <style type="text/css">
+@font-face{
+font-family:"Omnes_ATT W02";
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix");
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix") format("eot"),url("Fonts/af464c29-9cea-4b8d-81cc-00cd08fabfe7.woff") format("woff"),url("Fonts/20058a45-f038-4d9e-bce0-1c9ccc432bad.ttf") format("truetype"),url("Fonts/2c4d0064-5285-4d7f-9d73-9c5b3daff7d0.svg#2c4d0064-5285-4d7f-9d73-9c5b3daff7d0") format("svg");
+}
+@font-face{
+font-family:"OmnesATT W02 Bold Italic";
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix");
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix") format("eot"),url("Fonts/0df984e0-565b-416a-abda-22069b9ef7b1.woff") format("woff"),url("Fonts/7a9da78b-7de6-44b0-a74c-0c613875df92.ttf") format("truetype"),url("Fonts/e7a56966-375b-4700-b47f-74def0db152f.svg#e7a56966-375b-4700-b47f-74def0db152f") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Italic";
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix");
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix") format("eot"),url("Fonts/bb3ddfa8-3131-4094-a60d-1677ed2de9a2.woff") format("woff"),url("Fonts/7a1df320-73f0-4877-95dd-3db53c979c64.ttf") format("truetype"),url("Fonts/bd6a4532-d1c6-4200-99fc-663f46a5c842.svg#bd6a4532-d1c6-4200-99fc-663f46a5c842") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light Italic";
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix");
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix") format("eot"),url("Fonts/e085f067-0b0b-4e72-875b-2c4e928ed237.woff") format("woff"),url("Fonts/db0b8d3d-42f5-4560-9de0-085b0cb49164.ttf") format("truetype"),url("Fonts/cda8110c-0e81-428d-9882-3725f543b047.svg#cda8110c-0e81-428d-9882-3725f543b047") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Bold";
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix");
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix") format("eot"),url("Fonts/e2d6d300-111b-4caa-8ff8-192381da8b2b.woff") format("woff"),url("Fonts/130418a5-b1bf-4d14-9c79-f6c8fdca2e55.ttf") format("truetype"),url("Fonts/8177ad65-0a92-4dec-8648-e0f73461f572.svg#8177ad65-0a92-4dec-8648-e0f73461f572") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium Italic";
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix");
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix") format("eot"),url("Fonts/bd70a9c2-aae2-4d25-8c49-0913d41440fa.woff") format("woff"),url("Fonts/a4c39d18-abbe-4310-8ad7-b1c9a29675d2.ttf") format("truetype"),url("Fonts/94a9ebf3-400e-42d1-920c-ac205b5ea560.svg#94a9ebf3-400e-42d1-920c-ac205b5ea560") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light";
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix");
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix") format("eot"),url("Fonts/e0d8e81b-5325-424c-8f04-b0dc028ba635.woff") format("woff"),url("Fonts/6cbee459-f642-43e9-bf8b-389945b8c9d1.ttf") format("truetype"),url("Fonts/14143264-9384-4609-907b-2824b4892587.svg#14143264-9384-4609-907b-2824b4892587") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium";
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix");
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix") format("eot"),url("Fonts/b7f5f2fd-530a-477b-9283-bb182b23baa4.woff") format("woff"),url("Fonts/bb594ac8-7f76-47c5-9954-a7da05208c2e.ttf") format("truetype"),url("Fonts/822f5de4-eb23-48fb-aa3e-7304b432a38d.svg#822f5de4-eb23-48fb-aa3e-7304b432a38d") format("svg");
+}
+</style>
+<script type="text/javascript">
+var MTIProjectId='f017728a-fdf1-487c-bc0a-8b9166706a0a';
+ (function() {
+ var mtiTracking = document.createElement('script');
+ mtiTracking.type='text/javascript';
+ mtiTracking.async='true';
+ mtiTracking.src=('https:'==document.location.protocol?'https:':'http:')+'//fast.fonts.net/t/trackingCode.js';
+ (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild( mtiTracking );
+ })();
+</script>
+</head>
+<body>
+ <div class="content">
+ <h1 class="demo">Fonts.com Web fonts</h1>
+ <p style="margin-top: -10px; font-size: 1.5em; color: #666"><b>@font-face implementation instructions</b></p>
+ <p>The asynchronous script loads in the background allowing the other elements of your website to load first and undeterred.</p>
+ <hr />
+ <p class="uc">The fonts contained in this kit are:</p>
+ <div class="fontdisplay">
+ <div style="font-family:'Omnes_ATT W02 Italic';"> Omnes_ATT W02 Italic </div>
+<div style="font-family:'Omnes_ATT W02 Light';"> Omnes_ATT W02 Light </div>
+<div style="font-family:'Omnes_ATT W02 Light Italic';"> Omnes_ATT W02 Light Italic </div>
+<div style="font-family:'Omnes_ATT W02 Medium';"> Omnes_ATT W02 Medium </div>
+<div style="font-family:'Omnes_ATT W02 Medium Italic';"> Omnes_ATT W02 Medium Italic </div>
+<div style="font-family:'Omnes_ATT W02';"> Omnes_ATT W02 Regular </div>
+<div style="font-family:'Omnes_ATT W02 Bold';"> Omnes_ATT W02 Bold </div>
+<div style="font-family:'OmnesATT W02 Bold Italic';"> Omnes_ATT W02 Bold Italic </div>
+
+ </div>
+ <hr />
+ Click here for <a class="link uc" href="demo.htm">css only</a> web fonts implementation
+ <hr />
+ <div class="guidelines">
+ <h2>CSS Implementation Guidelines</h2>
+ <p>You and any third party web font hosting service are responsible for ensuring that the font software in the self-hosting kit, in its original format, can only be used on the Web Sites for which the self-hosting kit was downloaded and cannot be used or referenced by any other web site. This includes, but is not limited to installing adequate technical protection measures that restrict the use and/or access to the font software, for instance by utilizing JavaScript or access control mechanism for cross-origin resource sharing and protecting against use on web sites other than the Web Sites for which the self-hosting kit was downloaded by restricting domain access only to such Web Sites. You must also retain the pageview tracking code on any Web Site that you self-host. In the event this Agreement terminates for any reason, the font software included with the self-hosting kit must be deleted from the server and all copies must be destroyed or returned to Monotype Imaging.</p>
+ <p>View <a class="link" href="http://new.fonts.com/info/legal/eula/web-fonts" target="_blank">"WEB FONT SOFTWARE" LICENSE AGREEMENT</a></p>
+ <h2>Asynchronous Implementation (Requires JavaScript)</h2>
+ <p>Font file names have been obfuscated to protect the font software. You can identify font format based on file ending:</p>
+ <ul>
+ <li>1 - TrueType (ttf)</li>
+ <li>2 - Embedded OpenType (eot)</li>
+ <li>3 - Web Open Font Format (woff)</li>
+ <li>4 - Scalable Vector Graphics (svg)</li>
+ </ul>
+ <p class="uc" style="margin: 20px 0px -20px;"><b>Copy and paste following code in head section of the page</b></p>
+ <hr />
+ <textarea style="width: 830px; height: 350px; border: 1 #fff solid; background-color: #fcfcfc;"><style type="text/css">
+@font-face{
+font-family:"Omnes_ATT W02";
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix");
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix") format("eot"),url("Fonts/af464c29-9cea-4b8d-81cc-00cd08fabfe7.woff") format("woff"),url("Fonts/20058a45-f038-4d9e-bce0-1c9ccc432bad.ttf") format("truetype"),url("Fonts/2c4d0064-5285-4d7f-9d73-9c5b3daff7d0.svg#2c4d0064-5285-4d7f-9d73-9c5b3daff7d0") format("svg");
+}
+@font-face{
+font-family:"OmnesATT W02 Bold Italic";
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix");
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix") format("eot"),url("Fonts/0df984e0-565b-416a-abda-22069b9ef7b1.woff") format("woff"),url("Fonts/7a9da78b-7de6-44b0-a74c-0c613875df92.ttf") format("truetype"),url("Fonts/e7a56966-375b-4700-b47f-74def0db152f.svg#e7a56966-375b-4700-b47f-74def0db152f") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Italic";
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix");
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix") format("eot"),url("Fonts/bb3ddfa8-3131-4094-a60d-1677ed2de9a2.woff") format("woff"),url("Fonts/7a1df320-73f0-4877-95dd-3db53c979c64.ttf") format("truetype"),url("Fonts/bd6a4532-d1c6-4200-99fc-663f46a5c842.svg#bd6a4532-d1c6-4200-99fc-663f46a5c842") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light Italic";
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix");
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix") format("eot"),url("Fonts/e085f067-0b0b-4e72-875b-2c4e928ed237.woff") format("woff"),url("Fonts/db0b8d3d-42f5-4560-9de0-085b0cb49164.ttf") format("truetype"),url("Fonts/cda8110c-0e81-428d-9882-3725f543b047.svg#cda8110c-0e81-428d-9882-3725f543b047") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Bold";
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix");
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix") format("eot"),url("Fonts/e2d6d300-111b-4caa-8ff8-192381da8b2b.woff") format("woff"),url("Fonts/130418a5-b1bf-4d14-9c79-f6c8fdca2e55.ttf") format("truetype"),url("Fonts/8177ad65-0a92-4dec-8648-e0f73461f572.svg#8177ad65-0a92-4dec-8648-e0f73461f572") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium Italic";
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix");
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix") format("eot"),url("Fonts/bd70a9c2-aae2-4d25-8c49-0913d41440fa.woff") format("woff"),url("Fonts/a4c39d18-abbe-4310-8ad7-b1c9a29675d2.ttf") format("truetype"),url("Fonts/94a9ebf3-400e-42d1-920c-ac205b5ea560.svg#94a9ebf3-400e-42d1-920c-ac205b5ea560") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light";
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix");
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix") format("eot"),url("Fonts/e0d8e81b-5325-424c-8f04-b0dc028ba635.woff") format("woff"),url("Fonts/6cbee459-f642-43e9-bf8b-389945b8c9d1.ttf") format("truetype"),url("Fonts/14143264-9384-4609-907b-2824b4892587.svg#14143264-9384-4609-907b-2824b4892587") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium";
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix");
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix") format("eot"),url("Fonts/b7f5f2fd-530a-477b-9283-bb182b23baa4.woff") format("woff"),url("Fonts/bb594ac8-7f76-47c5-9954-a7da05208c2e.ttf") format("truetype"),url("Fonts/822f5de4-eb23-48fb-aa3e-7304b432a38d.svg#822f5de4-eb23-48fb-aa3e-7304b432a38d") format("svg");
+}
+</style>
+<script type="text/javascript">
+var MTIProjectId='f017728a-fdf1-487c-bc0a-8b9166706a0a';
+ (function() {
+ var mtiTracking = document.createElement('script');
+ mtiTracking.type='text/javascript';
+ mtiTracking.async='true';
+ mtiTracking.src=('https:'==document.location.protocol?'https:':'http:')+'//fast.fonts.net/t/trackingCode.js';
+ (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild( mtiTracking );
+ })();
+</script></textarea>
+ <h3>Licensing information should be included within the CSS</h3>
+<pre>/*
+This CSS resource incorporates links to font software which is the valuable copyrighted
+property of Monotype Imaging and/or its suppliers. You may not attempt to copy, install,
+redistribute, convert, modify or reverse engineer this font software. Please contact Monotype
+Imaging with any questions regarding Web Fonts: http://webfonts.fonts.com
+*/</pre>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catalog-ui/app/styles/fonts/OmnesATT/demo.htm b/catalog-ui/app/styles/fonts/OmnesATT/demo.htm
new file mode 100644
index 0000000000..edb62ad34a
--- /dev/null
+++ b/catalog-ui/app/styles/fonts/OmnesATT/demo.htm
@@ -0,0 +1,155 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Webfonts Demo</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <style type="text/css">
+ body, textarea { font-family:Arial, Sans-Serif; line-height:1.6em; font-size:13px; }
+ h1 { font-size:3em; }
+ h2, h3 { color:#999; }
+ .content { width:780px; margin: 20px auto 40px; }
+ .demo { font-weight:bold; font-style:normal; color: #999 }
+ .fontdisplay {font-size:2em;line-height:150%;margin: 10px 40px 0px 0px;}
+ .guidelines { color:#333; }
+ pre { padding:0 0 0 20px; }
+ hr { border:0; border-bottom:1px solid #537094; margin:20px 0; }
+ .link { color: #537094;text-decoration:none; font-weight:bold;}
+ .uc {text-transform:uppercase}
+ </style>
+ <style type="text/css">
+
+@import url("http://fast.fonts.net/t/1.css?apiType=css&projectid=f017728a-fdf1-487c-bc0a-8b9166706a0a");
+@font-face{
+font-family:"Omnes_ATT W02";
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix");
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix") format("eot"),url("Fonts/af464c29-9cea-4b8d-81cc-00cd08fabfe7.woff") format("woff"),url("Fonts/20058a45-f038-4d9e-bce0-1c9ccc432bad.ttf") format("truetype"),url("Fonts/2c4d0064-5285-4d7f-9d73-9c5b3daff7d0.svg#2c4d0064-5285-4d7f-9d73-9c5b3daff7d0") format("svg");
+}
+@font-face{
+font-family:"OmnesATT W02 Bold Italic";
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix");
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix") format("eot"),url("Fonts/0df984e0-565b-416a-abda-22069b9ef7b1.woff") format("woff"),url("Fonts/7a9da78b-7de6-44b0-a74c-0c613875df92.ttf") format("truetype"),url("Fonts/e7a56966-375b-4700-b47f-74def0db152f.svg#e7a56966-375b-4700-b47f-74def0db152f") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Italic";
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix");
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix") format("eot"),url("Fonts/bb3ddfa8-3131-4094-a60d-1677ed2de9a2.woff") format("woff"),url("Fonts/7a1df320-73f0-4877-95dd-3db53c979c64.ttf") format("truetype"),url("Fonts/bd6a4532-d1c6-4200-99fc-663f46a5c842.svg#bd6a4532-d1c6-4200-99fc-663f46a5c842") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light Italic";
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix");
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix") format("eot"),url("Fonts/e085f067-0b0b-4e72-875b-2c4e928ed237.woff") format("woff"),url("Fonts/db0b8d3d-42f5-4560-9de0-085b0cb49164.ttf") format("truetype"),url("Fonts/cda8110c-0e81-428d-9882-3725f543b047.svg#cda8110c-0e81-428d-9882-3725f543b047") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Bold";
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix");
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix") format("eot"),url("Fonts/e2d6d300-111b-4caa-8ff8-192381da8b2b.woff") format("woff"),url("Fonts/130418a5-b1bf-4d14-9c79-f6c8fdca2e55.ttf") format("truetype"),url("Fonts/8177ad65-0a92-4dec-8648-e0f73461f572.svg#8177ad65-0a92-4dec-8648-e0f73461f572") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium Italic";
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix");
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix") format("eot"),url("Fonts/bd70a9c2-aae2-4d25-8c49-0913d41440fa.woff") format("woff"),url("Fonts/a4c39d18-abbe-4310-8ad7-b1c9a29675d2.ttf") format("truetype"),url("Fonts/94a9ebf3-400e-42d1-920c-ac205b5ea560.svg#94a9ebf3-400e-42d1-920c-ac205b5ea560") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light";
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix");
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix") format("eot"),url("Fonts/e0d8e81b-5325-424c-8f04-b0dc028ba635.woff") format("woff"),url("Fonts/6cbee459-f642-43e9-bf8b-389945b8c9d1.ttf") format("truetype"),url("Fonts/14143264-9384-4609-907b-2824b4892587.svg#14143264-9384-4609-907b-2824b4892587") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium";
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix");
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix") format("eot"),url("Fonts/b7f5f2fd-530a-477b-9283-bb182b23baa4.woff") format("woff"),url("Fonts/bb594ac8-7f76-47c5-9954-a7da05208c2e.ttf") format("truetype"),url("Fonts/822f5de4-eb23-48fb-aa3e-7304b432a38d.svg#822f5de4-eb23-48fb-aa3e-7304b432a38d") format("svg");
+}
+
+ </style>
+</head>
+<body>
+ <div class="content">
+ <h1 class="demo">Fonts.com Web fonts</h1>
+ <p style="margin-top:-10px; font-size:1.5em;color:#666"><b>@font-face implementation instructions</b></p>
+ <hr />
+ <p class="uc">The fonts contained in this kit are:</p>
+ <div class="fontdisplay">
+<div style="font-family:'Omnes_ATT W02 Italic';"> Omnes_ATT W02 Italic </div>
+<div style="font-family:'Omnes_ATT W02 Light';"> Omnes_ATT W02 Light </div>
+<div style="font-family:'Omnes_ATT W02 Light Italic';"> Omnes_ATT W02 Light Italic </div>
+<div style="font-family:'Omnes_ATT W02 Medium';"> Omnes_ATT W02 Medium </div>
+<div style="font-family:'Omnes_ATT W02 Medium Italic';"> Omnes_ATT W02 Medium Italic </div>
+<div style="font-family:'Omnes_ATT W02';"> Omnes_ATT W02 Regular </div>
+<div style="font-family:'Omnes_ATT W02 Bold';"> Omnes_ATT W02 Bold </div>
+<div style="font-family:'OmnesATT W02 Bold Italic';"> Omnes_ATT W02 Bold Italic </div>
+
+ </div>
+ <hr />
+ Click here for <a class="link uc" href="demo-async.htm">asynchronous</a> web fonts implementation
+ <hr />
+ <div class="guidelines">
+ <h2>CSS Implementation Guidelines</h2>
+ <p>You and any third party web font hosting service are responsible for ensuring that the font software in the self-hosting kit, in its original format, can only be used on the Web Sites for which the self-hosting kit was downloaded and cannot be used or referenced by any other web site. This includes, but is not limited to installing adequate technical protection measures that restrict the use and/or access to the font software, for instance by utilizing JavaScript or access control mechanism for cross-origin resource sharing and protecting against use on web sites other than the Web Sites for which the self-hosting kit was downloaded by restricting domain access only to such Web Sites. You must also retain the pageview tracking code on any Web Site that you self-host. In the event this Agreement terminates for any reason, the font software included with the self-hosting kit must be deleted from the server and all copies must be destroyed or returned to Monotype Imaging.</p>
+ <p>View <a class="link" href="http://new.fonts.com/info/legal/eula/web-fonts" target="_blank">"WEB FONT SOFTWARE" LICENSE AGREEMENT</a></p>
+ <p>Font file names have been obfuscated to protect the font software. You can identify font format based on file ending:</p>
+ <ul>
+ <li>1 - TrueType (ttf)</li>
+ <li>2 - Embedded OpenType (eot)</li>
+ <li>3 - Web Open Font Format (woff)</li>
+ <li>4 - Scalable Vector Graphics (svg)</li>
+ </ul>
+ <p class="uc" style="margin: 20px 0px -20px;"><b>Copy and paste following code in head section of the page</b></p>
+ <hr />
+ <textarea style="width:830px; height:350px; border:1 #fff solid; background-color:#fcfcfc;"><style type="text/css">
+
+@import url("http://fast.fonts.net/t/1.css?apiType=css&projectid=f017728a-fdf1-487c-bc0a-8b9166706a0a");
+@font-face{
+font-family:"Omnes_ATT W02";
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix");
+src:url("Fonts/02b2af6d-a4c1-4c4b-8f11-baee44545140.eot?#iefix") format("eot"),url("Fonts/af464c29-9cea-4b8d-81cc-00cd08fabfe7.woff") format("woff"),url("Fonts/20058a45-f038-4d9e-bce0-1c9ccc432bad.ttf") format("truetype"),url("Fonts/2c4d0064-5285-4d7f-9d73-9c5b3daff7d0.svg#2c4d0064-5285-4d7f-9d73-9c5b3daff7d0") format("svg");
+}
+@font-face{
+font-family:"OmnesATT W02 Bold Italic";
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix");
+src:url("Fonts/9f7cc8cc-9e14-4f09-9e6c-6232451a64c0.eot?#iefix") format("eot"),url("Fonts/0df984e0-565b-416a-abda-22069b9ef7b1.woff") format("woff"),url("Fonts/7a9da78b-7de6-44b0-a74c-0c613875df92.ttf") format("truetype"),url("Fonts/e7a56966-375b-4700-b47f-74def0db152f.svg#e7a56966-375b-4700-b47f-74def0db152f") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Italic";
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix");
+src:url("Fonts/6a5c02a4-edd9-4454-a231-1b68e1c8cab9.eot?#iefix") format("eot"),url("Fonts/bb3ddfa8-3131-4094-a60d-1677ed2de9a2.woff") format("woff"),url("Fonts/7a1df320-73f0-4877-95dd-3db53c979c64.ttf") format("truetype"),url("Fonts/bd6a4532-d1c6-4200-99fc-663f46a5c842.svg#bd6a4532-d1c6-4200-99fc-663f46a5c842") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light Italic";
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix");
+src:url("Fonts/3021a9bb-7932-4845-b597-8abd29895a4b.eot?#iefix") format("eot"),url("Fonts/e085f067-0b0b-4e72-875b-2c4e928ed237.woff") format("woff"),url("Fonts/db0b8d3d-42f5-4560-9de0-085b0cb49164.ttf") format("truetype"),url("Fonts/cda8110c-0e81-428d-9882-3725f543b047.svg#cda8110c-0e81-428d-9882-3725f543b047") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Bold";
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix");
+src:url("Fonts/f6a9e81d-954e-414b-9f0b-23e854036e8f.eot?#iefix") format("eot"),url("Fonts/e2d6d300-111b-4caa-8ff8-192381da8b2b.woff") format("woff"),url("Fonts/130418a5-b1bf-4d14-9c79-f6c8fdca2e55.ttf") format("truetype"),url("Fonts/8177ad65-0a92-4dec-8648-e0f73461f572.svg#8177ad65-0a92-4dec-8648-e0f73461f572") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium Italic";
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix");
+src:url("Fonts/c6ddf40e-258b-4f6c-92ca-f8bacdfe6883.eot?#iefix") format("eot"),url("Fonts/bd70a9c2-aae2-4d25-8c49-0913d41440fa.woff") format("woff"),url("Fonts/a4c39d18-abbe-4310-8ad7-b1c9a29675d2.ttf") format("truetype"),url("Fonts/94a9ebf3-400e-42d1-920c-ac205b5ea560.svg#94a9ebf3-400e-42d1-920c-ac205b5ea560") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Light";
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix");
+src:url("Fonts/74c219c7-aeb3-43de-828f-4f3e06cd2a16.eot?#iefix") format("eot"),url("Fonts/e0d8e81b-5325-424c-8f04-b0dc028ba635.woff") format("woff"),url("Fonts/6cbee459-f642-43e9-bf8b-389945b8c9d1.ttf") format("truetype"),url("Fonts/14143264-9384-4609-907b-2824b4892587.svg#14143264-9384-4609-907b-2824b4892587") format("svg");
+}
+@font-face{
+font-family:"Omnes_ATT W02 Medium";
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix");
+src:url("Fonts/e2bed2c5-3d6e-499f-b875-c4a90ef54dd4.eot?#iefix") format("eot"),url("Fonts/b7f5f2fd-530a-477b-9283-bb182b23baa4.woff") format("woff"),url("Fonts/bb594ac8-7f76-47c5-9954-a7da05208c2e.ttf") format("truetype"),url("Fonts/822f5de4-eb23-48fb-aa3e-7304b432a38d.svg#822f5de4-eb23-48fb-aa3e-7304b432a38d") format("svg");
+}
+
+</style>
+
+</textarea>
+ <h3>Licensing information should be included within the CSS</h3>
+<pre>/*
+This CSS resource incorporates links to font software which is the valuable copyrighted
+property of Monotype Imaging and/or its suppliers. You may not attempt to copy, install,
+redistribute, convert, modify or reverse engineer this font software. Please contact Monotype
+Imaging with any questions regarding Web Fonts: http://webfonts.fonts.com
+*/</pre>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/catalog-ui/app/styles/form-elements.less b/catalog-ui/app/styles/form-elements.less
new file mode 100644
index 0000000000..9f8146d3f5
--- /dev/null
+++ b/catalog-ui/app/styles/form-elements.less
@@ -0,0 +1,199 @@
+// ---------------------------------------------------------------------------------------------------
+// Form
+// ---------------------------------------------------------------------------------------------------
+.w-sdc-form {
+ padding: 0;
+
+ .w-sdc-form-actions-container {
+ margin: 20px auto; // -10px;
+ width: 100%; // 320px;
+ }
+
+ .w-sdc-form-columns-wrapper {
+
+ display: flex;
+ justify-content: space-between;
+
+ .w-sdc-form-column {
+ width: 100%;
+ margin: 0;
+ padding: 0 6% 0 0;
+ text-align: left;
+
+ &:last-child {
+ padding: 0;
+ }
+ }
+
+ }
+
+ .i-sdc-form-item {
+ margin-bottom: 10px;
+ position: relative;
+ text-align: left;
+
+ &.error {
+ input[type="text"],
+ input[type="url"],
+ input[type="number"],
+ input[type="password"],
+ select,
+ textarea {
+ .h_1;
+ border-color: @main_color_g;
+ outline: none;
+ box-sizing: border-box;
+
+ &:hover {
+ .h_1;
+ border-color: @main_color_g;
+ }
+ }
+ label {
+ .m_14_m;
+ }
+ }
+
+ label {
+ .m_14_m;
+ margin: 0 0 2px 0;
+ padding: 0;
+ display: block;
+
+ &.required::before {
+ color: #f33;
+ content: '*';
+ margin: 0 4px 0 0;
+ }
+ }
+
+ input[type="text"],
+ input[type="url"],
+ input[type="number"],
+ input[type="password"],
+ select,
+ textarea {
+ .m_14_r;
+ background-color: @main_color_p;
+ .border-radius(2px);
+ margin: 0;
+ padding: 0;
+ border: solid 1px @main_color_o;
+ height: 30px;
+ width: 100%;
+ display: block;
+
+ &::-webkit-input-placeholder { font-style: italic; } /* Safari, Chrome and Opera */
+ &:-moz-placeholder { font-style: italic; } /* Firefox 18- */
+ &::-moz-placeholder { font-style: italic; } /* Firefox 19+ */
+ &:-ms-input-placeholder { font-style: italic; } /* IE 10+ */
+ &:-ms-input-placeholder { font-style: italic; } /* Edge */
+
+ &:disabled {
+ opacity: 0.4;
+ }
+ &.view-mode {
+ opacity: 1;
+ border: solid 1px @main_color_o;
+ background-color: #f8f8f8 !important;
+ pointer-events: none;
+ cursor: auto;
+ }
+
+ }
+
+ input[type="text"],
+ input[type="url"],
+ input[type="number"],
+ input[type="password"] { padding: 4px 10px;}
+ select { padding: 4px 7px;}
+ textarea { padding: 6px 10px;}
+
+ select {
+ optgroup {
+ color: @color_a;
+ option {
+ color: @color_b;
+ }
+
+ }
+ }
+
+ textarea {
+ min-height: 110px;
+ resize: none;
+ }
+
+ }
+
+ .i-sdc-form-item-error-icon {
+ .sprite;
+ .exclamation-mark-red-icon;
+ width: 13px;
+ height: 11px;
+ position: absolute;
+
+ .i-sdc-form-item-error-message {
+ .bg_h;
+ .c_3;
+ cursor: default;
+ position: absolute;
+ top: -10px;
+ left: -8px;
+ height: 30px;
+ padding: 6px 10px 6px 30px;
+ z-index: 999;
+ text-align: left;
+ .border-radius-top-right(4px);
+ .border-radius-bottom-right(4px);
+ .box-shadow(0px 1px 1px 0px rgba(24, 24, 25, 0.43));
+
+ &:before {
+ .arrow(5px, @color_h);
+ }
+
+ span {
+ white-space: nowrap;
+ }
+
+ span.error {
+ .sprite;
+ .exclamation-mark-red-icon;
+ position: absolute;
+ top: 9px;
+ left: 8px;
+ }
+
+ span.ok {
+ .c_3;
+ .hand;
+ position: absolute;
+ right: 10px;
+ text-decoration: underline;
+ }
+
+ .i-sdc-form-item-error-icon-open {
+ .sprite;
+ .exclamation-mark-white-icon;
+ width: 13px;
+ height: 11px;
+ position: absolute;
+ top: 9px;
+ left: 8px;
+ }
+
+ }
+
+ }
+
+ .input-error {
+ .q_12_m;
+ text-align: left;
+ }
+
+ .input-error-file-upload {
+ .input-error;
+ margin-top: -10px;
+ margin-bottom: 10px;
+ }
+}
diff --git a/catalog-ui/app/styles/global.less b/catalog-ui/app/styles/global.less
new file mode 100644
index 0000000000..9faf4aeb0e
--- /dev/null
+++ b/catalog-ui/app/styles/global.less
@@ -0,0 +1,52 @@
+html {
+ height: 100%;
+}
+
+body {
+ .b_7;
+ height: 100%;
+ margin: auto;
+ position: relative;
+}
+
+* {
+ box-sizing: border-box;
+ margin: 0;
+}
+
+html, body {
+ height: 100%;
+ overflow: hidden;
+}
+
+a {
+ text-decoration: none;
+}
+
+canvas {
+ outline: none;
+}
+
+.download-artifact {
+ .hand;
+}
+
+// ---------------------------------------------------------------------------------------------------
+// bootstrap overrides
+// ---------------------------------------------------------------------------------------------------
+html {
+
+ .w-sdc-resource-container {
+ padding: 20px 0;
+ }
+
+ // Hover on notification should keep it with opacity 1.
+ .ui-notification {
+ &.clickable {
+ &:hover {
+ opacity: 1;
+ }
+ }
+ }
+
+}
diff --git a/catalog-ui/app/styles/images/anonymous.jpg b/catalog-ui/app/styles/images/anonymous.jpg
new file mode 100644
index 0000000000..863af35321
--- /dev/null
+++ b/catalog-ui/app/styles/images/anonymous.jpg
Binary files differ
diff --git a/catalog-ui/app/styles/images/att_logo_white.png b/catalog-ui/app/styles/images/att_logo_white.png
new file mode 100644
index 0000000000..6321b7582d
--- /dev/null
+++ b/catalog-ui/app/styles/images/att_logo_white.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/AttachesTo.svg b/catalog-ui/app/styles/images/relationship-icons/AttachesTo.svg
new file mode 100644
index 0000000000..68b57a08c8
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/AttachesTo.svg
@@ -0,0 +1,37 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ stroke: #fff;
+ stroke-linejoin: round;
+ stroke-width: 2px;
+ filter: url(#drop-shadow-1);
+ fill-opacity: 0;
+ }
+ </style>
+
+ <filter id="drop-shadow-1" filterUnits="userSpaceOnUse">
+ <feOffset dx="1.714" dy="1.03" in="SourceAlpha"/>
+ <feGaussianBlur result="dropBlur"/>
+ <feFlood flood-opacity="0"/>
+ <feComposite operator="in" in2="dropBlur" result="dropShadowComp"/>
+ <feComposite in="SourceGraphic" result="shadowed"/>
+ </filter>
+ </defs>
+ <g>
+ <circle cx="12.578" cy="12.109" r="11.422" class="cls-1"/>
+ <g style="fill: #fff; filter: url(#drop-shadow-1)">
+ <circle cx="15.5" cy="12.5" r="2.5" id="circle-1" style="stroke: inherit; filter: none; fill: inherit" class="cls-2"/>
+ </g>
+ <use xlink:href="#circle-1" style="stroke: #fff; filter: none; fill: none"/>
+ <g style="fill: #fff; filter: url(#drop-shadow-1)">
+ <circle cx="9.5" cy="12.5" r="2.5" id="circle-2" style="stroke: inherit; filter: none; fill: inherit" class="cls-2"/>
+ </g>
+ <use xlink:href="#circle-2" style="stroke: #fff; filter: none; fill: none"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/BindsTo.svg b/catalog-ui/app/styles/images/relationship-icons/BindsTo.svg
new file mode 100644
index 0000000000..ae5647dda9
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/BindsTo.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <g>
+ <circle cx="12.578" cy="12.734" r="11.422" class="cls-1"/>
+ <path d="M17.500,14.000 C16.672,14.000 16.000,13.328 16.000,12.500 C16.000,11.672 16.672,11.000 17.500,11.000 C18.328,11.000 19.000,11.672 19.000,12.500 C19.000,13.328 18.328,14.000 17.500,14.000 ZM12.500,14.000 C11.672,14.000 11.000,13.328 11.000,12.500 C11.000,11.672 11.672,11.000 12.500,11.000 C13.328,11.000 14.000,11.672 14.000,12.500 C14.000,13.328 13.328,14.000 12.500,14.000 ZM7.500,14.000 C6.672,14.000 6.000,13.328 6.000,12.500 C6.000,11.672 6.672,11.000 7.500,11.000 C8.328,11.000 9.000,11.672 9.000,12.500 C9.000,13.328 8.328,14.000 7.500,14.000 Z" class="cls-2"/>
+ <path d="M6.000,17.000 L6.000,16.000 L19.000,16.000 L19.000,17.000 L6.000,17.000 ZM6.000,8.000 L19.000,8.000 L19.000,9.000 L6.000,9.000 L6.000,8.000 Z" class="cls-2"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/ConnectedTo.svg b/catalog-ui/app/styles/images/relationship-icons/ConnectedTo.svg
new file mode 100644
index 0000000000..4ee7672305
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/ConnectedTo.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <g>
+ <circle cx="12.5" cy="12.5" r="11.5" class="cls-1"/>
+ <path d="M16.016,14.813 C14.747,14.813 13.719,13.777 13.719,12.500 C13.719,11.223 14.747,10.187 16.016,10.187 C17.284,10.187 18.313,11.223 18.313,12.500 C18.313,13.777 17.284,14.813 16.016,14.813 ZM7.984,14.813 C6.716,14.813 5.687,13.777 5.687,12.500 C5.687,11.223 6.716,10.187 7.984,10.187 C9.253,10.187 10.281,11.223 10.281,12.500 C10.281,13.777 9.253,14.813 7.984,14.813 Z" class="cls-2"/>
+ <path d="M7.430,12.957 L7.430,12.036 L17.556,12.036 L17.556,12.957 L7.430,12.957 Z" class="cls-2"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/DependsOn.svg b/catalog-ui/app/styles/images/relationship-icons/DependsOn.svg
new file mode 100644
index 0000000000..e38808e2df
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/DependsOn.svg
@@ -0,0 +1,34 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1, .cls-2 {
+ fill: #3b7b9b;
+ }
+
+ .cls-1 {
+ fill-rule: evenodd;
+ }
+
+ .cls-3, .cls-4 {
+ fill: #fff;
+ stroke-linejoin: round;
+ stroke-width: 2px;
+ }
+
+ .cls-3 {
+ stroke: #fff;
+ fill-opacity: 0.01;
+ }
+
+ .cls-4 {
+ stroke: #3b7b9b;
+ }
+ </style>
+ </defs>
+ <g>
+ <path d="M16.478,-11.499 L10.911,-8.019 C10.253,-7.612 9.904,-8.255 9.904,-9.003 L9.904,-15.893 C9.904,-16.641 10.253,-17.283 10.911,-16.877 L16.478,-12.977 C17.136,-12.571 17.136,-11.906 16.478,-11.499 Z" class="cls-1"/>
+ <circle cx="12.5" cy="12.609" r="11.5" class="cls-2"/>
+ <circle cx="12.031" cy="12.156" r="5.531" class="cls-3"/>
+ <ellipse cx="16.188" cy="15.344" rx="3.219" ry="3.188" class="cls-4"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/HostedOn.svg b/catalog-ui/app/styles/images/relationship-icons/HostedOn.svg
new file mode 100644
index 0000000000..5daf84a334
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/HostedOn.svg
@@ -0,0 +1,25 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2, .cls-3 {
+ fill: #fff;
+ }
+
+ .cls-3 {
+ stroke: #fff;
+ stroke-linejoin: round;
+ stroke-width: 2px;
+ fill-opacity: 0;
+ }
+ </style>
+ </defs>
+ <g>
+ <ellipse cx="12.578" cy="12.516" rx="11.422" ry="11.266" class="cls-1"/>
+ <circle cx="12.594" cy="12.594" r="3.5" class="cls-2"/>
+ <circle cx="12.5" cy="12.5" r="7.5" class="cls-3"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/LinksTo.svg b/catalog-ui/app/styles/images/relationship-icons/LinksTo.svg
new file mode 100644
index 0000000000..fb4c687774
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/LinksTo.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ stroke: #fff;
+ stroke-linejoin: round;
+ stroke-width: 2px;
+ fill-opacity: 0;
+ }
+ </style>
+ </defs>
+ <g>
+ <circle cx="12.5" cy="12.5" r="11.5" class="cls-1"/>
+ <circle cx="15.156" cy="12.125" r="4.813" class="cls-2"/>
+ <ellipse cx="9.688" cy="12.125" rx="4.813" ry="4.594" class="cls-2"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/RoutesTo.svg b/catalog-ui/app/styles/images/relationship-icons/RoutesTo.svg
new file mode 100644
index 0000000000..4c3caf5886
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/RoutesTo.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <g>
+ <circle cx="12.5" cy="12.5" r="11.5" class="cls-1"/>
+ <path d="M7.430,12.957 L7.430,12.037 L17.556,12.037 L17.556,12.957 L7.430,12.957 Z" class="cls-2"/>
+ <path d="M19.179,13.005 L16.064,16.420 C15.807,16.702 15.387,16.702 15.130,16.420 C14.873,16.139 14.811,15.626 14.992,15.280 L16.126,13.121 C16.307,12.776 16.307,12.211 16.126,11.866 L14.992,9.706 C14.811,9.361 14.873,8.848 15.130,8.566 L15.130,8.566 C15.387,8.285 15.807,8.284 16.064,8.566 L19.179,11.981 C19.436,12.263 19.436,12.724 19.179,13.005 ZM9.948,15.288 C10.128,15.633 10.067,16.146 9.811,16.428 L9.811,16.428 C9.555,16.709 9.137,16.710 8.882,16.428 L5.783,13.013 C5.527,12.731 5.527,12.271 5.783,11.989 L8.882,8.574 C9.137,8.292 9.555,8.292 9.811,8.574 C10.067,8.856 10.128,9.369 9.948,9.714 L8.820,11.873 C8.639,12.218 8.639,12.783 8.820,13.128 L9.948,15.288 Z" class="cls-2"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/arrow.png b/catalog-ui/app/styles/images/relationship-icons/arrow.png
new file mode 100644
index 0000000000..d7c0ec8a86
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/arrow.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/arrow.svg b/catalog-ui/app/styles/images/relationship-icons/arrow.svg
new file mode 100644
index 0000000000..4696e50d57
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/arrow.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="7" height="9" viewBox="0 0 7 9">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <path d="M6.000,9.000 L0.471,5.037 C-0.183,4.625 -0.183,3.949 0.471,3.536 L6.000,-0.000 C6.653,-0.413 7.000,0.240 7.000,1.000 L7.000,8.000 C7.000,8.760 6.653,9.413 6.000,9.000 Z" class="cls-1"/>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/arrow_connection_right.svg b/catalog-ui/app/styles/images/relationship-icons/arrow_connection_right.svg
new file mode 100644
index 0000000000..ea2d9f258d
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/arrow_connection_right.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="7" height="9" viewBox="0 0 7 9">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <path d="M6.529,5.438 L1.000,8.878 C0.347,9.281 0.000,8.645 0.000,7.905 L0.000,1.095 C0.000,0.355 0.347,-0.280 1.000,0.122 L6.529,3.977 C7.183,4.379 7.183,5.036 6.529,5.438 Z" class="cls-1"/>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/attach.png b/catalog-ui/app/styles/images/relationship-icons/attach.png
new file mode 100644
index 0000000000..f01ebd86ca
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/attach.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/binding.png b/catalog-ui/app/styles/images/relationship-icons/binding.png
new file mode 100644
index 0000000000..15307d8267
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/binding.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/conected.png b/catalog-ui/app/styles/images/relationship-icons/conected.png
new file mode 100644
index 0000000000..1243146c3f
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/conected.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/conected.svg b/catalog-ui/app/styles/images/relationship-icons/conected.svg
new file mode 100644
index 0000000000..4ee7672305
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/conected.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="25" height="25" viewBox="0 0 25 25">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ fill-rule: evenodd;
+ }
+ </style>
+ </defs>
+ <g>
+ <circle cx="12.5" cy="12.5" r="11.5" class="cls-1"/>
+ <path d="M16.016,14.813 C14.747,14.813 13.719,13.777 13.719,12.500 C13.719,11.223 14.747,10.187 16.016,10.187 C17.284,10.187 18.313,11.223 18.313,12.500 C18.313,13.777 17.284,14.813 16.016,14.813 ZM7.984,14.813 C6.716,14.813 5.687,13.777 5.687,12.500 C5.687,11.223 6.716,10.187 7.984,10.187 C9.253,10.187 10.281,11.223 10.281,12.500 C10.281,13.777 9.253,14.813 7.984,14.813 Z" class="cls-2"/>
+ <path d="M7.430,12.957 L7.430,12.036 L17.556,12.036 L17.556,12.957 L7.430,12.957 Z" class="cls-2"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/dependency.png b/catalog-ui/app/styles/images/relationship-icons/dependency.png
new file mode 100644
index 0000000000..1bfd3e41c6
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/dependency.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/host.png b/catalog-ui/app/styles/images/relationship-icons/host.png
new file mode 100644
index 0000000000..cf314d1d4a
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/host.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/link.png b/catalog-ui/app/styles/images/relationship-icons/link.png
new file mode 100644
index 0000000000..9b81a4de57
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/link.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/local_storage.png b/catalog-ui/app/styles/images/relationship-icons/local_storage.png
new file mode 100644
index 0000000000..f01ebd86ca
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/local_storage.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/relationship-icons/local_storage.svg b/catalog-ui/app/styles/images/relationship-icons/local_storage.svg
new file mode 100644
index 0000000000..e255a4f058
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/local_storage.svg
@@ -0,0 +1,37 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid" width="23" height="23" viewBox="0 0 23 23">
+ <defs>
+ <style>
+ .cls-1 {
+ fill: #3b7b9b;
+ }
+
+ .cls-2 {
+ fill: #fff;
+ stroke: #fff;
+ stroke-linejoin: round;
+ stroke-width: 2px;
+ filter: url(#drop-shadow-1);
+ fill-opacity: 0;
+ }
+ </style>
+
+ <filter id="drop-shadow-1" filterUnits="userSpaceOnUse">
+ <feOffset dx="1.714" dy="1.03" in="SourceAlpha"/>
+ <feGaussianBlur result="dropBlur"/>
+ <feFlood flood-opacity="0"/>
+ <feComposite operator="in" in2="dropBlur" result="dropShadowComp"/>
+ <feComposite in="SourceGraphic" result="shadowed"/>
+ </filter>
+ </defs>
+ <g>
+ <ellipse cx="11.578" cy="11.109" rx="11.422" ry="11.266" class="cls-1"/>
+ <g style="fill: #fff; filter: url(#drop-shadow-1)">
+ <circle cx="14.5" cy="11.5" r="2.5" id="circle-1" style="stroke: inherit; filter: none; fill: inherit" class="cls-2"/>
+ </g>
+ <use xlink:href="#circle-1" style="stroke: #fff; filter: none; fill: none"/>
+ <g style="fill: #fff; filter: url(#drop-shadow-1)">
+ <circle cx="8.5" cy="11.5" r="2.5" id="circle-2" style="stroke: inherit; filter: none; fill: inherit" class="cls-2"/>
+ </g>
+ <use xlink:href="#circle-2" style="stroke: #fff; filter: none; fill: none"/>
+ </g>
+</svg>
diff --git a/catalog-ui/app/styles/images/relationship-icons/rout.png b/catalog-ui/app/styles/images/relationship-icons/rout.png
new file mode 100644
index 0000000000..6a3aefac44
--- /dev/null
+++ b/catalog-ui/app/styles/images/relationship-icons/rout.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Red_PLUS_HOVER.png b/catalog-ui/app/styles/images/resource-icons/Red_PLUS_HOVER.png
new file mode 100644
index 0000000000..eb623b0204
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Red_PLUS_HOVER.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COLLABORATION-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COLLABORATION-.png
new file mode 100644
index 0000000000..b0de76e4eb
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COLLABORATION-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COMPUTE-AS-SERVICE-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COMPUTE-AS-SERVICE-.png
new file mode 100644
index 0000000000..ec2723b766
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_COMPUTE-AS-SERVICE-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MESSAGING-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MESSAGING-.png
new file mode 100644
index 0000000000..e745e20604
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MESSAGING-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MOBILITY.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MOBILITY.png
new file mode 100644
index 0000000000..4993ffe1f5
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_MOBILITY.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-.png
new file mode 100644
index 0000000000..fb7a2518c2
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-CLOUD-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-CLOUD-.png
new file mode 100644
index 0000000000..a06093726a
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-CLOUD-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-1-3.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-1-3.png
new file mode 100644
index 0000000000..a9f876afd9
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-1-3.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-4.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-4.png
new file mode 100644
index 0000000000..e1023e743b
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NETWORK-L-4.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NOTIFICATION-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NOTIFICATION-.png
new file mode 100644
index 0000000000..dfa21d6d34
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_NOTIFICATION-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_ORPHAN.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_ORPHAN.png
new file mode 100644
index 0000000000..91619e0ae8
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_ORPHAN.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_PLATFORM-AS-SERVICE-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_PLATFORM-AS-SERVICE-.png
new file mode 100644
index 0000000000..8c763391e6
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_PLATFORM-AS-SERVICE-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SECURITY-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SECURITY-.png
new file mode 100644
index 0000000000..c015ffe569
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SECURITY-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SETTING-.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SETTING-.png
new file mode 100644
index 0000000000..b9db3ced5a
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_SETTING-.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_avater.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_avater.png
new file mode 100644
index 0000000000..209e6e7e34
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_NB_avater.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/Resource_Icons_STORAGE-AS-SERVICE.png b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_STORAGE-AS-SERVICE.png
new file mode 100644
index 0000000000..86111d0842
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/Resource_Icons_STORAGE-AS-SERVICE.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/alcatelLucent.png b/catalog-ui/app/styles/images/resource-icons/alcatelLucent.png
new file mode 100644
index 0000000000..259096fea4
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/alcatelLucent.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/applicationServer.png b/catalog-ui/app/styles/images/resource-icons/applicationServer.png
new file mode 100644
index 0000000000..f50b392249
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/applicationServer.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/aricent.png b/catalog-ui/app/styles/images/resource-icons/aricent.png
new file mode 100644
index 0000000000..6cc36f679f
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/aricent.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/att.png b/catalog-ui/app/styles/images/resource-icons/att.png
new file mode 100644
index 0000000000..80a814a34f
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/att.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/borderElement.png b/catalog-ui/app/styles/images/resource-icons/borderElement.png
new file mode 100644
index 0000000000..b3525c2e55
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/borderElement.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/broadsoft.png b/catalog-ui/app/styles/images/resource-icons/broadsoft.png
new file mode 100644
index 0000000000..2539d30d57
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/broadsoft.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/brocade.png b/catalog-ui/app/styles/images/resource-icons/brocade.png
new file mode 100644
index 0000000000..29b713105c
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/brocade.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/call_controll.png b/catalog-ui/app/styles/images/resource-icons/call_controll.png
new file mode 100644
index 0000000000..afedac5ad3
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/call_controll.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/canvasPlusIcon-red.png b/catalog-ui/app/styles/images/resource-icons/canvasPlusIcon-red.png
new file mode 100644
index 0000000000..bf56d13d6d
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/canvasPlusIcon-red.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/canvasPlusIcon.png b/catalog-ui/app/styles/images/resource-icons/canvasPlusIcon.png
new file mode 100644
index 0000000000..f2c289cea6
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/canvasPlusIcon.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/cisco.png b/catalog-ui/app/styles/images/resource-icons/cisco.png
new file mode 100644
index 0000000000..e83c771bf3
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/cisco.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/closeModule.png b/catalog-ui/app/styles/images/resource-icons/closeModule.png
new file mode 100644
index 0000000000..994cf2c4a5
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/closeModule.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/closeModuleHover.png b/catalog-ui/app/styles/images/resource-icons/closeModuleHover.png
new file mode 100644
index 0000000000..47f7cb2c6a
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/closeModuleHover.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/cloud.png b/catalog-ui/app/styles/images/resource-icons/cloud.png
new file mode 100644
index 0000000000..8e4c77694e
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/cloud.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/cloudep.png b/catalog-ui/app/styles/images/resource-icons/cloudep.png
new file mode 100644
index 0000000000..bdaadbd272
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/cloudep.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/compute-uncertified.png b/catalog-ui/app/styles/images/resource-icons/compute-uncertified.png
new file mode 100644
index 0000000000..f1254d2dfc
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/compute-uncertified.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/compute.png b/catalog-ui/app/styles/images/resource-icons/compute.png
new file mode 100644
index 0000000000..6deb0a0db6
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/compute.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/connector.png b/catalog-ui/app/styles/images/resource-icons/connector.png
new file mode 100644
index 0000000000..c479eea892
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/connector.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/cp.png b/catalog-ui/app/styles/images/resource-icons/cp.png
new file mode 100644
index 0000000000..f337d35afa
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/cp.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/database.png b/catalog-ui/app/styles/images/resource-icons/database.png
new file mode 100644
index 0000000000..b2e7684222
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/database.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_analytics.png b/catalog-ui/app/styles/images/resource-icons/dcae_analytics.png
new file mode 100644
index 0000000000..dd7180479d
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_analytics.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_collector.png b/catalog-ui/app/styles/images/resource-icons/dcae_collector.png
new file mode 100644
index 0000000000..2870362601
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_collector.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_database.png b/catalog-ui/app/styles/images/resource-icons/dcae_database.png
new file mode 100644
index 0000000000..b2e7684222
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_database.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_microservice.png b/catalog-ui/app/styles/images/resource-icons/dcae_microservice.png
new file mode 100644
index 0000000000..933abb106e
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_microservice.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_policy.png b/catalog-ui/app/styles/images/resource-icons/dcae_policy.png
new file mode 100644
index 0000000000..befb65ecc6
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_policy.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_source.png b/catalog-ui/app/styles/images/resource-icons/dcae_source.png
new file mode 100644
index 0000000000..58bfa34553
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_source.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/dcae_utilty.png b/catalog-ui/app/styles/images/resource-icons/dcae_utilty.png
new file mode 100644
index 0000000000..fd68ebf135
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/dcae_utilty.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/default.png b/catalog-ui/app/styles/images/resource-icons/default.png
new file mode 100644
index 0000000000..91619e0ae8
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/default.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/defaulticon.png b/catalog-ui/app/styles/images/resource-icons/defaulticon.png
new file mode 100644
index 0000000000..168859d253
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/defaulticon.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/ericsson.png b/catalog-ui/app/styles/images/resource-icons/ericsson.png
new file mode 100644
index 0000000000..7e3147593e
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/ericsson.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/firewall.png b/catalog-ui/app/styles/images/resource-icons/firewall.png
new file mode 100644
index 0000000000..5650f2276d
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/firewall.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/fortinet.png b/catalog-ui/app/styles/images/resource-icons/fortinet.png
new file mode 100644
index 0000000000..e4e52be0c4
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/fortinet.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/gateway.png b/catalog-ui/app/styles/images/resource-icons/gateway.png
new file mode 100644
index 0000000000..478a3af494
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/gateway.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/juniper.png b/catalog-ui/app/styles/images/resource-icons/juniper.png
new file mode 100644
index 0000000000..ed9b183b3b
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/juniper.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/loadBalancer.png b/catalog-ui/app/styles/images/resource-icons/loadBalancer.png
new file mode 100644
index 0000000000..5c5e555883
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/loadBalancer.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/metaswitch.png b/catalog-ui/app/styles/images/resource-icons/metaswitch.png
new file mode 100644
index 0000000000..970dfdd756
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/metaswitch.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/module.png b/catalog-ui/app/styles/images/resource-icons/module.png
new file mode 100644
index 0000000000..24574f5ac8
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/module.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/mysql.png b/catalog-ui/app/styles/images/resource-icons/mysql.png
new file mode 100644
index 0000000000..07eeaa7d2a
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/mysql.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/network.png b/catalog-ui/app/styles/images/resource-icons/network.png
new file mode 100644
index 0000000000..c98eef324e
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/network.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/networkrules.png b/catalog-ui/app/styles/images/resource-icons/networkrules.png
new file mode 100644
index 0000000000..4fe91fa7e5
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/networkrules.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/nokia_siemens.png b/catalog-ui/app/styles/images/resource-icons/nokia_siemens.png
new file mode 100644
index 0000000000..37a702d79f
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/nokia_siemens.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/objectStorage.png b/catalog-ui/app/styles/images/resource-icons/objectStorage.png
new file mode 100644
index 0000000000..27212b71c8
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/objectStorage.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/openModule.png b/catalog-ui/app/styles/images/resource-icons/openModule.png
new file mode 100644
index 0000000000..291d0c0361
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/openModule.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/openModuleHover.png b/catalog-ui/app/styles/images/resource-icons/openModuleHover.png
new file mode 100644
index 0000000000..0206c5e4d3
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/openModuleHover.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/oracle.png b/catalog-ui/app/styles/images/resource-icons/oracle.png
new file mode 100644
index 0000000000..b58acc0823
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/oracle.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/ossep.png b/catalog-ui/app/styles/images/resource-icons/ossep.png
new file mode 100644
index 0000000000..0c921dbc76
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/ossep.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/personep.png b/catalog-ui/app/styles/images/resource-icons/personep.png
new file mode 100644
index 0000000000..5b743b62d2
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/personep.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/port.png b/catalog-ui/app/styles/images/resource-icons/port.png
new file mode 100644
index 0000000000..95285b5b61
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/port.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/premisesep.png b/catalog-ui/app/styles/images/resource-icons/premisesep.png
new file mode 100644
index 0000000000..bedd82073f
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/premisesep.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/router.png b/catalog-ui/app/styles/images/resource-icons/router.png
new file mode 100644
index 0000000000..27d4897f33
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/router.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/securityrules.png b/catalog-ui/app/styles/images/resource-icons/securityrules.png
new file mode 100644
index 0000000000..520ab278cb
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/securityrules.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/selectedCPInstance.png b/catalog-ui/app/styles/images/resource-icons/selectedCPInstance.png
new file mode 100644
index 0000000000..2f63077037
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/selectedCPInstance.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/selectedInstance.png b/catalog-ui/app/styles/images/resource-icons/selectedInstance.png
new file mode 100644
index 0000000000..f2f5c90ac9
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/selectedInstance.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/selectedUcpeInstance.png b/catalog-ui/app/styles/images/resource-icons/selectedUcpeInstance.png
new file mode 100644
index 0000000000..f168929ed6
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/selectedUcpeInstance.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/selectedVLInstance.png b/catalog-ui/app/styles/images/resource-icons/selectedVLInstance.png
new file mode 100644
index 0000000000..17407f2609
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/selectedVLInstance.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/server.png b/catalog-ui/app/styles/images/resource-icons/server.png
new file mode 100644
index 0000000000..b51305d58f
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/server.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/tropo.png b/catalog-ui/app/styles/images/resource-icons/tropo.png
new file mode 100644
index 0000000000..88a5ea5e68
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/tropo.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/ucpe.png b/catalog-ui/app/styles/images/resource-icons/ucpe.png
new file mode 100644
index 0000000000..8e8e430e3c
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/ucpe.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/uncertified.png b/catalog-ui/app/styles/images/resource-icons/uncertified.png
new file mode 100644
index 0000000000..35d747a7d0
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/uncertified.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/vfw.png b/catalog-ui/app/styles/images/resource-icons/vfw.png
new file mode 100644
index 0000000000..b8adc1f09c
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/vfw.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/vl.png b/catalog-ui/app/styles/images/resource-icons/vl.png
new file mode 100644
index 0000000000..1fb2fc07ce
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/vl.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/vrouter.png b/catalog-ui/app/styles/images/resource-icons/vrouter.png
new file mode 100644
index 0000000000..70e89e5125
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/vrouter.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/resource-icons/wanx.png b/catalog-ui/app/styles/images/resource-icons/wanx.png
new file mode 100644
index 0000000000..daa493a0f2
--- /dev/null
+++ b/catalog-ui/app/styles/images/resource-icons/wanx.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/call_controll.png b/catalog-ui/app/styles/images/service-icons/call_controll.png
new file mode 100644
index 0000000000..ef1d92757e
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/call_controll.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/collaboration.png b/catalog-ui/app/styles/images/service-icons/collaboration.png
new file mode 100644
index 0000000000..3d34b914f5
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/collaboration.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/collaboration1.png b/catalog-ui/app/styles/images/service-icons/collaboration1.png
new file mode 100644
index 0000000000..6780931e2b
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/collaboration1.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/compute.png b/catalog-ui/app/styles/images/service-icons/compute.png
new file mode 100644
index 0000000000..cb2b127ef1
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/compute.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/defaulticon.png b/catalog-ui/app/styles/images/service-icons/defaulticon.png
new file mode 100644
index 0000000000..5afead32d8
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/defaulticon.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/messaging.png b/catalog-ui/app/styles/images/service-icons/messaging.png
new file mode 100644
index 0000000000..eb5f14d178
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/messaging.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/mobility.png b/catalog-ui/app/styles/images/service-icons/mobility.png
new file mode 100644
index 0000000000..285c442aba
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/mobility.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/network_l_1-3.png b/catalog-ui/app/styles/images/service-icons/network_l_1-3.png
new file mode 100644
index 0000000000..128c2bec8b
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/network_l_1-3.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/network_l_4.png b/catalog-ui/app/styles/images/service-icons/network_l_4.png
new file mode 100644
index 0000000000..7b05e1b101
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/network_l_4.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/notification.png b/catalog-ui/app/styles/images/service-icons/notification.png
new file mode 100644
index 0000000000..a5de34c40f
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/notification.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/platform.png b/catalog-ui/app/styles/images/service-icons/platform.png
new file mode 100644
index 0000000000..934eae9136
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/platform.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/storage.png b/catalog-ui/app/styles/images/service-icons/storage.png
new file mode 100644
index 0000000000..9abfd2d3a4
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/storage.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/service-icons/uncertified.png b/catalog-ui/app/styles/images/service-icons/uncertified.png
new file mode 100644
index 0000000000..35d747a7d0
--- /dev/null
+++ b/catalog-ui/app/styles/images/service-icons/uncertified.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/sprites/sprite-global-old.png b/catalog-ui/app/styles/images/sprites/sprite-global-old.png
new file mode 100644
index 0000000000..a1014f55d7
--- /dev/null
+++ b/catalog-ui/app/styles/images/sprites/sprite-global-old.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/sprites/sprite-global.png b/catalog-ui/app/styles/images/sprites/sprite-global.png
new file mode 100644
index 0000000000..bb2334216a
--- /dev/null
+++ b/catalog-ui/app/styles/images/sprites/sprite-global.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/sprites/sprite-product-icons.png b/catalog-ui/app/styles/images/sprites/sprite-product-icons.png
new file mode 100644
index 0000000000..e85467a09c
--- /dev/null
+++ b/catalog-ui/app/styles/images/sprites/sprite-product-icons.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/sprites/sprite-resource-icons.png b/catalog-ui/app/styles/images/sprites/sprite-resource-icons.png
new file mode 100644
index 0000000000..61989d63e1
--- /dev/null
+++ b/catalog-ui/app/styles/images/sprites/sprite-resource-icons.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/sprites/sprite-services-icons.png b/catalog-ui/app/styles/images/sprites/sprite-services-icons.png
new file mode 100644
index 0000000000..ac19fa186b
--- /dev/null
+++ b/catalog-ui/app/styles/images/sprites/sprite-services-icons.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/sprites/tlv-sprite.png b/catalog-ui/app/styles/images/sprites/tlv-sprite.png
new file mode 100644
index 0000000000..7f11c2ece1
--- /dev/null
+++ b/catalog-ui/app/styles/images/sprites/tlv-sprite.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/10.png b/catalog-ui/app/styles/images/tutorial/10.png
new file mode 100644
index 0000000000..312d48f99a
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/10.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/11.png b/catalog-ui/app/styles/images/tutorial/11.png
new file mode 100644
index 0000000000..8c4f8aec39
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/11.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/13.png b/catalog-ui/app/styles/images/tutorial/13.png
new file mode 100644
index 0000000000..d2c35ddb47
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/13.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/14.png b/catalog-ui/app/styles/images/tutorial/14.png
new file mode 100644
index 0000000000..0aabc9fd11
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/14.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/15.png b/catalog-ui/app/styles/images/tutorial/15.png
new file mode 100644
index 0000000000..d6720989c2
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/15.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/16.png b/catalog-ui/app/styles/images/tutorial/16.png
new file mode 100644
index 0000000000..95005bd5a2
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/16.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/17.png b/catalog-ui/app/styles/images/tutorial/17.png
new file mode 100644
index 0000000000..c22875e6d3
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/17.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/18.png b/catalog-ui/app/styles/images/tutorial/18.png
new file mode 100644
index 0000000000..e5b9b5a8b0
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/18.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/19.png b/catalog-ui/app/styles/images/tutorial/19.png
new file mode 100644
index 0000000000..f7374928ba
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/19.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/2.png b/catalog-ui/app/styles/images/tutorial/2.png
new file mode 100644
index 0000000000..4e141bd764
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/2.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/20.png b/catalog-ui/app/styles/images/tutorial/20.png
new file mode 100644
index 0000000000..c3849b0a17
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/20.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/21.png b/catalog-ui/app/styles/images/tutorial/21.png
new file mode 100644
index 0000000000..aa60a153a0
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/21.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/22.png b/catalog-ui/app/styles/images/tutorial/22.png
new file mode 100644
index 0000000000..0d4a71c1e4
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/22.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/23.png b/catalog-ui/app/styles/images/tutorial/23.png
new file mode 100644
index 0000000000..6854d0e4e5
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/23.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/24.png b/catalog-ui/app/styles/images/tutorial/24.png
new file mode 100644
index 0000000000..099fa28874
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/24.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/25.png b/catalog-ui/app/styles/images/tutorial/25.png
new file mode 100644
index 0000000000..718cf4a65f
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/25.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/26.png b/catalog-ui/app/styles/images/tutorial/26.png
new file mode 100644
index 0000000000..cd4885ec4a
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/26.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/27.png b/catalog-ui/app/styles/images/tutorial/27.png
new file mode 100644
index 0000000000..85c7378e39
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/27.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/28.png b/catalog-ui/app/styles/images/tutorial/28.png
new file mode 100644
index 0000000000..54c5ada0d7
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/28.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/29.png b/catalog-ui/app/styles/images/tutorial/29.png
new file mode 100644
index 0000000000..ae07010492
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/29.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/3.png b/catalog-ui/app/styles/images/tutorial/3.png
new file mode 100644
index 0000000000..ff1a86b4d0
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/3.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/30.png b/catalog-ui/app/styles/images/tutorial/30.png
new file mode 100644
index 0000000000..ef83db0e01
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/30.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/4.png b/catalog-ui/app/styles/images/tutorial/4.png
new file mode 100644
index 0000000000..dfc148868e
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/4.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/5.png b/catalog-ui/app/styles/images/tutorial/5.png
new file mode 100644
index 0000000000..f18e52527d
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/5.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/6.png b/catalog-ui/app/styles/images/tutorial/6.png
new file mode 100644
index 0000000000..173dc55d1c
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/6.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/7.png b/catalog-ui/app/styles/images/tutorial/7.png
new file mode 100644
index 0000000000..3e31376143
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/7.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/tutorial/8.png b/catalog-ui/app/styles/images/tutorial/8.png
new file mode 100644
index 0000000000..59f95dfa2e
--- /dev/null
+++ b/catalog-ui/app/styles/images/tutorial/8.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome.png b/catalog-ui/app/styles/images/welcome.png
new file mode 100644
index 0000000000..8534a33088
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/SD&C_Welcome15.jpg b/catalog-ui/app/styles/images/welcome/SD&C_Welcome15.jpg
new file mode 100644
index 0000000000..b53b229546
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/SD&C_Welcome15.jpg
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/SD&C_Welcome15_b.jpg b/catalog-ui/app/styles/images/welcome/SD&C_Welcome15_b.jpg
new file mode 100644
index 0000000000..bae42eb7d6
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/SD&C_Welcome15_b.jpg
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/001.jpg b/catalog-ui/app/styles/images/welcome/bg/001.jpg
new file mode 100644
index 0000000000..19bc76b300
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/001.jpg
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/002.jpg b/catalog-ui/app/styles/images/welcome/bg/002.jpg
new file mode 100644
index 0000000000..7d5fc2ef12
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/002.jpg
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/002.png b/catalog-ui/app/styles/images/welcome/bg/002.png
new file mode 100644
index 0000000000..f3e7a7c3ed
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/002.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/003.png b/catalog-ui/app/styles/images/welcome/bg/003.png
new file mode 100644
index 0000000000..c33b14fe9c
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/003.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/004.png b/catalog-ui/app/styles/images/welcome/bg/004.png
new file mode 100644
index 0000000000..08578bb1ec
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/004.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/bg02.png b/catalog-ui/app/styles/images/welcome/bg/bg02.png
new file mode 100644
index 0000000000..3b10aa45f7
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/bg02.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/bg03.png b/catalog-ui/app/styles/images/welcome/bg/bg03.png
new file mode 100644
index 0000000000..50fe924f5d
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/bg03.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/connection02.png b/catalog-ui/app/styles/images/welcome/bg/connection02.png
new file mode 100644
index 0000000000..157acba384
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/connection02.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/connection03.png b/catalog-ui/app/styles/images/welcome/bg/connection03.png
new file mode 100644
index 0000000000..01f21fcf41
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/connection03.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/connection04.png b/catalog-ui/app/styles/images/welcome/bg/connection04.png
new file mode 100644
index 0000000000..174dbfe5dd
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/connection04.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/global.png b/catalog-ui/app/styles/images/welcome/bg/global.png
new file mode 100644
index 0000000000..09b366404e
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/global.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/shape02.png b/catalog-ui/app/styles/images/welcome/bg/shape02.png
new file mode 100644
index 0000000000..91400da15d
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/shape02.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/shape03.png b/catalog-ui/app/styles/images/welcome/bg/shape03.png
new file mode 100644
index 0000000000..af6384674b
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/shape03.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/shape04.png b/catalog-ui/app/styles/images/welcome/bg/shape04.png
new file mode 100644
index 0000000000..36759df6e5
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/shape04.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/bg/shape05.png b/catalog-ui/app/styles/images/welcome/bg/shape05.png
new file mode 100644
index 0000000000..336411fd80
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/bg/shape05.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/laptop.png b/catalog-ui/app/styles/images/welcome/laptop.png
new file mode 100644
index 0000000000..160e743d1b
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/laptop.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/logo_att.png b/catalog-ui/app/styles/images/welcome/logo_att.png
new file mode 100644
index 0000000000..e145a1ae5d
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/logo_att.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/sprite.png b/catalog-ui/app/styles/images/welcome/sprite.png
new file mode 100644
index 0000000000..3bb7542446
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/sprite.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/ss-01.png b/catalog-ui/app/styles/images/welcome/ss-01.png
new file mode 100644
index 0000000000..abd4cd7a46
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/ss-01.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/ss-02.png b/catalog-ui/app/styles/images/welcome/ss-02.png
new file mode 100644
index 0000000000..d4d0038b64
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/ss-02.png
Binary files differ
diff --git a/catalog-ui/app/styles/images/welcome/ss-03.png b/catalog-ui/app/styles/images/welcome/ss-03.png
new file mode 100644
index 0000000000..9a259ca7c2
--- /dev/null
+++ b/catalog-ui/app/styles/images/welcome/ss-03.png
Binary files differ
diff --git a/catalog-ui/app/styles/layout/header.less b/catalog-ui/app/styles/layout/header.less
new file mode 100644
index 0000000000..f8e95e8b41
--- /dev/null
+++ b/catalog-ui/app/styles/layout/header.less
@@ -0,0 +1,78 @@
+.w-sdc-header {
+ .bg_j;
+ border-bottom: 1px solid @border_color_c;
+ height: 94px;
+ width: 100%;
+ display: flex;
+ align-items: center;
+}
+
+.w-sdc-header-logo {
+ .bg_i;
+ .hand;
+ border-bottom: 1px solid @border_color_a;
+ height: 94px;
+ width: 240px;
+ padding: 28px;
+ .flex-fixed(240px);
+ z-index: 1020;
+}
+
+.w-sdc-header-logo-icon {
+ display: inline-block;
+ margin-right: 8px;
+ vertical-align: -6px;
+}
+
+a.w-sdc-header-logo-link {
+ .c_5;
+ text-decoration: none;
+}
+
+.w-sdc-header-version{
+ .c_16;
+ .opacity(0.6);
+}
+
+.i-sdc-header-caption {
+ min-width: 175px;
+ text-align: left;
+ cursor: default;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ flex-grow: 10;
+ padding-left: 27px;
+ white-space: nowrap;
+
+ .i-sdc-header-caption-text {
+ .i_11;
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ }
+
+ .i-sdc-header-help {
+ .i_1;
+ .hand;
+ margin-left: 26px;
+ position: relative;
+ display: inline-block;
+
+ &::before {
+ .bg_a;
+ border-radius: 50%;
+ color: @color_c;
+ content: '?';
+ display: block;
+ height: 20px;
+ position: absolute;
+ left: -26px;
+ text-align: center;
+ width: 20px;
+ }
+ }
+}
+
+.sdc-error-403-container-title {
+ .b_11;
+}
diff --git a/catalog-ui/app/styles/layout/main.less b/catalog-ui/app/styles/layout/main.less
new file mode 100644
index 0000000000..925da09431
--- /dev/null
+++ b/catalog-ui/app/styles/layout/main.less
@@ -0,0 +1,124 @@
+.full-height {
+ height: 100%;
+ background-color: @main_color_l
+}
+
+.w-sdc-main-container {
+ position: absolute;
+ top: @header_height + @top_nav_height;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ .w-sdc-main-right-container {
+ .bg_n;
+ bottom: 0;
+ padding: 22px 12px;
+ position: absolute;
+ left: 240px;
+ right: 0;
+ top: 0;
+ overflow: hidden;
+
+
+ & > div:first-child { /* scroll fix */
+ padding-bottom: 80px;
+ }
+ }
+}
+
+.sdc-loading-page {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ position: absolute;
+ height: 100%;
+ width: 100%;
+
+ .caption1 {
+ .p_24;
+ text-align: center;
+ }
+
+ .caption2 {
+ .p_16_m;
+ display: block;
+ }
+}
+
+.main-loader {
+ @green: #cccccc;
+ @blue: #dddddd;
+ @red: #eeeeee;
+ @yellow: #bbbbbb;
+ @white: #ffffff;
+
+ @width: 80px;
+
+ .loader {
+ position: relative;
+ margin: 0px auto;
+ width: @width;
+ &:before {
+ content:'';
+ display: block;
+ padding-top: 100%;
+ }
+ }
+
+ .circular {
+ animation: rotate 2s linear infinite;
+ height: 100%;
+ transform-origin: center center;
+ width: 100%;
+ position: absolute;
+ top: 0; bottom: 0; left: 0; right: 0;
+ margin: auto;
+ }
+
+
+ .path {
+ stroke-dasharray: 1,200;
+ stroke-dashoffset: 0;
+ animation:
+ dash 1.5s ease-in-out infinite,
+ color 6s ease-in-out infinite;
+ stroke-linecap: round;
+ }
+
+ @keyframes rotate{
+ 100%{
+ transform: rotate(360deg);
+ }
+ }
+ @keyframes dash{
+ 0%{
+ stroke-dasharray: 1,200;
+ stroke-dashoffset: 0;
+ }
+ 50%{
+ stroke-dasharray: 89,200;
+ stroke-dashoffset: -35px;
+ }
+ 100%{
+ stroke-dasharray: 89,200;
+ stroke-dashoffset: -124px;
+ }
+ }
+ @keyframes color{
+ 100%, 0%{
+ stroke: @red;
+ }
+ 40%{
+ stroke: @blue;
+ }
+ 66%{
+ stroke: @green;
+ }
+ 80%, 90%{
+ stroke: @yellow;
+ }
+ }
+
+}
diff --git a/catalog-ui/app/styles/layout/sidebar.less b/catalog-ui/app/styles/layout/sidebar.less
new file mode 100644
index 0000000000..9b8dc240c7
--- /dev/null
+++ b/catalog-ui/app/styles/layout/sidebar.less
@@ -0,0 +1,163 @@
+.w-sdc-left-sidebar {
+ width: 242px;
+ overflow: hidden;
+ background-color: @main_color_p;
+ .box-shadow(1px 0px 4px 0px rgba(24, 24, 25, 0.17));
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ padding: 12px 18px;
+ z-index: 1;
+}
+
+.w-sdc-left-sidebar-menu {
+ margin: 0;
+ padding: 0;
+
+ ul {
+ margin: 0;
+ padding: 0;
+ }
+}
+
+/* Catalog */
+.i-sdc-left-sidebar-nav-item {
+ .c_6;
+ .hand;
+ .border-radius(4px);
+ width: 220px;
+ height: 60px;
+ line-height: 19px;
+ margin: 0 auto 10px;
+ padding: 20px 10px 20px 60px;
+ box-shadow: 0 0 2px 0 rgba(0, 0, 0, .3);
+ text-align: left;
+
+ &::before {
+ .sprite;
+ content: '';
+ position: absolute;
+ left: 50px;
+ top: 18px;
+ }
+
+ &.catalog {
+ .c_4;
+ .bg_a;
+ .uppercase;
+ position: relative;
+ text-indent: 20px;
+
+ &:hover { .bg_a_hover; }
+ &::before {.sprite.catalog;}
+ }
+
+ &.distribution {
+ .bg_o;
+ display: none;
+ position: relative;
+ }
+ &.distribution::before {
+ .sprite.distribution;
+ }
+
+ &.support {
+ .bg_a;
+ position: relative;
+ }
+
+ &.support::before {
+ .sprite.support;
+
+ }
+}
+
+
+/* */
+.i-sdc-left-sidebar-nav-item-hierachy {
+ .c_6;
+ .hand;
+ .border-radius(4px);
+ width: 220px;
+ height: 60px;
+ line-height: 19px;
+ margin: 0 auto 10px;
+ padding: 20px 10px 20px 60px;
+ box-shadow: 0 0 2px 0 rgba(0, 0, 0, .3);
+ text-align: left;
+
+ &::before {
+ .sprite;
+ content: '';
+ position: absolute;
+ left: 50px;
+ top: 18px;
+ }
+
+ &.catalog {
+ .c_4;
+ .bg_a;
+ .uppercase;
+ position: relative;
+ text-indent: 20px;
+
+ &:hover { .bg_a_hover; }
+ &::before {.sprite.catalog;}
+ }
+
+ &.distribution {
+ .bg_o;
+ display: none;
+ position: relative;
+ }
+ &.distribution::before {
+ .sprite.distribution;
+ }
+
+ &.support {
+ .bg_a;
+ position: relative;
+ }
+
+ &.support::before {
+ .sprite.support;
+
+ }
+}
+
+/* Dashboard (Non Catalog) */
+.i-sdc-left-sidebar-item {
+ list-style: none;
+ .m_14_r;
+ line-height: 24px;
+
+ &.category-title {
+ text-transform: uppercase;
+ .l_14_m;
+ line-height: 30px;
+ }
+
+ input[type="checkbox"] {
+ margin-right: 10px;
+ vertical-align: sub;
+ }
+
+ /*&.selectedLink {
+ .c_7;
+ }*/
+
+ .i-sdc-left-sidebar-item-state-count {
+ float: right;
+ }
+
+ .sdc-element-checkbox {
+ display: inline-flex;
+ }
+
+}
+
+.i-sdc-left-sidebar-item-seperator {
+ border-bottom: solid 1px @func_color_r;
+ width: 208px;
+}
diff --git a/catalog-ui/app/styles/mixins.less b/catalog-ui/app/styles/mixins.less
new file mode 100644
index 0000000000..7bd413ed05
--- /dev/null
+++ b/catalog-ui/app/styles/mixins.less
@@ -0,0 +1,217 @@
+@import "variables";
+
+.f-color {
+ .a {color: @main_color_a;}
+ .b {color: @main_color_b;}
+ .c {color: @main_color_c;}
+ .d {color: @main_color_d;}
+ .e {color: @main_color_e;}
+ .f {color: @main_color_f;}
+ .g {color: @main_color_g;}
+ .h {color: @main_color_h;}
+ .i {color: @main_color_i;}
+ .j {color: @main_color_j;}
+ .k {color: @main_color_k;}
+ .l {color: @main_color_l;}
+ .m {color: @main_color_m;}
+ .n {color: @main_color_n;}
+ .o {color: @main_color_o;}
+ .p {color: @main_color_p;}
+
+ .q {color: @func_color_q;}
+ .r {color: @func_color_r;}
+ .s {color: @func_color_s;}
+
+ .t {color: @tlv_color_t;}
+ .u {color: @tlv_color_u;}
+ .v {color: @tlv_color_v;}
+
+
+}
+
+.f-type {
+ ._36 {
+ font-family: @font-omnes-light;
+ font-size: 36px;
+ }
+ ._24 {
+ font-family: @font-omnes-light;
+ font-size: 24px;
+ }
+ ._18_r {
+ font-family: @font-omnes-regular;
+ font-size: 18px;
+ }
+ ._18_m {
+ font-family: @font-omnes-medium;
+ font-size: 18px;
+ }
+ ._16_r {
+ font-family: @font-omnes-regular;
+ font-size: 16px;
+ }
+ ._16_m {
+ font-family: @font-omnes-medium;
+ font-size: 16px;
+ }
+ ._14_r {
+ font-family: @font-omnes-regular;
+ font-size: 14px;
+ }
+ ._14_m {
+ font-family: @font-omnes-medium;
+ font-size: 14px;
+ }
+ ._14_i {
+ font-family: @font-omnes-medium-italic;
+ font-size: 14px;
+ }
+ ._13_r {
+ font-family: @font-omnes-regular;
+ font-size: 13px;
+ }
+ ._13_m {
+ font-family: @font-omnes-medium;
+ font-size: 13px;
+ }
+ ._13_i {
+ font-family: @font-omnes-medium-italic;
+ font-size: 13px;
+ }
+ ._12_r {
+ font-family: @font-omnes-regular;
+ font-size: 12px;
+ }
+ ._12_m {
+ font-family: @font-omnes-medium;
+ font-size: 12px;
+ }
+ ._12_i {
+ font-family: @font-omnes-medium-italic;
+ font-size: 12px;
+ }
+}
+
+.buildForColor(a);
+.buildForColor(b);
+.buildForColor(c);
+.buildForColor(d);
+.buildForColor(e);
+.buildForColor(f);
+.buildForColor(g);
+.buildForColor(h);
+.buildForColor(i);
+.buildForColor(j);
+.buildForColor(k);
+.buildForColor(l);
+.buildForColor(m);
+.buildForColor(n);
+.buildForColor(o);
+.buildForColor(p);
+
+.buildForFuncColor(q);
+.buildForFuncColor(r);
+.buildForFuncColor(s);
+
+.buildForTlvColor(t);
+.buildForTlvColor(u);
+.buildForTlvColor(v);
+
+
+.buildForColor(@c){
+ .@{c}_36 { color: ~"@{main_color_@{c}}"; .f-type > ._36;}
+ .@{c}_24 { color: ~"@{main_color_@{c}}"; .f-type > ._24;}
+ .@{c}_18_r { color: ~"@{main_color_@{c}}"; .f-type > ._18_r;}
+ .@{c}_18_m { color: ~"@{main_color_@{c}}"; .f-type > ._18_m;}
+ .@{c}_16_r { color: ~"@{main_color_@{c}}"; .f-type > ._16_r;}
+ .@{c}_16_m { color: ~"@{main_color_@{c}}"; .f-type > ._16_m;}
+ .@{c}_14_r { color: ~"@{main_color_@{c}}"; .f-type > ._14_r;}
+ .@{c}_14_m { color: ~"@{main_color_@{c}}"; .f-type > ._14_m;}
+ .@{c}_14_i { color: ~"@{main_color_@{c}}"; .f-type > ._14_i;}
+ .@{c}_13_r { color: ~"@{main_color_@{c}}"; .f-type > ._13_r;}
+ .@{c}_13_m { color: ~"@{main_color_@{c}}"; .f-type > ._13_m;}
+ .@{c}_13_i { color: ~"@{main_color_@{c}}"; .f-type > ._13_i;}
+ .@{c}_12_r { color: ~"@{main_color_@{c}}"; .f-type > ._12_r;}
+ .@{c}_12_m { color: ~"@{main_color_@{c}}"; .f-type > ._12_m;}
+ .@{c}_12_i { color: ~"@{main_color_@{c}}"; .f-type > ._12_i;}
+}
+
+.buildForFuncColor(@c){
+ .@{c}_36 { color: ~"@{func_color_@{c}}"; .f-type > ._36;}
+ .@{c}_24 { color: ~"@{func_color_@{c}}"; .f-type > ._24;}
+ .@{c}_18_r { color: ~"@{func_color_@{c}}"; .f-type > ._18_r;}
+ .@{c}_18_m { color: ~"@{func_color_@{c}}"; .f-type > ._18_m;}
+ .@{c}_16_r { color: ~"@{func_color_@{c}}"; .f-type > ._16_r;}
+ .@{c}_16_m { color: ~"@{func_color_@{c}}"; .f-type > ._16_m;}
+ .@{c}_14_r { color: ~"@{func_color_@{c}}"; .f-type > ._14_r;}
+ .@{c}_14_m { color: ~"@{func_color_@{c}}"; .f-type > ._14_m;}
+ .@{c}_14_i { color: ~"@{func_color_@{c}}"; .f-type > ._14_i;}
+ .@{c}_13_r { color: ~"@{func_color_@{c}}"; .f-type > ._13_r;}
+ .@{c}_13_m { color: ~"@{func_color_@{c}}"; .f-type > ._13_m;}
+ .@{c}_13_i { color: ~"@{func_color_@{c}}"; .f-type > ._13_i;}
+ .@{c}_12_r { color: ~"@{func_color_@{c}}"; .f-type > ._12_r;}
+ .@{c}_12_m { color: ~"@{func_color_@{c}}"; .f-type > ._12_m;}
+ .@{c}_12_i { color: ~"@{func_color_@{c}}"; .f-type > ._12_i;}
+}
+
+
+.buildForTlvColor(@c){
+ .@{c}_36 { color: ~"@{tlv_color_@{c}}"; .f-type > ._36;}
+ .@{c}_24 { color: ~"@{tlv_color_@{c}}"; .f-type > ._24;}
+ .@{c}_18_r { color: ~"@{tlv_color_@{c}}"; .f-type > ._18_r;}
+ .@{c}_18_m { color: ~"@{tlv_color_@{c}}"; .f-type > ._18_m;}
+ .@{c}_16_r { color: ~"@{tlv_color_@{c}}"; .f-type > ._16_r;}
+ .@{c}_16_m { color: ~"@{tlv_color_@{c}}"; .f-type > ._16_m;}
+ .@{c}_14_r { color: ~"@{tlv_color_@{c}}"; .f-type > ._14_r;}
+ .@{c}_14_m { color: ~"@{tlv_color_@{c}}"; .f-type > ._14_m;}
+ .@{c}_14_i { color: ~"@{tlv_color_@{c}}"; .f-type > ._14_i;}
+ .@{c}_13_r { color: ~"@{tlv_color_@{c}}"; .f-type > ._13_r;}
+ .@{c}_13_m { color: ~"@{tlv_color_@{c}}"; .f-type > ._13_m;}
+ .@{c}_13_i { color: ~"@{tlv_color_@{c}}"; .f-type > ._13_i;}
+ .@{c}_12_r { color: ~"@{tlv_color_@{c}}"; .f-type > ._12_r;}
+ .@{c}_12_m { color: ~"@{tlv_color_@{c}}"; .f-type > ._12_m;}
+ .@{c}_12_i { color: ~"@{tlv_color_@{c}}"; .f-type > ._12_i;}
+}
+
+.disabled {
+ opacity: 0.4 !important;
+ cursor: auto;
+ background-color: transparent;
+ pointer-events: none;
+}
+
+/* I'm not using hidden name, can not override bower_components/bootstrap/dist/css/less/responsive-utilities.less */
+.hideme {
+ visibility: hidden;
+}
+
+.view-mode {
+ opacity: 1;
+ border: solid 1px @main_color_o;
+ background-color: #f8f8f8;
+ cursor: auto;
+
+ & + &:not(.icons-text), &.no-border-top {
+ border-top: none;
+ }
+}
+
+.no-pointer-events {
+ pointer-events: none;
+}
+
+.unselectable {
+ -moz-user-select: -moz-none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.selectable {
+ -moz-user-select: text;
+ -khtml-user-select: text;
+ -webkit-user-select: text;
+ -ms-user-select: text;
+ user-select: text;
+}
diff --git a/catalog-ui/app/styles/mixins_old.less b/catalog-ui/app/styles/mixins_old.less
new file mode 100644
index 0000000000..8a4f609497
--- /dev/null
+++ b/catalog-ui/app/styles/mixins_old.less
@@ -0,0 +1,558 @@
+@import "variables";
+
+.font-color {
+ .a {color: @color_a;}
+ .b {color: @color_b;}
+ .c {color: @color_c;}
+ .d {color: @color_d;}
+ .e {color: @color_e;}
+ .f {color: @color_f;}
+ .g {color: @color_g;}
+ .h {color: @color_h;}
+ .i {color: @color_i;}
+ .j {color: @color_j;}
+ .k {color: @color_k;}
+ .l {color: @color_l;}
+ .m {color: @color_m;}
+ .n {color: @color_n;}
+ .o {color: @color_o;}
+ .p {color: @color_p;}
+ .q {color: @color_q;}
+ .r {color: @color_r;}
+ .s {color: @color_s;}
+ .t {color: @color_t;}
+ .u {color: @color_t;}
+ .v {color: @color_t;}
+ .w {color: @color_t;}
+ .x {color: @color_t;}
+ .y {color: @color_t;}
+ .z {color: @color_z;}
+ .zz {color: @color_zz;}
+
+ .hover {
+ .a {color: @color_a_hover;}
+ .b {color: @color_b_hover;}
+ .c {color: @color_c_hover;}
+ .d {color: @color_d_hover;}
+ .e {color: @color_e_hover;}
+ .f {color: @color_f_hover;}
+ .g {color: @color_g_hover;}
+ .h {color: @color_h_hover;}
+ .i {color: @color_i_hover;}
+ .j {color: @color_j_hover;}
+ .k {color: @color_k_hover;}
+ .l {color: @color_l_hover;}
+ .m {color: @color_m_hover;}
+ .n {color: @color_n_hover;}
+ .o {color: @color_o_hover;}
+ .p {color: @color_p_hover;}
+ .q {color: @color_q_hover;}
+ .r {color: @color_r_hover;}
+ .s {color: @color_s_hover;}
+ .t {color: @color_t_hover;}
+ .u {color: @color_u_hover;}
+ .v {color: @color_v_hover;}
+ .w {color: @color_w_hover;}
+ .x {color: @color_x_hover;}
+ .y {color: @color_y_hover;}
+ .z {color: @color_z_hover;}
+ .zz {color: @color_zz_hover;}
+ }
+}
+
+.font-type {
+ ._1 {
+ font-family: @font-omnes-regular;
+ font-size: 14px;
+ }
+ ._2 {
+ font-family: @font-omnes-regular;
+ font-size: 20px;
+ }
+ ._3 {
+ font-family: @font-omnes-regular;
+ font-size: 12px;
+ }
+ ._4 {
+ font-family: @font-omnes-regular;
+ font-size: 16px;
+ }
+ ._5 {
+ font-family: @font-omnes-medium;
+ font-size: 30px;
+ }
+ ._6 {
+ font-family: @font-omnes-medium;
+ font-size: 16px;
+ }
+ ._7 {
+ font-family: @font-omnes-medium;
+ font-size: 14px;
+ }
+ ._8 {
+ font-family: @font-omnes-regular;
+ font-size: 11px;
+ }
+ ._9 {
+ font-family: @font-omnes-regular;
+ font-size: 13px;
+ }
+ ._10 {
+ font-family: @font-omnes-regular;
+ font-size: 18px;
+ }
+ ._11 {
+ font-family: @font-omnes-medium;
+ font-size: 20px;
+ }
+ ._12 {
+ font-family: @font-omnes-medium;
+ font-size: 17px;
+ }
+ ._13 {
+ font-family: @font-omnes-medium;
+ font-size: 12px;
+ }
+ ._14 {
+ font-family: @font-omnes-medium;
+ font-size: 11px;
+ }
+ ._15 {
+ font-family: @font-omnes-medium;
+ font-size: 24px;
+ }
+ ._16 {
+ font-family: @font-omnes-regular;
+ font-size: 10px;
+ }
+ ._17 {
+ font-family: @font-omnes-medium;
+ font-size: 15px;
+ }
+ ._18 {
+ font-family: @font-omnes-regular;
+ font-size: 15px;
+ }
+ ._19 {
+ font-family: @font-omnes-medium;
+ font-size: 18px;
+ }
+}
+
+.a_1 {.font-color > .a; .font-type > ._1;}
+.a_2 {.font-color > .a; .font-type > ._2;}
+.a_3 {.font-color > .a; .font-type > ._3;}
+.a_4 {.font-color > .a; .font-type > ._4;}
+.a_5 {.font-color > .a; .font-type > ._5;}
+.a_6 {.font-color > .a; .font-type > ._6;}
+.a_7 {.font-color > .a; .font-type > ._7;}
+.a_8 {.font-color > .a; .font-type > ._8;}
+.a_9 {.font-color > .a; .font-type > ._9;}
+.a_10 {.font-color > .a; .font-type > ._10;}
+.a_15 {.font-color > .a; .font-type > ._15;}
+
+.b_1 {.font-color > .b; .font-type > ._1;}
+.b_3 {.font-color > .b; .font-type > ._3;}
+.b_4 {.font-color > .b; .font-type > ._4;}
+.b_5 {.font-color > .b; .font-type > ._5;}
+.b_6 {.font-color > .b; .font-type > ._6;}
+.b_7 {.font-color > .b; .font-type > ._7;}
+.b_9 {.font-color > .b; .font-type > ._9;}
+.b_10 {.font-color > .b; .font-type > ._10;}
+.b_11 {.font-color > .b; .font-type > ._11;}
+.b_13 {.font-color > .b; .font-type > ._13;}
+.b_15 {.font-color > .b; .font-type > ._15;}
+.b_17 {.font-color > .b; .font-type > ._17;}
+.b_19 {.font-color > .b; .font-type > ._19;}
+
+.c_1 {.font-color > .c; .font-type > ._1;}
+.c_2 {.font-color > .c; .font-type > ._2;}
+.c_3 {.font-color > .c; .font-type > ._3;}
+.c_4 {.font-color > .c; .font-type > ._4;}
+.c_5 {.font-color > .c; .font-type > ._5;}
+.c_6 {.font-color > .c; .font-type > ._6;}
+.c_7 {.font-color > .c; .font-type > ._7;}
+.c_8 {.font-color > .c; .font-type > ._8;}
+.c_9 {.font-color > .c; .font-type > ._9;}
+.c_10 {.font-color > .c; .font-type > ._10;}
+.c_11 {.font-color > .c; .font-type > ._11;}
+.c_15 {.font-color > .c; .font-type > ._15;}
+.c_16 {.font-color > .c; .font-type > ._16;}
+.c_17 {.font-color > .c; .font-type > ._17;}
+.c_18 {.font-color > .c; .font-type > ._18;}
+
+.d_1 {.font-color > .d; .font-type > ._1;}
+.d_2 {.font-color > .d; .font-type > ._2;}
+.d_3 {.font-color > .d; .font-type > ._3;}
+.d_4 {.font-color > .d; .font-type > ._4;}
+.d_5 {.font-color > .d; .font-type > ._5;}
+.d_6 {.font-color > .d; .font-type > ._6;}
+.d_12 {.font-color > .d; .font-type > ._12;}
+
+.e_1 {.font-color > .e; .font-type > ._1;}
+.e_2 {.font-color > .e; .font-type > ._2;}
+.e_3 {.font-color > .e; .font-type > ._3;}
+.e_4 {.font-color > .e; .font-type > ._4;}
+.e_5 {.font-color > .e; .font-type > ._5;}
+.e_6 {.font-color > .e; .font-type > ._6;}
+.e_7 {.font-color > .e; .font-type > ._7;}
+
+.f_1 {.font-color > .f; .font-type > ._1;}
+.f_2 {.font-color > .f; .font-type > ._2;}
+
+.g_1 {.font-color > .g; .font-type > ._1;}
+.g_2 {.font-color > .g; .font-type > ._2;}
+.g_3 {.font-color > .g; .font-type > ._3;}
+.g_4 {.font-color > .g; .font-type > ._4;}
+.g_5 {.font-color > .g; .font-type > ._5;}
+.g_6 {.font-color > .g; .font-type > ._6;}
+.g_7 {.font-color > .g; .font-type > ._7;}
+.g_8 {.font-color > .g; .font-type > ._8;}
+.g_9 {.font-color > .g; .font-type > ._9;}
+.g_10 {.font-color > .g; .font-type > ._10;}
+.g_13 {.font-color > .g; .font-type > ._13;}
+.g_14 {.font-color > .g; .font-type > ._14;}
+
+.h_1 {.font-color > .h; .font-type > ._1;}
+.h_7 {.font-color > .h; .font-type > ._7;}
+.h_9 {.font-color > .h; .font-type > ._9;}
+
+.i_1 {.font-color > .i; .font-type > ._1;}
+.i_6 {.font-color > .i; .font-type > ._6;}
+.i_11 {.font-color > .i; .font-type > ._11;}
+.i_17 {.font-color > .i; .font-type > ._17;}
+
+.l_1 {.font-color > .l; .font-type > ._1;}
+.l_9 {.font-color > .l; .font-type > ._9;}
+
+.m_4 {.font-color > .m; .font-type > ._4;}
+.m_7 {.font-color > .m; .font-type > ._7;}
+
+.p_1 {.font-color > .p; .font-type > ._1;}
+.p_3 {.font-color > .p; .font-type > ._3;}
+.p_9 {.font-color > .p; .font-type > ._9;}
+
+.q_7 {.font-color > .q; .font-type > ._7;}
+.q_9 {.font-color > .q; .font-type > ._9;}
+.q_11 {.font-color > .q; .font-type > ._11;}
+
+.r_12 {.font-color > .r; .font-type > ._12;}
+
+.t_1 {.font-color > .t; .font-type > ._1;}
+.t_7 {.font-color > .t; .font-type > ._7;}
+.t_15 {.font-color > .t; .font-type > ._15;}
+
+/* Added by ikram - */
+.s_12 {.font-color > .s; .font-type > ._12;}
+
+.z_9 {.font-color > .z; .font-type > ._9;}
+.backgroundColor {
+ .a {background-color: @color_a;}
+ .b {background-color: @color_b;}
+ .c {background-color: @color_c;}
+ .d {background-color: @color_d;}
+ .e {background-color: @color_e;}
+ .f {background-color: @color_f;}
+ .g {background-color: @color_g;}
+ .h {background-color: @color_h;}
+ .i {background-color: @color_i;}
+ .j {background-color: @color_j;}
+ .k {background-color: @color_k;}
+ .l {background-color: @color_l;}
+ .m {background-color: @color_m;}
+ .n {background-color: @color_n;}
+ .o {background-color: @color_o;}
+ .p {background-color: @color_p;}
+ .q {background-color: @color_q;}
+ .r {background-color: @color_r;}
+ .s {background-color: @color_s;}
+ .t {background-color: @color_t;}
+ .u {background-color: @color_u;}
+ .v {background-color: @color_v;}
+ .w {background-color: @color_w;}
+ .x {background-color: @color_x;}
+ .y {background-color: @color_y;}
+ .z {background-color: @color_z;}
+ .zz {background-color: @color_zz;}
+
+ .hover {
+ .a {background-color: @color_a_hover;}
+ .b {background-color: @color_b_hover;}
+ .c {background-color: @color_c_hover;}
+ .d {background-color: @color_d_hover;}
+ .e {background-color: @color_e_hover;}
+ .f {background-color: @color_f_hover;}
+ .g {background-color: @color_g_hover;}
+ .h {background-color: @color_h_hover;}
+ .i {background-color: @color_i_hover;}
+ .j {background-color: @color_j_hover;}
+ .k {background-color: @color_k_hover;}
+ .l {background-color: @color_l_hover;}
+ .m {background-color: @color_m_hover;}
+ .n {background-color: @color_n_hover;}
+ .o {background-color: @color_o_hover;}
+ .p {background-color: @color_p_hover;}
+ .q {background-color: @color_q_hover;}
+ .r {background-color: @color_r_hover;}
+ .s {background-color: @color_s_hover;}
+ .t {background-color: @color_t_hover;}
+ .u {background-color: @color_u_hover;}
+ .v {background-color: @color_v_hover;}
+ .w {background-color: @color_w_hover;}
+ .x {background-color: @color_x_hover;}
+ .y {background-color: @color_y_hover;}
+ .z {background-color: @color_z_hover;}
+ .zz {background-color: @color_zz_hover;}
+ }
+}
+
+.bg_a {.backgroundColor > .a;}
+.bg_b {.backgroundColor > .b;}
+.bg_c {.backgroundColor > .c;}
+.bg_d {.backgroundColor > .d;}
+.bg_e {.backgroundColor > .e;}
+.bg_f {.backgroundColor > .f;}
+.bg_g {.backgroundColor > .g;}
+.bg_h {.backgroundColor > .h;}
+.bg_i {.backgroundColor > .i;}
+.bg_j {.backgroundColor > .j;}
+.bg_k {.backgroundColor > .k;}
+.bg_l {.backgroundColor > .l;}
+.bg_m {.backgroundColor > .m;}
+.bg_n {.backgroundColor > .n;}
+.bg_o {.backgroundColor > .o;}
+.bg_p {.backgroundColor > .p;}
+.bg_q {.backgroundColor > .q;}
+.bg_r {.backgroundColor > .r;}
+.bg_s {.backgroundColor > .s;}
+.bg_t {.backgroundColor > .t;}
+.bg_u {.backgroundColor > .u;}
+.bg_v {.backgroundColor > .v;}
+.bg_w {.backgroundColor > .w;}
+.bg_x {.backgroundColor > .x;}
+.bg_y {.backgroundColor > .y;}
+.bg_z {.backgroundColor > .z;}
+.bg_zz {.backgroundColor > .zz;}
+
+.bg_a_hover {.backgroundColor > .hover > .a;}
+.bg_b_hover {.backgroundColor > .hover > .b;}
+.bg_c_hover {.backgroundColor > .hover > .c;}
+.bg_d_hover {.backgroundColor > .hover > .d;}
+.bg_e_hover {.backgroundColor > .hover > .e;}
+.bg_f_hover {.backgroundColor > .hover > .f;}
+.bg_g_hover {.backgroundColor > .hover > .g;}
+.bg_h_hover {.backgroundColor > .hover > .h;}
+.bg_i_hover {.backgroundColor > .hover > .i;}
+.bg_j_hover {.backgroundColor > .hover > .j;}
+.bg_l_hover {.backgroundColor > .hover > .l;}
+.bg_m_hover {.backgroundColor > .hover > .m;}
+.bg_n_hover {.backgroundColor > .hover > .n;}
+.bg_o_hover {.backgroundColor > .hover > .o;}
+.bg_p_hover {.backgroundColor > .hover > .p;}
+.bg_q_hover {.backgroundColor > .hover > .q;}
+.bg_r_hover {.backgroundColor > .hover > .r;}
+.bg_s_hover {.backgroundColor > .hover > .s;}
+.bg_t_hover {.backgroundColor > .hover > .t;}
+.bg_u_hover {.backgroundColor > .hover > .u;}
+.bg_v_hover {.backgroundColor > .hover > .v;}
+.bg_w_hover {.backgroundColor > .hover > .w;}
+.bg_x_hover {.backgroundColor > .hover > .x;}
+.bg_y_hover {.backgroundColor > .hover > .y;}
+.bg_z_hover {.backgroundColor > .hover > .z;}
+.bg_zz_hover {.backgroundColor > .hover > .zz;}
+
+.center {margin: 0 auto;}
+.vcenter {display: table-cell;vertical-align: middle;}
+.left {float: left;}
+.right {float: right;}
+.hand {cursor: pointer;}
+.default-pointer {cursor: default;}
+.text-center {text-align: center;}
+.display-none {display: none;}
+.nowrap {white-space: nowrap;}
+.bold {font-weight: bold;}
+.full-height {height: 100%;}
+.uppercase {text-transform: uppercase;}
+.capitalize {text-transform: capitalize;}
+.disabled {
+ //background-color: #f2f2f2;
+ //border-color: #d8d8d8;
+ //background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAIklEQVQIW2NkQAKrVq36zwjjgzhhYWGMYAEYB8RmROaABADeOQ8CXl/xfgAAAABJRU5ErkJggg==) repeat;
+ //color: @color_m;
+}
+
+.border-radius(@radius) {
+ -webkit-border-radius: @radius;
+ -moz-border-radius: @radius;
+ border-radius: @radius;
+}
+
+.border-radius-top-left(@radius) {
+ -webkit-border-top-left-radius: @radius;
+ -moz-border-radius-topleft: @radius;
+ -khtml-border-top-left-radius: @radius;
+ border-top-left-radius: @radius;
+}
+
+.border-radius-top-right(@radius) {
+ -webkit-border-top-right-radius: @radius;
+ -moz-border-radius-topright: @radius;
+ -khtml-border-top-right-radius: @radius;
+ border-top-right-radius: @radius;
+}
+
+.border-radius-bottom-left(@radius) {
+ -webkit-border-bottom-left-radius: @radius;
+ -moz-border-radius-bottomleft: @radius;
+ -khtml-border-bottom-left-radius: @radius;
+ border-bottom-left-radius: @radius;
+}
+
+.border-radius-bottom-right(@radius) {
+ -webkit-border-bottom-right-radius: @radius;
+ -moz-border-radius-bottomright: @radius;
+ -khtml-border-bottom-right-radius: @radius;
+ border-bottom-right-radius: @radius;
+}
+
+.opacity(@opacity: 0.5) {
+ -moz-opacity: @opacity;
+ -khtml-opacity: @opacity;
+ -webkit-opacity: @opacity;
+ opacity: @opacity;
+}
+
+.box-shadow(@arguments) {
+ -webkit-box-shadow: @arguments;
+ -moz-box-shadow: @arguments;
+ box-shadow: @arguments;
+}
+
+.square(@size) {
+ width: @size;
+ height: @size;
+}
+
+.selfClear:after {
+ content: ".";
+ display: block;
+ height: 0px;
+ clear: both;
+ visibility: hidden;
+}
+.selfClear {
+ display: inline-block;
+}
+
+.clearfix:after {
+ content: ".";
+ display: block;
+ clear: both;
+ visibility: hidden;
+ line-height: 0;
+ height: 0;
+}
+
+.clearfix {
+ display: inline-block;
+ clear: both;
+}
+
+.gradient(@d; @st; @st_o; @ec; @ec_0;){
+ background: -webkit-linear-gradient(@d, @st @st_o, @ec @ec_0);
+ background: -moz-linear-gradient(@d, @st @st_o, @ec @ec_0);
+ background: -o-linear-gradient(@d, @st @st_o, @ec @ec_0);
+ background: -ms-linear-gradient(@d, @st @st_o, @ec @ec_0);
+ background: linear-gradient(@d, @st @st_o, @ec @ec_0);
+}
+
+.hyphenate {
+ -ms-word-break: break-all;
+ word-break: break-all;
+
+ // Non standard for webkit
+ word-break: break-word;
+
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+}
+
+.user-select (@val;) {
+ -webkit-touch-callout: none;
+ -webkit-user-select: @val;
+ -khtml-user-select: @val;
+ -moz-user-select: @val;
+ -ms-user-select: @val;
+ user-select: @val;
+}
+
+.flex-fixed (@width) {
+ flex-basis: @width;
+ flex-grow: 0;
+ flex-shrink: 0;
+}
+
+.perfect-scrollbar {
+ position: relative;
+ overflow: hidden;
+}
+
+.noselect {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.arrow(@size, @color){
+ content: '';
+ position: absolute;
+ top: 50%;
+ right: 100%;
+ margin-top: -@size;
+ width: 0;
+ height: 0;
+ border-right: @size solid @color;
+ border-top: @size solid transparent;
+ border-bottom: @size solid transparent;
+}
+
+.circle(@size, @color){
+ background-color: @color;
+ .border-radius(50%);
+ color: @color_c;
+ content: '';
+ height: @size;
+ width: @size;
+ text-align: center;
+ display: block;
+}
+
+.sdc-ellipsis{
+ text-overflow: ellipsis;
+ overflow: hidden;
+ display: inline-block;
+ white-space: nowrap;
+ }
+
+// Works with animate.css
+.animation-duration(@seconds) {
+ animation-duration: @seconds; //3s
+ -webkit-animation-duration: @seconds;
+ -moz-animation-duration: @seconds;
+}
+
+.animation-delay(@seconds) {
+ animation-delay: @seconds; //3s
+ -webkit-animation-delay: @seconds;
+ -moz-animation-delay: @seconds;
+}
+
+.animation-iterations(@iterations) {
+ animation-iteration-count: @iterations; // infinite;
+ -webkit-animation-iteration-count: @iterations;
+ -moz-animation-iteration-count: @iterations;
+}
diff --git a/catalog-ui/app/styles/modal.less b/catalog-ui/app/styles/modal.less
new file mode 100644
index 0000000000..7c8bcd7c1d
--- /dev/null
+++ b/catalog-ui/app/styles/modal.less
@@ -0,0 +1,356 @@
+/* -----------------------------------------------------
+OVERRIDE BOOTSTRAP TO CENTER MODAL VERTICALLY.
+----------------------------------------------------- */
+.modal {
+ text-align: center;
+ padding: 0!important;
+}
+
+.modal:before {
+ content: '';
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
+ margin-right: -4px;
+}
+
+/* -----------------------------------------------------
+MODAL SIZES
+----------------------------------------------------- */
+.modal-dialog.modal-sdc-xl {
+ width: 1200px;
+}
+
+.modal-dialog.modal-sdc-l {
+ width: 875px;
+}
+
+.modal-dialog.modal-sdc-md {
+ width: 650px;
+}
+
+.modal-dialog.modal-sdc-sm {
+ width: 552px;
+}
+
+.modal-dialog.modal-sdc-xsm {
+ width: 432px;
+}
+
+/* -----------------------------------------------------
+MODAL WRAPPER
+----------------------------------------------------- */
+.modal-dialog {
+ /*
+ margin-top: 110px;
+ width: 1000px;
+ */
+ display: inline-block;
+ text-align: left;
+ vertical-align: middle;
+}
+
+.modal-content {
+ /*.border-radius(4px);
+ background-color: #ffffff;
+ box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.5);
+ min-height: 200px;*/
+}
+
+.modal-backdrop.in {
+ // opacity: .8;
+}
+
+.modal-backdrop {
+ background-color: #111922; //TODO: Replace the color
+}
+
+/* -----------------------------------------------------
+PREVIOUS MODAL
+----------------------------------------------------- */
+/*.w-sdc-modal,
+.modal-dialog,
+.w-sdc-modal-confirmation {
+
+ .w-sdc-modal-head {
+ .s_18_r;
+ height: 48px;
+ line-height: 48px;
+ text-align: center;
+ }
+
+ .w-sdc-modal-body {
+ height: auto;
+ padding: 20px 40px 20px 40px;
+ border-bottom: solid 1px @color_a;
+ position: relative;
+ }
+
+ .w-sdc-modal-body-content {
+ .b_1;
+ padding: 10px 0 0 0;
+ }
+
+ .w-sdc-modal-icon {
+ display: inline-block;
+ }
+
+ .w-sdc-modal-icon-DEBUG { .sprite; .message-DEBUG;}
+ .w-sdc-modal-icon-INFO { .sprite; .message-INFO;}
+ .w-sdc-modal-icon-WARNING { .sprite; .message-WARNING;}
+ .w-sdc-modal-icon-ERROR { .sprite; .message-ERROR;}
+
+ .w-sdc-modal-caption {
+ .a_6;
+ display: inline-block;
+ padding: 0 0 0 25px;
+ vertical-align: top;
+ }
+
+ .w-sdc-modal-action {
+ height: 99px;
+ text-align: center;
+ vertical-align: middle;
+ line-height: 105px;
+ }
+
+ .sdc-resource-viewer-modal-head {
+ .c_2;
+ border-radius: 6px 6px 0 0;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ }
+
+ .w-sdc-modal-close {
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAANCAYAAACdKY9CAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkZDQUM1MUVFMDg1NDExRTVBMzdBQ0IxMzM2OTYwNjM2IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkZDQUM1MUVGMDg1NDExRTVBMzdBQ0IxMzM2OTYwNjM2Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6RkNBQzUxRUMwODU0MTFFNUEzN0FDQjEzMzY5NjA2MzYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6RkNBQzUxRUQwODU0MTFFNUEzN0FDQjEzMzY5NjA2MzYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4EGRD0AAAAhElEQVR42mL8/+8fAxAIAvF7BvwArIYJSKQB8V0gNsajGKEGaMMZIP4PxO+A2BhkIxpOg8qDcAdIQBCPJmTFM8HOh0pg04ShGFkDNk0YitE1wDTdRVK8Ct1P+DyINSAY8HgQa0DgDA1coYdLMc7QQ464mVgiDV0TPOLKcShG1gRWAxBgACObyGI1tr+eAAAAAElFTkSuQmCC');
+ color: #3499F7; //TODO: Replace the color, should be sprite
+ .hand;
+ position: absolute;
+ right: 20px;
+ top: 20px;
+ display: block;
+ height: 13px;
+ width: 12px;
+ border-radius: 50%;
+ }
+
+ .w-sdc-resource-viewer-modal-close {
+ color: #3499F7; //TODO: Replace the color, should be sprite
+ .hand;
+ position: absolute;
+ right: 20px;
+ top: 5px;
+ display: block;
+ line-height: normal;
+ font-size: 18px;
+ font-weight: bold;
+ }
+
+}*/
+
+/* -----------------------------------------------------
+NEW DESIGN MODAL
+----------------------------------------------------- */
+.modal-type-standard.w-sdc-classic-modal {
+ .w-sdc-modal-head {
+ border-bottom: solid 3px @main_color_a;
+ }
+}
+.modal-type-error.w-sdc-classic-modal {
+ .w-sdc-modal-head {
+ border-bottom: solid 3px @func_color_q;
+ }
+}
+.modal-type-alert.w-sdc-classic-modal {
+ .w-sdc-modal-head {
+ border-bottom: solid 3px @main_color_h;
+ }
+}
+
+.w-sdc-classic-modal {
+
+ display: flex;
+ flex-direction: column;
+ .border-radius(4px);
+ background-color: #ffffff;
+ box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.5);
+ min-height: 165px;
+ min-width: 430px;
+
+ .w-sdc-modal-head {
+ flex-grow: 1;
+ .s_18_m;
+ height: 48px;
+ line-height: 48px;
+ display: flex;
+ text-align: left;
+ border-bottom: solid 3px @main_color_a;
+ align-items: center;
+
+ .w-sdc-modal-head-text {
+ .s_18_m;
+ flex-grow: 999;
+ }
+
+ .w-sdc-modal-close {
+ flex-grow: 1;
+ .sprite;
+ .sprite.x-btn-black;
+ cursor: pointer;
+ }
+ }
+
+ .w-sdc-modal-body {
+ flex-grow: 999;
+ .m_14_r;
+ padding-top: 20px;
+ position: relative;
+ border-bottom: none;
+ }
+
+ .w-sdc-modal-footer {
+ clear: both;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ border-radius: 6px;
+ background-color: #f8f8f8;
+
+ button {
+ display: block;
+ margin-right: 11px;
+ }
+
+ button:last-child {
+ margin-right: 0;
+ }
+
+ }
+
+}
+
+.w-sdc-classic-top-line-modal {
+
+ display: flex;
+ flex-direction: column;
+ background-color: #ffffff;
+ box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.5);
+ min-height: 165px;
+ min-width: 430px;
+ border-top: solid 3px @main_color_a;
+
+ .w-sdc-modal-head {
+ flex-grow: 1;
+ .s_18_r;
+ height: 48px;
+ line-height: 48px;
+ display: flex;
+ text-align: left;
+ align-items: center;
+
+ .w-sdc-modal-head-text {
+ .s_18_m;
+ flex-grow: 999;
+ }
+
+ .w-sdc-modal-close {
+ flex-grow: 1;
+ .sprite;
+ .sprite.x-btn-black;
+ cursor: pointer;
+ }
+ }
+
+ .w-sdc-modal-body {
+ flex-grow: 999;
+ .m_14_r;
+ padding-top: 10px;
+ position: relative;
+ border-bottom: none;
+ }
+
+ .w-sdc-modal-footer {
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+
+ button {
+ display: block;
+ margin-right: 11px;
+ }
+
+ button:last-child {
+ margin-right: 0;
+ }
+
+ }
+
+}
+
+.modal-sdc-xl {
+ .w-sdc-classic-modal {
+ padding: 0 40px;
+
+ .w-sdc-modal-head {
+ .b_15;
+ height: 60px;
+ line-height: 60px;
+
+ .w-sdc-modal-head-text {
+ flex-grow: 999;
+ }
+
+ .w-sdc-modal-close {
+ top: 27px;
+ right: 41px;
+ }
+ }
+
+ .w-sdc-modal-footer {
+ height: 80px;
+ margin: 0 -40px;
+ padding: 0 40px;
+ background-color: #f2f2f2;
+ }
+
+ }
+}
+
+.modal-sdc-xl,
+.modal-sdc-l,
+.modal-sdc-md,
+.modal-sdc-sm,
+.modal-sdc-xsm {
+
+ .w-sdc-classic-top-line-modal {
+ padding: 0 30px;
+
+ .w-sdc-modal-footer {
+ margin: 0 -30px;
+ padding: 17px 30px;
+ }
+ }
+
+ .w-sdc-classic-modal {
+ padding: 0 30px;
+
+ .w-sdc-modal-head {
+ .b_19;
+ height: 50px;
+ line-height: 50px;
+
+ .w-sdc-modal-close {
+ top: 21px;
+ right: 30px;
+ }
+ }
+
+ .w-sdc-modal-footer {
+ margin: 0 -30px;
+ padding: 17px 30px;
+ }
+ }
+}
diff --git a/catalog-ui/app/styles/scroller.less b/catalog-ui/app/styles/scroller.less
new file mode 100644
index 0000000000..fcdaf12f3f
--- /dev/null
+++ b/catalog-ui/app/styles/scroller.less
@@ -0,0 +1,15 @@
+// Inside
+.ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y {
+ .bg_b;
+ .border-radius(0px);
+ opacity: 1;
+ width: 6px;
+ margin: 0 2px;
+}
+// Outside
+.ps-container > .ps-scrollbar-y-rail{
+ .border-radius(0px);
+ width: 10px ;
+ background-color: rgba(255,255,255,0.5);
+ z-index: 1000;
+}
diff --git a/catalog-ui/app/styles/sprite-old.less b/catalog-ui/app/styles/sprite-old.less
new file mode 100644
index 0000000000..cb2dc2fbe0
--- /dev/null
+++ b/catalog-ui/app/styles/sprite-old.less
@@ -0,0 +1,132 @@
+.sprite {
+ background-image: url('images/sprites/sprite-global-old.png');
+ display: inline-block;
+}
+
+.sprite.logo { background-position: -50px -100px; width: 39px; height: 32px;}
+.sprite.white-arrow-down { background-position: -50px -200px; width: 15px; height: 9px;}
+.sprite.white-arrow-up { background-position: -100px -200px; width: 15px; height: 9px;}
+.arrow-left { background-position: -262px -4041px; width: 11px; height: 10px;}
+.sprite.tab-info { background-position: -50px -300px; width: 18px; height: 20px;}
+.sprite.edit { background-position: -51px -689px; width: 31px; height: 30px;}
+.sprite.details { background-position: -50px -900px; width: 19px; height: 19px;}
+.sprite.structure { background-position: -212px -3880px; width: 23px; height: 21px;}
+.sprite.artifacts { background-position: -50px -1098px; width: 20px; height: 20px;}
+.sprite.menu { background-position: -50px -1200px; width: 17px; height: 14px;}
+.sprite.catalog { background-position: -53px -1303px; width: 20px; height: 20px;}
+.sprite.distribution { background-position: -50px -1400px; width: 24px; height: 14px;}
+.sprite.support { background-position: -50px -1500px; width: 22px; height: 22px;}
+.sprite.filter { background-position: -50px -1600px; width: 15px; height: 16px;}
+.sprite.card-menu { background-position: -50px -2000px; width: 4px; height: 16px;}
+.sprite.relationships { background-position: -50px -2097px; width: 26px; height: 19px;}
+.sprite.lifecycle { background-position: -50px -2200px; width: 20px; height: 20px;}
+.sprite.properties { background-position: -50px -2300px; width: 19px; height: 19px;}
+.sprite.e-sdc-small-icon-delete { background-position: -64px -2771px; width: 11px; height: 13px;}
+.sprite.magnification-glass { background-position: -49px -3138px; width: 12px; height: 12px;}
+.sprite.clear-text { background-position: -104px -4040px; width: 10px; height: 11px;}
+/*new tabs icons*/
+.sprite.info { background-position: -105px -3583px; width: 18px; height: 18px;}
+.sprite.information-artifacts { background-position: -105px -3632px; width: 20px; height: 21px;}
+.sprite.deployment-artifacts { background-position: -105px -3682px; width: 19px; height: 19px;}
+.sprite.properties { background-position: -105px -3733px; width: 18px; height: 18px;}
+.sprite.relations { background-position: -105px -3781px; width: 20px; height: 20px;}
+.sprite.inputs { background-position: -104px -3887px; width: 18px; height: 14px;}
+.sprite.api { background-position: -104px -3930px; width: 25px; height: 21px;}
+
+.sprite.NOT_CERTIFIED_CHECKOUT { background-position: -54px -2887px; width: 13px; height: 13px;}
+.sprite.CERTIFICATION_IN_PROGRESS { background-position: -54px -2934px; width: 13px; height: 16px;}
+.sprite.NOT_CERTIFIED_CHECKIN { background-position: -54px -2985px; width: 13px; height: 15px;}
+.sprite.CERTIFIED { background-position: -54px -3034px; width: 13px; height: 16px;}
+.sprite.READY_FOR_CERTIFICATION { background-position: -49px -3087px; width: 23px; height: 16px;}
+
+.message-DEBUG { background-position: -51px -3187px; width: 47px; height: 47px;}
+.message-INFO { background-position: -51px -3187px; width: 47px; height: 47px;}
+.message-WARNING { background-position: -51px -3187px; width: 47px; height: 47px;}
+.message-ERROR { background-position: -51px -3187px; width: 47px; height: 47px;}
+
+.e-sdc-small-icon-eye { background-position: -60px -2722px; width: 20px; height: 20px;}
+.e-sdc-small-icon-pencil { background-position: -51px -3339px; width: 12px; height: 12px;}
+.e-sdc-small-icon-pad { background-position: -157px -2772px; width: 15px; height: 12px;}
+.sprite.e-sdc-small-icon-trash { background-position: -48px -300px; width: 20px; height: 20px;}
+.sprite.e-sdc-small-check { background-position: -48px -399px; width: 20px; height: 20px;}
+.sprite.e-sdc-small-clone { background-position: -49px -501px; width: 20px; height: 20px;}
+.sprite.e-sdc-small-print-screen { background-position: -49px -351px; width: 19px; height: 20px;}
+.sprite.e-sdc-small-certification { background-position: -48px -602px; width: 20px; height: 20px;}
+.sprite.e-sdc-small-download { background-position: -51px -3390px; width: 12px; height: 11px;}
+.sprite.e-sdc-cloud { background-position: -51px -3278px; width: 24px; height: 23px;}
+.sprite.e-sdc-pencil { background-position: -53px -2404px; width: 16px; height: 16px;}
+.sprite.e-sdc-green-save { background-position: -157px -4089px; width: 12px; height: 12px;}
+
+/* canvas items non-certified indicator */
+.s-sdc-state-non-certified { background-position: -157px -3386px; width: 15px; height: 15px;}
+
+/* dashboard card main icons */
+.s-sdc-service { background-position: -60px -2654px; width: 14px; height: 17px;}
+.s-sdc-resource { background-position: -54px -2585px; width: 16px; height: 16px;}
+
+/* dashboard card statuses icons */
+.s-sdc-state.NOT_CERTIFIED_CHECKIN { background-position: -47px -2839px; width: 20px; height: 19px;}
+.s-sdc-state.NOT_CERTIFIED_CHECKIN.green { background-position: -70px -2839px;}
+.s-sdc-state.NOT_CERTIFIED_CHECKIN.red { background-position: -93px -2839px;}
+
+.s-sdc-state.NOT_CERTIFIED_CHECKOUT { background-position: -53px -2889px; width: 14px; height: 14px;}
+.s-sdc-state.NOT_CERTIFIED_CHECKOUT.green { background-position: -76px -2889px;}
+.s-sdc-state.NOT_CERTIFIED_CHECKOUT.red { background-position: -99px -2889px;}
+
+.s-sdc-state.CERTIFIED { background-position: -53px -3034px; width: 14px; height: 16px;}
+.s-sdc-state.CERTIFIED.green { background-position: -76px -3034px;}
+.s-sdc-state.CERTIFIED.red { background-position: -99px -3034px;}
+
+.s-sdc-state.READY_FOR_CERTIFICATION { background-position: -53px -2985px; width: 14px; height: 16px;}
+.s-sdc-state.READY_FOR_CERTIFICATION.green { background-position: -76px -2985px;}
+.s-sdc-state.READY_FOR_CERTIFICATION.red { background-position: -99px -2985px;}
+
+.s-sdc-state.CERTIFICATION_IN_PROGRESS { background-position: -53px -2934px; width: 14px; height: 16px;}
+.s-sdc-state.CERTIFICATION_IN_PROGRESS.green { background-position: -76px -2934px;}
+.s-sdc-state.CERTIFICATION_IN_PROGRESS.red { background-position: -99px -2934px; }
+
+.s-sdc-state.DISTRIBUTED { background-position: -43px -3087px; width: 24px; height: 14px;}
+.s-sdc-state.DISTRIBUTED.green { background-position: -76px -3087px;}
+.s-sdc-state.DISTRIBUTED.red { background-position: -113px -3087px;}
+
+.sprite.e-sdc-small-blue-down-arrow { background-position: -51px -3262px; width: 10px; height: 7px;}
+.sprite.e-sdc-small-blue-up-arrow { background-position: -111px -3262px; width: 10px; height: 7px;}
+
+.e-checkbox-unchecked { background-position: -51px -3490px; width: 12px; height: 11px;}
+.e-checkbox-checked { background-position: -103px -3490px; width: 12px; height: 11px;}
+.e-checkbox-unchecked-disabled { background-position: -265px -3490px; width: 12px; height: 11px;}
+
+/* viewer modal */
+.sprite.x-btn { background-position: -50px -3438px; width: 14px; height: 13px;}
+.sprite.x-btn:hover { background-position: -103px -3438px; width: 14px; height: 13px;}
+.sprite.x-btn-white { background-position: -157px -3437px; width: 14px; height: 13px;}
+.sprite.x-btn-white:hover { background-position: -211px -3438px; width: 14px; height: 13px;}
+.sprite.x-btn-black { background-position: -52px -4041px; width: 10px; height: 11px;}
+.sprite.x-btn-black:hover { background-position: -104px -4041px; width: 10px; height: 11px;}
+.sprite.small-x-btn-black { background-position: -157px -4044px; width: 8px; height: 8px;}
+.sprite.small-x-btn-black:hover { background-position: -211px -4044px; width: 8px; height: 8px;}
+
+
+/* input file upload icons */
+.sprite.upload-icon { background-position: -51px -3286px; width: 16px; height: 15px; }
+.sprite.upload-icon-hover { background-position: -104px -3286px; }
+.sprite.uploaded { background-position: -143px -3283px; width: 18px; height: 18px; }
+.sprite.change-file { background-position: -261px -3285px; width: 22px; height: 16px; }
+.sprite.change-file-hover { background-position: -211px -3285px; }
+.sprite.e-sdc-small-icon-upload { background-position: -104px -4188px; width: 12px; height: 13px; }
+
+/* error messages icons */
+.exclamation-mark-white-icon { background-position: -51px -3990px; width: 13px; height: 11px; }
+.exclamation-mark-red-icon { background-position: -212px -3990px; width: 13px; height: 11px; }
+.question-message-icon-gray { background-position: -104px -3987px; width: 14px; height: 14px; }
+.question-message-icon-white { background-position: -158px -3987px; width: 14px; height: 14px; }
+
+/* wizard icons */
+.sprite.table-arrow { background-position: -57px -4241px; width: 7px; height: 10px; }
+.sprite.table-arrow.opened { background-position: -54px -4295px; width: 10px; height: 6px; }
+
+.sprite-green-tick { background-position: -157px -3290px; width: 15px; height: 11px; }
+
+/* tags category relation icon*/
+.sprite.relation-icon { background-position: -100px -4351px; width: 24px; height: 24px; }
+.sprite.relation-icon-hover { background-position: -52px -4351px; width: 24px; height: 24px; }
diff --git a/catalog-ui/app/styles/sprite-product-icons.less b/catalog-ui/app/styles/sprite-product-icons.less
new file mode 100644
index 0000000000..3485ec89a3
--- /dev/null
+++ b/catalog-ui/app/styles/sprite-product-icons.less
@@ -0,0 +1,71 @@
+.sprite-product-icons {
+ background-image: url('images/sprites/sprite-product-icons.png');
+ display: inline-block;
+}
+
+.sprite-product-icons.disable { opacity:0.5;}
+
+.sprite-product-icons.setting { background-position: -291px -102px; width: 61px; height: 67px;}
+.sprite-product-icons.setting.small { background-position: -217px -142px; width: 29px; height: 28px;}
+.sprite-product-icons.setting.medium { background-position: -148px -130px; width: 41px; height: 40px;}
+.sprite-product-icons.setting.large { background-position: -77px -110px; width: 60px; height: 60px;}
+
+.sprite-product-icons.cloud { background-position: -290px -215px; width: 64px; height: 43px;}
+.sprite-product-icons.cloud.small { background-position: -217px -232px; width: 29px; height: 28px;}
+.sprite-product-icons.cloud.medium { background-position: -148px -220px; width: 41px; height: 40px;}
+.sprite-product-icons.cloud.large { background-position: -77px -200px; width: 60px; height: 60px;}
+
+.sprite-product-icons.security { background-position: -289px -293px; width: 50px; height: 57px;}
+.sprite-product-icons.security.small { background-position: -217px -321px; width: 29px; height: 28px;}
+.sprite-product-icons.security.medium { background-position: -148px -310px; width: 41px; height: 40px;}
+.sprite-product-icons.security.large { background-position: -77px -290px; width: 60px; height: 60px;}
+
+.sprite-product-icons.network { background-position: -290px -383px; width: 56px; height: 57px;}
+.sprite-product-icons.network.small { background-position: -217px -411px; width: 29px; height: 29px;}
+.sprite-product-icons.network.medium { background-position: -148px -399px; width: 41px; height: 41px;}
+.sprite-product-icons.network.large { background-position: -77px -380px; width: 60px; height: 60px;}
+
+.sprite-product-icons.orphan { background-position: -290px -478px; width: 52px; height: 52px;}
+.sprite-product-icons.orphan.small { background-position: -217px -500px; width: 29px; height: 29px;}
+.sprite-product-icons.orphan.medium { background-position: -148px -488px; width: 41px; height: 41px;}
+.sprite-product-icons.orphan.large { background-position: -77px -470px; width: 60px; height: 60px;}
+
+.sprite-product-icons.defaulticon { background-position: -290px -478px; width: 52px; height: 52px;}
+.sprite-product-icons.defaulticon.small { background-position: -217px -500px; width: 29px; height: 29px;}
+.sprite-product-icons.defaulticon.medium { background-position: -148px -488px; width: 41px; height: 41px;}
+.sprite-product-icons.defaulticon.large { background-position: -77px -470px; width: 60px; height: 60px;}
+
+.sprite-product-icons.vfw { background-position: -289px -548px; width: 73px; height: 73px;}
+.sprite-product-icons.vfw.small { background-position: -217px -591px; width: 29px; height: 29px;}
+.sprite-product-icons.vfw.medium { background-position: -148px -580px; width: 41px; height: 41px;}
+.sprite-product-icons.vfw.large { background-position: -77px -560px; width: 60px; height: 61px;}
+
+.sprite-product-icons.wanx { background-position: -290px -664px; width: 59px; height: 47px;}
+.sprite-product-icons.wanx.small { background-position: -217px -681px; width: 30px; height: 30px;}
+.sprite-product-icons.wanx.medium { background-position: -148px -670px; width: 41px; height: 41px;}
+.sprite-product-icons.wanx.large { background-position: -77px -651px; width: 60px; height: 60px;}
+
+.sprite-product-icons.vrouter { background-position: -289px -758px; width: 69px; height: 43px;}
+.sprite-product-icons.vrouter.small { background-position: -217px -772px; width: 29px; height: 29px;}
+.sprite-product-icons.vrouter.medium { background-position: -148px -760px; width: 41px; height: 41px;}
+.sprite-product-icons.vrouter.large { background-position: -77px -741px; width: 60px; height: 60px;}
+
+.sprite-product-icons.ucpe { background-position: -289px -832px; width: 59px; height: 60px;}
+.sprite-product-icons.ucpe.small { background-position: -217px -862px; width: 28px; height: 29px;}
+.sprite-product-icons.ucpe.medium { background-position: -148px -849px; width: 41px; height: 41px;}
+.sprite-product-icons.ucpe.large { background-position: -77px -831px; width: 60px; height: 61px;}
+
+.sprite-product-icons.mobility { background-position: -288px -919px; width: 64px; height: 62px;}
+.sprite-product-icons.mobility.small { background-position: -216px -952px; width: 29px; height: 29px;}
+.sprite-product-icons.mobility.medium { background-position: -147px -940px; width: 41px; height: 41px;}
+.sprite-product-icons.mobility.large { background-position: -76px -921px; width: 60px; height: 60px;}
+
+.sprite-product-icons.wanx_customer_managed { background-position: -290px -1024px; width: 58px; height: 62px;}
+.sprite-product-icons.wanx.small_customer_managed { background-position: -217px -1041px; width: 30px; height: 38px;}
+.sprite-product-icons.wanx.medium_customer_managed { background-position: -148px -1030px; width: 42px; height: 47px;}
+.sprite-product-icons.wanx.large_customer_managed { background-position: -77px -1011px; width: 61px; height: 66px;}
+
+.sprite-product-icons.wanx_attr_managed { background-position: -290px -1114px; width: 59px; height: 62px;}
+.sprite-product-icons.wanx.small_att_managed { background-position: -218px -1131px; width: 31px; height: 36px;}
+.sprite-product-icons.wanx.medium_att_managed { background-position: -149px -1120px; width: 42px; height: 47px;}
+.sprite-product-icons.wanx.large_att_managed { background-position: -77px -1101px; width: 60px; height: 67px;}
diff --git a/catalog-ui/app/styles/sprite-resource-icons.less b/catalog-ui/app/styles/sprite-resource-icons.less
new file mode 100644
index 0000000000..0f36527dda
--- /dev/null
+++ b/catalog-ui/app/styles/sprite-resource-icons.less
@@ -0,0 +1,258 @@
+.sprite-resource-icons {
+ background-image: url('images/sprites/sprite-resource-icons.png');
+ display: inline-block;
+}
+
+.sprite-resource-icons.disable { opacity:0.5;}
+
+.sprite-resource-icons.borderElement { background-position: -282px -41px; width: 66px; height: 58px;}
+.sprite-resource-icons.borderElement.small { background-position: -210px -71px; width: 28px; height: 28px;}
+.sprite-resource-icons.borderElement.medium { background-position: -140px -59px; width: 40px; height: 40px;}
+.sprite-resource-icons.borderElement.large { background-position: -70px -39px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.router { background-position: -282px -109px; width: 60px; height: 60px;}
+.sprite-resource-icons.router.small { background-position: -210px -141px; width: 28px; height: 28px;}
+.sprite-resource-icons.router.medium { background-position: -140px -129px; width: 40px; height: 40px;}
+.sprite-resource-icons.router.large { background-position: -70px -109px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.database { background-position: -282px -180px; width: 63px; height: 56px;}
+.sprite-resource-icons.database.small { background-position: -210px -211px; width: 28px; height: 28px;}
+.sprite-resource-icons.database.medium { background-position: -140px -199px; width: 40px; height: 40px;}
+.sprite-resource-icons.database.large { background-position: -70px -179px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.network { background-position: -282px -252px; width: 57px; height: 57px;}
+.sprite-resource-icons.network.small { background-position: -210px -281px; width: 28px; height: 28px;}
+.sprite-resource-icons.network.medium { background-position: -140px -269px; width: 40px; height: 40px;}
+.sprite-resource-icons.network.large { background-position: -70px -249px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.applicationServer { background-position: -282px -327px; width: 37px; height: 51px;}
+.sprite-resource-icons.applicationServer.small { background-position: -210px -351px; width: 28px; height: 28px;}
+.sprite-resource-icons.applicationServer.medium { background-position: -140px -339px; width: 40px; height: 40px;}
+.sprite-resource-icons.applicationServer.large { background-position: -70px -319px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.server { background-position: -282px -398px; width: 31px; height: 51px;}
+.sprite-resource-icons.server.small { background-position: -210px -421px; width: 28px; height: 28px;}
+.sprite-resource-icons.server.medium { background-position: -140px -409px; width: 40px; height: 40px;}
+.sprite-resource-icons.server.large { background-position: -70px -389px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.port { background-position: -281px -484px; width: 41px; height: 35px;}
+.sprite-resource-icons.port.small { background-position: -210px -491px; width: 28px; height: 28px;}
+.sprite-resource-icons.port.medium { background-position: -140px -479px; width: 40px; height: 40px;}
+.sprite-resource-icons.port.large { background-position: -70px -459px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.loadBalancer { background-position: -282px -539px; width: 45px; height: 49px;}
+.sprite-resource-icons.loadBalancer.small { background-position: -210px -561px; width: 28px; height: 28px;}
+.sprite-resource-icons.loadBalancer.medium { background-position: -140px -549px; width: 40px; height: 40px;}
+.sprite-resource-icons.loadBalancer.large { background-position: -70px -529px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.objectStorage { background-position: -282px -611px; width: 50px; height: 47px;}
+.sprite-resource-icons.objectStorage.small { background-position: -210px -631px; width: 28px; height: 28px;}
+.sprite-resource-icons.objectStorage.medium { background-position: -140px -619px; width: 40px; height: 40px;}
+.sprite-resource-icons.objectStorage.large { background-position: -70px -599px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.compute { background-position: -282px -683px; width: 45px; height: 45px;}
+.sprite-resource-icons.compute.small { background-position: -210px -701px; width: 28px; height: 28px;}
+.sprite-resource-icons.compute.medium { background-position: -141px -688px; width: 40px; height: 40px;}
+.sprite-resource-icons.compute.large { background-position: -70px -669px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.gateway { background-position: -282px -758px; width: 45px; height: 41px}
+.sprite-resource-icons.gateway.small { background-position: -210px -772px; width: 28px; height: 27px;}
+.sprite-resource-icons.gateway.medium { background-position: -140px -759px; width: 40px; height: 40px;}
+.sprite-resource-icons.gateway.large { background-position: -70px -739px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.connector { background-position: -282px -830px; width: 44px; height: 38px;}
+.sprite-resource-icons.connector.small { background-position: -210px -842px; width: 28px; height: 27px;}
+.sprite-resource-icons.connector.medium { background-position: -140px -829px; width: 40px; height: 40px;}
+.sprite-resource-icons.connector.large { background-position: -70px -809px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.brocade { background-position: -282px -922px; width: 91px; height: 16px;}
+.sprite-resource-icons.brocade.small { background-position: -210px -911px; width: 28px; height: 28px;}
+.sprite-resource-icons.brocade.medium { background-position: -140px -899px; width: 40px; height: 40px;}
+.sprite-resource-icons.brocade.large { background-position: -70px -879px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.cisco { background-position: -282px -975px; width: 63px; height: 33px;}
+.sprite-resource-icons.cisco.small { background-position: -210px -981px; width: 28px; height: 28px;}
+.sprite-resource-icons.cisco.medium { background-position: -140px -969px; width: 40px; height: 40px;}
+.sprite-resource-icons.cisco.large { background-position: -70px -949px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.ericsson { background-position: -282px -1059px; width: 101px; height: 20px;}
+.sprite-resource-icons.ericsson.small { background-position: -210px -1051px; width: 28px; height: 28px;}
+.sprite-resource-icons.ericsson.medium { background-position: -140px -1039px; width: 40px; height: 40px;}
+.sprite-resource-icons.ericsson.large { background-position: -70px -1019px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.tropo { background-position: -282px -1109px; width: 48px; height: 39px;}
+.sprite-resource-icons.tropo.small { background-position: -210px -1121px; width: 28px; height: 28px;}
+.sprite-resource-icons.tropo.medium { background-position: -140px -1109px; width: 40px; height: 40px;}
+.sprite-resource-icons.tropo.large { background-position: -70px -1089px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.mySql { background-position: -282px -1172px; width: 89px; height: 46px;}
+.sprite-resource-icons.mySql.small { background-position: -210px -1191px; width: 28px; height: 28px;}
+.sprite-resource-icons.mySql.medium { background-position: -140px -1179px; width: 40px; height: 40px;}
+.sprite-resource-icons.mySql.large { background-position: -70px -1159px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.oracle { background-position: -282px -1277px; width: 97px; height: 11px;}
+.sprite-resource-icons.oracle.small { background-position: -210px -1261px; width: 28px; height: 28px;}
+.sprite-resource-icons.oracle.medium { background-position: -140px -1249px; width: 40px; height: 40px;}
+.sprite-resource-icons.oracle.large { background-position: -70px -1229px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.defaulticon { background-position: -282px -2069px; width: 60px; height: 60px;}
+.sprite-resource-icons.defaulticon.small { background-position: -210px -2101px; width: 28px; height: 28px;}
+.sprite-resource-icons.defaulticon.medium { background-position: -141px -2089px; width: 40px; height: 40px;}
+.sprite-resource-icons.defaulticon.large { background-position: -70px -2069px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.fortinet { background-position: -282px -1419px; width: 94px; height: 10px;}
+.sprite-resource-icons.fortinet.small { background-position: -210px -1401px; width: 28px; height: 28px;}
+.sprite-resource-icons.fortinet.medium { background-position: -140px -1389px; width: 40px; height: 40px;}
+.sprite-resource-icons.fortinet.large { background-position: -70px -1369px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.nokia_siemens { background-position: -281px -1474px; width: 89px; height: 25px;}
+.sprite-resource-icons.nokia_siemens.small { background-position: -210px -1471px; width: 28px; height: 28px;}
+.sprite-resource-icons.nokia_siemens.medium { background-position: -140px -1459px; width: 40px; height: 40px;}
+.sprite-resource-icons.nokia_siemens.large { background-position: -70px -1439px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.att { background-position: -281px -1538px; width: 77px; height: 31px;}
+.sprite-resource-icons.att.small { background-position: -210px -1541px; width: 28px; height: 28px;}
+.sprite-resource-icons.att.medium { background-position: -140px -1529px; width: 40px; height: 40px;}
+.sprite-resource-icons.att.large { background-position: -70px -1509px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.juniper { background-position: -281px -1609px; width: 93px; height: 31px;}
+.sprite-resource-icons.juniper.small { background-position: -210px -1611px; width: 28px; height: 28px;}
+.sprite-resource-icons.juniper.medium { background-position: -140px -1599px; width: 40px; height: 40px;}
+.sprite-resource-icons.juniper.large { background-position: -70px -1579px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.cloud { background-position: -281px -1664px; width: 44px; height: 45px;}
+.sprite-resource-icons.cloud.small { background-position: -210px -1681px; width: 28px; height: 28px;}
+.sprite-resource-icons.cloud.medium { background-position: -140px -1669px; width: 40px; height: 40px;}
+.sprite-resource-icons.cloud.large { background-position: -70px -1649px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.call_controll { background-position: -278px -1739px; width: 35px; height: 40px;}
+.sprite-resource-icons.call_controll.small { background-position: -210px -1751px; width: 28px; height: 28px;}
+.sprite-resource-icons.call_controll.medium { background-position: -140px -1739px; width: 40px; height: 40px;}
+.sprite-resource-icons.call_controll.large { background-position: -70px -1719px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.broadsoft { background-position: -282px -1824px; width: 108px; height: 22px;}
+.sprite-resource-icons.broadsoft.small { background-position: -210px -1821px; width: 28px; height: 28px;}
+.sprite-resource-icons.broadsoft.medium { background-position: -140px -1809px; width: 40px; height: 40px;}
+.sprite-resource-icons.broadsoft.large { background-position: -70px -1789px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.alcatelLucent { background-position: -281px -1901px; width: 118px; height: 25px;}
+.sprite-resource-icons.alcatelLucent.small { background-position: -210px -1891px; width: 28px; height: 28px;}
+.sprite-resource-icons.alcatelLucent.medium { background-position: -140px -1879px; width: 40px; height: 40px;}
+.sprite-resource-icons.alcatelLucent.large { background-position: -70px -1859px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.metaswitch { background-position: -282px -1961px; width: 115px; height: 27px;}
+.sprite-resource-icons.metaswitch.small { background-position: -210px -1961px; width: 28px; height: 28px;}
+.sprite-resource-icons.metaswitch.medium { background-position: -140px -1949px; width: 40px; height: 40px;}
+.sprite-resource-icons.metaswitch.large { background-position: -70px -1929px; width: 61px; height: 60px;}
+
+.sprite-resource-icons.aricent { background-position: -282px -2046px; width: 93px; height: 13px;}
+.sprite-resource-icons.aricent.small { background-position: -210px -2031px; width: 28px; height: 28px;}
+.sprite-resource-icons.aricent.medium { background-position: -140px -2019px; width: 40px; height: 40px;}
+.sprite-resource-icons.aricent.large { background-position: -70px -1999px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.cp { background-position: -90px -4038px; width: 21px; height: 21px;}
+.sprite-resource-icons.cp.small { background-position: -90px -4038px; width: 21px; height: 21px;}
+.sprite-resource-icons.cp.medium { background-position: -151px -4048px; width: 21px; height: 21px;}
+.sprite-resource-icons.cp.large { background-position: -194px -4034px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.vl { background-position: -210px -2591px; width: 28px; height: 28px;}
+.sprite-resource-icons.vl.small { background-position: -210px -2591px; width: 28px; height: 28px;}
+.sprite-resource-icons.vl.medium { background-position: -141px -2579px; width: 40px; height: 40px;}
+.sprite-resource-icons.vl.large { background-position: -70px -2560px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.cloudep { background-position: -282px -2225px; width: 63px; height: 43px;}
+.sprite-resource-icons.cloudep.small { background-position: -209px -2241px; width: 28px; height: 28px;}
+.sprite-resource-icons.cloudep.medium { background-position: -141px -2229px; width: 40px; height: 40px;}
+.sprite-resource-icons.cloudep.large { background-position: -70px -2209px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.personep { background-position: -282px -2292px; width: 47px; height: 47px;}
+.sprite-resource-icons.personep.small { background-position: -210px -2311px; width: 28px; height: 28px;}
+.sprite-resource-icons.personep.medium { background-position: -141px -2299px; width: 40px; height: 40px;}
+.sprite-resource-icons.personep.large { background-position: -70px -2279px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.premisesep { background-position: -281px -2360px; width: 41px; height: 50px;}
+.sprite-resource-icons.premisesep.small { background-position: -210px -2380px; width: 28px; height: 28px;}
+.sprite-resource-icons.premisesep.medium { background-position: -141px -2368px; width: 40px; height: 40px;}
+.sprite-resource-icons.premisesep.large { background-position: -70px -2349px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.ossep { background-position: -281px -2420px; width: 67px; height: 59px;}
+.sprite-resource-icons.ossep.small { background-position: -210px -2451px; width: 28px; height: 28px;}
+.sprite-resource-icons.ossep.medium { background-position: -141px -2439px; width: 40px; height: 40px;}
+.sprite-resource-icons.ossep.large { background-position: -70px -2419px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.firewall { background-position: -282px -3257px; width: 73px; height: 74px;}
+.sprite-resource-icons.firewall.small { background-position: -210px -3301px; width: 29px; height: 29px;}
+.sprite-resource-icons.firewall.medium { background-position: -141px -3289px; width: 41px; height: 41px;}
+.sprite-resource-icons.firewall.large { background-position: -70px -3270px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.vfw { background-position: -282px -3257px; width: 73px; height: 74px;}
+.sprite-resource-icons.vfw.small { background-position: -210px -3301px; width: 29px; height: 29px;}
+.sprite-resource-icons.vfw.medium { background-position: -141px -3289px; width: 41px; height: 41px;}
+.sprite-resource-icons.vfw.large { background-position: -70px -3270px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.ucpe { background-position: -282px -3120px; width: 59px; height: 60px;}
+.sprite-resource-icons.ucpe.small { background-position: -210px -3150px; width: 28px; height: 28px;}
+.sprite-resource-icons.ucpe.medium { background-position: -141px -3137px; width: 41px; height: 41px;}
+.sprite-resource-icons.ucpe.large { background-position: -70px -3119px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.vrouter { background-position: -282px -3066px; width: 69px; height: 43px;}
+.sprite-resource-icons.vrouter.small { background-position: -210px -3080px; width: 29px; height: 29px;}
+.sprite-resource-icons.vrouter.medium { background-position: -141px -3068px; width: 41px; height: 41px;}
+.sprite-resource-icons.vrouter.large { background-position: -70px -3049px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.wanx { background-position: -283px -2992px; width: 58px; height: 47px;}
+.sprite-resource-icons.wanx.small { background-position: -210px -3009px; width: 30px; height: 30px;}
+.sprite-resource-icons.wanx.medium { background-position: -141px -2998px; width: 41px; height: 41px;}
+.sprite-resource-icons.wanx.large { background-position: -70px -2979px; width: 60px; height: 60px;}
+
+//.sprite-resource-icons.roles { background-position: -282px -2069px; width: 60px; height: 60px;}
+//.sprite-resource-icons.roles.small { background-position: -210px -2101px; width: 28px; height: 28px;}
+//.sprite-resource-icons.roles.medium { background-position: -141px -2089px; width: 40px; height: 40px;}
+//.sprite-resource-icons.roles.large { background-position: -70px -2069px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.networkrules { background-position: -282px -4731px; width: 49px; height: 58px;}
+.sprite-resource-icons.networkrules.small { background-position: -210px -4749px; width: 31px; height: 36px;}
+.sprite-resource-icons.networkrules.medium { background-position: -141px -4737px; width: 42px; height: 47px;}
+.sprite-resource-icons.networkrules.large { background-position: -70px -4718px; width: 60px; height: 67px;}
+
+.sprite-resource-icons.securityrules { background-position: -282px -4651px; width: 49px; height: 58px;}
+.sprite-resource-icons.securityrules.small { background-position: -210px -4669px; width: 31px; height: 36px;}
+.sprite-resource-icons.securityrules.medium { background-position: -141px -4657px; width: 42px; height: 47px;}
+.sprite-resource-icons.securityrules.large { background-position: -70px -4638px; width: 60px; height: 67px;}
+
+.sprite-resource-icons.dcae_source { background-position: -282px -4563px; width: 60px; height: 60px;}
+.sprite-resource-icons.dcae_source.small { background-position: -210px -4588px; width: 29px; height: 29px;}
+.sprite-resource-icons.dcae_source.medium { background-position: -141px -4576px; width: 41px; height: 41px;}
+.sprite-resource-icons.dcae_source.large { background-position: -70px -4558px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.dcae_collector { background-position: -280px -4500px; width: 60px; height: 60px;}
+.sprite-resource-icons.dcae_collector.small { background-position: -210px -4508px; width: 29px; height: 29px;}
+.sprite-resource-icons.dcae_collector.medium { background-position: -141px -4496px; width: 41px; height: 41px;}
+.sprite-resource-icons.dcae_collector.large { background-position: -69px -4477px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.dcae_utilty { background-position: -283px -4246px; width: 60px; height: 60px;}
+.sprite-resource-icons.dcae_utilty.small { background-position: -210px -4268px; width: 29px; height: 29px;}
+.sprite-resource-icons.dcae_utilty.medium { background-position: -141px -4256px; width: 41px; height: 41px;}
+.sprite-resource-icons.dcae_utilty.large { background-position: -70px -4238px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.dcae_microservice { background-position: -281px -4167px; width: 60px; height: 60px;}
+.sprite-resource-icons.dcae_microservice.small { background-position: -210px -4188px; width: 28px; height: 28px;}
+.sprite-resource-icons.dcae_microservice.medium { background-position: -140px -4176px; width: 41px; height: 41px;}
+.sprite-resource-icons.dcae_microservice.large { background-position: -71px -4157px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.dcae_analytics { background-position: -282px -4409px; width: 60px; height: 60px;}
+.sprite-resource-icons.dcae_analytics.small { background-position: -210px -4424px; width: 29px; height: 29px;}
+.sprite-resource-icons.dcae_analytics.medium { background-position: -141px -4413px; width: 41px; height: 41px;}
+.sprite-resource-icons.dcae_analytics.large { background-position: -70px -4394px; width: 60px; height: 60px;}
+
+.sprite-resource-icons.dcae_database { background-position: -282px -180px; width: 63px; height: 56px;}
+.sprite-resource-icons.dcae_database.small { background-position: -210px -211px; width: 29px; height: 29px;}
+.sprite-resource-icons.dcae_database.medium { background-position: -140px -199px; width: 40px; height: 40px;}
+.sprite-resource-icons.dcae_database.large { background-position: -70px -179px; width: 60px; height: 60px;}
+
+
+.sprite-resource-icons.dcae_policy { background-position: -282px -4332px; width: 60px; height: 60px;}
+.sprite-resource-icons.dcae_policy.small { background-position: -210px -4348px; width: 29px; height: 29px;}
+.sprite-resource-icons.dcae_policy.medium { background-position: -141px -4336px; width: 41px; height: 41px;}
+.sprite-resource-icons.dcae_policy.large { background-position: -70px -4317px; width: 60px; height: 60px;}
+
diff --git a/catalog-ui/app/styles/sprite-services-icons.less b/catalog-ui/app/styles/sprite-services-icons.less
new file mode 100644
index 0000000000..d1e666ff12
--- /dev/null
+++ b/catalog-ui/app/styles/sprite-services-icons.less
@@ -0,0 +1,66 @@
+.sprite-services-icons {
+ background-image: url('images/sprites/sprite-services-icons.png');
+ display: inline-block;
+}
+
+.sprite-services-icons.disable { opacity:0.5;}
+
+.sprite-services-icons.compute { background-position: -282px -46px; width: 55px; height: 53px;}
+.sprite-services-icons.compute.small { background-position: -210px -71px; width: 29px; height: 28px;}
+.sprite-services-icons.compute.medium { background-position: -140px -59px; width: 41px; height: 40px;}
+.sprite-services-icons.compute.large { background-position: -70px -41px; width: 61px; height: 60px;}
+
+.sprite-services-icons.platform { background-position: -282px -122px; width: 55px; height: 45px;}
+.sprite-services-icons.platform.small { background-position: -210px -140px; width: 29px; height: 29px;}
+.sprite-services-icons.platform.medium { background-position: -140px -128px; width: 41px; height: 41px;}
+.sprite-services-icons.platform.large { background-position: -70px -109px; width: 61px; height: 60px;}
+
+.sprite-services-icons.storage { background-position: -282px -194px; width: 55px; height: 44px;}
+.sprite-services-icons.storage.small { background-position: -210px -210px; width: 29px; height: 29px;}
+.sprite-services-icons.storage.medium { background-position: -140px -198px; width: 41px; height: 41px;}
+.sprite-services-icons.storage.large { background-position: -70px -178px; width: 61px; height: 61px;}
+
+.sprite-services-icons.call_controll { background-position: -282px -268px; width: 36px; height: 41px;}
+.sprite-services-icons.call_controll.small { background-position: -210px -280px; width: 29px; height: 29px;}
+.sprite-services-icons.call_controll.medium { background-position: -140px -268px; width: 41px; height: 41px;}
+.sprite-services-icons.call_controll.large { background-position: -70px -248px; width: 61px; height: 61px;}
+
+.sprite-services-icons.collaboration { background-position: -282px -335px; width: 43px; height: 44px;}
+.sprite-services-icons.collaboration.small { background-position: -210px -350px; width: 29px; height: 29px;}
+.sprite-services-icons.collaboration.medium { background-position: -140px -338px; width: 41px; height: 41px;}
+.sprite-services-icons.collaboration.large { background-position: -70px -318px; width: 61px; height: 61px;}
+
+.sprite-services-icons.collaboration1 { background-position: -280px -417px; width: 49px; height: 32px;}
+.sprite-services-icons.collaboration1.small { background-position: -209px -419px; width: 29px; height: 29px;}
+.sprite-services-icons.collaboration1.medium { background-position: -140px -408px; width: 41px; height: 41px;}
+.sprite-services-icons.collaboration1.large { background-position: -70px -388px; width: 61px; height: 61px;}
+
+.sprite-services-icons.messaging { background-position: -282px -482px; width: 43px; height: 40px;}
+.sprite-services-icons.messaging.small { background-position: -210px -491px; width: 28px; height: 28px;}
+.sprite-services-icons.messaging.medium { background-position: -140px -478px; width: 41px; height: 41px;}
+.sprite-services-icons.messaging.large { background-position: -70px -458px; width: 61px; height: 61px;}
+
+.sprite-services-icons.notification { background-position: -282px -538px; width: 43px; height: 52px;}
+.sprite-services-icons.notification.small { background-position: -210px -561px; width: 28px; height: 28px;}
+.sprite-services-icons.notification.medium { background-position: -140px -548px; width: 41px; height: 41px;}
+.sprite-services-icons.notification.large { background-position: -70px -528px; width: 60px; height: 60px;}
+
+.sprite-services-icons.mobility { background-position: -282px -598px; width: 64px; height: 62px;}
+.sprite-services-icons.mobility.small { background-position: -210px -630px; width: 29px; height: 29px;}
+.sprite-services-icons.mobility.medium { background-position: -140px -618px; width: 41px; height: 41px;}
+.sprite-services-icons.mobility.large { background-position: -70px -601px; width: 60px; height: 60px;}
+
+.sprite-services-icons.network_l_1-3 { background-position: -282px -672px; width: 63px; height: 57px;}
+.sprite-services-icons.network_l_1-3.small { background-position: -210px -701px; width: 28px; height: 28px;}
+.sprite-services-icons.network_l_1-3.medium { background-position: -139px -688px; width: 41px; height: 41px;}
+.sprite-services-icons.network_l_1-3.large { background-position: -70px -671px; width: 60px; height: 60px;}
+
+.sprite-services-icons.network_l_4 { background-position: -286px -744px; width: 63px; height: 57px;}
+.sprite-services-icons.network_l_4.small { background-position: -210px -771px; width: 28px; height: 28px;}
+.sprite-services-icons.network_l_4.medium { background-position: -140px -758px; width: 41px; height: 41px;}
+.sprite-services-icons.network_l_4.large { background-position: -70px -740px; width: 61px; height: 61px;}
+
+.sprite-services-icons.defaulticon { background-position: -270px -810px; width: 60px; height: 60px;}
+.sprite-services-icons.defaulticon.small { background-position: -220px -842px; width: 28px; height: 28px;}
+.sprite-services-icons.defaulticon.medium { background-position: -169px -830px; width: 40px; height: 40px;}
+.sprite-services-icons.defaulticon.large { background-position: -70px -809px; width: 60px; height: 60px;}
diff --git a/catalog-ui/app/styles/sprite.html b/catalog-ui/app/styles/sprite.html
new file mode 100644
index 0000000000..875c26fe2a
--- /dev/null
+++ b/catalog-ui/app/styles/sprite.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+
+ <head>
+ <link rel="stylesheet" href="app.css">
+ </head>
+
+ <body style="padding: 20px;">
+
+ <h1>Sprite icons</h1>
+
+ <table>
+
+ <tr>
+ <th><span class="sprite-new checkbox_checked"></span></th>
+ <td>checkbox_checked</td>
+
+ <th><span class="sprite-new checkbox_unchecked"></span></th>
+ <td>checkbox_unchecked</td>
+
+ <th><span class="sprite-new checkbox_disabled"></span></th>
+ <td>checkbox_disabled</td>
+
+ <th><span class="sprite-new checkbox_focus"></span></th>
+ <td>checkbox_focus</td>
+ </tr>
+
+ <tr>
+ <th><span class="sprite-new add-icon"></span></th>
+ <td>add-icon</td>
+
+ <th><span class="sprite-new import-icon"></span></th>
+ <td>import-icon</td>
+ </tr>
+
+ <tr>
+ <th><span class="sprite-new video-icon"></span></th>
+ <td>video-icon</td>
+
+ <th><span class="sprite-new video-icon_1"></span></th>
+ <td>video-icon_1</td>
+
+ <th><span class="sprite-new video-icon_2"></span></th>
+ <td>video-icon_2</td>
+
+ <th><span class="sprite-new video-icon_disabled"></span></th>
+ <td>video-icon_disabled</td>
+
+ <th><span class="sprite-new video-icon_focus"></span></th>
+ <td>video-icon_focus</td>
+ </tr>
+
+ <tr>
+ <th><span class="sprite-new info-icon"></span></th>
+ <td>info-icon</td>
+
+ <th><span class="sprite-new info-icon_1"></span></th>
+ <td>info-icon_1</td>
+ </tr>
+
+ </table>
+
+ </body>
+
+</html>
diff --git a/catalog-ui/app/styles/sprite.less b/catalog-ui/app/styles/sprite.less
new file mode 100644
index 0000000000..b7c0146059
--- /dev/null
+++ b/catalog-ui/app/styles/sprite.less
@@ -0,0 +1,212 @@
+.sprite-new {
+ background-image: url('images/sprites/sprite-global.png');
+ display: inline-block;
+}
+
+.add-icon { background-position: -50px -77px; width: 23px; height: 23px;}
+.add-icon-hover { background-position: -150px -77px; width: 23px; height: 23px;}
+.import-icon { background-position: -100px -81px; width: 23px; height: 23px;}
+
+.add-icon-blue { background-position: -350px -125px; width: 18px; height: 18px;}
+.add-icon-blue-hover { background-position: -400px -125px; width: 18px; height: 18px;}
+
+.video-icon { background-position: -54px -113px; width: 33px; height: 26px;}
+.video-icon_1 { background-position: -104px -113px; width: 33px; height: 26px;}
+.video-icon_2 { background-position: -154px -113px; width: 33px; height: 26px;}
+.video-icon_disabled { background-position: -204px -113px; width: 33px; height: 26px;}
+.video-icon_focus { background-position: -250px -111px; width: 41px; height: 32px;}
+
+.vsp-list-icon { background-position: -54px -143px; width: 26px; height: 29px;}
+.vsp-list-icon-active { background-position: -104px -143px; width: 26px; height: 29px;}
+.vsp-list-icon:active:not(.disable) { background-position: -104px -143px; width: 26px; height: 29px;}
+.vsp-list-icon-hover { background-position: -154px -143px; width: 26px; height: 29px;}
+.vsp-list-icon:hover:not(.disable) { background-position: -154px -143px; width: 26px; height: 29px;}
+.vsp-list-icon.disable { background-position: -204px -143px; width: 26px; height: 29px;}
+
+.info-icon { background-position: -50px -179px; width: 14px; height: 14px;}
+.info-icon_1 { background-position: -100px -179px; width: 14px; height: 14px;}
+
+.plus-icon { background-position: -50px -231px; width: 12px; height: 12px;}
+.plus-icon-hover { background-position: -100px -231px; width: 12px; height: 12px;}
+
+.delete-icon { background-position: -650px -231px; width: 11px; height: 13px;}
+.delete-icon-hover { background-position: -702px -231px; width: 11px; height: 13px;}
+
+.arrow-up { background-position: -350px -236px; width: 12px; height: 7px;}
+.arrow-up-hover { background-position: -400px -236px; width: 12px; height: 7px;}
+
+.arrow-up-small { background-position: -250px -237px; width: 12px; height: 6px;}
+.arrow-up-small-hover { background-position: -300px -237px; width: 12px; height: 6px;}
+
+.sort-arrows { background-position: -450px -231px; width: 8px; height: 12px;}
+.sort-arrows-hover { background-position: -500px -231px; width: 8px; height: 12px;}
+
+.arrow-right { background-position: -550px -235px; width: 5px; height: 8px;}
+.arrow-right-hover { background-position: -600px -235px; width: 5px; height: 8px;}
+
+.menu-open-left { background-position: -350px -283px; width: 6px; height: 10px;}
+.menu-open-right { background-position: -400px -283px; width: 6px; height: 10px;}
+
+
+.private-status-icon { background-position: -50px -326px; width: 21px; height: 17px;}
+.in-design-status-icon { background-position: -307px -327px; width: 16px; height: 16px;}
+.checkout-editable-status-icon { background-position: -100px -326px; width: 24px; height: 17px;}
+.checkin-status-icon { background-position: -200px -326px; width: 24px; height: 17px;}
+.checkout-not-editable-status-icon { background-position: -150px -326px; width: 24px; height: 17px;}
+
+
+//workspace buttons
+.x-btn { background-position: -50px -419px; width: 24px; height: 24px; }
+.x-btn:hover { background-position: -50px -447px; width: 24px; height: 24px; }
+.x-btn:active { background-position: -50px -479px; width: 24px; height: 24px; }
+
+.delete-btn { background-position: -140px -419px; width: 24px; height: 24px; }
+.delete-btn:hover:not(.disable) { background-position: -140px -447px; width: 24px; height: 24px; }
+.delete-btn:active:not(.disable) { background-position: -140px -479px; width: 24px; height: 24px; }
+.delete-btn.disable { background-position: -140px -539px; width: 24px; height: 24px; }
+
+.save-btn { background-position: -230px -419px; width: 24px; height: 24px; }
+.save-btn:hover:not(.disable) { background-position: -230px -447px; width: 24px; height: 24px; }
+.save-btn:active:not(.disable) { background-position: -230px -479px; width: 24px; height: 24px; }
+.save-btn.disable { background-position: -230px -539px; width: 24px; height: 24px; }
+
+.revert-btn { background-position: -200px -419px; width: 24px; height: 24px; }
+.revert-btn:hover:not(.disable) { background-position: -200px -447px; width: 24px; height: 24px; }
+.revert-btn:active:not(.disable) { background-position: -200px -479px; width: 24px; height: 24px; }
+.revert-btn.disable { background-position: -200px -539px; width: 24px; height: 24px; }
+
+.success-circle { background-position: -50px -703px; width: 20px; height: 20px; }
+.success-circle-small { background-position: -170px -706px; width: 12px; height: 12px; }
+.sdc-success { background-position: -101px -707px; width: 10px; height: 10px; }
+.sdc-error { background-position: -151px -707px; width: 10px; height: 10px; }
+
+.print-screen-btn { background-position: -259px -419px; width: 24px; height: 24px; }
+.print-screen-btn:hover:not(.disable) { background-position: -259px -447px; width: 24px; height: 24px; }
+.print-screen-btn:active:not(.disable) { background-position: -259px -479px; width: 24px; height: 24px; }
+.print-screen-btn.disable { background-position: -259px -539px; width: 24px; height: 24px; }
+
+.url-btn { background-position: -636px -424px; width: 19px; height: 19px; }
+.url-btn:hover { background-position: -636px -424px; width: 19px; height: 19px; }
+.url-btn:active { background-position: -636px -482px; width: 19px; height: 19px; }
+
+
+
+
+/*new tabs icons*/
+.sprite-new.info { background-position: -51px -631px; width: 18px; height: 18px;}
+.sprite-new.info:active { background-position: -51px -669px; width: 18px; height: 18px;}
+.active > .sprite-new.info { background-position: -51px -669px; width: 18px; height: 18px;}
+
+
+.sprite-new.structure { background-position: -101px -630px; width: 18px; height: 17px;}
+.sprite-new.structure:active { background-position: -101px -668px; width: 18px; height: 17px;}
+.active > .sprite-new.structure { background-position: -101px -668px; width: 18px; height: 17px;}
+
+
+.sprite-new.deployment-artifacts { background-position: -150px -629px; width: 17px; height: 19px;}
+.sprite-new.deployment-artifacts:active { background-position: -150px -667px; width: 17px; height: 19px;}
+.active > .deployment-artifacts { background-position: -150px -667px; width: 17px; height: 19px;}
+
+.sprite-new.inputs { background-position: -200px -634px; width: 20px; height: 12px;}
+.sprite-new.inputs:active { background-position: -200px -672px; width: 20px; height: 12px;}
+.active > .sprite-new.inputs { background-position: -200px -672px; width: 20px; height: 12px;}
+
+
+.sprite-new.information-artifacts { background-position: -250px -631px; width: 17px; height: 19px;}
+.sprite-new.information-artifacts:active { background-position: -250px -669px; width: 17px; height: 19px;}
+.active > .sprite-new.information-artifacts { background-position: -250px -669px; width: 17px; height: 19px;}
+
+.sprite-new.relations { background-position: -300px -632px; width: 20px; height: 20px;}
+.sprite-new.relations:active { background-position: -300px -632px; width: 20px; height: 20px;}
+.active > .sprite-new.relations { background-position: -300px -632px; width: 20px; height: 20px;}
+
+
+.sprite-new.api { background-position: -400px -631px; width: 23px; height: 19px;}
+.sprite-new.api:active { background-position: -400px -671px; width: 23px; height: 19px;}
+.active > .sprite-new.api { background-position: -400px -671px; width: 23px; height: 19px;}
+
+.sprite-new.properties { background-position: -350px -631px; width: 19px; height: 19px;}
+.sprite-new.properties:active { background-position: -350px -671px; width: 19px; height: 19px;}
+.active > .sprite-new.properties { background-position: -350px -671px; width: 19px; height: 19px;}
+
+.sprite-new.distribution-bth { background-position: -399px -716px; width: 55px; height: 21px;}
+.sprite-new.distribution-bth.disable { background-position: -464px -716px; width: 55px; height: 21px;}
+
+.Distributed { background-position: -547px -718px; width: 16px; height: 16px;}
+.Deployed { background-position: -573px -718px; width: 16px; height: 16px;}
+
+.error-icon { background-position: -51px -751px; width: 13px; height: 11px;}
+.asdc-warning { background-position: -100px -748px; width: 17px; height: 15px;}
+
+.link-btn { background-position: -636px -424px; width: 19px; height: 19px;}
+.link-btn:hover { background-position: -636px -453px; width: 19px; height: 19px;}
+.link-btn:active { background-position: -636px -482px; width: 19px; height: 19px;}
+
+.refresh-btn { background-position: -293px -419px; width: 24px; height: 24px;}
+.refresh-btn:hover:not(.disable) { background-position: -293px -447px; width: 24px; height: 24px;}
+.refresh-btn:active:not(.disable) { background-position: -293px -479px; width: 24px; height: 24px;}
+.refresh-btn.focus { background-position: -293px -508px; width: 24px; height: 24px;}
+.refresh-btn.disable { background-position: -293px -539px; width: 24px; height: 24px;}
+
+.download-btn { background-position: -530px -419px; width: 24px; height: 24px;}
+.download-btn:hover:not(.disable) { background-position: -530px -448px; width: 24px; height: 24px;}
+.download-btn:active:not(.disable) { background-position: -530px -479px; width: 24px; height: 24px;}
+.download-btn.focus { background-position: -530px -508px; width: 24px; height: 24px;}
+.download-btn.disable { background-position: -530px -539px; width: 24px; height: 24px;}
+
+.expand-collapse-plus-icon { background-position: -334px -888px; width: 14px; height: 14px;}
+.expand-collapse-plus-icon.hover { background-position: -422px -888px; width: 14px; height: 14px;}
+.expand-collapse-minus-icon { background-position: -378px -888px; width: 14px; height: 14px;}
+.expand-collapse-minus-icon.hover { background-position: -466px -888px; width: 14px; height: 14px;}
+
+
+//tabs
+.close-open-left-arrow { background-position: -507px -942px; width: 6px; height: 10px;}
+.close-open-right-arrow { background-position: -506px -922px; width: 6px; height: 10px;}
+
+.hierarchy { background-position: -560px -887px; width: 21px; height: 15px;}
+.hierarchy.hover { background-position: -500px -887px; width: 21px; height: 15px;}
+.hierarchy.disable { background-position: -531px -887px; width: 21px; height: 15px;}
+.hierarchy.selected { background-position: -500px -887px; width: 21px; height: 15px;}
+
+.refresh-small-btn { background-position: -598px -419px; width: 24px; height: 24px;}
+.refresh-small-btn:hover:not(.disable) { background-position: -598px -448px; width: 24px; height: 24px;}
+.refresh-small-btn:active:not(.disable) { background-position: -598px -479px; width: 24px; height: 24px;}
+.refresh-small-btn.focus { background-position: -598px -508px; width: 24px; height: 24px;}
+.refresh-small-btn.disable { background-position: -598px -539px; width: 24px; height: 24px;}
+
+.download-file-btn { background-position: -673px -419px; width: 24px; height: 24px;}
+.download-file-btn:hover:not(.disable) { background-position: -673px -448px; width: 24px; height: 24px;}
+.download-file-btn:active:not(.disable) { background-position: -673px -479px; width: 24px; height: 24px;}
+.download-file-btn.focus { background-position: -673px -508px; width: 24px; height: 24px;}
+.download-file-btn.disable { background-position: -673px -539px; width: 24px; height: 24px;}
+
+.refresh-file-btn { background-position: -707px -419px; width: 24px; height: 24px;}
+.refresh-file-btn:hover:not(.disable) { background-position: -707px -448px; width: 24px; height: 24px;}
+.refresh-file-btn:active:not(.disable) { background-position: -707px -479px; width: 24px; height: 24px;}
+.refresh-file-btn.focus { background-position: -707px -508px; width: 24px; height: 24px;}
+.refresh-file-btn.disable { background-position: -707px -539px; width: 24px; height: 24px;}
+
+.import-file-btn { background-position: -745px -419px; width: 24px; height: 24px;}
+.import-file-btn:hover:not(.disable) { background-position: -745px -448px; width: 24px; height: 24px;}
+.import-file-btn:active:not(.disable) { background-position: -745px -479px; width: 24px; height: 24px;}
+.import-file-btn.focus { background-position: -745px -508px; width: 24px; height: 24px;}
+.import-file-btn.disable { background-position: -745px -539px; width: 24px; height: 24px;}
+
+.left-arrow { background-position: -590px -876px; width: 26px; height: 26px;}
+.left-arrow:hover { background-position: -662px -876px; width: 26px; height: 26px;}
+.right-arrow { background-position: -626px -876px; width: 26px; height: 26px;}
+.right-arrow:hover { background-position: -698px -876px; width: 26px; height: 26px;}
+.search-white-icon { background-position: -434px -128px; width: 14px; height: 14px;}
+
+.blue-right-arrow-circle { background-position: -650px -619px; width: 29px; height: 29px;}
+.blue-right-arrow-circle:hover { background-position: -689px -619px; width: 29px; height: 29px;}
+
+.small-x-button { background-position: -190px -282px; width: 11px; height: 11px;}
+.small-x-button:hover { background-position: -210px -283px; width: 11px; height: 11px;}
+.close-info-tooltip-button {.small-x-button}
+.close-info-tooltip-button:hover { background-position: -230px -283px; width: 11px; height: 11px;}
+.expand-all { background-position: -500px -420px; width: 20px; height: 22px;}
+.expand-all:hover { background-position: -500px -449px; width: 20px; height: 22px;}
+.collapse-all { background-position: -465px -420px; width: 20px; height: 22px;}
+.collapse-all:hover { background-position: -465px -449px; width: 20px; height: 22px;}
diff --git a/catalog-ui/app/styles/table-flex.less b/catalog-ui/app/styles/table-flex.less
new file mode 100644
index 0000000000..3ac014cc1b
--- /dev/null
+++ b/catalog-ui/app/styles/table-flex.less
@@ -0,0 +1,178 @@
+.table-container-flex {
+ /* height: 650px; */
+ margin-top: 35px;
+ clear: both;
+ .table {
+ width: 100%;
+ border: 1px solid #d8d8d8;
+ .head {
+ background-color: #eaeaea;
+ .head-row {
+ color: #333333;
+ font-weight: bold;
+ text-align: center;
+ border-right: 1px solid @border_color_d;
+ padding: 8px 15px;
+
+ &:last-child {
+ border-right: none;
+ }
+
+ .table-header-sort-arrow {
+ display: inline-block;
+ background-color: transparent;
+ border: none;
+ color: #AAA;
+ margin: 8px 0 0 5px;
+ &.up {
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid;
+ }
+ &.down {
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid;
+ }
+ }
+ }
+ }
+ .body {
+ .scrollbar-container {
+ max-height: 430px;
+ .perfect-scrollbar;
+ }
+ .b_9;
+
+ .data-row {
+ border-bottom: 1px solid @border_color_d;
+ div {
+
+ border-right: 1px solid @border_color_d;
+
+
+ &:last-child {
+ border-right: none;
+ }
+
+ .table-role-select {
+ background-color: transparent;
+ border: 0;
+ width: 100%;
+
+ }
+ .table-role-label {
+ margin-left:4px;
+ }
+
+ }
+
+ &.selected {
+ background-color: #e6f6fb;
+ }
+
+ & + div.item-opened {
+ border-bottom: 1px solid @border_color_d;
+ padding: 10px 23px;
+ text-align: left;
+ word-break: break-all;
+ }
+ }
+ .data-row:hover {
+ .bg_j;
+ }
+
+
+ button.add-button {
+ margin: 10px auto;
+ display: block;
+ border: 1px solid #eaeaea;
+ background-color: @func_color_r;
+ box-shadow: 0px 1px 1px 0px rgba(24, 24, 25, 0.05);
+ width: 297px;
+ height: 39px;
+ color: #666666;
+ font-size: 14px;
+ font-weight: bold;
+
+ &:before {
+ .sprite-new;
+ .add-icon-blue;
+ float: left;
+ margin-left: 5px;
+ content: "";
+ }
+ }
+ }
+
+ .no-row-text{
+ text-align: center;
+ margin-top:25px;
+
+ a {
+ cursor: pointer;
+ }
+ }
+
+ .table-btn-col {
+
+ line-height: 0px;
+ text-align: center;
+ .table-delete-btn {
+ background-color: transparent;
+ border: none;
+ .sprite;
+ .sprite.e-sdc-small-icon-delete;
+ opacity: 0.8;
+
+ }
+ .table-edit-btn {
+ background-color: transparent;
+ border: none;
+ .sprite;
+ .e-sdc-small-icon-pencil;
+ opacity: 0.8;
+ margin-right: 10px;
+ }
+ .table-save-btn {
+ background-color: transparent;
+ border: none;
+ .sprite;
+ .sprite.e-sdc-green-save;
+ }
+ .table-download-btn {
+ background-color: transparent;
+ border: none;
+ .hand;
+ margin-left: 10px;
+ opacity: 0.8;
+ .sprite;
+ .sprite.e-sdc-small-download;
+ }
+ }
+
+ }
+
+ .no-row-text {
+ text-align: center;
+ font-size: 15px;
+ border-bottom: 1px solid #d8d8d8;
+ width: 300px;
+ margin: 26px auto;
+ padding-bottom: 22px;
+ }
+
+ .flex-container {
+ display: flex;
+ }
+
+ .flex-item {
+ width:10px;
+ line-height: 25px;
+ padding: 5px 15px;
+ flex-grow: 1;
+ text-align: left;
+
+ }
+
+}
diff --git a/catalog-ui/app/styles/tlv-buttons.less b/catalog-ui/app/styles/tlv-buttons.less
new file mode 100644
index 0000000000..0e42a92231
--- /dev/null
+++ b/catalog-ui/app/styles/tlv-buttons.less
@@ -0,0 +1,234 @@
+.tlv-btn {
+ border-radius: 2px;
+ cursor: pointer;
+ display: inline-block;
+ font-family: omnes-medium, Sans-Serif;
+ height: 32px;
+ min-width: 96px;
+ line-height: 30px;
+ padding: 0 16px;
+ text-align: center;
+ vertical-align: middle;
+ font-size: 14px;
+
+ &:disabled {
+ opacity: .4;
+ pointer-events: none;
+ }
+
+ &.blue {
+ background-color: #009fdb;
+ border: 1px solid #0091c8;
+ color: #fff;
+
+ &:hover {
+ background-color: #1ec2ff;
+ }
+
+ &:active {
+ background-color: #0091c7;
+ border: 1px solid #006186;
+ outline: none;
+ }
+
+ &:focus {
+ border-color: #009fdb;
+ box-shadow: inset 0 0 0 1px #fff;
+ outline: none;
+ }
+ }
+
+ &.white {
+ background-color: #fff;
+ border: 1px solid #d8d8d8;
+ color: @main_color_n;
+
+ &:hover {
+ border-color: #959595;
+ }
+
+ &:active {
+ background-color: #f2f2f2;
+ border: 1px solid #595959;
+ outline: none;
+ }
+
+ &:focus {
+ border-color: #959595;
+ box-shadow: inset 0 0 0 1px #fff, inset 0 0 0 2px #959595;
+ outline: none;
+
+ }
+
+ }
+
+ &.grey {
+ background-color: #f8f8f8;
+ border: 1px solid #d8d8d8;
+ color: #000;
+
+ &:hover {
+ border-color: #959595;
+ }
+
+ &:active {
+ background-color: #d7d7d7;
+ border: 1px solid #595959;
+ outline: none;
+ }
+
+ &:focus {
+ background-color: @tlv_color_t;
+ border-color: @main_color_n;
+ box-shadow: inset 0 0 0 1px @main_color_p, inset 0 0 0 2px @main_color_n;
+ outline: none;
+
+ }
+
+ }
+
+
+ &.green {
+ background-color: #4ca90c;
+ border: 1px solid #45a006;
+ color: #fff;
+
+ &:hover {
+ background-color: #5ec616;
+ }
+
+ &:active {
+ background-color: #3f8c0a;
+ border: 1px solid #2c6604;
+ outline: none;
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px #fff;
+ outline: none;
+ }
+ }
+
+ &.red {
+ background-color: #fc2727;
+ border: 1px solid #a01a1a;
+ color: #fff;
+
+ &:hover {
+ background-color: #fc2727;
+ }
+
+ &:active {
+ background-color: #a01a1a;
+ border: 1px solid #730202;
+ outline: none;
+ }
+
+ &:focus {
+ box-shadow: inset 0 0 0 1px @main_color_p;
+ outline: none;
+ }
+ }
+
+
+ &.outline {
+ &.blue {
+ background-color: #fff;
+ border: 1px solid #009fdb;
+ color: #009fdb;
+
+ &:hover {
+ background-color: #e5f5fb;
+ }
+
+ &:active {
+ background-color: #b2e2f4;
+ border: 1px solid #009fdb;
+ outline: none;
+ }
+
+ &:focus {
+ background-color: #fff;
+ border-color: #009fdb;
+ box-shadow: inset 0 0 0 1px #fff, inset 0 0 0 2px #009fdb;
+ outline: none;
+ }
+ }
+
+ &.grey {
+ background-color: #ffffff;
+ border: 1px solid #959595;
+ color: #5a5a5a;
+
+ &:hover {
+ background-color: #f4f4f4;
+ border-color: #959595;
+ }
+
+ &:active {
+ background-color: #dfdfdf;
+ border: 1px solid #959595;
+ outline: none;
+ }
+
+ &:focus {
+ background-color: #fff;
+ border-color: #959595;
+ box-shadow: inset 0 0 0 1px #fff, inset 0 0 0 2px #959595;
+ outline: none;
+
+ }
+
+ }
+
+
+ &.green {
+ background-color: #ffffff;
+ border: 1px solid #4ca90c;
+ color: #4ca90c;
+
+ &:hover {
+ background-color: #edf6e6;
+ border-color: #4ca90c;
+ }
+
+ &:active {
+ background-color: #c9e5b6;
+ border: 1px solid #4ca90c;
+ outline: none;
+ }
+
+ &:focus {
+ background-color: #fff;
+ border-color: #4ca90c;
+ box-shadow: inset 0 0 0 1px #fff, inset 0 0 0 2px #4ca90c;
+ outline: none;
+ }
+ }
+
+ &.red {
+ background-color: #ffffff;
+ border: 1px solid #cf2a2a;
+ color: #cf2a2a;
+
+ &:hover {
+ background-color: #fae9e9;
+ border-color: #cf2a2a;
+ }
+
+ &:active {
+ background-color: #c9e5b6;
+ border: 1px solid #cf2a2a;
+ outline: none;
+ }
+
+ &:focus {
+ background-color: #fff;
+ border-color: #cf2a2a;
+ box-shadow: inset 0 0 0 1px #fff, inset 0 0 0 2px #cf2a2a;
+ outline: none;
+ }
+ }
+ }
+}
+
diff --git a/catalog-ui/app/styles/tlv-checkbox.less b/catalog-ui/app/styles/tlv-checkbox.less
new file mode 100644
index 0000000000..9dd5873545
--- /dev/null
+++ b/catalog-ui/app/styles/tlv-checkbox.less
@@ -0,0 +1,87 @@
+.tlv-checkbox {
+ font-family: clearview-book, Sans-Serif;
+ font-size: 14px;
+ display: inline-block;
+
+ .tlv-checkbox-i {
+ height: 0;
+ /*visibility: hidden;*/
+ display: none;
+ width: 0;
+
+ &:checked ~ .tlv-checkbox-label,
+ &:checked:hover ~ .tlv-checkbox-label{
+ &::before {
+ .tlv-sprite;
+ .checkbox-checked;
+ }
+ }
+
+ &:disabled ~ .tlv-checkbox-label{
+ pointer-events: none;
+ }
+
+ &:hover ~ .tlv-checkbox-label{
+ &::before {
+ .tlv-sprite;
+ .checkbox-hover;
+ }
+ }
+ }
+
+ .tlv-checkbox-label {
+ /*display: inline-block;*/
+ display: block;
+ vertical-align: middle;
+
+ &::before {
+ .tlv-sprite;
+ .checkbox;
+ content: '';
+ display: inline-block;
+ margin-right: 4px;
+ margin-top: -2px;
+ vertical-align: middle;
+ }
+ }
+}
+
+
+.tlv-radio {
+ font-size: 14px;
+ display: inline-block;
+
+ .tlv-radio-i {
+ height: 0;
+ visibility: hidden;
+ width: 0;
+
+ &:checked ~ .tlv-radio-label{
+ &::before {
+ .tlv-sprite;
+ .radio-checked;
+
+ }
+ }
+
+ &:disabled ~ .tlv-radio-label{
+ opacity: .6;
+ }
+ }
+
+ .tlv-radio-label {
+ display: inline-block;
+ vertical-align: middle;
+ margin-bottom: 0;
+
+ &::before {
+ .tlv-sprite;
+ .radio;
+ content: '';
+ display: inline-block;
+ margin-right: 4px;
+ margin-top: -2px;
+ vertical-align: middle;
+ }
+ }
+}
diff --git a/catalog-ui/app/styles/tlv-loader.less b/catalog-ui/app/styles/tlv-loader.less
new file mode 100644
index 0000000000..efe39916e2
--- /dev/null
+++ b/catalog-ui/app/styles/tlv-loader.less
@@ -0,0 +1,164 @@
+.tlv-loader {
+ height: 63px;
+ width: 63px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+}
+
+.tlv-loader.small {
+ transform: scale(0.26);
+ margin-top: -36.5px;
+ margin-left: -36.5px;
+}
+
+.tlv-loader.medium {
+ transform: scale(0.5);
+ margin-top: -26.5px;
+ margin-left: -26.5px;
+}
+
+.tlv-loader.large {
+ transform: scale(1);
+ margin-top: -10.5px;
+ margin-left: -10.5px;
+}
+
+.tlv-loader::before {
+ background-color: #eaeaea;
+ border-radius: 50%;
+ box-shadow: 21px 21px 0px 0px #eaeaea, 0px 42px 0px 0px #eaeaea, -21px 21px 0px 0px #eaeaea;
+ content: '';
+ display: block;
+ height: 21px;
+ width: 21px;
+ position: absolute;
+ left: 50%;
+ margin-left: -10.5px;
+}
+
+.tlv-loader::after {
+ border-radius: 50%;
+ content: '';
+ display: block;
+ position: absolute;
+ height: 21px;
+ width: 21px;
+ animation: dot-move-2 4.5s infinite ease-in;
+}
+
+@keyframes dot-move {
+ 0% {
+ background-color: #3bb2df;
+ left: 21px;
+ top: 0;
+ }
+ 25% {
+ background-color: #ffb81c;
+ left: 42px;
+ top: 21px;
+ }
+ 50% {
+ background-color: #caa2dd;
+ left: 21px;
+ top: 42px;
+ }
+ 75% {
+ background-color: #d9e51c;
+ left: 0;
+ top: 21px;
+ }
+ 100% {
+ background-color: #3bb2df;
+ left: 21px;
+ top: 0;
+ }
+}
+
+@keyframes dot-move-2 {
+ 0% {
+ background-color: #3bb2df;
+ left: 21px;
+ top: 0;
+ }
+ 6.25% {
+ background-color: #3bb2df;
+ left: 42px;
+ top: 21px;
+ }
+ 12.5% {
+ background-color: #3bb2df;
+ left: 21px;
+ top: 42px;
+ }
+ 18.75% {
+ background-color: #3bb2df;
+ left: 0;
+ top: 21px;
+ }
+ 25% {
+ background-color: #ffb81c;
+ left: 21px;
+ top: 0;
+ }
+ 31.25% {
+ background-color: #ffb81c;
+ left: 42px;
+ top: 21px;
+ }
+ 37.5% {
+ background-color: #ffb81c;
+ left: 21px;
+ top: 42px;
+ }
+ 43.75% {
+ background-color: #ffb81c;
+ left: 0;
+ top: 21px;
+ }
+ 50% {
+ background-color: #caa2dd;
+ left: 21px;
+ top: 0;
+ }
+ 56.25% {
+ background-color: #caa2dd;
+ left: 42px;
+ top: 21px;
+ }
+ 62.5% {
+ background-color: #caa2dd;
+ left: 21px;
+ top: 42px;
+ }
+ 68.75% {
+ background-color: #caa2dd;
+ left: 0;
+ top: 21px;
+ }
+ 75% {
+ background-color: #d9e51c;
+ left: 21px;
+ top: 0;
+ }
+ 81.25% {
+ background-color: #d9e51c;
+ left: 42px;
+ top: 21px;
+ }
+ 87.5% {
+ background-color: #d9e51c;
+ left: 21px;
+ top: 42px;
+ }
+ 93.75% {
+ background-color: #d9e51c;
+ left: 0;
+ top: 21px;
+ }
+ 100% {
+ background-color: #3bb2df;
+ left: 21px;
+ top: 0;
+ }
+}
diff --git a/catalog-ui/app/styles/tlv-sprite.less b/catalog-ui/app/styles/tlv-sprite.less
new file mode 100644
index 0000000000..472e2fb313
--- /dev/null
+++ b/catalog-ui/app/styles/tlv-sprite.less
@@ -0,0 +1,138 @@
+.tlv-sprite {
+ background-image: url("images/sprites/tlv-sprite.png");
+ display: inline-block;
+ vertical-align: middle;
+
+ &.logo-blue {
+ background-position: -10px -20px;
+ width: 28px;
+ height: 28px;
+ }
+
+ &.user {
+ background-position: -50px -20px;
+ width: 15px;
+ height: 20px;
+ }
+
+ .facebook {
+ background-position: -10px -260px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .facebook-hover {
+ background-position: -10px -300px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .twitter {
+ background-position: -50px -260px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .twitter-hover {
+ background-position: -50px -300px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .linkedin {
+ background-position: -89px -260px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .linkedin-hover {
+ background-position: -89px -300px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .youtube {
+ background-position: -130px -260px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .youtube-hover {
+ background-position: -130px -300px;
+ width: 36px;
+ height: 36px;
+ }
+
+ .footer-open {
+ background-position: -250px -59px;
+ width: 14px;
+ height: 10px;
+ }
+
+ .footer-open-hover {
+ background-position: -250px -79px;
+ width: 14px;
+ height: 10px;
+ }
+
+ .footer-close {
+ background-position: -230px -59px;
+ width: 14px;
+ height: 10px;
+ }
+
+ .footer-close-hover {
+ background-position: -230px -79px;
+ width: 14px;
+ height: 10px;
+ }
+
+ .checkbox-disadbled {
+ background-position: -10px -60px;
+ width: 14px;
+ height: 14px;
+ }
+
+ .checkbox {
+ background-position: -10px -60px;
+ width: 14px;
+ height: 14px;
+ }
+
+ .checkbox-hover {
+ background-position: -10px -100px;
+ width: 14px;
+ height: 14px;
+ }
+
+ .checkbox-checked {
+ background-position: -10px -120px;
+ width: 14px;
+ height: 14px;
+ }
+
+ .radio-disabled {
+ background-position: -50px -60px;
+ width: 14px;
+ height: 14px;
+ }
+
+ .radio {
+ background-position: -50px -80px;
+ width: 14px;
+ height: 14px;
+ }
+
+ .radio-checked {
+ background-position: -50px -120px;
+ width: 14px;
+ height: 14px;
+ }
+}
+
+.tlv-x-btn, .tlv-x-btn:hover {
+ background-position: -17px -147px;
+ width: 10px;
+ height: 10px;
+}
+
diff --git a/catalog-ui/app/styles/tooltips.less b/catalog-ui/app/styles/tooltips.less
new file mode 100644
index 0000000000..f4c618e2dc
--- /dev/null
+++ b/catalog-ui/app/styles/tooltips.less
@@ -0,0 +1,110 @@
+// ---------------------------------------------------------------------------------------------------
+// tooltips
+// ---------------------------------------------------------------------------------------------------
+div.tooltips {
+ position: relative;
+ display: inline;
+ float: right;
+ z-index: 1;
+ top: 30px;
+ left: 85px;
+
+ span {
+ .bg_s;
+ position: absolute;
+ width: 230px;
+ color: @color_c;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ border-radius: 6px;
+ visibility: hidden;
+
+ &:after {
+ .arrow(8px, @color_s);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------------------------------
+// Qtip
+// ---------------------------------------------------------------------------------------------------
+.qtip-custom {
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ background-color: rgb(80,99,113);
+ border-color:rgb(80,99,113);
+ color:white;
+ font-size:14px;
+
+}
+
+
+.tooltip-custom {
+ background-color: rgba(80, 99, 113, 0.9);
+ z-index: 1100;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+ max-width: 280px;
+ text-align: center;
+ word-break: normal;
+ word-wrap: break-word;
+ padding-right: 10px;
+ padding-left: 10px;
+}
+
+._720kb-tooltip-caret:before
+ {
+ border: 6px solid rgba(80, 99, 113, 0.9);
+
+ }
+
+.tooltip-rightside{
+ right: 2px;
+ left: initial !important;
+
+ ._720kb-tooltip-caret:before{
+ right: 21px;
+ left: initial;
+ }
+}
+
+.tab-tooltip{
+ max-width: 220px;
+ white-space: nowrap;
+}
+.break-word-tooltip{
+ word-break: break-all;
+ margin-right: 2px;
+}
+
+.icon-tooltip{
+ margin-left: 12px;
+ right: initial !important;
+
+ ._720kb-tooltip-caret:before{
+ left: 44px;
+ right: initial;
+ }
+}
+
+.uib-custom-tooltip {
+ z-index: 999999;
+
+ .tooltip-inner {
+ background-color: rgba(80, 99, 113, 0.9);
+ padding: 5px 10px 5px 10px;
+ border-radius: 2px;
+ }
+}
+.tooltip {
+ &.bottom .tooltip-arrow {
+
+ margin-left: -6px !important;
+ border-width: 0 6px 6px !important;
+ border-bottom-color: rgba(80, 99, 113, 0.9) !important;
+ }
+}
+
diff --git a/catalog-ui/app/styles/variables-old.less b/catalog-ui/app/styles/variables-old.less
new file mode 100644
index 0000000000..29eb61de7c
--- /dev/null
+++ b/catalog-ui/app/styles/variables-old.less
@@ -0,0 +1,77 @@
+/*---------------------------------------------- General ----------------------------------------------*/
+@images: "images";
+
+// Colors
+@color_a: #3b7b9b; // product category
+@color_b: #666666;
+@color_c: #ffffff;
+@color_d: #1d9a95; // dashboard service (S at the top left of the card)
+@color_e: #cdcdcd;
+@color_f: #506472; // #e0e5e9 composition right side tabs
+@color_g: #263d4d; // dashboard left sidebar categories
+@color_h: #da1f3d; // not valid
+@color_i: #445663; // dashboard left sidebar
+@color_j: #D3DBE0; // dashboard header | view left side
+@color_k: #e1e7ec; // composition item | composition right side background
+@color_l: #1e9a33; // valid
+@color_m: #a8b3b9; // composition sub category
+@color_n: #ECEFF3; // dashboard main section
+@color_o: #798996; // composition category
+@color_p: #8c8c8c; // composition relation tab?
+@color_q: #b5b5b5; // Liz please change from b6b5b5 to b5b5b5
+@color_r: #e85858; // dashboard resource (R at the top left of the card)
+@color_s: #000000; // #62727f
+@color_t: #3196c9; // #346d8a
+@color_u: #23aa63;
+@color_v: #198682;
+@color_w: #384752;
+@color_x: #c91d39;
+@color_y: #697a87;
+@color_z: #28bd6e;
+@color_zz: #93deb6;
+
+
+// Color hover
+@color_a_hover: darken(@color_a, 10.0%);
+@color_b_hover: darken(@color_b, 10.0%);
+@color_c_hover: darken(@color_c, 10.0%);
+@color_d_hover: darken(@color_d, 10.0%);
+@color_e_hover: darken(@color_e, 10.0%);
+@color_f_hover: darken(@color_f, 10.0%);
+@color_g_hover: darken(@color_g, 10.0%);
+@color_h_hover: darken(@color_h, 10.0%);
+@color_i_hover: darken(@color_i, 10.0%);
+@color_j_hover: darken(@color_j, 10.0%);
+@color_k_hover: darken(@color_k, 10.0%);
+@color_l_hover: darken(@color_l, 10.0%);
+@color_m_hover: darken(@color_m, 10.0%);
+@color_n_hover: darken(@color_n, 10.0%);
+@color_o_hover: darken(@color_o, 10.0%);
+@color_q_hover: darken(@color_q, 10.0%);
+@color_p_hover: darken(@color_p, 10.0%);
+@color_r_hover: darken(@color_r, 10.0%);
+@color_s_hover: darken(@color_s, 10.0%);
+@color_t_hover: darken(@color_t, 10.0%);
+@color_u_hover: darken(@color_u, 10.0%);
+@color_v_hover: darken(@color_v, 10.0%);
+@color_w_hover: darken(@color_w, 10.0%);
+@color_x_hover: darken(@color_x, 10.0%);
+@color_y_hover: darken(@color_y, 10.0%);
+@color_z_hover: darken(@color_z, 10.0%);
+@color_zz_hover: darken(@color_zz, 10.0%);
+
+/* to check if can delete */
+@border_color_a: #1D3A52;
+@border_color_c: #C1CDD5;
+@border_color_d: rgba(120, 136, 148, 0.26);
+@border_color_e: rgba(59, 123, 155, 0.5);
+@border_color_f: #cfcfcf;
+@border_color_g: @color_a;
+@border_color_view-mode: #ededed;
+
+// Fonts
+@font-omnes-light: omnes-light, sans-serif;
+@font-omnes-regular: omnes-regular, sans-serif;
+@font-omnes-medium: omnes-medium, sans-serif;
+@font-omnes-medium-italic: omnes-medium-italic, sans-serif;
+@font-omnes-bold: omnes-bold, sans-serif;
diff --git a/catalog-ui/app/styles/variables.less b/catalog-ui/app/styles/variables.less
new file mode 100644
index 0000000000..bf8854a5bb
--- /dev/null
+++ b/catalog-ui/app/styles/variables.less
@@ -0,0 +1,48 @@
+// Fonts
+@font-omnes-light: omnes-light, sans-serif;
+@font-omnes-regular: omnes-regular, sans-serif;
+@font-omnes-medium: omnes-medium, sans-serif;
+@font-omnes-medium-italic: omnes-medium-italic, sans-serif;
+@font-omnes-bold: omnes-bold, sans-serif;
+
+/*---------------------------------------------- General ----------------------------------------------*/
+@images: "images";
+
+// Main Colors
+@main_color_a: #009fdb;
+@main_color_b: #056bae;
+@main_color_c: #71c5eb;
+@main_color_d: #4ca90c;
+@main_color_e: #007a3e;
+@main_color_f: #b5bd00;
+@main_color_g: #ea7499;
+@main_color_h: #ffb81c;
+@main_color_i: #702f8a;
+@main_color_j: #9063cd;
+@main_color_k: #caa2dd;
+@main_color_l: #000000;
+@main_color_m: #5a5a5a;
+@main_color_n: #959595;
+@main_color_o: #d2d2d2;
+@main_color_p: #ffffff;
+
+// Functional Colors
+@func_color_q: #cf2a2a;
+@func_color_r: #f2f2f2;
+@func_color_s: #191919;
+@func_color_b: #0568ae;
+@func_color_e: #007a3e;
+@func_color_h: #ffb81c;
+
+// Tlv Colors
+@tlv_color_t: #f8f8f8;
+@tlv_color_u: #eaeaea;
+@tlv_color_v: #e6f6fb;
+
+/*---------------------------------------------- Parameters ----------------------------------------------*/
+@header_height: 0px;
+@top_nav_height: 50px;
+@top_nav_admin_height: 44px;
+@action_nav_height: 53px;
+
+@border_color_view-mode: #ededed;
diff --git a/catalog-ui/app/styles/welcome-sprite.less b/catalog-ui/app/styles/welcome-sprite.less
new file mode 100644
index 0000000000..010d30c9ef
--- /dev/null
+++ b/catalog-ui/app/styles/welcome-sprite.less
@@ -0,0 +1,57 @@
+.sprite-welcome {
+ background-image: url(images/welcome/sprite.png);
+ display: inline-block;
+ vertical-align: middle;
+
+ &.logo {
+ background-position: 0 0;
+ width: 149px;
+ height: 47px;
+ position: absolute;
+ top: 25px;
+ left: 20px;
+ }
+
+ &.play {
+ background-position: 0 -60px;
+ width: 120px;
+ height: 120px;
+ }
+
+ &.close {
+ background-position: -166px -11px;
+ width: 25px;
+ height: 25px;
+ }
+
+ &.close_white {
+ background-position: -166px -37px;
+ width: 25px;
+ height: 25px;
+ }
+
+ &.recycle {
+ background-position: -125px -60px;
+ width: 24px;
+ height: 24px;
+ }
+
+ &.idea {
+ background-position: -159px -57px;
+ width: 23px;
+ height: 25px;
+ }
+
+ &.jigsaw {
+ background-position: -125px -94px;
+ width: 25px;
+ height: 25px;
+ }
+
+ &.ball {
+ background-position: -159px -95px;
+ width: 23px;
+ height: 23px;
+ }
+
+}
diff --git a/catalog-ui/app/styles/welcome-style.less b/catalog-ui/app/styles/welcome-style.less
new file mode 100644
index 0000000000..0d969878c7
--- /dev/null
+++ b/catalog-ui/app/styles/welcome-style.less
@@ -0,0 +1,651 @@
+@zoom-in-animation-play: 2s;
+@zoom-in-animation: 3s;
+@slide-fade-in-text-main-animation: 2s;
+
+html,
+body {
+ height: 100%;
+ /*overflow-x: hidden;*/
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ background-image: url(images/welcome/bg/global.png);
+}
+
+.sdc-welcome-new-page {
+
+ opacity: 1;
+ height: 100%;
+ background-color: @main_color_l;
+
+ .os-welcome {
+ top: 47%;
+ position: fixed;
+ left: 42%;
+ font-size: 40px;
+ }
+
+ #slide-2, #slide-3, #slide-4, #slide-5 {
+ padding: 0 50px 0 30px;
+ }
+
+ .visible {
+ visibility: visible;
+ }
+
+ .asdc-welcome-close {
+ position: absolute;
+ right: 38px;
+ top: 30px;
+ z-index: 101;
+ cursor: pointer;
+
+ .sprite-welcome;
+ .sprite-welcome.close;
+
+ &:hover {
+ .sprite-welcome.close_white;
+ }
+ }
+
+ .asdc-welcome-frame {
+ height: 100vh;
+ width: 100%;
+ background-size: cover;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .asdc-welcome-frame.frame-0 {
+ background-image: url(images/welcome/bg/002.jpg);
+ background-size: cover;
+
+ .asdc-whats-new {
+
+ z-index: 1;
+
+ .news-items-row {
+ display: flex;
+ flex-direction: row;
+
+ .news-item-wrapper {
+ width: 250px;
+ height: 343px;
+ background-color: @main_color_p;
+ margin: 16px;
+ padding: 40px 20px 20px 20px;
+ border-radius: 4px;
+ opacity: 0;
+
+ ul {
+ padding-left: 17px;
+ li {
+ list-style: disc;
+ }
+ }
+
+ ol {
+ padding-left: 12px;
+ }
+
+ &.bg-1 {border-bottom: solid 5px @main_color_a;}
+ &.bg-2 {border-bottom: solid 5px @main_color_j;}
+ &.bg-3 {border-bottom: solid 5px @main_color_d;}
+ &.bg-4 {border-bottom: solid 5px @main_color_i;}
+ &.bg-5 {border-bottom: solid 5px @main_color_e;}
+ &.bg-6 {border-bottom: solid 5px @func_color_b;}
+ &.bg-7 {border-bottom: solid 5px @main_color_c;}
+ &.bg-8 {border-bottom: solid 5px @main_color_k;}
+
+ .news-body {
+ .m_14_r;
+ }
+ .news-title {
+ margin-bottom: 20px;
+ .s_18_m;
+ }
+ }
+ }
+
+
+ }
+ }
+
+ .asdc-welcome-frame.frame-01 {
+ background-image: url(images/welcome/bg/002.png);
+ background-size: cover;
+ }
+
+ .asdc-welcome-frame.frame-02 .asdc-welcome-slide-image-box {
+ position: absolute;
+ right: 11.6vw;
+ top: 10.5vw;
+ z-index: 1;
+ }
+
+ .asdc-welcome-frame.frame-02 .asdc-welcome-slide-text-box {
+ position: absolute;
+ top: 33%;
+ left: 10%;
+ z-index: 1;
+ }
+
+ .asdc-welcome-frame.frame-02 .asdc-welcome-frame-shape {
+ background-image: url(images/welcome/bg/shape02.png);
+ position: absolute;
+ height: 19vw;
+ width: 19vw;
+ top: 2.1vw;
+ left: 32.1vw;
+ /* opacity: .3; */
+ background-size: contain;
+ /* background-repeat: no-repeat; */
+ z-index: 0;
+ visibility: hidden;
+
+ animation-duration: @zoom-in-animation;
+ -moz-animation-duration: @zoom-in-animation;
+ -webkit-animation-duration: @zoom-in-animation;
+ }
+
+ .asdc-welcome-frame.frame-02 .asdc-welcome-frame-connection {
+ background-image: url(images/welcome/bg/connection02.png);
+ width: 56px;
+ height: 27vw;
+ position: absolute;
+ bottom: 0;
+ right: 295px;
+ background-position: 0px 35vw;
+ }
+
+ .asdc-welcome-frame.frame-03 .asdc-welcome-slide-image-box {
+ position: absolute;
+ left: 6%;
+ top: 20%;
+ z-index: 1;
+ }
+
+ .asdc-welcome-frame.frame-03 .asdc-welcome-slide-text-box {
+ position: absolute;
+ top: 38%;
+ right: 14.2vw;
+ z-index: 1;
+ }
+
+ .asdc-welcome-frame.frame-03 .asdc-welcome-frame-shape {
+ background-image: url(images/welcome/bg/shape03.png);
+ position: absolute;
+ height: 378px;
+ width: 252px;
+ top: 0vw;
+ right: 195px;
+ background-size: contain;
+ z-index: 0;
+ visibility: hidden;
+
+ animation-duration: @zoom-in-animation;
+ -moz-animation-duration: @zoom-in-animation;
+ -webkit-animation-duration: @zoom-in-animation;
+ }
+
+ .asdc-welcome-frame.frame-03 .asdc-welcome-frame-connection {
+ background-image: url(images/welcome/bg/connection03.png);
+ width: 204px;
+ height: 371px;
+ position: absolute;
+ bottom: -109px;
+ right: 590px;
+ }
+
+ .asdc-welcome-frame.frame-04 .asdc-welcome-slide-image-box {
+ position: absolute;
+ right: 10%;
+ top: 20%;
+ z-index: 5;
+ }
+
+ .asdc-welcome-frame.frame-04 .asdc-welcome-slide-text-box {
+ position: absolute;
+ top: 34%;
+ left: 8%;
+ z-index: 5;
+ }
+
+ .asdc-welcome-frame.frame-04 .asdc-welcome-frame-connection {
+ background-image: url(images/welcome/bg/connection04.png);
+ width: 56px;
+ height: 752px;
+ position: absolute;
+ bottom: -143px;
+ right: 946px;
+ }
+
+ .asdc-welcome-frame.frame-04 .asdc-welcome-frame-shape {
+ background-image: url(images/welcome/bg/shape04.png);
+ position: absolute;
+ height: 412px;
+ width: 355px;
+ top: 0;
+ right: 734px;
+ z-index: 1;
+ visibility: hidden;
+
+ animation-duration: @zoom-in-animation;
+ -moz-animation-duration: @zoom-in-animation;
+ -webkit-animation-duration: @zoom-in-animation;
+ }
+
+ .asdc-welcome-frame.frame-05 .asdc-welcome-slide-text-box {
+ position: absolute;
+ top: 29%;
+ right: 10%;
+ z-index: 5;
+ }
+
+ .asdc-welcome-frame.frame-05 .asdc-welcome-frame-shape {
+ background-image: url(images/welcome/bg/shape05.png);
+ position: absolute;
+ height: 794px;
+ width: 597px;
+ top: 0;
+ right: 40%;
+ z-index: 1;
+ visibility: hidden;
+
+ animation-duration: @zoom-in-animation;
+ -moz-animation-duration: @zoom-in-animation;
+ -webkit-animation-duration: @zoom-in-animation;
+ }
+
+ .asdc-welcome-frame.frame-06 {
+ background-image: url(images/welcome/bg/004.png);
+ background-size: cover;
+ }
+
+ .asdc-welcome-frame.frame-06 .asdc-welcome-main {
+ bottom: 30%;
+ }
+
+ .asdc-welcome-header {
+ /*background-color: rgba(17, 21, 33, 0.52);*/
+ height: 70px;
+ width: 100%;
+ padding: 55px 0 0 65px;
+ color: #fff;
+ z-index: 1;
+ position: absolute;
+ left: 0;
+ top: 0;
+ }
+
+ .welcome-nav {
+ visibility: hidden;
+ }
+
+ .welcome-logo {
+ cursor: pointer;
+ }
+
+ #sdc-welcome-video-wrapper {
+ opacity: 0;
+ }
+
+ .asdc-welcome-video-close {
+ position: absolute;
+ top: 30px;
+ right: 30px;
+ z-index: 1;
+ }
+
+ .asdc-welcome-video {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ min-width: 100%;
+ min-height: 100%;
+ width: auto;
+ height: auto;
+ -webkit-transform: translateX(-50%) translateY(-50%);
+ transform: translateX(-50%) translateY(-50%);
+ }
+
+ .asdc-welcome-video-icon {
+ cursor: pointer;
+ /*margin-bottom: 80px;*/
+ visibility: hidden;
+ width: 140px;
+ margin: 0 auto;
+ right: -24px;
+ position: relative;
+
+ animation-duration: @zoom-in-animation-play;
+ -moz-animation-duration: @zoom-in-animation-play;
+ -webkit-animation-duration: @zoom-in-animation-play;
+
+ .asdc-welcome-video-icon-play {
+ position: absolute;
+ top: -11px;
+ left: -11px;
+ z-index: 1;
+ }
+
+ .asdc-welcome-video-icon-play:hover + .asdc-welcome-inner-circle {
+ -webkit-animation-play-state: running;
+ -moz-animation-play-state: running;
+ animation-play-state: running;
+
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ transform: rotate(360deg);
+
+ -webkit-animation:turning_acw 3s;
+ -moz-animation:turning_acw 3s;
+ animation:turning_acw 3s;
+
+ -webkit-animation-iteration-count: 99;
+ -moz-animation-iteration-count: 99;
+ animation-iteration-count: 99;
+ }
+
+ .asdc-welcome-inner-circle {
+ border: 2px solid #ECEBFA;
+ border-left-color:#666666;
+ border-right-color:#999999;
+ .opacity(0.5);
+
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ transform: rotate(360deg);
+
+ position:absolute;
+
+ width: 96px;
+ height: 96px;
+
+ .border-radius(96px);
+
+ -webkit-animation:turning_acw 3s;
+ -moz-animation:turning_acw 3s;
+ animation:turning_acw 3s;
+
+ -webkit-animation-iteration-count: 3;
+ -moz-animation-iteration-count: 3;
+ animation-iteration-count: 3;
+ }
+ }
+
+ /*@-webkit-keyframes turning_acw {
+ 0%{
+ -webkit-transform: rotate(360deg); }
+ 100%{
+ -webkit-transform: rotate(0deg); }
+ }
+
+ @-moz-keyframes turning_acw {
+ 0%{
+ -moz-transform: rotate(360deg); }
+ 100%{
+ -moz-transform: rotate(0deg); }
+ }*/
+
+ @keyframes turning_acw {
+ 0%{
+ transform: rotate(360deg); }
+ 100%{
+ transform: rotate(0deg); }
+ }
+
+ .asdc-welcome-title {
+ color: #373f51;
+ font-size: 2.4vw;
+ font-family: omnes-light;
+ line-height: 1.0em;
+ letter-spacing: -0.01em;
+ }
+
+ .asdc-welcome-main {
+ text-align: center;
+ position: absolute;
+ bottom: 40%;
+ z-index: 1;
+ color: #fff;
+ left: 0;
+ width: 100%;
+ }
+
+ .whats-new {
+ .p_16_r;
+ position: absolute;
+ left: 205px;
+ top: 45px;
+ cursor: pointer;
+ text-decoration: none;
+ }
+
+ .asdc-welcome-main-title {
+ color: #fff;
+ font-size: 62px;
+ font-family: omnes-light;
+ visibility: hidden;
+ height: 72px;
+
+ animation-duration: @slide-fade-in-text-main-animation;
+ -moz-animation-duration: @slide-fade-in-text-main-animation;
+ -webkit-animation-duration: @slide-fade-in-text-main-animation;
+ }
+
+ .asdc-welcome-main-message {
+ font-size: 28px;
+ font-family: omnes-light;
+ line-height: 1.8em;
+ visibility: hidden;
+
+ animation-duration: @slide-fade-in-text-main-animation;
+ -moz-animation-duration: @slide-fade-in-text-main-animation;
+ -webkit-animation-duration: @slide-fade-in-text-main-animation;
+ }
+
+ .asdc-welcome-main-back-btn-ph {
+ margin: 24px 0 70px;
+ }
+
+ .asdc-welcome-main-back-btn {
+ font-size: 16px;
+ color: #009fdb;
+ font-family: omnes-medium;
+ border: 2px solid;
+ border-radius: 3px;
+ line-height: 40px;
+ min-width: 242px;
+ display: inline-block;
+ visibility: hidden;
+ text-decoration: none;
+
+ &:hover {
+ color: #33bfec;
+ border-color: #33bfec;
+ }
+ }
+
+ .asdc-welcome-cover {
+ position: absolute;
+ top: 0px;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ background-color: rgba(14, 13, 12, 0.8);
+ z-index: 0;
+ }
+
+ .asdc-welcome-slide-text-box {
+ border-radius: 10px;
+ background-color: #fff;
+ padding: 10px;
+ }
+
+ .asdc-welcome-slide-text-box-title {
+ color: #373f51;
+ font-size: 2.4vw;
+ font-family: omnes-light;
+ letter-spacing: -0.015em;
+ line-height: 1.04em;
+ visibility: hidden;
+
+ animation-duration: @slide-fade-in-text-main-animation;
+ -moz-animation-duration: @slide-fade-in-text-main-animation;
+ -webkit-animation-duration: @slide-fade-in-text-main-animation;
+ }
+
+ .asdc-welcome-slide-text-box-content {
+ color: #737b81;
+ font-size: 1vw;
+ font-family: omnes-light;
+ padding-top: .5vw;
+ line-height: 1.3em;
+ visibility: hidden;
+
+ animation-duration: @slide-fade-in-text-main-animation;
+ -moz-animation-duration: @slide-fade-in-text-main-animation;
+ -webkit-animation-duration: @slide-fade-in-text-main-animation;
+ }
+
+ .asdc-welcome-slide-image-box {
+ box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.2);
+ height: 29.6vw;
+ width: 40vw;
+ }
+
+ .asdc-welcome-slide-image {
+ height: 100%;
+ width: 100%;
+ }
+
+ /*.asdc-welcome-frame-content {*/
+ /*display: flex;*/
+ /*justify-content: center;*/
+ /*align-items: center;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-text {*/
+ /*color: #666;*/
+ /*font-size: 16px;*/
+ /*font-family: omnes-light;*/
+ /*margin-top: 30px;*/
+ /*line-height: 1.6em;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-text-p {*/
+ /*position: relative;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-text-p-icon {*/
+ /*position: absolute;*/
+ /*height: 25px;*/
+ /*width: 25px;*/
+ /*left: -40px;*/
+ /*top: 10px;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-02 .asdc-welcome-frame-content-right,*/
+ /*.asdc-welcome-frame.frame-04 .asdc-welcome-frame-content-right {*/
+ /*max-width: 30%;*/
+ /*flex: 1;*/
+ /*margin-left: 5%;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-02 .asdc-welcome-frame-content-left,*/
+ /*.asdc-welcome-frame.frame-04 .asdc-welcome-frame-content-left {*/
+ /*max-width: 55%;*/
+ /*flex: 1;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-04 .asdc-welcome-frame-content-img {*/
+ /*width: 36vw;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-03 .asdc-welcome-frame-content-left {*/
+ /*max-width: 30%;*/
+ /*flex: 1;*/
+ /*margin-right: 5%;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-03 .asdc-welcome-frame-content-right {*/
+ /*max-width: 55%;*/
+ /*flex: 1;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-03 .asdc-welcome-frame-content-img {*/
+ /*left: 0;*/
+ /*right: auto;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-03 .asdc-welcome-frame-content-img-bubble {*/
+ /*left: auto;*/
+ /*top: 0;*/
+ /*position: absolute;*/
+ /*right: 0;*/
+ /*top: -7vh;*/
+ /*}*/
+ /*.asdc-welcome-frame.frame-05 .asdc-welcome-frame-content{*/
+ /*color: #fff;*/
+ /*font-size: 37px;*/
+ /*font-family: omnes-light;*/
+ /*flex-direction: column;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-img-ph {*/
+ /*position: relative;*/
+ /*!*width: 42%*!*/
+ /*!*width: 840px;*!*/
+ /*!*height: 674px;*!*/
+ /*width: 52vw;*/
+ /*height: 39vw;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-img {*/
+ /*width: 45vw;*/
+ /*position: absolute;*/
+ /*z-index: 1;*/
+ /*right: 0;*/
+ /*bottom: 0;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-img-bubble {*/
+ /*border-radius: 50%;*/
+ /*height: 38vw;*/
+ /*width: 38vw;*/
+ /*position: absolute;*/
+ /*left: 0;*/
+ /*top: 0;*/
+ /*position: absolute;*/
+ /*right: 0;*/
+ /*top: -7vh;*/
+ /*}*/
+ /*.asdc-welcome-frame-content-img-bubble.blue {*/
+ /*background: linear-gradient(to right, #067ab4 0%, #44c8f5 100%);*/
+ /*}*/
+ /*.asdc-welcome-frame-content-img-bubble.green {*/
+ /*background: linear-gradient(to right, #77be46 0%, #d2e15f 100%);*/
+ /*}*/
+ /*.asdc-welcome-frame-content-img-bubble.purple {*/
+ /*background: linear-gradient(to right, #8a559f 0%, #d6b7e5 100%);*/
+ /*}*/
+ /*.asdc-welcome-button {*/
+ /*font-family: omnes-medium;*/
+ /*font-size: 14px;*/
+ /*background-color: #009fdb;*/
+ /*text-decoration: none;*/
+ /*border: 0;*/
+ /*border-radius: 4px;*/
+ /*color: #fff;*/
+ /*text-align: center;*/
+ /*line-height: 30px;*/
+ /*min-width: 200px;*/
+ /*margin-top: 50px;*/
+ /*}*/
+ .asdc-welcome-footer {
+ background-color: #000;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ font-size: 12px;
+ padding: 20px 10%;
+ left: 0;
+ color: #fff;
+ text-align: center;
+ }
+
+}
diff --git a/catalog-ui/app/third-party/PunchOutRegistry.js b/catalog-ui/app/third-party/PunchOutRegistry.js
new file mode 100644
index 0000000000..bc93453dc1
--- /dev/null
+++ b/catalog-ui/app/third-party/PunchOutRegistry.js
@@ -0,0 +1,98 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+(function(window) {
+ "use strict";
+
+ if (window.PunchOutRegistry) {
+ return;
+ }
+
+ var queuedFactoryRequests = new Map();
+ var factoryPromises = new Map();
+ var instancePromises = new Map();
+
+ function registerFactory(name, factory) {
+ if (factoryPromises.has(name) && !queuedFactoryRequests.has(name)) {
+ console.error("PunchOut \"" + name + "\" has been already registered");
+ return;
+ }
+ if (queuedFactoryRequests.has(name)) {
+ var factoryRequest = queuedFactoryRequests.get(name);
+ factoryRequest(factory);
+ queuedFactoryRequests.delete(name);
+ } else {
+ factoryPromises.set(name, Promise.resolve(factory));
+ }
+ }
+
+ function getFactoryPromise(name) {
+ var factoryPromise = factoryPromises.get(name);
+ if (!factoryPromise) {
+ factoryPromise = new Promise(function (resolveFactory) {
+ queuedFactoryRequests.set(name, resolveFactory);
+ });
+ factoryPromises.set(name, factoryPromise);
+ }
+ return factoryPromise;
+ }
+
+ function getInstancePromise(name, element) {
+ var factoryPromise;
+ var instancePromise = instancePromises.get(element);
+ if (!instancePromise) {
+ instancePromise = getFactoryPromise(name).then(function(factory) {
+ return factory();
+ });
+ instancePromises.set(element, instancePromise);
+ }
+ return instancePromise;
+ }
+
+ function renderPunchOut(params, element) {
+ var name = params.name;
+ var options = params.options || {};
+ var onEvent = params.onEvent || function () {};
+
+ getInstancePromise(name, element).then(function (punchOut) {
+ punchOut.render({options: options, onEvent: onEvent}, element);
+ });
+ }
+
+ function unmountPunchOut(element) {
+ if (!instancePromises.has(element)) {
+ console.error("There is no PunchOut in element", element);
+ return;
+ }
+ instancePromises.get(element).then(function(punchOut) {
+ punchOut.unmount(element);
+ });
+ instancePromises.delete(element);
+ }
+
+ var PunchOutRegistry = Object.freeze({
+ register: registerFactory,
+ render: renderPunchOut,
+ unmount: unmountPunchOut
+ });
+
+ window.PunchOutRegistry = PunchOutRegistry;
+
+})(this);
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/.npmignore b/catalog-ui/app/third-party/ng-infinite-scroll/.npmignore
new file mode 100644
index 0000000000..dcbd092327
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/.npmignore
@@ -0,0 +1,3 @@
+compile/*
+node_modules
+.tmp
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml b/catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml
new file mode 100644
index 0000000000..ee95ccb343
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/.travis.yml
@@ -0,0 +1,12 @@
+language: node_js
+node_js:
+ - 0.10
+
+install:
+ - npm install
+
+before_script:
+ - npm install -g grunt-cli
+
+script:
+ - grunt test:protractor-travis
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee b/catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee
new file mode 100644
index 0000000000..a9b9405731
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/Gruntfile.coffee
@@ -0,0 +1,110 @@
+module.exports = (grunt) ->
+ grunt.loadNpmTasks 'grunt-coffeelint'
+ grunt.loadNpmTasks 'grunt-contrib-clean'
+ grunt.loadNpmTasks 'grunt-contrib-coffee'
+ grunt.loadNpmTasks 'grunt-contrib-concat'
+ grunt.loadNpmTasks 'grunt-contrib-connect'
+ grunt.loadNpmTasks 'grunt-contrib-uglify'
+ grunt.loadNpmTasks 'grunt-protractor-runner'
+
+ sauceUser = 'pomerantsevp'
+ sauceKey = '497ab04e-f31b-4a7b-9b18-ae3fbe023222'
+
+ grunt.initConfig
+ pkg: grunt.file.readJSON 'package.json'
+ meta:
+ banner: '/* <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+ coffeelint:
+ src: 'src/**/*.coffee'
+ options:
+ max_line_length:
+ level: 'ignore'
+ line_endings:
+ value: 'unix'
+ level: 'error'
+ no_stand_alone_at:
+ level: 'error'
+ clean:
+ options:
+ force: true
+ build: ["compile/**", "build/**"]
+ coffee:
+ compile:
+ files: [
+ {
+ expand: true
+ cwd: 'src/'
+ src: '**/*.coffee'
+ dest: 'compile/'
+ ext: '.js'
+ }
+ ],
+ options:
+ bare: true
+ concat:
+ options:
+ banner: '<%= meta.banner %>'
+ dist:
+ src: 'compile/**/*.js'
+ dest: 'build/ng-infinite-scroll.js'
+ uglify:
+ options:
+ banner: '<%= meta.banner %>'
+ dist:
+ src: ['build/ng-infinite-scroll.js']
+ dest: 'build/ng-infinite-scroll.min.js'
+ connect:
+ testserver:
+ options:
+ port: 8000
+ hostname: '0.0.0.0'
+ middleware: (connect, options) ->
+ base = if Array.isArray(options.base) then options.base[options.base.length - 1] else options.base
+ [connect.static(base)]
+ protractor:
+ local:
+ options:
+ configFile: 'test/protractor-local.conf.js'
+ args:
+ params:
+ testThrottleValue: 500
+ travis:
+ options:
+ configFile: 'test/protractor-travis.conf.js'
+ args:
+ params:
+ # When using Sauce Connect, we should use a large timeout
+ # since everything is generally much slower than when testing locally.
+ testThrottleValue: 10000
+ sauceUser: sauceUser
+ sauceKey: sauceKey
+
+ grunt.registerTask 'webdriver', () ->
+ done = this.async()
+ p = require('child_process').spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update'])
+ p.stdout.pipe(process.stdout)
+ p.stderr.pipe(process.stderr)
+ p.on 'exit', (code) ->
+ if code isnt 0 then grunt.fail.warn('Webdriver failed to update')
+ done()
+
+ grunt.registerTask 'sauce-connect', () ->
+ done = this.async()
+ require('sauce-connect-launcher')({username: sauceUser, accessKey: sauceKey}, (err, sauceConnectProcess) ->
+ if err then console.error(err.message)
+ else done()
+ )
+
+ grunt.registerTask 'default', ['coffeelint', 'clean', 'coffee', 'concat', 'uglify']
+ grunt.registerTask 'test:protractor-local', [
+ 'default',
+ 'webdriver',
+ 'connect:testserver',
+ 'protractor:local'
+ ]
+
+ grunt.registerTask 'test:protractor-travis', [
+ 'connect:testserver',
+ 'sauce-connect',
+ 'protractor:travis'
+ ]
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/LICENSE b/catalog-ui/app/third-party/ng-infinite-scroll/LICENSE
new file mode 100644
index 0000000000..44ae2bfc40
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Michelle Tilley
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/README.md b/catalog-ui/app/third-party/ng-infinite-scroll/README.md
new file mode 100644
index 0000000000..aa8d8630e2
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/README.md
@@ -0,0 +1,71 @@
+[**Maintainer help needed**: I'm looking for fellows that are willing to help me maintain and improve this project.](https://github.com/sroze/ngInfiniteScroll/issues/267)
+
+---
+
+![logo](http://sroze.github.com/ngInfiniteScroll/images/logo-resized.png)
+
+[![Build Status](https://travis-ci.org/sroze/ngInfiniteScroll.png?branch=master)](https://travis-ci.org/sroze/ngInfiniteScroll)
+
+ngInfiniteScroll is a directive for [AngularJS](http://angularjs.org/) to evaluate an expression when the bottom of the directive's element approaches the bottom of the browser window, which can be used to implement infinite scrolling.
+
+Demos
+-----
+
+Check out the running demos [at the ngInfiniteScroll web site](http://sroze.github.com/ngInfiniteScroll/demos.html).
+
+Version Numbers
+---------------
+
+ngInfinite Scroll follows [semantic versioning](http://semver.org/) and uses the following versioning scheme:
+
+ * Versions starting with 0 (e.g. 0.1.0, 0.2.0, etc.) are for initial development, and the API is not stable
+ * Versions with an even minor version (1.0.0, 1.4.0, 2.2.0, etc.) are stable releases
+ * Versions with an odd minor version (1.1.0, 1.3.0, 2.1.0, etc.) are development releases
+
+The [download page](http://sroze.github.com/ngInfiniteScroll/#download) allows you to pick among various versions and specify which releases are stable (not including pre-release builds).
+
+Getting Started
+---------------
+
+ * Download ngInfiniteScroll from [the download page on the ngInfiniteScroll web site](http://sroze.github.com/ngInfiniteScroll/#download) or install it with:
+ * [Bower](http://bower.io/) via `bower install ngInfiniteScroll`
+ * [NPM](https://www.npmjs.com) via `npm install --save ng-infinite-scroll`
+ * [Nuget](https://www.nuget.org) via `PM> Install-Package ng-infinite-scroll`
+ * Include the script tag on your page after the AngularJS script tag (ngInfiniteScroll *doesn't* require jQuery)
+
+ <script type='text/javascript' src='path/to/angular.min.js'></script>
+ <script type='text/javascript' src='path/to/ng-infinite-scroll.min.js'></script>
+
+ * Ensure that your application module specifies `infinite-scroll` as a dependency:
+
+ angular.module('myApplication', ['infinite-scroll']);
+
+ * Use the directive by specifying an `infinite-scroll` attribute on an element.
+
+ <div infinite-scroll="myPagingFunction()" infinite-scroll-distance="3"></div>
+
+Note that neither the module nor the directive use the `ng` prefix, as that prefix is reserved for the core Angular module.
+
+Detailed Documentation
+----------------------
+
+ngInfiniteScroll accepts several attributes to customize the behavior of the directive; detailed instructions can be found [on the ngInfiniteScroll web site](http://sroze.github.com/ngInfiniteScroll/documentation.html).
+
+Ports
+-----
+
+If you use [AngularDart](https://github.com/angular/angular.dart), Juha Komulainen has [a port of the project](http://pub.dartlang.org/packages/ng_infinite_scroll) you can use.
+
+License
+-------
+
+ngInfiniteScroll is licensed under the MIT license. See the LICENSE file for more details.
+
+Testing
+-------
+
+ngInfiniteScroll uses Protractor for testing. Note that you will need to have Chrome browser, and the `grunt-cli` npm package installed globally if you wish to use grunt (`npm install -g grunt-cli`). Then, install the dependencies with `npm install`.
+
+* `grunt test:protractor-local` - run tests
+
+Thank you very much @pomerantsev for your work on these Protractor tests.
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/bower.json b/catalog-ui/app/third-party/ng-infinite-scroll/bower.json
new file mode 100644
index 0000000000..1171c45190
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/bower.json
@@ -0,0 +1,16 @@
+{
+ "name": "ngInfiniteScroll",
+ "main": "build/ng-infinite-scroll.js",
+ "ignore": [
+ "**/.*",
+ "_*",
+ "node_modules",
+ "compile",
+ "test",
+ "Gruntfile.coffee",
+ ".*"
+ ],
+ "dependencies": {
+ "angular": ">=1.2.0"
+ }
+}
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js
new file mode 100644
index 0000000000..0585004832
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.js
@@ -0,0 +1,209 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/* ng-infinite-scroll - v1.3.0 - 2016-06-30 */
+angular.module('infinite-scroll', []).value('THROTTLE_MILLISECONDS', null).directive('infiniteScroll', [
+ '$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS', function($rootScope, $window, $interval, THROTTLE_MILLISECONDS) {
+ return {
+ scope: {
+ infiniteScroll: '&',
+ infiniteScrollContainer: '=',
+ infiniteScrollDistance: '=',
+ infiniteScrollDisabled: '=',
+ infiniteScrollUseDocumentBottom: '=',
+ infiniteScrollListenForEvent: '@'
+ },
+ link: function(scope, elem, attrs) {
+ var changeContainer, checkInterval, checkWhenEnabled, container, handleInfiniteScrollContainer, handleInfiniteScrollDisabled, handleInfiniteScrollDistance, handleInfiniteScrollUseDocumentBottom, handler, height, immediateCheck, offsetTop, pageYOffset, scrollDistance, scrollEnabled, throttle, unregisterEventListener, useDocumentBottom, windowElement;
+ windowElement = angular.element($window);
+ scrollDistance = null;
+ scrollEnabled = null;
+ checkWhenEnabled = null;
+ container = null;
+ immediateCheck = true;
+ useDocumentBottom = false;
+ unregisterEventListener = null;
+ checkInterval = false;
+ height = function(elem) {
+ elem = elem[0] || elem;
+ if (isNaN(elem.offsetHeight)) {
+ return elem.document.documentElement.clientHeight;
+ } else {
+ return elem.offsetHeight;
+ }
+ };
+ offsetTop = function(elem) {
+ if (!elem[0].getBoundingClientRect || elem.css('none')) {
+ return;
+ }
+ return elem[0].getBoundingClientRect().top + pageYOffset(elem);
+ };
+ pageYOffset = function(elem) {
+ elem = elem[0] || elem;
+ if (isNaN(window.pageYOffset)) {
+ return elem.document.documentElement.scrollTop;
+ } else {
+ return elem.ownerDocument.defaultView.pageYOffset;
+ }
+ };
+ handler = function() {
+ var containerBottom, containerTopOffset, elementBottom, remaining, shouldScroll;
+ if (container === windowElement) {
+ containerBottom = height(container) + pageYOffset(container[0].document.documentElement);
+ elementBottom = offsetTop(elem) + height(elem);
+ } else {
+ containerBottom = height(container);
+ containerTopOffset = 0;
+ if (offsetTop(container) !== void 0) {
+ containerTopOffset = offsetTop(container);
+ }
+ elementBottom = offsetTop(elem) - containerTopOffset + height(elem);
+ }
+ if (useDocumentBottom) {
+ elementBottom = height((elem[0].ownerDocument || elem[0].document).documentElement);
+ }
+ remaining = elementBottom - containerBottom;
+ shouldScroll = remaining <= height(container) * scrollDistance + 1;
+ if (shouldScroll) {
+ checkWhenEnabled = true;
+ if (scrollEnabled) {
+ if (scope.$$phase || $rootScope.$$phase) {
+ return scope.infiniteScroll();
+ } else {
+ return scope.$apply(scope.infiniteScroll);
+ }
+ }
+ } else {
+ if (checkInterval) {
+ $interval.cancel(checkInterval);
+ }
+ return checkWhenEnabled = false;
+ }
+ };
+ throttle = function(func, wait) {
+ var later, previous, timeout;
+ timeout = null;
+ previous = 0;
+ later = function() {
+ previous = new Date().getTime();
+ $interval.cancel(timeout);
+ timeout = null;
+ return func.call();
+ };
+ return function() {
+ var now, remaining;
+ now = new Date().getTime();
+ remaining = wait - (now - previous);
+ if (remaining <= 0) {
+ $interval.cancel(timeout);
+ timeout = null;
+ previous = now;
+ return func.call();
+ } else {
+ if (!timeout) {
+ return timeout = $interval(later, remaining, 1);
+ }
+ }
+ };
+ };
+ if (THROTTLE_MILLISECONDS != null) {
+ handler = throttle(handler, THROTTLE_MILLISECONDS);
+ }
+ scope.$on('$destroy', function() {
+ container.unbind('scroll', handler);
+ if (unregisterEventListener != null) {
+ unregisterEventListener();
+ unregisterEventListener = null;
+ }
+ if (checkInterval) {
+ return $interval.cancel(checkInterval);
+ }
+ });
+ handleInfiniteScrollDistance = function(v) {
+ return scrollDistance = parseFloat(v) || 0;
+ };
+ scope.$watch('infiniteScrollDistance', handleInfiniteScrollDistance);
+ handleInfiniteScrollDistance(scope.infiniteScrollDistance);
+ handleInfiniteScrollDisabled = function(v) {
+ scrollEnabled = !v;
+ if (scrollEnabled && checkWhenEnabled) {
+ checkWhenEnabled = false;
+ return handler();
+ }
+ };
+ scope.$watch('infiniteScrollDisabled', handleInfiniteScrollDisabled);
+ handleInfiniteScrollDisabled(scope.infiniteScrollDisabled);
+ handleInfiniteScrollUseDocumentBottom = function(v) {
+ return useDocumentBottom = v;
+ };
+ scope.$watch('infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom);
+ handleInfiniteScrollUseDocumentBottom(scope.infiniteScrollUseDocumentBottom);
+ changeContainer = function(newContainer) {
+ if (container != null) {
+ container.unbind('scroll', handler);
+ }
+ container = newContainer;
+ if (newContainer != null) {
+ return container.bind('scroll', handler);
+ }
+ };
+ changeContainer(windowElement);
+ if (scope.infiniteScrollListenForEvent) {
+ unregisterEventListener = $rootScope.$on(scope.infiniteScrollListenForEvent, handler);
+ }
+ handleInfiniteScrollContainer = function(newContainer) {
+ if ((newContainer == null) || newContainer.length === 0) {
+ return;
+ }
+ if (newContainer.nodeType && newContainer.nodeType === 1) {
+ newContainer = angular.element(newContainer);
+ } else if (typeof newContainer.append === 'function') {
+ newContainer = angular.element(newContainer[newContainer.length - 1]);
+ } else if (typeof newContainer === 'string') {
+ newContainer = angular.element(document.querySelector(newContainer));
+ }
+ if (newContainer != null) {
+ return changeContainer(newContainer);
+ } else {
+ throw new Error("invalid infinite-scroll-container attribute.");
+ }
+ };
+ scope.$watch('infiniteScrollContainer', handleInfiniteScrollContainer);
+ handleInfiniteScrollContainer(scope.infiniteScrollContainer || []);
+ if (attrs.infiniteScrollParent != null) {
+ changeContainer(angular.element(elem.parent()));
+ }
+ if (attrs.infiniteScrollImmediateCheck != null) {
+ immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck);
+ }
+ return checkInterval = $interval((function() {
+ if (immediateCheck) {
+ handler();
+ }
+ return $interval.cancel(checkInterval);
+ }));
+ }
+ };
+ }
+]);
+
+if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports) {
+ module.exports = 'infinite-scroll';
+}
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js
new file mode 100644
index 0000000000..e2a3036422
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/build/ng-infinite-scroll.min.js
@@ -0,0 +1,22 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/* ng-infinite-scroll - v1.3.0 - 2016-06-30 */
+angular.module("infinite-scroll",[]).value("THROTTLE_MILLISECONDS",null).directive("infiniteScroll",["$rootScope","$window","$interval","THROTTLE_MILLISECONDS",function(a,b,c,d){return{scope:{infiniteScroll:"&",infiniteScrollContainer:"=",infiniteScrollDistance:"=",infiniteScrollDisabled:"=",infiniteScrollUseDocumentBottom:"=",infiniteScrollListenForEvent:"@"},link:function(e,f,g){var h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;return z=angular.element(b),u=null,v=null,j=null,k=null,r=!0,y=!1,x=null,i=!1,q=function(a){return a=a[0]||a,isNaN(a.offsetHeight)?a.document.documentElement.clientHeight:a.offsetHeight},s=function(a){if(a[0].getBoundingClientRect&&!a.css("none"))return a[0].getBoundingClientRect().top+t(a)},t=function(a){return a=a[0]||a,isNaN(window.pageYOffset)?a.document.documentElement.scrollTop:a.ownerDocument.defaultView.pageYOffset},p=function(){var b,d,g,h,l;return k===z?(b=q(k)+t(k[0].document.documentElement),g=s(f)+q(f)):(b=q(k),d=0,void 0!==s(k)&&(d=s(k)),g=s(f)-d+q(f)),y&&(g=q((f[0].ownerDocument||f[0].document).documentElement)),h=g-b,l=h<=q(k)*u+1,l?(j=!0,v?e.$$phase||a.$$phase?e.infiniteScroll():e.$apply(e.infiniteScroll):void 0):(i&&c.cancel(i),j=!1)},w=function(a,b){var d,e,f;return f=null,e=0,d=function(){return e=(new Date).getTime(),c.cancel(f),f=null,a.call()},function(){var g,h;return g=(new Date).getTime(),h=b-(g-e),h<=0?(c.cancel(f),f=null,e=g,a.call()):f?void 0:f=c(d,h,1)}},null!=d&&(p=w(p,d)),e.$on("$destroy",function(){if(k.unbind("scroll",p),null!=x&&(x(),x=null),i)return c.cancel(i)}),n=function(a){return u=parseFloat(a)||0},e.$watch("infiniteScrollDistance",n),n(e.infiniteScrollDistance),m=function(a){if(v=!a,v&&j)return j=!1,p()},e.$watch("infiniteScrollDisabled",m),m(e.infiniteScrollDisabled),o=function(a){return y=a},e.$watch("infiniteScrollUseDocumentBottom",o),o(e.infiniteScrollUseDocumentBottom),h=function(a){if(null!=k&&k.unbind("scroll",p),k=a,null!=a)return k.bind("scroll",p)},h(z),e.infiniteScrollListenForEvent&&(x=a.$on(e.infiniteScrollListenForEvent,p)),l=function(a){if(null!=a&&0!==a.length){if(a.nodeType&&1===a.nodeType?a=angular.element(a):"function"==typeof a.append?a=angular.element(a[a.length-1]):"string"==typeof a&&(a=angular.element(document.querySelector(a))),null!=a)return h(a);throw new Error("invalid infinite-scroll-container attribute.")}},e.$watch("infiniteScrollContainer",l),l(e.infiniteScrollContainer||[]),null!=g.infiniteScrollParent&&h(angular.element(f.parent())),null!=g.infiniteScrollImmediateCheck&&(r=e.$eval(g.infiniteScrollImmediateCheck)),i=c(function(){return r&&p(),c.cancel(i)})}}}]),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="infinite-scroll");
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/package.json b/catalog-ui/app/third-party/ng-infinite-scroll/package.json
new file mode 100644
index 0000000000..8859faecdc
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/package.json
@@ -0,0 +1,80 @@
+{
+ "name": "ng-infinite-scroll",
+ "version": "1.3.0",
+ "description": "Infinite scrolling for AngularJS",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/sroze/ngInfiniteScroll.git"
+ },
+ "main": "build/ng-infinite-scroll.js",
+ "browser": "build/ng-infinite-scroll.js",
+ "scripts": {
+ "test": "grunt test:protractor-local"
+ },
+ "author": {
+ "name": "Michelle Tilley",
+ "email": "michelle@michelletilley.net",
+ "url": "http://michelletilley.net"
+ },
+ "contributors": [
+ {
+ "name": "Samuel ROZE",
+ "email": "samuel.roze@gmail.com",
+ "url": "http://sroze.io"
+ }
+ ],
+ "license": "MIT",
+ "devDependencies": {
+ "coffee-script": "~1.8.0",
+ "grunt": "~0.4.5",
+ "grunt-coffeelint": "~0.0.13",
+ "grunt-contrib-clean": "~0.6.0",
+ "grunt-contrib-coffee": "~0.12.0",
+ "grunt-contrib-concat": "~0.5.0",
+ "grunt-contrib-connect": "0.8.0",
+ "grunt-contrib-uglify": "~0.6.0",
+ "grunt-protractor-runner": "1.1.4",
+ "mkdirp": "0.5.0",
+ "protractor": "1.4.0",
+ "sauce-connect-launcher": "0.9.0"
+ },
+ "gitHead": "6fbf4b41947f9a3023b4aba0e613231950ccc4a1",
+ "bugs": {
+ "url": "https://github.com/sroze/ngInfiniteScroll/issues"
+ },
+ "homepage": "https://github.com/sroze/ngInfiniteScroll#readme",
+ "_id": "ng-infinite-scroll@1.3.0",
+ "_shasum": "c2e98d8fd134b0525a4d2cf58c95d9b583755112",
+ "_from": "ng-infinite-scroll@>=1.3.0 <2.0.0",
+ "_npmVersion": "3.9.5",
+ "_nodeVersion": "6.2.2",
+ "_npmUser": {
+ "name": "graingert",
+ "email": "tagrain@gmail.com"
+ },
+ "dist": {
+ "shasum": "c2e98d8fd134b0525a4d2cf58c95d9b583755112",
+ "tarball": "https://registry.npmjs.org/ng-infinite-scroll/-/ng-infinite-scroll-1.3.0.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "binarymuse",
+ "email": "michelle@michelletilley.net"
+ },
+ {
+ "name": "graingert",
+ "email": "tagrain@gmail.com"
+ },
+ {
+ "name": "sroze",
+ "email": "samuel.roze@gmail.com"
+ }
+ ],
+ "_npmOperationalInternal": {
+ "host": "packages-12-west.internal.npmjs.com",
+ "tmp": "tmp/ng-infinite-scroll-1.3.0.tgz_1467302375605_0.8192659560590982"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/ng-infinite-scroll/-/ng-infinite-scroll-1.3.0.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee b/catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee
new file mode 100644
index 0000000000..cf2f90fc19
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/src/infinite-scroll.coffee
@@ -0,0 +1,209 @@
+angular.module('infinite-scroll', [])
+ .value('THROTTLE_MILLISECONDS', null)
+ .directive 'infiniteScroll', [
+ '$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS',
+($rootScope, $window, $interval, THROTTLE_MILLISECONDS) ->
+ scope:
+ infiniteScroll: '&'
+ infiniteScrollContainer: '='
+ infiniteScrollDistance: '='
+ infiniteScrollDisabled: '='
+ infiniteScrollUseDocumentBottom: '=',
+ infiniteScrollListenForEvent: '@'
+
+ link: (scope, elem, attrs) ->
+ windowElement = angular.element($window)
+
+ scrollDistance = null
+ scrollEnabled = null
+ checkWhenEnabled = null
+ container = null
+ immediateCheck = true
+ useDocumentBottom = false
+ unregisterEventListener = null
+ checkInterval = false
+
+ height = (elem) ->
+ elem = elem[0] or elem
+
+ if isNaN(elem.offsetHeight) then elem.document.documentElement.clientHeight else elem.offsetHeight
+
+ offsetTop = (elem) ->
+ if not elem[0].getBoundingClientRect or elem.css('none')
+ return
+
+ elem[0].getBoundingClientRect().top + pageYOffset(elem)
+
+ pageYOffset = (elem) ->
+ elem = elem[0] or elem
+
+ if isNaN(window.pageYOffset) then elem.document.documentElement.scrollTop else elem.ownerDocument.defaultView.pageYOffset
+
+ # infinite-scroll specifies a function to call when the window,
+ # or some other container specified by infinite-scroll-container,
+ # is scrolled within a certain range from the bottom of the
+ # document. It is recommended to use infinite-scroll-disabled
+ # with a boolean that is set to true when the function is
+ # called in order to throttle the function call.
+ handler = ->
+ if container == windowElement
+ containerBottom = height(container) + pageYOffset(container[0].document.documentElement)
+ elementBottom = offsetTop(elem) + height(elem)
+ else
+ containerBottom = height(container)
+ containerTopOffset = 0
+ if offsetTop(container) != undefined
+ containerTopOffset = offsetTop(container)
+ elementBottom = offsetTop(elem) - containerTopOffset + height(elem)
+
+ if(useDocumentBottom)
+ elementBottom = height((elem[0].ownerDocument || elem[0].document).documentElement)
+
+ remaining = elementBottom - containerBottom
+ shouldScroll = remaining <= height(container) * scrollDistance + 1
+
+ if shouldScroll
+ checkWhenEnabled = true
+
+ if scrollEnabled
+ if scope.$$phase || $rootScope.$$phase
+ scope.infiniteScroll()
+ else
+ scope.$apply(scope.infiniteScroll)
+ else
+ if checkInterval then $interval.cancel checkInterval
+ checkWhenEnabled = false
+
+ # The optional THROTTLE_MILLISECONDS configuration value specifies
+ # a minimum time that should elapse between each call to the
+ # handler. N.b. the first call the handler will be run
+ # immediately, and the final call will always result in the
+ # handler being called after the `wait` period elapses.
+ # A slimmed down version of underscore's implementation.
+ throttle = (func, wait) ->
+ timeout = null
+ previous = 0
+ later = ->
+ previous = new Date().getTime()
+ $interval.cancel(timeout)
+ timeout = null
+ func.call()
+
+ return ->
+ now = new Date().getTime()
+ remaining = wait - (now - previous)
+ if remaining <= 0
+ $interval.cancel(timeout)
+ timeout = null
+ previous = now
+ func.call()
+ else timeout = $interval(later, remaining, 1) unless timeout
+
+ if THROTTLE_MILLISECONDS?
+ handler = throttle(handler, THROTTLE_MILLISECONDS)
+
+ scope.$on '$destroy', ->
+ container.unbind 'scroll', handler
+ if unregisterEventListener?
+ unregisterEventListener()
+ unregisterEventListener = null
+ if checkInterval
+ $interval.cancel checkInterval
+
+ # infinite-scroll-distance specifies how close to the bottom of the page
+ # the window is allowed to be before we trigger a new scroll. The value
+ # provided is multiplied by the container height; for example, to load
+ # more when the bottom of the page is less than 3 container heights away,
+ # specify a value of 3. Defaults to 0.
+ handleInfiniteScrollDistance = (v) ->
+ scrollDistance = parseFloat(v) or 0
+
+ scope.$watch 'infiniteScrollDistance', handleInfiniteScrollDistance
+ # If I don't explicitly call the handler here, tests fail. Don't know why yet.
+ handleInfiniteScrollDistance scope.infiniteScrollDistance
+
+ # infinite-scroll-disabled specifies a boolean that will keep the
+ # infnite scroll function from being called; this is useful for
+ # debouncing or throttling the function call. If an infinite
+ # scroll is triggered but this value evaluates to true, then
+ # once it switches back to false the infinite scroll function
+ # will be triggered again.
+ handleInfiniteScrollDisabled = (v) ->
+ scrollEnabled = !v
+ if scrollEnabled && checkWhenEnabled
+ checkWhenEnabled = false
+ handler()
+
+ scope.$watch 'infiniteScrollDisabled', handleInfiniteScrollDisabled
+ # If I don't explicitly call the handler here, tests fail. Don't know why yet.
+ handleInfiniteScrollDisabled scope.infiniteScrollDisabled
+
+ # use the bottom of the document instead of the element's bottom.
+ # This useful when the element does not have a height due to its
+ # children being absolute positioned.
+ handleInfiniteScrollUseDocumentBottom = (v) ->
+ useDocumentBottom = v
+
+ scope.$watch 'infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom
+ handleInfiniteScrollUseDocumentBottom scope.infiniteScrollUseDocumentBottom
+
+ # infinite-scroll-container sets the container which we want to be
+ # infinte scrolled, instead of the whole window. Must be an
+ # Angular or jQuery element, or, if jQuery is loaded,
+ # a jQuery selector as a string.
+ changeContainer = (newContainer) ->
+ if container?
+ container.unbind 'scroll', handler
+
+ container = newContainer
+ if newContainer?
+ container.bind 'scroll', handler
+
+ changeContainer windowElement
+
+ if scope.infiniteScrollListenForEvent
+ unregisterEventListener = $rootScope.$on scope.infiniteScrollListenForEvent, handler
+
+ handleInfiniteScrollContainer = (newContainer) ->
+ # TODO: For some reason newContainer is sometimes null instead
+ # of the empty array, which Angular is supposed to pass when the
+ # element is not defined
+ # (https://github.com/sroze/ngInfiniteScroll/pull/7#commitcomment-5748431).
+ # So I leave both checks.
+ if (not newContainer?) or newContainer.length == 0
+ return
+
+ if newContainer.nodeType && newContainer.nodeType == 1
+ newContainer = angular.element newContainer
+ else if typeof newContainer.append == 'function'
+ newContainer = angular.element newContainer[newContainer.length - 1]
+ else if typeof newContainer == 'string'
+ newContainer = angular.element document.querySelector newContainer
+
+ if newContainer?
+ changeContainer newContainer
+ else
+ throw new Error("invalid infinite-scroll-container attribute.")
+
+ scope.$watch 'infiniteScrollContainer', handleInfiniteScrollContainer
+ handleInfiniteScrollContainer(scope.infiniteScrollContainer or [])
+
+ # infinite-scroll-parent establishes this element's parent as the
+ # container infinitely scrolled instead of the whole window.
+ if attrs.infiniteScrollParent?
+ changeContainer angular.element elem.parent()
+
+ # infinte-scoll-immediate-check sets whether or not run the
+ # expression passed on infinite-scroll for the first time when the
+ # directive first loads, before any actual scroll.
+ if attrs.infiniteScrollImmediateCheck?
+ immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck)
+
+ checkInterval = $interval (->
+ if immediateCheck
+ handler()
+ $interval.cancel checkInterval
+ )
+]
+if typeof module != "undefined" && typeof exports != "undefined" && module.exports == exports
+ module.exports = 'infinite-scroll'
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js
new file mode 100644
index 0000000000..134c723f91
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-local.conf.js
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var config = require('./protractor-shared.conf').config;
+
+config.multiCapabilities = [
+ { browserName: 'chrome' }
+];
+
+exports.config = config;
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js
new file mode 100644
index 0000000000..d1e567d7b4
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-shared.conf.js
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+exports.config = {
+ specs: ['**/*.spec.coffee'],
+ baseUrl: 'http://localhost:8000/',
+ allScriptsTimeout: 30000,
+ getPageTimeout: 30000
+};
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js
new file mode 100644
index 0000000000..54fe0bd828
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/protractor-travis.conf.js
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var config = require('./protractor-shared.conf').config;
+
+// All available platform / browser combinations can be found on https://saucelabs.com/platforms
+config.multiCapabilities = [
+ {
+ browserName: 'chrome',
+ platform: 'OS X 10.10',
+ version: '37'
+ }
+];
+
+exports.config = config;
diff --git a/catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee b/catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee
new file mode 100644
index 0000000000..07e6fec8be
--- /dev/null
+++ b/catalog-ui/app/third-party/ng-infinite-scroll/test/spec/ng-infinite-scroll.spec.coffee
@@ -0,0 +1,221 @@
+fs = require "fs"
+mkdirp = require "mkdirp"
+
+getTemplate = (angularVersion, container, attrs, throttle) ->
+ """
+ <!doctype html>
+ <head>
+ <style>
+ html, body {
+ height: 100%;
+ }
+ </style>
+ <script src='http://ajax.googleapis.com/ajax/libs/angularjs/#{angularVersion}/angular.min.js'></script>
+ <script src="../../build/ng-infinite-scroll.js"></script>
+ <script>
+ angular.module('app', ['infinite-scroll'])
+ .config(function ($provide) {
+ $provide.value('THROTTLE_MILLISECONDS', #{throttle});
+ })
+ .run(function ($rootScope) {
+ $rootScope.items = [];
+ $rootScope.loadMore = function () {
+ [].push.apply($rootScope.items, new Array(100));
+ };
+
+ $rootScope.busy = true;
+
+ $rootScope.enable = function () {
+ $rootScope.busy = false;
+ };
+
+ $rootScope.triggerEvent = function () {
+ $rootScope.$emit('anEvent');
+ };
+ });
+ </script>
+ </head>
+ <body ng-app="app">
+ <a id="action" ng-click="enable()">Enable</a>
+ <a id="force" ng-click="loadMore()">Force</a>
+ <a id="trigger" ng-click="triggerEvent()">Trigger</a>
+ #{containers[container].start}
+ <div infinite-scroll="loadMore()" #{containers[container].attr} #{attrs}>
+ <p ng-repeat='item in items track by $index'>
+ {{$index}}
+ </p>
+ </div>
+ #{containers[container].end}
+ </body>
+ """
+
+containers =
+ window:
+ start: ""
+ end: ""
+ attr: ""
+ parent:
+ start: "<div id='parent' style='height: 50%; overflow: auto;'>"
+ end: "</div>"
+ attr: "infinite-scroll-parent"
+ ancestor:
+ start: "<div id='ancestor' style='height: 50%; overflow: auto;'><div>"
+ end: "</div></div>"
+ attr: "infinite-scroll-container='\"#ancestor\"'"
+
+getElementByIdScript = (id) ->
+ "document.getElementById('#{id}')"
+
+calculateChildrenHeightScript = (id) ->
+ """
+ [].concat.apply([], #{getElementByIdScript(id)}.childNodes)
+ .map(function (el) { return el.offsetHeight ? el.offsetHeight : 0; })
+ .reduce(function (cur, prev) { return prev + cur; }, 0)
+ """
+
+scrollToBottomScript = (container) ->
+ if container is "window"
+ "window.scrollTo(0, document.body.scrollHeight)"
+ else
+ "#{getElementByIdScript(container)}.scrollTop = #{calculateChildrenHeightScript(container)}"
+
+scrollToLastScreenScript = (container, offset) ->
+ # 2 * window.innerHeight means that the bottom of the screen should be somewhere close to
+ # body height - window height. That means that the top of the window is body height - 2 * window height.
+ if container is "window"
+ "window.scrollTo(0, document.body.scrollHeight - 2 * window.innerHeight + #{offset})"
+ else
+ """
+ #{getElementByIdScript(container)}.scrollTop =
+ #{calculateChildrenHeightScript(container)} - 2 * #{getElementByIdScript(container)}.offsetHeight + #{offset}
+ """
+
+collapseItemsScript = (container) ->
+ """
+ var items = document.getElementsByTagName('p')
+ for (i = 0; i < items.length; ++i) {
+ items[i].style.display = 'none'
+ }
+ """
+
+getItems = ->
+ element.all(By.repeater "item in items")
+
+tmpDir = ".tmp"
+pathToDocument = "#{tmpDir}/index.html"
+
+describe "ng-infinite-scroll", ->
+ for angularVersion in ["1.2.0", "1.3.4"]
+ describe "with Angular #{angularVersion}", ->
+ for container in ["window", "ancestor", "parent"]
+ describe "with #{container} as container", ->
+
+ replaceIndexFile = (attrs, throttle) ->
+ mkdirp tmpDir
+ fs.writeFileSync(pathToDocument, getTemplate(angularVersion, container, attrs, throttle))
+
+ describe "without throttling", ->
+
+ throttle = null
+
+ it "should be triggered immediately and when container is scrolled to the bottom", ->
+ replaceIndexFile "", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(scrollToBottomScript(container))
+ expect(getItems().count()).toBe 200
+
+ it "does not trigger immediately when infinite-scroll-immediate-check is false", ->
+ replaceIndexFile "infinite-scroll-immediate-check='false'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 0
+ element(By.id("force")).click()
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(scrollToBottomScript(container))
+ expect(getItems().count()).toBe 200
+
+ it "respects the disabled attribute", ->
+ replaceIndexFile "infinite-scroll-disabled='busy'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 0
+ element(By.id("action")).click()
+ expect(getItems().count()).toBe 100
+
+ it "respects the infinite-scroll-distance attribute", ->
+ replaceIndexFile "infinite-scroll-distance='1'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(scrollToLastScreenScript(container, -20))
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(scrollToLastScreenScript(container, 20))
+ expect(getItems().count()).toBe 200
+
+ describe "with an event handler", ->
+
+ it "calls the event handler on an event", ->
+ replaceIndexFile "infinite-scroll-listen-for-event='anEvent'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(collapseItemsScript(container))
+ expect(getItems().count()).toBe 100
+ element(By.id("trigger")).click()
+ expect(getItems().count()).toBe 200
+
+ describe "with throttling", ->
+
+ throttle = browser.params.testThrottleValue
+
+ it "should be triggered immediately and when container is scrolled to the bottom", ->
+ replaceIndexFile "", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(scrollToBottomScript(container))
+ expect(getItems().count()).toBe 100
+ browser.sleep(throttle)
+ expect(getItems().count()).toBe 200
+
+ it "does not trigger immediately when infinite-scroll-immediate-check is false", ->
+ replaceIndexFile "infinite-scroll-immediate-check='false'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 0
+ element(By.id("force")).click()
+ expect(getItems().count()).toBe 100
+
+ it "respects the disabled attribute and is throttled when page loads", ->
+ replaceIndexFile "infinite-scroll-disabled='busy'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 0
+ element(By.id("action")).click()
+ expect(getItems().count()).toBe 0
+ browser.sleep(throttle)
+ expect(getItems().count()).toBe 100
+
+ it "is not throttled when re-enabled if the throttle time has already elapsed", ->
+ replaceIndexFile "infinite-scroll-disabled='busy'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 0
+ browser.sleep(throttle)
+ element(By.id("action")).click()
+ expect(getItems().count()).toBe 100
+
+ it "respects the infinite-scroll-distance attribute", ->
+ replaceIndexFile "infinite-scroll-distance='1'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(scrollToLastScreenScript(container, 20))
+ expect(getItems().count()).toBe 100
+ browser.sleep(throttle)
+ expect(getItems().count()).toBe 200
+
+ describe "with an event handler", ->
+
+ it "calls the event handler on an event", ->
+ replaceIndexFile "infinite-scroll-listen-for-event='anEvent'", throttle
+ browser.get pathToDocument
+ expect(getItems().count()).toBe 100
+ browser.driver.executeScript(collapseItemsScript(container))
+ expect(getItems().count()).toBe 100
+ element(By.id("trigger")).click()
+ expect(getItems().count()).toBe 100
+ browser.sleep(throttle)
+ expect(getItems().count()).toBe 200
diff --git a/catalog-ui/bower dependecies b/catalog-ui/bower dependecies
new file mode 100644
index 0000000000..7a21bbcef7
--- /dev/null
+++ b/catalog-ui/bower dependecies
@@ -0,0 +1,29 @@
+{
+ "name": "catalog-ui",
+ "private": true,
+ "dependencies": {
+ "angular": "~1.3.x",
+ "angular-base64": "~2.0.5", //for translate base 64
+ "angular-base64-upload": "~0.1.8", //upload file base 64
+ "angular-bootstrap": "~0.13.0",
+ "angular-dragdrop": "~1.0.11", //for drag & drop elements in canvas
+ "angular-filter": "~0.5.6", // for filter categories
+ "angular-md5": "~0.1.7", //translate from & to md5
+ "angular-perfect-scrollbar": "~0.0.4", // fir design cross browser scrollbar
+ "angular-qtip2-directive": "~0.1.2", // for design tooltips
+ "angular-resource": "~1.3.15", // for restfully Api
+ "angular-sanitize": "~1.4.4", //https://docs.angularjs.org/api/ngSanitize/service/$sanitize
+ "angular-ui-router": "~0.2.14",
+ "angular-uuid4": "~0.3.0",
+ "bootstrap": "3.3.4", // dependency for angular bootstrap
+ "checklist-model": "~0.2.4", // for categories check list model
+ "jquery": "~1.11.1",
+ "lodash": "~3.7.0", // js utils functions
+ "angular-translate": "~2.8.0", // dependencies for language support
+ "angular-translate-loader-static-files": "~2.8.0" // for language support
+ },
+ "resolutions": {
+ "angular": "~1.3.x",
+ "jquery": "~2.0.3"
+ }
+}
diff --git a/catalog-ui/bower.json b/catalog-ui/bower.json
new file mode 100644
index 0000000000..d941f1620a
--- /dev/null
+++ b/catalog-ui/bower.json
@@ -0,0 +1,42 @@
+{
+ "name": "catalog-ui",
+ "private": true,
+ "dependencies": {
+ "angular": "1.5.0",
+ "angular-base64": "2.0.5",
+ "angular-base64-upload": "0.1.19",
+ "angular-bootstrap": "0.14.3",
+ "angular-dragdrop": "https://github.com/dimkinv/angular-dragdrop.git#1.0.14",
+ "angular-filter": "0.5.14",
+ "angular-mocks": "1.5.0",
+ "angular-perfect-scrollbar": "0.0.4",
+ "angular-resource": "1.5.11",
+ "angular-sanitize": "1.4.4",
+ "angular-tooltips": "0.1.23",
+ "angular-translate": "2.8.0",
+ "angular-translate-loader-static-files": "2.8.0",
+ "angular-ui-router": "0.2.14",
+ "angular-uuid4": "0.3.0",
+ "bootstrap": "3.3.4",
+ "checklist-model": "0.2.4",
+ "jquery": "2.2.4",
+ "jspdf": "1.0.272",
+ "animate.css": "~3.5.1",
+ "angular-clipboard": "1.5.0",
+ "ui-bootstrap": "1.3.3",
+ "angular-resizable": "1.2.0",
+ "angular-ui-notification": "0.2.0",
+ "js-md5": "md5#0.4.1",
+ "cytoscape": "^2.7.10",
+ "lodash": "^4.17.2",
+ "restangular": "^1.5.2",
+ "cytoscape-expand-collapse": "https://github.com/bardit/cytoscape.js-expand-collapse.git",
+ "cytoscape-qtip": "https://github.com/bardit/cytoscape.js-qtip.git",
+ "qtip2": "^2.2.1"
+ },
+ "resolutions": {
+ "angular": "1.5.0",
+ "jquery": "2.2.4",
+ "angular-translate": "2.8.1"
+ }
+}
diff --git a/catalog-ui/build_catalog_ui.bat b/catalog-ui/build_catalog_ui.bat
new file mode 100644
index 0000000000..7a153e9ae7
--- /dev/null
+++ b/catalog-ui/build_catalog_ui.bat
@@ -0,0 +1,29 @@
+
+rem nvmw use v0.12.4
+
+echo "npm install"
+call npm install
+
+if %errorlevel% NEQ 0 GOTO BAD_EXIT
+
+echo "bower install"
+call bower install
+if %errorlevel% NEQ 0 GOTO BAD_EXIT
+
+
+echo "build --v"
+grunt build --v
+
+if %errorlevel% NEQ 0 GOTO BAD_EXIT
+
+GOTO SMOOTH
+
+
+:BAD_EXIT
+echo BOO
+exit/ b 1
+
+
+:SMOOTH
+echo "OK."
+exit /b 0
diff --git a/catalog-ui/build_catalog_ui.sh b/catalog-ui/build_catalog_ui.sh
new file mode 100644
index 0000000000..8bae776893
--- /dev/null
+++ b/catalog-ui/build_catalog_ui.sh
@@ -0,0 +1,84 @@
+
+#!/bin/sh
+
+### Set the node environment.
+#NODE_VERSION="v6.2.2"
+
+#echo "Set the node environment."
+#. "$NVM_DIR/nvm.sh"
+#echo "OK."
+#echo ""
+
+
+
+### Add newer c++ compiler.
+#if [ -f /opt/rh/devtoolset-4/enable ]; then
+# . /opt/rh/devtoolset-4/enable
+#fi
+
+
+
+
+### Set the node version manager version.
+#echo "Set the node version manager version."
+#nvm use ${NODE_VERSION}
+#echo "OK."
+#echo ""
+
+
+### Run install bower.
+echo "Run install bower."
+#if [ -e $NVM_DIR/versions/node/${NODE_VERSION}/lib/node_modules/bower/bin/bower ]; then
+# echo " - bower is already installed."
+#else
+npm install bower
+#fi
+#echo "OK."
+#echo ""
+
+
+
+### Run install grunt-cli.
+echo "Run install grunt-cli."
+#if [ -e $NVM_DIR/versions/node/${NODE_VERSION}/lib/node_modules/grunt-cli/bin/grunt ]; then
+# echo " - grunt-cli is already installed."
+#else
+npm install grunt-cli
+#fi
+#echo "OK."
+#echo ""
+
+
+
+### Clean the Node cache.
+#echo "Clean the Node cache - if stuck."
+#npm cache clean
+#echo "OK."
+#echo ""
+
+
+
+### Run the Node package manager (NPM).
+echo "Run the Node package manager (NPM)."
+#npm config set proxy http://one.proxy.att.com:8080
+#npm config set https-proxy http://one.proxy.att.com:8080
+#npm config set registry https://registry.npmjs.org
+
+npm install
+echo "OK."
+echo ""
+
+
+
+### Install the Bower components.
+echo "Install the Bower components."
+bower install
+echo "OK."
+echo ""
+
+
+
+### Build the application.
+echo "Build the application."
+grunt build
+
diff --git a/catalog-ui/configurations/MenuReadMe.txt b/catalog-ui/configurations/MenuReadMe.txt
new file mode 100644
index 0000000000..660427e5c9
--- /dev/null
+++ b/catalog-ui/configurations/MenuReadMe.txt
@@ -0,0 +1,60 @@
+*******************************************************************
+******* Explanation about menu.json *******
+*******************************************************************
+
+The menu.json defines the menu to show for each type of "roles":
+
+Supported roles:
+-----------------------------
+ADMIN
+DESIGNER
+PRODUCT_STRATEGIST
+PRODUCT_MANAGER
+TESTER
+OPS
+GOVERNOR
+
+The JSON is separated to roles, and for each role we define "states",
+what menu to show the user for each state of the component:
+
+Supported states:
+-----------------------------
+NOT_CERTIFIED_CHECKOUT
+NOT_CERTIFIED_CHECKIN
+READY_FOR_CERTIFICATION
+CERTIFICATION_IN_PROGRESS
+CERTIFIED
+
+For each state we can define the user that will see this menu, the available parameters are:
+
+Supported users:
+-----------------------------
+ANY
+NOT_OWNER
+
+Example:
+For designer, if the component state is checkout and the component was created by other user, the NOT_OWNER will be used.
+
+"DESIGNER":{
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"Edit" ,"action":"goToEntity"},
+ {"text":"Check in","action":"changeLifecycleState", "url":"lifecycleState/CHECKIN", "confirmationModal": "lifecycleState/CHECKIN"},
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "NOT_OWNER":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+
+
+Definition of the menu item:
+-----------------------------
+text - The text to show
+action - Function that will be called when pressing on the menu item
+url - Data added to menu item, in case the function need to use it, example: for function "changeLifecycleState", I need to pass also the url "lifecycleState/CHECKOUT" that I want the state to change to.
+confirmationModal - Open confirmation modal (user should select "OK" or "Cancel"), and continue with the action.
+emailModal - Open email modal (user should fill email details), and continue with the action.
+blockedForTypes - This item will not be shown for specific components types.
diff --git a/catalog-ui/configurations/dev.json b/catalog-ui/configurations/dev.json
new file mode 100644
index 0000000000..4cc09d3c31
--- /dev/null
+++ b/catalog-ui/configurations/dev.json
@@ -0,0 +1,430 @@
+{
+ "environment": "dev",
+ "api": {
+ "GET_component": "/v1/catalog/:type/:id",
+ "PUT_component": "/v1/catalog/:type/:id/metadata",
+ "GET_component_validate_name": "/v1/catalog/:type/validate-name/:name",
+ "POST_changeLifecycleState": "/v1/catalog/",
+ "component_api_root": "/v1/catalog/",
+ "welcome_page_video_url": "http://0.0.0.0:8282/moti/movie",
+
+ "GET_user": "/v1/user/:id",
+ "GET_user_authorize": "/v1/user/authorize",
+ "GET_all_users": "/v1/user/users",
+ "POST_create_user": "/v1/user",
+ "DELETE_delete_user": "/v1/user/:id",
+ "POST_edit_user_role": "/v1/user/:id/role",
+ "GET_resource": "/v1/catalog/resources/:id",
+ "GET_resources_latestversion_notabstract":"/v1/catalog/:type/latestversion/notabstract/:id",
+ "GET_resources_certified_not_abstract": "/v1/catalog/resources/certified/notabstract/:id",
+ "GET_resources_certified_abstract": "/v1/catalog/resources/certified/abstract/:id",
+ "GET_resource_property": "/v1/catalog/:type/:entityId/properties/:id",
+ "PUT_resource": "/v1/catalog/resources/:id/metadata",
+ "GET_resource_artifact": "/v1/catalog/:type/:entityId/artifacts/:id",
+ "GET_download_instance_artifact": "/v1/catalog/:type/:entityId/resourceInstances/:instanceId/artifacts/:id",
+ "POST_instance_artifact": "/v1/catalog/:type/:entityId/resourceInstance/:instanceId/artifacts/:id",
+ "GET_resource_additional_information": "/v1/catalog/:type/:entityId/additionalinfo/:id",
+ "GET_service_artifact": "/v1/catalog/services/:serviceId/artifacts/:id",
+ "GET_resource_interface_artifact": "/v1/catalog/:type/:entityId/standard/:operation/artifacts/:id",
+ "GET_resource_api_artifact": "/v1/catalog/:type/:entityId/artifacts/api/:id",
+ "GET_configuration_ui": "/v1/configuration/ui",
+ "GET_resource_validate_name": "/v1/catalog/resources/validate-name/:name",
+ "GET_activity_log": "/v1/catalog/audit-records/:type/:id",
+ "GET_service": "/v1/catalog/services/:id",
+ "GET_service_validate_name": "/v1/catalog/services/validate-name/:name",
+ "GET_service_distributions":"/v1/catalog/services/:uuid/distribution",
+ "GET_service_distributions_components":"/v1/catalog/services/distribution/:distributionId",
+ "POST_service_distribution_deploy" : "/v1/catalog/services/:serviceId/distribution/:distributionId/markDeployed",
+ "GET_element": "/v1/followed",
+ "GET_catalog": "/v1/screen",
+ "GET_ecomp_menu_items": "/v1/user/:userId/functionalmenu",
+ "GET_resource_category": "/v1/resourceCategories",
+ "GET_service_category": "/v1/serviceCategories",
+ "resource_instance": "/v1/catalog/:entityType/:entityId/resourceInstance/:id",
+ "GET_resource_instance_property": "/v1/catalog/:type/:entityId/resourceInstance/:componentInstanceId/property/:propertyValueId",
+ "GET_relationship": "/v1/catalog/:entityType/:entityId/resourceInstance/:action",
+ "GET_lifecycle_state_resource": "/v1/catalog/:type/:id/lifecycleState/:action",
+ "GET_lifecycle_state_CHECKIN":"lifecycleState/CHECKIN",
+ "GET_lifecycle_state_CERTIFICATIONREQUEST":"lifecycleState/CERTIFICATIONREQUEST",
+ "GET_lifecycle_state_UNDOCHECKOUT":"lifecycleState/UNDOCHECKOUT",
+ "root": "http://feHost:8181/sdc1/feProxy/rest",
+ "PUT_service": "/v1/catalog/services/:id/metadata",
+ "GET_download_artifact": "/v1/catalog/",
+ "GET_SDC_Version": "/version",
+ "GET_categories": "/v1/categories/:types",
+ "POST_category": "/v1/category/:types/:categoryId",
+ "POST_subcategory": "/v1/category/:types/:categoryId/subCategory/:subCategoryId",
+ "POST_change_instance_version": "/v1/catalog/:entityType/:entityId/resourceInstance/:id/changeVersion",
+ "GET_requirements_capabilities": "/v1/catalog/requirmentsCapabilities/:type/:id",
+ "GET_resource_artifact_types": "/v1/artifactTypes",
+ "GET_product_catalog": "/v1/productScreen",
+ "GET_product_category": "/v1/productCategories",
+ "GET_product_category_temp": "/v1/artifactTypes",
+ "POST_product": "/v1/catalog/products/:id/metadata",
+ "GET_product_validate_name": "/v1/catalog/services/validate-name/:name",
+ "GET_product": "/v1/catalog/products/:id",
+ "GET_product_sub_category": "/v1/productSubCategories",
+ "GET_onboarding": "http://fehost:8181/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/packages",
+ "GET_component_from_csar_uuid": "/v1/catalog/resources/csar/:csar_uuid",
+ "kibana": "/sdc1/kibanaProxy/"
+ },
+ "resourceTypesFilter":{
+ "resource":["CP","VFC"],
+ "service":["CP","VF"],
+ "product":[]
+ },
+ "logConfig": {
+ "minLogLevel": "debug",
+ "prefix": "sdcApp"
+ },
+ "cookie": {
+ "junctionName": "IV_JCT",
+ "prefix": "AMWEBJCT!",
+ "userIdSuffix": "USER_ID",
+ "userFirstName": "HTTP_CSP_FIRSTNAME",
+ "userLastName": "HTTP_CSP_LASTNAME",
+ "userEmail": "HTTP_CSP_EMAIL",
+ "xEcompRequestId": " X-ECOMP-RequestID"
+ },
+ "imagesPath": "",
+ "cpEndPointInstances" : ["cloudep","ossep","personep","premisesep"],
+ "toscaFileExtension":"yaml,yml",
+ "csarFileExtension":"csar",
+ "openSource": true,
+ "categories": {},
+ "testers": {
+ "RESOURCE": {
+ "Network L2-3": "DL-ASDCL1-3ResourceCertificationTeam",
+ "Network L4+": "DL-ASDCL4-7ResourceCertificationTeam",
+ "Application L4+": "DL-ASDCL4-7ResourceCertificationTeam",
+ "default": "DL-ASDCL1-3ResourceCertificationTeam;DL-ASDCL4-7ResourceCertificationTeam"
+ },
+ "SERVICE": {
+ "Network L1-3": "DL-ASDCL1-4ServiceCertificationTeam",
+ "Network L4+": "DL-ASDCL4-7ServiceCertificationTeam",
+ "default": "DL-ASDCL1-4ServiceCertificationTeam;DL-ASDCL4-7ServiceCertificationTeam"
+ }
+ },
+ "roles": ["ADMIN", "TESTER", "GOVERNOR", "OPS", "DESIGNER", "PRODUCT_MANAGER", "PRODUCT_STRATEGIST"],
+ "tutorial": {
+ "tabs": [
+ {
+ "id":1,
+ "name":"TUTRIAL_GENERAL_TAB_1",
+ "defaultPage":1
+ },
+ {
+ "id":2,
+ "name":"TUTRIAL_GENERAL_TAB_2",
+ "defaultPage":9
+ },
+ {
+ "id":3,
+ "name":"TUTRIAL_GENERAL_TAB_3",
+ "defaultPage":12
+ }
+ ],
+ "pages":
+ [
+ {
+ "id":1,
+ "template": "text-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE1_TITLE",
+ "description":"TUTORIAL_PAGE1_TEXT"
+ }
+
+ },
+ {
+ "id":2,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE2_TITLE",
+ "description":"TUTORIAL_PAGE2_TEXT",
+ "imageClass":"sdc-tutorial-page-2-image"
+ }
+ },
+ {
+ "id":3,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE3_TITLE",
+ "description":"TUTORIAL_PAGE3_TEXT",
+ "imageClass":"sdc-tutorial-page-3-image"
+ }
+ },
+ {
+ "id":4,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE4_TITLE",
+ "description":"TUTORIAL_PAGE4_TEXT",
+ "imageClass":"sdc-tutorial-page-4-image"
+ }
+ },
+ {
+ "id":5,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE5_TITLE",
+ "description":"TUTORIAL_PAGE5_TEXT",
+ "imageClass":"sdc-tutorial-page-5-image"
+ }
+ },
+ {
+ "id":6,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE6_TITLE",
+ "description":"TUTORIAL_PAGE6_TEXT",
+ "imageClass":"sdc-tutorial-page-6-image"
+ }
+ },
+ {
+ "id":7,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE7_TITLE",
+ "description":"TUTORIAL_PAGE7_TEXT",
+ "imageClass":"sdc-tutorial-page-7-image"
+ }
+ },
+ {
+ "id":8,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE8_TITLE",
+ "description":"TUTORIAL_PAGE8_TEXT",
+ "imageClass":"sdc-tutorial-page-8-image"
+ }
+ },
+ {
+ "id":9,
+ "template": "text-template",
+ "tab": 2,
+ "data":{
+ "title":"TUTORIAL_PAGE9_TITLE",
+ "description":"TUTORIAL_PAGE9_TEXT"
+ }
+ },
+ {
+ "id":10,
+ "template": "image-template",
+ "tab": 2,
+ "data":{
+ "title":"TUTORIAL_PAGE10_TITLE",
+ "description":"TUTORIAL_PAGE10_TEXT",
+ "imageClass":"sdc-tutorial-page-10-image"
+ }
+ },
+ {
+ "id":11,
+ "template": "image-template",
+ "tab": 2,
+ "data":{
+ "title":"TUTORIAL_PAGE11_TITLE",
+ "description":"TUTORIAL_PAGE11_TEXT",
+ "imageClass":"sdc-tutorial-page-11-image"
+ }
+ },
+ {
+ "id":12,
+ "template": "text-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE12_TITLE",
+ "description":"TUTORIAL_PAGE12_TEXT"
+ }
+ },
+ {
+ "id":13,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE13_TITLE",
+ "description":"TUTORIAL_PAGE13_TEXT",
+ "imageClass":"sdc-tutorial-page-13-image"
+ }
+ },
+ {
+ "id":14,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE14_TITLE",
+ "description":"TUTORIAL_PAGE14_TEXT",
+ "imageClass":"sdc-tutorial-page-14-image"
+ }
+ },
+ {
+ "id":15,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE15_TITLE",
+ "description":"TUTORIAL_PAGE15_TEXT",
+ "imageClass":"sdc-tutorial-page-15-image"
+ }
+ },
+ {
+ "id":16,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE16_TITLE",
+ "description":"TUTORIAL_PAGE16_TEXT",
+ "imageClass":"sdc-tutorial-page-16-image"
+ }
+ },
+ {
+ "id":17,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE17_TITLE",
+ "description":"TUTORIAL_PAGE17_TEXT",
+ "imageClass":"sdc-tutorial-page-17-image"
+ }
+ },
+ {
+ "id":18,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE18_TITLE",
+ "description":"TUTORIAL_PAGE18_TEXT",
+ "imageClass":"sdc-tutorial-page-18-image"
+ }
+ },
+ {
+ "id":19,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE19_TITLE",
+ "description":"TUTORIAL_PAGE19_TEXT",
+ "imageClass":"sdc-tutorial-page-19-image"
+ }
+ },
+ {
+ "id":20,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE20_TITLE",
+ "description":"TUTORIAL_PAGE20_TEXT",
+ "imageClass":"sdc-tutorial-page-20-image"
+ }
+ },
+ {
+ "id":21,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE21_TITLE",
+ "description":"TUTORIAL_PAGE21_TEXT",
+ "imageClass":"sdc-tutorial-page-21-image"
+ }
+ },
+ {
+ "id":22,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE22_TITLE",
+ "description":"TUTORIAL_PAGE22_TEXT",
+ "imageClass":"sdc-tutorial-page-22-image"
+ }
+ },
+ {
+ "id":23,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE23_TITLE",
+ "description":"TUTORIAL_PAGE23_TEXT",
+ "imageClass":"sdc-tutorial-page-23-image"
+ }
+ },
+ {
+ "id":24,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE24_TITLE",
+ "description":"TUTORIAL_PAGE24_TEXT",
+ "imageClass":"sdc-tutorial-page-24-image"
+ }
+ },
+ {
+ "id":25,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE25_TITLE",
+ "description":"TUTORIAL_PAGE25_TEXT",
+ "imageClass":"sdc-tutorial-page-25-image"
+ }
+ },
+ {
+ "id":26,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE26_TITLE",
+ "description":"TUTORIAL_PAGE26_TEXT",
+ "imageClass":"sdc-tutorial-page-26-image"
+ }
+ },
+ {
+ "id":27,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE27_TITLE",
+ "description":"TUTORIAL_PAGE27_TEXT",
+ "imageClass":"sdc-tutorial-page-27-image"
+ }
+ },
+ {
+ "id":28,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE28_TITLE",
+ "description":"TUTORIAL_PAGE28_TEXT",
+ "imageClass":"sdc-tutorial-page-28-image"
+ }
+ },
+ {
+ "id":29,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE29_TITLE",
+ "description":"TUTORIAL_PAGE29_TEXT",
+ "imageClass":"sdc-tutorial-page-29-image"
+ }
+ },
+ {
+ "id":30,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE30_TITLE",
+ "description":"TUTORIAL_PAGE30_TEXT",
+ "imageClass":"sdc-tutorial-page-30-image"
+ }
+ }
+
+ ]
+ }
+}
diff --git a/catalog-ui/configurations/menu.json b/catalog-ui/configurations/menu.json
new file mode 100644
index 0000000000..cd451e79f1
--- /dev/null
+++ b/catalog-ui/configurations/menu.json
@@ -0,0 +1,576 @@
+{
+ "roles":{
+ "ADMIN":{
+ "title": "Admin's Workspace",
+ "pages":[],
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"lifecycleState/certificationRequest" , "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "Submit for Testing", "action": "changeLifecycleState", "url": "lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ }
+ },
+ "DESIGNER":{
+ "title": "Designer's Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": true,
+ "showCreateNewProduct": false
+ },
+ "changeLifecycleStateButtons":{
+ "NOT_CERTIFIED_CHECKOUT":{ "submitForTesting": {"text":"Submit for Testing", "url":"lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ "checkIn": {"text":"Check in", "url":"lifecycleState/CHECKIN", "confirmationModal": "lifecycleState/CHECKIN"},
+ "deleteVersion": {"text":"Delete Version", "url":"lifecycleState/UNDOCHECKOUT", "alertModal": "lifecycleState/UNDOCHECKOUT"}
+ },
+ "CERTIFIED":{ "checkOut": {"text":"Check Out", "url":"lifecycleState/CHECKOUT"}
+ },
+ "NOT_CERTIFIED_CHECKIN":{ "submitForTesting": {"text":"Submit for Testing", "url":"lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ "checkOut": {"text":"Check Out", "url":"lifecycleState/CHECKOUT"}
+ }
+ },
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "Submit for Testing", "action": "changeLifecycleState", "url": "lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "IN_PROGRESS" },
+ {"text": "Check Out", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKOUT"},
+ {"text": "Check In", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKIN"},
+ {"text": "Followed Projects", "groupname": "FOLLOWING" },
+ {"text": "Ready For Testing", "group": "FOLLOWING", "state": "READY_FOR_CERTIFICATION"},
+ {"text": "In Testing", "group": "FOLLOWING", "state": "CERTIFICATION_IN_PROGRESS"},
+ {"text": "Certified", "group": "FOLLOWING", "state": "CERTIFIED"}
+ ]
+
+ },
+ "PRODUCT_STRATEGIST":{
+ "title": "Product Strategist's Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": false,
+ "showCreateNewProduct": false
+ },
+ "changeLifecycleStateButtons":{},
+
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "IN_PROGRESS" },
+ {"text": "Check Out", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKOUT"},
+ {"text": "Check In", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKIN"},
+ {"text": "Followed Projects", "groupname": "FOLLOWING" },
+ {"text": "Ready For Testing", "group": "FOLLOWING", "state": "READY_FOR_CERTIFICATION"},
+ {"text": "In Testing", "group": "FOLLOWING", "state": "CERTIFICATION_IN_PROGRESS"},
+ {"text": "Certified", "group": "FOLLOWING", "state": "CERTIFIED"}
+ ]
+ },
+ "PRODUCT_MANAGER":{
+ "title": "Product Manager Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": false,
+ "showCreateNewProduct": true
+ },
+ "changeLifecycleStateButtons":{
+ "NOT_CERTIFIED_CHECKOUT":{ "checkIn": {"text":"Check in", "url":"lifecycleState/CHECKIN", "confirmationModal": "lifecycleState/CHECKIN"},
+ "deleteVersion":{"text":"Delete Version", "url":"lifecycleState/UNDOCHECKOUT", "alertModal": "lifecycleState/UNDOCHECKOUT"}
+ },
+ "NOT_CERTIFIED_CHECKIN":{ "checkOut": {"text":"Check Out", "url":"lifecycleState/CHECKOUT"}
+ }
+ },
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "IN_PROGRESS" },
+ {"text": "Check Out", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKOUT"},
+ {"text": "Check In", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKIN"},
+ {"text": "Followed Projects", "groupname": "FOLLOWING" },
+ {"text": "Ready For Testing", "group": "FOLLOWING", "state": "READY_FOR_CERTIFICATION"},
+ {"text": "In Testing", "group": "FOLLOWING", "state": "CERTIFICATION_IN_PROGRESS"},
+ {"text": "Certified", "group": "FOLLOWING", "state": "CERTIFIED"}
+ ]
+ },
+ "TESTER":{
+ "title": "Tester's Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": false,
+ "showCreateNewProduct": false
+ },
+ "changeLifecycleStateButtons":{
+ "READY_FOR_CERTIFICATION":{ "startTesting": {"text":"Start Testing", "url":"lifecycleState/startCertification"}
+
+ },
+ "CERTIFICATION_IN_PROGRESS":{ "accept": {"text":"Accept", "url":"lifecycleState/certify", "confirmationModal": "lifecycleState/certify"},
+ "reject": {"text":"Reject", "url":"lifecycleState/failCertification", "confirmationModal": "lifecycleState/failCertification"},
+ "cancel": {"text":"Cancel","action":"changeLifecycleState", "url":"lifecycleState/cancelCertification", "confirmationModal": "lifecycleState/cancel"}
+ }
+ },
+ "states": {
+ "READY_FOR_CERTIFICATION":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"},
+ {"text":"Start Testing","action":"changeLifecycleState", "url":"lifecycleState/startCertification"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"},
+ {"text":"Accept","action":"changeLifecycleState", "url":"lifecycleState/certify", "confirmationModal": "lifecycleState/certify"},
+ {"text":"Reject","action":"changeLifecycleState", "url":"lifecycleState/failCertification", "confirmationModal": "lifecycleState/failCertification"},
+ {"text":"Cancel","action":"changeLifecycleState", "url":"lifecycleState/cancelCertification", "confirmationModal": "lifecycleState/cancel" }
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "FOLLOWING" },
+ {"text": "Ready For Testing", "group": "FOLLOWING", "state": "READY_FOR_CERTIFICATION"},
+ {"text": "In Testing", "group": "FOLLOWING", "state": "CERTIFICATION_IN_PROGRESS"}
+ ]
+ },
+ "OPS":{
+ "title": "Operations Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": false,
+ "showCreateNewProduct": false
+ },
+ "changeLifecycleStateButtons":{
+ "DISTRIBUTION_APPROVED":{ "distribute": {"text":"Distribute", "url":"distribution/PROD/activate"},
+ "monitor": {"text":"Monitor", "disabled":true}
+ },
+ "DISTRIBUTED":{ "redistribute": {"text":"Redistribute", "url":"distribution/PROD/activate"},
+ "monitor": {"text":"Monitor", "url":"distribution-state/monitor"}
+ }
+ },
+ "states": {
+ "CERTIFIED": {
+ "DISTRIBUTION_APPROVED": [
+ {"text": "View","action": "openViewerModal"},
+ {"text": "Distribute","action":"changeLifecycleState", "url":"distribution/PROD/activate"}
+ ],
+ "DISTRIBUTED": [
+ {"text": "View","action": "openViewerModal"},
+ {"text": "Monitor","action":"openDistributionModal", "url":"distribution-state/monitor"},
+ {"text": "Redistribute","action":"changeLifecycleState", "url":"distribution/PROD/activate"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "FOLLOWING" },
+ {"text": "Waiting For Distribution", "group": "FOLLOWING", "state": "CERTIFIED", "dist": "DISTRIBUTION_APPROVED"},
+ {"text": "Distributed", "group": "FOLLOWING", "state": "CERTIFIED", "dist": "DISTRIBUTED"}
+ ]
+ },
+ "GOVERNOR":{
+ "title": "Governance Rep's Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": false,
+ "showCreateNewProduct": false
+ },
+ "changeLifecycleStateButtons":{
+ "DISTRIBUTION_NOT_APPROVED":{ "approve": {"text":"Approve", "url":"distribution-state/approve", "confirmationModal": "distribution-state/approve"},
+ "reject": {"text":"Reject", "url":"distribution-state/reject", "confirmationModal": "distribution-state/reject"}
+ },
+ "DISTRIBUTION_APPROVED":{ "reject": {"text":"Reject", "url":"distribution-state/reject", "confirmationModal": "distribution-state/reject"}
+ },
+ "DISTRIBUTED": { "reject": {"text":"Reject", "url":"distribution-state/reject", "confirmationModal": "distribution-state/reject"}
+ },
+ "DISTRIBUTION_REJECTED": { "approve": {"text": "Approve", "url": "distribution-state/approve", "confirmationModal": "distribution-state/approve"}
+ }
+ },
+ "states": {
+ "CERTIFIED": {
+ "DISTRIBUTION_NOT_APPROVED": [
+ {"text": "View","action": "openViewerModal"},
+ {"text":"Approve","action":"changeLifecycleState", "url":"distribution-state/approve", "confirmationModal": "distribution-state/approve"},
+ {"text":"Reject","action":"changeLifecycleState", "url":"distribution-state/reject", "confirmationModal": "distribution-state/reject"}
+ ],
+ "DISTRIBUTION_APPROVED": [
+ {"text": "View","action": "openViewerModal"},
+ {"text":"Reject","action":"changeLifecycleState", "url":"distribution-state/reject", "confirmationModal": "distribution-state/reject"}
+ ],
+ "DISTRIBUTED": [
+ {"text": "View","action": "openViewerModal"},
+ {"text":"Reject","action":"changeLifecycleState", "url":"distribution-state/reject", "confirmationModal": "distribution-state/reject"}
+ ],
+ "DISTRIBUTION_REJECTED": [
+ {"text": "View","action": "openViewerModal"},
+ {"text":"Approve","action":"changeLifecycleState", "url":"distribution-state/approve", "confirmationModal": "distribution-state/approve"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "FOLLOWING" },
+ {"text": "Waiting For Approval", "group": "FOLLOWING", "state": "CERTIFIED", "dist": "DISTRIBUTION_NOT_APPROVED"},
+ {"text": "Distribution Rejected", "group": "FOLLOWING", "state": "CERTIFIED", "dist": "DISTRIBUTION_REJECTED"},
+ {"text": "Distribution Approved", "group": "FOLLOWING", "state": "CERTIFIED", "dist": "DISTRIBUTION_APPROVED,DISTRIBUTED"}
+
+ ]
+ }
+ },
+ "confirmationMessages": {
+ "lifecycleState/CHECKIN": {"showComment":true, "title": "Check in confirmation", "message": "Please add comment and confirm the check in."},
+ "lifecycleState/CHECKOUT": {"showComment":true, "title": "Check out confirmation", "message": "Please add comment and confirm the check out."},
+ "lifecycleState/certify": {"showComment":true, "title": "Distribution confirmation", "message": "Please add comment and confirm test results."},
+ "lifecycleState/cancel": {"showComment":true, "title": "Cancel test", "message": "Please add comment and cancel test."},
+ "lifecycleState/failCertification": {"showComment":true, "title": "Rejection confirmation", "message": "Please add comment and confirm test results."},
+ "lifecycleState/CERTIFICATIONREQUEST": {"showComment":true, "title": "Submit for testing", "message": "Please add comment and submit for testing."},
+ "distribution-state/approve": {"showComment":true, "title": "Distribution confirmation", "message": "Please add comment and confirm %1 approval for distribution."},
+ "distribution-state/reject": {"showComment":true, "title": "Rejection confirmation", "message": "Please add comment and confirm %1 rejection for distribution."},
+ "updateTemplate": {"showComment":false, "title": "Update Template Confirmation", "message": "Modifying the Template might cause losing of previous information"}
+ },
+ "alertMessages": {
+ "lifecycleState/UNDOCHECKOUT": {"title": "Delete Version Confirmation", "message": "Are you sure you want to delete this version?"},
+ "exitWithoutSaving": {"title": "Exit Without Saving Confirmation", "message": "All unsaved changes will be lost. Are you sure you want to exit this page?"},
+ "deleteInstance": {"title": "Delete Confirmation", "message": "Are you sure you would like to delete %1 ?"},
+ "deleteInput": {"title": "Delete Confirmation", "message": "Are you sure you would like to delete %1 ?"}
+ },
+ "statuses": {
+ "inDesign": {
+ "name": "In Design",
+ "values": [
+ "NOT_CERTIFIED_CHECKOUT",
+ "NOT_CERTIFIED_CHECKIN"
+ ]
+ },
+ "readyForCertification": {
+ "name": "Ready For Testing",
+ "values": ["READY_FOR_CERTIFICATION"]
+ },
+ "inCertification": {
+ "name": "In Testing",
+ "values": ["CERTIFICATION_IN_PROGRESS"]
+ },
+ "certified": {
+ "name": "Certified",
+ "values": ["CERTIFIED"]
+ },
+ "distributed": {
+ "name": "Distributed",
+ "values": ["DISTRIBUTED"]
+ }
+ },
+ "categoriesDictionary": {
+ "Mobility": "Application Layer 4+",
+ "Network L1-3": "Network Layer 2-3",
+ "Network L4": "Network Layer 4+",
+ "VoIP Call Control": "Application Layer 4+"
+ },
+ "catalogMenuItem":{
+ "DESIGNER":{
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "NOT_OWNER":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "Submit for Testing", "action": "changeLifecycleState", "url": "lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+
+ }
+ },
+ "PRODUCT_MANAGER":{
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "NOT_OWNER":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "IN_PROGRESS" },
+ {"text": "Check Out", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKOUT"},
+ {"text": "Check In", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKIN"},
+ {"text": "Followed Projects", "groupname": "FOLLOWING" },
+ {"text": "Ready For Testing", "group": "FOLLOWING", "state": "READY_FOR_CERTIFICATION"},
+ {"text": "In Testing", "group": "FOLLOWING", "state": "CERTIFICATION_IN_PROGRESS"},
+ {"text": "Certified", "group": "FOLLOWING", "state": "CERTIFIED"}
+ ]
+ },
+ "PRODUCT_STRATEGIST":{
+ "title": "Product Strategist's Workspace",
+ "pages":[],
+ "dashboard": {
+ "showCreateNew": false,
+ "showCreateNewProduct": true
+ },
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "NOT_OWNER":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "NOT_CERTIFIED_CHECKIN": {
+ "ANY": [
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "READY_FOR_CERTIFICATION": {
+ "ANY":[
+ {"text": "View","action": "openViewerModal"}
+ ]
+ },
+ "CERTIFICATION_IN_PROGRESS":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ },
+ "CERTIFIED":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ },
+ "folder":[
+ {"text": "Active Projects", "groupname": "IN_PROGRESS" },
+ {"text": "Check Out", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKOUT"},
+ {"text": "Check In", "group": "IN_PROGRESS", "state": "NOT_CERTIFIED_CHECKIN"},
+ {"text": "Followed Projects", "groupname": "FOLLOWING" },
+ {"text": "Ready For Testing", "group": "FOLLOWING", "state": "READY_FOR_CERTIFICATION"},
+ {"text": "In Testing", "group": "FOLLOWING", "state": "CERTIFICATION_IN_PROGRESS"},
+ {"text": "Certified", "group": "FOLLOWING", "state": "CERTIFIED"}
+ ]
+ },
+ "OTHER":{
+ "states":{
+ "ANY":{
+ "ANY":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+ }
+ }
+ },
+ "LifeCycleStatuses":{
+ "NOT_CERTIFIED_CHECKOUT": { "text":"In Design Check Out", "icon": "checkout-editable-status-icon"},
+ "NOT_CERTIFIED_CHECKIN" : { "text":"In Design Check In", "icon": "checkin-status-icon "},
+ "READY_FOR_CERTIFICATION": { "text":"Ready for testing"},
+ "CERTIFICATION_IN_PROGRESS": { "text":"In Testing"},
+ "CERTIFIED": { "text":"Certified", "icon": "checkin-status-icon "}
+ },
+ "DistributionStatuses":{
+ "DISTRIBUTION_NOT_APPROVED": { "text":"Waiting For Distribution"},
+ "DISTRIBUTION_APPROVED" : { "text":"Distribution Approved"},
+ "DISTRIBUTION_REJECTED": { "text":"Distribution Rejected"},
+ "DISTRIBUTED": { "text":"Distributed"}
+ },
+ "canvas_buttons":{
+ "checkIn": {"text":"Check in","action":"changeLifecycleState", "url":"lifecycleState/CHECKIN", "confirmationModal": "lifecycleState/CHECKIN"},
+ "submitForTesting": {"text":"Submit for Testing","action":"changeLifecycleState", "url":"lifecycleState/certificationRequest", "emailModal": "lifecycleState/CERTIFICATIONREQUEST"},
+ "deleteVersion": {"text":"Delete Version", "action":"changeLifecycleState", "url":"lifecycleState/UNDOCHECKOUT", "alertModal": "lifecycleState/UNDOCHECKOUT"}
+ },
+
+ "component_workspace_menu_option": {
+ "VFC":[
+ {"text":"General", "action":"onMenuItemPressed", "state": "workspace.general"},
+ {"text":"Icon", "action":"onMenuItemPressed", "state": "workspace.icons"},
+ {"text":"Information Artifact", "action":"onMenuItemPressed", "state": "workspace.information_artifacts"},
+ {"text":"TOSCA Artifacts", "action":"onMenuItemPressed", "state": "workspace.tosca_artifacts"},
+ {"text":"Properties", "action":"onMenuItemPressed", "state": "workspace.properties"},
+ {"text":"Attributes", "action":"onMenuItemPressed", "state": "workspace.attributes"},
+ {"text":"Req. & Capabilities", "action":"onMenuItemPressed", "state": "workspace.reqAndCap"},
+ {"text":"Activity Log", "action":"onMenuItemPressed", "state": "workspace.activity_log"}
+ ],
+ "VL":[
+ {"text":"General", "action":"onMenuItemPressed", "state": "workspace.general"},
+ {"text":"Icon", "action":"onMenuItemPressed", "state": "workspace.icons"},
+ {"text":"Information Artifact", "action":"onMenuItemPressed", "state": "workspace.information_artifacts"},
+ {"text":"TOSCA Artifacts", "action":"onMenuItemPressed", "state": "workspace.tosca_artifacts"},
+ {"text":"Properties", "action":"onMenuItemPressed", "state": "workspace.properties"},
+ {"text":"Attributes", "action":"onMenuItemPressed", "state": "workspace.attributes"},
+ {"text":"Req. & Capabilities", "action":"onMenuItemPressed", "state": "workspace.reqAndCap"},
+ {"text":"Activity Log", "action":"onMenuItemPressed", "state": "workspace.activity_log"}
+ ],
+ "CP":[
+ {"text":"General", "action":"onMenuItemPressed", "state": "workspace.general"},
+ {"text":"Icon", "action":"onMenuItemPressed", "state": "workspace.icons"},
+ {"text":"Information Artifact", "action":"onMenuItemPressed", "state": "workspace.information_artifacts"},
+ {"text":"TOSCA Artifacts", "action":"onMenuItemPressed", "state": "workspace.tosca_artifacts"},
+ {"text":"Properties", "action":"onMenuItemPressed", "state": "workspace.properties"},
+ {"text":"Attributes", "action":"onMenuItemPressed", "state": "workspace.attributes"},
+ {"text":"Req. & Capabilities", "action":"onMenuItemPressed", "state": "workspace.reqAndCap"},
+ {"text":"Activity Log", "action":"onMenuItemPressed", "state": "workspace.activity_log"}
+ ],
+ "VF":[
+ {"text":"General", "action":"onMenuItemPressed", "state": "workspace.general"},
+ {"text":"Icon", "action":"onMenuItemPressed", "state": "workspace.icons"},
+ {"text":"Deployment Artifact", "action":"onMenuItemPressed", "state": "workspace.deployment_artifacts"},
+ {"text":"Information Artifact", "action":"onMenuItemPressed", "state": "workspace.information_artifacts"},
+ {"text":"TOSCA Artifacts", "action":"onMenuItemPressed", "state": "workspace.tosca_artifacts"},
+ {"text":"Properties", "action":"onMenuItemPressed", "state": "workspace.properties"},
+ {"text":"Composition", "action":"onMenuItemPressed", "state": "workspace.composition.details"},
+ {"text":"Activity Log", "action":"onMenuItemPressed", "state": "workspace.activity_log"},
+ {"text":"Deployment", "action":"onMenuItemPressed", "state": "workspace.deployment"},
+ {"text":"Inputs", "action":"onMenuItemPressed", "state": "workspace.resource_inputs"}
+ ],
+ "SERVICE":[
+ {"text":"General", "action":"onMenuItemPressed", "state": "workspace.general"},
+ {"text":"Icon", "action":"onMenuItemPressed", "state": "workspace.icons"},
+ {"text":"TOSCA Artifacts", "action":"onMenuItemPressed", "state": "workspace.tosca_artifacts"},
+ {"text":"Composition", "action":"onMenuItemPressed", "state": "workspace.composition.details"},
+ {"text":"Activity Log", "action":"onMenuItemPressed", "state": "workspace.activity_log"},
+ {"text":"Management Workflow", "action":"onMenuItemPressed", "state": "workspace.management_workflow"},
+ {"text":"Network Call Flow ", "action":"onMenuItemPressed", "state": "workspace.network_call_flow"},
+ {"text":"Monitor ", "action":"onMenuItemPressed", "state": "workspace.distribution", "disabledRoles": ["ADMIN", "TESTER", "GOVERNOR", "DESIGNER", "PRODUCT_MANAGER", "PRODUCT_STRATEGIST"]},
+ {"text":"Deployment", "action":"onMenuItemPressed", "state": "workspace.deployment"},
+ {"text":"Inputs", "action":"onMenuItemPressed", "state": "workspace.service_inputs"}
+ ],
+ "PRODUCT":[
+ {"text":"General", "action":"onMenuItemPressed", "state": "workspace.general"},
+ {"text":"Hierarchy", "action":"onMenuItemPressed", "state": "workspace.hierarchy"},
+ {"text":"Icon", "action":"onMenuItemPressed", "state": "workspace.icons"},
+ {"text":"Composition", "action":"onMenuItemPressed", "state": "workspace.composition.details"}
+ ]
+ }
+
+
+}
diff --git a/catalog-ui/configurations/mock.json b/catalog-ui/configurations/mock.json
new file mode 100644
index 0000000000..21836a837d
--- /dev/null
+++ b/catalog-ui/configurations/mock.json
@@ -0,0 +1,192 @@
+{
+ "sdcConfig": {
+ "api": {
+ "GET_user": "/v1/user/:id",
+ "GET_user_authorize": "/v1/user/authorize",
+ "GET_resource": "/v1/resource/:id",
+ "GET_resources_certified_not_abstract": "/v1/catalog/resources/certified/notabstract/:id",
+ "GET_resources_certified_abstract": "/v1/catalog/resources/certified/abstract/:id",
+ "GET_resource_property": "/v1/:type/:entityId/property/:id",
+ "GET_resource_artifact": "/v1/catalog/:type/:entityId/artifacts/:id",
+ "GET_service_artifact": "/v1/catalog/services/:serviceId/artifacts/:id",
+ "GET_resource_artifact_types": "/v1/artifactTypes",
+ "GET_resource_validate_name": "/v1/resource/validate-name/:name",
+ "GET_service": "/v1/catalog/services/:id",
+ "GET_service_validate_name": "/v1/catalog/services/validate-name/:name",
+ "GET_service_distributions":"/v1/catalog/services/:uuid/distribution",
+ "GET_service_distributions_components":"/v1/catalog/services/distribution/:distributionId",
+ "POST_service_distribution_deploy" : "/v1/catalog/services/:serviceId/distribution/:distributionId/markDeployed",
+ "GET_element": "/v1/followed",
+ "GET_catalog": "/v1/screen",
+ "GET_resource_category": "/v1/resourceCategories",
+ "GET_service_category": "/v1/serviceCategories",
+ "resource_instance": "/v1/catalog/services/:serviceId/resourceInstance/:id",
+ "GET_resource_instance_property": "/v1/catalog/services/:serviceId/resourceInstance/:resourceInstanceId/property/:propertyValueId",
+ "GET_relationship": "/v1/catalog/services/:serviceId/resourceInstance/:action",
+ "GET_lifecycle_state_resource": "/v1/catalog/:type/:id/lifecycleState/:action",
+ "GET_lifecycle_state_CHECKIN":"lifecycleState/CHECKIN",
+ "GET_lifecycle_state_CERTIFICATIONREQUEST":"lifecycleState/CERTIFICATIONREQUEST",
+ "GET_lifecycle_state_UNDOCHECKOUT":"lifecycleState/UNDOCHECKOUT",
+ "root": "http://localhost:9999"
+ },
+ "logConfig": {
+ "minLogLevel": "debug",
+ "prefix": "sdcApp"
+ },
+ "cookie": {
+ "junctionName": "IV_JCT",
+ "prefix": "AMWEBJCT!",
+ "userIdSuffix": "USER_ID",
+ "userFirstName": "HTTP_CSP_FIRSTNAME",
+ "userLastName": "HTTP_CSP_LASTNAME",
+ "userEmail": "HTTP_CSP_EMAIL",
+ "xEcompRequestId": " X-ECOMP-RequestID"
+ },
+ "userTypes": {
+ "admin": {
+ "userId": "jh0003",
+ "email": "mail@gmail.com",
+ "firstName": "Jimmy",
+ "lastName": "Hendrix",
+ "xEcompRequestId": "ccccc"
+ },
+ "designer": {
+ "userId": "cs0008",
+ "email": "designer@sdc.com",
+ "firstName": "Carlos",
+ "lastName": "Santana",
+ "xEcompRequestId": "ccccc"
+ },
+ "tester": {
+ "userId": "kb0004",
+ "email": "tester@sdc.com",
+ "firstName": "Kate",
+ "lastName": "Bush",
+ "xEcompRequestId": "ccccc"
+ },
+ "governor": {
+ "userId": "gv0001",
+ "email": "governor@sdc.com",
+ "firstName": "gover",
+ "lastName": "nor",
+ "xEcompRequestId": "ccccc"
+ },
+ "ops": {
+ "userId": "op0001",
+ "email": "ops@sdc.com",
+ "firstName": "op",
+ "lastName": "ss",
+ "xEcompRequestId": "ccccc"
+ },
+ "product_strategist": {
+ "userId": "ps0001",
+ "email": "product_strategist@sdc.com",
+ "firstName": "Orit",
+ "lastName": "Barda",
+ "xEcompRequestId": "ccccc"
+ },
+ "product_manager": {
+ "userId": "pm0001",
+ "email": "product_manager@sdc.com",
+ "firstName": "Orit",
+ "lastName": "Barda",
+ "xEcompRequestId": "ccccc"
+ }
+ },
+ "statuses": {
+ "inDesign": {
+ "name": "In Design",
+ "values": [
+ "NOT_CERTIFIED_CHECKOUT",
+ "NOT_CERTIFIED_CHECKIN"
+ ]
+ },
+ "readyForCertification": {
+ "name": "Ready for certification",
+ "values": "READY_FOR_CERTIFICATION"
+ },
+ "inCertification": {
+ "name": "In Certification",
+ "values": "CERTIFICATION_IN_PROGRESS"
+ },
+ "certified": {
+ "name": "Certified",
+ "values": "CERTIFIED"
+ },
+ "distributed": {
+ "name": "Distributed",
+ "values": "TBD"
+ }
+ },
+ "imagesPath": ""
+ },
+ "roles":{
+ "ADMIN":{
+ "pages":[],
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":[
+ {"text":"Check in","action":"changeLifecycleState", "url":"CHECKIN"},
+ {"text":"View" ,"action":"openViewerModal"},
+ {"text":"Edit" ,"action":"goToEntity"}
+ ],
+ "NOT_CERTIFIED_CHECKIN":[
+ {"text":"Check out","action":"changeLifecycleState", "url":"CHECKOUT"},
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"certificationRequest"},
+ {"text":"View" ,"action":"openViewerModal"}
+
+ ],
+ "READY_FOR_CERTIFICATION":[],
+ "CERTIFICATION_IN_PROGRESS":[],
+ "CERTIFIED":[]
+ }
+ },
+ "DESIGNER":{
+ "pages":[
+ ],
+ "states":{
+ "NOT_CERTIFIED_CHECKOUT":[
+ {"text":"Edit" ,"action":"goToEntity"},
+ {"text":"Check in","action":"changeLifecycleState", "url":"CHECKIN"},
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"certificationRequest"},
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "NOT_CERTIFIED_CHECKIN":[
+ {"text":"Check out","action":"changeLifecycleState", "url":"CHECKOUT"},
+ {"text":"Submit for Testing","action":"changeLifecycleState", "url":"certificationRequest"},
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "READY_FOR_CERTIFICATION":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "CERTIFICATION_IN_PROGRESS":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ],
+ "CERTIFIED":[
+ {"text":"View" ,"action":"openViewerModal"}
+ ]
+ }
+
+ },
+ "TESTER":{
+ "pages":[],
+ "READY_FOR_CERTIFICATION":[],
+ "CERTIFICATION_IN_PROGRESS":[],
+ "CERTIFIED":[]
+
+ },
+ "OPS":{
+ "pages":[],
+ "READY_FOR_CERTIFICATION":[],
+ "CERTIFICATION_IN_PROGRESS":[],
+ "CERTIFIED":[]
+
+ },
+ "GOVERNOR":{
+ "pages":[],
+ "READY_FOR_CERTIFICATION":[],
+ "CERTIFICATION_IN_PROGRESS":[],
+ "CERTIFIED":[]
+
+ }
+ }
+}
diff --git a/catalog-ui/configurations/prod.json b/catalog-ui/configurations/prod.json
new file mode 100644
index 0000000000..cbe579c3e9
--- /dev/null
+++ b/catalog-ui/configurations/prod.json
@@ -0,0 +1,432 @@
+{
+ "environment": "prod",
+ "api": {
+
+ "GET_component": "/v1/catalog/:type/:id",
+ "PUT_component": "/v1/catalog/:type/:id/metadata",
+ "GET_component_validate_name": "/v1/catalog/:type/validate-name/:name",
+ "POST_changeLifecycleState": "/v1/catalog/",
+ "component_api_root": "/v1/catalog/",
+ "welcome_page_video_url": "http://0.0.0.0:8282/moti/movie",
+
+ "GET_user": "/v1/user/:id",
+ "GET_user_authorize": "/v1/user/authorize",
+ "GET_all_users": "/v1/user/users",
+ "POST_create_user": "/v1/user",
+ "DELETE_delete_user": "/v1/user/:id",
+ "POST_edit_user_role": "/v1/user/:id/role",
+ "GET_resource": "/v1/catalog/resources/:id",
+ "GET_resources_latestversion_notabstract":"/v1/catalog/:type/latestversion/notabstract/:id",
+ "GET_resources_certified_not_abstract": "/v1/catalog/resources/certified/notabstract/:id",
+ "GET_resources_certified_abstract": "/v1/catalog/resources/certified/abstract/:id",
+ "GET_resource_property": "/v1/catalog/:type/:entityId/properties/:id",
+ "PUT_resource": "/v1/catalog/resources/:id/metadata",
+ "GET_resource_artifact": "/v1/catalog/:type/:entityId/artifacts/:id",
+ "GET_download_instance_artifact": "/v1/catalog/:type/:entityId/resourceInstances/:instanceId/artifacts/:id",
+ "POST_instance_artifact": "/v1/catalog/:type/:entityId/resourceInstance/:instanceId/artifacts/:id",
+ "GET_resource_additional_information": "/v1/catalog/:type/:entityId/additionalinfo/:id",
+ "GET_service_artifact": "/v1/catalog/services/:serviceId/artifacts/:id",
+ "GET_resource_interface_artifact": "/v1/catalog/:type/:entityId/standard/:operation/artifacts/:id",
+ "GET_resource_api_artifact": "/v1/catalog/:type/:entityId/artifacts/api/:id",
+ "GET_configuration_ui": "/v1/configuration/ui",
+ "GET_resource_validate_name": "/v1/catalog/resources/validate-name/:name",
+ "GET_activity_log": "/v1/catalog/audit-records/:type/:id",
+ "GET_service": "/v1/catalog/services/:id",
+ "GET_service_validate_name": "/v1/catalog/services/validate-name/:name",
+ "GET_service_distributions":"/v1/catalog/services/:uuid/distribution",
+ "GET_service_distributions_components":"/v1/catalog/services/distribution/:distributionId",
+ "POST_service_distribution_deploy" : "/v1/catalog/services/:serviceId/distribution/:distributionId/markDeployed",
+ "GET_element": "/v1/followed",
+ "GET_catalog": "/v1/screen",
+ "GET_ecomp_menu_items": "/v1/user/:userId/functionalmenu",
+ "GET_resource_category": "/v1/resourceCategories",
+ "GET_service_category": "/v1/serviceCategories",
+ "resource_instance": "/v1/catalog/:entityType/:entityId/resourceInstance/:id",
+ "GET_resource_instance_property": "/v1/catalog/:type/:entityId/resourceInstance/:componentInstanceId/property/:propertyValueId",
+ "GET_relationship": "/v1/catalog/:entityType/:entityId/resourceInstance/:action",
+ "GET_lifecycle_state_resource": "/v1/catalog/:type/:id/lifecycleState/:action",
+ "GET_lifecycle_state_CHECKIN":"lifecycleState/CHECKIN",
+ "GET_lifecycle_state_CERTIFICATIONREQUEST":"lifecycleState/CERTIFICATIONREQUEST",
+ "GET_lifecycle_state_UNDOCHECKOUT":"lifecycleState/UNDOCHECKOUT",
+ "root": "/sdc1/feProxy/rest",
+ "PUT_service": "/v1/catalog/services/:id/metadata",
+ "GET_download_artifact": "/v1/catalog/",
+ "GET_SDC_Version": "/version",
+ "GET_categories": "/v1/categories/:types",
+ "POST_category": "/v1/category/:types/:categoryId",
+ "POST_subcategory": "/v1/category/:types/:categoryId/subCategory/:subCategoryId",
+ "POST_change_instance_version": "/v1/catalog/:entityType/:entityId/resourceInstance/:id/changeVersion",
+ "GET_requirements_capabilities": "/v1/catalog/requirmentsCapabilities/:type/:id",
+ "GET_resource_artifact_types": "/v1/artifactTypes",
+ "GET_product_catalog": "/v1/productScreen",
+ "GET_product_category": "/v1/productCategories",
+ "GET_product_category_temp": "/v1/artifactTypes",
+ "POST_product": "/v1/catalog/products/:id/metadata",
+ "GET_product_validate_name": "/v1/catalog/services/validate-name/:name",
+ "GET_product": "/v1/catalog/products/:id",
+ "GET_product_sub_category": "/v1/productSubCategories",
+ "GET_onboarding": "/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/packages",
+ "GET_component_from_csar_uuid": "/v1/catalog/resources/csar/:csar_uuid",
+ "kibana": "/sdc1/kibanaProxy/"
+ },
+ "resourceTypesFilter":{
+ "resource":["CP","VFC"],
+ "service":["CP","VF"],
+ "product":[]
+ },
+ "logConfig": {
+ "minLogLevel": "debug",
+ "prefix": "sdcApp"
+ },
+ "cookie": {
+ "junctionName": "IV_JCT",
+ "prefix": "AMWEBJCT!",
+ "userIdSuffix": "USER_ID",
+ "userFirstName": "HTTP_CSP_FIRSTNAME",
+ "userLastName": "HTTP_CSP_LASTNAME",
+ "userEmail": "HTTP_CSP_EMAIL",
+ "xEcompRequestId": " X-ECOMP-RequestID"
+ },
+ "imagesPath": "/sdc1",
+ "cpEndPointInstances" : ["cloudep","ossep","personep","premisesep"],
+ "toscaFileExtension":"yaml,yml",
+ "csarFileExtension":"csar",
+ "openSource": true,
+ "categories": {},
+ "testers": {
+ "RESOURCE": {
+ "Network L2-3": "DL-ASDCL1-3ResourceCertificationTeam",
+ "Network L4+": "DL-ASDCL4-7ResourceCertificationTeam",
+ "Application L4+": "DL-ASDCL4-7ResourceCertificationTeam",
+ "default": "DL-ASDCL1-3ResourceCertificationTeam;DL-ASDCL4-7ResourceCertificationTeam"
+ },
+ "SERVICE": {
+ "Network L1-3": "DL-ASDCL1-4ServiceCertificationTeam",
+ "Network L4+": "DL-ASDCL4-7ServiceCertificationTeam",
+ "default": "DL-ASDCL1-4ServiceCertificationTeam;DL-ASDCL4-7ServiceCertificationTeam"
+ }
+ },
+ "roles": ["ADMIN", "TESTER", "GOVERNOR", "OPS", "DESIGNER", "PRODUCT_MANAGER", "PRODUCT_STRATEGIST"],
+ "tutorial": {
+ "tabs": [
+ {
+ "id":1,
+ "name":"TUTRIAL_GENERAL_TAB_1",
+ "defaultPage":1
+ },
+ {
+ "id":2,
+ "name":"TUTRIAL_GENERAL_TAB_2",
+ "defaultPage":9
+ },
+ {
+ "id":3,
+ "name":"TUTRIAL_GENERAL_TAB_3",
+ "defaultPage":12
+ }
+ ],
+ "pages":
+ [
+ {
+ "id":1,
+ "template": "text-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE1_TITLE",
+ "description":"TUTORIAL_PAGE1_TEXT"
+ }
+
+ },
+ {
+ "id":2,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE2_TITLE",
+ "description":"TUTORIAL_PAGE2_TEXT",
+ "imageClass":"sdc-tutorial-page-2-image"
+ }
+ },
+ {
+ "id":3,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE3_TITLE",
+ "description":"TUTORIAL_PAGE3_TEXT",
+ "imageClass":"sdc-tutorial-page-3-image"
+ }
+ },
+ {
+ "id":4,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE4_TITLE",
+ "description":"TUTORIAL_PAGE4_TEXT",
+ "imageClass":"sdc-tutorial-page-4-image"
+ }
+ },
+ {
+ "id":5,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE5_TITLE",
+ "description":"TUTORIAL_PAGE5_TEXT",
+ "imageClass":"sdc-tutorial-page-5-image"
+ }
+ },
+ {
+ "id":6,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE6_TITLE",
+ "description":"TUTORIAL_PAGE6_TEXT",
+ "imageClass":"sdc-tutorial-page-6-image"
+ }
+ },
+ {
+ "id":7,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE7_TITLE",
+ "description":"TUTORIAL_PAGE7_TEXT",
+ "imageClass":"sdc-tutorial-page-7-image"
+ }
+ },
+ {
+ "id":8,
+ "template": "image-template",
+ "tab": 1,
+ "data":{
+ "title":"TUTORIAL_PAGE8_TITLE",
+ "description":"TUTORIAL_PAGE8_TEXT",
+ "imageClass":"sdc-tutorial-page-8-image"
+ }
+ },
+ {
+ "id":9,
+ "template": "text-template",
+ "tab": 2,
+ "data":{
+ "title":"TUTORIAL_PAGE9_TITLE",
+ "description":"TUTORIAL_PAGE9_TEXT"
+ }
+ },
+ {
+ "id":10,
+ "template": "image-template",
+ "tab": 2,
+ "data":{
+ "title":"TUTORIAL_PAGE10_TITLE",
+ "description":"TUTORIAL_PAGE10_TEXT",
+ "imageClass":"sdc-tutorial-page-10-image"
+ }
+ },
+ {
+ "id":11,
+ "template": "image-template",
+ "tab": 2,
+ "data":{
+ "title":"TUTORIAL_PAGE11_TITLE",
+ "description":"TUTORIAL_PAGE11_TEXT",
+ "imageClass":"sdc-tutorial-page-11-image"
+ }
+ },
+ {
+ "id":12,
+ "template": "text-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE12_TITLE",
+ "description":"TUTORIAL_PAGE12_TEXT"
+ }
+ },
+ {
+ "id":13,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE13_TITLE",
+ "description":"TUTORIAL_PAGE13_TEXT",
+ "imageClass":"sdc-tutorial-page-13-image"
+ }
+ },
+ {
+ "id":14,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE14_TITLE",
+ "description":"TUTORIAL_PAGE14_TEXT",
+ "imageClass":"sdc-tutorial-page-14-image"
+ }
+ },
+ {
+ "id":15,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE15_TITLE",
+ "description":"TUTORIAL_PAGE15_TEXT",
+ "imageClass":"sdc-tutorial-page-15-image"
+ }
+ },
+ {
+ "id":16,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE16_TITLE",
+ "description":"TUTORIAL_PAGE16_TEXT",
+ "imageClass":"sdc-tutorial-page-16-image"
+ }
+ },
+ {
+ "id":17,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE17_TITLE",
+ "description":"TUTORIAL_PAGE17_TEXT",
+ "imageClass":"sdc-tutorial-page-17-image"
+ }
+ },
+ {
+ "id":18,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE18_TITLE",
+ "description":"TUTORIAL_PAGE18_TEXT",
+ "imageClass":"sdc-tutorial-page-18-image"
+ }
+ },
+ {
+ "id":19,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE19_TITLE",
+ "description":"TUTORIAL_PAGE19_TEXT",
+ "imageClass":"sdc-tutorial-page-19-image"
+ }
+ },
+ {
+ "id":20,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE20_TITLE",
+ "description":"TUTORIAL_PAGE20_TEXT",
+ "imageClass":"sdc-tutorial-page-20-image"
+ }
+ },
+ {
+ "id":21,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE21_TITLE",
+ "description":"TUTORIAL_PAGE21_TEXT",
+ "imageClass":"sdc-tutorial-page-21-image"
+ }
+ },
+ {
+ "id":22,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE22_TITLE",
+ "description":"TUTORIAL_PAGE22_TEXT",
+ "imageClass":"sdc-tutorial-page-22-image"
+ }
+ },
+ {
+ "id":23,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE23_TITLE",
+ "description":"TUTORIAL_PAGE23_TEXT",
+ "imageClass":"sdc-tutorial-page-23-image"
+ }
+ },
+ {
+ "id":24,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE24_TITLE",
+ "description":"TUTORIAL_PAGE24_TEXT",
+ "imageClass":"sdc-tutorial-page-24-image"
+ }
+ },
+ {
+ "id":25,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE25_TITLE",
+ "description":"TUTORIAL_PAGE25_TEXT",
+ "imageClass":"sdc-tutorial-page-25-image"
+ }
+ },
+ {
+ "id":26,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE26_TITLE",
+ "description":"TUTORIAL_PAGE26_TEXT",
+ "imageClass":"sdc-tutorial-page-26-image"
+ }
+ },
+ {
+ "id":27,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE27_TITLE",
+ "description":"TUTORIAL_PAGE27_TEXT",
+ "imageClass":"sdc-tutorial-page-27-image"
+ }
+ },
+ {
+ "id":28,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE28_TITLE",
+ "description":"TUTORIAL_PAGE28_TEXT",
+ "imageClass":"sdc-tutorial-page-28-image"
+ }
+ },
+ {
+ "id":29,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE29_TITLE",
+ "description":"TUTORIAL_PAGE29_TEXT",
+ "imageClass":"sdc-tutorial-page-29-image"
+ }
+ },
+ {
+ "id":30,
+ "template": "image-template",
+ "tab": 3,
+ "data":{
+ "title":"TUTORIAL_PAGE30_TITLE",
+ "description":"TUTORIAL_PAGE30_TEXT",
+ "imageClass":"sdc-tutorial-page-30-image"
+ }
+ }
+
+ ]
+ }
+
+}
diff --git a/catalog-ui/docs/colors.jpg b/catalog-ui/docs/colors.jpg
new file mode 100644
index 0000000000..edc038c0d2
--- /dev/null
+++ b/catalog-ui/docs/colors.jpg
Binary files differ
diff --git a/catalog-ui/non_bower_components/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js b/catalog-ui/non_bower_components/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js
new file mode 100644
index 0000000000..147f19ce54
--- /dev/null
+++ b/catalog-ui/non_bower_components/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js
@@ -0,0 +1,554 @@
+(function (scope) {
+ var Class = function (param1, param2) {
+
+ var extend, mixins, definition;
+ if (param2) { //two parameters passed, first is extends, second definition object
+ extend = Array.isArray(param1) ? param1[0] : param1;
+ mixins = Array.isArray(param1) ? param1.slice(1) : null;
+ definition = param2;
+ } else { //only one parameter passed => no extend, only definition
+ extend = null;
+ definition = param1;
+ }
+
+
+ var Definition = definition.hasOwnProperty("constructor") ? definition.constructor : function () {
+ };
+
+ Definition.prototype = Object.create(extend ? extend.prototype : null);
+ var propertiesObject = definition.propertiesObject ? definition.propertiesObject : {};
+ if (mixins) {
+ var i, i2;
+ for (i in mixins) {
+ for (i2 in mixins[i].prototype) {
+ Definition.prototype[i2] = mixins[i].prototype[i2];
+ }
+ for (var i2 in mixins[i].prototype.propertiesObject) {
+ propertiesObject[i2] = mixins[i].prototype.propertiesObject[i2];
+ }
+ }
+ }
+
+ Definition.prototype.propertiesObject = propertiesObject;
+
+ Object.defineProperties(Definition.prototype, propertiesObject);
+
+ for (var key in definition) {
+ if (definition.hasOwnProperty(key)) {
+ Definition.prototype[key] = definition[key];
+ }
+ }
+
+ Definition.prototype.constructor = Definition;
+
+ return Definition;
+ };
+
+
+ var Interface = function (properties) {
+ this.properties = properties;
+ };
+
+ var InterfaceException = function (message) {
+ this.name = "InterfaceException";
+ this.message = message || "";
+ };
+
+ InterfaceException.prototype = new Error();
+
+ Interface.prototype.implements = function (target) {
+ for (var i in this.properties) {
+ if (target[this.properties[i]] == undefined) {
+ throw new InterfaceException("Missing property " + this.properties[i]);
+ }
+ }
+ return true;
+ };
+
+ Interface.prototype.doesImplement = function (target) {
+ for (var i in this.properties) {
+ if (target[this.properties[i]] === undefined) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ var VectorMath = {
+ distance: function (vector1, vector2) {
+ return Math.sqrt(Math.pow(vector1.x - vector2.x, 2) + Math.pow(vector1.y - vector2.y, 2));
+ }
+ };
+
+ var EventDispatcher = Class({
+ constructor: function () {
+ this.events = {};
+ },
+ on: function (name, listener, context) {
+ this.events[name] = this.events[name] ? this.events[name] : [];
+ this.events[name].push({
+ listener: listener,
+ context: context
+ })
+ },
+ once: function (name, listener, context) {
+ this.off(name, listener, context);
+ this.on(name, listener, context);
+ },
+ off: function (name, listener, context) {
+ //no event with this name registered? => finish
+ if (!this.events[name]) {
+ return;
+ }
+ if (listener) { //searching only for certains listeners
+ for (var i in this.events[name]) {
+ if (this.events[name][i].listener === listener) {
+ if (!context || this.events[name][i].context === context) {
+ this.events[name].splice(i, 1);
+ }
+ }
+ }
+ } else {
+ delete this.events[name];
+ }
+ },
+ trigger: function (name) {
+ var listeners = this.events[name];
+
+ for (var i in listeners) {
+ listeners[i].listener.apply(listeners[i].context, Array.prototype.slice.call(arguments, 1));
+ }
+ }
+ });
+
+ scope.CytoscapeEdgeEditation = Class({
+
+ init: function (cy, handleSize) {
+ this.DOUBLE_CLICK_INTERVAL = 300;
+ this.HANDLE_SIZE = handleSize ? handleSize : 5;
+ this.ARROW_END_ID = "ARROW_END_ID";
+
+ this._handles = {};
+ this._dragging = false;
+ this._hover = null;
+
+
+ this._cy = cy;
+ this._$container = $(cy.container());
+
+ this._cy.on('mouseover tap', 'node', this._mouseOver.bind(this));
+ this._cy.on('mouseout', 'node', this._mouseOut.bind(this));
+
+ this._$container.on('mouseout', function (e) {
+ if (this.permanentHandle) {
+ return;
+ }
+
+ this._clear();
+ }.bind(this));
+
+ this._$container.on('mouseover', function (e) {
+ if (this._hover) {
+ this._mouseOver({cyTarget: this._hover});
+ }
+ }.bind(this));
+
+ this._cy.on("select", "node", this._redraw.bind(this))
+
+ this._cy.on("mousedown", "node", function () {
+ this._nodeClicked = true;
+ }.bind(this));
+
+ this._cy.on("mouseup", "node", function () {
+ this._nodeClicked = false;
+ }.bind(this));
+
+ this._cy.on("remove", "node", function () {
+ this._hover = false;
+ this._clear();
+ }.bind(this));
+
+ this._cy.on('showhandle', function (cy, target) {
+ this.permanentHandle = true;
+ this._showHandles(target);
+ }.bind(this));
+
+ this._cy.on('hidehandles', this._hideHandles.bind(this));
+
+ this._cy.bind('zoom pan', this._redraw.bind(this));
+
+
+ this._$canvas = $('<canvas></canvas>');
+ this._$canvas.css("top", 0);
+ this._$canvas.on("mousedown", this._mouseDown.bind(this));
+ this._$canvas.on("mousemove", this._mouseMove.bind(this));
+
+ this._ctx = this._$canvas[0].getContext('2d');
+ this._$container.children("div").append(this._$canvas);
+
+ $(window).bind('mouseup', this._mouseUp.bind(this));
+
+ /*$(window).bind('resize', this._resizeCanvas.bind(this));
+ $(window).bind('resize', this._resizeCanvas.bind(this));*/
+
+ this._cy.on("resize", this._resizeCanvas.bind(this));
+
+ this._$container.bind('resize', function () {
+ this._resizeCanvas();
+ }.bind(this));
+
+ this._resizeCanvas();
+
+ this._arrowEnd = this._cy.add({
+ group: "nodes",
+ data: {
+ "id": this.ARROW_END_ID,
+ "position": {x: 150, y: 150}
+ }
+ });
+
+ this._arrowEnd.css({
+ "opacity": 0,
+ 'width': 0.0001,
+ 'height': 0.0001
+ });
+
+ },
+ registerHandle: function (handle) {
+ if (handle.nodeTypeNames) {
+ for (var i in handle.nodeTypeNames) {
+ var nodeTypeName = handle.nodeTypeNames[i];
+ this._handles[nodeTypeName] = this._handles[nodeTypeName] || [];
+ this._handles[nodeTypeName].push(handle);
+ }
+ } else {
+ this._handles["*"] = this._handles["*"] || [];
+ this._handles["*"].push(handle);
+ }
+
+ },
+ _showHandles: function (target) {
+ var nodeTypeName = target.data().type;
+ if (nodeTypeName) {
+
+ var handles = this._handles[nodeTypeName] ? this._handles[nodeTypeName] : this._handles["*"];
+
+ for (var i in handles) {
+ if (handles[i].type != null) {
+ this._drawHandle(handles[i], target);
+ }
+ }
+ }
+
+ },
+ _clear: function () {
+
+ var w = this._$container.width();
+ var h = this._$container.height();
+ this._ctx.clearRect(0, 0, w, h);
+ },
+ _drawHandle: function (handle, target) {
+
+ var position = this._getHandlePosition(handle, target);
+
+ this._ctx.beginPath();
+
+ if (handle.imageUrl) {
+ var base_image = new Image();
+ base_image.src = handle.imageUrl;
+ this._ctx.drawImage(base_image, position.x, position.y, this.HANDLE_SIZE, this.HANDLE_SIZE);
+ } else {
+ this._ctx.arc(position.x, position.y, this.HANDLE_SIZE, 0, 2 * Math.PI, false);
+ this._ctx.fillStyle = handle.color;
+ this._ctx.strokeStyle = "white";
+ this._ctx.lineWidth = 0;
+ this._ctx.fill();
+ this._ctx.stroke();
+ }
+
+ },
+ _drawArrow: function (fromNode, toPosition, handle) {
+ var toNode;
+ if (this._hover) {
+ toNode = this._hover;
+ } else {
+ this._arrowEnd.renderedPosition(toPosition);
+ toNode = this._arrowEnd;
+ }
+
+
+ if (this._edge) {
+ this._edge.remove();
+ }
+
+ this._edge = this._cy.add({
+ group: "edges",
+ data: {
+ id: "edge",
+ source: fromNode.id(),
+ target: toNode.id(),
+ type: 'temporary-link'
+ },
+ css: $.extend(
+ this._getEdgeCSSByHandle(handle),
+ {opacity: 0.5}
+ )
+ });
+
+ },
+ _clearArrow: function () {
+ if (this._edge) {
+ this._edge.remove();
+ this._edge = null;
+ }
+ },
+ _resizeCanvas: function () {
+ this._$canvas
+ .attr('height', this._$container.height())
+ .attr('width', this._$container.width())
+ .css({
+ 'position': 'absolute',
+ 'z-index': '999'
+ });
+ },
+ _mouseDown: function (e) {
+ this._hit = this._hitTestHandles(e);
+
+ if (this._hit) {
+ this._lastClick = Date.now();
+ this._dragging = this._hover;
+ this._hover = null;
+ e.stopImmediatePropagation();
+ }
+ },
+ _hideHandles: function () {
+ this.permanentHandle = false;
+ this._clear();
+
+ if(this._hover){
+ this._showHandles(this._hover);
+ }
+ },
+ _mouseUp: function () {
+ if (this._hover) {
+ if (this._hit) {
+ //check if custom listener was passed, if so trigger it and do not add edge
+ var listeners = this._cy._private.listeners;
+ for (var i = 0; i < listeners.length; i++) {
+ if (listeners[i].type === 'addedgemouseup') {
+ this._cy.trigger('addedgemouseup', {
+ source: this._dragging,
+ target: this._hover,
+ edge: this._edge
+ });
+ var that = this;
+ setTimeout(function () {
+ that._dragging = false;
+ that._clearArrow();
+ that._hit = null;
+ }, 0);
+
+
+ return;
+ }
+ }
+
+ var edgeToRemove = this._checkSingleEdge(this._hit.handle, this._dragging);
+ if (edgeToRemove) {
+ this._cy.remove("#" + edgeToRemove.id());
+ }
+ var edge = this._cy.add({
+ data: {
+ source: this._dragging.id(),
+ target: this._hover.id(),
+ type: this._hit.handle.type
+ }
+ });
+ this._initEdgeEvents(edge);
+ }
+ }
+ this._cy.trigger('handlemouseout', {
+ node: this._hover
+ });
+ $("body").css("cursor", "inherit");
+ this._dragging = false;
+ this._clearArrow();
+ },
+ _mouseMove: function (e) {
+ if (this._hover) {
+ if (!this._dragging) {
+ var hit = this._hitTestHandles(e);
+ if (hit) {
+ this._cy.trigger('handlemouseover', {
+ node: this._hover
+ });
+ $("body").css("cursor", "pointer");
+
+ } else {
+ this._cy.trigger('handlemouseout', {
+ node: this._hover
+ });
+ $("body").css("cursor", "inherit");
+ }
+ }
+ } else {
+ $("body").css("cursor", "inherit");
+ }
+
+ if (this._dragging && this._hit.handle) {
+ this._drawArrow(this._dragging, this._getRelativePosition(e), this._hit.handle);
+ }
+
+ if (this._nodeClicked) {
+ this._clear();
+ }
+ },
+ _mouseOver: function (e) {
+
+ if (this._dragging) {
+ if ( (e.cyTarget.id() != this._dragging.id()) && e.cyTarget.data().allowConnection || this._hit.handle.allowLoop) {
+ this._hover = e.cyTarget;
+ }
+ } else {
+ this._hover = e.cyTarget;
+ this._showHandles(this._hover);
+ }
+ },
+ _mouseOut: function (e) {
+ if(!this._dragging) {
+ if (this.permanentHandle) {
+ return;
+ }
+
+ this._clear();
+ }
+ this._hover = null;
+ },
+ _removeEdge: function (edge) {
+ edge.off("mousedown");
+ this._cy.remove("#" + edge.id());
+ },
+ _initEdgeEvents: function (edge) {
+ var self = this;
+ edge.on("mousedown", function () {
+ if (self.__lastClick && Date.now() - self.__lastClick < self.DOUBLE_CLICK_INTERVAL) {
+ self._removeEdge(this);
+ }
+ self.__lastClick = Date.now();
+ })
+ },
+ _hitTestHandles: function (e) {
+ var mousePoisition = this._getRelativePosition(e);
+
+ if (this._hover) {
+ var nodeTypeName = this._hover.data().type;
+ if (nodeTypeName) {
+ var handles = this._handles[nodeTypeName] ? this._handles[nodeTypeName] : this._handles["*"];
+
+ for (var i in handles) {
+ var handle = handles[i];
+
+ var position = this._getHandlePosition(handle, this._hover);
+ if (VectorMath.distance(position, mousePoisition) < this.HANDLE_SIZE) {
+ return {
+ handle: handle,
+ position: position
+ };
+ }
+ }
+ }
+ }
+ },
+ _getHandlePosition: function (handle, target) {
+ var position = target.renderedPosition();
+ var width = target.renderedOuterWidth();
+ var height = target.renderedOuterHeight();
+ var xpos = null;
+ var ypos = null;
+
+ switch (handle.positionX) {
+ case "left":
+ xpos = position.x - width / 2 + this.HANDLE_SIZE;
+ break;
+ case "right":
+ xpos = position.x + width / 2 - this.HANDLE_SIZE;
+ break;
+ case "center":
+ xpos = position.x;
+ break;
+ }
+
+ switch (handle.positionY) {
+ case "top":
+ ypos = position.y - height / 2 + this.HANDLE_SIZE;
+ break;
+ case "center":
+ ypos = position.y;
+ break;
+ case "bottom":
+ ypos = position.y + height / 2 - this.HANDLE_SIZE;
+ break;
+ }
+
+ var offsetX = handle.offsetX ? handle.offsetX : 0;
+ var offsetY = handle.offsetY ? handle.offsetY : 0;
+ return {x: xpos + offsetX, y: ypos + offsetY};
+ },
+ _getEdgeCSSByHandle: function (handle) {
+ var color = handle.lineColor ? handle.lineColor : handle.color;
+ return {
+ "line-color": color,
+ "target-arrow-color": color,
+ "line-style": handle.lineStyle? handle.lineStyle: 'solid',
+ "width": handle.width? handle.width : 3
+ };
+ },
+ _getHandleByType: function (type) {
+ for (var i in this._handles) {
+ var byNodeType = this._handles[i];
+ for (var i2 in byNodeType) {
+ var handle = byNodeType[i2];
+ if (handle.type == type) {
+ return handle;
+ }
+ }
+ }
+ },
+ _getRelativePosition: function (e) {
+ var containerPosition = this._$container.offset();
+ return {
+ x: e.pageX - containerPosition.left,
+ y: e.pageY - containerPosition.top
+ }
+ },
+ _checkSingleEdge: function (handle, node) {
+
+ if (handle.noMultigraph) {
+ var edges = this._cy.edges("[source='" + this._hover.id() + "'][target='" + node.id() + "'],[source='" + node.id() + "'][target='" + this._hover.id() + "']");
+
+ for (var i = 0; i < edges.length; i++) {
+ return edges[i];
+ }
+ } else {
+
+ if (handle.single == false) {
+ return;
+ }
+ var edges = this._cy.edges("[source='" + node.id() + "']");
+
+ for (var i = 0; i < edges.length; i++) {
+ if (edges[i].data()["type"] == handle.type) {
+ return edges[i];
+ }
+ }
+ }
+ },
+ _redraw: function () {
+ this._clear();
+ if (this._hover) {
+ this._showHandles(this._hover);
+ }
+ }
+ });
+
+})(this); \ No newline at end of file
diff --git a/catalog-ui/non_bower_components/cytoscape.js-edge-editation/README.md b/catalog-ui/non_bower_components/cytoscape.js-edge-editation/README.md
new file mode 100644
index 0000000000..909dcb06d1
--- /dev/null
+++ b/catalog-ui/non_bower_components/cytoscape.js-edge-editation/README.md
@@ -0,0 +1,58 @@
+# cytoscape.js-edge-editation
+Extension for Cytoscape.js, which adds handles to nodes and allows to create different types of edges
+
+
+## Dependencies
+
+Extension was tested with these versions of libraries:
+
+* jQuery 2.1.1
+* Cytoscape.js 2.6.0
+
+## Install
+
+Use git clone or direct zip download and unpack archive into your project. Then, simply insert \<script\> tag after
+Cytoscape.js and jQuery:
+
+```html
+<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
+<script src="cytoscape.js"></script>
+<script src="CytoscapeEdgeEditation.js"></script>
+```
+
+## How to use
+
+First, you need to initialize extension. After initializing Cytoscape.js:
+
+```js
+var cy = cytoscape({...});
+var handles = new CytoscapeEdgeEditation;
+handles.init(cy);
+```
+
+Then, you need to register handles to certain node types:
+
+```js
+handles.registerHandle({
+ positionX: "left", //horizontal position of the handle (left | center | right)
+ positionY: "center", //vertical position of the handle (top | center | bottom)
+ color: "#48FF00", //color of the handle
+ type: "some_type", //stored as data() attribute, can be used for styling
+ single: true, //wheter only one edge of this type can start from same node (default false)
+ nodeTypeNames: ["type2"] //which types of nodes will contain this handle
+ noMultigraph: false //whereter two nodes can't be connected with multiple edges (does not consider orientation)
+});
+
+handles.registerHandle({...});
+handles.registerHandle({...});
+```
+
+Type of node is stored in data section:
+
+```js
+cy.add({
+ data: { id: 'n4', type: "type2"},
+});
+```
+![Screenshot](http://i.imgbox.com/drCuXQqu.png)
+![Screenshot](http://i.imgbox.com/23jr7qPa.png)
diff --git a/catalog-ui/non_bower_components/cytoscape.js-edge-editation/index.html b/catalog-ui/non_bower_components/cytoscape.js-edge-editation/index.html
new file mode 100644
index 0000000000..d19fd36c24
--- /dev/null
+++ b/catalog-ui/non_bower_components/cytoscape.js-edge-editation/index.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<html lang="cs">
+<head>
+ <meta charset="UTF-8">
+ <title>CytoscapeEdgeEditation</title>
+ <script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
+ <script src="bower_components/cytoscape/dist/cytoscape.js"></script>
+ <script src="CytoscapeEdgeEditation.js"></script>
+ <script>
+ $(function(){
+ //INITIALIZATION
+ var domContainer = $(".cy");
+ var cy = cytoscape({
+ container: domContainer,
+ style: [
+ {
+ selector: 'node[type="type1"]',
+ style: {
+ 'background-color': '#FFEB12',
+ }
+ },
+ {
+ selector: 'node[type="type2"]',
+ style: {
+ 'background-color': '#2CE8F2',
+ }
+ },
+ {
+ selector: 'edge',
+ style: {
+ 'width': 3,
+ 'target-arrow-shape': 'triangle'
+ }
+ },
+ {
+ selector: 'edge[type="default"]',
+ style: {
+ 'line-color': 'green',
+ 'target-arrow-color': 'green',
+ }
+ },
+ {
+ selector: 'edge[type="yes"]',
+ style: {
+ 'line-color': '#48FF00',
+ 'target-arrow-color': '#48FF00',
+ }
+ },
+ {
+ selector: 'edge[type="no"]',
+ style: {
+ 'line-color': '#ED1000',
+ 'target-arrow-color': '#ED1000',
+ }
+ }
+ ]
+ });
+
+
+ var handles = new CytoscapeEdgeEditation;
+ handles.init(cy);
+
+ handles.registerHandle({
+ positionX: "center",
+ positionY: "bottom",
+ color: "green",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["type1"]
+ });
+
+ handles.registerHandle({
+ positionX: "center",
+ positionY: "top",
+ color: "green",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["type1"]
+ });
+
+ handles.registerHandle({
+ positionX: "left",
+ positionY: "center",
+ color: "green",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["type1"]
+ });
+
+ handles.registerHandle({
+ positionX: "right",
+ positionY: "center",
+ color: "green",
+ type: "default",
+ single: false,
+ nodeTypeNames: ["type1"]
+ // noMultigraph: true
+ });
+
+ handles.registerHandle({
+ positionX: "left",
+ positionY: "center",
+ color: "#48FF00",
+ type: "yes",
+ single: true,
+ nodeTypeNames: ["type2"]
+ });
+
+ handles.registerHandle({
+ positionX: "right",
+ positionY: "center",
+ color: "#ED1000",
+ type: "no",
+ single: true,
+ nodeTypeNames: ["type2"]
+ });
+
+
+ //ADD NODES
+ cy.add({
+ data: { id: 'n1', type: "type1"},
+ });
+
+ cy.add({
+ data: { id: 'n2', type: "type1"},
+ });
+
+ cy.add({
+ data: { id: 'n3', type: "type1"},
+ });
+
+ cy.add({
+ data: { id: 'n4', type: "type2"},
+ });
+
+
+ //ADJUST HEIGHT
+ var resizeViewport = function(){
+ $(".cy").height($(window).height());
+ cy.resize();
+ };
+
+ $(window).resize(resizeViewport);
+ resizeViewport();
+
+ //LAYOUT
+ cy.layout({
+ name: 'random',
+ fit: true,
+ padding: 40,
+ boundingBox: {x1: -200, y1:-200, x2:300, y2: 200}
+ });
+ });
+ </script>
+ <style>
+ * {
+ padding: 0;
+ margin: 0;
+ }
+ .cy {
+ width: 100%;
+ height:100%;
+ }
+ </style>
+</head>
+<body>
+ <div class="cy"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/catalog-ui/package.json b/catalog-ui/package.json
new file mode 100644
index 0000000000..beb2af9996
--- /dev/null
+++ b/catalog-ui/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "catalog-ui",
+ "description": "ASDC Single page application",
+ "repository": {
+ "type": "git",
+ "url": "git@gitlab:root/D2-SDnC.git"
+ },
+ "devDependencies": {
+ "apache-server-configs": "^2.7.1",
+ "cors": "2.7.1",
+ "grunt": "1.0.1",
+ "grunt-angular-templates": "1.1.0",
+ "grunt-autoprefixer": "3.0.4",
+ "grunt-concurrent": "0.5.0",
+ "grunt-contrib-clean": "1.0.0",
+ "grunt-contrib-concat": "1.0.1",
+ "grunt-contrib-connect": "0.10.1",
+ "grunt-contrib-copy": "1.0.0",
+ "grunt-contrib-cssmin": "0.10.0",
+ "grunt-contrib-htmlmin": "2.1.0",
+ "grunt-contrib-imagemin": "1.0.1",
+ "grunt-contrib-jshint": "1.1.0",
+ "grunt-contrib-uglify": "2.0.0",
+ "grunt-contrib-watch": "1.0.0",
+ "grunt-include-source": "1.0.0",
+ "grunt-karma": "2.0.0",
+ "grunt-mocha": "1.0.2",
+ "grunt-newer": "0.7.0",
+ "grunt-ng-constant": "1.1.0",
+ "grunt-rev": "0.1.0",
+ "grunt-svgmin": "4.0.0",
+ "grunt-text-replace": "0.4.0",
+ "grunt-tslint": "^3.3.0",
+ "grunt-usemin": "3.1.1",
+ "grunt-wiredep": "2.0.0",
+ "http-proxy-middleware": "^0.14.0",
+ "jasmine-core": "2.5.2",
+ "jshint-stylish": "2.2.1",
+ "karma": "1.4.0",
+ "karma-chrome-launcher": "0.2.2",
+ "karma-coverage": "1.1.1",
+ "karma-jasmine": "1.1.0",
+ "karma-junit-reporter": "0.3.8",
+ "karma-mocha-reporter": "2.2.2",
+ "karma-ng-html2js-preprocessor": "1.0.0",
+ "karma-ng-scenario": "1.0.0",
+ "karma-phantomjs-launcher": "0.2.1",
+ "load-grunt-tasks": "3.5.2",
+ "phantomjs": "2.1.7",
+ "time-grunt": "1.4.0",
+ "tslint": "^3.15.1"
+ },
+ "engines": {
+ "node": ">=6.9.4"
+ },
+ "dependencies": {
+ "cytoscape.js-undo-redo": "^1.0.1",
+ "express": "4.14.0",
+ "grunt-asset-injector": "0.1.0",
+ "grunt-contrib-less": "1.4.0",
+ "grunt-express-server": "0.5.3",
+ "grunt-ts": "5.5.1",
+ "grunt-html2js": "0.3.6",
+ "multer": "1.2.1",
+ "ng-html2js": "2.0.0"
+ }
+}
diff --git a/catalog-ui/pom.xml b/catalog-ui/pom.xml
new file mode 100644
index 0000000000..02d045a9fe
--- /dev/null
+++ b/catalog-ui/pom.xml
@@ -0,0 +1,231 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>catalog-ui</artifactId>
+ <packaging>pom</packaging>
+
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+
+ <build>
+ <plugins>
+
+ <!-- ============================================= -->
+ <!-- Clean webapps folder -->
+ <!-- ============================================= -->
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.6.1</version>
+ <executions>
+ <execution>
+ <id>clean.fe.webapp.folder</id>
+ <phase>initialize</phase>
+ <goals>
+ <goal>clean</goal>
+ </goals>
+ <configuration>
+
+ <filesets>
+ <fileset>
+ <directory>${project.parent.basedir}/catalog-fe/src/main/webapp</directory>
+ <includes>
+ <include>**/*</include>
+ <include>*</include>
+ </includes>
+ <excludes>
+ <exclude>META-INF/*</exclude>
+ <exclude>WEB-INF/*</exclude>
+ </excludes>
+ <followSymlinks>false</followSymlinks>
+ </fileset>
+ </filesets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.7</version>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.parent.basedir}/catalog-fe/src/main/webapp</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/app/dist</directory>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+
+
+ <profiles>
+ <profile>
+ <id>CI</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.7</version>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+
+ <configuration>
+ <outputDirectory>${project.parent.basedir}/catalog-fe/src/main/webapp</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/app/dist</directory>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+
+ <!-- ============================================= -->
+ <!-- Build the UI module node code -->
+ <!-- ============================================= -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.4.0</version>
+ <executions>
+ <execution>
+ <id>execute grant build</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+
+ <configuration>
+ <executable>./build_catalog_ui.sh</executable>
+ <workingDirectory>${project.basedir}</workingDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+
+
+ <profile>
+ <id>WINDOWS_ONLY</id>
+ <activation>
+ <os>
+ <family>Windows</family>
+ </os>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>exec-maven-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <version>1.4.0</version>
+ <executions>
+ <execution>
+ <id>execute grant build</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>build_catalog_ui.bat</executable>
+ <workingDirectory>${basedir}</workingDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+
+ <profile>
+ <id>not-minified</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.7</version>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.parent.basedir}/catalog-fe/src/main/webapp</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/app</directory>
+ <includes>
+ <include>languages/**</include>
+ <include>scripts/**</include>
+ <include>styles/**</include>
+ <include>third-party/**</include>
+ <include>index.html</include>
+ <include>robots.txt</include>
+ <include>favicon.png</include>
+ </includes>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <directory>${project.basedir}</directory>
+ <includes>
+ <include>bower_components/**</include>
+ </includes>
+ <filtering>false</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
+
diff --git a/catalog-ui/server-mock/mock-data/artifact/artifact-types.json b/catalog-ui/server-mock/mock-data/artifact/artifact-types.json
new file mode 100644
index 0000000000..b9cdabee51
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/artifact/artifact-types.json
@@ -0,0 +1,23 @@
+[
+ {
+ "name": "CHEF"
+ },
+ {
+ "name": "PUPPET"
+ },
+ {
+ "name": "SHELL"
+ },
+ {
+ "name": "YANG"
+ },
+ {
+ "name": "HEAT"
+ },
+ {
+ "name": "BPEL"
+ },
+ {
+ "name": "DG-XML"
+ }
+]
diff --git a/catalog-ui/server-mock/mock-data/category/category.json b/catalog-ui/server-mock/mock-data/category/category.json
new file mode 100644
index 0000000000..2dabb50ba6
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/category/category.json
@@ -0,0 +1,65 @@
+[
+ {
+ "name": "Infrastructure"
+ },
+ {
+ "name": "Databases"
+ },
+ {
+ "name": "Web Servers"
+ },
+ {
+ "name": "Web Applications"
+ },
+ {
+ "name": "Network Elements"
+ },
+ {
+ "name": "VoIP"
+ },
+ {
+ "name": "IMS"
+ },
+ {
+ "name": "Security"
+ },
+ {
+ "name": "Video"
+ },
+ {
+ "name": "Mobility"
+ },
+ {
+ "name": "IoT"
+ },
+ {
+ "name": "Big Data"
+ },
+ {
+ "name": "WAN Connectivity"
+ },
+ {
+ "name": "LAN Connectivity"
+ },
+ {
+ "name": "Compute as a Service"
+ },
+ {
+ "name": "Platform as a Service"
+ },
+ {
+ "name": "Storage as a Service"
+ },
+ {
+ "name": "Call Control"
+ },
+ {
+ "name": "Collaboration"
+ },
+ {
+ "name": "Messaging"
+ },
+ {
+ "name": "Abstract"
+ }
+]
diff --git a/catalog-ui/server-mock/mock-data/element/element.json b/catalog-ui/server-mock/mock-data/element/element.json
new file mode 100644
index 0000000000..181c0edcf2
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/element/element.json
@@ -0,0 +1,2915 @@
+{
+ "resources": [
+ {
+ "uniqueId": "res_hgdgg.0.1",
+ "resourceName": "hgdgg",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1435825652946,
+ "lastUpdateDate": 1435825652946,
+ "description": "hfhdgfdgfh",
+ "icon": "icon-red2",
+ "tags": [
+ "hgdgg"
+ ],
+ "category": "Call Control",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebApplication"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "app_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webapplication.1.0.app_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.webapplication.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.WebServer",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "fhgghf",
+ "vendorRelease": "fggf",
+ "contactId": "hg1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_martin223.0.1",
+ "resourceName": "martin223",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1435842460057,
+ "lastUpdateDate": 1435842460057,
+ "description": "desc",
+ "icon": "icon-red2",
+ "tags": [
+ "martin223",
+ "tag"
+ ],
+ "category": "Databases",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.5",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_mlrlv.0.1",
+ "resourceName": "mlrlv",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436079916172,
+ "lastUpdateDate": 1436079916172,
+ "description": "€Â‚ƒ„…†‡ˆ‰Š‹ŒÂŽÂ‘’“â€â€¢â€“—˜™š›œÂžŸ ¡¢£¤¥¦§¨©ª«¬-®¯°±²³´µ¶•¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
+ "icon": "icon-red2",
+ "tags": [
+ "mlrlvtag"
+ ],
+ "category": "Databases",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.4",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_%&+-.0.1",
+ "resourceName": "%&+-",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436079975445,
+ "lastUpdateDate": 1436079975445,
+ "description": "%&+-%&+-%&+-%&+-%&+-%&+-",
+ "icon": "icon-red2",
+ "tags": [
+ "%&+-%&+-"
+ ],
+ "category": "Databases",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "%&+-",
+ "vendorRelease": "%&+-",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_1.0.1",
+ "resourceName": "1",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436080002091,
+ "lastUpdateDate": 1436080002091,
+ "description": "1",
+ "icon": "icon-red2",
+ "tags": [
+ "11"
+ ],
+ "category": "Databases",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "1",
+ "vendorRelease": "1",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_jjjjjj.0.1",
+ "resourceName": "jjjjjj",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436080425475,
+ "lastUpdateDate": 1436080425475,
+ "description": "h",
+ "icon": "icon-red2",
+ "tags": [
+ "jjjjjj"
+ ],
+ "category": "Collaboration",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebServer"
+ ],
+ "artifacts": {
+ "10 numbers limit reached": {
+ "uniqueId": "res_jjjjjj.0.1.10 numbers limit reached",
+ "artifactType": "CHEF",
+ "artifactName": "10 numbers limit reached.png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436081038417,
+ "lastUpdateDate": 1436081038417,
+ "esId": "res_jjjjjj.0.1:10 numbers limit reached.png",
+ "logicalName": "10 numbers limit reached",
+ "description": "dsfsdfs"
+ }
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.WebApplication"
+ ]
+ },
+ "admin_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.admin_endpoint",
+ "type": "tosca.capabilities.Endpoint.Admin"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ },
+ "data_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.data_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "g",
+ "vendorRelease": "f",
+ "contactId": "aa1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_qfdsy.0.1",
+ "resourceName": "qfdsy",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436084743821,
+ "lastUpdateDate": 1436084743821,
+ "description": "€Â‚ƒ„…†‡ˆ‰Š‹ŒÂŽÂ‘’“â€â€¢â€“—˜™š›œÂžŸ ¡¢£¤¥¦§¨©ª«¬-®¯°±²³´µ¶•¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
+ "icon": "icon-red2",
+ "tags": [
+ "qfdsytag"
+ ],
+ "category": "Messaging",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.4",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_nqstw.0.1",
+ "resourceName": "nqstw",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436084798999,
+ "lastUpdateDate": 1436084798999,
+ "description": "€Â‚ƒ„…†‡ˆ‰Š‹ŒÂŽÂ‘’“â€â€¢â€“—˜™š›œÂžŸ ¡¢£¤¥¦§¨©ª«¬-®¯°±²³´µ¶•¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
+ "icon": "icon-red2",
+ "tags": [
+ "nqstwtag"
+ ],
+ "category": "WAN Connectivity",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.4",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_rfpjx.0.1",
+ "resourceName": "rfpjx",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436084844179,
+ "lastUpdateDate": 1436084844179,
+ "description": "€Â‚ƒ„…†‡ˆ‰Š‹ŒÂŽÂ‘’“â€â€¢â€“—˜™š›œÂžŸ ¡¢£¤¥¦§¨©ª«¬-®¯°±²³´µ¶•¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ×ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
+ "icon": "icon-red2",
+ "tags": [
+ "rfpjxtag"
+ ],
+ "category": "Messaging",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.4",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_martin22.0.1",
+ "resourceName": "martin22",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436093847314,
+ "lastUpdateDate": 1436093847314,
+ "description": "desc",
+ "icon": "icon-red2",
+ "tags": [
+ "martin22",
+ "martin22tag"
+ ],
+ "category": "Security",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+ "gizmorambo": {
+ "uniqueId": "res_martin22.0.1.gizmorambo",
+ "artifactType": "YANG",
+ "artifactName": "gizmorambo.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436093856933,
+ "lastUpdateDate": 1436093856933,
+ "esId": "res_martin22.0.1:gizmorambo.jpg",
+ "logicalName": "gizmorambo",
+ "description": "BlaBla"
+ }
+ },
+ "properties": {
+ "Test1": {
+ "uniqueId": "res_martin22.0.1.Test1",
+ "type": "string",
+ "required": false,
+ "defaultValue": "10",
+ "description": "BlaBla",
+ "constraints": [
+ {
+ "rangeMinValue": "100",
+ "rangeMaxValue": "990"
+ }
+ ],
+ "definition": true,
+ "password": false
+ }
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.5",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_jkjk.0.1",
+ "resourceName": "jkjk",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436095288548,
+ "lastUpdateDate": 1436095288548,
+ "description": "jkjkj",
+ "icon": "icon-red2",
+ "tags": [
+ "jkjk"
+ ],
+ "category": "Big Data",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Container.Runtime"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.container.runtime.1.0.host",
+ "type": "tosca.capabilities.Container"
+ },
+ "scalable": {
+ "uniqueId": "capability.res_tosca.nodes.container.runtime.1.0.scalable",
+ "type": "tosca.capabilities.Scalable"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "gggg",
+ "vendorRelease": "fffff",
+ "contactId": "ff1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_bvbvcxb.0.1",
+ "resourceName": "bvbvcxb",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436164182890,
+ "lastUpdateDate": 1436164182890,
+ "description": "vcbcv",
+ "icon": "icon-red2",
+ "tags": [
+ "bvbvcxb"
+ ],
+ "category": "Call Control",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebServer"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.WebApplication"
+ ]
+ },
+ "admin_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.admin_endpoint",
+ "type": "tosca.capabilities.Endpoint.Admin"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ },
+ "data_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.data_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "vcbxcvb",
+ "vendorRelease": "cvbvc",
+ "contactId": "qw1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_sd.0.1",
+ "resourceName": "sd",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436255440040,
+ "lastUpdateDate": 1436255440040,
+ "description": "de",
+ "icon": "icon-red3",
+ "tags": [
+ "sdde"
+ ],
+ "category": "Call Control",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "ded",
+ "vendorRelease": "ede",
+ "contactId": "dd2222",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_martin221.0.1",
+ "resourceName": "martin221",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436275310912,
+ "lastUpdateDate": 1436275310912,
+ "description": "desc",
+ "icon": "icon-red2",
+ "tags": [
+ "martin221",
+ "martin221tag"
+ ],
+ "category": "Security",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebServer"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.WebApplication"
+ ]
+ },
+ "admin_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.admin_endpoint",
+ "type": "tosca.capabilities.Endpoint.Admin"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ },
+ "data_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.data_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.5",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_gfnfgf.0.1",
+ "resourceName": "gfnfgf",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436275654252,
+ "lastUpdateDate": 1436275654252,
+ "description": "hfghfgh",
+ "icon": "icon-red2",
+ "tags": [
+ "gfnfgf"
+ ],
+ "category": "Call Control",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebServer"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.WebApplication"
+ ]
+ },
+ "admin_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.admin_endpoint",
+ "type": "tosca.capabilities.Endpoint.Admin"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ },
+ "data_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.data_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "gfhgf",
+ "vendorRelease": "gfhgf",
+ "contactId": "gg1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_ddd.0.1",
+ "resourceName": "ddd",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436338105711,
+ "lastUpdateDate": 1436338105711,
+ "description": "dsff",
+ "icon": "icon-red1",
+ "tags": [
+ "sddd"
+ ],
+ "category": "Big Data",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.SoftwareComponent"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "sadfasdf",
+ "vendorRelease": "adsfadsf",
+ "contactId": "ds200p",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_hhdfd.0.1",
+ "resourceName": "hhdfd",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436342424025,
+ "lastUpdateDate": 1436342424025,
+ "description": "fdhdffh",
+ "icon": "icon-red2",
+ "tags": [
+ "hhdfd"
+ ],
+ "category": "Big Data",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "fdhfddfh",
+ "vendorRelease": "fdhdf",
+ "contactId": "fg1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_infobae.0.1",
+ "resourceName": "infobae",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436346185743,
+ "lastUpdateDate": 1436346185743,
+ "description": "es el",
+ "icon": "icon-red2",
+ "tags": [
+ "infobae"
+ ],
+ "category": "Big Data",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebApplication"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "app_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webapplication.1.0.app_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.webapplication.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.WebServer",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "ffff",
+ "vendorRelease": "ffff",
+ "contactId": "ss1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_df.0.1",
+ "resourceName": "df",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436352980564,
+ "lastUpdateDate": 1436352980564,
+ "description": "sdf",
+ "icon": "icon-red2",
+ "tags": [
+ "dfsdf"
+ ],
+ "category": "Messaging",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebServer"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.WebApplication"
+ ]
+ },
+ "admin_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.admin_endpoint",
+ "type": "tosca.capabilities.Endpoint.Admin"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ },
+ "data_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.data_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "sdf",
+ "vendorRelease": "sdf",
+ "contactId": "jh7654",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_ddddd.0.1",
+ "resourceName": "ddddd",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436353021637,
+ "lastUpdateDate": 1436353021637,
+ "description": "ddsd",
+ "icon": "icon-red2",
+ "tags": [
+ "ddddd"
+ ],
+ "category": "Compute as a Service",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.Root"
+ ],
+ "artifacts": {
+ "misdevices": {
+ "uniqueId": "res_ddddd.0.1.misdevices",
+ "artifactType": "PUPPET",
+ "artifactName": "misdevices.docx",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436353036161,
+ "lastUpdateDate": 1436353036161,
+ "esId": "res_ddddd.0.1:misdevices.docx",
+ "logicalName": "misdevices",
+ "description": "eeeeee"
+ }
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ }
+ },
+ "vendorName": "dfsd",
+ "vendorRelease": "ddddd",
+ "contactId": "ss1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_yo.0.1",
+ "resourceName": "yo",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436369145382,
+ "lastUpdateDate": 1436369145382,
+ "description": "dddd",
+ "icon": "icon-red2",
+ "tags": [
+ "yo"
+ ],
+ "category": "Collaboration",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.WebServer"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.WebApplication"
+ ]
+ },
+ "admin_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.admin_endpoint",
+ "type": "tosca.capabilities.Endpoint.Admin"
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ },
+ "data_endpoint": {
+ "uniqueId": "capability.res_tosca.nodes.webserver.1.0.data_endpoint",
+ "type": "tosca.capabilities.Endpoint"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "dds",
+ "vendorRelease": "ssss",
+ "contactId": "ss1234",
+ "abstract": false,
+ "highestVersion": true
+ },
+ {
+ "uniqueId": "res_martin2213.0.1",
+ "resourceName": "martin2213",
+ "resourceVersion": "0.1",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "creationDate": 1436369231581,
+ "lastUpdateDate": 1436369231581,
+ "description": "desc",
+ "icon": "icon-red2",
+ "tags": [
+ "martin2213",
+ "martin2213tag"
+ ],
+ "category": "Messaging",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT",
+ "derivedFrom": [
+ "tosca.nodes.DBMS"
+ ],
+ "artifacts": {
+
+ },
+ "interfaces": {
+
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.res_tosca.nodes.dbms.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.Database"
+ ]
+ },
+ "feature": {
+ "uniqueId": "capability.res_tosca.nodes.root.1.0.feature",
+ "type": "tosca.capabilities.Node"
+ }
+ },
+ "requirements": {
+ "dependency": {
+ "uniqueId": "res_tosca.nodes.root.1.0.dependency",
+ "capability": "tosca.capabilities.Node",
+ "node": "tosca.nodes.Root",
+ "relationship": "tosca.relationships.DependsOn"
+ },
+ "host": {
+ "uniqueId": "res_tosca.nodes.softwarecomponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "relationship": "tosca.relationships.HostedOn"
+ }
+ },
+ "vendorName": "Oracle",
+ "vendorRelease": "1.2.5",
+ "contactId": "Al1234",
+ "abstract": false,
+ "highestVersion": true
+ }
+ ],
+ "services": [
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_wduma.0.1",
+ "name": "wduma",
+ "version": "0.1",
+ "creationDate": 1436081587500,
+ "lastUpdateDate": 1436081587500,
+ "description": "BLa BLa",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "wduma",
+ "wdumatag"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Databases",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "wduma",
+ "creationDate": 1436081587500,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "BLa BLa",
+ "tags": [
+ "wduma",
+ "wdumatag"
+ ],
+ "uniqueId": "svc_wduma.0.1",
+ "lastUpdateDate": 1436081587500,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_r.0.1",
+ "name": "r",
+ "version": "0.1",
+ "creationDate": 1436083434172,
+ "lastUpdateDate": 1436083434172,
+ "description": "r",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "rrrr"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "IoT",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "r",
+ "creationDate": 1436083434172,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "r",
+ "tags": [
+ "rrrr"
+ ],
+ "uniqueId": "svc_r.0.1",
+ "lastUpdateDate": 1436083434172,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_otdiz.0.1",
+ "name": "otdiz",
+ "version": "0.1",
+ "creationDate": 1436094819610,
+ "lastUpdateDate": 1436094819610,
+ "description": "BLa BLa",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "otdiz",
+ "otdiztag"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Platform as a Service",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "otdiz",
+ "creationDate": 1436094819610,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "BLa BLa",
+ "tags": [
+ "otdiz",
+ "otdiztag"
+ ],
+ "uniqueId": "svc_otdiz.0.1",
+ "lastUpdateDate": 1436094819610,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_gxkvl.0.1",
+ "name": "gxkvl",
+ "version": "0.1",
+ "creationDate": 1436094992972,
+ "lastUpdateDate": 1436094992972,
+ "description": "BLa BLa",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "gxkvl",
+ "gxkvltag"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Security",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "gxkvl",
+ "creationDate": 1436094992972,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "BLa BLa",
+ "tags": [
+ "gxkvl",
+ "gxkvltag"
+ ],
+ "uniqueId": "svc_gxkvl.0.1",
+ "lastUpdateDate": 1436094992972,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_testservice.0.1",
+ "name": "testService",
+ "version": "0.1",
+ "creationDate": 1436098158942,
+ "lastUpdateDate": 1436098158942,
+ "description": "description",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "tag1"
+ ],
+ "icon": "myICON.jpg",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "install_apache": {
+ "uniqueId": "svc_testservice.0.1.install_apache",
+ "artifactType": "SHELL",
+ "artifactName": "install_apache.sh",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436098195328,
+ "lastUpdateDate": 1436098195328,
+ "esId": "svc_testservice.0.1:install_apache.sh",
+ "logicalName": "install_apache"
+ }
+ },
+ "category": "Databases",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "testService",
+ "creationDate": 1436098158942,
+ "icon": "myICON.jpg",
+ "version": "0.1",
+ "description": "description",
+ "tags": [
+ "tag1"
+ ],
+ "uniqueId": "svc_testservice.0.1",
+ "lastUpdateDate": 1436098158942,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_newtestservice5.0.1",
+ "name": "newTestService5",
+ "version": "0.1",
+ "creationDate": 1436111412660,
+ "lastUpdateDate": 1436111412660,
+ "description": "service Description",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "newTestService5",
+ "serviceTag"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Big Data",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "newTestService5",
+ "creationDate": 1436111412660,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "service Description",
+ "tags": [
+ "newTestService5",
+ "serviceTag"
+ ],
+ "uniqueId": "svc_newtestservice5.0.1",
+ "lastUpdateDate": 1436111412660,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_newtestservice.0.1",
+ "name": "newTestService",
+ "version": "0.1",
+ "creationDate": 1436114463660,
+ "lastUpdateDate": 1436114463660,
+ "description": "service Description",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "serviceTag"
+ ],
+ "icon": "myIcon.jpg",
+ "vendorName": "Oracle",
+ "vendorRelease": "0.1",
+ "contactId": "al1976",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Big Data",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "newTestService",
+ "creationDate": 1436114463660,
+ "icon": "myIcon.jpg",
+ "version": "0.1",
+ "description": "service Description",
+ "tags": [
+ "serviceTag"
+ ],
+ "uniqueId": "svc_newtestservice.0.1",
+ "lastUpdateDate": 1436114463660,
+ "contactId": "al1976",
+ "vendorName": "Oracle",
+ "vendorRelease": "0.1",
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_éø⦹.0.1",
+ "name": "éø¦¹",
+ "version": "0.1",
+ "creationDate": 1436168481712,
+ "lastUpdateDate": 1436168481712,
+ "description": "service Description",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "éø¦¹",
+ "éø¦¹serviceTag"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Big Data",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "éø¦¹",
+ "creationDate": 1436168481712,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "service Description",
+ "tags": [
+ "éø¦¹",
+ "éø¦¹serviceTag"
+ ],
+ "uniqueId": "svc_éø⦹.0.1",
+ "lastUpdateDate": 1436168481712,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_mmmm.0.1",
+ "name": "mmmm",
+ "version": "0.1",
+ "creationDate": 1436175721980,
+ "lastUpdateDate": 1436175721980,
+ "description": "jjjj",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "mmmm"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "Subtitle": {
+ "uniqueId": "svc_mmmm.0.1.Subtitle",
+ "artifactType": "SHELL",
+ "artifactName": "Subtitle.png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436180446882,
+ "lastUpdateDate": 1436180446882,
+ "esId": "svc_mmmm.0.1:Subtitle.png",
+ "logicalName": "Subtitle",
+ "description": "dfdssfsd"
+ },
+ "10 numbers limit reached": {
+ "uniqueId": "svc_mmmm.0.1.10 numbers limit reached",
+ "artifactType": "SHELL",
+ "artifactName": "10 numbers limit reached.png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436178322094,
+ "lastUpdateDate": 1436178322094,
+ "esId": "svc_mmmm.0.1:10 numbers limit reached.png",
+ "logicalName": "10 numbers limit reached",
+ "description": "hdfhdfhddf"
+ },
+ "Screenshot_2014-09-08-13-07-49": {
+ "uniqueId": "svc_mmmm.0.1.Screenshot_2014-09-08-13-07-49",
+ "artifactType": "SHELL",
+ "artifactName": "Screenshot_2014-09-08-13-07-49.png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436180394486,
+ "lastUpdateDate": 1436180394486,
+ "esId": "svc_mmmm.0.1:Screenshot_2014-09-08-13-07-49.png",
+ "logicalName": "Screenshot_2014-09-08-13-07-49",
+ "description": "fdgfdgfdg"
+ },
+ "Slow upload ": {
+ "uniqueId": "svc_mmmm.0.1.Slow upload ",
+ "artifactType": "SHELL",
+ "artifactName": "Slow upload .png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436180490467,
+ "lastUpdateDate": 1436180490467,
+ "esId": "svc_mmmm.0.1:Slow upload .png",
+ "logicalName": "Slow upload ",
+ "description": "sdffdsfds"
+ },
+ "img009": {
+ "uniqueId": "svc_mmmm.0.1.img009",
+ "artifactType": "PUPPET",
+ "artifactName": "img009.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436178135843,
+ "lastUpdateDate": 1436178135843,
+ "esId": "svc_mmmm.0.1:img009.jpg",
+ "logicalName": "img009",
+ "description": "hhhhh"
+ },
+ "casa1": {
+ "uniqueId": "svc_mmmm.0.1.casa1",
+ "artifactType": "HEAT",
+ "artifactName": "casa1.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436180424159,
+ "lastUpdateDate": 1436180424159,
+ "esId": "svc_mmmm.0.1:casa1.JPG",
+ "logicalName": "casa1",
+ "description": "fgsdfsdsdf"
+ }
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "mmmm",
+ "creationDate": 1436175721980,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "jjjj",
+ "tags": [
+ "mmmm"
+ ],
+ "uniqueId": "svc_mmmm.0.1",
+ "lastUpdateDate": 1436175721980,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fgfdgdfg.0.1",
+ "name": "fgfdgdfg",
+ "version": "0.1",
+ "creationDate": 1436189750913,
+ "lastUpdateDate": 1436189750913,
+ "description": "fgdfgdf",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fgfdgdfg"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Databases",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fgfdgdfg",
+ "creationDate": 1436189750913,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "fgdfgdf",
+ "tags": [
+ "fgfdgdfg"
+ ],
+ "uniqueId": "svc_fgfdgdfg.0.1",
+ "lastUpdateDate": 1436189750913,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_jujuy.0.1",
+ "name": "jujuy",
+ "version": "0.1",
+ "creationDate": 1436190515151,
+ "lastUpdateDate": 1436190515151,
+ "description": "gfhdf",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "jujuyggg"
+ ],
+ "icon": "network_l_1-3",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "ServicePanel": {
+ "uniqueId": "svc_jujuy.0.1.ServicePanel",
+ "artifactType": "SHELL",
+ "artifactName": "ServicePanel.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436191109314,
+ "lastUpdateDate": 1436191109314,
+ "esId": "svc_jujuy.0.1:ServicePanel.JPG",
+ "logicalName": "ServicePanel",
+ "description": "ddddddddddddddddddd"
+ },
+ "IconEnd": {
+ "uniqueId": "svc_jujuy.0.1.IconEnd",
+ "artifactType": "PUPPET",
+ "artifactName": "IconEnd.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436190528852,
+ "lastUpdateDate": 1436190528852,
+ "esId": "svc_jujuy.0.1:IconEnd.JPG",
+ "logicalName": "IconEnd",
+ "description": "fffffff"
+ },
+ "IconCenter": {
+ "uniqueId": "svc_jujuy.0.1.IconCenter",
+ "artifactType": "SHELL",
+ "artifactName": "IconCenter.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436190584441,
+ "lastUpdateDate": 1436190584441,
+ "esId": "svc_jujuy.0.1:IconCenter.JPG",
+ "logicalName": "IconCenter",
+ "description": "fffff"
+ },
+ "udateresource": {
+ "uniqueId": "svc_jujuy.0.1.udateresource",
+ "artifactType": "YANG",
+ "artifactName": "udateresource.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436191131313,
+ "lastUpdateDate": 1436191131313,
+ "esId": "svc_jujuy.0.1:udateresource.JPG",
+ "logicalName": "udateresource",
+ "description": "dddd"
+ },
+ "toobolar": {
+ "uniqueId": "svc_jujuy.0.1.toobolar",
+ "artifactType": "SHELL",
+ "artifactName": "toobolar.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436191146471,
+ "lastUpdateDate": 1436191146471,
+ "esId": "svc_jujuy.0.1:toobolar.JPG",
+ "logicalName": "toobolar",
+ "description": "sssssssssssssss"
+ }
+ },
+ "category": "Collaboration",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "jujuy",
+ "creationDate": 1436190515151,
+ "icon": "network_l_1-3",
+ "version": "0.1",
+ "description": "gfhdf",
+ "tags": [
+ "jujuyggg"
+ ],
+ "uniqueId": "svc_jujuy.0.1",
+ "lastUpdateDate": 1436190515151,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_martin39.0.1",
+ "name": "martin39",
+ "version": "0.1",
+ "creationDate": 1436252371041,
+ "lastUpdateDate": 1436252371041,
+ "description": "fffffff",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "martin39"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "toobolar": {
+ "uniqueId": "svc_martin39.0.1.toobolar",
+ "artifactType": "SHELL",
+ "artifactName": "toobolar.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436252434415,
+ "lastUpdateDate": 1436252434415,
+ "esId": "svc_martin39.0.1:toobolar.JPG",
+ "logicalName": "toobolar",
+ "description": "ffff"
+ }
+ },
+ "category": "Compute as a Service",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "martin39",
+ "creationDate": 1436252371041,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "fffffff",
+ "tags": [
+ "martin39"
+ ],
+ "uniqueId": "svc_martin39.0.1",
+ "lastUpdateDate": 1436252371041,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_asdasd.0.1",
+ "name": "asdasd",
+ "version": "0.1",
+ "creationDate": 1436253177668,
+ "lastUpdateDate": 1436253177668,
+ "description": "asdasd",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "asdasd"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Collaboration",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "asdasd",
+ "creationDate": 1436253177668,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "asdasd",
+ "tags": [
+ "asdasd"
+ ],
+ "uniqueId": "svc_asdasd.0.1",
+ "lastUpdateDate": 1436253177668,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fgdfgfd.0.1",
+ "name": "fgdfgfd",
+ "version": "0.1",
+ "creationDate": 1436255258481,
+ "lastUpdateDate": 1436255258481,
+ "description": "dfgdfg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fgdfgfd"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Compute as a Service",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fgdfgfd",
+ "creationDate": 1436255258481,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "dfgdfg",
+ "tags": [
+ "fgdfgfd"
+ ],
+ "uniqueId": "svc_fgdfgfd.0.1",
+ "lastUpdateDate": 1436255258481,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fdgd.0.1",
+ "name": "fdgd",
+ "version": "0.1",
+ "creationDate": 1436257427821,
+ "lastUpdateDate": 1436257427821,
+ "description": "fdg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fdgd"
+ ],
+ "icon": "call_controll",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "toobolar": {
+ "uniqueId": "svc_fdgd.0.1.toobolar",
+ "artifactType": "SHELL",
+ "artifactName": "toobolar.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436257614747,
+ "lastUpdateDate": 1436257614747,
+ "esId": "svc_fdgd.0.1:toobolar.JPG",
+ "logicalName": "toobolar",
+ "description": "dfsdfsd"
+ }
+ },
+ "category": "Compute as a Service",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fdgd",
+ "creationDate": 1436257427821,
+ "icon": "call_controll",
+ "version": "0.1",
+ "description": "fdg",
+ "tags": [
+ "fdgd"
+ ],
+ "uniqueId": "svc_fdgd.0.1",
+ "lastUpdateDate": 1436257427821,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fdgdfgfd.0.1",
+ "name": "fdgdfgfd",
+ "version": "0.1",
+ "creationDate": 1436266653797,
+ "lastUpdateDate": 1436266653797,
+ "description": "fdfdgf",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fdgdfgfd"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "IMG_20141222_072027": {
+ "uniqueId": "svc_fdgdfgfd.0.1.IMG_20141222_072027",
+ "artifactType": "SHELL",
+ "artifactName": "IMG_20141222_072027.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436267970999,
+ "lastUpdateDate": 1436267970999,
+ "esId": "svc_fdgdfgfd.0.1:IMG_20141222_072027.jpg",
+ "logicalName": "IMG_20141222_072027",
+ "description": "FEDF"
+ }
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fdgdfgfd",
+ "creationDate": 1436266653797,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "fdfdgf",
+ "tags": [
+ "fdgdfgfd"
+ ],
+ "uniqueId": "svc_fdgdfgfd.0.1",
+ "lastUpdateDate": 1436266653797,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fbgvdfb.0.1",
+ "name": "fbgvdfb",
+ "version": "0.1",
+ "creationDate": 1436267394596,
+ "lastUpdateDate": 1436267394596,
+ "description": "sdbdbs",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fbgvdfb"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "IMG_20140413_185621": {
+ "uniqueId": "svc_fbgvdfb.0.1.IMG_20140413_185621",
+ "artifactType": "YANG",
+ "artifactName": "IMG_20140413_185621.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436267701461,
+ "lastUpdateDate": 1436267701461,
+ "esId": "svc_fbgvdfb.0.1:IMG_20140413_185621.jpg",
+ "logicalName": "IMG_20140413_185621",
+ "description": "ACSCA"
+ },
+ "IMG_20140413_185643": {
+ "uniqueId": "svc_fbgvdfb.0.1.IMG_20140413_185643",
+ "artifactType": "CHEF",
+ "artifactName": "IMG_20140413_185643.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436267871628,
+ "lastUpdateDate": 1436267871628,
+ "esId": "svc_fbgvdfb.0.1:IMG_20140413_185643.jpg",
+ "logicalName": "IMG_20140413_185643",
+ "description": "ASDFAWSE"
+ }
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fbgvdfb",
+ "creationDate": 1436267394596,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "sdbdbs",
+ "tags": [
+ "fbgvdfb"
+ ],
+ "uniqueId": "svc_fbgvdfb.0.1",
+ "lastUpdateDate": 1436267394596,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_asdasds.0.1",
+ "name": "asdasds",
+ "version": "0.1",
+ "creationDate": 1436268972057,
+ "lastUpdateDate": 1436268972057,
+ "description": "asdasdasd",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "asdasds"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Collaboration",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "asdasds",
+ "creationDate": 1436268972057,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "asdasdasd",
+ "tags": [
+ "asdasds"
+ ],
+ "uniqueId": "svc_asdasds.0.1",
+ "lastUpdateDate": 1436268972057,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_kkkkk.0.1",
+ "name": "kkkkk",
+ "version": "0.1",
+ "creationDate": 1436271848416,
+ "lastUpdateDate": 1436271848416,
+ "description": "hggh",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "kkkkk"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "dessaper": {
+ "uniqueId": "svc_kkkkk.0.1.dessaper",
+ "artifactType": "SHELL",
+ "artifactName": "dessaper.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436271866025,
+ "lastUpdateDate": 1436271866025,
+ "esId": "svc_kkkkk.0.1:dessaper.JPG",
+ "logicalName": "dessaper",
+ "description": "fgdf"
+ }
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "kkkkk",
+ "creationDate": 1436271848416,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "hggh",
+ "tags": [
+ "kkkkk"
+ ],
+ "uniqueId": "svc_kkkkk.0.1",
+ "lastUpdateDate": 1436271848416,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_alex.0.1",
+ "name": "alex",
+ "version": "0.1",
+ "creationDate": 1436276482974,
+ "lastUpdateDate": 1436276482974,
+ "description": "Bla Bla",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "alex",
+ "ddd"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "pencil": {
+ "uniqueId": "svc_alex.0.1.pencil",
+ "artifactType": "CHEF",
+ "artifactName": "pencil.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436276810898,
+ "lastUpdateDate": 1436276810898,
+ "esId": "svc_alex.0.1:pencil.JPG",
+ "logicalName": "pencil",
+ "description": "nk"
+ },
+ "Web Accessibility": {
+ "uniqueId": "svc_alex.0.1.Web Accessibility",
+ "artifactType": "HEAT",
+ "artifactName": "Web Accessibility.pptx",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436276689984,
+ "lastUpdateDate": 1436276689984,
+ "esId": "svc_alex.0.1:Web Accessibility.pptx",
+ "logicalName": "Web Accessibility",
+ "description": "dfhgf"
+ },
+ "toobolar": {
+ "uniqueId": "svc_alex.0.1.toobolar",
+ "artifactType": "CHEF",
+ "artifactName": "toobolar.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436276821186,
+ "lastUpdateDate": 1436276821186,
+ "esId": "svc_alex.0.1:toobolar.JPG",
+ "logicalName": "toobolar",
+ "description": "dgrt"
+ }
+ },
+ "category": "IoT",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "alex",
+ "creationDate": 1436276482974,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "Bla Bla",
+ "tags": [
+ "alex",
+ "ddd"
+ ],
+ "uniqueId": "svc_alex.0.1",
+ "lastUpdateDate": 1436276482974,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_martin392.0.1",
+ "name": "martin392",
+ "version": "0.1",
+ "creationDate": 1436276986104,
+ "lastUpdateDate": 1436276986104,
+ "description": "ddddd",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "martin392"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Collaboration",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "martin392",
+ "creationDate": 1436276986104,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "ddddd",
+ "tags": [
+ "martin392"
+ ],
+ "uniqueId": "svc_martin392.0.1",
+ "lastUpdateDate": 1436276986104,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_alex1.0.1",
+ "name": "Alex1",
+ "version": "0.1",
+ "creationDate": 1436277034499,
+ "lastUpdateDate": 1436277034499,
+ "description": "vvg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "Alex1"
+ ],
+ "icon": "network_l_1-3",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "IoT",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "Alex1",
+ "creationDate": 1436277034499,
+ "icon": "network_l_1-3",
+ "version": "0.1",
+ "description": "vvg",
+ "tags": [
+ "Alex1"
+ ],
+ "uniqueId": "svc_alex1.0.1",
+ "lastUpdateDate": 1436277034499,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_alex2.0.1",
+ "name": "Alex2",
+ "version": "0.1",
+ "creationDate": 1436277071235,
+ "lastUpdateDate": 1436277071235,
+ "description": "sas",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "Alex2"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Big Data",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "Alex2",
+ "creationDate": 1436277071235,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "sas",
+ "tags": [
+ "Alex2"
+ ],
+ "uniqueId": "svc_alex2.0.1",
+ "lastUpdateDate": 1436277071235,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_gfhgfhfg.0.1",
+ "name": "gfhgfhfg",
+ "version": "0.1",
+ "creationDate": 1436337425800,
+ "lastUpdateDate": 1436337425800,
+ "description": "fghg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "gfhgfhgfgfhfg"
+ ],
+ "icon": "network_l_1-3",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "Footer_menu": {
+ "uniqueId": "svc_gfhgfhfg.0.1.Footer_menu",
+ "artifactType": "SHELL",
+ "artifactName": "Footer_menu.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436337460108,
+ "lastUpdateDate": 1436337460108,
+ "esId": "svc_gfhgfhfg.0.1:Footer_menu.JPG",
+ "logicalName": "Footer_menu",
+ "description": "gfhgfh"
+ }
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "gfhgfhfg",
+ "creationDate": 1436337425800,
+ "icon": "network_l_1-3",
+ "version": "0.1",
+ "description": "fghg",
+ "tags": [
+ "gfhgfhgfgfhfg"
+ ],
+ "uniqueId": "svc_gfhgfhfg.0.1",
+ "lastUpdateDate": 1436337425800,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fffffdd.0.1",
+ "name": "fffffdd",
+ "version": "0.1",
+ "creationDate": 1436342516321,
+ "lastUpdateDate": 1436342516321,
+ "description": "fdgg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fffffdd"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fffffdd",
+ "creationDate": 1436342516321,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "fdgg",
+ "tags": [
+ "fffffdd"
+ ],
+ "uniqueId": "svc_fffffdd.0.1",
+ "lastUpdateDate": 1436342516321,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_martin.0.1",
+ "name": "martin",
+ "version": "0.1",
+ "creationDate": 1436342857036,
+ "lastUpdateDate": 1436342857036,
+ "description": "gggg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "martin"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "martin",
+ "creationDate": 1436342857036,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "gggg",
+ "tags": [
+ "martin"
+ ],
+ "uniqueId": "svc_martin.0.1",
+ "lastUpdateDate": 1436342857036,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_fghj.0.1",
+ "name": "fghj",
+ "version": "0.1",
+ "creationDate": 1436343201655,
+ "lastUpdateDate": 1436343201655,
+ "description": "fghjfhj",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "fghj"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Big Data",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "fghj",
+ "creationDate": 1436343201655,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "fghjfhj",
+ "tags": [
+ "fghj"
+ ],
+ "uniqueId": "svc_fghj.0.1",
+ "lastUpdateDate": 1436343201655,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_lol.0.1",
+ "name": "lol",
+ "version": "0.1",
+ "creationDate": 1436343380208,
+ "lastUpdateDate": 1436343380208,
+ "description": "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggddddddddddddddddddddddddddddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssss",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "lol"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "10 numbers limit reached": {
+ "uniqueId": "svc_lol.0.1.10 numbers limit reached",
+ "artifactType": "SHELL",
+ "artifactName": "10 numbers limit reached.png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436344950166,
+ "lastUpdateDate": 1436344950166,
+ "esId": "svc_lol.0.1:10 numbers limit reached.png",
+ "logicalName": "10 numbers limit reached",
+ "description": "yyyyyyy"
+ },
+ "Addproperty": {
+ "uniqueId": "svc_lol.0.1.Addproperty",
+ "artifactType": "PUPPET",
+ "artifactName": "Addproperty.JPG",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436344939800,
+ "lastUpdateDate": 1436344939800,
+ "esId": "svc_lol.0.1:Addproperty.JPG",
+ "logicalName": "Addproperty",
+ "description": "hhhhhhh"
+ },
+ "IMG_20140418_142614": {
+ "uniqueId": "svc_lol.0.1.IMG_20140418_142614",
+ "artifactType": "SHELL",
+ "artifactName": "IMG_20140418_142614.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436344895625,
+ "lastUpdateDate": 1436344895625,
+ "esId": "svc_lol.0.1:IMG_20140418_142614.jpg",
+ "logicalName": "IMG_20140418_142614",
+ "description": "bbbbbb"
+ },
+ "IMG_20140618_195014": {
+ "uniqueId": "svc_lol.0.1.IMG_20140618_195014",
+ "artifactType": "CHEF",
+ "artifactName": "IMG_20140618_195014.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436344912378,
+ "lastUpdateDate": 1436344912378,
+ "esId": "svc_lol.0.1:IMG_20140618_195014.jpg",
+ "logicalName": "IMG_20140618_195014",
+ "description": "bbbbb"
+ },
+ "IMG_20161213_222041": {
+ "uniqueId": "svc_lol.0.1.IMG_20161213_222041",
+ "artifactType": "HEAT",
+ "artifactName": "IMG_20161213_222041.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436343693049,
+ "lastUpdateDate": 1436343693049,
+ "esId": "svc_lol.0.1:IMG_20161213_222041.jpg",
+ "logicalName": "IMG_20161213_222041",
+ "description": "jjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjjjjjjjjjjjjjjjjjjdfjjjjjjj4444444444444444444444444444444444444444444444444444444444444444444"
+ },
+ "IMG_20140418_142634": {
+ "uniqueId": "svc_lol.0.1.IMG_20140418_142634",
+ "artifactType": "PUPPET",
+ "artifactName": "IMG_20140418_142634.jpg",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436344878280,
+ "lastUpdateDate": 1436344878280,
+ "esId": "svc_lol.0.1:IMG_20140418_142634.jpg",
+ "logicalName": "IMG_20140418_142634",
+ "description": "bbbbbb"
+ }
+ },
+ "category": "Web Servers",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "lol",
+ "creationDate": 1436343380208,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggddddddddddddddddddddddddddddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssssssssssswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpopopoddddssssssssss",
+ "tags": [
+ "lol"
+ ],
+ "uniqueId": "svc_lol.0.1",
+ "lastUpdateDate": 1436343380208,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_clarin.0.1",
+ "name": "clarin",
+ "version": "0.1",
+ "creationDate": 1436346144421,
+ "lastUpdateDate": 1436346144421,
+ "description": "soy yo",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "clarin"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Infrastructure",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "clarin",
+ "creationDate": 1436346144421,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "soy yo",
+ "tags": [
+ "clarin"
+ ],
+ "uniqueId": "svc_clarin.0.1",
+ "lastUpdateDate": 1436346144421,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_jkhkjh.0.1",
+ "name": "jkhkjh",
+ "version": "0.1",
+ "creationDate": 1436356292329,
+ "lastUpdateDate": 1436356292329,
+ "description": "kjhlk",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "jkhkjh"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+ "Screenshot_1": {
+ "uniqueId": "svc_jkhkjh.0.1.Screenshot_1",
+ "artifactType": "PUPPET",
+ "artifactName": "Screenshot_1.png",
+ "userIdLastUpdater": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "updaterFullName": "Carlos Santana",
+ "creationDate": 1436356484212,
+ "lastUpdateDate": 1436356484212,
+ "esId": "svc_jkhkjh.0.1:Screenshot_1.png",
+ "logicalName": "Screenshot_1",
+ "description": "zfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdhzfvbgsadfgsdhsdghsdhdsghsdh"
+ }
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "resourceInstances": [
+ {
+ "uniqueId": "svc_jkhkjh.0.1.res_tosca.nodes.compute.1.0.compute_1",
+ "name": "compute_1",
+ "resourceUid": "res_tosca.nodes.compute.1.0",
+ "creationTime": 1436356602608,
+ "modificationTime": 1436356602608,
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute\r\t node will be used to find the machine that fits the given requirements in the cloud\r\t available machines. If no sizing information are specified the cloud provider default\r\t machine will be used. It is strongly recommended to specify the required CPUs and memory\r\t at least.",
+ "posX": "781",
+ "posY": "241",
+ "icon": "defaulticon"
+ },
+ {
+ "uniqueId": "svc_jkhkjh.0.1.res_tosca.nodes.compute.1.0.compute_2",
+ "name": "compute_2",
+ "resourceUid": "res_tosca.nodes.compute.1.0",
+ "creationTime": 1436356789445,
+ "modificationTime": 1436356789445,
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute\r\t node will be used to find the machine that fits the given requirements in the cloud\r\t available machines. If no sizing information are specified the cloud provider default\r\t machine will be used. It is strongly recommended to specify the required CPUs and memory\r\t at least.",
+ "posX": "695",
+ "posY": "204",
+ "icon": "defaulticon"
+ },
+ {
+ "uniqueId": "svc_jkhkjh.0.1.res_tosca.nodes.network.network.1.0.network_3",
+ "name": "network_3",
+ "resourceUid": "res_tosca.nodes.network.network.1.0",
+ "creationTime": 1436356792215,
+ "modificationTime": 1436356792215,
+ "description": "Represents a simple , logical network service.",
+ "posX": "706",
+ "posY": "381",
+ "icon": "defaulticon"
+ },
+ {
+ "uniqueId": "svc_jkhkjh.0.1.res_tosca.nodes.network.port.1.0.port_4",
+ "name": "port_4",
+ "resourceUid": "res_tosca.nodes.network.port.1.0",
+ "creationTime": 1436356793840,
+ "modificationTime": 1436356793840,
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "posX": "662",
+ "posY": "552",
+ "icon": "defaulticon"
+ }
+ ],
+ "resourceInstancesRelations": [
+
+ ],
+ "serviceName": "jkhkjh",
+ "creationDate": 1436356292329,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "kjhlk",
+ "tags": [
+ "jkhkjh"
+ ],
+ "uniqueId": "svc_jkhkjh.0.1",
+ "lastUpdateDate": 1436356292329,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_resrw.0.1",
+ "name": "resrw",
+ "version": "0.1",
+ "creationDate": 1436358721893,
+ "lastUpdateDate": 1436358721893,
+ "description": "rer",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "resrwrer",
+ "lklkl"
+ ],
+ "icon": "network_l_1-3",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "resourceInstances": [
+ {
+ "uniqueId": "svc_resrw.0.1.res_tosca.nodes.compute.1.0.compute_1",
+ "name": "compute_1",
+ "resourceUid": "res_tosca.nodes.compute.1.0",
+ "creationTime": 1436358725759,
+ "modificationTime": 1436358725759,
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute\r\t node will be used to find the machine that fits the given requirements in the cloud\r\t available machines. If no sizing information are specified the cloud provider default\r\t machine will be used. It is strongly recommended to specify the required CPUs and memory\r\t at least.",
+ "posX": "221",
+ "posY": "251",
+ "icon": "defaulticon"
+ },
+ {
+ "uniqueId": "svc_resrw.0.1.res_tosca.nodes.network.network.1.0.network_2",
+ "name": "network_2",
+ "resourceUid": "res_tosca.nodes.network.network.1.0",
+ "creationTime": 1436358727329,
+ "modificationTime": 1436358727329,
+ "description": "Represents a simple , logical network service.",
+ "posX": "626",
+ "posY": "340",
+ "icon": "defaulticon"
+ },
+ {
+ "uniqueId": "svc_resrw.0.1.res_tosca.nodes.network.port.1.0.port_3",
+ "name": "port_3",
+ "resourceUid": "res_tosca.nodes.network.port.1.0",
+ "creationTime": 1436358729466,
+ "modificationTime": 1436358729466,
+ "description": "Represents a logical entity that associates between Compute and Network normative types.",
+ "posX": "558",
+ "posY": "275",
+ "icon": "defaulticon"
+ }
+ ],
+ "resourceInstancesRelations": [
+
+ ],
+ "serviceName": "resrw",
+ "creationDate": 1436358721893,
+ "icon": "network_l_1-3",
+ "version": "0.1",
+ "description": "rer",
+ "tags": [
+ "resrwrer",
+ "lklkl"
+ ],
+ "uniqueId": "svc_resrw.0.1",
+ "lastUpdateDate": 1436358721893,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_coco.0.1",
+ "name": "coco",
+ "version": "0.1",
+ "creationDate": 1436362653722,
+ "lastUpdateDate": 1436362653722,
+ "description": "hhh",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "coco"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "coco",
+ "creationDate": 1436362653722,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "hhh",
+ "tags": [
+ "coco"
+ ],
+ "uniqueId": "svc_coco.0.1",
+ "lastUpdateDate": 1436362653722,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_nermartin.0.1",
+ "name": "nerMartin",
+ "version": "0.1",
+ "creationDate": 1436366670488,
+ "lastUpdateDate": 1436366670488,
+ "description": "gggggggggg",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "nerMartin"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "IMS",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "nerMartin",
+ "creationDate": 1436366670488,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "gggggggggg",
+ "tags": [
+ "nerMartin"
+ ],
+ "uniqueId": "svc_nermartin.0.1",
+ "lastUpdateDate": 1436366670488,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ },
+ {
+ "componentMetadataDefinition": {
+ "uniqueId": "svc_susana.0.1",
+ "name": "susana",
+ "version": "0.1",
+ "creationDate": 1436367239625,
+ "lastUpdateDate": 1436367239625,
+ "description": "fff",
+ "state": "NOT_CERTIFIED_CHECKOUT",
+ "tags": [
+ "susana"
+ ],
+ "icon": "network_l_4",
+ "highestVersion": true
+ },
+ "artifacts": {
+
+ },
+ "category": "Call Control",
+ "creatorUserId": "cs0008",
+ "creatorFullName": "Carlos Santana",
+ "lastUpdaterUserId": "cs0008",
+ "lastUpdaterFullName": "Carlos Santana",
+ "serviceName": "susana",
+ "creationDate": 1436367239625,
+ "icon": "network_l_4",
+ "version": "0.1",
+ "description": "fff",
+ "tags": [
+ "susana"
+ ],
+ "uniqueId": "svc_susana.0.1",
+ "lastUpdateDate": 1436367239625,
+ "lifecycleState": "NOT_CERTIFIED_CHECKOUT"
+ }
+ ]
+}
diff --git a/catalog-ui/server-mock/mock-data/resource/properties.json b/catalog-ui/server-mock/mock-data/resource/properties.json
new file mode 100644
index 0000000000..8ed7e78970
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/resource/properties.json
@@ -0,0 +1,35 @@
+[
+ {
+ "uniqueId": "uniqueId_1",
+ "name": "disk_size",
+ "type": "integer",
+ "required": false,
+ "defaultValue": "10",
+ "description": "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.",
+ "constraints": [
+ {
+ "inRange": [
+ "100"
+ ]
+ }
+ ],
+ "isPassword": false
+ },
+ {
+ "uniqueId": "uniqueId_2",
+ "name": "num_cpus",
+ "type": "integer",
+ "required": false,
+ "defaultValue": "2",
+ "description": "Number of (actual or virtual) CPUs associated with the Compute node.",
+ "constraints": [
+ {
+ "inRange": [
+ "1",
+ "4"
+ ]
+ }
+ ],
+ "isPassword": false
+ }
+]
diff --git a/catalog-ui/server-mock/mock-data/resource/resource.json b/catalog-ui/server-mock/mock-data/resource/resource.json
new file mode 100644
index 0000000000..bc04f40730
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/resource/resource.json
@@ -0,0 +1,153 @@
+{
+ "uniqueId": "my-resource.1.0.0",
+ "isAbstract": false,
+ "resourceName": "my-resource",
+ "resourceVersion": "1.0.0",
+ "isHighestVersion": false,
+ "vendorName": "vendorName",
+ "vendorRelease": "vendorRelease",
+ "contactId": "contactId",
+ "olderVersions": [
+ "bla"
+ ],
+ "resourceIconSrc": "images/resource-icons/firewall.png",
+ "icon":"icon-red2",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh000322",
+ "lastUpdaterFullName": "JHaval Jimmy Hendrix",
+ "creationDate": 1431525184379,
+ "lastUpdateDate": 1431525184394,
+ "description": "Description bla bla",
+ "tags": [
+ "some tag",
+ "tag2",
+ "tag3",
+ "tag4"
+ ],
+ "category": "Infrastructure",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "template1"
+ ],
+ "artifacts": {
+ "db_content": {
+ "uniqueId": "my-resource.1.0.0.db_content",
+ "artifactType": "puppet",
+ "artifactRef": "http://swift OR CATALOG FE /myfile",
+ "artifactName": "some Artifact Name1",
+ "artifactRepository": "SWIFT",
+ "artifactData": "base 64 dajfvsd fsdfsfs s==",
+ "artifactChecksum": "base 64 dsdsd==",
+ "userIdCreator": "jh0003",
+ "userIdLastUpdater": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "updaterFullName": "Jimmy Hendrix",
+ "creationDate": 1431525184377,
+ "lastUpdateDate": 1431525184377
+ },
+ "db_content_2": {
+ "uniqueId": "my-resource.1.0.0.db_content_2",
+ "artifactType": "puppet",
+ "artifactRef": "http://swift OR CATALOG FE /myfile",
+ "artifactName": "some Artifact Name2",
+ "artifactRepository": "SWIFT",
+ "artifactData": "base 64 dajfvsd fsdfsfs s==",
+ "artifactChecksum": "base 64 dsdsd==",
+ "userIdCreator": "jh0003",
+ "userIdLastUpdater": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "updaterFullName": "Jimmy Hendrix",
+ "creationDate": 1431525184377,
+ "lastUpdateDate": 1431525184377
+ },
+ "db_content_3": {
+ "uniqueId": "my-resource.1.0.0.db_content_3",
+ "artifactType": "puppet",
+ "artifactRef": "http://swift OR CATALOG FE /myfile",
+ "artifactName": "some Artifact Name3",
+ "artifactRepository": "SWIFT",
+ "artifactData": "base 64 dajfvsd fsdfsfs s==",
+ "artifactChecksum": "base 64 dsdsd==",
+ "userIdCreator": "jh0003",
+ "userIdLastUpdater": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "updaterFullName": "Jimmy Hendrix",
+ "creationDate": 1431525184377,
+ "lastUpdateDate": 1431525184377
+ }
+ },
+ "properties": {
+ "disk_size": {
+ "uniqueId": "uniqueId_1",
+ "type": "integer",
+ "required": false,
+ "defaultValue": "10",
+ "description": "Size of the local disk, in Gigabytes (GB), available to applications running on the Compute node.",
+ "constraints": [
+ {
+ "inRange": [
+ "100"
+ ]
+ }
+ ],
+ "isPassword": false
+ },
+ "num_cpus": {
+ "uniqueId": "uniqueId_2",
+ "type": "integer",
+ "required": false,
+ "defaultValue": "2",
+ "description": "Number of (actual or virtual) CPUs associated with the Compute node.",
+ "constraints": [
+ {
+ "inRange": [
+ "1",
+ "4"
+ ]
+ }
+ ],
+ "isPassword": false
+ }
+ },
+ "attributes": {
+ "tmp": {}
+ },
+ "interfaces": {
+ "tmp": {
+ "operations": {}
+ }
+ },
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "host": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ },
+ "defaultCapabilities": [
+ "defaultCapabilities"
+ ]
+}
diff --git a/catalog-ui/server-mock/mock-data/resources/resourcesAbstract.json b/catalog-ui/server-mock/mock-data/resources/resourcesAbstract.json
new file mode 100644
index 0000000000..b678ea9b6b
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/resources/resourcesAbstract.json
@@ -0,0 +1,284 @@
+[
+ {
+ "uniqueId": "tosca.nodes.root.1.0",
+ "resourceName": "tosca.nodes.root",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412546396,
+ "lastUpdateDate": 1433412546396,
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "icon": "icon-red1",
+ "tags": [
+ "Root"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.softwarecomponent.1.0",
+ "resourceName": "tosca.nodes.softwarecomponent",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412547841,
+ "lastUpdateDate": 1433412547841,
+ "description": "Represents a generic software component that can be managed and run by a Compute Node Type.",
+ "icon": "icon-red2",
+ "tags": [
+ "Software Component"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "properties": {
+ "component_version": {
+ "uniqueId": "tosca.nodes.softwarecomponent.1.0.component_version",
+ "type": "version",
+ "required": false,
+ "definition": true,
+ "password": false
+ },
+ "admin_credential": {
+ "uniqueId": "tosca.nodes.softwarecomponent.1.0.admin_credential",
+ "type": "tosca.datatypes.Credential",
+ "required": false,
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.webserver.1.0",
+ "resourceName": "tosca.nodes.webserver",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412548140,
+ "lastUpdateDate": 1433412548140,
+ "description": "Represents an abstract software component or service that is capable of hosting and providing management operations for one or more Web Application nodes.",
+ "icon": "icon-red1",
+ "tags": [
+ "Web Server"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.softwarecomponent"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.webapplication.1.0",
+ "resourceName": "tosca.nodes.webapplication",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412548382,
+ "lastUpdateDate": 1433412548382,
+ "description": "Represents a software application that can be managed and run by a Web Server node. Specific types of web applications such as Java, etc. could be derived from this type.",
+ "icon": "icon-red3",
+ "tags": [
+ "Web Application"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "properties": {
+ "context_root": {
+ "uniqueId": "tosca.nodes.webapplication.1.0.context_root",
+ "type": "string",
+ "required": false,
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.dbms.1.0",
+ "resourceName": "tosca.nodes.dbms",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412548607,
+ "lastUpdateDate": 1433412548607,
+ "description": "Represents a typical relational, SQL Database Management System software component or service.",
+ "icon": "icon-red4",
+ "tags": [
+ "DBMS"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.softwarecomponent"
+ ],
+ "properties": {
+ "port": {
+ "uniqueId": "tosca.nodes.dbms.1.0.port",
+ "type": "integer",
+ "required": false,
+ "description": "the port the DBMS service will listen to for data and requests",
+ "definition": true,
+ "password": false
+ },
+ "root_password": {
+ "uniqueId": "tosca.nodes.dbms.1.0.root_password",
+ "type": "string",
+ "required": false,
+ "description": "the optional root password for the DBMS service",
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.database.1.0",
+ "resourceName": "tosca.nodes.database",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412548836,
+ "lastUpdateDate": 1433412548836,
+ "description": "Represents a logical database that can be managed and hosted by a DBMS node.",
+ "icon": "icon-red3",
+ "tags": [
+ "Database"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "properties": {
+ "port": {
+ "uniqueId": "tosca.nodes.database.1.0.port",
+ "type": "integer",
+ "required": false,
+ "description": "the port the underlying database service will listen to for data",
+ "definition": true,
+ "password": false
+ },
+ "name": {
+ "uniqueId": "tosca.nodes.database.1.0.name",
+ "type": "string",
+ "required": false,
+ "description": "the logical name of the database",
+ "definition": true,
+ "password": false
+ },
+ "user": {
+ "uniqueId": "tosca.nodes.database.1.0.user",
+ "type": "string",
+ "required": false,
+ "description": "the optional user account name for DB administration",
+ "definition": true,
+ "password": false
+ },
+ "password": {
+ "uniqueId": "tosca.nodes.database.1.0.password",
+ "type": "string",
+ "required": false,
+ "description": "the optional password for the DB user account",
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.container.runtime.1.0",
+ "resourceName": "tosca.nodes.container.runtime",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412549532,
+ "lastUpdateDate": 1433412549532,
+ "description": "Represents operating system-level virtualization technology used to run multiple application services on a single Compute host.",
+ "icon": "icon-red12",
+ "tags": [
+ "Container"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.softwarecomponent"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ },
+ {
+ "uniqueId": "tosca.nodes.container.application.1.0",
+ "resourceName": "tosca.nodes.container.application",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412549709,
+ "lastUpdateDate": 1433412549709,
+ "description": "Represents an application that requires Container-level virtualization technology.",
+ "icon": "icon-red4",
+ "tags": [
+ "Container Application"
+ ],
+ "category": "Abstract",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": true,
+ "highestVersion": false
+ }
+]
diff --git a/catalog-ui/server-mock/mock-data/resources/resourcesNotAbstract.json b/catalog-ui/server-mock/mock-data/resources/resourcesNotAbstract.json
new file mode 100644
index 0000000000..eab325c9cf
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/resources/resourcesNotAbstract.json
@@ -0,0 +1,510 @@
+[
+ {
+ "uniqueId": "tosca.nodes.compute.1.0",
+ "resourceName": "tosca.nodes.compute",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412547566,
+ "lastUpdateDate": 1433412547566,
+ "coordinates": {
+ "x":150,
+ "y":250
+ },
+ "description": "Represents a real or virtual machine or server. Information specified on the Compute\t node will be used to find the machine that fits the given requirements in the cloud\t available machines. If no sizing information are specified the cloud provider default\t machine will be used. It is strongly recommended to specify the required CPUs and memory\t at least.",
+ "icon": "icon-red2",
+ "tags": [
+ "Compute"
+ ],
+ "category": "Infrastructure",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": false,
+ "highestVersion": false,
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "db1": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "linux1": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "host": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "uniqueId": "tosca.nodes.objectstorage.1.0",
+ "resourceName": "tosca.nodes.objectstorage",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412549091,
+ "lastUpdateDate": 1433412549091,
+ "coordinates": {
+ "x":150,
+ "y":250
+ },
+ "description": "Represents storage that provides the ability to store data as objects (or BLOBs of data) without consideration for the underlying filesystem or devices.",
+ "icon": "icon-red1",
+ "tags": [
+ "ObjectStorage"
+ ],
+ "category": "Infrastructure",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "properties": {
+ "maxsize": {
+ "uniqueId": "tosca.nodes.objectstorage.1.0.maxsize",
+ "type": "scalar-unit.size",
+ "required": false,
+ "constraints": [
+ {
+
+ }
+ ],
+ "definition": true,
+ "password": false
+ },
+ "name": {
+ "uniqueId": "tosca.nodes.objectstorage.1.0.name",
+ "type": "string",
+ "required": false,
+ "definition": true,
+ "password": false
+ },
+ "size": {
+ "uniqueId": "tosca.nodes.objectstorage.1.0.size",
+ "type": "scalar-unit.size",
+ "required": false,
+ "constraints": [
+ {
+
+ }
+ ],
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": false,
+ "highestVersion": false,
+ "capabilities": {
+ "host2": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "db": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "linux2": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "db": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "uniqueId": "tosca.nodes.blockstorage.1.0",
+ "resourceName": "tosca.nodes.blockstorage",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412549327,
+ "lastUpdateDate": 1433412549327,
+ "coordinates": {
+ "x":150,
+ "y":250
+ },
+ "description": "Represents a server-local block storage device (i.e., not shared) offering evenly sized blocks of data from which raw storage volumes can be created.",
+ "icon": "icon-red3",
+ "tags": [
+ "BlockStorage"
+ ],
+ "category": "Infrastructure",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "properties": {
+ "volume_id": {
+ "uniqueId": "tosca.nodes.blockstorage.1.0.volume_id",
+ "type": "string",
+ "required": false,
+ "definition": true,
+ "password": false
+ },
+ "snapshot_id": {
+ "uniqueId": "tosca.nodes.blockstorage.1.0.snapshot_id",
+ "type": "string",
+ "required": false,
+ "definition": true,
+ "password": false
+ },
+ "size": {
+ "uniqueId": "tosca.nodes.blockstorage.1.0.size",
+ "type": "scalar-unit.size",
+ "required": false,
+ "constraints": [
+ {
+
+ }
+ ],
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": false,
+ "highestVersion": false,
+ "capabilities": {
+ "host3": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "db3": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "linux": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "linux": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "uniqueId": "tosca.nodes.loadbalancer.1.0",
+ "resourceName": "tosca.nodes.loadbalancer",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412549878,
+ "lastUpdateDate": 1433412549878,
+ "coordinates": {
+ "x":150,
+ "y":250
+ },
+ "description": "Represents logical function that be used in conjunction with a Floating Address to distribute an application’s traffic (load) across a number of instances of the application (e.g., for a clustered or scaled application).",
+ "icon": "icon-red4",
+ "tags": [
+ "Load Balancer"
+ ],
+ "category": "Infrastructure",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "properties": {
+ "algorithm": {
+ "uniqueId": "tosca.nodes.loadbalancer.1.0.algorithm",
+ "type": "string",
+ "required": false,
+ "definition": true,
+ "password": false
+ }
+ },
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": false,
+ "highestVersion": false,
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "db4": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "linux4": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "linux": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "uniqueId": "tosca.nodes.titan.1.0",
+ "resourceName": "tosca.nodes.titan",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412550046,
+ "lastUpdateDate": 1433412550046,
+ "coordinates": {
+ "x":150,
+ "y":250
+ },
+ "description": "Titan is a scalable graph database optimized for storing and querying graphs containing hundreds of billions of vertices and edges distributed across a multi-machine cluster. Titan is a transactional database that can support thousands of concurrent users executing complex graph traversals in real time.",
+ "icon": "icon-red4",
+ "tags": [
+ "Titan"
+ ],
+ "category": "Big Data",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": false,
+ "highestVersion": false,
+ "capabilities": {
+ "host5": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "db": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "linux5": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "linux": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "uniqueId": "tosca.nodes.tas.1.0",
+ "resourceName": "tosca.nodes.tas",
+ "resourceVersion": "1.0",
+ "creatorUserId": "jh0003",
+ "creatorFullName": "Jimmy Hendrix",
+ "lastUpdaterUserId": "jh0003",
+ "lastUpdaterFullName": "Jimmy Hendrix",
+ "creationDate": 1433412550197,
+ "lastUpdateDate": 1433412550197,
+ "coordinates": {
+ "x":150,
+ "y":250
+ },
+ "description": "Telephony application server.",
+ "icon": "icon-red1",
+ "tags": [
+ "TAS"
+ ],
+ "category": "VoIP",
+ "lifecycleState": "CERTIFIED",
+ "derivedFrom": [
+ "tosca.nodes.root"
+ ],
+ "vendorName": "ATT (Tosca)",
+ "vendorRelease": "1.0.0.wd03",
+ "contactId": "jh0003",
+ "abstract": false,
+ "highestVersion": false,
+ "capabilities": {
+ "host": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "db6": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ },
+ "linux6": {
+ "uniqueId": "capability.tosca.nodes.Compute.1.0.host",
+ "type": "tosca.capabilities.Container",
+ "validSourceTypes": [
+ "tosca.nodes.SC"
+ ]
+ }
+ },
+ "requirements": {
+ "db": {
+ "uniqueId": "tosca.nodes.SoftwareComponent.1.0.host",
+ "capability": "tosca.capabilities.Container",
+ "node": "tosca.nodes.Compute",
+ "requirementImpl": {
+ "uniqueId": "requirementImpl.my-resource.1.0.host",
+ "nodeId": "tosca.nodes.Compute.1.0",
+ "requirementProperties": {
+ "host": {
+ "uniqueId": "capabilityInst.requirementImpl.my-resource.1.0.host.host",
+ "properties": {
+ "disk_size": "10",
+ "num_cpus": "2"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+]
diff --git a/catalog-ui/server-mock/mock-data/template/template.json b/catalog-ui/server-mock/mock-data/template/template.json
new file mode 100644
index 0000000000..2747491c0e
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/template/template.json
@@ -0,0 +1,37 @@
+[
+ {
+ "uniqueId": "1",
+ "resourceName": "template1",
+ "description":"description description description description description "
+ },
+ {
+ "uniqueId": "2",
+ "resourceName": "template2",
+ "description":"description description description description description "
+ },
+ {
+ "uniqueId": "3",
+ "resourceName": "template3",
+ "description":"description description description description description "
+ },
+ {
+ "uniqueId": "4",
+ "resourceName": "template4",
+ "description":"description description description description description "
+ },
+ {
+ "uniqueId": "5",
+ "resourceName": "template5",
+ "description":"description description description description description "
+ },
+ {
+ "uniqueId": "6",
+ "resourceName": "template6",
+ "description":"description description description description description "
+ },
+ {
+ "uniqueId": "7",
+ "resourceName": "template7",
+ "description":"description description description description description "
+ }
+ ]
diff --git a/catalog-ui/server-mock/mock-data/user/user.json b/catalog-ui/server-mock/mock-data/user/user.json
new file mode 100644
index 0000000000..5e793db6c6
--- /dev/null
+++ b/catalog-ui/server-mock/mock-data/user/user.json
@@ -0,0 +1,7 @@
+{
+ "firstName": "James",
+ "lastName": "Brown",
+ "userId": "jb1234u",
+ "email": "jb1234u@sdc.com",
+ "role": "ADMIN"
+}
diff --git a/catalog-ui/server-mock/mock-server.js b/catalog-ui/server-mock/mock-server.js
new file mode 100644
index 0000000000..6f4c88a62d
--- /dev/null
+++ b/catalog-ui/server-mock/mock-server.js
@@ -0,0 +1,140 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var express = require('express');
+var mockApis = require('../configurations/mock.json').sdcConfig;
+//var mockUris = require('../configurations/mock.json');
+var cors = require('cors');
+var multer = require('multer')
+var basePathToMockData = './mock-data/';
+var app = express();
+
+var allowedHeaders = 'Content-Type,Authorization,If-Modified-Since,';
+allowedHeaders += mockApis.cookie.userIdSuffix;
+allowedHeaders += ','+mockApis.cookie.userEmail;
+allowedHeaders += ','+mockApis.cookie.userFirstName;
+allowedHeaders += ','+mockApis.cookie.userLastName;
+allowedHeaders += ','+mockApis.cookie.xEcompRequestId;
+
+
+
+app.use(cors({
+ // origin: '*',
+ origin: function(origin, callback) {
+ callback(null, true);
+ },
+ methods: 'GET, POST, PUT, DELETE',
+ allowedHeaders: allowedHeaders,
+ credentials: true
+}));
+
+//set cookie middleware
+app.use(function(req, res, next) {
+
+ res.cookie(mockApis.cookie.userIdSuffix, req.headers[mockApis.cookie.userIdSuffix] || mockApis.userTypes.designer.userId );
+ res.cookie(mockApis.cookie.userEmail, req.headers[mockApis.cookie.userEmail] || mockApis.userTypes.designer.email);
+ res.cookie(mockApis.cookie.userFirstName, req.headers[mockApis.cookie.userFirstName] || mockApis.userTypes.designer.firstName);
+ res.cookie(mockApis.cookie.userLastName, req.headers[mockApis.cookie.userLastName] || mockApis.userTypes.designer.lastName);
+ res.cookie(mockApis.cookie.xEcompRequestId, req.headers[mockApis.cookie.xEcompRequestId] || mockApis.userTypes.designer.lastName);
+ next();
+});
+
+var userRoutes = require('./routes/user');
+app.use('/v1/user', userRoutes);
+var resourceRoutes = require('./routes/resource');
+app.use('/v1/resource', resourceRoutes);
+var templateRoutes = require('./routes/template');
+app.use('/v1/template', templateRoutes);
+var propertyRoutes = require('./routes/property');
+app.use('/v1/resource/:resourceId/property', propertyRoutes);
+var resourcesRoutes = require('./routes/resources');
+app.use('/v1/catalog/resources', resourcesRoutes);
+
+/******************************************* MOCKS ENPOINTS *************************************************/
+/* get user details */
+// app.get(mockApis.api.GET_user, function (req, res) {
+// var user = require(basePathToMockData+'user/user.json');
+// res.send(user);
+// });
+
+/* get elements */
+app.get(mockApis.api.GET_element, function (req, res) {
+
+ var element = require(basePathToMockData+'element/element.json');
+ res.send(element);
+});
+
+/* get elements */
+app.get(mockApis.api.GET_catalog, function (req, res) {
+
+ var element = require(basePathToMockData+'element/element.json');
+ res.send(element);
+});
+
+/* get categories */
+app.get(mockApis.api.GET_category, function (req, res) {
+
+ var categories = require(basePathToMockData+'category/category.json');
+ res.send(categories);
+});
+
+
+/* get categories */
+app.get(mockApis.api.GET_configuration_ui, function (req, res) {
+
+ var categories = require(basePathToMockData+'artifact/artifact-types.json');
+ res.send(categories);
+});
+
+
+
+
+
+
+
+
+//upload artifact file
+app.use(multer({ dest: './uploads/',
+ rename: function (fieldname, filename) {
+ return filename+Date.now();
+ },
+ onFileUploadStart: function (file) {
+ console.log(file.originalname + ' is starting ...')
+ },
+ onFileUploadComplete: function (file) {
+ console.log(file.fieldname + ' uploaded to ' + file.path)
+ done=true;
+ }
+}));
+
+var done=false;
+app.post(mockApis.api.GET_resource_artifact,function(req,res){
+ if(done==true){
+ console.log(req.files);
+ res.end("File uploaded.");
+ }
+});
+
+
+/**************************************************** *******************************************************/
+
+var server = app.listen(9999, function () {
+ console.log('mock server listening on port %d', server.address().port);
+});
diff --git a/catalog-ui/server-mock/routes/property.js b/catalog-ui/server-mock/routes/property.js
new file mode 100644
index 0000000000..2756018035
--- /dev/null
+++ b/catalog-ui/server-mock/routes/property.js
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var express = require('express');
+var properties = require('../mock-data/resource/properties.json');
+
+var router = express.Router();
+
+router.get('/', function (req, res) {
+ console.log('query');
+ res.send(properties);
+});
+
+router.post('/:id', function (req, res) {
+ console.log("post /:id", req);
+ res.send(properties[0]);
+});
+
+router.get('/:id', function (req, res) {
+ res.send(properties[0]);
+});
+
+
+router.post('/', function (req, res) {
+ console.log("post ", req);
+ res.send(properties[0]);
+});
+module.exports= router;
diff --git a/catalog-ui/server-mock/routes/resource.js b/catalog-ui/server-mock/routes/resource.js
new file mode 100644
index 0000000000..f02b6227e8
--- /dev/null
+++ b/catalog-ui/server-mock/routes/resource.js
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var express = require('express');
+var resource = require('../mock-data/resource/resource.json');
+
+var router = express.Router();
+
+router.get('/', function (req, res) {
+ console.log('query');
+ res.send([resource]);
+});
+
+router.post('/:id', function (req, res) {
+ console.log("post /:id");
+ res.send(resource);
+});
+
+router.get('/:id', function (req, res) {
+ console.log("post ");
+ res.send(resource);
+});
+
+
+router.post('/', function (req, res) {
+ console.log("post ");
+ res.send(resource);
+});
+
+router.get('/validate-name/:name', function (req, res) {
+
+ var name = req.params.name;
+ var response = {'isValid':true};
+ if (name==='exist'){
+ response = {'isValid':false};
+ }
+ res.send(response);
+
+});
+
+
+
+module.exports= router;
diff --git a/catalog-ui/server-mock/routes/resources.js b/catalog-ui/server-mock/routes/resources.js
new file mode 100644
index 0000000000..589848981c
--- /dev/null
+++ b/catalog-ui/server-mock/routes/resources.js
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var express = require('express');
+var resourcesNotAbstract = require('../mock-data/resources/resourcesNotAbstract.json');
+var resourcesAbstract = require('../mock-data/resources/resourcesAbstract.json');
+
+var router = express.Router();
+
+router.get('/certified/notabstract', function (req, res) {
+ res.send(resourcesNotAbstract);
+});
+
+
+router.get('/certified/abstract', function (req, res) {
+ res.send(resourcesAbstract);
+});
+
+
+
+module.exports= router;
diff --git a/catalog-ui/server-mock/routes/template.js b/catalog-ui/server-mock/routes/template.js
new file mode 100644
index 0000000000..3b7653967e
--- /dev/null
+++ b/catalog-ui/server-mock/routes/template.js
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var express = require('express');
+var templates = require('../mock-data/template/template.json');
+
+var router = express.Router();
+
+router.get('/', function (req, res) {
+ console.log('query');
+ res.send(templates);
+});
+
+router.post('/:id', function (req, res) {
+ console.log("post /:id", req);
+ res.send(templates[0]);
+});
+
+router.get('/:id', function (req, res) {
+ res.send(templates[0]);
+});
+
+
+router.post('/', function (req, res) {
+ console.log("post ", req);
+ res.send(templates[0]);
+});
+module.exports= router;
diff --git a/catalog-ui/server-mock/routes/user.js b/catalog-ui/server-mock/routes/user.js
new file mode 100644
index 0000000000..d01f4397e9
--- /dev/null
+++ b/catalog-ui/server-mock/routes/user.js
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+var express = require('express');
+var user = require('../mock-data/user/user.json');
+
+var router = express.Router();
+
+router.get('/', function (req, res) {
+ console.log('query');
+ res.send([user]);
+});
+
+router.post('/:id', function (req, res) {
+ console.log("post /:id", req);
+ res.send(user);
+});
+
+router.get('/:id', function (req, res) {
+ res.send(user);
+});
+
+
+router.post('/', function (req, res) {
+ console.log("post ", req);
+ res.send(user);
+});
+module.exports= router;
diff --git a/catalog-ui/tests/karma.unit.conf.js b/catalog-ui/tests/karma.unit.conf.js
new file mode 100644
index 0000000000..1b8277faed
--- /dev/null
+++ b/catalog-ui/tests/karma.unit.conf.js
@@ -0,0 +1,149 @@
+// Karma configuration
+
+module.exports = function (config) {
+ config.set({
+
+ // base path, that will be used to resolve files and exclude
+ basePath: '../',
+
+
+ // frameworks to use
+ frameworks: ['jasmine'],
+
+ // list of files / patterns to load in the browser
+ files: [
+ 'bower_components/jquery/dist/jquery.js',
+ 'bower_components/angular/angular.js',
+ 'bower_components/angular-base64/angular-base64.js',
+ 'bower_components/angular-base64-upload/src/angular-base64-upload.js',
+ 'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
+ 'bower_components/jquery-ui/jquery-ui.js',
+ 'bower_components/angular-dragdrop/src/angular-dragdrop.js',
+ 'bower_components/angular-filter/dist/angular-filter.min.js',
+ 'bower_components/angular-md5/angular-md5.js',
+ 'bower_components/perfect-scrollbar/src/perfect-scrollbar.js',
+ 'bower_components/angular-perfect-scrollbar/src/angular-perfect-scrollbar.js',
+ 'bower_components/angular-mocks/angular-mocks.js',
+ 'bower_components/angular-resource/angular-resource.js',
+ 'bower_components/angular-sanitize/angular-sanitize.js',
+ 'bower_components/angular-tooltips/dist/angular-tooltips.min.js',
+ 'bower_components/angular-translate/angular-translate.js',
+ 'bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js',
+ 'bower_components/angular-ui-router/release/angular-ui-router.js',
+ 'bower_components/angular-uuid4/angular-uuid4.js',
+ 'bower_components/bootstrap/dist/js/bootstrap.js',
+ 'bower_components/checklist-model/checklist-model.js',
+ 'bower_components/angular-clipboard/angular-clipboard.js',
+ 'bower_components/angular-resizable/src/angular-resizable.js',
+ 'bower_components/angular-ui-notification/src/angular-ui-notification.js',
+ 'bower_components/lodash/lodash.js',
+ 'bower_components/restangular/dist/restangular.js',
+ 'bower_components/jspdf/dist/jspdf.min.js',
+ 'app/scripts/utils/**/*.js',
+ 'app/scripts/services/**/*.js',
+ 'app/scripts/models/**/*.js',
+ 'app/scripts/view-models/**/*.js',
+ 'app/scripts/filters/**/*.js',
+ 'app/scripts/directives/**/*.js',
+ 'app/scripts/modules/**/*.js',
+
+ 'app/scripts/app.js',
+ 'app/languages/**/*.js',
+
+ 'app/scripts/templates.js',
+
+
+
+
+
+ //'app/scripts/view-models/dashboard/dashboard-view-model-tests.js',
+
+
+ 'app/scripts/**/*-tests.js',
+ //'app/scripts/app.js',
+
+
+ //definition to allow to debug TS tests file in browser
+ {pattern: 'app/scripts/**/*-tests.ts', included: false},
+ {pattern: 'app/scripts/**/*-tests.js.map', included: false},
+
+
+ //definition to allow to debug TS sources files in browser
+ {pattern: 'app/scripts/**/*.ts', included: false},
+ {pattern: 'app/scripts/**/*.js.map', included: false}
+
+ ],
+
+ // list of files to exclude
+ exclude: [
+
+ ],
+
+ junitReporter: {
+ outputFile: 'tests/testOutput.xml',
+ suite: ''
+ },
+
+ //NOTE: This is handled from gruntfile.js
+ coverageReporter : {
+ type : 'html',
+ dir: 'tests/Coverage'
+ },
+ // test results reporter to use
+ // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
+ reporters: [
+ 'mocha',//uncomment this line if you need to debug your unit test and print out the 'describe' of the test
+ 'junit',
+ 'dots',
+ 'progress',
+ 'coverage'
+ ],
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ // logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+ preprocessors: {
+ '**/*.html': 'html2js',
+ 'app/scripts/**/!(*-tests|*Tests|*Test).js': 'coverage'
+ },
+
+ //ngHtml2JsPreprocessor: {
+ // stripPrefix: 'client/'
+ //},
+
+ // Start these browsers, currently available:
+ // - Chrome
+ // - ChromeCanary
+ // - Firefox
+ // - Opera (has to be installed with `npm install karma-opera-launcher`)
+ // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
+ // - PhantomJS
+ // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
+
+ //NOTE: This is handled from gruntfile.js
+
+ browsers: ['Chrome'],
+
+
+ // If browser does not capture in given timeout [ms], kill it
+ captureTimeout: 60000,
+
+
+ // Continuous Integration mode
+ // if true, it capture browsers, run tests and exit
+ singleRun: false
+ });
+};
diff --git a/catalog-ui/tsd.json b/catalog-ui/tsd.json
new file mode 100644
index 0000000000..1f8ffec3cb
--- /dev/null
+++ b/catalog-ui/tsd.json
@@ -0,0 +1,15 @@
+{
+ "version": "v4",
+ "repo": "borisyankov/DefinitelyTyped",
+ "ref": "master",
+ "path": "typings",
+ "bundle": "typings/tsd.d.ts",
+ "installed": {
+ "notifyjs/notifyjs.d.ts": {
+ "commit": "581563c4684d405afec14260ba79a89a52666c09"
+ },
+ "jasmine/jasmine.d.ts": {
+ "commit": "14ab703f435375194360cdba3abab61afc879c18"
+ }
+ }
+}
diff --git a/catalog-ui/tslint.json b/catalog-ui/tslint.json
new file mode 100644
index 0000000000..2cd2dece6f
--- /dev/null
+++ b/catalog-ui/tslint.json
@@ -0,0 +1,58 @@
+{
+ "rules": {
+ "class-name": true,
+ "comment-format": [
+ false,
+ "check-space"
+ ],
+ "indent": [
+ true,
+ "spaces"
+ ],
+ "no-duplicate-variable": true,
+ "no-eval": true,
+ "no-internal-module": true,
+ "no-trailing-whitespace": true,
+ "no-unsafe-finally": true,
+ "no-var-keyword": true,
+ "one-line": [
+ true,
+ "check-open-brace",
+ "check-whitespace"
+ ],
+ "quotemark": [
+ false,
+ "single"
+ ],
+ "semicolon": [
+ true,
+ "always"
+ ],
+ "triple-equals": [
+ true,
+ "allow-null-check"
+ ],
+ "typedef-whitespace": [
+ true,
+ {
+ "call-signature": "nospace",
+ "index-signature": "nospace",
+ "parameter": "nospace",
+ "property-declaration": "nospace",
+ "variable-declaration": "nospace"
+ }
+ ],
+ "variable-name": [
+ true,
+ "ban-keywords"
+ ],
+ "whitespace": [
+ false,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-type"
+ ]
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/typings/angular-ui-bootstrap/angular-ui-bootstrap.d.ts b/catalog-ui/typings/angular-ui-bootstrap/angular-ui-bootstrap.d.ts
new file mode 100644
index 0000000000..4062f141f1
--- /dev/null
+++ b/catalog-ui/typings/angular-ui-bootstrap/angular-ui-bootstrap.d.ts
@@ -0,0 +1,658 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Angular UI Bootstrap 0.11.0
+// Project: https://github.com/angular-ui/bootstrap
+// Definitions by: Brian Surowiec <https://github.com/xt0rted>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+/// <reference path="../angularjs/angular.d.ts" />
+
+declare module angular.ui.bootstrap {
+
+ interface IAccordionConfig {
+ /**
+ * Controls whether expanding an item will cause the other items to close.
+ *
+ * @default true
+ */
+ closeOthers?: boolean;
+ }
+
+
+ interface IButtonConfig {
+ /**
+ * @default: 'active'
+ */
+ activeClass?: string;
+
+ /**
+ * @default: 'Click'
+ */
+ toggleEvent?: string;
+ }
+
+
+ interface IDatepickerConfig {
+ /**
+ * Format of day in month.
+ *
+ * @default 'dd'
+ */
+ dayFormat?: string;
+
+ /**
+ * Format of month in year.
+ *
+ * @default 'MMM'
+ */
+ monthFormat?: string;
+
+ /**
+ * Format of year in year range.
+ *
+ * @default 'yyyy'
+ */
+ yearFormat?: string;
+
+ /**
+ * Format of day in week header.
+ *
+ * @default 'EEE'
+ */
+ dayHeaderFormat?: string;
+
+ /**
+ * Format of title when selecting day.
+ *
+ * @default 'MMM yyyy'
+ */
+ dayTitleFormat?: string;
+
+ /**
+ * Format of title when selecting month.
+ *
+ * @default 'yyyy'
+ */
+ monthTitleFormat?: string;
+
+ /**
+ * Whether to display week numbers.
+ *
+ * @default true
+ */
+ showWeeks?: boolean;
+
+ /**
+ * Starting day of the week from 0-6 where 0=Sunday and 6=Saturday.
+ *
+ * @default 0
+ */
+ startingDay?: number;
+
+ /**
+ * Number of years displayed in year selection.
+ *
+ * @default 20
+ */
+ yearRange?: number;
+
+ /**
+ * Defines the minimum available date.
+ *
+ * @default null
+ */
+ minDate?: any;
+
+ /**
+ * Defines the maximum available date.
+ *
+ * @default null
+ */
+ maxDate?: any;
+ }
+
+ interface IDatepickerPopupConfig {
+ /**
+ * The format for displayed dates.
+ *
+ * @default 'yyyy-MM-dd'
+ */
+ dateFormat?: string;
+
+ /**
+ * The text to display for the current day button.
+ *
+ * @default 'Today'
+ */
+ currentText?: string;
+
+ /**
+ * The text to display for the toggling week numbers button.
+ *
+ * @default 'Weeks'
+ */
+ toggleWeeksText?: string;
+
+ /**
+ * The text to display for the clear button.
+ *
+ * @default 'Clear'
+ */
+ clearText?: string;
+
+ /**
+ * The text to display for the close button.
+ *
+ * @default 'Done'
+ */
+ closeText?: string;
+
+ /**
+ * Whether to close calendar when a date is chosen.
+ *
+ * @default true
+ */
+ closeOnDateSelection?: boolean;
+
+ /**
+ * Append the datepicker popup element to `body`, rather than inserting after `datepicker-popup`.
+ *
+ * @default false
+ */
+ appendToBody?: boolean;
+
+ /**
+ * Whether to display a button bar underneath the datepicker.
+ *
+ * @default true
+ */
+ showButtonBar?: boolean;
+ }
+
+
+ interface IModalService {
+ /**
+ * @param {IModalSettings} options
+ * @returns {IModalServiceInstance}
+ */
+ open(options: IModalSettings): IModalServiceInstance;
+ }
+
+ interface IModalServiceInstance {
+ /**
+ * a method that can be used to close a modal, passing a result
+ */
+ close(result?: any): void;
+
+ /**
+ * a method that can be used to dismiss a modal, passing a reason
+ */
+ dismiss(reason?: any): void;
+
+ /**
+ * a promise that is resolved when a modal is closed and rejected when a modal is dismissed
+ */
+ result: angular.IPromise<any>;
+
+ /**
+ * a promise that is resolved when a modal gets opened after downloading content's template and resolving all variables
+ */
+ opened: angular.IPromise<any>;
+ }
+
+ interface IModalScope extends angular.IScope {
+ /**
+ * Those methods make it easy to close a modal window without a need to create a dedicated controller
+ */
+
+ /**
+ * Dismiss the dialog without assigning a value to the promise output
+ */
+ $dismiss(reason?: any): void;
+
+ /**
+ * Close the dialog resolving the promise to the given value
+ */
+ $close(result?: any): void;
+ }
+
+ interface IModalSettings {
+ /**
+ * a path to a template representing modal's content
+ */
+ templateUrl?: string;
+
+ /**
+ * inline template representing the modal's content
+ */
+ template?: string;
+
+ /**
+ * a scope instance to be used for the modal's content (actually the $modal service is going to create a child scope of a provided scope).
+ * Defaults to `$rootScope`.
+ */
+ scope?: angular.IScope|IModalScope;
+
+ /**
+ * a controller for a modal instance - it can initialize scope used by modal.
+ * A controller can be injected with `$modalInstance`
+ */
+ controller?: any;
+
+ /**
+ * an alternative to the controller-as syntax, matching the API of directive definitions.
+ * Requires the controller option to be provided as well
+ */
+ controllerAs?: string;
+
+ /**
+ * members that will be resolved and passed to the controller as locals; it is equivalent of the `resolve` property for AngularJS routes
+ */
+ resolve?: any;
+
+ /**
+ * controls the presence of a backdrop
+ * Allowed values:
+ * - true (default)
+ * - false (no backdrop)
+ * - 'static' backdrop is present but modal window is not closed when clicking outside of the modal window
+ *
+ * @default true
+ */
+ backdrop?: any;
+
+ /**
+ * indicates whether the dialog should be closable by hitting the ESC key, defaults to true
+ */
+ keyboard?: boolean;
+
+ /**
+ * additional CSS class(es) to be added to a modal window template
+ */
+ windowClass?: string;
+
+ /**
+ * optional size of modal window. Allowed values: 'sm' (small) or 'lg' (large). Requires Bootstrap 3.1.0 or later
+ */
+ size?: string;
+
+ /**
+ * a path to a template overriding modal's window template
+ */
+ windowTemplateUrl?: string;
+ }
+
+ interface IModalStackService {
+ /**
+ * Opens a new modal instance.
+ */
+ open(modalInstance: IModalServiceInstance, modal: any): void;
+
+ /**
+ * Closes a modal instance with an optional result.
+ */
+ close(modalInstance: IModalServiceInstance, result?: any): void;
+
+ /**
+ * Dismisses a modal instance with an optional reason.
+ */
+ dismiss(modalInstance: IModalServiceInstance, reason?: any): void;
+
+ /**
+ * Dismiss all open modal instances with an optional reason that will be passed to each instance.
+ */
+ dismissAll(reason?: any): void;
+
+ /**
+ * Gets the topmost modal instance that is open.
+ */
+ getTop(): IModalStackedMapKeyValuePair;
+ }
+
+ interface IModalStackedMapKeyValuePair {
+ key: IModalServiceInstance;
+ value: any;
+ }
+
+
+ interface IPaginationConfig {
+ /**
+ * Current page number. First page is 1.
+ */
+ page?: number;
+
+ /**
+ * Total number of items in all pages.
+ */
+ totalItems?: number;
+
+ /**
+ * Maximum number of items per page. A value less than one indicates all items on one page.
+ *
+ * @default 10
+ */
+ itemsPerPage?: number;
+
+ /**
+ * Limit number for pagination size.
+ *
+ * @default: null
+ */
+ maxSize?: number;
+
+ /**
+ * An optional expression assigned the total number of pages to display.
+ *
+ * @default angular.noop
+ */
+ numPages?: number;
+
+ /**
+ * Whether to keep current page in the middle of the visible ones.
+ *
+ * @default true
+ */
+ rotate?: boolean;
+
+ /**
+ * An optional expression called when a page is selected having the page number as argument.
+ *
+ * @default null
+ */
+ onSelectPage?(page: number): void;
+
+ /**
+ * Whether to display Previous / Next buttons.
+ *
+ * @default true
+ */
+ directionLinks?: boolean;
+
+ /**
+ * Text for Previous button.
+ *
+ * @default 'Previous'
+ */
+ previousText?: string;
+
+ /**
+ * Text for Next button.
+ *
+ * @default 'Next'
+ */
+ nextText?: string;
+
+ /**
+ * Whether to display First / Last buttons.
+ *
+ * @default false
+ */
+ boundaryLinks?: boolean;
+
+ /**
+ * Text for First button.
+ *
+ * @default 'First'
+ */
+ firstText?: string;
+
+ /**
+ * Text for Last button.
+ *
+ * @default 'Last'
+ */
+ lastText?: string;
+ }
+
+ interface IPagerConfig {
+ /**
+ * Whether to align each link to the sides.
+ *
+ * @default true
+ */
+ align?: boolean;
+
+ /**
+ * Current page number. First page is 1.
+ */
+ page?: number;
+
+ /**
+ * Total number of items in all pages.
+ */
+ totalItems?: number;
+
+ /**
+ * Maximum number of items per page. A value less than one indicates all items on one page.
+ *
+ * @default 10
+ */
+ itemsPerPage?: number;
+
+ /**
+ * An optional expression assigned the total number of pages to display.
+ *
+ * @default angular.noop
+ */
+ numPages?: number;
+
+ /**
+ * An optional expression called when a page is selected having the page number as argument.
+ *
+ * @default null
+ */
+ onSelectPage?(page: number): void;
+
+ /**
+ * Text for Previous button.
+ *
+ * @default '« Previous'
+ */
+ previousText?: string;
+
+ /**
+ * Text for Next button.
+ *
+ * @default 'Next »'
+ */
+ nextText?: string;
+ }
+
+
+ interface IPositionCoordinates {
+ width?: number;
+ height?: number;
+ top?: number;
+ left?: number;
+ }
+
+ interface IPositionService {
+ /**
+ * Provides a read-only equivalent of jQuery's position function.
+ */
+ position(element: JQuery): IPositionCoordinates;
+
+ /**
+ * Provides a read-only equivalent of jQuery's offset function.
+ */
+ offset(element: JQuery): IPositionCoordinates;
+ }
+
+
+ interface IProgressConfig {
+ /**
+ * Whether bars use transitions to achieve the width change.
+ *
+ * @default: true
+ */
+ animate?: boolean;
+
+ /**
+ * A number that specifies the total value of bars that is required.
+ *
+ * @default: 100
+ */
+ max?: number;
+ }
+
+
+ interface IRatingConfig {
+ /**
+ * Changes the number of icons.
+ *
+ * @default: 5
+ */
+ max?: number;
+
+ /**
+ * A variable used in the template to specify the state for selected icons.
+ *
+ * @default: null
+ */
+ stateOn?: string;
+
+ /**
+ * A variable used in the template to specify the state for unselected icons.
+ *
+ * @default: null
+ */
+ stateOff?: string;
+ }
+
+
+ interface ITimepickerConfig {
+ /**
+ * Number of hours to increase or decrease when using a button.
+ *
+ * @default 1
+ */
+ hourStep?: number;
+
+ /**
+ * Number of minutes to increase or decrease when using a button.
+ *
+ * @default 1
+ */
+ minuteStep?: number;
+
+ /**
+ * Whether to display 12H or 24H mode.
+ *
+ * @default true
+ */
+ showMeridian?: boolean;
+
+ /**
+ * Meridian labels based on locale. To override you must supply an array like ['AM', 'PM'].
+ *
+ * @default null
+ */
+ meridians?: Array<string>;
+
+ /**
+ * Whether the user can type inside the hours & minutes input.
+ *
+ * @default false
+ */
+ readonlyInput?: boolean;
+
+ /**
+ * Whether the user can scroll inside the hours & minutes input to increase or decrease it's values.
+ *
+ * @default true
+ */
+ mousewheel?: boolean;
+ }
+
+
+ interface ITooltipOptions {
+ /**
+ * Where to place it? Defaults to 'top', but also accepts 'right', 'bottom', or 'left'.
+ *
+ * @default 'top'
+ */
+ placement?: string;
+
+ /**
+ * Should it fade in and out?
+ *
+ * @default true
+ */
+ animation?: boolean;
+
+ /**
+ * For how long should the user have to have the mouse over the element before the tooltip shows (in milliseconds)?
+ *
+ * @default 0
+ */
+ popupDelay?: number;
+
+ /**
+ * Should the tooltip be appended to `$body` instead of the parent element?
+ *
+ * @default false
+ */
+ appendtoBody?: boolean;
+ }
+
+ interface ITooltipProvider {
+ /**
+ * Provide a set of defaults for certain tooltip and popover attributes.
+ */
+ options(value: ITooltipOptions): void;
+
+ /**
+ * Extends the default trigger mappings with mappings of your own. E.g. `{ 'openTrigger': 'closeTrigger' }`.
+ */
+ setTriggers(triggers: Object): void;
+ }
+
+
+ interface ITransitionService {
+ /**
+ * The browser specific animation event name.
+ */
+ animationEndEventName: string;
+
+ /**
+ * The browser specific transition event name.
+ */
+ transitionEndEventName: string;
+
+ /**
+ * Provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
+ *
+ * @param element The DOMElement that will be animated
+ * @param trigger The thing that will cause the transition to start:
+ * - As a string, it represents the css class to be added to the element.
+ * - As an object, it represents a hash of style attributes to be applied to the element.
+ * - As a function, it represents a function to be called that will cause the transition to occur.
+ * @param options Optional settings for the transition.
+ *
+ * @return A promise that is resolved when the transition finishes.
+ */
+ (element: angular.IAugmentedJQuery, trigger: any, options?: ITransitionServiceOptions): angular.IPromise<angular.IAugmentedJQuery>;
+ }
+
+ interface ITransitionServiceOptions {
+ animation?: boolean;
+ }
+
+}
diff --git a/catalog-ui/typings/angular-ui-router/angular-ui-router.d.ts b/catalog-ui/typings/angular-ui-router/angular-ui-router.d.ts
new file mode 100644
index 0000000000..c27425e39c
--- /dev/null
+++ b/catalog-ui/typings/angular-ui-router/angular-ui-router.d.ts
@@ -0,0 +1,207 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Angular JS 1.1.5+ (ui.router module)
+// Project: https://github.com/angular-ui/ui-router
+// Definitions by: Michel Salib <https://github.com/michelsalib>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+/// <reference path="../angularjs/angular.d.ts" />
+
+declare module angular.ui {
+
+ interface IState {
+ name?: string;
+ /**
+ * String HTML content, or function that returns an HTML string
+ */
+ template?: string | {(): string};
+ /**
+ * String URL path to template file OR Function, returns URL path string
+ */
+ templateUrl?: string | {(): string};
+ /**
+ * Function, returns HTML content string
+ */
+ templateProvider?: Function;
+ /**
+ * A controller paired to the state. Function OR name as String
+ */
+ controller?: Function | string;
+ controllerAs?: string;
+ /**
+ * Function (injectable), returns the actual controller function or string.
+ */
+ controllerProvider?: Function;
+ resolve?: {};
+ /**
+ * A url with optional parameters. When a state is navigated or transitioned to, the $stateParams service will be populated with any parameters that were passed.
+ */
+ url?: string;
+ /**
+ * A map which optionally configures parameters declared in the url, or defines additional non-url parameters. Only use this within a state if you are not using url. Otherwise you can specify your parameters within the url. When a state is navigated or transitioned to, the $stateParams service will be populated with any parameters that were passed.
+ */
+ params?: any;
+ /**
+ * Use the views property to set up multiple views. If you don't need multiple views within a single state this property is not needed. Tip: remember that often nested views are more useful and powerful than multiple sibling views.
+ */
+ views?: {};
+ abstract?: boolean;
+ /**
+ * Callback function for when a state is entered. Good way to trigger an action or dispatch an event, such as opening a dialog.
+ * If minifying your scripts, make sure to explictly annotate this function, because it won't be automatically annotated by your build tools.
+ */
+ onEnter?: Function|(string|Function)[];
+ /**
+ * Callback functions for when a state is entered and exited. Good way to trigger an action or dispatch an event, such as opening a dialog.
+ * If minifying your scripts, make sure to explictly annotate this function, because it won't be automatically annotated by your build tools.
+ */
+ onExit?: Function|(string|Function)[];
+ /**
+ * Arbitrary data object, useful for custom configuration.
+ */
+ data?: any;
+ /**
+ * Boolean (default true). If false will not retrigger the same state just because a search/query parameter has changed. Useful for when you'd like to modify $location.search() without triggering a reload.
+ */
+ reloadOnSearch?: boolean;
+ }
+
+ interface IStateProvider extends ng.IServiceProvider {
+ state(name:string, config:IState): IStateProvider;
+ state(config:IState): IStateProvider;
+ decorator(name?: string, decorator?: (state: IState, parent: Function) => any): any;
+ }
+
+ interface IUrlMatcher {
+ concat(pattern: string): IUrlMatcher;
+ exec(path: string, searchParams: {}): {};
+ parameters(): string[];
+ format(values: {}): string;
+ }
+
+ interface IUrlMatcherFactory {
+ compile(pattern: string): IUrlMatcher;
+ isMatcher(o: any): boolean;
+ type(name: string, definition: any, definitionFn?: any): any;
+ }
+
+ interface IUrlRouterProvider extends ng.IServiceProvider {
+ when(whenPath: RegExp, handler: Function): IUrlRouterProvider;
+ when(whenPath: RegExp, handler: any[]): IUrlRouterProvider;
+ when(whenPath: RegExp, toPath: string): IUrlRouterProvider;
+ when(whenPath: IUrlMatcher, hanlder: Function): IUrlRouterProvider;
+ when(whenPath: IUrlMatcher, handler: any[]): IUrlRouterProvider;
+ when(whenPath: IUrlMatcher, toPath: string): IUrlRouterProvider;
+ when(whenPath: string, handler: Function): IUrlRouterProvider;
+ when(whenPath: string, handler: any[]): IUrlRouterProvider;
+ when(whenPath: string, toPath: string): IUrlRouterProvider;
+ otherwise(handler: Function): IUrlRouterProvider;
+ otherwise(handler: any[]): IUrlRouterProvider;
+ otherwise(path: string): IUrlRouterProvider;
+ rule(handler: Function): IUrlRouterProvider;
+ rule(handler: any[]): IUrlRouterProvider;
+ }
+
+ interface IStateOptions {
+ /**
+ * {boolean=true|string=} - If true will update the url in the location bar, if false will not. If string, must be "replace", which will update url and also replace last history record.
+ */
+ location?: boolean | string;
+ /**
+ * {boolean=true}, If true will inherit url parameters from current url.
+ */
+ inherit?: boolean;
+ /**
+ * {object=$state.$current}, When transitioning with relative path (e.g '^'), defines which state to be relative from.
+ */
+ relative?: IState;
+ /**
+ * {boolean=true}, If true will broadcast $stateChangeStart and $stateChangeSuccess events.
+ */
+ notify?: boolean;
+ /**
+ * {boolean=false}, If true will force transition even if the state or params have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd use this when you want to force a reload when everything is the same, including search params.
+ */
+ reload?: boolean;
+ }
+
+ interface IHrefOptions {
+ lossy?: boolean;
+ inherit?: boolean;
+ relative?: IState;
+ absolute?: boolean;
+ }
+
+ interface IStateService {
+ /**
+ * Convenience method for transitioning to a new state. $state.go calls $state.transitionTo internally but automatically sets options to { location: true, inherit: true, relative: $state.$current, notify: true }. This allows you to easily use an absolute or relative to path and specify only the parameters you'd like to update (while letting unspecified parameters inherit from the currently active ancestor states).
+ *
+ * @param to Absolute state name or relative state path. Some examples:
+ *
+ * $state.go('contact.detail') - will go to the contact.detail state
+ * $state.go('^') - will go to a parent state
+ * $state.go('^.sibling') - will go to a sibling state
+ * $state.go('.child.grandchild') - will go to grandchild state
+ *
+ * @param params A map of the parameters that will be sent to the state, will populate $stateParams. Any parameters that are not specified will be inherited from currently defined parameters. This allows, for example, going to a sibling state that shares parameters specified in a parent state. Parameter inheritance only works between common ancestor states, I.e. transitioning to a sibling will get you the parameters for all parents, transitioning to a child will get you all current parameters, etc.
+ *
+ * @param options Options object.
+ */
+ go(to: string, params?: {}, options?: IStateOptions): ng.IPromise<any>;
+ transitionTo(state: string, params?: {}, updateLocation?: boolean): void;
+ transitionTo(state: string, params?: {}, options?: IStateOptions): void;
+ includes(state: string, params?: {}): boolean;
+ is(state:string, params?: {}): boolean;
+ is(state: IState, params?: {}): boolean;
+ href(state: IState, params?: {}, options?: IHrefOptions): string;
+ href(state: string, params?: {}, options?: IHrefOptions): string;
+ get(state: string): IState;
+ get(): IState[];
+ current: IState;
+ params: IStateParamsService;
+ reload(): void;
+ }
+
+ interface IStateParamsService {
+ [key: string]: any;
+ }
+
+ interface IUrlRouterService {
+ /*
+ * Triggers an update; the same update that happens when the address bar
+ * url changes, aka $locationChangeSuccess.
+ *
+ * This method is useful when you need to use preventDefault() on the
+ * $locationChangeSuccess event, perform some custom logic (route protection,
+ * auth, config, redirection, etc) and then finally proceed with the transition
+ * by calling $urlRouter.sync().
+ *
+ */
+ sync(): void;
+ }
+
+ interface IUiViewScrollProvider {
+ /*
+ * Reverts back to using the core $anchorScroll service for scrolling
+ * based on the url anchor.
+ */
+ useAnchorScroll(): void;
+ }
+}
diff --git a/catalog-ui/typings/angularjs/angular-mocks.d.ts b/catalog-ui/typings/angularjs/angular-mocks.d.ts
new file mode 100644
index 0000000000..65b0ac1a09
--- /dev/null
+++ b/catalog-ui/typings/angularjs/angular-mocks.d.ts
@@ -0,0 +1,337 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Angular JS 1.3 (ngMock, ngMockE2E module)
+// Project: http://angularjs.org
+// Definitions by: Diego Vilar <http://github.com/diegovilar>, Tony Curtis <http://github.com/daltin>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+/// <reference path="angular.d.ts" />
+
+declare module "angular-mocks/ngMock" {
+ var _: string;
+ export = _;
+}
+
+declare module "angular-mocks/ngMockE2E" {
+ var _: string;
+ export = _;
+}
+
+declare module "angular-mocks/ngAnimateMock" {
+ var _: string;
+ export = _;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ngMock module (angular-mocks.js)
+///////////////////////////////////////////////////////////////////////////////
+declare module angular {
+
+ ///////////////////////////////////////////////////////////////////////////
+ // AngularStatic
+ // We reopen it to add the MockStatic definition
+ ///////////////////////////////////////////////////////////////////////////
+ interface IAngularStatic {
+ mock: IMockStatic;
+ }
+
+ // see https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
+ interface IInjectStatic {
+ (...fns: Function[]): any;
+ (...inlineAnnotatedConstructor: any[]): any; // this overload is undocumented, but works
+ strictDi(val?: boolean): void;
+ }
+
+ interface IMockStatic {
+ // see https://docs.angularjs.org/api/ngMock/function/angular.mock.dump
+ dump(obj: any): string;
+
+ inject: IInjectStatic
+
+ // see https://docs.angularjs.org/api/ngMock/function/angular.mock.module
+ module(...modules: any[]): any;
+
+ // see https://docs.angularjs.org/api/ngMock/type/angular.mock.TzDate
+ TzDate(offset: number, timestamp: number): Date;
+ TzDate(offset: number, timestamp: string): Date;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ExceptionHandlerService
+ // see https://docs.angularjs.org/api/ngMock/service/$exceptionHandler
+ // see https://docs.angularjs.org/api/ngMock/provider/$exceptionHandlerProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface IExceptionHandlerProvider extends IServiceProvider {
+ mode(mode: string): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // TimeoutService
+ // see https://docs.angularjs.org/api/ngMock/service/$timeout
+ // Augments the original service
+ ///////////////////////////////////////////////////////////////////////////
+ interface ITimeoutService {
+ flush(delay?: number): void;
+ flushNext(expectedDelay?: number): void;
+ verifyNoPendingTasks(): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // IntervalService
+ // see https://docs.angularjs.org/api/ngMock/service/$interval
+ // Augments the original service
+ ///////////////////////////////////////////////////////////////////////////
+ interface IIntervalService {
+ flush(millis?: number): number;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // LogService
+ // see https://docs.angularjs.org/api/ngMock/service/$log
+ // Augments the original service
+ ///////////////////////////////////////////////////////////////////////////
+ interface ILogService {
+ assertEmpty(): void;
+ reset(): void;
+ }
+
+ interface ILogCall {
+ logs: string[];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // HttpBackendService
+ // see https://docs.angularjs.org/api/ngMock/service/$httpBackend
+ ///////////////////////////////////////////////////////////////////////////
+ interface IHttpBackendService {
+ /**
+ * Flushes all pending requests using the trained responses.
+ * @param count Number of responses to flush (in the order they arrived). If undefined, all pending requests will be flushed.
+ */
+ flush(count?: number): void;
+
+ /**
+ * Resets all request expectations, but preserves all backend definitions.
+ */
+ resetExpectations(): void;
+
+ /**
+ * Verifies that all of the requests defined via the expect api were made. If any of the requests were not made, verifyNoOutstandingExpectation throws an exception.
+ */
+ verifyNoOutstandingExpectation(): void;
+
+ /**
+ * Verifies that there are no outstanding requests that need to be flushed.
+ */
+ verifyNoOutstandingRequest(): void;
+
+ /**
+ * Creates a new request expectation.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param method HTTP method.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ expect(method: string, url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)) :mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for DELETE requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url is as expected.
+ * @param headers HTTP headers object to be compared with the HTTP headers in the request.
+ */
+ expectDELETE(url: string | RegExp | ((url: string) => boolean), headers?: Object): mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for GET requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param headers HTTP headers object to be compared with the HTTP headers in the request.
+ */
+ expectGET(url: string | RegExp | ((url: string) => boolean), headers?: Object): mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for HEAD requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param headers HTTP headers object to be compared with the HTTP headers in the request.
+ */
+ expectHEAD(url: string | RegExp | ((url: string) => boolean), headers?: Object): mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for JSONP requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ */
+ expectJSONP(url: string | RegExp | ((url: string) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for PATCH requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ expectPATCH(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object): mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for POST requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ expectPOST(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object): mock.IRequestHandler;
+
+ /**
+ * Creates a new request expectation for PUT requests.
+ * Throws a preformatted error if expectation(s) don't match supplied string, regular expression, object, or if function returns false.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ expectPUT(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param method HTTP method.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ when(method: string, url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for DELETE requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenDELETE(url: string | RegExp | ((url: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for GET requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenGET(url: string | RegExp | ((url: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for HEAD requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenHEAD(url: string | RegExp | ((url: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for JSONP requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenJSONP(url: string | RegExp | ((url: string) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for PATCH requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenPATCH(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for POST requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenPOST(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+
+ /**
+ * Creates a new backend definition for PUT requests.
+ * Returns an object with respond method that controls how a matched request is handled.
+ * @param url HTTP url string, regular expression or function that receives a url and returns true if the url matches the current expctation.
+ * @param data HTTP request body string, json object, regular expression or function that receives the data and returns true if the data matches the current expectation.
+ * @param headers HTTP headers object or function that receives the headers and returns true if the headers match the current expectation.
+ */
+ whenPUT(url: string | RegExp | ((url: string) => boolean), data?: string | RegExp | Object | ((data: string) => boolean), headers?: Object | ((object: Object) => boolean)): mock.IRequestHandler;
+ }
+
+ export module mock {
+ // returned interface by the the mocked HttpBackendService expect/when methods
+ interface IRequestHandler {
+
+ /**
+ * Controls the response for a matched request using a function to construct the response.
+ * Returns the RequestHandler object for possible overrides.
+ * @param func Function that receives the request HTTP method, url, data, and headers and returns an array containing response status (number), data, headers, and status text.
+ */
+ respond(func: ((method: string, url: string, data: string | Object, headers: Object) => [number, string | Object, Object, string])): IRequestHandler;
+
+ /**
+ * Controls the response for a matched request using supplied static data to construct the response.
+ * Returns the RequestHandler object for possible overrides.
+ * @param status HTTP status code to add to the response.
+ * @param data Data to add to the response.
+ * @param headers Headers object to add to the response.
+ * @param responseText Response text to add to the response.
+ */
+ respond(status: number, data: string | Object, headers?: Object, responseText?: string): IRequestHandler;
+
+ /**
+ * Controls the response for a matched request using the HTTP status code 200 and supplied static data to construct the response.
+ * Returns the RequestHandler object for possible overrides.
+ * @param data Data to add to the response.
+ * @param headers Headers object to add to the response.
+ * @param responseText Response text to add to the response.
+ */
+ respond(data: string | Object, headers?: Object, responseText?: string): IRequestHandler;
+
+ // Available when ngMockE2E is loaded
+ /**
+ * Any request matching a backend definition or expectation with passThrough handler will be passed through to the real backend (an XHR request will be made to the server.)
+ */
+ passThrough(): IRequestHandler;
+ }
+
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// functions attached to global object (window)
+///////////////////////////////////////////////////////////////////////////////
+//Use `angular.mock.module` instead of `module`, as `module` conflicts with commonjs.
+//declare var module: (...modules: any[]) => any;
+declare var inject: angular.IInjectStatic;
diff --git a/catalog-ui/typings/angularjs/angular-resource.d.ts b/catalog-ui/typings/angularjs/angular-resource.d.ts
new file mode 100644
index 0000000000..969997b1bc
--- /dev/null
+++ b/catalog-ui/typings/angularjs/angular-resource.d.ts
@@ -0,0 +1,183 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Angular JS 1.3 (ngResource module)
+// Project: http://angularjs.org
+// Definitions by: Diego Vilar <http://github.com/diegovilar>, Michael Jess <http://github.com/miffels>
+// Definitions: https://github.com/daptiv/DefinitelyTyped
+
+/// <reference path="angular.d.ts" />
+///////////////////////////////////////////////////////////////////////////////
+// ngResource module (angular-resource.js)
+///////////////////////////////////////////////////////////////////////////////
+declare module angular.resource {
+
+ /**
+ * Currently supported options for the $resource factory options argument.
+ */
+ interface IResourceOptions {
+ /**
+ * If true then the trailing slashes from any calculated URL will be stripped (defaults to true)
+ */
+ stripTrailingSlashes?: boolean;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ResourceService
+ // see http://docs.angularjs.org/api/ngResource.$resource
+ // Most part of the following definitions were achieved by analyzing the
+ // actual implementation, since the documentation doesn't seem to cover
+ // that deeply.
+ ///////////////////////////////////////////////////////////////////////////
+ interface IResourceService {
+ (url: string, paramDefaults?: any,
+ /** example: {update: { method: 'PUT' }, delete: deleteDescriptor }
+ where deleteDescriptor : IActionDescriptor */
+ actions?: any, options?: IResourceOptions): IResourceClass<IResource<any>>;
+ <T, U>(url: string, paramDefaults?: any,
+ /** example: {update: { method: 'PUT' }, delete: deleteDescriptor }
+ where deleteDescriptor : IActionDescriptor */
+ actions?: any, options?: IResourceOptions): U;
+ <T>(url: string, paramDefaults?: any,
+ /** example: {update: { method: 'PUT' }, delete: deleteDescriptor }
+ where deleteDescriptor : IActionDescriptor */
+ actions?: any, options?: IResourceOptions): IResourceClass<T>;
+ }
+
+ // Just a reference to facilitate describing new actions
+ interface IActionDescriptor {
+ method: string;
+ isArray?: boolean;
+ params?: any;
+ headers?: any;
+ url?: any;
+ cache?: Boolean;
+ transformRequest?: Function;
+ transformResponse?: Function;
+ }
+
+ // Baseclass for everyresource with default actions.
+ // If you define your new actions for the resource, you will need
+ // to extend this interface and typecast the ResourceClass to it.
+ //
+ // In case of passing the first argument as anything but a function,
+ // it's gonna be considered data if the action method is POST, PUT or
+ // PATCH (in other words, methods with body). Otherwise, it's going
+ // to be considered as parameters to the request.
+ // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L461-L465
+ //
+ // Only those methods with an HTTP body do have 'data' as first parameter:
+ // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L463
+ // More specifically, those methods are POST, PUT and PATCH:
+ // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L432
+ //
+ // Also, static calls always return the IResource (or IResourceArray) retrieved
+ // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L538-L549
+ interface IResourceClass<T> {
+ new(dataOrParams? : any) : T;
+ get(): T;
+ get(params: Object): T;
+ get(success: Function, error?: Function): T;
+ get(params: Object, success: Function, error?: Function): T;
+ get(params: Object, data: Object, success?: Function, error?: Function): T;
+
+ query(): IResourceArray<T>;
+ query(params: Object): IResourceArray<T>;
+ query(success: Function, error?: Function): IResourceArray<T>;
+ query(params: Object, success: Function, error?: Function): IResourceArray<T>;
+ query(params: Object, data: Object, success?: Function, error?: Function): IResourceArray<T>;
+
+ save(): T;
+ save(data: Object): T;
+ save(success: Function, error?: Function): T;
+ save(data: Object, success: Function, error?: Function): T;
+ save(params: Object, data: Object, success?: Function, error?: Function): T;
+
+ remove(): T;
+ remove(params: Object): T;
+ remove(success: Function, error?: Function): T;
+ remove(params: Object, success: Function, error?: Function): T;
+ remove(params: Object, data: Object, success?: Function, error?: Function): T;
+
+ delete(): T;
+ delete(params: Object): T;
+ delete(success: Function, error?: Function): T;
+ delete(params: Object, success: Function, error?: Function): T;
+ delete(params: Object, data: Object, success?: Function, error?: Function): T;
+ }
+
+ // Instance calls always return the the promise of the request which retrieved the object
+ // https://github.com/angular/angular.js/blob/v1.2.0/src/ngResource/resource.js#L538-L546
+ interface IResource<T> {
+ $get(): angular.IPromise<T>;
+ $get(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
+ $get(success: Function, error?: Function): angular.IPromise<T>;
+
+ $query(): angular.IPromise<IResourceArray<T>>;
+ $query(params?: Object, success?: Function, error?: Function): angular.IPromise<IResourceArray<T>>;
+ $query(success: Function, error?: Function): angular.IPromise<IResourceArray<T>>;
+
+ $save(): angular.IPromise<T>;
+ $save(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
+ $save(success: Function, error?: Function): angular.IPromise<T>;
+
+ $remove(): angular.IPromise<T>;
+ $remove(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
+ $remove(success: Function, error?: Function): angular.IPromise<T>;
+
+ $delete(): angular.IPromise<T>;
+ $delete(params?: Object, success?: Function, error?: Function): angular.IPromise<T>;
+ $delete(success: Function, error?: Function): angular.IPromise<T>;
+
+ /** the promise of the original server interaction that created this instance. **/
+ $promise : angular.IPromise<T>;
+ $resolved : boolean;
+ }
+
+ /**
+ * Really just a regular Array object with $promise and $resolve attached to it
+ */
+ interface IResourceArray<T> extends Array<T> {
+ /** the promise of the original server interaction that created this collection. **/
+ $promise : angular.IPromise<IResourceArray<T>>;
+ $resolved : boolean;
+ }
+
+ /** when creating a resource factory via IModule.factory */
+ interface IResourceServiceFactoryFunction<T> {
+ ($resource: angular.resource.IResourceService): IResourceClass<T>;
+ <U extends IResourceClass<T>>($resource: angular.resource.IResourceService): U;
+ }
+}
+
+/** extensions to base ng based on using angular-resource */
+declare module angular {
+
+ interface IModule {
+ /** creating a resource service factory */
+ factory(name: string, resourceServiceFactoryFunction: angular.resource.IResourceServiceFactoryFunction<any>): IModule;
+ }
+}
+
+interface Array<T>
+{
+ /** the promise of the original server interaction that created this collection. **/
+ $promise : angular.IPromise<Array<T>>;
+ $resolved : boolean;
+}
diff --git a/catalog-ui/typings/angularjs/angular.d.ts b/catalog-ui/typings/angularjs/angular.d.ts
new file mode 100644
index 0000000000..a5f56c0ccd
--- /dev/null
+++ b/catalog-ui/typings/angularjs/angular.d.ts
@@ -0,0 +1,1613 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Angular JS 1.3+
+// Project: http://angularjs.org
+// Definitions by: Diego Vilar <http://github.com/diegovilar>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+
+/// <reference path="../jquery/jquery.d.ts" />
+
+declare var angular: angular.IAngularStatic;
+
+// Support for painless dependency injection
+interface Function {
+ $inject?: string[];
+}
+
+// Collapse angular into ng
+import ng = angular;
+// Support AMD require
+declare module 'angular' {
+ export = angular;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ng module (angular.js)
+///////////////////////////////////////////////////////////////////////////////
+declare module angular {
+
+ // not directly implemented, but ensures that constructed class implements $get
+ interface IServiceProviderClass {
+ new (...args: any[]): IServiceProvider;
+ }
+
+ interface IServiceProviderFactory {
+ (...args: any[]): IServiceProvider;
+ }
+
+ // All service providers extend this interface
+ interface IServiceProvider {
+ $get: any;
+ }
+
+ interface IAngularBootstrapConfig {
+ strictDi?: boolean;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // AngularStatic
+ // see http://docs.angularjs.org/api
+ ///////////////////////////////////////////////////////////////////////////
+ interface IAngularStatic {
+ bind(context: any, fn: Function, ...args: any[]): Function;
+
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: string, modules?: string, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: string, modules?: Function, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: string, modules?: string[], config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: JQuery, modules?: string, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: JQuery, modules?: Function, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: JQuery, modules?: string[], config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: Element, modules?: string, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: Element, modules?: Function, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: Element, modules?: string[], config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: Document, modules?: string, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: Document, modules?: Function, config?: IAngularBootstrapConfig): auto.IInjectorService;
+ /**
+ * Use this function to manually start up angular application.
+ *
+ * @param element DOM element which is the root of angular application.
+ * @param modules An array of modules to load into the application.
+ * Each item in the array should be the name of a predefined module or a (DI annotated)
+ * function that will be invoked by the injector as a run block.
+ * @param config an object for defining configuration options for the application. The following keys are supported:
+ * - `strictDi`: disable automatic function annotation for the application. This is meant to assist in finding bugs which break minified code.
+ */
+ bootstrap(element: Document, modules?: string[], config?: IAngularBootstrapConfig): auto.IInjectorService;
+
+ /**
+ * Creates a deep copy of source, which should be an object or an array.
+ *
+ * - If no destination is supplied, a copy of the object or array is created.
+ * - If a destination is provided, all of its elements (for array) or properties (for objects) are deleted and then all elements/properties from the source are copied to it.
+ * - If source is not an object or array (inc. null and undefined), source is returned.
+ * - If source is identical to 'destination' an exception will be thrown.
+ *
+ * @param source The source that will be used to make a copy. Can be any type, including primitives, null, and undefined.
+ * @param destination Destination into which the source is copied. If provided, must be of the same type as source.
+ */
+ copy<T>(source: T, destination?: T): T;
+
+ /**
+ * Wraps a raw DOM element or HTML string as a jQuery element.
+ *
+ * If jQuery is available, angular.element is an alias for the jQuery function. If jQuery is not available, angular.element delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
+ */
+ element: IAugmentedJQueryStatic;
+ equals(value1: any, value2: any): boolean;
+ extend(destination: any, ...sources: any[]): any;
+
+ /**
+ * Invokes the iterator function once for each item in obj collection, which can be either an object or an array. The iterator function is invoked with iterator(value, key), where value is the value of an object property or an array element and key is the object property key or array element index. Specifying a context for the function is optional.
+ *
+ * It is worth noting that .forEach does not iterate over inherited properties because it filters using the hasOwnProperty method.
+ *
+ * @param obj Object to iterate over.
+ * @param iterator Iterator function.
+ * @param context Object to become context (this) for the iterator function.
+ */
+ forEach<T>(obj: T[], iterator: (value: T, key: number) => any, context?: any): any;
+ /**
+ * Invokes the iterator function once for each item in obj collection, which can be either an object or an array. The iterator function is invoked with iterator(value, key), where value is the value of an object property or an array element and key is the object property key or array element index. Specifying a context for the function is optional.
+ *
+ * It is worth noting that .forEach does not iterate over inherited properties because it filters using the hasOwnProperty method.
+ *
+ * @param obj Object to iterate over.
+ * @param iterator Iterator function.
+ * @param context Object to become context (this) for the iterator function.
+ */
+ forEach<T>(obj: { [index: string]: T; }, iterator: (value: T, key: string) => any, context?: any): any;
+ /**
+ * Invokes the iterator function once for each item in obj collection, which can be either an object or an array. The iterator function is invoked with iterator(value, key), where value is the value of an object property or an array element and key is the object property key or array element index. Specifying a context for the function is optional.
+ *
+ * It is worth noting that .forEach does not iterate over inherited properties because it filters using the hasOwnProperty method.
+ *
+ * @param obj Object to iterate over.
+ * @param iterator Iterator function.
+ * @param context Object to become context (this) for the iterator function.
+ */
+ forEach(obj: any, iterator: (value: any, key: any) => any, context?: any): any;
+
+ fromJson(json: string): any;
+ identity(arg?: any): any;
+ injector(modules?: any[]): auto.IInjectorService;
+ isArray(value: any): boolean;
+ isDate(value: any): boolean;
+ isDefined(value: any): boolean;
+ isElement(value: any): boolean;
+ isFunction(value: any): boolean;
+ isNumber(value: any): boolean;
+ isObject(value: any): boolean;
+ isString(value: any): boolean;
+ isUndefined(value: any): boolean;
+ lowercase(str: string): string;
+
+ /**
+ * The angular.module is a global place for creating, registering and retrieving Angular modules. All modules (angular core or 3rd party) that should be available to an application must be registered using this mechanism.
+ *
+ * When passed two or more arguments, a new module is created. If passed only one argument, an existing module (the name passed as the first argument to module) is retrieved.
+ *
+ * @param name The name of the module to create or retrieve.
+ * @param requires The names of modules this module depends on. If specified then new module is being created. If unspecified then the module is being retrieved for further configuration.
+ * @param configFn Optional configuration function for the module.
+ */
+ module(
+ name: string,
+ requires?: string[],
+ configFn?: Function): IModule;
+
+ noop(...args: any[]): void;
+ reloadWithDebugInfo(): void;
+ toJson(obj: any, pretty?: boolean): string;
+ uppercase(str: string): string;
+ version: {
+ full: string;
+ major: number;
+ minor: number;
+ dot: number;
+ codeName: string;
+ };
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Module
+ // see http://docs.angularjs.org/api/angular.Module
+ ///////////////////////////////////////////////////////////////////////////
+ interface IModule {
+ animation(name: string, animationFactory: Function): IModule;
+ animation(name: string, inlineAnnotatedFunction: any[]): IModule;
+ animation(object: Object): IModule;
+ /**
+ * Use this method to register work which needs to be performed on module loading.
+ *
+ * @param configFn Execute this function on module load. Useful for service configuration.
+ */
+ config(configFn: Function): IModule;
+ /**
+ * Use this method to register work which needs to be performed on module loading.
+ *
+ * @param inlineAnnotatedFunction Execute this function on module load. Useful for service configuration.
+ */
+ config(inlineAnnotatedFunction: any[]): IModule;
+ /**
+ * Register a constant service, such as a string, a number, an array, an object or a function, with the $injector. Unlike value it can be injected into a module configuration function (see config) and it cannot be overridden by an Angular decorator.
+ *
+ * @param name The name of the constant.
+ * @param value The constant value.
+ */
+ constant(name: string, value: any): IModule;
+ constant(object: Object): IModule;
+ /**
+ * The $controller service is used by Angular to create new controllers.
+ *
+ * This provider allows controller registration via the register method.
+ *
+ * @param name Controller name, or an object map of controllers where the keys are the names and the values are the constructors.
+ * @param controllerConstructor Controller constructor fn (optionally decorated with DI annotations in the array notation).
+ */
+ controller(name: string, controllerConstructor: Function): IModule;
+ /**
+ * The $controller service is used by Angular to create new controllers.
+ *
+ * This provider allows controller registration via the register method.
+ *
+ * @param name Controller name, or an object map of controllers where the keys are the names and the values are the constructors.
+ * @param controllerConstructor Controller constructor fn (optionally decorated with DI annotations in the array notation).
+ */
+ controller(name: string, inlineAnnotatedConstructor: any[]): IModule;
+ controller(object: Object): IModule;
+ /**
+ * Register a new directive with the compiler.
+ *
+ * @param name Name of the directive in camel-case (i.e. ngBind which will match as ng-bind)
+ * @param directiveFactory An injectable directive factory function.
+ */
+ directive(name: string, directiveFactory: IDirectiveFactory): IModule;
+ /**
+ * Register a new directive with the compiler.
+ *
+ * @param name Name of the directive in camel-case (i.e. ngBind which will match as ng-bind)
+ * @param directiveFactory An injectable directive factory function.
+ */
+ directive(name: string, inlineAnnotatedFunction: any[]): IModule;
+ directive(object: Object): IModule;
+ /**
+ * Register a service factory, which will be called to return the service instance. This is short for registering a service where its provider consists of only a $get property, which is the given service factory function. You should use $provide.factory(getFn) if you do not need to configure your service in a provider.
+ *
+ * @param name The name of the instance.
+ * @param $getFn The $getFn for the instance creation. Internally this is a short hand for $provide.provider(name, {$get: $getFn}).
+ */
+ factory(name: string, $getFn: Function): IModule;
+ /**
+ * Register a service factory, which will be called to return the service instance. This is short for registering a service where its provider consists of only a $get property, which is the given service factory function. You should use $provide.factory(getFn) if you do not need to configure your service in a provider.
+ *
+ * @param name The name of the instance.
+ * @param inlineAnnotatedFunction The $getFn for the instance creation. Internally this is a short hand for $provide.provider(name, {$get: $getFn}).
+ */
+ factory(name: string, inlineAnnotatedFunction: any[]): IModule;
+ factory(object: Object): IModule;
+ filter(name: string, filterFactoryFunction: Function): IModule;
+ filter(name: string, inlineAnnotatedFunction: any[]): IModule;
+ filter(object: Object): IModule;
+ provider(name: string, serviceProviderFactory: IServiceProviderFactory): IModule;
+ provider(name: string, serviceProviderConstructor: IServiceProviderClass): IModule;
+ provider(name: string, inlineAnnotatedConstructor: any[]): IModule;
+ provider(name: string, providerObject: IServiceProvider): IModule;
+ provider(object: Object): IModule;
+ /**
+ * Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.
+ */
+ run(initializationFunction: Function): IModule;
+ /**
+ * Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.
+ */
+ run(inlineAnnotatedFunction: any[]): IModule;
+ service(name: string, serviceConstructor: Function): IModule;
+ service(name: string, inlineAnnotatedConstructor: any[]): IModule;
+ service(object: Object): IModule;
+ /**
+ * Register a value service with the $injector, such as a string, a number, an array, an object or a function. This is short for registering a service where its provider's $get property is a factory function that takes no arguments and returns the value service.
+
+ Value services are similar to constant services, except that they cannot be injected into a module configuration function (see config) but they can be overridden by an Angular decorator.
+ *
+ * @param name The name of the instance.
+ * @param value The value.
+ */
+ value(name: string, value: any): IModule;
+ value(object: Object): IModule;
+
+ // Properties
+ name: string;
+ requires: string[];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Attributes
+ // see http://docs.angularjs.org/api/ng.$compile.directive.Attributes
+ ///////////////////////////////////////////////////////////////////////////
+ interface IAttributes {
+ /**
+ * this is necessary to be able to access the scoped attributes. it's not very elegant
+ * because you have to use attrs['foo'] instead of attrs.foo but I don't know of a better way
+ * this should really be limited to return string but it creates this problem: http://stackoverflow.com/q/17201854/165656
+ */
+ [name: string]: any;
+
+ /**
+ * Adds the CSS class value specified by the classVal parameter to the
+ * element. If animations are enabled then an animation will be triggered
+ * for the class addition.
+ */
+ $addClass(classVal: string): void;
+
+ /**
+ * Removes the CSS class value specified by the classVal parameter from the
+ * element. If animations are enabled then an animation will be triggered for
+ * the class removal.
+ */
+ $removeClass(classVal: string): void;
+
+ /**
+ * Set DOM element attribute value.
+ */
+ $set(key: string, value: any): void;
+
+ /**
+ * Observes an interpolated attribute.
+ * The observer function will be invoked once during the next $digest
+ * following compilation. The observer is then invoked whenever the
+ * interpolated value changes.
+ */
+ $observe(name: string, fn: (value?: any) => any): Function;
+
+ /**
+ * A map of DOM element attribute names to the normalized name. This is needed
+ * to do reverse lookup from normalized name back to actual name.
+ */
+ $attr: Object;
+ }
+
+ /**
+ * form.FormController - type in module ng
+ * see https://docs.angularjs.org/api/ng/type/form.FormController
+ */
+ interface IFormController {
+
+ /**
+ * Indexer which should return ng.INgModelController for most properties but cannot because of "All named properties must be assignable to string indexer type" constraint - see https://github.com/Microsoft/TypeScript/issues/272
+ */
+ [name: string]: any;
+
+ $pristine: boolean;
+ $dirty: boolean;
+ $valid: boolean;
+ $invalid: boolean;
+ $submitted: boolean;
+ $error: any;
+ $addControl(control: INgModelController): void;
+ $removeControl(control: INgModelController): void;
+ $setValidity(validationErrorKey: string, isValid: boolean, control: INgModelController): void;
+ $setDirty(): void;
+ $setPristine(): void;
+ $commitViewValue(): void;
+ $rollbackViewValue(): void;
+ $setSubmitted(): void;
+ $setUntouched(): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // NgModelController
+ // see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
+ ///////////////////////////////////////////////////////////////////////////
+ interface INgModelController {
+ $render(): void;
+ $setValidity(validationErrorKey: string, isValid: boolean): void;
+ // Documentation states viewValue and modelValue to be a string but other
+ // types do work and it's common to use them.
+ $setViewValue(value: any, trigger?: string): void;
+ $setPristine(): void;
+ $validate(): void;
+ $setTouched(): void;
+ $setUntouched(): void;
+ $rollbackViewValue(): void;
+ $commitViewValue(): void;
+ $isEmpty(value: any): boolean;
+
+ $viewValue: any;
+
+ $modelValue: any;
+
+ $parsers: IModelParser[];
+ $formatters: IModelFormatter[];
+ $viewChangeListeners: IModelViewChangeListener[];
+ $error: any;
+ $name: string;
+
+ $touched: boolean;
+ $untouched: boolean;
+
+ $validators: IModelValidators;
+ $asyncValidators: IAsyncModelValidators;
+
+ $pending: any;
+ $pristine: boolean;
+ $dirty: boolean;
+ $valid: boolean;
+ $invalid: boolean;
+ }
+
+ interface IModelValidators {
+ [index: string]: (...args: any[]) => boolean;
+ }
+
+ interface IAsyncModelValidators {
+ [index: string]: (...args: any[]) => IPromise<boolean>;
+ }
+
+ interface IModelParser {
+ (value: any): any;
+ }
+
+ interface IModelFormatter {
+ (value: any): any;
+ }
+
+ interface IModelViewChangeListener {
+ (): void;
+ }
+
+ /**
+ * $rootScope - $rootScopeProvider - service in module ng
+ * see https://docs.angularjs.org/api/ng/type/$rootScope.Scope and https://docs.angularjs.org/api/ng/service/$rootScope
+ */
+ interface IRootScopeService {
+ [index: string]: any;
+
+ $apply(): any;
+ $apply(exp: string): any;
+ $apply(exp: (scope: IScope) => any): any;
+
+ $applyAsync(): any;
+ $applyAsync(exp: string): any;
+ $applyAsync(exp: (scope: IScope) => any): any;
+
+ $broadcast(name: string, ...args: any[]): IAngularEvent;
+ $destroy(): void;
+ $digest(): void;
+ $emit(name: string, ...args: any[]): IAngularEvent;
+
+ $eval(): any;
+ $eval(expression: string, locals?: Object): any;
+ $eval(expression: (scope: IScope) => any, locals?: Object): any;
+
+ $evalAsync(): void;
+ $evalAsync(expression: string): void;
+ $evalAsync(expression: (scope: IScope) => any): void;
+
+ // Defaults to false by the implementation checking strategy
+ $new(isolate?: boolean, parent?: IScope): IScope;
+
+ /**
+ * Listens on events of a given type. See $emit for discussion of event life cycle.
+ *
+ * The event listener function format is: function(event, args...).
+ *
+ * @param name Event name to listen on.
+ * @param listener Function to call when the event is emitted.
+ */
+ $on(name: string, listener: (event: IAngularEvent, ...args: any[]) => any): Function;
+
+ $watch(watchExpression: string, listener?: string, objectEquality?: boolean): Function;
+ $watch(watchExpression: string, listener?: (newValue: any, oldValue: any, scope: IScope) => any, objectEquality?: boolean): Function;
+ $watch(watchExpression: (scope: IScope) => any, listener?: string, objectEquality?: boolean): Function;
+ $watch(watchExpression: (scope: IScope) => any, listener?: (newValue: any, oldValue: any, scope: IScope) => any, objectEquality?: boolean): Function;
+
+ $watchCollection(watchExpression: string, listener: (newValue: any, oldValue: any, scope: IScope) => any): Function;
+ $watchCollection(watchExpression: (scope: IScope) => any, listener: (newValue: any, oldValue: any, scope: IScope) => any): Function;
+
+ $watchGroup(watchExpressions: any[], listener: (newValue: any, oldValue: any, scope: IScope) => any): Function;
+ $watchGroup(watchExpressions: { (scope: IScope): any }[], listener: (newValue: any, oldValue: any, scope: IScope) => any): Function;
+
+ $parent: IScope;
+ $root: IRootScopeService;
+ $id: number;
+
+ // Hidden members
+ $$isolateBindings: any;
+ $$phase: any;
+ }
+
+ interface IScope extends IRootScopeService { }
+
+ /**
+ * $scope for ngRepeat directive.
+ * see https://docs.angularjs.org/api/ng/directive/ngRepeat
+ */
+ interface IRepeatScope extends IScope {
+
+ /**
+ * iterator offset of the repeated element (0..length-1).
+ */
+ $index: number;
+
+ /**
+ * true if the repeated element is first in the iterator.
+ */
+ $first: boolean;
+
+ /**
+ * true if the repeated element is between the first and last in the iterator.
+ */
+ $middle: boolean;
+
+ /**
+ * true if the repeated element is last in the iterator.
+ */
+ $last: boolean;
+
+ /**
+ * true if the iterator position $index is even (otherwise false).
+ */
+ $even: boolean;
+
+ /**
+ * true if the iterator position $index is odd (otherwise false).
+ */
+ $odd: boolean;
+
+ }
+
+ interface IAngularEvent {
+ /**
+ * the scope on which the event was $emit-ed or $broadcast-ed.
+ */
+ targetScope: IScope;
+ /**
+ * the scope that is currently handling the event. Once the event propagates through the scope hierarchy, this property is set to null.
+ */
+ currentScope: IScope;
+ /**
+ * name of the event.
+ */
+ name: string;
+ /**
+ * calling stopPropagation function will cancel further event propagation (available only for events that were $emit-ed).
+ */
+ stopPropagation?: Function;
+ /**
+ * calling preventDefault sets defaultPrevented flag to true.
+ */
+ preventDefault: Function;
+ /**
+ * true if preventDefault was called.
+ */
+ defaultPrevented: boolean;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // WindowService
+ // see http://docs.angularjs.org/api/ng.$window
+ ///////////////////////////////////////////////////////////////////////////
+ interface IWindowService extends Window {
+ [key: string]: any;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // BrowserService
+ // TODO undocumented, so we need to get it from the source code
+ ///////////////////////////////////////////////////////////////////////////
+ interface IBrowserService {
+ defer: angular.ITimeoutService;
+ [key: string]: any;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // TimeoutService
+ // see http://docs.angularjs.org/api/ng.$timeout
+ ///////////////////////////////////////////////////////////////////////////
+ interface ITimeoutService {
+ (func: Function, delay?: number, invokeApply?: boolean): IPromise<any>;
+ cancel(promise: IPromise<any>): boolean;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // IntervalService
+ // see http://docs.angularjs.org/api/ng.$interval
+ ///////////////////////////////////////////////////////////////////////////
+ interface IIntervalService {
+ (func: Function, delay: number, count?: number, invokeApply?: boolean): IPromise<any>;
+ cancel(promise: IPromise<any>): boolean;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // AngularProvider
+ // see http://docs.angularjs.org/api/ng/provider/$animateProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface IAnimateProvider {
+ /**
+ * Registers a new injectable animation factory function.
+ *
+ * @param name The name of the animation.
+ * @param factory The factory function that will be executed to return the animation object.
+ */
+ register(name: string, factory: () => IAnimateCallbackObject): void;
+
+ /**
+ * Gets and/or sets the CSS class expression that is checked when performing an animation.
+ *
+ * @param expression The className expression which will be checked against all animations.
+ * @returns The current CSS className expression value. If null then there is no expression value.
+ */
+ classNameFilter(expression?: RegExp): RegExp;
+ }
+
+ /**
+ * The animation object which contains callback functions for each event that is expected to be animated.
+ */
+ interface IAnimateCallbackObject {
+ eventFn(element: Node, doneFn: () => void): Function;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // FilterService
+ // see http://docs.angularjs.org/api/ng.$filter
+ // see http://docs.angularjs.org/api/ng.$filterProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface IFilterService {
+ (name: string): Function;
+ }
+
+ interface IFilterProvider extends IServiceProvider {
+ register(name: string, filterFactory: Function): IServiceProvider;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // LocaleService
+ // see http://docs.angularjs.org/api/ng.$locale
+ ///////////////////////////////////////////////////////////////////////////
+ interface ILocaleService {
+ id: string;
+
+ // These are not documented
+ // Check angular's i18n files for exemples
+ NUMBER_FORMATS: ILocaleNumberFormatDescriptor;
+ DATETIME_FORMATS: ILocaleDateTimeFormatDescriptor;
+ pluralCat: (num: any) => string;
+ }
+
+ interface ILocaleNumberFormatDescriptor {
+ DECIMAL_SEP: string;
+ GROUP_SEP: string;
+ PATTERNS: ILocaleNumberPatternDescriptor[];
+ CURRENCY_SYM: string;
+ }
+
+ interface ILocaleNumberPatternDescriptor {
+ minInt: number;
+ minFrac: number;
+ maxFrac: number;
+ posPre: string;
+ posSuf: string;
+ negPre: string;
+ negSuf: string;
+ gSize: number;
+ lgSize: number;
+ }
+
+ interface ILocaleDateTimeFormatDescriptor {
+ MONTH: string[];
+ SHORTMONTH: string[];
+ DAY: string[];
+ SHORTDAY: string[];
+ AMPMS: string[];
+ medium: string;
+ short: string;
+ fullDate: string;
+ longDate: string;
+ mediumDate: string;
+ shortDate: string;
+ mediumTime: string;
+ shortTime: string;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // LogService
+ // see http://docs.angularjs.org/api/ng.$log
+ // see http://docs.angularjs.org/api/ng.$logProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface ILogService {
+ debug: ILogCall;
+ error: ILogCall;
+ info: ILogCall;
+ log: ILogCall;
+ warn: ILogCall;
+ }
+
+ interface ILogProvider {
+ debugEnabled(): boolean;
+ debugEnabled(enabled: boolean): ILogProvider;
+ }
+
+ // We define this as separate interface so we can reopen it later for
+ // the ngMock module.
+ interface ILogCall {
+ (...args: any[]): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ParseService
+ // see http://docs.angularjs.org/api/ng.$parse
+ // see http://docs.angularjs.org/api/ng.$parseProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface IParseService {
+ (expression: string): ICompiledExpression;
+ }
+
+ interface IParseProvider {
+ logPromiseWarnings(): boolean;
+ logPromiseWarnings(value: boolean): IParseProvider;
+
+ unwrapPromises(): boolean;
+ unwrapPromises(value: boolean): IParseProvider;
+ }
+
+ interface ICompiledExpression {
+ (context: any, locals?: any): any;
+
+ // If value is not provided, undefined is gonna be used since the implementation
+ // does not check the parameter. Let's force a value for consistency. If consumer
+ // whants to undefine it, pass the undefined value explicitly.
+ assign(context: any, value: any): any;
+ }
+
+ /**
+ * $location - $locationProvider - service in module ng
+ * see https://docs.angularjs.org/api/ng/service/$location
+ */
+ interface ILocationService {
+ absUrl(): string;
+ hash(): string;
+ hash(newHash: string): ILocationService;
+ host(): string;
+
+ /**
+ * Return path of current url
+ */
+ path(): string;
+
+ /**
+ * Change path when called with parameter and return $location.
+ * Note: Path should always begin with forward slash (/), this method will add the forward slash if it is missing.
+ *
+ * @param path New path
+ */
+ path(path: string): ILocationService;
+
+ port(): number;
+ protocol(): string;
+ replace(): ILocationService;
+
+ /**
+ * Return search part (as object) of current url
+ */
+ search(): any;
+
+ /**
+ * Change search part when called with parameter and return $location.
+ *
+ * @param search When called with a single argument the method acts as a setter, setting the search component of $location to the specified value.
+ *
+ * If the argument is a hash object containing an array of values, these values will be encoded as duplicate search parameters in the url.
+ */
+ search(search: any): ILocationService;
+
+ /**
+ * Change search part when called with parameter and return $location.
+ *
+ * @param search New search params
+ * @param paramValue If search is a string or a Number, then paramValue will override only a single search property. If paramValue is null, the property specified via the first argument will be deleted. If paramValue is an array, it will override the property of the search component of $location specified via the first argument. If paramValue is true, the property specified via the first argument will be added with no value nor trailing equal sign.
+ */
+ search(search: string, paramValue: string|number|string[]|boolean): ILocationService;
+
+ state(): any;
+ state(state: any): ILocationService;
+ url(): string;
+ url(url: string): ILocationService;
+ }
+
+ interface ILocationProvider extends IServiceProvider {
+ hashPrefix(): string;
+ hashPrefix(prefix: string): ILocationProvider;
+ html5Mode(): boolean;
+
+ // Documentation states that parameter is string, but
+ // implementation tests it as boolean, which makes more sense
+ // since this is a toggler
+ html5Mode(active: boolean): ILocationProvider;
+ html5Mode(mode: { enabled?: boolean; requireBase?: boolean; rewriteLinks?: boolean; }): ILocationProvider;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // DocumentService
+ // see http://docs.angularjs.org/api/ng.$document
+ ///////////////////////////////////////////////////////////////////////////
+ interface IDocumentService extends IAugmentedJQuery {}
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ExceptionHandlerService
+ // see http://docs.angularjs.org/api/ng.$exceptionHandler
+ ///////////////////////////////////////////////////////////////////////////
+ interface IExceptionHandlerService {
+ (exception: Error, cause?: string): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // RootElementService
+ // see http://docs.angularjs.org/api/ng.$rootElement
+ ///////////////////////////////////////////////////////////////////////////
+ interface IRootElementService extends JQuery {}
+
+ interface IQResolveReject<T> {
+ (): void;
+ (value: T): void;
+ }
+ /**
+ * $q - service in module ng
+ * A promise/deferred implementation inspired by Kris Kowal's Q.
+ * See http://docs.angularjs.org/api/ng/service/$q
+ */
+ interface IQService {
+ new (resolver: (resolve: IQResolveReject<any>) => any): IPromise<any>;
+ new (resolver: (resolve: IQResolveReject<any>, reject: IQResolveReject<any>) => any): IPromise<any>;
+ new <T>(resolver: (resolve: IQResolveReject<T>, reject: IQResolveReject<any>) => any): IPromise<T>;
+
+ /**
+ * Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
+ *
+ * Returns a single promise that will be resolved with an array of values, each value corresponding to the promise at the same index in the promises array. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
+ *
+ * @param promises An array of promises.
+ */
+ all(promises: IPromise<any>[]): IPromise<any[]>;
+ /**
+ * Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
+ *
+ * Returns a single promise that will be resolved with a hash of values, each value corresponding to the promise at the same key in the promises hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
+ *
+ * @param promises A hash of promises.
+ */
+ all(promises: { [id: string]: IPromise<any>; }): IPromise<{ [id: string]: any; }>;
+ /**
+ * Creates a Deferred object which represents a task which will finish in the future.
+ */
+ defer<T>(): IDeferred<T>;
+ /**
+ * Creates a promise that is resolved as rejected with the specified reason. This api should be used to forward rejection in a chain of promises. If you are dealing with the last promise in a promise chain, you don't need to worry about it.
+ *
+ * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of reject as the throw keyword in JavaScript. This also means that if you "catch" an error via a promise error callback and you want to forward the error to the promise derived from the current promise, you have to "rethrow" the error by returning a rejection constructed via reject.
+ *
+ * @param reason Constant, message, exception or an object representing the rejection reason.
+ */
+ reject(reason?: any): IPromise<any>;
+ /**
+ * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
+ *
+ * @param value Value or a promise
+ */
+ when<T>(value: IPromise<T>|T): IPromise<T>;
+ /**
+ * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
+ *
+ * @param value Value or a promise
+ */
+ when(): IPromise<void>;
+ }
+
+ interface IPromise<T> {
+ /**
+ * Regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason. Additionally, the notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.
+ *
+ * This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback. It also notifies via the return value of the notifyCallback method. The promise can not be resolved or rejected from the notifyCallback method.
+ */
+ then<TResult>(successCallback: (promiseValue: T) => IHttpPromise<TResult>|IPromise<TResult>|TResult, errorCallback?: (reason: any) => any, notifyCallback?: (state: any) => any): IPromise<TResult>;
+
+ /**
+ * Shorthand for promise.then(null, errorCallback)
+ */
+ catch<TResult>(onRejected: (reason: any) => IHttpPromise<TResult>|IPromise<TResult>|TResult): IPromise<TResult>;
+
+ /**
+ * Allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful to release resources or do some clean-up that needs to be done whether the promise was rejected or resolved. See the full specification for more information.
+ *
+ * Because finally is a reserved word in JavaScript and reserved keywords are not supported as property names by ES3, you'll need to invoke the method like promise['finally'](callback) to make your code IE8 and Android 2.x compatible.
+ */
+ finally<TResult>(finallyCallback: () => any): IPromise<TResult>;
+ }
+
+ interface IDeferred<T> {
+ resolve(value?: T): void;
+ reject(reason?: any): void;
+ notify(state?: any): void;
+ promise: IPromise<T>;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // AnchorScrollService
+ // see http://docs.angularjs.org/api/ng.$anchorScroll
+ ///////////////////////////////////////////////////////////////////////////
+ interface IAnchorScrollService {
+ (): void;
+ yOffset: any;
+ }
+
+ interface IAnchorScrollProvider extends IServiceProvider {
+ disableAutoScrolling(): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // CacheFactoryService
+ // see http://docs.angularjs.org/api/ng.$cacheFactory
+ ///////////////////////////////////////////////////////////////////////////
+ interface ICacheFactoryService {
+ // Lets not foce the optionsMap to have the capacity member. Even though
+ // it's the ONLY option considered by the implementation today, a consumer
+ // might find it useful to associate some other options to the cache object.
+ //(cacheId: string, optionsMap?: { capacity: number; }): CacheObject;
+ (cacheId: string, optionsMap?: { capacity: number; }): ICacheObject;
+
+ // Methods bellow are not documented
+ info(): any;
+ get(cacheId: string): ICacheObject;
+ }
+
+ interface ICacheObject {
+ info(): {
+ id: string;
+ size: number;
+
+ // Not garanteed to have, since it's a non-mandatory option
+ //capacity: number;
+ };
+ put<T>(key: string, value?: T): T;
+ get(key: string): any;
+ remove(key: string): void;
+ removeAll(): void;
+ destroy(): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // CompileService
+ // see http://docs.angularjs.org/api/ng.$compile
+ // see http://docs.angularjs.org/api/ng.$compileProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface ICompileService {
+ (element: string, transclude?: ITranscludeFunction, maxPriority?: number): ITemplateLinkingFunction;
+ (element: Element, transclude?: ITranscludeFunction, maxPriority?: number): ITemplateLinkingFunction;
+ (element: JQuery, transclude?: ITranscludeFunction, maxPriority?: number): ITemplateLinkingFunction;
+ }
+
+ interface ICompileProvider extends IServiceProvider {
+ directive(name: string, directiveFactory: Function): ICompileProvider;
+
+ // Undocumented, but it is there...
+ directive(directivesMap: any): ICompileProvider;
+
+ aHrefSanitizationWhitelist(): RegExp;
+ aHrefSanitizationWhitelist(regexp: RegExp): ICompileProvider;
+
+ imgSrcSanitizationWhitelist(): RegExp;
+ imgSrcSanitizationWhitelist(regexp: RegExp): ICompileProvider;
+
+ debugInfoEnabled(enabled?: boolean): any;
+ }
+
+ interface ICloneAttachFunction {
+ // Let's hint but not force cloneAttachFn's signature
+ (clonedElement?: JQuery, scope?: IScope): any;
+ }
+
+ // This corresponds to the "publicLinkFn" returned by $compile.
+ interface ITemplateLinkingFunction {
+ (scope: IScope, cloneAttachFn?: ICloneAttachFunction): IAugmentedJQuery;
+ }
+
+ // This corresponds to $transclude (and also the transclude function passed to link).
+ interface ITranscludeFunction {
+ // If the scope is provided, then the cloneAttachFn must be as well.
+ (scope: IScope, cloneAttachFn: ICloneAttachFunction): IAugmentedJQuery;
+ // If one argument is provided, then it's assumed to be the cloneAttachFn.
+ (cloneAttachFn?: ICloneAttachFunction): IAugmentedJQuery;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ControllerService
+ // see http://docs.angularjs.org/api/ng.$controller
+ // see http://docs.angularjs.org/api/ng.$controllerProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface IControllerService {
+ // Although the documentation doesn't state this, locals are optional
+ (controllerConstructor: Function, locals?: any): any;
+ (controllerName: string, locals?: any): any;
+ }
+
+ interface IControllerProvider extends IServiceProvider {
+ register(name: string, controllerConstructor: Function): void;
+ register(name: string, dependencyAnnotatedConstructor: any[]): void;
+ allowGlobals(): void;
+ }
+
+ /**
+ * HttpService
+ * see http://docs.angularjs.org/api/ng/service/$http
+ */
+ interface IHttpService {
+ /**
+ * Object describing the request to be made and how it should be processed.
+ */
+ <T>(config: IRequestConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform GET request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param config Optional configuration object
+ */
+ get<T>(url: string, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform DELETE request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param config Optional configuration object
+ */
+ delete<T>(url: string, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform HEAD request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param config Optional configuration object
+ */
+ head<T>(url: string, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform JSONP request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param config Optional configuration object
+ */
+ jsonp<T>(url: string, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform POST request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param data Request content
+ * @param config Optional configuration object
+ */
+ post<T>(url: string, data: any, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform PUT request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param data Request content
+ * @param config Optional configuration object
+ */
+ put<T>(url: string, data: any, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Shortcut method to perform PATCH request.
+ *
+ * @param url Relative or absolute URL specifying the destination of the request
+ * @param data Request content
+ * @param config Optional configuration object
+ */
+ patch<T>(url: string, data: any, config?: IRequestShortcutConfig): IHttpPromise<T>;
+
+ /**
+ * Runtime equivalent of the $httpProvider.defaults property. Allows configuration of default headers, withCredentials as well as request and response transformations.
+ */
+ defaults: IRequestConfig;
+
+ /**
+ * Array of config objects for currently pending requests. This is primarily meant to be used for debugging purposes.
+ */
+ pendingRequests: any[];
+ }
+
+ /**
+ * Object describing the request to be made and how it should be processed.
+ * see http://docs.angularjs.org/api/ng/service/$http#usage
+ */
+ interface IRequestShortcutConfig {
+ /**
+ * {Object.<string|Object>}
+ * Map of strings or objects which will be turned to ?key1=value1&key2=value2 after the url. If the value is not a string, it will be JSONified.
+ */
+ params?: any;
+
+ /**
+ * Map of strings or functions which return strings representing HTTP headers to send to the server. If the return value of a function is null, the header will not be sent.
+ */
+ headers?: any;
+
+ /**
+ * Name of HTTP header to populate with the XSRF token.
+ */
+ xsrfHeaderName?: string;
+
+ /**
+ * Name of cookie containing the XSRF token.
+ */
+ xsrfCookieName?: string;
+
+ /**
+ * {boolean|Cache}
+ * If true, a default $http cache will be used to cache the GET request, otherwise if a cache instance built with $cacheFactory, this cache will be used for caching.
+ */
+ cache?: any;
+
+ /**
+ * whether to to set the withCredentials flag on the XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5 for more information.
+ */
+ withCredentials?: boolean;
+
+ /**
+ * {string|Object}
+ * Data to be sent as the request message data.
+ */
+ data?: any;
+
+ /**
+ * {function(data, headersGetter)|Array.<function(data, headersGetter)>}
+ * Transform function or an array of such functions. The transform function takes the http request body and headers and returns its transformed (typically serialized) version.
+ */
+ transformRequest?: any;
+
+ /**
+ * {function(data, headersGetter)|Array.<function(data, headersGetter)>}
+ * Transform function or an array of such functions. The transform function takes the http response body and headers and returns its transformed (typically deserialized) version.
+ */
+ transformResponse?: any;
+
+ /**
+ * {number|Promise}
+ * Timeout in milliseconds, or promise that should abort the request when resolved.
+ */
+ timeout?: any;
+
+ /**
+ * See requestType.
+ */
+ responseType?: string;
+ }
+
+ /**
+ * Object describing the request to be made and how it should be processed.
+ * see http://docs.angularjs.org/api/ng/service/$http#usage
+ */
+ interface IRequestConfig extends IRequestShortcutConfig {
+ /**
+ * HTTP method (e.g. 'GET', 'POST', etc)
+ */
+ method: string;
+ /**
+ * Absolute or relative URL of the resource that is being requested.
+ */
+ url: string;
+ }
+
+ interface IHttpHeadersGetter {
+ (): { [name: string]: string; };
+ (headerName: string): string;
+ }
+
+ interface IHttpPromiseCallback<T> {
+ (data: T, status: number, headers: IHttpHeadersGetter, config: IRequestConfig): void;
+ }
+
+ interface IHttpPromiseCallbackArg<T> {
+ data?: T;
+ status?: number;
+ headers?: IHttpHeadersGetter;
+ config?: IRequestConfig;
+ statusText?: string;
+ }
+
+ interface IHttpPromise<T> extends IPromise<IHttpPromiseCallbackArg<T>> {
+ success(callback: IHttpPromiseCallback<T>): IHttpPromise<T>;
+ error(callback: IHttpPromiseCallback<any>): IHttpPromise<T>;
+ then<TResult>(successCallback: (response: IHttpPromiseCallbackArg<T>) => IPromise<TResult>|TResult, errorCallback?: (response: IHttpPromiseCallbackArg<any>) => any): IPromise<TResult>;
+ }
+
+ /**
+ * Object that controls the defaults for $http provider
+ * https://docs.angularjs.org/api/ng/service/$http#defaults
+ */
+ interface IHttpProviderDefaults {
+ xsrfCookieName?: string;
+ xsrfHeaderName?: string;
+ withCredentials?: boolean;
+ headers?: {
+ common?: any;
+ post?: any;
+ put?: any;
+ patch?: any;
+ }
+ }
+
+ interface IHttpProvider extends IServiceProvider {
+ defaults: IHttpProviderDefaults;
+ interceptors: any[];
+ useApplyAsync(): boolean;
+ useApplyAsync(value: boolean): IHttpProvider;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // HttpBackendService
+ // see http://docs.angularjs.org/api/ng.$httpBackend
+ // You should never need to use this service directly.
+ ///////////////////////////////////////////////////////////////////////////
+ interface IHttpBackendService {
+ // XXX Perhaps define callback signature in the future
+ (method: string, url: string, post?: any, callback?: Function, headers?: any, timeout?: number, withCredentials?: boolean): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // InterpolateService
+ // see http://docs.angularjs.org/api/ng.$interpolate
+ // see http://docs.angularjs.org/api/ng.$interpolateProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface IInterpolateService {
+ (text: string, mustHaveExpression?: boolean, trustedContext?: string, allOrNothing?: boolean): IInterpolationFunction;
+ endSymbol(): string;
+ startSymbol(): string;
+ }
+
+ interface IInterpolationFunction {
+ (context: any): string;
+ }
+
+ interface IInterpolateProvider extends IServiceProvider {
+ startSymbol(): string;
+ startSymbol(value: string): IInterpolateProvider;
+ endSymbol(): string;
+ endSymbol(value: string): IInterpolateProvider;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // TemplateCacheService
+ // see http://docs.angularjs.org/api/ng.$templateCache
+ ///////////////////////////////////////////////////////////////////////////
+ interface ITemplateCacheService extends ICacheObject {}
+
+ ///////////////////////////////////////////////////////////////////////////
+ // SCEService
+ // see http://docs.angularjs.org/api/ng.$sce
+ ///////////////////////////////////////////////////////////////////////////
+ interface ISCEService {
+ getTrusted(type: string, mayBeTrusted: any): any;
+ getTrustedCss(value: any): any;
+ getTrustedHtml(value: any): any;
+ getTrustedJs(value: any): any;
+ getTrustedResourceUrl(value: any): any;
+ getTrustedUrl(value: any): any;
+ parse(type: string, expression: string): (context: any, locals: any) => any;
+ parseAsCss(expression: string): (context: any, locals: any) => any;
+ parseAsHtml(expression: string): (context: any, locals: any) => any;
+ parseAsJs(expression: string): (context: any, locals: any) => any;
+ parseAsResourceUrl(expression: string): (context: any, locals: any) => any;
+ parseAsUrl(expression: string): (context: any, locals: any) => any;
+ trustAs(type: string, value: any): any;
+ trustAsHtml(value: any): any;
+ trustAsJs(value: any): any;
+ trustAsResourceUrl(value: any): any;
+ trustAsUrl(value: any): any;
+ isEnabled(): boolean;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // SCEProvider
+ // see http://docs.angularjs.org/api/ng.$sceProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface ISCEProvider extends IServiceProvider {
+ enabled(value: boolean): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // SCEDelegateService
+ // see http://docs.angularjs.org/api/ng.$sceDelegate
+ ///////////////////////////////////////////////////////////////////////////
+ interface ISCEDelegateService {
+ getTrusted(type: string, mayBeTrusted: any): any;
+ trustAs(type: string, value: any): any;
+ valueOf(value: any): any;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // SCEDelegateProvider
+ // see http://docs.angularjs.org/api/ng.$sceDelegateProvider
+ ///////////////////////////////////////////////////////////////////////////
+ interface ISCEDelegateProvider extends IServiceProvider {
+ resourceUrlBlacklist(blacklist: any[]): void;
+ resourceUrlWhitelist(whitelist: any[]): void;
+ }
+
+ /**
+ * $templateRequest service
+ * see http://docs.angularjs.org/api/ng/service/$templateRequest
+ */
+ interface ITemplateRequestService {
+ /**
+ * Downloads a template using $http and, upon success, stores the
+ * contents inside of $templateCache.
+ *
+ * If the HTTP request fails or the response data of the HTTP request is
+ * empty then a $compile error will be thrown (unless
+ * {ignoreRequestError} is set to true).
+ *
+ * @param tpl The template URL.
+ * @param ignoreRequestError Whether or not to ignore the exception
+ * when the request fails or the template is
+ * empty.
+ *
+ * @return A promise whose value is the template content.
+ */
+ (tpl: string, ignoreRequestError?: boolean): IPromise<string>;
+ /**
+ * total amount of pending template requests being downloaded.
+ * @type {number}
+ */
+ totalPendingRequests: number;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Directive
+ // see http://docs.angularjs.org/api/ng.$compileProvider#directive
+ // and http://docs.angularjs.org/guide/directive
+ ///////////////////////////////////////////////////////////////////////////
+
+ interface IDirectiveFactory {
+ (...args: any[]): IDirective;
+ }
+
+ interface IDirectiveLinkFn {
+ (
+ scope: IScope,
+ instanceElement: IAugmentedJQuery,
+ instanceAttributes: IAttributes,
+ controller: any,
+ transclude: ITranscludeFunction
+ ): void;
+ }
+
+ interface IDirectivePrePost {
+ pre?: IDirectiveLinkFn;
+ post?: IDirectiveLinkFn;
+ }
+
+ interface IDirectiveCompileFn {
+ (
+ templateElement: IAugmentedJQuery,
+ templateAttributes: IAttributes,
+ transclude: ITranscludeFunction
+ ): IDirectivePrePost;
+ }
+
+ interface IDirective {
+ compile?: IDirectiveCompileFn;
+ controller?: any;
+ controllerAs?: string;
+ bindToController?: boolean;
+ link?: IDirectiveLinkFn | IDirectivePrePost;
+ name?: string;
+ priority?: number;
+ replace?: boolean;
+ require?: any;
+ restrict?: string;
+ scope?: any;
+ template?: any;
+ templateUrl?: any;
+ terminal?: boolean;
+ transclude?: any;
+ }
+
+ /**
+ * angular.element
+ * when calling angular.element, angular returns a jQuery object,
+ * augmented with additional methods like e.g. scope.
+ * see: http://docs.angularjs.org/api/angular.element
+ */
+ interface IAugmentedJQueryStatic extends JQueryStatic {
+ (selector: string, context?: any): IAugmentedJQuery;
+ (element: Element): IAugmentedJQuery;
+ (object: {}): IAugmentedJQuery;
+ (elementArray: Element[]): IAugmentedJQuery;
+ (object: JQuery): IAugmentedJQuery;
+ (func: Function): IAugmentedJQuery;
+ (array: any[]): IAugmentedJQuery;
+ (): IAugmentedJQuery;
+ }
+
+ interface IAugmentedJQuery extends JQuery {
+ // TODO: events, how to define?
+ //$destroy
+
+ find(selector: string): IAugmentedJQuery;
+ find(element: any): IAugmentedJQuery;
+ find(obj: JQuery): IAugmentedJQuery;
+ controller(): any;
+ controller(name: string): any;
+ injector(): any;
+ scope(): IScope;
+ isolateScope(): IScope;
+
+ inheritedData(key: string, value: any): JQuery;
+ inheritedData(obj: { [key: string]: any; }): JQuery;
+ inheritedData(key?: string): any;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // AnimateService
+ // see http://docs.angularjs.org/api/ng.$animate
+ ///////////////////////////////////////////////////////////////////////
+ interface IAnimateService {
+ addClass(element: JQuery, className: string, done?: Function): IPromise<any>;
+ enter(element: JQuery, parent: JQuery, after: JQuery, done?: Function): void;
+ leave(element: JQuery, done?: Function): void;
+ move(element: JQuery, parent: JQuery, after: JQuery, done?: Function): void;
+ removeClass(element: JQuery, className: string, done?: Function): void;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // AUTO module (angular.js)
+ ///////////////////////////////////////////////////////////////////////////
+ export module auto {
+
+ ///////////////////////////////////////////////////////////////////////
+ // InjectorService
+ // see http://docs.angularjs.org/api/AUTO.$injector
+ ///////////////////////////////////////////////////////////////////////
+ interface IInjectorService {
+ annotate(fn: Function): string[];
+ annotate(inlineAnnotatedFunction: any[]): string[];
+ get(name: string): any;
+ has(name: string): boolean;
+ instantiate(typeConstructor: Function, locals?: any): any;
+ invoke(inlineAnnotatedFunction: any[]): any;
+ invoke(func: Function, context?: any, locals?: any): any;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+ // ProvideService
+ // see http://docs.angularjs.org/api/AUTO.$provide
+ ///////////////////////////////////////////////////////////////////////
+ interface IProvideService {
+ // Documentation says it returns the registered instance, but actual
+ // implementation does not return anything.
+ // constant(name: string, value: any): any;
+ /**
+ * Register a constant service, such as a string, a number, an array, an object or a function, with the $injector. Unlike value it can be injected into a module configuration function (see config) and it cannot be overridden by an Angular decorator.
+ *
+ * @param name The name of the constant.
+ * @param value The constant value.
+ */
+ constant(name: string, value: any): void;
+
+ /**
+ * Register a service decorator with the $injector. A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
+ *
+ * @param name The name of the service to decorate.
+ * @param decorator This function will be invoked when the service needs to be instantiated and should return the decorated service instance. The function is called using the injector.invoke method and is therefore fully injectable. Local injection arguments:
+ *
+ * $delegate - The original service instance, which can be monkey patched, configured, decorated or delegated to.
+ */
+ decorator(name: string, decorator: Function): void;
+ /**
+ * Register a service decorator with the $injector. A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
+ *
+ * @param name The name of the service to decorate.
+ * @param inlineAnnotatedFunction This function will be invoked when the service needs to be instantiated and should return the decorated service instance. The function is called using the injector.invoke method and is therefore fully injectable. Local injection arguments:
+ *
+ * $delegate - The original service instance, which can be monkey patched, configured, decorated or delegated to.
+ */
+ decorator(name: string, inlineAnnotatedFunction: any[]): void;
+ factory(name: string, serviceFactoryFunction: Function): IServiceProvider;
+ factory(name: string, inlineAnnotatedFunction: any[]): IServiceProvider;
+ provider(name: string, provider: IServiceProvider): IServiceProvider;
+ provider(name: string, serviceProviderConstructor: Function): IServiceProvider;
+ service(name: string, constructor: Function): IServiceProvider;
+ value(name: string, value: any): IServiceProvider;
+ }
+
+ }
+}
diff --git a/catalog-ui/typings/angularjs/restangular.d.ts b/catalog-ui/typings/angularjs/restangular.d.ts
new file mode 100644
index 0000000000..3428b0493f
--- /dev/null
+++ b/catalog-ui/typings/angularjs/restangular.d.ts
@@ -0,0 +1,156 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Restangular v1.4.0
+// Project: https://github.com/mgonto/restangular
+// Definitions by: Boris Yankov <https://github.com/borisyankov/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+
+/// <reference path="../angularjs/angular.d.ts" />
+
+// Support AMD require (copying angular.d.ts approach)
+// allows for import {IRequestConfig} from 'restangular' ES6 approach
+declare module 'restangular' {
+ export = restangular;
+}
+
+
+
+declare module restangular {
+
+ interface IPromise<T> extends angular.IPromise<T> {
+ call(methodName: string, params?: any): IPromise<T>;
+ get(fieldName: string): IPromise<T>;
+ $object: T;
+ }
+
+ interface ICollectionPromise<T> extends angular.IPromise<T[]> {
+ push(object: any): ICollectionPromise<T>;
+ call(methodName: string, params?: any): ICollectionPromise<T>;
+ get(fieldName: string): ICollectionPromise<T>;
+ $object: T[];
+ }
+
+ interface IResponse {
+ status: number;
+ data: any;
+ headers(name: string): string;
+ config: {
+ method: string;
+ url: string;
+ params: any;
+ }
+ }
+
+ interface IProvider {
+ setBaseUrl(baseUrl: string): void;
+ setExtraFields(fields: string[]): void;
+ setParentless(parentless: boolean, routes: string[]): void;
+ setDefaultHttpFields(httpFields: any): void;
+ addElementTransformer(route: string, transformer: Function): void;
+ addElementTransformer(route: string, isCollection: boolean, transformer: Function): void;
+ setTransformOnlyServerElements(active: boolean): void;
+ setOnElemRestangularized(callback: (elem: any, isCollection: boolean, what: string, restangular: IService) => any): void;
+ setResponseInterceptor(responseInterceptor: (data: any, operation: string, what: string, url: string, response: IResponse, deferred: angular.IDeferred<any>) => any): void;
+ setResponseExtractor(responseInterceptor: (data: any, operation: string, what: string, url: string, response: IResponse, deferred: angular.IDeferred<any>) => any): void;
+ addResponseInterceptor(responseInterceptor: (data: any, operation: string, what: string, url: string, response: IResponse, deferred: angular.IDeferred<any>) => any): void;
+ setRequestInterceptor(requestInterceptor: (element: any, operation: string, what: string, url: string) => any): void;
+ addRequestInterceptor(requestInterceptor: (element: any, operation: string, what: string, url: string) => any): void;
+ setFullRequestInterceptor(fullRequestInterceptor: (element: any, operation: string, what: string, url: string, headers: any, params: any, httpConfig: angular.IRequestShortcutConfig) => {element: any; headers: any; params: any}): void;
+ addFullRequestInterceptor(requestInterceptor: (element: any, operation: string, what: string, url: string, headers: any, params: any, httpConfig: angular.IRequestShortcutConfig) => {headers: any; params: any; element: any; httpConfig: angular.IRequestShortcutConfig}): void;
+ setErrorInterceptor(errorInterceptor: (response: IResponse, deferred: angular.IDeferred<any>) => any): void;
+ setRestangularFields(fields: {[fieldName: string]: string}): void;
+ setMethodOverriders(overriders: string[]): void;
+ setJsonp(jsonp: boolean): void;
+ setDefaultRequestParams(params: any): void;
+ setDefaultRequestParams(method: string, params: any): void;
+ setDefaultRequestParams(methods: string[], params: any): void;
+ setFullResponse(fullResponse: boolean): void;
+ setDefaultHeaders(headers: any): void;
+ setRequestSuffix(suffix: string): void;
+ setUseCannonicalId(useCannonicalId: boolean): void;
+ setEncodeIds(encode: boolean): void;
+ }
+
+ interface ICustom {
+ customGET(path: string, params?: any, headers?: any): IPromise<any>;
+ customGETLIST(path: string, params?: any, headers?: any): ICollectionPromise<any>;
+ customDELETE(path: string, params?: any, headers?: any): IPromise<any>;
+ customPOST(elem?: any, path?: string, params?: any, headers?: any): IPromise<any>;
+ customPUT(elem?: any, path?: string, params?: any, headers?: any): IPromise<any>;
+ customOperation(operation: string, path: string, params?: any, headers?: any, elem?: any): IPromise<any>;
+ addRestangularMethod(name: string, operation: string, path?: string, params?: any, headers?: any, elem?: any): IPromise<any>;
+ }
+
+ interface IService extends ICustom, IProvider {
+ one(route: string, id?: number): IElement;
+ one(route: string, id?: string): IElement;
+ oneUrl(route: string, url: string): IElement;
+ all(route: string): IElement;
+ allUrl(route: string, url: string): IElement;
+ copy(fromElement: any): IElement;
+ withConfig(configurer: (RestangularProvider: IProvider) => any): IService;
+ restangularizeElement(parent: any, element: any, route: string, collection?: any, reqParams?: any): IElement;
+ restangularizeCollection(parent: any, element: any, route: string): ICollection;
+ service(route: string, parent?: any): IService;
+ stripRestangular(element: any): any;
+ extendModel(route: string, extender: (model: IElement) => any): void;
+ }
+
+ interface IElement extends IService {
+ get(queryParams?: any, headers?: any): IPromise<any>;
+ get<T>(queryParams?: any, headers?: any): IPromise<T>;
+ getList(subElement?: any, queryParams?: any, headers?: any): ICollectionPromise<any>;
+ getList<T>(subElement?: any, queryParams?: any, headers?: any): ICollectionPromise<T>;
+ put(queryParams?: any, headers?: any): IPromise<any>;
+ post(subElement: any, elementToPost: any, queryParams?: any, headers?: any): IPromise<any>;
+ post<T>(subElement: any, elementToPost: T, queryParams?: any, headers?: any): IPromise<T>;
+ post(elementToPost: any, queryParams?: any, headers?: any): IPromise<any>;
+ post<T>(elementToPost: T, queryParams?: any, headers?: any): IPromise<T>;
+ remove(queryParams?: any, headers?: any): IPromise<any>;
+ head(queryParams?: any, headers?: any): IPromise<any>;
+ trace(queryParams?: any, headers?: any): IPromise<any>;
+ options(queryParams?: any, headers?: any): IPromise<any>;
+ patch(queryParams?: any, headers?: any): IPromise<any>;
+ clone(): IElement;
+ plain(): any;
+ plain<T>(): T;
+ withHttpConfig(httpConfig: angular.IRequestShortcutConfig): IElement;
+ save(queryParams?: any, headers?: any): IPromise<any>;
+ getRestangularUrl(): string;
+ }
+
+ interface ICollection extends IService, Array<any> {
+ getList(queryParams?: any, headers?: any): ICollectionPromise<any>;
+ getList<T>(queryParams?: any, headers?: any): ICollectionPromise<T>;
+ post(elementToPost: any, queryParams?: any, headers?: any): IPromise<any>;
+ post<T>(elementToPost: T, queryParams?: any, headers?: any): IPromise<T>;
+ head(queryParams?: any, headers?: any): IPromise<any>;
+ trace(queryParams?: any, headers?: any): IPromise<any>;
+ options(queryParams?: any, headers?: any): IPromise<any>;
+ patch(queryParams?: any, headers?: any): IPromise<any>;
+ putElement(idx: any, params: any, headers: any): IPromise<any>;
+ withHttpConfig(httpConfig: angular.IRequestShortcutConfig): ICollection;
+ clone(): ICollection;
+ plain(): any;
+ plain<T>(): T[];
+ getRestangularUrl(): string;
+ }
+}
diff --git a/catalog-ui/typings/cytoscape/cytoscape-extension.js b/catalog-ui/typings/cytoscape/cytoscape-extension.js
new file mode 100644
index 0000000000..dbf22c37b9
--- /dev/null
+++ b/catalog-ui/typings/cytoscape/cytoscape-extension.js
@@ -0,0 +1 @@
+//# sourceMappingURL=cytoscape-extension.js.map \ No newline at end of file
diff --git a/catalog-ui/typings/cytoscape/cytoscape-extension.js.map b/catalog-ui/typings/cytoscape/cytoscape-extension.js.map
new file mode 100644
index 0000000000..5eeda401dc
--- /dev/null
+++ b/catalog-ui/typings/cytoscape/cytoscape-extension.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"cytoscape-extension.js","sourceRoot":"","sources":["cytoscape-extension.ts"],"names":[],"mappings":""} \ No newline at end of file
diff --git a/catalog-ui/typings/cytoscape/cytoscape-extension.ts b/catalog-ui/typings/cytoscape/cytoscape-extension.ts
new file mode 100644
index 0000000000..88843af088
--- /dev/null
+++ b/catalog-ui/typings/cytoscape/cytoscape-extension.ts
@@ -0,0 +1,40 @@
+/**
+ * Created by obarda on 12/21/2016.
+ */
+declare module Cy {
+
+ interface Instance {
+
+ expandCollapse(options?: ExpandCollapseOptions);
+ collapseAll(options?: ExpandCollapseOptions);
+ }
+
+
+
+ interface CollectionNodes {
+
+ qtip(tooltipOptions: TooltipOption);
+ }
+
+
+ interface TooltipOption {
+ content?: string | Function;
+ position?: any;
+ style?: any;
+ show?:any;
+ hide?:any;
+ includeLabels?: boolean;
+ }
+
+ interface ExpandCollapseOptions {
+ layoutBy:any;
+ fisheye: boolean;
+ undoable: boolean;
+ expandCueImage: string;
+ collapseCueImage: string;
+ expandCollapseCueSize: number;
+ expandCollapseCueSensitivity: number;
+ cueOffset: number;
+ // expandCollapseCuePosition: string;
+ }
+} \ No newline at end of file
diff --git a/catalog-ui/typings/cytoscape/cytoscape.d.ts b/catalog-ui/typings/cytoscape/cytoscape.d.ts
new file mode 100644
index 0000000000..e10b6273ae
--- /dev/null
+++ b/catalog-ui/typings/cytoscape/cytoscape.d.ts
@@ -0,0 +1,3095 @@
+// Type definitions for Cytoscape.js 2.4.2
+// Project: http://js.cytoscape.org/
+// Definitions by: Fabian Schmidt
+//
+// Translation from Objects in help to Typescript interface.
+// cy --> Cy.Instance
+// the core
+// ele --> Cy.CollectionFirst
+// a collection of a single element (node or edge)
+// eles --> Cy.Collection
+// a collection of one or more elements (nodes and edges)
+// node --> Cy.CollectionFirstNode
+// a collection of a single node
+// nodes -> Cy.CollectionNodes
+// a collection of one or more nodes
+// edge --> Cy.CollectionFirstEdge
+// a collection of a single edge
+// edges -> Cy.CollectionEdges
+// a collection of one or more edges
+//
+// The library makes a diferrence between input and output parameter due to the dynamic behaviour of the Cytoscape library.
+// For a input parameter it will always expect:
+// - Cy.Collection
+// The input can be any element (node and edge) collection.
+// - Cy.CollectionNodes
+// The input must be a node collection.
+// - Cy.CollectionEdges
+// The input must be a edge collection.
+// - Cy.CollectionFirst
+// The input must be a single element.
+// - Cy.CollectionFirstNode
+// The inut must be a single node.
+// - Cy.CollectionFirstEdge
+// The input must be a single edge.
+//
+// For a output of a function it will always give:
+// - Cy.CollectionElements
+// The output is a collection of node and edge elements OR single element.
+// - Cy.CollectionEdges
+// The output is a collection of edge elements OR single edge.
+// - Cy.CollectionNodes
+// The output is a collection of node elements OR single node.
+
+declare module Cy {
+ /**
+ * See http://js.cytoscape.org/#selectors for details about writing selectors.
+ */
+ import ClipExtent = d3.geo.ClipExtent;
+ type Selector = string;
+
+ /**
+ * Possbile values are 'additive' or 'single'.
+ */
+ type SelectionType = string;
+
+ /**
+ * Possible values are 'nodes' or 'edges'.
+ */
+ type ElementGroup = string;
+
+ /**
+ * Possible values are 'x' or 'y'.
+ */
+ type PositionDimension = string;
+
+ /**
+ * Usually temp or nonserialisable data can be stored.
+ */
+ type Scratchpad = any;
+
+ interface CollectionElements extends CollectionEdges, CollectionNodes, CollectionFirstElement {
+ //Intentionally empty.
+ }
+
+ interface Extent {
+ x1: number, y1: number, x2: number, y2: number, w: number, h: number
+ }
+
+ interface CollectionEdges extends Collection, CollectionFirstEdge,
+ CollectionEdgesTraversing {
+ }
+
+ interface CollectionNodes extends Collection, CollectionFirstNode,
+ CollectionNodesMetadata, CollectionNodesPosition, CollectionNodesTraversing, CollectionNodesCompound {
+ }
+
+ interface Collection extends CollectionFirst,
+ CollectionManipulation, CollectionEvents, CollectionData, CollectionPosition, CollectionLayout, CollectionSelection, CollectionStyle, CollectionAnimation, CollectionComparision, CollectionIteration, CollectionBuildingUnion, CollectionAlgorithms, CollectionTraversing {
+ }
+
+ interface CollectionFirstElement extends CollectionFirstEdge, CollectionFirstNode {
+ //Intentionally empty.
+ }
+
+ interface CollectionFirstEdge extends CollectionFirst,
+ CollectionFirstEdgeData, CollectionFirstEdgeTraversing {
+ }
+
+ interface CollectionFirstNode extends CollectionFirst,
+ CollectionFirstNodeMetadata, CollectionFirstNodePosition, CollectionFirstNodeCompound {
+ }
+
+ interface CollectionFirst extends CollectionFirstManipulation, CollectionFirstData, CollectionFirstPosition, CollectionFirstSelection, CollectionFirstStyle, CollectionFirtsAnimation {
+ }
+
+ interface CollectionManipulation {
+ /**
+ * Remove the elements from the graph.
+ */
+ remove(): CollectionElements;
+
+ /**
+ * Put removed elements back into the graph.
+ */
+ restore(): CollectionElements;
+
+ /**
+ * Get a new collection containing clones (i.e. copies) of the elements in the calling collection.
+ */
+ clone(): CollectionElements;
+ /**
+ * Get a new collection containing clones (i.e. copies) of the elements in the calling collection.
+ */
+ copy(): CollectionElements;
+
+ /**
+ * Effectively move edges to different nodes. The modified (actually new) elements are returned.
+ */
+ move(location: { source?: string, target?: string }): CollectionEdges;
+ /**
+ * Effectively move nodes to different parent node. The modified (actually new) elements are returned.
+ */
+ move(location: { parent: string }): CollectionNodes;
+ }
+
+ interface CollectionEvents {
+ //TODO: http://js.cytoscape.org/#collection/events
+ //TODO: http://js.cytoscape.org/#collection/events
+
+ // on(event:string, callback: () => void);
+
+ on(events: string, any);
+ }
+
+ interface CollectionData {
+ //http://js.cytoscape.org/#collection/data
+
+ // The following fields are immutable:
+ //id: The id field is used to uniquely identify an element in the graph.
+ //source & target : These fields define an edge's relationship to nodes, and this relationship can not be changed after creation.
+ //parent: The parent field defines the parent (compound) node.
+
+ /**
+ * Remove developer-defined data associated with the elements.
+ */
+ removeData(): CollectionElements;
+ /**
+ * Remove developer-defined data associated with the elements.
+ *
+ * @param names A space-separated list of fields to delete.
+ */
+ removeData(names: string): CollectionElements;
+ /**
+ * Remove developer-defined data associated with the elements.
+ */
+ removeAttr(): CollectionElements;
+ /**
+ * Remove developer-defined data associated with the elements.
+ *
+ * @param names A space-separated list of fields to delete.
+ */
+ removeAttr(names: string): CollectionElements;
+
+ /**
+ * Get an array of the plain JavaScript object representation of all elements in the collection.
+ */
+ jsons(): string[];
+ }
+ interface CollectionNodesMetadata {
+ //http://js.cytoscape.org/#collection/metadata
+
+ /**
+ * Get the maximum degree of the nodes in the collection.
+ *
+ * For a node, the degree is the number of edge connections it has. Each time a node is referenced as source or target of an edge in the graph, that counts as an edge connection.
+ *
+ * For a set of nodes, the the total degree is the total number of edge connections to nodes in the set.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ maxDegree(includeLoops: boolean): number;
+ /**
+ * Get the minimum indegree of the nodes in the collection.
+ *
+ * For a node, the indegree is the number of incoming edge connections it has. Each time a node is referred to as target of an edge in the graph, that counts as an incoming edge connection.
+ *
+ * For a set of nodes, the the total degree is the total number of edge connections to nodes in the set.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ minIndegree(includeLoops: boolean): number;
+ /**
+ * Get the maximum indegree of the nodes in the collection.
+ *
+ * For a node, the indegree is the number of incoming edge connections it has. Each time a node is referred to as target of an edge in the graph, that counts as an incoming edge connection.
+ *
+ * For a set of nodes, the the total degree is the total number of edge connections to nodes in the set.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ maxIndegree(includeLoops: boolean): number;
+ /**
+ * Get the minimum outdegree of the nodes in the collection.
+ *
+ * For a node, the outdegree is the number of outgoing edge connections it has. Each time a node is referred to as source of an edge in the graph, that counts as an outgoing edge connection.
+ *
+ * For a set of nodes, the the total degree is the total number of edge connections to nodes in the set.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ minOutdegree(includeLoops: boolean): number;
+ /**
+ * Get the maximum outdegree of the nodes in the collection.
+ *
+ * For a node, the outdegree is the number of outgoing edge connections it has. Each time a node is referred to as source of an edge in the graph, that counts as an outgoing edge connection.
+ *
+ * For a set of nodes, the the total degree is the total number of edge connections to nodes in the set.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ maxOutdegree(includeLoops: boolean): number;
+ }
+
+ interface BoundingBox {
+ x1: number;
+ x2: number;
+ y1: number;
+ y2: number;
+ w: number;
+ h: number;
+ }
+ interface CollectionPosition {
+ //http://js.cytoscape.org/#collection/position--dimensions
+
+ /**
+ * Get the bounding box of the elements in model coordinates.
+ *
+ * @param options An object containing options for the function.
+ */
+ boundingBox(options: BoundingBoxOptions): BoundingBox;
+ /**
+ * Get the bounding box of the elements in model coordinates.
+ *
+ * @param options An object containing options for the function.
+ */
+ boundingbox(options?: BoundingBoxOptions): BoundingBox;
+
+ /**
+ * Get the bounding box of the elements in rendered coordinates.
+ *
+ * @param options An object containing options for the function.
+ */
+ renderedBoundingBox(options?: BoundingBoxOptions): BoundingBox;
+ /**
+ * Get the bounding box of the elements in rendered coordinates.
+ *
+ * @param options An object containing options for the function.
+ */
+ renderedBoundingbox(options?: BoundingBoxOptions): BoundingBox;
+ }
+ interface CollectionNodesPosition {
+ //http://js.cytoscape.org/#collection/position--dimensions
+
+ /**
+ * Set the positions functionally.
+ *
+ * @param callback A callback function that returns the position to set for each element.
+ * i - The index of the element when iterating over the elements in the collection.
+ * ele - The element being iterated over for which the function should return a position to set.
+ */
+ positions(callback: (i: number, ele: CollectionNodes) => Position): CollectionNodes;
+ /**
+ * Set positions for all nodes based on a single position object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ positions(pos: Position): CollectionNodes;
+
+ /**
+ * Set the positions functionally.
+ *
+ * @param callback A callback function that returns the position to set for each element.
+ * i - The index of the element when iterating over the elements in the collection.
+ * ele - The element being iterated over for which the function should return a position to set.
+ */
+ modelPositions(callback: (i: number, ele: CollectionNodes) => Position): CollectionNodes;
+ /**
+ * Set positions for all nodes based on a single position object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ modelPositions(pos: Position): CollectionNodes;
+
+ /**
+ * Set the positions functionally.
+ *
+ * @param callback A callback function that returns the position to set for each element.
+ * i - The index of the element when iterating over the elements in the collection.
+ * ele - The element being iterated over for which the function should return a position to set.
+ */
+ points(callback: (i: number, ele: CollectionNodes) => Position): CollectionNodes;
+ /**
+ * Set positions for all nodes based on a single position object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ points(pos: Position): CollectionNodes;
+
+ /**
+ * Allow the user to grab the nodes.
+ */
+ grabify(): CollectionNodes;
+
+ /**
+ * Disallow the user to grab the nodes.
+ */
+ ungrabify(): CollectionNodes;
+
+ /**
+ * Lock the nodes such that their positions can not be changed.
+ */
+ lock(): CollectionNodes;
+ /**
+ * Unlock the nodes such that their positions can be changed.
+ */
+ unlock(): CollectionNodes;
+ }
+ interface CollectionLayout {
+ // http://js.cytoscape.org/#collection/layout
+
+ /**
+ * Run a layout on the elements in the calling collection, algorithmically positioning the nodes.
+ * This function is useful for running a layout on a subset of the elements in the graph.
+ *
+ * @param options The layout options.
+ */
+ layout(options: LayoutOptions): CollectionElements;
+
+ /**
+ * Get a new layout, which can be used to algorithmically position the nodes in the collection.
+ * This function is useful for running a layout on a subset of the elements in the graph, perhaps in parallel to other layouts.
+ *
+ * You must specify options.name with the name of the layout you wish to use.
+ *
+ * Note: that you must call layout.run() in order for it to affect the graph.
+ *
+ * @param options The layout options.
+ */
+ makeLayout(options: LayoutOptions): LayoutInstance;
+ /**
+ * Get a new layout, which can be used to algorithmically position the nodes in the collection.
+ * This function is useful for running a layout on a subset of the elements in the graph, perhaps in parallel to other layouts.
+ *
+ * You must specify options.name with the name of the layout you wish to use.
+ *
+ * Note: that you must call layout.run() in order for it to affect the graph.
+ *
+ * @param options The layout options.
+ */
+ createLayout(options: LayoutOptions): LayoutInstance;
+ }
+ interface CollectionSelection {
+ // http://js.cytoscape.org/#collection/selection
+
+ /**
+ * Make the elements selected (NB other elements outside the collection are not affected).
+ */
+ select(): CollectionElements;
+
+ /**
+ * Make the elements not selected (NB other elements outside the collection are not affected).
+ */
+ unselect(): CollectionElements;
+ /**
+ * Make the elements not selected (NB other elements outside the collection are not affected).
+ */
+ deselect(): CollectionElements;
+
+ /**
+ * Make the selection states of the elements mutable.
+ */
+ selectify(): CollectionElements;
+
+ /**
+ * Make the selection states of the elements immutable.
+ */
+ unselectify(): CollectionElements;
+ }
+ interface CollectionStyle {
+ // http://js.cytoscape.org/#collection/style
+
+ /**
+ * Add classes to elements.
+ *
+ * @param classes A space-separated list of class names to add to the elements.
+ */
+ addClass(classes: string): CollectionElements;
+
+ /**
+ * Remove classes from elements.
+ *
+ * @param classes A space-separated list of class names to remove from the elements.
+ */
+ removeClass(classes: string): CollectionElements;
+
+ /**
+ * Toggle whether the elements have the specified classes.
+ *
+ * @param classes A space-separated list of class names to toggle on the elements.
+ * @param toggle [optional] Instead of automatically toggling, adds the classes on truthy values or removes them on falsey values.
+ */
+ toggleClass(classes: string, toggle?: boolean): CollectionElements;
+
+ /**
+ * Add classes to the elements, and then remove the classes after a specified duration.
+ *
+ * @param classes A space-separated list of class names to flash on the elements.
+ * @param duration [optional] The duration in milliseconds that the classes should be added on the elements. After the duration, the classes are removed.
+ */
+ flashClass(classes: string, duration?: number): CollectionElements;
+
+
+ /**
+ * Get a name-value pair object containing visual style properties and their values for the element.
+ */
+ style(): Css.ElementCss;
+ /**
+ * Get a particular style property value.
+ *
+ * @param name The name of the visual style property to get.
+ */
+ style(name: string): Css.ElementCss;
+
+ /**
+ * Get a name-value pair object containing visual style properties and their values for the element.
+ */
+ css(): Css.ElementCss;
+ /**
+ * Get a particular style property value.
+ *
+ * @param name The name of the visual style property to get.
+ */
+ css(name: string): Css.ElementCss;
+
+ /**
+ * Get a name-value pair object containing visual style properties and their values for the element.
+ */
+ bypass(): Css.ElementCss;
+ /**
+ * Get a particular style property value.
+ *
+ * @param name The name of the visual style property to get.
+ */
+ bypass(name: string): Css.ElementCss;
+
+
+ /**
+ * Set the specified visual style property for the elements.
+ *
+ * You should use this function very sparingly, because it overrides the style of an element, despite the state and classes that it has. In general, it's much better to specify a better stylesheet at initialisation that reflects your application state rather than programmatically modifying style.
+ *
+ * If you would like to remove a particular overridden style property, set null to it.
+ *
+ * @param name The name of the property to set.
+ * @param value The value to set to the visual style property.
+ */
+ style(name: string, value: string): CollectionElements;
+ /**
+ * Set several visual style properties at once for the elements.
+ *
+ * You should use this function very sparingly, because it overrides the style of an element, despite the state and classes that it has. In general, it's much better to specify a better stylesheet at initialisation that reflects your application state rather than programmatically modifying style.
+ *
+ * If you would like to remove a particular overridden style property, set null to it.
+ *
+ * @param props An object with name-value pairs representing properties to set on the elements.
+ */
+ style(props: Css.ElementCss): CollectionElements;
+
+ /**
+ * Set the specified visual style property for the elements.
+ *
+ * You should use this function very sparingly, because it overrides the style of an element, despite the state and classes that it has. In general, it's much better to specify a better stylesheet at initialisation that reflects your application state rather than programmatically modifying style.
+ *
+ * If you would like to remove a particular overridden style property, set null to it.
+ *
+ * @param name The name of the property to set.
+ * @param value The value to set to the visual style property.
+ */
+ css(name: string, value: string): CollectionElements;
+ /**
+ * Set several visual style properties at once for the elements.
+ *
+ * You should use this function very sparingly, because it overrides the style of an element, despite the state and classes that it has. In general, it's much better to specify a better stylesheet at initialisation that reflects your application state rather than programmatically modifying style.
+ *
+ * If you would like to remove a particular overridden style property, set null to it.
+ *
+ * @param props An object with name-value pairs representing properties to set on the elements.
+ */
+ css(props: Css.ElementCss): CollectionElements;
+
+ /**
+ * Set the specified visual style property for the elements.
+ *
+ * You should use this function very sparingly, because it overrides the style of an element, despite the state and classes that it has. In general, it's much better to specify a better stylesheet at initialisation that reflects your application state rather than programmatically modifying style.
+ *
+ * If you would like to remove a particular overridden style property, set null to it.
+ *
+ * @param name The name of the property to set.
+ * @param value The value to set to the visual style property.
+ */
+ bypass(name: string, value: string): CollectionElements;
+ /**
+ * Set several visual style properties at once for the elements.
+ *
+ * You should use this function very sparingly, because it overrides the style of an element, despite the state and classes that it has. In general, it's much better to specify a better stylesheet at initialisation that reflects your application state rather than programmatically modifying style.
+ *
+ * If you would like to remove a particular overridden style property, set null to it.
+ *
+ * @param props An object with name-value pairs representing properties to set on the elements.
+ */
+ bypass(props: Css.ElementCss): CollectionElements;
+
+ /**
+ * Removes all overridden style of the elements.
+ */
+ removeStyle(): CollectionElements;
+ /**
+ * Removes particular overridden style properties of the elements.
+ *
+ * @param names A space-separated list of property names for which overridden styles will be removed.
+ */
+ removeStyle(names: string): CollectionElements;
+ /**
+ * Removes all overridden style of the elements.
+ */
+ removeCss(): CollectionElements;
+ /**
+ * Removes particular overridden style properties of the elements.
+ *
+ * @param names A space-separated list of property names for which overridden styles will be removed.
+ */
+ removeCss(names: string): CollectionElements;
+ /**
+ * Removes all overridden style of the elements.
+ */
+ removeBypass(): CollectionElements;
+ /**
+ * Removes particular overridden style properties of the elements.
+ *
+ * @param names A space-separated list of property names for which overridden styles will be removed.
+ */
+ removeBypass(names: string): CollectionElements;
+ }
+ interface CollectionAnimation {
+ // http://js.cytoscape.org/#collection/animation
+
+ /**
+ * Animate the elements.
+ *
+ * Note that you can specify only one of position and renderedPosition: You can not animate to two positions at once.
+ *
+ * @param anis An object containing the details of the animation.
+ * position - A position to which the elements will be animated.
+ * renderedPosition - A rendered position to which the elements will be animated.
+ * style - An object containing name-value pairs of style properties to animate.
+ * @param options An object containing animation options.
+ * duration - The duration of the animation in milliseconds.
+ * queue - A boolean indicating whether to queue the animation.
+ * complete - A function to call when the animation is done.
+ * step - A function to call each time the animation steps.
+ */
+ animate(anis: {
+ postion?: Position,
+ renderedPosition?: Position,
+ style?: Css.ElementCss
+ }, options?: {
+ duration?: number,
+ queue?: boolean,
+ complete?: () => void,
+ step?: () => void
+ }): CollectionElements;
+
+ /**
+ * Add a delay between animations for the elements.
+ *
+ * @param duration How long the delay should be in milliseconds.
+ * @param complete A function to call when the delay is complete.
+ */
+ delay(duration: number, complete?: () => void): CollectionElements;
+
+ /**
+ * Stop all animations that are currently running.
+ *
+ * @param clearQueue A boolean, indicating whether the queue of animations should be emptied.
+ * @param jumpToEnd A boolean, indicating whether the currently-running animations should jump to their ends rather than just stopping midway.
+ */
+ stop(clearQueue?: boolean, jumpToEnd?: boolean): CollectionElements;
+
+ /**
+ * Remove all queued animations for the elements.
+ */
+ clearQueue(): CollectionElements;
+ }
+ interface CollectionComparision {
+ // http://js.cytoscape.org/#collection/comparison
+
+ /**
+ * Determine whether this collection contains exactly the same elements as another collection.
+ *
+ * @param eles The other elements to compare to.
+ */
+ same(eles: Collection): boolean;
+
+ /**
+ * Determine whether this collection contains any of the same elements as another collection.
+ *
+ * @param eles The other elements to compare to.
+ */
+ anySame(eles: Collection): boolean;
+
+ /**
+ * Determine whether all elements in the specified collection are in the neighbourhood of the calling collection.
+ *
+ * @param eles The other elements to compare to.
+ */
+ allAreNeighbors(eles: Collection): boolean;
+ /**
+ * Determine whether all elements in the specified collection are in the neighbourhood of the calling collection.
+ *
+ * @param eles The other elements to compare to.
+ */
+ allAreNeighbours(eles: Collection): boolean;
+
+ /**
+ * Determine whether any element in this collection matches a selector.
+ *
+ * @param selector The selector to match against.
+ */
+ is(selector: Selector): boolean;
+
+ /**
+ * Determine whether all elements in the collection match a selector.
+ * @param selector The selector to match against.
+ */
+ allAre(selector: Selector): boolean;
+
+ /**
+ * Determine whether any element in this collection satisfies the specified test function.
+ *
+ * @param test The test function that returns truthy values for elements that satisfy the test and falsey values for elements that do not satisfy the test.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being tested.
+ * @param thisArg [optional] The value for this within the test function.
+ */
+ some(test: (ele: CollectionElements, i: number, eles: CollectionElements) => boolean, thisArg?: any): boolean;
+
+ /**
+ * Determine whether all elements in this collection satisfy the specified test function.
+ *
+ * @param test The test function that returns truthy values for elements that satisfy the test and falsey values for elements that do not satisfy the test.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being tested.
+ * @param thisArg [optional] The value for this within the test function.
+ */
+ every(test: (ele: CollectionElements, i: number, eles: CollectionElements) => boolean, thisArg?: any): boolean;
+ }
+ interface CollectionIteration {
+ // http://js.cytoscape.org/#collection/iteration
+
+ /**
+ * Get the number of elements in the collection.
+ */
+ size(): number;
+ /**
+ * Get the number of elements in the collection.
+ */
+ length: number;
+
+ /**
+ * Get whether the collection is empty, meaning it has no elements.
+ */
+ empty(): boolean;
+ /**
+ * Get whether the collection is nonempty, meaning it has elements.
+ */
+ nonempty(): boolean;
+
+ /**
+ * Iterate over the elements in the collection.
+ *
+ * Note that although this function is convenient in some cases, it is less efficient than making your own loop.
+ *
+ * @param each The function executed each iteration.
+ * i - The index of the element in the collection.
+ * ele - The element at the current index.
+ */
+ each(each: (i: number, ele: CollectionElements) => void);
+
+ /**
+ * Iterate over the elements in the collection using an implementation like the native array function namesake.
+ *
+ * This function behaves like Array.prototype.forEach() with minor changes for convenience:
+ * You can exit the iteration early by returning false in the iterating function. The Array.prototype.forEach() implementation does not support this, but it is included anyway on account of its utility.
+ *
+ * @param each The function executed each iteration.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being iterated.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ forEach(each: (ele: CollectionElements, i: number, eles: CollectionElements) => void|boolean, thisArg?: any);
+
+ /**
+ * Get an element at a particular index in the collection.
+ *
+ * You may use eles[i] in place of eles.eq(i) as a more performant alternative.
+ *
+ * @param index The index of the element to get.
+ */
+ eq(index: number): CollectionElements;
+ /**
+ * Get an element at a particular index in the collection.
+ *
+ * @param index The index of the element to get.
+ */
+ [index: number]: CollectionElements;
+ /**
+ * Get the first element in the collection.
+ */
+ first(): CollectionElements;
+ /**
+ * Get the last element in the collection.
+ */
+ last(): CollectionElements;
+
+ /**
+ * Get a subset of the elements in the collection based on specified indices.
+ *
+ * @param start [optional] An integer that specifies where to start the selection. The first element has an index of 0. Use negative numbers to select from the end of an array.
+ * @param end [optional] An integer that specifies where to end the selection. If omitted, all elements from the start position and to the end of the array will be selected. Use negative numbers to select from the end of an array.
+ */
+ slice(start?: number, end?: number): CollectionElements;
+ }
+ interface CollectionBuildingUnion {
+ /**
+ * Get a new collection, resulting from adding the collection with another one
+ *
+ * @param eles The elements to add.
+ */
+ (eles: Collection): CollectionElements;
+ /**
+ * Get a new collection, resulting from adding the collection with another one
+ *
+ * @param elesArray An array of elements to add.
+ */
+ (elesArray: Collection[]): CollectionElements;
+ /**
+ * Get a new collection, resulting from adding the collection with another one
+ *
+ * @param selector Elements in the graph matching this selector are added.
+ */
+ (selector: Selector): CollectionElements;
+ }
+ interface CollectionBuildingDifference {
+ /**
+ * Get a new collection, resulting from the collection without some specified elements.
+ *
+ * @param eles The elements that will not be in the resultant collection.
+ */
+ (eles: Collection): CollectionElements;
+ /**
+ * Get a new collection, resulting from the collection without some specified elements.
+ *
+ * @param selector Elements from the calling collection matching this selector will not be in the resultant collection.
+ */
+ (selector: Selector): CollectionElements;
+ }
+ interface CollectionBuildingIntersection {
+ /**
+ * Get the elements in both this collection and another specified collection.
+ *
+ * @param eles The elements to intersect with.
+ */
+ (eles: Collection): CollectionElements;
+ /**
+ * Get the elements in both this collection and another specified collection.
+ *
+ * @param selector A selector representing the elements to intersect with. All elements in the graph matching the selector are used as the passed collection.
+ */
+ (selector: Selector): CollectionElements;
+ }
+ interface CollectionSymmetricDifference {
+ /**
+ * Get the elements that are in the calling collection or the passed collection but not in both.
+ *
+ * @param eles The elements to apply the symmetric difference with.
+ */
+ (eles: Collection): CollectionElements;
+ /**
+ * Get the elements that are in the calling collection or the passed collection but not in both.
+ *
+ * @param selector A selector representing the elements to apply the symmetric difference with. All elements in the graph matching the selector are used as the passed collection.
+ */
+ (selector: Selector): CollectionElements;
+ }
+ interface CollectionBuildingUnion {
+ // http://js.cytoscape.org/#collection/building--filtering
+
+ union: CollectionBuildingUnion;
+ //[index: "u"]: CollectionBuildingUnion;
+ add: CollectionBuildingUnion;
+ //[index: "+"]: CollectionBuildingUnion;
+ or: CollectionBuildingUnion;
+ //[index: "|"]: CollectionBuildingUnion;
+
+ difference: CollectionBuildingDifference;
+ //[index: "\\"]: CollectionBuildingDifference;
+ not: CollectionBuildingDifference;
+ //[index: "!"]: CollectionBuildingDifference;
+ relativeComplement(): CollectionBuildingDifference;
+ //[index: "-"]: CollectionBuildingDifference;
+
+ /**
+ * Get all elements in the graph that are not in the calling collection.
+ */
+ absoluteComplement(): CollectionElements;
+ /**
+ * Get all elements in the graph that are not in the calling collection.
+ */
+ abscomp(): CollectionElements;
+ /**
+ * Get all elements in the graph that are not in the calling collection.
+ */
+ complement(): CollectionElements;
+
+ intersection: CollectionBuildingIntersection;
+ intersect: CollectionBuildingIntersection;
+ and: CollectionBuildingIntersection;
+ //[index: "n"]: CollectionBuildingIntersection;
+ //[index: "&"]: CollectionBuildingIntersection;
+ //[index: "."]: CollectionBuildingIntersection;
+
+ symmetricDifference: CollectionSymmetricDifference;
+ symdiff: CollectionSymmetricDifference;
+ xor: CollectionSymmetricDifference;
+ //[index: "^"]: CollectionSymmetricDifference;
+ //[index: "(+)"]: CollectionSymmetricDifference;
+ //[index: "(-)"]: CollectionSymmetricDifference;
+
+ //[index: string]: CollectionBuildingDifference |CollectionBuildingUnion | CollectionBuildingIntersection | CollectionSymmetricDifference;
+
+ /**
+ * Perform a traditional left/right diff on the two collections.
+ *
+ * @param eles The elements on the right side of the diff.
+ */
+ diff(eles: Collection): CollectionElements;
+ /**
+ * Perform a traditional left/right diff on the two collections.
+ *
+ * @param selector A selector representing the elements on the right side of the diff. All elements in the graph matching the selector are used as the passed collection.
+ * @return This function returns a plain object of the form { left, right, both } where
+ * left - is the set of elements only in the calling (i.e. left) collection,
+ * right - is the set of elements only in the passed (i.e. right) collection, and
+ * both - is the set of elements in both collections.
+ */
+ diff(selector: Selector): {
+ left: CollectionElements,
+ right: CollectionElements,
+ both: CollectionElements
+ };
+
+ /**
+ * Get a new collection containing elements that are accepted by the specified filter.
+ *
+ * @param selector The selector to match against.
+ */
+ filter(selector: Selector): CollectionElements;
+ /**
+ * Get a new collection containing elements that are accepted by the specified filter.
+ *
+ * @filter selector The filter function that returns true for elements to include.
+ * i - The index of the current element being considered.
+ * ele - The element being considered.
+ */
+ filter(filter: (i: number, ele: CollectionElements) => boolean): CollectionElements;
+ /**
+ * Get the nodes that match the specified selector.
+ *
+ * @param selector The selector to match against.
+ */
+ nodes(selector: Selector): CollectionNodes;
+ /**
+ * Get the edges that match the specified selector.
+ *
+ * @param selector The selector to match against.
+ */
+ edges(selector: Selector): CollectionEdges;
+
+ /**
+ * Get a new collection containing elements that are accepted by the specified filter, using an implementation like the standard array namesake.
+ *
+ * @param filter The filter function that returns truthy values for elements to include and falsey values for elements to exclude.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being filtered.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ filterFn(filter: (ele: CollectionElements, i: number, eles: CollectionElements) => boolean, thisArg?: any): CollectionElements;
+ /**
+ * Get a new collection containing elements that are accepted by the specified filter, using an implementation like the standard array namesake.
+ *
+ * @param filter The filter function that returns truthy values for elements to include and falsey values for elements to exclude.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being filtered.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ fnFilter(filter: (ele: CollectionElements, i: number, eles: CollectionElements) => boolean, thisArg?: any): CollectionElements;
+ /**
+ * Get a new collection containing elements that are accepted by the specified filter, using an implementation like the standard array namesake.
+ *
+ * @param filter The filter function that returns truthy values for elements to include and falsey values for elements to exclude.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being filtered.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ stdFilter(filter: (ele: CollectionElements, i: number, eles: CollectionElements) => boolean, thisArg?: any): CollectionElements;
+
+ /**
+ * Get a new collection containing the elements sorted by the specified comparison function.
+ *
+ * @param sort The sorting comparison function that returns a negative number for ele1 before ele2, 0 for ele1 same as ele2, or a positive number for ele1 after ele2.
+ */
+ sort(sort: (ele1: CollectionElements, ele2: CollectionElements) => number): CollectionElements;
+
+ /**
+ * Get an array containing values mapped from the collection.
+ *
+ * @param fn The function that returns the mapped value for each element.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being mapped.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ map(fn: (ele: CollectionElements, i: number, eles: CollectionElements) => any, thisArg?: any): any[];
+
+ /**
+ * Find a minimum value in a collection.
+ *
+ * @param fn The function that returns the value to compare for each element.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being mapped.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ min(fn: (ele: CollectionElements, i: number, eles: CollectionElements) => any, thisArg?: any): {
+ /**
+ * The minimum value found.
+ */
+ value: any,
+ /**
+ * The element that corresponds to the minimum value.
+ */
+ ele: CollectionElements
+ };
+
+ /**
+ * Find a maximum value and the corresponding element.
+ *
+ * @param fn The function that returns the value to compare for each element.
+ * ele - The current element.
+ * i - The index of the current element.
+ * eles - The collection of elements being mapped.
+ * @param thisArg [optional] The value for this within the iterating function.
+ */
+ max(fn: (ele: CollectionElements, i: number, eles: CollectionElements) => any, thisArg?: any): {
+ /**
+ * The maximum value found.
+ */
+ value: any,
+ /**
+ * The element that corresponds to the maximum value.
+ */
+ ele: CollectionElements
+ };
+ }
+ interface CollectionTraversing {
+ // http://js.cytoscape.org/#collection/traversing
+
+ /**
+ * Get the open neighbourhood of the elements.
+ *
+ * The neighbourhood returned by this function is a bit different than the traditional definition of a "neighbourhood": This returned neighbourhood includes the edges connecting the collection to the neighbourhood. This gives you more flexibility.
+ * An open neighbourhood is one that does not include the original set of elements. If unspecified, a neighbourhood is open by default.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ neighborhood(selector?: Selector): CollectionElements;
+ /**
+ * Get the open neighbourhood of the elements.
+ *
+ * The neighbourhood returned by this function is a bit different than the traditional definition of a "neighbourhood": This returned neighbourhood includes the edges connecting the collection to the neighbourhood. This gives you more flexibility.
+ * An open neighbourhood is one that does not include the original set of elements. If unspecified, a neighbourhood is open by default.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ openNeighborhood(selector?: Selector): CollectionElements;
+ /**
+ * Get the closed neighbourhood of the elements.
+ *
+ * The neighbourhood returned by this function is a bit different than the traditional definition of a "neighbourhood": This returned neighbourhood includes the edges connecting the collection to the neighbourhood. This gives you more flexibility.
+ * A closed neighbourhood is one that does include the original set of elements.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ closedNeighborhood(selector?: Selector): CollectionElements;
+ }
+ interface CollectionEdgesTraversing {
+ // http://js.cytoscape.org/#collection/traversing
+
+ /**
+ * Get the nodes connected to the edges in the collection
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ connectedNodes(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get source nodes connected to the edges in the collection.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ sources(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get target nodes connected to the edges in the collection.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ targets(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get edges parallel to those in the collection.
+ *
+ * Two edges are said to be parallel if they connect the same two nodes. Any two parallel edges may connect nodes in the same direction, in which case the edges share the same source and target. They may alternatively connect nodes in the opposite direction, in which case the source and target are reversed in the second edge.
+ * That is:
+ * - edge1.source().id() === edge2.source().id()
+ * && edge1.target().id() === edge2.target().id()
+ * OR
+ * - edge1.source().id() === edge2.target().id()
+ * && edge1.target().id() === edge2.source().id()
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ parallelEdges(selector?: Selector): CollectionEdges;
+
+ /**
+ * Get edges codirected to those in the collection.
+ *
+ * Two edges are said to be codirected if they connect the same two nodes in the same direction: The edges have the same source and target.
+ * That is:
+ * - edge1.source().id() === edge2.source().id()
+ * && edge1.target().id() === edge2.target().id()
+
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ codirectedEdges(selector?: Selector): CollectionEdges;
+ }
+ interface CollectionNodesTraversing {
+ // http://js.cytoscape.org/#collection/traversing
+
+ /**
+ * Get the edges connecting the collection to another collection. Direction of the edges does not matter.
+ *
+ * @param eles The other collection.
+ */
+ edgesWith(eles: Collection): CollectionEdges;
+ /**
+ * Get the edges connecting the collection to another collection. Direction of the edges does not matter.
+ *
+ * @param selector The other collection, specified as a selector which is matched against all elements in the graph.
+ */
+ edgesWith(selector: Selector): CollectionEdges;
+
+ /**
+ * Get the edges coming from the collection (i.e. the source) going to another collection (i.e. the target).
+ *
+ * @param eles The other collection.
+ */
+ edgesTo(eles: Collection): CollectionEdges;
+ /**
+ * Get the edges coming from the collection (i.e. the source) going to another collection (i.e. the target).
+ *
+ * @param selector The other collection, specified as a selector which is matched against all elements in the graph.
+ */
+ edgesTo(selector: Selector): CollectionEdges;
+
+ /**
+ * Get the edges connected to the nodes in the collection.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ connectedEdges(selector?: Selector): CollectionEdges;
+
+ /**
+ * From the set of calling nodes, get the nodes which are leaves (i.e. no outgoing edges, as in a directed acyclic graph).
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ leaves(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get edges (and their targets) coming out of the nodes in the collection.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ outgoers(selector?: Selector): CollectionEdges;
+
+ /**
+ * Recursively get edges (and their targets) coming out of the nodes in the collection (i.e. the outgoers, the outgoers' outgoers, ...).
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ successors(selector?: Selector): CollectionEdges;
+
+ /**
+ * Get edges (and their sources) coming into the nodes in the collection.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ incomers(selector?: Selector): CollectionEdges;
+
+ /**
+ * Recursively get edges (and their sources) coming into the nodes in the collection (i.e. the incomers, the incomers' incomers, ...).
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ predecessors(selector?: Selector): CollectionEdges;
+ }
+ interface SearchBreadthFirstOptions {
+ /**
+ * The root nodes (selector or collection) to start the search from.
+ */
+ roots: Selector|Collection;
+ /**
+ * A handler function that is called when a node is visited in the search. The handler returns true when it finds the desired node, and it returns false to cancel the search.
+ * i - The index indicating this node is the ith visited node.
+ * depth - How many edge hops away this node is from the root nodes.
+ * v - The current node.
+ * e - The edge connecting the previous node to the current node.
+ * u - The previous node.
+ */
+ visit?: (i: number, depth: number, v: CollectionNodes, e: CollectionEdges, u: CollectionNodes) => boolean;
+ /**
+ * A boolean indicating whether the algorithm should only go along edges from source to target (default false).
+ */
+ directed?: boolean;
+ }
+ interface SearchBreadthFirstResult {
+ /**
+ * The path of the search.
+ * - The path returned includes edges such that if path[i] is a node, then path[i - 1] is the edge used to get to that node.
+ */
+ path: CollectionElements;
+ /**
+ * The node found by the search
+ * - If no node was found, then found is empty.
+ * - If your handler function returns false, then the only the path up to that point is returned.
+ */
+ found: CollectionNodes;
+ }
+ interface SearchDijkstraOptions {
+ /**
+ * The root node (selector or collection) where the algorithm starts.
+ */
+ root: Selector|Collection;
+
+ /**
+ * A function that returns the positive numeric weight for this edge.
+ *
+ * If no weight function is defined, a constant weight of 1 is used for each edge.
+ */
+ weight?: (edge: CollectionEdges) => number;
+
+ /**
+ * A boolean indicating whether the algorithm should only go along edges from source to target (default false).
+ */
+ directed?: boolean;
+ }
+ interface SearchDijkstraResult {
+ /**
+ * Returns the distance from the source node to node.
+ */
+ distanceTo: (node: CollectionFirstNode) => number;
+
+ /**
+ * Returns a collection containing the shortest path from the source node to node.
+ * The path starts with the source node and includes the edges between the nodes in the path such that if pathTo(node)[i] is an edge, then pathTo(node)[i-1] is the previous node in the path and pathTo(node)[i+1] is the next node in the path.
+ */
+ pathTo: (node: CollectionFirstNode) => Collection;
+ }
+ interface SearchAStarOptions {
+ }
+ interface SearchAStarResult {
+ }
+ interface CollectionAlgorithms {
+ // http://js.cytoscape.org/#collection/algorithms
+
+ /**
+ * Perform a breadth-first search within the elements in the collection.
+ *
+ * Note that this function performs a breadth-first search on only the subset of the graph in the calling collection.
+ */
+ breadthFirstSearch(options: SearchBreadthFirstOptions): SearchBreadthFirstResult;
+ /**
+ * Perform a breadth-first search within the elements in the collection.
+ *
+ * Note that this function performs a breadth-first search on only the subset of the graph in the calling collection.
+ *
+ * @param roots The root nodes (selector or collection) to start the search from.
+ * @param visit [optional] A handler function that is called when a node is visited in the search. The handler returns true when it finds the desired node, and it returns false to cancel the search.
+ * i - The index indicating this node is the ith visited node.
+ * depth - How many edge hops away this node is from the root nodes.
+ * v - The current node.
+ * e - The edge connecting the previous node to the current node.
+ * u - The previous node.
+ * @param directed [optional] A boolean indicating whether the search should only go along edges from source to target (default false).
+ */
+ breadthFirstSearch(roots: Selector|Collection,
+ visit?: (i: number, depth: number, v: CollectionNodes, e: CollectionEdges, u: CollectionNodes) => boolean,
+ directed?: boolean): SearchBreadthFirstResult;
+ /**
+ * Perform a breadth-first search within the elements in the collection.
+ *
+ * Note that this function performs a breadth-first search on only the subset of the graph in the calling collection.
+ */
+ bfs(options: SearchBreadthFirstOptions): SearchBreadthFirstResult;
+ /**
+ * Perform a breadth-first search within the elements in the collection.
+ *
+ * Note that this function performs a breadth-first search on only the subset of the graph in the calling collection.
+ *
+ * @param roots The root nodes (selector or collection) to start the search from.
+ * @param visit [optional] A handler function that is called when a node is visited in the search. The handler returns true when it finds the desired node, and it returns false to cancel the search.
+ * i - The index indicating this node is the ith visited node.
+ * depth - How many edge hops away this node is from the root nodes.
+ * v - The current node.
+ * e - The edge connecting the previous node to the current node.
+ * u - The previous node.
+ * @param directed [optional] A boolean indicating whether the search should only go along edges from source to target (default false).
+ */
+ bfs(roots: Selector|Collection,
+ visit?: (i: number, depth: number, v: CollectionNodes, e: CollectionEdges, u: CollectionNodes) => boolean,
+ directed?: boolean): SearchBreadthFirstResult;
+
+ /**
+ * Perform a depth-first search within the elements in the collection.
+ *
+ * Note that this function performs a depth-first search on only the subset of the graph in the calling collection.
+ */
+ depthFirstSearch(options: SearchBreadthFirstOptions): SearchBreadthFirstResult;
+ /**
+ * Perform a depth-first search within the elements in the collection.
+ *
+ * Note that this function performs a depth-first search on only the subset of the graph in the calling collection.
+ *
+ * @param roots The root nodes (selector or collection) to start the search from.
+ * @param visit [optional] A handler function that is called when a node is visited in the search. The handler returns true when it finds the desired node, and it returns false to cancel the search.
+ * i - The index indicating this node is the ith visited node.
+ * depth - How many edge hops away this node is from the root nodes.
+ * v - The current node.
+ * e - The edge connecting the previous node to the current node.
+ * u - The previous node.
+ * @param directed [optional] A boolean indicating whether the search should only go along edges from source to target (default false).
+ */
+ depthFirstSearch(roots: Selector|Collection,
+ visit?: (i: number, depth: number, v: CollectionNodes, e: CollectionEdges, u: CollectionNodes) => boolean,
+ directed?: boolean): SearchBreadthFirstResult;
+ /**
+ * Perform a depth-first search within the elements in the collection.
+ *
+ * Note that this function performs a depth-first search on only the subset of the graph in the calling collection.
+ */
+ dfs(options: SearchBreadthFirstOptions): SearchBreadthFirstResult;
+ /**
+ * Perform a depth-first search within the elements in the collection.
+ *
+ * Note that this function performs a depth-first search on only the subset of the graph in the calling collection.
+ *
+ * @param roots The root nodes (selector or collection) to start the search from.
+ * @param visit [optional] A handler function that is called when a node is visited in the search. The handler returns true when it finds the desired node, and it returns false to cancel the search.
+ * i - The index indicating this node is the ith visited node.
+ * depth - How many edge hops away this node is from the root nodes.
+ * v - The current node.
+ * e - The edge connecting the previous node to the current node.
+ * u - The previous node.
+ * @param directed [optional] A boolean indicating whether the search should only go along edges from source to target (default false).
+ */
+ dfs(roots: Selector|Collection,
+ visit?: (i: number, depth: number, v: CollectionNodes, e: CollectionEdges, u: CollectionNodes) => boolean,
+ directed?: boolean): SearchBreadthFirstResult;
+
+ /**
+ * Perform Dijkstra's algorithm on the elements in the collection. This finds the shortest paths to all other nodes in the collection from the root node.
+ *
+ * Note that this function performs Dijkstra's algorithm on only the subset of the graph in the calling collection.
+ */
+ dijkstra(options: SearchDijkstraOptions): SearchDijkstraResult;
+ /**
+ * Perform Dijkstra's algorithm on the elements in the collection. This finds the shortest paths to all other nodes in the collection from the root node.
+ *
+ * Note that this function performs Dijkstra's algorithm on only the subset of the graph in the calling collection.
+ *
+ * @param root The root node (selector or collection) where the algorithm starts.
+ * @param weight [optional] A function that returns the positive numeric weight for this edge.
+ * If no weight function is defined, a constant weight of 1 is used for each edge.
+ * @param directed [optional] A boolean indicating whether the algorithm should only go along edges from source to target (default false).
+ */
+ dijkstra(root: Selector|Collection,
+ weight?: (edge: CollectionEdges) => number,
+ directed?: boolean): SearchDijkstraResult;
+
+
+ //TODO: continue here: http://js.cytoscape.org/#collection/algorithms/eles.dijkstra
+ }
+ interface CollectionNodesCompound {
+ // http://js.cytoscape.org/#collection/compound-nodes
+
+ /**
+ * Get the compound parent node of each node in the collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ parent(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all compound ancestor nodes (i.e. parents, parents' parents, etc.) of each node in the collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ parents(selector?: Selector): CollectionNodes;
+ /**
+ * Get all compound ancestor nodes (i.e. parents, parents' parents, etc.) of each node in the collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ ancestors(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all compound ancestors common to all the nodes in the collection, starting with the closest and getting progressively farther.
+ *
+ * You can get the closest common ancestor via
+ * nodes.commonAncestors().first()
+ * and the farthest via
+ * nodes.commonAncestors().last()
+ * , because the common ancestors are in descending order of closeness.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection
+ */
+ commonAncestors(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all orphan (i.e. has no compound parent) nodes in the calling collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ orphans(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all nonorphan (i.e. has a compound parent) nodes in the calling collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ nonorphans(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all compound child (i.e. direct descendant) nodes of each node in the collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ children(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all compound descendant (i.e. children, children's children, etc.) nodes of each node in the collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ descendants(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get all sibling (i.e. same compound parent) nodes of each node in the collection.
+ *
+ * @param selector [optional] A selector used to filter the resultant collection.
+ */
+ siblings(selector?: Selector): CollectionNodes;
+ }
+
+ interface CollectionFirstManipulation {
+ /**
+ * Get whether the element has been removed from the graph.
+ */
+ removed(): boolean;
+ /**
+ * Get whether the element is inside the graph (i.e. not removed).
+ */
+ inside(): boolean;
+
+ }
+ interface CollectionFirstData {
+ //http://js.cytoscape.org/#collection/data
+
+ /**
+ * Get all data for the element.
+ */
+ data(): any;
+ /**
+ * Get a particular data field for the element.
+ */
+ data(name: string): any;
+ /**
+ * Set a particular data field for the element.
+ */
+ data(name: string, value: any);
+ /**
+ * Update multiple data fields at once via an object.
+ */
+ data(obj: any);
+
+ /**
+ * Get the entire scratchpad object for the element, where temporary or non-JSON data can be stored. App-level scratchpad data should use namespaces prefixed with underscore, like '_foo'.
+ */
+ scratch(): Scratchpad;
+ /**
+ * Get the scratchpad at a particular namespace, where temporary or non-JSON data can be stored. App-level scratchpad data should use namespaces prefixed with underscore, like '_foo'.
+ *
+ * @param namespace A namespace string.
+ */
+ scratch(namespace: string): Scratchpad;
+ /**
+ * Set the scratchpad at a particular namespace, where temporary or non-JSON data can be stored. App-level scratchpad data should use namespaces prefixed with underscore, like '_foo'.
+ *
+ * @param namespace A namespace string.
+ * @param value The value to set at the specified namespace.
+ */
+ scratch(namespace: string, value: any): Scratchpad;
+
+ /**
+ * Remove scratchpad data. You should remove scratchpad data only at your own namespaces.
+ *
+ * @param namespace A namespace string.
+ */
+ removeScratch(namespace: string);
+
+ /**
+ * A shortcut to get the ID of an element.
+ */
+ id(): string;
+
+ /**
+ * Get the element's plain JavaScript object representation.
+ */
+ json(): string;
+
+ /**
+ * Get the group string that defines the type of the element.
+ *
+ * The group strings are 'nodes' for nodes and 'edges' for edges. In general, you should be using ele.isEdge() and ele.isNode() instead of ele.group().
+ */
+ group(): ElementGroup;
+
+ /**
+ * Get whether the element is a node.
+ */
+ isNode(): boolean;
+
+ /**
+ * Get whether the element is an edge.
+ */
+ isEdge(): boolean;
+
+
+ cy(): Cy.Instance
+ }
+ interface CollectionFirstEdgeData {
+ //http://js.cytoscape.org/#collection/data
+
+ /**
+ * Get whether the edge is a loop (i.e. source same as target).
+ */
+ isLoop(): boolean;
+
+ /**
+ * Get whether the edge is simple (i.e. source different than target).
+ */
+ isSimple(): boolean;
+ }
+ interface CollectionFirstNodeMetadata {
+ //http://js.cytoscape.org/#collection/metadata
+
+ /**
+ * Get the degree of a node.
+ *
+ * For a node, the degree is the number of edge connections it has. Each time a node is referenced as source or target of an edge in the graph, that counts as an edge connection.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ degree(includeLoops: boolean): number;
+ /**
+ * Get the indegree of a node.
+ *
+ * For a node, the indegree is the number of incoming edge connections it has. Each time a node is referred to as target of an edge in the graph, that counts as an incoming edge connection.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ indegree(includeLoops: boolean): number;
+ /***
+ * Get the outdegree of a node.
+ *
+ * For a node, the outdegree is the number of outgoing edge connections it has. Each time a node is referred to as source of an edge in the graph, that counts as an outgoing edge connection.
+ *
+ * @param includeLoops A boolean, indicating whether loops are to be included in degree calculations.
+ */
+ outdegree(includeLoops: boolean): number;
+ }
+ interface CollectionFirstNodePosition extends CollectionPosition{
+ //http://js.cytoscape.org/#collection/position--dimensions
+
+ /**
+ * Get the (model) position of a node.
+ */
+ position(): Position;
+ /**
+ * Get the value of a specified position dimension.
+ *
+ * @param dimension The position dimension to get.
+ */
+ position(dimension: PositionDimension): Position;
+ /**
+ * Set the value of a specified position dimension.
+ *
+ * @param dimension The position dimension to set.
+ * @param value The value to set to the dimension.
+ */
+ position(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ position(pos: Position): CollectionNodes;
+
+ /**
+ * Get the (model) position of a node.
+ */
+ modelPosition(): Position;
+ /**
+ * Get the value of a specified position dimension.
+ *
+ * @param dimension The position dimension to get.
+ */
+ modelPosition(dimension: PositionDimension): Position;
+ /**
+ * Set the value of a specified position dimension.
+ *
+ * @param dimension The position dimension to set.
+ * @param value The value to set to the dimension.
+ */
+ modelPosition(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ modelPosition(pos: Position): CollectionNodes;
+
+ /**
+ * Get the (model) position of a node.
+ */
+ point(): Position;
+ /**
+ * Get the value of a specified position dimension.
+ *
+ * @param dimension The position dimension to get.
+ */
+ point(dimension: PositionDimension): Position;
+ /**
+ * Set the value of a specified position dimension.
+ *
+ * @param dimension The position dimension to set.
+ * @param value The value to set to the dimension.
+ */
+ point(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ point(pos: Position): CollectionNodes;
+
+ /**
+ * Get the rendered (on-screen) position of a node.
+ */
+ renderedPosition(): Position;
+ /**
+ * Get the value of a specified rendered posisition dimension.
+ *
+ * @param dimension The position dimension to get.
+ */
+ renderedPosition(dimension: PositionDimension): number;
+ /**
+ * Set the value of a specified rendered posisition dimension.
+ *
+ * @param dimension The position dimension to get.
+ * @param value The value to set to the dimension.
+ */
+ renderedPosition(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the rendered position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ renderedPosition(pos: Position): CollectionNodes;
+
+ /**
+ * Get the rendered (on-screen) position of a node.
+ */
+ renderedPoint(): Position;
+ /**
+ * Get the rendered (on-screen) position of a node.
+ *
+ * @param dimension The position dimension to get.
+ */
+ renderedPoint(dimension: PositionDimension): number;
+ /**
+ * Set the value of a specified rendered posisition dimension.
+ *
+ * @param dimension The position dimension to get.
+ * @param value The value to set to the dimension.
+ */
+ renderedPoint(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the rendered position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ renderedPoint(pos: Position): CollectionNodes;
+
+ /**
+ * Get the position of a node, relative to its compound parent.
+ */
+ relativePosition(): Position;
+ /**
+ * Get the position of a node, relative to its compound parent.
+ *
+ * @param dimension The position dimension to get.
+ */
+ relativePosition(dimension: PositionDimension): number;
+ /**
+ * Set the value of a specified relative posisition dimension.
+ *
+ * @param dimension The position dimension to get.
+ * @param value The value to set to the dimension.
+ */
+ relativePosition(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the relative position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ relativePosition(pos: Position): CollectionNodes;
+
+ /**
+ * Get the position of a node, relative to its compound parent.
+ */
+ relativePoint(): Position;
+ /**
+ * Get the position of a node, relative to its compound parent.
+ *
+ * @param dimension The position dimension to get.
+ */
+ relativePoint(dimension: PositionDimension): number;
+ /**
+ * Set the value of a specified relative posisition dimension.
+ *
+ * @param dimension The position dimension to get.
+ * @param value The value to set to the dimension.
+ */
+ relativePoint(dimension: PositionDimension, value: number): CollectionNodes;
+ /**
+ * Set the relative position using name-value pairs in the specified object.
+ *
+ * @param pos An object specifying name-value pairs representing dimensions to set.
+ */
+ relativePoint(pos: Position): CollectionNodes;
+ }
+ interface CollectionFirstPosition {
+ //http://js.cytoscape.org/#collection/position--dimensions
+
+ /**
+ * Get the width of the element.
+ */
+ width(): number;
+ /**
+ * Get the outer width of the element (includes width & border).
+ */
+ outerWidth(): number;
+ /**
+ * Get the width of the element in rendered dimensions.
+ */
+ renderedWidth(): number;
+ /**
+ * Get the outer width of the element (includes width & border) in rendered dimensions.
+ */
+ renderedOuterWidth(): number;
+
+ /**
+ * Get the height of the element.
+ */
+ height(): number;
+ /**
+ * Get the outer height of the element (includes height & border).
+ */
+ outerHeight(): number;
+ /**
+ * Get the height of the element in rendered dimensions.
+ */
+ renderedHeight(): number;
+ /**
+ * Get the outer height of the element (includes height & border) in rendered dimensions.
+ */
+ renderedOuterHeight(): number;
+
+ /**
+ * Gets whether the element is active (e.g. on user tap, grab, etc).
+ */
+ active(): boolean;
+ }
+ interface CollectionFirstSelection {
+ // http://js.cytoscape.org/#collection/selection
+
+ /**
+ * Get whether the element is selected.
+ */
+ selected(): boolean;
+
+ /**
+ * Get whether the element's selection state is mutable.
+ */
+ selectable(): boolean;
+ }
+ interface CollectionFirstStyle {
+ // http://js.cytoscape.org/#collection/style
+
+ /**
+ * Get whether an element has a particular class.
+ *
+ * @param className The name of the class to test for.
+ */
+ hasClass(className: string): boolean;
+
+ ///**
+ //* Get a name-value pair object containing visual style properties and their values for the element.
+ //*/
+ //style(): Css.ElementCss;
+ ///**
+ // * Get a particular style property value.
+ // *
+ // * @param name The name of the visual style property to get.
+ // */
+ //style(name: string): Css.ElementCss;
+
+ ///**
+ // * Get a name-value pair object containing visual style properties and their values for the element.
+ // */
+ //css(): Css.ElementCss;
+ ///**
+ // * Get a particular style property value.
+ // *
+ // * @param name The name of the visual style property to get.
+ // */
+ //css(name: string): Css.ElementCss;
+
+ ///**
+ // * Get a name-value pair object containing visual style properties and their values for the element.
+ // */
+ //bypass(): Css.ElementCss;
+ ///**
+ // * Get a particular style property value.
+ // *
+ // * @param name The name of the visual style property to get.
+ // */
+ //bypass(name: string): Css.ElementCss;
+
+ /**
+ * Get a name-value pair object containing rendered visual style properties and their values for the element.
+ */
+ renderedStyle(): Css.ElementCss;
+ /**
+ * Get a particular rendered style property value.
+ *
+ * @param name The name of the visual style property to get.
+ */
+ renderedStyle(name: string): Css.ElementCss;
+ /**
+ * Get a name-value pair object containing rendered visual style properties and their values for the element.
+ */
+ renderedCss(): Css.ElementCss;
+ /**
+ * Get a particular rendered style property value.
+ *
+ * @param name The name of the visual style property to get.
+ */
+ renderedCss(name: string): Css.ElementCss;
+
+ /**
+ * Get whether the element is visible.
+ */
+ visible(): boolean;
+ /**
+ * Get whether the element is hidden.
+ */
+ hidden(): boolean;
+
+ /**
+ * Get the effective opacity of the element (i.e. on-screen opacity), which takes into consideration parent node opacity.
+ */
+ effectiveOpacity(): number;
+
+ /**
+ * Get whether the element's effective opacity is completely transparent, which takes into consideration parent node opacity.
+ */
+ transparent(): boolean;
+ }
+ interface CollectionFirtsAnimation {
+ // http://js.cytoscape.org/#collection/animation
+
+ /**
+ * Get whether the element is currently being animated.
+ */
+ animated(): boolean;
+ }
+ interface CollectionFirstEdgeTraversing {
+ // http://js.cytoscape.org/#collection/traversing
+
+ /**
+ * Get source node of this edge.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ source(selector?: Selector): CollectionNodes;
+
+ /**
+ * Get target node of this edge.
+ *
+ * @param selector [optional] An optional selector that is used to filter the resultant collection.
+ */
+ target(selector?: Selector): CollectionNodes;
+ }
+ interface CollectionFirstNodeCompound {
+ // http://js.cytoscape.org/#collection/compound-nodes
+
+ /**
+ * Get whether the node is a compound parent (i.e. a node containing one or more child nodes)
+ */
+ isParent(): boolean;
+
+ /**
+ * Get whether the node is a compound child (i.e. contained within a node)
+ */
+ isChild(): boolean;
+ }
+
+ interface ElementsDefinition {
+ nodes: Sdc.Models.Graph.CommonNodeBase[];
+ edges: Sdc.Models.CommonLinkBase[];
+ }
+
+ interface ElementDefinition {
+
+
+ group?: ElementGroup;
+ data: Sdc.Models.Graph.CommonNodeBase| Sdc.Models.CommonLinkBase;
+ /**
+ * Scratchpad data (usually temp or nonserialisable data)
+ */
+ scatch?: Scratchpad;
+ /**
+ * The model position of the node (optional on init, mandatory after)
+ */
+ position?: Position;
+ /**
+ * can alternatively specify position in rendered on-screen pixels
+ */
+ renderedPosition?: Position;
+ /**
+ * Whether the element is selected (default false)
+ */
+ selected?: boolean;
+ /**
+ * Whether the selection state is mutable (default true)
+ */
+ selectable?: boolean;
+ /**
+ * When locked a node's position is immutable (default false)
+ */
+ locked?: boolean;
+ /**
+ * Wether the node can be grabbed and moved by the user
+ */
+ grabbable?: boolean;
+ /**
+ * a space separated list of class names that the element has
+ */
+ classes?: string;
+ style?: CSSStyleDeclaration;
+ /**
+ * you should only use `style`/`css` for very special cases; use classes instead
+ */
+ css?: Css.ElementCss;
+ }
+
+ // interface ElementDataDefinition {
+ // /**
+ // * elided id => autogenerated id
+ // */
+ // isSdcElement?: boolean;
+ // id?: string;
+ // position?: Position;
+ // label?: string;
+ // }
+ interface EdgeDefinition extends ElementDefinition {
+ data: Sdc.Models.CommonLinkBase;
+ }
+
+ // interface EdgeDataDefinition extends ElementDataDefinition { /**
+ // * the source node id (edge comes from this node)
+ // */
+ // source: string;
+ // /**
+ // * the target node id (edge goes to this node)
+ // */
+ // target: string;
+ // commonGraphLink?: Sdc.Models.CompositionCiLinkBase; //this is sdc-link-data-object
+ // }
+ interface NodeDefinition extends ElementDefinition {
+
+ data: Sdc.Models.Graph.CommonNodeBase;
+
+ }
+ // interface NodeDataDefinition extends ElementDataDefinition {
+ // parent?: string;
+ // commonGraphNode?: Sdc.Models.Graph.CommonNodeBase; //this is sdc-node-data-object
+ // type: string
+ //
+ // }
+
+ interface Stylesheet {
+ selector: string;
+ css: Css.ElementCss;
+ }
+
+ export module Css {
+ //TODO: http://js.cytoscape.org/#style
+
+ type Colour = string;
+
+ // TODO: How to constrain to a value?
+ type Shape = string; // 'rectangle', 'roundrectangle', 'ellipse', 'triangle', pentagon, hexagon, heptagon, octagon, star
+
+ // TODO: How to constrain to a value?
+ type Style = string; // solid, dotted, dashed, or double
+
+ export interface NodeCss {
+ width?: number;
+ height?: number;
+ shape?: Shape;
+ backgroundColor?: Colour;
+ "background-color"?: any;
+ "background-blacken"?: number;
+ "background-opacity"?: number;
+ "background-image"?: any;
+ "background-width"?: number;
+ "background-height"?: number;
+ "border-width"?: number;
+ "border-style"?: Style;
+ "border-color"?: Colour;
+ "border-opacity"?: number;
+ "label"?: string;
+ "events"?: string;
+ "text-events"?: string;
+ "text-valign"? :string;
+ "text-halign"? :string;
+ "text-margin-y"? :any;
+ "active-bg-size"?: number;
+ "background-fit"?: string;
+ "background-clip"?: string;
+ "background-image-opacity"?: number;
+ "overlay-color"?: string;
+ "font-size"? :number;
+ "font-family"? :string;
+ "background-position-x"? :number;
+ "background-position-y"? :number;
+ 'overlay-opacity'?: number;
+ 'text-border-width'?: number;
+ 'text-border-color'?: string;
+ 'text-border-opacity'?: number;
+ }
+
+ export interface SelectionBox {
+ "selection-box-color"?: string
+ "selection-box-border-color"?: string
+ "selection-box-border-width"?: number
+ "selection-box-opacity"?: number
+
+ }
+
+ export interface CompoundNodeCss extends NodeCss {
+ "padding-left"?: number;
+ "padding-right"?: number;
+ "padding-top"?: number;
+ "padding-bottom"?: number;
+ "compound-sizing-wrt-labels"?: string;
+ }
+
+ export interface EdgeCss {
+
+ "line-color"?: string;
+ "target-arrow-color"?: string;
+ "target-arrow-shape"?: string;
+ "curve-style"?: string;
+ "control-point-step-size"?: number;
+ "control-point-distances"?: string,
+ "control-point-weights"?: string,
+ "segment-distances"?: string,
+ "segment-weights"?: string
+ "line-style"? : string
+ }
+
+ export interface ElementCss extends NodeCss, CompoundNodeCss, EdgeCss, SelectionBox {
+ }
+ //export interface ElementCss extends CSSStyleDeclaration { }
+ }
+
+ interface Renderer {
+ /**
+ * The name of the renderer to use. By default, the 'canvas' renderer is used. If you build and register your own renderer, then you can specify its name here.
+ */
+ name: string;
+ }
+
+ interface Instance extends InstanceEvent, InstanceViewPort, InstanceAnimation, InstanceLayout, InstanceStyle, InstanceExport {
+ /**
+ * Add elements to the graph and return them.
+ */
+ add(eleObj: ElementDefinition): CollectionElements;
+ /**
+ * Add elements to the graph and return them.
+ */
+ add(eleObjs: ElementDefinition[]): CollectionElements;
+ /**
+ * Add elements to the graph and return them.
+ */
+ add(eles: Collection): CollectionElements;
+
+ /**
+ * Remove elements from the graph and return them.
+ */
+ remove(eles: Collection): CollectionElements;
+ /**
+ * Remove elements in the graph matching the specified selector.
+ */
+ remove(selector: Selector): CollectionElements;
+
+ /**
+ * Get an empty collection.
+ */
+ collection(): CollectionElements;
+ /**
+ * Get a collection from elements in the graph matching the specified selector.
+ */
+ collection(selector: Selector): CollectionElements;
+ /**
+ * Get a collection from an array of elements.
+ */
+ collection(elesArray: CollectionElements[]): CollectionElements;
+
+ /**
+ * Get an element from its ID in a very performant way.
+ */
+ getElementById(id: string): CollectionElements;
+
+ /**
+ * Get elements in the graph matching the specified selector.
+ */
+ $(selector: Selector): CollectionElements;
+ /**
+ * Get elements in the graph.
+ */
+ elements(): CollectionElements;
+ /**
+ * Get elements in the graph matching the specified selector.
+ */
+ elements(selector: Selector): CollectionElements;
+ /**
+ * Get nodes in the graph.
+ */
+ nodes(): CollectionNodes;
+ /**
+ * Get nodes in the graph matching the specified selector.
+ */
+ nodes(selector: Selector): CollectionNodes;
+ /**
+ * Get edges in the graph.
+ */
+ edges(): CollectionEdges;
+ /**
+ * Get edges in the graph matching the specified selector.
+ */
+ edges(selector: Selector): CollectionEdges;
+ /**
+ * Get elements in the graph matching the specified selector.
+ */
+ filter(selector: Selector): CollectionElements;
+ /**
+ * Get elements in the graph matching the specified filter function.
+ */
+ filter(filter: (i: number, ele: CollectionFirst) => boolean): CollectionElements;
+
+ /**
+ * Allow for manipulation of elements without triggering multiple style calculations or multiple redraws.
+ *
+ * A callback within which you can make batch updates to elements.
+ */
+ batch(callback: () => void): void;
+ /**
+ * Allow for manipulation of elements without triggering multiple style calculations or multiple redraws.
+ * Starts batching manually (useful for asynchronous cases).
+ */
+ startBatch(): void;
+ /**
+ * Allow for manipulation of elements without triggering multiple style calculations or multiple redraws.
+ * Ends batching manually (useful for asynchronous cases).
+ */
+ endBatch(): void;
+
+ /**
+ * A convenience function to explicitly destroy the instance.
+ */
+ destroy(): void;
+ }
+
+ interface EventObject {
+ /**
+ * Holds a reference to the originator of the event (core or element)
+ */
+ cyTarget: any; //CollectionElements or Cy.Instance;
+ data: any;
+ }
+
+ interface InstanceEvent {
+ // http://js.cytoscape.org/#core/events
+
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ */
+ on(events: string, handler: (evt: EventObject, data?: any) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ */
+ on(events: string, selector: Selector, handler: (evt: EventObject, data?: any) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ on(events: string, selector: Selector, data: any, handler: (evt: EventObject, data?: any) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param eventsMap A map of event names to handler functions.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ on(eventsMap: {
+ [value: string]: (evt: EventObject, data?: any) => void
+ }, selector?: Selector, data?: any);
+
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ */
+ bind(events: string, handler: (evt: EventObject, data?: any) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ */
+ bind(events: string, selector: Selector, handler: (evt: EventObject, data?: any) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ bind(events: string, selector: Selector, data: any, handler: (evt: EventObject, data?: any) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param eventsMap A map of event names to handler functions.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ bind(eventsMap: {
+ [value: string]: (evt: EventObject, data?: any) => void
+ }, selector?: Selector, data?: any);
+
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ */
+ listen(events: string, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ */
+ listen(events: string, selector: Selector, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ listen(events: string, selector: Selector, data: any, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param eventsMap A map of event names to handler functions.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ listen(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector, data?: any);
+
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ */
+ addListener(events: string, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ */
+ addListener(events: string, selector: Selector, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ addListener(events: string, selector: Selector, data: any, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph.
+ *
+ * @param eventsMap A map of event names to handler functions.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ addListener(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector, data?: any);
+
+ /**
+ * Bind to events that occur in the graph, and trigger the handler only once.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ */
+ one(events: string, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph, and trigger the handler only once.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ */
+ one(events: string, selector: Selector, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph, and trigger the handler only once.
+ *
+ * @param events A space separated list of event names.
+ * @param handler The handler function that is called when one of the specified events occurs.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ one(events: string, selector: Selector, data: any, handler: (evt: EventObject) => void): void;
+ /**
+ * Bind to events that occur in the graph, and trigger the handler only once.
+ *
+ * @param eventsMap A map of event names to handler functions.
+ * @param selector A selector to specify elements for which the handler is triggered.
+ * @param data A plain object which is passed to the handler in the event object argument.
+ */
+ one(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector, data?: any);
+
+ /**
+ * Remove event handlers.
+ * @param events A space separated list of event names.
+ * @param selector [optional] The same selector used to bind to the events.
+ * @param handler [optional] A reference to the handler function to remove.
+ */
+ off(events: string, selector?: Selector, handler?: (evt: EventObject) => void): void;
+
+ /**
+ * Remove event handlers.
+ * @param eventsMap A map of event names to handler functions to remove.
+ * @param selector [optional] The same selector used to bind to the events.
+ */
+ off(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector): void;
+
+ /**
+ * Remove event handlers.
+ * @param events A space separated list of event names.
+ * @param selector [optional] The same selector used to bind to the events.
+ * @param handler [optional] A reference to the handler function to remove.
+ */
+ unbind(events: string, selector?: Selector, handler?: (evt: EventObject) => void): void;
+
+ /**
+ * Remove event handlers.
+ * @param eventsMap A map of event names to handler functions to remove.
+ * @param selector [optional] The same selector used to bind to the events.
+ */
+ unbind(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector): void;
+
+ /**
+ * Remove event handlers.
+ * @param events A space separated list of event names.
+ * @param selector [optional] The same selector used to bind to the events.
+ * @param handler [optional] A reference to the handler function to remove.
+ */
+ unlisten(events: string, selector?: Selector, handler?: (evt: EventObject) => void): void;
+
+ /**
+ * Remove event handlers.
+ * @param eventsMap A map of event names to handler functions to remove.
+ * @param selector [optional] The same selector used to bind to the events.
+ */
+ unlisten(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector): void;
+
+ /**
+ * Remove event handlers.
+ * @param events A space separated list of event names.
+ * @param selector [optional] The same selector used to bind to the events.
+ * @param handler [optional] A reference to the handler function to remove.
+ */
+ removeListener(events: string, selector?: Selector, handler?: (evt: EventObject) => void): void;
+
+ /**
+ * Remove event handlers.
+ *
+ * @param eventsMap A map of event names to handler functions to remove.
+ * @param selector [optional] The same selector used to bind to the events.
+ */
+ removeListener(eventsMap: {
+ [value: string]: (evt: EventObject) => void
+ }, selector?: Selector): void;
+
+ /**
+ * Trigger one or more events.
+ *
+ * @param events A space separated list of event names to trigger.
+ * @param extraParams [optional] An array of additional parameters to pass to the handler.
+ */
+ trigger(events: string, extraParams?: any[]): void;
+
+ /**
+ * Trigger one or more events.
+ *
+ * @param events A space separated list of event names to trigger.
+ * @param extraParams [optional] An array of additional parameters to pass to the handler.
+ */
+ emit(events: string, extraParams?: any[]): void;
+
+ /**
+ * Get whether the initial render event has occurred (useful for plugins etc).
+ *
+ * This function returns whether the initrender event has occurred on the graph, meaning that the renderer has drawn the graph at least once. This is useful when you need to grab image data from the core, as this function will let you know whether that data is available yet: You can not grab the graph scene if it has not yet been rendered.
+ */
+ initrender(): boolean;
+
+ /**
+ * Run a handler function every time a frame is rendered.
+ *
+ * @param handler The handler function to call on each frame.
+ */
+ onRender(handler: () => void): void;
+
+ /**
+ * Remove handlers function bound via cy.onRender().
+ *
+ * @param handler [optional] A reference to the handler function to remove. All handlers are removed if this is unspecified.
+ */
+ offRender(handler?: () => void): void;
+
+ /**
+ * Run a callback as soon as the graph becomes ready. If the graph is already ready, then the callback is called immediately.
+ * @param fn The callback run as soon as the graph is ready, inside which this refers to the core (cy).
+ */
+ ready(fn: () => void): void;
+ }
+
+ interface InstanceViewPort {
+ // http://js.cytoscape.org/#core/viewport-manipulation
+
+ /**
+ * Pan the graph to the centre of a collection.
+ *
+ * @param eles The collection to centre upon.
+ */
+ center(eles?: Collection): CollectionElements;
+
+ /**
+ * Pan and zooms the graph to fit to a collection.
+ *
+ * @param eles [optional] The collection to fit to.
+ * @param padding [optional] An amount of padding (in pixels) to have around the graph
+ */
+ fit(eles?: Collection, padding?: number): CollectionElements;
+
+ /**
+ * Reset the graph to the default zoom level and panning position.
+ */
+ reset(): CollectionElements;
+
+ /**
+ * Get the panning position of the graph.
+ */
+ pan(): Position;
+
+ /**
+ * Set the panning position of the graph.
+ *
+ * @param renderedPosition The rendered position to pan the graph to.
+ */
+ pan(renderedPosition?: Position): void;
+
+ /**
+ * Relatively pan the graph by a specified rendered position vector.
+ *
+ * @param renderedPosition The rendered position vector to pan the graph by.
+ */
+ panBy(renderedPosition: Position): void;
+
+ /**
+ * Get whether panning is enabled. If cy.boxSelectionEnabled() === true, then the user must taphold to initiate panning.
+ */
+ panningEnabled(): boolean;
+
+ /**
+ * Set whether panning is enabled. If cy.boxSelectionEnabled() === true, then the user must taphold to initiate panning.
+ *
+ * @param bool A truthy value enables panning; a falsey value disables it.
+ */
+ panningEnabled(bool: boolean): void;
+
+ /**
+ * Get whether panning by user events (e.g. dragging the graph background) is enabled. If cy.boxSelectionEnabled() === true, then the user must taphold to initiate panning.
+ */
+ userPanningEnabled(): boolean;
+
+ /**
+ * Set whether panning by user events (e.g. dragging the graph background) is enabled. If cy.boxSelectionEnabled() === true, then the user must taphold to initiate panning.
+ *
+ * @param bool A truthy value enables user panning; a falsey value disables it.
+ */
+ userPanningEnabled(bool: boolean): void;
+
+ /**
+ * Get the zoom level.
+ */
+ zoom(): number;
+ /**
+ * Set the zoom level.
+ *
+ * @param level The zoom level to set.
+ */
+ zoom(level: number): void;
+ /**
+ * Set the zoom level.
+ *
+ * @param options The options for zooming.
+ */
+ zoom(options: ZoomOptions): void;
+
+ /**
+ * Get whether zooming is enabled.
+ */
+ zoomingEnabled(): boolean;
+ /**
+ * Set whether zooming is enabled.
+ *
+ * @param bool A truthy value enables zooming; a falsey value disables it.
+ */
+ zoomingEnabled(bool: boolean): void;
+
+ /**
+ * Get whether zooming by user events (e.g. mouse wheel, pinch-to-zoom) is enabled.
+ */
+ userZoomingEnabled(): boolean;
+ /**
+ * Set whether zooming by user events (e.g. mouse wheel, pinch-to-zoom) is enabled.
+ *
+ * @param bool A truthy value enables user zooming; a falsey value disables it.
+ */
+ userZoomingEnabled(bool: boolean): void;
+
+ /**
+ * Get the minimum zoom level.
+ */
+ minZoom(): number;
+ /**
+ * Set the minimum zoom level.
+ *
+ * @param zoom The new minimum zoom level to use.
+ */
+ minZoom(zoom: number): void;
+
+ /**
+ * Get the maximum zoom level.
+ */
+ maxZoom(): number;
+ /**
+ * Set the maximum zoom level.
+ *
+ * @param zoom The new maximum zoom level to use.
+ */
+ maxZoom(zoom: number): void;
+
+ /**
+ * Set the viewport state (pan & zoom) in one call.
+ *
+ * @param zoom The zoom level to set.
+ * @param pan The pan to set (a rendered position).
+ */
+ viewport(zoom: number, pan: Position): void;
+
+ /**
+ * Get whether box selection is enabled. If enabled, the user must hold left-click to initiate panning.
+ */
+ boxSelectionEnabled(): boolean;
+ /**
+ * Set whether box selection is enabled. If enabled, the user must hold left-click to initiate panning.
+ *
+ * @param bool A truthy value enables box selection; a falsey value disables it.
+ */
+ boxSelectionEnabled(bool: boolean): void;
+
+ /**
+ * Get the on-screen width of the viewport in pixels.
+ */
+ width(): number;
+
+ /**
+ * Get the on-screen height of the viewport in pixels.
+ */
+ height(): number;
+
+ /**
+ * Get the extent of the viewport, a bounding box in model coordinates that lets you know what model positions are visible in the viewport.
+ */
+ extent(): Extent;
+
+ /**
+ * Get whether nodes are automatically locked (i.e. if true, nodes are locked despite their individual state).
+ */
+ autolock(): boolean;
+ /**
+ * Set whether nodes are automatically locked (i.e. if true, nodes are locked despite their individual state).
+ *
+ * @param bool A truthy value enables autolocking; a falsey value disables it.
+ */
+ autolock(bool: boolean): void;
+
+ /**
+ * Get whether nodes are automatically ungrabified (i.e. if true, nodes are ungrabbale despite their individual state).
+ */
+ autoungrabify(): boolean;
+ /**
+ * Set whether nodes are automatically ungrabified (i.e. if true, nodes are ungrabbale despite their individual state).
+ *
+ * @param bool A truthy value enables autolocking; a falsey value disables it.
+ */
+ autoungrabify(bool: boolean): void;
+
+ /**
+ * Get whether nodes are automatically unselectified (i.e. if true, nodes are unselectable despite their individual state).
+ */
+ autounselectify(): boolean;
+ /**
+ * Set whether nodes are automatically unselectified (i.e. if true, nodes are unselectable despite their individual state).
+ *
+ * @param bool A truthy value enables autolocking; a falsey value disables it.
+ */
+ autounselectify(bool: boolean): void;
+
+ /**
+ * Force the renderer to redraw (i.e. draw a new frame).
+ *
+ * This function forces the renderer to draw a new frame. It is useful for very specific edgecases, such as in certain UI plugins, but it should not be needed for most developers.
+ */
+ forceRender(): void;
+
+ /**
+ * Force the renderer to recalculate the viewport bounds.
+ *
+ * If your code resizes the graph's dimensions or position (i.e. by changing the style of the HTML DOM element that holds the graph), you will want to call cy.resize() to have the graph resize and redraw itself.
+ *
+ * Cytoscape.js can not automatically monitor the bounding box of the viewport, as querying the DOM for those dimensions can be expensive. Although cy.resize() is automatically called for you on the window's resize event, there is no resize or style event for arbitrary DOM elements.
+ */
+ resize(): CollectionElements;
+ }
+
+ interface InstanceAnimation {
+ // http://js.cytoscape.org/#core/animation
+
+ /**
+ * Get whether the viewport is currently being animated.
+ */
+ animated(): boolean;
+
+ /**
+ * Animate the viewport.
+ *
+ * @param anis An object containing the details of the animation.
+ * zoom A zoom level to which the graph will be animated.
+ * pan A panning position to which the graph will be animated.
+ * panBy A relative panning position to which the graph will be animated.
+ * fit An object containing fitting options from which the graph will be animated.
+ * eles Elements or a selector to which the viewport will be fitted.
+ * padding Padding to use with the fitting.
+ * center An object containing centring options from which the graph will be animated.
+ * eles Elements or a selector to which the viewport will be centred.
+ * @param options An object containing animation options.
+ * duration - The duration of the animation in milliseconds.
+ * queue - A boolean indicating whether to queue the animation.
+ * complete - A function to call when the animation is done.
+ * step - A function to call each time the animation steps.
+ */
+ animate(anis: {
+ zoom?: number,
+ pan?: Position,
+ panBy?: Position,
+ fit?: {
+ eles: Collection,
+ padding?: number
+ },
+ center?: {
+ eles: Collection
+ }
+ }, options?: {
+ duration?: number,
+ queue?: boolean,
+ complete?: () => void,
+ step?: () => void
+ }): Cy.Instance;
+
+ /**
+ * Add a delay between animations for the viewport.
+ *
+ * @param duration How long the delay should be in milliseconds.
+ * @param complete A function to call when the delay is complete.
+ */
+ delay(duration: number, complete?: () => void): Cy.Instance;
+
+ /**
+ * Stop all viewport animations that are currently running.
+ *
+ * @param clearQueue A boolean, indicating whether the queue of animations should be emptied.
+ * @param jumpToEnd A boolean, indicating whether the currently-running animations should jump to their ends rather than just stopping midway.
+ */
+ stop(clearQueue?: boolean, jumpToEnd?: boolean): Cy.Instance;
+
+ /**
+ * Remove all queued animations for the viewport.
+ */
+ clearQueue(): Cy.Instance;
+ }
+
+ interface InstanceLayout {
+ //TODO: http://js.cytoscape.org/#core/layout
+ layout(layout: LayoutOptions): void;
+
+ }
+
+ interface InstanceStyle {
+ //TODO: http://js.cytoscape.org/#core/style
+ }
+
+ interface InstanceExport {
+ /**
+ * Export the current graph view as a PNG image in Base64 representation.
+ */
+ png(): string;
+ /**
+ * Export the current graph view as a PNG image in Base64 representation.
+ */
+ png(options: ExportOptions): string;
+
+ /**
+ * Export the current graph view as a JPG image in Base64 representation.
+ */
+ jpg(): string;
+ /**
+ * Export the current graph view as a JPG image in Base64 representation.
+ */
+ jpg(options: ExportOptions): string;
+
+ /**
+ * Export the current graph view as a JPG image in Base64 representation.
+ */
+ jpeg(): string;
+ /**
+ * Export the current graph view as a JPG image in Base64 representation.
+ */
+ jpeg(options: ExportOptions): string;
+
+ /**
+ * Export the graph as JSON, the same format used at initialisation.
+ */
+ json(): string;
+ }
+
+ interface Position {
+ x: number;
+ y: number;
+ }
+
+ interface LayoutInstance {
+ //TODO: http://js.cytoscape.org/#layouts/layout-manipulation
+
+ /**
+ * Start running the layout.
+ */
+ run(): void;
+ /**
+ * Start running the layout.
+ */
+ start(): void;
+
+ /**
+ * Stop running the (asynchronous/discrete) layout.
+ */
+ stop(): void;
+ }
+
+ interface LayoutOptions {
+ // TODO: http://js.cytoscape.org/#layouts
+ /**
+ *
+ * The default is 'grid'.
+ */
+ name?: string;
+ padding?: number;
+ avoidOverlap?: boolean;
+ }
+
+ interface CytoscapeOptions {
+ // very commonly used options:
+ /**
+ * A HTML DOM element in which the graph should be rendered.
+ * This is optional if Cytoscape.js is run headlessly or if you initialise using jQuery (in which case your jQuery object already has an associated DOM element).
+ *
+ * The default is undefined.
+ */
+ container?: HTMLElement | JQuery;
+ /**
+ * The [[Stylesheet]] used to style the graph. For convenience, this option can alternatively be specified as a promise that resolves to the stylesheet.
+ */
+ style?: Stylesheet[]|Promise<Stylesheet[]>;
+ /**
+ * An array of [[Elements]] specified as plain objects. For convenience, this option can alternatively be specified as a promise that resolves to the elements JSON.
+ */
+ elements?: ElementsDefinition|ElementDefinition[]|Promise<ElementsDefinition>|Promise<ElementDefinition[]>;
+ /**
+ * A plain object that specifies layout options. Which layout is initially run is specified by the name field. Refer to a layout's documentation for the options it supports. If you want to specify your node positions yourself in your elements JSON, you can use the preset layout — by default it does not set any positions, leaving your nodes in their current positions (e.g. specified in options.elements at initialisation time)
+ */
+ layout?: LayoutOptions;
+ /**
+ * A callback function that is called when Cytoscape.js has loaded the graph and the layout has specified initial positions of the nodes. After this point, rendering can happen, the user can interact with the graph, et cetera.
+ */
+ ready?: (evt: any) => void;
+
+ // initial viewport state:
+ /**
+ * The initial zoom level of the graph. Make sure to disable viewport manipulation options, such as fit, in your layout so that it is not overridden when the layout is applied. You can set options.minZoom and options.maxZoom to set restrictions on the zoom level.
+ *
+ * The default value is 1.
+ */
+ zoom?: number;
+ /**
+ * The initial panning position of the graph. Make sure to disable viewport manipulation options, such as fit, in your layout so that it is not overridden when the layout is applied.
+ */
+ pan?: Position;
+
+ // interaction options?:
+ /**
+ * A minimum bound on the zoom level of the graph. The viewport can not be scaled smaller than this zoom level.
+ *
+ * The default value is 1e-50.
+ */
+ minZoom?: number;
+ /**
+ * A maximum bound on the zoom level of the graph. The viewport can not be scaled larger than this zoom level.
+ *
+ * The default value is 1e50.
+ */
+ maxZoom?: number;
+ /**
+ * Whether zooming the graph is enabled, both by user events and programmatically.
+ *
+ * The default value is true.
+ */
+ zoomingEnabled?: boolean;
+ /**
+ * Whether user events (e.g. mouse wheel, pinch-to-zoom) are allowed to zoom the graph. Programmatic changes to zoom are unaffected by this option.
+ *
+ * The default value is true.
+ */
+ userZoomingEnabled?: boolean;
+ /**
+ * Whether panning the graph is enabled, both by user events and programmatically.
+ *
+ * The default value is true.
+ */
+ panningEnabled?: boolean;
+ /**
+ * Whether user events (e.g. dragging the graph background) are allowed to pan the graph. Programmatic changes to pan are unaffected by this option.
+ *
+ * The default value is true.
+ */
+ userPanningEnabled?: boolean;
+ /**
+ * Whether box selection (i.e. drag a box overlay around, and release it to select) is enabled. If enabled, the user must taphold to pan the graph.
+ *
+ * The default value is false.
+ */
+ boxSelectionEnabled?: boolean;
+ /**
+ * A string indicating the selection behaviour from user input. By default, this is set automatically for you based on the type of input device detected. On touch devices, 'additive' is default — a new selection made by the user adds to the set of currenly selected elements. On mouse-input devices, 'single' is default — a new selection made by the user becomes the entire set of currently selected elements (i.e. the previous elements are unselected).
+ *
+ * The default value is (isTouchDevice ? 'additive' : 'single').
+ */
+ selectionType?: SelectionType;
+ /**
+ * A nonnegative integer that indicates the maximum allowable distance that a user may move during a tap gesture, on touch devices and desktop devices respectively. This makes tapping easier for users. These values have sane defaults, so it is not advised to change these options unless you have very good reason for doing so. Larger values will almost certainly have undesirable consequences.
+ *
+ * The default value is is 8.
+ */
+ touchTapThreshold?: number;
+ /**
+ * A nonnegative integer that indicates the maximum allowable distance that a user may move during a tap gesture, on touch devices and desktop devices respectively. This makes tapping easier for users. These values have sane defaults, so it is not advised to change these options unless you have very good reason for doing so. Larger values will almost certainly have undesirable consequences.
+ *
+ * The default value is 4.
+ */
+ desktopTapThreshold?: number;
+ /**
+ * Whether nodes should be locked (not draggable at all) by default (if true, overrides individual node state).
+ *
+ * The default value is false.
+ */
+ autolock?: boolean;
+ /**
+ * Whether nodes should be ungrabified (not grabbable by user) by default (if true, overrides individual node state).
+ *
+ * The default value is false.
+ */
+ autoungrabify?: boolean;
+ /**
+ * Whether nodes should be unselectified (immutable selection state) by default (if true, overrides individual element state).
+ *
+ * The default value is false.
+ */
+ autounselectify?: boolean;
+
+ // rendering options:
+ /**
+ * A convenience option that initialises the instance to run headlessly. You do not need to set this in environments that are implicitly headless (e.g. Node.js). However, it is handy to set headless: true if you want a headless instance in a browser.
+ *
+ * The default value is false.
+ */
+ headless?: boolean;
+ /**
+ * A boolean that indicates whether styling should be used. For headless (i.e. outside the browser) environments, display is not necessary and so neither is styling necessary — thereby speeding up your code. You can manually enable styling in headless environments if you require it for a special case. Note that it does not make sense to disable style if you plan on rendering the graph.
+ *
+ * The default value is true.
+ */
+ styleEnabled?: boolean;
+ /**
+ * When set to true, the renderer does not render edges while the viewport is being manipulated. This makes panning, zooming, dragging, et cetera more responsive for large graphs.
+ *
+ * The default value is false.
+ */
+ hideEdgesOnViewport?: boolean;
+ /**
+ * hen set to true, the renderer does not render labels while the viewport is being manipulated. This makes panning, zooming, dragging, et cetera more responsive for large graphs.
+ *
+ * The default value is false.
+ */
+ hideLabelsOnViewport?: boolean;
+ /**
+ * When set to true, the renderer uses a texture (if supported) during panning and zooming instead of drawing the elements, making large graphs more responsive.
+ *
+ * The default value is false.
+ */
+ textureOnViewport?: boolean;
+ /**
+ * When set to true, the renderer will use a motion blur effect to make the transition between frames seem smoother. This can significantly increase the perceived performance for a large graphs.
+ *
+ * The default value is false.
+ */
+ motionBlur?: boolean;
+ /**
+ * When motionBlur: true, this value controls the opacity of motion blur frames. Higher values make the motion blur effect more pronounced.
+ *
+ * The default value is 0.2.
+ */
+ motionBlurOpacity?: number;
+ /**
+ * Changes the scroll wheel sensitivity when zooming. This is a multiplicative modifier. So, a value between 0 and 1 reduces the sensitivity (zooms slower), and a value greater than 1 increases the sensitivity (zooms faster).
+ *
+ * The default value is 1.
+ */
+ wheelSensitivity?: number;
+ /**
+ * Overrides the screen pixel ratio with a manually set value (1.0 or 0.666 recommended, if set). This can be used to increase performance on high density displays by reducing the effective area that needs to be rendered. If you want to use the hardware's actual pixel ratio at the expense of performance, you can set pixelRatio: 'auto'.
+ *
+ * The default value is 1.
+ */
+ pixelRatio?: number;
+ /**
+ * A callback function that is called when Cytoscape.js has rendered its first frame. This is useful for grabbing screenshots etc after initialision, but in general you should use ready instead.
+ */
+ initrender?: (evt: any) => void;
+ /**
+ * A plain object containing options for the renderer to be used. The options.renderer.name field specifies which renderer is used. You need not specify anything for the renderer option, unless you want to specify one of the rendering options.
+ */
+ renderer?: Renderer;
+ }
+
+ interface ExportOptions {
+ /**
+ * The background colour of the image (transparent by default).
+ */
+ bg?: string;
+ /**
+ * Whether to export the current viewport view (false, default) or the entire graph (true).
+ */
+ full?: boolean;
+ /**
+ * This value specifies a positive number that scales the size of the resultant image.
+ */
+ scale?: number;
+ /**
+ * Specifies the scale automatically in combination with maxHeight such that the resultant image is no wider than maxWidth.
+ */
+ maxWidth?: number;
+ /**
+ * Specifies the scale automatically in combination with maxWidth such that the resultant image is no taller than maxHeight.
+ */
+ maxHeight?: number;
+ }
+
+ interface BoundingBoxOptions {
+ /**
+ * A boolean indicating whether to include nodes in the bounding box.
+ */
+ includeNodes: boolean;
+ /**
+ * A boolean indicating whether to include edges in the bounding box.
+ */
+ includeEdges: boolean;
+ /**
+ * A boolean indicating whether to include labels in the bounding box.
+ */
+ includeLabels: boolean;
+ /**
+ * A boolean indicating whether to include shadows in the bounding box (default true).
+ */
+
+ includeShadows: boolean
+ }
+
+ interface ZoomOptions {
+ /**
+ * The zoom level to set.
+ */
+ level: number;
+ /**
+ * The position about which to zoom.
+ */
+ position?: Position;
+ /**
+ * The rendered position about which to zoom.
+ */
+ renderedPosition?: Position;
+ }
+
+ interface Static {
+ (options?: CytoscapeOptions): Instance;
+ (extensionName: string, foo: string, bar: any): Instance;
+ version: string;
+
+ Promise<T>(): Cy.Promise<T>;
+ }
+
+ enum PromiseState {
+ STATE_PENDING = 0,
+ STATE_FULFILLED = 1,
+ STATE_REJECTED = 2
+
+ }
+
+ interface Promise<T> {
+ id: string;
+ state: PromiseState;
+ fulfillValue: T;
+ rejectReason: any;
+ onFulfilled: any[];
+ onRejected: any[];
+
+ fulfill(value: T): Promise<T>;
+ reject(error: any): Promise<any>;
+ then<U>(onFulfilled?: (value: T) => U | Promise<U>, onRejected?: (error: any) => U | Promise<U>): Promise<U>;
+ then<U>(onFulfilled?: (value: T) => U | Promise<U>, onRejected?: (error: any) => void): Promise<U>;
+ }
+}
+
+declare module "cytoscape" {
+ export = cytoscape;
+}
+
+declare var cytoscape: Cy.Static; \ No newline at end of file
diff --git a/catalog-ui/typings/cytoscape/edge-editation.d.ts b/catalog-ui/typings/cytoscape/edge-editation.d.ts
new file mode 100644
index 0000000000..e5ebd709b2
--- /dev/null
+++ b/catalog-ui/typings/cytoscape/edge-editation.d.ts
@@ -0,0 +1,28 @@
+/**
+ * Created by obarda on 11/6/2016.
+ */
+
+interface CytoscapeEdgeEditation {
+ new(): CytoscapeEdgeEditation;
+
+ init(cy: Cy.Instance, handleSize?: number):void;
+ registerHandle(handle: Handle): void;
+}
+
+interface Handle {
+ positionX: string,
+ positionY: string,
+ offsetX?: number,
+ offsetY?: number,
+ color: string,
+ type: string,
+ single: boolean,
+ nodeTypeNames: Array<string>;
+ imageUrl: string;
+ lineWidth: number;
+ lineStyle: string;
+}
+
+declare var CytoscapeEdgeEditation: CytoscapeEdgeEditation;
+
+declare function require(name:string); \ No newline at end of file
diff --git a/catalog-ui/typings/d3/d3.d.ts b/catalog-ui/typings/d3/d3.d.ts
new file mode 100644
index 0000000000..ff4335106c
--- /dev/null
+++ b/catalog-ui/typings/d3/d3.d.ts
@@ -0,0 +1,3308 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for d3JS
+// Project: http://d3js.org/
+// Definitions by: Alex Ford <https://github.com/gustavderdrache>, Boris Yankov <https://github.com/borisyankov>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+declare module d3 {
+ /**
+ * The current version of D3.js.
+ */
+ export var version: string;
+
+ /**
+ * Find the first element that matches the given selector string.
+ */
+ export function select(selector: string): Selection<any>;
+
+ /**
+ * Create a selection from the given node reference.
+ */
+ export function select(node: EventTarget): Selection<any>;
+
+ /**
+ * Find all elements that match the given selector string.
+ */
+ export function selectAll(selector: string): Selection<any>;
+
+ /**
+ * Create a selection from the given list of nodes.
+ */
+ export function selectAll(nodes: EventTarget[]): Selection<any>;
+
+ /**
+ * Returns the root selection (as if by d3.select(document.documentElement)). This function may be used for 'instanceof' tests, and extending its prototype will add properties to all selections.
+ */
+ export function selection(): Selection<any>;
+
+ module selection {
+ export var prototype: Selection<any>;
+
+ /**
+ * Selections are grouped into arrays of nodes, with the parent tracked in the 'parentNode' property.
+ */
+ interface Group extends Array<EventTarget> {
+ parentNode: EventTarget;
+ }
+
+ interface Update<Datum> {
+ /**
+ * Retrieve a grouped selection.
+ */
+ [index: number]: Group;
+
+ /**
+ * The number of groups in this selection.
+ */
+ length: number;
+
+ /**
+ * Retrieve the value of the given attribute for the first node in the selection.
+ *
+ * @param name The attribute name to query. May be prefixed (see d3.ns.prefix).
+ */
+ attr(name: string): string;
+
+ /**
+ * For all nodes, set the attribute to the specified constant value. Use null to remove.
+ *
+ * @param name The attribute name, optionally prefixed.
+ * @param value The attribute value to use. Note that this is coerced to a string automatically.
+ */
+ attr(name: string, value: Primitive): Update<Datum>;
+
+ /**
+ * Derive an attribute value for each node in the selection based on bound data.
+ *
+ * @param name The attribute name, optionally prefixed.
+ * @param value The function of the datum (the bound data item) and index (the position in the subgrouping) which computes the attribute value. If the function returns null, the attribute is removed.
+ */
+ attr(name: string, value: (datum: Datum, index: number) => Primitive): Update<Datum>;
+
+ /**
+ * Set multiple properties at once using an Object. D3 iterates over all enumerable properties and either sets or computes the attribute's value based on the corresponding entry in the Object.
+ *
+ * @param obj A key-value mapping corresponding to attributes and values. If the value is a simple string or number, it is taken as a constant. Otherwise, it is a function that derives the attribute value.
+ */
+ attr(obj: { [key: string]: Primitive | ((datum: Datum, index: number) => Primitive) }): Update<Datum>;
+
+ /**
+ * Returns true if the first node in this selection has the given class list. If multiple classes are specified (i.e., "foo bar"), then returns true only if all classes match.
+ *
+ * @param name The class list to query.
+ */
+ classed(name: string): boolean;
+
+ /**
+ * Adds (or removes) the given class list.
+ *
+ * @param name The class list to toggle. Spaces separate class names: "foo bar" is a list of two classes.
+ * @param value If true, add the classes. If false, remove them.
+ */
+ classed(name: string, value: boolean): Update<Datum>;
+
+ /**
+ * Determine if the given class list should be toggled for each node in the selection.
+ *
+ * @param name The class list. Spaces separate multiple class names.
+ * @param value The function to run for each node. Should return true to add the class to the node, or false to remove it.
+ */
+ classed(name: string, value: (datum: Datum, index: number) => boolean): Update<Datum>;
+
+ /**
+ * Set or derive classes for multiple class lists at once.
+ *
+ * @param obj An Object mapping class lists to values that are either plain booleans or functions that return booleans.
+ */
+ classed(obj: { [key: string]: boolean | ((datum: Datum, index: number) => boolean) }): Update<Datum>;
+
+ /**
+ * Retrieve the computed style value for the first node in the selection.
+ * @param name The CSS property name to query
+ */
+ style(name: string): string;
+
+ /**
+ * Set a style property for all nodes in the selection.
+ * @param name the CSS property name
+ * @param value the property value
+ * @param priority if specified, either null or the string "important" (no exclamation mark)
+ */
+ style(name: string, value: Primitive, priority?: string): Update<Datum>;
+
+ /**
+ * Derive a property value for each node in the selection.
+ * @param name the CSS property name
+ * @param value the function to derive the value
+ * @param priority if specified, either null or the string "important" (no exclamation mark)
+ */
+ style(name: string, value: (datum: Datum, index: number) => Primitive, priority?: string): Update<Datum>;
+
+ /**
+ * Set a large number of CSS properties from an object.
+ *
+ * @param obj an Object whose keys correspond to CSS property names and values are either constants or functions that derive property values
+ * @param priority if specified, either null or the string "important" (no exclamation mark)
+ */
+ style(obj: { [key: string]: Primitive | ((datum: Datum, index: number) => Primitive) }, priority?: string): Update<Datum>;
+
+ /**
+ * Retrieve an arbitrary node property such as the 'checked' property of checkboxes, or the 'value' of text boxes.
+ *
+ * @param name the node's property to retrieve
+ */
+ property(name: string): any;
+
+ /**
+ * For each node, set the property value. Internally, this sets the node property directly (e.g., node[name] = value), so take care not to mutate special properties like __proto__.
+ *
+ * @param name the property name
+ * @param value the property value
+ */
+ property(name: string, value: any): Update<Datum>;
+
+ /**
+ * For each node, derive the property value. Internally, this sets the node property directly (e.g., node[name] = value), so take care not to mutate special properties like __proto__.
+ *
+ * @param name the property name
+ * @param value the function used to derive the property's value
+ */
+ property(name: string, value: (datum: Datum, index: number) => any): Update<Datum>;
+
+ /**
+ * Set multiple node properties. Caveats apply: take care not to mutate special properties like __proto__.
+ *
+ * @param obj an Object whose keys correspond to node properties and values are either constants or functions that will compute a value.
+ */
+ property(obj: { [key: string]: any | ((datum: Datum, index: number) => any) }): Update<Datum>;
+
+ /**
+ * Retrieve the textContent of the first node in the selection.
+ */
+ text(): string;
+
+ /**
+ * Set the textContent of each node in the selection.
+ * @param value the text to use for all nodes
+ */
+ text(value: Primitive): Update<Datum>;
+
+ /**
+ * Compute the textContent of each node in the selection.
+ * @param value the function which will compute the text
+ */
+ text(value: (datum: Datum, index: number) => Primitive): Update<Datum>;
+
+ /**
+ * Retrieve the HTML content of the first node in the selection. Uses 'innerHTML' internally and will not work with SVG or other elements without a polyfill.
+ */
+ html(): string;
+
+ /**
+ * Set the HTML content of every node in the selection. Uses 'innerHTML' internally and thus will not work with SVG or other elements without a polyfill.
+ * @param value the HTML content to use.
+ */
+ html(value: string): Selection<Datum>;
+
+ /**
+ * Compute the HTML content for each node in the selection. Uses 'innerHTML' internally and thus will not work with SVG or other elements without a polyfill.
+ * @param value the function to compute HTML content
+ */
+ html(value: (datum: Datum, index: number) => string): Selection<Datum>;
+
+ /**
+ * Appends a new child to each node in the selection. This child will inherit the parent's data (if available). Returns a fresh selection consisting of the newly-appended children.
+ *
+ * @param name the element name to append. May be prefixed (see d3.ns.prefix).
+ */
+ append(name: string): Selection<Datum>;
+
+ /**
+ * Appends a new child to each node in the selection by computing a new node. This child will inherit the parent's data (if available). Returns a fresh selection consisting of the newly-appended children.
+ *
+ * @param name the function to compute a new element
+ */
+ append(name: (datum: Datum, index: number) => EventTarget): Update<Datum>;
+
+ /**
+ * Inserts a new child to each node in the selection. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the element name to append. May be prefixed (see d3.ns.prefix).
+ * @param before the selector to determine position (e.g., ":first-child")
+ */
+ insert(name: string, before: string): Update<Datum>;
+
+ /**
+ * Inserts a new child to each node in the selection. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the element name to append. May be prefixed (see d3.ns.prefix).
+ * @param before a function to determine the node to use as the next sibling
+ */
+ insert(name: string, before: (datum: Datum, index: number) => EventTarget): Update<Datum>;
+
+ /**
+ * Inserts a new child to the end of each node in the selection by computing a new node. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the function to compute a new child
+ * @param before the selector to determine position (e.g., ":first-child")
+ */
+ insert(name: (datum: Datum, index: number) => EventTarget, before: string): Update<Datum>;
+
+ /**
+ * Inserts a new child to the end of each node in the selection by computing a new node. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the function to compute a new child
+ * @param before a function to determine the node to use as the next sibling
+ */
+ insert(name: (datum: Datum, index: number) => EventTarget, before: (datum: Datum, index: number) => EventTarget): Update<Datum>;
+
+ /**
+ * Removes the elements from the DOM. They are in a detached state and may be re-added (though there is currently no dedicated API for doing so).
+ */
+ remove(): Update<Datum>;
+
+ /**
+ * Retrieves the data bound to the first group in this selection.
+ */
+ data(): Datum[];
+
+ /**
+ * Binds data to this selection.
+ * @param data the array of data to bind to this selection
+ * @param key the optional function to determine the unique key for each piece of data. When unspecified, uses the index of the element.
+ */
+ data<NewDatum>(data: NewDatum[], key?: (datum: NewDatum, index: number) => string): Update<NewDatum>;
+
+ /**
+ * Derives data to bind to this selection.
+ * @param data the function to derive data. Must return an array.
+ * @param key the optional function to determine the unique key for each data item. When unspecified, uses the index of the element.
+ */
+ data<NewDatum>(data: (datum: Datum, index: number) => NewDatum[], key?: (datum: NewDatum, index: number) => string): Update<NewDatum>;
+
+ /**
+ * Filters the selection, returning only those nodes that match the given CSS selector.
+ * @param selector the CSS selector
+ */
+ filter(selector: string): Update<Datum>;
+
+ /**
+ * Filters the selection, returning only those nodes for which the given function returned true.
+ * @param selector the filter function
+ */
+ filter(selector: (datum: Datum, index: number) => boolean): Update<Datum>;
+
+ /**
+ * Return the data item bound to the first element in the selection.
+ */
+ datum(): Datum;
+
+ /**
+ * Set the data item for each node in the selection.
+ * @param value the constant element to use for each node
+ */
+ datum<NewDatum>(value: NewDatum): Update<NewDatum>;
+
+ /**
+ * Derive the data item for each node in the selection. Useful for situations such as the HTML5 'dataset' attribute.
+ * @param value the function to compute data for each node
+ */
+ datum<NewDatum>(value: (datum: Datum, index: number) => NewDatum): Update<NewDatum>;
+
+ /**
+ * Reorders nodes in the selection based on the given comparator. Nodes are re-inserted into the document once sorted.
+ * @param comparator the comparison function, which defaults to d3.ascending
+ */
+ sort(comparator?: (a: Datum, b: Datum) => number): Update<Datum>;
+
+ /**
+ * Reorders nodes in the document to match the selection order. More efficient than calling sort() if the selection is already ordered.
+ */
+ order(): Update<Datum>;
+
+ /**
+ * Returns the listener (if any) for the given event.
+ * @param type the type of event to load the listener for. May have a namespace (e.g., ".foo") at the end.
+ */
+ on(type: string): (datum: Datum, index: number) => any;
+
+ /**
+ * Adds a listener for the specified event. If one was already registered, it is removed before the new listener is added. The return value of the listener function is ignored.
+ * @param type the of event to listen to. May have a namespace (e.g., ".foo") at the end.
+ * @param listener an event listener function, or null to unregister
+ * @param capture sets the DOM useCapture flag
+ */
+ on(type: string, listener: (datum: Datum, index: number) => any, capture?: boolean): Update<Datum>;
+
+ /**
+ * Begins a new transition. Interrupts any active transitions of the same name.
+ * @param name the transition name (defaults to "")
+ */
+ transition(name?: string): Transition<Datum>;
+
+ /**
+ * Interrupts the active transition of the provided name. Does not cancel scheduled transitions.
+ * @param name the transition name (defaults to "")
+ */
+ interrupt(name?: string): Update<Datum>;
+
+ /**
+ * Creates a subselection by finding the first descendent matching the selector string. Bound data is inherited.
+ * @param selector the CSS selector to match against
+ */
+ select(selector: string): Update<Datum>;
+
+ /**
+ * Creates a subselection by using a function to find descendent elements. Bound data is inherited.
+ * @param selector the function to find matching descendants
+ */
+ select(selector: (datum: Datum, index: number) => EventTarget): Update<Datum>;
+
+ /**
+ * Creates a subselection by finding all descendents that match the given selector. Bound data is not inherited.
+ * @param selector the CSS selector to match against
+ */
+ selectAll(selector: string): Update<Datum>;
+
+ /**
+ * Creates a subselection by using a function to find descendent elements. Bound data is not inherited.
+ * @param selector the function to find matching descendents
+ */
+ selectAll(selector: (datum: Datum, index: number) => Array<EventTarget> | NodeList): Update<any>;
+
+ /**
+ * Invoke the given function for each element in the selection. The return value of the function is ignored.
+ * @param func the function to invoke
+ */
+ each(func: (datum: Datum, index: number) => any): Update<Datum>;
+
+ /**
+ * Call a function on the selection. sel.call(foo) is equivalent to foo(sel).
+ * @param func the function to call on the selection
+ * @param args any optional args
+ */
+ call(func: (sel: Update<Datum>, ...args: any[]) => any, ...args: any[]): Update<Datum>;
+
+ /**
+ * Returns true if the current selection is empty.
+ */
+ empty(): boolean;
+
+ /**
+ * Returns the first non-null element in the selection, or null otherwise.
+ */
+ node(): EventTarget;
+
+ /**
+ * Returns the total number of elements in the selection.
+ */
+ size(): number;
+
+ /**
+ * Returns the placeholder nodes for each data element for which no corresponding DOM element was found.
+ */
+ enter(): Enter<Datum>;
+
+ /**
+ * Returns a selection for those DOM nodes for which no new data element was found.
+ */
+ exit(): Selection<Datum>;
+ }
+
+ interface Enter<Datum> {
+ append(name: string): Selection<Datum>;
+ append(name: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+
+ insert(name: string, before?: string): Selection<Datum>;
+ insert(name: string, before: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+ insert(name: (datum: Datum, index: number) => EventTarget, before?: string): Selection<Datum>;
+ insert(name: (datum: Datum, index: number) => EventTarget, before: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+
+ select(name: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+ call(func: (selection: Enter<Datum>, ...args: any[]) => any, ...args: any[]): Enter<Datum>;
+ }
+ }
+
+ /**
+ * Administrivia: JavaScript primitive types, or "things that toString() predictably".
+ */
+ export type Primitive = number | string | boolean;
+
+ /**
+ * Administrivia: anything with a valueOf(): number method is comparable, so we allow it in numeric operations
+ */
+ interface Numeric {
+ valueOf(): number;
+ }
+
+ /**
+ * A grouped array of nodes.
+ * @param Datum the data bound to this selection.
+ */
+ interface Selection<Datum> {
+ /**
+ * Retrieve a grouped selection.
+ */
+ [index: number]: selection.Group;
+
+ /**
+ * The number of groups in this selection.
+ */
+ length: number;
+
+ /**
+ * Retrieve the value of the given attribute for the first node in the selection.
+ *
+ * @param name The attribute name to query. May be prefixed (see d3.ns.prefix).
+ */
+ attr(name: string): string;
+
+ /**
+ * For all nodes, set the attribute to the specified constant value. Use null to remove.
+ *
+ * @param name The attribute name, optionally prefixed.
+ * @param value The attribute value to use. Note that this is coerced to a string automatically.
+ */
+ attr(name: string, value: Primitive): Selection<Datum>;
+
+ /**
+ * Derive an attribute value for each node in the selection based on bound data.
+ *
+ * @param name The attribute name, optionally prefixed.
+ * @param value The function of the datum (the bound data item) and index (the position in the subgrouping) which computes the attribute value. If the function returns null, the attribute is removed.
+ */
+ attr(name: string, value: (datum: Datum, index: number) => Primitive): Selection<Datum>;
+
+ /**
+ * Set multiple properties at once using an Object. D3 iterates over all enumerable properties and either sets or computes the attribute's value based on the corresponding entry in the Object.
+ *
+ * @param obj A key-value mapping corresponding to attributes and values. If the value is a simple string or number, it is taken as a constant. Otherwise, it is a function that derives the attribute value.
+ */
+ attr(obj: { [key: string]: Primitive | ((datum: Datum, index: number) => Primitive) }): Selection<Datum>;
+
+ /**
+ * Returns true if the first node in this selection has the given class list. If multiple classes are specified (i.e., "foo bar"), then returns true only if all classes match.
+ *
+ * @param name The class list to query.
+ */
+ classed(name: string): boolean;
+
+ /**
+ * Adds (or removes) the given class list.
+ *
+ * @param name The class list to toggle. Spaces separate class names: "foo bar" is a list of two classes.
+ * @param value If true, add the classes. If false, remove them.
+ */
+ classed(name: string, value: boolean): Selection<Datum>;
+
+ /**
+ * Determine if the given class list should be toggled for each node in the selection.
+ *
+ * @param name The class list. Spaces separate multiple class names.
+ * @param value The function to run for each node. Should return true to add the class to the node, or false to remove it.
+ */
+ classed(name: string, value: (datum: Datum, index: number) => boolean): Selection<Datum>;
+
+ /**
+ * Set or derive classes for multiple class lists at once.
+ *
+ * @param obj An Object mapping class lists to values that are either plain booleans or functions that return booleans.
+ */
+ classed(obj: { [key: string]: boolean | ((datum: Datum, index: number) => boolean) }): Selection<Datum>;
+
+ /**
+ * Retrieve the computed style value for the first node in the selection.
+ * @param name The CSS property name to query
+ */
+ style(name: string): string;
+
+ /**
+ * Set a style property for all nodes in the selection.
+ * @param name the CSS property name
+ * @param value the property value
+ * @param priority if specified, either null or the string "important" (no exclamation mark)
+ */
+ style(name: string, value: Primitive, priority?: string): Selection<Datum>;
+
+ /**
+ * Derive a property value for each node in the selection.
+ * @param name the CSS property name
+ * @param value the function to derive the value
+ * @param priority if specified, either null or the string "important" (no exclamation mark)
+ */
+ style(name: string, value: (datum: Datum, index: number) => Primitive, priority?: string): Selection<Datum>;
+
+ /**
+ * Set a large number of CSS properties from an object.
+ *
+ * @param obj an Object whose keys correspond to CSS property names and values are either constants or functions that derive property values
+ * @param priority if specified, either null or the string "important" (no exclamation mark)
+ */
+ style(obj: { [key: string]: Primitive | ((datum: Datum, index: number) => Primitive) }, priority?: string): Selection<Datum>;
+
+ /**
+ * Retrieve an arbitrary node property such as the 'checked' property of checkboxes, or the 'value' of text boxes.
+ *
+ * @param name the node's property to retrieve
+ */
+ property(name: string): any;
+
+ /**
+ * For each node, set the property value. Internally, this sets the node property directly (e.g., node[name] = value), so take care not to mutate special properties like __proto__.
+ *
+ * @param name the property name
+ * @param value the property value
+ */
+ property(name: string, value: any): Selection<Datum>;
+
+ /**
+ * For each node, derive the property value. Internally, this sets the node property directly (e.g., node[name] = value), so take care not to mutate special properties like __proto__.
+ *
+ * @param name the property name
+ * @param value the function used to derive the property's value
+ */
+ property(name: string, value: (datum: Datum, index: number) => any): Selection<Datum>;
+
+ /**
+ * Set multiple node properties. Caveats apply: take care not to mutate special properties like __proto__.
+ *
+ * @param obj an Object whose keys correspond to node properties and values are either constants or functions that will compute a value.
+ */
+ property(obj: { [key: string]: any | ((datum: Datum, index: number) => any) }): Selection<Datum>;
+
+ /**
+ * Retrieve the textContent of the first node in the selection.
+ */
+ text(): string;
+
+ /**
+ * Set the textContent of each node in the selection.
+ * @param value the text to use for all nodes
+ */
+ text(value: Primitive): Selection<Datum>;
+
+ /**
+ * Compute the textContent of each node in the selection.
+ * @param value the function which will compute the text
+ */
+ text(value: (datum: Datum, index: number) => Primitive): Selection<Datum>;
+
+ /**
+ * Retrieve the HTML content of the first node in the selection. Uses 'innerHTML' internally and will not work with SVG or other elements without a polyfill.
+ */
+ html(): string;
+
+ /**
+ * Set the HTML content of every node in the selection. Uses 'innerHTML' internally and thus will not work with SVG or other elements without a polyfill.
+ * @param value the HTML content to use.
+ */
+ html(value: string): Selection<Datum>;
+
+ /**
+ * Compute the HTML content for each node in the selection. Uses 'innerHTML' internally and thus will not work with SVG or other elements without a polyfill.
+ * @param value the function to compute HTML content
+ */
+ html(value: (datum: Datum, index: number) => string): Selection<Datum>;
+
+ /**
+ * Appends a new child to each node in the selection. This child will inherit the parent's data (if available). Returns a fresh selection consisting of the newly-appended children.
+ *
+ * @param name the element name to append. May be prefixed (see d3.ns.prefix).
+ */
+ append(name: string): Selection<Datum>;
+
+ /**
+ * Appends a new child to each node in the selection by computing a new node. This child will inherit the parent's data (if available). Returns a fresh selection consisting of the newly-appended children.
+ *
+ * @param name the function to compute a new element
+ */
+ append(name: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+
+ /**
+ * Inserts a new child to each node in the selection. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the element name to append. May be prefixed (see d3.ns.prefix).
+ * @param before the selector to determine position (e.g., ":first-child")
+ */
+ insert(name: string, before: string): Selection<Datum>;
+
+ /**
+ * Inserts a new child to each node in the selection. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the element name to append. May be prefixed (see d3.ns.prefix).
+ * @param before a function to determine the node to use as the next sibling
+ */
+ insert(name: string, before: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+
+ /**
+ * Inserts a new child to the end of each node in the selection by computing a new node. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the function to compute a new child
+ * @param before the selector to determine position (e.g., ":first-child")
+ */
+ insert(name: (datum: Datum, index: number) => EventTarget, before: string): Selection<Datum>;
+
+ /**
+ * Inserts a new child to the end of each node in the selection by computing a new node. This child will inherit its parent's data (if available). Returns a fresh selection consisting of the newly-inserted children.
+ * @param name the function to compute a new child
+ * @param before a function to determine the node to use as the next sibling
+ */
+ insert(name: (datum: Datum, index: number) => EventTarget, before: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+
+ /**
+ * Removes the elements from the DOM. They are in a detached state and may be re-added (though there is currently no dedicated API for doing so).
+ */
+ remove(): Selection<Datum>;
+
+ /**
+ * Retrieves the data bound to the first group in this selection.
+ */
+ data(): Datum[];
+
+ /**
+ * Binds data to this selection.
+ * @param data the array of data to bind to this selection
+ * @param key the optional function to determine the unique key for each piece of data. When unspecified, uses the index of the element.
+ */
+ data<NewDatum>(data: NewDatum[], key?: (datum: NewDatum, index: number) => string): selection.Update<NewDatum>;
+
+ /**
+ * Derives data to bind to this selection.
+ * @param data the function to derive data. Must return an array.
+ * @param key the optional function to determine the unique key for each data item. When unspecified, uses the index of the element.
+ */
+ data<NewDatum>(data: (datum: Datum, index: number) => NewDatum[], key?: (datum: NewDatum, index: number) => string): selection.Update<NewDatum>;
+
+ /**
+ * Filters the selection, returning only those nodes that match the given CSS selector.
+ * @param selector the CSS selector
+ */
+ filter(selector: string): Selection<Datum>;
+
+ /**
+ * Filters the selection, returning only those nodes for which the given function returned true.
+ * @param selector the filter function
+ */
+ filter(selector: (datum: Datum, index: number) => boolean): Selection<Datum>;
+
+ /**
+ * Return the data item bound to the first element in the selection.
+ */
+ datum(): Datum;
+
+ /**
+ * Derive the data item for each node in the selection. Useful for situations such as the HTML5 'dataset' attribute.
+ * @param value the function to compute data for each node
+ */
+ datum<NewDatum>(value: (datum: Datum, index: number) => NewDatum): Selection<NewDatum>;
+
+ /**
+ * Set the data item for each node in the selection.
+ * @param value the constant element to use for each node
+ */
+ datum<NewDatum>(value: NewDatum): Selection<NewDatum>;
+
+ /**
+ * Reorders nodes in the selection based on the given comparator. Nodes are re-inserted into the document once sorted.
+ * @param comparator the comparison function, which defaults to d3.ascending
+ */
+ sort(comparator?: (a: Datum, b: Datum) => number): Selection<Datum>;
+
+ /**
+ * Reorders nodes in the document to match the selection order. More efficient than calling sort() if the selection is already ordered.
+ */
+ order(): Selection<Datum>;
+
+ /**
+ * Returns the listener (if any) for the given event.
+ * @param type the type of event to load the listener for. May have a namespace (e.g., ".foo") at the end.
+ */
+ on(type: string): (datum: Datum, index: number) => any;
+
+ /**
+ * Adds a listener for the specified event. If one was already registered, it is removed before the new listener is added. The return value of the listener function is ignored.
+ * @param type the of event to listen to. May have a namespace (e.g., ".foo") at the end.
+ * @param listener an event listener function, or null to unregister
+ * @param capture sets the DOM useCapture flag
+ */
+ on(type: string, listener: (datum: Datum, index: number) => any, capture?: boolean): Selection<Datum>;
+
+ /**
+ * Begins a new transition. Interrupts any active transitions of the same name.
+ * @param name the transition name (defaults to "")
+ */
+ transition(name?: string): Transition<Datum>;
+
+ /**
+ * Interrupts the active transition of the provided name. Does not cancel scheduled transitions.
+ * @param name the transition name (defaults to "")
+ */
+ interrupt(name?: string): Selection<Datum>;
+
+ /**
+ * Creates a subselection by finding the first descendent matching the selector string. Bound data is inherited.
+ * @param selector the CSS selector to match against
+ */
+ select(selector: string): Selection<Datum>;
+
+ /**
+ * Creates a subselection by using a function to find descendent elements. Bound data is inherited.
+ * @param selector the function to find matching descendants
+ */
+ select(selector: (datum: Datum, index: number) => EventTarget): Selection<Datum>;
+
+ /**
+ * Creates a subselection by finding all descendents that match the given selector. Bound data is not inherited.
+ * @param selector the CSS selector to match against
+ */
+ selectAll(selector: string): Selection<any>;
+
+ /**
+ * Creates a subselection by finding all descendants that match the given selector. Bound data is not inherited.
+ *
+ * Use this overload when data-binding a subselection (that is, sel.selectAll('.foo').data(d => ...)). The type will carry over.
+ */
+ selectAll<T>(selector: string): Selection<T>;
+
+ /**
+ * Creates a subselection by using a function to find descendent elements. Bound data is not inherited.
+ * @param selector the function to find matching descendents
+ */
+ selectAll(selector: (datum: Datum, index: number) => Array<EventTarget> | NodeList): Selection<any>;
+
+ /**
+ * Creates a subselection by using a function to find descendent elements. Bound data is not inherited.
+ *
+ * Use this overload when data-binding a subselection (that is, sel.selectAll('.foo').data(d => ...)). The type will carry over.
+ * @param selector the function to find matching descendents
+ */
+ selectAll<T>(selector: (datum: Datum, index: number) => Array<EventTarget> | NodeList): Selection<T>;
+
+ /**
+ * Invoke the given function for each element in the selection. The return value of the function is ignored.
+ * @param func the function to invoke
+ */
+ each(func: (datum: Datum, index: number) => any): Selection<Datum>;
+
+ /**
+ * Call a function on the selection. sel.call(foo) is equivalent to foo(sel).
+ * @param func the function to call on the selection
+ * @param args any optional args
+ */
+ call(func: (sel: Selection<Datum>, ...args: any[]) => any, ...args: any[]): Selection<Datum>;
+
+ /**
+ * Returns true if the current selection is empty.
+ */
+ empty(): boolean;
+
+ /**
+ * Returns the first non-null element in the selection, or null otherwise.
+ */
+ node(): EventTarget;
+
+ /**
+ * Returns the total number of elements in the selection.
+ */
+ size(): number;
+ }
+
+ export function transition(): Transition<any>;
+ module transition {
+ export var prototype: Transition<any>;
+ }
+
+ interface Transition<Datum> {
+ delay(): number;
+ delay(delay: number): Transition<Datum>;
+ delay(delay: (datum: Datum, index: number) => number): Transition<Datum>;
+
+ duration(): number;
+ duration(duration: number): Transition<Datum>;
+ duration(duration: (datum: Datum, index: number) => number): Transition<Datum>;
+
+ ease(): (t: number) => number;
+ ease(value: string, ...args: any[]): Transition<Datum>;
+ ease(value: (t: number) => number): Transition<Datum>;
+
+ attr(name: string, value: Primitive): Transition<Datum>;
+ attr(name: string, value: (datum: Datum, index: number) => Primitive): Transition<Datum>;
+ attr(obj: { [key: string]: Primitive | ((datum: Datum, index: number) => Primitive) }): Transition<Datum>;
+
+ attrTween(name: string, tween: (datum: Datum, index: number, attr: string) => Primitive): Transition<Datum>;
+
+ style(name: string, value: Primitive, priority?: string): Transition<Datum>;
+ style(name: string, value: (datum: Datum, index: number) => Primitive, priority?: string): Transition<Datum>;
+ style(obj: { [key: string]: Primitive | ((datum: Datum, index: number) => Primitive) }, priority?: string): Transition<Datum>;
+
+ styleTween(name: string, tween: (datum: Datum, index: number, attr: string) => Primitive, priority?: string): Transition<Datum>;
+
+ text(value: Primitive): Transition<Datum>;
+ text(value: (datum: Datum, index: number) => Primitive): Transition<Datum>;
+
+ tween(name: string, factory: () => (t: number) => any): Transition<Datum>;
+
+ remove(): Transition<Datum>;
+
+ select(selector: string): Transition<Datum>;
+ select(selector: (d: Datum, i: number) => EventTarget): Transition<Datum>;
+
+ selectAll(selector: string): Transition<any>;
+ selectAll(selector: (d: Datum, i: number) => EventTarget[]): Transition<any>;
+
+ filter(selector: string): Transition<Datum>;
+ filter(selector: (d: Datum, i: number) => boolean): Transition<Datum>;
+
+ each(type: string, listener: (d: Datum, i: number) => any): Transition<Datum>;
+ each(listener: (d: Datum, i: number) => any): Transition<Datum>;
+
+ call(func: (transition: Transition<Datum>, ...args: any[]) => any, ...args: any[]): Transition<Datum>;
+
+ empty(): boolean;
+ node(): EventTarget;
+ size(): number;
+ }
+
+ export function ease(type: 'linear'): (t: number) => number;
+ export function ease(type: 'linear-in'): (t: number) => number;
+ export function ease(type: 'linear-out'): (t: number) => number;
+ export function ease(type: 'linear-in-out'): (t: number) => number;
+ export function ease(type: 'linear-out-in'): (t: number) => number;
+
+ export function ease(type: 'poly', k: number): (t: number) => number;
+ export function ease(type: 'poly-in', k: number): (t: number) => number;
+ export function ease(type: 'poly-out', k: number): (t: number) => number;
+ export function ease(type: 'poly-in-out', k: number): (t: number) => number;
+ export function ease(type: 'poly-out-in', k: number): (t: number) => number;
+
+ export function ease(type: 'quad'): (t: number) => number;
+ export function ease(type: 'quad-in'): (t: number) => number;
+ export function ease(type: 'quad-out'): (t: number) => number;
+ export function ease(type: 'quad-in-out'): (t: number) => number;
+ export function ease(type: 'quad-out-in'): (t: number) => number;
+
+ export function ease(type: 'cubic'): (t: number) => number;
+ export function ease(type: 'cubic-in'): (t: number) => number;
+ export function ease(type: 'cubic-out'): (t: number) => number;
+ export function ease(type: 'cubic-in-out'): (t: number) => number;
+ export function ease(type: 'cubic-out-in'): (t: number) => number;
+
+ export function ease(type: 'sin'): (t: number) => number;
+ export function ease(type: 'sin-in'): (t: number) => number;
+ export function ease(type: 'sin-out'): (t: number) => number;
+ export function ease(type: 'sin-in-out'): (t: number) => number;
+ export function ease(type: 'sin-out-in'): (t: number) => number;
+
+ export function ease(type: 'circle'): (t: number) => number;
+ export function ease(type: 'circle-in'): (t: number) => number;
+ export function ease(type: 'circle-out'): (t: number) => number;
+ export function ease(type: 'circle-in-out'): (t: number) => number;
+ export function ease(type: 'circle-out-in'): (t: number) => number;
+
+ export function ease(type: 'elastic', a?: number, b?: number): (t: number) => number;
+ export function ease(type: 'elastic-in', a?: number, b?: number): (t: number) => number;
+ export function ease(type: 'elastic-out', a?: number, b?: number): (t: number) => number;
+ export function ease(type: 'elastic-in-out', a?: number, b?: number): (t: number) => number;
+ export function ease(type: 'elastic-out-in', a?: number, b?: number): (t: number) => number;
+
+ export function ease(type: 'back', s: number): (t: number) => number;
+ export function ease(type: 'back-in', s: number): (t: number) => number;
+ export function ease(type: 'back-out', s: number): (t: number) => number;
+ export function ease(type: 'back-in-out', s: number): (t: number) => number;
+ export function ease(type: 'back-out-in', s: number): (t: number) => number;
+
+ export function ease(type: 'bounce'): (t: number) => number;
+ export function ease(type: 'bounce-in'): (t: number) => number;
+ export function ease(type: 'bounce-out'): (t: number) => number;
+ export function ease(type: 'bounce-in-out'): (t: number) => number;
+ export function ease(type: 'bounce-out-in'): (t: number) => number;
+
+ export function ease(type: string, ...args: any[]): (t: number) => number;
+
+ export function timer(func: () => any, delay?: number, time?: number): void;
+
+ module timer {
+ export function flush(): void;
+ }
+
+ /**
+ * The current event's value. Use this variable in a handler registered with selection.on.
+ */
+ export var event: Event;
+
+ /**
+ * Returns the x and y coordinates of the mouse relative to the provided container element, using d3.event for the mouse's position on the page.
+ * @param container the container element (e.g. an SVG <g> element)
+ */
+ export function mouse(container: EventTarget): [number, number];
+
+ /**
+ * Given a container element and a touch identifier, determine the x and y coordinates of the touch.
+ * @param container the container element (e.g., an SVG <svg> element)
+ * @param identifier the given touch identifier
+ */
+ export function touch(container: EventTarget, identifer: number): [number, number];
+
+ /**
+ * Given a container element, a list of touches, and a touch identifier, determine the x and y coordinates of the touch.
+ * @param container the container element (e.g., an SVG <svg> element)
+ * @param identifier the given touch identifier
+ */
+ export function touch(container: EventTarget, touches: TouchList, identifer: number): [number, number];
+
+ /**
+ * Given a container element and an optional list of touches, return the position of every touch relative to the container.
+ * @param container the container element
+ * @param touches an optional list of touches (defaults to d3.event.touches)
+ */
+ export function touches(container: EventTarget, touches?: TouchList): Array<[number, number]>;
+
+ // NB. this is limited to primitive values due to D3's use of the <, >, and >= operators. Results get weird for object instances.
+ /**
+ * Compares two primitive values for sorting (in ascending order).
+ */
+ export function ascending(a: Primitive, b: Primitive): number;
+
+ /**
+ * Compares two primitive values for sorting (in ascending order).
+ */
+ export function descending(a: Primitive, b: Primitive): number;
+
+ /**
+ * Return the minimum value in the array using natural order.
+ */
+ export function min(array: number[]): number;
+
+ /**
+ * Return the minimum value in the array using natural order.
+ */
+ export function min(array: string[]): string;
+
+ /**
+ * Return the minimum value in the array using natural order.
+ */
+ export function min<T extends Numeric>(array: T[]): T;
+
+ /**
+ * Return the minimum value in the array using natural order.
+ */
+ export function min<T>(array: T[], accessor: (datum: T, index: number) => number): number;
+
+ /**
+ * Return the minimum value in the array using natural order.
+ */
+ export function min<T>(array: T[], accessor: (datum: T, index: number) => string): string;
+
+ /**
+ * Return the minimum value in the array using natural order.
+ */
+ export function min<T, U extends Numeric>(array: T[], accessor: (datum: T, index: number) => U): U;
+
+ /**
+ * Return the maximum value in the array of numbers using natural order.
+ */
+ export function max(array: number[]): number;
+
+ /**
+ * Return the maximum value in the array of strings using natural order.
+ */
+ export function max(array: string[]): string;
+
+ /**
+ * Return the maximum value in the array of numbers using natural order.
+ */
+ export function max<T extends Numeric>(array: T[]): T;
+
+ /**
+ * Return the maximum value in the array using natural order and a projection function to map values to numbers.
+ */
+ export function max<T>(array: T[], accessor: (datum: T, index: number) => number): number;
+
+ /**
+ * Return the maximum value in the array using natural order and a projection function to map values to strings.
+ */
+ export function max<T>(array: T[], accessor: (datum: T, index: number) => string): string;
+
+ /**
+ * Return the maximum value in the array using natural order and a projection function to map values to easily-sorted values.
+ */
+ export function max<T, U extends Numeric>(array: T[], accessor: (datum: T, index: number) => U): U;
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent(array: number[]): [number, number];
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent(array: string[]): [string, string];
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent<T extends Numeric>(array: T[]): [T, T];
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent<T extends Numeric>(array: Array<T | Primitive>): [T | Primitive, T | Primitive];
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent<T>(array: T[], accessor: (datum: T, index: number) => number): [number, number];
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent<T>(array: T[], accessor: (datum: T, index: number) => string): [string, string];
+
+ /**
+ * Return the min and max simultaneously.
+ */
+ export function extent<T, U extends Numeric>(array: U[], accessor: (datum: T, index: number) => U): [U | Primitive, U | Primitive];
+
+ /**
+ * Compute the sum of an array of numbers.
+ */
+ export function sum(array: number[]): number;
+
+ /**
+ * Compute the sum of an array, using the given accessor to convert values to numbers.
+ */
+ export function sum<T>(array: T[], accessor: (datum: T, index: number) => number): number;
+
+ export function mean(array: number[]): number;
+ export function mean<T>(array: T[], accessor: (datum: T, index: number) => number): number;
+
+ export function quantile(array: number[], p: number): number;
+
+ export function variance(array: number[]): number;
+ export function variance<T>(array: T[], accessor: (datum: T, index: number) => number): number;
+
+ export function deviation(array: number[]): number;
+ export function deviation<T>(array: T[], accessor: (datum: T, index: number) => number): number;
+
+ export function bisectLeft(array: number[], x: number, lo?: number, hi?: number): number;
+ export function bisectLeft(array: string[], x: string, lo?: number, hi?: number): number;
+
+ export var bisect: typeof bisectRight;
+
+ export function bisectRight<T>(array: T[], x: T, lo?: number, hi?: number): number;
+
+ export function bisector<T, U>(accessor: (x: T) => U): {
+ left: (array: T[], x: T, lo?: number, hi?: number) => number;
+ right: (array: T[], x: T, lo?: number, hi?: number) => number;
+ }
+
+ export function bisector<T, U>(comparator: (a: T, b: U) => number): {
+ left: (array: T[], x: U, lo?: number, hi?: number) => number;
+ right: (array: T[], x: U, lo?: number, hi?: number) => number;
+ }
+
+ export function shuffle<T>(array: T[], lo?: number, hi?: number): T[];
+
+ /**
+ * Returns the enumerable property names of the specified object.
+ * @param object a JavaScript object
+ */
+ export function keys(object: Object): string[];
+
+ /**
+ * Returns an array containing the property values of the specified object.
+ */
+ export function values<T>(object: { [key: string]: T }): T[];
+ /**
+ * Returns an array containing the property values of the specified object.
+ */
+ export function values<T>(object: { [key: number]: T }): T[];
+ /**
+ * Returns an array containing the property values of the specified object.
+ */
+ export function values(object: Object): any[];
+
+ /**
+ * Returns an array of key-value pairs containing the property values of the specified object.
+ */
+ export function entries<T>(object: { [key: string]: T }): { key: string; value: T }[];
+
+ /**
+ * Returns an array of key-value pairs containing the property values of the specified object.
+ */
+ export function entries<T>(object: { [key: number]: T }): { key: string; value: T }[];
+
+ /**
+ * Returns an array of key-value pairs containing the property values of the specified object.
+ */
+ export function entries(object: Object): { key: string; value: any }[];
+
+ /**
+ * A shim for ES6 maps. The implementation uses a JavaScript object internally, and thus keys are limited to strings.
+ */
+ interface Map<T> {
+ /**
+ * Does the map contain the given key?
+ */
+ has(key: string): boolean;
+
+ /**
+ * Retrieve the value for the given key. Returns undefined if there is no value stored.
+ */
+ get(key: string): T;
+
+ /**
+ * Set the value for the given key. Returns the new value.
+ */
+ set(key: string, value: T): T;
+
+ /**
+ * Remove the value for the given key. Returns true if there was a value and false otherwise.
+ */
+ remove(key: string): boolean;
+
+ /**
+ * Returns an array of all keys in arbitrary order.
+ */
+ keys(): string[];
+
+ /**
+ * Returns an array of all values in arbitrary order.
+ */
+ values(): T[];
+
+ /**
+ * Returns an array of key-value objects in arbitrary order.
+ */
+ entries(): { key: string; value: T }[];
+
+ /**
+ * Calls the function for each key and value pair in the map. The 'this' context is the map itself.
+ */
+ forEach(func: (key: string, value: T) => any): void;
+
+ /**
+ * Is this map empty?
+ */
+ empty(): boolean;
+
+ /**
+ * Returns the number of elements stored in the map.
+ */
+ size(): number;
+ }
+
+ /**
+ * Constructs an initially empty map.
+ */
+ export function map<T>(): Map<T>;
+
+ /**
+ * Construct a new map by copying keys and values from the given one.
+ */
+ export function map<T>(object: Map<T>): Map<T>;
+
+ /**
+ * Construct a new map by copying enumerable properties and values from the given object.
+ */
+ export function map<T>(object: { [key: string]: T }): Map<T>;
+
+ /**
+ * Construct a new map by copying enumerable properties and values from the given object.
+ */
+ export function map<T>(object: { [key: number]: T }): Map<T>;
+
+ /**
+ * Construct a new map by copying elements from the array. The key function is used to identify each object.
+ */
+ export function map<T>(array: T[], key: (datum: T, index: number) => string): Map<T>;
+
+ /**
+ * Construct a new map by copying enumerable properties and values from the given object.
+ */
+ export function map(object: Object): Map<any>;
+
+ /**
+ * A shim for ES6 sets. Is only able to store strings.
+ */
+ interface Set {
+ /**
+ * Is the given string stored in this set?
+ */
+ has(value: string): boolean;
+
+ /**
+ * Add the string to this set. Returns the value.
+ */
+ add(value: string): string;
+
+ /**
+ * Remove the given value from the set. Returns true if it was stored, and false otherwise.
+ */
+ remove(value: string): boolean;
+
+ /**
+ * Returns an array of the strings stored in this set.
+ */
+ values(): string[];
+
+ /**
+ * Calls a given function for each value in the set. The return value of the function is ignored. The this context of the function is the set itself.
+ */
+ forEach(func: (value: string) => any): void;
+
+ /**
+ * Is this set empty?
+ */
+ empty(): boolean;
+
+ /**
+ * Returns the number of values stored in this set.
+ */
+ size(): number;
+ }
+
+ /**
+ * Creates an initially-empty set.
+ */
+ export function set(): Set;
+
+ /**
+ * Initializes a set from the given array of strings.
+ */
+ export function set(array: string[]): Set;
+
+ /**
+ * Merges the specified arrays into a single array.
+ */
+ export function merge<T>(arrays: T[][]): T[];
+
+ /**
+ * Generates a 0-based numeric sequence. The output range does not include 'stop'.
+ */
+ export function range(stop: number): number[];
+
+ /**
+ * Generates a numeric sequence starting from the given start and stop values. 'step' defaults to 1. The output range does not include 'stop'.
+ */
+ export function range(start: number, stop: number, step?: number): number[];
+
+ /**
+ * Given the specified array, return an array corresponding to the list of indices in 'keys'.
+ */
+ export function permute<T>(array: { [key: number]: T }, keys: number[]): T[];
+
+ /**
+ * Given the specified object, return an array corresponding to the list of property names in 'keys'.
+ */
+ export function permute<T>(object: { [key: string]: T }, keys: string[]): T[];
+
+ // TODO construct n-tuples from n input arrays
+ export function zip<T>(...arrays: T[][]): T[][];
+
+ export function transpose<T>(matrix: T[][]): T[][];
+
+ /**
+ * For each adjacent pair of elements in the specified array, returns a new array of tuples of elements i and i - 1.
+ * Returns the empty array if the input array has fewer than two elements.
+ */
+ export function pairs<T>(array: T[]): Array<[T, T]>;
+
+ interface Nest<T> {
+ key(func: (datum: T) => string): Nest<T>;
+ sortKeys(comparator: (a: string, b: string) => number): Nest<T>;
+ sortValues(comparator: (a: T, b: T) => number): Nest<T>;
+ rollup<U>(func: (values: T[]) => U): Nest<T>;
+ map(array: T[]): { [key: string]: any };
+ map(array: T[], mapType: typeof d3.map): Map<any>;
+ entries(array: T[]): { key: string; values: any }[];
+ }
+
+ export function nest<T>(): Nest<T>;
+
+ export module random {
+ export function normal(mean?: number, deviation?: number): () => number;
+ export function logNormal(mean?: number, deviation?: number): () => number;
+ export function bates(count: number): () => number;
+ export function irwinHall(count: number): () => number;
+ }
+
+ interface Transform {
+ rotate: number;
+ translate: [number, number];
+ skew: number;
+ scale: [number, number];
+ toString(): string;
+ }
+
+ export function transform(transform: string): Transform;
+
+ export function format(specifier: string): (n: number) => string;
+
+ interface FormatPrefix {
+ symbol: string;
+ scale(n: number): number;
+ }
+
+ export function formatPrefix(value: number, precision?: number): FormatPrefix;
+
+ export function round(x: number, n?: number): number;
+
+ export function requote(string: string): string;
+
+ export var rgb: {
+ new (r: number, g: number, b: number): Rgb;
+ new (color: string): Rgb;
+
+ (r: number, g: number, b: number): Rgb;
+ (color: string): Rgb;
+ };
+
+ interface Rgb extends Color {
+ r: number;
+ g: number;
+ b: number;
+
+ brighter(k?: number): Rgb;
+ darker(k?: number): Rgb;
+
+ hsl(): Hsl;
+
+ toString(): string;
+ }
+
+ export var hsl: {
+ new (h: number, s: number, l: number): Hsl;
+ new (color: string): Hsl;
+
+ (h: number, s: number, l: number): Hsl;
+ (color: string): Hsl;
+ };
+
+ interface Hsl extends Color {
+ h: number;
+ s: number;
+ l: number;
+
+ brighter(k?: number): Hsl;
+ darker(k?: number): Hsl;
+
+ rgb(): Rgb;
+
+ toString(): string;
+ }
+
+ export var hcl: {
+ new (h: number, c: number, l: number): Hcl;
+ new (color: string): Hcl;
+
+ (h: number, c: number, l: number): Hcl;
+ (color: string): Hcl;
+ };
+
+ interface Hcl extends Color {
+ h: number;
+ c: number;
+ l: number;
+
+ brighter(k?: number): Hcl;
+ darker(k?: number): Hcl;
+ }
+
+ export var lab: {
+ new (l: number, a: number, b: number): Lab;
+ new (color: string): Lab;
+
+ (l: number, a: number, b: number): Lab;
+ (color: string): Lab;
+ }
+
+ interface Lab extends Color {
+ l: number;
+ a: number;
+ b: number;
+
+ brighter(k?: number): Lab;
+ darker(k?: number): Lab;
+
+ rgb(): Rgb;
+ toString(): string;
+ }
+
+ export var color: {
+ (): Color;
+ new (): Color;
+ };
+
+ interface Color {
+ rgb(): Rgb;
+ }
+
+ export module ns {
+ interface Qualified {
+ space: string;
+ local: string;
+ }
+
+ export var prefix: { [key: string]: string };
+ export function qualify(name: string): Qualified | string;
+ }
+
+ export function functor<T extends Function>(value: T): T;
+ export function functor<T>(value: T): () => T;
+
+ export function rebind(target: {}, source: {}, ...names: string[]): any;
+
+ export function dispatch(...names: string[]): Dispatch;
+
+ interface Dispatch {
+ on(type: string): (...args: any[]) => void;
+ on(type: string, listener: (...args: any[]) => any): Dispatch;
+ [event: string]: (...args: any[]) => void;
+ }
+
+ export module scale {
+ export function identity(): Identity;
+
+ interface Identity {
+ (n: number): number;
+ invert(n: number): number;
+
+ domain(): number[];
+ domain(numbers: number[]): Identity;
+
+ range(): number[];
+ range(numbers: number[]): Identity;
+
+ ticks(count?: number): number[];
+
+ tickFormat(count?: number, format?: string): (n: number) => string;
+
+ copy(): Identity;
+ }
+
+ export function linear(): Linear<number, number>;
+ export function linear<Output>(): Linear<Output, Output>;
+ export function linear<Range, Output>(): Linear<Range, Output>;
+
+ interface Linear<Range, Output> {
+ (x: number): Output;
+ invert(y: number): number;
+
+ domain(): number[];
+ domain(numbers: number[]): Linear<Range, Output>;
+
+ range(): Range[];
+ range(values: Range[]): Linear<Range, Output>;
+
+ rangeRound(values: number[]): Linear<number, number>;
+
+ interpolate(): (a: Range, b: Range) => (t: number) => Output;
+ interpolate(factory: (a: Range, b: Range) => (t: number) => Output): Linear<Range, Output>;
+
+ clamp(): boolean;
+ clamp(clamp: boolean): Linear<Range, Output>;
+
+ nice(count?: number): Linear<Range, Output>;
+
+ ticks(count?: number): number[];
+
+ tickFormat(count?: number, format?: string): (n: number) => string;
+
+ copy(): Linear<Range, Output>;
+ }
+
+ export function sqrt(): Pow<number, number>;
+ export function sqrt<Output>(): Pow<Output, Output>;
+ export function sqrt<Range, Output>(): Pow<Range, Output>;
+
+ export function pow(): Pow<number, number>;
+ export function pow<Output>(): Pow<Output, Output>;
+ export function pow<Range, Output>(): Pow<Range, Output>;
+
+ interface Pow<Range, Output> {
+ (x: number): Output;
+
+ invert(y: number): number;
+
+ domain(): number[];
+ domain(numbers: number[]): Pow<Range, Output>;
+
+ range(): Range[];
+ range(values: Range[]): Pow<Range, Output>;
+
+ rangeRound(values: number[]): Pow<number, number>;
+
+ exponent(): number;
+ exponent(k: number): Pow<Range, Output>;
+
+ interpolate(): (a: Range, b: Range) => (t: number) => Output;
+ interpolate(factory: (a: Range, b: Range) => (t: number) => Output): Pow<Range, Output>;
+
+ clamp(): boolean;
+ clamp(clamp: boolean): Pow<Range, Output>;
+
+ nice(m?: number): Pow<Range, Output>;
+
+ ticks(count?: number): number[];
+
+ tickFormat(count?: number, format?: string): (n: number) => string;
+
+ copy(): Pow<Range, Output>;
+ }
+
+ export function log(): Log<number, number>;
+ export function log<Output>(): Log<Output, Output>;
+ export function log<Range, Output>(): Log<Range, Output>;
+
+ interface Log<Range, Output> {
+ (x: number): Output;
+
+ invert(y: number): number;
+
+ domain(): number[];
+ domain(numbers: number[]): Log<Range, Output>;
+
+ range(): Range[];
+ range(values: Range[]): Log<Range, Output>;
+
+ rangeRound(values: number[]): Log<number, number>;
+
+ base(): number;
+ base(base: number): Log<Range, Output>;
+
+ interpolate(): (a: Range, b: Range) => (t: number) => Output;
+ interpolate(factory: (a: Range, b: Range) => (t: number) => Output): Log<Range, Output>;
+
+ clamp(): boolean;
+ clamp(clamp: boolean): Log<Range, Output>;
+
+ nice(): Log<Range, Output>;
+
+ ticks(): number[];
+
+ tickFormat(count?: number, format?: string): (t: number) => string;
+
+ copy(): Log<Range, Output>;
+ }
+
+ export function quantize<T>(): Quantize<T>;
+
+ interface Quantize<T> {
+ (x: number): T;
+
+ invertExtent(y: T): [number, number];
+
+ domain(): number[];
+ domain(numbers: number[]): Quantize<T>;
+
+ range(): T[];
+ range(values: T[]): Quantize<T>;
+
+ copy(): Quantize<T>;
+ }
+
+ export function quantile<T>(): Quantile<T>;
+
+ interface Quantile<T> {
+ (x: number): T;
+
+ invertExtent(y: T): [number, number];
+
+ domain(): number[];
+ domain(numbers: number[]): Quantile<T>;
+
+ range(): T[];
+ range(values: T[]): Quantile<T>;
+
+ quantiles(): number[];
+
+ copy(): Quantile<T>;
+ }
+
+ export function threshold<Range>(): Threshold<number, Range>;
+ export function threshold<Domain, Range>(): Threshold<Domain, Range>;
+
+ interface Threshold<Domain, Range> {
+ (x: number): Range;
+
+ invertExtent(y: Range): [Domain, Domain];
+
+ domain(): Domain[];
+ domain(domain: Domain[]): Threshold<Domain, Range>;
+
+ range(): Range[];
+ range(values: Range[]): Threshold<Domain, Range>;
+
+ copy(): Threshold<Domain, Range>;
+ }
+
+ export function ordinal<Range>(): Ordinal<string, Range>;
+ export function ordinal<Domain extends { toString(): string }, Range>(): Ordinal<Domain, Range>;
+ export function category10(): Ordinal<string, string>;
+ export function category10<Domain extends { toString(): string }>(): Ordinal<Domain, string>;
+ export function category20(): Ordinal<string, string>;
+ export function category20<Domain extends { toString(): string }>(): Ordinal<Domain, string>;
+ export function category20b(): Ordinal<string, string>;
+ export function category20b<Domain extends { toString(): string }>(): Ordinal<Domain, string>;
+ export function category20c(): Ordinal<string,string>;
+ export function category20c<Domain extends { toString(): string }>(): Ordinal<Domain, string>;
+
+ interface Ordinal<Domain extends { toString(): string }, Range> {
+ (x: Domain): Range;
+
+ domain(): Domain[];
+ domain(values: Domain[]): Ordinal<Domain, Range>;
+
+ range(): Range[];
+ range(values: Range[]): Ordinal<Domain, Range>;
+
+ rangePoints(interval: [number, number], padding?: number): Ordinal<Domain, number>;
+ rangeRoundPoints(interval: [number, number], padding?: number): Ordinal<Domain, number>;
+
+ rangeBands(interval: [number, number], padding?: number, outerPadding?: number): Ordinal<Domain, number>;
+ rangeRoundBands(interval: [number, number], padding?: number, outerPadding?: number): Ordinal<Domain, number>;
+
+ rangeBand(): number;
+ rangeExtent(): [number, number];
+
+ copy(): Ordinal<Domain, Range>;
+ }
+ }
+
+ export function interpolate(a: number, b: number): (t: number) => number;
+ export function interpolate(a: string, b: string): (t: number) => string;
+ export function interpolate(a: string | Color, b: Color): (t: number) => string;
+ export function interpolate(a: Array<string | Color>, b: Color[]): (t: number) => string;
+ export function interpolate<Range, Output>(a: Range[], b: Output[]): (t: number) => Output[];
+ export function interpolate<Range, Output>(a: Range[], b: Range[]): (t: number) => Output[];
+ export function interpolate(a: { [key: string]: string | Color }, b: { [key: string]: Color }): (t: number) => { [key: string]: string };
+ export function interpolate<Range, Output>(a: { [key: string]: Range }, b: { [key: string]: Output }): (t: number) => { [key: string]: Output };
+ export function interpolate<Range, Output>(a: { [key: string]: Range }, b: { [key: string]: Range }): (t: number) => { [key: string]: Output };
+
+ export function interpolateNumber(a: number, b: number): (t: number) => number;
+
+ export function interpolateRound(a: number, b: number): (t: number) => number;
+
+ export function interpolateString(a: string, b: string): (t: number) => string;
+
+ export function interpolateRgb(a: string | Color, b: string | Color): (t: number) => string;
+
+ export function interpolateHsl(a: string | Color, b: string | Color): (t: number) => string;
+
+ export function interpolateLab(a: string | Color, b: string | Color): (t: number) => string;
+
+ export function interpolateHcl(a: string | Color, b: string | Color): (t: number) => string;
+
+ export function interpolateArray(a: Array<string | Color>, b: Color[]): (t: number) => string[];
+ export function interpolateArray<Range, Output>(a: Range[], b: Range[]): (t: number) => Output[];
+ export function interpolateArray<Range, Output>(a: Range[], b: Output[]): (t: number) => Output[];
+
+ export function interpolateObject(a: { [key: string]: string | Color }, b: { [key: string]: Color }): (t: number) => { [key: string]: string };
+ export function interpolateObject<Range, Output>(a: { [key: string]: Range }, b: { [key: string]: Output }): (t: number) => { [key: string]: Output };
+ export function interpolateObject<Range, Output>(a: { [key: string]: Range }, b: { [key: string]: Range }): (t: number) => { [key: string]: Output };
+
+ export function interpolateTransform(a: string | Transform, b: string | Transform): (t: number) => string;
+
+ export function interpolateZoom(a: [number, number, number], b: [number, number, number]): {
+ (t: number): [number, number, number];
+ duration: number;
+ };
+
+ export var interpolators: Array<(a: any, b: any) => (t: number) => any>;
+
+ export module time {
+ export var second: Interval;
+ export var minute: Interval;
+ export var hour: Interval;
+ export var day: Interval;
+ export var week: Interval;
+ export var sunday: Interval;
+ export var monday: Interval;
+ export var tuesday: Interval;
+ export var wednesday: Interval;
+ export var thursday: Interval;
+ export var friday: Interval;
+ export var saturday: Interval;
+ export var month: Interval;
+ export var year: Interval;
+
+ interface Interval {
+ (d: Date): Date;
+
+ floor(d: Date): Date;
+
+ round(d: Date): Date;
+
+ ceil(d: Date): Date;
+
+ range(start: Date, stop: Date, step?: number): Date[];
+
+ offset(date: Date, step: number): Date;
+
+ utc: {
+ (d: Date): Date;
+
+ floor(d: Date): Date;
+
+ round(d: Date): Date;
+
+ ceil(d: Date): Date;
+
+ range(start: Date, stop: Date, step?: number): Date[];
+
+ offset(date: Date, step: number): Date;
+ }
+ }
+
+ export function seconds(start: Date, stop: Date, step?: number): Date[];
+ export function minutes(start: Date, stop: Date, step?: number): Date[];
+ export function hours(start: Date, stop: Date, step?: number): Date[];
+ export function days(start: Date, stop: Date, step?: number): Date[];
+ export function weeks(start: Date, stop: Date, step?: number): Date[];
+ export function sundays(start: Date, stop: Date, step?: number): Date[];
+ export function mondays(start: Date, stop: Date, step?: number): Date[];
+ export function tuesdays(start: Date, stop: Date, step?: number): Date[];
+ export function wednesdays(start: Date, stop: Date, step?: number): Date[];
+ export function thursdays(start: Date, stop: Date, step?: number): Date[];
+ export function fridays(start: Date, stop: Date, step?: number): Date[];
+ export function saturdays(start: Date, stop: Date, step?: number): Date[];
+ export function months(start: Date, stop: Date, step?: number): Date[];
+ export function years(start: Date, stop: Date, step?: number): Date[];
+
+ export function dayOfYear(d: Date): number;
+ export function weekOfYear(d: Date): number;
+ export function sundayOfYear(d: Date): number;
+ export function mondayOfYear(d: Date): number;
+ export function tuesdayOfYear(d: Date): number;
+ export function wednesdayOfYear(d: Date): number;
+ export function fridayOfYear(d: Date): number;
+ export function saturdayOfYear(d: Date): number;
+
+ export function format(specifier: string): Format;
+
+ export module format {
+ export function multi(formats: Array<[string, (d: Date) => boolean]>): Format;
+ export function utc(specifier: string): Format;
+ export var iso: Format;
+ }
+
+ interface Format {
+ (d: Date): string;
+ parse(input: string): Date;
+ }
+
+ export function scale(): Scale<number, number>;
+ export function scale<Output>(): Scale<Output, Output>;
+ export function scale<Range, Output>(): Scale<Range, Output>;
+
+ export module scale {
+ export function utc(): Scale<number, number>;
+ export function utc<Output>(): Scale<Output, Output>;
+ export function utc<Range, Output>(): Scale<Range, Output>;
+ }
+
+
+ interface Scale<Range, Output> {
+ (x: Date): Output;
+
+ invert(y: number): Date;
+
+ domain(): Date[];
+ domain(dates: number[]): Scale<Range, Output>;
+ domain(dates: Date[]): Scale<Range, Output>;
+
+ nice(): Scale<Range, Output>;
+ nice(interval: Interval, step?: number): Scale<Range, Output>;
+
+ range(): Range[];
+ range(values: Range[]): Scale<Range, Output>;
+
+ rangeRound(values: number[]): Scale<number, number>;
+
+ interpolate(): (a: Range, b: Range) => (t: number) => Output;
+ interpolate(factory: (a: Range, b: Range) => (t: number) => Output): Scale<Range, Output>;
+
+ clamp(): boolean;
+ clamp(clamp: boolean): Scale<Range, Output>;
+
+ ticks(): Date[];
+ ticks(interval: Interval, step?: number): Date[];
+ ticks(count: number): Date[];
+
+ tickFormat(count: number): (d: Date) => string;
+
+ copy(): Scale<Range, Output>;
+ }
+ }
+
+ export module behavior {
+ export function drag<Datum>(): Drag<Datum>;
+
+ interface Drag<Datum> {
+ (selection: Selection<Datum>): void;
+
+ on(type: string): (d: Datum, i: number) => any;
+ on(type: string, listener: (d: Datum, i: number) => any): Drag<Datum>;
+
+ origin(): (d: Datum, i: number) => { x: number; y: number };
+ origin(accessor: (d: Datum, i: number) => { x: number; y: number }): Drag<Datum>;
+ }
+
+ export function zoom<Datum>(): Zoom<Datum>;
+
+ module zoom {
+ interface Scale {
+ domain(): number[];
+ domain(values: number[]): Scale;
+
+ invert(y: number): number;
+
+ range(values: number[]): Scale;
+ range(): number[];
+ }
+ }
+
+ interface Zoom<Datum> {
+ (selection: Selection<Datum>): void;
+
+ translate(): [number, number];
+ translate(translate: [number, number]): Zoom<Datum>;
+
+ scale(): number;
+ scale(scale: number): Zoom<Datum>;
+
+ scaleExtent(): [number, number];
+ scaleExtent(extent: [number, number]): Zoom<Datum>;
+
+ center(): [number, number];
+ center(center: [number, number]): Zoom<Datum>;
+
+ size(): [number, number];
+ size(size: [number, number]): Zoom<Datum>;
+
+ x(): zoom.Scale;
+ x(x: zoom.Scale): Zoom<Datum>;
+
+ y(): zoom.Scale;
+ y(y: zoom.Scale): Zoom<Datum>;
+
+ on(type: string): (d: Datum, i: number) => any;
+ on(type: string, listener: (d: Datum, i: number) => any): Zoom<Datum>;
+
+ event(selection: Selection<Datum>): void;
+ event(transition: Transition<Datum>): void;
+ }
+ }
+
+ export module geo {
+ export function path(): Path;
+
+ interface Path {
+ (feature: any, index?: number): string;
+
+ area(feature: any): number;
+
+ centroid(feature: any): [number, number];
+
+ bounds(feature: any): [[number, number], [number, number]];
+
+ projection(): Transform | ((coordinates: [number, number]) => [number, number]);
+ projection(stream: Transform): Path;
+ projection(projection: (coordinates: [number, number]) => [number, number]): Path;
+
+ pointRadius(): number | ((datum: any, index: number) => number);
+ pointRadius(radius: number): Path;
+ pointRadius(radius: (datum: any, index: number) => number): Path;
+
+ context(): CanvasRenderingContext2D;
+ context(context: CanvasRenderingContext2D): Path;
+ }
+
+ export function graticule(): Graticule;
+
+ interface Graticule {
+ (): any;
+
+ lines(): any[];
+
+ outline(): any;
+
+ extent(): [[number, number], [number, number]];
+ extent(extent: [[number, number], [number, number]]): Graticule;
+
+ majorExtent(): [[number, number], [number, number]];
+ majorExtent(extent: [[number, number], [number, number]]): Graticule;
+
+ minorExtent(): [[number, number], [number, number]];
+ minorExtent(extent: [[number, number], [number, number]]): Graticule;
+
+ step(): [number, number];
+ step(step: [number, number]): Graticule;
+
+ majorStep(): [number, number];
+ majorStep(step: [number, number]): Graticule;
+
+ minorStep(): [number, number];
+ minorStep(step: [number, number]): Graticule;
+
+ precision(): number;
+ precision(precision: number): Graticule;
+ }
+
+ export function circle(): Circle;
+
+ interface Circle {
+ (...args: any[]): any;
+
+ origin(): [number, number] | ((...args: any[]) => [number, number]);
+ origin(origin: [number, number]): Circle;
+ origin(origin: (...args: any[]) => [number, number]): Circle;
+
+ angle(): number;
+ angle(angle: number): Circle;
+
+ precision(): number;
+ precision(precision: number): Circle;
+ }
+
+ export function area(feature: any): number;
+ export function centroid(feature: any): [number, number];
+ export function bounds(feature: any): [[number, number], [number, number]];
+ export function distance(a: [number, number], b: [number, number]): number;
+ export function length(feature: any): number;
+ export function interpolate(a: [number, number], b: [number, number]): (t: number) => [number, number];
+
+ export function rotation(rotate: [number, number] | [number, number, number]): Rotation;
+
+ interface Rotation {
+ (location: [number, number]): [number, number];
+ invert(location: [number, number]): [number, number];
+ }
+
+ export function stream(object: any, listener: Listener): void;
+
+ interface Listener {
+ point(x: number, y: number, z: number): void;
+ lineStart(): void;
+ lineEnd(): void;
+ polygonStart(): void;
+ polygonEnd(): void;
+ sphere(): void;
+ }
+
+ export function transform(methods: TransformMethods): Transform;
+
+ interface TransformMethods {
+ point?(x: number, y: number, z: number): void;
+ lineStart?(): void;
+ lineEnd?(): void;
+ polygonStart?(): void;
+ polygonEnd?(): void;
+ sphere?(): void;
+ }
+
+ interface Transform {
+ stream(stream: Listener): Listener;
+ }
+
+ export function clipExtent(): ClipExtent;
+
+ interface ClipExtent extends Transform {
+ extent(): [[number, number], [number, number]];
+ extent(extent: [[number, number], [number, number]]): ClipExtent;
+ }
+
+ export function projection(raw: RawInvertibleProjection): InvertibleProjection;
+ export function projection(raw: RawProjection): Projection;
+
+ export function projectionMutator(factory: (...args: any[]) => RawInvertibleProjection): (...args: any[]) => InvertibleProjection;
+ export function projectionMutator(factory: (...args: any[]) => RawProjection): (...args: any[]) => Projection;
+
+ export function albers(): ConicProjection;
+ export function albersUsa(): ConicProjection;
+ export function azimuthalEqualArea(): InvertibleProjection;
+ module azimuthalEqualArea {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function azimuthalEquidistant(): InvertibleProjection;
+ module azimuthalEquidistant {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function conicConformal(): ConicProjection;
+ module conicConformal {
+ export function raw(phi0: number, phi1: number): RawInvertibleProjection;
+ }
+
+ export function conicEqualArea(): ConicProjection;
+ module conicEqualArea {
+ export function raw(phi0: number, phi1: number): RawInvertibleProjection;
+ }
+
+ export function conicEquidistant(): ConicProjection;
+ module conicEquidistant {
+ export function raw(phi0: number, phi1: number): RawInvertibleProjection;
+ }
+
+ export function equirectangular(): InvertibleProjection;
+ module equirectangular {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function gnomonic(): InvertibleProjection;
+ module gnomonic {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function mercator(): InvertibleProjection;
+ module mercator {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function orthographic(): InvertibleProjection;
+ module orthographic {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function stereographic(): InvertibleProjection;
+ module stereographic {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ export function transverseMercator(): InvertibleProjection;
+ module transverseMercator {
+ export function raw(lambda: number, phi: number): [number, number];
+ module raw {
+ export function invert(x: number, y: number): [number, number];
+ }
+ }
+
+ interface Projection {
+ (location: [number, number]): [number, number];
+
+ rotate(): [number, number, number];
+ rotate(rotation: [number, number, number]): Projection;
+
+ center(): [number, number];
+ center(location: [number, number]): Projection;
+
+ translate(): [number, number];
+ translate(point: [number, number]): Projection;
+
+ scale(): number;
+ scale(scale: number): Projection;
+
+ clipAngle(): number;
+ clipAngle(angle: number): Projection;
+
+ clipExtent(): [[number, number], [number, number]];
+ clipExtent(extent: [[number, number], [number, number]]): Projection;
+
+ precision(): number;
+ precision(precision: number): Projection;
+
+ stream(listener: Listener): Listener;
+ }
+
+ interface InvertibleProjection extends Projection {
+ invert(point: [number, number]): [number, number];
+ }
+
+ interface ConicProjection extends InvertibleProjection {
+ parallels(): [number, number];
+ parallels(parallels: [number, number]): ConicProjection;
+
+ rotate(): [number, number, number];
+ rotate(rotation: [number, number, number]): ConicProjection;
+
+ center(): [number, number];
+ center(location: [number, number]): ConicProjection;
+
+ translate(): [number, number];
+ translate(point: [number, number]): ConicProjection;
+
+ scale(): number;
+ scale(scale: number): ConicProjection;
+
+ clipAngle(): number;
+ clipAngle(angle: number): ConicProjection;
+
+ clipExtent(): [[number, number], [number, number]];
+ clipExtent(extent: [[number, number], [number, number]]): ConicProjection;
+
+ precision(): number;
+ precision(precision: number): ConicProjection;
+ }
+
+ interface RawProjection {
+ (lambda: number, phi: number): [number, number];
+ }
+
+ interface RawInvertibleProjection extends RawProjection {
+ invert(x: number, y: number): [number, number];
+ }
+ }
+
+ module svg {
+ export function line(): Line<[number, number]>;
+ export function line<T>(): Line<T>;
+
+ interface Line<T> {
+ (data: T[]): string;
+
+ x(): number | ((d: T, i: number) => number);
+ x(x: number): Line<T>;
+ x(x: (d: T, i: number) => number): Line<T>;
+
+ y(): number | ((d: T, i: number) => number);
+ y(x: number): Line<T>;
+ y(y: (d: T, i: number) => number): Line<T>;
+
+ interpolate(): string | ((points: Array<[number, number]>) => string);
+ interpolate(interpolate: "linear"): Line<T>;
+ interpolate(interpolate: "linear-closed"): Line<T>;
+ interpolate(interpolate: "step"): Line<T>;
+ interpolate(interpolate: "step-before"): Line<T>;
+ interpolate(interpolate: "step-after"): Line<T>;
+ interpolate(interpolate: "basis"): Line<T>;
+ interpolate(interpolate: "basis-open"): Line<T>;
+ interpolate(interpolate: "basis-closed"): Line<T>;
+ interpolate(interpolate: "bundle"): Line<T>;
+ interpolate(interpolate: "cardinal"): Line<T>;
+ interpolate(interpolate: "cardinal-open"): Line<T>;
+ interpolate(interpolate: "cardinal-closed"): Line<T>;
+ interpolate(interpolate: "monotone"): Line<T>;
+ interpolate(interpolate: string): Line<T>;
+ interpolate(interpolate: (points: Array<[number, number]>) => string): Line<T>;
+
+ tension(): number;
+ tension(tension: number): Line<T>;
+
+ defined(): (d: T, i: number) => boolean;
+ defined(defined: (d: T, i: number) => boolean): Line<T>;
+ }
+
+ module line {
+ export function radial(): Radial<[number, number]>;
+ export function radial<T>(): Radial<T>;
+
+ interface Radial<T> {
+ (data: T[]): string;
+
+ radius(): number | ((d: T, i: number) => number);
+ radius(radius: number): Radial<T>;
+ radius(radius: (d: T, i: number) => number): Radial<T>;
+
+ angle(): number | ((d: T, i: number) => number);
+ angle(angle: number): Radial<T>;
+ angle(angle: (d: T, i: number) => number): Radial<T>;
+
+ interpolate(): string | ((points: Array<[number, number]>) => string);
+ interpolate(interpolate: "linear"): Radial<T>;
+ interpolate(interpolate: "linear-closed"): Radial<T>;
+ interpolate(interpolate: "step"): Radial<T>;
+ interpolate(interpolate: "step-before"): Radial<T>;
+ interpolate(interpolate: "step-after"): Radial<T>;
+ interpolate(interpolate: "basis"): Radial<T>;
+ interpolate(interpolate: "basis-open"): Radial<T>;
+ interpolate(interpolate: "basis-closed"): Radial<T>;
+ interpolate(interpolate: "bundle"): Radial<T>;
+ interpolate(interpolate: "cardinal"): Radial<T>;
+ interpolate(interpolate: "cardinal-open"): Radial<T>;
+ interpolate(interpolate: "cardinal-closed"): Radial<T>;
+ interpolate(interpolate: "monotone"): Radial<T>;
+ interpolate(interpolate: string): Radial<T>;
+ interpolate(interpolate: (points: Array<[number, number]>) => string): Radial<T>;
+
+ tension(): number;
+ tension(tension: number): Radial<T>;
+
+ defined(): (d: T, i: number) => boolean;
+ defined(defined: (d: T, i: number) => boolean): Radial<T>;
+ }
+ }
+
+ export function area(): Area<[number, number]>;
+ export function area<T>(): Area<T>;
+
+ interface Area<T> {
+ (data: T[]): string;
+
+ x(): number | ((d: T, i: number) => number);
+ x(x: number): Area<T>;
+ x(x: (d: T, i: number) => number): Area<T>;
+
+ x0(): number | ((d: T, i: number) => number);
+ x0(x0: number): Area<T>;
+ x0(x0: (d: T, i: number) => number): Area<T>;
+
+ x1(): number | ((d: T, i: number) => number);
+ x1(x1: number): Area<T>;
+ x1(x1: (d: T, i: number) => number): Area<T>;
+
+ y(): number | ((d: T, i: number) => number);
+ y(y: number): Area<T>;
+ y(y: (d: T, i: number) => number): Area<T>;
+
+ y0(): number | ((d: T, i: number) => number);
+ y0(y0: number): Area<T>;
+ y0(y0: (d: T, i: number) => number): Area<T>;
+
+ y1(): number | ((d: T, i: number) => number);
+ y1(y1: number): Area<T>;
+ y1(y1: (d: T, i: number) => number): Area<T>;
+
+ interpolate(): string | ((points: Array<[number, number]>) => string);
+ interpolate(interpolate: "linear"): Area<T>;
+ interpolate(interpolate: "step"): Area<T>;
+ interpolate(interpolate: "step-before"): Area<T>;
+ interpolate(interpolate: "step-after"): Area<T>;
+ interpolate(interpolate: "basis"): Area<T>;
+ interpolate(interpolate: "basis-open"): Area<T>;
+ interpolate(interpolate: "cardinal"): Area<T>;
+ interpolate(interpolate: "cardinal-open"): Area<T>;
+ interpolate(interpolate: "monotone"): Area<T>;
+ interpolate(interpolate: string): Area<T>;
+
+ tension(): number;
+ tension(tension: number): Area<T>;
+
+ defined(): (d: T, i: number) => boolean;
+ defined(defined: (d: T, i: number) => boolean): Area<T>;
+ }
+
+ module area {
+ export function radial(): Radial<[number, number]>;
+ export function radial<T>(): Radial<T>;
+
+ interface Radial<T> {
+ (data: T[]): string;
+
+ radius(): number | ((d: T, i: number) => number);
+ radius(radius: number): Radial<T>;
+ radius(radius: (d: T, i: number) => number): Radial<T>;
+
+ innerRadius(): number | ((d: T, i: number) => number);
+ innerRadius(innerRadius: number): Radial<T>;
+ innerRadius(innerRadius: (d: T, i: number) => number): Radial<T>;
+
+ outerRadius(): number | ((d: T, i: number) => number);
+ outerRadius(outerRadius: number): Radial<T>;
+ outerRadius(outerRadius: (d: T, i: number) => number): Radial<T>;
+
+ angle(): number | ((d: T, i: number) => number);
+ angle(angle: number): Radial<T>;
+ angle(angle: (d: T, i: number) => number): Radial<T>;
+
+ startAngle(): number | ((d: T, i: number) => number);
+ startAngle(startAngle: number): Radial<T>;
+ startAngle(startAngle: (d: T, i: number) => number): Radial<T>;
+
+ endAngle(): number | ((d: T, i: number) => number);
+ endAngle(endAngle: number): Radial<T>;
+ endAngle(endAngle: (d: T, i: number) => number): Radial<T>;
+
+ interpolate(): string | ((points: Array<[number, number]>) => string);
+ interpolate(interpolate: "linear"): Radial<T>;
+ interpolate(interpolate: "step"): Radial<T>;
+ interpolate(interpolate: "step-before"): Radial<T>;
+ interpolate(interpolate: "step-after"): Radial<T>;
+ interpolate(interpolate: "basis"): Radial<T>;
+ interpolate(interpolate: "basis-open"): Radial<T>;
+ interpolate(interpolate: "cardinal"): Radial<T>;
+ interpolate(interpolate: "cardinal-open"): Radial<T>;
+ interpolate(interpolate: "monotone"): Radial<T>;
+ interpolate(interpolate: string): Radial<T>;
+ interpolate(interpolate: (points: Array<[number, number]>) => string): Radial<T>;
+
+ tension(): number;
+ tension(tension: number): Radial<T>;
+
+ defined(): (d: T, i: number) => boolean;
+ defined(defined: (d: T, i: number) => boolean): Radial<T>;
+ }
+ }
+
+ export function arc(): Arc<arc.Arc>;
+ export function arc<T>(): Arc<T>;
+
+ module arc {
+ interface Arc {
+ innerRadius: number;
+ outerRadius: number;
+ startAngle: number;
+ endAngle: number;
+ padAngle: number
+ }
+ }
+
+ interface Arc<T> {
+ (d: T, i: number): string;
+
+ innerRadius(): (d: T, i: number) => number;
+ innerRadius(radius: number): Arc<T>;
+ innerRadius(radius: (d: T, i: number) => number): Arc<T>;
+
+ outerRadius(): (d: T, i: number) => number;
+ outerRadius(radius: number): Arc<T>;
+ outerRadius(radius: (d: T, i: number) => number): Arc<T>;
+
+ cornerRadius(): (d: T, i: number) => number;
+ cornerRadius(radius: number): Arc<T>;
+ cornerRadius(radius: (d: T, i: number) => number): Arc<T>;
+
+ padRadius(): string | ((d: T, i: number) => number);
+ padRadius(radius: "auto"): Arc<T>;
+ padRadius(radius: string): Arc<T>;
+ padRadius(radius: (d: T, i: number) => number): Arc<T>;
+
+ startAngle(): (d: T, i: number) => number;
+ startAngle(angle: number): Arc<T>;
+ startAngle(angle: (d: T, i: number) => number): Arc<T>;
+
+ endAngle(): (d: T, i: number) => number;
+ endAngle(angle: number): Arc<T>;
+ endAngle(angle: (d: T, i: number) => number): Arc<T>;
+
+ padAngle(): (d: T, i: number) => number;
+ padAngle(angle: number): Arc<T>;
+ padAngle(angle: (d: T, i: number) => number): Arc<T>;
+
+ centroid(d: T, i?: number): [number, number];
+ }
+
+ export function symbol(): Symbol<{}>;
+ export function symbol<T>(): Symbol<T>;
+
+ interface Symbol<T> {
+ (d: T, i?: number): string;
+
+ type(): (d: T, i: number) => string;
+ type(type: string): Symbol<T>;
+ type(type: (d: T, i: number) => string): Symbol<T>;
+
+ size(): (d: T, i: string) => number;
+ size(size: number): Symbol<T>;
+ size(size: (d: T, i: number) => number): Symbol<T>;
+ }
+
+ export var symbolTypes: string[];
+
+ export function chord(): Chord<chord.Link<chord.Node>, chord.Node>;
+ export function chord<Node>(): Chord<chord.Link<Node>, Node>;
+ export function chord<Link, Node>(): Chord<Link, Node>;
+
+ module chord {
+ interface Link<Node> {
+ source: Node;
+ target: Node;
+ }
+
+ interface Node {
+ radius: number;
+ startAngle: number;
+ endAngle: number
+ }
+ }
+
+ interface Chord<Link, Node> {
+ (d: Link, i: number): string;
+
+ source(): (d: Link, i: number) => Node;
+ source(source: Node): Chord<Link, Node>;
+ source(source: (d: Link, i: number) => Node): Chord<Link, Node>;
+
+ target(): (d: Link, i: number) => Node;
+ target(target: Node): Chord<Link, Node>;
+ target(target: (d: Link, i: number) => Node): Chord<Link, Node>;
+
+ radius(): (d: Node, i: number) => number;
+ radius(radius: number): Chord<Link, Node>;
+ radius(radius: (d: Node, i: number) => number): Chord<Link, Node>;
+
+ startAngle(): (d: Node, i: number) => number;
+ startAngle(angle: number): Chord<Link, Node>;
+ startAngle(angle: (d: Node, i: number) => number): Chord<Link, Node>;
+
+ endAngle(): (d: Node, i: number) => number;
+ endAngle(angle: number): Chord<Link, Node>;
+ endAngle(angle: (d: Node, i: number) => number): Chord<Link, Node>;
+ }
+
+ export function diagonal(): Diagonal<diagonal.Link<diagonal.Node>, diagonal.Node>;
+ export function diagonal<Node>(): Diagonal<diagonal.Link<Node>, Node>;
+ export function diagonal<Link, Node>(): Diagonal<Link, Node>;
+
+ module diagonal {
+ interface Link<Node> {
+ source: Node;
+ target: Node;
+ }
+
+ interface Node {
+ x: number;
+ y: number;
+ }
+ }
+
+ interface Diagonal<Link, Node> {
+ (d: Link, i: number): string;
+
+ source(): (d: Link, i: number) => Node;
+ source(source: Node): Diagonal<Link, Node>;
+ source(source: (d: Link, i: number) => Node): Diagonal<Link, Node>;
+
+ target(): (d: Link, i: number) => Node;
+ target(target: Node): Diagonal<Link, Node>;
+ target(target: (d: Link, i: number) => Node): Diagonal<Link, Node>;
+
+ projection(): (d: Node, i: number) => [number, number];
+ projection(projection: (d: Node, i: number) => [number, number]): Diagonal<Link, Node>;
+ }
+
+ module diagonal {
+ export function radial(): Radial<Link<Node>, Node>;
+ export function radial<Node>(): Radial<Link<Node>, Node>;
+ export function radial<Link, Node>(): Radial<Link, Node>;
+
+ interface Radial<Link, Node> {
+ (d: Link, i: number): string;
+
+ source(): (d: Link, i: number) => Node;
+ source(source: Node): Radial<Link, Node>;
+ source(source: (d: Link, i: number) => Node): Radial<Link, Node>;
+
+ target(): (d: Link, i: number) => Node;
+ target(target: Node): Radial<Link, Node>;
+ target(target: (d: Link, i: number) => Node): Radial<Link, Node>;
+
+ projection(): (d: Node, i: number) => [number, number];
+ projection(projection: (d: Node, i: number) => [number, number]): Radial<Link, Node>;
+ }
+ }
+
+ export function axis(): Axis;
+
+ interface Axis {
+ (selection: Selection<any>): void;
+ (selection: Transition<any>): void;
+
+ scale(): any;
+ scale(scale: any): Axis;
+
+ orient(): string;
+ orient(orientation: string): Axis;
+
+ ticks(): any[];
+ ticks(...args: any[]): Axis;
+
+ tickValues(): any[];
+ tickValues(values: any[]): Axis;
+
+ tickSize(): number;
+ tickSize(size: number): Axis;
+ tickSize(inner: number, outer: number): Axis;
+
+ innerTickSize(): number;
+ innerTickSize(size: number): Axis;
+
+ outerTickSize(): number;
+ outerTickSize(size: number): Axis;
+
+ tickPadding(): number;
+ tickPadding(padding: number): Axis;
+
+ tickFormat(): (t: any) => string;
+ tickFormat(format: (t: any) => string): Axis;
+ }
+
+ export function brush(): Brush<any>;
+ export function brush<T>(): Brush<T>;
+
+ module brush {
+ interface Scale {
+ domain(): number[];
+ domain(domain: number[]): Scale;
+
+ range(): number[];
+ range(range: number[]): Scale;
+
+ invert?(y: number): number;
+ }
+ }
+
+ interface Brush<T> {
+ (selection: Selection<T>): void;
+ (selection: Transition<T>): void;
+
+ event(selection: Selection<T>): void;
+ event(selection: Transition<T>): void;
+
+ x(): brush.Scale;
+ x(x: brush.Scale): Brush<T>;
+
+ y(): brush.Scale;
+ y(y: brush.Scale): Brush<T>;
+
+ extent(): [number, number] | [[number, number], [number, number]];
+ extent(extent: [number, number] | [[number, number], [number, number]]): Brush<T>;
+
+ clamp(): boolean | [boolean, boolean];
+ clamp(clamp: boolean | [boolean, boolean]): Brush<T>;
+
+ clear(): void;
+
+ empty(): boolean;
+
+ on(type: 'brushstart'): (datum: T, index: number) => void;
+ on(type: 'brush'): (datum: T, index: number) => void;
+ on(type: 'brushend'): (datum: T, index: number) => void;
+ on(type: string): (datum: T, index: number) => void;
+
+ on(type: 'brushstart', listener: (datum: T, index: number) => void): Brush<T>;
+ on(type: 'brush', listener: (datum: T, index: number) => void): Brush<T>;
+ on(type: 'brushend', listener: (datum: T, index: number) => void): Brush<T>;
+ on(type: string, listener: (datum: T, index: number) => void): Brush<T>;
+ }
+ }
+
+ export function xhr(url: string, mimeType?: string, callback?: (err: any, data: any) => void): Xhr;
+ export function xhr(url: string, callback: (err: any, data: any) => void): Xhr;
+
+ interface Xhr {
+ header(name: string): string;
+ header(name: string, value: string): Xhr;
+
+ mimeType(): string;
+ mimeType(type: string): Xhr;
+
+ responseType(): string;
+ responseType(type: string): Xhr;
+
+ response(): (request: XMLHttpRequest) => any;
+ response(value: (request: XMLHttpRequest) => any): Xhr;
+
+ get(callback?: (err: any, data: any) => void): Xhr;
+
+ post(data?: any, callback?: (err: any, data: any) => void): Xhr;
+ post(callback: (err: any, data: any) => void): Xhr;
+
+ send(method: string, data?: any, callback?: (err: any, data: any) => void): Xhr;
+ send(method: string, callback: (err: any, data: any) => void): Xhr;
+
+ abort(): Xhr;
+
+ on(type: "beforesend"): (request: XMLHttpRequest) => void;
+ on(type: "progress"): (request: XMLHttpRequest) => void;
+ on(type: "load"): (response: any) => void;
+ on(type: "error"): (err: any) => void;
+ on(type: string): (...args: any[]) => void;
+
+ on(type: "beforesend", listener: (request: XMLHttpRequest) => void): Xhr;
+ on(type: "progress", listener: (request: XMLHttpRequest) => void): Xhr;
+ on(type: "load", listener: (response: any) => void): Xhr;
+ on(type: "error", listener: (err: any) => void): Xhr;
+ on(type: string, listener: (...args: any[]) => void): Xhr;
+ }
+
+ export function text(url: string, mimeType?: string, callback?: (err: any, data: string) => void): Xhr;
+ export function text(url: string, callback: (err: any, data: string) => void): Xhr;
+
+ export function json(url: string, callback?: (err: any, data: any) => void): Xhr;
+
+ export function xml(url: string, mimeType?: string, callback?: (err: any, data: any) => void): Xhr;
+ export function xml(url: string, callback: (err: any, data: any) => void): Xhr;
+
+ export function html(url: string, callback?: (err: any, data: DocumentFragment) => void): Xhr;
+
+ export var csv: Dsv;
+ export var tsv: Dsv;
+ export function dsv(delimiter: string, mimeType: string): Dsv;
+
+ interface Dsv {
+ (url: string, callback: (rows: { [key: string]: string }[]) => void): DsvXhr<{ [key: string]: string }>;
+ (url: string, callback: (error: any, rows: { [key: string]: string }[]) => void): DsvXhr<{ [key: string]: string }>;
+ (url: string): DsvXhr<{ [key: string]: string }>;
+ <T>(url: string, accessor: (row: { [key: string]: string }) => T, callback: (rows: T[]) => void): DsvXhr<T>;
+ <T>(url: string, accessor: (row: { [key: string]: string }) => T, callback: (error: any, rows: T[]) => void): DsvXhr<T>;
+ <T>(url: string, accessor: (row: { [key: string]: string }) => T): DsvXhr<T>;
+
+ parse(string: string): { [key: string]: string }[];
+ parse<T>(string: string, accessor: (row: { [key: string]: string }, index: number) => T): T[];
+
+ parseRows(string: string): string[][];
+ parseRows<T>(string: string, accessor: (row: string[], index: number) => T): T[];
+
+ format(rows: Object[]): string;
+
+ formatRows(rows: string[][]): string;
+ }
+
+ interface DsvXhr<T> extends Xhr {
+ row(): (row: { [key: string]: string }) => T;
+ row<U>(accessor: (row: { [key: string]: string }) => U): DsvXhr<U>;
+
+ header(name: string): string;
+ header(name: string, value: string): DsvXhr<T>;
+
+ mimeType(): string;
+ mimeType(type: string): DsvXhr<T>;
+
+ responseType(): string;
+ responseType(type: string): DsvXhr<T>;
+
+ response(): (request: XMLHttpRequest) => any;
+ response(value: (request: XMLHttpRequest) => any): DsvXhr<T>;
+
+ get(callback?: (err: any, data: T) => void): DsvXhr<T>;
+ post(data?: any, callback?: (err: any, data: T) => void): DsvXhr<T>;
+ post(callback: (err: any, data: T) => void): DsvXhr<T>;
+
+ send(method: string, data?: any, callback?: (err: any, data: T) => void): DsvXhr<T>;
+ send(method: string, callback: (err: any, data: T) => void): DsvXhr<T>;
+
+ abort(): DsvXhr<T>;
+
+ on(type: "beforesend"): (request: XMLHttpRequest) => void;
+ on(type: "progress"): (request: XMLHttpRequest) => void;
+ on(type: "load"): (response: T) => void;
+ on(type: "error"): (err: any) => void;
+ on(type: string): (...args: any[]) => void;
+
+ on(type: "beforesend", listener: (request: XMLHttpRequest) => void): DsvXhr<T>;
+ on(type: "progress", listener: (request: XMLHttpRequest) => void): DsvXhr<T>;
+ on(type: "load", listener: (response: T) => void): DsvXhr<T>;
+ on(type: "error", listener: (err: any) => void): DsvXhr<T>;
+ on(type: string, listener: (...args: any[]) => void): DsvXhr<T>;
+ }
+
+ export function locale(definition: LocaleDefinition): Locale;
+
+ interface LocaleDefinition {
+ decimal: string;
+ thousands: string;
+ grouping: number[];
+ currency: [string, string];
+ dateTime: string;
+ date: string;
+ time: string;
+ periods: [string, string];
+ days: [string, string, string, string, string, string, string];
+ shortDays: [string, string, string, string, string, string, string];
+ months: [string, string, string, string, string, string, string, string, string, string, string, string];
+ shortMonths: [string, string, string, string, string, string, string, string, string, string, string, string];
+ }
+
+ interface Locale {
+ numberFormat(specifier: string): (n: number) => string;
+ timeFormat: {
+ (specifier: string): time.Format;
+ utc(specifier: string): time.Format;
+ }
+ }
+
+ module layout {
+ export function bundle(): Bundle<bundle.Node>;
+ export function bundle<T extends bundle.Node>(): Bundle<T>
+
+ module bundle {
+ interface Node {
+ parent: Node;
+ }
+
+ interface Link<T extends Node> {
+ source: T;
+ target: T;
+ }
+ }
+
+ interface Bundle<T extends bundle.Node> {
+ (links: bundle.Link<T>[]): T[][];
+ }
+
+ export function chord(): Chord;
+
+ module chord {
+ interface Link {
+ source: Node;
+ target: Node;
+ }
+
+ interface Node {
+ index: number;
+ subindex: number;
+ startAngle: number;
+ endAngle: number;
+ value: number;
+ }
+
+ interface Group {
+ index: number;
+ startAngle: number;
+ endAngle: number;
+ value: number;
+ }
+ }
+
+ interface Chord {
+ matrix(): number[][];
+ matrix(matrix: number[][]): Chord;
+
+ padding(): number;
+ padding(padding: number): Chord;
+
+ sortGroups(): (a: number, b: number) => number;
+ sortGroups(comparator: (a: number, b: number) => number): Chord;
+
+ sortSubgroups(): (a: number, b: number) => number;
+ sortSubgroups(comparator: (a: number, b: number) => number): Chord;
+
+ sortChords(): (a: number, b: number) => number;
+ sortChords(comparator: (a: number, b: number) => number): Chord;
+
+ chords(): chord.Link[];
+ groups(): chord.Group[];
+ }
+
+ export function cluster(): Cluster<cluster.Result>;
+ export function cluster<T extends cluster.Result>(): Cluster<T>;
+
+ module cluster {
+ interface Result {
+ parent?: Result;
+ children?: Result[];
+ depth?: number;
+ x?: number;
+ y?: number;
+ }
+
+ interface Link<T extends Result> {
+ source: T;
+ target: T;
+ }
+ }
+
+ interface Cluster<T extends cluster.Result> {
+ (root: T): T[];
+
+ nodes(root: T): T[];
+
+ links(nodes: T[]): cluster.Link<T>;
+
+ children(): (node: T) => T[];
+ children(accessor: (node: T) => T[]): Cluster<T>;
+
+ sort(): (a: T, b: T) => number;
+ sort(comparator: (a: T, b: T) => number): Cluster<T>;
+
+ separation(): (a: T, b: T) => number;
+ separation(separation: (a: T, b: T) => number): Cluster<T>;
+
+ size(): [number, number];
+ size(size: [number, number]): Cluster<T>;
+
+ nodeSize(): [number, number];
+ nodeSize(nodeSize: [number, number]): Cluster<T>;
+
+ value(): (a: T) => number;
+ value(value: (a: T) => number): Cluster<T>;
+ }
+
+ export function force(): Force<force.Link<force.Node>, force.Node>;
+ export function force<Node extends force.Node>(): Force<force.Link<Node>, Node>;
+ export function force<Link extends force.Link<force.Node>, Node extends force.Node>(): Force<Link, Node>;
+
+ module force {
+ interface Link<T extends Node> {
+ source: T;
+ target: T;
+ }
+
+ interface Node {
+ index?: number;
+ x?: number;
+ y?: number;
+ px?: number;
+ py?: number;
+ fixed?: boolean;
+ weight?: number;
+ }
+
+ interface Event {
+ type: string;
+ alpha: number;
+ }
+ }
+
+ interface Force<Link extends force.Link<force.Node>, Node extends force.Node> {
+ size(): [number, number];
+ size(size: [number, number]): Force<Link, Node>;
+
+ linkDistance(): number | ((link: Link, index: number) => number);
+ linkDistance(distance: number): Force<Link, Node>;
+ linkDistance(distance: (link: Link, index: number) => number): Force<Link, Node>;
+
+ linkStrength(): number | ((link: Link, index: number) => number);
+ linkStrength(strength: number): Force<Link, Node>;
+ linkStrength(strength: (link: Link, index: number) => number): Force<Link, Node>;
+
+ friction(): number;
+ friction(friction: number): Force<Link, Node>;
+
+ charge(): number | ((node: Node, index: number) => number);
+ charge(charge: number): Force<Link, Node>;
+ charge(charge: (node: Node, index: number) => number): Force<Link, Node>;
+
+ chargeDistance(): number;
+ chargeDistance(distance: number): Force<Link, Node>;
+
+ theta(): number;
+ theta(theta: number): Force<Link, Node>;
+
+ gravity(): number;
+ gravity(gravity: number): Force<Link, Node>;
+
+ nodes(): Node[];
+ nodes(nodes: Node[]): Force<Link, Node>;
+
+ links(): Link[];
+ links(links: { source: number; target: number }[]): Force<Link, Node>;
+ links(links: Link[]): Force<Link, Node>;
+
+ start(): Force<Link, Node>;
+
+ alpha(): number;
+ alpha(value: number): Force<Link, Node>;
+
+ resume(): Force<Link, Node>;
+
+ stop(): Force<Link, Node>;
+
+ on(type: string): (event: force.Event) => void;
+ on(type: string, listener: (event: force.Event) => void): Force<Link, Node>;
+
+ drag(): behavior.Drag<Node>;
+ drag(selection: Selection<Node>): void;
+ }
+
+ export function hierarchy(): Hierarchy<hierarchy.Result>;
+ export function hierarchy<T extends hierarchy.Result>(): Hierarchy<T>;
+
+ module hierarchy {
+ interface Result {
+ parent?: Result;
+ children?: Result[];
+ value?: number;
+ depth?: number;
+ }
+ }
+
+ interface Hierarchy<T extends hierarchy.Result> {
+ (root: T): T[];
+
+ children(): (node: T) => T[];
+ children(accessor: (node: T) => T[]): Hierarchy<T>;
+
+ sort(): (a: T, b: T) => number;
+ sort(comparator: (a: T, b: T) => number): Hierarchy<T>;
+
+ value(): (node: T) => number;
+ value(accessor: (node: T) => number): Hierarchy<T>;
+
+ revalue(root: T): T[];
+ }
+
+ export function histogram(): Histogram<number>;
+ export function histogram<T>(): Histogram<T>;
+
+ module histogram {
+ interface Bin<T> extends Array<T> {
+ x: number;
+ dx: number;
+ y: number;
+ }
+ }
+
+ interface Histogram<T> {
+ (values: T[], index?: number): histogram.Bin<T>[];
+
+ value(): (datum: T, index: number) => number;
+ value(value: (datum: T, index: number) => number): Histogram<T>;
+
+ range(): (values: T[], index: number) => [number, number];
+ range(range: (values: T[], index: number) => [number, number]): Histogram<T>;
+
+ bins(): (range: [number, number], values: T[], index: number) => number[];
+ bins(count: number): Histogram<T>;
+ bins(thresholds: number[]): Histogram<T>;
+ bins(func: (range: [number, number], values: T[], index: number) => number[]): Histogram<T>;
+
+ frequency(): boolean;
+ frequency(frequency: boolean): Histogram<T>;
+ }
+
+ export function pack(): Pack<pack.Node>;
+ export function pack<T extends pack.Node>(): Pack<T>;
+
+ module pack {
+ interface Node {
+ parent?: Node;
+ children?: Node[];
+ value?: number;
+ depth?: number;
+ x?: number;
+ y?: number;
+ r?: number;
+ }
+
+ interface Link<T extends Node> {
+ source: Node;
+ target: Node;
+ }
+ }
+
+ interface Pack<T extends pack.Node> {
+ (root: T): T[];
+
+ nodes(root: T): T[];
+
+ links(nodes: T[]): pack.Link<T>[];
+
+ children(): (node: T, depth: number) => T[];
+ children(children: (node: T, depth: number) => T[]): Pack<T>;
+
+ sort(): (a: T, b: T) => number;
+ sort(comparator: (a: T, b: T) => number): Pack<T>;
+
+ value(): (node: T) => number;
+ value(value: (node: T) => number): Pack<T>;
+
+ size(): [number, number];
+ size(size: [number, number]): Pack<T>;
+
+ radius(): number | ((node: T) => number);
+ radius(radius: number): Pack<T>;
+ radius(radius: (node: T) => number): Pack<T>;
+
+ padding(): number;
+ padding(padding: number): Pack<T>;
+ }
+
+ export function pie(): Pie<number>;
+ export function pie<T>(): Pie<T>;
+
+ module pie {
+ interface Arc<T> {
+ value: number;
+ startAngle: number;
+ endAngle: number;
+ padAngle: number;
+ data: T;
+ }
+ }
+
+ interface Pie<T> {
+ (data: T[], index?: number): pie.Arc<T>[];
+
+ value(): (datum: T, index: number) => number;
+ value(accessor: (datum: T, index: number) => number): Pie<T>;
+
+ sort(): (a: T, b: T) => number;
+ sort(comparator: (a: T, b: T) => number): Pie<T>;
+
+ startAngle(): number | ((data: T[], index: number) => number);
+ startAngle(angle: number): Pie<T>;
+ startAngle(angle: (data: T[], index: number) => number): Pie<T>;
+
+ endAngle(): number | ((data: T[], index: number) => number);
+ endAngle(angle: number): Pie<T>;
+ endAngle(angle: (data: T[], index: number) => number): Pie<T>;
+
+ padAngle(): number | ((data: T[], index: number) => number);
+ padAngle(angle: number): Pie<T>;
+ padAngle(angle: (data: T[], index: number) => number): Pie<T>;
+ }
+
+ export function stack(): Stack<stack.Value[], stack.Value>;
+ export function stack<Value>(): Stack<Value[], Value>;
+ export function stack<Series, Value>(): Stack<Series, Value>;
+ module stack {
+ interface Value {
+ x: number;
+ y: number;
+ y0?: number;
+ }
+ }
+
+ interface Stack<Series, Value> {
+ (layers: Series[], index?: number): Series[];
+
+ values(): (layer: Series, index: number) => Value[];
+ values(accessor: (layer: Series, index: number) => Value[]): Stack<Series, Value>;
+
+ offset(): (data: Array<[number, number]>) => number[];
+ offset(offset: "silhouette"): Stack<Series, Value>;
+ offset(offset: "wiggle"): Stack<Series, Value>;
+ offset(offset: "expand"): Stack<Series, Value>;
+ offset(offset: "zero"): Stack<Series, Value>;
+ offset(offset: string): Stack<Series, Value>;
+ offset(offset: (data: Array<[number, number]>) => number[]): Stack<Series, Value>;
+
+ order(): (data: Array<[number, number]>) => number[];
+ order(order: "inside-out"): Stack<Series, Value>;
+ order(order: "reverse"): Stack<Series, Value>;
+ order(order: "default"): Stack<Series, Value>;
+ order(order: string): Stack<Series, Value>;
+ order(order: (data: Array<[number, number]>) => number[]): Stack<Series, Value>;
+
+ x(): (value: Value, index: number) => number;
+ x(accessor: (value: Value, index: number) => number): Stack<Series, Value>;
+
+ y(): (value: Value, index: number) => number;
+ y(accesor: (value: Value, index: number) => number): Stack<Series, Value>;
+
+ out(): (value: Value, y0: number, y: number) => void;
+ out(setter: (value: Value, y0: number, y: number) => void): Stack<Series, Value>;
+ }
+
+ export function tree(): Tree<tree.Node>;
+ export function tree<T extends tree.Node>(): Tree<T>;
+
+ module tree {
+ interface Link<T extends Node> {
+ source: T;
+ target: T;
+ }
+
+ interface Node {
+ parent?: Node;
+ children?: Node[];
+ depth?: number;
+ x?: number;
+ y?: number;
+ }
+ }
+
+ interface Tree<T> {
+ (root: T, index?: number): T[];
+
+ nodes(root: T, index?: number): T[];
+
+ links(nodes: T[]): tree.Link<T>[];
+
+ children(): (datum: T, index: number) => T[];
+ children(children: (datum: T, index: number) => T[]): Tree<T>;
+
+ separation(): (a: T, b: T) => number;
+ separation(separation: (a: T, b: T) => number): Tree<T>;
+
+ size(): [number, number];
+ size(size: [number, number]): Tree<T>;
+
+ nodeSize(): [number, number];
+ nodeSize(size: [number, number]): Tree<T>;
+
+ sort(): (a: T, b: T) => number;
+ sort(comparator: (a: T, b: T) => number): Tree<T>;
+
+ value(): (datum: T, index: number) => number;
+ value(value: (datum: T, index: number) => number): Tree<T>;
+ }
+
+ export function treemap(): Treemap<treemap.Node>;
+ export function treemap<T extends treemap.Node>(): Treemap<T>;
+
+ module treemap {
+ interface Node {
+ parent?: Node;
+ children?: Node[];
+ value?: number;
+ depth?: number;
+ x?: number;
+ y?: number;
+ dx?: number;
+ dy?: number;
+ }
+
+ interface Link<T extends Node> {
+ source: T;
+ target: T;
+ }
+
+ type Padding = number | [number, number, number, number];
+ }
+
+ interface Treemap<T extends treemap.Node> {
+ (root: T, index?: number): T[];
+
+ nodes(root: T, index?: number): T[];
+
+ links(nodes: T[]): treemap.Link<T>[];
+
+ children(): (node: T, depth: number) => T[];
+ children(children: (node: T, depth: number) => T[]): Treemap<T>;
+
+ sort(): (a: T, b: T) => number;
+ sort(comparator: (a: T, b: T) => number): Treemap<T>;
+
+ value(): (node: T, index: number) => number;
+ value(value: (node: T, index: number) => number): Treemap<T>;
+
+ size(): [number, number];
+ size(size: [number, number]): Treemap<T>;
+
+ padding(): (node: T, depth: number) => treemap.Padding;
+ padding(padding: treemap.Padding): Treemap<T>;
+ padding(padding: (node: T, depth: number) => treemap.Padding): Treemap<T>;
+
+ round(): boolean;
+ round(round: boolean): Treemap<T>;
+
+ sticky(): boolean;
+ sticky(sticky: boolean): boolean;
+
+ mode(): string;
+ mode(mode: "squarify"): Treemap<T>;
+ mode(mode: "slice"): Treemap<T>;
+ mode(mode: "dice"): Treemap<T>;
+ mode(mode: "slice-dice"): Treemap<T>;
+ mode(mode: string): Treemap<T>;
+
+ ratio(): number;
+ ratio(ratio: number): Treemap<T>;
+ }
+ }
+
+ module geom {
+ export function voronoi(): Voronoi<[number, number]>;
+ export function voronoi<T>(): Voronoi<T>;
+
+ module voronoi {
+ interface Link<T> {
+ source: T;
+ target: T;
+ }
+ }
+
+ interface Voronoi<T> {
+ (data: T[]): Array<[number, number]>;
+
+ x(): (vertex: T) => number;
+ x(x: (vertex: T) => number): Voronoi<T>;
+
+ y(): (vertex: T) => number;
+ y(y: (vertex: T) => number): Voronoi<T>;
+
+ clipExtent(): [[number, number], [number, number]];
+ clipExtent(extent: [[number, number], [number, number]]): Voronoi<T>;
+
+ links(data: T[]): voronoi.Link<T>[];
+
+ triangles(data: T[]): Array<[T, T, T]>;
+ }
+
+ /**
+ * @deprecated use d3.geom.voronoi().triangles() instead
+ */
+ export function delaunay(vertices: Array<[number, number]>): Array<[[number, number], [number, number], [number, number]]>;
+
+ export function quadtree(): Quadtree<[number, number]>;
+ export function quadtree<T>(): Quadtree<T>;
+
+ module quadtree {
+ interface Node<T> {
+ nodes: [Node<T>, Node<T>, Node<T>, Node<T>];
+ leaf: boolean;
+ point: T;
+ x: number;
+ y: number;
+ }
+
+ interface Quadtree<T> extends Node<T> {
+ add(point: T): void;
+ visit(callback: (node: Node<T>, x1: number, y1: number, x2: number, y2: number) => boolean | void): void;
+ find(point: [number, number]): T;
+ }
+ }
+
+ interface Quadtree<T> {
+ (points: T[]): quadtree.Quadtree<T>;
+
+ x(): (datum: T, index: number) => number;
+ x(x: number): Quadtree<T>;
+ x(x: (datum: T, index: number) => number): Quadtree<T>;
+
+ y(): (datum: T, index: number) => number;
+ y(y: number): Quadtree<T>;
+ y(y: (datum: T, index: number) => number): Quadtree<T>;
+
+ extent(): [[number, number], [number, number]];
+ extent(extent: [[number, number], [number, number]]): Quadtree<T>;
+ }
+
+ export function hull(vertices: Array<[number, number]>): Array<[number, number]>;
+ export function hull(): Hull<[number, number]>;
+ export function hull<T>(): Hull<T>;
+
+ interface Hull<T> {
+ (vertices: T[]): Array<[number, number]>;
+
+ x(): (datum: T) => number;
+ x(x: (datum: T) => number): Hull<T>;
+
+ y(): (datum: T) => number;
+ y(y: (datum: T) => number): Hull<T>;
+ }
+
+ export function polygon(vertices: Array<[number, number]>): Polygon;
+
+ interface Polygon {
+ area(): number;
+
+ centroid(): [number, number];
+
+ clip(subject: Array<[number, number]>): Array<[number, number]>;
+ }
+ }
+}
+
+// we need this to exist
+interface TouchList { }
+
+declare module 'd3' {
+ export = d3;
+}
diff --git a/catalog-ui/typings/jasmine/jasmine.d.ts b/catalog-ui/typings/jasmine/jasmine.d.ts
new file mode 100644
index 0000000000..99ccb91fed
--- /dev/null
+++ b/catalog-ui/typings/jasmine/jasmine.d.ts
@@ -0,0 +1,515 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for Jasmine 2.2
+// Project: http://jasmine.github.io/
+// Definitions by: Boris Yankov <https://github.com/borisyankov/>, Theodore Brown <https://github.com/theodorejb>, David Pärsson <https://github.com/davidparsson/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+
+// For ddescribe / iit use : https://github.com/borisyankov/DefinitelyTyped/blob/master/karma-jasmine/karma-jasmine.d.ts
+
+declare function describe(description: string, specDefinitions: () => void): void;
+declare function fdescribe(description: string, specDefinitions: () => void): void;
+declare function xdescribe(description: string, specDefinitions: () => void): void;
+
+declare function it(expectation: string, assertion?: () => void, timeout?: number): void;
+declare function it(expectation: string, assertion?: (done: () => void) => void, timeout?: number): void;
+declare function fit(expectation: string, assertion?: () => void, timeout?: number): void;
+declare function fit(expectation: string, assertion?: (done: () => void) => void, timeout?: number): void;
+declare function xit(expectation: string, assertion?: () => void, timeout?: number): void;
+declare function xit(expectation: string, assertion?: (done: () => void) => void, timeout?: number): void;
+
+/** If you call the function pending anywhere in the spec body, no matter the expectations, the spec will be marked pending. */
+declare function pending(reason?: string): void;
+
+declare function beforeEach(action: () => void, timeout?: number): void;
+declare function beforeEach(action: (done: () => void) => void, timeout?: number): void;
+declare function afterEach(action: () => void, timeout?: number): void;
+declare function afterEach(action: (done: () => void) => void, timeout?: number): void;
+
+declare function beforeAll(action: () => void, timeout?: number): void;
+declare function beforeAll(action: (done: () => void) => void, timeout?: number): void;
+declare function afterAll(action: () => void, timeout?: number): void;
+declare function afterAll(action: (done: () => void) => void, timeout?: number): void;
+
+declare function expect(spy: Function): jasmine.Matchers;
+declare function expect(actual: any): jasmine.Matchers;
+
+declare function fail(e?: any): void;
+
+declare function spyOn(object: any, method: string): jasmine.Spy;
+
+declare function runs(asyncMethod: Function): void;
+declare function waitsFor(latchMethod: () => boolean, failureMessage?: string, timeout?: number): void;
+declare function waits(timeout?: number): void;
+
+declare module jasmine {
+
+ var clock: () => Clock;
+
+ function any(aclass: any): Any;
+ function anything(): Any;
+ function arrayContaining(sample: any[]): ArrayContaining;
+ function objectContaining(sample: any): ObjectContaining;
+ function createSpy(name: string, originalFn?: Function): Spy;
+ function createSpyObj(baseName: string, methodNames: any[]): any;
+ function createSpyObj<T>(baseName: string, methodNames: any[]): T;
+ function pp(value: any): string;
+ function getEnv(): Env;
+ function addCustomEqualityTester(equalityTester: CustomEqualityTester): void;
+ function addMatchers(matchers: CustomMatcherFactories): void;
+ function stringMatching(str: string): Any;
+ function stringMatching(str: RegExp): Any;
+
+ interface Any {
+
+ new (expectedClass: any): any;
+
+ jasmineMatches(other: any): boolean;
+ jasmineToString(): string;
+ }
+
+ // taken from TypeScript lib.core.es6.d.ts, applicable to CustomMatchers.contains()
+ interface ArrayLike<T> {
+ length: number;
+ [n: number]: T;
+ }
+
+ interface ArrayContaining {
+ new (sample: any[]): any;
+
+ asymmetricMatch(other: any): boolean;
+ jasmineToString(): string;
+ }
+
+ interface ObjectContaining {
+ new (sample: any): any;
+
+ jasmineMatches(other: any, mismatchKeys: any[], mismatchValues: any[]): boolean;
+ jasmineToString(): string;
+ }
+
+ interface Block {
+
+ new (env: Env, func: SpecFunction, spec: Spec): any;
+
+ execute(onComplete: () => void): void;
+ }
+
+ interface WaitsBlock extends Block {
+ new (env: Env, timeout: number, spec: Spec): any;
+ }
+
+ interface WaitsForBlock extends Block {
+ new (env: Env, timeout: number, latchFunction: SpecFunction, message: string, spec: Spec): any;
+ }
+
+ interface Clock {
+ install(): void;
+ uninstall(): void;
+ /** Calls to any registered callback are triggered when the clock is ticked forward via the jasmine.clock().tick function, which takes a number of milliseconds. */
+ tick(ms: number): void;
+ mockDate(date?: Date): void;
+ }
+
+ interface CustomEqualityTester {
+ (first: any, second: any): boolean;
+ }
+
+ interface CustomMatcher {
+ compare<T>(actual: T, expected: T): CustomMatcherResult;
+ compare(actual: any, expected: any): CustomMatcherResult;
+ }
+
+ interface CustomMatcherFactory {
+ (util: MatchersUtil, customEqualityTesters: Array<CustomEqualityTester>): CustomMatcher;
+ }
+
+ interface CustomMatcherFactories {
+ [index: string]: CustomMatcherFactory;
+ }
+
+ interface CustomMatcherResult {
+ pass: boolean;
+ message: string;
+ }
+
+ interface MatchersUtil {
+ equals(a: any, b: any, customTesters?: Array<CustomEqualityTester>): boolean;
+ contains<T>(haystack: ArrayLike<T> | string, needle: any, customTesters?: Array<CustomEqualityTester>): boolean;
+ buildFailureMessage(matcherName: string, isNot: boolean, actual: any, ...expected: Array<any>): string;
+ }
+
+ interface Env {
+ setTimeout: any;
+ clearTimeout: void;
+ setInterval: any;
+ clearInterval: void;
+ updateInterval: number;
+
+ currentSpec: Spec;
+
+ matchersClass: Matchers;
+
+ version(): any;
+ versionString(): string;
+ nextSpecId(): number;
+ addReporter(reporter: Reporter): void;
+ execute(): void;
+ describe(description: string, specDefinitions: () => void): Suite;
+ // ddescribe(description: string, specDefinitions: () => void): Suite; Not a part of jasmine. Angular team adds these
+ beforeEach(beforeEachFunction: () => void): void;
+ beforeAll(beforeAllFunction: () => void): void;
+ currentRunner(): Runner;
+ afterEach(afterEachFunction: () => void): void;
+ afterAll(afterAllFunction: () => void): void;
+ xdescribe(desc: string, specDefinitions: () => void): XSuite;
+ it(description: string, func: () => void): Spec;
+ // iit(description: string, func: () => void): Spec; Not a part of jasmine. Angular team adds these
+ xit(desc: string, func: () => void): XSpec;
+ compareRegExps_(a: RegExp, b: RegExp, mismatchKeys: string[], mismatchValues: string[]): boolean;
+ compareObjects_(a: any, b: any, mismatchKeys: string[], mismatchValues: string[]): boolean;
+ equals_(a: any, b: any, mismatchKeys: string[], mismatchValues: string[]): boolean;
+ contains_(haystack: any, needle: any): boolean;
+ addCustomEqualityTester(equalityTester: CustomEqualityTester): void;
+ addMatchers(matchers: CustomMatcherFactories): void;
+ specFilter(spec: Spec): boolean;
+ }
+
+ interface FakeTimer {
+
+ new (): any;
+
+ reset(): void;
+ tick(millis: number): void;
+ runFunctionsWithinRange(oldMillis: number, nowMillis: number): void;
+ scheduleFunction(timeoutKey: any, funcToCall: () => void, millis: number, recurring: boolean): void;
+ }
+
+ interface HtmlReporter {
+ new (): any;
+ }
+
+ interface HtmlSpecFilter {
+ new (): any;
+ }
+
+ interface Result {
+ type: string;
+ }
+
+ interface NestedResults extends Result {
+ description: string;
+
+ totalCount: number;
+ passedCount: number;
+ failedCount: number;
+
+ skipped: boolean;
+
+ rollupCounts(result: NestedResults): void;
+ log(values: any): void;
+ getItems(): Result[];
+ addResult(result: Result): void;
+ passed(): boolean;
+ }
+
+ interface MessageResult extends Result {
+ values: any;
+ trace: Trace;
+ }
+
+ interface ExpectationResult extends Result {
+ matcherName: string;
+ passed(): boolean;
+ expected: any;
+ actual: any;
+ message: string;
+ trace: Trace;
+ }
+
+ interface Trace {
+ name: string;
+ message: string;
+ stack: any;
+ }
+
+ interface PrettyPrinter {
+
+ new (): any;
+
+ format(value: any): void;
+ iterateObject(obj: any, fn: (property: string, isGetter: boolean) => void): void;
+ emitScalar(value: any): void;
+ emitString(value: string): void;
+ emitArray(array: any[]): void;
+ emitObject(obj: any): void;
+ append(value: any): void;
+ }
+
+ interface StringPrettyPrinter extends PrettyPrinter {
+ }
+
+ interface Queue {
+
+ new (env: any): any;
+
+ env: Env;
+ ensured: boolean[];
+ blocks: Block[];
+ running: boolean;
+ index: number;
+ offset: number;
+ abort: boolean;
+
+ addBefore(block: Block, ensure?: boolean): void;
+ add(block: any, ensure?: boolean): void;
+ insertNext(block: any, ensure?: boolean): void;
+ start(onComplete?: () => void): void;
+ isRunning(): boolean;
+ next_(): void;
+ results(): NestedResults;
+ }
+
+ interface Matchers {
+
+ new (env: Env, actual: any, spec: Env, isNot?: boolean): any;
+
+ env: Env;
+ actual: any;
+ spec: Env;
+ isNot?: boolean;
+ message(): any;
+
+ toBe(expected: any, expectationFailOutput?: any): boolean;
+ toEqual(expected: any, expectationFailOutput?: any): boolean;
+ toMatch(expected: any, expectationFailOutput?: any): boolean;
+ toBeDefined(expectationFailOutput?: any): boolean;
+ toBeUndefined(expectationFailOutput?: any): boolean;
+ toBeNull(expectationFailOutput?: any): boolean;
+ toBeNaN(): boolean;
+ toBeTruthy(expectationFailOutput?: any): boolean;
+ toBeFalsy(expectationFailOutput?: any): boolean;
+ toHaveBeenCalled(): boolean;
+ toHaveBeenCalledWith(...params: any[]): boolean;
+ toContain(expected: any, expectationFailOutput?: any): boolean;
+ toBeLessThan(expected: any, expectationFailOutput?: any): boolean;
+ toBeGreaterThan(expected: any, expectationFailOutput?: any): boolean;
+ toBeCloseTo(expected: any, precision: any, expectationFailOutput?: any): boolean;
+ toContainHtml(expected: string): boolean;
+ toContainText(expected: string): boolean;
+ toThrow(expected?: any): boolean;
+ toThrowError(expected?: any, message?: string): boolean;
+ not: Matchers;
+
+ Any: Any;
+ }
+
+ interface Reporter {
+ reportRunnerStarting(runner: Runner): void;
+ reportRunnerResults(runner: Runner): void;
+ reportSuiteResults(suite: Suite): void;
+ reportSpecStarting(spec: Spec): void;
+ reportSpecResults(spec: Spec): void;
+ log(str: string): void;
+ }
+
+ interface MultiReporter extends Reporter {
+ addReporter(reporter: Reporter): void;
+ }
+
+ interface Runner {
+
+ new (env: Env): any;
+
+ execute(): void;
+ beforeEach(beforeEachFunction: SpecFunction): void;
+ afterEach(afterEachFunction: SpecFunction): void;
+ beforeAll(beforeAllFunction: SpecFunction): void;
+ afterAll(afterAllFunction: SpecFunction): void;
+ finishCallback(): void;
+ addSuite(suite: Suite): void;
+ add(block: Block): void;
+ specs(): Spec[];
+ suites(): Suite[];
+ topLevelSuites(): Suite[];
+ results(): NestedResults;
+ }
+
+ interface SpecFunction {
+ (spec?: Spec): void;
+ }
+
+ interface SuiteOrSpec {
+ id: number;
+ env: Env;
+ description: string;
+ queue: Queue;
+ }
+
+ interface Spec extends SuiteOrSpec {
+
+ new (env: Env, suite: Suite, description: string): any;
+
+ suite: Suite;
+
+ afterCallbacks: SpecFunction[];
+ spies_: Spy[];
+
+ results_: NestedResults;
+ matchersClass: Matchers;
+
+ getFullName(): string;
+ results(): NestedResults;
+ log(arguments: any): any;
+ runs(func: SpecFunction): Spec;
+ addToQueue(block: Block): void;
+ addMatcherResult(result: Result): void;
+ expect(actual: any): any;
+ waits(timeout: number): Spec;
+ waitsFor(latchFunction: SpecFunction, timeoutMessage?: string, timeout?: number): Spec;
+ fail(e?: any): void;
+ getMatchersClass_(): Matchers;
+ addMatchers(matchersPrototype: CustomMatcherFactories): void;
+ finishCallback(): void;
+ finish(onComplete?: () => void): void;
+ after(doAfter: SpecFunction): void;
+ execute(onComplete?: () => void): any;
+ addBeforesAndAftersToQueue(): void;
+ explodes(): void;
+ spyOn(obj: any, methodName: string, ignoreMethodDoesntExist: boolean): Spy;
+ removeAllSpies(): void;
+ }
+
+ interface XSpec {
+ id: number;
+ runs(): void;
+ }
+
+ interface Suite extends SuiteOrSpec {
+
+ new (env: Env, description: string, specDefinitions: () => void, parentSuite: Suite): any;
+
+ parentSuite: Suite;
+
+ getFullName(): string;
+ finish(onComplete?: () => void): void;
+ beforeEach(beforeEachFunction: SpecFunction): void;
+ afterEach(afterEachFunction: SpecFunction): void;
+ beforeAll(beforeAllFunction: SpecFunction): void;
+ afterAll(afterAllFunction: SpecFunction): void;
+ results(): NestedResults;
+ add(suiteOrSpec: SuiteOrSpec): void;
+ specs(): Spec[];
+ suites(): Suite[];
+ children(): any[];
+ execute(onComplete?: () => void): void;
+ }
+
+ interface XSuite {
+ execute(): void;
+ }
+
+ interface Spy {
+ (...params: any[]): any;
+
+ identity: string;
+ and: SpyAnd;
+ calls: Calls;
+ mostRecentCall: { args: any[]; };
+ argsForCall: any[];
+ wasCalled: boolean;
+ }
+
+ interface SpyAnd {
+ /** By chaining the spy with and.callThrough, the spy will still track all calls to it but in addition it will delegate to the actual implementation. */
+ callThrough(): Spy;
+ /** By chaining the spy with and.returnValue, all calls to the function will return a specific value. */
+ returnValue(val: any): void;
+ /** By chaining the spy with and.callFake, all calls to the spy will delegate to the supplied function. */
+ callFake(fn: Function): Spy;
+ /** By chaining the spy with and.throwError, all calls to the spy will throw the specified value. */
+ throwError(msg: string): void;
+ /** When a calling strategy is used for a spy, the original stubbing behavior can be returned at any time with and.stub. */
+ stub(): Spy;
+ }
+
+ interface Calls {
+ /** By chaining the spy with calls.any(), will return false if the spy has not been called at all, and then true once at least one call happens. **/
+ any(): boolean;
+ /** By chaining the spy with calls.count(), will return the number of times the spy was called **/
+ count(): number;
+ /** By chaining the spy with calls.argsFor(), will return the arguments passed to call number index **/
+ argsFor(index: number): any[];
+ /** By chaining the spy with calls.allArgs(), will return the arguments to all calls **/
+ allArgs(): any[];
+ /** By chaining the spy with calls.all(), will return the context (the this) and arguments passed all calls **/
+ all(): CallInfo[];
+ /** By chaining the spy with calls.mostRecent(), will return the context (the this) and arguments for the most recent call **/
+ mostRecent(): CallInfo;
+ /** By chaining the spy with calls.first(), will return the context (the this) and arguments for the first call **/
+ first(): CallInfo;
+ /** By chaining the spy with calls.reset(), will clears all tracking for a spy **/
+ reset(): void;
+ }
+
+ interface CallInfo {
+ /** The context (the this) for the call */
+ object: any;
+ /** All arguments passed to the call */
+ args: any[];
+ }
+
+ interface Util {
+ inherit(childClass: Function, parentClass: Function): any;
+ formatException(e: any): any;
+ htmlEscape(str: string): string;
+ argsToArray(args: any): any;
+ extend(destination: any, source: any): any;
+ }
+
+ interface JsApiReporter extends Reporter {
+
+ started: boolean;
+ finished: boolean;
+ result: any;
+ messages: any;
+
+ new (): any;
+
+ suites(): Suite[];
+ summarize_(suiteOrSpec: SuiteOrSpec): any;
+ results(): any;
+ resultsForSpec(specId: any): any;
+ log(str: any): any;
+ resultsForSpecs(specIds: any): any;
+ summarizeResult_(result: any): any;
+ }
+
+ interface Jasmine {
+ Spec: Spec;
+ clock: Clock;
+ util: Util;
+ }
+
+ export var HtmlReporter: HtmlReporter;
+ export var HtmlSpecFilter: HtmlSpecFilter;
+ export var DEFAULT_TIMEOUT_INTERVAL: number;
+}
diff --git a/catalog-ui/typings/jquery/jquery.d.ts b/catalog-ui/typings/jquery/jquery.d.ts
new file mode 100644
index 0000000000..cc20de588b
--- /dev/null
+++ b/catalog-ui/typings/jquery/jquery.d.ts
@@ -0,0 +1,3181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for jQuery 1.10.x / 2.0.x
+// Project: http://jquery.com/
+// Definitions by: Boris Yankov <https://github.com/borisyankov/>, Christian Hoffmeister <https://github.com/choffmeister>, Steve Fenton <https://github.com/Steve-Fenton>, Diullei Gomes <https://github.com/Diullei>, Tass Iliopoulos <https://github.com/tasoili>, Jason Swearingen <https://github.com/jasons-novaleaf>, Sean Hill <https://github.com/seanski>, Guus Goossens <https://github.com/Guuz>, Kelly Summerlin <https://github.com/ksummerlin>, Basarat Ali Syed <https://github.com/basarat>, Nicholas Wolverson <https://github.com/nwolverson>, Derek Cicerone <https://github.com/derekcicerone>, Andrew Gaspar <https://github.com/AndrewGaspar>, James Harrison Fisher <https://github.com/jameshfisher>, Seikichi Kondo <https://github.com/seikichi>, Benjamin Jackman <https://github.com/benjaminjackman>, Poul Sorensen <https://github.com/s093294>, Josh Strobl <https://github.com/JoshStrobl>, John Reilly <https://github.com/johnnyreilly/>, Dick van den Brink <https://github.com/DickvdBrink>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+/* *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+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
+
+THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+
+/**
+ * Interface for the AJAX setting that will configure the AJAX request
+ */
+interface JQueryAjaxSettings {
+ /**
+ * The content type sent in the request header that tells the server what kind of response it will accept in return. If the accepts setting needs modification, it is recommended to do so once in the $.ajaxSetup() method.
+ */
+ accepts?: any;
+ /**
+ * By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success().
+ */
+ async?: boolean;
+ /**
+ * A pre-request callback function that can be used to modify the jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object before it is sent. Use this to set custom headers, etc. The jqXHR and settings objects are passed as arguments. This is an Ajax Event. Returning false in the beforeSend function will cancel the request. As of jQuery 1.5, the beforeSend option will be called regardless of the type of request.
+ */
+ beforeSend? (jqXHR: JQueryXHR, settings: JQueryAjaxSettings): any;
+ /**
+ * If set to false, it will force requested pages not to be cached by the browser. Note: Setting cache to false will only work correctly with HEAD and GET requests. It works by appending "_={timestamp}" to the GET parameters. The parameter is not needed for other types of requests, except in IE8 when a POST is made to a URL that has already been requested by a GET.
+ */
+ cache?: boolean;
+ /**
+ * A function to be called when the request finishes (after success and error callbacks are executed). The function gets passed two arguments: The jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object and a string categorizing the status of the request ("success", "notmodified", "error", "timeout", "abort", or "parsererror"). As of jQuery 1.5, the complete setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
+ */
+ complete? (jqXHR: JQueryXHR, textStatus: string): any;
+ /**
+ * An object of string/regular-expression pairs that determine how jQuery will parse the response, given its content type. (version added: 1.5)
+ */
+ contents?: { [key: string]: any; };
+ //According to jQuery.ajax source code, ajax's option actually allows contentType to set to "false"
+ // https://github.com/borisyankov/DefinitelyTyped/issues/742
+ /**
+ * When sending data to the server, use this content type. Default is "application/x-www-form-urlencoded; charset=UTF-8", which is fine for most cases. If you explicitly pass in a content-type to $.ajax(), then it is always sent to the server (even if no data is sent). The W3C XMLHttpRequest specification dictates that the charset is always UTF-8; specifying another charset will not force the browser to change the encoding.
+ */
+ contentType?: any;
+ /**
+ * This object will be made the context of all Ajax-related callbacks. By default, the context is an object that represents the ajax settings used in the call ($.ajaxSettings merged with the settings passed to $.ajax).
+ */
+ context?: any;
+ /**
+ * An object containing dataType-to-dataType converters. Each converter's value is a function that returns the transformed value of the response. (version added: 1.5)
+ */
+ converters?: { [key: string]: any; };
+ /**
+ * If you wish to force a crossDomain request (such as JSONP) on the same domain, set the value of crossDomain to true. This allows, for example, server-side redirection to another domain. (version added: 1.5)
+ */
+ crossDomain?: boolean;
+ /**
+ * Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests. See processData option to prevent this automatic processing. Object must be Key/Value pairs. If value is an Array, jQuery serializes multiple values with same key based on the value of the traditional setting (described below).
+ */
+ data?: any;
+ /**
+ * A function to be used to handle the raw response data of XMLHttpRequest.This is a pre-filtering function to sanitize the response. You should return the sanitized data. The function accepts two arguments: The raw data returned from the server and the 'dataType' parameter.
+ */
+ dataFilter? (data: any, ty: any): any;
+ /**
+ * The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string).
+ */
+ dataType?: string;
+ /**
+ * A function to be called if the request fails. The function receives three arguments: The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object, a string describing the type of error that occurred and an optional exception object, if one occurred. Possible values for the second argument (besides null) are "timeout", "error", "abort", and "parsererror". When an HTTP error occurs, errorThrown receives the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error." As of jQuery 1.5, the error setting can accept an array of functions. Each function will be called in turn. Note: This handler is not called for cross-domain script and cross-domain JSONP requests. This is an Ajax Event.
+ */
+ error? (jqXHR: JQueryXHR, textStatus: string, errorThrown: string): any;
+ /**
+ * Whether to trigger global Ajax event handlers for this request. The default is true. Set to false to prevent the global handlers like ajaxStart or ajaxStop from being triggered. This can be used to control various Ajax Events.
+ */
+ global?: boolean;
+ /**
+ * An object of additional header key/value pairs to send along with requests using the XMLHttpRequest transport. The header X-Requested-With: XMLHttpRequest is always added, but its default XMLHttpRequest value can be changed here. Values in the headers setting can also be overwritten from within the beforeSend function. (version added: 1.5)
+ */
+ headers?: { [key: string]: any; };
+ /**
+ * Allow the request to be successful only if the response has changed since the last request. This is done by checking the Last-Modified header. Default value is false, ignoring the header. In jQuery 1.4 this technique also checks the 'etag' specified by the server to catch unmodified data.
+ */
+ ifModified?: boolean;
+ /**
+ * Allow the current environment to be recognized as "local," (e.g. the filesystem), even if jQuery does not recognize it as such by default. The following protocols are currently recognized as local: file, *-extension, and widget. If the isLocal setting needs modification, it is recommended to do so once in the $.ajaxSetup() method. (version added: 1.5.1)
+ */
+ isLocal?: boolean;
+ /**
+ * Override the callback function name in a jsonp request. This value will be used instead of 'callback' in the 'callback=?' part of the query string in the url. So {jsonp:'onJSONPLoad'} would result in 'onJSONPLoad=?' passed to the server. As of jQuery 1.5, setting the jsonp option to false prevents jQuery from adding the "?callback" string to the URL or attempting to use "=?" for transformation. In this case, you should also explicitly set the jsonpCallback setting. For example, { jsonp: false, jsonpCallback: "callbackName" }
+ */
+ jsonp?: any;
+ /**
+ * Specify the callback function name for a JSONP request. This value will be used instead of the random name automatically generated by jQuery. It is preferable to let jQuery generate a unique name as it'll make it easier to manage the requests and provide callbacks and error handling. You may want to specify the callback when you want to enable better browser caching of GET requests. As of jQuery 1.5, you can also use a function for this setting, in which case the value of jsonpCallback is set to the return value of that function.
+ */
+ jsonpCallback?: any;
+ /**
+ * A mime type to override the XHR mime type. (version added: 1.5.1)
+ */
+ mimeType?: string;
+ /**
+ * A password to be used with XMLHttpRequest in response to an HTTP access authentication request.
+ */
+ password?: string;
+ /**
+ * By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.
+ */
+ processData?: boolean;
+ /**
+ * Only applies when the "script" transport is used (e.g., cross-domain requests with "jsonp" or "script" dataType and "GET" type). Sets the charset attribute on the script tag used in the request. Used when the character set on the local page is not the same as the one on the remote script.
+ */
+ scriptCharset?: string;
+ /**
+ * An object of numeric HTTP codes and functions to be called when the response has the corresponding code. f the request is successful, the status code functions take the same parameters as the success callback; if it results in an error (including 3xx redirect), they take the same parameters as the error callback. (version added: 1.5)
+ */
+ statusCode?: { [key: string]: any; };
+ /**
+ * A function to be called if the request succeeds. The function gets passed three arguments: The data returned from the server, formatted according to the dataType parameter; a string describing the status; and the jqXHR (in jQuery 1.4.x, XMLHttpRequest) object. As of jQuery 1.5, the success setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
+ */
+ success? (data: any, textStatus: string, jqXHR: JQueryXHR): any;
+ /**
+ * Set a timeout (in milliseconds) for the request. This will override any global timeout set with $.ajaxSetup(). The timeout period starts at the point the $.ajax call is made; if several other requests are in progress and the browser has no connections available, it is possible for a request to time out before it can be sent. In jQuery 1.4.x and below, the XMLHttpRequest object will be in an invalid state if the request times out; accessing any object members may throw an exception. In Firefox 3.0+ only, script and JSONP requests cannot be cancelled by a timeout; the script will run even if it arrives after the timeout period.
+ */
+ timeout?: number;
+ /**
+ * Set this to true if you wish to use the traditional style of param serialization.
+ */
+ traditional?: boolean;
+ /**
+ * The type of request to make ("POST" or "GET"), default is "GET". Note: Other HTTP request methods, such as PUT and DELETE, can also be used here, but they are not supported by all browsers.
+ */
+ type?: string;
+ /**
+ * A string containing the URL to which the request is sent.
+ */
+ url?: string;
+ /**
+ * A username to be used with XMLHttpRequest in response to an HTTP access authentication request.
+ */
+ username?: string;
+ /**
+ * Callback for creating the XMLHttpRequest object. Defaults to the ActiveXObject when available (IE), the XMLHttpRequest otherwise. Override to provide your own implementation for XMLHttpRequest or enhancements to the factory.
+ */
+ xhr?: any;
+ /**
+ * An object of fieldName-fieldValue pairs to set on the native XHR object. For example, you can use it to set withCredentials to true for cross-domain requests if needed. In jQuery 1.5, the withCredentials property was not propagated to the native XHR and thus CORS requests requiring it would ignore this flag. For this reason, we recommend using jQuery 1.5.1+ should you require the use of it. (version added: 1.5.1)
+ */
+ xhrFields?: { [key: string]: any; };
+}
+
+/**
+ * Interface for the jqXHR object
+ */
+interface JQueryXHR extends XMLHttpRequest, JQueryPromise<any> {
+ /**
+ * The .overrideMimeType() method may be used in the beforeSend() callback function, for example, to modify the response content-type header. As of jQuery 1.5.1, the jqXHR object also contains the overrideMimeType() method (it was available in jQuery 1.4.x, as well, but was temporarily removed in jQuery 1.5).
+ */
+ overrideMimeType(mimeType: string): any;
+ /**
+ * Cancel the request.
+ *
+ * @param statusText A string passed as the textStatus parameter for the done callback. Default value: "canceled"
+ */
+ abort(statusText?: string): void;
+ /**
+ * Incorporates the functionality of the .done() and .fail() methods, allowing (as of jQuery 1.8) the underlying Promise to be manipulated. Refer to deferred.then() for implementation details.
+ */
+ then(doneCallback: (data: any, textStatus: string, jqXHR: JQueryXHR) => void, failCallback?: (jqXHR: JQueryXHR, textStatus: string, errorThrown: any) => void): JQueryPromise<any>;
+ /**
+ * Property containing the parsed response if the response Content-Type is json
+ */
+ responseJSON?: any;
+}
+
+/**
+ * Interface for the JQuery callback
+ */
+interface JQueryCallback {
+ /**
+ * Add a callback or a collection of callbacks to a callback list.
+ *
+ * @param callbacks A function, or array of functions, that are to be added to the callback list.
+ */
+ add(callbacks: Function): JQueryCallback;
+ /**
+ * Add a callback or a collection of callbacks to a callback list.
+ *
+ * @param callbacks A function, or array of functions, that are to be added to the callback list.
+ */
+ add(callbacks: Function[]): JQueryCallback;
+
+ /**
+ * Disable a callback list from doing anything more.
+ */
+ disable(): JQueryCallback;
+
+ /**
+ * Determine if the callbacks list has been disabled.
+ */
+ disabled(): boolean;
+
+ /**
+ * Remove all of the callbacks from a list.
+ */
+ empty(): JQueryCallback;
+
+ /**
+ * Call all of the callbacks with the given arguments
+ *
+ * @param arguments The argument or list of arguments to pass back to the callback list.
+ */
+ fire(...arguments: any[]): JQueryCallback;
+
+ /**
+ * Determine if the callbacks have already been called at least once.
+ */
+ fired(): boolean;
+
+ /**
+ * Call all callbacks in a list with the given context and arguments.
+ *
+ * @param context A reference to the context in which the callbacks in the list should be fired.
+ * @param arguments An argument, or array of arguments, to pass to the callbacks in the list.
+ */
+ fireWith(context?: any, ...args: any[]): JQueryCallback;
+
+ /**
+ * Determine whether a supplied callback is in a list
+ *
+ * @param callback The callback to search for.
+ */
+ has(callback: Function): boolean;
+
+ /**
+ * Lock a callback list in its current state.
+ */
+ lock(): JQueryCallback;
+
+ /**
+ * Determine if the callbacks list has been locked.
+ */
+ locked(): boolean;
+
+ /**
+ * Remove a callback or a collection of callbacks from a callback list.
+ *
+ * @param callbacks A function, or array of functions, that are to be removed from the callback list.
+ */
+ remove(callbacks: Function): JQueryCallback;
+ /**
+ * Remove a callback or a collection of callbacks from a callback list.
+ *
+ * @param callbacks A function, or array of functions, that are to be removed from the callback list.
+ */
+ remove(callbacks: Function[]): JQueryCallback;
+}
+
+/**
+ * Allows jQuery Promises to interop with non-jQuery promises
+ */
+interface JQueryGenericPromise<T> {
+ /**
+ * Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.
+ *
+ * @param doneFilter A function that is called when the Deferred is resolved.
+ * @param failFilter An optional function that is called when the Deferred is rejected.
+ */
+ then<U>(doneFilter: (value?: T, ...values: any[]) => U|JQueryPromise<U>, failFilter?: (...reasons: any[]) => any, progressFilter?: (...progression: any[]) => any): JQueryPromise<U>;
+
+ /**
+ * Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.
+ *
+ * @param doneFilter A function that is called when the Deferred is resolved.
+ * @param failFilter An optional function that is called when the Deferred is rejected.
+ */
+ then(doneFilter: (value?: T, ...values: any[]) => void, failFilter?: (...reasons: any[]) => any, progressFilter?: (...progression: any[]) => any): JQueryPromise<void>;
+}
+
+/**
+ * Interface for the JQuery promise/deferred callbacks
+ */
+interface JQueryPromiseCallback<T> {
+ (value?: T, ...args: any[]): void;
+}
+
+interface JQueryPromiseOperator<T, U> {
+ (callback1: JQueryPromiseCallback<T>|JQueryPromiseCallback<T>[], ...callbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryPromise<U>;
+}
+
+/**
+ * Interface for the JQuery promise, part of callbacks
+ */
+interface JQueryPromise<T> extends JQueryGenericPromise<T> {
+ /**
+ * Determine the current state of a Deferred object.
+ */
+ state(): string;
+ /**
+ * Add handlers to be called when the Deferred object is either resolved or rejected.
+ *
+ * @param alwaysCallbacks1 A function, or array of functions, that is called when the Deferred is resolved or rejected.
+ * @param alwaysCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved or rejected.
+ */
+ always(alwaysCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...alwaysCallbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryPromise<T>;
+ /**
+ * Add handlers to be called when the Deferred object is resolved.
+ *
+ * @param doneCallbacks1 A function, or array of functions, that are called when the Deferred is resolved.
+ * @param doneCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved.
+ */
+ done(doneCallback1?: JQueryPromiseCallback<T>|JQueryPromiseCallback<T>[], ...doneCallbackN: Array<JQueryPromiseCallback<T>|JQueryPromiseCallback<T>[]>): JQueryPromise<T>;
+ /**
+ * Add handlers to be called when the Deferred object is rejected.
+ *
+ * @param failCallbacks1 A function, or array of functions, that are called when the Deferred is rejected.
+ * @param failCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is rejected.
+ */
+ fail(failCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...failCallbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryPromise<T>;
+ /**
+ * Add handlers to be called when the Deferred object generates progress notifications.
+ *
+ * @param progressCallbacks A function, or array of functions, to be called when the Deferred generates progress notifications.
+ */
+ progress(progressCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...progressCallbackN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryPromise<T>;
+
+ // Deprecated - given no typings
+ pipe(doneFilter?: (x: any) => any, failFilter?: (x: any) => any, progressFilter?: (x: any) => any): JQueryPromise<any>;
+}
+
+/**
+ * Interface for the JQuery deferred, part of callbacks
+ */
+interface JQueryDeferred<T> extends JQueryGenericPromise<T> {
+ /**
+ * Determine the current state of a Deferred object.
+ */
+ state(): string;
+ /**
+ * Add handlers to be called when the Deferred object is either resolved or rejected.
+ *
+ * @param alwaysCallbacks1 A function, or array of functions, that is called when the Deferred is resolved or rejected.
+ * @param alwaysCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved or rejected.
+ */
+ always(alwaysCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...alwaysCallbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryDeferred<T>;
+ /**
+ * Add handlers to be called when the Deferred object is resolved.
+ *
+ * @param doneCallbacks1 A function, or array of functions, that are called when the Deferred is resolved.
+ * @param doneCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved.
+ */
+ done(doneCallback1?: JQueryPromiseCallback<T>|JQueryPromiseCallback<T>[], ...doneCallbackN: Array<JQueryPromiseCallback<T>|JQueryPromiseCallback<T>[]>): JQueryDeferred<T>;
+ /**
+ * Add handlers to be called when the Deferred object is rejected.
+ *
+ * @param failCallbacks1 A function, or array of functions, that are called when the Deferred is rejected.
+ * @param failCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is rejected.
+ */
+ fail(failCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...failCallbacksN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryDeferred<T>;
+ /**
+ * Add handlers to be called when the Deferred object generates progress notifications.
+ *
+ * @param progressCallbacks A function, or array of functions, to be called when the Deferred generates progress notifications.
+ */
+ progress(progressCallback1?: JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[], ...progressCallbackN: Array<JQueryPromiseCallback<any>|JQueryPromiseCallback<any>[]>): JQueryDeferred<T>;
+
+ /**
+ * Call the progressCallbacks on a Deferred object with the given args.
+ *
+ * @param args Optional arguments that are passed to the progressCallbacks.
+ */
+ notify(value?: any, ...args: any[]): JQueryDeferred<T>;
+
+ /**
+ * Call the progressCallbacks on a Deferred object with the given context and args.
+ *
+ * @param context Context passed to the progressCallbacks as the this object.
+ * @param args Optional arguments that are passed to the progressCallbacks.
+ */
+ notifyWith(context: any, value?: any, ...args: any[]): JQueryDeferred<T>;
+
+ /**
+ * Reject a Deferred object and call any failCallbacks with the given args.
+ *
+ * @param args Optional arguments that are passed to the failCallbacks.
+ */
+ reject(value?: any, ...args: any[]): JQueryDeferred<T>;
+ /**
+ * Reject a Deferred object and call any failCallbacks with the given context and args.
+ *
+ * @param context Context passed to the failCallbacks as the this object.
+ * @param args An optional array of arguments that are passed to the failCallbacks.
+ */
+ rejectWith(context: any, value?: any, ...args: any[]): JQueryDeferred<T>;
+
+ /**
+ * Resolve a Deferred object and call any doneCallbacks with the given args.
+ *
+ * @param value First argument passed to doneCallbacks.
+ * @param args Optional subsequent arguments that are passed to the doneCallbacks.
+ */
+ resolve(value?: T, ...args: any[]): JQueryDeferred<T>;
+
+ /**
+ * Resolve a Deferred object and call any doneCallbacks with the given context and args.
+ *
+ * @param context Context passed to the doneCallbacks as the this object.
+ * @param args An optional array of arguments that are passed to the doneCallbacks.
+ */
+ resolveWith(context: any, value?: T, ...args: any[]): JQueryDeferred<T>;
+
+ /**
+ * Return a Deferred's Promise object.
+ *
+ * @param target Object onto which the promise methods have to be attached
+ */
+ promise(target?: any): JQueryPromise<T>;
+
+ // Deprecated - given no typings
+ pipe(doneFilter?: (x: any) => any, failFilter?: (x: any) => any, progressFilter?: (x: any) => any): JQueryPromise<any>;
+}
+
+/**
+ * Interface of the JQuery extension of the W3C event object
+ */
+interface BaseJQueryEventObject extends Event {
+ data: any;
+ delegateTarget: Element;
+ isDefaultPrevented(): boolean;
+ isImmediatePropagationStopped(): boolean;
+ isPropagationStopped(): boolean;
+ namespace: string;
+ originalEvent: Event;
+ preventDefault(): any;
+ relatedTarget: Element;
+ result: any;
+ stopImmediatePropagation(): void;
+ stopPropagation(): void;
+ target: Element;
+ pageX: number;
+ pageY: number;
+ which: number;
+ metaKey: boolean;
+}
+
+interface JQueryInputEventObject extends BaseJQueryEventObject {
+ altKey: boolean;
+ ctrlKey: boolean;
+ metaKey: boolean;
+ shiftKey: boolean;
+}
+
+interface JQueryMouseEventObject extends JQueryInputEventObject {
+ button: number;
+ clientX: number;
+ clientY: number;
+ offsetX: number;
+ offsetY: number;
+ pageX: number;
+ pageY: number;
+ screenX: number;
+ screenY: number;
+}
+
+interface JQueryKeyEventObject extends JQueryInputEventObject {
+ char: any;
+ charCode: number;
+ key: any;
+ keyCode: number;
+}
+
+interface JQueryEventObject extends BaseJQueryEventObject, JQueryInputEventObject, JQueryMouseEventObject, JQueryKeyEventObject{
+}
+
+/*
+ Collection of properties of the current browser
+*/
+
+interface JQuerySupport {
+ ajax?: boolean;
+ boxModel?: boolean;
+ changeBubbles?: boolean;
+ checkClone?: boolean;
+ checkOn?: boolean;
+ cors?: boolean;
+ cssFloat?: boolean;
+ hrefNormalized?: boolean;
+ htmlSerialize?: boolean;
+ leadingWhitespace?: boolean;
+ noCloneChecked?: boolean;
+ noCloneEvent?: boolean;
+ opacity?: boolean;
+ optDisabled?: boolean;
+ optSelected?: boolean;
+ scriptEval? (): boolean;
+ style?: boolean;
+ submitBubbles?: boolean;
+ tbody?: boolean;
+}
+
+interface JQueryParam {
+ /**
+ * Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request.
+ *
+ * @param obj An array or object to serialize.
+ */
+ (obj: any): string;
+
+ /**
+ * Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request.
+ *
+ * @param obj An array or object to serialize.
+ * @param traditional A Boolean indicating whether to perform a traditional "shallow" serialization.
+ */
+ (obj: any, traditional: boolean): string;
+}
+
+/**
+ * The interface used to construct jQuery events (with $.Event). It is
+ * defined separately instead of inline in JQueryStatic to allow
+ * overriding the construction function with specific strings
+ * returning specific event objects.
+ */
+interface JQueryEventConstructor {
+ (name: string, eventProperties?: any): JQueryEventObject;
+ new (name: string, eventProperties?: any): JQueryEventObject;
+}
+
+/**
+ * The interface used to specify coordinates.
+ */
+interface JQueryCoordinates {
+ left: number;
+ top: number;
+}
+
+/**
+ * Elements in the array returned by serializeArray()
+ */
+interface JQuerySerializeArrayElement {
+ name: string;
+ value: string;
+}
+
+interface JQueryAnimationOptions {
+ /**
+ * A string or number determining how long the animation will run.
+ */
+ duration?: any;
+ /**
+ * A string indicating which easing function to use for the transition.
+ */
+ easing?: string;
+ /**
+ * A function to call once the animation is complete.
+ */
+ complete?: Function;
+ /**
+ * A function to be called for each animated property of each animated element. This function provides an opportunity to modify the Tween object to change the value of the property before it is set.
+ */
+ step?: (now: number, tween: any) => any;
+ /**
+ * A function to be called after each step of the animation, only once per animated element regardless of the number of animated properties. (version added: 1.8)
+ */
+ progress?: (animation: JQueryPromise<any>, progress: number, remainingMs: number) => any;
+ /**
+ * A function to call when the animation begins. (version added: 1.8)
+ */
+ start?: (animation: JQueryPromise<any>) => any;
+ /**
+ * A function to be called when the animation completes (its Promise object is resolved). (version added: 1.8)
+ */
+ done?: (animation: JQueryPromise<any>, jumpedToEnd: boolean) => any;
+ /**
+ * A function to be called when the animation fails to complete (its Promise object is rejected). (version added: 1.8)
+ */
+ fail?: (animation: JQueryPromise<any>, jumpedToEnd: boolean) => any;
+ /**
+ * A function to be called when the animation completes or stops without completing (its Promise object is either resolved or rejected). (version added: 1.8)
+ */
+ always?: (animation: JQueryPromise<any>, jumpedToEnd: boolean) => any;
+ /**
+ * A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string. When a custom queue name is used the animation does not automatically start; you must call .dequeue("queuename") to start it.
+ */
+ queue?: any;
+ /**
+ * A map of one or more of the CSS properties defined by the properties argument and their corresponding easing functions. (version added: 1.4)
+ */
+ specialEasing?: Object;
+}
+
+/**
+ * Static members of jQuery (those on $ and jQuery themselves)
+ */
+interface JQueryStatic {
+
+ /**
+ * Perform an asynchronous HTTP (Ajax) request.
+ *
+ * @param settings A set of key/value pairs that configure the Ajax request. All settings are optional. A default can be set for any option with $.ajaxSetup().
+ */
+ ajax(settings: JQueryAjaxSettings): JQueryXHR;
+ /**
+ * Perform an asynchronous HTTP (Ajax) request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param settings A set of key/value pairs that configure the Ajax request. All settings are optional. A default can be set for any option with $.ajaxSetup().
+ */
+ ajax(url: string, settings?: JQueryAjaxSettings): JQueryXHR;
+
+ /**
+ * Handle custom Ajax options or modify existing options before each request is sent and before they are processed by $.ajax().
+ *
+ * @param dataTypes An optional string containing one or more space-separated dataTypes
+ * @param handler A handler to set default values for future Ajax requests.
+ */
+ ajaxPrefilter(dataTypes: string, handler: (opts: any, originalOpts: JQueryAjaxSettings, jqXHR: JQueryXHR) => any): void;
+ /**
+ * Handle custom Ajax options or modify existing options before each request is sent and before they are processed by $.ajax().
+ *
+ * @param handler A handler to set default values for future Ajax requests.
+ */
+ ajaxPrefilter(handler: (opts: any, originalOpts: JQueryAjaxSettings, jqXHR: JQueryXHR) => any): void;
+
+ ajaxSettings: JQueryAjaxSettings;
+
+ /**
+ * Set default values for future Ajax requests. Its use is not recommended.
+ *
+ * @param options A set of key/value pairs that configure the default Ajax request. All options are optional.
+ */
+ ajaxSetup(options: JQueryAjaxSettings): void;
+
+ /**
+ * Load data from the server using a HTTP GET request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param success A callback function that is executed if the request succeeds.
+ * @param dataType The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html).
+ */
+ get(url: string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR;
+ /**
+ * Load data from the server using a HTTP GET request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param data A plain object or string that is sent to the server with the request.
+ * @param success A callback function that is executed if the request succeeds.
+ * @param dataType The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html).
+ */
+ get(url: string, data?: Object|string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR;
+ /**
+ * Load JSON-encoded data from the server using a GET HTTP request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param success A callback function that is executed if the request succeeds.
+ */
+ getJSON(url: string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any): JQueryXHR;
+ /**
+ * Load JSON-encoded data from the server using a GET HTTP request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param data A plain object or string that is sent to the server with the request.
+ * @param success A callback function that is executed if the request succeeds.
+ */
+ getJSON(url: string, data?: Object|string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any): JQueryXHR;
+ /**
+ * Load a JavaScript file from the server using a GET HTTP request, then execute it.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param success A callback function that is executed if the request succeeds.
+ */
+ getScript(url: string, success?: (script: string, textStatus: string, jqXHR: JQueryXHR) => any): JQueryXHR;
+
+ /**
+ * Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request.
+ */
+ param: JQueryParam;
+
+ /**
+ * Load data from the server using a HTTP POST request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param success A callback function that is executed if the request succeeds. Required if dataType is provided, but can be null in that case.
+ * @param dataType The type of data expected from the server. Default: Intelligent Guess (xml, json, script, text, html).
+ */
+ post(url: string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR;
+ /**
+ * Load data from the server using a HTTP POST request.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param data A plain object or string that is sent to the server with the request.
+ * @param success A callback function that is executed if the request succeeds. Required if dataType is provided, but can be null in that case.
+ * @param dataType The type of data expected from the server. Default: Intelligent Guess (xml, json, script, text, html).
+ */
+ post(url: string, data?: Object|string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR;
+
+ /**
+ * A multi-purpose callbacks list object that provides a powerful way to manage callback lists.
+ *
+ * @param flags An optional list of space-separated flags that change how the callback list behaves.
+ */
+ Callbacks(flags?: string): JQueryCallback;
+
+ /**
+ * Holds or releases the execution of jQuery's ready event.
+ *
+ * @param hold Indicates whether the ready hold is being requested or released
+ */
+ holdReady(hold: boolean): void;
+
+ /**
+ * Accepts a string containing a CSS selector which is then used to match a set of elements.
+ *
+ * @param selector A string containing a selector expression
+ * @param context A DOM Element, Document, or jQuery to use as context
+ */
+ (selector: string, context?: Element|JQuery): JQuery;
+
+ /**
+ * Accepts a string containing a CSS selector which is then used to match a set of elements.
+ *
+ * @param element A DOM element to wrap in a jQuery object.
+ */
+ (element: Element): JQuery;
+
+ /**
+ * Accepts a string containing a CSS selector which is then used to match a set of elements.
+ *
+ * @param elementArray An array containing a set of DOM elements to wrap in a jQuery object.
+ */
+ (elementArray: Element[]): JQuery;
+
+ /**
+ * Binds a function to be executed when the DOM has finished loading.
+ *
+ * @param callback A function to execute after the DOM is ready.
+ */
+ (callback: (jQueryAlias?: JQueryStatic) => any): JQuery;
+
+ /**
+ * Accepts a string containing a CSS selector which is then used to match a set of elements.
+ *
+ * @param object A plain object to wrap in a jQuery object.
+ */
+ (object: {}): JQuery;
+
+ /**
+ * Accepts a string containing a CSS selector which is then used to match a set of elements.
+ *
+ * @param object An existing jQuery object to clone.
+ */
+ (object: JQuery): JQuery;
+
+ /**
+ * Specify a function to execute when the DOM is fully loaded.
+ */
+ (): JQuery;
+
+ /**
+ * Creates DOM elements on the fly from the provided string of raw HTML.
+ *
+ * @param html A string of HTML to create on the fly. Note that this parses HTML, not XML.
+ * @param ownerDocument A document in which the new elements will be created.
+ */
+ (html: string, ownerDocument?: Document): JQuery;
+
+ /**
+ * Creates DOM elements on the fly from the provided string of raw HTML.
+ *
+ * @param html A string defining a single, standalone, HTML element (e.g. <div/> or <div></div>).
+ * @param attributes An object of attributes, events, and methods to call on the newly-created element.
+ */
+ (html: string, attributes: Object): JQuery;
+
+ /**
+ * Relinquish jQuery's control of the $ variable.
+ *
+ * @param removeAll A Boolean indicating whether to remove all jQuery variables from the global scope (including jQuery itself).
+ */
+ noConflict(removeAll?: boolean): Object;
+
+ /**
+ * Provides a way to execute callback functions based on one or more objects, usually Deferred objects that represent asynchronous events.
+ *
+ * @param deferreds One or more Deferred objects, or plain JavaScript objects.
+ */
+ when<T>(...deferreds: Array<T|JQueryPromise<T>/* as JQueryDeferred<T> */>): JQueryPromise<T>;
+
+ /**
+ * Hook directly into jQuery to override how particular CSS properties are retrieved or set, normalize CSS property naming, or create custom properties.
+ */
+ cssHooks: { [key: string]: any; };
+ cssNumber: any;
+
+ /**
+ * Store arbitrary data associated with the specified element. Returns the value that was set.
+ *
+ * @param element The DOM element to associate with the data.
+ * @param key A string naming the piece of data to set.
+ * @param value The new data value.
+ */
+ data<T>(element: Element, key: string, value: T): T;
+ /**
+ * Returns value at named data store for the element, as set by jQuery.data(element, name, value), or the full data store for the element.
+ *
+ * @param element The DOM element to associate with the data.
+ * @param key A string naming the piece of data to set.
+ */
+ data(element: Element, key: string): any;
+ /**
+ * Returns value at named data store for the element, as set by jQuery.data(element, name, value), or the full data store for the element.
+ *
+ * @param element The DOM element to associate with the data.
+ */
+ data(element: Element): any;
+
+ /**
+ * Execute the next function on the queue for the matched element.
+ *
+ * @param element A DOM element from which to remove and execute a queued function.
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ */
+ dequeue(element: Element, queueName?: string): void;
+
+ /**
+ * Determine whether an element has any jQuery data associated with it.
+ *
+ * @param element A DOM element to be checked for data.
+ */
+ hasData(element: Element): boolean;
+
+ /**
+ * Show the queue of functions to be executed on the matched element.
+ *
+ * @param element A DOM element to inspect for an attached queue.
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ */
+ queue(element: Element, queueName?: string): any[];
+ /**
+ * Manipulate the queue of functions to be executed on the matched element.
+ *
+ * @param element A DOM element where the array of queued functions is attached.
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ * @param newQueue An array of functions to replace the current queue contents.
+ */
+ queue(element: Element, queueName: string, newQueue: Function[]): JQuery;
+ /**
+ * Manipulate the queue of functions to be executed on the matched element.
+ *
+ * @param element A DOM element on which to add a queued function.
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ * @param callback The new function to add to the queue.
+ */
+ queue(element: Element, queueName: string, callback: Function): JQuery;
+
+ /**
+ * Remove a previously-stored piece of data.
+ *
+ * @param element A DOM element from which to remove data.
+ * @param name A string naming the piece of data to remove.
+ */
+ removeData(element: Element, name?: string): JQuery;
+
+ /**
+ * A constructor function that returns a chainable utility object with methods to register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.
+ *
+ * @param beforeStart A function that is called just before the constructor returns.
+ */
+ Deferred<T>(beforeStart?: (deferred: JQueryDeferred<T>) => any): JQueryDeferred<T>;
+
+ /**
+ * Effects
+ */
+ fx: {
+ tick: () => void;
+ /**
+ * The rate (in milliseconds) at which animations fire.
+ */
+ interval: number;
+ stop: () => void;
+ speeds: { slow: number; fast: number; };
+ /**
+ * Globally disable all animations.
+ */
+ off: boolean;
+ step: any;
+ };
+
+ /**
+ * Takes a function and returns a new one that will always have a particular context.
+ *
+ * @param fnction The function whose context will be changed.
+ * @param context The object to which the context (this) of the function should be set.
+ * @param additionalArguments Any number of arguments to be passed to the function referenced in the function argument.
+ */
+ proxy(fnction: (...args: any[]) => any, context: Object, ...additionalArguments: any[]): any;
+ /**
+ * Takes a function and returns a new one that will always have a particular context.
+ *
+ * @param context The object to which the context (this) of the function should be set.
+ * @param name The name of the function whose context will be changed (should be a property of the context object).
+ * @param additionalArguments Any number of arguments to be passed to the function named in the name argument.
+ */
+ proxy(context: Object, name: string, ...additionalArguments: any[]): any;
+
+ Event: JQueryEventConstructor;
+
+ /**
+ * Takes a string and throws an exception containing it.
+ *
+ * @param message The message to send out.
+ */
+ error(message: any): JQuery;
+
+ expr: any;
+ fn: any; //TODO: Decide how we want to type this
+
+ isReady: boolean;
+
+ // Properties
+ support: JQuerySupport;
+
+ /**
+ * Check to see if a DOM element is a descendant of another DOM element.
+ *
+ * @param container The DOM element that may contain the other element.
+ * @param contained The DOM element that may be contained by (a descendant of) the other element.
+ */
+ contains(container: Element, contained: Element): boolean;
+
+ /**
+ * A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.
+ *
+ * @param collection The object or array to iterate over.
+ * @param callback The function that will be executed on every object.
+ */
+ each<T>(
+ collection: T[],
+ callback: (indexInArray: number, valueOfElement: T) => any
+ ): any;
+
+ /**
+ * A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.
+ *
+ * @param collection The object or array to iterate over.
+ * @param callback The function that will be executed on every object.
+ */
+ each(
+ collection: any,
+ callback: (indexInArray: any, valueOfElement: any) => any
+ ): any;
+
+ /**
+ * Merge the contents of two or more objects together into the first object.
+ *
+ * @param target An object that will receive the new properties if additional objects are passed in or that will extend the jQuery namespace if it is the sole argument.
+ * @param object1 An object containing additional properties to merge in.
+ * @param objectN Additional objects containing properties to merge in.
+ */
+ extend(target: any, object1?: any, ...objectN: any[]): any;
+ /**
+ * Merge the contents of two or more objects together into the first object.
+ *
+ * @param deep If true, the merge becomes recursive (aka. deep copy).
+ * @param target The object to extend. It will receive the new properties.
+ * @param object1 An object containing additional properties to merge in.
+ * @param objectN Additional objects containing properties to merge in.
+ */
+ extend(deep: boolean, target: any, object1?: any, ...objectN: any[]): any;
+
+ /**
+ * Execute some JavaScript code globally.
+ *
+ * @param code The JavaScript code to execute.
+ */
+ globalEval(code: string): any;
+
+ /**
+ * Finds the elements of an array which satisfy a filter function. The original array is not affected.
+ *
+ * @param array The array to search through.
+ * @param func The function to process each item against. The first argument to the function is the item, and the second argument is the index. The function should return a Boolean value. this will be the global window object.
+ * @param invert If "invert" is false, or not provided, then the function returns an array consisting of all elements for which "callback" returns true. If "invert" is true, then the function returns an array consisting of all elements for which "callback" returns false.
+ */
+ grep<T>(array: T[], func: (elementOfArray: T, indexInArray: number) => boolean, invert?: boolean): T[];
+
+ /**
+ * Search for a specified value within an array and return its index (or -1 if not found).
+ *
+ * @param value The value to search for.
+ * @param array An array through which to search.
+ * @param fromIndex he index of the array at which to begin the search. The default is 0, which will search the whole array.
+ */
+ inArray<T>(value: T, array: T[], fromIndex?: number): number;
+
+ /**
+ * Determine whether the argument is an array.
+ *
+ * @param obj Object to test whether or not it is an array.
+ */
+ isArray(obj: any): boolean;
+ /**
+ * Check to see if an object is empty (contains no enumerable properties).
+ *
+ * @param obj The object that will be checked to see if it's empty.
+ */
+ isEmptyObject(obj: any): boolean;
+ /**
+ * Determine if the argument passed is a Javascript function object.
+ *
+ * @param obj Object to test whether or not it is a function.
+ */
+ isFunction(obj: any): boolean;
+ /**
+ * Determines whether its argument is a number.
+ *
+ * @param obj The value to be tested.
+ */
+ isNumeric(value: any): boolean;
+ /**
+ * Check to see if an object is a plain object (created using "{}" or "new Object").
+ *
+ * @param obj The object that will be checked to see if it's a plain object.
+ */
+ isPlainObject(obj: any): boolean;
+ /**
+ * Determine whether the argument is a window.
+ *
+ * @param obj Object to test whether or not it is a window.
+ */
+ isWindow(obj: any): boolean;
+ /**
+ * Check to see if a DOM node is within an XML document (or is an XML document).
+ *
+ * @param node he DOM node that will be checked to see if it's in an XML document.
+ */
+ isXMLDoc(node: Node): boolean;
+
+ /**
+ * Convert an array-like object into a true JavaScript array.
+ *
+ * @param obj Any object to turn into a native Array.
+ */
+ makeArray(obj: any): any[];
+
+ /**
+ * Translate all items in an array or object to new array of items.
+ *
+ * @param array The Array to translate.
+ * @param callback The function to process each item against. The first argument to the function is the array item, the second argument is the index in array The function can return any value. Within the function, this refers to the global (window) object.
+ */
+ map<T, U>(array: T[], callback: (elementOfArray: T, indexInArray: number) => U): U[];
+ /**
+ * Translate all items in an array or object to new array of items.
+ *
+ * @param arrayOrObject The Array or Object to translate.
+ * @param callback The function to process each item against. The first argument to the function is the value; the second argument is the index or key of the array or object property. The function can return any value to add to the array. A returned array will be flattened into the resulting array. Within the function, this refers to the global (window) object.
+ */
+ map(arrayOrObject: any, callback: (value: any, indexOrKey: any) => any): any;
+
+ /**
+ * Merge the contents of two arrays together into the first array.
+ *
+ * @param first The first array to merge, the elements of second added.
+ * @param second The second array to merge into the first, unaltered.
+ */
+ merge<T>(first: T[], second: T[]): T[];
+
+ /**
+ * An empty function.
+ */
+ noop(): any;
+
+ /**
+ * Return a number representing the current time.
+ */
+ now(): number;
+
+ /**
+ * Takes a well-formed JSON string and returns the resulting JavaScript object.
+ *
+ * @param json The JSON string to parse.
+ */
+ parseJSON(json: string): any;
+
+ /**
+ * Parses a string into an XML document.
+ *
+ * @param data a well-formed XML string to be parsed
+ */
+ parseXML(data: string): XMLDocument;
+
+ /**
+ * Remove the whitespace from the beginning and end of a string.
+ *
+ * @param str Remove the whitespace from the beginning and end of a string.
+ */
+ trim(str: string): string;
+
+ /**
+ * Determine the internal JavaScript [[Class]] of an object.
+ *
+ * @param obj Object to get the internal JavaScript [[Class]] of.
+ */
+ type(obj: any): string;
+
+ /**
+ * Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers.
+ *
+ * @param array The Array of DOM elements.
+ */
+ unique(array: Element[]): Element[];
+
+ /**
+ * Parses a string into an array of DOM nodes.
+ *
+ * @param data HTML string to be parsed
+ * @param context DOM element to serve as the context in which the HTML fragment will be created
+ * @param keepScripts A Boolean indicating whether to include scripts passed in the HTML string
+ */
+ parseHTML(data: string, context?: HTMLElement, keepScripts?: boolean): any[];
+
+ /**
+ * Parses a string into an array of DOM nodes.
+ *
+ * @param data HTML string to be parsed
+ * @param context DOM element to serve as the context in which the HTML fragment will be created
+ * @param keepScripts A Boolean indicating whether to include scripts passed in the HTML string
+ */
+ parseHTML(data: string, context?: Document, keepScripts?: boolean): any[];
+}
+
+/**
+ * The jQuery instance members
+ */
+interface JQuery {
+ /**
+ * Register a handler to be called when Ajax requests complete. This is an AjaxEvent.
+ *
+ * @param handler The function to be invoked.
+ */
+ ajaxComplete(handler: (event: JQueryEventObject, XMLHttpRequest: XMLHttpRequest, ajaxOptions: any) => any): JQuery;
+ /**
+ * Register a handler to be called when Ajax requests complete with an error. This is an Ajax Event.
+ *
+ * @param handler The function to be invoked.
+ */
+ ajaxError(handler: (event: JQueryEventObject, jqXHR: JQueryXHR, ajaxSettings: JQueryAjaxSettings, thrownError: any) => any): JQuery;
+ /**
+ * Attach a function to be executed before an Ajax request is sent. This is an Ajax Event.
+ *
+ * @param handler The function to be invoked.
+ */
+ ajaxSend(handler: (event: JQueryEventObject, jqXHR: JQueryXHR, ajaxOptions: JQueryAjaxSettings) => any): JQuery;
+ /**
+ * Register a handler to be called when the first Ajax request begins. This is an Ajax Event.
+ *
+ * @param handler The function to be invoked.
+ */
+ ajaxStart(handler: () => any): JQuery;
+ /**
+ * Register a handler to be called when all Ajax requests have completed. This is an Ajax Event.
+ *
+ * @param handler The function to be invoked.
+ */
+ ajaxStop(handler: () => any): JQuery;
+ /**
+ * Attach a function to be executed whenever an Ajax request completes successfully. This is an Ajax Event.
+ *
+ * @param handler The function to be invoked.
+ */
+ ajaxSuccess(handler: (event: JQueryEventObject, XMLHttpRequest: XMLHttpRequest, ajaxOptions: JQueryAjaxSettings) => any): JQuery;
+
+ /**
+ * Load data from the server and place the returned HTML into the matched element.
+ *
+ * @param url A string containing the URL to which the request is sent.
+ * @param data A plain object or string that is sent to the server with the request.
+ * @param complete A callback function that is executed when the request completes.
+ */
+ load(url: string, data?: string|Object, complete?: (responseText: string, textStatus: string, XMLHttpRequest: XMLHttpRequest) => any): JQuery;
+
+ /**
+ * Encode a set of form elements as a string for submission.
+ */
+ serialize(): string;
+ /**
+ * Encode a set of form elements as an array of names and values.
+ */
+ serializeArray(): JQuerySerializeArrayElement[];
+
+ /**
+ * Adds the specified class(es) to each of the set of matched elements.
+ *
+ * @param className One or more space-separated classes to be added to the class attribute of each matched element.
+ */
+ addClass(className: string): JQuery;
+ /**
+ * Adds the specified class(es) to each of the set of matched elements.
+ *
+ * @param function A function returning one or more space-separated class names to be added to the existing class name(s). Receives the index position of the element in the set and the existing class name(s) as arguments. Within the function, this refers to the current element in the set.
+ */
+ addClass(func: (index: number, className: string) => string): JQuery;
+
+ /**
+ * Add the previous set of elements on the stack to the current set, optionally filtered by a selector.
+ */
+ addBack(selector?: string): JQuery;
+
+ /**
+ * Get the value of an attribute for the first element in the set of matched elements.
+ *
+ * @param attributeName The name of the attribute to get.
+ */
+ attr(attributeName: string): string;
+ /**
+ * Set one or more attributes for the set of matched elements.
+ *
+ * @param attributeName The name of the attribute to set.
+ * @param value A value to set for the attribute.
+ */
+ attr(attributeName: string, value: string|number): JQuery;
+ /**
+ * Set one or more attributes for the set of matched elements.
+ *
+ * @param attributeName The name of the attribute to set.
+ * @param func A function returning the value to set. this is the current element. Receives the index position of the element in the set and the old attribute value as arguments.
+ */
+ attr(attributeName: string, func: (index: number, attr: string) => string|number): JQuery;
+ /**
+ * Set one or more attributes for the set of matched elements.
+ *
+ * @param attributes An object of attribute-value pairs to set.
+ */
+ attr(attributes: Object): JQuery;
+
+ /**
+ * Determine whether any of the matched elements are assigned the given class.
+ *
+ * @param className The class name to search for.
+ */
+ hasClass(className: string): boolean;
+
+ /**
+ * Get the HTML contents of the first element in the set of matched elements.
+ */
+ html(): string;
+ /**
+ * Set the HTML contents of each element in the set of matched elements.
+ *
+ * @param htmlString A string of HTML to set as the content of each matched element.
+ */
+ html(htmlString: string): JQuery;
+ /**
+ * Set the HTML contents of each element in the set of matched elements.
+ *
+ * @param func A function returning the HTML content to set. Receives the index position of the element in the set and the old HTML value as arguments. jQuery empties the element before calling the function; use the oldhtml argument to reference the previous content. Within the function, this refers to the current element in the set.
+ */
+ html(func: (index: number, oldhtml: string) => string): JQuery;
+ /**
+ * Set the HTML contents of each element in the set of matched elements.
+ *
+ * @param func A function returning the HTML content to set. Receives the index position of the element in the set and the old HTML value as arguments. jQuery empties the element before calling the function; use the oldhtml argument to reference the previous content. Within the function, this refers to the current element in the set.
+ */
+
+ /**
+ * Get the value of a property for the first element in the set of matched elements.
+ *
+ * @param propertyName The name of the property to get.
+ */
+ prop(propertyName: string): any;
+ /**
+ * Set one or more properties for the set of matched elements.
+ *
+ * @param propertyName The name of the property to set.
+ * @param value A value to set for the property.
+ */
+ prop(propertyName: string, value: string|number|boolean): JQuery;
+ /**
+ * Set one or more properties for the set of matched elements.
+ *
+ * @param properties An object of property-value pairs to set.
+ */
+ prop(properties: Object): JQuery;
+ /**
+ * Set one or more properties for the set of matched elements.
+ *
+ * @param propertyName The name of the property to set.
+ * @param func A function returning the value to set. Receives the index position of the element in the set and the old property value as arguments. Within the function, the keyword this refers to the current element.
+ */
+ prop(propertyName: string, func: (index: number, oldPropertyValue: any) => any): JQuery;
+
+ /**
+ * Remove an attribute from each element in the set of matched elements.
+ *
+ * @param attributeName An attribute to remove; as of version 1.7, it can be a space-separated list of attributes.
+ */
+ removeAttr(attributeName: string): JQuery;
+
+ /**
+ * Remove a single class, multiple classes, or all classes from each element in the set of matched elements.
+ *
+ * @param className One or more space-separated classes to be removed from the class attribute of each matched element.
+ */
+ removeClass(className?: string): JQuery;
+ /**
+ * Remove a single class, multiple classes, or all classes from each element in the set of matched elements.
+ *
+ * @param function A function returning one or more space-separated class names to be removed. Receives the index position of the element in the set and the old class value as arguments.
+ */
+ removeClass(func: (index: number, className: string) => string): JQuery;
+
+ /**
+ * Remove a property for the set of matched elements.
+ *
+ * @param propertyName The name of the property to remove.
+ */
+ removeProp(propertyName: string): JQuery;
+
+ /**
+ * Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument.
+ *
+ * @param className One or more class names (separated by spaces) to be toggled for each element in the matched set.
+ * @param swtch A Boolean (not just truthy/falsy) value to determine whether the class should be added or removed.
+ */
+ toggleClass(className: string, swtch?: boolean): JQuery;
+ /**
+ * Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument.
+ *
+ * @param swtch A boolean value to determine whether the class should be added or removed.
+ */
+ toggleClass(swtch?: boolean): JQuery;
+ /**
+ * Add or remove one or more classes from each element in the set of matched elements, depending on either the class's presence or the value of the switch argument.
+ *
+ * @param func A function that returns class names to be toggled in the class attribute of each element in the matched set. Receives the index position of the element in the set, the old class value, and the switch as arguments.
+ * @param swtch A boolean value to determine whether the class should be added or removed.
+ */
+ toggleClass(func: (index: number, className: string, swtch: boolean) => string, swtch?: boolean): JQuery;
+
+ /**
+ * Get the current value of the first element in the set of matched elements.
+ */
+ val(): any;
+ /**
+ * Set the value of each element in the set of matched elements.
+ *
+ * @param value A string of text or an array of strings corresponding to the value of each matched element to set as selected/checked.
+ */
+ val(value: string|string[]): JQuery;
+ /**
+ * Set the value of each element in the set of matched elements.
+ *
+ * @param func A function returning the value to set. this is the current element. Receives the index position of the element in the set and the old value as arguments.
+ */
+ val(func: (index: number, value: string) => string): JQuery;
+
+
+ /**
+ * Get the value of style properties for the first element in the set of matched elements.
+ *
+ * @param propertyName A CSS property.
+ */
+ css(propertyName: string): string;
+ /**
+ * Set one or more CSS properties for the set of matched elements.
+ *
+ * @param propertyName A CSS property name.
+ * @param value A value to set for the property.
+ */
+ css(propertyName: string, value: string|number): JQuery;
+ /**
+ * Set one or more CSS properties for the set of matched elements.
+ *
+ * @param propertyName A CSS property name.
+ * @param value A function returning the value to set. this is the current element. Receives the index position of the element in the set and the old value as arguments.
+ */
+ css(propertyName: string, value: (index: number, value: string) => string|number): JQuery;
+ /**
+ * Set one or more CSS properties for the set of matched elements.
+ *
+ * @param properties An object of property-value pairs to set.
+ */
+ css(properties: Object): JQuery;
+
+ /**
+ * Get the current computed height for the first element in the set of matched elements.
+ */
+ height(): number;
+ /**
+ * Set the CSS height of every matched element.
+ *
+ * @param value An integer representing the number of pixels, or an integer with an optional unit of measure appended (as a string).
+ */
+ height(value: number|string): JQuery;
+ /**
+ * Set the CSS height of every matched element.
+ *
+ * @param func A function returning the height to set. Receives the index position of the element in the set and the old height as arguments. Within the function, this refers to the current element in the set.
+ */
+ height(func: (index: number, height: number) => number|string): JQuery;
+
+ /**
+ * Get the current computed height for the first element in the set of matched elements, including padding but not border.
+ */
+ innerHeight(): number;
+
+ /**
+ * Sets the inner height on elements in the set of matched elements, including padding but not border.
+ *
+ * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string).
+ */
+ innerHeight(height: number|string): JQuery;
+
+ /**
+ * Get the current computed width for the first element in the set of matched elements, including padding but not border.
+ */
+ innerWidth(): number;
+
+ /**
+ * Sets the inner width on elements in the set of matched elements, including padding but not border.
+ *
+ * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string).
+ */
+ innerWidth(width: number|string): JQuery;
+
+ /**
+ * Get the current coordinates of the first element in the set of matched elements, relative to the document.
+ */
+ offset(): JQueryCoordinates;
+ /**
+ * An object containing the properties top and left, which are integers indicating the new top and left coordinates for the elements.
+ *
+ * @param coordinates An object containing the properties top and left, which are integers indicating the new top and left coordinates for the elements.
+ */
+ offset(coordinates: JQueryCoordinates): JQuery;
+ /**
+ * An object containing the properties top and left, which are integers indicating the new top and left coordinates for the elements.
+ *
+ * @param func A function to return the coordinates to set. Receives the index of the element in the collection as the first argument and the current coordinates as the second argument. The function should return an object with the new top and left properties.
+ */
+ offset(func: (index: number, coords: JQueryCoordinates) => JQueryCoordinates): JQuery;
+
+ /**
+ * Get the current computed height for the first element in the set of matched elements, including padding, border, and optionally margin. Returns an integer (without "px") representation of the value or null if called on an empty set of elements.
+ *
+ * @param includeMargin A Boolean indicating whether to include the element's margin in the calculation.
+ */
+ outerHeight(includeMargin?: boolean): number;
+
+ /**
+ * Sets the outer height on elements in the set of matched elements, including padding and border.
+ *
+ * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string).
+ */
+ outerHeight(height: number|string): JQuery;
+
+ /**
+ * Get the current computed width for the first element in the set of matched elements, including padding and border.
+ *
+ * @param includeMargin A Boolean indicating whether to include the element's margin in the calculation.
+ */
+ outerWidth(includeMargin?: boolean): number;
+
+ /**
+ * Sets the outer width on elements in the set of matched elements, including padding and border.
+ *
+ * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string).
+ */
+ outerWidth(width: number|string): JQuery;
+
+ /**
+ * Get the current coordinates of the first element in the set of matched elements, relative to the offset parent.
+ */
+ position(): JQueryCoordinates;
+
+ /**
+ * Get the current horizontal position of the scroll bar for the first element in the set of matched elements or set the horizontal position of the scroll bar for every matched element.
+ */
+ scrollLeft(): number;
+ /**
+ * Set the current horizontal position of the scroll bar for each of the set of matched elements.
+ *
+ * @param value An integer indicating the new position to set the scroll bar to.
+ */
+ scrollLeft(value: number): JQuery;
+
+ /**
+ * Get the current vertical position of the scroll bar for the first element in the set of matched elements or set the vertical position of the scroll bar for every matched element.
+ */
+ scrollTop(): number;
+ /**
+ * Set the current vertical position of the scroll bar for each of the set of matched elements.
+ *
+ * @param value An integer indicating the new position to set the scroll bar to.
+ */
+ scrollTop(value: number): JQuery;
+
+ /**
+ * Get the current computed width for the first element in the set of matched elements.
+ */
+ width(): number;
+ /**
+ * Set the CSS width of each element in the set of matched elements.
+ *
+ * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string).
+ */
+ width(value: number|string): JQuery;
+ /**
+ * Set the CSS width of each element in the set of matched elements.
+ *
+ * @param func A function returning the width to set. Receives the index position of the element in the set and the old width as arguments. Within the function, this refers to the current element in the set.
+ */
+ width(func: (index: number, width: number) => number|string): JQuery;
+
+ /**
+ * Remove from the queue all items that have not yet been run.
+ *
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ */
+ clearQueue(queueName?: string): JQuery;
+
+ /**
+ * Store arbitrary data associated with the matched elements.
+ *
+ * @param key A string naming the piece of data to set.
+ * @param value The new data value; it can be any Javascript type including Array or Object.
+ */
+ data(key: string, value: any): JQuery;
+ /**
+ * Store arbitrary data associated with the matched elements.
+ *
+ * @param obj An object of key-value pairs of data to update.
+ */
+ data(obj: { [key: string]: any; }): JQuery;
+ /**
+ * Return the value at the named data store for the first element in the jQuery collection, as set by data(name, value) or by an HTML5 data-* attribute.
+ *
+ * @param key Name of the data stored.
+ */
+ data(key: string): any;
+ /**
+ * Return the value at the named data store for the first element in the jQuery collection, as set by data(name, value) or by an HTML5 data-* attribute.
+ */
+ data(): any;
+
+ /**
+ * Execute the next function on the queue for the matched elements.
+ *
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ */
+ dequeue(queueName?: string): JQuery;
+
+ /**
+ * Remove a previously-stored piece of data.
+ *
+ * @param name A string naming the piece of data to delete or space-separated string naming the pieces of data to delete.
+ */
+ removeData(name: string): JQuery;
+ /**
+ * Remove a previously-stored piece of data.
+ *
+ * @param list An array of strings naming the pieces of data to delete.
+ */
+ removeData(list: string[]): JQuery;
+
+ /**
+ * Return a Promise object to observe when all actions of a certain type bound to the collection, queued or not, have finished.
+ *
+ * @param type The type of queue that needs to be observed. (default: fx)
+ * @param target Object onto which the promise methods have to be attached
+ */
+ promise(type?: string, target?: Object): JQueryPromise<any>;
+
+ /**
+ * Perform a custom animation of a set of CSS properties.
+ *
+ * @param properties An object of CSS properties and values that the animation will move toward.
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ animate(properties: Object, duration?: string|number, complete?: Function): JQuery;
+ /**
+ * Perform a custom animation of a set of CSS properties.
+ *
+ * @param properties An object of CSS properties and values that the animation will move toward.
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition. (default: swing)
+ * @param complete A function to call once the animation is complete.
+ */
+ animate(properties: Object, duration?: string|number, easing?: string, complete?: Function): JQuery;
+ /**
+ * Perform a custom animation of a set of CSS properties.
+ *
+ * @param properties An object of CSS properties and values that the animation will move toward.
+ * @param options A map of additional options to pass to the method.
+ */
+ animate(properties: Object, options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Set a timer to delay execution of subsequent items in the queue.
+ *
+ * @param duration An integer indicating the number of milliseconds to delay execution of the next item in the queue.
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ */
+ delay(duration: number, queueName?: string): JQuery;
+
+ /**
+ * Display the matched elements by fading them to opaque.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeIn(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Display the matched elements by fading them to opaque.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeIn(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Display the matched elements by fading them to opaque.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ fadeIn(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Hide the matched elements by fading them to transparent.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeOut(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Hide the matched elements by fading them to transparent.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeOut(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Hide the matched elements by fading them to transparent.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ fadeOut(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Adjust the opacity of the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param opacity A number between 0 and 1 denoting the target opacity.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeTo(duration: string|number, opacity: number, complete?: Function): JQuery;
+ /**
+ * Adjust the opacity of the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param opacity A number between 0 and 1 denoting the target opacity.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeTo(duration: string|number, opacity: number, easing?: string, complete?: Function): JQuery;
+
+ /**
+ * Display or hide the matched elements by animating their opacity.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeToggle(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Display or hide the matched elements by animating their opacity.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ fadeToggle(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Display or hide the matched elements by animating their opacity.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ fadeToggle(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Stop the currently-running animation, remove all queued animations, and complete all animations for the matched elements.
+ *
+ * @param queue The name of the queue in which to stop animations.
+ */
+ finish(queue?: string): JQuery;
+
+ /**
+ * Hide the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ hide(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Hide the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ hide(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Hide the matched elements.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ hide(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Display the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ show(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Display the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ show(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Display the matched elements.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ show(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Display the matched elements with a sliding motion.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ slideDown(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Display the matched elements with a sliding motion.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ slideDown(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Display the matched elements with a sliding motion.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ slideDown(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Display or hide the matched elements with a sliding motion.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ slideToggle(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Display or hide the matched elements with a sliding motion.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ slideToggle(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Display or hide the matched elements with a sliding motion.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ slideToggle(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Hide the matched elements with a sliding motion.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ slideUp(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Hide the matched elements with a sliding motion.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ slideUp(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Hide the matched elements with a sliding motion.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ slideUp(options: JQueryAnimationOptions): JQuery;
+
+ /**
+ * Stop the currently-running animation on the matched elements.
+ *
+ * @param clearQueue A Boolean indicating whether to remove queued animation as well. Defaults to false.
+ * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately. Defaults to false.
+ */
+ stop(clearQueue?: boolean, jumpToEnd?: boolean): JQuery;
+ /**
+ * Stop the currently-running animation on the matched elements.
+ *
+ * @param queue The name of the queue in which to stop animations.
+ * @param clearQueue A Boolean indicating whether to remove queued animation as well. Defaults to false.
+ * @param jumpToEnd A Boolean indicating whether to complete the current animation immediately. Defaults to false.
+ */
+ stop(queue?: string, clearQueue?: boolean, jumpToEnd?: boolean): JQuery;
+
+ /**
+ * Display or hide the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param complete A function to call once the animation is complete.
+ */
+ toggle(duration?: number|string, complete?: Function): JQuery;
+ /**
+ * Display or hide the matched elements.
+ *
+ * @param duration A string or number determining how long the animation will run.
+ * @param easing A string indicating which easing function to use for the transition.
+ * @param complete A function to call once the animation is complete.
+ */
+ toggle(duration?: number|string, easing?: string, complete?: Function): JQuery;
+ /**
+ * Display or hide the matched elements.
+ *
+ * @param options A map of additional options to pass to the method.
+ */
+ toggle(options: JQueryAnimationOptions): JQuery;
+ /**
+ * Display or hide the matched elements.
+ *
+ * @param showOrHide A Boolean indicating whether to show or hide the elements.
+ */
+ toggle(showOrHide: boolean): JQuery;
+
+ /**
+ * Attach a handler to an event for the elements.
+ *
+ * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names.
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ bind(eventType: string, eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Attach a handler to an event for the elements.
+ *
+ * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ bind(eventType: string, handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Attach a handler to an event for the elements.
+ *
+ * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names.
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param preventBubble Setting the third argument to false will attach a function that prevents the default action from occurring and stops the event from bubbling. The default is true.
+ */
+ bind(eventType: string, eventData: any, preventBubble: boolean): JQuery;
+ /**
+ * Attach a handler to an event for the elements.
+ *
+ * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names.
+ * @param preventBubble Setting the third argument to false will attach a function that prevents the default action from occurring and stops the event from bubbling. The default is true.
+ */
+ bind(eventType: string, preventBubble: boolean): JQuery;
+ /**
+ * Attach a handler to an event for the elements.
+ *
+ * @param events An object containing one or more DOM event types and functions to execute for them.
+ */
+ bind(events: any): JQuery;
+
+ /**
+ * Trigger the "blur" event on an element
+ */
+ blur(): JQuery;
+ /**
+ * Bind an event handler to the "blur" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ blur(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "blur" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ blur(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "change" event on an element.
+ */
+ change(): JQuery;
+ /**
+ * Bind an event handler to the "change" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ change(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "change" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ change(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "click" event on an element.
+ */
+ click(): JQuery;
+ /**
+ * Bind an event handler to the "click" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ */
+ click(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "click" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ click(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "dblclick" event on an element.
+ */
+ dblclick(): JQuery;
+ /**
+ * Bind an event handler to the "dblclick" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ dblclick(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "dblclick" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ dblclick(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ delegate(selector: any, eventType: string, handler: (eventObject: JQueryEventObject) => any): JQuery;
+ delegate(selector: any, eventType: string, eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "focus" event on an element.
+ */
+ focus(): JQuery;
+ /**
+ * Bind an event handler to the "focus" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ focus(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "focus" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ focus(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Bind an event handler to the "focusin" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ focusin(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "focusin" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ focusin(eventData: Object, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Bind an event handler to the "focusout" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ focusout(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "focusout" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ focusout(eventData: Object, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Bind two handlers to the matched elements, to be executed when the mouse pointer enters and leaves the elements.
+ *
+ * @param handlerIn A function to execute when the mouse pointer enters the element.
+ * @param handlerOut A function to execute when the mouse pointer leaves the element.
+ */
+ hover(handlerIn: (eventObject: JQueryEventObject) => any, handlerOut: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind a single handler to the matched elements, to be executed when the mouse pointer enters or leaves the elements.
+ *
+ * @param handlerInOut A function to execute when the mouse pointer enters or leaves the element.
+ */
+ hover(handlerInOut: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "keydown" event on an element.
+ */
+ keydown(): JQuery;
+ /**
+ * Bind an event handler to the "keydown" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ keydown(handler: (eventObject: JQueryKeyEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "keydown" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ keydown(eventData?: any, handler?: (eventObject: JQueryKeyEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "keypress" event on an element.
+ */
+ keypress(): JQuery;
+ /**
+ * Bind an event handler to the "keypress" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ keypress(handler: (eventObject: JQueryKeyEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "keypress" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ keypress(eventData?: any, handler?: (eventObject: JQueryKeyEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "keyup" event on an element.
+ */
+ keyup(): JQuery;
+ /**
+ * Bind an event handler to the "keyup" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ keyup(handler: (eventObject: JQueryKeyEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "keyup" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ keyup(eventData?: any, handler?: (eventObject: JQueryKeyEventObject) => any): JQuery;
+
+ /**
+ * Bind an event handler to the "load" JavaScript event.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ load(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "load" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ load(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mousedown" event on an element.
+ */
+ mousedown(): JQuery;
+ /**
+ * Bind an event handler to the "mousedown" JavaScript event.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mousedown(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "mousedown" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mousedown(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mouseenter" event on an element.
+ */
+ mouseenter(): JQuery;
+ /**
+ * Bind an event handler to be fired when the mouse enters an element.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseenter(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to be fired when the mouse enters an element.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseenter(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mouseleave" event on an element.
+ */
+ mouseleave(): JQuery;
+ /**
+ * Bind an event handler to be fired when the mouse leaves an element.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseleave(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to be fired when the mouse leaves an element.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseleave(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mousemove" event on an element.
+ */
+ mousemove(): JQuery;
+ /**
+ * Bind an event handler to the "mousemove" JavaScript event.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mousemove(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "mousemove" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mousemove(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mouseout" event on an element.
+ */
+ mouseout(): JQuery;
+ /**
+ * Bind an event handler to the "mouseout" JavaScript event.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseout(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "mouseout" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseout(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mouseover" event on an element.
+ */
+ mouseover(): JQuery;
+ /**
+ * Bind an event handler to the "mouseover" JavaScript event.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseover(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "mouseover" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseover(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "mouseup" event on an element.
+ */
+ mouseup(): JQuery;
+ /**
+ * Bind an event handler to the "mouseup" JavaScript event.
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseup(handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "mouseup" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ mouseup(eventData: Object, handler: (eventObject: JQueryMouseEventObject) => any): JQuery;
+
+ /**
+ * Remove an event handler.
+ */
+ off(): JQuery;
+ /**
+ * Remove an event handler.
+ *
+ * @param events One or more space-separated event types and optional namespaces, or just namespaces, such as "click", "keydown.myPlugin", or ".myPlugin".
+ * @param selector A selector which should match the one originally passed to .on() when attaching event handlers.
+ * @param handler A handler function previously attached for the event(s), or the special value false.
+ */
+ off(events: string, selector?: string, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Remove an event handler.
+ *
+ * @param events One or more space-separated event types and optional namespaces, or just namespaces, such as "click", "keydown.myPlugin", or ".myPlugin".
+ * @param handler A handler function previously attached for the event(s), or the special value false.
+ */
+ off(events: string, handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Remove an event handler.
+ *
+ * @param events An object where the string keys represent one or more space-separated event types and optional namespaces, and the values represent handler functions previously attached for the event(s).
+ * @param selector A selector which should match the one originally passed to .on() when attaching event handlers.
+ */
+ off(events: { [key: string]: any; }, selector?: string): JQuery;
+
+ /**
+ * Attach an event handler function for one or more events to the selected elements.
+ *
+ * @param events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
+ * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false. Rest parameter args is for optional parameters passed to jQuery.trigger(). Note that the actual parameters on the event handler function must be marked as optional (? syntax).
+ */
+ on(events: string, handler: (eventObject: JQueryEventObject, ...args: any[]) => any): JQuery;
+ /**
+ * Attach an event handler function for one or more events to the selected elements.
+ *
+ * @param events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
+ * @param data Data to be passed to the handler in event.data when an event is triggered.
+ * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.
+ */
+ on(events: string, data : any, handler: (eventObject: JQueryEventObject, ...args: any[]) => any): JQuery;
+ /**
+ * Attach an event handler function for one or more events to the selected elements.
+ *
+ * @param events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
+ * @param selector A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.
+ * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.
+ */
+ on(events: string, selector: string, handler: (eventObject: JQueryEventObject, ...eventData: any[]) => any): JQuery;
+ /**
+ * Attach an event handler function for one or more events to the selected elements.
+ *
+ * @param events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
+ * @param selector A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.
+ * @param data Data to be passed to the handler in event.data when an event is triggered.
+ * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.
+ */
+ on(events: string, selector: string, data: any, handler: (eventObject: JQueryEventObject, ...eventData: any[]) => any): JQuery;
+ /**
+ * Attach an event handler function for one or more events to the selected elements.
+ *
+ * @param events An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s).
+ * @param selector A selector string to filter the descendants of the selected elements that will call the handler. If the selector is null or omitted, the handler is always called when it reaches the selected element.
+ * @param data Data to be passed to the handler in event.data when an event occurs.
+ */
+ on(events: { [key: string]: any; }, selector?: string, data?: any): JQuery;
+ /**
+ * Attach an event handler function for one or more events to the selected elements.
+ *
+ * @param events An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s).
+ * @param data Data to be passed to the handler in event.data when an event occurs.
+ */
+ on(events: { [key: string]: any; }, data?: any): JQuery;
+
+ /**
+ * Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
+ *
+ * @param events A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names.
+ * @param handler A function to execute at the time the event is triggered.
+ */
+ one(events: string, handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
+ *
+ * @param events A string containing one or more JavaScript event types, such as "click" or "submit," or custom event names.
+ * @param data An object containing data that will be passed to the event handler.
+ * @param handler A function to execute at the time the event is triggered.
+ */
+ one(events: string, data: Object, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
+ *
+ * @param events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
+ * @param selector A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.
+ * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.
+ */
+ one(events: string, selector: string, handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
+ *
+ * @param events One or more space-separated event types and optional namespaces, such as "click" or "keydown.myPlugin".
+ * @param selector A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.
+ * @param data Data to be passed to the handler in event.data when an event is triggered.
+ * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false.
+ */
+ one(events: string, selector: string, data: any, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
+ *
+ * @param events An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s).
+ * @param selector A selector string to filter the descendants of the selected elements that will call the handler. If the selector is null or omitted, the handler is always called when it reaches the selected element.
+ * @param data Data to be passed to the handler in event.data when an event occurs.
+ */
+ one(events: { [key: string]: any; }, selector?: string, data?: any): JQuery;
+
+ /**
+ * Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
+ *
+ * @param events An object in which the string keys represent one or more space-separated event types and optional namespaces, and the values represent a handler function to be called for the event(s).
+ * @param data Data to be passed to the handler in event.data when an event occurs.
+ */
+ one(events: { [key: string]: any; }, data?: any): JQuery;
+
+
+ /**
+ * Specify a function to execute when the DOM is fully loaded.
+ *
+ * @param handler A function to execute after the DOM is ready.
+ */
+ ready(handler: (jQueryAlias?: JQueryStatic) => any): JQuery;
+
+ /**
+ * Trigger the "resize" event on an element.
+ */
+ resize(): JQuery;
+ /**
+ * Bind an event handler to the "resize" JavaScript event.
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ resize(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "resize" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ resize(eventData: Object, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "scroll" event on an element.
+ */
+ scroll(): JQuery;
+ /**
+ * Bind an event handler to the "scroll" JavaScript event.
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ scroll(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "scroll" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ scroll(eventData: Object, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "select" event on an element.
+ */
+ select(): JQuery;
+ /**
+ * Bind an event handler to the "select" JavaScript event.
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ select(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "select" JavaScript event.
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ select(eventData: Object, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Trigger the "submit" event on an element.
+ */
+ submit(): JQuery;
+ /**
+ * Bind an event handler to the "submit" JavaScript event
+ *
+ * @param handler A function to execute each time the event is triggered.
+ */
+ submit(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "submit" JavaScript event
+ *
+ * @param eventData An object containing data that will be passed to the event handler.
+ * @param handler A function to execute each time the event is triggered.
+ */
+ submit(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Execute all handlers and behaviors attached to the matched elements for the given event type.
+ *
+ * @param eventType A string containing a JavaScript event type, such as click or submit.
+ * @param extraParameters Additional parameters to pass along to the event handler.
+ */
+ trigger(eventType: string, extraParameters?: any[]|Object): JQuery;
+ /**
+ * Execute all handlers and behaviors attached to the matched elements for the given event type.
+ *
+ * @param event A jQuery.Event object.
+ * @param extraParameters Additional parameters to pass along to the event handler.
+ */
+ trigger(event: JQueryEventObject, extraParameters?: any[]|Object): JQuery;
+
+ /**
+ * Execute all handlers attached to an element for an event.
+ *
+ * @param eventType A string containing a JavaScript event type, such as click or submit.
+ * @param extraParameters An array of additional parameters to pass along to the event handler.
+ */
+ triggerHandler(eventType: string, ...extraParameters: any[]): Object;
+
+ /**
+ * Remove a previously-attached event handler from the elements.
+ *
+ * @param eventType A string containing a JavaScript event type, such as click or submit.
+ * @param handler The function that is to be no longer executed.
+ */
+ unbind(eventType?: string, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Remove a previously-attached event handler from the elements.
+ *
+ * @param eventType A string containing a JavaScript event type, such as click or submit.
+ * @param fls Unbinds the corresponding 'return false' function that was bound using .bind( eventType, false ).
+ */
+ unbind(eventType: string, fls: boolean): JQuery;
+ /**
+ * Remove a previously-attached event handler from the elements.
+ *
+ * @param evt A JavaScript event object as passed to an event handler.
+ */
+ unbind(evt: any): JQuery;
+
+ /**
+ * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements.
+ */
+ undelegate(): JQuery;
+ /**
+ * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements.
+ *
+ * @param selector A selector which will be used to filter the event results.
+ * @param eventType A string containing a JavaScript event type, such as "click" or "keydown"
+ * @param handler A function to execute at the time the event is triggered.
+ */
+ undelegate(selector: string, eventType: string, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements.
+ *
+ * @param selector A selector which will be used to filter the event results.
+ * @param events An object of one or more event types and previously bound functions to unbind from them.
+ */
+ undelegate(selector: string, events: Object): JQuery;
+ /**
+ * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements.
+ *
+ * @param namespace A string containing a namespace to unbind all events from.
+ */
+ undelegate(namespace: string): JQuery;
+
+ /**
+ * Bind an event handler to the "unload" JavaScript event. (DEPRECATED from v1.8)
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ unload(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "unload" JavaScript event. (DEPRECATED from v1.8)
+ *
+ * @param eventData A plain object of data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ unload(eventData?: any, handler?: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * The DOM node context originally passed to jQuery(); if none was passed then context will likely be the document. (DEPRECATED from v1.10)
+ */
+ context: Element;
+
+ jquery: string;
+
+ /**
+ * Bind an event handler to the "error" JavaScript event. (DEPRECATED from v1.8)
+ *
+ * @param handler A function to execute when the event is triggered.
+ */
+ error(handler: (eventObject: JQueryEventObject) => any): JQuery;
+ /**
+ * Bind an event handler to the "error" JavaScript event. (DEPRECATED from v1.8)
+ *
+ * @param eventData A plain object of data that will be passed to the event handler.
+ * @param handler A function to execute when the event is triggered.
+ */
+ error(eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery;
+
+ /**
+ * Add a collection of DOM elements onto the jQuery stack.
+ *
+ * @param elements An array of elements to push onto the stack and make into a new jQuery object.
+ */
+ pushStack(elements: any[]): JQuery;
+ /**
+ * Add a collection of DOM elements onto the jQuery stack.
+ *
+ * @param elements An array of elements to push onto the stack and make into a new jQuery object.
+ * @param name The name of a jQuery method that generated the array of elements.
+ * @param arguments The arguments that were passed in to the jQuery method (for serialization).
+ */
+ pushStack(elements: any[], name: string, arguments: any[]): JQuery;
+
+ /**
+ * Insert content, specified by the parameter, after each element in the set of matched elements.
+ *
+ * param content1 HTML string, DOM element, array of elements, or jQuery object to insert after each element in the set of matched elements.
+ * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert after each element in the set of matched elements.
+ */
+ after(content1: JQuery|any[]|Element|Text|string, ...content2: any[]): JQuery;
+ /**
+ * Insert content, specified by the parameter, after each element in the set of matched elements.
+ *
+ * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert after each element in the set of matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set.
+ */
+ after(func: (index: number, html: string) => string|Element|JQuery): JQuery;
+
+ /**
+ * Insert content, specified by the parameter, to the end of each element in the set of matched elements.
+ *
+ * param content1 DOM element, array of elements, HTML string, or jQuery object to insert at the end of each element in the set of matched elements.
+ * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements.
+ */
+ append(content1: JQuery|any[]|Element|Text|string, ...content2: any[]): JQuery;
+ /**
+ * Insert content, specified by the parameter, to the end of each element in the set of matched elements.
+ *
+ * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert at the end of each element in the set of matched elements. Receives the index position of the element in the set and the old HTML value of the element as arguments. Within the function, this refers to the current element in the set.
+ */
+ append(func: (index: number, html: string) => string|Element|JQuery): JQuery;
+
+ /**
+ * Insert every element in the set of matched elements to the end of the target.
+ *
+ * @param target A selector, element, HTML string, array of elements, or jQuery object; the matched set of elements will be inserted at the end of the element(s) specified by this parameter.
+ */
+ appendTo(target: JQuery|any[]|Element|string): JQuery;
+
+ /**
+ * Insert content, specified by the parameter, before each element in the set of matched elements.
+ *
+ * param content1 HTML string, DOM element, array of elements, or jQuery object to insert before each element in the set of matched elements.
+ * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert before each element in the set of matched elements.
+ */
+ before(content1: JQuery|any[]|Element|Text|string, ...content2: any[]): JQuery;
+ /**
+ * Insert content, specified by the parameter, before each element in the set of matched elements.
+ *
+ * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert before each element in the set of matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set.
+ */
+ before(func: (index: number, html: string) => string|Element|JQuery): JQuery;
+
+ /**
+ * Create a deep copy of the set of matched elements.
+ *
+ * param withDataAndEvents A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false.
+ * param deepWithDataAndEvents A Boolean indicating whether event handlers and data for all children of the cloned element should be copied. By default its value matches the first argument's value (which defaults to false).
+ */
+ clone(withDataAndEvents?: boolean, deepWithDataAndEvents?: boolean): JQuery;
+
+ /**
+ * Remove the set of matched elements from the DOM.
+ *
+ * param selector A selector expression that filters the set of matched elements to be removed.
+ */
+ detach(selector?: string): JQuery;
+
+ /**
+ * Remove all child nodes of the set of matched elements from the DOM.
+ */
+ empty(): JQuery;
+
+ /**
+ * Insert every element in the set of matched elements after the target.
+ *
+ * param target A selector, element, array of elements, HTML string, or jQuery object; the matched set of elements will be inserted after the element(s) specified by this parameter.
+ */
+ insertAfter(target: JQuery|any[]|Element|Text|string): JQuery;
+
+ /**
+ * Insert every element in the set of matched elements before the target.
+ *
+ * param target A selector, element, array of elements, HTML string, or jQuery object; the matched set of elements will be inserted before the element(s) specified by this parameter.
+ */
+ insertBefore(target: JQuery|any[]|Element|Text|string): JQuery;
+
+ /**
+ * Insert content, specified by the parameter, to the beginning of each element in the set of matched elements.
+ *
+ * param content1 DOM element, array of elements, HTML string, or jQuery object to insert at the beginning of each element in the set of matched elements.
+ * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the beginning of each element in the set of matched elements.
+ */
+ prepend(content1: JQuery|any[]|Element|Text|string, ...content2: any[]): JQuery;
+ /**
+ * Insert content, specified by the parameter, to the beginning of each element in the set of matched elements.
+ *
+ * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert at the beginning of each element in the set of matched elements. Receives the index position of the element in the set and the old HTML value of the element as arguments. Within the function, this refers to the current element in the set.
+ */
+ prepend(func: (index: number, html: string) => string|Element|JQuery): JQuery;
+
+ /**
+ * Insert every element in the set of matched elements to the beginning of the target.
+ *
+ * @param target A selector, element, HTML string, array of elements, or jQuery object; the matched set of elements will be inserted at the beginning of the element(s) specified by this parameter.
+ */
+ prependTo(target: JQuery|any[]|Element|string): JQuery;
+
+ /**
+ * Remove the set of matched elements from the DOM.
+ *
+ * @param selector A selector expression that filters the set of matched elements to be removed.
+ */
+ remove(selector?: string): JQuery;
+
+ /**
+ * Replace each target element with the set of matched elements.
+ *
+ * @param target A selector string, jQuery object, DOM element, or array of elements indicating which element(s) to replace.
+ */
+ replaceAll(target: JQuery|any[]|Element|string): JQuery;
+
+ /**
+ * Replace each element in the set of matched elements with the provided new content and return the set of elements that was removed.
+ *
+ * param newContent The content to insert. May be an HTML string, DOM element, array of DOM elements, or jQuery object.
+ */
+ replaceWith(newContent: JQuery|any[]|Element|Text|string): JQuery;
+ /**
+ * Replace each element in the set of matched elements with the provided new content and return the set of elements that was removed.
+ *
+ * param func A function that returns content with which to replace the set of matched elements.
+ */
+ replaceWith(func: () => Element|JQuery): JQuery;
+
+ /**
+ * Get the combined text contents of each element in the set of matched elements, including their descendants.
+ */
+ text(): string;
+ /**
+ * Set the content of each element in the set of matched elements to the specified text.
+ *
+ * @param text The text to set as the content of each matched element. When Number or Boolean is supplied, it will be converted to a String representation.
+ */
+ text(text: string|number|boolean): JQuery;
+ /**
+ * Set the content of each element in the set of matched elements to the specified text.
+ *
+ * @param func A function returning the text content to set. Receives the index position of the element in the set and the old text value as arguments.
+ */
+ text(func: (index: number, text: string) => string): JQuery;
+
+ /**
+ * Retrieve all the elements contained in the jQuery set, as an array.
+ */
+ toArray(): any[];
+
+ /**
+ * Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place.
+ */
+ unwrap(): JQuery;
+
+ /**
+ * Wrap an HTML structure around each element in the set of matched elements.
+ *
+ * @param wrappingElement A selector, element, HTML string, or jQuery object specifying the structure to wrap around the matched elements.
+ */
+ wrap(wrappingElement: JQuery|Element|string): JQuery;
+ /**
+ * Wrap an HTML structure around each element in the set of matched elements.
+ *
+ * @param func A callback function returning the HTML content or jQuery object to wrap around the matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set.
+ */
+ wrap(func: (index: number) => string|JQuery): JQuery;
+
+ /**
+ * Wrap an HTML structure around all elements in the set of matched elements.
+ *
+ * @param wrappingElement A selector, element, HTML string, or jQuery object specifying the structure to wrap around the matched elements.
+ */
+ wrapAll(wrappingElement: JQuery|Element|string): JQuery;
+ wrapAll(func: (index: number) => string): JQuery;
+
+ /**
+ * Wrap an HTML structure around the content of each element in the set of matched elements.
+ *
+ * @param wrappingElement An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the content of the matched elements.
+ */
+ wrapInner(wrappingElement: JQuery|Element|string): JQuery;
+ /**
+ * Wrap an HTML structure around the content of each element in the set of matched elements.
+ *
+ * @param func A callback function which generates a structure to wrap around the content of the matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set.
+ */
+ wrapInner(func: (index: number) => string): JQuery;
+
+ /**
+ * Iterate over a jQuery object, executing a function for each matched element.
+ *
+ * @param func A function to execute for each matched element.
+ */
+ each(func: (index: number, elem: Element) => any): JQuery;
+
+ /**
+ * Retrieve one of the elements matched by the jQuery object.
+ *
+ * @param index A zero-based integer indicating which element to retrieve.
+ */
+ get(index: number): HTMLElement;
+ /**
+ * Retrieve the elements matched by the jQuery object.
+ */
+ get(): any[];
+
+ /**
+ * Search for a given element from among the matched elements.
+ */
+ index(): number;
+ /**
+ * Search for a given element from among the matched elements.
+ *
+ * @param selector A selector representing a jQuery collection in which to look for an element.
+ */
+ index(selector: string|JQuery|Element): number;
+
+ /**
+ * The number of elements in the jQuery object.
+ */
+ length: number;
+ /**
+ * A selector representing selector passed to jQuery(), if any, when creating the original set.
+ * version deprecated: 1.7, removed: 1.9
+ */
+ selector: string;
+ [index: string]: any;
+ [index: number]: HTMLElement;
+
+ /**
+ * Add elements to the set of matched elements.
+ *
+ * @param selector A string representing a selector expression to find additional elements to add to the set of matched elements.
+ * @param context The point in the document at which the selector should begin matching; similar to the context argument of the $(selector, context) method.
+ */
+ add(selector: string, context?: Element): JQuery;
+ /**
+ * Add elements to the set of matched elements.
+ *
+ * @param elements One or more elements to add to the set of matched elements.
+ */
+ add(...elements: Element[]): JQuery;
+ /**
+ * Add elements to the set of matched elements.
+ *
+ * @param html An HTML fragment to add to the set of matched elements.
+ */
+ add(html: string): JQuery;
+ /**
+ * Add elements to the set of matched elements.
+ *
+ * @param obj An existing jQuery object to add to the set of matched elements.
+ */
+ add(obj: JQuery): JQuery;
+
+ /**
+ * Get the children of each element in the set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ children(selector?: string): JQuery;
+
+ /**
+ * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ closest(selector: string): JQuery;
+ /**
+ * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ * @param context A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead.
+ */
+ closest(selector: string, context?: Element): JQuery;
+ /**
+ * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
+ *
+ * @param obj A jQuery object to match elements against.
+ */
+ closest(obj: JQuery): JQuery;
+ /**
+ * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
+ *
+ * @param element An element to match elements against.
+ */
+ closest(element: Element): JQuery;
+
+ /**
+ * Get an array of all the elements and selectors matched against the current element up through the DOM tree.
+ *
+ * @param selectors An array or string containing a selector expression to match elements against (can also be a jQuery object).
+ * @param context A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead.
+ */
+ closest(selectors: any, context?: Element): any[];
+
+ /**
+ * Get the children of each element in the set of matched elements, including text and comment nodes.
+ */
+ contents(): JQuery;
+
+ /**
+ * End the most recent filtering operation in the current chain and return the set of matched elements to its previous state.
+ */
+ end(): JQuery;
+
+ /**
+ * Reduce the set of matched elements to the one at the specified index.
+ *
+ * @param index An integer indicating the 0-based position of the element. OR An integer indicating the position of the element, counting backwards from the last element in the set.
+ *
+ */
+ eq(index: number): JQuery;
+
+ /**
+ * Reduce the set of matched elements to those that match the selector or pass the function's test.
+ *
+ * @param selector A string containing a selector expression to match the current set of elements against.
+ */
+ filter(selector: string): JQuery;
+ /**
+ * Reduce the set of matched elements to those that match the selector or pass the function's test.
+ *
+ * @param func A function used as a test for each element in the set. this is the current DOM element.
+ */
+ filter(func: (index: number, element: Element) => any): JQuery;
+ /**
+ * Reduce the set of matched elements to those that match the selector or pass the function's test.
+ *
+ * @param element An element to match the current set of elements against.
+ */
+ filter(element: Element): JQuery;
+ /**
+ * Reduce the set of matched elements to those that match the selector or pass the function's test.
+ *
+ * @param obj An existing jQuery object to match the current set of elements against.
+ */
+ filter(obj: JQuery): JQuery;
+
+ /**
+ * Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ find(selector: string): JQuery;
+ /**
+ * Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
+ *
+ * @param element An element to match elements against.
+ */
+ find(element: Element): JQuery;
+ /**
+ * Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
+ *
+ * @param obj A jQuery object to match elements against.
+ */
+ find(obj: JQuery): JQuery;
+
+ /**
+ * Reduce the set of matched elements to the first in the set.
+ */
+ first(): JQuery;
+
+ /**
+ * Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ has(selector: string): JQuery;
+ /**
+ * Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element.
+ *
+ * @param contained A DOM element to match elements against.
+ */
+ has(contained: Element): JQuery;
+
+ /**
+ * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ is(selector: string): boolean;
+ /**
+ * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
+ *
+ * @param func A function used as a test for the set of elements. It accepts one argument, index, which is the element's index in the jQuery collection.Within the function, this refers to the current DOM element.
+ */
+ is(func: (index: number, element: Element) => boolean): boolean;
+ /**
+ * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
+ *
+ * @param obj An existing jQuery object to match the current set of elements against.
+ */
+ is(obj: JQuery): boolean;
+ /**
+ * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
+ *
+ * @param elements One or more elements to match the current set of elements against.
+ */
+ is(elements: any): boolean;
+
+ /**
+ * Reduce the set of matched elements to the final one in the set.
+ */
+ last(): JQuery;
+
+ /**
+ * Pass each element in the current matched set through a function, producing a new jQuery object containing the return values.
+ *
+ * @param callback A function object that will be invoked for each element in the current set.
+ */
+ map(callback: (index: number, domElement: Element) => any): JQuery;
+
+ /**
+ * Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ next(selector?: string): JQuery;
+
+ /**
+ * Get all following siblings of each element in the set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ nextAll(selector?: string): JQuery;
+
+ /**
+ * Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed.
+ *
+ * @param selector A string containing a selector expression to indicate where to stop matching following sibling elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ nextUntil(selector?: string, filter?: string): JQuery;
+ /**
+ * Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed.
+ *
+ * @param element A DOM node or jQuery object indicating where to stop matching following sibling elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ nextUntil(element?: Element, filter?: string): JQuery;
+ /**
+ * Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed.
+ *
+ * @param obj A DOM node or jQuery object indicating where to stop matching following sibling elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ nextUntil(obj?: JQuery, filter?: string): JQuery;
+
+ /**
+ * Remove elements from the set of matched elements.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ not(selector: string): JQuery;
+ /**
+ * Remove elements from the set of matched elements.
+ *
+ * @param func A function used as a test for each element in the set. this is the current DOM element.
+ */
+ not(func: (index: number, element: Element) => boolean): JQuery;
+ /**
+ * Remove elements from the set of matched elements.
+ *
+ * @param elements One or more DOM elements to remove from the matched set.
+ */
+ not(...elements: Element[]): JQuery;
+ /**
+ * Remove elements from the set of matched elements.
+ *
+ * @param obj An existing jQuery object to match the current set of elements against.
+ */
+ not(obj: JQuery): JQuery;
+
+ /**
+ * Get the closest ancestor element that is positioned.
+ */
+ offsetParent(): JQuery;
+
+ /**
+ * Get the parent of each element in the current set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ parent(selector?: string): JQuery;
+
+ /**
+ * Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ parents(selector?: string): JQuery;
+
+ /**
+ * Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object.
+ *
+ * @param selector A string containing a selector expression to indicate where to stop matching ancestor elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ parentsUntil(selector?: string, filter?: string): JQuery;
+ /**
+ * Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object.
+ *
+ * @param element A DOM node or jQuery object indicating where to stop matching ancestor elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ parentsUntil(element?: Element, filter?: string): JQuery;
+ /**
+ * Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object.
+ *
+ * @param obj A DOM node or jQuery object indicating where to stop matching ancestor elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ parentsUntil(obj?: JQuery, filter?: string): JQuery;
+
+ /**
+ * Get the immediately preceding sibling of each element in the set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ prev(selector?: string): JQuery;
+
+ /**
+ * Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ prevAll(selector?: string): JQuery;
+
+ /**
+ * Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object.
+ *
+ * @param selector A string containing a selector expression to indicate where to stop matching preceding sibling elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ prevUntil(selector?: string, filter?: string): JQuery;
+ /**
+ * Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object.
+ *
+ * @param element A DOM node or jQuery object indicating where to stop matching preceding sibling elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ prevUntil(element?: Element, filter?: string): JQuery;
+ /**
+ * Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object.
+ *
+ * @param obj A DOM node or jQuery object indicating where to stop matching preceding sibling elements.
+ * @param filter A string containing a selector expression to match elements against.
+ */
+ prevUntil(obj?: JQuery, filter?: string): JQuery;
+
+ /**
+ * Get the siblings of each element in the set of matched elements, optionally filtered by a selector.
+ *
+ * @param selector A string containing a selector expression to match elements against.
+ */
+ siblings(selector?: string): JQuery;
+
+ /**
+ * Reduce the set of matched elements to a subset specified by a range of indices.
+ *
+ * @param start An integer indicating the 0-based position at which the elements begin to be selected. If negative, it indicates an offset from the end of the set.
+ * @param end An integer indicating the 0-based position at which the elements stop being selected. If negative, it indicates an offset from the end of the set. If omitted, the range continues until the end of the set.
+ */
+ slice(start: number, end?: number): JQuery;
+
+ /**
+ * Show the queue of functions to be executed on the matched elements.
+ *
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ */
+ queue(queueName?: string): any[];
+ /**
+ * Manipulate the queue of functions to be executed, once for each matched element.
+ *
+ * @param newQueue An array of functions to replace the current queue contents.
+ */
+ queue(newQueue: Function[]): JQuery;
+ /**
+ * Manipulate the queue of functions to be executed, once for each matched element.
+ *
+ * @param callback The new function to add to the queue, with a function to call that will dequeue the next item.
+ */
+ queue(callback: Function): JQuery;
+ /**
+ * Manipulate the queue of functions to be executed, once for each matched element.
+ *
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ * @param newQueue An array of functions to replace the current queue contents.
+ */
+ queue(queueName: string, newQueue: Function[]): JQuery;
+ /**
+ * Manipulate the queue of functions to be executed, once for each matched element.
+ *
+ * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue.
+ * @param callback The new function to add to the queue, with a function to call that will dequeue the next item.
+ */
+ queue(queueName: string, callback: Function): JQuery;
+}
+declare module "jquery" {
+ export = $;
+}
+declare var jQuery: JQueryStatic;
+declare var $: JQueryStatic;
diff --git a/catalog-ui/typings/jsMd5/md5.d.ts b/catalog-ui/typings/jsMd5/md5.d.ts
new file mode 100644
index 0000000000..3f06a6f509
--- /dev/null
+++ b/catalog-ui/typings/jsMd5/md5.d.ts
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for js-md5 v0.3.0
+// Project: https://github.com/emn178/js-md5
+// Definitions by: Roland Greim <https://github.com/tigerxy>
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped/
+
+/// <reference path="../jquery/jquery.d.ts"/>
+
+interface JQuery {
+ md5(value: string): string;
+ md5(value: Array<any>): string;
+ md5(value: Uint8Array): string;
+}
+
+interface JQueryStatic {
+ md5(value: string): string;
+ md5(value: Array<any>): string;
+ md5(value: Uint8Array): string;
+}
+
+interface md5 {
+ (value: string): string;
+ (value: Array<any>): string;
+ (value: Uint8Array): string;
+}
+
+interface String {
+ md5(value: string): string;
+ md5(value: Array<any>): string;
+ md5(value: Uint8Array): string;
+}
+
+declare module "js-md5" {
+ export = md5;
+}
+
+declare var md5: md5;
diff --git a/catalog-ui/typings/lodash/lodash.d.ts b/catalog-ui/typings/lodash/lodash.d.ts
new file mode 100644
index 0000000000..9d23982103
--- /dev/null
+++ b/catalog-ui/typings/lodash/lodash.d.ts
@@ -0,0 +1,22949 @@
+// Type definitions for Lo-Dash 4.14
+// Project: http://lodash.com/
+// Definitions by: Brian Zengel <https://github.com/bczengel>, Ilya Mochalov <https://github.com/chrootsu>, Stepan Mikhaylyuk <https://github.com/stepancar>
+// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
+
+
+/**
+ ### 4.0.0 Changelog (https://github.com/lodash/lodash/wiki/Changelog)
+
+ #### TODO:
+ removed:
+ - [x] Removed _.support
+ - [x] Removed _.findWhere in favor of _.find with iteratee shorthand
+ - [x] Removed _.where in favor of _.filter with iteratee shorthand
+ - [x] Removed _.pluck in favor of _.map with iteratee shorthand
+
+ renamed:
+ - [x] Renamed _.first to _.head
+ - [x] Renamed _.indexBy to _.keyBy
+ - [x] Renamed _.invoke to _.invokeMap
+ - [x] Renamed _.overArgs to _.overArgs
+ - [x] Renamed _.padLeft & _.padRight to _.padStart & _.padEnd
+ - [x] Renamed _.pairs to _.toPairs
+ - [x] Renamed _.rest to _.tail
+ - [x] Renamed _.restParam to _.rest
+ - [x] Renamed _.sortByOrder to _.orderBy
+ - [x] Renamed _.trimLeft & _.trimRight to _.trimStart & _.trimEnd
+ - [x] Renamed _.trunc to _.truncate
+
+ split:
+ - [x] Split _.indexOf & _.lastIndexOf into _.sortedIndexOf & _.sortedLastIndexOf
+ - [x] Split _.max & _.min into _.maxBy & _.minBy
+ - [x] Split _.omit & _.pick into _.omitBy & _.pickBy
+ - [x] Split _.sample into _.sampleSize
+ - [x] Split _.sortedIndex into _.sortedIndexBy
+ - [x] Split _.sortedLastIndex into _.sortedLastIndexBy
+ - [x] Split _.uniq into _.sortedUniq, _.sortedUniqBy, & _.uniqBy
+
+ changes:
+ - [x] Absorbed _.sortByAll into _.sortBy
+ - [x] Changed the category of _.at to “Objectâ€
+ - [x] Changed the category of _.bindAll to “Utilityâ€
+ - [x] Made _.capitalize uppercase the first character & lowercase the rest
+ - [x] Made _.functions return only own method names
+
+
+ added 23 array methods:
+ - [x] _.concat
+ - [x] _.differenceBy
+ - [x] _.differenceWith
+ - [x] _.flatMap
+ - [x] _.fromPairs
+ - [x] _.intersectionBy
+ - [x] _.intersectionWith
+ - [x] _.join
+ - [x] _.pullAll
+ - [x] _.pullAllBy
+ - [x] _.reverse
+ - [x] _.sortedIndexBy
+ - [x] _.sortedIndexOf
+ - [x] _.sortedLastIndexBy
+ - [x] _.sortedLastIndexOf
+ - [x] _.sortedUniq
+ - [x] _.sortedUniqBy
+ - [x] _.unionBy
+ - [x] _.unionWith
+ - [x] _.uniqBy
+ - [x] _.uniqWith
+ - [x] _.xorBy
+ - [x] _.xorWith
+
+ added 18 lang methods:
+ - [x] _.cloneDeepWith
+ - [x] _.cloneWith
+ - [x] _.eq
+ - [x] _.isArrayLike
+ - [x] _.isArrayLikeObject
+ - [x] _.isEqualWith
+ - [x] _.isInteger
+ - [x] _.isLength
+ - [x] _.isMatchWith
+ - [x] _.isNil
+ - [x] _.isObjectLike
+ - [x] _.isSafeInteger
+ - [x] _.isSymbol
+ - [x] _.toInteger
+ - [x] _.toLength
+ - [x] _.toNumber
+ - [x] _.toSafeInteger
+ - [x] _.toString
+
+ added 13 object methods:
+ - [x] _.assignIn
+ - [x] _.assignInWith
+ - [x] _.assignWith
+ - [x] _.functionsIn
+ - [x] _.hasIn
+ - [x] _.mergeWith
+ - [x] _.omitBy
+ - [x] _.pickBy
+
+
+ added 8 string methods:
+ - [x] _.lowerCase
+ - [x] _.lowerFirst
+ - [x] _.upperCase
+ - [x] _.upperFirst
+ - [x] _.toLower
+ - [x] _.toUpper
+
+ added 8 utility methods:
+ - [x] _.toPath
+
+ added 4 math methods:
+ - [x] _.maxBy
+ - [x] _.mean
+ - [x] _.minBy
+ - [x] _.sumBy
+
+ added 2 function methods:
+ - [x] _.flip
+ - [x] _.unary
+
+ added 2 number methods:
+ - [x] _.clamp
+ - [x] _.subtract
+
+ added collection method:
+ - [x] _.sampleSize
+
+ Added 3 aliases
+
+ - [x] _.first as an alias of _.head
+
+ Removed 17 aliases
+ - [x] Removed aliase _.all
+ - [x] Removed aliase _.any
+ - [x] Removed aliase _.backflow
+ - [x] Removed aliase _.callback
+ - [x] Removed aliase _.collect
+ - [x] Removed aliase _.compose
+ - [x] Removed aliase _.contains
+ - [x] Removed aliase _.detect
+ - [x] Removed aliase _.foldl
+ - [x] Removed aliase _.foldr
+ - [x] Removed aliase _.include
+ - [x] Removed aliase _.inject
+ - [x] Removed aliase _.methods
+ - [x] Removed aliase _.object
+ - [x] Removed aliase _.run
+ - [x] Removed aliase _.select
+ - [x] Removed aliase _.unique
+
+ Other changes
+ - [x] Added support for array buffers to _.isEqual
+ - [x] Added support for converting iterators to _.toArray
+ - [x] Added support for deep paths to _.zipObject
+ - [x] Changed UMD to export to window or self when available regardless of other exports
+ - [x] Ensured debounce cancel clears args & thisArg references
+ - [x] Ensured _.add, _.subtract, & _.sum don’t skip NaN values
+ - [x] Ensured _.clone treats generators like functions
+ - [x] Ensured _.clone produces clones with the source’s [[Prototype]]
+ - [x] Ensured _.defaults assigns properties that shadow Object.prototype
+ - [x] Ensured _.defaultsDeep doesn’t merge a string into an array
+ - [x] Ensured _.defaultsDeep & _.merge don’t modify sources
+ - [x] Ensured _.defaultsDeep works with circular references
+ - [x] Ensured _.keys skips “length†on strict mode arguments objects in Safari 9
+ - [x] Ensured _.merge doesn’t convert strings to arrays
+ - [x] Ensured _.merge merges plain-objects onto non plain-objects
+ - [x] Ensured _#plant resets iterator data of cloned sequences
+ - [x] Ensured _.random swaps min & max if min is greater than max
+ - [x] Ensured _.range preserves the sign of start of -0
+ - [x] Ensured _.reduce & _.reduceRight use getIteratee in their array branch
+ - [x] Fixed rounding issue with the precision param of _.floor
+ - [x] Added flush method to debounced & throttled functions
+
+ ** LATER **
+ Misc:
+ - [ ] Made _.forEach, _.forIn, _.forOwn, & _.times implicitly end a chain sequence
+ - [ ] Removed thisArg params from most methods
+ - [ ] Made “By†methods provide a single param to iteratees
+ - [ ] Made _.words chainable by default
+ - [ ] Removed isDeep params from _.clone & _.flatten
+ - [ ] Removed _.bindAll support for binding all methods when no names are provided
+ - [ ] Removed func-first param signature from _.before & _.after
+ - [ ] _.extend as an alias of _.assignIn
+ - [ ] _.extendWith as an alias of _.assignInWith
+ - [ ] Added clear method to _.memoize.Cache
+ - [ ] Added support for ES6 maps, sets, & symbols to _.clone, _.isEqual, & _.toArray
+ - [ ] Enabled _.flow & _.flowRight to accept an array of functions
+ - [ ] Ensured “Collection†methods treat functions as objects
+ - [ ] Ensured _.assign, _.defaults, & _.merge coerce object values to objects
+ - [ ] Ensured _.bindKey bound functions call object[key] when called with the new operator
+ - [ ] Ensured _.isFunction returns true for generator functions
+ - [ ] Ensured _.merge assigns typed arrays directly
+ - [ ] Made _(...) an iterator & iterable
+ - [ ] Made _.drop, _.take, & right forms coerce n of undefined to 0
+
+ Methods:
+ - [ ] _.concat
+ - [ ] _.differenceBy
+ - [ ] _.differenceWith
+ - [ ] _.flatMap
+ - [ ] _.fromPairs
+ - [ ] _.intersectionBy
+ - [ ] _.intersectionWith
+ - [ ] _.join
+ - [ ] _.pullAll
+ - [ ] _.pullAllBy
+ - [ ] _.reverse
+ - [ ] _.sortedLastIndexOf
+ - [ ] _.unionBy
+ - [ ] _.unionWith
+ - [ ] _.uniqWith
+ - [ ] _.xorBy
+ - [ ] _.xorWith
+ - [ ] _.toString
+
+ - [ ] _.invoke
+ - [ ] _.setWith
+ - [ ] _.toPairs
+ - [ ] _.toPairsIn
+ - [ ] _.unset
+
+ - [ ] _.replace
+ - [ ] _.split
+
+ - [ ] _.cond
+ - [ ] _.conforms
+ - [ ] _.nthArg
+ - [ ] _.over
+ - [ ] _.overEvery
+ - [ ] _.overSome
+ - [ ] _.rangeRight
+
+ - [ ] _.next
+ */
+
+declare var _: _.LoDashStatic;
+
+declare module _ {
+ interface LoDashStatic {
+ /**
+ * Creates a lodash object which wraps the given value to enable intuitive method chaining.
+ *
+ * In addition to Lo-Dash methods, wrappers also have the following Array methods:
+ * concat, join, pop, push, reverse, shift, slice, sort, splice, and unshift
+ *
+ * Chaining is supported in custom builds as long as the value method is implicitly or
+ * explicitly included in the build.
+ *
+ * The chainable wrapper functions are:
+ * after, assign, bind, bindAll, bindKey, chain, chunk, compact, compose, concat, countBy,
+ * createCallback, curry, debounce, defaults, defer, delay, difference, filter, flatten,
+ * forEach, forEachRight, forIn, forInRight, forOwn, forOwnRight, functions, groupBy,
+ * keyBy, initial, intersection, invert, invoke, keys, map, max, memoize, merge, min,
+ * object, omit, once, pairs, partial, partialRight, pick, pluck, pull, push, range, reject,
+ * remove, rest, reverse, sample, shuffle, slice, sort, sortBy, splice, tap, throttle, times,
+ * toArray, transform, union, uniq, unset, unshift, unzip, values, where, without, wrap, and zip
+ *
+ * The non-chainable wrapper functions are:
+ * clone, cloneDeep, contains, escape, every, find, findIndex, findKey, findLast,
+ * findLastIndex, findLastKey, has, identity, indexOf, isArguments, isArray, isBoolean,
+ * isDate, isElement, isEmpty, isEqual, isFinite, isFunction, isNaN, isNull, isNumber,
+ * isObject, isPlainObject, isRegExp, isString, isUndefined, join, lastIndexOf, mixin,
+ * noConflict, parseInt, pop, random, reduce, reduceRight, result, shift, size, some,
+ * sortedIndex, runInContext, template, unescape, uniqueId, and value
+ *
+ * The wrapper functions first and last return wrapped values when n is provided, otherwise
+ * they return unwrapped values.
+ *
+ * Explicit chaining can be enabled by using the _.chain method.
+ **/
+ (value: number): LoDashImplicitWrapper<number>;
+ (value: string): LoDashImplicitStringWrapper;
+ (value: boolean): LoDashImplicitWrapper<boolean>;
+ (value: Array<number>): LoDashImplicitNumberArrayWrapper;
+ <T>(value: Array<T>): LoDashImplicitArrayWrapper<T>;
+ <T extends {}>(value: T): LoDashImplicitObjectWrapper<T>;
+ (value: any): LoDashImplicitWrapper<any>;
+
+ /**
+ * The semantic version number.
+ **/
+ VERSION: string;
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in embedded Ruby
+ * (ERB). Change the following template settings to use alternative delimiters.
+ **/
+ templateSettings: TemplateSettings;
+ }
+
+ /**
+ * By default, the template delimiters used by Lo-Dash are similar to those in embedded Ruby
+ * (ERB). Change the following template settings to use alternative delimiters.
+ **/
+ interface TemplateSettings {
+ /**
+ * The "escape" delimiter.
+ **/
+ escape?: RegExp;
+
+ /**
+ * The "evaluate" delimiter.
+ **/
+ evaluate?: RegExp;
+
+ /**
+ * An object to import into the template as local variables.
+ **/
+ imports?: Dictionary<any>;
+
+ /**
+ * The "interpolate" delimiter.
+ **/
+ interpolate?: RegExp;
+
+ /**
+ * Used to reference the data object in the template text.
+ **/
+ variable?: string;
+ }
+
+ /**
+ * Creates a cache object to store key/value pairs.
+ */
+ interface MapCache {
+ /**
+ * Removes `key` and its value from the cache.
+ * @param key The key of the value to remove.
+ * @return Returns `true` if the entry was removed successfully, else `false`.
+ */
+ delete(key: string): boolean;
+
+ /**
+ * Gets the cached value for `key`.
+ * @param key The key of the value to get.
+ * @return Returns the cached value.
+ */
+ get(key: string): any;
+
+ /**
+ * Checks if a cached value for `key` exists.
+ * @param key The key of the entry to check.
+ * @return Returns `true` if an entry for `key` exists, else `false`.
+ */
+ has(key: string): boolean;
+
+ /**
+ * Sets `value` to `key` of the cache.
+ * @param key The key of the value to cache.
+ * @param value The value to cache.
+ * @return Returns the cache object.
+ */
+ set(key: string, value: any): _.Dictionary<any>;
+ }
+ interface MapCacheConstructor {
+ new (): MapCache;
+ }
+
+ interface LoDashWrapperBase<T, TWrapper> { }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> extends LoDashWrapperBase<T, TWrapper> { }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> extends LoDashWrapperBase<T, TWrapper> { }
+
+ interface LoDashImplicitWrapper<T> extends LoDashImplicitWrapperBase<T, LoDashImplicitWrapper<T>> { }
+
+ interface LoDashExplicitWrapper<T> extends LoDashExplicitWrapperBase<T, LoDashExplicitWrapper<T>> { }
+
+ interface LoDashImplicitStringWrapper extends LoDashImplicitWrapper<string> { }
+
+ interface LoDashExplicitStringWrapper extends LoDashExplicitWrapper<string> { }
+
+ interface LoDashImplicitObjectWrapper<T> extends LoDashImplicitWrapperBase<T, LoDashImplicitObjectWrapper<T>> { }
+
+ interface LoDashExplicitObjectWrapper<T> extends LoDashExplicitWrapperBase<T, LoDashExplicitObjectWrapper<T>> { }
+
+ interface LoDashImplicitArrayWrapper<T> extends LoDashImplicitWrapperBase<T[], LoDashImplicitArrayWrapper<T>> {
+ pop(): T;
+ push(...items: T[]): LoDashImplicitArrayWrapper<T>;
+ shift(): T;
+ sort(compareFn?: (a: T, b: T) => number): LoDashImplicitArrayWrapper<T>;
+ splice(start: number): LoDashImplicitArrayWrapper<T>;
+ splice(start: number, deleteCount: number, ...items: any[]): LoDashImplicitArrayWrapper<T>;
+ unshift(...items: T[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> extends LoDashExplicitWrapperBase<T[], LoDashExplicitArrayWrapper<T>> {
+ pop(): LoDashExplicitObjectWrapper<T>;
+ push(...items: T[]): LoDashExplicitArrayWrapper<T>;
+ shift(): LoDashExplicitObjectWrapper<T>;
+ sort(compareFn?: (a: T, b: T) => number): LoDashExplicitArrayWrapper<T>;
+ splice(start: number): LoDashExplicitArrayWrapper<T>;
+ splice(start: number, deleteCount: number, ...items: any[]): LoDashExplicitArrayWrapper<T>;
+ unshift(...items: T[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitNumberArrayWrapper extends LoDashImplicitArrayWrapper<number> { }
+
+ interface LoDashExplicitNumberArrayWrapper extends LoDashExplicitArrayWrapper<number> { }
+
+ /*********
+ * Array *
+ *********/
+
+ //_.chunk
+ interface LoDashStatic {
+ /**
+ * Creates an array of elements split into groups the length of size. If collection can’t be split evenly, the
+ * final chunk will be the remaining elements.
+ *
+ * @param array The array to process.
+ * @param size The length of each chunk.
+ * @return Returns the new array containing chunks.
+ */
+ chunk<T>(
+ array: List<T>,
+ size?: number
+ ): T[][];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.chunk
+ */
+ chunk(size?: number): LoDashImplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.chunk
+ */
+ chunk<TResult>(size?: number): LoDashImplicitArrayWrapper<TResult[]>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.chunk
+ */
+ chunk(size?: number): LoDashExplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.chunk
+ */
+ chunk<TResult>(size?: number): LoDashExplicitArrayWrapper<TResult[]>;
+ }
+
+ //_.compact
+ interface LoDashStatic {
+ /**
+ * Creates an array with all falsey values removed. The values false, null, 0, "", undefined, and NaN are
+ * falsey.
+ *
+ * @param array The array to compact.
+ * @return (Array) Returns the new array of filtered values.
+ */
+ compact<T>(array?: List<T>): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.compact
+ */
+ compact(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.compact
+ */
+ compact<TResult>(): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.compact
+ */
+ compact(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.compact
+ */
+ compact<TResult>(): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.concat DUMMY
+ interface LoDashStatic {
+ /**
+ * Creates a new array concatenating `array` with any additional arrays
+ * and/or values.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to concatenate.
+ * @param {...*} [values] The values to concatenate.
+ * @returns {Array} Returns the new concatenated array.
+ * @example
+ *
+ * var array = [1];
+ * var other = _.concat(array, 2, [3], [[4]]);
+ *
+ * console.log(other);
+ * // => [1, 2, 3, [4]]
+ *
+ * console.log(array);
+ * // => [1]
+ */
+ concat<T>(array: T[]|List<T>, ...values: (T|T[]|List<T>)[]) : T[];
+ }
+
+ //_.difference
+ interface LoDashStatic {
+ /**
+ * Creates an array of unique array values not included in the other provided arrays using SameValueZero for
+ * equality comparisons.
+ *
+ * @param array The array to inspect.
+ * @param values The arrays of values to exclude.
+ * @return Returns the new array of filtered values.
+ */
+ difference<T>(
+ array: T[]|List<T>,
+ ...values: Array<T[]|List<T>>
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.difference
+ */
+ difference(...values: (T[]|List<T>)[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.difference
+ */
+ difference<TValue>(...values: (TValue[]|List<TValue>)[]): LoDashImplicitArrayWrapper<TValue>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.difference
+ */
+ difference(...values: (T[]|List<T>)[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.difference
+ */
+ difference<TValue>(...values: (TValue[]|List<TValue>)[]): LoDashExplicitArrayWrapper<TValue>;
+ }
+
+ //_.differenceBy
+ interface LoDashStatic {
+ /**
+ * This method is like _.difference except that it accepts iteratee which is invoked for each element of array
+ * and values to generate the criterion by which uniqueness is computed. The iteratee is invoked with one
+ * argument: (value).
+ *
+ * @param array The array to inspect.
+ * @param values The values to exclude.
+ * @param iteratee The iteratee invoked per element.
+ * @returns Returns the new array of filtered values.
+ */
+ differenceBy<T>(
+ array: T[]|List<T>,
+ values?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ array: T[]|List<T>,
+ values?: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ array: T[]|List<T>,
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ array: T[]|List<T>,
+ ...values: any[]
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ ...values: any[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ ...values: any[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ ...values: any[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: ((value: T) => any)|string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T, W extends Object>(
+ values1?: T[]|List<T>,
+ values2?: T[]|List<T>,
+ values3?: T[]|List<T>,
+ values4?: T[]|List<T>,
+ values5?: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.differenceBy
+ */
+ differenceBy<T>(
+ ...values: any[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.differenceWith DUMMY
+ interface LoDashStatic {
+ /**
+ * Creates an array of unique `array` values not included in the other
+ * provided arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.difference([3, 2, 1], [4, 2]);
+ * // => [3, 1]
+ */
+ differenceWith(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.drop
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array with n elements dropped from the beginning.
+ *
+ * @param array The array to query.
+ * @param n The number of elements to drop.
+ * @return Returns the slice of array.
+ */
+ drop<T>(array: T[]|List<T>, n?: number): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.drop
+ */
+ drop(n?: number): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.drop
+ */
+ drop<T>(n?: number): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.drop
+ */
+ drop(n?: number): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.drop
+ */
+ drop<T>(n?: number): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.dropRight
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array with n elements dropped from the end.
+ *
+ * @param array The array to query.
+ * @param n The number of elements to drop.
+ * @return Returns the slice of array.
+ */
+ dropRight<T>(
+ array: List<T>,
+ n?: number
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.dropRight
+ */
+ dropRight(n?: number): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.dropRight
+ */
+ dropRight<TResult>(n?: number): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.dropRight
+ */
+ dropRight(n?: number): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.dropRight
+ */
+ dropRight<TResult>(n?: number): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.dropRightWhile
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array excluding elements dropped from the end. Elements are dropped until predicate
+ * returns falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * match the properties of the given object, else false.
+ *
+ * @param array The array to query.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the slice of array.
+ */
+ dropRightWhile<TValue>(
+ array: List<TValue>,
+ predicate?: ListIterator<TValue, boolean>
+ ): TValue[];
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TValue>(
+ array: List<TValue>,
+ predicate?: string
+ ): TValue[];
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TWhere, TValue>(
+ array: List<TValue>,
+ predicate?: TWhere
+ ): TValue[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TValue>(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<TValue>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TValue>(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropRightWhile
+ */
+ dropRightWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<TValue>;
+ }
+
+ //_.dropWhile
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array excluding elements dropped from the beginning. Elements are dropped until predicate
+ * returns falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param array The array to query.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the slice of array.
+ */
+ dropWhile<TValue>(
+ array: List<TValue>,
+ predicate?: ListIterator<TValue, boolean>
+ ): TValue[];
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TValue>(
+ array: List<TValue>,
+ predicate?: string
+ ): TValue[];
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TWhere, TValue>(
+ array: List<TValue>,
+ predicate?: TWhere
+ ): TValue[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TValue>(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<TValue>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TValue>(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.dropWhile
+ */
+ dropWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<TValue>;
+ }
+
+ //_.fill
+ interface LoDashStatic {
+ /**
+ * Fills elements of array with value from start up to, but not including, end.
+ *
+ * Note: This method mutates array.
+ *
+ * @param array The array to fill.
+ * @param value The value to fill array with.
+ * @param start The start position.
+ * @param end The end position.
+ * @return Returns array.
+ */
+ fill<T>(
+ array: any[],
+ value: T,
+ start?: number,
+ end?: number
+ ): T[];
+
+ /**
+ * @see _.fill
+ */
+ fill<T>(
+ array: List<any>,
+ value: T,
+ start?: number,
+ end?: number
+ ): List<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.fill
+ */
+ fill<T>(
+ value: T,
+ start?: number,
+ end?: number
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.fill
+ */
+ fill<T>(
+ value: T,
+ start?: number,
+ end?: number
+ ): LoDashImplicitObjectWrapper<List<T>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.fill
+ */
+ fill<T>(
+ value: T,
+ start?: number,
+ end?: number
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.fill
+ */
+ fill<T>(
+ value: T,
+ start?: number,
+ end?: number
+ ): LoDashExplicitObjectWrapper<List<T>>;
+ }
+
+ //_.findIndex
+ interface LoDashStatic {
+ /**
+ * This method is like _.find except that it returns the index of the first element predicate returns truthy
+ * for instead of the element itself.
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param array The array to search.
+ * @param predicate The function invoked per iteration.
+ * @param fromIndex The index to search from.
+ * @return Returns the index of the found element, else -1.
+ */
+ findIndex<T>(
+ array: List<T>,
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex<T>(
+ array: List<T>,
+ predicate?: string,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex<W, T>(
+ array: List<T>,
+ predicate?: W,
+ fromIndex?: number
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.findIndex
+ */
+ findIndex(
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.findIndex
+ */
+ findIndex<TResult>(
+ predicate?: ListIterator<TResult, boolean>,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.findIndex
+ */
+ findIndex(
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.findIndex
+ */
+ findIndex<TResult>(
+ predicate?: ListIterator<TResult, boolean>,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findIndex
+ */
+ findIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.findLastIndex
+ interface LoDashStatic {
+ /**
+ * This method is like _.findIndex except that it iterates over elements of collection from right to left.
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param array The array to search.
+ * @param predicate The function invoked per iteration.
+ * @param fromIndex The index to search from.
+ * @return Returns the index of the found element, else -1.
+ */
+ findLastIndex<T>(
+ array: List<T>,
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<T>(
+ array: List<T>,
+ predicate?: string,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<W, T>(
+ array: List<T>,
+ predicate?: W,
+ fromIndex?: number
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex(
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<TResult>(
+ predicate?: ListIterator<TResult, boolean>,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): number;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex(
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<TResult>(
+ predicate?: ListIterator<TResult, boolean>,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex(
+ predicate?: string,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.findLastIndex
+ */
+ findLastIndex<W>(
+ predicate?: W,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.first
+ interface LoDashStatic {
+ /**
+ * @see _.head
+ */
+ first<T>(array: List<T>): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.head
+ */
+ first(): string;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.head
+ */
+ first(): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.head
+ */
+ first<T>(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.head
+ */
+ first(): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.head
+ */
+ first<T>(): T;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.head
+ */
+ first<T>(): T;
+ }
+
+ interface RecursiveArray<T> extends Array<T|RecursiveArray<T>> {}
+ interface ListOfRecursiveArraysOrValues<T> extends List<T|RecursiveArray<T>> {}
+
+ //_.flatten
+ interface LoDashStatic {
+ /**
+ * Flattens a nested array. If isDeep is true the array is recursively flattened, otherwise it’s only
+ * flattened a single level.
+ *
+ * @param array The array to flatten.
+ * @param isDeep Specify a deep flatten.
+ * @return Returns the new flattened array.
+ */
+ flatten<T>(array: ListOfRecursiveArraysOrValues<T>, isDeep: boolean): T[];
+
+ /**
+ * @see _.flatten
+ */
+ flatten<T>(array: List<T|T[]>): T[];
+
+ /**
+ * @see _.flatten
+ */
+ flatten<T>(array: ListOfRecursiveArraysOrValues<T>): RecursiveArray<T>;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.flatten
+ */
+ flatten(): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.flatten
+ */
+ flatten<TResult>(isDeep?: boolean): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.flatten
+ */
+ flatten<TResult>(isDeep?: boolean): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.flatten
+ */
+ flatten(): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.flatten
+ */
+ flatten<TResult>(isDeep?: boolean): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.flatten
+ */
+ flatten<TResult>(isDeep?: boolean): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.flattenDeep
+ interface LoDashStatic {
+ /**
+ * Recursively flattens a nested array.
+ *
+ * @param array The array to recursively flatten.
+ * @return Returns the new flattened array.
+ */
+ flattenDeep<T>(array: ListOfRecursiveArraysOrValues<T>): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.flattenDeep
+ */
+ flattenDeep(): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.flattenDeep
+ */
+ flattenDeep<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.flattenDeep
+ */
+ flattenDeep<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.flattenDeep
+ */
+ flattenDeep(): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.flattenDeep
+ */
+ flattenDeep<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.flattenDeep
+ */
+ flattenDeep<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ // _.flattenDepth
+ interface LoDashStatic {
+ /**
+ * Recursively flatten array up to depth times.
+ *
+ * @param array The array to recursively flatten.
+ * @param number The maximum recursion depth.
+ * @return Returns the new flattened array.
+ */
+ flattenDepth<T>(array: ListOfRecursiveArraysOrValues<T>, depth?: number): T[];
+ }
+
+ //_.fromPairs
+ interface LoDashStatic {
+ /**
+ * The inverse of `_.toPairs`; this method returns an object composed
+ * from key-value `pairs`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} pairs The key-value pairs.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.fromPairs([['fred', 30], ['barney', 40]]);
+ * // => { 'fred': 30, 'barney': 40 }
+ */
+ fromPairs<T>(
+ array: List<[_.StringRepresentable, T]>
+ ): Dictionary<T>;
+
+ /**
+ @see _.fromPairs
+ */
+ fromPairs(
+ array: List<any[]>
+ ): Dictionary<any>;
+ }
+
+ //_.fromPairs DUMMY
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.fromPairs
+ */
+ fromPairs(): LoDashImplicitObjectWrapper<any>;
+ }
+
+ //_.fromPairs DUMMY
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.fromPairs
+ */
+ fromPairs(): LoDashExplicitObjectWrapper<any>;
+ }
+
+ //_.head
+ interface LoDashStatic {
+ /**
+ * Gets the first element of array.
+ *
+ * @alias _.first
+ *
+ * @param array The array to query.
+ * @return Returns the first element of array.
+ */
+ head<T>(array: List<T>): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.head
+ */
+ head(): string;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.head
+ */
+ head(): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.head
+ */
+ head<T>(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.head
+ */
+ head(): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.head
+ */
+ head<T>(): T;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.head
+ */
+ head<T>(): T;
+ }
+
+ //_.indexOf
+ interface LoDashStatic {
+ /**
+ * Gets the index at which the first occurrence of `value` is found in `array`
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+ * for equality comparisons. If `fromIndex` is negative, it's used as the offset
+ * from the end of `array`. If `array` is sorted providing `true` for `fromIndex`
+ * performs a faster binary search.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 1, 2], 2);
+ * // => 1
+ *
+ * // using `fromIndex`
+ * _.indexOf([1, 2, 1, 2], 2, 2);
+ * // => 3
+ */
+ indexOf<T>(
+ array: List<T>,
+ value: T,
+ fromIndex?: boolean|number
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.indexOf
+ */
+ indexOf(
+ value: T,
+ fromIndex?: boolean|number
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.indexOf
+ */
+ indexOf<TValue>(
+ value: TValue,
+ fromIndex?: boolean|number
+ ): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.indexOf
+ */
+ indexOf(
+ value: T,
+ fromIndex?: boolean|number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.indexOf
+ */
+ indexOf<TValue>(
+ value: TValue,
+ fromIndex?: boolean|number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.intersectionBy DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.intersection` except that it accepts `iteratee`
+ * which is invoked for each element of each `arrays` to generate the criterion
+ * by which uniqueness is computed. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of shared values.
+ * @example
+ *
+ * _.intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);
+ * // => [2.1]
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }]
+ */
+ intersectionBy(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.intersectionWith DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.intersection` except that it accepts `comparator`
+ * which is invoked to compare elements of `arrays`. The comparator is invoked
+ * with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of shared values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.intersectionWith(objects, others, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }]
+ */
+ intersectionWith(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.join
+ interface LoDashStatic {
+ /**
+ * Converts all elements in `array` into a string separated by `separator`.
+ *
+ * @param array The array to convert.
+ * @param separator The element separator.
+ * @returns Returns the joined string.
+ */
+ join(
+ array: List<any>,
+ separator?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.join
+ */
+ join(separator?: string): string;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.join
+ */
+ join(separator?: string): string;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.join
+ */
+ join(separator?: string): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.join
+ */
+ join(separator?: string): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.join
+ */
+ join(separator?: string): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.join
+ */
+ join(separator?: string): LoDashExplicitWrapper<string>;
+ }
+
+ //_.pullAll DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.pull` except that it accepts an array of values to remove.
+ *
+ * **Note:** Unlike `_.difference`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3, 1, 2, 3];
+ *
+ * _.pull(array, [2, 3]);
+ * console.log(array);
+ * // => [1, 1]
+ */
+ pullAll(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.pullAllBy DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.pullAll` except that it accepts `iteratee` which is
+ * invoked for each element of `array` and `values` to to generate the criterion
+ * by which uniqueness is computed. The iteratee is invoked with one argument: (value).
+ *
+ * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
+ *
+ * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
+ * console.log(array);
+ * // => [{ 'x': 2 }]
+ */
+ pullAllBy(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.reverse DUMMY
+ interface LoDashStatic {
+ /**
+ * Reverses `array` so that the first element becomes the last, the second
+ * element becomes the second to last, and so on.
+ *
+ * **Note:** This method mutates `array` and is based on
+ * [`Array#reverse`](https://mdn.io/Array/reverse).
+ *
+ * @memberOf _
+ * @category Array
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _.reverse(array);
+ * // => [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
+ */
+ reverse(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.sortedIndexOf
+ interface LoDashStatic {
+ /**
+ * This method is like `_.indexOf` except that it performs a binary
+ * search on a sorted `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.sortedIndexOf([1, 1, 2, 2], 2);
+ * // => 2
+ */
+ sortedIndexOf<T>(
+ array: List<T>,
+ value: T
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedIndexOf
+ */
+ sortedIndexOf(
+ value: T
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedIndexOf
+ */
+ sortedIndexOf<TValue>(
+ value: TValue
+ ): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedIndexOf
+ */
+ sortedIndexOf(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedIndexOf
+ */
+ sortedIndexOf<TValue>(
+ value: TValue
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.initial
+ interface LoDashStatic {
+ /**
+ * Gets all but the last element of array.
+ *
+ * @param array The array to query.
+ * @return Returns the slice of array.
+ */
+ initial<T>(array: List<T>): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.initial
+ */
+ initial(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.initial
+ */
+ initial<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.initial
+ */
+ initial(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.initial
+ */
+ initial<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.intersection
+ interface LoDashStatic {
+ /**
+ * Creates an array of unique values that are included in all of the provided arrays using SameValueZero for
+ * equality comparisons.
+ *
+ * @param arrays The arrays to inspect.
+ * @return Returns the new array of shared values.
+ */
+ intersection<T>(...arrays: (T[]|List<T>)[]): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.intersection
+ */
+ intersection<TResult>(...arrays: (TResult[]|List<TResult>)[]): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.intersection
+ */
+ intersection<TResult>(...arrays: (TResult[]|List<TResult>)[]): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.intersection
+ */
+ intersection<TResult>(...arrays: (TResult[]|List<TResult>)[]): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.intersection
+ */
+ intersection<TResult>(...arrays: (TResult[]|List<TResult>)[]): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.last
+ interface LoDashStatic {
+ /**
+ * Gets the last element of array.
+ *
+ * @param array The array to query.
+ * @return Returns the last element of array.
+ */
+ last<T>(array: List<T>): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.last
+ */
+ last(): string;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.last
+ */
+ last(): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.last
+ */
+ last<T>(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.last
+ */
+ last(): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.last
+ */
+ last<T>(): T;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.last
+ */
+ last<T>(): T;
+ }
+
+ //_.lastIndexOf
+ interface LoDashStatic {
+ /**
+ * This method is like _.indexOf except that it iterates over elements of array from right to left.
+ *
+ * @param array The array to search.
+ * @param value The value to search for.
+ * @param fromIndex The index to search from or true to perform a binary search on a sorted array.
+ * @return Returns the index of the matched value, else -1.
+ */
+ lastIndexOf<T>(
+ array: List<T>,
+ value: T,
+ fromIndex?: boolean|number
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.lastIndexOf
+ */
+ lastIndexOf(
+ value: T,
+ fromIndex?: boolean|number
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.lastIndexOf
+ */
+ lastIndexOf<TResult>(
+ value: TResult,
+ fromIndex?: boolean|number
+ ): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.lastIndexOf
+ */
+ lastIndexOf(
+ value: T,
+ fromIndex?: boolean|number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.lastIndexOf
+ */
+ lastIndexOf<TResult>(
+ value: TResult,
+ fromIndex?: boolean|number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.pull
+ interface LoDashStatic {
+ /**
+ * Removes all provided values from array using SameValueZero for equality comparisons.
+ *
+ * Note: Unlike _.without, this method mutates array.
+ *
+ * @param array The array to modify.
+ * @param values The values to remove.
+ * @return Returns array.
+ */
+ pull<T>(
+ array: T[],
+ ...values: T[]
+ ): T[];
+
+ /**
+ * @see _.pull
+ */
+ pull<T>(
+ array: List<T>,
+ ...values: T[]
+ ): List<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.pull
+ */
+ pull(...values: T[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.pull
+ */
+ pull<TValue>(...values: TValue[]): LoDashImplicitObjectWrapper<List<TValue>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.pull
+ */
+ pull(...values: T[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.pull
+ */
+ pull<TValue>(...values: TValue[]): LoDashExplicitObjectWrapper<List<TValue>>;
+ }
+
+ //_.pullAt
+ interface LoDashStatic {
+ /**
+ * Removes elements from array corresponding to the given indexes and returns an array of the removed elements.
+ * Indexes may be specified as an array of indexes or as individual arguments.
+ *
+ * Note: Unlike _.at, this method mutates array.
+ *
+ * @param array The array to modify.
+ * @param indexes The indexes of elements to remove, specified as individual indexes or arrays of indexes.
+ * @return Returns the new array of removed elements.
+ */
+ pullAt<T>(
+ array: List<T>,
+ ...indexes: (number|number[])[]
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.pullAt
+ */
+ pullAt(...indexes: (number|number[])[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.pullAt
+ */
+ pullAt<T>(...indexes: (number|number[])[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.pullAt
+ */
+ pullAt(...indexes: (number|number[])[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.pullAt
+ */
+ pullAt<T>(...indexes: (number|number[])[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.remove
+ interface LoDashStatic {
+ /**
+ * Removes all elements from array that predicate returns truthy for and returns an array of the removed
+ * elements. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * Note: Unlike _.filter, this method mutates array.
+ *
+ * @param array The array to modify.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the new array of removed elements.
+ */
+ remove<T>(
+ array: List<T>,
+ predicate?: ListIterator<T, boolean>
+ ): T[];
+
+ /**
+ * @see _.remove
+ */
+ remove<T>(
+ array: List<T>,
+ predicate?: string
+ ): T[];
+
+ /**
+ * @see _.remove
+ */
+ remove<W, T>(
+ array: List<T>,
+ predicate?: W
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.remove
+ */
+ remove(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.remove
+ */
+ remove(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.remove
+ */
+ remove<W>(
+ predicate?: W
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.remove
+ */
+ remove<TResult>(
+ predicate?: ListIterator<TResult, boolean>
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.remove
+ */
+ remove<TResult>(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.remove
+ */
+ remove<W, TResult>(
+ predicate?: W
+ ): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.remove
+ */
+ remove(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.remove
+ */
+ remove(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.remove
+ */
+ remove<W>(
+ predicate?: W
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.remove
+ */
+ remove<TResult>(
+ predicate?: ListIterator<TResult, boolean>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.remove
+ */
+ remove<TResult>(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.remove
+ */
+ remove<W, TResult>(
+ predicate?: W
+ ): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.tail
+ interface LoDashStatic {
+ /**
+ * Gets all but the first element of array.
+ *
+ * @alias _.tail
+ *
+ * @param array The array to query.
+ * @return Returns the slice of array.
+ */
+ tail<T>(array: List<T>): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.tail
+ */
+ tail(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.tail
+ */
+ tail<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.tail
+ */
+ tail(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.tail
+ */
+ tail<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.slice
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array from start up to, but not including, end.
+ *
+ * @param array The array to slice.
+ * @param start The start position.
+ * @param end The end position.
+ * @return Returns the slice of array.
+ */
+ slice<T>(
+ array: T[],
+ start?: number,
+ end?: number
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.slice
+ */
+ slice(
+ start?: number,
+ end?: number
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.slice
+ */
+ slice(
+ start?: number,
+ end?: number
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.sortedIndex
+ interface LoDashStatic {
+ /**
+ * Uses a binary search to determine the lowest index at which `value` should
+ * be inserted into `array` in order to maintain its sort order.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @returns {number} Returns the index at which `value` should be inserted into `array`.
+ * @example
+ *
+ * _.sortedIndex([30, 50], 40);
+ * // => 1
+ *
+ * _.sortedIndex([4, 5], 4);
+ * // => 0
+ */
+ sortedIndex<T, TSort>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<W, T>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T>(
+ array: List<T>,
+ value: T
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<TSort>(
+ value: string
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<TSort>(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex(
+ value: T
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T, TSort>(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T>(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<W, T>(
+ value: T
+ ): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<TSort>(
+ value: string
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<TSort>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<W>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T, TSort>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<T>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndex
+ */
+ sortedIndex<W, T>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+
+ }
+
+ //_.sortedIndexBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.sortedIndex` except that it accepts `iteratee`
+ * which is invoked for `value` and each element of `array` to compute their
+ * sort ranking. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the index at which `value` should be inserted into `array`.
+ * @example
+ *
+ * var dict = { 'thirty': 30, 'forty': 40, 'fifty': 50 };
+ *
+ * _.sortedIndexBy(['thirty', 'fifty'], 'forty', _.propertyOf(dict));
+ * // => 1
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.sortedIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');
+ * // => 0
+ */
+ sortedIndexBy<T, TSort>(
+ array: List<T>,
+ value: T,
+ iteratee: (x: T) => TSort
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ array: List<T>,
+ value: T,
+ iteratee: (x: T) => any
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ array: List<T>,
+ value: T,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<W, T>(
+ array: List<T>,
+ value: T,
+ iteratee: W
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ array: List<T>,
+ value: T,
+ iteratee: Object
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<TSort>(
+ value: string,
+ iteratee: (x: string) => TSort
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy(
+ value: T,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<W>(
+ value: T,
+ iteratee: W
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T, TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ value: T,
+ iteratee: (x: T) => any
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ value: T,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<W, T>(
+ value: T,
+ iteratee: W
+ ): number;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ value: T,
+ iteratee: Object
+ ): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<TSort>(
+ value: string,
+ iteratee: (x: string) => TSort
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy(
+ value: T,
+ iteratee: string
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<W>(
+ value: T,
+ iteratee: W
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T, TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ value: T,
+ iteratee: (x: T) => any
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ value: T,
+ iteratee: string
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<W, T>(
+ value: T,
+ iteratee: W
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedIndexBy
+ */
+ sortedIndexBy<T>(
+ value: T,
+ iteratee: Object
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.sortedLastIndex
+ interface LoDashStatic {
+ /**
+ * This method is like `_.sortedIndex` except that it returns the highest
+ * index at which `value` should be inserted into `array` in order to
+ * maintain its sort order.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @returns {number} Returns the index at which `value` should be inserted into `array`.
+ * @example
+ *
+ * _.sortedLastIndex([4, 5], 4);
+ * // => 1
+ */
+ sortedLastIndex<T, TSort>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<W, T>(
+ array: List<T>,
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T>(
+ array: List<T>,
+ value: T
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<TSort>(
+ value: string
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<TSort>(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<W>(
+ value: T
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T, TSort>(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T>(
+ value: T
+ ): number;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<W, T>(
+ value: T
+ ): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<TSort>(
+ value: string
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<TSort>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T, TSort>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<T>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndex
+ */
+ sortedLastIndex<W, T>(
+ value: T
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.sortedLastIndexBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
+ * which is invoked for `value` and each element of `array` to compute their
+ * sort ranking. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the index at which `value` should be inserted into `array`.
+ * @example
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.sortedLastIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');
+ * // => 1
+ */
+ sortedLastIndexBy<T, TSort>(
+ array: List<T>,
+ value: T,
+ iteratee: (x: T) => TSort
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ array: List<T>,
+ value: T,
+ iteratee: (x: T) => any
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ array: List<T>,
+ value: T,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<W, T>(
+ array: List<T>,
+ value: T,
+ iteratee: W
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ array: List<T>,
+ value: T,
+ iteratee: Object
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<TSort>(
+ value: string,
+ iteratee: (x: string) => TSort
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy(
+ value: T,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<W>(
+ value: T,
+ iteratee: W
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T, TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ value: T,
+ iteratee: (x: T) => any
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ value: T,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<W, T>(
+ value: T,
+ iteratee: W
+ ): number;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ value: T,
+ iteratee: Object
+ ): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<TSort>(
+ value: string,
+ iteratee: (x: string) => TSort
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy(
+ value: T,
+ iteratee: string
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<W>(
+ value: T,
+ iteratee: W
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T, TSort>(
+ value: T,
+ iteratee: (x: T) => TSort
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ value: T,
+ iteratee: (x: T) => any
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ value: T,
+ iteratee: string
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<W, T>(
+ value: T,
+ iteratee: W
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sortedLastIndexBy
+ */
+ sortedLastIndexBy<T>(
+ value: T,
+ iteratee: Object
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.sortedLastIndexOf DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.lastIndexOf` except that it performs a binary
+ * search on a sorted `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to search.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.sortedLastIndexOf([1, 1, 2, 2], 2);
+ * // => 3
+ */
+ sortedLastIndexOf(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.tail
+ interface LoDashStatic {
+ /**
+ * @see _.rest
+ */
+ tail<T>(array: List<T>): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.rest
+ */
+ tail(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.rest
+ */
+ tail<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.rest
+ */
+ tail(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.rest
+ */
+ tail<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.take
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array with n elements taken from the beginning.
+ *
+ * @param array The array to query.
+ * @param n The number of elements to take.
+ * @return Returns the slice of array.
+ */
+ take<T>(
+ array: List<T>,
+ n?: number
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.take
+ */
+ take(n?: number): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.take
+ */
+ take<TResult>(n?: number): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.take
+ */
+ take(n?: number): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.take
+ */
+ take<TResult>(n?: number): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.takeRight
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array with n elements taken from the end.
+ *
+ * @param array The array to query.
+ * @param n The number of elements to take.
+ * @return Returns the slice of array.
+ */
+ takeRight<T>(
+ array: List<T>,
+ n?: number
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.takeRight
+ */
+ takeRight(n?: number): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.takeRight
+ */
+ takeRight<TResult>(n?: number): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.takeRight
+ */
+ takeRight(n?: number): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.takeRight
+ */
+ takeRight<TResult>(n?: number): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.takeRightWhile
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array with elements taken from the end. Elements are taken until predicate returns
+ * falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param array The array to query.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the slice of array.
+ */
+ takeRightWhile<TValue>(
+ array: List<TValue>,
+ predicate?: ListIterator<TValue, boolean>
+ ): TValue[];
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TValue>(
+ array: List<TValue>,
+ predicate?: string
+ ): TValue[];
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TWhere, TValue>(
+ array: List<TValue>,
+ predicate?: TWhere
+ ): TValue[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TValue>(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<TValue>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TValue>(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeRightWhile
+ */
+ takeRightWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<TValue>;
+ }
+
+ //_.takeWhile
+ interface LoDashStatic {
+ /**
+ * Creates a slice of array with elements taken from the beginning. Elements are taken until predicate returns
+ * falsey. The predicate is bound to thisArg and invoked with three arguments: (value, index, array).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param array The array to query.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the slice of array.
+ */
+ takeWhile<TValue>(
+ array: List<TValue>,
+ predicate?: ListIterator<TValue, boolean>
+ ): TValue[];
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TValue>(
+ array: List<TValue>,
+ predicate?: string
+ ): TValue[];
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TWhere, TValue>(
+ array: List<TValue>,
+ predicate?: TWhere
+ ): TValue[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TValue>(
+ predicate?: string
+ ): LoDashImplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashImplicitArrayWrapper<TValue>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile(
+ predicate?: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TWhere>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TValue>(
+ predicate?: ListIterator<TValue, boolean>
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TValue>(
+ predicate?: string
+ ): LoDashExplicitArrayWrapper<TValue>;
+
+ /**
+ * @see _.takeWhile
+ */
+ takeWhile<TWhere, TValue>(
+ predicate?: TWhere
+ ): LoDashExplicitArrayWrapper<TValue>;
+ }
+
+ //_.union
+ interface LoDashStatic {
+ /**
+ * Creates an array of unique values, in order, from all of the provided arrays using SameValueZero for
+ * equality comparisons.
+ *
+ * @param arrays The arrays to inspect.
+ * @return Returns the new array of combined values.
+ */
+ union<T>(...arrays: List<T>[]): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.union
+ */
+ union(...arrays: List<T>[]): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.union
+ */
+ union<T>(...arrays: List<T>[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.union
+ */
+ union<T>(...arrays: List<T>[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.union
+ */
+ union(...arrays: List<T>[]): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.union
+ */
+ union<T>(...arrays: List<T>[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.union
+ */
+ union<T>(...arrays: List<T>[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.unionBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.union` except that it accepts `iteratee` which is
+ * invoked for each element of each `arrays` to generate the criterion by which
+ * uniqueness is computed. The iteratee is invoked with one argument: (value).
+ *
+ * @param arrays The arrays to inspect.
+ * @param iteratee The iteratee invoked per element.
+ * @return Returns the new array of combined values.
+ */
+ unionBy<T>(
+ arrays: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays1: T[]|List<T>,
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: W
+ ): T[];
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays: T[]|List<T>,
+ ...iteratee: any[]
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ ...iteratee: any[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: W
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ ...iteratee: any[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ ...iteratee: any[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: (value: T) => any
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T, W extends Object>(
+ arrays2: T[]|List<T>,
+ arrays3: T[]|List<T>,
+ arrays4: T[]|List<T>,
+ arrays5: T[]|List<T>,
+ iteratee?: W
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.unionBy
+ */
+ unionBy<T>(
+ ...iteratee: any[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.uniq
+ interface LoDashStatic {
+ /**
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each element
+ * is kept.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */
+ uniq<T>(
+ array: List<T>
+ ): T[];
+
+ /**
+ * @see _.uniq
+ */
+ uniq<T, TSort>(
+ array: List<T>
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.uniq
+ */
+ uniq<TSort>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.uniq
+ */
+ uniq<TSort>(): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniq
+ */
+ uniq(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ uniq<T>(): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniq
+ */
+ uniq<T, TSort>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.uniq
+ */
+ uniq<TSort>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.uniq
+ */
+ uniq<TSort>(): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniq
+ */
+ uniq(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.uniq
+ */
+ uniq<T>(): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniq
+ */
+ uniq<T, TSort>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.uniqBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.uniq` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the criterion by which
+ * uniqueness is computed. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
+ * // => [2.1, 1.2]
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
+ */
+ uniqBy<T>(
+ array: List<T>,
+ iteratee: ListIterator<T, any>
+ ): T[];
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T, TSort>(
+ array: List<T>,
+ iteratee: ListIterator<T, TSort>
+ ): T[];
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ array: List<T>,
+ iteratee: string
+ ): T[];
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ array: List<T>,
+ iteratee: Object
+ ): T[];
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TWhere extends {}, T>(
+ array: List<T>,
+ iteratee: TWhere
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy(
+ iteratee: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TWhere extends {}>(
+ iteratee: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ iteratee: ListIterator<T, any>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T, TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ iteratee: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ iteratee: Object
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TWhere extends {}, T>(
+ iteratee: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy(
+ iteratee: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TWhere extends {}>(
+ iteratee: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ iteratee: ListIterator<T, any>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T, TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ iteratee: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<T>(
+ iteratee: Object
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.uniqBy
+ */
+ uniqBy<TWhere extends {}, T>(
+ iteratee: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.sortedUniq
+ interface LoDashStatic {
+ /**
+ * This method is like `_.uniq` except that it's designed and optimized
+ * for sorted arrays.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.sortedUniq([1, 1, 2]);
+ * // => [1, 2]
+ */
+ sortedUniq<T>(
+ array: List<T>
+ ): T[];
+
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<T, TSort>(
+ array: List<T>
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<TSort>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<TSort>(): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ sortedUniq<T>(): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<T, TSort>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<TSort>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<TSort>(): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<T>(): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniq
+ */
+ sortedUniq<T, TSort>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.sortedUniqBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.uniqBy` except that it's designed and optimized
+ * for sorted arrays.
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
+ * // => [1.1, 2.2]
+ */
+ sortedUniqBy<T>(
+ array: List<T>,
+ iteratee: ListIterator<T, any>
+ ): T[];
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T, TSort>(
+ array: List<T>,
+ iteratee: ListIterator<T, TSort>
+ ): T[];
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ array: List<T>,
+ iteratee: string
+ ): T[];
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ array: List<T>,
+ iteratee: Object
+ ): T[];
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TWhere extends {}, T>(
+ array: List<T>,
+ iteratee: TWhere
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy(
+ iteratee: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TWhere extends {}>(
+ iteratee: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ iteratee: ListIterator<T, any>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T, TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ iteratee: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ iteratee: Object
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TWhere extends {}, T>(
+ iteratee: TWhere
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy(
+ iteratee: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TWhere extends {}>(
+ iteratee: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ iteratee: ListIterator<T, any>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T, TSort>(
+ iteratee: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ iteratee: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<T>(
+ iteratee: Object
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortedUniqBy
+ */
+ sortedUniqBy<TWhere extends {}, T>(
+ iteratee: TWhere
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.unionWith DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.union` except that it accepts `comparator` which
+ * is invoked to compare elements of `arrays`. The comparator is invoked
+ * with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.unionWith(objects, others, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ */
+ unionWith(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.uniqWith DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.uniq` except that it accepts `comparator` which
+ * is invoked to compare elements of `array`. The comparator is invoked with
+ * two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.uniqWith(objects, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
+ */
+ uniqWith(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.unzip
+ interface LoDashStatic {
+ /**
+ * This method is like _.zip except that it accepts an array of grouped elements and creates an array
+ * regrouping the elements to their pre-zip configuration.
+ *
+ * @param array The array of grouped elements to process.
+ * @return Returns the new array of regrouped elements.
+ */
+ unzip<T>(array: List<List<T>>): T[][];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.unzip
+ */
+ unzip<T>(): LoDashImplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.unzip
+ */
+ unzip<T>(): LoDashImplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.unzip
+ */
+ unzip<T>(): LoDashExplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.unzip
+ */
+ unzip<T>(): LoDashExplicitArrayWrapper<T[]>;
+ }
+
+ //_.unzipWith
+ interface LoDashStatic {
+ /**
+ * This method is like _.unzip except that it accepts an iteratee to specify how regrouped values should be
+ * combined. The iteratee is bound to thisArg and invoked with four arguments: (accumulator, value, index,
+ * group).
+ *
+ * @param array The array of grouped elements to process.
+ * @param iteratee The function to combine regrouped values.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the new array of regrouped elements.
+ */
+ unzipWith<TArray, TResult>(
+ array: List<List<TArray>>,
+ iteratee?: MemoIterator<TArray, TResult>
+ ): TResult[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.unzipWith
+ */
+ unzipWith<TArr, TResult>(
+ iteratee?: MemoIterator<TArr, TResult>
+ ): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.unzipWith
+ */
+ unzipWith<TArr, TResult>(
+ iteratee?: MemoIterator<TArr, TResult>
+ ): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ //_.without
+ interface LoDashStatic {
+ /**
+ * Creates an array excluding all provided values using SameValueZero for equality comparisons.
+ *
+ * @param array The array to filter.
+ * @param values The values to exclude.
+ * @return Returns the new array of filtered values.
+ */
+ without<T>(
+ array: List<T>,
+ ...values: T[]
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.without
+ */
+ without(...values: T[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.without
+ */
+ without<T>(...values: T[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.without
+ */
+ without(...values: T[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.without
+ */
+ without<T>(...values: T[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.xor
+ interface LoDashStatic {
+ /**
+ * Creates an array of unique values that is the symmetric difference of the provided arrays.
+ *
+ * @param arrays The arrays to inspect.
+ * @return Returns the new array of values.
+ */
+ xor<T>(...arrays: List<T>[]): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.xor
+ */
+ xor(...arrays: List<T>[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.xor
+ */
+ xor<T>(...arrays: List<T>[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.xor
+ */
+ xor(...arrays: List<T>[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.xor
+ */
+ xor<T>(...arrays: List<T>[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.xorBy DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.xor` except that it accepts `iteratee` which is
+ * invoked for each element of each `arrays` to generate the criterion by which
+ * uniqueness is computed. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of values.
+ * @example
+ *
+ * _.xorBy([2.1, 1.2], [4.3, 2.4], Math.floor);
+ * // => [1.2, 4.3]
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 2 }]
+ */
+ xorBy(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.xorWith DUMMY
+ interface LoDashStatic {
+ /**
+ * This method is like `_.xor` except that it accepts `comparator` which is
+ * invoked to compare elements of `arrays`. The comparator is invoked with
+ * two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.xorWith(objects, others, _.isEqual);
+ * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ */
+ xorWith(
+ array: any[]|List<any>,
+ ...values: any[]
+ ): any[];
+ }
+
+ //_.zip
+ interface LoDashStatic {
+ /**
+ * Creates an array of grouped elements, the first of which contains the first elements of the given arrays,
+ * the second of which contains the second elements of the given arrays, and so on.
+ *
+ * @param arrays The arrays to process.
+ * @return Returns the new array of grouped elements.
+ */
+ zip<T>(...arrays: List<T>[]): T[][];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.zip
+ */
+ zip<T>(...arrays: List<T>[]): _.LoDashImplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.zip
+ */
+ zip<T>(...arrays: List<T>[]): _.LoDashImplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.zip
+ */
+ zip<T>(...arrays: List<T>[]): _.LoDashExplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.zip
+ */
+ zip<T>(...arrays: List<T>[]): _.LoDashExplicitArrayWrapper<T[]>;
+ }
+
+ //_.zipObject
+ interface LoDashStatic {
+ /**
+ * The inverse of _.pairs; this method returns an object composed from arrays of property names and values.
+ * Provide either a single two dimensional array, e.g. [[key1, value1], [key2, value2]] or two arrays, one of
+ * property names and one of corresponding values.
+ *
+ * @param props The property names.
+ * @param values The property values.
+ * @return Returns the new object.
+ */
+ zipObject<TValues, TResult extends {}>(
+ props: List<StringRepresentable>|List<List<any>>,
+ values?: List<TValues>
+ ): TResult;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TResult extends {}>(
+ props: List<StringRepresentable>|List<List<any>>,
+ values?: List<any>
+ ): TResult;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject(
+ props: List<StringRepresentable>|List<List<any>>,
+ values?: List<any>
+ ): _.Dictionary<any>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TValues, TResult extends {}>(
+ values?: List<TValues>
+ ): _.LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TResult extends {}>(
+ values?: List<any>
+ ): _.LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject(
+ values?: List<any>
+ ): _.LoDashImplicitObjectWrapper<_.Dictionary<any>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TValues, TResult extends {}>(
+ values?: List<TValues>
+ ): _.LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TResult extends {}>(
+ values?: List<any>
+ ): _.LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject(
+ values?: List<any>
+ ): _.LoDashImplicitObjectWrapper<_.Dictionary<any>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TValues, TResult extends {}>(
+ values?: List<TValues>
+ ): _.LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TResult extends {}>(
+ values?: List<any>
+ ): _.LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject(
+ values?: List<any>
+ ): _.LoDashExplicitObjectWrapper<_.Dictionary<any>>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TValues, TResult extends {}>(
+ values?: List<TValues>
+ ): _.LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject<TResult extends {}>(
+ values?: List<any>
+ ): _.LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.zipObject
+ */
+ zipObject(
+ values?: List<any>
+ ): _.LoDashExplicitObjectWrapper<_.Dictionary<any>>;
+ }
+
+ //_.zipWith
+ interface LoDashStatic {
+ /**
+ * This method is like _.zip except that it accepts an iteratee to specify how grouped values should be
+ * combined. The iteratee is bound to thisArg and invoked with four arguments: (accumulator, value, index,
+ * group).
+ * @param {...Array} [arrays] The arrays to process.
+ * @param {Function} [iteratee] The function to combine grouped values.
+ * @param {*} [thisArg] The `this` binding of `iteratee`.
+ * @return Returns the new array of grouped elements.
+ */
+ zipWith<TResult>(...args: any[]): TResult[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.zipWith
+ */
+ zipWith<TResult>(...args: any[]): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ /*********
+ * Chain *
+ *********/
+
+ //_.chain
+ interface LoDashStatic {
+ /**
+ * Creates a lodash object that wraps value with explicit method chaining enabled.
+ *
+ * @param value The value to wrap.
+ * @return Returns the new lodash wrapper instance.
+ */
+ chain(value: number): LoDashExplicitWrapper<number>;
+ chain(value: string): LoDashExplicitWrapper<string>;
+ chain(value: boolean): LoDashExplicitWrapper<boolean>;
+ chain<T>(value: T[]): LoDashExplicitArrayWrapper<T>;
+ chain<T extends {}>(value: T): LoDashExplicitObjectWrapper<T>;
+ chain(value: any): LoDashExplicitWrapper<any>;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.chain
+ */
+ chain(): LoDashExplicitWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.chain
+ */
+ chain(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.chain
+ */
+ chain(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.chain
+ */
+ chain(): TWrapper;
+ }
+
+ //_.tap
+ interface LoDashStatic {
+ /**
+ * This method invokes interceptor and returns value. The interceptor is bound to thisArg and invoked with one
+ * argument; (value). The purpose of this method is to "tap into" a method chain in order to perform operations
+ * on intermediate results within the chain.
+ *
+ * @param value The value to provide to interceptor.
+ * @param interceptor The function to invoke.
+ * @parem thisArg The this binding of interceptor.
+ * @return Returns value.
+ **/
+ tap<T>(
+ value: T,
+ interceptor: (value: T) => void
+ ): T;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.tap
+ */
+ tap(
+ interceptor: (value: T) => void
+ ): TWrapper;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.tap
+ */
+ tap(
+ interceptor: (value: T) => void
+ ): TWrapper;
+ }
+
+ //_.thru
+ interface LoDashStatic {
+ /**
+ * This method is like _.tap except that it returns the result of interceptor.
+ *
+ * @param value The value to provide to interceptor.
+ * @param interceptor The function to invoke.
+ * @param thisArg The this binding of interceptor.
+ * @return Returns the result of interceptor.
+ */
+ thru<T, TResult>(
+ value: T,
+ interceptor: (value: T) => TResult
+ ): TResult;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends number>(
+ interceptor: (value: T) => TResult): LoDashImplicitWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends string>(
+ interceptor: (value: T) => TResult): LoDashImplicitWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends boolean>(
+ interceptor: (value: T) => TResult): LoDashImplicitWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends {}>(
+ interceptor: (value: T) => TResult): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult>(
+ interceptor: (value: T) => TResult[]): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends number>(
+ interceptor: (value: T) => TResult
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends string>(
+ interceptor: (value: T) => TResult
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends boolean>(
+ interceptor: (value: T) => TResult
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult extends {}>(
+ interceptor: (value: T) => TResult
+ ): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.thru
+ */
+ thru<TResult>(
+ interceptor: (value: T) => TResult[]
+ ): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.prototype.commit
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * Executes the chained sequence and returns the wrapped result.
+ *
+ * @return Returns the new lodash wrapper instance.
+ */
+ commit(): TWrapper;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.commit
+ */
+ commit(): TWrapper;
+ }
+
+ //_.prototype.concat
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * Creates a new array joining a wrapped array with any additional arrays and/or values.
+ *
+ * @param items
+ * @return Returns the new concatenated array.
+ */
+ concat<TItem>(...items: Array<TItem|Array<TItem>>): LoDashImplicitArrayWrapper<TItem>;
+
+ /**
+ * @see _.concat
+ */
+ concat(...items: Array<T|Array<T>>): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.concat
+ */
+ concat<TItem>(...items: Array<TItem|Array<TItem>>): LoDashExplicitArrayWrapper<TItem>;
+
+ /**
+ * @see _.concat
+ */
+ concat(...items: Array<T|Array<T>>): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.prototype.plant
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * Creates a clone of the chained sequence planting value as the wrapped value.
+ * @param value The value to plant as the wrapped value.
+ * @return Returns the new lodash wrapper instance.
+ */
+ plant(value: number): LoDashImplicitWrapper<number>;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: string): LoDashImplicitStringWrapper;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: boolean): LoDashImplicitWrapper<boolean>;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: number[]): LoDashImplicitNumberArrayWrapper;
+
+ /**
+ * @see _.plant
+ */
+ plant<T>(value: T[]): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.plant
+ */
+ plant<T extends {}>(value: T): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: any): LoDashImplicitWrapper<any>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.plant
+ */
+ plant(value: number): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: string): LoDashExplicitStringWrapper;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: boolean): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: number[]): LoDashExplicitNumberArrayWrapper;
+
+ /**
+ * @see _.plant
+ */
+ plant<T>(value: T[]): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.plant
+ */
+ plant<T extends {}>(value: T): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.plant
+ */
+ plant(value: any): LoDashExplicitWrapper<any>;
+ }
+
+ //_.prototype.reverse
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * Reverses the wrapped array so the first element becomes the last, the second element becomes the second to
+ * last, and so on.
+ *
+ * Note: This method mutates the wrapped array.
+ *
+ * @return Returns the new reversed lodash wrapper instance.
+ */
+ reverse(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.reverse
+ */
+ reverse(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.prototype.toJSON
+ interface LoDashWrapperBase<T, TWrapper> {
+ /**
+ * @see _.value
+ */
+ toJSON(): T;
+ }
+
+ //_.prototype.toString
+ interface LoDashWrapperBase<T, TWrapper> {
+ /**
+ * Produces the result of coercing the unwrapped value to a string.
+ *
+ * @return Returns the coerced string value.
+ */
+ toString(): string;
+ }
+
+ //_.prototype.value
+ interface LoDashWrapperBase<T, TWrapper> {
+ /**
+ * Executes the chained sequence to extract the unwrapped value.
+ *
+ * @alias _.toJSON, _.valueOf
+ *
+ * @return Returns the resolved unwrapped value.
+ */
+ value(): T;
+ }
+
+ //_.valueOf
+ interface LoDashWrapperBase<T, TWrapper> {
+ /**
+ * @see _.value
+ */
+ valueOf(): T;
+ }
+
+ /**************
+ * Collection *
+ **************/
+
+ //_.at
+ interface LoDashStatic {
+ /**
+ * Creates an array of elements corresponding to the given keys, or indexes, of collection. Keys may be
+ * specified as individual arguments or as arrays of keys.
+ *
+ * @param collection The collection to iterate over.
+ * @param props The property names or indexes of elements to pick, specified individually or in arrays.
+ * @return Returns the new array of picked elements.
+ */
+ at<T>(
+ collection: List<T>|Dictionary<T>,
+ ...props: (number|string|(number|string)[])[]
+ ): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.at
+ */
+ at(...props: (number|string|(number|string)[])[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.at
+ */
+ at<T>(...props: (number|string|(number|string)[])[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.at
+ */
+ at(...props: (number|string|(number|string)[])[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.at
+ */
+ at<T>(...props: (number|string|(number|string)[])[]): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.countBy
+ interface LoDashStatic {
+ /**
+ * Creates an object composed of keys generated from the results of running each element of collection through
+ * iteratee. The corresponding value of each key is the number of times the key was returned by iteratee. The
+ * iteratee is bound to thisArg and invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for iteratee the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for iteratee the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the composed aggregate object.
+ */
+ countBy<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): Dictionary<number>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<number>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<T>(
+ collection: NumericDictionary<T>,
+ iteratee?: NumericDictionaryIterator<T, any>
+ ): Dictionary<number>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ iteratee?: string
+ ): Dictionary<number>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<W, T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ iteratee?: W
+ ): Dictionary<number>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ iteratee?: Object
+ ): Dictionary<number>;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<W>(
+ iteratee?: W
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.countBy
+ */
+ countBy<T>(
+ iteratee?: ListIterator<T, any>|DictionaryIterator<T, any>|NumericDictionaryIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<W>(
+ iteratee?: W
+ ): LoDashImplicitObjectWrapper<Dictionary<number>>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<W>(
+ iteratee?: W
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.countBy
+ */
+ countBy<T>(
+ iteratee?: ListIterator<T, any>|DictionaryIterator<T, any>|NumericDictionaryIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+
+ /**
+ * @see _.countBy
+ */
+ countBy<W>(
+ iteratee?: W
+ ): LoDashExplicitObjectWrapper<Dictionary<number>>;
+ }
+
+ //_.each
+ interface LoDashStatic {
+ /**
+ * @see _.forEach
+ */
+ each<T>(
+ collection: T[],
+ iteratee?: ListIterator<T, any>
+ ): T[];
+
+ /**
+ * @see _.forEach
+ */
+ each<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): List<T>;
+
+ /**
+ * @see _.forEach
+ */
+ each<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forEach
+ */
+ each<T extends {}>(
+ collection: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+
+ /**
+ * @see _.forEach
+ */
+ each<T extends {}, TValue>(
+ collection: T,
+ iteratee?: ObjectIterator<TValue, any>
+ ): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ each(
+ iteratee: ListIterator<string, any>
+ ): LoDashImplicitWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ each(
+ iteratee: ListIterator<T, any>
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ each<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ each(
+ iteratee: ListIterator<string, any>
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ each(
+ iteratee: ListIterator<T, any>
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ each<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.eachRight
+ interface LoDashStatic {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<T>(
+ collection: T[],
+ iteratee?: ListIterator<T, any>
+ ): T[];
+
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): List<T>;
+
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<T extends {}>(
+ collection: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<T extends {}, TValue>(
+ collection: T,
+ iteratee?: ObjectIterator<TValue, any>
+ ): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight(
+ iteratee: ListIterator<string, any>
+ ): LoDashImplicitWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight(
+ iteratee: ListIterator<T, any>
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight(
+ iteratee: ListIterator<string, any>
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight(
+ iteratee: ListIterator<T, any>
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ eachRight<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.every
+ interface LoDashStatic {
+ /**
+ * Checks if predicate returns truthy for all elements of collection. Iteration is stopped once predicate
+ * returns falsey. The predicate is invoked with three arguments: (value, index|key, collection).
+ *
+ * @param collection The collection to iterate over.
+ * @param predicate The function invoked per iteration.
+ * @return Returns true if all elements pass the predicate check, else false.
+ */
+ every<T>(
+ collection: List<T>,
+ predicate?: ListIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every<T>(
+ collection: Dictionary<T>,
+ predicate?: DictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every<T>(
+ collection: NumericDictionary<T>,
+ predicate?: NumericDictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ predicate?: string|any[]
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every<TObject extends {}, T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ predicate?: TObject
+ ): boolean;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.every
+ */
+ every(
+ predicate?: ListIterator<T, boolean>|NumericDictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every(
+ predicate?: string|any[]
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every<TObject extends {}>(
+ predicate?: TObject
+ ): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.every
+ */
+ every<TResult>(
+ predicate?: ListIterator<TResult, boolean>|DictionaryIterator<TResult, boolean>|NumericDictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every(
+ predicate?: string|any[]
+ ): boolean;
+
+ /**
+ * @see _.every
+ */
+ every<TObject extends {}>(
+ predicate?: TObject
+ ): boolean;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.every
+ */
+ every(
+ predicate?: ListIterator<T, boolean>|NumericDictionaryIterator<T, boolean>
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.every
+ */
+ every(
+ predicate?: string|any[]
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.every
+ */
+ every<TObject extends {}>(
+ predicate?: TObject
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.every
+ */
+ every<TResult>(
+ predicate?: ListIterator<TResult, boolean>|DictionaryIterator<TResult, boolean>|NumericDictionaryIterator<T, boolean>
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.every
+ */
+ every(
+ predicate?: string|any[]
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.every
+ */
+ every<TObject extends {}>(
+ predicate?: TObject
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.filter
+ interface LoDashStatic {
+ /**
+ * Iterates over elements of collection, returning an array of all elements predicate returns truthy for. The
+ * predicate is bound to thisArg and invoked with three arguments: (value, index|key, collection).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param collection The collection to iterate over.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the new filtered array.
+ */
+ filter<T>(
+ collection: List<T>,
+ predicate?: ListIterator<T, boolean>
+ ): T[];
+
+ /**
+ * @see _.filter
+ */
+ filter<T>(
+ collection: Dictionary<T>,
+ predicate?: DictionaryIterator<T, boolean>
+ ): T[];
+
+ /**
+ * @see _.filter
+ */
+ filter(
+ collection: string,
+ predicate?: StringIterator<boolean>
+ ): string[];
+
+ /**
+ * @see _.filter
+ */
+ filter<T>(
+ collection: List<T>|Dictionary<T>,
+ predicate: string
+ ): T[];
+
+ /**
+ * @see _.filter
+ */
+ filter<W extends {}, T>(
+ collection: List<T>|Dictionary<T>,
+ predicate: W
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.filter
+ */
+ filter(
+ predicate?: StringIterator<boolean>
+ ): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.filter
+ */
+ filter(
+ predicate: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter(
+ predicate: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter<W>(predicate: W): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.filter
+ */
+ filter<T>(
+ predicate: ListIterator<T, boolean>|DictionaryIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter<T>(
+ predicate: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter<W, T>(predicate: W): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.filter
+ */
+ filter(
+ predicate?: StringIterator<boolean>
+ ): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.filter
+ */
+ filter(
+ predicate: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter(
+ predicate: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter<W>(predicate: W): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.filter
+ */
+ filter<T>(
+ predicate: ListIterator<T, boolean>|DictionaryIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter<T>(
+ predicate: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.filter
+ */
+ filter<W, T>(predicate: W): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.find
+ interface LoDashStatic {
+ /**
+ * Iterates over elements of collection, returning the first element predicate returns truthy for.
+ * The predicate is bound to thisArg and invoked with three arguments: (value, index|key, collection).
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param collection The collection to search.
+ * @param predicate The function invoked per iteration.
+ * @param fromIndex The index to search from.
+ * @return Returns the matched element, else undefined.
+ */
+ find<T>(
+ collection: List<T>,
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ */
+ find<T>(
+ collection: Dictionary<T>,
+ predicate?: DictionaryIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ */
+ find<T>(
+ collection: List<T>|Dictionary<T>,
+ predicate?: string,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ */
+ find<TObject extends {}, T>(
+ collection: List<T>|Dictionary<T>,
+ predicate?: TObject,
+ fromIndex?: number
+ ): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.find
+ */
+ find(
+ predicate?: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ */
+ find(
+ predicate?: string,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ */
+ find<TObject extends {}>(
+ predicate?: TObject,
+ fromIndex?: number
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.find
+ */
+ find<TResult>(
+ predicate?: ListIterator<TResult, boolean>|DictionaryIterator<TResult, boolean>,
+ fromIndex?: number
+ ): TResult;
+
+ /**
+ * @see _.find
+ */
+ find<TResult>(
+ predicate?: string,
+ fromIndex?: number
+ ): TResult;
+
+ /**
+ * @see _.find
+ */
+ find<TObject extends {}, TResult>(
+ predicate?: TObject,
+ fromIndex?: number
+ ): TResult;
+ }
+
+ //_.findLast
+ interface LoDashStatic {
+ /**
+ * This method is like _.find except that it iterates over elements of a collection from
+ * right to left.
+ * @param collection Searches for a value in this list.
+ * @param callback The function called per iteration.
+ * @param fromIndex The index to search from.
+ * @return The found element, else undefined.
+ **/
+ findLast<T>(
+ collection: Array<T>,
+ callback: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ **/
+ findLast<T>(
+ collection: List<T>,
+ callback: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ **/
+ findLast<T>(
+ collection: Dictionary<T>,
+ callback: DictionaryIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ * @param _.pluck style callback
+ **/
+ findLast<W, T>(
+ collection: Array<T>,
+ whereValue: W,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ * @param _.pluck style callback
+ **/
+ findLast<W, T>(
+ collection: List<T>,
+ whereValue: W,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ * @param _.pluck style callback
+ **/
+ findLast<W, T>(
+ collection: Dictionary<T>,
+ whereValue: W,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ * @param _.where style callback
+ **/
+ findLast<T>(
+ collection: Array<T>,
+ pluckValue: string,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ * @param _.where style callback
+ **/
+ findLast<T>(
+ collection: List<T>,
+ pluckValue: string,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.find
+ * @param _.where style callback
+ **/
+ findLast<T>(
+ collection: Dictionary<T>,
+ pluckValue: string,
+ fromIndex?: number
+ ): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.findLast
+ */
+ findLast(
+ callback: ListIterator<T, boolean>,
+ fromIndex?: number
+ ): T;
+ /**
+ * @see _.findLast
+ * @param _.where style callback
+ */
+ findLast<W>(
+ whereValue: W,
+ fromIndex?: number
+ ): T;
+
+ /**
+ * @see _.findLast
+ * @param _.where style callback
+ */
+ findLast(
+ pluckValue: string,
+ fromIndex?: number
+ ): T;
+ }
+
+ //_.flatMap
+ interface LoDashStatic {
+ /**
+ * Creates an array of flattened values by running each element in collection through iteratee
+ * and concating its result to the other mapped values. The iteratee is invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @return Returns the new flattened array.
+ */
+ flatMap<T, TResult>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ collection: List<any>,
+ iteratee?: ListIterator<any, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<T, TResult>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ collection: Dictionary<any>,
+ iteratee?: DictionaryIterator<any, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<T, TResult>(
+ collection: NumericDictionary<T>,
+ iteratee?: NumericDictionaryIterator<T, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ collection: NumericDictionary<any>,
+ iteratee?: NumericDictionaryIterator<any, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TObject extends Object, TResult>(
+ collection: TObject,
+ iteratee?: ObjectIterator<any, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ collection: Object,
+ iteratee?: ObjectIterator<any, TResult|TResult[]>
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TWhere extends Object, TObject extends Object>(
+ collection: TObject,
+ iteratee: TWhere
+ ): boolean[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TObject extends Object, TResult>(
+ collection: TObject,
+ iteratee: Object|string
+ ): TResult[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TObject extends Object>(
+ collection: TObject,
+ iteratee: [string, any]
+ ): boolean[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ collection: string
+ ): string[];
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ collection: Object,
+ iteratee?: Object|string
+ ): TResult[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ iteratee: ListIterator<string, TResult|TResult[]>
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap(): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ iteratee: ListIterator<T, TResult|TResult[]>|string
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TWhere extends Object>(
+ iteratee: TWhere
+ ): LoDashImplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap(
+ iteratee: [string, any]
+ ): LoDashImplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.flatMap
+ */
+ flatMap<T, TResult>(
+ iteratee: ListIterator<T, TResult|TResult[]>|DictionaryIterator<T, TResult|TResult[]>|NumericDictionaryIterator<T, TResult|TResult[]>
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ iteratee: ObjectIterator<any, TResult|TResult[]>|string
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TWhere extends Object>(
+ iteratee: TWhere
+ ): LoDashImplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap(
+ iteratee: [string, any]
+ ): LoDashImplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ iteratee: ListIterator<string, TResult|TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap(): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ iteratee: ListIterator<T, TResult|TResult[]>|string
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TWhere extends Object>(
+ iteratee: TWhere
+ ): LoDashExplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap(
+ iteratee: [string, any]
+ ): LoDashExplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.flatMap
+ */
+ flatMap<T, TResult>(
+ iteratee: ListIterator<T, TResult|TResult[]>|DictionaryIterator<T, TResult|TResult[]>|NumericDictionaryIterator<T, TResult|TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(
+ iteratee: ObjectIterator<any, TResult|TResult[]>|string
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TWhere extends Object>(
+ iteratee: TWhere
+ ): LoDashExplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap(
+ iteratee: [string, any]
+ ): LoDashExplicitArrayWrapper<boolean>;
+
+ /**
+ * @see _.flatMap
+ */
+ flatMap<TResult>(): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.forEach
+ interface LoDashStatic {
+ /**
+ * Iterates over elements of collection invoking iteratee for each element. The iteratee is bound to thisArg
+ * and invoked with three arguments:
+ * (value, index|key, collection). Iteratee functions may exit iteration early by explicitly returning false.
+ *
+ * Note: As with other "Collections" methods, objects with a "length" property are iterated like arrays. To
+ * avoid this behavior _.forIn or _.forOwn may be used for object iteration.
+ *
+ * @alias _.each
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ */
+ forEach<T>(
+ collection: T[],
+ iteratee?: ListIterator<T, any>
+ ): T[];
+
+ /**
+ * @see _.forEach
+ */
+ forEach<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): List<T>;
+
+ /**
+ * @see _.forEach
+ */
+ forEach<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forEach
+ */
+ forEach<T extends {}>(
+ collection: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+
+ /**
+ * @see _.forEach
+ */
+ forEach<T extends {}, TValue>(
+ collection: T,
+ iteratee?: ObjectIterator<TValue, any>
+ ): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ forEach(
+ iteratee: ListIterator<string, any>
+ ): LoDashImplicitWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ forEach(
+ iteratee: ListIterator<T, any>
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ forEach<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ forEach(
+ iteratee: ListIterator<string, any>
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ forEach(
+ iteratee: ListIterator<T, any>
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forEach
+ */
+ forEach<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.forEachRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.forEach except that it iterates over elements of collection from right to left.
+ *
+ * @alias _.eachRight
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function called per iteration.
+ * @param thisArg The this binding of callback.
+ */
+ forEachRight<T>(
+ collection: T[],
+ iteratee?: ListIterator<T, any>
+ ): T[];
+
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): List<T>;
+
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight<T extends {}>(
+ collection: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight<T extends {}, TValue>(
+ collection: T,
+ iteratee?: ObjectIterator<TValue, any>
+ ): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight(
+ iteratee: ListIterator<string, any>
+ ): LoDashImplicitWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight(
+ iteratee: ListIterator<T, any>
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight(
+ iteratee: ListIterator<string, any>
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight(
+ iteratee: ListIterator<T, any>
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forEachRight
+ */
+ forEachRight<TValue>(
+ iteratee?: ListIterator<TValue, any>|DictionaryIterator<TValue, any>
+ ): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.groupBy
+ interface LoDashStatic {
+ /**
+ * Creates an object composed of keys generated from the results of running each element of collection through
+ * iteratee. The corresponding value of each key is an array of the elements responsible for generating the
+ * key. The iteratee is bound to thisArg and invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for iteratee the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for iteratee the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the composed aggregate object.
+ */
+ groupBy<T, TKey>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, TKey>
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ collection: List<any>,
+ iteratee?: ListIterator<T, any>
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T, TKey>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, TKey>
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ collection: Dictionary<any>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T, TValue>(
+ collection: List<T>|Dictionary<T>,
+ iteratee?: string
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ collection: List<T>|Dictionary<T>,
+ iteratee?: string
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TWhere, T>(
+ collection: List<T>|Dictionary<T>,
+ iteratee?: TWhere
+ ): Dictionary<T[]>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ collection: List<T>|Dictionary<T>,
+ iteratee?: Object
+ ): Dictionary<T[]>;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TKey>(
+ iteratee?: ListIterator<T, TKey>
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TKey>(
+ iteratee?: ListIterator<T, TKey>
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TValue>(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TWhere>(
+ iteratee?: TWhere
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T, TKey>(
+ iteratee?: ListIterator<T, TKey>|DictionaryIterator<T, TKey>
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ iteratee?: ListIterator<T, any>|DictionaryIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T, TValue>(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TWhere, T>(
+ iteratee?: TWhere
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ iteratee?: Object
+ ): LoDashImplicitObjectWrapper<Dictionary<T[]>>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TKey>(
+ iteratee?: ListIterator<T, TKey>
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TKey>(
+ iteratee?: ListIterator<T, TKey>
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TValue>(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TWhere>(
+ iteratee?: TWhere
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T, TKey>(
+ iteratee?: ListIterator<T, TKey>|DictionaryIterator<T, TKey>
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ iteratee?: ListIterator<T, any>|DictionaryIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T, TValue>(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<TWhere, T>(
+ iteratee?: TWhere
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+
+ /**
+ * @see _.groupBy
+ */
+ groupBy<T>(
+ iteratee?: Object
+ ): LoDashExplicitObjectWrapper<Dictionary<T[]>>;
+ }
+
+ //_.includes
+ interface LoDashStatic {
+ /**
+ * Checks if target is in collection using SameValueZero for equality comparisons. If fromIndex is negative,
+ * it’s used as the offset from the end of collection.
+ *
+ * @param collection The collection to search.
+ * @param target The value to search for.
+ * @param fromIndex The index to search from.
+ * @return True if the target element is found, else false.
+ */
+ includes<T>(
+ collection: List<T>|Dictionary<T>,
+ target: T,
+ fromIndex?: number
+ ): boolean;
+
+ /**
+ * @see _.includes
+ */
+ includes(
+ collection: string,
+ target: string,
+ fromIndex?: number
+ ): boolean;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.includes
+ */
+ includes(
+ target: T,
+ fromIndex?: number
+ ): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.includes
+ */
+ includes<TValue>(
+ target: TValue,
+ fromIndex?: number
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.includes
+ */
+ includes(
+ target: string,
+ fromIndex?: number
+ ): boolean;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.includes
+ */
+ includes(
+ target: T,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.includes
+ */
+ includes<TValue>(
+ target: TValue,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.includes
+ */
+ includes(
+ target: string,
+ fromIndex?: number
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.keyBy
+ interface LoDashStatic {
+ /**
+ * Creates an object composed of keys generated from the results of running each element of collection through
+ * iteratee. The corresponding value of each key is the last element responsible for generating the key. The
+ * iteratee function is bound to thisArg and invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * If a property name is provided for iteratee the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for iteratee the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the composed aggregate object.
+ */
+ keyBy<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ collection: NumericDictionary<T>,
+ iteratee?: NumericDictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ collection: List<T>|NumericDictionary<T>|Dictionary<T>,
+ iteratee?: string
+ ): Dictionary<T>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<W extends Object, T>(
+ collection: List<T>|NumericDictionary<T>|Dictionary<T>,
+ iteratee?: W
+ ): Dictionary<T>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ collection: List<T>|NumericDictionary<T>|Dictionary<T>,
+ iteratee?: Object
+ ): Dictionary<T>;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.keyBy
+ */
+ keyBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.keyBy
+ */
+ keyBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<W extends Object>(
+ iteratee?: W
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ iteratee?: ListIterator<T, any>|NumericDictionaryIterator<T, any>|DictionaryIterator<T, any>
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<W extends Object, T>(
+ iteratee?: W
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ iteratee?: Object
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.keyBy
+ */
+ keyBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.keyBy
+ */
+ keyBy(
+ iteratee?: ListIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<W extends Object>(
+ iteratee?: W
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ iteratee?: ListIterator<T, any>|NumericDictionaryIterator<T, any>|DictionaryIterator<T, any>
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<W extends Object, T>(
+ iteratee?: W
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.keyBy
+ */
+ keyBy<T>(
+ iteratee?: Object
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ //_.invoke
+ interface LoDashStatic {
+ /**
+ * Invokes the method at path of object.
+ * @param object The object to query.
+ * @param path The path of the method to invoke.
+ * @param args The arguments to invoke the method with.
+ **/
+ invoke<TObject extends Object, TResult>(
+ object: TObject,
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+
+ /**
+ * @see _.invoke
+ **/
+ invoke<TValue, TResult>(
+ object: Dictionary<TValue>|TValue[],
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+
+ /**
+ * @see _.invoke
+ **/
+ invoke<TResult>(
+ object: any,
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.invoke
+ **/
+ invoke<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.invoke
+ **/
+ invoke<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.invoke
+ **/
+ invoke<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.invoke
+ **/
+ invoke<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ ...args: any[]): TResult;
+ }
+
+ //_.invokeMap
+ interface LoDashStatic {
+ /**
+ * Invokes the method named by methodName on each element in the collection returning
+ * an array of the results of each invoked method. Additional arguments will be provided
+ * to each invoked method. If methodName is a function it will be invoked for, and this
+ * bound to, each element in the collection.
+ * @param collection The collection to iterate over.
+ * @param methodName The name of the method to invoke.
+ * @param args Arguments to invoke the method with.
+ **/
+ invokeMap<TValue extends {}, TResult>(
+ collection: TValue[],
+ methodName: string,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TValue extends {}, TResult>(
+ collection: Dictionary<TValue>,
+ methodName: string,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ collection: {}[],
+ methodName: string,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ collection: Dictionary<{}>,
+ methodName: string,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TValue extends {}, TResult>(
+ collection: TValue[],
+ method: (...args: any[]) => TResult,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TValue extends {}, TResult>(
+ collection: Dictionary<TValue>,
+ method: (...args: any[]) => TResult,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ collection: {}[],
+ method: (...args: any[]) => TResult,
+ ...args: any[]): TResult[];
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ collection: Dictionary<{}>,
+ method: (...args: any[]) => TResult,
+ ...args: any[]): TResult[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ methodName: string,
+ ...args: any[]): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ method: (...args: any[]) => TResult,
+ ...args: any[]): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ methodName: string,
+ ...args: any[]): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ method: (...args: any[]) => TResult,
+ ...args: any[]): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ methodName: string,
+ ...args: any[]): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ method: (...args: any[]) => TResult,
+ ...args: any[]): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ methodName: string,
+ ...args: any[]): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.invokeMap
+ **/
+ invokeMap<TResult>(
+ method: (...args: any[]) => TResult,
+ ...args: any[]): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.map
+ interface LoDashStatic {
+ /**
+ * Creates an array of values by running each element in collection through iteratee. The iteratee is bound to
+ * thisArg and invoked with three arguments: (value, index|key, collection).
+ *
+ * If a property name is provided for iteratee the created _.property style callback returns the property value
+ * of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for iteratee the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like _.every, _.filter, _.map, _.mapValues,
+ * _.reject, and _.some.
+ *
+ * The guarded methods are:
+ * ary, callback, chunk, clone, create, curry, curryRight, drop, dropRight, every, fill, flatten, invert, max,
+ * min, parseInt, slice, sortBy, take, takeRight, template, trim, trimLeft, trimRight, trunc, random, range,
+ * sample, some, sum, uniq, and words
+ *
+ * @param collection The collection to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the new mapped array.
+ */
+ map<T, TResult>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, TResult>
+ ): TResult[];
+
+ /**
+ * @see _.map
+ */
+ map<T extends {}, TResult>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, TResult>
+ ): TResult[];
+
+ map<T extends {}, TResult>(
+ collection: NumericDictionary<T>,
+ iteratee?: NumericDictionaryIterator<T, TResult>
+ ): TResult[];
+
+ /**
+ * @see _.map
+ */
+ map<T, TResult>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ iteratee?: string
+ ): TResult[];
+
+ /**
+ * @see _.map
+ */
+ map<T, TObject extends {}>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ iteratee?: TObject
+ ): boolean[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.map
+ */
+ map<TResult>(
+ iteratee?: ListIterator<T, TResult>
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TResult>(
+ iteratee?: string
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashImplicitArrayWrapper<boolean>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.map
+ */
+ map<TValue, TResult>(
+ iteratee?: ListIterator<TValue, TResult>|DictionaryIterator<TValue, TResult>
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TValue, TResult>(
+ iteratee?: string
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashImplicitArrayWrapper<boolean>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.map
+ */
+ map<TResult>(
+ iteratee?: ListIterator<T, TResult>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TResult>(
+ iteratee?: string
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashExplicitArrayWrapper<boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.map
+ */
+ map<TValue, TResult>(
+ iteratee?: ListIterator<TValue, TResult>|DictionaryIterator<TValue, TResult>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TValue, TResult>(
+ iteratee?: string
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.map
+ */
+ map<TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashExplicitArrayWrapper<boolean>;
+ }
+
+ //_.partition
+ interface LoDashStatic {
+ /**
+ * Creates an array of elements split into two groups, the first of which contains elements predicate returns truthy for,
+ * while the second of which contains elements predicate returns falsey for.
+ * The predicate is bound to thisArg and invoked with three arguments: (value, index|key, collection).
+ *
+ * If a property name is provided for predicate the created _.property style callback
+ * returns the property value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback
+ * returns true for elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns
+ * true for elements that have the properties of the given object, else false.
+ *
+ * @param collection The collection to iterate over.
+ * @param callback The function called per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the array of grouped elements.
+ **/
+ partition<T>(
+ collection: List<T>,
+ callback: ListIterator<T, boolean>): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<T>(
+ collection: Dictionary<T>,
+ callback: DictionaryIterator<T, boolean>): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<W, T>(
+ collection: List<T>,
+ whereValue: W): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<W, T>(
+ collection: Dictionary<T>,
+ whereValue: W): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<T>(
+ collection: List<T>,
+ path: string,
+ srcValue: any): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<T>(
+ collection: Dictionary<T>,
+ path: string,
+ srcValue: any): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<T>(
+ collection: List<T>,
+ pluckValue: string): T[][];
+
+ /**
+ * @see _.partition
+ **/
+ partition<T>(
+ collection: Dictionary<T>,
+ pluckValue: string): T[][];
+ }
+
+ interface LoDashImplicitStringWrapper {
+ /**
+ * @see _.partition
+ */
+ partition(
+ callback: ListIterator<string, boolean>): LoDashImplicitArrayWrapper<string[]>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.partition
+ */
+ partition(
+ callback: ListIterator<T, boolean>): LoDashImplicitArrayWrapper<T[]>;
+ /**
+ * @see _.partition
+ */
+ partition<W>(
+ whereValue: W): LoDashImplicitArrayWrapper<T[]>;
+ /**
+ * @see _.partition
+ */
+ partition(
+ path: string,
+ srcValue: any): LoDashImplicitArrayWrapper<T[]>;
+ /**
+ * @see _.partition
+ */
+ partition(
+ pluckValue: string): LoDashImplicitArrayWrapper<T[]>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.partition
+ */
+ partition<TResult>(
+ callback: ListIterator<TResult, boolean>): LoDashImplicitArrayWrapper<TResult[]>;
+
+ /**
+ * @see _.partition
+ */
+ partition<TResult>(
+ callback: DictionaryIterator<TResult, boolean>): LoDashImplicitArrayWrapper<TResult[]>;
+
+ /**
+ * @see _.partition
+ */
+ partition<W, TResult>(
+ whereValue: W): LoDashImplicitArrayWrapper<TResult[]>;
+
+ /**
+ * @see _.partition
+ */
+ partition<TResult>(
+ path: string,
+ srcValue: any): LoDashImplicitArrayWrapper<TResult[]>;
+
+ /**
+ * @see _.partition
+ */
+ partition<TResult>(
+ pluckValue: string): LoDashImplicitArrayWrapper<TResult[]>;
+ }
+
+ //_.reduce
+ interface LoDashStatic {
+ /**
+ * Reduces a collection to a value which is the accumulated result of running each
+ * element in the collection through the callback, where each successive callback execution
+ * consumes the return value of the previous execution. If accumulator is not provided the
+ * first element of the collection will be used as the initial accumulator value. The callback
+ * is bound to thisArg and invoked with four arguments; (accumulator, value, index|key, collection).
+ * @param collection The collection to iterate over.
+ * @param callback The function called per iteration.
+ * @param accumulator Initial value of the accumulator.
+ * @param thisArg The this binding of callback.
+ * @return Returns the accumulated value.
+ **/
+ reduce<T, TResult>(
+ collection: Array<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: List<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: Dictionary<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: NumericDictionary<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: Array<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: List<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: Dictionary<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<T, TResult>(
+ collection: NumericDictionary<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.reduce
+ **/
+ reduce<TResult>(
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<TResult>(
+ callback: MemoIterator<T, TResult>): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.reduce
+ **/
+ reduce<TValue, TResult>(
+ callback: MemoIterator<TValue, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<TValue, TResult>(
+ callback: MemoIterator<TValue, TResult>): TResult;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.reduce
+ **/
+ reduce<TValue, TResult>(
+ callback: MemoIterator<TValue, TResult>,
+ accumulator: TResult): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.reduce
+ **/
+ reduce<TValue, TResult>(
+ callback: MemoIterator<TValue, TResult>): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**LoDashExplicitWrapper
+ * @see _.reduce
+ */
+ reduce<TResult>(
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.reduce
+ */
+ reduce<TResult>(
+ callback: MemoIterator<T, TResult>): LoDashExplicitWrapper<TResult>;
+ }
+
+ //_.reduceRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.reduce except that it iterates over elements of a collection from
+ * right to left.
+ * @param collection The collection to iterate over.
+ * @param callback The function called per iteration.
+ * @param accumulator Initial value of the accumulator.
+ * @param thisArg The this binding of callback.
+ * @return The accumulated value.
+ **/
+ reduceRight<T, TResult>(
+ collection: Array<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduceRight
+ **/
+ reduceRight<T, TResult>(
+ collection: List<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduceRight
+ **/
+ reduceRight<T, TResult>(
+ collection: Dictionary<T>,
+ callback: MemoIterator<T, TResult>,
+ accumulator: TResult): TResult;
+
+ /**
+ * @see _.reduceRight
+ **/
+ reduceRight<T, TResult>(
+ collection: Array<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+
+ /**
+ * @see _.reduceRight
+ **/
+ reduceRight<T, TResult>(
+ collection: List<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+
+ /**
+ * @see _.reduceRight
+ **/
+ reduceRight<T, TResult>(
+ collection: Dictionary<T>,
+ callback: MemoIterator<T, TResult>): TResult;
+ }
+
+ //_.reject
+ interface LoDashStatic {
+ /**
+ * The opposite of _.filter; this method returns the elements of collection that predicate does not return
+ * truthy for.
+ *
+ * @param collection The collection to iterate over.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the new filtered array.
+ */
+ reject<T>(
+ collection: List<T>,
+ predicate?: ListIterator<T, boolean>
+ ): T[];
+
+ /**
+ * @see _.reject
+ */
+ reject<T>(
+ collection: Dictionary<T>,
+ predicate?: DictionaryIterator<T, boolean>
+ ): T[];
+
+ /**
+ * @see _.reject
+ */
+ reject(
+ collection: string,
+ predicate?: StringIterator<boolean>
+ ): string[];
+
+ /**
+ * @see _.reject
+ */
+ reject<T>(
+ collection: List<T>|Dictionary<T>,
+ predicate: string
+ ): T[];
+
+ /**
+ * @see _.reject
+ */
+ reject<W extends {}, T>(
+ collection: List<T>|Dictionary<T>,
+ predicate: W
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.reject
+ */
+ reject(
+ predicate?: StringIterator<boolean>
+ ): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.reject
+ */
+ reject(
+ predicate: ListIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject(
+ predicate: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject<W>(predicate: W): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.reject
+ */
+ reject<T>(
+ predicate: ListIterator<T, boolean>|DictionaryIterator<T, boolean>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject<T>(
+ predicate: string
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject<W, T>(predicate: W): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.reject
+ */
+ reject(
+ predicate?: StringIterator<boolean>
+ ): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.reject
+ */
+ reject(
+ predicate: ListIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject(
+ predicate: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject<W>(predicate: W): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.reject
+ */
+ reject<T>(
+ predicate: ListIterator<T, boolean>|DictionaryIterator<T, boolean>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject<T>(
+ predicate: string
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.reject
+ */
+ reject<W, T>(predicate: W): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.sample
+ interface LoDashStatic {
+ /**
+ * Gets a random element from collection.
+ *
+ * @param collection The collection to sample.
+ * @return Returns the random element.
+ */
+ sample<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>
+ ): T;
+
+ /**
+ * @see _.sample
+ */
+ sample<O extends Object, T>(
+ collection: O
+ ): T;
+
+ /**
+ * @see _.sample
+ */
+ sample<T>(
+ collection: Object
+ ): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sample
+ */
+ sample(): string;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sample
+ */
+ sample(): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sample
+ */
+ sample<T>(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sample
+ */
+ sample(): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sample
+ */
+ sample<TWrapper>(): TWrapper;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sample
+ */
+ sample<TWrapper>(): TWrapper;
+ }
+
+ //_.sampleSize
+ interface LoDashStatic {
+ /**
+ * Gets n random elements at unique keys from collection up to the size of collection.
+ *
+ * @param collection The collection to sample.
+ * @param n The number of elements to sample.
+ * @return Returns the random elements.
+ */
+ sampleSize<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ n?: number
+ ): T[];
+
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize<O extends Object, T>(
+ collection: O,
+ n?: number
+ ): T[];
+
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize<T>(
+ collection: Object,
+ n?: number
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize(
+ n?: number
+ ): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize(
+ n?: number
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize<T>(
+ n?: number
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize(
+ n?: number
+ ): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize(
+ n?: number
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sampleSize
+ */
+ sampleSize<T>(
+ n?: number
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.shuffle
+ interface LoDashStatic {
+ /**
+ * Creates an array of shuffled values, using a version of the Fisher-Yates shuffle.
+ *
+ * @param collection The collection to shuffle.
+ * @return Returns the new shuffled array.
+ */
+ shuffle<T>(collection: List<T>|Dictionary<T>): T[];
+
+ /**
+ * @see _.shuffle
+ */
+ shuffle(collection: string): string[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.shuffle
+ */
+ shuffle(): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.shuffle
+ */
+ shuffle(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.shuffle
+ */
+ shuffle<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.shuffle
+ */
+ shuffle(): LoDashExplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.shuffle
+ */
+ shuffle(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.shuffle
+ */
+ shuffle<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.size
+ interface LoDashStatic {
+ /**
+ * Gets the size of collection by returning its length for array-like values or the number of own enumerable
+ * properties for objects.
+ *
+ * @param collection The collection to inspect.
+ * @return Returns the size of collection.
+ */
+ size<T>(collection: List<T>|Dictionary<T>): number;
+
+ /**
+ * @see _.size
+ */
+ size(collection: string): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.size
+ */
+ size(): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.size
+ */
+ size(): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.size
+ */
+ size(): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.size
+ */
+ size(): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.size
+ */
+ size(): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.size
+ */
+ size(): LoDashExplicitWrapper<number>;
+ }
+
+ //_.some
+ interface LoDashStatic {
+ /**
+ * Checks if predicate returns truthy for any element of collection. Iteration is stopped once predicate
+ * returns truthy. The predicate is invoked with three arguments: (value, index|key, collection).
+ *
+ * @param collection The collection to iterate over.
+ * @param predicate The function invoked per iteration.
+ * @return Returns true if any element passes the predicate check, else false.
+ */
+ some<T>(
+ collection: List<T>,
+ predicate?: ListIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<T>(
+ collection: Dictionary<T>,
+ predicate?: DictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<T>(
+ collection: NumericDictionary<T>,
+ predicate?: NumericDictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some(
+ collection: Object,
+ predicate?: ObjectIterator<any, boolean>
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ predicate?: string|[string, any]
+ ): boolean;
+
+
+ /**
+ * @see _.some
+ */
+ some(
+ collection: Object,
+ predicate?: string|[string, any]
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<TObject extends {}, T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ predicate?: TObject
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<T>(
+ collection: List<T>|Dictionary<T>|NumericDictionary<T>,
+ predicate?: Object
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<TObject extends {}>(
+ collection: Object,
+ predicate?: TObject
+ ): boolean;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.some
+ */
+ some(
+ predicate?: ListIterator<T, boolean>|NumericDictionaryIterator<T, boolean>
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some(
+ predicate?: string|[string, any]
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<TObject extends {}>(
+ predicate?: TObject
+ ): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.some
+ */
+ some<TResult>(
+ predicate?: ListIterator<TResult, boolean>|DictionaryIterator<TResult, boolean>|NumericDictionaryIterator<T, boolean>|ObjectIterator<any, boolean>
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some(
+ predicate?: string|[string, any]
+ ): boolean;
+
+ /**
+ * @see _.some
+ */
+ some<TObject extends {}>(
+ predicate?: TObject
+ ): boolean;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.some
+ */
+ some(
+ predicate?: ListIterator<T, boolean>|NumericDictionaryIterator<T, boolean>
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.some
+ */
+ some(
+ predicate?: string|[string, any]
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.some
+ */
+ some<TObject extends {}>(
+ predicate?: TObject
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.some
+ */
+ some<TResult>(
+ predicate?: ListIterator<TResult, boolean>|DictionaryIterator<TResult, boolean>|NumericDictionaryIterator<T, boolean>|ObjectIterator<any, boolean>
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.some
+ */
+ some(
+ predicate?: string|[string, any]
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.some
+ */
+ some<TObject extends {}>(
+ predicate?: TObject
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.sortBy
+ interface LoDashStatic {
+ /**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection through each iteratee. This method
+ * performs a stable sort, that is, it preserves the original sort order of
+ * equal elements. The iteratees are invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {...(Function|Function[]|Object|Object[]|string|string[])} [iteratees=[_.identity]]
+ * The iteratees to sort by, specified individually or in arrays.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 42 },
+ * { 'user': 'barney', 'age': 34 }
+ * ];
+ *
+ * _.sortBy(users, function(o) { return o.user; });
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
+ *
+ * _.sortBy(users, ['user', 'age']);
+ * // => objects for [['barney', 34], ['barney', 36], ['fred', 42], ['fred', 48]]
+ *
+ * _.sortBy(users, 'user', function(o) {
+ * return Math.floor(o.age / 10);
+ * });
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
+ */
+ sortBy<T, TSort>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, TSort>
+ ): T[];
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T, TSort>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, TSort>
+ ): T[];
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(
+ collection: List<T>|Dictionary<T>,
+ iteratee: string
+ ): T[];
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<W extends {}, T>(
+ collection: List<T>|Dictionary<T>,
+ whereValue: W
+ ): T[];
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(
+ collection: List<T>|Dictionary<T>
+ ): T[];
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(
+ collection: (Array<T>|List<T>),
+ iteratees: (ListIterator<T, any>|string|Object)[]): T[];
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(
+ collection: (Array<T>|List<T>),
+ ...iteratees: (ListIterator<T, boolean>|Object|string)[]): T[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sortBy
+ */
+ sortBy<TSort>(
+ iteratee?: ListIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy(iteratee: string): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<W extends {}>(whereValue: W): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy(): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy(...iteratees: (ListIterator<T, boolean>|Object|string)[]): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ **/
+ sortBy(iteratees: (ListIterator<T, any>|string|Object)[]): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T, TSort>(
+ iteratee?: ListIterator<T, TSort>|DictionaryIterator<T, TSort>
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(iteratee: string): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<W extends {}, T>(whereValue: W): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sortBy
+ */
+ sortBy<TSort>(
+ iteratee?: ListIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy(iteratee: string): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<W extends {}>(whereValue: W): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T, TSort>(
+ iteratee?: ListIterator<T, TSort>|DictionaryIterator<T, TSort>
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(iteratee: string): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<W extends {}, T>(whereValue: W): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.sortBy
+ */
+ sortBy<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.orderBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.sortBy` except that it allows specifying the sort
+ * orders of the iteratees to sort by. If `orders` is unspecified, all values
+ * are sorted in ascending order. Otherwise, specify an order of "desc" for
+ * descending or "asc" for ascending sort order of corresponding values.
+ *
+ * @static
+ * @memberOf _
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function[]|Object[]|string[]} [iteratees=[_.identity]] The iteratees to sort by.
+ * @param {string[]} [orders] The sort orders of `iteratees`.
+ * @param- {Object} [guard] Enables use as an iteratee for functions like `_.reduce`.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 34 },
+ * { 'user': 'fred', 'age': 42 },
+ * { 'user': 'barney', 'age': 36 }
+ * ];
+ *
+ * // sort by `user` in ascending order and by `age` in descending order
+ * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 42]]
+ */
+ orderBy<W extends Object, T>(
+ collection: List<T>,
+ iteratees: ListIterator<T, any>|string|W|(ListIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): T[];
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ collection: List<T>,
+ iteratees: ListIterator<T, any>|string|Object|(ListIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): T[];
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ collection: NumericDictionary<T>,
+ iteratees: NumericDictionaryIterator<T, any>|string|W|(NumericDictionaryIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): T[];
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ collection: NumericDictionary<T>,
+ iteratees: NumericDictionaryIterator<T, any>|string|Object|(NumericDictionaryIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): T[];
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ collection: Dictionary<T>,
+ iteratees: DictionaryIterator<T, any>|string|W|(DictionaryIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): T[];
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ collection: Dictionary<T>,
+ iteratees: DictionaryIterator<T, any>|string|Object|(DictionaryIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.orderBy
+ */
+ orderBy(
+ iteratees: ListIterator<T, any>|string|(ListIterator<T, any>|string)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object>(
+ iteratees: ListIterator<T, any>|string|W|(ListIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ iteratees: ListIterator<T, any>|string|W|(ListIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ iteratees: ListIterator<T, any>|string|Object|(ListIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ iteratees: NumericDictionaryIterator<T, any>|string|W|(NumericDictionaryIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ iteratees: NumericDictionaryIterator<T, any>|string|Object|(NumericDictionaryIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ iteratees: DictionaryIterator<T, any>|string|W|(DictionaryIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ iteratees: DictionaryIterator<T, any>|string|Object|(DictionaryIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.orderBy
+ */
+ orderBy(
+ iteratees: ListIterator<T, any>|string|(ListIterator<T, any>|string)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object>(
+ iteratees: ListIterator<T, any>|string|W|(ListIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ iteratees: ListIterator<T, any>|string|W|(ListIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ iteratees: ListIterator<T, any>|string|Object|(ListIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ iteratees: NumericDictionaryIterator<T, any>|string|W|(NumericDictionaryIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ iteratees: NumericDictionaryIterator<T, any>|string|Object|(NumericDictionaryIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<W extends Object, T>(
+ iteratees: DictionaryIterator<T, any>|string|W|(DictionaryIterator<T, any>|string|W)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+
+ /**
+ * @see _.orderBy
+ */
+ orderBy<T>(
+ iteratees: DictionaryIterator<T, any>|string|Object|(DictionaryIterator<T, any>|string|Object)[],
+ orders?: boolean|string|(boolean|string)[]
+ ): LoDashExplicitArrayWrapper<T>;
+ }
+
+ /********
+ * Date *
+ ********/
+
+ //_.now
+ interface LoDashStatic {
+ /**
+ * Gets the number of milliseconds that have elapsed since the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @return The number of milliseconds.
+ */
+ now(): number;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.now
+ */
+ now(): number;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.now
+ */
+ now(): LoDashExplicitWrapper<number>;
+ }
+
+ /*************
+ * Functions *
+ *************/
+
+ //_.after
+ interface LoDashStatic {
+ /**
+ * The opposite of _.before; this method creates a function that invokes func once it’s called n or more times.
+ *
+ * @param n The number of calls before func is invoked.
+ * @param func The function to restrict.
+ * @return Returns the new restricted function.
+ */
+ after<TFunc extends Function>(
+ n: number,
+ func: TFunc
+ ): TFunc;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.after
+ **/
+ after<TFunc extends Function>(func: TFunc): LoDashImplicitObjectWrapper<TFunc>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.after
+ **/
+ after<TFunc extends Function>(func: TFunc): LoDashExplicitObjectWrapper<TFunc>;
+ }
+
+ //_.ary
+ interface LoDashStatic {
+ /**
+ * Creates a function that accepts up to n arguments ignoring any additional arguments.
+ *
+ * @param func The function to cap arguments for.
+ * @param n The arity cap.
+ * @returns Returns the new function.
+ */
+ ary<TResult extends Function>(
+ func: Function,
+ n?: number
+ ): TResult;
+
+ ary<T extends Function, TResult extends Function>(
+ func: T,
+ n?: number
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.ary
+ */
+ ary<TResult extends Function>(n?: number): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.ary
+ */
+ ary<TResult extends Function>(n?: number): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.before
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes func, with the this binding and arguments of the created function, while
+ * it’s called less than n times. Subsequent calls to the created function return the result of the last func
+ * invocation.
+ *
+ * @param n The number of calls at which func is no longer invoked.
+ * @param func The function to restrict.
+ * @return Returns the new restricted function.
+ */
+ before<TFunc extends Function>(
+ n: number,
+ func: TFunc
+ ): TFunc;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.before
+ **/
+ before<TFunc extends Function>(func: TFunc): LoDashImplicitObjectWrapper<TFunc>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.before
+ **/
+ before<TFunc extends Function>(func: TFunc): LoDashExplicitObjectWrapper<TFunc>;
+ }
+
+ //_.bind
+ interface FunctionBind {
+ placeholder: any;
+
+ <T extends Function, TResult extends Function>(
+ func: T,
+ thisArg: any,
+ ...partials: any[]
+ ): TResult;
+
+ <TResult extends Function>(
+ func: Function,
+ thisArg: any,
+ ...partials: any[]
+ ): TResult;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes func with the this binding of thisArg and prepends any additional _.bind
+ * arguments to those provided to the bound function.
+ *
+ * The _.bind.placeholder value, which defaults to _ in monolithic builds, may be used as a placeholder for
+ * partially applied arguments.
+ *
+ * Note: Unlike native Function#bind this method does not set the "length" property of bound functions.
+ *
+ * @param func The function to bind.
+ * @param thisArg The this binding of func.
+ * @param partials The arguments to be partially applied.
+ * @return Returns the new bound function.
+ */
+ bind: FunctionBind;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.bind
+ */
+ bind<TResult extends Function>(
+ thisArg: any,
+ ...partials: any[]
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.bind
+ */
+ bind<TResult extends Function>(
+ thisArg: any,
+ ...partials: any[]
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.bindAll
+ interface LoDashStatic {
+ /**
+ * Binds methods of an object to the object itself, overwriting the existing method. Method names may be
+ * specified as individual arguments or as arrays of method names. If no method names are provided all
+ * enumerable function properties, own and inherited, of object are bound.
+ *
+ * Note: This method does not set the "length" property of bound functions.
+ *
+ * @param object The object to bind and assign the bound methods to.
+ * @param methodNames The object method names to bind, specified as individual method names or arrays of
+ * method names.
+ * @return Returns object.
+ */
+ bindAll<T>(
+ object: T,
+ ...methodNames: (string|string[])[]
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.bindAll
+ */
+ bindAll(...methodNames: (string|string[])[]): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.bindAll
+ */
+ bindAll(...methodNames: (string|string[])[]): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.bindKey
+ interface FunctionBindKey {
+ placeholder: any;
+
+ <T extends Object, TResult extends Function>(
+ object: T,
+ key: any,
+ ...partials: any[]
+ ): TResult;
+
+ <TResult extends Function>(
+ object: Object,
+ key: any,
+ ...partials: any[]
+ ): TResult;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes the method at object[key] and prepends any additional _.bindKey arguments
+ * to those provided to the bound function.
+ *
+ * This method differs from _.bind by allowing bound functions to reference methods that may be redefined
+ * or don’t yet exist. See Peter Michaux’s article for more details.
+ *
+ * The _.bindKey.placeholder value, which defaults to _ in monolithic builds, may be used as a placeholder
+ * for partially applied arguments.
+ *
+ * @param object The object the method belongs to.
+ * @param key The key of the method.
+ * @param partials The arguments to be partially applied.
+ * @return Returns the new bound function.
+ */
+ bindKey: FunctionBindKey;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.bindKey
+ */
+ bindKey<TResult extends Function>(
+ key: any,
+ ...partials: any[]
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.bindKey
+ */
+ bindKey<TResult extends Function>(
+ key: any,
+ ...partials: any[]
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.createCallback
+ interface LoDashStatic {
+ /**
+ * Produces a callback bound to an optional thisArg. If func is a property name the created
+ * callback will return the property value for a given element. If func is an object the created
+ * callback will return true for elements that contain the equivalent object properties,
+ * otherwise it will return false.
+ * @param func The value to convert to a callback.
+ * @param thisArg The this binding of the created callback.
+ * @param argCount The number of arguments the callback accepts.
+ * @return A callback function.
+ **/
+ createCallback(
+ func: string,
+ argCount?: number): () => any;
+
+ /**
+ * @see _.createCallback
+ **/
+ createCallback(
+ func: Dictionary<any>,
+ argCount?: number): () => boolean;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.createCallback
+ **/
+ createCallback(
+ argCount?: number): LoDashImplicitObjectWrapper<() => any>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.createCallback
+ **/
+ createCallback(
+ argCount?: number): LoDashImplicitObjectWrapper<() => any>;
+ }
+
+ //_.curry
+ interface LoDashStatic {
+ /**
+ * Creates a function that accepts one or more arguments of func that when called either invokes func returning
+ * its result, if all func arguments have been provided, or returns a function that accepts one or more of the
+ * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curry<T1, R>(func: (t1: T1) => R):
+ CurriedFunction1<T1, R>;
+ /**
+ * Creates a function that accepts one or more arguments of func that when called either invokes func returning
+ * its result, if all func arguments have been provided, or returns a function that accepts one or more of the
+ * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curry<T1, T2, R>(func: (t1: T1, t2: T2) => R):
+ CurriedFunction2<T1, T2, R>;
+ /**
+ * Creates a function that accepts one or more arguments of func that when called either invokes func returning
+ * its result, if all func arguments have been provided, or returns a function that accepts one or more of the
+ * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curry<T1, T2, T3, R>(func: (t1: T1, t2: T2, t3: T3) => R):
+ CurriedFunction3<T1, T2, T3, R>;
+ /**
+ * Creates a function that accepts one or more arguments of func that when called either invokes func returning
+ * its result, if all func arguments have been provided, or returns a function that accepts one or more of the
+ * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curry<T1, T2, T3, T4, R>(func: (t1: T1, t2: T2, t3: T3, t4: T4) => R):
+ CurriedFunction4<T1, T2, T3, T4, R>;
+ /**
+ * Creates a function that accepts one or more arguments of func that when called either invokes func returning
+ * its result, if all func arguments have been provided, or returns a function that accepts one or more of the
+ * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curry<T1, T2, T3, T4, T5, R>(func: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R):
+ CurriedFunction5<T1, T2, T3, T4, T5, R>;
+ /**
+ * Creates a function that accepts one or more arguments of func that when called either invokes func returning
+ * its result, if all func arguments have been provided, or returns a function that accepts one or more of the
+ * remaining func arguments, and so on. The arity of func may be specified if func.length is not sufficient.
+ * @param func The function to curry.
+ * @param arity The arity of func.
+ * @return Returns the new curried function.
+ */
+ curry<TResult extends Function>(
+ func: Function,
+ arity?: number): TResult;
+ }
+
+ interface CurriedFunction1<T1, R> {
+ (): CurriedFunction1<T1, R>;
+ (t1: T1): R;
+ }
+
+ interface CurriedFunction2<T1, T2, R> {
+ (): CurriedFunction2<T1, T2, R>;
+ (t1: T1): CurriedFunction1<T2, R>;
+ (t1: T1, t2: T2): R;
+ }
+
+ interface CurriedFunction3<T1, T2, T3, R> {
+ (): CurriedFunction3<T1, T2, T3, R>;
+ (t1: T1): CurriedFunction2<T2, T3, R>;
+ (t1: T1, t2: T2): CurriedFunction1<T3, R>;
+ (t1: T1, t2: T2, t3: T3): R;
+ }
+
+ interface CurriedFunction4<T1, T2, T3, T4, R> {
+ (): CurriedFunction4<T1, T2, T3, T4, R>;
+ (t1: T1): CurriedFunction3<T2, T3, T4, R>;
+ (t1: T1, t2: T2): CurriedFunction2<T3, T4, R>;
+ (t1: T1, t2: T2, t3: T3): CurriedFunction1<T4, R>;
+ (t1: T1, t2: T2, t3: T3, t4: T4): R;
+ }
+
+ interface CurriedFunction5<T1, T2, T3, T4, T5, R> {
+ (): CurriedFunction5<T1, T2, T3, T4, T5, R>;
+ (t1: T1): CurriedFunction4<T2, T3, T4, T5, R>;
+ (t1: T1, t2: T2): CurriedFunction3<T3, T4, T5, R>;
+ (t1: T1, t2: T2, t3: T3): CurriedFunction2<T4, T5, R>;
+ (t1: T1, t2: T2, t3: T3, t4: T4): CurriedFunction1<T5, R>;
+ (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5): R;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.curry
+ **/
+ curry<TResult extends Function>(arity?: number): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ //_.curryRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight
+ * instead of _.partial.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curryRight<T1, R>(func: (t1: T1) => R):
+ CurriedFunction1<T1, R>;
+ /**
+ * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight
+ * instead of _.partial.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curryRight<T1, T2, R>(func: (t1: T1, t2: T2) => R):
+ CurriedFunction2<T2, T1, R>;
+ /**
+ * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight
+ * instead of _.partial.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curryRight<T1, T2, T3, R>(func: (t1: T1, t2: T2, t3: T3) => R):
+ CurriedFunction3<T3, T2, T1, R>;
+ /**
+ * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight
+ * instead of _.partial.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curryRight<T1, T2, T3, T4, R>(func: (t1: T1, t2: T2, t3: T3, t4: T4) => R):
+ CurriedFunction4<T4, T3, T2, T1, R>;
+ /**
+ * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight
+ * instead of _.partial.
+ * @param func The function to curry.
+ * @return Returns the new curried function.
+ */
+ curryRight<T1, T2, T3, T4, T5, R>(func: (t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => R):
+ CurriedFunction5<T5, T4, T3, T2, T1, R>;
+ /**
+ * This method is like _.curry except that arguments are applied to func in the manner of _.partialRight
+ * instead of _.partial.
+ * @param func The function to curry.
+ * @param arity The arity of func.
+ * @return Returns the new curried function.
+ */
+ curryRight<TResult extends Function>(
+ func: Function,
+ arity?: number): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.curryRight
+ **/
+ curryRight<TResult extends Function>(arity?: number): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ //_.debounce
+ interface DebounceSettings {
+ /**
+ * Specify invoking on the leading edge of the timeout.
+ */
+ leading?: boolean;
+
+ /**
+ * The maximum time func is allowed to be delayed before it’s invoked.
+ */
+ maxWait?: number;
+
+ /**
+ * Specify invoking on the trailing edge of the timeout.
+ */
+ trailing?: boolean;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since
+ * the last time the debounced function was invoked. The debounced function comes with a cancel method to
+ * cancel delayed invocations and a flush method to immediately invoke them. Provide an options object to
+ * indicate that func should be invoked on the leading and/or trailing edge of the wait timeout. Subsequent
+ * calls to the debounced function return the result of the last func invocation.
+ *
+ * Note: If leading and trailing options are true, func is invoked on the trailing edge of the timeout only
+ * if the the debounced function is invoked more than once during the wait timeout.
+ *
+ * See David Corbacho’s article for details over the differences between _.debounce and _.throttle.
+ *
+ * @param func The function to debounce.
+ * @param wait The number of milliseconds to delay.
+ * @param options The options object.
+ * @param options.leading Specify invoking on the leading edge of the timeout.
+ * @param options.maxWait The maximum time func is allowed to be delayed before it’s invoked.
+ * @param options.trailing Specify invoking on the trailing edge of the timeout.
+ * @return Returns the new debounced function.
+ */
+ debounce<T extends Function>(
+ func: T,
+ wait?: number,
+ options?: DebounceSettings
+ ): T & Cancelable;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.debounce
+ */
+ debounce(
+ wait?: number,
+ options?: DebounceSettings
+ ): LoDashImplicitObjectWrapper<T & Cancelable>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.debounce
+ */
+ debounce(
+ wait?: number,
+ options?: DebounceSettings
+ ): LoDashExplicitObjectWrapper<T & Cancelable>;
+ }
+
+ //_.defer
+ interface LoDashStatic {
+ /**
+ * Defers invoking the func until the current call stack has cleared. Any additional arguments are provided to
+ * func when it’s invoked.
+ *
+ * @param func The function to defer.
+ * @param args The arguments to invoke the function with.
+ * @return Returns the timer id.
+ */
+ defer<T extends Function>(
+ func: T,
+ ...args: any[]
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.defer
+ */
+ defer(...args: any[]): LoDashImplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.defer
+ */
+ defer(...args: any[]): LoDashExplicitWrapper<number>;
+ }
+
+ //_.delay
+ interface LoDashStatic {
+ /**
+ * Invokes func after wait milliseconds. Any additional arguments are provided to func when it’s invoked.
+ *
+ * @param func The function to delay.
+ * @param wait The number of milliseconds to delay invocation.
+ * @param args The arguments to invoke the function with.
+ * @return Returns the timer id.
+ */
+ delay<T extends Function>(
+ func: T,
+ wait: number,
+ ...args: any[]
+ ): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.delay
+ */
+ delay(
+ wait: number,
+ ...args: any[]
+ ): LoDashImplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.delay
+ */
+ delay(
+ wait: number,
+ ...args: any[]
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes `func` with arguments reversed.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to flip arguments for.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * var flipped = _.flip(function() {
+ * return _.toArray(arguments);
+ * });
+ *
+ * flipped('a', 'b', 'c', 'd');
+ * // => ['d', 'c', 'b', 'a']
+ */
+ flip<T extends Function>(func: T): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.flip
+ */
+ flip(): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.flip
+ */
+ flip(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.flow
+ interface LoDashStatic {
+ /**
+ * Creates a function that returns the result of invoking the provided functions with the this binding of the
+ * created function, where each successive invocation is supplied the return value of the previous.
+ *
+ * @param funcs Functions to invoke.
+ * @return Returns the new function.
+ */
+ // 1-argument first function
+ flow<A1, R1, R2>(f1: (a1: A1) => R1, f2: (a: R1) => R2): (a1: A1) => R2;
+ flow<A1, R1, R2, R3>(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1) => R3;
+ flow<A1, R1, R2, R3, R4>(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1) => R4;
+ flow<A1, R1, R2, R3, R4, R5>(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1) => R5;
+ flow<A1, R1, R2, R3, R4, R5, R6>(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1) => R6;
+ flow<A1, R1, R2, R3, R4, R5, R6, R7>(f1: (a1: A1) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1) => R7;
+ // 2-argument first function
+ flow<A1, A2, R1, R2>(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2): (a1: A1, a2: A2) => R2;
+ flow<A1, A2, R1, R2, R3>(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1, a2: A2) => R3;
+ flow<A1, A2, R1, R2, R3, R4>(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1, a2: A2) => R4;
+ flow<A1, A2, R1, R2, R3, R4, R5>(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1, a2: A2) => R5;
+ flow<A1, A2, R1, R2, R3, R4, R5, R6>(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1, a2: A2) => R6;
+ flow<A1, A2, R1, R2, R3, R4, R5, R6, R7>(f1: (a1: A1, a2: A2) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1, a2: A2) => R7;
+ // 3-argument first function
+ flow<A1, A2, A3, R1, R2>(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2): (a1: A1, a2: A2, a3: A3) => R2;
+ flow<A1, A2, A3, R1, R2, R3>(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1, a2: A2, a3: A3) => R3;
+ flow<A1, A2, A3, R1, R2, R3, R4>(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1, a2: A2, a3: A3) => R4;
+ flow<A1, A2, A3, R1, R2, R3, R4, R5>(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1, a2: A2, a3: A3) => R5;
+ flow<A1, A2, A3, R1, R2, R3, R4, R5, R6>(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1, a2: A2, a3: A3) => R6;
+ flow<A1, A2, A3, R1, R2, R3, R4, R5, R6, R7>(f1: (a1: A1, a2: A2, a3: A3) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1, a2: A2, a3: A3) => R7;
+ // 4-argument first function
+ flow<A1, A2, A3, A4, R1, R2>(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2): (a1: A1, a2: A2, a3: A3, a4: A4) => R2;
+ flow<A1, A2, A3, A4, R1, R2, R3>(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3): (a1: A1, a2: A2, a3: A3, a4: A4) => R3;
+ flow<A1, A2, A3, A4, R1, R2, R3, R4>(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4): (a1: A1, a2: A2, a3: A3, a4: A4) => R4;
+ flow<A1, A2, A3, A4, R1, R2, R3, R4, R5>(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5): (a1: A1, a2: A2, a3: A3, a4: A4) => R5;
+ flow<A1, A2, A3, A4, R1, R2, R3, R4, R5, R6>(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6): (a1: A1, a2: A2, a3: A3, a4: A4) => R6;
+ flow<A1, A2, A3, A4, R1, R2, R3, R4, R5, R6, R7>(f1: (a1: A1, a2: A2, a3: A3, a4: A4) => R1, f2: (a: R1) => R2, f3: (a: R2) => R3, f4: (a: R3) => R4, f5: (a: R4) => R5, f6: (a: R5) => R6, f7: (a: R6) => R7): (a1: A1, a2: A2, a3: A3, a4: A4) => R7;
+ // generic function
+ flow<TResult extends Function>(...funcs: Function[]): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.flow
+ */
+ flow<TResult extends Function>(...funcs: Function[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.flow
+ */
+ flow<TResult extends Function>(...funcs: Function[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.flowRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.flow except that it creates a function that invokes the provided functions from right
+ * to left.
+ *
+ * @param funcs Functions to invoke.
+ * @return Returns the new function.
+ */
+ flowRight<TResult extends Function>(...funcs: Function[]): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.flowRight
+ */
+ flowRight<TResult extends Function>(...funcs: Function[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.flowRight
+ */
+ flowRight<TResult extends Function>(...funcs: Function[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+
+ //_.memoize
+ interface MemoizedFunction extends Function {
+ cache: MapCache;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a function that memoizes the result of func. If resolver is provided it determines the cache key for
+ * storing the result based on the arguments provided to the memoized function. By default, the first argument
+ * provided to the memoized function is coerced to a string and used as the cache key. The func is invoked with
+ * the this binding of the memoized function.
+ *
+ * @param func The function to have its output memoized.
+ * @param resolver The function to resolve the cache key.
+ * @return Returns the new memoizing function.
+ */
+ memoize: {
+ <T extends Function>(func: T, resolver?: Function): T & MemoizedFunction;
+ Cache: MapCacheConstructor;
+ }
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.memoize
+ */
+ memoize(resolver?: Function): LoDashImplicitObjectWrapper<T & MemoizedFunction>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.memoize
+ */
+ memoize(resolver?: Function): LoDashExplicitObjectWrapper<T & MemoizedFunction>;
+ }
+
+ //_.overArgs (was _.modArgs)
+ interface LoDashStatic {
+ /**
+ * Creates a function that runs each argument through a corresponding transform function.
+ *
+ * @param func The function to wrap.
+ * @param transforms The functions to transform arguments, specified as individual functions or arrays
+ * of functions.
+ * @return Returns the new function.
+ */
+ overArgs<T extends Function, TResult extends Function>(
+ func: T,
+ ...transforms: Function[]
+ ): TResult;
+
+ /**
+ * @see _.overArgs
+ */
+ overArgs<T extends Function, TResult extends Function>(
+ func: T,
+ transforms: Function[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.overArgs
+ */
+ overArgs<TResult extends Function>(...transforms: Function[]): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.overArgs
+ */
+ overArgs<TResult extends Function>(transforms: Function[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.overArgs
+ */
+ overArgs<TResult extends Function>(...transforms: Function[]): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.overArgs
+ */
+ overArgs<TResult extends Function>(transforms: Function[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.negate
+ interface LoDashStatic {
+ /**
+ * Creates a function that negates the result of the predicate func. The func predicate is invoked with
+ * the this binding and arguments of the created function.
+ *
+ * @param predicate The predicate to negate.
+ * @return Returns the new function.
+ */
+ negate<T extends Function>(predicate: T): (...args: any[]) => boolean;
+
+ /**
+ * @see _.negate
+ */
+ negate<T extends Function, TResult extends Function>(predicate: T): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.negate
+ */
+ negate(): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;
+
+ /**
+ * @see _.negate
+ */
+ negate<TResult extends Function>(): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.negate
+ */
+ negate(): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;
+
+ /**
+ * @see _.negate
+ */
+ negate<TResult extends Function>(): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.once
+ interface LoDashStatic {
+ /**
+ * Creates a function that is restricted to invoking func once. Repeat calls to the function return the value
+ * of the first call. The func is invoked with the this binding and arguments of the created function.
+ *
+ * @param func The function to restrict.
+ * @return Returns the new restricted function.
+ */
+ once<T extends Function>(func: T): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.once
+ */
+ once(): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.once
+ */
+ once(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.partial
+ interface LoDashStatic {
+ /**
+ * Creates a function that, when called, invokes func with any additional partial arguments
+ * prepended to those provided to the new function. This method is similar to _.bind except
+ * it does not alter the this binding.
+ * @param func The function to partially apply arguments to.
+ * @param args Arguments to be partially applied.
+ * @return The new partially applied function.
+ **/
+ partial: Partial;
+ }
+
+ type PH = LoDashStatic;
+
+ interface Function0<R> {
+ (): R;
+ }
+ interface Function1<T1, R> {
+ (t1: T1): R;
+ }
+ interface Function2<T1, T2, R> {
+ (t1: T1, t2: T2): R;
+ }
+ interface Function3<T1, T2, T3, R> {
+ (t1: T1, t2: T2, t3: T3): R;
+ }
+ interface Function4<T1, T2, T3, T4, R> {
+ (t1: T1, t2: T2, t3: T3, t4: T4): R;
+ }
+
+ interface Partial {
+ // arity 0
+ <R>(func: Function0<R>): Function0<R>;
+ // arity 1
+ <T1, R>(func: Function1<T1, R>): Function1<T1, R>;
+ <T1, R>(func: Function1<T1, R>, arg1: T1): Function0<R>;
+ // arity 2
+ <T1, T2, R>(func: Function2<T1, T2, R>): Function2<T1, T2, R>;
+ <T1, T2, R>(func: Function2<T1, T2, R>, arg1: T1): Function1< T2, R>;
+ <T1, T2, R>(func: Function2<T1, T2, R>, plc1: PH, arg2: T2): Function1<T1, R>;
+ <T1, T2, R>(func: Function2<T1, T2, R>, arg1: T1, arg2: T2): Function0< R>;
+ // arity 3
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>): Function3<T1, T2, T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1): Function2< T2, T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, plc1: PH, arg2: T2): Function2<T1, T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, arg2: T2): Function1< T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, plc1: PH, plc2: PH, arg3: T3): Function2<T1, T2, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, plc2: PH, arg3: T3): Function1< T2, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, plc1: PH, arg2: T2, arg3: T3): Function1<T1, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, arg2: T2, arg3: T3): Function0< R>;
+ // arity 4
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>): Function4<T1, T2, T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1): Function3< T2, T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, arg2: T2): Function3<T1, T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2): Function2< T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, plc2: PH, arg3: T3): Function3<T1, T2, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, arg3: T3): Function2< T2, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, arg2: T2, arg3: T3): Function2<T1, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, arg3: T3): Function1< T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, plc2: PH, plc3: PH, arg4: T4): Function3<T1, T2, T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, plc3: PH, arg4: T4): Function2< T2, T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, arg2: T2, plc3: PH, arg4: T4): Function2<T1, T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, plc3: PH, arg4: T4): Function1< T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, plc2: PH, arg3: T3, arg4: T4): Function2<T1, T2, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, arg3: T3, arg4: T4): Function1< T2, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, plc1: PH, arg2: T2, arg3: T3, arg4: T4): Function1<T1, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, arg3: T3, arg4: T4): Function0< R>;
+ // catch-all
+ (func: Function, ...args: any[]): Function;
+ }
+
+ //_.partialRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.partial except that partial arguments are appended to those provided
+ * to the new function.
+ * @param func The function to partially apply arguments to.
+ * @param args Arguments to be partially applied.
+ * @return The new partially applied function.
+ **/
+ partialRight: PartialRight
+ }
+
+ interface PartialRight {
+ // arity 0
+ <R>(func: Function0<R>): Function0<R>;
+ // arity 1
+ <T1, R>(func: Function1<T1, R>): Function1<T1, R>;
+ <T1, R>(func: Function1<T1, R>, arg1: T1): Function0<R>;
+ // arity 2
+ <T1, T2, R>(func: Function2<T1, T2, R>): Function2<T1, T2, R>;
+ <T1, T2, R>(func: Function2<T1, T2, R>, arg1: T1, plc2: PH): Function1< T2, R>;
+ <T1, T2, R>(func: Function2<T1, T2, R>, arg2: T2): Function1<T1, R>;
+ <T1, T2, R>(func: Function2<T1, T2, R>, arg1: T1, arg2: T2): Function0< R>;
+ // arity 3
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>): Function3<T1, T2, T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, plc2: PH, plc3: PH): Function2< T2, T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg2: T2, plc3: PH): Function2<T1, T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, arg2: T2, plc3: PH): Function1< T3, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg3: T3): Function2<T1, T2, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, plc2: PH, arg3: T3): Function1< T2, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg2: T2, arg3: T3): Function1<T1, R>;
+ <T1, T2, T3, R>(func: Function3<T1, T2, T3, R>, arg1: T1, arg2: T2, arg3: T3): Function0< R>;
+ // arity 4
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>): Function4<T1, T2, T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, plc3: PH, plc4: PH): Function3< T2, T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg2: T2, plc3: PH, plc4: PH): Function3<T1, T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, plc3: PH, plc4: PH): Function2< T3, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg3: T3, plc4: PH): Function3<T1, T2, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, arg3: T3, plc4: PH): Function2< T2, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg2: T2, arg3: T3, plc4: PH): Function2<T1, T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, arg3: T3, plc4: PH): Function1< T4, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg4: T4): Function3<T1, T2, T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, plc3: PH, arg4: T4): Function2< T2, T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg2: T2, plc3: PH, arg4: T4): Function2<T1, T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, plc3: PH, arg4: T4): Function1< T3, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg3: T3, arg4: T4): Function2<T1, T2, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, plc2: PH, arg3: T3, arg4: T4): Function1< T2, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg2: T2, arg3: T3, arg4: T4): Function1<T1, R>;
+ <T1, T2, T3, T4, R>(func: Function4<T1, T2, T3, T4, R>, arg1: T1, arg2: T2, arg3: T3, arg4: T4): Function0< R>;
+ // catch-all
+ (func: Function, ...args: any[]): Function;
+ }
+
+ //_.rearg
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes func with arguments arranged according to the specified indexes where the
+ * argument value at the first index is provided as the first argument, the argument value at the second index
+ * is provided as the second argument, and so on.
+ * @param func The function to rearrange arguments for.
+ * @param indexes The arranged argument indexes, specified as individual indexes or arrays of indexes.
+ * @return Returns the new function.
+ */
+ rearg<TResult extends Function>(func: Function, indexes: number[]): TResult;
+
+ /**
+ * @see _.rearg
+ */
+ rearg<TResult extends Function>(func: Function, ...indexes: number[]): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.rearg
+ */
+ rearg<TResult extends Function>(indexes: number[]): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.rearg
+ */
+ rearg<TResult extends Function>(...indexes: number[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ //_.rest
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes func with the this binding of the created function and arguments from start
+ * and beyond provided as an array.
+ *
+ * Note: This method is based on the rest parameter.
+ *
+ * @param func The function to apply a rest parameter to.
+ * @param start The start position of the rest parameter.
+ * @return Returns the new function.
+ */
+ rest<TResult extends Function>(
+ func: Function,
+ start?: number
+ ): TResult;
+
+ /**
+ * @see _.rest
+ */
+ rest<TResult extends Function, TFunc extends Function>(
+ func: TFunc,
+ start?: number
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.rest
+ */
+ rest<TResult extends Function>(start?: number): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.rest
+ */
+ rest<TResult extends Function>(start?: number): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.spread
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes func with the this binding of the created function and an array of arguments
+ * much like Function#apply.
+ *
+ * Note: This method is based on the spread operator.
+ *
+ * @param func The function to spread arguments over.
+ * @return Returns the new function.
+ */
+ spread<F extends Function, T extends Function>(func: F): T;
+
+ /**
+ * @see _.spread
+ */
+ spread<T extends Function>(func: Function): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.spread
+ */
+ spread<T extends Function>(): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.spread
+ */
+ spread<T extends Function>(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.throttle
+ interface ThrottleSettings {
+ /**
+ * If you'd like to disable the leading-edge call, pass this as false.
+ */
+ leading?: boolean;
+
+ /**
+ * If you'd like to disable the execution on the trailing-edge, pass false.
+ */
+ trailing?: boolean;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a throttled function that only invokes func at most once per every wait milliseconds. The throttled
+ * function comes with a cancel method to cancel delayed invocations and a flush method to immediately invoke
+ * them. Provide an options object to indicate that func should be invoked on the leading and/or trailing edge
+ * of the wait timeout. Subsequent calls to the throttled function return the result of the last func call.
+ *
+ * Note: If leading and trailing options are true, func is invoked on the trailing edge of the timeout only if
+ * the the throttled function is invoked more than once during the wait timeout.
+ *
+ * @param func The function to throttle.
+ * @param wait The number of milliseconds to throttle invocations to.
+ * @param options The options object.
+ * @param options.leading Specify invoking on the leading edge of the timeout.
+ * @param options.trailing Specify invoking on the trailing edge of the timeout.
+ * @return Returns the new throttled function.
+ */
+ throttle<T extends Function>(
+ func: T,
+ wait?: number,
+ options?: ThrottleSettings
+ ): T & Cancelable;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.throttle
+ */
+ throttle(
+ wait?: number,
+ options?: ThrottleSettings
+ ): LoDashImplicitObjectWrapper<T & Cancelable>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.throttle
+ */
+ throttle(
+ wait?: number,
+ options?: ThrottleSettings
+ ): LoDashExplicitObjectWrapper<T & Cancelable>;
+ }
+
+ //_.unary
+ interface LoDashStatic {
+ /**
+ * Creates a function that accepts up to one argument, ignoring any
+ * additional arguments.
+ *
+ * @static
+ * @memberOf _
+ * @category Function
+ * @param {Function} func The function to cap arguments for.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * _.map(['6', '8', '10'], _.unary(parseInt));
+ * // => [6, 8, 10]
+ */
+ unary<T extends Function>(func: T): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.unary
+ */
+ unary(): LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.unary
+ */
+ unary(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.wrap
+ interface LoDashStatic {
+ /**
+ * Creates a function that provides value to the wrapper function as its first argument. Any additional
+ * arguments provided to the function are appended to those provided to the wrapper function. The wrapper is
+ * invoked with the this binding of the created function.
+ *
+ * @param value The value to wrap.
+ * @param wrapper The wrapper function.
+ * @return Returns the new function.
+ */
+ wrap<V, W extends Function, R extends Function>(
+ value: V,
+ wrapper: W
+ ): R;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<V, R extends Function>(
+ value: V,
+ wrapper: Function
+ ): R;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(
+ value: any,
+ wrapper: Function
+ ): R;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.wrap
+ */
+ wrap<W extends Function, R extends Function>(wrapper: W): LoDashImplicitObjectWrapper<R>;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(wrapper: Function): LoDashImplicitObjectWrapper<R>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.wrap
+ */
+ wrap<W extends Function, R extends Function>(wrapper: W): LoDashImplicitObjectWrapper<R>;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(wrapper: Function): LoDashImplicitObjectWrapper<R>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.wrap
+ */
+ wrap<W extends Function, R extends Function>(wrapper: W): LoDashImplicitObjectWrapper<R>;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(wrapper: Function): LoDashImplicitObjectWrapper<R>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.wrap
+ */
+ wrap<W extends Function, R extends Function>(wrapper: W): LoDashExplicitObjectWrapper<R>;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(wrapper: Function): LoDashExplicitObjectWrapper<R>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.wrap
+ */
+ wrap<W extends Function, R extends Function>(wrapper: W): LoDashExplicitObjectWrapper<R>;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(wrapper: Function): LoDashExplicitObjectWrapper<R>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.wrap
+ */
+ wrap<W extends Function, R extends Function>(wrapper: W): LoDashExplicitObjectWrapper<R>;
+
+ /**
+ * @see _.wrap
+ */
+ wrap<R extends Function>(wrapper: Function): LoDashExplicitObjectWrapper<R>;
+ }
+
+ /********
+ * Lang *
+ ********/
+
+ //_.castArray
+ interface LoDashStatic {
+ /**
+ * Casts value as an array if it’s not one.
+ *
+ * @param value The value to inspect.
+ * @return Returns the cast array.
+ */
+ castArray<T>(value: T): T[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.castArray
+ */
+ castArray(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.castArray
+ */
+ castArray(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.castArray
+ */
+ castArray(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.castArray
+ */
+ castArray(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.castArray
+ */
+ castArray(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.castArray
+ */
+ castArray(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.clone
+ interface LoDashStatic {
+ /**
+ * Creates a shallow clone of value.
+ *
+ * Note: This method is loosely based on the structured clone algorithm and supports cloning arrays,
+ * array buffers, booleans, date objects, maps, numbers, Object objects, regexes, sets, strings, symbols,
+ * and typed arrays. The own enumerable properties of arguments objects are cloned as plain objects. An empty
+ * object is returned for uncloneable values such as error objects, functions, DOM nodes, and WeakMaps.
+ *
+ * @param value The value to clone.
+ * @return Returns the cloned value.
+ */
+ clone<T>(value: T): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.clone
+ */
+ clone(): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+
+ /**
+ * @see _.clone
+ */
+ clone(): T[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.clone
+ */
+ clone(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.clone
+ */
+ clone(): LoDashExplicitWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+
+ /**
+ * @see _.clone
+ */
+ clone(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.clone
+ */
+ clone(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.cloneDeep
+ interface LoDashStatic {
+ /**
+ * This method is like _.clone except that it recursively clones value.
+ *
+ * @param value The value to recursively clone.
+ * @return Returns the deep cloned value.
+ */
+ cloneDeep<T>(value: T): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.cloneDeep
+ */
+ cloneDeep(): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.cloneDeep
+ */
+ cloneDeep(): T[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.cloneDeep
+ */
+ cloneDeep(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.cloneDeep
+ */
+ cloneDeep(): LoDashExplicitWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.cloneDeep
+ */
+ cloneDeep(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.cloneDeep
+ */
+ cloneDeep(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.cloneDeepWith
+ interface CloneDeepWithCustomizer<TValue, TResult> {
+ (value: TValue): TResult;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like _.cloneWith except that it recursively clones value.
+ *
+ * @param value The value to recursively clone.
+ * @param customizer The function to customize cloning.
+ * @return Returns the deep cloned value.
+ */
+ cloneDeepWith<TResult>(
+ value: any,
+ customizer?: CloneDeepWithCustomizer<any, TResult>
+ ): TResult;
+
+ /**
+ * @see _.clonDeepeWith
+ */
+ cloneDeepWith<T, TResult>(
+ value: T,
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult>(
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): TResult;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult>(
+ customizer?: CloneDeepWithCustomizer<T[], TResult>
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult>(
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): TResult;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult extends (number|string|boolean)>(
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult>(
+ customizer?: CloneDeepWithCustomizer<T, TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult extends Object>(
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult extends (number|string|boolean)>(
+ customizer?: CloneDeepWithCustomizer<T[], TResult>
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult>(
+ customizer?: CloneDeepWithCustomizer<T[], TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult extends Object>(
+ customizer?: CloneDeepWithCustomizer<T[], TResult>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult extends (number|string|boolean)>(
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult>(
+ customizer?: CloneDeepWithCustomizer<T, TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.cloneDeepWith
+ */
+ cloneDeepWith<TResult extends Object>(
+ customizer?: CloneDeepWithCustomizer<T, TResult>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.cloneWith
+ interface CloneWithCustomizer<TValue, TResult> {
+ (value: TValue): TResult;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like _.clone except that it accepts customizer which is invoked to produce the cloned value.
+ * If customizer returns undefined cloning is handled by the method instead.
+ *
+ * @param value The value to clone.
+ * @param customizer The function to customize cloning.
+ * @return Returns the cloned value.
+ */
+ cloneWith<TResult>(
+ value: any,
+ customizer?: CloneWithCustomizer<any, TResult>
+ ): TResult;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<T, TResult>(
+ value: T,
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult>(
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): TResult;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult>(
+ customizer?: CloneWithCustomizer<T[], TResult>
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult>(
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): TResult;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult extends (number|string|boolean)>(
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult>(
+ customizer?: CloneWithCustomizer<T, TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult extends Object>(
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult extends (number|string|boolean)>(
+ customizer?: CloneWithCustomizer<T[], TResult>
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult>(
+ customizer?: CloneWithCustomizer<T[], TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult extends Object>(
+ customizer?: CloneWithCustomizer<T[], TResult>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult extends (number|string|boolean)>(
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): LoDashExplicitWrapper<TResult>;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult>(
+ customizer?: CloneWithCustomizer<T, TResult[]>
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.cloneWith
+ */
+ cloneWith<TResult extends Object>(
+ customizer?: CloneWithCustomizer<T, TResult>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.eq
+ interface LoDashStatic {
+ /**
+ * Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
+ * comparison between two values to determine if they are equivalent.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'user': 'fred' };
+ * var other = { 'user': 'fred' };
+ *
+ * _.eq(object, object);
+ * // => true
+ *
+ * _.eq(object, other);
+ * // => false
+ *
+ * _.eq('a', 'a');
+ * // => true
+ *
+ * _.eq('a', Object('a'));
+ * // => false
+ *
+ * _.eq(NaN, NaN);
+ * // => true
+ */
+ eq(
+ value: any,
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEqual
+ */
+ eq(
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEqual
+ */
+ eq(
+ other: any
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.gt
+ interface LoDashStatic {
+ /**
+ * Checks if value is greater than other.
+ *
+ * @param value The value to compare.
+ * @param other The other value to compare.
+ * @return Returns true if value is greater than other, else false.
+ */
+ gt(
+ value: any,
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.gt
+ */
+ gt(other: any): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.gt
+ */
+ gt(other: any): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.gte
+ interface LoDashStatic {
+ /**
+ * Checks if value is greater than or equal to other.
+ *
+ * @param value The value to compare.
+ * @param other The other value to compare.
+ * @return Returns true if value is greater than or equal to other, else false.
+ */
+ gte(
+ value: any,
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.gte
+ */
+ gte(other: any): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.gte
+ */
+ gte(other: any): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isArguments
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as an arguments object.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isArguments(value?: any): value is IArguments;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isArguments
+ */
+ isArguments(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isArguments
+ */
+ isArguments(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isArray
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as an Array object.
+ * @param value The value to check.
+ *
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isArray<T>(value?: any): value is T[];
+ }
+
+ interface LoDashImplicitWrapperBase<T,TWrapper> {
+ /**
+ * @see _.isArray
+ */
+ isArray(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T,TWrapper> {
+ /**
+ * @see _.isArray
+ */
+ isArray(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isArrayBuffer
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as an ArrayBuffer object.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isArrayBuffer(value?: any): value is ArrayBuffer;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isArrayBuffer
+ */
+ isArrayBuffer(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isArrayBuffer
+ */
+ isArrayBuffer(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isArrayLike
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is array-like. A value is considered array-like if it's
+ * not a function and has a `value.length` that's an integer greater than or
+ * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+ * @example
+ *
+ * _.isArrayLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isArrayLike(document.body.children);
+ * // => true
+ *
+ * _.isArrayLike('abc');
+ * // => true
+ *
+ * _.isArrayLike(_.noop);
+ * // => false
+ */
+ isArrayLike<T>(value?: any): value is T[];
+ }
+
+ interface LoDashImplicitWrapperBase<T,TWrapper> {
+ /**
+ * @see _.isArrayLike
+ */
+ isArrayLike(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T,TWrapper> {
+ /**
+ * @see _.isArrayLike
+ */
+ isArrayLike(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isArrayLikeObject
+ interface LoDashStatic {
+ /**
+ * This method is like `_.isArrayLike` except that it also checks if `value`
+ * is an object.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array-like object, else `false`.
+ * @example
+ *
+ * _.isArrayLikeObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isArrayLikeObject(document.body.children);
+ * // => true
+ *
+ * _.isArrayLikeObject('abc');
+ * // => false
+ *
+ * _.isArrayLikeObject(_.noop);
+ * // => false
+ */
+ isArrayLikeObject<T>(value?: any): value is T[];
+ }
+
+ interface LoDashImplicitWrapperBase<T,TWrapper> {
+ /**
+ * @see _.isArrayLikeObject
+ */
+ isArrayLikeObject(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T,TWrapper> {
+ /**
+ * @see _.isArrayLikeObject
+ */
+ isArrayLikeObject(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isBoolean
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a boolean primitive or object.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isBoolean(value?: any): value is boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isBoolean
+ */
+ isBoolean(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isBoolean
+ */
+ isBoolean(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isBuffer
+ interface LoDashStatic {
+ /**
+ * Checks if value is a buffer.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is a buffer, else false.
+ */
+ isBuffer(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isBuffer
+ */
+ isBuffer(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isBuffer
+ */
+ isBuffer(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isDate
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a Date object.
+ * @param value The value to check.
+ *
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isDate(value?: any): value is Date;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isDate
+ */
+ isDate(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isDate
+ */
+ isDate(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isElement
+ interface LoDashStatic {
+ /**
+ * Checks if value is a DOM element.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is a DOM element, else false.
+ */
+ isElement(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isElement
+ */
+ isElement(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isElement
+ */
+ isElement(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isEmpty
+ interface LoDashStatic {
+ /**
+ * Checks if value is empty. A value is considered empty unless it’s an arguments object, array, string, or
+ * jQuery-like collection with a length greater than 0 or an object with own enumerable properties.
+ *
+ * @param value The value to inspect.
+ * @return Returns true if value is empty, else false.
+ */
+ isEmpty(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEmpty
+ */
+ isEmpty(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEmpty
+ */
+ isEmpty(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isEqual
+ interface LoDashStatic {
+ /**
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent.
+ *
+ * **Note:** This method supports comparing arrays, array buffers, booleans,
+ * date objects, error objects, maps, numbers, `Object` objects, regexes,
+ * sets, strings, symbols, and typed arrays. `Object` objects are compared
+ * by their own, not inherited, enumerable properties. Functions and DOM
+ * nodes are **not** supported.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'user': 'fred' };
+ * var other = { 'user': 'fred' };
+ *
+ * _.isEqual(object, other);
+ * // => true
+ *
+ * object === other;
+ * // => false
+ */
+ isEqual(
+ value: any,
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEqual
+ */
+ isEqual(
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEqual
+ */
+ isEqual(
+ other: any
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ // _.isEqualWith
+ interface IsEqualCustomizer {
+ (value: any, other: any, indexOrKey?: number|string): boolean;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like `_.isEqual` except that it accepts `customizer` which is
+ * invoked to compare values. If `customizer` returns `undefined` comparisons are
+ * handled by the method instead. The `customizer` is invoked with up to seven arguments:
+ * (objValue, othValue [, index|key, object, other, stack]).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * function isGreeting(value) {
+ * return /^h(?:i|ello)$/.test(value);
+ * }
+ *
+ * function customizer(objValue, othValue) {
+ * if (isGreeting(objValue) && isGreeting(othValue)) {
+ * return true;
+ * }
+ * }
+ *
+ * var array = ['hello', 'goodbye'];
+ * var other = ['hi', 'goodbye'];
+ *
+ * _.isEqualWith(array, other, customizer);
+ * // => true
+ */
+ isEqualWith(
+ value: any,
+ other: any,
+ customizer: IsEqualCustomizer
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEqualWith
+ */
+ isEqualWith(
+ other: any,
+ customizer: IsEqualCustomizer
+ ): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isEqualWith
+ */
+ isEqualWith(
+ other: any,
+ customizer: IsEqualCustomizer
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isError
+ interface LoDashStatic {
+ /**
+ * Checks if value is an Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, or URIError
+ * object.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is an error object, else false.
+ */
+ isError(value: any): value is Error;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isError
+ */
+ isError(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isError
+ */
+ isError(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isFinite
+ interface LoDashStatic {
+ /**
+ * Checks if value is a finite primitive number.
+ *
+ * Note: This method is based on Number.isFinite.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is a finite number, else false.
+ */
+ isFinite(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isFinite
+ */
+ isFinite(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isFinite
+ */
+ isFinite(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isFunction
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a Function object.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isFunction(value?: any): value is Function;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isFunction
+ */
+ isFunction(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isFunction
+ */
+ isFunction(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isInteger
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is an integer.
+ *
+ * **Note:** This method is based on [`Number.isInteger`](https://mdn.io/Number/isInteger).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an integer, else `false`.
+ * @example
+ *
+ * _.isInteger(3);
+ * // => true
+ *
+ * _.isInteger(Number.MIN_VALUE);
+ * // => false
+ *
+ * _.isInteger(Infinity);
+ * // => false
+ *
+ * _.isInteger('3');
+ * // => false
+ */
+ isInteger(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isInteger
+ */
+ isInteger(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isInteger
+ */
+ isInteger(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isLength
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is a valid array-like length.
+ *
+ * **Note:** This function is loosely based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+ * @example
+ *
+ * _.isLength(3);
+ * // => true
+ *
+ * _.isLength(Number.MIN_VALUE);
+ * // => false
+ *
+ * _.isLength(Infinity);
+ * // => false
+ *
+ * _.isLength('3');
+ * // => false
+ */
+ isLength(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isLength
+ */
+ isLength(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isLength
+ */
+ isLength(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isMap
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a Map object.
+ *
+ * @param value The value to check.
+ * @returns Returns true if value is correctly classified, else false.
+ */
+ isMap<K, V>(value?: any): value is Map<K, V>;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isMap
+ */
+ isMap(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isMap
+ */
+ isMap(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isMatch
+ interface isMatchCustomizer {
+ (value: any, other: any, indexOrKey?: number|string): boolean;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Performs a deep comparison between `object` and `source` to determine if
+ * `object` contains equivalent property values.
+ *
+ * **Note:** This method supports comparing the same values as `_.isEqual`.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ * @example
+ *
+ * var object = { 'user': 'fred', 'age': 40 };
+ *
+ * _.isMatch(object, { 'age': 40 });
+ * // => true
+ *
+ * _.isMatch(object, { 'age': 36 });
+ * // => false
+ */
+ isMatch(object: Object, source: Object): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.isMatch
+ */
+ isMatch(source: Object): boolean;
+ }
+
+ //_.isMatchWith
+ interface isMatchWithCustomizer {
+ (value: any, other: any, indexOrKey?: number|string): boolean;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like `_.isMatch` except that it accepts `customizer` which
+ * is invoked to compare values. If `customizer` returns `undefined` comparisons
+ * are handled by the method instead. The `customizer` is invoked with three
+ * arguments: (objValue, srcValue, index|key, object, source).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ * @example
+ *
+ * function isGreeting(value) {
+ * return /^h(?:i|ello)$/.test(value);
+ * }
+ *
+ * function customizer(objValue, srcValue) {
+ * if (isGreeting(objValue) && isGreeting(srcValue)) {
+ * return true;
+ * }
+ * }
+ *
+ * var object = { 'greeting': 'hello' };
+ * var source = { 'greeting': 'hi' };
+ *
+ * _.isMatchWith(object, source, customizer);
+ * // => true
+ */
+ isMatchWith(object: Object, source: Object, customizer: isMatchWithCustomizer): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.isMatchWith
+ */
+ isMatchWith(source: Object, customizer: isMatchWithCustomizer): boolean;
+ }
+
+ //_.isNaN
+ interface LoDashStatic {
+ /**
+ * Checks if value is NaN.
+ *
+ * Note: This method is not the same as isNaN which returns true for undefined and other non-numeric values.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is NaN, else false.
+ */
+ isNaN(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isNaN
+ */
+ isNaN(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isNaN
+ */
+ isNaN(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isNative
+ interface LoDashStatic {
+ /**
+ * Checks if value is a native function.
+ * @param value The value to check.
+ *
+ * @retrun Returns true if value is a native function, else false.
+ */
+ isNative(value: any): value is Function;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNative
+ */
+ isNative(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNative
+ */
+ isNative(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isNil
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is `null` or `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
+ * @example
+ *
+ * _.isNil(null);
+ * // => true
+ *
+ * _.isNil(void 0);
+ * // => true
+ *
+ * _.isNil(NaN);
+ * // => false
+ */
+ isNil(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNil
+ */
+ isNil(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNil
+ */
+ isNil(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isNull
+ interface LoDashStatic {
+ /**
+ * Checks if value is null.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is null, else false.
+ */
+ isNull(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNull
+ */
+ isNull(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNull
+ */
+ isNull(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isNumber
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a Number primitive or object.
+ *
+ * Note: To exclude Infinity, -Infinity, and NaN, which are classified as numbers, use the _.isFinite method.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isNumber(value?: any): value is number;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNumber
+ */
+ isNumber(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isNumber
+ */
+ isNumber(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isObject
+ interface LoDashStatic {
+ /**
+ * Checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0),
+ * and new String(''))
+ *
+ * @param value The value to check.
+ * @return Returns true if value is an object, else false.
+ */
+ isObject(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isObject
+ */
+ isObject(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isObject
+ */
+ isObject(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isObjectLike
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+ isObjectLike(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isObjectLike
+ */
+ isObjectLike(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isObjectLike
+ */
+ isObjectLike(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isPlainObject
+ interface LoDashStatic {
+ /**
+ * Checks if value is a plain object, that is, an object created by the Object constructor or one with a
+ * [[Prototype]] of null.
+ *
+ * Note: This method assumes objects created by the Object constructor have no inherited enumerable properties.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is a plain object, else false.
+ */
+ isPlainObject(value?: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isPlainObject
+ */
+ isPlainObject(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isPlainObject
+ */
+ isPlainObject(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isRegExp
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a RegExp object.
+ * @param value The value to check.
+ *
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isRegExp(value?: any): value is RegExp;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isRegExp
+ */
+ isRegExp(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isRegExp
+ */
+ isRegExp(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isSafeInteger
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754
+ * double precision number which isn't the result of a rounded unsafe integer.
+ *
+ * **Note:** This method is based on [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.
+ * @example
+ *
+ * _.isSafeInteger(3);
+ * // => true
+ *
+ * _.isSafeInteger(Number.MIN_VALUE);
+ * // => false
+ *
+ * _.isSafeInteger(Infinity);
+ * // => false
+ *
+ * _.isSafeInteger('3');
+ * // => false
+ */
+ isSafeInteger(value: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isSafeInteger
+ */
+ isSafeInteger(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isSafeInteger
+ */
+ isSafeInteger(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isSet
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a Set object.
+ *
+ * @param value The value to check.
+ * @returns Returns true if value is correctly classified, else false.
+ */
+ isSet<T>(value?: any): value is Set<T>;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isSet
+ */
+ isSet(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isSet
+ */
+ isSet(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isString
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a String primitive or object.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isString(value?: any): value is string;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isString
+ */
+ isString(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isString
+ */
+ isString(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isSymbol
+ interface LoDashStatic {
+ /**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+ isSymbol(value: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isSymbol
+ */
+ isSymbol(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isSymbol
+ */
+ isSymbol(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isTypedArray
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a typed array.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is correctly classified, else false.
+ */
+ isTypedArray(value: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isTypedArray
+ */
+ isTypedArray(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isTypedArray
+ */
+ isTypedArray(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isUndefined
+ interface LoDashStatic {
+ /**
+ * Checks if value is undefined.
+ *
+ * @param value The value to check.
+ * @return Returns true if value is undefined, else false.
+ */
+ isUndefined(value: any): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isUndefined
+ */
+ isUndefined(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * see _.isUndefined
+ */
+ isUndefined(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isWeakMap
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a WeakMap object.
+ *
+ * @param value The value to check.
+ * @returns Returns true if value is correctly classified, else false.
+ */
+ isWeakMap<K, V>(value?: any): value is WeakMap<K, V>;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isSet
+ */
+ isWeakMap(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isSet
+ */
+ isWeakMap(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.isWeakSet
+ interface LoDashStatic {
+ /**
+ * Checks if value is classified as a WeakSet object.
+ *
+ * @param value The value to check.
+ * @returns Returns true if value is correctly classified, else false.
+ */
+ isWeakSet<T>(value?: any): value is WeakSet<T>;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isWeakSet
+ */
+ isWeakSet(): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.isWeakSet
+ */
+ isWeakSet(): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.lt
+ interface LoDashStatic {
+ /**
+ * Checks if value is less than other.
+ *
+ * @param value The value to compare.
+ * @param other The other value to compare.
+ * @return Returns true if value is less than other, else false.
+ */
+ lt(
+ value: any,
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.lt
+ */
+ lt(other: any): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.lt
+ */
+ lt(other: any): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.lte
+ interface LoDashStatic {
+ /**
+ * Checks if value is less than or equal to other.
+ *
+ * @param value The value to compare.
+ * @param other The other value to compare.
+ * @return Returns true if value is less than or equal to other, else false.
+ */
+ lte(
+ value: any,
+ other: any
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.lte
+ */
+ lte(other: any): boolean;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.lte
+ */
+ lte(other: any): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.toArray
+ interface LoDashStatic {
+ /**
+ * Converts value to an array.
+ *
+ * @param value The value to convert.
+ * @return Returns the converted array.
+ */
+ toArray<T>(value: List<T>|Dictionary<T>|NumericDictionary<T>): T[];
+
+ /**
+ * @see _.toArray
+ */
+ toArray<TValue, TResult>(value: TValue): TResult[];
+
+ /**
+ * @see _.toArray
+ */
+ toArray<TResult>(value?: any): TResult[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.toArray
+ */
+ toArray<TResult>(): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.toArray
+ */
+ toArray(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.toArray
+ */
+ toArray<TResult>(): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.toArray
+ */
+ toArray<TResult>(): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.toArray
+ */
+ toArray(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.toArray
+ */
+ toArray<TResult>(): LoDashExplicitArrayWrapper<TResult>;
+ }
+
+ //_.toPlainObject
+ interface LoDashStatic {
+ /**
+ * Converts value to a plain object flattening inherited enumerable properties of value to own properties
+ * of the plain object.
+ *
+ * @param value The value to convert.
+ * @return Returns the converted plain object.
+ */
+ toPlainObject<TResult extends {}>(value?: any): TResult;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toPlainObject
+ */
+ toPlainObject<TResult extends {}>(): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ //_.toInteger
+ interface LoDashStatic {
+ /**
+ * Converts `value` to an integer.
+ *
+ * **Note:** This function is loosely based on [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
+ * @example
+ *
+ * _.toInteger(3);
+ * // => 3
+ *
+ * _.toInteger(Number.MIN_VALUE);
+ * // => 0
+ *
+ * _.toInteger(Infinity);
+ * // => 1.7976931348623157e+308
+ *
+ * _.toInteger('3');
+ * // => 3
+ */
+ toInteger(value: any): number;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toInteger
+ */
+ toInteger(): LoDashImplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toInteger
+ */
+ toInteger(): LoDashExplicitWrapper<number>;
+ }
+
+ //_.toLength
+ interface LoDashStatic {
+ /**
+ * Converts `value` to an integer suitable for use as the length of an
+ * array-like object.
+ *
+ * **Note:** This method is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @return {number} Returns the converted integer.
+ * @example
+ *
+ * _.toLength(3);
+ * // => 3
+ *
+ * _.toLength(Number.MIN_VALUE);
+ * // => 0
+ *
+ * _.toLength(Infinity);
+ * // => 4294967295
+ *
+ * _.toLength('3');
+ * // => 3
+ */
+ toLength(value: any): number;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toLength
+ */
+ toLength(): LoDashImplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toLength
+ */
+ toLength(): LoDashExplicitWrapper<number>;
+ }
+
+ //_.toNumber
+ interface LoDashStatic {
+ /**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3);
+ * // => 3
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3');
+ * // => 3
+ */
+ toNumber(value: any): number;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toNumber
+ */
+ toNumber(): LoDashImplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toNumber
+ */
+ toNumber(): LoDashExplicitWrapper<number>;
+ }
+
+ //_.toSafeInteger
+ interface LoDashStatic {
+ /**
+ * Converts `value` to a safe integer. A safe integer can be compared and
+ * represented correctly.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
+ * @example
+ *
+ * _.toSafeInteger(3);
+ * // => 3
+ *
+ * _.toSafeInteger(Number.MIN_VALUE);
+ * // => 0
+ *
+ * _.toSafeInteger(Infinity);
+ * // => 9007199254740991
+ *
+ * _.toSafeInteger('3');
+ * // => 3
+ */
+ toSafeInteger(value: any): number;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toSafeInteger
+ */
+ toSafeInteger(): LoDashImplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toSafeInteger
+ */
+ toSafeInteger(): LoDashExplicitWrapper<number>;
+ }
+
+ //_.toString DUMMY
+ interface LoDashStatic {
+ /**
+ * Converts `value` to a string if it's not one. An empty string is returned
+ * for `null` and `undefined` values. The sign of `-0` is preserved.
+ *
+ * @static
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ * @example
+ *
+ * _.toString(null);
+ * // => ''
+ *
+ * _.toString(-0);
+ * // => '-0'
+ *
+ * _.toString([1, 2, 3]);
+ * // => '1,2,3'
+ */
+ toString(value: any): string;
+ }
+
+ /********
+ * Math *
+ ********/
+
+ //_.add
+ interface LoDashStatic {
+ /**
+ * Adds two numbers.
+ *
+ * @param augend The first number to add.
+ * @param addend The second number to add.
+ * @return Returns the sum.
+ */
+ add(
+ augend: number,
+ addend: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.add
+ */
+ add(addend: number): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.add
+ */
+ add(addend: number): LoDashExplicitWrapper<number>;
+ }
+
+ //_.ceil
+ interface LoDashStatic {
+ /**
+ * Calculates n rounded up to precision.
+ *
+ * @param n The number to round up.
+ * @param precision The precision to round up to.
+ * @return Returns the rounded up number.
+ */
+ ceil(
+ n: number,
+ precision?: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.ceil
+ */
+ ceil(precision?: number): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.ceil
+ */
+ ceil(precision?: number): LoDashExplicitWrapper<number>;
+ }
+
+ //_.floor
+ interface LoDashStatic {
+ /**
+ * Calculates n rounded down to precision.
+ *
+ * @param n The number to round down.
+ * @param precision The precision to round down to.
+ * @return Returns the rounded down number.
+ */
+ floor(
+ n: number,
+ precision?: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.floor
+ */
+ floor(precision?: number): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.floor
+ */
+ floor(precision?: number): LoDashExplicitWrapper<number>;
+ }
+
+ //_.max
+ interface LoDashStatic {
+ /**
+ * Computes the maximum value of `array`. If `array` is empty or falsey
+ * `undefined` is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @returns {*} Returns the maximum value.
+ */
+ max<T>(
+ collection: List<T>
+ ): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.max
+ */
+ max(): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.max
+ */
+ max<T>(): T;
+ }
+
+ //_.maxBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.max` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the criterion by which
+ * the value is ranked. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {*} Returns the maximum value.
+ * @example
+ *
+ * var objects = [{ 'n': 1 }, { 'n': 2 }];
+ *
+ * _.maxBy(objects, function(o) { return o.a; });
+ * // => { 'n': 2 }
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.maxBy(objects, 'n');
+ * // => { 'n': 2 }
+ */
+ maxBy<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy<T>(
+ collection: List<T>|Dictionary<T>,
+ iteratee?: string
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy<TObject extends {}, T>(
+ collection: List<T>|Dictionary<T>,
+ whereValue?: TObject
+ ): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.maxBy
+ */
+ maxBy(
+ iteratee?: ListIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy(
+ iteratee?: string
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy<TObject extends {}>(
+ whereValue?: TObject
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.maxBy
+ */
+ maxBy<T>(
+ iteratee?: ListIterator<T, any>|DictionaryIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy<T>(
+ iteratee?: string
+ ): T;
+
+ /**
+ * @see _.maxBy
+ */
+ maxBy<TObject extends {}, T>(
+ whereValue?: TObject
+ ): T;
+ }
+
+ //_.mean
+ interface LoDashStatic {
+ /**
+ * Computes the mean of the values in `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @returns {number} Returns the mean.
+ * @example
+ *
+ * _.mean([4, 2, 8, 6]);
+ * // => 5
+ */
+ mean<T>(
+ collection: List<T>
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.mean
+ */
+ mean<T>(): number;
+
+ /**
+ * @see _.mean
+ */
+ mean(): number;
+ }
+
+ //_.min
+ interface LoDashStatic {
+ /**
+ * Computes the minimum value of `array`. If `array` is empty or falsey
+ * `undefined` is returned.
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @returns {*} Returns the minimum value.
+ */
+ min<T>(
+ collection: List<T>
+ ): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.min
+ */
+ min(): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.min
+ */
+ min<T>(): T;
+ }
+
+ //_.minBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.min` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the criterion by which
+ * the value is ranked. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {*} Returns the minimum value.
+ * @example
+ *
+ * var objects = [{ 'n': 1 }, { 'n': 2 }];
+ *
+ * _.minBy(objects, function(o) { return o.a; });
+ * // => { 'n': 1 }
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.minBy(objects, 'n');
+ * // => { 'n': 1 }
+ */
+ minBy<T>(
+ collection: List<T>,
+ iteratee?: ListIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy<T>(
+ collection: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy<T>(
+ collection: List<T>|Dictionary<T>,
+ iteratee?: string
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy<TObject extends {}, T>(
+ collection: List<T>|Dictionary<T>,
+ whereValue?: TObject
+ ): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.minBy
+ */
+ minBy(
+ iteratee?: ListIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy(
+ iteratee?: string
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy<TObject extends {}>(
+ whereValue?: TObject
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.minBy
+ */
+ minBy<T>(
+ iteratee?: ListIterator<T, any>|DictionaryIterator<T, any>
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy<T>(
+ iteratee?: string
+ ): T;
+
+ /**
+ * @see _.minBy
+ */
+ minBy<TObject extends {}, T>(
+ whereValue?: TObject
+ ): T;
+ }
+
+ //_.round
+ interface LoDashStatic {
+ /**
+ * Calculates n rounded to precision.
+ *
+ * @param n The number to round.
+ * @param precision The precision to round to.
+ * @return Returns the rounded number.
+ */
+ round(
+ n: number,
+ precision?: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.round
+ */
+ round(precision?: number): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.round
+ */
+ round(precision?: number): LoDashExplicitWrapper<number>;
+ }
+
+ //_.sum
+ interface LoDashStatic {
+ /**
+ * Computes the sum of the values in `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @returns {number} Returns the sum.
+ * @example
+ *
+ * _.sum([4, 2, 8, 6]);
+ * // => 20
+ */
+ sum<T>(collection: List<T>): number;
+
+ /**
+ * @see _.sum
+ */
+ sum(collection: List<number>|Dictionary<number>): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sum
+ */
+ sum(): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sum
+ **/
+ sum<TValue>(): number;
+
+ /**
+ * @see _.sum
+ */
+ sum(): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sum
+ */
+ sum(): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sum
+ */
+ sum<TValue>(): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sum
+ */
+ sum(): LoDashExplicitWrapper<number>;
+ }
+
+ //_.sumBy
+ interface LoDashStatic {
+ /**
+ * This method is like `_.sum` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the value to be summed.
+ * The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {Array} array The array to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the sum.
+ * @example
+ *
+ * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];
+ *
+ * _.sumBy(objects, function(o) { return o.n; });
+ * // => 20
+ *
+ * // using the `_.property` iteratee shorthand
+ * _.sumBy(objects, 'n');
+ * // => 20
+ */
+ sumBy<T>(
+ collection: List<T>,
+ iteratee: ListIterator<T, number>
+ ): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ collection: List<{}>,
+ iteratee: string
+ ): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ collection: List<number>
+ ): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ collection: List<{}>,
+ iteratee: Dictionary<{}>
+ ): number;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ iteratee: ListIterator<T, number>
+ ): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: string): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: Dictionary<{}>): number;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ iteratee: ListIterator<{}, number>
+ ): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: string): number;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: Dictionary<{}>): number;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ iteratee: ListIterator<T, number>
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: string): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: Dictionary<{}>): LoDashExplicitWrapper<number>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.sumBy
+ */
+ sumBy(
+ iteratee: ListIterator<{}, number>
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: string): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.sumBy
+ */
+ sumBy(iteratee: Dictionary<{}>): LoDashExplicitWrapper<number>;
+ }
+
+ /**********
+ * Number *
+ **********/
+
+ //_.subtract
+ interface LoDashStatic {
+ /**
+ * Subtract two numbers.
+ *
+ * @static
+ * @memberOf _
+ * @category Math
+ * @param {number} minuend The first number in a subtraction.
+ * @param {number} subtrahend The second number in a subtraction.
+ * @returns {number} Returns the difference.
+ * @example
+ *
+ * _.subtract(6, 4);
+ * // => 2
+ */
+ subtract(
+ minuend: number,
+ subtrahend: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.subtract
+ */
+ subtract(
+ subtrahend: number
+ ): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.subtract
+ */
+ subtract(
+ subtrahend: number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.clamp
+ interface LoDashStatic {
+ /**
+ * Clamps `number` within the inclusive `lower` and `upper` bounds.
+ *
+ * @static
+ * @memberOf _
+ * @category Number
+ * @param {number} number The number to clamp.
+ * @param {number} [lower] The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the clamped number.
+ * @example
+ *
+ * _.clamp(-10, -5, 5);
+ * // => -5
+ *
+ * _.clamp(10, -5, 5);
+ * // => 5
+ */
+ clamp(
+ number: number,
+ lower: number,
+ upper: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.clamp
+ */
+ clamp(
+ lower: number,
+ upper: number
+ ): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.clamp
+ */
+ clamp(
+ lower: number,
+ upper: number
+ ): LoDashExplicitWrapper<number>;
+ }
+
+ //_.inRange
+ interface LoDashStatic {
+ /**
+ * Checks if n is between start and up to but not including, end. If end is not specified it’s set to start
+ * with start then set to 0.
+ *
+ * @param n The number to check.
+ * @param start The start of the range.
+ * @param end The end of the range.
+ * @return Returns true if n is in the range, else false.
+ */
+ inRange(
+ n: number,
+ start: number,
+ end: number
+ ): boolean;
+
+
+ /**
+ * @see _.inRange
+ */
+ inRange(
+ n: number,
+ end: number
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.inRange
+ */
+ inRange(
+ start: number,
+ end: number
+ ): boolean;
+
+ /**
+ * @see _.inRange
+ */
+ inRange(end: number): boolean;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.inRange
+ */
+ inRange(
+ start: number,
+ end: number
+ ): LoDashExplicitWrapper<boolean>;
+
+ /**
+ * @see _.inRange
+ */
+ inRange(end: number): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.random
+ interface LoDashStatic {
+ /**
+ * Produces a random number between min and max (inclusive). If only one argument is provided a number between
+ * 0 and the given number is returned. If floating is true, or either min or max are floats, a floating-point
+ * number is returned instead of an integer.
+ *
+ * @param min The minimum possible value.
+ * @param max The maximum possible value.
+ * @param floating Specify returning a floating-point number.
+ * @return Returns the random number.
+ */
+ random(
+ min?: number,
+ max?: number,
+ floating?: boolean
+ ): number;
+
+ /**
+ * @see _.random
+ */
+ random(
+ min?: number,
+ floating?: boolean
+ ): number;
+
+ /**
+ * @see _.random
+ */
+ random(floating?: boolean): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.random
+ */
+ random(
+ max?: number,
+ floating?: boolean
+ ): number;
+
+ /**
+ * @see _.random
+ */
+ random(floating?: boolean): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.random
+ */
+ random(
+ max?: number,
+ floating?: boolean
+ ): LoDashExplicitWrapper<number>;
+
+ /**
+ * @see _.random
+ */
+ random(floating?: boolean): LoDashExplicitWrapper<number>;
+ }
+
+ /**********
+ * Object *
+ **********/
+
+ //_.assign
+ interface LoDashStatic {
+ /**
+ * Assigns own enumerable properties of source objects to the destination
+ * object. Source objects are applied from left to right. Subsequent sources
+ * overwrite property assignments of previous sources.
+ *
+ * **Note:** This method mutates `object` and is loosely based on
+ * [`Object.assign`](https://mdn.io/Object/assign).
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Foo() {
+ * this.c = 3;
+ * }
+ *
+ * function Bar() {
+ * this.e = 5;
+ * }
+ *
+ * Foo.prototype.d = 4;
+ * Bar.prototype.f = 6;
+ *
+ * _.assign({ 'a': 1 }, new Foo, new Bar);
+ * // => { 'a': 1, 'c': 3, 'e': 5 }
+ */
+ assign<TObject, TSource>(
+ object: TObject,
+ source: TSource
+ ): TObject & TSource;
+
+ /**
+ * @see assign
+ */
+ assign<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see assign
+ */
+ assign<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see assign
+ */
+ assign<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.assign
+ */
+ assign<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.assign
+ */
+ assign<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.assign
+ */
+ assign<TSource>(
+ source: TSource
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assign
+ */
+ assign<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assign
+ */
+ assign<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assign
+ */
+ assign<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assign
+ */
+ assign(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assign
+ */
+ assign<TResult>(...otherArgs: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.assign
+ */
+ assign<TSource>(
+ source: TSource
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assign
+ */
+ assign<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assign
+ */
+ assign<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assign
+ */
+ assign<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assign
+ */
+ assign(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assign
+ */
+ assign<TResult>(...otherArgs: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.assignWith
+ interface AssignCustomizer {
+ (objectValue: any, sourceValue: any, key?: string, object?: {}, source?: {}): any;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like `_.assign` except that it accepts `customizer` which
+ * is invoked to produce the assigned values. If `customizer` returns `undefined`
+ * assignment is handled by the method instead. The `customizer` is invoked
+ * with five arguments: (objValue, srcValue, key, object, source).
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function customizer(objValue, srcValue) {
+ * return _.isUndefined(objValue) ? srcValue : objValue;
+ * }
+ *
+ * var defaults = _.partialRight(_.assignWith, customizer);
+ *
+ * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+ * // => { 'a': 1, 'b': 2 }
+ */
+ assignWith<TObject, TSource>(
+ object: TObject,
+ source: TSource,
+ customizer: AssignCustomizer
+ ): TObject & TSource;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.assignWith
+ */
+ assignWith<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.assignWith
+ */
+ assignWith<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.assignWith
+ */
+ assignWith<TSource>(
+ source: TSource,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignWith
+ */
+ assignWith(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignWith
+ */
+ assignWith<TResult>(...otherArgs: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.assignWith
+ */
+ assignWith<TSource>(
+ source: TSource,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assignWith
+ */
+ assignWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignWith
+ */
+ assignWith(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignWith
+ */
+ assignWith<TResult>(...otherArgs: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.assignIn
+ interface LoDashStatic {
+ /**
+ * This method is like `_.assign` except that it iterates over own and
+ * inherited source properties.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @alias extend
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function Foo() {
+ * this.b = 2;
+ * }
+ *
+ * function Bar() {
+ * this.d = 4;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ * Bar.prototype.e = 5;
+ *
+ * _.assignIn({ 'a': 1 }, new Foo, new Bar);
+ * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }
+ */
+ assignIn<TObject, TSource>(
+ object: TObject,
+ source: TSource
+ ): TObject & TSource;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.assignIn
+ */
+ assignIn<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.assignIn
+ */
+ assignIn<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.assignIn
+ */
+ assignIn<TSource>(
+ source: TSource
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignIn
+ */
+ assignIn(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignIn
+ */
+ assignIn<TResult>(...otherArgs: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.assignIn
+ */
+ assignIn<TSource>(
+ source: TSource
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assignIn
+ */
+ assignIn<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignIn
+ */
+ assignIn(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignIn
+ */
+ assignIn<TResult>(...otherArgs: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.assignInWith
+ interface AssignCustomizer {
+ (objectValue: any, sourceValue: any, key?: string, object?: {}, source?: {}): any;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like `_.assignIn` except that it accepts `customizer` which
+ * is invoked to produce the assigned values. If `customizer` returns `undefined`
+ * assignment is handled by the method instead. The `customizer` is invoked
+ * with five arguments: (objValue, srcValue, key, object, source).
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @alias extendWith
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function customizer(objValue, srcValue) {
+ * return _.isUndefined(objValue) ? srcValue : objValue;
+ * }
+ *
+ * var defaults = _.partialRight(_.assignInWith, customizer);
+ *
+ * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+ * // => { 'a': 1, 'b': 2 }
+ */
+ assignInWith<TObject, TSource>(
+ object: TObject,
+ source: TSource,
+ customizer: AssignCustomizer
+ ): TObject & TSource;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith<TSource>(
+ source: TSource,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith<TResult>(...otherArgs: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith<TSource>(
+ source: TSource,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see assignInWith
+ */
+ assignInWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignInWith
+ */
+ assignInWith<TResult>(...otherArgs: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.create
+ interface LoDashStatic {
+ /**
+ * Creates an object that inherits from the given prototype object. If a properties object is provided its own
+ * enumerable properties are assigned to the created object.
+ *
+ * @param prototype The object to inherit from.
+ * @param properties The properties to assign to the object.
+ * @return Returns the new object.
+ */
+ create<T extends Object, U extends Object>(
+ prototype: T,
+ properties?: U
+ ): T & U;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.create
+ */
+ create<U extends Object>(properties?: U): LoDashImplicitObjectWrapper<T & U>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.create
+ */
+ create<U extends Object>(properties?: U): LoDashExplicitObjectWrapper<T & U>;
+ }
+
+
+ //_.defaults
+ interface LoDashStatic {
+ /**
+ * Assigns own enumerable properties of source object(s) to the destination object for all destination
+ * properties that resolve to undefined. Once a property is set, additional values of the same property are
+ * ignored.
+ *
+ * Note: This method mutates object.
+ *
+ * @param object The destination object.
+ * @param sources The source objects.
+ * @return The destination object.
+ */
+ defaults<TObject, TSource>(
+ object: TObject,
+ source: TSource
+ ): TSource & TObject;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2
+ ): TSource2 & TSource1 & TObject;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): TSource3 & TSource2 & TSource1 & TObject;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): TSource4 & TSource3 & TSource2 & TSource1 & TObject;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TResult>(
+ object: any,
+ ...sources: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource>(
+ source: TSource
+ ): LoDashImplicitObjectWrapper<TSource & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashImplicitObjectWrapper<TSource2 & TSource1 & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashImplicitObjectWrapper<TSource3 & TSource2 & TSource1 & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashImplicitObjectWrapper<TSource4 & TSource3 & TSource2 & TSource1 & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TResult>(...sources: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource>(
+ source: TSource
+ ): LoDashExplicitObjectWrapper<TSource & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashExplicitObjectWrapper<TSource2 & TSource1 & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashExplicitObjectWrapper<TSource3 & TSource2 & TSource1 & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashExplicitObjectWrapper<TSource4 & TSource3 & TSource2 & TSource1 & T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.defaults
+ */
+ defaults<TResult>(...sources: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.defaultsDeep
+ interface LoDashStatic {
+ /**
+ * This method is like _.defaults except that it recursively assigns default properties.
+ * @param object The destination object.
+ * @param sources The source objects.
+ * @return Returns object.
+ **/
+ defaultsDeep<T, TResult>(
+ object: T,
+ ...sources: any[]): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.defaultsDeep
+ **/
+ defaultsDeep<TResult>(...sources: any[]): LoDashImplicitObjectWrapper<TResult>
+ }
+
+ // _.extend
+ interface LoDashStatic {
+ /**
+ * @see _.assignIn
+ */
+ extend<TObject, TSource>(
+ object: TObject,
+ source: TSource
+ ): TObject & TSource;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource>(
+ source: TSource
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TResult>(...otherArgs: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource>(
+ source: TSource
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignIn
+ */
+ extend<TResult>(...otherArgs: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashStatic {
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TObject, TSource>(
+ object: TObject,
+ source: TSource,
+ customizer: AssignCustomizer
+ ): TObject & TSource;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TObject>(object: TObject): TObject;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource>(
+ source: TSource,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith(): LoDashImplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TResult>(...otherArgs: any[]): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource>(
+ source: TSource,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: AssignCustomizer
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith(): LoDashExplicitObjectWrapper<T>;
+
+ /**
+ * @see _.assignInWith
+ */
+ extendWith<TResult>(...otherArgs: any[]): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.findKey
+ interface LoDashStatic {
+ /**
+ * This method is like _.find except that it returns the key of the first element predicate returns truthy for
+ * instead of the element itself.
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param object The object to search.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the key of the matched element, else undefined.
+ */
+ findKey<TValues, TObject>(
+ object: TObject,
+ predicate?: DictionaryIterator<TValues, boolean>
+ ): string;
+
+ /**
+ * @see _.findKey
+ */
+ findKey<TObject>(
+ object: TObject,
+ predicate?: ObjectIterator<any, boolean>
+ ): string;
+
+ /**
+ * @see _.findKey
+ */
+ findKey<TObject>(
+ object: TObject,
+ predicate?: string
+ ): string;
+
+ /**
+ * @see _.findKey
+ */
+ findKey<TWhere extends Dictionary<any>, TObject>(
+ object: TObject,
+ predicate?: TWhere
+ ): string;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.findKey
+ */
+ findKey<TValues>(
+ predicate?: DictionaryIterator<TValues, boolean>
+ ): string;
+
+ /**
+ * @see _.findKey
+ */
+ findKey(
+ predicate?: ObjectIterator<any, boolean>
+ ): string;
+
+ /**
+ * @see _.findKey
+ */
+ findKey(
+ predicate?: string
+ ): string;
+
+ /**
+ * @see _.findKey
+ */
+ findKey<TWhere extends Dictionary<any>>(
+ predicate?: TWhere
+ ): string;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.findKey
+ */
+ findKey<TValues>(
+ predicate?: DictionaryIterator<TValues, boolean>
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.findKey
+ */
+ findKey(
+ predicate?: ObjectIterator<any, boolean>
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.findKey
+ */
+ findKey(
+ predicate?: string
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.findKey
+ */
+ findKey<TWhere extends Dictionary<any>>(
+ predicate?: TWhere
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ //_.findLastKey
+ interface LoDashStatic {
+ /**
+ * This method is like _.findKey except that it iterates over elements of a collection in the opposite order.
+ *
+ * If a property name is provided for predicate the created _.property style callback returns the property
+ * value of the given element.
+ *
+ * If a value is also provided for thisArg the created _.matchesProperty style callback returns true for
+ * elements that have a matching property value, else false.
+ *
+ * If an object is provided for predicate the created _.matches style callback returns true for elements that
+ * have the properties of the given object, else false.
+ *
+ * @param object The object to search.
+ * @param predicate The function invoked per iteration.
+ * @param thisArg The this binding of predicate.
+ * @return Returns the key of the matched element, else undefined.
+ */
+ findLastKey<TValues, TObject>(
+ object: TObject,
+ predicate?: DictionaryIterator<TValues, boolean>
+ ): string;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TObject>(
+ object: TObject,
+ predicate?: ObjectIterator<any, boolean>
+ ): string;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TObject>(
+ object: TObject,
+ predicate?: string
+ ): string;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TWhere extends Dictionary<any>, TObject>(
+ object: TObject,
+ predicate?: TWhere
+ ): string;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TValues>(
+ predicate?: DictionaryIterator<TValues, boolean>
+ ): string;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey(
+ predicate?: ObjectIterator<any, boolean>
+ ): string;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey(
+ predicate?: string
+ ): string;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TWhere extends Dictionary<any>>(
+ predicate?: TWhere
+ ): string;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TValues>(
+ predicate?: DictionaryIterator<TValues, boolean>
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey(
+ predicate?: ObjectIterator<any, boolean>
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey(
+ predicate?: string
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.findLastKey
+ */
+ findLastKey<TWhere extends Dictionary<any>>(
+ predicate?: TWhere
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ //_.forIn
+ interface LoDashStatic {
+ /**
+ * Iterates over own and inherited enumerable properties of an object invoking iteratee for each property. The
+ * iteratee is bound to thisArg and invoked with three arguments: (value, key, object). Iteratee functions may
+ * exit iteration early by explicitly returning false.
+ *
+ * @param object The object to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns object.
+ */
+ forIn<T>(
+ object: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forIn
+ */
+ forIn<T extends {}>(
+ object: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forIn
+ */
+ forIn<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forIn
+ */
+ forIn<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.forInRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.forIn except that it iterates over properties of object in the opposite order.
+ *
+ * @param object The object to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns object.
+ */
+ forInRight<T>(
+ object: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forInRight
+ */
+ forInRight<T extends {}>(
+ object: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forInRight
+ */
+ forInRight<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forInRight
+ */
+ forInRight<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.forOwn
+ interface LoDashStatic {
+ /**
+ * Iterates over own enumerable properties of an object invoking iteratee for each property. The iteratee is
+ * bound to thisArg and invoked with three arguments: (value, key, object). Iteratee functions may exit
+ * iteration early by explicitly returning false.
+ *
+ * @param object The object to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns object.
+ */
+ forOwn<T>(
+ object: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forOwn
+ */
+ forOwn<T extends {}>(
+ object: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forOwn
+ */
+ forOwn<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forOwn
+ */
+ forOwn<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.forOwnRight
+ interface LoDashStatic {
+ /**
+ * This method is like _.forOwn except that it iterates over properties of object in the opposite order.
+ *
+ * @param object The object to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns object.
+ */
+ forOwnRight<T>(
+ object: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, any>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.forOwnRight
+ */
+ forOwnRight<T extends {}>(
+ object: T,
+ iteratee?: ObjectIterator<any, any>
+ ): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.forOwnRight
+ */
+ forOwnRight<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashImplicitObjectWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.forOwnRight
+ */
+ forOwnRight<TValue>(
+ iteratee?: DictionaryIterator<TValue, any>
+ ): _.LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.functions
+ interface LoDashStatic {
+ /**
+ * Creates an array of function property names from own enumerable properties
+ * of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the new array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = _.constant('a');
+ * this.b = _.constant('b');
+ * }
+ *
+ * Foo.prototype.c = _.constant('c');
+ *
+ * _.functions(new Foo);
+ * // => ['a', 'b']
+ */
+ functions<T extends {}>(object: any): string[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.functions
+ */
+ functions(): _.LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.functions
+ */
+ functions(): _.LoDashExplicitArrayWrapper<string>;
+ }
+
+ //_.functionsIn
+ interface LoDashStatic {
+ /**
+ * Creates an array of function property names from own and inherited
+ * enumerable properties of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the new array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = _.constant('a');
+ * this.b = _.constant('b');
+ * }
+ *
+ * Foo.prototype.c = _.constant('c');
+ *
+ * _.functionsIn(new Foo);
+ * // => ['a', 'b', 'c']
+ */
+ functionsIn<T extends {}>(object: any): string[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.functionsIn
+ */
+ functionsIn(): _.LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.functionsIn
+ */
+ functionsIn(): _.LoDashExplicitArrayWrapper<string>;
+ }
+
+ //_.get
+ interface LoDashStatic {
+ /**
+ * Gets the property value at path of object. If the resolved value is undefined the defaultValue is used
+ * in its place.
+ *
+ * @param object The object to query.
+ * @param path The path of the property to get.
+ * @param defaultValue The value returned if the resolved value is undefined.
+ * @return Returns the resolved value.
+ */
+ get<TObject, TResult>(
+ object: TObject,
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult
+ ): TResult;
+
+ /**
+ * @see _.get
+ */
+ get<TResult>(
+ object: any,
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult
+ ): TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.get
+ */
+ get<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult
+ ): TResult;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.get
+ */
+ get<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.get
+ */
+ get<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult
+ ): TResult;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.get
+ */
+ get<TResultWrapper>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: any
+ ): TResultWrapper;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.get
+ */
+ get<TResultWrapper>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: any
+ ): TResultWrapper;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.get
+ */
+ get<TResultWrapper>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: any
+ ): TResultWrapper;
+ }
+
+ //_.has
+ interface LoDashStatic {
+ /**
+ * Checks if `path` is a direct property of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ * @example
+ *
+ * var object = { 'a': { 'b': { 'c': 3 } } };
+ * var other = _.create({ 'a': _.create({ 'b': _.create({ 'c': 3 }) }) });
+ *
+ * _.has(object, 'a');
+ * // => true
+ *
+ * _.has(object, 'a.b.c');
+ * // => true
+ *
+ * _.has(object, ['a', 'b', 'c']);
+ * // => true
+ *
+ * _.has(other, 'a');
+ * // => false
+ */
+ has<T extends {}>(
+ object: T,
+ path: StringRepresentable|StringRepresentable[]
+ ): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.has
+ */
+ has(path: StringRepresentable|StringRepresentable[]): boolean;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.has
+ */
+ has(path: StringRepresentable|StringRepresentable[]): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.hasIn
+ interface LoDashStatic {
+ /**
+ * Checks if `path` is a direct or inherited property of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ * @example
+ *
+ * var object = _.create({ 'a': _.create({ 'b': _.create({ 'c': 3 }) }) });
+ *
+ * _.hasIn(object, 'a');
+ * // => true
+ *
+ * _.hasIn(object, 'a.b.c');
+ * // => true
+ *
+ * _.hasIn(object, ['a', 'b', 'c']);
+ * // => true
+ *
+ * _.hasIn(object, 'b');
+ * // => false
+ */
+ hasIn<T extends {}>(
+ object: T,
+ path: StringRepresentable|StringRepresentable[]
+ ): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.hasIn
+ */
+ hasIn(path: StringRepresentable|StringRepresentable[]): boolean;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.hasIn
+ */
+ hasIn(path: StringRepresentable|StringRepresentable[]): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.invert
+ interface LoDashStatic {
+ /**
+ * Creates an object composed of the inverted keys and values of object. If object contains duplicate values,
+ * subsequent values overwrite property assignments of previous values unless multiValue is true.
+ *
+ * @param object The object to invert.
+ * @param multiValue Allow multiple values per key.
+ * @return Returns the new inverted object.
+ */
+ invert<T extends {}, TResult extends {}>(
+ object: T,
+ multiValue?: boolean
+ ): TResult;
+
+ /**
+ * @see _.invert
+ */
+ invert<TResult extends {}>(
+ object: Object,
+ multiValue?: boolean
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.invert
+ */
+ invert<TResult extends {}>(multiValue?: boolean): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.invert
+ */
+ invert<TResult extends {}>(multiValue?: boolean): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.inverBy
+ interface InvertByIterator<T> {
+ (value: T): any;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like _.invert except that the inverted object is generated from the results of running each
+ * element of object through iteratee. The corresponding inverted value of each inverted key is an array of
+ * keys responsible for generating the inverted value. The iteratee is invoked with one argument: (value).
+ *
+ * @param object The object to invert.
+ * @param interatee The iteratee invoked per element.
+ * @return Returns the new inverted object.
+ */
+ invertBy(
+ object: Object,
+ interatee?: InvertByIterator<any>|string
+ ): Dictionary<string[]>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<T>(
+ object: _.Dictionary<T>|_.NumericDictionary<T>,
+ interatee?: InvertByIterator<T>|string
+ ): Dictionary<string[]>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<W>(
+ object: Object,
+ interatee?: W
+ ): Dictionary<string[]>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<T, W>(
+ object: _.Dictionary<T>,
+ interatee?: W
+ ): Dictionary<string[]>;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.invertBy
+ */
+ invertBy(
+ interatee?: InvertByIterator<any>
+ ): LoDashImplicitObjectWrapper<Dictionary<string[]>>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.invertBy
+ */
+ invertBy(
+ interatee?: InvertByIterator<T>|string
+ ): LoDashImplicitObjectWrapper<Dictionary<string[]>>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<W>(
+ interatee?: W
+ ): LoDashImplicitObjectWrapper<Dictionary<string[]>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.invertBy
+ */
+ invertBy(
+ interatee?: InvertByIterator<any>|string
+ ): LoDashImplicitObjectWrapper<Dictionary<string[]>>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<W>(
+ interatee?: W
+ ): LoDashImplicitObjectWrapper<Dictionary<string[]>>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.invertBy
+ */
+ invertBy(
+ interatee?: InvertByIterator<any>
+ ): LoDashExplicitObjectWrapper<Dictionary<string[]>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.invertBy
+ */
+ invertBy(
+ interatee?: InvertByIterator<T>|string
+ ): LoDashExplicitObjectWrapper<Dictionary<string[]>>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<W>(
+ interatee?: W
+ ): LoDashExplicitObjectWrapper<Dictionary<string[]>>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.invertBy
+ */
+ invertBy(
+ interatee?: InvertByIterator<any>|string
+ ): LoDashExplicitObjectWrapper<Dictionary<string[]>>;
+
+ /**
+ * @see _.invertBy
+ */
+ invertBy<W>(
+ interatee?: W
+ ): LoDashExplicitObjectWrapper<Dictionary<string[]>>;
+ }
+
+ //_.keys
+ interface LoDashStatic {
+ /**
+ * Creates an array of the own enumerable property names of object.
+ *
+ * Note: Non-object values are coerced to objects. See the ES spec for more details.
+ *
+ * @param object The object to query.
+ * @return Returns the array of property names.
+ */
+ keys(object?: any): string[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.keys
+ */
+ keys(): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.keys
+ */
+ keys(): LoDashExplicitArrayWrapper<string>;
+ }
+
+ //_.keysIn
+ interface LoDashStatic {
+ /**
+ * Creates an array of the own and inherited enumerable property names of object.
+ *
+ * Note: Non-object values are coerced to objects.
+ *
+ * @param object The object to query.
+ * @return An array of property names.
+ */
+ keysIn(object?: any): string[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.keysIn
+ */
+ keysIn(): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.keysIn
+ */
+ keysIn(): LoDashExplicitArrayWrapper<string>;
+ }
+
+ //_.mapKeys
+ interface LoDashStatic {
+ /**
+ * The opposite of _.mapValues; this method creates an object with the same values as object and keys generated
+ * by running each own enumerable property of object through iteratee.
+ *
+ * @param object The object to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the new mapped object.
+ */
+ mapKeys<T, TKey>(
+ object: List<T>,
+ iteratee?: ListIterator<T, TKey>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<T, TKey>(
+ object: Dictionary<T>,
+ iteratee?: DictionaryIterator<T, TKey>
+ ): Dictionary<T>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<T, TObject extends {}>(
+ object: List<T>|Dictionary<T>,
+ iteratee?: TObject
+ ): Dictionary<T>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<T>(
+ object: List<T>|Dictionary<T>,
+ iteratee?: string
+ ): Dictionary<T>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TKey>(
+ iteratee?: ListIterator<T, TKey>
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TResult, TKey>(
+ iteratee?: ListIterator<TResult, TKey>|DictionaryIterator<TResult, TKey>
+ ): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TResult, TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TResult>(
+ iteratee?: string
+ ): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TKey>(
+ iteratee?: ListIterator<T, TKey>
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<T>>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TResult, TKey>(
+ iteratee?: ListIterator<TResult, TKey>|DictionaryIterator<TResult, TKey>
+ ): LoDashExplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TResult, TObject extends {}>(
+ iteratee?: TObject
+ ): LoDashExplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapKeys
+ */
+ mapKeys<TResult>(
+ iteratee?: string
+ ): LoDashExplicitObjectWrapper<Dictionary<TResult>>;
+ }
+
+ //_.mapValues
+ interface LoDashStatic {
+ /**
+ * Creates an object with the same keys as object and values generated by running each own
+ * enumerable property of object through iteratee. The iteratee function is bound to thisArg
+ * and invoked with three arguments: (value, key, object).
+ *
+ * If a property name is provided iteratee the created "_.property" style callback returns
+ * the property value of the given element.
+ *
+ * If a value is also provided for thisArg the creted "_.matchesProperty" style callback returns
+ * true for elements that have a matching property value, else false;.
+ *
+ * If an object is provided for iteratee the created "_.matches" style callback returns true
+ * for elements that have the properties of the given object, else false.
+ *
+ * @param {Object} object The object to iterate over.
+ * @param {Function|Object|string} [iteratee=_.identity] The function invoked per iteration.
+ * @param {Object} [thisArg] The `this` binding of `iteratee`.
+ * @return {Object} Returns the new mapped object.
+ */
+ mapValues<T, TResult>(obj: Dictionary<T>, callback: ObjectIterator<T, TResult>): Dictionary<TResult>;
+ mapValues<T>(obj: Dictionary<T>, where: Dictionary<T>): Dictionary<boolean>;
+ mapValues<T, TMapped>(obj: T, pluck: string): TMapped;
+ mapValues<T>(obj: T, callback: ObjectIterator<any, any>): T;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.mapValues
+ * TValue is the type of the property values of T.
+ * TResult is the type output by the ObjectIterator function
+ */
+ mapValues<TValue, TResult>(callback: ObjectIterator<TValue, TResult>): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapValues
+ * TResult is the type of the property specified by pluck.
+ * T should be a Dictionary<Dictionary<TResult>>
+ */
+ mapValues<TResult>(pluck: string): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapValues
+ * TResult is the type of the properties of each object in the values of T
+ * T should be a Dictionary<Dictionary<TResult>>
+ */
+ mapValues<TResult>(where: Dictionary<TResult>): LoDashImplicitArrayWrapper<boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.mapValues
+ * TValue is the type of the property values of T.
+ * TResult is the type output by the ObjectIterator function
+ */
+ mapValues<TValue, TResult>(callback: ObjectIterator<TValue, TResult>): LoDashExplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapValues
+ * TResult is the type of the property specified by pluck.
+ * T should be a Dictionary<Dictionary<TResult>>
+ */
+ mapValues<TResult>(pluck: string): LoDashExplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.mapValues
+ * TResult is the type of the properties of each object in the values of T
+ * T should be a Dictionary<Dictionary<TResult>>
+ */
+ mapValues<TResult>(where: Dictionary<TResult>): LoDashExplicitObjectWrapper<boolean>;
+ }
+
+ //_.merge
+ interface LoDashStatic {
+ /**
+ * Recursively merges own and inherited enumerable properties of source
+ * objects into the destination object, skipping source properties that resolve
+ * to `undefined`. Array and plain object properties are merged recursively.
+ * Other objects and value types are overridden by assignment. Source objects
+ * are applied from left to right. Subsequent sources overwrite property
+ * assignments of previous sources.
+ *
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * var users = {
+ * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
+ * };
+ *
+ * var ages = {
+ * 'data': [{ 'age': 36 }, { 'age': 40 }]
+ * };
+ *
+ * _.merge(users, ages);
+ * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
+ */
+ merge<TObject, TSource>(
+ object: TObject,
+ source: TSource
+ ): TObject & TSource;
+
+ /**
+ * @see _.merge
+ */
+ merge<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see _.merge
+ */
+ merge<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see _.merge
+ */
+ merge<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.merge
+ */
+ merge<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.merge
+ */
+ merge<TSource>(
+ source: TSource
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TResult>(
+ ...otherArgs: any[]
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.merge
+ */
+ merge<TSource>(
+ source: TSource
+ ): LoDashExplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TSource1, TSource2, TSource3, TSource4>(
+ ): LoDashExplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.merge
+ */
+ merge<TResult>(
+ ...otherArgs: any[]
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.mergeWith
+ interface MergeWithCustomizer {
+ (value: any, srcValue: any, key?: string, object?: Object, source?: Object): any;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like `_.merge` except that it accepts `customizer` which
+ * is invoked to produce the merged values of the destination and source
+ * properties. If `customizer` returns `undefined` merging is handled by the
+ * method instead. The `customizer` is invoked with seven arguments:
+ * (objValue, srcValue, key, object, source, stack).
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} customizer The function to customize assigned values.
+ * @returns {Object} Returns `object`.
+ * @example
+ *
+ * function customizer(objValue, srcValue) {
+ * if (_.isArray(objValue)) {
+ * return objValue.concat(srcValue);
+ * }
+ * }
+ *
+ * var object = {
+ * 'fruits': ['apple'],
+ * 'vegetables': ['beet']
+ * };
+ *
+ * var other = {
+ * 'fruits': ['banana'],
+ * 'vegetables': ['carrot']
+ * };
+ *
+ * _.merge(object, other, customizer);
+ * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
+ */
+ mergeWith<TObject, TSource>(
+ object: TObject,
+ source: TSource,
+ customizer: MergeWithCustomizer
+ ): TObject & TSource;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TObject, TSource1, TSource2>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ customizer: MergeWithCustomizer
+ ): TObject & TSource1 & TSource2;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TObject, TSource1, TSource2, TSource3>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: MergeWithCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TObject, TSource1, TSource2, TSource3, TSource4>(
+ object: TObject,
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: MergeWithCustomizer
+ ): TObject & TSource1 & TSource2 & TSource3 & TSource4;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TResult>(
+ object: any,
+ ...otherArgs: any[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TSource>(
+ source: TSource,
+ customizer: MergeWithCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource>;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TSource1, TSource2>(
+ source1: TSource1,
+ source2: TSource2,
+ customizer: MergeWithCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2>;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TSource1, TSource2, TSource3>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ customizer: MergeWithCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3>;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TSource1, TSource2, TSource3, TSource4>(
+ source1: TSource1,
+ source2: TSource2,
+ source3: TSource3,
+ source4: TSource4,
+ customizer: MergeWithCustomizer
+ ): LoDashImplicitObjectWrapper<T & TSource1 & TSource2 & TSource3 & TSource4>;
+
+ /**
+ * @see _.mergeWith
+ */
+ mergeWith<TResult>(
+ ...otherArgs: any[]
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ //_.omit
+ interface LoDashStatic {
+ /**
+ * The opposite of `_.pick`; this method creates an object composed of the
+ * own and inherited enumerable properties of `object` that are not omitted.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {...(string|string[])} [props] The property names to omit, specified
+ * individually or in arrays..
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.omit(object, ['a', 'c']);
+ * // => { 'b': '2' }
+ */
+
+ omit<TResult extends {}, T extends {}>(
+ object: T,
+ ...predicate: (StringRepresentable|StringRepresentable[])[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+
+ /**
+ * @see _.omit
+ */
+ omit<TResult extends {}>(
+ ...predicate: (StringRepresentable|StringRepresentable[])[]
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+
+ /**
+ * @see _.omit
+ */
+ omit<TResult extends {}>(
+ ...predicate: (StringRepresentable|StringRepresentable[])[]
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.omitBy
+ interface LoDashStatic {
+ /**
+ * The opposite of `_.pickBy`; this method creates an object composed of the
+ * own and inherited enumerable properties of `object` that `predicate`
+ * doesn't return truthy for.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked per property.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.omitBy(object, _.isNumber);
+ * // => { 'b': '2' }
+ */
+ omitBy<TResult extends {}, T extends {}>(
+ object: T,
+ predicate: ObjectIterator<any, boolean>
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.omitBy
+ */
+ omitBy<TResult extends {}>(
+ predicate: ObjectIterator<any, boolean>
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.omitBy
+ */
+ omitBy<TResult extends {}>(
+ predicate: ObjectIterator<any, boolean>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.pick
+ interface LoDashStatic {
+ /**
+ * Creates an object composed of the picked `object` properties.
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {...(string|string[])} [props] The property names to pick, specified
+ * individually or in arrays.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.pick(object, ['a', 'c']);
+ * // => { 'a': 1, 'c': 3 }
+ */
+ pick<TResult extends {}, T extends {}>(
+ object: T,
+ ...predicate: (StringRepresentable|StringRepresentable[])[]
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.pick
+ */
+ pick<TResult extends {}>(
+ ...predicate: (StringRepresentable|StringRepresentable[])[]
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.pick
+ */
+ pick<TResult extends {}>(
+ ...predicate: (StringRepresentable|StringRepresentable[])[]
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.pickBy
+ interface LoDashStatic {
+ /**
+ * Creates an object composed of the `object` properties `predicate` returns
+ * truthy for. The predicate is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function|Object|string} [predicate=_.identity] The function invoked per property.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
+ *
+ * _.pickBy(object, _.isNumber);
+ * // => { 'a': 1, 'c': 3 }
+ */
+ pickBy<TResult extends {}, T extends {}>(
+ object: T,
+ predicate?: ObjectIterator<any, boolean>
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.pickBy
+ */
+ pickBy<TResult extends {}>(
+ predicate?: ObjectIterator<any, boolean>
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.pickBy
+ */
+ pickBy<TResult extends {}>(
+ predicate?: ObjectIterator<any, boolean>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.result
+ interface LoDashStatic {
+ /**
+ * This method is like _.get except that if the resolved value is a function it’s invoked with the this binding
+ * of its parent object and its result is returned.
+ *
+ * @param object The object to query.
+ * @param path The path of the property to resolve.
+ * @param defaultValue The value returned if the resolved value is undefined.
+ * @return Returns the resolved value.
+ */
+ result<TObject, TResult>(
+ object: TObject,
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult|((...args: any[]) => TResult)
+ ): TResult;
+
+ /**
+ * @see _.result
+ */
+ result<TResult>(
+ object: any,
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult|((...args: any[]) => TResult)
+ ): TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.result
+ */
+ result<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult|((...args: any[]) => TResult)
+ ): TResult;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.result
+ */
+ result<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult|((...args: any[]) => TResult)
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.result
+ */
+ result<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: TResult|((...args: any[]) => TResult)
+ ): TResult;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.result
+ */
+ result<TResultWrapper>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: any
+ ): TResultWrapper;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.result
+ */
+ result<TResultWrapper>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: any
+ ): TResultWrapper;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.result
+ */
+ result<TResultWrapper>(
+ path: StringRepresentable|StringRepresentable[],
+ defaultValue?: any
+ ): TResultWrapper;
+ }
+
+ //_.set
+ interface LoDashStatic {
+ /**
+ * Sets the value at path of object. If a portion of path doesn’t exist it’s created. Arrays are created for
+ * missing index properties while objects are created for all other missing properties. Use _.setWith to
+ * customize path creation.
+ *
+ * @param object The object to modify.
+ * @param path The path of the property to set.
+ * @param value The value to set.
+ * @return Returns object.
+ */
+ set<TResult>(
+ object: Object,
+ path: StringRepresentable|StringRepresentable[],
+ value: any
+ ): TResult;
+
+ /**
+ * @see _.set
+ */
+ set<V, TResult>(
+ object: Object,
+ path: StringRepresentable|StringRepresentable[],
+ value: V
+ ): TResult;
+
+ /**
+ * @see _.set
+ */
+ set<O, V, TResult>(
+ object: O,
+ path: StringRepresentable|StringRepresentable[],
+ value: V
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.set
+ */
+ set<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: any
+ ): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.set
+ */
+ set<V, TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: V
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.set
+ */
+ set<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: any
+ ): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.set
+ */
+ set<V, TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: V
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.setWith
+ interface SetWithCustomizer<T> {
+ (nsValue: any, key: string, nsObject: T): any;
+ }
+
+ interface LoDashStatic {
+ /**
+ * This method is like _.set except that it accepts customizer which is invoked to produce the objects of
+ * path. If customizer returns undefined path creation is handled by the method instead. The customizer is
+ * invoked with three arguments: (nsValue, key, nsObject).
+ *
+ * @param object The object to modify.
+ * @param path The path of the property to set.
+ * @param value The value to set.
+ * @parem customizer The function to customize assigned values.
+ * @return Returns object.
+ */
+ setWith<TResult>(
+ object: Object,
+ path: StringRepresentable|StringRepresentable[],
+ value: any,
+ customizer?: SetWithCustomizer<Object>
+ ): TResult;
+
+ /**
+ * @see _.setWith
+ */
+ setWith<V, TResult>(
+ object: Object,
+ path: StringRepresentable|StringRepresentable[],
+ value: V,
+ customizer?: SetWithCustomizer<Object>
+ ): TResult;
+
+ /**
+ * @see _.setWith
+ */
+ setWith<O, V, TResult>(
+ object: O,
+ path: StringRepresentable|StringRepresentable[],
+ value: V,
+ customizer?: SetWithCustomizer<O>
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.setWith
+ */
+ setWith<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: any,
+ customizer?: SetWithCustomizer<T>
+ ): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.setWith
+ */
+ setWith<V, TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: V,
+ customizer?: SetWithCustomizer<T>
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.setWith
+ */
+ setWith<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: any,
+ customizer?: SetWithCustomizer<T>
+ ): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.setWith
+ */
+ setWith<V, TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ value: V,
+ customizer?: SetWithCustomizer<T>
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.toPairs
+ interface LoDashStatic {
+ /**
+ * Creates an array of own enumerable key-value pairs for object.
+ *
+ * @param object The object to query.
+ * @return Returns the new array of key-value pairs.
+ */
+ toPairs<T extends {}>(object?: T): any[][];
+
+ toPairs<T extends {}, TResult>(object?: T): TResult[][];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.toPairs
+ */
+ toPairs<TResult>(): LoDashImplicitArrayWrapper<TResult[]>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.toPairs
+ */
+ toPairs<TResult>(): LoDashExplicitArrayWrapper<TResult[]>;
+ }
+
+ //_.toPairsIn
+ interface LoDashStatic {
+ /**
+ * Creates an array of own and inherited enumerable key-value pairs for object.
+ *
+ * @param object The object to query.
+ * @return Returns the new array of key-value pairs.
+ */
+ toPairsIn<T extends {}>(object?: T): any[][];
+
+ toPairsIn<T extends {}, TResult>(object?: T): TResult[][];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.toPairsIn
+ */
+ toPairsIn<TResult>(): LoDashImplicitArrayWrapper<TResult[]>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.toPairsIn
+ */
+ toPairsIn<TResult>(): LoDashExplicitArrayWrapper<TResult[]>;
+ }
+
+ //_.transform
+ interface LoDashStatic {
+ /**
+ * An alternative to _.reduce; this method transforms object to a new accumulator object which is the result of
+ * running each of its own enumerable properties through iteratee, with each invocation potentially mutating
+ * the accumulator object. The iteratee is bound to thisArg and invoked with four arguments: (accumulator,
+ * value, key, object). Iteratee functions may exit iteration early by explicitly returning false.
+ *
+ * @param object The object to iterate over.
+ * @param iteratee The function invoked per iteration.
+ * @param accumulator The custom accumulator value.
+ * @param thisArg The this binding of iteratee.
+ * @return Returns the accumulated value.
+ */
+ transform<T, TResult>(
+ object: T[],
+ iteratee?: MemoVoidArrayIterator<T, TResult[]>,
+ accumulator?: TResult[]
+ ): TResult[];
+
+ /**
+ * @see _.transform
+ */
+ transform<T, TResult>(
+ object: T[],
+ iteratee?: MemoVoidArrayIterator<T, Dictionary<TResult>>,
+ accumulator?: Dictionary<TResult>
+ ): Dictionary<TResult>;
+
+ /**
+ * @see _.transform
+ */
+ transform<T, TResult>(
+ object: Dictionary<T>,
+ iteratee?: MemoVoidDictionaryIterator<T, Dictionary<TResult>>,
+ accumulator?: Dictionary<TResult>
+ ): Dictionary<TResult>;
+
+ /**
+ * @see _.transform
+ */
+ transform<T, TResult>(
+ object: Dictionary<T>,
+ iteratee?: MemoVoidDictionaryIterator<T, TResult[]>,
+ accumulator?: TResult[]
+ ): TResult[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.transform
+ */
+ transform<TResult>(
+ iteratee?: MemoVoidArrayIterator<T, TResult[]>,
+ accumulator?: TResult[]
+ ): LoDashImplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.transform
+ */
+ transform<TResult>(
+ iteratee?: MemoVoidArrayIterator<T, Dictionary<TResult>>,
+ accumulator?: Dictionary<TResult>
+ ): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.transform
+ */
+ transform<T, TResult>(
+ iteratee?: MemoVoidDictionaryIterator<T, Dictionary<TResult>>,
+ accumulator?: Dictionary<TResult>
+ ): LoDashImplicitObjectWrapper<Dictionary<TResult>>;
+
+ /**
+ * @see _.transform
+ */
+ transform<T, TResult>(
+ iteratee?: MemoVoidDictionaryIterator<T, TResult[]>,
+ accumulator?: TResult[]
+ ): LoDashImplicitArrayWrapper<TResult>;
+ }
+
+ //_.unset
+ interface LoDashStatic {
+ /**
+ * Removes the property at path of object.
+ *
+ * Note: This method mutates object.
+ *
+ * @param object The object to modify.
+ * @param path The path of the property to unset.
+ * @return Returns true if the property is deleted, else false.
+ */
+ unset<T>(
+ object: T,
+ path: StringRepresentable|StringRepresentable[]
+ ): boolean;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.unset
+ */
+ unset(path: StringRepresentable|StringRepresentable[]): LoDashImplicitWrapper<boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.unset
+ */
+ unset(path: StringRepresentable|StringRepresentable[]): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.update
+ interface LoDashStatic {
+ /**
+ * This method is like _.set except that accepts updater to produce the value to set. Use _.updateWith to
+ * customize path creation. The updater is invoked with one argument: (value).
+ *
+ * @param object The object to modify.
+ * @param path The path of the property to set.
+ * @param updater The function to produce the updated value.
+ * @return Returns object.
+ */
+ update<TResult>(
+ object: Object,
+ path: StringRepresentable|StringRepresentable[],
+ updater: Function
+ ): TResult;
+
+ /**
+ * @see _.update
+ */
+ update<U extends Function, TResult>(
+ object: Object,
+ path: StringRepresentable|StringRepresentable[],
+ updater: U
+ ): TResult;
+
+ /**
+ * @see _.update
+ */
+ update<O extends {}, TResult>(
+ object: O,
+ path: StringRepresentable|StringRepresentable[],
+ updater: Function
+ ): TResult;
+
+ /**
+ * @see _.update
+ */
+ update<O, U extends Function, TResult>(
+ object: O,
+ path: StringRepresentable|StringRepresentable[],
+ updater: U
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.update
+ */
+ update<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ updater: any
+ ): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.update
+ */
+ update<U extends Function, TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ updater: U
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.update
+ */
+ update<TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ updater: any
+ ): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.update
+ */
+ update<U extends Function, TResult>(
+ path: StringRepresentable|StringRepresentable[],
+ updater: U
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.values
+ interface LoDashStatic {
+ /**
+ * Creates an array of the own enumerable property values of object.
+ *
+ * @param object The object to query.
+ * @return Returns an array of property values.
+ */
+ values<T>(object?: Dictionary<T>): T[];
+
+ /**
+ * @see _.values
+ */
+ values<T>(object?: any): T[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.values
+ */
+ values<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.values
+ */
+ values<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ //_.valuesIn
+ interface LoDashStatic {
+ /**
+ * Creates an array of the own and inherited enumerable property values of object.
+ *
+ * @param object The object to query.
+ * @return Returns the array of property values.
+ */
+ valuesIn<T>(object?: Dictionary<T>): T[];
+
+ /**
+ * @see _.valuesIn
+ */
+ valuesIn<T>(object?: any): T[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.valuesIn
+ */
+ valuesIn<T>(): LoDashImplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.valuesIn
+ */
+ valuesIn<T>(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ /**********
+ * String *
+ **********/
+
+ //_.camelCase
+ interface LoDashStatic {
+ /**
+ * Converts string to camel case.
+ *
+ * @param string The string to convert.
+ * @return Returns the camel cased string.
+ */
+ camelCase(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.camelCase
+ */
+ camelCase(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.camelCase
+ */
+ camelCase(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.capitalize
+ interface LoDashStatic {
+ /**
+ * Converts the first character of string to upper case and the remaining to lower case.
+ *
+ * @param string The string to capitalize.
+ * @return Returns the capitalized string.
+ */
+ capitalize(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.capitalize
+ */
+ capitalize(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.capitalize
+ */
+ capitalize(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.deburr
+ interface LoDashStatic {
+ /**
+ * Deburrs string by converting latin-1 supplementary letters to basic latin letters and removing combining
+ * diacritical marks.
+ *
+ * @param string The string to deburr.
+ * @return Returns the deburred string.
+ */
+ deburr(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.deburr
+ */
+ deburr(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.deburr
+ */
+ deburr(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.endsWith
+ interface LoDashStatic {
+ /**
+ * Checks if string ends with the given target string.
+ *
+ * @param string The string to search.
+ * @param target The string to search for.
+ * @param position The position to search from.
+ * @return Returns true if string ends with target, else false.
+ */
+ endsWith(
+ string?: string,
+ target?: string,
+ position?: number
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.endsWith
+ */
+ endsWith(
+ target?: string,
+ position?: number
+ ): boolean;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.endsWith
+ */
+ endsWith(
+ target?: string,
+ position?: number
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ // _.escape
+ interface LoDashStatic {
+ /**
+ * Converts the characters "&", "<", ">", '"', "'", and "`" in string to their corresponding HTML entities.
+ *
+ * Note: No other characters are escaped. To escape additional characters use a third-party library like he.
+ *
+ * hough the ">" character is escaped for symmetry, characters like ">" and "/" don’t need escaping in HTML
+ * and have no special meaning unless they're part of a tag or unquoted attribute value. See Mathias Bynens’s
+ * article (under "semi-related fun fact") for more details.
+ *
+ * Backticks are escaped because in IE < 9, they can break out of attribute values or HTML comments. See #59,
+ * #102, #108, and #133 of the HTML5 Security Cheatsheet for more details.
+ *
+ * When working with HTML you should always quote attribute values to reduce XSS vectors.
+ *
+ * @param string The string to escape.
+ * @return Returns the escaped string.
+ */
+ escape(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.escape
+ */
+ escape(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.escape
+ */
+ escape(): LoDashExplicitWrapper<string>;
+ }
+
+ // _.escapeRegExp
+ interface LoDashStatic {
+ /**
+ * Escapes the RegExp special characters "^", "$", "\", ".", "*", "+", "?", "(", ")", "[", "]",
+ * "{", "}", and "|" in string.
+ *
+ * @param string The string to escape.
+ * @return Returns the escaped string.
+ */
+ escapeRegExp(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.escapeRegExp
+ */
+ escapeRegExp(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.escapeRegExp
+ */
+ escapeRegExp(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.kebabCase
+ interface LoDashStatic {
+ /**
+ * Converts string to kebab case.
+ *
+ * @param string The string to convert.
+ * @return Returns the kebab cased string.
+ */
+ kebabCase(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.kebabCase
+ */
+ kebabCase(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.kebabCase
+ */
+ kebabCase(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.lowerCase
+ interface LoDashStatic {
+ /**
+ * Converts `string`, as space separated words, to lower case.
+ *
+ * @param string The string to convert.
+ * @return Returns the lower cased string.
+ */
+ lowerCase(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.lowerCase
+ */
+ lowerCase(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.lowerCase
+ */
+ lowerCase(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.lowerFirst
+ interface LoDashStatic {
+ /**
+ * Converts the first character of `string` to lower case.
+ *
+ * @param string The string to convert.
+ * @return Returns the converted string.
+ */
+ lowerFirst(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.lowerFirst
+ */
+ lowerFirst(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.lowerFirst
+ */
+ lowerFirst(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.pad
+ interface LoDashStatic {
+ /**
+ * Pads string on the left and right sides if it’s shorter than length. Padding characters are truncated if
+ * they can’t be evenly divided by length.
+ *
+ * @param string The string to pad.
+ * @param length The padding length.
+ * @param chars The string used as padding.
+ * @return Returns the padded string.
+ */
+ pad(
+ string?: string,
+ length?: number,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.pad
+ */
+ pad(
+ length?: number,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.pad
+ */
+ pad(
+ length?: number,
+ chars?: string
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ //_.padEnd
+ interface LoDashStatic {
+ /**
+ * Pads string on the right side if it’s shorter than length. Padding characters are truncated if they exceed
+ * length.
+ *
+ * @param string The string to pad.
+ * @param length The padding length.
+ * @param chars The string used as padding.
+ * @return Returns the padded string.
+ */
+ padEnd(
+ string?: string,
+ length?: number,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.padEnd
+ */
+ padEnd(
+ length?: number,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.padEnd
+ */
+ padEnd(
+ length?: number,
+ chars?: string
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ //_.padStart
+ interface LoDashStatic {
+ /**
+ * Pads string on the left side if it’s shorter than length. Padding characters are truncated if they exceed
+ * length.
+ *
+ * @param string The string to pad.
+ * @param length The padding length.
+ * @param chars The string used as padding.
+ * @return Returns the padded string.
+ */
+ padStart(
+ string?: string,
+ length?: number,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.padStart
+ */
+ padStart(
+ length?: number,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.padStart
+ */
+ padStart(
+ length?: number,
+ chars?: string
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ //_.parseInt
+ interface LoDashStatic {
+ /**
+ * Converts string to an integer of the specified radix. If radix is undefined or 0, a radix of 10 is used
+ * unless value is a hexadecimal, in which case a radix of 16 is used.
+ *
+ * Note: This method aligns with the ES5 implementation of parseInt.
+ *
+ * @param string The string to convert.
+ * @param radix The radix to interpret value by.
+ * @return Returns the converted integer.
+ */
+ parseInt(
+ string: string,
+ radix?: number
+ ): number;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.parseInt
+ */
+ parseInt(radix?: number): number;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.parseInt
+ */
+ parseInt(radix?: number): LoDashExplicitWrapper<number>;
+ }
+
+ //_.repeat
+ interface LoDashStatic {
+ /**
+ * Repeats the given string n times.
+ *
+ * @param string The string to repeat.
+ * @param n The number of times to repeat the string.
+ * @return Returns the repeated string.
+ */
+ repeat(
+ string?: string,
+ n?: number
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.repeat
+ */
+ repeat(n?: number): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.repeat
+ */
+ repeat(n?: number): LoDashExplicitWrapper<string>;
+ }
+
+ //_.replace
+ interface LoDashStatic {
+ /**
+ * Replaces matches for pattern in string with replacement.
+ *
+ * Note: This method is based on String#replace.
+ *
+ * @param string
+ * @param pattern
+ * @param replacement
+ * @return Returns the modified string.
+ */
+ replace(
+ string: string,
+ pattern: RegExp|string,
+ replacement: Function|string
+ ): string;
+
+ /**
+ * @see _.replace
+ */
+ replace(
+ pattern?: RegExp|string,
+ replacement?: Function|string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.replace
+ */
+ replace(
+ pattern?: RegExp|string,
+ replacement?: Function|string
+ ): string;
+
+ /**
+ * @see _.replace
+ */
+ replace(
+ replacement?: Function|string
+ ): string;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.replace
+ */
+ replace(
+ pattern?: RegExp|string,
+ replacement?: Function|string
+ ): string;
+
+ /**
+ * @see _.replace
+ */
+ replace(
+ replacement?: Function|string
+ ): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.replace
+ */
+ replace(
+ pattern?: RegExp|string,
+ replacement?: Function|string
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.replace
+ */
+ replace(
+ replacement?: Function|string
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.replace
+ */
+ replace(
+ pattern?: RegExp|string,
+ replacement?: Function|string
+ ): LoDashExplicitWrapper<string>;
+
+ /**
+ * @see _.replace
+ */
+ replace(
+ replacement?: Function|string
+ ): LoDashExplicitWrapper<string>;
+ }
+
+ //_.snakeCase
+ interface LoDashStatic {
+ /**
+ * Converts string to snake case.
+ *
+ * @param string The string to convert.
+ * @return Returns the snake cased string.
+ */
+ snakeCase(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.snakeCase
+ */
+ snakeCase(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.snakeCase
+ */
+ snakeCase(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.split
+ interface LoDashStatic {
+ /**
+ * Splits string by separator.
+ *
+ * Note: This method is based on String#split.
+ *
+ * @param string
+ * @param separator
+ * @param limit
+ * @return Returns the new array of string segments.
+ */
+ split(
+ string: string,
+ separator?: RegExp|string,
+ limit?: number
+ ): string[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.split
+ */
+ split(
+ separator?: RegExp|string,
+ limit?: number
+ ): LoDashImplicitArrayWrapper<string>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.split
+ */
+ split(
+ separator?: RegExp|string,
+ limit?: number
+ ): LoDashExplicitArrayWrapper<string>;
+ }
+
+ //_.startCase
+ interface LoDashStatic {
+ /**
+ * Converts string to start case.
+ *
+ * @param string The string to convert.
+ * @return Returns the start cased string.
+ */
+ startCase(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.startCase
+ */
+ startCase(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.startCase
+ */
+ startCase(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.startsWith
+ interface LoDashStatic {
+ /**
+ * Checks if string starts with the given target string.
+ *
+ * @param string The string to search.
+ * @param target The string to search for.
+ * @param position The position to search from.
+ * @return Returns true if string starts with target, else false.
+ */
+ startsWith(
+ string?: string,
+ target?: string,
+ position?: number
+ ): boolean;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.startsWith
+ */
+ startsWith(
+ target?: string,
+ position?: number
+ ): boolean;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.startsWith
+ */
+ startsWith(
+ target?: string,
+ position?: number
+ ): LoDashExplicitWrapper<boolean>;
+ }
+
+ //_.template
+ interface TemplateOptions extends TemplateSettings {
+ /**
+ * The sourceURL of the template's compiled source.
+ */
+ sourceURL?: string;
+ }
+
+ interface TemplateExecutor {
+ (data?: Object): string;
+ source: string;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Creates a compiled template function that can interpolate data properties in "interpolate" delimiters,
+ * HTML-escape interpolated data properties in "escape" delimiters, and execute JavaScript in "evaluate"
+ * delimiters. Data properties may be accessed as free variables in the template. If a setting object is
+ * provided it takes precedence over _.templateSettings values.
+ *
+ * Note: In the development build _.template utilizes
+ * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) for easier
+ * debugging.
+ *
+ * For more information on precompiling templates see
+ * [lodash's custom builds documentation](https://lodash.com/custom-builds).
+ *
+ * For more information on Chrome extension sandboxes see
+ * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
+ *
+ * @param string The template string.
+ * @param options The options object.
+ * @param options.escape The HTML "escape" delimiter.
+ * @param options.evaluate The "evaluate" delimiter.
+ * @param options.imports An object to import into the template as free variables.
+ * @param options.interpolate The "interpolate" delimiter.
+ * @param options.sourceURL The sourceURL of the template's compiled source.
+ * @param options.variable The data object variable name.
+ * @return Returns the compiled template function.
+ */
+ template(
+ string: string,
+ options?: TemplateOptions
+ ): TemplateExecutor;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.template
+ */
+ template(options?: TemplateOptions): TemplateExecutor;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.template
+ */
+ template(options?: TemplateOptions): LoDashExplicitObjectWrapper<TemplateExecutor>;
+ }
+
+ //_.toLower
+ interface LoDashStatic {
+ /**
+ * Converts `string`, as a whole, to lower case.
+ *
+ * @param string The string to convert.
+ * @return Returns the lower cased string.
+ */
+ toLower(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.toLower
+ */
+ toLower(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.toLower
+ */
+ toLower(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.toUpper
+ interface LoDashStatic {
+ /**
+ * Converts `string`, as a whole, to upper case.
+ *
+ * @param string The string to convert.
+ * @return Returns the upper cased string.
+ */
+ toUpper(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.toUpper
+ */
+ toUpper(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.toUpper
+ */
+ toUpper(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.trim
+ interface LoDashStatic {
+ /**
+ * Removes leading and trailing whitespace or specified characters from string.
+ *
+ * @param string The string to trim.
+ * @param chars The characters to trim.
+ * @return Returns the trimmed string.
+ */
+ trim(
+ string?: string,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.trim
+ */
+ trim(chars?: string): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.trim
+ */
+ trim(chars?: string): LoDashExplicitWrapper<string>;
+ }
+
+ //_.trimEnd
+ interface LoDashStatic {
+ /**
+ * Removes trailing whitespace or specified characters from string.
+ *
+ * @param string The string to trim.
+ * @param chars The characters to trim.
+ * @return Returns the trimmed string.
+ */
+ trimEnd(
+ string?: string,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.trimEnd
+ */
+ trimEnd(chars?: string): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.trimEnd
+ */
+ trimEnd(chars?: string): LoDashExplicitWrapper<string>;
+ }
+
+ //_.trimStart
+ interface LoDashStatic {
+ /**
+ * Removes leading whitespace or specified characters from string.
+ *
+ * @param string The string to trim.
+ * @param chars The characters to trim.
+ * @return Returns the trimmed string.
+ */
+ trimStart(
+ string?: string,
+ chars?: string
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.trimStart
+ */
+ trimStart(chars?: string): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.trimStart
+ */
+ trimStart(chars?: string): LoDashExplicitWrapper<string>;
+ }
+
+ //_.truncate
+ interface TruncateOptions {
+ /** The maximum string length. */
+ length?: number;
+ /** The string to indicate text is omitted. */
+ omission?: string;
+ /** The separator pattern to truncate to. */
+ separator?: string|RegExp;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Truncates string if it’s longer than the given maximum string length. The last characters of the truncated
+ * string are replaced with the omission string which defaults to "…".
+ *
+ * @param string The string to truncate.
+ * @param options The options object or maximum string length.
+ * @return Returns the truncated string.
+ */
+ truncate(
+ string?: string,
+ options?: TruncateOptions
+ ): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.truncate
+ */
+ truncate(options?: TruncateOptions): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.truncate
+ */
+ truncate(options?: TruncateOptions): LoDashExplicitWrapper<string>;
+ }
+
+ //_.unescape
+ interface LoDashStatic {
+ /**
+ * The inverse of _.escape; this method converts the HTML entities &amp;, &lt;, &gt;, &quot;, &#39;, and &#96;
+ * in string to their corresponding characters.
+ *
+ * Note: No other HTML entities are unescaped. To unescape additional HTML entities use a third-party library
+ * like he.
+ *
+ * @param string The string to unescape.
+ * @return Returns the unescaped string.
+ */
+ unescape(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.unescape
+ */
+ unescape(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.unescape
+ */
+ unescape(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.upperCase
+ interface LoDashStatic {
+ /**
+ * Converts `string`, as space separated words, to upper case.
+ *
+ * @param string The string to convert.
+ * @return Returns the upper cased string.
+ */
+ upperCase(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.upperCase
+ */
+ upperCase(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.upperCase
+ */
+ upperCase(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.upperFirst
+ interface LoDashStatic {
+ /**
+ * Converts the first character of `string` to upper case.
+ *
+ * @param string The string to convert.
+ * @return Returns the converted string.
+ */
+ upperFirst(string?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.upperFirst
+ */
+ upperFirst(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.upperFirst
+ */
+ upperFirst(): LoDashExplicitWrapper<string>;
+ }
+
+ //_.words
+ interface LoDashStatic {
+ /**
+ * Splits `string` into an array of its words.
+ *
+ * @param string The string to inspect.
+ * @param pattern The pattern to match words.
+ * @return Returns the words of `string`.
+ */
+ words(
+ string?: string,
+ pattern?: string|RegExp
+ ): string[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.words
+ */
+ words(pattern?: string|RegExp): string[];
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.words
+ */
+ words(pattern?: string|RegExp): LoDashExplicitArrayWrapper<string>;
+ }
+
+ /***********
+ * Utility *
+ ***********/
+
+ //_.attempt
+ interface LoDashStatic {
+ /**
+ * Attempts to invoke func, returning either the result or the caught error object. Any additional arguments
+ * are provided to func when it’s invoked.
+ *
+ * @param func The function to attempt.
+ * @return Returns the func result or error object.
+ */
+ attempt<TResult>(func: (...args: any[]) => TResult, ...args: any[]): TResult|Error;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.attempt
+ */
+ attempt<TResult>(...args: any[]): TResult|Error;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.attempt
+ */
+ attempt<TResult>(...args: any[]): LoDashExplicitObjectWrapper<TResult|Error>;
+ }
+
+ //_.constant
+ interface LoDashStatic {
+ /**
+ * Creates a function that returns value.
+ *
+ * @param value The value to return from the new function.
+ * @return Returns the new function.
+ */
+ constant<T>(value: T): () => T;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.constant
+ */
+ constant<TResult>(): LoDashImplicitObjectWrapper<() => TResult>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.constant
+ */
+ constant<TResult>(): LoDashExplicitObjectWrapper<() => TResult>;
+ }
+
+ //_.defaultTo
+ interface LoDashStatic {
+ /**
+ * Checks `value` to determine whether a default value should be returned in
+ * its place. The `defaultValue` is returned if `value` is `NaN`, `null`,
+ * or `undefined`.
+ *
+ * @param value The value to check.
+ * @param defaultValue The default value.
+ * @returns Returns the resolved value.
+ */
+ defaultTo<T>(value: T, defaultValue: T): T;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.defaultTo
+ */
+ defaultTo<TResult>(value: TResult): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.defaultTo
+ */
+ defaultTo<TResult>(value: TResult): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.identity
+ interface LoDashStatic {
+ /**
+ * This method returns the first argument provided to it.
+ *
+ * @param value Any value.
+ * @return Returns value.
+ */
+ identity<T>(value?: T): T;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.identity
+ */
+ identity(): T;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.identity
+ */
+ identity(): T[];
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.identity
+ */
+ identity(): T;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.identity
+ */
+ identity(): LoDashExplicitWrapper<T>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.identity
+ */
+ identity(): LoDashExplicitArrayWrapper<T>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.identity
+ */
+ identity(): LoDashExplicitObjectWrapper<T>;
+ }
+
+ //_.iteratee
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes `func` with the arguments of the created
+ * function. If `func` is a property name the created callback returns the
+ * property value for a given element. If `func` is an object the created
+ * callback returns `true` for elements that contain the equivalent object properties, otherwise it returns `false`.
+ *
+ * @static
+ * @memberOf _
+ * @category Util
+ * @param {*} [func=_.identity] The value to convert to a callback.
+ * @returns {Function} Returns the callback.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
+ *
+ * // create custom iteratee shorthands
+ * _.iteratee = _.wrap(_.iteratee, function(callback, func) {
+ * var p = /^(\S+)\s*([<>])\s*(\S+)$/.exec(func);
+ * return !p ? callback(func) : function(object) {
+ * return (p[2] == '>' ? object[p[1]] > p[3] : object[p[1]] < p[3]);
+ * };
+ * });
+ *
+ * _.filter(users, 'age > 36');
+ * // => [{ 'user': 'fred', 'age': 40 }]
+ */
+ iteratee<TResult>(
+ func: Function
+ ): (...args: any[]) => TResult;
+
+ /**
+ * @see _.iteratee
+ */
+ iteratee<TResult>(
+ func: string
+ ): (object: any) => TResult;
+
+ /**
+ * @see _.iteratee
+ */
+ iteratee(
+ func: Object
+ ): (object: any) => boolean;
+
+ /**
+ * @see _.iteratee
+ */
+ iteratee<TResult>(): (value: TResult) => TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.iteratee
+ */
+ iteratee<TResult>(): LoDashImplicitObjectWrapper<(object: any) => TResult>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.iteratee
+ */
+ iteratee(): LoDashImplicitObjectWrapper<(object: any) => boolean>;
+
+ /**
+ * @see _.iteratee
+ */
+ iteratee<TResult>(): LoDashImplicitObjectWrapper<(...args: any[]) => TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.iteratee
+ */
+ iteratee<TResult>(): LoDashExplicitObjectWrapper<(object: any) => TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.iteratee
+ */
+ iteratee(): LoDashExplicitObjectWrapper<(object: any) => boolean>;
+
+ /**
+ * @see _.iteratee
+ */
+ iteratee<TResult>(): LoDashExplicitObjectWrapper<(...args: any[]) => TResult>;
+ }
+
+ //_.matches
+ interface LoDashStatic {
+ /**
+ * Creates a function that performs a deep comparison between a given object and source, returning true if the
+ * given object has equivalent property values, else false.
+ *
+ * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and
+ * strings. Objects are compared by their own, not inherited, enumerable properties. For comparing a single own
+ * or inherited property value see _.matchesProperty.
+ *
+ * @param source The object of property values to match.
+ * @return Returns the new function.
+ */
+ matches<T>(source: T): (value: any) => boolean;
+
+ /**
+ * @see _.matches
+ */
+ matches<T, V>(source: T): (value: V) => boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.matches
+ */
+ matches<V>(): LoDashImplicitObjectWrapper<(value: V) => boolean>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.matches
+ */
+ matches<V>(): LoDashExplicitObjectWrapper<(value: V) => boolean>;
+ }
+
+ //_.matchesProperty
+ interface LoDashStatic {
+ /**
+ * Creates a function that compares the property value of path on a given object to value.
+ *
+ * Note: This method supports comparing arrays, booleans, Date objects, numbers, Object objects, regexes, and
+ * strings. Objects are compared by their own, not inherited, enumerable properties.
+ *
+ * @param path The path of the property to get.
+ * @param srcValue The value to match.
+ * @return Returns the new function.
+ */
+ matchesProperty<T>(
+ path: StringRepresentable|StringRepresentable[],
+ srcValue: T
+ ): (value: any) => boolean;
+
+ /**
+ * @see _.matchesProperty
+ */
+ matchesProperty<T, V>(
+ path: StringRepresentable|StringRepresentable[],
+ srcValue: T
+ ): (value: V) => boolean;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.matchesProperty
+ */
+ matchesProperty<SrcValue>(
+ srcValue: SrcValue
+ ): LoDashImplicitObjectWrapper<(value: any) => boolean>;
+
+ /**
+ * @see _.matchesProperty
+ */
+ matchesProperty<SrcValue, Value>(
+ srcValue: SrcValue
+ ): LoDashImplicitObjectWrapper<(value: Value) => boolean>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.matchesProperty
+ */
+ matchesProperty<SrcValue>(
+ srcValue: SrcValue
+ ): LoDashExplicitObjectWrapper<(value: any) => boolean>;
+
+ /**
+ * @see _.matchesProperty
+ */
+ matchesProperty<SrcValue, Value>(
+ srcValue: SrcValue
+ ): LoDashExplicitObjectWrapper<(value: Value) => boolean>;
+ }
+
+ //_.method
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes the method at path on a given object. Any additional arguments are provided
+ * to the invoked method.
+ *
+ * @param path The path of the method to invoke.
+ * @param args The arguments to invoke the method with.
+ * @return Returns the new function.
+ */
+ method<TObject, TResult>(
+ path: string|StringRepresentable[],
+ ...args: any[]
+ ): (object: TObject) => TResult;
+
+ /**
+ * @see _.method
+ */
+ method<TResult>(
+ path: string|StringRepresentable[],
+ ...args: any[]
+ ): (object: any) => TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.method
+ */
+ method<TObject, TResult>(...args: any[]): LoDashImplicitObjectWrapper<(object: TObject) => TResult>;
+
+ /**
+ * @see _.method
+ */
+ method<TResult>(...args: any[]): LoDashImplicitObjectWrapper<(object: any) => TResult>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.method
+ */
+ method<TObject, TResult>(...args: any[]): LoDashImplicitObjectWrapper<(object: TObject) => TResult>;
+
+ /**
+ * @see _.method
+ */
+ method<TResult>(...args: any[]): LoDashImplicitObjectWrapper<(object: any) => TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.method
+ */
+ method<TObject, TResult>(...args: any[]): LoDashExplicitObjectWrapper<(object: TObject) => TResult>;
+
+ /**
+ * @see _.method
+ */
+ method<TResult>(...args: any[]): LoDashExplicitObjectWrapper<(object: any) => TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.method
+ */
+ method<TObject, TResult>(...args: any[]): LoDashExplicitObjectWrapper<(object: TObject) => TResult>;
+
+ /**
+ * @see _.method
+ */
+ method<TResult>(...args: any[]): LoDashExplicitObjectWrapper<(object: any) => TResult>;
+ }
+
+ //_.methodOf
+ interface LoDashStatic {
+ /**
+ * The opposite of _.method; this method creates a function that invokes the method at a given path on object.
+ * Any additional arguments are provided to the invoked method.
+ *
+ * @param object The object to query.
+ * @param args The arguments to invoke the method with.
+ * @return Returns the new function.
+ */
+ methodOf<TObject extends {}, TResult>(
+ object: TObject,
+ ...args: any[]
+ ): (path: StringRepresentable|StringRepresentable[]) => TResult;
+
+ /**
+ * @see _.methodOf
+ */
+ methodOf<TResult>(
+ object: {},
+ ...args: any[]
+ ): (path: StringRepresentable|StringRepresentable[]) => TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.methodOf
+ */
+ methodOf<TResult>(
+ ...args: any[]
+ ): LoDashImplicitObjectWrapper<(path: StringRepresentable|StringRepresentable[]) => TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.methodOf
+ */
+ methodOf<TResult>(
+ ...args: any[]
+ ): LoDashExplicitObjectWrapper<(path: StringRepresentable|StringRepresentable[]) => TResult>;
+ }
+
+ //_.mixin
+ interface MixinOptions {
+ chain?: boolean;
+ }
+
+ interface LoDashStatic {
+ /**
+ * Adds all own enumerable function properties of a source object to the destination object. If object is a
+ * function then methods are added to its prototype as well.
+ *
+ * Note: Use _.runInContext to create a pristine lodash function to avoid conflicts caused by modifying
+ * the original.
+ *
+ * @param object The destination object.
+ * @param source The object of functions to add.
+ * @param options The options object.
+ * @param options.chain Specify whether the functions added are chainable.
+ * @return Returns object.
+ */
+ mixin<TResult, TObject>(
+ object: TObject,
+ source: Dictionary<Function>,
+ options?: MixinOptions
+ ): TResult;
+
+ /**
+ * @see _.mixin
+ */
+ mixin<TResult>(
+ source: Dictionary<Function>,
+ options?: MixinOptions
+ ): TResult;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.mixin
+ */
+ mixin<TResult>(
+ source: Dictionary<Function>,
+ options?: MixinOptions
+ ): LoDashImplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.mixin
+ */
+ mixin<TResult>(
+ options?: MixinOptions
+ ): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.mixin
+ */
+ mixin<TResult>(
+ source: Dictionary<Function>,
+ options?: MixinOptions
+ ): LoDashExplicitObjectWrapper<TResult>;
+
+ /**
+ * @see _.mixin
+ */
+ mixin<TResult>(
+ options?: MixinOptions
+ ): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.noConflict
+ interface LoDashStatic {
+ /**
+ * Reverts the _ variable to its previous value and returns a reference to the lodash function.
+ *
+ * @return Returns the lodash function.
+ */
+ noConflict(): typeof _;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.noConflict
+ */
+ noConflict(): typeof _;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.noConflict
+ */
+ noConflict(): LoDashExplicitObjectWrapper<typeof _>;
+ }
+
+ //_.noop
+ interface LoDashStatic {
+ /**
+ * A no-operation function that returns undefined regardless of the arguments it receives.
+ *
+ * @return undefined
+ */
+ noop(...args: any[]): void;
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.noop
+ */
+ noop(...args: any[]): void;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.noop
+ */
+ noop(...args: any[]): _.LoDashExplicitWrapper<void>;
+ }
+
+ //_.nthArg
+ interface LoDashStatic {
+ /**
+ * Creates a function that returns its nth argument.
+ *
+ * @param n The index of the argument to return.
+ * @return Returns the new function.
+ */
+ nthArg<TResult extends Function>(n?: number): TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.nthArg
+ */
+ nthArg<TResult extends Function>(): LoDashImplicitObjectWrapper<TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.nthArg
+ */
+ nthArg<TResult extends Function>(): LoDashExplicitObjectWrapper<TResult>;
+ }
+
+ //_.over
+ interface LoDashStatic {
+ /**
+ * Creates a function that invokes iteratees with the arguments provided to the created function and returns
+ * their results.
+ *
+ * @param iteratees The iteratees to invoke.
+ * @return Returns the new function.
+ */
+ over<TResult>(...iteratees: (Function|Function[])[]): (...args: any[]) => TResult[];
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.over
+ */
+ over<TResult>(...iteratees: (Function|Function[])[]): LoDashImplicitObjectWrapper<(...args: any[]) => TResult[]>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.over
+ */
+ over<TResult>(...iteratees: (Function|Function[])[]): LoDashImplicitObjectWrapper<(...args: any[]) => TResult[]>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.over
+ */
+ over<TResult>(...iteratees: (Function|Function[])[]): LoDashExplicitObjectWrapper<(...args: any[]) => TResult[]>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.over
+ */
+ over<TResult>(...iteratees: (Function|Function[])[]): LoDashExplicitObjectWrapper<(...args: any[]) => TResult[]>;
+ }
+
+ //_.overEvery
+ interface LoDashStatic {
+ /**
+ * Creates a function that checks if all of the predicates return truthy when invoked with the arguments
+ * provided to the created function.
+ *
+ * @param predicates The predicates to check.
+ * @return Returns the new function.
+ */
+ overEvery(...predicates: (Function|Function[])[]): (...args: any[]) => boolean;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.overEvery
+ */
+ overEvery(...predicates: (Function|Function[])[]): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.overEvery
+ */
+ overEvery(...predicates: (Function|Function[])[]): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.overEvery
+ */
+ overEvery(...predicates: (Function|Function[])[]): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.overEvery
+ */
+ overEvery(...predicates: (Function|Function[])[]): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ //_.overSome
+ interface LoDashStatic {
+ /**
+ * Creates a function that checks if any of the predicates return truthy when invoked with the arguments
+ * provided to the created function.
+ *
+ * @param predicates The predicates to check.
+ * @return Returns the new function.
+ */
+ overSome(...predicates: (Function|Function[])[]): (...args: any[]) => boolean;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.overSome
+ */
+ overSome(...predicates: (Function|Function[])[]): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.overSome
+ */
+ overSome(...predicates: (Function|Function[])[]): LoDashImplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.overSome
+ */
+ overSome(...predicates: (Function|Function[])[]): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.overSome
+ */
+ overSome(...predicates: (Function|Function[])[]): LoDashExplicitObjectWrapper<(...args: any[]) => boolean>;
+ }
+
+ //_.property
+ interface LoDashStatic {
+ /**
+ * Creates a function that returns the property value at path on a given object.
+ *
+ * @param path The path of the property to get.
+ * @return Returns the new function.
+ */
+ property<TObj, TResult>(path: StringRepresentable|StringRepresentable[]): (obj: TObj) => TResult;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.property
+ */
+ property<TObj, TResult>(): LoDashImplicitObjectWrapper<(obj: TObj) => TResult>;
+ }
+
+ interface LoDashImplicitArrayWrapper<T> {
+ /**
+ * @see _.property
+ */
+ property<TObj, TResult>(): LoDashImplicitObjectWrapper<(obj: TObj) => TResult>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.property
+ */
+ property<TObj, TResult>(): LoDashExplicitObjectWrapper<(obj: TObj) => TResult>;
+ }
+
+ interface LoDashExplicitArrayWrapper<T> {
+ /**
+ * @see _.property
+ */
+ property<TObj, TResult>(): LoDashExplicitObjectWrapper<(obj: TObj) => TResult>;
+ }
+
+ //_.propertyOf
+ interface LoDashStatic {
+ /**
+ * The opposite of _.property; this method creates a function that returns the property value at a given path
+ * on object.
+ *
+ * @param object The object to query.
+ * @return Returns the new function.
+ */
+ propertyOf<T extends {}>(object: T): (path: string|string[]) => any;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.propertyOf
+ */
+ propertyOf(): LoDashImplicitObjectWrapper<(path: string|string[]) => any>;
+ }
+
+ interface LoDashExplicitObjectWrapper<T> {
+ /**
+ * @see _.propertyOf
+ */
+ propertyOf(): LoDashExplicitObjectWrapper<(path: string|string[]) => any>;
+ }
+
+ //_.range
+ interface LoDashStatic {
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from start up to, but not including, end.
+ * If end is not specified it’s set to start with start then set to 0. If end is less than start a zero-length
+ * range is created unless a negative step is specified.
+ *
+ * @param start The start of the range.
+ * @param end The end of the range.
+ * @param step The value to increment or decrement by.
+ * @return Returns a new range array.
+ */
+ range(
+ start: number,
+ end: number,
+ step?: number
+ ): number[];
+
+ /**
+ * @see _.range
+ */
+ range(
+ end: number,
+ step?: number
+ ): number[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.range
+ */
+ range(
+ end?: number,
+ step?: number
+ ): LoDashImplicitArrayWrapper<number>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.range
+ */
+ range(
+ end?: number,
+ step?: number
+ ): LoDashExplicitArrayWrapper<number>;
+ }
+
+ //_.rangeRight
+ interface LoDashStatic {
+ /**
+ * This method is like `_.range` except that it populates values in
+ * descending order.
+ *
+ * @static
+ * @memberOf _
+ * @category Util
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns the new array of numbers.
+ * @example
+ *
+ * _.rangeRight(4);
+ * // => [3, 2, 1, 0]
+ *
+ * _.rangeRight(-4);
+ * // => [-3, -2, -1, 0]
+ *
+ * _.rangeRight(1, 5);
+ * // => [4, 3, 2, 1]
+ *
+ * _.rangeRight(0, 20, 5);
+ * // => [15, 10, 5, 0]
+ *
+ * _.rangeRight(0, -4, -1);
+ * // => [-3, -2, -1, 0]
+ *
+ * _.rangeRight(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.rangeRight(0);
+ * // => []
+ */
+ rangeRight(
+ start: number,
+ end: number,
+ step?: number
+ ): number[];
+
+ /**
+ * @see _.rangeRight
+ */
+ rangeRight(
+ end: number,
+ step?: number
+ ): number[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.rangeRight
+ */
+ rangeRight(
+ end?: number,
+ step?: number
+ ): LoDashImplicitArrayWrapper<number>;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.rangeRight
+ */
+ rangeRight(
+ end?: number,
+ step?: number
+ ): LoDashExplicitArrayWrapper<number>;
+ }
+
+ //_.runInContext
+ interface LoDashStatic {
+ /**
+ * Create a new pristine lodash function using the given context object.
+ *
+ * @param context The context object.
+ * @return Returns a new lodash function.
+ */
+ runInContext(context?: Object): typeof _;
+ }
+
+ interface LoDashImplicitObjectWrapper<T> {
+ /**
+ * @see _.runInContext
+ */
+ runInContext(): typeof _;
+ }
+
+ //_.times
+ interface LoDashStatic {
+ /**
+ * Invokes the iteratee function n times, returning an array of the results of each invocation. The iteratee
+ * is invoked with one argument; (index).
+ *
+ * @param n The number of times to invoke iteratee.
+ * @param iteratee The function invoked per iteration.
+ * @return Returns the array of results.
+ */
+ times<TResult>(
+ n: number,
+ iteratee: (num: number) => TResult
+ ): TResult[];
+
+ /**
+ * @see _.times
+ */
+ times(n: number): number[];
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.times
+ */
+ times<TResult>(
+ iteratee: (num: number) => TResult
+ ): TResult[];
+
+ /**
+ * @see _.times
+ */
+ times(): number[];
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.times
+ */
+ times<TResult>(
+ iteratee: (num: number) => TResult
+ ): LoDashExplicitArrayWrapper<TResult>;
+
+ /**
+ * @see _.times
+ */
+ times(): LoDashExplicitArrayWrapper<number>;
+ }
+
+ //_.toPath
+ interface LoDashStatic {
+ /**
+ * Converts `value` to a property path array.
+ *
+ * @static
+ * @memberOf _
+ * @category Util
+ * @param {*} value The value to convert.
+ * @returns {Array} Returns the new property path array.
+ * @example
+ *
+ * _.toPath('a.b.c');
+ * // => ['a', 'b', 'c']
+ *
+ * _.toPath('a[0].b.c');
+ * // => ['a', '0', 'b', 'c']
+ *
+ * var path = ['a', 'b', 'c'],
+ * newPath = _.toPath(path);
+ *
+ * console.log(newPath);
+ * // => ['a', 'b', 'c']
+ *
+ * console.log(path === newPath);
+ * // => false
+ */
+ toPath(value: any): string[];
+ }
+
+ interface LoDashImplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toPath
+ */
+ toPath(): LoDashImplicitWrapper<string[]>;
+ }
+
+ interface LoDashExplicitWrapperBase<T, TWrapper> {
+ /**
+ * @see _.toPath
+ */
+ toPath(): LoDashExplicitWrapper<string[]>;
+ }
+
+ //_.uniqueId
+ interface LoDashStatic {
+ /**
+ * Generates a unique ID. If prefix is provided the ID is appended to it.
+ *
+ * @param prefix The value to prefix the ID with.
+ * @return Returns the unique ID.
+ */
+ uniqueId(prefix?: string): string;
+ }
+
+ interface LoDashImplicitWrapper<T> {
+ /**
+ * @see _.uniqueId
+ */
+ uniqueId(): string;
+ }
+
+ interface LoDashExplicitWrapper<T> {
+ /**
+ * @see _.uniqueId
+ */
+ uniqueId(): LoDashExplicitWrapper<string>;
+ }
+
+ interface ListIterator<T, TResult> {
+ (value: T, index: number, collection: List<T>): TResult;
+ }
+
+ interface DictionaryIterator<T, TResult> {
+ (value: T, key?: string, collection?: Dictionary<T>): TResult;
+ }
+
+ interface NumericDictionaryIterator<T, TResult> {
+ (value: T, key?: number, collection?: Dictionary<T>): TResult;
+ }
+
+ interface ObjectIterator<T, TResult> {
+ (element: T, key?: string, collection?: any): TResult;
+ }
+
+ interface StringIterator<TResult> {
+ (char: string, index?: number, string?: string): TResult;
+ }
+
+ interface MemoVoidIterator<T, TResult> {
+ (prev: TResult, curr: T, indexOrKey?: any, list?: T[]): void;
+ }
+ interface MemoIterator<T, TResult> {
+ (prev: TResult, curr: T, indexOrKey?: any, list?: T[]): TResult;
+ }
+
+ interface MemoVoidArrayIterator<T, TResult> {
+ (acc: TResult, curr: T, index?: number, arr?: T[]): void;
+ }
+ interface MemoVoidDictionaryIterator<T, TResult> {
+ (acc: TResult, curr: T, key?: string, dict?: Dictionary<T>): void;
+ }
+
+ //interface Collection<T> {}
+
+ // Common interface between Arrays and jQuery objects
+ interface List<T> {
+ [index: number]: T;
+ length: number;
+ }
+
+ interface Dictionary<T> {
+ [index: string]: T;
+ }
+
+ interface NumericDictionary<T> {
+ [index: number]: T;
+ }
+
+ interface StringRepresentable {
+ toString(): string;
+ }
+
+ interface Cancelable {
+ cancel(): void;
+ flush(): void;
+ }
+}
+
+// Named exports
+
+declare module "lodash/after" {
+ const after: typeof _.after;
+ export = after;
+}
+
+
+declare module "lodash/ary" {
+ const ary: typeof _.ary;
+ export = ary;
+}
+
+
+declare module "lodash/assign" {
+ const assign: typeof _.assign;
+ export = assign;
+}
+
+
+declare module "lodash/assignIn" {
+ const assignIn: typeof _.assignIn;
+ export = assignIn;
+}
+
+
+declare module "lodash/assignInWith" {
+ const assignInWith: typeof _.assignInWith;
+ export = assignInWith;
+}
+
+
+declare module "lodash/assignWith" {
+ const assignWith: typeof _.assignWith;
+ export = assignWith;
+}
+
+
+declare module "lodash/at" {
+ const at: typeof _.at;
+ export = at;
+}
+
+
+declare module "lodash/before" {
+ const before: typeof _.before;
+ export = before;
+}
+
+
+declare module "lodash/bind" {
+ const bind: typeof _.bind;
+ export = bind;
+}
+
+
+declare module "lodash/bindAll" {
+ const bindAll: typeof _.bindAll;
+ export = bindAll;
+}
+
+
+declare module "lodash/bindKey" {
+ const bindKey: typeof _.bindKey;
+ export = bindKey;
+}
+
+
+declare module "lodash/castArray" {
+ const castArray: typeof _.castArray;
+ export = castArray;
+}
+
+
+declare module "lodash/chain" {
+ const chain: typeof _.chain;
+ export = chain;
+}
+
+
+declare module "lodash/chunk" {
+ const chunk: typeof _.chunk;
+ export = chunk;
+}
+
+
+declare module "lodash/compact" {
+ const compact: typeof _.compact;
+ export = compact;
+}
+
+
+declare module "lodash/concat" {
+ const concat: typeof _.concat;
+ export = concat;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/cond" {
+ const cond: typeof _.cond;
+ export = cond;
+ }
+ */
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/conforms" {
+ const conforms: typeof _.conforms;
+ export = conforms;
+ }
+ */
+
+declare module "lodash/constant" {
+ const constant: typeof _.constant;
+ export = constant;
+}
+
+
+declare module "lodash/countBy" {
+ const countBy: typeof _.countBy;
+ export = countBy;
+}
+
+
+declare module "lodash/create" {
+ const create: typeof _.create;
+ export = create;
+}
+
+
+declare module "lodash/curry" {
+ const curry: typeof _.curry;
+ export = curry;
+}
+
+
+declare module "lodash/curryRight" {
+ const curryRight: typeof _.curryRight;
+ export = curryRight;
+}
+
+
+declare module "lodash/debounce" {
+ const debounce: typeof _.debounce;
+ export = debounce;
+}
+
+
+declare module "lodash/defaults" {
+ const defaults: typeof _.defaults;
+ export = defaults;
+}
+
+
+declare module "lodash/defaultsDeep" {
+ const defaultsDeep: typeof _.defaultsDeep;
+ export = defaultsDeep;
+}
+
+
+declare module "lodash/defer" {
+ const defer: typeof _.defer;
+ export = defer;
+}
+
+
+declare module "lodash/delay" {
+ const delay: typeof _.delay;
+ export = delay;
+}
+
+
+declare module "lodash/difference" {
+ const difference: typeof _.difference;
+ export = difference;
+}
+
+
+declare module "lodash/differenceBy" {
+ const differenceBy: typeof _.differenceBy;
+ export = differenceBy;
+}
+
+
+declare module "lodash/differenceWith" {
+ const differenceWith: typeof _.differenceWith;
+ export = differenceWith;
+}
+
+
+declare module "lodash/drop" {
+ const drop: typeof _.drop;
+ export = drop;
+}
+
+
+declare module "lodash/dropRight" {
+ const dropRight: typeof _.dropRight;
+ export = dropRight;
+}
+
+
+declare module "lodash/dropRightWhile" {
+ const dropRightWhile: typeof _.dropRightWhile;
+ export = dropRightWhile;
+}
+
+
+declare module "lodash/dropWhile" {
+ const dropWhile: typeof _.dropWhile;
+ export = dropWhile;
+}
+
+
+declare module "lodash/fill" {
+ const fill: typeof _.fill;
+ export = fill;
+}
+
+
+declare module "lodash/filter" {
+ const filter: typeof _.filter;
+ export = filter;
+}
+
+
+declare module "lodash/flatMap" {
+ const flatMap: typeof _.flatMap;
+ export = flatMap;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/flatMapDeep" {
+ const flatMapDeep: typeof _.flatMapDeep;
+ export = flatMapDeep;
+ }
+ */
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/flatMapDepth" {
+ const flatMapDepth: typeof _.flatMapDepth;
+ export = flatMapDepth;
+ }
+ */
+
+declare module "lodash/flatten" {
+ const flatten: typeof _.flatten;
+ export = flatten;
+}
+
+
+declare module "lodash/flattenDeep" {
+ const flattenDeep: typeof _.flattenDeep;
+ export = flattenDeep;
+}
+
+declare module "lodash/flattenDepth" {
+ const flattenDepth: typeof _.flattenDepth;
+ export = flattenDepth;
+}
+
+declare module "lodash/flip" {
+ const flip: typeof _.flip;
+ export = flip;
+}
+
+
+declare module "lodash/flow" {
+ const flow: typeof _.flow;
+ export = flow;
+}
+
+
+declare module "lodash/flowRight" {
+ const flowRight: typeof _.flowRight;
+ export = flowRight;
+}
+
+
+declare module "lodash/fromPairs" {
+ const fromPairs: typeof _.fromPairs;
+ export = fromPairs;
+}
+
+
+declare module "lodash/functions" {
+ const functions: typeof _.functions;
+ export = functions;
+}
+
+
+declare module "lodash/functionsIn" {
+ const functionsIn: typeof _.functionsIn;
+ export = functionsIn;
+}
+
+
+declare module "lodash/groupBy" {
+ const groupBy: typeof _.groupBy;
+ export = groupBy;
+}
+
+
+declare module "lodash/initial" {
+ const initial: typeof _.initial;
+ export = initial;
+}
+
+
+declare module "lodash/intersection" {
+ const intersection: typeof _.intersection;
+ export = intersection;
+}
+
+
+declare module "lodash/intersectionBy" {
+ const intersectionBy: typeof _.intersectionBy;
+ export = intersectionBy;
+}
+
+
+declare module "lodash/intersectionWith" {
+ const intersectionWith: typeof _.intersectionWith;
+ export = intersectionWith;
+}
+
+
+declare module "lodash/invert" {
+ const invert: typeof _.invert;
+ export = invert;
+}
+
+
+declare module "lodash/invertBy" {
+ const invertBy: typeof _.invertBy;
+ export = invertBy;
+}
+
+
+declare module "lodash/invokeMap" {
+ const invokeMap: typeof _.invokeMap;
+ export = invokeMap;
+}
+
+
+declare module "lodash/iteratee" {
+ const iteratee: typeof _.iteratee;
+ export = iteratee;
+}
+
+
+declare module "lodash/keyBy" {
+ const keyBy: typeof _.keyBy;
+ export = keyBy;
+}
+
+
+declare module "lodash/keys" {
+ const keys: typeof _.keys;
+ export = keys;
+}
+
+
+declare module "lodash/keysIn" {
+ const keysIn: typeof _.keysIn;
+ export = keysIn;
+}
+
+
+declare module "lodash/map" {
+ const map: typeof _.map;
+ export = map;
+}
+
+
+declare module "lodash/mapKeys" {
+ const mapKeys: typeof _.mapKeys;
+ export = mapKeys;
+}
+
+
+declare module "lodash/mapValues" {
+ const mapValues: typeof _.mapValues;
+ export = mapValues;
+}
+
+
+declare module "lodash/matches" {
+ const matches: typeof _.matches;
+ export = matches;
+}
+
+
+declare module "lodash/matchesProperty" {
+ const matchesProperty: typeof _.matchesProperty;
+ export = matchesProperty;
+}
+
+
+declare module "lodash/memoize" {
+ const memoize: typeof _.memoize;
+ export = memoize;
+}
+
+
+declare module "lodash/merge" {
+ const merge: typeof _.merge;
+ export = merge;
+}
+
+
+declare module "lodash/mergeWith" {
+ const mergeWith: typeof _.mergeWith;
+ export = mergeWith;
+}
+
+
+declare module "lodash/method" {
+ const method: typeof _.method;
+ export = method;
+}
+
+
+declare module "lodash/methodOf" {
+ const methodOf: typeof _.methodOf;
+ export = methodOf;
+}
+
+
+declare module "lodash/mixin" {
+ const mixin: typeof _.mixin;
+ export = mixin;
+}
+
+
+declare module "lodash/negate" {
+ const negate: typeof _.negate;
+ export = negate;
+}
+
+
+declare module "lodash/nthArg" {
+ const nthArg: typeof _.nthArg;
+ export = nthArg;
+}
+
+
+declare module "lodash/omit" {
+ const omit: typeof _.omit;
+ export = omit;
+}
+
+
+declare module "lodash/omitBy" {
+ const omitBy: typeof _.omitBy;
+ export = omitBy;
+}
+
+
+declare module "lodash/once" {
+ const once: typeof _.once;
+ export = once;
+}
+
+
+declare module "lodash/orderBy" {
+ const orderBy: typeof _.orderBy;
+ export = orderBy;
+}
+
+
+declare module "lodash/over" {
+ const over: typeof _.over;
+ export = over;
+}
+
+
+declare module "lodash/overArgs" {
+ const overArgs: typeof _.overArgs;
+ export = overArgs;
+}
+
+
+declare module "lodash/overEvery" {
+ const overEvery: typeof _.overEvery;
+ export = overEvery;
+}
+
+
+declare module "lodash/overSome" {
+ const overSome: typeof _.overSome;
+ export = overSome;
+}
+
+
+declare module "lodash/partial" {
+ const partial: typeof _.partial;
+ export = partial;
+}
+
+
+declare module "lodash/partialRight" {
+ const partialRight: typeof _.partialRight;
+ export = partialRight;
+}
+
+
+declare module "lodash/partition" {
+ const partition: typeof _.partition;
+ export = partition;
+}
+
+
+declare module "lodash/pick" {
+ const pick: typeof _.pick;
+ export = pick;
+}
+
+
+declare module "lodash/pickBy" {
+ const pickBy: typeof _.pickBy;
+ export = pickBy;
+}
+
+
+declare module "lodash/property" {
+ const property: typeof _.property;
+ export = property;
+}
+
+
+declare module "lodash/propertyOf" {
+ const propertyOf: typeof _.propertyOf;
+ export = propertyOf;
+}
+
+
+declare module "lodash/pull" {
+ const pull: typeof _.pull;
+ export = pull;
+}
+
+
+declare module "lodash/pullAll" {
+ const pullAll: typeof _.pullAll;
+ export = pullAll;
+}
+
+
+declare module "lodash/pullAllBy" {
+ const pullAllBy: typeof _.pullAllBy;
+ export = pullAllBy;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/pullAllWith" {
+ const pullAllWith: typeof _.pullAllWith;
+ export = pullAllWith;
+ }
+ */
+
+declare module "lodash/pullAt" {
+ const pullAt: typeof _.pullAt;
+ export = pullAt;
+}
+
+
+declare module "lodash/range" {
+ const range: typeof _.range;
+ export = range;
+}
+
+
+declare module "lodash/rangeRight" {
+ const rangeRight: typeof _.rangeRight;
+ export = rangeRight;
+}
+
+
+declare module "lodash/rearg" {
+ const rearg: typeof _.rearg;
+ export = rearg;
+}
+
+
+declare module "lodash/reject" {
+ const reject: typeof _.reject;
+ export = reject;
+}
+
+
+declare module "lodash/remove" {
+ const remove: typeof _.remove;
+ export = remove;
+}
+
+
+declare module "lodash/rest" {
+ const rest: typeof _.rest;
+ export = rest;
+}
+
+
+declare module "lodash/reverse" {
+ const reverse: typeof _.reverse;
+ export = reverse;
+}
+
+
+declare module "lodash/sampleSize" {
+ const sampleSize: typeof _.sampleSize;
+ export = sampleSize;
+}
+
+
+declare module "lodash/set" {
+ const set: typeof _.set;
+ export = set;
+}
+
+
+declare module "lodash/setWith" {
+ const setWith: typeof _.setWith;
+ export = setWith;
+}
+
+
+declare module "lodash/shuffle" {
+ const shuffle: typeof _.shuffle;
+ export = shuffle;
+}
+
+
+declare module "lodash/slice" {
+ const slice: typeof _.slice;
+ export = slice;
+}
+
+
+declare module "lodash/sortBy" {
+ const sortBy: typeof _.sortBy;
+ export = sortBy;
+}
+
+
+declare module "lodash/sortedUniq" {
+ const sortedUniq: typeof _.sortedUniq;
+ export = sortedUniq;
+}
+
+
+declare module "lodash/sortedUniqBy" {
+ const sortedUniqBy: typeof _.sortedUniqBy;
+ export = sortedUniqBy;
+}
+
+
+declare module "lodash/split" {
+ const split: typeof _.split;
+ export = split;
+}
+
+
+declare module "lodash/spread" {
+ const spread: typeof _.spread;
+ export = spread;
+}
+
+
+declare module "lodash/tail" {
+ const tail: typeof _.tail;
+ export = tail;
+}
+
+
+declare module "lodash/take" {
+ const take: typeof _.take;
+ export = take;
+}
+
+
+declare module "lodash/takeRight" {
+ const takeRight: typeof _.takeRight;
+ export = takeRight;
+}
+
+
+declare module "lodash/takeRightWhile" {
+ const takeRightWhile: typeof _.takeRightWhile;
+ export = takeRightWhile;
+}
+
+
+declare module "lodash/takeWhile" {
+ const takeWhile: typeof _.takeWhile;
+ export = takeWhile;
+}
+
+
+declare module "lodash/tap" {
+ const tap: typeof _.tap;
+ export = tap;
+}
+
+
+declare module "lodash/throttle" {
+ const throttle: typeof _.throttle;
+ export = throttle;
+}
+
+
+declare module "lodash/thru" {
+ const thru: typeof _.thru;
+ export = thru;
+}
+
+
+declare module "lodash/toArray" {
+ const toArray: typeof _.toArray;
+ export = toArray;
+}
+
+
+declare module "lodash/toPairs" {
+ const toPairs: typeof _.toPairs;
+ export = toPairs;
+}
+
+
+declare module "lodash/toPairsIn" {
+ const toPairsIn: typeof _.toPairsIn;
+ export = toPairsIn;
+}
+
+
+declare module "lodash/toPath" {
+ const toPath: typeof _.toPath;
+ export = toPath;
+}
+
+
+declare module "lodash/toPlainObject" {
+ const toPlainObject: typeof _.toPlainObject;
+ export = toPlainObject;
+}
+
+
+declare module "lodash/transform" {
+ const transform: typeof _.transform;
+ export = transform;
+}
+
+
+declare module "lodash/unary" {
+ const unary: typeof _.unary;
+ export = unary;
+}
+
+
+declare module "lodash/union" {
+ const union: typeof _.union;
+ export = union;
+}
+
+
+declare module "lodash/unionBy" {
+ const unionBy: typeof _.unionBy;
+ export = unionBy;
+}
+
+
+declare module "lodash/unionWith" {
+ const unionWith: typeof _.unionWith;
+ export = unionWith;
+}
+
+
+declare module "lodash/uniq" {
+ const uniq: typeof _.uniq;
+ export = uniq;
+}
+
+
+declare module "lodash/uniqBy" {
+ const uniqBy: typeof _.uniqBy;
+ export = uniqBy;
+}
+
+
+declare module "lodash/uniqWith" {
+ const uniqWith: typeof _.uniqWith;
+ export = uniqWith;
+}
+
+
+declare module "lodash/unset" {
+ const unset: typeof _.unset;
+ export = unset;
+}
+
+
+declare module "lodash/unzip" {
+ const unzip: typeof _.unzip;
+ export = unzip;
+}
+
+
+declare module "lodash/unzipWith" {
+ const unzipWith: typeof _.unzipWith;
+ export = unzipWith;
+}
+
+
+declare module "lodash/update" {
+ const update: typeof _.update;
+ export = update;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/updateWith" {
+ const updateWith: typeof _.updateWith;
+ export = updateWith;
+ }
+ */
+
+declare module "lodash/values" {
+ const values: typeof _.values;
+ export = values;
+}
+
+
+declare module "lodash/valuesIn" {
+ const valuesIn: typeof _.valuesIn;
+ export = valuesIn;
+}
+
+
+declare module "lodash/without" {
+ const without: typeof _.without;
+ export = without;
+}
+
+
+declare module "lodash/words" {
+ const words: typeof _.words;
+ export = words;
+}
+
+
+declare module "lodash/wrap" {
+ const wrap: typeof _.wrap;
+ export = wrap;
+}
+
+
+declare module "lodash/xor" {
+ const xor: typeof _.xor;
+ export = xor;
+}
+
+
+declare module "lodash/xorBy" {
+ const xorBy: typeof _.xorBy;
+ export = xorBy;
+}
+
+
+declare module "lodash/xorWith" {
+ const xorWith: typeof _.xorWith;
+ export = xorWith;
+}
+
+
+declare module "lodash/zip" {
+ const zip: typeof _.zip;
+ export = zip;
+}
+
+
+declare module "lodash/zipObject" {
+ const zipObject: typeof _.zipObject;
+ export = zipObject;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/zipObjectDeep" {
+ const zipObjectDeep: typeof _.zipObjectDeep;
+ export = zipObjectDeep;
+ }
+ */
+
+
+declare module "lodash/zipWith" {
+ const zipWith: typeof _.zipWith;
+ export = zipWith;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/entries" {
+ const entries: typeof _.entries;
+ export = entries;
+ }
+ */
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/entriesIn" {
+ const entriesIn: typeof _.entriesIn;
+ export = entriesIn;
+ }
+ */
+
+
+declare module "lodash/extend" {
+ const extend: typeof _.extend;
+ export = extend;
+}
+
+
+declare module "lodash/extendWith" {
+ const extendWith: typeof _.extendWith;
+ export = extendWith;
+}
+
+
+declare module "lodash/add" {
+ const add: typeof _.add;
+ export = add;
+}
+
+
+declare module "lodash/attempt" {
+ const attempt: typeof _.attempt;
+ export = attempt;
+}
+
+
+declare module "lodash/camelCase" {
+ const camelCase: typeof _.camelCase;
+ export = camelCase;
+}
+
+
+declare module "lodash/capitalize" {
+ const capitalize: typeof _.capitalize;
+ export = capitalize;
+}
+
+
+declare module "lodash/ceil" {
+ const ceil: typeof _.ceil;
+ export = ceil;
+}
+
+
+declare module "lodash/clamp" {
+ const clamp: typeof _.clamp;
+ export = clamp;
+}
+
+
+declare module "lodash/clone" {
+ const clone: typeof _.clone;
+ export = clone;
+}
+
+
+declare module "lodash/cloneDeep" {
+ const cloneDeep: typeof _.cloneDeep;
+ export = cloneDeep;
+}
+
+
+declare module "lodash/cloneDeepWith" {
+ const cloneDeepWith: typeof _.cloneDeepWith;
+ export = cloneDeepWith;
+}
+
+
+declare module "lodash/cloneWith" {
+ const cloneWith: typeof _.cloneWith;
+ export = cloneWith;
+}
+
+
+declare module "lodash/deburr" {
+ const deburr: typeof _.deburr;
+ export = deburr;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/divide" {
+ const divide: typeof _.divide;
+ export = divide;
+ }
+ */
+
+declare module "lodash/endsWith" {
+ const endsWith: typeof _.endsWith;
+ export = endsWith;
+}
+
+
+declare module "lodash/eq" {
+ const eq: typeof _.eq;
+ export = eq;
+}
+
+
+declare module "lodash/escape" {
+ const escape: typeof _.escape;
+ export = escape;
+}
+
+
+declare module "lodash/escapeRegExp" {
+ const escapeRegExp: typeof _.escapeRegExp;
+ export = escapeRegExp;
+}
+
+
+declare module "lodash/every" {
+ const every: typeof _.every;
+ export = every;
+}
+
+
+declare module "lodash/find" {
+ const find: typeof _.find;
+ export = find;
+}
+
+
+declare module "lodash/findIndex" {
+ const findIndex: typeof _.findIndex;
+ export = findIndex;
+}
+
+
+declare module "lodash/findKey" {
+ const findKey: typeof _.findKey;
+ export = findKey;
+}
+
+
+declare module "lodash/findLast" {
+ const findLast: typeof _.findLast;
+ export = findLast;
+}
+
+
+declare module "lodash/findLastIndex" {
+ const findLastIndex: typeof _.findLastIndex;
+ export = findLastIndex;
+}
+
+
+declare module "lodash/findLastKey" {
+ const findLastKey: typeof _.findLastKey;
+ export = findLastKey;
+}
+
+
+declare module "lodash/floor" {
+ const floor: typeof _.floor;
+ export = floor;
+}
+
+
+declare module "lodash/forEach" {
+ const forEach: typeof _.forEach;
+ export = forEach;
+}
+
+
+declare module "lodash/forEachRight" {
+ const forEachRight: typeof _.forEachRight;
+ export = forEachRight;
+}
+
+
+declare module "lodash/forIn" {
+ const forIn: typeof _.forIn;
+ export = forIn;
+}
+
+
+declare module "lodash/forInRight" {
+ const forInRight: typeof _.forInRight;
+ export = forInRight;
+}
+
+
+declare module "lodash/forOwn" {
+ const forOwn: typeof _.forOwn;
+ export = forOwn;
+}
+
+
+declare module "lodash/forOwnRight" {
+ const forOwnRight: typeof _.forOwnRight;
+ export = forOwnRight;
+}
+
+
+declare module "lodash/get" {
+ const get: typeof _.get;
+ export = get;
+}
+
+
+declare module "lodash/gt" {
+ const gt: typeof _.gt;
+ export = gt;
+}
+
+
+declare module "lodash/gte" {
+ const gte: typeof _.gte;
+ export = gte;
+}
+
+
+declare module "lodash/has" {
+ const has: typeof _.has;
+ export = has;
+}
+
+
+declare module "lodash/hasIn" {
+ const hasIn: typeof _.hasIn;
+ export = hasIn;
+}
+
+
+declare module "lodash/head" {
+ const head: typeof _.head;
+ export = head;
+}
+
+
+declare module "lodash/identity" {
+ const identity: typeof _.identity;
+ export = identity;
+}
+
+
+declare module "lodash/includes" {
+ const includes: typeof _.includes;
+ export = includes;
+}
+
+
+declare module "lodash/indexOf" {
+ const indexOf: typeof _.indexOf;
+ export = indexOf;
+}
+
+
+declare module "lodash/inRange" {
+ const inRange: typeof _.inRange;
+ export = inRange;
+}
+
+
+declare module "lodash/invoke" {
+ const invoke: typeof _.invoke;
+ export = invoke;
+}
+
+
+declare module "lodash/isArguments" {
+ const isArguments: typeof _.isArguments;
+ export = isArguments;
+}
+
+
+declare module "lodash/isArray" {
+ const isArray: typeof _.isArray;
+ export = isArray;
+}
+
+
+declare module "lodash/isArrayBuffer" {
+ const isArrayBuffer: typeof _.isArrayBuffer;
+ export = isArrayBuffer;
+}
+
+
+declare module "lodash/isArrayLike" {
+ const isArrayLike: typeof _.isArrayLike;
+ export = isArrayLike;
+}
+
+
+declare module "lodash/isArrayLikeObject" {
+ const isArrayLikeObject: typeof _.isArrayLikeObject;
+ export = isArrayLikeObject;
+}
+
+
+declare module "lodash/isBoolean" {
+ const isBoolean: typeof _.isBoolean;
+ export = isBoolean;
+}
+
+
+declare module "lodash/isBuffer" {
+ const isBuffer: typeof _.isBuffer;
+ export = isBuffer;
+}
+
+
+declare module "lodash/isDate" {
+ const isDate: typeof _.isDate;
+ export = isDate;
+}
+
+
+declare module "lodash/isElement" {
+ const isElement: typeof _.isElement;
+ export = isElement;
+}
+
+
+declare module "lodash/isEmpty" {
+ const isEmpty: typeof _.isEmpty;
+ export = isEmpty;
+}
+
+
+declare module "lodash/isEqual" {
+ const isEqual: typeof _.isEqual;
+ export = isEqual;
+}
+
+
+declare module "lodash/isEqualWith" {
+ const isEqualWith: typeof _.isEqualWith;
+ export = isEqualWith;
+}
+
+
+declare module "lodash/isError" {
+ const isError: typeof _.isError;
+ export = isError;
+}
+
+
+declare module "lodash/isFinite" {
+ const isFinite: typeof _.isFinite;
+ export = isFinite;
+}
+
+
+declare module "lodash/isFunction" {
+ const isFunction: typeof _.isFunction;
+ export = isFunction;
+}
+
+
+declare module "lodash/isInteger" {
+ const isInteger: typeof _.isInteger;
+ export = isInteger;
+}
+
+
+declare module "lodash/isLength" {
+ const isLength: typeof _.isLength;
+ export = isLength;
+}
+
+
+declare module "lodash/isMap" {
+ const isMap: typeof _.isMap;
+ export = isMap;
+}
+
+
+declare module "lodash/isMatch" {
+ const isMatch: typeof _.isMatch;
+ export = isMatch;
+}
+
+
+declare module "lodash/isMatchWith" {
+ const isMatchWith: typeof _.isMatchWith;
+ export = isMatchWith;
+}
+
+
+declare module "lodash/isNaN" {
+ const isNaN: typeof _.isNaN;
+ export = isNaN;
+}
+
+
+declare module "lodash/isNative" {
+ const isNative: typeof _.isNative;
+ export = isNative;
+}
+
+
+declare module "lodash/isNil" {
+ const isNil: typeof _.isNil;
+ export = isNil;
+}
+
+
+declare module "lodash/isNull" {
+ const isNull: typeof _.isNull;
+ export = isNull;
+}
+
+
+declare module "lodash/isNumber" {
+ const isNumber: typeof _.isNumber;
+ export = isNumber;
+}
+
+
+declare module "lodash/isObject" {
+ const isObject: typeof _.isObject;
+ export = isObject;
+}
+
+
+declare module "lodash/isObjectLike" {
+ const isObjectLike: typeof _.isObjectLike;
+ export = isObjectLike;
+}
+
+
+declare module "lodash/isPlainObject" {
+ const isPlainObject: typeof _.isPlainObject;
+ export = isPlainObject;
+}
+
+
+declare module "lodash/isRegExp" {
+ const isRegExp: typeof _.isRegExp;
+ export = isRegExp;
+}
+
+
+declare module "lodash/isSafeInteger" {
+ const isSafeInteger: typeof _.isSafeInteger;
+ export = isSafeInteger;
+}
+
+
+declare module "lodash/isSet" {
+ const isSet: typeof _.isSet;
+ export = isSet;
+}
+
+
+declare module "lodash/isString" {
+ const isString: typeof _.isString;
+ export = isString;
+}
+
+
+declare module "lodash/isSymbol" {
+ const isSymbol: typeof _.isSymbol;
+ export = isSymbol;
+}
+
+
+declare module "lodash/isTypedArray" {
+ const isTypedArray: typeof _.isTypedArray;
+ export = isTypedArray;
+}
+
+
+declare module "lodash/isUndefined" {
+ const isUndefined: typeof _.isUndefined;
+ export = isUndefined;
+}
+
+
+declare module "lodash/isWeakMap" {
+ const isWeakMap: typeof _.isWeakMap;
+ export = isWeakMap;
+}
+
+
+declare module "lodash/isWeakSet" {
+ const isWeakSet: typeof _.isWeakSet;
+ export = isWeakSet;
+}
+
+
+declare module "lodash/join" {
+ const join: typeof _.join;
+ export = join;
+}
+
+
+declare module "lodash/kebabCase" {
+ const kebabCase: typeof _.kebabCase;
+ export = kebabCase;
+}
+
+
+declare module "lodash/last" {
+ const last: typeof _.last;
+ export = last;
+}
+
+
+declare module "lodash/lastIndexOf" {
+ const lastIndexOf: typeof _.lastIndexOf;
+ export = lastIndexOf;
+}
+
+
+declare module "lodash/lowerCase" {
+ const lowerCase: typeof _.lowerCase;
+ export = lowerCase;
+}
+
+
+declare module "lodash/lowerFirst" {
+ const lowerFirst: typeof _.lowerFirst;
+ export = lowerFirst;
+}
+
+
+declare module "lodash/lt" {
+ const lt: typeof _.lt;
+ export = lt;
+}
+
+
+declare module "lodash/lte" {
+ const lte: typeof _.lte;
+ export = lte;
+}
+
+
+declare module "lodash/max" {
+ const max: typeof _.max;
+ export = max;
+}
+
+
+declare module "lodash/maxBy" {
+ const maxBy: typeof _.maxBy;
+ export = maxBy;
+}
+
+
+declare module "lodash/mean" {
+ const mean: typeof _.mean;
+ export = mean;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/meanBy" {
+ const meanBy: typeof _.meanBy;
+ export = meanBy;
+ }
+ */
+
+declare module "lodash/min" {
+ const min: typeof _.min;
+ export = min;
+}
+
+
+declare module "lodash/minBy" {
+ const minBy: typeof _.minBy;
+ export = minBy;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/multiply" {
+ const multiply: typeof _.multiply;
+ export = multiply;
+ }
+ */
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash/nth" {
+ const nth: typeof _.nth;
+ export = nth;
+ }
+ */
+
+declare module "lodash/noConflict" {
+ const noConflict: typeof _.noConflict;
+ export = noConflict;
+}
+
+
+declare module "lodash/noop" {
+ const noop: typeof _.noop;
+ export = noop;
+}
+
+
+declare module "lodash/now" {
+ const now: typeof _.now;
+ export = now;
+}
+
+
+declare module "lodash/pad" {
+ const pad: typeof _.pad;
+ export = pad;
+}
+
+
+declare module "lodash/padEnd" {
+ const padEnd: typeof _.padEnd;
+ export = padEnd;
+}
+
+
+declare module "lodash/padStart" {
+ const padStart: typeof _.padStart;
+ export = padStart;
+}
+
+
+declare module "lodash/parseInt" {
+ const parseInt: typeof _.parseInt;
+ export = parseInt;
+}
+
+
+declare module "lodash/random" {
+ const random: typeof _.random;
+ export = random;
+}
+
+
+declare module "lodash/reduce" {
+ const reduce: typeof _.reduce;
+ export = reduce;
+}
+
+
+declare module "lodash/reduceRight" {
+ const reduceRight: typeof _.reduceRight;
+ export = reduceRight;
+}
+
+
+declare module "lodash/repeat" {
+ const repeat: typeof _.repeat;
+ export = repeat;
+}
+
+
+declare module "lodash/replace" {
+ const replace: typeof _.replace;
+ export = replace;
+}
+
+
+declare module "lodash/result" {
+ const result: typeof _.result;
+ export = result;
+}
+
+
+declare module "lodash/round" {
+ const round: typeof _.round;
+ export = round;
+}
+
+
+declare module "lodash/runInContext" {
+ const runInContext: typeof _.runInContext;
+ export = runInContext;
+}
+
+
+declare module "lodash/sample" {
+ const sample: typeof _.sample;
+ export = sample;
+}
+
+
+declare module "lodash/size" {
+ const size: typeof _.size;
+ export = size;
+}
+
+
+declare module "lodash/snakeCase" {
+ const snakeCase: typeof _.snakeCase;
+ export = snakeCase;
+}
+
+
+declare module "lodash/some" {
+ const some: typeof _.some;
+ export = some;
+}
+
+
+declare module "lodash/sortedIndex" {
+ const sortedIndex: typeof _.sortedIndex;
+ export = sortedIndex;
+}
+
+
+declare module "lodash/sortedIndexBy" {
+ const sortedIndexBy: typeof _.sortedIndexBy;
+ export = sortedIndexBy;
+}
+
+
+declare module "lodash/sortedIndexOf" {
+ const sortedIndexOf: typeof _.sortedIndexOf;
+ export = sortedIndexOf;
+}
+
+
+declare module "lodash/sortedLastIndex" {
+ const sortedLastIndex: typeof _.sortedLastIndex;
+ export = sortedLastIndex;
+}
+
+
+declare module "lodash/sortedLastIndexBy" {
+ const sortedLastIndexBy: typeof _.sortedLastIndexBy;
+ export = sortedLastIndexBy;
+}
+
+
+declare module "lodash/sortedLastIndexOf" {
+ const sortedLastIndexOf: typeof _.sortedLastIndexOf;
+ export = sortedLastIndexOf;
+}
+
+
+declare module "lodash/startCase" {
+ const startCase: typeof _.startCase;
+ export = startCase;
+}
+
+
+declare module "lodash/startsWith" {
+ const startsWith: typeof _.startsWith;
+ export = startsWith;
+}
+
+
+declare module "lodash/subtract" {
+ const subtract: typeof _.subtract;
+ export = subtract;
+}
+
+
+declare module "lodash/sum" {
+ const sum: typeof _.sum;
+ export = sum;
+}
+
+
+declare module "lodash/sumBy" {
+ const sumBy: typeof _.sumBy;
+ export = sumBy;
+}
+
+
+declare module "lodash/template" {
+ const template: typeof _.template;
+ export = template;
+}
+
+
+declare module "lodash/times" {
+ const times: typeof _.times;
+ export = times;
+}
+
+
+declare module "lodash/toInteger" {
+ const toInteger: typeof _.toInteger;
+ export = toInteger;
+}
+
+
+declare module "lodash/toLength" {
+ const toLength: typeof _.toLength;
+ export = toLength;
+}
+
+
+declare module "lodash/toLower" {
+ const toLower: typeof _.toLower;
+ export = toLower;
+}
+
+
+declare module "lodash/toNumber" {
+ const toNumber: typeof _.toNumber;
+ export = toNumber;
+}
+
+
+declare module "lodash/toSafeInteger" {
+ const toSafeInteger: typeof _.toSafeInteger;
+ export = toSafeInteger;
+}
+
+
+declare module "lodash/toString" {
+ const toString: typeof _.toString;
+ export = toString;
+}
+
+
+declare module "lodash/toUpper" {
+ const toUpper: typeof _.toUpper;
+ export = toUpper;
+}
+
+
+declare module "lodash/trim" {
+ const trim: typeof _.trim;
+ export = trim;
+}
+
+
+declare module "lodash/trimEnd" {
+ const trimEnd: typeof _.trimEnd;
+ export = trimEnd;
+}
+
+
+declare module "lodash/trimStart" {
+ const trimStart: typeof _.trimStart;
+ export = trimStart;
+}
+
+
+declare module "lodash/truncate" {
+ const truncate: typeof _.truncate;
+ export = truncate;
+}
+
+
+declare module "lodash/unescape" {
+ const unescape: typeof _.unescape;
+ export = unescape;
+}
+
+
+declare module "lodash/uniqueId" {
+ const uniqueId: typeof _.uniqueId;
+ export = uniqueId;
+}
+
+
+declare module "lodash/upperCase" {
+ const upperCase: typeof _.upperCase;
+ export = upperCase;
+}
+
+
+declare module "lodash/upperFirst" {
+ const upperFirst: typeof _.upperFirst;
+ export = upperFirst;
+}
+
+
+declare module "lodash/each" {
+ const each: typeof _.each;
+ export = each;
+}
+
+
+declare module "lodash/eachRight" {
+ const eachRight: typeof _.eachRight;
+ export = eachRight;
+}
+
+
+declare module "lodash/first" {
+ const first: typeof _.first;
+ export = first;
+}
+
+declare module "lodash/fp" {
+ export = _;
+}
+
+declare module "lodash" {
+ export = _;
+}
+
+/*************************************************
+ * *
+ * The lodash method _.XXX exported as a module. *
+ * *
+ *************************************************/
+
+
+declare module "lodash.after" {
+ const after: typeof _.after;
+ export = after;
+}
+
+
+declare module "lodash.ary" {
+ const ary: typeof _.ary;
+ export = ary;
+}
+
+
+declare module "lodash.assign" {
+ const assign: typeof _.assign;
+ export = assign;
+}
+
+
+declare module "lodash.assignIn" {
+ const assignIn: typeof _.assignIn;
+ export = assignIn;
+}
+
+
+declare module "lodash.assignInWith" {
+ const assignInWith: typeof _.assignInWith;
+ export = assignInWith;
+}
+
+
+declare module "lodash.assignWith" {
+ const assignWith: typeof _.assignWith;
+ export = assignWith;
+}
+
+
+declare module "lodash.at" {
+ const at: typeof _.at;
+ export = at;
+}
+
+
+declare module "lodash.before" {
+ const before: typeof _.before;
+ export = before;
+}
+
+
+declare module "lodash.bind" {
+ const bind: typeof _.bind;
+ export = bind;
+}
+
+
+declare module "lodash.bindAll" {
+ const bindAll: typeof _.bindAll;
+ export = bindAll;
+}
+
+
+declare module "lodash.bindKey" {
+ const bindKey: typeof _.bindKey;
+ export = bindKey;
+}
+
+
+declare module "lodash.castArray" {
+ const castArray: typeof _.castArray;
+ export = castArray;
+}
+
+
+declare module "lodash.chain" {
+ const chain: typeof _.chain;
+ export = chain;
+}
+
+
+declare module "lodash.chunk" {
+ const chunk: typeof _.chunk;
+ export = chunk;
+}
+
+
+declare module "lodash.compact" {
+ const compact: typeof _.compact;
+ export = compact;
+}
+
+
+declare module "lodash.concat" {
+ const concat: typeof _.concat;
+ export = concat;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.cond" {
+ const cond: typeof _.cond;
+ export = cond;
+ }
+ */
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.conforms" {
+ const conforms: typeof _.conforms;
+ export = conforms;
+ }
+ */
+
+declare module "lodash.constant" {
+ const constant: typeof _.constant;
+ export = constant;
+}
+
+
+declare module "lodash.countBy" {
+ const countBy: typeof _.countBy;
+ export = countBy;
+}
+
+
+declare module "lodash.create" {
+ const create: typeof _.create;
+ export = create;
+}
+
+
+declare module "lodash.curry" {
+ const curry: typeof _.curry;
+ export = curry;
+}
+
+
+declare module "lodash.curryRight" {
+ const curryRight: typeof _.curryRight;
+ export = curryRight;
+}
+
+
+declare module "lodash.debounce" {
+ const debounce: typeof _.debounce;
+ export = debounce;
+}
+
+
+declare module "lodash.defaults" {
+ const defaults: typeof _.defaults;
+ export = defaults;
+}
+
+
+declare module "lodash.defaultsDeep" {
+ const defaultsDeep: typeof _.defaultsDeep;
+ export = defaultsDeep;
+}
+
+
+declare module "lodash.defer" {
+ const defer: typeof _.defer;
+ export = defer;
+}
+
+
+declare module "lodash.delay" {
+ const delay: typeof _.delay;
+ export = delay;
+}
+
+
+declare module "lodash.difference" {
+ const difference: typeof _.difference;
+ export = difference;
+}
+
+
+declare module "lodash.differenceBy" {
+ const differenceBy: typeof _.differenceBy;
+ export = differenceBy;
+}
+
+
+declare module "lodash.differenceWith" {
+ const differenceWith: typeof _.differenceWith;
+ export = differenceWith;
+}
+
+
+declare module "lodash.drop" {
+ const drop: typeof _.drop;
+ export = drop;
+}
+
+
+declare module "lodash.dropRight" {
+ const dropRight: typeof _.dropRight;
+ export = dropRight;
+}
+
+
+declare module "lodash.dropRightWhile" {
+ const dropRightWhile: typeof _.dropRightWhile;
+ export = dropRightWhile;
+}
+
+
+declare module "lodash.dropWhile" {
+ const dropWhile: typeof _.dropWhile;
+ export = dropWhile;
+}
+
+
+declare module "lodash.fill" {
+ const fill: typeof _.fill;
+ export = fill;
+}
+
+
+declare module "lodash.filter" {
+ const filter: typeof _.filter;
+ export = filter;
+}
+
+
+declare module "lodash.flatMap" {
+ const flatMap: typeof _.flatMap;
+ export = flatMap;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.flatMapDeep" {
+ const flatMapDeep: typeof _.flatMapDeep;
+ export = flatMapDeep;
+ }
+ */
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.flatMapDepth" {
+ const flatMapDepth: typeof _.flatMapDepth;
+ export = flatMapDepth;
+ }
+ */
+
+declare module "lodash.flatten" {
+ const flatten: typeof _.flatten;
+ export = flatten;
+}
+
+
+declare module "lodash.flattenDeep" {
+ const flattenDeep: typeof _.flattenDeep;
+ export = flattenDeep;
+}
+
+declare module "lodash.flattenDepth" {
+ const flattenDepth: typeof _.flattenDepth;
+ export = flattenDepth;
+}
+
+declare module "lodash.flip" {
+ const flip: typeof _.flip;
+ export = flip;
+}
+
+
+declare module "lodash.flow" {
+ const flow: typeof _.flow;
+ export = flow;
+}
+
+
+declare module "lodash.flowRight" {
+ const flowRight: typeof _.flowRight;
+ export = flowRight;
+}
+
+
+declare module "lodash.fromPairs" {
+ const fromPairs: typeof _.fromPairs;
+ export = fromPairs;
+}
+
+
+declare module "lodash.functions" {
+ const functions: typeof _.functions;
+ export = functions;
+}
+
+
+declare module "lodash.functionsIn" {
+ const functionsIn: typeof _.functionsIn;
+ export = functionsIn;
+}
+
+
+declare module "lodash.groupBy" {
+ const groupBy: typeof _.groupBy;
+ export = groupBy;
+}
+
+
+declare module "lodash.initial" {
+ const initial: typeof _.initial;
+ export = initial;
+}
+
+
+declare module "lodash.intersection" {
+ const intersection: typeof _.intersection;
+ export = intersection;
+}
+
+
+declare module "lodash.intersectionBy" {
+ const intersectionBy: typeof _.intersectionBy;
+ export = intersectionBy;
+}
+
+
+declare module "lodash.intersectionWith" {
+ const intersectionWith: typeof _.intersectionWith;
+ export = intersectionWith;
+}
+
+
+declare module "lodash.invert" {
+ const invert: typeof _.invert;
+ export = invert;
+}
+
+
+declare module "lodash.invertBy" {
+ const invertBy: typeof _.invertBy;
+ export = invertBy;
+}
+
+
+declare module "lodash.invokeMap" {
+ const invokeMap: typeof _.invokeMap;
+ export = invokeMap;
+}
+
+
+declare module "lodash.iteratee" {
+ const iteratee: typeof _.iteratee;
+ export = iteratee;
+}
+
+
+declare module "lodash.keyBy" {
+ const keyBy: typeof _.keyBy;
+ export = keyBy;
+}
+
+
+declare module "lodash.keys" {
+ const keys: typeof _.keys;
+ export = keys;
+}
+
+
+declare module "lodash.keysIn" {
+ const keysIn: typeof _.keysIn;
+ export = keysIn;
+}
+
+
+declare module "lodash.map" {
+ const map: typeof _.map;
+ export = map;
+}
+
+
+declare module "lodash.mapKeys" {
+ const mapKeys: typeof _.mapKeys;
+ export = mapKeys;
+}
+
+
+declare module "lodash.mapValues" {
+ const mapValues: typeof _.mapValues;
+ export = mapValues;
+}
+
+
+declare module "lodash.matches" {
+ const matches: typeof _.matches;
+ export = matches;
+}
+
+
+declare module "lodash.matchesProperty" {
+ const matchesProperty: typeof _.matchesProperty;
+ export = matchesProperty;
+}
+
+
+declare module "lodash.memoize" {
+ const memoize: typeof _.memoize;
+ export = memoize;
+}
+
+
+declare module "lodash.merge" {
+ const merge: typeof _.merge;
+ export = merge;
+}
+
+
+declare module "lodash.mergeWith" {
+ const mergeWith: typeof _.mergeWith;
+ export = mergeWith;
+}
+
+
+declare module "lodash.method" {
+ const method: typeof _.method;
+ export = method;
+}
+
+
+declare module "lodash.methodOf" {
+ const methodOf: typeof _.methodOf;
+ export = methodOf;
+}
+
+
+declare module "lodash.mixin" {
+ const mixin: typeof _.mixin;
+ export = mixin;
+}
+
+
+declare module "lodash.negate" {
+ const negate: typeof _.negate;
+ export = negate;
+}
+
+
+declare module "lodash.nthArg" {
+ const nthArg: typeof _.nthArg;
+ export = nthArg;
+}
+
+
+declare module "lodash.omit" {
+ const omit: typeof _.omit;
+ export = omit;
+}
+
+
+declare module "lodash.omitBy" {
+ const omitBy: typeof _.omitBy;
+ export = omitBy;
+}
+
+
+declare module "lodash.once" {
+ const once: typeof _.once;
+ export = once;
+}
+
+
+declare module "lodash.orderBy" {
+ const orderBy: typeof _.orderBy;
+ export = orderBy;
+}
+
+
+declare module "lodash.over" {
+ const over: typeof _.over;
+ export = over;
+}
+
+
+declare module "lodash.overArgs" {
+ const overArgs: typeof _.overArgs;
+ export = overArgs;
+}
+
+
+declare module "lodash.overEvery" {
+ const overEvery: typeof _.overEvery;
+ export = overEvery;
+}
+
+
+declare module "lodash.overSome" {
+ const overSome: typeof _.overSome;
+ export = overSome;
+}
+
+
+declare module "lodash.partial" {
+ const partial: typeof _.partial;
+ export = partial;
+}
+
+
+declare module "lodash.partialRight" {
+ const partialRight: typeof _.partialRight;
+ export = partialRight;
+}
+
+
+declare module "lodash.partition" {
+ const partition: typeof _.partition;
+ export = partition;
+}
+
+
+declare module "lodash.pick" {
+ const pick: typeof _.pick;
+ export = pick;
+}
+
+
+declare module "lodash.pickBy" {
+ const pickBy: typeof _.pickBy;
+ export = pickBy;
+}
+
+
+declare module "lodash.property" {
+ const property: typeof _.property;
+ export = property;
+}
+
+
+declare module "lodash.propertyOf" {
+ const propertyOf: typeof _.propertyOf;
+ export = propertyOf;
+}
+
+
+declare module "lodash.pull" {
+ const pull: typeof _.pull;
+ export = pull;
+}
+
+
+declare module "lodash.pullAll" {
+ const pullAll: typeof _.pullAll;
+ export = pullAll;
+}
+
+
+declare module "lodash.pullAllBy" {
+ const pullAllBy: typeof _.pullAllBy;
+ export = pullAllBy;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.pullAllWith" {
+ const pullAllWith: typeof _.pullAllWith;
+ export = pullAllWith;
+ }
+ */
+
+declare module "lodash.pullAt" {
+ const pullAt: typeof _.pullAt;
+ export = pullAt;
+}
+
+
+declare module "lodash.range" {
+ const range: typeof _.range;
+ export = range;
+}
+
+
+declare module "lodash.rangeRight" {
+ const rangeRight: typeof _.rangeRight;
+ export = rangeRight;
+}
+
+
+declare module "lodash.rearg" {
+ const rearg: typeof _.rearg;
+ export = rearg;
+}
+
+
+declare module "lodash.reject" {
+ const reject: typeof _.reject;
+ export = reject;
+}
+
+
+declare module "lodash.remove" {
+ const remove: typeof _.remove;
+ export = remove;
+}
+
+
+declare module "lodash.rest" {
+ const rest: typeof _.rest;
+ export = rest;
+}
+
+
+declare module "lodash.reverse" {
+ const reverse: typeof _.reverse;
+ export = reverse;
+}
+
+
+declare module "lodash.sampleSize" {
+ const sampleSize: typeof _.sampleSize;
+ export = sampleSize;
+}
+
+
+declare module "lodash.set" {
+ const set: typeof _.set;
+ export = set;
+}
+
+
+declare module "lodash.setWith" {
+ const setWith: typeof _.setWith;
+ export = setWith;
+}
+
+
+declare module "lodash.shuffle" {
+ const shuffle: typeof _.shuffle;
+ export = shuffle;
+}
+
+
+declare module "lodash.slice" {
+ const slice: typeof _.slice;
+ export = slice;
+}
+
+
+declare module "lodash.sortBy" {
+ const sortBy: typeof _.sortBy;
+ export = sortBy;
+}
+
+
+declare module "lodash.sortedUniq" {
+ const sortedUniq: typeof _.sortedUniq;
+ export = sortedUniq;
+}
+
+
+declare module "lodash.sortedUniqBy" {
+ const sortedUniqBy: typeof _.sortedUniqBy;
+ export = sortedUniqBy;
+}
+
+
+declare module "lodash.split" {
+ const split: typeof _.split;
+ export = split;
+}
+
+
+declare module "lodash.spread" {
+ const spread: typeof _.spread;
+ export = spread;
+}
+
+
+declare module "lodash.tail" {
+ const tail: typeof _.tail;
+ export = tail;
+}
+
+
+declare module "lodash.take" {
+ const take: typeof _.take;
+ export = take;
+}
+
+
+declare module "lodash.takeRight" {
+ const takeRight: typeof _.takeRight;
+ export = takeRight;
+}
+
+
+declare module "lodash.takeRightWhile" {
+ const takeRightWhile: typeof _.takeRightWhile;
+ export = takeRightWhile;
+}
+
+
+declare module "lodash.takeWhile" {
+ const takeWhile: typeof _.takeWhile;
+ export = takeWhile;
+}
+
+
+declare module "lodash.tap" {
+ const tap: typeof _.tap;
+ export = tap;
+}
+
+
+declare module "lodash.throttle" {
+ const throttle: typeof _.throttle;
+ export = throttle;
+}
+
+
+declare module "lodash.thru" {
+ const thru: typeof _.thru;
+ export = thru;
+}
+
+
+declare module "lodash.toArray" {
+ const toArray: typeof _.toArray;
+ export = toArray;
+}
+
+
+declare module "lodash.toPairs" {
+ const toPairs: typeof _.toPairs;
+ export = toPairs;
+}
+
+
+declare module "lodash.toPairsIn" {
+ const toPairsIn: typeof _.toPairsIn;
+ export = toPairsIn;
+}
+
+
+declare module "lodash.toPath" {
+ const toPath: typeof _.toPath;
+ export = toPath;
+}
+
+
+declare module "lodash.toPlainObject" {
+ const toPlainObject: typeof _.toPlainObject;
+ export = toPlainObject;
+}
+
+
+declare module "lodash.transform" {
+ const transform: typeof _.transform;
+ export = transform;
+}
+
+
+declare module "lodash.unary" {
+ const unary: typeof _.unary;
+ export = unary;
+}
+
+
+declare module "lodash.union" {
+ const union: typeof _.union;
+ export = union;
+}
+
+
+declare module "lodash.unionBy" {
+ const unionBy: typeof _.unionBy;
+ export = unionBy;
+}
+
+
+declare module "lodash.unionWith" {
+ const unionWith: typeof _.unionWith;
+ export = unionWith;
+}
+
+
+declare module "lodash.uniq" {
+ const uniq: typeof _.uniq;
+ export = uniq;
+}
+
+
+declare module "lodash.uniqBy" {
+ const uniqBy: typeof _.uniqBy;
+ export = uniqBy;
+}
+
+
+declare module "lodash.uniqWith" {
+ const uniqWith: typeof _.uniqWith;
+ export = uniqWith;
+}
+
+
+declare module "lodash.unset" {
+ const unset: typeof _.unset;
+ export = unset;
+}
+
+
+declare module "lodash.unzip" {
+ const unzip: typeof _.unzip;
+ export = unzip;
+}
+
+
+declare module "lodash.unzipWith" {
+ const unzipWith: typeof _.unzipWith;
+ export = unzipWith;
+}
+
+
+declare module "lodash.update" {
+ const update: typeof _.update;
+ export = update;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.updateWith" {
+ const updateWith: typeof _.updateWith;
+ export = updateWith;
+ }
+ */
+
+declare module "lodash.values" {
+ const values: typeof _.values;
+ export = values;
+}
+
+
+declare module "lodash.valuesIn" {
+ const valuesIn: typeof _.valuesIn;
+ export = valuesIn;
+}
+
+
+declare module "lodash.without" {
+ const without: typeof _.without;
+ export = without;
+}
+
+
+declare module "lodash.words" {
+ const words: typeof _.words;
+ export = words;
+}
+
+
+declare module "lodash.wrap" {
+ const wrap: typeof _.wrap;
+ export = wrap;
+}
+
+
+declare module "lodash.xor" {
+ const xor: typeof _.xor;
+ export = xor;
+}
+
+
+declare module "lodash.xorBy" {
+ const xorBy: typeof _.xorBy;
+ export = xorBy;
+}
+
+
+declare module "lodash.xorWith" {
+ const xorWith: typeof _.xorWith;
+ export = xorWith;
+}
+
+
+declare module "lodash.zip" {
+ const zip: typeof _.zip;
+ export = zip;
+}
+
+
+declare module "lodash.zipObject" {
+ const zipObject: typeof _.zipObject;
+ export = zipObject;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.zipObjectDeep" {
+ const zipObjectDeep: typeof _.zipObjectDeep;
+ export = zipObjectDeep;
+ }
+ */
+
+
+declare module "lodash.zipWith" {
+ const zipWith: typeof _.zipWith;
+ export = zipWith;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.entries" {
+ const entries: typeof _.entries;
+ export = entries;
+ }
+ */
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.entriesIn" {
+ const entriesIn: typeof _.entriesIn;
+ export = entriesIn;
+ }
+ */
+
+
+declare module "lodash.extend" {
+ const extend: typeof _.extend;
+ export = extend;
+}
+
+
+declare module "lodash.extendWith" {
+ const extendWith: typeof _.extendWith;
+ export = extendWith;
+}
+
+
+declare module "lodash.add" {
+ const add: typeof _.add;
+ export = add;
+}
+
+
+declare module "lodash.attempt" {
+ const attempt: typeof _.attempt;
+ export = attempt;
+}
+
+
+declare module "lodash.camelCase" {
+ const camelCase: typeof _.camelCase;
+ export = camelCase;
+}
+
+
+declare module "lodash.capitalize" {
+ const capitalize: typeof _.capitalize;
+ export = capitalize;
+}
+
+
+declare module "lodash.ceil" {
+ const ceil: typeof _.ceil;
+ export = ceil;
+}
+
+
+declare module "lodash.clamp" {
+ const clamp: typeof _.clamp;
+ export = clamp;
+}
+
+
+declare module "lodash.clone" {
+ const clone: typeof _.clone;
+ export = clone;
+}
+
+
+declare module "lodash.cloneDeep" {
+ const cloneDeep: typeof _.cloneDeep;
+ export = cloneDeep;
+}
+
+
+declare module "lodash.cloneDeepWith" {
+ const cloneDeepWith: typeof _.cloneDeepWith;
+ export = cloneDeepWith;
+}
+
+
+declare module "lodash.cloneWith" {
+ const cloneWith: typeof _.cloneWith;
+ export = cloneWith;
+}
+
+
+declare module "lodash.deburr" {
+ const deburr: typeof _.deburr;
+ export = deburr;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.divide" {
+ const divide: typeof _.divide;
+ export = divide;
+ }
+ */
+
+declare module "lodash.endsWith" {
+ const endsWith: typeof _.endsWith;
+ export = endsWith;
+}
+
+
+declare module "lodash.eq" {
+ const eq: typeof _.eq;
+ export = eq;
+}
+
+
+declare module "lodash.escape" {
+ const escape: typeof _.escape;
+ export = escape;
+}
+
+
+declare module "lodash.escapeRegExp" {
+ const escapeRegExp: typeof _.escapeRegExp;
+ export = escapeRegExp;
+}
+
+
+declare module "lodash.every" {
+ const every: typeof _.every;
+ export = every;
+}
+
+
+declare module "lodash.find" {
+ const find: typeof _.find;
+ export = find;
+}
+
+
+declare module "lodash.findIndex" {
+ const findIndex: typeof _.findIndex;
+ export = findIndex;
+}
+
+
+declare module "lodash.findKey" {
+ const findKey: typeof _.findKey;
+ export = findKey;
+}
+
+
+declare module "lodash.findLast" {
+ const findLast: typeof _.findLast;
+ export = findLast;
+}
+
+
+declare module "lodash.findLastIndex" {
+ const findLastIndex: typeof _.findLastIndex;
+ export = findLastIndex;
+}
+
+
+declare module "lodash.findLastKey" {
+ const findLastKey: typeof _.findLastKey;
+ export = findLastKey;
+}
+
+
+declare module "lodash.floor" {
+ const floor: typeof _.floor;
+ export = floor;
+}
+
+
+declare module "lodash.forEach" {
+ const forEach: typeof _.forEach;
+ export = forEach;
+}
+
+
+declare module "lodash.forEachRight" {
+ const forEachRight: typeof _.forEachRight;
+ export = forEachRight;
+}
+
+
+declare module "lodash.forIn" {
+ const forIn: typeof _.forIn;
+ export = forIn;
+}
+
+
+declare module "lodash.forInRight" {
+ const forInRight: typeof _.forInRight;
+ export = forInRight;
+}
+
+
+declare module "lodash.forOwn" {
+ const forOwn: typeof _.forOwn;
+ export = forOwn;
+}
+
+
+declare module "lodash.forOwnRight" {
+ const forOwnRight: typeof _.forOwnRight;
+ export = forOwnRight;
+}
+
+
+declare module "lodash.get" {
+ const get: typeof _.get;
+ export = get;
+}
+
+
+declare module "lodash.gt" {
+ const gt: typeof _.gt;
+ export = gt;
+}
+
+
+declare module "lodash.gte" {
+ const gte: typeof _.gte;
+ export = gte;
+}
+
+
+declare module "lodash.has" {
+ const has: typeof _.has;
+ export = has;
+}
+
+
+declare module "lodash.hasIn" {
+ const hasIn: typeof _.hasIn;
+ export = hasIn;
+}
+
+
+declare module "lodash.head" {
+ const head: typeof _.head;
+ export = head;
+}
+
+
+declare module "lodash.identity" {
+ const identity: typeof _.identity;
+ export = identity;
+}
+
+
+declare module "lodash.includes" {
+ const includes: typeof _.includes;
+ export = includes;
+}
+
+
+declare module "lodash.indexOf" {
+ const indexOf: typeof _.indexOf;
+ export = indexOf;
+}
+
+
+declare module "lodash.inRange" {
+ const inRange: typeof _.inRange;
+ export = inRange;
+}
+
+
+declare module "lodash.invoke" {
+ const invoke: typeof _.invoke;
+ export = invoke;
+}
+
+
+declare module "lodash.isArguments" {
+ const isArguments: typeof _.isArguments;
+ export = isArguments;
+}
+
+
+declare module "lodash.isArray" {
+ const isArray: typeof _.isArray;
+ export = isArray;
+}
+
+
+declare module "lodash.isArrayBuffer" {
+ const isArrayBuffer: typeof _.isArrayBuffer;
+ export = isArrayBuffer;
+}
+
+
+declare module "lodash.isArrayLike" {
+ const isArrayLike: typeof _.isArrayLike;
+ export = isArrayLike;
+}
+
+
+declare module "lodash.isArrayLikeObject" {
+ const isArrayLikeObject: typeof _.isArrayLikeObject;
+ export = isArrayLikeObject;
+}
+
+
+declare module "lodash.isBoolean" {
+ const isBoolean: typeof _.isBoolean;
+ export = isBoolean;
+}
+
+
+declare module "lodash.isBuffer" {
+ const isBuffer: typeof _.isBuffer;
+ export = isBuffer;
+}
+
+
+declare module "lodash.isDate" {
+ const isDate: typeof _.isDate;
+ export = isDate;
+}
+
+
+declare module "lodash.isElement" {
+ const isElement: typeof _.isElement;
+ export = isElement;
+}
+
+
+declare module "lodash.isEmpty" {
+ const isEmpty: typeof _.isEmpty;
+ export = isEmpty;
+}
+
+
+declare module "lodash.isEqual" {
+ const isEqual: typeof _.isEqual;
+ export = isEqual;
+}
+
+
+declare module "lodash.isEqualWith" {
+ const isEqualWith: typeof _.isEqualWith;
+ export = isEqualWith;
+}
+
+
+declare module "lodash.isError" {
+ const isError: typeof _.isError;
+ export = isError;
+}
+
+
+declare module "lodash.isFinite" {
+ const isFinite: typeof _.isFinite;
+ export = isFinite;
+}
+
+
+declare module "lodash.isFunction" {
+ const isFunction: typeof _.isFunction;
+ export = isFunction;
+}
+
+
+declare module "lodash.isInteger" {
+ const isInteger: typeof _.isInteger;
+ export = isInteger;
+}
+
+
+declare module "lodash.isLength" {
+ const isLength: typeof _.isLength;
+ export = isLength;
+}
+
+
+declare module "lodash.isMap" {
+ const isMap: typeof _.isMap;
+ export = isMap;
+}
+
+
+declare module "lodash.isMatch" {
+ const isMatch: typeof _.isMatch;
+ export = isMatch;
+}
+
+
+declare module "lodash.isMatchWith" {
+ const isMatchWith: typeof _.isMatchWith;
+ export = isMatchWith;
+}
+
+
+declare module "lodash.isNaN" {
+ const isNaN: typeof _.isNaN;
+ export = isNaN;
+}
+
+
+declare module "lodash.isNative" {
+ const isNative: typeof _.isNative;
+ export = isNative;
+}
+
+
+declare module "lodash.isNil" {
+ const isNil: typeof _.isNil;
+ export = isNil;
+}
+
+
+declare module "lodash.isNull" {
+ const isNull: typeof _.isNull;
+ export = isNull;
+}
+
+
+declare module "lodash.isNumber" {
+ const isNumber: typeof _.isNumber;
+ export = isNumber;
+}
+
+
+declare module "lodash.isObject" {
+ const isObject: typeof _.isObject;
+ export = isObject;
+}
+
+
+declare module "lodash.isObjectLike" {
+ const isObjectLike: typeof _.isObjectLike;
+ export = isObjectLike;
+}
+
+
+declare module "lodash.isPlainObject" {
+ const isPlainObject: typeof _.isPlainObject;
+ export = isPlainObject;
+}
+
+
+declare module "lodash.isRegExp" {
+ const isRegExp: typeof _.isRegExp;
+ export = isRegExp;
+}
+
+
+declare module "lodash.isSafeInteger" {
+ const isSafeInteger: typeof _.isSafeInteger;
+ export = isSafeInteger;
+}
+
+
+declare module "lodash.isSet" {
+ const isSet: typeof _.isSet;
+ export = isSet;
+}
+
+
+declare module "lodash.isString" {
+ const isString: typeof _.isString;
+ export = isString;
+}
+
+
+declare module "lodash.isSymbol" {
+ const isSymbol: typeof _.isSymbol;
+ export = isSymbol;
+}
+
+
+declare module "lodash.isTypedArray" {
+ const isTypedArray: typeof _.isTypedArray;
+ export = isTypedArray;
+}
+
+
+declare module "lodash.isUndefined" {
+ const isUndefined: typeof _.isUndefined;
+ export = isUndefined;
+}
+
+
+declare module "lodash.isWeakMap" {
+ const isWeakMap: typeof _.isWeakMap;
+ export = isWeakMap;
+}
+
+
+declare module "lodash.isWeakSet" {
+ const isWeakSet: typeof _.isWeakSet;
+ export = isWeakSet;
+}
+
+
+declare module "lodash.join" {
+ const join: typeof _.join;
+ export = join;
+}
+
+
+declare module "lodash.kebabCase" {
+ const kebabCase: typeof _.kebabCase;
+ export = kebabCase;
+}
+
+
+declare module "lodash.last" {
+ const last: typeof _.last;
+ export = last;
+}
+
+
+declare module "lodash.lastIndexOf" {
+ const lastIndexOf: typeof _.lastIndexOf;
+ export = lastIndexOf;
+}
+
+
+declare module "lodash.lowerCase" {
+ const lowerCase: typeof _.lowerCase;
+ export = lowerCase;
+}
+
+
+declare module "lodash.lowerFirst" {
+ const lowerFirst: typeof _.lowerFirst;
+ export = lowerFirst;
+}
+
+
+declare module "lodash.lt" {
+ const lt: typeof _.lt;
+ export = lt;
+}
+
+
+declare module "lodash.lte" {
+ const lte: typeof _.lte;
+ export = lte;
+}
+
+
+declare module "lodash.max" {
+ const max: typeof _.max;
+ export = max;
+}
+
+
+declare module "lodash.maxBy" {
+ const maxBy: typeof _.maxBy;
+ export = maxBy;
+}
+
+
+declare module "lodash.mean" {
+ const mean: typeof _.mean;
+ export = mean;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.meanBy" {
+ const meanBy: typeof _.meanBy;
+ export = meanBy;
+ }
+ */
+
+declare module "lodash.min" {
+ const min: typeof _.min;
+ export = min;
+}
+
+
+declare module "lodash.minBy" {
+ const minBy: typeof _.minBy;
+ export = minBy;
+}
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.multiply" {
+ const multiply: typeof _.multiply;
+ export = multiply;
+ }
+ */
+
+/**
+ * uncoment it if definition exists
+ */
+/*
+ declare module "lodash.nth" {
+ const nth: typeof _.nth;
+ export = nth;
+ }
+ */
+
+declare module "lodash.noConflict" {
+ const noConflict: typeof _.noConflict;
+ export = noConflict;
+}
+
+
+declare module "lodash.noop" {
+ const noop: typeof _.noop;
+ export = noop;
+}
+
+
+declare module "lodash.now" {
+ const now: typeof _.now;
+ export = now;
+}
+
+
+declare module "lodash.pad" {
+ const pad: typeof _.pad;
+ export = pad;
+}
+
+
+declare module "lodash.padEnd" {
+ const padEnd: typeof _.padEnd;
+ export = padEnd;
+}
+
+
+declare module "lodash.padStart" {
+ const padStart: typeof _.padStart;
+ export = padStart;
+}
+
+
+declare module "lodash.parseInt" {
+ const parseInt: typeof _.parseInt;
+ export = parseInt;
+}
+
+
+declare module "lodash.random" {
+ const random: typeof _.random;
+ export = random;
+}
+
+
+declare module "lodash.reduce" {
+ const reduce: typeof _.reduce;
+ export = reduce;
+}
+
+
+declare module "lodash.reduceRight" {
+ const reduceRight: typeof _.reduceRight;
+ export = reduceRight;
+}
+
+
+declare module "lodash.repeat" {
+ const repeat: typeof _.repeat;
+ export = repeat;
+}
+
+
+declare module "lodash.replace" {
+ const replace: typeof _.replace;
+ export = replace;
+}
+
+
+declare module "lodash.result" {
+ const result: typeof _.result;
+ export = result;
+}
+
+
+declare module "lodash.round" {
+ const round: typeof _.round;
+ export = round;
+}
+
+
+declare module "lodash.runInContext" {
+ const runInContext: typeof _.runInContext;
+ export = runInContext;
+}
+
+
+declare module "lodash.sample" {
+ const sample: typeof _.sample;
+ export = sample;
+}
+
+
+declare module "lodash.size" {
+ const size: typeof _.size;
+ export = size;
+}
+
+
+declare module "lodash.snakeCase" {
+ const snakeCase: typeof _.snakeCase;
+ export = snakeCase;
+}
+
+
+declare module "lodash.some" {
+ const some: typeof _.some;
+ export = some;
+}
+
+
+declare module "lodash.sortedIndex" {
+ const sortedIndex: typeof _.sortedIndex;
+ export = sortedIndex;
+}
+
+
+declare module "lodash.sortedIndexBy" {
+ const sortedIndexBy: typeof _.sortedIndexBy;
+ export = sortedIndexBy;
+}
+
+
+declare module "lodash.sortedIndexOf" {
+ const sortedIndexOf: typeof _.sortedIndexOf;
+ export = sortedIndexOf;
+}
+
+
+declare module "lodash.sortedLastIndex" {
+ const sortedLastIndex: typeof _.sortedLastIndex;
+ export = sortedLastIndex;
+}
+
+
+declare module "lodash.sortedLastIndexBy" {
+ const sortedLastIndexBy: typeof _.sortedLastIndexBy;
+ export = sortedLastIndexBy;
+}
+
+
+declare module "lodash.sortedLastIndexOf" {
+ const sortedLastIndexOf: typeof _.sortedLastIndexOf;
+ export = sortedLastIndexOf;
+}
+
+
+declare module "lodash.startCase" {
+ const startCase: typeof _.startCase;
+ export = startCase;
+}
+
+
+declare module "lodash.startsWith" {
+ const startsWith: typeof _.startsWith;
+ export = startsWith;
+}
+
+
+declare module "lodash.subtract" {
+ const subtract: typeof _.subtract;
+ export = subtract;
+}
+
+
+declare module "lodash.sum" {
+ const sum: typeof _.sum;
+ export = sum;
+}
+
+
+declare module "lodash.sumBy" {
+ const sumBy: typeof _.sumBy;
+ export = sumBy;
+}
+
+
+declare module "lodash.template" {
+ const template: typeof _.template;
+ export = template;
+}
+
+
+declare module "lodash.times" {
+ const times: typeof _.times;
+ export = times;
+}
+
+
+declare module "lodash.toInteger" {
+ const toInteger: typeof _.toInteger;
+ export = toInteger;
+}
+
+
+declare module "lodash.toLength" {
+ const toLength: typeof _.toLength;
+ export = toLength;
+}
+
+
+declare module "lodash.toLower" {
+ const toLower: typeof _.toLower;
+ export = toLower;
+}
+
+
+declare module "lodash.toNumber" {
+ const toNumber: typeof _.toNumber;
+ export = toNumber;
+}
+
+
+declare module "lodash.toSafeInteger" {
+ const toSafeInteger: typeof _.toSafeInteger;
+ export = toSafeInteger;
+}
+
+
+declare module "lodash.toString" {
+ const toString: typeof _.toString;
+ export = toString;
+}
+
+
+declare module "lodash.toUpper" {
+ const toUpper: typeof _.toUpper;
+ export = toUpper;
+}
+
+
+declare module "lodash.trim" {
+ const trim: typeof _.trim;
+ export = trim;
+}
+
+
+declare module "lodash.trimEnd" {
+ const trimEnd: typeof _.trimEnd;
+ export = trimEnd;
+}
+
+
+declare module "lodash.trimStart" {
+ const trimStart: typeof _.trimStart;
+ export = trimStart;
+}
+
+
+declare module "lodash.truncate" {
+ const truncate: typeof _.truncate;
+ export = truncate;
+}
+
+
+declare module "lodash.unescape" {
+ const unescape: typeof _.unescape;
+ export = unescape;
+}
+
+
+declare module "lodash.uniqueId" {
+ const uniqueId: typeof _.uniqueId;
+ export = uniqueId;
+}
+
+
+declare module "lodash.upperCase" {
+ const upperCase: typeof _.upperCase;
+ export = upperCase;
+}
+
+
+declare module "lodash.upperFirst" {
+ const upperFirst: typeof _.upperFirst;
+ export = upperFirst;
+}
+
+
+declare module "lodash.each" {
+ const each: typeof _.each;
+ export = each;
+}
+
+
+declare module "lodash.eachRight" {
+ const eachRight: typeof _.eachRight;
+ export = eachRight;
+}
+
+
+declare module "lodash.first" {
+ const first: typeof _.first;
+ export = first;
+}
+
+// Backward compatibility with --target es5
+interface Set<T> {}
+interface Map<K, V> {}
+interface WeakSet<T> {}
+interface WeakMap<K, V> {} \ No newline at end of file
diff --git a/catalog-ui/typings/notifyjs/notifyjs.d.ts b/catalog-ui/typings/notifyjs/notifyjs.d.ts
new file mode 100644
index 0000000000..f72828fc06
--- /dev/null
+++ b/catalog-ui/typings/notifyjs/notifyjs.d.ts
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+// Type definitions for notify.js 1.2.0
+// Project: https://github.com/alexgibson/notify.js
+// Definitions by: soundTricker <https://github.com/soundTricker>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+declare var Notify: {
+ new (title : string , options? : notifyjs.INotifyOption): notifyjs.INotify;
+
+ /**
+ * Check is permission is needed for the user to receive notifications.
+ * @return true : needs permission, false : does not need
+ */
+ needsPermission() : boolean;
+
+ /**
+ * Asks the user for permission to display notifications
+ * @param onPermissionGrantedCallback A callback for permmision is granted.
+ * @param onPermissionDeniedCallback A callback for permmision is denied.
+ */
+ requestPermission(onPermissionGrantedCallback?: ()=> any, onPermissionDeniedCallback? : ()=> any) : void;
+
+ /**
+ * return true if the browser supports HTML5 Notification
+ * @param true : the browser supports HTML5 Notification, false ; the browswer does not supports HTML5 Notification.
+ */
+ isSupported() : boolean;
+}
+
+declare module notifyjs {
+
+ /**
+ * Interface for Web Notifications API Wrapper.
+ */
+ interface INotify {
+ /**
+ * Show the notification.
+ */
+ show() : void;
+
+ /**
+ * Remove all event listener.
+ */
+ destroy() : void;
+
+ /**
+ * Close the notification.
+ */
+ close() : void;
+ onShowNotification(e : Event) : void;
+ onCloseNotification() : void;
+ onClickNotification() : void;
+ onErrorNotification() : void;
+ handleEvent(e : Event) : void;
+ }
+
+ /**
+ * Interface for the Notify's optional parameter.
+ */
+ interface INotifyOption {
+
+ /**
+ * notification message body
+ */
+ body? : string;
+
+ /**
+ * path for icon to display in notification
+ */
+ icon? : string;
+
+ /**
+ * unique identifier to stop duplicate notifications
+ */
+ tag? : string;
+
+ /**
+ * number of seconds to close the notification automatically
+ */
+ timeout? : number;
+
+ /**
+ * callback when notification is shown
+ */
+ notifyShow? (e : Event): any;
+ /**
+ * callback when notification is closed
+ */
+ notifyClose? : Function;
+ /**
+ * callback when notification is clicked
+ */
+ notifyClick? : Function;
+ /**
+ * callback when notification throws an error
+ */
+ notifyError? : Function;
+ /**
+ * callback when user has granted permission
+ */
+ permissionGranted? : Function;
+ /**
+ * callback when user has denied permission
+ */
+ permissionDenied? : Function;
+ }
+}
diff --git a/catalog-ui/typings/tsd.d.ts b/catalog-ui/typings/tsd.d.ts
new file mode 100644
index 0000000000..4fe638ef72
--- /dev/null
+++ b/catalog-ui/typings/tsd.d.ts
@@ -0,0 +1,21 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+/// <reference path="notifyjs/notifyjs.d.ts" />
+/// <reference path="jasmine/jasmine.d.ts" />
diff --git a/common-app-api/.gitignore b/common-app-api/.gitignore
new file mode 100644
index 0000000000..19f2e002ce
--- /dev/null
+++ b/common-app-api/.gitignore
@@ -0,0 +1,2 @@
+/target
+/target
diff --git a/common-app-api/pom.xml b/common-app-api/pom.xml
new file mode 100644
index 0000000000..955e721a94
--- /dev/null
+++ b/common-app-api/pom.xml
@@ -0,0 +1,209 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>common-app-api</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>security-utils</artifactId>
+ <version>${security-utils.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- functional java -->
+
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- yaml to object converter -->
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- listen to file changes -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Gson -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-jackson</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Aspects -->
+ <dependency>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-aspects</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- logging -->
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- logging end -->
+ <!-- -->
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version><!--$NO-MVN-MAN-VER$-->
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.fusesource</groupId>
+ <artifactId>sigar</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- jsoup HTML parser library @ http://jsoup.org/ -->
+ <dependency>
+ <groupId>org.jsoup</groupId>
+ <artifactId>jsoup</artifactId>
+ <version>1.8.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-validator</groupId>
+ <artifactId>commons-validator</artifactId>
+ <version>1.5.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jettison</groupId>
+ <artifactId>jettison</artifactId>
+ <version>1.3.8</version>
+ </dependency>
+
+ <dependency>
+ <groupId>de.ruedigermoeller</groupId>
+ <artifactId>fst</artifactId>
+ <version>2.47</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+<build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+</build>
+
+</project>
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/BeEcompErrorManager.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/BeEcompErrorManager.java
new file mode 100644
index 0000000000..d2675ac0c0
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/BeEcompErrorManager.java
@@ -0,0 +1,431 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import org.openecomp.sdc.common.config.AbsEcompErrorManager;
+import org.openecomp.sdc.common.config.EcompErrorEnum;
+import org.openecomp.sdc.common.config.IEcompConfigurationManager;
+
+public class BeEcompErrorManager extends AbsEcompErrorManager {
+
+ public enum ComponentName {
+ SERVICE, PRODUCT, VF
+ }
+
+ public enum ErrorSeverity {
+ INFO, WARNING, ERROR, FATAL
+ }
+
+ private static volatile BeEcompErrorManager instance;
+ private static ConfigurationManager configurationManager;
+
+ private BeEcompErrorManager() {
+ };
+
+ public static BeEcompErrorManager getInstance() {
+ if (instance == null) {
+
+ instance = init();
+ }
+ return instance;
+ }
+
+ private static synchronized BeEcompErrorManager init() {
+ if (instance == null) {
+ instance = new BeEcompErrorManager();
+ configurationManager = ConfigurationManager.getConfigurationManager();
+ }
+ return instance;
+ }
+
+ @Override
+ public IEcompConfigurationManager getConfigurationManager() {
+ return configurationManager;
+ }
+
+ public void logBeUebAuthenticationError(String context, String reason) {
+ processEcompError(context, EcompErrorEnum.BeUebAuthenticationError, reason);
+ }
+
+ public void logBeHealthCheckRecovery(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckRecovery);
+ }
+
+ public void logBeHealthCheckTitanRecovery(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckTitanRecovery);
+ }
+
+ public void logBeHealthCheckElasticSearchRecovery(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckElasticSearchRecovery);
+ }
+
+ public void logBeHealthCheckUebClusterRecovery(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckUebClusterRecovery);
+ }
+
+ public void logFeHealthCheckRecovery(String context) {
+ processEcompError(context, EcompErrorEnum.FeHealthCheckRecovery);
+ }
+
+ public void logBeHealthCheckError(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckError);
+ }
+
+ public void logBeHealthCheckTitanError(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckTitanError);
+ }
+
+ public void logBeHealthCheckElasticSearchError(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckElasticSearchError);
+ }
+
+ public void logBeHealthCheckUebClusterError(String context) {
+ processEcompError(context, EcompErrorEnum.BeHealthCheckUebClusterError);
+ }
+
+ public void logFeHealthCheckError(String context) {
+ processEcompError(context, EcompErrorEnum.FeHealthCheckError);
+ }
+
+ /**
+ * @param context
+ * @param reason
+ */
+ public void logBeUebConnectionError(String context, String reason) {
+ processEcompError(context, EcompErrorEnum.BeUebConnectionError, reason);
+ }
+
+ public void logBeUebUnkownHostError(String context, String host) {
+ processEcompError(context, EcompErrorEnum.BeUebUnkownHostError, host);
+ }
+
+ public void logBeComponentMissingError(String context, String componentType, String name) {
+ processEcompError(context, EcompErrorEnum.BeComponentMissingError, componentType, name);
+ }
+
+ public void logBeIncorrectComponentError(String context, String componentType, String name) {
+ processEcompError(context, EcompErrorEnum.BeIncorrectComponentError, componentType, name);
+ }
+
+ public void logBeInvalidConfigurationError(String context, String parameterName, String parameterValue) {
+ processEcompError(context, EcompErrorEnum.BeInvalidConfigurationError, parameterName, parameterValue);
+ }
+
+ public void logBeUebObjectNotFoundError(String context, String notFoundObjectName) {
+ processEcompError(context, EcompErrorEnum.BeUebObjectNotFoundError, notFoundObjectName);
+ }
+
+ public void logBeDistributionEngineInvalidArtifactType(String context, String artifactType,
+ String validArtifactTypes) {
+ processEcompError(context, EcompErrorEnum.BeDistributionEngineInvalidArtifactType, artifactType,
+ validArtifactTypes);
+ }
+
+ public void logBeMissingConfigurationError(String context, String parameterName) {
+ processEcompError(context, EcompErrorEnum.BeMissingConfigurationError, parameterName);
+ }
+
+ public void logBeConfigurationInvalidListSizeError(String context, String parameterName, int listMinimumSize) {
+ processEcompError(context, EcompErrorEnum.BeConfigurationInvalidListSizeError, parameterName,
+ String.valueOf(listMinimumSize));
+ }
+
+ public void logErrorConfigFileFormat(String context, String description) {
+ processEcompError(context, EcompErrorEnum.ErrorConfigFileFormat, description);
+ }
+
+ public void logBeMissingArtifactInformationError(String context, String missingInfo) {
+ processEcompError(context, EcompErrorEnum.BeMissingArtifactInformationError, missingInfo);
+ }
+
+ public void logBeArtifactMissingError(String context, String artifactName) {
+ processEcompError(context, EcompErrorEnum.BeArtifactMissingError, artifactName);
+ }
+
+ public void logBeUserMissingError(String context, String userId) {
+ processEcompError(context, EcompErrorEnum.BeUserMissingError, userId);
+ }
+
+ public void logBeInvalidTypeError(String context, String type, String name) {
+ processEcompError(context, EcompErrorEnum.BeInvalidTypeError, type, name);
+ }
+
+ public void logBeInvalidValueError(String context, String value, String name, String type) {
+ processEcompError(context, EcompErrorEnum.BeInvalidValueError, value, name, type);
+ }
+
+ public void logBeArtifactPayloadInvalid(String context) {
+ processEcompError(context, EcompErrorEnum.BeArtifactPayloadInvalid);
+ }
+
+ public void logBeArtifactInformationInvalidError(String context) {
+ processEcompError(context, EcompErrorEnum.BeArtifactInformationInvalidError);
+ }
+
+ public void logBeDistributionMissingError(String context, String distributionName) {
+ processEcompError(context, EcompErrorEnum.BeDistributionMissingError, "Distribution", distributionName);
+ }
+
+ public void logBeGraphObjectMissingError(String context, String objectType, String objectName) {
+ processEcompError(context, EcompErrorEnum.BeGraphObjectMissingError, objectType, objectName);
+ }
+
+ public void logBeInvalidJsonInput(String context) {
+ processEcompError(context, EcompErrorEnum.BeInvalidJsonInput);
+ }
+
+ public void logBeInitializationError(String context) {
+ processEcompError(context, EcompErrorEnum.BeInitializationError);
+ }
+
+ public void logBeFailedAddingResourceInstanceError(String context, String resourceName, String serviceId) {
+ processEcompError(context, EcompErrorEnum.BeFailedAddingResourceInstanceError, resourceName, serviceId);
+ }
+
+ public void logBeUebSystemError(String context, String operation) {
+ processEcompError(context, EcompErrorEnum.BeUebSystemError, operation);
+ }
+
+ public void logBeDistributionEngineSystemError(String context, String operation) {
+ processEcompError(context, EcompErrorEnum.BeDistributionEngineSystemError, operation);
+ }
+
+ public void logBeFailedAddingNodeTypeError(String context, String nodeType) {
+ processEcompError(context, EcompErrorEnum.BeFailedAddingNodeTypeError, nodeType);
+ }
+
+ public void logBeDaoSystemError(String context) {
+ processEcompError(context, EcompErrorEnum.BeDaoSystemError);
+ }
+
+ public void logBeSystemError(String context) {
+ processEcompError(context, EcompErrorEnum.BeSystemError);
+ }
+
+ public void logBeExecuteRollbackError(String context) {
+ processEcompError(context, EcompErrorEnum.BeExecuteRollbackError);
+ }
+
+ public void logBeFailedLockObjectError(String context, String type, String id) {
+ processEcompError(context, EcompErrorEnum.BeFailedLockObjectError, type, id);
+ }
+
+ public void logBeFailedCreateNodeError(String context, String nodeName, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedCreateNodeError, nodeName, status);
+ }
+
+ public void logBeFailedUpdateNodeError(String context, String nodeName, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedUpdateNodeError, nodeName, status);
+ }
+
+ public void logBeFailedDeleteNodeError(String context, String nodeName, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedDeleteNodeError, nodeName, status);
+ }
+
+ public void logBeFailedRetrieveNodeError(String context, String nodeName, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedRetrieveNodeError, nodeName, status);
+ }
+
+ public void logBeFailedFindParentError(String context, String node, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedFindParentError, node, status);
+ }
+
+ public void logBeFailedFindAllNodesError(String context, String nodeType, String parentNode, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedFindAllNodesError, nodeType, parentNode, status);
+ }
+
+ public void logBeFailedFindAssociationError(String context, String nodeType, String fromNode, String status) {
+ processEcompError(context, EcompErrorEnum.BeFailedFindAssociationError, nodeType, fromNode, status);
+ }
+
+ public void logBeComponentCleanerSystemError(String context, String operation) {
+ processEcompError(context, EcompErrorEnum.BeComponentCleanerSystemError, operation);
+ }
+
+ public void logBeRestApiGeneralError(String context) {
+ processEcompError(context, EcompErrorEnum.BeRestApiGeneralError);
+ }
+
+ public void logFqdnResolveError(String context, String description) {
+ processEcompError(context, EcompErrorEnum.FqdnResolveError, description);
+ }
+
+ public void logSiteSwitchoverInfo(String context, String description) {
+ processEcompError(context, EcompErrorEnum.SiteSwitchoverInfo, description);
+ }
+
+ public void logInternalAuthenticationError(String context, String description, ErrorSeverity severity) {
+
+ if (severity == null) {
+ processEcompError(context, EcompErrorEnum.InternalAuthenticationError, description);
+ } else {
+ switch (severity) {
+ case INFO:
+ processEcompError(context, EcompErrorEnum.InternalAuthenticationInfo, description);
+ break;
+ case WARNING:
+ processEcompError(context, EcompErrorEnum.InternalAuthenticationWarning, description);
+ break;
+ case ERROR:
+ processEcompError(context, EcompErrorEnum.InternalAuthenticationError, description);
+ break;
+ case FATAL:
+ processEcompError(context, EcompErrorEnum.InternalAuthenticationFatal, description);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ public void logInternalConnectionError(String context, String description, ErrorSeverity severity) {
+
+ if (severity == null) {
+ processEcompError(context, EcompErrorEnum.InternalConnectionError, description);
+ } else {
+ switch (severity) {
+ case INFO:
+ processEcompError(context, EcompErrorEnum.InternalConnectionInfo, description);
+ break;
+ case WARNING:
+ processEcompError(context, EcompErrorEnum.InternalConnectionWarning, description);
+ break;
+ case ERROR:
+ processEcompError(context, EcompErrorEnum.InternalConnectionError, description);
+ break;
+ case FATAL:
+ processEcompError(context, EcompErrorEnum.InternalConnectionFatal, description);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ public void logInternalDataError(String context, String description, ErrorSeverity severity) {
+
+ if (severity == null) {
+ processEcompError(context, EcompErrorEnum.InternalDataError, description);
+ } else {
+ switch (severity) {
+ case INFO:
+ processEcompError(context, EcompErrorEnum.InternalDataInfo, description);
+ break;
+ case WARNING:
+ processEcompError(context, EcompErrorEnum.InternalDataWarning, description);
+ break;
+ case ERROR:
+ processEcompError(context, EcompErrorEnum.InternalDataError, description);
+ break;
+ case FATAL:
+ processEcompError(context, EcompErrorEnum.InternalDataFatal, description);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ public void logInvalidInputError(String context, String description, ErrorSeverity severity) {
+
+ if (severity == null) {
+ processEcompError(context, EcompErrorEnum.InvalidInputError, description);
+ } else {
+ switch (severity) {
+ case INFO:
+ processEcompError(context, EcompErrorEnum.InvalidInputWarning, description);
+ break;
+ case WARNING:
+ processEcompError(context, EcompErrorEnum.InvalidInputInfo, description);
+ break;
+ case ERROR:
+ processEcompError(context, EcompErrorEnum.InvalidInputError, description);
+ break;
+ case FATAL:
+ processEcompError(context, EcompErrorEnum.InvalidInputFatal, description);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ public void logInternalFlowError(String context, String description, ErrorSeverity severity) {
+
+ if (severity == null) {
+ processEcompError(context, EcompErrorEnum.InternalFlowError, description);
+ } else {
+ switch (severity) {
+ case INFO:
+ processEcompError(context, EcompErrorEnum.InternalFlowInfo, description);
+ break;
+ case WARNING:
+ processEcompError(context, EcompErrorEnum.InternalFlowWarning, description);
+ break;
+ case ERROR:
+ processEcompError(context, EcompErrorEnum.InternalFlowError, description);
+ break;
+ case FATAL:
+ processEcompError(context, EcompErrorEnum.InternalFlowFatal, description);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ public void logInternalUnexpectedError(String context, String description, ErrorSeverity severity) {
+
+ if (severity == null) {
+ processEcompError(context, EcompErrorEnum.InternalUnexpectedError, description);
+ } else {
+ switch (severity) {
+ case INFO:
+ processEcompError(context, EcompErrorEnum.InternalUnexpectedInfo, description);
+ break;
+ case WARNING:
+ processEcompError(context, EcompErrorEnum.InternalUnexpectedWarning, description);
+ break;
+ case ERROR:
+ processEcompError(context, EcompErrorEnum.InternalUnexpectedError, description);
+ break;
+ case FATAL:
+ processEcompError(context, EcompErrorEnum.InternalUnexpectedFatal, description);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/CleanComponentsConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/CleanComponentsConfiguration.java
new file mode 100644
index 0000000000..6411005449
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/CleanComponentsConfiguration.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import java.util.List;
+
+public class CleanComponentsConfiguration {
+
+ private long cleanIntervalInMinutes;
+ private List<String> componentsToClean;
+
+ public long getCleanIntervalInMinutes() {
+ return cleanIntervalInMinutes;
+ }
+
+ public void setCleanIntervalInMinutes(long cleanIntervalInMinutes) {
+ this.cleanIntervalInMinutes = cleanIntervalInMinutes;
+ }
+
+ public List<String> getComponentsToClean() {
+ return componentsToClean;
+ }
+
+ public void setComponentsToClean(List<String> componentsToClean) {
+ this.componentsToClean = componentsToClean;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java
new file mode 100644
index 0000000000..96505f4e5d
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/Configuration.java
@@ -0,0 +1,1233 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+import static java.lang.String.format;
+
+public class Configuration extends BasicConfiguration {
+
+ private List<String> identificationHeaderFields;
+ /**
+ * Requests from these Urls will not be logged by
+ * org.openecomp.sdc.be.filters.BeServletFilter.<br>
+ **/
+ private List<String> unLoggedUrls;
+
+ /**
+ * backend host
+ */
+ private String beFqdn;
+ /**
+ * backend http port
+ */
+ private Integer beHttpPort;
+ /**
+ * backend http secured port
+ */
+ private Integer beSslPort;
+ /**
+ * be http context
+ */
+ private String beContext;
+ /**
+ * backend protocol. http | https
+ */
+ private String beProtocol = "http";
+
+ private Date released;
+ private String version = "1111";
+ private List<String> protocols;
+ private Map<String, String> users;
+ private Map<String, Object> neo4j;
+ private ElasticSearchConfig elasticSearch;
+ private String titanCfgFile;
+ private Boolean titanInMemoryGraph;
+ private Long titanLockTimeout;
+ private Long titanReconnectIntervalInSeconds;
+ private Long titanHealthCheckReadTimeout;
+ private Long esReconnectIntervalInSeconds;
+ private Long uebHealthCheckReconnectIntervalInSeconds;
+ private Long uebHealthCheckReadTimeout;
+
+ private List<String> resourceTypes;
+ private List<String> excludeResourceCategory;
+ private Map<String, Object> deploymentResourceArtifacts;
+ private Map<String, Object> deploymentResourceInstanceArtifacts;
+ private Map<String, Object> toscaArtifacts;
+ private Map<String, Object> informationalResourceArtifacts;
+ private Map<String, Object> informationalServiceArtifacts;
+ private Map<String, DeploymentArtifactTypeConfig> resourceDeploymentArtifacts;
+ private Map<String, DeploymentArtifactTypeConfig> serviceDeploymentArtifacts;
+ private Map<String, DeploymentArtifactTypeConfig> resourceInstanceDeploymentArtifacts;
+ private Map<String, DeploymentArtifactTypeConfig> resourceInformationalDeployedArtifacts;
+ private Map<String, Object> serviceApiArtifacts;
+ private List<String> excludeServiceCategory;
+ private Map<String, Set<String>> requirementsToFulfillBeforeCert;
+ private Map<String, Set<String>> capabilitiesToConsumeBeforeCert;
+
+ private List<String> artifactTypes;
+ private List<String> licenseTypes;
+
+ private Integer additionalInformationMaxNumberOfKeys;
+ private Integer defaultHeatArtifactTimeoutMinutes;
+
+ private BeMonitoringConfig systemMonitoring;
+ private CleanComponentsConfiguration cleanComponentsConfiguration;
+
+ private String artifactsIndex;
+
+ private String heatEnvArtifactHeader;
+ private String heatEnvArtifactFooter;
+
+ private String toscaFilesDir;
+ private String heatTranslatorPath;
+
+ private OnboardingConfig onboarding;
+
+ private CassandrConfig cassandraConfig;
+
+ private SwitchoverDetectorConfig switchoverDetector;
+
+ private ApplicationL1CacheConfig applicationL1Cache;
+
+ private ApplicationL2CacheConfig applicationL2Cache;
+
+ private ToscaValidatorsConfig toscaValidators;
+
+ private boolean disableAudit;
+
+ public SwitchoverDetectorConfig getSwitchoverDetector() {
+ return switchoverDetector;
+ }
+
+ public void setSwitchoverDetector(SwitchoverDetectorConfig switchoverDetector) {
+ this.switchoverDetector = switchoverDetector;
+ }
+
+ public ApplicationL1CacheConfig getApplicationL1Cache() {
+ return applicationL1Cache;
+ }
+
+ public void setApplicationL1Cache(ApplicationL1CacheConfig applicationL1Cache) {
+ this.applicationL1Cache = applicationL1Cache;
+ }
+
+ public ApplicationL2CacheConfig getApplicationL2Cache() {
+ return applicationL2Cache;
+ }
+
+ public void setApplicationL2Cache(ApplicationL2CacheConfig applicationL2Cache) {
+ this.applicationL2Cache = applicationL2Cache;
+ }
+
+ private EcompPortalConfig ecompPortal;
+
+ public CassandrConfig getCassandraConfig() {
+ return cassandraConfig;
+ }
+
+ public void setCassandraConfig(CassandrConfig cassandraKeySpace) {
+ this.cassandraConfig = cassandraKeySpace;
+ }
+
+ public List<String> getIdentificationHeaderFields() {
+ return identificationHeaderFields;
+ }
+
+ public void setIdentificationHeaderFields(List<String> identificationHeaderFields) {
+ this.identificationHeaderFields = identificationHeaderFields;
+ }
+
+ public Date getReleased() {
+ return released;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setReleased(Date released) {
+ this.released = released;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<String> getProtocols() {
+ return protocols;
+ }
+
+ public void setProtocols(List<String> protocols) {
+ this.protocols = protocols;
+ }
+
+ public Map<String, String> getUsers() {
+ return users;
+ }
+
+ public void setUsers(Map<String, String> users) {
+ this.users = users;
+ }
+
+ public String getBeFqdn() {
+ return beFqdn;
+ }
+
+ public void setBeFqdn(String beHost) {
+ this.beFqdn = beHost;
+ }
+
+ public Integer getBeHttpPort() {
+ return beHttpPort;
+ }
+
+ public void setBeHttpPort(Integer beHttpPort) {
+ this.beHttpPort = beHttpPort;
+ }
+
+ public Integer getBeSslPort() {
+ return beSslPort;
+ }
+
+ public void setBeSslPort(Integer beSslPort) {
+ this.beSslPort = beSslPort;
+ }
+
+ public String getBeContext() {
+ return beContext;
+ }
+
+ public void setBeContext(String beContext) {
+ this.beContext = beContext;
+ }
+
+ public String getBeProtocol() {
+ return beProtocol;
+ }
+
+ public void setBeProtocol(String beProtocol) {
+ this.beProtocol = beProtocol;
+ }
+
+ public Map<String, Object> getNeo4j() {
+ return neo4j;
+ }
+
+ public void setNeo4j(Map<String, Object> neo4j) {
+ this.neo4j = neo4j;
+ }
+
+ public ElasticSearchConfig getElasticSearch() {
+ return elasticSearch;
+ }
+
+ public void setElasticSearch(ElasticSearchConfig elasticSearch) {
+ this.elasticSearch = elasticSearch;
+ }
+
+ public String getTitanCfgFile() {
+ return titanCfgFile;
+ }
+
+ public void setTitanCfgFile(String titanCfgFile) {
+ this.titanCfgFile = titanCfgFile;
+ }
+
+ public Boolean getTitanInMemoryGraph() {
+ return titanInMemoryGraph;
+ }
+
+ public void setTitanInMemoryGraph(Boolean titanInMemoryGraph) {
+ this.titanInMemoryGraph = titanInMemoryGraph;
+ }
+
+ public Long getTitanLockTimeout() {
+ return titanLockTimeout;
+ }
+
+ public void setTitanLockTimeout(Long titanLockTimeout) {
+ this.titanLockTimeout = titanLockTimeout;
+ }
+
+ public Long getTitanHealthCheckReadTimeout() {
+ return titanHealthCheckReadTimeout;
+ }
+
+ public Long getTitanHealthCheckReadTimeout(long defaultVal) {
+ return titanHealthCheckReadTimeout == null ? defaultVal : titanHealthCheckReadTimeout;
+ }
+
+ public void setTitanHealthCheckReadTimeout(Long titanHealthCheckReadTimeout) {
+ this.titanHealthCheckReadTimeout = titanHealthCheckReadTimeout;
+ }
+
+ public Long getTitanReconnectIntervalInSeconds() {
+ return titanReconnectIntervalInSeconds;
+ }
+
+ public Long getTitanReconnectIntervalInSeconds(long defaultVal) {
+ return titanReconnectIntervalInSeconds == null ? defaultVal : titanReconnectIntervalInSeconds;
+ }
+
+ public void setTitanReconnectIntervalInSeconds(Long titanReconnectIntervalInSeconds) {
+ this.titanReconnectIntervalInSeconds = titanReconnectIntervalInSeconds;
+ }
+
+ public Long getEsReconnectIntervalInSeconds() {
+ return esReconnectIntervalInSeconds;
+ }
+
+ public Long getEsReconnectIntervalInSeconds(long defaultVal) {
+ return esReconnectIntervalInSeconds == null ? defaultVal : esReconnectIntervalInSeconds;
+ }
+
+ public void setEsReconnectIntervalInSeconds(Long esReconnectIntervalInSeconds) {
+ this.esReconnectIntervalInSeconds = esReconnectIntervalInSeconds;
+ }
+
+ public List<String> getArtifactTypes() {
+ return artifactTypes;
+ }
+
+ public void setArtifactTypes(List<String> artifactTypes) {
+ this.artifactTypes = artifactTypes;
+ }
+
+ public List<String> getExcludeResourceCategory() {
+ return excludeResourceCategory;
+ }
+
+ public void setExcludeResourceCategory(List<String> excludeResourceCategory) {
+ this.excludeResourceCategory = excludeResourceCategory;
+ }
+
+ public Map<String, Object> getToscaArtifacts() {
+ return toscaArtifacts;
+ }
+
+ public void setToscaArtifacts(Map<String, Object> toscaArtifacts) {
+ this.toscaArtifacts = toscaArtifacts;
+ }
+
+ public Map<String, Object> getInformationalResourceArtifacts() {
+ return informationalResourceArtifacts;
+ }
+
+ public void setInformationalResourceArtifacts(Map<String, Object> informationalResourceArtifacts) {
+ this.informationalResourceArtifacts = informationalResourceArtifacts;
+ }
+
+ public Map<String, Object> getInformationalServiceArtifacts() {
+ return informationalServiceArtifacts;
+ }
+
+ public void setInformationalServiceArtifacts(Map<String, Object> informationalServiceArtifacts) {
+ this.informationalServiceArtifacts = informationalServiceArtifacts;
+ }
+
+ public Map<String, Object> getServiceApiArtifacts() {
+ return serviceApiArtifacts;
+ }
+
+ public void setServiceApiArtifacts(Map<String, Object> serviceApiArtifacts) {
+ this.serviceApiArtifacts = serviceApiArtifacts;
+ }
+
+ public Map<String, DeploymentArtifactTypeConfig> getServiceDeploymentArtifacts() {
+ return serviceDeploymentArtifacts;
+ }
+
+ public void setServiceDeploymentArtifacts(Map<String, DeploymentArtifactTypeConfig> serviceDeploymentArtifacts) {
+ this.serviceDeploymentArtifacts = serviceDeploymentArtifacts;
+ }
+
+ public Map<String, DeploymentArtifactTypeConfig> getResourceDeploymentArtifacts() {
+ return resourceDeploymentArtifacts;
+ }
+
+ public void setResourceDeploymentArtifacts(Map<String, DeploymentArtifactTypeConfig> resourceDeploymentArtifacts) {
+ this.resourceDeploymentArtifacts = resourceDeploymentArtifacts;
+ }
+
+ public void setResourceInstanceDeploymentArtifacts(
+ Map<String, DeploymentArtifactTypeConfig> resourceInstanceDeploymentArtifacts) {
+ this.resourceInstanceDeploymentArtifacts = resourceInstanceDeploymentArtifacts;
+ }
+
+ public Map<String, DeploymentArtifactTypeConfig> getResourceInstanceDeploymentArtifacts() {
+ return resourceInstanceDeploymentArtifacts;
+ }
+
+ public List<String> getExcludeServiceCategory() {
+ return excludeServiceCategory;
+ }
+
+ public void setExcludeServiceCategory(List<String> excludeServiceCategory) {
+ this.excludeServiceCategory = excludeServiceCategory;
+ }
+
+ public List<String> getLicenseTypes() {
+ return licenseTypes;
+ }
+
+ public void setLicenseTypes(List<String> licenseTypes) {
+ this.licenseTypes = licenseTypes;
+ }
+
+ public Integer getAdditionalInformationMaxNumberOfKeys() {
+ return additionalInformationMaxNumberOfKeys;
+ }
+
+ public void setAdditionalInformationMaxNumberOfKeys(Integer additionalInformationMaxNumberOfKeys) {
+ this.additionalInformationMaxNumberOfKeys = additionalInformationMaxNumberOfKeys;
+ }
+
+ public BeMonitoringConfig getSystemMonitoring() {
+ return systemMonitoring;
+ }
+
+ public void setSystemMonitoring(BeMonitoringConfig systemMonitoring) {
+ this.systemMonitoring = systemMonitoring;
+ }
+
+ public Integer getDefaultHeatArtifactTimeoutMinutes() {
+ return defaultHeatArtifactTimeoutMinutes;
+ }
+
+ public void setDefaultHeatArtifactTimeoutMinutes(Integer defaultHeatArtifactTimeoutMinutes) {
+ this.defaultHeatArtifactTimeoutMinutes = defaultHeatArtifactTimeoutMinutes;
+ }
+
+ public Long getUebHealthCheckReconnectIntervalInSeconds() {
+ return uebHealthCheckReconnectIntervalInSeconds;
+ }
+
+ public void setUebHealthCheckReconnectIntervalInSeconds(Long uebHealthCheckReconnectIntervalInSeconds) {
+ this.uebHealthCheckReconnectIntervalInSeconds = uebHealthCheckReconnectIntervalInSeconds;
+ }
+
+ public Long getUebHealthCheckReadTimeout() {
+ return uebHealthCheckReadTimeout;
+ }
+
+ public void setUebHealthCheckReadTimeout(Long uebHealthCheckReadTimeout) {
+ this.uebHealthCheckReadTimeout = uebHealthCheckReadTimeout;
+ }
+
+ public static class ElasticSearchConfig {
+
+ List<IndicesTimeFrequencyEntry> indicesTimeFrequency;
+
+ public List<IndicesTimeFrequencyEntry> getIndicesTimeFrequency() {
+ return indicesTimeFrequency;
+ }
+
+ public void setIndicesTimeFrequency(List<IndicesTimeFrequencyEntry> indicesTimeFrequency) {
+ this.indicesTimeFrequency = indicesTimeFrequency;
+ }
+
+ public static class IndicesTimeFrequencyEntry {
+
+ String indexPrefix;
+ String creationPeriod;
+
+ public String getIndexPrefix() {
+ return indexPrefix;
+ }
+
+ public void setIndexPrefix(String indexPrefix) {
+ this.indexPrefix = indexPrefix;
+ }
+
+ public String getCreationPeriod() {
+ return creationPeriod;
+ }
+
+ public void setCreationPeriod(String creationPeriod) {
+ this.creationPeriod = creationPeriod;
+ }
+ }
+ }
+
+ public static class CassandrConfig {
+
+ List<String> cassandraHosts;
+ String localDataCenter;
+ Long reconnectTimeout;
+ List<KeyspaceConfig> keySpaces;
+ boolean authenticate;
+ String username;
+ String password;
+ boolean ssl;
+ String truststorePath;
+ String truststorePassword;
+
+ public String getLocalDataCenter() {
+ return localDataCenter;
+ }
+
+ public void setLocalDataCenter(String localDataCenter) {
+ this.localDataCenter = localDataCenter;
+ }
+
+ public boolean isAuthenticate() {
+ return authenticate;
+ }
+
+ public void setAuthenticate(boolean authenticate) {
+ this.authenticate = authenticate;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isSsl() {
+ return ssl;
+ }
+
+ public void setSsl(boolean ssl) {
+ this.ssl = ssl;
+ }
+
+ public String getTruststorePath() {
+ return truststorePath;
+ }
+
+ public void setTruststorePath(String truststorePath) {
+ this.truststorePath = truststorePath;
+ }
+
+ public String getTruststorePassword() {
+ return truststorePassword;
+ }
+
+ public void setTruststorePassword(String truststorePassword) {
+ this.truststorePassword = truststorePassword;
+ }
+
+ public Long getReconnectTimeout() {
+ return reconnectTimeout;
+ }
+
+ public void setReconnectTimeout(Long reconnectTimeout) {
+ this.reconnectTimeout = reconnectTimeout;
+ }
+
+ public List<String> getCassandraHosts() {
+ return cassandraHosts;
+ }
+
+ public void setCassandraHosts(List<String> cassandraHosts) {
+ this.cassandraHosts = cassandraHosts;
+ }
+
+ public List<KeyspaceConfig> getKeySpaces() {
+ return keySpaces;
+ }
+
+ public void setKeySpaces(List<KeyspaceConfig> cassandraConfig) {
+ this.keySpaces = cassandraConfig;
+ }
+
+ public static class KeyspaceConfig {
+
+ String name;
+ String replicationStrategy;
+ List<String> replicationInfo;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getReplicationStrategy() {
+ return replicationStrategy;
+ }
+
+ public void setReplicationStrategy(String replicationStrategy) {
+ this.replicationStrategy = replicationStrategy;
+ }
+
+ public List<String> getReplicationInfo() {
+ return replicationInfo;
+ }
+
+ public void setReplicationInfo(List<String> replicationInfo) {
+ this.replicationInfo = replicationInfo;
+ }
+ }
+ }
+
+ public static class SwitchoverDetectorConfig {
+
+ String gBeFqdn;
+ String gFeFqdn;
+ String beVip;
+ String feVip;
+ int beResolveAttempts;
+ int feResolveAttempts;
+ Boolean enabled;
+ long interval;
+ String changePriorityUser;
+ String changePriorityPassword;
+ String publishNetworkUrl;
+ String publishNetworkBody;
+ Map<String, GroupInfo> groups;
+
+ public String getgBeFqdn() {
+ return gBeFqdn;
+ }
+
+ public void setgBeFqdn(String gBeFqdn) {
+ this.gBeFqdn = gBeFqdn;
+ }
+
+ public String getgFeFqdn() {
+ return gFeFqdn;
+ }
+
+ public void setgFeFqdn(String gFeFqdn) {
+ this.gFeFqdn = gFeFqdn;
+ }
+
+ public String getBeVip() {
+ return beVip;
+ }
+
+ public void setBeVip(String beVip) {
+ this.beVip = beVip;
+ }
+
+ public String getFeVip() {
+ return feVip;
+ }
+
+ public void setFeVip(String feVip) {
+ this.feVip = feVip;
+ }
+
+ public int getBeResolveAttempts() {
+ return beResolveAttempts;
+ }
+
+ public void setBeResolveAttempts(int beResolveAttempts) {
+ this.beResolveAttempts = beResolveAttempts;
+ }
+
+ public int getFeResolveAttempts() {
+ return feResolveAttempts;
+ }
+
+ public void setFeResolveAttempts(int feResolveAttempts) {
+ this.feResolveAttempts = feResolveAttempts;
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public long getInterval() {
+ return interval;
+ }
+
+ public void setInterval(long interval) {
+ this.interval = interval;
+ }
+
+ public String getChangePriorityUser() {
+ return changePriorityUser;
+ }
+
+ public void setChangePriorityUser(String changePriorityUser) {
+ this.changePriorityUser = changePriorityUser;
+ }
+
+ public String getChangePriorityPassword() {
+ return changePriorityPassword;
+ }
+
+ public void setChangePriorityPassword(String changePriorityPassword) {
+ this.changePriorityPassword = changePriorityPassword;
+ }
+
+ public String getPublishNetworkUrl() {
+ return publishNetworkUrl;
+ }
+
+ public void setPublishNetworkUrl(String publishNetworkUrl) {
+ this.publishNetworkUrl = publishNetworkUrl;
+ }
+
+ public String getPublishNetworkBody() {
+ return publishNetworkBody;
+ }
+
+ public void setPublishNetworkBody(String publishNetworkBody) {
+ this.publishNetworkBody = publishNetworkBody;
+ }
+
+ public Map<String, GroupInfo> getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Map<String, GroupInfo> groups) {
+ this.groups = groups;
+ }
+
+ public static class GroupInfo {
+
+ String changePriorityUrl;
+ String changePriorityBody;
+
+ public String getChangePriorityUrl() {
+ return changePriorityUrl;
+ }
+
+ public void setChangePriorityUrl(String changePriorityUrl) {
+ this.changePriorityUrl = changePriorityUrl;
+ }
+
+ public String getChangePriorityBody() {
+ return changePriorityBody;
+ }
+
+ public void setChangePriorityBody(String changePriorityBody) {
+ this.changePriorityBody = changePriorityBody;
+ }
+ }
+
+ }
+
+ public static class BeMonitoringConfig {
+
+ Boolean enabled;
+ Boolean isProxy;
+ Integer probeIntervalInSeconds;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Boolean getIsProxy() {
+ return isProxy;
+ }
+
+ public void setIsProxy(Boolean isProxy) {
+ this.isProxy = isProxy;
+ }
+
+ public Integer getProbeIntervalInSeconds() {
+ return probeIntervalInSeconds;
+ }
+
+ public Integer getProbeIntervalInSeconds(int defaultVal) {
+ return probeIntervalInSeconds == null ? defaultVal : probeIntervalInSeconds;
+ }
+
+ public void setProbeIntervalInSeconds(Integer probeIntervalInSeconds) {
+ this.probeIntervalInSeconds = probeIntervalInSeconds;
+ }
+ }
+
+ public static class DeploymentArtifactTypeConfig {
+
+ List<String> acceptedTypes;
+ List<String> validForResourceTypes;
+
+ public List<String> getValidForResourceTypes() {
+ return validForResourceTypes;
+ }
+
+ public void setValidForResourceTypes(List<String> validForResourceTypes) {
+ this.validForResourceTypes = validForResourceTypes;
+ }
+
+ public List<String> getAcceptedTypes() {
+ return acceptedTypes;
+ }
+
+ public void setAcceptedTypes(List<String> acceptedTypes) {
+ this.acceptedTypes = acceptedTypes;
+ }
+ }
+
+ public static class OnboardingConfig {
+
+ String protocol = "http";
+ String host;
+ Integer port;
+ String downloadCsarUri;
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public String getDownloadCsarUri() {
+ return downloadCsarUri;
+ }
+
+ public void setDownloadCsarUri(String downloadCsarUri) {
+ this.downloadCsarUri = downloadCsarUri;
+ }
+
+ @Override
+ public String toString() {
+ return "OnboardingConfig [protocol=" + protocol + ", host=" + host + ", port=" + port + ", downloadCsarUri="
+ + downloadCsarUri + "]";
+ }
+
+ }
+
+ public static class EcompPortalConfig {
+
+ private String defaultFunctionalMenu;
+
+ public String getDefaultFunctionalMenu() {
+ return defaultFunctionalMenu;
+ }
+
+ public void setDefaultFunctionalMenu(String defaultFunctionalMenu) {
+ this.defaultFunctionalMenu = defaultFunctionalMenu;
+ }
+
+ @Override
+ public String toString() {
+ return "EcompPortalConfig [defaultFunctionalMenu=" + defaultFunctionalMenu + "]";
+ }
+
+ }
+
+ public static class ApplicationL1CacheConfig {
+
+ ApplicationL1CacheInfo datatypes;
+
+ public ApplicationL1CacheInfo getDatatypes() {
+ return datatypes;
+ }
+
+ public void setDatatypes(ApplicationL1CacheInfo datatypes) {
+ this.datatypes = datatypes;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationL1CacheConfig [datatypes=" + datatypes + "]";
+ }
+
+ }
+
+ public static class ApplicationL2CacheConfig {
+
+ boolean enabled;
+ ApplicationL1CacheCatalogInfo catalogL1Cache;
+
+ QueueInfo queue;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public ApplicationL1CacheCatalogInfo getCatalogL1Cache() {
+ return catalogL1Cache;
+ }
+
+ public void setCatalogL1Cache(ApplicationL1CacheCatalogInfo catalogL1Cache) {
+ this.catalogL1Cache = catalogL1Cache;
+ }
+
+ public QueueInfo getQueue() {
+ return queue;
+ }
+
+ public void setQueue(QueueInfo queue) {
+ this.queue = queue;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationL2CacheConfig [enabled=" + enabled + ", catalogL1Cache=" + catalogL1Cache + "]";
+ }
+
+ }
+
+ public static class ToscaValidatorsConfig {
+
+ private Integer stringMaxLength;
+
+ public Integer getStringMaxLength() {
+ return stringMaxLength;
+ }
+
+ public void setStringMaxLength(Integer stringMaxLength) {
+ this.stringMaxLength = stringMaxLength;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaValidatorsConfig [stringMaxLength=" + stringMaxLength + "]";
+ }
+
+ }
+
+ public static class ApplicationL1CacheInfo {
+
+ Boolean enabled;
+ Integer firstRunDelay;
+ Integer pollIntervalInSec;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Integer getFirstRunDelay() {
+ return firstRunDelay;
+ }
+
+ public void setFirstRunDelay(Integer firstRunDelay) {
+ this.firstRunDelay = firstRunDelay;
+ }
+
+ public Integer getPollIntervalInSec() {
+ return pollIntervalInSec;
+ }
+
+ public void setPollIntervalInSec(Integer pollIntervalInSec) {
+ this.pollIntervalInSec = pollIntervalInSec;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationL1CacheInfo [enabled=" + enabled + ", firstRunDelay=" + firstRunDelay
+ + ", pollIntervalInSec=" + pollIntervalInSec + "]";
+ }
+ }
+
+ public static class ApplicationL1CacheCatalogInfo {
+
+ Boolean enabled;
+ Integer resourcesSizeInCache;
+ Integer servicesSizeInCache;
+ Integer productsSizeInCache;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Integer getResourcesSizeInCache() {
+ return resourcesSizeInCache;
+ }
+
+ public void setResourcesSizeInCache(Integer resourcesSizeInCache) {
+ this.resourcesSizeInCache = resourcesSizeInCache;
+ }
+
+ public Integer getServicesSizeInCache() {
+ return servicesSizeInCache;
+ }
+
+ public void setServicesSizeInCache(Integer servicesSizeInCache) {
+ this.servicesSizeInCache = servicesSizeInCache;
+ }
+
+ public Integer getProductsSizeInCache() {
+ return productsSizeInCache;
+ }
+
+ public void setProductsSizeInCache(Integer productsSizeInCache) {
+ this.productsSizeInCache = productsSizeInCache;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationL1CacheCatalogInfo [enabled=" + enabled + ", resourcesSizeInCache="
+ + resourcesSizeInCache + ", servicesSizeInCache=" + servicesSizeInCache + ", productsSizeInCache="
+ + productsSizeInCache + "]";
+ }
+
+ }
+
+ public static class QueueInfo {
+ Integer numberOfCacheWorkers;
+ Integer waitOnShutDownInMinutes;
+ Integer syncIntervalInSecondes;
+
+ public Integer getWaitOnShutDownInMinutes() {
+ return waitOnShutDownInMinutes;
+ }
+
+ public void setWaitOnShutDownInMinutes(Integer waitOnShutDownInMinutes) {
+ this.waitOnShutDownInMinutes = waitOnShutDownInMinutes;
+ }
+
+ public Integer getSyncIntervalInSecondes() {
+ return syncIntervalInSecondes;
+ }
+
+ public void setSyncIntervalInSecondes(Integer syncIntervalInSecondes) {
+ this.syncIntervalInSecondes = syncIntervalInSecondes;
+ }
+
+ public Integer getNumberOfCacheWorkers() {
+ return numberOfCacheWorkers;
+ }
+
+ public void setNumberOfCacheWorkers(Integer numberOfCacheWorkers) {
+ this.numberOfCacheWorkers = numberOfCacheWorkers;
+ }
+
+ @Override
+ public String toString() {
+ return "QueueInfo[" + "waitOnShutDownInMinutes=" + waitOnShutDownInMinutes + ", syncIntervalInSecondes="
+ + syncIntervalInSecondes + ", numberOfCacheWorkers=" + this.numberOfCacheWorkers + ']';
+ }
+ }
+
+ public CleanComponentsConfiguration getCleanComponentsConfiguration() {
+ return cleanComponentsConfiguration;
+ }
+
+ public void setCleanComponentsConfiguration(CleanComponentsConfiguration cleanComponentsConfiguration) {
+ this.cleanComponentsConfiguration = cleanComponentsConfiguration;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append(format("backend host: %s\n", beFqdn))
+ .append(format("backend http port: %s\n", beHttpPort))
+ .append(format("backend ssl port: %s\n", beSslPort)).append(format("backend context: %s\n", beContext))
+ .append(format("backend protocol: %s\n", beProtocol)).append(format("Version: %s\n", version))
+ .append(format("Released: %s\n", released)).append(format("Supported protocols: %s\n", protocols))
+ .append(format("Users: %s\n", users)).append(format("Neo4j: %s\n", neo4j))
+ .append(format("ElasticSearch: %s\n", elasticSearch))
+ .append(format("Titan Cfg File: %s\n", titanCfgFile))
+ .append(format("Titan In memory: %s\n", titanInMemoryGraph))
+ .append(format("Titan lock timeout: %s\n", titanLockTimeout))
+ .append(format("Titan reconnect interval seconds: %s\n", titanReconnectIntervalInSeconds))
+ .append(format("excludeResourceCategory: %s\n", excludeResourceCategory))
+ .append(format("informationalResourceArtifacts: %s\n", informationalResourceArtifacts))
+ .append(format("deploymentResourceArtifacts: %s\n", deploymentResourceArtifacts))
+ .append(format("informationalServiceArtifacts: %s\n", informationalServiceArtifacts))
+ .append(format("Supported artifacts types: %s\n", artifactTypes))
+ .append(format("Supported license types: %s\n", licenseTypes))
+ .append(format("Additional information Maximum number of preoperties: %s\n",
+ additionalInformationMaxNumberOfKeys))
+ .append(format("Default Heat Artifact Timeout in Minutes: %s\n", defaultHeatArtifactTimeoutMinutes))
+ .append(format("URLs For HTTP Requests that will not be automatically logged : %s\n", unLoggedUrls))
+ .append(format("Service Api Artifacts: %s\n", serviceApiArtifacts))
+ .append(format("heat env artifact header: %s\n", heatEnvArtifactHeader))
+ .append(format("heat env artifact footer: %s\n", heatEnvArtifactFooter))
+ .append(format("onboarding: %s\n", onboarding)).toString();
+ }
+
+ public List<String> getUnLoggedUrls() {
+ return unLoggedUrls;
+ }
+
+ public void setUnLoggedUrls(List<String> unLoggedUrls) {
+ this.unLoggedUrls = unLoggedUrls;
+ }
+
+ public Map<String, Object> getDeploymentResourceArtifacts() {
+ return deploymentResourceArtifacts;
+ }
+
+ public void setDeploymentResourceArtifacts(Map<String, Object> deploymentResourceArtifacts) {
+ this.deploymentResourceArtifacts = deploymentResourceArtifacts;
+ }
+
+ public String getHeatEnvArtifactHeader() {
+ return heatEnvArtifactHeader;
+ }
+
+ public void setHeatEnvArtifactHeader(String heatEnvArtifactHeader) {
+ this.heatEnvArtifactHeader = heatEnvArtifactHeader;
+ }
+
+ public String getHeatEnvArtifactFooter() {
+ return heatEnvArtifactFooter;
+ }
+
+ public void setHeatEnvArtifactFooter(String heatEnvArtifactFooter) {
+ this.heatEnvArtifactFooter = heatEnvArtifactFooter;
+ }
+
+ public Map<String, Object> getDeploymentResourceInstanceArtifacts() {
+ return deploymentResourceInstanceArtifacts;
+ }
+
+ public void setDeploymentResourceInstanceArtifacts(Map<String, Object> deploymentResourceInstanceArtifacts) {
+ this.deploymentResourceInstanceArtifacts = deploymentResourceInstanceArtifacts;
+ }
+
+ public String getArtifactsIndex() {
+ return artifactsIndex;
+ }
+
+ public void setArtifactsIndex(String artifactsIndex) {
+ this.artifactsIndex = artifactsIndex;
+ }
+
+ public Map<String, DeploymentArtifactTypeConfig> getResourceInformationalDeployedArtifacts() {
+ return resourceInformationalDeployedArtifacts;
+ }
+
+ public void setResourceInformationalDeployedArtifacts(
+ Map<String, DeploymentArtifactTypeConfig> resourceInformationalDeployedArtifacts) {
+ this.resourceInformationalDeployedArtifacts = resourceInformationalDeployedArtifacts;
+ }
+
+ public List<String> getResourceTypes() {
+ return resourceTypes;
+ }
+
+ public void setResourceTypes(List<String> resourceTypes) {
+ this.resourceTypes = resourceTypes;
+ }
+
+ public String getToscaFilesDir() {
+ return toscaFilesDir;
+ }
+
+ public void setToscaFilesDir(String toscaFilesDir) {
+ this.toscaFilesDir = toscaFilesDir;
+ }
+
+ public String getHeatTranslatorPath() {
+ return heatTranslatorPath;
+ }
+
+ public void setHeatTranslatorPath(String heatTranslatorPath) {
+ this.heatTranslatorPath = heatTranslatorPath;
+ }
+
+ public Map<String, Set<String>> getRequirementsToFulfillBeforeCert() {
+ return requirementsToFulfillBeforeCert;
+ }
+
+ public void setRequirementsToFulfillBeforeCert(Map<String, Set<String>> requirementsToFulfillBeforeCert) {
+ this.requirementsToFulfillBeforeCert = requirementsToFulfillBeforeCert;
+ }
+
+ public Map<String, Set<String>> getCapabilitiesToConsumeBeforeCert() {
+ return capabilitiesToConsumeBeforeCert;
+ }
+
+ public void setCapabilitiesToConsumeBeforeCert(Map<String, Set<String>> capabilitiesToConsumeBeforeCert) {
+ this.capabilitiesToConsumeBeforeCert = capabilitiesToConsumeBeforeCert;
+ }
+
+ public OnboardingConfig getOnboarding() {
+ return onboarding;
+ }
+
+ public void setOnboarding(OnboardingConfig onboarding) {
+ this.onboarding = onboarding;
+ }
+
+ public EcompPortalConfig getEcompPortal() {
+ return ecompPortal;
+ }
+
+ public void setEcompPortal(EcompPortalConfig ecompPortal) {
+ this.ecompPortal = ecompPortal;
+ }
+
+ public ToscaValidatorsConfig getToscaValidators() {
+ return toscaValidators;
+ }
+
+ public void setToscaValidators(ToscaValidatorsConfig toscaValidators) {
+ this.toscaValidators = toscaValidators;
+ }
+
+ public boolean isDisableAudit() {
+ return disableAudit;
+ }
+
+ public void setDisableAudit(boolean enableAudit) {
+ this.disableAudit = enableAudit;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/ConfigurationManager.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/ConfigurationManager.java
new file mode 100644
index 0000000000..0015729be0
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/ConfigurationManager.java
@@ -0,0 +1,154 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.FileChangeCallback;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration;
+import org.openecomp.sdc.common.config.IEcompConfigurationManager;
+
+public class ConfigurationManager implements FileChangeCallback, IEcompConfigurationManager {
+
+ ConfigurationSource configurationSource = null;
+ private static ConfigurationManager instance;
+
+ public ConfigurationManager(ConfigurationSource configurationSource) {
+ super();
+ this.configurationSource = configurationSource;
+ loadConfigurationFiles();
+ instance = this;
+ }
+
+ Map<String, Object> configurations = new HashMap<String, Object>();
+
+ private void loadConfigurationFiles() {
+
+ loadConfigurationClass(Configuration.class);
+ loadConfigurationClass(ErrorConfiguration.class);
+ loadConfigurationClass(Neo4jErrorsConfiguration.class);
+ loadConfigurationClass(EcompErrorConfiguration.class);
+ loadConfigurationClass(DistributionEngineConfiguration.class);
+ }
+
+ private <T extends BasicConfiguration> void loadConfigurationClass(Class<T> clazz) {
+ ConfigurationListener configurationListener = new ConfigurationListener(clazz, this);
+
+ T object = configurationSource.getAndWatchConfiguration(clazz, configurationListener);
+
+ configurations.put(getKey(clazz), object);
+ }
+
+ private <T> String getKey(Class<T> class1) {
+
+ return class1.getSimpleName();
+
+ }
+
+ public Configuration getConfiguration() {
+
+ return (Configuration) configurations.get(getKey(Configuration.class));
+
+ }
+
+ public void setConfiguration(Configuration configuration) {
+
+ configurations.put(getKey(Configuration.class), configuration);
+
+ }
+
+ public void setErrorConfiguration(ErrorConfiguration configuration) {
+
+ configurations.put(getKey(ErrorConfiguration.class), configuration);
+
+ }
+
+ public ErrorConfiguration getErrorConfiguration() {
+
+ return (ErrorConfiguration) configurations.get(getKey(ErrorConfiguration.class));
+
+ }
+
+ public Neo4jErrorsConfiguration getNeo4jErrorsConfiguration() {
+ return (Neo4jErrorsConfiguration) configurations.get(getKey(Neo4jErrorsConfiguration.class));
+ }
+
+ @Override
+ public EcompErrorConfiguration getEcompErrorConfiguration() {
+
+ return (EcompErrorConfiguration) configurations.get(getKey(EcompErrorConfiguration.class));
+
+ }
+
+ public Configuration getConfigurationAndWatch(ConfigurationListener configurationListener) {
+
+ if (configurationListener != null) {
+
+ configurationSource.addWatchConfiguration(Configuration.class, configurationListener);
+
+ }
+ return (Configuration) configurations.get(getKey(Configuration.class));
+
+ }
+
+ public static ConfigurationManager getConfigurationManager() {
+ return instance;
+ }
+
+ public void reconfigure(BasicConfiguration obj) {
+
+ // if (obj != null) {
+
+ // if (obj instanceof Configuration) {
+ // configurations.put(getKey(Configuration.class), obj);
+ // }
+ // if (obj instanceof ErrorConfiguration) {
+ // configurations.put(getKey(ErrorConfiguration.class), obj);
+ // }
+ //
+ // if (obj instanceof EcompErrorConfiguration) {
+ // configurations.put(getKey(EcompErrorConfiguration.class), obj);
+ // }
+ // }
+
+ }
+
+ /**
+ * FOR TEST ONLY
+ *
+ * @param ecompErrorConfiguration
+ */
+ public void setEcompErrorConfiguration(EcompErrorConfiguration ecompErrorConfiguration) {
+
+ configurations.put(getKey(EcompErrorConfiguration.class), ecompErrorConfiguration);
+
+ }
+
+ public DistributionEngineConfiguration getDistributionEngineConfiguration() {
+
+ return (DistributionEngineConfiguration) configurations.get(getKey(DistributionEngineConfiguration.class));
+
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/DistributionEngineConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/DistributionEngineConfiguration.java
new file mode 100644
index 0000000000..e4a2092e2f
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/DistributionEngineConfiguration.java
@@ -0,0 +1,430 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+public class DistributionEngineConfiguration extends BasicConfiguration {
+
+ private List<String> uebServers;
+
+ private String distributionNotifTopicName;
+
+ private String distributionStatusTopicName;
+
+ private Integer initRetryIntervalSec;
+
+ private Integer initMaxIntervalSec;
+
+ private ComponentArtifactTypesConfig distribNotifServiceArtifactTypes;
+
+ private ComponentArtifactTypesConfig distribNotifResourceArtifactTypes;
+
+ // private List<String> distribNotifServiceInfoArtifactTypes;
+
+ // private List<String> distribNotifResourceLifecycleArtifactTypes;
+
+ private String uebPublicKey;
+
+ private String uebSecretKey;
+
+ private List<String> environments;
+
+ private DistributionStatusTopicConfig distributionStatusTopic;
+
+ private CreateTopicConfig createTopic;
+
+ private boolean startDistributionEngine;
+
+ private DistributionNotificationTopicConfig distributionNotificationTopic;
+
+ private Integer defaultArtifactInstallationTimeout = 60;
+
+ public static class DistribNotifServiceArtifacts {
+
+ Map<String, Object> service;
+ Map<String, Object> resource;
+
+ public Map<String, Object> getService() {
+ return service;
+ }
+
+ public void setService(Map<String, Object> service) {
+ this.service = service;
+ }
+
+ public Map<String, Object> getResource() {
+ return resource;
+ }
+
+ public void setResource(Map<String, Object> resource) {
+ this.resource = resource;
+ }
+
+ }
+
+ public static class NotifArtifactTypes {
+
+ List<String> info;
+ List<String> lifecycle;
+
+ public List<String> getInfo() {
+ return info;
+ }
+
+ public void setInfo(List<String> info) {
+ this.info = info;
+ }
+
+ public List<String> getLifecycle() {
+ return lifecycle;
+ }
+
+ public void setLifecycle(List<String> lifecycle) {
+ this.lifecycle = lifecycle;
+ }
+
+ }
+
+ public static class NotifArtifactTypesResource {
+
+ List<ArtifcatTypeEnum> lifecycle;
+
+ }
+
+ public static enum ArtifcatTypeEnum {
+
+ MURANO_PKG("MURANO-PKG"), HEAT("HEAT"), DG_XML("DG_XML");
+
+ String value;
+
+ private ArtifcatTypeEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+
+ return value;
+ }
+ }
+
+ public List<String> getUebServers() {
+ return uebServers;
+ }
+
+ public void setUebServers(List<String> uebServers) {
+ this.uebServers = uebServers;
+ }
+
+ public String getDistributionNotifTopicName() {
+ return distributionNotifTopicName;
+ }
+
+ public void setDistributionNotifTopicName(String distributionNotifTopicName) {
+ this.distributionNotifTopicName = distributionNotifTopicName;
+ }
+
+ public String getDistributionStatusTopicName() {
+ return distributionStatusTopicName;
+ }
+
+ public void setDistributionStatusTopicName(String distributionStatusTopicName) {
+ this.distributionStatusTopicName = distributionStatusTopicName;
+ }
+
+ public Integer getInitRetryIntervalSec() {
+ return initRetryIntervalSec;
+ }
+
+ public void setInitRetryIntervalSec(Integer initRetryIntervalSec) {
+ this.initRetryIntervalSec = initRetryIntervalSec;
+ }
+
+ public ComponentArtifactTypesConfig getDistribNotifServiceArtifactTypes() {
+ return distribNotifServiceArtifactTypes;
+ }
+
+ public void setDistribNotifServiceArtifactTypes(ComponentArtifactTypesConfig distribNotifServiceArtifactTypes) {
+ this.distribNotifServiceArtifactTypes = distribNotifServiceArtifactTypes;
+ }
+
+ public ComponentArtifactTypesConfig getDistribNotifResourceArtifactTypes() {
+ return distribNotifResourceArtifactTypes;
+ }
+
+ public void setDistribNotifResourceArtifactTypes(ComponentArtifactTypesConfig distribNotifResourceArtifactTypes) {
+ this.distribNotifResourceArtifactTypes = distribNotifResourceArtifactTypes;
+ }
+
+ public String getUebPublicKey() {
+ return uebPublicKey;
+ }
+
+ public void setUebPublicKey(String uebPublicKey) {
+ this.uebPublicKey = uebPublicKey;
+ }
+
+ public String getUebSecretKey() {
+ return uebSecretKey;
+ }
+
+ public void setUebSecretKey(String uebSecretKey) {
+ this.uebSecretKey = uebSecretKey;
+ }
+
+ public List<String> getEnvironments() {
+ return environments;
+ }
+
+ public void setEnvironments(List<String> environments) {
+
+ Set<String> set = new HashSet<String>();
+ if (environments != null) {
+ set.addAll(environments);
+ this.environments = new ArrayList<String>(set);
+ } else {
+ this.environments = null;
+ }
+
+ }
+
+ public DistributionStatusTopicConfig getDistributionStatusTopic() {
+ return distributionStatusTopic;
+ }
+
+ public void setDistributionStatusTopic(DistributionStatusTopicConfig distributionStatusTopic) {
+ this.distributionStatusTopic = distributionStatusTopic;
+ }
+
+ public Integer getInitMaxIntervalSec() {
+ return initMaxIntervalSec;
+ }
+
+ public void setInitMaxIntervalSec(Integer initMaxIntervalSec) {
+ this.initMaxIntervalSec = initMaxIntervalSec;
+ }
+
+ public CreateTopicConfig getCreateTopic() {
+ return createTopic;
+ }
+
+ public void setCreateTopic(CreateTopicConfig createTopic) {
+ this.createTopic = createTopic;
+ }
+
+ public boolean isStartDistributionEngine() {
+ return startDistributionEngine;
+ }
+
+ public void setStartDistributionEngine(boolean startDistributionEngine) {
+ this.startDistributionEngine = startDistributionEngine;
+ }
+
+ public DistributionNotificationTopicConfig getDistributionNotificationTopic() {
+ return distributionNotificationTopic;
+ }
+
+ public void setDistributionNotificationTopic(DistributionNotificationTopicConfig distributionNotificationTopic) {
+ this.distributionNotificationTopic = distributionNotificationTopic;
+ }
+
+ public int getDefaultArtifactInstallationTimeout() {
+ return defaultArtifactInstallationTimeout;
+ }
+
+ public void setDefaultArtifactInstallationTimeout(int defaultArtifactInstallationTimeout) {
+ this.defaultArtifactInstallationTimeout = defaultArtifactInstallationTimeout;
+ }
+
+ public static class CreateTopicConfig {
+
+ private Integer partitionCount;
+ private Integer replicationCount;
+
+ public Integer getPartitionCount() {
+ return partitionCount;
+ }
+
+ public void setPartitionCount(Integer partitionCount) {
+ this.partitionCount = partitionCount;
+ }
+
+ public Integer getReplicationCount() {
+ return replicationCount;
+ }
+
+ public void setReplicationCount(Integer replicationCount) {
+ this.replicationCount = replicationCount;
+ }
+
+ @Override
+ public String toString() {
+ return "CreateTopicConfig [partitionCount=" + partitionCount + ", replicationCount=" + replicationCount + "]";
+ }
+
+ }
+
+ public static class EnvironmentConfig {
+
+ private String name;
+ private List<String> uebServers;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<String> getUebServers() {
+ return uebServers;
+ }
+
+ public void setUebServers(List<String> uebServers) {
+ this.uebServers = uebServers;
+ }
+
+ @Override
+ public String toString() {
+ return "EnvironmentConfig [name=" + name + ", uebServers=" + uebServers + "]";
+ }
+
+ }
+
+ public static class DistributionStatusTopicConfig {
+
+ private Integer pollingIntervalSec;
+ private Integer fetchTimeSec;
+ private String consumerGroup;
+ private String consumerId;
+
+ public Integer getPollingIntervalSec() {
+ return pollingIntervalSec;
+ }
+
+ public void setPollingIntervalSec(Integer pollingIntervalSec) {
+ this.pollingIntervalSec = pollingIntervalSec;
+ }
+
+ public Integer getFetchTimeSec() {
+ return fetchTimeSec;
+ }
+
+ public void setFetchTimeSec(Integer fetchTimeSec) {
+ this.fetchTimeSec = fetchTimeSec;
+ }
+
+ public String getConsumerGroup() {
+ return consumerGroup;
+ }
+
+ public void setConsumerGroup(String consumerGroup) {
+ this.consumerGroup = consumerGroup;
+ }
+
+ public String getConsumerId() {
+ return consumerId;
+ }
+
+ public void setConsumerId(String consumerId) {
+ this.consumerId = consumerId;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionStatusTopicConfig [pollingIntervalSec=" + pollingIntervalSec + ", fetchTimeSec=" + fetchTimeSec + ", consumerGroup=" + consumerGroup + ", consumerId=" + consumerId + "]";
+ }
+
+ }
+
+ public static class DistributionNotificationTopicConfig {
+
+ private Integer maxWaitingAfterSendingSeconds;
+ private Integer maxThreadPoolSize;
+ private Integer minThreadPoolSize;
+
+ public Integer getMaxWaitingAfterSendingSeconds() {
+ return maxWaitingAfterSendingSeconds;
+ }
+
+ public void setMaxWaitingAfterSendingSeconds(Integer maxWaitingAfterSendingSeconds) {
+ this.maxWaitingAfterSendingSeconds = maxWaitingAfterSendingSeconds;
+ }
+
+ public Integer getMaxThreadPoolSize() {
+ return maxThreadPoolSize;
+ }
+
+ public void setMaxThreadPoolSize(Integer maxThreadPoolSize) {
+ this.maxThreadPoolSize = maxThreadPoolSize;
+ }
+
+ public Integer getMinThreadPoolSize() {
+ return minThreadPoolSize;
+ }
+
+ public void setMinThreadPoolSize(Integer minThreadPoolSize) {
+ this.minThreadPoolSize = minThreadPoolSize;
+ }
+
+ @Override
+ public String toString() {
+ return "DistributionNotificationTopicConfig [maxWaitingAfterSendingSeconds=" + maxWaitingAfterSendingSeconds + ", maxThreadPoolSize=" + maxThreadPoolSize + ", minThreadPoolSize=" + minThreadPoolSize + "]";
+ }
+
+ }
+
+ public static class ComponentArtifactTypesConfig {
+
+ private List<String> info;
+ private List<String> lifecycle;
+
+ public List<String> getInfo() {
+ return info;
+ }
+
+ public void setInfo(List<String> info) {
+ this.info = info;
+ }
+
+ public List<String> getLifecycle() {
+ return lifecycle;
+ }
+
+ public void setLifecycle(List<String> lifecycle) {
+ this.lifecycle = lifecycle;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtifactTypesConfig [info=" + info + ", lifecycle=" + lifecycle + "]";
+ }
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorConfiguration.java
new file mode 100644
index 0000000000..8250df6c04
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorConfiguration.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import java.util.Map;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+public class ErrorConfiguration extends BasicConfiguration {
+
+ private Map<String, ErrorInfo> errors;
+
+ public Map<String, ErrorInfo> getErrors() {
+ return errors;
+ }
+
+ public void setErrors(Map<String, ErrorInfo> errors) {
+ this.errors = errors;
+ }
+
+ public ErrorInfo getErrorInfo(String key) {
+ ErrorInfo clone = null;
+ ErrorInfo other = errors.get(key);
+ if (other != null) {
+ clone = new ErrorInfo();
+ clone.cloneData(other);
+ }
+ return clone;
+ }
+
+ @Override
+ public String toString() {
+ return "ErrorConfiguration [errors=" + errors + "]";
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorInfo.java
new file mode 100644
index 0000000000..40daf96d36
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/ErrorInfo.java
@@ -0,0 +1,102 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import org.openecomp.sdc.common.config.EcompErrorName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ErrorInfo {
+
+ private Integer code;
+ private String message;
+ private String messageId;
+ private ErrorInfoType errorInfoType;
+
+ private static final String SVC_PREFIX = "SVC";
+ private static final String POL_PREFIX = "POL";
+
+ private static Logger log = LoggerFactory.getLogger(ErrorInfo.class.getName());
+
+ public ErrorInfo() {
+ this.errorInfoType = ErrorInfoType.OK;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public void setCode(Integer code) {
+ this.code = code;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessageId() {
+ return messageId;
+ }
+
+ public void setMessageId(String messageId) {
+ // Determining the type of error
+ if (messageId == null) {
+ this.errorInfoType = ErrorInfoType.OK;
+ } else if (messageId.startsWith(SVC_PREFIX)) {
+ this.errorInfoType = ErrorInfoType.SERVICE_EXCEPTION;
+ } else if (messageId.startsWith(POL_PREFIX)) {
+ this.errorInfoType = ErrorInfoType.POLICY_EXCEPTION;
+ } else {
+ // unexpected - should it fail the startup?
+ BeEcompErrorManager.getInstance().processEcompError(EcompErrorName.ErrorConfigFileFormat, "Error Info");
+ BeEcompErrorManager.getInstance().logErrorConfigFileFormat("Error Info",
+ "Could not set error info type for message id " + messageId);
+ log.debug("Error: unexpected error message ID {}, should start with {} or {}", messageId, SVC_PREFIX,
+ POL_PREFIX);
+ }
+ this.messageId = messageId;
+ }
+
+ public ErrorInfoType getErrorInfoType() {
+ return this.errorInfoType;
+ }
+
+ public void cloneData(ErrorInfo other) {
+ this.code = other.getCode();
+ this.message = other.getMessage();
+ this.messageId = other.getMessageId();
+ this.errorInfoType = other.errorInfoType;
+ }
+
+ @Override
+ public String toString() {
+ return "ErrorInfo [code=" + code + ", messageId=" + messageId + ", message=" + message + "]";
+ }
+
+ public enum ErrorInfoType {
+ OK, POLICY_EXCEPTION, SERVICE_EXCEPTION
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/Neo4jErrorsConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/Neo4jErrorsConfiguration.java
new file mode 100644
index 0000000000..24a85dc627
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/Neo4jErrorsConfiguration.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config;
+
+import java.util.Map;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+public class Neo4jErrorsConfiguration extends BasicConfiguration {
+ private Map<String, String> errors;
+
+ public Map<String, String> getErrors() {
+ return errors;
+ }
+
+ public void setErrors(Map<String, String> errors) {
+ this.errors = errors;
+ }
+
+ public String getErrorMessage(String key) {
+ return errors.get(key);
+ }
+
+ @Override
+ public String toString() {
+ return "Neo4jErrorsConfiguration [errors=" + errors + "]";
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/config/validation/DeploymentArtifactHeatConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/be/config/validation/DeploymentArtifactHeatConfiguration.java
new file mode 100644
index 0000000000..72b53bac75
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/config/validation/DeploymentArtifactHeatConfiguration.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.config.validation;
+
+import java.util.Map;
+
+/**
+ * Currently for deployment artifacts HEAT validation only.
+ *
+ * Other artifacts might require different fields validation Be sure to check it
+ * before you re-use this class
+ *
+ * @author paharoni
+ *
+ */
+public class DeploymentArtifactHeatConfiguration {
+
+ // All the rest of heat file is not needed for now...
+ String heat_template_version;
+
+ Map<String, Object> resources;
+
+ public String getHeat_template_version() {
+ return heat_template_version;
+ }
+
+ public void setHeat_template_version(String heat_template_version) {
+ this.heat_template_version = heat_template_version;
+ }
+
+ public Map<String, Object> getResources() {
+ return resources;
+ }
+
+ public void setResources(Map<String, Object> resources) {
+ this.resources = resources;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/be/monitoring/BeMonitoringService.java b/common-app-api/src/main/java/org/openecomp/sdc/be/monitoring/BeMonitoringService.java
new file mode 100644
index 0000000000..ac7391d8cc
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/be/monitoring/BeMonitoringService.java
@@ -0,0 +1,140 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.monitoring;
+
+import java.io.IOException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.openecomp.sdc.be.config.Configuration;
+import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.monitoring.MonitoringEvent;
+import org.openecomp.sdc.common.monitoring.MonitoringMetricsFetcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class BeMonitoringService {
+
+ private static final String URL = "%s://%s:%s/sdc2/rest/monitoring";
+ private static Logger monitoringLogger = LoggerFactory.getLogger("asdc.be.monitoring.service");
+ private static Logger log = LoggerFactory.getLogger(BeMonitoringService.class.getName());
+ private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ private class MonitoringScheduledTask implements Runnable {
+ @Override
+ public void run() {
+ monitoringLogger.trace("Executing BE Monitoring Task - Start");
+ MonitoringEvent monitoringMetrics = MonitoringMetricsFetcher.getInstance().getMonitoringMetrics();
+ processMonitoringEvent(monitoringMetrics);
+ monitoringLogger.trace("Executing BE Monitoring Task - Status = {}", monitoringMetrics.toString());
+ }
+ }
+
+ /**
+ * This executor will execute the Monitoring task.
+ */
+ ScheduledExecutorService monitoringExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "BE-Monitoring-Thread");
+ }
+ });
+ private ServletContext context;
+
+ public BeMonitoringService(ServletContext context) {
+ this.context = context;
+ }
+
+ public void start(int interval) {
+ Configuration config = ((ConfigurationManager) context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR))
+ .getConfiguration();
+ if (config.getSystemMonitoring().getEnabled()) {
+ log.info("BE monitoring service is enabled, interval is {} seconds", interval);
+ this.monitoringExecutor.scheduleAtFixedRate(new MonitoringScheduledTask(), 0, interval, TimeUnit.SECONDS);
+ } else {
+ log.info("BE monitoring service is disabled");
+ }
+ }
+
+ private void processMonitoringEvent(MonitoringEvent monitoringMetrics) {
+ CloseableHttpClient httpClient = null;
+ try {
+ Configuration config = ((ConfigurationManager) context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR))
+ .getConfiguration();
+ String redirectedUrl = String.format(URL, config.getBeProtocol(), config.getBeFqdn(),
+ config.getBeHttpPort());
+ httpClient = getHttpClient(config);
+ HttpPost httpPost = new HttpPost(redirectedUrl);
+ String monitoringMetricsJson = gson.toJson(monitoringMetrics);
+ HttpEntity myEntity = new StringEntity(monitoringMetricsJson);
+ httpPost.setEntity(myEntity);
+ httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+ int beResponseStatus;
+ CloseableHttpResponse beResponse;
+ try {
+ beResponse = httpClient.execute(httpPost);
+ beResponseStatus = beResponse.getStatusLine().getStatusCode();
+ if (beResponseStatus != HttpStatus.SC_OK) {
+ monitoringLogger.error("Unexpected HTTP response from BE : {}", beResponseStatus);
+ }
+ } catch (Exception e) {
+ monitoringLogger.error("Monitoring error when trying to connect to BE", e);
+ }
+ } catch (Exception e) {
+ monitoringLogger.error("Unexpected monitoring error", e);
+ } finally {
+ if (httpClient != null) {
+ try {
+ httpClient.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private CloseableHttpClient getHttpClient(Configuration config) {
+ int timeout = 3000;
+ RequestConfig.Builder requestBuilder = RequestConfig.custom();
+ requestBuilder.setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout);
+
+ HttpClientBuilder builder = HttpClientBuilder.create();
+ builder.setDefaultRequestConfig(requestBuilder.build());
+ return builder.build();
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ApplicationErrorCodesEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ApplicationErrorCodesEnum.java
new file mode 100644
index 0000000000..fa0f30842b
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ApplicationErrorCodesEnum.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public enum ApplicationErrorCodesEnum {
+
+ POL5000, // INTERNAL_ERROR
+
+ SVC4000, // INVALID_INPUT
+
+ SVC4002, // MISSING_INFORMATION
+ SVC4003, // RESOURCE_NAME_ALREADY_EXIST
+ SVC4004, // MISSING_CATEGORY
+ SVC4005, // MISSING_TAGS
+ SVC4006, // MISSING_DESCRIPTION
+ SVC4007, // BAD_CATEGORY
+
+ SVC4030; // RESTRICTED_ACTION
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactGroupTypeEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactGroupTypeEnum.java
new file mode 100644
index 0000000000..b7968dc53f
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactGroupTypeEnum.java
@@ -0,0 +1,66 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum ArtifactGroupTypeEnum {
+
+ INFORMATIONAL("INFORMATIONAL"),
+ DEPLOYMENT("DEPLOYMENT"),
+ LIFE_CYCLE("LIFE_CYCLE"),
+ SERVICE_API("SERVICE_API"),
+ TOSCA("TOSCA"),
+ OTHER("OTHER");
+
+ private String type;
+
+ ArtifactGroupTypeEnum(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public static ArtifactGroupTypeEnum findType(final String type) {
+ for (ArtifactGroupTypeEnum ate : ArtifactGroupTypeEnum.values()) {
+ if (ate.getType().equals(type)) {
+ return ate;
+ }
+ }
+ return null;
+ }
+
+ public static List<String> getAllTypes() {
+ List<String> types = new ArrayList<String>();
+ for (ArtifactGroupTypeEnum ate : ArtifactGroupTypeEnum.values()) {
+ types.add(ate.getType());
+ }
+ return types;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactTypeEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactTypeEnum.java
new file mode 100644
index 0000000000..c0fbad006a
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ArtifactTypeEnum.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Enum That Represents possible Artifacts Types.
+ *
+ */
+public enum ArtifactTypeEnum {
+ CHEF("CHEF"), PUPPET("PUPPET"), YANG("YANG"), SHELL_SCRIPT("SHELL_SCRIPT"), ICON( "ICON"),
+ UNKNOWN("UNKNOWN"), HEAT("HEAT"), DG_XML("DG_XML"), MURANO_PKG( "MURANO_PKG"), HEAT_ENV("HEAT_ENV"),
+ YANG_XML("YANG_XML"), HEAT_VOL("HEAT_VOL"), HEAT_NET("HEAT_NET"), OTHER("OTHER"), WORKFLOW("WORKFLOW"),
+ NETWORK_CALL_FLOW("NETWORK_CALL_FLOW"), TOSCA_TEMPLATE("TOSCA_TEMPLATE"), TOSCA_CSAR("TOSCA_CSAR"),
+ VNF_CATALOG("VNF_CATALOG"), VF_LICENSE("VF_LICENSE"),VENDOR_LICENSE("VENDOR_LICENSE"),
+ MODEL_INVENTORY_PROFILE("MODEL_INVENTORY_PROFILE"), MODEL_QUERY_SPEC("MODEL_QUERY_SPEC"),
+ APPC_CONFIG("APPC_CONFIG"), HEAT_NESTED("HEAT_NESTED"), HEAT_ARTIFACT("HEAT_ARTIFACT"),
+ VF_MODULES_METADATA("VF_MODULES_METADATA"),
+ //DCAE Artifacts
+ DCAE_TOSCA("DCAE_TOSCA"), DCAE_JSON("DCAE_JSON"), DCAE_POLICY("DCAE_POLICY"), DCAE_DOC("DCAE_DOC"),
+ DCAE_EVENT("DCAE_EVENT"), DCAE_INVENTORY_TOSCA("DCAE_INVENTORY_TOSCA"), DCAE_INVENTORY_JSON("DCAE_INVENTORY_JSON"),
+ DCAE_INVENTORY_POLICY("DCAE_INVENTORY_POLICY"), DCAE_INVENTORY_DOC("DCAE_INVENTORY_DOC"),
+ DCAE_INVENTORY_BLUEPRINT("DCAE_INVENTORY_BLUEPRINT"), DCAE_INVENTORY_EVENT("DCAE_INVENTORY_EVENT"),
+ //AAI Artifacts
+ AAI_SERVICE_MODEL("AAI_SERVICE_MODEL"), AAI_VF_MODEL("AAI_VF_MODEL"), AAI_VF_MODULE_MODEL("AAI_VF_MODULE_MODEL"),
+ AAI_VF_INSTANCE_MODEL("AAI_VF_INSTANCE_MODEL")
+
+ ;
+
+ ArtifactTypeEnum(String type) {
+ this.type = type;
+ }
+
+ private String type;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public static ArtifactTypeEnum findType(final String type) {
+ for (ArtifactTypeEnum ate : ArtifactTypeEnum.values()) {
+ //According to Pavel/Ella
+ if (ate.getType().equalsIgnoreCase(type)) {
+ return ate;
+ }
+ }
+ return null;
+ }
+
+ public static List<String> getAllTypes() {
+ List<String> types = new ArrayList<String>();
+ for (ArtifactTypeEnum ate : ArtifactTypeEnum.values()) {
+ types.add(ate.getType());
+ }
+ return types;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/BasicConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/BasicConfiguration.java
new file mode 100644
index 0000000000..5152b2af26
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/BasicConfiguration.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public class BasicConfiguration {
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationListener.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationListener.java
new file mode 100644
index 0000000000..d60ed6dabc
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationListener.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public class ConfigurationListener {
+
+ private Class<? extends BasicConfiguration> type;
+ private FileChangeCallback callBack;
+
+ public ConfigurationListener(Class<? extends BasicConfiguration> type, FileChangeCallback callBack) {
+ super();
+ this.type = type;
+ this.callBack = callBack;
+ }
+
+ public Class<? extends BasicConfiguration> getType() {
+ return type;
+ }
+
+ public void setType(Class<? extends BasicConfiguration> type) {
+ this.type = type;
+ }
+
+ public FileChangeCallback getCallBack() {
+ return callBack;
+ }
+
+ public void setCallBack(FileChangeCallback callBack) {
+ this.callBack = callBack;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationSource.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationSource.java
new file mode 100644
index 0000000000..5901d1e267
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ConfigurationSource.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public interface ConfigurationSource {
+
+ public <T> T getAndWatchConfiguration(Class<T> className, ConfigurationListener configurationListener);
+
+ public <T> void addWatchConfiguration(Class<T> className, ConfigurationListener configurationListener);
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/Constants.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/Constants.java
new file mode 100644
index 0000000000..673060cb0d
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/Constants.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public interface Constants {
+
+ public static final String APPLICATION_NAME = "application-name";
+ public static final String APPLICATION_VERSION = "application-version";
+ public static final String CONFIG_HOME = "config.home";
+ public static final String LOG_HOME = "log.home";
+ public static final String YAML_SUFFIX = ".yaml";
+ public static final String CONFIGURATION_SOURCE_ATTR = "configuration-source";
+ public static final String MDC_APP_NAME = "APP_NAME";
+ public static final String CONFIGURATION_MANAGER_ATTR = "configuration-manager";
+ public static final String HEALTH_CHECK_SERVICE_ATTR = "healthCheckService";
+ public static final String REST_CLIENT_ATTR = "rest-client";
+ public static final String ARTIFACT_DAO_ATTR = "artifact-dao";
+ public static final String UPLOAD_VALIDATORR_ATTR = "upload-validator";
+ public static final String THREAD_EXECUTOR_ATTR = "thread-executor";
+ public static final String ERROR_LOG_FORMAT = "EVENT = ARTIFACT_UPLOAD USER_ID=%s USER_NAME=%s ACCESS_IP=%s ACCESS_TYPE=%s RURL=%s SC=%d";
+ public static final String FIRST_NAME_HEADER = "HTTP_CSP_FIRSTNAME";
+ public static final String LAST_NAME_HEADER = "HTTP_CSP_LASTNAME";
+ public static final String USER_ID_HEADER = "USER_ID";
+ public static final String MD5_HEADER = "Content-MD5";
+ public static final String CONTENT_DISPOSITION_HEADER = "Content-Disposition";
+ public static final String CONTENT_TYPE_HEADER = "Content-Type";
+ public static final String ORIGIN_HEADER = "HTTP_IV_REMOTE_ADDRESS";
+ public static final String ACCESS_HEADER = "HTTP_CSP_WSTYPE";
+ public static final String X_ECOMP_REQUEST_ID_HEADER = "X-ECOMP-RequestID";
+ public static final String X_ECOMP_INSTANCE_ID_HEADER = "X-ECOMP-InstanceID";
+ public static final String X_ECOMP_SERVICE_ID_HEADER = "X-ECOMP-ServiceID";
+ public static final String HTTP = "http";
+ public static final String HTTPS = "https";
+ public static final String HTTP_IV_USER = "HTTP_IV_USER";
+ public static final String A4C_CSAR_CONTEXT = "/rest/csars/";
+ public static final String WEB_APPLICATION_CONTEXT_WRAPPER_ATTR = "web-application-context-wrapper";
+ public static final String CATALOG_BE = "catalog-be";
+ public static final String HTTP_CSP_FIRSTNAME = "HTTP_CSP_FIRSTNAME";
+ public static final String HTTP_CSP_LASTNAME = "HTTP_CSP_LASTNAME";
+ public static final String HTTP_IV_REMOTE_ADDRESS = "HTTP_IV_REMOTE_ADDRESS";
+ public static final String HTTP_CSP_TYPE = "HTTP_CSP_WSTYPE";
+ public static final String RESOURCE_SUPPORTED_VERSION = "0.0.1";
+ public static final String ARTIFACT_ID_FORMAT = "%s:%s:%s"; // resourceName:resourceVersion:artifactName
+ public static final String SERVICE_ARTIFACT_ID_FORMAT = "%s:%s:%s:%s"; // serviceName:serviceVersion:nodeTemplateName:artifactName
+ public static final String CONTENT_DISPOSITION = "content-disposition";
+ public static final String DOWNLOAD_ARTIFACT_LOGIC_ATTR = "downloadArtifactLogic";
+ public static final String SDC_RELEASE_VERSION_ATTR = "SDC-Version";
+ // public static final String AUDITING_MANAGER = "auditingManager";
+ // public static final String USER_ADMIN_MANAGER = "userAdminManager";
+ public static final String YEAR = "year";
+ public static final String MONTH = "month";
+ public static final String DAY = "day";
+ public static final String HOUR = "hour";
+ public static final String MINUTE = "minute";
+ public static final String NONE = "none";
+ public static final String RESOURCE_OPERATION_MANAGER = "resourceOperationManager";
+ public static final String PROPERTY_OPERATION_MANAGER = "propertyOperationManager";
+ public static final String SERVICE_OPERATION_MANAGER = "serviceOperationManager";
+ public static final String EMPTY_STRING = "";
+ public static final String NULL_STRING = "null";
+ public static final String DOUBLE_NULL_STRING = "null null";
+ public static final String ECOMP_ERROR_MNGR_ATTR = "ecompErrorMngrAttr";
+ public static final String AUTHORIZATION_HEADER = "Authorization";
+ public static final String ACCEPT_HEADER = "Accept";
+ public static final String STANDARD_INTERFACE_TYPE = "standard";
+ public static final String MURANO_PKG_ARTIFACT_TYPE = "MURANO-PKG";
+ public static final String ARTIFACT_GROUP_TYPE_FIELD = "artifactGroupType";
+
+ // TOSCA
+ public static final String TOSCA_META_PATH = "TOSCA-Metadata/TOSCA.meta";
+ public static final String TOSCA_META_ENTRY_DEFINITIONS = "Entry-Definitions";
+ public static final String USER_DEFINED_RESOURCE_NAMESPACE_PREFIX = "org.openecomp.resource.";
+
+ public static final String IS_BASE = "isBase";
+ public static final String HEAT_FILE_PROPS = "heat_file";
+ public static final String MODULE_NAME_FORMAT = "%s..%s..module-%s";
+ public static final String MODULE_DESC_PATTERN = "[\\_\\-\\.a-zA-Z0-9]+";
+ public static final String MODULE_OLD_NAME_PATTERN = "([\\w\\_\\-\\.\\s]+)(::module-)(\\d+)";
+ public static final String MODULE_NEW_NAME_PATTERN = "([\\w\\_\\-\\.\\s]+\\.\\.)([\\_\\-\\.a-zA-Z0-9]+)(..module-)(\\d+)";
+ public static final String MODULE_NAME_DELIMITER = "module-";
+ public static final String IMPORT_STRUCTURE = "importStructure";
+ public static final String DEFAULT_GROUP_VF_MODULE = "org.openecomp.groups.VfModule";
+
+ public static final String ARTIFACT_GROUP_TYPE = "artifactGroupType";
+ public static final String ARTIFACT_LABEL = "artifactLabel";
+ public static final String ARTIFACT_PAYLOAD_DATA = "payloadData";
+ public static final String ARTIFACT_DISPLAY_NAME = "artifactDisplayName";
+ public static final String ARTIFACT_DESCRIPTION = "description";
+ public static final String ARTIFACT_TYPE = "artifactType";
+ public static final String ARTIFACT_NAME = "artifactName";
+ public static final String ARTIFACT_ID = "uniqueId";
+ public static final String REQUIRED_ARTIFACTS = "requiredArtifacts";
+
+ public static final String ABSTRACT = "abstract";
+ public static final String GLOBAL_SUBSTITUTION_TYPE_SERVICE_TEMPLATE = "Definitions/GlobalSubstitutionTypesServiceTemplate.yaml";
+
+ public static final String VENDOR_LICENSE_MODEL = "vendor-license-model.xml";
+ public static final String VENDOR_LICENSE_LABEL = "vendorlicense";
+ public static final String VENDOR_LICENSE_DISPLAY_NAME = "Vendor License";
+ public static final String VENDOR_LICENSE_DESCRIPTION = " Vendor license file";
+
+ public static final String VF_LICENSE_MODEL = "vf-license-model.xml";
+ public static final String VF_LICENSE_LABEL = "vflicense";
+ public static final String VF_LICENSE_DISPLAY_NAME = "VF License";
+ public static final String VF_LICENSE_DESCRIPTION = "VF license file";
+ public static final String ARTIFACTS = "Artifacts/";
+ public static final String GET_INPUT = "get_input";
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/FileChangeCallback.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/FileChangeCallback.java
new file mode 100644
index 0000000000..5c4d3d5926
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/FileChangeCallback.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public interface FileChangeCallback {
+
+ public void reconfigure(BasicConfiguration obj);
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckInfo.java
new file mode 100644
index 0000000000..653b8b0866
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckInfo.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public class HealthCheckInfo {
+
+ private HealthCheckComponent healthCheckComponent;
+ private HealthCheckStatus healthCheckStatus;
+ private String version;
+ private String description;
+
+ public HealthCheckInfo(HealthCheckComponent healthCheckComponent, HealthCheckStatus healthCheckStatus,
+ String version, String description) {
+ super();
+ this.healthCheckComponent = healthCheckComponent;
+ this.healthCheckStatus = healthCheckStatus;
+ this.version = version;
+ this.description = description;
+ }
+
+ public HealthCheckInfo() {
+ super();
+ }
+
+ public HealthCheckComponent getHealthCheckComponent() {
+ return healthCheckComponent;
+ }
+
+ public HealthCheckStatus getHealthCheckStatus() {
+ return healthCheckStatus;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public enum HealthCheckComponent {
+ FE, BE, TITAN, ES, DE;
+ }
+
+ public enum HealthCheckStatus {
+ UP, DOWN, UNKNOWN;
+ }
+
+ @Override
+ public String toString() {
+ return "HealthCheckInfo [healthCheckComponent=" + healthCheckComponent + ", healthCheckStatus="
+ + healthCheckStatus + ", version=" + version + ", description=" + description + "]";
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckWrapper.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckWrapper.java
new file mode 100644
index 0000000000..29c6350516
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/HealthCheckWrapper.java
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import java.util.List;
+
+public class HealthCheckWrapper {
+
+ String sdcVersion;
+ String siteMode;
+ List<HealthCheckInfo> componentsInfo;
+
+ public HealthCheckWrapper(List<HealthCheckInfo> componentsInfo, String sdcVersion, String siteMode) {
+ super();
+ this.componentsInfo = componentsInfo;
+ this.sdcVersion = sdcVersion;
+ this.siteMode = siteMode;
+ }
+
+ public List<HealthCheckInfo> getComponentsInfo() {
+ return componentsInfo;
+ }
+
+ public void setComponentsInfo(List<HealthCheckInfo> componentsInfo) {
+ this.componentsInfo = componentsInfo;
+ }
+
+ public String getSdcVersion() {
+ return sdcVersion;
+ }
+
+ public void setSdcVersion(String sdcVersion) {
+ this.sdcVersion = sdcVersion;
+ }
+
+ public String getSiteMode() {
+ return siteMode;
+ }
+
+ public void setSiteMode(String siteMode) {
+ this.siteMode = siteMode;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ResourceType.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ResourceType.java
new file mode 100644
index 0000000000..b93fed2e0c
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ResourceType.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public enum ResourceType {
+ RESOURCE, SERVICE
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ResponseInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ResponseInfo.java
new file mode 100644
index 0000000000..337d5d1b8a
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ResponseInfo.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class ResponseInfo {
+
+ public static enum ResponseStatusEnum {
+ SUCCESS("success"),
+ LOGIN_FAILED("loginFailed"),
+ INTERNAL_ERROR("internalError"),
+ MISSING_HEADERS("required headers are missing"),
+ TIMEOUT("timeout"),
+ PARSING_ERROR("parsingFailed");
+
+ ResponseStatusEnum(String status) {
+ this.statusDescription = status;
+ }
+
+ public String statusDescription;
+ }
+
+ private ResponseStatusEnum applicativeStatus;
+ private String description;
+
+ public ResponseInfo(ResponseStatusEnum applicativeStatus, String description) {
+ super();
+ this.applicativeStatus = applicativeStatus;
+ this.description = description;
+ }
+
+ public ResponseInfo(ResponseStatusEnum applicativeStatus) {
+ super();
+ this.applicativeStatus = applicativeStatus;
+ }
+
+ public ResponseStatusEnum getApplicativeStatus() {
+ return applicativeStatus;
+ }
+
+ public void setApplicativeStatus(ResponseStatusEnum applicativeStatus) {
+ this.applicativeStatus = applicativeStatus;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String toString() {
+ ObjectMapper mapper = new ObjectMapper();
+ String tostring = super.toString();
+ try {
+ tostring = mapper.writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+
+ }
+ return tostring;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInfo.java
new file mode 100644
index 0000000000..f6608952d0
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInfo.java
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import java.util.List;
+
+public class ToscaNodeTypeInfo {
+
+ private String nodeName;
+ private String templateVersion;
+ private List<ToscaNodeTypeInterface> interfaces;
+ private String iconPath;
+ private String templateName;
+
+ public String getTemplateName() {
+ return templateName;
+ }
+
+ public void setTemplateName(String templateName) {
+ this.templateName = templateName;
+ }
+
+ public String getNodeName() {
+ return nodeName;
+ }
+
+ public void setNodeName(String nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ public String getTemplateVersion() {
+ return templateVersion;
+ }
+
+ public void setTemplateVersion(String templateVersion) {
+ this.templateVersion = templateVersion;
+ }
+
+ public List<ToscaNodeTypeInterface> getInterfaces() {
+ return interfaces;
+ }
+
+ public void setInterfaces(List<ToscaNodeTypeInterface> interfaces) {
+ this.interfaces = interfaces;
+ }
+
+ public String getIconPath() {
+ return iconPath;
+ }
+
+ public void setIconPath(String iconPath) {
+ this.iconPath = iconPath;
+ }
+
+ @Override
+ public String toString() {
+ return "ToscaNodeTypeInfo [nodeName=" + nodeName + ", templateVersion=" + templateVersion + ", templateName="
+ + templateName + ", interfaces=" + interfaces + ", iconPath=" + iconPath + "]";
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInterface.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInterface.java
new file mode 100644
index 0000000000..4095714f4b
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/ToscaNodeTypeInterface.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import java.util.List;
+
+public class ToscaNodeTypeInterface {
+
+ List<String> scripts;
+
+ public List<String> getScripts() {
+ return scripts;
+ }
+
+ public void setScripts(List<String> scripts) {
+ this.scripts = scripts;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/UploadArtifactInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/UploadArtifactInfo.java
new file mode 100644
index 0000000000..9e1734135c
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/UploadArtifactInfo.java
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public class UploadArtifactInfo {
+
+ public UploadArtifactInfo() {
+
+ }
+
+ public UploadArtifactInfo(String artifactName, String artifactPath, ArtifactTypeEnum artifactType,
+ String artifactDescription) {
+ super();
+ this.artifactName = artifactName;
+ this.artifactPath = artifactPath;
+ this.artifactType = artifactType;
+ this.artifactDescription = artifactDescription;
+ }
+
+ public UploadArtifactInfo(String artifactName, String artifactPath, ArtifactTypeEnum artifactType,
+ String artifactDescription, String artifactData) {
+ super();
+ this.artifactName = artifactName;
+ this.artifactPath = artifactPath;
+ this.artifactType = artifactType;
+ this.artifactDescription = artifactDescription;
+ this.artifactData = artifactData;
+ }
+
+ private String artifactName;
+ private String artifactPath;
+ private ArtifactTypeEnum artifactType;
+ private String artifactDescription;
+ private String artifactData;
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactPath() {
+ return artifactPath;
+ }
+
+ public void setArtifactPath(String artifactPath) {
+ this.artifactPath = artifactPath;
+ }
+
+ public ArtifactTypeEnum getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(ArtifactTypeEnum artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getArtifactDescription() {
+ return artifactDescription;
+ }
+
+ public void setArtifactDescription(String artifactDescription) {
+ this.artifactDescription = artifactDescription;
+ }
+
+ public String getArtifactData() {
+ return artifactData;
+ }
+
+ public void setArtifactData(String artifactData) {
+ this.artifactData = artifactData;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((artifactData == null) ? 0 : artifactData.hashCode());
+ result = prime * result + ((artifactDescription == null) ? 0 : artifactDescription.hashCode());
+ result = prime * result + ((artifactName == null) ? 0 : artifactName.hashCode());
+ result = prime * result + ((artifactPath == null) ? 0 : artifactPath.hashCode());
+ result = prime * result + ((artifactType == null) ? 0 : artifactType.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ UploadArtifactInfo other = (UploadArtifactInfo) obj;
+ if (artifactData == null) {
+ if (other.artifactData != null)
+ return false;
+ } else if (!artifactData.equals(other.artifactData))
+ return false;
+ if (artifactDescription == null) {
+ if (other.artifactDescription != null)
+ return false;
+ } else if (!artifactDescription.equals(other.artifactDescription))
+ return false;
+ if (artifactName == null) {
+ if (other.artifactName != null)
+ return false;
+ } else if (!artifactName.equals(other.artifactName))
+ return false;
+ if (artifactPath == null) {
+ if (other.artifactPath != null)
+ return false;
+ } else if (!artifactPath.equals(other.artifactPath))
+ return false;
+ if (artifactType != other.artifactType)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "UploadArtifactInfo [artifactName=" + artifactName + ", artifactPath=" + artifactPath + ", artifactType="
+ + artifactType + ", artifactDescription=" + artifactDescription + ", artifactData=" + artifactData
+ + "]";
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/UserRoleEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/UserRoleEnum.java
new file mode 100644
index 0000000000..6d333bf457
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/UserRoleEnum.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public enum UserRoleEnum {
+ ADMIN("ADMIN"),
+ DESIGNER("DESIGNER"),
+ TESTER("TESTER"),
+ GOVERNOR("GOVERNOR"),
+ OPS("OPS"),
+ PRODUCT_MANAGER("PRODUCT_MANAGER"),
+ PRODUCT_STRATEGIST("PRODUCT_STRATEGIST");
+
+ private String name;
+
+ UserRoleEnum(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlConstants.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlConstants.java
new file mode 100644
index 0000000000..0dadef9292
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlConstants.java
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+public interface YamlConstants {
+
+ public static final String NODE_TYPES = "node_types";
+ public static final String INTERFACES = "interfaces";
+ public static final String TEMPLATE_VERSION = "template_version";
+ public static final String TEMPLATE_NAME = "template_name";
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlSuffixEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlSuffixEnum.java
new file mode 100644
index 0000000000..4ac6290940
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/api/YamlSuffixEnum.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum YamlSuffixEnum {
+
+ YAML("YAML"),
+
+ yaml("yaml"),
+
+ YML("YML"),
+
+ yml("yml");
+
+ YamlSuffixEnum(String suffix) {
+ this.suffix = suffix;
+ }
+
+ private String suffix;
+
+ public String getSuffix() {
+ return suffix;
+ }
+
+ public void setSuufix(String suffix) {
+ this.suffix = suffix;
+ }
+
+ public static List<String> getSuffixes() {
+
+ List<String> arrayList = new ArrayList<String>();
+
+ for (YamlSuffixEnum yamlSuffixEnum : YamlSuffixEnum.values()) {
+ arrayList.add(yamlSuffixEnum.getSuffix());
+ }
+
+ return arrayList;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/AbsEcompErrorManager.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/AbsEcompErrorManager.java
new file mode 100644
index 0000000000..6a54071f42
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/AbsEcompErrorManager.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+import java.util.Formatter;
+import java.util.IllegalFormatException;
+import java.util.Locale;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration.EcompAlarmSeverity;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration.EcompErrorSeverity;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration.EcompErrorType;
+
+import com.jcabi.aspects.Loggable;
+
+@Loggable(prepend = true, value = Loggable.TRACE, trim = false)
+public abstract class AbsEcompErrorManager implements IEcompErrorManager {
+
+ public static final String PARAM_STR = "%s";
+
+ public abstract IEcompConfigurationManager getConfigurationManager();
+
+ @Deprecated
+ @Override
+ public void processEcompError(EcompErrorName ecompErrorName, String ecompErrorContext,
+ String... descriptionParams) {
+
+ /*
+ * //Getting the relevant config manager IEcompConfigurationManager
+ * configurationManager = getConfigurationManager();
+ *
+ * //Getting the error by name EcompErrorInfo ecompErrorInfo =
+ * configurationManager.getEcompErrorConfiguration().getEcompErrorInfo(
+ * ecompErrorName.name());
+ *
+ * if (ecompErrorInfo != null){ ecompErrorInfo =
+ * setDescriptionParams(ecompErrorInfo, ecompErrorName.name(),
+ * descriptionParams); EcompErrorLogUtil.logEcompError(ecompErrorName,
+ * ecompErrorInfo, ecompErrorContext); } else {
+ * EcompErrorLogUtil.logEcompError(EcompErrorName.EcompErrorNotFound,
+ * getErrorInfoForUnknownErrorName(ecompErrorName.name()),
+ * ecompErrorContext); }
+ */
+
+ }
+
+ private EcompErrorInfo setDescriptionParams(EcompErrorInfo ecompErrorInfo, String ecompErrorName,
+ String... descriptionParams) {
+ String description = ecompErrorInfo.getDescription();
+ // Counting number of params in description
+ int countMatches = StringUtils.countMatches(description, PARAM_STR);
+ // Catching cases when there are more params passed than there are in
+ // the description (formatter will ignore extra params and won't throw
+ // exception)
+ if (countMatches != descriptionParams.length) {
+ return getErrorInfoForDescriptionParamsMismatch(ecompErrorName);
+ }
+ // Setting params of the description if any
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ try {
+ formatter.format(description, (Object[]) descriptionParams).toString();
+ ecompErrorInfo.setDescription(formatter.toString());
+ } catch (IllegalFormatException e) {
+ // Number of passed params doesn't match number of params in config
+ // file
+ return getErrorInfoForDescriptionParamsMismatch(ecompErrorName);
+ } finally {
+ formatter.close();
+ }
+ return ecompErrorInfo;
+ }
+
+ private EcompErrorInfo getErrorInfoForUnknownErrorName(String ecompErrorName) {
+ EcompErrorInfo ecompErrorInfo = new EcompErrorInfo();
+ ecompErrorInfo.setCode(EcompErrorConfiguration.ECODE_PREFIX + "3001");
+ ecompErrorInfo.setType(EcompErrorType.CONFIG_ERROR.name());
+ ecompErrorInfo.setSeverity(EcompErrorSeverity.ERROR.name());
+ ecompErrorInfo.setAlarmSeverity(EcompAlarmSeverity.MAJOR.name());
+ ecompErrorInfo.setDescription(new StringBuilder().append("Ecomp error element not found in YAML, name: ")
+ .append(ecompErrorName).toString());
+ return ecompErrorInfo;
+ }
+
+ private EcompErrorInfo getErrorInfoForDescriptionParamsMismatch(String ecompErrorName) {
+ EcompErrorInfo ecompErrorInfo = new EcompErrorInfo();
+ ecompErrorInfo.setCode(EcompErrorConfiguration.ECODE_PREFIX + "3002");
+ ecompErrorInfo.setType(EcompErrorType.CONFIG_ERROR.name());
+ ecompErrorInfo.setSeverity(EcompErrorSeverity.ERROR.name());
+ ecompErrorInfo.setAlarmSeverity(EcompAlarmSeverity.MAJOR.name());
+ ecompErrorInfo.setDescription(new StringBuilder()
+ .append("Ecomp error description params mismatch between code and YAML or wrong format, name: ")
+ .append(ecompErrorName).toString());
+ return ecompErrorInfo;
+ }
+
+ public void processEcompError(String context, EcompErrorEnum ecompErrorEnum, String... descriptionParams) {
+
+ EcompErrorLogUtil.logEcompError(context, ecompErrorEnum, descriptionParams);
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompClassification.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompClassification.java
new file mode 100644
index 0000000000..31499486de
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompClassification.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public enum EcompClassification {
+
+ INFORMATION("I"), WARNING("W"), ERROR("E"), FATAL("F");
+
+ String classification;
+
+ EcompClassification(String classification) {
+ this.classification = classification;
+ }
+
+ public String getClassification() {
+ return classification;
+ }
+
+ public void setClassification(String classification) {
+ this.classification = classification;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorCode.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorCode.java
new file mode 100644
index 0000000000..0f45d34886
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorCode.java
@@ -0,0 +1,155 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public enum EcompErrorCode {
+
+ E_100("Authentication problem towards U-EB server. Reason: %s",
+ "An Authentication failure occured during access to UEB server. Please check that UEB keys are configured correctly in ASDC BE distribution configuration."), E_199(
+ "Internal authentication problem. Description: %s"),
+
+ E_200("ASDC Backend probably lost connectivity to either one of the following components: Titan DB, Elasticsearch, UEB Cluster. Please check the logs for more information."), E_201(
+ "ASDC Backend probably lost connectivity to Titan DB. Please check the logs for more information."), E_202(
+ "ASDC Backend probably lost connectivity to ElasticSearch. Please check the logs for more information."), E_203(
+ "ASDC Backend probably lost connectivity to UEB Cluster. Please check the logs for more information.",
+ "Check connectivity to UEB cluster which is configured under parameter uebServers in distribution-configuration.yaml."), E_204(
+ "Unable to connect to a valid ASDC Backend Server",
+ "Please check connectivity from this FE instance towards BE or BE Load Balancer. Please check that parameters in FE configuration.yaml: beHost, beHttpPort and beSslPort point to a valid host and port values."),
+
+ E_205("ASDC Backend Recovery to either one of the following components: Titan DB, Elasticsearch, UEB Cluster."), E_206(
+ "ASDC Backend connection recovery to Titan DB."), E_207(
+ "ASDC Backend connection recovery to ElasticSearch."), E_208(
+ "ASDC Backend connection recovery to UEB Cluster."), E_209(
+ "Connectivity to ASDC BE Server is recovered."), E_210(
+ "Connection problem towards U-EB server. Reason: %s",
+ "Please check that that parameter uebServers in distribution-configuration.yaml points to a valid UEB Cluster."), E_211(
+ "Connection problem towards U-EB server. Cannot reach host %s",
+ "Please check that that parameter uebServers in distribution-configuration.yaml points to a valid UEB Cluster."), E_212(
+ "Couldn't resolve hostIP. Desciption: %s"), E_213(
+ "Site switch over was done. Site is now in %s mode"), E_299(
+ "Internal Connection problem. Description: %s"),
+
+ // [resource/service/product]
+ E_300("Mandatory %s Component %s cannot be found in repository"),
+ // [SERVICE/RESOURCE/PRODUCT] [id] is not valid. Cannot be found in graph.
+ E_301("%s Component %s is not valid. Cannot be found in graph."), E_302(
+ "Configuration parameter %s is invalid. Value configured is %s."), E_303(
+ "Error occured during access to U-EB Server. Data not found: %s",
+ "An error occured during access to UEB Server, ASDC failed to either register or unregister to/from UEB topic."), E_304(
+ "The artifact type %s does not appear in the list of valid artifacts %s"), E_305(
+ "Configuration parameter %s is missing"), E_306(
+ "Configuration parameter %s is invalid. At least %s values shall be configured"), E_307(
+ "Invalid configuration in YAML file. %s"), E_308(
+ "Artifact uploaded has missing information. Missing %s"), E_309(
+ "Artifact %s requested is not found"), E_310(
+ "User %s requested is not found"), E_311(
+ "Ecomp error description params mismatch between code and YAML or wrong format, name: %s"), E_312(
+ "Ecomp error element not found in YAML, name: %s"),
+
+ E_399("Internal Invalid Object. Description: %s"),
+
+ E_400("The type %s of %s is invalid"), E_401("The value %s of %s from type %s is invalid"), E_402(
+ "Payload of artifact uploaded is invalid (invalid MD5 or encryption)"), E_403(
+ "Input for artifact metadata is invalid"), E_404("%s %s required is missing"), E_405(
+ "Failed to convert json input to object"), E_406("Distribution %s required is missing"),
+
+ E_499("Invalid input. Description: %s"),
+
+ E_500("Catalog-BE was not initialized properly"), E_501(
+ "Failed to add resource instance of resource %s to service %s"), E_502(
+ "Error occured during access to U-EB Server. Operation: %s",
+ "An error occured in ASDC distribution mechanism. Please check the logs for more information."), E_503(
+ "Error occured in Distribution Engine. Failed operation: %s",
+ "System Error occured in ASDC Distribution Engine. Please check ASDC logs for more details."), E_504(
+ "Failed adding node of type %s to graph."), E_505(
+ "Operation towards database failed.",
+ "Please check Titan DB health or look at the logs for more details."), E_506(
+ "Unexpected error during operation"), E_507(
+ "Going to execute rollback on graph."), E_508(
+ "Failed to lock object for update. Type = %s, Id = %s"), E_509(
+ "Failed to create node %s on graph. status is %s"), E_510(
+ "Failed to update node %s on graph. Status is %s"), E_511(
+ "Failed to delete node %s on graph. Status is %s"), E_512(
+ "Failed to retrieve node %s from graph. Status is %s"), E_513(
+ "Failed to find parent node of %s on graph. Status is %s"), E_514(
+ "Failed to fetch all nodes with type %s of parent node %s . Status is %s"), E_515(
+ "Cannot find node with type %s associated with node %s . Status is %s"), E_516(
+ "Error occured in Component Cleaner Task. Failed operation: %s"), E_517(
+ "Error when logging FE HTTP request/response"), E_518(
+ "Error when trying to access FE Portal page"),
+
+ E_599("Internal flow error. Operation: %s"),
+
+ E_900("Unexpected error during BE REST API execution"), E_901("General error during FE Health Check"), E_999(
+ "Unexpected error. Description: %s");
+
+ /*
+ * 100-199 Security/Permission Related - Authentication problems (from
+ * external client, to external server) - Certification errors -
+ *
+ * 200-299 Availability/Timeout Related - connectivity error - connection
+ * timeout
+ *
+ * 300-399 Data Access/Integrity Related - Data in graph in invalid(E.g. no
+ * creator is found for service) - Artifact is missing in ES, but exists in
+ * graph.
+ *
+ * 400-499 Schema Interface Type/Validation - received payload checksum is
+ * inavlid - received json is not valid
+ *
+ * 500-599 Business/Flow Processing Related - check out to service is not
+ * allowed - rollback is done - failed to generate heat file
+ *
+ *
+ * 600-899 Reserved – do not use
+ *
+ * 900-999 Unknown Errors - unexpected exception
+ */
+
+ String description;
+ String resolution;
+
+ EcompErrorCode(String description, String resolution) {
+ this.description = description;
+ this.resolution = resolution;
+ }
+
+ EcompErrorCode(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getResolution() {
+ return resolution;
+ }
+
+ public void setResolution(String resolution) {
+ this.resolution = resolution;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorConfiguration.java
new file mode 100644
index 0000000000..368104dea1
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorConfiguration.java
@@ -0,0 +1,161 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+public class EcompErrorConfiguration extends BasicConfiguration {
+
+ private Map<String, EcompErrorInfo> errors = new HashMap<>();
+ static final String ECODE_PREFIX = "ASDC_";
+ private static final Pattern ECODE_PATTERN = Pattern.compile("^" + ECODE_PREFIX + "\\d{4}$");
+
+ public Map<String, EcompErrorInfo> getErrors() {
+ return errors;
+ }
+
+ public void setErrors(Map<String, EcompErrorInfo> errors) {
+ // Validating ecomp-error-configuration.yaml
+ for (Map.Entry<String, EcompErrorInfo> ecompErrorInfo : errors.entrySet()) {
+ String ecompErrorName = ecompErrorInfo.getKey();
+ EcompErrorInfo res = validateEcompErrorInfo(ecompErrorName, ecompErrorInfo.getValue());
+ if (res != null) {
+ // Validation failed!
+ EcompErrorLogUtil.logEcompError(EcompErrorName.EcompConfigFileFormat, res, this.getClass().getName());
+ return;
+ }
+ }
+ // Validation passed
+ this.errors = errors;
+ }
+
+ public EcompErrorInfo getEcompErrorInfo(String key) {
+ EcompErrorInfo clone = null;
+ EcompErrorInfo other = errors.get(key);
+ if (other != null) {
+ clone = new EcompErrorInfo();
+ clone.cloneData(other);
+ }
+ return clone;
+ }
+
+ protected EcompErrorInfo validateEcompErrorInfo(String ecompErrorName, EcompErrorInfo ecompErrorInfoToValidate) {
+ if (ecompErrorInfoToValidate == null) {
+ return getErrorInfoForConfigFile("error " + ecompErrorName + " not found ");
+ }
+ String type = ecompErrorInfoToValidate.getType();
+ if (type == null) {
+ return getErrorInfoForConfigFile("empty error type for error " + ecompErrorName
+ + ", value should be one of the following: " + Arrays.asList(EcompErrorType.values()));
+ }
+ try {
+ EcompErrorType.valueOf(type);
+ } catch (IllegalArgumentException e) {
+ return getErrorInfoForConfigFile("error type " + type + " is invalid for error " + ecompErrorName
+ + ", value should be one of the following: " + Arrays.asList(EcompErrorType.values()));
+ }
+
+ String severity = ecompErrorInfoToValidate.getSeverity();
+ if (severity == null) {
+ return getErrorInfoForConfigFile("empty error severity for error " + ecompErrorName
+ + ", value should be one of the following: " + Arrays.asList(EcompErrorSeverity.values()));
+ }
+ try {
+ EcompErrorSeverity.valueOf(severity);
+ } catch (IllegalArgumentException e) {
+ return getErrorInfoForConfigFile("error severity " + severity + " is invalid for error " + ecompErrorName
+ + ", value should be one of the following: " + Arrays.asList(EcompErrorSeverity.values()));
+ }
+ String alarmSeverity = ecompErrorInfoToValidate.getAlarmSeverity();
+ if (alarmSeverity == null) {
+ return getErrorInfoForConfigFile("empty error alarm for error " + ecompErrorName
+ + ", , value should be one of the following: " + Arrays.asList(EcompAlarmSeverity.values()));
+ }
+ try {
+ EcompAlarmSeverity.valueOf(alarmSeverity);
+ } catch (IllegalArgumentException e) {
+ return getErrorInfoForConfigFile("error alarm severity " + alarmSeverity + " is invalid for error "
+ + ecompErrorName + ", , value should be one of the following: "
+ + Arrays.asList(EcompAlarmSeverity.values()));
+ }
+
+ String code = ecompErrorInfoToValidate.getCode();
+ if (code != null && ECODE_PATTERN.matcher(code).matches()) {
+ String[] split = code.split("_");
+ int parseInt = Integer.parseInt(split[1]);
+ if (parseInt < 3010 || parseInt > 9999) {
+ return getErrorInfoForInvalidCode(code, ecompErrorName);
+ }
+ } else {
+ return getErrorInfoForInvalidCode(code, ecompErrorName);
+ }
+ return null;
+ }
+
+ private EcompErrorInfo getErrorInfoForInvalidCode(String code, String ecompErrorName) {
+ return getErrorInfoForConfigFile("error code " + code + " is invalid for error " + ecompErrorName
+ + ", should be in format ASDC_[3010-9999]");
+ }
+
+ private EcompErrorInfo getErrorInfoForConfigFile(String errorMessage) {
+ EcompErrorInfo ecompErrorInfo = new EcompErrorInfo();
+ ecompErrorInfo.setCode(ECODE_PREFIX + "3000");
+ ecompErrorInfo.setType(EcompErrorType.CONFIG_ERROR.name());
+ ecompErrorInfo.setSeverity(EcompErrorSeverity.FATAL.name());
+ ecompErrorInfo.setAlarmSeverity(EcompAlarmSeverity.CRITICAL.name());
+ ecompErrorInfo.setDescription(errorMessage);
+ return ecompErrorInfo;
+ }
+
+ @Override
+ public String toString() {
+ return "EcompErrorConfiguration [errors=" + errors + "]";
+ }
+
+ /*******************************
+ * Enums
+ */
+
+ public enum EcompErrorType {
+ RECOVERY, CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM;
+ }
+
+ public enum EcompAlarmSeverity {
+ CRITICAL, MAJOR, MINOR, INFORMATIONAL, NONE;
+ }
+
+ public enum EcompErrorSeverity {
+ INFO, WARN, ERROR, FATAL;
+ }
+
+ public enum EcompErrorSeverityPrefix {
+ I, W, E, F;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(Arrays.asList(EcompErrorType.values()));
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorEnum.java
new file mode 100644
index 0000000000..c79677a9ef
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorEnum.java
@@ -0,0 +1,484 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public enum EcompErrorEnum {
+
+ BeUebAuthenticationError(EcompErrorCode.E_100, ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR),
+
+ InternalAuthenticationInfo(EcompErrorCode.E_199, ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION), InternalAuthenticationWarning(EcompErrorCode.E_199,
+ ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.MINOR,
+ EcompClassification.WARNING), InternalAuthenticationError(EcompErrorCode.E_199,
+ ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), InternalAuthenticationFatal(EcompErrorCode.E_199,
+ ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.CRITICAL,
+ EcompClassification.FATAL),
+
+ BeHealthCheckRecovery(EcompErrorCode.E_205, ErrorType.RECOVERY, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION, null), BeHealthCheckTitanRecovery(EcompErrorCode.E_206, ErrorType.RECOVERY,
+ AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION,
+ null), BeHealthCheckElasticSearchRecovery(EcompErrorCode.E_207, ErrorType.RECOVERY,
+ AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION,
+ null), BeHealthCheckUebClusterRecovery(EcompErrorCode.E_208, ErrorType.RECOVERY,
+ AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION,
+ null), FeHealthCheckRecovery(EcompErrorCode.E_209, ErrorType.RECOVERY,
+ AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION,
+ null), BeHealthCheckError(EcompErrorCode.E_200, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ BeHealthCheckRecovery),
+
+ BeHealthCheckTitanError(EcompErrorCode.E_201, ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR, BeHealthCheckTitanRecovery), BeHealthCheckElasticSearchError(
+ EcompErrorCode.E_202, ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ BeHealthCheckElasticSearchRecovery), BeHealthCheckUebClusterError(EcompErrorCode.E_203,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ BeHealthCheckUebClusterRecovery), FeHealthCheckError(EcompErrorCode.E_204,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ FeHealthCheckRecovery), BeUebConnectionError(EcompErrorCode.E_210,
+ ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeUebUnkownHostError(EcompErrorCode.E_211,
+ ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR),
+
+ FqdnResolveError(EcompErrorCode.E_212, ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), SiteSwitchoverInfo(EcompErrorCode.E_213, ErrorType.RECOVERY,
+ AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION),
+
+ InternalConnectionInfo(EcompErrorCode.E_299, ErrorType.CONNECTION_PROBLEM, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION), InternalConnectionWarning(EcompErrorCode.E_299,
+ ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MINOR,
+ EcompClassification.WARNING), InternalConnectionError(EcompErrorCode.E_299,
+ ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), InternalConnectionFatal(EcompErrorCode.E_299,
+ ErrorType.CONNECTION_PROBLEM, AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+
+ BeComponentMissingError(EcompErrorCode.E_300, ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeIncorrectComponentError(EcompErrorCode.E_301, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MAJOR, EcompClassification.ERROR), BeInvalidConfigurationError(EcompErrorCode.E_302,
+ ErrorType.CONFIG_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.FATAL), BeUebObjectNotFoundError(EcompErrorCode.E_303,
+ ErrorType.DATA_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeDistributionEngineInvalidArtifactType(
+ EcompErrorCode.E_304, ErrorType.DATA_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.WARNING), BeMissingConfigurationError(
+ EcompErrorCode.E_305, ErrorType.CONFIG_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.FATAL), BeConfigurationInvalidListSizeError(
+ EcompErrorCode.E_306, ErrorType.CONFIG_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.FATAL), ErrorConfigFileFormat(
+ EcompErrorCode.E_307, ErrorType.CONFIG_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeMissingArtifactInformationError(
+ EcompErrorCode.E_308, ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeArtifactMissingError(
+ EcompErrorCode.E_309,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeUserMissingError(
+ EcompErrorCode.E_310,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), EcompMismatchParam(
+ EcompErrorCode.E_311,
+ ErrorType.CONFIG_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), EcompMissingError(
+ EcompErrorCode.E_312,
+ ErrorType.CONFIG_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR),
+
+ InternalDataInfo(EcompErrorCode.E_399, ErrorType.DATA_ERROR, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION), InternalDataWarning(EcompErrorCode.E_399, ErrorType.DATA_ERROR,
+ AlarmSeverity.MINOR, EcompClassification.WARNING), InternalDataError(EcompErrorCode.E_399,
+ ErrorType.DATA_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), InternalDataFatal(EcompErrorCode.E_399, ErrorType.DATA_ERROR,
+ AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+
+ BeInvalidTypeError(EcompErrorCode.E_400, ErrorType.DATA_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.WARNING), BeInvalidValueError(EcompErrorCode.E_401, ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR, EcompClassification.WARNING), BeArtifactPayloadInvalid(EcompErrorCode.E_402,
+ ErrorType.DATA_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeArtifactInformationInvalidError(EcompErrorCode.E_403,
+ ErrorType.DATA_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeGraphObjectMissingError(EcompErrorCode.E_404,
+ ErrorType.DATA_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR), BeInvalidJsonInput(EcompErrorCode.E_405,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeDistributionMissingError(
+ EcompErrorCode.E_406, ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR, EcompClassification.ERROR),
+
+ InvalidInputInfo(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION), InvalidInputWarning(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MINOR, EcompClassification.WARNING), InvalidInputError(EcompErrorCode.E_499,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), InvalidInputFatal(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+
+ BeInitializationError(EcompErrorCode.E_500, ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR), BeFailedAddingResourceInstanceError(EcompErrorCode.E_501,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR, EcompClassification.ERROR), BeUebSystemError(
+ EcompErrorCode.E_502, ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeDistributionEngineSystemError(EcompErrorCode.E_503,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedAddingNodeTypeError(EcompErrorCode.E_504,
+ ErrorType.DATA_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR), BeDaoSystemError(EcompErrorCode.E_505,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR), BeSystemError(EcompErrorCode.E_506,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR), BeExecuteRollbackError(
+ EcompErrorCode.E_507, ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedLockObjectError(
+ EcompErrorCode.E_508,
+ ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.CRITICAL,
+ EcompClassification.WARNING), BeFailedCreateNodeError(
+ EcompErrorCode.E_509,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedUpdateNodeError(
+ EcompErrorCode.E_510,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedDeleteNodeError(
+ EcompErrorCode.E_511,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedRetrieveNodeError(
+ EcompErrorCode.E_512,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedFindParentError(
+ EcompErrorCode.E_513,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedFindAllNodesError(
+ EcompErrorCode.E_514,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeFailedFindAssociationError(
+ EcompErrorCode.E_515,
+ ErrorType.DATA_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), BeComponentCleanerSystemError(
+ EcompErrorCode.E_516,
+ ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), FeHttpLoggingError(
+ EcompErrorCode.E_517,
+ ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MINOR,
+ EcompClassification.ERROR), FePortalServletError(
+ EcompErrorCode.E_518,
+ ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MAJOR,
+ EcompClassification.ERROR),
+
+ InternalFlowInfo(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION), InternalFlowWarning(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MINOR, EcompClassification.WARNING), InternalFlowError(EcompErrorCode.E_599,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), InternalFlowFatal(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+
+ BeRestApiGeneralError(EcompErrorCode.E_900, ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ EcompClassification.ERROR, null), FeHealthCheckGeneralError(EcompErrorCode.E_901, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.CRITICAL, EcompClassification.ERROR, null),
+
+ InternalUnexpectedInfo(EcompErrorCode.E_999, ErrorType.SYSTEM_ERROR, AlarmSeverity.INFORMATIONAL,
+ EcompClassification.INFORMATION), InternalUnexpectedWarning(EcompErrorCode.E_999, ErrorType.SYSTEM_ERROR,
+ AlarmSeverity.MINOR, EcompClassification.WARNING), InternalUnexpectedError(EcompErrorCode.E_999,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR,
+ EcompClassification.ERROR), InternalUnexpectedFatal(EcompErrorCode.E_999,
+ ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+
+ /*
+ * BeUebAuthenticationError(EcompErrorCode.E_100,
+ * ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.MAJOR,
+ * EcompClassification.ERROR),
+ *
+ * InternalAuthenticationInfo(EcompErrorCode.E_199,
+ * ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.INFORMATIONAL,
+ * EcompClassification.INFORMATION),
+ * InternalAuthenticationWarning(EcompErrorCode.E_199,
+ * ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.MINOR,
+ * EcompClassification.WARNING),
+ * InternalAuthenticationError(EcompErrorCode.E_199,
+ * ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.MAJOR,
+ * EcompClassification.ERROR),
+ * InternalAuthenticationFatal(EcompErrorCode.E_199,
+ * ErrorType.AUTHENTICATION_PROBLEM, AlarmSeverity.CRITICAL,
+ * EcompClassification.FATAL),
+ * //BeFailedDeletingResourceInstanceError(EcompErrorCode.E_200,
+ * ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR, Classification.ERROR),
+ *
+ * BeHealthCheckRecovery(EcompErrorCode.E_205, ErrorType.RECOVERY,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION, null),
+ * BeHealthCheckTitanRecovery(EcompErrorCode.E_206, ErrorType.RECOVERY,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION, null),
+ * BeHealthCheckElasticSearchRecovery(EcompErrorCode.E_207,
+ * ErrorType.RECOVERY, AlarmSeverity.INFORMATIONAL,
+ * EcompClassification.INFORMATION, null),
+ * BeHealthCheckUebClusterRecovery(EcompErrorCode.E_208, ErrorType.RECOVERY,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION, null),
+ * FeHealthCheckRecovery(EcompErrorCode.E_209, ErrorType.RECOVERY,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION, null),
+ * BeHealthCheckError(EcompErrorCode.E_200, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ * BeHealthCheckRecovery),
+ *
+ * BeHealthCheckTitanError(EcompErrorCode.E_201, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ * BeHealthCheckTitanRecovery),
+ * BeHealthCheckElasticSearchError(EcompErrorCode.E_202,
+ * ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ * EcompClassification.ERROR, BeHealthCheckElasticSearchRecovery),
+ * BeHealthCheckUebClusterError(EcompErrorCode.E_203,
+ * ErrorType.SYSTEM_ERROR, AlarmSeverity.CRITICAL,
+ * EcompClassification.ERROR, BeHealthCheckUebClusterRecovery),
+ * FeHealthCheckError(EcompErrorCode.E_204, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR,
+ * FeHealthCheckRecovery), BeUebConnectionError(EcompErrorCode.E_210,
+ * ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ * EcompClassification.ERROR), BeUebUnkownHostError(EcompErrorCode.E_211,
+ * ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ * EcompClassification.ERROR),
+ *
+ * InternalConnectionInfo(EcompErrorCode.E_299,
+ * ErrorType.CONNECTION_PROBLEM, AlarmSeverity.INFORMATIONAL,
+ * EcompClassification.INFORMATION),
+ * InternalConnectionWarning(EcompErrorCode.E_299,
+ * ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MINOR,
+ * EcompClassification.WARNING),
+ * InternalConnectionError(EcompErrorCode.E_299,
+ * ErrorType.CONNECTION_PROBLEM, AlarmSeverity.MAJOR,
+ * EcompClassification.ERROR), InternalConnectionFatal(EcompErrorCode.E_299,
+ * ErrorType.CONNECTION_PROBLEM, AlarmSeverity.CRITICAL,
+ * EcompClassification.FATAL),
+ *
+ * BeComponentMissingError(EcompErrorCode.E_300, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeIncorrectComponentError(EcompErrorCode.E_301, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeInvalidConfigurationError(EcompErrorCode.E_302, ErrorType.CONFIG_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.FATAL),
+ * BeUebObjectNotFoundError(EcompErrorCode.E_303, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeDistributionEngineInvalidArtifactType(EcompErrorCode.E_304,
+ * ErrorType.DATA_ERROR, AlarmSeverity.MAJOR, EcompClassification.WARNING),
+ * BeMissingConfigurationError(EcompErrorCode.E_305, ErrorType.CONFIG_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.FATAL),
+ * BeConfigurationInvalidListSizeError(EcompErrorCode.E_306,
+ * ErrorType.CONFIG_ERROR, AlarmSeverity.MAJOR, EcompClassification.FATAL),
+ * ErrorConfigFileFormat(EcompErrorCode.E_307, ErrorType.CONFIG_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeMissingArtifactInformationError(EcompErrorCode.E_308,
+ * ErrorType.DATA_ERROR, AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeArtifactMissingError(EcompErrorCode.E_309, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeUserMissingError(EcompErrorCode.E_310, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * EcompMismatchParam(EcompErrorCode.E_311, ErrorType.CONFIG_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * EcompMissingError(EcompErrorCode.E_312, ErrorType.CONFIG_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ *
+ * InternalDataInfo(EcompErrorCode.E_399, ErrorType.DATA_ERROR,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION),
+ * InternalDataWarning(EcompErrorCode.E_399, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MINOR, EcompClassification.WARNING),
+ * InternalDataError(EcompErrorCode.E_399, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * InternalDataFatal(EcompErrorCode.E_399, ErrorType.DATA_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+ *
+ * BeInvalidTypeError(EcompErrorCode.E_400, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.WARNING),
+ * BeInvalidValueError(EcompErrorCode.E_401, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.WARNING),
+ * BeArtifactPayloadInvalid(EcompErrorCode.E_402, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeArtifactInformationInvalidError(EcompErrorCode.E_403,
+ * ErrorType.DATA_ERROR, AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeDistributionMissingError(EcompErrorCode.E_404, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeGraphObjectMissingError(EcompErrorCode.E_404, ErrorType.DATA_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR),
+ * BeInvalidJsonInput(EcompErrorCode.E_405, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ *
+ * InvalidInputInfo(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION),
+ * InvalidInputWarning(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MINOR, EcompClassification.WARNING),
+ * InvalidInputError(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * InvalidInputFatal(EcompErrorCode.E_499, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+ *
+ * BeInitializationError(EcompErrorCode.E_500, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR),
+ * BeFailedAddingResourceInstanceError(EcompErrorCode.E_501,
+ * ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeUebSystemError(EcompErrorCode.E_502, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeDistributionEngineSystemError(EcompErrorCode.E_503,
+ * ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedAddingNodeTypeError(EcompErrorCode.E_504, ErrorType.DATA_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR),
+ * BeDaoSystemError(EcompErrorCode.E_505, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR),
+ * BeSystemError(EcompErrorCode.E_506, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR),
+ * BeExecuteRollbackError(EcompErrorCode.E_507, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedLockObjectError(EcompErrorCode.E_508, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.WARNING),
+ * BeFailedCreateNodeError(EcompErrorCode.E_509, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedUpdateNodeError(EcompErrorCode.E_510, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedDeleteNodeError(EcompErrorCode.E_511, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedRetrieveNodeError(EcompErrorCode.E_512, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedFindParentError(EcompErrorCode.E_513, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedFindAllNodesError(EcompErrorCode.E_514, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeFailedFindAssociationError(EcompErrorCode.E_515, ErrorType.DATA_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * BeComponentCleanerSystemError(EcompErrorCode.E_516,
+ * ErrorType.SYSTEM_ERROR, AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * FeHttpLoggingError(EcompErrorCode.E_517, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MINOR, EcompClassification.ERROR),
+ * FePortalServletError(EcompErrorCode.E_518, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ *
+ * InternalFlowInfo(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION),
+ * InternalFlowWarning(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MINOR, EcompClassification.WARNING),
+ * InternalFlowError(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * InternalFlowFatal(EcompErrorCode.E_599, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+ *
+ * BeRestApiGeneralError(EcompErrorCode.E_900, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR, null),
+ * FeHealthCheckGeneralError(EcompErrorCode.E_901, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.ERROR, null),
+ *
+ * InternalUnexpectedInfo(EcompErrorCode.E_999, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.INFORMATIONAL, EcompClassification.INFORMATION),
+ * InternalUnexpectedWarning(EcompErrorCode.E_999, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MINOR, EcompClassification.WARNING),
+ * InternalUnexpectedError(EcompErrorCode.E_999, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.MAJOR, EcompClassification.ERROR),
+ * InternalUnexpectedFatal(EcompErrorCode.E_999, ErrorType.SYSTEM_ERROR,
+ * AlarmSeverity.CRITICAL, EcompClassification.FATAL),
+ */
+
+ ;
+
+ EcompErrorCode ecompErrorCode;
+ ErrorType eType;
+ AlarmSeverity alarmSeverity;
+ EcompClassification classification;
+ EcompErrorEnum clearCode;
+
+ EcompErrorEnum(EcompErrorCode ecompErrorCode, ErrorType eType, AlarmSeverity alarmSeverity,
+ EcompClassification classification, EcompErrorEnum clearCode) {
+
+ this.ecompErrorCode = ecompErrorCode;
+ this.eType = eType;
+ this.alarmSeverity = alarmSeverity;
+ this.classification = classification;
+ this.clearCode = clearCode;
+ }
+
+ EcompErrorEnum(EcompErrorCode ecompErrorCode, ErrorType eType, AlarmSeverity alarmSeverity,
+ EcompClassification classification) {
+
+ this.ecompErrorCode = ecompErrorCode;
+ this.eType = eType;
+ this.alarmSeverity = alarmSeverity;
+ this.classification = classification;
+ }
+
+ public ErrorType geteType() {
+ return eType;
+ }
+
+ public void seteType(ErrorType eType) {
+ this.eType = eType;
+ }
+
+ public AlarmSeverity getAlarmSeverity() {
+ return alarmSeverity;
+ }
+
+ public void setAlarmSeverity(AlarmSeverity alarmSeverity) {
+ this.alarmSeverity = alarmSeverity;
+ }
+
+ public EcompErrorCode getEcompErrorCode() {
+ return ecompErrorCode;
+ }
+
+ public void setEcompErrorCode(EcompErrorCode ecompErrorCode) {
+ this.ecompErrorCode = ecompErrorCode;
+ }
+
+ public EcompClassification getClassification() {
+ return classification;
+ }
+
+ public void setClassification(EcompClassification classification) {
+ this.classification = classification;
+ }
+
+ public EcompErrorEnum getClearCode() {
+ return clearCode;
+ }
+
+ public void setClearCode(EcompErrorEnum clearCode) {
+ this.clearCode = clearCode;
+ }
+
+ public static enum ErrorType {
+ RECOVERY, CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM
+ }
+
+ public static enum AlarmSeverity {
+ CRITICAL, MAJOR, MINOR, INFORMATIONAL, NONE
+ }
+
+ // public String toString() {
+ // return eType + "," + eCode + "," + desc;
+ // }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorInfo.java
new file mode 100644
index 0000000000..e1fd34b26d
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorInfo.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public class EcompErrorInfo {
+
+ private String type;
+ private String code;
+ private String severity;
+ private String description;
+ private String alarmSeverity;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getSeverity() {
+ return severity;
+ }
+
+ public void setSeverity(String severity) {
+ this.severity = severity;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getAlarmSeverity() {
+ return alarmSeverity;
+ }
+
+ public void setAlarmSeverity(String alarmSeverity) {
+ this.alarmSeverity = alarmSeverity;
+ }
+
+ public void cloneData(EcompErrorInfo other) {
+ this.type = other.getType();
+ this.severity = other.getSeverity();
+ this.description = other.getDescription();
+ this.code = other.getCode();
+ this.alarmSeverity = other.getAlarmSeverity();
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorLogUtil.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorLogUtil.java
new file mode 100644
index 0000000000..274625aae4
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorLogUtil.java
@@ -0,0 +1,199 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+import java.util.Formatter;
+import java.util.IllegalFormatException;
+import java.util.Locale;
+
+import org.apache.commons.lang3.StringUtils;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration.EcompAlarmSeverity;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration.EcompErrorSeverity;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration.EcompErrorType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+import fj.data.Either;
+
+public class EcompErrorLogUtil {
+
+ private static String ECOMP_ERROR_TMPL = "ETYPE = \"%s\" ENAME = \"%s\" ECODE = \"%s\" ECONTEXT = \"%s\" EDESC = \"%s\"";
+ private static Logger log = LoggerFactory.getLogger(EcompErrorLogUtil.class.getName());
+ private static final String FATAL_ERROR_PREFIX = "FATAL ERROR!! ";
+
+ public static void logEcompError(EcompErrorName ecompErrorName, EcompErrorInfo ecompErrorInfo,
+ String ecompErrorContext, String... ecompDescriptionParams) {
+ if (ecompErrorInfo != null) {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ try {
+ String description = ecompErrorInfo.getDescription();
+ String severityStr = ecompErrorInfo.getSeverity();
+ EcompErrorConfiguration.EcompErrorSeverity severity = EcompErrorSeverity.ERROR;
+ // Since there is no FATAL log level, this is how we distinguish
+ // the FATAL errors
+ if (severityStr.equals(EcompErrorConfiguration.EcompErrorSeverity.FATAL.name())) {
+ description = FATAL_ERROR_PREFIX + description;
+ severity = EcompErrorSeverity.FATAL;
+ } else if (severityStr.equals(EcompErrorConfiguration.EcompErrorSeverity.WARN.name())) {
+ severity = EcompErrorSeverity.WARN;
+ } else if (severityStr.equals(EcompErrorConfiguration.EcompErrorSeverity.INFO.name())) {
+ severity = EcompErrorSeverity.INFO;
+ }
+
+ MDC.put("alarmSeverity", ecompErrorInfo.getAlarmSeverity());
+ // else it stays ERROR
+ formatter.format(ECOMP_ERROR_TMPL, ecompErrorInfo.getType(), ecompErrorName.name(),
+ ecompErrorInfo.getCode(), ecompErrorContext, description);
+ switch (severity) {
+ case INFO:
+ log.info(formatter.toString());
+ break;
+ case WARN:
+ log.warn(formatter.toString());
+ break;
+ case ERROR:
+ log.error(formatter.toString());
+ break;
+ case FATAL:
+ // same as ERROR for now, might be additional logic later..
+ log.error(formatter.toString());
+ break;
+ default:
+ break;
+ }
+ } finally {
+ formatter.close();
+ MDC.remove("alarmSeverity");
+ }
+ }
+ }
+
+ public static void logEcompError(String ecompErrorContext, EcompErrorEnum ecompErrorEnum,
+ String... ecompDescriptionParams) {
+ logEcompError(ecompErrorContext, ecompErrorEnum, true, ecompDescriptionParams);
+ }
+
+ public static void logEcompError(String ecompErrorContext, EcompErrorEnum ecompErrorEnum, boolean logMissingParams,
+ String... ecompDescriptionParams) {
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ try {
+ String description = ecompErrorEnum.getEcompErrorCode().getDescription();
+
+ Either<String, Boolean> setDescriptionParamsResult = setDescriptionParams(ecompErrorEnum,
+ ecompDescriptionParams);
+ if (setDescriptionParamsResult.isLeft()) {
+ description = setDescriptionParamsResult.left().value();
+ } else {
+ EcompErrorEnum mismatchErrorEnum = EcompErrorEnum.EcompMismatchParam;
+ if (logMissingParams == true) {
+ logEcompError("logEcompError", mismatchErrorEnum, false, ecompErrorEnum.name().toString());
+ return;
+ } else {
+ log.info("Failed to log the error code " + mismatchErrorEnum);
+ return;
+ }
+ }
+ EcompClassification classification = ecompErrorEnum.getClassification();
+
+ EcompErrorConfiguration.EcompErrorSeverity severity = EcompErrorSeverity.ERROR;
+ // Since there is no FATAL log level, this is how we distinguish the
+ // FATAL errors
+ if (classification == EcompClassification.FATAL) {
+ description = FATAL_ERROR_PREFIX + description;
+ severity = EcompErrorSeverity.FATAL;
+ } else if (classification == EcompClassification.WARNING) {
+ severity = EcompErrorSeverity.WARN;
+ } else if (classification == EcompClassification.INFORMATION) {
+ severity = EcompErrorSeverity.INFO;
+ }
+
+ String eCode = createEcode(ecompErrorEnum);
+
+ MDC.put("alarmSeverity", ecompErrorEnum.alarmSeverity.name());
+ // else it stays ERROR
+ formatter.format(ECOMP_ERROR_TMPL, ecompErrorEnum.geteType(), ecompErrorEnum.name(), eCode,
+ ecompErrorContext, description);
+ switch (severity) {
+ case INFO:
+ log.info(formatter.toString());
+ break;
+ case WARN:
+ log.warn(formatter.toString());
+ break;
+ case ERROR:
+ log.error(formatter.toString());
+ break;
+ case FATAL:
+ // same as ERROR for now, might be additional logic later..
+ log.error(formatter.toString());
+ break;
+ default:
+ break;
+ }
+ } finally {
+ formatter.close();
+ MDC.remove("alarmSeverity");
+ }
+ }
+
+ public static String createEcode(EcompErrorEnum ecompErrorEnum) {
+
+ EcompClassification classification = ecompErrorEnum.getClassification();
+ String ecompErrorCode = ecompErrorEnum.getEcompErrorCode().name();
+
+ String ecodeNumber = ecompErrorCode.substring(ecompErrorCode.indexOf("_") + 1);
+ String eCode = "ASDC" + ecodeNumber + classification.getClassification();
+
+ return eCode;
+ }
+
+ private static Either<String, Boolean> setDescriptionParams(EcompErrorEnum ecompErrorEnum,
+ String... descriptionParams) {
+ String description = ecompErrorEnum.getEcompErrorCode().getDescription();
+
+ // Counting number of params in description
+ int countMatches = StringUtils.countMatches(description, AbsEcompErrorManager.PARAM_STR);
+ // Catching cases when there are more params passed than there are in
+ // the description (formatter will ignore extra params and won't throw
+ // exception)
+ if (countMatches != descriptionParams.length) {
+ return Either.right(false);
+ }
+ // Setting params of the description if any
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ try {
+ formatter.format(description, (Object[]) descriptionParams).toString();
+ return Either.left(formatter.toString());
+ } catch (IllegalFormatException e) {
+ // Number of passed params doesn't match number of params in config
+ // file
+ return Either.right(false);
+ } finally {
+ formatter.close();
+ }
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorName.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorName.java
new file mode 100644
index 0000000000..6bd139bb28
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/EcompErrorName.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public enum EcompErrorName {
+ // general
+ EcompConfigFileFormat, EcompErrorNotFound, EcompErrorDescParamsMismatch,
+
+ // FE
+ FeHealthCheckConnectionError, FeHealthCheckRecovery /* Recovery */, FeHttpLoggingError, FePortalServletError, FeHealthCheckGeneralError,
+
+ // BE section here...
+ BeHealthCheckError, BeHealthCheckRecovery, /* Recovery */
+ BeRestApiGeneralError, BeInitializationError, BeResourceMissingError, BeServiceMissingError, BeMissingArtifactInformationError, BeArtifactMissingError, BeArtifactPayloadInvalid, BeUserMissingError, BeArtifactInformationInvalidError, BeIncorrectServiceError, BeFailedAddingResourceInstanceError,
+ // BeFailedDeletingResourceInstanceError,
+ BeFailedAddingCapabilityTypeError, BeCapabilityTypeMissingError, BeInterfaceMissingError,
+ // BeRepositoryObjectNotFoundError,
+ // BeRepositoryDeleteError,
+ // BeRepositoryQueryError,
+ BeDaoSystemError, BeSystemError, BeInvalidConfigurationError, BeMissingConfigurationError, BeUebConnectionError, BeUebObjectNotFoundError, BeUebSystemError, BeDistributionEngineSystemError, BeDistributionEngineInvalidArtifactType, BeConfigurationInvalidListSizeError, BeUebAuthenticationError, BeUebUnkownHostError, BeInvalidTypeError, BeInvalidValueError, BeFailedLockObjectError, BeInvalidJsonInput, BeDistributionMissingError, ErrorConfigFileFormat,
+ // model
+ BeFailedCreateNodeError, BeFailedUpdateNodeError, BeFailedDeleteNodeError, BeFailedRetrieveNodeError, BeExecuteRollbackError, BeFailedFindParentError, BeFailedFindAllNodesError, BeFailedFindAssociationError, BeFailedToAssociateError, BeComponentCleanerSystemError;
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompConfigurationManager.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompConfigurationManager.java
new file mode 100644
index 0000000000..c16d8d329b
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompConfigurationManager.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public interface IEcompConfigurationManager {
+ public EcompErrorConfiguration getEcompErrorConfiguration();
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompErrorManager.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompErrorManager.java
new file mode 100644
index 0000000000..7b9aa8e730
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/IEcompErrorManager.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config;
+
+public interface IEcompErrorManager {
+
+ public void processEcompError(EcompErrorName ecompErrorName, String ecompErrorContext, String... descriptionParams);
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/config/generation/GenerateEcompErrorsCsv.java b/common-app-api/src/main/java/org/openecomp/sdc/common/config/generation/GenerateEcompErrorsCsv.java
new file mode 100644
index 0000000000..275339ee20
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/config/generation/GenerateEcompErrorsCsv.java
@@ -0,0 +1,242 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.config.generation;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.openecomp.sdc.common.config.EcompErrorEnum;
+import org.openecomp.sdc.common.config.EcompErrorLogUtil;
+import org.openecomp.sdc.common.config.EcompErrorEnum.AlarmSeverity;
+import org.openecomp.sdc.common.config.EcompErrorEnum.ErrorType;
+
+public class GenerateEcompErrorsCsv {
+
+ private static String DATE_FORMAT = "dd-M-yyyy-hh-mm-ss";
+
+ private static String NEW_LINE = System.getProperty("line.separator");
+
+ private static void usage() {
+ System.out.println("java org.openecomp.sdc.common.config.generation.GenerateEcompErrorsCsv <target folder>");
+ System.exit(1);
+ }
+
+ public static void main(String[] args) {
+
+ String targetFolder = "target";
+ if (args.length > 1) {
+ targetFolder = args[0];
+ }
+
+ GenerateEcompErrorsCsv ecompErrorsCsv = new GenerateEcompErrorsCsv();
+
+ ecompErrorsCsv.generateEcompErrorsCsvFile(targetFolder, true);
+ }
+
+ public static class EcompErrorRow {
+
+ String errorName;
+ String errorCode;
+ String description;
+ ErrorType errorType;
+ AlarmSeverity alarmSeverity;
+ String cleanErrorCode;
+ String resolution;
+
+ public String getErrorName() {
+ return errorName;
+ }
+
+ public void setErrorName(String errorName) {
+ this.errorName = errorName;
+ }
+
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ public void setErrorCode(String errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ErrorType getErrorType() {
+ return errorType;
+ }
+
+ public void setErrorType(ErrorType errorType) {
+ this.errorType = errorType;
+ }
+
+ public AlarmSeverity getAlarmSeverity() {
+ return alarmSeverity;
+ }
+
+ public void setAlarmSeverity(AlarmSeverity alarmSeverity) {
+ this.alarmSeverity = alarmSeverity;
+ }
+
+ public String getCleanErrorCode() {
+ return cleanErrorCode;
+ }
+
+ public void setCleanErrorCode(String cleanErrorCode) {
+ this.cleanErrorCode = cleanErrorCode;
+ }
+
+ public String getResolution() {
+ return resolution;
+ }
+
+ public void setResolution(String resolution) {
+ this.resolution = resolution;
+ }
+
+ }
+
+ public boolean generateEcompErrorsCsvFile(String targetFolder, boolean addTimeToFileName) {
+
+ targetFolder += File.separator;
+
+ boolean result = false;
+ String dateFormatted = "";
+
+ if (addTimeToFileName == true) {
+ DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+ Date date = new Date();
+
+ dateFormatted = "." + dateFormat.format(date);
+
+ }
+
+ String outputFile = targetFolder + "ecompErrorCodes" + dateFormatted + ".csv";
+
+ FileWriter writer = null;
+
+ try {
+ writer = new FileWriter(outputFile);
+
+ List<EcompErrorRow> errors = new ArrayList<>();
+
+ for (EcompErrorEnum ecompErrorEnum : EcompErrorEnum.values()) {
+
+ EcompErrorRow ecompErrorRow = new EcompErrorRow();
+
+ String errorCode = EcompErrorLogUtil.createEcode(ecompErrorEnum);
+
+ EcompErrorEnum clearCodeEnum = ecompErrorEnum.getClearCode();
+ String cleanErrorCode = null;
+ if (clearCodeEnum != null) {
+ cleanErrorCode = EcompErrorLogUtil.createEcode(clearCodeEnum);
+ }
+
+ ecompErrorRow.setAlarmSeverity(ecompErrorEnum.getAlarmSeverity());
+ ecompErrorRow.setCleanErrorCode(cleanErrorCode);
+ ecompErrorRow.setDescription(ecompErrorEnum.getEcompErrorCode().getDescription());
+ ecompErrorRow.setErrorCode(errorCode);
+ ecompErrorRow.setErrorName(ecompErrorEnum.name().toString());
+ ecompErrorRow.setErrorType(ecompErrorEnum.geteType());
+ ecompErrorRow.setResolution(ecompErrorEnum.getEcompErrorCode().getResolution());
+
+ errors.add(ecompErrorRow);
+ }
+
+ writeHeaders(writer);
+
+ for (EcompErrorRow ecompErrorRow : errors) {
+ writer.append(addInvertedCommas(ecompErrorRow.getErrorCode()));
+ writer.append(',');
+ writer.append(addInvertedCommas(ecompErrorRow.getErrorType().toString()));
+ writer.append(',');
+ writer.append(addInvertedCommas(ecompErrorRow.getDescription()));
+ writer.append(',');
+ writer.append(addInvertedCommas(ecompErrorRow.getResolution()));
+ writer.append(',');
+ writer.append(addInvertedCommas(ecompErrorRow.getAlarmSeverity().toString()));
+ writer.append(',');
+ writer.append(addInvertedCommas(ecompErrorRow.getErrorName()));
+ writer.append(',');
+ writer.append(addInvertedCommas(ecompErrorRow.getCleanErrorCode()));
+ writer.append(NEW_LINE);
+ }
+
+ result = true;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (writer != null) {
+ try {
+ writer.flush();
+ writer.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ return result;
+ }
+
+ private void writeHeaders(FileWriter writer) throws IOException {
+
+ writer.append("\"ERROR CODE\"");
+ writer.append(',');
+ writer.append("\"ERROR TYPE\"");
+ writer.append(',');
+ writer.append("\"DESCRIPTION\"");
+ writer.append(',');
+ writer.append("\"RESOLUTION\"");
+ writer.append(',');
+ writer.append("\"ALARM SEVERITY\"");
+ writer.append(',');
+ writer.append("\"ERROR NAME\"");
+ writer.append(',');
+ writer.append("\"CLEAN CODE\"");
+ writer.append(NEW_LINE);
+ }
+
+ private String addInvertedCommas(String str) {
+
+ if (str == null) {
+ return "\"\"";
+ }
+
+ return "\"" + str + "\"";
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/AuditingFieldsKeysEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/AuditingFieldsKeysEnum.java
new file mode 100644
index 0000000000..263d446674
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/AuditingFieldsKeysEnum.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.datastructure;
+
+import java.util.Date;
+
+public enum AuditingFieldsKeysEnum {
+ // General
+ AUDIT_TIMESTAMP(Date.class, "TIMESTAMP"),
+ AUDIT_ACTION(String.class, "ACTION"),
+ AUDIT_MODIFIER_NAME(String.class, "MODIFIER_ID"),
+ AUDIT_MODIFIER_UID(String.class, "MODIFIER"),
+ AUDIT_STATUS(String.class, "STATUS"),
+ AUDIT_DESC(String.class, "DESC"),
+ AUDIT_SERVICE_INSTANCE_ID(String.class, "SERVICE_INSTANCE_ID"),
+ AUDIT_INVARIANT_UUID(String.class, "INVARIANT_UUID"),
+ AUDIT_REQUEST_ID(String.class, "REQUEST_ID"),
+
+ // Users administration
+ AUDIT_USER_UID(String.class, "USER"),
+ AUDIT_USER_BEFORE(String.class, "USER_BEFORE"),
+ AUDIT_USER_AFTER(String.class, "USER_AFTER"),
+ AUDIT_USER_DETAILS(String.class, "DETAILS"),
+
+ // Resource administration
+ AUDIT_RESOURCE_NAME(String.class, "RESOURCE_NAME"),
+ AUDIT_RESOURCE_TYPE(String.class, "RESOURCE_TYPE"),
+ AUDIT_RESOURCE_CURR_VERSION(String.class, "CURR_VERSION"),
+ AUDIT_RESOURCE_PREV_VERSION(String.class, "PREV_VERSION"),
+ AUDIT_RESOURCE_CURR_STATE(String.class, "CURR_STATE"),
+ AUDIT_RESOURCE_PREV_STATE(String.class, "PREV_STATE"),
+ AUDIT_RESOURCE_COMMENT(String.class, "COMMENT"),
+ AUDIT_ARTIFACT_DATA(String.class, "ARTIFACT_DATA"),
+ AUDIT_PREV_ARTIFACT_UUID(String.class, "PREV_ARTIFACT_UUID"),
+ AUDIT_CURR_ARTIFACT_UUID(String.class, "CURR_ARTIFACT_UUID"),
+ AUDIT_RESOURCE_DPREV_STATUS(String.class, "DPREV_STATUS"),
+ AUDIT_RESOURCE_DCURR_STATUS(String.class, "DCURR_STATUS"),
+ AUDIT_RESOURCE_TOSCA_NODE_TYPE(String.class, "TOSCA_NODE_TYPE"),
+
+ // Distribution Engine
+ AUDIT_DISTRIBUTION_ENVRIONMENT_NAME(String.class, "D_ENV"),
+ AUDIT_DISTRIBUTION_TOPIC_NAME(String.class, "TOPIC_NAME"),
+ AUDIT_DISTRIBUTION_NOTIFICATION_TOPIC_NAME(String.class, "DNOTIF_TOPIC"),
+ AUDIT_DISTRIBUTION_STATUS_TOPIC_NAME(String.class, "DSTATUS_TOPIC"),
+ AUDIT_DISTRIBUTION_ROLE(String.class, "ROLE"),
+ AUDIT_DISTRIBUTION_ID(String.class, "DID"),
+ AUDIT_DISTRIBUTION_API_KEY(String.class, "API_KEY"),
+ AUDIT_DISTRIBUTION_CONSUMER_ID(String.class, "CONSUMER_ID"),
+ AUDIT_DISTRIBUTION_RESOURCE_URL(String.class, "RESOURCE_URL"),
+ AUDIT_DISTRIBUTION_STATUS_TIME(String.class, "STATUS_TIME"),
+ AUDIT_DISTRIBUTION_STATUS_DESC(String.class, "STATUS_DESC"),
+
+ // category
+ AUDIT_CATEGORY_NAME(String.class, "CATEGORY_NAME"),
+ AUDIT_SUB_CATEGORY_NAME(String.class, "SUB_CATEGORY_NAME"),
+ AUDIT_GROUPING_NAME(String.class, "GROUPING_NAME"),
+ AUDIT_DETAILS(String.class, "DETAILS"),
+
+ // authentication
+ AUDIT_AUTH_URL(String.class, "URL"),
+ AUDIT_AUTH_USER(String.class, "USER"),
+ AUDIT_AUTH_STATUS(String.class, "AUTH_STATUS"),
+ AUDIT_AUTH_REALM(String.class, "REALM"),
+ AUDIT_ECOMP_USER(String.class, "ECOMP_USER");
+
+ private Class<?> clazz;
+ private String displayName;
+
+ AuditingFieldsKeysEnum(Class<?> clazz, String displayName) {
+ this.clazz = clazz;
+ this.displayName = displayName;
+ }
+
+ public Class<?> getValueClass() {
+ return this.clazz;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/CapList.java b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/CapList.java
new file mode 100644
index 0000000000..d202715fae
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/CapList.java
@@ -0,0 +1,324 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.datastructure;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Thread Safe List with a cap for Max elements.<br>
+ * If an element would be inserted to the list and it is full, the oldest
+ * element will be taken out.
+ *
+ * @author mshitrit
+ *
+ * @param <T>
+ */
+public class CapList<T> implements List<T> {
+ private static final int DEFAULT_CAP = 1000;
+ private int cap;
+ private ReadWriteLock readWriteLock;
+ private List<T> innerList;
+
+ public CapList() {
+ this(DEFAULT_CAP);
+ }
+
+ public CapList(int cap) {
+ if (cap < 1) {
+ throw new RuntimeException("List Cap Must Be Positive");
+ }
+ this.cap = cap;
+ innerList = new ArrayList<>();
+ readWriteLock = new ReentrantReadWriteLock();
+ }
+
+ @Override
+ public boolean add(T e) {
+ try {
+ readWriteLock.writeLock().lock();
+ boolean result = innerList.add(e);
+ removeExtras();
+ return result;
+
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+
+ }
+
+ private void removeExtras() {
+ while (innerList.size() > cap) {
+ innerList.remove(0);
+ }
+ }
+
+ @Override
+ public void add(int index, T element) {
+ try {
+ readWriteLock.writeLock().lock();
+ innerList.add(index, element);
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ try {
+ readWriteLock.writeLock().lock();
+ boolean result = innerList.addAll(c);
+ removeExtras();
+ return result;
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends T> c) {
+ try {
+ readWriteLock.writeLock().lock();
+ boolean result = innerList.addAll(index, c);
+ removeExtras();
+ return result;
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public void clear() {
+ try {
+ readWriteLock.writeLock().lock();
+ innerList.clear();
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.contains(o);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.containsAll(c);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public T get(int index) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.get(index);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.indexOf(o);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.isEmpty();
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.iterator();
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.lastIndexOf(o);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public ListIterator<T> listIterator() {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.listIterator();
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public ListIterator<T> listIterator(int index) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.listIterator(index);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ try {
+ readWriteLock.writeLock().lock();
+ return innerList.remove(o);
+
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public T remove(int index) {
+ try {
+ readWriteLock.writeLock().lock();
+ return innerList.remove(index);
+
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ try {
+ readWriteLock.writeLock().lock();
+ return innerList.removeAll(c);
+
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ try {
+ readWriteLock.writeLock().lock();
+ return innerList.retainAll(c);
+
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public T set(int index, T element) {
+ try {
+ readWriteLock.writeLock().lock();
+ return innerList.set(index, element);
+
+ } finally {
+ readWriteLock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public int size() {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.size();
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public List<T> subList(int fromIndex, int toIndex) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.subList(fromIndex, toIndex);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public Object[] toArray() {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.toArray();
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ try {
+ readWriteLock.readLock().lock();
+ return innerList.toArray(a);
+
+ } finally {
+ readWriteLock.readLock().unlock();
+ }
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/ESTimeBasedEvent.java b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/ESTimeBasedEvent.java
new file mode 100644
index 0000000000..55b9f7f87f
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/ESTimeBasedEvent.java
@@ -0,0 +1,125 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.datastructure;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+/**
+ * Extending this class enforces the objects of implementing classes to have a
+ * timestamp, so that like in logstash, we can derive the index name for those
+ * object from the timestamp.
+ *
+ * @author paharoni
+ *
+ */
+public class ESTimeBasedEvent {
+
+ protected static String dateFormatPattern = "yyyy-MM-dd HH:mm:ss.SSS z";
+ protected String timestamp;
+ protected Map<String, Object> fields = new HashMap<String, Object>();
+
+ public ESTimeBasedEvent() {
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormatPattern);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.timestamp = simpleDateFormat.format(new Date());
+ fields.put(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName(), this.timestamp);
+
+ }
+
+ public static ESTimeBasedEvent createEventFromJson(String jsonString) throws JSONException {
+
+ ESTimeBasedEvent event = new ESTimeBasedEvent();
+ JSONObject gsonObj;
+ gsonObj = new JSONObject(jsonString);
+ Iterator keys = gsonObj.keys();
+
+ while (keys.hasNext()) {
+ String key = (String) keys.next();
+ event.fields.put(key, gsonObj.get(key));
+ if (key.equals(AuditingFieldsKeysEnum.AUDIT_TIMESTAMP.getDisplayName())) {
+ event.timestamp = (String) gsonObj.get(key);
+ }
+ }
+ return event;
+ }
+
+ public String calculateYearIndexSuffix() {
+ return timestamp.substring(0, 4);
+ }
+
+ public String calculateMonthIndexSuffix() {
+ return timestamp.substring(0, 7);
+ }
+
+ public String calculateDayIndexSuffix() {
+ return timestamp.substring(0, 10);
+ }
+
+ public String calculateHourIndexSuffix() {
+ return new StringBuilder().append(timestamp.substring(0, 10)).append("-").append(timestamp.substring(11, 13))
+ .toString();
+ }
+
+ public String calculateMinuteIndexSuffix() {
+ return new StringBuilder().append(timestamp.substring(0, 10)).append("-").append(timestamp.substring(11, 13))
+ .append("-").append(timestamp.substring(14, 16)).toString();
+ }
+
+ protected String getFormattedString(String template, Object... params) {
+ String res = null;
+ StringBuilder sb = new StringBuilder();
+ Formatter formatter = new Formatter(sb, Locale.US);
+ try {
+ formatter.format(template, params);
+ res = formatter.toString();
+ } finally {
+ formatter.close();
+ }
+ return res;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public Map<String, Object> getFields() {
+ return fields;
+ }
+
+ public void setFields(Map<String, Object> fields) {
+ this.fields = fields;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/FunctionalInterfaces.java b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/FunctionalInterfaces.java
new file mode 100644
index 0000000000..12ba17baa5
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/FunctionalInterfaces.java
@@ -0,0 +1,282 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.datastructure;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Class For Functional interfaces And Functional Methods
+ *
+ * @author mshitrit
+ *
+ */
+public class FunctionalInterfaces {
+ private static final int DEFAULT_REDO_INTERVAL_TIME_MS = 50;
+ private static final int DEFAULT_MAX_WAIT_TIME_MS = 10000;
+
+ /**
+ * @author mshitrit Consumer that takes two parameters
+ */
+ public interface ConsumerTwoParam<T1, T2> {
+ /**
+ * Same Accept method, but takes two parameters
+ *
+ * @param t1
+ * @param t2
+ */
+ void accept(T1 t1, T2 t2);
+ }
+
+ /**
+ * @author mshitrit Function that takes two parameters
+ */
+ public interface FunctionTwoParam<T1, T2, R> {
+ /**
+ * Same apply method, but takes two parameters
+ *
+ * @param t1
+ * @param t2
+ * @return
+ */
+ R apply(T1 t1, T2 t2);
+ }
+
+ /**
+ * @author mshitrit Function that throws an exception
+ */
+ public interface FunctionThrows<T, R, E extends Exception> {
+ /**
+ * Same apply method, but throws an exception
+ *
+ * @param t
+ * @return
+ */
+ R apply(T t) throws E;
+ }
+
+ /**
+ * @author mshitrit Supplier that throws an exception
+ */
+ public interface SupplierThrows<R, E extends Exception> {
+ /**
+ * Same get method, but throws an exception
+ *
+ * @return
+ * @throws E
+ */
+ R get() throws E;
+ }
+
+ /**
+ * @author mshitrit Consumer that throws an exception
+ */
+ public interface ConsumerThrows<T, E extends Exception> {
+ /**
+ * Same accept, but throws an exception
+ *
+ * @param t
+ * @throws E
+ */
+ void accept(T t) throws E;
+ }
+
+ /**
+ * @author mshitrit Runnable that throws an exception
+ */
+ public interface RunnableThrows<E extends Exception> {
+ /**
+ * Same run, but throws an exception
+ *
+ * @throws E
+ */
+ void run() throws E;
+ }
+
+ /**
+ * Runs a method that declares throwing an Exception and has a return value.
+ * <br>
+ * In case Exception Occurred replaces it with FunctionalAttException. <br>
+ * This is useful for two cases:<br>
+ * 1.using methods that throws exceptions in streams.<br>
+ * 2.replacing declared exception with undeclared exception (Runtime).<br>
+ * See below Use Case:<br>
+ * Instead of: intList.stream().map(e -> fooThrowsAndReturnsBoolean(e)); -
+ * does not compile !<br>
+ * Use This : intList.stream().map(e -> swallowException( () ->
+ * fooThrowsAndReturnsBoolean(e))); - compiles !<br>
+ *
+ * @param methodToRun
+ * @return
+ */
+ public static <R, E extends Exception> R swallowException(SupplierThrows<R, E> methodToRun) {
+ try {
+ final R result = methodToRun.get();
+ return result;
+ } catch (Exception e) {
+ throw new FunctionalAttException(e);
+ }
+ }
+
+ /**
+ * Runs a method that declares throwing an Exception without return value.
+ * <br>
+ * In case Exception Occurred replaces it with FunctionalAttException. <br>
+ * This is useful for two cases:<br>
+ * 1.using methods that throws exceptions in streams.<br>
+ * 2.replacing declared exception with undeclared exception (Runtime).<br>
+ * See below Use Case:<br>
+ *
+ * @param methodToRun
+ */
+ public static <E extends Exception> void swallowException(RunnableThrows<E> methodToRun) {
+
+ SupplierThrows<Boolean, E> runnableWrapper = () -> {
+ methodToRun.run();
+ return true;
+ };
+ swallowException(runnableWrapper);
+ }
+
+ private static class FunctionalAttException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private FunctionalAttException(Exception e) {
+ super(e);
+ }
+ }
+
+ /**
+ * Runs the given method.<br>
+ * Verify the method result against the resultVerifier.<br>
+ * If verification passed returns the result.<br>
+ * If Verification failed keeps retrying until it passes or until 10 seconds
+ * pass.<br>
+ * If Exception Occurred keeps retrying until it passes or until 10 seconds
+ * pass,<br>
+ * If last retry result caused an exception - it is thrown.
+ *
+ * @param methodToRun
+ * given Method
+ * @param resultVerifier
+ * verifier for the method result
+ * @return
+ */
+ public static <R> R retryMethodOnResult(Supplier<R> methodToRun, Function<R, Boolean> resultVerifier) {
+ return retryMethodOnResult(methodToRun, resultVerifier, DEFAULT_MAX_WAIT_TIME_MS,
+ DEFAULT_REDO_INTERVAL_TIME_MS);
+ }
+
+ /**
+ * Runs the given method.<br>
+ * Verify the method result against the resultVerifier.<br>
+ * If verification passed returns the result.<br>
+ * If Verification failed keeps retrying until it passes or until maxWait
+ * pass.<br>
+ * If Exception Occurred keeps retrying until it passes or until maxWait
+ * pass,<br>
+ * If last retry result caused an exception - it is thrown.
+ *
+ * @param methodToRun
+ * given Method
+ * @param resultVerifier
+ * verifier for the method result
+ * @param maxWaitMS
+ * @param retryIntervalMS
+ * @return
+ */
+ public static <R> R retryMethodOnResult(Supplier<R> methodToRun, Function<R, Boolean> resultVerifier,
+ long maxWaitMS, long retryIntervalMS) {
+ boolean stopSearch = false;
+ R ret = null;
+ int timeElapsed = 0;
+ FunctionalAttException functionalExceotion = null;
+ boolean isExceptionInLastTry = false;
+ while (!stopSearch) {
+ try {
+ ret = methodToRun.get();
+ stopSearch = resultVerifier.apply(ret);
+ isExceptionInLastTry = false;
+ } catch (Exception e) {
+ functionalExceotion = new FunctionalAttException(e);
+ isExceptionInLastTry = true;
+
+ } finally {
+ sleep(retryIntervalMS);
+ timeElapsed += retryIntervalMS;
+ if (timeElapsed > maxWaitMS) {
+ stopSearch = true;
+ }
+ }
+
+ }
+ if (isExceptionInLastTry) {
+ throw functionalExceotion;
+ } else {
+ return ret;
+ }
+
+ }
+
+ /**
+ * Runs the given method.<br>
+ * In case exception occurred runs the method again either until succeed or
+ * until 10 seconds pass.
+ *
+ * @param methodToRun
+ * given method
+ * @return
+ */
+
+ public static <R> R retryMethodOnException(Supplier<R> methodToRun) {
+ Function<R, Boolean> dummyVerifier = someResult -> true;
+ return retryMethodOnResult(methodToRun, dummyVerifier, DEFAULT_MAX_WAIT_TIME_MS, DEFAULT_REDO_INTERVAL_TIME_MS);
+ }
+
+ /**
+ * Runs the given method.<br>
+ * In case exception occurred runs the method again either until succeed or
+ * until 10 seconds pass.
+ *
+ * @param methodToRun
+ * given method
+ */
+ public static void retryMethodOnException(Runnable methodToRun) {
+ Function<Boolean, Boolean> dummyVerifier = someResult -> true;
+ Supplier<Boolean> dummySupplier = () -> {
+ methodToRun.run();
+ return true;
+ };
+ retryMethodOnResult(dummySupplier, dummyVerifier, DEFAULT_MAX_WAIT_TIME_MS, DEFAULT_REDO_INTERVAL_TIME_MS);
+ }
+
+ /**
+ * Same as Thread.sleep but throws a FunctionalAttException
+ * (RuntimeException) instead of InterruptedException.<br>
+ *
+ * @param millis
+ */
+ public static void sleep(long millis) {
+ swallowException(() -> Thread.sleep(millis));
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/MonitoringFieldsKeysEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/MonitoringFieldsKeysEnum.java
new file mode 100644
index 0000000000..f92b4ef687
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/MonitoringFieldsKeysEnum.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.datastructure;
+
+import java.util.Date;
+
+public enum MonitoringFieldsKeysEnum {
+ MONITORING_TIMESTAMP(Date.class, "TIMESTAMP"),
+ MONITORING_HOST_IP(String.class, "HOST_IP"),
+ MONITORING_HOST_CPU(Long.class, "HOST_CPU"),
+ MONITORING_HOST_MEM(Long.class, "HOST_MEMORY"),
+ MONITORING_HOST_DISC(Long.class, "HOST_DISC"),
+ MONITORING_JVM_ID(String.class, "JVM_ID"),
+ MONITORING_JVM_CPU(Long.class, "JVM_CPU"),
+ MONITORING_JVM_MEM(Long.class, "JVM_MEMORY"),
+ MONITORING_JVM_TNUM(Long.class, "JVM_TNUM"),
+ MONITORING_APP_ID(String.class, "APP_ID"),
+ MONITORING_APP_STAT(String.class, "APP_STAT");
+
+ private Class<?> clazz;
+ private String displayName;
+
+ MonitoringFieldsKeysEnum(Class<?> clazz, String displayName) {
+ this.clazz = clazz;
+ this.displayName = displayName;
+ }
+
+ public Class<?> getValueClass() {
+ return this.clazz;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/Wrapper.java b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/Wrapper.java
new file mode 100644
index 0000000000..43fd5ca15f
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/datastructure/Wrapper.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.datastructure;
+
+/**
+ * Very Basic Wrapper class.
+ *
+ * @author mshitrit
+ *
+ * @param <T>
+ */
+public class Wrapper<T> {
+ private T innerElement;
+
+ public Wrapper(T innerElement) {
+ this.innerElement = innerElement;
+ }
+
+ public Wrapper() {
+ this.innerElement = null;
+ }
+
+ public T getInnerElement() {
+ return innerElement;
+ }
+
+ public void setInnerElement(T innerElement) {
+ this.innerElement = innerElement;
+ }
+
+ public boolean isEmpty() {
+ return innerElement == null;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/impl/ConfigFileChangeListener.java b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/ConfigFileChangeListener.java
new file mode 100644
index 0000000000..9b5debb53a
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/ConfigFileChangeListener.java
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.jci.listeners.FileChangeListener;
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConfigFileChangeListener extends FileChangeListener {
+
+ private static Logger log = LoggerFactory.getLogger(ConfigFileChangeListener.class.getName());
+
+ private Map<String, List<ConfigurationListener>> fileChangeToCallBack = new HashMap<String, List<ConfigurationListener>>();
+
+ private Object lock = new Object();
+
+ private YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+
+ @Override
+ public void onFileChange(File pFile) {
+
+ super.onFileChange(pFile);
+
+ if (pFile != null) {
+
+ if (fileChangeToCallBack != null) {
+
+ String id = findIdFromFileName(pFile.getName());
+
+ if (id != null) {
+
+ List<ConfigurationListener> listeners = fileChangeToCallBack.get(id);
+ if (listeners != null) {
+ for (ConfigurationListener configurationListener : listeners) {
+
+ Class<? extends BasicConfiguration> configClass = configurationListener.getType();
+
+ BasicConfiguration basicConfiguration = yamlToObjectConverter
+ .convert(pFile.getAbsolutePath(), configClass);
+
+ if (basicConfiguration == null) {
+ log.warn(
+ "Cannot update the listeners for file Change since the file content is invalid");
+ continue;
+ }
+ log.debug("Loaded configuration after converting is " + basicConfiguration);
+ // System.out.println("New configuration is " +
+ // basicConfiguration);
+
+ configurationListener.getCallBack().reconfigure(basicConfiguration);
+
+ }
+ }
+ } else {
+
+ log.warn("Cannot calculate id from file " + pFile.getName());
+ }
+ }
+
+ }
+
+ log.debug("File {} was changed.", pFile);
+ }
+
+ private String findIdFromFileName(String name) {
+
+ String result = null;
+ if (name != null) {
+ int startIndex = 0;
+ int endIndex = name.length();
+ if (name.contains(File.separator)) {
+ startIndex = name.lastIndexOf(File.separator);
+ }
+ // String subNameString = name.substring(startIndex, endIndex);
+ // if (subNameString.contains(".")) {
+ // endIndex = subNameString.indexOf(".");
+ // }
+
+ result = name.substring(startIndex, endIndex);
+
+ }
+
+ return result;
+ }
+
+ public void register(String id, ConfigurationListener configurationListener) {
+
+ if (configurationListener != null) {
+
+ synchronized (lock) {
+
+ List<ConfigurationListener> callbacks = fileChangeToCallBack.get(id);
+ if (callbacks == null) {
+ callbacks = new ArrayList<ConfigurationListener>();
+ fileChangeToCallBack.put(id, callbacks);
+ }
+ callbacks.add(configurationListener);
+
+ }
+
+ }
+
+ }
+
+ // public void notify(String id, BasicConfiguration object) {
+ //
+ // if (fileChangeToCallBack != null) {
+ // List<ConfigurationListener> listeners = fileChangeToCallBack
+ // .get(id);
+ // if (listeners != null) {
+ // for (ConfigurationListener configurationListener : listeners) {
+ //
+ // configurationListener.getCallBack().reconfigure(object);
+ //
+ // }
+ // }
+ // }
+ //
+ // }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/impl/ExternalConfiguration.java b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/ExternalConfiguration.java
new file mode 100644
index 0000000000..b23dcb8812
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/ExternalConfiguration.java
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.impl;
+
+import java.io.File;
+
+import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+
+/**
+ *
+ * Save the
+ *
+ * @author esofer
+ *
+ */
+public class ExternalConfiguration {
+
+ private static String appName;
+ private static String appVersion;
+ private static String configDir;
+ private static ConfigurationSource configurationSource;
+
+ private static FilesystemAlterationMonitor fam = null;
+
+ private static ConfigFileChangeListener changeListener = new ConfigFileChangeListener();
+
+ private static boolean enableReconfigure = true;
+
+ public static String getAppName() {
+ return appName;
+ }
+
+ public static void setAppName(String appName) {
+ ExternalConfiguration.appName = appName;
+ }
+
+ public static String getAppVersion() {
+ return appVersion;
+ }
+
+ public static void setAppVersion(String appVersion) {
+ ExternalConfiguration.appVersion = appVersion;
+ }
+
+ public static String getConfigDir() {
+ return configDir;
+ }
+
+ public static void setConfigDir(String configDir) {
+ ExternalConfiguration.configDir = configDir;
+ }
+
+ public static ConfigurationSource getConfigurationSource() {
+ return configurationSource;
+ }
+
+ public static void setConfigurationSource(ConfigurationSource configurationSource) {
+ ExternalConfiguration.configurationSource = configurationSource;
+ }
+
+ public static ConfigFileChangeListener getChangeListener() {
+ return changeListener;
+ }
+
+ public static void listenForChanges() {
+
+ String watchingDir = configDir + File.separator + appName;
+ if (enableReconfigure) {
+ if (fam == null) {
+ fam = new FilesystemAlterationMonitor();
+ fam.setInterval(1000);
+ fam.addListener(new File(watchingDir), changeListener);
+ fam.start();
+ }
+ }
+ }
+
+ public static void stopListenForFileChanges() {
+ if (enableReconfigure) {
+ if (fam != null) {
+ fam.stop();
+ fam = null;
+ }
+ }
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/impl/FSConfigurationSource.java b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/FSConfigurationSource.java
new file mode 100644
index 0000000000..76f64ca278
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/FSConfigurationSource.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.impl;
+
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+
+/**
+ * Read configuration from file system
+ *
+ * @author esofer
+ *
+ */
+public class FSConfigurationSource implements ConfigurationSource {
+
+ private YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+
+ private ConfigFileChangeListener changeListener = null;
+ private String appConfigDir = null;
+
+ public FSConfigurationSource(ConfigFileChangeListener changeListener, String appConfigDir) {
+ super();
+ this.changeListener = changeListener;
+ this.appConfigDir = appConfigDir;
+ }
+
+ /*
+ * get and watch configuration changes. The file name we looking for is the
+ * lower case of the class name separated by "-".
+ *
+ * (non-Javadoc)
+ *
+ * @see
+ * org.openecomp.sdc.common.api.ConfigurationSource#getAndWatchConfiguration
+ * (java.lang.Class, org.openecomp.sdc.common.api.ConfigurationListener)
+ */
+ public <T> T getAndWatchConfiguration(Class<T> className, ConfigurationListener configurationListener) {
+
+ String configFileName = calculateFileName(className);
+
+ T object = yamlToObjectConverter.convert(this.appConfigDir, className, configFileName);
+
+ if (configurationListener != null && changeListener != null) {
+ if (object != null) {
+ changeListener.register(configFileName, configurationListener);
+ }
+ }
+
+ return object;
+ }
+
+ public <T> void addWatchConfiguration(Class<T> className, ConfigurationListener configurationListener) {
+
+ String configFileName = calculateFileName(className);
+
+ if (configurationListener != null) {
+ changeListener.register(configFileName, configurationListener);
+ }
+
+ }
+
+ /**
+ * convert camel case string to list of words separated by "-" where each
+ * word is in lower case format. For example, MyClass will be calculated to
+ * be my-class.yaml .
+ *
+ * @param className
+ * @return file name based on the class name
+ */
+ private static <T> String calculateFileName(Class<T> className) {
+
+ String[] words = className.getSimpleName().split("(?=\\p{Upper})");
+
+ StringBuilder builder = new StringBuilder();
+
+ // There cannot be a null value returned from "split" - words != null is
+ // redundant
+ // if (words != null) {
+ boolean isFirst = true;
+ for (int i = 0; i < words.length; i++) {
+
+ String word = words[i];
+ if (word != null && !word.isEmpty()) {
+ if (!isFirst) {
+ builder.append("-");
+ } else {
+ isFirst = false;
+ }
+ builder.append(words[i].toLowerCase());
+ }
+ }
+ return builder.toString() + Constants.YAML_SUFFIX;
+
+ /*
+ * } else { return className.getSimpleName().toLowerCase() +
+ * Constants.YAML_SUFFIX; }
+ */
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/impl/MutableHttpServletRequest.java b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/MutableHttpServletRequest.java
new file mode 100644
index 0000000000..c7ce7a8c32
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/impl/MutableHttpServletRequest.java
@@ -0,0 +1,73 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.impl;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
+ // holds custom header and value mapping
+ private final Map<String, String> customHeaders;
+
+ public MutableHttpServletRequest(HttpServletRequest request) {
+ super(request);
+ this.customHeaders = new HashMap<String, String>();
+ }
+
+ public void putHeader(String name, String value) {
+ this.customHeaders.put(name, value);
+ }
+
+ public String getHeader(String name) {
+ // check the custom headers first
+ String headerValue = customHeaders.get(name);
+
+ if (headerValue != null) {
+ return headerValue;
+ }
+ // else return from into the original wrapped object
+ return ((HttpServletRequest) getRequest()).getHeader(name);
+ }
+
+ public Enumeration<String> getHeaderNames() {
+ // create a set of the custom header names
+ Set<String> set = new HashSet<String>(customHeaders.keySet());
+
+ // now add the headers from the wrapped request object
+ @SuppressWarnings("unchecked")
+ Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
+ while (e.hasMoreElements()) {
+ // add the names of the request headers into the list
+ String n = e.nextElement();
+ set.add(n);
+ }
+
+ // create an enumeration from the set and return
+ return Collections.enumeration(set);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/kpi/api/ASDCKpiApi.java b/common-app-api/src/main/java/org/openecomp/sdc/common/kpi/api/ASDCKpiApi.java
new file mode 100644
index 0000000000..5644ef5c41
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/kpi/api/ASDCKpiApi.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.kpi.api;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ASDCKpiApi {
+
+ private static Logger log = LoggerFactory.getLogger(ASDCKpiApi.class.getName());
+
+ /* Number of activated resource imports. */
+ public static void countImportResourcesKPI() {
+ // TODO Auto-generated method stub
+ log.trace("Number of activated resource imports.");
+
+ }
+
+ /* Number of created resources. */
+ public static void countCreatedResourcesKPI() {
+ // TODO Auto-generated method stub
+ log.trace("Number of created resources.");
+
+ }
+
+ /* Number of created services */
+ public static void countCreatedServicesKPI() {
+ // TODO Auto-generated method stub
+ log.trace("Number of created services.");
+
+ }
+
+ /*
+ * Number of ASDC portal accesses ( number of activated user authorizations)
+ */
+ public static void countUsersAuthorizations() {
+ // TODO Auto-generated method stub
+ log.trace("Number of activated distribution");
+
+ }
+
+ /* Number of activated distribution */
+ public static void countActivatedDistribution() {
+ // TODO Auto-generated method stub
+ log.trace("Number of activated distribution");
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/listener/AppContextListener.java b/common-app-api/src/main/java/org/openecomp/sdc/common/listener/AppContextListener.java
new file mode 100644
index 0000000000..3f588e8aa2
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/listener/AppContextListener.java
@@ -0,0 +1,129 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.listener;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+
+public class AppContextListener implements ServletContextListener {
+
+ private static Logger log = LoggerFactory.getLogger(AppContextListener.class.getName());
+
+ public void contextInitialized(ServletContextEvent context) {
+
+ log.debug("ServletContextListener initialized ");
+
+ log.debug("After read values from Manifest {}", getManifestInfo(context.getServletContext()));
+
+ Map<String, String> manifestAttr = getManifestInfo(context.getServletContext());
+
+ String appName = setAndGetAttributeInContext(context, manifestAttr, Constants.APPLICATION_NAME);
+ String appVersion = setAndGetAttributeInContext(context, manifestAttr, Constants.APPLICATION_VERSION);
+
+ ExternalConfiguration.setAppName(appName);
+ ExternalConfiguration.setAppVersion(appVersion);
+ String configHome = System.getProperty(Constants.CONFIG_HOME);
+ ExternalConfiguration.setConfigDir(configHome);
+
+ String appConfigDir = configHome + File.separator + appName;
+ // ChangeListener changeListener = new ChangeListener();
+ ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir);
+
+ context.getServletContext().setAttribute(Constants.CONFIGURATION_SOURCE_ATTR, configurationSource);
+
+ ExternalConfiguration.setConfigurationSource(configurationSource);
+
+ ExternalConfiguration.listenForChanges();
+
+ }
+
+ public void contextDestroyed(ServletContextEvent context) {
+
+ log.debug("ServletContextListener destroyed");
+ ExternalConfiguration.stopListenForFileChanges();
+ }
+
+ private String setAndGetAttributeInContext(ServletContextEvent context, Map<String, String> manifestAttr, String attr) {
+
+ String name = manifestAttr.get(attr);
+ if (name != null) {
+ context.getServletContext().setAttribute(attr, name);
+ }
+
+ return name;
+ }
+
+ public static Map<String, String> getManifestInfo(ServletContext application) {
+
+ Map<String, String> result = new HashMap<String, String>();
+ InputStream inputStream = null;
+ try {
+
+ inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF");
+
+ Manifest manifest = new Manifest(inputStream);
+
+ Attributes attr = manifest.getMainAttributes();
+ String name = attr.getValue("Implementation-Title");
+ if (name != null) {
+ result.put(Constants.APPLICATION_NAME, name);
+ }
+
+ String version = attr.getValue("Implementation-Version");
+ if (version != null) {
+ result.put(Constants.APPLICATION_VERSION, version);
+ }
+
+ } catch (IOException e) {
+
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return result;
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringEvent.java b/common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringEvent.java
new file mode 100644
index 0000000000..61651f3ddc
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringEvent.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.monitoring;
+
+import org.openecomp.sdc.common.datastructure.ESTimeBasedEvent;
+
+public class MonitoringEvent extends ESTimeBasedEvent {
+
+ private static String MONITORING_EVENT_TEMPLATE = "hostid=\"%s\" hostcpu=\"%s\" hostmem=\"%s\" hostdisk=\"%s\" "
+ + "jvmid=\"%s\" jvmcpu=\"%s\" jvmmem=\"%s\" jvmtnum=\"%s\" " + "appid=\"%s\" appstat=\"%s\"";
+
+ private String hostid;
+ private Long hostcpu;
+ private Double hostmem;
+ private String hostdisk;
+ private String jvmid;
+ private Long jvmcpu;
+ private Long jvmmem;
+ private Integer jvmtnum;
+ private String appid;
+ private String appstat;
+
+ public String getHostid() {
+ return hostid;
+ }
+
+ public void setHostid(String hostid) {
+ this.hostid = hostid;
+ }
+
+ public Long getHostcpu() {
+ return hostcpu;
+ }
+
+ public void setHostcpu(Long hostcpu) {
+ this.hostcpu = hostcpu;
+ }
+
+ public Double getHostmem() {
+ return hostmem;
+ }
+
+ public void setHostmem(Double hostmem) {
+ this.hostmem = hostmem;
+ }
+
+ public String getHostdisk() {
+ return hostdisk;
+ }
+
+ public void setHostdisk(String hostdisk) {
+ this.hostdisk = hostdisk;
+ }
+
+ public String getJvmid() {
+ return jvmid;
+ }
+
+ public void setJvmid(String jvmid) {
+ this.jvmid = jvmid;
+ }
+
+ public Long getJvmcpu() {
+ return jvmcpu;
+ }
+
+ public void setJvmcpu(Long jvmcpu) {
+ this.jvmcpu = jvmcpu;
+ }
+
+ public Long getJvmmem() {
+ return jvmmem;
+ }
+
+ public void setJvmmem(Long jvmmem) {
+ this.jvmmem = jvmmem;
+ }
+
+ public Integer getJvmtnum() {
+ return jvmtnum;
+ }
+
+ public void setJvmtnum(Integer jvmtnum) {
+ this.jvmtnum = jvmtnum;
+ }
+
+ public String getAppid() {
+ return appid;
+ }
+
+ public void setAppid(String appid) {
+ this.appid = appid;
+ }
+
+ public String getAppstat() {
+ return appstat;
+ }
+
+ public void setAppstat(String appstat) {
+ this.appstat = appstat;
+ }
+
+ @Override
+ public String toString() {
+ return getFormattedString(MONITORING_EVENT_TEMPLATE, hostid, hostcpu, hostmem, hostdisk, jvmid, jvmcpu, jvmmem,
+ jvmtnum, appid, appstat);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringMetricsFetcher.java b/common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringMetricsFetcher.java
new file mode 100644
index 0000000000..9df1aea983
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/monitoring/MonitoringMetricsFetcher.java
@@ -0,0 +1,212 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.monitoring;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.lang.management.ThreadMXBean;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.hyperic.sigar.FileSystem;
+import org.hyperic.sigar.Sigar;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MonitoringMetricsFetcher {
+
+ private static Logger monitoringLogger = LoggerFactory.getLogger("asdc.fe.monitoring.fetcher");
+
+ private static volatile MonitoringMetricsFetcher instance;
+ private static RuntimeMXBean runtimeMXBean;
+ private static ThreadMXBean threadMXBean;
+ private static MemoryMXBean memoryMXBean;
+ private static MBeanServer platformMBeanServer;
+ private static Sigar sigarSession;
+
+ private static String appName;
+ private static String jvmName = "Unknown";
+ private final String PROCESS_CPU_TIME_ATTR = "ProcessCpuTime";
+
+ private static String FE_JVM_NAME = "jetty-fe";
+ private static String BE_JVM_NAME = "jetty-be";
+
+ private MonitoringMetricsFetcher() {
+ };
+
+ public static MonitoringMetricsFetcher getInstance() {
+ if (instance == null) {
+ instance = init();
+ }
+ return instance;
+ }
+
+ public MonitoringEvent getMonitoringMetrics() {
+ MonitoringEvent monitoringEvent = new MonitoringEvent();
+ monitoringEvent.setHostid(getFQDN());
+ monitoringEvent.setHostcpu(getHostCpuTime());
+ monitoringEvent.setHostmem(getHostUsedMemory());
+ monitoringEvent.setHostdisk(getHostUsedDisk().toString());
+
+ monitoringEvent.setJvmid(jvmName);
+
+ monitoringEvent.setJvmcpu(getJvmCpuTime());
+ monitoringEvent.setJvmmem(getJvmUsedHeapMemory());
+ monitoringEvent.setJvmtnum(getJvmThreads());
+
+ monitoringEvent.setAppid(appName);
+ // this is probably from healthcheck
+ // TODO
+ monitoringEvent.setAppstat("appStatus");
+ return monitoringEvent;
+ }
+
+ private static synchronized MonitoringMetricsFetcher init() {
+ if (instance == null) {
+ instance = new MonitoringMetricsFetcher();
+ threadMXBean = ManagementFactory.getThreadMXBean();
+ memoryMXBean = ManagementFactory.getMemoryMXBean();
+ runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+ platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+ sigarSession = new Sigar();
+ appName = ExternalConfiguration.getAppName();
+ monitoringLogger.debug("appName is {}", appName);
+ // Accoridng to Yaki, there is no "calculated" jvmName like it was
+ // in TAS,
+ // just "jetty-be" or "jetty-fe"
+ if (appName.contains("fe")) {
+ jvmName = FE_JVM_NAME;
+ } else if (appName.contains("be")) {
+ jvmName = BE_JVM_NAME;
+ } else {
+ monitoringLogger
+ .warn("Couldn't determine jvmName, appName is expected to contain \"be\" or \"fe\" string");
+ }
+ }
+ return instance;
+ }
+
+ /**
+ * Returns the number of live threads for this JVM
+ *
+ * @return number of live threads
+ */
+ private Integer getJvmThreads() {
+ return threadMXBean.getThreadCount();
+ }
+
+ /**
+ * Returns the number of used heap memory (bytes)
+ *
+ * @return the number of used heap memory (bytes)
+ */
+ private long getJvmUsedHeapMemory() {
+ return memoryMXBean.getHeapMemoryUsage().getUsed();
+ }
+
+ /**
+ * Returns the jvm cpu time (msec)
+ *
+ * @return the jvm cpu time (msec)
+ */
+ private long getJvmCpuTime() {
+
+ long cpuTime = -1;
+ try {
+ cpuTime = (long) platformMBeanServer.getAttribute(
+ new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME), PROCESS_CPU_TIME_ATTR);
+ } catch (Exception e) {
+ monitoringLogger.error("Couldn't measure JVM CPU time, error: {}", e);
+ }
+ return cpuTime;
+ }
+
+ /**
+ * Returns the host total cpu time (msec)
+ *
+ * @return the host total cpu time (msec)
+ */
+ private long getHostCpuTime() {
+ long cpuTime = -1;
+ try {
+ cpuTime = sigarSession.getCpu().getTotal();
+ } catch (Exception e) {
+ monitoringLogger.error("Couldn't measure host CPU time, error: {}", e);
+ }
+ return cpuTime;
+ }
+
+ /**
+ * Returns the host used memory(msec)
+ *
+ * @return the host used memory(msec)
+ */
+ private Double getHostUsedMemory() {
+ Double memory = -1.0;
+ try {
+ memory = sigarSession.getMem().getUsedPercent();
+ } catch (Exception e) {
+ monitoringLogger.error("Couldn't measure host used memory, error: {}", e);
+ }
+ return memory;
+ }
+
+ /**
+ * Returns the percentage of all available FS
+ *
+ * @return the host avail disk(bytes)
+ */
+ private Map<String, Double> getHostUsedDisk() {
+ Map<String, Double> res = new HashMap<>();
+ try {
+ FileSystem[] fileSystemList = sigarSession.getFileSystemList();
+ for (FileSystem fileSystem : fileSystemList) {
+
+ String dirName = fileSystem.getDirName();
+ double usePercent = sigarSession.getFileSystemUsage(dirName).getUsePercent() * 100;
+ res.put(dirName, usePercent);
+ }
+ } catch (Exception e) {
+ monitoringLogger.error("Couldn't measure host used disk, error: {}", e);
+ }
+ return res;
+ }
+
+ /**
+ * Returns the FQDN
+ *
+ * @return the FQDN
+ */
+ private String getFQDN() {
+ String fqdn = "";
+ try {
+ fqdn = sigarSession.getFQDN();
+ } catch (Exception e) {
+ monitoringLogger.error("Couldn't get FQDN, error: {}", e);
+ }
+ return fqdn;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/IRestClient.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/IRestClient.java
new file mode 100644
index 0000000000..8deb3d7d64
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/IRestClient.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.api;
+
+import java.util.Properties;
+
+/**
+ * This interface describe the methods of the REST generic client. Each method
+ * will receive the destination URI and specific list of headers. With this
+ * information the REST Client will create a request to the specific REST Web
+ * server. Error from the REST Web server will be return by proprietary
+ * exception object.
+ *
+ * @author esofer
+ *
+ */
+public interface IRestClient {
+
+ /**
+ * This method will return resource according to the given URI.
+ *
+ * @param uri
+ * Full URL path to the desire resource.
+ * @param headers
+ * - list of headers in format of name and value, to be add as
+ * part of the HTTP request.
+ * @return JSON representation of the requested resource.
+ */
+ public RestResponse doGET(String uri, Properties headers);
+
+ /**
+ * This method will CREATE resource according to the given URI.
+ *
+ * @param uri
+ * Full URL path to the desire resource.
+ * @param headers
+ * - list of headers in format of name and value, to be add as
+ * part of the HTTP request.
+ * @param objectToCreate
+ * - JSON representation of the resource.
+ */
+ public RestResponse doPOST(String uri, Properties headers, Object objectToCreate);
+
+ /**
+ * This method will UPDATE resource according to the given URI.
+ *
+ * @param uri
+ * Full URL path to the desire resource.
+ * @param headers
+ * - list of headers in format of name and value, to be add as
+ * part of the HTTP request.
+ * @param objectToUpdate
+ * - JSON representation of the resource.
+ */
+ public RestResponse doPUT(String uri, Properties headers, Object objectToUpdate);
+
+ /**
+ * This method will return resource according to the given URI.
+ *
+ * @param uri
+ * Full URL path to the desire resource.
+ * @param headers
+ * - list of headers in format of name and value, to be add as
+ * part of the HTTP request.
+ *
+ */
+ public RestResponse doDELETE(String uri, Properties headers);
+
+ /**
+ * initialize the rest client instance. The timeout is infinite.
+ */
+ public boolean init() throws Exception;
+
+ /**
+ * initialize the rest client instance with a given timeout in milliseconds.
+ *
+ * @param restConfigurationInfo
+ */
+ public boolean init(RestConfigurationInfo restConfigurationInfo);
+
+ /**
+ * destroy the connections
+ */
+ public void destroy();
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestClientServiceExeption.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestClientServiceExeption.java
new file mode 100644
index 0000000000..b64b651913
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestClientServiceExeption.java
@@ -0,0 +1,78 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.api;
+
+public class RestClientServiceExeption extends Exception {
+
+ private static final long serialVersionUID = 8258477052369440242L;
+
+ /**
+ * Default Constructor.
+ */
+ public RestClientServiceExeption() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param msg
+ * String
+ */
+ public RestClientServiceExeption(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cause
+ * Throwable
+ */
+ public RestClientServiceExeption(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param msg
+ * String
+ * @param cause
+ * Throwable
+ */
+ public RestClientServiceExeption(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ /**
+ *
+ * @param response
+ *
+ * public RSClientServiceExeption(ClientResponse response) {
+ * super(); this.response = response; }
+ *
+ * public ClientResponse getResponse() { return response; }
+ *
+ * public void setResponse(ClientResponse response) {
+ * this.response = response; }
+ */
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestConfigurationInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestConfigurationInfo.java
new file mode 100644
index 0000000000..89a9e24dff
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestConfigurationInfo.java
@@ -0,0 +1,107 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.api;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+public class RestConfigurationInfo extends BasicConfiguration {
+
+ private Integer readTimeoutInSec = null;
+
+ private Boolean ignoreCertificate = null;
+
+ private Integer connectionPoolSize = null;
+
+ private Integer connectTimeoutInSec = null;
+
+ /**
+ * @return the readTimeoutInSec
+ */
+ public Integer getReadTimeoutInSec() {
+ return readTimeoutInSec;
+ }
+
+ /**
+ * @param readTimeoutInSec
+ * the readTimeoutInSec to set
+ */
+ public void setReadTimeoutInSec(Integer readTimeoutInSec) {
+ this.readTimeoutInSec = readTimeoutInSec;
+ }
+
+ /**
+ * @return the ignoreCertificate
+ */
+ public Boolean getIgnoreCertificate() {
+ return ignoreCertificate;
+ }
+
+ /**
+ * @param ignoreCertificate
+ * the ignoreCertificate to set
+ */
+ public void setIgnoreCertificate(Boolean ignoreCertificate) {
+ this.ignoreCertificate = ignoreCertificate;
+ }
+
+ /**
+ * @return the connectionPoolSize
+ */
+ public Integer getConnectionPoolSize() {
+ return connectionPoolSize;
+ }
+
+ /**
+ * @param connectionPoolSize
+ * the connectionPoolSize to set
+ */
+ public void setConnectionPoolSize(Integer connectionPoolSize) {
+ this.connectionPoolSize = connectionPoolSize;
+ }
+
+ /**
+ * @return the connectTimeoutInSec
+ */
+ public Integer getConnectTimeoutInSec() {
+ return connectTimeoutInSec;
+ }
+
+ /**
+ * @param connectTimeoutInSec
+ * the connectTimeoutInSec to set
+ */
+ public void setConnectTimeoutInSec(Integer connectTimeoutInSec) {
+ this.connectTimeoutInSec = connectTimeoutInSec;
+ }
+
+ public String toString() {
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("[ timeoutInSec=" + readTimeoutInSec + ",");
+ builder.append("connectTimeoutInSec=" + connectTimeoutInSec + ",");
+ builder.append("ignoreCertificate=" + ignoreCertificate + ",");
+ builder.append("connectionPoolSize=" + connectionPoolSize + "]");
+
+ return builder.toString();
+
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponse.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponse.java
new file mode 100644
index 0000000000..b08cf7332b
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponse.java
@@ -0,0 +1,108 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.api;
+
+public class RestResponse {
+
+ private String response;
+
+ private String statusDescription;
+
+ private int httpStatusCode = 0;
+
+ public RestResponse(String response, String statusDescription, int httpStatusCode) {
+ super();
+ this.response = response;
+ this.statusDescription = statusDescription;
+ this.httpStatusCode = httpStatusCode;
+ }
+
+ /**
+ * @return the response
+ */
+ public String getResponse() {
+ return response;
+ }
+
+ /**
+ * @param response
+ * the response to set
+ */
+ public void setResponse(String response) {
+ this.response = response;
+ }
+
+ /**
+ * @return the httpStatusCode
+ */
+ public int getHttpStatusCode() {
+ return httpStatusCode;
+ }
+
+ /**
+ * @param httpStatusCode
+ * the httpStatusCode to set
+ */
+ public void setHttpStatusCode(int httpStatusCode) {
+ this.httpStatusCode = httpStatusCode;
+ }
+
+ /**
+ * @return the statusDescription
+ */
+ public String getStatusDescription() {
+ return statusDescription;
+ }
+
+ /**
+ * @param statusDescription
+ * the statusDescription to set
+ */
+ public void setStatusDescription(String statusDescription) {
+ this.statusDescription = statusDescription;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ *
+ * ERROR: Error from Rest Server:Status: 400 Message: Bad Request Body:
+ * Invalid cell: Cell with cell name dmgrCell22 does not exist!
+ *
+ */
+ public String toString() {
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.append("Status: ");
+ stringBuilder.append(httpStatusCode);
+ stringBuilder.append("\n");
+ stringBuilder.append("Message: ");
+ stringBuilder.append(statusDescription);
+ stringBuilder.append("\n");
+ stringBuilder.append("Body: ");
+ stringBuilder.append(response);
+
+ return stringBuilder.toString();
+
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponseAsByteArray.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponseAsByteArray.java
new file mode 100644
index 0000000000..7a4d8ee4cb
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/api/RestResponseAsByteArray.java
@@ -0,0 +1,133 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.api;
+
+import java.util.Arrays;
+
+import org.apache.http.HttpStatus;
+
+public class RestResponseAsByteArray {
+
+ private byte[] response;
+
+ private String statusDescription;
+
+ private int httpStatusCode = 0;
+
+ public RestResponseAsByteArray(byte[] response, String statusDescription, int httpStatusCode) {
+ super();
+ this.response = response;
+ this.statusDescription = statusDescription;
+ this.httpStatusCode = httpStatusCode;
+ }
+
+ /**
+ * @return the response
+ */
+ public byte[] getResponse() {
+ return response;
+ }
+
+ /**
+ * @param response
+ * the response to set
+ */
+ public void setResponse(byte[] response) {
+ this.response = response;
+ }
+
+ /**
+ * @return the httpStatusCode
+ */
+ public int getHttpStatusCode() {
+ return httpStatusCode;
+ }
+
+ /**
+ * @param httpStatusCode
+ * the httpStatusCode to set
+ */
+ public void setHttpStatusCode(int httpStatusCode) {
+ this.httpStatusCode = httpStatusCode;
+ }
+
+ /**
+ * @return the statusDescription
+ */
+ public String getStatusDescription() {
+ return statusDescription;
+ }
+
+ /**
+ * @param statusDescription
+ * the statusDescription to set
+ */
+ public void setStatusDescription(String statusDescription) {
+ this.statusDescription = statusDescription;
+ }
+
+ public String toString() {
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.append("Status: ");
+ stringBuilder.append(httpStatusCode);
+ stringBuilder.append("\n");
+ stringBuilder.append("Message: ");
+ stringBuilder.append(statusDescription);
+ stringBuilder.append("\n");
+ stringBuilder.append("Body length: ");
+ stringBuilder.append(response == null ? 0 : response.length);
+
+ return stringBuilder.toString();
+
+ }
+
+ public String toPrettyString() {
+
+ int maxBytesToDisplay = 200;
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ stringBuilder.append("Status: ");
+ stringBuilder.append(httpStatusCode);
+ stringBuilder.append("\n");
+ stringBuilder.append("Message: ");
+ stringBuilder.append(statusDescription);
+ stringBuilder.append("\n");
+ if (httpStatusCode != HttpStatus.SC_OK) {
+ stringBuilder.append("Body(maximum " + maxBytesToDisplay + " bytes): ");
+ if (response != null) {
+ byte[] subArray = Arrays.copyOfRange(response, 0, Math.min(maxBytesToDisplay, response.length));
+ if (subArray != null && subArray.length > 0) {
+ String responseStr = new String(subArray);
+ stringBuilder.append(responseStr);
+ }
+ }
+ } else {
+ stringBuilder.append("Body length: ");
+ stringBuilder.append(response == null ? 0 : response.length);
+ }
+
+ return stringBuilder.toString();
+
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/HttpRestClientServiceImpl.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/HttpRestClientServiceImpl.java
new file mode 100644
index 0000000000..7175b41943
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/HttpRestClientServiceImpl.java
@@ -0,0 +1,436 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.impl;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HeaderElement;
+import org.apache.http.HeaderElementIterator;
+import org.apache.http.HeaderIterator;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+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.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.apache.http.message.BasicHeaderElementIterator;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.openecomp.sdc.common.rest.api.IRestClient;
+import org.openecomp.sdc.common.rest.api.RestClientServiceExeption;
+import org.openecomp.sdc.common.rest.api.RestConfigurationInfo;
+import org.openecomp.sdc.common.rest.api.RestResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpRestClientServiceImpl implements IRestClient {
+
+ private Logger log = LoggerFactory.getLogger(HttpRestClientServiceImpl.class.getName());
+
+ public final static int DEFAULT_CONNECTION_POOL_SIZE = 10;
+
+ public final static int DEFAULT_CONNECT_TIMEOUT = 10;
+
+ private DefaultHttpClient httpClient;
+
+ private SSLSocketFactory sslFactory;
+
+ private Logger logger = LoggerFactory.getLogger(HttpRestClientServiceImpl.class.getName());
+
+ PoolingClientConnectionManager cm = null;
+
+ public boolean init() {
+
+ return init(new RestConfigurationInfo());
+
+ }
+
+ public boolean init(RestConfigurationInfo restConfigurationInfo) {
+
+ boolean initialized = false;
+
+ logger.debug("HttpRestClientServiceImpl::init - start. restConfigurationInfo=" + restConfigurationInfo);
+
+ try {
+ createHttpClient(restConfigurationInfo);
+
+ initialized = true;
+ } catch (KeyManagementException e) {
+ String msg = "Failed creating client config for rest. " + e.getMessage();
+ logger.error(msg, e);
+ // throw new RestClientServiceExeption(e.getMessage());
+ } catch (NoSuchAlgorithmException e) {
+ String msg = "Failed creating client config for rest. " + e.getMessage();
+ logger.error(msg, e);
+ // throw new RestClientServiceExeption(msg);
+ }
+
+ logger.debug("HttpRestClientServiceImpl::init - finish successfully");
+
+ return initialized;
+ }
+
+ public void destroy() {
+
+ if (this.httpClient != null) {
+ this.httpClient.getConnectionManager().shutdown();
+ logger.info("After closing connection Manager of rest Client.");
+ }
+
+ }
+
+ private void createHttpClient(RestConfigurationInfo restConfigurationInfo)
+ throws KeyManagementException, NoSuchAlgorithmException {
+
+ PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
+
+ Integer connPoolSizeObj = restConfigurationInfo.getConnectionPoolSize();
+ int connPoolSize = DEFAULT_CONNECTION_POOL_SIZE;
+ if (connPoolSizeObj != null) {
+ connPoolSize = connPoolSizeObj.intValue();
+ if (connPoolSize <= 0) {
+ connPoolSize = DEFAULT_CONNECTION_POOL_SIZE;
+ }
+ }
+ cm.setMaxTotal(connPoolSize);
+
+ this.httpClient = new DefaultHttpClient(cm);
+
+ int timeoutInSec = restConfigurationInfo.getReadTimeoutInSec() == null ? 0
+ : restConfigurationInfo.getReadTimeoutInSec();
+ int connectTimeoutInSec = restConfigurationInfo.getConnectTimeoutInSec() == null ? DEFAULT_CONNECT_TIMEOUT
+ : restConfigurationInfo.getConnectTimeoutInSec();
+ HttpParams params = new BasicHttpParams();
+ params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectTimeoutInSec * 1000);
+ params.setParameter(CoreConnectionPNames.SO_TIMEOUT, timeoutInSec * 1000);
+
+ this.httpClient.setParams(params);
+
+ Boolean ignoreCertificateObj = restConfigurationInfo.getIgnoreCertificate();
+ boolean ignoreCertificate = false;
+ if (ignoreCertificateObj != null) {
+ ignoreCertificate = ignoreCertificateObj.booleanValue();
+ }
+ if (ignoreCertificate == true) {
+
+ this.sslFactory = createSSLSocketFactory();
+
+ Scheme scheme = new Scheme("https", 9443, sslFactory);
+ this.httpClient.getConnectionManager().getSchemeRegistry().register(scheme);
+ }
+
+ // addKeepAlive();
+
+ this.cm = cm;
+ }
+
+ private void addKeepAlive() {
+
+ this.httpClient.setReuseStrategy(new ConnectionReuseStrategy() {
+
+ public boolean keepAlive(HttpResponse response, HttpContext context) {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ });
+
+ this.httpClient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
+
+ public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
+ log.debug("============ In getKeepAliveDuration ================= ");
+
+ HeaderIterator headerIterator = response.headerIterator(HTTP.CONN_KEEP_ALIVE);
+ if (headerIterator != null) {
+ HeaderElementIterator it = new BasicHeaderElementIterator(headerIterator);
+ while (it.hasNext()) {
+ HeaderElement he = it.nextElement();
+ String param = he.getName();
+ String value = he.getValue();
+ if (value != null && param.equalsIgnoreCase("timeout")) {
+ try {
+ log.debug("============ In getKeepAliveDuration ================= {}", value);
+
+ return Long.parseLong(value) * 1000;
+ } catch (NumberFormatException ignore) {
+ log.error("Failed parsing retrieved value of timeout header.", ignore);
+ }
+ }
+ }
+ }
+ return 20;
+ }
+ });
+
+ }
+
+ protected SSLSocketFactory createSSLSocketFactory() throws NoSuchAlgorithmException, KeyManagementException {
+ TrustManager easyTrustManager = new javax.net.ssl.X509TrustManager() {
+
+ public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
+ throws CertificateException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
+ throws CertificateException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ };
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, new TrustManager[] { easyTrustManager }, null);
+ SSLSocketFactory sslFactory = new SSLSocketFactory(sslContext);
+
+ sslFactory.setHostnameVerifier(new X509HostnameVerifier() {
+
+ public boolean verify(String arg0, SSLSession arg1) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void verify(String host, SSLSocket ssl) throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void verify(String host, java.security.cert.X509Certificate cert) throws SSLException {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
+ // TODO Auto-generated method stub
+
+ }
+
+ });
+ return sslFactory;
+ }
+
+ /**
+ * Executes RS-GET to perform FIND.
+ *
+ * @param headerParameterKey
+ * String
+ * @param headerParameterValue
+ * String
+ * @return String
+ */
+ public RestResponse doGET(String uri, Properties headers) {
+ logger.debug("Before executing uri " + uri + ". headers = " + headers);
+
+ HttpGet httpGet = new HttpGet(uri);
+
+ RestResponse response = execute(httpGet, headers);
+
+ return response;
+ }
+
+ private void addHeadersToRequest(HttpRequestBase httpRequestBase, Properties headers) {
+
+ if (headers != null) {
+ for (Entry<Object, Object> entry : headers.entrySet()) {
+ httpRequestBase.addHeader(entry.getKey().toString(), entry.getValue().toString());
+ }
+ }
+
+ }
+
+ public RestResponse doPOST(String uri, Properties headers, Object objectToCreate) {
+
+ logger.debug("Before executing uri " + uri + ". body = "
+ + (objectToCreate != null ? objectToCreate.toString() : null) + ". headers = " + headers);
+
+ HttpPost httpPost = new HttpPost(uri);
+
+ if (objectToCreate != null) {
+ StringEntity se;
+ try {
+ se = new StringEntity(objectToCreate.toString());
+
+ // se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
+ // "application/json"));
+ httpPost.setEntity(se);
+ } catch (UnsupportedEncodingException e) {
+ String msg = "Failed creating Entity for post request." + e.getMessage();
+ log.error(msg, e);
+ return null;
+ // throw new RestClientServiceExeption(msg);
+ }
+ }
+
+ RestResponse response = execute(httpPost, headers);
+
+ return response;
+
+ }
+
+ public RestResponse doPUT(String uri, Properties headers, Object objectToCreate) {
+
+ logger.debug("Before executing uri " + uri + ". body = "
+ + (objectToCreate != null ? objectToCreate.toString() : null) + ". headers = " + headers);
+
+ HttpPut httpPut = new HttpPut(uri);
+
+ if (objectToCreate != null) {
+ StringEntity se;
+ try {
+ se = new StringEntity(objectToCreate.toString());
+
+ // se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
+ // "application/json"));
+ httpPut.setEntity(se);
+ } catch (UnsupportedEncodingException e) {
+ String msg = "Failed creating Entity for post request." + e.getMessage();
+ // throw new RestClientServiceExeption(msg);
+ log.error(msg, e);
+ return null;
+ }
+ }
+
+ RestResponse response = execute(httpPut, headers);
+
+ return response;
+ }
+
+ private RestResponse execute(HttpRequestBase httpRequestBase, Properties headers) {
+
+ String response = null;
+ String statusDesc = null;
+ int statusCode = HttpStatus.SC_OK;
+
+ try {
+
+ addHeadersToRequest(httpRequestBase, headers);
+
+ HttpResponse httpResponse = this.httpClient.execute(httpRequestBase);
+
+ statusCode = httpResponse.getStatusLine().getStatusCode();
+ statusDesc = httpResponse.getStatusLine().getReasonPhrase();
+
+ HttpEntity entity = httpResponse.getEntity();
+ if (entity != null) {
+ response = EntityUtils.toString(entity);
+ }
+ // ensure the connection gets released to the manager
+ EntityUtils.consume(entity);
+
+ logResponse(response, httpRequestBase.getMethod());
+
+ } catch (Exception exception) {
+ httpRequestBase.abort();
+ log.error("Failed to execute the " + httpRequestBase.getMethod() + " request " + httpRequestBase.getURI(),
+ exception);
+ // processAndThrowException(exception);
+ return null;
+ }
+
+ RestResponse restResponse = new RestResponse(response, statusDesc, statusCode);
+
+ if (logger.isDebugEnabled()) {
+ URI uri = httpRequestBase.getURI();
+ String url = uri.toString();
+ logger.debug("After executing uri " + url + ". response = " + restResponse);
+ }
+
+ return restResponse;
+ }
+
+ public RestResponse doDELETE(String uri, Properties headers) {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Before executing uri " + uri + ". headers = " + headers);
+ }
+
+ HttpDelete httpDelete = new HttpDelete(uri);
+
+ RestResponse restResponse = execute(httpDelete, headers);
+
+ return restResponse;
+
+ }
+
+ /**
+ * This method print the JSON response from the REST Server
+ *
+ * @param response
+ * the JSON response from the REST server
+ * @param method
+ * name of method
+ */
+ private void logResponse(String response, String method) {
+ logger.debug(method + " response = " + response);
+ }
+
+ /**
+ * Exception during client invocation usually it happens when status code
+ * starting with 400 or 500 is returned
+ *
+ * @param exception
+ * Exception
+ * @throws RSClientServiceExeption
+ */
+ private void processAndThrowException(Exception exception) throws RestClientServiceExeption {
+
+ logger.debug("\n------------------------");
+ logger.debug("FAILURE: " + exception.getMessage());
+
+ throw new RestClientServiceExeption(exception);
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/RestClientServiceFactory.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/RestClientServiceFactory.java
new file mode 100644
index 0000000000..5bf250f59c
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/RestClientServiceFactory.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.impl;
+
+import org.openecomp.sdc.common.rest.api.IRestClient;
+import org.openecomp.sdc.common.rest.api.RestConfigurationInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RestClientServiceFactory {
+
+ private static Logger log = LoggerFactory.getLogger(RestClientServiceFactory.class.getName());
+
+ public static IRestClient createRestClientService(RestConfigurationInfo restConfigurationInfo) {
+
+ log.trace("Enter createRestClientService");
+
+ HttpRestClientServiceImpl restClientServiceImpl = new HttpRestClientServiceImpl();
+
+ boolean result = restClientServiceImpl.init(restConfigurationInfo);
+ if (result == false) {
+ return null;
+ }
+
+ log.trace("Exit createRestClientService");
+
+ return restClientServiceImpl;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RequestHeadersValidator.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RequestHeadersValidator.java
new file mode 100644
index 0000000000..5e2ff37735
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RequestHeadersValidator.java
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.impl.validator;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.commons.codec.binary.Base64;
+import org.openecomp.sdc.common.api.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RequestHeadersValidator {
+ private static Logger log = LoggerFactory.getLogger(RequestHeadersValidator.class.getName());
+
+ public static void validateContentType(HttpServletRequest request, MediaType expectedContentType,
+ Map<String, String> headersMap) throws RestRequestValidationException {
+
+ log.debug("validateContentType - expected: " + expectedContentType);
+ if (request == null || expectedContentType == null) {
+ throw new RestRequestValidationException("request or media-type are null");
+ }
+ String contentType = request.getHeader(Constants.CONTENT_TYPE_HEADER);
+ if (contentType == null || !contentType.contains(MediaType.APPLICATION_JSON)) {
+ throw new RestRequestValidationException(
+ "Content-Type of requset is different then " + expectedContentType);
+ } else {
+ headersMap.put(Constants.CONTENT_TYPE_HEADER, contentType);
+ }
+ }
+
+ public static void validateIdentificationHeaders(HttpServletRequest request, List<String> identificationList,
+ Map<String, String> headersMap) throws RestRequestValidationException {
+
+ log.debug("validateIdentificationHeaders");
+ for (String requiredHeader : identificationList) {
+ String headerVal = request.getHeader(requiredHeader);
+ if (headerVal != null && !headerVal.isEmpty()) {
+ headersMap.put(requiredHeader, headerVal);
+ log.debug("found header - " + requiredHeader + ": " + headerVal);
+ } else {
+ log.error("missing identification header: " + requiredHeader);
+ throw new RestRequestValidationException("missing identification header: " + requiredHeader);
+ }
+ }
+
+ }
+
+ public static void validateMd5(byte[] encodedData, HttpServletRequest request, Map<String, String> headersMap)
+ throws RestRequestValidationException {
+
+ // validate parameters
+ if (encodedData == null || request == null) {
+ throw new RestRequestValidationException("encoded data or request are not valid");
+ }
+
+ // calculate MD5 on the data
+ String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(encodedData);
+ byte[] encodedMd5 = Base64.encodeBase64(md5.getBytes());
+
+ // read the Content-MD5 header
+ String origMd5 = request.getHeader(Constants.MD5_HEADER);
+ if ((origMd5 == null) || origMd5.isEmpty()) {
+ throw new RestRequestValidationException("missing Content-MD5 header ");
+ }
+
+ // verify MD5 value
+ if (!origMd5.equals(new String(encodedMd5))) {
+ throw new RestRequestValidationException("uploaded file failed MD5 validation");
+ }
+ headersMap.put(Constants.MD5_HEADER, origMd5);
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RestRequestValidationException.java b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RestRequestValidationException.java
new file mode 100644
index 0000000000..90ed39e1c2
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/rest/impl/validator/RestRequestValidationException.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.rest.impl.validator;
+
+public class RestRequestValidationException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1962654783383591672L;
+
+ public RestRequestValidationException(String message) {
+ super(message);
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java b/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java
new file mode 100644
index 0000000000..816ebe3416
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/servlets/BasicServlet.java
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.servlets;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public abstract class BasicServlet {
+
+ protected Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/CapabilityTypeNameEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/CapabilityTypeNameEnum.java
new file mode 100644
index 0000000000..cb78b8ed9b
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/CapabilityTypeNameEnum.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+public enum CapabilityTypeNameEnum {
+ ROOT("tosca.capabilities.Root"),
+ NODE("tosca.capabilities.Node"),
+ CONTAINER("tosca.capabilities.Container"),
+ ENDPOINT("tosca.capabilities.Endpoint"),
+ ENDPOINT_PUBLIC("tosca.capabilities.Endpoint.Public"),
+ ENDPOINT_ADMIN("tosca.capabilities.Endpoint.Admin"),
+ ENDPOINT_DATABASE("tosca.capabilities.Endpoint.Database"),
+ OPERATING_SYSTEM("tosca.capabilities.OperatingSystem"),
+ SCALABLE("tosca.capabilities.Scalable"),
+ BINDABLE("tosca.capabilities.network.Bindable"),
+ DOCKER("tosca.capabilities.Container.Docker"),
+ ATTACHMENT("tosca.capabilities.Attachment");
+
+ private String capabilityName;
+
+ private CapabilityTypeNameEnum(String capName) {
+ this.capabilityName = capName;
+ }
+
+ public String getCapabilityName() {
+ return capabilityName;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/GeneralUtility.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/GeneralUtility.java
new file mode 100644
index 0000000000..0c62b03d14
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/GeneralUtility.java
@@ -0,0 +1,150 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.openecomp.sdc.common.api.Constants;
+
+public class GeneralUtility {
+
+ public static boolean generateTextFile(String fileName, String fileData) {
+ boolean isSuccessfull = true;
+ try {
+ FileUtils.writeStringToFile(new File(fileName), fileData);
+ } catch (IOException e) {
+ isSuccessfull = false;
+ }
+ return isSuccessfull;
+ }
+
+ public static boolean isBase64Encoded(byte[] data) {
+ return Base64.isBase64(data);
+ }
+
+ public static boolean isBase64Encoded(String str) {
+ boolean isEncoded = false;
+ try {
+ // checks if the string was properly padded to the
+ isEncoded = ((str.length() % 4 == 0) && (Pattern.matches("\\A[a-zA-Z0-9/+]+={0,2}\\z", str)));
+ if (isEncoded) {
+ // If no exception is caught, then it is possibly a base64
+ // encoded string
+ byte[] data = Base64.decodeBase64(str);
+ }
+
+ } catch (Exception e) {
+ // If exception is caught, then it is not a base64 encoded string
+ isEncoded = false;
+ }
+ return isEncoded;
+ }
+
+ /**
+ * Checks whether the passed string exceeds a limit of number of characters.
+ *
+ * @param str str
+ * @param limit limit
+ * @return the result of comparison, or false if str is null.
+ */
+ public static boolean isExceedingLimit(String str, int limit) {
+ if (str == null) {
+ return false;
+ }
+ return str.length() > limit;
+ }
+
+ /**
+ * Checks the passed string list whether the cumulative length of strings
+ * and delimiters between them exceeds a limit of number of characters. For
+ * example for list ("one","two","three") with delimiter "," the length of
+ * list is calculated 3+1+3+1+5=13
+ *
+ * @param strList strList
+ * @param limit Limit
+ * @param delimiterLength delimiterLength
+ * - 0 if there is no delimeter.
+ * @return the result of comparison, or false if strList is null.
+ */
+ public static boolean isExceedingLimit(List<String> strList, int limit, int delimiterLength) {
+ if (strList == null || strList.isEmpty()) {
+ return false;
+ }
+ int sum = 0;
+ int size = strList.size();
+ for (int i = 0; i < size - 1; i++) {
+ String str = strList.get(i);
+ if (str != null) {
+ sum += str.length();
+ }
+ sum += delimiterLength;
+ }
+ String str = strList.get(size - 1);
+ if (str != null) {
+ sum += str.length();
+ }
+ return sum > limit;
+ }
+
+ /**
+ * Return the extension as the substring from the last dot. For input
+ * "kuku.txt", "txt" will be returned. If no dot is found or input is null,
+ * empty string is returned.
+ *
+ * @param fileName fileName
+ * @return extension
+ */
+ public static String getFilenameExtension(String fileName) {
+ String res = Constants.EMPTY_STRING;
+ if (fileName != null) {
+ int indexOf = fileName.lastIndexOf('.');
+ if (indexOf != -1 && indexOf < (fileName.length() - 1)) {
+ res = fileName.substring(indexOf + 1);
+ }
+ }
+ return res;
+ }
+
+ public static String calculateMD5ByByteArray(byte[] payload) {
+ String decodedMd5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(payload);
+ byte[] encodeMd5 = Base64.encodeBase64(decodedMd5.getBytes());
+ return new String(encodeMd5);
+
+ }
+
+ /**
+ *
+ * @param data data
+ * @return
+ */
+ public static String calculateMD5ByString(String data) {
+ String calculatedMd5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(data);
+
+ // encode base-64 result
+ byte[] encodeBase64 = Base64.encodeBase64(calculatedMd5.getBytes());
+ return new String(encodeBase64);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/GsonFactory.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/GsonFactory.java
new file mode 100644
index 0000000000..7c3bcb6cad
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/GsonFactory.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class GsonFactory {
+ private static Gson gson = null;
+
+ public static Gson getGson() {
+ if (gson == null) {
+ synchronized (GsonFactory.class) {
+ if (gson == null) {
+ gson = new GsonBuilder().disableHtmlEscaping().serializeNulls().create();
+ }
+ }
+ }
+
+ return gson;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/HtmlCleaner.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/HtmlCleaner.java
new file mode 100644
index 0000000000..4cbd9a7760
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/HtmlCleaner.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTML.Tag;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+public class HtmlCleaner {
+
+ private static Set<String> htmlTags = new HashSet<>();
+
+ private static String patternHtmlFullTagStr = "</?\\w+((\\s+\\w+(\\s*=\\s*(?:\".*?\"|'.*?'|[\\^'\">\\s]+))?)+\\s*|\\s*)/?>";
+
+ private static String patternHtmlTagOnlyStr = "</?(\\w+)[^>]*/?>";
+
+ private static Pattern onlyTagPattern = Pattern.compile(patternHtmlTagOnlyStr);
+
+ private static Pattern fullTagPattern = Pattern.compile(patternHtmlFullTagStr);
+
+ static {
+ Tag[] allTags = HTML.getAllTags();
+ for (Tag tag : allTags) {
+ htmlTags.add(tag.toString().toLowerCase());
+ }
+ }
+
+ public static String stripHtml(String input) {
+
+ return stripHtml(input, false);
+
+ }
+
+ public static String stripHtml(String input, boolean toEscape) {
+
+ if (input == null || true == input.isEmpty()) {
+ return input;
+ }
+
+ Matcher matcher = onlyTagPattern.matcher(input);
+
+ Set<String> tagsToRemove = new HashSet<>();
+
+ while (matcher.find()) {
+
+ int start = matcher.start();
+ int end = matcher.end();
+
+ String matchTag = input.substring(start, end);
+
+ int groupCount = matcher.groupCount();
+
+ if (groupCount > 0) {
+ String tag = matcher.group(1);
+ if (tag != null && htmlTags.contains(tag.toLowerCase())) {
+ if (false == tagsToRemove.contains(matchTag)) {
+ tagsToRemove.add(matchTag);
+ }
+ }
+ }
+ }
+
+ String stripHtmlStr = removeTagsFromString(tagsToRemove, input);
+
+ if (stripHtmlStr != null) {
+ if (true == toEscape) {
+ stripHtmlStr = StringEscapeUtils.escapeHtml4(stripHtmlStr);
+ }
+ }
+
+ return stripHtmlStr;
+
+ }
+
+ private static String removeTagsFromString(Set<String> tagsToRemove, String input) {
+
+ String stripStr = input;
+ if (input == null || true == tagsToRemove.isEmpty()) {
+ return input;
+ }
+
+ for (String tag : tagsToRemove) {
+ stripStr = stripStr.replaceAll(tag, "");
+ }
+ return stripStr;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/HttpUtil.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/HttpUtil.java
new file mode 100644
index 0000000000..f4f34a9934
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/HttpUtil.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import fj.data.Either;
+
+public class HttpUtil {
+ public static Either<String, IOException> readJsonStringFromRequest(HttpServletRequest request) {
+ Either<String, IOException> eitherResult;
+ try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+ ServletInputStream reader = request.getInputStream();
+ int value;
+ while ((value = reader.read()) != -1) {
+ stream.write(value);
+ }
+ eitherResult = Either.left(new String(stream.toByteArray()));
+ } catch (IOException e) {
+ eitherResult = Either.right(e);
+ }
+ return eitherResult;
+
+ }
+
+ /**
+ * Builds an object from a JSON in the POST Body of the request.
+ */
+ public static <T> Either<T, Exception> getObjectFromJson(HttpServletRequest request, Class<T> classOfT) {
+ Either<T, Exception> eitherResult;
+ try {
+ Either<String, IOException> eitherReadJson = readJsonStringFromRequest(request);
+ if (eitherReadJson.isLeft()) {
+ eitherResult = convertJsonStringToObject(eitherReadJson.left().value(), classOfT);
+ } else {
+ eitherResult = Either.right((Exception) eitherReadJson.right().value());
+ }
+ } catch (Exception e) {
+ eitherResult = Either.right(e);
+ }
+
+ return eitherResult;
+ }
+
+ public static <T> Either<T, Exception> convertJsonStringToObject(String sentJson, Class<T> classOfT) {
+ Either<T, Exception> eitherResult;
+ try {
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ T object = gson.fromJson(sentJson, classOfT);
+ eitherResult = Either.left(object);
+ } catch (Exception e) {
+ eitherResult = Either.right(e);
+ }
+ return eitherResult;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/JsonUtils.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/JsonUtils.java
new file mode 100644
index 0000000000..30d70cd019
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/JsonUtils.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import com.google.gson.JsonElement;
+
+public class JsonUtils {
+
+ public static String toString(JsonElement jsonElement) {
+
+ if (jsonElement == null) {
+ return null;
+ }
+
+ if (false == jsonElement.isJsonNull()) {
+ if (false == jsonElement.isJsonObject()) {
+ return jsonElement.getAsString();
+ } else {
+ return jsonElement.toString();
+ }
+ } else {
+ return null;
+ }
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/MethodActivationStatusEnum.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/MethodActivationStatusEnum.java
new file mode 100644
index 0000000000..bba04fa359
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/MethodActivationStatusEnum.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+public enum MethodActivationStatusEnum {
+ SUCCESS, NOT_ALLOWED, FAILED, NOT_FOUND;
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/SerializationUtils.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/SerializationUtils.java
new file mode 100644
index 0000000000..ee2895f9fa
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/SerializationUtils.java
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+
+import org.nustaq.serialization.FSTConfiguration;
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import fj.data.Either;
+
+public class SerializationUtils {
+
+ private static Logger log = LoggerFactory.getLogger(SerializationUtils.class.getName());
+
+ private static FSTConfiguration conf = FSTConfiguration.createDefaultConfiguration();
+
+ public static Either<byte[], Boolean> serialize(Object object) {
+
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = new ObjectOutputStream(bos)) {
+ out.writeObject(object);
+ return Either.left(bos.toByteArray());
+ } catch (Exception e) {
+ log.debug("Failed to serialize object", e);
+ return Either.right(false);
+ }
+ }
+
+ public static Either<Object, Boolean> deserialize(byte[] bytes) {
+
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInput in = new ObjectInputStream(bis)) {
+ return Either.left(in.readObject());
+ } catch (Exception e) {
+ log.debug("Failed to deserialize object", e);
+ return Either.right(false);
+ }
+ }
+
+ public static Either<byte[], Boolean> serializeExt(Object object) {
+ try {
+ byte[] value = conf.asByteArray(object);
+ return Either.left(value);
+ } catch (Exception e) {
+ return Either.right(false);
+ }
+ }
+
+ public static <T> Either<T, Boolean> deserializeExt(byte[] bytes, Class<T> clazz, String componentName) {
+ try {
+ Object object = conf.asObject(bytes);
+ T castObject = clazz.cast(object);
+ return Either.left(castObject);
+ } catch (Exception e) {
+ log.debug("Failed to deserialize object of type " + clazz + " and uid " + componentName, e);
+ BeEcompErrorManager.getInstance().logInternalUnexpectedError("DeserializeObjectFromCache",
+ "Failed to deserialize object of type " + clazz, ErrorSeverity.WARNING);
+ return Either.right(false);
+ }
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/StreamUtils.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/StreamUtils.java
new file mode 100644
index 0000000000..90371a3403
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/StreamUtils.java
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.Spliterators.AbstractSpliterator;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/**
+ * Utility Class For Actions On Streams
+ *
+ * @author mshitrit
+ *
+ */
+public final class StreamUtils {
+ private StreamUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Breaks the stream when the predicate is not met.<br>
+ * Does not evaluate elements after the stream breaks.<br>
+ * This method evaluates the stream.<br>
+ *
+ * @param stream
+ * @param predicate
+ * @return
+ */
+ public static <T> Stream<T> takeWhilePlusOneNoEval(Stream<T> stream, Predicate<T> predicate) {
+ List<T> results = new ArrayList<>();
+ Consumer<T> listAdder = e -> results.add(e);
+ stream.map(e -> {
+ listAdder.accept(e);
+ return e;
+ }).filter(e -> !predicate.test(e)).findFirst();
+ return results.stream();
+ }
+
+ public static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<T> predicate) {
+ return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false);
+ }
+
+ public static <T> Stream<T> takeWhilePlusOne(Stream<T> stream, Predicate<T> predicate) {
+ return StreamSupport.stream(takeWhile(stream.spliterator(), new StopAfterFailPredicate<T>(predicate)), false);
+ }
+
+ private static <T> Spliterator<T> takeWhile(Spliterator<T> splitr, Predicate<T> predicate) {
+ return new MySplitIterator<T>(splitr, predicate);
+ }
+
+ public static class MySplitIterator<T> extends AbstractSpliterator<T> implements Spliterator<T> {
+ boolean stillGoing = true;
+ private Spliterator<T> innerItr;
+ private Predicate<T> innerPred;
+
+ private MySplitIterator(Spliterator<T> splitItr, Predicate<T> pred) {
+ super(splitItr.estimateSize(), 0);
+ innerItr = splitItr;
+ innerPred = pred;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer<? super T> action) {
+ boolean canAdvance = true;
+ if (stillGoing) {
+ stillGoing = innerItr.tryAdvance(createConsumerWrapper(action));
+ } else {
+ canAdvance = false;
+ }
+ return canAdvance;
+ }
+
+ private Consumer<? super T> createConsumerWrapper(Consumer<? super T> action) {
+ Consumer<? super T> cons = new Consumer<T>() {
+ @Override
+ public void accept(T t) {
+ stillGoing = innerPred.test(t);
+ if (stillGoing) {
+ action.accept(t);
+ }
+
+ }
+ };
+
+ return cons;
+ }
+
+ }
+
+ public static class StopAfterFailPredicate<T> implements Predicate<T> {
+ boolean hasNotFailed;
+ Predicate<T> innerPredicate;
+
+ private StopAfterFailPredicate(Predicate<T> pred) {
+ hasNotFailed = true;
+ innerPredicate = pred;
+ };
+
+ @Override
+ public boolean test(T t) {
+ boolean isPassed;
+ if (hasNotFailed) {
+ isPassed = true;
+ hasNotFailed = innerPredicate.test(t);
+ } else {
+ isPassed = false;
+ }
+ return isPassed;
+ }
+
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/ThreadLocalsHolder.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ThreadLocalsHolder.java
new file mode 100644
index 0000000000..92e0c07a4f
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ThreadLocalsHolder.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+public class ThreadLocalsHolder {
+
+ private static final ThreadLocal<String> uuidThreadLocal = new ThreadLocal<>();
+ private static final ThreadLocal<Long> requestStartTimeThreadLocal = new ThreadLocal<>();
+ private static final ThreadLocal<Boolean> isMdcProcessedThreadLocal = new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
+
+ public static void setMdcProcessed(Boolean isMdcProcessed) {
+ isMdcProcessedThreadLocal.set(isMdcProcessed);
+ }
+
+ public static void setUuid(String uuid) {
+ uuidThreadLocal.set(uuid);
+ }
+
+ public static void setRequestStartTime(Long requestStartTime) {
+ requestStartTimeThreadLocal.set(requestStartTime);
+ }
+
+ public static String getUuid() {
+ return uuidThreadLocal.get();
+ }
+
+ public static Long getRequestStartTime() {
+ return requestStartTimeThreadLocal.get();
+ }
+
+ public static Boolean isMdcProcessed() {
+ return isMdcProcessedThreadLocal.get();
+ }
+
+ public static void cleanup() {
+ uuidThreadLocal.remove();
+ requestStartTimeThreadLocal.remove();
+ isMdcProcessedThreadLocal.remove();
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java
new file mode 100644
index 0000000000..b2b0344b39
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ValidationUtils.java
@@ -0,0 +1,498 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.lang3.text.WordUtils;
+import org.apache.commons.validator.routines.UrlValidator;
+import org.jsoup.Jsoup;
+import org.jsoup.helper.StringUtil;
+import org.jsoup.safety.Whitelist;
+
+import com.google.common.base.CharMatcher;
+
+public class ValidationUtils {
+ public final static Integer COMPONENT_NAME_MAX_LENGTH = 1024;
+ public final static Pattern COMPONENT_NAME_PATTERN = Pattern.compile("^[\\w \\.\\-\\_\\:\\+]{1," + COMPONENT_NAME_MAX_LENGTH + "}$");
+ public final static Integer ADDITIONAL_INFORMATION_KEY_MAX_LENGTH = 50;
+ public final static Pattern ADDITIONAL_INFORMATION_KEY_PATTERN = Pattern.compile("^[\\w\\s\\.\\-\\_]{1," + COMPONENT_NAME_MAX_LENGTH + "}$");
+ public final static Integer RSI_NAME_MAX_LENGTH = 1024;
+ public final static Pattern RSI_NAME_PATTERN = Pattern.compile("^[\\w \\s\\.\\-\\_\\:\\+]{1," + RSI_NAME_MAX_LENGTH + "}$");
+ public final static Integer COMMENT_MAX_LENGTH = 256;
+
+ public final static Integer ICON_MAX_LENGTH = 25;
+ public final static Pattern ICON_PATTERN = Pattern.compile("^[\\w\\-]{1," + ICON_MAX_LENGTH + "}$");
+ public final static Integer PROJECT_CODE_MAX_LEGTH = 10;
+ //public final static Pattern PROJECT_CODE_PATTERN = Pattern.compile("^[\\d]{5," + PROJECT_CODE_MAX_LEGTH + "}$");
+ public final static Pattern PROJECT_CODE_PATTERN = Pattern.compile("^[\\s\\w_.-]{1,50}$");
+ public final static Integer CONNTACT_ID_MAX_LENGTH = 6;
+ // Format : aannnX (where a=a-z or A-Z, n=0-9, and X=a-z,A-Z, or 0-9)
+ //public final static Pattern CONTACT_ID_PATTERN = Pattern.compile("[mM]{1}[0-9]{5}|[a-zA-Z]{2}[0-9]{4}|[a-zA-Z]{2}[0-9]{3}[a-zA-Z]{1}");
+ public final static Pattern CONTACT_ID_PATTERN = Pattern.compile("^[\\s\\w_.-]{1,50}$");
+ public final static Pattern OCTET_PATTERN = Pattern.compile("%[a-fA-F0-9]{2}");
+ public final static Pattern NONE_UTF8_PATTERN = Pattern.compile("[^\\x00-\\x7F]+");
+ public final static Pattern URL_INVALIDE_PATTERN = Pattern.compile("[,#?&@$<>~^`\\\\\\[\\]{}|\")(*!+=;%]+");
+
+ public final static Pattern ENGLISH_PATTERN = Pattern.compile("^[\\p{Graph}\\x20]+$");
+ public final static Integer COMPONENT_DESCRIPTION_MAX_LENGTH = 1024;
+ public final static Integer TAG_MAX_LENGTH = 1024;
+ public final static Integer TAG_LIST_MAX_LENGTH = 1024;
+ public final static Integer VENDOR_NAME_MAX_LENGTH = 25;
+ public final static Pattern VENDOR_NAME_PATTERN = Pattern.compile("^[\\x20-\\x21\\x23-\\x29\\x2B-\\x2E\\x30-\\x39\\x3B\\x3D\\x40-\\x5B\\x5D-\\x7B\\x7D-\\xFF]+$");
+ public final static Integer VENDOR_RELEASE_MAX_LENGTH = 25;
+ public final static Pattern VENDOR_RELEASE_PATTERN = Pattern.compile("^[\\x20-\\x21\\x23-\\x29\\x2B-\\x2E\\x30-\\x39\\x3B\\x3D\\x40-\\x5B\\x5D-\\x7B\\x7D-\\xFF]+$");
+
+ public final static Pattern CLEAN_FILENAME_PATTERN = Pattern.compile("[\\x00-\\x1f\\x80-\\x9f\\x5c/<?>\\*:|\"/]+");
+
+ public final static Pattern DASH_PATTERN = Pattern.compile("[-]+");
+ public final static Pattern UNDERSCORE_PATTERN = Pattern.compile("[_]+");
+ public final static Pattern PLUS_PATTERN = Pattern.compile("[+]+");
+ public final static Pattern SPACE_PATTERN = Pattern.compile("[ ]+");
+ public final static Pattern AMP_PATTERN = Pattern.compile("[&]+");
+ public final static Pattern DOT_PATTERN = Pattern.compile("[\\.]+");
+ public final static Pattern APOST_PATTERN = Pattern.compile("[']+");
+ public final static Pattern HASHTAG_PATTERN = Pattern.compile("[#]+");
+ public final static Pattern EQUAL_PATTERN = Pattern.compile("[=]+");
+ public final static Pattern COLON_PATTERN = Pattern.compile("[:]+");
+ public final static Pattern AT_PATTERN = Pattern.compile("[@]+");
+ public final static Pattern AND_PATTERN = Pattern.compile(" [aA][Nn][Dd] ");
+ public final static Set<String> CATEGORY_CONJUNCTIONS = new HashSet<String>(Arrays.asList("of", "to", "for", "as", "a", "an", "the"));
+
+ public final static Pattern COST_PATTERN = Pattern.compile("^[0-9]{1,5}\\.[0-9]{1,3}$");
+ public final static Pattern ARTIFACT_LABEL_PATTERN = Pattern.compile("^[a-zA-Z0-9 \\-+]+$");
+ public final static Integer ARTIFACT_LABEL_LENGTH = 25;
+ public final static Pattern CATEGORY_LABEL_PATTERN = Pattern.compile("^[a-zA-Z0-9][a-zA-Z0-9 &\\.'#=:@_\\-+]+$");
+ public final static Integer CATEGORY_LABEL_MIN_LENGTH = 4;
+ public final static Integer CATEGORY_LABEL_MAX_LENGTH = 25;
+
+ public final static Pattern COMPONENT_NAME_DELIMETER_PATTERN = Pattern.compile("[\\.\\-\\_]+");
+ public final static Pattern COMPONENT_INCTANCE_NAME_DELIMETER_PATTERN = Pattern.compile("[\\.\\-]+");
+ public final static Pattern PRODUCT_NAME_DELIMETER_PATTERN = Pattern.compile("[\\.\\-\\_&=#@':\\[\\]\\+]+");
+ public final static Integer CONSUMER_NAME_MAX_LENGTH = 255;
+ // public final static Pattern CONSUMER_NAME_PATTERN =
+ // Pattern.compile("^[\\w]{1}?[\\w\\.\\-]{0," + CONSUMER_NAME_MAX_LENGTH +
+ // "}?$");
+ public final static Pattern CONSUMER_NAME_PATTERN = Pattern.compile("^[\\w]+[\\w\\.\\-]*$");
+ public final static Integer CONSUMER_SALT_LENGTH = 32;
+ public final static Integer CONSUMER_PASSWORD_LENGTH = 64;
+ public final static Pattern CONSUMER_PASS_SALT_PATTERN = Pattern.compile("^[a-z0-9]+$");
+ public final static Pattern FLOAT_PATTERN = Pattern.compile("^[\\d]+[\\.]{1}[\\d]+$");
+ public final static Pattern CERTIFIED_VERSION_PATTERN = Pattern.compile("^[1-9][0-9]*\\.0$");
+ public final static Pattern MINOR_VERSION_PATTERN = Pattern.compile("^0\\.[1-9][0-9]*$");
+ public final static Pattern TAGS_PATTERN = Pattern.compile("<[^><]*>");
+
+ public final static Integer ARTIFACT_NAME_LENGTH = 255;
+ public final static Integer API_URL_LENGTH = 100;
+ public final static Integer ARTIFACT_DESCRIPTION_MAX_LENGTH = 256;
+
+ public final static Integer PRODUCT_FULL_NAME_MIN_LENGTH = 4;
+ public final static Integer PRODUCT_FULL_NAME_MAX_LENGTH = 100;
+
+ public static boolean validateArtifactLabel(String label) {
+ return ARTIFACT_LABEL_PATTERN.matcher(label).matches();
+ }
+
+ public static boolean validateCategoryDisplayNameFormat(String label) {
+ boolean res = true;
+ if (label != null) {
+ label = label.trim();
+ res = CATEGORY_LABEL_PATTERN.matcher(label).matches();
+ }
+ return res;
+ }
+
+ public static String normalizeCategoryName4Display(String str) {
+ if (str != null) {
+ str = str.trim();
+ str = DASH_PATTERN.matcher(str).replaceAll("-");
+ str = UNDERSCORE_PATTERN.matcher(str).replaceAll("_");
+ str = AMP_PATTERN.matcher(str).replaceAll("&");
+ str = PLUS_PATTERN.matcher(str).replaceAll("+");
+ str = DOT_PATTERN.matcher(str).replaceAll(".");
+ str = APOST_PATTERN.matcher(str).replaceAll("'");
+ str = HASHTAG_PATTERN.matcher(str).replaceAll("#");
+ str = EQUAL_PATTERN.matcher(str).replaceAll("=");
+ str = COLON_PATTERN.matcher(str).replaceAll(":");
+ str = AT_PATTERN.matcher(str).replaceAll("@");
+ str = normaliseWhitespace(str);
+ str = AND_PATTERN.matcher(str).replaceAll(" & ");
+
+ // Case normalizing
+ StringBuilder sb = new StringBuilder();
+ String[] split = str.split(" ");
+ for (int i = 0; i < split.length; i++) {
+ String splitted = split[i];
+ String lowerCase = splitted.toLowerCase();
+ // BANK OF AMERICA --> BANK of AMERICA ("of" is lowercased), but
+ // OF BANK OF AMERICA --> OF BANK of AMERICA (first "OF" is not
+ // lowercased because it's first word)
+ // Agreed with Ella, 26/11/15
+ if ((i > 0) && CATEGORY_CONJUNCTIONS.contains(lowerCase)) {
+ sb.append(lowerCase);
+ } else {
+ sb.append(WordUtils.capitalize(splitted));
+ }
+ sb.append(" ");
+ }
+ str = sb.toString().trim();
+ }
+ return str;
+ }
+
+ public static String normalizeCategoryName4Uniqueness(String str) {
+ return str.toLowerCase();
+ }
+
+ public static boolean validateCategoryDisplayNameLength(String label) {
+ return (label != null && label.length() >= CATEGORY_LABEL_MIN_LENGTH && label.length() <= CATEGORY_LABEL_MAX_LENGTH);
+ }
+
+ public static boolean validateProductFullNameLength(String fullName) {
+ return (fullName != null && fullName.length() >= PRODUCT_FULL_NAME_MIN_LENGTH && fullName.length() <= PRODUCT_FULL_NAME_MAX_LENGTH);
+ }
+
+ public static boolean validateArtifactLabelLength(String label) {
+ return label.length() > 0 && label.length() <= ARTIFACT_LABEL_LENGTH;
+ }
+
+ public static boolean validateResourceInstanceNameLength(String resourceInstanceName) {
+ return resourceInstanceName.length() <= RSI_NAME_MAX_LENGTH;
+ }
+
+ public static boolean validateResourceInstanceName(String resourceInstanceName) {
+ return RSI_NAME_PATTERN.matcher(resourceInstanceName).matches();
+ }
+
+ public static boolean validateUrlLength(String url) {
+ return url.length() <= API_URL_LENGTH;
+ }
+
+ public static boolean validateArtifactNameLength(String artifactName) {
+ return (artifactName.length() <= ARTIFACT_NAME_LENGTH && artifactName.length() > 0);
+ }
+
+ public static boolean validateComponentNamePattern(String componentName) {
+ return COMPONENT_NAME_PATTERN.matcher(componentName).matches();
+ }
+
+ public static boolean validateComponentNameLength(String componentName) {
+ return componentName.length() <= COMPONENT_NAME_MAX_LENGTH;
+ }
+
+ public static boolean validateIcon(String icon) {
+ return ICON_PATTERN.matcher(icon).matches();
+ }
+
+ public static boolean validateIconLength(String icon) {
+ return icon.length() <= ICON_MAX_LENGTH;
+ }
+
+ public static boolean validateProjectCode(String projectCode) {
+ return PROJECT_CODE_PATTERN.matcher(projectCode).matches();
+ }
+
+ public static boolean validateProjectCodeLegth(String projectCode) {
+ return projectCode.length() <= PROJECT_CODE_MAX_LEGTH;
+ }
+
+ public static boolean validateContactId(String contactId) {
+ return CONTACT_ID_PATTERN.matcher(contactId).matches();
+ }
+
+ public static boolean validateCost(String cost) {
+ return COST_PATTERN.matcher(cost).matches();
+ }
+
+ public static String removeHtmlTags(String str) {
+ return Jsoup.clean(str, Whitelist.none());
+ }
+
+ public static String removeAllTags(String htmlText) {
+
+ String stripped = TAGS_PATTERN.matcher(htmlText).replaceAll("").trim();
+ return stripped;
+ }
+
+ public static String normaliseWhitespace(String str) {
+ return StringUtil.normaliseWhitespace(str);
+ }
+
+ public static String stripOctets(String str) {
+ return OCTET_PATTERN.matcher(str).replaceAll("");
+ }
+
+ public static String removeNoneUtf8Chars(String input) {
+ return NONE_UTF8_PATTERN.matcher(input).replaceAll("");
+ }
+
+ public static boolean validateIsEnglish(String input) {
+ return ENGLISH_PATTERN.matcher(input).matches();
+ }
+
+ public static boolean validateIsAscii(String input) {
+
+ boolean isAscii = CharMatcher.ASCII.matchesAllOf(input);
+
+ return isAscii;
+ }
+
+ public static String convertHtmlTagsToEntities(String input) {
+ return StringEscapeUtils.escapeHtml4(input);
+ }
+
+ public static List<String> removeDuplicateFromList(List<String> list) {
+ Set<String> hs = new LinkedHashSet<>(list);
+ list.clear();
+ list.addAll(hs);
+ return list;
+
+ }
+
+ public static boolean validateTagLength(String tag) {
+ if (tag != null) {
+ return tag.length() <= TAG_MAX_LENGTH;
+ }
+ return false;
+ }
+
+ public static boolean validateTagListLength(int tagListLength) {
+ return tagListLength <= TAG_LIST_MAX_LENGTH;
+ }
+
+ public static boolean validateDescriptionLength(String description) {
+ return description.length() <= COMPONENT_DESCRIPTION_MAX_LENGTH;
+ }
+
+ public static boolean validateStringNotEmpty(String value) {
+ if ((value == null) || (value.trim().isEmpty())) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean validateListNotEmpty(List<?> list) {
+ if ((list == null) || (list.isEmpty())) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean validateVendorName(String ventorName) {
+ return VENDOR_NAME_PATTERN.matcher(ventorName).matches();
+ }
+
+ public static boolean validateVendorNameLength(String ventorName) {
+ return ventorName.length() <= VENDOR_NAME_MAX_LENGTH;
+ }
+
+ public static boolean validateVendorRelease(String vendorRelease) {
+ return VENDOR_RELEASE_PATTERN.matcher(vendorRelease).matches();
+ }
+
+ public static boolean validateVendorReleaseLength(String vendorRelease) {
+ return vendorRelease.length() <= VENDOR_RELEASE_MAX_LENGTH;
+ }
+
+ public static boolean hasBeenCertified(String version) {
+ return NumberUtils.toDouble(version) >= 1;
+ }
+
+ public static String normaliseComponentName(String name) {
+ String[] split = splitComponentName(name);
+ StringBuffer sb = new StringBuffer();
+ for (String splitElement : split) {
+ sb.append(splitElement);
+ }
+ return sb.toString();
+
+ }
+
+ public static String normaliseComponentInstanceName(String name) {
+ String[] split = splitComponentInctanceName(name);
+ StringBuffer sb = new StringBuffer();
+ for (String splitElement : split) {
+ sb.append(splitElement);
+ }
+ return sb.toString();
+
+ }
+
+ private static String[] splitComponentName(String name) {
+ String normalizedName = name.toLowerCase();
+ normalizedName = COMPONENT_NAME_DELIMETER_PATTERN.matcher(normalizedName).replaceAll(" ");
+ String[] split = normalizedName.split(" ");
+ return split;
+ }
+
+ private static String[] splitComponentInctanceName(String name) {
+ String normalizedName = name.toLowerCase();
+ normalizedName = COMPONENT_INCTANCE_NAME_DELIMETER_PATTERN.matcher(normalizedName).replaceAll(" ");
+ String[] split = normalizedName.split(" ");
+ return split;
+ }
+
+ public static String convertToSystemName(String name) {
+ String[] split = splitComponentName(name);
+ StringBuffer sb = new StringBuffer();
+ for (String splitElement : split) {
+ String capitalize = WordUtils.capitalize(splitElement);
+ sb.append(capitalize);
+ }
+ return sb.toString();
+ }
+
+ public static String normalizeFileName(String filename) {
+ // String[] split = filename.split(Pattern.quote(File.separator));
+ // String name = "";
+ //
+ // name = split[split.length - 1];
+ return cleanFileName(filename);
+
+ }
+
+ private static String cleanFileName(String str) {
+ str = CLEAN_FILENAME_PATTERN.matcher(str).replaceAll("");
+ str = normaliseWhitespace(str);
+ str = SPACE_PATTERN.matcher(str).replaceAll("-");
+ str = DASH_PATTERN.matcher(str).replaceAll("-");
+ str = StringUtils.strip(str, "-_ .");
+
+ return str;
+ }
+
+ public static boolean validateUrl(String url) {
+
+ UrlValidator urlValidator = new UrlValidator();
+ if (!urlValidator.isValid(url)) {
+ return false;
+ }
+ if (NONE_UTF8_PATTERN.matcher(url).find()) {
+ return false;
+ }
+
+ if (URL_INVALIDE_PATTERN.matcher(url).find()) {
+ return false;
+ }
+ return true;
+
+ }
+
+ public static String cleanArtifactDisplayName(String strIn) {
+ String str = DASH_PATTERN.matcher(strIn).replaceAll("-");
+ str = UNDERSCORE_PATTERN.matcher(str).replaceAll("_");
+ str = PLUS_PATTERN.matcher(str).replaceAll("+");
+ str = normaliseWhitespace(str);
+ str = str.trim();
+ // str = str.replaceAll(" ", "");
+
+ return str;
+ }
+
+ public static String normalizeArtifactLabel(String strIn) {
+
+ String str = DASH_PATTERN.matcher(strIn).replaceAll("");
+ str = UNDERSCORE_PATTERN.matcher(str).replaceAll("");
+ str = PLUS_PATTERN.matcher(str).replaceAll("");
+ str = SPACE_PATTERN.matcher(str).replaceAll("");
+ str = str.toLowerCase();
+
+ return str;
+ }
+
+ public static boolean validateAdditionalInformationKeyName(String str) {
+ return ADDITIONAL_INFORMATION_KEY_PATTERN.matcher(str).matches();
+ }
+
+ public static String normalizeAdditionalInformation(String str) {
+ if (str != null) {
+ str = DASH_PATTERN.matcher(str).replaceAll("-");
+ str = UNDERSCORE_PATTERN.matcher(str).replaceAll("_");
+ str = normaliseWhitespace(str);
+ }
+ return str;
+ }
+
+ public static boolean validateLength(String str, int length) {
+ if (str == null) {
+ return true;
+ }
+ return str.length() <= length;
+ }
+
+ public static boolean validateConsumerName(String consumerName) {
+ return CONSUMER_NAME_PATTERN.matcher(consumerName).matches();
+ }
+
+ public static boolean isUTF8Str(String str) {
+ if (NONE_UTF8_PATTERN.matcher(str).find()) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean validateConsumerPassSalt(String consumerSalt) {
+ return CONSUMER_PASS_SALT_PATTERN.matcher(consumerSalt).matches();
+ }
+
+ public static boolean isFloatNumber(String number) {
+ return FLOAT_PATTERN.matcher(number).matches();
+ }
+
+ public static boolean validateCertifiedVersion(String version) {
+ return (version != null && CERTIFIED_VERSION_PATTERN.matcher(version).matches());
+ }
+
+ public static boolean validateMinorVersion(String version) {
+ return (version != null && MINOR_VERSION_PATTERN.matcher(version).matches());
+ }
+
+ public static String normaliseProductName(String name) {
+ String[] split = splitComponentName(PRODUCT_NAME_DELIMETER_PATTERN, name);
+ StringBuffer sb = new StringBuffer();
+ for (String splitElement : split) {
+ sb.append(splitElement);
+ }
+ return sb.toString();
+
+ }
+
+ private static String[] splitComponentName(Pattern pattern, String name) {
+ String normalizedName = name.toLowerCase();
+ normalizedName = pattern.matcher(normalizedName).replaceAll(" ");
+ String[] split = normalizedName.split(" ");
+ return split;
+ }
+
+ public static String removeHtmlTagsOnly(String htmlText) {
+ String stripped = HtmlCleaner.stripHtml(htmlText, false);
+ return stripped;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/YamlToObjectConverter.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/YamlToObjectConverter.java
new file mode 100644
index 0000000000..75ac208432
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/YamlToObjectConverter.java
@@ -0,0 +1,275 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.be.config.Configuration.ApplicationL1CacheConfig;
+import org.openecomp.sdc.be.config.Configuration.ApplicationL2CacheConfig;
+import org.openecomp.sdc.be.config.Configuration.BeMonitoringConfig;
+import org.openecomp.sdc.be.config.Configuration.DeploymentArtifactTypeConfig;
+import org.openecomp.sdc.be.config.Configuration.EcompPortalConfig;
+import org.openecomp.sdc.be.config.Configuration.ElasticSearchConfig;
+import org.openecomp.sdc.be.config.Configuration.OnboardingConfig;
+import org.openecomp.sdc.be.config.Configuration.SwitchoverDetectorConfig;
+import org.openecomp.sdc.be.config.Configuration.ToscaValidatorsConfig;
+import org.openecomp.sdc.be.config.Configuration.ElasticSearchConfig.IndicesTimeFrequencyEntry;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.ComponentArtifactTypesConfig;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.CreateTopicConfig;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.DistributionNotificationTopicConfig;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration.DistributionStatusTopicConfig;
+import org.openecomp.sdc.be.config.validation.DeploymentArtifactHeatConfiguration;
+import org.openecomp.sdc.fe.config.Configuration.FeMonitoringConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.nodes.Node;
+
+public class YamlToObjectConverter {
+
+ private static Logger log = LoggerFactory.getLogger(YamlToObjectConverter.class.getName());
+
+ private static HashMap<String, Yaml> yamls = new HashMap<String, Yaml>();
+
+ private static Yaml defaultYaml = new Yaml();
+
+ static {
+
+ org.yaml.snakeyaml.constructor.Constructor deConstructor = new org.yaml.snakeyaml.constructor.Constructor(
+ DistributionEngineConfiguration.class);
+ TypeDescription deDescription = new TypeDescription(DistributionEngineConfiguration.class);
+ deDescription.putListPropertyType("distributionStatusTopic", DistributionStatusTopicConfig.class);
+ deDescription.putListPropertyType("distribNotifServiceArtifactTypes", ComponentArtifactTypesConfig.class);
+ deDescription.putListPropertyType("distribNotifResourceArtifactTypes", ComponentArtifactTypesConfig.class);
+ deDescription.putListPropertyType("createTopic", CreateTopicConfig.class);
+ deDescription.putListPropertyType("distributionNotificationTopic", DistributionNotificationTopicConfig.class);
+ deConstructor.addTypeDescription(deDescription);
+ Yaml yaml = new Yaml(deConstructor);
+ yamls.put(DistributionEngineConfiguration.class.getName(), yaml);
+
+ // FE conf
+ org.yaml.snakeyaml.constructor.Constructor feConfConstructor = new org.yaml.snakeyaml.constructor.Constructor(
+ org.openecomp.sdc.fe.config.Configuration.class);
+ TypeDescription feConfDescription = new TypeDescription(org.openecomp.sdc.fe.config.Configuration.class);
+ feConfDescription.putListPropertyType("systemMonitoring", FeMonitoringConfig.class);
+ feConfConstructor.addTypeDescription(feConfDescription);
+ yamls.put(org.openecomp.sdc.fe.config.Configuration.class.getName(), new Yaml(feConfConstructor));
+
+ // BE conf
+ org.yaml.snakeyaml.constructor.Constructor beConfConstructor = new org.yaml.snakeyaml.constructor.Constructor(
+ org.openecomp.sdc.be.config.Configuration.class);
+ TypeDescription beConfDescription = new TypeDescription(org.openecomp.sdc.be.config.Configuration.class);
+ beConfConstructor.addTypeDescription(beConfDescription);
+
+ // systemMonitoring
+ beConfDescription.putListPropertyType("systemMonitoring", BeMonitoringConfig.class);
+
+ // elasticSearch
+ beConfDescription.putListPropertyType("elasticSearch", ElasticSearchConfig.class);
+ TypeDescription esDescription = new TypeDescription(ElasticSearchConfig.class);
+ esDescription.putListPropertyType("indicesTimeFrequency", IndicesTimeFrequencyEntry.class);
+ beConfConstructor.addTypeDescription(esDescription);
+
+ // resourceDeploymentArtifacts and serviceDeploymentArtifacts
+ beConfDescription.putMapPropertyType("resourceDeploymentArtifacts", String.class,
+ DeploymentArtifactTypeConfig.class);
+ beConfDescription.putMapPropertyType("serviceDeploymentArtifacts", String.class,
+ DeploymentArtifactTypeConfig.class);
+
+ // onboarding
+ beConfDescription.putListPropertyType("onboarding", OnboardingConfig.class);
+
+ // ecompPortal
+ beConfDescription.putListPropertyType("ecompPortal", EcompPortalConfig.class);
+ // switchoverDetector
+ beConfDescription.putListPropertyType("switchoverDetector", SwitchoverDetectorConfig.class);
+
+ // ApplicationL1Cache
+ beConfDescription.putListPropertyType("applicationL1Cache", ApplicationL1CacheConfig.class);
+
+ // ApplicationL2Cache
+ beConfDescription.putListPropertyType("applicationL2Cache", ApplicationL2CacheConfig.class);
+
+ // tosca validators config
+ beConfDescription.putListPropertyType("toscaValidators", ToscaValidatorsConfig.class);
+
+ yamls.put(org.openecomp.sdc.be.config.Configuration.class.getName(), new Yaml(beConfConstructor));
+
+ // HEAT deployment artifact
+ org.yaml.snakeyaml.constructor.Constructor depArtHeatConstructor = new org.yaml.snakeyaml.constructor.Constructor(
+ DeploymentArtifactHeatConfiguration.class);
+ PropertyUtils propertyUtils = new PropertyUtils();
+ // Skip properties which are found in YAML but not found in POJO
+ propertyUtils.setSkipMissingProperties(true);
+ depArtHeatConstructor.setPropertyUtils(propertyUtils);
+ yamls.put(org.openecomp.sdc.be.config.validation.DeploymentArtifactHeatConfiguration.class.getName(),
+ new Yaml(depArtHeatConstructor));
+
+ }
+
+ private static <T> Yaml getYamlByClassName(Class<T> className) {
+
+ Yaml yaml = yamls.get(className.getName());
+ if (yaml == null) {
+ yaml = defaultYaml;
+ }
+
+ return yaml;
+ }
+
+ public <T> T convert(String dirPath, Class<T> className, String configFileName) {
+
+ T config = null;
+
+ try {
+
+ String fullFileName = dirPath + File.separator + configFileName;
+
+ config = convert(fullFileName, className);
+
+ } catch (Exception e) {
+ log.error("Failed to convert yaml file " + configFileName + " to object.", e);
+ }
+
+ return config;
+ }
+
+ public class MyYamlConstructor extends org.yaml.snakeyaml.constructor.Constructor {
+ private HashMap<String, Class<?>> classMap = new HashMap<String, Class<?>>();
+
+ public MyYamlConstructor(Class<? extends Object> theRoot) {
+ super(theRoot);
+ classMap.put(DistributionEngineConfiguration.class.getName(), DistributionEngineConfiguration.class);
+ classMap.put(DistributionStatusTopicConfig.class.getName(), DistributionStatusTopicConfig.class);
+ }
+
+ /*
+ * This is a modified version of the Constructor. Rather than using a
+ * class loader to get external classes, they are already predefined
+ * above. This approach works similar to the typeTags structure in the
+ * original constructor, except that class information is pre-populated
+ * during initialization rather than runtime.
+ *
+ * @see
+ * org.yaml.snakeyaml.constructor.Constructor#getClassForNode(org.yaml.
+ * snakeyaml.nodes.Node)
+ */
+ protected Class<?> getClassForNode(Node node) {
+ String name = node.getTag().getClassName();
+ Class<?> cl = classMap.get(name);
+ if (cl == null)
+ throw new YAMLException("Class not found: " + name);
+ else
+ return cl;
+ }
+ }
+
+ public <T> T convert(String fullFileName, Class<T> className) {
+
+ T config = null;
+
+ Yaml yaml = getYamlByClassName(className);
+
+ InputStream in = null;
+ try {
+
+ File f = new File(fullFileName);
+ if (false == f.exists()) {
+ log.warn("The file " + fullFileName + " cannot be found. Ignore reading configuration.");
+ return null;
+ }
+ in = Files.newInputStream(Paths.get(fullFileName));
+
+ config = yaml.loadAs(in, className);
+
+ // System.out.println(config.toString());
+ } catch (Exception e) {
+ log.error("Failed to convert yaml file " + fullFileName + " to object.", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return config;
+ }
+
+ public <T> T convert(byte[] fileContents, Class<T> className) {
+
+ T config = null;
+
+ Yaml yaml = getYamlByClassName(className);
+
+ InputStream in = null;
+ try {
+
+ in = new ByteArrayInputStream(fileContents);
+
+ config = yaml.loadAs(in, className);
+
+ } catch (Exception e) {
+ log.error("Failed to convert yaml file to object", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return config;
+ }
+
+ public boolean isValidYaml(byte[] fileContents) {
+ if (Base64.isBase64(fileContents)) {
+ log.trace("Received Base64 data - decoding before validating...");
+ fileContents = Base64.decodeBase64(fileContents);
+ }
+ try {
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) defaultYaml
+ .load(new ByteArrayInputStream(fileContents));
+ } catch (Exception e) {
+ log.error("Failed to convert yaml file to object - yaml is invalid", e);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/common/util/ZipUtil.java b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ZipUtil.java
new file mode 100644
index 0000000000..ac25c40cf7
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/common/util/ZipUtil.java
@@ -0,0 +1,178 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ZipUtil {
+
+ private static Logger log = LoggerFactory.getLogger(ZipUtil.class.getName());
+
+ private ZipUtil() {
+ }
+
+ public static Map<String, byte[]> readZip(byte[] zipAsBytes) {
+
+ ZipInputStream zis = null;
+ zis = new ZipInputStream(new ByteArrayInputStream(zipAsBytes));
+
+ return readZip(zis);
+ }
+
+ public static Map<String, byte[]> readZip(ZipInputStream zis) {
+
+ Map<String, byte[]> fileNameToByteArray = new HashMap<String, byte[]>();
+
+ byte[] buffer = new byte[1024];
+ try {
+ // get the zipped file list entry
+ ZipEntry ze = zis.getNextEntry();
+
+ while (ze != null) {
+
+ String fileName = ze.getName();
+
+ if (false == ze.isDirectory()) {
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ os.write(buffer, 0, len);
+ }
+
+ fileNameToByteArray.put(fileName, os.toByteArray());
+
+ } finally {
+ if (os != null) {
+ os.close();
+ }
+ }
+ }
+ ze = zis.getNextEntry();
+
+ }
+
+ zis.closeEntry();
+ zis.close();
+
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return null;
+ } finally {
+ if (zis != null) {
+ try {
+ zis.closeEntry();
+ zis.close();
+ } catch (IOException e) {
+ // TODO: add log
+ }
+
+ }
+ }
+
+ return fileNameToByteArray;
+
+ }
+
+ public static void main(String[] args) {
+
+ String zipFileName = "/src/test/resources/config/config.zip";
+
+ zipFileName = "C:\\Git_work\\D2-SDnC\\catalog-be\\src\\test\\resources\\config\\config.zip";
+
+ Path path = Paths.get(zipFileName);
+
+ try {
+ byte[] zipAsBytes = Files.readAllBytes(path);
+ // encode to base
+
+ ZipUtil.readZip(zipAsBytes);
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ public static byte[] zipBytes(byte[] input) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream zos = new ZipOutputStream(baos);
+ ZipEntry entry = new ZipEntry("zip");
+ entry.setSize(input.length);
+ zos.putNextEntry(entry);
+ zos.write(input);
+ zos.closeEntry();
+ zos.close();
+ return baos.toByteArray();
+ }
+
+ public static byte[] unzip(byte[] zipped) {
+ ZipInputStream zipinputstream = null;
+ ByteArrayOutputStream outputStream = null;
+ try {
+ byte[] buf = new byte[1024];
+
+ zipinputstream = new ZipInputStream(new ByteArrayInputStream(zipped));
+ ZipEntry zipentry = zipinputstream.getNextEntry();
+ outputStream = new ByteArrayOutputStream();
+ int n;
+ while ((n = zipinputstream.read(buf, 0, 1024)) > -1) {
+ outputStream.write(buf, 0, n);
+ }
+
+ return outputStream.toByteArray();
+ } catch (Exception e) {
+ throw new IllegalStateException("Can't unzip input stream", e);
+ } finally {
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ log.debug("Failed to close output stream", e);
+ }
+ }
+ if (zipinputstream != null) {
+ try {
+ zipinputstream.closeEntry();
+ zipinputstream.close();
+ } catch (IOException e) {
+ log.debug("Failed to close zip input stream", e);
+ }
+ }
+ }
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/AbstractSdncException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/AbstractSdncException.java
new file mode 100644
index 0000000000..83e8321ea6
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/AbstractSdncException.java
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+import java.util.Arrays;
+import java.util.Formatter;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractSdncException {
+
+ private String messageId;
+
+ private String text;
+
+ private String[] variables;
+
+ private static Logger log = LoggerFactory.getLogger(AbstractSdncException.class.getName());
+
+ private final static Pattern ERROR_PARAM_PATTERN = Pattern.compile("%\\d");
+
+ public AbstractSdncException() {
+ }
+
+ public AbstractSdncException(String messageId, String text, String[] variables) {
+ super();
+ this.messageId = messageId;
+ this.text = text;
+ this.variables = validateParameters(messageId, text, variables);
+ }
+
+ private String[] validateParameters(String messageId, String text, String[] variables) {
+ String[] res = null;
+ Matcher m = ERROR_PARAM_PATTERN.matcher(text);
+ int expectedParamsNum = 0;
+ while (m.find()) {
+ expectedParamsNum += 1;
+ }
+ int actualParamsNum = (variables != null) ? variables.length : 0;
+ if (actualParamsNum < expectedParamsNum) {
+ log.warn(
+ "Received less parameters than expected for error with messageId {}, expected: {}, actual: {}. Missing parameters are padded with null values.",
+ messageId, expectedParamsNum, actualParamsNum);
+ } else if (actualParamsNum > expectedParamsNum) {
+ log.warn(
+ "Received more parameters than expected for error with messageId {}, expected: {}, actual: {}. Extra parameters are ignored.",
+ messageId, expectedParamsNum, actualParamsNum);
+ }
+ if (variables != null) {
+ res = Arrays.copyOf(variables, expectedParamsNum);
+ }
+
+ return res;
+ }
+
+ public String getMessageId() {
+ return this.messageId;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public String[] getVariables() {
+ return variables;
+ }
+
+ public void setMessageId(String messageId) {
+ this.messageId = messageId;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public void setVariables(String[] variables) {
+ this.variables = variables;
+ }
+
+ public String getFormattedErrorMessage() {
+ String res;
+ if (variables != null && variables.length > 0) {
+ Formatter formatter = new Formatter();
+ try {
+ res = formatter.format(this.text.replaceAll("%\\d", "%s"), (Object[]) this.variables).toString();
+ } finally {
+ formatter.close();
+ }
+ } else {
+ res = this.text;
+ }
+ return res;
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/AlreadyExistException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/AlreadyExistException.java
new file mode 100644
index 0000000000..3baec2067e
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/AlreadyExistException.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * Exception to be thrown when trying to insert an existing an element that
+ * already exists.
+ *
+ * @author luc boutier
+ */
+public class AlreadyExistException extends TechnicalException {
+ private static final long serialVersionUID = -6151150122897145634L;
+
+ /**
+ * Create a new {@link AlreadyExistException} with the cause.
+ *
+ * @param message
+ * Message.
+ */
+ public AlreadyExistException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteLastApplicationEnvironmentException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteLastApplicationEnvironmentException.java
new file mode 100644
index 0000000000..ec33b69d82
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteLastApplicationEnvironmentException.java
@@ -0,0 +1,34 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+public class DeleteLastApplicationEnvironmentException extends TechnicalException {
+
+ private static final long serialVersionUID = -5192834855057177252L;
+
+ public DeleteLastApplicationEnvironmentException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DeleteLastApplicationEnvironmentException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteReferencedObjectException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteReferencedObjectException.java
new file mode 100644
index 0000000000..6957e960c3
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/DeleteReferencedObjectException.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * This exception should be thrown when attempting to delete an object in Alien
+ * which is still referenced (used) by other object
+ *
+ * @author Minh Khang VU
+ */
+public class DeleteReferencedObjectException extends TechnicalException {
+ private static final long serialVersionUID = 1L;
+
+ public DeleteReferencedObjectException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/FunctionalException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/FunctionalException.java
new file mode 100644
index 0000000000..5bf67484d5
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/FunctionalException.java
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * All functional exception which is related to user input must go here. It's a
+ * checked exception to force error handling and checking.
+ *
+ * @author mkv
+ *
+ */
+public class FunctionalException extends Exception {
+
+ private static final long serialVersionUID = 6712845685798792493L;
+
+ public FunctionalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FunctionalException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/IndexingServiceException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/IndexingServiceException.java
new file mode 100644
index 0000000000..d8b8518919
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/IndexingServiceException.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * All errors happened while trying to access to index service
+ *
+ * @author mkv
+ */
+public class IndexingServiceException extends TechnicalException {
+
+ private static final long serialVersionUID = 8644422735660389058L;
+
+ public IndexingServiceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public IndexingServiceException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/InvalidArgumentException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/InvalidArgumentException.java
new file mode 100644
index 0000000000..ef6cd659e4
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/InvalidArgumentException.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+public class InvalidArgumentException extends TechnicalException {
+ private static final long serialVersionUID = 931646037604062840L;
+
+ public InvalidArgumentException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidArgumentException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/NotFoundException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/NotFoundException.java
new file mode 100644
index 0000000000..988325ff3e
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/NotFoundException.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+public class NotFoundException extends TechnicalException {
+
+ private static final long serialVersionUID = -5838741067731786413L;
+
+ public NotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/OkResponseInfo.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/OkResponseInfo.java
new file mode 100644
index 0000000000..78405e0eb1
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/OkResponseInfo.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+public class OkResponseInfo extends AbstractSdncException {
+
+ public OkResponseInfo(String messageId, String text, String[] variables) {
+ super(messageId, text, variables);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/PolicyException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/PolicyException.java
new file mode 100644
index 0000000000..8c5c8609b6
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/PolicyException.java
@@ -0,0 +1,31 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+public class PolicyException extends AbstractSdncException {
+
+ public PolicyException(String messageId, String text, String[] variables) {
+ super(messageId, text, variables);
+ }
+
+ public PolicyException() {
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java
new file mode 100644
index 0000000000..5ccbc25d0e
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java
@@ -0,0 +1,191 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * Nested POJOs to express required JSON format of the error
+ *
+ * { "requestError": { "serviceException": { "messageId": "", "text": "",
+ * "variables": [] } } }
+ *
+ *
+ * @author paharoni
+ *
+ */
+
+public class ResponseFormat {
+
+ private int status;
+ private RequestErrorWrapper requestErrorWrapper;
+
+ public ResponseFormat() {
+ super();
+ }
+
+ public ResponseFormat(int status) {
+ super();
+ this.status = status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public RequestErrorWrapper getRequestError() {
+ return requestErrorWrapper;
+ }
+
+ public void setRequestError(RequestErrorWrapper requestErrorWrapper) {
+ this.requestErrorWrapper = requestErrorWrapper;
+ }
+
+ public void setPolicyException(PolicyException policyException) {
+ this.requestErrorWrapper = new RequestErrorWrapper(new RequestError());
+ requestErrorWrapper.setPolicyException(policyException);
+ }
+
+ public void setServiceException(ServiceException serviceException) {
+ this.requestErrorWrapper = new RequestErrorWrapper(new RequestError());
+ requestErrorWrapper.setServiceException(serviceException);
+ }
+
+ public void setOkResponseInfo(OkResponseInfo okResponseInfo) {
+ this.requestErrorWrapper = new RequestErrorWrapper(new RequestError());
+ requestErrorWrapper.setOkResponseInfo(okResponseInfo);
+ }
+
+ public String getFormattedMessage() {
+ if (this.requestErrorWrapper.requestError.okResponseInfo != null) {
+ return this.requestErrorWrapper.requestError.okResponseInfo.getFormattedErrorMessage();
+ }
+ if (this.requestErrorWrapper.requestError.serviceException != null) {
+ return this.requestErrorWrapper.requestError.serviceException.getFormattedErrorMessage();
+ }
+ return this.requestErrorWrapper.requestError.policyException.getFormattedErrorMessage();
+ }
+
+ public String getText() {
+ if (this.requestErrorWrapper.requestError.okResponseInfo != null) {
+ return this.requestErrorWrapper.requestError.okResponseInfo.getText();
+ }
+ if (this.requestErrorWrapper.requestError.serviceException != null) {
+ return this.requestErrorWrapper.requestError.serviceException.getText();
+ }
+ return this.requestErrorWrapper.requestError.policyException.getText();
+ }
+
+ public String[] getVariables() {
+ if (this.requestErrorWrapper.requestError.okResponseInfo != null) {
+ return this.requestErrorWrapper.requestError.okResponseInfo.getVariables();
+ }
+ if (this.requestErrorWrapper.requestError.serviceException != null) {
+ return this.requestErrorWrapper.requestError.serviceException.getVariables();
+ }
+ return this.requestErrorWrapper.requestError.policyException.getVariables();
+ }
+
+ public String getMessageId() {
+ if (this.requestErrorWrapper.requestError.okResponseInfo != null) {
+ return this.requestErrorWrapper.requestError.okResponseInfo.getMessageId();
+ }
+ if (this.requestErrorWrapper.requestError.serviceException != null) {
+ return this.requestErrorWrapper.requestError.serviceException.getMessageId();
+ }
+ return this.requestErrorWrapper.requestError.policyException.getMessageId();
+ }
+
+ public class RequestErrorWrapper {
+ private RequestError requestError;
+
+ public RequestErrorWrapper() {
+ this.requestError = new RequestError();
+ }
+
+ public RequestErrorWrapper(RequestError requestError) {
+ this.requestError = requestError;
+ }
+
+ public RequestError getRequestError() {
+ return requestError;
+ }
+
+ public void setRequestError(RequestError requestError) {
+ this.requestError = requestError;
+ }
+
+ public void setPolicyException(PolicyException policyException) {
+ requestError.setPolicyException(policyException);
+ }
+
+ public void setServiceException(ServiceException serviceException) {
+ requestError.setServiceException(serviceException);
+ }
+
+ public void setOkResponseInfo(OkResponseInfo okResponseInfo) {
+ requestError.setOkResponseInfo(okResponseInfo);
+ }
+ }
+
+ public class RequestError {
+ @SuppressWarnings("unused")
+ private PolicyException policyException;
+ @SuppressWarnings("unused")
+ private ServiceException serviceException;
+ @SuppressWarnings("unused")
+ private OkResponseInfo okResponseInfo;
+
+ public RequestError() {
+ }
+
+ public PolicyException getPolicyException() {
+ return policyException;
+ }
+
+ public ServiceException getServiceException() {
+ return serviceException;
+ }
+
+ public OkResponseInfo getOkResponseInfo() {
+ return okResponseInfo;
+ }
+
+ public void setPolicyException(PolicyException policyException) {
+ this.policyException = policyException;
+ }
+
+ public void setServiceException(ServiceException serviceException) {
+ this.serviceException = serviceException;
+ }
+
+ public void setOkResponseInfo(OkResponseInfo okResponseInfo) {
+ this.okResponseInfo = okResponseInfo;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ResponseFormat[" + "status=" + status + ", requestErrorWrapper=" + requestErrorWrapper + ']';
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/ServiceException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/ServiceException.java
new file mode 100644
index 0000000000..3aae180517
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/ServiceException.java
@@ -0,0 +1,32 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+public class ServiceException extends AbstractSdncException {
+
+ public ServiceException(String messageId, String text, String[] variables) {
+ super(messageId, text, variables);
+ }
+
+ public ServiceException() {
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/TechnicalException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/TechnicalException.java
new file mode 100644
index 0000000000..96ef2f3159
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/TechnicalException.java
@@ -0,0 +1,40 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * Base class for all Alien technical exception
+ *
+ * @author mkv
+ *
+ */
+public abstract class TechnicalException extends RuntimeException {
+
+ private static final long serialVersionUID = -9152473183025390161L;
+
+ public TechnicalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TechnicalException(String message) {
+ super(message);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/VersionConflictException.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/VersionConflictException.java
new file mode 100644
index 0000000000..a998d6393e
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/VersionConflictException.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.exception;
+
+/**
+ * Exception happens when a4c cannot resolve version conflicts in a transparent
+ * manner for users
+ */
+public class VersionConflictException extends TechnicalException {
+ private static final long serialVersionUID = 1L;
+
+ public VersionConflictException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java
new file mode 100644
index 0000000000..89953d671e
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Configuration.java
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.config;
+
+import java.util.Date;
+import java.util.List;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+import static java.lang.String.format;
+
+public class Configuration extends BasicConfiguration {
+ /**
+ * fe FQDN
+ */
+ private String feFqdn;
+ /**
+ * backend host
+ */
+ private String beHost;
+ /**
+ * backend http port
+ */
+ private Integer beHttpPort;
+ /**
+ * backend http secured port
+ */
+ private Integer beSslPort;
+
+ private Integer healthCheckSocketTimeoutInMs;
+
+ private Integer healthCheckIntervalInSeconds;
+
+ private FeMonitoringConfig systemMonitoring;
+
+ private String kibanaHost;
+
+ private Integer kibanaPort;
+
+ private String kibanaProtocol;
+
+ private String onboardingForwardContext;
+
+ public String getKibanaProtocol() {
+ return kibanaProtocol;
+ }
+
+ public void setKibanaProtocol(String kibanaProtocol) {
+ this.kibanaProtocol = kibanaProtocol;
+ }
+
+ public String getKibanaHost() {
+ return kibanaHost;
+ }
+
+ public void setKibanaHost(String kibanaHost) {
+ this.kibanaHost = kibanaHost;
+ }
+
+ public Integer getKibanaPort() {
+ return kibanaPort;
+ }
+
+ public void setKibanaPort(Integer kibanaPort) {
+ this.kibanaPort = kibanaPort;
+ }
+
+ public FeMonitoringConfig getSystemMonitoring() {
+ return systemMonitoring;
+ }
+
+ public void setSystemMonitoring(FeMonitoringConfig systemMonitoring) {
+ this.systemMonitoring = systemMonitoring;
+ }
+
+ public Integer getHealthCheckSocketTimeoutInMs() {
+ return healthCheckSocketTimeoutInMs;
+ }
+
+ public Integer getHealthCheckSocketTimeoutInMs(int defaultVal) {
+ return healthCheckSocketTimeoutInMs == null ? defaultVal : healthCheckSocketTimeoutInMs;
+ }
+
+ public void setHealthCheckSocketTimeoutInMs(Integer healthCheckSocketTimeout) {
+ this.healthCheckSocketTimeoutInMs = healthCheckSocketTimeout;
+ }
+
+ public Integer getHealthCheckIntervalInSeconds() {
+ return healthCheckIntervalInSeconds;
+ }
+
+ public Integer getHealthCheckIntervalInSeconds(int defaultVal) {
+ return healthCheckIntervalInSeconds == null ? defaultVal : healthCheckIntervalInSeconds;
+ }
+
+ public void setHealthCheckIntervalInSeconds(Integer healthCheckInterval) {
+ this.healthCheckIntervalInSeconds = healthCheckInterval;
+ }
+
+ /**
+ * be http context
+ */
+ private String beContext;
+ /**
+ * backend protocol. http | https
+ */
+ private String beProtocol = "http";
+
+ private Date released;
+ private String version = "1111";
+ private Connection connection;
+ private List<String> protocols;
+
+ private int threadpoolSize;
+ private int requestTimeout;
+
+ private List<List<String>> identificationHeaderFields;
+ private List<List<String>> optionalHeaderFields;
+
+ public Date getReleased() {
+ return released;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setReleased(Date released) {
+ this.released = released;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Connection getConnection() {
+ return connection;
+ }
+
+ public void setConnection(Connection connection) {
+ this.connection = connection;
+ }
+
+ public List<String> getProtocols() {
+ return protocols;
+ }
+
+ public void setProtocols(List<String> protocols) {
+ this.protocols = protocols;
+ }
+
+ public String getBeHost() {
+ return beHost;
+ }
+
+ public void setBeHost(String beHost) {
+ this.beHost = beHost;
+ }
+
+ public Integer getBeHttpPort() {
+ return beHttpPort;
+ }
+
+ public void setBeHttpPort(Integer beHttpPort) {
+ this.beHttpPort = beHttpPort;
+ }
+
+ public Integer getBeSslPort() {
+ return beSslPort;
+ }
+
+ public void setBeSslPort(Integer beSslPort) {
+ this.beSslPort = beSslPort;
+ }
+
+ public String getBeContext() {
+ return beContext;
+ }
+
+ public void setBeContext(String beContext) {
+ this.beContext = beContext;
+ }
+
+ public String getBeProtocol() {
+ return beProtocol;
+ }
+
+ public void setBeProtocol(String beProtocol) {
+ this.beProtocol = beProtocol;
+ }
+
+ public int getThreadpoolSize() {
+ return threadpoolSize;
+ }
+
+ public void setThreadpoolSize(int threadpoolSize) {
+ this.threadpoolSize = threadpoolSize;
+ }
+
+ public int getRequestTimeout() {
+ return requestTimeout;
+ }
+
+ public void setRequestTimeout(int requestTimeout) {
+ this.requestTimeout = requestTimeout;
+ }
+
+ public List<List<String>> getIdentificationHeaderFields() {
+ return identificationHeaderFields;
+ }
+
+ public void setIdentificationHeaderFields(List<List<String>> identificationHeaderFields) {
+ this.identificationHeaderFields = identificationHeaderFields;
+ }
+
+ public List<List<String>> getOptionalHeaderFields() {
+ return optionalHeaderFields;
+ }
+
+ public void setOptionalHeaderFields(List<List<String>> optionalHeaderFields) {
+ this.optionalHeaderFields = optionalHeaderFields;
+ }
+
+ public String getFeFqdn() {
+ return feFqdn;
+ }
+
+ public void setFeFqdn(String feFqdn) {
+ this.feFqdn = feFqdn;
+ }
+
+ public static class FeMonitoringConfig {
+
+ Boolean enabled;
+ Boolean isProxy;
+ Integer probeIntervalInSeconds;
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Boolean getIsProxy() {
+ return isProxy;
+ }
+
+ public void setIsProxy(Boolean isProxy) {
+ this.isProxy = isProxy;
+ }
+
+ public Integer getProbeIntervalInSeconds() {
+ return probeIntervalInSeconds;
+ }
+
+ public Integer getProbeIntervalInSeconds(int defaultVal) {
+ return probeIntervalInSeconds == null ? defaultVal : probeIntervalInSeconds;
+ }
+
+ public void setProbeIntervalInSeconds(Integer probeIntervalInSeconds) {
+ this.probeIntervalInSeconds = probeIntervalInSeconds;
+ }
+ }
+
+ public String getOnboardingForwardContext() {
+ return onboardingForwardContext;
+ }
+
+ public void setOnboardingForwardContext(String onboardingForwardContext) {
+ this.onboardingForwardContext = onboardingForwardContext;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append(format("backend host: %s%n", beHost))
+ .append(format("backend http port: %s%n", beHttpPort))
+ .append(format("backend ssl port: %s%n", beSslPort)).append(format("backend context: %s%n", beContext))
+ .append(format("backend protocol: %s%n", beProtocol))
+ .append(format("onboarding forward context: %s%n", onboardingForwardContext))
+ .append(format("Version: %s%n", version)).append(format("Released: %s%n", released))
+ .append(format("Connecting to database: %s%n", connection))
+ .append(format("Supported protocols: %s%n", protocols)).toString();
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/fe/config/ConfigurationManager.java b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/ConfigurationManager.java
new file mode 100644
index 0000000000..b3164fd283
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/ConfigurationManager.java
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.FileChangeCallback;
+import org.openecomp.sdc.common.config.EcompErrorConfiguration;
+import org.openecomp.sdc.common.config.IEcompConfigurationManager;
+import org.openecomp.sdc.common.rest.api.RestConfigurationInfo;
+
+public class ConfigurationManager implements FileChangeCallback, IEcompConfigurationManager {
+
+ ConfigurationSource configurationSource = null;
+ private static ConfigurationManager instance;
+
+ public ConfigurationManager(ConfigurationSource configurationSource) {
+ super();
+ this.configurationSource = configurationSource;
+ loadConfigurationFiles();
+ instance = this;
+ }
+
+ Map<String, Object> configurations = new HashMap<String, Object>();
+
+ private void loadConfigurationFiles() {
+
+ loadConfigurationClass(Configuration.class);
+ loadConfigurationClass(RestConfigurationInfo.class);
+ loadConfigurationClass(EcompErrorConfiguration.class);
+
+ }
+
+ private <T extends BasicConfiguration> void loadConfigurationClass(Class<T> clazz) {
+ ConfigurationListener configurationListener = new ConfigurationListener(clazz, this);
+
+ T object = configurationSource.getAndWatchConfiguration(clazz, configurationListener);
+
+ configurations.put(getKey(clazz), object);
+ }
+
+ private <T> String getKey(Class<T> class1) {
+
+ return class1.getSimpleName();
+
+ }
+
+ public Configuration getConfiguration() {
+
+ return (Configuration) configurations.get(getKey(Configuration.class));
+
+ }
+
+ public RestConfigurationInfo getRestClientConfiguration() {
+
+ return (RestConfigurationInfo) configurations.get(getKey(RestConfigurationInfo.class));
+
+ }
+
+ @Override
+ public EcompErrorConfiguration getEcompErrorConfiguration() {
+
+ return (EcompErrorConfiguration) configurations.get(getKey(EcompErrorConfiguration.class));
+
+ }
+
+ public Configuration getConfigurationAndWatch(ConfigurationListener configurationListener) {
+
+ if (configurationListener != null) {
+
+ configurationSource.addWatchConfiguration(Configuration.class, configurationListener);
+
+ }
+ return (Configuration) configurations.get(getKey(Configuration.class));
+
+ }
+
+ public void reconfigure(BasicConfiguration obj) {
+ //
+ // if (obj != null) {
+ //
+ // if (obj instanceof Configuration) {
+ // configurations.put(getKey(Configuration.class), obj);
+ // }
+ //
+ // if (obj instanceof EcompErrorConfiguration) {
+ // configurations.put(getKey(EcompErrorConfiguration.class), obj);
+ // }
+ // }
+ }
+
+ public static ConfigurationManager getConfigurationManager() {
+ return instance;
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Connection.java b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Connection.java
new file mode 100644
index 0000000000..7208398525
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/Connection.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.config;
+
+public class Connection {
+
+ private String url;
+ private int poolSize;
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public int getPoolSize() {
+ return poolSize;
+ }
+
+ public void setPoolSize(int poolSize) {
+ this.poolSize = poolSize;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("'%s' with pool of %d", getUrl(), getPoolSize());
+ }
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/fe/config/FeEcompErrorManager.java b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/FeEcompErrorManager.java
new file mode 100644
index 0000000000..6733e179ba
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/fe/config/FeEcompErrorManager.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.config;
+
+import org.openecomp.sdc.common.config.AbsEcompErrorManager;
+import org.openecomp.sdc.common.config.EcompErrorEnum;
+import org.openecomp.sdc.common.config.IEcompConfigurationManager;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+
+public class FeEcompErrorManager extends AbsEcompErrorManager {
+
+ private static volatile FeEcompErrorManager instance;
+ private static ConfigurationManager configurationManager;
+
+ private FeEcompErrorManager() {
+ };
+
+ public static FeEcompErrorManager getInstance() {
+ if (instance == null) {
+
+ instance = init();
+ }
+ return instance;
+ }
+
+ private static synchronized FeEcompErrorManager init() {
+ if (instance == null) {
+ instance = new FeEcompErrorManager();
+ configurationManager = ConfigurationManager.getConfigurationManager();
+ }
+ return instance;
+ }
+
+ @Override
+ public IEcompConfigurationManager getConfigurationManager() {
+ return configurationManager;
+ }
+
+ public void logFeHealthCheckRecovery(String context) {
+ processEcompError(context, EcompErrorEnum.FeHealthCheckRecovery);
+ }
+
+ public void logFeHealthCheckError(String context) {
+ processEcompError(context, EcompErrorEnum.FeHealthCheckError);
+ }
+
+ public void logFeHttpLoggingError(String context) {
+ processEcompError(context, EcompErrorEnum.FeHttpLoggingError);
+ }
+
+ public void logFePortalServletError(String context) {
+ processEcompError(context, EcompErrorEnum.FePortalServletError);
+ }
+
+ public void logFeHealthCheckGeneralError(String context) {
+ processEcompError(context, EcompErrorEnum.FeHealthCheckGeneralError);
+ }
+
+}
diff --git a/common-app-api/src/main/java/org/openecomp/sdc/fe/monitoring/FeMonitoringService.java b/common-app-api/src/main/java/org/openecomp/sdc/fe/monitoring/FeMonitoringService.java
new file mode 100644
index 0000000000..482be7697d
--- /dev/null
+++ b/common-app-api/src/main/java/org/openecomp/sdc/fe/monitoring/FeMonitoringService.java
@@ -0,0 +1,140 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.fe.monitoring;
+
+import java.io.IOException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.monitoring.MonitoringEvent;
+import org.openecomp.sdc.common.monitoring.MonitoringMetricsFetcher;
+import org.openecomp.sdc.fe.config.Configuration;
+import org.openecomp.sdc.fe.config.ConfigurationManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class FeMonitoringService {
+
+ private static final String URL = "%s://%s:%s/sdc2/rest/monitoring";
+ private static Logger monitoringLogger = LoggerFactory.getLogger("asdc.fe.monitoring.service");
+ private static Logger log = LoggerFactory.getLogger(FeMonitoringService.class.getName());
+ private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ private class MonitoringScheduledTask implements Runnable {
+ @Override
+ public void run() {
+ monitoringLogger.trace("Executing FE Monitoring Task - Start");
+ MonitoringEvent monitoringMetrics = MonitoringMetricsFetcher.getInstance().getMonitoringMetrics();
+ processMonitoringEvent(monitoringMetrics);
+ monitoringLogger.trace("Executing FE Monitoring Task - Status = {}", monitoringMetrics.toString());
+ }
+ }
+
+ /**
+ * This executor will execute the Monitoring task.
+ */
+ ScheduledExecutorService monitoringExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "FE-Monitoring-Thread");
+ }
+ });
+ private ServletContext context;
+
+ public FeMonitoringService(ServletContext context) {
+ this.context = context;
+ }
+
+ public void start(int interval) {
+ Configuration config = ((ConfigurationManager) context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR))
+ .getConfiguration();
+ if (config.getSystemMonitoring().getEnabled()) {
+ log.info("FE monitoring service enabled, interval is {} seconds", interval);
+ this.monitoringExecutor.scheduleAtFixedRate(new MonitoringScheduledTask(), 0, interval, TimeUnit.SECONDS);
+ } else {
+ log.info("FE monitoring service is disabled");
+ }
+ }
+
+ private void processMonitoringEvent(MonitoringEvent monitoringMetrics) {
+ CloseableHttpClient httpClient = null;
+ try {
+ Configuration config = ((ConfigurationManager) context.getAttribute(Constants.CONFIGURATION_MANAGER_ATTR))
+ .getConfiguration();
+ String redirectedUrl = String.format(URL, config.getBeProtocol(), config.getBeHost(),
+ config.getBeHttpPort());
+ httpClient = getHttpClient(config);
+ HttpPost httpPost = new HttpPost(redirectedUrl);
+ String monitoringMetricsJson = gson.toJson(monitoringMetrics);
+ HttpEntity myEntity = new StringEntity(monitoringMetricsJson);
+ httpPost.setEntity(myEntity);
+ httpPost.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+ int beResponseStatus;
+ CloseableHttpResponse beResponse;
+ try {
+ beResponse = httpClient.execute(httpPost);
+ beResponseStatus = beResponse.getStatusLine().getStatusCode();
+ if (beResponseStatus != HttpStatus.SC_OK) {
+ monitoringLogger.error("Unexpected HTTP response from BE : {}", beResponseStatus);
+ }
+ } catch (Exception e) {
+ monitoringLogger.error("Monitoring error when trying to connect to BE", e);
+ }
+ } catch (Exception e) {
+ monitoringLogger.error("Unexpected monitoring error", e);
+ } finally {
+ if (httpClient != null) {
+ try {
+ httpClient.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private CloseableHttpClient getHttpClient(Configuration config) {
+ int timeout = 3000;
+ RequestConfig.Builder requestBuilder = RequestConfig.custom();
+ requestBuilder.setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout);
+
+ HttpClientBuilder builder = HttpClientBuilder.create();
+ builder.setDefaultRequestConfig(requestBuilder.build());
+ return builder.build();
+ }
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/data_structure/CapListTest.java b/common-app-api/src/test/java/org/openecomp/sdc/common/data_structure/CapListTest.java
new file mode 100644
index 0000000000..7aeb2ac70d
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/data_structure/CapListTest.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.data_structure;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Test;
+import org.openecomp.sdc.common.datastructure.CapList;
+
+public class CapListTest {
+ public enum LIST_ACTION {
+ Add, Remove, Size, Get
+ }
+
+ @Test
+ public void testCap() {
+ List<Integer> testList = new CapList<>(10);
+ for (int i = 0; i < 100; i++) {
+ testList.add(i);
+ }
+ assertTrue(testList.size() == 10);
+ for (int i = 0; i < testList.size(); i++) {
+ assertTrue(testList.get(i) == (i + 90));
+ }
+ }
+
+ @Test
+ public void testThreadSafe() {
+ List<Integer> testList = new CapList<>(1000);
+
+ ExecutorService executor = Executors.newFixedThreadPool(4);
+ for (int i = 0; i < 10; i++) {
+ Runnable worker;
+ // 0 - 4
+ if (i < 5) {
+ worker = new ThreadWorker(i, LIST_ACTION.Add, testList);
+ }
+ // 5, 8
+ else if (i == 5 || i == 8) {
+ worker = new ThreadWorker(i, LIST_ACTION.Remove, testList);
+ }
+ // 6
+ else if (i == 6) {
+ worker = new ThreadWorker(i, LIST_ACTION.Size, testList);
+ }
+ // 7, 9
+ else {
+ worker = new ThreadWorker(i, LIST_ACTION.Get, testList);
+ }
+ executor.execute(worker);
+ }
+ executor.shutdown();
+ while (!executor.isTerminated()) {
+ }
+ assertTrue(testList.size() == 60);
+ }
+
+ public static class ThreadWorker implements Runnable {
+ private LIST_ACTION action;
+ private List<Integer> list;
+ private Integer id;
+
+ ThreadWorker(Integer id, LIST_ACTION action, List<Integer> list) {
+ this.action = action;
+ this.list = list;
+ this.id = id;
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < 20; i++) {
+ threadNap();
+ switch (action) {
+ case Add:
+ list.add(id * 100 + i);
+ break;
+ case Remove: {
+ int index = (int) (Math.random() * 10);
+ list.remove(index);
+ break;
+ }
+ case Get:
+ int index = (int) (Math.random() * 10);
+ Integer integer = list.get(index);
+
+ break;
+ case Size:
+ int size = list.size();
+ break;
+ }
+ }
+
+ }
+
+ private void threadNap() {
+ long napTime = (long) (Math.random() * 100);
+ try {
+ Thread.sleep(napTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/test/CommonUtilsTest.java b/common-app-api/src/test/java/org/openecomp/sdc/common/test/CommonUtilsTest.java
new file mode 100644
index 0000000000..1a062576dc
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/test/CommonUtilsTest.java
@@ -0,0 +1,549 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
+import org.junit.Test;
+import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.GeneralUtility;
+import org.openecomp.sdc.common.util.HtmlCleaner;
+import org.openecomp.sdc.common.util.ValidationUtils;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.html.HTMLStyleElement;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class CommonUtilsTest {
+ private static Logger log = LoggerFactory.getLogger(CommonUtilsTest.class.getName());
+
+ /*
+ * Validation utils start
+ */
+ @Test
+ public void testValidateServiceName() {
+
+ assertTrue(ValidationUtils.validateComponentNamePattern("1111222"));
+ assertTrue(ValidationUtils.validateComponentNamePattern("sfE4444"));
+ assertTrue(ValidationUtils.validateComponentNamePattern("1111sfd222"));
+ assertTrue(ValidationUtils.validateComponentNamePattern("11sdf 1124_22"));
+ assertTrue(ValidationUtils.validateComponentNamePattern("111----1222"));
+ assertTrue(ValidationUtils.validateComponentNamePattern("1111f .222"));
+ assertTrue(ValidationUtils.validateComponentNamePattern("1111222"));
+ assertFalse(ValidationUtils.validateComponentNamePattern("11!11222"));
+ assertFalse(ValidationUtils.validateComponentNamePattern("111|`1222"));
+
+ }
+
+ @Test
+ public void validateServiceNameLengthTest() {
+ assertTrue(ValidationUtils.validateComponentNameLength("fsdlfsdlksdsd;"));
+ // assertFalse(ValidationUtils.validateComponentNameLength("ddddddddddddddddddddddsdfsddddddddddddddddddddddsdfs"));
+ }
+
+ @Test
+ public void testValidateIcon() {
+
+ assertTrue(ValidationUtils.validateIcon("something"));
+ assertTrue(ValidationUtils.validateIcon("sfE4444"));
+ assertTrue(ValidationUtils.validateIcon("1111sfd222"));
+ assertTrue(ValidationUtils.validateIcon("11sdf1124_22"));
+ assertTrue(ValidationUtils.validateIcon("111----1222"));
+ assertFalse(ValidationUtils.validateIcon("1111f.222"));
+ assertTrue(ValidationUtils.validateIcon("1111222"));
+ assertFalse(ValidationUtils.validateIcon("1111 222"));
+ assertFalse(ValidationUtils.validateIcon("11!11222"));
+ assertFalse(ValidationUtils.validateIcon("111|`1222"));
+
+ }
+
+ @Test
+ public void testFloatParsing() {
+ assertTrue(ValidationUtils.isFloatNumber("15555.45"));
+ assertTrue(ValidationUtils.isFloatNumber("0.5"));
+ assertFalse(ValidationUtils.isFloatNumber("15555"));
+ assertFalse(ValidationUtils.isFloatNumber("1"));
+ assertFalse(ValidationUtils.isFloatNumber("jk532"));
+ assertFalse(ValidationUtils.isFloatNumber("12..6"));
+
+ }
+
+ @Test
+ public void testValidateIconLength() {
+ assertTrue(ValidationUtils.validateIconLength("fsdlfsdlksdsd"));
+ assertFalse(ValidationUtils.validateIconLength("ddddddddddddddddddddddsdfsddddddddddddddddddddddsdfs"));
+ }
+
+ // 1610OS Support - Because of changes in the validation in the ui this test needs to be fixed
+// @Test
+// public void testValidateProjectCode() {
+//
+// assertTrue(ValidationUtils.validateProjectCode("15555"));
+// assertTrue(ValidationUtils.validateProjectCode("12434501"));
+// assertTrue(ValidationUtils.validateProjectCode("00000"));
+// assertFalse(ValidationUtils.validateProjectCode("something"));
+// assertFalse(ValidationUtils.validateProjectCode("som ething"));
+// assertFalse(ValidationUtils.validateProjectCode("3255 656"));
+// assertFalse(ValidationUtils.validateProjectCode("43535t636"));
+// assertFalse(ValidationUtils.validateProjectCode("098&656"));
+// }
+
+ @Test
+ public void testValidateProjectCodeLength() {
+ assertTrue(ValidationUtils.validateProjectCodeLegth("00000"));
+ assertFalse(ValidationUtils.validateProjectCodeLegth("ddddddddddddddddddddddsdfsddddddddddddddddddddddsdfs"));
+ }
+
+ // 1610OS Support - Because of changes in the validation in the ui this test needs to be fixed
+// @Test
+// public void testValidateContactId() {
+//
+// assertTrue(ValidationUtils.validateContactId("ml7889"));
+// assertTrue(ValidationUtils.validateContactId("Ml7889"));
+// assertTrue(ValidationUtils.validateContactId("ml788r"));
+// assertFalse(ValidationUtils.validateContactId("something"));
+// assertFalse(ValidationUtils.validateContactId("mlk111"));
+// assertFalse(ValidationUtils.validateContactId("12ml89"));
+// assertFalse(ValidationUtils.validateContactId("!!78900"));
+// }
+
+ @Test
+ public void testRemoveHtml() {
+
+ assertTrue("gooboo".equals(ValidationUtils.removeHtmlTags("<b>goo<b></b></b><b>boo</b>")));
+ assertTrue("goo&lt;boo".equals(ValidationUtils.removeHtmlTags("<b>goo<b></b><</b><b>boo</b>")));
+ assertTrue("goo boo".equals(ValidationUtils.removeHtmlTags("goo boo")));
+ assertTrue("goo# . boo12".equals(ValidationUtils.removeHtmlTags("goo# . boo12")));
+ }
+
+ @Test
+ public void testnormaliseWhitespace() {
+
+ assertTrue("goo boo".equals(ValidationUtils.normaliseWhitespace("goo boo")));
+ assertTrue("goo boo ".equals(ValidationUtils.normaliseWhitespace("goo boo ")));
+ assertTrue("goo boo".equals(ValidationUtils.normaliseWhitespace("goo boo")));
+ }
+
+ @Test
+ public void teststripOctets() {
+ assertTrue("goo boo".equals(ValidationUtils.stripOctets("goo%1F boo")));
+ assertTrue("goo boo ".equals(ValidationUtils.stripOctets("goo boo %1F")));
+ assertTrue("goo boo".equals(ValidationUtils.stripOctets("%1Fgoo boo")));
+ }
+
+ @Test
+ public void testRemoveNoneUtf8Chars() {
+ assertTrue("goo boo".equals(ValidationUtils.removeNoneUtf8Chars("goo boo")));
+ assertTrue("goo boo!!._".equals(ValidationUtils.removeNoneUtf8Chars("goo boo!!._")));
+ assertTrue("goo boo".equals(ValidationUtils.removeNoneUtf8Chars("goo boo")));
+ assertTrue("goo bo123o".equals(ValidationUtils.removeNoneUtf8Chars("goo bo123o")));
+ assertTrue("goo bo123o".equals(ValidationUtils.removeNoneUtf8Chars("goo קקbo123oגכקק")));
+ assertTrue("goo bo123o".equals(ValidationUtils.removeNoneUtf8Chars("goo bo1������23o")));
+ }
+
+ @Test
+ public void validateEnglishTest() {
+ assertTrue(ValidationUtils.validateIsEnglish("ml7889"));
+ assertFalse(ValidationUtils.validateIsEnglish("ml7889קר"));
+ assertFalse(ValidationUtils.validateIsEnglish("ml7889æ–‡"));
+ }
+
+ @Test
+ public void removeDuplicateFromListTest() {
+ List<String> tagsBefore = new ArrayList<>();
+ tagsBefore.add("tag1");
+ tagsBefore.add("tag7");
+ tagsBefore.add("tag3");
+ tagsBefore.add("tag4");
+ tagsBefore.add("tag1");
+
+ List<String> tagsAfter = new ArrayList<>();
+ tagsAfter.add("tag1");
+ tagsAfter.add("tag7");
+ tagsAfter.add("tag3");
+ tagsAfter.add("tag4");
+ assertTrue(tagsAfter.containsAll(ValidationUtils.removeDuplicateFromList(tagsBefore)));
+ tagsBefore = new ArrayList<>();
+ tagsBefore.add("tag1");
+ tagsBefore.add("tag7");
+ tagsBefore.add("tag3");
+ tagsBefore.add("tag4");
+ tagsBefore.add("Tag1");
+
+ tagsAfter = new ArrayList<>();
+ tagsAfter.add("tag1");
+ tagsAfter.add("tag7");
+ tagsAfter.add("tag3");
+ tagsAfter.add("tag4");
+ tagsAfter.add("Tag1");
+ assertTrue(tagsAfter.containsAll(ValidationUtils.removeDuplicateFromList(tagsBefore)));
+ }
+
+ @Test
+ public void validateTagLengthTest() {
+ assertTrue(ValidationUtils.validateTagLength("fsdlfsdlkfjkljsdf"));
+ // assertFalse(ValidationUtils.validateTagLength("ddddddddddddddddddddddsdfsddddddddddddddddddddddsdfs"));
+
+ }
+
+ @Test
+ public void validateTagListLengthTest() {
+ assertTrue(ValidationUtils.validateTagListLength("fsdlfsdlkfjkljsdf,dsfsdfsdf".length()));
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i <= 1024; i++) {
+ sb.append("a");
+ }
+ assertFalse(ValidationUtils.validateTagListLength(sb.toString().length()));
+
+ }
+
+ @Test
+ public void validateDescriptionLengthTest() {
+ assertTrue(ValidationUtils.validateDescriptionLength("fsdlfsdlkfjkljsddgfgdfgdfgdfgff"));
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i <= 1024; i++) {
+ sb.append("a");
+ }
+ assertFalse(ValidationUtils.validateDescriptionLength(sb.toString()));
+
+ }
+
+ @Test
+ public void validateStringNotEmptyTest() {
+ assertTrue(ValidationUtils.validateStringNotEmpty("fsdlfsdlk"));
+ assertFalse(ValidationUtils.validateStringNotEmpty(""));
+ assertFalse(ValidationUtils.validateStringNotEmpty(" "));
+ assertFalse(ValidationUtils.validateStringNotEmpty(" "));
+ }
+
+ @Test
+ public void validateVendorNameTest() {
+ assertTrue(ValidationUtils.validateVendorName("fsdlfsdlk"));
+ assertTrue(ValidationUtils.validateVendorName("fsdlfsdlk.sdsd;"));
+ assertFalse(ValidationUtils.validateVendorName("sadf:"));
+ assertFalse(ValidationUtils.validateVendorName("sadf/"));
+ assertFalse(ValidationUtils.validateVendorName("sadf?"));
+ }
+
+ @Test
+ public void validateVendorNameLengthTest() {
+ assertTrue(ValidationUtils.validateVendorNameLength("fsdlfsdlk.sdsd;"));
+ assertFalse(ValidationUtils.validateVendorNameLength("ddddddddddddddddddddddsdfs"));
+ }
+
+ @Test
+ public void validateVendorReleaseTest() {
+ assertTrue(ValidationUtils.validateVendorRelease("fsdlfsdlk"));
+ assertTrue(ValidationUtils.validateVendorRelease("fsdlfsdlk.sdsd;"));
+ assertFalse(ValidationUtils.validateVendorRelease("sadf:"));
+ assertFalse(ValidationUtils.validateVendorRelease("sadf/"));
+ assertFalse(ValidationUtils.validateVendorRelease("sadf?"));
+ }
+
+ @Test
+ public void validateVendorReleaseLengthTest() {
+ assertTrue(ValidationUtils.validateVendorNameLength("fsdlfsdlk.sdsd;"));
+ assertFalse(ValidationUtils.validateVendorNameLength("ddddddddddddddddddddddsdfs"));
+ }
+
+ @Test
+ public void hasBeenCertifiedTest() {
+ assertTrue(ValidationUtils.hasBeenCertified("1.2"));
+ assertTrue(ValidationUtils.hasBeenCertified("2.2"));
+ assertTrue(ValidationUtils.hasBeenCertified("1.0"));
+ assertFalse(ValidationUtils.hasBeenCertified("0.1"));
+
+ }
+
+ @Test
+ public void normalizedNameTest() {
+ String input = "MyNewSysName";
+ String outputNorm = ValidationUtils.normaliseComponentName(input);
+ String outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "My New Sys Name";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "My.New-Sys_Name";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "My..New-Sys_Name";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "My.New--sys_NAme";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "Layer 3 Connectivity";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "Layer 3 VPN";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "Layer-3 Connectivity";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ input = "IP-connectivity";
+ outputNorm = ValidationUtils.normaliseComponentName(input);
+ outputSys = ValidationUtils.convertToSystemName(input);
+ log.debug("{} <> {} <> {}", input, outputNorm, outputSys);
+
+ }
+
+ @Test
+ public void normalizeFileNameTest() {
+ assertTrue("too.jpeg".equals(ValidationUtils.normalizeFileName("too.jpeg")));
+ assertTrue("too..jpeg".equals(ValidationUtils.normalizeFileName("too..jpeg")));
+ assertTrue("too..jpeg".equals(ValidationUtils.normalizeFileName("t*o:o..jpe<>g")));
+ assertTrue("goo.too..jpeg".equals(ValidationUtils.normalizeFileName("goo.t*o:o..jpe<>g")));
+ assertTrue("goo.too..jpeg".equals(ValidationUtils.normalizeFileName(" goo.t*o:o..jpe<>g ")));
+ assertTrue("goo-too-mo.jpeg".equals(ValidationUtils.normalizeFileName("goo too----mo.jpeg")));
+ assertTrue("goo-too-mo.jpeg".equals(ValidationUtils.normalizeFileName(".\\..\\goo too----mo.jpeg")));
+ assertTrue("goo-too-mo.jpeg".equals(ValidationUtils.normalizeFileName("__--goo too----mo.jpeg--__")));
+ assertTrue("goo-too-mo.jpeg".equals(ValidationUtils.normalizeFileName("_ -goo too----mo.jpeg _-- _-")));
+
+ }
+
+ @Test
+ public void validateUrlTest() {
+ assertTrue(ValidationUtils.validateUrl("http://google.co.il/"));
+ assertTrue(ValidationUtils.validateUrl("https://google.co.il/"));
+ assertTrue(ValidationUtils.validateUrl("https://google.co.il/go/go"));
+ assertTrue(ValidationUtils.validateUrl("https://google.co.il/go/go"));
+ assertTrue(ValidationUtils.validateUrl("http://google.co.il/go/go"));
+ assertFalse(ValidationUtils.validateUrl("google.co.il/go/go"));
+ assertFalse(ValidationUtils.validateUrl("https://google.co.il/go/go!"));
+ assertFalse(ValidationUtils.validateUrl("https://g;oogle.co.il/go/go"));
+
+ }
+
+ @Test
+ public void normalizeArtifactLabel() {
+ assertEquals(ValidationUtils.normalizeArtifactLabel("Test--3 134++"), "test3134");
+ }
+
+ @Test
+ public void cleanArtifactLabel() {
+ assertEquals(ValidationUtils.cleanArtifactDisplayName("Test--3 134++"), "Test-3 134+");
+ }
+
+ @Test
+ public void validateArtifactLabel() {
+ assertTrue(ValidationUtils.validateArtifactLabel("dsflkjsdf345JKL"));
+ assertTrue(ValidationUtils.validateArtifactLabel("dsfsd lkj "));
+ assertTrue(ValidationUtils.validateArtifactLabel("sdfdsf---+"));
+ assertTrue(ValidationUtils.validateArtifactLabel(" - +"));
+ assertFalse(ValidationUtils.validateArtifactLabel("sfsdfhkj111="));
+ assertFalse(ValidationUtils.validateArtifactLabel("sfsdfhkj111=dfsf%"));
+ assertFalse(ValidationUtils.validateArtifactLabel("sdfsdfljghgklsdg908*"));
+
+ }
+
+ @Test
+ public void validateConsumerNameTest() {
+ assertTrue(ValidationUtils.validateConsumerName("ab037cd"));
+ assertFalse(ValidationUtils.validateConsumerName(" "));
+ assertTrue(ValidationUtils.validateConsumerName("_dD.d9"));
+ assertTrue(ValidationUtils.validateConsumerName("_dd.G9-"));
+ assertFalse(ValidationUtils.validateConsumerName(".dA.d9-"));
+ assertFalse(ValidationUtils.validateConsumerName("-d"));
+ assertFalse(ValidationUtils.validateConsumerName("d?"));
+ assertTrue(ValidationUtils.validateConsumerName("9"));
+ }
+
+ @Test
+ public void validateConsumerPassSaltTest() {
+ assertTrue(ValidationUtils.validateConsumerPassSalt("ad35fg2"));
+ assertTrue(ValidationUtils.validateConsumerPassSalt("12s"));
+ assertTrue(ValidationUtils.validateConsumerPassSalt("9"));
+ assertFalse(ValidationUtils.validateConsumerPassSalt("dA.d9-"));
+ assertFalse(ValidationUtils.validateConsumerPassSalt("dASQe"));
+ assertFalse(ValidationUtils.validateConsumerPassSalt("_d"));
+ assertFalse(ValidationUtils.validateConsumerPassSalt("?"));
+ assertFalse(ValidationUtils.validateConsumerPassSalt(""));
+ assertFalse(ValidationUtils.validateConsumerPassSalt(" "));
+ }
+
+ @Test
+ public void validateCategoryNameFormatTest() {
+ assertTrue(ValidationUtils.validateCategoryDisplayNameFormat("Net ele-2_3#456&+.'=:@@@@@#####"));
+ // this will fail at length
+ assertTrue(ValidationUtils.validateCategoryDisplayNameFormat(null));
+ // * is not allowed
+ assertFalse(ValidationUtils.validateCategoryDisplayNameFormat("Net ele-2_3#456&*+.'=:@"));
+ assertFalse(ValidationUtils.validateCategoryDisplayNameFormat(""));
+ // should start with alphanumeric
+ assertFalse(ValidationUtils.validateCategoryDisplayNameFormat("#abcdef"));
+ }
+
+ @Test
+ public void validateCategoryNameLengthTest() {
+ assertTrue(ValidationUtils.validateCategoryDisplayNameLength("Netele-2_3#456&+.'=:@@@@@"));
+ assertTrue(ValidationUtils.validateCategoryDisplayNameLength("Nete"));
+ assertFalse(ValidationUtils.validateCategoryDisplayNameLength("Netele-2_3#456&+.'=:@@@@@1"));
+ assertFalse(ValidationUtils.validateCategoryDisplayNameLength("Net"));
+ assertFalse(ValidationUtils.validateCategoryDisplayNameLength(null));
+ }
+
+ @Test
+ public void normalizeCategoryNameTest() {
+ assertEquals("NeteLE-2_3 of #456&+. & Goal a Abc'=:@ AT & T", ValidationUtils.normalizeCategoryName4Display(
+ " neteLE---2___3 OF ###456&&&+++... aNd goal A abc'''==::@@@@@ AT and T "));
+ assertEquals("The Bank of America", ValidationUtils.normalizeCategoryName4Display("The Bank OF America"));
+ }
+
+ @Test
+ public void normalizeCategoryLabelTest() {
+ assertEquals("netele-2_3 of #456&+.&goal a abc'=:@ at&t",
+ ValidationUtils.normalizeCategoryName4Uniqueness("NeteLE-2_3 of #456&+.&Goal a Abc'=:@ AT&T"));
+ }
+
+ /*
+ * Validation utils end
+ */
+
+ /*
+ * General utility start
+ */
+
+ @Test
+ public void validateFileExtension() {
+ assertEquals(Constants.EMPTY_STRING, GeneralUtility.getFilenameExtension("lalatru"));
+ assertEquals(Constants.EMPTY_STRING, GeneralUtility.getFilenameExtension("aa."));
+ assertEquals(Constants.EMPTY_STRING, GeneralUtility.getFilenameExtension(null));
+ assertEquals("yaml", GeneralUtility.getFilenameExtension("lala.tru.yaml"));
+ assertEquals("txt", GeneralUtility.getFilenameExtension("kuku.txt"));
+ }
+
+ @Test
+ public void yamlTest() {
+
+ log.debug("\"kuku\"");
+ DumperOptions options = new DumperOptions();
+ // options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ options.setDefaultScalarStyle(DumperOptions.ScalarStyle.FOLDED);
+ Yaml yaml = new Yaml(options);
+
+ Map<String, Object> parameters = new HashMap<String, Object>();
+ parameters.put("k1", "val");
+ parameters.put("k2", "\"val\"");
+
+ String str = yaml.dump(parameters);
+ log.debug(str);
+ }
+
+ @Test
+ public void yamlValidTest() {
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("key: \"!@;/?:&=+$,_.~*'()[]\"");
+ byte[] payload = sb.toString().getBytes();// Base64.decodeBase64(sb.toString());
+
+ YamlToObjectConverter yamlToObjectConverter = new YamlToObjectConverter();
+
+ assertTrue(yamlToObjectConverter.isValidYaml(payload));
+ }
+
+ @Test
+ public void testRemoveOnlyHtmlTags() {
+
+ assertEquals("gooboo", HtmlCleaner.stripHtml("<b>goo<b></b></b><b>boo</b>"));
+ String str = HtmlCleaner.stripHtml("<esofer><b>goo<b></b><</b><b>boo</b>");
+
+ String stripHtmlAndEscape = HtmlCleaner.stripHtml("<esofer><b>goo<b></b><</b><b>boo</b>");
+ assertEquals("<esofer>goo<boo", stripHtmlAndEscape);
+
+ stripHtmlAndEscape = HtmlCleaner.stripHtml("<esofer><b>goo<b></b><</b><b>boo</b>", true);
+ assertEquals("&lt;esofer&gt;goo&lt;boo", stripHtmlAndEscape);
+
+ stripHtmlAndEscape = HtmlCleaner.stripHtml("<esofer><b>goo<b></b><&</b><b>boo</b>dvc&", true);
+ assertEquals("&lt;esofer&gt;goo&lt;&amp;boodvc&amp;", stripHtmlAndEscape);
+
+ assertEquals("esofer&gt;&gt;&lt;&lt;", HtmlCleaner.stripHtml("esofer>><<", true));
+ assertEquals("esofer>><<", HtmlCleaner.stripHtml("esofer>><<", false));
+
+ assertEquals("<esofer1>><<esofer2>", HtmlCleaner.stripHtml("<esofer1>><<esofer2>"));
+
+ assertEquals("<esofer1 a= b>><<esofer2>", HtmlCleaner.stripHtml("<esofer1 a= b><h1>><<esofer2><br>"));
+
+ assertEquals("&lt;esofer1 a= 'b'&gt;&gt;&lt;&lt;esofer2&gt;",
+ HtmlCleaner.stripHtml("<esofer1 a= 'b'>><<esofer2>", true));
+ assertEquals("<esofer1 a= 'b'>><<esofer2>", HtmlCleaner.stripHtml("<esofer1 a= 'b'>><H6><<esofer2>"));
+
+ assertEquals("<esofer1 a= b>><<esofer2>", HtmlCleaner.stripHtml("<esofer1 a= b>><<esofer2>"));
+
+ assertEquals("<esofer1 sd sa= b>><<esofer2>", HtmlCleaner.stripHtml("<esofer1 sd sa= b>><<esofer2>"));
+
+ assertEquals("&lt;esofer1 sd sa= b&gt;&gt;&lt;&lt;esofer2&gt;",
+ HtmlCleaner.stripHtml("<esofer1 sd sa= b>><<esofer2>", true));
+ assertEquals("<esofer1 sd sa= b>><<esofer2>", HtmlCleaner.stripHtml("<esofer1 sd sa= b>><<esofer2>", false));
+ assertEquals("&lt;esofer1 sd sa= b&gt;&gt;&lt;&lt;esofer2&gt;",
+ HtmlCleaner.stripHtml("<esofer1 sd sa= b>><<esofer2>", true));
+ assertEquals("<esofer1 sd sa= b>><<esofer2>",
+ HtmlCleaner.stripHtml("<esofer1 sd sa= b>><br><H1><<esofer2>", false));
+ assertEquals("&lt;esofer&gt;goo&lt;&amp;boodvc&amp;",
+ HtmlCleaner.stripHtml("<esofer><b>goo<b></b><&</b><b>boo</b>dvc&", true));
+ assertEquals("<esofer>goo<&boodvc&", HtmlCleaner.stripHtml("<esofer><b>goo<b></b><&</b><b>boo</b>dvc&", false));
+ assertEquals("<<<>>>;\"", HtmlCleaner.stripHtml("<<<>>>;\"", false));
+ assertEquals("&lt;&lt;&lt;&gt;&gt;&gt;;&quot;", HtmlCleaner.stripHtml("<<<>>>;\"", true));
+ assertEquals("<<<>>>;\"", HtmlCleaner.stripHtml("<<<>>>;\"", false));
+ assertEquals("abc ab a", HtmlCleaner.stripHtml("abc ab a", true));
+ assertEquals("abc ab a", HtmlCleaner.stripHtml("abc ab a", false));
+ assertEquals("< esofer1 sd &<>sa= b>><<esofer2>",
+ HtmlCleaner.stripHtml("< esofer1 sd &<>sa= b>><br><H1><<esofer2>", false));
+ assertEquals("sa= b>><<esofer2>", HtmlCleaner.stripHtml("<br sd &<>sa= b>><br><H1><<esofer2>", false));
+ assertEquals("< br sd &<>sa= b>><<esofer2>",
+ HtmlCleaner.stripHtml("< br sd &<>sa= b>><br><H1><<esofer2>", false));
+ assertEquals("sa= b>><<esofer2>", HtmlCleaner.stripHtml("</br sd &<>sa= b>><br><H1><<esofer2>", false));
+ assertEquals("sa= b>><<esofer2>", HtmlCleaner.stripHtml("<br sd &</>sa= b>><br><H1><<esofer2>", false));
+
+ }
+
+ /*
+ * General utility end
+ */
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/test/TestExternalConfiguration.java b/common-app-api/src/test/java/org/openecomp/sdc/common/test/TestExternalConfiguration.java
new file mode 100644
index 0000000000..aa3b2e4083
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/test/TestExternalConfiguration.java
@@ -0,0 +1,258 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.test;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.openecomp.sdc.be.config.DistributionEngineConfiguration;
+import org.openecomp.sdc.common.api.BasicConfiguration;
+import org.openecomp.sdc.common.api.ConfigurationListener;
+import org.openecomp.sdc.common.api.ConfigurationSource;
+import org.openecomp.sdc.common.api.FileChangeCallback;
+import org.openecomp.sdc.common.impl.ConfigFileChangeListener;
+import org.openecomp.sdc.common.impl.ExternalConfiguration;
+import org.openecomp.sdc.common.impl.FSConfigurationSource;
+import org.openecomp.sdc.common.test.config.TestConfiguration;
+import org.openecomp.sdc.common.test.config.TestNotExistConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TestExternalConfiguration {
+
+ private static Logger log = LoggerFactory.getLogger(TestExternalConfiguration.class.getName());
+ private static final String NEW_LINE = System.getProperty("line.separator");
+ ConfigurationSource configurationSource = null;
+
+ @Before
+ public void setup() {
+
+ ExternalConfiguration.setAppName("common");
+ ExternalConfiguration.setConfigDir("src/test/resources/config");
+ ExternalConfiguration.listenForChanges();
+
+ configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), ExternalConfiguration.getConfigDir() + File.separator + ExternalConfiguration.getAppName());
+
+ }
+
+ @Test
+ public void testReadConfigurationFile() {
+
+ ConfigurationListener configurationListener = new ConfigurationListener(TestConfiguration.class, new FileChangeCallback() {
+
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+ log.debug("In reconfigure of {}", obj);
+ }
+
+ });
+
+ TestConfiguration testConfiguration = configurationSource.getAndWatchConfiguration(TestConfiguration.class, configurationListener);
+
+ assertTrue(testConfiguration != null);
+ log.debug("{}", testConfiguration);
+ assertEquals(testConfiguration.getBeHost(), "172.20.37.245");
+ assertEquals(testConfiguration.getBeProtocol(), "http");
+ assertEquals(testConfiguration.getBeContext(), "/sdc/rest/config/get");
+
+ }
+
+ @Test
+ public void testNotExistConfigurationFile() {
+
+ ConfigurationListener configurationListener = new ConfigurationListener(TestConfiguration.class, new FileChangeCallback() {
+
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+ log.debug("In reconfigure of {}", obj);
+ }
+
+ });
+
+ TestNotExistConfiguration testConfiguration = configurationSource.getAndWatchConfiguration(TestNotExistConfiguration.class, configurationListener);
+
+ assertTrue(testConfiguration == null);
+
+ }
+
+ @Test
+ public void testUpdateConfigurationFile() {
+
+ ConfigurationListener configurationListener = new ConfigurationListener(TestConfiguration.class, new FileChangeCallback() {
+
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+ log.debug("In reconfigure of {}", obj);
+ // assertEquals(((TestConfiguration)obj).getBeSslPort(),
+ // 8444);
+
+ // assertTrue(((TestConfiguration)obj).getBeSslPort() ==
+ // 8444);
+ }
+
+ });
+
+ TestConfiguration testConfiguration = configurationSource.getAndWatchConfiguration(TestConfiguration.class, configurationListener);
+
+ assertTrue(testConfiguration != null);
+ log.debug("{}", testConfiguration);
+ assertEquals(testConfiguration.getBeHost(), "172.20.37.245");
+ assertEquals(testConfiguration.getBeProtocol(), "http");
+ assertEquals(testConfiguration.getBeContext(), "/sdc/rest/config/get");
+
+ // updateFileContent();
+
+ }
+
+ private void updateFileContent() {
+ File file = new File(ExternalConfiguration.getConfigDir() + File.separator + ExternalConfiguration.getAppName() + File.separator + "test-configuration.yaml");
+ replaceFile(file);
+
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public void replaceFile(File f1) {
+ FileReader fr = null;
+ BufferedReader br = null;
+ FileWriter fw = null;
+ BufferedWriter out = null;
+ try {
+ List<String> lines = new ArrayList<String>();
+ String line = null;
+ fr = new FileReader(f1);
+ br = new BufferedReader(fr);
+ while ((line = br.readLine()) != null) {
+ if (line.contains("beSslPort: 8443"))
+ line = line.replace("8443", "8444");
+ lines.add(line);
+ }
+
+ fw = new FileWriter(f1);
+ out = new BufferedWriter(fw);
+ for (String s : lines)
+ out.write(s + NEW_LINE);
+ out.flush();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ if (fr != null) {
+ try {
+ fr.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ if (fw != null) {
+ try {
+ fw.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testReadDistributionEngineConfigurationFile() {
+
+ ConfigurationListener configurationListener = new ConfigurationListener(TestConfiguration.class, new FileChangeCallback() {
+
+ public void reconfigure(BasicConfiguration obj) {
+ // TODO Auto-generated method stub
+ log.debug("In reconfigure of ", obj);
+ }
+
+ });
+
+ DistributionEngineConfiguration deConfiguration = configurationSource.getAndWatchConfiguration(DistributionEngineConfiguration.class, configurationListener);
+
+ assertTrue(deConfiguration != null);
+ log.debug("{}", deConfiguration);
+ assertEquals(deConfiguration.getDistributionNotifTopicName(), "ASDC-DISTR-NOTIF-TOPIC");
+ assertEquals(deConfiguration.getDistributionStatusTopicName(), "ASDC-DISTR-STATUS-TOPIC");
+
+ assertEquals(deConfiguration.getDistributionStatusTopic().getConsumerGroup(), "asdc");
+ assertEquals(deConfiguration.getDistributionStatusTopic().getConsumerGroup(), "asdc");
+ assertEquals(deConfiguration.getDistributionStatusTopic().getFetchTimeSec().intValue(), 15);
+ assertEquals(deConfiguration.getDistributionStatusTopic().getPollingIntervalSec().intValue(), 60);
+
+ assertEquals(deConfiguration.getEnvironments().size(), 1);
+ assertEquals(deConfiguration.getEnvironments().iterator().next(), "PROD");
+
+ assertEquals(deConfiguration.getDistribNotifResourceArtifactTypes().getInfo(), null);
+ assertEquals(deConfiguration.getDistribNotifResourceArtifactTypes().getLifecycle().size(), 2);
+ assertTrue(deConfiguration.getDistribNotifResourceArtifactTypes().getLifecycle().contains("HEAT"));
+ assertTrue(deConfiguration.getDistribNotifResourceArtifactTypes().getLifecycle().contains("DG_XML"));
+
+ assertEquals(deConfiguration.getDistribNotifServiceArtifactTypes().getLifecycle(), null);
+ assertEquals(deConfiguration.getDistribNotifServiceArtifactTypes().getInfo().size(), 1);
+ assertTrue(deConfiguration.getDistribNotifServiceArtifactTypes().getInfo().contains("MURANO-PKG"));
+
+ assertEquals(deConfiguration.getUebPublicKey(), "fff");
+ assertEquals(deConfiguration.getUebSecretKey(), "ffff");
+ assertEquals(deConfiguration.getUebServers().size(), 3);
+ assertEquals(deConfiguration.getInitRetryIntervalSec().intValue(), 5);
+
+ }
+
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/test/YamlTest.java b/common-app-api/src/test/java/org/openecomp/sdc/common/test/YamlTest.java
new file mode 100644
index 0000000000..340bca9f1d
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/test/YamlTest.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.test;
+
+import static org.junit.Assert.*;
+
+import org.apache.commons.codec.binary.Base64;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openecomp.sdc.common.util.YamlToObjectConverter;
+
+public class YamlTest {
+
+ private static YamlToObjectConverter yamlToObjectConverter;
+ private static String validYaml = "heat_template_version: 2013-05-23\r\ndescription: A load-balancer server\r\nparameters:\r\n image:\r\n type: string\r\n description: Image used for servers\r\n key_name:\r\n type: string\r\n description: SSH key to connect to the servers\r\n flavor:\r\n type: string\r\n description: flavor used by the servers\r\n pool_id:\r\n type: string\r\n description: Pool to contact\r\n user_data:\r\n type: string\r\n description: Server user_data\r\n metadata:\r\n type: json\r\n network:\r\n type: string\r\n description: Network used by the server\r\n\r\nresources:\r\n server:\r\n type: OS::Nova::Server\r\n properties:\r\n flavor: {get_param: flavor}\r\n image: {get_param: image}\r\n key_name: {get_param: key_name}\r\n metadata: {get_param: metadata}\r\n user_data: {get_param: user_data}\r\n user_data_format: RAW\r\n networks: [{network: {get_param: network} }]\r\n member:\r\n type: OS::Neutron::PoolMember\r\n properties:\r\n pool_id: {get_param: pool_id}\r\n address: {get_attr: [server, first_address]}\r\n protocol_port: 80\r\n\r\noutputs:\r\n server_ip:\r\n description: IP Address of the load-balanced server.\r\n value: { get_attr: [server, first_address] }\r\n lb_member:\r\n description: LB member details.\r\n value: { get_attr: [member, show] }";
+ // Missing square brackets at the end of string
+ private static String invalidYaml = "heat_template_version: 2013-05-23\r\ndescription: A load-balancer server\r\nparameters:\r\n image:\r\n type: string\r\n description: Image used for servers\r\n key_name:\r\n type: string\r\n description: SSH key to connect to the servers\r\n flavor:\r\n type: string\r\n description: flavor used by the servers\r\n pool_id:\r\n type: string\r\n description: Pool to contact\r\n user_data:\r\n type: string\r\n description: Server user_data\r\n metadata:\r\n type: json\r\n network:\r\n type: string\r\n description: Network used by the server\r\n\r\nresources:\r\n server:\r\n type: OS::Nova::Server\r\n properties:\r\n flavor: {get_param: flavor}\r\n image: {get_param: image}\r\n key_name: {get_param: key_name}\r\n metadata: {get_param: metadata}\r\n user_data: {get_param: user_data}\r\n user_data_format: RAW\r\n networks: [{network: {get_param: network} }]\r\n member:\r\n type: OS::Neutron::PoolMember\r\n properties:\r\n pool_id: {get_param: pool_id}\r\n address: {get_attr: [server, first_address]}\r\n protocol_port: 80\r\n\r\noutputs:\r\n server_ip:\r\n description: IP Address of the load-balanced server.\r\n value: { get_attr: [server, first_address] }\r\n lb_member:\r\n description: LB member details.\r\n value: { get_attr: [member, show}";
+
+ @BeforeClass
+ public static void setup() {
+ yamlToObjectConverter = new YamlToObjectConverter();
+ }
+
+ @Test
+ public void testValidYaml() {
+ assertTrue(yamlToObjectConverter.isValidYaml(validYaml.getBytes()));
+ }
+
+ @Test
+ public void testInvalidYaml() {
+ assertFalse(yamlToObjectConverter.isValidYaml(invalidYaml.getBytes()));
+ }
+
+ @Test
+ public void testValidYamlBase64() {
+ assertTrue(yamlToObjectConverter.isValidYaml(Base64.encodeBase64(validYaml.getBytes())));
+ }
+
+ @Test
+ public void testInvalidYamlBase64() {
+ assertFalse(yamlToObjectConverter.isValidYaml(Base64.encodeBase64(invalidYaml.getBytes())));
+ }
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConfiguration.java b/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConfiguration.java
new file mode 100644
index 0000000000..b8bf394fd7
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConfiguration.java
@@ -0,0 +1,149 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.test.config;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+import static java.lang.String.format;
+
+public class TestConfiguration extends BasicConfiguration {
+
+ /**
+ * backend host
+ */
+ private String beHost;
+ /**
+ * backend http port
+ */
+ private Integer beHttpPort;
+ /**
+ * backend http secured port
+ */
+ private Integer beSslPort;
+ /**
+ * be http context
+ */
+ private String beContext;
+ /**
+ * backend protocol. http | https
+ */
+ private String beProtocol = "http";
+
+ private Date released;
+ private String version = "1111";
+ private TestConnection connection;
+ private List<String> protocols;
+ private Map<String, String> users;
+
+ public Date getReleased() {
+ return released;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setReleased(Date released) {
+ this.released = released;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public TestConnection getConnection() {
+ return connection;
+ }
+
+ public void setConnection(TestConnection connection) {
+ this.connection = connection;
+ }
+
+ public List<String> getProtocols() {
+ return protocols;
+ }
+
+ public void setProtocols(List<String> protocols) {
+ this.protocols = protocols;
+ }
+
+ public Map<String, String> getUsers() {
+ return users;
+ }
+
+ public void setUsers(Map<String, String> users) {
+ this.users = users;
+ }
+
+ public String getBeHost() {
+ return beHost;
+ }
+
+ public void setBeHost(String beHost) {
+ this.beHost = beHost;
+ }
+
+ public Integer getBeHttpPort() {
+ return beHttpPort;
+ }
+
+ public void setBeHttpPort(Integer beHttpPort) {
+ this.beHttpPort = beHttpPort;
+ }
+
+ public Integer getBeSslPort() {
+ return beSslPort;
+ }
+
+ public void setBeSslPort(Integer beSslPort) {
+ this.beSslPort = beSslPort;
+ }
+
+ public String getBeContext() {
+ return beContext;
+ }
+
+ public void setBeContext(String beContext) {
+ this.beContext = beContext;
+ }
+
+ public String getBeProtocol() {
+ return beProtocol;
+ }
+
+ public void setBeProtocol(String beProtocol) {
+ this.beProtocol = beProtocol;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append(format("backend host: %s\n", beHost))
+ .append(format("backend http port: %s\n", beHttpPort))
+ .append(format("backend ssl port: %s\n", beSslPort)).append(format("backend context: %s\n", beContext))
+ .append(format("backend protocol: %s\n", beProtocol)).append(format("Version: %s\n", version))
+ .append(format("Released: %s\n", released)).append(format("Connecting to database: %s\n", connection))
+ .append(format("Supported protocols: %s\n", protocols)).append(format("Users: %s\n", users)).toString();
+ }
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConnection.java b/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConnection.java
new file mode 100644
index 0000000000..ad546f7f97
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestConnection.java
@@ -0,0 +1,48 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.test.config;
+
+public class TestConnection {
+
+ private String url;
+ private int poolSize;
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public int getPoolSize() {
+ return poolSize;
+ }
+
+ public void setPoolSize(int poolSize) {
+ this.poolSize = poolSize;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("'%s' with pool of %d", getUrl(), getPoolSize());
+ }
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestNotExistConfiguration.java b/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestNotExistConfiguration.java
new file mode 100644
index 0000000000..76f694f5b8
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/test/config/TestNotExistConfiguration.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.test.config;
+
+import static java.lang.String.format;
+
+import org.openecomp.sdc.common.api.BasicConfiguration;
+
+public class TestNotExistConfiguration extends BasicConfiguration {
+
+ /**
+ * backend host
+ */
+ private String beHost;
+ /**
+ * backend http port
+ */
+ private Integer beHttpPort;
+
+ /**
+ * backend http secured port
+ */
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append(format("backend host: %s\n", beHost))
+ .append(format("backend http port: %s\n", beHttpPort)).toString();
+ }
+}
diff --git a/common-app-api/src/test/java/org/openecomp/sdc/common/util/StreamUtilsTests.java b/common-app-api/src/test/java/org/openecomp/sdc/common/util/StreamUtilsTests.java
new file mode 100644
index 0000000000..31dc5f5f47
--- /dev/null
+++ b/common-app-api/src/test/java/org/openecomp/sdc/common/util/StreamUtilsTests.java
@@ -0,0 +1,143 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.common.util;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.sdc.common.util.StreamUtils;
+
+import fj.data.Either;
+
+public class StreamUtilsTests {
+ @Test
+ public void testTakeWhilePredicateNotMet() {
+ List<Either<Integer, Boolean>> list = buildListWith10Integers();
+
+ assertTrue(StreamUtils.takeWhile(list.stream(), p -> p.isLeft()).count() == 10);
+ }
+
+ @Test
+ public void testTakeWhilePredicateIsMet() {
+ List<Either<Integer, Boolean>> list = buildListWith10Integers();
+ addToBooleansToList(list);
+
+ final Stream<Either<Integer, Boolean>> takeWhileStream = StreamUtils.takeWhile(list.stream(), p -> p.isLeft());
+ assertTrue(takeWhileStream.filter(p -> p.isRight()).count() == 0);
+ }
+
+ @Test
+ public <T> void testTakeErrorEvalOnlyOnce() {
+ List<Integer> bucket = new ArrayList<>();
+ // API
+ Function<Integer, Either<Integer, Boolean>> cons = num -> {
+ Either<Integer, Boolean> ret;
+ bucket.add(num);
+ if (num > 5) {
+ ret = Either.right(false);
+ } else {
+ ret = Either.left(num);
+ }
+ ;
+ return ret;
+ };
+
+ List<Integer> num1to10 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ Stream<Either<Integer, Boolean>> streamEithers = num1to10.stream().map(e -> cons.apply(e));
+ List<Either<Integer, Boolean>> collect = StreamUtils.takeWhilePlusOneNoEval(streamEithers, e -> e.isLeft())
+ .collect(Collectors.toList());
+ assertTrue(bucket.size() <= 6);
+ assertTrue(collect.size() <= 6);
+ assertTrue(collect.stream().filter(e -> e.isRight()).count() == 1);
+
+ }
+
+ @Test
+ public void testTakeWhilePlusOnePredicateNotMet() {
+ List<Either<Integer, Boolean>> list = buildListWith10Integers();
+
+ assertTrue(StreamUtils.takeWhilePlusOne(list.stream(), p -> p.isLeft()).count() == 10);
+ }
+
+ @Test
+ public void testTakeWhilePlusOnePredicateIsMet() {
+ List<Either<Integer, Boolean>> list = buildListWith10Integers();
+ addToBooleansToList(list);
+
+ final Stream<Either<Integer, Boolean>> takeWhilePlusOneStream = StreamUtils.takeWhilePlusOne(list.stream(),
+ p -> p.isLeft());
+ assertTrue(takeWhilePlusOneStream.filter(p -> p.isRight()).count() == 1);
+ }
+
+ private void addToBooleansToList(List<Either<Integer, Boolean>> list) {
+ list.add(Either.right(false));
+ list.add(Either.right(false));
+ }
+
+ private List<Either<Integer, Boolean>> buildListWith10Integers() {
+ List<Either<Integer, Boolean>> list = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ list.add(Either.left(i));
+ }
+ return list;
+ }
+
+ @Test
+ public void myTest() {
+ List<Integer> list = new ArrayList<Integer>();
+ for (int i = 0; i < 10; i++) {
+ list.add(i);
+ }
+
+ List<Either<Integer, Boolean>> container = new ArrayList<Either<Integer, Boolean>>();
+ list.stream().map(e -> myBusinessLogic(e, container)).filter(p -> p.isRight()).findAny();
+ // Actual Results are in container
+ assertTrue(container.size() == 6);
+
+ }
+
+ private Either<Integer, Boolean> myBusinessLogic(int e, List<Either<Integer, Boolean>> cobtainerList) {
+ Either<Integer, Boolean> eitherElement = similuteDBAccess(e);
+ // Keep The results in external List
+ cobtainerList.add(eitherElement);
+
+ return eitherElement;
+ }
+
+ private Either<Integer, Boolean> similuteDBAccess(int e) {
+ Either<Integer, Boolean> eitherElement;
+ if (e < 5) {
+ // DB Success
+ eitherElement = Either.left(e);
+ } else {
+ // DB Fail
+ eitherElement = Either.right(true);
+ }
+ return eitherElement;
+ }
+}
diff --git a/common-app-api/src/test/resources/config/common/distribution-engine-configuration.yaml b/common-app-api/src/test/resources/config/common/distribution-engine-configuration.yaml
new file mode 100644
index 0000000000..a4ce7cc08c
--- /dev/null
+++ b/common-app-api/src/test/resources/config/common/distribution-engine-configuration.yaml
@@ -0,0 +1,35 @@
+uebServers:
+ - ueb.fqdn.1:8888
+ - ueb.fqdn.2:8888
+ - ueb.fqdn.3:8888
+
+uebPublicKey: fff
+
+uebSecretKey: ffff
+
+distributionNotifTopicName: ASDC-DISTR-NOTIF-TOPIC
+distributionStatusTopicName: ASDC-DISTR-STATUS-TOPIC
+
+initRetryIntervalSec: 5
+initMaxIntervalSec: 60
+
+distribNotifServiceArtifactTypes:
+ info:
+ - MURANO-PKG
+
+distribNotifResourceArtifactTypes:
+ lifecycle:
+ - HEAT
+ - DG_XML
+
+environments:
+ - PROD
+
+distributionStatusTopic:
+ pollingIntervalSec: 60
+ fetchTimeSec: 15
+ consumerGroup: asdc
+
+createTopic:
+ partitionCount: 1
+ replicationCount: 1
diff --git a/common-app-api/src/test/resources/config/common/test-configuration.yaml b/common-app-api/src/test/resources/config/common/test-configuration.yaml
new file mode 100644
index 0000000000..38912c06ff
--- /dev/null
+++ b/common-app-api/src/test/resources/config/common/test-configuration.yaml
@@ -0,0 +1,35 @@
+# catalog backend hostname
+beHost: 172.20.37.245
+
+# catalog backend http port
+beHttpPort: 8080
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: 8444
+
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 17
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+
+
diff --git a/common-app-api/src/test/resources/logback-test.xml b/common-app-api/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..d2b9bff23f
--- /dev/null
+++ b/common-app-api/src/test/resources/logback-test.xml
@@ -0,0 +1,13 @@
+<!-- only one line, shut up logback ! -->
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <Pattern>
+ %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+ </Pattern>
+ </encoder>
+ </appender>
+ <root level="OFF">
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration> \ No newline at end of file
diff --git a/common-be/.gitignore b/common-be/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/common-be/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/common-be/pom.xml b/common-be/pom.xml
new file mode 100644
index 0000000000..9d27eb8338
--- /dev/null
+++ b/common-be/pom.xml
@@ -0,0 +1,116 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openecomp.sdc.be</groupId>
+ <artifactId>common-be</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <properties>
+ <aspectj.version>1.7.4</aspectj.version>
+ <hibernate-validator.version>5.3.4.Final</hibernate-validator.version>
+ <groovy.version>2.3.5</groovy.version>
+ <lucene.version>4.10.2</lucene.version>
+ <mockito.version>1.9.0</mockito.version>
+ <regex.version>3.0.3</regex.version>
+ <codehaus.jackson.core>1.9.2</codehaus.jackson.core>
+ </properties>
+
+
+ <dependencies>
+ <!-- Common of SD&C -->
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>common-app-api</artifactId>
+ <version>${common-app-api.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <version>${codehaus.jackson.core}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- TEST -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+
+ </dependencies>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings
+ only. It has no influence on the Maven build itself. -->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>fr.fastconnect</groupId>
+ <artifactId>plantuml-maven-plugin</artifactId>
+ <versionRange>[1.0.0,)</versionRange>
+ <goals>
+ <goal>plant</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+</project>
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/CategoryDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/CategoryDataDefinition.java
new file mode 100644
index 0000000000..754721a5a6
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/CategoryDataDefinition.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.category;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class CategoryDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2882352060242714427L;
+
+ private String name;
+ private String normalizedName;
+ private String uniqueId;
+ private List<String> icons;
+
+ public CategoryDataDefinition() {
+
+ }
+
+ public CategoryDataDefinition(CategoryDataDefinition c) {
+ this.name = c.name;
+ this.normalizedName = c.normalizedName;
+ this.uniqueId = c.uniqueId;
+ this.icons = c.icons;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNormalizedName() {
+ return normalizedName;
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ this.normalizedName = normalizedName;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public List<String> getIcons() {
+ return icons;
+ }
+
+ public void setIcons(List<String> icons) {
+ this.icons = icons;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((normalizedName == null) ? 0 : normalizedName.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((icons == null) ? 0 : icons.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ CategoryDataDefinition other = (CategoryDataDefinition) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (normalizedName == null) {
+ if (other.normalizedName != null)
+ return false;
+ } else if (!normalizedName.equals(other.normalizedName))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ if (icons == null) {
+ if (other.icons != null)
+ return false;
+ } else if (!icons.equals(other.icons))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "CategoryDataDefinition [name=" + name + ", normalizedName=" + normalizedName + ", uniqueId=" + uniqueId
+ + ", icons=" + icons + "]";
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/GroupingDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/GroupingDataDefinition.java
new file mode 100644
index 0000000000..87924e99d4
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/GroupingDataDefinition.java
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.category;
+
+import java.io.Serializable;
+
+public class GroupingDataDefinition implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2641475929148843849L;
+
+ private String name;
+ private String normalizedName;
+ private String uniqueId;
+
+ public GroupingDataDefinition() {
+
+ }
+
+ public GroupingDataDefinition(GroupingDataDefinition c) {
+ this.name = c.name;
+ this.normalizedName = c.normalizedName;
+ this.uniqueId = c.uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNormalizedName() {
+ return normalizedName;
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ this.normalizedName = normalizedName;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((normalizedName == null) ? 0 : normalizedName.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ GroupingDataDefinition other = (GroupingDataDefinition) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (normalizedName == null) {
+ if (other.normalizedName != null)
+ return false;
+ } else if (!normalizedName.equals(other.normalizedName))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupingDataDefinition [name=" + name + ", normalizedName=" + normalizedName + ", uniqueId=" + uniqueId
+ + "]";
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/SubCategoryDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/SubCategoryDataDefinition.java
new file mode 100644
index 0000000000..2774fe4e40
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/category/SubCategoryDataDefinition.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.category;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class SubCategoryDataDefinition implements Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8279397988497086676L;
+
+ private String name;
+ private String normalizedName;
+ private String uniqueId;
+ private List<String> icons;
+
+ public SubCategoryDataDefinition() {
+
+ }
+
+ public SubCategoryDataDefinition(SubCategoryDataDefinition c) {
+ this.name = c.name;
+ this.normalizedName = c.normalizedName;
+ this.uniqueId = c.uniqueId;
+ this.icons = c.icons;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getNormalizedName() {
+ return normalizedName;
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ this.normalizedName = normalizedName;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public List<String> getIcons() {
+ return icons;
+ }
+
+ public void setIcons(List<String> icons) {
+ this.icons = icons;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((normalizedName == null) ? 0 : normalizedName.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((icons == null) ? 0 : icons.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SubCategoryDataDefinition other = (SubCategoryDataDefinition) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (normalizedName == null) {
+ if (other.normalizedName != null)
+ return false;
+ } else if (!normalizedName.equals(other.normalizedName))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ if (icons == null) {
+ if (other.icons != null)
+ return false;
+ } else if (!icons.equals(other.icons))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SubCategoryDataDefinition [name=" + name + ", normalizedName=" + normalizedName + ", uniqueId="
+ + uniqueId + ", icons=" + icons + "]";
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ComponentMetadataDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ComponentMetadataDataDefinition.java
new file mode 100644
index 0000000000..4af7a279a3
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ComponentMetadataDataDefinition.java
@@ -0,0 +1,429 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.components;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public abstract class ComponentMetadataDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -9114770126086263552L;
+
+ private String uniqueId;
+
+ private String name; // archiveName
+
+ private String version; // archiveVersion
+
+ private Boolean isHighestVersion;
+
+ private Long creationDate;
+
+ private Long lastUpdateDate;
+
+ private String description;
+
+ private String state;
+
+ private List<String> tags;
+
+ private String icon;
+
+ private String UUID;
+
+ private String normalizedName;
+
+ private String systemName;
+
+ private String contactId;
+
+ private Map<String, String> allVersions;
+
+ private Boolean isDeleted;
+
+ private String projectCode;
+
+ private String csarUUID;
+
+ private String csarVersion;
+
+ private String importedToscaChecksum;
+
+ private String invariantUUID;
+
+ public ComponentMetadataDataDefinition() {
+
+ }
+
+ public ComponentMetadataDataDefinition(ComponentMetadataDataDefinition other) {
+ this.uniqueId = other.getUniqueId();
+ this.name = other.getName();
+ this.version = other.getVersion();
+ this.isHighestVersion = other.isHighestVersion();
+ this.creationDate = other.getCreationDate();
+ this.lastUpdateDate = other.getLastUpdateDate();
+ this.description = other.getDescription();
+ this.state = other.getState();
+ this.tags = new ArrayList<String>(other.getTags());
+ this.icon = other.getIcon();
+ this.contactId = other.getContactId();
+ this.UUID = other.getUUID();
+ this.normalizedName = other.getNormalizedName();
+ this.systemName = other.getSystemName();
+ this.allVersions = new HashMap<String, String>(other.getAllVersions());
+ this.isDeleted = other.isDeleted();
+ this.projectCode = other.getProjectCode();
+ this.csarUUID = other.getCsarUUID();
+ this.csarVersion = other.csarVersion;
+ this.importedToscaChecksum = other.getImportedToscaChecksum();
+ this.invariantUUID = other.getInvariantUUID();
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Boolean isHighestVersion() {
+ return isHighestVersion;
+ }
+
+ public void setHighestVersion(Boolean isHighestVersion) {
+ this.isHighestVersion = isHighestVersion;
+ }
+
+ public Long getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Long creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public Long getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(Long lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public List<String> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<String> tags) {
+ this.tags = tags;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+ public String getContactId() {
+ return contactId;
+ }
+
+ public void setContactId(String contactId) {
+ this.contactId = contactId;
+ }
+
+ public String getUUID() {
+ return UUID;
+ }
+
+ public void setUUID(String uUID) {
+ UUID = uUID;
+ }
+
+ public String getNormalizedName() {
+ return normalizedName;
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ this.normalizedName = normalizedName;
+ }
+
+ public String getSystemName() {
+ return systemName;
+ }
+
+ public void setSystemName(String systemName) {
+ this.systemName = systemName;
+ }
+
+ public Map<String, String> getAllVersions() {
+ return allVersions;
+ }
+
+ public void setAllVersions(Map<String, String> allVersions) {
+ this.allVersions = allVersions;
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ public Boolean isDeleted() {
+ return isDeleted;
+ }
+
+ public void setIsDeleted(Boolean isDeleted) {
+ this.isDeleted = isDeleted;
+ }
+
+ public String getProjectCode() {
+ return projectCode;
+ }
+
+ public void setProjectCode(String projectCode) {
+ this.projectCode = projectCode;
+ }
+
+ public String getCsarUUID() {
+ return csarUUID;
+ }
+
+ public void setCsarUUID(String csarUUID) {
+ this.csarUUID = csarUUID;
+ }
+
+ public String getCsarVersion() {
+ return csarVersion;
+ }
+
+ public void setCsarVersion(String csarVersion) {
+ this.csarVersion = csarVersion;
+ }
+
+ public String getImportedToscaChecksum() {
+ return importedToscaChecksum;
+ }
+
+ public void setImportedToscaChecksum(String importedToscaChecksum) {
+ this.importedToscaChecksum = importedToscaChecksum;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentMetadataDataDefinition [uniqueId=" + uniqueId + ", name=" + name + ", version=" + version
+ + ", isHighestVersion=" + isHighestVersion + ", creationDate=" + creationDate + ", lastUpdateDate="
+ + lastUpdateDate + ", description=" + description + ", state=" + state + ", tags=" + tags + ", icon="
+ + icon + ", UUID=" + UUID + ", normalizedName=" + normalizedName + ", systemName=" + systemName
+ + ", contactId=" + contactId + ", allVersions=" + allVersions + ", isDeleted=" + isDeleted
+ + ", projectCode=" + projectCode + ", csarUUID=" + csarUUID + ", csarversion=" + csarVersion
+ + ", importedToscaChecksum=" + importedToscaChecksum + ", invariantUUID=" + invariantUUID + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((UUID == null) ? 0 : UUID.hashCode());
+ result = prime * result + ((allVersions == null) ? 0 : allVersions.hashCode());
+ result = prime * result + ((contactId == null) ? 0 : contactId.hashCode());
+ result = prime * result + ((creationDate == null) ? 0 : creationDate.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((icon == null) ? 0 : icon.hashCode());
+ result = prime * result + ((isDeleted == null) ? 0 : isDeleted.hashCode());
+ result = prime * result + ((isHighestVersion == null) ? 0 : isHighestVersion.hashCode());
+ result = prime * result + ((lastUpdateDate == null) ? 0 : lastUpdateDate.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((normalizedName == null) ? 0 : normalizedName.hashCode());
+ result = prime * result + ((state == null) ? 0 : state.hashCode());
+ result = prime * result + ((systemName == null) ? 0 : systemName.hashCode());
+ result = prime * result + ((tags == null) ? 0 : tags.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ result = prime * result + ((projectCode == null) ? 0 : projectCode.hashCode());
+ result = prime * result + ((csarUUID == null) ? 0 : csarUUID.hashCode());
+ result = prime * result + ((csarVersion == null) ? 0 : csarVersion.hashCode());
+ result = prime * result + ((importedToscaChecksum == null) ? 0 : importedToscaChecksum.hashCode());
+ result = prime * result + ((invariantUUID == null) ? 0 : invariantUUID.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ComponentMetadataDataDefinition other = (ComponentMetadataDataDefinition) obj;
+ if (UUID == null) {
+ if (other.UUID != null)
+ return false;
+ } else if (!UUID.equals(other.UUID))
+ return false;
+ if (allVersions == null) {
+ if (other.allVersions != null)
+ return false;
+ } else if (!allVersions.equals(other.allVersions))
+ return false;
+ if (contactId == null) {
+ if (other.contactId != null)
+ return false;
+ } else if (!contactId.equals(other.contactId))
+ return false;
+ if (creationDate == null) {
+ if (other.creationDate != null)
+ return false;
+ } else if (!creationDate.equals(other.creationDate))
+ return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (icon == null) {
+ if (other.icon != null)
+ return false;
+ } else if (!icon.equals(other.icon))
+ return false;
+ if (isDeleted == null) {
+ if (other.isDeleted != null)
+ return false;
+ } else if (!isDeleted.equals(other.isDeleted))
+ return false;
+ if (isHighestVersion == null) {
+ if (other.isHighestVersion != null)
+ return false;
+ } else if (!isHighestVersion.equals(other.isHighestVersion))
+ return false;
+ if (lastUpdateDate == null) {
+ if (other.lastUpdateDate != null)
+ return false;
+ } else if (!lastUpdateDate.equals(other.lastUpdateDate))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (normalizedName == null) {
+ if (other.normalizedName != null)
+ return false;
+ } else if (!normalizedName.equals(other.normalizedName))
+ return false;
+ if (state == null) {
+ if (other.state != null)
+ return false;
+ } else if (!state.equals(other.state))
+ return false;
+ if (systemName == null) {
+ if (other.systemName != null)
+ return false;
+ } else if (!systemName.equals(other.systemName))
+ return false;
+ if (tags == null) {
+ if (other.tags != null)
+ return false;
+ } else if (!tags.equals(other.tags))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ if (version == null) {
+ if (other.version != null)
+ return false;
+ } else if (!version.equals(other.version))
+ return false;
+ if (projectCode == null) {
+ if (other.projectCode != null)
+ return false;
+ } else if (!projectCode.equals(other.projectCode))
+ return false;
+ if (csarUUID == null) {
+ if (other.csarUUID != null)
+ return false;
+ } else if (!csarUUID.equals(other.csarUUID))
+ return false;
+ if (csarVersion == null) {
+ if (other.csarVersion != null)
+ return false;
+ } else if (!csarVersion.equals(other.csarVersion))
+ return false;
+ if (importedToscaChecksum == null) {
+ if (other.importedToscaChecksum != null)
+ return false;
+ } else if (!importedToscaChecksum.equals(other.importedToscaChecksum))
+ return false;
+ if (invariantUUID == null) {
+ if (other.invariantUUID != null)
+ return false;
+ } else if (!invariantUUID.equals(other.invariantUUID))
+ return false;
+ return true;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ResourceMetadataDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ResourceMetadataDataDefinition.java
new file mode 100644
index 0000000000..2eb5e23352
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ResourceMetadataDataDefinition.java
@@ -0,0 +1,176 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.components;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+
+public class ResourceMetadataDataDefinition extends ComponentMetadataDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1142973528643758481L;
+
+ private String vendorName;
+ private String vendorRelease;
+ private ResourceTypeEnum resourceType = ResourceTypeEnum.VFC; // ResourceType.VFC
+ // is
+ // default
+ private Boolean isAbstract;
+ private String cost;
+ private String licenseType;
+ private String toscaResourceName;
+
+ public ResourceMetadataDataDefinition() {
+ super();
+ }
+
+ public ResourceMetadataDataDefinition(ResourceMetadataDataDefinition other) {
+ super(other);
+ this.vendorName = other.getVendorName();
+ this.vendorRelease = other.getVendorRelease();
+ this.isAbstract = other.isHighestVersion();
+ this.resourceType = other.getResourceType();
+ this.toscaResourceName = other.getToscaResourceName();
+ }
+
+ public String getVendorName() {
+ return vendorName;
+ }
+
+ public void setVendorName(String vendorName) {
+ this.vendorName = vendorName;
+ }
+
+ public String getVendorRelease() {
+ return vendorRelease;
+ }
+
+ public void setVendorRelease(String vendorRelease) {
+ this.vendorRelease = vendorRelease;
+ }
+
+ public ResourceTypeEnum getResourceType() {
+ return resourceType;
+ }
+
+ public void setResourceType(ResourceTypeEnum resourceType) {
+ this.resourceType = resourceType;
+ }
+
+ public Boolean isAbstract() {
+ return isAbstract;
+ }
+
+ public void setAbstract(Boolean isAbstract) {
+ this.isAbstract = isAbstract;
+ }
+
+ public String getCost() {
+ return cost;
+ }
+
+ public void setCost(String cost) {
+ this.cost = cost;
+ }
+
+ public String getLicenseType() {
+ return licenseType;
+ }
+
+ public void setLicenseType(String licenseType) {
+ this.licenseType = licenseType;
+ }
+
+ public String getToscaResourceName() {
+ return toscaResourceName;
+ }
+
+ public void setToscaResourceName(String toscaResourceName) {
+ this.toscaResourceName = toscaResourceName;
+ }
+
+ @Override
+ public String toString() {
+ return "ResourceMetadataDataDefinition [vendorName=" + vendorName + ", vendorRelease=" + vendorRelease
+ + ", resourceType=" + resourceType + ", isAbstract=" + isAbstract + super.toString() + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((cost == null) ? 0 : cost.hashCode());
+ result = prime * result + ((isAbstract == null) ? 0 : isAbstract.hashCode());
+ result = prime * result + ((licenseType == null) ? 0 : licenseType.hashCode());
+ result = prime * result + ((resourceType == null) ? 0 : resourceType.hashCode());
+ result = prime * result + ((vendorName == null) ? 0 : vendorName.hashCode());
+ result = prime * result + ((vendorRelease == null) ? 0 : vendorRelease.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceMetadataDataDefinition other = (ResourceMetadataDataDefinition) obj;
+ if (cost == null) {
+ if (other.cost != null)
+ return false;
+ } else if (!cost.equals(other.cost))
+ return false;
+ if (isAbstract == null) {
+ if (other.isAbstract != null)
+ return false;
+ } else if (!isAbstract.equals(other.isAbstract))
+ return false;
+ if (licenseType == null) {
+ if (other.licenseType != null)
+ return false;
+ } else if (!licenseType.equals(other.licenseType))
+ return false;
+ if (resourceType != other.resourceType)
+ return false;
+ if (vendorName == null) {
+ if (other.vendorName != null)
+ return false;
+ } else if (!vendorName.equals(other.vendorName))
+ return false;
+ if (vendorRelease == null) {
+ if (other.vendorRelease != null)
+ return false;
+ }
+ if (toscaResourceName == null) {
+ if (other.toscaResourceName != null)
+ return false;
+ } else if (!vendorRelease.equals(other.vendorRelease))
+ return false;
+
+ return super.equals(obj);
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ServiceMetadataDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ServiceMetadataDataDefinition.java
new file mode 100644
index 0000000000..2ebbadcdaf
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/components/ServiceMetadataDataDefinition.java
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.components;
+
+import java.io.Serializable;
+
+public class ServiceMetadataDataDefinition extends ComponentMetadataDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -7661001892509435120L;
+
+ private String distributionStatus;
+
+ public ServiceMetadataDataDefinition() {
+ super();
+ }
+
+ public ServiceMetadataDataDefinition(ServiceMetadataDataDefinition other) {
+ super(other);
+ }
+
+ public String getDistributionStatus() {
+ return distributionStatus;
+ }
+
+ public void setDistributionStatus(String distributionStatus) {
+ this.distributionStatus = distributionStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "ServiceMetadataDataDefinition [ distributionStatus=" + distributionStatus + ", parent="
+ + super.toString() + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((distributionStatus == null) ? 0 : distributionStatus.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof ComponentMetadataDataDefinition)) {
+ return false;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ return false;
+ ServiceMetadataDataDefinition other = (ServiceMetadataDataDefinition) obj;
+ if (distributionStatus == null) {
+ if (other.distributionStatus != null)
+ return false;
+ } else if (!distributionStatus.equals(other.distributionStatus))
+ return false;
+ return super.equals(obj);
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AdditionalInfoParameterDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AdditionalInfoParameterDataDefinition.java
new file mode 100644
index 0000000000..4c062e3fab
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AdditionalInfoParameterDataDefinition.java
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+public class AdditionalInfoParameterDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -565365728516901670L;
+
+ private String uniqueId;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private Integer lastCreatedCounter = 0;
+
+ public AdditionalInfoParameterDataDefinition() {
+
+ }
+
+ public AdditionalInfoParameterDataDefinition(AdditionalInfoParameterDataDefinition p) {
+ this.uniqueId = p.uniqueId;
+ this.creationTime = p.creationTime;
+ this.modificationTime = p.modificationTime;
+ this.lastCreatedCounter = p.lastCreatedCounter;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public Integer getLastCreatedCounter() {
+ return lastCreatedCounter;
+ }
+
+ public void setLastCreatedCounter(Integer lastCreatedCounter) {
+ this.lastCreatedCounter = lastCreatedCounter;
+ }
+
+ @Override
+ public String toString() {
+ return "AdditionalInfoParameterDataDefinition [uniqueId=" + uniqueId + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + ", lastCreatedCounter=" + lastCreatedCounter + "]";
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ArtifactDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ArtifactDataDefinition.java
new file mode 100644
index 0000000000..4baf09ea68
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ArtifactDataDefinition.java
@@ -0,0 +1,587 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+
+import java.io.Serializable;
+
+public class ArtifactDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -1691343090754083941L;
+
+ /**
+ * The unique id of the artifact
+ */
+ private String uniqueId;
+
+ /**
+ * Tosca logical name
+ */
+ // private String logicalName;
+
+ /** This attribute specifies the type of this artifact. */
+ private String artifactType;
+
+ /** Specifies the reference of the artifact. uri to the SWIFT */
+ private String artifactRef;
+
+ /** Specifies the display name of the artifact. */
+ private String artifactName;
+
+ /**
+ * Non TOSCA compliant property
+ */
+ private String artifactRepository;
+
+ /**
+ * Checksum value of the uploaded artifact file retrieved from "Content-MD5"?
+ * header of the HTTP POST/PUT request. Should be updated each time when the
+ * artifact file is updated.
+ */
+ private String artifactChecksum;
+
+ /**
+ * artifact creator
+ */
+ private String userIdCreator;
+
+ /**
+ * USER ID of the last resource (artifact) updater
+ */
+ private String userIdLastUpdater;
+
+ /**
+ * Full name of artifact creator
+ */
+ private String creatorFullName;
+
+ /**
+ * Full name of the last resource (artifact) updater
+ */
+ private String updaterFullName;
+
+ /**
+ * Timestamp of the resource (artifact) creation
+ */
+ private Long creationDate;
+
+ /**
+ * Timestamp of the last resource (artifact) creation
+ */
+ private Long lastUpdateDate;
+
+ /**
+ * Id of artifact data in ES
+ */
+ private String esId;
+
+ /**
+ * Logical artifact name. Used by TOSCA
+ */
+ private String artifactLabel;
+
+ private String artifactCreator;
+
+ private String description;
+
+ private Boolean mandatory = Boolean.FALSE;
+
+ private String artifactDisplayName;
+
+ private String apiUrl;
+
+ private Boolean serviceApi = Boolean.FALSE;
+
+ /**
+ * Flag that set to TRUE if generated from AI&I Artifact generator to
+ * distinguish between manually uploaded and generated artifacts
+ */
+ private Boolean generated = Boolean.FALSE;
+
+ private ArtifactGroupTypeEnum artifactGroupType;
+ private Integer timeout;
+ private String artifactVersion;
+ private String artifactUUID;
+ private Long payloadUpdateDate;
+ private Long heatParamsUpdateDate;
+
+ private List<String> requiredArtifacts;
+
+ public ArtifactDataDefinition() {
+ artifactVersion = "0";
+ }
+
+ public ArtifactDataDefinition(ArtifactDataDefinition a) {
+ this.uniqueId = a.uniqueId;
+ this.artifactType = a.artifactType;
+ this.artifactRef = a.artifactRef;
+ this.artifactName = a.artifactName;
+ this.artifactRepository = a.artifactRepository;
+ this.artifactChecksum = a.artifactChecksum;
+ this.userIdCreator = a.userIdCreator;
+ this.userIdLastUpdater = a.userIdLastUpdater;
+ this.creatorFullName = a.creatorFullName;
+ this.updaterFullName = a.updaterFullName;
+ this.creationDate = a.creationDate;
+ this.lastUpdateDate = a.lastUpdateDate;
+ this.description = a.description;
+ this.esId = a.esId;
+ this.artifactLabel = a.artifactLabel;
+ this.artifactCreator = a.artifactCreator;
+ this.mandatory = a.mandatory;
+ this.artifactDisplayName = a.artifactDisplayName;
+ this.apiUrl = a.apiUrl;
+ this.serviceApi = a.serviceApi;
+ this.artifactGroupType = a.artifactGroupType;
+ this.timeout = a.timeout;
+ this.artifactVersion = a.artifactVersion;
+ this.artifactUUID = a.artifactUUID;
+ this.payloadUpdateDate = a.payloadUpdateDate;
+ this.heatParamsUpdateDate = a.heatParamsUpdateDate;
+ this.setGenerated(a.getGenerated());
+ if (a.requiredArtifacts != null)
+ this.requiredArtifacts = new ArrayList<>(a.getRequiredArtifacts());
+ }
+
+ public String getArtifactName() {
+ return artifactName != null ? artifactName : artifactRef;
+ }
+
+ public String getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(String artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getArtifactRef() {
+ return artifactRef;
+ }
+
+ public void setArtifactRef(String artifactRef) {
+ this.artifactRef = artifactRef;
+ }
+
+ public String getArtifactRepository() {
+ return artifactRepository;
+ }
+
+ public void setArtifactRepository(String artifactRepository) {
+ this.artifactRepository = artifactRepository;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactChecksum() {
+ return artifactChecksum;
+ }
+
+ public void setArtifactChecksum(String artifactChecksum) {
+ this.artifactChecksum = artifactChecksum;
+ }
+
+ public String getUserIdCreator() {
+ return userIdCreator;
+ }
+
+ public void setUserIdCreator(String userIdCreator) {
+ this.userIdCreator = userIdCreator;
+ }
+
+ public String getUserIdLastUpdater() {
+ return userIdLastUpdater;
+ }
+
+ public void setUserIdLastUpdater(String userIdLastUpdater) {
+ this.userIdLastUpdater = userIdLastUpdater;
+ }
+
+ public String getCreatorFullName() {
+ return creatorFullName;
+ }
+
+ public void setCreatorFullName(String creatorFullName) {
+ this.creatorFullName = creatorFullName;
+ }
+
+ public String getUpdaterFullName() {
+ return updaterFullName;
+ }
+
+ public void setUpdaterFullName(String updaterFullName) {
+ this.updaterFullName = updaterFullName;
+ }
+
+ public Long getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Long creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public Long getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(Long lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getArtifactLabel() {
+ return artifactLabel;
+ }
+
+ public void setArtifactLabel(String artifactLabel) {
+ this.artifactLabel = artifactLabel;
+ }
+
+ public String getEsId() {
+ return esId;
+ }
+
+ public void setEsId(String esId) {
+ this.esId = esId;
+ }
+
+ public String getArtifactCreator() {
+ return artifactCreator;
+ }
+
+ public void setArtifactCreator(String artifactCreator) {
+ this.artifactCreator = artifactCreator;
+ }
+
+ public Boolean getMandatory() {
+ return mandatory;
+ }
+
+ public void setMandatory(Boolean mandatory) {
+ this.mandatory = mandatory;
+ }
+
+ public String getArtifactDisplayName() {
+ return artifactDisplayName;
+ }
+
+ public void setArtifactDisplayName(String artifactDisplayName) {
+ this.artifactDisplayName = artifactDisplayName;
+ }
+
+ public String getApiUrl() {
+ return apiUrl;
+ }
+
+ public void setApiUrl(String apiUrl) {
+ this.apiUrl = apiUrl;
+ }
+
+ public Boolean getServiceApi() {
+ return serviceApi;
+ }
+
+ public void setServiceApi(Boolean serviceApi) {
+ this.serviceApi = serviceApi;
+ }
+
+ public ArtifactGroupTypeEnum getArtifactGroupType() {
+ return artifactGroupType;
+ }
+
+ public void setArtifactGroupType(ArtifactGroupTypeEnum artifactGroupType) {
+ this.artifactGroupType = artifactGroupType;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public String getArtifactVersion() {
+ return artifactVersion;
+ }
+
+ public void setArtifactVersion(String artifactVersion) {
+ this.artifactVersion = artifactVersion;
+ }
+
+ public String getArtifactUUID() {
+ return artifactUUID;
+ }
+
+ public void setArtifactUUID(String artifactUUID) {
+ this.artifactUUID = artifactUUID;
+ }
+
+ public Long getPayloadUpdateDate() {
+ return payloadUpdateDate;
+ }
+
+ public void setPayloadUpdateDate(Long payloadUpdateDate) {
+ this.payloadUpdateDate = payloadUpdateDate;
+ }
+
+ public Long getHeatParamsUpdateDate() {
+ return heatParamsUpdateDate;
+ }
+
+ public void setHeatParamsUpdateDate(Long heatParamsUpdateDate) {
+ this.heatParamsUpdateDate = heatParamsUpdateDate;
+ }
+
+ public List<String> getRequiredArtifacts() {
+ return requiredArtifacts;
+ }
+
+ public void setRequiredArtifacts(List<String> requiredArtifacts) {
+ this.requiredArtifacts = requiredArtifacts;
+ }
+
+ public Boolean getGenerated() {
+ return generated;
+ }
+
+ public void setGenerated(Boolean generated) {
+ this.generated = generated;
+ }
+
+ @Override
+ public String toString() {
+ return "ArtifactDataDefinition [uniqueId=" + uniqueId + ", artifactType=" + artifactType + ", artifactRef="
+ + artifactRef + ", artifactName=" + artifactName + ", artifactRepository=" + artifactRepository
+ + ", artifactChecksum=" + artifactChecksum + ", userIdCreator=" + userIdCreator + ", userIdLastUpdater="
+ + userIdLastUpdater + ", creatorFullName=" + creatorFullName + ", updaterFullName=" + updaterFullName
+ + ", creationDate=" + creationDate + ", lastUpdateDate=" + lastUpdateDate + ", esId=" + esId
+ + ", artifactLabel=" + artifactLabel + ", artifactCreator=" + artifactCreator + ", description="
+ + description + ", mandatory=" + mandatory + ", artifactDisplayName=" + artifactDisplayName
+ + ", apiUrl=" + apiUrl + ", serviceApi=" + serviceApi + ", artifactGroupType=" + artifactGroupType
+ + ", timeout=" + timeout + ", artifactVersion=" + artifactVersion + ", artifactUUID=" + artifactUUID
+ + ", payloadUpdateDate=" + payloadUpdateDate + ", heatParamsUpdateDate=" + heatParamsUpdateDate
+ + ", requiredArtifacts=" + requiredArtifacts + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((apiUrl == null) ? 0 : apiUrl.hashCode());
+ result = prime * result + ((artifactChecksum == null) ? 0 : artifactChecksum.hashCode());
+ result = prime * result + ((artifactCreator == null) ? 0 : artifactCreator.hashCode());
+ result = prime * result + ((artifactDisplayName == null) ? 0 : artifactDisplayName.hashCode());
+ result = prime * result + ((artifactGroupType == null) ? 0 : artifactGroupType.hashCode());
+ result = prime * result + ((artifactLabel == null) ? 0 : artifactLabel.hashCode());
+ result = prime * result + ((artifactName == null) ? 0 : artifactName.hashCode());
+ result = prime * result + ((artifactRef == null) ? 0 : artifactRef.hashCode());
+ result = prime * result + ((artifactRepository == null) ? 0 : artifactRepository.hashCode());
+ result = prime * result + ((artifactType == null) ? 0 : artifactType.hashCode());
+ result = prime * result + ((artifactUUID == null) ? 0 : artifactUUID.hashCode());
+ result = prime * result + ((artifactVersion == null) ? 0 : artifactVersion.hashCode());
+ result = prime * result + ((userIdCreator == null) ? 0 : userIdCreator.hashCode());
+ result = prime * result + ((userIdLastUpdater == null) ? 0 : userIdLastUpdater.hashCode());
+ result = prime * result + ((creationDate == null) ? 0 : creationDate.hashCode());
+ result = prime * result + ((creatorFullName == null) ? 0 : creatorFullName.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((esId == null) ? 0 : esId.hashCode());
+ result = prime * result + ((heatParamsUpdateDate == null) ? 0 : heatParamsUpdateDate.hashCode());
+ result = prime * result + ((lastUpdateDate == null) ? 0 : lastUpdateDate.hashCode());
+ result = prime * result + ((mandatory == null) ? 0 : mandatory.hashCode());
+ result = prime * result + ((payloadUpdateDate == null) ? 0 : payloadUpdateDate.hashCode());
+ result = prime * result + ((requiredArtifacts == null) ? 0 : requiredArtifacts.hashCode());
+ result = prime * result + ((serviceApi == null) ? 0 : serviceApi.hashCode());
+ result = prime * result + ((timeout == null) ? 0 : timeout.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((updaterFullName == null) ? 0 : updaterFullName.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ArtifactDataDefinition other = (ArtifactDataDefinition) obj;
+ if (apiUrl == null) {
+ if (other.apiUrl != null)
+ return false;
+ } else if (!apiUrl.equals(other.apiUrl))
+ return false;
+ if (artifactChecksum == null) {
+ if (other.artifactChecksum != null)
+ return false;
+ } else if (!artifactChecksum.equals(other.artifactChecksum))
+ return false;
+ if (artifactCreator == null) {
+ if (other.artifactCreator != null)
+ return false;
+ } else if (!artifactCreator.equals(other.artifactCreator))
+ return false;
+ if (artifactDisplayName == null) {
+ if (other.artifactDisplayName != null)
+ return false;
+ } else if (!artifactDisplayName.equals(other.artifactDisplayName))
+ return false;
+ if (artifactGroupType != other.artifactGroupType)
+ return false;
+ if (artifactLabel == null) {
+ if (other.artifactLabel != null)
+ return false;
+ } else if (!artifactLabel.equals(other.artifactLabel))
+ return false;
+ if (artifactName == null) {
+ if (other.artifactName != null)
+ return false;
+ } else if (!artifactName.equals(other.artifactName))
+ return false;
+ if (artifactRef == null) {
+ if (other.artifactRef != null)
+ return false;
+ } else if (!artifactRef.equals(other.artifactRef))
+ return false;
+ if (artifactRepository == null) {
+ if (other.artifactRepository != null)
+ return false;
+ } else if (!artifactRepository.equals(other.artifactRepository))
+ return false;
+ if (artifactType == null) {
+ if (other.artifactType != null)
+ return false;
+ } else if (!artifactType.equals(other.artifactType))
+ return false;
+ if (artifactUUID == null) {
+ if (other.artifactUUID != null)
+ return false;
+ } else if (!artifactUUID.equals(other.artifactUUID))
+ return false;
+ if (artifactVersion == null) {
+ if (other.artifactVersion != null)
+ return false;
+ } else if (!artifactVersion.equals(other.artifactVersion))
+ return false;
+ if (userIdCreator == null) {
+ if (other.userIdCreator != null)
+ return false;
+ } else if (!userIdCreator.equals(other.userIdCreator))
+ return false;
+ if (userIdLastUpdater == null) {
+ if (other.userIdLastUpdater != null)
+ return false;
+ } else if (!userIdLastUpdater.equals(other.userIdLastUpdater))
+ return false;
+ if (creationDate == null) {
+ if (other.creationDate != null)
+ return false;
+ } else if (!creationDate.equals(other.creationDate))
+ return false;
+ if (creatorFullName == null) {
+ if (other.creatorFullName != null)
+ return false;
+ } else if (!creatorFullName.equals(other.creatorFullName))
+ return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (esId == null) {
+ if (other.esId != null)
+ return false;
+ } else if (!esId.equals(other.esId))
+ return false;
+ if (heatParamsUpdateDate == null) {
+ if (other.heatParamsUpdateDate != null)
+ return false;
+ } else if (!heatParamsUpdateDate.equals(other.heatParamsUpdateDate))
+ return false;
+ if (lastUpdateDate == null) {
+ if (other.lastUpdateDate != null)
+ return false;
+ } else if (!lastUpdateDate.equals(other.lastUpdateDate))
+ return false;
+ if (mandatory == null) {
+ if (other.mandatory != null)
+ return false;
+ } else if (!mandatory.equals(other.mandatory))
+ return false;
+ if (payloadUpdateDate == null) {
+ if (other.payloadUpdateDate != null)
+ return false;
+ } else if (!payloadUpdateDate.equals(other.payloadUpdateDate))
+ return false;
+ if (requiredArtifacts == null) {
+ if (other.requiredArtifacts != null)
+ return false;
+ } else if (!requiredArtifacts.equals(other.requiredArtifacts))
+ return false;
+ if (serviceApi == null) {
+ if (other.serviceApi != null)
+ return false;
+ } else if (!serviceApi.equals(other.serviceApi))
+ return false;
+ if (timeout == null) {
+ if (other.timeout != null)
+ return false;
+ } else if (!timeout.equals(other.timeout))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ if (updaterFullName == null) {
+ if (other.updaterFullName != null)
+ return false;
+ } else if (!updaterFullName.equals(other.updaterFullName))
+ return false;
+ return true;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AttributeDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AttributeDataDefinition.java
new file mode 100644
index 0000000000..b52824697a
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/AttributeDataDefinition.java
@@ -0,0 +1,186 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Represents AttributeDataDefinition
+ *
+ * @author mshitrit
+ *
+ */
+public class AttributeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3046831950009259569L;
+
+ private String uniqueId;
+ private String name;
+ private String type;
+ private String description;
+
+ private String defaultValue;
+ private String value;
+
+ private String status;
+ private SchemaDefinition schema;
+
+ public AttributeDataDefinition() {
+ // Used From Attribute Defenition
+ }
+
+ /**
+ * Clone Constructor
+ *
+ * @param attribute
+ */
+ public AttributeDataDefinition(AttributeDataDefinition attribute) {
+ this.uniqueId = attribute.uniqueId;
+ this.name = attribute.name;
+ this.type = attribute.type;
+ this.description = attribute.description;
+ this.defaultValue = attribute.defaultValue;
+ this.value = attribute.value;
+ this.status = attribute.status;
+ this.schema = attribute.schema;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public SchemaDefinition getSchema() {
+ return schema;
+ }
+
+ public void setSchema(SchemaDefinition entrySchema) {
+ this.schema = entrySchema;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((defaultValue == null) ? 0 : defaultValue.hashCode());
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ result = prime * result + ((status == null) ? 0 : status.hashCode());
+ result = prime * result + ((schema == null) ? 0 : schema.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ boolean equals = true;
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ AttributeDataDefinition other = (AttributeDataDefinition) obj;
+ if (!Objects.equals(defaultValue, other.defaultValue)) {
+ equals = false;
+ } else if (!Objects.equals(value, other.value)) {
+ equals = false;
+ } else if (!Objects.equals(description, other.description)) {
+ equals = false;
+ } else if (!Objects.equals(name, other.name)) {
+ equals = false;
+ } else if (!Objects.equals(type, other.type)) {
+ equals = false;
+ } else if (!Objects.equals(uniqueId, other.uniqueId)) {
+ equals = false;
+ } else if (!Objects.equals(status, other.status)) {
+ equals = false;
+ } else if (!Objects.equals(schema, other.schema)) {
+ equals = false;
+ }
+ return equals;
+ }
+
+ @Override
+ public String toString() {
+ return "AttributeDataDefinition [uniqueId=" + uniqueId + ", name=" + name + ", type=" + type + ", description="
+ + description + ", defaultValue=" + defaultValue + ", value=" + value + ", status=" + status
+ + ", entrySchema=" + schema + "]";
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityTypeDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityTypeDataDefinition.java
new file mode 100644
index 0000000000..f4de5b8c9d
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityTypeDataDefinition.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class CapabilityTypeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 3242123405337612089L;
+
+ private String uniqueId;
+
+ private String description;
+
+ /** Identifies the type of the capability. */
+ private String type;
+
+ private List<String> validSourceTypes;
+
+ private String version;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ // private String derivedFrom;
+
+ public CapabilityTypeDataDefinition(CapabilityTypeDataDefinition cdt) {
+ super();
+ this.uniqueId = cdt.getUniqueId();
+ this.description = cdt.getDescription();
+ this.type = cdt.getType();
+ this.validSourceTypes = cdt.getValidSourceTypes();
+ this.version = cdt.getVersion();
+ this.creationTime = cdt.getCreationTime();
+ this.modificationTime = cdt.getModificationTime();
+ }
+
+ public CapabilityTypeDataDefinition() {
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List<String> getValidSourceTypes() {
+ return validSourceTypes;
+ }
+
+ public void setValidSourceTypes(List<String> validSourceTypes) {
+ this.validSourceTypes = validSourceTypes;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public String toString() {
+ return "CapabilityTypeDataDefinition [uniqueId=" + uniqueId + ", description=" + description + ", type=" + type
+ + ", validSourceTypes=" + validSourceTypes + ", version=" + version + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + "]";
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ComponentInstanceDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ComponentInstanceDataDefinition.java
new file mode 100644
index 0000000000..c9a9516f21
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ComponentInstanceDataDefinition.java
@@ -0,0 +1,187 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum;
+
+public class ComponentInstanceDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 7215033872921497743L;
+
+ private String uniqueId;
+
+ private String name;
+ private String normalizedName;
+
+ private String componentUid;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ private String description;
+
+ private String posX;
+
+ private String posY;
+ private Integer propertyValueCounter = 1;
+ private Integer attributeValueCounter;
+ private Integer inputValueCounter = 1;
+ private OriginTypeEnum originType;
+
+ public ComponentInstanceDataDefinition() {
+ super();
+ }
+
+ public ComponentInstanceDataDefinition(ComponentInstanceDataDefinition dataDefinition) {
+ this.uniqueId = dataDefinition.uniqueId;
+ this.name = dataDefinition.name;
+ this.componentUid = dataDefinition.componentUid;
+ this.creationTime = dataDefinition.creationTime;
+ this.modificationTime = dataDefinition.modificationTime;
+ this.description = dataDefinition.description;
+ this.posX = dataDefinition.posX;
+ this.posY = dataDefinition.posY;
+ this.propertyValueCounter = dataDefinition.propertyValueCounter;
+ this.normalizedName = dataDefinition.normalizedName;
+ this.originType = dataDefinition.originType;
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getPosX() {
+ return posX;
+ }
+
+ public void setPosX(String posX) {
+ this.posX = posX;
+ }
+
+ public String getPosY() {
+ return posY;
+ }
+
+ public void setPosY(String posY) {
+ this.posY = posY;
+ }
+
+ public String getComponentUid() {
+ return componentUid;
+ }
+
+ public void setComponentUid(String resourceUid) {
+ this.componentUid = resourceUid;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getPropertyValueCounter() {
+ return propertyValueCounter;
+ }
+
+ public void setPropertyValueCounter(Integer propertyValueCounter) {
+ this.propertyValueCounter = propertyValueCounter;
+ }
+
+ public String getNormalizedName() {
+ return normalizedName;
+ }
+
+ public void setNormalizedName(String normalizedName) {
+ this.normalizedName = normalizedName;
+ }
+
+ public OriginTypeEnum getOriginType() {
+ return originType;
+ }
+
+ public void setOriginType(OriginTypeEnum originType) {
+ this.originType = originType;
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentInstanceDataDefinition [uniqueId=" + uniqueId + ", name=" + name + ", normalizedName="
+ + normalizedName + ", componentUid=" + componentUid + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + ", description=" + description + ", posX=" + posX
+ + ", posY=" + posY + ", propertyValueCounter=" + propertyValueCounter + ", originType=" + originType
+ + "]";
+ }
+
+ public Integer getAttributeValueCounter() {
+ return attributeValueCounter;
+ }
+
+ public void setAttributeValueCounter(Integer attributeValueCounter) {
+ this.attributeValueCounter = attributeValueCounter;
+ }
+
+ public Integer getInputValueCounter() {
+ return inputValueCounter;
+ }
+
+ public void setInputValueCounter(Integer inputValueCounter) {
+ this.inputValueCounter = inputValueCounter;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ConsumerDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ConsumerDataDefinition.java
new file mode 100644
index 0000000000..6cd7d6c0fb
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ConsumerDataDefinition.java
@@ -0,0 +1,204 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+public class ConsumerDataDefinition {
+
+ // ECOMP Consumer Name - UTF-8 string up to 255 characters containing the
+ // following characters : ( maybe to limit 4-64 chars ? )
+ // Lowercase characters {a-z}
+ // Uppercase characters {A-Z}
+ // Numbers {0-9}
+ // Dash {-}; this character is not supported as the first character in the
+ // user name
+ // Period {.}; this character is not supported as the first character in the
+ // user name
+ // Underscore {_}
+ // * ECOMP Consumer Password - expected to be SHA-2 256 encrypted value (
+ // SALT + "real" password ) => maximal length 256 bytes = 32 characters
+ // Before storing/comparing please convert upper case letter to lower.
+ // The "normalized" encrypted password should match the following format :
+ // [a-z0-9]{32} = alphanumeric string
+ //
+ // * ECOMP Consumer Salt - alphanumeric string [a-z0-9] , length = 32 chars.
+ // * ECOMP Consumer Last Authentication Time ( for future use) -
+ // time when ECOMP Consumer was authenticated for the last time in
+ // milliseconds from 1970 (GMT) - should be set to "0" on creation .
+ // * ECOMP Consumer Details Last updated time - time of the last update in
+ // milliseconds from 1970 (GMT)
+ // * USER_ID - USER_ID of the last user that created/updated credentials (
+ // should be retrieved from USER_ID header)
+ private String consumerName;
+ private String consumerPassword;
+ private String consumerSalt;
+ private Long consumerLastAuthenticationTime;
+ private Long consumerDetailsLastupdatedtime;
+ private String lastModfierAtuid;
+
+ public ConsumerDataDefinition() {
+
+ }
+
+ public ConsumerDataDefinition(ConsumerDataDefinition a) {
+ this.consumerName = a.consumerName;
+ this.consumerPassword = a.consumerPassword;
+ this.consumerSalt = a.consumerSalt;
+ this.consumerLastAuthenticationTime = a.consumerLastAuthenticationTime;
+ this.consumerDetailsLastupdatedtime = a.consumerDetailsLastupdatedtime;
+ this.lastModfierAtuid = a.lastModfierAtuid;
+
+ }
+
+ public String getConsumerName() {
+ return consumerName;
+ }
+
+ public void setConsumerName(String consumerName) {
+ this.consumerName = consumerName;
+ }
+
+ public String getConsumerPassword() {
+ return consumerPassword;
+ }
+
+ public void setConsumerPassword(String consumerPassword) {
+ this.consumerPassword = consumerPassword;
+ }
+
+ public String getConsumerSalt() {
+ return consumerSalt;
+ }
+
+ public void setConsumerSalt(String consumerSalt) {
+ this.consumerSalt = consumerSalt;
+ }
+
+ public Long getConsumerLastAuthenticationTime() {
+ return consumerLastAuthenticationTime;
+ }
+
+ public void setConsumerLastAuthenticationTime(Long consumerLastAuthenticationTime) {
+ this.consumerLastAuthenticationTime = consumerLastAuthenticationTime;
+ }
+
+ public Long getConsumerDetailsLastupdatedtime() {
+ return consumerDetailsLastupdatedtime;
+ }
+
+ public void setConsumerDetailsLastupdatedtime(Long consumerDetailsLastupdatedtime) {
+ this.consumerDetailsLastupdatedtime = consumerDetailsLastupdatedtime;
+ }
+
+ public String getLastModfierAtuid() {
+ return lastModfierAtuid;
+ }
+
+ public void setLastModfierAtuid(String lastModfierAtuid) {
+ this.lastModfierAtuid = lastModfierAtuid;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ConsumerDataDefinition [").append("consumerName=").append(consumerName).append(",")
+ .append("consumerPassword=").append(consumerPassword).append(",").append("consumerSalt=")
+ .append(consumerSalt).append(",").append("consumerLastAuthenticationTime=")
+ .append(consumerLastAuthenticationTime).append(",").append("consumerDetailsLastupdatedtime=")
+ .append(consumerDetailsLastupdatedtime).append(",").append("lastModfierAtuid=").append(lastModfierAtuid)
+ .append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((consumerName == null) ? 0 : consumerName.hashCode());
+ result = prime * result + ((consumerPassword == null) ? 0 : consumerPassword.hashCode());
+ result = prime * result + ((consumerSalt == null) ? 0 : consumerSalt.hashCode());
+ result = prime * result
+ + ((consumerLastAuthenticationTime == null) ? 0 : consumerLastAuthenticationTime.hashCode());
+ result = prime * result
+ + ((consumerDetailsLastupdatedtime == null) ? 0 : consumerDetailsLastupdatedtime.hashCode());
+ result = prime * result + ((lastModfierAtuid == null) ? 0 : lastModfierAtuid.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ConsumerDataDefinition other = (ConsumerDataDefinition) obj;
+ if (consumerName == null) {
+ if (other.consumerName != null) {
+ return false;
+ }
+ } else if (!consumerName.equals(other.consumerName)) {
+ return false;
+ }
+ if (consumerPassword == null) {
+ if (other.consumerPassword != null) {
+ return false;
+ }
+ } else if (!consumerPassword.equals(other.consumerPassword)) {
+ return false;
+ }
+
+ if (consumerSalt == null) {
+ if (other.consumerSalt != null) {
+ return false;
+ }
+ } else if (!consumerSalt.equals(other.consumerSalt)) {
+ return false;
+ }
+
+ if (consumerLastAuthenticationTime == null) {
+ if (other.consumerLastAuthenticationTime != null) {
+ return false;
+ }
+ } else if (!consumerLastAuthenticationTime.equals(other.consumerLastAuthenticationTime)) {
+ return false;
+ }
+
+ if (consumerDetailsLastupdatedtime == null) {
+ if (other.consumerDetailsLastupdatedtime != null) {
+ return false;
+ }
+ } else if (!consumerDetailsLastupdatedtime.equals(other.consumerDetailsLastupdatedtime)) {
+ return false;
+ }
+
+ if (lastModfierAtuid == null) {
+ if (other.lastModfierAtuid != null) {
+ return false;
+ }
+ } else if (!lastModfierAtuid.equals(other.lastModfierAtuid)) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/DataTypeDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/DataTypeDataDefinition.java
new file mode 100644
index 0000000000..bfcfefc433
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/DataTypeDataDefinition.java
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+public class DataTypeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1751768352205892172L;
+
+ private String name;
+
+ private String uniqueId;
+
+ // "boolean", "string", "float", "integer", "version" })
+ private String derivedFromName;
+
+ private String description;
+
+ /**
+ * Timestamp of data type creation
+ */
+ private Long creationTime;
+
+ /**
+ * Timestamp of the data type last update
+ */
+ private Long modificationTime;
+
+ public DataTypeDataDefinition() {
+
+ }
+
+ public DataTypeDataDefinition(DataTypeDataDefinition p) {
+ this.name = p.name;
+ this.uniqueId = p.uniqueId;
+ this.derivedFromName = p.derivedFromName;
+ this.description = p.description;
+ this.creationTime = p.creationTime;
+ this.modificationTime = p.modificationTime;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDerivedFromName() {
+ return derivedFromName;
+ }
+
+ public void setDerivedFromName(String derivedFromName) {
+ this.derivedFromName = derivedFromName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public String toString() {
+ return "DataTypeDataDefinition [name=" + name + ", uniqueId=" + uniqueId + ", derivedFromName="
+ + derivedFromName + ", description=" + description + ", creationTime=" + creationTime
+ + ", modificationTime=" + modificationTime + "]";
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupDataDefinition.java
new file mode 100644
index 0000000000..be3dd8c0ed
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupDataDefinition.java
@@ -0,0 +1,142 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+public class GroupDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1565606165279109427L;
+
+ private String name;
+
+ // the id is unique per group instance on graph.
+ private String uniqueId;
+
+ private String type;
+
+ // version should be changed when there is a change to the group's metadata
+ // or to the groups members
+ // (not necessarily when the VF version is changed). This field cannot be
+ // updated by user
+ private String version;
+
+ // this id is constant and does not changed (also not when changing
+ // version). This field cannot be updated by user
+ private String invariantUUID;
+
+ // the group UUID should be changed when one of the artifacts/component
+ // instances has been changed.
+ private String groupUUID;
+
+ private String description;
+
+ private Integer propertyValueCounter = 0;
+
+ public GroupDataDefinition() {
+
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getInvariantUUID() {
+ return invariantUUID;
+ }
+
+ public void setInvariantUUID(String invariantUUID) {
+ this.invariantUUID = invariantUUID;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Integer getPropertyValueCounter() {
+ return propertyValueCounter;
+ }
+
+ public void setPropertyValueCounter(Integer propertyValueCounter) {
+ this.propertyValueCounter = propertyValueCounter;
+ }
+
+ public String getGroupUUID() {
+ return groupUUID;
+ }
+
+ public void setGroupUUID(String groupUUID) {
+ this.groupUUID = groupUUID;
+ }
+
+ public GroupDataDefinition(GroupDataDefinition other) {
+ this.name = other.name;
+ this.uniqueId = other.uniqueId;
+ this.type = other.type;
+ this.version = other.version;
+ this.invariantUUID = other.invariantUUID;
+ this.description = other.description;
+ this.propertyValueCounter = other.propertyValueCounter;
+ this.groupUUID = other.groupUUID;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupDataDefinition [name=" + name + ", uniqueId=" + uniqueId + ", type=" + type + ", version="
+ + version + ", invariantUUID=" + invariantUUID + ", description=" + description
+ + ", propertyValueCounter=" + propertyValueCounter + ", groupUUID=" + groupUUID + "]";
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupTypeDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupTypeDataDefinition.java
new file mode 100644
index 0000000000..1642d17252
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/GroupTypeDataDefinition.java
@@ -0,0 +1,163 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+public class GroupTypeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -7001817818907172496L;
+
+ private String uniqueId;
+
+ private String type;
+
+ private String version;
+
+ private String derivedFrom;
+
+ private List<String> members;
+
+ private Map<String, String> metadata;
+
+ private String description;
+
+ /**
+ * Timestamp of data type creation
+ */
+ private Long creationTime;
+
+ /**
+ * Timestamp of the data type last update
+ */
+ private Long modificationTime;
+
+ private boolean highestVersion;
+
+ public GroupTypeDataDefinition() {
+
+ }
+
+ public GroupTypeDataDefinition(GroupTypeDataDefinition p) {
+ this.uniqueId = p.uniqueId;
+ this.type = p.type;
+ this.version = p.version;
+ this.members = p.members;
+ this.metadata = p.metadata;
+ // this.derivedFromName = p.derivedFromName;
+ this.description = p.description;
+ this.creationTime = p.creationTime;
+ this.modificationTime = p.modificationTime;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<String> getMembers() {
+ return members;
+ }
+
+ public void setMembers(List<String> members) {
+ this.members = members;
+ }
+
+ public Map<String, String> getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map<String, String> metadata) {
+ this.metadata = metadata;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public String toString() {
+ return "GroupTypeDataDefinition [uniqueId=" + uniqueId + ", type=" + type + ", version=" + version
+ + ", members=" + members + ", metadata=" + metadata + ", description=" + description + ", creationTime="
+ + creationTime + ", modificationTime=" + modificationTime + "]";
+ }
+
+ public String getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(String derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public boolean isHighestVersion() {
+ return highestVersion;
+ }
+
+ public void setHighestVersion(boolean isLatestVersion) {
+ this.highestVersion = isLatestVersion;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/HeatParameterDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/HeatParameterDataDefinition.java
new file mode 100644
index 0000000000..a84485bef6
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/HeatParameterDataDefinition.java
@@ -0,0 +1,162 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+public class HeatParameterDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8995421762148502848L;
+
+ String uniqueId;
+
+ String name;
+ String type;
+ String description;
+
+ String currentValue;
+ String defaultValue;
+
+ public HeatParameterDataDefinition() {
+ }
+
+ public HeatParameterDataDefinition(HeatParameterDataDefinition hp) {
+ this.uniqueId = hp.uniqueId;
+ this.name = hp.name;
+ this.type = hp.type;
+ this.description = hp.description;
+ this.currentValue = hp.currentValue;
+ this.defaultValue = hp.defaultValue;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCurrentValue() {
+ return currentValue;
+ }
+
+ public void setCurrentValue(String currentValue) {
+ this.currentValue = currentValue;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ @Override
+ public String toString() {
+ return "HeatParameterDataDefinition [uniqueId=" + uniqueId + ", name=" + name + ", type=" + type
+ + ", description=" + description + ", currentValue=" + currentValue + ", defaultValue=" + defaultValue
+ + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((currentValue == null) ? 0 : currentValue.hashCode());
+ result = prime * result + ((defaultValue == null) ? 0 : defaultValue.hashCode());
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ HeatParameterDataDefinition other = (HeatParameterDataDefinition) obj;
+ if (currentValue == null) {
+ if (other.currentValue != null)
+ return false;
+ } else if (!currentValue.equals(other.currentValue))
+ return false;
+ if (defaultValue == null) {
+ if (other.defaultValue != null)
+ return false;
+ } else if (!defaultValue.equals(other.defaultValue))
+ return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ return true;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InputsValueDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InputsValueDataDefinition.java
new file mode 100644
index 0000000000..87c163a3ae
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InputsValueDataDefinition.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+public class InputsValueDataDefinition {
+ private String name;
+ private String value;
+ private String uniqueId;
+
+ public InputsValueDataDefinition(String name, String value) {
+ super();
+ this.name = name;
+ this.value = value;
+ }
+
+ public InputsValueDataDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InterfaceDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InterfaceDataDefinition.java
new file mode 100644
index 0000000000..46c1ede031
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/InterfaceDataDefinition.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class InterfaceDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2208369368489725049L;
+
+ private String type;
+
+ private String description;
+
+ private String uniqueId;
+
+ /**
+ * Timestamp of the resource (artifact) creation
+ */
+ private Long creationDate;
+
+ /**
+ * Timestamp of the last resource (artifact) creation
+ */
+ private Long lastUpdateDate;
+
+ public InterfaceDataDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public InterfaceDataDefinition(String type, String description) {
+ super();
+ this.type = type;
+ this.description = description;
+
+ }
+
+ public InterfaceDataDefinition(InterfaceDataDefinition p) {
+ this.uniqueId = p.uniqueId;
+ this.type = p.type;
+ this.description = p.description;
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Long getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Long creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public Long getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(Long lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/OperationDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/OperationDataDefinition.java
new file mode 100644
index 0000000000..12c069b142
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/OperationDataDefinition.java
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+public class OperationDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1951516966187326915L;
+
+ private String uniqueId;
+
+ /**
+ * Timestamp of the resource (artifact) creation
+ */
+ private Long creationDate;
+
+ /**
+ * Timestamp of the last resource (artifact) creation
+ */
+ private Long lastUpdateDate;
+
+ /** Description of the operation. */
+ private String description;
+
+ public OperationDataDefinition() {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ public OperationDataDefinition(String description) {
+ super();
+ this.description = description;
+
+ }
+
+ public OperationDataDefinition(OperationDataDefinition p) {
+ this.uniqueId = p.uniqueId;
+ this.description = p.description;
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Long creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public Long getLastUpdateDate() {
+ return lastUpdateDate;
+ }
+
+ public void setLastUpdateDate(Long lastUpdateDate) {
+ this.lastUpdateDate = lastUpdateDate;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PolicyTypeDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PolicyTypeDataDefinition.java
new file mode 100644
index 0000000000..9b2b0659b1
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PolicyTypeDataDefinition.java
@@ -0,0 +1,163 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+public class PolicyTypeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3783390539788578388L;
+
+ private String uniqueId;
+
+ private String type;
+
+ private String version;
+
+ private String derivedFrom;
+
+ private List<String> targets;
+
+ private Map<String, String> metadata;
+
+ private String description;
+
+ /**
+ * Timestamp of data type creation
+ */
+ private Long creationTime;
+
+ /**
+ * Timestamp of the data type last update
+ */
+ private Long modificationTime;
+
+ private boolean highestVersion;
+
+ public PolicyTypeDataDefinition() {
+
+ }
+
+ public PolicyTypeDataDefinition(PolicyTypeDataDefinition p) {
+ this.uniqueId = p.uniqueId;
+ this.type = p.type;
+ this.version = p.version;
+ this.targets = p.targets;
+ this.metadata = p.metadata;
+ // this.derivedFromName = p.derivedFromName;
+ this.description = p.description;
+ this.creationTime = p.creationTime;
+ this.modificationTime = p.modificationTime;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<String> getTargets() {
+ return targets;
+ }
+
+ public void setTargets(List<String> members) {
+ this.targets = members;
+ }
+
+ public Map<String, String> getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map<String, String> metadata) {
+ this.metadata = metadata;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public String toString() {
+ return "PolicyTypeDataDefinition [uniqueId=" + uniqueId + ", type=" + type + ", version=" + version
+ + ", targets=" + targets + ", metadata=" + metadata + ", description=" + description + ", creationTime="
+ + creationTime + ", modificationTime=" + modificationTime + "]";
+ }
+
+ public String getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setDerivedFrom(String derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public boolean isHighestVersion() {
+ return highestVersion;
+ }
+
+ public void setHighestVersion(boolean isLatestVersion) {
+ this.highestVersion = isLatestVersion;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ProductMetadataDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ProductMetadataDataDefinition.java
new file mode 100644
index 0000000000..3f044c1d46
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/ProductMetadataDataDefinition.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.components.ComponentMetadataDataDefinition;
+
+public class ProductMetadataDataDefinition extends ComponentMetadataDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -1877577227290771160L;
+
+ private String fullName;
+
+ private List<String> contacts;
+
+ private Boolean isActive;
+
+ public ProductMetadataDataDefinition() {
+ super();
+ }
+
+ public ProductMetadataDataDefinition(ProductMetadataDataDefinition other) {
+ super(other);
+ }
+
+ public Boolean getIsActive() {
+ return isActive;
+ }
+
+ public void setIsActive(Boolean active) {
+ isActive = active;
+ }
+
+ public List<String> getContacts() {
+ return contacts;
+ }
+
+ public void setContacts(List<String> contacts) {
+ this.contacts = contacts;
+ }
+
+ public void addContact(String contact) {
+ if (contact != null) {
+ if (contacts == null) {
+ contacts = new ArrayList<>();
+ }
+ contacts.add(contact);
+ }
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ public void setFullName(String fullName) {
+ this.fullName = fullName;
+ }
+
+ @Override
+ public String toString() {
+ return "ProductMetadataDataDefinition [fullName=" + fullName + ", contacts=" + contacts + ", isActive="
+ + isActive + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+
+ result = prime * result + ((contacts == null) ? 0 : contacts.hashCode());
+ result = prime * result + ((fullName == null) ? 0 : fullName.hashCode());
+ result = prime * result + ((isActive == null) ? 0 : isActive.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ProductMetadataDataDefinition other = (ProductMetadataDataDefinition) obj;
+ if (contacts == null) {
+ if (other.contacts != null)
+ return false;
+ } else if (!contacts.equals(other.contacts))
+ return false;
+ if (fullName == null) {
+ if (other.fullName != null)
+ return false;
+ } else if (!fullName.equals(other.fullName))
+ return false;
+ if (isActive == isActive) {
+ if (other.isActive != null)
+ return false;
+ } else if (!isActive.equals(other.isActive))
+ return false;
+ return super.equals(obj);
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyDataDefinition.java
new file mode 100644
index 0000000000..c522f3e0e9
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyDataDefinition.java
@@ -0,0 +1,195 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+
+public class PropertyDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 5798685557528432389L;
+
+ private String uniqueId;
+
+ // "boolean", "string", "float", "integer", "version" })
+ private String type;
+
+ private Boolean required;
+
+ protected boolean definition = false;
+
+ private String defaultValue;
+
+ private String description;
+
+ private SchemaDefinition schema;
+
+ private boolean password;
+
+ public PropertyDataDefinition() {
+
+ }
+
+ public PropertyDataDefinition(PropertyDataDefinition p) {
+ this.uniqueId = p.uniqueId;
+ this.required = p.required;
+ this.defaultValue = p.defaultValue;
+ this.description = p.description;
+ this.schema = p.schema;
+ this.password = p.password;
+ this.type = p.type;
+ }
+
+ // @Override
+ public boolean isDefinition() {
+ return true;
+ }
+
+ public void setDefinition(boolean definition) {
+ this.definition = definition;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Boolean isRequired() {
+ return required;
+ }
+
+ public void setRequired(Boolean required) {
+ this.required = required;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public boolean isPassword() {
+ return password;
+ }
+
+ public void setPassword(boolean password) {
+ this.password = password;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public SchemaDefinition getSchema() {
+ return schema;
+ }
+
+ public void setSchema(SchemaDefinition entrySchema) {
+ this.schema = entrySchema;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyDataDefinition [uniqueId=" + uniqueId + ", type=" + type + ", required=" + required
+ + ", defaultValue=" + defaultValue + ", description=" + description + ", entrySchema=" + schema
+ + ", password=" + password + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((defaultValue == null) ? 0 : defaultValue.hashCode());
+ result = prime * result + (definition ? 1231 : 1237);
+ result = prime * result + ((description == null) ? 0 : description.hashCode());
+ result = prime * result + (password ? 1231 : 1237);
+ result = prime * result + ((required == null) ? 0 : required.hashCode());
+ result = prime * result + ((schema == null) ? 0 : schema.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ PropertyDataDefinition other = (PropertyDataDefinition) obj;
+ if (defaultValue == null) {
+ if (other.defaultValue != null)
+ return false;
+ } else if (!defaultValue.equals(other.defaultValue))
+ return false;
+ if (definition != other.definition)
+ return false;
+ if (description == null) {
+ if (other.description != null)
+ return false;
+ } else if (!description.equals(other.description))
+ return false;
+ if (password != other.password)
+ return false;
+ if (required == null) {
+ if (other.required != null)
+ return false;
+ } else if (!required.equals(other.required))
+ return false;
+ if (schema == null) {
+ if (other.schema != null)
+ return false;
+ } else if (!schema.equals(other.schema))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ if (uniqueId == null) {
+ if (other.uniqueId != null)
+ return false;
+ } else if (!uniqueId.equals(other.uniqueId))
+ return false;
+ return true;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyRule.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyRule.java
new file mode 100644
index 0000000000..c9ef85ac66
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/PropertyRule.java
@@ -0,0 +1,136 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.List;
+import org.codehaus.jackson.annotate.JsonIgnore;
+
+public class PropertyRule implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -3357933382124599996L;
+
+ public static String FORCE_ALL = "FORCE_ALL";
+ public static String ALL = "ALL";
+ public static String RULE_ANY_MATCH = ".+";
+
+ List<String> rule;
+ String value;
+
+ public PropertyRule() {
+ super();
+ }
+
+ public PropertyRule(List<String> rule, String value) {
+ super();
+ this.rule = rule;
+ this.value = value;
+ }
+
+ public List<String> getRule() {
+ return rule;
+ }
+
+ public void setRule(List<String> rule) {
+ this.rule = rule;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ @JsonIgnore
+ public String getFirstToken() {
+ return getToken(1);
+ }
+
+ public String getToken(int tokenNumber) {
+
+ int index = tokenNumber - 1;
+ if (rule == null || index >= rule.size() || index < 0) {
+ return null;
+ }
+
+ return rule.get(index);
+ }
+
+ @JsonIgnore
+ public int getRuleSize() {
+ if (rule == null) {
+ return 0;
+ }
+ return rule.size();
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyRule [rule=" + rule + ", value=" + value + "]";
+ }
+
+ public boolean compareRule(PropertyRule comparedPropertyRule) {
+
+ if (comparedPropertyRule == null) {
+ return false;
+ }
+
+ List<String> comparedRule = comparedPropertyRule.getRule();
+ if (rule == null && comparedRule == null) {
+ return true;
+ }
+
+ if (rule != null && comparedRule != null) {
+ if (rule.size() != comparedRule.size()) {
+ return false;
+ } else {
+ int size = rule.size();
+ boolean isEqual = true;
+ for (int i = 0; i < size; i++) {
+ String item = rule.get(i);
+ String comparedItem = comparedRule.get(i);
+ if (item == null || false == item.equals(comparedItem)) {
+ isEqual = false;
+ break;
+ }
+ }
+ return isEqual;
+ }
+ } else {
+ return false;
+ }
+
+ }
+
+ public void replaceFirstToken(String token) {
+
+ if (rule != null && rule.size() > 0) {
+ rule.set(0, token);
+ }
+
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipTypeDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipTypeDataDefinition.java
new file mode 100644
index 0000000000..0f7c56842f
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/RelationshipTypeDataDefinition.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class RelationshipTypeDataDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1104043358598884458L;
+
+ private String uniqueId;
+
+ private String description;
+
+ /** Identifies the type of the capability. */
+ private String type;
+
+ private List<String> validSourceTypes;
+
+ private String version;
+
+ private Long creationTime;
+
+ private Long modificationTime;
+
+ // private String derivedFrom;
+
+ public RelationshipTypeDataDefinition(RelationshipTypeDataDefinition cdt) {
+ super();
+ this.uniqueId = cdt.getUniqueId();
+ this.description = cdt.getDescription();
+ this.type = cdt.getType();
+ this.validSourceTypes = cdt.getValidSourceTypes();
+ this.version = cdt.getVersion();
+ this.creationTime = cdt.getCreationTime();
+ this.modificationTime = cdt.getModificationTime();
+ }
+
+ public RelationshipTypeDataDefinition() {
+
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public List<String> getValidSourceTypes() {
+ return validSourceTypes;
+ }
+
+ public void setValidSourceTypes(List<String> validSourceTypes) {
+ this.validSourceTypes = validSourceTypes;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Long getCreationTime() {
+ return creationTime;
+ }
+
+ public void setCreationTime(Long creationTime) {
+ this.creationTime = creationTime;
+ }
+
+ public Long getModificationTime() {
+ return modificationTime;
+ }
+
+ public void setModificationTime(Long modificationTime) {
+ this.modificationTime = modificationTime;
+ }
+
+ @Override
+ public String toString() {
+ return "RelationshipTypeDataDefinition [uniqueId=" + uniqueId + ", description=" + description + ", type="
+ + type + ", validSourceTypes=" + validSourceTypes + ", version=" + version + ", creationTime="
+ + creationTime + ", modificationTime=" + modificationTime + "]";
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SchemaDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SchemaDefinition.java
new file mode 100644
index 0000000000..3845a706b1
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/SchemaDefinition.java
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.elements;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Schema allows to create new types that can be used along TOSCA definitions.
+ */
+public class SchemaDefinition implements Serializable {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 7117891081909380577L;
+
+ private String derivedFrom;
+ private List<String> constraints;
+ private Map<String, PropertyDataDefinition> properties;
+
+ private PropertyDataDefinition property;
+
+ public SchemaDefinition() {
+
+ }
+
+ public SchemaDefinition(String derivedFrom, List<String> constraints,
+ Map<String, PropertyDataDefinition> properties) {
+ this.derivedFrom = derivedFrom;
+ this.constraints = constraints;
+ this.properties = properties;
+ }
+
+ public String getDerivedFrom() {
+ return derivedFrom;
+ }
+
+ public void setProperty(PropertyDataDefinition property) {
+ this.property = property;
+ }
+
+ public PropertyDataDefinition getProperty() {
+ return this.property;
+ }
+
+ public void setDerivedFrom(String derivedFrom) {
+ this.derivedFrom = derivedFrom;
+ }
+
+ public List<String> getConstraints() {
+ return constraints;
+ }
+
+ public void setConstraints(List<String> constraints) {
+ this.constraints = constraints;
+ }
+
+ public Map<String, PropertyDataDefinition> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(Map<String, PropertyDataDefinition> properties) {
+ this.properties = properties;
+ }
+
+ public void addProperty(String key, PropertyDataDefinition property) {
+
+ properties.put(key, property);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((constraints == null) ? 0 : constraints.hashCode());
+ result = prime * result + ((derivedFrom == null) ? 0 : derivedFrom.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + ((property == null) ? 0 : property.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SchemaDefinition other = (SchemaDefinition) obj;
+ if (constraints == null) {
+ if (other.constraints != null)
+ return false;
+ } else if (!constraints.equals(other.constraints))
+ return false;
+ if (derivedFrom == null) {
+ if (other.derivedFrom != null)
+ return false;
+ } else if (!derivedFrom.equals(other.derivedFrom))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ if (property == null) {
+ if (other.property != null)
+ return false;
+ } else if (!property.equals(other.property))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SchemaDefinition [" + "derivedFrom='" + derivedFrom + ", constraints=" + constraints + ", properties="
+ + properties + ", property=" + property + ']';
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/AssetTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/AssetTypeEnum.java
new file mode 100644
index 0000000000..e130671572
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/AssetTypeEnum.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+public enum AssetTypeEnum {
+ RESOURCES("resources", "Resource"), SERVICES("services", "Service"), PRODUCTS("products", "Product");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getCorrespondingComponent() {
+ return correspondingComponent;
+ }
+
+ private String correspondingComponent;
+
+ private AssetTypeEnum(String value, String correspondingComponent) {
+ this.value = value;
+ this.correspondingComponent = correspondingComponent;
+ }
+
+ public static ComponentTypeEnum convertToComponentTypeEnum(String assetType) {
+ ComponentTypeEnum ret = null;
+ for (AssetTypeEnum curr : AssetTypeEnum.values()) {
+ if (curr.value.equals(assetType)) {
+ return ComponentTypeEnum.findByValue(curr.correspondingComponent);
+ }
+ }
+
+ return ret;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ComponentTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ComponentTypeEnum.java
new file mode 100644
index 0000000000..9da0b81d3b
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ComponentTypeEnum.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+public enum ComponentTypeEnum {
+ RESOURCE("Resource"), SERVICE("Service"), RESOURCE_INSTANCE("Resource Instance"), PRODUCT("Product"), SERVICE_INSTANCE("Service Instance");
+
+ private String value;
+
+ private ComponentTypeEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ // Those values cannot be another field in enum, because they are needed
+ // as constants for Swagger allowedValues param
+ public static final String RESOURCE_PARAM_NAME = "resources";
+ public static final String SERVICE_PARAM_NAME = "services";
+ public static final String PRODUCT_PARAM_NAME = "products";
+
+ public NodeTypeEnum getNodeType() {
+
+ switch (this) {
+ case RESOURCE:
+ return NodeTypeEnum.Resource;
+ case SERVICE:
+ return NodeTypeEnum.Service;
+ case PRODUCT:
+ return NodeTypeEnum.Product;
+ case RESOURCE_INSTANCE:
+ return NodeTypeEnum.ResourceInstance;
+ default:
+ throw new UnsupportedOperationException("No nodeType is defined for: " + this.getValue());
+ }
+ }
+
+ public static ComponentTypeEnum findByValue(String value) {
+ ComponentTypeEnum ret = null;
+ for (ComponentTypeEnum curr : ComponentTypeEnum.values()) {
+ if (curr.getValue().equals(value)) {
+ ret = curr;
+ return ret;
+ }
+ }
+ return ret;
+ }
+
+ public static ComponentTypeEnum findByParamName(String paramName) {
+ ComponentTypeEnum ret = null;
+ switch (paramName) {
+ case RESOURCE_PARAM_NAME:
+ ret = RESOURCE;
+ break;
+ case SERVICE_PARAM_NAME:
+ ret = SERVICE;
+ break;
+ case PRODUCT_PARAM_NAME:
+ ret = PRODUCT;
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ public static String findParamByType(ComponentTypeEnum type) {
+ String ret = null;
+ switch (type) {
+ case RESOURCE:
+ ret = RESOURCE_PARAM_NAME;
+ break;
+ case SERVICE:
+ ret = SERVICE_PARAM_NAME;
+ break;
+ case PRODUCT:
+ ret = PRODUCT_PARAM_NAME;
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/FilterKeyEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/FilterKeyEnum.java
new file mode 100644
index 0000000000..bf8ae79467
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/FilterKeyEnum.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public enum FilterKeyEnum {
+ SUB_CATEGORY("subCategory"), CATEGORY("category"), DISTRIBUTION_STATUS("distributionStatus");
+
+ private String name;
+
+ FilterKeyEnum(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static List<String> getAllFilters() {
+ return Arrays.stream(FilterKeyEnum.values()).map(f -> f.getName()).collect(Collectors.toList());
+ }
+
+ public static List<String> getValidFiltersByAssetType(ComponentTypeEnum assetType) {
+ switch (assetType) {
+ case RESOURCE:
+ return getAllFilters().subList(0, 2);
+ case SERVICE:
+ return getAllFilters().subList(1, 3);
+ default:
+ return null;
+ }
+
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/GroupTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/GroupTypeEnum.java
new file mode 100644
index 0000000000..e5783e4a28
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/GroupTypeEnum.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+public enum GroupTypeEnum {
+ VF_MODULE("org.openecomp.groups.VfModule"), HEAT_STACK("org.openecomp.groups.HeatStack");
+
+ private String groupTypeName;
+
+ GroupTypeEnum(String groupTypeName) {
+ this.groupTypeName = groupTypeName;
+ }
+
+ public String getGroupTypeName() {
+ return groupTypeName;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java
new file mode 100644
index 0000000000..2938b4576a
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+public enum NodeTypeEnum {
+ User("user"),
+ Service("service"),
+ Resource("resource"),
+ Product("product"),
+ ResourceCategory("resourceCategory"),
+ ServiceCategory("serviceCategory"),
+ ServiceNewCategory("serviceNewCategory"),
+ ResourceNewCategory("resourceNewCategory"),
+ ProductCategory("productCategory"),
+ ResourceSubcategory("resourceSubcategory"),
+ ProductSubcategory("productSubcategory"),
+ ProductGrouping("productGrouping"),
+ Tag("tag"),
+ Property("property"),
+ Attribute("attribute"),
+ CapabilityType("capabilityType"),
+ Requirement("requirement"),
+ RelationshipType("relationshipType"),
+ Capability("capability"),
+ RequirementImpl("requirementImpl"),
+ CapabilityInst("capabilityInst"),
+ AttributeValue("attributeValue"),
+ InputValue("inputValue"),
+ PropertyValue("propertyValue"),
+ LockNode("lockNode"),
+ ArtifactRef("artifactRef"),
+ Interface("interface"),
+ InterfaceOperation("interfaceOperation"),
+ ResourceInstance("resourceInstance"),
+ RelationshipInst("relationshipInst"),
+ AdditionalInfoParameters("additionalInfoParameters"),
+ ConsumerCredentials("consumerCredentials"),
+ HeatParameter("heatParameter"),
+ HeatParameterValue("heatParameterValue"),
+ DataType("dataType"),
+ GroupType("groupType"),
+ PolicyType("policyType"),
+ Group("group"),
+ UserFunctionalMenu("userFunctionalMenu"),
+ Input("input");
+
+ private String name;
+
+ NodeTypeEnum(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public static NodeTypeEnum getByName(String name) {
+ for (NodeTypeEnum inst : NodeTypeEnum.values()) {
+ if (inst.getName().equals(name)) {
+ return inst;
+ }
+ }
+ return null;
+ }
+
+ public static NodeTypeEnum getByNameIgnoreCase(String name) {
+ for (NodeTypeEnum inst : NodeTypeEnum.values()) {
+ if (inst.getName().equalsIgnoreCase(name)) {
+ return inst;
+ }
+ }
+ return null;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/OriginTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/OriginTypeEnum.java
new file mode 100644
index 0000000000..14b3e4ef86
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/OriginTypeEnum.java
@@ -0,0 +1,69 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+public enum OriginTypeEnum {
+ PRODUCT("Product", "Product", "product instance", ComponentTypeEnum.PRODUCT),
+ SERVICE("Service", "Service", "service instance", ComponentTypeEnum.SERVICE),
+ VF("VF", "VF (Virtual Function)", "resource instance", ComponentTypeEnum.RESOURCE),
+ VFC("VFC", "VFC (Virtual Function Component)","resource instance",ComponentTypeEnum.RESOURCE),
+ CP("CP", "CP (Connection Point)", "resource instance", ComponentTypeEnum.RESOURCE),
+ VL("VL", "VL (Virtual Link)", "resource instance", ComponentTypeEnum.RESOURCE);
+
+ private String value;
+ private String displayValue;
+ private String instanceType;
+ private ComponentTypeEnum componentType;
+
+ private OriginTypeEnum(String value, String displayValue, String instanceType, ComponentTypeEnum componentType) {
+ this.value = value;
+ this.displayValue = displayValue;
+ this.instanceType = instanceType;
+ this.componentType = componentType;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getDisplayValue() {
+ return displayValue;
+ }
+
+ public String getInstanceType() {
+ return instanceType;
+ }
+
+ public ComponentTypeEnum getComponentType() {
+ return componentType;
+ }
+
+ public static OriginTypeEnum findByValue(String value) {
+ OriginTypeEnum ret = null;
+ for (OriginTypeEnum curr : OriginTypeEnum.values()) {
+ if (curr.getValue().equals(value)) {
+ ret = curr;
+ break;
+ }
+ }
+ return ret;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ResourceTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ResourceTypeEnum.java
new file mode 100644
index 0000000000..7864a2cf5f
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/ResourceTypeEnum.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.datatypes.enums;
+
+public enum ResourceTypeEnum {
+
+ VFC("VFC (Virtual Function Component)"),
+ VF("VF"/* (Virtual Function)" */),
+ CP("CP (Connection Point)"),
+ VL("VL (Virtual Link)");
+
+ String value;
+
+ private ResourceTypeEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static boolean contains(String type) {
+
+ for (ResourceTypeEnum e : ResourceTypeEnum.values()) {
+ if (e.name().equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/utils/CommonBeUtils.java b/common-be/src/main/java/org/openecomp/sdc/be/utils/CommonBeUtils.java
new file mode 100644
index 0000000000..54a55811e3
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/utils/CommonBeUtils.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.utils;
+
+import org.openecomp.sdc.common.api.Constants;
+
+public class CommonBeUtils {
+ /**
+ * Compares two ASDC versions of a component. It's for internal usage, so
+ * the assumption is that the versions are in valid format.
+ *
+ * @param firstVersion
+ * - version in format major.minor or just major (e.g, 2.0 or 2)
+ * @param secondVersion
+ * - version in format major.minor or just major (e.g, 2.0 or 2)
+ * @return Returns true iff:<br>
+ * 1) first version's major number is higher than second's (e.g.,
+ * firstVersion = 1.1, secondVersion = 0.3)<br>
+ * 2) major version are equal, but first's minor version is higher
+ * than second's (e.g., firstVersion = 0.10, secondVersion = 0.9)
+ * <br>
+ */
+ public static boolean compareAsdcComponentVersions(String firstVersion, String secondVersion) {
+ String[] firstVersionNums = firstVersion.split("\\.");
+ String[] secondVersionNums = secondVersion.split("\\.");
+ int firstMajor = Integer.parseInt(firstVersionNums[0]);
+ int secondMajor = Integer.parseInt(secondVersionNums[0]);
+ int compareRes = Integer.compare(firstMajor, secondMajor);
+ if (compareRes == 0) {
+ int firstMinor = (firstVersionNums.length == 2 ? Integer.parseInt(firstVersionNums[1]) : 0);
+ int secondMinor = (secondVersionNums.length == 2 ? Integer.parseInt(secondVersionNums[1]) : 0);
+ compareRes = Integer.compare(firstMinor, secondMinor);
+ return (compareRes > 0);
+ } else {
+ return (compareRes > 0);
+ }
+ }
+
+ public static String generateToscaResourceName(String resourceType, String resourceSystemName) {
+ return Constants.USER_DEFINED_RESOURCE_NAMESPACE_PREFIX + resourceType.toLowerCase() + "." + resourceSystemName;
+ }
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/workers/Job.java b/common-be/src/main/java/org/openecomp/sdc/be/workers/Job.java
new file mode 100644
index 0000000000..68929aa61d
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/workers/Job.java
@@ -0,0 +1,29 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.workers;
+
+/**
+ * Created by michael on 6/24/2016.
+ */
+public abstract class Job<E> {
+ public abstract E doWork();
+
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/workers/Main.java b/common-be/src/main/java/org/openecomp/sdc/be/workers/Main.java
new file mode 100644
index 0000000000..cf2bc54fb6
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/workers/Main.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.workers;
+
+/**
+ * Created by michael on 6/24/2016.
+ */
+public class Main {
+
+ public static void main(String[] args) {
+
+ Manager manger = new Manager();
+ manger.init(2);
+ manger.addJob(new Job() {
+ @Override
+ public String doWork() {
+ return "go";
+ }
+ });
+ manger.addJob(new Job() {
+ @Override
+ public String doWork() {
+ return "go go";
+ }
+ });
+ // try {
+ System.out.println(manger.start());
+ // } catch (InterruptedException e) {
+ // e.printStackTrace();
+ // }
+ }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/workers/Manager.java b/common-be/src/main/java/org/openecomp/sdc/be/workers/Manager.java
new file mode 100644
index 0000000000..76d2cc5a6d
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/workers/Manager.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.workers;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.concurrent.*;
+
+/**
+ * Created by michael on 6/24/2016.
+ */
+public class Manager<T extends Job, E> {
+ private static Logger log = LoggerFactory.getLogger(Manager.class.getName());
+ private ExecutorService executor;
+ private LinkedBlockingQueue<T> inputQueue;
+ private LinkedBlockingQueue<E> outputQueue;
+ private int numberOfWorkers;
+
+ public void init(int numberOfWorkers) {
+ log.debug("initializing workers, creating {} workers", numberOfWorkers);
+ this.numberOfWorkers = numberOfWorkers;
+ final ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Worker-%d").build();
+ this.executor = Executors.newFixedThreadPool(numberOfWorkers, threadFactory);
+ this.inputQueue = new LinkedBlockingQueue<T>();
+ this.outputQueue = new LinkedBlockingQueue<E>();
+ }
+
+ public void addJob(T job) {
+ log.trace("job add to input queue");
+ this.inputQueue.add(job);
+ }
+
+ public LinkedBlockingQueue<E> start() {
+ for (int i = 0; i < numberOfWorkers; i++) {
+ String workerName = "worker-" + i;
+ log.debug("starting worker:{}", workerName);
+ this.executor.submit(new Worker(workerName, this.inputQueue, this.outputQueue));
+ }
+ executor.shutdown();
+ try {
+ if (!executor.awaitTermination(30, TimeUnit.MINUTES)) {
+ log.error("timer elapsed while waiting for the worker's to finish. ");
+ }
+ log.debug("all workers finished");
+ } catch (InterruptedException e) {
+ log.error("failed while waiting for", e);
+ }
+ return outputQueue;
+ }
+
+ //
+ // public static void main(String[] args) {
+ // ExecutorService executor = Executors.newFixedThreadPool(NTHREDS);
+ // for (int i = 0; i < 500; i++) {
+ // Runnable worker = new MyRunnable(10000000L + i);
+ // executor.execute(worker);
+ // }
+ // // This will make the executor accept no new threads
+ // // and finish all existing threads in the queue
+ // executor.shutdown();
+ // // Wait until all threads are finish
+ // executor.awaitTermination();
+ // System.out.println("Finished all threads");
+ // }
+ // }
+}
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/workers/Worker.java b/common-be/src/main/java/org/openecomp/sdc/be/workers/Worker.java
new file mode 100644
index 0000000000..83be4b2ee5
--- /dev/null
+++ b/common-be/src/main/java/org/openecomp/sdc/be/workers/Worker.java
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.workers;
+
+import org.openecomp.sdc.be.config.BeEcompErrorManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by michael on 6/24/2016.
+ */
+public class Worker<T extends Job<E>, E> implements Runnable {
+
+ private String workerName;
+ private LinkedBlockingQueue<T> inputQueue;
+
+ private LinkedBlockingQueue<E> outputQueue;
+
+ private static Logger log = LoggerFactory.getLogger(Worker.class.getName());
+
+ public Worker(String workerName, LinkedBlockingQueue<T> inputQueue, LinkedBlockingQueue<E> outputQueue) {
+ this.workerName = workerName;
+ this.inputQueue = inputQueue;
+ this.outputQueue = outputQueue;
+ }
+
+ @Override
+ public void run() {
+
+ try {
+ while (true) {
+ log.trace("worker:{} doing work", workerName);
+ T job = inputQueue.poll(500, TimeUnit.MILLISECONDS);
+ if (job == null) {
+
+ log.debug("worker:{} nothing to do");
+ break;
+ }
+ this.outputQueue.put(job.doWork());
+ log.trace("worker:{} done with work", workerName);
+ }
+ } catch (Exception e) {
+ BeEcompErrorManager.getInstance().logInternalFlowError("executingJobFailed",
+ "failed during job execution worker" + workerName, BeEcompErrorManager.ErrorSeverity.ERROR);
+ log.debug("worker:" + workerName + " nothing to do stoping", e);
+ }
+ }
+
+}
diff --git a/common-be/src/test/java/org/openecomp/sdc/be/utils/CommonBeUtilsTest.java b/common-be/src/test/java/org/openecomp/sdc/be/utils/CommonBeUtilsTest.java
new file mode 100644
index 0000000000..9c3582ba8a
--- /dev/null
+++ b/common-be/src/test/java/org/openecomp/sdc/be/utils/CommonBeUtilsTest.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.be.utils;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+import org.openecomp.sdc.be.utils.CommonBeUtils;
+
+public class CommonBeUtilsTest {
+
+ @Test
+ public void testCompareAsdcComponentVersions() {
+
+ assertTrue(CommonBeUtils.compareAsdcComponentVersions("1.1", "0.15"));
+ assertFalse(CommonBeUtils.compareAsdcComponentVersions("0.5", "0.5"));
+ assertFalse(CommonBeUtils.compareAsdcComponentVersions("0.5", "0.6"));
+ assertFalse(CommonBeUtils.compareAsdcComponentVersions("1.5", "2.6"));
+ assertTrue(CommonBeUtils.compareAsdcComponentVersions("0.10", "0.1"));
+ assertFalse(CommonBeUtils.compareAsdcComponentVersions("1", "1.0"));
+ assertTrue(CommonBeUtils.compareAsdcComponentVersions("2", "1.15"));
+ }
+}
diff --git a/distribution-ci/.gitignore b/distribution-ci/.gitignore
new file mode 100644
index 0000000000..ae3c172604
--- /dev/null
+++ b/distribution-ci/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/distribution-ci/etc/asdc-client.jks b/distribution-ci/etc/asdc-client.jks
new file mode 100644
index 0000000000..eb0a0d35af
--- /dev/null
+++ b/distribution-ci/etc/asdc-client.jks
Binary files differ
diff --git a/distribution-ci/etc/asdcclientstore.jks b/distribution-ci/etc/asdcclientstore.jks
new file mode 100644
index 0000000000..5dc006db0c
--- /dev/null
+++ b/distribution-ci/etc/asdcclientstore.jks
Binary files differ
diff --git a/distribution-ci/log4j.properties b/distribution-ci/log4j.properties
new file mode 100644
index 0000000000..80b2ca8825
--- /dev/null
+++ b/distribution-ci/log4j.properties
@@ -0,0 +1,17 @@
+log4j.rootCategory=DEBUG, CONSOLE, LOGFILE
+
+log4j.logger.org.openecomp.sdc=TRACE, CONSOLE, LOGFILE
+
+# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS Z} %c{1} - %m%n
+
+# LOGFILE is set to be a File appender using a PatternLayout.
+log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
+log4j.appender.LOGFILE.File=logs/wordnik.log
+log4j.appender.LOGFILE.Append=true
+log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.LOGFILE.layout.ConversionPattern=%p %d{yyyy-MM-dd HH:mm:ss.SSS Z} %c{1} - %m%n
+log4j.appender.LOGFILE.MaxFileSize=10MB
+log4j.appender.LOGFILE.MaxBackupIndex=10
diff --git a/distribution-ci/pom.xml b/distribution-ci/pom.xml
new file mode 100644
index 0000000000..f770a34ee9
--- /dev/null
+++ b/distribution-ci/pom.xml
@@ -0,0 +1,64 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.openecomp.sdc.sdc-main</groupId>
+ <artifactId>distribution-ci</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1610.1.3</version>
+ </parent>
+
+
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20131018</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-distribution-client</artifactId>
+ <version>1.1.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>asdc-tests</artifactId>
+ <version>${asdc.full.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+
+ <!-- ================================================== -->
+ <!-- Set the JDK compiler version. -->
+ <!-- ================================================== -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <inherited>true</inherited>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/distribution-ci/src/main/java/org/openecomp/test/BaseInit.java b/distribution-ci/src/main/java/org/openecomp/test/BaseInit.java
new file mode 100644
index 0000000000..b056b35b75
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/BaseInit.java
@@ -0,0 +1,122 @@
+package org.openecomp.test;
+
+//import static org.junit.Assert.assertNotNull;
+//import static org.junit.Assert.assertTrue;
+//
+//import java.io.IOException;
+//
+//import org.junit.Before;
+//import org.junit.rules.TestName;
+//
+//import org.openecomp.sdc.be.model.User;
+//import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+//import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+//import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+//import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+//import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+//import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+////import org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest;
+//import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+//import org.openecomp.sdc.ci.tests.datatypes.ComponentInstanceReqDetails;
+//import org.openecomp.sdc.ci.tests.utils.ArtifactUtils;
+//import org.openecomp.sdc.ci.tests.utils.DbUtils;
+////import org.openecomp.sdc.ci.tests.utils.ResourceUtils;
+////import org.openecomp.sdc.ci.tests.utils.ServiceUtils;
+//import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+//import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+//import org.openecomp.sdc.ci.tests.utils.rest.ArtifactRestUtils;
+//import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+//import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+//import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+
+public abstract class BaseInit {
+
+
+// protected ResourceReqDetails resourceDetails;
+// protected ResourceReqDetails resourceDetails1;
+// protected ServiceReqDetails serviceDetails;
+// protected ServiceReqDetails serviceDetails2;
+// protected ComponentInstanceReqDetails resourceInstanceReqDetails;
+// protected ComponentInstanceReqDetails resourceInstanceReqDetails2;
+// protected User sdncDesignerDetails1;
+// protected User sdncTesterDeatails1;
+// protected User sdncAdminDetails1;
+// protected ArtifactReqDetails heatArtifactDetails;
+//
+// protected ArtifactReqDetails defaultArtifactDetails;
+// //protected ResourceUtils resourceUtils;
+// protected ArtifactUtils artifactUtils;
+//
+//
+// //static ServiceUtils serviceUtils = new ServiceUtils();
+// public BaseInit(TestName testName, String className) {
+// super(testName, className);
+// }
+// @Before
+// public void before() throws Exception{
+//
+// initializeMembers();
+//
+// createComponents();
+//
+// }
+// public void initializeMembers() throws IOException, Exception {
+//
+// resourceDetails = ElementFactory.getDefaultResource();
+// resourceDetails1 = ElementFactory.getDefaultResource("secondResource", NormativeTypesEnum.ROOT);
+// serviceDetails = ElementFactory.getDefaultService();
+// serviceDetails2 = ElementFactory.getDefaultService("newTestService2", ServiceCategoriesEnum.MOBILITY, "al1976");
+// sdncDesignerDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.DESIGNER);
+// sdncTesterDeatails1 = ElementFactory.getDefaultUser(UserRoleEnum.TESTER);
+// sdncAdminDetails1 = ElementFactory.getDefaultUser(UserRoleEnum.ADMIN);
+// heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+// resourceInstanceReqDetails = ElementFactory.getDefaultComponentInstance();
+// resourceInstanceReqDetails2 = ElementFactory.getDefaultComponentInstance();
+//
+// }
+// protected void createComponents() throws Exception{
+//
+//// Create resources
+// RestResponse response = ResourceRestUtils.createResource(resourceDetails, sdncDesignerDetails1);
+// assertTrue("create request returned status:" + response.getErrorCode(),response.getErrorCode() == 201);
+// assertNotNull("resource uniqueId is null:", resourceDetails.getUniqueId());
+//
+// response = ResourceRestUtils.createResource(resourceDetails1, sdncDesignerDetails1);
+// assertTrue("create request returned status:" + response.getErrorCode(),response.getErrorCode() == 201);
+// assertNotNull("resource uniqueId is null:", resourceDetails1.getUniqueId());
+//
+//// Create services
+// response = ServiceRestUtils.createService(serviceDetails, sdncDesignerDetails1);
+// assertTrue("create request returned status:" + response.getErrorCode(),response.getErrorCode() == 201);
+// assertNotNull("service uniqueId is null:", serviceDetails.getUniqueId());
+//
+// response = ServiceRestUtils.createService(serviceDetails2, sdncDesignerDetails1);
+// assertTrue("create request returned status:" + response.getErrorCode(),response.getErrorCode() == 201);
+// assertNotNull("service uniqueId is null:", serviceDetails2.getUniqueId());
+//
+// ArtifactReqDetails heatArtifactDetails = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+// response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails, sdncDesignerDetails1, resourceDetails.getUniqueId());
+// assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),response.getErrorCode() == 200);
+//
+// ArtifactReqDetails heatArtifactDetails1 = ElementFactory.getDefaultDeploymentArtifactForType(ArtifactTypeEnum.HEAT.getType());
+// response = ArtifactRestUtils.addInformationalArtifactToResource(heatArtifactDetails1, sdncDesignerDetails1, resourceDetails1.getUniqueId());
+// assertTrue("add HEAT artifact to resource request returned status:" + response.getErrorCode(),response.getErrorCode() == 200);
+//
+// // certified resources
+//// response = LCSbaseTest.certifyResource(resourceDetails);
+//// assertTrue("certify resource request returned status:" + response.getErrorCode(),response.getErrorCode() == 200);
+////
+//// response = LCSbaseTest.certifyResource(resourceDetails1);
+//// assertTrue("certify resource request returned status:" + response.getErrorCode(),response.getErrorCode() == 200);
+////
+//// add resource instance with HEAT deployment artifact to the service
+// resourceInstanceReqDetails.setUniqueId(resourceDetails.getUniqueId());
+//// response = ServiceUtils.createResourceInstance(resourceInstanceReqDetails, sdncDesignerDetails1, serviceDetails.getUniqueId());
+//// assertTrue("response code is not 200, returned: " + response.getErrorCode(),response.getErrorCode() == 200);
+//
+// DbUtils.cleanAllAudits();
+//
+//
+// }
+
+}
diff --git a/distribution-ci/src/main/java/org/openecomp/test/CallableTask.java b/distribution-ci/src/main/java/org/openecomp/test/CallableTask.java
new file mode 100644
index 0000000000..814fe9ae46
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/CallableTask.java
@@ -0,0 +1,57 @@
+package org.openecomp.test;
+
+//import java.util.concurrent.Callable;
+//
+//import org.openecomp.sdc.api.IDistributionClient;
+//import org.openecomp.sdc.api.results.IDistributionClientResult;
+//import org.openecomp.sdc.impl.DistributionClientFactory;
+//import org.openecomp.sdc.be.model.Service;
+//import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+
+public class CallableTask {
+
+// NotificationCallback notifCallback = null;
+// //private ServiceUtils serviceUtils;
+// private Service service;
+//
+// //public CallableTask(ServiceUtils serviceUtils, Service service) {
+// public CallableTask(Service service) {
+// //this.serviceUtils = serviceUtils;
+// this.service = service;
+// }
+//
+// public NotificationCallback getNotifCallback() {
+// return notifCallback;
+// }
+//
+// @Override
+// public Boolean call() throws Exception {
+//
+// IDistributionClient client = DistributionClientFactory.createDistributionClient();
+//
+// notifCallback = new NotificationCallback(client);
+// IDistributionClientResult result = client.init(new SimpleConfiguration(), notifCallback);
+//
+// System.out.println("result.getDistributionMessageResult: " + result.getDistributionMessageResult());
+//
+// System.out.println("Starting client...");
+// IDistributionClientResult start = client.start();
+// System.out.println(start.getDistributionMessageResult());
+//
+//// RestResponse changeStateToDISTRIBUTED = serviceUtils.changeStateToDISTRIBUTED(service, UserUtils.getGovernorDetails1());
+// ServiceReqDetails serviceDetails = new ServiceReqDetails();
+// serviceDetails.setUniqueId(service.getUniqueId());
+// //RestResponse changeStateToDISTRIBUTED = serviceUtils.changeDistributionStatus(serviceDetails, "1.0", UserUtils.getGovernorDetails1(), "change", DistributionStatusEnum.DISTRIBUTED);
+// //assertTrue("response code is not 200, returned :" + changeStateToDISTRIBUTED.getErrorCode(), changeStateToDISTRIBUTED.getErrorCode() == 200);
+//
+//
+//// while (simpleCallback.getSimpleCallbackResults().size()<3){
+// System.err.println("Sleeping...");
+// Thread.sleep(5000);
+// System.err.println("Finished Sleeping...");
+//// }
+//
+// return true;
+// }
+
+}
diff --git a/distribution-ci/src/main/java/org/openecomp/test/ClientTest.java b/distribution-ci/src/main/java/org/openecomp/test/ClientTest.java
new file mode 100644
index 0000000000..1a10860828
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/ClientTest.java
@@ -0,0 +1,33 @@
+package org.openecomp.test;
+
+import org.slf4j.LoggerFactory;
+
+import org.openecomp.sdc.api.IDistributionClient;
+import org.openecomp.sdc.api.results.IDistributionClientResult;
+import org.openecomp.sdc.impl.DistributionClientFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+
+public class ClientTest {
+ public static void main(String[] args) throws InterruptedException {
+
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ lc.getLogger("org.apache.http").setLevel(Level.INFO);
+
+ IDistributionClient client = DistributionClientFactory.createDistributionClient();
+
+ IDistributionClientResult result = client.init(new SimpleConfiguration(), new NotificationCallback(client));
+ System.out.println(result.getDistributionMessageResult());
+
+ System.out.println("Starting client...");
+ IDistributionClientResult startResult = client.start();
+
+ // Thread.sleep(10000);
+ // client.stop();
+
+ System.out.println(startResult.getDistributionMessageResult());
+
+ }
+
+}
diff --git a/distribution-ci/src/main/java/org/openecomp/test/E2eFlows.java b/distribution-ci/src/main/java/org/openecomp/test/E2eFlows.java
new file mode 100644
index 0000000000..521fc21312
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/E2eFlows.java
@@ -0,0 +1,108 @@
+package org.openecomp.test;
+
+import org.junit.rules.TestName;
+
+//import static org.junit.Assert.assertTrue;
+//
+//import java.util.concurrent.ExecutorService;
+//import java.util.concurrent.Executors;
+//import java.util.concurrent.Future;
+//import java.util.concurrent.TimeUnit;
+//
+//import org.junit.After;
+//import org.junit.Test;
+//import org.junit.rules.TestName;
+//
+//import org.openecomp.sdc.api.IDistributionClient;
+//import org.openecomp.sdc.api.notification.IArtifactInfo;
+//import org.openecomp.sdc.api.notification.INotificationData;
+//import org.openecomp.sdc.api.results.IDistributionClientResult;
+//import org.openecomp.sdc.impl.DistributionClientFactory;
+//import org.openecomp.sdc.utils.DistributionActionResultEnum;
+//import org.openecomp.sdc.be.model.DistributionStatusEnum;
+//import org.openecomp.sdc.be.model.Service;
+//import org.openecomp.sdc.ci.tests.execute.lifecycle.LCSbaseTest;
+//import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+////import org.openecomp.sdc.ci.tests.users.UserUtils;
+//import org.openecomp.sdc.ci.tests.utils.DbUtils;
+//import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+
+public class E2eFlows {
+
+
+
+// private IDistributionClient client = DistributionClientFactory.createDistributionClient();
+// private ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
+//
+// private CallableTask callableTask;
+// private NotificationCallback notifCallback;
+// private IArtifactInfo iArtifactInfo;
+//
+
+
+//
+// @After
+// public void after() throws Exception {
+// client.stop();
+// Thread.sleep(5000);
+// }
+//
+// // ---------------------------------Success
+// // scenario--------------------------------------------------------------------------------
+//
+// @Test
+// public void distributeService() throws Exception{
+// createServiceReadyForDistribution();
+// //RestResponse changeStateToDISTRIBUTED = serviceUtils.changeDistributionStatus(serviceDetails, "1.0", UserUtils.getGovernorDetails1(), "change", DistributionStatusEnum.DISTRIBUTED);
+// //assertTrue("response code is not 200, returned :" + changeStateToDISTRIBUTED.getErrorCode(), changeStateToDISTRIBUTED.getErrorCode() == 200);
+//
+// }
+// @Test
+// public void E2E_Success() throws Exception {
+// Service service = createServiceReadyForDistribution();
+//
+//// clean audit
+// DbUtils.cleanAllAudits();
+//
+// //Create task to run in BG
+// callableTask = new CallableTask(service);
+//
+// //Run the task in BG
+// System.err.println("Executing Task...");
+// Future<Boolean> f = newSingleThreadExecutor.submit(callableTask);
+//
+// //Wait for task to complete and return with result
+//
+//// while (!f.isDone()){
+// System.err.println("Waiting for result...");
+// // Thread.sleep(30000);
+//// }
+// Boolean result = f.get(15, TimeUnit.SECONDS);
+//
+// System.out.println("future:" + result);
+//
+// //Check Results
+// notifCallback = callableTask.getNotifCallback();
+//
+// INotificationData data = notifCallback.getData();
+//
+// System.out.println("result map size = "+ notifCallback.getSimpleCallbackResults().size());
+// IDistributionClientResult downloadResult = notifCallback.getSimpleCallbackResults().get("downloadResult");
+// assertTrue("response code is not SUCCESS, returned :"+ downloadResult.getDistributionActionResult(),downloadResult.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS);
+//
+// }
+//
+//
+// private Service createServiceReadyForDistribution() throws Exception {
+//// RestResponse response = LCSbaseTest.certifyService(serviceDetails);
+//// assertTrue("response code is not 200, returned :" + response.getErrorCode(), response.getErrorCode() == 200);
+//// Service service = ResponseParser.convertServiceResponseToJavaObject(response.getResponse());
+////
+//// RestResponse changeDistributionStateToApprove = serviceUtils.changeDistributionStateToApprove(service,UserUtils.getGovernorDetails1());
+//// assertTrue("response code is not 200, returned :" + changeDistributionStateToApprove.getErrorCode(), changeDistributionStateToApprove.getErrorCode() == 200);
+//// service = ResponseParser.convertServiceResponseToJavaObject(changeDistributionStateToApprove.getResponse());
+//// return service;
+// return null;
+// }
+
+}
diff --git a/distribution-ci/src/main/java/org/openecomp/test/NotificationCallback.java b/distribution-ci/src/main/java/org/openecomp/test/NotificationCallback.java
new file mode 100644
index 0000000000..247b9a9984
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/NotificationCallback.java
@@ -0,0 +1,19 @@
+package org.openecomp.test;
+
+import org.openecomp.sdc.api.IDistributionClient;
+import org.openecomp.sdc.api.notification.INotificationData;
+
+public class NotificationCallback extends SimpleCallback{
+ INotificationData latestCallbackData;
+ public INotificationData getData() {
+ return latestCallbackData;
+ }
+ public NotificationCallback(IDistributionClient client) {
+ super(client);
+ }
+
+ public void activateCallback(INotificationData data) {
+ this.latestCallbackData = data;
+ super.activateCallback(data);
+ }
+}
diff --git a/distribution-ci/src/main/java/org/openecomp/test/SimpleCallback.java b/distribution-ci/src/main/java/org/openecomp/test/SimpleCallback.java
new file mode 100644
index 0000000000..a7b2311867
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/SimpleCallback.java
@@ -0,0 +1,275 @@
+package org.openecomp.test;
+
+import static org.junit.Assert.*;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.commons.codec.binary.Base64;
+
+import org.openecomp.sdc.api.IDistributionClient;
+import org.openecomp.sdc.api.consumer.IDistributionStatusMessage;
+import org.openecomp.sdc.api.consumer.INotificationCallback;
+import org.openecomp.sdc.api.notification.IArtifactInfo;
+import org.openecomp.sdc.api.notification.INotificationData;
+import org.openecomp.sdc.api.notification.IResourceInstance;
+import org.openecomp.sdc.api.notification.IVfModuleMetadata;
+import org.openecomp.sdc.api.results.IDistributionClientDownloadResult;
+import org.openecomp.sdc.api.results.IDistributionClientResult;
+import org.openecomp.sdc.utils.ArtifactTypeEnum;
+import org.openecomp.sdc.utils.DistributionActionResultEnum;
+import org.openecomp.sdc.utils.DistributionStatusEnum;
+import org.openecomp.sdc.ci.tests.utils.Decoder;
+import org.openecomp.sdc.ci.tests.utils.general.FileUtils;
+import com.google.common.io.BaseEncoding;
+
+public class SimpleCallback implements INotificationCallback {
+ private IDistributionClient client;
+ public List<IArtifactInfo> iArtifactInfo;
+
+ public final Map<String, IDistributionClientResult> simpleCallbackResults = new HashMap<String, IDistributionClientResult>();
+
+ public Map<String, IDistributionClientResult> getSimpleCallbackResults() {
+ return simpleCallbackResults;
+ }
+
+ public List<IArtifactInfo> getIArtifactInfo() {
+ return iArtifactInfo;
+ }
+
+ public SimpleCallback(IDistributionClient client) {
+ this.client = client;
+ }
+
+ public void activateCallback(INotificationData data) {
+
+ List<IArtifactInfo> artifacts = getArtifacts(data);
+
+ for (IArtifactInfo iArtifactInfo : artifacts) {
+
+ IArtifactInfo artifactMetadataByUUID = data.getArtifactMetadataByUUID(iArtifactInfo.getArtifactUUID());
+ assertEquals("check artifact checksum", iArtifactInfo.getArtifactChecksum(), artifactMetadataByUUID.getArtifactChecksum());
+ System.out.println(artifactMetadataByUUID.getArtifactURL());
+ if (artifactMetadataByUUID.getArtifactType().equals(ArtifactTypeEnum.VF_MODULES_METADATA)) {
+ IDistributionClientDownloadResult download = client.download(iArtifactInfo);
+ if (download.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS) {
+ List<IVfModuleMetadata> decodeVfModuleArtifact = client.decodeVfModuleArtifact(download.getArtifactPayload());
+ // assertEquals("decoded not equal to actual group amount ", decodeVfModuleArtifact.size(), 2);
+ if (!decodeVfModuleArtifact.isEmpty()) {
+ for (IVfModuleMetadata moduleMetadata : decodeVfModuleArtifact) {
+ List<String> moduleArtifacts = moduleMetadata.getArtifacts();
+ if (moduleArtifacts != null) {
+
+ for (String artifactId : moduleArtifacts) {
+
+ IArtifactInfo artifactInfo = data.getArtifactMetadataByUUID(artifactId);
+ IDistributionClientDownloadResult downloadArt = client.download(artifactInfo);
+ assertEquals(downloadArt.getDistributionActionResult(), DistributionActionResultEnum.SUCCESS);
+
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (IArtifactInfo relevantArtifact : artifacts) {
+ // Download Artifact
+ IDistributionClientDownloadResult downloadResult = client.download(relevantArtifact);
+
+ postDownloadLogic(downloadResult);
+
+ simpleCallbackResults.put("downloadResult", downloadResult);
+ System.out.println("downloadResult: " + downloadResult.toString());
+ System.out.println("<<<<<<<<<<< Artifact content >>>>>>>>>>");
+ System.out.println(Decoder.encode(downloadResult.getArtifactPayload()));
+
+ ///// Print artifact content to console///////
+
+ // byte[] contentInBytes = BaseEncoding.base64().decode(Decoder.encode(downloadResult.getArtifactPayload()));
+ // try {
+ // System.out.println("Source content: " + new String(contentInBytes, "UTF-8"));
+ // } catch (UnsupportedEncodingException e1) {
+ // // TODO Auto-generated catch block
+ // e1.printStackTrace();
+ // }
+ System.out.println("ArtInfo_timeout: " + relevantArtifact.getArtifactTimeout());
+ System.out.println("ArtInfo_Art_description: " + relevantArtifact.getArtifactDescription());
+ System.out.println("ArtInfo_Art_CheckSum: " + relevantArtifact.getArtifactChecksum());
+ System.out.println("ArtInfo_Art_Url: " + relevantArtifact.getArtifactURL());
+ System.out.println("ArtInfo_Art_Type: " + relevantArtifact.getArtifactType());
+ System.out.println("ArtInfo_Art_Name: " + relevantArtifact.getArtifactName());
+ System.out.println("ArtInfo_UUID: " + relevantArtifact.getArtifactUUID());
+ System.out.println("ArtInfo_Version: " + relevantArtifact.getArtifactVersion());
+ System.out.println("ArtInfo_RelatedArtifacts: " + relevantArtifact.getRelatedArtifacts());
+
+ System.out.println("ArtInfo_Serv_description: " + data.getServiceDescription());
+ System.out.println("ArtInfo_Serv_Name: " + data.getServiceName());
+ System.out.println("Get_serviceVersion: " + data.getServiceVersion());
+ System.out.println("Get_Service_UUID: " + data.getServiceUUID());
+ System.out.println("ArtInfo_DistributionId: " + data.getDistributionID());
+ System.out.println("ArtInfo_ServiceInvariantUUID: " + data.getServiceInvariantUUID());
+
+ // assertTrue("response code is not 200, returned :" + downloadResult.getDistributionActionResult(), downloadResult.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS );
+
+ try {
+ String payload = new String(downloadResult.getArtifactPayload());
+ // System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
+ // System.out.println(payload);
+ // System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
+
+ } catch (Exception e) {
+ System.out.println("catch");
+ // break;
+ // TODO: handle exception
+ }
+
+ if (downloadResult.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS) {
+ handleSuccessfullDownload(data, relevantArtifact);
+ } else {
+ handleFailedDownload(data, relevantArtifact);
+ }
+ }
+ // if (data != null){
+ // iArtifactInfo.addAll(artifacts);
+ // }
+
+ }
+
+ private List<IArtifactInfo> getArtifacts(INotificationData data) {
+ List<IArtifactInfo> ret = new ArrayList<IArtifactInfo>();
+ List<IResourceInstance> resources = data.getResources();
+ // data.getArtifactMetadataByUUID(arg0)
+ List<String> relevantArtifactTypes = client.getConfiguration().getRelevantArtifactTypes();
+
+ List<IArtifactInfo> collect = resources.stream().flatMap(e -> e.getArtifacts().stream()).filter(p -> relevantArtifactTypes.contains(p.getArtifactType())).collect(Collectors.toList());
+ // if( resources != null ){
+ // for( IResourceInstance resourceInstance : resources){
+ // if( resourceInstance.getArtifacts() != null ){
+ //
+ //
+ //
+ // ret.addAll(resourceInstance.getArtifacts());
+ //
+ //
+ // }
+ // }
+ // }
+ ret.addAll(collect);
+
+ List<IArtifactInfo> servicesArt = data.getServiceArtifacts();
+ if (servicesArt != null) {
+ ret.addAll(servicesArt);
+ }
+
+ System.out.println("I am here: " + ret.toString());
+ return ret;
+ }
+
+ private void handleFailedDownload(INotificationData data, IArtifactInfo relevantArtifact) {
+ // Send Download Status
+ IDistributionClientResult sendDownloadStatus = client.sendDownloadStatus(buildStatusMessage(client, data, relevantArtifact, DistributionStatusEnum.DOWNLOAD_ERROR));
+ postDownloadStatusSendLogic(sendDownloadStatus);
+ }
+
+ private void handleSuccessfullDownload(INotificationData data, IArtifactInfo relevantArtifact) {
+ // Send Download Status
+ IDistributionClientResult sendDownloadStatus = client.sendDownloadStatus(buildStatusMessage(client, data, relevantArtifact, DistributionStatusEnum.DOWNLOAD_OK));
+
+ simpleCallbackResults.put("sendDownloadStatus", sendDownloadStatus);
+ // assertTrue("response code is not 200, returned :" + sendDownloadStatus.getDistributionActionResult(), sendDownloadStatus.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS );
+
+ // Doing deployment ...
+ postDownloadStatusSendLogic(sendDownloadStatus);
+ boolean isDeployedSuccessfully = handleDeployment();
+ IDistributionClientResult deploymentStatus;
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ if (isDeployedSuccessfully) {
+ deploymentStatus = client.sendDeploymentStatus(buildStatusMessage(client, data, relevantArtifact, DistributionStatusEnum.DEPLOY_OK));
+
+ simpleCallbackResults.put("sendDeploymentStatus", deploymentStatus);
+ // assertTrue("response code is not 200, returned :" + deploymentStatus.getDistributionActionResult(), deploymentStatus.getDistributionActionResult() == DistributionActionResultEnum.SUCCESS );
+
+ } else {
+ deploymentStatus = handleFailedDeployment(data, relevantArtifact);
+ }
+
+ postDeploymentStatusSendLogic(deploymentStatus);
+ }
+
+ private IDistributionClientResult handleFailedDeployment(INotificationData data, IArtifactInfo relevantArtifact) {
+ IDistributionClientResult deploymentStatus;
+ boolean isAlreadyDeployed = checkIsDeployed();
+ if (isAlreadyDeployed) {
+ deploymentStatus = client.sendDeploymentStatus(buildStatusMessage(client, data, relevantArtifact, DistributionStatusEnum.ALREADY_DEPLOYED));
+ } else {
+ deploymentStatus = client.sendDeploymentStatus(buildStatusMessage(client, data, relevantArtifact, DistributionStatusEnum.DEPLOY_ERROR));
+ }
+ return deploymentStatus;
+ }
+
+ private void postDownloadLogic(IDistributionClientDownloadResult downloadResult) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private void postDownloadStatusSendLogic(IDistributionClientResult sendDownloadStatus) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private void postDeploymentStatusSendLogic(IDistributionClientResult deploymentStatus) {
+ // TODO Auto-generated method stub
+
+ }
+
+ private boolean checkIsDeployed() {
+ return false;
+ }
+
+ private boolean handleDeployment() {
+ return true;
+ // to return deploy_error use return false
+ // return false;
+ }
+
+ public static IDistributionStatusMessage buildStatusMessage(final IDistributionClient client, final INotificationData data, final IArtifactInfo relevantArtifact, final DistributionStatusEnum status) {
+ IDistributionStatusMessage statusMessage = new IDistributionStatusMessage() {
+
+ public long getTimestamp() {
+ long currentTimeMillis = System.currentTimeMillis();
+ return currentTimeMillis;
+ }
+
+ public DistributionStatusEnum getStatus() {
+ return status;
+ }
+
+ public String getDistributionID() {
+ return data.getDistributionID();
+ }
+
+ public String getConsumerID() {
+ return client.getConfiguration().getConsumerID();
+ }
+
+ public String getArtifactURL() {
+ return relevantArtifact.getArtifactURL();
+ }
+ };
+ return statusMessage;
+ }
+
+}
diff --git a/distribution-ci/src/main/java/org/openecomp/test/SimpleConfiguration.java b/distribution-ci/src/main/java/org/openecomp/test/SimpleConfiguration.java
new file mode 100644
index 0000000000..7f2ff91906
--- /dev/null
+++ b/distribution-ci/src/main/java/org/openecomp/test/SimpleConfiguration.java
@@ -0,0 +1,149 @@
+package org.openecomp.test;
+
+import java.util.List;
+
+import org.openecomp.sdc.api.consumer.IConfiguration;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+
+public class SimpleConfiguration implements IConfiguration{
+
+// public String getUser()
+// {
+// return System.getProperty("user");
+// }
+//
+// public List<String> getRelevantArtifactTypes() {
+// List<String> res = new ArrayList<String>();
+// for (ArtifactTypeEnum type : ArtifactTypeEnum.values()){
+// res.add(type.name());
+// }
+// return res;
+// }
+//
+// public int getPollingTimeout()
+// {
+// return 20;
+// }
+//
+// public int getPollingInterval()
+// {
+// return 20;
+// }
+//
+// public String getPassword()
+// {
+// return System.getProperty("password");
+// }
+//
+// public String getEnvironmentName()
+// {
+// return System.getProperty("env");
+// }
+//
+// public String getConsumerID()
+// {
+// return System.getProperty("consumerID");
+// }
+//
+// public String getConsumerGroup()
+// {
+// return System.getProperty("groupID");
+// }
+//
+// public String getAsdcAddress()
+// {
+// return System.getProperty("beAddress");
+// }
+//
+// public String getKeyStorePath()
+// {
+// return "";
+// }
+//
+// public String getKeyStorePassword()
+// {
+// return "Aa123456";
+// }
+//
+// public boolean activateServerTLSAuth()
+// {
+// return Boolean.parseBoolean(System.getProperty("auth"));
+//// res.add(ArtifactTypeEnum.HEAT_ARTIFACT);
+//// res.add(ArtifactTypeEnum.HEAT_ENV);
+//// res.add(ArtifactTypeEnum.MURANO_PKG);
+//// res.add(ArtifactTypeEnum.VF_LICENSE);
+//// res.add(ArtifactTypeEnum.APPC_CONFIG);
+//// res.add(ArtifactTypeEnum.MODEL_INVENTORY_PROFILE);
+//// res.add(ArtifactTypeEnum.VNF_CATALOG);
+//// res.add(ArtifactTypeEnum.APPC_CONFIG);
+//// res.add(ArtifactTypeEnum.VF_MODULES_METADATA);
+//// return "PROD-Tedy-Only";
+//// return "A-AI";
+//// return "A-AI";
+// }
+
+
+
+ public String getUser() {
+ return "ci";
+ }
+
+ public List<String> getRelevantArtifactTypes() {
+
+// List<String> res = new ArrayList<String>();
+// for (ArtifactTypeEnum type : AssetTypeEnum.values()){
+// res.add(type.name());
+// }
+ return ArtifactTypeEnum.getAllTypes();
+ }
+
+
+ public int getPollingTimeout() {
+ return 20;
+ }
+
+ public int getPollingInterval() {
+ return 20;
+ }
+
+ public String getPassword() {
+ return "123456";
+ }
+
+ public String getEnvironmentName() {
+ return "PROD-Rom";
+ }
+
+ public String getConsumerID() {
+ return "ys9693-groupVasya";
+ }
+
+ public String getConsumerGroup() {
+// return "mso-groupTedy";
+ return "ys9693-consumerVasya";
+ }
+
+ public String getAsdcAddress() {
+ return "127.0.0.1:8443";
+
+ }
+
+ @Override
+ public String getKeyStorePath() {
+ //return "";
+ return "etc/asdc-client.jks";
+ }
+
+ @Override
+ public String getKeyStorePassword() {
+
+ return "Aa123456";
+ }
+
+ @Override
+ public boolean activateServerTLSAuth() {
+
+ return false;
+ }
+
+}
diff --git a/distribution-ci/src/main/resources/ci/conf/log4j.properties b/distribution-ci/src/main/resources/ci/conf/log4j.properties
new file mode 100644
index 0000000000..3e159ec8df
--- /dev/null
+++ b/distribution-ci/src/main/resources/ci/conf/log4j.properties
@@ -0,0 +1,34 @@
+# Define the root logger with appender file
+log4j.rootLogger = DEBUG, FILE, stdout
+
+# Define the file appender
+log4j.appender.FILE=org.apache.log4j.RollingFileAppender
+log4j.appender.FILE.File=${targetlog}logs/ci-log.out
+
+# Define the layout for file appender
+log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p [%10c] : %m%n
+
+# Set the maximum file size before rollover
+log4j.appender.FILE.maxFileSize=5MB
+
+# Set the the backup index
+log4j.appender.FILE.maxBackupIndex=10
+
+
+#############################################################
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %10c:%L - %m%n
+
+log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG
+log4j.logger.com.thinkaurelius.titan.diskstorage.cassandra.CassandraTransaction=INFO, FILE, stdout
+
+log4j.logger.org.openecomp.sdc.ci.tests.utils=TRACE, FILE, stdout
+log4j.additivity.org.openecomp.sdc.ci.tests.utils=false
+
+
diff --git a/distribution-ci/src/main/resources/ci/conf/sdc-packages.yaml b/distribution-ci/src/main/resources/ci/conf/sdc-packages.yaml
new file mode 100644
index 0000000000..34a78d9275
--- /dev/null
+++ b/distribution-ci/src/main/resources/ci/conf/sdc-packages.yaml
@@ -0,0 +1,10 @@
+packages:
+ - org.openecomp.sdc.ci.tests.execute.general
+ - org.openecomp.sdc.ci.tests.execute.user
+ - org.openecomp.sdc.ci.tests.execute.property
+ - org.openecomp.sdc.ci.tests.execute.lifecycle
+ - org.openecomp.sdc.ci.tests.execute.resource
+ - org.openecomp.sdc.ci.tests.execute.service
+ - org.openecomp.sdc.ci.tests.execute.artifacts
+ - org.openecomp.sdc.ci.tests.execute.imports
+ - org.openecomp.sdc.ci.tests.execute.category \ No newline at end of file
diff --git a/distribution-ci/src/main/resources/ci/conf/sdc.yaml b/distribution-ci/src/main/resources/ci/conf/sdc.yaml
new file mode 100644
index 0000000000..a9c449e672
--- /dev/null
+++ b/distribution-ci/src/main/resources/ci/conf/sdc.yaml
@@ -0,0 +1,35 @@
+outputFolder: target
+reportName: index.html
+catalogBeHost: behost
+catalogFeHost: fehost
+esHost: eshost
+disributionClientHost: disClient
+catalogFePort: 8181
+catalogBePort: 8080
+disributionClientPort: 8181
+esPort: 9200
+neoHost: neoHost
+neoPort: 7474
+neoDBusername: neo4j
+neoDBpassword: 123456
+
+resourceConfigDir: src/test/resources/CI/tests
+componentsConfigDir: src/test/resources/CI/components
+importResourceConfigDir: src/test/resources/CI/importResource
+importResourceTestsConfigDir: src/test/resources/CI/importResourceTests
+errorConfigurationFile: ../catalog-be/src/main/resources/config/error-configuration.yaml
+
+titanPropertiesFile: src/main/resources/ci/conf/titan.properties
+
+stopOnClassFailure: false
+
+#List of non-abstract resources to keep during titan cleanup between tests
+#Only 1.0 version will be kept
+resourcesNotToDelete:
+ - tosca.nodes.Compute
+ - tosca.nodes.Database
+ - tosca.nodes.ObjectStorage
+ - tosca.nodes.BlockStorage
+ - tosca.nodes.LoadBalancer
+ - org.openecomp.resource.cp.Port
+ - org.openecomp.resource.vl.Network \ No newline at end of file
diff --git a/distribution-ci/src/main/resources/ci/conf/titan.properties b/distribution-ci/src/main/resources/ci/conf/titan.properties
new file mode 100644
index 0000000000..691f4d6877
--- /dev/null
+++ b/distribution-ci/src/main/resources/ci/conf/titan.properties
@@ -0,0 +1,5 @@
+storage.backend=cassandra
+storage.hostname=cassandrahost
+storage.port=9160
+
+cache.db-cache = false \ No newline at end of file
diff --git a/distribution-ci/src/main/resources/ci/scripts/startTest.sh b/distribution-ci/src/main/resources/ci/scripts/startTest.sh
new file mode 100644
index 0000000000..88659a80b6
--- /dev/null
+++ b/distribution-ci/src/main/resources/ci/scripts/startTest.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <jar file>"
+}
+
+function exitOnError() {
+ if [ $1 -ne 0 ]
+ then
+ echo "Failed running task $2"
+ exit 2
+ fi
+}
+
+if [ $# -lt 1 ]
+then
+ usage
+ exit 2
+fi
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+LOGS_PROP_FILE=file:${FULL_PATH}/conf/log4j.properties
+#############################################
+TARGET_DIR=${FULL_PATH}/target
+CONF_FILE=${FULL_PATH}/conf/sdc.yaml
+DEBUG=true
+MainClass=org.openecomp.sdc.ci.tests.run.StartTest
+
+JAR_FILE=$1
+
+#TARGET_DIR=`echo ${TARGET_DIR} | sed 's/\//\//g'`
+#echo $TARGET_DIR
+
+TESTS_DIR=/opt/app/sdc/ci/resources/tests
+COMPONENTS_DIR=/opt/app/sdc/ci/resources/components
+
+#sed -i 's#\(outputFolder:\).*#\1 '${TARGET_DIR}'#g' $CONF_FILE
+#sed -i 's#\(resourceConfigDir:\).*#\1 '${TESTS_DIR}'#g' $CONF_FILE
+#sed -i 's#\(componentsConfigDir:\).*#\1 '${COMPONENTS_DIR}'#g' $CONF_FILE
+TARGET_LOG_DIR="${TARGET_DIR}/"
+
+mkdir -p ${TARGET_DIR}
+if [ -d ${TARGET_DIR} ]
+then
+ rm -rf ${TARGET_DIR}/*
+ exitOnError $? "Failed_to_delete_target_dir"
+fi
+
+debug_port=8800
+#JAVA_OPTION="-javaagent:/var/tmp/jacoco/lib/jacocoagent.jar=destfile=jacoco-it.exec"
+JAVA_OPTION=""
+case "$2" in
+ -debug) echo "Debug mode, Listen on port $debug_port"; JAVA_OPTION="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${debug_port}" ;;
+ "") echo "Standard mode";;
+ *) echo "USAGE: startTest.sh [-debug]";;
+esac
+
+cmd="java $JAVA_OPTION -DdisplayException=true -Dtargetlog=${TARGET_LOG_DIR} -Dconfig.resource=${CONF_FILE} -Ddebug=${DEBUG} -Dlog4j.configuration=${LOGS_PROP_FILE} -cp $JAR_FILE ${MainClass}"
+
+#echo $cmd
+#console=`$cmd`
+
+if [ $DEBUG == "true" ]
+then
+ $cmd
+else
+ $cmd >> /dev/null
+fi
+status=`echo $?`
+
+
+
+echo "##################################################"
+echo "################# status is ${status} #################"
+echo "##################################################"
+
+exit $status
+
diff --git a/distribution-ci/src/test/java/org/openecomp/sdc/ci/tests/execute/downloadArtifactUGN/ClientDownloadArtifact.java b/distribution-ci/src/test/java/org/openecomp/sdc/ci/tests/execute/downloadArtifactUGN/ClientDownloadArtifact.java
new file mode 100644
index 0000000000..7337c941a5
--- /dev/null
+++ b/distribution-ci/src/test/java/org/openecomp/sdc/ci/tests/execute/downloadArtifactUGN/ClientDownloadArtifact.java
@@ -0,0 +1,491 @@
+package org.openecomp.sdc.ci.tests.execute.downloadArtifactUGN;
+
+import org.junit.Test;
+
+/**
+ *
+ * @author al714h
+ * US510007 - Story : ASDC Distr Client - Download Artifact
+ * following test set partially cover the US451327 - Story : API to download the specific artifact, cover the audit message.
+ */
+
+//public class ClientDownloadArtifact extends AttSdcTest{
+ public class ClientDownloadArtifact {
+
+// Logger logger = null;
+//
+// DistributionUtils distributionUtils = new DistributionUtils();
+// DownloadArtifactDetails downloadArtifactDetails = new DownloadArtifactDetails();
+// Utils utils = new Utils();
+// DbUtils dbUtils = new DbUtils();
+// UserUtils userUtils = new UserUtils();
+// ResourceUtils resourceUtils = new ResourceUtils();
+// ServiceUtils serviceUtils = new ServiceUtils();
+// ArtifactUtils artifactUtils = new ArtifactUtils();
+// private JSONParser jsonParser = new JSONParser();
+//
+ String serviceBaseVersion = "0.1";
+//
+// @Before
+// public void before() throws Exception {
+//
+// distributionUtils.resetInit();
+// dbUtils.cleanAllAudits();
+// }
+// @Rule
+// public static TestName name = new TestName();
+//
+// }
+//
+// @Rule
+// public static TestName name = new TestName();
+//
+//// public ClientDownloadArtifact() {
+//// super(name, ClientDownloadArtifact.class.getName());
+//// }
+//
+// ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+//// get default artifact info to upload
+// ArtifactDefinition uploadedResourceArtifactInfo = artifactUtils.constructDefaultArtifactInfo();
+//// set artifact info for download
+// downloadArtifactDetails.setArtifactName(uploadedResourceArtifactInfo.getArtifactName());
+// String artifactName = downloadArtifactDetails.getArtifactName();
+//// Andrey TODO String resourceArtifactUrl = String.format(Urls.DOWNLOAD_RESOURCE_ARTIFACT,);
+//
+// int numOfResourcArtifacts = 1;
+// RestResponse createCertifiedResourceWithArtifacts = resourceUtils.createCertifiedResourceWithArtifacts(resourceDetails, uploadedResourceArtifactInfo, numOfResourcArtifacts);
+//
+//
+//
+// try{
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+// distributionUtils.verifyDownloadedArtifact(downloadArtifactRestResponse, uploadedResourceArtifactInfo);
+//
+// // validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+// }finally{
+//// delete created resource
+// resourceUtils.deleteResource_allVersions(resourceDetails, UserUtils.getAdminDetails());
+// }
+
+// }
+
+// send all mandatory headers only
+ @Test
+ public void downloadServiceArtifactSuccess() throws Exception{
+
+//// ServiceReqDetails serviceDetails = serviceUtils.createDefaultDetailsService();
+// ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+// Service service = new Service();
+// try{
+//// get default artifact info to upload
+// ArtifactDefinition uploadedResourceArtifactInfo = artifactUtils.constructDefaultArtifactInfo();
+// uploadedResourceArtifactInfo.setArtifactType(ArtifactTypeEnum.HEAT.getType());
+// ArtifactDefinition uploadedServiceArtifactInfo = artifactUtils.constructDefaultArtifactInfo();
+// uploadedServiceArtifactInfo.setArtifactType(ArtifactTypeEnum.MURANO_PKG.getType());
+//
+//// set artifact info for download
+// downloadArtifactDetails.setArtifactName(uploadedServiceArtifactInfo.getArtifactName());
+// downloadArtifactDetails.setArtifactType(ArtifactTypeEnum.MURANO_PKG.getType());
+// String artifactName = downloadArtifactDetails.getArtifactName();
+//
+//// create default resource details
+// ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+//
+// int numOfResourcArtifacts = 1;
+// service = serviceUtils.createCertifiedServiceWithResourceInstanceAndArtifacts(serviceDetails, resourceDetails, uploadedResourceArtifactInfo, uploadedServiceArtifactInfo , numOfResourcArtifacts);
+// logger.debug("service version = " + service.getVersion());
+// logger.debug("service distribution status = " + service.getDistributionStatus());
+// RestResponse changeDistributionStateToApprove = serviceUtils.changeDistributionStateToApprove(service, UserUtils.getGovernorDetails1());
+//
+// int status = changeDistributionStateToApprove.getErrorCode();
+// assertTrue("response code is not 200, returned :" + status, status == 200);
+//
+// serviceDetails.setVersion(service.getVersion());
+// serviceDetails.setUniqueId(service.getUniqueId());
+//
+// String responseString = changeDistributionStateToApprove.getResponse();
+// Gson gson = new Gson();
+// service = gson.fromJson(responseString, Service.class);
+// ServiceMetadataDataDefinition serviceMetadataDataDefinition = gson.fromJson(responseString, ServiceMetadataDataDefinition.class);
+// service.setDistributionStatus(DistributionStatusEnum.findState(serviceMetadataDataDefinition.getDistributionStatus()));
+// DistributionStatusEnum distributionStatus = service.getDistributionStatus();
+// assertNotNull("distribution state is null",distributionStatus.name());
+// assertTrue("the default distribution state is invalid", DistributionStatusEnum.DISTRIBUTION_APPROVED.equals(distributionStatus));
+//
+// String serviceArtifactUrl = String.format(Urls.DISTRIBUTION_DOWNLOAD_ARTIFACT,service.getName(), service.getVersion(), artifactName);
+// logger.debug("download service artifact url: "+ serviceArtifactUrl+ " serviceName = " + service.getName() + " service version = " + service.getVersion() + "service artifact name = " + artifactName);
+//
+//
+//// Log.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//// distributionUtils.verifyDownloadedArtifact(downloadArtifactRestResponse, uploadedArtifactInfo);
+////
+//// // validate audit message server side
+//// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+//// String action = "ArtifactDownload";
+//// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+//// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+// }finally{
+//// delete created service
+// serviceUtils.deleteService_allVersions(serviceDetails, UserUtils.getAdminDetails());
+// }
+
+// @Test
+// public void downloadServiceArtifactSuccess() throws Exception{
+//
+//// ServiceReqDetails serviceDetails = serviceUtils.createDefaultDetailsService();
+// ServiceReqDetails serviceDetails = ElementFactory.getDefaultService();
+// Service service = new Service();
+// try{
+//// get default artifact info to upload
+// ArtifactDefinition uploadedResourceArtifactInfo = artifactUtils.constructDefaultArtifactInfo();
+// uploadedResourceArtifactInfo.setArtifactType(ArtifactTypeEnum.HEAT.getType());
+// ArtifactDefinition uploadedServiceArtifactInfo = artifactUtils.constructDefaultArtifactInfo();
+// uploadedServiceArtifactInfo.setArtifactType(ArtifactTypeEnum.MURANO_PKG.getType());
+//
+//// set artifact info for download
+// downloadArtifactDetails.setArtifactName(uploadedServiceArtifactInfo.getArtifactName());
+// downloadArtifactDetails.setArtifactType(ArtifactTypeEnum.MURANO_PKG.getType());
+// String artifactName = downloadArtifactDetails.getArtifactName();
+//
+//// create default resource details
+// ResourceReqDetails resourceDetails = ElementFactory.getDefaultResource();
+//
+// int numOfResourcArtifacts = 1;
+// service = serviceUtils.createCertifiedServiceWithResourceInstanceAndArtifacts(serviceDetails, resourceDetails, uploadedResourceArtifactInfo, uploadedServiceArtifactInfo , numOfResourcArtifacts);
+// logger.debug("service version = " + service.getVersion());
+// logger.debug("service distribution status = " + service.getDistributionStatus());
+// RestResponse changeDistributionStateToApprove = serviceUtils.changeDistributionStateToApprove(service, UserUtils.getGovernorDetails1());
+//
+// int status = changeDistributionStateToApprove.getErrorCode();
+// assertTrue("response code is not 200, returned :" + status, status == 200);
+//
+// serviceDetails.setVersion(service.getVersion());
+// serviceDetails.setUniqueId(service.getUniqueId());
+//
+// String responseString = changeDistributionStateToApprove.getResponse();
+// Gson gson = new Gson();
+// service = gson.fromJson(responseString, Service.class);
+// ServiceMetadataDataDefinition serviceMetadataDataDefinition = gson.fromJson(responseString, ServiceMetadataDataDefinition.class);
+// service.setDistributionStatus(DistributionStatusEnum.findState(serviceMetadataDataDefinition.getDistributionStatus()));
+// DistributionStatusEnum distributionStatus = service.getDistributionStatus();
+// assertNotNull("distribution state is null",distributionStatus.name());
+// assertTrue("the default distribution state is invalid", DistributionStatusEnum.DISTRIBUTION_APPROVED.equals(distributionStatus));
+//
+// String serviceArtifactUrl = String.format(Urls.DISTRIBUTION_DOWNLOAD_ARTIFACT,service.getName(), service.getVersion(), artifactName);
+// logger.debug("download service artifact url: "+ serviceArtifactUrl+ " serviceName = " + service.getName() + " service version = " + service.getVersion() + "service artifact name = " + artifactName);
+//
+//
+//// Log.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//// distributionUtils.verifyDownloadedArtifact(downloadArtifactRestResponse, uploadedArtifactInfo);
+////
+//// // validate audit message server side
+//// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+//// String action = "ArtifactDownload";
+//// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+//// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+// }finally{
+//// delete created service
+// serviceUtils.deleteService_allVersions(serviceDetails, UserUtils.getAdminDetails());
+// }
+//
+// }
+//
+//// Andrey not relevant, dev ci should cover, qa can't influence on sending headers
+////// send all headers mandatory and not mandatory
+//// @Test
+//// public void downloadArtifactSuccessMandatoryAndNotMandatoryHeaders() throws Exception{
+////
+//// Log.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+////
+//// Map<String, String> headersMap = new HashMap<String,String>();
+//// headersMap.put(HttpHeaderEnum.X_ECOMP_InstanceID.getValue(), "instar_name"); // mandatory
+//// headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), "usernamePassword"); // mandatory
+//// headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/octet-stream"); // not mandatory
+//// headersMap.put(HttpHeaderEnum.X_ECOMP_REQUEST_ID_HEADER.getValue(),downloadArtifactDetails.getResourceUUID());// not mandatory
+//// Log.debug("headers detailes: "+ headersMap.toString());
+////
+//// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, headersMap);
+//// distributionUtils.verifyDownloadedArtifact(downloadArtifactRestResponse);
+////
+////// validate audit message server side
+//// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+//// String action = "ArtifactDownload";
+//// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+//// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+////
+//// }
+//
+//
+////---------------------------------Failure scenario--------------------------------------------------------------------------------
+//
+//// Andrey not relevant, dev ci should cover, qa can't influence on sending headers
+////// missing InstanceID mandatory header
+//// @Test
+//// public void downloadArtifactMissingInstanceIdHeader() throws Exception, JSONException{
+////
+//// Log.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+////
+//// Map<String, String> headersMap = new HashMap<String,String>();
+//// headersMap.put(HttpHeaderEnum.AUTHORIZATION.getValue(), "usernamePassword"); // mandatory
+//// Log.debug("headers detailes: "+ headersMap.toString());
+////
+//// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, headersMap);
+////
+//// ErrorInfo errorInfo = utils.parseYaml(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID.name());
+//// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+////
+//// List<String> variables = Arrays.asList();
+//// utils.checkBodyResponseOnError(ActionStatus.MISSING_X_ECOMP_INSTANCE_ID.name(), variables, downloadArtifactRestResponse.getResponse()));
+////
+//// }
+//
+//// Andrey not relevant, dev ci should cover, qa can't influence on sending headers
+////// missing Authorization mandatory header
+//// @Test
+//// public void downloadArtifactMissingAuthorizationHeader() throws Exception, JSONException{
+////
+//// Log.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+////
+//// Map<String, String> headersMap = new HashMap<String,String>();
+//// headersMap.put(HttpHeaderEnum.X_ECOMP_InstanceID.getValue(), "usernamePassword"); // mandatory
+//// Log.debug("headers detailes: "+ headersMap.toString());
+////
+//// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, headersMap);
+////
+//// ErrorInfo errorInfo = utils.parseYaml(ActionStatus.ECOMP_RESEND_WITH_BASIC_AUTHENTICATION_CREDENTIALS.name());
+//// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+////
+//// List<String> variables = Arrays.asList();
+//// utils.checkBodyResponseOnError(ActionStatus.ECOMP_RESEND_WITH_BASIC_AUTHENTICATION_CREDENTIALS.name(), variables, downloadArtifactRestResponse.getResponse()));
+////
+//// }
+//
+//
+//// artifact not found
+// @Test
+// public void downloadArtifactArtifactNotFound() throws Exception, JSONException{
+//
+// downloadArtifactDetails.setArtifactName("artifactNotExist");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.DISTRIBUTION_ARTIFACT_NOT_FOUND.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.DISTRIBUTION_ARTIFACT_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+//// validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// expectedArtifactDownloadAuditMessageInfo.setStatus(errorInfo.getCode().toString());
+// String desc = "Error: Artifact " + downloadArtifactDetails.getArtifactName() + " was not found";
+// expectedArtifactDownloadAuditMessageInfo.setDesc(desc);
+//
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+//
+// }
+//
+//// service not found
+// @Test
+// public void downloadArtifactServiceNameNotFound() throws Exception, JSONException{
+//
+// downloadArtifactDetails.setArtifactURL("/asdc/v1/services/serviceNotExist/0.1/artifacts/aaa.hh");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.SERVICE_NOT_FOUND.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+//// validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// expectedArtifactDownloadAuditMessageInfo.setStatus(errorInfo.getCode().toString());
+//// TODO Andrey, change desc message
+// String desc = "Error: Artifact " + downloadArtifactDetails.getArtifactName() + " was not found";
+// expectedArtifactDownloadAuditMessageInfo.setDesc(desc);
+//
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+//
+// }
+//
+//
+//// service version not found
+// @Test
+// public void downloadArtifactServiceVersionNotFound() throws Exception, JSONException{
+//
+// downloadArtifactDetails.setArtifactURL("/asdc/v1/services/serviceName/0.888/artifacts/aaa.hh");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.COMPONENT_VERSION_NOT_FOUND.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.COMPONENT_VERSION_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+//// validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// expectedArtifactDownloadAuditMessageInfo.setStatus(errorInfo.getCode().toString());
+//// TODO Andrey, change desc message
+// String desc = "Error: Artifact " + downloadArtifactDetails.getArtifactName() + " was not found";
+// expectedArtifactDownloadAuditMessageInfo.setDesc(desc);
+//
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+//
+// }
+//
+//// invalid HTTP method, PUT HTTP method
+// @Test
+// public void downloadArtifactByPutMethod() throws Exception, JSONException{
+//
+// String method = "PUT";
+//
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendDownloadedArtifactByMethod(downloadArtifactDetails, null, method);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+// }
+//
+//// invalid HTTP method, DELETE HTTP method
+// @Test
+// public void downloadArtifactByDeleteMethod() throws Exception, JSONException{
+//
+// String method = "DELETE";
+// downloadArtifactDetails.setArtifactName("artifactNotExist");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.DISTRIBUTION_ARTIFACT_NOT_FOUND.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.DISTRIBUTION_ARTIFACT_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+//// validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// expectedArtifactDownloadAuditMessageInfo.setStatus(errorInfo.getCode().toString());
+// String desc = "Error: Artifact " + downloadArtifactDetails.getArtifactName() + " was not found";
+// expectedArtifactDownloadAuditMessageInfo.setDesc(desc);
+//
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+//
+// downloadArtifactDetails.setArtifactURL("/asdc/v1/services/serviceNotExist/0.1/artifacts/aaa.hh");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.SERVICE_NOT_FOUND.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.SERVICE_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+//// validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// expectedArtifactDownloadAuditMessageInfo.setStatus(errorInfo.getCode().toString());
+//// TODO Andrey, change desc message
+// String desc = "Error: Artifact " + downloadArtifactDetails.getArtifactName() + " was not found";
+// expectedArtifactDownloadAuditMessageInfo.setDesc(desc);
+//
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+// downloadArtifactDetails.setArtifactURL("/asdc/v1/services/serviceName/0.888/artifacts/aaa.hh");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.COMPONENT_VERSION_NOT_FOUND.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.COMPONENT_VERSION_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse());
+//
+//// validate audit message server side
+// ArtifactDownloadAuditMessageInfo expectedArtifactDownloadAuditMessageInfo = distributionUtils.constructDefaultArtifactDownloadAuditMessageInfo();
+// expectedArtifactDownloadAuditMessageInfo.setStatus(errorInfo.getCode().toString());
+//// TODO Andrey, change desc message
+// String desc = "Error: Artifact " + downloadArtifactDetails.getArtifactName() + " was not found";
+// expectedArtifactDownloadAuditMessageInfo.setDesc(desc);
+//
+// String action = "ArtifactDownload";
+// ArtifactDownloadAuditMessageInfo actualArtifactDownloadAuditMessageInfo = userUtils.parseDestributionAuditRespByAction(action);
+// distributionUtils.validateArtifactDownloadAuditMessage(actualArtifactDownloadAuditMessageInfo, expectedArtifactDownloadAuditMessageInfo);
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendDownloadedArtifactByMethod(downloadArtifactDetails, null, method);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT.name(), variables, downloadArtifactRestResponse.getResponse());
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendDownloadedArtifactByMethod(downloadArtifactDetails, null, method);
+//
+// ErrorInfo errorInfo = utils.parseErrorConfigYaml(ActionStatus.METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT.name());
+// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+//
+// List<String> variables = Arrays.asList();
+// utils.checkBodyResponseOnError(ActionStatus.METHOD_NOT_ALLOWED_TO_DOWNLOAD_ARTIFACT.name(), variables, downloadArtifactRestResponse.getResponse());
+// downloadArtifactDetails.setArtifactChecksum("invalidChecksum");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//// TODO
+//
+//// ErrorInfo errorInfo = utils.parseYaml(ActionStatus.SPECIFIED_SERVICE_RESOURCE_VERSION_NOT_FOUND.name());
+//// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+////
+//// List<String> variables = Arrays.asList();
+//// utils.checkBodyResponseOnError(ActionStatus.SPECIFIED_SERVICE_RESOURCE_VERSION_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse()));
+//
+// downloadArtifactDetails.setArtifactChecksum("invalidChecksum");
+// logger.debug("artifact detailes: "+ downloadArtifactDetails.toString());
+//
+// RestResponse downloadArtifactRestResponse = distributionUtils.sendGetDownloadedArtifact(downloadArtifactDetails, null);
+//// TODO
+//
+//// ErrorInfo errorInfo = utils.parseYaml(ActionStatus.SPECIFIED_SERVICE_RESOURCE_VERSION_NOT_FOUND.name());
+//// assertEquals("Check response code after artifact download request", errorInfo.getCode(), downloadArtifactRestResponse.getErrorCode());
+////
+//// List<String> variables = Arrays.asList();
+//// utils.checkBodyResponseOnError(ActionStatus.SPECIFIED_SERVICE_RESOURCE_VERSION_NOT_FOUND.name(), variables, downloadArtifactRestResponse.getResponse()));
+//
+// }
+//
+//
+//
+//
+
+ }
+
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000000..963cfd4d1a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,833 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+
+
+ <modules>
+ <module>security-utils</module>
+ <module>common-app-api</module>
+ <module>common-be</module>
+ <module>catalog-dao</module>
+ <module>catalog-model</module>
+ <module>catalog-be</module>
+ <module>asdctool</module>
+ <module>catalog-ui</module>
+ <module>catalog-fe</module>
+ <module>asdc-tests</module>
+ <module>ui-ci</module>
+ <module>sdc-os-chef</module>
+
+ <!--<module>openecomp-be</module>-->
+ <!--<module>openecomp-ui</module>-->
+
+ </modules>
+
+
+
+ <properties>
+
+ <!-- ==================== -->
+ <!-- Generic properties -->
+ <!-- ==================== -->
+ <build.type>-SNAPSHOT</build.type>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+
+ <!-- ==================== -->
+ <!-- Versions -->
+ <!-- ==================== -->
+ <!-- Global project version -->
+ <asdc.major.version>1610</asdc.major.version>
+ <asdc.minor.version>0</asdc.minor.version>
+ <asdc.servicepack.version>2</asdc.servicepack.version>
+ <sprint.number>33</sprint.number>
+ <asdc.full.version>${project.version}</asdc.full.version>
+
+ <!-- BE -->
+ <catalog-be.major.version>${asdc.major.version}</catalog-be.major.version>
+ <catalog-be.minor.version>${asdc.minor.version}</catalog-be.minor.version>
+ <catalog-be.servicepack.version>${asdc.servicepack.version}</catalog-be.servicepack.version>
+ <catalog-be.full.version>${catalog-be.major.version}.${catalog-be.minor.version}.${catalog-be.servicepack.version}${build.type}</catalog-be.full.version>
+
+ <!-- FE -->
+ <catalog-fe.major.version>${asdc.major.version}</catalog-fe.major.version>
+ <catalog-fe.minor.version>${asdc.minor.version}</catalog-fe.minor.version>
+ <catalog-fe.servicepack.version>${asdc.servicepack.version}</catalog-fe.servicepack.version>
+ <catalog-fe.full.version>${catalog-fe.major.version}.${catalog-fe.minor.version}.${catalog-fe.servicepack.version}${build.type}</catalog-fe.full.version>
+
+ <!-- CI -->
+ <catalog-ci.major.version>${asdc.major.version}</catalog-ci.major.version>
+ <catalog-ci.minor.version>${asdc.minor.version}</catalog-ci.minor.version>
+ <catalog-ci.servicepack.version>${asdc.servicepack.version}</catalog-ci.servicepack.version>
+ <catalog-ci.full.version>${catalog-ci.major.version}.${catalog-ci.minor.version}.${catalog-ci.servicepack.version}${build.type}</catalog-ci.full.version>
+
+ <!-- UI -->
+ <catalog-ui.major.version>${asdc.major.version}</catalog-ui.major.version>
+ <catalog-ui.minor.version>${asdc.minor.version}</catalog-ui.minor.version>
+ <catalog-ui.servicepack.version>${asdc.servicepack.version}</catalog-ui.servicepack.version>
+ <catalog-ui.full.version>${catalog-ui.major.version}.${catalog-ui.minor.version}.${catalog-ui.servicepack.version}${build.type}</catalog-ui.full.version>
+
+ <!-- Model -->
+ <catalog-model.major.version>${asdc.major.version}</catalog-model.major.version>
+ <catalog-model.minor.version>${asdc.minor.version}</catalog-model.minor.version>
+ <catalog-model.servicepack.version>${asdc.servicepack.version}</catalog-model.servicepack.version>
+ <catalog-model.full.version>${catalog-model.major.version}.${catalog-model.minor.version}.${catalog-model.servicepack.version}${build.type}</catalog-model.full.version>
+
+ <!-- CHEF -->
+ <asdc-chef.major.version>${asdc.major.version}</asdc-chef.major.version>
+ <asdc-chef.minor.version>${asdc.minor.version}</asdc-chef.minor.version>
+ <asdc-chef.servicepack.version>${asdc.servicepack.version}</asdc-chef.servicepack.version>
+ <asdc-chef.full.version>${asdc-chef.major.version}.${asdc-chef.minor.version}.${asdc-chef.servicepack.version}${build.type}</asdc-chef.full.version>
+
+ <!-- TESTS -->
+ <sdnc-tests.major.version>${asdc.major.version}</sdnc-tests.major.version>
+ <sdnc-tests.minor.version>${asdc.minor.version}</sdnc-tests.minor.version>
+ <sdnc-tests.servicepack.version>${asdc.servicepack.version}</sdnc-tests.servicepack.version>
+ <sdnc-tests.full.version>${sdnc-tests.major.version}.${sdnc-tests.minor.version}.${sdnc-tests.servicepack.version}${build.type}</sdnc-tests.full.version>
+
+ <!-- TESTS2 -->
+ <asdc-tests.major.version>${asdc.major.version}</asdc-tests.major.version>
+ <asdc-tests.minor.version>${asdc.minor.version}</asdc-tests.minor.version>
+ <asdc-tests.servicepack.version>${asdc.servicepack.version}</asdc-tests.servicepack.version>
+ <asdc-tests.full.version>${asdc-tests.major.version}.${asdc-tests.minor.version}.${asdc-tests.servicepack.version}${build.type}</asdc-tests.full.version>
+
+
+ <!-- Sub modules versioning -->
+ <sdnc-tests.version>${asdc.full.version}</sdnc-tests.version>
+ <asdc-tests.version>${asdc.full.version}</asdc-tests.version>
+ <catalog-dao.version>${asdc.full.version}</catalog-dao.version>
+ <catalog-model.version>${asdc.full.version}</catalog-model.version>
+ <common-app-api.version>${asdc.full.version}</common-app-api.version>
+ <common-be.version>${asdc.full.version}</common-be.version>
+ <security-utils.version>${asdc.full.version}</security-utils.version>
+ <asdctool.version>${asdc.full.version}</asdctool.version>
+
+
+ <!-- 3rd parties versions -->
+ <lang3.version>3.3.2</lang3.version>
+ <guava.version>18.0</guava.version>
+ <titan.version>1.0.0</titan.version>
+ <spring-boot.version>1.1.6.RELEASE</spring-boot.version>
+ <spring.version>4.3.4.RELEASE</spring.version>
+ <spring.security.version>3.2.3.RELEASE</spring.security.version>
+ <spring.ldap.version>2.0.1.RELEASE</spring.ldap.version>
+ <mockito.version>1.9.0</mockito.version>
+ <ecomp.version>1.0.0-SNAPSHOT</ecomp.version>
+ <artefact-gen-api.version>1.0.0-SNAPSHOT</artefact-gen-api.version>
+ <artefact-gen-core.version>1.0.0-SNAPSHOT</artefact-gen-core.version>
+ <dox-common-lib.version>1.0.0-SNAPSHOT</dox-common-lib.version>
+
+
+ <!-- Elastic Search mapper (reference the elastic search version actually). -->
+ <elastic-search.version>2.1.0</elastic-search.version>
+ <springockito.version>1.0.4</springockito.version>
+ <alien4cloud.version>1.0.0-SM19</alien4cloud.version>
+ <catalog-artifacts.version>1.0.0-SNAPSHOT</catalog-artifacts.version>
+ <catalog-builders.version>1.0.0-SNAPSHOT</catalog-builders.version>
+ <jetty.version>9.2.10.v20150310</jetty.version>
+
+ <!-- JSON and YAML Parsing -->
+ <jackson.version>2.6.2</jackson.version>
+ <jackson.annotations.version>2.6.0</jackson.annotations.version>
+
+ <!-- Yaml for properties -->
+ <snakeyaml.version>1.12</snakeyaml.version>
+ <functionaljava.version>4.2</functionaljava.version>
+ <httpclient.version>4.4.1</httpclient.version>
+ <httpcore.version>4.4.1</httpcore.version>
+ <json-simple.version>1.1</json-simple.version>
+ <jetty.servlets.version>9.2.10.v20150310</jetty.servlets.version>
+
+ <!-- Logging start -->
+ <!-- logback -->
+ <groovy.version>2.3.5</groovy.version>
+ <janino.version>3.0.6</janino.version>
+ <!-- aspects -->
+ <jcabi.version>0.20.1</jcabi.version>
+ <aspectjrt.version>1.8.4</aspectjrt.version>
+ <jcabi.plugin.version>0.13.2</jcabi.plugin.version>
+ <!-- Logging end -->
+ <!-- System Metrics -->
+ <sigar.version>1.6.4</sigar.version>
+
+
+ <!--JaCoCO -->
+ <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
+ <sonar.jacoco.reportPath>${project.basedir}/target/jacoco.exec</sonar.jacoco.reportPath>
+ <sonar.jacoco.itReportPath>${project.basedir}/target/it-jacoco.exec</sonar.jacoco.itReportPath>
+ <!--sonar.language>java</sonar.language-->
+ <sonar.version>${asdc.major.version}</sonar.version>
+
+ <!--cassandra -->
+ <cassandra.driver.version>3.0.0</cassandra.driver.version>
+
+ <!-- Sonar properties -->
+ <sonar.login>sonaruser</sonar.login>
+ <sonar.password>us7USi0Htu93nFY91DPuQLFo6ebKcKXv</sonar.password>
+ <sonar.host.url>http://104.239.145.8:9000</sonar.host.url>
+ <sonar.sourceEncoding>${project.build.sourceEncoding}</sonar.sourceEncoding>
+ <sonar.skipDesign>true</sonar.skipDesign>
+ <sonar.projectBaseDir>${project.basedir}</sonar.projectBaseDir>
+ <sonar.sources>.</sonar.sources>
+ <sonar.exclusions>**/scripts/**/*</sonar.exclusions>
+ <sonar.test.exclusions>**/test/**/*,**/tests/**/*</sonar.test.exclusions>
+ <sonar.inclusions>app/**/*.js,server-mock/**/*.js,src/**/*.js,src/main/**/*.java</sonar.inclusions>
+
+ <!--nexus-->
+ <nexus.proxy>https://nexus.openecomp.org</nexus.proxy>
+ </properties>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.10.4</version>
+ <configuration>
+ <failOnError>false</failOnError>
+ <doclet>org.umlgraph.doclet.UmlGraphDoc</doclet>
+ <docletArtifact>
+ <groupId>org.umlgraph</groupId>
+ <artifactId>umlgraph</artifactId>
+ <version>5.6</version>
+ </docletArtifact>
+ <additionalparam>-views</additionalparam>
+ <useStandardDocletOptions>true</useStandardDocletOptions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>wagon-maven-plugin</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>${guava.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>${lang3.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.elasticsearch</groupId>
+ <artifactId>elasticsearch</artifactId>
+ <version>${elastic-search.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>9.2.10.v20150310</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <version>9.2.10.v20150310</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <version>9.2.10.v20150310</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey</groupId>
+ <artifactId>jersey-bom</artifactId>
+ <version>2.24</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.10</version>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet</artifactId>
+ <version>2.24</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-jackson</artifactId>
+ <version>2.24</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-moxy</artifactId>
+ <version>2.24</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- yaml to object converter -->
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <version>1.14</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- listen to file changes -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <version>1.1</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Gson -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.3.1</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>${httpclient.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>${httpclient.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <version>${httpcore.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.1.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>1.1.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Logback extensions -->
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <version>${groovy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.janino</groupId>
+ <artifactId>janino</artifactId>
+ <version>${janino.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.2.1-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.10</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.relevantcodes</groupId>
+ <artifactId>extentreports</artifactId>
+ <version>2.41.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.10.19</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <version>${json-simple.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-messaging</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aspects</artifactId>
+ <version>${spring.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <version>${spring.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ <version>${spring.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <version>${jackson.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>${jackson.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>${jackson.annotations.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- functional java -->
+ <dependency>
+ <groupId>org.functionaljava</groupId>
+ <artifactId>functionaljava</artifactId>
+ <version>${functionaljava.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- Aspects -->
+ <dependency>
+ <groupId>com.jcabi</groupId>
+ <artifactId>jcabi-aspects</artifactId>
+ <version>${jcabi.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <version>${aspectjrt.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- System metrics -->
+ <dependency>
+ <groupId>org.fusesource</groupId>
+ <artifactId>sigar</artifactId>
+ <version>${sigar.version}</version>
+ </dependency>
+ </dependencies>
+
+ </dependencyManagement>
+
+
+
+
+ <build>
+ <pluginManagement>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>3.4</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.wagon</groupId>
+ <artifactId>wagon-webdav-jackrabbit</artifactId>
+ <version>2.10</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.6</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.4.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.sonarsource.scanner.maven</groupId>
+ <artifactId>sonar-maven-plugin</artifactId>
+ <version>3.0.2</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.19.1</version>
+ <configuration>
+ <systemPropertyVariables>
+ <logback.configurationFile>src/test/resources/logback-test.xml</logback.configurationFile>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+
+ <!-- ================================================== -->
+ <!-- Set the JDK compiler version. -->
+ <!-- ================================================== -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <inherited>true</inherited>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+
+ <!-- ============================================= -->
+ <!-- Java Code Coverage -->
+ <!-- ============================================= -->
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>0.7.1.201405082137</version>
+ <inherited>True</inherited>
+
+ <executions>
+
+ <!-- Unit-Tests -->
+ <execution>
+ <id>prepare-agent</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ <configuration>
+ <destFile>${sonar.jacoco.reportPath}</destFile>
+ </configuration>
+ </execution>
+
+
+ <execution>
+ <id>report</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <configuration>
+ <dataFile>${sonar.jacoco.reportPath}</dataFile>
+ <outputDirectory>${project.basedir}/target/site/jacoco</outputDirectory>
+ </configuration>
+ </execution>
+
+ <!-- Integration Tests (Only report goal) -->
+ <execution>
+ <id>report-integration</id>
+ <goals>
+ <goal>report-integration</goal>
+ </goals>
+ <configuration>
+ <dataFile>${sonar.jacoco.itReportPath}</dataFile>
+ <outputDirectory>${project.basedir}/target/site/it-jacoco</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+
+ <!-- ================================================== -->
+ <!-- Set the deployment repositories properties. -->
+ <!-- ================================================== -->
+ <plugin>
+ <groupId>org.codehaus.gmaven</groupId>
+ <artifactId>gmaven-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <inherited>false</inherited>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>execute</goal>
+ </goals>
+ <configuration>
+ <source>
+ pom.properties['deploy.url']=
+ pom.version.contains('-SNAPSHOT') ?
+ project.distributionManagement.snapshotRepository.url :
+ project.distributionManagement.repository.url;
+ pom.properties['repo.id']= pom.version.contains('-SNAPSHOT') ?
+ project.distributionManagement.snapshotRepository.id :
+ project.distributionManagement.repository.id;
+ </source>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+
+
+ <!-- license plugin -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>license-maven-plugin</artifactId>
+ <version>1.10</version>
+ <configuration>
+ <addJavaLicenseAfterPackage>false</addJavaLicenseAfterPackage>
+ <processStartTag>============LICENSE_START=======================================================</processStartTag>
+ <processEndTag>============LICENSE_END=========================================================</processEndTag>
+ <sectionDelimiter>================================================================================</sectionDelimiter>
+ <licenseName>apache_v2</licenseName>
+ <inceptionYear>2017</inceptionYear>
+ <organizationName>AT&amp;T Intellectual Property. All rights reserved.</organizationName>
+ <projectName>SDC</projectName>
+ <canUpdateCopyright>true</canUpdateCopyright>
+ <canUpdateDescription>true</canUpdateDescription>
+ <canUpdateLicense>true</canUpdateLicense>
+ <emptyLineAfterHeader>true</emptyLineAfterHeader>
+ <verbose>false</verbose>
+ <includes>
+ <include>**/*.java</include>
+ <include>**/*.js</include>
+ <include>**/*.ts</include>
+ </includes>
+ <roots>
+ <root>src</root>
+ <root>app</root>
+ <root>server-mock</root>
+ <root>typings</root>
+ </roots>
+ </configuration>
+ <executions>
+ <execution>
+ <id>first</id>
+ <goals>
+ <goal>update-file-header</goal>
+ </goals>
+ <!--phase>process-sources</phase-->
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- blackduck maven plugin -->
+ <!-- <plugin>
+ <groupId>com.blackducksoftware.integration</groupId>
+ <artifactId>hub-maven-plugin</artifactId>
+ <version>1.0.4</version>
+ <inherited>false</inherited>
+ <configuration>
+ <target>${project.basedir}</target>
+ </configuration>
+ <executions>
+ <execution>
+ <id>create-bdio-file</id>
+ <phase>package</phase>
+ <goals>
+ <goal>createHubOutput</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin> -->
+ </plugins>
+ </build>
+
+
+
+ <repositories>
+ <repository>
+ <id>virtuos</id>
+ <name>Virtuos</name>
+ <url>http://nexus.virtuos.uos.de/nexus/content/repositories/public/</url>
+ <layout>default</layout>
+ </repository>
+ <repository>
+ <id>apache-public</id>
+ <name>Apache-Public</name>
+ <url>https://repository.apache.org/content/groups/public/</url>
+ <layout>default</layout>
+ </repository>
+ <repository>
+ <id>elasticsearch-releases</id>
+ <url>https://maven.elasticsearch.org/releases</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>central</id>
+ <name>Official Maven repository</name>
+ <url>http://repo2.maven.org/maven2/</url>
+ </repository>
+ <repository>
+ <id>ecomp-releases</id>
+ <name>Release Repository</name>
+ <url>${nexus.proxy}/content/repositories/releases/</url>
+ </repository>
+ <repository>
+ <id>ecomp-staging</id>
+ <name>Staging Repository</name>
+ <url>${nexus.proxy}/content/repositories/staging/</url>
+ </repository>
+ <repository>
+ <id>ecomp-snapshots</id>
+ <name>Snapshots Repository</name>
+ <url>${nexus.proxy}/content/repositories/snapshots/</url>
+ </repository>
+ </repositories>
+
+ <distributionManagement>
+
+ <repository>
+ <id>ecomp-releases</id>
+ <name>Release Repository</name>
+ <url>${nexus.proxy}/content/repositories/releases/</url>
+ </repository>
+ <snapshotRepository>
+ <id>ecomp-snapshots</id>
+ <name>Snapshot Repository</name>
+ <url>${nexus.proxy}/content/repositories/snapshots/</url>
+ </snapshotRepository>
+
+ </distributionManagement>
+</project>
+
diff --git a/sdc-os-chef/docker-compose.yml b/sdc-os-chef/docker-compose.yml
new file mode 100644
index 0000000000..c91fb9a140
--- /dev/null
+++ b/sdc-os-chef/docker-compose.yml
@@ -0,0 +1,156 @@
+version: "2.1"
+services:
+################################################################
+ elasticsearch:
+ build: .
+# context: elasticsearch
+ container_name: "sdc-es"
+ restart: "always"
+ image: "ecomp-nexus:51212/ecomp/sdc-elasticsearch:1610.2.13"
+ mem_limit: "1g"
+ memswap_limit: "1g"
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ max-file: "10"
+ ports:
+ - "9200:9200"
+ - "9300:9300"
+ environment:
+# - ES_JAVA_OPTS="-Xms1g -Xmx1g"
+ - ES_HEAP_SIZE=1073741824
+ - HOST_IP:HOSTIP
+ - ENVNAME:DEP_ENV
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /data/ES:/usr/share/elasticsearch/data
+ - /data/ASDC/environments:/root/chef-solo/environments
+ ulimits:
+ memlock: -1
+ nproc: 65535
+ nofile:
+ soft: 100000
+ hard: 100000
+
+
+ cassandra:
+ build: .
+# context: cassandra
+ container_name: "sdc-cs"
+ restart: "always"
+ image: "ecomp-nexus:51212/ecomp/sdc-cassandra:1610.2.13"
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ max-file: "10"
+ ports:
+ - "9042:9042"
+ - "9160:9160"
+ environment:
+ - ES_HEAP_SIZE=1073741824
+ - HOST_IP:HOSTIP
+ - ENVNAME:DEP_ENV
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /data/CS:/var/lib/cassandra
+ - /data/ASDC/environments:/root/chef-solo/environments
+ ulimits:
+ memlock: -1
+ nproc: 65535
+ nofile:
+ soft: 100000
+ hard: 100000
+
+
+
+ kibana:
+ build: .
+ container_name: "sdc-kbn"
+ restart: "always"
+ image: "ecomp-nexus:51212/ecomp/sdc-kibana:1610.2.13"
+ mem_limit: "1g"
+ memswap_limit: "1g"
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ max-file: "10"
+ ports:
+ - "5601:5601"
+ environment:
+ - ENVNAME:DEP_ENV
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /data/ASDC/environments:/root/chef-solo/environments
+ ulimits:
+ memlock: -1
+ nproc: 65535
+ nofile:
+ soft: 100000
+ hard: 100000
+ depends_on:
+ - elasticsearch
+
+
+
+
+ jettyBE:
+ build: .
+ container_name: "sdc-be"
+ image: "ecomp-nexus:51212/ecomp/sdc-backend:1610.2.13"
+ mem_limit: "3g"
+ memswap_limit: "3g"
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ max-file: "10"
+ ports:
+ - "8080:8080"
+ - "8443:8443"
+ environment:
+ - HOST_IP:HOSTIP
+ - ENVNAME:DEP_ENV
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /data/ASDC/logs/BE:/var/lib/jetty/logs
+ - /data/ASDC/environments:/root/chef-solo/environments
+ ulimits:
+ memlock: -1
+ nproc: 65535
+ nofile:
+ soft: 100000
+ hard: 100000
+
+
+
+
+ jettyFE:
+ build: .
+ container_name: "sdc-fe"
+ image: "ecomp-nexus:51212/ecomp/sdc-frontend:1610.2.13"
+ mem_limit: "1g"
+ memswap_limit: "1g"
+ logging:
+ driver: "json-file"
+ options:
+ max-size: "100m"
+ max-file: "10"
+ ports:
+ - "8181:8181"
+ - "9443:9443"
+ environment:
+ - HOST_IP:HOSTIP
+ - ENVNAME:DEP_ENV
+ volumes:
+ - /etc/localtime:/etc/localtime:ro
+ - /data/ASDC/logs/FE:/var/lib/jetty/logs
+ - /data/ASDC/environments:/root/chef-solo/environments
+ ulimits:
+ memlock: -1
+ nproc: 65535
+ nofile:
+ soft: 100000
+ hard: 100000
diff --git a/sdc-os-chef/environments/Template.json b/sdc-os-chef/environments/Template.json
new file mode 100644
index 0000000000..d055cdfe53
--- /dev/null
+++ b/sdc-os-chef/environments/Template.json
@@ -0,0 +1,75 @@
+{
+ "name": "xxx",
+ "description": "OpenSource-xxx",
+ "cookbook_versions": {
+ "Deploy-SDandC": "= 1.0.0"
+ },
+ "json_class": "Chef::Environment",
+ "chef_type": "environment",
+
+ "default_attributes": {
+ "CS_VIP": "yyy",
+ "BE_VIP": "yyy",
+ "FE_VIP": "yyy",
+ "ES_VIP": "yyy",
+ "interfaces": {
+ "application": "eth0",
+ "private": "eth1"
+ },
+ "ECompP": {
+ "ecomp_rest_url": "http://portal.api.simpledemo.openecomp.org:8989/ECOMPPORTAL/auxapi",
+ "ueb_url_list": "23.253.97.75,10.0.11.1",
+ "app_secret": "XftIATw9Jr3VzAcPqt3NnJOu",
+ "app_key": "x9UfO7JsDn8BESVX",
+ "inbox_name": "ECOMP-PORTAL-INBOX",
+ "ecomp_redirect_url": "http://portal.api.simpledemo.openecomp.org:8989/ECOMPPORTAL/login.htm",
+ "app_topic_name": "ECOMP-PORTAL-OUTBOX-SDC1",
+ "decryption_key": "AGLDdG4D04BKm2IxIWEr8o=="
+ },
+ "UEB": {
+ "PublicKey": "iPIxkpAMI8qTcQj8",
+ "SecretKey": "Ehq3WyT4bkif4zwgEbvshGal",
+ "fqdn": ["23.253.97.75", "10.0.11.1"]
+ },
+ "Nodes": {
+ "CS": "yyy",
+ "BE": "yyy",
+ "FE": "yyy",
+ "ES": "yyy"
+ }
+ },
+ "override_attributes": {
+ "FE": {
+ "http_port": "8181",
+ "https_port": "9443"
+ },
+ "BE": {
+ "http_port": "8080",
+ "https_port": "8443"
+ },
+ "elasticsearch": {
+ "cluster_name": "SDC-ES-",
+ "ES_path_home": "/usr/share/elasticsearch",
+ "ES_path_data": "/usr/share/elasticsearch/data",
+ "num_of_replicas": "0",
+ "num_of_shards": "1"
+ },
+
+ "cassandra": {
+ "concurrent_reads": "32",
+ "num_tokens": "256",
+ "data_dir": "/var/lib/cassandra/data",
+ "hinted_handoff_enabled": "true",
+ "cassandra_user": "asdc_user",
+ "cassandra_password": "Aa1234%^!",
+ "concurrent_writes": "32",
+ "cluster_name": "SDC-CS-",
+ "multithreaded_compaction": "false",
+ "cache_dir": "/var/lib/cassandra/saved_caches",
+ "log_file": "/var/lib/cassandra/log/system.log",
+ "phi_convict_threshold": "8",
+ "commitlog_dir": "/var/lib/cassandra/commitlog"
+ }
+ }
+}
+
diff --git a/sdc-os-chef/pom.xml b/sdc-os-chef/pom.xml
new file mode 100644
index 0000000000..ec10e4fdd6
--- /dev/null
+++ b/sdc-os-chef/pom.xml
@@ -0,0 +1,46 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>sdc-os-chef</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.google.code.maven-replacer-plugin</groupId>
+ <artifactId>replacer</artifactId>
+ <version>1.5.3</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>replace</goal>
+ </goals>
+ </execution>
+ </executions>
+
+ <configuration>
+ <basedir>${project.basedir}</basedir>
+ <includes>
+ <include>sdc-backend/Dockerfile</include>
+ <include>sdc-frontend/Dockerfile</include>
+ <include>scripts/docker_run.sh</include>
+ </includes>
+ <replacements>
+ <replacement>
+ <token>__SDC-RELEASE__</token>
+ <value>${project.version}</value>
+ </replacement>
+ </replacements>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/sdc-os-chef/scripts/docker_clean.sh b/sdc-os-chef/scripts/docker_clean.sh
new file mode 100644
index 0000000000..0b2227d45a
--- /dev/null
+++ b/sdc-os-chef/scripts/docker_clean.sh
@@ -0,0 +1,6 @@
+pat=$1
+docker_ids=`docker ps -a | grep ${pat} | awk '{print $1}'`
+for X in ${docker_ids}
+do
+ docker rm -f ${X}
+done
diff --git a/sdc-os-chef/scripts/docker_health.sh b/sdc-os-chef/scripts/docker_health.sh
new file mode 100644
index 0000000000..6fdf826084
--- /dev/null
+++ b/sdc-os-chef/scripts/docker_health.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+curl localhost:9200/_cluster/health?pretty=true
+
+echo "BE health-Check:"
+curl http://localhost:8080/sdc2/rest/healthCheck
+
+echo ""
+echo ""
+echo "FE health-Check:"
+curl http://localhost:8181/sdc1/rest/healthCheck
+
+
+echo ""
+echo ""
+res=`curl -s -X GET -H "Accept: application/json" -H "Content-Type: application/json" -H "USER_ID: jh0003" "http://localhost:8080/sdc2/rest/v1/user/demo" | wc -l`
+if [[ ${res} != 0 ]]
+then
+ echo "Error [${res}] while user existance check"
+ exit ${res}
+fi
+echo "check user existance: OK"
diff --git a/sdc-os-chef/scripts/docker_login.sh b/sdc-os-chef/scripts/docker_login.sh
new file mode 100644
index 0000000000..15544f4d24
--- /dev/null
+++ b/sdc-os-chef/scripts/docker_login.sh
@@ -0,0 +1,8 @@
+pat=$1
+docker_ids=`docker ps | grep ${pat} | awk '{print $1}'|head -1`
+if [ -z "$docker_ids" ]; then
+ echo "No dockers were found matching pattern [${pat}]"
+ exit 0
+else
+ docker exec -it ${docker_ids} bash
+fi
diff --git a/sdc-os-chef/scripts/docker_run.sh b/sdc-os-chef/scripts/docker_run.sh
new file mode 100644
index 0000000000..1b3f370d27
--- /dev/null
+++ b/sdc-os-chef/scripts/docker_run.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+
+
+function usage
+{
+ echo "usage: docker_run.sh [ -r|--release <RELEASE-NAME> ] [ -e|--environment <ENV-NAME> ] [ -p|--port <Docker-hub-port>] [ -h|--help ]"
+}
+
+
+RELEASE=1.0.0-SNAPSHOT
+[ -f /opt/config/env_name.txt ] && DEP_ENV=$(cat /opt/config/env_name.txt) || DEP_ENV=__ENV-NAME__
+PORT=51212
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -r | --release )
+ shift
+ RELEASE=${1}
+ ;;
+ -e | --environment )
+ shift
+ DEP_ENV=${1}
+ ;;
+ -p | --port )
+ shift
+ PORT=${1}
+ ;;
+ -h | --help )
+ usage
+ exit
+ ;;
+ * )
+ usage
+ exit 1
+ esac
+ shift
+done
+
+[ -f /opt/config/nexus_username.txt ] && NEXUS_USERNAME=$(cat /opt/config/nexus_username.txt) || NEXUS_USERNAME=release
+[ -f /opt/config/nexus_password.txt ] && NEXUS_PASSWD=$(cat /opt/config/nexus_password.txt) || NEXUS_PASSWD=sfWU3DFVdBr7GVxB85mTYgAW
+[ -f /opt/config/nexus_docker_repo.txt ] && NEXUS_DOCKER_REPO=$(cat /opt/config/nexus_docker_repo.txt) || NEXUS_DOCKER_REPO=ecomp-nexus:${PORT}
+
+[ -f /opt/config/nexus_username.txt ] && docker login -u $NEXUS_USERNAME -p $NEXUS_PASSWD $NEXUS_DOCKER_REPO
+
+
+# cleanup
+echo "performing old dockers cleanup"
+docker_ids=`docker ps -a | egrep "ecomp-nexus:${PORT}/sdc|sdc|Exit" | awk '{print $1}'`
+for X in ${docker_ids}
+do
+ docker rm -f ${X}
+done
+
+export IP=`ifconfig eth0 | awk -F: '/inet addr/ {gsub(/ .*/,"",$2); print $2}'`
+
+echo ""
+
+# Elastic-Search
+echo "docker run sdc-elasticsearch..."
+docker pull ecomp-nexus:${PORT}/ecomp/sdc-elasticsearch:${RELEASE}
+docker run --detach --name sdc-es --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --memory 1g --memory-swap=1g --ulimit memlock=-1:-1 --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro -e ES_HEAP_SIZE=1024M --volume /data/ES:/usr/share/elasticsearch/data --volume /data/environments:/root/chef-solo/environments --publish 9200:9200 --publish 9300:9300 ecomp-nexus:${PORT}/ecomp/sdc-elasticsearch:${RELEASE}
+
+
+# cassandra
+echo "docker run sdc-cassandra..."
+docker pull ecomp-nexus:${PORT}/ecomp/sdc-cassandra:${RELEASE}
+docker run --detach --name sdc-cs --env ENVNAME="${DEP_ENV}" --env HOST_IP=${IP} --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/CS:/var/lib/cassandra --volume /data/environments:/root/chef-solo/environments --publish 9042:9042 --publish 9160:9160 ecomp-nexus:${PORT}/ecomp/sdc-cassandra:${RELEASE}
+
+
+echo "please wait while CS is starting..."
+echo ""
+c=25 # seconds to wait
+REWRITE="\e[25D\e[1A\e[K"
+while [ $c -gt 0 ]; do
+ c=$((c-1))
+ sleep 1
+ echo -e "${REWRITE}$c"
+done
+echo -e ""
+
+
+# kibana
+echo "docker run sdc-kibana..."
+docker pull ecomp-nexus:${PORT}/ecomp/sdc-kibana:${RELEASE}
+docker run --detach --name sdc-kbn --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 2g --memory-swap=2g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/environments:/root/chef-solo/environments --publish 5601:5601 ecomp-nexus:${PORT}/ecomp/sdc-kibana:${RELEASE}
+
+
+# Back-End
+echo "docker run sdc-backend..."
+docker pull ecomp-nexus:${PORT}/ecomp/sdc-backend:${RELEASE}
+docker run --detach --name sdc-BE --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 4g --memory-swap=4g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/logs/BE/:/var/lib/jetty/logs --volume /data/environments:/root/chef-solo/environments --publish 8443:8443 --publish 8080:8080 ecomp-nexus:${PORT}/ecomp/sdc-backend:${RELEASE}
+
+echo "please wait while BE is starting..."
+echo ""
+c=45 # seconds to wait
+REWRITE="\e[45D\e[1A\e[K"
+while [ $c -gt 0 ]; do
+ c=$((c-1))
+ sleep 1
+ echo -e "${REWRITE}$c"
+done
+echo -e ""
+
+
+# Front-End
+echo "docker run sdc-frontend..."
+docker pull ecomp-nexus:${PORT}/ecomp/sdc-frontend:${RELEASE}
+docker run --detach --name sdc-FE --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 2g --memory-swap=2g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/logs/FE/:/var/lib/jetty/logs --volume /data/environments:/root/chef-solo/environments --publish 9443:9443 --publish 8181:8181 ecomp-nexus:${PORT}/ecomp/sdc-frontend:${RELEASE}
+
+
+
+# running healthCheck scripts
+echo "Running health checks, please wait..."
+echo ""
+c=25 # seconds to wait
+REWRITE="\e[45D\e[1A\e[K"
+while [ $c -gt 0 ]; do
+ c=$((c-1))
+ sleep 1
+ echo -e "${REWRITE}$c"
+done
+echo -e ""
+
+/data/scripts/docker_health.sh
+
+if [ $? -ne 0 ]; then
+ exit 1
+fi
diff --git a/sdc-os-chef/scripts/docker_stats.sh b/sdc-os-chef/scripts/docker_stats.sh
new file mode 100644
index 0000000000..9258b3471d
--- /dev/null
+++ b/sdc-os-chef/scripts/docker_stats.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+FILE='/data/logs/docker_stats.out'
+FE_ID=`docker ps|grep sdc-front|awk '{print $1}'`
+BE_ID=`docker ps|grep sdc-back |awk '{print $1}'`
+
+echo `date` >> ${FILE}
+
+if [ ! -z "${FE_ID}" ]; then
+ docker stats ${FE_ID} --no-stream >> /data/logs/docker_stats.out
+else
+ echo "frontend Docker is down!!!" >> /data/logs/docker_stats.out
+fi
+
+if [ ! -z "${BE_ID}" ]; then
+ docker stats ${BE_ID} --no-stream >> /data/logs/docker_stats.out
+else
+ echo "backend Docker is down!!!" >> /data/logs/docker_stats.out
+fi
+
+echo "------------------------------------------" >> ${FILE}
+
+grep -v "^\-" ${FILE} |grep -v ^CONT| awk 'BEGIN {
+ split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",month)
+ for (i in month) {
+ month_nums[month[i]]=i
+ }
+}
+/UTC/ {
+ d=$6"-"$2"-"substr("00",0,2-length($3))$3"-"substr($4,0,5)
+ next
+}
+/GiB/ {
+ print $1" "d" "$3" "$8
+ next
+}' > `echo ${FILE}|awk -F"." '{ print $1".csv"}'`
diff --git a/sdc-os-chef/scripts/docker_watchdog.sh b/sdc-os-chef/scripts/docker_watchdog.sh
new file mode 100644
index 0000000000..cc8452537b
--- /dev/null
+++ b/sdc-os-chef/scripts/docker_watchdog.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+if [ $# -eq 0 ]; then
+ echo "No arguments supplied"
+ exit 1
+fi
+
+
+RELEASE=$1
+DEP_ENV=$2
+
+
+docker_ids=`docker ps -a | egrep "Exit" | awk '{print $1}'`
+for X in ${docker_ids}
+do
+ docker rm -f ${X}
+done
+
+export IP=`ifconfig eth0 | awk -F: '/inet addr/ {gsub(/ .*/,"",$2); print $2}'`
+
+echo ""
+
+# Back-End
+my_cnt=`docker ps|egrep -c "sdc-backend:${RELEASE}"`
+if [ "${my_cnt}" -eq "1" ]; then
+# echo "`date` - BE is running" >> /data/ASDC/logs/watchdog.log
+ echo "`date` - BE is running" > /dev/null
+else
+ echo "`date` - BE was down" >> /data/ASDC/logs/watchdog.log
+ docker pull ecomp-nexus:51212/ecomp/sdc-backend:${RELEASE}
+ docker run --detach --name sdc-BE --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 3g --memory-swap=3g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/ASDC/logs/BE/:/var/lib/jetty/logs --volume /data/ASDC/environments:/root/chef-solo/environments --publish 8443:8443 --publish 8080:8080 ecomp-nexus:51212/ecomp/sdc-backend:${RELEASE}
+fi
+
+# Front-End
+my_cnt=`docker ps|egrep -c "sdc-frontend:${RELEASE}"`
+if [ "${my_cnt}" -eq "1" ]; then
+# echo "`date` - FE is running" >> /data/ASDC/logs/watchdog.log
+ echo "`date` - FE is running" >> /dev/null
+else
+ echo "`date` - FE was down" >> /data/ASDC/logs/watchdog.log
+ docker pull ecomp-nexus:51212/ecomp/sdc-frontend:${RELEASE}
+ docker run --detach --name sdc-FE --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 2g --memory-swap=2g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/ASDC/logs/FE/:/var/lib/jetty/logs --volume /data/ASDC/environments:/root/chef-solo/environments --publish 9443:9443 --publish 8181:8181 ecomp-nexus:51212/ecomp/sdc-frontend:${RELEASE}
+fi
diff --git a/sdc-os-chef/scripts/restart_docker.sh b/sdc-os-chef/scripts/restart_docker.sh
new file mode 100644
index 0000000000..619f51af5c
--- /dev/null
+++ b/sdc-os-chef/scripts/restart_docker.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+
+function usage
+{
+ echo "usage: docker_run.sh [ -r|--release <RELEASE-NAME> ] [ -e|--environment <ENV-NAME> ] [ -p|--port <Docker-hub-port>] [ -h|--help ]"
+}
+
+
+RELEASE=__SDC-RELEASE__
+[ -f /opt/config/env_name.txt ] && DEP_ENV=$(cat /opt/config/env_name.txt) || DEP_ENV=__ENV-NAME__
+PORT=51212
+
+while [ "$1" != "" ]; do
+ case $1 in
+ -d | --docker )
+ shift
+ DOCKER=${1}
+ ;;
+ -r | --release )
+ shift
+ RELEASE=${1}
+ ;;
+ -e | --environment )
+ shift
+ DEP_ENV=${1}
+ ;;
+ -p | --port )
+ shift
+ PORT=${1}
+ ;;
+ -h | --help )
+ usage
+ exit
+ ;;
+ * )
+ usage
+ exit 1
+ esac
+ shift
+done
+
+[ -f /opt/config/nexus_username.txt ] && NEXUS_USERNAME=$(cat /opt/config/nexus_username.txt) || NEXUS_USERNAME=release
+[ -f /opt/config/nexus_password.txt ] && NEXUS_PASSWD=$(cat /opt/config/nexus_password.txt) || NEXUS_PASSWD=sfWU3DFVdBr7GVxB85mTYgAW
+[ -f /opt/config/nexus_docker_repo.txt ] && NEXUS_DOCKER_REPO=$(cat /opt/config/nexus_docker_repo.txt) || NEXUS_DOCKER_REPO=ecomp-nexus:${PORT}
+
+[ -f /opt/config/nexus_username.txt ] && docker login -u $NEXUS_USERNAME -p $NEXUS_PASSWD $NEXUS_DOCKER_REPO
+
+
+# cleanup
+echo "performing old dockers cleanup"
+docker_ids=`docker ps -a | egrep "${DOCKER}|Exit" | awk '{print $1}'`
+for X in ${docker_ids}
+do
+ docker rm -f ${X}
+done
+
+export IP=`ifconfig eth0 | awk -F: '/inet addr/ {gsub(/ .*/,"",$2); print $2}'`
+
+echo ""
+
+case ${DOCKER} in
+ elastic | es )
+ # Elastic-Search
+ echo "docker run sdc-elasticsearch..."
+ docker pull ecomp-nexus:${PORT}/ecomp/sdc-elasticsearch:${RELEASE}
+ docker run --detach --name sdc-es --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --memory 1g --memory-swap=1g --ulimit memlock=-1:-1 --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro -e ES_HEAP_SIZE=1024M --volume /data/ES:/usr/share/elasticsearch/data --volume /data/environments:/root/chef-solo/environments --publish 9200:9200 --publish 9300:9300 ecomp-nexus:${PORT}/ecomp/sdc-elasticsearch:${RELEASE}
+ ;;
+
+ cassandra | cs )
+ # cassandra
+ echo "docker run sdc-cassandra..."
+ docker pull ecomp-nexus:${PORT}/ecomp/sdc-cassandra:${RELEASE}
+ docker run --detach --name sdc-cs --env ENVNAME="${DEP_ENV}" --env HOST_IP=${IP} --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/CS:/var/lib/cassandra --volume /data/environments:/root/chef-solo/environments --publish 9042:9042 --publish 9160:9160 ecomp-nexus:${PORT}/ecomp/sdc-cassandra:${RELEASE}
+ ;;
+
+ kibana | kbn )
+ # kibana
+ echo "docker run sdc-kibana..."
+ docker pull ecomp-nexus:${PORT}/ecomp/sdc-kibana:${RELEASE}
+ docker run --detach --name sdc-kbn --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 1g --memory-swap=1g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/environments:/root/chef-solo/environments --publish 5601:5601 ecomp-nexus:${PORT}/ecomp/sdc-kibana:${RELEASE}
+ ;;
+
+ backend | be )
+ # Back-End
+ echo "docker run sdc-backend..."
+ docker pull ecomp-nexus:${PORT}/ecomp/sdc-backend:${RELEASE}
+ docker run --detach --name sdc-BE --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 4g --memory-swap=4g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/logs/BE/:/var/lib/jetty/logs --volume /data/environments:/root/chef-solo/environments --publish 8443:8443 --publish 8080:8080 ecomp-nexus:${PORT}/ecomp/sdc-backend:${RELEASE}
+ ;;
+
+ frontend | fe )
+ # Front-End
+ echo "docker run sdc-frontend..."
+ docker pull ecomp-nexus:${PORT}/ecomp/sdc-frontend:${RELEASE}
+ docker run --detach --name sdc-FE --env HOST_IP=${IP} --env ENVNAME="${DEP_ENV}" --log-driver=json-file --log-opt max-size=100m --log-opt max-file=10 --ulimit memlock=-1:-1 --memory 2g --memory-swap=2g --ulimit nofile=4096:100000 --volume /etc/localtime:/etc/localtime:ro --volume /data/logs/FE/:/var/lib/jetty/logs --volume /data/environments:/root/chef-solo/environments --publish 9443:9443 --publish 8181:8181 ecomp-nexus:${PORT}/ecomp/sdc-frontend:${RELEASE}
+ ;;
+esac
diff --git a/sdc-os-chef/sdc-backend/Dockerfile.template b/sdc-os-chef/sdc-backend/Dockerfile.template
new file mode 100644
index 0000000000..88a0ad6597
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/Dockerfile.template
@@ -0,0 +1,30 @@
+FROM jetty
+
+RUN apt-get -y update
+RUN apt-get -y install apt-utils
+RUN apt-get -y install curl
+RUN apt-get -y install vim
+RUN apt-get -y install jq
+RUN apt-get -y install python libssl-dev libcurl4-openssl-dev python-dev gcc
+
+COPY chef-solo /root/chef-solo/
+COPY chef-repo/cookbooks/. /root/chef-solo/cookbooks/
+
+RUN python /root/chef-solo/cookbooks/sdc-normatives/files/default/get-pip.py
+RUN pip install pycurl
+
+ENV BASE_NEXUS zl999y:ChangeMe@10.208.197.75:8443/repository/maven-public/org/openecomp/sdc
+
+# install chef-solo
+RUN curl -L https://www.opscode.com/chef/install.sh | bash
+
+#ADD https://${BASE_NEXUS}/api-docs/1610.0.0-SNAPSHOT/api-docs-1610.0.0-20161211.143739-1.war /var/lib/jetty/webapps/
+ADD onboarding-be-__SDC-RELEASE__.war /var/lib/jetty/webapps/
+ADD catalog-be-__SDC-RELEASE__.war /var/lib/jetty/webapps/
+RUN chown -R jetty:jetty /var/lib/jetty/webapps
+
+COPY startup.sh /root/
+
+RUN chmod 770 /root/startup.sh
+
+ENTRYPOINT [ "/root/startup.sh" ]
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/attributes/default.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/attributes/default.rb
new file mode 100644
index 0000000000..4287ca8617
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/attributes/default.rb
@@ -0,0 +1 @@
+# \ No newline at end of file
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-ecomp-error-configuration.yaml b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..9d7cd74a2b
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-ecomp-error-configuration.yaml
@@ -0,0 +1,383 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM, AUTHENTICATION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+## Range of BE codes - 3010-7999
+
+errors:
+
+ BeRestApiGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4000,
+ severity: ERROR,
+ description: "Unexpected error during BE REST API execution",
+ alarmSeverity: CRITICAL
+ }
+
+ BeHealthCheckError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3010,
+ severity: ERROR,
+ description: "Error during BE Health Check",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInitializationError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4019,
+ severity: ERROR,
+ description: "Catalog-BE was not initialized properly",
+ alarmSeverity: CRITICAL
+ }
+
+ BeResourceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3011,
+ severity: ERROR,
+ description: "Mandatory resource %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeServiceMissingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3012,
+ severity: ERROR,
+ description: "Mandatory service %s cannot be found in repository",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedAddingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3013,
+ severity: ERROR,
+ description: "Failed to add resource instance of resource %s to service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeIncorrectServiceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3014,
+ severity: ERROR,
+ description: "Service %s is not valid",
+ alarmSeverity: MAJOR
+ }
+
+ BeRepositoryDeleteError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3015,
+ severity: ERROR,
+ description: "Failed to delete object %s from repository",
+ alarmSeverity: CRITICAL
+ }
+
+ BeRepositoryQueryError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3016,
+ severity: ERROR,
+ description: "Failed to fetch from repository %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeInvalidConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3017,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. Value configured is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4001,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3019,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebObjectNotFoundError: {
+ type: DATA_ERROR,
+ code: ASDC_4005,
+ severity: ERROR,
+ description: "Error occured during access to U-EB Server. Data not found: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3021,
+ severity: ERROR,
+ description: "Error occured in Distribution Engine. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebAuthenticationError: {
+ type: AUTHENTICATION_PROBLEM,
+ code: ASDC_4003,
+ severity: ERROR,
+ description: "Authentication problem towards U-EB server. Reason: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeUebUnkownHostError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_4002,
+ severity: ERROR,
+ description: "Connection problem towards U-EB server. Cannot reach host %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionEngineInvalidArtifactType: {
+ type: DATA_ERROR,
+ code: ASDC_4006,
+ severity: WARN,
+ description: "The artifact type %s does not appear in the list of valid artifacts %s",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4008,
+ severity: WARN,
+ description: "The type %s of %s is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeInvalidValueError: {
+ type: DATA_ERROR,
+ code: ASDC_3028,
+ severity: WARN,
+ description: "The value %s of %s from type %s is invalid",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeletingResourceInstanceError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_3029,
+ severity: ERROR,
+ description: "Failed to delete resource instance %s from service %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingConfigurationError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3030,
+ severity: FATAL,
+ description: "Configuration parameter %s is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeConfigurationInvalidListSizeError: {
+ type: CONFIG_ERROR,
+ code: ASDC_3031,
+ severity: FATAL,
+ description: "Configuration parameter %s is invalid. At least %s values shall be configured",
+ alarmSeverity: MAJOR
+ }
+
+ ErrorConfigFileFormat: {
+ type: CONFIG_ERROR,
+ code: ASDC_3032,
+ severity: ERROR,
+ description: "Error element not found in YAML name: %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeMissingArtifactInformationError: {
+ type: DATA_ERROR,
+ code: ASDC_4010,
+ severity: ERROR,
+ description: "Artifact uploaded has missing information. Missing %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4011,
+ severity: ERROR,
+ description: "Artifact %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactPayloadInvalid: {
+ type: DATA_ERROR,
+ code: ASDC_4012,
+ severity: ERROR,
+ description: "Payload of artifact uploaded is invalid (invalid MD5 or encryption)",
+ alarmSeverity: MAJOR
+ }
+
+ BeUserMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4009,
+ severity: ERROR,
+ description: "User %s requested is not found",
+ alarmSeverity: MAJOR
+ }
+
+ BeArtifactInformationInvalidError: {
+ type: DATA_ERROR,
+ code: ASDC_4013,
+ severity: ERROR,
+ description: "Input for artifact metadata is invalid",
+ alarmSeverity: MAJOR
+ }
+ BeFailedAddingCapabilityTypeError: {
+ type: DATA_ERROR,
+ code: ASDC_4015,
+ severity: ERROR,
+ description: "Failed adding capability type",
+ alarmSeverity: CRITICAL
+ }
+
+ BeCapabilityTypeMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4016,
+ severity: ERROR,
+ description: "Capability Type %s not found",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInterfaceMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4020,
+ severity: ERROR,
+ description: "Interface %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeDaoSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4014,
+ severity: ERROR,
+ description: "Operation towards database failed",
+ alarmSeverity: CRITICAL
+ }
+
+ BeSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4017,
+ severity: ERROR,
+ description: "Unexpected error during operation",
+ alarmSeverity: CRITICAL
+ }
+
+ BeFailedLockObjectError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4007,
+ severity: WARN,
+ description: "Failed to lock object for update",
+ alarmSeverity: CRITICAL
+ }
+
+ BeInvalidJsonInput: {
+ type: SYSTEM_ERROR,
+ code: ASDC_4018,
+ severity: ERROR,
+ description: "Failed to convert json input to object",
+ alarmSeverity: MAJOR
+ }
+
+ BeDistributionMissingError: {
+ type: DATA_ERROR,
+ code: ASDC_4021,
+ severity: ERROR,
+ description: "Distribution %s required is missing",
+ alarmSeverity: MAJOR
+ }
+
+ BeHealthCheckRecovery: {
+ type: RECOVERY,
+ code: ASDC_4022,
+ severity: INFO,
+ description: "BE Health Check Recovery",
+ alarmSeverity: INFORMATIONAL
+ }
+ BeFailedCreateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6000,
+ severity: ERROR,
+ description: "Failed to create node %s on graph. status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeFailedUpdateNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6001,
+ severity: ERROR,
+ description: "Failed to update node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedDeleteNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6002,
+ severity: ERROR,
+ description: "Failed to delete node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedRetrieveNodeError: {
+ type: DATA_ERROR,
+ code: ASDC_6003,
+ severity: ERROR,
+ description: "Failed to retrieve node %s from graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeExecuteRollbackError: {
+ type: DATA_ERROR,
+ code: ASDC_6004,
+ severity: ERROR,
+ description: "Going to execute rollback on graph.",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindParentError: {
+ type: DATA_ERROR,
+ code: ASDC_6005,
+ severity: ERROR,
+ description: "Failed to find parent node %s on graph. Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAllNodesError: {
+ type: DATA_ERROR,
+ code: ASDC_6006,
+ severity: ERROR,
+ description: "Failed to fetch all nodes with type %s of parent node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6007,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+
+ BeFailedFindAssociationError: {
+ type: DATA_ERROR,
+ code: ASDC_6008,
+ severity: ERROR,
+ description: "Cannot find node with type %s associated with node %s . Status is %s",
+ alarmSeverity: MAJOR
+ }
+ BeComponentCleanerSystemError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_6009,
+ severity: ERROR,
+ description: "Error occured in Component Cleaner Task. Failed operation: %s",
+ alarmSeverity: MAJOR
+ }
+ \ No newline at end of file
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-error-configuration.yaml b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-error-configuration.yaml
new file mode 100644
index 0000000000..0054ce2be9
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-error-configuration.yaml
@@ -0,0 +1,1694 @@
+# Errors
+errors:
+ OK: {
+ code: 200,
+ message: "OK"
+ }
+ CREATED: {
+ code: 201,
+ message: "OK"
+ }
+ NO_CONTENT: {
+ code: 204,
+ message: "No Content"
+ }
+#--------POL4050-----------------------------
+ NOT_ALLOWED: {
+ code: 405,
+ message: "Error: Method not allowed.",
+ messageId: "POL4050"
+ }
+#--------POL5000-----------------------------
+ GENERAL_ERROR: {
+ code: 500,
+ message: "Error: Internal Server Error. Please try again later.",
+ messageId: "POL5000"
+ }
+#---------POL5001------------------------------
+ MISSING_X_ECOMP_INSTANCE_ID: {
+ code: 400 ,
+ message: "Error: Missing 'X-ECOMP-InstanceID' HTTP header.",
+ messageId: "POL5001"
+ }
+#---------POL5002------------------------------
+ AUTH_REQUIRED: {
+ code: 401 ,
+ message: "Error: Authentication is required to use the API.",
+ messageId: "POL5002"
+ }
+#---------POL5003------------------------------
+ AUTH_FAILED: {
+ code: 403 ,
+ message: "Error: Not authorized to use the API.",
+ messageId: "POL5003"
+ }
+#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }#---------SVC4000-----------------------------
+ INVALID_CONTENT: {
+ code: 400,
+ message: "Error: Invalid content.",
+ messageId: "SVC4000"
+ }
+#---------SVC4002-----------------------------
+ MISSING_INFORMATION: {
+ code: 403,
+ message: "Error: Missing information.",
+ messageId: "SVC4002"
+ }
+#---------SVC4003------------------------------
+# %1 - Users's USER_ID
+ USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: User '%1' was not found.",
+ messageId: "SVC4003"
+ }
+#---------SVC4004-----------------------------
+# %1 - Users's email address
+ INVALID_EMAIL_ADDRESS: {
+ code: 400,
+ message: "Error: Invalid email address '%1'.",
+ messageId: "SVC4004"
+ }
+#---------SVC4005------------------------------
+# %1 - role
+ INVALID_ROLE: {
+ code: 400,
+ message: "Error: Invalid role '%1'.",
+ messageId: "SVC4005"
+ }
+#---------SVC4006------------------------------
+# %1 - Users's USER_ID
+ USER_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: User with '%1' ID already exists.",
+ messageId: "SVC4006"
+ }
+#---------SVC4007------------------------------
+ DELETE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator can only be deleted by another administrator.",
+ messageId: "SVC4007"
+ }
+#---------SVC4008-----------------------------
+# %1 - Users's userId
+ INVALID_USER_ID: {
+ code: 400,
+ message: "Error: Invalid userId '%1'.",
+ messageId: "SVC4008"
+ }
+#---------SVC4049------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_CONTACT: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 contact id.",
+ messageId: "SVC4049"
+ }
+#---------SVC4050-----------------------------
+# %1 - Service/Resource/Additional parameter
+# %2 - service/resource/label name
+ COMPONENT_NAME_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: %1 with name '%2' already exists.",
+ messageId: "SVC4050"
+ }
+#---------SVC4051------------------------------
+# %1 - resource/service
+ COMPONENT_MISSING_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 category.",
+ messageId: "SVC4051"
+ }
+
+#---------SVC4052------------------------------
+ COMPONENT_MISSING_TAGS: {
+ code: 400,
+ message: "Error: Invalid Content. At least one tag has to be specified.",
+ messageId: "SVC4052"
+ }
+
+#---------SVC4053------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 description.",
+ messageId: "SVC4053"
+ }
+#---------SVC4054------------------------------
+# %1 - resource/service
+ COMPONENT_INVALID_CATEGORY: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 category.",
+ messageId: "SVC4054"
+ }
+#---------SVC4055------------------------------
+ MISSING_VENDOR_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor name.",
+ messageId: "SVC4055"
+ }
+#---------SVC4056------------------------------
+ MISSING_VENDOR_RELEASE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing vendor release.",
+ messageId: "SVC4056"
+ }
+
+#---------SVC4057------------------------------
+ MISSING_DERIVED_FROM_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing derived from template specification.",
+ messageId: "SVC4057"
+ }
+
+#---------SVC4058------------------------------
+# %1 - service/resource
+ COMPONENT_MISSING_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 icon.",
+ messageId: "SVC4058"
+ }
+#---------SVC4059------------------------------
+# %1 - service/resource
+ COMPONENT_INVALID_ICON: {
+ code: 400,
+ message: "Error: Invalid Content. Invalid %1 icon.",
+ messageId: "SVC4059"
+ }
+#---------SVC4060------------------------------
+ PARENT_RESOURCE_NOT_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Derived from resource template was not found.",
+ messageId: "SVC4060"
+ }
+#---------SVC4061------------------------------
+ MULTIPLE_PARENT_RESOURCE_FOUND: {
+ code: 400,
+ message: "Error: Invalid Content. Multiple derived from resource template is not allowed.",
+ messageId: "SVC4061"
+ }
+
+#---------SVC4062------------------------------
+# %1 - service/resource
+ MISSING_COMPONENT_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. Missing %1 name.",
+ messageId: "SVC4062"
+ }
+#---------SVC4063------------------------------
+ #%1  -  resource/service name
+ RESOURCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource was not found.",
+ messageId: "SVC4063"
+ }
+
+#---------SVC4064------------------------------
+# %1 - Service/Resource
+ COMPONENT_INVALID_DESCRIPTION: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description contains non-english characters.",
+ messageId: "SVC4064"
+ }
+#---------SVC4065------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name length
+ COMPONENT_DESCRIPTION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 description exceeds limit of %2 characters.",
+ messageId: "SVC4065"
+ }
+#---------SVC4066------------------------------
+# %1 - max length
+ COMPONENT_TAGS_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Tags overall length exceeds limit of %1 characters.",
+ messageId: "SVC4066"
+ }
+#---------SVC4067------------------------------
+# %1 - max length
+ VENDOR_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor name exceeds limit of %1 characters.",
+ messageId: "SVC4067"
+ }
+#---------SVC4068------------------------------
+# %1 - max length
+ VENDOR_RELEASE_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Vendor release exceeds limit of %1 characters.",
+ messageId: "SVC4068"
+ }
+
+#---------SVC4069------------------------------
+# %1 - Service/Resource/Product
+ COMPONENT_INVALID_CONTACT_ID: {
+ code: 400,
+ message: "Error: Invalid Content. %1 contact id should be in format 'mnnnnnn' or 'aannna' or 'aannnn', where m=m ,a=a-zA-Z and n=0-9",
+ messageId: "SVC4069"
+ }
+#---------SVC4070------------------------------
+# %1 - Service/Resource
+ INVALID_COMPONENT_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. %1 name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4070"
+ }
+
+#---------SVC4071------------------------------
+ INVALID_VENDOR_NAME: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor name is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4071"
+ }
+#---------SVC4072------------------------------
+ INVALID_VENDOR_RELEASE: {
+ code: 400,
+ message: 'Error: Invalid Content. Vendor release is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4072"
+ }
+#---------SVC4073------------------------------
+# %1 - Service/Resource
+# %2 - max resource/service name
+ COMPONENT_NAME_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 name exceeds limit of %2 characters.",
+ messageId: "SVC4073"
+ }
+#---------SVC4080------------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CHECKOUT_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for modification by %3 %4(%5).",
+ messageId: "SVC4080"
+ }
+#---------SVC4081-----------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_IN_CERT_IN_PROGRESS_STATE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is locked for certification by %3 %4(%5).",
+ messageId: "SVC4081"
+ }
+
+#-----------SVC4082---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_SENT_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is sent for certification by %3 %4(%5).",
+ messageId: "SVC4082"
+ }
+#-----------SVC4083---------------------------
+ COMPONENT_VERSION_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Version of this %1 was already promoted.",
+ messageId: "SVC4083"
+ }
+#-----------SVC4084---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CHECKED_IN: {
+ code: 409,
+ message: "Error: The current version of '%1' %2 was already checked-in by %3 %4(%5).",
+ messageId: "SVC4084"
+ }
+#-----------SVC4085---------------------------
+# %1 - resource/service/product name
+# %2 - resource/service/product
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_CHECKOUT_BY_ANOTHER_USER: {
+ code: 403,
+ message: "Error: %1 %2 has already been checked out by %3 %4(%5).",
+ messageId: "SVC4085"
+ }
+#-----------SVC4086---------------------------
+# %1  - resource/service name
+# %2  - resource/service
+ COMPONENT_IN_USE: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is in use by another user.",
+ messageId: "SVC4086"
+ }
+#-----------SVC4087---------------------------
+# %1 - component name
+# %2 - resource/service/product
+ COMPONENT_HAS_NEWER_VERSION: {
+ code: 409,
+ message: "Error: Checking out of the requested version of the '%1' %2 is not allowed as a newer version exists.",
+ messageId: "SVC4087"
+ }
+#-----------SVC4088---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+# %3 - First name of last modifier
+# %4 - Last name of last modifier
+# %5 - USER_ID of last modifier
+ COMPONENT_ALREADY_CERTIFIED: {
+ code: 403,
+ message: "Error: Requested %1 %2 has already been certified by %3 %4(%5).",
+ messageId: "SVC4088"
+ }
+#-----------SVC4089---------------------------
+# %1 - resource/service name
+# %2 - resource/service
+ COMPONENT_NOT_READY_FOR_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification.",
+ messageId: "SVC4089"
+ }
+#-----------SVC4100---------------------------
+#%1 - property name
+ PROPERTY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' property was not found.",
+ messageId: "SVC4100"
+ }
+#-----------SVC4101---------------------------
+#%1 - property name
+ PROPERTY_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Property with '%1' name already exists.",
+ messageId: "SVC4101"
+ }
+
+#-----------SVC4102---------------------------
+# %1 - capability type name
+ CAPABILITY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Capability Type with name '%1' already exists.",
+ messageId: "SVC4102"
+ }
+#-----------SVC4114---------------------------
+ AUTH_FAILED_INVALIDE_HEADER: {
+ code: 400,
+ message: "Error: Invalid Authorization header.",
+ messageId: "SVC4114"
+ }
+#-----------SVC4115---------------------------
+# %1 - capability type name
+ MISSING_CAPABILITY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Capability Type '%1'.",
+ messageId: "SVC4115"
+ }
+ RESOURCE_INSTANCE_BAD_REQUEST: {
+ code: 400,
+ message: "Error: Invalid Content.",
+ messageId: "SVC4116"
+ }
+#-----------SVC4117---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_MATCH_NOT_FOUND: {
+ code: 404,
+ message: "Error: Match not found between resource instance '%1' and resource instance '%2' for requirement '%3'.",
+ messageId: "SVC4117"
+ }
+#-----------SVC4118---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Resource instances '%1' and '%2' are already associated with requirement '%3'.",
+ messageId: "SVC4118"
+ }
+#-----------SVC4119---------------------------
+# %1 - resource instance name
+# %2 - resource instance name
+# %3 - requirement name
+ RESOURCE_INSTANCE_RELATION_NOT_FOUND: {
+ code: 404,
+ message: "Error: No relation found between resource instances '%1' and '%2' for requirement '%3'.",
+ messageId: "SVC4119"
+ }
+#-----------SVC4120---------------------------
+# %1 - User's USER_ID
+ USER_INACTIVE: {
+ code: 404,
+ message: "Error: User %1 was not found.",
+ messageId: "SVC4120"
+ }
+#-----------SVC4121---------------------------
+# %1 - User's USER_ID
+ USER_HAS_ACTIVE_ELEMENTS: {
+ code: 403,
+ message: "Error: User with %1 ID can not be deleted since it has active elements(resources/services/artifacts).",
+ messageId: "SVC4121"
+ }
+#-----------SVC4122---------------------------
+# %1 - artifact type
+ ARTIFACT_TYPE_NOT_SUPPORTED: {
+ code: 400,
+ message: "Error: Invalid artifact type '%1'.",
+ messageId: "SVC4122"
+ }
+#-----------SVC4123---------------------------
+ ARTIFACT_LOGICAL_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Artifact logical name cannot be changed.",
+ messageId: "SVC4123"
+ }
+#-----------SVC4124---------------------------
+ MISSING_ARTIFACT_TYPE: {
+ code: 400,
+ message: "Error: Missing artifact type.",
+ messageId: "SVC4124"
+ }
+#-----------SVC4125---------------------------
+# %1-artifact name
+ ARTIFACT_EXIST: {
+ code: 400,
+ message: "Error: Artifact '%1' already exists.",
+ messageId: "SVC4125"
+ }
+#---------SVC4126------------------------------
+# %1 - resource/service/product/...
+# %2 - field (tag, vendor name...)
+ INVALID_FIELD_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 format.",
+ messageId: "SVC4126"
+ }
+#-----------SVC4127---------------------------
+ ARTIFACT_INVALID_MD5: {
+ code: 400,
+ message: "Error: Invalid artifact checksum.",
+ messageId: "SVC4127"
+ }
+#-----------SVC4128---------------------------
+ MISSING_ARTIFACT_NAME: {
+ code: 400,
+ message: "Error: Invalid content. Missing artifact name.",
+ messageId: "SVC4128"
+ }
+#-----------SVC4129---------------------------
+ MISSING_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing PROJECT_CODE number.",
+ messageId: "SVC4129"
+ }
+#-----------SVC4130---------------------------
+ INVALID_PROJECT_CODE: {
+ code: 400,
+ message: "Error: Invalid Content. PROJECT_CODE number must be numeric from 5 up to 10 digits.",
+ messageId: "SVC4130"
+ }
+#-----------SVC4131---------------------------
+# %1-resource/service
+# %2-srtifact/artifacts
+# %3-semicolomn separated list of artifact
+ COMPONENT_MISSING_MANDATORY_ARTIFACTS: {
+ code: 403,
+ message: "Error: Missing mandatory informational %1 %2: [%3].",
+ messageId: "SVC4131"
+ }
+#-----------SVC4132---------------------------
+# %1 - lifecycle type name
+ LIFECYCLE_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Lifecycle Type with name '%1' already exists.",
+ messageId: "SVC4132"
+ }
+#-----------SVC4133---------------------------
+# %1 - service version
+# %2 - service name
+ SERVICE_NOT_AVAILABLE_FOR_DISTRIBUTION: {
+ code: 403,
+ message: "Error: Version %1 of '%2' service is not available for distribution.",
+ messageId: "SVC4133"
+ }
+#-----------SVC4134---------------------------
+ MISSING_LIFECYCLE_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing interface life-cycle type.",
+ messageId: "SVC4134"
+ }
+#---------SVC4135------------------------------
+ SERVICE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service category cannot be changed once the service is certified.",
+ messageId: "SVC4135"
+ }
+#---------SVC4136------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_AVAILABLE: {
+ code: 500,
+ message: "Error: Requested distribution environment '%1' is not available.",
+ messageId: "SVC4136"
+ }
+#---------SVC4137------------------------------
+# %1 - distribution environment name
+ DISTRIBUTION_ENVIRONMENT_NOT_FOUND: {
+ code: 400,
+ message: "Error: Requested distribution environment '%1' was not found.",
+ messageId: "SVC4137"
+ }
+#---------SVC4138------------------------------
+ DISTRIBUTION_ENVIRONMENT_INVALID: {
+ code: 400,
+ message: "Error: Invalid distribution environment.",
+ messageId: "SVC4138"
+ }
+#---------SVC4139------------------------------
+# %1 - service name
+ DISTRIBUTION_ARTIFACT_NOT_FOUND: {
+ code: 409,
+ message: "Error: Service '%1' cannot be distributed due to missing deployment artifacts.",
+ messageId: "SVC4139"
+ }
+#---------SVC4200------------------------------
+# %1 - Service/Resource
+# %2 - max icon name length
+ COMPONENT_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 icon name exceeds limit of %2 characters.",
+ messageId: "SVC4200"
+ }
+#---------SVC4300------------------------------
+ RESTRICTED_ACCESS: {
+ code: 403,
+ message: "Error: Restricted access.",
+ messageId: "SVC4300"
+ }
+#---------SVC4301------------------------------
+ RESTRICTED_OPERATION: {
+ code: 409,
+ message: "Error: Restricted operation.",
+ messageId: "SVC4301"
+ }
+#---------SVC4500------------------------------
+ MISSING_BODY: {
+ code: 400 ,
+ message: "Error: Missing request body.",
+ messageId: "SVC4500"
+ }
+#---------SVC4501------------------------------
+ MISSING_PUBLIC_KEY: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'apiPublicKey'." ,
+ messageId: "SVC4501"
+ }
+#---------SVC4502------------------------------
+ DISTRIBUTION_ENV_DOES_NOT_EXIST: {
+ code: 400 ,
+ message: "Error: Invalid Body : Missing mandatory parameter 'distrEnvName'." ,
+ messageId: "SVC4502"
+ }
+#-----------SVC4503---------------------------
+# %1 - service name
+ SERVICE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' service was not found.",
+ messageId: "SVC4503"
+ }
+
+#---------SVC4504------------------------------
+# %1 - Service/Resource
+# %2 - service/resource version
+ COMPONENT_VERSION_NOT_FOUND: {
+ code: 404,
+ message: "Error: %1 version %2 was not found.",
+ messageId: "SVC4504"
+ }
+#-----------SVC4505---------------------------
+ #%1-artifact name
+
+ ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Artifact '%1' was not found.",
+ messageId: "SVC4505"
+ }
+#---------SVC4506------------------------------
+ MISSING_ENV_NAME: {
+ code: 400 ,
+ message: "Error: Invalid Content. Missing mandatory parameter 'distrEnvName'.",
+ messageId: "SVC4506"
+ }
+#---------SVC4507------------------------------
+ COMPONENT_INVALID_TAGS_NO_COMP_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. One of the tags should be the component name.",
+ messageId: "SVC4507"
+ }
+
+#---------SVC4508------------------------------
+ SERVICE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Service name cannot be changed once the service is certified.",
+ messageId: "SVC4508"
+ }
+
+#---------SVC4509------------------------------
+ SERVICE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the service is certified.",
+ messageId: "SVC4509"
+ }
+#---------SVC4510------------------------------
+# %1 - icon name max length
+ SERVICE_ICON_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Icon name exceeds limit of %1 characters.",
+ messageId: "SVC4510"
+ }
+#---------SVC4511------------------------------
+ DISTRIBUTION_REQUESTED_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested distribution was not found.",
+ messageId: "SVC4511"
+ }
+#---------SVC4512------------------------------
+# %1 - Distribution ID
+ DISTRIBUTION_REQUESTED_FAILED: {
+ code: 403,
+ message: "Error: Requested distribution '%1' failed.",
+ messageId: "SVC4512"
+ }
+#---------SVC4513------------------------------
+ RESOURCE_CATEGORY_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource category cannot be changed once the resource is certified.",
+ messageId: "SVC4513"
+ }
+#---------SVC4514------------------------------
+ RESOURCE_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Resource name cannot be changed once the resource is certified.",
+ messageId: "SVC4514"
+ }
+#---------SVC4515------------------------------
+ RESOURCE_ICON_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Icon cannot be changed once the resource is certified.",
+ messageId: "SVC4515"
+ }
+#---------SVC4516------------------------------
+ RESOURCE_VENDOR_NAME_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Vendor name cannot be changed once the resource is certified.",
+ messageId: "SVC4516"
+ }
+#---------SVC4517------------------------------
+ RESOURCE_DERIVED_FROM_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: Derived from resource template cannot be changed once the resource is certified.",
+ messageId: "SVC4517"
+ }
+#---------SVC4518------------------------------
+# %1 - max length
+ COMPONENT_SINGLE_TAG_EXCEED_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Single tag exceeds limit of %1 characters.",
+ messageId: "SVC4518"
+ }
+#---------SVC4519------------------------------
+ INVALID_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: mismatch in data-type occurred for property %1. data type is %2 and default value found is %3.",
+ messageId: "SVC4519"
+ }
+#---------SVC4520------------------------------
+# %1 - service or resource
+ ADDITIONAL_INFORMATION_MAX_NUMBER_REACHED: {
+ code: 409,
+ message: "Error: Maximal number of additional %1 parameters was reached.",
+ messageId: "SVC4520"
+ }
+#---------SVC4521------------------------------
+ ADDITIONAL_INFORMATION_EMPTY_STRING_NOT_ALLOWED: {
+ code: 400,
+ message: "Error: Invalid Content. The Additional information label and value cannot be empty.",
+ messageId: "SVC4521"
+ }
+#---------SVC4522------------------------------
+# %1 - label/value
+# %2 - Maximal length of %1
+ ADDITIONAL_INFORMATION_EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. Additional information %1 exceeds limit of %2 characters.",
+ messageId: "SVC4522"
+ }
+#---------SVC4523------------------------------
+ ADDITIONAL_INFORMATION_KEY_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information label is not allowed to contain characters like <>:"\/|?* and space characters other than regular space.',
+ messageId: "SVC4523"
+ }
+#---------SVC4524------------------------------
+ ADDITIONAL_INFORMATION_NOT_FOUND: {
+ code: 409,
+ message: "Error: Requested additional information was not found.",
+ messageId: "SVC4524"
+ }
+#---------SVC4525------------------------------
+ ADDITIONAL_INFORMATION_VALUE_NOT_ALLOWED_CHARACTERS: {
+ code: 400,
+ message: 'Error: Invalid Content. Additional information contains non-english characters.',
+ messageId: "SVC4525"
+ }
+#---------SVC4526------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found.",
+ messageId: "SVC4526"
+ }
+#---------SVC4527------------------------------
+ ASDC_VERSION_NOT_FOUND: {
+ code: 500,
+ message: 'Error: ASDC version cannot be displayed.',
+ messageId: "SVC4527"
+ }
+#---------SVC4528------------------------------
+# %1-artifact url/artifact label/artifact description/VNF Service Indicator
+ MISSING_DATA: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1.",
+ messageId: "SVC4528"
+ }
+#---------SVC4529------------------------------
+# %1-artifact url/artifact label/artifact description/artifact name
+# %2 - Maximal length of %1
+ EXCEEDS_LIMIT: {
+ code: 400,
+ message: "Error: Invalid Content. %1 exceeds limit of %2 characters.",
+ messageId: "SVC4529"
+ }
+#---------SVC4530------------------------------
+ ARTIFACT_INVALID_TIMEOUT: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact Timeout should be set to valid positive non-zero number of minutes.",
+ messageId: "SVC4530"
+ }
+#---------SVC4531------------------------------
+ SERVICE_IS_VNF_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: VNF Indicator cannot be updated for certified service.",
+ messageId: "SVC4531"
+ }
+ #---------SVC4532------------------------------
+ RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE: {
+ code: 404,
+ message: "Error: Requested '%1' resource instance was not found on the service '%2.",
+ messageId: "SVC4532"
+ }
+ #---------SVC4533------------------------------
+ # %1 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"/"YANG_XML"
+ WRONG_ARTIFACT_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for %1 artifact type.",
+ messageId: "SVC4533"
+ }
+
+#---------SVC4534------------------------------
+# %1 - "HEAT"/"HEAT_ENV"
+ INVALID_YAML: {
+ code: 400,
+ message: "Error: Uploaded YAML file for %1 artifact is invalid.",
+ messageId: "SVC4534"
+ }
+
+#---------SVC4535------------------------------
+# %1 - "HEAT"
+ INVALID_DEPLOYMENT_ARTIFACT_HEAT: {
+ code: 400,
+ message: "Error: Invalid %1 artifact.",
+ messageId: "SVC4535"
+ }
+#---------SVC4536------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "HEAT"/"HEAT_ENV"/"MURANO_PKG"
+# %4 - "HEAT"/"HEAT_ENV"/"MURANO_PKG
+ DEPLOYMENT_ARTIFACT_OF_TYPE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact of %3 type .Please delete or update an existing %4 artifact.",
+ messageId: "SVC4536"
+ }
+
+#---------SVC4537------------------------------
+ MISSING_HEAT: {
+ code: 400,
+ message: "Error: Missing HEAT artifact. HEAT_ENV artifact cannot be uploaded without corresponding HEAT template.",
+ messageId: "SVC4537"
+ }
+#---------SVC4538------------------------------
+ MISMATCH_HEAT_VS_HEAT_ENV: {
+ code: 400,
+ message: "Error: Invalid artifact content. Parameter's set in HEAT_ENV '%1' artifact doesn't match the parameters in HEAT '%2' artifact.",
+ messageId: "SVC4538"
+ }
+#---------SVC4539------------------------------
+ INVALID_RESOURCE_PAYLOAD: {
+ code: 400,
+ message: "Error: Invalid resource payload.",
+ messageId: "SVC4539"
+ }
+#---------SVC4540------------------------------
+ INVALID_TOSCA_FILE_EXTENSION: {
+ code: 400,
+ message: "Error: Invalid file extension for TOSCA template.",
+ messageId: "SVC4540"
+ }
+#---------SVC4541------------------------------
+ INVALID_YAML_FILE: {
+ code: 400,
+ message: "Error: Invalid YAML file.",
+ messageId: "SVC4541"
+ }
+#---------SVC4542------------------------------
+ INVALID_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Invalid TOSCA template.",
+ messageId: "SVC4542"
+ }
+#---------SVC4543------------------------------
+ NOT_RESOURCE_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: Imported Service TOSCA template.",
+ messageId: "SVC4543"
+ }
+#---------SVC4544------------------------------
+ NOT_SINGLE_RESOURCE: {
+ code: 400,
+ message: "Error: Imported TOSCA template should contain one resource definition.",
+ messageId: "SVC4544"
+ }
+#---------SVC4545------------------------------
+ INVALID_RESOURCE_NAMESPACE: {
+ code: 400,
+ message: "Error: Invalid resource namespace.",
+ messageId: "SVC4545"
+ }
+#---------SVC4546------------------------------
+ RESOURCE_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: Imported resource already exists in ASDC Catalog.",
+ messageId: "SVC4546"
+ }
+#---------SVC4549------------------------------
+ INVALID_RESOURCE_CHECKSUM: {
+ code: 400,
+ message: "Error: Invalid resource checksum.",
+ messageId: "SVC4549"
+ }
+#---------SVC4550------------------------------
+ #%1  -  Consumer salt
+ INVALID_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 length.",
+ messageId: "SVC4550"
+ }
+ #---------SVC4551------------------------------
+ #%1  -  ECOMP User name
+ ECOMP_USER_NOT_FOUND: {
+ code: 404,
+ message: "Error: ECOMP User '%1' was not found.",
+ messageId: "SVC4551"
+ }
+#---------SVC4552------------------------------
+ CONSUMER_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: ECOMP User already exists.",
+ messageId: "SVC4552"
+ }
+#---------SVC4553-----------------------------
+ #%1  -  Consumer name / Consumer password/ Consumer salt
+ INVALID_CONTENT_PARAM: {
+ code: 400,
+ message: "Error: %1 is invalid.",
+ messageId: "SVC4553"
+ }
+ #---------SVC4554------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_ARTIFACT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested artifact doesn't belong to specified %1.",
+ messageId: "SVC4554"
+ }
+#---------SVC4554------------------------------
+# %1 - "Service name"
+ SERVICE_DEPLOYMENT_ARTIFACT_NOT_FOUND: {
+ code: 403,
+ message: "Error: Requested '%1' service is not ready for certification. Service has to have at least one deployment artifact.",
+ messageId: "SVC4554"
+ }
+#---------SVC4555------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_LENGTH: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name length.",
+ messageId: "SVC4555"
+ }
+#---------SVC4556------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category"
+ COMPONENT_ELEMENT_INVALID_NAME_FORMAT: {
+ code: 400,
+ message: "Error: Invalid %1 %2 name format.",
+ messageId: "SVC4556"
+ }
+#---------SVC4557------------------------------
+#%1 - "Resource"/"Service"/"Product"
+#%2 - "category name"
+ COMPONENT_CATEGORY_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: %1 category name '%2' already exists.",
+ messageId: "SVC4557"
+ }
+#---------SVC4558------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ VALIDATED_RESOURCE_NOT_FOUND: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource.",
+ messageId: "SVC4558"
+ }
+#---------SVC4559------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_ALREADY_VALIDATED_RESOURCE: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use already available validated resource version.",
+ messageId: "SVC4559"
+ }
+#---------SVC4560------------------------------
+# %1 - "service"/"VF"
+# %2 - "Resource name"
+ FOUND_LIST_VALIDATED_RESOURCES: {
+ code: 403,
+ message: "Error: Submit for Testing is not permitted as your '%1' includes non-validated '%2' resource. Please use one of available validated resource versions.",
+ messageId: "SVC4560"
+ }
+#---------SVC4561------------------------------
+# %1 - "resource"/"product"
+# %2 - "category"
+# %3 - "category name"
+ COMPONENT_CATEGORY_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested %1 %2 '%3' was not found.",
+ messageId: "SVC4561"
+ }
+#---------SVC4562------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %3 - "category name"
+ COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY: {
+ code: 409,
+ message: "Error: %1 sub-category '%2' already exists under '%3' category.",
+ messageId: "SVC4562"
+ }
+#---------SVC4563------------------------------
+# %1 - "Product"
+# %2 - "grouping name"
+# %3 - "sub-category name"
+ COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY: {
+ code: 409,
+ message: "Error: %1 grouping '%2' already exists under '%3' sub-category.",
+ messageId: "SVC4563"
+ }
+#---------SVC4564------------------------------
+# %1 - product name
+ PRODUCT_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' product was not found.",
+ messageId: "SVC4564"
+ }
+#---------SVC4565------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+# %3 - parameter name
+ INVALID_HEAT_PARAMETER_VALUE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Invalid %2 value set for '%3' parameter.",
+ messageId: "SVC4565"
+ }
+#---------SVC4566------------------------------
+# %1 - "HEAT"
+# %2 - parameter type ("string" , "boolean" , "number")
+ INVALID_HEAT_PARAMETER_TYPE: {
+ code: 400,
+ message: "Error: Invalid %1 artifact. Unsupported '%2' parameter type.",
+ messageId: "SVC4566"
+ }
+#---------SVC4567------------------------------
+# %1 - "YANG_XML"
+ INVALID_XML: {
+ code: 400,
+ message: "Error: Uploaded XML file for %1 artifact is invalid.",
+ messageId: "SVC4567"
+ }
+#---------SVC4567------------------------------
+# %1 - "User Name and userId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_DELETE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: User cannot be deleted. User '%1' has %2 projects.",
+ messageId: "SVC4567"
+ }
+#---------SVC4568------------------------------
+# %1 - "User Name and userId"
+# %2 -"checked-out"/"in-certification"
+ CANNOT_UPDATE_USER_WITH_ACTIVE_ELEMENTS: {
+ code: 409,
+ message: "Error: Role cannot be changed. User '%1' has %2 projects.",
+ messageId: "SVC4568"
+ }
+#---------SVC4570------------------------------
+ UPDATE_USER_ADMIN_CONFLICT: {
+ code: 409,
+ message: "Error: An administrator is not allowed to change his/her role.",
+ messageId: "SVC4570"
+ }
+#---------SVC4571------------------------------
+ SERVICE_CANNOT_CONTAIN_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Sub category cannot be defined for service",
+ messageId: "SVC4571"
+ }
+#---------SVC4572------------------------------
+# %1 - "Resource"/"Service"
+ COMPONENT_TOO_MUCH_CATEGORIES: {
+ code: 400,
+ message: "Error: %1 must have only 1 category",
+ messageId: "SVC4572"
+ }
+#---------SVC4574------------------------------
+ RESOURCE_TOO_MUCH_SUBCATEGORIES: {
+ code: 400,
+ message: "Error: Resource must have only 1 sub category",
+ messageId: "SVC4574"
+ }
+#---------SVC4575------------------------------
+ COMPONENT_MISSING_SUBCATEGORY: {
+ code: 400,
+ message: "Error: Missing sub category",
+ messageId: "SVC4575"
+ }
+ #---------SVC4576------------------------------
+# %1 - "component type"
+ UNSUPPORTED_ERROR: {
+ code: 400,
+ message: "Error : Requested component type %1 is unsupported.",
+ messageId: "SVC4576"
+ }
+ #---------SVC4577------------------------------
+# %1 - "resource type"
+ RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES: {
+ code: 409,
+ message: "Error : Resource of type %1 cannot contain resource instances.",
+ messageId: "SVC4577"
+ }
+#---------SVC4578------------------------------
+# %1 - "Resource"/"Service"
+# %2 - resource/service name
+# %3 - "artifact name"
+ DEPLOYMENT_ARTIFACT_NAME_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: %1 '%2' already has a deployment artifact named '%3'.",
+ messageId: "SVC4578"
+ }
+#---------SVC4579------------------------------
+# %1 - "Category"/"Sub-Category"/"Group"
+# %2 - category/sub-category/grouping name.
+ INVALID_GROUP_ASSOCIATION: {
+ code: 400,
+ message: "Error: Invalid group association. %1 '%2' was not found.",
+ messageId: "SVC4579"
+ }
+#---------SVC4580------------------------------
+ EMPTY_PRODUCT_CONTACTS_LIST: {
+ code: 400,
+ message: "Error: Invalid content. At least one Product Contact has to be specified.",
+ messageId: "SVC4580"
+ }
+#---------SVC4581------------------------------
+# %1 - userId
+ INVALID_PRODUCT_CONTACT: {
+ code: 400,
+ message: "Error: Invalid content. User '%1' cannot be set as Product Contact.",
+ messageId: "SVC4581"
+ }
+#---------SVC4582------------------------------
+# %1 - Product
+# %2 - "abbreviated"/"full"
+ MISSING_ONE_OF_COMPONENT_NAMES: {
+ code: 400,
+ message: "Error: Invalid content. Missing %1 %2 name.",
+ messageId: "SVC4582"
+ }
+#---------SVC4583------------------------------
+# %1 - "Icon"
+# %2 - "resource"/"service"/"product"
+ COMPONENT_PARAMETER_CANNOT_BE_CHANGED: {
+ code: 400,
+ message: "Error: %1 cannot be changed once the %2 is certified.",
+ messageId: "SVC4583"
+ }
+#---------SVC4584------------------------------
+# %1 - service/VF name
+# %2 - "service" /"VF"
+# %3 - resource instance origin type
+# %4 - resource instance name
+# %5 - requirement/capability
+# %6 - requirement/capability name
+# %7 - "fulfilled" (for req)/"consumed (for cap)"
+ REQ_CAP_NOT_SATISFIED_BEFORE_CERTIFICATION: {
+ code: 403,
+ message: "Error: Requested '%1' %2 is not ready for certification. %3 '%4' has to have %5 '%6' %7.",
+ messageId: "SVC4584"
+ }
+#---------SVC4585------------------------------
+ INVALID_OCCURRENCES: {
+ code: 400,
+ message: "Error: Invalid occurrences format.",
+ messageId: "SVC4585"
+ }
+#---------SVC4586------------------------------
+#---------SVC4586------------------------------
+ INVALID_SERVICE_API_URL: {
+ code: 400,
+ message: 'Error: Invalid Service API URL. Please check whether your URL has a valid domain extension and does not contain the following characters - #?&@%+;,=$<>~^`\[]{}|"*!',
+ messageId: "SVC4586"
+ }
+#---------SVC4587------------------------------
+# %1 - Data type name
+ DATA_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Data type %1 already exists.',
+ messageId: "SVC4587"
+ }
+#---------SVC4588------------------------------
+# %1 - Data type name
+ DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM: {
+ code: 400,
+ message: 'Error: Invalid Data type %1. Data type must have either a valid derived from declaration or at least one valid property',
+ messageId: "SVC4588"
+ }
+#---------SVC4589------------------------------
+# %1 - Data type name
+ DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Data type %1. 'properties' parameter cannot be empty if provided.",
+ messageId: "SVC4589"
+ }
+#---------SVC4590------------------------------
+# %1 - Property type name
+# %2 - Property name
+ INVALID_PROPERTY_TYPE: {
+ code: 400,
+ message: "Error: Invalid Property type %1 in property %2.",
+ messageId: "SVC4590"
+ }
+#---------SVC4591------------------------------
+# %1 - Property inner type
+# %2 - Property name
+ INVALID_PROPERTY_INNER_TYPE: {
+ code: 400,
+ message: "Error: Invalid property inner type %1, in property %2",
+ messageId: "SVC4591"
+ }
+#---------SVC4592------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+ COMPONENT_INSTANCE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found.",
+ messageId: "SVC4592"
+ }
+#---------SVC4593------------------------------
+# %1 - component instance name
+# %2 - "resource instance"/"service instance"
+# %3 - "resource/"service"/"product"
+# %4 - container name
+ COMPONENT_INSTANCE_NOT_FOUND_ON_CONTAINER: {
+ code: 404,
+ message: "Error: Requested '%1' %2 was not found on the %3 '%4'.",
+ messageId: "SVC4593"
+ }
+#---------SVC4594------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+ IMPORT_DUPLICATE_REQ_CAP_NAME: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains more than one %1 named '%2'.",
+ messageId: "SVC4594"
+ }
+#---------SVC4595------------------------------
+#%1 - requirement / capability
+#%2 - requirement name
+#%3 - parent containing the requirement
+ IMPORT_REQ_CAP_NAME_EXISTS_IN_DERIVED: {
+ code: 400,
+ message: "Error: Imported TOSCA template contains %1 '%2' that is already defined by derived template %3.",
+ messageId: "SVC4595"
+ }
+#---------SVC4596------------------------------
+# %1 - Data type name
+ DATA_TYPE_DERIVED_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The ancestor data type %1 cannot be found in the system.",
+ messageId: "SVC4596"
+ }
+#---------SVC4597------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties named %2 which are already defined in one of its ancestors.",
+ messageId: "SVC4597"
+ }
+#---------SVC4598------------------------------
+# %1 - Data type name
+ DATA_TYPE_DUPLICATE_PROPERTY: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains duplicate property.",
+ messageId: "SVC4598"
+ }
+#---------SVC4599------------------------------
+# %1 - Data type name
+# %2 - Property names
+ DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 contains properties %2 which their type is this data type.",
+ messageId: "SVC4599"
+ }
+#---------SVC4600------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_HAVE_PROPERTIES: {
+ code: 400,
+ message: "Error: Invalid Content. The data type %1 cannot have properties since it is of type scalar",
+ messageId: "SVC4600"
+ }
+#---------SVC4601------------------------------
+ NOT_TOPOLOGY_TOSCA_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file %1 cannot be modeled to VF as it does not contain 'topology_template.",
+ messageId: "SVC4601"
+ }
+#---------SVC4602--------------------------------
+# %1 - yaml file name
+# %2 - node_template label
+# %3 - node_template type
+ INVALID_NODE_TEMPLATE: {
+ code: 400,
+ message: "Error: TOSCA yaml file '%1' contains node_template '%2' of type '%3' that does not represent existing VFC/CP/VL",
+ messageId: "SVC4602"
+ }
+#---------SVC4603------------------------------
+# %1 - component type
+# %2 - component name
+# %3 - state
+ ILLEGAL_COMPONENT_STATE: {
+ code: 403,
+ message: "Error: Component instance of %1 can not be created because the component '%2' is in an illegal state %3.",
+ messageId: "SVC4603"
+ }
+#---------SVC4604------------------------------
+# %1 - csar file name
+ CSAR_INVALID: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. 'TOSCA-Metadata/Tosca.meta' file must be provided.",
+ messageId: "SVC4604"
+ }
+#---------SVC4605------------------------------
+# %1 - csar file name
+ CSAR_INVALID_FORMAT: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is invalid. Invalid 'TOSCA-Metadata/Tosca.meta' file format.",
+ messageId: "SVC4605"
+ }
+#---------SVC4606------------------------------
+# %1 - property name
+# %2 - property type
+# %3 - property innerType
+# %4 - default value is
+ INVALID_COMPLEX_DEFAULT_VALUE: {
+ code: 400,
+ message: "Error: Invalid default value of property %1. Data type is %2 with inner type %3 and default value found is %4.",
+ messageId: "SVC4606"
+ }
+#---------SVC4607------------------------------
+# %1 - csar file name
+ CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: TOSCA CSAR '%1' is not found.",
+ messageId: "SVC4607"
+ }
+#---------SVC4608------------------------------
+# %1 - artifact name
+# %2 - component type
+# %3 - actual component type
+ MISMATCH_BETWEEN_ARTIFACT_TYPE_AND_COMPONENT_TYPE: {
+ code: 400,
+ message: "Error: Artifact %1 is only compatible with component of type %2, but component type is %3.",
+ messageId: "SVC4608"
+ }
+
+#---------SVC4609------------------------------
+# %1 - "INVALID_JSON"
+ INVALID_JSON: {
+ code: 400,
+ message: "Error: Uploaded JSON file for %1 artifact is invalid.",
+ messageId: "SVC4609"
+ }
+#---------SVC4610------------------------------
+# %1 - csar file name
+# %2 - missing file name
+ YAML_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error - TOSCA CSAR %1 is invalid. TOSCA-Metadata/Tosca.meta refers to file %2 that is not provided.",
+ messageId: "SVC4610"
+ }
+#---------SVC4611------------------------------
+# %1 - group name
+ GROUP_MEMBER_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Group %1 member list was provided but does not have values",
+ messageId: "SVC4611"
+ }
+#---------SVC4612------------------------------
+# %1 - group name
+ GROUP_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: 'Error: Group type %1 already exists.',
+ messageId: "SVC4612"
+ }
+#---------SVC4613------------------------------
+# %1 - group name
+# %2 - VF name(component name)
+# %3 - actual component type [VF]
+ GROUP_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Group with name '%1' already exists in %2 %3.",
+ messageId: "SVC4613"
+ }
+#---------SVC4614------------------------------
+# %1 - group type
+ GROUP_TYPE_IS_INVALID: {
+ code: 400,
+ message: "Error: Invalid content. Group type %1 does not exist",
+ messageId: "SVC4614"
+ }
+#---------SVC4615------------------------------
+# %1 - group name
+ GROUP_MISSING_GROUP_TYPE: {
+ code: 400,
+ message: "Error: Invalid Content. Missing Group Type for group '%1'",
+ messageId: "SVC4615"
+ }
+#---------SVC4616------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - VF name
+# %4 - component type [VF ]
+ GROUP_INVALID_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: Member '%1' listed in group '%2' is not part of '%3' %4.",
+ messageId: "SVC4616"
+ }
+#---------SVC4617------------------------------
+# %1 - member name
+# %2 - group name
+# %3 - group type
+ GROUP_INVALID_TOSCA_NAME_OF_COMPONENT_INSTANCE: {
+ code: 400,
+ message: "Error: member %1 listed in group %2 is not part of allowed members of group type %3.",
+ messageId: "SVC4617"
+ }
+#---------SVC4618------------------------------
+# %1 - missing file name
+# %2 - csar file name
+ ARTIFACT_NOT_FOUND_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 is defined in CSAR %2 manifest but is not provided",
+ messageId: "SVC4618"
+ }
+#---------SVC4619------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - existing artifact type
+ ARTIFACT_ALRADY_EXIST_IN_DIFFERENT_TYPE_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 already exists in type %3.",
+ messageId: "SVC4619"
+ }
+#---------SVC4620------------------------------
+ FAILED_RETRIVE_ARTIFACTS_TYPES: {
+ code: 400,
+ message: "Error: Failed to retrieve list of suported artifact types.",
+ messageId: "SVC4620"
+ }
+#---------SVC4621------------------------------
+# %1 - artifact name
+# %2 - master
+ ARTIFACT_ALRADY_EXIST_IN_MASTER_IN_CSAR: {
+ code: 400,
+ message: "Error: artifact %1 already exists in master %2 .",
+ messageId: "SVC4621"
+ }
+#---------SVC4622------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - master name
+# %4 - master type
+ ARTIFACT_NOT_VALID_IN_MASTER: {
+ code: 400,
+ message: "Error: artifact %1 in type %2 can not be exists under master %3 in type %4.",
+ messageId: "SVC4622"
+ }
+#---------SVC4623------------------------------
+# %1 - artifact name
+# %2 - artifact type
+# %3 - env name
+# %4 - existing env
+ ARTIFACT_NOT_VALID_ENV: {
+ code: 400,
+ message: "Error: Artifact %1 in type %2 with env %3 already exists with another env %4",
+ messageId: "SVC4623"
+ }
+#---------SVC4624------------------------------
+# %1 - groups names
+# %2 - VF name
+# %3 - component type [VF ]
+ GROUP_IS_MISSING: {
+ code: 400,
+ message: "Error: Invalid Content. The groups '%1' cannot be found under %2 %3.",
+ messageId: "SVC4624"
+ }
+#---------SVC4625------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_ASSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already associated to group '%1'.",
+ messageId: "SVC4625"
+ }
+#---------SVC4626------------------------------
+# %1 - groups name
+ GROUP_ARTIFACT_ALREADY_DISSOCIATED: {
+ code: 400,
+ message: "Error: Invalid Content. Artifact already dissociated from group '%1'.",
+ messageId: "SVC4626"
+ }
+#---------SVC4627------------------------------
+# %1 - property name
+# %2 - group name
+# %3 - group type name
+ GROUP_PROPERTY_NOT_FOUND: {
+ code: 400,
+ message: "Error: property %1 listed in group %2 is not exist in group type %3.",
+ messageId: "SVC4627"
+ }
+#---------SVC4628------------------------------
+# %1 - csarUUID
+# %2 - VF name
+ VSP_ALREADY_EXISTS: {
+ code: 400,
+ message: "Error: The VSP with UUID %1 was already imported for VF %2. Please select another or update the existing VF.",
+ messageId: "SVC4628"
+ }
+#---------SVC4629------------------------------
+# %1 - VF name
+ MISSING_CSAR_UUID: {
+ code: 400,
+ message: "Error: The Csar UUID or payload name is missing for VF %1.",
+ messageId: "SVC4629"
+ }
+#---------SVC4630------------------------------
+# %1 - VF name
+# %2 - new csarUUID
+# %3 - old csarUUID
+ RESOURCE_LINKED_TO_DIFFERENT_VSP: {
+ code: 400,
+ message: "Error: Resource %1 cannot be updated using CsarUUID %2 since the resource is linked to a different VSP with csarUUID %3.",
+ messageId: "SVC4630"
+ }
+#---------SVC4631------------------------------
+# %1 - policy name
+ POLICY_TYPE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Policy type %1 already exists.",
+ messageId: "SVC4631"
+ }
+#---------SVC4632------------------------------
+# %1 - target name
+# %2 - policy type name
+ TARGETS_NON_VALID: {
+ code: 400,
+ message: "Error: target %1 listed in policy type %2 is not a group or resource.",
+ messageId: "SVC4632"
+ }
+#---------SVC4633------------------------------
+# %1 - policy name
+ TARGETS_EMPTY: {
+ code: 400,
+ message: "Error: Invalid Content. Policy %1 target list was provided but does not have values",
+ messageId: "SVC4633"
+ }
+#---------SVC4634------------------------------
+ DATA_TYPE_CANNOT_BE_EMPTY: {
+ code: 500,
+ message: "Error: Data types are empty. Please import the data types.",
+ messageId: "SVC4634"
+ }
+#---------SVC4635------------------------------
+# %1 - csar uuid
+ RESOURCE_FROM_CSAR_NOT_FOUND: {
+ code: 400,
+ message: "Error: resource from csar uuid %1 not found",
+ messageId: "SVC4635"
+ }
+#---------SVC4636------------------------------
+# %1 - Data type name
+ DATA_TYPE_CANNOT_BE_UPDATED_BAD_REQUEST: {
+ code: 400,
+ message: 'Error: Data type %1 cannot be upgraded. The new data type does not contain old properties or the type of one of the properties has been changed.',
+ messageId: "SVC4636"
+ }
+#-----------SVC4637---------------------------
+#%1 - attribute name
+ ATTRIBUTE_NOT_FOUND: {
+ code: 404,
+ message: "Error: Requested '%1' attribute was not found.",
+ messageId: "SVC4637"
+ }
+#-----------SVC4638---------------------------
+#%1 - attribute name
+ ATTRIBUTE_ALREADY_EXIST: {
+ code: 409,
+ message: "Error: Attribute with '%1' name already exists.",
+ messageId: "SVC4638"
+ }
+#-----------SVC4639---------------------------
+#%1 - property name
+ PROPERTY_NAME_ALREADY_EXISTS: {
+ code: 409,
+ message: "Error: Property with '%1' name and different type already exists.",
+ messageId: "SVC4639"
+ }
+#-----------SVC4640---------------------------
+#%1 - property name
+ INVALID_PROPERTY: {
+ code: 409,
+ message: "Error: Invalid property received.",
+ messageId: "SVC4640"
+ }
+#---------SVC4641-----------------------------
+#%1 - invalid filter
+#%2 - valid filters
+ INVALID_FILTER_KEY: {
+ code: 400,
+ message: "Error: The filter %1 is not applicable. Please use one of the following filters: %2",
+ messageId: "SVC4641"
+ }
+#---------SVC4642-----------------------------
+#%1 - asset type
+#%2 - filter
+ NO_ASSETS_FOUND: {
+ code: 404,
+ message: "No %1 were found to match criteria %2",
+ messageId: "SVC4642"
+ }
+#---------SVC4643------------------------------
+# %1 - "Resource"/"Product"
+# %2 - "sub-category name"
+# %3 - "category name"
+ COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY: {
+ code: 404,
+ message: "Error: %1 sub-category '%2' not found under category '%3'.",
+ messageId: "SVC4643"
+ }
+#---------SVC4644------------------------------
+# %1 - Format
+ CORRUPTED_FORMAT: {
+ code: 400,
+ message: "Error: %1 format is corrupted.",
+ messageId: "SVC4644"
+ }
+#---------SVC4645------------------------------
+# %1 - "groupType"
+ INVALID_VF_MODULE_TYPE: {
+ code: 400,
+ message: "Error: Invalid group type '%1' (should be VfModule).",
+ messageId: "SVC4645"
+ }
+#---------SVC4646------------------------------
+# %1 - "groupName"
+ INVALID_VF_MODULE_NAME: {
+ code: 400,
+ message: "Error: Invalid Content. VF Module name '%1' contains invalid characters",
+ messageId: "SVC4646"
+ }
+
+#---------SVC4647------------------------------
+# %1 - "modifiedName"
+ INVALID_VF_MODULE_NAME_MODIFICATION: {
+ code: 400,
+ message: "Error: Invalid VF Module name modification, can not modify '%1'",
+ messageId: "SVC4647"
+ }
+#---------SVC4648------------------------------
+# %1 - "inputId"
+# %2 - "componentId"
+ INPUT_IS_NOT_CHILD_OF_COMPONENT: {
+ code: 400,
+ message: "Error: Input id: '%1' is not child of component id: '%2'",
+ messageId: "SVC4648"
+ }
+#---------SVC4649------------------------------
+# %1 - "groupName"
+ GROUP_HAS_CYCLIC_DEPENDENCY: {
+ code: 400,
+ message: "Error: The group '%1' has cyclic dependency",
+ messageId: "SVC4649"
+ }
+#---------SVC4650------------------------------
+# %1 - "Component Type"
+# %2 - <ServiceName>
+# %3 - error description
+ AAI_ARTIFACT_GENERATION_FAILED: {
+ code: 500,
+ message: "Error: %1 %2 automatic generation of artifacts failed. Description: %3",
+ messageId: "SVC4650"
+ }
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-logback.xml b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-logback.xml
new file mode 100644
index 0000000000..5add40be51
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/BE-logback.xml
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="5 seconds">
+
+ <property scope="system" name="ECOMP-component-name" value="ASDC" />
+ <property scope="system" name="ECOMP-subcomponent-name" value="ASDC-BE" />
+ <property file="${config.home}/catalog-be/configuration.yaml" />
+ <property scope="context" name="enable-all-log" value="false" />
+
+ <!-- value used by pattern field list (| - is inter-field separator, || - unavailable or not applicable field value) (m - mandatory, o- optional)-->
+ <!--timestamp(m)| requestID(m)| serviceInstanceID(o)| threadID(m)| physicalServerName(o)| serviceName(m)| userID(m)| logLevel(m)| severity(o)| serverIpAddress(m)| serverName(m)| clientIpAddress(o)| className(m)| timer(o)| detailedMessage(o)-->
+ <property name="default-log-pattern"
+ value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{uuid}|%X{serviceInstanceID}|%thread||${ECOMP-subcomponent-name}|%X{userId}|%level|%X{alarmSeverity}|%X{localAddr}|${beFqdn}|%X{remoteAddr}|%logger{35}|%X{timer}|ActivityType=&lt;%M&gt;, Desc=&lt;%msg&gt;%n" />
+
+ <!-- All log -->
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender name="ALL_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log
+ </file>
+
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="ASYNC_ALL" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ALL_ROLLING" />
+ </appender>
+ </then>
+ </if>
+
+ <!-- Error log -->
+ <appender name="ERROR_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log
+ </file>
+
+ <!-- Audit messages filter - deny audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>AUDIT_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- Transaction messages filter - deny Transaction messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Debug log -->
+ <appender name="DEBUG_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log
+ </file>
+
+ <!-- No need to deny audit messages - they are INFO only, will be denied
+ anyway -->
+ <!-- Transaction messages filter - deny Transaction messages, there are
+ some DEBUG level messages among them -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- accept DEBUG and TRACE level -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
+ <expression>
+ e.level.toInt() &lt;= DEBUG.toInt()
+ </expression>
+ </evaluator>
+ <OnMismatch>DENY</OnMismatch>
+ <OnMatch>NEUTRAL</OnMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Audit log -->
+ <appender name="AUDIT_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/audit.log
+ </file>
+
+ <!-- Audit messages filter - accept audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>AUDIT_MARKER</marker>
+ </evaluator>
+ <onMismatch>DENY</onMismatch>
+ <onMatch>ACCEPT</onMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/audit.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- SdncTransaction log -->
+ <appender name="TRANSACTION_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/transaction.log
+ </file>
+
+ <!-- Transaction messages filter - accept audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>DENY</onMismatch>
+ <onMatch>ACCEPT</onMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/transaction.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Asynchronicity Configurations -->
+ <appender name="ASYNC_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="DEBUG_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_TRANSACTION" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="TRANSACTION_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ERROR_ROLLING" />
+ </appender>
+
+
+ <root level="INFO">
+ <appender-ref ref="ASYNC_ERROR" />
+ <appender-ref ref="ASYNC_DEBUG" />
+ <appender-ref ref="AUDIT_ROLLING" />
+ <appender-ref ref="ASYNC_TRANSACTION" />
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender-ref ref="ALL_ROLLING" />
+ </then>
+ </if>
+ </root>
+
+ <logger name="org.openecomp.sdc" level="INFO" />
+</configuration>
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/consumers.py b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/consumers.py
new file mode 100644
index 0000000000..c7d6fe729a
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/consumers.py
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+import subprocess
+#from time import sleep
+import time
+from datetime import datetime
+
+class bcolors:
+ HEADER = '\033[95m'
+ OKBLUE = '\033[94m'
+ OKGREEN = '\033[92m'
+ WARNING = '\033[93m'
+ FAIL = '\033[91m'
+ ENDC = '\033[0m'
+ BOLD = '\033[1m'
+ UNDERLINE = '\033[4m'
+
+
+##############################
+# Functions
+##############################
+def checkBackend():
+ command="curl -s -o /dev/null -I -w \"%{http_code}\" -i http://localhost:8080/sdc2/rest/v1/user/jh0003"
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE )
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+def checkConsumer(consumerName):
+ command="curl -s -o /dev/null -I -w \"%{http_code}\" -i -H \"Accept: application/json; charset=UTF-8\" -H \"Content-Type: application/json\" -H \"USER_ID: jh0003\" http://localhost:8080/sdc2/rest/v1/consumers/" + consumerName
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE )
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+def createConsumer( consumerName, consumerSalt, consumerPass ):
+ print '[INFO] ' + consumerName
+ command="curl -s -o /dev/null -w \"%{http_code}\" -X POST -i -H \"Accept: application/json; charset=UTF-8\" -H \"Content-Type: application/json\" -H \"USER_ID: jh0003\" http://localhost:8080/sdc2/rest/v1/consumers/ -d '{\"consumerName\": '" + consumerName + "', \"consumerSalt\": '" + consumerSalt + "',\"consumerPassword\": '" + consumerPass + "'}'"
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE)
+
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+
+
+##############################
+# Definitions
+##############################
+consumersList = [ "aai" , "appc" , "dcae" , "mso" , "sdnc" , "vid" ]
+salt = "9cd4c3ad2a6f6ce3f3414e68b5157e63"
+password = "35371c046f88c603ccba152cb3db34ec4475cb2e5713f2fc0a43bf18a5243495"
+beStat=0
+
+
+##############################
+# Main
+##############################
+
+for i in range(1,10):
+ myResult = checkBackend()
+ if myResult == '200':
+ print '[INFO]: Backend is up and running'
+ beStat=1
+ break
+ else:
+ currentTime = datetime.now()
+ print '[ERROR]: ' + currentTime.strftime('%Y/%m/%d %H:%M:%S') + bcolors.FAIL + ' Backend not responding, try #' + str(i) + bcolors.ENDC
+ time.sleep(10)
+
+if beStat == 0:
+ print '[ERROR]: ' + time.strftime('%Y/%m/%d %H:%M:%S') + bcolors.FAIL + 'Backend is DOWN :-(' + bcolors.ENDC
+ exit()
+
+for consumer in consumersList:
+ myResult = checkConsumer(consumer)
+ if myResult == '200':
+ print '[INFO]: ' + consumer + ' already exists'
+ else:
+ myResult = createConsumer( consumer, salt, password )
+ if myResult == '201':
+ print '[INFO]: ' + consumer + ' created, result: [' + myResult + ']'
+ else:
+ print '[ERROR]: ' + bcolors.FAIL + consumer + bcolors.ENDC + ' error creating , result: [' + myResult + ']'
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/user.py b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/user.py
new file mode 100644
index 0000000000..d1ce9cfad8
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/files/default/user.py
@@ -0,0 +1,92 @@
+#!/usr/bin/python
+import subprocess
+#from time import sleep
+import time
+from datetime import datetime
+
+class bcolors:
+ HEADER = '\033[95m'
+ OKBLUE = '\033[94m'
+ OKGREEN = '\033[92m'
+ WARNING = '\033[93m'
+ FAIL = '\033[91m'
+ ENDC = '\033[0m'
+ BOLD = '\033[1m'
+ UNDERLINE = '\033[4m'
+
+
+##############################
+# Functions
+##############################
+def checkBackend():
+ command="curl -s -o /dev/null -I -w \"%{http_code}\" -i http://localhost:8080/sdc2/rest/v1/user/jh0003"
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE )
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+def checkUser(userName):
+ command="curl -s -o /dev/null -I -w \"%{http_code}\" -i -H \"Accept: application/json; charset=UTF-8\" -H \"Content-Type: application/json\" -H \"USER_ID: jh0003\" http://localhost:8080/sdc2/rest/v1/user/" + userName
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE )
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+
+
+def createUser( firstName, lastName, userId , email , role ):
+ print '[INFO] create first:[' + firstName + '], last:[' + lastName + '], Id:[' + userId + '], email:[' + email + '], role:[' + role +']'
+ command="curl -s -o /dev/null -w \"%{http_code}\" -X POST -i -H \"Accept: application/json; charset=UTF-8\" -H \"Content-Type: application/json\" -H \"USER_ID: jh0003\" http://localhost:8080/sdc2/rest/v1/user/ -d '{\"firstName\": '" + firstName + "', \"lastName\": '" + lastName + "',\"userId\": '" + userId + "',\"email\": '" + email + "',\"role\": '" + role + "'}'"
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE)
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+
+
+##############################
+# Definitions
+##############################
+userList = [ "demo" ]
+lastName = "demo"
+userId = "demo"
+email = "demo@openecomp.org"
+role = "ADMIN"
+beStat=0
+
+
+##############################
+# Main
+##############################
+
+for i in range(1,10):
+ myResult = checkBackend()
+ if myResult == '200':
+ print '[INFO]: Backend is up and running'
+ beStat=1
+ break
+ else:
+ currentTime = datetime.now()
+ print '[ERROR]: ' + currentTime.strftime('%Y/%m/%d %H:%M:%S') + bcolors.FAIL + ' Backend not responding, try #' + str(i) + bcolors.ENDC
+ time.sleep(10)
+
+if beStat == 0:
+ print '[ERROR]: ' + time.strftime('%Y/%m/%d %H:%M:%S') + bcolors.FAIL + 'Backend is DOWN :-(' + bcolors.ENDC
+ exit()
+
+for user in userList:
+ myResult = checkUser(user)
+ if myResult == '200':
+ print '[INFO]: ' + user + ' already exists'
+ else:
+ myResult = createUser( user, lastName, userId, email, role )
+ if myResult == '201':
+ print '[INFO]: ' + userId + ' created, result: [' + myResult + ']'
+ else:
+ print '[ERROR]: ' + bcolors.FAIL + userId + bcolors.ENDC + ' error creating , result: [' + myResult + ']'
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_1_cleanup_jettydir.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_1_cleanup_jettydir.rb
new file mode 100644
index 0000000000..35147a30fb
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_1_cleanup_jettydir.rb
@@ -0,0 +1,49 @@
+#directory "BE_tempdir_cleanup" do
+# path "/var/lib/jetty/tempdir"
+# recursive true
+# action :delete
+#end
+
+
+directory "BE_tempdir_creation" do
+ path "/var/lib/jetty/temp"
+ owner 'jetty'
+ group 'jetty'
+ mode '0755'
+ action :create
+end
+
+
+#directory "BE_webapps_cleanup" do
+# path "/var/lib/jetty/webapps"
+# recursive true
+# action :delete
+#end
+
+
+#directory "BE_webapps_creation" do
+# path "/var/lib/jetty/webapps"
+# owner 'jetty'
+# group 'jetty'
+# mode '0755'
+# action :create
+#end
+
+
+directory "BE_create_config_dir" do
+ path "/var/lib/jetty/config"
+ owner 'jetty'
+ group 'jetty'
+ mode '0755'
+ action :create
+end
+
+
+directory "BE_create_catalog-be" do
+ path "/var/lib/jetty/config/catalog-be"
+ owner 'jetty'
+ group 'jetty'
+ mode '0755'
+ action :create
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_2_locate_wars.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_2_locate_wars.rb
new file mode 100644
index 0000000000..af3dea15ef
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_2_locate_wars.rb
@@ -0,0 +1,26 @@
+jetty_base="/var/lib/jetty"
+jetty_home="/usr/local/jetty"
+
+###### create Jetty modules
+bash "create-jetty-modules" do
+cwd "#{jetty_base}"
+code <<-EOH
+ cd "#{jetty_base}"
+ java -jar "/#{jetty_home}"/start.jar --add-to-start=deploy
+ java -jar "/#{jetty_home}"/start.jar --add-to-startd=http,https,logging,setuid
+EOH
+not_if "ls /#{jetty_base}/start.d/https.ini"
+end
+
+###### copy catalog-be.war
+
+
+
+###### copy onboarding-be.war& api-docs.war
+#bash "copy-onboarding-be" do
+# code <<-EOH
+# /bin/tar -xvf /var/tmp/onboarding-be.tar -C /var/tmp/
+# mv /var/tmp/onboarding-be*.war /var/tmp//api-docs*.war #{jetty_base}/webapps
+# EOH
+#end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_3_create_DMaaP_keys.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_3_create_DMaaP_keys.rb
new file mode 100644
index 0000000000..583dfff7b2
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_3_create_DMaaP_keys.rb
@@ -0,0 +1,34 @@
+http_request 'create-DMaaP-apiKeys' do
+ action :post
+ url 'http://23.253.97.75:3904/apiKeys/create'
+ message ({:some => 'data'}.to_json)
+ headers({
+ 'Content-Type' => 'application/json'
+ })
+end
+
+
+selfEnviroment = node.chef_environment
+
+ruby_block "create-DMaaP-apiKeys" do
+ block do
+ sleep(15)
+ #tricky way to load this Chef::Mixin::ShellOut utilities
+ Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
+ curl_command = "https://es_admin:Aa123456@#{application_host}:9200/_cluster/health?pretty=true --insecure"
+ resp = Net::HTTP.get_response URI.parse(curl_command)
+ stat = JSON.parse(resp.read_body)['status']
+
+ case stat
+ when "green"
+ printf("\033[32m%s\n\033[0m", " ElasticSearch tests completed successfully.")
+ when "yellow"
+ printf("\033[33m%s\n\033[0m", " ElasticSearch tests completed successfully, with warnings")
+ when "red"
+ printf("\033[31m%s\n\033[0m", " ElasticSearch tests failed!!!")
+ end
+ end
+end
+
+curl POST -d '{"email":"Grinberg.Moti","description":"New Api Key for ASDC OS"}' http://23.253.97.75:3904/apiKeys/create
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_4_setup_configuration.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_4_setup_configuration.rb
new file mode 100644
index 0000000000..4facd4247c
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_4_setup_configuration.rb
@@ -0,0 +1,43 @@
+replication_factor=1
+
+template "titan.properties" do
+ path "/var/lib/jetty/config/catalog-be/titan.properties"
+ source "BE-titan.properties.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables({
+ :CASSANDRA_IP => node['Nodes']['CS'],
+ :CASSANDRA_PWD => node['cassandra'][:cassandra_password],
+ :CASSANDRA_USR => node['cassandra'][:cassandra_user],
+ :DC_NAME => "DC-"+node.chef_environment,
+ :rep_factor => replication_factor
+ })
+end
+
+
+template "catalog-be-config" do
+ path "/var/lib/jetty/config/catalog-be/configuration.yaml"
+ source "BE-configuration.yaml.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables({
+ :host_ip => node['HOST_IP'],
+ :catalog_port => node['BE'][:http_port],
+ :ssl_port => node['BE'][:https_port],
+ :cassandra_ip => node['Nodes']['CS'],
+ :rep_factor => 1,
+ :DC_NAME => "DC-"+node.chef_environment
+ })
+end
+
+
+template "distribution-engine-configuration" do
+ path "/var/lib/jetty/config/catalog-be/distribution-engine-configuration.yaml"
+ source "BE-distribution-engine-configuration.yaml.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_5_setup_elasticsearch.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_5_setup_elasticsearch.rb
new file mode 100644
index 0000000000..39fc5af883
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_5_setup_elasticsearch.rb
@@ -0,0 +1,15 @@
+clusterName = node['elasticsearch'][:cluster_name]+node.chef_environment
+node_name = node[:hostname]
+
+template "elasticsearch.yml-config" do
+ path "/var/lib/jetty/config/elasticsearch.yml"
+ source "BE-elasticsearch.yml.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables({
+ :cluster_name => "#{clusterName}",
+ :es_host_ip => node['Nodes']['ES']
+ })
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_6_setup_portal_properties.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_6_setup_portal_properties.rb
new file mode 100644
index 0000000000..8c57de6caf
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_6_setup_portal_properties.rb
@@ -0,0 +1,17 @@
+template "template portal.properties" do
+ path "/var/lib/jetty/resources/portal.properties"
+ source "BE-portal.properties.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables ({
+ :ecomp_rest_url => node['ECompP']['ecomp_rest_url'],
+ :ecomp_redirect_url => node['ECompP']['ecomp_redirect_url'],
+ :ueb_url_list => node['ECompP']['ueb_url_list'],
+ :inbox_name => node['ECompP']['inbox_name'],
+ :app_key => node['ECompP']['app_key'],
+ :app_secret => node['ECompP']['app_secret'],
+ :app_topic_name => node['ECompP']['app_topic_name'],
+ :decrypt_key => node['ECompP']['decryption_key']
+ })
+end
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_7_logback.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_7_logback.rb
new file mode 100644
index 0000000000..18ee216d5c
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_7_logback.rb
@@ -0,0 +1,7 @@
+cookbook_file "/var/lib/jetty/config/catalog-be/logback.xml" do
+ source "BE-logback.xml"
+ mode 0755
+ owner "jetty"
+ group "jetty"
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_8_errors_config.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_8_errors_config.rb
new file mode 100644
index 0000000000..c23112c5b5
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_8_errors_config.rb
@@ -0,0 +1,14 @@
+cookbook_file "/var/lib/jetty/config/catalog-be/ecomp-error-configuration.yaml" do
+ source "BE-ecomp-error-configuration.yaml"
+ mode 0755
+ owner "jetty"
+ group "jetty"
+end
+
+cookbook_file "/var/lib/jetty/config/catalog-be/error-configuration.yaml" do
+ source "BE-error-configuration.yaml"
+ mode 0755
+ owner "jetty"
+ group "jetty"
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_9_import_Normatives.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_9_import_Normatives.rb
new file mode 100644
index 0000000000..6e9b24133d
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/recipes/BE_9_import_Normatives.rb
@@ -0,0 +1,16 @@
+cookbook_file "/tmp/normatives.tar.gz" do
+ source "normatives.tar.gz"
+end
+
+working_directory = "/tmp"
+
+bash "import-normatives" do
+ cwd "#{working_directory}"
+ code <<-EOH
+ tar xvfz /tmp/normatives.tar.gz
+ cd scripts/import/tosca/
+ /bin/chmod +x importNormativeAll.py
+ python importNormativeAll.py
+ EOH
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-configuration.yaml.erb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-configuration.yaml.erb
new file mode 100644
index 0000000000..c12c1b4ff6
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-configuration.yaml.erb
@@ -0,0 +1,459 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+# catalog backend hostname
+beFqdn: <%= @host_ip %>
+
+# catalog backend http port
+beHttpPort: <%= @catalog_port %>
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: <%= @ssl_port %>
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: /var/lib/jetty/config/catalog-be/titan.properties
+titanInMemoryGraph: false
+titanLockTimeout: 1800
+# The interval to try and reconnect to titan DB when it is down during ASDC startup:
+titanReconnectIntervalInSeconds: 3
+
+# The read timeout towards Titan DB when health check is invoked:
+titanHealthCheckReadTimeout: 1
+
+# The interval to try and reconnect to Elasticsearch when it is down during ASDC startup:
+
+esReconnectIntervalInSeconds: 3
+uebHealthCheckReconnectIntervalInSeconds: 15
+uebHealthCheckReadTimeout: 4
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+
+cassandraConfig:
+ cassandraHosts: [<%= @cassandra_ip %>]
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: true
+ username: asdc_user
+ password: Aa1234%^!
+ ssl: false
+ truststorePath : /config/.truststore
+ truststorePassword : Aa123456
+ keySpaces:
+ - { name: dox, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @DC_NAME %>','<%= @rep_factor %>']}
+ - { name: sdcaudit, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @DC_NAME %>','<%= @rep_factor %>']}
+ - { name: sdcartifact, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @DC_NAME %>','<%= @rep_factor %>']}
+ - { name: sdccomponent, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @DC_NAME %>','<%= @rep_factor %>']}
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # PLEASE NOTE: the timestamps are created in UTC/GMT timezone! This is needed so that timestamps will be correctly presented in Kibana.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+ - indexPrefix: monitoring_events
+ creationPeriod: month
+artifactTypes:
+ - CHEF
+ - PUPPET
+ - SHELL
+ - YANG
+ - YANG_XML
+ - HEAT
+ - BPEL
+ - DG_XML
+ - MURANO_PKG
+ - WORKFLOW
+ - NETWORK_CALL_FLOW
+ - TOSCA_TEMPLATE
+ - TOSCA_CSAR
+ - AAI_SERVICE_MODEL
+ - AAI_VF_MODEL
+ - AAI_VF_MODULE_MODEL
+ - AAI_VF_INSTANCE_MODEL
+ - OTHER
+
+
+licenseTypes:
+ - User
+ - Installation
+ - CPU
+
+#Deployment artifacts placeHolder
+resourceTypes: &allResourceTypes
+ - VFC
+ - CP
+ - VL
+ - VF
+
+# validForResourceTypes usage
+# validForResourceTypes:
+# - VF
+# - VL
+deploymentResourceArtifacts:
+
+
+deploymentResourceInstanceArtifacts:
+ heatEnv:
+ displayName: "HEAT ENV"
+ type: HEAT_ENV
+ description: "Auto-generated HEAT Environment deployment artifact"
+ fileExtension: "env"
+
+#tosca artifacts placeholders
+toscaArtifacts:
+ assetToscaTemplate:
+ artifactName: -template.yml
+ displayName: Tosca Template
+ type: TOSCA_TEMPLATE
+ description: TOSCA representation of the asset
+ assetToscaCsar:
+ artifactName: -csar.csar
+ displayName: Tosca Model
+ type: TOSCA_CSAR
+ description: TOSCA definition package of the asset
+
+#Informational artifacts placeHolder
+excludeResourceCategory:
+ - Generic
+informationalResourceArtifacts:
+ features:
+ displayName: Features
+ type: OTHER
+ capacity:
+ displayName: Capacity
+ type: OTHER
+ vendorTestResult:
+ displayName: Vendor Test Result
+ type: OTHER
+ testScripts:
+ displayName: Test Scripts
+ type: OTHER
+ cloudQuestionnaire:
+ displayName: Cloud Questionnaire (completed)
+ type: OTHER
+ HEATTemplateFromVendor:
+ displayName: HEAT Template from Vendor
+ type: HEAT
+ resourceSecurityTemplate:
+ displayName: Resource Security Template
+ type: OTHER
+
+excludeServiceCategory:
+
+informationalServiceArtifacts:
+ serviceArtifactPlan:
+ displayName: Service Artifact Plan
+ type: OTHER
+ summaryOfImpactsToECOMPElements:
+ displayName: Summary of impacts to ECOMP elements,OSSs, BSSs
+ type: OTHER
+ controlLoopFunctions:
+ displayName: Control Loop Functions
+ type: OTHER
+ dimensioningInfo:
+ displayName: Dimensioning Info
+ type: OTHER
+ affinityRules:
+ displayName: Affinity Rules
+ type: OTHER
+ operationalPolicies:
+ displayName: Operational Policies
+ type: OTHER
+ serviceSpecificPolicies:
+ displayName: Service-specific Policies
+ type: OTHER
+ engineeringRules:
+ displayName: Engineering Rules (ERD)
+ type: OTHER
+ distributionInstructions:
+ displayName: Distribution Instructions
+ type: OTHER
+ certificationTestResults:
+ displayName: TD Certification Test Results
+ type: OTHER
+ deploymentVotingRecord:
+ displayName: Deployment Voting Record
+ type: OTHER
+ serviceQuestionnaire:
+ displayName: Service Questionnaire
+ type: OTHER
+ serviceSecurityTemplate:
+ displayName: Service Security Template
+ type: OTHER
+
+serviceApiArtifacts:
+ configuration:
+ displayName: Configuration
+ type: OTHER
+ instantiation:
+ displayName: Instantiation
+ type: OTHER
+ monitoring:
+ displayName: Monitoring
+ type: OTHER
+ reporting:
+ displayName: Reporting
+ type: OTHER
+ logging:
+ displayName: Logging
+ type: OTHER
+ testing:
+ displayName: Testing
+ type: OTHER
+
+
+additionalInformationMaxNumberOfKeys: 50
+
+systemMonitoring:
+ enabled: true
+ isProxy: false
+ probeIntervalInSeconds: 15
+defaultHeatArtifactTimeoutMinutes: 60
+
+serviceDeploymentArtifacts:
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ AAI_SERVICE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_INSTANCE_MODEL:
+ acceptedTypes:
+ - xml
+ OTHER:
+ acceptedTypes:
+
+
+resourceDeploymentArtifacts:
+ HEAT:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_VOL:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NET:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NESTED:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_ARTIFACT:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VF_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VENDOR_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ APPC_CONFIG:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ #DCAE Artifacts
+ DCAE_TOSCA:
+ acceptedTypes:
+ - yml
+ - yaml
+ validForResourceTypes:
+ - VF
+ DCAE_JSON:
+ acceptedTypes:
+ - json
+ validForResourceTypes:
+ - VF
+ DCAE_POLICY:
+ acceptedTypes:
+ - emf
+ validForResourceTypes:
+ - VF
+ DCAE_DOC:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ DCAE_EVENT:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+#AAI Artifacts
+ AAI_VF_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ OTHER:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+
+resourceInstanceDeploymentArtifacts:
+ HEAT_ENV:
+ acceptedTypes:
+ - env
+ VF_MODULES_METADATA:
+ acceptedTypes:
+ - json
+#DCAE_VF Instance Artifacts
+ DCAE_INVENTORY_TOSCA:
+ acceptedTypes:
+ - yml
+ - yaml
+ DCAE_INVENTORY_JSON:
+ acceptedTypes:
+ - json
+ DCAE_INVENTORY_POLICY:
+ acceptedTypes:
+ - emf
+ DCAE_INVENTORY_DOC:
+ acceptedTypes:
+ DCAE_INVENTORY_BLUEPRINT:
+ acceptedTypes:
+ DCAE_INVENTORY_EVENT:
+ acceptedTypes:
+resourceInformationalDeployedArtifacts:
+
+
+requirementsToFulfillBeforeCert:
+
+capabilitiesToConsumeBeforeCert:
+
+unLoggedUrls:
+ - /sdc2/rest/healthCheck
+
+cleanComponentsConfiguration:
+ cleanIntervalInMinutes: 1440
+ componentsToClean:
+ - Resource
+ - Service
+
+artifactsIndex: resources
+
+heatEnvArtifactHeader:
+ ""
+heatEnvArtifactFooter:
+ ""
+
+onboarding:
+ protocol: http
+ host: <%= @host_ip %>
+ port: <%= @catalog_port %>
+ downloadCsarUri: "/onboarding-api/v1.0/vendor-software-products/packages"
+
+
+# #GSS IDNS
+switchoverDetector:
+ gBeFqdn:
+ gFeFqdn:
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl:
+ publishNetworkBody: '{"note":"comment"}'
+ groups:
+ beSet: { changePriorityUrl: "", changePriorityBody: '{"name":"","uri":"","no_ad_redirection":false,"v4groups":{"failover_groups":["","","failover_policy":["FAILALL"]},"comment":"","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "", changePriorityBody: '{"name":"","uri":"","no_ad_redirection":false,"v4groups":{"failover_groups":["",""],"failover_policy":["FAILALL"]},"comment":"","intended_app_proto":"DNS"}'}
+
+applicationL1Cache:
+ datatypes:
+ enabled: true
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: true
+ catalogL1Cache:
+ enabled: true
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 43200
+ waitOnShutDownInMinutes: 10
+ numberOfCacheWorkers: 4
+
+toscaValidators:
+ stringMaxLength: 65536
+
+disableAudit: false
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-distribution-engine-configuration.yaml.erb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-distribution-engine-configuration.yaml.erb
new file mode 100644
index 0000000000..e71f6a9e23
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-distribution-engine-configuration.yaml.erb
@@ -0,0 +1,44 @@
+uebServers:
+ <% node['UEB']['fqdn'].each do |conn| -%>
+ - <%= conn %>
+ <% end -%>
+
+uebPublicKey: <%= node['UEB']['PublicKey'] %>
+uebSecretKey: <%= node['UEB']['SecretKey'] %>
+
+distributionNotifTopicName: SDC-DISTR-NOTIF-TOPIC
+distributionStatusTopicName: SDC-DISTR-STATUS-TOPIC
+
+initRetryIntervalSec: 5
+initMaxIntervalSec: 60
+
+distribNotifServiceArtifactTypes:
+ info:
+ - MURANO-PKG
+
+distribNotifResourceArtifactTypes:
+ lifecycle:
+ - HEAT
+ - DG-XML
+
+environments:
+ - <%= node.chef_environment %>
+
+distributionStatusTopic:
+ pollingIntervalSec: 60
+ fetchTimeSec: 15
+ consumerGroup: sdc-<%= node.chef_environment %>
+ consumerId: sdc-<%= node.chef_environment %>1
+
+
+distributionNotificationTopic:
+ minThreadPoolSize: 0
+ maxThreadPoolSize: 10
+ maxWaitingAfterSendingSeconds: 5
+
+createTopic:
+ partitionCount: 1
+ replicationCount: 1
+
+startDistributionEngine: true
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-elasticsearch.yml.erb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-elasticsearch.yml.erb
new file mode 100644
index 0000000000..611285ea3f
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-elasticsearch.yml.erb
@@ -0,0 +1,11 @@
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+node.name: <%= node[:hostname] %>
+cluster.name: <%= @cluster_name %>
+node.master: false
+node.data: false
+http.cors.enabled: true
+path.home: "/var/lib/jetty/config"
+elasticSearch.transportclient: true
+transport.client.initial_nodes:
+ - <%= @es_host_ip %>:9300
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-portal.properties.erb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-portal.properties.erb
new file mode 100644
index 0000000000..4f23cb827f
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-portal.properties.erb
@@ -0,0 +1,32 @@
+# Portal REST URL, ends "/auxapi"
+ecomp_rest_url = <%= @ecomp_rest_url %>
+
+# Java implementation of interface
+portal.api.impl.class = org.openecomp.sdc.be.ecomp.EcompIntImpl
+
+# CSP-SSO URL
+
+ecomp_redirect_url = <%= @ecomp_redirect_url %>
+
+# Comma-separated list of UEB server names
+
+ueb_url_list = <%= @ueb_url_list %>
+
+# UEB topic where Portal listens
+ecomp_portal_inbox_name = <%= @inbox_name %>
+
+# UEB key generated while on-boarding
+ueb_app_key = <%= @app_key %>
+
+# UEB secret generated while on-boarding
+ueb_app_secret = <%= @app_secret %>
+
+# UEB topic where App listens
+ueb_app_mailbox_name = <%= @app_topic_name %>
+
+# Consumer group name; most Apps should use {UUID}
+ueb_app_consumer_group_name = {UUID}
+
+ueb_listeners_enable = true
+
+decryption_key = <%= @decrypt_key %>
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-titan.properties.erb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-titan.properties.erb
new file mode 100644
index 0000000000..27386d47ef
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-catalog-be/templates/default/BE-titan.properties.erb
@@ -0,0 +1,24 @@
+storage.backend=cassandra
+storage.hostname=<%= @CASSANDRA_IP %>
+storage.port=9160
+storage.username=<%= @CASSANDRA_USR %>
+storage.password=<%= @CASSANDRA_PWD %>
+storage.connection-timeout=10000
+
+storage.cassandra.ssl.enabled=false
+storage.cassandra.ssl.truststore.location=/var/lib/jetty/config/.truststore
+storage.cassandra.ssl.truststore.password=Aa123456
+
+cache.db-cache = false
+cache.db-cache-clean-wait = 20
+cache.db-cache-time = 180000
+cache.db-cache-size = 0.5
+
+storage.cassandra.read-consistency-level=LOCAL_QUORUM
+storage.cassandra.write-consistency-level=LOCAL_QUORUM
+storage.cassandra.replication-strategy-class=org.apache.cassandra.locator.NetworkTopologyStrategy
+storage.cassandra.replication-strategy-options=<%= @DC_NAME %>,<%= @rep_factor %>
+storage.cassandra.astyanax.local-datacenter=<%= @DC_NAME %>
+
+storage.lock.retries=5
+storage.lock.wait-time=500
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/attributes/default.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/attributes/default.rb
new file mode 100644
index 0000000000..792d600548
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/attributes/default.rb
@@ -0,0 +1 @@
+#
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/check_Backend_Health.py b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/check_Backend_Health.py
new file mode 100644
index 0000000000..c6ebcdc4f1
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/check_Backend_Health.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+import subprocess
+#from time import sleep
+import time
+from datetime import datetime
+
+class bcolors:
+ HEADER = '\033[95m'
+ OKBLUE = '\033[94m'
+ OKGREEN = '\033[92m'
+ WARNING = '\033[93m'
+ FAIL = '\033[91m'
+ ENDC = '\033[0m'
+ BOLD = '\033[1m'
+ UNDERLINE = '\033[4m'
+
+
+##############################
+# Functions
+##############################
+def checkBackend():
+ command="curl -s -o /dev/null -I -w \"%{http_code}\" -i http://localhost:8080/sdc2/rest/v1/user/jh0003"
+
+ proc = subprocess.Popen( command , shell=True , stdout=subprocess.PIPE )
+ (out, err) = proc.communicate()
+ result = out.strip()
+ return result
+
+
+
+
+##############################
+# Main
+##############################
+beStat=0
+
+for i in range(1,10):
+ myResult = checkBackend()
+ if myResult == '200':
+ print '[INFO]: Backend is up and running'
+ beStat=1
+ break
+ else:
+ currentTime = datetime.now()
+ print '[ERROR]: ' + currentTime.strftime('%Y/%m/%d %H:%M:%S') + bcolors.FAIL + ' Backend not responding, try #' + str(i) + bcolors.ENDC
+ time.sleep(10)
+
+if beStat == 0:
+ print '[ERROR]: ' + time.strftime('%Y/%m/%d %H:%M:%S') + bcolors.FAIL + 'Backend is DOWN :-(' + bcolors.ENDC
+ exit()
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/get-pip.py b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/get-pip.py
new file mode 100644
index 0000000000..87ee652f53
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/get-pip.py
@@ -0,0 +1,17760 @@
+#!/usr/bin/env python
+#
+# Hi There!
+# You may be wondering what this giant blob of binary data here is, you might
+# even be worried that we're up to something nefarious (good for you for being
+# paranoid!). This is a base85 encoding of a zip file, this zip file contains
+# an entire copy of pip.
+#
+# Pip is a thing that installs packages, pip itself is a package that someone
+# might want to install, especially if they're looking to run this get-pip.py
+# script. Pip has a lot of code to deal with the security of installing
+# packages, various edge cases on various platforms, and other such sort of
+# "tribal knowledge" that has been encoded in its code base. Because of this
+# we basically include an entire copy of pip inside this blob. We do this
+# because the alternatives are attempt to implement a "minipip" that probably
+# doesn't do things correctly and has weird edge cases, or compress pip itself
+# down into a single file.
+#
+# If you're wondering how this is created, it is using an invoke task located
+# in tasks/generate.py called "installer". It can be invoked by using
+# ``invoke generate.installer``.
+
+import os.path
+import pkgutil
+import shutil
+import sys
+import struct
+import tempfile
+
+# Useful for very coarse version differentiation.
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ iterbytes = iter
+else:
+ def iterbytes(buf):
+ return (ord(byte) for byte in buf)
+
+try:
+ from base64 import b85decode
+except ImportError:
+ _b85alphabet = (b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ b"abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")
+
+ def b85decode(b):
+ _b85dec = [None] * 256
+ for i, c in enumerate(iterbytes(_b85alphabet)):
+ _b85dec[c] = i
+
+ padding = (-len(b)) % 5
+ b = b + b'~' * padding
+ out = []
+ packI = struct.Struct('!I').pack
+ for i in range(0, len(b), 5):
+ chunk = b[i:i + 5]
+ acc = 0
+ try:
+ for c in iterbytes(chunk):
+ acc = acc * 85 + _b85dec[c]
+ except TypeError:
+ for j, c in enumerate(iterbytes(chunk)):
+ if _b85dec[c] is None:
+ raise ValueError(
+ 'bad base85 character at position %d' % (i + j)
+ )
+ raise
+ try:
+ out.append(packI(acc))
+ except struct.error:
+ raise ValueError('base85 overflow in hunk starting at byte %d'
+ % i)
+
+ result = b''.join(out)
+ if padding:
+ result = result[:-padding]
+ return result
+
+
+def bootstrap(tmpdir=None):
+ # Import pip so we can use it to install pip and maybe setuptools too
+ import pip
+ from pip.commands.install import InstallCommand
+
+ # Wrapper to provide default certificate with the lowest priority
+ class CertInstallCommand(InstallCommand):
+ def parse_args(self, args):
+ # If cert isn't specified in config or environment, we provide our
+ # own certificate through defaults.
+ # This allows user to specify custom cert anywhere one likes:
+ # config, environment variable or argv.
+ if not self.parser.get_default_values().cert:
+ self.parser.defaults["cert"] = cert_path # calculated below
+ return super(CertInstallCommand, self).parse_args(args)
+
+ pip.commands_dict["install"] = CertInstallCommand
+
+ # We always want to install pip
+ packages = ["pip"]
+
+ # Check if the user has requested us not to install setuptools
+ if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"):
+ args = [x for x in sys.argv[1:] if x != "--no-setuptools"]
+ else:
+ args = sys.argv[1:]
+
+ # We want to see if setuptools is available before attempting to
+ # install it
+ try:
+ import setuptools # noqa
+ except ImportError:
+ packages += ["setuptools"]
+
+ # Check if the user has requested us not to install wheel
+ if "--no-wheel" in args or os.environ.get("PIP_NO_WHEEL"):
+ args = [x for x in args if x != "--no-wheel"]
+ else:
+ # We want to see if wheel is available before attempting to install it.
+ try:
+ import wheel # noqa
+ except ImportError:
+ args += ["wheel"]
+
+ delete_tmpdir = False
+ try:
+ # Create a temporary directory to act as a working directory if we were
+ # not given one.
+ if tmpdir is None:
+ tmpdir = tempfile.mkdtemp()
+ delete_tmpdir = True
+
+ # We need to extract the SSL certificates from requests so that they
+ # can be passed to --cert
+ cert_path = os.path.join(tmpdir, "cacert.pem")
+ with open(cert_path, "wb") as cert:
+ cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem"))
+
+ # Execute the included pip and use it to install the latest pip and
+ # setuptools from PyPI
+ sys.exit(pip.main(["install", "--upgrade"] + packages + args))
+ finally:
+ # Remove our temporary directory
+ if delete_tmpdir and tmpdir:
+ shutil.rmtree(tmpdir, ignore_errors=True)
+
+
+def main():
+ tmpdir = None
+ try:
+ # Create a temporary working directory
+ tmpdir = tempfile.mkdtemp()
+
+ # Unpack the zipfile into the temporary directory
+ pip_zip = os.path.join(tmpdir, "pip.zip")
+ with open(pip_zip, "wb") as fp:
+ fp.write(b85decode(DATA.replace(b"\n", b"")))
+
+ # Add the zipfile to sys.path so that we can import it
+ sys.path.insert(0, pip_zip)
+
+ # Run the bootstrap
+ bootstrap(tmpdir=tmpdir)
+ finally:
+ # Clean up our temporary working directory
+ if tmpdir:
+ shutil.rmtree(tmpdir, ignore_errors=True)
+
+
+DATA = b"""
+P)h>@6aWAK2mtey7Dt5400EW_006Ei000jF003}la4%n9X>MtBUtcb8d7WBuZ`-yK|KFd2brwikWo_B
+;u!rn<*pQ}JfvyeG_Vod&A;=bObCD&Dl#;mHzWcj7k|JfvSvC!;CGmK7Jl_3ycgo4LuUaL)T8i>3Uf!
+{K-)yULvX<43rRlnDTFKLtiCtaEhGk1t6>Y;){XChN_eHhYh;m~eE7jfAO`S=_?el#mOCVI;OttT5C7
+)=ywWt&Ru;O(is#00muS(TqMUmmlODQWEvx{oC%gWq5U5T3R9Fw*YMK^!Ln^b5XJWq3>8Yz}7iHK>im
+euCS+?>~vuSm3`xLY~iqKVm#%T+5yR>VxT%R4R=kjHG9ea<q;*a<7E-!eCSnJG@Tk(SFvRj$S6EiL<s
+Mq>7%&OvOsdBN9NTQurtUeqruxyzb{dn;XOOY|12T6iY~H_KCECyGp_mh|{!`wT`}HI6L3<7HmSMDpK
+St{Rop+3GgiaFw*OD8%yHkxdIH3@+F@4yNJPdge#%1o0%AOeQBRQ%Y>g9WNWUt|VI**)9J!Ybv(nY@5
+~f9*N#>g<@;*z!l3_4crO=YisuGe#=WE4S2FUk%8apXYkt@aA)_NW#C*VOq8O5{SgW&n=Qv>v0at71&
+`U(F0?blF0b@zrVNBjT!IpJ$Ox>%Kr;@T1iRXxviFs|W#B?Jm&G0=0sjo#TQn`XO=7*A4Bl~`xLY<q9
+HV7(W;_X3(kh;7-kd4kv~^PNNf~^RSKyE<@F93PNmDZoj4)@@?~=3Rn<<zZBNaSuZbLQ!*LY3u!Za<j
+9Rz!Xf`1k{C$5Kk_Y>_MBV(C;pV+8Q)}jEnw2$Ew_O+6H8Z(F;zNzGXKJE(W2svM~tZgdrzPjKI52JH
+(p6PUI;+$5hjl&ET-lEf>rn?L*V}?y&g>Ht5h#S5Tjlu}++`dPZOo;BX%$5ab%RN(7D_6MWs^qL%lPF
+KR+VY}cY9&PtY(t3ZEdzx<t1YlH`qS?Tdr_4^W9M=v*0`S%d|cssk^|d|98!OIqTrenObD)CU8O&Lc>
+gxNc>BxM>&y3-0XZP9TqOYDLRO`=8(tE8M3(fp0td~}$sFBHfK1YlZ?9jx3l@p03(<tp&Dr_F&43A_Y
+{J2$3!D`!v{4(mcQRpjCC2jHh`iB!X6#I})gS?WmGcL}>#?apptr%_ra8NT;Gpe>Uj$jiH;T0$+=Y=A
+<l^dPVRpc=8|v-GC=PJiJqf!<3C4V|1@!3YwYd5EFBdlC{RX^AFVVOvm`ieqk`&!I)i1+nptoI>b%+D
+bP9#hMm;|(g*i-zR1>mQ3;Bx}M1K;T}9B0brb2Htsr&aC2D;~gS&Z47Y0c<fnSiz}_J7tbvhR3fW>$a
+tN%lw^rM3HVnK*5aOZ~P{K*RoOfqS=PQEQtErI^mr|*sEBf`3;kzy6-G+FH{s&w!U^Gj|gd@X;l{c`F
+q*&0}afga7uE^_rAKtDx%fcF&oq-gdik`ta^(|@Hi^-&@$-je}ZxWxFnr~xa)cYy6G`n;}$I8M90Hy0
+J42cuHD3;TE49wA;YVdh%HDQ4aR-v8i^y^Fh5PXIdGMQw}Tc4p2Nn6@PKDt3co3CUAYOP7)g>clN&Wu
+#I6ZQ`jMaLAWby;6g@qK#g6aOnnYj_v1juxEl%EjxVpN&eD^N>`SSJEV&a97K1x~DLdqcdov2y`5k$e
+Y7QDj&eGq4N8RT|&U>!y?&c(G73_ci)59UDxs71?q=((B0>czl(XuXG+-9<Fd+;&GUVdI1K+fM9aWJ!
+h|&Q=TfqO99yRN!qV5bqAe-TEP9ctuX4*){ZJH?QxB=mlUnUa+|#uxReo){(`)Xf8A$soaXymSHX|D5
+^H<Fh&f&vysqsV!<bYagRVpMMt7Nk{7BE?%V2KXFTTouzDf*=%l#hE3OorkUpXwfl5qSMu!G4YJExge
+ov+rtkcGhZDiHfYB6A7(P|h$(U`EX(gGT@kNV`O2w@rz9`N!rhUcW`VYEACk=1CpdIl3u4_du(0Sl5}
+D^Z3w4=%ubtuRhT9XHa8?;#(h#bcSlxtBboxA$PB-#bNuRzY1Bz@h)CJjJq>{l`fEf!!EKSp>18&+<I
+QWu|%%X1>h8;XI19tvO-r(V7&{)XhR9Mm07?waf}%B5DOAw(hpG{yfNuh9z<M<(J)}Bab+NV+0Wp&Su
+a8a1vhTDaK`JJOrw%w7%WIaUpdYTxZP|rb*N?JocO|75+kXgo!v*M^R6zEzTf}v2;8&fDFH(Zl%C&6t
+GiM3FGCJX0fkSOgnGJZqC157`*|3a$VtVc*!NCRzAqXJB#nGbWC<oL#KWahTfoIqX%NMlHRJ&A9}N`l
+*r$ogz#t1Fx4SUsz^(Um(E|<ZCTwbyAtim_UNYUkg7?{8MrQ@Y_?%f^DeIkNe6j8(RuR_mx5ryp2N@S
+?3?ct7@uq}V)m<vd$^+$?~wbV=Sw}F5Je`Wz4yivy23K^Ot{hmS}UZC;=J$DqiMb|&zq`&oY|0K&aBu
+YZXH{TORgXeb#W?`H8m!5LgAT3r#?}d4hWrBSlCr#e#Mp?Ohx7Yu>QdRIXEdeH|`vsONpTZ41gji!<_
+Nlydpv8upP5mBX%6Jnfbqhm<2Y4huhf<t6;o)_2$+4swz}q%QdT5YR~Q-BjB{~#iVpb0?AXDA8?Kx2k
+gUWFQ6Z7Yn>VzX|tILQd@)_Ftai2)qfD!s>wm?<X(z~hkf*jjuQe56{6&d*L@;7AUcf@dj9x>ekl58K
+j_1VK;6U%=!wahTPG%Q-4mvRU71IlT=3EjM>u%qr2^JOhf>D%qkfh1Ap}?*<GZdP_BEMUg}ZTE4*4m+
+Q)bgbqGRFVpv@Ut9$ucXQyYScwnT91*s|%Vnj&7<DK2rrO-3R8TnPBsOeWZ6`z2@ls%1N#K}u_R(KJu
+C%mmop?_5L1Fta%WaEu)6Lja@YC|EI7z+#|p%Fc>Jqpf&j`#0uwBKfYm<A;VY$YtNg9lcFDwtRJ)3RM
+*VSGf;m_8rKhG@;wB>1<}lrnp@c{LY4M8lD(EE$o<)jo?TU=h!E)V%G+?6m?G=W3zU2JGi~029`0QY2
+#I`WG`F~pMA8SA&^QRKHXqy+LyQh?{)Ix5|X})pWeTE`{tdK-L*c!?*R97i4#t9of=K!G0xov>+GhLT
+0xJpV{6*r3X7y>3rF#0%XAA@nZwU1XkgDuNj04f!hUOYGSasjID$n&ca-^kF8jF!`_k}g0CrqTgJILl
+>+84I=WrNLAs~QufOn4eQM|LQfMY#?E^b5XPxAvWwo5ZfoQ(lf(39JBB=m9O<ey(Sn|*{;>3%uqpE)&
+iA;4Be27vWaR-bu!txHJ!{t;HoU-_y5e>a=E#%~#LWT$DUc(IO|-XN$tW5#pQpmWEhKFn70F!B%@CnN
+@3JHYJ*fCapVfBUa3Qy8<ieF7`X*dA}SvME;lk`q&;s$r#}3}!O?ls}KY2(-b|9CM!$vhbU)KjrQ}nm
+{vtBwOt`pkLlu1#@g_y@jg+&ThP;NCPD1<!TR=WO)nk*8(!UCIc8Dobrr=jXIOv*+jpDj1_y9gUp{=Y
+IZ{8+|o0J*H0Y#JP5bu8=PFF((Si@yZ{YVs@Q_#rim4HQv2tQw@ZYi{#Y-j?WK2nM1xS9&dTvj)-S}n
+-uA&a*g^0+10(n(28+VMI9^lDf|?beHhq+<P)p`%NbvmMi+6|u4adqVX47$Y*uq36wu{-8ouPC&0&on
+<4kizKe%fBtqxWCKHsQ$N0Qx(N4lb&;&~57+pjhqz?SN}tDbZAN?>rN-d8l68-aJ8V>yb7o8CT%sggt
+x4o~TCfWNT5FWUA*~Lt4*ub?(p&^cq}`lKvf;*f3QZ=@Po{jRLZ8{PP7!b!kwixs?9WGvuO}7(*s-Go
+8=Dec<2ubP=;pgM|f`4=@AMn=_hA2^+El_k~H{cbc5a?{$A1Z!tq0^3uv^T<b5#wHrG9&K8FhP+K;uE
+h2QG5`)W$;+b4+!$HM?SFQ_EZ<2xOM`s~ub;2-*>bMLeL*11Y*+9er)`bBK2SZ=M4?u3-_$LG;@W9-o
+4<nLc2giefR*w*PeDv^C8KoRm9)ru*Z+rz$0S&wYleC^gWnUs|glgpPAOoS2Fax}O{6of*5Q3xuO1ym
+azh5pvT!$2$#1Gf6O?uq34I83G$4`Hy2k10^HF7>qJAxPkVE6}%u_qTE4|rr*Xu;+P*iNnYy))-jokL
+_E<>#kOk1LOtA1C7r(LC=kGAv=D`)+6BERWm7u?l`aRevJBxVZcarjKM5jy1w=aH@VhRVRG2nVSaNQC
+)ohKOSVD@$o?fBoL)DqVbP~c)7PWt1pz!-D+83TAe<cJlk{=+@uk!gM70u-dWzH*CeH*af(<-bwCQ4Z
+oP*DH~Ex*5$JT@k&87OaS|_uzd6_2NFNVE?@O0iF{Sxl>7W{EDhE$zy@j?2@P+p(fW8uD-)G!(RQ;Jo
+xa*Q)S2yfc3lX+tL7NbPX@O5j4F#4!HT9nzQTr*?ebLnXr9oHuQDp|X_x}M<O9KQH000080P~d=M@vb
+hWitT)07wD=01p5F0B~t=FJE76VQFq(UoLQYU6D^u!!QiR@BI{(c4&u2#~En{j$9G<D748MktQK_Rrc
++%vy=^T%Afb1?O$w;Gbm*jTC!0J+?--2&>tB6LPlNBO;hh9ZachS)9m2Y!fHTg19=I-g)>-5)UZy*_y
+g#11e(nC2o%NFCruN3-zSHly^kvv&4T)DAYEvR0g_9P5B)m%Bk#ZkBv#&T8)v!gq=!%TaNnmBwC!^Py
+mm5=^JN<Rn{!lZ)-EX3r%TmZmyuv}Iq2EIAVrX}O9;_Idv9nDfM$qcya`0Xv2D$IGq`vX2r{8mm$6DP
+huttZLgQ^Uy%a*4?%@DN!8+)$eF#@6`to|>e<-@X_TBwIZv^)H;@Wb1=4di?Plpn;|NU2C%)Ny8rJE~
+SiK$#S29>XbnQuoOn|gWaxXKSuO9KQH000080P~d=N1nacs-_D70C_0@01^NI0B~t=FJfVHWn*t`ZDD
+R?E^v9x8f$OcIP!ab1))V?Y2mutI~;HbVC=O?x@&Ce1bMW32ns^BXq#JE^pcbl-=hEhX86#PN?P1LP9
+GA9oEZ-1J*1;3dXp+fPQ`Yc78zOQsZwKtuaf1dX7FE>niot(QFOGDVoNApmAaISQo^@8AvH-CO5~+x)
+P5fwx$#`A*SuK!PoeyqI#jP>r+2AT%zr<mvH%=)WEb1<onK|S%>bB-LRC|fTr5*F<bh;aAUZ9Jj7hrZ
+QkQAYihYfeiH9N=Y34Dm`0j=&1w?8X_PAub7Tx$wnmHkj$*fR1&2zKzxs*bV$sMfP9XujWJdT({zmja
+vMXBDDJkQR!G{Z^F&{Ax7sU~f?(u^<lsVG)_T|+KFokpP2LtBVTv!5$71MKcHV6<4Du~k~;TK&lK-CH
+5Isn$%^#3cLGQ*nE~X1|tPvMnq0n3#2_tcMz!0cc$+x)d3!g=i#lb9Z`reseP>=hxSl*TBpCr}^cFId
+z}!XV<rPuP@H$@9F&VmcG5bo1a-xUR|HRpIzSF&^LFpi!*vQyLLE74M~Bq=GB60rs+<K^_Ud6xt#xpQ
+IO-N13t;vPBLpr9`%}S#hzue0_@|TAFNS!(VQM?5oI6)7zjwxB7iGp#;F_z%+NiPXnb_JVarcPM@N)`
+n8CNE<bLFgWb_fnaWPQp)N6kNo4YV$F{MSi1%FHc%y_W3u2?L7VN0Ey82KR(;Lk<D%y+5Mb!%Y5GuQ!
+)yiIduhhQ<Pv}Ea4!FU|8M}6}0CHOBWID_p(W6~aDQbxcB;%00G!GafDQyMFluf~K!pg;jk%?ge$e5}
+dRPY<xlZ!O0e88$~J1Sw3+N87VTs9~5N9V>FIM&y{pUhi=-wrIHZq=UBdS_0fB{UPpRrddWe06}d$0(
+`Tb4iv8G*~a(IH2B3*a!<fOQJRS*r5!*oGl251HN@wm>eY~Nu0IWUef;gWm6Vej?Hg|_il~h!^Nhguh
+>NsD3!*n%xo@(DY_O|PVbBavXIZELyP{D;we!g`-EnQ)FHYM0LQqF3qH+05FtH?lK+17rQ{(n}tu4F;
+mi2~#!xv1Zxo5wEfN{S}okk|x^b>PBq=s<Zl6(Ww(X~)*{WLkOCX$d=CTKUU<vd$Whgo0>pvtBs=QfU
+wF(hRzIa5<~*)h?4%fJWCSHqVaF6_0=ZJnK^5N)P$FG01VWGvJK|4)7qyokLh3@)S|&_}e<dKV>0j}B
+jv9ynp(B6X~(B-NIt-=O=Cl&V(+{AokM((+3<fHomW$83pJ%0?)yrXE(yQh@{6(Wbr{#`GgNL(a$&AR
+&4$(-(^p2NW^U9L=l*ZB_`)%`&M^I@V#tKY=LER|jk56=-15o0|(L!Z_p+f)ISgAuX}sS_X;^FNrhF1
+#9@gy3UkTXeBpCg!EwH1wGu9^exNJ0om$xNS_X%Z6+ZFnGgp$&IkniM=*g9_c(P3GwiT>DF+{G+b3-@
+{gTdrsi)ySH+er}kWsc!P$Q2Iu!*!l?zcN#Sr&oJMA8yIp_*`235<ZSx-LFJsEI|#LS$+r>RBlSB;Q@
+xP&ar=DGQHX2OV3;rE;xYz`@r>MEQoS(xnzCY1AC@rx{du3!Za*=##HQUW2lsS+BWGc@bW%igq{-d#d
+U62z7J~g(&M7XLpuNr{vX1Pmej<vmDlnKD@r3&*ncw&l36D6GTi|+9rV({shM9_3U<b`ud_qVtHd#2p
+<d4B~T%soxi#Jp@}&NIh((|jB3&K8;Q8>36ZN#!N6a&jXKJ>N*6h!?2#)pK57e8^o2k*6s>r!?QFo;p
+x?^Nj8WhG1)&UNoWNYvE5Rr4(#H$j<Xf{$6z;tuBL^Vhv7EB|(4RXV+}(^X4F~qaO(*coF8b}l0M~Xk
+!03n73g$T8f>T3noTz9J3Jj_lAF#G(kGq^Np+YyJQG=)^Ku7n-v4J!7m-<MaT{Zb?(QZs^9}+Btd?<n
+0uw*L@<NpD9nieT+MyDbd66&Aq#`HWk(F~fCZHyvUy*FB=`|myu>9TTz`@Yo=B6$Nrrk!m)E7+#ID8u
+uFC)~=5tqOmCo#<Zu{!+lCis9)C_HRS!*YExfL(Z;+Hf<zCA@+;_zz`U^QKNJKTA1gfml@l?EXq6|!p
+&Ho9su!HmhATwR@WD$5aA&9oSQYhIEVBQTeFXC+j2i3UFX|F!f%`M092*wX3j>zR^3M*@ne!aDF-OpY
+_20|aqzi=ESTbWw&gsAlm)a13QM|8x@W`~l9f+~m?r@02b$T0+-{gMl^<CMy)nl{bkB+mtH8qcwTo0T
+bAF>b#7rr+!7>uiA2tlYlz{RX>dUh5eTyh1s9_;fY?CH!9oJbKi-3NH3Fn;td4_+&4rZ5fMl+no3|O!
+N>pq}eTh#CZBB?2B#=h?Q_#~#yWSx?!`vme9K7ruUZ+=lF=0L=-*3c$$2vw5?Rp2Y0n?ez-@V%5djQ+
+jDdV`D4;et1i!rPUf&n**t=?gx3@d8M^@FZSX5@5j!?3)$DAB;lbkEZy*^Bg9i&^L#pefdSW>6F+D0^
+*9tNx)e{$E$6qR>yaHd3Je1v^?N})S8)I3DmfQc#N)uXZm$_zO?6A-#75BEV$la`y!bV()3F}OsEMf_
+FO`b1z#JHXO{LPC~x0VwS`>MxF223uIT)d&gNHlx1*1EDE4X;BxUMmMWL^P&!_2~|2(_7y`is9Z)cZt
+OO%Y-r!p;SVOw$M^k|IxlTH|!tw3uyfnB)+%!oOXMiN|`J{1ylo*&QvRsupxw?)F*a_IggP6Ex32|bg
+g<k;rs;jl0|HD{7Ydj)w6%#h$F_r@k``Xu&TCzH_^I4Pc1gUoasx^8I54@|Cec3|!`d{+wFja%(%(h=
+<2+0XCJPYB4(n`L#(>bhmv5HjKjchHT~ZNL$^IWNgf{^ZKsH5E(d&rk}+GUK9$Ei~y}z0~lrT?9xJI-
+n5WzqdKDmto;6DguE5<~!YHN3T%X#Q@YfCv;F5Ta@c~bnb85fHhmYpjV8>NS)%)6pNjPVjBDo-3xVh5
+P#vl9jWj7lK+bh26xlEK>Q*uM~2J^-jSG9|5R>&VuyuDB?Nq^17*3>&+vM=W73{{EzxjuD3r+JV2;N|
+e<tJ4iPSImg-f*VIx7GOY#3O(LHX2&-v>65SRk5|1qlxCfRy&>m57zOCQtV%&#|QJ`hVPP`u(v@@9NR
+Sn&uf}^MRBL-Gr=TU1Tz0#vY-1v3Gfac!CMn-E6gQH3sW0Pk#`=-w%8@4B~G^wvZnGG47r!C&r~6BYl
+?f>4_$G;7RW!)!k)R_pXiWaeoEtBgu(H{uf49zT7gXMQjE&2e4vSqE?%i=^!l~cfM_sX<P_lP8>QoYC
+UcXV_UjoQeinYmop8;;cq<r`@2-CO`78^7&H&iPngqKNE`x6g$7$dSx<r#un_ibF4l$BO$7YWG6hndV
+igh0PY#N}PYc3E<LQSbP_JZs(u-^Aq~<xAK52GNa??tJ4cSe@a`aC}d*%YLF^1Z0*i>WtDC?7>uOZc2
+yZ?iY30BAybs3f=kQ>_;xxT0OI`ac`E3yG$4~XTNVl2G>s;tep#&GpV3B%JL7D#X+vp*A{`(=Hz*cb~
+V{Y5f`N*rYa(VC*Ow0_-TA;MnT^u0gWO`q|~o(F!7r~+OYk_m=~|IvM22V}0N@Eosa!#LPmyThORk-~
+5O*CY7vb2p)T$|x}O=TEA?$*sccz<Bg;QIc&5)xw(ZA%<&`vlPDYfT$V7A*%P=CijevV|(CF;%$0>?(
+cv!Sqf-)yg{nzrzjXU6BT`*jL7j2oH>F*5sZm}pZ5CPddo?t+zwx`eWe(NcK`ya#h@6oTgP7kRpSwEj
+o9CFG;v+k=#FAyU@1BJFHlPZ1QY-O00;o{l@>?ZFvLBk3;+P(C;$Kv0001RX>c!MVRL10VRCb2axQRr
+omy>g+_(|`?q5Ny5lBkc>LrJMG3rCpyxbWmF2SMc7snO25_gxiBnl+8>%F4?y)%4~NJ(ouX9Ui!G#t(
+h=jEB9SF6?EsbWNQnv0fnR4OKkV!H2EtJTF;iiQ-$w%5I6ML~Ge38@L)C{g#C754kZg?p|}%6|lghS~
+6acq4~rnmy{Hq@CTS;!5|L*DA-~ek-M=78@bz3rp4UE-wzOt%S@Ke+<ZYyDRjmV``BwZ^VHqPhN_4%X
+e1Wk-QcqMKcU!=>>tGn(b(LD(sqK%WJlF42liduB8n;kbT>t1btgESsb|3J*`=LSQ{3KgRuez>LMGrH
+A)Yr?YfFfwI(~J3(aK1TUr;2|IRKhE(&1CrD%&zvMqlsBqKLq&%6{(2V7IR`uCUHFU4w&4{tMAQPxx`
+@<lSOPrtCb`y^yTHSkUS0p+(KajRJsz0R(U+~5Q6NQbLAlYa)e6*(}u5#TX<ASYW-kDrD!&(U?v`igD
+AeR#`tQKX91+ckMj<qp(){rcgEpWH_Hb7^^Y0<4A3*z`)14I{MXyOvet$n}2UmYbbmN0nDhm696=9(Y
+;icS%DZ3*=gOuE{-^?8hHwgxpvGn3s|@@X64<nUNiNrT6CD?0nMf?uUDFMcxjJo;Gp*jA2F#S}PYeMh
+ole%B`{w11rK|^|B@SHZPV}+OKPpkiWiCKPB)1{ssG;Sr%@%Ce4A$ZKH1nCU_RCyD_{*anKd~=ijVo6
+r!hT=oL^?Nx>|nhR`Qs>$8yCkkGaw4bv2MQu)Qe<{FnZj`*4o$W=AN<*C}c)UzhFXm|K2TN9S=u1T6)
+ZBewVE2|vnJK^UPy7pT-vpZ?vVA+Azy^)tHPfcEV@&)lgK@iWWDdeI}kPN)(?tavddqd=fQ3%gLjTZH
+*a!3`B-qJZO_+hrh4Q=v5W&`Pz7vwY~QW^P)yjkGGTS%!oedGNhThdF@2c`-%Cqzxm#2f#{)A|QCzu*
+&ycMZAv2uUnEtH|tp{E9#z7b;1T|3`4A)-3Awj8<^|&imcBKxoj)JtQ|bB>8M)R7tGWu+wyJzw}Qex&
+GEBv)y||cg*c=KA#D#kFA0VP4u3@Z?1@F>l6H9q-DpN!~YwmkBqgJiHR(rC0i&p+6xh?G%#uf3G!>cx
+Ot;McanT-zasN%@=Ad!V0n{+Tw+BbRO%}}oFP6<tx9XM0*2YtHo$gfk3be@mEcKKOKL_BpkQCuSgf@a
+H30-5LwceG*dtUsco`oco-$RCx24(ERv!uZq@lb`CO(WmHQlfpt@0joU0bOnB4M>+Pb~T{$`P--BuX@
+lRH7WR<R10JO2TRFj~nWUqiYi#F$mSaiH^2LhFuEBm}D>7bBm>K*wOfu>S+0gHkWqu>BiPg*65BcxP|
+qlCs;o3aiP0R{cg@Ld8HsMy+5bsd}5bd3iBmEGC%M!Crq830hkj$<Jd1Ra&1dG_iFomad-Rb`=pHQYl
+TEBuicA4RPYT;h851fg-mDi5#mMjfwyLLuDY6Q>wWjrOtAXau56ATIF8`<YgYkkZ+9QqmiDz)G0a_nz
+9Y7M4+s-L_yUyem67bdf$TP?e=#BNaL7ORkQ?D&kDG&jm`p1t0OVR=!3cZoASN)o!`o&JI2KBf&$IXl
+PYSB`5xBrXO36wMSsM+qcla8C5v<6)z~doU=7rny92kVd57L#*=bHQqg&{uWfG97lPM)<26JG8g&XV;
+rSobM$hm*9&5n6lfQo^TBLF<bMH#}0^Nzr$XIj<g{YBs=_w`MKmEn=DfjH?MCK1y~RI>(_C$-`o2_VB
+5OR#m+BGKe2c9MY=16Jf?%AaG#HsA3+?!HaoppZ=|rq67NVU>(N|y{G{1GnR>M@H0boJ^lt7w?<(!mv
+!&lSJ-fH_}obsT1A=|4^=>(ch_SmV^ncuGS_pOaTK_VMi9qCW^>(va`c^9q^5Isnniv*_Bif&xgSEBL
+nMzOeIn(al@BhO4IA6f_tMM^h-vFh5p+#Dc9BpY0eW(}fp<grawTfhM%<836cX+qXqAl(?f$?eoMz;|
+(WAA6v%<u|a9RONEJx#k2|h|DWPywU>B>J|t*z*jRn`X153oTFM;>W5OH1bYLMF1yatXLJ9SO(vaNro
+ZgF^F;J4)FvZnS42PXgxBfo9aw`XE>cTtj0ufIed{a7qC|v|-k#DibxbLg&Kig?Amx%%X404m}*1G4w
+F3=8{*CNRc~QTskt09}P}MWfd?A51eeN+K0#^Pzbv!2rlcNctB}_^P=~kfBkg(bMfi+A0Lz1!NOTZ91
+M$!9a?}Ni%=nKWiK(EqRntnMvlw^N7|muMC|IwD;9^*m@b?vp<UA1_$&*zfSh;Lqvmlywva?ZEwJ)Mz
+CA<up`IAXplC<+9<mV#q0->>0^m1t`R+3$eF5&j9Pk9%%FY{|G{nvukH$`lqbG5VzySwi#PqZa>^jMX
+<QhGbS~vl~$*>}R$eBN_!F=c$*nC-T+~Ord@A+>3LaCi8kli!#&femWKR^Q=7b)<^ja+l|A+InzNLL;
+2R@wUOy6FsfkuaM*jeOViPyqVIf$^-YV2;qTZ#t71urZIo{kg&Obg%+|=1|OX!4u|DCwOh<%yZ42R6U
+!<)g+Flk~|koGx`}2up{*$jhjlnh&9*bft}V+lj~qpEl-Py%#E8YH#@8vR*xOiW63%_Ej<51>W@+a=A
+jB{;tjyjp%q=dgZ|6Vh_dVf{#T^x&8#tU0i36-p!)<av1Rdqz@$3;F`V5lq4)IwWMcI9HRxU}<Q)KyD
+OW~mMa)eF(ge|Mpw4JQc<IXu`5^EuWNxj&%~Z!qoJ>GSS2Ze!K(K`JO|j9z`x-!=VF5Q6Fe&DJl|>j6
+vki#26DB#=LOd8=xVH%lqC_uC!*622Ni77||L^|-wr}!Cvk0;(Xv#1SLD)3e)nY?ysF4HmnE0*(F7^x
+-Ldq<|rl{t>vjV)ybyorKa2uxX|13KbTbJZ*ehzAWN7;)deo{bx`Gn*6G|NI!-hl%*J+jByepj<c?@0
+((bdbCaH|JM3W7wF0aeK{sOk`Z(_}ySjon}*?fQQx(oclPig>8%7D>_pj#dxSre*hSbnx@Q4!gsCd<e
+Ltv(u7lBUwAXmVK|!MZO`JeDWXvon$<qR)H0oZ-P+x~Qn(W&WQ9`{GbDhz>p(yGnD25RXs@h`!i!!{b
+pdE*-`*pt#A=6;4*=~*TRjXUAn8Iq!ViFB@e41}rzXrwH`UCi=5YFfX!9w<d8>?912hOXXwf7qp!PAm
+xv}52h)(fqDf(6~O<tXw_<Q3tO4s<q>e+TUxwq9OImZxwyBXEIq+5zR+q*=?CoVggalZp<2TSf>B2@{
+;u%YF{f?PvX;26T++~DQNTtIk<*LdTtigP@7w|7=0UjjvYz%h$0f^OZU5c;1>^2wA7`oIdS$BYEKu7J
+7IurQCoJ`2*=PHkb0djYG0j2>tm5)X$FDqwT@LQ@!@%@S(&`RJKLM<YdReSW(Oj_lrg*u^wYp{oq%e7
+(#D$lQhWct2rtl@TF3lY22fr$EZ{9Pz|LF<29mEn_GV-0hl5T5)ssRGw@y9{5x!6SmJg=wZ!Y6a?k}x
+M!x4V=gyjN4$nI5Ms-1HyxC`vEZQt49Pt1AwvWa2oXVmX;Cox#n#5NxWV7KWL-^5mL`R{PfliV7xOd{
+LgkZMq4sHVb+weLEK_*-O5yzieuelkcXx=q!4Uo$J1#q;9{%vrcP3{!5-komMx22sI>9o-WYEJO91g~
+yDzvMg=eT0Jr)pro0FuJLP>9N}p`EJX#|d8i2!+fCn$`~yf9_bz%)PsHKrCl7xa5$6ZF7kE+nGSEsap
+iIlSRA$W|;qlPOcc0<=)iyxd)s$t1FT&E$cVwYGotX-(82<#M{G#tj*0!7WwRkNoI5@NCS!4&N$sHbH
+w~~i-RWK;)F?a>JB~v`SHNC6H4(7w)(TSBO{gY_ae}fLKYD6A)h$SfK59o0Jy7(GE+46xvtiDR#(_$J
+pgR~3)!$eJ#fJN`I3u0122aqg9ThVU%mqI-@s|R698ZntG$e|rN=-1FL3aP!1Wx;o4Jff$v1-xW-NEM
+n1j){vL^&7$gNpNfBv@MW#3=^&()}fo_MIHZq50tA$JOs12DH)*ao{))TrHb(rx->|F;m(^(tM#ib-K
+Jt?=75S>bgpHq6NA;{Q-f0|XQR000O8^OY7yRWz-g0}ucJZ8-n{5dZ)HaA|NaV{K$_aCB*JZgVbhdCe
+RBZyUGucl{N_1;a^&bm=w}!;E^EYr9FXG%;eQE0zWa>O@JLPj}bjogHo1|GxJ@-jOF+a^e<ffYuWE`1
+rp09{ChS(a5Z2trnuJ8eJAf$YLQ@b=g*CG`d+UQPsN0bd@WyR<#n^h^7<^wbX^`)|5r2refYU;;W);z
+7o1HjVu~18zq{x+Kg6tIhT39O~hMRRx4@BLd=yeRxmG{4$-}C)<S0Z<usO6rHVx?GFiZXb-fjUwNazF
+YVMUPL?vsZOd={>+0SC73RTOz!`IyF3}`MIRTsGbl-M)tvdtGo+>9z|ka|X(h?^3K5*e)2rcp%$;Cb#
+52Va(Do|pGPH3(v%j9wMVXcU1Mmvy-jX}WBiwpM8>^rkB7M##A-^R`hbe;+a2vMsWvEOX<)fPN5H&a<
+!~@#7}n5&<tzDN}L%{_5)8<@I&?^78f7)Xt48<h`s55UR1;fMgO~EYv55X*?3}?`nJXYAU`j>y2#A%c
+7~vd@7ckCe4~WU4w*jm2Q9!nWySgmbVMlPZwo6*M+RNQ$pnpw??J+Yo+o@zj<C}l7(jpI6q6jd;jL*^
+<{c>cJp#7-d|t7OV7{FUtXpcuij0Csk5}I!MZ+;MkBB?v5~hbWeuccmg5!6fh1H<PKXRt0RDZaK>7tJ
+*_W9p=l@n&Ljbf`h}lel=GzU}eM355=&WJuiwn6?G1*CMH6M#xwKcS!cct}3ucs>-4Jh$R7N<TrW5zL
+MT0Zc8A15r%vbHP1XdQr~;v@4q#Gc5-!b)gt|4bNL&C>9JtyF6TR(WUj<&?*IwK$BGtX>LmY3Bn;r{t
+cNj4cS5bU;h8tkh0)fctD^Y=I`0uxzce+mzx*o~QFx=Zlnshoft1VeHq_H)WxEE4a``&LLJ2a)Z=Ys-
+77TM3q!q2qT_9L)a5A1OAJPPe3hl<0el8#y-{>YqyUnrD&@K#tVO4Koqa`7SQa}3DdMym20p);8?bKg
+CGOdnk29$y$m<NFpP|QS_q~)T`+Gb*_CS0(#FN4Ut|pgrexF9;{eB06jR!RZ3)*R-VYHg#X?0>5wU*!
+5vM;l^wBXPi|u$L!H1ccDWjcCo@Pzfa640SATAmv_+i`)<G+{<1rH!J39v^|E}hjZT7^o3lgmxaX2kW
+($u|y>JqG}mLI;6ERt#<|iKlqbWVgAxQ~Bwi(B_}@|3=R`i+L7|kJc)$Qjmx}B$yMnO^(9xYz?^M*$g
+4$snxk@PGfJt9jo3Demsq@*KnZW$0P=lG(-k4?nSyO7wXWa5in?BBX7$nKoA9cN2V#^aBS*UbxX(tPQ
+RDAQ65pfwxV}!K}Wo%xbCnE#10;{D#{wQsERvXm&FE7ZE+`SjZuJ;nHHTl>hXb@EcL3b>0nBDVlo{8>
+$Uo&)j%I$!ov?x9^(I*K+4J@-=bxMt~JO@iheO<L{jJ0=@YA{ncZa8!?EhRaT@Vd*iqF_I92xIj1P-5
+NE(3CJ5|q1b5OnU%o1i{1De7yM^09@MKhq}Yv0md=sN|9gScC^P1QDu-4VkXoMO6xc}~$V=bMxJfdVV
+^#y^WjL##;R5z}vQ-9h?)?C3|>A^qOigW~l3!|jN~sZY8gUjfCa<}eYzFcAb9kl>Geo&dqHjT0bJk(a
+9@ehLs6mg&Q6sdMGN!FkA&p8xjWH*hY1FP%n}Y}S!SM|1@Ps1zxjC2_Kjo(Su~tC%jxAK|3SbdyrdGl
+%`@pS5nlX)*31Pj-nzp}^5>REF8u`4h6_KxVuokn<iYel0J8qemB@g~ME3fSo}dU~Vb9qXmxkqMXt9f
+MAF9*P^nj%TL?GMmi&yfC_)4*;Wb(s0Ta8zMDkXmCE#T3pT+!LcB&@;qfg#U=TjRYU_US_m4E6z{njA
+LRpYR6n&dotAi#&VUnN*5z-%HYW0pIed81RgZ!j7?M4*sW{$ZXro_CxlGz%wkV28*ZLugIK~kHl@eUF
+ZjV)^N2ls(O)P&f<kL6+80Cp@C2MLR%9}K)OHYy9)4#Al1WnRi5<A3xykFOzOp<Yax-72OS$=u}ywkw
+MTaDy?%4yJe65g*3H*I{rm)8%w#3sv=o+(1FFrz3hQRI*A$Z0?HArJWpLvw&fbh|jUip+lHkU8VL^#e
+BiQmlf2=2l+Y^48-LkolEc$Q3)TFV*w{-{=|%y2kbmR$5U9N8VZgl1IRdVjBRdhQ+;Nt&#s=i6Btroy
+ivjHm%LVTv1K$JAn?R^XN=cU(Q^~*>di7A*}Bx3@`ic9oPLN68o$Q>-Q)k}@kbvuGyTCE8(a_d6P=^%
+(7--_Va<}KnPa(!XDHviba8AZ_qxf}Q!&;P%r+rI8Q)KIg@5N0NavxnM28<`sy^oK2ol(UH}H_c2UjL
+xOhD>`DBPXQIo)|RN}QjGd5d>CL}dnrX91PdBWNYaJtX%Sj!=%(<74T)zJ4tjvp~#xsWTuRl0ji0sep
+<e(-w*(QWt!0sDfZz)%p(Ey;WOE(s16xgvZg<<qv|ZNfHI!jHPXz9~Ok>;7rgnfab5&w$l8~6HSM47`
+orTdp(d1-oYhCZ>n730zX23Cn2AL6?iILZJRaN>J^xoTq*I2F7tL`HOFY{{7o&>j*uhnPhJfReC~GqI
+zVXlIb6@)K?mLE&CK#8Zh|};WV*84(x!@UTnuTDiv$n|40$R^_~<|xQ1-Mq9L-(kwHO3sxAsdjulN<k
+hmd|TrV=qP%Uil_Xn^n~%*=9K+#2Q?FA5<3cxpt5lpv03aLESQ<J?hUkL*je)M+}j5O(2LOYl;3BRJP
+97k&z{ZZ<i2?G$~{Uf0v%&zu94=fC8`vd-2};h01aPO48BC9FbJC(obLPWWgLL(M1_aCT;(pY3#ri}p
+mR)0Pgi%SyX-K#XZXF&x5C^b#-lp=97UgCpXHDvID|4n7(PKMK)BI|_(c0ny_W5M7;}|Kse%Wi(X6nS
+GW}!rnU3^#*#A?CcSWpMvfx8Z5XSNno5q*{w^{-|C=Tm<hbnbE}S2oGcW){2FlX3&qFxr58nq7|(OZy
+5VOgyz%we?$~syhy8FQ#fjsJx6m_$-ch@c$~;EvorYh&y}o&K_QT~7i*R*JeanwgxDiyDj)y608%~dQ
+wMLCEN{|BHKCk7S5_dA!iyghAqZC{5^5*91I`P#1OuSQ@5|+s&2v7Va`5NPFXxT>$0s`({NBJy!SfcW
+e7)1C!HVVjhb_tfEvbOg5={>(Kj9jY6`h8nKl><2j+EG91Hh3T!_=|ow@qoR-Pkrr;YbPwcOtqBrBa7
+VjMsmnTSfr1&kb&@q7BZNAqJ@lhN&jQ=4PKf+bz;(mLS?8T+wuW=ga^B9)-U5DIbh`>++Q35FAGvfL$
+CFO8#4`1b-x3jxrrC+@Rb{qE)8|((G(-&6r*3VlVW(-o}6RgK|f3~;&ZowyHzld6S_KJrO}c-8Yo#vy
+l0yD24kdsTJ<lNH$Vn;H`v?dN%un9*X2j$QwLZ0xMoK{KEB^ZLa*4*1wFp^Qt>x_x>(%3%~6pk7Kwx3
+Ei2pw(36FM`Qh_DdFi;52;E3x3jk(+Zew~tf|@=7fzbOf_hW=Fd%#Jwz&qozBKT;+;_Y%%i`V6-Qv7&
++EvO=ybdk~2*^z1lAXr?cZ0k(<pJ$M%4!?8e8r~(<SxS&$-PsR05%vuhe&KS_rNi#A8Xp*XSy?_YeQr
+u;K(T+<hi8TN_Js#Z^esMFk!=3<9QKX&F2wAd-%j00Wq2{f_!_SRfCsxg=3xEPPd}YtXakZZWjZxSZy
+@2XaO!lu1xJJqa5I7n1lg^AY!P?^LkH83qXhf?5XZ~bFCm*w#lv)u<BO8g!|$P3q@y0K1WrRHtE->r*
+32mS4$;?@=+rLbxlZhrxLp%aA}%gLIOk_Kmlw$VQr>}0^ZPn*#?ScVIWF9!E7hc71Hc$k*B0-MIcoK-
+A1@T-zaj*-tv`wDqZl+igs2h}Ww*2KgX-H07>IyVIA^AURu3AH=mHT1bJ}gf^U2=5aLK{Ot3IUK-P{I
+A--y+Z;Z1`b9rEE#{oXk7NZZGs<D}Qb(bT*%CdkP-=L*dNhFzA09YJ%*hrI;a4V~`Tz?82#AvLS_<mW
+*{-5Ac4@nkCK&t&H+ntcs#wHR|(s3zi^kKwLKC_IY!d3;#7(W4d4;|-Z{&!n9SN#cn(%Q96#5LX&zYj
+Fa6p6t7ZS=c5jJUa*EO~pxpeMh(q-@~^2mZX{~9)BhFgw{LwHvXj;Hxf~+T~=;3P_epxPV5v-#G8_uM
+xk0a<wot`>I!<S<(pb(w|r)mn+h+8Q8*j9h0vmN9JfabhppW!qCWZ2h7+IC($QT9`Hpkc@DTV{HNC($
+etqNPoZ@$Dd;{`+d*i!u)`cLL0S&QPcK{;6_U5hM7`)Ho1{OBNwrg=2$Wmb@ccjoHILrlu%CcKMs2B%
+bwn2^<_2Bj$A5#W?K0$P1AsdOO^|zRaac2hvM4mfZ(LavPhiiCivDPL!qHH0%DDR8Bl#Aok;H$C)W}3
+o><p9rvuUHQ?fk0|^tZe~Afl!>u^B1nnhrJBc(5BKo^Pl}35pm~<Gf3wHK58;LZs<H>bMb7>#1pjlu`
+f`%uY2l~ZjXm6ls%P5TOiT>B`#JU6oLpHeC3j0&~AD}6=7HA;QcN@G!T4HmUaw;u=YZRp0i}VYPsy@r
+hzV~+6dWoB|8oI&zTgSfYULC>0IBcoqp2OpQC=rWVMFqdiuOI^>eCCpZnM#Z2cTf67e38bs>F-&~I3#
+A4YVLJ}M7r$N)XtY|EC;X1<+~4y*lXM}Vq-guxnd$}*~zd+&2UouFB=)Uea|h@OLn+(6ACnkQ=6)~=p
+d4yig&G38fk>B6lu1FL%C7$a@2<dIHCinIv!#8AOowUzZRrymz6?9rT7x=QUWT*{XPhtC1KZU*aIH|C
+7i1&6|e*uVH*l$okjt;>bZ=*fr4#m9I48B?R`4gQh|PHRd_q$pJ1>>1eolDm*0^Spzjp3Wn@yKrX*82
+^TKPL|$3F_Y+?i3_j(x7W?s$Xt(PelNEsZHxct>ad#s#c`Gw%xFPi*FL64wYKhiEAji$`FCJJJ32Ta|
+Hi8OY0H0^I{F#QiHKj=pJ2TWJf?G?6Y(KPN7Ft#4I0P$;oYw6e$IBE1_P#(82+O++x}}_u7WSa*3f>^
+wPE%n)VAo;MGb@ffNiDQ$<47}tsi`#>#GL{8mT<&-h-l7FsR!9*0<*PHHA&K0^VrUBlRKYV~~%d|G9p
+d2h<*Rs60aXT}O$}dC+Py(8a<RQs2_xT^|SUk;8HSaWTMrco{!k?O8CKc<5E97vTQ^P)h>@6aWAK2mt
+ey7DocJ&cxFq006*z000jF003}la4%$UcW!KNVPr0FdF?&@bK5qSzx%JiD&ry5%8b)?Z)V-`?xktE^<
+A1|;`C$3^)a*vNvtVSOOUqJH21gP`v5=yq-3W%H#@U;noc8;z{A7C!}|ujXzH~{(?!>IjZ9NftT%Pji
+tI|&W!K7-e;*v^=ksM@zRGo0lu6eXr819JnOc>_mHD}p?Iu(9?JugX%=fZhE{keuey)qPY_}U}+t&8m
+rp($!-K@>;Mw)MGg@fB)Tid0T>&>Dl<-tMQY)=jZd`u%%3h_u(_1`mk{O!~8^zDmtaVp+6ojj0tbGd2
+788v?1G<9<lH2Wqi6~J8})i%W@NpED8*G-~|yJTJ8NR@O=2_!HBZn8#6ktxBCQ~Lez?v4C=CspeJ&`<
+1w02Buuo<LDo%0M`)RGXDRLwHCtnu%{=mA)&~I&0^vzU#WqWof&;-o8HT-_atQSFzfZMJw;xnP}E+Bj
+rrs`ebdYieKSJUO<Cv3sY@ZGa}!}=PH|DcbhaX0G~`<Q{z<L6{=M;(N!CGo#GPi1Tcq5rK-EyH1%96=
+=Ai>vv1FSc%DB0>Fx8E=Vz~8o=<}bBnS}L1lYsOWxkSgJ$QXv$tLWf+0-;Is$5oW3Qxm^U18dM4S>oz
+%U?J3vH{G#%3zA&!}Cp1dBc_UJoDD=wgDQaup|=xS!d0)Y=|Izmz%k53||L46|fLc2$*chdFFMJ<=Li
+{jfMGDrsPw81}yXT?Q8vGABe79neI>FJA&Z8@U58RqdmQS27Dvc2FNk8S8NB>Hr*V>mhJQm1_Xwel`7
+ieMt)t)+dch}$+BhNn|Ijf!yfYt`J}GerY`%T;Mp^Nwv#e$NB&_4`v%sMU<`W!FI?kNo2D<|H*MWLa>
+KN}TbIpdp3chx81>-bAWd<fq#(I2A`r*KLMdVduOn}BMTJU%0?|xF@K4&-v@P%_Z4dZOZ^JHGGhYFz2
+{85Ly$c+fmPK_PbZ~aI84O@x<NjCwa;)pBZ8sKAeuTd+VXnAYWdl>yyEcIdzo2JvnpW8w)Y}xA<#Hi9
+1=|VMzG~wsi}=Z8LjS&zz)_XR1aKGb{6=no!Jr}jXjhWD3hJ789rLWsVC?r6=}Ez&S2mLq@qU5`z?TU
+u-()5xH?Y-Uzrzz+&s6_T(+~Z$C!1}%;x<|dCj7*-j;Bsz;DA!D0WT6HIHo5%4FVx3zncqDWAfpEHRy
+t-coB|sdBKn`#p$Vtp1mfh(TRn!tCJxB^xHi>f+EX@jSV_$>kJH?K-dGw&DK)4s1`K{fKq~>DCLbTsp
+F!kveM~^pFuZ3a}?ag$>*20CkQb4JK=m3!6wNh`K2zZ*c<Btcs9P9qB<6Lpsa?2_n3YhY5yZ!)kor4j
+U8Gk{tD7m{S%h^tf{L3xBV~SWp)6qoM{ESyJ^*J(XQeN$hz)sGIiJ<Oqm83I|n5cgG>Vj;jdPit*`P-
++?|NKizk=9q4R68iS=Y1Spi>*d~!MK5v5^M8mqYKcmq2-csjLTL9@;OF>C;Wi;R!P)f}m{YjB_(Ht}}
+`Q;6WsiTwD*?ZOR$PPB~thC1=r`xhwBn%kn9Ohs1ZJ>u(3<9%^_8Hy}tkY0MfIu}0$;(H8MFEe{&$nc
+D({xFG>Ehwa{E8BR&O#uA+57VC2haiT)wMhF2=(>kLfCV!g0cN)<s=){slli9ef!LM|q+x|Rc~8x)PQ
+1Lp`CWc^Jb122d-*}UN00HtBw_a=_69!1|Mkp<El=@(N#3nDDyFwWN$|I_wr<pEJelFTolK_i7%ZjqT
+5i=TdVQK@(KMKs;I4}AfLz7bHMqBxwOeCj&%*p!-Gdd8f=n&iG>t)-EMPl;b+$n}fx-5K!q<s?i?lM|
+gC;QgZQbMwS?+^74tYCf957%V;N(%}(8KjG(T$`pF*L>>dNJ_xNc>f{WXe>s1zS_#2B|x6GOH~(^;a^
+_0p7fk=<5{V@7kWa{iz-^;onI^4q-g`cOdYS>BZ5LOLwqyJs2P%>IHo@UTs_3H!coiL+fdbOcRYjoG9
+aTaq{F+d@5o_Z8nAP`o(7_mmU$mN533KQ@|end;zHp955+X*^MM++O7(vXKx{(9f&Lp>#mm#HM_TrXn
+-0_MEtU@pfUb`>LK#{u5Ge;>tSG*0hG8Q5$r~TQw2aY;;z6nC<(w8$MHqyK7@fnsWX1;EwF>FI|$Il0
+P+WdHAmE}wM?x^?CR!TYoBBnJR}N~c!;oriD51Pi{o73Miu0MA}jHVnOKS22c7|J&nJ!DDNQC0edzZT
+_mH<@Pumt`9QX*Uy1qs(Ucv0ZU;E<t3BYH+)o0yCE<u}s&w)MSiD@*M@$|WW3jLjNwBUs9y(H)xT3Mh
+rGx5nM*SA@-R8!Xw`Vr<+m9U*MSc~imcbA9Y4A~#(1QH3&R%EW*tfF1{OXpDSkq|@?plMTg%N5ZZ7`D
+qw3(RE@e1@vG?G$*LxfFjm#eq+PWzdsTn*$$b#I~w20tplaTv%52u9vEXA~VjwV2Lus1A+LG_W>x}D@
+|c;p*VdVV2BbU195SC5BJ-*r=pMl71gE#S^B*Jbg01U5Qiezkv+b4E9+=$qW_I2ug&fh6A#~hckaXy2
+@fv_0}c(_IMT<-f?#)hv$%hgqcvZt4hGO63qn#`DKvw+DVFHHVS`G}(E>)?L0#6jqV5`;qQ}XO?rCJF
+p(vU|B@<`!!9wdn{fA9<YwRQpq*(8O2Y8z2X4u`7b#$OBC4ProLVOsIzrrXM4I1G{@GQliFr1mCrW}}
+yRgTimktl<12}o98ply<EHr!lohEmJ4?nT#7$*=^|9PRn-U`n9o<5xp1rrqdq^W*r^Ymd-Q2%72X!*g
+&p4H6CTgaD4o4AYX;c{ul8(OZ-9MGY3*H|IJ~7(205*PbNuTN@7-ZH8eenXOM5W?QT!$Z`D(4CP|6)j
+#I4(a-(qf$+O4q@q`z)!44I>~W%fu^u)Mahh99NH(7OJHnOqr{c5YWB)aqZ4TrfuuadH4}wDLw^jr-H
+fZ)E>J-hZbBc&!Vg#Oqf_;G07sp==o4`(nSI~V=_-0+_#iAgM;N-isM*>cUj0t5JE?wj5%#b&h>y}*r
+50sWBDdxEVoK1NpOAJ)d<9@!t#<a^Z&+-^tD{x(h6IjVC$B6yuSl~N;Kj8DpGe+U)EsO=OtBcVv(PCS
+_kkztXL3=w@O7sjnyvS60^qr;{v^T`_n|=taIA<+tkSLZ0;H@=RlW_l`z$$Acjr2lsOb<p3Cs+JX8`@
+mnE>Ozu(erE}ZB#zC--7t?Q$eA5us6|Zzy{)WRe&`J_ND>|Msvy=S-~60_DKt}6#SsPXh70}@D%H8OW
+@E*xk7UalUN0sJh1AR`$6-k4>0d?IX^)eJ!=1{ZUchGn-d$G)5O9EdwYr(Gh;Xz;Hi$xg^W#{eh$b!z
+?f)2qOM_L*w+p$APr{Ma&fiot)zL^G^o+#ot$^%7h@n?oGnBFgsP<?+E7Z+0fk0tQI?#zBVeFc5KNC&
+2kNE7hKmBVV5M;<kOVGq;PG$;4>m;7Lk=HT7K6M{xJC{v?8QVtg<sZ7umXXwNT7a5ZJ36MM_SLsPGag
+yA<k?h8dyBsd2hXii4IkSd{lre9gruS>tqOxCZd;z(;eE&;@Pc0=w3-6g(g9sh}Ue9jlgmiN;d04DN1
+r8K$<s)1TqIfVu2Ar{!);Wr@9<e@V4%7JF)YEk0K?Gz7>lyTPBmCl=2DfzaQ~0TjK4We6S;45>M4;`Z
+PIC<?S8^#1$I^_zE{Nh6e82!^EDzZ+TrsEqnf1kIIbsv0oRps=G2LaRN(xRBt@43-B2Y5wPUkqo1{C-
+6LFZUIV#ex@+Yf+l=PSDDs&&dqrQSVJrb$!h-Y=Fb<gYTkw@iS<NN8_>wzwY-FAxomiIkbP3>@O$?36
+&lTz#_-Rf-%O#uYTvFhLs(|H-MF7e4xy=-gD=Xd%UMtoxX3^EZ=%TCtU-aFJ&NUqWlIyOg_Ww&CEWNf
+U(IN*uDxK<?*!k|de(1iX?9gdWLAQK{&Y5xa*vE6|g)I{52w0;R5IuYT=56}&)ywBm&?rl>ow8a;Blx
+=5oJ&&4v8{^H4-9hJ<s9g*2OFZmMGZKh0Dxa5n|gy0P<jd;_HoZH%-McJdN$E<^Pa^uWz$uy+7Pz?68
+c`ak$-0Ae&!!#73;_^ngkS~dx~`+yFxk~+GQW@f+BiH)Sl91ZUxGY5ikaX`+zIMbnLe=qbWw;AB_Uwr
+{ftc-@I=mIIlE}E_+Jez>;hQCE{?qu~$Q;E=SWkg+tF_3)O4z{~TZ|cuT1Xih}E<O-4At8!8UAmq`@0
+b(@u<>eg3m9-8iCCT3{R0!g|uYwTu)tsTHZ@#$|&f_kc9%|bixsrcjZ=PX<V6<J~ZqX56Sn3D`z%dAq
+p5A+Q<y72Vq&*BxuA45UW$0C<?OX95QV8>HBN7ZW#w1b`N4hk=T7i5!_0W6StJsJR56~ImaDFb;8I2G
+hV5eQYoB1EuU%UWc}b|~BeXbrD%>(fTZN)~l9mnAqzrx$?O<&a1@4y)D~vc$RuKMR)!D>>m`_#)d|^a
+>Uk2<+U(i^YIEb(1q2ZyNNHQQ586vM~i#urY3md9Zvq3Jj7iGSq0N$H`}ZbSe#k6oN@(q?rFmwOFxv4
+{!+u3kaa-LbmWS*KuT(VE4Rsz>(IulJl(N6bb8`o2)D_`r{=ZkQ$(5jQHuHJK%Vn8R;FX?AdbxzR->Z
+Ds_|ns0&qBzH8}#Ln4Eg2mhzSbd0<)!h)E%?*)(+#hthCphD?g7_TSEsf$4=vNWi39QvOG=+qCO%-K)
+?vSF2PibaiXy50S(v%6I9%zlTu4IGPN5TP4j>}uN)$R4x20{LgOwA<16c8VUJoAt&Rxj5^?6AmO$D_l
+{mjReq(ly$q2(|%_n4;3vZh~OvIGM+qsEWUs90$gI{K)aQ=#}p-~v6aGRsLo7oI=aC{Z>~`iz!qCpHS
+HfPQYUHP0)ye4Wv>PV8#7b(6OMEZNq~4_(4$<Y3wgS;Ikyfm@sr|YTv4nIrw)okje^q+09+7apY9S0x
+CUr4S4U=1(G1kYpp^ldTj9l8Fl$_OmrJ=OS<d+Pw8h@ENJSM`C?iR+fPC+dKRmXTF4$+flR{{IX#}<P
+wU+Iw&b76;&&7C3njG9Up0&`$LDtHvsq!_jEVOOq))MLVCV#DedL;k4P#Zj9;HQhjq?hAi3{5gK$-UF
+l6}@3`b4-orAXGuIfiTo*5<NFJ9Wt3r{wiDTK<G3hN^)YpFLA$Obcq}-e8Qn=;yc{9GZ5(nh8P>4B&L
+X1iP)6&&kW=#yw!1aRP9|=V1^<w(PeVo2*aVz)?@{+E{5*Oaq^|`WHA4!plBN`^An>ca&axUlt2W^by
+x;@A|gFr)Q8;kGSc}F5D%#WLB5tC4uA!)+&iZ1kVcnUIbmmNi|Gw~o-CWX+r&?%39ub*N>}^DrY%+Am
+NFux*ss{Ye8Eru+O6)3koeIw@FkgGg99)_^%cEg`XEIV$M+AhdSn5X7MQ{cR~4Sj`ba;nYZ(my9g4U|
+koeUvf(&OIlnQ5LyhAUYfs9TfN@+g19BYUFz!uq**<8la&p%^D{Nu>am)JGLB%cDGZ<Xc^mg~b}(mTq
+w_<e?p8Vu|&t@u;`o>XTA_-Mp`+lRPcT2cMIt6Mpa2ZMCh8RoYWEqH0LD6t-P6A#G2sYq<H$Dnv5zB&
+8pyXPmM%8__vuLn96KRPL@w-3cV?Y?l^B*cbjT&HYj$3c`C`J%W>UjOa$otbHqN9B5-1e1bZ)8Qm`cX
+o>!ypRut)F}gp^FXs=;lnh-@l-5;@3P#~YwJUEuf{X7588#%Gm2(WVgfKQ0;}JUCjxUChnNnJ0enwp{
+$}#>Ec1DS2M4|6qHG~deZ2u#aTEPlj1uEFQog?-{3&iVXmU0^7}+af_s5^a7unITm(!nrv)xz?@gbU+
+a=2E#7<JHs`HRa<r8k&-FYEdmgh_FY$_0dsqXuwKRpdNUQ!k!^Q{{t*_`r%>Y!^t5<4aHS8hy(>0Q3b
+*PLLc#3jAUY@+{yvmQvo#RYGU&sFN}WXq=(}WEVe43L5T4;wjQzPf}G5?@q62rj+EfSKdaaei7I}J)c
+beR(BM|qpCKV2t!BX5S{GW>SxfrNV~&kiltM1F+*y~O=g723?HN;#a>LrzTYFa_!3s?<ulzMJXkD(p(
+kpkOV^0W%{qoPEVhOgBsR!wAh510r53(~bZRC(Gg)z6PpT!Ix2z=BczK*7TP#_3tq%=IOJi3GER?sDS
+8a9p=L%hoT-r0iq^FS-1dGIX&do74qx|*NGpg~p9jrb0DQjXH^8kR-Z^DwNGhXLQ$6YjE%t8to>U?G=
+9Lj}$f4V2uMmH**1;({|hi~7ZzR$eS3;Jb3gCV+E`wav$>I8j2mZ5_d^fn;?#%8elhDTNh+nkOi54OG
+DgZ@#w82>jD#^ltq!nYVaK`sS50L?`3fb!INP93LV>x98u6V^z$Js@~Q^n%QYFiDgj`lsUt(TR}iI_X
+7`8CJ^~U-kI2a0g9BlrB-q>C&&gK56KptD9fdbs6uOn<L=O0mTt01|zfJV=~RqkxKyZ$b6jZ<H7W6CL
+FkU490z#<i%3z3LGq~)hF(gO5QR#@BtlmoVrbWYU)ygc~f9uxeUArJQ^;zpP(Uh#lAi&D-LrJAF3$@K
+rnK(wjQ3Jv7gAhjhv%gKzvx)9jZSB>G+*9V-oQH`x&_bKfBmuUAQOO<LFyE<XS%?p0slyL!#&T0f|@g
+j&T7W>JO(q5+6mxkZ@`j;e1LBiNuuHiJ?QJ2hztkyuQkEM2-d@QYql6nL{=n$B_xt3LPJotX%TMhxlE
+N_H0gXT4FCdyS8ThLsoWDClakkJRL01Xu-_n`$`Av0`?5%KN;{Juf-UZ`@&Dw<Gn5Iks)fXBDSnIj@`
+XNhZ6;TU~aFv>Kcvh4O`n^9RKOx1|AaE5<$bE`F{nyuJ`P>ZbS8p)Z5dRUNf`88+~&@T2wg(nZbew9o
+Jx#Zdb@jgEP$xBnuuzD4$<o9yjHPY>yv|@rNz9HTr{|Q@NG5R6R8YS?!NyIfw0fuKURHtOs-4pzD4P#
+E&*ny?IiyD=n#R6Q&5ZaqJe1UPPMIN8BX3+`YU0Q*QU{vtXcO#)U%vxhXOS_Rm^y+jyxbBq2;zm&}>|
+w4o*>^mx8lU<U0*zQ9x3Ac+5LPxYU@{N|Mta9Y*3mO1SOEi{|bq&OIr2lw$N^=APaPkbVt93LM~cZ0B
+Y8bQEa=|Xu>Ki7pH$MC?uViTDyDE-@w4XaH`B7^oeY1HYS9q~w<g9<9}5JjfXe3(S7*A8E>P%5gv_?8
+RQhfGh5V8<W7$Atp(7?95;hCxm2@|314R$y(rsW&Gt=L4b8zD60HnKcqk9|MyDON+0zEjWb$16XSZ5S
+uZ>{39@-DaVoM_R}&H<N<(Y-vPm5&{v!wBXIQwhnLahQ{8$805ey&sdd4$L1D0w!`P^v2l^x6!C~4GH
+2{hhf<bO`J)d`tjzR1I!1EVbo}=Gmiol;&utFG3W*MSmp*uk2<P}q<6>9@}s$%U7%K1gH?7)>8DlP7R
+B+fDF#(fl{52P&SP8~AekV3{(wgZf?)GACei4(5~uaxM_TS!^yp5zGOaIY3sjgae2yB*NwAD_wY!Aso
+CTW9Ha&0-G-OfB198D;XrDV&Jquf=933|{yL&Xe-mdZPB2A=Yo;=p%7`GYFyAX4fX$LR5;Br2*9)-@6
+BdNhRow3*#;`5L)<Dahb`^p5s*6JNq55zS`PM8y_Ght)OKco>i^EAlH>0x4RHpffY$*_+_y!Dl9nwwE
+*0_b|jp_JBD(S-pMGKG4VPQNZ%H+%vorMrq9!sZ<rV}fAB;hP&R9FTCyXGXVzE0fCBf?*=6bC$5^}08
+9QHr4SMXie(^K3fWujGh-zTkVx(mFv^P^w@0vi~Db)%kVVsXOv{lYTN>9Z*Djb8U`T#sQP}y=#;cGpZ
+X9}EXoym10_{w6B!j`R_ku%7FRWoY7;(@ji+tplMhHR|8*ojWXI@i9%F8JxL>W!V$9=SAah{`&<J9UQ
+%MoG}o==pY#5^<*)%!()UJMh%^xW&%kyUk7yf$u3bs~612z(@T7XdgK3a(UG)<H&7DmJO#cBjdr_NS!
+t4MjX7}kR;!8Cg>k8i06A#TF4D+jqme3@#DIG30;5Zws^3^cVArXUQc&kZ?+u5=+pg!JU*Y9bK@G=%{
+AyPY}40?f>SKJhe=a0gW2ht#(XPcJb>rb*y;+j)9^CIAlmcRm>Y-sER;$_TuiZi2xb%CP79n>d^M%(8
+AT)x{bh8nb{s(ndPW82j&AXPSCOCm?vXqd&@9X?67_md%x)#G+ikSh^Nzm8f^^;-(d<5S{rwK)ZX}+;
+A8EM<qpJX;9AlV7;#236%Y86M<}na`(N_Qp=LL&$NOib4!9lb8*5{rM2KBx2n{QoB#wj)H=WqEKm)7-
+csXIP&_SYWrxbiPL2WvNf;Np5o5_4@}kR)+NhE>{KBlP?0a>DWd;j$dO+!m1q{xbL6ChO}QzsJ!L4@b
+8)7Mr8U<}c5%pvlY?kCo7QRdoC5fG(98_pT7z+NQar!+(wZ3&GNF)BbI$b9`=a&LwM4s@;9H)ONeYLF
+5}A^^u|-kouu0%%p+uXgtt7`%Y$Cc=m<?Urj#(R}38$OJho^w(AWNdx+I69JHT$z^cmB#F>S+YszcQ^
+>sQKsm}&nbw$amY#2Rp4kS|`Any$X4Nk*Qbku0exo$2-=7A*5i7zx}2VYk6LK~UR#h$S<$-R2h%wrE@
+eyOQS>cN{RGUOPuk!?Wt$K1yuuA=WTc46(oT00;24gy-Gt0+6$GZ0%Kbais<EUGK6ycFYJan&cUzh`#
+99$XD!*PdFO1#|*3gUNSO<T{){{Wf5jw5l%%n1RtvzBtl~!00d(wdTMDN5x#&i9NE8&CD)zOGb9OJ+8
+ni$nhsfi4dZqyWoJiV<#2L2#wS!c^GdrG@+a|8R{TMX>kiPh`TguyT<1#xcXtIFeLi8%IyWMobr$T+0
+t$oNED`?oH7+!89bt$32pW29aJrfX3gcScq%~+%xcHyF!M7>lCcDzO9U?~=vv@Dq+sL!9;HMZpBR?Px
+Bn$|lCN3ZUY49X(elxX>;I1G2o-^2&is?~hT~r1-J-meJef79-T^`S3{{_d!z=9v%tnZJw&Odb?>p1^
+UBdJ^t8QwfJuMww<Y3P=Ky!WN=1{OE;(4-6PMk~8ij8fZj6VJE$GN<Dye!(IMs8~Lm@m*h;+xlw+PW^
+U3mww3y5SLQ&Q_jqssjGgH%<V9{qX$F-^BTgS3lw+;q?x>?yjt<GM{eR=8F+EAV*%B916%epfsKv1nz
+Bg0GcAE?*eK5F40ha#DIL!nxoag1|@MjBFEbztAxU(AZ9Uh#|X6naW17juLIOrbyrw53gp;ikAYr~!S
+o0EKmP2=pZ;jY#=WX(pqBJtK2G#dfi~Er<Vr{MJ=tuzOr6@Kn4QPF;PIMVzEA@*^Or#jbG27=G}oGD_
+|9TCF@IVEhr^XdDFAF&6~D?aQ?_K%)5GS=-jkubJYrK$Yx2XU6ZkGzIRQQl{xgjYjHxa<cHp?>@6FFY
+W1k5Bna+gi1y69aXiBA^hCaJy715)?q{XLrfBw-CJ~%@C+i%oMH~7YS{7qNUr8s5*G|%vDemVlDEkWu
+`T-pC=3HOTK;ICZ7JxU;BZu3(|a!{ODwK?7o=BCxHuxBjDGVCQqKhfRcDR_*hLOt#TT{0ccoJ~-gJAp
+jZb-6TcdgMsBf$$F+_)=8+<~U)td)-6qKha-3c#8*Okr!j{uOKA$c`sC7v*imi<sG-h3J}}wo$&7o7d
+J(QP5To{OgA^jVgC%k@N7IT?OjR{bL-5}vB<2IcyelxkJ*8K62Ns~xJTo0G^)hid5xvTC^(~++FEi4i
+mu2ETk3F#Jpr-nY#N?ro0-Gw_F}vAs?N7`v%yuZh7>U9$1Yj}Sp7wN;r#&jUKQLrw^R%YBKmvP-be10
+Y0-&@=t4GFpy-3|I`k1(k3I=#Lwy#FZmssFKM2R|{%Dj=^}qIBz~MD>29vRKCvH1JQ_~-?*53k9d|aT
+vBiI0sol$NFbyD^&2|Q7{`<}kNRh0OdV1IqO{ZQ%j;ZmnmN6V#PjKremTmi@lt36RoB9`xiKP*}pqCi
+K^O*7`}U8fEmhuvCLXWD@yH}HpN=k&`V8`=x*2s5l7<v&e?spZh1CGSz%2{_2omA_guc+mpW2Gv!K-j
+*Pj0u8SKm!rM?@qJ)p&-yry4jlwI2YrK?;(2E~4B<!j&zLcd1b#$^du7gd=wTgre|)&uRRxZpd+O<pI
+wjOCM9K*4C<=FgJ}97Lhd>k39l~`7_l2VI(+g(jb{FT!Ss0M^x8$I_&|>O6^;UIk_^?VgTurC^b;lpN
+8Xi1&c!bn$F_Q-6O+~k;BhT3wdPk9X8wb;n3@7Q@T|ZNAPFOvP-0NmN9Wrt>(4$cg*QcE=hkH6lIqH<
+46m)5ByY*}023ImO5PeFU!*~RVQq6t!kgX4J>%>Mb>d3@R))W$kh0TAPZIyQ3>Bh%FX;<M^b|nF*kFD
+tPDz^ey;sKRh!-}l|ki8XLABC}r84QNFzhY~&GV5Mcch@9+bR||_aWxqAK^0izl~zMoedX0h%_gj&qH
+DYP)*n6w2=MIYkfuiQM;SHYd{*^_ptz+}bluRZXmstA_!d0QMOT(4%s9B`gF#W|oGwb>nnu1U34<)l0
+z+k*a4yTxC8Oc?8n5dk?%LK}WG=#2<WbSp6afUMr@tkr1l28F#am_<XbLFazX2c{HCdSD=DTC?<gP9@
+LdLnqn0#4MNCo-?K)}@HLQ5>c#k2vf*lpch`_jPJvhOE>oWnn`V-|DrA<!$my*qvaWBjBwzI(V;de6C
+@d#uqt*5}THBW!l!*C3Yb*6Wi7u{HbH00SL5N@0@dMn&1*XsW;_$07u6R(xy1X!W(o{Jn^`b<}uyG|F
+`vRsR`-E0`esSlU0PW$?_M9{bRB+C@%HzyOF-piV(k{L4LS^ugxKLVF(!KOX7aQ`G9vckk-v-Mf(@a(
+m;FxeTTf6Veb9Oeu8kO)s`4+!B?ra&CO-iO=}*+|A=?ir4U(Uz!=c6QZ{2$J~{m6N~V!CkXcmqx<j_@
+B0Sp&Sf1h@#I1A@ck4oK5v5&`?@MLdH!%}uMP-NF@YjG4f}U7+$$d*iU8Th!8u_HK^Ze`X1h)MfEk^x
+vR$$0NT028>&Tp_{lktO9k4L5r^`o@_5S(I`?5pU6xBCsdoCIs|7qk+QFp5J3NEw)fqBmB27}v_%(Jt
+h%$a#(tdK?B(O<{Vr>+#Ay(UM`ron8O%Aheuci(6GxazbZ^~y=UK?Y{Vsuhu>3?ro^{}Re!4k~^_q{!
+4*T?$tMd1JfmH<H@)Cch`U=>ZKhocI|;jfl%0QheC&CDKdo;mje#`zpim6TP-PbQJOspY0S^S+3K;!&
+_?zL?dliJY+ReT;@^EtDiBSVU2cQ{;*?_%*Q=J89W<~={Ei!P)h>@6aWAK2mtey7Dw4?P5ssZ008L(0
+00pH003}la4%(eV`Xr3X>V?GE^v9JludKfFc60C{uMjEv>cKjz>UxrIKX5&0~|9OYaQ<(OJ-LJ<liIt
+gDB93&fuHneV=!~<hri!e>OA(_D&%x83>*Dc<6i#Fktfy4^-E6)r#+7w`*gFLc1MsKX?g%d-XO3+D-3
+ORW%k>Vavn4h?m>n^jEJdNKayIPzeSefR=CBU{QW>8qiwI=M!RKoeA&Ak)S;Tz(iqp04~HO^t0pt!AA
+ES_>MIQ?_)my<imrH&fNZ0!r9^xIrsQSM-C87p@s&X^8xk*ZKgo*pb4SvDMbo>k1W)a3#I+z`B)#=(i
+TN&D(}Tf9G0qc5csBvqG|G?x$+BVg*JJBWk>5Wiw$Y`LP_A=8!Lq7G@FxoqRXXw=m>hR0phR<eq}Ha9
+48X^Ho4Cm2&{k;dc^R;zsv*8Ti^HS{$+b)2@x&1BczfMnn{&i@(9NMm*AQD+}`|Akxc2jxufhB{wjA4
+P!4hIX6djMI<k+tiEPas%ZsgUG<RuMCnMKdZbBa2KBLl#2{pmXsdRgf+I?j0F*Mz^MJcy?nthj)a6}t
+P!FYEQV1%qMZ*U4=^R*+gc_|(Cbj@Xs&qa|PZL(s0d4Dwy`3FN2_8P8;osc8x7f?$B1QY-O00;o{l@>
+>WU|OI>F#rJPx&QzS0001RX>c!TZe(S6E^v9pegAvgHj?-6{wrAdvXUB!iM`vqJ>R;$Y?@sCylI*zPT
+PB*tWQTvkj0Hesw5Rh`|WRk=Q9I<00_#?Cht@3T`H5n02mAg^9_T+VDP%!HF=@xq*CjwQH!K0lUMs!7
+n39}7V3kl2ZO=UvMM*pY_{AryGqSwNxs>ZRg+}bb-CU(Y9@al9qIS$^5!NlZp@$an_M8w%k^5#o4hP)
+1ER<_YSHYrYh_C3jjEcmT-WABy|+J0`>V3$H~B_2`>nE{wq>1v*k;YjytvJ(g1S>5NiVkfHl4j!#iFc
+IDw%G|_o_~J)q0&@n{I8hs#Zx>C-Qp6f8W1)rGD9|x^V#LH%=Gk<%T+GYUJB&v8YsCw*_ck))r5rqlB
+J|VmV3X*?grIvu#yw2~+z?Qk2yuTjzgKGhlO))Y~;7@k8T&0X`-S<H_r%-(CFlZ1(KG-#mMHdGW)`%S
+m$i<EvLcyngfSTj%XK=t;Uz+e*zdOrPo1YlYD-E{a$A_V+&vn!{?E80<ias)}YtZ`*?E!(44WvXJ3^o
+7YX1U+?hgW!XG0cg13o{E_hZld5WZ`D9(GY_Y#6>Lw%7qK_BFdm8ZK=ao|H=lNPOMi|(SMZMbsxRS4v
+BWHw*^0ru)*}}B{-IL4Nn-_!`n!Z_63i=ZKi@)e)TXj<|R69>tI_<YxG-N(Ws|mlG(e$*%wrcyk)32K
+B#)7e3o#E{y_<L5ASv98)-Kb`^rDeOMdG1iBfwgKj>#v9gCx$Eu-uB%Mkz`%&s<{IA{6m+xx7qw%hN(
+;63${CpgMZBQ*De6lTx5PPFJC;Xs<JvdI-1RB>WHVDCvS((L9flzvS_MuJ)9)N<))d<oAqp!6^pf+ZH
+NJ7K;C@4TPPHG#hp~oK~~l9ir^h5Z?bo)NVq#sSM;wM*2$VkDJhppt>(nSlCsKaWOXvSRO*Z{)NIclJ
+-W`DbiIFc@=LY9{69~}N0-l@{`mUY?1$GE|8?>55*=hhI!<WmH6<~#NwO;IMiw12Kztqz0InWPlEIhq
+&v>Fg@Zallo~=<Y_VMKO-_pnU-=p8hKA)YPJUTfIKP`#QO>-~HLeU2x0t-)7)NQ>2Q=*m^{BAU3PBEK
+}sn~p-)peqm5U=j1gzS7lo585O{xk9G@!1i#v`|ah1o9$pX0uVP*2Iel6DToBG!aY^3=cr4QlcLeO?6
+4?j{fC;HY9fL6V*unckGuojqu>)r(a<9PVMRA$ZdM!wKVY|jol$u#Od@9oncgpcE2JS{2`93zdrt=`u
+imL>xum5l>RgRdXO%eq(>G?h>(N5n%HVy|5jPO>g^vsjw}+qdh2%L%4VgiZe^**TT?~Tk3ux1+2(qYC
+B!Gnv!qV`kd!`D^;w{v2Vm7Np9Ac92=?Z)^!Dox*w2F6uLG#XXX)%0L1#^A2k;~zA!=1FoCfkGi4U}2
+*Ez_&m|N&k@^>may`xS0VnedshPHgQI0`#2ZMMX!J7Wk}vnG$lw=4;8NH%@5%IjoF->@BV!8(<O_zV5
+tMrB0<v6j~1e3huI-p_Iw18oeAny-qSHmZbHDshwY9fTG7%`gneNpihUAZy^`7H<+7xm!HRwvfoGoRC
+4gP4SMpp}W}2J<;tMm(|iXB#V0DVUi!}YIzHpK~+VzJ|B%I`qi!=#*r^`wZO~1ir8Rn<>%SDRukt7Np
+&Rq6IxWQKX&0X)vkv0fqkn!$0ozPpM-~cMslHoP&ePpSbBj_1K@GGx-do}M&9iCr9(u7wx@(|!e!<YK
+-yB#8BuYz2>_iXPZCdzNQ7c&%jeB8O@6FJ%h46QeiHd%Nr{re@&b!UmJX6f$#q_2)xLd0@>~H}^={oj
+{0+hJ#0s^vRADH?{LYsKJD^zfGgwbqP9oPEQgc{r8g-C6@K?#0)FJ{$q<O+pUtZCeX;s~p)gsBNn;ll
+<Su*It8R~uv{cqzI?Z?R<clmnZP;Z)CENO%ma6*f#Y6vxXvr7I!v`t!Cwok5=10&;|d!0ad5#2L?uP`
+kc2~``FEU3%#rmXg9a#<4n?X5E7BKa7wfm=c#{Zb!t7H@ZiEa2YiKMNzEq)t`CAV_}CuT@3Fw%+qhs_
+MOTXUbjAZ&nR;9s^d%pJ^dbHytq7goDMp?H`eBv2RvDKH~vD7!z6Yk2GyNz-+HYBIL^}`;nOuy(aQ;5
+bYBMjTvuK!8V#04gWC=I5D0=-Ew_u!q*5#csuTtC%|Q$qChGb9T^A_;CKvhU8*bWIJ821lGufA*+)jp
+0|{9BhhHdUH-a~BS{2k2lJc)f%qQeav1Ucu_9~-U%|vUYDW#RmQu2b<`9`hE+dycQkG7p!HKVZ4KKG$
+Q!3&y4j~$H|$V6aytLob_xmCiKNpQ`UiV0izdv?86<HYcAA~rB$wcyoLf8+Pa-z;>ueH+237&C<xATP
+hX*QK9VIqjIJQ8PlhsBllcgW|uUQlYbEJ3@C4^sU{sbniTZxwETn^-w&PUOzF*kEfg#xaqdjF2Aiy`^
+gOdAS(ZM9v?;DECN5DK9lb5P5A1*^qb#gP;7bV)R9}_<ir;@d20q^bn>nR`EJL%#CLDryt?!omua0DP
+1$I_<`In)ofl=)`-m3s33E2`WC!pT|Aiu9fmUz}z+23hv;`=xMT5z2h&BjU#9u82_3EanG((<e1yg6W
+bCk?u`hNuqCFWjJ-)>zRrmbQl@-A&Vt;tE#{QE3lW7d516pBUwl)7`nlFz-mzblw7e)Z0KOR>{^A?8~
+xKMPSa>5=fKrbcS)rCLX_^a$5^-)&8cl}XFz1xrCIebT2G7^0toO(MC}64$L75*}G*fRgY|SwmU4B5G
+};(7#-O7d*uaUT(b0ym@g+lLp)`CYYom`T5D~mzXJ<|Lr;>sgfF|Inqjm1B;N86UwM50@4Z$S|ii8$+
+OOiGxCD9pr`W{F@sUCw@$(>IkKs<5e6jT+P`7s<{$lHgVamwIT59T@~w?wJz@h}qf*V7x<aja2HSiKj
+!k>&GQZhj4Y5FF%p{uKX+bw4)mlJ=w?~(kFTl8N6;Zwj{PjB7l#AUO!g}(GHV+ZQf$RgY?Al7>g}MmB
+b4{|xyH+F27K;v@YV9zbn`8Rl^Nauf{@EGznz~o7%H4VadL!BNW?f!0GlX<XUs>pu5=2Mqy$6LvFxq&
+C*fqu=v)9mefDNtK2uka%nu9w4!^(rvS$FTV^-je)i+y&2#i6^?RMY~^B?%QXHqwsrRHY)czwii)wW0
+%S-KQ$EzcD>UG^B4^hVp`YtifXcTV=I~gT~3hY#L-noZ&h~QiDo-A1sz6;u%XcS4#(7?KkEs@k{yi5|
+wpR>@`|=uC>hbpR^4$`CM!%t)-zZD|lJ-0vXv~6WTeqG_)BhSfZ)>TDSduDV{vD!t#RXg-y-%AQ{2^K
+nhYlW-JZBR!ADaZ!zsyC>haHUV*kinsqW_2oqEoN1NsEzAmH{0$<AC?iwaad}Z#3-#65!LR3m<FVbmz
+@QpKdyR`yWj4D=#@K3-E0sh?e{79it5wl*%^kxTA#qSZ7CT2@p^1RWge*5?eTyY5Z+#@(<hF#8x;TkQ
+8e)~;9TIyC|q<M8bMG|Aki*_>4j{NS|Oekx{0Sz|L{W^W1@@Q}-sG)y74HTcwhU23ULw+l8PqkpWBMK
+|THxS)n_~XJ)aYXr*jAySqMBN30iM*!O5~B{MY`d)DMVzJ$SlSBKE#39j!W7b;zQG()RnYGp-7!x~*`
+~=qmn0B~HxnSo@m5LxSRS4q^Bg2r3#!E<ARg*@uomw@`<)(yR%YQiT+TIu8_TtD%6Dq7x7~Tfr0p`O=
+et6zgF9_Uyio-kk3{|gG+5eNiHxHg9J?Px&l|RF3e(cv<o%N+MG{6T$iZr<D#$uHl-RcZg@}QW-`KPS
+e{!uw%$l9VFjgCf8a98gZnPeYK^$RGEfkif144?ph(Afi(#CFmQA29y^J%Jq3p5zt@=V;{sdi+~L>%1
+^F}id7+te6b%?4vXzAPJcmarPjf?%BsXqo`ExyBF3L?lt>^Oo(u2#rxmBF>Mz25PpLx3YqDf7b{i7Wt
+B}&NfVFQzC=~AXxTfjAbO`S}fhNbi=YgHBUl=(@T=G9NU6RP6i>xZUb+oB5~dG^?TBgrA@~JdY)Xoyn
+OTI#fxX(&VG9K8nGMxBB5=MAfMQGydt}hXmQSX+WgwbtdffTITl>ui(nk@a4vH_inOuRwL1GJDEii=5
+VaUZScLQ=4CRPx4uc1^k>ay9fI|0CBnJ9y!Y9(?qEFf%&_G1WmgSWWk_Wo8%NQ2$j3ZMp9o_b=fw40?
+#N>c>roSi@hh)YfvvZJc36tu%?my;t`lHb`pb_MAC^Pm;5BvLLC6+Z}chm^25W{FjEHCrAA18YKk$7J
+;vbtcFYUuwxMZ$6sW)%2Ai@R2|a26R`{*~DA7v&scl=ig!FAKw#Z~w0+>ns$tLOX(3_m_rQv!%b32sa
+cXF+C?nw=0OFU>Jyxh>q9zS%oCM`G_FRm*}Z;=h{Kr*=_ui6_n9hoxiCZ9m<_FUU*#JHOm=Ik@e_k?8
+R~Nt)h))1J?yiZ$Jv<Hr~846AnWvSy4+gotm$*BCj_V!X2gnnis2FIAkpSLkNav5||hZffG0*FVMPg4
+tM)an}Pue%dH>AgT2alVw+V}?rv77;J{jC)nZ;Q)S@R2Xac&sRix`>HJj&1EzdC`QmM<r*@cc>8$?+M
+K0Vi4V%+7aN0#2Su~{^TE_xWVx{gC${B)&Acc#@<D?OjeMk9%TTh$?J6<E+bzBu>$fp=K7otIQ_@MXk
+k1E)q_?36FuH`Tsd^f>uhC9pH1w~JCRwNRLaWC8r}&P|NGQr7q>-kAs}TBcl4T)CveLd`3!t>!fSkal
+Vzo)T^(6Ihw%+nN3u-!Js=Pp+b036cE<DmO^NK;fvm6p9`zgX5&Edh6Rt#{2sb1|_ziTL7t`cr@5G%i
+j;WHM(yK)!dfVyAW0Sb$rY|!SrLy!An>rL{I<N!kQm9M2S62eu8w(zVjYZbu72nB|B@G<t7>>P*}|?j
+T#z38G$9%<hd+V5WQaW*$o^sjSU&ByCmB#)|1F$J&7&Wlm5ku$1d0qt36nd6&MU1tvX|Nd5;NN?gA5Y
+Ep~iut(C3c0E!Ntmn;Vz6{g%B!@3W9l#taZYc#9%G=lC^M)y6)|I|#u{mW~yG>Cb*Yjo^l7}5mYlubh
+na7C(x)!$k|)7t<Kp4x>n69DpePnO%Pbymweo*;t9eSg&7r$%%7ZJ9&zv&yC2ys^q0kMXT*GG=Ql%j<
+Jqv^hu68*;QN56>?Y#1tgUm9#nEfkiSA)zmg&yTiaI#%?%PU7MB)54j=+Z&*vmxdH^FNJk((HHoWYXt
+!o!cYASxvVqix2lZNU55yzL@iUAzrxnOK#>2o0H2Y|QlP<K1IO1jlQ!#J1u%zqK#Bo%%(1CTgP@eYXj
+-zpEs4nx+v_0T%7__19;`QS&oBeIJ*HJyg=~Rz*%A<Tx4@0YWDCynrW7mG;cqLgBM$mif&2ntqUPv+r
+5BNIFYlbHaqcv<VajJk39QeuFjxbxX)@Ft*!k=Qb21e~loZSqXM9gs;_SKrPIVefwZ-GHmMZI0@<{U8
+4_Q_L3JX7`N9V&14+ni0&uy^_VgJ8g1ZH~Z*y^rnC1}~=daljOw6WwmI5BX-d5hF>HU+3$**~^5xVUv
+?qa1%p&07@Ymll>y3SP%?ELano!)@$-)O%fXf^hScOzz#It;A;v1u}+C26>Nl%LjY_8$^oU3YJfcE03
+M-oL45!zH({uH)^$)9oN;=i*o)hh#d<$RGyrw2XqU{(Dh+oNumQq<is6eb{820#OQs0_K1`@YjLXu;c
+HYB@3stdSc4ebRF4?DuX{o&MBI8PH$-76ja7A&C;OAw9h`jY46NSSzXW{<zLxK1?2E)rjmH@;+{_*4M
+7hr1aqw#8@aJ!;ucre>_1<!!?gW-_c5($9SJc{+|(AyU9qZ^gz@E+zccro&O)-hUfB`vWhhE!9MTrjy
+>j)8t0fa#LN9j;3}gXgr8guTOpn>?sw|6#ioyumx#z-oPfZ@0avvPGXKe6WW)Et7)1RLn_ku%wRo5|+
+oaPb298_NeBy_2{oC_jk69dBivg<~J6moZQDh8kseox$g3iS>yl*^EQrDCbTXgO+32k>2TO?q_YqO!e
+ZW%?DX9c45I<{ZzCIk>*5Q>(kf=A{*|+lGJ+6~6Bq;gcTku(c&l<G6J3{k;nr*i%9gmf?4fQLiWB>a#
+nTDbG4-ENgHAKpuCvPcP2A`^m<z==sDl`0UhlMN5Dfm70~^KH%yCjIa5@2nUT`+|xs@1PHW3v>JOl<{
+*ri5AorwRL><9uh@Y@G%x6qbr2NeBr2ON)>g7_aVB!DU2kvX<bGx+O{cA{Ymt~*)-v#9iY#tSke>M)+
+Et2zdl^>E`3p&ghX=Bi}D!>&Y<H=5;rkoUiuBnpIwNNmB51O7$K5MPwPpCMEavgCBiotgp`jbu6nx~D
+!}bHjxCbw1CruP#bn{L&RCXn79Kz>zY&ON2q0I6WFIe2Wl}V9S(PSfThusf5F5Mc>l|TY*MTvYjM9^p
+m}shXT$z6ErgO8gN!`mfhtL;koyL(XN9-0K6&=O^*-7;}`*|&k}EK@LmLy6SAT;%!Rt%-He70T77mT{
+t{7Xxoehdl}p;;&xY|0h@FMrOpOEH?MH!F{h3DXRSPu;x1V1^vvWKPEl85c%OXd7hTe6(GsP*wTV?0<
+x@1ICw`ZHI0g;)rzWU855?((ET6K?wGWO1C55SM8I2_hCXOLX3YMElYbr?<;?kPT*cescgU|CD_Dy#+
+K(e@8?cpw}KA5NFU19Tn+a83gDj<#Z~ZtLxdodYK#&lZ6cT>C`UD<5=;=Sh*R_kU@RV8{t^G{QuMUuj
+3KW^J?05)ktOd&4j>aXpr|=*)4;dhZi##mva=E@;^9M^Z(c=fpS3Nq6GRFJeBCJ{&-OXEei3ye11$XF
+L<#(nNt`I(QpJt2s<%{rV;d&t?iJVjb?lx*7)@*OwFSMz$|bbSE=k`jCR4Y4=kKNoM3aV#gnY-xu~Y+
+oz${4NHpzP()*_cr1i{TV9xqGq{hye0wgAk0Q|8(vHw=mD66=?5(ptR4zvS$W0zbS?j}qBR%vW9HJn{
+H_m{aNM^0Y(3^=-gncdMO|`dbG>tKOo|*AUAX>aU`>-XZR}0*>+UvGqIRYguQjz4EL?V^7vZ1j*_O_{
+ysM-qOXL}v-63c{w;s<h}L{%AA+w&D=PY<Lqrhx=;oILl$%229=Ax{oi!?<AL##@WL-)wN?3+>q|smQ
+P0H6~juC=G(S4RDj=y&UtE!Q8@rk>py<Ggw@$;X4A34?)Qx3@mF{7Z)6?LLJ;~S)vsa<-~ZJ+1XI57_
+d&dlvGbl)Gc%zh)camS6V1_8=MvaC&9<Ec5jJp+CwdZ6n3P*OF*EA*fL()6@oo(VxtksDAmVW>ZaW4l
+VWW0b-NC6;C&9yLwC|!c<i}El&}WF0Ufu+#GH9rRMY}{tTH}d%XYJI*Gu>o?>`vi$gcW99}To5cGYFO
+?jiRBqC)(PQmnYq2jZul*l`_o<o7BW?XO5&QAY<mPM*M@Jb|;23ULsXV2$G~6!B{oq%>W7;Z1lx#4*R
+i@-a*$y6pGLGh^}cC0<tJ9NFx8-*3hO7m;I+*<!&&U_-!c7u?t}Xh0}sH)#)A1W_r@qgG9KiXV4rBA&
+-3I@^XJ#x=2U{5JG1TDlXi88dVC|78$1Ye!Gi;rVO34*qzh;Jja)?i@3Z-5+A%f*wWi$x>~E#?p_p-#
+N=30s8jwRU7F_M0H1`E1tt*8lK^^Aa=RU4v>r<kTQ;(A@FJLw$rAZ^3IGg4CBzu#Mka=sved7{)BR$$
+oJ1M)M)4XMmh?yVx|3>WZm&6j~P##2;QDXSh|lQn{lR49ZHM<jp|Nw5^rH0h=})3IXJ?^-6JMJk6n(*
+Su{#Oe~Zcobp)Hk5o^M>FFn|Uq<KJpi0@*NI92aEiC$pvxxa<x;YQHo^uU9tRxe3cyy#tlS=|#tJ?E-
+>3IE`^(ZMAg!ae8gN$RMa^Vl`p|7+fQdc0hG%36|`B)bBhV&<VE2w2l83jtGm5d6U*NL+RtJ0gguf1|
+k*U+q!uPr=w7`5ZjV^^DNRigUtT@bZ}u(61OpmhVfQ{RynlJteQ*Nmv4;ttSsy<?CtmvusgLlwHx0ed
+GjXt`9a|JJIzYV~M!9_pUbDW^EJ1h9aD*mowdyr?>2?m+M4udUAFZjh7u1#NG#o%=AJPhty(sK4C$R$
+uh~;$;n4q*M6*>X#|}kV~&zg^Sju54^e$yl=kvo$#zAv*k)k7gUUS&j>7t>!!aB+K!QliuI756e%!X)
+%fs|}pKZw|E9UvV%#65C52xwIpM^aPJ?J*XK<rM|k2SHeGkqrsIcpu3dPCAX$Hz!$*^-Y<v#E0;MvC@
+uvknu0xrL;RrWN{ie2#n0P|SF{ddJ8x=M`-z6m)Gh1&)fE1fTbLv+vDLCebW<WUxXWigG$me#&lk$fm
+W0#;+3#a2(f@R~KoT(k_x$Vrs$A)1bCa9B`d+e>deKU$Pt1$pOOMa{H4!Joloh;sHI8Se_t`=dfTnBt
+4OZKm|*egwR}PwM-s6HF~0jf@ATBwK0CPAm33pKF@Wvww?>WqudEM@DDE-9;XPC<Hp$cwme^v^LMsGo
+gM*HG>+{~_2V_{5Qe4$v6z*HVY^5sJchf({1%s7JY1`-_-{i+#f4=KO5Nqea>@Sx=4wp~l)Xjbt(u7^
+2f};1B3<{xyL`KKE#pCk65jX~P9fB?*W;WKL=v5T`Vms!F*W+$!O(>3`sNXCI*J5{)JSJxa+aBwPK;M
+)ntNQww=1Mm@LKJJoSJ03Ua53jZby$hl;*mj(Z>Vv@9s+!O$lA2h$|XTe7(1YgI@G6d9MfFmp$l#Yx?
+gy>yy+2oylb!1WD@FM{ca0juVXJ8|&LMTbx9bg!LV*?@e3P3HCS7-Af$LfCO*tc>^&&kZ3DW7mOL+ws
+s()4(ItmG!{wdIxkzFmcX4O4BuDv#1o%XJeUz?lO}}!K7xze4O_AA1PGpkbKYu6eJK_{VJaltE^o9`@
+6Z$!W+Y6|82emyJQJwSY0C)a0U)8y_&F!>Q2Mf7GVY&wpJ8=VQ_i;5_c*J-Un<2HHz12eHJonuqqirg
+zrFfX`pw(N(|^DEX8bS1&Qa?_Ih|a_Zrp=v-6mubS3UB6z~_n!9g3;CvGEi*b}&io3y1gP2=zqsZne%
+gd6BWEx!AF6->CTJLZE82GQ$rJ5Sh3^BGEm?Wq<uyN?V$bvHX_1_hlC2Wk$W4EvxK?v)4wa8vXX?u~V
+WqF^nE|yt)=J3~C;7OU_hxLKB<`w{Zz>`flV4?$7z^23Iou#4@$Lw;NtG=#}fl16&B9@B6(pZ3hi5-g
+gio-yH-HgPO7YqZ6h&2!O7Y)!w=E4pju%Ru6?0U1}*yAUvr5L&vDOEd{be0No)qX8=(gx})gKqCHe}r
+%&HqWG3YV&7OOKhnrfqRmMFh;@#^2U-DvEvS1jL^b3AyY?%-3r-<uFL!7u(%oQ`Eh$4YB%#qcp2{oh=
+!R%0u&|$k6_6hu9OX!X!pV<91_wGS_yOnP3+Ds!hfKOcGXWC#$y|{0O2a-B`&=Nf#Vl)wu;wNjMfZ01
+@KFc6pmXfp_{)sJeDl{ewh73XX10%%&sCy4*lg-Qe!^z`;;ry&-y5kt8f62Fz6laHN!N5i8g^!|!!<d
+VvDZt}rt>J;h^VHzc=gDKKa1Mywy<^P<j>$@+Aw@>rGjcupQD5+_WSsFrZ<8<14`>uj3N}vmkScLcBE
++Kyds~&bK0)6E!*+oY)&lIJ_o2hXIJjpDp-<?)NYH4<9FSZqlMM=aKsZL|<L1tZwnM~lvrkr;9AV!g6
+tQTG=N9|jALZ(SpC!2#%n%WHY-ZaPYxuLY$e;Y27mM<ilNfxT%{@4&=|BV&Qa74ht63tkLgs!rR?@Dy
+TO?AJ*`hXC7$bm?C@W2VC^+Y>IeTDJ%}7F?LPImh(UuYW|FOA!!Q>ZXzO(xv))hp()NOXIyjV#lFCsX
+as7-8yBw}lbo>z#c6Jbf$@aFQtv~DPplfjfX@~lBB9Hx{(`_0_R6kWBw1lRus59?zlZ<U*S@ir&&yHj
+Ju6Rj?c_ah-NpP#Suc+dcz@j<oNL1UnH_g0B#B0AXHwv{xNEz)*XpWbY(+s={Baewm181F&$w)5!4Z3
+nV8ek^Cg)2~CDXJ8ls4h~MWCtMSLzaq~ynMouZ#hAF#keU7ix_y5}%N}OfO1*+h&*6ecb4#770<QQil
+0V6<ExdV@?`C>jeBf}R!#QE=t9L3n{kO-Dy@c8W*)s2v$-d~7EbcpDtt@8aCO=jb(^zx6k6vpm1*)M%
+rIFufIxonm73hQ3!oy4X(v9%xs)-*t^iSFSBSRNKjiXp$SG<qO&acr|Dlm(O{py@xvVHnEBkui-w{BJ
+*$tLP#GT|HyG;mXwv-7m6TrevEummE377jEJ?gBPm1Es#tBzqE3C(33R(ZBC!dG4L)k8~@Cb^6MM7o*
+&Jw!1U#m~cOO5+w>+uV#2V5d;ECy}^l#NoN*2^L$`l^;Hv*M6CkLrowa_bm^xobZcln97}YVNDf<eLo
+$(*2lkkdYqU*eO1n2nB5OCQ^)XKU>+Knr5<zJ+_H&)JEl~w2cT<(S?dYWIacYYq*VMjFZ3Dl(`Yd|BK
+BM69?7YxX0e7p>zKKl^m}vW&z1{Q7HMCiLhL*v2SaoNwu`y4f9nL!MTGLzi-ox+SeE;GVd~L0(Pn+oE
+jyd<j4qPP&$eo?gHG6woqIO98g4N8{uhhBc2N=E0rhj=n{r6dV^>93G|N7$LlwJ&BluR!SQy2^euXb{
+8i=8S7lh`#9`XQ-K^+k93k~`D(8K;ld$xxvsvJ9L&OK(zI$mR4jJ#ni!!;zZC8SmfpzzM0=B`ybQ%2Y
+V3bH^DqtaDZKaazvBY);6FqQvpu^zk~s*_FFmB0p-=(U#||^LI-6IX%^fhuZ7aO2R+h!+D4z^jGQ*2G
+b#<&`i`K!k&<y=#$QJ<uu~^vV2V(sJ)93|25pgol(Ok%qv+iTEq(YguhnW&|n#}nN(J3q|bKQbj;}1y
+vy;NcCxwx*OF*vW;*V2n#UcOZPB;wPss{08cv7fGvQ%6{KSXl?RO+@n_Dx1>;d+Xfz;#K_s;>%Ij6a{
++?&`I&`YKGx|`8(hS0U?&;a*DL489OV{U^})JV6iF!4H}d6q1)S`Nn|*(;8TGsCwyv-Yir$zVFL(}yp
+Pl56UAS&EN{w}=gF3k%A8>$n$lqAx2oe5rF!>1$jpQj1@&P>IBpWLR_h#4~;P@%4*C*M-83sgPBVgUe
+PbTM)kVZJOq&c#u*sNTX|wFStO9_J)NV4G?Wd$7JBRjfUOwM^m=zkI+RW&D~S!;S?8DvQ=1r^|v=Y4$
+k|@`G2zwe2w#)Je9a|sWsA--}+#$x`EJx`3m)#fka>cUUx%v#vn!B4t2HRl@|`VOmzp*C;8=nwi%Jl&
+FX2y0O11i<u}ujbMwRh%(<6{H7k(PGwllw?^F%$!?rhZ_^-{}oOdRaKO0y7xuG7H@`$tcUNLRj)ECe&
+<p=lA*ll@^&;z|Uze5UMo!DPeawYDau)cfn6YoQ{9k}OJsCVCcDoAF<!o{?63ksZKqv0`exf9&<;@T}
+FyBc`bPfsrsNrtV1!gSgfWA_Bl*=;2u^|jnJ?4*+B3<~)+eLt@e$cYL+&Ff34+ZIC(O1XoJRzzDsXW{
+O8=+S-6K3!%h#l;>-MVKe*cUOQse0Ub|?gZPTNCy~}&X{RqB~4E7xT8LLqNn4;0j0Qf6~7!uZ%3YQr(
+2M)cAYJtpJH%NUO)Zr;-_Z^U5dH~24rhC)Nl@1`dlsuPP48hZq0pv*zPCS3t54;?mvrzr{5Zf>mZy)V
+}#&bSaipgGjhLK-`D=&3-jDB&dEG&EPzfc(w_TXChoS;lZ~O%`kkg!Jl+O&uyjAt@q}6|_rO~`|D~G7
+8J&d71{)wrX>b{_e3xR1NWhRRQ0nZ_Bdj*fXgDV?9}m`dE9b%4=8;*DV(*?La#4_Mmy_g|ovQXEVe4>
+$F;VTGI^&Dm=406tN9`?vd}-9M*XHkE!=zi#cQm4nQ+$;eR$Lksv(q2*fnz@%sgdrrU<RWK0mI1(o6&
+&Oi<pmXi9^h^^txdcdTgu@9S)Lr5EjO+yPvxEF0?6KhwYq46nvkOgdR*1PJ6rd1Uq#jv4T<RBHV1-8!
+w@5cUL7k(|Kv+)4xVXJ$S+y_XcOlz_OMeSHj?FMuale_a~#qCi^g@1#$kbj~_?s{QcnhAR11}(gWoB8
+Sc*8)uZl;LM8&cWt+Vv$*#-yimy~kkes|WImR(job(oR9WmBHII?M|u@ly!3G<!l3_DG}vo}&^*3F!C
+V7Wo`C11J@GE086ra$p>WXX5Wo_uTWG3AuDNJ`ZKhmN<l$9DxTEyl#Li6Gxd8Wb@zFP(AZqUdzMm7Ko
+49g9xxAehXS4ZlLRa1-#(w_Ewf#$aFM3DdO1)Hih5`jBIE(C^Uit{3558=FIY|J)IW&#(dYZ#suJnqH
+fpj|f~k!16_R1dLp(R`OrWSX`v{C)IT$pZnIF4wo?(>ZV9b&LTSmT>2Qq`4QEC<(*1I&pbs(%A#fso)
++qiZvmkV<9CmLCpk7weA<B!C268(u>5GFAVJbfGdFO~|MvHhrmawXZBTztds-UIZCyF%foUIbnAlu%*
+S=QY&~htx&Hb9e+C=`0W)O#ZdHJHh?-C-3+?&X35AOpfhtXjYa>ETw1~>M0+&MCgEIDrYtk+>Yup=Gc
+XAIH{Z(dF(2{_ETUUsb+jz`uJ9kqf00zY^a>b=@N(QS7XQM`_8q5S3C;}VWbLV>+Fc?0;!nCL#Bieze
+!3k|~H)kCA{=m@SF;K!zt^vF}_@*!jV)#*(6m?CS({+8igQ5Tg818FcKsE9rs9=6j`pZ2$phPWUaY@9
+EO?w%ynX8}3AySbJCdy?6Uz6r`_HS)jM_4iGTVunN4Y2nE?V0wa4aq3k{M5Q{&2zRF((S}L;+m~<5qN
+O}A(>;fnvzXzcS{lLL&Um)Xjprz|#}E#(Zf+naw~u45;(U=bsL>#O^avFPoelGY^bnG?^vs%6ag#UoW
+2*bn(#BP=QnEk+j|N@Zh1-m{3QiYu%X|l>iI|Gs<1?~0pNVs)Pc%nlU3X@qwI%do8>b3wet8G(wZ6sv
+qfw2Dy{KMQYB?CY9_=`0rFEEL_?JbP)Q_^oSi!rQX<*vxpr<{|;<FlkR+6u=FlSc8$8_VxzOy)8%vU(
+7-jA~^920UcaIOAf<H36zL+ljoQLV&CizIyqysJ_Di)$KMnQ+4gQ;3=;b|0Ud`4E3U;v)2QFfa{reh3
+yPB9Ia%@!mUlOGr=>yz9%CNVL^g@Z6dUu23=x3N8Vsxyb!Yg^k*XCN{GP(OPz;@2d)4eogXSTou+=NH
+~RSE^T5boc@uR-P|hGMT;4a7k^{>-c7n286NUB_M4tu;;Iy>4F^LN2L>+tCOUPs%{Xo*ija>SL2dH{o
+0J36#<}_7xYOISs3tAux<02}S06OS&TEvh;rNa<Z9{8Ccpy-6aL&Ikb8S*Q;%AV9`qP)bMftrL0;hMG
+IeqG#f$kKzOI6~D8>EKy$S=9YGG+Lj7)Reu5KZ`)goWh}D3}8z1~9gyoB{lXtHxK({TUygk6?bf_&>=
+2@x<T83E60t?Cml~Fr|y#)0c1mN8&90<u?x}kIw$%{G0zwj~~qb^ryc~uT02Ay9}IhN>lztEJVTI6KA
+Z&){$A34F9=ymExMN<c%0-CQOydkp5S%5Lmz45_wo?jSf+m$X!LWemIW&w#v;NX%9{xCnGtXiH*M`b~
+nX6K5p)S2at2+Mk6>XvpL9boW?Gvl{;oNp3-`E?N8=9+gvX)9mS9gA3S()`eDfM7bD;6JvJsl?at;2v
+aak_3Gp4%JcDh7wRTBct`zl9+YTNxIqG2hEcx-}Q$m>>SEy;&p0gPxzc^1WiluQ$c}|Ky(GzMALhDdj
+kXG5;nG!gBd;F(fgn#>oU&z@w%um0-$Gch0R@ychmDPsGGsL~qA+#eim`PawK9XNk+4^oc7E#o;-xWO
+k!wU%#`&wdn*Gspm`o+=+6S6->Rso%xQ&D2VBo~)xUzW{<$z9{*Hx49M=qe1V6=S6{h58jb^F>c**3G
+ZbnLqUO<<~&WA9@gT^J^gH>z=;+8i@J22QjOxUiB_Rd<<7_S9vpT>JZ2*wC&J~S+7B-ZN}6$7l3q=4|
+Vbmv5SD)3Tyj~6>_Vce+E-*JBb8mj)rVYt4wAJSlQYGl$sg&{WNFz?d?=u6p3}J2g`Th!-jlx!OD>TN
+w6oqVELAS`vBGGysM?tD~I#jGu}NxK<0B~+O=>W%zZCi9%zKac<o{H1E@e6DSa)ZJ@6g^{WL>nFFl3D
+cZF2wai{C{w2ez-T=$rMMASW|@8_ep|MQ!xFUP}vG&(>!mO?K;K4{LwzII0#kAHQ>?wJB2fs`j^gv`F
+ek+n64BNFyb{zm_tp8gL1`R(udKc`>)EB^D<$?4xVi?7D#2(Dft)yl)M_{)2-VOe4?7Ic>mj=q27?kG
+>=alHq6HjDA$|6koZ`KazeqlsBSb=AJ*9*cl8FTOD^dS|OW8xk=t+TSKc4s7wh`Aq8%_RNSln~h~-BB
+{>#F48t$L%iOSh>b(uB|=s^!xu*RkLN^erFu`ur|r!g1r9`Tc<rQ6=SVO{(vx{gzYo14Z=8Xf%i-D^s
+3jtsZwCExlkLB3F~f&%u6N;u;EALX!su}V$GbrtDK&MnG|ZW#5!w|Cb0R?9c7Q#3bz!dv@;*kr0j_QE
+`~dcxa=nMDeOPcXIB(((h1QEOX#+|lrX(M<AC;&l(Jf-Gg@w_}c}-&PNf)*|p*T;Mv(|51>wu9rv6=4
+r=7D~F8G6#DD~L^;GfBxQkxblT#mm81m|-KHud{r^kubYP*3+=INx@<#?4mIVTEPX2JB*Ef^47VM`EJ
+IHd8$7C7##?Qp_E0PF9b2@ao5J}UUKigkB2SZub&g=;uKmez<acZK@RB7RgEjYKwB>Refh&1XF>N3L#
+}sd#}H>T_hKzL`yD}l%Pr;RyWF4t?gj2$#-G<SK3RQ801R`x9DtP}P$8Rl<R6!HTeEK6ayW!WN_O%hq
+3Sz04m7A_q;R^5<~6IXv-O&Fn0(oC@5Zk_$gWA~rHTW6^`?Y+vGm96fJ4CTjb70%#^Su`XZJquHp-hm
+;~?rR8sgoZ3Fj@7`_a*H^2FYzL?fxpffYIX1Bw`!6d#f6B>3c-cY^ddk*wHkt*dRE+=lI}Sb`YTD!+l
+Qd(=srZmt2R+Av2x=OZdQ22dX?S~NRSr*T-(`8MQaQYHxiLiO}-lACe&7iL=z5}c#Ch0+K=2dBN$)j-
+=$)%57-V#(>LV6=u=nDDsQ85O}DYb94D3CI2G@QiSCHvBu6(1&zIwxjn)`q*7<b|BJOYQ<?>u`tDLDr
+R49FBPX4_7Z7di#pWi5lxeA48ipo2koUCzF6t><D6eQ=erO<%;wM^Yu)-F<PpvbO9v)8+Y51eaq^h*9
+P|j$P%^&oNg>;%w&^22z{JNb&3&Fa{ARPms-opPU+)%b#IF7c))ejdzI_E;@;u=JMu42?=sq!->mj2L
+RJNsbv75jS?w;6|Ci-3@6P$+UUVFR-<hN(^k2yG}%Onqz6VEk2JVR1?>Wz+Q@p>dI^=Hvb2}cE+!f)H
+VdKY;;&#HxlX9~L@s^qq4zrx~*GgW?t)o5;K_nOt`-Z->ccP|Lbh~orLC;RRl*Sp5Ab(N{mqYKP94(4
+^PVUJ$S{jd7(&YiC^5}QB~UcI*_wc5lc^tiGIID?C7aW$eqPMswlW@fZq9|}B89K4D41GF5ZADm@I7M
+w%PpV+Yoq>t^9IYL^Me?d3_oVX|*&XEOcW=YS=;Wcl&pgTJ0c5@QLL8SFvt+d<_??AVM!apQ}tmaz;o
+c>IwML8vyQ^~`I2zhLO^Gql8b-Z|-4a)H8B@K{Nbm!)bVe*RafJ@{K6auUF6j{^QI90YtY3$$X7HIeU
+S-RDV^R&Cp-|3)8jQ-MGZ|v@S@)8@KIaS}1rn%Z!cD<+B7X8GrH)1bxkEI+nms9yiM`uvFkp4;~2ACY
+R=KIere7zf_Zh70LRA{^4ZZ%#F-8U(3Q|(B`=QbUMX)C$3>3%Za^>`w&OBvFIRArU}eqWb-g)oPfV?X
++TP)h>@6aWAK2mtey7Dr3@xpO=U008tE000mG003}la4&3cV_|e@Z*FrgaCyC0U2o&K6@B-wU_B4Ar;
+d!p0)0pk6xoSqC)msf##zi3=|DqEbj*z`YDp@NyV(8hJ@=B5B`29}i=ugnO!8jdd+rAhIUbL%OfJ1Pl
+@q&-vP$ffD2=%jYhy)js<pm#GodTzWmyX6oAr7ol+VZG@n~($R%F?_@r_kkCiJ#8)(g3Erfj^*!tc>2
+j^8R@OXu33ruUWB(r?<IPJ7jT@7*ZuQ)q1Nb?L-mD%f{4pXO$}l~s|pM#uH>=S(bLy;;0nWIrtaz7*n
+xsLU@i?osQSq+v`e^y%{5#rsdo!}P!eS=WWOuyFK3ygC=(HC0h6(IGl|b)J3q<GYKiMfTzR`pr_DiJO
+rSwCROfX<5yGTs4(%=HIKz8$+<BJU^b}bM9O@&z1FVp34*Z&}$9N3LaWNo{gfa*Qzj9&Og3cT=j(OJ3
+aTMJ1AJOaYNDfwW^jsE*Hm}cB^Z(9j^XvdGUmW!*#E%Qdl>?u=3VaugiUuXq=swz^0tLjkM}unl;9sR
+PM;HH)g9=R_%nT#QWt>kCc!B1|}UD@tJ~g?!DU9UU(yDu~$O&0^~OcF!DT?m7t77YG9#Nzck8uWILud
+@}6W$Bo)+|wWy7AdIe!qDJM7`?xoeHag>PI&86aU9ko`*i?yx_T8!OPh4F;SN0Bza(T*&8N3+g!<`il
+zo05&*VMyI8+ZiPw0_mov*^T7jj9x(5I|Ju1c42nNZ>!|?l<e4>M(niP&=e{|uGnoTk&QQn@+zkc6;O
+n7+t%e?G)_U?Q-6<*DGV@}l=@CV5L+b#R*B7vJplFjMitNKYE`5h{FGbHWI7EXMN3qneK5AvjL<W>7&
+k1XeVzF7Wms(}HUjv|mvr=KqEZijGI`)0_kie&<3iQzwkf6R0Xg6%57X(46;8R@&+EF>q0GgFwt&-Et
+=tI3^f7yNjt-MuTz<q>u|=eS*Jil{G#P$R#^@zuQgyMoT3j!(x91;!faKf7a(Vv!0&+=`{9|-YAgncF
+hyQgcb5#g#(yKi+BML6EjGF=>2&fvHqlHVXZv3dXG#!oJS9#2VTIP51mX*3D&y{oQrUdY@Lt+zN7PaN
+b7vvJsccn&!Dv>;em^4)hInFbyw&p&nCS#}R2-c52Tz<%g-o3uOTD&`d%kIIZlc4N`1W$?x;y(2cJ^?
+VO2C=g^3bL>)+e&)}3~7f+w|aUS`T|ZP`u~Xp7z%<X2#O#WaimITOAi|kG|(!FJ8Wz-;Af+&!;UlYyk
+k>VvC|Nz&NOM2%|x<GrUGGUov4G|^w$lg?C#{rjF?75Y*SUby3NqYAak#+Z)B;e`^gayAJHppC9Z9wD
+4EfIkP1<l;;aj5ZwL9Acr8nZ?gs2E8V9<|Kp%8$q?6v;3ECa}LD7|2ZPi*oB-4X4_OLIHsS=WqNW)U`
+r!%4f2R-AJbSml@&w4AL(K+f3u*DIVgC|*KvfI+EWcde}pzMJFB)AJ!CiZcrA8m=aVRFRYz17wl>p0I
+!bE|XGb+90<_uN_d<RQGVE{a#?B-nOByPR~pAm=&rco|J)TT@V@=%pckRdTB)?QhU5`ahxNX~uPtWz&
+#?9R@snoXh<U5X!F@l}y{{Kc9FEU#nj`uFv8A&&84>mt{zZjM^i6cZgYxSAbcq*K}l|&9p1&P^Ou@u@
+>}6)YecZOZzDs+1sP-BNI*WpVUpFmx0vPyP}_E_(|*PU84)a9^Z;Sr_;3FWn>8)`6@tRduQ{Q=a0hCB
+|U<HWdL0vvrk((b@|iV#i_VcswO3rYx%C=46DpeOmG4sCy8=ML97R4&{6`Hup>C;?fwA4^L7O1pnEdy
+4B;An$Q8KM`{jbt{-EiFxP&RF-$5L9#@<oVcMX#o2aXP$@0bT%NERDgb8)Z=7~rJ!lWv5w;2Mg9Cw$U
+?HNqN|H(XZK;+cpMkE@93Rkv8UA`wI(9e28>bYyd2#9Nue6Y-PybFX6i9V%jpbgGrbp=cFuGxtnr0@4
+n=6m0+o5CIZ%0&wolyx#kbspczP&C@h8vs0uit)y<rr%KT<Tl3qR0CJJr!e(0K_FFKXJ2!eVMqRyklk
+66!2Q#uNK?>wKeD|Ck+}lVY9q2}cf)s-qAC0&dv_G+1cdMdY$bwH<saL2GrZC(c&sa;!WM_GKy$Dmoc
+BLL_GzKoO<acD!aXA7fsQ+p$EOV=C?_wH~0I53`>CAd0@j|Q`T^6SC)HR$)pdum2KY=xw9g%SJx3*VM
+(2qD@FtOLY{tcUHi|Ih%k8BPSEE15<=uG4m7kCmbvN{P1N_8vqedbVCh}RL~2hE|cLo}Ka#iIJBj)cz
+Q$2-irxg+z!M88@6NBI12u%Y4q2AuT&g-+YWjm?=07|KP_yT`&uE?02xs*tugw5YX(9_ptpGj~zhdFi
+O#v9{mmS9(P)e|}|d-JDg?4$MNSF~2euI+#jv6JeQqt#-<`Y0tqarH9z|=JYS0#TkHw89OQ!z8-ebyE
+*+P&d--jmAajiCrTt4x|qaP(?i*H?;!)Q17SWwGenAf<2Fj%EnckpT4`Vs^Zs+>7u~y1=5YDi(%r#%K
+DTGgCC->e&cp_7{fsVnGePeW;d5{rb$=Ro;>uM7(WhCa>_1qwuV-w)@VYU4x}vX^hg{&>>QzH`%#cMv
+9;p5L+o+9SYs(=5xZ7Hpv6s@`63D-f9rs24Jw6pT<C7DRKJxw~U*C>D{}#D=TgT6+3CjjU2?3d$iVoD
+HHEB~9FmH0CEtW+A6@51+y#+jvw6JcNG%Z<!C=;_RKKEs2_46c&P$x(roVi||5z}l>gLm(*L8P4WOdq
+)SAfYJm%u)CTjf>GqAe%%mSxKbuVIfj1Y72D+zCjF#GBfTSUsH?5lHgibvef@nnW=ev(i($}uy{|eO-
+UiB!k|mGG0sbRm*N)=GL3F02g~BEM5nQRww{E6HZ$n^5Kf(TgreogDLZr){9<wXigQ#0nB#vuM_+O78
+J`G)J4Me3{U_EQ^JLd<umoMCy9R1B<A_|AfR$moYntsi_=j+{%Jo`<%RoOrg{8<{;Wnlzi>}08(nHmr
+>DkLp319i?Eo7ool@g8f@(%3>h~%nJ^e$rPeQSl=uc#ZVXg{P>R6y+!Bhyqcr<(zRzG0w*FJ-xtd-sy
+;5_0WTn<I~r`YXNknF_>mCo6vIqpdC7$Js<3ZZ%yXUvT_Erb?Y_&wP|$&<F?Q1{;5CDAQITaU!@HyJw
+pTNzdGb;0uKlY(i1y{!lp6<9M|d%*!G;X9EOgEkeWL>9lK7e{?tMWk<465{q2&`PoC5S06aVU>ttrWm
+Bl+XqZlk<FP<)Dd$KVqTm3>&FQzFhwBdKMgKn0)zv!GNW3=Csc|~AH25`+evQBG7yXgG$0yIDYrwJI>
+CHE%N2X5)?<Mj0`4>=20|XQR000O8^OY7y?@^De^#uR`OA!D75dZ)HaA|NaaAj~bGBtEzXLBxad97G&
+Z`(Ey{;pra*$70TvgX8D+8Hy3uIRRWS%bFN3S@>#%e2ExqBN39Vif)FyQ4&zvfQ94W*~{om%HbldsRl
+G(QjOG#SAA*uE~9={vr3m+>oo+SLFQc2lA>cDrUq=<ihO8d$!fdXfz5osw_yFZfa92o~A?;RjCYND_!
+Qb;c4?d2-<bU-ILyF_ju2g6mqMBz^L6kAn@5A%1XHr+kii0yfWm{uD@2QRC7Ycq*82KFe1yWyyMDV9h
+0k_xhW<2iJT?ReQ9f<O>IQ3lTI#S+EX$(hrfd$E%ignRCbdB17rb4BpI+Fa!yr9lV?#%|6cEZ!$KGZL
+9pf<vgIaatCd0?avC+p0dH?P@FfXu@jC&muMuR<3oZ??4Co~*xMkQHupu{0Gh-A5v@r>*UA0TWVhu-A
+Ava~%%EnK%Ls<Yctg6ti@*E_<&7{hi*_5hCv|-9<^e_!?*&RE6|91C<`(y#TSuVn3w?wB^^JDv2yRRA
+&nhlRI{EEg6`8wdJC7`3J6&QxeZ7C!zSVgr_u|@8abv-{@M!t)jSwoFwtw?7y`h<k{Le9=Ya6&F*{g6
+JqxJWO~Lx*8ZK79B9ND2tZeI$`Juee-uneBX}{YXptP!h%AOU7xKptdyhRwnI=XM?UQj9$K`t*LtPM=
+5y>$$_!E_?3Y>h7m||z(7iHue(nSBsT#JgtP3%kxX>OGqDjEsnQDh==6L{0=qD;m|}&ypy$4Dqaee(t
+r*wL4JVe?>(OjBipgkpHd@AAynuehpOE)>PACxMpxlc*Co8B_TGs^vE**O7ub01ECeBRzcW>H)TrY#x
+Pk**xNKu1q5VsX~TsBotaWvPUWQ18MzTpbI$6Y!)ONh`G=04859jnW&7VH)bS`0&Rx<4}}?^-Ju*jHg
+?C~JTxBwTHanv<S%x6o0|5h00Ua?9yJ$>}mClT-Zh(%c7|unOao4pN6&;BliNiZE;1ii)XxP~Mt7wi4
+PSx~u8d6D906G&D8W#(eXb-Z4ePcW=Ob5ZJbWv_=1oE%;cgxvlz_;CD{Or_<->on+TU5`3VePt(t%1f
+z=?%A=UPt)&qK|EoC_4FI7J1Lxdk5&NKh>J`hZvW}Se1rsg4p3bLBG#kqsdFF0b&@M4uBk;kp<7b8hX
+>Mrb_b2!hIFIUjvk?#IQdmuz{KgfosizpR+GPl%9?Ariga^L1dK0!@Q}0fL4-3p0LiAji@CSn-p_`(D
+nM|#SE(q)?7dR3K45N-hS(>)+cr?McHfZ)$u4l_(hgmZP)i&(gG47JRU>UUL$>}t?m<VZjP9Q_}i$6}
+9+T6Ph0bx<}ntEbaDSnN5rva4R>Lb7p_JWxB*%1?#nVRKkYp1=KY*krTnnnWw-eGR@#amboP6H5MI>Z
+GH#q7le_JOL(4t;%8N$0B#GvN@>V=CGv_arUWBTv+WrhKD1FlzRd+CU3P|8_li<y+{3r-qchr45uP0j
+Q<GvdxZhYnllQs$cr)^mBS5(i0uJI_QP$k#Owda2R~H#q8S)*g%_p0Cn!Lqxg3i-su@}{On%8GZO7Mf
+#w4_74@<9hf4PuH^l`~*>F2otz~H1T&F*O_n5i;d>e{CJn6OTSG9tnpqoo#lC=vjuPzB))gm`;Y|5ae
+?<^@ep)}3hW6rV#JqP)Ms@g|jH`|!{5{=!6;~1pji3Z;zi`bmwuGK@?hkgiJVk^uQ^Twjzp~tR-WJ+)
+t($*yyj+MOD+|EJm5CZ0TEq$oD+O?WJosHV2P4qR7q4`?lzqiXVS=IQW_-`#>3bS<e$NqWOBt>TxQo|
+(-gZKv9sf`{*d7K{)WXR+$jCvhPU|X6V_9J*3-okL%b2SplapuibsrAI+%-YxJoFB-bcJ92zQ52bO=E
+cG6-4Em^Ilb=yXWMZPZ1H~qB5VEqKk<DS74g%l{tBs&nAyH`bwYxmdra*+&*I=kDmpljHWSU@T>FOgU
+e!Db{sT};0|XQR000O8^OY7y+pKr?eE<LeoB#j-6951JaA|Nab97;Jb#q^1Z)9b2E^v80i$My)Fc3xe
+bBY{6TemK16ERq4B+10(pwNO~iNxgo7R2t}f1fwyG%Jy*mYPQq^_<@+7j=Jp(yNwHejC7Z-y84MUTwf
+QM{c1lFrJZaD1XyHv@UQr7>t4x<VcKqIa;_n6D_fq1KUB1wr;I<fFzeL^e5xjqG{m^P)h>@6aWAK2mt
+ey7DuA+%h!}7008QB000aC003}la4&agWo2wGaCyBwX>;7hvETJ8Cd;T6u$CYtA1}q2R(wziE0(1a<=
+82T55_KLmk6=I1_xK`#Q**EH3yDe(aw7hRT6Q`^z`)f^mO<1^iC#|M_27;Q&x>ymX*qix=HdpD^@BgQ
+Z;Y0JjI9RR;$VOR_lDC=2?+cJGDs`cgad)wRO@&k0t<WS(R%Q$IG^9D;>uwTNC7DUYB{>=$M}$J(Bl}
+a=poN4Gr2i^}V^(RaKPcVc8aorY!T?ytqy3+dP|_$Glvv08jH+*6uFaB+adPthBkSZ`&rb?Lw~`d%v@
+_8@=8v0Yme=O{xN#sULAq=Si(U`^5CJt@1iq>UjRyC%Raaspdj@on$#6QPsLKMPJjCK-pxQD8AQ4T2@
+h=y*Fj9nhKh_d}XNx<y?id+!lG6q^2}>GH%KkkeI2qz`?<LS3$pD=uMNAMQy5F7WYY>r9a@PzJN{^$y
+(3U_eIV8p!th~ke7?310c=n28me5_2O1z<=2<5<8Lm$y?A>Ozx?veUoYOoFD}2m`0mS>7Z!{~SZ_Xg_
+Gyy<F>MP3%oGS7DYzEE9C6*wH&waNbv;uW@B`jTvudW+$(@d==atR@w|hWgk!+Cp(ztHYvTbGrrCK+Y
+)?u%uNVvwdm=&omni$>=2L}5J{1(-VD%&&`$t!w%33yb4I<}{GE5NTV+X{LxI^@y1yw?`yqAZr#Y9lQ
+2h-O`^-#6-9O{48?K7I7)Zx?T_E?<2YfBov^>o4D4e)a7Id<>q>)M*Gcuv@@u=Vn$!E8Toc?}9kSQI2
+CQw#bvZRy4?8Coq6PIsb=VG~pSM45ss!KumbA%B3PCY7!AVxs9Z#Bb@hCBh^co`&p4SaU9e-U(UEbj}
+?oF7&_NfWiE|KLtj@qX|zhX8LkyQvL()d7RlNHJ5z4~tEwy;L=8og)aX^{HhHGLB)a1$zmHzfx3Bq=;
+-0IfRC%_}8Vd+75_cI4&s-y~0II2?K^$oqX<_b>0#R&{hT#xd1XFPX!nK9r4eaMoWlPsX1;p0syRy(d
+aNRBe%<c;}5CkfXiJ&qVAl2_?Mr;!wc%x8h7}cU%twV-hcMP~Ow0eyIs}XB(T__r3)u0E;u^s(=!}ug
+1WGWb%5*8d7of>Y0=&9k!tRS{T7{W7O=|uw6#Q}oS3H*EMGjLMu6i`;xIHzZ7s}%^B`z+OdYXk^d<QV
+{v*3i(57Dad{aFW(-4lHw<G#-vy{FayH9kNQh5fO$UZnLHq{C<EbRpdLILK+vDTU=7$37WG!h3o(iJ6
+P!M0l^$Pl+AOk?sZjz3??{5yPm@%3_{6xKv&YTn34!-k~J`Hqdkz@thqI<NqL}6Q8K@`MfT5DN9rP4M
+JhTm%lkT8-8PIxTK){u7w4Rr{Mejk#i4Nu1A$8ns#yLsI*p<T3qZF3ODx6}WGrRs<7iA%knt%HPeGSq
+NvDAT>}%j95fB<grw~Z7Tt$5VCMdktX$DJ|P<mW)xM{{y8h8aT-;s3it07Bq4ZJh5hS-MsyuHm9M9>;
+Yo9Bkg#A%V`X1Y1TLEk0?$(WTcbVUNMK&1g+A-h5kpf3XDPVZ)JJWb7iv~?r!Dt#<GuD*ZsE%bzW9>&
+2Nu@a{Mywhu%Cs=q{C95?ob42~E7HGmYI9HN6ESpfYtVOT^DzL~_u;`tuYno<&bd1ad1!Dv6hRVXAPz
+hAeX!a{04saOBGHTL(_P;neWE+c;%?9`&n4C|df0S7fTz3J_9Hg5tw1^@-&(-8eDp_!$8HWo}&4|+Qk
+?2qJqP|U@J^O?h5QdUTo7FyDk;Q6Q9`d9TShZfXRo3iKfNWqY<0^qt3P64O*$lY6Vb}@0LXE0|uP|?*
+@n!?x0+uFiE#Puca2TczBlVYZ3w&PDprzSz33Igo1ww`jRUd$x^BU;BBV!6U>>{GR04-IaDCFTpiUGf
+$8x=a@o(Dp`h;`Q>45M59KFwCTZh~-xiwpQ5$g%Xp-q$4zIegqyI7f*K$(De3BnX;v1Kqp_wNm)btGg
+v}y)`1IY6~P3)&l*nQxafNLocqlOO`viZ`WtfZf4TY>t|=5xQ9>9K64MBo!#7+eq9zdE&wvcQLk4<-K
+DACS#m%Lr4^hg%x&WnGMs>;^#TMW^cQq5=ojNDi>%QDh(o<)kPWPQGE*NJo_#b}%Lgm)!L|3y%u1YTP
+7L;!eT3wfB*}NE5Y4@;W}F@b4ogF)vSqSpK3Ku;!~VjHq$rCFECiM`=Uq)ZB4Z4{1Rauq)(`7mz#@Z*
+hrHud!{r*N+2o?s)Ip!X-DX3B_k``@33Y&#60E)-eNtf0Kd2Y+0$A-FAhrmw@tU>D99AT#5FkV@&(@x
+Lb#+lysPfdxqaz@zBQmcM&w%cti|<}teD`*y?9<zCF5h?x;#d#pU6R$>3`d%!u&&WmyfO6k(8cCF<Q?
+!%_iPsd3|S|Es}tH%(!(($whIc!Eh}2>cOQp{fJWBX4SIb&QeTmhLK>qLu)vx-SoeyPq*~mb=w@-aDM
+u2soCTd`W8%0NG_@d{d87%$?z}Zn4$cMBQVoEGy;00~h6f28WRjUz{`&Oh`sq#R3n(`izD17od<%3Bs
+5TX7B04>PTeVifuQUo)DglaezADeByb?}fDFkVHS7$%#^QY?b&(*V2r|yfUA7Eu*d0nia1tL3PPfDO&
+s|vjh5Oiqlgj;VRvmfaTLxRtz^hgUatkQ)bIIZT>5GTgc5tXn+qbfoxFR)05-qZsg<8obgk=CA^;~m!
+wI|Xi|c7yaD`*yfpUW3v4XL6oT)svyK$ysZ+kzKX%F^p181*YgCDV|QlsH&SP+XU0|X=m62u4e~9#ub
+T1;%QOe2dsLn0iIi1>Rla)vJVWsEP<oc=jvlepaQ4zzkYFa1~~xTnNL|(fvwvwJB6_X-qzDIHJuJBPC
+Kh9j;TWnXfsDk?W2#1-1K0Xy^q;(!~E;<9#~eOG#&kpr!{lQ(eF;M*hy2K@N3^9MUKZ#qgxPkp;|3&l
+VSxHhvCR0_2Z8}sy7#3zk2fxs~Tb+6dE)L(2eF=F)O5ofM$MXHA3}qJjl7+r7j;XvkDzm1ulpDop=i0
+1YM>R;bJR9VA6rofvpAesiDZUCl)=zzew_0cRuR(T2^9Y33ytxO#)V{(JApQ=tI~^xCN_(-oyM3!f<a
+d5EtZl1N;fEc%XjjjR8DK>P41Kx<D$PH69f_3Ji$4JwQ|eu?1aU2Nt(*e;I7reXbnm06)T7O|t|v6~G
+R(a`A!(6>!jh2hU%Bj@^X$^o6<qB6{*X436;;U%yN7IeZ=t2Bv?#Be&%-w)T4fKEu}jcJbybPz$WkhS
+eYj)ocSs2gp#FNVF26S8CKLW2OMA%KCj%fktO^W4pqmbqz*Oo2P13vTsA0h&mw29l0#?luQzQf{~(08
+BC4=<4HWRni?6h*n+3>^f1K>w5|opeV@`1y$le5BO7#q%cAy9chRaUTM&$h2e8BWxsP8@L$M6hSzQf2
+GeQ>l>W9BvTzoqXiMM*%vgLMmUpe(?SOhPb4>VUrhc%LXoE2D4*Vv5-j`ML=AIl(|;R>!N;|<~E^Q`y
+GbDmiTW^xP~QMKDZ$5Brf^{Z=iIHxBJ$Doi0`d`1yaE%Zd`WB7vOdL$c2;Y@0DlVWHfT0XVHSLg~NF_
+94#zSo@)U~Rb{WAh@t&>8$UgTC=Cp!gX$3KWA$YjzZ)#Z1WB--nU96w2eTR;t&&xlS_8q*)?4hv<Hn!
+PfZ2N`XgL9?J%ZE#&dUaeBH``KbA!_c-Qu(-XRmSPD-I?Be83s#N<*uZQLd1LSMkzd2q65%Amp7<u*S
+7`FC@Wd&7sI|TePQ?|l*Z>axPDS)4*?vvWytyTKEjA>1mzw}#n95~Qm$`OI!K%&N13JPt=-7#Cu=qi#
+2&Pg&7V+9&lpL%yTQmXGBzM<Hop2v?XnhPaBh`c2DcjMWq`eu|lNo~BI&31xG{z!bEB#L=VrS^1sm^I
+)&D3qV*5~LEn~`MZ+wn=d*~!8|O889i9-LIp02S0Q7_JlJm+ZJT{#NUsl*!6490~YYWU0T^*WDZ7Zc#
+t*Kj{YkC*5G1!y;vFERfJ*@u2kxFZd|iDvZ(fRH{sGMzqS2(trV%=B9C-;1XiGBERAr&zcoq^SQ;x$X
+_GmKqU!TK{1a;p1lDbyh>H7n*{eb#nD7uL2QyrIc0q7v>x*JMY+kex9P+yt?U-0plYC_-)ssX2vJEL0
+C{HX#oOZ#%y#C{?^tp)yA66a@?B_w8E>@%hr0RBd!M$Ii4hRk2C$XIGWWc$$z*q^*4kJsev8-EMfWvx
+JGs?_d@uzBCE3oHf|OpBvM9{`t{VQbv!<tC_qL4#)&=Q)WZgG$0MP=qhS)szeF#+E6a_>ZUD=}3Y*mz
+%o(`987ZqVtLuA8%nUDN^3p3~c*=D*CBiyn&?ev1tH*Z$~_W+e{Sae$S>bfmLXnu`s8r0QdrZ&SBdAP
+l9idr4b<8;t<PNp9Cia0*DV~%xkux7-vd<O<ww#<5B3NH{$FO!WzFAFqltSM}YJnRCL{j>hZ$lrkltx
+X&tepk6=D^Z-`ski;o4pEUGp6+P6buF#jU_}PeQ81;4J)e}=j7gf>Mm*9j@^U`cZpc%z%0VikV|s?>E
+J#`w^vqTC;E~S-Nf7uD=MV7GTD;jfccxjf$lFv;iEwVS9lz7ERJU`KelwY2tafjc{7%s7@j9Z$@)Q2Q
+Mkk0Vb`#}8DcRLKG%53aPZf7t01u2;AqQ_ea+#>!<XO{Y%BSJ==?&P4DO<MF2<7^iRXcFr8ER19g&M<
+Zh$MeJh(6GBIMc^_f%^J%N^czShxCBZF{_qwIF2;Cx3_q~61r4@ht^&bq3+;h3{AA64^%orEt-M`Kns
+dCRf&NY>>Q?jstuUVy24GP=LZ@#=8xyHIA{#_+p_X&dOQWW6;JK1K)8plU~%AHJ^S`qRe*{w+q}s(uq
+e6y`x{l$akEV7n-Ow@91eJINxD2vz#&>k>go<`khZ`bIn)oZ*cgwoj39>M+hDn;mwUNF<mQ0IDY9qCV
+MeBXU`mYCJo17m78n+U5*}D4Pk!D}50Yggo1)YJcZh7TIjB)1@W?zwap<<wjh2F@-3}Y9|LEPLi8)1+
+a0FOeSiv6+r%qSu(twV@_(AiwY`NJ{7XoA|)B$JoZiaR#-aHFMiy<G2LfB*7lfaLJLpcx-Qlqn7irdJ
+6ZEr~$sM#kTCq7hTkA+5Kq4tQ_Zx6v5{i+L#n#Ew7H7LDgwHiBqE4#>RismsfSXg`u&__U72-xtVX;D
+L^mZ{coBsjP;GF=|39-{;EwE;-%W!p3>CPP@X748E;`xGC5fjlFiWO1#jA~5lqCHxxiM$m0|qF@xN!F
+6=C0JAk|nhI99nVPf+Y_ex212p>X=`f%SLJKir1IrOT5JKT7H7=L5zfCLy9d+K^8Am{oKLbm676_Fdf
+#tb^$GQc`CJPMjbOQyievDuJbx-deMlig<_*iFkO<}9&YX2%B<iHJte^WGYbQNFz`2E!z|MAanUi*))
+-u`72-Z&<J?&202@J7T#Ze$3YX*Be-3*kr;+#||88xRV4YG%LWLlkv^X8boj{ZzE$nUd{5EF%l1TP!D
+!>#=2${cp>%Ez*g4j4QP<`2MUVzC<s<G)>vbr`XBICjKTKTVza=p!dNN?Yjn}=6xdum+!3s0DWJkfcA
+89(1=`tSnS%M=gK@%7q4gV5BEN3!e6{TmW=@rEhsXL!R>5ZUl1SporKC5l?HZkyW!Y(Z{$gfF=~*fhe
+5-A)sQ^fvM+;wuFsA?xpAgKz8{*y{Q)qao<0cZ1fV5-sr4ZgYkj!9N5D%mt39e6J5mer`*v>Nz-88t$
+KGM@Jur%}1DQy$GN7&wAFRslaHJ^*xOF0UMGfn}MGJ%NhwjeCo^1tbxUZ*<wkW(}%oE%}7T4v-CWHWR
+d!-{qqywXKp;O#?$`1F!SeZj}(0S^(#7(EfxPdG_R5p+4e`_D}9~nPfxv(2v@P5G7v~dm^GTnG#gQWd
+{v2Uj^PG{5>&|%)_B9f9<TC3Zp+0<tzCoACic8+eGlg)0EoNThq2?+dF*C$W^aC+*niKIt>6>k%ETD6
+L+7dg69_eqr{xZy!8BuLF|>@)A5B>B3uF{6bxIu^yx&C7I0ida2{`#fvr?cz>1QCY1ReK`cdT{!vp(-
+W@)78~~D1yeAr!mz)c1LC~}!Jk?@DMEFd)mjN~*7|jAdqfIn;sq)X)rnt=_%k1Iz?RqjF6pnb^#-Kaj
+$?k=o5>c`d)5Y%MPv7qaD_-P99#<4TUbfUtqJTzvV$N8H5udztg?$c+$~!CM(jU1`AyY6KCx?AjLRLP
+8aH5F1T>q+9e`D23$D>;u3?e>S7$O#fO>7NCuh8PfuRix_WSOx#e~ncknwE`*6k7siqmEiP0&-*tA^*
+kdgWb-U%crY_4|a8b-h>qp!fQ8jvmJ&b^Ot>;)sMZ)i%rHKj52@vtv7R0_<_Xoe%BloyURK)ey>Mcq)
+cZ-H-^5<8_i1aV#<zV<M7jb&o+a6b=CLE~ut~tiA7Gx!nHy9j@!)^WUSh0{(e*LJLH{2P!@ekYZttj$
+SYL{A!~YS(1y3CEE}uyCt#{lrE7xxLqL5dj?`>GMWQ*%iEzwlqm78)L>;`TT9+Wn2j_RqV+4lC=?sLY
+xosaBs_7!hk+&*k4Z=KQ^yGNS@d5R1^n8D=Nu_`N9}zaeeAv|c$z8QEF$$4g|%y-KnEU*mN_s*N>K^|
+%Wbn*GZ1=8ryCU8Pt>W>gh5E~L+x#Gptd8>67`V{ApFQb7a=%OAcc<;#S4Muzpct*MLW@!U=^y(B!$>
+6Ww}cBIq2vM-L+lWfq;?{Z(*4PrkWdIb-TFrhTF%0gV)*Cr<LcHoK?_wK<uS~{wz9$#!V;hlPvdT@?{
+~oz_LIjvpm8ved8^2-*rYO7w`~+Km&_f`o0fiHq&<!Y{i@Uw&xkTprKC&+@vis1eD|xb2cc*A|1_q7}
+}joL0ioXmmm;Eorz&i9eL9Vd4whavKSaRhAmXg&J;5_?z0Mg8oIcj2{2gkO}H4sw8)y%CF-i2p<f1rA
+j-`uK{p_bO=oCk?sx~PDHR5|<ACg;yPOZ<-Xd03A~XZ`gW41zk<~JVYmV;OnWGNJplz*j=t+O+;D)G~
+F|7w&W28)}l9Y~G@o@p1EVsCB5|-pbrp<eNj~L84p%xNdYgS)N>&7BX<BR9C<V2YI>8FeDuD*YBfhSm
+By?%T7>bt9-euAnnG-ZM3TLc)XImlFe_=nH^hzE;bnd$8;y+-w%hNhQ;CeM?+k+M=mfxLT#hs6N@AEU
+q1zyMWu##}$fmquJTONWkHnMU`vHf)MpAu;;n>{mAwf%#!)7+#_up`UDHJh_bqn~IkQl%4~uc!PkIEh
+}8`!YMtV&o>t@zWn~%w_X?Cdg|C*;{g<m9P_sgv3I76ro(<$y*9_lEoV-$jp!B}2!l3bv?({i6u!V3O
+*?W_5_g?#;Z79(WYBqgdZ5JJ6=J?#C?c$`7y^sz41d%U4|b=Z9U@Q+8r)`J#RvbVCE9oTu?tSx!_DGg
+1hW~aNTK>2Xi9DnCVP>4jE2DDdJ5WN;Kc`DpFO>qc_@d!8Rv(z_3@sz9?*^{3LK_$EfyCar*rZX2Vvl
+qGR&cIfUa4<-1&|LjB+sni521>yi0?3(YrK!&MMwr=&W5^n?d%1x^!-!Uf)UBi^r^c_|q@aXVj7E|63
+gEgBI2uY3)Deht6z0$QdK8|L?O%r`Myr(m{inrbC1ea*z)oIDVHhD={NkNFyB8Yxp2*S(mI+2`n}!Bm
+eyUWmlUJD}c0Wr&ky@!|(v99;z?Y>Dhj|_xyfl5aP9Ly$07)Z<O!uxqtT_xg>Niym8bBeMHc)_n#s$@
+RjYy3YxPR4MY3>g%O6+Gjc`z()sFRPq*y1KkWVH*ckD7qqQ49yjFw@8P~L7srDQQgTccH95!z^5k}4}
+*h(G(7;*okQ*{t&g>j?g5re7`KLoa!?!rMatEMPF1P<(QTfHi`I1~(Y<bXrDWpmtVHYhpy^-MmGZqP|
+hWIo<88Q+}2VowFqGq(Hb?9@KU+$HH*xy5pOVg)Sg4jabPRj#R#z4v}A!E#2G`iC=2Tb4Q_QEz=vE?#
+PR!%mE;iJz6tTHFEeatQym`6D<%L9i^uQS~RA6evPZMU7vqi%SZBlvJx44+Gt8J@;0Pi30qHL(?sDz+
+hSIsVT!u<z(GMU_%1nTMF;$9yu`AF4K8RU}P$jzm1WG{(=GY>YG<*tU9ksIatjHq~{usD%ZY?f?QCK{
+v;H7B>ned<S2@-4$unj9qITxAwox$Z*{Mh#M^O1Slw{khO!ZN$C3izINIzMk348LOwWL|5o97*)C&Nq
+6EK$0=fgY9=Hm<vKNeR|6G595gc3m5Z9~cM@V&wKH8gsq8=`rr8j#L4=QEI4(KrTBbhI`msNG{I<0*(
+<02Dk_N~UjCE!sRWS$%ley28m6Cpx1V9UDJPOy`vXD~>~Vrp1UN$3kO=HVL9Rkv^0_J4<(8S>tmTZf|
+h7jh6zfW#Ki(zF0bd)+tEAVCPWw`t11Q?q19&x&=r1fR2aFE_zX}9=bfYIYwTzCfU`A_ntq(;iLn<$A
+5FKG_c`&XOU(^gR-W?DZ(zV%yz@9bD~Akk}UAN5_-GnNP{{t5MiQr*`;${ndSy)Z7I(bQr0^M(U(~P>
+kv;uv(e<bcmpHp)L5p3obed&QDBY}jh8nkFF4y+-N^B%*gJZGm+?@Xd$q5p9g0qGqW;MuSF34J<yj{i
+km}9Md=TE5MwY!j$>yi6;gj2g%?AHy5lqv?U5wGs!0_`7;O+teF;m{2yJZYKgkIy>^|Gqb5r|>dHK$a
+v&RAp-^dd6QyvIYrAg{rslXGj8UeZpQ^BW{RwnO*E6rFJE<R7>Pr!j7dz$CYrCW;bgCCEP0T6ql<Hi3
+>-<U##HJ&m4*f8>NLfHx%(Vg=R9#^>C|=nYzDMQP0pX^j9rjh+cW>5!B;0|?KKY2=_|6lIB_eyc&Bf|
+uBrnR<yW@Ho<$divj{0#kH6n==FffhY-pvMJ^`hm54)sd#MqdF9SHSJJ^TLAT8+yBBJh!IrZsrelte2
+#J0>UDX_eP{>P9xp!PqMY#|8V3wpgeB6yJuqK1CAwkXiLJbqK!TVp0(zNam!s9wL`EmMuLpp&O`AHX4
+Nv<ZuT@KE`Y-EXyvlV8vovr>Hm6Q>=>)dG1ywfy%#6Zq6bh|UXbWBUDYJy?$pqvkTIPIE02XLKvxONi
+PjpVFj{zRq1+W=#AYMt!nnnEo*hgD=3iVoDEgBZbcMR#^<a9Y=m@xf!e{;A!Mo_j<O_&_ghDVdMY>D6
+Y3<!$9;p1B9yt)wRl=N+a5{I_?~H>9J!o^&-wejf?_@XtuDzLAre{$UC;k4F=3@LV>kp2_=UCKRX8a1
+MYzXr?kng`IP3c!%!d=&<<tDvnL$LDhTqBTc?Be23<`$OUpSD^CpA{zN#b%)=hLoKUv}I3E_L0`k#e-
+92>dLepb3l8(K#{z#Hj0eiLpV-io~SnK3?qSBTmF=Jg|^0$?Qvv~D`hXkfP{q(fgr@q4AV-4R9B255@
+rA-j#5C(k)C{BauR);AtUJ*VVsD_hePlNEKThSxZmtwUv$qI$<Tw+^ma{v!;>4dr3VUIDJso0er775<
+Z)NZDJ=Cmg7*=p>NWnf_MY$57AzWUkK!;bacK{%y{X=QkBBVzqCLjDZ&6l)CFt2r0yd)~jn6WFD<%{}
+G*$9?)(XkcY!`d-^DMZ5YjfiS>LY0NUno9B|F@ei&X;p;>grP^{xkiAHdXe27amT{+qjuf3NO66%}I_
+J=%RCJkdK+DXx?T;t)L~Ny1%2`K8w=qhvT-<fWPuRd^xQ)6La1fEmoGyuq(Q~k1Hd)x=cD#iOEp-+io
+E2uGb;yrL5Gk9ef$nmN_W1;3eGl=+Wi8naiAtWbWHw<biO2K*TOvvC=4FYAOq&2dhCr$YrjDH8?oui2
+=?Iv|3C;7qszC2;Vo+99-f(;xZup`z;ZSbd$`y?PFI+%@;MI(yL}K3XtoNPZUghOH$vdSw4k|t%<u^d
+;N9Rpn0u`x5aN<q(qa+!Vlw&<Ok(^QIWVSBHYY&7Ni*(Sf4?(_3QtDyl4oxtrOXnH*<NBssp-Uf((C)
+tADsF59V4>aU%p}jE=Sb*`Jc<F%#^t3NW^UuGpLQuXX#puAnD`a|0Tv|gTS`Z3_K6R4ZKB`Z<P?wAcP
+ZY}hVd~$_1DcoN;C}QrE&23O1&h&BL^i05X|KzWb34lV?fuZHwS8sd3zk0J2}ioq4>_;4iIX*Q-9_)I
+Sx$h5M0@V=@sS%Chv>&5uV}xONR|c!H9%$tqn@`T^1v?2Z7gP?_p|U+(S=KrR3y7eP7&+@^pW+(ajd4
+uX?a?*v-L&YT05s8kOmVEd4QNtvfz{T>Y_UTq3N%m_L-G#1sxCyOL7_-@U8ey(>@`#6K{UJy@nD9?$@
+=(=e379}(daZ``4(8})=1_FzOVczRoh7v8i*K?<;mE1Xj@AW`XI>JndIb6rmDoSmP3;~i}3^OTI1smQ
+z|!{CPGiMpP8O_Oe%n%Xz&U56-7MnvU)anMVQA2<wVYOzhviI&#OI76q_R+1aYY5Os5wfTPV8smDQ5P
+8~y=?ju~?q?!CNMNri@%P~yWOsrgjGnYjX<&|}2JlzzVV-#Y^@B4!z{Xh;ge$%5%{B$K%c;SxmF^VgL
+>C>gkIr3y2MyWWu>O{bmxJD}B$6%5Y=(%!!B8cjF5iu>8FGlKzy3m_flnieu<fq%zTL<q;m&hdBJPbh
+Rg2M>FiL8mP4pLd?zEOXHlD)aO+l!`ddesS@41dB8$}&>m8maW{kj{hfcVj7EkYlxiO6HznFMsg2OjH
+}dcy>^UIFq2ctqZll*<!4qXqDq;z8{V4%B0qonomcISS34<RE87Wbd3I=Nx{*^yVne|Nl@PicZ@FM%|
+z^-IJ(DsY9I_@G+L9eCzmtJu)xGGYFdtbAmkpx&i1!<AXr;51`@I1HU(E+a2r6Q73J#e{oh1KC?g_CB
+Foo%h;RO&1GoSs}(1?BQF`uDTc<|Oq4*_+G2Z$=SY21>KZq`C?qz|D7U!r?;p$IBiq@5Gj9mbo%BYB7
+3HEYO0`WW!XF!HHzW)zO~0a0Cxsq4M{TaXX$gcH{nX3*b2;yA0xwUhMZKiY2kY%iJ8ik%CYRVh1P_{&
+cugKOG0kt5Ob)~12@G&>{-zQF5l!|<A^+MtCPt8(^cDcB8){0S`BG>!zqC7sg%>AcPQA6$t(AMJHvOu
+jlCT49QRca({b6D4nw7b3P24o$9~>}5@K+&&_)TX|!rYM-;eJM_pAU*vySiQQPZ{we+KesjE?nQt{Iw
+6Sk(Pg>AaX{K8s;4D>kcz3u*Ml+uW5;wmj>fQme;{F<^9XsbqXe5jj5bsf8(f54o_KgnIQ)8yPwrN<J
+4j6eKihb*BM($@al|DPvIL>aQh>+aL*}li1OLe12B$Fhf_b;<p(h!UX&a8%$^2aLwQiJ(-zjd1tl2c8
+PriW_YdR|9a5q_(!8W&U1htvrJtW@bNZDH(zDFLOHP1w0NPqNx8+D)7gvIQJqC9R=Nd%1Y{b-L&XpDZ
+D4v{qrZh6g2LPGh$H*F3TuGQGsr)q0{`N%$bgGNhXnILjJC2nP&V#<Cxc^7o4lQ5=iFVw+B%=k4A)3@
+5ZhwM(cZ36dgOXwZQPxRyr>n{Sd1gbey<Y;dww?3uqm_41HwCi<Q+3*Lj2YG*{fk?!B|QbJL@u30vI0
+W{I$?G}?}~C;97aLT8<y2wm-PYa0e0;10xOw3aZRMy1N5D~nZ^M2N^C~=d&*xfdoIPDJ&Jf3S<!`izb
+<fCweo&?z{#w^4-o9DF7~r;L3~JaH6dT#?vVLmt}#h$F87m*AI?O^zuVHEcC4{;4ucO28>NgnA00mVI
+3&sbm1qC*=ILM{W$-Y(tOzGk+E?p3OU9_vXk59SUU8LWn_FkgKr5qyA6Cpv^S*Y5S2#pedhlxy6>k^n
+*EHvUGnUf!ek_mrsn*eX#0nt)Xj^S*=6PTE7eVmnC;V0@jrr-L{{c`-0|XQR000O8^OY7ynY3ELV*~&
+I%n1Mh7XSbNaA|NaUv_0~WN&gWUtei%X>?y-E^v8`R>6+jHW0n*D+uLKyLIHGKw4md92(e*E)eukw8$
+Y?2$VFkSW}`(Qr5ab{=IKV$!o{nqB^W4&TwY<=Dp#jX`1w^&+-Fxm9auOQR63msct7K>hVrpsmKXa3p
+fjJL~jW`UUWniyT#=sdJEGDXIk<No9M}t?WE3<<X&k+QJqs2O~Fo?U1J7aiGoCFf`g?>sJ0!vEHnv;u
+#=1F^bvl^&{xvK3FREfI)!bbwx$(B@4O}1W=V?Ug{3^N2R~TKbHwb8^+FcT=)qGSzmsHI-)U3K@5W`l
+^v(R~jxI@FiH#Y%m_VRLg;!M!-dHEbMwN|d<bi-&F?5wid1kPeC{D+a+8IPc0_*`)P8_5jh>rshyd5Y
+LfAI3Cw8ppE?a0CrSX7nrIIh%jH&8|(>?`HqNCeoO&}vU2b?PadLdt_wI)do}^vP!X;m_YTAGaIvUfi
+P|;^TcdqTE^z+OLE*W{-Zw@+xJi#5s?FB2wUhs2pv~#zu=e^fn?eQYae1P%3BVstekwu46`E3!n_pVi
+mzqk@wX0o~>}7ThtxHSt1UI#zmMeK;(u#_*Cc3czw~00KkR{CA&bMdjiiSN@Una<(dE)wtB{llog02J
+~c!-s^F?~@RzD$K&JL`amlA&x7&~T=HYjsI58#zyZCo8e*?QtwzL2bfm5C@mr0-!)e7g4!nG1jZb<j&
+aSdR(zagJi=oVKJbpk@QrW$mthH=FKG&Dg0pgJs_TzMG~iOzWF5a3Ldg1jWRA@p@b0x$Q}1@$0G<9+Y
+$*6W?}%}`{eY1goJ(6c4w|5;o5dm!iCx*xP&|M>2wpED@GKBZm63i-gxyF}obEjh0Nru563ui2NkU(*
+aYx6%U_w&eLrob64*UT=>v2tWBG*F0Zb#9Rg4g2Ia>v$mYyFs1bSH_Nb}o=WPycnATu)*5>=?sEbECn
+!Hm=BX*4FRFUPQO}Lfp&w713+a%kAz8usVs~a;ua#e<S-KK$&(e{HT<qdJGDmrgVOB4tjFfw*A<yq1I
+RmQ6pg8bZo((L-ANmy&sS8S$nDgM;4rB~vvX-WC#AAmC5|eaN_61e7Dy+0D9dK+UP#nKJRM?p%Hb%8E
+(W@#BF%B?~hw}*-ha1aeCT?k}d0a|R{I$UybnF%Sq>oHvGon-WaztEAh!T?l*zibHKEe<#ALsdQ8|6{
+R(i+Fg;=H|<p84vs1VTFv&&Z-z;+N77oO+&`^DXy0A5bgwUY!q21~UxFhT2KdQ#gnbMNEjE07+)T@z>
+`&W*Cz+<3Fcq^OsTG;*@qJ((gHAx?GAMPJ5GYtB#Xq!uO(!=QA+7(DNcKW!Vrw^VT52w??Beaa(z1;4
+Xk0SL=;$^}AaJsBh1JT^aa(yRioTAIQCI$p))$<>_U)m$;{PFI%$dpN;?Ua6Of(R`9!a+V65p4)~Tg>
+g%o9!Fp}5gfV!lta%p0@GnqH0|XQR000O8^OY7yhqJe*GdlnPD>nfE7ytkOaA|NaUv_0~WN&gWX>eg=
+WO8M5b1ras?R|T98@aLP|N0ad={h8BiXthRmKDoc$FUQgGx5xwagyEjIy_CXDRoSCbGn;)WV83PZ&l&
+_=w?%rJ)Yd*z46#2P$(1%g+ih5!vTApt#9+>;<9AJe*YQ!W0qYkV|Jd-dV2?qeVfeUw20@d%-HuozGa
+(q9_K8AT4|Xl(@mM>?A1jc$E!FkdwYBP`}?ohA}Y!*TO!fxm`I<0e>FOSKgMhorOA4;jLIZSp~y6k@>
+`aq?EBmDGE0HN`^%)zKNM^uTA$@{RK`c^>{EQCh{QyhMd_Ro^=LlN<D!U*J^U(-%j+!vRB*ob_7-`zV
+w1^YQ*QEjGGWPTo#iF;DVb&Sc(P2&IFFXa-rk=01-~e>489Uy6lK1dm3w=W$yJ;e&;_7!##)13zc(1Q
+_CR8<v(*|pH%)-rEn95T8TPc;o6P8kNs^Y6@^&2;P_GUD=yvwX{PuK@!KbneTW0A+XD_~)#p{y2i&pW
+QJkN4oYaJDZPXjoG+M;U}HSMzSCw&X#px11ATLJ^$@b8lao3{Q>zxAR2k)55f{wZSzV#wGrD`FUsGMN
+=re;zLsWg;>uGy2Qq3WVE+zjb)?_{n1!ixeB%i<d=wdS?nB_|Wt=={lNyYEN6={Jzy?(++aH041HGl0
+nUnVR55s=q5lK@MTYZSx5oCD!JC1QZ%{sNg=oR-!6;Oc@m|dod)wl*6Jq|Zflxcw2bZ{H+}VQ>dTer3
+=-DF41!6Kq(vE}v$#FI>iUY+u`2O4+W4OrFW5<kJ%J`%0mH8_G0e`2K`Bs0ffLZuIK3z@{hs<v$r=@m
+)r$p`k++0jk97axdrp_VV0{L1690O6#)j<iV|F~WWtB%s5sN9^i*Z2P`%opm01KCGN|r64NO}>I%v<L
+z{O>9%8P2Hvjw>HQM{3_WXdiE%myDMd+k<g)z6ZmT`!N2W|G69IP`;~Jc*({i^{?Ns{>{Sq$ll4htND
+AEmFKJVl2(RzP7|ixK}&KO6%ouMZrNa1%L$iDOQ_2MR;3B3e`8$++B1t;c?tcv&e9hCnSa8GFzh{J*O
+ze~lbK}kO;VHrQ>19M6W@F!Mq9vTg)It`Kq=$g0anz4(VJzPv}||mfy0qTnbL$qGiv=>JK1PJ+c16dk
+|r3)qm0*Q1AB_$ckHDw7YP}4s6hX^zD!_UPe8|@wr=<~iI;PhXk}zi&WH@OKU)G>HhG_w(ek?-7XBo?
+_;YfTr0s0_k9a1n@MKafv$CM25M(FD@K5n_4f;sqg;lSh&*f#zGWa8B)T=xxVExFT7`X!lL(?g$PbSL
+}DB?0cnZ!R$CgS_Q#VoqYk~v>mNz^6sH^~HpY#PrZa2jAN*gT61)Rapgvy8QJ8F~qv<T><ZdCP9&lC>
+|(a(#MqG~HYjy*2kqvi#yGDT+-z==Vp%j#dlet6jv)MVEK0(_jwl8THe+vVQ#224)-#1^n6*E%7p@-Q
+cCbOR^hs%mvpeyNEjhQkZf?&EyY<;6D9ZR`+j7W?goV8uGhGg}mOOi9~ks*I-X;WF|^rRg=++>W<!n(
+Orh&=NRucu?^BBvK>nxpJn9k$R{7Iz4Pz?I{Nm_yFb4F6EuH3#D9;~zhRM77V8z?1^hKyZn&e?)_)Zp
+p?wN{%jj!p4!0Au(?BaFHWEGBHwd!t;&K%gpYB68<s<}I6m_zmh_>3|znawh=o4^~!qx`YXs~TZuqcv
+A6vBbuxFyCVAL$EY#?Y7Q@$r0>Ul_AQv~kK_36jF!K;NNDB%m?bdB)x(WUXnFmF33h*}E;+)!l;KWyN
+N_UMBJUw~}PR6#*6jSf(F8J|42`WVtlxOp)ibNWgY;DNV(eZhDKF7;ToN=*JJ#Wuq&vQYtA18~cGwse
+h7OWL-2?MpZp-r>Lyh?Z=`sk7+*zyd<=8#9ROoBLU!l#|HPJ&Ym1F`mfqw7+P#Fs)m_6PJCa{_Efg(t
+D7+gY0p>J+W7Odh7OGO;sxz4-z$2HgFg+e>EC*k(*jLXJX){gC>IJ4rBdT8J?pWSP2dmU2Ha*F;t##A
+NNif$xS>@s1vcz3WI2vt<ybf*ppMuh3yt|*i#=jWXnVRhMmKim9B6u;o~b|`Z45-rLdzDX>8w0^pKs!
+y-!cg9wps=mdHFdF!#mD{91Hc&9SwsJ#_m?3$G*!Y5g`xU`moEaC>d-e3RprVAKPrlyRcS#W*hQ(TfN
+7$65`xpTA|wIi%1)-pMNg>qE^{+DL**|#ur~~Lh&ZRUo=~IPtv^loo0x<H4xW4E@pYc)knDnxJtmzvr
+J~2Wu#>-R2@{D9JSk49<ymwB(rD<JHrU~tJ5UqQESkGDR{ip89`T%JgAMdG=h(YQ~cc1(9b)$KUaJAn
+AU2NHl5g4BOW=-CK$Ym=l<mWAzs55F->S$n}HD?F<wJ>4B#wC6WyLB7l%CZ*kKe@*fPaaISoO&qA{f>
+vWLcWub5}3#*&WYQ?N+vHs`Z$3ZC;RC>w$eS{h#fw^uZ;%PeCsDK26)KDI3i^rKFzy*RO2M@C&9&b3v
+}3}|-QcUPbb%k0|KG*7?w<=hT;Wx=&;w6BzAd{?CzjK8GTjPI>Az;AO#?>3Ty@s}UUu^P%r0SjR{$<`
+>jXgLwvYkx##FfwCQ!`zGWl|H;-!6hq=<9R%n(m~4X%se?$jrE9hyLBXF!Q=h(bBDcT!|mIn{qIs(Y_
+c?FK!gFqtE9x>0Sw=xeBa!c7-NIqNpUQabS}dQB4Vt(2Q7@ZPzsi`BK~O;V@Mq|uE=t12s*lfPRuA|0
+g^s=WvO3Km%-Ekv1L+_dt_7+j)AQFzcA2jIu@*iw#YFlqa;O_07qq!<OTP>VD&7GDZtg390<X<qqs1v
+XB2=p`b<A?l4qRY84T!6*-nHPiz=0@dFb9gky-a9@WVH}8vXqV8`uFX9y=%UT`Fe%Dw|E#I&>D9-}u#
+4$bSO{iVo#5ay|KHoM(9WP_)y{YKkr-{zDJwYb?)ontsQDz&d=Fk#VW<Gk&DU&=Cv{DX)Ht21r<yp85
+@Goq`K`ldLu?qakVo&*zbeIy=>zH|b~d8tyPJBqjR<MV=%l;_OHKdzGYZ{H_~1#rZEm={HPBmL0M|rz
+6cvv01I6Jo#tr%qe5Y^dS5w3&U)X#+IMLU4S7=w<)*i<(r!bW1+fRFJHc-$<?l6#tbdB2G57Reh>cF>
+U0%}Ua!Y$KS2_MV<6J$G^fJY!91J6>QJ7tC-1)d;m@zWJ^$ZtCV%_W`TI9Ne*fzA8%64axuw-S9}S-Z
++bsCwV0dzLJTQAzQSP{ZbUbWzK8Dnr#D8jxL{0oq*cy^?&ziuTfEd7r^(*&QlyZVeG(zRoD3kqdkN!3
+8C~dOsDY<#Z41>L>k(8i>v<NAlpu{5B<)Bw?E8_0;iSuX`Gm#aeX&BCDh^|46oKc{4L3uto-_5GFgIt
+N$r0fKfasW6v+Fgu&8`7o&5F?l=CiUd^Q4xzl>|l@WjfhcU8F}S9o^zvyrQLF5xY}SCD7IwZC6;CIO#
+2aB1hTtwO%ngEZgT%oLG3-#BPG?$7%X%w?8vP7F>RgA5BM}R9je{AmSg2qpRF+_Vy(_zxU)`*#~PMTW
+8!nCa~Y~N_G^vpT7Jrc$@?b#lxEiy77|nyk84_tfhKQs0*m4VeE3Cj1M7-*IdGey3yDP^MIwHXjs<<s
+kLPv07iX%du|A7-aZ#)B5)_rL<khsB#wV~OD*g@)X2A35(9)<miMb=w4m1WOiBV(m;I0l-vTZRZe#Qo
+vca*B7+f)R>%K(QIeX>{IemnW@-J2h6b20^m`lQ{b<uY0qYN3n^9{z(JLdYHdHp|u+Jj=7q#ie=phx1
+X;LhlqD)!8P`aa#x-nJ%;0CvVj@RBm8vs<qCITgGAOdRSF*JaUU3R~5ze(J4EsEc<-O%c{b)BKrV+!L
+c3UAIJE|=p$&uOJe{k7xa-@iSa|JoyBFgS<W%&hf&FBxx{@(!c#DQi%vY05no>=k<p0HWBbF$&bF<|D
+|2fid2AKVsX(i!TA~|Phpp2Zchwl`O<^))l2Q!+pT|~+ES@_n(E)o)wsaN42AA)@vkCnJ4Fvjq&Ss|A
+OhNn52>jpwXBZAy>(rT{^84O8Ter1#6dTxG@)Gc8PyW*=L0MGToio{F?;=Jol$W%#5@cCA@K0lVZi?z
+3WYv$nQQgwgB=HUXXj3>~e<q1w3_G4}C@>(XTIJ^P(r?Wwp~Qi4$aL;>=K%L9(kFvm$Q4Crip`QZlgo
+0K6`zvz8oz?Zz+nSTHMtR+JQRf5Vxp+QBjnrcwiM_UE;mKn^cpZ|fc1s<;!E>@{V4L_;#*qPInAz9h}
+IUtv79itrr>D1Uqf3F-=x@Hmf)o~&9i7e!*xN{c*zNQKlu4<xVul%jh+SmN}#soa{n8c=mE3+T9mSa4
+~~!Q?WRIDq;4}(=@#Lq+7Lrk*F<`s1~#+WsM#Bbt*4!p@x#ClM2bjnKq_cCVP?GnLHAso?&z^uB{yi%
+rN?fOGJ|Vq**<&}ewOhMVe~$Hl=+1`kY~k*%$$EGx}$|>1$m5&<hA3=`<a_--U9sh!{Oj#Z({i4=$Ux
+#xcqz$w_NeH?n;IXcS}f5TDfFx*!86dZYhUVdRCSf`%CY=6~zvUq-RQnBvYN@UrofZrKX&V*b^ctvCz
+`5OpKBvM~<{k8EPaaA6|yR<|$%q%vQO=fvW&tbW{b5Ok3->TuLZFqC>kp4y{7d6cJQ`H7K!?yF{RFXJ
+8u}RiqY*g(FAKbqTaMz4)f&&cOC?pG*l-=OXo6jRLk#C_))g1Q%*TmXK~cOg_c8m`ZJ)Jn@)5gI}QIN
+f}L-{M-X2!?T;q>o`{DIa~?cD54BPnWxci%mxV(b)5t7TY2=&jprtvN0^jJN8s~hv50B&!%xUzy5O-D
+Dw%fht74JpdDJX-BJortYp-}d2J|0qp7khr3_NpR2UNRERxxJLT*p@!3g?;sm<ERF5!Xp^Dcopw8G)W
+9YpH3Zv%q=51&TGGmFwuXhYJ?|6OVeZ;v9RJfTIUr60up7;ypC_R^92s6E+kC4AmKMcd<cP(Ge4$1EH
+-648o~Sb}-g#H*FCMJF7L!!aQ5&3A(1xrXSKWRx=<YmNU_Yf8zm)^d_i}Qj<s<jms)Qa{lV*l9O{sQw
+!($s_O;k-wUEhza6-3&^c9I#dCy}xIuumc_J<T7`lY;KTu$CUfy#3igBB<US@MLP|JnU2|e{FDw(Ahb
+Qs|T7>!6)gg!IRWa-HvF#5`qqeW{I*z)E)Mw6^3yizrb7?PpMyoE_c^zK}mr<l}M6;C-%HL!B)jI3LL
+F*@MEg-0+UYOp`N`;m!SH0x{zyDaWq^lX!i2OetD_KfuC{lV}V7>#~!u;1x999b0TIwll@;z@dl)q7F
+Cj#>?y8TR{w)A{t|^t53^&HwMDH|@=lHTu^OnjH()Oj(AN$sL2r!4m4=4$`W?y?pBtbg!U1#57ZyQ>^
+1zvPj}NCcn0EY2vlJ#>I)sJ6%B7ygD$ujAx%H!$L}wUn7V-%K<h<?M&UfP@LQSM|0MO-#T(Xtk1+zwJ
+A;Fky`%tEechK>_{0psdE}hx<V42PKdjgqEI;YXt0Vm_8vceH2?hkTF!d?Hr4sK{dGr<r;mgOuZsp0)
++hKKn(cUd(+O=SRN8~(5Nml-De!kqS|O48jw_~;wXrq`5ePIDw3|3ir0$<QPGkZJ<W`~1od(&E)J}tI
+iquzaZZK%!T*c74xgL9-BFpbCbG>TM@Y1r<F~{7?RfzvCFSY`S3<a5#*Vs>yVWW+k9nip!-f$WzQC**
+OPZRNi7)05~Sg5n5&?!BS+22{){sI;S-QzlW@lsq~Ty|Vu{O`d<+ZGjJ5nwkRVfE^y#i{$ElBrCfwx9
+vv6JUy(pv$lRHWuF&**ifW`~>q0*PD{3bFRSmSi-cz(<%7RZ!o>72#!z*lVb^`C!*+CT_OyZ#7=WVe`
+w4t3#w<S5f%xQV}~xAI*bV>0>8%o(a{*4T!BJWRe)`kEZn98FwG4fc$tRNq78;mR&@@+(0)~Nessi6t
+m5;y1jY&@n@wNn=h9UatsFo35e|jO3I;n`;>MSOiN+nSGvcj)8T9O*;K;kJsn1qxW@H{CLI?1D6)lsw
+N(H1@DgJ?uZonhT<suR>sw<15Gi20sT_Nu_pmv0|+YIq{u^F&Ut;1%m1&J^j3bsl!;pR88wb;NZ<z$I
+me4g$*%Tib?Xm2K~Jh-v&q~_X;;7A1R#Q~#x8p~yNjoTbCN7$32L-YZz<K+_nTWp|}4L_N?zDz)$!NG
+~U29+Yiqq;bSk^)|U-$|fgIBZGHiHN+>KKQSr-sBH&UZ4N@)wh#Bod5Cs{f|AoBECi!y=+$vC4OjXRw
+%1o&#;WQ6(h-kN~-FKh-Os|&K)=rB53z9qNBNZ5wbY<@Aod#-&v5&iN*gJa9|>-Uq^T+i>#QK5W<nMG
+<(a!tmc$Q^J|}7iqnc|_I;F7>?_c<Wuop*sZ8}strbh6&=u3;6r&{u%GF=bU2d*elA_U@_xUXYhiG<*
+<E%6QcD)$eyT;rE9Id0l<No6#9zc<vR1Sl3v+nc^Vt5yc+YA&>c@0Xlf|YWUZ3?m-u$=${bx7+r1#00
+Rd|m8dK+~X9Qc4tUUj$2aqBOnPfGaAz(58k!U*6%ooRg+X?-aKqz<O!R?Wq4`@Z^XoO%R(T&9fNozn|
+GY_m}EZW3AwL2YEc-%p~T4AKesS2V{9BlP+s}p|fsBM>LH<Y1<BO8llWr>>^HS<yObmVs=xS41CF{iT
+wD&%6Lpto^OH?XKo1#T-|1uN~;JqZF-BSIfUotWq8&`PPtHUe%4DQS)1i+;83bYyDjP9DI`^*e|UM9;
+$!V-jVf}F>zR(4!@P>HwC+v~`iWQv%@8@3=rV7(x3{>RX7#bOmt<OhDu|K2R`5vB?54Hc+Y<lAmH55b
+k$IJO;fUYOP#O~yv?}>R7j*UbZL|azkG?%%KdP8IT{Y4ds}g?TL#L5b*wE1dP>;`4zNiWct2p9^Kp3s
+1o4RyTDkvNowdtalJteu?!IyW?I}b>07u5?|ImGAUlb<~ijF__ny7INLc^F_5Z`V~CG0@NUqr5{3Xj6
+s1;c*7qde79`VkoQ0yES{l(XrMwsiZd9%c!`lq>DecFXNlGUEd_&p2UR-wXNCB1BUln79}(8YA6qWo2
+`cSg@@0vDjLY3f&pvUOSI+pk1co~XVL63=2@6uiM9dxqepX1-APRm;b1qsiFDD4D*}glf5agnkX4l<h
+E{D;P~HI5;*oO+3*w~u5|Arw5J03r02~alDjUdbxjxWLjpTg%Akd{uF!VAU%u&OSR2jY>4)tHJA)2aD
++RJ6s<#8>zp_~#6i%&NBgbliyren}}x+5QLIUi}z+c&nWMFMii;OFa=(Id+Kzgw@s3PHzKYBHr?sBgI
+bV3MkHsK_(0#GpX|O}8rF>Ku&84TrU}el*RWEdZl|x@54Fu06QinKDnoYZ1gAjytP?&R6T~`bjIg(+}
+Y6nxC)F@omnXo5-!0bbhFIj=c==Fo96ZwCgup#4l=Fq8cyk3N`NdefN}=yTtn4r7M!N3ZYg~G0hovu|
+o;A@TJx9&cp6ixBH8gR+jzUXW23h($}gShyxZaucO<7EpYTuNf5&TR(}D_UMkpE{BGR4`!q9^M7KVVq
+b0@nlM-CLlsBb(5~H!*%*_ssx%0#~_y=AYqm7!ud12}fy7}-;z1sQfqA{B2z+uK}#Ykdg91l*!z~Fcx
+Skm%VI@}zMf^$=qs^r}-5f#v*v&~(Y-<k^e3D3raHy*B{s|6e5es*O-l~>1|W;Y5Bm39SmQL5$DYFP&
+SCVW{+ld>1?Y6baM?E>OyhYZ*{dEN&59|El7ovj;puy3jaCfjHvCt3Kx#KUdw-<_PTn(itNcIzb?0Da
+nZuT9y+OF9X+CIi?tZ57+vk`k(IsA>dvYDuYDRa5C^Wn9Ga_n+?+Vu;#n(xI%lh0Cweh0IWmEo6t)`w
+iUbRPpL4oWtyaILo69ef0RGb&*;3ZP-6w${X(r=L}DQ`1$q1@Zk^GD$508Ur}ODr}1TUm1G-OXl8j<6
+c(nW61wWFxiA|y0g`l{TqScvkR}Yu0OUg`Nff%9%@Te%C8&`M?w2d-OL*H<a%PHN)8xp2AGO@f<LagD
+hj^7;#U`ki$e|#3shrWBZX>|(7NN1dtKIm)`U9TjlLqcIoH(g6SbSJfd_Z_5KJ}j4Eg}NWq-XD|ZW1E
+h3OJRa3#KAI!NQ{>go3G{baFI&Ze8*g7Z4i=%t*8j%fJ<xSz!<q@-9-VrR<9)Tzm+{pEWN&g5u-m#ZR
+Dk@EU5b*K0=Nq~GtKo*o?^TVv!WFwkr{Z3La`ZL*u>W;&NlE3TG`Xf2`yz#O)Stdew72q&&S3BtaJN=
+_kKKjEn7bVY&RW}=hqfLG4s1|5tl2Z2+&7oI-h2B^)dPV$9S6C-WQxQs1-8V_mFw1oqkB2cnrOIsg}c
+&NLAb~NHaV>!rTg?N!K^IXShk_&<R0h4UuE*!cc3yxHdg|VR#&Wzw&#NCa?_$Z!n=QFfTG}9;kj2GA_
+o9sGn(Lzpdod|$C)wzY#W?54-B8OHTa2eiKaPPBI7A&Wr{Y^ut#Q?fKlpeMKDKH=*IMPHhP`h+~AUah
+4_M)xc__%>&{D@%k(ECC2GYD{lAG)XJH8QMjWKh${u(A<%FOQ!CoZQzjy8u<f8;F#wmB%eg7}{LWRYk
+g*=o@Nr34mVgfq5s?k^@eHON0sA3B6+kD;Q1g!1aaTr&SD2agrB9`j4>bkE-G4-A|iKBOGxYD6<nh4A
+A38qk=N6D?Tua`>~Qc$@a6z^<Fad<f(jCZWCg4`Lfz+ea%Z!a+wEkfggF)mZf}InIWPH`07)93XCH<=
+(m<E{9nVvb|Gj38;rQ<iN-L=p-^dWii!rd2Y&Hz&%hjyC{vg3;D`dA&i0tRd3znDde=f=lDyN3B;CcO
+d$@5_B+$_~CAg+J>`zq1)}NsGE0OVmfB!xHz17t3vCo_F6Le?J)iq1$B@lwD?`Oybwky_%!tsd@2P)3
+?A~aKAYFwOF-kW}k*Q)C=NE~V=q`&F$f{O@#<_PHpRJx+pP}Q>P^i`MS?BAhF$ii9uP4QiObQTJhZmn
+k3(WCTow;su1fO<89co&hgDrA}MJu@nXoM*#*mU#W9qN7?XB!CK!^?;+j(>#|q>bYr-3_t95*4pwJ&)
+V*D40y23?`G3@nO)N&b?SAf;o(~F)ZuWjmE6l6$PL^sH9qCq(!tP~-VPn2>Mt8q*R{nLeJ9z;LNN%06
+8X=Ev`8YjhD34jt-bNy2fq!1`9yd-TIjTliR<;zI0%%t`1nGppdtujOW}F!0|WaSGD5<8AZCOjL^z>I
+QTFf`g(V?+;|e2`$JHqO%TbQG*UP7XzKVhK{A_@ir>;fR?F<9*{=I(*Nyj(u|HyEW&p3=nAY5ypg{10
+^4VN?-S(Vw9Nig*D?^Tty69h!PJsv8If84DHO5odYl7hsL?{tMH8X3_EI@9|Hf|O>P+}V`Z-KRoP`L{
+BomBQt=FOo3p!vPWT(m|5qBiB4dt!JVol2OB#Ul2xa;ndgTLu$4xGXAhoxD3CeGLMS{-7@&zxnLqZJO
+R`iIc<qRsQ}Av7b^s6%ms;##_Uyae*71A4^-JTN2Yd%c8W47^km;*-gb%gy&5o+4}6Az`fI&O;P39f`
+=Lfj1o)F0FkG<cEg9@S;RMQ`9J<_q2yqMXD{0FaCWy*marK1P;?BE@c?&Disf<1a<`e&_1}{wKht-Bk
+C?CsI-Z12$UF?O+QB`)78S^ppT&gyLyu`WVfB6~+&d8ui3$Qieak{d8$ejkAvS(&b55q?uVLm+s4WC8
+(`Y6?s!g$I024k8Be#OvLfhSJm6(fp8Uv5_xk5NzBA<iG+(@{*^?2#Cobbup+c|m{A;!Z{SbO;QeOKy
+~s7{jPGOGczOna_+I4|Oieag|NX0WMBib0I7OlJqk*-M66K`<B8esFzx0II%5Bc5P`m83B9_3o53!Af
+RIJ;Vua5S?A>s16<ijCVsf37gh#GEzCfNUWw<ixe}WPfnD*OgS#@q_SAF;)#Xt&L19fg?vfWMy3Z4cJ
+Te1~=1>TWsZfLQQ?>Y!{}ardr&`;A8|?IOus*(p>9O-(+~ctrL|1c|kMz4e&aGAEe4It{+%sp5*GXC#
+YrT}pWzsGt)<xGjlS}R$mPywj>tpPDE`cu}XbB_;v9E9qbY*#H`bG7tRJHzAuTk5VPJczRbqT4!*gXP
+EEM!2~gCQG0$v_^xw#~e2R}Iq~z5``*Se@YV8x}~FGNDI!bM}dnB`x(!b-r9uOjd}Qjmu#ODlRTc8Cz
+0GcxdX1IKPVdTCVEjjh(H=4@^V0gz4L5_)2boc4QFr?y2MF$2ev`ynW4v$Ip)0u=k9Z5O5?-w%(4Qd!
+`72iNay0!K&wN(d7x}FE|0WeHj%|S?2PjwdDw|6?k$5K5nf*Ze=)mH+*1<NwvW!SXD}QRqp7^vx<T2Y
+!lA*gkdkt`InS}NBELF&LFN6(utx#615QXjH@kYTP+Nc0&U?eQyQ0dNF`mBUls466z}B&Dc*~JU&RZq
+CvtIUzMK$Eg6%ke^Zu<`UGBe@U|%soJ(XCe&hU#>j2`fm=}jEu`i={UBDvc-BCFMj71ZO?1oq6)@{a2
+?5;!CR_ZO@pj4yTKVmul?AKX!9x??*^O;@aw+d5CKqB5=(nm#GuQA{LS3bBd$?lt5G5}xe~Mrm}Itgl
+9g1o9d!5A*mUDG<#CkNCu|WAE2H3SRbXH>rzSbt1o5X45TdS}XC*beYT^P~hm~e_^4kJU}h68i8$6h3
+eaUavPQIBqE-Pf<z5UCLA>|CvxsAVutViv^Tt??paOlq<==uT639Y>uEIeZbekfGh(ui^OEyDG|#8mR
+ItZ)wWg}Q-E66-=3qhmb=2nJDFglizZWNXHs%B3m*Ncx^kXEX0|tX6kb_Z&?uYOok;FVAA}W2!dLPC>
+exScNC`j~`I`(HH`r?4S`Dp|5ZYhBNhfhIS_5W~g9}mUDwhK0BiA|=4mj%*8_V_Vy_Sez3Z{Gd!{hu_
+{rvoR54+rMiKk*5Kvfb(rhR36)<7X$&WeUduN4}uwH@>=B(j5|Uu_IO3Zbgruqk_B^o25D^6^wCMECR
+R`G|W~%K%ctM$r1iBgg-_n_(vc97{Ygh=kVQN2;cQT3Z@*$(KDl?g6a3_EeB#NqDOy`GY}Z>43A<G?_
+Gh-$gwt{zbmxrg3>iU)rA>m^wc<L1E_C|9DIt^J*kcWYm;AuQ9J5j`omXIs(|x@$b=S01V|X=7q<1Fj
+Nyp2MJXBnA&_nHOt!)c7U5@+JYYx3c|HMV$xRKX{?K^O8FB8M=xoO<C;<>vMxcz&+hO!J;`X!s;b?!S
++A^{&s~hvDZ%}eVtI$`TU@pMntikl+q(&M#RNM|ZgdbC7p_27`IQ=*Hz7SW;*9fCdGfkrlCLVZemal^
+KJ^bNn)L2v_kA}ER{?0>!+<{wRM<nO-41v`Tf7(RkORfBHJ}1R92cp|5fZTAs+79c9=yg^yduF{l<3U
+SuY8qkGIpy*d>I1YSo+vcuq8a)$kqZ5~O<~p>L6qtlfBwA<PO7Ig4xY~O-3A~|IHj)yt**@Q<3DV3PT
+re^1=79`yB<1g660eYIng6dM2O?<N>ox>(qDPrQs7dI=uIGc;WgK8y^?nqd4PTzvv{>GZ_SiQwFU}l&
+=Yg*R?ohgj5ic{$qq)XM$Ovae<Psz@kMIh4&FP-@pg-cQd)&<VcIg%Aq42gRlM+zC|1#$&xSR^xAOJ9
+E1K?jfZiUOCXOzA*|2kujNY`Nn$qZcKy1w!p9b_-e<r-<(7B)?bMVqvu&Jkl?nJ27d_~$-etAlmF=D6
+56yUAzqW+HFmx>5iedGt<zcqSD>-~p*4;ROe_0vFiEihP?3ZlY`hOI|4_RX;2-w-96o^7N<)5{0}3&9
+iaqF+6u%LDC>fxFG~ZKugUqa2^;DmRNo&t;Jmu^KNsH14Fs{Arc%&{dRznI{+MVCx4zxZ>4!o#V^vX3
+Xx!{JpPOF3t1|3T$h@x17s9^Gg%lJ}HT?*n(a<ka%anEQfriYuri7Utqvfarpugc;k7{q!4rLB|8qv;
+Z+GFVO)-lQF%B)$XN!wD`yd$Z+rz^3LX9SWqg6bF<Fn3;<(NPR3b^k-k@)if0f=!!$--Uusam^(Iq}*
+`*?+~JUm9RUFX=Jlwd$+WwbQG+Hq^c^GxR%&K#l=-$XQfRL-~D2vT)x*LtAz2>X)9|KK(m=duKDHU~!
+c?j?K1uXo7)edvG0L;hC36&@>v1KlPrjzqg1Mz8+z!E$Ed9f!{06AZ>SL+~A$0D7?qi+3D8R5KRfvV7
+=G{!(Yz12~tJ&!fEON!PG-KAa|{(mgWn-1Ssr25d%7(suJlQcR5UMsAJ~U5T{s)qtl!Yz3T%9%e?{z|
+pjI?QK$gbE!RTee?TPmrXmMSwT<P{n(qGsd%m<bfLum;I0Omsw|6^YHH@3d2fS^qz_?YMJ0s|9a7=h8
+o@Hdtwk}beTM~@p|&QSJ9xM23^6nkfqLP*v9n7Iu7cHyfu}G~oh#|T>Ja7pjpG{)&WK(de~KkRf0HBQ
+$n*~{e53Vd3p#r6I8`h}#0I&x_%CX=S!Jq-X7Ppi>*3&IuqF!Coggmo+x901jkgd(uMN^`g!;)rJdpN
+gKfgLDK}vYwnwv7rke}trlrO74ynQ|BA3tTsy{DDaoU^O85MGGLP=ODpry!b26JgWBEYX(pkRi9cUWn
+R?zSW|rSS72-RXikU5fWuogxB1Qi)gP0V7{jYP(+DnVO>ROvfeE5@JPjP^+qtRdg`-<H$1@<O(xfI^a
+;8&nY1kQx(YPk;3)bgMP8~>HiZsw#1iF&xz4}0Z+Ozamyo~l4c)G}Y3D&Y@lHAhI-QEar*sGUf~5TIc
+sLvkhfklMJngvgl?)a>azP1N89AtF1DEKkHao$4lvSm74g{Ppj8}RCu>gPG+Sd)=EEdTup~$BU2jhFt
+xPel;Gg;aR5X!4j+k%7wFU)(|(#yKgh4D#)8q7ZvFyggj8}&#)67gpkJpvW{^Oe7vXXjBzRc~Z-KD{z
+c7ZH^UKaVnO>u*hf0%!-H5%b0I3O0r-`DBgxu<|rECLz8MEw1U=*Vhe7e5W8@-YMj(K72n`Qj&&z9am
+Mt9#T-sHsM~!VIW#ZErP!mH5!IW{*uZ^8YKZ9Hu3KbXtBz_dci~4b~UIL4SdCez0)L(^4s9;e|?r7!d
+JT*@tKlM4Elsl`Gs?M!v0(HEFL#~$I9Os+4(#54nEyoRGt|bq6l1yp6a%|q5AKhR#Gn}n6+&wD$F7i7
+k7e^6Wvi)dw)R*$BJAy&G>lmY|uQ#xKg!LD-Cq(7nct5^cgwa+a;qeBpQ-2no}6o)w22O@;YP&-0|^o
+|BkvLX*E!duPO^r)6!CSK74jpDF8Jhro~`vRL&(1r|J31y+Nb5Kq}m^Hn~nAx6<*^jCOk!jrp#YyyRs
+grRXKts}Q)%X>S0{8i7l?p)OKZ!p^<<fT<#86%lh#7gSz`ZW{TWz}<=@IdDjZ5A2%>qN%VorUISZoKf
+^5JXGS4+9c+I->dQ}{N+^eUT6%B6kQVqx0M17lWfiRohtRjQ>ZP&^+Bce+)o%4l9yczNq-mFHIth$rF
+VHlr}n|7I@~>ri_n`pwJ#e8i9!(s>pgQu%ByJ3RYFj-s%N?L1tDvj*u23YomEE_s5+Up)x!akQ571zb
+Z{W!_GFml-31ebShj-Q`0l;u!XNM>AbvW#NqzKUgt&;x7UGrgBO`ns!68vbpW;FwS(JH1hj?Q9IHO0&
+dm$lsk5|+@sv^a^B=j9;guQy^2wOFqjb+6nER6>9#eK%|Z!vp3&mQyBVkZ+*qhQOuJz?PB@VFxy8b<8
+80We3>;D-f-Gbd>`8>rgnGt@@>@18BQpU#l>$wmwiF-!TB8d_-U-JG8Ot<p(bSFmQr;I{6;n*-Jt4`l
+&A53v2ct@48d;NOoU11R6{PUZ4yhtc0DI9;Mjk(@*zvg=4r;e^x+dAe$$1UxaI9fgCfrgL%1J_yKzLZ
+uCdAncwYvb5}u2Syf-5}RNns`pOWeh>c-Vajz;FU`DyzJ(^PaK-GhR`Rw5`lr|HQ#O32^ThGL2kbRT8
+Z6ePh(!`IKHY3!Ny~h6KJlfw<|AZT`fVEx<x$~Y$vLw)=X;!lF53`}Q#}C0{f{=-hu=u@AKjw{8>X@5
+M>FHMq8yYw;)4&R+%ojG0vLFStcIr=R^A6?x3eFURkDoo<*hsSjDw2GUyhu-{+g1YXh8;x%;qpw_XfE
+4!2zX!>Dw+_L<wEfEZrj@uuNC5x)El`RC>eL3G|*cE;fMbxSPay_U?iKU%fqtnt(0khwJz%Exm-bC}V
+y?dL&=hkOB7&^2k$9o#+gcDG(|nxQkCZVwdzI=0@)G0)Hj6nj#E(l4dErip~~<Im5HVX%t8g(4Ray>2
+O#TD@lMzJH)Jpm`Ap6-PquiyS%g)43-Cx+|`k9_NM9;cP>nYS#S2<?V;SuKzoRyf_^%aATK23F6I6$q
+Rp}tBS-<b!VY)S=U!)uK1!(%x@w#$IusfmDeAC_JXF-*G&@!dm;ZbRizXdEb6!pzjmv|S6!4U50MZ)b
+F60iGcN}PnhH=~=g(75KHgq*}vxZ&1r5t8l%ybnh152uOlM_f|ao8ID^1@#7fHHJK=^>F?%XDoNjOXW
+9M|O;RHx=<9EBa$MasX!IW)+`G0UXJ3p-b=q^zpmL%^C&4@D_|ZQ2sEmQ^cw;>i>ps5O6SVac?NCBB9
+&Z79ngtY+ktU4kPDX{1AHC1f)#}SJe1T;AzHh&cv_2^b$Eg!&hg54@=;aNg4*dh)die@Ec-o{qT@Al&
+|bj&Ca6s6L|2^>f6tICNHi^g^uQTiElQLW9`8@YOnp~h@7RgVh$a5TFp;$X8EJMeU9CAl9-%^G#6n|+
+?1c-{0YqzoGOjAHE_l5uG6K#AlCF_m-$ik2&$B*I}T@Z?{8mvJ@j77R(t$HgE8{=zW@F44z}DlN2<ZG
+lUq6MuJ-%A@d&pAM}vL^T2J^Y`ZiKwS^=;H;~qQGdo7O{o$KuWp(AtOG#EdJ-G|^{EFD|#$kwYie8x9
+KM<=R|mjd8c8V*OIl0yIY5-Tw0SP8(Y-Pa9v!zBg{ikN&?m2pH;RqPq;1jfA7u)36pBlPKQx-EqU&-%
+_|cs1`&)D#+wPquRZ{G>M!rJmN7dOGN#mew{p>>nfhgX1m5Mt!XSRb}je2Y)$`ii+E;UKW$5W@am*P9
+O=xvD{L>u*#lw1ftIHdAQt|Z4Q{kxEf4id<QUz@fQG-81pYes1Rch72;2C{^$EQ?<cRn`^&rcQ1V25y
+9FoW)$d>b;mzA<I)n8@#5P0d45C4aDdE90OgHK5(g0@|e?f4DU*Qz~+rTN@2lc=L0vJov^6v%&z{5&o
+b!C2stIt)J{uQ+*Pb=UZBvs8ysiSK5zVbi*d9M7+J6HBKt<#nj9eb5p-KJNK3VhNGJZ5mlhhXWJ_&QS
+inA0<EVoX*^I+2YWyL(A-)#`;&tm@(~Z|c!paIA93^|gCVL#a_CG3hs%d2i2c-~W<lQs5eVSd_Q;0_0
+`^^PnsD@EF|(WxR}UB5^0ux^B_xQIdPbjj$k&3hpGtm0~;S0VI0sY>i6?%plvYh;#(EdqEt7I&sut8J
+P7sH_>_>r*qiM-q2xiTe1G~B^!)!C*8k!3!f&HRb8V}jTVAdw*=5Mh2$yu;Y8`;b59p(c^3&SR0>f;V
+(|#}rjU!v{^{wyy45O>VYm}yPy%(Fi=YpX;|ExDB!Z2a>W?mmP=7L$mOt}^4JxL@OQUM-U3i!gqQ~w<
+_~08521DRt-EX4&At_*+8|8B;IT%d3eFzh9k$`*HIz4Uiqa_4i71xKS;4{W?Y7YxNWid}?bW~5*<0F=
+D=)4J~8uMv5g?g;`lz`gIb$$E-)Mg&vfLstdUj2e&m-r7yK(`G^`|%24YWa*!K87Qsy3tC|dfma-Ayq
+{LRg_S$jx)-#Y-o`A5Z1(KD@M{SY{@Y(2bv@?xlCj-yMPSI#Fbb6VCDK&9GpzEtN3UdFEa52q}4}BY?
+(D8Mz60O=~G&ZR>NVCuc<Dw4V^C`4O+`n0d>I-Xz9>;mf<$VR_2~>xlk5oM&p4J(7`iX%?w@-*`QkE`
+T>notsb5+{|#Sx_ZSL<HBe~Nx58+_`p)+^TAN^YAqgA?(z5`ThWykhmB#WxvH$%P9K@eC36z7s*UT|*
+RkXxd-{oai+vWGtIDer&pZ<sL^Xch-1QbKqui$!c$l7Y8pQsK7&T~vcFG8nfqWcPTDbmXY$H89fEP=Y
+t*LS=f>cSx4FofPX{!ZR*>#EqTn0RH9YyXP_6Ol~l{arUVQq5DCy!d)eQDTOO^mV(igD)l061ZpxwbT
+(wBslpUV*I{*<fONHzyMyiYX*Qrv-plHiXLAmen{z8xN2L?%Y)g6FJv1YZgM~1#HKYt>q*s`(66-#{T
+dLo16bUyyBwyACvKBIt24yG@We!raddbsb`*B|%0=R?k?ctzs38cvcmd{kT=na}Wcb3IZ_<(Kgc#!V*
+kfA~bvzzQVRl*n3-Bpo9K517Xg?oboZyziCH(I-h;d@i#LHCxDAoo~bPEpv94FYgCvqG=g!ho+fVkud
+eMe{L5lRmClvP-ApAq*>2dhoYx8TrerLo|u(6nLqDzM)`i41CHm%b8gH-kIa*^X*Aw4?iiyM;yZttkN
+h))@uw`G%5zFZZ!BxJ^i9Zm<*8KsV9FC`a9##hoYRKMP}R&fCJBw7h6c>V=93`Xx#lS{p3JCniwN<Ml
+F{$)3<7p9O!cBuOoFw*pqP7{?!K@Avm*`YRD6w{U--C*;k^Cm?T1HUsXYUZxO8A-+h`R3^cca{r{>A?
+IeN*vu})L&uvbZ@x4VC}%LWu-N|TKF0x)LgMb)I?wQu2%nr%1ak?}V?J!?hG`qvH_8I{WNEIkv@PYWZ
+cip~C{-b3!N^SJ*=D+oDTFhTuWuS}g?nZA8G&BJx74qzO6w}_N2NZZ4#Z$W@fG2??dvFh5~-3w@V`)#
+Pb&6r0nU;2su;?TrEupWOLZ|kRJiH*)uQpY>j7@~!>u9FS26eAxau$GoW~^|7%rj;80d<DcD`v=Rd&|
+<&W;!dPonA+rcSV+bEee~cE`)>^h)tpW!n`!dJ^n@6G^(Mt@xlR9~0kN1rwJ;T_R@Bj0G-q7AWlp%z_
+WC*2j?T`4H~zX{bRwkn`vS`eRt+9ebswV!ygGoCo$qexZk6ERxpucY(+Zy`3v?GM#M>_hyaLmoJ5qX?
+w#a10g`6I7GI=L+nNh+BCVikcf~V1ohAcy~&<dzOTg(9dXw~mu0=DRprJ|jDT^4(yslId$TfaV~f+;v
+d(J6`|<SRta-KwQWIcPtw;UiA)Ny`J1tY^_(75IzzBr)=JW>CaP*P<Fbx~YcZ&TkJ02p)*T=m8IqHq)
+p2?Z)YIN(gWqT#Y<+U9i(13w?bF=0b9r*LJseCN0iMLX7lzk_yjft0P4qQFb$q@W51y<8HGJQ@1K#(<
+0iZ830Q1@XPH)7&|_zPWSJyY@Jyr|5PcF)clD6@h%b@jJKb?fBcHZQukvI@hN8W0akr+LK6%eAha1R}
+(xr@({5m4>u&2e{JXr_aaq=2JafDQ-1YR<fAJa}H11<2$JsrPO7umM;t!uat-hxYG6bmjG3&vV}x0;e
+DV=$Da+V)EL0vtzW&&e1==U4|(}eiMuuI_Xnr*>B;G7tLhS<Dm)rK9rd4$$D;u(`opKshbK?R&-=&EP
+e#w54#rQ9Pln^C<Gy>I+vKLAYC_yo4D(lcO)*^jF1Hjb?<j6tlYRw1y$ARyMy|{{zoMW1ihkM%{d9Z>
+^wX~lpfWND2Dio%s!}{2`RohBRpy@oEcPpV@Y#eyH4y(jvAI_-YH?7%DlA9yzF5N-op(WUtQ10#fZ4r
+|5RiZ;gm)PwXovulE~2OuYeXzliF$K1Ec`~^^p*ozo;-W5XW3ssRgD*u<@4%+t9{~Vg3|{b+@#4*8w|
+A03}iOrW{SpD6%CID&yPRDa2`J$@P}o`cN|m{V$(tW1%e~*`oQz&0!RMY?vj@GxJ&x$>)7hE%j?+MZ&
+JxLzYNUd9mUW98Cr^APz|ehHJYq23qPa;M!<#uU3kH!eLr7#ksRJ(k5X6i?79}ARz+hJ$JQ=Sb;MHnr
+g$OYcu3iM{Bl7BP-N;I_=oTk)D;D~rcfo{5|J6RkcQD6JXp!%)ij=C{FN7lwzfacCF`Pi+hjywt3O*N
+gjodyaC=1JvDM8YGS6bZ)dt~4x!7Grk_$OrZCQ+;385osQK4=B=@E#vnf#xRh5WH%A3lx8z`|{?XpTo
+6doX;oL9_0Eg-5V*2#e&V%of>ZOGJ}YbzEj6J)#Zg$$PW*p<A1>eMRKW9@bD$D=@5>_z~eYbt`vu12-
+59^~eqHG;=I^C;g5)gN&f01xqf2OkWz7T()~hSaNwP^3bs4TY6Z5O1`BtUpp#!Ls2U!{K7%W>qg5Os=
+Lc#7dcdnQ2@VuH>nxZ`IiYvZj95fkmUcCkmSY){|ZTNllc{ryc&{R9gJ-ajQ<KrZXu4<GW2snl2?p>Z
+44z8jG!52>7`BM4#A4hW<S`BBw+d3Ho*G$o&f7IEsu8mPOPjJRefxpALF+0Xn=4|TR_!AhtF8K5?NhW
+I6Yl#2UV}2zo-IM*X0-RIo<k4zXEU_`tE=!JqYBwWN+Ih%7t8)ELP`U$RnT6kb9vOiSfiBXVzk_CTJm
+--jNs$51L(%A1Ky|2WGB*rhr7%AN5teOZ-h>+Q)RA)xtt|sAy#LU9r$zb~G|N^y5JRwUrfVl+z*$f5w
+~^*@Vn#3G!E+(-JIh3rt{(>hLZBnq#cL%i^@y<l>m@I_8iMbQ;EG=n9^n5IBg}K;@##e4EH2*Kjl8-U
+HA{%PdPZgtkWrscgwVzI%d>N#$Wc#4Cu0LBy}4S`=~o`Ln}_Z%;--L$+IXPvf9fHBzivn`(nspA}8~D
+?|t14ea|qh~3Rc#*4)LHwODwviTM4yC!ZJ3<g#RvNn<I-xTfJ%5e=&pkD^scawhXGC_Vev~S#d(9eMr
+wLg+y2j1*?iT{O3hImY{?TMz#nAf75OS!sRo~*osiPzMbY*WU>U}Gu|$AYKLKay8{_(QhA%Lp*F?aaW
+Scc3Hh+l{l@WSXVDou@9Z(&23AGTG!4MA^|apLnBiOHclA@mt%$_Dx+#<JZiwuPbvbs8u~{pmsolw=3
+Fk?&DqI-1Sar5ufyNbKT#D%5GFy^s&lGV@P&gm2YEQBz5!mEk9EP26pvF&t0T)U1~t=ja|fWXLopdG(
+4^^F@!RsYG#bk#IXzK?vxrJLpA+c%!uV@)dln@)kB-t%AO$XL$a#vq+rbce#B*5S?`2?e;R5Jf2bat5
+ifA;)g0>|Dw_<~e6_9S>#CY>wsHHmU(wIECtC#c_6-$y?hdGzRq9XJnVztF);X7pJFm^c-v0+sO9KQH
+000080P~d=M_?WYjb;G=00jd802lxO0B~t=FJE?LZe(wAFLGrqc4cm4Z*nehd3933Zo)7Oz4t3Tc1Sw
+WO6-WlXY7Pf6u>PWA+3^tK-$O8Nz+nkEQi!_-gBOxOP9WL0XkpM5c$Ux_SBDIYr6e}I9&soqBx;L*f6
+#3bvV^}@8egF=Oa0*FDR92$e>bXSrkWX;OcdYvRoIyt9N9BTztjc8XQ(Pw8zfX{R6fKgWCQKiQS@1cv
+e^Aoq}BDY6&z}YdQyw1+r0;As<+Sj570mTv^sk_;*J(p_WW8Cm=Nwf_yBLnX_6+QF5bpm*5a5OoF_Fr
+#p}b&4FzVLUdvYwhNr<_2JB2CnP^~wTA3jz7Qq23L7Fu0|!SY2dS?wn!#H^)(35jU`iEu&-BX0Og1*$
+gV>dmTcaCnSsZa4Hz_`9Y}<-#(}`KP|8!cgQA)*FDcEd4C>8Z6B?c;A8?-V`PP_J2!rKNupH=hkFhWP
+zcC|~z(tO1ep68v;XkHM8K4*;4u1xXx_w8R$O9KQH000080P~d=M<IK>3eO1u0Q4pR02crN0B~t=FJE
+?LZe(wAFLGsca(QWPXD)Dgy;}co<F*z5-G2q){4hD^Q=D9j4%u8ZyGzkvZ89WjZ^i8(&=M8%PL?#1ie
+q&B-}fG=FQi1-xgwi!xLD-l=Y4;Cq<nNl-WKIf@Q1Y`XN%J}ui?M1$SvKGABrqtVsdl@|HzL#X1Qbus
+d5-3YR$++Nn`j^&&<g$Oh{hj<Sbl}012k`)O7Y2gxD1o*#K@{C{jrV7+jK+XN<6?n3am~oW#YZ%s9<s
+Mz&n7HJ=(KM2uf+jAErIkW*Mt!c%G!5~^yU*GiS=CnwwOHl$kEun-R?nIV%WKVH4PyuQ194FcB-e$F!
+{B@yi36&IlFYDZ`Z0>yL%VrFzp3PI?DU@)f&6nHDR;`zgz$Rbr+Dj32fTq?m=m2&LWhzDBR1Yj|mlj+
+4Bxw@N@zg^s2-OUl?{nh;sH$UH#_ZPRf7uWY!mv`jmmb|^Wes^_$b#o0*-;;~$U&-IEuHVfG18V^v_E
+ZYg0L0{INtS4{-7)5fkQPQHS+ba?JO<VBhl)Nh@=!c7k%LC0WMac5dQ^gd3BqK2!xdF}XrNa(nE=Z|C
+?bHo*i2GUY)Hw=FnVM#pe4d-$)6fZxs%OT#W$?|E@;eFH2yr9OpcH!+|VbUD{?~4z|tJU;~zV<2GhL>
+&qz`*ng2<VHGKr<Tl8TVPJX)hXY~H!>Ygmg>EfF=-+XoY=d(9}o3Ir8ti&!jPN;a0b8>u~eBRE^Cj>s
+z=~VxH$Kpaz1;VFQ9;4=<GR07pisp(_#iTaEEkdE$iq$6;E4}!;k_x&Z2LGIth2)4pGXewEhUH3<l1g
+cUX&(Ygc1}LC-L?>kclDSIjUr&is?^3^6_8ioY1PrM_19oVs3eax;|UlrSM*6Q<0;8l9%v0_WC=FZYh
+s#Zm=b2<!{TFYHvFJrw}O_D#0X?T3UlqGRtf?jL<$LW&P=nkWJ#0;$BkQBuh<`ux)OP-b1(~0s33&})
+;gDSy{2W+%~~^!+^uScie-`wmHdBc=f>zusS0TjXAnXi9}_u^PAG8{@tmtD3M9+Y`9M%2RZ%XliySt9
+E#=C5^^--Al@V2nZAum8)n>)SLGpxUbT>wY45O4kv1BiWsYlU<=a!)DD1F+y7>bQxm~CL52$EhX8^oD
+yANB%5iH2FM9a*%gGR3h>3mep-7#C0HZB*ou**>AN9HfN30XU9|htc1GeNQ3e$9#k3aKBp)5q#nh38G
+cd&La%9Fd7LNDCA#bT^gH)?G^EK!j2J*anjMIcZ!9>!i@Wo^;{3I3rqv1(X2rSsT=p9bioTy9ga0T1n
+VaEA=0P5aMsK?XuUJ^K!<7Ki0hdiau^aF<kUWNPTNOxfM@&Q<Ll2`<lAx`AsKS@6CUKMJ=QifUanr(^
+(cINx@>g(W(^NvyU8wfkI4PayBk6iXcE@YqU(0VWD;31=+Jl}q($vabb>V{MuIdSKDOig(;g2ND5ATa
+c32_qg?XZPvQQ7x6cwnEtR2*w`+pQ(19-r3(H`6e1uW2#cOWdQVH&@Tug>UZl~7V^3GIRw=o}ikU62O
+7;h8=gq<FOHG@IFc<sMcK42}m%m|j5x%npVIhLSfloQZb4-CT5N)Vf)Hkm<D-PRzKK*Kh9**YTrX70H
+aT(siMy3OB%b`tsD%FXCG-9b@dTrvm}T%itFY<8m7jX}tIi#PU?EFOtVzKPJvIvP%cpvLo+hm!2Nau6
+p%Yc7a}dv#V3>aCX(K-0Z?ZC%ep%4o;|T3x~HW%Gy_W>S-GHs2F*umu8A)Te_1#(4I8+$nVD)4i0LCK
+8o>lXK1lttajwG#ZbGFNuLEwpSw@}n&LUS*QB-LIeUbf{-t`i7`>5x&+E3}7@W>PcPtxjUv*fNf<e8k
+WN&}012EE;Cr}H8R4v6uS|`xybb5!yTx08Ng$A*Saq{hQY!?oj>jwPP2h9<IG|Ij{->a)u;(w+o-a!$
+4ZG}&IsaI*uz^Cgz&K`x$(V^8dFHALiIsM)hcrT_wK@mc;3^r6KL99(EpB9TwBy0P7L8}{i3^<{iqJl
+jM?{LxlE2g%LK@p?z+DPdWv470!B(^usmlxTfkICHc3Gk|{*9drYF_U`7=ebGa!P#%_Kfuj8y@z}B%W
+2<!ug#Em-0(8fHHJwi?Y<fhJ3;7Buzb_5W{&kSXf%5mOa60?YOZDNl0AAteMN#Xg05LQCc#(<dreMfG
+k1H@eE&)o?rzY^vluwv+6K^#gVyGSya>3!{tQ~=F{r)98)@#25!YjYZ}%KwM=UeG;AIVL@87j;4T)u}
+da1qu>sC4~&w-`)qOw-Xbx~!ByRtcS+Dma7bTE7^t!vtFO=Z^$^bMdF|7SVcaW%bY_(-#geK`HtMeeY
+D8HF<nyI8Zq<#OE~wFc1l)kg0$pZAYzmJM`XM!JqH306Y^wIvB~AjJQI8N?jkSy_2ExE?(1YRnp6FQP
+MC^+1{7oqMy>FUdvSQKfm%7j;hh38=m0-t-%r4>_n62Kr9I0VakDlNQ138B(a#kSVIvyYc6#KI@p{xD
+#;0Q0uv?@g^KF0$(56<FvQSIpj7dnw^vT3bc0#!5=JhK4bKoYTRI+kx|X(2+v)hfO${bxJSR8ruJFO*
+c?I~-)+NrO%>1=z~;^+OXg%%Df69ts-$_<O(pX(G-S3<hEX!iSa-F4p>rujAzJTG4zD+j;<7t*ewiD5
+UhrNq`Xx8>ct+|PqGF>%$BW_xX;sjFU+H7`VqRkpCDajG@3VTvi4N0n(F^%rjd_Jdrg?eR<Z1iv3>P%
+r$VJ}F6PCxcgzBl^8B<*oF-RzZbO0~+-Z0~@92iC_&}yW-5F6;W>Xg-m*E*{-Yw`C9xphQN7+#y`t2X
+FQ*LD9@#Bn9?2M$Rka1J5Z6N@Wc6SfMp_1k?3XqXR+l9Nn!QAh6?ftO~jJ2fca`(D;KOn}yT?j&wX+1
+&e9YoxusHMT`-IA+$rFX64H>$m^0zR;?()v|fm;$5XZ?s(sz1lrEUlg*1-gyxCnNNus|WNuDxD~4V(w
+I%eb(xl#=0>}H(dG?2XF+t2{RTBOb3Z|C_o%=s5KF+OZfF8toJUh^mbSFO(L{@kTR*E`2E#E%F2sP@o
+c7;4Azb$^B6Rqv}zw+Nsf1idaW?sEB_G5lJ4L=n;589us)YOq$1+HqdXDHVf;}={E-w#RAahkdB^o`d
+pdr8?ACp)y9@`Nb>C~6q;=9gl196s<z48*EDTr&_Y8g?RDa(hl+sdkSxo>U$mH<s%(`2eQ-=-SI+KKU
+<DO9KQH000080P~d=M`CG)PT?E?0N`@~01^NI0B~t=FJE?LZe(wAFLP;lE^v9RJ^gdrHnP9#ufS8MW9
+m-jOPsdzUZ%dOVk?c_`Rhqe)4p7WLy?fgnj%?(w5_|j|NZSQ022I^EXR|H2w-=ySnMtqAh{e4haa;b4
+6=X=wu)2sn8J0mW?t-bmTlaOrFkS+9I@M7wt=5h=0-kyGaL>FgKyY*ob1wIy~&v6jo9g{*RQ^V|9)Wa
+c=U(64FHR0JQZ;?_y(YFdAbdR2!Ko=*oLQkxnt|pjWX_!*(&9n#Vh7*+;q*yEQ_J59ZLwG#mmeMB4o`
+4=8_$d1K4Z>ghaf`9^I4^XRZ*j7q~!?`LUO8d6c;s_P7c{7^9VKI2$e$#&AU4_}mSF9<Yn8x{*BwFyb
+5}z(tk@9#R<tkQe4Y@}Zl<U>m4D5S_#!fE*Cc1&jl^9kXrh2P^#LG`J*RhJo0OnI9nWWu8H$z)Futhy
+`Q$PnZwELu3tT;0)1-OAZMN`$<r+nG%)2n#WDNt&S!@o>qApLARW+{1`+>z5c<y3~LZ!6^CK`D0Lw61
+00ulOVf7;t?n{@;4~yT?NOWo$C3}6i=>=l)g(4<7_uc-l7p^6&TeH?De@pP(1XAYSrVtzX??V|Wa7h=
+EpFc5eVWXtY_?#x^P6917t;$ioGhR`9J5ceyAL-X?-)SLC)ansvzzy9a{W8|`|SE+%%;EH&Zmn7yO{&
+A+12gkYznor>+{Qx7qjbM*gL?yzPV$Uv#Z%1kiEO1E)}uabb;iqrt|X;P@KG*UC!=)9|NiPv%71g`~G
+ImChT@Hznh(ZyqwJ0?Z^4;&0-2HTma$g+4cK5^fSGhUf<c!FVwN=uTWr%50lGF><p+)KEfF0$R#_!x&
+3`U`{lzO`*3r4F@?%^Q{Zm$?s6(SgQ1;YPG(nQb}_k{{4yo18=!+DL_o<S`}ARomDuM5{+-{=Zmw}K=
+Qr1Pb105stn<49`O|DM9ka=Nwm`AGpWj^JFj268b3<eR^Li@jpwO#x10Y!bxR@3s*~N5n3Dg#dEC;H=
+?ZIG`g33D1D$nwiI}QuBs0pBPA`Zbk99bR=RQ)QCyey7Gp(_ILG?j`Nl+n%NRF}oBXv%g87lVQ0xH%Y
+c3ca1NVZ&*$pO&iX=Y+hU9jEKR4FT-|+CfJp-fR26?N{IqVK^TJU*#crQUKY3hP$cYOo4;3uU6poP|s
+1t?ZNHur$7N1vo*yaTE(AVePL&3>=c^cbT_{NQLGq%yd5z3LpKtw9U2)TW$Bpe1MseEo)!&ImJMDAzO
+tysUs<2=r;Ms46{=eXk(=&x?Q)l)^swP-^4ntee^bTf)_oGeKly-%g12(DbOo1H?aW!o9Sh@VJ!;|6A
+|~bJ+2`1F2bZG;*Lf7+`qjcYjz@O9B8HRD&0w;&Hh7;j6X?>d;UDgUI~+;=@$Vb<KXUDJ!$)@~(5nmf
+<`hV0!Y-gnNx&E^>oxo7C-&y`h<(RiOAbq+NTRds1pH+T&T<)Y^u8MxTr0{iURfjp_8(`ue7kc5UOL&
+R?2@FzezRh7;0DL|=kpa<Q^-L{Em%cXwPyTFIhb0+6stvZmhP&sK*ajZ8jXrd{^ao_V>h6cD_AV1X&R
+?hly5*l-+_QCm3u|h6qBU&<M#*A`tePTmd``>8yF_Z!u5RzLIMjN#~6bHB@qZ^CVo9yz~Do=7_?K2b`
+TURYzL{3<-@NgF8zuFt9%}ED{{BIq&|~AN4AZX)Nz5u)lJqicLgqoDqv1Cpqv$*I-0m6=?;vkBcme<;
+xjgW0e+-bIG64}cNbiEX{jEAQZFEs%9LV^nZYjpVWo|AFhu+@@z%UV1G-?0iGc}`QXXb#XGp?<VjMnj
+Yh;iH`DVn@xa?=F5JzYg(L;O>_T@MNyRy5r5(5&5IN+a)rpWSj2=Ns}Qm_&*_aP2^fx)YiG=i-mf&}M
+PqmuG%{7?Y-D6$}dWy7wSQ>Zxy4Um4xF~-m^j!CBCx)$q50NW56O{Ek;F@IMOXZcrBoyxh~Gl?)KAw<
+#<e>}U6qspA-uozkfQOWwqE~r!oVXiDj9Z*JrBk`s?!A7MqW;KZjloZ?eV~?F1fG`YWpjwY=ZcGWTW_
+y~-sHV0!dxfyDU{F%HJ&OC_hB>w?LYAYgnJ6{%yX<8wQAc&IskUmZYPRL_CJVH_9d5WZ$p)=GlPm{Yx
+HDB$x~&_`&ix>5t|mB2T%A7;6$ZrMaEV>P^_LplUjY!Wq=Ugg2}`&qklk-lMIBcgkN8p-9FdVsLHL+2
+iCu6Q0yblz&j3A51KPX>(#9Gjk=HL(Lo1B+nYM;%K=vjtum<}f>y!0GvE3Mpj*qrS?77z_9c6p{c8O4
+J)aC4Skq00>=58R(2SK$kf`3hK(1NMEY7Rq_R<H*<nYK~yA5a0&CDl5=dtF)Ke>$C+hE!Id{m|C1b6f
+hN!lx{Prz-%vb+e5OL3mmX6?KF$VOxNMRVmx3T^Gh8vEG4+;K@cC612^Spcl96D(bN1*k~n|-2zg`F_
+6M_`F48y=G7@%1(6RV5j~7uxP+Z=rY&}f=LuWr9OGc3hGBSd-emtE&vn8OR@Ok!>6F?H(Gh`!ds7)ui
+^;+ulu(_XaHj@Ug}IO7$4E7!6ZzM_jfutR(wH<l@ENnez-}-^O2X(6ot~~o$5+6#XlkX;&n2sWWkY)i
+|7%>>5n_)9Ll~)57}B8474&&Mm3`o}w)Zh6Wvcr<FfMc4&2Fm#Fu1F8jD+i9>At}>0?6*smt)niDyaK
+A-?fBusT;s1U)mToTSGDksp}BTgStd<#;_A@>ZVAIPz6z*KN32-^k8p|2I0cMkWA}Xjs3zi2U&?(r2|
+y~gCYEKM8=#4B8c#G&*K&ug%O3;Hl>m*{NEOWHGMe&I#S9-1XJ2<+gO#E)WlM;oIp5<>)pHSo*B@N2L
+I-gMwaHF_JKUJL~vhEHSJIoN#!r`?-PJ=uFg%t0dHbDl?7>nKTK0Rk`)Q}m~`2Kykdc@yyF}Ddj?!`b
+hO|9VC;NN2Gt-pt|(&Dj#;C?4J{V}H$y2A6c1E-<uR{Yrj*T}d644ieqag~MN+I2+&7u+;STkjKLAD!
+i@{D>oiNR=)k|O*sfjpGJr%;!Xpo#hSLBMKZZL3)1@NqW1=W$QvCxu2&oaIhX7F7uvRFr!jcXTJC<UZ
+?UdwI16g)+z$lKOU?~ykpGw7~0k|pF?5Igi1bjUWl!C1Uf2x9!ds2Po0@mE0zV<mWfs)~hW4)tKn)KJ
+2xt6o|w7tFUouv(}=n#B&<L6qc~+;1q}<o3g4G<6@H0#06ZQiJ}Y7INT9x^T8`(k~_jfIl51Qa4)jJ%
+e~EfaFCNDQ+wvEhIs?i3Wjn6Y+De4ZApU@4^(DkUu$p=5a>oNdP-pk{{ONg>34;Hr2GIvK<&71y3&mk
+7m}q_Tn()badCrDuQ1^IH^(f^?|68KaE_L4nTkUiu3)?V2}1G0Q~7D;oxaAm*ej-&l;pz1llEWb&gSC
+#2l*0h=%pzXcepzyjwyp<($g5vfPf4>{6~|p6ndFZiDleTW1~R2)Bl7{DmJItJ=UFKi_V5PPR!o7zhH
+_%2j|i5}PbbU@?jB1OA7bN(QkI21_hpvt2gtp&+C%@Un(1I#9QvgEyEmg^Lwb-oZ*<)Yt_`Cy{b4KHO
+bh-WDZ7Pr4^YG9L!egXXw0a@o>`$+vD0I@<tN*KH2E0-X>Es<*FZSJSIzHG<nE_#V1Z9CeU}SfPu01!
++$QcpIup_^whz1PN#lA-q#oCG@URLxc#ZjS#|qxVyVu@bp1WTeF6yL&#zqjq_h-RfI;xJ_-wp@v1m&b
+^GXew}E#r*E8cXkymX9f95<VnE$1VHjtDjsnSclY=sfAch56X@=oPNH=~B1Fg!+&ugLBx=0LDJb=d>v
+_<<Y7<nu0yE7fMJwmx)x1g0eM9!gCz2239*;ib6J`C8u>s;sqLldHh>5+uDSxs!U*<gWtPM=%R0;<%G
+P8XaqUv9fytS3epHihWqHdeP%6LQZ6*sA-gyy)zv7l7_k{W;g0pAmAX3Q*RT;%1dYW=U8*DYZ_;m244
+nde;3;=J`q&T$T`*MIqK`e!0eU&eN{!X`xhzcDvp#gzAvs6fw*H0(x_W51Z=vNvC^I^S$-E+w8?<#A6
+8wvv-_%kcgt?CzUOHOeoQXcKw584>D-P^ZS5L_!jNltA4!gT>Ryr>LX(u`Zjyb(dfR%qOygyomBC7_s
+2DSA+h{#)!!+@Xutip=1wt13LKlTEYRlYjSC{kK^Kzk6gnE{`#~@Nst)ngh>dU~qPu4gEP*2+EJCo&U
+guDZ(Zn2j)Dyv6w@+M{bnOt!gVw>mN5@RSQ2+uK#UPc3a;zHi?F!*TR)x*P;dfi|kZ!(-!4^$dDF&qu
+r`Jz_M*l=Xr;iCh6dA1Ktj%g4_oY>IWwZ11(wJv-rc2J>z_?Zob*mHUcKxyuNi#2!85N2n`qf2K06fe
+OIk3j|)*qNm6`bgwI;vhN_)r5a8u0U}giJM0+={b*%?z=ddDO~R1XigI5;`z9FbY!Or@z`!t*!ALx?(
+<Pey0C*tcSqu8g)ToAGq0>#2ai>NPeTjZs>SQsN`(X>o`tb@G&(@I+oQY(p_}r4K6Z)^X9W*?EvmMV_
+WJ4Ss@l7Tw5+q`I@pWZzB2amHRxFV_N;Py!opEA1XY1(dkksTYWeP6a49_eS_`g>H7g4)RUN+Iew<%U
+s{!n3Z!Bw|;`zm~xaY9<07Jct<HbhbxoS1~f=avEwvYBOwI$r6)Rr)N)wZ3l=c=vJl`gfdig=&e7W<S
+o5_{D)<=~w}_O%pOk*lh%sytkI0WVH?)UJQJi_ZI85c`Lrg9Lbo3hyC-r}_R%2;j|5oZC|XZ`J-i_yB
+dPX&i&U=2?=)PwJHXx!7~ne9XviNN5)U-r?_ZAbUul`?=sN7h-*KA0OPv509J(q&4S0vGY<o@<zpN8h
+c!bBgY2VPrH{eLMPoX9LO#BgU2}auhw6M#-||LsQH?6H{2dYbFvg!>UtTBFYqRLwt49wzX~CmuCGE#k
+MAPDTZ&&}fM16ajqvL+^-}!BO9<d`>WlDS#{aqNZTJgme2nN>y_brS{N~F%6+opNO8nud6$qfbkh@Wy
+9D@zPYkl>7#x%N-w|fx<TswEpa)(a^?!#{nU%Min!va`^eN+gmaC?K;F7BQi#ul+-XCPY>^uAD5gV%%
+Eg3JNotVrVM>z#aRdi8)-)*c?wf;9C_?-p%ReRQZJ;Cf*!_yz)yV+r7^wJ*W#4rEL8E>q}`)uFZ^(T_
+9+iM+=eIv9MuIcVMMv4^U(_Zx(QZ(4*VwBIDk+e2T?DvUc$Rl_J74!4atBH`j)K8JJAjjj%T>tW$Ql`
+AP6r*$QMzvA_9`fS}ZC%Iewns*BKYG8FHy2<U+!?sudlmeco+|4+18;VAcnv2Bsbh;KU_;!gesNi$x`
+Z4hm>fpzX5r`Ns-%3J38xLl;9Sy2i-7&a25Ko`NAf8&p4#X^%Jc%pK{b1z6I}mm`Plpny&MXq?c}Y?|
+k^cFFFiytogvN~*Y8diTGDX)V{gW@x)b(!tK2WDgS8+J9uLpT^wfe%m8)3?gExxl*Dy3Gh*1ORNq8tU
+0Q+?12*m7;Dy*WRp7TFD@2WfUxc^5{%q+RK?d>>`co7s)mYS53#-f&jE%yB+H`>mjAXVx$RY31IA=#J
+U9>5zTPXw>5o%ENrm)4`WQ9DL{+vy3=&<v2s54V6r`;T5*YMuNnEC{MX;SA_yyLf~5vvKg+bZN5CN5V
+BpDsxa#?Ub>+IQ3Zr3-_)rNA=uJo+X$cuFS`a{mfJ>96uO2`spuGkS;iH+&<EFlz(*w<d>k4R%FvspP
+uW0C|252>G5^wRsu)&~HJw3_*+|A34e$qu*0D?;JU0y8CA}l6FMImeAo#v#vtu{fSvcu<2J8$mGPuWO
+snBwIL-Ag|%^6X;6`}*}+ccJMzL<KdUlGcqWt>O8qtj+ss-nq&YLJud(bDEfT=gE*nM(3<MMYc+CG4!
+60U3N5x^^@hw$jVeiMOz%(uTCkv(9S?OaGPc)T?zu1_633wfr2K2U3le#4SdjmVRX%VfC6b=v0>?14^
+5ejAa2_>n}W$$-q=A!$fEFZlOw8v(v&WKmZFBgVsxuL_r!SOQm9>SCj#SUZg5~!^p%^9-$jUjuJ6+2Z
+pC;MKire9aB1x9y9=`;TV3U8&3n(a7%a7jjCYU%nUulJ>Lln-!1;j{bQR`0Xn`-Km=jLCfG)KC-&nju
+r>+-J(snQfP`wp4+D|y$Cpqh{zJeYaagA8q|Ts*1JV#%hX%A9L^r%_H!+CoZUkxu_^Q)6cv1?$7fg%+
+`l886Uphf0<nty;Lp*Du3Yce1Rsn)Ih40gI02{TrUJY5XutQKLvoO&Z$l>`i^P`*YY<%+2q$63{LrIN
+oA1&h4bi#c!EnrV4mVr%LknX2XQ}sOhpC#*Gdb((OsHG*EDkE(9FqSpM=X9d)UaMUfBIK<IRDpTiHn`
+Tq|5Tx}wpEhhz5c>}I%BWRO~49sf}LWPIxA>jJt>2oWd-9=dv3NOv(yq7zSAeRa)nIL<;pCd#oNm=U6
+@RZNiSrd{<WZ^x#-r>EOv;M;h4P!Jz559B^j(e1$+|~RYWZmuhf&gk<XuqfC&dM2r3}}S_9y<tW*gTV
+=C!aEfFHNb%~nZA*OCo1sOYI&Yd(nhX^2>{+YWW#?SS-p$R&tx3%Yd&7VqGqp}KI1Csz%uiLI@137=6
+;`H84Wqg9^MKd?@-7t<gTe{x8fcR_EuYf9rpe5{-C!y<cD?RzgKVV;uKX8Gs$}1R$3(rmTK|-Ok@?uc
+a16@TUP;+c8TNsE_AL>89`chg-IosAUUyWJNft9a52MW!c{^IRjd|nGPty;zFoi@IuJVxx1sUQ_yFw~
+?}3td@Nn;z|;J^AysBiXAS$(5UV8?dC(?JdU`$|AEYBp(-2j9Rqk)Q)c~Rod0TM{Niy_rYJ>bS>n{h*
+xR`YDsw{XhRAktBzgi%NXv_of$Ot_4cxUI(snJ1OCK4$4Htc&r<Yj!61DEAc<=gPdS~MSXrW$w=v6>E
+oo$AI=0pXO*&IsmRO?(3hb5e83T@908`A$ye!$W?c_!xVAhQi$I6yQDy9-_P;*W+a!VeofY30a7mmsb
+=7h1Q3e^l6Q$WfyE^}{VlH~H#);3dQ1S?IkT>`6AB{PDh63;KGY#GTx$dnX?GYM|5^tPh{g<TQ5-N~?
+2dr1e%buxI6M$P5PS-6LEK~^zpoVjt&0&(CklM<$T8xl;5S6Q+~a1i4l1(TCnX&xwIZPCZuQ_96_y&1
+F9(c6~(keq{OmzJW3*NzO`-s+&HtZNFx)UsMxo{5&ol2H<EE2DpV5uXQiY?{CU6dOh~NMIGodR+!VK)
+6IxhEt+YDssdAWqrd%RV)rOnn(QcJCW@|E-}g@4-6cNBxK)H60%eKhl(Yx5@1ylwv#xaTqk%V5~VLPA
+7aOf3j**~U6-D=62M{}es1}0=GFpOOv?u@%XoncfyPE=<(5Rh2qLo|OOh3UZ`e#GEkfooOAzh}C|<`j
+&GL%tfJo+M3o^UTlx1+V0_#CyBNSSLnFi~MbUXAau{AH+UDrIP{-YZz!q6RLV<>A2g=sLe(s;e;3k%v
+q`6+;5mx%F4Z*~mU$BIIg0RS-OxoCQHBRroA)Y;T~On_4~3SQSasA&ujJpfNkfi<+qm<9kXXQ3&88b5
+bwUQKyO4yUG+L{e0etm;2$@pw+EZiyhL+gxOr2@qMu4+6L<bNJB6{}uVQk#l-Sx-oJDJaFUy`n5++Bo
+7=pa(ckXp%XdmwlKvY7ewGk10N4B@WE3CD7nnUh~J1n?`DmvWRZ7skhMSp*-6(Fi!^#~3KL-Zgn5WS_
+1`k(?!C!qH<RGM!yq!!zFWh8M8m3~$@}951RM6{l`Baisa8rrp~cvm>L$&Vuv%2K2``mgF;K2EFcHC6
+&jpWQR$;485*N+&A!t!#ax=h8RW7m`s2I+Ns(_7AXT0+hg%NL5$BtY!&(wl@`1)%up5U^37td9gi%os
+4#X5i||5$=bewteoLqL)mF6gTs9|_)>04?+QP{og9d3Rb`ATF15Cr+h`AGL>yuS(f{KPuyt$J9-Ph5s
+j*ClB^l!8+ppS-D;RG@snwPUp^Kv6%gGjUNtK)NkVFiI1PWX+ipUdoj72E)2QrPB8~@{pQlDa!$~}+E
+{lt0{kjE?5`9!Jz1iz+fLvbDLoZSMifciHyM(<SlX9i+FH7qX5kggHbp@#Pt9Pxy6r{*jLQntBA`>Je
+yL1ZsI$t)xS2~rH=={Sw-hKX@o{$+_}oS90pGfH@39w34W(!m^vQV9w}<=zwn6xTBXwt0eep(pIf$O!
+p`#{&Id<_bSdsDZ8|v2eF!Q0oIkib@RY}xl8*dOkI*#RqLg%i=q?43m2x|OMTR2K2<&z1l05lH<@D8M
+{Nf-T(*-6In_z1tcK{Y31W{t+|U;nN=Q&QoVu8e7;A^F^k<+(IUkZ#GU&p-9gqp9C8Vq<V|8mwUhLV+
+B#6!*Jr%phidr67#u-8~IY{^`VVsKIegs(}_k9~x>Qa94bw`~6H8U#g~s$C%VTFhoxrVxdW+<Qbi0fi
+Y?dfdEdl3{L8SR0Hl2mgJPLT0jkjk#j1AD<44`D^ZgUVX+`w(I##FQbpwFcAQha<U3`CXzRYJEc81uw
+1rFwh`<7hpOMfM9CG+Dhxi!)m2Xu*M1UE3i)|he`)Jm^!LLc^489vsK%Ma#pW{jBn6&z3G_9C0=j%`%
+=HS@JiYI8QGGR!8zCwZLk(BW|Ny44}C`B<i`H?Qe{<R3`;O(?>N_L_;I*~DI{V|WWX!2Lwh^uh&=bl+
+$o*N-i2waiRfcvbuC#w7nf%R_%Ab4&rxRz>kqUTCW#TwZ|_H92cJr13TwO(J&`B_m%#2b)uBF<RoZkN
+7G*=OJC9JB=$U`cK1PV#K^-G7}_G?z#X<k#@<u29C+hg~q4N%cOFJy45`JmP~jMm}bNQJ?;rok|*vPt
+_%u9ym{Qm3sb|h>!y|z#-&`%`C%>$2--sChv#bPH!3JWk)r`>+gSfO&EksNW}w$m?T%`7iMpM{E_`U=
+ajw1pHMJ>Bg0@vogIRpD3SxYCLEtt8#Vo+ZnKLd@{Ztc>TT%YP=5JMGp%@IKch*alK8*6X+RINe8bek
+fnb$jts;18Nn($lHi+oaSiWOY@sczK{H?InOt=xy+Mx4^$eRNpiUQ;#-twu#GLy9W(jAXfl5OtW%vyj
+VBov{LPUE-#kTkjMc*_CQ6)^clM2(En{78wY=)OoiTnX_j@Rg5Jec&lS5k(Intn8Q_p9}GpO(H3lp<Z
+}~`fGY5D5*1B{DPlB5Nvq`W0I>Z-8@4XK`+1{xUWYtdazeA-*QPCp{{xx?%m@^40=U(jd?Mq#h5=$5j
+<!Z($}yIV$jG_{Hi0~u-1|yS6h5k&Gd@O1JV=KmWCe{E(6bOhi&PdhmAw1;<;P-iGb2H8sFOrtCRaVs
+IK7Uo)$rk3CFJo1`+189%F2z?=KghVIXf(lOe-5UF<=nLz|S?w~9xFM}z+dP)h>@6aWAK2mtey7Dsmm
+g2n;?001-s001HY003}la4%nWWo~3|axY(PVRCC_a%^d0FJE72ZfSI1UoLQYjZiyo!!Qipa|!|->;Qo
+dP#|N6E}gmyicPmdB~hSgCqdD>SGE<w2|ARMNIkv}g|E*$$Yp&b7BEScc3|=lZB%-~&=V`|4Syj!ds6
+Ew-Bx-W%;-d8m@HH97~}vQ`Dh&;zy?8%jIzT^hf&~l*?M6nF3JNmS+>bg_;7SGF17phYc4i6;nHQHzr
+^mqC?f~>{;3PaNV}TeK<CeOsE5#Q_>ONBvT>S_+4i`!o4vW7pg|wy3b#`jMtJ8D8~DVLZ4%qTnL;BuR
+1)JH;6>PE(U{a7RdF0h3Ykj6Hn#hUswS1zJ#|{cvCJN0S7c3JuV3;X;Qv7j>B9lDv0qS20|XQR000O8
+^OY7yE)(3O#svTXiw^(*ApigXaA|NaUv_0~WN&gWUu|J>Yh`k5X<{#JVRCC_a&s<ld97D%Z`(Ey{;pq
+fa}Y>sU13evtqA4;+3F72vZQE=wm_B%j6^wHWKtt(JI2!gzB`hVB}YZSOh8~tyf@xG_waH6&z?MkLX|
+=;j$vi1XRqi>G?`3JrRBQO+``-WTX^x^OW<-Xw316MEi9S7<Jx3VH2?JW9b5_dFq=JmGkcKAf{@qXKP
+&yfWO)r)hLW3GuDRd1;kA+r16Bc(TTm6)TV?oh6ahZX7T;vPMh8Mkm_dAKvO^QYAtYvNvapd0S*fdUu
+cz?n<MZpZZ>d_<b<bQ+JTpx#Y?7iGr=UzG*^;ABSh=sH(A3OUN-u+c`|90<mu%-_|5K6<C6}zY6_WQQ
+!>4`8a0an#YEHRlcF)78K(NA~(wu{_8Y?`7THJAnkFxLMDJTu`M#vXOF_k-t@|<Coa<qIEm%L)D+UC8
+Iqefh)Wg}`%OVPq3zK$Yvt+WMZY`avZU3G~H@0h%Hx#8?i^9o(xtVC^v%!31rA*`av>g};RN~P7Zvj(
+m8v9m|~zTl08QztXmT4`@WRJ#JtVDdLxvx(oK=Ky|l*5D|63AfD9d0?r^Rn4bxv$8<g{#w?dTFHWzS!
+WlMIUZ+{?wr$u*<5J}6CcLJ?eUu0qLX04U{0a~l%<*$mX~0#N3ny}Obd2Xa}(tsPTpN&4*!k&NR5vNz
+@6?~$HRt0!Ck{WYOUj=>z?WXygB{!#~d1lB62b86k%M-G_PG?K8e<`fZGB^FMVShwk1wj;E8y)$gmPW
+WJgb@ND?I}Ix0d{%qTWW2^(Q=`w>1qQor*<0*0L*1ETpk^?yGh0VSvrR)cKqwKhsU?)FiI(BXZBuIWb
+=Mqxwh<8bQR;P+ND6g^kSGML$0&TGJrTn;kzIf~E|fd^?D`N7!4!6&=pTa%<|*En$&lLQLx;OHc;@Kd
+Ha#$50OrH~x*IK4*vb=yc0)C41}(zXCH&^wR*LN)F}2*UpMdG!(&wYp(7)?T>wVtShqZT0$wc6{(@6c
+sfy22L*Df4mizyK`YXXXmQq?=_Q#(#v)5PQm2}Z(;L1F}$wuO2M4E)b@ImcCv1RE`n}-0F*^eR0B(H3
+|n9yI>nQ-vx|@O*C_1*_yu{5Y3v1h`B|y@qVek!i~WoZ61L6>bZ&K~Sm{@nR(n=I0MZ22aP2B@2A-Y<
+S0^&)U36;RyD&&{Us0a_<8^<v?eE+#PG$Ht?eDn?GH2aykj0(W4lYk+8g=Pj3xnO~b1ktby)<K4=hCb
++@8ws?dW+CNqzj3}T-R!Y{ZERfgFyhQn?R6^P+1}(NoMLx7tXlPy2Pn)xkcOe?yg8;47T%um>(N_{*;
+G?{*WUF)<&LWh@0tG<&l%%i91|Ys5)0wg%qE{Q;7eH({y(kc-W7?I(wAey~O7o+P+wVH^@Oq7C`h-^E
+HwOHYID-fE5M8e2{y|H#ylssJ94CChj7c(B$UMvu5>*XQK2TGqRV4bz$#E&qjoh*Q)Rw*E;FW<!)EJe
+R%FmllY%-7)#TSkH-mx-6`Gcd=_(zC-z>+aF8CbMlhExxa4@@=03F~nfA%2Rrdjf>7@<Hxi0fTWI=C#
+^cl(`m;U$|eLoS$y3j|lRAQSTnc>wc?CiZ>QI_%o?-@E<n7Oi#`1^s1@w(&FBpn1lOk|&+-F6v*0UDI
+LsmI}>u`~*>VdD@xcW~*3x_%~Hh*#dKpT@%+_dDPAhB$f}B=`1WqJH;E8I43uf2yM6_`ST6NiUokU5h
+va<9)3KB7t7_cwyq6u>p;8Ix?KKi3zpa4Y30l?7=kszhZ5-Vbzq#G^U^Y{d3S9+=}BkMFx)_A*qwVLG
+tsydcU$?w9prtjo<#!7vwX4ilhx99jgPiK(d$hY2S+1>XMq}d8rEbFOL7_wuiXu_E3J%r$cnQM(O?sN
+=AJ5%cKXL)0RFNMEW04O9KQH000080P~d=N1Xbo@2&s<04@Ol03-ka0B~t=FJE?LZe(wAFJob2Xk}w>
+Zgg^QY%gD5X>MtBUtcb8d2NqD4uUWcMfW|$q#JgPE?k;uhzk=v1DVtT8lclmi^SVo1QHi)|9gM(M{8Z
+8ZK-6RgnMv9U`cTIiKIqLMyV%u1TFz#{6OMOsf7oybJwoj$)%RZ1U>r(JPs5?z7UPlTC1wUF=MGJI74
+w0XW@Yo*%uknfUaK%WpZ)sg>7e8ni;=IVvZK!V2(#~DwO&SFq2>iB-^V0XI2AW7}CCRUC=77fAsrqj@
+1`XO9KQH000080P~d=NAZw!T=@h50AvsV03!eZ0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gJCVQ_S1a
+xQRromWw7<Tem~-(Mjvl=X(iBwU{Y`_Sfc*Fq@=-QylgF<N^zE26brNzQir>pLUavTVx<)DYseXFiQ)
+zHc-Z=SFGEj%{69rKDM{j#`~rBbvN;g}PEY*YI}>#+sa0ys@AS6W-~U{r>Au{#oaim#Vf}NeQ|)dgSE
+^9>e3_j-K`=pG0Y$S@-fNBwRFanixgO1211b<E!uf)oQgWB{ycYLKjXJg|>NEF}y_m%B85d74>s*276
+MKVLN8PW`C}~e%r6NZ2jr!x2OHn`mfE(xm0juMImZoiy|{1k6Y&A-u<p>*gBKZ<z5zG`Lo&Wb(=H^i6
+~s~OHc;1z;OQsy<~BMRtsZAxnyw5^|RTs+uN5*mjVaeV(PL)%C?MlxXHtYWB<V&ZMx#^{C=6zLjkjiW
+e5_j!~H7QMA<lU<e$D0>Lf+9z?4Mea0brp5|G&_HNytRDT`|t-ESw(p(?}eU=q>BpmaLQbn=;1;=_jz
+;r<UaXS_Gf**62sVs5(!NZzB;XXLJ`nN^66+Bq^|0|5MksYPMd&dnEwBc{LeP^M9}agG;YH9$pB-l)!
+#VWDXsl2;7d?b!Ou(|-LhPhM4oC3e%o<iWzBW!?YH2nAQ006olDt--ds_V9;R$iX%Xd7gkIq_KQa11h
+}8Nx_@d$oYohi}HnGF_@!=OCPT)BsZ^IXFM`&PFgj{4htjaK9ikk-Zt2bAe%)uOP#7KL6zsS=zP=pi-
+M)SQx)D5yQArtpfQNcHDL|Jo+N!j@sRU4k)OFVut5Bu3C=OUqt4;V&in;?^dJXn0Ii`%f!p~I<^CiLy
+9g;6X{c?`KuR(;PE+{j_u(NMWudmHTzMV3kmE}TVMz(KveB+O<OOj|Sc8<1SM<1|P8*@Ujzd2*Qx)`)
+<i>K_8p8c}_v2g)H|#sGbBh@|@mHV?TU>-a?fLW2N?d+Q)nb~@!0QBvJ#mXIVT}+6fZE513E@&B-L91
+W&V(5}j-$*C7%l)@n^cjrU-3p@u{7qE$bg;~j=^ESv6>Q5B_O;WqW*=MaO-0H{vl>$=J$T<@wmmraIQ
+PLW<;J@O*%qXv1t?-GSOwZkI@y8X~(GB%%NR{xdv9@8ILTtCzlF5d|olRgW9N2j3?Y1WsEq;sQn)lmN
+Y9B7E(}6JeHSG0>E#>H3AwlQ2&kycx2F<1F-wMyC1O8XxJ>&Rf7cHoo85<8cIihn&uoK@Nt8a=e)E*2
+DH1Zf>KpjMl3AyylX0N`M6cdzRnUwiwH9*-c-C)0eRt96CI`>@9qP_SiT^a1bjbdLGqMLoM`=%&19(*
+vu@a9IHRcJ3~k1%=`5a4wijD>o+LmHfB-;pb&Xf`eRpapj+03*&Go51gL&?HV>awCr2!oH$1@ClA$U1
+()_*DZ9z_R1s8a$f3gvPFKQ0-I3_D{FGrj?0MNE5!>J3U3M><~<UnNU@W$;VSBjE!qZ@8udDh))}+pJ
+k0cX~R#Vel5jTx|bi;FydLsR1d`e4~YKa8~YTSaWj6TKKYqctGeK<}<A5=Evk0p+DA5ldG4+j9au?E=
+{-eu9s|%_v3sG-yL~{tqBLe;56FZ7x6kC{rPC<uvU{85r8$I8dO0`$Bi8oA|IVB!;g1HyHcudqKm&5h
+q(Rw=nL<?V)qXJ0Z>Z=1QY-O00;o{l@>=5s>bjv0RR9N0{{Ra0001RX>c!Jc4cm4Z*nhVVPj}zV{dMB
+a&K%eV_{=xWiD`eol(7RgD?=@{S+rABBeY)qzwI8B1KV$?#Q)|;Kta<K3YXl-o0l?11uG-2N2wSzs|B
+uywwOwDt`L+4^#w?uRExG0MoP<8+2scaoC6jq-~9r>@0zaAc5+DQi~`n1mhkWY<xI_Hj0dQGMo#Mu}B
+qsb6ZVpOzi`~-m8-kLRlFjJjsa9NhKeAtTsXbA6A1SQXbJ(YdGPV2XH$<nuu`lwEOXjI$w>rIK{2uQ`
+!#y0M}@d@L$Tx`Zypbpq6vq^S*FJL*9GxY2>mr&QMw2)ccRr>h|E>G(#E}mWzaK-zS@xjkF}mqdOk>W
+-r$T)gpDljT}W<>|5*&EbjQrP(7L)|IZ8-UCS@bFHEr+D6Y%y;Wv6yrDcvj3e6Yh9$Md~fGhD0P)h>@
+6aWAK2mtey7DvLpK6;V>001`u001HY003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7PZ*6d4bS`jtl}^1
+5f<O%3`xN&Cx45~R=tc}X!K0YUL2G*ueR~m)7!w`r)O`O*?^|&5waUF4HAXslsnyU?FRbZ_U6=;ITR=
+(fu%J6t)=%SC6F^!EG7}04T|`%#ljZvvoDEq8uoMZ2uO=KCR<C#+(UQ%3Lsy^C{A>SaW=wc^&)Jt{am
+?61Ib))@W{_2_R3Wlq0}oJ30|XQR000O8^OY7y%Rm<*LJR-^j3)p9CIA2caA|NaUv_0~WN&gWV_{=xW
+n*t{baHQOFJo_RbaHQOY-MsTaCy}kYi}F5@w<KnE90P5BduS7do`d-b4~&n$XyOeuJ^%p1ZtO(81F+z
+a{aiX|GhKA$8uMa<23Du3P`-mof!`2J;UK}IQY00Y;DY@kc$(>i&d?pS(ln^YGp*umbGGy7VHjInG(M
+@LK~e9hJYzcgn>o>l<`7TIamJO$WjcJsxFx*xh&Gg$U-ysal@4sIX7Z%{uo%OjohU3TOd)ZG;F1fYBJ
+L(5il>f7MGP4m6k@{idQl-jzF66436XA@WTZzxQ|*W$qV^M0Qle+)L=09_5CHA!C}&@E;q6ei5h-NlB
+-YWhc6DUpN-GQFOtJUc*oa2kKyg=6FnvWz(==skfiu`I0k~bSlUC*8zmF?H$ERQ_<`tt!DY=j!%Z74s
+}(cAw-PIH&sK1ziph$-+-yJ=@}B+3>Lq*s=7OF6_4~7QKveLTXgFLcCX6>`je2{Sz>a1;VIVsNFH6N&
+rKrppPS$0`b3%Vts%|zKV1S*p<R)7aOlsE?3#<evp<;<$ygI&~IJ~RV_VIc9c-B6Ce~t3U3a&LHg<RB
+?QFT!WmDG#RA~UUMUgFV>P)nW(W@@GdsB7shkLh*Y6geY3=L~GKs!iHC42pd|mz6a0d7?$JoLK1D&vh
+l-^IRCd(zB1Mft6quia;DI_(N=GQZtK)I|j3Ig6Tq9(jMNvb-~*G04jO`(yc)PZNmB+Nv#y1-@H9uql
+&6lWs((o!dlC6V)z+8SvyCg`t|)=lw+m@_!^j;=1akD!~=OY0uZBKdjAcx!XKy%G{Y<<r>B-)%Pv_Km
+k?lH-)CZDWL+i0cUAGg_(Io3WAFf%;3^7uFN*q3<SCvQcK%Ke*>`mM)+5}0Gxv}pf{OGMH~^6F9H9E#
+<Dt8(;tHS+4~H%A0lOfZ%)wY6D#P#D0s>UdDyIgHhe8jw+PGv30iqWXK1LjfjV=o0as`&2f-wmAml5_
+^dXk<XjSnVgXJ=_Zc78FpA|d@SUgd1~VwireWtCh-n!6sOCU7C(gE@gR9lDd=Gd4UwJRHK`R$R~God(
+hN6ck!2k65jB2r%7UX6p#Smo!*+tJ*e{S|XW^AI`nkg6Bf%t`mf3inXnwGl%Q3+|DR`4x%S<a}XlsyI
+WqsnFA^BF}YkWg#rhFCxsfi6*}FyIS>aLAliTas})+7&F$u}J|d>3j;T>>fg=hsQx_{om=PS$U7PHX>
+*91Qx`o?E2}Eo#bh(dL0pmK=P#>6NG#QPzG3F?%Iad_+t~%fGXPOOU)2n;T<Z_dYy}OY(?~xg#;&vF$
+h7)!?z8-IpS(52O)^-RQ&on(|f0|kRrL1y9?_yr>Vz=JUY*KLu8$C{uqn-R9aG#&qjFz9oPcRQOyreJ
+8XI*~BeP^-QgRX<+bLXheIo#VG=Xh@^ai373%G%UWTH#rwVd7qDJXqEG&xF<RtnMIW_xX9l3oUm0LGX
+P_c_CL~FmMc}@H~!O!~^bfdynEBKaRy!T}=@h9a<Ymd9C|a_1q_O%>3kIRZY->+zWOUa0Gjhjw_b@e#
+%z?3L|WmU4`Xq)cR4^nR>g$JpKs9FsmD5^NeMH-Ej>kHr*U#Nu2kMCP+J`dI|LUy3#c(gwei=k_57<D
+b>`Fh$8eQ=zh&aRX3}3q*=Z3nrKTE@C%-6uZBsa(R3wD5_CK6Xy#%qvYV)+yMw-i--_1wY$@1J!pOoC
+1+atW>Cy4TBjD-FQsfe>qIgKzZxG4rYGh*T5r6|M8mu+zMzs!s?oO5;635~&vjUCqp>$Oe`V#1bpoAF
+`6MRvKAp`AN>Hi|2ET|xPq!AwE`t#_OQAegB3!A!zlqiK+rnOofu1#4Ss%7?<lhfk^ZHt5HS^7LZX|Z
+FG7ZS5n#=$5cr@_ILuzE%tq3p&M53sm_;;`*PNUaeW%7xZS7mX%BF9bMNqZ^1}i4?(>$7!rY6k&?4*l
+2VU(8x}8B3P?UPk*+n+e?UK@cy2`cvMi>2>Z(st?tWt2kbXI9ReZ-*6WkjgQAFtLaf9s7{EecN1XS_U
+`%k`2>|2*{0?f>4iH8X+~9GJ@E3WMr4%{U6MUhn??7_cey8|fQ~=XCXQ5N4&#N-&XUWi+C%zW2qKxu#
+6s#MC#n2ogA&`ccm>f-rg(+><2?8JAy?S@fK7t<!bOw+q6pBqrg+GX$8)t&LDGb<MQJ^q+$2IT=awz2
+DwAGG{9Vt)-swICQ2?OeannqbcGkGVr$Yi9hjj9*mq)<5D)-ngZfU#CaT8{vmfV5+W5Y9uk$OJALKr1
+i^-Qe-zFU1pS(oR5~Kc3xoWwjhkK$Wr*HcVGSM^&j`nHO>?P@zEmXh-A)!McP6M}`iLcalY~Fy5Q~{0
+Y$B-E))(Q>meUfkJNFHR_#ALbOb_NsN0j-8f@`NDXn_fk7irhr+N7MkMYx5+_7G-bFz!XFu+*pgs0%k
+Lh~?y{Z&wdrUAoG&r0eY&)|Bn!7{yUC)siJ8#*pGj~bOIax$`kGa@9i`_HS7G%JI9xG%ijkf`a`j1pR
+-9tex**@i74fmAWl??X$?C+R}UMp3rCz3rHba|E4l;EDuEuhS8CXAR2WWJuwnBGx8GvuEeS5CN4$<PN
+O@!}F-+Cz4mx+tbTo3-R)En^+EhB#yQMKx!pp~8bl;|0%EvO|(T#QSvT9|Zk`L=Dw}7%4&KPdKTo!>F
+`!)mX(|Q(nyq!4=j)Q2cf@_~oqlXhw{<aZDPT)j=expf*`UtSX|gu$?rieX6TmQu|-HwpZ`%p}w}C`Q
+|#>6RO1ByseGmse-Gtx>Ea^GHOq$@>rP?adS1gTuw3mO@G1xGPXovjxc)t5hz`6?@(}Ure&SWr4;$sZ
+S|AGnts6K-repklJx-bzE1HGtzP#$_o=+A`AJCe6Sk=HhyI{AZgpS!D-_C2z7=h6P68m6DpdW@Re*<p
+p1fPr?A+ymy}>&Te{plpUXi=mOn!27HqxQ$!DxWreA~BGzO>dtegc6BYDC4WRp<qD-^!3bU(-9Uy*fF
+9jC68zI)M`Z2>(02-W5Lv9j8D-^DkQ|?YC{IS%%Y&uNoD!sE?y{S&R34qU%RuenkNb;~rf^wI!GDg7K
+mUyEBTG0H#f?uo1<|CmvtWI@S;w1qhs8(9(zoyH*<gFTH_+B|*)~zG(BG1zw8!qgyPw;V||R3oA3<P*
+v<kh>b7rNxCs>TQ?}kVjF_`3fapelsU;(@CY1Fc6VA)NY0@di9&ME@A1EJmq<qlFLOIS2pZ<q1A2mX(
++2ngPH!&&SW(wEwx!^4w~ffJuuXGEZ36(6vwip5)l&cO<uU#W8OUWv>me!SqC_P_dlbt2c$9*X#rCUi
+Y)wXVN1Zr?GY}>S*HCoj3!J7=H4LvkSz0;tdICEb_@ZGQxFg)*)+ThVp^y5ZSM1T4-HivWBl^mByq(f
+;Ah%T?Bgt*hrzrIa%I%Q)7NhsSUe)0HwQyaVh2Cs}ajg`*(x}4t8W%qFh`5#np=pNEEylNcy2n-MB;5
+y!y1M^6abvmCY^Y5QGyliSmTs-`4k!MnN6%3cG-Clvg%v1^uuH^iM(?6DkwGZXL|i)9-EB)_=VW%6h?
+ByJEo#s&{Wun?rGDY#ETG1#UO!dCsVR`Uvwn`bL{qZ_?E@5$=vINosy0UWV{qLGFyD3s(M4!QySIGr)
+i_r2`@(TI(?-Lz{uHyfdp|5;gC+B}!|IPK_cd<xEeeot@pd56rdK?Td{L-=Ayh7CCGM2*$QmorJ@e{Z
+@uivK+7zJ{QcpL}r%}5#Jvj@*?C0rmOEn%GuF)D4T8bS7>1MO3wH?=kndCj2CF?5rO`KAqu`W6p#161
+pnWyZ>#-wS=s7=QS0!L>A3$)`$-E<NY?uYk-bb|wyt;4{Nb}r>=O`}H~D;6a;GGloox`nrS*x(&OH2j
+Ry{C!q5IViuai@O(<Lo8FlGp2>8R%ShHt?hcR`T7btwaV^VEnPn*HxqViZ)Nc*j;sM$8d2(G`!W=*_9
+k5Hx{jcKj-BRy5W)+G=HZBLReacR$7HvXx4PSVKwl3bnV5b*R)tTGMfg2Y{R@%2$5Ir<$6HNk4|ecxP
+)h>@6aWAK2mtey7Dqt>BlV#J004Lh001Wd003}la4%nWWo~3|axY_HV`yb#Z*FvQZ)`7SX>4V8a$#_A
+WpXZXd7W0lZsRr(z56Q$$ssn7%0-U>a!9vbpqHk=7T7~k2oyE4xTZ*fq+<K;`-ZY5S=&qNgY8(uH*em
+&8Kw!oBjd?*y$^|gz9f#ny>B)fZB>l)Nm*Mdef(@Je+{bdIc$CPjdj|+YzVK1hll+6r9&j0#t>4$53V
+LfO;2Muom!(?IvGSIX5tSHm(&_}punmo<B}(pp{k*2I6RXA!l`9Psa1&@Fd!32TOS$mX~g0)>me~x=_
+^(Io?4vY45qzH>{2A(j5s5DvB^*0fS^(erUFI;ZKgi>vq1vT0S2KA4)!8EGlQ|3>+wI^Oh|+pBF=Z*R
+UG2TKvC_7%3dL&_5k0O<&G@s6}&!hW6l&&cg%3kCrPWcHZ?e-o(siR)&2x6W}d|)eEw2%LuF~4NoBc>
+Y@27O{yfQX?UTIZVNsS)fL?E%-tN|q(Xv+{tbd;5eVw3}`460g;6%Zxju)vPp%v4IIt(fp&fPeuP^kl
+>L)Jo4;~n%<E{<)Q$(Xo65s8#NsY^seDhOpjdQo$tkTojHY#-@hAIyhJyaWNazkd81K-QlkS?`a6F!V
+Wo!T?=6aK;JB<Rr>1VBT(lzlPw$XjRy<_8i?)f-_A7!|aimynven%4;xj3l!rT=^dlj0agw<k0>0)3)
+h+umAPpZSrUb1IkQx)%9$$$6Om^QT7*mGZAyK7+3#zwW6@&{y(@e;?0?$BlR$Tx_k4yyV?<wK(WcHms
+^q&LvNKI%;OGf6JLbybz6sl>@?SQHaDAG67dQ>U-Adhxd9!=&>>Y8Y9A`f}Y~igGF7qD?z)6=PyNG`G
+ZkCPDn)|fs;XO=`x!(MTEF-)BpW%_dGJPAOSV-Eal}5|qD0JQx%Tap&<^6@;>KePFWBB)Q5Yvb)ilSK
+S`dQh?vzD@LuQRH;^eUn(w5(N9b2Cv?4oBd3YXV!OI0YDYD`$3d8K${~+%;`?U-}$%+a~wV7nyDW+eK
+!WB~Ryex5zw+2w5>|GH`i_q{ghyGe$!ztZMSmG@Oh~lT1~1U%rD@U?^X<Fj}aO7r7g-(7QN}V4lcNf8
+^<rvm7umc!$im(5%<TxI(*SA4mDRPr<SEHs1(}rtuTr(%tcj%$Mg148x8CWm&Vuwv$L-7xaZwk`s5eS
+198c$Fvu1d4(h!?z#&lWVM*F+JIBzUxf4|0fO#)ko*2p+#!8`Q{<6b8&dqT`3F!-0|XQR000O8^OY7y
+n;li0umu1B)eiswCIA2caA|NaUv_0~WN&gWV_{=xWn*t{baHQOFKA_Ta%ppPX=8IPaCwziTaVf}6n^J
+dc&QM9g#-qs)uQF0yKQ$Wb=uvQQMIZ@kx3kI$BDDHGYqZz-}l(D9m3o?JcL}%&EL6vHqA>e6iP57MT$
+j-*3XLOq|?a+&k@NnWnrZ#lgNI}xX7_eu_7Z>Vj;=VZLGdoW&NaWIZgLGG9FUG6z+6ZZ_i)HZ_Za}iM
+l;JLcJ9A)~NR{>YXim=ZoGa^w;^S+v#*tk|A<aQbA&zDIx-6U}UT=-}8bD799jX{S3y;9~a(fSnypiF
+cN~ON))xkugJz>>^tFD@pwycs=X*{*J?G(4;*fxR@vkrYVK53G7=0112CE}EG7B_OL9gkL8YQez}H_%
+qOI29g4-Py1uZs#BrF?&BU$o7y7lN^x7+piAL}?Uy2gyAXnJ$w^idtM{!kGxEE7T3`n}o`NOH0z-Uv>
+=R#N4pP%`ZJw=0ly-Vv3k6#SZ|Bt?pIgc;*I=Sm_*uLzn>XI^=$RJn*E5M?r?L}eitn`o<Y7Kto*aWb
+1fldx{ei}7iA8qU#fO9VkXf(k-XZ$@#QWkwUK*d9r|Z$-qfVSOiawB?n6CzghHVq|mGpH3%ejJh8WEP
+PUswQj!wzbZ}D*4W7hb8ulq4V*qGtA}<}Z4oZ^u$rBFc>wME53AP;09Z$G_Tl2)E5LJy_s~^E!m5-Y$
+)Qm`w-c$j*ROw)ZVH<1Ap)%0VeGK1Q=FppzD_u!klqn0xH_@|0^l0)%*gp9`=B2{^!twnI9!%&e?MNf
+QY^4>U6f0t<wIBSUhTk7{U&tL)afy9!lP7q?M9tucl!m{0SBbV#G-}9eg3}pZ7%wYJ1zbg97E$Q`L6#
+^kXLyBr%p=HngA9g2d|N6XA?41r$rY7F6`WsXbKZ*4<)0ejc!(fZxXibJ*B8!^)7v8EsA~XvTtys#Vw
+%PFxsK(Ngy~D|I*j5`<H{3np@YhJ=r}y1h8DWoDN1u6@2H<5P_TE!BF3QSv<cSv|ajm+yXBxSscxM`}
+rPa(`w^{7fNtuO!UiY4O6RsTozWvSH<hv?n|@K$ID_kgt<E$g0=rDB9+wq1G67;Obb1jHNBQZP@XnL;
+Hw~xX+c#S+x!oQSG$AUo0c*#L--BkM(XL~@t#yRPHr{K_ZGB1AB%^w(Jq9eG%S1)8nGWwU|E%yOlPuY
+jhB_;!1NSY9*D@!R*im7hR8EYPB&YH)-b;{uiVPMG1)tS+SkE5EY&$rX+}x<U>&?QV_Mi5=IX~$1ZDz
+m_9K)*m}rfYE5r+9eC(_nE0uH`fvxj{^d`-Gd;)dP!;|pDC;?FeG)cvD>tk3jmb}1gM(X{C<-Ru%Y{q
+XOPRUWqA#hD}YOA9$TL`5o5h~aRA|aBLTocCOZJ6_Klrd}qBE|UPQ<U;VMxV(#I$N#&j&zsk+5G0^KZ
+i?UH*HmWKNf^RoFtO4e@IAF5Va%XDuQ9Fc*0qv)uTe5!JkX<8Q>cxW%+FO+Y963qA-D5lX?TgXiinV5
+<ul-KHiG<NH*1@v5D9F7-|t$OvO+!s*<K<BvHW^Uppt0$p|hpeVOaa^AVcqceBZKgy#C)d~ynx=|o@P
+@pLjZ+Lu4mxOa|!{UP+T%6)GU0rkV|m_l1y1E<&S%>!*9wDkT-`b#rPf5wf8yGGJ6m1(vI5&x<94PQB
+%E_7)BKGV&W+vYl!c+|9I%Hy-c;qfUr=FDbvYTvQ*-6CAC(+=;lwKp=(p-S~WNUx=B&VG1w32>6KjA5
+b=8W%|=1Z+BEywPjS@l7xp8S$}k-3U6`F#(Nlq0x$~(aIp(+k!|LGn&;$OYbG8MSUVf5z>7DhlC-TJ)
+cb4yTdz7{JuPl`_a`mL}w3>w@uz+x&v}Eut_B{1D!kl)<@!Slbg4W?FJ9B*Fhg!KRSDz{{c`-0|XQR0
+00O8^OY7y3?_n=#|HoaEf@d*B>(^baA|NaUv_0~WN&gWV_{=xWn*t{baHQOFLPybX<=+>dSxzfd7W5o
+Z`?K#{_bBfC*!bMpH7#TqPQB^4{e&><$xx|#b`em#+s5wUdCEc6{+=Yi~RS_kkpELv7PH8i1k7~oEgq
+M^H6f%sMbIxG=BZnnRItj^;e&?s{8BTDw&;~m95%CBb#)4z`9Uv+TyPrYNJza+AcR;iyHilH-!4G)>v
+z4<N?2zx$yvLuJ(;E{lI@;UH$Z5)QzIS38Clr6`q})6<ESH`}(Ub*10Nhn;i}6I9p$y0sUH}#cFSQO2
+gbJdY=|(uOgvyDWmlQ6d4=qG0=P^oqEWqGvz;{^$5tV@tjTGlI(=!O<IC_%%;&wdhpH}ML>Baw1$t^N
+>Rz*u#Hvr33EeG7K_&@x_v{Fu__brJH{7%$TL+OH}6%AetBntd%NACD6rMAS=!LJAC~%I>$4MrEG;ld
+4U9cN^UuE7oK~Sc5=HD?%~fr%Hk%(rr7-~H-#NVhaP{^QzWL^_peftH><FL!6$Ai0J%x9xJ*c`mLWSZ
+W>pPHUg5iEUr1{xDa(D-Ys#gYf;(#E40&U=k28@EHRR>(0ElmpF2`zI`RY!^;mWtpyO$#oLzs*IyL-w
+szRa(jlA6n5g*rsm?Artxi5A^I8_v|)V#IVlpDbOHlz_R4(AtNb@Z0!z#VabiUs3;CsyJgF7mlg!QW3
+*hhc_)o_OL06QJ1(Nol7@rJ%i7%(+FtwJ13SIe-5!gCUo3)<<y}Ptgp4HMmVs_Tw0AVkVO)snK^*l3G
+?e97EN?|kL9pT74oOWxpclY}v>eRap=44nklS=L?39|@vY||sD*cX2@ZE=)jSSMV5Ju2|-$%2fetL8{
+d1K!)1ie|HH^xrp6MT8moPx`4Hh_Qel;ZB!$&B_6L_SNCDVN|rfdj{ETGXjDxYzM~;*zD>2-C5lClvQ
+&Vd#OdrK-cR{?Wo<VhC-=PCW`aghnje)Kb*OHt~~%p<@mTr;Oi>r+yx8KM$F44sU3tZH%D077n64f{*
+DaeXb*Z72R+eeGUoKJrDP!=i@P*(lCidz}3^?*DFR^-K4syq={F_YP_4g{cb2PHio;LMahrc_zN4&W+
+6X9->%)*8*7|xL8tV(Lle8}#I}lc7{a%9r)~Ux4&ukWN1e#E06bFpQ{zQqC12A8^U;4ket7>_pyze;h
+4sVxa$stzm9gdmQlEmuJfM9&g)7f$DR8#e4^a}OpOmcQ>v7MF@_h3u8fPBsGOMR($Mu{SSi+^Q{V$e1
+o0d@28qo@)TD^&B@dTpFh<w-75w`bu)SIie!+Ei+CkkmiBDlR-4_c&BqL^tBHX{%p=kPJ?MjY`6Tv2n
+*5No(2@lo3jxNppn8xg3|LWHHmyTi}M+3P|a=GXUVu-!}A#)lngQcpmP$ldIePp!SRT>n6sHRM?U^&r
+jebZgq<q((WteY4;{z7#VX@RvhE;Qw-L3?+Nj;oG)V?F@zhY%aWrLPvfS_t<M9;RuHrHsVwjB+Xf;P>
+<~$wEa=D>=971(h8s&<s=?hxh^2_9h&=T9|bA~6#Fy`IYD1vzP^FKc>AKU<=%I8;BVvYdeX0dd2Q1hU
+U&P9#;VTrn?zHFGIux!W+flrjg9a1-(TL$j|M>=NKI#we8<GXpe|%KU15)ArwUus{K_Sux99N<_vK9N
+CJc*voAS}t7QpuK`-{&}N{D-5VuD()<8d`i^Wj^<kXdJza^jq{o|D1D{yyMX)!JpD?CRX`Yg(BLoa?H
+pCSadB62QE@RC$JZRKQ-+8G}>CJH@>BXkl@g6CV+$41#Q0@m8)CLCr@|^fMJzd7QOMQoOt`yn24>r-G
+npd%CtF0(^Z!3D1(?4nk?TL`X3W=2XLEiMBliRu?PRVh&t->^FJ8M^S4ICN&tQl)jPzpyI?J(&wY~?b
+L&LUo_E}$Vi;K9HJ+E6CAbs3)}16eA^aX*GNbfte+#YxAjo`Y{jTs_NhBH&4rdC9sqgEPEQ7HkoeF%Y
+GdEs#<r4Y&$fIf4GTV38&g+VBX3*rKxOW%xE|E@P1>j?cKSLf?^$j7^XEj>GwQ7Hu8z$UHOIPR$497|
+-3-rop%x)dIlKf{?GU?99P0_3JvzcqN1~b<UZwEu>6*DV=JNqx(4aMSnyO;C)71`()y@P?^wVGSEQq^
+WS|Q~j5^8snw%#J;qVqK%GCJ>)xf8Hb$jY|2NemVaRztX8MQg2yh0tjw-8J@mk3BJVWofl0l};&g{KC
+!Y1;>g7$7(&-PGSAWGtdqHH&!XcQ_PIxj}GG@7Z>w1o60x)HODjii)Ymhoy<YC@=n&=Ven4mN0&NI7`
+V*hxG{gTWr8XWYt`ND0u))hgkn191MrC(k$M2_J*N{9SB-oE@2TGNx}~5XXU;vxr;~ury?gnLce7ISl
+(X?)_EIMR7kk`enrF%YezVz-bk=C5EoSb*t3U06mDA1vE4*1xc<F61CST|Nw1emHi`zKfSchrL#?q^!
+crS?Uw?=SfF=)|w!d^R0<de_*v*LoYOLK#ye`;|5t>URuR`AjDbjk{zO}|ovr>Q5GTsU4mWvc(EFpUb
+E@N7)_xyci2J$u@GHV*&)X>+{x7o0c0u#ElyP)h>@6aWAK2mtey7DtPF#X`IQ008m<001KZ003}la4%
+nWWo~3|axY_HV`yb#Z*FvQZ)`7ja$#_AWpXZXd3BIK4}%~O#dkgho0u5gO&A(Gck12{JS&NcIgX{Dz7
+hYRMp-!We)sNi4ceaPs#YTba?_6%iC0RuP}zadc8^(NO#t<ztk~0N3MgZ&hUU;F95|OTem=oKzU%;Z;
+s;25oqVIeLL8t`o%sRYnY7^1I5Ii_Y021T&E;}ijNwHZfOQ4(>i1$B6Z*5`b4k3bBe2UG%P7aNTQ!Sw
+sA-9LZ~Z_bX^F<ND3WZAdX2af6CecgI0Tmc0Z>Z=1QY-O00;o{l@>>y9aj9y0001S0RR9i0001RX>c!
+Jc4cm4Z*nhVVPj}zV{dMBa&K%eV_{=xWpgiIUukY>bYEXCaCv=@F>eAf42Adninm=^_yI_rDk`xQ39)
+plqQobx-X)Raqww#W<TwNinViqhPtWcpj!?SjpV$N*Qxt_KPeF}Qxo!-~>#J(r^FTk&x)UwJ&q6*bFm
+!R06uZW^B2nha5S1O_{pR`BbzS3M`{Q<lT|g910*#n5L5ua>6W2IloU-C%&(x-d=zePoaL8(hf&3cZM
+dfT*5Lj_$i^OS8fLh+Wi$iObFsoK1;gW66x-t)o-}V&poF6XH{?Dg4RP_25$>wbS0Z>Z=1QY-O00;o{
+l@>?Arw0F91pold4FCWw0001RX>c!Jc4cm4Z*nhVVPj}zV{dMBa&K%eV_{=xWpgiPX>4U*V_{=xWiD`
+eomXvd+cpsX?q9*VA1Y^t5_DS!7znT=O@IPv45Znzq9_DfIz@~nQXuI#9{S^VM^X}HC)+T6u_)gA^W2
+dZO{=tJ6*pBaRukcu%1kDuRt;-Kn`TcS3#HRq<?B+^z=F^<uCH+Yx@6j{3!(S*KGDZs*=wh1&hx4lIp
++p0=tIA&q}8gfL3bi<`=b9mnM?{OS!N*LXvkCxGTUllAtBe2H!x>qqY9WUCJdi9>>iks^^R@1w9G2zr
+0K<0!Z6aBGou(;V!|>ZgM?EeOU_PN^9RFDv0~{myT4uDTz{Da2kz6w?d91W`|cN)e_mX(t*C2Oa#1r;
+qAzf+7_T+(V#nZF7;Bv8t@jnIc|8}+k%aA$Q#0w1jA<bk<<5t{f}C#*kc}=skOnJ*+uS<0lmhLy+!%%
+`_$?T7J8B3njWYdy`bu71)*lp;%CZtsQCntqO)ccwPynq4<cG!78c-5V6SNIq3;Do!C+Xq~rZBi*hRX
+s0@YrJYT@^U<lo=w#dLoaxwK`E)!s#-*Szax#ub21Jfcjw`okSP@QnGu2B;Fcy{}wCWaJ?QV!8q1dNU
+nFUcs&1jb9VdXb$@=p^L7rU(hdcelG^A%ya=}fYCzg?8OTB9a-ldRg$bq*EbRfbdN+7Sry3gdH1xsD?
+ZI;nt!2r_<)zk2&)M>`dx-*n!|pnEHXCCdtS*9%tQ|{pF4-eVn6+pbuW*k7r<5?*-X0OHFY`EEVj9sE
+N%v1(6H^#V&x0||a|}YsLRg_BBGRID>H)h2bVJ7bGB_$ZIFW76Guq-ZSOO}6_q77FD-0AVqSQCj#)?|
+}g;rQU)toU?sZCwP{pzEjFgw1Z7%~N#)<K8`!WGZgn0RQg!p{+^TRgRhKi%*;1XzRJXi0iy;WY~+;&w
+HSagISflOau&V>@XmaIRsj6cYh*^?^nm_qeOoBjh$FQ(h~BTq<Tm08}L$zj;lAgl4)M%}GDQ1Et(}&V
+&>;%Mt^1IcIndpn0W{Nz?~Vpii%OZ769pLkXXrsFU|6C-dH0|DbLs@GA-seKP!(5iX}Uim;{E%!TDXU
+>v;}ge6uWY>3_@<`^=l##@U~7%p-42d_8aS|^!aa7m)Xm67#5i&i}VwJSpOT0mn@k?!gK@fjHy2Yndu
+$jqSy!X)h>L6QD_EE1qXf$+As9iXHA@Ot1P?1#7U_9)6>G4X-^W-PZNZ?J%>=y?S)M?1;$?4U2g#U?c
+sfA{X^<T(wZvsqffb0HqU*d*ZK7+T6ePB$y##<^MET`q7YVr{+quImZ&yyEh~9S_v*cxvp>9JV9P1iU
+_^ksbeh23WAl+SV+Nj~|4sHY>d4G{-o>G$wuQ4!3aaITprjz#PB-sW(G;0SydSvyjv<-sx$%wyuUavJ
+)@=b@9u$8T*la^~-NBOp%t`Dt%PKmm!T>^vD^EYK1HgwOYe2Diwv%MMm|c-I%9Tug;Ud?<9N0jzS_D4
+;<ZMq1?fe_~H0guinn6>Sgt!c3Gu#RK*CJIO;ijqW=B2;3M=t@ipGH{4oSWi-e0?FF2af^kBkx*8x;8
+NtZk>$equAKI)F4VE^Hy_GovfYhS0mcJgkhScek^Z;<|Sizfij?S|K`c}FtN<_i~WvgY!cj|aeuxX^>
+3cuimX0UCeR`QMz1r_(|4!!5|Si2t*&(RF53M)778e}W(U(UV8#)^s|(^<?TV>;B?NDr|+Poa=Gg{Qd
+Si_1U;7Q7G_dM}Kb#`kpI4bmrYJCWQIz{j2f+iZYCgV)F@i%<jS*%JDZ)O9KQH000080P~d=N7IPLRH
+gv{0L=pc04@Lk0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FLGsMX>(s=VPj}zE^v8uQ%!H%F
+buu>S8%!1PE#-Ih8zm?w98J#FkojvvFKW@ECmwNHvfL4qxfT6bg*dhy~hvJZ45mq)gI{>Q7N$fAw&XW
+-)yqM7ey-oqY26OIEM3kQ4|JSpg`KGgiY`!m1L@i0{E;}tDh(8f`|Kh2TkY?n)Kc}OD7Hu5ZgkhlO~E
+x+rZ62=_c#QiO2<dAX6`sRmA}56Ff?=w@nN=HU!v~5UbbO4YvX``jlV|_uu&Ob+_GC#VkwN3u>UQ;md
+Zt-c|7VY;ebo(<#9(G&X(LO^0RJzhFaL?4Th7QObHtN|gy+yW!TopY<ZnT6|oXTodmZDN|nY^l$egu@
+;~}CTf~fH}Iz25jTJz2OBXxJ_V06Js!Icbcs&>MRj#LC&^;fh(ece$;m?8&&9eMcck{=y{xXMz>jm1<
+#)|G)tG#2)SzS1f5$ZGX3%m(jYAy=;*7(a_ygSmBeWr&A=W>Gb`GdRNsz#L(9)0I>x4D@P6+Rox>2Li
+N8+F5(`BpWh#HfHJC!ABWp!=dSBmC+{=4WI$0oUX|9HlK08mQ<1QY-O00;o{l@>=YN2j8v0002t0000
+W0001RX>c!Jc4cm4Z*nhVZ)|UJVQpbAUtei%X>?y-E^v8Gj6Dm&KoCUx`xOfoY2p#R+CpPQMQkEihC?
+@I@p2pXP9^`|_ytzOJl@O*a_)MeOA}$s-p0mySX+q>dzfYuqhV1l!HjcfZvny434Op60wU}-d9g1GsL
+7#LwQviXJVmmO2AA57CBloOaK=x{JHN8^fqvSGWw>3~(=~-M_HHQoO#;iG8~H)rOn;O4fDU|oA*HJ7j
+uMD{Rly9BOdD+yrJhhr0|XQR000O8^OY7ym!pF1Gy?zt00{s98~^|SaA|NaUv_0~WN&gWV{dG4a$#*@
+FJW$TX)bViwUyCw+Bg)3@BI{K$c14_i4ATu%?=mFxCtqN4v@)qb~_%`96+s+8A)uy+pi=$7>7}3bFsb
+vOP~IuqvK!F1w{Go!0CD;;Vx%HZU}>0!exdB=z4yy1B3Yxdher*>;*zbQ)B|$AP`|ZisH#Uh84}Q<G8
+NtEH+fYHec>Dgf%kcM52IjG9N*@A)KTVIi&egb;=xeoH9w-TJ>Z(H(iQvDI=NaICI2n*hsm%zPv=%d7
+{7Q4wsa4a=yOQtxFv@L6MRjCOW7|!HLvleQ^KwdcD6~XF88wTHjq~5RZ$Nccn%;OI(?35}7ADKhe~!*
+0lrb;VAc<fe!Z$gyaQN@9tJ>(~JnUG$v@I*VD{@{KQn2S{*n^D5Ek-ngX*`yI6IJ^|Q)3FtSCU43@Nr
+e#-SjSHN;<hINEgUqw7oxoNtI`)Z(m{vp{O)#}zLi7e9<Z6j`-4Ho5~A~!vz{i+fsT&q=I>7I+6pD*>
+{>r0@%f0Ef*1(_~iBWxUn!|CJXEz&_H{XCsLhO^;Y)KR4#gwdC`NQag5>vUc!pZk6r4!@TljQo#A*?c
+z5+eYC%^%W9H)n03w-@5U%bJf_c(HgG)m4Qp!W=MD<QjW-)YR6%}YxC{4^ljs=ajMNI!~d%M(y001bI
+!4l492REC%)=K&of{a4{L@~z&GG#7RQr1=o;`X9*?JwHP9>Pc!*zx3JkapZ{o=!JOv*Nh`xoBSBj4YJ
+l>8LaUEQh>CWRt9ei>Oxq!t6)yYRV!irFy9pDMG48(r7%elm58-DDi*{3_TLm^**+@LBSVN1f79TQ|c
+y18ALS|#zHCj2L_0;*O;w*h_yOs$TJZUu^38KOY%bdHC(jHN(OfwESbDe$2Jx>l9qV+BmDj+0;23@{+
++FPi}Zn*jow0Ro!=0-FH>n*jow0Ro!=0-FJX|JMM%XLEq>*(~6DHV^oo%>=$@bAj*KY~Xt~ANXF~2y=
+PJUXFs99$f}7J;Txcv6@BVlj-a}9G5-as_;oT))%wqIQnQ&Z`HkeJaC}pXxp7b)7x@PD(AH5ZaKx>^}
+>sbtmXU*P)h>@6aWAK2mtey7DxVZxGdud001r|001KZ003}la4%nWWo~3|axY_VY;SU5ZDB8AZgXjLZ
++B^KGcqo4d97M)Z`(K${+?gK+kB9mQ;fWPI3PWva2#)&ZJGi}id-)Z1VyG}ZDmqRQcZn(``>Sdq$G-x
+72n<&D595{hcm-xhC{0RWX6k)$eve<{K8qHRtY2T6QOdtAty)2r(<${dqGYQXL(XeO7bkFOwx!*O3Bs
+bY<_(^C(A6S<GpNMaG{8x)s@`H>XOU7y`|u5GESJxh<!SR>z}y*G`u=b(r@PKR&8>4sQ*~Cmy9VY)|K
+Xc#_r%Y!iyQtxzJah!s$sR4mXQD-`m@(U?$|6Gca^%Uy)1_#+810wnyOCNZPT&5$G@I5=7%9R%#PUC1
+|o98F@ifDHvHLGEqv{cM%CPyx#;PlCT9aPsiq&22ArrO7Ik)-;1Ous0jJfuQXK_HwXg#7hg(}SO^lzG
+F_2G!u^7mY7~(qRT+E6M+s2^!dxVRGDQl(e{9H-3z9$x5DE><o=QbQC#5ia0-}oOxUH#L@dXK9M5bss
+3L^5d%3vgj&w}RkIZ<>$6eqkC8f(VccPb!KBx6z~ECnLCW@EtOIAfWL<51Fk34q$oMMTm{*Sd<oeFE-
+0<@B&*3o2v^>>w1RHfn|5!N`Sxd+-Y*x1tgzO05V>)^N#}4o>YQJZD6$Xf<#ZLKEh3t%BB#<G7NVRPw
+%nH5&ptX<XQIynUt$2d^DQ;~W<)Q>;|j9@a`XaAqP5kfJ}71LWH?HnLi(rWWSY9lytH=qJ|Yy2$AoB8
+tSR=e;6=p8W_T2oMYYB4PeA$QOuVR*>oS?IlT9i2(9ZLkLNKD=E-G8a`p%(V$54AMy>zT9iuJ$posZj
+fJ#+pbF$UQ@qMU$m3<nfZkw53M7_oilGtl4F28BVMYWUr+i&NMGAwjN2jM>AHwkM+1JIt-;54M;X`ux
+uj%1GA4iaoh9*E@C3sG5t=;4j?-yli{n6;3Zf9H29}#_w;BQCa*B?iJtB?))C&=eSJA1p43jCV!^%^3
+vw`+1tup&h!-1a&tVXmp9YVvcEOWI1FyDrT`#>E<<w!wNlHsyi<dF>q)V$^0-Hvyah<cgO$(1H?e0+<
+QKSK?HHGf9~vi<VdpbkY(_NpFPO0~n*ec1KCUawtzo9#g)wB^XAiJC;J$zc#Y<dE!;nI;to%YDVHLel
+fHL6)wk^dt@>p0aJl1MAv1GmCu!KRFT=i-TdZb0JKFI%D!iUv;Md_nd9Y3<J;R>6EemqKml$Rr`l)zs
+$&eUWIa}QLZx-}-z}z^t12-)n=wUgdg`of8C~i0nq9r@HQdhCIL)~P77qAhN=mgFTRo3z%~1Br^>mS?
+0K6o+CrSB&Y9hBQju{0ONT3Gr+6$tVjkWt2N_+%$J$4?4BX>ktk}<F~olOG1ZG(cV=6MBKWh4Z%$=ar
+bm}$~0h^`_|MW|{d{_aFcacW>CmBcI#Z~>LO)YXAex`42g%OzQ2U5POL%))6%*%)b(Oc!mOnkQ%^mP=
+S!^zsFzMZa;FffAXNrOrTJ<G4}4qGWrp+9Cl|%LLkVhbC*(XUd?W;@c-$1L-=5YZT+`(5_RY0IopSvx
+XI`WI47uyub-f@_KHQJX0HJX}>j4O=*Ho4#t28pczzR-&0Do_S;J$o;6djayzxn4Ypn6TO7oq+hksK=
+M_MlP@h8Hq+(5Qagm_5U2W%?HRAWyL;TfE_V{LgJHLykS662wv>0P;O$;MPWE2hPp5I))dw=IBO4GMj
+owyYk#JITpH~?_{>E`3~Y7oGP{8<d#eT>hqrn3(Y!cuQZs-1;-J*0m#zu2w~FNbvB-OT6L+qL!UA^k7
+&tE*4<+X2k`VedcBhn)XBAJYCfeK)_pn{EfN0z(+HFVle#;M*bX`}dc30}en0zYI?vBtS=fNaG4gtw|
+Bj|M3cKB3*S~MFWxyV!`_zku+fjv9!dFXofw5a7JWDJY>@=LHj)45z7*55YEc%h=@!ZghPQHu{0}Rn}
+;uhV0a$VsZCG%kYuOYr1jgi$<y9mo3yT9n>_8I+NAY6waEh*tWDPYR&DAyzqK}L?QU)I0ETOm#_(#B2
+QW~Zw6<THx)Iz~o9yJZYm-qQ(%4p;`e^ey`TA>9mqa@v_0*<5O6`c&SDShXwj*A*HucbLN37o3)K9)0
+5xcdihl)F5_0*;wV)lal;WxOrg$j2&uBzJBcKd6aSZ`lzc0B+ULvvf5aM_csN{B+!->YW!kM3dER-t8
+H%9Yc?S2os7dCvfKd#n#|E61Cs!`*rhAU64?-w%Uz5RvdG8o6!)0`7}ihC93C<~-UF7`+-9um5*_=|;
+YHLf!q&J*w!<;&b*Lw~Zib&xySQDSMcuH+~7*=u-t}|2uGcSnc0n^JneSr-;>Yq1kcPP|iuAZ){o$sg
+G}u>POn%No%HJE>n*`c#6JG6rP9My`6LTI9|fe6QUqOFHYJql?ZTyGCWt)5Mx;+EbQebMjy7X8H*7qG
+!ZaHM+T`!t+E;qGr|Ki#IxXKA;VEyymlX~@sIunht4T8zIDms=rml39JjrUz~|)e_+qCNe$WbMApW>R
+HaEt#f?^EQM>$AA_Kc^j`Z#vibTzsaPrxob_>Rrf&|<Yl=$oe|+dU1}a1Yy<-aA6ebSr`M4Bjt;E!L~
+D$Y}^U6BZ-X!g+<Ozd%Chs!EPE(+h-gh62XsnF@n9fk&v9RhCnOVjO{)J!y}Pzy11*xcjcQ&0hSKgvZ
+fPt?zf{6|+5O+g?=ftP2wm`0sr1;{VHsb<h=R*^ZUIBcThv3p72SUA&5f(ef8(^VUs=hI_q?>L(K(i2
+JVSK%-6*?B0OXGX8OCzdv{~7whA-Nk`*BV?A!w1C?6+;izBELN{{9XZ?cqkZ)odoccs|(mtT=PF`h9c
+!2s5IXEy#-g@5Ri5J?cC!->fG?uBLH7mg_{1*3`F`Xek3=WZJjQ)=UV6D+__N&#U;4;%+><&rtBzayc
++G_%KO-3cIRIDw3$`F$)TMQ92PZYE#EkjscbbQk6lPM^bMMTcw?}ERuh|J&$xDRs*Npk@=*g3bfeney
+gkiQ30umcjD9I{bFf^&PPZ)SJ{Xh(R1_J)W0?vDrS%VTe>Y@CwCuccHW4m1>_?kb24O>ZZ+rLQrcJ^v
+4VjqR@VN`9tQ9-+ePTkHTHdn$W-fv&-;j>fOtE(h>4tfeDPBHn`j`TXj&RmWW}etPp$H@o*?fjq<y-s
+~9~(@eptrw4@q!(NJ-PZXSCgX+li;T$(Owf2Iu0NVVr(En!zKhyMSo_H2)t-8o(9gm}vEvtu{gBq)<0
+>@65W!Dm%tiAsLP)h>@6aWAK2mtey7Dw)4%f<=<002D(001HY003}la4%nWWo~3|axY_VY;SU5ZDB8I
+ZfSIBVQgu0WiD`ejZ{r<+b|5h`&SUyp*D~jTXP%W!<J>(hM|`h+hH97wWebMieymovi$o|@|Wy%O$`L
+G`0@BiKI;u^OmiZ+uN{0dTDV$h_#((DJi*P|ck3KJ-hG1g^+t)-A}INU+Tsc<B7Fb4+1}o5p^^&oL>?
+ML4ur$M(hVnjvPn{rIY2J7mChWcUf)3P{lKPs^V6G>Br!~5XI*Jp2ZhhfyTj-}qRGLBBsr338Z4j9ih
+SpZ9m`#5G_vQjP!^d%i4_o9bUUF`n!QhepNM1{QOxovvd@)Tx>U5z1XmGYSMX%?A0$`Yaxos9BiZ!to
+?>xNEpn+)OIDzb3w98?<lia%H3E)MJ<9K%p?^q7Ue6DqTHF#DO0NQVpy1Og-kh^=l&<!uU|fv^g7%^Q
+Ro2ztEAuE6%*VQd`6vgb)2u;S@EuKb&KBd{!HZ02ii<Fc^Q4C{Zop(8p*X~gSWaaH(ZGC8wjk`)@Dkx
+pEiV)9V$PB-6Xv*A5RR7-Nj26;WS-b{MpoutT8EU563rGNgYy0Id-0YS<lAeCtHj?U&&wB`&uq55D0&
+Id5B$FX-Rz?1@l#wdS_tGasd>UPFt4Mw(0|7+-fs7Id9kxjmg4i3;>)H7ok?oAOXh`&Ww~N}n-)JdPP
+wg3tIC}}rqLY|`VK|Mo;fxcLJXm6N%tW<g?~^>0|XQR000O8^OY7yv#FkMF9iSq0TciL9RL6TaA|NaU
+v_0~WN&gWV{dG4a$#*@FL!BfGcqo4dCgaCZ=*&K{_bC~N^~_`3Chd2sQKVHPUFgFN5-e*bUGmnL!ia1
+wY$XeRsHXsWic!^kh4yeiXtn_JoC!T?CdES()E_bFOeXB(9{!=my!pMi3GOf^8Dh;Cb!NVxjGvrUdDk
+Wu@5N+i*Nuk84pKOXGFp{0lU{bB_X3LvWe5B$H9(z&f;$Nc|mT-*~Nu?8O`VU%Vn=8*!H?d@QGJz$^`
+Mnb`3nI<Vw{0jys8$%wyXL^#Wiy2#o|ug4${<Ly6f>ae6nIm~HA%Seb)2AJ&2x10h(v%mj=Wqs$`X9G
+AbC*L8bSnnF%-Kn!WhX#xv)6Qu@(B;INnc;usdt<aEgxNh@hxn&Sq#1Sm>MaD3*zUxOGI`KFgPZy(kF
+P9w7X7fALy>s1mbBbd6iN`rYrp|0Ka-HFPG@81%pYQKSb9X$wpBbh0A{zOeoSg8SOyX3k^=WEHHd0H3
+8^(|X+(olrOQL*?lVIb--{Hhkewo$*dzdj!*(2q#h-umi+?09ah$(}6YQl1cpEaNx9q4=-OeXGpG+Z?
+J@X33NSJ_HQwfOt3{4aozxGtu|bqx+lsHI9i3^0Ew=^GyctMkw7lMlqG$;wBnv{l;dFIMgK`2bw<>uJ
+;k_ZY1zv;?<7c6qfIbiGfSKq&TUw^Rta+NaLt2x$-G;9F?19P;Ynn5xR%-$6Jcc<`_jLA3(5r2QAL6q
+2h;TW22%*dBXPji*<0Sx9%X<WyOFYY=9@B~EX^hr#r2GOAoFhJoWVfb=#CLtx`Hq|J`DfZtR0P7unas
+<?{@Vzf^Nb3f!Z!wm_zoy{gi`0cZBKlT=6r<x<9u1%3zd~N!FG`M=xbw*c*b}KqEzI=qW#I>imJFxpW
+Nea1HCf1o4;<@Ab0@$D-whr{W>W+-CH~~AmbT1Qnx!2+K67O>I$ZU0$c{}($Wj|l+|8>TW@0&%8>*Q1
+SG?0)%v*0@^nLG}i#k{4@m4Z%3qkP0m<$ssT)#iF#KLrd$)wTz<(>B16TEPBMM&x$UK3)x6JHT7Had1
+=EsFsgtcit6#el4-p?&9-hjEIg6&@8b$^L^kl+>Uu)-(QqvOYN$LG-g)%ip5A$t591eZ55|QJS6fX<1
+B2o4RT}WsM6i!PJKY?{;UfcN(X;tT-+>Jrtiz^YV7A-WPIFFs$86saY#f26*%UEW`fX=FfV;U*8)mXD
+xx@ju}iTyECuW@<hL7gPAIEL&pD}S9n}mEa;Hg%f@+cC{noIrH934PY_bG&h@79viyP)*6XA`H45rRl
+25_Osc3tYyCh)cfUr0&g&DxqZIlL@#Vr;E9vyxMZg}^<?r564fxv=!u@!ixjrIggOQ49YyhK;;h^*P}
+_qj+mu>&I}}2Mn!ubU@LF<%U}mK8OSz|MdoS$|d?8<a2?PyS0|mIq+-+SR8ESq05ZQF2&sbWP|J#eAy
+xUZfV`pen#s3qAsZkq%R`ERbF^Z96Pm810vLEZ^$1T@ERI`p^X=1K%d<C8D%JXg-f%QyJ4{qkRF21^%
+k8{JhDaP3H-t8#y_+ioJqq$UO;*M8m}C+1YqLS!^@N1G(S6*iRJ;?kh9%X)%-skt{T&R^^y!aK340nZ
+U0#Pe!oi(i3*05PkH@LiSulOOnGTTCZ#M;ePoeee3=aXRD2Q>Z5&k`UVXc&mK;NJ{>SyPYr&3$Hr7TN
+@DCM8(@86L9Ye@p?b|jSnfXps0DJ!eP)h>@6aWAK2mtey7Dtq_Cm|~Z003kb0018V003}la4%nWWo~3
+|axY_VY;SU5ZDB8WX>N37a&0bfdF@wQkJ>mCe&<&l326sfCW4u6w_1rx3`3{Wox%zjweu8(#6VhOBgZ
+Mjsy}{xOJaw>K)18nhvfwy`}o}aozG4k`gFZz@nXs010?}p1_a&*jAOKgi+1PIgR99kT%PxFkR}Ll93
+qmS79<G4-EDs`oD5(dW8~H5jIO|gc(taC!zLz|7qwcg4r*SAAtm0j7UCdD;8RTc6jRoqvo9#*&EIPve
+ph$B{vYDM3vDCc4?xKsCEtt(gQ1cyl-z&l4fXn^lE;H<(DExKf9%~1hSQ#qe^v6s;O=hpNv^-n>^*4x
+-wp&i;aiN{K!>C8$KD+{KN0j}yA5@S;uUc8$XjYO3&O7pMpF{$l*YF=@23?z--w+veGX4-_OC>hZpNe
+g;kDWP#&v7VSeuJvriiVIT$I%a&B6C$5_8{gBnZz<lJX}-&leg&in;KmPSv|XyiZ9&F`9%7A##<@=ZM
+{sIc;c#$@F@3KW%!O9_K8cr5q)$$pmpf5Nk=p@zAVx?9VAf!n>Z7!X7=Da;_(ZJ$i{e(p*tVMJ*OiY*
+sNC;wd9m6!6k=1uvlUI-e=obn|V&w*aSTr_Lr#wFtimP`b+|yM|tW`xag`E3~rQGAoS?!k)q2cPPzRf
+NB;YGo*sX*n~`pFdZSrx<g8KVn6OcrB<ks(@@&ol6LN0mclreL*=t2*hc`n`!2wefr^0CgcVV&_d}e3
++*d5fpMxbEVAY4=tO&y@1lOWn_wU_ub@x}GNFX0s6_BTGDj<g^S3seqvI0(**sW8IH)rKXDq!L3M~k6
+Mn`)@BDaXMI=y)ni;1YmC3wzqBV(o<RElr(-M*k5>D8=d|^pZ)UV6CF(#9bb#;B8j6jpv?S!@K(i<8i
+KGCi6RBDlVOz=No#jNwDd%>Y64ok0~)V(5hCt+4?aTjoIu)n|{g?%KkKab7OV5LZN!I2slP`lR!u*i(
+>J{K?ww+x!dQnAVE>f+9HhZ<mcK4^tcw6X969)+uMoW`wTC$7mG9h;Le-tBwH?_ML8{3EEA6bPF~DP?
+9^}1RzN5R;fNu^@fJ7}jUJN)@FmK3i*}=OLbEBMV0y)Ktyw3RqcM-Y>FDaT|7DE2XPARD3(>-<DSQxp
+NfU0${@8-;p=v)*4hL&$;GZk&cv0kAyBap~WCk`u;jwvYwRU`DQ>GwA5fKxu;U}tRO$}ExLf!VED%PP
+5$&#ixf)JyC>G!w<qdpVh3W<P3kJ7msY7d@eqH~mq8Ct}INUZNQbm1D(j(N;7-In5t75!%0Zgg6m=ce
+B<nZ+j%q?M8YkjjIks~KW4^>7)8rsqNk5ys-0Az85`C%{ZuD?Cdb@u6T2Rs<nq5ez(u|3U&&FC6)y<a
+xi>8B%Yp%&RHqx=iTWy;e5-xIY9J!m0tPS3cVZB0&mW@kv#BG71#=>h>rc?zwe}KsohQ8^|rC!kt&;Q
+WZz%KpYwX{n2QA-4K1%ZXOduwMT?4pBF#dXP&nOh<S|hZchpOd<iogkgrLJoZg8I2I-@euT$R7K$O$d
+8Jx9BF|@4k?M@(cBX3#F1q$S6iF&c<Z&e7wP(2kEc>7cwyDy*ebL_aF_Mbo<gd3a0HtISi2t_DzG>_z
+Q!BWs^?$9-k33^VlmiB)FRhP}|Cu&#ozs1JSNHz{3o}WT{C(z9vnth58o`~f~iRd3iqbv14IaLJZe3I
+~(=bx`?Y|+8aH=)wiTD89cP)h>@6aWAK2mtey7Ds~_d-q)d002b-0018V003}la4%nWWo~3|axY|Qb9
+8KJVlQ7`X>MtBUtcb8d4*BIZo)7Oz2_?|aiE<>1!#w<5|?#MLqaf7wrdqyx6Rfjm1{Qm`?;k|+?GRPz
+dS!bCnK2LO(1odu|)_+&L@u@qDE>2k!#Lmiju*Le3(yX^NDN^@L{YzL##hd69{c>QnW2*zz(=Xcsq0H
+EW|G7A591^?r1a3MlcDGKubT%WDZAmk`;tlqvcL3Muhb$T5jTHz$XT6Gl;gE{ciR2zTfWR4iug;b>oP
+kaz!D=l}eItq*hua2`ped4d%gYtW;X*wuNWYa%B$7Q;JPz!~2W;P=TyQSV}RSFKJG$zDmQ;X>+WqS8-
+DzDeTK()!!_M9EC7~hICQu5Ui8&#7!@CF>q7t4Z6XQ4-UGOVGd=FdM#G@JrJgjGC#cdyQN`m_+L^=s2
+SHT{pzVg>5iZhK;CHMEGpHxksn!H#|;YPb&q*TVs(v4;t!-iXW4a#+kt#v{Q^)+0|XQR000O8^OY7yx
+xzwfPbvTa)SdtU9RL6TaA|NaUv_0~WN&gWWNCABY-wUIV{dJ6VRSBVdF_2$d)qj&;Cp@rjyyS{9GQuo
+nZ9^Oy_0sFY44=toY?8^J&s3Bi<HeaMXGtRqsivKZ(RTo06|GfPk(!6zvG8Q5`jXYs!%r+z#Xx-yC-H
+voaWbuqN(S5U*Rtgb{_1Aqhh%#)9agB1V<xr@MQn#9{lr1@k5$ND{&V6k}gAN_A;F%d6mSXE<}Bkh}W
+z7rpU!vF|Y5VG7&F|CXb^!h2P*qQi=%1%j+_|YU%>MU6)C+Nb(wCoh6BQd2)38>g+hI-`66_V{vr)>i
+o^gw{Oo+-<;u((C)#!EEZxqoi}w;Cex`%7t5lA=2umbHFYxO?++fx--}9pDiigoT2=6Tj{k>uNm&7Y(
+=?wK7f&w5pT*NdA$G*FjII~ap~wqB85%th@JC~x0si^*$*CGyeWw-`eVNv)Wm1WWxQeQTe;U){I(c8y
+LwMHYDG)zt0dpq_^+lQ`=h!f+#2h|TFKR0rOs7{(n$>9zG=MhpOJz1WD)M=H{W>ZuBz=Xnhwn@J-VPg
+1ucFy4bx`!VY3ej<TQp@xoz!#neT<))y#4%3k>>QFT4rf2AJb<GSe9NXPyu``6QEFX2OxfJikfCTrB4
+7LS0BpkU>Vgnd9+BFl)gXJ;sBbDar!j~*LhK#-N0DKWm%MCLFgbcM-K>2oXmOLpc)-&$RHhQ1>~%wd<
+IX(id#k#VmI)y3X>ck1w&dx!;x+Q6Kl#`P6og~Fqy-2IB*Gbpegev`LzMzp}7~!1PJ@~%}Y6n@6OL(^
+T%FVP@8X~DxE!R>YMLiIc16L`8ukq`=X4$zuwgU%<5--d6UFxnapbSV?Q7i&3KAaNXn5(f}6SqW?Y#+
+y<cSIa_0Q_A5GGbSp13~T8e#l{{2fS-Lhr-i#^UF13^gZ6)>AuNEzM3l3zA8Fi?MPA*Xdx)<u!&<(R@
+6ta%mjr_F8WQC1~w2l~nqFl_+AWm#Oq;IE22(Q-(#i4TLDqN)v98w>{RvmyR_akxY-#+QG=rtl3$z8s
+Fk-rhIDZZI5+q1h$`U>fjsR$N6{i;=kiS~Sy&@U&&WRjcN&=G@98oE3{DXs=-S_aNLI{RRK|!%!{+{9
+8oz>;{_ZZXx}`^EZ*9pAXfNqHp1KSv1RFe=H6<YYx#J3xx9EK>~!|pwrW08*L6>yJ{SM^ualYCMTyqm
+eF#l#ipyoeOkMtrTeuqtdr2v%)tiLtc6JG+BK_|{!IPb(dm{hb<rwWdzpx_60;P(spGcL@9NLe^BY_L
+Rz+xl`KRbZ`PWs8CjAoB?yXu=>fW#cKJ(^k*AP@Gv_bjvC|ieB(8x4OmPR%Jc~!KdnNj1iWNx9FL(<J
+DEHOfJt*+jrr%hc;typbOl-7Pwm#f3Je^q6wC#jSvY7CM=aRiV-lh2|$VbUh=XUVb_C)AvNI_%RavkU
+~Q10nb!${PM?sCLxS4_a4b8s`-vFl9X$=d8sTr$zLBdJj@Fi^{l~?Ar$7d+Y$}N<_06$TWTexBShEBk
+{%l!JlBUf^ecI{zdr5@M)`oQ7#T2KPD=MX;RO_qP%{LY<OJGXV`T|_T1y1#*oG&YE+OHm!`_ji(16FZ
+8W5Me$g63?p{*=I1fps1jEp9qq1aPFACHWWwMA;3M3}n2R!z%{Iq{~*#gu=f`+O)*rFx}(7Qv+u(VCT
+vFU3gz9UYm3cQe={V%`%>dB#q?us-9j^!y#MHFDWlkyJ2A#t?=H0Mo)^j{Q?b=-k4Dar*HCq1q}`!Gi
+-!azufXQFCK5Tq3Xg=HCCWodPj#15Qgk)^Yhh!zEmb9VL;#W<s2isG#b%tdhzky%=!6kLHL;s7X<s48
+-Ro`~5^0hot2qoNedN=Jk{T5SM=19Zz*1|id2%-W9P`KvR5@_Q%-;t`CyEJ3VCh7+&@h9m)8n<cEF1<
+XjyAZ`-(kBb(lD_A{jlJW(Dl)?1NU?e6Jy@1qj=Eyzk1HLv_pj{SXKQvmOLy#*q;n{bmZ(lwauTIZJR
+71lh5t{v(2-HGSK`UfHjOuKjq9)A6{Y^T%F#uxJtR-P~6P3{n^%c5JB`^R~qR67FBohU+ELnQ64+`}j
+N;N+YP4Xy#$Eky~yK?;FO_Jm9=+#A9@=$Kmx=OP77-fd$LBr>;@Nsr45f^oqRFwlxi>~!MDufVypF9q
+R7k>{g9~rAI$qXz*5OG+)_i23-4BzK_d*H-^<<mUtK6WRxk%dn~r^jz%qXX+6_>%PT5lEaYu97$g2sj
+0h0AS#=J;_rS)?4*pj?GdSxXfueM_Kw001+3c|7bDwlN-G5q?$#`Bv2$CnWV20mwu-xTK6eBiYOME^|
+@xbbcDwu39mzOe}BLGDWijfvCJX}qs!sDU5rLh9zY9Q>oAIAA|k7JRGvz(AoxzNbI_#1(lWa5ykPx_%
+S#)G!z~cRP9_r2$_`op-)BQqS;KeXFhmbJKu9D0=|6{PkiwI{zB+w#eDv(>xD$jxV<VhFu8j;!Z#x_T
+YRuNw+ot`8q)g{4T>Z7!#fG~gfZ4_g<{e_B002Ypi3f(Cu2#A~7>BcBcAM1Uby9<#EfGL48fiu&*>}~
+zyI_KFK^=Ux{}tun)aq&{*JLOiz>j&6p+=3z;LCzz^ctB63#O8fQB8v{0!|a-Xb#d+-8C_|X2|%&U?#
+;pO0x#2EKYM+&H$_eaX^IUO-^gwp?QU)q8AjJ^mB~|QoykrWbRrAEf}!Q1ANf4YjGSqd=oiXF4U6)3t
+s1MfHt}!J|Iy1hN)195=k+LBqasE3Chv&oAc@E>+_S-SI=G|Ao}6W@qfHMd2{@H(2AgRBOR}-0<|)s>
+7XzKL(q`FfX;lD)vus22y|fR3S`-BvKos!BnvJt=<ehI=GcM^4On@{)W`;oM@zuGf^PAHxY>E6W~CN0
+0UNpORjph_hiYHW7^SSpvlZy-1b7mo8YjyNa+{(MiTKqS(nNp;Dpw95B!xve>p9X59xFG<qBN7ZJ7*8
+An=3zQcEnFV{nT~xal!J^AlnZf)eJ=z!bI|@I#{;g77erq53zwY7kb+mY-R_Iq{WY(Z2wtYSARX+SAU
+zO07A$>wHkqAzwcY^9p`qS7xhEmN*u&RQsqPG9H|uxw)$b!ZTwIKZQO=NkL|}IS_bF=kM$%*qfy5*GL
+Z+O{~p*sEH~h+Fn|zit^d%64J;vGPg#H;i$OpuH>kBd=-CSj;`Jm0y?URJooVuTfVR_)-mMLjGYWvPZ
+?yzx1Fa!uNWdLP{IUCn;9d0%3c|}a$olg$agF&KSZ6cUfz~pqlSPx&DImp*x*}5{qcLTuy+A1sFa<BD
+o=yXH$#-{e@1yd%a`zwLVK|+}=?t2N%`yg32eM^{T%15u^x{b5QwXzz&5Rh6p~^kKr!q5uxD)7LwgAU
+q0S?NfsKOW1)4z%TBjD3d@M%mLya^_0ZMyz7y#u;MBrV{4aQ`v+A(43f?EE|JiAom>^&P@;0xQg+f>T
+tfjmc|*i@?ySpsh*(HbzlIBv56Tx*t4^78WLM4S(flgz_`aGXnO~2%Y(b;%7w4ZpGeh1z$BR&>aPCAK
+j&8k&~SmAYOwJVKxI}4WfedFH;C1a`qH}I-szTpGj02c;bi#o~A(Uh$A#}>_A468aOXCMns;K3{nVFu
+r8R*B$}BK@Uu8hIU-TUlcD~M;C^QM;TW6@ElMzH5t>@@BlzXTePw`BO)&F^Suh$G=)uS^@X%fq{Afy#
+f_cvJG4iGW0)Rv$N#3VbT|w&+$&ntQ?e8@-*cPXvz@!!6i%_hv<piWX8J0@IA}Nf_CrtxNQH@1b6t@^
+$H%s{lNLWTFcveOr!Z>D*OlRv(a&HhT1V<*tEXyow;zSY(+m=ZN3-d0K(q;giHNZ`Xb`7pjC>K7iW@W
+l;B{H4s2}4^BOcwE|<Mr8`&`aH8*CjikiH}RP{bi0$^z-gF(UXaP!Hn@|kPvLsaR>27uZZZqHv>FPos
+*D~5LL1qStw%J00gu(Y8W)ngZngpdNA;Ln;p7~I=V%1hp!2&=viVcjnE~s6h8n0{n01v&O~7zrg?>7-
+IH<K!@!y`LQ)<7bWV#jLH0$6P$8FbA0h}I!xTU10Ul6)Fc^7=W{-{q9;|0g1!QI$3Vxcr2j3NAMq&vJ
+o#a@eQFXI8Fp>K-%S426DEO~9nd*i%(b96(q&3@N9&WHCO9lEM8MvTe8AD_AkQkvc2M0B7i;#Mw?ZiS
+QN?^^4n@BFSPIN%cy4-{r;zChLNFZg*X)Tzc`f)_g15#UZ8$SA?g*4;H4&Yr{PrLq^+g>~(_}y_@K;9
+%#E>n<7@++|_8Xy_EluQc@yDO2Uw~3ZCq)!42f~K9cSyZzGRLy*zzLz-zV9u<;oHvlWD#R}snviWMNQ
+Hnc*-IVL+Qeu>-6X+Sw*o=6L)zZ8l*6p>;Y6!I`~_3d0j(y7pSU_z7mU|nye{SO*D#9XK(_4K8bk>QO
+QcA7QB?luh<y7NIf^F$<XCn0+p&(amRm`FZLR*KhRn^V(NftDcEo?C%NK-oCF{CSp1^I3t~u9z5Pr#-
+A*2+2|Kb6cZ~Y@(@_gA9Re-=z_!g)wzfy_TO;km7T?WSQ5KB#`n3OH2)8VM!T~$yg7SL9<doVeEUpvU
+iUmh6ILHlc9H!?`-@#3)@Y#R?A+vy(-HO?UA+zF=N_rWKfUhmCt0{ng|dGld2%L)*ASAAQB6|gi(qX~
+{5Fc>IiWb9(vJ|L}(!C!O@^KmF=lFduAf6)mlRB%gQp@ETDZ}f(2w#74C)7M7_Lk>x{+>lSZDN?@K1W
+xU}!cW=R#`$@H1a=_zhVhGJyr#BKR^-yQm7+F`&`H$}U$0)ThNN8EN=>gka7)LFWqmmj!(XDiXz1_vz
+X%Vjr!khb`E&w)52NjFK`CHH3s-3lkJN*fLv1xGm;8JRANndcHLP}+8F)vu%{$Q_)kv5vQ9=LOk!D4f
+@!p!ww;U;0S}qiH%$OJyk`yq?DdG88_qX#D71vU-nN#27Bi`;%kNBIK$+McPijxgFjH!f)ayTJYgC|Q
+icU9FWiTq3|2eh4L2au)3)vPjP2_NH#=zt7S)@L>I-u9vFW}rq2R2#exHEb!UCLAC}PM*}RNL3BMrmV
+nA^$VnGWTzLsP;^DxdPu6&)u76_o!sU$Ayzc2{T1@^8GX!lU~wIJ3J<G+c(^t_jCi&r0z*lO=IO{JB#
+XzV5OPZrY(dN|SQ1M7{;LwwNwgNXNuK^gyGe0U!;n*j`6nwbocc^_l5bDHpT0eR@f9aXnx&~6OXWPkl
+Qfx@iM>NRU<57>CztQ4-QZ&H-F<lZXf!ZHVG0X;ir#mW(e#3%Os^4j+hCn6QZPb5L8m!dA2t0loP~B;
+?Uc5VFmQ>kil*kB$+U!^W&fZp126&>7#9_oMB(Ca|KL&wgle07FvW;^Z(t=dsN+NosK$JNe1>04`@~P
+6msjp&KNliQE*N0aDBs)1LDQ<(`|7JdecgUj52+~QoeA0*U&Gn60qr@E0K*;>jlo^xp$OKp)e*s<Ork
+hT-F&kCjL8_c%Sdk`V+hm8b1dA76A?=BQwpHEDVnTJV#y6N?m~tsQeI6}H03Pe#IX=3D8;`vX^G1-hh
+`;}HDC&xQEp2`Q_f9SD0lbK3S$ubuH+9errn09E=nWQdm`OYw7(iCV6%wcr;BDmX_fnQCiCb<;#|)UC
+!)axQKkL1kxad3DTp*xrK|)(uLPBM(7_W@cIXq|Pys>|!!&H{n530GCMy<9-&2hZX2JlzJco1z&+vdy
+uG?#PtO;;rxy6aaIS|e#%e^NDUpz&E&!ImhU=a*YK=mrscy&v43{m5>qSHt~BXPC@fAl>UBM@P6zlJI
+j7`QbJ2^eGG+~DI~l%-q<R3*r2QrZ@1fb|MbyO3<9_S69y?Rm90^4XHTD!rz<Ewz1Q(it-nEncZXAIc
+Cg%jJdy(pnx@X{V@R`WBEj2<lIn4N}M~b6`L)bb&b5Hk4i$tq+)$lm%i;i4ySOg)ZtPpsg2Y^-vR>sQ
+Fo;8Dcsu%4t;>%fL}Xof+bvTrFybPS7Z4b_UQ+YFw9{_{4y5H5}S;;ylgcDa%jbsIFdocEoe3Y$^dZb
+MLEA9H+80FrlT%qS(?0lv5TlYTcUj9bJ>iC6}pEb0`qY23E_nv!jy}+K$4iKv^k3BYS{hDXU_DLZDkw
+fLKmtTsWa`${XsOpfZU3mJeX@^gSYke~Sts4%G4kq!(#LmAF1~bT^^Xc`J!>C#$;;VGlO^@R3q@AO5Z
+UI1ITGD}YC14XK-!V{KsQ0_@;q>s4NS(z-q&3JmD@z#yPED=GW-N}{!3h{Ww;>bOf17?uHS7Ef~Qkg4
+Rz1q!a0p)aXT4<}`I|0=nO?ou$Pz{o{9tE}7sW{E(V*G@^MB!tPxO-vj#`E6d@x2uLrl{>aa&%m2yUj
+t8z$f5Y~aiGLRM+|G7bUzXCZyPyE=dBX;mS+*mI9Kv-T50xG9{nb!$N{%wOiRq6CgQRzuCiny4=WG7{
+rXw62BN_p2JLdlpIYyse+MNs+9Md6Ot{be)xxG9av(8rjF#!bQw!4xSD}=fXf({|dD%GSYH}F*|KjPP
+y`kjVQyYJ&aHy-l5TCn`u?W#;yG<KNP~4!0m*YBu0c`4N?fuLOnruJm5N&R@0dE&}5AqD_^RXXGY>bi
+os2B}*#0xAFDe^n4z5<l~UHp0P8}aYaV);KrQqMvsjK@PgDzN6<lP_{N!u0FZY3@5Te|UhW=7XnW@kc
+3kP0r1MMD3|B2yoT!?k0?hmu}<W?#n>lL}~tgwET?pLkHIPyy*zgmU<0Z7LGb%<(_ePy-a2e5aJGv^O
+6pEPVJvK6WBvyxw_mwsB<zZ7@G{kFm!Xx!=bG`&>y0JkdH0gVw6GH=-9T+JTX{Oc2SGG1rB4#&Umgup
+nw;BsQh}Wh6(Ugss84z16h{SvdMFqdZm5YPAe)O<3qE)?H~0=)u<wkxy_i8HnhbM^lpM;7dj!(qOo!N
+Q`5|^{tZyH#|uOFhxhtQEtrZGtVKbdnYR<D9jv}u2hl(bB3nE!(T|)Cai}!S<B1(bxW^CJu}slHLF7>
+tj06?UT_v*!9~bCr6g7CJ(GskC)g)C1oMbM9ii;v{GQ7cKg6yMRUW)>>&#M{@ySTTI;$pS|47FGbgEO
+CNfpC+xP@){{3Ln7R0L*GR9D`fv@$Z;361>eCZ5%o)(cEoSDl)M3o3W~3eT!$houDK2Ra?1=ThNsVKx
+1(A$ASS=BTzRmIkR*P3yHwq;sqW8V=mmO=lyKjeY#7Ndm~+e2|2nGCZjz;1(z~3V698e>Tu>M#O6`WS
+t#%tm(5j{&M31pGqNtah!L>mfn&kvFzJK~3{makAurBC5YV>dlc!f<4%onwW0XpkS`r)|((+^qDAQ~O
+4Y0~KVud3UIc*SplJgZf5nA&5GOd$Jk*VSYU7TbI+a{_(Dwdd7&~>Zi188Ux0|Vosn`LkdiIEIln{on
+eQaWV@m}_acTA`jM*FuuduBVmrw?lkRL|F_4H~S9Mb3E3fA>@(5ltdQm&}0fWe9$pJ=6Bk^M^q?tP}K
+oV2RVN6;p1i7ZbwAfeYAq%L2Qz!q6#iV$|a6W5NP_Kn)GeB{4H6ri%)vg0Rk%l4;rokf;Lx@go}mbG`
+dQ>*H-AOF9^y7jB#l_-@(No74=YBJK1>16{*sbM51?t6!ucoH2o;hS#=LivWTUnB@)2Kt!kFML)~1FY
+NB&z5$1;Hn6xrF+r-5}Lrv7Nnu1$8?%=}2X^lSqMv<ZF?=ldgytVZv8C7M}<inzI-qj2ojpPK&q`^r^
+-E|XHH>&I(t6B?~oZc*N$4xgCx?qFekzHu=2tu_^m}Ol?z6z9xuxwgV*?|mZJzI+>0`ztNi|gHv9LVI
+Wtb;uK@B~DQWM(?mhWcahVgKVu$LSqu<@x*0-5B&U@uUG3V9|=$!Vvv_pi~viGCYWiSzanm-1XG+43C
+uk(;8G(f=i``D^D89xZMCzKtpd{0LBflU#Ky0k#M**E0!xCJ%_K7`xY@iqsCr>L^6lx*ctseBoJPQ#y
+dvyild7qU9;dtQ>p;Tr&bmKNJtogm)^=Qf-%rW(Os^{W5N&id;qQK>?X~mvw!HaTK(;C>FM)dqWroT@
+(CU+tz9<NjT08wq%fvW3{uBh(fmbJ=9OT&t0DsUndHp=!$)(nrkG*L7zFDpYlh1Ri^Qu9^IJ1RE4k5~
+AW51c`&JX^<rFX4s%!7$$wgneofHFB$`fwqLIkI0g9Y{4Md;QY!<TICw`-1hAHpZjX;Ho`HM}lmO%Ls
+4KcebT2pEF1@vg!*$)dPR%&pjn)cgX(AT14E!NL)5EacEfQVWxA762E@VduI{ENe|F6uqhW?Wy&uKvq
+?2s1**o#4*Ljt4Jl0cA>?tMG}*4DMThDOkFz7Wmj2wtS0^Wv<eJd|KH2k3|v}oeq?xrPt<(J==$EtNQ
+G81=I0}@B|T0xDQkk=DFI3sOMO9EG`mTr`0JD_9X_>yv;zRIRk%Ut?y9+-%JXiC>*Jx*Sn_F}_UyzkT
+(0c<d~6{8O$MjUgcK0|H0_7vC^kB@fLZZbOUv%wL))zAl!VghmPfM57U|B@5(3X8evaPzmIU9~5g$H$
+P**(RPEQEj2Vaci+*z@B4DMOFOsdC!`tpk}zJB^x1m)MG2Rq_hRagqXfH80|l8FWK-OGl8RJHeW5dLu
+t|NII6`2zp>l2+?-wOru^udmtb=f?053>clSshYHAeVFF6qFiD=15bm1<)uCCh+4@IIqb>I);C;v!lc
+saK+4JLZyVFG1S)P8i`8^HfIZkuqbp>f>J3Z2`XNp3$9%zr{CooHmp*Pp%eW$psc&)wT^2K+^SlkInM
+uh4ub?UMz#uLlDrLw1tw8RGXSj2BlVCa`W+#YZY*Ovu`EelQUWDZIkU@TB#=~P3TaB&ogcBXm$_3fp{
+=%*}rw<6-y$G)qHamQqUhka>l~S%#yzPQ=+dz4<yDq7U_<5OJH(6A+$p<`B`Jgj7x_l?ov2C<8vlSM3
+vNXS~I9(MN%~eJ<Qh2EZC9fR7bTW;G?=%&>lSjls4X2|Atewxn)k(6%zG_L{Y2iEvR~A-3(zoJl62K2
+?+`6r>;2vv&c+)n@t_$Gc&0=JNJO=elXi*Un$Fo!urmDbzqt|pyxJ>Y3&5PG>j(-@7SI0k{kHz1P|Mz
+9MJ_FUBYlt^wqNJos31iZSLY$@7d4$(LBZ3Zh9p|>oX+Dw!tiy{Ru^6wRONR7~HJ}iS1^g5{SFWifH;
+hLIzO1!GMK|SDT9=67`V^MD;elL<?$s28wo~Fe)F&dNns4{mZxJ7hYpnH+hvH!icZ*UDDc5CD*vX(Rs
+<oW%xX;S}U0k}&=x?W$&|H=CTl0n97l#-dT;RvC;D6C>U-k`*>v1AJeC#?h)2Tc~WeeK&G0d3cMKzdE
+@NKkrMYf2|r$IUvtC9HgWb%zD;*0chva;Ea2Vps-${{DY{+tu2<WIErxGL)!kEBMb95q%)WLarAaB+F
+EX01Eg*+{a=%(y=3k0Zk5AM@rQ1|glJtGCi;l{q4h#4`W;WD5VXs8W&wZl?F~#r|d6q6G)6R<YwnMtO
+6Vk4~&-vaK7LZV_@y&dVo?^SYsz^DP*~b__~^^{Y#l@y{u<kDSMIWxy@U1|$DDb=XQa7!@oo(7cw?kh
++)eQC(+A!4jn_h`DOVLz^X8WgF9(Zz`*e)s_X_Zxts_3g4uH5s+JL5c(8TBV+!UC-0dj7UNXx)YF$au
+KRZz;jb(Vy3h7GRA!KTE}26K)gm4_S&;SeiwFOdWbWHC2aL)1oWH6V#8%xEvcmgpsNKauSNi1u^mzZ$
+oneXQyh`GAvozYsnW|rY(`=vo-m`sjxov0*)0DBP*!zZz171}pvnl9oqd(35H#FTM)SpY?*QsxoSd<W
+Uc-;!&7Ed;07l+-sZJ`N2)7<IE+<d&HweD=(kZLn%-Jic2lWLw|*;v<E148RW5^^QD1#LSTx0$V2YA3
+L4fuV~%X67~g!d{rZ#1Q!M^5n&-a<S%YL(t~~WH5vM!g@-#K5uruxbn+u#hg@)Z8$02=Is7&;I>4$I}
++*iHO5fsI;r>8wrQ=VU(y`yX+HNCOPa{-_Y9o#jUa@ar;Y5}tJ^I&jA3U2Y)WW^1GgqIbA1ErIeRh_7
+IG`y=siT;%B2|u?(K1Ajy40#h%_$vlEGw24p}a}Efp?;Y|bb)+1s-h746;hC^mXVD@esy6kEaqC}MBE
+p7y>yD_1J6o?(<)GY3+el)Xb;v^%QumO`a49o0<=xNm5FXpP)a2y3z6)*6@5W@O5B6xYuQB~HO}9UW%
+Qh^HG^Ah%{aEHLcHnz1F>`^N6)3QfD;6KBiIWb%+?6OXt-FIpDMxasGjh^9iK=@8HxzWGd#Y<P@~UYK
+6NTY3O^$&4Q8fyZjsR2-98df^y~lQ}$~n@lK~rB3yZ=~5tbPfkzn_z=DPThv|0Nb3%?8%mm}wax0oh1
+Wup?bZN2B`bF<5BTW$6msRYpb03j$-&ar{U^D)!4gmY;1MyrQ>-#~KMz|JCA1f69h*=;CEIbZMYcn-b
+tLb~QaO(UtfG*2&k29=zlBJf+6f-UT(LC5BuSw089R9rI?+wn*TK`zLX=C5+U$%7o~V)Sp{OEdLIb(R
+eMfwcWxV(@oKhE+-6!xHmKayfV#(VAeByXOg=_XvCaz_hh@f*isAa`u;^T$tNq9MS-9GJ3EjeE9zs{D
+F12**g(XC;ba5K;LZDw2oB*(<!rpm30_Km8R)aMWGy-cffRqvKl>{b$YL7}eGYs?!A^lYQ6tQXc1^9R
+fbC04<e-tdwwFa?A%bX9_Mp^Kw3wI<j_RNj(I@I9!(`rTYrpQZZsg$%*%#W=nsoqab}F+zKw7rE#hR;
+CLnt7VbX;S1jPb#<;RxzyXMfRA}v7Dbp=qj{B^xw$S+`EJx`hTGxWL6-I0np>SF%7OJiwF|PHq1Abu)
+W{bhp*Kb|ecrFPBCbkm`TLf|cgR+wQ_EfW9ZUFb$1#M35TF$Zn~leW7;>vJi>?>3na*~-Uq`>Hm7-?2
+?c}YZw6U0)<@9}X*L`&IW$8&XN%LYsJhZ=dliGMBW9lAwnD(7`Nat+_Al-ERc?VTvhX+lphgQD}1re#
+azd5n8Hpn)IeF$2POtM{mq^tbKy*2UJ^<lZbGcORSOl?TLM<{S*_RPI2BylrKdUA5|{wvyN7irF=9CT
++gXqsg@yUnmH*XA6#(-U!V(R#+0wlD1%WU$3_M;uv$Qo8lB+RpEgQ!{Hn-(zJoTvpVand(THZq+MQ4r
+9h5U_TYaS9kWRq`rPz);VILkZ3?*jHqt-dQer`*b~lq3K)M&eJG#c@@b3V@br0G%z1VJZCO=f&0crEl
+Q(L($ckP+Ye7RJDPzUt*9IJ8+^rV@Zx@)iPZK5^EE}Gf#Bx)Q@US*f)AOvGekVt%+RlA_ziJgr*4XPR
+$Y$-1+GK(|z(>Vm8I@^;*_Q%{z<%rLb9$`<RSQ&jX>9EtZ<G0JySLsZ)&=?s2=4MqFq@r%u5)ZGwEl~
+cGkM|PXfnZ%ObXtjG1M3O>+I9YAG{>y{i+UOUCQUlTFR@tKcx!SLvNKGqk0WIw?dcWq(*Vjmz|6l9tr
+Bp(F`zMrJk#moYZ?%0n4tatT^GVRBVgMI9T0Wm@)9pNmb%_Q)lpdf8IvnW2VrsK4AkU+}w*xTMN{OO>
+eSBA!8)fK7l&a%ZrjONm3ZuMQK@aeT@~Je8r%?zALH{e$#YA*&@<?X)G(GKA|-5<oWTd^OF}R$8X$s$
+6iEx|CnClzfbnQo?h<$LB4v)e4UapsP!i21psRy%l9!=R>>gUnc+M1W4sS&_=>Kz=Zi(uWe575V29#i
+iN1&dSCW*D+cTD2taGG~YvXHNRA`3FMxOJ-FH{?f$|T13iOJ#g8z!o;geUj<SM>5hx{X>M2B(Xfu&|D
+g-sxl_2T~FY-Xot#NZbz(bt@1RUr$O00LPTG<-(-8S>k=md=M-xCDyTogBM-Fa9o%|uSSd1+ruikiLK
+tPW(=ubU{xk+bPdpWhe)>KfNw`WqB<o>G<9ta-9#P9cj&S_wnzTNO_9ZFcRZTaU@zP0x9wqSbl6p&t*
+5I%tz(s%hrSZL!C>&rJe6<Sa(Pk|Z&q@Y^5!#S*0zSfgR`iOKr}R`7)tEiH|G#B<PBGv5h^)R!NBu*Q
+qtXJ71EhDP;ny&YwuihsC#iy6{4~}q2Y;lB`CV4kROle$~v3=V}l<ru?A*iZV><4KIbUWz3-vIQA0kV
+AxXKeA;)R(uTQ<YN31{Xe4E97f4BeU|Gy0X*W}I|-uLfjdieh7Jl}>@zXuOC(|;@4_tM=%b60sIq5)d
+AHOiYdD^0bn8h)zQrdr_t*4qEts}}$4m7B)zCwu$rcy6`*XYhJkHvJdp!4?aY;0Lucin~PV`9Eci5~S
+a6k^X<eIH%#(i-)=EhKuXpuXoNmt#!^RD3QmORg#M?q^*ht-N-v{GM)IcuQJtNj!s|wfUiw`{p|ew_{
+}Tfy!7|)!T)^y=Vb|S-+zNK=jEf(;p4~nBcIj!CfptU!93OY<LR5{y}<BZ>UZ~#M)1F}2mJiBfB1Kyz
+t735=f^+w_qs58y}NXVJNfI$ngQcS)_h1vr^=MjrS%`|>=&4ip*W=fRlA`rgTvYUnwr4>o+cm7WYUj`
+SftlCU}vMeR^eCF8ZLhO61}jDa}HU0)%q{=3++&1wmqG3CMuXt`HANRD%NonmM-sWLO9Jaal<{?qpyR
+d4%GUwFn~o-WOs=~z^!dD#UKCPaBCe!;|m9A$yD-o8=WVR2^qTaSjD_HmULBNxOO;s4R0mi=QRwS$_2
+{nP52$;Xxg4^an?lLJ;)hN$uPVWDei9Yn6LB3LHtUJu)zBiLwrFuw)Zw$d-0aSN?p<<w~Xnv;NDJK7T
+c=7ZD#vfxTxF7u#NR_-62BVW-RI}!SB9MFUbi;csRJz6w6P|6xb16TBXESJjAqD#anQ&JV#wb?{Gr}P
+fg)24t}Z2XqMnhOX#i#LZv%9O1(K{{)VEF%f@%z0Lb!lxj(M8mwLe#f&ev+Y0<iv6<O#CzPS&iR;cgf
+h@sFv7Zm`p0hOxu?xif$A~H>m%xPk$j=!<!zh$-i%Ja85r3;NT;=^(P?De=Z{?ZtUH~x3Az(d->$Rk#
+<?KeS7zI6<w@X2Mj`OXsbUM=D=!R<WeEggL1Ux}JzhRN-vm`S<*g^d%4H=&Jx8!n?&m|2E>BW|1VqZT
+n9JGXFL-@e_m4=ry&;)Aiv`VsMuDFbcS54N5z>Tj*`HvjNFe;^`+BOZz>>CFjwgU6ob)d~TX5aRYo)^
+iP_e=7O<iO^(N>FT<`?Qq^esr3xrZk-WwYbo8`<>;z_&6Sq=Z}<?C?Q$zbz4Dy|*-2Iq{_REcbh5&yi
+(_}ke+#W^eV3k_+5w@xV(Yc!7-xmOn;Z}VIcU)vTIi@9?|v9zXq3?r0J>fw6lc;~qdg6L?;^b_#PJ!`
+3sG0bzJ<Fdg3g?|FQU7|#d=3hu4rRhT5`m8XL+x=+p4FsQ-#WoPwX)fy0zCKHe2bs+jY2NnA?Y|lf&!
+Bl`?+6Kdu~JBeL)OtEb!9O>*?lfs-P?eG*+9IKrTJV7WmF{%L#V#Lv<~^kCGl$>P>P#Z-&rZN-5&Y0D
+vh9{N>X>t;zb-7uCmO&El)rpZ89_gUlItD9W_E&kktmW!J5@OAXbPx_6Lk^tQ_;PJdAAfC&$lrR7(=)
+Ljl;$F}pDmRlD<qh&43cSU34=-}euts+t(HpHh&g+bE#Yh<A!(rgozRLxqUl%y@$l6RXQF-Ds8wP6d$
+{8pe`q;W7j_CE)=zH_-eY<UMpNZu6Y|5RM-~ub(q=$N|jvHZ6s;VFCl7rjPWlxH57WiM`iPu8nEP4*6
+(wpRTT{yMuFAT@ON(vSaJT@VCMa7rbj;bjsqV*>BZJlOmy=t!*mv4|!g#&Cyu|j~%Xh%Z4%4dTPulux
+~CHVgrP)h>@6aWAK2mtey7DvvPfCGLm004Br0018V003}la4%nWWo~3|axY|Qb98KJVlQN2bYWs)b7d
+}YdF_4ubK|&?=<oh3m~l;!a%9bZU&?pRoipRzo4TEkH=a%Qm3K;}B*^BPBDExCTgkis{q+aF0aEhpY%
+W)KtYpU`fd<fMG`btzjl<;l=s20zi=w(dNjBZ`_z(Ez;PBuud0wx#O>uqGCE4>)^2KMLe{l@|`6_vTB
+a*k<?xwDii+b7J=Z#3dtvA&o?~1ybK+OvwlJ8$WKYx93KItC1B(E09^Ea>GzkB(oAKt%tcY!aV+`*G4
+Pu`xtO}_l6Z<1oQF2zbz9Rq<c2M5chUM17%a?@=ZF`Z(Wy6KWlRX~Tuv@AN&<YjwsAV19VR($i7`h{i
+Yt@%}T;-M>xnR;`Rw>Q?$vcA3sB&eUYuDPz;;$fY4H|j;Z)!+VFtobk5*@^1oi=xH0&L8GtjRP7djcD
+slGZ(EaIj>g$RF%Kz8fg6G8~O20G%eIpU#~?sZRa<DpK<a-)o#}KRxJL^rOunCZe+!k=yI5cT-AIfe~
+*(_=kL#6oV`Dre*5zK^Vesu&Si~FSCp!Bw$7VYOdIjbrf6t3#>qT~h8NRyQ?EtSZN~|hsq&SW!n{spS
+H}lQ_%tG4OvMAjZpX=e0~2O`KEL?$JNPad`tvXNu^Am4989NqS;B0eCYLFqu_<O7L}5Bk(m%lrzpUC0
+N{EH~(QZHODU+n<*ViwrWj#>U{`3}RJ^cWELgEk&bV*Xl1ZL!WdY4TZ6;tT?`HyekynBD4F%0F?^>zU
+OkL9Q*Key1}!Sgq-UY)&u5iJRHu>!oRq6aTtUc9Gr7f>imU%tM0fA;<N=kL<Ul)?btoj-r`?nO#}{hu
+GsFW#TONd38iPZw`~c=!DL0?PTFhME`ue)bNkjkxibufKgWJ^%4N_Hja8EC7Shvx9k=w{0?gjuevBv!
+BJh8=X*FxP0;7v!tbk%~X`E^7R_9mmtPk;tB>}f=jGTx;j}{GtfHV7Geqfps0#&I?Y;9E@g9e8GK$si
+6SqHRzM?3fy7YF#RNGQ@T#Iss=8SLeQcAu63Mb|5&`{7{HdQrfn&Q9904HYZ~y7O#xX=*UyBMj6|7kJ
+_APKMaiE7YFGb!AjpaEONV*$|!!bbYIz(d8A+n0DO$r`v0>kXYj!sXw!BN=n_?LmM=Zgh(d`vUe)BUr
+>BFUX8#a`>-nBJZMU&&Wg@`Sb+zYa7PA6v1cvW)zy2A*1(QJpxnuU}v48XsU__#-Sw=mKoRSo2NUWmM
+%>01LeQFq!}<i)ukHM|v&z6ck?-@5CB*1K_T(25>r)dg&59lPvNtAW$enBg?w&x|kEy!VW}zA5Dgr$C
+zrKz6L5E1I&53fo)S<U!T%U_7NO#?ao;^3GEczjd4=bhDQBhkVuTBxX$O>gwTOIO8d+rqi(bA>KfkZL
+5h((;D8Kh>WB<z>@fYC7RHbx#$a7blr@0cx+z3ENnS2#>f5!L7t2B{qWe*zF^5b5ATEN2Ei9cN(D%&-
++U;(N)}Z6QC`;H0>RVzR$i^BNV^c{i#9Nk+JI^qldjY`UUy!GOU7-j=lP=|=UW!+Pz`1p4T=F0_(kO!
+pKTOrYv4c<o$K6u`Py+?5Uk6@6X7;CQXBfpkODMxds(e-w=ZW-&<stZHgrh8#OBJ^+LN+c4{O-W>xvi
+YMNXr(KHVS2&+~wuQUP<x(Qz?e#e^P=~7It(TX?Emo9sG6Y2GSDCEkFitVCnk2H`pl{YJ5j9^QXUCc-
+j`CTudD*AL`uy#p1RU0ITU4EUF2B&*)7rr?9~sCVvr0B><toDWG@rTU;aeBE1vPX#ta41FdKgJjqKGO
+@Wio#5@N+04vUdMTNgFs?7tgqE4*ZBomYC34eGYW<_2-`(d`Jx(&>11HZa<ga`)V0+b;e(sorA)h(>@
+n*sz35J_OA*H|)H7NtO*LK7pZyP~Npl(;7rwxQ#v$Um(qK#s#}zju>c7S)1;RXyGbznVhjDVEP@^KdE
+wx@5g5Qz6O)6$+3doDbLzSENR=G!5V)Uv3iUH~ldw+9^WH3lJ>!Z3H(-zyxVMSu@cCbK4?8uId`xn5a
+$&-tR6I9|gtiUN;kiZ&8IHdwVSbMc6BD8PxIwM2PmjfElqR?NPiDrQ1yv6IY7q<mbAmcqkF2ARHY&tZ
+jJMa9W7hlbGS^@qRi885{{TuN2e_*8@mPrwn<Lw^La4B41^ro~=Cc@HTLo@;;o#N5Hz=WFcmo>ntV1M
+e?)-O^7EtUJ0juf&%YQA>w_&hVE&Wrz8@}3S&LyQ}xIG!$S6ON=!7UBFccmaAS=SSe7DBW!>!&O$QHw
+Nk=_2`dcHnmYa)*Tt<$)=d;$bdw0B;=gfj>Q6UHMBs3Vnx?XQel!>+4k|0Vo3>MwGo4iBz4?tkl@qG&
+Vr35UJR;|5OD_TxrmXhKV>_MO%IfMfAp<9K~NuVbI<D>#=Q;BZlktqk@MXTgu68^_Y)b`RtE;a&?09c
+#yO9@a`cGjoi8yNm3q;L?ziB5tqb6QXW?8Q0~0HPZIMpO$7;HF+^V#QwJs)X(}sB&iWvK^C&1f)wguJ
+zC^sz*my@QCQ)NR~cINR<&F>$OCXz>MJ&b_N7MQof7E2o%6~5yr7-=lL4S05PmUTbzgqK;0%+SLtzjb
+p=ut0G(@?jaZlY+@dpF5m<PNg;~Ub6~Su@tt>!V@YUFlpcHmmezt@j>^vDb@DtftNd3fL$}C!KRx{u-
+W|B|zh^fX23NdEXXgomUNVUkDg=<$J__M7X4%&ZXWJV7nquBsUsBsIdjzOAR!vHrc(G+tVJ%kBp5TKQ
+zM4%suSW5#11z*Viw`32FjZflWaEJ;|3}upBtaCIx_jUyy5QI^<UDRlr#fgC>ET<6%+cVZdkeMLsH^C
+g~2J-espfMPcq|mtnovm08AqyU^iDO=fs?}pAEYn9HlNwV#GFjnkPqYPLOIWuGV0~53UUAawN0uSE0y
+3DzaROv-_?A>C;R8!%m(F7TFiL)(*y5hqw@?GzTXlNd)OW>#?5GKygup;}6GGEO+q0L+v750HOFFrVa
+&1`1O;9poLch|sk=9sKanZxv0tv0`l`3(odkjT(^Z~jZ5XOF;=`)@LJ16VbpJ-ELNyV`#hC3KT^yCz8
++X{QYyA;X%WK!7GzQ2D3f<#!mr%~F~b~GGoJVw^ibb_m*hqk}h6r58rk89UPWV1-?JsRnjn`$o0lv_p
+ZRC-x7Fe*?Tf!VN(M)t(2PF8gzc(by1A{r-hS@Ez2E+H1m$aq86_g2BUx<YjswljFe?V(vQAoyV-9}i
+M=fN4OCYBq{LIm%Pg<fGiHtnY<w+WE~_g|gD$ifwYrorqJA?t5UBSI-Re!rNcDws5?N4sU72yQRD27b
+VXA;qtQ&PL%<rEYc>eto_^7#lyDczQeW5@>|P|qHhiAorw+#`i|6yUju)yp)d&t?%o2MF`5vhq09XyJ
+0N9MvrWTt$}Oq2&?RA26&;(|XM*hX48QCIABFJ{+h4c%L3Ef4XZ>qvxOXw1BVpI!03Dr8DZzmb6E#TH
+q*|PD5mgH&xslZ5xrN-p0h7g?&~1smgVK>avxh!_fP^AENzi=KhCc}?^y}^U+v6|4`WpDgUUKK4AZ61
+BG%&v*tE0nmBwNw`3^Chc*K9-5H}Gt8|3tpd(x+{bJ#9y6@-)d@UCGrSr40IKoJ?|*h^(?44~l({jVv
+uHAb~=z+1i@6r_C7t4^8iBMhQCTSaTh28cKj3D^)8*^2do<+@a*KMxd&AIO_G3d$mRn4muKwxpidgsB
+u)yPOq(4mg7c-`8_@nk;#8trmvoU(?HNV@n6|-rlX9;-Xq97s#}hzgBmrP*=-)%h!WJ=JO0wcWZy9Z@
+2q3bTwC!;Q;(aKq)91CK}3AcKF<Ufn>k(yD12?!v{<sVszZf&0o2bN50{?{U+zIfo7ffAdq#+h1Q%`W
+A|n=4G+Y*<mDoczcwM7q8ygzhq9pc6ab-5GKqe9-t_VEFZn|Q=Df31H5BRkH!iUi67tTm4@@9Uc4;%3
+G*{{sJ6!r>p9L-yj>IZ4R#Oawb$)KV{$Jp2&&nOzjA*Ab(Fa78t;aN$z1dzcrm#WZ*7!zPwhWEcgE}(
+>-2p3Y1Txl%RJYtn2UhW9?4!y)Qau@a1=<{E~1NnrD2MYBVq#Z#f63btsVRIFpsyrytI4`KweZD2ZX0
+>V2{SeP4(Ilb|1mGtTMcNV(%Rlj0Q`*_E2e7?E7dCVDwN#y@KW3W(B=N2Q7QPVZog%9FR{QPP<GK*!J
+cO1>vf*Mow^e;ljy5yF#F{g}V+5F305h}=5*VC$?p~5FJA()E;T(CrLkUH+wkMJN3`nl)Qjo3Ck$n_)
+aVcM2X@53Qwe$fJ@<gCP0wtYlpnsLLT;12r!kQ)KtMl9ZTEJY`UN*BWN|}w_Pn<~<LY;Ete_~KEn&{C
+*d~Z}IALpdz^AZJx8IkruvCJmw2<-|w(awQ7*?3x*C2v#hz^T7=ET{f-3rGc0eVjN}Gb%yao#j3y)lB
+?;$m9#_=@>>Ks<yzLAR#~4rf6%+C`I*^%xJae3&K>ctmkA%@+43CwkWPe8|cCGNj!87OecTh_lM|XR<
+L(dvJ^SG9k+&V-TKm@*amQDpW}n5(cA*jp}y3Rgy!}P7F${8i|M8*+u)Sn>U_)og!Jj(l(UE?Ik~w4k
+RekcQjQcNaPLC2tM-#0-hEFKLoQCc3B`7n(eq+@As2%+I6Ajip9x8Sf+cv5W@>hU26`<E7*fv&1BrBC
+oM#F8o&hlJRb%wgb(wElbEbg8pw>z%lnG$YZh(RGc$g@guB~@yG9Nn>JL(&XlGTTY5OD~!9A;%3MpZ`
+MH86M5*nMIEKcVqws<Tg|R@XD`b!o|#dcw8Vp$QhfgZILXs(~gF<*iALczRArUI6T5*#`0@efFHZEVp
+$fJi@DK-gUN&zgqaC(VyJI>+htWtqqp<cFiY22?RF3m~=I4S+IWXZvH|m7y5<mXRC$PnQ$?6P+$^_fq
+;w?=2WK^*V_C1UO5gZZZ+zq&1O@n#sm6v9OQytrgqh|w*f@j$F|_q?QeqRpO0+At=!)T2qxXfw&B$6Z
+(<?tKDGtBs_g~IdT(o&HcO<>H6q{QDs_EdC7>i*Zb}fo%63F5s)icKFYt{;7po9TnFPq@Q)FtQ(!h{H
+{$*rU)rT?Z1uRSUxdpzn8ePG7r01DjQ^_%#J+MC|`*75)?Y~FM{hsCUH3(a5i0p0@9!x)t3B^V+BG>T
+9h{vk8kJ3w1Be0BFRx#HehCXq+Spz^?zj|VZT-b-nk3aq>_vGVMy%4M=R^$jL@<p#HQnF3h(ktMXSvs
+#bC3~k(HMPq=Z2;YyR>gmijxp?s<X{gUDSyKI3GC+ua9rQ?-isi%QXF`YlsO2+R>XiWogP^0PMYG4>V
+l6pw6QOy+!np9?4vAg#(wp?KI>W3>o96f)xD3|zk<@CsqHazL_>zP;<GIOSYiq*W!(%1@+gx|f}7U{r
+1D0EPULLo_t?`o3Pqj97&+8CFWM}Z#REz($>r1b1IjO6d9$Fx!zNuh4TQyiu`LXV6px;^e;ksK#uB3K
+&Nc<fL1~qw+4ga4AvDK!<i~;~-<Bj_6eI~sz^mzPJo<*!G<JNOB=5;e02~LV1U}lzmHVicyb~c~0nyz
+;_AhOvJ+KrbK{41&O+?hSwhC+DySEEO9rPk&6f4sfz4B<3CppRyLJH9Z&6J}}nh~Rvk)AQTny{*5VPd
+GBrFZG7=3O9$h6@x!WQP~%b0CXNCn%Vft*iIBQo59=K$ch}6;#Yn1RB(^5RyR@`pTF$N{Mh@+9Ohyz1
+}#{EaH@?IzdN>l3+ZupRoYBbet*Ek_uy%{xa-<>|4OF&*q41m)U46<E>DJA3B(QtQqO7+F?bKDl^-Xc
+>el#X-Dg16w<k^j<BRvKEqIkLie<fYT=QgW}bnsk~ToZh~qM6M~m?s%A*r`0sw`z%OUzDqTht}gT4wK
+E39#;6J9osh(7(AdjONf9ne&y5rB0SlKuF=fex5xGm0w-c{*Aq2V9L|rv|a7cOgih>4_#jZLM^TP`v?
+T<b^H2!sy+z9KAZn36nCD{tz91hbHM8$w}%Zo5r~^3ysdf4VB8v>$-vc2;Eherg!ShB|OwIc2uG*wsl
+k27QXW!7oBZ8%g7<2@K_iQOS0(4z#P!YVt`w@6qnFSBNGnvioBLn&m=j2$PpH~NAfYW<&SW6m9{te=j
+qi|FEB3h#TQ>;A^GdeKkzU34}Sal8})6q_*(ko4ukvp^Doe2mHnU;I|I73j=6Zz@u&Xvg=P?n16AvEh
+YO%u41MraG^g^<FkM@q$Hl6sSaX{RjpA|g>c!VP;Bj0ggCi1D1Q!S$Et7*H-cFL(gne9OcW}ju2T)w*
+MTb$mL$Rvt<-$jT3|6_rfB+2QA-t7`h{6Dp4Mo5$>k>`zM2G2V>g(OE&h5!W2ASb0pWf1lCwR^8TURc
+O36P<yIMGO5(K-~bG#F)TQ|nBA8TG(?+Ma4j-|M(OHsJRe@kMEmx2*KK7$+W?pea9Jis|f|uSA9EM=~
+xknlwnv>ud(72RMShrFlCqiiq3r)Angg8UpuqEZZ^CO<c0+W%Ztz!|~k~`5Yk^KkC^r%|56N&TIi8So
+$!s1`+=yEBw4<^aieC{TA$hB*VKfxy2QV8YmZZ>I9FMIS2{pYc)oO2&=Fq&QG=%fbdEuI8pm!24$9;l
+J;qY#~V?zJ#IEt!gh=n)(x&-T+_WmSt@yAuVCiIadA|AL^>mi(m|Myj>g;(iK(*EEN1_V|G@+W)2_)8
+5e^LKm<$8E^W%{zrb)&$n$M9)QtLp_D|bn$6G9U@xjvdCG*KrLFX(Vbz~W)@Uf_^(Ai-mrWSMI~?ybJ
+jWWXsYh*echhcT-|P{>KfIFyl(%u^|gAxh=y4YRWh*}4WFA`rKaw0ccaqshFiQA)}hJ92V_8yNqHc-R
+dDVRD^u7*OenmyZ)IEse&4G)Sv%w=E)6$ww?ef#oZhZ<7)yFp-`qi6mmt+H1+ts<(X{8rwH!x7kbEjG
+{P2>nYG!-oYPiK5~svy|yGJ!#n0AfZN-FC(z8(_Q~$^P3zokj(!$HW&$PLtJEDvpXM<5n2r_Nb<TACZ
+lh#lrYKei&@NQ`RZIZmos7p+AHt=FC6g7UH?yQd>ItbH%D2m?loSe(&p~txq0u_48{VVX;TZJRj8*A^
+5`7dpsmG|#076d9nE1-_>Gu8lC{$4GC7H_iWa#kMF$SuwGjnBJM6v^_#FIU3=zGXi4$1VAkWYp*Jo3>
+x3KBS9R;q_Z5lX6uC`wIr_0y#0EG82b9BC2s-gWa?OZ%+zJ*<yY-_-Zy5H~zZd{#wEYAMXtYn4T+pt^
+Z=EwWNn82}rNlP^c1LmNI!xs<G;-5{?;f;^=j{9ZwiRy>qW)oOS}3j@qNw^Daf5}g)?YUt3lXXzNhv?
+Ai>>(^AxQAnGEO6Kcv2+C7mp5$k-QIC2|k*$iFbyLu3?jt=L_c%b{<R|P0kF=p3L-%%`zyp4C3<4>n)
+-8{9MzdVObX-MqsutqKGP|8qt#gdkx1;DBPcA_|aUgte@-hS_+m-x^gEw!*mIp^pSOeU~O;)yp+4!mA
+9C@?pF?!2=(If)Qb{d|Y(u0$mFVs}9MMqi>mC<kl0*H)$a3ZpP?W~qjkMQ4cR{u+;@Yjo&x`XbF@@}$
+=3Y>J2QU#O0dgy_2NXkVQ%qq`Zlrl77NLwyf_*nYrmGVvIvPY3gFFZp&`%%j>N*aif0kR+>0KN;m8Q?
+#?PiexJ9;G-_rO~wXJpA2Q0OfwNJ1pY$oA>7@Y*;^g`;z@QZg7{?!WLzSwU)TW_F-?VOSClhL{u0%$T
+f7M(jC&UyeL!c)?)SBWU4a*!lsO{Ggjsm)%FfukYwaeG|p+6Ip7S&r49*919Xx_7SDl0g5A=kN)-Bc6
+fmWkq*wYPIZa}DjEe<&Ev3zBkLvg!vs3eQdg@JP@6@Q+7Sp0ZBpC(%Lu`#nAVOujO`0=M>C(Qo(!z2(
+M}4m^at2w-RF|aHJh*4Nah%Gt9(4#%epGmhl_&sxFE(~+hO4X0=v-2MlX??{{g*{0!p*jIBmP4W(a!Q
+r(;iZ|K5wjXdh#yEcf|LR7>ZbK1xBR}t<84YY^uTZn6$~XKIRapI;b*7l`Tev<FIF?FgDCda#k4?Qas
+SIyY`{cxeDfH!J`74EF4ECDhs#LmaPseFm<jv?O@9)yt(H+(PkEXp1#U5?g;t{HFjP%Ivn3iH6t})L)
+K0W9^Xx1GGL3K#8mvD7rItA+&)MRO(naGZ)>4;EPd6mNS4mg5{{_n3{?iA8!pF0;fuG#3Y@S4#o9QPb
+!Nk(M4$9JjHXwXSelw>+`Lir0M)GMC9<NE-)p^{V=5B(x9&~Qb(kz_&fvD`SmngU&I?f+DUQGVr*ERQ
+$svRjBb+LQH4T~AK*A<j=P5BYH87Yt{18b?=-`&b5at|dGn10Yx}RUWy;USaXVc3y=_J`GY)&RuwMi7
+ok5Hb41U+zQ8h?D?lKy+<%TvB#8_-7vV_GWV0GQAw{7-$KS<IHPp|x(ODn-8#^Ws$_xlo6QbIqYkd#w
+a`im3tiTJ?uweT|JM7>!dtO^Gc;lnGtPN|=gPhsSJ|&&f=|mPCE5+-gH(tAsk8%hTfFb|O&rgp$?<RU
+8xJK-+g{V|=GK+iYF8Z85`G(bc-!j+D+|U=^)&tePa}xZ&!ImvrnVzj8HoWGwe^I<Dj0MuU?hkl3Wn2
+$^Au<4cj`Qyg@f<a0!*Oe%mL(V!bGs#H2-Yj7Ds5EaXxgm5zQHkcW%!#xQB%RdqFXsjZ2DZ_#W#LHG?
+2+QZ>{Yfw5yO%+WYd_t8ulmRxMX~U3foYi~Dx-Pp<S{nAK7(A$4yRH@la#9mxTi0%*D0oszFt{`=^Fb
+CRdF(Hmae=s4W+Zs5w3gMVOp5Vodh(8EV_+E8N1%_Y#_hmt*qC~+cPR!A!A9;7Q9HyG&(~by*f#Hnj+
+EcDv6KdJi&#fPdoq_C!YtN@un|*d$R-999sEL|5n@&R-%VaV}h+%?!<9_4G%*$px>SDg(-acXOXy@yL
+PQM!i~j1+F_h-Z(s*lXdC&Cpg-5p#C7#43PwU>bjq#Rvj3N4_88EYviM*HQl#q*Y^}u%x%e8CM)@b2l
+-A7KZ%8wyKhbKM(uzna#t;()THZmF`=D+>rCQe&aeti^E3~c9R+3w`u%(qfF0it*p}Na&Y3D?{4D2wQ
+s-rxMBnu;*(54{wO$`e}wG)aIH__$^MX7U|?z;lrPUsqyV^l{0XV)7FkQz;rmz4txBR<q7XrUmXdmgC
+M_Y}@TyQs3?x$2a&6_5$0t~*W>so<%wbD8ivIB;+f&bf+cJXBQ2vf?@7d@fLp97|hIDem&BbIvvjJMt
+o5nCzK+t&8JP>z>B3qtE`fW=Z0)?MUyh_H{DWI`&w-1!)wq7x7h26Jk16QRF=-x)bpIRi`Q4U)9TukQ
+7ilPz;CpCB4mDrbAvP1t%?~?;$Xv52ag>&BNiovCT(jKC`H7C;^&%^N4@x2wytz2(^nrUILsC`ZfadZ
+NU5<<zm`yvbVj&=V%VW%7{9TJPm0^-MxMDe6G#r<_vyO&o|l`*WpGdn1lD^${$_mS+10S`zjC;N9Ex*
+*%N>}fHGax+HS);NX$<_KH8R}o3l;3wgWpG54kvCs>j#+BNyU;HN;+#A7u?utId+daarLafa%HfzLlh
+MgR*udYp<dW%Flsij<Q>#X<uj!4JKSJB27{^)w)8TQ0cuzOU`$^!U53jJkpK^GE8PI=W&wxYP<_x7nQ
+A4t)+wS;B@zUa=Im}%31nTCDYGRA3{mS0w1R8bk70IqPK>pGz%HSZ|*6J<&^5ru0}98)2G>tkm^tRt>
+gBSK5f`7id|-Nl@5O5&)sf6%!pFB!x^)0a6gdu8ge#3C>=Wz3>+rUd{qCWX|WyFJX?RLjz%XY^nYm89
+a6;UfM1aXYtY@-vPl>=Ly00x45l)6M^DHo(;**@<rqHjEWNrqmf5|9oOb)HY%t2G*D0CqUvlNKf9?kZ
+Ok6rU7j00F<k3YfTC{4AQL7)2J~Qn1mPSJC2ar1<K{oMl9@*T9lH!mQMy4AaKkU4J$QCRU3mJ@AMt$Q
+IIMN<BW-}mWp7EDD6Q%SQO8$77eC9`xQz9w9k$-gx`RVS*j%N}JQL>54<1bDE-tN~Y^<Awdzy9YR_D=
+U0ovYMkNqA(36hb_2Cd%66*ku3AILAtDVc%y{5&ueSQ#h0w0klmqvojGMQ2#e!#6F|z6H{UboDSFfwC
+9_wYC6Z4b~KsJ#fXX+^t6*EQ0Ypd7^;p?jI#q{Zf#UvbiBB+-OTJu>~@`+y4t2a>iw;p#$4%n9`19%%
+FF`!gJk4&BZD+h)hrbYp*X3(r*+dlgD_(bLGuBzEAb2l*Gp2(tV>R)NZ&0d(bFI+j#5QoTQttMCEs-2
+`sCR&&{$yDbhq-PZmyq!j4T?orawbD7=u~LLrzpUQPJ51MEyX#d7o9#P$aMTJ|RQ9qlHW~t-CMN`cOe
+b1WsN2`N*Iltp@Q~)K~>s7fZ`+hwyttg0}K|$xVO8E_lk+Zka|195r!u_4Yr$JI0$CU^q%jqQed)7e(
+_KQpN$AOuEsLgPuVl#9(;96sA@LzJVvKu;7HuD@ag$71YZ1)*FHpi8&&RBP`r7TiQ6O#>t(x7+RIeuk
+v1V{=Uk}+xLjHeGJ0n?Yr|XhO{<%_k$lxY%T9chcW4k3ib$P(q;R?nYf2Bv^t^saufIS6k0(hfn~<(k
+72v&r7ifY=P-FKbPy|AerIBddCPGizNh%F>9WsX_l?b=VY-+#F*>?q6g3oKMURLX(s3smTTv}YWx)#0
+GAI+DovEpv5)?00CqA10*|*NB?4Kw|NPlAmh%US5G|dj2BZPf)qPSm2spKeX++2=(46JgonTtgXn%61
+M`0|TqXTe9AUpTMSuP}7dXJz7~DAuTPQ-0UmaC+%mZ8ShHUDOYiI{YOdCkc!`$h?=d^PDoT?{{|{0*a
+p_#nXt#vv<XzsJkKe8{KP4gEg<OtD;p2WUV%MNCW*Hx>>T3=>YfZ36*uT8Ir%GMQnJsw4ba`cZI0r29
+b*v6_O^r*~4Dm^>h=-R~g7S9nND6+jHJB$?b_GMIAjo=Y5ODX!oKoOyw8b3TX0JHj+^y1n*>58_s`e2
+f2&%a``28yz$96jDjz@4DE}2LDt7{m_1lbG}BmM#Z(=Whc%Anvd4EzHWy>kJ$_p<i1X!}ee#j@&YjXG
+v`?M9>oV}vh&bfM<7=ZPd5Mkl?^w`8q$r^?vXWw9x(MM^!;ggNMliZ>S8UpdLsHx7Aep<<6J5WD$+s_
+me02`Oa<wSc<(jQZc=Oj{t&UTWljL`KQKHxpfWd;$8~BIgbSL^R(e!u}$}nfBbwMW0mv7?c9|X-#K!|
+mXre|ilbTS7Z>p!Fs*N>qnYx(aU^A}xe_(;%w9o;dR`1DVxmWu2_9!E#HYyEi*IR{1L91~f0d`;v>F1
+{TE|633_J_AkmEb-puy<=rZ(#-YP_oz=~JQS7G<28B{dDZ%O&u#y@Rt7##Lm{6~#!+0)L1iGwZcODv7
+aOI8Sz7RpnE8lC@edj?|Gz${|MPrM<NnXV@w_IAW11X~H(8ud`eE7u2Jx_haWaMfJ<1PNHl@s!gFd79
+4CyEP`;K}<^*I009?!f_6&)3smft#5%IE?+0;L?aW4c;=9YZimN4~%2Zb4q!DJ*{}7idwThf5?nj{*W
+k7S)xkOkbI7U^KZA4?4A$J0RoD8}&EAW4<o$xCH!3<qtmXveSNV)_Q)-QjF+BcKE_${MT$_O^>hbKE@
+h+))qoAQ6-5reHbF+L|=Z0CzEGS_~u5we1{Ai^6HbB=<Yf3r<3ia2U1SilPr%;CkJS}Ru7XIxC1J*&s
+4{9zv$#?C;311B*c)7t{)wDrJi1XsS!aZME5AmE%_Fxrrun$cft~MpndQlMdN|4NFPJZXvdO!z*Z<%-
+6*hjI5iwH=2U2OB(XEBzwbPEtepl1-3fmkht`L^Mvk@)eIIw&Yq9y>GqAnRdX2k`!T`A&9;sjCt#WmR
+n;)jFq#o<kkg~f2G}kqG)uOj0^_=rpz(7jZNn?_{(R4THiUWGay_Af|Hr0oI=Qd<LR&r!9!;}|)MYh2
+G&b>bgi*Ij>wLHVq?>-UR-odgTVOuB9;mNZemti>sHxs%2>=M%uKu-YTC)z5AYwmeYZ`IMXGRRfs!G3
+R<;h+}-0sH@}T>~P@=s%=mKqc;p5l`7c;56>ja8S$Jy>+q3_{^1x`fj}MntXi|zg6*_av*eX(@U<gik
+2FjOzV<gQtD<?fnA~C^G&hl$QYA^Ek{i_U;LcUvFH@H9pFl&Mz8WUvy<QoYa@7r>@KIX%x9uRxm_lmW
+Ah{wpzkuranVY*DQ$jGYNJ?OlO9T7lq1E}75xODbeUpswTiwDdQ4Gk8{qhuJXm>Cj3&uh+16v?FLZmg
+ST2isL09WEIYP?IvEJtdyx6+EySjS7y~K<cW(1g>XxqA>^yRl`&nvh$S62;!z>Hr|`tsoe=72t{cxbc
+<c(8NCRvn-izRc+I#=MdS4KB(v3R}7?!C8~5t&;`KITgXFmRD{A>bBn0UtP(HI7E|zPxeQL3}{*yq66
+o=jB4ED^aIZ%aP}>1*kitT(j#3(`xaxdygK~Mu`bPe5jUJn%S=Y(vq?;6JCdOJEpq?6{mPZN&SWt7kz
+M3S`S8$EwS^c?nr;<6(H^e&7VqV2znCDiqWll>Jl}&H8c2NzVuUMQ+2a{P@}=GJ53aHopnRzCp*;h!g
+3f&4`jPYSFiy6T)kKoy<6Pvu>+R@)rp0}?J@OJ8X?RD}%26<R4<9pQNgc^H{?XIK9ohb)bc7If0_+5e
+sLLZbP3Yc)_3%M!Olp2D`RFiNfo@cwSrq4%`C87I+UYjn86Jr>nBLkpK0GK_-;g%9p2KsjgzAxf#SoI
+CB-;pQ7~5>HFNPIqRA<FJvML<uz({u0b2-X?A0~35Zm2WP8<I4l2yt(deA;T<kjF<qdZXOjjs|qAT48
+|>6tmgT*$Gy%W;Sw&LZ=vXP(m5n`4ofx3{No#WQAvf1|*O-&{=u<xl15#n}WQZ_(o~af@$lp5mj$GOh
+yCzkv-u|f2pT#{Fp72=_L^1=H5D36y_FFs{t13osy_;G4P?A%_TfUnz}M9lSrU7?o{9bYWEU+l_p1wu
+H^TW!hlBV)&f*%%*bV|kgkv*0el`*q#K1*eHEGdQ<X;hB_2AuWEHAietz;HypTXgl5qu#4!U}}s1;qo
+|4@xY%!0aE(OJqVYSk`5$OKw>^5V^VHTRl9kOm-S3@uxc@#Sb~vkJ721dWlQq~)v4jlG19dC-#PNM#x
+l$#gl3bXv&l5|ZF!@*K;W(Dd59VTPE)@&rxX*uHd*f_`A{uda^E;#Qy;zRBJ8xneLD<Sl3&>0vH*QA@
+JGM+E>rSrm*;M{@q#Pt_AQ8i2_bmQdTXo+bco6-!uxIgYX32=&*`NaInq1a~a4<)go<bwNuYu%7tq9o
+vhZSYgY0;l>xOjh*xf9@!rMF`<7iS@OcaKm3FtI)j^t%^=M!ZsGQ>o_^~?v^PIhJ{LBPjGP0(D7I-Ha
+BA$AqvVf%w3cpL@nn~%{uZzcU4hYjnml=7Rd~{0es(>t%bH^^7gQ{mX5yzsl?;=OgDymp#!<w_$BKjP
+>DPZcpS;%=k6T0UuZub6cY!56bbk0&G-zqXlR#4|!CLB&TPjYff|ZMy%nM_AIFA`11YaeEHH{K$1I9a
+q$N@8?E$vHa^SrS(O0)v7yok53Coq-kqvBXN*%SC{o^RNO%y++q;O0fcmw34<J>BY<Nc7FO^Kw_>YFq
+D0UgfvwW6<7^_?*n^^;V%=b2B$@a~|r?6res%?jFSm3C`s$C04k!;uQg-ux;Tk<OflqUGJVv35Y-#(W
+<@^IyFeMaicOijKroAfeWVHVeCeh+x;9tcY1l{U7RE7#uQt=jhk~9zyZ|A{e6LM%~Wt=et&=A8<k73q
+(#p&krib0=`fmM#I){FfqFt8ZoKH3Fr<^6xRZ{gKuomlMPJ@O!}wUUe3{{8?jz<~W4!01LJ+V%lwPI>
+@A>3A8o?`&ZuoeKDVh%+`!}9T$1i!I1axyLCiTZ{K6->F<vm@h=g4ksQxv8i%0{EzE?KZr-$m}|m5eV
+g_PY}PN6I6vknIV~o_O6a*dFj@>9=A^{iTYnal?};SZWN~uPA|0#Eh*ek(J(<HQPG*qjqe^k5E`O+A$
+RLGl@UKP!dg8`B<gcWGm;s$v|`~tld2b@UHMvbP&A~-MR#wQF$61D~w#@D>8Airl2vGl5Eh8?uaUKqS
+4NML<iE4uTEK{W7up{lnYA<q4V|v{sXk3uYk9s89lMZj&I;iJ}|kmgzFIdTGj5kJDeA}38~xf?r@jUk
+R`X)oku=1mj6%3ZVM7a4lq^EHbHPD%k<!(SRY|kuovTU(nlffF5FqZw=4p~cBuVtlmExaBHwrOGQLA$
+L7LT|`a;}IL-$k8HvNko72{4u)K9AS#`)m(`o~_P&e7HsXxSa}MRrRia8dZGe`*vV0qy59rz~c=s-mc
+;O>!*LR`XS+Rk__AVigPx9~n^!-=x7Inu4$wT`a{1#GjZu>oGPJvEs*~x|NilYwR?<;FN5Xz(=iyP;m
+e<(9|r8HFlLtgb@*}@ac8b*Ily)ME_<%u&Dh!mS+5>7zfc}cu^mg>IY)j7@bt`#;h<hj#Git0UaCNTP
+zgir2HuBhn+K>Qub=@m+p<++^L-0&?z!PnRbXLw`ym%jG-jD%r}7EI{~WIwW!F|sBcr|9iEKY?U6PkN
+dd_+BT<PXXUpGfyEi3Iyt8Et+g;5B`NpXB`SwfH;qHaZ5y{s_$lXfqxH5x^l2m=>+Y>_&$}q?|RA`Rs
+&dwNuRLX)l>WKQTc6(p`(qbq2qOf(B5}9i(huG`tMU7$w5le7-N7WVTQ9z`i{q}}<Bybk=1Lrnbi9={
+stUUn6H<NX3JM2Jrv?<HoRNBp);t*rK`4CvusI;0M5%J2Qww0U<g$;VBN1u&wbW&rax#lB7u^y8XLC0
+5xpg;gU;pCBc9F6{MwRDWoH(ji)6(l#s#ou88siyE){eRcbCt5!!+n~0^<RKBfW9h_BRzBh4u_Xi)9O
+CO|E&V#;T?$tam6sC)P={9#7S;$-L7}!HNC!oplzen_3zF4!>p3=^p`RVhbV0!<m}9bDu#ZlD%U9SF?
+Jza`j3HS$3nZv)NZ=C(V!K(b&{yR&;RXq9seDV3qj-_uO<k(=a_SRfaJ8`G%5<;gH=P6o{!y>2>>ZY5
+2_H;P)L$5w1I@=;x6x_qjoAut{r>?_O9KQH000080P~d=N8$#~TnQBb0Ps-&02}}S0B~t=FJE?LZe(w
+AFJx(RbZlv2FKKRMWq2-ddF?#?bK5qSzx%JiC6^)9%1oTzcJ{RAo5{NF)-!49vEAL<$z~*qge24y$pb
+*k+ME5~_q_*z1ixf8$=q~1<z!-t1RfsV_j>^NoSlAg%90`#`Fh4Gy*m9pJ~?@Q@|;~1<z9;QMzio@!d
+|~R`-=Tp<nf-}#y^QNf);N@!gIw_rVFMwoL%qrrpVcCvC_L(a`tUe<!P)1JcAo9SqwwvT8d?*3;4B`o
+Nsxq5!Nl|?CsUX<-6O<NIz;8=PA4R;oaTM)gOPn`{CvmA40nmu`LU!*(O$-Of22ctXQvM3hrm2+@0D~
+T4eRFYFWx6;i`VG`L<k%jGt(^pPewcR<hW_Y$T77D9pCIqX#GaG2x|VSM=~w%0hab(rUZi&&Rr+P@Bj
+-{YEGa6SyRRKq31&DYj*-U8C^3ySwW@#!4h_D!us*Xp(U`WjFkPE3WjE;p^*Isa+w{@7Holxb&_{W@R
+iDpR#2ovUFaQJV!7JIFYaCdS7xivEw7P+~`T1Y<M~cij`dI{gnM(l=D<T&s#1ZxV)Cd<No9XX%D<TcT
+9-ZT)(Af;e4LQTRxvpPENkL{PxX{Z|~+;@4mVGA7~tGv@X@`<;!wkil`*Xi3+)<-$C8{=JL(k@3Fage
+3E1^U3MKO_wkxvflxh$#quYf=!s#(vuDrf@7+cyW*SRgO0Gc66v$s`dNP4snW45=W}NMW-hcpKUrjwx
+CQx=TMx#abD0H(V3731oa2cG|*WKny&EcQ^jswAn(Q_Jc4jta5@y%c7{~j0!DPOTUycBvq4;9Z=Q{>n
+AyCUayO8&I)x`MVM&V=HiBrp-B<2>QfN!?%u+>W;hW(IWN?8lq8tXR>s2#JVYtyo@wI+Q#SE5TDUXKc
+3uy+kT7p$3Ji0(FlD(L&rK7!yc={{x&B%iHZxBj#WWR$^UAvJl~<X#rxl1!>K>&WdCTJkuNeA10-y3`
+AdoEUsj{HbaqYhi>==j9A$|hzs??8|nvt%^*Yuk+YD*F_>Dh3_Q}~C1a#^S7pOM<PX4z6h*910&LoT#
+$Kp^y<mNG=t*gnb-ppW3^PDZw;L&y%|X48Gs6wm<$CP-d{e-zhF;1tK~;X-ci)oaqfH146{Y+kud)mX
+u?r?FR!oWfzD*1i5oL%51HMM0!QU>)@TzTU_mT-9(HP?hc#+)ClQ_%5_rPM%B$&Sf5(gj9Or=Go&k>O
+k12=v_UoIu}oecH`Awgu$*{fkp2}ncpVWq%p2_k*pdu#p054WDL=w2ujo-F|DIY(nB6Ht)t7UYCvd&C
+(0iEGkbv@&SB6|<d)O(NG^#)(F6R2w+f(mT~?^GtGG$$UltEtp|lH}1|22~G=~z311+!3%3X2iqe8t_
+R|Db8%tDtGlE?Z6B6r9}Jr9eqlOkXgFY94v_E$T3eeB#EBD>$Tp2g52_Neg<xm7Z>BP3z%C?j4UK?6J
+d~P{C{8LJXAE@yJ~B?JEXpd2HCJr00Bb7ILoOCm_~ncP{Hnn%7BHD?yI4>_1U+Pi_u9|}fY@+_w=o`O
++jGR$6O{%{#IJ5f%84>RB#R9jlBh9zt_PskykDWlB+;DMVZekPd*}#&4ObL7gn@e&jMz@Ut$WsOOGNV
+<(gQSA6i0NiAg|@Mb<=_ljUlhaOT`cTc=5W0)`!I_Vt*|vwyhL~wa{1rlYH4=zFzoZ;>*F3uTTrohN8
+A>6tyjfASU%9gE#9~<Wo-qpiyN5_*!6f<pg%z&s&2nc?xkA1S860?B|xUxdK*jCqON+nYJav<53^M11
+!40#(-hmr+b^;#GWPRIr3;q?@eeBH@1$u@Bn=agcrk9gm-NP08XG+rqHA%?e@iS0Z9!GQL90aXe~*wJ
+kc*vK{^?PNBDUYfX(ZFha;|X8^tN8x`h`us@T9f%)ge94FpR=`A}GHTeg1<RDq`^mIf7wAq9;`ZNF&^
+nL+9~8&>2t7pRocCe9#Mc@Qb+iUZk?xZRr>d&6dwe9RW#+h>b9Qb#l)-UqNCZyAq)H!(wMj0ZQ!O&bx
+hyxJ}SVRY}#@z-dBsx1$IakTM6_}b}Xrhw#yR;q-!fWu9~#RJTkd<>bd3f^GgwiSwyF=<6_L}?P#k2D
+}Pfho8KSLwY&>tDauMJf{QzkMIfFx<hw5tBxWqW6IpIS^jX^6)$<Qa-m&!!#0_Zvm4jd;h@?H!MADFG
+wZRx{v)kjsg)&e=-Ub@f;Ywi67D1*5YrKOv6LaK?tLl)tC`9WeO&%Jh$TI>KNcqz`+2kNe*H!aK`|%f
+F}TT#bsY1aCjqGS+N%2o9Ky4Mh#jlBoBlG+5!S-U$&n_U0|lU0cX3y;Dj_HX2YwU$TFtESD+`{W5zGa
+QI!BUobyCC6)d1oS!o9z@bqWf!^qInP7pK;p~NL*QYM}x)C}=-F{}%_jmS8=lU2vQE0hmHf>LD!61Ec
+<VN>{iPSMXq2pXhzC~Dv%u*w%qB8i5h?0_{>ip(rR)qZIzJpgD~t;67ja>;IJ#cbjkaD#l36$&x0#S?
+wK66?H`+dwuCWUxTsB^9Tzk@Elt^1ge%r}*Gd#pK4Q5Assz7^^*gl7u6XH6|?jLhNuee8yq~39*$Jd~
+58kK|SruEzQXE7G&-yv3E*9Z{070!2p1o6b=ie%8iV{Dmh1B00rY2s<}!j9>Am`-B_)7mIS?#9dVnfl
+Zb=U0pCDzKL~KLp{g!KmWmL;PUA?>m?CGUz>(Mhc(5Qf4#c_A0Fx+J_3-22I<Uo-pg<%3&avK!LyRRt
+(&SoFMqDZ~z?co^=si1ejn$OJu*N4_bYbsZO@WFXFd1E`(^ZNI3KdjNPxInb>wWOSj~E?0hGW9|?RmQ
+zGz6jD6L5<7qehK?4?|-o;4t9fnty0j)mrk1xFGBL_;jEyW1Vcw+#2b%bAA<gBWNU$tIJ2*`xNi}#Bl
+}h3l*U_Mn4rI4>ib(d$0tksGp%z+X?%U1yQULFZAu8mO?<0Y))}r2EkZv3u$g4XpOT|3|NC<E949ov^
+CxXlSa>^c{6L<Pmw<q_q6T;RuuXJmNXkcZqjr8@DRazac<SMq0~g=fWaK2qk{&q%4sdeNWGvR;E7V^{
+Q8sxoE9-~DQ0irGKAYGY2Q==3P!OUXmrq3-m@zWmNQiSmfB<9YV#EcEcTk)bqZ%>{9?<%MJQT(47S#p
+>DKfHtic<mtCAc;u0a1O3b6*d*6RRrJ0J&o(W$c&#j1~tart#%(u1;Cn35(|eaPy3A}B89V#M0q?_#+
+|_y2z_*nvSHnEBDo_0{#|wAT!PA8Pgw-!L?69k&VwqxT3}m=L)G+Y2EOM>Lin{AYkr`&rq6L8WX`hA+
+M_qxrRozd*_avAIU~Kw?_u=!GCiVH?PdKyH#4U+p;C#(PLx$;x+%8%f5&f0>eMug2<CPaB(d21uF+JJ
+f&@*XO48VWM@A13>dUbbJBzi_EPne|r`|pTWCW`gxCjeKdIFrv(qUQ%g}F{Ocb0BUwv8MIP-WBr##rV
+=~yP6&jisyI8blb!H4=3%AY_YaYwU+!)>TNC$KC3U}zZ&@LYtbsiO5-a?n(V#oj2*wGC+vX0qAhvT3=
+DQt|Fs>rZ;%}|d8FfFtrj;s*bQNY$mg{U*Kz+&9JSd+BY0bMtD`uMDA@NyjUv@7W<lf%UiKM>iUIaCW
+zTLh^y)0W|p3H!I+>fBiQ6TvpQT@2(&jZNmb{KkS~unZ6vo7l>vUxTqsX{-vw4Mm*#cEuH_`WA)^Nrz
+45*&u4z9k*wk2MX(aXt?*b4hWBmN`jCdXQh55BtiWo$C6`XdzxJVzld_CXA%uv2aMu~QT2832?+nfX4
+Q#=85HkY6JK)VL_Kj+6BFuSI^AD8HkKJ8s<cWl{=t<I1V3dw@=)qt5RD=%u&6MB#bSv+=3owa=EtXYB
+DEx&nK*fX$=GdV1_@J0GcpT*apCj;Idu`SZ((rK;qLAp%wr5}n`i8bDoYPQ0}+BLk(yzwUH~p~hn5Gj
+0P2e^kRs6RtLXK>B!9(D@RzYxV9TH``-cQgY1QQZ$GHuJu)>{OuAo`>ue*QznuO=YTib4IA%%B#g}Az
+|t>E;_yS9h`i4C$Jk*~{Gps;I{y7&QHA62hT4KNBWgLWLwGsXMRyD>)-GJnQkqaSEAdytx80=YC>kXD
+`jN6^A#BInHPB=gsf(%;Qfu{J9#c9UoX55v0GB--#t)5Pb}CVu_9ulrri^QU^JQB{&qbkc0M5;S80K`
+?f?Q%DMcZ8|Av&zG{T&$?}#*@u_zwhLw7^snzbZ5rx8qvl69>-TAa$-n>GOZ}VqBT86hcpnC6$v{nmL
+lwDc!^6pkjzW;IwHe+BhkHLR9K=g#H6|Z5wr+`oHnGj?NPP%zH|peojJq)>?V-%}<4A6`<UDkc!TPrW
+`%&hR%Y!tDk6IzL@oHKm6>h$6Hn8}CE4;<6zK5g97KmS9FxkWJ(Bk-LuxLp!9Bm$Q<l_OCkC%<n)W!u
+AZLk=7jRI<g4U2^#{YGzbkJum){*`>WfM8OFeL<vw4tR1ex&nVL(VsgYjls&hYY4UpSeAy0!IN=D{t*
+!dVFW#oiY<&Qbn@-R8RkXP)wHPl(N=w91MRN;nvz&YfRQSH7>FCQ)LL-ow15=Y#rmv4;|CMNje2IE0T
+`t)a6YKU*)#BiUc$`7q3g9iYzYm*pJE2Fnqu~cbk<R7UodZC5yY+ec_`HR^z~?i)S(sWK86SCi_Nmh;
+WgFfs=@b7p-+a-HKvJ9nb}g+8SvM>0NwEVw<YMKm!Las?Iy1y6!)_&#Wq&NT9A6^&tOB2u^@k@VbOxr
+C1x}ozZr~QwZV`TM-6H(E$qNz*waFQ1ZMT@TZpUpu<jo$FN{Sm{>)jGH{}JlSc->Zjw`Nvz1#aS{6&t
+gTmB@6>|LL*z|mckaOcKeP1)IzF`${%$1hJju`pqeF*T+sEp~Z#WulT9&<+A2lSP}9-Y!buu?ML)MR9
++~^0<OB9p!!Cx!{<#nUe-&gWRx90Au%!=6Jxck;-?4+~O8ZSBu4co3!Vn=~AbY*ruq~pthBi5I1J~CO
+|ICUVp@C3;CPPd*D|nA|5$-2@JpFHF7q&siDOAo|f$<p6x^fV&?_We!@K|nYcIk*=$wi$qY~90QIHd4
+;&IMcx3kd=Pg@+il4?3w}LKZjAs|^sW2+5<Nlf~D|X5<gR0imV9J`}`GB;d%`k2vl>QO{jO~o=aj<5d
+s3}dVft#>+N~LiASem`Blnfkqe$-oB@rDQ?`hXlWJ&<BIHse9WC<~Nw{^|10#rggHzD`U#ZlhUV^_hu
+y3H4Ntk0*Bq5_}^36FZSYtO#P&lc+U(m=G@7MKrZYnl5(v1=zeUBxw8A*!l3#Qp#e1wYp%jnAk%oG%E
+ND3#-e!&WsvWw)-6ENQffc7!uu%#12ueBgCWL#Y*vDcxc}B54Gae4#?gW4p7Yz&t3FO*SJGPIUgI^N+
+k<1j-DExOa{)UI+oT|UvqOrncQ;roJy*}*d)Q1p??o;F?njcO*-|+_SC=W%-gL=7`XmcCgkj@K;vN&h
+?G<S)JcF;*u8Han<KC5U%1=ZglDsawl+B4GgTNXvNJZ*SR>vNkLQkHzEJ?_f-Y-spL<ibZ5&Rpvt`?K
+Rko@6oW&{lj8%b$@$42O^SKBJ^}%lCF@9tKY~t(avC|Py-f?p_3ug?@SIhm&mB`V(w9B!pa_|&~{h&g
+Kr<vNk89c(gp<Pw9tA15Rh;-OD2+OQU?v?mECPTkJ`_I<{WomkNdNxo-i!mO0H4HPawu3K02cFrw$|<
+matvAn5u`RO0XOAroBG;j9_x{<1h4I$F_|TskhCq#l83f$C4;|9eh9pxOxDR;jI7Dmymh4p6uOW_}?n
+XJHR^#WphmUTXSpXxxJZC^oGt}@fAk22t+kr;4ZS1c2wIJCRZawJ$yjm6jzrG0B5uaj{ndHx9l~t{Ql
+@}mHZ(rYY8=yI}vMx-FvfC6Hx7!K;Rby~`S3<)1-yrJdpe6F+zv$sn@pccnz#76B>`RVzE#n%33q%JG
+9I+QEo_;~2_9CT?$EqKYzhEKaVTs}!V>1IXLNRj=2n;Jl5EOPr{@jK|!5+RnZ{%QTwXp>PcSo6SeRhm
+xA}{J-b1M|#Mv(y?=&1vXqtZDd>kB*u4@9(QfoiAfjQ+8Jx|buOncd_FpXt(ITgGu7v(AWGHs2&f@M~
+;eC#S91-N2_VwppLCK%Zldc9W1&E+%wSPjJgB{Qw)rHsP5obps?s@u)?bW^E=MKoZ;D*rt==Sj`@)Rq
+f5x?p$*B>}^}PT;KYf*ie^A52*iPlOq87%^P6m>qYp%9q(gC;TH}<FE2I?A__#1+l@GMmK5BcV1Hp@S
+TruFqgyngWS*8bP>IUIoF2fclg)AzeJyEdb1?Qd%(bwr1b9_oFh^#w!>$B9oqhDrDH3*WoQxka%u|&W
+TE8b7IWdEdeQ@GU8kgEFC7O>f0d=Of3Ws?I@(62KvDeZAfZyYpy?luTk8ok?fn%wf<A<_dN(hllu3nz
+~$JbxI`r6w`9!uSkYwtr%BwOxx-2i!aZ|O^4x3S#mT3ssqG^vJZ|8%Nh;Q~(P?!!r|39b83k&)i^`_q
+(M)w}Q8Xo|t<X~4d$r#j5LFmUl5_yzjT1#Q6t1h%JGD)<Af$ellXp`L*`A7=mkq`PHzc&ueGa%SqGx8
+--zW(42+duNF#h9_B<I8PAfX`=iLg8ZSzX`DV`3-`$plp3Ux(ep`1rt5ijr-irpXo}GMZp$JyA1C{p{
+9A5UH3*bfeAU!oTV-0{tC!x?9sF~Du*QYdc~EEHlcT8Jv6OUo7+ti|6x?D2SuzFLfUYMC&FLa*%x^qf
+9Z@k8drTcBEngqYWMhpwL{j+aF#L{PtP9z#Z?>xkwsGt0cx{=Z7C39O{5>-dT1TNepWGTOhx9(n<$YG
+qo1qq4jvv7KDta}%&|QOw?4&!>#of38c&T7=@=s7p0|XQR000O8^OY7ypI>kZFERiCtHb~R9{>OVaA|
+NaUv_0~WN&gWWNCABY-wUIY;R*>bZ>HVE^vA6eQTH7Hj?P~{uNC3cu6H{+U`7dMw52d<2W1NlZV%KW^
++%wqozbk&1s2LNXl(xod1671rPv1O6_Fs=ALtT61PMFg#u8h7Yc=w;Pk`OU|yzqv77|kW^wvwd~<Yib
+P_x%H@hldUN=GXWE7k|czAXS|9l$!Jui}7@G|*FzKNmOH~Bm(>MRYKGH9-|;Q6k(E{ou0xoB>aDhs|U
+w?&#XIlO}pSrsJEUeQ$fY}=IZZCPd6Ix8B4^)kzXZ=OAQ`rXT?ar3qbk|GVBeE;367tj9o!>jLKyu_D
+K?&v6AZ_26(mcQhiqeWG&gS-sn-QRXiRzLe*75PV97V7h=TrQ!b>T6l6Pn)vN-)@rTTD_>Uqo&$Tjsk
+e(RdZcs369fI_I93an&26|e_B;#Wfe@f>-BD`iw8+<)Ri*7tn%3rV;-w|U*>fKub&ch7%usFUakQ%RV
+dn4tACVvF%ICvCaLO-K5dKk^BH!eNY?O8eL92JW5TiX{JhP!Q1a((*<ke|tLMoEKGiTo#d6y0Hd#FmX
+4`y~PRmVJWK|EKuV1}-{vu2BDw{W7!^Ev{s_WoyNu4>bdjP!10GGNM6C@heaqz>7Z|KuV1_KBwnE^y|
+%!ra;yvEkI=g{?Wuug8WX$r5U{yJ+CgrVwxE8pd3sJYT3Jx}J>SvuWR(Cw<(;b_7TPv=#Z0=eeN3dRK
+}e_LhKdX?1I-fq>KRo+Y&`6}ZXS!NAF{0EH26dP7hE9g9o5iSHT94>`0rZ9-pH(6CvmtSTTe12Y)Z+B
+{p<Tuq1Y_W!(VCz2sx*L3(rGMvAG`dpp_BzXS_x~HejDx(MG6CkZRd#fQD+uQGTrH&-pnpT}qA6oH9U
+UEg{rKhAPhU*&4-~C3qG1?<DEujUef!bqJi16u7Y|PVa{19H934IR`tggGPhZ(ZtMIR{>ksGG*zX2Do
+x}f8{NV`x{3#q`$!DXZuU~!p%@i5L)9+qQU;XdrPo3tP>}_-Zx>>K@B^#KCIkBPpZ=>sG^u>AwO@I0H
+tH(cl^J@C+yDy*q2$jR@rrAvH-`@}ui_2;WA2)e;bd+Wb8p>p~B7#zMM!l|aS)PAa7TJhd!SV6&5z}<
+Dt%?9D1!kpyDg`%1d0Pat9q_pz2j2KLrb-i->|~8gc@n$=jtYw)C>O+B%~BP?o&=AB6@bPP*bKw$HWp
+iW<qHH4vl}4N6HRgW@;tCcg?^i_a-dB3$)Gpv^EhbobyjYh^N-^PBPz;hNsSm$n{JZ%O|pbh0@|3vQm
+F%nBSao9;f&<tfNO%3xh<=kAgNHUfH0YDmoO|rU9Pq4`8Ym{-vT1548S@)KfxibvaoKe;=mG9Q7dNwa
+*5y(AgLB*@iL}2X91M?VYV%rZSZ}O<yAf(#lbTe|KOXFX)L$~exFrRm4~Db2*UkjHivc9K$H2Z4rbXr
+*&;)0f;!pNgbXj&APl8GOw+7fZh_yx5(R3SH(QwM9o4xmx2rTj2w1aX9vaqC6G;bq(?X3&$jU(Fb-m4
+klZPLF_Lt9in2^kI4NNm+4AYMvoJDoETHu2Fc^oV@V^kJ^ldS9ISUOpcTKWMIj^kDnmDE9$7xS{hNnP
+zmiZa&QRg-U4nLAju0VS%-0NCU#nL#&epx<Cw!h*rB{P5x#jCSw?^mvh1K=@oc*0qc5mdO}NTffDsH`
+xy7I|^6QGBA>GTjinA16dtXR4|)#Sh8+JN?bsN%TB2oi2WP=7Qjli&bn(Cj=2<p4>UEjp+tSe)kh{oR
+V)^wIR_>d+JH5#qB}$|m&mxMi!CrSco~hxfeTu|Unh0aG*u+sgz>7f%_^B^Q-0$P9X?Vvwo67ZLjGp5
+Rh{)hp@9Y5xuPy5wB+F%@uB4F*Fpv#Mes*d*J|h?(t`QoWB0|syD!cZH>8D7R#7=ay`aPRh&ORbq~aB
+qu7gup)*yqCXCw{3&MeOFu)ez7RxlkP6G3gkp8=sLjGLs2m%rdrppUa(&eUi63o15$S7m0uggk#UFa)
+Yy2VS;ZQo-zPMt^UwS5g5K=hZd^oyx7UNf+T*M)kxHXiWmW1DQvX8&v7r>wJD4ke*TpyK)=cCLonSB&
+?HWejSsv1P+z}hY?tBk`-_`0EO#~ivb@=mPuaJ4Y5UPqt$VO+q}7EUb-y`RM{C2b4+6MorZ%F%2(VLE
+D=yx7{@8W1_qa?NmC3R8c&LOh7gjrw_D(fz%(~IV4mY3ndR_LHNPH7zq=hKa7I(Y^O%!^9<VAwyM<2K
+1Ky>Bk8l!jp423u7?@7;0(94@tRUtu=b?<Rx$qT%;qwgYfNq=v$&yyX9c{U@C^rmk68ufXpC;;6S>6z
+tuge<kusP}-BA8)pVW4+?3%AJF3Plx&W>Cp<VU6$;G=)?G{I}j@^L&wKbssbYFyDgkg7%+3eSQk4lxx
+t>fqoUhQ<A$^SK%sKCi7i*b=3zfXR1*ciWUwF0;V1b+gi0qG0%6;pT*t*#@UFcWSqcU{_|J+2PFjrMM
+I>jkp@Boy{FbXlW3OL%sr`b^F6Te2Bu?Oj}2J28TB0KC_rtK?ZTi+Xc+)EDuRD(VfMB)_6^uJ7Km*`r
+Ovr!T1Kv3I2swPK3kaPQ#3*`ItN7v#*_>g=71>75OH4Xl_o(05Kcd=QL3c0CNaLi(5l6mp}?`mq*SFp
+BXOETalJz}jF^BbPvZh&Rgt07X@!%uPGPCdK~t@GPHD9nYs;AKn2JT_5^aq;x1I<{t}evT)~oG$W9D6
+*W;Hd@&s<->Q-*OgHBC$dw2P>kIBpTGk{%xN75asOW&Q?~U8zKtGrR;*fNKQg4ovW7L;jUFphzQ^(`*
+7q-}D8v`jXHB2uA&mxdE%uGjc4fh!TJZaEWYZBzdhWK!_&YC7M)}{yny*`!)x|Y?cwxA;;H%Q~{-;n}
+mpL0z^{10IiAS%WckPf`W!5<Yy68f<?b8n`doj$k30%=VTMLy;Sy(>St(78qS%=)O85WY}hb&5ooW;O
+RmDaR$TvI-itpl<sl)}*7`sgX{iSht=%zsLW!s9KBEDPYylBjB|D&^5(jqUA50KHrr_u?Kxz=|C_AX6
+IdC*&x^T8aQwFWFyfDEIf};`_SnJXN<ZAv0<A6^GFu8wfrTsBHqDXnGiJ}IxG$Yf8Ndj#WKSlf}&;n{
+QXh9`hPt>uN;gh(SUza7&<fh6Npy0zhu)SO5V$mfm<)H9SSYz6KmdS+X01f_m>$i^8%czO*dt2JjY|n
+#N)wY#`c|B#1-?;`EgL+e=0vwrYyU>p+Z%>#7-|`kl<U))T?2$0VR$U#rvjoVCbooGBaWz^H=z)t{k6
+cKrJub2a*hkljQ)rG2)W*7p24movvWeg!d(eAm;R#PMS`w%o-Ig@RsDJ}e%z#M;xZweGk>)AL12)%EA
+3H#`9%!L%oijM0N?%1+p3OWXESJ4aaPhZY7Sk<wQ^x_AX(NlbwA!m?8SHIYByT`*tH~CvC-?7jCrW5M
+5bx($<JZzJ3OZCuxgg91`d{!R+dj&p!agb>DmD1|DsOf{1oYF2Jt{FGp4Om43kN`2B(~Bc4-{*_8FE}
+84{f5XA1V&gE_5vAu#Yousk4p~1i(Fl(p?{giPA&2mi+huBiyabzVtZFKpl@lkaGb$Opu}9tJn^`hc>
+uDmk0PwY_Km52ieCetsM+mD(!}b+RPXbs*jr{TW?sSL0~}Ksy;4d#SR&sS_4QN;-!>VeN@4Eu8n06eu
+l<f767)vIiEJ=R2j=)@-E7j9URm>vJP;?qO&z;u`C=w*uH|U-LZ-8bkwkeUqidb?LuI9bMo-cvJB1FD
+%ogdvj}k41MHM#HnMe67}*@wpQWk*K0HEDpQisN+ttA_cIS8;9Lvxiw>;%o4c#$K!7+@&@n2g7bj*$v
+1FU~qh;nd0$duj>S@$|~e1_&v=;_0+@v6K<`%hl0ukIS7vgq@qQUeRrl6?617tcw{b~9Oyz@)>|5O-<
+9Y3R}`dV5M3cn=^{51I^=I2I3<KcXSWZeAEGA@0YW2Sl)m0E;nVG}7Jf{^ap1TkF~aZ@I~p87I<}c93
+b0GFjKSOPp0;jx4HV*{hzwtl;lz#$xU)D;3#tdG30QZGLsHp6Fo$)s{4S1c%~lLcFWrZELPoMzY0f4f
+q<{l5Cnqb{W8YZR3`#mgN{oVhJ`K09eP%s@!hs$W(=0GIHwR=#K3%E~|Wr<BAF##vB;!(`#IJ_~GL8;
+U)R=?>i2Cqw>WB3b_zEYMfnNM>d#`QUzIQvvo)VyshDhqUGv!UFZrqV}|@Th*hB=cd>8vs>i>Iy8tX!
+M3}}t@z8^|!0B&Ducd$XQztwjwh~SP1A*c4vJ!L^we3G#OR*GPO-sY!da#V4N*hfQMJ_i`lzZpB6NiC
+>;b^o68P@kt+Yz9>1lqpZ+xSHoOHp*KM{h39CYPfTN!~ZeP<T|x&3-w6$FJ5e67{3Qb1=B2U;VmGD%?
+DX!n}Bsta4t^gaO-7!96r>>}5t~wY`36W%YRSM_#6&H7l=m*j?9bSTx#_?`f>t(GIRqxM$_t{0i`+vf
+b@ce6;QFXmlco4q2`RsM1aUVs-Uy*~7ts5b*bf>b$dLKEv$CQfL7T(i`-yt1LLUTU-)D5lrS~3pqzE^
+*smjCVUis+RrPfbh|bW;!9M=;7x~dC93r=<V^V7nLFWg5=vMLhR7j*s!??u(oX`HrMM3+DI}8N3B`ve
+^}^+n=l~-KUI3kF1R)L7H&AQwdEzcWBb%}^69)IF7&gLh_L+`T_8?<Xv8m9!>T|S-mzDG;@v9q#4Cvp
+uHArsPhOHRcp~BrM4JAYj6~cyj^g=>#lHlx=_F03-RMbYKwqiulm=-lV*MleQL0Pq1j)(*9k%4W74CC
+e3kIz2+Y|L(qZ@>I>kLRKjk%BmINJ@R303;N0>-1AsW5Z|^&;vVD;hIAdwSuEOU<*mlu@psDba|UiDS
+flM-Jp%DVi75vQAb-~(ZT8dfhxm%2wKKelTEzHi)5vKByupF!6P<iTbGhioegZ+-<`Mom%X;FZGBy?(
+wb1L)`{hvTduOCXm@wCNTui_vEU2su~IHP(_!bQ1Ch(vWV|Ui5%b%2_?6*f7Ew#vGE;Pjr<xUewh<HU
+3crxTwzyrVw2JFShr|w;Gy+ct(?*VDlh6QT1A265WAs)VOZr3W{TKE@l%#8MI1>>t$%ad3sa=Fztqw0
+Q;CBzlLR&A5B@zO`ePF?xIQW|-1v|riZ2iug30_tTKLAE?oi!3f5w$~@HPL7kCuwS1(=<M^O(re7eO2
+=-W85xK*_G4*P&&XEUEeA?^8Bj==DTk@EN2(36^vmyrg>4RxEd@<dmILix><u8OAv_hBZje>yPpPc2c
+@AbgXm%WU=-pK++@{tvgwW6pognGIuD@MIQTp`!#^M3*N;b`i-s{86ZE59U7@{sbv4F4xbhZ%Ka~P{q
+i?YFro@dQ9{w~AzHgmpcLs$U3I(}_U2mHa|2v^g&&id!^LVAV98o!fKB4Tnx?&wOsJHX$ZoAgZ&ee{%
+>c<^KTSeNw6W1OYpDk`y^=wDd{;zu~f<5I)z*dz-LH8<@i{4gr(1H^%(P39km`3|aqG#nrRK?zQY}-N
+#qZtuI_v%qNRshE+hq6e%jp8<VUP013%EE5UozN<_?H?FJG^$jUQSc}@n*`{DM2<uHo$y0eoRFx162T
+_4ZIKg-{K1IsdfXy_rNxT1O4lnE3m3Lzc{5+t?Z_#Y#gs_2V|m`72vaS2ZOj{1kBD`Ga-{frkI{oCi}
+J$bEb{uiqzPPK3SIUK-VE4OuD=q7Hf=%h_KKe6oA<FB$x)_R60TkZkK+o6#d$q#e;u|IXolisvjH1D>
+Oo~cI^9m!H#rADnCvVZ1YkW%sljytjGm0_8$D{V-?(@EFMfnw_Y1|^GGy8WtBp;teY1q2V7Mj*Mp0HC
+6~Sfqu#1&iCN&+q;i6+)hv&jl;A3~k5lijc+hd3nJ}RCH#-mfM)hnsMh3PU@k7nd*HFotzZ83JD<*PV
+*+f<2X1#<=Y8Ff<m=+Td~>uE|{tHlI)pXLDn{B=a%9nHzsSM9(^nuW9jP_Yt%+2KqSreAnb^YSSvU#E
+V$OqCv5^Cxfrg-3%P*C&r`DQnwh5=@Sp^e>)2Q8DH6*9j_rWkg?L1WO(K`0Y2RP^}GobRx-vXA~|CN^
+e;)3iG5uzNA86XkFD5YY?n+Ty`~Da$u~Jwal-`mLe{VV$%*!_rx5NI{o2?H?W+oZG}Suj$JL!I!?u*z
+%<d)@lJ#?H#!NP>wr*>Fa!0I2O4?-1GJs@2Cz}t$es`0EJ1-U9o4<FryM=DQ+;Z~{_ViSSb9+Zr{nlk
+Bq}?BV*kmoI|YljK22r|6IjBAa)4jk<?m`z(!Ml3RdQC0T2%epxzJk%ejP;^l?hhB5u;FcvcsM5{Heb
+Zxlp{7^ug70eP%V<Yj#V)So{w7`GS28m;QpSpy0CFujntn;-lYEoVlvJ3wuxjVRu_rX>HX}FM$<bT=r
+C~x9c@}cg%Y7&S^`$6{ay1DKH&D+r!U`Mae6#zIf1;l`T@HSHS0vFmZ|x^=?>|dTf97C?9~f{@nsd!P
+K3+=;<sz`+L9$-K_%#f<E6zzt_VDV4p|Fk%p}>{9iA>|L(5R5bK3?fK_2snygwRAX7jF!b?~bJ5ajTx
+rB%>wgvkhG186T(FVRcy@)ars2hYM9fx>tnb%4aqeJf@PRt|gJnDZKLD3)ZO*X%dww8k%ExhnwCq3+w
+lh$C4(IbB!6HK7rfifs?ka9ct9kUbK-lzq{XI|(}m;8m<WBC}9M?@n~`|<E%Gy<DsK}O+iN9#poVpLS
+?sLSri4STTk5CqbT5Er*A0@D3LB|TJ}q<0Y{>D@(0`dfuZ+ATB0mqjZ`71Q(R`WNl!csaMpwKbeB>po
+QQnAS@_w>5-Rt<9CgIDJ2tZ#%l5-S@Kk1F$#&#&JtWH8>zysdb%U)z~16S_W8^2Hb(IuR$O68hM(a&k
+gb(_Czsm$qLu-E=W!LO+Dq^7N%5D3s63Ji?j<+K6Oj^2)=YFUY9&n9cf9s+^dz)Jp2~5q`V7}8x`J*(
+v4oe&wh>%%%S9d(l>&v*PXcV-EAv#96N;%(op@i9Gd79IsjFC4nT5v@P}B<AgH_Yn*&0@#8a2G+ZLot
+E;0%=0!1cE#b;-%?jDB@SgHQh0DORq3RRgn^lLh$!~x?vP~4LO1!e^|x6mnCuQ1CO?uaLOwL4W|7FD*
+)-twU{j(SiV-QvDM->tyI-SA?5jmZh};zn(?V<<cR(x`yy<LfG090yrW96DdGvoued3|ijc-eQ`i0<W
+}xWmKU<m?|eY24;QSLgiEcZ6kaNUruKUJkXq0Nu``rX}Lu$k`y)!;D*lXuk3ACUrwbbARJOW+b7^~5L
+Q{S1X!|9nZ3o~Z%-*qv8RERj0i_R0@PJ5qc0}M(HGA@hc^!&UHo)>`QhmJT_|tP;7tf`!guoGV|elCb
+^W@4m!sG9N24!}umuMep6ak+;Rz257QC=v9Yp}mi`6F`tUh(I`pkp`C_xuFGgI&m|GeZsKQe(&DUF4l
+;|VH!j;{d+qdOPj@#Pm0v~~RG@}rRpv_C@u=hNyx%IEhKwh148C~{P=X~nceE~k{(ZD?j6n4|nfQ)J+
+rzEYyikO$Wwqwif;-&*q_EDL-N<4qzBbtr7zZ9(GJqlYqfQ#?psOe=Rf!i%4h(_bE({^d{q_R;wM<ge
+#n{6~CpZ~FT6yVFaZbBiGShm#ylPYJI7Lo7xa(ORXgq9uiOIb8-#K~n+;=JV4qL%u+rWL9p;EuB{`TN
+v}<y9{jibzZc|LqGweQ@RZ(WMEUa`k~Svc*gC*lq8P;F18h+L@ZXD^KK{AnSQqG?8!NPm4hO<-5?FZ2
+*k(s)cxl37x;)E848$WbCp6{_r^MUnpgf>ZcU%rZH%K43$^jqo)X4I9xpJV3rvea%TBnF+CFBo#n7%O
+>>O%A!j4#Ve;8gO-{vo8=9iD@i?4tD`zL1MPt7l%g^n4?ONouKwn1dD=9iBh9d*!s(3v+WElC)=^J*{
+*y@tWKokgo;Jxded13`H2-o3N8=#T(&I8rPB!D!SQmqbbu?;k+Ft&AK0ozb?X<R;*lzUz{<elf<Sq7P
+1ZP$#VAMD3jbuPho5)$qosf{#0+(w$}!G#3vp5if+Ao!icNzq<62wxC|h`W$xrJ=R`7Uhk|p)ve-GoB
+Ls!LCC4TPP`xKW7W2(+Z<<{`LmWY{WuEG?tlMRjA0^fhVloG3^u4@)X0y5Wm&Qs1fMtgCPW30b}BF!3
+wNs}2~t_z@DVcnF9ChC+yZ7veXY^KUbYG!O1-K*uAXMeiW%#hq{^}G6P9?VPYccmE4-*yiQ!N-R<#vK
+j>&*lFgD?YGMbWyYE7C@%Tq#3(h3D+PG=L!+8X~S{PPt`9o!KF?e<*qwniza?d<R}U=+ZKEf_QY@z-8
+uz}gSvLs-RjJ;h;zUYtLC@ZKAX>UVcvu`0{P-CXR738^KRaZUL$W=08`M~az_D2R;mR`QHVZjrisQcy
+iae_{<0$?2%C2y|L$MQ1)rM;)w;5n{~X#SAk`ECPP5>DkOh1%wSmJuF2!s6Bje=N;FfT~_a^|6RP}R!
+KF6=V~<VpQp$(dUG>PYdp>Cib?*dDW_~3p0lzM{}=w-DK<;e>2=9L0v<3Jm_vtlD&PoMhJrioGR7Q30
+ppv~6mY#8?59dy9KV23y@^cmVB|&m<kJaeg;EKwW+X3i-l)D+XZ#hj1yVpZWI&>$u-w#`(_|8Bf~6oy
+4C2KWm_0Bflu2OQg$=E-7F7cnFituOO)GF-f*i~X&L4*ft7tn1W6KpOpAv4Q>djFpbP>aFD<IHJQaT%
+CC~ksH?@+bO$$>GXA-@v5D3=5y#32+kj%K15o&SpS!?3Cg<u#}oMQh+_ND5LimUjf2f;qtO&ey>I0Xo
+bYE<*rEEDu(65aqGK?}$E0k<Ml;97h9|kLsx%AV{ttimOSArG#J_B<LPgwFZ^PC2C*+XAQeYdCk~LO@
+l6>FOzIt7Llzl$gg-F**QsWVXQ*E8gu?`6JQ{ROdg*2RtAkB{Po-f(QF6p6`UFzKLKTeGRq0La2_=*Y
+*RU1*1~A((m&?t{%$hP1leYqyG7-t+^~}aR1N%v7f+q+SdlQ*G(wm2OQ9n@OHd^e1g$A-j|-|X@fe_*
+r^TCX6H#(xZs)x~HhiG7!#7PTYp3h?-2>#%CS$RCdaD6C35aw7>r@|~+&|n>z>UV<O~!UKP{8OcaowL
+==`>Wx;Z)B0@x{^FVTmNUIM_1u7#+skne=f;Ifnx9p<_ZT4^CXwTDHzE9xOUz=K8D(e5G<APSXrI0|#
+GOj+575g5|~b?P-+1PXGMbySIP-Z2H+J?~-bb`Oe=R78_fIx6r?j&)%IT>-4ivM(@_iysY1ze(^f}X!
+Lq!dtivnF&4Ok+uoE`Ut%g{_C5r3h=tRTDoCgL=#;W}zVL!EGYodIP!20Q!T=ZL9LxiqaAhyDD!WS2r
+dO%Ac|&mkI<JbAQ^lJUrOK9Iu4R?!Mp9YhJSWi19FO+7GwpKfoG^o?Lee=51X`xd4bg~kPTIUiKCE+j
+lU-J}2{aQ*P+pd3<!f+Mb5DXdKTUM~P!i*0MU-{}!KD-js}1GMAvV=vbbTiyTr!{|5~q(e%aEgkX79R
+(8V$BgOc~#ukmSRy0!epjGG|Kki2DFN+@kM5V{DygbQb`=G*fF*D#<Bn@j~6aDI$sCDt5E<EKTuOVva
+fXeI`_!&mHn{M217*_vj@hZ>4=05)*>w^X)3>ctwpwmxN&2W2IW^oYy5e<^N1KBqC}}nJ1TlFZVFZx=
+u3bkNTM=)$nAK9(96kSSH(M5an6$*-JQ-7_NmKG4s|~!uSqt@WCyf_1^;?Iz164ort+E*BKH3or;lV<
+Q|I|8~{EN)xv8UO7yMM?k(W^T?e#>`w(Ru2+HL^XzrrEMAXMwA);^s2tFLSv6+~!zN8IlN<BombI|5o
+;oGpQBlO_l$HS^%C8TjqWf<#UpZwyrYw$S568f&?F_{Inxi?K4t4Kqp2_LW*2bF9@s|?6=rG{JugsMN
+j(@xMaWgbMPlsB!HfOl(XlSPSPBl96HB_|PLck9G3d4V;PY{xXzI}EVBF4^g|EllTtpF@Em1KI%2Nhd
+s8yUVo_xy)Sa&#TjNY;a#blVxU|%flV|-Y^ehgv&PH5xzTNCr7eCl?0?c9aHM%cfF_D@LA|xcVrKkpA
+0wrX?6yWn2}hK)!$kVdk&PfRt9bw*bKj?oe0}driG*Y5dh2P-P5uuTe;KizQz8m8NGIjTAq4g_D*IOC
+(DZIj!3V8f<a-oc;idTv9aXwP2QAwii==b2b0si22qpLH&cu$^JK<@QTGz?i9HH0l>-826}R~E23=)w
+*lnD%LTHYoDSvMYU6Y|m@&sT*V@yy~=NKccG8^W_={j4Npn<cM+H!~=fBZ3c_5GLMlSVyJ8P_G1ae>=
+Ittw5nEgrn4aP6e7w`+8UfEKR7QLr-Z&EJ-&g(J%Y`3y`%vYX;&^ny;paxzx!xl};|NYxrzks(=&8?D
++JDIwV5J(cP`{<0I5cXb2f8#mdCZW`DYd9y<k^Zu9Fn<p3yUEP10)VpVh!fJIQ=T}bYN~lxdsCQS`wo
+cwY;|dwvl?jQ9mo;S+j`KRrE6R_mZk6NR<B^Z&DmO;Qoep{h-(iKfa)rhwvT=mY@l^~ah;EB@kJY)EP
+*HB0>z$z+His)}f0HueKN&AwB{;gT+N;0MwenIOyv1Y3Te{H#>N2#p8~F8*OtDq=GoUBe?y&(U7u=lx
+GkiS9$hFf<HUlxiQBL>XHM{WFE^p{PUu}2bt4trn?eh)E*m<Mm=@uWgsb_A;_`!JE21YR4w2XiXy(in
+&Us583=!(QZUj)U$<!UeW73PMbj8c87LFWoPIf8CN9}sOMUS#iz5MCmguX<3%r+cuVx2T%@zTT;w;-2
+0~4;7SQ`7Ha(YSz_u^M$H?86cy?$VnyLC|%PqOo_f?Se_FRJ)I5dBGi7T9foGfrG?7PWlh)6Xsm~a!N
+9_Z+UhIayXw?q#Gk3-BdUdgk>K!F35PQ-IK4wF;R<)$P9KKi^Iw3-nkB|Io%?8ZN#U*>%BP9DAJ>i)&
+~-eQ0XQAh_l)+Wa>NJeH>Rk-56**69(=MlFN6{C`;`;yYl@ow1PAKM(*dM@b5r=Z-m^VJtYs8w{#_Ac
+^k83#%-^L8y?S+vvZEVldI-q=!kZX!xfd+LW<gOIw6f`F9HnV-11H1J!EgnO{ruvmN0%QyiY|Wo{PLs
+G=YOQTRQw@&UPIRQPMHcCC8vZUhvn({p&%zc;D_opOlEK|=z;AC6*LCBjTj`;))O&k1hN@{gTOg^CzN
+BwzNE{hz}`6Ed??r8Sg_i;u!}qtsRD+vrI{0g<%v*Ywfy%9LPA(Z6p}=x+Za*)eUetcDc3=7oR6&j#e
+lT<5Q7A_RgG!$PLZO}XT$ma#8kptLbfx7c%D%hM8EK1aC8p0Dd}=Pa^;OVV1oAG%gyNu7W=9ljec-+F
+&K_emX5vCA-e&p59cy+oT^X158X5Z9HZn?+X3N8k~vq|V9Bh;zfKxoS(&z>Mn&$DP)iSwN~C2s(ym-6
+7)a)FP1@p^kwn?uOCl1C;pyAq<1Gmz&*F^2?`yC(@XSWyy;5P|R+TS;$8~T^ff$^Uu0_ON6>^UcsBU;
+2q?yJa2w_u|ZvcZ7E0x>jHG5QQK80dl4rpJmGMEEHC;LAv!SlZ?paI7a83m?4#Y<fgFXCcAL$}H5CbD
+MRAtUnTl2?|hd0nTxyJ8c?q8L-7J=V;kFbJKmsU-{-g<pTsjs_j#&Q1%owhqk#tUMWKXfnBr3>rQl4K
+P0;H%qC5{d(k`_EGm_qxWTZRZss;rhgMtfNCeLW@6nY4$2Dq$5eFwKf&|!M<sZQ4tqa>H*`NfJIc8Te
+t!yQP3V2HiBP0SvI>Z`V{n4b=0lq!ZDI?#_?555%6YF%Zl}-bsFXf8&;)9Cb-qIk%hgtew4^NHCGV)d
+S@(#$L`f@k?&9`LgM^A|sJT8vsSi<Q=?5rTcjcB~t?|?-NWWy67bIGfM%AcS>grQ7(aHZbYxpCk7ZKN
+q0klm5WodZ$w#$}ujtHIBf>;!1!SFIFULzYyT7OCR>~Y@uK%ikx`8J(A{Z1hpp6zpddbcL&@K8#-D|L
+gErE+i>Hz?_4z!BS+U%HzO_oUsdndRs-kmM&oXGOMvYU!4OVcPZJ0nx6I&X1ks)@DW%$&%h?E;)cdN=
+MJTsYc%Ca)R(VUhRar%BANzZ^<F39FR(4AUSti<nT;aC%jF^iSgYWP3i$U3{@t3$pFDDj{wUro_r_x*
+og`vN|!%&YWjK^TdYk|tkd)unS6cOAa$eJTfDT}t}E|)s!4(Ysy&qM#VG5^YhYHlG8-84u-DO1H_t)F
+-$8Pr)1_vQ16_6O@pzE-fQcdPyRTL7B%mlK1>t9vqx$i3S!GK)jv+`~?M;b=P5&luiR<rB0Q-_><50B
+;w0(|~?g=Dkhz~IC(b-aARHAAqYCGy;;~3(qpb(R7jPDmw!t*Rpu`8ZEG6<^;Umbji%p17%w|hs8N&F
+M~*5!M;O}j9z6kdBR_qrVY>Zxzw=>dV=g?d)CU;EaZEVwS2@AoZIws}5UNB0RQbh<OWkIvDo!cb4j^^
+1C-QtI|Doex#X-)-$Efb4E`5&A<o8F{qG!AR~v*Tcv@p$agsw{r~L0It<9Ou0KMMrEg*tGdVLU6|;BM
+AzSdY;GgYB@6+zTMZ0PW)b=+BAr`#O#IjZ%?zEx;<2zDmGllo7GM2fm)Dq_+m-_rAV$|E?Ezk=>5_tS
+csIt?`1gS|tJ)@g8<t}u@8;rRtH6fSu9(`s6JRW7u_pGwO!XZC^z(1P!vREP^gESNL$M1f9WQ`Ak4;?
+P(<b@#JrGY@VzBNZ=(?#;Nc8(hi0*ovA>@!zJ9KWw?;b@^rOP%e^=I`~4;~SCOF`b5TylXyU$<4>WR4
+Ox*57r8C@(~5pPg=zCwCg5yYO|d*Xh9nLRU8|2Esq^4uvtND5e_y>FXT$bg#k?=oasbo6F!GQ0rjP{d
+Wko!1`dNY0qT#j*_AQ8mFGr>kL`X4%hH(UiWsuI_UiH&&1?W5TL5S9GgN>_#{RK=7hSbtOpudaq9383
+q26^4PKp|@y3-pT7|c9P`FgyO|{3c618tyGZJy|m2p!%-%IW8JN1B)Bf^~VOJBJo!PQp3f<-S7b??p@
+RK@;;Alx<pIpZu!0OJ;n{i5V<@teBr0Y)yRdONEzM6GCADJk{_=A+528+M?@9$2@u(y4E1Gsf&9Wz@h
+DbdwwA`y#ugua_JyK!NmlMUvfXb@Zt0`rkZw!3dC(MD`tH>&#d#cg?{hHyD+MN5Fdk5p(6+octLgG#8
+fJ1oqy)%T)c|x9_Uz-NO}jDY*q^VD);iKj5O4gS%lqwg=cjfFSMjL<i>e4a4C7pW8w?OkMCsr3ut`wk
+F}}rW3i!`o70&+ihZF-Z12vW#o^<RS8BEsG0MfQn~2Lik)7xl*&lQPauOAr#w~WssK!1D!{yh9i70->
+B2G?=I2kJ2cMjM7To7gpFD_<oM3PmV2_dLr5tn5p~vm#&w9j%xiGSPx5;C^EHo~w<$b=V<NlpIlIG<S
+{(9nW40)8$Yc8pi&fQ3OrX0|HzkB@c)9K%zzIgfU`|qYNp1S9%G0`BB#iNVYx2KmMjb7Iu?m0_cg|8z
+_WO$C51V>*W0GO*s7e9@#`sh!HmH5Q8!<aKG`EzW3Bgvmk=OG<>vTjGwRr>63={IE}x|0Du=CtwViDS
+I;$C2cHx3=`#g)J4TPn0gtnkBnN?;UEkU1QiKNt)UorPiI-GX{^-)Cum;$-`)lV)mC%iV9Zf+I%kk2?
+B4gOBK$63pQywzKN9XP<rL^PE-NXo<nDLaJl$8t=bg7m|8b7sif)XWV$hONor4AJ)P}{5nkXw9cOV{?
+n3SfG5uAePI<c2T5Sr(Y7A`FX<g<SsEhk3_6{^!6+;khY{qPNALp&ngG}+8WBETEyBA^GL!!H-Dx0ov
+u4%ZuBO8iAY*n@fJMND3_JS(8B&Ux>?j^d`J8GmA`0Yf;eK~~gsN`><Je>x^Lw!Ak{G|7Po#tfjXtLg
+$@RS!^0dP7_Wn!L=F7276RD?lSEzyJLv|;Ic>Rw5v*ed(Ek7D^0uNpa}%X3hib=j&zYzR^!MB7qrAv(
+muWq^*__w6O4Zj+IjL$~zUr)pzG#UluS_*({W)#J77XphDnoUIsYFXF_Z(&hEhg?hW$P+rNubE_Ve@q
+`W)qOOrNr)v!HhdGo>%_tEPasXf6d|67^vK#^H9R3={+4h@>rYb+SWP!|QDl}8o`zb?T)@$$7NswcL7
+uBp5$io~$IfzmWlkxyssWZ}{JvRlLcr<-Yn7wjF(p)R{)T`?d#6HBU&~c9ENsVVDYmBLE@;Bz~dH#@i
+S)?vwXgU3Yo8mYI<~r8gTbE3z`7?|2`tLG|-qSqn;yHINDOH@e>&d#qB5<2vW8LS$r4s(iMhKSbuis;
+LkogP4fKg&qw!z2;6)Z1s$Lv+17c;O2`*yP)H%+$QG%QRB5N7ymr-L)56a*~R`3Ks|;jR3Hlz5LqNZw
+eHCS(hs_|fTJd84tG9ESQz>((rd{uc19)XK5;M3>ROJscT}@+%7YP`gyBGklHr)s^bc)fM1SV?+&#hw
+j>{jlSSAyt<;ELp529b(aq)*vt#sk>fnFFiWzzuf`XLv?F$?nGS@|2cUbXL8f@3<ND3LwlTXhNGp>NZ
+c?-Q%|itb9g=UzkzfvIO9c|xy&5<ukOa*Z=UeRs;^`DHL6F@+pF-erjes69vCa7#8*`#_&I=#)PGsjH
+m!U-xw7Y?h37KTi<>7Py16KDfW!2Pk+2TSA#YQyq`tuymqvcUjSKxB^<<Fo(68KhTZVTpZsn_j9;U-o
+0O56e2P<ypAn}Mo6X&tu9-85dr+-fTtLjUHf6`WyFvz<1Dy=u@Zuc(sE8=G`GY;VB#0gUJuyfCYYG-4
+x1M?E;_&WS|~`+6YL+BLtZVsF+x4;fHD1}5BS4QiW)VmnZVu~>Vo%#KgOFR}cYetIMpYHF>1rk3iM;v
+1)Y-?5~iHtzLiSN)JK=UAo46p*z$fmkXM7#)>fWlrJK(dopWv{UXS#|j<vvtgIV0a+bps8at4HDfGLL
+sW4i_&Si=p+U@DV<_mP$zbBg!DP10S76fZ%MVkRfqCvmOAkHNBtWjNa4M01QJjr+ce(cFQtINt#+3L8
+PI+D6uG3ijzd{~H%Y&e{#R{Z4T`GbCk;QgGIZIS5vN55`$FGEfFK(@kS>r8HRoK*Snc0}Rmt2cMcd<<
+tM^mHn7~(}iZzzFqJLZ^SZgM5Wm0p<{=dy-Vk@N5)=Oh@o@zrv9<#D;XG6)n(Z*;KG)fFwX5zQdW$9_
+bJa;E69p0;tpLs%Je(K-(?KXO?&rxFCo-W^N(6Z!98?ToJ0JKFJ7`U4$vt2s9{gJKmz*T1?7w?z#t*X
+Z{R0aK+Lm<w4QWD{|Z6Q-(YNO*UWY+CEpYU~PS{!Jm<^Jo}HZgO+pE9rT!+)AF`iIemD*5+$@h3Et38
+(=!-6^|H8Jo#`!fy5@0u{DME2j$J7N;cC!2_{&OI1Z>tThNb?25_$=pXYc(mK^O#`+(#A7R3yP%*617
+jIc5Km}YM#7_4hzZ^VCE(!*TX?hPrdPpQlMd>$QqoSd7yl<r|lB(n-2R@$Diu0r56Xu17)<H5pxknMR
+MQ!E;vyRk*R0jT)wKZq`SBz(TrKzPW_$aN%bj5AQ=;gowBvd$i}UiYp}+mvZ3FDX9Nq-JI{Q?;C^Oz9
+M2%Mqb?X9<6xKtH=%jt7|D4fsc7$f=f@CeB*WJ?dFU$?AGu{jmclu-5RxnN2$TEB=Ej-V_$r+|6)?59
+_7p9<)4OT<H$i`GALEAbEu2grK<Nj_bkpljMq(F;#5wavrhl-?axUrX806wQ1F5>E$h$se*MUjiFv2g
+-u1>UQ*0Ow&T2?$fuAuQKOVhG<)Ob{nV{va-7M%dV=f6+(mHk$Of6Qr$6s#R}sat40U*~)&n4o*}cW?
+sj-lef2W4=oq+Aw737A~H9Zjo%G;`_%JIQ^+*&FvhAMMN7Od||qa_YcLF|AnfdS$7oN4Dt5Az)##zWO
+}f*NtrqM_r*B!~?tDV2zZx?}IPK#X?Ax-4H;P^c4to&hskrTi-@@vf<}_ma%rhN61~!~i0Dv-`*dKH*
+&gR<d@36DJs(CcdfAVLgAv;ozzQgFer72aut+-h$Y-YvFV~LI|TSXCIEIBGIzn&nul~3O}$s<~JtZ#c
+P-%IuB!K78JASfi2_H+0K?MdrfJByi3H4o(^|k)053e?V0Z3BO=2$@c{F^tHf4Mk7LF1sPv5DFs<HMP
+8*%k@_I8o?|7?7)>e=JQ4!yivSTysL+}Xw&5*=56|}ZvG2cELjcf;|B4iH%8v@@kS4A68Q)!W*Pw@Mp
+>`N(mPZBRv<x|RdYM_9@r|~|Tr^fqf+-{HJWie$fi#(4~EnXDrXFK#q{|8V@0|XQR000O8^OY7yp$jB
+<Ko0-_9yI^}9{>OVaA|NaUv_0~WN&gWWNCABY-wUIZDDR{W@U49E^v9x8{2Z*Huha#fyA33=}JtKzU;
+VhGG52o^)$9Kw!7_2>{TEVl2}s&OOTc|(~sYC4geCo$Z_H}Q$55IE(hm+1G2*=yAu}4ILQ`!tSaY|f8
+xk!XSBl(Wxgqr#j<4nVZdH~|KjBY{`rA@*py3|u?smb*SrwyovgB$mx;_m*mWTUdw+a*bb4_VmUkuNS
+<DX4PA|`o|Mm0b+4%(y!n)DZr%y+RDOZXWA}@pzSqV6>aw%AvsFKM!n<uGIEXf#WF$@*StkS29j()F_
+Tb{xmR!W+LnN)?i19y)ZEQS4duwTiQV0j^DsaUCy&P`UiEMPuc@iJQ4pQ&6d00sL~D*LSvqtU#OD;6>
+{@)piWliAT-ByvQ_Oh<B+^U^Mwt5^VMg_%SQ?NlVQTTzr#SiKyLM$;)z)9IA$vya{h&yu-NrFT6VAx5
+HrVS`$@5aoLs^QY5{uf%j3jDY8yU9q`LQ@Iu~n{717D(4Z9{~bFzdq0{Up1ps6@ZsWU`r+X6^62~&fE
+I!f2A28W)z#H{_bT(oIC>J``jeBR)61i`)Az@xN1fF>;qDv%&Hhyu{Bf1RmwyCiBl`pYTmVv|h-7Q2@
+#X9{5tYH-h`|SBaL|$RtV+@naR<#3cX=ucs|_^2QKeXsF2T=I6htS%;(4BKaP2KG5?Lt*JcR|BLd{q&
+K^$v@0XqR0AZHO6(U(}v*>sv@NjaVRN~Ckp9InLvsmz2y)~>hTk704b0eb=XfOe{qXA!`y7wqM{;46Z
+&$1Z^a{02%|00U&PLA*4Df`Q?PA{;yhkqK$%KB!`dniBGv!oR-#oylTF1GG*SxDZq%TE~761Z_}YV_*
+*etO5KTu2&*&F9cQ8BH3rih4!SKQKIw%P4bTZmwl*aX%ex6565P>W-xLepb~%EOnrxPfhnT|#10ZV;f
+LaegV?7}7N<|2kbAfQSRvD@Y|ukKph>7kr{?IGhPg{736iUg^<sK_aenmgF@1S^e6GK|KmBR6l{?PYk
+Jt68Ldw$Ra<jeU(G9M}70vow=C~XuL-^i|S87>LfRc+WZcte-;b1-2@Y6XR0Qo^{LbHZc2@Mal(Z{vH
+sI_ViI)p3H>C%oibXroSDJ^N#@;8}e1Z_;4Ixw|%9Nfwz2E7NDq%`9r;GbfX3Hz-=N014&PCyf4IR*j
+q?1pV*^`r-hw$jn~&9~SDy@f0AyqbR{ES=)d-kM}?<?iEr(5DtZR?bEWzV++P;r-Jg3eBx5Y1pLAnJl
+ed-FG6&-XX0vBp%1cNhRP8xA>=n7)**Rz}m{nIN`pJLNMA#S@Xd?a7+k;i2@mAY$m{`rx4$7Yk;WN!*
+&H!BaH|b1yq!3oq&IGJWz*ZT-jBn+I9?`11=~;R`lE;t#0#c3dC<5rm1_m){aRJVxMaBPdN;Pq~^;xh
+$cu(0Ta~lTl~^a2#D95sYzAf#Dvw3HP6t()&4u4s%>%XT$ELjnT<`Z)x;@TfH!V!t$$eNq8q`3#w_m0
+6-NorxNRIdr3qs(s}{azU?42O;hFLfP+LFdEM`v}f7-VgkOd@yV>Wg4Nabl#cF)ESBqfk|nLS!pGv<4
+6u=m^>Sl%L1?{fSb<gHa&nt0e~XMl4%pg1=Ws36(PH&0v(vEu1-td6Yi1Zu<_(bT8)8PG=3vH*UeLi+
+QLdonoxu}>#Dq>ws%Y+2$iew~(8o`M5IJeuEtP~SOgC2R`;zc!m2I)dv)d!tnFVjw>_#BoXxB1msRFR
+iAAy;P~4nY5<bfx12vY66T#Q?2n}oW^?U-&+jlgWg9~iCKvOIa)H2V2+9MFlC3yLonIsBr!F#PoK=-7
+!$ZctmLg2+HF?#oluR0c0>(M;A#ws#+$39tkM|W5!F;65~zO$0}#`dOae6%LkHfJSb)jN!Rhh4ql?Qh
+$$%8XYp);j>9Z`$eDC@5SVo`{R5pgPSUj(xJy)_SB7widWw}as;9PLGu^MDN<e0nwpQ>xb3<xF-12@U
+4e5}n)RfS%lE7z=A%|IgH(%0b5lmiO*K?T8>0<+P6+9*<S3l^$(gp7GS!Wz%REj(TGjSB`82{4KG00~
+c-dZkc$GahSk1vtNuj+ilthdXe}WdVgVmeU3z{KafTVL?O*FCp<Go&)9pGStfEfUt$nnEB*Wfh;=|ka
+FR$83~%;qXCbd?En{6k|CHuo6thR3IcE}rm>ZggUQ)XkO|5)R$e#wx5P56Ap#i*)jQ;ah$Ado@>>#`1
+7c8omKVrS@+bt@0EJ9aLB;0+!Qe|K0PG|+UO}a|;wckFA&W`06wwWq)vj|k(DwJ4mt;{|#lqX`xns)~
+up`U1zFRiAkY=2LH8sn9^B1)*TFPSovZ%IYgC=CId65CKzL!b2zG2kEzykPL;nh-f#6hX3LlxxUkD*E
+*YW2YYyPvFnIbc{ru=+>rY@r$w$XhJ&)6K-N)8;MSFpSQ}`#Od#QaR)4<XdV(H*a%44_Ai)whVCv667
+^{LY@6JeYP)Q&No#m&7HrER=FJ4gt#1iAdoL+Jow$JP~dsLjbiHJZC{6mZ>5>KAJkMNuEE+c^&l2~8}
+)4S=x?l_LwtP@tcUY{QSf=$;rG3ohP}p`g-)W<XVd_XEGfxwRQ#N>Imd0MC@-q4t3fS_x_a>_iiV`#r
+}DFA<h6sHOK3wU_Ngg>Ecjx@_kbkG|8Je#lF_tVLu3NxA#cNu82Dv{Ie1fEM7+Z6ty3ZHFq1x(VwI=7
+6jRLO+O;XiwtDw=#qUuGshLS2wS0g$_RRd~wxx^Y7S0;#OZT$bht+@o#gh@FQvfz8<O8a*TLZw^r0b7
+8cEEITE$8>=<yOgt3niT_?>p$}QoIp5C~hh;pB4tF-Y;}6j=w*Cf88`jY;YMjiQ{L@srTuoP+t$>5jg
+Y%=A(c-h8yG?$JV8E9KsITpV8|DoCGS63M?)wQ~HAHa_r#LvT1GCRcn%Ao6G-x+^lk3oAqk7yz80z@@
+(H|o7N($ZuU-SpkVq^ZRzPmPsZI&aZ6=iv6p>6`cU^l=R-6UcRARHVy_H&FJ9MQU%qBx81}r%0G~d(m
+$`(V?sicJ0Jx%g{9^C=dN{Ta;|7#-NZCHak{|v=mVne>e+6S|t?6+EcyJv&j)^btXX5>K)n?X{{XbKI
+I8zW~!Ee8EjOF!KCbcbU3#Zxv`XONw@?~tdWcNfx+O2zC4~j2Nu<RymI#>=B{B_bp8PInBn5bzX7UFK
+`n&LuWyO&{6lr0qV@rg*a;<I!^$uC{C1iClsHT_?YIzlxZN`62cG>)Jdoc)|7cN0}^sIi&G5eNP9(eV
+V96sN=b!73>B<uD}vUL~b=!V2%oFz<m>Myy0u>X#Qd<(YRSjbC2LxJn@t@OC{4v)&ueBA3Z*qCklfp0
+a2OsY3)Of-H3X_|!}}7i@}Q?u5PDiS7`>*?UZ-TrIIZhV=c2-EtHyjp_#Hj7K+iQ4Q?v*~Rl;k}Q^M^
+&_pKeF)}+N9^pvVa4$r8wDt0>VuXbX68H5C?NIG?K!L3nnGB;l7)K@jv^(jpYDeF=Bd8-)r+6brKB-@
+zk?jdf#)+`;9!Vo)NovM=BPkpHj|}ouqxqz?a-mMlQya<hs;RC278&g`o-u56O(6Z9@>31EI%nx^+2m
+^QcX+*nK*lD-J{<;+mw`!CwMJbu7zl%0T`^tF4k=tlSL-+-AjT@rn^}sI)qt+szo$zAduc!%2A&UJW`
+K_sTN;qb}KMBI1y6Ty5Eehjs%EqtK<n5EHFmLVvm_SZia^F-48ZrA*(41o~e}E9&k(rlJ2?VS{S0Y0O
+p-hgB9Wq*$<@qYLY02Xze9n!uko~9w4hP6ajJP%DCBPu0GLMVOY@BH1g!07b83D*I$2Sc$1;Y*N=|)O
+oF1WZGYRfWP<pn-fw#d<m<Zm7Kp8GFIXmR?SEPKI<6gPtN9*_ly89eRC3ZoEG6s(7~XHYxm){s4ET>8
+d*H&^zTj~Va-WK{!1SVSLu04}FQTPS&l>vCw{yKe{I0|D5rx+cX*B1AtZqgA*M_Wfy1RQ=$a12UHuk;
++I{JD>mPdu9!0<r2>P8_HhSv?y_$Uwb(=ARf?d!@`%p?yTC-X#m7l<#B2)7zZTcaH;sXqW^HpM9Y7!2
+P+;Il*~!bQkP914vebn@cx1ibG|-&xZ%guKi#jOd2rn@6Uv2wR=$;i~tqwXo(Ca~qF*bnpBhp!f6pm0
+n-|!m39peV@9=tNs0oe`!y|;II^FI!TioC%>}!0BS?++|lP(DQ}ugkV}KXDE+|K4xTsnvhGH47j`2ag
+*!KvX6<06dm&(pH5@`mM;;%<MHjd>x-}vIaZ^yz+_o`@`8qf%yUmSu!Pd7?7Tun%B+5P{ZFCVZ8PFFa
+b>}&5ucJrY>=g~p=2miVFfe27Jp;yrx$?A>Fw{S|e`pJU=QSsqm#sjCP!Z3CzwHH>=LM~{m3hYsBEa;
+pAFm()CYVV=7KU!d>Rz2&x4E>+%Z*ct^+@zOZ-Vsc+xVd$xP9E4V4>N99bC7-D3O~-jLz#@L!cg|mh;
+t>H{>vIZ-JBdu=;-<`;M7czOX~H)vwY2imp|-iy)g*f_@A6z7MsvjPM{E1XIV-**GIMPx+$D2t76Q-f
+J77%}lS_?b{2I;J8gkjxB%rAXkT*+iuQyw|l+XP%2$#!{FfFCi6K)drTRc{J>O~VeRK3(CeZAatW+3V
+=1(8n3AerUN%0-*AJI4pQy`9T0c*sEpDvE{ysAOi<BLzcd7o^HJEldHWLxPck)8BENs@DAC2|nDhJ&0
+$$!q!0jvuN90O%?;o}oMucX3GD(&SpA9VGb=n{GCfTQww1ClC5M%f9RyD_&J2+C})w(wHlrnTU0uy%&
+(MB+NAb?F5gR{Gl4#UP?mH54{D!d3@ReF(jq&if^X#(?=zRp7(KbhBq3Q<DpE<TvPVKI3b4*jjk_e-x
+Bk;t{G@l2^tEXmqS!-mpwKMVp3!|Ji|vHqTyd7ouDzkr?oa=+|7&W;sSPFFyY7nYzZNmP<!VAu-viW`
+5!M{+m}%uC4+f;RU<FRS4;@fi`yCSl3fHPUQbkO9KQH000080P~d=M?Dd)0*wa%0E!p@03HAU0B~t=F
+JE?LZe(wAFJx(RbZlv2FKuCRYh`kCE^v9R7;A6aw)VS!1#f~ta_b0fw&7kF%Yd%Ph5$obEZM#Ufy!94
+%~=+8Nh*mq^uO;MJ|t42bBo~$NK8@Z{U%Sz`KxoX5*2SYOVa7}`G0U{b~-yH??ro%e6!Uge?KP|Z{A*
+<!@qCH@4TT0azp>%Z2@4v@D*zmtB4jvZyEV?&|A@v8?n|;R5J3D=$eXZ4tua*l2G8+XvrTsE#Pe<8QZ
+f)Bdr_8$S>FLuRh#d75Z5d+EnEI#}Bu^UjO*p?Z;nla1-EWCnqPLs8m2~4J42&YaY224H6=IDtAmOvg
+a!)&THCiI=W$`Vr$-joV+2Qu0D}(zWuHMI<pzyw?YChrTtPUzfy;O+16A8xBYCkmSRtemDsmb6aUp{U
+BX*!v@r@Vhyp#EO{otpQx31wy!KeUDIY=d*{m#SU6&=fBzGBaG?T4lI=i3EW~-ViMXnxc-BGYtE*}11
+D?MM%2s|i1{=OqM-*XMhF%M`g<9xxMft*qxkt%vTvk)zl2(5tgUuFlNjQuxTlA7)xDoUOg<gg^q<nod
+n76ZsePeFbdL9hf!Ooca75b#e3QYRRFKY~aw`aWjVh>*;Zz&n6O-&0EeYeFR@F%lc3{$foF4i!(VD$y
+ZqWM2rY(#I2!gV(~p2HadsJwhyl>i{l0Q^lFe$eF=#`z3GI;%<3yKVY7c7P12ZFQQDiGLlMGbNW!T1%
+V92<bfnLpNPPhfXWl(Ib`*QX~K$4K`x4a7Z<M=CM`{>LmC05Ppl`dx1nScP1YR*`Jhe!TBx?>I?qV9A
+aCb)Zvtg9p~{Bt(Zxa)xC%|=bSqsZAyqAN_m<D+DOfN7qY4T7t-@{PN%%qH9^P%G;rDd41%rg}ZYvF+
+tR0A*+V><}l!5I@3EFdAmbqf}dSR+7d)Aj9M8jO2z{l4Le69hW)0!&=zJ$<Knl>v|%zA(&1Q6YOQcH5
+ZCaPsCzUHi25OjhfSlnwNcHsH0#rW4-2!=gvTZp_O$rlAI98t&!zk*8j*OwSbiB7H<z@Qd`st}6hTG9
+=g!>KFQYsK^tMSo@{!Z9C!&DyLT>8CAUZ83aTv@^LvNh<~9#Xc>&XIs{+0lD7383cyc>`Y=_=7I2cR<
+fo2A+I9}_wyc|uSpUFsvP_eDF#YfzOR9CQ50Ff4PB#?4N<Yhwvezjlg?m`sF>+UFW8I{htoIM-u>j=&
+8?w9kVe{8P7L5pwf20oN|d&G^%5+Md*_3+u6?mU)CE~{umgnLOA+lFB*e4#uWh{;Tb7D-^5@T<+UL&@
+0&FN!6^9x`4+zAhgqE0A4ZOvb&zRhj4!iu=M^P{%c=UM(Bhtn+hinhuu*RbavM&)7@bps0)CUrGnTN>
+kft-JdptU?q(TyiBar!B+X?T@hy|Q%Y5dwQ&u~w6t0~Dj@E3^_Il7vvGl2JWMSXQ`Xoxn?qwJ4yQLyy
+E4>kHz#-hkMLkh5x0mS$2aOIV@iu)zK+^8v?)bRe3~j3FmNM|%V)5*T{Ru~S0lCSklOvfM#o|D}(V*V
+usPS!~=|eYGWx$}3TqDT7Xpc~U_`(PY<%C$fb<XzD)G$QfGq1Z!6^He8;n`Fxz+q3z~z!NIIAckB?Pa
+gZ0_CWgcBIE^p{-)Va?U~?!K)W6s$g)tUJUsg@Z-7ptf!<dn);FaZEAelfwC__Erw1D7RGt$KZTC!Hm
+4-jFaOoy+$0%!e1!H@lysZ+r$p-X#HSu>_6A_lbAsChz8gxm43Iab6FTK<n1fmz%#98L>Mmv_J^%8P_
+RBkD|bwf1T{kUK!Tf59*@Qmj#o>)dV;BF8=mQHwV1)tG+YX$IFSm^%YKRVk#eE}|U@45m89z(*Ov;!O
+Mk@Vlm(#y>I;(qV@N&K*JgvtrK?Ft;eUYX^DrCAK2=YaHoFsMp)&{j{<iv&XbLJBb|Ykw9L~qZ0Y&K{
+C4g3Llz-wLFrV2%{*BuZMEAWvg9YZ<Vbc(k_l<<*n@EW*GPJo(lTv2Fis|DFBdTfT@n@0)WpcoVbh$)
+BJ{-7h~C<mjoFwNAeil9dJpO8pl1sZYPhR$fGEIl|pWCUPcmQOCKph`22(ZhEJk?UAy{a<A<xEeyRCz
+vT?fmT&>w!hu<dYFtJsiHc?1n+KIN0LQKFvd7Ha>pj;=iqL~Yq@tpW#Oh=%i1?FLmIX>|?^8<#vqA<G
+kmp+M_Fg0t}sIJA^Dj5AOrtJ*oyd&Dt=#j8sQi-_u^b*~P_8!$Ys!#M7O3YO9xVjA997p5ee<#;hRHU
+IJ9_uXj)HUrZ528B6qX&8_O(k7FjM^1WGcUK?qlGTriyh6dA164)P52)%9QE-D-~ZEy;^<C>)Py&cmM
+^-|kb)ER4B`9B*xcSKFMu~&b(jNX;-bZ$6t6uRoJTF9Yn_Gz^){+l%bLm@Q=9M#+ug*tTNq1VNp9g7?
+ufwv-%DoCsY`Ph@}Qt&NMRzgCFY084zKUc-~hpC?sU2~q?$l?*F2HZPP^f*dV)6}(!)wx;L*9w;c)Rc
+P)h>@6aWAK2mtey7DxD<?~7|A0063$0018V003}la4%nWWo~3|axY|Qb98KJVlQoFbYWy+bYU)VdF?&
+@cigy*zx%IX<K{_|otegNdUti}YrV;2>)XW1vz`5@*Hz>ZZL8Kuvy#S+yUqW80QfCZnsKsy?e(2syRj
+t_1VIo4KmgiilP424ud=+n+GDHsa`IpJWNUkCn?0|V_jP`C-Lm-ki0wYz*=48KoV~qoud9-sRF~~tT6
+6ZYT9sMa=2ba`PA8nRSBKAMM<=ss`=MoNnX%_@j!usczkGN4=J*63Lc6U;j~*Q^mIYt%vZc<fx@0YYf
+6dzzz@_Z6s?}J_bbgaw@rJ#fy=|sjTPLgKvZ~vLrA5IOR)<9j596Kb(=pqb?%;oR$ArS}^eN*Xmb}hs
+z+yB-47M)oYQd7^a@DSCo+K<^AY8U8b07&%ioE4@S~Oc*@&!JfH=-L~q<NuwEO^rZh6%n&F7tw`9)E4
+BQvF_3S64u1^=HkuwgjlD0`nqo+9JQ0eVFql5@ig?-SWDDUz0?(omY!x+NvHWZ4Dp~-;7y9za{Pck~d
+@4@((TjE_*G~`UU{0Zg~kaTh_dluUD;^3-qkhd7E6^CpY|l%&vHw&@(ylsl-UN$M?<r8kTa53-s*S)8
+w1k@yX$vqvUwDwS}|hHQQIypF;ar^ej%2GF<@OM?i>qkv5H9{nvTZK-*beS9RQ_%4lzk!4E9p0TA)WR
+bKPVutDCih3Jhd+y(r+DldzC-mZn*wtTT{SzEynX~uvV?`mKNi`{ZwWDW2vEC%j_xrDq*i)m=&cV*4z
+)m54Qjc4Bo3a&%qU3pVhccmFqQNCO)E_fXr=CHg?i#%ITIRi8;RvgKei>?H9n=MxtFdcUA_E2n1q-&L
+}fVGnZ_8sh&eRdY9VWTmN-hTa6a(MLeO)@)r{^rHu(O1sHx5u-Wv*Y903&kDLISw-`h4f*o238R_JxT
+%SHNjpC_foUvL58s-l2#F+TJuX@BNs>lP1&ESCBIx1#XY;rK}cM{`dp{w70;kKtSJZvR+V#(49kA~>^
+C_5Mo1Na!U4?v-8F|D+K)b>073>3NM0Qt&8WfQcks04v_}E)IQpSv;^&9IN29G|_t{SJ@^JR*#R;^FH
+Ss2rACO*0up{ta>(SdHZBe*I<1Oa?L?^2S?Cg7N^@2C^I;Uk0H2j+1-&J+iV9P&M3qDzbxWYdURv=<)
+^V@{vS^&bUe9p^;!!)*%U++Box#uBiKQWE|DYJu?&v8HHu)V3@3)p}~m1dK7$FK13Hy2G+@Rm0LAaCp
+HHqSVraV!K=qv`ef;P`9c93q$nq^DO0kM2+%yl9MPzxg*?lh-L6Q}BPW5aut{!CE!1?}m#wK?xaN$-n
+Y>tJ<q2lFiLJ_4U{9g$CnvnSFq>w@eGYYBp!A8z}(V=Z)_4LW7T8`uadZ^tQ=@*gxC-kN@Yn|5H3ifb
+-k5<z|sQND9?#@+A@dgtx2Z#N$K+TK0g@*w0XN)Xd_tdIt}$*SL#{&!uZo!;ueOy)tat@HQUV&!(#-F
+fEU*(>eU!oO(U)wFC~oH@gLpntgYAd;kivt1#426p^m|L-S;RAN~_RiqHP?==_V(qrXS+TLiyFBWQ#w
+bH*>3G7ol19W7w`v3w$Rz5G&Zjr*jrJdO4=8NmQp)g`lLPOo_3_1X#aGW6){wHxZCsTA(D8^qAij$YM
+i-thIRwIYQg)&*}`MX^{Y@J1zm0c<vJN&Uoj8mWvgc}XfN{DE(!=8p<1so>b3z+b0LA`Gn<EGMwSq}4
+St^FakyJOy=Iz29^A8t7qKAe=pq?Il%C>lN=(>Lu8#U|8Z4?2AhJ`IZ`)>^Z6LmvSCcuxb`fY~hFtqT
+0k~=dhjL9esWD=G!CKSw~gLM=nBO__TRxVS?jM(<M?3=`a%5EGrO>7X?@JC)9`^$AZDef@8*l1<pmUZ
+NRdI#Q+%)fw5KJqxRl#jz~%y40ZRiYS}UcV-!pkz)(38<WTnfeWC&+e2yS;XD^WFfH1@t9{w&Byw}s3
+FRELx0T95bZ!}AjmZR-_?r1v$qZu7hj<)-`qwV&OW@!gB+V1C4a+fG6`J&s5_z=$OhW!X8I3B~=9Bh;
+%EO;4v;72HSc07^ewL5!yE*KIWUhEe}di6tnYC&}*mg|OyJVPH2DKk^S`;zIAyH{oYBWzD4B^rT}m}H
+aeBs<DB4N{vH!<SXIkuox{cG>;_pY*~qYORK~n>=Ov16<Gt%hDT0Y9T=_i^Wt&><jbPuJsoTH)8+oEb
+Pz{d|$1E3nDLthYfK<FXFFY^1nj=p%9m5S?)NyPH#C0n)!7FY9TrtpN`R2M2<)J7y8N8j7{(@bb*Poi
+@LhuC6kJ+lw%jGmVx?u)3Cd|y(Z^oTUABlK-j_c1{s*%R(a;U*#$5kc%y+LI2^6W+ZLvhW)-Q;Rv(;I
+nq}zNso_}TDY`+$I=~?41Ysq=Q#7Y-E!YcN=s$PuKY{9o;6$T&wJNeQY5_ZFmX`OdS@JpT&$(n^TYL<
+-Th<E>xpDhpXzrm|cspLWbL=*|p*Fjb-~jam85)($)6?T!5_iDmKT(TF?PrB>w8sKQl4y&rP<$^8Ch^
+Sb)@i*|=Fy&KPet01mDsI9rj=?h%&R@i(h_en%i7ut8DG(jvbgqa>r1@51`8}93vACZ$Hd!Im}Gm#IJ
+2K8!rs~o8f?+RSZR9!JJxz{;P|$jXj@2oZm(<FMRQ}%?FHO1Q6??CWL47$uEi(XRLbg0GBi#0dhEbxD
+J>t}tYiL(R?^#TT7_)Fs6m@3@!AGWr4HPsFdg^$+zh5AR~F@7(9CpO+l{(2wkPXR%^{f86AfiWcE@g3
+Ey%RAw_Vuk)gxue#l1b4WpAQECXLBG-{>_fM|PXDbkd65`Uv6ygwcz`=Le@V+hO9_t-j~R<pVG*r7Sx
+M!cmK5+S$$Y+(+oMJ~_2s+3ptCk1^!%wB41q8*>w1$EI$s4o`KU*dCJ}=TR@)2NBYq2it`})H;3l_SM
+WLnLRqX8D_6$uV+W6C+ZN0Es2Ql-yI!3fAhiyVcb%#->Jvpg+N{(o}7pakm>@5kMZHl!&kGDgO?p=LU
+i_*gUO%M$={w%emgz?!uSmE#*S{@;NaHgFg_S7jd_gb5?y5C<9#wJjir1nPCit8P`K1Ns2X%F1#N}yq
+V8eDNqKnbfFlhCOxLkGLQ|$*O<=a^Qd|Trv~iug{cQ!~B`8@J6^uxLQ4vw*0BRcb3e?I9RhM#-7R&3j
+TrGGFng{fXrjZ3_ex2f_Cti`_WnRtU=$r!{LK9<nod`NqG>N8+9b{RK^G)%335K!2G)w4(H)*!{DX(V
+P1&5X4&cKBAKGO?0ZB`dCj54M-#<J25l2B7cY>9LnF=U)+G#WX2%HwA8xQW<f<}S6le0{~Q()qp8Ph!
+DeX4UcXzn~_@PN~oeuX(}KhRem)m-AY$SNHL>JgBF}aK=}YjQlM;8%-H1-}6+creMLfY1!r=tc7Y#xY
+0To%O3Rjw7y#5Wq!kASk2P-B_`|%sPETL#?&2uC4Jexxh=SAiNC`{pRlVORC)1kQsg&WZZ9#g?h5KOn
+wc=H0<cJzOIX||$kZup2M`EWchySVkIP1yE$AVww4$+8&QIUGc*C-4P7|bQ$YJO@7Re|*GU#_oS4E3<
+_m*))C3%^*NfI}_xEvD+_R(=OW^%gnF9}rq<vE!oe*2LeBXV6tX9!k9)WFU`{H(wNBS#VYJ=^W*=wyS
+P-Y>bdyBsJqH8oY-hy265ST(RTmX`r1HBF=w4pi-r?i;P4(ABZ=f0?erp~uU4PVW&@w}o7oeMtd(*2G
+TI;e;LC*+~S_EXH;QWpWd!Ij-+bsK>H>2a2N3Fcj%>mW@Y#1JDtZu5}WR3=0L{V^50-?1eb<?V59xR*
+uKj?qH3TP-KO8E?HJ{l@wLzj<mg~;`Nj|#vs}D*yHAh(iU#i!yW`rJ#=;IZ9x*&Gw=vUXc`LvZ1ryf{
+e9GIFpUJGTG`MtUJPMF#7N}vAm|2y2M8%n?$|vZ+WOvQ0)Xx=kAY`)1E;VM21*L;#n47o?|f<~Z`&Gj
+#^S&)-CbMNp0w$kW4Eu3gwr~R1-dOH0@jU8&~2c_F#brw<3HwD#j`d_qS3Ud?sy%Kx^uN$0Gfl;L2i(
+i(ljBKSdX=(r{giF9y_{ba6XpUQyPGB<6GDW*J@IL=9yp^P5wcm)Wg?ytc%b<e!!bMpTB_sY1`I)?BO
+hoCR=bK*#FGsT)~_Ntx!F=$D-Tv9DS=}nB?QFZGoY40DuLz1U9;lNinuxjv4NH=aDr@Y>hES7W>helU
+}+SGyZ&n){H-gmm1`QwPt(Of*L;&R`{FF$AZk-L_9Zhv1Xh%j+E}KDGY2~OqnGnod+Y!j2;E*ijC%`a
+?aYSoh|L)xPbkY4`KSL<C)E9#-rojtUew5opnz$p~E>5cMYRxO0kNhS2!zoxda47yy+C!vy2sRkU?l8
+av3+bFXP|_4uh^<u{3QjbFyOG_Ls})yr>#(>jS2{eL#Q0M1@}LOm)#*!Cbu%TB!hq6~7*R13;Z=uj@p
+)f6mQ@+GgKg^OB|m#G~L=;b=Gay6Cf(wg^!J#IC$&95_Tszr#lI@(U30uWh}XQm3cOV1DmyiJ7={ARb
+==K~Z!!USmmlq6i*F7_upDyhmPJ4+GoNK_;~T)~j-QgPyf3KrN=fr_B;Hoj4kUJxavXcgv#rxd%$?PK
+1>_TpAZ|4&pULB59~2QNlElOhTajq0QB|nX4fI`^COFM8hNq;@)B5b-K6EYF&{6^7$rTCM$7K7&>0Qr
+SN2D`>7D+N`u*0#N2&?h*4@h$ru;mPmnkkG{+_Y>G2R^m`Gwu4`4(X@(1*ZqqS>97&}tI55f_rsV}2C
+u@id7XtN`*M}aBr`8rz?G;Aw61A{fr^xxgV@MpaU8qh2?bsUS$UKh#ru-T`7t@1LKTi2I4!+59&f+2C
+J8)MLr?Im3!o5U|ht0(={1Zw|1@{u)iO^K{w32w<x9~lG8rBaV@boYth;uBY>tFTLlf!JvkY@Ay9h@L
+#Txl8M-#@$~hybYi7xaH85Rx-++!8`Q#1nxbtt$<}IxL<NO&GL$(7|3t-9$jeCfAM|~6=2n0+?>CEk6
+{7t-{bJtRc72)(CD6gMnzSjE(~T(O=G@)Pn!|CokKU&Mw=$TDmlI((DKz(gPMUn8LPLlk2RqRaC#q`S
+gH{=kuUAJeU0%~lpm5+cmWRLXMXKEB9Ut-<sogwu@2*4-pRdOW|)r#mw}lA6Pw!WU|yoph3AfimZzb}
+@F)AKTFa6iK0=TIH5tRYTqcr3;Latox$w5dL!=}fsW4|TP3cD9u0vo6OXRw}VDZtMj0wz`?wy;cGee5
+-!U<BNDK?1%B2bV?a{1ZKx$R)<Dis70CaR(KgS&MJAvnDsn%oIq@LeBR>69DvJpn^J2YS{O>9c4#w;s
+DqA7Xde6N=-o^Fg~MP!8*hw-IudGbs@uBE0)^sT+=2jHl(8k-j(@`7G=Au=CskbW=*++3gftE@LTIwZ
+ydaQCI&9tCSkvxyz7o$V1be^FLwM_{#(5Wv{Zx)|=+dtVD5^y{zh3CGLHDaC~%l^i^kgbsKN-l5#4zh
+nM!Mn}osGa<_Xrk0!8pb#4^$L6gK?_pn-kH-CJ7BP1~0*@?pp4-)cO1bazAClCi>CDb$_0nh9BR`J;H
+qS4vYkQ%0bB)LHrltAIQw;ldOkLx}5xK?u(9u=W4!sAgOI^O=VmkA_C_kvN-;^pCYuV*6HujbgiQ5Q{
+Paz7!FC?)t09S*aXs~e!O;cE5J6r>XHKHiQeBDy_rwDnN4LonrpQrB-AlP*j_u`hip28r?W&_dZE|B^
+Hax^|tA2T#oDV`Er-SE5QBQvr{4R}XO<MdUeFdcWhmb<R@hmfu$a7Vfe7)#sk1-C0Wu$HvqnkGk81wN
+qI6#8HjZ*zWa$&z{nyDVsfX(cGo5DI6L^kX$`<H%6anx8Vr3qb9tE{A&>Lv~wklKhmrkI4U~Jw%*E<W
+2rO<0PCBT;e#1}w%I9&+Ncpl5KNrWx?yk}IB;lZp6#5Ef=dtWw|W+3!1mvKT^_8w*xX`iXIq{3fsyrY
+rbArrXB=(bhP*=0>8Q^yT`yUVF&zsSHZ9vJ8k_<EKBs#OQl-U>Q+{bSe8o`7LGAY&6>$VQgNz2u9_&!
+sX5W4H9c!*JT3YKQ+yv3Y5^eHY-C<aFmBKiNO0wv|m`Twu_<Zarc*ss@MhU-BH#Kv#hiK@E3K#b(JHE
+TZ1j3SF{*p>$6klQo_k@r%rg#t07M-hS=PoF5GZ3j3Ua<$f_Qy+qigzMcifr0pd?)bVsVxB`jQZ#k${
+_W1_0J`S)If25nk6Yn3i+YrTsUwAN64ZkPo49m&(%oYYBvXuh#_{9$1c|KxlU1cybi$io~qUaRg`u5_
+|KwMJ85u%7nC#SyK$Q_xgx-HE=1y89D8=hI6T+UMw(OH)r&XkwN4#=;Mkuvw2BuDtg3c@hBO)caZZmb
+b3H;)Vj)Hd-@@P@W|;={V8zyI*G1pf6>Kp97jY)VSj0m*K2eLkhqnqce_NruQXM?#q?e5c0s!Q#Ose7
+qEsx<#;r#}=+?cv0LYV}q2V!8=h+B<SnZatztC~+J7-gQqi>AFVtY9@@6slZv$*%90aE475`3HbPP2+
+sZr%Wek*p0Q+8-+cM8=Yn{@GRM(oG;g|js3KkaUd<G!`iV(kWm!{ytfvpUI>i|t5y-#UK^9N;?xlE+N
+GU}0(~tKT{yr=Wbs-TrT~eqw9;*3r{i=(!@F&7tWpR%4BczBv;yUg@2=Zw?j!_Q=>a~5c1ldQ2!hqxK
+11-pbHX@vrm__TN6FHfr+iBi&kbw)F(s6A?|w^5Awkyw%7bOc%WZVn`U&s&V{qQ}_JN?7o2KJsLIebS
+?4|1js)7vn<5oF{KwXkhUpGO?PfQ8*=_Eh-i6MO>>GYm%&5=<1MNi3&b*;_L^isn*J>3&(Eqm_vt2Up
+(q7n?=;h<vYBti9_=YxD5>d<eKcEOqJ%aw4kuhbmuLC!m-=9>Mp)4P9mdiKxKN;}diNQ2z(R%aUITQ~
+pd^SEN?AI24%2`}aNfyPFENwZ8P3Q{hG)MV^OZqVif*H6VPMe=$Rvp}?!^8{47Cyes5D9>39+jxZ4`e
+mndRxtl8pb*+O^kExK5Tvl^=VlF@b>#9n`(ocAlgbKzZ4x!)6H4ZwENCP*-4s6oU9(1igQZH+b!8{QU
+alEAz8?}~;zZ)&fHDlXQ;0;~2NQ>dZcFcpW=?8}xE|@j^Gsi~_kg!NHsa#52adSK*3d@l!6m@P%fK{M
+==1L%H;n%G-=y)2O_h*hlO$7Bh*FF>C6Dkr8;M$4IaIG_rw1<%P7f$S>*(M$CgVlRdp!B3SOqllN;P^
+JR`l<w?VED)tKd~m6+G>-sbCQ_l*k|d&&iu3A(M1>s$r<p=E=E5l~Uw`F{}c+tJ5W#B5w8<!BKuZfRo
+R!VF#BPrqrMT0t+w^wp6G7>QqmLe$iA>7U=7P)8~Jh9d|Mn|MEljMf}4Q{yTc|OI2b4iP+cv?CIpU2a
+`Xa$MTnCIyrx$9*+L-%Ls+o^iV?b2M-h~#fB&EUcWv#{=R3_=}$Z3-KWp~^P|GBJ3Ce1LcINIb~HOaI
+DLa)qD++HWbxx>ge+(mb``J=j$UBn?~|`*-=F9j0{Ek91H{$|WYq?69-Cx-uqkLsqp^JdQD$Qvy_kKc
+1`%eJz7LXC2$N8lWpOZJ&#)-4F&tZmDbs@~>%uZLjVaNDAvqR=B=d{e+ZiBz^!$5+wx~#p8J;@Rowb;
+3r2o2D<wa)x42>p-<hQ1o(kF<o&0{-Hd;pXzy#N|^d7IZ&Nk@a4#>w|brw899-yFO;q?PV0a-dj5+_!
+zku;})xD+aN*%-H6D9?tQ>hPzl?)!8DUE|Srbq$w((MM4Xfs8%dWRK-}~Vv5}3>fb(fQ6|xbG7l4J4R
+4;4xGk$NFSCo^;eW_Ag9V&<?0AXS<_&r=y#^`r|6x4^2}I=;j#(&l!<&q3?KG0PXX`4s^<kt**vVN|Y
+|%k*e-KYKy?7`REVM9^w8#fOq9S7%!>U!*#sx-2HLOqj6a&co(LhXrWVEpJ0(;7&$UuL*OqN$&;mgu(
+b_m-p2SPL|>AkEl#kye=^gCW){h*Bd(J;<J{}h~9NU%}2u5b&hT+tN=!#r)NRt)vi=c?b~V~1=ZV^)_
+k($|!ToK-Tt$D=GKiT_M9j&bv`7Z|U8f}6>s7pjF*JnR}m!3Se{cw5i_>feQOlYN7FTF-XEpKV}@mhY
+|Ij4Z1-Nsk^Vc?s$!Xp|HPYzAd<j#t^KU){b3j?gycRj9aG3j($r)Gh#2p{?<;>Vg%Xtf>yHO_!=^--
+Mdgd`=f|DBHo1rDP!{mjYEHj_}9$>Mkw&poq}2iqZ_zD_FyUW$u$}>hM$L{sq5mc8}8;XILO=R4k!Lj
+ev*?P*!D4NsL4_9(l|6Zn?2-546%Blx2$L8t4KGRReLN({+}4i8mwWCH^J5;syXUOd?|GPqr#Z>TkEE
+Oy|NZaur1C5-um(?Px2E&`nRqYT~G(J#8Efv!{(va|t)EL`@ehQJ%jK65T3Md6M3tNOe_jk>u7}Tz}X
+Gy|0a4Pslnogt~|})vBIzS-&anR!S!It3#%)UK}blZT0)86w1Pyslhvtr%ZXoTJ6N}b|WhV>+6a;)1F
+R|wMDEWLGbJP-WFf`B{iidk!P+1FK$qIy`j%*i1*|#>7M<zYzdx+#ZA9zfIq;WBF2nvVo964v7-6$oE
+4AaheSDPs5X9dJ6zmslI^E1l|KO~w^C_rxO;5&!PxAmv9o>Fid|<n^u{STlP9g>%FW++04cXcQN^^N>
+s=W)HhasS{pPnlA|BJPV0&0)q}E_5I)pVNe;TG}S`$!@@CS9${+kB5K<HIAl4p?a8r4i7AnwMd*2?x!
+RsE2Xtp~N`+2FXDH)IC;J1_d?z1jSGW;MheW*$RVIAN3(&;Jup&M#FD{056;v;I*u^gwW_wWWnD?nXY
+zuEUa;N>)2?`{if92<t%UF3G_5_W~b9TkFu$#(-Tz#L!t=y`5s+<}9H>J$F<muLCH<>vF6d8KPo$xM%
+RxiKWW|M3EgmrdG&+4MfagRSq%!l)}H_9tM)O+@eRuI&pu_myse76OU<qr3{JJkwLxgy}q9a%?bdh3W
+?AagIJ1B(9r!KWQuMnBB!`YFzK&jI0{|~?Z$s?{U-TG>o&wBU;*pz(Q&~egRZBu;^y2FLip1aL_e=?u
+}-rv(nMVdhcN6Zov^V6(K+vd8>OGsIrhiwr;ojw*9HfBcIKVo;R!ip-|X-fC!mdZ=j;w6FVO0xY(I4e
+$23gwz%e&D^z^PAkQoE#ltooj>N&Ki503iBAEm*|(0`Gy*{3B8Wbp-HQK)bj*06#UNcfZp42v2^Iy`X
+m^=(^f_(yfD=mQ{F$th5)tGDWtxZ!rLu*Vb`J?a}$U5j;xdboCU)`=WDAK<=_mgH<86Z~^#<#D5;5<P
+D8U2ET*-7tE!i^QNIL{uzsGpM5dQDKFswlcHbqoIFZ50Y|_f&szMrKH7ShHBxoxSoKZwi%00gEsOCj+
+eJx4bzSj{u1MiMUe*dexmQxEv+WXM9{Y`FwQ}KVpMg<&%C5<-dx_3_Z1X1@%aKcpsc%9QH}LOYdpbw%
+d}1x0^vP&K)xpg-&BB04(IFyUA3UFILP!!1K0!C7#*S7w)hDkdGm}|S(tbjh-NkldIA_w0EENZwHI6^
+w(zF4i#sSN<t>2dnY{HeA&%=Pzrq_@jm`EcQEij`*b$DBB>UF4Vq^IXP&|{><)-41A2*kYZHFrsI$DG
+iG*bIB6JL|D2w`6Jc}G?yFp8?$96n%5Fh?>J{LWXE`e`wMVE^GlS1p2E6T?&!V5{QZURv|A-$OEt@G#
+OYfK0n=@KbHVICC~*XTGxM`e{<L3=3bUn7N>L`B>&NSu^g1B^lgcX{~#|yF!0NYuI05XrRA{9E*M=--
+7Dv;Oyl=L27Uog9@wt`{zF2OUow~HxXzFWc(KY!P|q7dXI=kdyktvJk>BRFMu95>la2*yi*@l>ha2KG
+yJyld{tuv1DFIwF{Tg7<gL1x19C0$603BB<~QdO=^kKMyXS&a;XNERGq-UAoJJBM;97j#m|GJ;2lx2z
+Uk{aj<Q{SE;U+oslMQz~7Lj$<>Iklah4&IDo$C7ss#tbzES63bt^De=hKju?SSRAckXUO%hmI1$%Kg~
+5d#jDVcS66EerluIRkOTlJW$p8l-XD;Dl2Jq3v7jN@OA8wzQlKslrII_d-q@?Oaur<QiLY7_G5`&DzS
+Uc>i~DlAE;c3ZtP;XsF7=`%9#bvdsP>Iy`RuR*7@L|O+xWHm{%+_fvr?<MEqw;Dt725i$1CK3=$~puc
+KNmPU5W$!_IZVwoMs3lp*f;twk2Sumd}5Vx=dj-u`lQYmX@6?CDhi(wgCk&}#*)hxL%VVv}GQFO#{G$
+oWa4S5i=daY;C!N3RoqCRVqvl!JRSV4nb;xui`4(%y|TQ8~5N+2En*26cU6FxN&wTqby{SY%z^v?)t%
+wkdDMFs5AvS66~t7o%cjgl0oumS*nfzyhKTM+3Bj0_~>bmF#g!mN%^9ynKKeLY;5CS7I5@O7d0%erGp
+EqTk$!XX1FVzMX9=Q&h?h@jr{APIU(A@06LRv5cb-$Go{S<B5=+IFR({#O?%hvO*s<8+$Q4BUkQLAU?
+~R@OO0aTbgRsXd@tXs+)74ybcY|ezL!6Ci{H!iQqU{h?CV5vm-@7y_5TD(i7e1fers^_SM02jKhBWR(
+uf3PZ+R2CP>eFKPX6Vdq0IoZ+kxzNN?AEDv(;aVM6{l^*&m%tgU(^ne?U2d4qBGZH;=K{CCuqv&w?#`
+ht-=gry`p25c=9%_yQB<JfOj-dc?YZPQu?ym8wq^jaZiRDpt;_xc=AY~$EUgcE*4*XBTpX_{qm{V7gy
+ZG=btm!tXRIwV|3lEC}y(c|V3DqQqn5PW&N#+={G>UZ%#FENo(R)c_1)R()5!y(oOS~vp%+zpl|`kho
+d<62|USI6nc)~ed#obTdeRnF-y_L6_TcOp&AQ02nC=W`@$i3L<|v;pACqFw_)$l@||OiH;~k&uNJC~g
+h1nEl(`@-(oM|MP`F#)+%6?BLx#Ae~Gh3C51{^a}1wLmn0-`#X`EbOO+a3FDv}RCViSnCf2jj^8*Gsc
+LuGHC;ps>YgmYE6a~GE38E<K4%6RXk!f!R4whI^u1X*g2zPdlTML#QrrfzaNOjZUpQanCbY=hGEv=eH
+2VN}7x(I`aP(y^WO@5LTbyS+>n7j?@;xw4AfV!}z4Q~$6Hi@r8iwVB-_;y=p7|9{G0`C8hp+-43J4eB
+-vBG8n!iev+5mL$3VE1_h5}&+RzJ2q0!ohBn=nHe0NHA>j58|>4D_8<&6Bj5=Q*+K7=#tN;P-cjj8xo
+k6xFit8_r>@DZR%2?t=Br1(i`UkSOG0|H*iVaM!#2lj;Jjzqy=xa2<$47PBqqd+&oCGs1TYr^Q`*?|t
+$Qe@GpKBZ2(!Hk6(VsleTn=q|3Noa-QZ5nf7-5VZ+5xu|)1)8%a0uQ#qq!~Mzm2D44<PO4Ow#?!n+bs
+~N`mcTY)>>96fg|;m^IoBWT@AM|T;x6WPKkH0`?oJq7IOX3i#ahY|jZs`yqE4;RU(GjF?U(>+4ULth)
+IAqXB>@UA7%nHURyo8Ut+Uu}6<y+8MJoI+P)h>@6aWAK2mtey7DsQxo_Tr;004I>001BW003}la4%nW
+Wo~3|axY|Qb98KJVlQ%Kb8mHWV`XzLaCxm-dym_=5&z$xg3aP!`RY|ScP&sDU9?G>92Q8ELmmZM!%%3
+8wwaYBL8N!rIox;e%<v&nlD*!$8bMZ%nc;ArKa#p&m)~EqRh7$fGh=(bzWggb8C{Gn*sH2OG;*`mEO|
+9yKU_Wkk^Nhi{J=i)ud+^o;;meXQi+`Dis`LjKOgkAD%r<st?zjw*qdr!=3Gm71{b1X9EK`&BbR$!!Q
+G}2Vkb(CvOWsI-rl@={qE!IR6l6O%bdM>|L)U=o0p$Iz5nnL9|GNI-Bdf4W$V4(HzLcJ+|^a1LB51ph
+^&xWG`vuwk$th0O02ZIla;$Fs?7!_?QSdOuIk&(UdzJWs%><4@O1i63xD%X*5+zt6il67|CCA>a{2mU
+C2BN;eT_05$%?PGBG2lkszsv@Q??O0qlej&D=}p?*V{~2_E|2QDSL&NqY+L5%zEu+lLG5odX{8a$#-C
+&$!IhpMxc6Em4d<Vf~^|CH8}2mD@s-hk&7JZ3a%9UAXK$)R)WSz=>N%VgnYSJgXv`{b(SSc6zeHN4X;
+tICBdIu(_<zTjo7|0V}FKMB}B!YU<zWP++g_dyGrK3q4q*Ch?gJIhZ$4C$J7PH-=DA2NE8in4X#VkLh
+08^mPu^8WHQAul5u0_bV={VlZh9k_F(lSnltdg+$O1?DvAO-Iul5hDa{LEP2gl3M+J6x_UxIzzEPAR#
+uLQN){p}DYy*}ky_CO?JkPA=4d3}UGxiCnXXK>W7pL(TwiIYX6Gds48C5cjc*UwU4e3NOS+;5gyp4eD
+DuJ)r3v_3zI{1{dPX}jtVFDAx-tj`()CU&Mw!Fz1g!r-$J2ty)?Qe#nky;Cb7dys1V%DDoRElsqwi1!
+D(-WqAF<?kp!qm#updqXQF{jQnVyI@_h_!q$0Rj>0IMcPnvY56(E6a72_&J9eWkd|;6fo=y4T4d=e<K
+QYj4nzUG<0ez4PBa%u;4(cNzC2E26rS5UXwjqdG<+S28yX+4rQf1sZ=Q4GE8DU@dtD}ek*`Ld8$+L!~
+;}X<wowyGdXu{0xf*eH&aMX7V!z+N%gKiMAVmc!R{MqOB5u_gT?~aiJ&lQp_Z>Gae8v@C1!9fN;}Ib8
+{aR-6UG%=_eaIwI;D}l-YE^=OFkp6*8pB(1+A7}p?@B4049W|tm8(qn|TLTG#Bwp8#?%>H*ylHV5Pcz
+dch2BBOo*tpa-me9Lh|2S@YE`--w_htNm(=_TdbQ5eGe>azQdf7D+|YFoepg+3}({*aDy>wZ`Rv?G<Q
+`umbwrUb6;#4iDf3C*E!ac|)`O0!>xIs5@SQE-G?#8qEa43gHl!B^l`rz!uTQ%uARIpr~Ad20I8hxs(
+8I4sDU-7?se+cjSo^1u_aXGKS$x+xlotLgwJiK@p5gOhP6h&z5(x+T(P%#(NVwD6ACsR$yCtAds1syC
+db$hFuUNR06}<7}fsK!A5@W=*SFnusionQ7w64GAS&dzI*knr$a|E?&&3oDetZ`J*FTfT35Lv^A2bqk
+*ef3ai-9gv=8iD&<&+oCnyO4oDq;mRd#`Qt}#0FJ>^Z3#bwFGmZP!th7cA;3c_|R+V-#7JRI5h0E)CO
+xCWM83RR%d3d?OW{>tz8I4o?pvaVsG;z7gO2g<YpD<$K!KCH%2kl}Ade@W}HiK4j0oFgGXo1J>QtMYx
+(tF-1R$T1i3%cA0@41;~S1~_bpV5}@N@3IU=CKMlYb`Z8}?*nouAyLX0!F8)V&%f@KZd0`fl5H5cA)o
+>jL~I&tD+udQD8~6R>olGVfZDK#U>AS6n$gMzix_o^1{NH+Xv6Mnz;NkS7qHq01QZfVo>M5JWD<2)Or
+&y9F^SZenzWzHj2yMC_C*fEKx^4@@OjBPV;Mu&VB+QYPvaBF%@Bn&HHK(_B}su))sT{4p?VS%rl2sSW
+pXub9UE4-Azb)9arWvU@kf4jXs>k^=HFTFwy3S+><%72x)QL$FmT)VB)F9osB$c1EGjn>YvU{?5B-l_
+(omE1&J-hyqHZVrij{{1LD_}uf|6D>harKH(0(Bwn&25Iw<qy&L53x@Hj5S<9W)twGidFnJn6t~>qsa
+l&2rp{jkaM93X5=ZPt*Whwxh4H`s7JxFyBZ|DsJN$7>GU^xFay`aD!3_-W5f4FY;vF3oRNkS+hS}W95
+amL8+3BY0nH|^T<*_l)33?6mk77G#N;Boy^y!jp8a7W?4YK;=(PSAjWa4?;_qSSsjVeBUm6#g&{*m6q
+vZsjpS-4pp@rHw7v`#!F@qsc%&A1oP6CPlM|Jo-2*V#xsNV&l~*8a?kE@0TUaU-f@lVl)2V_4X<gOH)
+ucyukOG>FBksKpluScs3@;eng5I;x@@z1kASj4fwtH8p5A9*;Rv$DJ4Ro9ef3U8|!bRdSy5Z7xL=v=6
+)7|Ec>Dc)HqHk6ATY-b}C>Wfu<2<<BK(;gN?SAB7f(Us+yvL<KK3x+LB*w$%|H`_9pyS&!khFI$x;*_
+RYr@tlUelh1SZT|(r%`X))iQcM-oTvhd}<o#n%bu^aJ73_1wgKA_MH8Z!Bt`@TY(pV|9(EOyb`J$Yrt
+qAoHN3*u&@P$i+Ig%?W2ZRVGq?F7K6wD^^t}B9UQ;qilA|zA>v~aIi7Suoq-TIT0AH7@FX4#S-pyB2A
+6GZG$3kj=e>wcgf@S`adBz}B3X&Q1;59j>jVG{fWy4ws@Wp6BoWFooazyb6>aGaVl0w{zN29;pD(a?L
+Z<+r+)t%ILkJv06$?bXPRB~A`IEE;9oiYx&EW*}YI)~!5iULZf&`r+u^S|Q-89ueQSoAFH>nF+&D)sE
+oFS#TSnfB;m^{bU9L$~1*mn*58=E;zqX@Oz!Y9l=B=^2*Zl5Oi1g1P^g<yBhPgjiYt~>>aD;idzAU0j
+Z2YdQYf~O(au^J&4Zcv`Yjb~zLS*0&gft&XxEL)8{&yx`DpmmOCZUp5hYv;(ZnE$wVG7Ns=&`(Kplik
+<d^sY_EAje1P7o@8W_6DC)3Jlhh>~AE#rJMB1m0w>TEerj`UqW!)#yz52T;_FbuXbK3&!Jq~-2-EiDS
+JMdUo9pK+azsa$lXrlxHoQ~MI+)93W?@?{#fG1=>GrEA%2ImpW?h;4S#C=ex!CEsoNbZW(JNy?rdNkM
+vI?ch!*#?5_1gS*(sgU`xYStaAwv=g`;0bBR^I9eL}Wws&Asq%xxi;W)ap=W!@u8KU%?scuQq&5o|QP
+X-|aCkc2V}$AAoL+2n@E$+lDWLYvP_1l`-{ssRZDA~xI7I2ep-22UJRv%26ekmKs<N5(b`lZ8LsGCZx
+1+_AzhzuF<~NcNDb2e`@6!C7{Iyg9Q>25bAHeR^gNr^AEknAja*_fNdtd6nDD^j{=I0{m&!+C4{97R_
+?jFO$*xxn(rj8$*RaES5b4d<SLsvpaw<vk?JH17}44-fw^h-0`qMX&KnRrf+QeyWk#&4j#L6s2aU5e3
+J2h%mM+Yv!U-3B3JrBPp;wYZ%mapih*&f-EZ3152H13l#6aU;R+)>GDgJwB%^#T`I5Fe%=vZ{-+%?#c
+dX}AA57t>y;b3Gq#4+NMbk)%&$+XFzuO%$A2jqWSNboy{e*L;kA^Z!meG+$ki!`$owcVFPt0V5yyl45
+9uWAGZ2Ns2?HAa?*}>0veotmQZl`X4r}_edw<ur%TpmpDZ>#E7(PwFjx!z$uwP}x8?N@VzHXTjS0ez&
+>IUDAhBg)!y)t20UIB;fk`pBI13qs`U&g;}2Ty@Vr?0)gMTVT>QvFNKi9)8x)iPtDn;|Tm8P)h>@6aW
+AK2mtey7Dtv^Z6!Pr003t<0015U003}la4%nWWo~3|axY|Qb98KJVlQ)Ja%pgMb1rastvhRT+c=Wn^(
+zqjTqGwFoyYFfoNJUhPsXm&mM3w=&TRIQ91Ef#3o%7<0noOl_Q!8G0FnS7vS)9YR3Z_LMx)=|fDXy*y
+BSG~70+)@NTt@ZAMnlOaB@iA7UfRxn~fsj+lah+@$&Ty{`o8UjpuYnuIM*j#sGG~Q<h7%BB~&2!^q`M
+ZHk;+6>D`*1taH0m9MDc@D4sOK`5-3E5VnQD&X6VU~J2Bg}AO5BNvOeXYa1gV)dX1%~#~@`*+tL7C(Q
+ye*fVLUjp1@Es8DS1>xJW5Q_Y~Q%o-2PwdyMxVeG-)L#o(e-f<zB$cRA)qLB@iNP7C#kQo1v;#ovn%_
+*xiYb<=gyn`=Q}VyOJm(p+^8}McB~m8qscVj#wWF2dSv?TqtoJ1o6t+JlHw=(I;0WMWVmC$Z+%fnHbZ
+n**(tpepmR5=`Gd3kWPwrTL7fmL(bKvm2W<d;~7y4b8Bstx(Bmt=8m$UQJj~Ca;ucz-8=Vw>fFd+zn$
+&WuhY%>CDOAyxl;AQ;cfYke)AAGz%pZ#zEoCBfItjO8?V8`UaPj4naQYqPXne7P7&*l7}68VWtH*8Dg
+Y|B$oNKl3uu=a$?ZG89gfNW{b*G#J4+{NBZNP}vz0&Z}%^T@$J2NP0?0vIg|aVpXcN3m1_sQL86_b`a
+NoF5!X_^5Uze7xuR>sJTIZzh2K*=$A(=Jif1D=o{6r$##^a8%%ug8j4NLMx?=$!vynfxbw!$k)Xa_xM
+`xrbdaM5i9JNtu6(>0~NSo(tCZTB?^RGusfEK4F8=UP`SwOid!ZQNX1W6-1q!oO*6@GOXSDn5wyqd(#
+H+;Agtq>#cx37F&F|bLr{uC0-H&1318P1%q2-_mVsr%7i89&3klZ6b4KpD+Q2&i0o?`NM-ffV7ay*!l
+Z(Z>v*g1W=#*gEoPels8T{Yjv-rDGW4{0C^NZPk$6vn-Ka(%&Yy4gG(|-lg<m&B*#pQK!efI0+#p(5#
+QOd&+2L)u-8cpT^nIu~Zc8~-o+G1M!oubw2cPj>S&`z^E7H*4Gg|@S<@-#YWtSm{)%p|c8Q8NVJfh52
+fn-Tfh{QtFqD5+2qU=U|4L9tXA#6gPz)~+%I0AQ6=DS=B&&8)aA%J4<h!mhzJmSEXt_{4JqJv`<GpmN
+s&qsT3&**`d!77y-&ikrL7FTNT{$!ZyjAe_k*6heK8%iaCIzQo{WP&iW%#nhb>O<GD}w}7JIY;aVwPU
+5_yP_tONd`K;m!iXG_ci>83qlyVoB+z3*C3&G?*+a@oMb7kpXrVB|P6*5?a&R`XDp6FR7`4W}nUh!X_
+ud|*0xvA$doTqg?MQz4k_Y7Ih_DVlFDLaB_5gwtX*5BfIGLmw7!0}6Z26UfC5FZF8~7ZnH3tU=`u|g6
+03iyd5I70QFBOa`Mhr(K(V@UZiRK+bfsLUbH|v)y<!kGaWbCbA<|K-3%M9_(NylO)mTPrn00_-oU>K^
+C+^{9hZ=_=l3C}s|x@6gUYH3SW9Kw<UK|`3)pXq9qWEAAJ0be}778M%^ODnNUM3v9a(Kmrzfz=n~9M^
+Zu3)hL;Wze3QFFsJaJ^-~}d?1f7fb+ilpum6>8KTMahtjNAE_KxO2M@_Y8BN=Q*d+i%nuGF{OrbYmWX
+Vy4Ik|j)wfGcwb_oP)pg9Q(8KbMxj;bsqe+VL!y!ZYay`uppJVOzG{{v`}E`FbwJ1RKMwQ~Zx!Vs-F_
+!R#mh&q6YksI2l`2b|P`ATTT963saqM;esZw^}NBqmm?+)Sm=b}Su@06%VDVy+SfHx&okv>+m#{<%EL
+Rp5KVRSue#1GNB!>5}!j?UIoPu0hv1Bx_(nDfj~ukB}KQG6ntw&z9;nz7;$V&B{H0+fFbHg6*M8L4yS
+i+=4qocaG6BG!l=rKPv^j+0qk|qw|1_r$MV3%w+<$n`J$QMm{%cM^Qh&fIAgi4A(#g25{?Lz{ffpgBV
+V&Z}Db_n59EZ#o@zeI_yV#m-FIF{pduYC|T|b1s(ai>;pTDT$dmvx(Z)KTJnd_f{tKxXb9h5oe6<y^y
+0n#5cQFooH&MBB!^HD2IrJ#rdmO=$Pq@VF@<pmGQPnV%$h$W-?ZZFQ~M50s1n-L8nKRo;&Vv;UWi*Nu
+!_=d{BKQmmKTcTjIB(DcER)N0ksVRgp7N5Y(|7iH|Y<q^+23Ke2A|!3N*({0W1<~7Y5b}10xN+RIpcm
+jv;t*%V4Q?EY6C12&hra@>OjWdV8371cTi82tN2;K;8}>8-QGvz~8?^)m7ZfF(HmnWk-YKN{Ztp&yS&
++Ld76QJ%esMo{grpw41UJ{I5z(Ix}CQpSA9-8wy$e&#KT-90RKB54Co*>z<5zqz<GH*x=WiYq<v*N(I
+hTXYThr%g7SGKy=F?4S`y{Shw_{q)@|}8aM`pHEuFlRy<pU(H_$L8a0f3ytZQ9i@|fZEfI(q--m(Cp@
+L|zNNyhM+Ko7UJmzBp(J>6pKApMIkO9x0N?dRG@l}3ay!&PGVSwa@N<(Kjj^eko4_Ax#?;hLsUNX#PB
+UkIJnm(p&<I#R7&QCnO{{IBR5o(4#@Q^#lLPKn6#=#a+Dvjglx{E;>=P(avjG$eZ*jh<&NGra|17+jv
+4)PZ-`{9r_(jA&y&ajZDVh3T!K?P|)AVI<$LXZY&;;1Ysc9=pmOUQDI?bh>po;+*&#~dnvKis8dkCX%
+Kopd~(vsv&Tj2!;;<q268xYY_dDYg599ZH@<mVM7irgx%x8`LcZwV}%7BgThfEL$<aph>7CR&=`3kB0
+^0#1FG@7-;ao6${Uen2R-8h$1%?BDm)aBp~V9FoxwC<yt^&lXXGqaxrw3R>!_H<AGu7?9<7)o)~C9iM
+4v``#lCX;8m85Pw6;AXM{YTlVw2K1>2<EauCNMM<UN%PCfT=00`8+4vTEnTvCC$Yb+A;B22uRoY2Zcc
+mF`?6wh2HD|GZaL$ocLhldBS+~StAm|Qy|-cXGi!_CH3D5s|clA^lom}=$dV+4Wb$hM`604sY1@oULI
+M6l>p;i*=;ZmVHfh&vec)>>8TrLh)OtPu?8n$1N&<{Za=(NH=W?R0G@x}M)?R+-U$*a%#Pg-0T)lRTe
+vV;ee}8Q1}DWMzl_Ic*_2-=!q2qyhwWwn9QQU+PTnGd-IAQGPn>Hw&qvh<)#52cCgRnf>_O8Z0I{lBk
+h%Yqlqe<7py!qF93AiCS7i>_99g%p0_A0vYv6N(<6WKkH`DgKAPx;F;q?>gl=}(P<qWL8MBR?bFTm%(
+xT`qUobah5KL+&(4<B8fM#oUw8MhE!aJ4X!n8Js3F^;3Cj#;7fbZvZ7jyG2octhgWGM^KhpCZ*6BW=W
+u9FV(a0%jBw=~A#4{z+$FdN>YKJheoTk@Qrz3tpYM^`3%tnVNS!s4=zl;Vmn~1wBc080rGSXso^?9?*
+a}Q)slB=sTWg1DlfeNp?W37wz%P>&g{z-3vCm`uIVWfh91i`mH<_rB{of+A~xX{2(2_nHR!|&_<^1rW
+5I~ZWY=G-8L?fSC!SNWZn$%-n9H0w1mr@lka(IKZJi)OFS2gOpOZM*4^{ArDW+(7G{Pk&J<qfedk$Zw
+e%rH;Ci9akWHAUR^)P8xa_yJWvHg1X}6@yNr0n%$>6?~S)(!I9L3KiI8(*#jPo3!3jv`_jlafl3}yZ2
+hj@#Xb?^(>-z+Uh9z9CK9fL{@hFS$xLVZ`s9461qqDrH)QY`?@j3)_();vCX|kZo6ZwpY==;pF>w0~m
+*Q#A-!VR`wIhIf#y%<FgEgbo752cTlA%E}q5=2-ykJ|X>uS)3pYOoG(G0smdiQJK0|6}^URk5jQJ|4(
+gYOVrr5K<3=V%E&R&*=2tUz$5C?cIY>&~((IFmm=L1e+xgf(gb4QpHu4?;do%ke4kNQ)p_rNVZRQD)T
+e8{jkS6Vs+AxO}55ZYb2@5yP70xzhrSO&y~i^(CmTwqMdn4(E+5@9Jvp)fzI~qoCn+yT*ET%gYjFt5M
+<y)FIPQ9(9`dU}K%<RbNB@9PO-Ii1i876a1z0x^;S1_}MP^wR472Cqyy!VDnWQAKEMn&&&KI%U*49vZ
+;}_s9UjOQQO+hSdR*#9tK+P6sbK<7731nL_UA*#X}tFGsof!Yy&s|yVNlj-}jmA(~B^pX<S>Yw^>i!M
+Yh0R`?%#%(@twpnR>#*<P-#^6c{DMT8~NGJ^yZyXbIV)LN5yqbQ{S#Q5;G{t$M6hp>G3-;nN>Q5i~7P
+tNS|{+dDyc521rS{>OiSe>S?n$?LC!T~d?VkDXg~yK5*YNT=^xT^{N#TPUZuqh;$?EG_Z9iltYpI0lI
+~5`2T~&2gxvt8bMq-a3cnwbJ!dPxt14dwtCk?C|iZ32d+6>tU*oC)(3}&z0A%EoXSbRByL-RzCCnSdJ
+)=Vd&A1Bnhi>rivM4=`3nW1m6*u+OutTAClATp6;YMVKi5#u&uSMR2?`m!>I&b7tHbM#7tDVxl=Hkd9
+4(x2w<9$dlo?W1@;h{rj@`Ve$CiwNzIk5d)*u}ojbq5X6W71-dbzt$?}FrfmrstTlRdlS1mUDwig3Nn
+k>B)(e3Xuhvqy@*Ge#<bN@12>+|9MsWiqtb{oFvMlAt;Rks=Eq@5b}Vgl}`1%CWA`AD9ilHega)BxL3
+?MxOBN}C;k&ODa-Y5tibn(Y>pSYPG{qbV6wHqFzH!r`-}D&3&by0`FR{WffO)ZBeo24BP%y!xQK!cmV
+Adg4HItbK!bYFR&iG}I{K8&Onc_)_0^!Fb*^V1u!zSObN-&d+48;C8KDJ35^#buGS&|B)2w&rgqPh`z
+F0o#Oer2!q=5pffY9v3_Xw#-x^ZW1~vdaixPA?(J^Wa`z=Hg9qM&x#+UFTZ6uCpKWRG3`EOu&h=*XC%
+LzSnqV@To)5&_&-qs;)*9b|#@UUwoo(;cUp@3crM+I!#Az!D9Sui1xgDWdO!gZps<MZsU3^obyH(xGc
+pZGeEDhG&UK+iAbx6+I8n@|k*(0>r0lR1$qQV|vY?LbH$?@@$t7VnmG8Gr%=J*bOR}f45UBR*B+cImr
+RmYu+H(tZg?CzH5tAU#~`ksHB(kzwycgTe=%@vj(PBd-b|78X=^aQ2v^}&X}|4ZlA(Vzm>(63+l&~1_
+0wzWbebE>EK8dD#wzHUxdH&m=Jf5*T=m$Jwzg@?iEExlpHq^gilbdaptD_{6?hm0zI$Le2ZK;9rA>93
+J^Q&&*JW_+2{3(TAA)v;)<GEd3N2$Jqj?qe?_n;$%c9Yj%#?@b{P``u?VyLV0bkX%;FjHl%EazWMwHi
+eukt5{mrDXe=}cC?^$Z>iz+-R593ZguLUq=IgJgeT_s(qH3#!y(4vsTx-s{ZkgO7LM2Hyo2N6N7q|EM
+iPQ(wk}o1K<Fc=>N~?asM5w24WL`@9z$j0jaj$8pPUtjz$orAsjk7vsXjx3&v=cYXiFpg;~o$jBDF-^
+upF<C!mxwRh#v7|pfdXO7cyK&)78clqV)-{a*hyPsWy%nl<;$_oQB==-Vba-PXe7g6F3FLi#pUP8@03
+a>L_0|+Cpkiq&HaH_Q-f+q*wIUHzR9M-zbtTSP(C>LF*Xrx44YFHkwn5!^fZ#60KX_eReQ|?R2jpNms
+-}_uKzdZJ7QMZO|9sJ=4QBflud1A_vhO6FGabi0)_5@$^q_TS@&JP)h>@6aWAK2mtey7DwX#G~lPI00
+0170RS5S003}la4%nWWo~3|axY|Qb98KJVlQ+vGA?C!W$e9wU=-E0I6nI$$t2li7DzDs4j3#F)aU{wT
+>=}(hA0FVVs=R}0iPJRE43JA09yiyvuiUM)*r3DSKrfCiuR#x-@_jWR>55sv!GV_VKEBTXj8q*L=6&?
+1w!U~&Yjs!g3|il_w)Jw^+hvt=iYOFoqO)N=iGbF-O3+6#F!X{F~gs#GR$5^`{!W(`9A`G*^~a1&Agc
+Y=gE5wOa46h7VnSN=dWw{$(;?i{W$;j+wQ*mCw%_D+>zfP+@1fUyYtI#uFC)MPuARVZB|xBfnJ71{y4
+AV^n;5Ne<z>1V)2deuG(0z*iGLf7B8ajsf*{pyYv_17gti)2Nqjt{A3+ISAUPB@8v(b-HYWV+UjyL%o
+0Ni^UDuMR43v(7^5N6kijs2f{$NoaYv@Y!-@eN3m7&sObTP5>4`V<q=6#slkEn;pQD)qB5d-Ve9?cqz
+G9fU7$`C@Pr1GYnGYG5cUgvMI?6EP{}Ix^xMXagoesn0!LQ}-*u=xzw^wfv_SHNbCZAzeU)!+eHvTq9
+ny&YK2K?<G4hQu*uGPSpgV$n=K`Zn0H{e3oHmq;B9m2G)&}U{jyw872c@1~0{Rse0$<}Z{%Ufc&6mG%
+gd-(tV`+xJVTJCD@G6O+u_Zv$Y+v}3EUu<L<!5MT~U8-OS2Kq0ks#gUb;YGS}kzc^biwASG1eZ7ew1X
+jUr9)iE;UdpxW1_`pP;;3h4o0r*xBZ#ik;X8GBT$oE$$D&Ru)N>J?N141molzpWN{|V5eV_)UdAXn-O
+dU|IUcMq$WFE}94ss9SKg7Mt7@?Xv1^9M>wqeQ&VHNfT|ExUkQ)1?#%}T!)M7hqk8+QMVdguSWh7r!R
+iDwL3jJhY1h6-Fr@{L%$w^ZB2UJx&SRhq*NzS-E+Gvs*<5g~ejU(7Q8u%+_AjB%@jKGptvM`F3yNON3
+toimRKSA#HrPKr)-#z=Z+{hM2P0sjZayOJ21Ac(B$7*(y@i40;XO9e(GEDb{n!)-KwcV!G=EjP8$Z}d
+y4^;F9v{CN1h3tLYqap7a=#wOLxyX$e?d&Ra_V`ku#u@;AOlt#HO>O`@Kx@Q<GYW;yLZPGZ?7c=?ua9
+wKRlQbP74*qNVio|z;@ifB`r5rMP`-<FU8Hykmd#V^Rw~)`vi0SsAfetqf={ckN7sL$m?UR67WD|UT6
+T8VtfE8fmb=wlD-5}K72`Ad3{W4C59?BM9Y_b=fnLF)=FUNYc&cD1m)q*J0YzdPx!pUVlmQY*_c4$o5
+1?!zhnC(MZ|*w9B<S;?<S%GvuwoWQ%cgofQdz-xHMa;WX%ChaSY5t#@Y>|^rB|uB(;ym(wTPPwtm}jn
+C{!`pLf${nt{R}L&D#oEa2gabOic~V$!j@T$mxBH!oA-QWK>q?hQyg37diiZO8*jHFioB{6Tp1E>IoM
+lt|2`zGEs9u7tQut$OdJ8ttEQNds-r^U}8W^?9md}l0RvQ1vJsCB^tbqBOOe%tl(DUvuf^Ks0)DeFvk
+p+$K!!P^r$>EO3k%E8U%B^4qC!hP=X9lFtH#uZwI0mIyGK{0(_NWyHzcmqm>KOoqpzH&)3jmKDvMnfo
+Q8Xo!^21Sq6BQOXeJPFt4-#P!$ZaGBsGv0^{KJF!g}#Nn(5oKi0>($gWI8Yak?80IXkqkCMT0TPV<r1
+pNrD<7ZF@kZX<D#@37Tr_>0OZJ~N`-V|>;By*31T%-mdY@wQZDOGGU)`(G~4v^mk0FMH|9NJ7}1Xfw1
+SIK7rFt;Za8YFxsJ1tTXu#mcylvbO&3QR*89?tt*GxV=soHK<t`|_0(-xM)qu36?H`%H;0L<1dw)txZ
+5N<na)QLFCljBlw<+KrGJ^Vp9p%I$;UE1(sW;P(`sm39+=QL>@|eE$nXio_^CiXXPa^^#>lFwhNQSDV
+Hs34>^g1L{=hGEmCeE=1nE^U(9)&#@+3Z;kwpJ-$A-bwtzoEZ)*|K7&sywI6EuQZdSB5;*tMY-yOSrY
+acdxrkMyGKGC^kB{}nE+U=HpcB>>vYoV_#XP%cp5y0m6zV0vfSGD)g1hluU;G5V_%YCT#K-=09Um;<q
+rP~e@EIMfE4`<>^wE^hQQ|hPQnE2aFp(j{)Q9-9M+8E%O!itn*R!mkSv2_CZ-Ea6x)4=v=?Q!j4>U*$
+bRs50#ZTbd>-bzJm8^4GECPDhCSv>IfLxkGJeU`(Fap;zMeFUyh2FyO(YV|#(+a+kAO%k0JfkD{&0L3
+MN-#keB;-Lt;3Om*I2tz{G6gZ8C&*vVy2(BmyX3%ncYcnzz7yJ`QX*0*nW($F=#%#46o9zh9`c$Q`B0
+IEoPg1MD^>u#wr3$t3&yjN*Z`~|K8yFr7qB|#<pO!AQe&qrB-gU`kDwxZRLCJaa~LMdj0W~c7DLb$Qh
+o*yf`zU(u)KW;15rFr3m+qQjlgw`g`8RBZkjU>aw>;m`K>Ru3;CobS)6Ym=<IfRu`xs*P1kp2J1hrQc
+NMvdN(ffT*%@`>BCL{h%FjE=OOQW;OYI?$w`(G@N|zOZ1)edA^pApKN7qS3<Vgt0ZAyW+iNUqZ3PK7Q
+Nta}clnR7lhJK`xw-AbfHHH=g%SF9hZBf{I`y2d7F$7bUtq&X)y6cOrJn$183>PE*8fXH{*I9WZOJOo
+p&=o|;bCaK9iQU>7<|5PKt42yqRIp6Eqn~kuP=YcO_Q8AwLZe{QE94bB`}t9%7_uPYk|HdrvR`6h5j?
+EruY!RQFLZ4DiQ6NVV9kt>M~t8d_$HDiV?p*(ag=Vof)f8p-YiEH6Rc*g!G<ouSsM?i!c{&?U2)3hyi
+$hGsv@aS2C}bb04IA#!Uz_T=jaRBp^{TY{yq)>$P*)R>_8v@Dp*+?0cD|-iEIyDi@YnR4)Uk%MJ-54w
+YN2<$!$9@#b*ZIJP}*8<Ee?bd<zqOX16p8(}-hOLW*zfin@sH*vnY1lCmA6C5urE=Zgo-&F7kcP&S|T
+4b%aI6(BtFfp9!UHV2wu-IC9XpG^vXmMWXm#JI|{_BP%OMH;2+A^U>nbL$|(=C@bbLUj?+nhzRONg3j
+gPa$q-gE|ZAA`^hphL9BPpS`<bO7p;KsC(mB`R&3(;d3cs*t(1yn#?fGODzo0SB;V(w&lCkTo+(>2r-
+HAoW{{&$m$~hF&T;1(p;gz1BlZihVornmcbmf=k{>Jq!L<TUh`^;x>Z#R!_ZM$(7pLIyO#HsA$coVEb
+?4V<0w53ZRAbg1b++v!K!bm0xDZB-%`NZPPX1!VCg%63O+x&HA6gLxZzE{K_mwL1}#=NEfRCXNRNCW{
+5RJ05x%UZkCbt%VEN;-6FD4^L-=ZC1Y!R!XV~6+O&u86Vmc?DQEl(w2!6~M49$Fpd{aL_UwMPVj|s=D
+5VSzhs2o}qT%fLu1Q!gf!skW$?1v}qV9=R|WGv)pa~UU3(NyFSd5KxsWd$U_46%UZ(P)(_<5F#ZK;_4
+ZjX4aT1Jn2l@>ed&%9=PNV!GDLLSCR3q`yO|?O0!5u`_4GWbuN^TdI7Z9o%rOrg-%7;X#0nySe{v%;_
+t)Sb!~qRv~}HvvPSJ$nQ#8)Y*gDDKKjP2T+j?#FxqCK#zP0*YXuIvJ3Y122fw@js4uuUx5gZIvZslAx
+`FB!!TEGF&pL?8smi@0S~;ov0rZNjs*b9Ufa`ftJv7X@RME!UbfYC%>Mp{bUEAyia=ONlOq_26dq7A_
+hPcWeWOv1*q!|wR|3O{!zu#mdX0;O3KPoI&v34+F*xIe?N>Ydl>#?;vj9LacDdNt&+t>^%DC!`w-}&%
+i&fx8&Yr+YE^w8K%0*7>Rn(6fL3+e7!Qo+v;Tl>vxhETOJq21Fdk~?PTJq&+A<XVR!5Z^xF;=AuR-bN
+Mef~Wgg%e5WEEcT%b;UvsKfPGU6Rx49b5Ob)3x8^cR(MW!av=9);Xsn#WYc9KkIX}{SVCOp4WpL#sLG
+NvWJO8QRuqM1`)Pl2>L;^-=nENtNLP)V+6Xk&`Xw~=ORVvvRErB`BWu5aOJ4+v*qW7W)D#E;xx`+ltg
+^V)LSgt*QX}gjkE0+8#M!&Z;t+n~A>S+1Q9e$L5AdVqBjNKY@*(l#N#T!EO_oQIyD2FzGv-pJx3plZa
+5aLCLC_qTa-)t(;f_?3Ij8~drGSotCO(IJZb`Q2Aqa|<ooaDyr*Nf*{G|{V9@O4J=C*jqp&7VrcY55!
+Ls2~b(q(skmcHyRyO2cGvc0$$SYPa91z1iG!+K!QKG$2hJx*56TANzRaC^+9$^=~Ve1@Qtb)nL~LR&6
+PlTjPMze>xM;lqh}Y^j0?3|z^60gyU<unT|A)R1}y)B=)C_tn@180vWODl=%xtIR$_m7Dx*4kZx~*zF
+N++XZaC2bdcRnFO|QkvTRMMX5CTW>#)5d|#^V={;cL=gKD#={XeH;-{0spQiSf-C%Bl(Ku>9vfe6pH~
+k<(Ff3ePKhf|t<iWWijFTfV)H40YHhoF%%S^O0_H%f^*cv$hi?nug;F!QpuI`CFji0q~S%?>gO$WjkQ
+lO^b_$RPV5Se5Xt+fbhy%%(22ARQsQ7zH}Q&`BC7qP(LOox>8ra2jcNp-RgSSil-EwbR&oZ`S7l!M&+
+eSLc0n*!)k=Fl}6MrET_zS+_nW_h+RjGHvPNnVskE5=&QJQfLS5HJGS*<*6{*R2BP2t;3mM1MYmQnHf
+;N`|4!B@bsL2WDml9T;UA(#L*~N^4Ng!H?;<pni)GcP2xpCeyiLE3RB_*(nNETB24smV2kz@uk>kHAv
++aA=^#<0yEhFi`^Yorp8TdC_<H6YW$&lS+S!ZV3g524gAQRET8=hc2h`;mggY~v`OtId%j1x;PTnH69
+kBY`NTaM4wkBQbdI~QyP21E_qXZE?mE-8X3ducE369{;(&oq5eJOI2ekivdjH)z<C&yOo!`WQlCERnV
+&5<<c*J*95D0U~HG`ENVHqYfs+9GXTSR`2o6Ln+w;U%S7;HD`ybAZ+x@S|NnGXCrsxne8ACEexi#WfA
+Ce)7yE>mw|P^6<BU56c=lCJRu6zN&2{n75ez$R;Uvs!nvplS!7MT>yOUyF@*lXrR(-6eC?;e(C*Re2Y
+Rb(8Uf9TZEtKvgw@;B_P2&|=9ixP;{TTMLSV(6}7$L)f?gi;F_>tS3P@6EfXyp8<rd)xbRVR{jt<O$z
+S%@+q)tD6_ZEg>ds;jKN)e;q=RylpD3=L5y%p+{?o|d&6<t;o@C{L6t5nRd$j!nJ8|9EI*#qV*AOW`F
+ezvETIu+?7$j@ev&Z)6Ygbw<H+N%*wm=Gw?a#HJdT3nY=GI(3H>7X{6L3d$p(ZXr@ya9q?0=^!e=0Jv
+6~>ErU4OR)b<%5itZ?ahfAGRfO7h*e0Y+jU+I+7Q7m){sV?#XFkxbVEXuXAvrA^>bgA-`YZ;k48}SyA
+?;x`C6o8A3oeaW<5WWMKC2dG#X9wAk2IEFfK`&#E;ufJww>qYQn71R&0VB%l!DntaJ|}eHa~8p8Nhdt
+(x8+)`_{uWV*MtrlnR1H0rnb}9HSfaf@dr~_0Fj-{j(!f`<{b=t!wRs&g7H-AjiyvUngRaXj)^<^0fW
+($Zg{4fnNT#PC%cp$q|i+mDbNmCwztqAs>W*WIvfB86ah$mdOji-RGhWAGxhjU2L!#ikywHN!21EF8D
+YFwwzdliLh%}SC08En_W0OR#&)a<W+AR1Qgx419ha)R6%%e$i1YG!+)fbVxA8?{yn(+`jBn(xAor&vC
+(G3-$TwoQ0lRs{060~$Y$wIaeuI+!O1c4OTE9`-0<s-0_0-!BaQ+hz9lUqzpYZ%5aG3Lthi`6ANQ_r;
+5B?G*@~tAt=ls3M3rQxwyn<gSl05zzk!0|bLDc2~+YQLx<6Ph^21h!M7!%*L#v<^}{3!M+E=Lgu7x*K
+Mcf@{A!MYJF_83hxa)E~^PwE%{HULaieAAd2h5pA-c@`VvjKC+$%~$Rg$u0a3MUu@sD*zABs9cexa)B
+`b&=+AsfNW>x2g)K8R0VtU9sTNSFx1W-%&%NeW2dH9_a|Z|>3K#pzlq9`!tJH<b33T$hZSRHdveQ+3!
+I|jW;et6pTzxx{8iex6wl-1K9k$~6bw2kU8JvmTmbiM#kSNzAQ-_CK}cpv@%{mFxXHS5J&TYjwq-ywV
+A)VgeXMscO<p!M*^0?VMw#ME^S(usZ%8JO6Wg*e*}x2;7n_QejU>03l0;%g!Wi#uGSD?C=)`?b)<rtY
+=$5g*Igt<YwVKj_XB{ARS6Jjqi?qos&5<^-(x&lJg_Y_qKC5>Za>|t^lu7CK4bb@vy6sTgj{<dvovPY
+g@C}?usOAREyNdJpl`<ncaJ8*?2GK5*2FovsaqGsB!E(?ur;Vf=0-XPmL@ssaE;pHS2KU(Lw`|_k>wv
+Y$;&PLFX>_^rupEa5D7X9Okjco{<SeSfo1X*Ocd7SDPA>|wY8oj|<v<!IjDiZt<6l5Qn%+NtmohF1h+
+49ib?z>xL0V<8U*JbK-$~VJko=@otTahlHD0{gswS47Or+>2xgVxtM5bt&J0iJb1ai)f9N@9K@~APuA
+$z@YEv&+z1g2GyC3Y0gpGJPg_?86e2A~gC+dPoEf|QMEoGBP6x~TDM%AKS@2ktNiPvP^?c6>hZE<Sf3
+!RNlWa9?!WqjCBQcG1@phv+MCkiMP{(bpeeh1cT`2D&dI12+dN%~B9|Q=_y#WS&xhVA_rSQm_*PKq*0
+Nmt1Lu7}}PuB){}Aa$IAQLE3DW#wLM$tpSi&^iF$zfWueWzsmOKmE=uDRgp)+`n&%MV4iNDk-Sb%#y~
+3@%1G;U1!`6f-E7*dovWB?)s9QGvHynVyk!fy@E?h48dh>-Y=`aUKR*Ox!H+5x&t21SCRp*gcy7eTY+
+u$27%=mnA2H&{rIg{xU3QKA(74c*VKKzkMo4rPUA<HH#!aP61mYs(#Y9xU7G==|E%FJjh?VRzi6!^pN
+-uYlSK%!c)#<w=ga5p%uW>MI)LD24B$pLfrL_gtAp8|}RJlErY_50q+g^7`cMy=kfsg7$C^_RnXZM;N
+m@gt%cCV4LFj%t+Ca?VW+GUA)^z|o?ZePgjS=%fu(<7(0IaNkyl);}_QR&N)mzquQ*er9%%+t{Rr53r
+I)xHbx+sXDi%=H>GhVc(p6RE0SUJ0uikt(~y3j_SB;ElinR?186&k2^@Z<U<gGN>Q%z+uoQ-y>KRRB8
+x>HaWH~lAUq69cmTcZfaB+zCcbL-h}63S`*x~2|pm)@!Q#>^)7;2A$0Q>u-sgH`}!Yufbv@MtBJVpmh
+Z(kxqSihJlq-K<Z4)=m#euLFCGM4#{x0r5d+;hD_&vYM|w4+9G4G~f7PPp$~dydOIkJVl2&EXR&~o2E
+Oc-><iv9{wYbW{`EQ1RUhJEs6p~_NkTUV~>Xrg440VpYq#&<)xog=nZ_{;28b};7mKCsTq^UYp6v-=C
+fSzF+Bd@cF0|UGr%9tshAK=aQuQps8oVg)*>kC#n6Iv|(%*+UHc@5Z$g)F&YA;Y|kV~ihV@S*Hl^B{`
+PxW5I+j6kvGzEX24#Oy^l-Ujut&>`C)at%>|dc1k+I*Yo_s;<i+^E8d4mcl?cB;#J(#fwx!F|2&Gde3
+<Eo;>xQX(U$%JYZ(3-JUA>G#=;Jj@f!`s(N%KstNLG`HUO}-ud7H6l@^%Fr=&Mr??yK_V}3P-m8l66n
+);W7zSF3r?G1b*yYRcn33!r(936m?RarnM*+=`2e*e|ad|Yn19zq6aB>e5h;W>*!**;%-x<5pvYsP_C
+?)DU&b1Nlzbj#SoB`d)P;Lu}105Sjxqa-=y=|E2H;5&;t<CV`-Q?O7ZD0GC>;$!lPfZl1WWn5k%2J~S
+j7aR+db_CDxJ~0*x!zMpDQj!dzEgm4sYhxPn?x)*gj@q7wPQ~Z`Cq_u#RvqBW@t-|n+#ls+Z1l{h#3{
+Nt)TC^$$^C!{v&<<wiKUoDIM#&K_w6x$stpgv!R_v^{CipwhNYqu0&hKl85L-8so(`89Sf$l+U2;!Kf
+PY6bKaF)zAlT1xiNSvAKwiTihP+6}LgmS^(JVfVBN(@TigF_O|t$oCd`h)gxlmY#(SiBbGdZ^{lGn$9
+XYCcFogjviKOfO_7VSI6A(3?-7(m-Z>9!nI->;C_v577IGA)EP)~7{J34RqXL4hPU~-R172*jFkAqoI
+dxVGuJ5yO(NIsw?NaTLz)9Y#&hn%1aRMcacct1>Qe(ULr5VEZ3k{3O_G>}(P2)d@k9q3%F&lXRqNFtS
+xK<uUC5kAnJtd?RJKOneET{H}<ZP#<%C$$7-$$LDYXAq$jh*Y@wSEMsxH6+7!r5t7K8-p%cnZ+5NeAc
+{1n8gun%t33Wo9cQTpgvo#`y~9c#TD!5vXP#N(qq8qmK9t#zx4;Ko;7t`WS5+^}qG0lZ>V2C66Uy(R9
+Mc5J~&SPlzBt)jw$-5;JJ}^$ob<XbLu6c4;c;`<1NS@_Ne$ph0Ii#Q5ljFX#!E`SoodI0ogSe#7uHaN
+M476Wo24<F=DE(mpfPX}`ev*8-r=M8=fU%}fL5PtzjUa%~D`i6wY|0|{S1OPUAz;mP@D1CaONNg&Z2U
+{SBR0AC)`;7p3}XpiQ_gX{hGL6W<wen)FgseVlU$^|1`*(Z4)fdZ=B<Ohj-yd@!8Eb7DgmI?_DXw}>e
+)7|785Pi_>v>@j>RQR^fbo88e8x&CZewB-G2^n12=k-J6er%T8L+)Fc5b~c^cVm|!?(SsV!(MB?f$`3
+P3?8DCYDbxMRroJ<FA6C=YVJ&WUT#0jmx}``Uup}rS{AizOJN7_czpaKo^_Pq+S@>?f&r|+6%1gStEK
+h=$tuMiD?I{4u7@<Esa16-on7+@N@sxSUzcg9=X2rX;W7>FlXSETqrHf6GIap$v@kRd_vVI(!<+Jw;;
+=dkKav6*M;RZ^`4nn!Gk6l(wZt8Cz6dPxag;0_zWh2jS&QegH6X)<1s1n!Irt7f!~mi1pgWzjczD_mt
+e-80QpAc2YJJq9G8s~Z2?CRtj5fz)_^`E9Tqn<>yRjih&~>4?4GXZJX!vuLr@pxIQ_i1FB>-X%bAA&B
+^Td!DDAW|$&rnmUxc$S;M)||y30Pg@U9b7!g)Jm+8Fbr#Wnv1Z<p*16jx>9C{q=gA#MLIIY4xNgakY)
+n+6T}43~1bSjLbokOm9P@>_;1p0hiRO*!){1Ri+`E8aTP70YxVUx9V?2;lu;O)tcTLHt?0bVOGcll>J
+IWZi(C{emY5xhT|#WPg6}ZwbM+@ETw`V*~+w(o2lv)EVm?8CeDvqy;{zS&bWDz>g=~)*!+O)q<j{Opl
+CyxNaZuJ?$oBG<|gHywsLcG;G~e^>s_H_1Q)7(?c+jX=xdiwnsHH8`2_)$pXW)o%{CP&MVcqZjRKb9P
+}0S?Niai@{YIPk8PFaj1Hq!H$O#vU7fjqXFN9P&=CD#wldV-N>kdb7N3mC1>^!z!WK&~5MxIgwO33$$
+Q<{MOHcSFie+m_q`w7fKyB2AJoN_mGPIyDx$xIf4jFT!C|J8>j;ne)jWkoJ2OE0Ol6wNMbuu(ILofiH
+(mKN6cSgqb}ToCu4RaJUm1aj2FCQUW>nN~@S9rhB`%^Po#`>1wXJboANOeofzi+E1s<|>S%Np*ILYR=
+|q!}aGU6KXN)&Z|EfqB}2AwqLs2+z73Jg@Vi;w412X_uS<}O&^<VC!5Y01;;aZQo<L5R^gH76~+VVmK
+h#U@$a+JP4g=&v;&E#z(GEL=w43jxQKi1X?VaVzNxN)jAFeRh__CTtgIcBXFzC6cN3JJYn1EUWHSmQ$
+Wf3GCiRtDaQ}<Xag#At+{%6s>Z$2#uNfCYQ@lF5C2Z{mTsC|KxWsJ^sqqwaW&w6aY8<}PAr9T?n8Vk}
+3`M9Lx^Y_vGCkX|1yXf?0+(`yr8g|{*A-W@!Uz|+N!5`)5s<EoDQ;wioZhez$KyIZNid~5K@#aFz&Dt
+3#cxw(p<{5D#78TMBnDt46$n@Z3<7VGou|mzGulY}fuYi(fgTZ1X%Q&zwHBZyaqib;X5`uvg66Ls5R&
+H5`KA_$(Y)k@v%pePu=zOf<W+SMv48@iaC=5ptK<nt9Pa%X2LlQ7dX%`uY~Zsz>TwU5`BxAWu42%fu6
+bZ}3UIGe6mpe}wlV1lDVSX9@xk;@>sBV03U%h|2%NAe@L9ZbaA@A^#-Uk+7DOkDC!kC&<dHQX+4v^XD
+XA?x`+E>MKoG&1w86y_d9xf<e(cL5pX1RY2%#4Qj@-~aDEKQbUj|vhXcMP+qEJY2;Yr{@xWYqjKZRTM
+7r#tog2|?2O7oO752?VUODDs+ZXALPa<!T~n1GJCgIdqTd5ka$h+U&mf%T0b&)_+SI(xW{huD>0<*}X
+grP)F?K_JQ6j<dfnuFZjNucs}cyM3b}i0dHTVQsYH*0TKqKgNDm$R$e#!M^;0f+=^=qkGBOX^(F-0WM
+Wxe9(#eCsAkD0&2^YBMHO><gQk;)I=%=go;`A$mS_(W#^_ekV{+glo61}RYhjdwGN3nMRw*W9G*pYw%
+j^T<(>$M#7Iy0bepCe2HNxL7s>Xl(%MrSsgi50RP2Mr!wR$5S}a(Ek+x9mSpW<4@@aA-GFmIyn#@d(K
+*>+w8(Obz{n#&Tm@dy*SmpN4_XDl$qmpVKZY?Y#<yh66DMAgI3n6Ka;*#?`I^ZK3U|ZQdl|kq+AUdnU
+6=bBIXDVSaPo6S95W06<TPg*zlCz&<ThRJ|{OjkmBUCxnRkyrLEJ@C3l_adRaERRGp-dDbEO@rK^FFB
+YNm?Wl5M^~RqSpa-cRKhi5=hpj{H@A%a;JeGE!CNKS~))=LSD*>9RpG}ut_Kk&oU1gAq)^e&d;EAh%k
+ff`T&{`%C~p(7V;DgJM7&;7MiU+yId3H(%i(2L1321qdlA-x95T1%JYI11}R%!npYUVQCe!YU)VCigC
+=H#UywKqS1QFr`A{p8*u8S4MLdW02ItV;AS`xFl8=VZr%*G3M~9ganDhA@pRuZH89C!(m@5we!67wGg
+<4xH*%nqA4Kv<@=lWP(O0v4*#(rQU8lDt9Ri=^zUHOxfO+M2GY#%yyJCOi)5_F_uEp(&83w@BY?OD^Z
+OBsHooYj$8$^<Ib32Ef9l?;kS<z{;d$$-Ss*!yHUOX5{nju~Qm9qWujj##nI2$dL=44H+J9cD~ytHTY
+Ce=>oeSCTWKTG1x(tHUPnc$mO>L>>gYRtI~89<R{33KJFPX){Vd^Eae}iiuNONe!Ck!~9N8Y#>F~(7E
+h}xx4`9at58ts<R_8oolI0Ee^o9%ymaHk<N81o$K{d*_MIJO!jw9C)?g|`kzhq|GH$d|00<=CSwWro&
+MIzt~?2I_~F6Hej}+ltxeooa`S0A*>9nfodS9X(5@Lk>!l@I28K@eEA?F8BV$g}={^yr`vkOs&ekTp@
+pN*+KOMVd!t3HkF8OOL+2bY%#?I6EW7#Qk{x2Dv%Us|yd>cV<rn80GcZi43@<5xYAi^A5iXu!3(3q5}
+Zpc$Nux_#@jY>y%rfT$$re2&M@q)Gm{`>Hdt=w{-unct6G~L*z$W5-*!UKE(sMM3;19Sj)a4yvV$O!m
+`iCKe_GOcEnJ|kNl=7Rg?-Y2A?>0v(T(Oal#gOH2e7;f<Za+I8{E;Vy68A_#WC5OuU9ab@9O$RmrA{y
+Xma3LEnSJQ#_4XluyT~c)?aG!DHH3F#2XUJ0|$@_r98&epj3hll(vvw-kH%uar5*!xtvm_vyNVNH|Gz
+Bejp=%yAchPz$r-7o<x}?Yq8j+AgR%n^$B{M7D=JPmLHZGNFd+f(Ke<~CLLKqjg2>mOrweYiA%Zdt&p
+(F7Y=K!}qRN*t%@M9{3Z23Un5#+z(Cj(NoRj##2wcW~3wOC;c`QkHL)4QJ~31K4ymD%!vqeQB-+QSVd
+$!SFbUk&22+zt{)H%x_z<hP$CL<{mP4YE2i?uG_wyD_+ulrM(Pa#C6LG@Q=2WY3T})7!nJ<_bB&|Fre
+Sl%{hTd;{J6S*uoBn;Hk0mQ0xoE;Q(y{B32~DH*&==319zPeouS1)kr+%A7;XfuKeWbZUk$m4aS{pkp
+a$P68CbOd8l#89pXhNtQvkTymN^GgZyqsjHEjOn6K{0*ASlAEPEyY2|*t2IJBUl|8|-RC6!W#Q8g+X;
+8C~^XK4}%V9pF$(|vkG_hvI)RccguI)yPp@>uq9IVIQ&WT4Ediv06m^+s9|2KTh&EhRBriG<|<JiMg2
+J5M?A6lOmEH$7tQ>GX;LQ<P4LlFS~%8ym272511)M!i{-fAI3Gp~jaV21#9<g{LBY6YLu)Tn0gFSnVp
+<h0gGwPi_aiZaz++YQPqCOiq_q-sWezrK;0%Ft*tz%PUb6+5lmedR!QO0Jxy_1%G;G%6{r257t-DRg$
+@06-_X0D3<n1`ok|acidk@TSy-vHPjK>fl~FtZ-u7ymb^*-jKbRdnwb=^fJoP3{P4|aWB2W?U(Q~a9G
+G9i~f%EgdUa3PfP8G1hc7KamZY6+4az+sS8_fxjqFH@XhJ1qhzk-mh7nuQ3p?aUA3^VT^ZTe{+Q3O99
+^l%L8_ryLC7tv?LJCyU9M7vZXYwmZv(A3L&D62m3mhvsx_5ect~OgEjP_YMjb^_+}Q=(i3`YdtEDF<Y
+H#@p+hKczdo*kcK|#`z(M3ud&_rgv_>YN#o^}9^_pstQAdB~L0-ej^N7PH1V#Fwhj4<-C-IQ{A#PEPU
+ynZ73ONhl8LKfZJvHCJ%58$#2MC8ZZPODL;5@XOAzbuukq*PL!ppt3zVq-jmf8Lk1qFyo~rNog^FmMh
+6Q{}X_vec_Gl<7!0X&P{VlFk4k_;zYwVAbN&wL+l%2?8361T+TbG@otmO+^CAHY(}$GNXaPpa4sY{Q|
+WS+CE=OoxKfKxYEBRIp>Xk!&AtZ50}mOkGa4PF>ps06T21HM{WPaXZ5Z?rR!e+mU;yY0K2ySO=H(ze=
+?L^zl4-}auaK(yHgEgN$e91?Cm5Nxd(#EF*yQkI>DmPYIcxv8n?f!$Sigk_%_X|*l8UXa-d0^Ud+!wN
+ib7oPZjC9UElUcbQgifsms|_<QL}>hH%OiWcgs-Kh(-WE3|}~X3^EO!=K>3cr9B+X091}c28~8xMaQL
+`Hyw^MRr&R7yTSq^wE^!HtIe&ihKxAns>;3xYyLCR8WpN8f2V?5pLQ{mOOliwtX8-S~Oor&*3Z-_7#9
+#pG_XYlBU#1tb9g!-Zy&1r6<PnEd>@lvkM&Ke`E`_mKCu6kTAOWJPPa$*<#7s3Dox>7Vv@`FP{_7n;W
+k0dtdz6#9IoFwxo$sgBUXiz4D=O2WtxB%L=bI#7|7&n2}FC`+F!jLxVewa8SXj2t3V$0*{-#gS*9KZM
+nYPxtT0^Th}otRyZ>E?S!7O8km-5Y@*w%3rnj!KnU$)KpCFTyn@1nkY7a>ccF=28lZnvupATv35B2)Q
+h6STJW7^4U)sV-^Q5{w&}~3KF^-@%_KubM9?qcm%=b`;#!Vk9o?yAj-Jn1<*IQIn>gw~*J5;P^nHtE`
+TWWFesp7qmsX)EayeU&2(PC5$+)E+F0_*-V7peG4mwN?MdTK;XO`Kdb885NuuxQ6>9=Q=oG8lYnh&4t
+h^#*&9KD+7j5PcH*e2YF`rBAfYAag)CAP;_o-;cmI&{-jA`Y1_`S3<C9-!4dD_zXGl2R!jvz3w7T#_w
+lRS<oTWf&7OO`=3y(8)E&9hTn2`cp!yZfA6N+m{ER%ntuPsP}6U;`#zU4#Pr)JA3(crE^v>EVzm8z&V
+MI_T1V+dC_!q365I>VmPfyYr~eq|e;|Wl7TS8HC(ziWcsBxa+wO(P;DV01D>ic6{sY|prtXst5Jov1M
+{d7AY&`{hD=i#1*GPeF=usBtex46`)PlgHD*!TxXan4~Pz+PN?WXwLO)<HfVt6-VG8O|C0Ck8YpT9{Y
+CVsX^Sbl0n1=^IExWF4|o*G1WHnu+*HpiY%h7GabLAaTQaeBr6EtzDD$q;TjKV6t1M$OtF%Uk=Ueb^T
+J02f?4SYjWwy@~x0Vq`k@cZD*z{ci-z*drP(#O6biv>SUCo2eIj)$n48UTiKcmf|3mP75TL6i7??0xx
+nne<NSrbl%ICHJx9}=QW+*$OTTYiT<FF8H*yAeXTHhp&kiyMjNi!AJ|0ut+6MQ;r!Sm5N<Wioh?jk`k
+7&-kO%9Ip0_EPcOAgYwNZ#0G>E4W0$qo6h@}XzhVz%>`$wFA9=?CV`Df!>;QZI(dlTos3g0U@|3o}R*
+^TrtnT~BRfHTa+y>!4uuDY4-VYCL2?rQicauDZTkkT8;>jx?|E!3*@gG{vxjhBW(^j?7W>`&83CBBAx
+@Fld8^sg|YhE>Hqh}Tu{OGHx2TY+>6;E%1S-~zt~t)b~ael*ZREu|Pnqx+Z}^7YYqUIcCC0(h&(5R5;
+_3{4g?29snus^U3SNTCimF#Z~#75hF&326Go`3KDC2P9-}Uz3V9s~9QMXOwagYJm9``ezcr-V~6!q1{
+^x!<-*`hwWD&QKqGSOGSJUw`Wnm%ZvJ-?xPzF;1qX}$~C$nI6o;$*%mdl9M!t0za^lVj<xHNT1KsHvC
+UXrT9T$AyIrm@gJ?HXs~a)Ey~apEgywS|LqF9aUHLv_*G4`z1Fwo$o$R%XEJnAYST1%pzK(ma#E8Q;l
+@8w>+%^-9zmt1l4^^3e#BUbkck|bZ@t=S~4tVLM)i-IV&>D~G7r#~?8T2_^Ee0^vEYp{~uAi)3tV!tq
+0;;4|LOyS)mjg&q^-`+BYC$P=5J$0jDM^geq8(ao87Pm?h`un+A<o~Sd69U`Vw8<pjF;pJQh#Uacp*J
+O?EnK$QvQnM(`boO27uN)icc8PbpB4^b}fpIlc$)5x1?%(qM7m)G$GqkBGUqz+iKAThU^2my_Hfl@Zr
+7Mh*@>J!`)b)+^%G31!Hz-F14<J7E6I9OkUJ41^k0ERiz9YrkF~r(Wn4opQIp|qspbu+L?;R6zUOqA9
+htdIKKHa<h%R?kv5bOa`fV=)LAd;ur)PJ`>>sN3VARgh5+mu1h8KtjL^pudbd!+TU9Jfhn(m`E(LLoR
+;G(&J?Fn0*aS=y{|c0kakY5GkQj$3UCY16Bxy$*M=LctxB=i>DS&xdTL6tRi&_GIp}BB<LghM^JwV6W
+itHh0fKI^Pa6X@>A*DAALJ9ZUqJG7UlkhQR6yOpgO^?AEd2A49Y~a||(VNYI(0XfM`=}Fr9}M<TpHmM
+(lh4cTg3)#fc~DvyD^jl9r}^*<qYF8b85L3|(<L=1oYfW?l`^P6Xlp7a^Vi6<+m9`Gd8$Ys>b&71=?R
+TA<n!XW;w`BriqV$rrOLjb^K4;Tx!x`$U7A_iO{e?60CA<sZ#NYe@g9|O0GU493`GuQ{*Cnzp9VS{Xt
+ZYwDG45pE7K_VTYU-lQ_^m1ngi0W17N?_V6~dWeK-#R;b`NgO*h2hEEoqolT6pf>KbJ%o}=qug^C3a=
+SFB>`Pv9lQ#!h87t(?YbqVfCTB1dmD#w)xLl>A``8Shapp(o#{8D*yK;8P0N>MBCLkUlQf&$Y@fX&iH
+>EMh6b+I)y{Iy!p42AE*dXzC*(ej(Sif$&eE-CzP;yz@Qj5a>J5D0C#N8YWW;#W8c_oS*p47hx$L1zz
+C`4E1HZAP-LPG|1I>|z?rU#sqZ=+c(bJhYh;(4lW1mIZo%@H^#7k&1(=GFHm~6TE}=jY5A}NKcGtQ<=
+>-=$+!P);skLtlipk3D(4Y$cY$!l)4)O<ZuW8o~Cg%GF{<YO)XRm@Q3(Oa&Q}MInI~XBe90Bzg2M77<
+JO(#r_&vygYQB%fVc`)WOuj->dM)-t1sT!CwvhNsu-~75d^9ZP!(>lJ}1f(T$Z#55!T!0hgNlhlCvjn
+$V$cj`A*^v%1LP<G4iv8OG7AOx@{R(~yJcvzLD{dFH<f_3}5457Emv9oIDF+M#vRgMwjt`7#}Bb`p%d
+f9$*H<yB<EEtk`}dk2PS-KUNvOYS^&S*^S3ctY#mf6K5t7Cdh9RF%Ge?b=Q;FkleUdz)}K#vo*(6WsV
+-N11^i?K6^lt8iyjj+1qmr^X&di_A3`c4=433~tiEd<6dICZXl}Uhyyl*R(;GK$7Au9^yEr4}=lDw<*
+4Vh4}vAu_5?owZ4(hj}FDR|5AK^d{hH#OM;Of9sO?jPOZ8G-+<}A=+2IyS@VMxe+z!k9!=o4u4)*5=~
+)CnaVN`<RX4EYfqhgf?$d6zky26HE;kRkU_fS?=TNii>Eb4<NhpvVIVJfizZ}EomQ2Q1cF7ccrIj#_#
+|%OW=^9P#Xb+ef?}IBHy04TilrV^IO=ajEOt3;C173QoT+Pa1PqCHH5<`~Y%Xorqk2ZYS9Hyc0db4&f
+23;9kY~YxXW(&o>#$KOK5iH_r7B@p<oixIrS%1ovmMXb!wX}3Kh?e3xmJ8tal&_#h-g2wBrkI5l%F=u
+e%isLD%*b2Tlsv#NAt60lz6Q-Ylr1F>CBE_YEamQ!hZEm8zDixmFB9MBF|RBw2`0Yr7)F^_@?hc{H*1
+w^OMa2~ZpH73CA*FKcM!j`OJ2n9H2B7?M|Hy*AFCd#k+!T+(y)We@S|?^N_yZ_8xc#kp|vdF+Bz2<G-
+shxpvQ(g1bIk<xc5D`gf}d5lR0Bx?1Ox&p*7VX5>ETfi#(+0B(;I6kxI}d5Gejlt%NGY<|b*7p=n-8O
+>l*QpW-uA5#veR`M~WMe!jcPMb@uK7$hzNam94DX&xgSQfUEX!7D)PQ!&Vh-(#XFO%pZCR3NOCtLh@^
+EUb`>r%)KA+_5uZM#;RLup3<_S6ZuVA-9w!e<fF2qPVGx&c%E>&ORq=mPSd={-$|T1U!Ir_P;QNdn8n
+j2helXw7@Z*ZB1=4E|97%trNMIBCc|>ZiQw}JW5SNV_;zWm6q02)oE!lltTqjo&O|%19iB5rs+aHpVD
++8UHA=@WNuraGB^dHo18xe&1g-Xo6no)rVFX%%GssL-)I8v)6+etbi9!R8)#Jxwv!9dxvadjY2K~;Ic
+)7!coH><-g#gNmK&PpOcBg-w~~c6@6ZkC1oYu(43wdfH`2<Dw=RGV9Tv`DUtk3JYT8B?ny`3oI=?7zk
+}u)*hg<-BC6PT^C$m~pp?g~A)U?5#Dp|y<gP0%zlBzA`F*h&`$ez|_WDsMR!5Yh^ew=LGN`wXOr5c7f
+&f8A9J;f7{1c3q{C{EbUc8o#W*_H{NW6^477g~*>+p08cE{wE2!lyys=Hv+`N$8iW`*AD1sw&vnLo(<
+ca%f3cf=AfvZ^E7VbL#p9ay<N*F=*!O;fC2RGOHbLpsTT;1u;R(ZW{2ov4F!D69rW4K$ljDy0_Xwib*
+@59ny5Sn)@ofp3T;4|B!A_Zmci1@m7#2LADg)`n%gBm+VE$CbzpV;yxO8xT@T4$=Rd6hbEtG{2Ukg-F
+uV|_ruq8w_6qBE>-9ODD^$OpbIaho3&TFBce*peG%&f>P*+ei9115_ukMK4rV_-Jl|><zp9Qx9YUx>{
+|E|g<TpXl5j8giFKT<>R%h|3`s5x=+<P}poz*oqVRS2ZCjk!OXIoul`?^$ZpKvtjU_D7ls3;3l)a|^U
+o(-$(Td3w`(E?&$zK`bk-o3a=&U{l<y?K96I61(Q6D_}WQ>TM&x1#OVmK<rzy0-iAP-|h^{fki%i<Wy
+sP>?T+{N;m$YySqXntKEaNUUu0kcV5yiwGb!_Tx=z<!jM$YCGC+Kv><dmW=;CP<SFoF0Qtx-++Y>s>O
+5XEb_$rXp05KJo0|R98QxC)LGa=5F`#_)*}>MOTBm$=`)avf1$)z6X@lC;MO43S=~eM1H+N$18q4@?V
+Z)!cErt6GwA(Fx|>XY31V%%P}92*zX~ypUK?9+mSH{U2mZ$+co+sLfW{6)<L8GVmAcFO<J5$p0GB`)`
+Ri^3ET5r$y`z22)V{*n*Ie!E1?|hBeLbyxm1$r9rhQdtUy}B9YY7e$Mr^_eK-Z%8_0f>Gc=4Va=)x&Y
+sN$Mc95Fu4Ll*BIV*KXOMk)J!EsknQTh>C}D?KC|UC8Yv)Z`|oS#@Q>+ak_q1SmwFJ!Ie}GPw0{_C>U
+}n+|wDa;C%g;d^w{AZ#JWw6N+KN^E%TR63b?k)B~iU~NRd8t*Lt;89O_EAYL8;J}^m&ZQq++Q&@#L5K
+8&Sb`9YJPW7A4x9%3Xt4wV84yxRx@iC}ek$0GGm93n9cM!{1v8f42C4z2jvd+*!U?%UpE;aAj4M2yhA
+#C4ZXNAHDxLinS}<dFdDbTCoua|$jn$}4Z>%;o^~TaV$kY>bW9D|b1nJS6b~_R@&HA2tWJh9FmnG==n
+&U}&E*ehH?@?MFhqOGS|594UODogJdsI}cTQ%s=jt3y(9dWu)R(W1q`l;iUSMUyJkjAqD5k9@|$PV<V
+SMG2l-&S+GC|l^a32`--AV2KZYQc9WedDEVYHkPRn>{vXx2@aWC#3Da8A?{HfwO`|Zp#|6h+)DV*352
++Ix_a1kY-^m)C5VKx(-jj5>1>5i&n4G+T@nIrP@<1wF6RZJnDQ)GvYkLr%Sc(wmDU+k`Z;jOSf=qLAg
+Bz^GUOc;{qS1;(gRjAlo~w!pJ_Sm2spo=;(8bH#b$PO|sJ}c9;qyFGJ=KZWK513oH0(6?{JFcpa@l#B
+7yBUQZki-uXHn4aPpC=gd9IIA|LPH&`qK#dc{vjSj^mRrkow9G|tOFwB1+{-(p<1o*Rx#34LgT-`%Oh
+Aw4d)Y*l07)mY#aBE4iK!9%Un50^e;;nyzf|)B1yo{zhp4U(+vxm3Z_-U{(7r-!GQG$Ch{4FJT1GjKZ
+)JbS5w?F`IXjT?OfL5@egh>IwnI#Nh1hO9BuO<Tr6FvO+V4{bCgV;m5%tf8rEgfkPpzOeDjXJw=J1-F
+BN6T5`VYTi%dSzvv|L~?X{my`$4t`{FWjBP(&6Pdq?Jib!nV+FYd8YKn?w$F3x|Yr=nLDj~n)YEO|Bj
+CGZvw5_Bf?~|<{(wRq={tFK~$FTnm{rao5sXeYch^p(oG$fRiQgdg;iA`LFZeroOi*Bj<%by=yyPA>p
+(6+PodD2hwO)hLKrdwKehRe9^9g**&!F?{bt7R%d;=1&krQ5Bi@c6tNU#PTLv;ltlo?b=}0(92xb4Y4
+0Qi%iBMm`4qfx@75+$Wj$pshAy|1*^RTE?lO7h8DvQvw$&7_CEUEGlJnl<gQG`}uHLXD0hYc-DjOK5o
+LvpapSkRQ;)KYn|>5hvPEtRUUwKv?v`F8^K0D_GCI-ju$%0Kai?kr)yAdDez9{`ne4r*Gw3lfdpsv1+
+N`9V8_ra0{wd-#32r2u^&TQ4@Yj}=DA(Z`l!$}3AW_kak0fLcV&CL6z?un@DW*QrAN)1dy>aEiW6Z@g
+%<3Y_dbb!AjYNw2gjrgWzji15m&J<OMtAaU_mm*Caq{FD;BcY%HxwJ$S6HCr<yOl)%w7?eB^kN4uG8<
+Yw%AkL_m;mPQem-GJtZ$MR{9S*6nlM6hd*|Si_aAmjz=e5T2ODLDP#~$Va_v&s&QSSb`@dA&yaYMEkH
+*Y8u<0)KVB_2cVcMjOp3eG<fPi?rsA_LlgcB%uw&W*8==mlr=cmNgC>3wz9%me&e9vU%bs%}zik8u76
+0Lwm8(AkN2yt7@$8t30Ytw4;t59opmpvypWW9OHzyv7JCu-7r|o@3g;Khx6_BX|Fg(62Xc=6>$c?zYC
+t{!pOT&iT>9L2JRkX^~eRfSLlR-^S9Y$lo~?Z}ut~fQK*^;Ld-JbKut@orOq3JhlotR)U=r#(8{3fC+
+Xb+k61`g%SPpXf5lt6y@ImE!c9OX{8(~jJkqlCA7D1=V}HPxCH+kvYO)dkGB35SOw5E-f$*c=mEEO(O
+Iy}P|(WWP&v@_@zx^tau8^IW(~)zp^wv=KG~Y_MpNTOriB$IQ*J&Eb7{xtdPXgmxrxmm#!0qNPO>REZ
+4<o2XF^s2q%##c486=V7g-f>l%!GBzrCiam#qgl!)$UKU3O#J@yg!W^4mjtK{<$I&IZjXR()PosUsoT
+ydQLMUyheO0}|+6ujy_ucyCjleif@Qid@*A;PSuOPfc8EfcJwU-pJ~gwH7yy=V8L-Ox=nH9PQ&m4*m_
+0*oT_KBMr)J<kq-;VP3~jnOz3H83n}?R+Jg9QRyA(FK+DQrxiDLXqZjjNvHQ`u*W}fB)7Y$KkLw2A;@
+HPs1ZKMF1ApW2TiK{c(09I%LYygW69Y4Nh^ze`&5-$B+(mfs9`eyksFVfD!a+Q9;H72;-(x%C@uVm+h
+ewi@zRDYG5*7itQgAY_H-*oxtrUQhS8ZAol2uaV$`TsTKTeikih2G+q?OQG6!WIZjT9s@0;*;o^I_Mg
+mLdpbONFM8qEgz`3%gNf;kUyd*J`J!f;b#KU2qp{vH=^TX%8L5WB=R4&A?ndvUJ>dWu_Gnz8Z~q-bBV
+r^jE_d#c<nS6Tvx`A-VZK##I7>CqAB(GhNs;Tt^)(;h*)j$ylw@DbXs1BGE_l#dy7gAnOfv31Z=$I75
+04xhLW!(th^P(0q=omFo)3M>>X9xy7OLL|2*qcB`M>^=*~qTkbH;I(2(=bFOJbLjP4U)ql87j#wRagQ
+934-B)QN^Wpg%r!wvOLMB++@5<<ZO1&pG8S*^90$WKS6J-l8q(zztm%FGg@%j;VrZKEL<6BJ{BVOTjp
+PQr(_rpscX@0FyfQAaiygecBW`=$mAtJ(x`P1OW5|nFijnD*I`N{4TcBOn&}+uD6c<flA>{|MqaaKs^
+}$+fNZhzJ<jxQ6QVe==m+e?HUS1ZWJ;!^yba3>_zBB{2U%PFg8rM|sukLYBv$VI|3{#sZ^wRqwZlG6J
+RP@^pk*wd*`ys5dYua+zRj{1o;4*QL{l@hWc<Du1fwkHNh<fs#OYgeC`{d0UZUAZ;-gSYuT&VmH#LWd
+WR6f<t7tGjjO>pK0+46$4q`-pLTx@;;R_80UdoIdeV9AY)9$1A^&mL^H-dp{m3tMQ7Ouw7&HDenDtGv
+u2FSisgV}%sjwMf;nSDHX6QZn^`jCCz$rG;kcE=%<?*NU3jnw6lHsz*>?uRv~D@+aV4Q$d!I9h+dBM$
+<ivBT5=5O+%1R?2GWaJAjRN_cS>4U7H>KpnDB^Ni#5EcbM_P;=?kobjbC+a#jxLs_tT!8F=iOyLv4qm
+?I&Jy{)0^uEQ2nC0l0?uTQ~bdYiO53(L^<De(3LFShmJmy-^T?est+bL(P0SC06MsA)`rKNkKVEG#7N
+Q`rpw@lYMNmv6z{K5XK4d??lG##`m%+QoLkb+A|}%jAeiIQ|Bar1Fz1_>ts|y-6<fV+blQ@5eT6$A>a
+{KX!CGK9r38*wyX$fK?m^!lgAF)(2ZiZ0s;_{u6X7xI;+unTQh>hhk?ZZdm;K5N@&H{;7C@<ujm1e2R
+F%ER2(HUM*F=D=k_L@xna$<~35~k?5i|+mIX}W`>-_0y?G2Q*xFC0w(%dTzRT-j9hsN5Gs3amC8o2s`
+HfcSMsl2)Rbe$QiWwqQa7%tQmZ>^lrexzxv?Fu#^YHZL#~VB76nZk-mDR_Gts70*?B}-w8j=9j=hNs8
+tBbY!$y5a-r(o5Dz|22;Yr*-q1`@#_gVC-C-4FQ?mo)p2MR9)7nwmyYY?hP4zNVI9lmSq?dvP{QeXG>
+^+q{jir6C!oC`NCS4Z^gJ4Oc=g2dP`kH+4l$MV;ORii67bFWUdv~mGdD7s_u9{wtV*{!~}gEq#=YR6~
+VMyJp0%O@`#pl+Gn?jiXpFswx>zPuH65%DSpZ_oBwye&wlv6s-D63+|tyFU9bbJu6e9zY!-e@Y+_VIh
+hNAARjL6}6h<tE%pKh;Ddd_0rEVEJn?uI<4W;Rdtu%$3*#Em|j?2&}tCF27icOsznH2(u;F~PZbP#$%
+a4CyQq(d-Ky<?7zT9}uc9V$X+Pw=!QaUjJOf%C!;g9f)lA-~{5e>vVl9Q?NU(Gu8M>GZ_3I&BS}7PbH
+N{^YfmtCtEp89sgY?HV^KG$YJg)YVlL(F8gLGf4hwgX>PI7)Lvd9v;BewVN_=6em;r<hKP@<boF7<?W
+CnoH`Le%&0`w{w`pZwlLzY7xIJ7$cdKbK<luGUJ#xM?_x@gY|djZ#6=T2!2cu>5=%(klk#INqEB0@aM
+bNq!nOh948GJhjYc0pV(y&)`yz<9#~4&bUDUZQqJN52e_)V_WP_!e34I&-f|o5y{zwdAAqnDV&zVqAH
+Gw>MYtHjI&Znq!XoWOhgh*9{E@NM!SM6aS%P|ik3^#UQEcmzkfm7*#MHl>wWMz5)HlnDxatqf57mmAg
+tUh^gypZD?EyVjAYIL5eDTM)6K#c`0EP70mwjhzc6Ut{j4_4tMO{_9Jl5dcTBJrS5v<@Drw*ujn<e^<
+fktr8Z(hLM)@n`pzSa!M~1Y@sJts55HElPEPsW<95yCfZ_M|gG0GJAOyO7D9!qMy#Nycp-d1!oZ(1xU
+c!>jgzOexn^iwJ|U_n+84SlY9sqChK6|prc+PE2s@_e!<F`uem_ep=}KrdAIy3olUjaSrO2D#5ZpXkr
+h=ab0A%anu^MD0iNiNYiJGYNQ4zt+kmD&Iv^`UV@IygGQz)24~yIpY?Kc+Mo`Y{v^>Ya{d}WoaMehUb
+&((qEoS822aa$AwH(7-GK$hEp3sQ*vNZS;$vz{WV=`luD0ML~n_55Br&hcw!qm@zVzmH_3cOyNF<dIM
+A`-O*gslCscJ`cy7@B9J@?@`y6h|CX~D7&mpf%RX!oD!{UFS#miCU4cgefonz7Eeg05ObmR+&Wmx0`G
+yjfec5~>n2%g1_R(gj1U#WP~E=(fxo<ko=Qt(_78_`{wTb-PmFqIgn3Xa!I6-?T1Aha%-Q(!!N<ec<G
+0hHMioDa{Yb4EU;>6}6M9uQ&QQ9SLOKst751`x0BN*3>hU~B@E-CH)toX>N3#S`v0|2T&#I&Lz4w0<+
+>V`%hf>Mct%H*x-L@IfM<qJ*{9cDT4ACm6_wTDeEUF4?5cOq0*3xy(_#_X{s<Sq;$kGrVOEZ5roy;qA
+=1{Wv;LP?XTjATk7IAW}dxy8p@)v`2IN9WD1TlRv}-?gXCMnknxtpukP%GH}Z^F#hkTc$=c)ra26s+v
+=LxQr**1`L(!u4uft&JuYQ2w?FJ6Z%)w;Tz?=3vChIbO_iW>I_!tI`=6$pHo-vI-_hie+&pD)f8rNrh
+FPf6hA88FhAIunMGA+&pi3a0ocmIOl|8^LgF%E@=2CZKxAfd<?q2=qkMozH4<Qqzy>+><jdB1xB{P6J
+?YbYtcm~?%(#&!Ysl^nQ0!TZ>yD=lTt=EK>Uf6$z^W$bvvP|5%8!WWV*^@P_;OA&Ygfwv5cEN`l!17g
+EdJK01=Yk41*C<Tp_SkHP#R0%{7<FFS7^Acs`zHmlW*u<3O5@1Jxq#$)J4F%~xDo52g&OsC@>lz^R=6
+aZPHq|oJf5~=4s8dv2DnZ!t&h9!+(l>>i*Ab9t)n!UmRq=)a<){)&53<!#EZR=H0K~`ZimmNeMmEm6x
+52|iOVXbv|>=%@4twY{HCq9kP<>LPytVSrFH#Pt)mu}N>AXRHJ#4@rbFA&#$-9lTda=q<^gpS!!-Ow?
+$)vtb|b#0X=6xZzI_-MVs#YGK$~uF<F?r`E>cJtf*sw+)}r&k)MV3*T;R{c8(o3M(@!HlpzT`Akz}#j
+I&AzxZqF#who-l+_KVQ|v4-8)eqYwgdMQt)(WW-TZY)B3)=O4XTk}2)4q(sr{!e@MBf~d)cAMdno-M+
+jEvAs#T%VEXl0eNq@l)^EkZzsRyJeRjrNq8xq=8bs>22&<w$`c3c4sFrw1$wa*X{OJgL)YZb}KL;gF!
+5|FiZ{EU2@pv)_o-D%xt%R$oc<GpGCCq(2X`%&_4?^e;WdLi)l9k+CSX-rlS9jokrRabE`}{TBvt=P6
+8d7Xo@)>+JZUJMOWR85bDf1$zpRgoQhAEQ6i>!hnFwXHG~n?iu))Ge~mAtu9!|iA%}eWY|_5%i)YcbP
+?@h!&#&OKM!7!G7i8uRP+e&oo92wAW*7zJcQil#-YEI6XEm$AL(l-3Hl&bFij~@3v32A(kJ8)7tlpid
+BpiHph*e&LmX~;*{fAbNi*>M~!B!uC=DS(ttt-5o73AIVAy$wlo=KMc;WL-Df~<TtVWqdXklxc{^&Y}
+Lbw88r(<mZ8Gqf+;hxcV9Svy!0hkf|qvVBOUeR#Wy{9*c#KBPh)uCF53P1lSj%}42pQ>H%j&>6FMM<+
+vd+A(A#HmtH!V#(J#|B?A2|4gD|A5Bm0$1dw#XmPmZZW(QX_$={fAZ4Y2sA5P}=84~<_cU%P0>QDBQ7
+bL-n`8}Me(7Yzd8&{t&Yv!D@|(Nnlck5~@4+xz|AM9iDYCO)N=sb%@f|$!{&4Y9kG!21|B*)?_2APy@
+_q+m9J%<P`2F}r-6QXd|4fQC5C10`7Vk|M7Qc9L&?E1*As%^;zy5#GBd-@3itdp|?Wd*Pnti?IAcywV
+gMN9>XHXxAp<a2?aId_VnnHh1dgaMMEaa|GI`@*F?SOb68z$yD&1n(Xu>VUuUP`MO;;C2l5L!DWeD$a
+wUjn0thTyn6iR0L3gT8td=&Lugu#Ng!<xuB~!;qRzUHkqId+LFjBmIv(^~9&CGfK>Mc~?CwH)n{e9(#
+FLz0WV>s`r=Su6k{kch&p-5Ldn53~|-_<#%w^`^i6b)vMH9^_;q^UWx9iH%oWbyGD1_o2t9&O~4jBO}
+#C|r>P@CkQ&=-&U*J=@eiE!?mBphv)-eZI_upC5V2Q=IP2}9&U)4V#941=!dY+I#bM5RRsY{O>s|N`&
+U%Lv&U)jAIP1NjJL{4ErL*3XGln?pJ$3QBI_us04QIW%nzP=l@8qmE6T6}feQesL-g-}<n|EwP!dq`P
+_12q`@Ycg$d8XcaFEE<7-X`?cn@+v;{&uOip2p+;iKpIyQJSaTP5<0eFM*)%?y2`z05=WDi{i4UKbC%
+}r=IS#_AL&3=rQwu%vEpXABVW=J$Z4ss~*<$-CgzQJXNWm?KfTZes%GG>Z(VJ`$w*N34nj@s&|m~hPE
+e&3%Bm3$Lnr-D-&*d`-iycA>3u$^kz{vJp{PCo8E{aZhBYz@7?sq1&(dKg1YIA=xY~`G5^?4PsiT>&`
+~e&ogMX-j?^9XG{Q(a>LDurxuf3ggN}MDzTv3nxUpWG!z4WQzWU#L>ft>6zv!v=tBe1@Q;+PsjHez-e
+&6P)H}>lUU%Av%59=B3sV84j_&@U0L$5pPsYjjj5}tbKL`yyO+O8VnsfV%Xsn>F8O9wsm)>B_PEbE(|
+dXHbc)Kkw%FVg%rPd(bHZ(!~DSHIz@hm44N>S2IPl2FH_ao_aR<FpBD>uo&}Tl00&R}(Y^8c5>;!Jcm
+wu`lu6+xm0V4}&){Cx7l>+5-+I<9-KI4}Z_Y-+uTrL;UO(-Ft7@GJUsHa>iw+)#C=eflQtNXnjYmu}6
+gwB;)afsv}3`_mrEc`A7dUYV}VY`p_*PDvU~82E?bkNdJGJ*89%oJ^7%rYH06aymgkK&WAbBbP?~3@1
+VczIv|WE+y6t;RPX<fWE&##aZL*hffkseYqa<3aCJ#IVt}A>l-kM!=Tc=s?$)(I5bl>;Zx%lWjn+keJ
+^m7ncKq-kwbU#&B<Crqx<eU5`u{yygITLoYw~av@RVHLLGFfh`r~sSj87_o+7&SLhZ@N0-=W=kB`cMp
+X+xHHqlJx@(p%+}5%6ISmgYbbL?IuROIh(YGb0&>{`l>Hz>&L>4Z;KOAy^qnT!U>r`xs#3!*AM3fnIC
+?z1UG3x#lSlEchuRG4of5B#XCH@FrU*veyhUN@VXy`ZTlnM5%?ID9YW`?!cx7%g1|M<VSmPlbG9cNDf
+O|%y?1F7EAbJYi2mO#rpf-|NdpPm=_OP0J!k&*Dzyij-kcsxLN26-ammJtJ63Z;+6`1tLhR;?f8^oO0
+6dtAm?$aZZNC4Ea>ahpcjRkeu{(^e+mwT!yawuX$^b<pltz+$cH&r6nx@Kh~Qp2#Jyy)AKEy&Fzj#zA
+IZXWU;c9PyFGLhqjjMHbO3)yIN@W|4VGY_AMeWBV0J;>2<oE01<*1be-Ww*;vp7hpeLE^flO7zel;?u
+G993QC;*jGDbQ#NRDgKf=EQ1{pJz8MNu6MA-IjvIDkG4o=VDYoMnOR>+for>N?Oa~G=P15O<As?F&gN
+EVtzw!sJWVk<wx-u^k-wT=}7R0`Qn8!8#9`gWKXaLm*p#2;`uQfE%=!qT-MQIRZMLRQ)+*wp)K+IM1>
+;VbFz5Zuh1UnS9n3s+h`fFo!$((XdMVVTh}uD74k+eb?L|_AN-oSbP(^a@E+hSa{fOP&J-KHlF(_NMX
+wi~9q8r9x`+5x3}}88ZgN({%eE0nTI7`}G-PuR_llvW)ep!*CsYvn-QX`Ec3G=nWY9{j8@J;o6pcei+
+5ur=GzEjy(mB@(v@@r+pLgm;W6E4{;G!^cD2N3>l#}8>zc3Pi-o{}bOjHhH-Zb>RLLS^U=xLSz%^esI
+S)=`)L$B1x+6RPeIYR!_kJBZFzbywHvxSQHc4y!(c%TC#UPc}&DnYC6hR>Q+auKC99w3|57!7+-;oB1
+h4xDpT+ZlF43|`dc(Iy2(eIKIe3=J+E?{<;HFG7XK7T_k`&&<+#OK~;f?#DUP)c6Gyc^vg0;mg)+|6$
+JGhVtP{<uFqY$MC>@NHO=lNH@Wtn7a-aNkT{PIVO{ijZ)ZJj%ET^b1%)q+oJ$w9j#4W&?&aeJ^@NIEV
+9QpoR%9|Sh~@3@(2u^>g*8PSh2Cg3?($g<S4W)x)5*QWyp^%z^qJ_7FvP}twE^=3i@Hae0{xCgjR7O<
+4yk$dv5|zW%2co51U+<1XB~2@}{^Hsh54ZAQxE_0!2~KTnObND4TFEYAK*sRJ>yP%@#}3T*}J&nz^*8
+pypEUmYG_qsa?d<(gc&7|M|@GJeLciS#R(A{(k@8_knX~o>|YCIWu!+&O9^5U(DCU2N{(ci5rSz9UgY
+nX)6kDAo)VbP8vvo;g5WD8$_y9x@hA~bGW0W6-A*`OA}O84yE}jxzCcis%OzvRlOPLi+Xk`8G2jj0<D
+8<mtxTn+NAeHL+Jkk`No~$7LPBaZ9=ClxbVxIoKAIanFI+YQl$-dbeWZes!&6PH(>?Zh1ii0MUEfg3O
+#BP=3Cd+n7T6DQ)j`zK4L^^<QaT?Q7bkqo)#a3$*rW7IVwFwYA5lQFN!ha8Izu14mm6~qTGnewRb7G1
+c~1Xt<%n><Xfz<KVX@>T4{pej;nOogAlG-6(P(Ps|Iw$XM?50cr{MbUDM35;jU?=Eh2r$SyyEdJ|SR&
+=?~0{b?Y>D?im$4DkTrJi0RZ}xuJ)dJJ-?_eiylDwN|uSi`HyK{6T^E!*EOBUn?zIe0iZEWPe$S=3Lb
+cX9}oi+>&A?SWXu~d0g<k^*}sFll~0$%vw0PS-%c`aKay!L~NJY<22v;9j^6AeqWRnu0T%W$IWxr3u(
+gO5RaD+ZC^IpzLeL!lqJXSiBw_AI^)@z^Hj!$re+7(&-839q_c2G<$b3sk5d^bfiEe%(?+qc>v-MKxj
++L~nL`H%)J}`7P5(=1jTvd)lRVn?>VnxgliB-Jl6I#dCu9J=T}wC525RImCeY3JlzH}t(px)vYaMUV7
+E(2CMl8!5TmwA9JbPok03GTA{Lvi&_-64Dn0=jl^>ndSYIeC4F@hrM%(HjVTQ4-_c*}7LV4jU#%iA&C
+_Sh3|*(sb3%Ws>eBQeCcyggk=aOT+{G{k~p=@|GjZ~`_CU8%R-3@^(Jk!x9@wGfM>LTqJrCM22@62!H
+m5GXLY*6^UYzhj+qB%DoqM49eMwD{q*UL9lWv{sy}Xz)oiYqZsdo=K$vmI-L;*ln9=`5EGTUp4}Rp!h
+hD2Elx=uzAlIv6)QV126#n&Y^|WQnu{Iz6famj>;v;t&j%bPL>sL&droZ;h`@{qwu+f7=>F{3@elmOn
+vboqG@+#qGbe^x|{R$+Tb<{owXdXjpyrs>}I{Es09}FlUz#t!~)EYcPa2E%F2H4M9cf{in|<c8@eP~)
+{x!y?By5ez3%MgDtq(Jc{+FZqF{TMSr&%W4DZT!L{LKMHVKVtkWlPh60&odwOl0RVUtj2n}k|7NXX%i
+gl^~@iw&d2cKz!UoUm~@-ZIQhSxR&kSF=}*_U3`puNG?fK*uQ$nw!jv4Y@v7Wo{`S3Eh(9>yYa-Xga;
+F938&5zjpf7X@}FWMNn;ua=bm|0dq_27b>+gHL<UH0Qb66gy?Rf?I`2U+fy;_NZ%Unwe#K==oE|bX>o
+<QqE6&NmEh25L)*a7t_IhDM<BJRh)Jc*Oy1?#jwtWmKk>TQy&pKdjqwpFP!pHUEh&_(RD(|*zYsg|H#
+dp5p#6!BkkcUx&Bcc@rki$!-2&rWr>VV<298#_ys%>kmDk|J2~ByfqjVB(NjZha)+tx5h?1uc7-MJXX
+fDPJW#%#~P3+<pB3R{9;J=s4l;e7Bk%1;jEB0cEoXT+)FC{&}u6}MVB|(_be=OhXRX3CSN}9>lef>c0
+D2anBFd>Lknd7dSOYvE@%NED5ZdlpGHwD&D3ij=^rl=CPbKx58y8V)g*bd&j+}P6`D{eJx;eE9F7xM}
+104MXRd1rMhx$iaahUsvmsz2FBkqeG7r7%Y-kDNMk>Bx!Z`;6DCubHn>s5TTL!ris{qQjBAmRbroS(W
+FarLYA}2YQf4$D!_Ryibu$MYa~zC#z4GPBQc7=40k>kk6PMuF?$uhEAOCIYq?`9mM&?eZ;4&AAx<-v=
+v4tQ%gR2CvP(PPt}y+iki}wIL2iQ^px==$i9f}`H?DnQ(sP<7gUw56GF+oP~7DLUFG4%bd^&a8|W(A$
+ht~ww42_dtZXC1v`t^)ciDOTmaG%MHRAV#_`NQEPmAC6;`cE9T7IX=hh?x?=qhi?y2^fLtFH3GJS?P?
+-;x(nKD||^tNaPq(%;bDHXL%<(ynxqMc{B*{O%IJYw`QS8Tw7r9(S3wx*0Yyjc~YGx^=}ZasJ#bXcDJ
+Ln#43y4RncH8tM{7T9PhNW2;MqGjxfA#OGzsE^{7`G>PVQk|q)4V(rd`-V-(@P-88D$}`X;ZfT%N^r(
+<EiJOHc5y30B2>l$aj+O6N?RJ&Rg(eYpp-Ehb7~-|Egsf?%-yb)6m>o<z9hJ%fBGmj!)9MneUIxd?p+
+fC<;+CZL+ajy|gjb+YB!Vnf*-J^cTfKCz%jvch>53Alv==E}Z;&G1D}Pi;gx5qH2AgWq4LVazroqirG
+ufqNze*uV=(V=0kY8w~v6aJ3+1Ztx-%u&|yH>tpwW})^h=iR@HGanSX{I83ALE0jJ&tljDx=CQ1g`Ox
+e7z*!HSmQRv2u{`l@I$g)yOJDsB~ue2#e8WmWQxRahcUcScbdIYA@7?U<;xbO<_ox;IKWSmc=Zo9h!6
+HRcD#(RF~&Gg*Iusq7B4-=eXlM%-~84@Ram@gG<R-nmn7J4SZQnN#bg=C}N3dUD?*!TZ+<V>s3)5XbU
+C#TuK+%W2pgQAuCmWCv=F2B<T?MvAUAm%543N&a%6CtH_CSqtkYwJ>2PdLxfUGRc{_yTe=LHy3D!>)!
+`}Y`Z*j;Yc7jY*@PDopck}q!ud4NmpVIP0}{FqnuZRJw>1A<-obAd6~zSsfm`uA?QrF3Mc?>6;SXKo3
+&7jr>@w^6ZERd}Df!qD1q+7sz7eJ1x9PA=ueACW?sc16viBBNj*)G=Ss1%Z_5nq{ew|P)!au&O1SJ}8
+=`Xg+kRUPDrF0Woa+Pyhsqm9RNJL6qPo*C;k#%iCY^fBQNN=fFt(r*0F+8}$V@Dl-RQ>{G;+n&viQ%Y
+En=*{Iw8g<2m4_W<J>&(UM?PRD>mj!(ddMFko7Q9)vZ04`7kbE*LJukWhtf@89K%?-EkFaAXhoK4(Gp
+QDn((YCLi;F+P-q{)$!Vny&_0Te)?tt6)CAhqjtT?m_LLy~W>zkew2n)#nDw1GyvCeRTltk8CVkX<j5
+j7pTq)LQp49FwR7o9!dGCyyLT~6__P(SsJhKo>V@P^LY027KipH?6+|4!67$UOJ7&^!r!=04kLU@PQm
+X<88t8~5zElFq$uUpxJTrZ0th>>KgF<b^xpfUVG(HM$u5DGz&T#~KEFwL|Th(cv}$~J_J$}m)@3`O}6
+dHtpKnpD~THpsay`V5Sp&L|gmp*DQBR<QW2rHb0{pjbXcC`n-`YF8?KNntn*>!h0D#HssH03xWVd@#K
+qyd|z;2e&3cV_2f}r!FOUuWQ?ePLf3@l@GLs;X->DUc02Ko$N<Nt7s5KDA~DDEuuoPoMk;Nq9{>l5v?
+52!6dq5MT-b`Xb}a6$XY}YQMT!p(9wzErc4@w>J*765J{2fbc1MvB60q$dWu98jHE~`K|(^8s5B0#4=
+oiPmtl<1CGIW`&=?Z9XuoE`fl@!Un`x?oMaP$FZM2eqT?4ObI!I_7n>bXIa2yQ^?;|x*E~c$$P*e$P#
+dc7juvTaAcuOb|9VhgvYPr^qOpmH2YwZnfCa6cGm6q)n$)A>!H*dv2?IAi$kF7$^5UjYih0(q05ou7G
+pq56ZL6&(!aRoacA#`Bh@lA+h-U>O)yzEQS7WPX$A%a=v{H9bdG>3g8tD~{IS>#gHVS+|V+q`p-<%x!
+=pEGS09h-5Yd6^iPP8Ft@1>@K2h?O3AwP=G+T0Z|(QtFh<7ZqFR&Nr<VB`eSq*;&f3bMCMe0Y;RZDq3
+IF+f*hpuIeVWXo(^&YEhv}MBFTE4e`RJdCLSD9+wHaqsWr3H!L#KR`f<fZ3v6Sl8NC?xQ#FiG1Dws1x
+X{`H%dz+S#&qC(c#KTzd;6tya2r+OlOx8yWbiWcco6(I>qciQPvxFS*>GTR_fSLf`1d-B5+=%V{rr#t
+7W|*t`|!xDW5Vz(l+G}mQD${DsCW>)NL*m7n_)ZTXLhXd_yi)!;O!aEX?4F9ZAO42@|eYsS+mqR^^Z|
+;RbK(c7zglF74FByL@ETLzYEFR`u1AB4tyo^$t;UoJt(Vru|~tNbLgxhXiv4^o_qXxCKd4>-m|2HQb<
+4F6({R;|qgpv_Tbp7c5LIn$S$Ms;#AY9Tu&N#8EG{7hlP0I5$gmp`?L4U1+miwVpn%gXNeU=0j^3%Sf
+_TeRbyv3pXNEg!2Kjqe`s$q+`|R8wzWlt`Zm7iYsmFZnKp?81+6CI7VgkozToOe*s3d(Y1JQf5%0#Mu
+*bT=XsP$S@6*?T={g~@UTc*`ou+6+m=NHAYQyUjOe`Ol|r0IaEj2NOa_+OUhUZEK@5m1-C?<Z{{*-6x
+uByJiXra2_y+g%gyxn<%H&HR#a>Z-7}DIbzf3yk4408}olBV(-Ivc+B#h5FaG*%3@r>C~xHyTgIj_2y
+Uc{&s<!EOdX)2^?YXISBLPFlB!l^8TTgN*K!>M%i=_&_Y^{cOCmY%4(>8F{oUD=ox2@>T<)6g+NBE_^
+@S7kC!KVdeWq4Z<sqYOlZi@PZpO{P60JS%QDq2fH_<Z!jrc;19-Ca3nVd;-UX;Ev?AbFFLqhLo}vB1W
+m5d|$G;)r=j2rXBh1cIzCONyT^ToSNY@OjEgzB2|4`@uO4e^z#xg95S8lTya*ZvE5*uB+KqUq*8UX-J
+q;|F?wtomIIqx=9_`N{QY|p){o3_uHdl>^AU&L+S>&xZH>#!6;M3ggp`ukNx502byA|~M;FsY7wzpa$
+wsHj2injGyhnT<+NPq?-gM5vbW@f0*tIp>{e`RV6Z7o(7@np|$GjG%GY$@WX;$k<?%chMZP2lWWX_`3
+g>C3ZN03U;^~k&?bDif!+0E^5H!sSblAYV%-n84nv_~Yq-<mjeTGp^4l$~iej*CF3H>{x+N?}a95e65
+#knddEG=3!GOM?9by9hod*hsLBU^QrnSa7~H;<@u=TFA7Jd6~@1WL_onDw)^GyiR5fnKfkAl382kh=W
+d<!&QeGr8GE*RuQxz=(z!nOEo5W-_C`=(AF+dFS`(q^-6?_LkU}E@T@JH7Fa9SfqPvYJn+Z0HMNxQ{1
+1?C^*2<~!5;{El{&d8)rkk{WZ?!;CkVW71=)8ITqc+o<<RxW-2M)xpGE0i5v8}mCTZX%hpDTppDCoMg
+}F8%7utlQKy`KYvxDghP<EM#b2;m$ts`RqK|H~5f~N`6333TcABei?L}lHHOgA#!$ZSJq8!}zVbS1L|
+nJvgvk*Olnkxa+>bt9G0Qu<v@u#sTz2R3yR<v>{5)P;DjUk|o!_3AFo<x2VB1Y8B`LanmMyu);^sKVl
+G@RvWJPID=;BM5WFj~YK3{OIr#kDsUUlUr7bb~wOzxa6q8-@K#3;%IQGav)?7zbx8yfboXuoQt88d58
+C@ic9teRcZUOXvY|14OOtA$=vo~1B}0q9KO?hqWY9+r>n;?@A4t$W5g-sXBQS@{Jo+IaVRCnCyg~vRS
+rXc)r`08M1$fgw0uR2R=3`qWS;J-y=H8sy;bmlw32LJ>PAE7v(k$Lbzb6r&Z2_WmTi-jZ(YYu7T@kBa
+-$m-VWZ8;a<oecjwVhsN4EJ07n-JND+>Iy*9<O{eDkD)M2o|VVvB|)f3mVjY)MC2@>;Qc{tH&RDOPvw
+t;uT30OZ;Qxn3^d9N|jjHRVct+&j04yZ|m8fhN|ZgSm99D#8dYa$JkS2}y|)?8@TW3{EH=-9oH5e<)5
+QGPDUC-NL0reEb`y(tcssT54DIk+dDkRP&5W3C=IkUUQk15A&AG?1?xXibx4~L6A~8Qu%DS!KJy+ixu
+lOil{LR5|_<~(31+3!fab03e#KqY}j$GebqVhuO3(Dspht5qMIc?7`{ePTJ5%H7HvV@=fb<CPjBlM#8
+s$^t)*f?oQ8ty^M<vVHPr55654Pz*Ms$z$h$_m%BQzGc$e!MT`PAKtK}1z<rQ9k?cFUQ5tqW+B|zU@D
+lTlZWPJm5xkVh;RXXf4bR{EjF)m7C@Hd#icl{g(0o~+=Fbq`FT|KUL{M}_Pwki~<sC<R0Qe32!FhRxJ
+x*x$e{cU5#4z*fbVr>aETCbS&F@k3VpQK|w0lBobgMy<pvZjPjYgXi_y_V-_-dSaD-i>o0#AWq6MTG%
+1za_|hN|T~vB{z(R%*LzSSc4!(gp!29<4jr^g#kF#@szl_&~nItYgV0%%`F=Y%HedTgS^yuy=trWR?(
+zslx_aRggPcjJi)ngE3nI~2(%<}Eh3Q~-z%vp2kDrH0ZU3FuXf#0I_x?$=8stttjSwO8tT5FE^R`%b~
+Ro%-KNxVa(#k0vd@A<tG>3)>KkztUL_1kA1v=>5u5tT+DP(sGg%BhI4{iIzC6ig^HfKdZROG?=p!{VZ
+izeR<H;lkLq9Tc-duN>_RR1IOh;x6DUNn5Ha9Da^>m-ts`%WD;?M2owyNq;-*sm3=k`^W;uOcCst4+P
+iXSa5fjDk-76(+0-igBK#>yE9g*s+)gowD$E;Qe&0yvk4#xtA(JTR&wiC!vke3JO09EuUs<u+-7kt*q
+W>4Zd~D!Oc0H5;eWUN#SG<9$l|g|RKATQ*>On(1cy$&P8#Nl;Z!eu!RVf*qpgKZ0WQN)wV86ni5KPnZ
+`#ZSAtz(XL8eHmr6Cbt%JCL*fTp4mKBO^hf~>j`Uj_FuDe9>N?sKIMwpRM`D#qHQ^ykZ?cZK;pEdYjM
+*DnO|VOE%b0G0gJnUk;8C)<PgRZ-m-NmLnk=dr0pEU$CD?e&^JB!f{dHgbx3#GAti`i)l6)dSy|4Rw9
+uc?E+gtvaVwL;{n?lwT4!4a?mc`VvUdIbKP|}a?+lh-&Yw?M8oN6#J>=R;dlHnZmpZjd|XM@Xd*vDSt
+<ilO}ns;?$SE;Yuo7c=-F=DZql{&ib?1h$H!m29oR7RVJxk+=uV}+e*;4pt49pGkA>8OfsnD%k=4v&f
+f+8xvFNOE>79bFSR+TGY(ob8Y6sw3UazE6l_sRKv%L~rBf@<KV3q>i6SXYd%^`El&+xvIS-P7WAC&V}
+I<&UhakUR`afvCs1_iwISP;B3*!tu3iki{@Q4D0JGx_V345&Unkp(NtRG)N~I+>6^+`rIFWhHtF@i(b
+o;@lEjIbIHwIqwz*5^;+}z|?yI5FN=T3bhz{X6Hrwu$&e0XenU=)48Xhx8s^N%5j=;zom*-YVh<CD$D
+DL$%Pp{G5D(q*2ydRL)R!n}#@^&-wuhrf%r0;PC=B7QWst)m%3!@q$FI^=;0Yj<Jqb#o4<boqf5)uMO
+tBsBc2~|zFQ7x{lu1QJ&Wn#c23X&}PKg403*Da&|5CiRqGW~g+v=vwDeZaEyDeF-rb)!-b6MDX$Wsz5
+rlALbc;dX?m_yEJbv4LptU>ijv7)5YFCtEJ)RL2Esa6ucv1+H~m&<R{XEPxMoiwc8h@T<5z*~hl-)0^
+H|_iv26qxepgoHBe|<`^13fhAac%0zQT`G68|Kqpa*t%$n#1aa`;aH-T1gyvQ%tjpHIS~#MS!ulN&m(
+g7mR)b=y5<*3%#)>IXK5=^-&IKrNwB$k?U!bz23$umgX<@orh6}TeC0>|rmH{yREMekRZ3z^nyTwzOJ
+uQz3^9jqN!qiyW3)9EaN|>E2O@$d?u@h#n<>m-rsN)4X%g@-GQV?OeD9izt?}Qm^IWEk2%Ryldwd@w=
+aLYDfj<ReP=F^t<gqdo2LzwB7*MymAc~O|TmIcBzSY``ziltDP(<}ut3Y&|Cd<oahxctI0T3E4X&oV?
+<ag>&&zpz4XwFC<*KDoAd3hT?Tb{E#Auyz#I6|lMr>uOk?gcYYISbiUF-3N7LxOErPd11vC49hpd3Ng
+%bP+0ei9(%ZqGPXcjHVKap#r`K@#UZAa<-&Rz*2Th#UdB=?thhtOQYft0fn&)R*2{u^VHJC%MhojzLB
+FtG7xW8jji6sxYX$wniqX*0U088bf~BLdVuPo}RY4i|p<3*P2Tp0SRO3|D&23=4EUa#@o)uPTGcCu3R
+SoMG!s-rdxv=(xb(63@0qfhss)2R6u=>EdSXcvKEfv;aSPO+!2W!5tM!=dbtOH;jog||ii?>0-BOcZW
+Va4fimOx=04r^~=9R+JwVSO4_H<5_jn9;V)&M?B<MC&AW*wN_xTcS9V-cjomE4%z4xu~>G@v_Si$wi}
+enj*Vw7cK=pTHk3cr43~7!liyjF0-&V)Ew19>oio3H%CgzRqHfdb}>pWZM06KWS4Q0i<{QzY1w74<kC
+s&lq$P~N-k=xQ@ZTZOLB47I%Uc(9VM3lty8h=(o}K@);i6SU8;vkXZLHJ=E^P?B$o)S(|p<GnB+1*>$
+FgIsSqv&v0C57GWGu+W<&j8Oji>v9p_-0(Sqn7v391hy~QKLx?nr7g<1$Ll_7TG5bg&oErn+nXj#s#w
+KKFcr8%;-t}e5%W?&wuGM%|y+OjkXiNQyl06)Rrbmq#)<~zyxEpgB96BfTj`Bq><3(Mn)%8kGe;Dja3
+L1~qakUf2XtkqF1&@_^y{h(Cc=DuYE>`Pmk2fBIGczjVh(6NLUKAeODaV<Bu$|o+=Rt+@mRa1tQAz}{
+N0&}E}rknV*zQS~-v-wEH@6t!};^^usrw_49?}pA%ZD^Z-!-oc2jQi_Ee?u?iJ6e@i>Q?nI4)m!-cII
+2cX>_e+hBe>7>9vOD)?%x2Ht!Ut8jI_*lgzutQin?kWEJxf)7hemOU?#;Rg<z;rS*=SCIkar=_gT5KV
+z`o;Y=_HOCU*D0!b#PoHQN%WXID_!PE3pm`fhxu`H5<Ws%8P7AeXt6FqgA*bN$ETvf8q5KUBH!u8PYZ
+1XE}E-P>?D{_r7UNN0@F}Ry6aNY9*=7&n#l@&Oa6*)#5ui-cX2SZb=v_<ugHdYTGlRSFo$54QJ9QOEp
+uz80P;GwzgM1TrQy(9)0uZ^f|uFUC49xW*^Y#xuB(cZF?CWjc3<E>LX-d1x&S|PW2&|Kxaf#@+0Yk~D
+9`)1{arluWsF7G(HSN%e*nWS5CH&xipYa+aM+qtZAbkF~ps(qJwjIDfa9_sFBj&)a6?-1*Q!Djm)J7a
+H()IBK0&e*NldBejM7T2IKdt-ZYY!#H^V01NAIGT1iHal-TYNnas&V?PzkDM`;+wH900h!3)HqV}Lp=
+5p}1Srz!>K)iQYFM3S#-X+3agbbhBmK`P{jH`7`>MHrjXpCMgtbrz`=u<3OO#>0pz@%KaB-;UxUYG|h
+{}(z;lLT$)md>}eGRkN`1lD9^q)@X>C#;<;b1_VCcO%im7U6zg|ex+O&y7J61CK6;MCw21rM6zRB5I_
+W@xG-BD9e$@_uPhUDEn_f;rB$`94D&FrVIHOV|fbZB~P>?#<56b2juKO1csyrag8+Kjt+F`eCxY{l%a
+?WZOx$-9cxJ&2cCB5$}CLXADiT#gOOOSAS!!7>V`6hUYWlXxUhI*XDb1I;*iMt|qTOyTg?wRG*c%9j3
+asagc0h9gdA8FW#+~2>Z|Tf8z552~t4>u>@%ZQwZi0tR&b*aFXCEL6hGY>p~Df5KAzMAd_Gg!HWc+6C
+5WvNANqrgEts+C-5VPC3u=(8o?_B?-3j)xJYn=z`mL>SAs4Cegypph7yb=Fc8cqSWK{jU<1Kkf^!5l1
+P_w*_Xxog1VIE51fvKt31$+!La>ow7r{w_>jd^UDIS5EKts@vAdz4!K_<Zzf_Vh55NsycO>lzXJi+yw
+Qn_vT?as37)IrBi8g;Dy5*>SWqmCUJsk8b$yGO?kbkN-yiJs@bYc&ieda*GC1AZnTY56Mk4+C7n*f{#
+jC--4&D2rmfSb&Y)llie&7Aevh#s;!z7S0B-2;mZ*lNUK9!w{dBGftmR31m?!<5&*MV|x1Q%~Dw={V{
+qsvTSBx85Ba#)C76-hj40<?gOluFo#l3gIEIn*@lsP6Da3_tbZduR3{WGj)jR3jrrplLUd@DH~qo27l
+k8HivIM3Q!bfmf*kra5)HsHPUIL%Hc*r*+|oq4St4vYQIk)$6#C5<sf;CBr;<x5rIAOz*=#($Wm5=HU
+LaDaOAG!P!Y75|Ww2-Da5?1Oj7maDAK_BzcbxFo%Xz9PH={@&{%XoMN8}2>T>8tj#*k9%O+L|rN-5PW
+a?Ka-2sx3`m2d}Lz%EbZWF%LF$sx+sBE3uzUZN~T<gAoaFF_m1GK0eC31>Cy$@-9MCZ+b6C_Qk&I4e%
+J<(blGNo5Nh!9C+d?c@nu^6JHm5IDtA+zdgdQs+v2ds*u{n_?IQe`Jf&8c(#UDTiE&J5H`&DfLV;C2E
+zLl6<mkO0;1<o`1quch<Mwro6)`ouP8Q<XL^B63!8&hW7N|!50nb&{*kEa8k;xF$z)}#P_1ULy2z`H9
+A%<#e0<k$^iJ?*<QVv|KF^?$%4Xrx3k9b^ZOh5flE{WW`4fb{BlK2A>9HBpGUEbB8|pcj?xzEQdH9O`
+5X8ri&XV&iom>&%u&)CYEG%eJMs5qf%mNM9Lh~EV=T4j9N8beV1eMq#?!6ON5BRtjT4xq+VIT1`+H9t
+{%Jg&EV-A<5G^iGq?&5&11A1y`hSOC>gSXy$~#3)T}ijD9imKg1V8i?{b;%vbI{WD*0Pnj;m-D`v8kW
+?#}6SS9z>mGiFZRi{4_T8bN}h4(C>H(p`@692mfkp`s*}N2U6>iMx0!F&k!-yVsy$CE_vcz!9t3YTCX
+e=*{A189d;kF!{6w{!pIad5kf;Iroksp=vXVblldUr$!rbxuXW;*v2-%qlj%lg2Qt?l*RhUd29PPvxc
+o}TaLM6XGCPxb=1ZNBaHf#?D4BDQ>WKdsOC@tE<S;UyA@d0`v9jn+CiVt6k{Ku3MxGd_G5&(KMA7#vy
++Szipg%R0Tz%;(mTY4wCQ5s%puL$~4!y-6W5HBzNO`;nl_ih8Qf#C(QIzIn`t2*%P5m4qtu5kFk#>B&
+)Pe-fJt>A!aOGnnjh@-Y%uKR2!M`2J<IJH6Yhv%}7#SJqgvVDy=^&swAbz;Biur3aN%77kM5|F{O!aj
+7)AUeb=1!qUMIo3oQ>n;M(Vw%5{pZ1>zT>Z__3uu<JO2hy&D`s6RwIA)L(H)Gv%Akf+xRnW{_OAbPl>
+Dk-!A_D1D-qUH2r^?1MKSl?)Rz30d{qN_V@W$#{vJ<zdxfNe{JZ`_RZGszDbtpuK#|&^w!bc&_&#<&W
+dNuG|eiRJ!fv|JoEgr1<x&9^sna^zwqKqFE4rJ)uqc`TfXA;m8({-dE?EsZ@vA_x_96E_xkrYe6VrT<
+}Dw7^zkR3Zr!%Mykf`BU7zjVvv=R;U+h0{@X+BSM~{8^)$y-Sd~@>D>2JUL{><5PKb*gC@kdMLrK-z6
+{e0!>FTY;9{@ab}nw!7hs{P|OXLj}uj!sRSRZW{UZ}C7&7uQw~wr<n*p>}TVJ9O;y@FShok9O(m-mQC
+&o{v55@kFoQeKek4-afv5{sDnO+Tf7Bp}Lf@<5Kl$<I^)HOw7#6&dHsWmv1l@OrA3JnUSMLk4b*|?*7
+kCn_gJ-zfJ%DcK-k6@(=459uXPUe?WB1ld*9F2gMH_k}z~wV$$#tPf6u}cmMw*${%;6HJ5wDhso|J+h
+4S?myunO8y48upR=(qw6QO;vH#1){=AKSqkWxylZ_qPiKbF|Vx!)ZBt;Ej1L-$btWxx55oC)MVWTqhq
+w@56m<f7AVoIh_*!$bq!!k2-#vv>@%Ij}Q8QG~hlM@UnhK!tSxDU(M=M5S=K|jtAot>7GmnA%sq&TrD
+`G&A@hKvGzd`_-0SGY;<QZg)#QcM}I@0T+ri49{30^5do;%QP`JiB;CF$r`Ivx{fXEN<iEL=s&|NeNY
+eMpGPLJeQ=io;?EMlZr`RBcU!n9=i<GJ&WfSQ!GYeU3z-DO65EyH+OPwZasN!0R07#L<yNPU#w7)>@5
+F1!Laru{i&@kQ{_J<-1`XkKC%lV`7%zJVX_$^W`fe%9cFoW29hLO(7<hwy#A!PB#@aaukk2uVPfWE6l
+(!^VubWN)oRDwQ_4G6tPW-h93i8R!##m4iu}ilwXQn3Jw~hm)wxLcDmW^5DflQjD0wUSO8HE+=B?zb<
+f-I?bW+5sL!OwEN;pIbZ0p{!0)-IyHf~8`wv{bbp%mAl6w3BJPRu3i;v@*%Y~ND^EmD3;K1$k3dP+J<
+e4L*gl$8~fFPI~ho+s3C^r&H(N)L;R>yw(=XR62~o~k{JAVNP*pEXu5DtBN`fz>`}oFNC+1bwbSaSSt
+#H|86le9aVT#2B+hMTX_(Wr#XV$r~r?Pd_%#n35-jNy!#g$_MEqU8IS$kPgzIcvF!E;v+8Nu!IzYF)u
+Ymq!E>uA#6j_jd}U9TarFCTQ9#1HA=R)oNVEcV3f@i1L7hc!Xq5Qz+aT}fA_HPC&RMP9t<RaegWSHM5
+P!~GS&LLyqr9BMz%UL#h5)VU7r`E?v^hmt$Bh&5iTQBpQ<+GsB?1l*=qe1{Wzl`Wo#xYV^}sU@Jpl4O
+P!LfPRXS}sT3ZyL!DZ0n=pC$lvH(EUQQN;7SUPPN2R_jCTA?+VC^c^lZ^VjsTtYh)oCL0JN)zY27`?^
+%QvJF38}{1%#3kT2*pSE<roc(dy*d&K;wROc#k8*4SIF1)Z43#`N+CQxBMP9WiwuHprXkzsK@1`>eV@
+EN@&devGyhPL6K8(X;6@2NF_$$v$9giMx};iiHsh-sf>HTmY=R4n?jD68QJ>YO21#1hCVMVBRfSD1d&
+L{_L^kOAw;R1Y^v#Q2@SQ0F2$RiVMs?&<r`A+Y@F{Sxw>)?RLTC0(5v7q@tkcs^}?p=MKRj?%f&uyXj
+HF&JJapawTC)I#vG+zFXcw?=<#R|qKk&|$)Ya1-cj6*#JPJJG*Yj5<BeJRY(u`fr@nw%O-7nppOtHv`
+q;hm8LL+-OqT*V18k=-h$w_CeO3<jZ(Ue7>fyW4xZ6#vNY;Oso&oIdKiJ#4{%4(#orWYtBz(4+9sRn=
+>9~KzT<q{(!G~{Pq7xo83UW^Vs6?vJsiR01g20nMDnBDDH&dUi^e3Z+=ri>x`T9}evC#>_D^~XDotrw
+=CeNpP(cGyIV=E>#juSpNg!!dxD(?&3boccC?vZ=@JFD;QKdj5W{YQ4ax4+iCzW;t3|9^GUu{XNa_dn
+bqe);3|{mUEp$9vy9{=UGw=MOVMtFxAIXR_aoj^n}i&S!eez5TC^xVL}8$ol>#8u*VITZh|38+*fWkE
+GW5J#J%fNOy<qyXW`hBpn+w>F)kt=jqs~ynENnE<=6)hUFPnaPRWCIrHBBM@{wp8`5`T(Y^Bze&OEnT
+Q0eG{C_RIxBt5L>ih3);Ge%<^2hV<f9u%gU#+ifHTGZ}X3YJ1o!d6-Q8~gIeg3o8<-fMqrM;c)c9J=E
+G$#$0hMia%b&}X5N16`X%w<U)ljgT<EejXYkunvgb|7LK^YUo;Q&PO?n3R%Xh|0-Jpy4)CFUK8EA+z;
+)6gxa8J3l8=A8yFYjHKa`r2dCkSbl!S_-r|ap*eL~vhR@t5Dq6H!MX>gjXk-><)j)j^-&~^3{1%)#Mv
+5$!H^{z#eKX(oH5go(Qm3jKQt#PBUK-sPIH16WQwfe!f1Yjv=a=e1E@qGx!T*yGx<n}&y4j#`PdL4h0
+9|W79oq4dO7M47M_`tue?1OIb>jDthZP1)J&SvMA{`7^5{RB@J<*In-mrwEj$j{574LNiiHXGg|JJD5
+I?iXY!ax#^D+#SMP>rwiUkN3=n$(<DbU~Ho*_|@M|CiCYA#A=c7vJ@W5-1L^=lgoH;S}aSKh}Qhh?V=
+GEyU_jH8Jgg`|QvVDh#G<#Bg!hv@tW{aEAp@%p^@JbgYY{0IB!Y(h6BGvk?th=*mTCgf&hhvyh6+$Ew
+JvSEBq254uWW3FfO?&dhekU1=y#JW^=K+q!9CY7$eykZlUnwlplaI51OsXnMgx;Z6L?t+6@4ycfPq|D
+hhlo$L^er%jno+NS_b5UGL><D3#sW;qF^cx%#GW9eMEq53~b0Cp8OGE0gDyXy8cXBZCUWy%RJ~F!?BQ
+GZ#GNn|5)YMuzDNH*w$I8j<q-Y(D1ZST)4z;dd?Sb98uC*6$))lYacoXb)g=&RRqZkwQsnOXq5y}#+G
+atCy@c(BvZGLDv=6TUHg{rcRP1U5tep70b@%lW$V%g)Q;-w;Z9wjg|W86f!yesbPODN%~qTHwE8}wO2
+GpLO6MU9Y+JtwzaI3%%9huEAH;_I<_DS1<g{-c5)hUldtctzrUsm;dIcxCfWl|VI=WEYib%uh#R{fud
+8B<s?wScHm4b+dHX!w}aW7!8VygS{DXH<e>Xs_30k#_QP}l$K5J;YcB5YI5?T^jLe)EA1cwVI#9s2c=
+0|5@TPt&Is&joRwNAwG$efByN?lPGY?rV#DLeQ&f3U<74H2p8q$VavWPOo04Wqeq8Sii#-~4cRJVexB
+mxm`_J?L{|Edu@S^UM_uFreP~Pz+xWY>gvI(6K_W0oD4ID0j!#|;~!LfqS*QjK(scbfr{hHhOJtW74=
+_04=D!XA{(StI;{ZZN7SAGwZ-^1nicsb4x**!rvljQVA$a!bV?pch9-4D<MU@uahjB67)e6fu44B4D1
+`<rC{S#mnKr2{*5UX$Nnm+@IG=l7=UzD36GpzQy(?C<6*m4#Af_}ZhmZX4F$dh8mQf7ShN{(m+6-TeR
+a{Qc+8_D9$^cpT(MiLW}XV>PFAEcaU-%llp@_>IeRdpv5V=iA%n_s++<`-1<O$Mvp{jQw4o{~-kbo1e
+RDizNTMYn%Re@%|^D|JQjye*Q0K1>ECfXYZ)OZF5i0oH|ohDY7@dm(17KNal>?XRy_m=?aU5xt79LtU
+9?$eV24++?Gt8Y;L;G@L{sKnZh4hd46RFc44Ju<;c6IU--JP4JH9-@X0YJ)hzksgp*!(hCg=VyE8W#^
+DLBNVjUGS%iKyGn?+DeFpXdefq@{GAf4c8g5d<Q1UiBMf+q-i61Wqn2|5wD5wsz2C1^pQB5)*N1hp0&
+yG~#sI74v4B85Lp=3atwf{g_02v!g*CRj)?pP-l^li+EBrjB)E_;7lUCm2AWBM2bS5cEXa1S*1>A9d_
+J!C``Of^`I|36>HpCYVn!i(ndofgqD0m0%RXP=Z*3V1k|mod{gz@QwsE7l|H%F9~)LtS4AXu$W*z!4!
+gcf?xtX|6Q^NZUp)#-|g6)xZ)59<-vGebC&4)f#|2WxETnKfyQv#IbLrWbQE`J$3KT6`H7(L!#my`fQ
+y}+wk@8wZDa@WJn7Wr&eHnN^H(2f&z*yqZ}p_<NJ}Q>D#e&{&6TDnHsPE_ova{t@WO{=BHm##;cp=m^
+E3)njLLB%6LT?*UBh(j#EypnjkqKIryFs{GsT7>Y~APH#r@^GxUap7`-gXN-+LGL6L)d9G~$lFt%lrH
+UUm!@UY(aWJ6`p%V>m%Zd3W=(V{27)@8x~%Sh`c)d(Q~+?^gGIID_6@>)xk4L*X6k-p>@;iG6I!`+y=
+l_LNP2IDS}7pc7asJ1`r6diU<l`uFe8l9G~GMn(oR8jVs#6&KTgF?;#tm)V<dzR5oN=p$BMUe3P$`fG
+Ok_H8CN-C_}#UT~ZbO+=sDU)+MZ?3q0Ika#`BuhbT|sGU4{+B5{X!uQu6;`^(~dk?*<=;aE<rT58`@o
+qV^fB$~*E~XVK`nkeu(}e%zX;)R3DKg(JzfV4-szLm;v|8!C2;ui1N=rMmM}DuB{4>eD7VngPE&1c2F
+hx@O(tBpL2)}>-q1}>0wMhSa<S)Hb{+yc+L1_v%;8%O-=J)h=1O5X4nYHuw*Y3Z8H1K|V$f#%vP@75V
+)4S8B``SFVbpIhDAr0lDI`Qs|)>ouC6b?R8{ZZo8Dt7-N3NatWrK)}F70&=EfOxMtaA<Xxb^AqF0J&c
+d3+6QxU915R{s&hL-Ct9CsJL8&Kg9VT!?xGd3U7p`@~vE2!-cmfe<Wd%yb)fU3voj77U>JzByXmgD?g
+GqtL<D~bjG1Llvtp%4LA_k6*H-$xeM6I;cVlD-UW&Xcrmjha3F9da3Sc-!RM|rum2gJ`}p{<xVSi$kd
+VNhdFGjVJic(@LiYCCZ?laXHwqp<e*8GQaN&ZL&vUWdvYz_T^;?`#6VK*8%QvTOXGPnuY)_k*!-}Yi(
+zbJ6w7n{sIALPZ&%Y&aCQH$47e;b^D{cC|3&VyD+nyHd^A4HG6JsU~;(SvwFD&h`edMrYR>W6r-!u&A
+eY61S7jgbmb@h5CxcFJd3Oje^uFjn~bC7}E1C%h%GPp?Pgfh~Ja=Q1|Tiv>KV;&wJOrz1TkdP1-78b^
+$qoYOH4IVt0B_<}a5hF&h(W6JR!qg}>e*AbgVZsEKm6gTv^72IgF@5@U_PQ~Yz4J^6`=C(EOyeWj{E6
+XgaZVU}HBZMrnGwX^Eskcd%oxkIyqwE+Klda%ylNurxs9_Yc5vpoo3qeeocVsvS@1#5MjhZR{7cTBJj
+vPM?>WmS7<P`ckw0=a?hI#RFLO3y#tc?kTFMqJTEt#@=_R&g$r847=~A|0#R|4&%^Fd^>({Smn>K9{e
+c7j<e#(yRT+e3y!r7*)oPGA$XKdfTeeA%21MJ9=BkapBzhoy*o@C#C`z^b2;RHMOD`#iVo)vsiSy{=h
+U%AA7xXIbot5;cdb+zCXJobHPe28bP2{DaoA!E&mZ<<qMZ%GYU%^zeT+>?#ugV|I*iM`Ahu}%Da_9Z`
+}^rx&1#qUD#H55OD;>S|_5fpzs#h=1kv3V4KDaC(};+IqWqZI$VP5kZ@Uq|tuqWA`izkuSuMe%n~{38
+_q1jYZ3;{QPLD=GdJihsi<exL4)#gllLOVgKy2FBJepgGN3G^XvOMtHOp<0m{B|8_9r=O;0KX%XXB-)
+H=WP5d?#zYE3JQ2f3WKaS#4T30hD{#1%TkK!+*`2VK(6%_v{#lK(^pM;NiYRb7JY~a4I_01@L3!I-p@
+zoD9-k0K!qWI5H{Ff;HW{Ur%O?=jp#j|$QW*=n>nFlpnKX!;X|0;`Sywy{Td*(7ecsApcmNLF*1LN=S
+W&FtZHu2RI-;d(QQv9(Le+tE4K=D^o{0}JpHj2N8;!~cBPE-5~6#uF<{u7kKlaxXRr7(w5SVJl7q7=?
+j3Rhck_Nymn*9UWUV-jc8i#WUaK4-U%aBKXI6u%e652g4+DE>H#UqJEaQv4+p{|$=2k>c;5_=hO|X^L
+NE6JI5lKwFC6k>Wo}@w-#}Cn$bjia(s<PontqDgGN2e>=rLX%oNxlPo>9PT}GG!uo}Y^?ermc+YO`UA
+lDdZ}ko97ZV*F5fKv}8X6Xn+^c7g9^Ksg_wTBt5D`t@^dC)@h=^p5#}J@@f3@r%6BiyC6H3WPhKGgr3
+rl_i0lIbT@~G?|9j6P62@8)1r})6D_Y)o-eUs~ujERd;o~Vv)?UH-<mQrx<@~FCNf5fNsV`8F1W0D_k
+-QJD-DL_v-K=)*d9~y?p;W5b_Teog6`3oG~yQm4tXlwe(ZQHbN{bYy;;2{D$sz&_q2>6D<zfJ3hpOjI
+HeH=MZ{N&KExPdV-kuk{~kU)F4cI_T&(V|7CSmXfzIPp;W9qI*0MlrxYJTfjiCMGU2u5-Nr<lp0Qk$%
+6ph?vN@h`6XnI<%Do!2hwHy<(Y^ez?d&DYfKa1dz*5r0=PXqau!rjENIOh#ZviC;XEmJas@JhSKN|71
+g?RYYJfXPxfn)tm_j=84M)<N1~zx3Z1O};Vw=t0WCF=F%fZ8%2AyK5f4Yn>=7Ci7Ut#P;-m>pj!TY<j
++C*CQ@ATS+^1i7A4j`p17#1CRdj@PrW>6c*vGYL6Fa+B0|-N)7#9;tj8x|z=I^WVZt9?VFq%BW`w=hu
+_=Jbo`6nm$3mc?u>Fnf3*6?Jc9vxAae{wj24AMSe@2rD=cqp-J46E};`4EHYnzw2>C^<YXR7<?Zm~Hy
+fGr+Y~U|blLW3-Jo(vJ|{<nHesu8pqmE%6VfA3=;59qAj?z}qH$Dw^o9pa$M7rv4Mxz?<{(q8gXt&@&
+`Rl>amGTu~#?s4Q2^LL10)_m)!>d2Yy%AwnK0)c3c_9&b+R%jRZ8vX}G1*~XcXZ2c>R%zrP*6x7cR_?
+olCbCS$3h0GUUe38BS>Z@$ovSsY`*I#F=SFaYb!UrFGAmqW1KmJ(A3SX7K$zCOSV?D_VyLRnj2M-<;v
+cdUt-?MY)&aw07&$FtkDt7tuW%lbYSA=Zv+i$<IKmPcGEvn}1eR@A~%ch_0^C%5;{xr~0yIeSq2D+&<
+&^<>3-5abG-^e`qHa3{=Ws~?Zwuql*@AC^ZeqXhTPXl5+&FFJ!uwD2B#rLE5I*K1d@kdhpNfdt$#a~A
+8H&Fb26u)kq`;VORA35d!Ryn1E+y`{%&_OKyw-;K+4sPB0^y!1kJUXa5ba3nN=;NJTTef_J_^f@Gp54
+0j=+>u|tIJ~#(0hkRyY}coVOzO&?&HzNgLUfic+Wl$H|x`i0<?VKfk!$$+O21w=FNIOB0H$nJs<C*Qn
+lzwaUOfH<pUku+CAQ<xk}ZnsgqNS$6Q=GI5z3<_`}Ur&EU|-wR2l%JLfKa@I2hq3F$xNX5X6Lns;s1w
+5bQRuSdIebm&CB6y;%hr~ExSwdv8-qfZ|XT$)JvKknh-?&0yMhoG+$J<vft`lzJoC~{#e7>jTYGTd#U
++Ri2n!egUEXK8&1*L6KDf~fD=&)&zQZ0TrX15R?T{}a#OS*8cR*h0~^bLY+|Q~*Aa>)y0!Q|ej7alKS
+h;>a25Da1AmrNsZK$3afF#eIfHxng((`>U5PU!Fwc&L>x{T)Fu3&p-c2W6!yte){PGncrW(e*Fr~OV6
+J^efs@9d-j;^?Cd;(gM<6XG`Y#?U`q<h;jfe*>KX&{gWbD#?@A`hs^xvgbsITyWT+oKWbgR(*Iz$J^J
+u<(`*!~0k3Vu6Px<lV$FV@gzyJPwPV;XLf1-iYIQy-|VyPrr&mKK`^tFnLilTn~`gt>{p0EX{9h0`m0
+2kD2GZ|o4wgh48&BF+rm6hdPQ&aOhLpON;__wF>K16NM2Wj~G`*#O^gqOho^Upu$AAR%@C;a$_AATtC
+zHs3Jzj*N?KYaKwKXT-V2!}W%Bf$MU#aaIT`|nTRvSrJ364@e6CextY+}wx-3l_waTn*f?J;<Y7yLR5
+CrKL}Kd3mAi+#(_(dLrGwivP81*Eq>Ci@J90>Puy~;L@c_yu7^JTF!?K9pc}7^Nqlh>PO%X`<XLm_^D
+H;#5-tGfMgzF63<=#<(FS-i0^JunwH;x|DFH(>#x_o`s%BXe)!>s?<udF2uE~2PwmZ@a9Du2e-(d{r;
+wKwfWHEuK>^f>0>EFPL$NE+kQN09XDd*hTfx>H@OSXvxpU`ys>d$G6SF|WjvYG${@{<3Cr^qxID7W2c
+n6MX6V!%<dF<FR0cabj516Pov>n9x;)^c?{-{H0{~Yyj`}S>a`_TI>a<7FywPCNng8z*hH#iMLg8u^o
+0=k2b0l@#jfdl-LPd?#P=bU6_(S}j}sE>X7_6Y#)d-v`YZRNy?6T%J}&<B9u&`wZCz-{x-oa;|=9(s^
+-pM9M7`3(0AbKbk0^C6#e{`LjVf4gzx7`2s7e+B<-+qNxocX#(C8GZrvm#7ax1Mo$eqg+7`>H@gKj=K
+NuyYB?RjxtAi17Gj~XaU|Rf7J2H%bZ6X=UjJ`bL}C{{r7Y3y_aa%#rfkzL-(zmclns}1*bW$t*u@9SM
+aC4-U`590i`Y!_&e<c?Ew6e^d0B@PjDXo6_r0|pgQpTf^#pTA^U62Z`5+0c7$`yHmhplzVNTEt_Cm9_
+xASgLcA~wG*CU<K?`sJ9|6b4=s>xn%uz?+8}L8+U7E8Aga#4*Po9!!==UY(eF@hfqQRH=CijGdf5tJ+
+-9N3rf9=okr!j*o{Xh7h#v=}BSSJdfqg+uJ;4AQkLI>J7_zZlHcA=PP7kCoRaz2n~h#~%u`dXqP<S^%
+f2P7Ia)E1uD!FkW^e;t47ix+k4*3FmdVFB71`j*D%0I%PTzfk`0$GCua=x1!7#PiffLBqG4M-vT^M8o
+=@`29S;5WI3X{BPd8$!VOa2cSU#g^tGRq%k^f-MYn(e6)<OnHIxenH0)j%+U!Xp1R2Supc-de1`Kl(D
+04io{49ImG-<(YR~ACx^I)(bFWXOHcdV5{=4B%^*rCl$EOSR=d*x6_@p5n;Ct|OLpl^%!0X_n@Aq%yZ
+%z*8tBfK1wfw$<20WuHIUga@5dS^rv4qorZ*1B#Xpq};Z}dsjr*@-0=~1Gg^M_L3O3#eO@bA*4i<SRr
+jK?^CM}46EVmwgl0__3s3Jt)Y`aNF$QYwFEYA}CeatL2ZG%O<;@T5@xIf`f)PIwGGC()p^XV9RuXY@&
+)d#K!Za{d@-pz>FLMD2gCNakQ;_)~u`_@CO^0?0#Xa|#^_EuaD8!QFTZ_@kfPG$V|^_pFw`O*E__8eT
+U@_~&2Y{OKy9!NPgc1*tuwPl`EJ*PcOx(w;$s(w@;L_54I6Q{51Mv@?t^BxhLxG$_!Jj>g)ELJOWPMS
+*<7v>^W8Gg|)E6r!OZg#UK+694*liH2mNVdRfQgH3ymk=t|FF{w`qI3SJBN}tq&Xz2P$qy3V=pXB@bz
+P`R)NJg0j{LwGM1kZp5$UF)yaKl)u$Yt=ucng^U&&S1qd~;zSf1hZ0muOf^G<>=whhM)^d0(APCDEBh
+brPVoXIaMRv8{3Z(O)S19~2bS9d)4q+AHt|Zoplk1${sId(3OlZ(^*%*oJ4@%mDro(Xgp7h_9cf<?D!
+shVA(U>XQ~_h4J|l`|-IM;oLMnoEN1=aI^6#{@vGy1r4?`MsHchxEub|-wXaHd=>z2-~|&n0dKSi;H}
+gN`gmLU7~?N!1pn`x+m~-88a^Z%HclrRs4bv9gNBtd4NGJi7J`Ne{rH@8q9HAU7p6uE8t|Y`3bK(gd_
+QZP|Iue)41*j2Si5#Df9IWdIOHM)Z0qD+?c|R?{@{mSOW_qJfBxwVqJi4N2Smfa<@Ss|>2+gYzAUdVe
+>pdlFV5ES=Q6{%nP`|zG|VI#rV|Z#8p;@Bjy2x@0Ds~I!T-e9vp~!2*|Yh&b?XE@_d|z53*uUie86|i
+^5^A5!zV<8XwO6g`Xr@2ub@6@X<jIQDObmzCmI$I4fBYG65^v7dT|w~=##>1WQ-508t?yszrz1ot+qR
+G0Dg1l&b89ekdAxN^2Z;w{A|T5{ImJte7h-te<HW%4L0q0wM@gSG7bMCK3Xs_jF)CeG~f|3293|L1j~
+LD<L?*j&Zj-<oW|cpg9i_e$<EIH9Jr!Aq74)l777|{`RmW>1SZ;4!#P%C&)2)&v&tA~&t-CZZt&bw{s
+I1|`vC(6pdR>JZ@ndCFUT^brKP-}pukGQz38}Co!~*Aw6X9$+B4D6NI!G8r#;GP;lhO#)OYe8J$mru<
+Yc~P%^HqzIX^$28w>_MfBt;YuD|{EThZ@87HW(Z;Ek~XeHCOO$fKAK;6Zyn^oEhw{!a6aRRz}etjHKk
+<o3Lf+QOgV|LLcnx{$0hRKdSjuU>v>X=&N1si{@ekBWL&xpE~RG-wb{NJ!viWo7)u7hmLy7cUn0V15s
+LK?BADj1vkSn8Sbu^rzrMjD^T!mpOtTeG~ZK;GeH9sFN`YBpCzk8GX_#Q{sUBpU%I6gM&-s;^Jn2e~8
+x?(Y)Vx)22=9s10-S<5Q+g;nXHL-k*H(Ng*SA_St8GAAmb>MI9-8rPK%L1Mgv;1Gx@$f%bqlE!s2HgK
+c|$t3i9-zhyb(z*p~wc>)6i7cW_|M681#Z@De=;^JbSn3%}BcJ0a^d+aezvbDekc%W_A0%%a6AuRy#l
+#(|3)&tAN@=r^2G(Jl*2FB-irib$_&yVM)zSzuf{Px>1s_*+=e|Y)jm-(z&v#u>#w204~IrF!to_dOB
+W@d_Vr+KB|CD4F&0Xl&j=x9g_)*vtrgYPj$qpe^)0`o|?K}JH|ftE%dBLC)hD5Jl+{)5QWc30%)=6?U
+|v(MfJo=Hhb0??)v8XkZAaXxwSWKj<oGnBO*g%02g{FOCd%ne|oJ)n)CJv40Z7_)8X9+*F~zqtN}wgv
+ttUV44Tj2T~|oyNq(@M+VgiFyDHz`tL=eth`w;ldqt02&kk4a&S1b1}%*;CtW+6FdakY<UYZ6~a?r@-
+KIHcfXArH$I0s#NWXmya@m=VZ9k{zzb{6Mx#-*-|+BouG8sw@7}#dJ-{Dv8q(1iZz*j<F~LXZ55QOGk
+5PBZ`Uc?yb=&>wF9iNX_k2G;zb;gUv(TQmZrv(qK>1+ZklKf!Atfb+$HvAA-deqSwZI=`jWUNFc%vPl
+|A9MXQ}7RDZL|Zl0kjFEi$0iezfI}Pa&vRjQN6F+wQCoyD22|)`F`<7zP9p@Qmz2Z7l9AeEhpImyNX4
+7p!(*-Z-NIfMxl(AILdk=`cafA`u`0ZHt_e}drypk|Ni&CMLd)>m6L_~w$U_3P9xlRkQ=TQhu-6U^*2
+I(gZzX3pW5RByraFr1TLs=v{~X)AxmUsWr@C(+PWCu(AR*!6+T2eK)XbpfKN~d=#y>bA=r^F>In6L@t
+o+z7h32yo$eENEB>~;YRkj6ybaty7kCJ5Va}X6ocb&=PTM}fO##F~KZCvq5BLXjA^>m)UCWm*#~R}bd
+wcu8in|qmMgFJqo&_3|ez7q+P~PZ+XzUjA1n?Bb+D6v&1^%cTMTUT02Ye4+LYboty^PKLf4$u6;cv_T
+=p!1Au?=Z~i8c07qek(UUV2IJw{5vAfN<a?@H*Zx1^{<OKSFZtvi}0^0)N~2FO5A5z?T?<K!>v4kNyL
+F9mXSDI#9NoH*e;zyz+|RXQd4TK)13s54j2N;2A}KKz-($4h|0g72K`(+m4I2GK{TkbFVr9tXQ!^#0O
+mn588k)cnf_eXi;z{`Sgvy$?tdI4_v5y3I3<?dzLc)x)&|Vnx~?_L*2j)^`PkFP-p0ipi6)b3bX)s%H
+!|!Gxx;bcKt`0OWmtZAm2m&!8`PF0Pq&f-Me>-eivnc_6mLn{%8lpkE{Pd+y(x&>z~w5Sl53*1IA9sF
+X;ck2N;hqcA|_i=S10~9+dT6)C1np=cBJhKSpy3q1Paq|7qL>{@~qv@%i2O-L@}+zp`$Dat4jyb>gGj
+BqLa;t}w24_=j=FgX2gZ`fmp0fqf7E$k9E#m<E^alPA|QXMQ_$^5ip&g<h3Rvi-xRgKXN#Ci{m>y9Vj
+o<XbG8<^Kf!MY72y(@r)WWYbwTU1YPfY=+8ak!*6ww1ZjyyB#(A8xQvPILqL~?jb8;y^mlY!Hov<MZ8
+0!(r`WSZV&QuBiUC&V}6WYuOCeEegyUT*oKX5XOOwuG8;Zc+TYf2UT}`{0mmgB8`grMQ}Ei!`KW`OAO
+BV0axWY}YbPfsCx4pTO`<kkgZ_+U&floNt)zbGGWAO*Nj_gmvh4(tf$<H4CqWmIH?E97FX_p!779I|R
+$j~f@*3xde&IZHm&pI;d*uHx_2H{1&l+2Oy0X_qSwEL${@VoCsLwq{{pQC=7&=qv8=>cdeg$jdSd0DY
+I_HOfy)RvFXY|uEXU<Gnym)aUaDeQD`2)sB^y?Ul&{sp2g`5IE*zt_C)aeVM<AJ^tYueE7U`-x6wm-`
+sa{!uyX48COf-=9yJQw*wjz*a%a~{Y|wh#1WPyHzA`=Be^c(t*mi+azM2K*rMx83{DaL)tE1mo9F7f$
+jwO^IS1bAh}DIQA#blP^hn(}Z)9-dEW_V9Srt4?)iuOy*bhd9pLc6q>)Lker)nyAK2P4;dJ^VEp-HNt
+Rdx!+J2*<dysZV=hU0Pw3kOKc0|uZP4+6AEBdyt`mB~5nqU?dzJh#j?;WUd;0Y06KwU}iaZMwvh{m~;
+bJWj>$zA*w9Oy7SLlzE8t|h|*3m%M2OSgm(POLBKPdS_{;}1SHq^fZ7d-1_{luyQX+8G(l}miaFZFQ%
+KPEQd$52^k>MiqQ13jI{pXNy^OO`B2w8aB?gU?~&fxZoEs?aZBO?dN5xqqrNy!1Qgd4x;I5lPpEHUWL
+7ZT^rercRxjT~t&w!B+PQ9I$SJvcQA&b?BPjmGynlGhsax{0JQrbQ>mFzcg)}1TW6a5I6{aJY1*yZkT
+^VU2sDkFzUZzPN>j#L05z|O{|S8{D^gN=vSbd5IQb7|LLhwBCq~7{1~#oe*HrxD=jTeS-Ny-BHAM6`;
+gB;H*i52z&yGtL+A~mufn?OJM!B0a#_ECHUa$zbcMzG2x}SOvF)RwyS3$iw5?~Jc_y3s{|SnI0P_OO?
+NBD@dr%hFE#L6ZpN|)M0<2H3m)F-<+q4Pjnar}DtVqTIkKjkjcND?t>iX?pnV+E@Lgu&Cv)JnHP)>iQ
+Gdi~8-$KU%UEM6gp-|Q}+B|pkf5_i#HmAJy+G~l>4M83U9?HC0**6G0P!{Nq;D-nL+xMQSYZHy}qYbY
+$+W$kwo;Gb-_MAC$vQQ4sJ@=gG_t5WR>_h(#UO;((7m>HE&gtiKU-F;M9j7+YfFCo%th%y$<&QB1>l|
+3qMHyp$MQzc#mflborqm7U;<H8Z{Ob8H`IR4z*YTsFjvrqw9D(c`9senPdnNzG#Kg0qp`l{k6}st+j0
+`?+-aIkC15Qd`p!AifAC%2C%SnFl^@;o=Q<&Hbpz!0$sZo5x3%R`N{AtebBLDR5+t*vC(@g~(g@uJVW
+5<r&qtR&i#EBEdI`D!83&h%mGWS85!$kQjdmB+64aa0WNRz@HCEmKH{MV~juVK{g#XJyWJ@hirEs>nX
+M~)mR=)T)pbHln(#&KnDB_)r2&-ebopUNM3ll*ZBx<srSM@L7C^;h69Y0@N7?&uE`9f`sN${Mo54>V5
+HW@cuNCtl7c83`YDW6u9)bE?iLD=Pbi>FMdaq2s|C{jg!fIO+oWH5!+#`T~@@!iUNp5yhmj>US#7XDO
+}aG(Z1|NuO-|+0$8B<3}2h?NHuW%LNZ&EggCdv=3W70_p($0^$J|lm+^2^qIhO`}Xb6vHR0ei1t5!{`
+{2JUw=JOnRBC_fy4Om<GF{22Ol_apukO8_fzyk2nW7V`U~t!+^}H-j>K$xA9;)Xktg~elgTsz>0wPCV
+?O#E=oUtg9xZgtXcq{J2W6nF@j_mPP6cv1(XjL{<ZaEr;To3hngqt9yu3WoH(;--vaX2wLRo`u^vTDL
+9ed+1<}LC^KGfHU{=YFi6dCya_uuEsmMs%&#poNc=A`Hp|7PAIf8?W#zu+a%tH|JJdx~BXIDp5X<G}j
+UmMvS9eS&{CZ!7)xvj&QO27MpudHwqJ;J=@!EJ~TQe)4x8d~4$RHv#SI&-%B!KDC{TL(kaN`}P*_XW#
+7Fe!FXp$0TTY{>oqYZdZL*JZU`ad^bPh$MEILmv3CTa^(%`qkdkqX3f9ec;k(F{m!~|>#k55Id8xHws
+5B~j;mI!ie9l|#kP0feOJ_{GKQg^sZAfZjSqcVMMVYHS#MDqXNdo<qa9*y41Rr&<{$~Dq<v7KBwxRJP
+9(KOiI1@0X~=MjJfpNJ^kvDF(wqtM=}{7EeD>6nj~T{b<b}4T%!zDe!IYn*Iq;TW?-+)EBiM|-3GE2D
+D&Kb;{&Y28lq1Q8<K(#$=8u@aVy=L>iiYM&O9(IMS}`XF?%;pq`&MDJka4cj9PHKWQW}_xWA25yALf0
+ScLvbB6EX~BD&UQN3lHWnkQYCAVUkG0_Nlrht?6JL0dqE#AL<EXkg^B$li86%-h#XbSr4)%WL?ZR)-5
+d+X<*JVfbc;5jTkXP=(*8{p&ZWadYx}B4iWQ7$nKEaF>k=!X}Z3@$Rpu<&eu}8y!z^^LLS5(5$M%Wm*
+AI%viRYO^+N8SKF%t0gV;?dKN=TAzXhEf_75myDW3NW`|?X?j*0Tb9I7Nk<Zr#v2E2#99(;<r95Q5xC
+{y&)sLStmtmGSJ_80p<mrZ$+fBx~Ch`&jRzirz#A*(6-anNr==0bg-KefdLc8uk={EzTT{VRKtkQT;C
+<d5+P^8}=gewoJpBHQ@rr-3)pL0Ldo1e!2rV?7CRG0(=>y=&L5dU5d-I<E_JC14IRuzP?K##sgzxZ5#
+jrdGR()1GkRnjKDb6>m6i&7R?$R-7Dw)7BgqPIbi_&RTP18jaLD;iNSuIiy;gv(`iosTQZKIkVuD{Cs
+`Z*vzTwDOs7>`5|45dD%hv<I?q6Dfzv!GREcQ<maRrdX3A;3QEb(>RsU3MV*zBosp)`Hzexw@-uR>L%
+MkO)^zFnKr^*E*pO$;7k8`NXKLP3Jc^mGA7{+VFie%-$t6!e$w;~AQ{(e83NkYF<MsJgPh0Ouad90X6
+00xJXR0&t8`32uKRUZ0XQDo@i`tkGHVzj|gmg(u$;{Vx=^NbVE|Kdp?{oLugZtFOHMozJXnGIsqm*ag
+_^|k}h=}Nc{f7^iE&qA{=iV_0<HM6?nlQ~%nrAd+nvI%mn&X<cJ)3!T^@{Tv>y_;_$7`|I+g^LTzVrI
+c%gy^??@;fd-jlo+crWvQ$9s?WVegCHKY3SsJNP{4)6VBHAAg@<pJ#nu@%hB(mQPb(jc<zY1m8)%vwc
+_ke&~C}x3ynazjVJ>{Vw{|`aS2r!T*r|_x_^-Is~c%y#f~p?hM=)cp|V}(8EFgK~X^oLCHZmK?Ol2L5
+qT33)&I%Wze~x>L5F95A9Rh3~iqFMeS$W(S22+(?U(5YeLtBLX5z|j=yG*rcyK7^AFF<USD~~`Q-T&`
+ONou*=M!S+diNA?D9F}^N?>x-)_FizNx+w3Ac;BmwkWrt?|9>>*&|q??J!zermrSe!cyC{X+aA{o?#a
+_>J`&@0aB_*{|5o?Dvx2QonV6@B3}>+v<1B@1$R)-x~oZ0;&TZ3Y;3aG;kYHzyn=^0)t|Ll7hwrWd{`
+#{$B<C7_>)wMEjk#Q*hT{O|V~Ze(-a_8-hOzwhMVABrhbk@6f)PedqN3uCG&Qt59e>FaUJY#A!xomTT
+5%{;jcSe%4fLZfn$@FL*BVT<`gbXSwH5&(oe~iAra$hrN1w^`qJv=B4+V=rzr2lh<~y3tpGKns`6p9p
+HVE>W6zr`Hb;N_c8gHeHQsF_c`Ws#^*Pm?!Mu^&-rfkJ@5OAZ?*5Ee*KBs$$p>u?esh0*V+Fu|Dpbw{
+`36b^Z(BOs{bPaVFCRECIvhbFgsvt!0v#f0nG#3P`w2PJ{34Fa9-d`ME%==B|&S0J_tG<)J>bI&DT!X
+KBs*{yHWeG_FL_F?H}4!!R><8!QR11!7l|@1ve+UjeReNUJGSHVTD_(3p9nA=QXcs0>OFSCEgu<VttP
+I&hcC17fINB;eW#a-GD6tNr5i}?g_jS7_Yskt<ioIJSyawkQE`UuS?&@`!aDk1MX%G_4M`H=iS05z-N
+R{h~EnVO9GY!ydJP7;H`jn1Ktm~7;r71bzrZ+<iI(BZG(mcjS4aZEekqC9ClQDLhBI{MD;Q)WNXOYkf
+R|#g!~-xUg-AFJ)s9ekA|KAzE4Ya6rqXHq-hptPHHY{e$)J}>FBBU4D%e|In?th&os|WPow9vp0hj`d
+cNrSn&)b&x6PhAJr8&u^ZeG6d$#ck@_NRrnA+BQugzZjz0P|z^Pc7XruX~amEOO22l?oH5`9McjPuFx
+$@MAlnJ((?Up_BU?XC6M=<|`!Hsa;IJ_o5Tzw!Cb=O-Tr-=@B;d>`_C#J8)j2emwZ-zeWFeFytK<@=0
+pvG2>iANzjmd&&0(@p%*Ca;;yu-$1__|0w}K1l$N{8`vW-C@?l~e&D*m8NsW9PX{}MbPeeh5)qOSGCw
+4+?+DPG%UC%Xdo>3%-)sKRwDN4{Ip6aa;y|O<b6zideeCs%SG89yQQ_j<#=E`uW8TBP$9SiCzw5oh`!
+Z2f<Nb%Xqfb-nC8qey@R{S|LYVgU_4N($eb;w`?<c;W`~F1ju-dn!Uz%U0U!I@4e}DfW{=@y#{j>ZF{
+b&2n_kYoUss9`P>-;zSZ}<P4`i>L+=lp;2?-|f9U{t`EAbrrppo*Y9LC1ocX}f5{wJWue!TR7~A%!7d
+heQJZVo5LBS#yQjk)P*esugc)F=bxP-k#orz2|r@@_y3iXP*|nUcN8-zUTX;@8y6W0u9<N+8?xz!9Rx
+Ja`<%=KS7hE8L3IujMI$QOw{CPwrF-~zSZ2)_<JUMR(i&H<$C?#rS|sse$(eepI?1A^@8nu)xMAWj`7
+X#&G(&8X}{%rfclz1zgTK(-}rU$U*x~uzgvK3Ktw<ywXmrHGXh>F4m}WXJm7S|g@8)|Hv`mx(bUee13
+wOI8Z<a)O;9@Zel3H01_uV`1-}`*H~3PpH*stCzMg%*>KhJPS4d}dRBO(8{_gpRR}bHbesB0!`nL`64
+^Ri0srPxF+Tg3f%c-v44E`keVDR_Bjv=8TLqaBm%nSK8#HsHieS`Xr@4L9~^1cW9p6YwCZ}-rEP($dF
+&^JO4BK`Fu3&u1mO_b(s&1ubbDgg&im1hf2SI<ByiRr}mbBR9|dM@^S*>kDq3eQWPS3R$L)_B%>j`hm
+&noB(M|Fw7iEKUSr7~g}4DN<Y!GzTeCqzIT|cV@G@&OS4n!Xib)AQll)Y!NizAt$IDctzk2Nf9AMiU<
+dnB1OOyK_XmnLW(OC4m?odiYp=<x<!hBDXxg%n@gpiVV@5$@BDe*-@`OBZF0>%`^bK=4W8m@p5a-pbC
+Z|_{+c&=i??})9}u}e<i6(a+YjyU_PK34Rp-6)>b4^eXeW-uXAuaKSaSnE;mci_Tryz1I_v9fgWY11-
+D58($KJAcRP77<!G5!A{3bcn<#+jG^QrkK_r!i?zqEJkH})e(yL!xc`8RISC{Zt$jfzpDZeFgUDcWLR
+^u$1nsP?fqp<Fr_7h)>rVj&`K%#FKAH|=I!)1?6>t2-`KUImm@8L!~gyoT5F`raom@U+=#o>4ucn|ev
+#(p&maKc)S=)FZ?a7<sDg8+EEYHYSF~QY_DWqDGeuuAYBi)8cEZbMUPyW$UKZvf5UMQmSk9tUhJc&>C
+5R2;8YVbLYg%g{vWgC}LPg90??e$!YRZmXgVY10at(D4>WE9-xd0d{j}xCbsY#+t@)JduX7E7P{!6j{
+$}l;Sfg{<Cxrdf>V6M8Bt_{3j~;AhB+>=fF>g{Dr0h8#$`e#WlE-HMrNfhP3cI;JQ1fL`|^;udnN-JQ
+wfz+DV0_krK^G}suE>oSyhy;s;Wk*xvh3oUG1rcYN{6bwWI#OLxc(yDpaUYq5johP)h>@6aWAK2mtey
+7DsGqy$n^j0001f0RS5S003}la4%nWWo~3|axY|Qb98KJVlQ+yG%jU$W#qjJcvRIDFnT7FNhUAOAPfW
+$i7;q1KBB>zI7H{bjGU1fi4`<eP&62+pwe(gv4SL?OwF*J7F%t#Pg`5HRqLZwUMiUc^57Zp6+i_Q;S2
+{f_(&!^a_(AdpP7VL+wb1*-tW8L&qrp?+3&sf+UvE~+7rBGxnLCpAszk<Ll9O9{GUho@Bb3;H)zc2LB
+hYYHlMrF64-q1^tnG?Sg@ex{<~}LxVNC{j{EMrUn#iru7aA-eFZ<hufRL)#)5nApL5qGd3o6`vyF5A_
+>brAGS+n^|GHnF*)<2gAG!1Xu7~OSzAig`-_kW7zUM!7bJuJtyS}TO((f_L-)4UQh`xXL<EpvX-kHW;
+;S+>8Kg$rF_;m1$WLlRnR>(=q7KAPE@-j~=ErbsT2Z=>6pVI`vh95%zx3I@TKz!l$G=Qh4E?Fu0b?W;
+GkHW-Ui!c!%7FdM8R>1dci(t<lP@uyi%)tSCAqZ#x*OeLYZ;v1}q@6kY66LN%3Vcu4WDW@Do_=DP0vO
+FDHFNGz?hu4eC-63fAHd)C6U%wv|0TSbP+y7_VZcM-+ip3zT;nARctM&Y%vH#R@3T)S=eeY2VNDf06H
+EX$!VlrQ@O0(wntwkOya<uGgRh0~(^JY#Ix{5x&-@o|58WGJANwOWI)ahO`N7E40=e#>4A7IA?S&y0K
+`vP*N9tY0kP=`URwG>f(aHJV=p;jq23*B*#Oo^1zP~~cwAndOChCQ>s61PaI$t{oiGKp5T7~OiIG1k~
+giXzxHp!8hF1s92T#lP%^<V+BT{;f>VMk<kxMWv=wFjb;j*0Jf_*uJlZXR~qCWjl9(ae@f<+ey}i(mB
+a@Eb9I5ft{89n(8xrnpLF)-1D`(7(E_;Pz(f$ASIi%aO&df*I0uX@+!@bhFG3Lcg8>iwB~%A{Zo$b?@
+cSbTnA(E7==}+*PdYwF`p8Hia7@&jCfmCu3SipJB)h%D^1cO15hSLlH{BVl8I~e+X1{Nzr$YL9wtQiS
+jXDNhAA6>m7n~z-o(K7Kwc%txN}a_}ONyN~CX@b!Zy3-H8(&1LN3(eGJ8nhhKrVs|<h9aoKt_{wM%)4
+Q$kOwoxm~;=MwbK)a5Hp?+6?ud3Ormo@v@M*ubL`$1TK3pV;w=nZBEbDOS1BFvI?iciI~ua3hgpm&+Q
+C#}Sts~vlpy2pA&eJ}#;8UReuk}mAHXvx94uW>H@z2=#{Cg=DOPIK`ff+h4e=!D*Cp||U!GmDI%`;(f
+H;tE?1WLHaNRwX-Fv>Or^exqB1><fbNOW1OBrpx1J>-_B2Qa@V=j}Oa!_Jwr2bel9wnmOwf-15k5r_6
+c{=M|fwV|AR+3fPI%@Lj~n0E3aV1=vQJeFQZM)j6&LK+_*XS=JeD4CNZmAUvyy4AJ{3B3LEIuL{5~58
+{`4un_joSuF3LfIZ=M#a^=AaP9<3x)Pg*QiaVX4lZ`(&yb_@UA3^eYAv+6u5*~1%eMfAShBsO-EeN%g
+p>5`kyb6o1^^!b4JuY=(aT2KYKHR}C<JISxNfnl0*i$*X@BWyCG7y>_u{Z>=N&VQx?q=3Jg~WBGlp~c
+Cc^nPD9YNj-lK+5vK=?`0@&4awP%3Duj)1o1Aw()F74c_slp5dm-bu4)Tr|%UiodPyy|->Y&b{oLTy+
+m#0x#o3;hNQeMg19+en2vuuugr^k-hE<|w}9y#R@;aXziSmjw%UzHK94`N{ymQbvd@@WE687#<mxtW?
+0yVgWu11;9dB%#wB(dI2XoCDfsFW*pbM75d4Be!Q@Xb7eN&RSEF$Mn->CY=NSMSd;}^vkm9H=3}M00S
+0YsFyG|pbXSGp%;aqr^MS8AVi=GK>90b$O@M<8R<#naa4xL;0w6w%3OMP3#b02h#SacjQP$a39Oh!Ex
+^^s#xVo%Vu|=o#>6zk+t+LvfF0R;yz<L1)Z%MZ|T5X376v_%jFaB&lEc&=WbZ(kHL}KrYE84U>a|tyD
+`PKDllDNVrR72S>0?{jMmso^%a7&i7``L$Hw%%}Fw*fmV*DtmRtRs{Ig+7NzSv`;@M;~`pq~TVKOt`7
+VwtTcjsL)=;S#E(@-x43Vhu`kSRa4Umr~42|-JJ`E*OuieVr^Nrk}VrC+1*z2m2L$jhx%b$@k^lzth^
+K|dm669CG`{?4X|dI#;W*nLGZGzk@>Dd6X9QL2RLsbQam0zuS#I$Nl>{uqAd2{zVcL;DK3u!L?j5L=6
+&r)IRuDo830jT77{}Hs?{aJIGVuVI49>8Xh0WD$QAMmc~~I{xV>2CT&k02*4dmGQ0Kes)6{{&p8W`n&
+9Kh^Pu7ejO0x$k`{mVT3+&28)n%1-<vekPY+r3h8v8P}6B-ItZTpeE23&TaBd|Vt3)>P5W1GkJagYUI
+0Dg6yy}E3c5E=)>@@y9k(6yfr@FK{^b#p<XG@5;?$PN2>6InQivv}8+WhW749U@Gws~QN}UR<X`P-B|
+LYkbs$YtIKV&rEBEw30@@+HL@!_OoNrinQu)UX|H#xo2A-Dq$+T`{`Xa+5^^DGNtdN6nOPVXWBCXd9-
+uWXlGrknK+j4h))3wv>DJ_08pYd0ED_Y5Y3yZAyk1JzbCV;fyf+0miZuv<k}GSTeW&X*G;$=fktcdj`
+5e&zv^mnOg+gzvO6!9Z4p9)<yg#7-4ip6P};Z6a;)7EzZkgl02$(D+{4fuNIbwNe6XS<vG__3No>6wZ
+Wo_wghd>#?aT+-B@9F8-VKi>jd4&01S+$g$|xVhOSQM`LjJZz>l(^wJz#qUZtgto*VINnQuVb^@{-V4
+O-(fm@W~Z8L4x^?5QOE+OB(etASU^M@Ni!L&R{H%K#<lD#c4&xRf$Y&Ht?llWS79fHf{3Uh$5JuHG*&
+_vmG#xkW(8DeXYT+BKg{L!(sC^>n>?!Ep<v_**>J)5i8<!I`o~XeTk)z;}zW6yh+chb~h=5Wi<xd!h*
+ZvYe)#C%PiJh(g=JLSml+}Z-B*Qme+BPML^Cew9q~nuKi{>0R;HrdZMt`z&rHDTThM|=)H%;O^?=`&j
+ARPN0mW>;?$-OCk#?@wCiwa0hcFYNAWP(#z`_LPF?NY^WfsO#JR)mvgqrDp<Mw(Lzon3Q^o*SJO@|x6
+VSg_8Td4A1ooTffI`4qa0<Tln9_wKxv0$c3ztPu2H_&i7{f`0{*tzO7%n8Pp!RC&QD!arS=yygO8adp
+5qTL(vn1Mbkrsp~n;(d6Glv1Qy*<$)tU*Z)ddysj%BXI0H;aL0xvGt}c*OP$6wzh^8ImKmdoRNIG=^N
+!3INB($mqqGc@593Ff%h(jzr9RfaHl?R!EEtwa97{$kj2sY;07rfP;;L!umesw;*#s8DCVk`aznCI@>
+R>faKRG#}YR6voU^_-Xza!uMDuE7QcGLP;%h2Pq78V+d~<xX@E$E-)O|v{YHD}9cYi1i1zJ}@q$oWE&
+%@#6r1+9u>|dZ6j?~LioXfsqlpW+e9^pO9IAUq&D8*;e`nxx=Yy9cg;@F0Kj`0ua?v)q=YSkL0^DeW9
+E)EQ4GeZ@>&^p`wt8N^Xm%Z)N*ctivN3*1Gthu!A3N3Fp-YCSF^BfclWSa;sxh^h%K#wPq~Q=Y)La3>
+8BfD$d+-8r^-rKy(S`wI>WQB;pm@o6lpbTn&|Eq+hu!9ht84N_=sP|PGqFQs{Qf5DD+rjCTRK>QwTbz
+Sw$OL}o=$mGqdYFwya{wW(&^NLrJ%MI`XiIO{A!gZXlpJa3ANOenG0szbaP5SqMaEJu)}gBfO67?AnO
+ex!TUyzOm{87{ze926FY<9?$G&~HY7#yLPL^hy9fc2l+16xZ5EK~w_QX+!iF*k<W05B@b`QdaJM~hfv
+ol!iX*^UO17iEkbfapRG{E<0}3V{e98}+s`%>eo+<gcByi|ThL=rq=^4^0G+uaNxU=Q(F0sB6vdvP`Y
+Cy52%58zF2kpLaw^AWCplIlKf8`Y?eeYvi0`7M4u>-`Vfd_3vx;j^T@dwb{1rORK))IjIH&-swWJ@OI
+E%2g<7_fH9MQ!mvQ#Hu<v6$bj)y(w)SPEbo?gJ0xNUX`nzL9zk>#$+gd)fE?o-S|EH+nWcx)1ooDc?Z
+zvOD(R{m8QNpi7_G9SCVa_jXUfeJ$1_t5Oq&G7olDkrsczKwWXGCSSypD`{9rRnM0i2q3bt*>AJ}lO>
+2C&zy@(31tq&cO&gafaj$E{z4%v-Pp5m6;WD=UjaqbY54_m(I)_ZP#}B0ix=`Vhkn4%TH_<(tzXVv03
+32rejqZtfOAMcI~-&h@W%lA43T9vBFn9a6}UQ~u`<$u>;P*5CAtXvetvKYgG%60zm#MF;M&UxHv=Gi0
+RKd7a+7vF>ZqKx{pJ`xwu0tEDCa>+Da#D>q5zu%+>RI>>1iN00JjXHw$~(sOs{zmh8+F*JSt!gvON&-
+z&zxF8*w)v4MNqd8=6n@_k8V$BQ@5?;1RGhzMB*46n_sy=eTBTcYva?yn9N%M-2Z=;8Mwj6Znc<s3!W
+9@p4rM?fPC@AZq&+@Cx<wG4j0am455cHXvuO13A+N!^4Y6*BD4uw)>z^_oULLq4C(tNNmLkgdP~HNo|
+qR`mMQ*-BM|-GDN$+f2_Meno+kHuwgI6kyK1TB{#@6k;X)d&{nMkFc6p*`v_oby#fRaOuQVX0aywY7x
+ytT&uW0BooM1pG#QI4c2>LFLMRCMR-(!VZwnp3B3=ZdC$4B0SES4Od8DHTK(zIs5>@PFF~fQ3yQq)=#
+BAaU$Pib=#1$LI#ga_=%zBhb@_{kuYxjt$Ira?V>v0S8rM-JOygY0d0#$pKq^nhS5HTdS2{c@b_6rZr
+Gu*gnJG(KzrwPrIq3oiCcGeOf1JBG2ptg<!z3A(rd5-QTt1=M~&kv(Xv9v}6bhp=TuZ0C*lO1|?ZE1E
+W%~&r3FLPUK4g;J=vGwr|7*_rEMTfO@gOa0+-vX%D-DDB#UlsTWLzyLq;(vjcqK6$pb}-1kvp#J1$NF
+rN`sJ9ZEWIl@?@*=G?X^K~i<7fR)7q~Q1b0hl9QMz)%GS2zAX;e<v6{m@lk?Gdt4~Jb%!g1<d+HjZC=
+DON{v~MvsOsSE$@z9A2MdFa$r6f{J?sLWWf^M`u7lpG=X3?yE$WGSuMo_wHXpZ|0un3iw@M=RMCwg`;
+y`+1Ps&yUHjq<Wm6uwuYx8Kq^pV<x1a5Y~yG$4v-`%SqiOa2~^kd3w^OFKONkCG*#1g=m*p3&~kVBi9
+Y8>0WN8R&v$)O08Q4^L>mi7;z9qc%eZfHJj6zZ9`=KJb*J(Mk}UmWvBZq3(6O5M#CWhi{wl|hoY`itz
+E%+|>+1Qk~LaWPhECF<<Inl>|NDC?s>!xYu#qZLv?%Y-Sf4X{Bb0V1(Z9B}UmP<bvl1(o7lKKhHpyOe
+D8s70}<N7F)|Xbw<<mU~$<`w;i#R|u$1d|Hm`Su+RJ$$j%4)vboPUxsQ|^>@??N^ef9er>muuPRq*T_
+nzKWC?A{HGH~1y_DqdqgNx=JY<7r^dT~vT8O+RTbuA5($K0xL9NOc_(~O@H17d80F?(!c&PS9PHG(2z
+$1|H^I#kvB}2U<U(ma>vE3#{Twx?J;sr?3KOBG&W<S_P@yg&WXwa}-xWh0&E7_%&%2l=>?I5GlF#NK{
+>Pqq0C-lXt9JFqf$@!tql3nUbWUqrB$SQjp546OmnxRRLHk?c;p|NWDYam6Wf&A$M*=E(*L`xk>4FK)
+aTA@E#G;9_fiAC`=MSCq(v>O`K)<gfxwTFigJhNAg!i+Kp8i0yckqo{LWE1TrFFhARYuXEw&9bDg=Rs
+NRNspO?20iVFXd2kHMR}xTWWtJlDWY6r`}JVZOd_`53`PdxTm)Y%?>_j{zU)GnqIm`QF;-X%6|`T#hd
+!Jo)Wx7$h3t_06V!pV$KZ20Utk%Ts%iVw{v~rOXZ!&a2R6}i%XJ4ENJ-5*Hy4d+Y$X}Ia`RA)@VY8VW
+0($t0NT?u{WU@0dN1@@At;jE>UEWpVh);}j9JBG9>S+WAWEBZcsn2y1dz>pk#j+FCGFZX(3U<{X7w%_
+K*aWRF52|XSG5sHhiyD2G20{f6|sGtjc@g?<-B|swB2fJfyAtIp|RE02v4LAGTR@H@Nt4LY<tFhUSK$
+Xy#f`iVyMSg;3gRvn&Et41wml#DqPt+;6*LZ&LJ+t%UvR4#<OpsQ5L$AS)1X!9P8Ar!@&Yx)0xbzc7J
+4EzCW_C0MBOz%IBUSd&nMSmoX{b&mJlb7WD+#X90FVX7LxR#qc3$#9#Fj*&mtO7K}^=F{8zOrKr}S&}
+;?x&uq<QlM?}Vm-y(5P?SwRhR^Eni6Ny6V*M}i$!$?aFe{z%_}K@3Ypc<Lydwg6k0^CZgsO%2?g<sjY
+I(*vsG*$$Gd>Qwn$|gm8<kdb;B7XX`yiQ3O_#_nPHk<~9<}2mEG@e+Jx~=SkQQP>q2KyZfUU=a5pMsh
+061wX6bF%3ZFb;HRv;rSGtdMThh-OnOe!cj<SRK8ENXqRKyjCBFB|><C|=X-OoF*J)?_jZd_9nBfcO`
+4xQ8#y*S`c&(AvlXtq!1z8Ek%Qu5R_RIKoY4pSrJHI72p?<=7YLvUN9`3<^kBO&N#U!pnT&LyFDoE>b
+ST2lu>VN}=i&LL(3t7e%kNyl#<T?6Mai8??4cMn`h!HE*uuf)Jf#+zNo52j2?^9%I}-z&<e_`G(>u7`
+XPu?24Zc2Zg(&+t1>@=p)<~wG%WpP-cAWLm&IJ+FN#2C>wfAgD1DrEv~R~*sF;<lwvheql{A%4=Tg8i
+!)M0c(sj;Rg-}<uC-WUE9FV7Ps-`i?Q63vC_AQMdlKt_#;Q>(X@*n~dF#JJs=1;~?Y(j7L&`l;(N?j3
+8+H}XQKlH}%GGK-UpZfmXDcJr-t^GpKByCZK|o!nL*43#{|&3J{yH;`*yrhP1#uZMOx@~<FQZbO`eH~
+<3q7Fj?^E|3Q@5sxtG~+B7eGUyJE0LN$Iz?P_;h8O8Xu%ggf1_{zK5&58De-O;7Cua&<K0)2hhtPy$m
+$8P?3+diL1K@=|%W9n%=$|q>naB=0ZtP7sb{4a~Ikq_Mx5*3As3ZbME1EXx4{{=DTPqN$-a=?13F`g3
+`ItO1S@?cK<YzxWZ46+SQyfuyD2NE*HZO@|;uy&S$9s8$YeletkW07hKJ37{*!p^-v^AMeJsgh5bbwP
+-L)P1VtXDBAI59itG6>vUz8NpqS%?p?J$m#PC5n{|lfxy9vY`*r|Asf(KSwbAUIn1Bw{7unjgeki$qQ
+KjJ&v8wr8t6oQV{!<Pg83c0ce;?M#VNfwbRb{hOP?Qg{;yP{Jqn%fC{ro_O*T6g*97l8H<J&5D4>H)I
+*#t5|gOzaYrY8j-`Zcn}%n$hFQ7iwX}0?{eQ)r8|gM|7G)%N@$q=GSQM&WErf8~P3EGG7@+fQ`O;1X`
+%lzr{9`T$y!1)^8w7x5<cYMr-^bnB=NT+!oK#sc9RXf!Z#?rXslslz(QRg=Y1$B2x0VUZ({(M`n3B)L
+U+^HcY3!j^;yOs_UqryP$zX`IMW$x`uPi>&T@?cVZEC_E8k0KtEPS*HzK1P-y}A%0VZWT+o_&w!t}wg
+N~e=2dKR!UykHG1lzT_U+$cNnr!rCmjln&>JE01f!wy~TY(=yh4-YRbs9uy5Yc)K5MG{;1UyVzARs0v
+pl_^v5_^0!pOdWxa@6OLqd^Bhjzi{cIREw<&g5^XNg;O!l7@<<miu>Zg}u@|r~~Tg!B%IZs8Ebgm3X=
+jFNN4*5EVYq(}9_90=}f5Ewiv4#~TUrmXmKU(#p~sOmP7qj3P&TwT9F78Zuy(Fr44MD$oRE!@1{GS{+
+B&2<56r4G@qvYA<`Rg(yy5F4?4gzaJJW`g<2b2gow;p@qdrl4st|?FWUNe6Ai2+**pt7VY<FxO9H{Pc
+scM1Oy@37(ee-ZvQ(~R{`xH4hXU{(*p3`;YX478<3A)MU7%(0L3d1Z8syh&JCbQ6NpR)?$>d$x<%_O!
+{(dKT`z7kNcK_>mdbR1?JH>q(BpZs_F%t}BM{9yd>9G5pKbF-J$84tQe-sxtsQFO;+k)w(jhf5_`!^o
+c_@9<#k^2)B`Hz8pafmp*epLSF>fs0l>2U-NXr_^0)fo>Ehm7D4u0N^`R)Pv`@b>shf=<2x89)*A5f+
+_RVI?J^|=wxoG*RDQ71hRt#H7CxnW3BBrRpYKht?m-N8;+(_l$s5YLDKAOVysw1($Ny23oOHdG-o8)a
+62biNnLIsC1$0^6@f=ytQr^8UiS{but_2|&CWO^$TbOfCh6v-1@aBP|(ZMprH{obN))#EgX_VcB({nB
+*KItwPr%JS=$nm4TiD1NAjODt1SZ%8bU7`AK`^p2M=cb)l#&2E0q24(PLF@zq}5LfDW+Zvw67#-0cBp
+}&Wrzi5RW@XBz$`Z9<0#cKIc6hoB}s5ZtjXj^PVlJiEZ72DMRfiD^S<pg~$xo*JU{IB9<kMcI5si^b(
+SBXZ#elQc$*+xLz6VGd4%~zl%x6yDu_7d$8QfcDXqKaWYUo{Ayd3i0Xj8k6LsSV2DT<jdAVW^i*<?r{
+H-+eN7kiZ_}+hjbqb8lF9aX&!mtZs+Q=*TJ!$JT6@-MecJo$N2fi=$19DnKj40Y;$o=HP0i8QYRI<0p
+V;KwXfB_nn1Sq#a5=Kx-E~Yb_H=BDUk?H(<gA0XoOK`W4a>MEKGk0pLJax4`>HSHk;OIJfBNFA${B^1
+bk-4?5ih)bgd+9l8zWJ%DZhh<7F;6=;{D6={a^Hb_sNCJmS?@n6LI{1bEwkSEzrFYDzh=kq`lmaFBDT
+0t2S>n+e#tL;83*NnUk<E!8i1flTzgfV#^4@RZ$t$*-iFjC);6+g#w-=f^7|G^I@xf<7vqbLk84c~!=
+eQhUiE!o{yvp}-}B;>NpP@29E6K<E8XgDjWey67W0QXy_@gB51hg!ax=fHb_-Y<dofcx8!E#TIaw*%~
+3oHocN^j)0l(;SQbTUe5fFVT{`3}4y`5{H~^JZTjs{S}gy`@(w_kwd2;2Sn8H-q3liwpnQ$C_Ll-9Av
+9owdP~!YPHpH{_;gcOE$A#BJvE7h=^pzt4WWBXIM5%ys{4+b4uFLW*MJKS(`|wf+W@yFY8O4f5F~*0c
+a5H1P_YLC5>cqz(%woyj~z``^mpK{qF$UKZuUp9BN`%&EXOx@1O~R?$u~d;<Do;-2>=q1=+xCztn}K>
+xu{a?A4w{J*=A_z>4)>!3d+*TeY#*qq+eo6EYJ9b15}XEx#6;fQ*@tQD+=O`M-W8@%M`_2K<uV9%@Av
+V0-ONQXkQu<Htd6DHg*+Etq%l53nFPnN>%wxXg+I)CZt<zkst|?Aj0gTnH^(4J~jVw89{53THoeq`7G
+Y2>=fk&~Zm1R1y;(n1TsA12l+l0cz4JlIEiU-`g^a`HR-e+Y)jS@b8bby;D!$Zd(tu3)NrzHwqN*!!)
+$ZsAxzEphq61jNvIG0mmj_H!B?lC?0MmdPa)T$<6)*n>{&k=D<Xr<i{k_<*M#^zTIG$AhRY6Wg7dC&d
+rzQNDcPtvJz#m(L`sk&1rQ<z5|ffe2J2i$J6j{S4kS$38!$=eKE;)=y1UrsUO36j2eHOPyq$0mp|o#6
+e0^3q~?JjwXhV0sg&#w{F5N6^-U!lQHJx^uQ@zMtL3gZU`S}_Dnt%#KI}PeL%Z`h2{eTy=U!k-or4o?
+&&#*rp1<mO&VwB2>ehkwu$88JoO!f-f^4?fm7ns>TFw_4r&SJ(tVa1idQTgy$2nG93PAA=WTSRGW0;p
+l%>H9EA=v~pU~Z1&{S)8Z9SeQ1xw2&Sx*nstb&?@udm}Ygc)C=`Zkco$zD@Bq)J#UZ5>`-~D&^>PJ-i
+w8mRb+=3^G6`Q(Sk7^%Ll<6OB)}zs|!AP*t!-u;Zf4O%GbXEXx-I`R<Kk^f8)2`823Zqeoo2GmXPzE^
+~_5TeN|?iDX(oh&YdD;ecZcB{^Eb2V8sSrL`vvx;T5lfHO}WY&&zVz*f{&yZgPx4SEI}{X!OsxP9SWk
+1Y196J3Tf7r*+HJEU%(U{P+tFMDW^pXIuMAO4kgkWa4q8i?%UP?eM_&?VI`$KZ*JJnvAYY`rK38k-BG
+d_h{~Lswl#gU@a_C;pY|dOt>a+?Q*#`i=GIALTx}xZcP1`Prv3E5DM)H_>gNQ_4hlpORf0aNBF+VIq=
+Vm(k*N+v|EdSEASBsCG5>J&<(K3j&SrIUI>b+a`!mlu6DR#>eay--3N`ky}lqg)*^8kDje2tRWjd+X0
+o_4pTvV>N!5lCe)t2?p!^Oip#C(?miOIn=2(_om_Vm^&IhW83m=h*>=R|zdVOz8~4<5GM?QE(&0SVR}
+Yhdk~(ua-B}s2hVne%OC=!a9G28g=*X)HpCo;aLxo{OaiN87CiKF}KL@Jnjs8IcboaV<OH7x<DF-F@!
+BE?PlMS{QRgr#A>;K$;iGgkdERLPiStBYr+Tia~L%6WaHV?XeU@S7p5B>r@NB_hJF#1L$^6V%K;+W(<
+7Ha%2gE;dTx=Eaia{zpV16VgD<uY_BIvT(zz7%K_TMXwk8pR*YQEV1)6yL%qzLVVFh2H&dpg6_0VMM1
+{wA(@IUmm^Pis#wx(Y6s5VaZ)`)we(ccG#1KjS89NcF|#sLtC@YFc9l9j8-60F7@cp#g{?hm+fRg=;8
+*1KmM7-s^9R$!`feVlFi{UJJ}q*6^v&67u;U32U&INDdvJxeDS4x1lA6hbz)lRnjR@1@lp8!D=T^k)a
+OSo*5*2~#~U49@}+$PSh~A4B(ll*^^FgS>Yn3~$@zMwIyJvQ-2$MV>OdO?YvC?Tk(?7{=6>28Zb#!43
+hx+RqTNlRuwuhQumU`n1gQqkSvP9&p>!ZMa+)CYbdGB5C+hw4=%?ufeJ_ibbi)!{Z_z$ZL-W!uN!@Dj
+RaiP4O-$&N5~%tDfIAIT^Xt@!djw_fb!_OK?n$r`w?VH*chOOl?NZEVxqE1dN+k8TC3Ked15`}it;#r
+~1-f_XqkQ2*p<z%Y4OVdk&y++jTD3s(bm|${#?a59S=Yyq+Z<njwkBF6H@d4ptD?7$uT=$kog<pfHZ5
+ql+o^Coo9q22&zqcgqT}EH0^Nm6cKO*cb*bGFI!~@@;k2klj^@^a(w)|aydt+vj(|x0{U3>-oo9do-|
+A40x)!%{y|Ui{4F=&caa^uyMtM<ntwSwz*Xhn>*Sd%mjSj##SdYw#<nSS7l=`sUf(r=*=Zimb_Vo2fN
+dC~i)$EXsEur_J(wHwWZz`GDa#Xh^lwmmkh2^Qt*3cKHKWvq&+F@QhKgUCkcGwSXvN3+$ACT9b2%{N<
+c2zsx<+^wIv~Ktg4cF#?&=kFXw07*fq`WcdFp{aGvK3^Si|e3azq<H1;9@#!(H~lcp<$YOBt^+Y?bEL
+YXy=uyFt$mn7RZreBd;2|*yIT6AGot4VzvIEVQ7EpuiLHX`6Dxr3wnmu@gbpJ-d{h*O5MK)K}J2@qRc
+wMegKmLF`(l!gv2hOExU!#VD0-pQZVgNd9b$rTO4)n?*0u4uv-56Q(#3gGdy0VqGVT)?Fpa_>l<Ldo!
+;o=LUbSMl_Rq{v^nWyga|pcoriE9-2W9GaSuNb1ck{ahr5-}0xT#8n9s8cow5S%&m?kL6{p0Y$&OZ^H
+i?enLVMt^BjW3nj1N$9?N&;{jf>7Tf}M-(GV^sb<m(QMA}DFserMq>HR-HXA5=E0b5g_1^9=D1&C$ty
+XsxW^40B<MVJ725&cG|6Ox%w?Fc>!y47ZE*Xn_vm5j|3=5$NO--a!i-|B?(ZP^6t6wu|+R$f4aG4+OR
+M5274=tp!AyC`h#|p9Hu7KhrAY<~!g-WWj4d*Q!KcXukGGsHK*U6~idVY0Kd)lCM9aJ$8^zvAt8#$hz
+6f3e3iuPtzF66D{5`hJY=OL`SbszIH8dH&micgePyLK))c|_)uX>CY2hkop%t|41K@!9BstG)B--Q(*
+mBs<D(c!0oomt#>hf$fz<xgm$Ex@I0KC50*n_FMC7DBW&?m&8wp#1P0g39y1HYJ!VdhSj$PxiHn9*8v
+r+c#j(L<k?NL27#?MQP?wCax5qa3&9dn5F5u#L+9qMO&a>N>$nh*R4^3*+Ft8Hn(k^YW;fT2sZweRD&
+8jEFjr!q);oxjb7w@?m<Md6!hN1|V>HXfdV@sKoDAH^fmBIJ5S^8oE8S)da(*=4jvCfmb@9vR_f>!JG
+dQ_y)vzgcZBfDy#60R%@cyK!n?pK=bxT><5Fz6?|s{j^A~Z3nqFw@J^0wO#iK%u})#D8oy(vn}M`^Ui
+dnP6<P-|C)lsraG2+UBwL+eb_rhxX_IXin8@u5q0f8E{4}bN%1YTx^Qoh`TgwvB%a$M;bFt}&{A#i`M
+BjKgTA!9k#^q@nKi7Wr>S<??fm-zpm@VrZ5t$oZS(d4DD7JM9hh$grg+PjXrh^HS4NQ|QUevs^J1|qE
+w7T=wqsT|wU8DXs#2M>mdW`RJyUd~eJd`t4D`)D_1^&dF6iHC!d`Qu_||s+DvSBgFTT1Bl07x&$UQsM
+&oY&aKv7JNIAm*EkhS^MJ=geS@r+Ozf60Uwt1`H{x_bF?If8!bcJw{|4qX+q8wQ(RwB60(7>|I3;J?4
+F7sGBB7GwFy4L~<;a6ml|e0Rd%9;gpr8vJ#^Un110KWW;R#pc2A2Q}b>SAmC4{Gdwkt>H%(ek4L2UA4
+I`AwK<dy#0Ts#hr*QVchrEYpJcgYd>w}_zOhNOmh6`Nfg&#cMN*I=7FqMUndf1J#%t2-34?wNw$$n1F
+1=o<(WX1{YaLFaI%b_Xhn(mI`p9?g~s?Yq|oPLCYDVfK)ydVGm(6YK)$OdMP^p_{SWl}B$*=_oZ%}ub
+P5GOd@2S1lvD5nNeaFtNx|2f6ud$^Yk!J@@2*Nx@I?bD_<|JC{h>+07ib^vGb#Av2=p!ckb=ii!ql4f
+5fT4Y)j%S?Xdn^qZx7w#tG$u=xAzUC;<dc(&;;#)eVmG42vqz6D0nF+<G1e{K*oQ#FSYi&zf6(w3Zx0
+Ai32$$9-^0rg5>tM@(i>RAA*)`DgQ-PtViPbqDKs)(euSL5Y*ZVB2s=Fp{=%A;E|KvOs%s529zl?3D(
+MjClBMY@XTw9OJ+AaK$=fv3PNOxJ!vN<lJ{G9=$?}QXy|sV@Qs}_`HA@QCB5`1m*PvO9cjW=IeNtpd-
+0^WQ;r?AX-#(Qpi@%67!#bgt1@6c`%^tL5FLG6CkWWfCfi+)PJA?E6Ib7#pI5h(ocltfpf|hXgi-ZOA
+xB8^C>Oo42#!-UrrE`}EEvre5%r;{d#HOHYNI1-O1^rxT`-}0QmjT~y32v*OEYd9;9O4|xKgf)NvyNg
+w+Ge#$gI7%fFrbb_Zmit5h>N4+M8sA@%0&iss<ow!(ZXaZM!Z3!&0B76Opa}O9Y~MSH58w{+MA?_l=p
+#9`7x<7SzFc-IdF{m)T#mi1qWKY~4}Z&kqdOZiSYig9&?+>l(j31A(<4y&0~~z@-$!Kggi%pltIln}1
+oUv=UFOd})WIj^mQ}?q+Ypy|ayWWhl_h9M;^^@uEd}QT?JXz`oIEo9|C=(<OJOBu?rCW;*+8)JiERW|
+X$(i=>#Vb<iUn{Oqv-S_S2j?c|?Z6hzZj-t5=0UguO+Pg4p+W)^#rFMjr+v=Zg(Zf`Nr*81PU<FY_;m
+ULB{SpRcmK`<S3yOtUTCF@A)21By$^ha;9_@c9Wyba#sEMNU0<<4XQXrK%lP;LnCQi2WHse0C(ve5)p
+5?G~7ss+98k^yzD!aAd&&WQdxyh1Nj#X46dmn9Gle(XhqLGERA%3(P5C%9#(0R`x)86N`?YFLbZxXrS
+=LwxKf>12hT@fvJ+>*vsV0-M1}*NXL5;L&~U@ieji@ig?sQSzkPtFzG*Rcue!ZMAcX?MMAp>-_A~Ap6
+R1Z2_{Zk)zirR5ahl!(KF|O&lrKBPEosTBqbSSY@NxZ>*1RB5v!`WTP$M))o%;M|~FbFV4W=QvHzt)8
+e>WVP=Ws%z&m>;?zt(FJrwq$IK_Ve~RHe{tJ4lE?e@katF+7$*YDja8j}|vUW*jwlc=sFfQ5hGGAqb_
+p&VA?RS3~%E9cAt;L$H7XiXZ=K1LcL+idy5HJ&3Js(<CMBMSVv*8iif;k1;_iEy!xv&lNF$25waCoKZ
+SqKZ7zL$OBE817PWVS^NZ%d;C9<ja+?Wo4aZfh-wh@iH=j{X2-Sr*kJ=}U*h=xWY_0!EYgmUU9;+d|j
+*7D3r7zLhgce7D|$Z+McZt=@xJe<2dOjYd2Kts0H)CTO#&Nw#+CvrYtu8@g%$yjG8-EgrngnuH0adM_
+(ek61&eq+W$B6?N)E06slCO$b$O*FNwAR{8b@*u51}(Wl!qFWVSoEy#RIwo}wC23W#sNk2z=um>5Y;S
+Bto3@$zxG!AzIH0Ew*v-W_Z9ec$vt}_Sg5ua-HS@%X37t8VI$uDd*ZcZhyS}Gg%9}#jkA>>{C2uYE&Y
+65Wb5<K0J-0KuaHAeq6ry=IWdE}1|*jHm8BIZay%(Ut<M<@%j)8nt8H3x9>lT&fClj9~H)?-U1a^CZE
+Ldijfb9R)wIQ<$50D^MCA8@Gi<&%-rO|z>mo6)Iwfc=hJ=Ic!Mg{ZpBw`0I-S+Z`!^x~`Z0AjHtRX=2
+_jttSSgr$;On~)a(0kmot%79zhy!Tt6cj{@Fo|{a&_GOX}#vxDuuV;&oqTsKOJuP16a*%Qma@2+eL^K
+GxzgO&j&|U6nf#p5gCyCyVS+o8l!#U$|<gqrO*EydiIgwUCkLZ^cmyV;rb8je}?bhD}{1^{e@r#b5Kc
++rX>-mIuOvfjws<S@f5{SC4vOgy5)>#AZ=pbM5fgxxTvbO_rRj+CwZ~O~1QL-zNuRWA%HjdtRCA*L}{
+sErc<(C{r-uMKe!2SU1!61r2wEW7$Xjj_{vcTTTsVuz*He{f{u6|}we$1Msx&*RD@z>lf-Hl_<*M@$Q
+<d6EzAZBFUEeOl0YPYw*Zczq%S&N^206D`kN}*GCYM+0cnr+X=*aqwu$bax|ik}@x`Oxs1{$ldSDX}a
+ikrfO$6Q=khlEQxWaY_ms!Yk=kCY?RdZ=_##^P_VS$iGKG_@$_vn|LD<)J#V8Zr+6<1NuZ%dCrpE@73
+f{F!hI8?vG){8+mBf@^4U&=vLOt?xPEHH4D}!;qqPb4LU{OF~_mk<=|#W&@#1BJjrO&E||i(+;4CSrR
+!5_IE`<JcHa7aRs2CBScYxvC!~Ut3|SS3j(-%-ovRXlb>cB0R7BF5mH{%lKaze7MT>knI=O)K*oc+(c
+x18*G*5kmpXDCI5Dtumnrz1}P;8-8PTkb}w85qePBMn97>ygH{kHsYyK*_H%srFxsVdv4oGr5rS~I9t
+byfMsSFjvauB3~eBN+o7-u_77baqkD(O_}dPS3J9clq)x+iYyq&Mkxeb`7XO;ayAjpCOdwOgc}2)a}X
+lki#Xk?q%crOk>}Ga;W6`)kO{=WRom|^{ZikFsKRAvY<dce_(9U{!vP78hQ?$rMfQ+4dywoU!yIDoN)
+KjZRpi(MM`su7h1syx<mfJ_|fRguHruOHtgU&1GjBVdK<P`c!1~*9*+nT@?6j<YyGSv$PNQ9n&V{}h6
+zw`0=lN4`_@eOo32S9Mrw<97={l|xqZ)KDB(rgyd9~nk?6#{UEb1JPb)dVB9^PqqM_k9s=cc-kpyftx
++mv*K(w^IU>IWfZvuB8`qeS=^YDPkgO>b4)Go#asv4o-$Ik<Xu{KHFGX?|og7X?HgVy?XB(ge2FhY@4
+Pz08sbZO`UtbGpF9zn5vO&HKemL3f1doo6(c~WVua*_7$&eX7_GYnf|t#NB69z1WyIET5oS6h9{fG&R
+H1-N<eLzdX~KxD>izIu>Qnn@>^wNhGx8jas*MA*#@cZ&^w&)^50dnQ&sc$Qy$cO6i`$L+OWp)E?(trF
+XSG6R~=hqgi1liANgR_sVR^e~%kK)v_|7>EIi@=xvjd#w5WnVG)H{9a~$D;e}19)~?chiYPu_=|;PPP
+;D+d5a|0my+prvRMBCX?;F^wyh?ni1nlC;YLMK6P059hxB%fGF(mEA=c;7Ta{RkSG2{8@IopELTL+Lj
+DkvGr5&Bv&J)){$E&~0S$Nhnt$;qb$%~q{CF0$9ss|IB9zRGoBaDu3=2-CAdC=SkkS6u4FNW#Cz7ba+
+%JH*vB)7p@$zl!qAoc_$9Cn-wdd5R<$qr=EJNhrYx%lV8DJ}KaRL=K_E3Qw_ojT~G8f48{WG<~W`qV*
+tGr4FfExR@WZ!*3$_@MBt#&{`=5z-c6np6uLQQhpp42gXkHpIv|X_W2FK=8~YVBaM^ZnfgW-H&Wn6F=
+tSzM8m?`$(&a`#EJ+6AP3oHL*~v&qV8GBE;<>YGRQXZia?do{wXyFxslUWf*R*#?pZBcF9E><!!xs0R
+RF4fcFyt0Q795&09KH$pobtN_|tb5q7*Be;DHn93<$iJ|vQVI&OQ3!lghPduV3*4J}?*MX?mU;hP4p>
+jr(AL>^U`=kev}pa-z45tLfudJ|xP3g~6ky}m#Jaq3DvEplZNf^ocX2LU9}X$+h03amumYHwHzWg>p$
+F3bvY^wBS&1;SQ_!ly^c42o;7P3Xg6so={&X&J#Pcux1RYZLUI2fOkiyT7Lsr(?%fv#He@tB*B?jfXP
+nLJ2)bQornx+(#vG+EJjNbMStI=pQgo?&%!WK5hfRCcw5DTf^;3ZMf0LpgFJ64_mV5aDX*NCl#xSvPa
+UaEvTSu)ZZt@5DjAsz;L}q%^>?@#ECDgi}k}Tbefk{>n;}Svn`~z7?9awEsh_>vo42Jd+hGeWW*90UY
+R*GY^u&cweFGg>RM-E&8n^1v%B%O58rFJzcOXk$$E4<R>g9#^<<Wpu@OaT0G{~nF}bQ63a>PruRn@L>
+R_#xZ6$Ya7C>`X3p#tlqgQ6{p|3ZTSZQ=f?0DUNG?OXklr)w>+o565Z0iO*^!hQiGozOLd1&Ax!v^L5
+iSqpe^K0D~hwQW#7`y|!I%gcLIsgk0g(7mwrn_c}VT=%_^}7sL%f2uZQSM?`rtb9?#epwHv~U2BfIb*
+f(-bOFplcu>1iTbp4$Us`7R`Z1i}pj2+|XGR-wbn<*!!;+Xo(&)+xwn1qn;^VttDyVyEH)<Ygh8&GyT
+M>#o9X<(z)0r6ELPLcZZEfs$}-9S6nejjy}~$&T_#QrIonHjjgiUlBQiZ$J`B}OWBMU!`qcY>KpBf<^
+ZBpk9vQ-huA2<@nyYGVm)4%s0V}R%+d0Jnpci#Dld&-40kU&7YD42>Tj9PHXDb`0S(O%vWR$b)u7c>8
+Mze9$V;pY-P<{r0{;2XpP<090%T#~DZQWpmChhW7+kLYnHVAZ4@6OLKnJs_D-o!_cNJL4p&JE^fwxQm
+e)yGA_?)X;0H5>GBy#M*(4U9^>rYFyrSn7Cz#8#AS8o8y;Lsn1&jJehkd|?>H1P)^r&PPhG9fg`uQsM
+pTD%epOf2CUm!~qm7wM>H;vBL5O8f@mdMSPbNj)FGfrgI3??tdJ@f+Bgh-x0PR>WFKd}|Z2BP})+kA4
+AcMHnZsfL!#XWt%kHr2<7x%=IoCD;rHDGZDWZZs8gIMR#73LiYHt@Q%OQV;6=%U?6&ln;ZGVa=QG_z$
+N#_Rd|W!u0<9dT`YG{+Wl1E2#(4dzcbm)D2bCvl+J+UM*NteZcgv%)I|)#jONx-D_&>?UgH<=iUo=Up
+W|So)gCKS#=_^t$`JT07Q>mm1$(^jIBzQcJ%6yoF%Su%o*Dm~C#A(P@Cb*r2tQ`xM<sp)eC$ASHk_@$
+1_ib0TT}AYXIl)zQyO|Y{>N^lV<+0Vt9J0%myOyRmHfCZ<j{t1O&vpmru9Q0U=%=4u3wWM!Lv3jXE&U
+eEa9315?es^F;Gw6gxdy`si?CNJrT@%8O&R7YUkMP$CIJbC)$c<a$9jyj5al3FZ<lofb+FyVIpdQpe@
+>xQh<Mfmd^G?3*F@<(AmOyGTR+sU;AUnLG8W514-%^W8`_eQ0x5)l-_(&dVQdlmR53w_N#7hzNyc`Hi
+nE{6jrVe)6PouqMgq><_hiem88&)(gvHuiEmC7d|>kcjW%^9!0op+pWvI&3$1u>=FnDt1nR+WJ`;phs
+IsYsC6wi<Ux}xWp=@_=Xmqz{wnZ7PId3+DoYC*wj&<G5$~n-^v75}$gEbE)ZH&)6j8|L0vh0HV;+xPG
+up5sEKzzpo0gP8b#|W0Y0nY{|wRlI^1rQi>0S9j5<L_4nQ}+(!C)dWr8uciy$e_&}1p79nR-}%v$X)K
+yf8GNtG7H^D*taqnBF6s{lGOdMB1Wa3rGE!2Qc5dwCG`C~jC47sc6&=<MRFjimF_~&2WdY`wGS!`WQ@
+I;0_I0)(M@~+ccltw-)tOk*h6|O!k?e}qT>?iUdmW_u%jO9cWC$hn-tj|yalomk90wW#uHo9SC;NTvD
+B4Puy2vNC_g<ko_Pw&F-%5rmY<7XgUZL^S7y23dBzgT(29l&!Y1;}DopzO;(aFMjeP4*NdZjWjd`+uE
+W=*x)sB$b)1GuGf1C*2@@HxNH<Xnr4mtWLomHzb2l}8MOuoC@7N$eK)z8}LT<?eIEXyQ&5S#JgwLfUp
+$_qsNb}8yPM(zcEyc>dide5VNUZUu-$iMJQeJI^7hS5K0busm>(je3(M<5bNOUQLcu-nD^>&kvE2#st
+Fiqj#nRVA@nFrB#T%9cZBG8qvh{&Pxx0+VxjG9GX1KdmeK)qphgPt~jI%Ay0(@CI1@hjnGY7?6hN9Qx
+IDWxpJdhOx8yg>_|bruC<xJyRc6SN3m6W38)k5Tds!LV4_0_(_tsWOPJmCZOR0pqM`$QsfB1CH{UN;m
+PSt6sL=MQ_HvE?KmNu_Ix>MTQ_dR<1EzOM<K}n4Ve?m@p2Iq)^5WaD55j1mSxuZ#?b!g6bs#9CRep-z
+x#k+`xCLnI=DkK3}UnmTB~)F=j2AMU<AIDM;BO>OXRArlHuptY^)l|#5xPf%=H1vC70Yv1Y;^XF3VLd
+vb7T$3ax-fU)#VH!s)N0RcidR8)(VT7g}uxJ{2g$BX!@<Nl|$x2jDfe(ltqWgJ8&-jW=h(J7MAi-Qn~
+f?zag*+p~l0u%9I)^)AHX&td0Etri~cwOz>!MAFdHx#Uo-du(VhFrh=9x5kQ3HiF>GG!W?xAKkT;xy;
+%CI#{dtRte^8(VR4KV8SOAfLu>~w=ybxDD;Y_wy!|RfX}SZpRr6<%M?S<QS~V41~7^BUkC_m(6?&cSs
+@!#)eloTtOvOl*!_`-cn76jGBV7V6&;Hiv0C>`r5lI*gZd4j(cx|dB-+WA+S0KhioVT3(1p8U9;|CyT
+pw(-WB(Z~nKNOCIOK49NJsQgriHsh_K5|`l!;l&WB|yO;=65H=`G}k2D4~X1}($G9GV+eG^eTOkQ!q&
+V;aH0XpaBLAeRa3M=*=Whe8^8O88Zm3rc4Ew~KFxPw<c#j`|;d%u)ZPtH>?C<3quWSwYhFQH!nVBNX4
+Ai4g6K_K%V5l&5VWlLA6k2w8!l(@hv8X!8J%q*VS6218Wg?nBcA{u?8^4=<c4R|U}Fr`K3`gB%@tSax
+r#k<ntgV;kQ=F#Xa%G`*|8U3q}4fA?{IJ+23aGG#LogN-GP-pJ@H;aRu#dl4wy1?=|!;7jK*oL7X9kn
+iQ)iuHfz9K70F20FGGd_FGJoq9TmH+h!IMV<IvEN?pssxO`iPqmb^d-0M_OfN8;A1X)`G?>W^xWSDMo
+EaW4D-yLp)v+2~%UIGr;4<0YDYWf#&=G#&D`?#NPDao3hp|c4qUX@ty-ISE4|JG-1)ocCTQi#n$TQK=
+w?9eiTlwUfV)RawB~taS<n@aGhkAf9#`v2T^4MbFr!4P>YcM?5Q1T4ten?mIJdi-qG4XltQ_R;X;g3<
+<=m_Fi9WpWX4u{Yx(UtqHb*N|IQ%ju!Uz1zvdhiP>1lCXm6v*)2CPDkn2jq~dVoH}`CO!%suA(#jMU)
+*`L);kDOPH2z?mBEkr6k7iON(6z!Va%-ExO^IA(ql`Zu$wZro8IaD0CanuWC|W`*Kvatj050t!``T>h
+uzp=U$CPIt=FqD!UO<hm~yCx+q?I3#O@4?MB3^XKKnzc%p>M{ZA@8@bY*w2K6UK%DdMdl^t5N4c|>yH
+IgtcuyCi@{(D53zZ4ypZQ9V6$Zb#NZyEfplD{SXL2q;U+X4QzfWLjr-xl$=E%e4}380wp4o@IKBx;-b
+7>PZ2KPIHF#pM98_wakPN{qYlVdNH6J-f@u3Jr%k58_=z3{#<EhBhB6E{95QZ%?k`8@zgQ6;UJIsy&0
+@mhST3HVJ|<5?Z+|hVKbwka~>}%EA((`d4+qb{?tg_y=Fzq@rWOb;LT*(LCG8&3`t^hO|6a{sgbnd1x
+hf_K|6Mxo7+zHmBD3GM0DqN^_0(Eg)8Q4pBud7lUh@2NtB(n4j!qY8#J{%rfxq*QB&*Ykos8FA$g4Uj
+CQe7e7;kvuagx<sOiEY%jloH=)uUIEHh@0`t_%aQ<}xc^LUib{Wn^1D@-#2$U{OCd^xayc+-zDA^^i<
+a8QR#&yaoOU&*9?%e^=3p8)^51==H&wKDR;SJtZ^7Ka?AH)b-K+)JLe$ka(v0~(Al{`_lwv{y2mC@Z+
+xvTlzRyX7W2mQ8rQ=m$k2m(ofZ4X#ulDNV%)F-Z}%(yNZ5b}M%WzJzY<ObZ^YqCK|wl)Ra9W|n4#6Ye
+t^tm_Ie2zV^*Z`+Yq&)!^3q<ppqDVa3wR?E5#@Jv{%+GHJZXjmbsEEwtifLndI@CQDGg6~46<>;H6Z}
+rJ87DeN)g44sFEOMyYbADAW;<Jb8s3ELjoxjEEa~(#EUA<uOS*jZn-zOxNkUrT&@;V}yAs}I_|pZQ+8
+fjPDgFhS_-i)MR=6wV=xGr=nZSz6e4UEbmwlXV;W3@KKN_WR?GirJDz_T#iUfb9+Jv=ugstWnTK3xhW
+7A*1%Wqoyy$cDZT(=H~I?(*&WnKCRPj`!5u|t;vba_a^Lu!w1^|3A%ob8Q-8l`tg*Nr=ri}7O|evH5m
+QSJ4F>V57m_L2BxlRj8t+w@$CeV}K8Zmy@Xk66np0iES$tQ!}T=&nWOTFKq0l$E0`O}Vfff6GTXryTv
+)l#9yotX?TB7c$^$kUOS~j=L~Qj(w>Qk>0j(yY$=W+l8b1Y!yx>65J$(yOd$eY?IatLU+el-`p2#qf(
+vdZG$YBnMWBWu8xnC#MNIp)D7t{6iCNeiL3YJF3gZ}%yJ+;Lb*|THat6&On5#UGPLC~UgeDDlXJC>ca
+kuE{vA|bah0{-!6OsmPm{~D&*NpjbutT9v=Vz_zD;<(T#QaDfoM>Z*oR&&5xDL}Lg(8<4%1<V?249_w
+Tz3oW1-1J3Ow~)N*T0~Y!oKZJ?69x;)D$6*7^O7hOUyU@REGJgPf2D_;7>H-hy_WKErv=Jo1(q4s+19
+zYP;9jsGs)G*Em#&(t@lnZN$0W<I(Hn_->Wmbd8_NA?)bkMBX3w$R_Thu=m}A(_{IFS*x;GNjtA9z6@
+g4%6Y$F0&bXSdP|!zQ@$lRjHm#S0L!mmz}`wuv>4#Vms(RIj0>=X|1%Z)g?FhI>a!lv%Y#mjQkBvuJz
+w5A(rD~>(TY<1q@X1wIP&7Xh+r<MqMeER)R1P>sN*mM+bJ6BS&Yv&&N<5S@OR2$%hn=GQ3({^8Uz>L#
+~?92IblHocgbW)=}Kmt~<y*g#X2>(h$bN5LV-kUWK7y;cr5D%-1=tDU_abSWnC8f<<ovbe~*~LXC1kI
+sSB$GPWFlxCys|2fofN$DarylSR4^8trZHRqDf6;k{QT#$<aN?yc0*k=*c|EY`nFhX$>HlS>*SOWMTn
+pD=;75~hudl_Q4n<H>TOCJWp#G5oN_^dGCi2ltl`I3)F0`U4lkr%epw5sUb4u+q!+<Eg3B!cR@F#akA
+9ZAKqxii6PO79kC=ILpOV-%Uy2SX{{%fhUq6ZsT`PX^(tBa1-nCeks5dWfbDb9d%3EzFxCd2<3UBo<5
+v5-CirM@U@lq0b>e{gf*B3AlJ@XouYf}X#9Pg6^<t~hWZAoq`h{)?7`WGPImWJl361;v|sXEy&P{#SO
+hZ{e|8p{*kShKV|_GQKB@9?G%2zETb@X@3&)w(*sJ4s3j+WHM=iZ$r5^lM>!7Y2oo;LM6|K{XhM8W&k
+E8Np-Q#l~T)I<F$8}1&zvXzB-BoFBgWA1IPr-Aq9X{o%AtxQ$v)mR21qPQcjcJa?J(+gz;8IWNl8#UT
+5&4m)-#7ugy}%?tt%yuw_$lNQ^<6MR5`u9@XWvcqEJ=$}WC(~-&lsddc=Z-eS7IL-PVZb39#<I7mDNO
+LUI%F+P&LZWn#rsQ13-Hw4q#I4oP2v9Eac@9AQ1hq3eIc;&8#;$vn_HYJ)5=0FYd(yn*J*V7K0X;wE_
+(nNjcm@@)7Doi}4O=BY73_*DLy2<HIYEHIhME6$jk(sq9b}yA}z`5UD4&ly03xbbAtW*5d8dh3Mo_;A
+I==w*WpONlV*_XGxuqF##$U11rNzk>o1Tyy#etI_GTUaWoi1!_u3Ga57QLn#4VSYw-M9G~)LdM#>WEA
+zf8HSSqm?-Qn$H9RUz80Ol4`Oonm5nr-)kFln3|!*m+`7BpKw-E)n>)odLZ88>h_Jv36=_Ezc$;x>3h
+*>7q5CDVHI8C^8sXWO)UHldgEOjm)gtZHcJ8o8)PTygf*)#_2HCL2C)c`&_NZ7vX3WLB#QN=i^tV}Y!
+;rpd-;#ZwJ(oV(iHskG=5=zwrKtl+MxxT{5a5m$6-8}T|BwIwZCD;sOsu+}DVMVq*yxhUo>1=ShvLxU
+wSkKX=+2Fzu_l13CQ*^V;XFI#~)Jcwu4KQ-t7X8-)(H0RIlzBvCt(ay=@irgX9YA?;Imu3|!02%eHYV
+`=E9ig<Q0>9dn<~Q1K%<VYl4az!kMU%Lqy{L`Fs7b(IZAPDYIRwUf<b&kOe|$nYtJ<9w%0p)bnYR<#2
+RY)3?4npwAMs&y$Ibw2hn<v=x`p%r+XfiRJoaE!MS4$Xz`9MYY730p=C5kLjtzBCq?2rytyAsYl2uf4
+#kVA@x~EHiI$&)Jlx#rrGlet%;di)?08T+BmBJ3h1&`q6S_7_q;&*B>3N>x$Fx9+tW1B8$UtO0hW!li
+Ukqv!X6{4$c`T7BEMDil|8p&J6zZm~|e7$*R70Y`FQ;hK=tI&vW((jt2u9n<E*YC|P>1Ny118JuJ$AL
+8EPW%YsN2Pkes@$j^NLMaV4`e9Aj19^a+L!-It#u7N>YhM!yoG)XBku@k|8XRHkkq%1l6v4vUkva2`1
+@{rpRc8NCCxb89A9&S**{QaD|$!}%6^ot3_-g;{lvtcblKX3Cji<BuM2HhEDiLY=pFVTyU3+uO!Qpd+
+a2rhhD(7C_h$ngzLOk={TR)iS<)CkMi$INp=>D{v}y|gZ8w1iYzGAUJt(cq)uVQ0uzEB{$yJZ0E13b<
+5rz6i0roCh#{(4Ptv-*>0+b95@v^tjlHU3={6r||HZSfZ%q(Mh!9_sIw;|<WHXl4&+uYlhTqsv<pa|x
+l(bUyBZFa$E!rS0j-q}z{e@{Dfh#VhXXugG!r8R@<kCt-xZV-ijP4@h)lJS9VV;kYiy&IHt7;+X2Sq$
+eJ_**(OUiWS>oJFV6G|Kq#K29#@l5fMwmo@@A1)EU^HNW}ZAq#!<x_qto9jx3&Kiu-lY{Jkjt@O3Fl}
+wr;TXb^1o`J5CC*Q@QoOl-t*}o5KyZ*(4pC4<bdvs^>A5vm@_tX0gv&k6bcdrv4{T<;*!e4YO5WU|HY
+iSoB#TZ)r!GJhj_0Z*auU|NW?rf9YO|m$tL$-FvpzjXGGO>5i6oi;J5)TgL=uHK(`@<SP=Kbv*uFg)B
+V}@0Zo}D0Ze&g*lwSz6#$ri)8hTb86s@-t@lV`S4=BL=AHtJv8a&+SF6r6Z(I?@3hmjL67dOD4^<Xtx
+dY=-k*0PM}C>E;+tpV=!~42`WJ{X|3B>EkEmo*nYv(eFwTtCufx$VOxRcIEaLGeh6{R~miv?-BnRfk#
+%>$;=VN%MBat0qX|2XS=^-n}6JU3UIxO1k}5szFV0h#|}8)xeT5!HJ{Ig=i&S@yS_WLN{)IhMl*i)(d
+TjcJVu{K>GKGE_R?npaBIeZlW<@`wvl+(1^SSB(d$tQkc+Ff_W&(i+34m*!T>S+0old!7KRW<oVU~3l
+X&?n@|tm87cv#H%WgB}&YxP$l%82sBA-ftcx1b;5gW1JX6Apy^Y<vp{F^9Wn`P#&;Q7y{@~@`+wq*Wq
+W+4W5LdF%1T#z3N#ec+$8_s8w<?f$F=wpXMI{uJ?v6dT%ZQX0>!qA?z+(vb00Cx5)s^X=(d7rGM`5Mj
+%v&>%|>ju2V8#s**@#{j8uOzb3q1Q+$Nj*~ZhVP*j$#@@+M!LN&k0k`!`yr<esG9c^>TRns!Ek;$Q!u
+YK>`47sCCjTc3k)%w@0bPb<c!6yV!L210^vC`$Khq0gXBC<Mfg!W9$1uY((ZqfICw?Mo^q>6W5@*-Pj
+r<*C_H2#haX+-jSSZ2ztS&)CSE4%3=tOcP|PBxZ)^x1#z^OkYF}iqzS&>Y1~P_FJt>lIK_4Cz5o}l}j
+)fdpNE`M_($)RNl1BZNRNOR%-FBc`{UDitY!4McflF~@!*OJ1VWzD*GD6pIjAz4>*8Xz;5N8Y+B1laE
+*5p@vjnLV_qMb0PwCc%`^dTti;Gix=Q6;Vy28y<Uq?A6gT8dcxcv6C4$|w%mFlu=(dHF-$C=@@#_u=m
+;VUs!4k(oF2Nl{OQz8ps}`DJrT@sL4>k}+|GL#Fv}4}B*W#qdruqpd$i{LC;<ywpFT`j`4A^uIv%?U#
+~}Z34ixn!lmBs6g#adn8ZowT6Z<o97kCK9|`_;X9IdIlc|i)R$5chU2AVFiBzaDRHYi&Cu0sJn|X_2&
+LbHy=Q7a`xQLTl+`199-ItVdFva=%QwkuwUbVY0(e1V{FWrm2;E<1+XxB^KVE*~Uu5VklGUSo9(V^+m
+O={I?$7jlUD{?ce*;5I{}JhIA$X5i^$)avCo}X)%9u$RcmTEL)Zb%@zU=Dw5`@O$Q84?Rpwr1kzy@vh
+-<uRMTrd(XralUoId`$kD_8CC;SYJ?B^5lBGc-oK1G+f3RJ-aQDL}UVoyUaZO`rjGG{C-)qqgjKfoa8
+#*(7!U7`*HT&(bP_^NdPqnIe{=gfrWd*6r!&^VIgG(HXHNlqIniiu?Sn)a@1R1xjAYcD9jjF}QsQ-Ah
+>L<N+WX=m-96e|ty3-xk>e#WOLI@n3k5)3A?dIcO|1K@x7{=Nk{dgEHSx90};GqWEO9;k@}rys;%1^|
+(vP^3->W^$ReP&N%_H00N-jeu9AmzA)qj)6n$SEr#o1y_e;bG}iWJg9eJh1<=7r8p_Z;lDc?!g`gBj%
+e=z_4FT63JW{N|>zXa8b0CX;wm|ob@3w2Vx#-+sf}gd>Ma_QedKqsz{P`;uZv6M~s(AhP-f^aB;tg`c
+ZPw2=EfVZKDycOm3p`=r2NB8NGX<hJ6OU+TbMHPn2PxT(+A7@)ZZw+*nj3C5!}Y}a>n;3Tr-C~oz~su
+`#+2MPJm#feFP)7*^s#k@bJEQmh-1;dbW|!jZbc8evN^6w@o^iv*e`Yk1pFPP3a$mD+KjIegccZA9%0
+7`4fDAf&V4uWwp#>hr_v%m{sv}vU0$PA|0&`OVNP1;9C3ve)M(IZv#aqJT_y3!brh!tSQkK98NLP-zl
+we+NW1N7{FNan9=aRUSu`-6Yeo;UP1RV{qq$Q>piAtSrox(~qV<xyW#M`81IG<x8OUqS5qNIe3X}O<Y
+Ve8yK+DgiR&Qnn8JuUj0`XqLtVuJ_tt@^p%}mufChuA;(4A0(_<+fMX&90gWM2}-p*sZO;Akrd$v5VC
+(FInHzFB}l-R%-vXAb9lX<53<Z>*b`9m;7KCZRJ%3kI7Tv4#wMtm`#kAJuG;&z*@b)8PV&*>3R`HR8n
+q6(H~2NavopF>-`Nt%Mc?mq(VPPhok`8)xukK?hYUUR}l;l8d_JD*Uw#cZ>LVE?Rfly43_t8l3p(wNO
+6(Ex-fUKn!;#Cncj977LzfT9*SqNT;77#RU$D)sbNm+8@Xc5u?BANCv&u_4eTmo6kJMpRD@tGrX4{{?
+rHi&OE~lEvL>9Z<me_wwUw6-<g-_E^PW$Wc<mc<cuyoaYng-ZI+~I0spGa*SaGwLccXF19vq2ba7D|?
+&k%#k7qy+EJmTX&lEM4Y~Pmff~0MZm~ZYwHzE`gI3-kZ@NPD5YI|30k)u!MVQC+01prm7<N%`kcsN{G
+uH9SIVmN2qNCYd#3W|r<-M4h54qD2BA0kHMt5agf_=JHDNbWxIQ7bBm2;;CJhOuj)voqDBw$N0q_qmj
+>;h?|L{T3^G(JFB+HK(`Zs>nct3up*FlJyMddq3nW^RHv3XZtSwufP`$LSyko_#?vRDl_~U6qX(%&fr
+mUq}nCINU{EMD55Q;p6Mreo_vlRALHeaUIh4nKDAL~gD6Lyzl{5cH;ke~I%JzC{1V|Ad~E{*d~GN2_O
+;ySPUCCa`qnAFw(q}nW?$P)7oFm3>nC5^Aqmsh_W8f0K)U8H1AJ|-y4du!{Tol!mh>0Q{>y*zwLQsSm
+1gD8u1-~t|AA&BHlUB8yZrk%VMa^P*@*R+zN%^cGctWu`HV0e_DE66R~2Ve1W5~vOy#BNhNL5|qax{u
+3&`WtKKnBTz^?vt%9oYqc+*8R$06FEQyJPNfBp|XtdsIRe%7hv%IZ%kxX}>w@y2!bMR_tyYXCJkO?)e
+++mTH#eUpwLLg`snJ<Uk{fb4&dSN!KMh5sR5vG+g6D?afSpzNk+Pl{Ju^K44|lAk?uyy7m3SKN|}SDb
+O*z<9+knkim!`L8fuvE_t##f$Fyukngm_YH_wZ22GK71R5N{luTn6tCD4cky^d3Rgg_q1C%=><RITJ{
+UqWUUBaU@rudN28>r+b85WeTaVi-5(DEEV{$aN(O=alTidP`->T1A0J&03OUFe3i7jCl>yd9stj&jXr
+^*aWeB2CNoM3)`Yo>o{e*fP5t~Uc1UH{(%FlPGE4fX$C0At}(94!j)sP%LKjAMYL4-8;z{f_~RpFJs@
+F@Ul4gaF2ACk8OKU3%I8#wq`Q3t;U0mGJ*|0OK_XO6|&FsQ|{`{XYv}B=o@m#@3Sq7^m<6#wh~=7^hG
+G<COn%0vLBc|DOUFAtmqsV((1=qO7{c@jEOsEINaNf})NJisFKTiK5BKpfflq?p79{3_`I?!;E6NfIx
+vGhHbBvmX(#QZ>w9qiu;C&OJ!z7X7v!3RElWo{hxF1eTD%+?fu^6`~JTMAJ4hlx#!&V+&%7bJO{A1=>
+f(%1Nk2wU~Ka^Dy8cgot6$Te!UXiOPt+rIKUYHljB_7fBFF9ux&Rvz?i-5W(OFrZvE2-7)!(c!~w>-t
+xXOv*6T{GH#xw#W9w}kU^HxP`2b@=L6ZZF{3sr(*p3s78`CI-j*gvR9Ez*yI70_rkO#Cn4YxYb_|TGD
+A7~uK4m1vQI?!0o4>XqMkI1yOg|3sGXRMjeHdq`|*JN`<Q)2>TnTvd=(J%iYJky8+6@I31EI-qT_Cp-
+@7gaR_#gWF|;z(mRaip=MIMUco9BFJLjx^$ajuwtI{?fkXBaK7wB;M^EY4oUf<VsC}(~(Bjg33o4m#`
+Cy*Rc@jYAh`rX}r(*NaG}@BaN)#l#et<-Qq}Nz%7q7)?X7x8h@6LG@g}@G=Awg(s)!p(zx$tM;f^_yr
+X{sk2DrM?NGtmJuQwjK3wm-PeUAM#N>>EmZQ46*fZiQ#XT+S*7%%y?F>lbC`{s!*tVh8o*Bowu_*N30
+mh3u-T*`KkB6YO`yGcGYw=0177jJy_$}(jecZPiXH`DM2R7@Ta*RwK=i{t-(W8<euH5V<<E+frTOMcO
+_T$v<O~zU69pkJo*|O%z+D_6aM>Nu89<!d0LaH#WU>Fi4jSw8QZ}j>CTbF2Yti{J9)ZO|thW~<JOOvw
+}`<qxbHX&^b#|J3I^)s~{z~F}W)PMz>A!OIC#<E+|*)pEv%w6H->$bMiiYH|rYR5p542*7TTn|aon_b
+o6pS+$l+e3&sqS~rcDhsP)YTHW_9l@p3Plzqhp*wH5M~1H~-W8;`*KAd=3V2Y5Ps2-(y}>knec2+lwr
+qSGt!m>=UFp3(#?pJ;(tdwaUp8u>9p_fAEpzM6?oGq{yMKpf8kOHIxM6dd+wc;0X(BxU4lSM;lkW%S`
+)a1?%hu=dhwAeALv_81<)N6F?WFwGZg9i-#zg9$981W@M8W43M)H9g&exPEXaz(Xz0-1WnJ#!bbbDry
+Z(;EC+P<m{GZZ@2h6-Kuj`<z6VbE<?#FrV}%y{kK`x@!_*D<5_x-9c3s<0imkM&`*H*CTNpE9zKb<^1
+o@|S3falTocY~r2Fv_b6Mych!B?9+YN%R*&wW_)Q#&-a%CZ#w(Vi2XRfFAW^fg7;#adTJU%%iif0fiC
+xFssc2R%%?u=Gh^8rGn<K=(A5~WM_ccz0JX_ey-erzU2Qusx>(iuo;azk{H~+`6RXx^PXT|{mTt%8Bv
+7qv`)WtyJ7W{NLOQ&lEsN0<HU!RB0-3Tgnpz(%n%V)CvN6@oXh7Qy34PpxvoB`xmfq`m{0W$R{+3>V!
+Jexz*dGox*uN8VFRa8#&m3mwk>neL+phrSse`_76HfdStEb++3ipcDX7VE@-Ct&V0bbxI8Zd>TyMD7A
+JdVM^QkOdF4H{LM`|1+Xc(lP?TDbt@aNn>T13k*Z+TGVL=UPN~9p&5kc?|z}k+O4%%ChkqY2DL~<$A{
+%ZC2M~@Ei@kQFu&x7u3qakO%SQc03Love$j~OZ*rO^*<3qNd7`7Jk}}B`>AT<3jerfYroe$%Hi`R@L7
+AJ@Q<rDKWsxad)<SCn?1&@_G3=O2c5#jK%1?%#qc)|<arF_3`DzF;8-MwTK1b{pzMfC1HQY2C!Q2A%&
+6(ew3Y$y*>Pn?Z?84jcWXbv2!}`U5v<m=(%^ayujneg>~5(kpwcxQE4nK1y`~z5Un01@><IP$oxdnw^
+aGnM#XTxuxkp&Cb%U}ALHLLywk*;pNV3w!Zl(A6;7Wr=^$6P!3H7UY)U4p%)&Ws771mJIhA7nry(0SZ
+eE+Z`Kyz4EU?|_q&ER^%5LRish^v-S0k-4%va#JZUk3vDN~OVd7jv^ua4V=;hr8hW6xM)}S6I{5s-pX
+?Zg>aVCO0>1gg3QE;Iqk1iQ92D`GTo*tdFiVO<C(&dY@8Xwpa}<-DXrxp_ya*V~2Oe^ERsDP1pH~*{9
+6~OML|k{nRW-*sh7oa0Ts*+dpt?{lqd|KQ+$CL<Lv}N8P2c_E&B2Rc+8JqO0ZyLIoevRlCep>v}?~RG
+@8wYh`rRymL4*jS93KF%(uP6I@Rl!p_+~hIA`9zm5(5)0Ea;h3DO@oeIylwR+Pdw$2}O(LUy)eauDs7
+)3LNeFn)5%&NziE$nw;OB<GRZYnWTZ3sI717Ii`mMi>VuMtWP)Z*`~TrJIKI>W9QSs$%*+7D@lb~`V?
+SemTimoM8dgIYtSiUVvPm!!sPwd~Jht%DwR>s35hSREbf#-9Y+tid%u-7~Cf#uLF%US_l2=V@L~_h~m
+VFAF+D{zSuQOKY!`l<at-#GbNco>y#QTNy%LF2eI@o;F`$Wr(iC`ftOY_<4R<&9(j!cJa8X_zHk}_ZL
+d>88Y@*_EvUo>aeu=c{~ag>sIRi_vi6lY-OqY8w~L&b$<ns&y8j7`|gH93?;qriu)pe{hR};jk@vbG;
+L{-z9LouO=9gC`{(Fha0QFTq7L9?nJzGBOi7WVJdAnx7B|@zP^{&PI>HEbSy3SNndoC}fAF!l_G_rjq
+y)KEqDXBoJSvN{6*H!^>vxYbk$`9SHK3q{q2ksX(&4#6^mv->z>ppXzmj3O<0?b<2uQs1TKn#1+mMWG
+ILPjU7W<G2M+IWLX7k&OhpHq`e0I{^^(DvgXH+EWs6uZqP%HSx4p-Ki7HFCp1DYDcosDJMnS20<dv37
+Z9-df-<0LiC9D)xQePLb$Yx@$FjaC&J+!q0buwbSFlOR7?fqT;MEFMk)X$$w$I-SDPi8D7aE1g)A%>m
+8K;Z4lW+&%QD;Lb;IhhDrmdu?U*gdOMafXtsxQIgCG>h>G=Dink=moG5V1^TkKIjBxWZ?|UY?S(S)DT
+PtQHcM}}bME(BI~ESd*E}{_*eO7yAZ=uG@AiPEKAv*&(U<!`qS7KK`v)0bC`&#LBO3#bIJ2oQsKLfnO
+5px+25%c{4Z@B{!?|6ef*bpBBa)H2aeO8l;=3CkA?$c!=fcgH49M5py%{g)LHhMUoXLK8=2o82?!M@K
+__#W2d%azSvUs5^?$UnrSGc4BH3a)arIX9wnaf@c>YiKKD5c33jGD(DWM^UJcMncsPH8dxNTc1?7=6q
+-2(R?FACLxK!{gq^tlbS@c$*E;MgY6Ol)ePd=v6`88^Hz=JDuK=6XzKZ?PkrGtjmlArPUhX&}G_5Z!`
+B*Xsiw6CnP64iwaE{I}fY3`^Q3KBu(eXO}U2w&p7ELek+^~p2}w!QHR%vMIP+^FD-EN{^7VxqPJgBZT
+wYLT+E8u0qIRVf?76U=EJPT?6I1kv>A$Hr}8)14fZ(aa)j-*!5dS=ZR&VV*6B9&1lOR`wXR0{SK4K<Q
+Nc?_y6_oo3>g0=KdZw?j&dd$-jrmIo0C+iinb6=^3ofe<X|-X^advxjE1k?XhIUt$=W4+Gwy+(XBu4l
+pRSL?_4rbumjpAdO{dy;m_My9bq-{A$5vRqHsOiP8uU0EbnL}<>dP{NgXu(NUPmZ|cqcS8a~`S<O7^g
+UM{PUR)&zoYKWd)CyG`*7_CIj!vpX6>FWjrzxTiKoKW8G&E?+O5sDyD_Z=jP_I=B`(i*ZW4x0u~_pQi
+${X{9h;_He;K-uMvC#>3LZP+5hAZg^G}-;ry^ffIi;l&|sG+j{2lmUzEHKfJBaDt4-3XA6C^{AL6@tx
+zY9f<r;?fYY??bWuHRA#BHany$15-@=rP^(UR3_Q0M2qfG?Wy)2;`_pfh$4wr`St!C#Hda(zCRU1GMS
+6!5;fvpJD2}PBkzkOS^K?P%$YN?tuUIL3}V089Uyxmn>cv-PHTpRuC!Z5sQ1OzDlQuS1Y(Y|+!C!bso
+Wjms%|7#IEAZWAg^>Yf?P0-PGmfpIkxeBYs&Mc4#RnZ-+A|=!bwYvuAv80${`R45#I7OY#g{>Ct;yZ0
+P58ARx_<}NjpdqfBv0>M{QU9(H{E#Vbp00hh65njyUy~25Lz6LrwBlL5unpyI!<Tw{Nep)KpnTmGf>Z
+bo)!ym61yk%RZyamr{z*!$ubFm~HvNI`Tv6O7v)$%i=uMp#NpE$3t!ksVzgD#o@2`Cdms|d=_t(bq`)
+h}5RU4fyu<gpWJMMPD8*KgY23x<)qfsAZ?(7Utas86Y+qlHGr8QyARW=d9-iWumKWmjIBC1@S)`au)r
+PK1EYLxRUY>i-*2;~I^`qhqgVZB%v&crohX~8NeiytnP*M&Fnb>WwnI%fC6m&yyQ>scXP>r_Z*d7*T1
+`B--k#;G=jYVF73>{sIK2XKBGms!)|ihouW_3&bAs}+TZY1xFkbY=JTz#W;!(ztwlV4%U1U9`SCO&hi
+oGJn25C?B)++)lN@sI0zWnRUC~bsz3015Z!_eH7a5HEwu(D;;XDQdM*w3EKC^p%xY|R#p={IcyN-R|G
+5<FiyZee8sgmQ82YR6@-o6u6nG!3yw60x{B4-N_n+)l0vMuZjo18-{Y&Ts-o>|wQr`X=wTPW;yRJ7xU
+QC0Tye9T4hn#7+W>3^hEEO7R2AQgOT6{FVpK(g+}X0;S*5D5-VKkbE)1UKSVpPwcXwPhYb?d`19>o9U
+ak#i6fv-OZb#KNqASZe@r(`%yABs^b@qnZl{(e7s5tviY!Vt@aqF5hpD*0bt>P;T_J*1yY=kcDVvDY>
+ao*nzhrZ5W@3x7r-8MMljw||3RWyY!-a`K4vSklO4OdWC;Io6esE(>4HO2R!+ClSME~WgzmrOwW*3&(
+FkC`r-F8r+Kr|V54g<N1#YS}|mxGyi6Ng~H=jV#dxr{dDTu4HPk$v7t;TV5kn0_<s1@sS`Y_p*p>fj5
+TDtaVE(jjPg^-CGrBzgDvd#}jcSjC`DH?dG`z&*J0KM`0x{1y|YYa9$r5f3@3CH^fa}`kFQJVld9svp
+~_?7w_Gi!3zvqDP)VnyTD>|j<99(GYw|Yq;X;RO$=!hPWVW`<~(jFz6iw2w12PFLy~gIF;$P-F!D@X_
+t{zNLyx5ChO2SqxWc8F;>z((J<HviH$h{Rmxa&qWnsP~jE%uo6fLeS124H;urRH#ViLaY6Fdu-h%tHZ
+t9?LTBR;!e67#L|{-M?=Zn|aZE_pVH6jc@FLy|ye5EEB+w+8dWJq`2{mr{?t(8jPu<8oJ?lzh>c5!C(
+zZ!mLOKd!bqZoVCk<<`eihX$k<$YR7gAzy+U#4o}1XLe4P;5K@6UPwNt1oCVl`E2urWKV73*-O#aRTH
+n{IVdw<M?TW}I<lt4b!1nqypVj{$eQx9;v;+|S<hCIAxCt@KS24v%Jyt~>Y~C_MXuPM(}Pg*btPdn7o
+j@XeA9H%HMY@O##_zbAP=J(wA*l9ST(T$scEC>;y#Tl$xoFyR+3dk>DRc@e+jog>T6wfcFM2Ay_CzZ!
+{v#i+Io#IDOXVcHDPSwvz)IdCzR;yM{qeAtU%w{qqiT|u}3MYwDzlFIk`)mSS#L=i?fBZ7=dZ!Y^|8p
+049OFpj^g04RiLy+NGGNXtt_+PF_{MaF7)yY?s`5TZpY<%}-ZwQFoWZs@fDLs{X8+E8Nn>+F_GMwk*s
+0mMtxN;%YLp*72uGt-*P5VV87ammq|^plsjA)`dTlUgbC9ZgMOr&vIH&{^}BoMrrK=|GU}znWxR+tUG
+6+)A1VG;-78M!_mbldix(*d|5QEoHsni;AGJdfgTO2!EYCMql(u=Ee^p&#XQ_R%(qiZXX`<6aJ`20se
+Z{8#&+!h1_}@0<W5X&F9)M6`o_iNq2e}N@Kk&(FA15o-o=^KG&1{fJ!;9=UPc_#eyrhDFGegZL(|PRn
+(dS7X7hD&vA_)7HG1O#YKG!OEz;oTTD-tzcVUCUnpoJ7ZjIfX$yzRJz~-5(iJPw~_I2S6kavv-UIU{Y
+u4HbG+iK7G)cnF!?n-sd_qg5F`@5Qx3ejk#X$zz7uyq8G=fZEkbz}B58x%;ZFe*Yg4skeMP}MvGj+?N
+{hS;FHUWUdDhY_zo*p%ZiV(YXCTi-JMcibjPfvv8F8zw6_)2bqzZP2mZc9?N|H5gP)dBcYke34~B&0%
+~gO<Q=apDk2ebISx~H4p1A_9A$q(|#Rn^z3mmwl=(Rw^$q2*Wh+$u{Nx)ImlQ=di}+Hw@&oLGVxn%ny
+J$5(n+=|j5{eEtHnT9UM<ciT%XZK@3h`o*wD*5WOEqLuwI)7aKw)l@96D3lV$)>W$h#+T{{;3SJcK}`
+g=h0Tv+K+8=)(mR)Sn+I<FL;;gWHsLXAZU$`BrJPe(P85k@tUX@$iNm})TAYXe%Fhi#NDY8ulp6V}Qb
+6L#qBWvJfb(DU2-_zZBkJKLCmYOwRh1nJlL4vls3{FXIV`<~i&Zq)n5ObiT+=SH`E*iJ}pl77x*ec6D
+=aQ@4F32#W<e%&2!NrfTLMSL9FgW94CN40};rLcwI6c^oZ^``r6ftxSj2+U{mMFj4f>)H9}x%i?b3t1
+ZE5m5m{ow)gqmC(EA^9{C+7u#+!PGPJ9pASa2J-!V1%wP}e4)}2y(*J|qx<K7=EgE9GM)<OG9IzWeA;
+(80r6aq2oY?Ks0=p5%sQ(5wB?ct)g+8v7as0$Q2gmE@wZO4_Z?IS^_Lr7&U((%<fVBz0`rzoc0myn^{
+hXtq<@aZ-J8$&F&DQlgHE<4Hmz%maNQx4a)Qv2xv56IMcIK*u`Tyx+`+?E^9UdMsmJQue>a<cH`2*`=
+3l~QC5L;x~L{Dkrqj;E_nL9({9ibNQB0mt!p7uW0>k8FH`0yu`dp!cZIJK(A+kJijzkzD%kLPNd+@p*
+yA>(9)p0Cfdd}I=jC~Zk%Afs06X~?=><c9a5<-H0qyej<=7x8o1eRw_a2gU5h8V~y()+B;WDR5REL1A
+Qcyo9MA!G$zNRc1$Zc+RDGdr%!tB(N7K*`j5<-K|dVPP}pA;-sn&9}cdMRVq})crQfZ!;lywxya#2m|
+67DmyMSbXF@N2d7-W>U4aT@Xss)a6^V4ci1&`E=%4!U?8!;Pn4g*P&>g6@AQ5^Ry$781r4hz5_s+oh_
+%L8})64dOTcnrl1NE0`jrKE;jcx|W>Fv^JqieN&@AhhUooh9_9jO-YZk&m<@V)Ivp{K$t&UMj0sEYCJ
+CAzl;Pcd)7wx=w9pHz)IoN!K(-N34!gKH@3a6O~ct*X#{0bm+~-KR=lu$?E&_T0{UVsJx5_?{R~ufk*
+>s1_A78HL)<w*TmkDiSDT;xZ6%3y8t)R<TE?R4VL8q<gMmKB;t|V*ZGpH+y3OeO1WjcbZLbu_-nA1on
+;RrHwGk4?C(_`n-Z&6s)wQ)Nl8&-hs~#doOdtW0-7%L;ZFat18V77KM_02Bdzwo3$;sa$`1b`26hl8h
+6_yj<{O=peTCjnIn6GwhmvX#e<eggMBA^e-`3k5#XA_?`WCEe=>hg@6&>ckUItm8tqfPaQ0E7FV$8X@
+mX5C&Z`b>8`wLw+Bze?QF{U+>t2I596Hl0*B4yDy(*TEm{z9>aPv|H=)7Vecd$bPHqPk_YS71~7?wUh
+JU*jpr>7Agx;5I3UTU-o0HZ;Tb^&OoLAy|V0T=D7(OyD(&KTW?wsl7K3ho<*0tsU<6g03$I17<U{xa_
+7>x|uI^21kdaNXBDgY<O2oJTB!^o2DpPMO3q2<+S(nIvaWVq*ps)+jMeS;8qAGsuHy5OIp)HuFd4)qo
+P$0cFe}Xg(*Y(H0WUk4K}0frGE@Q<gosw3)mtf9fcm&+`ZJBHuZic>Zj6${(YM57V~+)3*-oB(%d!Fi
+2=ujdmx|t_t{KIfU6aNsac5GoC@1&AvCwpDowL9s6lGJA|iL`RsGD!QOz66|<>l8=rc{S!b6Xz7Z8Q%
+Lre&axL_v2L2w~Jb$S?r}3O|P!4;rELCrxhK~?eOA*C<7Ft{Qupdzs<MBe(#<-}>l(6~CdGUDky**xD
+*_Ivc#U<`-5Jno2BhLa$g8<E6)IjWnX+d?4OK%*@-pTB|5Ks!o%S`)9*Rm-sOX>*>*|TvA-l7Iok*V2
+S^E5D*v|;n&dvVJI!6;Nk{>+<W#<|8Fr%X+upn8sX)0f4C@;w{upqQ~NE=oS|$!-+EJB6u#byt3IbK#
+;;kT`<fviPO-#ZuSWHPrv*ARGcbq$=)0`KxWsWZwzhgLG%MIDHKQ9LVCrtBTfLZ_3$|>>{0WQQ|fC0r
+E{qVhs)QsV%M_DA@^jU5P^aF{?47a<iOIxbII0+oxSdVoTf<23k=h&V&DqWuRS&uk=oqd>VW=C_M*mc
+!xL!k8>YobKqM0Wv#tV8d=Ea!9#H#TwAozwX3Z=2<y-0#+~QId+iYqGq|%=h?Zu^oi8%R%c1N?wVT;X
+w-FT)`)e%x4enO%=oU)sPZ1RFCmyG;|0>x&!xAa6R}B)u&IMm`my$li)B~$gdiz)Uu#>0{tiWkX>R%O
+vvs+k$%4R?#voFrRm-@TnnKhsSX**b2k?D9Ww=fg$MQrs7>Lk{KZNZrxi1ceV9<SEfcN(I<(c3?bQzh
+&L>zB9lwKO07D$dE`F124uhD>}`+TG?Yb<dRV7StCWq4*xeVd)9RYDf?^CA)!1R99zoh8&x=;NCc#1^
+*O0J@hFTQdNX6mxG1rBD)I)^t3Lh&gr{Q{iSd8xY3(ZTZpvb0XZV+-~-|y{Fy+!84^A4?ZrUcz=(Hs5
+WmFkp0Bwiw=qVXo$h+q5<(LAktc&V@)U3PIXGsSP7yU$wPBV*=~AIB_zr8dPPMg4d-MvlpwKp2X95IU
+E4HY3QD7)S7h=^QbR#zXn0%u`nGZVK!7=%%B&q<+BuY^m-pqa}vv;Wt0Fy$wq*g7v`=GlbDnG9HOPdF
+qb*oirFP-XA5ke{maYrUFLzP00$7>5ZNzdNz7*_QGls&ar>t%Ppb0MUU!g&v;I*nbQDwg*)@s*r?s-i
+atn`Ek%RQZ^;_f}&QY3rlfI89wPW?LHW=401tVeN0y@ToTLs5PrLCaBrw&9vwPxgl(aqvUlrQ+(S%3#
++ajg+BE5LL8#2Hn=Hk``gF+K?|ngO%}V<4O%cYpDo$gmaUXQK&u=4e`BQlz9F{4DBp!O@gsiYj@M?E4
+2fW@cOQN~WRBI3A2HwJPe_cmOz$Ffm`f?U%2nE(E$-T4&%8^MGaBDNPj?!<uzTj(&N5@;iWV>km>9_J
+)n-qS)VzUYDp}I9M$q22<bJFWFv#+eK2l+pwVlUpDC8=pdTj`AD#cl3oWKzW;jaa-Ci@H3MjabqMm4#
+Xr=|OO3eTyHrGCLiEZ+$cUmAg0fHABpK{e*M$!HG>!71g0=;OH^)3gO=NL@nNbEpky_qd|14f=Kni)9
+KTQ{$e+X-?OBCF06<jK1_<_-uN8=_0&WXUAPQ+~2J)jZ@n1O^%=m_Fv=d4W-ivG!OlB_TvWob}c;+6N
+NX!(TX@|x$E#DZ9GZ8gYJvoSvFI#(8YdIXE){9FU1v~wR!063Y|UK?a~@n<=893U5;t(mDrUPo>B65c
+Xv?#lm0kLlEg(7^{8QQs^d2#tBNZY--laL$5b0GU4+{2VwpI;%s4=;2Sp0n==uHhWm**s73RC*402(0
+orJIF;=LGUFc8G9@2$Nam6h!35Ot#C;(M>X<g2eRmG`RAPYLIa6>?xu$M1<}&jLq_3oDFl#mGi_SiuS
+YgO2EMoc$2=tL$FJ;7&L(Tqj?opxuPr#T7SLhuIU%xEh8NlF`4a9&=+8lX2--I~?J)cuNpF=zY2)zJg
+9L&c$=N;QCATOz@e||J|WBMDLqFR2JGx??@;n`<`n;cI+K+z==;7?{$^spXj(eyPYimPBpvd3iO9;XK
+qTZj>Y!)j=1X>@4d3enQ^6(U21(8AJD+bYaE8MscTg8YTJq>@^mH+BqjA90tslZgTZHA!ElVz<}SHT!
+2Kr^8!diIfZk#cGzLn2#39LSWX%=yZ%{bxP&ms>YI(-#-W%SzHKR7kbb~~L9gjUonumc}QSc^K>Q)C=
+ux1dPbr3EFwJUJM<AQ7`3OKv5jnC?af^jVix7Yp|Yqxe@1@&lQaJo<$l0|D}gd{c_?Dwb*_S^u2{kx>
+*bu0$!Sa`EKcHeZljuE>p9dBx>Nx-+At`yhX+CX)&o82zmYlh^98q(z$D}hi^u#SA#9_vW8s3Ul}<l?
+!G%wJ-RuAV;=>P4`lUJP!nh`juVqlkoKw!h~n9={qL#Y5#(JlN9{;ISmZzKh+jp5R&`EqVy=k3J;V8T
+(=ODuXC7%J!_W^Ecu>*H9~Q$L100Z)lH~_ctjBSH?q5?S*_NnD2S<e<t6P9r+%k#g~p7q|gPpnP@f3P
+sqnrBHv>$-@|#qEDA2?ZIKZ(nD<O^*I5qF6giye)E}AIN&wUUF@CJaXQ7Xo(s;$cQo3V2yQN<(4d6(C
+6wDEg)P*CVQhSbsOP&nzmK59~Mrw%XZv#l@Ig%*-z>%5KR~$)}PH-e$I>?bIX$MEl(q@k2N$<uxUI&o
+Yal8caRUAjDrR5yQbdmDo9ghb{R*qw`N$DKN>LJbI_$Jo(avbZDq~kcIk`%>pyslE}&vBebmoyy5mQ3
+>FxP-Wp<8_E%H88vZ@j8y9z9F6ExDxS`9A_IAD>;ta8>I@4<7P<dLqp?6#`T8A9f+$rj(ZrTa*pG$Kq
+@jgHZn?i2FC_QDU;)Pph8OKI37)wrgA(VaXrUz#abH4@p8n&Ilc<<V2-CF9>DSSi2HE-UBn5;Q9Y0@=
+y}>AE^!<W1xlwmUWIrS$JZjhi{rRnBW>b%HR5k^Tta*;$LkP(lH(1Cmv9^xf~AEVS0ZlaxEk?Pjt3w<
+ljCdywvpqZh{td|9PvnwM<E`{@fgH|IF7f&NNSEJBJRfVnTR)xV|X&+=Qy5@cs0lINQ-oW<9Ueh<9I&
+epK!bc@pn01j`%u`uR?qk$JZjhoa5^e&*%8Nh+8?1cQr}r<JcgV-wy#J+_c8J?x~M%`)Lx46WpaSEW~
+&v*c{|G;oL?IHru&PH*PZ%Y%XydA8wNjHa~NlD`Vx@Slfc7->Gd00FMi~$D?C;lL9stZnKTsgn~^vw|
+Se}goDj}+~zrM69qO?xlJjzi2<8<ZZn_T=)oq2+nBgbI@nC$HdDBb8EkaiM$2vTz-9!u88p_>0D<KIZ
+rORPqa^}M4Yzb3>u8d|vIDoQi*rhMdEF&IzPLnZ|5k55ptqlr>R>2ipArD7R{f5ppxs;Vb=)E{K0w2F
+aAJk8-w|VND@|vvY+|80g4XX)SOZHRR@U!OtBTlE6CSxcb6sERS~?}b9vc8|h1KT^_qmkD2E5K5oF61
+T!Lo=wQg3Z16>}4NpxztCq<8IO10uG`{_<W*FU1C6v&HZCwukBMQ`BJ(s6mT!fk^DJ!G#jgRu=!fI8b
+Nb4dEATW-t5AC_JD67HUS~T+nrD?NxaFy0shL9SK@dng0?qzvNmz-RJ7Fqt-)T*7k1`K~SwyW9@`f4D
+6b<@;&k(`5c?giB+fRN;S1X?5PPHk^oVNnq7{im#%EW=%cTxinrp{AJxWPj)?`Q`&OFvyWxg8+YR=U_
+A4+xDBiyqhHzCVJzmSemK$-QHW{=-9oGu+B+%zN`#!wbl6n*3bmBlf8`bjoC+w)vegLO%yDU?TS?1?C
+Vux+Io^CIyu+Gq5+K$_5Y(JDYUQZfd=I5DKaE7YV3a&J$<{t!QD&Fn~Rd$DMn_-K5ovG0khI)0BO|wV
+rFcj~!?yXhnyTHKVB;6TNfs?l7MaQgtit4Ss#092Z@B&kTs{r@rs9RCJ&5p04h`=rI&t{=}q6mIz<;x
+T!+`Mf4`3yeT*?tjI%eL+~2dd)kK4OOdS3QvKQh|ia!IO)17|5fK1|Rq6?OJGdbY4oE5l}UvYTGD40}
+u_c4a5n5Tthg7^TzW0^>cFgF7gt?FF1o;TZl%-XZP{dtM=(t`$2K!v9~%tMW$^$lNvJtW$n(h3j~22n
+}OF;PJld%m-Fn~W*K$VDWi5X-U=mUV}{N1#r%S76WO)B)<^G@>bIX)B^Zp)E9m*;D*P8p_!|jOm9F+;
+FHPcZ^B5f;ihZHP#X9%L?`AW=dif2^wgq?dK|k)Iy?ou;hWd~5Ao2y=>`8<Qb_HmfZn%PtmDxpL(K{@
+^{Jyi2hFEZ$V~Cagzu1FKIN(0tX<{M~-vE&I#NwF%sDPmMGM8}fk}SO?T*74+5hh%Eym=Vzq5vh?u9Q
+|SZ^4znsdOm41*gJ)U`tM{-fTOX^m<Ev^m7t4xcFxG@0N`f+wXvMmlG56uUT~WwMfd|_P}_YC<$sq$P
+KQTB*V_MBmkQ_^hs|PX`?%vGCeo<CQLyJMeHw#awnuh_2hQ@c6O0`lXNd!6i%!WdiykU;i5=_?+<v1B
+}ZeK``P;)?+@V0BY%IuODg@gg={KZluXzK;`hvLK^94IR|~fGgcxb$x2=lq+Y%)%b$wb(PHc(z%hsJL
+(ppVRws^YM$({W5`~S~RrC^|)fEocu2xt^AS-^P$J}%%p0`3y<lz{aD`ltn60mB815pbq}_Y3&EfNu!
+6LBPWT{vcq3fL^{Fb`x-ffHMWm6fj@FX9avmz)u7`E8qnI-P&{5S-?;M?+{Qg;4A^t1$;=r5&@qT@O=
+Td2)IYUlLG!Cpt6IIN5Cin#|k)A!21Nu7O+Ubasl5EaEpLd0{$dmgMdmu4m%3iTfis*jRMXTaJGQi0_
+F=?F5p@L-xqMJfQJQ?e~+k}np^vA>Vl6q5i&BLL%&x!Oikmk`7ihjZoew1W%rhTE5G)gh7eWd+3-av6
+eQ|*?p7V4V07{$Ih+H3xd5k;sbnk}K%xW<**uusL5w8ML3<jRKnx_7Oe8vH8=GT^%geAPT5{%?a)D45
+P@6+?h=rKoZy-q}nFP0WT8NEg6D!F8A10!K&jNqwPs8Z8C0-0ph1e#NDe&j)M|Oo_CB!~~jBCXeRCfr
+SK(x$POMWJUJmDk){?KLs_|XDaR`8ic%m6j;<-q?okOTQlU@;m2L#a}kT^ggA#r&p&M7e;a!2h|7%50
+E#0obGh4GXwtleu8Z1|KN>JVv3B7P@CJmlOz>K^_wRa=_jTQbVSX{!-!pIn3Q8;?zKFHbx)aH4twOix
+u6>@R#Wb!6^;|7Xy=$Q_TX~TxLd}4**@xJIaOJSy)Uquth&PAi0Lo%Vhq!WGO6WIh_VD*)T0Lz>f)d)
+{sy#2y8QfT3?oWSQ6$qcydn9lonGeoAHPxXb#IA3**JoB&3e<lmKBfn4Hb?dx#^yvmu0)m5pqcT5~~G
+4a8uEuyaKIa_X4?xwLXlahEKo6m>3p&hZVtMftih-%U>G9t(7)iu|!STzE?7u++o4_3tPj&C@O1A&1P
+9oPI4yklIRm41sn<rek9{JX@sd%~Au?0{L{NDbpMFPv+-*Ch5)VT}$!Z@lV8ur8f1SjBltTJ~PWPq&p
+A%TOgE;(P*hA$#t`lqD(LHpWr`<Q8ke~#`z#ovbYIzPR_*}`5#P%-!#8-AU2cWnAh(d;f}51JXR`AH<
+Tr3`c2Cda)lJ<Fb-3l%1&$jM00w>Zb2_gw1OF|CR!NvR7V@}K&$b{^#7Ui(=?`3mi{Ttq>$-1)>BOD9
+9B+3S$mt#`X+1%OpY|?rKI)x8t#;Tx6d#7;AIr^G>e(7E%F=glz+ERKL!4u3qEAZxh>?VOuw<cPh)w+
+Ya8#e%wW!76f~@NGcy|tGt1oYFsV1Bk&BF+<NBCV7}wTvv!^h}2(uir?k8?m3v;<J`wDZoFt-=x>Kbn
+DAk6u~93aeUVGb1L3W=LL3v;3{caiD+$jw1={KDK#nDxTkU5@VuZq~^033Cr&)|}<$JUKmtd4U|?cij
+At%%3n{73NLCOoe%sFek9OZ((`~>Km9B(^(%Uw|cQ84E{8b(@ix>BVe;3B$g7+g_l#l4P<|ycSBwy^|
+{Pvme4mi1*G=?D*^N2|2tW@=CQ;%YIzSvKd}k55svf<g-|wD%ldM2wk<Og&^Gu_f#txp4Qb<|c8iOPb
+I0G%aG-@AHBpH(J(Ofvc=(J&4@hVYD`Lvz^kn!!f}8W;rvx9wgD90sEP_hnp(KC(@Ta-qFVyjOquY&t
+ddQiZ{S~$F*VIRW)1Trt|D3}wb^3F;%|AJ;=D(f%{|kOM=4twWx&$a1|8Do!qy#7$|6FeKudxLDPycQ
+uKmPf_pR>u??Y2c0ayK>KuDp$MH@Cq%${K(91xpKyiXVBjq;#2mdD)7`%2z)A#FI}w{kK)mJp0`0HP6
+4W_QjW8UiZqYudRRmjW^$V`<=hP``-H>eE89Zjhi-a`S_E6Y~5C|eaFsSyZ7wfxBtMw%0q{bR2@Bb{K
+Ti9eSY$bQ(vC`>g#W+zdiHa+3$b&QL6c=_T10EoUi-!x8E;ZtiRN7`N|(xuU)4^;o|D%-o``e>E-Ryw
+w+4t+rERJf5%P%ft|Yqb?w$&)1zmv;E>*ZLi_d$>px)Npzy&%A|i+0F)V8Mi0F}{?i?MHGJ8&{DQ#|g
+#{CauW@YD?AGG9JZS&^mEqLgjS@+(Tl-#=e!wVNJUh?0T|Gypo|2X}%V`6o2W5?+Y@#BpN6DB53nmlF
+dwCOWu-gP%m|JL3ASEN6#2YEZyhpu3Db22~O%v=U$XI)}NGxKB3%;n9@E1Q`gZ)Sd?nfXJPrsj{DnQ>
+VYb?OTEa5-q4NuFz*k&&I6Gk=OT#hQ_mjrP-WO_qtX?>Eh{8nV-JELqH9hM)uP#*|#Ec8)b;o+&ZMY%
+@FTxg94*5`c2bT+^7GJRWiynF2cBWHy5|kmx|mG!o}uoBaGo@(c1op>usC|B<Ene(vs|(G?dLL&ArL=
+O^ZufOZzzCn|A9KIm(p)Fme3E)q>>en~!rBB0i#r>85G9(iW-e6#rmeNGR5BS5o6-7=TW)bd}d`0Gz_
+?v4Lx95xHYA7S=`nEfDO17icQ!=F|_{OQ<em(O3~NLKz$0PS{O3-%MmT%2q>1)y2Xb2<CevJst)&4u7
+Eo@vDk9CYHvc$+x<o7t>srZ6WkeSQw~;ruri{LW@`fsJ}Tr<1}oZrPU0Bl9owEAuJyCG#Wm!Q)%t;6s
+jIj#rKc>7=lk7z-PhH}Wx-ao=c%Gj!-Hx0$~gY-E|uX6l?>rh;#0y973tZuBvQ@#$<%VRG?!<@jX!GC
+i4&93IM*eneK*h+Id3N`+s4O#=Khh%A5~Pyd8Lsi}jwArXp;RumbXX^tstw#iW-CgjX(VxBn1nuEqEC
+bLy`(AwtOa)H@+TPC9yZ_8#GuQgjTFb@(^EOQ+BY?^JcrC1yZ7N3&Mj1Vuzh4hg&(nNYl3&JlzT1W@O
+V;C|e#cH#prZ75VEg1rXbV;{aavdf}FH>r^$;o1>jhhm3vYF)+n}8`+q=Vrx4EjSq=+46ZyStlPboXp
+!W&(V;eiMu`r&v=mH71KC$D+x|)?}vGvgf3mEF&}_xomK3VWk@VWn`LCHP#$Wj@gv0G3A-&*sLkDGv%
+5&EgKE!o(2u2CM8>wVg`??5C(G%8qk5w{8>yXshTuPP8NjVWO~Vy2biBZvw<H+Gp%{hX0j~E$eyc7V~
+pJ3o@=sNot#OoH5J53wV5+B=5Qaf3;%(8j?LP-2XKc(Y2Ce1jyWKR)ub`=UPoigMK=0`<o0QnZgWjmN
+VW{CW=>A3Nt2T%hv4)EvN^@Zb}lZ@3>_5@$x{@<Wo4xR2C0fsfOvfdLO%3CEH~XWI|VE=GqOcO-$2J?
+$;!x1VTlBy1M>qOwB-QfkcLj#c~fFo?w!c+@bfdQ>6nzc))b4A^=*=EOdlqt@Y90)GXK1sI@7u#y;4m
+qiJkq3Y?(H7?0~42)3;BrKAIFE1?En(7;g06qYucKnVB=6<#(?evZ0lHY&}g{$#=_KTb3!?nyU#l&4Z
+ekk)|<anXL=@-a5Y7CXHN3Q-IMqSWM+Y#1bXTl$9fz(4Hg&dX=7_8-%dg;HG9mCQs2#IsGE-o0I4}Zg
+YkGwF$=JJ~)ty9We{_s@z%AA#)bY0^JK0tlU|-8Cm8`Q<B`C%*wkXa@J&1rYR-YG%MC<n8KWm!gZiIb
+vC)ZAI7O@<3idDaY^*;pd=kqeMpB$Z_;71s;X^;w|P}xJ_q|!AR)Wq%m0FhI{^6CPG&(PT;Z)C-VnLM
+&x`m)s)*kZbzQq^pDM2krP(97O(OF*8R81`B7wWSNuU*AswWAIRFc5S{#6(s^0ZciH+&QFtf223<W7R
+d1XOf1`zQN_6C(RdhBmHSPbWUQ`jD<xZ_+i@i*%jpNxA~vu0sN=J5~8t_^RB9D$;|fhH$wsd>J&_e}n
+HsIlP~e_@%-()dTo%gZ!I){n~Il2#aPp?0X@gCx<kKZHfL1y$N}}ccULqFE`>9=}EjMw{iHfLethEl#
+s(;aH#NcAwCLUqR{vdO{y=^OjQ$2WILj<1l4t}4yfu_;b&F^`H-Mg6$zT!js!&lJ~^PSW3^vZ`wHM!)
+rP1bO;sSfYH}klk3%c*{vblm30?vRLi(icf;8Gmx+=Dkb`?Hm??li|77<eIE~f?LBc!7r(DIwCHY?l|
+#0~S;!-aS#p5x_5{+Rr2nEbv$UYtMRJIK<=o6Jj}I|vy%j1Z4gf|mi_q)X}~(q(EQ=@L1SbWy|;9)k0
+O2LlvA-XzEx05W$1x*bW7qCN4i?oj1h0d82HtWb_1p8_H80)dafA(-bar$zQ?Le7kCObb>Xtv(PQ%4D
+P$mc=@xESLX&S_lJQnBe!<7(yNe^F6|>@K+LlD}1RQ#2@1JAL3i#d!wDBe9w&~<Q4el$>CZ25I3ZE;8
+aI_s8DoLl1{*Xr>Ov;Y;_va!R)BFUA#yas~XZx1!>lfba|YmSfV1piv&PD4w&jq0zj64$vh<$L7pV&C
+6KGyw@Ovf*6fq)9qtAB?Td84e;26tU50e3<7ujLq*ecEgxm+;lQOUBHbiapAZn0FJ;bL%;psv=hq#9;
+0+bLhe31SD;1)2%&&<*Q=z{FkyhZU!mg!!D0mGowM!LSfZHO=M>pP^WzsI00{s6wmz7+AZJoSb=^<sI
+dV0Eeu@fy;`?CXTfd3Oh>iZSrLE#jF5_(J#&$?<k@gEr5DbQq#?ggKo9b=BM$rVHSc&5dDroJtmtC)S
+BEjcw8}P`>YnukI9wieN7i92rQ0f9qTqP~EZ0ucE!#H(8y?dG#V*md3VV(L>Po!Pj1vm6eOOP%fZsP8
+DU-;$P7`&S#*GtXafygub1;pxm^BGSilHn%tpIPJP+`;3b4?gYTX%oy)&dTReo90@?pd`Glkw5VAn#3
++rq*s8f%7S9?}@RJfbnl3f#B!m+OE(d`az5)%2KYltGvwP_E)>$9c{#Cemlk?|NI&pmeQvJqWQ$ddBL
+vJn{WO}a;>lJ1JTNlWqWbGdoEM^_W#zNRtWz(Jm*oAo2oE%igvE%F1>P4O;K3tDDqBY7TrILaH+cxNr
+pc#)9xa#~@ZXK@J+?5>15qb6!gBUeZ#N>2Zq^oM@0KwtApBTZk?-^%VwUMJ)!_;$+a&_hYuPgRlj&@Q
+yMu$Da;;|iDCja|@YSfR~GZ433@2ip$OZomiaPXNRZ^_R69JWXGPG+o_dnpU}qItlgO(f%`eKn`gC0#
+hCBakb3v;!mJ&gzxJn<*yz1g|ZI(KpC%gZhzPOgOFYDT^9azfPcRg<UHM29u@x3#zOk~zu(T>HrXe!N
+jqVIcA{n7pbm8dg}V!Jk5mvvdpAh4wxs>z-iZoN1@RP$6zlieV?Wh4uDi88q?<3)Ej8&7+wO6%I%Rd6
+Dz^$3ksf+S8|4detBLBEw;93>?JnBtPLL-ZBHCG+hYNRvQ$e_PMD;%K+|Grx8{!qN@N&U27OrTI`RWB
+c0vy8gRne@ElhdO)WHKz3<7mvQ!2T|zv!ayq0C^c00{x6N68MZDofP3j)(P-{qoTVH>2CEU-BZ<2CRC
+(*W8a<_IHWDnSZ$!QSV3n2tvAv_G>UA(wJ%O1*;ViXZC9R8<I+JM?&qwAHnUx$-Q!@FcZRh5`)l7ZgM
+aNi=J3C@@3_0dc{^^IVw}ku%ZG$U7!l4Oi`}M}oae{mb7u27bF!YqvY8HfdL7c1g|=BNFg%qhD&1$KW
+LU@MSf;?RIMXD;W`WOalLbP@=49vQWSU~FmdrR9&RQWwh&DGjV{W#fFg2$!O7a87fDFgNfZnl(xU&n`
+Cgh~rGEHMMGEEawvVd{&n(HK-BWjWN4A%r(rZr>C0;_3i&WwyyQ*3&Qg*+uBa`*%N_yMFn#hR*z6v0_
+3rHfb@h{Gu|=)W<2oP^;1EaV)~iK!x!2s)X>X6EF|rtxu;C&U>eh73&2%!E<8Vv5xQ{|qzmAgpnQHqp
+QwK6lZZQp{s5CW!es!?ieYC%=)nY^%u<Yss)e6q!?iS6tK}(XK{Q$~@Bz_GT`H1@d6(0yC!0@@6@$C5
+PSQxtzw7J>xblJDo9{8kaZ6WJYR`#8#Z1{QxCz)!D_>kgGGzw#}VuvLsqextO)R-6mTzr)OBKwv^0i+
+4D29L3i$scE+5X2W;lCF!)CbAR10W1fWZ7irH$jU@5Vh@~k8T?Re0*JW}go$Ob`EGBX}(F0wW|b&5G7
+J2uA#@%{p-g)=~jIT@I0<RH!pJDI;^DciK1Lrumsn{Kk?GSjEb4=*23J6*)Ok~THf!cwYRV;ST*3~3z
+cJ_ER7Ws)q!)Z#9jDRKpK0NpT0$o)KrQXp~6$i@tE47kiRS#QY93b!elCX<=$be(KUNoB-|6{#aCi$n
+r%tP@b0=PIx)#bwXSu;gUpED|phP<b`(ay6Km<ER_tTUOs%@J{YljLpo&1WL)YnaDk8nj)L-LK7>r`N
+)@3Z8_-FLQGA^8D9gGftl!^7i{u6ueFIL3(Jb^IXt;Qq-Rm0sTp$~5b3w~#?}Ui<+F5LkZUz%P0awAb
+6E-lMjjKj8!cuKt*bF71xo&GONwOy$a9M2?_?8aV=b>8yjFwu@V=875*1SXUf|i5n~ucB*wWIVn1Bwz
+d?jM`k!-}Z81_!&XkuIf;yzF-AdNCoS^JeT*F>Jg6mn``QqUZiojNg%=j<ex#uZHD5Jq;qv_hFDB!ZV
+P!PyKl$kiB|0FEr1W{|&rxBTVn`MH+n<|eyB)Z~^w)bSO!^wreA%PoEV_517h*Y9ROxy}uF>6-!1gb{
+M+Ud^$z6O=l;;~jW%eRCBM_pspJXnYC{PZZ-DE4Xh259{N;5v72h0(uEIZzs2oA}m6cpsN;kxKF#i_|
+U$GupcVST49bA=0p)@vap{b;0!_kE)jR8u+Jj6bCIyU3AjhwB6w{h{O1dv3j|y$+zW+!k)VV3RpXB1=
+Y{!2!OuDo-)q8tqu}qbaQ|Gm2YB$bkkgEcU;d3pC;p|s=56h*+TMS)|34f4-%bC|hX3pLfAe>%{jo01
+ey-vthhOyzKQ~b#AgSZf<u`G@f;P6D`19G%?f+Hq-}_Uy`s41JR)7C?*8U^E)_41G_ttj{{df8PSN{H
+AF9Xef|Hq4jxvv<Hxwx?{uK50Y^3nxMdkFVezv1x3S2!$qp_&~Ti&>n{;Ck@C{pC|HYg(aSaGk>#0YA
+D;|5^bzfd3;eoqee@`Tiy8rF&YZzxYLlO#+2;(y9ASX-Lwk`%ewI(f{$2UsqoyWbk4h@;bD~u}<!JS9
+v>X70@hTrhw@JrV5xW;7kD%1=I@|C19w4K?14;^bt@gpqqe1z^i|7*dX8q0qX=jC!i$YSplmBJSpHl0
+Y4G&!#|qRe@no10<IRYT)+|m^8`#6Fj>G^0wxL=C19w4o^DNbCXKKU5Kt|kQa~cheMQJCV6}i%0#*pP
+Uchnz^94*7Fj>Hv0wxNm7cfS^C;`I-3>8o#V1R&X0hIzaTo(KZSS4V^<)-xC73Q@9mJ66CV7h>b0!9h
+gU%+4i)dETlB0d5CQ+nyTflcRi{<pc`GA$xn2`}`u@(UuJ>qWka_{@WwhI12oojlhxP<%4|x*;5%js4
+faCE|BM(3N8UIj9hPHcx}=Vth%)ySKSn^4<0ipZbAsm$!mg0e%VJhk&03co+unb%0j^jP3w!j+cUr1e
+gq8q>qBZ$^L|#06$X!eiZ=iCD>O3{3(F@!2{Rb;j2(6hz8&_ylNjO7dyikzca2ULA(IpgD)NEd<gKOu
+x|kPP8ZJ4rY`JGA7+nmTM(yT0kBtBZXXPAn!xd`6qjxw9^h^OqczYr06r4ncko34eiontI?_5<@B?s2
+Pv8OUBLP0!lk>A0;N)N^Z(u(a;9K|sj_|$^sMmle13U%aS-?*NoZg#|qBaUL6X4d~IO`330vr|!?H1r
+s0OLbB|3-jM_a$U1*yCP_r^2AEMjW6G3P2v<c>s?LBIHTHPXg>U82Smog8@z(%<0SoxEH?DV80LG4}-
+Z};n2k<4&i<fngxz<!w{ZURRAx**MP6pL_j)4V8wy_0@wpSH^9RI4u($&cqG7O5uDC)fW0HRyrBS7B0
+0@efDZ}$Nr0aV{3Jl{p^zp(#|L2CP##tf@RGnA06sblSHK|M0RAwHhxLmh<iQcpCxE>b;KmW63;_In1
+ov|R;GR*S4}hP20LR`5eIwv{fCmL$32@SAj!y-69lo`AKYt7%3u1VATL^Gp45x|kLoJw*AAq_sVDGMA
+@ZcC;2F?OJ0E$Ww*jEBn>$ofk>)~4n_6-1I#sdF<BfM`c&!c33KaYcY1@?6SSL%7Z?*e?*0A&E|R|DJ
+-&*^Le=x&7a0Jsw1-3grMSpb(zh4KmZ`2e4r2K5@l0^Bi;r`0Zi<EL}~Mu1&sK;DBN4Zs=jxna2l_&R
+((fWHM$I}_@xC&&x1ZZ^~frGmjn=5RVC0OzJc9}ND}0S246><E9v)k44}fZfx$TpEBWX`G)_fC~kV(3
+sBC6XC)PUY-$-e}LCPBfv44p#K3)9l)q8NH-4!gXSzAFT(W#{}A98S)Bf9fGe|s4%j~lFd&EfK{zsp^
+M)|T4C#h+0RCzg<qF_y4?-OQ`)~`CL-^bvUMs*L7)k^Kj_?=wMgm?3u;;@(e}e&zSjhPt3Gl>1(Kaq3
+WX>W{UjP;?;`T)VUxP0X{I3VNTi7=M?3WMiAkqQ2B%g=HgK^6WpnQV;a)2)vaR2K7o+{un)&X3#6!-@
+}2-g;YZV5QTkYY|J6yPO+`#l2m9})BcRy-o=8o(zW<@x?3zzZuN9icoRJpLHS4)_UxZ<a&*3HVz8e=8
+Sh2yo|fy!`9}c-Lyq+bn>i*8okhj{*3^8qsb5{Cy3N_X5ED=Xu#cXkH7t9Qa4r??q^@pnUcR_z8T$fF
+m5d4)ij>BLTL573v+}egH?l%4uf4M#%T;xhxXE(XaD(V*q{xUmp111aRY<kamDq0gQVa`~#i{@Ts?XT
+&n<nBk*c~o!;Se0sv;b!|PEdz&3y9^+*YD_7><b08a(z`!Scx58zh#l+e#s0F3;E^NH{a_yWM*?H@pI
+8^{8<2H?~k(2fC)FklxU%K=B&e-|%%2+MYHe#!xQ@8<ou2H>VW&{hDB@R7YxegH24xN$F!cN4&}eZU*
+omm}QA?GbwJ=Xe0X2jM%7egJ;GAD;_?bO889CGY_FTL8x#0{$SKbpT&F40=1@>j3sV0{s)<!2sJIhdK
+hdAHe+My#GbG>;$)84zSB-koSNG0bBrI5#Z$jV?KvG0z465r;|{=0S^cGJ$&mie*yOT0_4TG0FM2F^N
+;5a@QlJP&`A*DKL2n?D};xIJ;HMWM~Hjg5l0v%aD-C?j(f`I2^`^*0!R3^z!B~fIPOW71de--aqlqV2
+yuTe@`G@Jut)fqz!9z&IKrI*NBFhC5ndKJLch}-$Gw-40{`px|MricX?JDogP@Y;w<BzAEg#niOZak{
+lRpnOE?EoqIIh|R5W`ggM0W`wt}BA4d>o_%0K~P!a7A-E3U<Oz--11x6KKI6e|b*D)!DwJ75i1K*spK
+Jep4&<`&zL-*^0f?f<4Z&;Da1W3$#g)cxT0B0(DmCe-(}9fO$}#m5t_##h_ccH=09>z&)hVT=hDb)s5
+!7E#Tj+(OkV%!On!sX8kq=x!Wl|$aA70Oa%rmoo_g}@i%bbKr(LJIM5eokc^BBVzb$JMu8%d58tX)tH
+^7wy+$@~-b^YgD#+)be@?DnzfQ!`N<Q<9FNUJYhdfr9??Y63=g&XFj7RACtNA`x=g(ib5Ivlyl~<3@%
+1hw9w+`pwz#YPZdH#GfOGhdzE18)Mi<R)-d3rUCxzAr%r#uIN=^kO8e?-}U;nUKta`O`OUwI@g?Z{qX
+zRKM*!Tu_mf&SGa{7qNzvxL*<=FCgXe`V#7J>23FqyG)Y&&?1&rS>D0$Qsxoe^-xO{sv6-=+5|GdUaX
+l)yjILf#&OzXBoi5)l8rdX7^9_`}JL2c?3jA!}L*}e7m5-c|He#59lbb6%X+>RvrN#%Q3l>S6_d2a1{
+5z%ngT*tn2w!CG(3P*q_smqzw?9tyu&AhhLsr*>Lqpeg*S?gwku%wl!R3&gdV~w`O$%WzH=9k%YvZ(L
+cK`{3Lf~^cip5nJ7!dk2{mA-7A(<yXKFEBGBFGbA?ZlPk2Mq%BK+ioWqT7<wMn&<`V^cuJC!lr-H9L#
+d59|W&Kv=JTfwpBqSt|DO0A9haP&UNf|FMFDGxj@do+u!w*>*|Ln8R$oJoW?<nVH+#c{Qw4v{A^uU~W
+*!(cvkhYC1*>-+g+5<Uc31m^)HcFRlt4)HE@W7H^E+%aNWXbd2-$UsiX^ZxMKW*ByZE42HHvuL+5dYv
+rN<T`Xi%a`#yJuPwSwdgl_R%z?w|NE9Uqb26moB|aSSg0_#^UbXsoJAEC9dKlH~C~g9^zB+biy=>Vd-
+@9+tNZpLP%Ix7zqy#C!<D<B3iAM7z_rMc9SMeBGac&CwJX-7rFP|d&%O|v1IPtx#a%)?<ZMVS;S(ou=
+ZooqDACI+i3FUL!-zCi=#>5+&HrQfmrfnj+Q)Ui6I{sj39648_2T-v&qI)X0qq8@uceI2T15vO8W1hW
+bhtJM(?6z=mAPb9;Rg0AxdITP%{1$C6m6PBp1GEXDGSnM@r^YQ!@J;B?Scqq_nh@tX#R0{OxalBhNhZ
+3|YN;HCelMEqUdYS6KeO`|i8sqmMpfZP_QEd_s=ze3vZ!m6DI@DA~PxH`%{`KRI;h5IK7EC^>QB1UYr
+;6#43_ugG~&JdgiI$+zEr%gRMfO%1tl{wMO?WlHMm>d2)_msnZBpUWWV9};1FfMZP<BY8o&@rKIY4l1
+yQwkM<LU~&(gL>ABo$tt>%d_><PC+JbRJtck+z9)nahw!5yyb;3R1>xsH_&n-MmO=Q{5dIwqUjgBdLH
+M&y;d?{)7zlqigttQY6%hV)2)_fuABFHIA^g`6{yPX?1L4m@_<E=CgL;DwuZ6OH4~*@sguDx5!6Ptks
+@n;b@R%>5CkGSy)g(gCK1k?KD+#T8kI;Ij@O}`!Cxj1&@OMJ^1PBkb>R{YY7eM%B5Pl7W|2u@=4&jeM
+`0t&<gYuCGRoRShNO(ecF9`30XHg)$rahr|Likw_{vineHweE0!k=&oPuh`0(h2Hp4^mFTpxWL+jzGz
+;BL+f!?<RDxnb1j(5c=S1LRY>|=zIGJJ^GDPcnyTV1Hv02{A>uH2jN#h_;nEe0|>tr!taIf5a-I%5dM
+1zU*`zlA1I6m3K>A*QK0Y&P}l_&z6A<(zLfknn34;VD5-ytl1nQox%?g_e;lQb@LeGM00=)C!cT_qb0
+GXY2wwu>pMmhNLii6M{0<0z1j3(&@U>3il_CZFA$%7I-vh$;hVcC%{GAYfCWL<w!Y_yLuR{235dM@?_
+@=)k{&Tj9jUA&Mqt!9o*e^6BxM$Da;~cKqG4Te2P8T0LdbC!TG$6E3pOD~j<9f*ybOvySe+EEwx}>na
+=waMAjc|`oh>eRM4dmluwWG&qllr5FkdU4|gu5XjMjNk<)x|=1<ZEF6u&_Im8kvkwh?jq3y99Jf8aR+
+s2=3WK(`y`t2m0~xhSBj!T{{E@fID~y6&`vgLHN;H3>+Ju)TKj*K<>_X4DP7`CJheyN&bEvI*cF1JcK
+b1Jv10TR)?-ybocAfb-ZBB*bifX@JXY!2@~Su<KmM#BZ0txPMtdX`1k}FF$Q!`U_YSWxrv7)Oa^q1jY
+}}Z$0x)kbZ_DT-23!n^v5LV;^Pu@31ho;_7@(|y>I9MBjNO8Srl@rC5=Q6BK;Ws!O;nj#0hcn2}}qUg
+Pi`rf0Ax+46+apG&+wR+o3}T@ZfMyx}!}}%%C`kU;?;z8#|V<5ae)=Rk^F8+J(o(>k=T9$988zbR8>-
+$LJAS?GRU$d-&+2gro#RoZ!-NQ<EV!a!l+XH-*;(;ecsn&}kj+Ny7)JJ=-W0zIxygSxkr@4F##uT{~=
+Oc!Z~`vb_NuW5+;Qj*N_rZFEmc8l#;U-Oj`P4nSj*kh($F7=KbMdYKsA*2N<R-D5{Xag8U9?wCGMz+$
+|8+f7W0O&A>wWsMML`uwL?`wmagLOL3poRPkcIfMPMh}dXDQ)ga&fW8h2q9JbRh-S`C^dV^s+7Zp1Nq
+o~^LNjMdE0#2<@<&&LB2n?L(dU-5@U^JR<&)9P^tqesDY8B{dGchYk1RHgbLbwg<=sh2GUCW8ODy?tX
+&ia?*~MhoKF}$kozs6#$@DW^XUGHi^wUq1=bn3xtXZ>$y!hgaWZk-TOjr2egAbTKxMj;0rYn3}@fvv!
+^o@5xSJ<^{7dd?RFw+grp81BHIdg`bJ$sha*4C19=gyJeem&20gNqk0l55wlk(HMyc@NA-|8Qz&2lar
+CZWwfQP%q2pKu5O#I=aW8qkEP3(htdCx|K|#`^bazI9W+gllSQN(0|uCg@+C?5k~Z8=xodTL-;!&d<=
+w-hw%46_y-~UqY!=#gnu8x?}zY>{oG%A%3pfQ|5ZIjzURAh=gvJ42C|F5I|qae8Z@XMVw%pK13LHU*I
+nJNT{kFafjvV*Li&UZ@>Q$)wgq$N9=-bX0l&WL?t{Vxg^{41{Xz$I^%~?09@@2S+byU^Na!GMuh4G7L
+Zb=oH%O`U3576y+qY}mIiOR&LEcKGm#4eCPhXX)vs;_a{knQ9z0hKiy1T!J!lUOP{B`woNBSKDTsnZs
+yO)=zXBgD49wA*^gTNJnbOkfS9~R`-r&rjZL1B2a7R28#EG#%ItVbA=F9?3Pr>{p3W+Y2gxGqKb0RUI
+h`sNFTlOObt8~eNS`5`>tp3J;xZraY?#!o(i(8lR=7qR{)e*esbH}Z>H{{6dm?~aLzA6^;P+tbq%S{C
+-)C|(DTSI9$4!Hx~cDgLkexeB^Ix9K0#6}v|){+btFcwq+gJ0G7vfBuJGe);7`=zGrm{PWM>1N`Q~g$
+w6lTzdBO>C^A+-MiPWP$<GijvP5i$P^&x;1LQ;hd+~knAg}bx9{D%cQ1gLR_$&xY`{JD+%x(P_yOMK^
+Uptj493xP+qP}=#~**B(4W%JKKl$OsOUG}d_!UUP0<}>pwQ2LB}q~Z$olQEW5=G~zJ2?WF=NKy&IZgU
+JTlUW@FN|_3+Ahr_z;(mu;5XVuIM)_D=VU*q2UU_X7Kj;4}|nS0(CGFX$%`StT*xpd@=qH95_HXZ{AF
+SKe}ntCdT*o-+xbk_~8dyRaHfg9zDwZVHnU6(EcohdEveH-dnVB<Hkjxvc(k^7EUyq&AJsURwRO6jlA
+J;matBpIz^P0mfk&N$Pi4s0G&=3igf=h|G)qKI|W^4Wv^bnhC&*y`01ygXhlVZBb|>NIYPhq;tR$n<P
+YN=@#^Yo`sJ5jGBe5~f1vX)2xacVufP7<0OjrvpebFsa)ti(+i$;r`st^ezx(dHZy>JA=nv$40_xjP;
+9&)Z{j>aoK80~PKIC8iPzL$KJdr=-UzS70<*&Ic@&`OSKFD*&=WLJPKg0jdojaF9KK6t%QG_z=*s+80
+kLBaksZ%TuzWw%FW=0;dPCy-I@c8lL?8CZ&`GFAg4eJhuIe73O;~(=7>OaMNxPD#UWyXJC{sZi<qC3>
+#A%BMd`uchb-4HAPQBhI7u^i)v{2w}Wh<^O>#}x9Mg3iq9Fs47|$Nv5M*@wLE+qaL^m6In=G8|>VHUP
+^T))UMl<ZZ(*l$uUaI{GlBk^3nfw42hf?UW9zpmg#9O5gaN(u?)=$DytS-I8-B{<m)3x-vL8cqr)bE1
+<o^{9rO5znJEjt|$-Y1@ewK=Ka@Sf6YF`G0ic(kzXtaC=2q9>5qB*(m6_XpHUifjMC^Mln$$;G-4mfu
+#3`uAVcqeP}*|~r7KQTdiCnn)qjS6XzLvx@-KgKUdZ1+Q%|rSV0oGGHKpTDQX2ayq(904d2q);N{4_9
+*`HHdf0fd-qm+hkbu3NX7XOznUBa@wJR%~ZCzORElmYVL23e37EJw&=OLAbkW13?gVY$KbkL@mu*%(6
+;f&cMeav8>)p!81Qbp*&T6v~bHB<DZlIHkd#i2awh$v^ZNRBr#V{6l|4@oSzZayiFz#k|0Bg=IsQ1M4
+`JGc5O5FJy@I0)JD!rE~(w5D(>l?B`sDQB{-<Kg4AShq};z2c@Cg{yhKC7OxBm2^k9cumbBDwk<8mfn
+~k5@`dS-?$|G2IBaK}f78!G9Yq<wqSOE~#DNU&{!DM@_bYtQw#NVE%a<wiGfh5}LH=YpTFR4_<oM%{K
+j_iTYv?Nr<LR>xj;2rN#4slA{(;hI-%&cLn$iT6;R{ipq0Eew>+^nIpRrBqy_MJJ0iW<X4J~d<@9HxC
+A)l8=Mn?98_PhxB$8yqK4lMUruA9pt%YtPc%h5NLAJW(6kEAc#M$zYU?_@II@7@|p?-DX3enY7dc+!9
+2RG(1>QJ)85n*?oY2((E(K!)y{c-soUf|mI2*|Vpk{6im){rnC2f%O;r1353SKA>5a0r`h^k5>FGmA<
+)PBz<-MDEbn}um)tnUkbFJvp|NKz{k`xTn4#5qYQF=#x`m2UP$+yl=ej#ApJF)q5kh<WDd8)KeYF({6
+k$^f%*{EIav-_7L)<|gVxFx@{jH0M+I8?&co644Upj#kl{ru=Rfy6rOCA*gGA|!?|FU3HYxtg#`=sh$
+n_azkn1zHNueJzGMAe3kM#`u7tpgDAIczq&E;sRj>xj$Z{w2T^!<e+=sOQZ)7SGrhIymt#k!y9=U2E4
+Ng%^LKY|QS^*LVDXYFy`CPf|M{j=OA^#K`recWQZ#P|pOe)-U$LwkabQiS|tyND3W49b8yk1Pw?VP7l
+jW$1?eE$R&T+mb(=Zdg2=z6UbA4Kl0;89sR?hh8{eb6br~4ak`Vc@ibpXQ5;C+1fJy*j~uxf5eCpy)i
+H359=%Pjl3c6vMkv4V|$O|8f-VQufo0!e_NME(aj*kM~g?$cNa#}w?KyG_4z4ilU8PF>GB80(2|T;S~
+xeBE=ko<yX|iJ_2*Sg24@{(pwKZ|;~(04R{nvX705U8g%Ei{zOg<a-*TQ{8}F<iWB-dXV)@@$awq)<$
+gl}y_;3-(0CfTDGs^Iikl`62LpjQD{}}pcI>?Zwql;7Hm<;&CHfe;Djxlt1%jF;24D7>DkHByJ`t|h9
+H{YbF7s;P<p4_aST)TFSRz06Ww-*khpA>)$P!~P`8U8NnGqy=D+U}%lEO*jX=F#-Y>=^o3rk2`4hDSh
+#r69v1kO6<qb&UItx7_|9|4<fK`G;~{gt9#H$RqTvx87p%+>RWwEErZg`T^ZhG>le&3?G9GtUiMb*e1
+#Kc`dX_t1YAH-^?-e36Nn0$gm7#D28%WVA3&0u}#uC=@=i>w%q<B|8n_{j*jk)JRpB1B_$3Sn#*xBS*
+~5XO26IyEZx04mToJIq92R;{Jv9tUMFODPRQ^$l%o|7XlZE%mjQoF$AJFX2;Z6?S^s;G-1rN`Jcs^w<
+)lfI;<K}}4<N5tAF&QBUc8ve;9S0Ll_v<XPBkB6we<UZ*E<d!1M72{sL#!QH`RZTf6RNmUXS@eUw{2|
+rhB0-Q(9U|=gphvkl|)>+$>M<hi%e_i*KVogA6USGp+psF`dfG%eO<@N&EEaLz9w{=qs<hLa|@Y&CR7
+&tCcQazMR$TufF<<wL7Q_wImDjjeP^QRj3Q0K8oW3{9%1Q@~Vwqy#nKnm*+X^v#eu0BkFTG)P-B||H&
+txs6f}5D)T>Jz<@i_($cb1Q&Vf99cB6O(n~MVi4!N%DO0A<va&Mz^wUq%C!c(h@q^=g<QHYYz5x3PSq
+>b-pbXfaVmZXV5aZZo*U@9IA^-LCz`A*jI>tP%V_<#8HtE^C1Z4k~<FAn;N0ufeBotuzfwI05#{EM-`
+sky#pbk@Tqj`CG6zT*;^Z4=OnU1h~_ik1mkay%2^GGgNa(<wESoUz7gL)n21=a_w)2u#2J~-Fs*PGSn
+%8f6e9{Akt6rSP3hd=quGtaPj5R6;YWtyL#Pp40xPJ8w0Mf>*cOF_40ydWP~H=I9|LH?S{f*+PCnKrh
+qht|xdpA^SH|IBp^?4RFU6iYWgF_(UMa09Ksc=0&o_ifKVtXj2-78Mo!zH;SCx^(H%i+A6BH_gn<Wa$
+p$N>-Lo2CNq-C-R1JG?xYEAg~|Ca*us9))kzOz;Ptnp^k)khqAQr1Mx3^jcN2}=YKFT)ZMjav-z8cAA
+a~c@;PJ14EAB2mSyPIuOFR1e?H3x>@(!K9a#?K7x|axd~s}m5bFch5v&i*>pS+@&SMW8Ka)Q>|Auu7%
+RiK*|F6As500w3<M=|PVLH>1f9zyB?PMq8AIva7tR0%Mc_AnXWtdclG!&>TaV3E~AR!QF2;tENgaNc#
+-sbKuVq$mqh7^HndD#@H?buY1Nv&;DYNrv4prFmD!|CsH_MYWt&5M{$|MAS6oZQ`e@A;kI`JLbEd+y!
+9`t|ECuuf;roT=*S>hwG?2I#;4{`*x?QIY<fIbaN?G6oa(5{uzqBlplriwrR~w`}24alYu1C-1)d?kP
+u(9N9q(F&z4lO)9cPzM0RUi=1;!O^sf^)22;Rxw*M&(xgdx9{8SX635Y(Y?-xTTI2|QfLx)EnLCr;5S
+oD7hKgTk`eoePrc9ZVCOq84dOmUDgdPL@A#W(_Lyy7d^Ql?0W@*_vc<`X6A6~<A{)cYX0s4o}@lBB*d
+~MbNYk)PuebK=}`%Sserj(SFT$%S^dwYAgCx-bX50!rUb^AYtuT<hi=#jZq;#;t*Sg!|}Zzb}kWdIum
+kIgkEpNJlXr|AE-wl)=wMzsyJZChUluZ2(DqT3dTjjR*eUzX1rJu%#4sQ8Wc-|&CX|FRypbIf|9g%;+
+UH7jzeeTnk&a;;lst!w*+u0g(y9I_5rm&^%r!W^KJ-Tn~&b6@6&`M{pbczMGGdUMPWX^+$ImQ}Y5yJZ
+{N7#A|cT4-!+RHC!Aopu*!GnH%5Gw31;@<U8Sg?7f()YL?d(HcE^^zG6fr{DPh!tYIt!RW=l#sR<4L1
+MdgoPbPWYx~IQYx<cR<3oV!Aos`;JZBB<W;6fo^Su}SZuv(?B-+@-vCxuZpFe-T+O=z!mTx!TP31gf3
+0dbD8vyMF9}&Oyz~`V{)9;Rd#rAARF0nz3!{qzXALu&lk$W8Q_UO^0YVY2?TAs}srZR4mo5yeB7@0Bn
+f#}TVl9G}>5AAXK-FDIK!?=B$f#!s2S(d(@adAFlgI;6{-N{%C?c$#v8m7E|hJI*~^`+%s?Dr-Ue+@J
+ilk+tA9dpBH%!9$?m@{+{SOORnV}W+*V>o(dK>FSJ9}`OrG$;7?_&*$j%TbXn+OucRYP|~&Sg*)C^s^
+2`9uJNn?V5gf{!{csJpaQOu$}lX=s)BDdxY(T$HbiQo_R3&UFLyfbUwNkJti@Q#x-QjBTc)eAK4v9&i
+j*hw=Uv)led7+j1gHEIl3u6LbuEncJ*r`Ogn|wXMXj6T<L>-4<qtAUezGR<<zQGpL&wj&74)Mx;&noY
+q3`T8{sy|X-7G&XN1~O3HNvP+u*dPM}q!!POD<=D5p(w+GMA_(`i$kHpgk#IjxGdqiB0Sx8wB;qp-gx
+*{NeZ1Ne&M`=tI>>PHFjBF7*qiTS|(3Vo%B@0%_*f2QB>|DO2!(?#dA4V!Ie_}q7STyo02|I(w>%1cV
++U5sID<buHzzWHmV=D()Y#Sb(s1JS@(zdmNnm~TsLw_MhA5Bf}e&JRW3z9V|+-=de^6#qOZzU>n6fq9
+1E9;s>KZ(Lpcb_^#Y7Yfez9VeH2;kr_PcwecUcHRGb1N8q5(cvNKv&W66o4qC`fA0AFH>F+|oqJyN=8
+N1I%oMy4oC|z~oH)7IH*P4^@xhR=-c<DTh7B8hPd)WiAvEAS5kFub(d*bEbTz&#ehT05KV@F`ExZtn2
+fUM<Huw%Xc`&xY`X>gE7_>s-g(W7wC(fl`{AhS$Vjlb^cL6W^!M|d7A6VIuYkj3IdX=jQ@}T>7?|n$z
+^8in<UswM5rh0B;q0VD&cXEJ>t|;}$<rr@I@TC~;YxWPg<q>=coH0|{H+sutDmF#pSD*N~h3<VA%s)O
+bv|xW;+EcD`VB~|z$(#PEKfD~nJ;B?wJa)#gHZUIK5sV6~6P$4F@AReTO#j$%iT5kku3fvtjdvS=mKN
+VST0c$a63OS1M|AfO_6mOd{RDZ;bub#RJ}@Taal(n1elY#x|G2TzMEo6ED6I}Y5n36`$KJSlS*?G+Hx
+0;RVS+s7IGE|Zjyxvdbh>|ulYD#j>?w59LEp$ZEd{)doGSPNIpL$bmJNy-UjA6ADxoFok1?!`H38n~?
+jOHm_3G6X>(;GX;>KQ~fxHR4ppah&Yl=8{A2<{FP~;Je32bAdgD=$;$Lh~ElxiBZJa*i|z7zXT#DWts
+VCMf!V~)nVz>3Iek{dVjNM0O#1#CiNTu%RMAAL~wHNz#3S-<Z+|M+BEwruh3-@m_*wMe{=f6lm}1s>4
+89xBziA$S#e)t@`L?Is6bU`>FJfE70Qr^k6faqDQXTetkPw$`j!Qz80)iNObm3yAID3AzVf-01$3diA
+FT8Ydt>-Rk7m54zR_IMY@KCtK&xK+*Cj{mz&A)_;1he-odv4)OWjIEx#*gHMBDM(1C)HO2x~w@GNIcd
+$lR`HcRf|E*iM`kr~_nL@B3{Bh_oakbeu2p#YOeZ+SZ@Z0E`TWg{(d34EIpZXskyRNRTqOq~D96s#Wu
+|w-U^e(m!{f{ib4`h+P-I&vRmoBI)moCbhNRY>}sd21qp#HHb<T=Rc!einqS&Q*pdLkBP=7zcW%@YOc
++S?b@)qh;PC66m^$>YBIxpd#B{g?ZXHvJbC7XCdaCr9U9!KO<~OV#6#Kd$3DXfnFM=t|}X-dyi~Q@s{
+gs(!g~s@@A=<T1GVLDly3GWG7;Zz<J}|C61aeQ$1V?rO$SUteFjXwjlq($mw`(xppv9(eoq?K*d1Vjp
+-;3;)gDM);9vlPTO&&U;;CYe4^R;>3w_WZmmH5L*u}1GXf7mYO$jo*s99x#q;VF?QVSt(4od?|I+{{D
+uGYE&j)4utf64`T6-e{|XJumoL|RM;{oB#K?fjAscxRJMn2*S=nNd<!bSfc(9w8f3TP;6<!JNcb1fto
+CV_{M?YuI9K~FKuZdlb;|1{D$f4OIVp_3P9}7Q!B=>5P`230|_Q=LyCDr8kxd*--{3e%+43bL+*I<3P
+aRlZ7y})(Q0x!_p=uGH5b?Vd(&(JUmz5cgt+vW=d0)-~#W}cy8@#4j5!h{KG_Uzf3Hk0==_z>qIH%4E
+uFR`tyjkkf_HH5x(|MZFe*|>4z67EM%9-EKO0b5wGV1dS%Sr?p3fd?kXi@yw}g5NG<*nb;+$NNvrVYz
+b>*rTedDy<vXt7`I!%on_7-00-<=g%Ly&AxU2^dq`P>;Jy!Fh20%!-v&@0|#`j7~M$D$zY1Z>|6IwKg
+RwdON`g};H*7^OF{!O2F5}D>DaMjW}o12`;L!)C^=B{47!hbZf$Kv{;miwws>Osli?PgH5oU|%K92iz
+x7)_O>M~O8rAFBTk2rX?7E-bjbG=9)x`I&+{Sae-hSy6dzjkacOs9|nwpx91cSkkL`S{%?6c24dFarg
+UV0}S4qugfR6hUw^ZIi+=Z;V)ly6zq$w(xk=hN6Q=2_PCMfdgKX=l!yA<z1W+@nk6?*{9T*cf?@N(}P
+wpJV%=a>T#hcS+ysn7N*Fk!gH5<Ik8ig)V#Komk9-fBL$(HQ#!rmw)WR2GbX7&BR1*U(k0Y76Tvq;4^
+0UL#ap6O{^nmHP3f+{OX{3qB7<i7CW&M@gwmou>!G5y2MI*gf6gFVsdCl{^_@+K41Gd*ChtqcO!NWVs
+T<GVn5<O;?4|-JMm%gsh}IZMIna4U;M?>%k@3n<=szWIUVu{#BA`7dBO&nJ*Y4J<N@t(;qT$=;cMdS5
+^sd}Z_)Q4=ExH|n7_Gm=W3iA9R?q|+5_rnLza#w@!j#;i5rNW*7|4YJ|6y?Qq97bef#!le~>*Q;A+ez
+@{;I_cbsX}e*fCyxX;aCUx0sN7qs32lVksYv89y5_1Wrj*LlrPVyGXN>i%z-`A7E9^~fo6`OrfTX`Z5
+|najVv98_%^X6XH&2Wr2oUVZU7u7AN?fAZu>?W>vnIOuJBF6IM$>ZXPNvE^?0=X^8&W=|6L!cNjZ_J}
+xvd!v`d_OEkakDi8Z?gKBtiWn0%n|u=25@%z(+uPfFT}!D?4o@8?)sy5@&j8h&lkC(x`FxZo+2i$&({
+Fp?jccR$g$#Yf`_@Lwcd<7I@V2!i55JPZ5${^N!;_vK(@wl;ZH#lKSHEZNOU{{I{g$<4Po}TB+F!n?Y
+_+$xysV-+E3Kxg;=bzQ5`Vd`dSZELaaCn?<zp)*7FU+v=c_KCwDO)bZ@I6c^f7<6`Hju2w0kC{r)7WT
+%U*BhimIAw{cD^<yxYC8>*UI6e{oG!>5A3Pv3yeHUtS}<_#Z8(DqUGx=3nfuj(_R?`hi-xlwa7K<zMM
+9^Oo^9E6rD(U$L@sslO`CTT?o<n4e+HN_)&#R_#yA&YaxO#kV@2+<)(xlY7ycIXOOPInJDH_?cZWwP5
+P>>G`u~6csuDd~rN-$NXkDNo_0G9Bd694|W7QgWk~iP)WEpyg9r(+#Eh0J{Rr`cZYkzV<Rb%@sW&3UZ
+f~e5~+=Bj_i&!M~+84Ugt%P);Q~0b5FCUWo(PLWqeC|OGZm>OI}MsOHs?hmXa1L(i%M;Jsmw4?TB_py
+Q1CEYtf#l#~y2svr}xZJ>E{YGwfVD&n~cw?1grTz09t)8|=;YPJ6d)+0AyVecV26pR+scPP@zQwy)Ve
+wnx8M($f8>SYB(qm2PENxmKQ4U=>*ltrBaQRckd^o2{MJZZZ4KR;zW~I&Gb^I;>8s%j&kSSv{5~Fg7q
+QkP`3)#s|^^8G+nDUZ5aQ6j&H233LW}0x7|aU{P?l=F#ckIpI@RuqWsVjSY<prG(&EdMG258_Ek6g%*
+ZNLd!z6p@z`r(9TeQ-pd!|i}L?3{{>J>0|XQR000O8^OY7yf29?aEI9xGKFI(88vp<RaA|NaUv_0~WN
+&gWWNCABY-wUIb#!TLE^v9}eQkT(Hj?0X{R+lfFWGX7lAX!!tu}3sqbOtT*p^n3GqcyyFjSMR=7>#p2
+)17GvcLW61pomMl;q6p?%i|!Byu+i6bgkxp{h_QY;J4@`}OL+DXtc65bckGSGzA?ZNs161!s#qIKFQe
+brqb}^Y%7v^5DCAU1e!o)Kv^MPxCx@f4G0}@$?{W@7f@(vS9z{<JrmKH=oXqPEPS5l-nqltGa1}S)Ju
+Gu`zGzC44W-e1?ETAYW(skL$cETebOJTNao4iMZjaS7m*v>J;=N6@h58gnp>vMXkR5Rn(RGUe;GvMRl
+cq)<S(--6ykpxhhJHL8HHk#kws@{i~i`=dJ!Fx)<xqRa4J$p@E8f{k2V-xdFV*mn-_+HurlQ0esYpMU
+$r)qL|;!@>Lrg(({9+sT-?cwq7pp6I~o>(A8DIie<jRM2l6uZwrA`Js{{ne)$?ZpSG${B-#exB<=kw7
+vlg&g1(O>Eyh8U-X=x0TDRlil*%3+jS0QpXO_+ud4jDZqFy&M0J3h%dX-n>V9~ZKKxQ1=Ez4##qpt#<
+wA7d%-n+B2<9EPmW!{YG$Eo~<B={y3x%q4W1{T||t9fy?Zql{}KsRYw00(7x1wDdSZCX^=z>G?*(nbs
+d`*iZ2hG!fs({{E<7PV-rbP4_3&zrWG7qhg@Wr_b?tiFR`A8l+PCj!nB#gg$=-oB@2QIcTwB!N<!Fw0
+J0UKCBf1k?gV1V<aEQZTQZ080ihf||xMEfHTBJ3zh_=3^MZCKUuOkFVk&oY!^mdJ?>hUyXyWgIDoxFu
+hCx!OQfgi;&9U^l$R3{0>I23PheSgEY7-+F+3u%{`1$9o*)@<+>=d0G_T_LA{0<H4nrxEpa$jMGlhzA
+l)vC*&;yku}rJ<Do-!V9L2)M{?UgIZ-E3&crLca@PEjUl9K~`n#XcVM)VZOaro`Q$1|!Bo!|a4jkhmG
+FT#z3KcAhvP0V*&zwF_k^V=7ry<b8g1iyR{grjid`-79y<AZ%Ds-8moTW`XRjgy1-N5}9yjHX||o_z7
+27n9L1@C^u}DKak_fwBP@I4DF%;O(H82kE6C{<$s6GPulP(qHElvSs+66C~<4Z%+;q5Rj*U@8QR@Q)n
+s*BgBteModO;fDZIb;fYw&111;z#9v-sG{E?vjJ84`;<tw<2m5D0<L{`eI5W@1i|Ebq*BP+c{B^uF8p
+$3X6zu{=68->V!1$TmRS!HbD&$h2!y*?r{2NY(f!Glu2kp&XzD5*#WC2`h)C^2}{pBv6kA5LQuLc1@p
+%M0)ndJT9De{QPF_NB|y3RYG8P`2!Mx9FB^E+zN0INsu92n;0;NL$1_8*uhVU&@)6;v^p(HJG!!V8`X
+qc=z)&mcQ}8@>4l=$g?~Z{is5q%`>?@+@fUK;W!~6{B8XEodN(F@%D|NqSBZ`t|Cy!>W=LH`QyG(?Sz
+~2P^#!u*&i|$+bLbI!PCa(cT7iA76uPOgj9c<!Db+CIisR_zL>5%8FSVjr5x)Z`Vx~WYe%-g%@M|a#M
+vD47JI{x@@8D$GXbt$r66pq>M?P117xg&V%Jsnk9)m!PYWtO+7~R`SI*!c!9GAzrHd`Km%3XQbnAH;l
+-Xc{IF=6=;9|YMQ#_`46yDDCDc5JNjS@`*AfW}NC&%XImY#WWpqm7hV}QJ1d&GzStqofH^M`kem|f+g
+fQK`pmsr~gkQh@ViNB4#GXU|B`k0r{;~nJIj!(CxF$!&^P<Y|ZG?43zD`YhY)4D(646m`FZ#U;pNk-R
+E=FPSJcw8k2;sl@uXRyHQ+)ee;5&d!@bF)Nhz(tgdL##0<##SG@N)h*JLqz1S&d1%iX>2TuE^=2RUg8
+9!sr{KVS?Ys67Yoo8PJOqWId-RjX<>lb-;-rEpP(=kK}x$$;x<auuwqq!vm@yk}Makh`QfRUBgn_tjk
+<Taq#Tfv+rO~11wooDH|6^VO=Reom)UTTeQG(o`Ckdh%P}d90&N-kT9Dt4h`ru#ZLeLO&rD*z6T}ElU
+1H=?`(@4gcxXg8P(Ud5hTzR|B0J?Ri?8%szr=tVyFU)u@iRVfRCXxy?wlNDnKi3xwHTaqZPM>>_txuV
+xei7SNwMGB4*qp0;tGg&}|a@{3|_00y161dOk1i##9bDmT2G6X$7LAxPyucVPbf9z-;6YIA~1?Q3<CN
+ns5<A8ri&z#mL%o8c@|0e)d#W4G>{<0}9rtFHaP{39<^bW$}mG83lRN0|T>{$s}lw?jak&EU0gCT5Mr
+9H1}8tH0N8Gp`c~u-dSXhNxZ68k%ixwfcIPPOM<R@+!eQH9TkWBr0P@0CWP8Blg5p~Uyt={YW3=Zv<2
+DD3xtO1hQ!pA7A6QOGm0v?$*Y@4jx11kk&3i!n+S|?zzi&8fJ6KhT66rSXxeoO6XYg90HQA%&zd!lkx
+kvr;P~J;_}%VTL-nBHSk8?vi0TG9NOFbF*eLn1P|WZ0*}6qD3j4{1Ns=7@$M**xzfa!3{q%AF-N8wc1
+X&6S0J31ZVwJqS2U!LdXN9I`mFHQW!GNQUHe00dP@rar{3+V?Wl*N;YKHp%GACUzC67kjwH~D9ZF(<)
+Rn1zXRU1~dqtoEe@RY}aI#;GG%t%nRV42L)=C-Ip5~)$>!9V1>kA7fU1N#B-i>9uKAnp(t@2B3DF1Qk
+pY`?zUZ7yIBj`2iI1za^|Fusvpdn5;vd_{Q%!Va`zlqF?Z-{x5YBZ2E=jt+x_UrunAquiaR&=&oYyt~
+51g38cOu(V-RkWoASL{YB2HPOmgYwWsmm{8~7WllC{%MnuF_=Q=^)e4bjpuEM{r6#fk0P1!)W>$y$(&
+`eX>a|<Zq)NZainOoU9w=Dn6)RcM%qHbncG;R>GdU!r&dl26@;<rF?<1HS@Q>(3Ng)bQgm6+t_yQ)Z;
+U+Vr3BCkW@E@sgspcd<8A~e^LSkUqJ(HsHd1m*LAxz=r1?kcF#V0DTH|PumQGpIUfeyVqU3OhTp1jE$
+0iqK)V_u`C{dy97X{kNHo+J@2VWyK4FkmnpZ_!V{gk8Jn!@^c!$isCz-~M0WNHx5{AkSdcYf%EF8Tv*
+3?eys5IG6(`pi}EIon52YDD$gyc8}Bn1B^A3v`vwv2v&?uNQ!C%loK+-qWn&~Rt*T~3y!gM3omPIuq3
+L-ZC)XDM0ha`SL6ndB{A`d{%WZ*WDkyMbuV&qg)rC{%!Q?h9Cd?+e;nLkIa$SDka{XDi;1)zBCP3I`A
+M;u$Yla?Oz2T0`emj1D0LA*Q@>}(DWHJd^ctCtio4pSAqA!1T05Bc9Bu&>A-|4x$H6XRG+Pn>bZ*v|J
++sI3BhtpVTE}NAUdA)h70g9^wu<$0YkchmTNazItTGSGO{gH`UWGX__FPnBt^p_@Y(m|n<vN$Zc#NUm
+s4rWsAvUVoqxaNNe$ohU((5=YNUIZhIq$WFgOAAA<L^^XZX4*GmOAp^aq!<JeyIHc@$)23vnC~@-=CB
+q$d0}}0;MSjiAANc8tozQa0!#!(kx3P14Y-i?ITB~3T|Sq=T3cG^JsY3xY726S3Yn_)*$UM^x6ZG-}W
+T8nYG7OtG)G`U(U<)%Cm4QHl98g7X(<C4iKa2$c9VPtVOxTWtjVg8oQW=RuAaL#_zj5!dQOg{e^}&5x
+3Z~qEQ5G(PO8!-QwkShTkL4N^xK0Wd_BW8^F9MOXDr?f5pub(8!}U8$F80Y=Ky05{=ny#s)0#Dtz}L0
+54S?8!10Vx;TJ)0$F(bGQq9E8uUTn%eu<M<mFciL6~q8SqhPq%AP9k!B>_Qy|NPalX@`-1n|LL%}S7E
+yqB1vDz^f;b|2N3e}#D^F~{B|AWe!kNg@<upjm(cV@xP~7~?bOA^y|NXL1w3>Vi`dZ4uspL#|DdWu2`
+{{VS_y@YRw91X)^j)GU4-YfBGXQqM?rA8i=;q6k;$LU+c&tQ4k(FJ~OQ^3Q!Kge|eni_N6#FA}O72Bu
+n^Bob?ApIX+%V?}`8;<E__;VrtnlVsYjfD6PUtxJTd6t5Y;C9oE>?1;cW2&?nyop6Ovy$MlsOxe#)Mk
+#H#-PR2$k7$3QOW-DFXWzYmi5@e>(N1PZkl<}*tbpzXG<U!{VhyydVNB5MO|Md5hc#`u(hkYGK_i+6k
+KF7v2yf~4tD86oK9Wfd3)U=M3q}CZBu$VEW9Z5%5VNKL4Jnl_A8AC3^$Jje+EDgW1)^TG_vqn4+!iVN
+jc6s5#NtgHgjv?JwB)IauPR(-pdfH`jvKVN%>y>g8mC);NI{*Y!pERc5S*likPFxMI5@OrjTEZtY8x@
+yF2o|>;sSZn7?GSe3fT!6)D5d$F!=-YNWy3dn7@ly4XIKCKz0H7$Q!T|?zt?Yz^l@zy{Ko87FNGev*^
+Lco*JEuo)K4sxDW!pupQJnE=66g+x!`<VVWy@;J07?Cip-bCJj`y8(_O0ehTRb#>di()Xp?E^ilM$8(
+>A=bcHI0Hx_@!>~f5B!I4W#(IccT>+@;Q623(hJE@Ej4M!{94|!B1XU%=mtgFd)X(@77Hp`c2h4DB0H
+CSZpCeKK=m|Jf2lAD29SvR<>cI}(bv83B5zXG6ylCxOg1W8cy0CUjaE>KD&Gf+xe12f$^15Z*7M3TYT
+V?dR-!^JTRdWfJ#8L^L6H&o^5X<^_h&IV>9h%%!Yn0jY92uo-Vr$4_Z!vJfeG?j9S07$8cpA)&|(Kcw
+sz|`jwF!AreY+!#8fLWr@fj+RS5f;v3juH*wYEU7!3acqdmjO_!Opw*NsKVBR$s7j@>tEI|i>R($wGi
+ADTqZ#K!Pf5UHF`cZEb6anKV|_K9K#xrVnhzIHOoK~OT>s~4>f5Z_OWi|deknkZR!2Tq9CyEK?u(<*e
+Ks)sT(MkQA>l8T|DHdIq|~AvS0P&8Fb}YY!0Z&L)e&_s!Yy^@ph~V7Eo>|SoDncJi_OV+f9&AJvHC+Q
+B8y7q_teO-P_Z2Q0%cOp~!-VobDbG1uIRf;KPq(iTYyjTECJo^{Z~8%<9#>bP>>G0rQh~Qb5_{FJ#qC
+uwi@VV4NhZWJDvdc~@xBfo1@bY8z!1G9N6uGgwv8xbcsXA;uEa*%c+=JBs;8B4Ln><HTY#lA{n&z&4+
+gT@1@f5B(mkO}T1Qcqy)YJNM|GMGEu{J6^B}-$Il{b*<6$?=^7)XbR#M1ZWORtb`_^tk-uml|~zqJ5(
+D)V3T_aL8JItIG`%i<X7u5ZD=5ekilRNapl<^bXRtS{~`!wjN5I!E>ZmpV9{-)uy(e;qydRsNazoapR
+pd8%ZDy!Gc=pUnx*-t!PI#*t1&dxH7%rv$fhfCrWc>Topq(p#^PWH+t1)(@<njZhFv27R(pyt?ljU=3
+2!f*;qiM)ObF7~<N^u}8mlihXv=6GU<c5iB8wibn;Jho<GF`sJS%IFyPC>Vn2G&&m(W6ENl6^)G5N+_
+_2~hYAQj~^Mw1j`A}lvY!-Lk|yx-Tk^apkR{s;Q$=t|s4ifZ<Du^Y<&3p*PVZh53p{M_DyhKfE7)$Pj
+y9kk0-T#s2ZaU2gK=7=_sqN`dIcOhz0k@@(q=}pQuG(1h5(vDolLjx^`Ky=&4hysATrHE^}hl1e1rZ2
+M*n4k^_;Qr2h4R9qUJu|jv380Db09?wunhg(*`j{f@8SRmyq(SOor1Nv}3~2T_;TKeMLgXVgWzHH3KK
++HrFbe*W#^N_&Ln5_CoW}IjA9z6yLwFtOQdCS>fdz@gv`m+m8LZs^b1y(^UGmOu{nb}rjf36#pW#D9(
+EMf05wEQy!%tjg(T+KhCbo+Hv&=tN5s-?4(+u~it7aHYW|0h#H?UzlyI;(m{h9sRUg}V@XEiO9rhU(Y
+{;+J2!?e7{ro8nBtwoKjX_xv8#>;z<fhr=`lz<r1zAS#q*+d_+QmcOO_JFHJWO7V<XFa5BqtTLJ2lRt
+)0G(|{$I)=5T<-D5qnZJ0;~)m86dTAUFt1D?#|U-OKZCO(y^_84@0AYD{efsbGvMce#&rCi3XZcQPrL
+I=I?CGU>R3Y>lSMV6I-63RrTB=)77>U3Dgui_vfQc9UZ-r()$#bmwM-2?2|OkMnIQwCDfuhQFV|O5c!
+Efg!KPiexQ7#f!LHfN8^`zfR3)-m8$*M@9-OzbjEYt4O=EuzJ!sa9m!7{qB7O$#+jav)rPk1-B3te=*
+q<tq&GpoQ;z;Q+E<S(b;M8K69um-J4~*rJdt_v6@12E;o;w8>pr}6Y#L*E7wx5z+!AJ)#FgTHhXPX5b
+il;L?#V7>`{QgsN^v6EY`T<%TWN56WmTv6grAo7GdrAP97KM5nhB3Fo5EMN@3@(e-_4u03n0+cCU9_N
+A$Qa0Rz)q&Q^4<b3j%6#lt}N^K@<>T<uvC8#`+PC(&th9BJ09*w`rp)LiThbD1s<I=T5&FcU!p#q{&m
+ygviX4M=w_m$H`B=#Q05opLP_}mUA|nk7_xdhR$)b}yji3x3hQ^OC&}ub^^XLP;vzJsM3ZC433|6>Fe
+{hB=-51M)Sej_7DXd@%!WXPac>;A-=f}Nja38eJM7VvT)kMqW8ak=d;`?MZyp6xQke=qs9s1=lK=x<H
+ATV;Zyk}j(e$VeBZTSh#h~@jEJ?TbV5nbaO2d%jIZum{Ij`ELL4A<3W%#0~OW+f^{m#)KSq&VRC58+`
+PhD4nr1L#!&h*HjG(3=Tyz<r5sKvywmoBc5Bzz(|NlZNSE}DT(-~FVgI@I(uu%fNfi3vmP^2On(4@HF
+&0JJezrt<*1hW(FRwvq=hTZhehaQFd$^2eI<*4_a{KpjwQ*<(9<JVGjdNF5g8NFT?IBG1TG_MQj4&}T
+@TASQY#L!c%Sw*8XY!MG0mm!;tEop2G2IK>{ga<L--v7Ld=*lgYk-e~ewBm3Wn6n<?`FOYG4d!*F7=C
+~+Dim}=G2TxOZKL&PVgBTX3`Hx+c32xQ*7X%}?nM#b!!S8L(cvX?U@}~<%#<))y1^xvp{XmB;hER3n?
+?-AWPEobw??-A$7E+ZY5xcJRmf0j(q+$W<&7_N|;U_SG4nCfp{73KTYZHcam=^o~nhvpV#}}g)epwp?
+=VEK34okzDh{_?>dNGQod*|^5e%pGWZlsSZQ)ga7`DlWb-b8rZzWWJK=-~3`%>Xb!8`rn^`-78jj!q9
+o)@c+o0F|9_Gdh$bxdy%FiW;$ir4|p>TIG~HMLt}5B&cO+<8JIUBuQ@qK|?>h=s$nb&S!X7arCAQvTv
+&RNQ3*O&jU}7?D_DNBf}I;)2-D~;l-1}a`dee#U&rqqore3uPazO_Cm`?qRLEyU7HTJn3h1skDVv&Ab
+v3kUJh-K{ava1FohrNj#^Glny6(0NJ?Mr^Vj^=yOH!aaoztYZB&=14;{GHh34w)x8Xpi{cK6haX7l@P
+f$}&ct{WMsv7!)Kk2HMbYzNZnQlg9U&&}dbpOjpQ9fdeVQ@q_Qni`N?2i<2aLDm4o#zL`8wi{ZaTw!K
+%tD7{#F!|7!!DSmK#901&`O-^#23PV$g8$-Yzp0%VJr~|<x$T9gIP2v89Z`S1%E24tiA=y6N<e*+JF2
+0?eX!qZ_nN$LIAI*x;PPejBQDYM&LokaJhLQ)=Q2Iq?q{*JKj!FX-Oyyl8o<;A~lmpo>x(b9Uq+@{+V
+mn8ciRTbX1)&CA2Wqun9%5sHKY6wX`imD)CI--3mZs(J1`Sa0IU&!I40jc+!J2{Qyz<>sqwVaGL{B`C
++F*YD;b2EQ#RhC77ucr9@kG99Rm1TJJaU^B;a-4*B7SUU=hB%)XXV9bT%6xvy5WysH+P#18G_j-JJ`v
+&50OH)$iH@E<5j6bxP5K@5GX(`N9NxF?`(JL+}~hMOGn$6C=txWTY)i2;I;oVOuu@#krVOct_(0JI4e
+B@1P5(+VeytgMVh)WY&msF)i$3ShXKah|rDn9(z|)wQ4<M$@gO44<B3ODZ5-#TJj!LwTDv6|im;eyjO
+LMy*}0b+niA;_b2bQljJ`c`qOUw|vs;9DM|lS*_O@eH591vS$RevMx#35Z<3Ef&c=7<(!Ek`B>;Qj>m
+zao9;CJ0di>$88~d=C8v~`T8OVJ20NCrqDM(zT&n7ZHq>NlnRb!1wOn2FUV^=<>~mCgfWgu-?3a}<pp
++p;SpeV)u;zRPDK9aG97@Zp8W>=)#3C3JDbv_&#+IN@t9y_fpql*fgAz$ugF)-SMDk3%mM`J<_I9Ym3
+&42`b?X^Nqt9sT_lF-ik{W?|>zmc#K}=YOTpZnBRi?Jj6-Cg=;O>hOhLLyUwsVQt3U8(}1VCTM0d<AT
+((z+6e*mWr(C^g_Q}6<cz6cb~>ZmU7G*MW2WE{~Y*Jg~7=d`(o0n<IhTyRnWvlX#n<{^T)fr;P>VD+f
+VTi{9w1jU?G)>p9D-g4ZqR+>>q{5=V1u4OoTq&CFQ(e*)Jlv4G3Ak9ll5COH+-nVZz(nQ4el=M|QRd#
+IR%(XfVS+_3<(yyeDHxxFR?l4(n#DY3#Wv&qXVz++vih=7#o~djeT`oi-k5&sz3W+`m_~JI8rC?zW0?
+y6?gaDtag%u5hZjI^&qZlra;ltckleWJDKiw{cbifw9gO*MQk57*N4bz%^eEZ=bdHeC(1QTZ+9(}YO1
+z|Kzw}0B*{+Hz9MTD8|@Mm(NA8vukzWr1yp#v7b*uHo%Vq5-jv~m0&XYY<a`Y~w2?bUsBo}I^U&a*d^
+M2%07A<rc0Y)hoR(8+pqf{=^Ur@9~T@p^S4mXCizHO?_Ae+QJ0GSz2if~cwkeHvT(w4@Qx0|Z}DanY1
+lE%zM>T-HhdWm7}BDikKZG++vhwCf{d66+33^w;U$l6U?iX+srri)f_n;U2$kGI$;Eo_|+p1k%eey_Y
+;_oT?1I<YJbt@?MQ5JiiRbf!&vWfACXLe?Wvfc^fnlyY5sM9mr`#h+W8WQ2p!la$5%`dJoJ5*_)>z+d
+0|w2#odjFGnUa&NKvt7ZXqbJEt=~p+!TWv{G1AWgdcz6uh#y&Vy%Xx9|g2*8RH4pKTD{5@(O6Py6|%=
+eI$O^NvznJ~&g2?1+H(>*-(4#Rw~oFw>v0MA30OV3$O4f3$KY+uH!+5sLOcSmrRlGFC@Goxu|yn%SI5
+$0AT_GvDDe$`+ai$855SZS-HT|6G>Z!_#leV6_J=>O<}+C2!y?qtVNFcf`Hk<0051MX84ao$71XD@+b
+*_5?Y_p|oU>)u7{W$k$Sq+lU!Z0-N7aO>@8Zg7fgXI1edki$VK+c3oyc_SY$wiAs0Exdy@hy)eC`m?|
+y8bZEJt-NN(+>>SDn<E(4lAtPN{KJ9*kl$**OIt@n_>V6xAP^H)kIU|LHVd_Qlk=mOLxB5F!zf8K8^g
+`PVK*WhpHtcrjo39j))^tr!+<#+6ML|Vp4PIAhB@!u`=}Ra4399S&Qf<=64h9Bk^J5L_@2pHQwvdKtP
+e$%3kiF0a-ImVqF%(dtDB1_*^{zI5N9-G)_%<nT3q;EIit8*oxj<&5lrja+?{bP;5G9zim$s86L!*jV
+CRi@QM1Bq%%RCsCmAk2OKL<<7IIc{648vGsD)0&<RFkv8g$XytMpZ*J%e>@;5Fl_iUw-*d|ME|9@Lh4
+oLqcX`ZS4PVN;4A7wY6wLtf?k~1?P#5O>RfaM4}n#1cmrnP;MJas05`raM4<vWC1)kk0%~rz^iuq3>3
+<6gdX!S#Us9K;_hH78fS9YaZsPC{0<Kp<ry^x#9-4Cljf*77e2&KoQmhc>^unAiNlbYa=4_-wF4+o%(
+<#(*U%%bv+D>aEZ^dbE(Z^{AzdR*+8A68jQ&W{<ee=44p^sK(G)jwq8}vFb0HltFofeEf7tb`)Ft9&<
+M=rzMvX#DR4(>*c5ZKPfh5^>26Gx$`kmGNs@RdW#g4VO!&<Tf|H+pzjkn6`g1NEQV0Y7ELG3wNXa=U(
+kikTKpo5Ef*9WZ{{~bHSZEQamkMyT&+roLMTPogQA8mX)(x6jlxJa=C4FTQXazbMbC(9>033KL-lDsP
+2d3Sk&otX}`8?hkS37SHaQVu3iPjDlETEpaw6wJyhlF>#@NgK6LDmWl*rcQv7x^;Qad&x3H1=G4=i-^
+kYBF{_tywi7B>FCE+aMDgaAZD=HA(SgXJPXvO0%gz|2UDenh9Fq;vUe^J6ApWlw3Hz`mvT?VJXc}1`!
+_jx8NkyWz4&nskFvwpAq;*@FRGnw6d>r67xYFSzW7Hv$v{GS*DI4fo?liLm9@WWiW?wS=w>*j$O4%J*
+7cm3nF8qnRW;%^*h_=nOXD<!n;g{+X(T@;l2Wm%%FF8WWXfqFeV9IgMs@|qi7La^HLkM~%;g8eEwR93
+?ExuO__)1gNxPAi-26~Ma+<hqX0^(VYttpcV0HFKz31}!@z79>Hbv~yr*mI8Bz}xQN2fjfSO;C1lLsN
+$k>xJ5;dE;rlE&=owSdSGHcEBTUzkqhLvaW68xYBKvR#l!4p!?+7?;J4SYKj>lS?IO9lx?G+}zl(MAv
+U*HZ@%jbyuDQ`3>!Vc16e1jqN$Nt-rs`GPH4Z9Zn8hF|fYCq{oJ>3UaM0?3C?*as{Iixfz?GS{<Rii=
+iCooX5Rem<<p41E?{SFp{=lKlGaBe(wc;T5#4HoagiixfNz9eQkR<rPWZwB)jRTZdZYVO4ZJ{Ljvo3J
+5*7@&E`OwBA7d+4t7Bq=gby7nNk@_afy0vH0?lLJf!i8A1OLe18~hNNvmGZ#gA%cbPYY_)P$~A=JVF7
+t9e!mfdRH1;M5b&6#YNNtV&w^R}FFR6n*%yHn2d&oq}2j%*bk44y=-`UXr7ZX+Se+KS*zqfoYf5Uo*Q
+YjqD*99s%w?S+cylWW#uHrKR+RJ>rr@DY+*aSt<fc2AjdQ(&h@3&8@BLTl_ZA>tn78Y{m$8x$QBvJ<j
+-A#>xu$oK@Pwh>JIIe26JENhH2~F$Q>YQ4Bzkx9Y&dHLCigZsSIjN_s`$LeZO$eVm+Tm>}HHb$(B~VZ
+W`Ffp+X6mDZf2#~2e%ohEX5*5y|Gcoh|$ZB18>2AJS8)rP*s-Pz{Z8vUhmP-#Tjso_cWuJG#}mQ?;Im
+b*@#IolH=d4^1S><UFvkL6g(ADY%^^ZQ3k(uK*wmF=JcM$6tMz-#VUzmZ#!c)!}FRFF>P?&M=>uX;KT
+tjn2`D^|_<+06T*0y9M@=P*6Tdnxc4nM+k25X>C{V5v-0RmJGzd2bgP0z;0$syhzetzB+?kTTg-E_xZ
+f$Ue(*&C(3lS)h#ur3kOAgX*|=AoTu`q!j~}Cz6<gBo-Mbl!~4OnvY=dc{|zt9%NxdCJKHQWG?5-uuZ
+j!<9U}N7rkaxmf7x@mF$#EH#>Yj`DWl~a$cJ3HNC!gN}Jr>zr9U@gKc`$b;JkNo|&1AQ=NxK=gSupdh
+44dMo!QO%ZzynjQC7@W4hGM4jHv%>s>8EWI#pxY*c$vS<P*8D6ob-8fv1O#KW>ikS>epKj!zIz^=Xu^
+_=$(Qi7Zdfwn3tf%okKL>9#eQ@0<+TThn8-S%LzYQ4(@^>@LPRX#by)VzH-7EH;N%5Mi>`(Zs1KJKIs
+4+Bnb>oL>aIXy`Ivx<?3a&<~5p|nZL!835GQX%V>@l)edbzRlBmC|{+(t|2QTW7WXMC6|Agjh@){5Di
+RGmHfs@r*Q>a7{j3=2}o4m3)VuH_6E2Lg+b7OLXzyQ|bPqyh)x_-1|}b7TyFRYPu8$<ACMCO!&Vn^8S
+czvJEXyT~dTHv%)Bd<CsD+aZIEp@2O5yA#BHde<~)ERnAXL6wbRw?paf=j1f|O)liY>iwQ&bOHF%kkl
+6X5a>_RCl^d{1bD1L25BM>l`0>avS`nF6(Wqa8c!uJ1SStHYPVqSD;4doh``+n1(KdB;g%__?D(k+ZL
+v7*?#@@~jbn~{jE>?L~q;cI`?clGSv$XkZT0MXD&lE%kpvrp%3~w8$mn%?3LEBGssAe~dt1Q1u=qL?s
+oK1I)Io4uwU~Afy^)0#v9es^}*pJLvtRG%A>1tuG0Nu-u5EAvsQQe3RIOF9}h!v$Kz?PZc$t|7b`br+
+Ulod=mjI5<yQYdOqZhSJuI_P(?C}EPp?Dx}3yDx(mli;Nzc7U{sAq^e0>*qdjL4li@ddaY-o<FymE{n
+%rk=l&#XBt8lm}y_9>Q*swcBsN$O{yy^og<2remMcQhpw<ld86Sz$Lz!)_ihTXlXL%xMw@kXt9~id8_
+s1>MfSt7{;6Sk&WDp%+a?E&>`>p>y`oEZS+Z%SQQm6`a8|n%M*8hOFg4W!J=Fs})r07CfB(1loo9yGa
+fdq(=fMwHl*6e5yc<qhO0bAJUAoFEibzFA5@6-7uP#0lb6)ld>5*%Wlu;kw)llZA8;FUVkEj6niRNjw
+^PfJ*Y8HP$=dfFQo=hLyO4lO_3yVDM_M1Ugs2i2D-ME;>{Mc)t0nPH$D07x%M=w@~-aa~wJwm*f&(=I
+s++Po~x|hYITTjBQ;C18=!Ox-VWtz<#b@;a!Ds$dO$YdzG3#3oYFu=p^+ppp8zmiICm{cP#eFxnI9>4
+_Bw&R^rzlJ^4BRsD{^NfhP3%SAo--412S%FT3-A~1eQfR}UtDoS{CH%Qvlo$l{_GJIv;r9p0!Jp3#K4
+MHFU*#P`>G<j=y6gnMUjFn-eW!l`;*f6&OR1Wd{4cbnVm{Xr(L9p>CSBAuUV?}_vSWE+mb@&^%3`c}i
+Jo>BEj$<!&nJQ@OD4v|JGzIFmTWLO$UH#e?s;>z5skY)d*;dv2~>q?8+J7Xflytbp3O%`9y7BDUrdk+
+YJ_=lq_VqK+Sub(Jg92~s35ivg)ltZej$g*IT*r&(~pBjgUK1w?)XY1c}8(V{{k_DISE26)tTevV?a$
+lg@1?P;#=#LObZz{d*+*`wWz1V<%3OL4mODZp4ukQy5UyfrN0%kg<eG6koB%0Q{Wl#lZzSihNY)=Trr
+VDXt&le(*G`2n2p{u`vKpcjXsdRn{OhSD6n+Sl>C7GHB{YOrlR5p)Kf#6*m3%O%5{AK%G0Sp3a{*UsG
+qm=={*7ql^*>U^r#!FFXUK}Hs`y?>}&HHGyGuA+uROWefiHXzxuK}myU)R38)!gzb)_v7?i1F2aqJ5x
+~Y;v_tT;j==+cjyo&GG3$x6ZxWmM0RLwvt2Bn+AoJ?;|_YV&-sTo4WZOUzQ!jyT+H?ZNx=n_L$i`IdH
+Swv~EUo*n;ej19ZXYUDv1|Ymj^po^LpOADE_1q04WAD8RvA<MF5Hgw0(2}J-W@%<f{c~_BQ<zD@gE4J
+LeJa(<%H4@4?^Mt^x_OBQmg2;+DnhG^wSiYw=oY1|q)6#Bz_h92@JQyL>792f>R6VLb0RZVE!`5mp=~
+53Xw*^KXSdHNC-qN8Qfi8|r0cIS#7-uSmVsHfX?fl2f>}|`!GWaA^L1Iep<CRRJss7j$+0+=m}1ZN`f
+1MOW!26>;c^JzPb$?mV#3OmH|W;b7X*d<3LcB#GHp;f{PgkgjP*fW?f!^=KH#4}gyZlG{yz@8c-LcD*
+WN!|%S8nw&8Lrl{CM=IkKtfsDA=nvX*t=&6yA6E?d9%nf9HbVDeVIlDEZ{|L_+BNT&Rr0Gi4Zn*&PfN
+O3}^5fb2J<wdK~?Sp6<9)0LoiyY`C=F;hn@b$8d8vOsOl04_I{OWn-0h&X>ek!AcP(mRs`ToUW3w>Co
+Da|a#_49biGWCi}Q<NvMBd(+4`<N;_8ESe6zu<30Jg0VqT%^He|)>lBxnm(QIDGs?qd4tB(=@(qxNEu
+Iu7JWO}W>pw~p5=uvOgQz*laJCI9CdQmLkX*`)JU0ZvQKwmOy81*O2u-$9LU$kZOEzTU_~s01Jtp~Kp
+XH2uRlt!dI{!q!g<dfeo|bAFnNRLj*jc%F;q8r^Tw|0veONef(GK<U@Pbbdt1+*QLqze>YC378hrj`7
+Ce8B%I?iW9u1@l>rP3AHy~jw2{WnsX%i_@k8x)1a?)qt^zG*8M58)Hn>TOv=)aCUn?&2LDf3M2rd@e^
+dyoEm&@#|jFHkawwwaT32%o>(d;VeX`6(;_^yZ3gBhp<Qd3H%2@&z57y?r`c>12xGxA4w3cXSF`DPC-
+~j+0&ntL*Y$@eif_N4>`scYPXk?E#uj2CGN;SRu7-Zw+)aK+8d33c7;AH~t=tueDYhKju&Fv<Y-x)@l
+3reBt6d#%kJ?^umo=DDiRxlhivv%B3aGH%SMZk^9k^YVBxvr)`zzy*u%I^d8`K5G7BU5<{}a+dWFIQW
+fM?B~7Y-Mv~!vjk0XFKaPE8vaGN}($B<MlM>{Z_-2QRsZ?J?Li!2+P}Z0IlaMEe`|pxJyg&NJJ@<Y7b
+2R<SuNPaRb96-}`|l3+|CpQ{?0-5rMW>;!%JeTg=jY>}FV45lw|>Dt{NUG%4#0=Q(+_XYpvlvNGq?I*
+rhobQV(a|ZU(SD~mj6K~YbdS&cXH(=_^mT4^ifZId}(?>RdVDNU49*GZIJ~2xn8v|$MEMB{`vCP*vNC
+`?V?Jb;I@q)zLg6g6f*f8z7L)^d(bW56M>!rdh~2->sheGgkI`MRa%1Ru!1aoS90;K8{DjerAf!%hU2
+mjbpD)T<a1Q6n`<z%!=LGDobcE1^U+hs<J{qrqu6`SXg0-ck?4njt!7E%9us<})4Sjn6WD%sO-wWKvZ
+pNi%7HNWa_qFL>vdwNH8L>{6s9ySEnh_|@aAWoUeFWzC-d%0GKA8XujL-X(P^K2F>HLNH{Df%KW$k>N
+8`7;bUO;wJ{}t1HX9g`exrptYuIntq*;?@#VjqQN1Z?_k^pX2b|8s!ml-z<`;B*Naok&obzA<b9V(%d
+tzux|#KV13T+nTt4?vXP;*v8a==*G+R{H$hDXy4<ToE2mMDMJeCQoYJL8K4h`LBoQ&YsC(x4a3AfW+0
+uefVK!m{%~NX-Ro^c6NNauR~nq{ed@mgP8?IFoOxm={$3>L&63gX7v&scDVMi_rL)XVOR`a`|dnfH@m
+0FEa#l#q(ZCckwVA2tBhx~+fGHv*%-yETwX!P{!vCm8e0ITuJ~^0jC{4K88Z#i1~W<p^x}&%$zORa6_
+U5KBdpaD9k$@un>EGX$eZFhf1ME}6tSAIs4<r(=Z*%39LewUI(f@lzp^fE50iEcu*w7>z>IUln+XN7y
+*oVmFC~!3*yNS&%0();HBmxir>E~JKfqnvzai~AV7FKCojYw=i;~p!gaY6d>P`M5?dXa!-aj}wgC+Uj
+KOLSNeEWoH?5+Rt=;OgK1)$VzldcjTk_i8i<i5`N_CWcQVAF5!<nVnoIurfv>6HO9l^AD&SRS7oogM8
+Ty-$GbH?O|@3@jwMCl>J^GJ<A2s**h2Fh?FdM!uSHO>sy96Ru#LqhlBauHfegMO$xl2tmYT3xs(xTcq
+kRY&RzMkl9G-GZscPo7MU-Kgz~QrroRi<R0eC9@4V1$_pu?S&BFg4Tpi9p4>Fu-n~6dPCp(FOl!Nd(-
+71jXqW^6C-_muGCmoc4hr%9l;|J9amm*?IZO%rgyTQ7HfT&EjSp_2fOXd(^hgOoEY>HMeb(k(En2k>W
+io@tKxG*^x8JQ*p73vrN->^g&g$DLl2ZKi-6vp$Z~t`CN2{ykUS_I3q*_~R;XBUe5`B}3+<Y*6X@weq
+v9+|hr-)s6v3<J7c0?NMtorH{uoqGDr{U(ul`X+U25NzN+MzzmU4iK<_>^&%=`{_ywX#bS0n2lPp`rJ
+9IeOTc;dBhns={Q(eY`rsz1uP8ylvGT?Lj(g*93_=WJOJ<T$>X~Q+{SnZ9`4S%=U5cRsb(zQV)R|<z-
+GcpHpcJCD*_JC(sAixWgo)wXxusv{0S8;^>dGy9(%G)Ump*NnQP;VL_cOG%vSwhZ)b?JS<C1xhnWniF
+BU3N*fjKV37((_<~|3Y+VWH_8s3?FM}D0nv^k{4m*ym%PD4yy2hj#1QB&L<6xpB|05?3UDa^#FyO<2T
+!vN|ENvC`mbJON$MKK3iYYq{by4ztW2NrwNkP+lFKHuBHgz+Ifpn`j3$9Nm?@3@esfHv~LPG~<fp|}Q
+?Rm!8{$Xv&F+Gx~eO^a4`(4p!CSaBzd95)O{?Ew$&v}k0*;oFfPeOY&Jt;TQ3^}7?gM9k;3XO|4SYp1
+1D`FrZb_zE2jQ56#rGhBGOL5y%V8Xu-X|;V=ZJ#ajZ2<P6$THp!y}Os2{{#htO!A6i6v#&W-S57n{Nj
+|Z1|!y&PymJrtq3BAAOq>cdljR=nSNx@|K0sh0mfSL;SZSL-K1BEjiAVA3@|{Xfq0>0ww(T_#*B{$O{
+V?Q)14+73Nk*KM2R6y3dBUMLp&bRTnjn`T`gb$DNh1r2Ex*EbwD>~4brhFGh)Xa<rGOsOh5r}RO>i=)
+JoA3$Le#XMCv1;hg#6g#b<1`Wvp!2`{8-R45?V&$}UDI$b@16*`ldQ)f5J0wr(Ea%dlG}_Bwu)&eK1C
+c)xveyf3r*uo*@+EGl;tG!snvaWs3cE;csMY1b8+1ktN;@K4H`)r7LlUh`7g{%VgiWP;oB$cHje(zBj
+Q2|&zB?(YE2vfW^rr@+R@P4sG)FSa$WqQK{UX9L0MpMaJJKR|x142i{bOsIbmTeAqjKoq@#HVIB`)Bp
+b$A}|5Y8bAo&Mt94yS<R3P`q{ANCs`3Q%u~*^93M=~*a_2dAb0lQn||$SEq2;4K^xoYxF(sNi*8(u))
+(U-hyU-s+Wi-`jN&0gEJYI{vc>&Fb47hD?I2oc8ch5y5^sh|eag}3019)&hR()1@20z+V+&k}VyTiYs
+bb#u9QIn81k4R4zndPgfxYS;Rxiy<Y%D!Qp6my8&&OES^Zyp+vReEvU@&drKZV6`^koW-N8JR<nwDMb
+tZ28dpUt|bQY1LC>;E~%wS2d-J5P2^4SxIyKYq(p&{-}}MLkBf&YTI|)8TorZVK<>;^%(&#wF?Ok!Bp
+GOxK-FzGwrl=ixOl7wm$b)k_%|xhm6pj)HZWZ4-@SeT9$7g9?e^AS5tjFuoM;`=h-Xj4_R%i#DarIob
+7yQo@?Apj)2f)zk#-%#bqc*`8@<CGwml7M`H=%WD2AdaA0oG+Ak6pjzaYX146H2t}i-U?CKrT<iF!C)
+F&jVKs99^m}^?KGv=deKnU(5KK|fd=J%3+{i4OXbOa_R=-t<bsW5_Z_!CW9t4#&G|SVHRaRMyu9Lvzo
+hJwTM<?I%UQ?_yB11E*d(SJ5Vehd$j_za8;^ZWK0fR3ugtCWHjZ&54VKa71hSoJELl|`VRZqW1Usf+&
+4eD(qzI9_^mY2m6vr&b6WbESB?3my21cZkoNE?;vv`h3!G{n<2H=U{%{<yB&90SZ1xMv19c%U64FkWw
+u4`C6A@1)s;wqXYdyUurXVV*7p?Ys&{!KNB4X2)O6pxGQx9}nKuYKJL8={tiRx758zt^gC5PkCtsa%&
+==aGqsJ?qT`p5sm12V8G;O@U6D|TFaKHD&Na@=VQ3n6Zd=qj{%OF&{xON9tMN$uWhRN;k?pZ#3L1ST?
+h6+@T)G~O)%AG+^h^QoD(oqsmpsUZsd>1fQD3OZ;|AC>!RG^>1<NbwvJm+F0=Y&Y-YKBU|f2~M&v~{D
+(IlQ%cgh9nE}~37{QSdj~$3$fYXAG@?*x%p;HjvJ8-e5bO$beG3LOZ8p>t&=o~<KHKZbV#vhA^x9*rz
+(7qaT{_)f?=hF49a+}n*PBh$82CMPs22g_sK)u8D*+WGD<tXLVa8h={NgavB(vCJrQS6P6hOwpxQi2y
+>N-R7tuGTU)q8kb)$J{7GTQKGWbqbXdy(N4~sAct;Zq{(i$8N!qxB4_Jsu{0@@I}2CJ}xn%COAp6c}E
+cuVT}pQO(b~hYt-o{T-~4Dnf~Dn!5|T5w^?K`*-SzO-021a3mwN_BRdh*eQ%6;N%uI2O1ij|4#)cfPr
+9IFR8;FxG*zGrzTUN<V3`ZoY20fFN60U4!f3=dQa&VE^frJ?gxZ%w!l%7ju$--E^`ZP`58%?lm|kD-e
+l3$3em==<?)F6vl5aP{l#uwDaeL5vHf^F&*t4tEMp!g+NIg?+s=Mip)LR(SD;(gl$70%7dvXB>GZu8g
+9hg8yA{A-Eo1mr_p`GrB4z;=Zh^)v-8m1j(s6BbG?@Aa%rfhR4F{A6T9tUn02V>NYB|pSw3GamPj;@V
+6h_&m1#0Ww5q|cf?xC2I1^c+@`+4AVMN|EPt3UH`_iX5)GKHWSV;LidIqy@s(GU__N_h}Ty6q-U2>*8
+u-x+*Ma&hq`Sa*q0Ls&Ro*WbuP@_K}2{s#}IMmfhkMpVqFmy7yDdbDee<s_*UWAm!V7R+q84$=SOIAi
+Wfk3mrcy_vlb2cqi#7nR`;FB~^polZH7CXt6D-tBG1}ao2F}C;|N|PUeGz+N2$`yMk}5Zq`*Lb8gF96
+>|;>nWd|Ct<sqk4W=+yt=n$VJ=GOFiP}92Akg^|%oWbwIo5|4dut(5NtT5U$#3>+eeiLj!I()Ewcb4r
+loBRGWCpV^r+5uV1wB{yk?GEbvg6A5K+@y|B|KLf<7tP5f~Z7l5Eb*z=|c%5NXzNYZlYp`=O!rry(XP
+cw9tWpWX(p`EmG(L9qCL;cC0p#jYnw9h|C5gM$~f>ktg#h2On6n$qo*Awao6|HJECQp0FaS)S%os|Eg
+<Hk`LCx^7J{Yey_f8trJh^P*-POuEoM2p=+|^Gh)|ug-L~$X{B<O&X$?iF2STG9bd-BBq^vcG)bE&#>
+>OwgGW5NjAyTsXPs(a;u^a^zQ9ZR&gdH+eTVg9Vpy7-;dl~Z@|29+b|HT;N!7koH&J$vUi}96fIGv7F
+Bbe)J@~8mHZ80MU$R8DYB^6-c*^5^Y!!<_JPwD7P>3-n)-_oQq?c{`>`H*ntG*Sy+W0?EO9KQH00008
+0P~d=M;R7wBCHtz0H0g{03HAU0B~t=FJE?LZe(wAFJx(RbZlv2FLq^eb7^mGE^v9>JZpE`wvylVD-b(
+5Bx8z^WT$DgvD4nH+nYVD>l3HlyDKTW6a~p_B$6d5*>2tZ_MI63B*2Gk_i=mAx{XcZF&GR6^TL4ptoy
+Xff_NTA%OTt3i|*%G(%J9qvo~>alZMO7oY`+2Ht6*SUHEmx-d}Qdev@Cu5xa;N`IVn?_Eo%z=6)W=kq
+135IA`CyeRFztaq8vQIrF1Ad-L7d`*&~u_Wk?s-d$iJwCn8c?RDO+lNDd{D5u^mUNE06R{T1gtvK8AG
+=ow=j%C3mG-F8`Z^JniY?35SbGC?6c7A%^J^cJLw=)m3JPl`?oOgqGod8S(gX3p>?MHbSXfQoOpcAeM
+&MICmp-{c1ywh2v@tS!85R>HyKUz-nn}lbb&UEUpR?{gvW+Uq?PS^e_{F%>x5JOll(+a=!bC^PExgA#
+iSl{raAKYjiL_u@o3pod^xhiVz@O>nyRN!=)rg4fLmpq>enp%@i2Pe-{n1-Z+2i4zDnLV9G{+dsxPN&
+leR(_VT7O3_Q{%XVN(HRmNK;HM4VaDKpKVqA*n_2`97XVu(0JnfIf!guxM;_!7)|@ZcbQ(rsKAqYbUo
+Bjg$twK85^pLN2d0tXKM!9M+o?24{4~dU^_NQFCF6Ehwgp5uEO(fNQI`8rz-?;pvU~#!TWh!Ck!aws6
+V~e#C`-6Cd{aNnI6I5;g8%tkP|<>SAL>F?!NLgkM7Eg`bu6a@K85uQK2A$cmjlH)u!1&NxM0YyRO_%u
+$KtKpszmEOR7+$6KZ@d<(GalDpQN;zo}?Cg!i>>$w5I&0Vv(w&h&8AoE}E6(;rS+w%mq6Zu;jZwT*O<
+4lYnpz0+_%}4yVCb<$pZNuUau)wvVP^Y)h00H_?uPw5w0VoXMTkxGz`(<NHEg-VdtK5|??N44*xl$3f
+;Lq*J^&T|P_sf~Ood-7`=s{yLuX)xMAh0P51uE~irsHBlhAyM`53+S;NRlS+A#w)Wh6lG)Z1*l>G_*I
+{tlgVU*}q@rs`9s@WOwZhgri9TO|W=lYl+!PSPY?`D%1`-D~IPLP|yg_~_>A^G4Dh0Vz8m6F?qFKxo#
+N$~UaXBqeu0S#<OF3!95OzVj_rV@tjmFvd%JRCCr;a_gZSScwcAVFHVs~KEdOz!f{p$Gk<oNd0?aA$<
+<J;q32JYdnx1Wxk*W>J|J^C=t+{uCS*b+dZ&AJK3cz$4yJ^1ZBeQXu1wM9*YYKMo&av(kf(9=HJ0>F|
+`#BlY5v9adBc5~`D#TD@LIK_GYQqbCZWewTNU(e<~+qx`pSu$i>*xf;?ZLO2WHYY}#SI3QwK(YmhR}J
+J{%}waX?ahr=_i47zW;e)${$`cCOf#j9PuR(^SAzTWFdQE>H-A(N{;R%&TD3hWG}=KXuflpGvo>k8(l
+jadN0#s)T-*Q$NGMF{SJm}iHpN<55OQ~TYur@2B>~8yFkjz=7i?x^+;BdW`c;FmOyf<02&_HvXj@f8i
+KCBY%TaIAZ2f%XHwt0w;H}~-5CD!?b2Oy`piLKS9v)?39&Wi*{b3eISph0o!DP|so#?ZaLk}kAOathd
+!*nCEwnl+MObmz8sp#ls$tuik%e9<|0i$TD0W(K2)<Q}gxfX#_YuUUe?Q8QkqLo4W2au5FPk>BWhh1N
+*dYd@WmkM_Z?xb3?tZdbvG)*G&cr?$hK+4&c_tbKl=K~F4K%6ijZ=;}aX)CrgZzjPQ1{hle1!O6(&74
+-Hj&$pf1=Q2l4`~=Db=u$$dY_xsoXx;_BSSs(SH(WHxwrJh-~*gAySfa6%N96S;3wt63<q1i-0iS-G5
+g4Q!oplsNNwB%W(&<h7catV<gn4OJD9laeY)Xo<Sy(@p|&I=ZlfI(-)H}|flZaij4VyiQs8uMBXXXET
+u`XDf~d<x9J1D&uQJ|@2N-3SME4eYU4b|NPcBHgpQBsd0z|n?v_`0HOBr$x^42GjIWGIk2drzoNf3E{
+lE5~w?Kp8YSWcVcM;@F_T{VME%5Bm$a(HdEl%tA2Z)kncsk&4FiPEJNaCj!?U2wbCDf$lq5r3^Z3@M<
+Zdc*P)0Ol`!BqM-92U-%eRJ=oU;Ytt}c}iLe=shHw4gV><=zlhJ)+ZA#bRV}|Y%R=DVOoW1<5DTg6+M
+szlv(9!1SwWLG<+s=sD&D&w9GIvVXHTjx6P?6c1}V;2WWntr*hpFHGsI07Y&*wLyN#hiAG|zG{V~jqJ
+^Y*LSoM@DbnIsH0YIflOM2YM!a0lI^i1#BApX+tI0x=b}gO?zxIQCD#xmOJQ)~~%8}48#E^;Zn4!_wm
+$AB{6tp8MNKJ2{0b7k)s39sHHJvGEAzJ>i5tPEqB@6743RB?Q1A{A>2(D!R!r%(og9>GQsnGw=3%mU5
+0{}2RhaLRRh`^Kd&yNRm&QH&ej(XD{PTyU;{qAh~?zHCjZeQEr^geRvkMe*W+xBl~x4~^1I9LuY_?_l
+iYC&&1i9r|2F8Ds@+Xq27ut$FP=P$eeJDyJv9qr9|4M4~@?+_glo=kCauo||K%5~QYP5BxXOy8<*!;1
+~p7M>;tt71wG<!+N8+e~+`;!48-WT?Fr-pf|=UiyXi;#pF9gIx+PGs-yxfRSM#-|%hJ?N4xX^-A>RC{
+#Oy>W)6m!dWQW^CSjC0YFfRY1OGZM7HQEuu4+RPdplaCWIt%Kw11^_;MmycdPjzy<vYK%2#m!7I|57I
+8n$Fc+ia~?p+f48oP_JB~?v~MHT!8L|wugpZk+yu?2>$kOltDphX@2DS_jQ2Aw9_23$~~5w@s;-vwlR
+)&L7i)nXHhfXxU?OI7o)k=?KuIga{j+-9noeKaD$ecu2<$%z1l;%OKtJ(Hnbf5PyLEr^$?pMdw|GJhL
+~^9mekwu4;>a(m6@VG06xb>p(xCWjFM5X-QkXjcdyOb(mX;H20SJP2^)DlS$?XC%27X7g|f^Jy?kXxc
+!jzFawYt(Zs69*yePbLr9SLL^q6gITSXMr?PfBP6c46@f3y!S+>*amHbZ&>!Gk8MAfKGFXHW0Fx!O`~
+5}E(`s~xSQKl4d#cM>hiuM7>?z9!>!{mzN*+LRtImF}6b&_EDF{DXu2T-HAP~Oj^XnL7Qin(J70@gG$
+o!l<9x%Rh3asiA@(k)hMSa^^Q!)0wfi=uY?`qIS?n#RX44tJ_k)+5Y7T8kVlfsm8HGwW!M=D;DlP<|z
+GK^iq$VKR`U<A-x)2KMbor(aOuEmOQdhGz%{=x7g!R`7IbQGR*m(dLA7k(Yb^Kx!|Z=kI||7jC~*1}L
+etjQi!dc8fpt=H4v{Jv3l=nu^IS*1Rxyn9E*`}^<y^SdFiFswcKq47rA&VTcDY&BNeXX`Kuft)PAijk
+}{n5KbU*-~wa72om|TVwR&(%+&;Q8gLJEm#2)eMQT{v}!$LRc=9L{q*4Mezg)7JwxvuY>5?0zMpv-bM
+(N8y{r3gAA`ml-0I+X214Fi8H+f_kU&Ka|HJzqR59?!>lF{$+U>Ky$5%iL@Cw27VZdI>M<%-mu|dFqO
+g<!i0Dc#`f_m8A{sc~xcjP_ydQK6Fn1>58r$KfjPFfpiRfYw2+g!!^;yPuUAOKW~uDfEM!nT!^n+kgU
+24hOBXw8c|e|P%s^qcAXFTcJZ2QarsBnhn<{tEC{O21)8TNCHE9kz*K{piNdr8i2G(#NJyS0$K1ZewB
+i5+SEzMeS>s-N+Fr=#{7TwV3CPe7z}j&NVX2O%0tQ{wSs6H5kx1Dn)~D>gbnlJ;rX(D!nm8;q@pYixf
+!SAkx%Xq5Ru=SXdE(f6|E@9XnZ)q@|~8>4$Avi&bt_j_Op+(x`c*gEhgO=v7Oj>R7MhKUuZYHdW`Rvk
+(MJdSg|O#8it&0oKpK{P~Zt=cc5M%mC#baUEfeLz$`W$<LZ9E5|yp)X_i*j5N2>P%@y>)xs%t8v@J}@
+a|hcFfcnSt!tc8#3?hc()8^@2Rlb`j0j6zA?ATR0QNPIq0+e!z-5dTAw(7sOz@(&gE%5QqsG=KaTkH`
+Y^H=%p+5jFx(c6PNxK7(RDB0zjL~6d#5Jpl#|GxmxJm7Z2XRtEt0LTHu;(stI&svLPL8#f)u>)$G_s;
+b^wlvdOB-{6A{NA&_hTGJ_6VhGD0EJUY;Sg()aZ#!U`p>|mH^tR(RTT7LfcpW4{2LOX#Wb@n)}P7Z=D
+PNjW9jgi7Ef-ujN<iQ`huVVZYmj0MzGE!l-Mb()4fUttKdKM+6n!{UBPY@Gk5MbuxyNz;_*m>R{1d$v
+Y2Pkw*_+ksUk!;j2<RxcQ4k?(v->*T@>sh4uYp^?u7L?cX)jZxQbDkj|{7Fm@Hg3T(4L1hZA+->WfpY
+71~!&%gQd&FQzNXYbJ;w5hkPot;=C@1Q$*Y&qZ(;;*J33{9+Kur7-zK(dK~JgWgqtq&t_Vvpwzf=aEP
+H||$~?lbQUV^KZ$<Kz7SuodfA0i5n+CsHL=K%e<;q*My#V}Vu^QW6#hgd?zmRYb1>XlqNUMLC&xAS59
+UB*Yq0MUN(Msm-^opdJVg7IN~-!2Oko<~36dqZO+>#Q1c&i86FA(Yvi<HlEr3YT@7d$))d%X9Uw<!Ak
+)b16x^^@D-*%K}9nVjF3|GEj-bKfYzyEW<kAYj?jQTIz)3noriK$R>reuJon`)TljphWyWoC@%>j{z5
+T~McFTv+@RK)%9dodQIq%Dv*2sOj=h&n1WEZO!&^FPPPbn3_rJpuI`!qb5GP!M9IDnufD?i{YT&?&LU
+Bwn?e<VA3y9Eq|*OhER5V$61-+z1h?(LiD`MXnCo9c<Lan4(wre>Svl7qTZ*PF^Lj56p<f8VDR`;+Z4
+-A!GH0?E53@mprX;#-+iR}7*>N{gIXOc%opJHQlHEMXXZ!bn+uNjI!?6v{LWb=0fUPpMj*aDjfs1w&0
+>uxCS97aDunW@cl1R~8!wHKM4YCkSfz&r-puGj)i8D5%pip<rB@5P>OMImcYVc#|`{7$*CQsBo5xsqV
+{LV#$2=ANb45cs_-d4!Cx%U0G=vVQs))JK8SN;}q(uBHJuPg|v{ctah*}T9mO_N)ImLtM!&649n_DS=
+5wDygb>K#t_&qhqXf`s8A<tJ;PkmJA%nn=~<c5XpKp(wlmxnek%I38x~@~(cy41!8DLo;07gRr{UqGV
+JYqyO0!)G^u&Xa>VTmlD%B1Z?_wjtduu<-{G|wPNx(&IeK$48{1zIT8%q>BMm9nf;c`#X%p156wh)r6
+Yas{<#*2F?G$b~~Kx7*{ST5{;AlvJhgc-^{5JzeQ?aDl>I1{D_txGpuh+Y(evuvl`9Ef28AYO4Lmf@C
+{uD6Wu3F^IFu$y?JB9~?paYhtkWU@RYv_p@34N-LjCc~I)6@Em)<lx{D!C%v@UCf=v_&T7q<QR_C4-#
+!ms7I{j5kTcsk|dls^IM=UFApl|LMO#weR;fxYA~Y=$bvKkEi(xsX6dpS822K$kJo)?8Be#x)n7S!#)
+3^M&#Qn*(5Ha<^PBUx9{ZYr`E0&fC(wle<(EkQBD}jHa-QG3ftNwA_tIrYgU??ed`x2yE81m)!~THT&
+kwzqhfm~DSwm17)fq~BFdCwz@{CBw;mrI^9z##i7Ltk7+H~3B(TgKyf8O_cM+NvRT%b)R78}S|c2Z{^
+)cFgMADppYfM&n4FC!MFwFOdqMI8iRXRLm_T#y~sTcoyxi&AONdCt>y7$JSRsjTx<J8{}^fT|)KB#`_
+SSiSR`0t+Drt^=OrZE0D0(n}`O3saE~ri=rrX(^q977<8CQy|V*`yV-oMtM7h(v8z_S;{@zwuDKJhk#
+@uLjf7fbVx|=Mt#jzmp`^u=_Sq;0|`>*A&}A64x$SJP4(K*N;<(39F%yqk}fz0bmVo%h<KzR3|dOED4
+v-H{C>D=t#m1ii<M@cG_&oJ`o+qjhKrHIk@NAwir7Jz!GT%|vo5Y+GtK9)uX`m2fc;BaXb)OE-UFEU(
+8CAGe`YHHCtAUSYE>ct1CZD)5h1y`H|q}e5Q{zEg9n+#0ys)>ApsO&1}UUj63>A8Hw;<A6&|6>cpk|1
+D~@RZDvAIQ=YDpH!_4En0d;%i&w>f2ZH~s>F+~&(95I=3e>^ZJ10tmhQ|zXPFZvi*fOikx;Y~K^d418
+~#Y+)*=wtnhmkk5U;V|7(Jibm}YlD0B(cb_YMng7$L4EJgrH5JHg;8F5y-zZiq%w&2Y4Tc3(HVQM<+K
+{XZhHuG+m~4pux@FlA8T7%tIm3({%~TzDg02Y2de-=Q0AdX8(n0zY$7mnVWB~dXW^W8`C@@NYPiUFzs
+O~KpgRt|-gB?_a!*1>6+|gc*^s?>!R${1knO+%Dxu4q_juMjAY$6DAC2cu`@EU|Z1yY=n<9#fvq5%>_
+nh{C82%nEBt7-^Jaz#xH_798g?T9pm0+_^xhh@*s)M8F%zoiL|7;hwh^bg3$#~0G{gcW*LQb}bwa(7Y
+ESCFAaXkrlGQ2d=z<X^|hg<kP_EKa3ox!lQ!eIfC1c&M>jR|W+cabnrZO&KWI&7XpSL8oCJNJ*zgwg{
+giK0U6{yF9#?a}Qfu$zRopJTdV%m_)Da8=zs-ndJ<=a%|wlj_|u=EDX81`=aAg^;^r5R%nmYC=yGLkg
+iZZyJR<-HP@!mpyX;yr^WQ7P-X43SHuX43GCkCZ9^&!cy6ZfoM4L`(E1b$!!a)EX%M@zo#{P=^Y++`!
+5EALD>+?L`y8MHcnOR*2Z;)OmDll-5M)muSRJ{bH?lhkSY}OA@5~_uGg@F&OsA8(A-2F_A!<X4mE;ED
+Gr5tGjAE@MY|o%W<RHQH3K!^<FKA=C=i4)v!O0G*r)#U9!R;Pol;Kh@LRz27SOoI2A4$j&=g{}=1!5*
+AxtE2U4Tm+gX}>A(FX{nNI(>G8U%g4MK>rD6zdIuQUGBa@+(0XNxh(%BA*6*X7{|$cFTRB0KmMJaBsV
+YKng&x4o)f?D_A{y{}BaU#`coyJ#}$1OwDbz_S26_+*eGb8YdfyW~Vi;%&Get>S@;GQ-kfkPsdQ=3r2
++llB99KGje+D#?Lc3(63Wu9G-vq?tLxxiPuRc2e&ZATabew1}(HOy|+$B%T%PWMv*3l0d`ER3S>f+C0
+D~J>*5<py{;+(qh4=;TOHoNQ19?UZj^ZQiq~ING+{-vH*U9JIeu=5vV`o$4Ap9)KLu=blE`pf;S^QU<
+T18VB?aA-Ok7sEVvR{*^;~%j%JeKuX-G!H{sbSamsayFVH_;+e#W$5`}2ofFQy34Q;Qw2q*gCTQ6%I$
+o#qPbsqjK}9uQZ_T9CAP6_6r^ky*P)k1g0MrUtLiPQ*jyxTqdwbWvXNksZ4&1eH@v;-vDafV;r<<WB?
+KMwpF6mIM^}xGD;*5<rdblU4HBcjsP<!&+lI{Kn*4xtM0M60Yg)%fql}wss%*x^f?w=y_dTcPy>Z@>*
+N_#%r?@!^STH%u?=uY~xmg%J)mLHC9AW)X0*Zhe57DX!oX_O3M-E&`xke*0X2@RCy3m`|6JMp>`If7V
+`IHWjs6^LKPMt{wQ%)dFy7T&0Sw*5#Pb{uKW}~5NKO(${V_%wDb?*h=?BLtxKGrWV=6xA?#)?<K#(ZH
+Il|@ny|rJW~RNKE*jF2B<+iO(rM<U#i>s<&vbWo1@2v;-7o6jQ6&9+ar!OjpQfv-WSNXT{ZBicK-qUu
++xNOJi;JZ2+?^n+?YN6dmr&a~J}wLfp{C1I10}T7{EK;%mrd0<fjVNX>UNAkPKEf)TrgZS9_1!mw|$$
+aNT(8=++7J&#pen!+|hJ;hd=x5zv&YlR(Y$OzPm$*m?Zr&5lW}ecOKG5Jr<k!5)Tk6fMz!oPw1kWr>i
+IOT$D2TGwhg!>&=Qj1X8(TSo_dNF;PLKQE5F0<y&0-6DL7Qsj+F+^&}u&+vDcf*HcA~9&|bQjtjtR|3
+~~-131_CC!xLHa7-$1uM7_-6BW$3jOl0yz2#dW0nh@<V)=a+b>Km_-sC;WmC%e77oBaw)g19Dc}xy+I
+B^lA<2KYi$GWGMq2KL>IXJF1N}-J^pBeoPTEDVYe$S;F{gV!T`WD2aW1M#l+xuzH%G|2o9KE0~qTc!C
+QJ9u1Z&%?bJj5{$Pme3thP5wl;XOg4SoWr+r6;qxfJtY@^0}i^j_$D=AKft5!f^Sj!?xccFt)3qbnfd
+O8W0-4B~nf1EM_CId+2QbZSXI3_!Pe`)G!bI(>vd1@*7&3i;Tw;co^v8cQ4o?<g0ns*je8mHTqfe&3b
+)9pQK>+lgyEyI4BU79JS-idL{qu)?CI%U3O&snm`*45EV_Q7jOQ4`t9jO>ByJS3h=hgeOifRxb^#Lt;
+bo#&yMqdc^GC1C#F7n>D;)c3Gb?J97+NYfyNS+5{-3ey3lm1V(qSZv?hW%6q`^~9a-|Dj%50mr5cSV(
+%vaQ5N#tLNghGHid-dcj423JHLHI(ZWDcs;;Tq~!JEE|BR`=>fKBsDVuUr5&i?^WO9KQH000080P~d=
+M}9?s1w^I*002$_02=@R0B~t=FJE?LZe(wAFJx(RbZlv2FLyICE@gOS?7e?{RK>Y4e)d;#l5BDoNHF}
+45G)$h=n_j@f(v9rRDz2!yF>`+EynFim4<TwuLKfLmgZzwYg@dnZ|kiT>D8-kxm5(K;x22mL{S4;#ey
+2!RA)U@qQqo@kn??>IcGNk_1^pWeE)d=dHJyCoSAuM=9!u2dFFX$o|)9#{xE0ZIL-<`P2;%zoc_<v{m
+=g>{PL##DUW+D>y1nIo0hzB>1~1UuP<8Hbl=@gcmAO0dw1S@?|ov?zuZ;SB;8x|{d<clZ>cT%!F_A)x
++*s}yVxjWU(VX!pAMSOr+#-7|DW?;!kZ30b^f31{j2js?EU2VU%~tA*q@vqV6c|+|H<N?G2nk;ynoH!
+w|@V70W2?ttJlMEOH4NI=O2!#OU3nYW>bzSo8z8=z`q-Cm%+mUzlhNb_-*Dm8)stasW<nqiFMlHWhO{
+=w=zJ4O~2DG_V1a$aojxoC^B(Rc)y`DTTI;JJQN${xXJ%bpU?X6qOk!yt4-eyf0cOGCK28fpEeM}wpu
+TQDdM<0u4-Cyr+6pFU3P`h_Sx{;b0Hkm>%K|{<9e@xN1svViLb#$u4-D}^gZ}yZG|>-x5E46H<Z_O*V
+_94Fe8s+eZb0_Z@L(6;kUQ&|NZ;_^RJGa^aV=)>gKe9%7~jIeLKb61zad1O$ZH$*9GQ5icc$WL((oiL
+7W)apTluMF5m!G2zW88#0NR8Tg?JUJ3u-ks~TcZaO+7cP})vwA4L6G@eIk$JSWWwdmLU(vWG)M=QJ&#
+f$#|+L3*j_T8`6Wz4R_k)8tnRgC?zjL$qp!oNv&^Opfcqf=LbUcWU9PA+K=2);;XzyvwL;=Q#OQJ0Lj
+e1&ktDE?zQ|WVqTyJbXzsTv<A#zD44z8EW#$+5SKPa1VQioLV4|vw-s|%|l9aAAJLAadx@l!j5QQ9Y8
+Fj*8nm>vtN^j=oMo@Ov2kahT|SpJk&NcjEEL1bte^1(iLyED9y=QAHXIN?A#az{-PBKx)vHE@$_^aM)
+9PNI$0ZwTyb#<=?mKG!_DuU-c6eMlDNf_e3bM-nF)|9+yP(;tB>-~>x!qJK&8NW=juo5OVt+EYx7}6{
+j`Hs)DIQC1~`&FXT&uqJQ4|Phc+qFNiV$}qdg}}JpDl%)L0L|kLoyJ)pRNdM!-gXb#{r=TO#$8oW9@e
+90&qAKvmQAs-R7NdMQ-Iitn5h89Wx)1Lb?^`SYw_!j<#2RV&oIM&fwkFeEg(#)z5KuK4=TRg2>3!=fT
+k6!G-cuVzE*BYj#yJ`A~h85cAMO;8_D3-8qmmN4=R4M=vaU>*V_GbB?L>C|bB(bm|V>vS5;3UZKRGNV
+Pep5B{mJ&AHkC&yQnw&G(PR?Nd_IW@musVttX70kg(j)g0W9p2zNcy00qvud@1*$@rI+U3o~j&+g^3R
+TU{NZ=jT)UnXi*6qdZ$WEmkS6|O^ih52Sat8Vt+-E~TMwP34kT}QhrA;m**nCASo<+*$0$6ZBd)EtzY
+iK`=Ox#+We;l(t3)!H|uk^$KeNRs;Wr_c$C-&=!Yw4f##3d~8em&6?`14pd7q2W{iF21$Pz`kfa3SWH
+?e+WpFo=Fl1f;YA2c*Ggejva~_%4({016ljVheX+_d=tJbSUUAFXFA~--UX)z}+lZj4j{Jis?Ith(NS
+MXXnE(Aj_cNNrgM==3d+bK(#Q)>dbHz4{}1-$2CH4Pm`0=#fd@QOHbrrw}Mcz1K5!E9wURdosrN067)
+-~j_ptfkZZl%$v4V3Os|(Nbw(QH`O^d32{$L~h<IrbAe@o<MkPb;G}p^<vjI?z0FMB`jSNk73|9Glqe
+?LcfQ5Y-fROYl@z|A85Fx8pbm{V~*fLtaA>S8Rp?!_=-07k<Sftv5)8&Y@ewmjZu%wz05A{H=b^}SM#
+o={ky}Gm6-%y>h7a?`waR5b>55#Q2ELK4UhNp|XvX=skni~({I|z|7HIqj$;OuHt><h!8KKS-_>T;5D
+*iA|3It`i(l(ORl#@=!kS|0up*5n+hCvUlu>kB%@w4BWq?JZ}s#moh+LrtetGx;Lb=g+Wgg_y0rHXQ0
+dk5y!FC4)YHkPjrzBb}8phIK}q$6co}&oP##_Y96gqtYf}ruzEuUVJx}KZY-H0<<0b<FhQIKX~Ym#`3
+Asr);oh1gswzqxq{djJVCK)jZ7bLMlVHWf1$%6_Y5-a%o^rD$Dm-79C#s2KaF3BzBcgc?{pw4+trtUh
+K(8`D6GF1U@iGW$GC{76Ai0Qn5V55|#tzu5h&(q@E?-=z3ckD2X0Pl0L#J_*#MzI)Qw~M)2#o4iQE$V
+J{>ULPF>`Bpf`Fv>dX8F`qvyUc>w7Q!t072R6|C0rvH#Bup`kh%`nf+TN4w$=YK55T<sYpAOnk4wd@p
+yD*xEX)(0=uqzjdS~8!GCCWg$2kn7AfTr4bpbt%H?sY~;1Mm6>YI4P;e7XbT;~YFV`dtKxs1E>yWM>l
+&%x@pUNR-dlzmHP?IGo6M$eBxTXE_TYr`iScZ)3SjDx#iralzrRr_UQeWN1?%o8Z-*FdsO4wbbvxutV
+|m#yvO-Ycz`q^j`WHWR2lm+a<st$B4$}6^RF-F^<v#D0ciRrIc=mFVd+N2U<9s%p9PU(6MYzwksJx8C
+GaVCf$!v985B-7?>{_Nu6Eg8(pu7W910&D&H9DlKL9U9U=%611#`T6g8^b^l76Y@C*dKx*7#x8{npO8
+=#2iA*i(;BX#K*<ms}Bh&g=RH$iWLj}}b9Y1T*I1@pj1J>&5CBK!U=d;W4FKHDZ@uN1BtX*IM$0BPbN
+d7&=RtR0a%tuD#lbTZW>x#Gms1so?%2;iHpm;n_cUod<ys-Etc4oX@8+7R%<gYHChQI_52^9O!D4`Lh
+uu)iA84lIF3JxRJc*9#;QiZN^5a@^_~Zu&y5=)rncuM#H(@QZ#iU9ZU=#F-wt3uCbhgGB+yM^Ng+`7l
+&G^<WpscMaiOUT$+}z-(<ecki;pXNSk}8y?<rvz-${kK;&|y#wWtvgJ5Qy-SWM4c(#RVn8eFhG>XLD$
+VaG4JVZ5WAZ60e7nvy-AwmS;<)lm@t+WwuYDJ@(cKWGWNL5g<zZB+i1LOLlC9ixOw7Y_8oCwFF;*&R=
+vJSOdwSPGAG9|2Zi3hPF+h-nGdjZ4YgIpqdwMoAfSwiupl1Z2hXLs1Tm+4gn;o2Vd7QNxjUwodhD)G3
+kZN8=6${cu9%2_`Y>fT}$U+}h=v34*=^vPaYHvu#GA;DcR4i&(L5?bV7=sW&|J4YxJoNn;Ol76halir
+^viTnX!NWTFfZ`=ysha?0TsHJi!AeHvQlXDNFcxW6*VnwX1VZ&nMvCy3;h6eB0Y`IyvB3<4MkRhgDlG
+R5iR0+?kOhg6UV|mo3@JP)d|i6}T985Vxt@*p`TTMP*33zT^hzirIF)X^6eyNq`g5iP0R>n|Otk<Bc}
+Ebv181mYNRuuL+E<m^tPm;Y)>3%_BTVTjVE-3DI|W(sNA|+0U(K(j|AGMY-^ZYs0<!}Mb45e!F#8AEl
+D!r%zLnfo%sY>FtSq(<9>fI}aePO%e9(05>td5kP2#nBtn`^otxaS7<XrUcyyYW&IV>NklU9S;Am*iV
+xFLu1<%$@>{)1#YUw=g#9^PU(BY&Ye-;xhnTR-MZrdF|s+%yExGp<#|3DJZDKJD;nRu8QXFVt4V!V8C
+2<MTXw4#AUkW7t!OT_WY{A`k~+(|4E<NePIP4luZ&x9y;@H9M4=q|z`H(!_~!b3P~LgY>(E{?w740m>
+ZMAxuXiQXyE8cKMLzJUTd7?#bV9seDco?X^L$s+vl5gKa>JjsR@jEBv83f3V7K2WkgvjC>^Wq^c0?9y
+K%W>BrRpnBdy}3KbbZVkN1vLWfrf?O&q&Kj%8u1ZJPBc}V!tixA=0u0x$sO41))&2g7+v6|+anv*3T!
+PIei^AKt7ON0Q*)zIIxQf}_&#Az=8X>WBNb-lkKi$n*(fQqV_B!(Zcl7ni_eoS^9+h`^+muG0>3LxI3
+kJ_N@dQbqaN*SRK$Vp9HWAY?Rj$Q5<Qj2|b+;>onLF}z^^AIOaCpAgUlWaFZ^KRCFvOWEw<3i{%4UMw
+=#7oG5&7gJTnCP6m;A&Ply-dKq243V6KSrpf_97B5i9ruajzpUt<4{k+q+N<q^d<q7Iz{R&mmK2N<x;
++QMY&WcUCBxppmZM=-m7CyyYkWlxi$|6lHMY)*|d=IJhCtLW#I+X@*Xqw@<&a?Hhm&iS++_(Jqf3?fy
+n?FZ)cNjeo7qd$Mwg?at|-T%-a>)j{*;a>m9;A4^MJ$8vx0_&$>XJf-)m$3VV1jE)cBI#f{}sv>fnPW
+1kW2O3|*p7Kj!&(RB`RQFGI+dk8*(?h<!FEuaNeUWu{?nwKnJZ3U}(wKZs}^-<F`tXn|2cg2M5=dfLV
+U}zpPNqos-YhosRkxX(6PmYzmuQc=z9JGk@$h*<AHj@9y<6thypG=E>k}*(ut+hoQPmZ{{*E>jG%k|l
+kY0*O0yG>s~9;63p5{V^nR%<)D=@ea*#xwCFcnmDIn}oLKSnbx(QHj5_u0OFGgAGX{B}<}~gVA#~s3|
+=8F{~4`D`H`_mO`!fv)L6GV2;jlK(<m5S<C}dTMnsNEpxLai{|0ou4vQg!Nqo54piNkgK>>^wL#MN+n
+~GDx%+W76h>vEgWO^ti1H#o7{&D)(Lxp%vWl@*tB6H{wgkjaJpC5W(5lrS5P&AVNR!Xok+h%`&<SDa3
+h1r&^uT<T!v;p3g(R}WX3YH<xB`f){u}hY-`Nd)?|s_^`W~sWKPgKNR-#@wmV39{a|)(SlTu}u@_h6!
+K)oi9UB1h~)%)l_ptX>#zAbV;FZT=qj5_{FlQ{NCUd($6n<-_+s|v9TbVBygoD#<Q$F9OvBj_mLaN&_
+?c!37f95i<wHaDJgQFFhS#)xJZ&Aow04v>Y}@~}y?$-`#pudMx6qy0XE*`^r^>eqYWKXQy-c-wkGFZk
+uRG?-BH>F3$$N`C^MIdQdepvo?bbA9x4z}`oa^!;m4hn#x_F2`jt(s!_de@j!xlB&sQpL(ectO~htGT
+_x{=Wt474Sg6J`XQ7n*Ys+r{qxLHWX%;vYQ7{}Z*DGB?G|%c5rF(>5FPW;-Ii2y={)Vi!A6;)z5~Vj=
+$Dps1Lcazs2NE%I%kruq%==R&HRt`<IE4pEf4HL<Q703Vb3o>4_}81Lj&byhd`Z^a(up^$)^>pg~<Ii
+x1-Fl;hM8(1DUB_w{sqRx9-On+(VE)dm)F(rS?LIkxt0_MR@0Jc-z@kzK3#X2u78fUi#rgR9rwSnoMi
+4CG?hjBf>!!vWU~rA0SO>@8y_qKOdY#!vkms6a;X!^;J}npC|)0Hy>I^Kbm7e@$_zlqRX!}BC@CtBZ4
+M+B{taADQ0@<A_nsp7{US1t0pl6gyJF963nh&=Jf@zEp*(MVBLCp%B5Z@!%Mrqf-dfZhk6z9oFqKSQf
+f|km(d&9f>w;&MTzGGfXmIj9K!zr;opLXF+8Be(#wDnW3(MmPM|u;#;5h<EEud^vp5h+xyAS_D8lEI$
+@nZQ#Aii5JR7$cxY;Xr4tq^;u$RrwUNd?2y3z`-U;WrN7hc4}?V8Qr)?KCWhS^}(JPb2yId0p;G-eaG
+6BBpMhEUvA1kWrh7m3@74Skv|-YB7CkYy*&KGCGs3R-Xo)&UT7>oRCjJ=!OmaYgGLX4!4R1;`2nfO!D
+B9$^Alw!V4^L?*%uW-$kp8rvC}imNyItAGMBnFLicT%FCCq;nUh1g%~=7fYz$^+X<9eV-b%`jj$Eqth
+-(umvYBUj=jh3s|md+lkSN-7H6oP!Bxw@Rx3G^Jl@~RRB>05PotnnzRvX2&V;dR{nHa^wSJt%|tzf2T
+}peNir+fK=y^LXIdb`<~LV6BdcPxaW==bRG?)Aj6loE(5}L|XeJWbh;(7?bM-Y%ZyjC-b#I(V-Yhv3J
+!6xjj%Ad9NNQbb=fJ?OSF`2LB4D#N=$28|w4BUu9xq26Ub-p=<(R#-T89S^kjjxFubyQj2WvUZRnsb1
+g@vtm*tM;iRuYAVvV!Jiv+R1__EC9jcr5Zve)BjZ4?{BdTfjl>-I<>Q7U|JiXeg=xN?pHFmsINdR12<
+3$@7auTsxAJcZ#KQvPqmFCpU_h&~3J~w7cAff<JL54ET#CU<5VSd0eg;GO1ZFW|>e_4w?1!K4;egf1~
+T5(Dp7whwtC|C*1T3bqQ^gAuQ~R$jMsa$3H_eVyjGxgth^k%xI2yYqhvYriJ2_GR+n*1^Yk<?KI*1oD
+@Qb0VeKABALstI}$N?=X{iS3H6DXTL}G*#k&(vGq64cOFYUF%|hs5HmMBBe;<ZfP=4K<6NmOET9`$e<
+Rmd&ZjpMgOm7o!mua5psfK<4qzYu3A%rFXz+epb5+zU0U)9B^T}!UkyN0w^P;B&Le)Sp_J2R_pC>1l!
+$TOz(b)*AT*w2*yW1`jBrJ8e&rPoJ=&<Um^_i;iS+Wo<z)%v)U&li(Hi!X2j*D9W!XQ(ei2p8z(&WsT
+t7{LljN@uYJdLf68{>^1%k#gkDY(N9%eRV;Q5A?C**yv;jCYw2RdN4C!J&4Iqq?0Gfoq3pS;zrSnQ^U
+%}(mO3_BC#T2Ob+1YCnyzR4=!@^Uh2D^?GG3WQAJ=BXtql80%iqQ+ewXG*<@AbDw}v^(`2RE!3?gTBh
+Zagoezq19%oB+Ae^jBac>wxE1<{4q>2!J4W%D)blAG5yii<0IO0Zisro7Gc4<Ml>b#tEY#bY|0+!-3n
+MEwL?MUU)=IrsY?ScAw7ADp`18z_z>|P&zh(%YaKfzUzUiEvyxwHW15t7TKmG!?|F>z_&alt4Gvg*vY
+ohkAxP8tUl&|jW{f^-o#d5=0N4Tu6k&pK}p)S#@kyUvN@TkmF)15|fqHLpxlR!^31aik_Fnu8P_r>$m
+OAYt8-pOIE0K&<SlhQ2jeU!S!`E$ov9P@}+d&8(%1(9)B4<0QicmxS4BZxGa_U6<nSB}VJhNjOw`URT
+xFl%j-sa5*`A0-ui@!{=k~;B#*`K3_hJi{RTINwQb?Bzrw}h`mCuve)hid;RVuc>U_fp}zAt>srG#Rw
+ax}=W$ja&Ui|QV%m*EO1Kw4fKtMU7pZYT48!GQdXfurB&pAAVTQI>pWAT3Fa#>6(deaD;qcWAt#-b#f
+_{|HG&HY3Z8~rX%y_<_nf_T{F!Eh6`x%RnS{Zh&R<K-O{j@Y_Jr|phY&ppp@!~%k*UiH8$T+)P);Dls
+6UQx(&s^E`MY#H(@|iIk^Mbj{VZf|!?D!IewyhGkY4hq`hbBd4MCAzI5Tk`zOzFzlpF9n-FvP{EEfqD
+SM;VJ;!1k!d)bPs`o<4{Q8tJ1i!CNU^m0H`ue}RXuc5`dAGTiJVmBkKaZLuQ^zmlF>pPy08HJ%~opS{
+Xm6s#u@p$7dao@Ch5w`LdSi;<eXHA*gi)~^P>CI4txma4*-dJ5Tc3t7)Q%dBh~&*=1MoFOB_4>;uHu$
+W7hS}kum?Q>}j1=N??Nfob$D=_Tg2i(?1T@J(0hwG?PJ49B%L`IdGlk&M?adr4c;Qke4$+rA(<u-@n=
+_6n|CPQ6fK6#H~RZyuZ6xrn7x|n#9<QUW{z1h;NabhvaxDbWcKGjk1F%+(+f5Nb*UvFIumu}b^KXh<w
+`S;fUphvn~u6R5T*PV+3_@;kn`}eq#Bak{!sczK@FkXHY>{L6%&>beGACxb*i(><Nr@T!L(SOsUNlg-
+^;zd}^xd^LbhE*S_=AnUCKu+8*)yiw_Lfb7G?uoEsU#ANoDKP<w5_c(YD|Wz8=aVJHg>|=jmn{qIxh7
+2msqN&-Vt$P>)1Zn{vYZF#+0F@Mon0Ot7F|%r9Qo|9Xmx$L;i~YQ4dIo~I!F#+EI(l7q{9Idwqg-aZ~
+ZREy?|qkfi)0N_pJFbnm)K52b~#%Vy%M<tRT9${Rk&Ip*|iO<UB;HKGA@B0)^T-ySC1ut;?tLb!)MK!
+9X(<^L|`)jMYIgJh@!^;biTHh1w5i(E<bTpp~oh`D@8%xNYY=>Kt%t+L0A#;*rnD7bFT|^AFddP6Mf3
+kgjQ;;7Y8|ALMQg_^w57i39Dp$iD)2-PacLw=To&WV+W_Z$8L#AUOQafmggB6XbbcS^Lp^n-aEu#le>
+{mrV;=sT?m%%?F~rN**rW{I)aVUcD+N7c)TIWBP(<wPeaK0LXU$<NRcPNE64&TdXEA*RQ?prz=2G$(M
+1sC)_$*^c85gCm7^vg8MsJ2q{^-+S?=PpR8JumOI*<KO=BTDa1`MoLkT0HnO%DcfpRAPr-#`sgNyb1g
+BCiCuMvVeS&i!D9GVC4B_&rqLswe9|Q|&*5w@AA~`1s^mZsLwT7&EgV}UBr_a^pQ>7Bt7yeW~NSF9&2
+Su}PW;&D7(5vPs^VLj0y$_QvUK98kK*pfd%eA!94h{2#^_I!=VV?EU8JNgS(=jv$OVO@NtLdQiIC`Mb
+HYkWL!YxMkCg{5HY)PhD&4fWg<1kh4Ua(T>RID^GZ4P$b!)Ql6Zdt&M%(G8GXxEeYcm^MPEDJ^{_awL
+f9T?AYuNB0=7xxU<EfNnh+SS=~@}4fcrG{VSimta|GTUs^%CHQ5YQb%zQpC0XEDjLr1<l9U7D3L|o5c
+bW3!3XoqT+P;@$iFhX%QVX<E|+H!ep0^%YnB6QyoAQcj7~>(>AUoZ_@_sqVPJNWT?wXOs0Z(E$F!7rP
+ZLx{rn7$2GiuOhfno|1Bla3e5m6OU_*D}L(M*bP2Gu)xThC+*Q+BAX0<aSH}{x8`{z)1?~yWt7P<r|s
+NB=bta)7caz@aF>Krd-LyJV4e9kIOBDdV3)V!lCz60W=`Q(;0N=<is@tW-@FCb<%$)(VpV9Jx+lkj0-
+!Sb3D%@at?3FuJbU#V1%;Wf_*^#h9Qi%LdnC<}}-rfD13)M|A-_38xZOwxP|mjFaQ$kBID&5N<L3(*=
+wJSm(qh^Jdwyv7+}d*dTSu>XQl-}Tak!N^EZD~ni0DGvhMI>lW>8}sa8+l2<5#bCyO7l<$rF<M#iS4e
+<Qq`ZbEX=R7fC6T@~z>^s4Tov1i{Euewc&}F0orcu9<y=hBb`~4yc`$6xp!uK63P@}KatAi?BI$iN2$
+M+u!PMR_uz$R2<#OnAxsm5kXl%(>wyf*ib_hCmQRlXsfeIGIs{)QQporztmDr(-vo-{@f;~_`YB1)be
+PgFC1W=lX&<~|*Ewb!R9<su>wqY&(5}2=k^zhLgfKCjm1w5emVsvi-F-9ky)1?8rtKiO@R)#GkN%|^g
+?PlGzG>{Lh0pkAgVzw||9~uy^_vw&Y+2K+B17lGb1j8pe%$$4*_G8~HwQ5R+n&qR%{*I(O05v@qLBZR
+(ifv|ZMP&=?!S%3zLl{hfZrp_3`11isWebRHNt9$&#ZU?G0bzhuv-ME39-5<v?$bl_^w6Dp$gPKJ^-!
+fA^5~&zJ@j2Yw6X$+2_r7U2<Wai&l;mawghmu3!3nfE{KIz4M$7_se9&cqol51AEigm>Tzru-?A3+&h
+XP-Ck&3O0*6b?Z`DeZ@V3i0a1s=9=jef(%Hb01PAr$c^OdF{IkVxr>kJz;__ot}{af>oCN|v2%%XG7q
+i4$Mn2(g9yFtJU0DkS=KqbC+y@~_(ZeT789MS`&EWn2JUAY1wI8ugeu?yJ%WPJq!a$>GrahQGJqEYcq
+<SbUiPUMET4Kp6T4ych)$1a_PkRf*&oFlYF+4el<@oMj)If)L&blOMI?lR0y${uE$_BxD#tWIqPvN~%
+T$g(=<GeG1pl;0y2NRNT=TqIDI^*yb7SBk4EQ}o<@X_}r(FQn)97%fjiTAn?0F)gFZUnYHzxsa_|ZR}
+Xp`gAt~f67Nd_YgpzgCXr*(&suYJcu5gWVqy9c(GMZTAQR=dbf*1AH&7gde^b_)%!WH&f&vMVwRXI)+
+Bh*DRRT5hP&AK@FEAq&S$ZA7_s8jQO)cP&)IKGu&i>4Cl##w-a`jBt517x7emIwqpzaA9}6~{HCRLs7
+hyRl1V{2SU|>jFuy8r3S(l*~FX}vxBSU7K2Fkqa1JI_7t(c*2*@~GCfH7M6K}yym>^=Y(JchoZ_0=fV
+efxVpbaMyr;r~WYk=0B_?57At{|6{PsG$31eU;KRlpo<YK*-vxrcYL{O0WDGEBhu~OaUYo%(FyW=duO
+;Q_iEtl74leu;U;(INIDhoK{S#=UEO3`xfNa`h<Ny%y1s{hbwv9-JV36?WEf7I@6Q|TEFFe*SV(bg>q
+z;>)j^Gy8VLU^zwqkfe3V~ZcFY&?cOV&<VOt1Bp8rsYf)k96@KXp_g64g9Sx=10Iw_A#)O{YWhp$QTu
+<D^Q95m~TkbKXR_I6BoVroQ7W$n>TXDN1!dgB`m2*bEAp_!$3B1ex=bqO<f@Qnq<oKq~S(jS>-20k)W
+CaDq-)PrJ+==p0+*~I~=ka>wWh+$UIw!QP1wc@DZkoro$3Y3o)FaC0>0THhb~q>o5<Ul9T8FVKgtqGd
+C@}djbYebOG_M>32KLikuJmFbo?{81KjVRH>jKVK+qi3N#sb58^2KvzwgxaQaPoCGSL>tqr1FXO)FQ-
+`QyNTD--QG>U^Q>U6+U_e1j1I&4ir>}O5O}wj+_bfLIEZ3*LtZtwOS{=95?}y2M{fvpFUfjS`OT;?Zq
+ZT+^^GdKM6RBOh5;a!A~d8)g9bc)#AUz{kWXbuNBN?`ikqc2xgEbE^tOV?2FsC+xTJhnw-2?<lPl$sy
+ETva0n~#hC`U<ZErY7=YJPBCyfZ;_K5CFzj_s}<6LqK*Kwey-@0D!da(ck2d~$=eVWnj()a-OIGs6+-
+un18iB0sNb_H-{7PYTeaX+mKy8V&pE;NWpG|hLRE|91_&ZEl*zi@f%>jfJ4e8l2uhzWnFvHQpuAa_hv
+pac*1Y#iqc@}vE80F`Uy3Utlk#K}JT=~ue3a+G)+FkTsH<0+TaIH0nOxCtZD&TS`)YW>dRK`>P6!(b6
+K^qhXWydfVfww`~c)pi*^QTJzy-pvkN?RT9MC%8^a1@wgxu$4NPdJl5}0paILZnS`zp-Gd&9$eCjdrs
+mx6|f`}iFy^ON2}wRhhjdAUYRSldAe59yD5`+dfE%sF_6bwOJ|!<6ypqKBc6O!a7M}-dfQjd*M!F+GB
+vYZbHmRnp96d@AHnDFNrq2Dtd=x57=MM1;~ui)D3|QgSZ5^hp8y6TDicH$=mrjY@?<JAb1R@HAPm^+t
+8uP#8?GR87uEU#Pd@;{<Yi{D7vhr7V)`srHFvsHPoIJ>Wv=Qag?<BYhYr|TIbY)tdV<{S(4<S~RwK_$
+Dq)^Nb#f?j|D?_g2IQc3FkH}!g<AB6Fj;BL8if~mB%RSKNx6#P5c%lC>_mhe_v@d05Ngb2MdDP1J~23
+=S3~bU=@xV8mujju_3yRL<4>Bz@yaUelMbN`JAy3b<(^?BPpQJf&_Cdi8NLAmCi17y%Sf6{uk8gCB1N
+t~(M~U8-)>i*l#5=Y)0XNylj)<c*TCmsl^s1q{6bp`Q(t*9KU{56^2pM{lH`rbQmgCSmMMO8d!z9sDF
++j^Mm|&owIYe#PipM)8BLrdpBWa%lc?M?jU0)dwMBa}ELP@LqT2Q^A8>O6ZflX4A2iq2E~AfBa@>rAK
+yXM+GojXw8or%Z$AbajM_(DNZh5-8^5!8i%jhQ+JvC+`bFW^J@~*>m0`f&?l^r?odvtL>j3G3ndMz}g
+Isk1Td9GZh&W<IybBBRZtJg`H^vV)6B1x6iWt)2*631if)9E~Y3MMS2$_lXq?saCcxvSTip%RmtO?W8
+TZN<dSRSnFF`)?U|+GlA_IGE5G_@xUNcrq|>Ax$RIV81lLcCZ~U#;d@nz^ARi2HFi9vcN7!)>bpZ)*F
+Do(~J$I=pD_=KH%kr$jjM`mo-mMik%ynPdg64H}JYUoyd6I!Fat<soXOBEzCY|1ow~V|HSOSzlhm2nr
+3!_C7hz)$m}`)20Z+e5oW)Uc2b>j8|X`vG5c+d*%@G97|#^|Xrr=Z%kXG+f7r<N9lDJ&cApCDJ_XMp<
+mn85A5Am-Sb7n|8{XLk^rke@^q-$<TH-9@AN!NC&~}PLUKT>1;@d3d=-k45rsPBD&7(6a*qn=R!u@6&
+(3p~;Z79?>@IJaGley{KouSh|x(o_!*e}d5c@XzKtLzU-%Y5|uOv6vF)JLz<zlX(Q&|NNtKs0Idh3B!
+|bUFsYz?k*Ol$rIb4UX(^TZ<o@_n?%C8{eR3w{BtXLQ(-X<3cYYm_13Jw$v&-Z(5+_srjfR%RLS`;>Z
+Fq&}c<RnURTXGO1$&9~@q;cup#Hy&!!i(b>>TauFc&X<GIHg*SsrSBvK^HuJ8s7C^RX8a024!$RpNDL
+_g-(fMJi4ISa3X?}De(_2R}qn~7SEGe}PL`_mY{qrX*^V8|fYM}Kj^2)}gN<+WvZJ{j#3K@u+h0u9uU
+wMOFEbFK&EjCBGlkJ{i;XtH1Xss6~R7-i};9xh(U-@stN}Yo=*p-Gp^<VW^X#!oL*R<f1G$Hi<L(|mj
+$iX92sd2cXO%}!DKmw1BLp!P-vI8qjrNSpE#e!bKK6#@v?zN)fm-2`HY(C@?l*+tW$j-Or%_f57&4C5
+hY7!Iwwc|!x%b9GkiMds;)oL6q&BI)K4iUnOOvdI*XJwu(Tl5m4V@cjj1oklC8`^myxb++eYSuw#W=k
+^}=w%2xk%8u?Kmp96gI$&#<ia&{88pjFGPOAwTEQWv`v`5DEVz9`LtR4q58%^P;}Cwd2IDeKHT~hr4C
+?^bBDCRpBGhaa+R)4eJt(`yl`Yv?c&ln@DLO|Q`p^q6rZj*A>vtU!<Rcv073wg}n<%vX0Rr=KMSHtt(
+E{k>#7~$t*I(^Aw7xLBz=R%xIdaqtNu8E#Rf7Ikf1r6BfU}1(M@;QPtfg#yVF|vVcPR7@$s7PwtHu15
+W-VKMq0^E}GCOLt_9YoMb*8JKPs(5kkHa`=x^w8l3g&*sqOAbG2oNgwID`kQfb7%)lBu`djg2&`whj{
+@PhusWJ{$mOq!8*xfe1Z_i)}Y|<g|5d%2<@x#<WGZ@O+mlfIYr-98}(vceC((j=SXrFW8QpNIS*}&%Y
+)dP%s$kl5**De@A-4lhNwGE^r-^td?V{n+OAy*FckIE^5E+8k?LnZ_er%M}+p<@@6iQlNKozx@=L&F?
+H<Vu}6cZTk$9(4KSAk2bjwx4Sh!_&dar$)Cc<X$hj%>298K`Ql9hmxF#jnoW#v5P@{2~YHK&HTX{}`b
+P_^@ZG!bDWc^Jpadx?4pp9E1P>`}@e5u+AG?CLN|Mx_}cA=n;_wn)>AdB~rfzITLV;YqlIcAn4W*GUz
+UPd|na&*`gT|X7iE6L^AV5LQ@kQ&TRJcQFK5K$ZE_BhN2m6*ex<hN4E3PvS$DJq%OC^sjw#Xkmfmp3X
+Fq?9C53Vxh{kC`O1vohndZ1oDHoJ<`!#7JitJ2;b>a}GT|OD_cQPZ7{WB%lev(_)@=AOi^~&#Y!O5>5
+w$K>?AL_$gY!48Ndlm8%nGxZi)AmYmOjj2pv=AWoaf9|$2lN-N)Wl1r={W~*<gMFky!9$Y(l4Pe<e#4
+w2K=8tu8{VyMn7T3=sr5@kJyQVN%J(2nhFxN<xANBy&M@bCCbV@{@)_sdfrf{IL)C#7)*r|J`dmNJ@Z
+kSC4qnQ4llrUBI)zZp3!^sl6yVPnD4L!S-{`5@Bxm3M`zS5JfJKn>}LC?<Af{)!;@VVw=T<U4yYw7m7
+(e4z??{jmyUOKJE&|iq#F*51r!=#ViEqAixyW{B10Hq(!I`t9sTBlmgB<6U~ahk^X7@9m)ChB%_Jnb3
+(9kxGeXRt2<?D{-v!jh)fD?Ir^{bO+a^3*0ixvkiad#ItK;`hOnt}N!;BGUNQv#6*y<;fMBa?#MVCAR
+oLe<x?;v(~0-2H%%IwuttUBkh@T+$1MV(f~OW?cps^d^zCtn*29QG+`DqPCpF=XX|jEAske&ItEW`Y8
+qeS3wxe3d{Be#V9_cct;j_i5Lh-U@kkB=i16^kIDhS8-rQ^kcO6`RogA^r)#tRvxLf10m1+xoOyjaU-
+IqeZ*<O7$DPtbz5qH>ouC<dbEWX?HMy<cGyyg?3Est?Gbw!0X3w{<dmL0Oh4j?2RC;xD>nS5Bzh_Lyd
+SDt_uVn`pe8_SN%WE-Xxh1*$<Ob`Iq7&ysySh$uurnSg-I5{0Zcot;?a#zE23n*FM1f*O?nvTkGt6s(
+C->4aceL1zz@hwg0V!+mFSE9z{CpAMIS)hH0H3Lyz%7HHH*7txa$RH3ti32A9^;_oX%z&A}@bC<EaP3
+uAwsjh=vZuX@gf=t|v?^ZqB&~}z&yeff=7*fe$!RQtbsPE)XrNX?-5D)Qtu5*gJFBd%pjz011IyLy@F
+KQ|DM!BY8f!91S<}RYAx)g`PvhofR+KUyrUMB}ajRK47g*tw{~Au>Y`{m&MzDAWa@8XFoJH6kKx>4Jx
+mIu(S5|a+f_B<KT3hzf{AIeH3Yt*<2cx8kTH}W}WDKn7ofM{cnh&KJE(YU5mzP>Uz)1x%Ok&EP;Uucd
+lrWwgC^~O=cRK>y#~{hL4@OC{XHV#|<rgPLOR^moOS0_-*bmcSH2=i^Mv~RiUwOYxP7Mr?l2ezAlsxU
+kx5_EyL`qKeded^M);J|V4z+x2aUO3uW0u@cq2(@?gYn=e^GlK;ZOd%G*Qalud1+yVeuN?}36QTXa=#
+$=oX1lFnaq@aU0V$qQFT``R*~36<_(x8=rdBUUhSis^-Xt?9jcf%2xc~_9{E1{;#s`^qU%;%MmSbKDT
+3pLV~`&x!ZFx<JiWPnB50zQ_vkx<7r6Gx+$h(+x#B9qG1ojFG$Zs5nxM|33zfQ|l=B^_whu&Q@#^wAU
+K-=2Hz8)o3R5-c-wEZ-yp#`$y(oC;)kczJ$?}|lS}Cg7Rcf14BT~<ZTi`>il2~d8#v*Mr^ClPu(Ly{Y
+==r~*SAvQD>_fI8X<~;OMxR*l(eExbj4<y`MwG)QDQlnwRSFZRqQhos@_@S%jEJC_+LxkwPm<JvdFow
+pTqYdEWf-r1Kf#EmL6Xp$nvd4j7zWP;G+*BdRK@{6+x?WjZ}0}&I$<nOjop9#{ZTBi=Y5^FA9;T?3$$
+I#0*V24ZyJn_dH;XM0*}^Q!~#J4e=)XRRDn(&srVa6{@>nDk^C1mUq2TkP6capBG|^XtW(ok8|{Po^$
+1Fa0(Ntib(D`b;acZ1pRFt8O%97xOx*buMYf+!5DO|U#aCX%bbMu2aPCL(C`SAPED4uz0+W4i!*SjjN
+jcidAM1O+Lff@C4J}<s>Ua|MmpepIBJJT9a2eJWZ#vZ)Wna<tR{d->?$;78A37>!IwOg%u+?X6l3l)o
+M~yzw%OXtr*<ey*uO+MQP?p|7s;%-FUI?L%6)dhNTUN?z%6U$X+FQ?H`J4YqII?9;1*okNDJx#J2DwM
+wQt@yqj2n3By%j%6g>ih<RTV!=h4J{Hy0jvk3gcRWI=|w_sW4g`>Qxm#O@%u!Jhfu4*$9U*oLBK2hBG
+0I`m?rSO_0}))+<}qfMkINF2lg8J65n&lLlkstGwbE9$>N2!Kt`q`>L^lcOdzWo9oD!SF{my8}TY2=;
+C?Pr7UMxNE3EM$r<7Kh?nlTML&FPs;W}o2GGo^jttO*zG6G{GAkHMerzfp3@XDTC{Q7u%S94L`*2=`X
+j<%}Cjg*6EM}Nk_RoUW#eVt&b`?Q=y;9MK>wcQ}x?V!9>h#g4Aw$djiu&+!lQ=zSs--vJaiw|%H}`Jv
+)q3gJTT;6ti$PVeoNk%VNryC60PTnYE6v1DPW}!Tw;cvFP0XUJU$J^sOe@0*=^6%wpKLztP3`I2_?Oh
+7<z-;{);c3TC6jzf>g;heeDF}4n1#&k;jPLz(2QE<PnU3!&NKAvbYVxN3KwbTX_=tm@f{iMW{{!wj;X
+@)F>e+1=qteDI4u)TqKnx;fE^i{$KGzLf(oFzw&UWp?4aLME$50vTg$mD>DN$_wR54yA%{aVg|-Bsfg
+>kc=VeJ5RqE-b>fc#Hw=P3DK+vNj=+I2g<BQNIzG^|s{FUMv#P%{=t(nHom)gTsV7O10tfWuPMSneL1
+~dZNa3le?;Mf~k<>r+Op+Q~J7uXgUL9veE$O8&D&&v`QhmMOC!hwhvfM?J<YV=0tc4R>F^u}qKBP~^P
+X@vn|fdojZvsWd2Ad?__W~Z6M9>WZdL>@ac#IIU`upk^Y)2IWR&f`9R`4oe;CrwUm<nh_Kk=H)Oo`ag
+vG4ugPl2^(&pa)k>%!IbhEtD*ZG(_r#(A=!84LA4GY<AC45@@OdmwDIUgx2dBZT&)$jDBhkTLo9N={h
+fMGBL*h*J<%`BfDkT@52JR&Zi2f-qkyd9u9OE1)6Hn7cobn?$Zi-aiTfq9B_TeOq-33<xbH7wsT{7sg
+yL{eST8WgN}<nUrEe&B<c3n`g~x7Yww{N-YL%Y(!Zmofagv-#VdSZ`Xs%Y)DKYFdw7WqdIK~^a3YX~Y
+PEtBSSL_tmhqk5?>Ek|;6AK;SCSE0t^bv!pSWLq5%2qh$uc(-6SD@pcj4(L^)4T_TPx^i&Pzj1DXA6o
+VR?PJF9g0%vNzt#p%qY8Z@<&i=j?M0N|^_4fs$26=(J=fow;Kca~#-5IeiLqVjg@~DZ^UWGPgW)Gg?~
+4oZxp3pdB|eg1f`@Ax-*Dxy=E?ykQ!2XcC>%w^3aD(j&3kNgwJVujvYEx)7%IvQvP6$C6Nt{IAQ3T~w
+A)(%){<g&7nyyWC?J|Cgmj)2ed-^@cwmfhqC0(tzg<q<3-L8is<F)k<@Zs{)m_=G(|1Q=rNx?-4zz!x
+W%${if5<#w?(X$y)yASr?v}_(kd{1fG|;=fJ4*5{uQH7kg*49nlr9iGNA!Tk^>{z;6Q{M~jd2DGevu8
+-|sJWZZLDcQ5T0vy_H+Iz5_0&5nECK{46hpoKjry!6v0`k9c>hZ%TnBpA>kk3$+e=y7oFOb#!|I3ZeF
+YP1&OamYQElGqE7Ir1lXw1|tU#aY#25xut?7klMAjV|j>S^i(Wg_eKfL$-|6uTBEoKrUmk929#}cO`e
+L7NxGAc=Ce|bn5&r{H}oC6!<x0>Xvqw*Yz`LcT$}i_ngFKR<!^=a9LKh!v{NSWKkMGzj6EtkGIS?_yV
+rX`(MEc#TDJ^6lZ}<DFzO?qyiW4#oH?IqD<+^xQDV*Zi5fJNKw5RK3D|{D>xef&Z*#_M<DA#@p8KTty
+Bx|dn?t#ajb=mL*pL(U~49PFn=+RBkt+L#fVT`98Yq4K+p^xnNd^N*0m|qIHmQZTO8Y3gLf}lTWk99e
+34vp()tu`Cy17;=DsJ3#4J6XS96|ph?#o8LB~|$i7idZaK)rc>6dRZwN#l(|MDiPk$4FYeGNVdm$*Y0
+&!l3A9TCx_zBrp_HHn5jhd~mZ1Yv{>0`jT{N?Qja6+JE@GY+{9NhL63CUIu#UH!Nk#j-;#$out--zC?
+5oAEiETKru3Ci=paGR#0cCcwIaB70Icw^aE*mVwvijDM5evD0JN1Ow9V=m&3LyV1o(eOx!#YqYe%UZe
+2{eI4BmY6Qt3-Ez;gkyHJ+=~LeU#NB0TUt+d?Egh1>mFD7>qL%iW^DTFsuWql=q^$$d7NPA)Gakxyn8
+kHLb1jsAd7mMwT<4?-bS2=_Iu~_N(F=+6OHE5?Y<2M%hb!&JF!r;5F#8wUKCw}5J~mMrN8*p(iYXsg>
+Go+%JjgaYuA{G=Vz3aiXTYOD{j;F{SCB<tK$A1n>;O~Nb7DqZvSrmcRIuzEK!h{muBcd9fy5<V4thPj
+rdQxeHx@GMAuGppek&(U?eq+r)Iylq_v0}~Mg`drXI8E7<P7E&+MdA+#x%gg4Hm2rdQ4xgW`bcxv;z5
+B_X(_EQsRfMs1Ul}Shb1^+wR39lS%W2JUMCIP$DO7LTCjZk38TRc52l^+f)ct3!%j(+}`cghC$&sC&r
+>Tk=gHut~gc9)`fEpiiiCy;=^KN$J`YY+8%;l4qC#VUhK#AUZdB9whe5N*DMBsE`$(XpW52od+NnkIB
+7YyI-y@9$Pj#k?I)UrZQn;vtfW==kzc<$5-a;*s&=6bO>7;5m3riigHTflHUC5=(^h(C;$;{W!|;$M0
+$fiY^1$rZ4H2S9$;4`CSOqpxn&c0f5$2z1oDYJ=!n%JxtEE#<QU4Eg3t}F$tRS(HxHnu`!CJd;w!VIf
+TcJOKtd^wfqpcr+r~tag!@R^EW}F2Sj&yA*?%=Pj8E*M_YbpP25V*JGh$bA7k272Twl({;mge(ZJ1<?
+zr1>Pwg|O+2dM;}VUvz;a*}^2rrZn58WJ%D1q6A21CQ2A~e^3F6Dv&7YJ(HQQY1+5e1Crr7x&+9qKCu
+&T#Ji5XIjR**f+*HJQ2G*eXElvEWYO#{Fdu^Xfp(nG`@4*F7`*DB(75PK8b_6`l$5`+i>+YSgX{+zqM
+0|YLMv~cECR#j&)hn!yG6Ri*Oca=(cUgdgQSzLOd9v9^o(}uCLlNCpfkgavbT%5cSHT<&AsBR^5!1BX
+Yu$o8wjI4BQ2&C>4gAx57g*$c=S?jw2uv_jm}7|A9qaJ@FX!9Mxo=<MEc~R^qT(7@X0nD*coExcOZTQ
+s%n0?rjPFXfE~4xH|2BEf|8GfeO8y8T+oy&CvV@#gYF{i>r>67PuQ1<(K#5M!J;E_+^p3&#LC9dfOtc
+rt51xnb5Y$C_F262-z)Jvbe|s1^wNha83W<=6w3zr#ca%J!<>hNeel1tB-#R2%_<R0kEDF(y7MD`78O
+fCZR32>{b=SvOPRMWTdcc;9*Cz~dS~2dsro%Icffr5TgeyDqP&Y*)D11_7WSFG)}knD5#V(c@#+?146
+lPFQFUC98}V|I*}A49z2}dYf9d|JXhQN>MgbI$mt*EOy37&}1<MD`>L(B>?8`2Rva3#Oef~NW{sDS7y
+PKEJuI0Ffln>{{=f|#PN8750oQG(}9(I1rLA<kWCEns&X+GOm;lFl#3=hRs7CY*^!QnvP-4~yQ!+S-o
+y6-AL&BC*AcnVGvua`F$&(_2YmsmV|!<FGV8;Jc`Wl6CekHKwz7N(I)^mA~P&+_y}&iGi(cC8R?!yDB
+&2<MEfAuQZ_zZG$i9AufD+-fgh#!EKVv{>!37at!0t0u?zAXwMUyt2ru++(j>=3QRjP`~1!mD9Ro^pC
+GIFLFLOo<0Yk^#e9g!8x-yCTI!(+qr`l`f-d&QTY0=KY}FC|3QEI(ccK<9ckuClpL{ix`sFKuB7nb<A
+6o$8Md=DIkmGi&hL{y735C@*YK*<k}YxXLXI1Zv#Y@JqY1P8H;a6RZ@T>SZmsx{Jwd=Z{wGY76V@g>X
+wjBKT5;&r@58tjha-^uFuW`A{SL%&@KFrE8Am!Y+A}+<Ozn$J9aUy1wtcZ#?zG5pR`}s`;o%!cU>TH2
+?nFL9sNbfc9@b&xPK3dYEl6!2UB-XFYX|(c!><i~o8c$IZ!P@RU>hnQ>QyR#+zW3CZwl{|@IDFeK6v-
+RyC2^D@J_-z3GX3z4~4C`HpW@{EXh=FXxQjO@OuY-6k|O6s?}YwhY`ck-+Jqf7{Kv@9)a#i!G^_ahQg
+c6hm0{O9vOq&^QU(YLA^`XV!ea!Lr)Lg!`hn~lQa65l;D_n?_pztzz^R6-)-=F27Vn?mKjId7hB}dS?
+`@<z4ymaMI&wo%*a@M4v=gV+DAb?JPHngM#k)Oi+l=E79Pa4mJ7?~;Kjqw0lz}{6~S*7{7T`sXe}F?$
+<Wu6;av#tLU`xHJ0IQ-cst;2hqoQxJiK{$Tj6cJaBTEGS_#iJ@Dt(JzIN2uR9OJ)QDecv7mmZ|xC_Q^
+WJ6gTD^tF8?gt;B|CE<*`2(<NLwX~p49`p9RbPJXUGgJTB5`TSi-G;41J8{PL`DMP)!w*=N4ePgKr#Z
+nFJ=*oktRPdgiCshtf_F!q1Esb6z~qf+ptK89dTzvH1L`_RO1n~m)IGR|4V3r+{a7LM{Rg*!>v%~aX;
+7u_f;$h3sVe`ZHO8~tST>*ZYSIJ>!C{^G|o%M{T}G*dCWzREo4VWwmo+Nz?Z*E11x0#+Vkva&tFI4-}
+)Up5W6j+XRL&bZnEuF2wgdhh|=E!0NIAB#w#c?K|Q6vWKrElD9j4O{Pe-!v5Asw!-iHI!fu&V{tLtjv
+AJzN7>Lhf60K*ohPR|UajV{mV@M_-kpoBoCCx#g^3_A)ShCnU5<LZ@<v8opxR2&x@PaW$eaEGf<{Td>
+bq$KM{K{M!L^n@78RDa#;}sC|xq3-x9cD+qGJSMC?%$C3Dz>%6juk(N3gxa>K*9U?5N?g^WbU$6>%{5
+umbIKXnZAb6>(;X4az*s+XV`H$`ur=nnq#LkUrikwEJJ@>xpS?L*0U>GM;*{%OBM$JdTI=FOTDoxp8z
+2SRZy#ViA-}vd$nkBMq>N%&i>f`vFwRAhs5@ev9g6anDqPD1dUhkPxgE1o)2-K2UHmnRkm1_ExfYD9{
+t#n6Af>1JpJ_3FW@0Lm@omj<jq$wV@JNJ-Qm7P8Vqlnf-AZli{l|~sWg~6tkZgOvh9MSBmQuR;-;v!!
+KxK-Q8&kyXmCq^h%NoMx3OFJ-L803e@EzZ0PPH6L{VErEDb+)3L=E(4++m(T!%J}FNwOn;T^e{9xS?*
+{__Jir*|wefd<(Yk=_M$Yy<vn0(5TNVD&=X2<m0GqJF*z*`^laAr?l~pHB8erdqmlA!;X;2Efp0fB?H
+P4uLi^pknN|?H;TKT^#aSmSjwEbZoa_vFaGyZY{v5B8-B9+P7z5hpCzEzhWQQ)>qV9^=^!ZdZCzK<Ds
+3d%h@-11fSuyJWRH9hi@;E&rR5v-LfQaiX*(NNX?bcPS|M2U{QEkPdixsogBBo^;T17>W+_dyV=<gd*
+Be#X6DO4L7q-rr_gD0@j7veycKBk60$L1kY)E%I$2J88Obu2J`_!zpWGNwq+VAU^9R2U7-OPu)KSV|{
+V1i6{vPx1L?C5xfG}vt=DYA9EL$vl2*?*IxGgej71L1*BZGFw4M#LhnNo-sp$`vBQ{y)LWNTLiJkZYE
+h9S{om;vfMdHB3Eb~K0;K-A;%@Q^eX?}>0*M-tUnF>fYr1l+OiJn0-+UoX1grWiZh!lrBVlTHIlGw&L
+f@<@#SiQOcg!26k?VPJ6Vcb~_*jBz&&7k4A26}V2<^l7U`&*K><5%otc!M+3R9;moiE3ij(e-<C!6y>
+2ndyFYC>brO-3K=cDXtK{s9q21?bRl|5JYZGU+so^yunl>prTKFx@@@DEOQ$;W+PZ``AOKEyz6v;{EA
+bR}K`~u_#nWTGP|Q7DrHa%O{x7dSHfjmX)WBqy3(wCNo{y-|Wj!4&^B|G0bWMTMOp9H~4WHp3k?1tzH
+eNRjoaX6~J9)Xe#|kAhB}g1_i!VZ_3yyvtk5XqSi|pY=j<8Y*1>N3Au4z<C(awpOZ<4+sr`Z|I#B)Fm
+ElVs@tZr9y!vGe)MdO4}DRg>x(bN}kMrq-B_l?pSm>%gE$O5G*wY+Lk*7JtU(nZw+D5})|C@;ft7DBf
+TGe5QTA*nBa!x$kyK#>uKUx3t_9*xe8;pi&OCn#Q;Lskc1%;^vDCLa&)T4WX4{5TXv!1Q$Dr^pw_LdS
+)+Du{dmub~2#Am>i+mS?cPdqAH%uS@G5TuvWiCg=%v2HfD1F{CO`q4SBCZu>26AFK-0l8Qa-xaUCCI<
+pY^46+T_kULF62vj6IP@Z!j#e$2hyJkWA$Rd8%Z1%SAD#g3aA}~eBNn5j{kt|u{XjJT;$q?;UDNCt9B
+52O-SW-B-y}k6D8xj(F0&+w%2&_VsFzxMghcN9Ejd5)n?Qx8o(}=&Z8xfy^akbie7fej0fNba`WM_3I
+?w%C2p3`&H(w}24dhPzS2cuQKs|Qy>=<^Vl-l`rOJWqW%v&L=5(wo95Gn&W46sPv3&H111Gn^4GxVG9
+6W!HR|X$DrAy3ZNy<tV$rxwyk5N0Hu_>Jic@b}>u%iQ-W&V)_%io4H%=)0_w8sP!p!H8Uj(h9Kv)ZM|
+YKu$KmgAaa=|n$<VL3pA{yBpM4Z7*2nkPk#*=Uxu_P9+5Wq*GqtEh{x{p!>G_N59n^Fa>X26)=)fDY7
+Xo<hC}izwkwH5=s**@f}IV2;usqW6dmlK1v`EXDq~;=ChWpOwD&OF!oqXY;WaEgFBRTZnZy3<iX*T@F
+A?M15VsQ_q=xFpq+L;Qad|GniZ^)iurz*>Bwno!n$+yjad8$RBTfj{oLCmLgEU_jG<mhR@tog)Cux$h
+T5BBm=c-iaPQ=CKq2lH2B)K?U>sCA`G4IZKMv9=P@VMuIiCP(J55_szl1ncxD`FyQVrhmq{aU}>D&>n
+ba-^n!KC?HqxaZxgZ{dJ4h{q1mBc)NvP(L8=Hp<s?VupOse2df%t@^a&2(H*D)@+brxNu{+Mfx27N`r
+I|GSJ)ij<~7*r*7)h;Z@uDKK*>pQOQwW$IkaK%@g<0b!5iTX=!9)&#{%p4<HA_!>o3X!pf|^Lk`O4G^
+vn$iK|wKOrC+vcK{i6I{BjHOJScqqfy~;e;Q9w-XdCVmQ-|p#1lMd%!KY|r-HE{2dS|Ky^TucO~cC*I
+L|>P^x@~yElC75<N8nb=Ms!UrLRgn{E=jJ!?$$qEzhOebNbnI=i(tx$%b9~5qY<y8~@q@-gc>DneNJW
+uq%ZT1k{&Cj-PItDxWcLvCC&HQvOc7EVd!WUP`VWpzl4KmJ9#-tRWW$7TDb|i1+ir_1cZrO*v#tyIC{
+gBxe^6M3#A}7&p#xNtUdPn3iE2OdMEbcl$^mL0<$hSAGB_4`>W_Q-(TU{to*i1zSp?Q#v@U#!g<RJJ8
+kB!^`tEDNnxP3P~WZ@410)(b(PJ+}6L~1ravl8B#J+$MRZFwwN!vn$E8Ol?O0La3wb!erD9Qa=q+tm^
+{gzee8LNJt=!0X3r<s6EB&gW-v5p=N~crD+q&TS3>vwG2K76!)MFO`ymDG&Ja#ps0*P>(49H#1eIG_<
+)cYZ!Rr5nhoNxedJ=lnjwgrYkEfBo=&&vNafZddmz`lWlh+0o6mOM&iEtATE<d$i&rUjq4XA?y?&209
+^h2b;0oR8@+uiWhF;3abZqwV#ZqwWQ5_>)l&-O=7!L#kC(Duh{j$5QWhBxPxGhku6g0}!K?3uTGqaYk
+OC_LX%^giI(BQPNl#zO9iWC_o=@otC@@=<DCrGzlr)hV={LbD;W-W3&oR0QQ{#i3Vzgx(f%7&{btZTq
+MmJv6G1`_g@!_%sW*tPaYwNUWBrMVwR8DHc~(i}?@`LS2xWwHLt?2jEi<<EJ(8$Mm-;@mu(AW#4EgCw
+`tzGA9Upx17B~nk~nz`rwnTL(0pD39zuTcBI5XcDZ2Uc8DPi(|aP>!hzSqm3+63dEy2H-i__z5>BJs%
+PuUJ8s+A*a#<H7cvd34s6-4W{>fu0H;Q#FX9Hqo%h|PJVawT#Lg+7iszK;hp6Em{*IH@(A|n!HzCMPD
+r}<O^9Eo42zl#z-gYOQ@yz8W?Ef1LHNQGd`8F^dMdG7<5c}@m#tq!ppA@Ckp1EK~Y)(CALe1BhPtHAe
+tLR%TWC86y~d~XukX5xFf&^84z*p0LSFC1Gg975*v3eO+((kly?e?&(JiLqXsPQu8NVMd1Zm!Oe$SPa
+r^gZcLnR3k@2+>dJRi<vsDB-aQ(-iH=ITb&uV7;A+eqa%g5M5YTw2M`Yu5MNy_gq{SQq~%~yoKYfk*I
+>+$sD*rkj0{TwYaxVx?s8P0|Ck${EM<=*5jL*!8BMaWzl;DBpaVMa2*3y};PsY5+c_)#fd(mO=N$BtV
+s~B=_KY*)XP3-&S@G^#=)cLJMFHMhOQJ&Cv$%H)_@he&VWoaUMPjM2Z}E_BOdEf6DjY)Y_tH<xjN8h^
+X>rDX%w!wav3RIGq+gGD%!t%8>bNED$LccER1MkZk!mZ<EoFM$*aN~VoDxQ8G5=Bg!%L%@G6$QY4}D@
+5{;k8(bhEuQidPjRaPdS=t`mNIof*e(Ivc;cgzfl;59Q?D!b8upP2=y2o8;uZ;uUi8J|XmXNE=}Fx9E
+ML%Q$hZF)UDqdWRgs6f0pbvTlfO7^AO>+=Pn?EO(J;ZzLfkC!9ff{Zq*p>7c}XTe`bh(Qdt;vQZmk5r
+a`=A))O9TuB%vi<87Zl*lq)luKr!fD5xf;>GT_Rvu&borZn^&t`j^2>@JxQ-ji&ma}(D-_xVmDEZ6T_
+m&J@PPDSZaQ|pa1=-7DIvY%QE7Du<1J^Kmu2({j>*M7xZdTokHIZX#wq7u1Cz-n5DaUyw)Pl*2hm=qo
+@|e!2+Fs-mU@(nUqtgJy{(^yEj#{r)=KhM^Nz_eV#<t3@PHz1a1+O^8Xtk7Y6j!U2Eiqv0>sww%Jnxn
+Y@n*RZfa(zd*Ok&3V?a%8n!<L}CSGN&Vfch`;#RMa>3X3}1OWjI(zXH(RGcXem{NlfXA?Xgf-;CkA4L
+31svx`ozJmc+mvx5l%GuKP&|g?CoSM-5N{kt11MNV;aLF)Zy@6<vSg3c%KooQ#;g!WhsukJqQFR>jBS
+u;tg-P@12+*X^(XE$kwuU0>#|$2mk8*>59ciAyP7edlXUQ?i?9`0jOA!a7go!ef$%FbInYe%|NGvBVW
+n9XT4oj!?DMn+$DHPe7fyv^Pgf;xpTfP2TYR1107A;jC(+Pu|mCuxK$*{0KZO>b(z8v<PF6pc?5JQp;
+>52qn_3trn^EX?{OU30H<9@=Pou*Q^>b|j2o(t9~=%Jw3ep$jO@JLdf#YA7}MWRp5ys-tKZEmIm>(t7
+sl3zwXgg(dZN}b)X8_QrQ@C-_pK3rF-6LE>q2$iUITpb*vhl=z;WS(Pe)-GWMml#^dFIb6oX(makQ%2
+7&dx~zMg<l~C7}7**NZb06wik1emr?U0k1=3Y3CLJBBTf7&C0F?Rdhsg#(+Y*ZjP<A!^rAt7Exm=l*n
+eT+f0tioQc@ovQ3^%2{E+O`yW(Nw!F?H87(cwhjIgI4s%*!A+-at{rG{jet}AD;;#Jz-_KPuPc^I1OS
+=Cq;S6?svR-GZUOP4iuqMiZxyN9)nL3`LAiO^ByHeYKrO1#`?)Yp3LZ>KNnHTh)}L}1TrO!uUpf=(~*
+W^wg|P469+cBTWg;nlW`S)2nM-HA1<|C#mxmIC|86hh(tZ&a~$(VaF@S3ZnJYWPNO@m4oi+3e;v!0#y
+hrfzX_SHN!_{C>05t>2-sY#FW)fRUhhl3-E!ePB1xzu+m#x3qd!TpB~){%z_Y35k>U)SH+O$Ivq7b<B
+>Cu|GB|jY}Qa6|=nb&$teyzV+>A$Dp!0z?ZF_@qcr3Gy*x${}BIZS`WMXe^{DKgTK{}$J{c4LyWwg*v
+a<5Ht{Z-ag1i60r#CW9DQv!4ABwh@DiTKj0MtX9HRl{e#tdf`D2sV>7}<$yyzIs<O~0Rk?wY(cupvFJ
+?aGd=iTWVaLo#xBpJ#V9<Vt<>U!w4fDHSiD-TUfd)Dmlg>M?Wa<rLd41nJW^gibwIMvK$&r*1nH#^vg
+mfvUKxff{~UA`Ny-JtV#rwy-<pU`h=AQ`EHOT)F{$@;+|i~dI)I+n~WHg|QOQ64LXGTXy9z_aCyS+up
+BF-hMcHIBg}csO7RJ^O^-j~_jeF8)FIOiY2Y2P)@Ui$nqcU=Ut|cYw`z4j-MLy8r!AocS#Sm6_HSp=~
+=ZZ(j9#piB2!XIFVOOmjt0tFR;LB^GT?Ciy}uD2(FOP<ZC_4uE!jA=>9M)Py!K?uZoRLpphkbqUMtj3
+}Ym*eM=|ux*A7rLy}UID=s(P?SS=<DO9t*;~wjTh3&on{R0HKhW9B5|=lDq%G+1&S|gfZ?E}EzGE(jC
+(rx6>dnG|sF!wU>E{!!Ct<9!<aJAp!x?eA4hh?KGe^O2sPZ2;?bF+`MttIa3Od{(jUl2=3YuzlATKQ)
+1%tOt;6an1EnL&TENBXc5oQ@k8*G-5TPx^^==YI=b%EDXfHsP)_4*J;62e9i4!TaistbEO8#PpxnS_y
+AEKwzdv{SwpGYZ=WP>*~W+YggHIv}RYM0e4VLOVVExfc}tUo0a+nuP6pAfSb?e2tZ!z^&_fCeb?2EL|
+$>b2_`^Vd&{7`dKk#%*tMDpAyEJ4ZvG97DqQ-0iE3FVx1&}Zp3<6p=JY5@$z8qa<9VD>CG1aPiA<`Wq
+2SqAg;<;eZqrh&ZBjSH(%s+jMHJ-ZxiN_Jf%{Yn>c00zrTc}ITuNDCj?twMw$WLT`&4c<XT2)<uyQC=
+aG_McMg;=LI{Vd;pwVztiP;d+@b}_V>oCnXS1ao9gj{f*Im`_a96bsYvVYs=`W;D&r;Hd{XI(`Ll*PR
+!?*~mV{j&hx~o&z?!vfO2@?n|yqRDv8qhP-sGEh*8y6y7jn>0%><_?Q#~eu(t6hc2FB0~RvwUdz|Jr*
+KfGDf4fBdp3u3%ZBSx$;e;WEH5Gt96I0xAfKqN2GV%AhElFyqo!1p`VNHT||}X_`yfZsk&`3GSM?q?M
+_an${qelp7^^|L1d`=NSeBv%bCW`+NUy51hI8x$8OSp1Yp=+<Q>MzmV{6=duC#PiiqTt;EMBoZWW2Y(
+NsyZ(51B+wM7AVZ8~;KKQegrMuipS&B;?%KCz`{vxDQ*SjkuS;g1~uQFjyWc@0WWwbpjgniRpE<)<v4
+uZ6n5>XA3vz8jP+|s~n!-h>9+JfeN^D8T;Le8I$Wc|v-R4&0*mM}~{c0<(6<5*pKUam!gb|PbU!u+Bj
+M6Qnkmga675YYHd{wEdk)#Q?BlglWZAk<GLTCcF98O9^wgbQNg)EB-1A-G2GNcJ9yr-f^xgb<6kwbL&
+YeF(E=6mAt^EIlT9j0qN1(AwJ0mODBz&*eC+q9R<bp3Rq|<Mvl*L1S*Bk%_>uK;KS$wd89reF^c~BmU
+l(q|d7N5&|}Xh@YqwBPDI|82hJB2ql^K#BS*3zk12(8`*g;$=`p!blcCw&2q3<M=<-3%kJzOS?gU5eI
+}k&<@A|&<uXUgRm&RuOnlr+$~UuH@2a`BP~5vBPCK;6VRN7@-m@eE+df(p#x(cb6CsXrs%S+o?(-vBl
+NUK*Vr7f)EKG4{B`(hQ9o@8g*~dGO!R}6{Va{Flel>VjEF}(nbL$RfqTYq5H;u0SftTZ;&E{B$P4X(%
+ib?Y%=+!nWtFb+Y*+*;<pA_A+bR1@nC%f~RR-yD>bEZ{=t8K@VCSTwD5Ld4vJ40&ix5(ckQ9|BM3Axu
+v=*s5$6LRq?S}qbgy16EyJ)3J1+Pt}TLT}uX&^42Lfn~JVCw*m-2M$V2u-iK+%Ri09HIfyh18H1UIa|
+#KxKF>!Mx&$9D|0<<E#;%OS`rIGuMA7Dzx9rCB-`G;##xm!52xY0TvcHo_nXsO*jhq=QQ}hA$hD#c?x
+3UyZG6T4CuJahv<Q+feP6fNg$6+${h&sErp#7Wvoirjg2T8h_XLmjws`9wz~%x^;|`0YTnQ}2mq7Bi>
+4J4)+jQ{o*4784K#jaMwxm$bn-6dp@h4&n^u|UBcI=@N$8*fWmW8?aLZR?im)V`Ed7Y^W{4991r`NOF
+2NS;r4V%=2=eSEJ3>BABXsB|$jUq~}N<YTMa-XdLFO->AQJOe4R_v50$5oy7w_Z?=L2^b0_1P$NLn5b
+AoN+`+Pjac9n^&;`jowG{HN-We@6V;tca1QUJ2)}*9qikQ_O!)U+DdSS6Yl=(jMXS?=63Am-nYY%qNg
+}s7FYh&oVPm++nbw}TD#fe#4R4p13$a|v+X!`G?Q6r+i^XW-1pjc!*n}R(U<I_$OXs5P?#g-M@}BOaO
+8Nieby`2FWV|9RBH+m<?DU@d$%LGEsYdznrE(;k-|=GGQf{K?i=FUI`AafJjvGl`ibku3r{fHe%n#oS
+IB3~c5i7`cFiPoOw&D?rJXnzs+Tx)`a^LGs<F)KQP>h1>PDmgP_-1PsFs3AV!Sp%w?y5R{W++SqCH&;
+!}tiR7t~AMlfub;wm8KB`lZA5>6fOv*U>Mvmi0?e{LN}UfBt&xrr2-{zst_yxA;x*TP1#vi{C5a_muc
+uCw>p(28oq#(NEf9p<lWo>z9%iJLVz3J&(z6=?!`EyX8iWe(A@ixW(3Z({jjbQ=5_vc7el1@w-#}uEp
+=Or|CD%_=Q)|>ZTaYk8u07Wb29>7%d})*SzVH_9v~d3i_W-b@e|YElK~QcGmyE8Ty}r;=7*4UQe`;v_
+EgWENOp0F4kL|=soVlfhq?F%1=Z4v#E~u$FEG*{%jQ59|SMoBy=jUx|hG<u)CBm7up}#h4yD5Vu;uBV
+zQ<c{`RodkLH9s+?B!sB2@iK(rOaCz6_4#Lxig4_zg)_vq@If2(MtF`~g|4yoZu*=UVArk<)D{(iI#i
+cNHmKsgok!%YX2c2(OB<3@WUeXfYL5WmtR)tEPDsAMjL261s@3p2#me!`jO2SJ|1m*Ya0975u%+UvSt
+}<qJf@jSH)^*0yPd`K}u4U4?tx<%q<i@=OG-3Y2^UB;Pggh3cn#pzuxgD(WLFvf2mB@I@M7v3eEx3Cn
+b^qK?8c+^eXqQ2jtGh+b5M!SjWL?GdFcHZ!$BbuO=LER&t;^_0KR0Bln<IJnvm*ZD<Qyh#8ak?tk&Dn
+3J#-a=?_7RxC~Qf(6~mWVdZ-&%b`S!mx{DT)Jip?IHH$pTmG2t-&cx0in-^gf6r>3#OG8qRHH&VJU$v
+b$}o$caXg9@~V*XNUVW5lST$y=`Q5$ueZ>RrD*=JtrM&o^UK&b5U?*173=`*Me3aIMV<+uEri1g`@fK
++6Wzbv){zk?BWm#E(i$Tir<-s%TFmfqHhU*=!c#K-uA{`MOSWO^zK#skvkX*fegIXN5OByVW(DU^$nc
+QG`D!~4b1P5Esft{>@MC16#2S0g*piS3G<7=(FA*6F=oWAw_YV1P?IYfr<DjlDTG9%)bx~VF*mQN6Jl
+LKXpaIVwmP&&h-0~nrW`f$QT{XL#cK|WD)xh$Lw8gu&?r6$;W_Lk>yFL~-QTA!vhL_(MR)WA7HF!{Em
+_bV`3l|9N})Rv?L)~12##f(To<4<8s<QjO3@xAu1@eOnl3a(f`vk31W8USaf8N4G_-DeM589qzV5CFk
+ZDgb(r@aJB1u!U1e&66Y>`#A#Om_TaU|<q)Ot*?CP`8$*7^Gz_s;f|8V1|mIll^Bk8b|El9s1@8+NcF
+=@BKxYi}r8o*KT}s-WdTWTEA8leIiMD8+^Fj;t;zex|0-`3BS^q2;;a5D#*_D1snHlCzd)8AySa=Q~B
+qBbq@daYS-S&RU+d!mU6QYMztMA)M4a;X=(L_(SCNmFjC!dE1*H=Za`E(0?`tUwnnC=U>%A#EX_Hs-A
+;lmoGv|N*+<V689w~&rIw~P!A_bJpcxXpoOJ_=>6agaqA$YH3?dtVx>LxD#m+F-L|xsEGCIR(D+0OjZ
+b9tl8QF6AF)=^`iM}nbG@1$g<?6&TAClhQD}Y~649<uG|7tQ2ky}P2nmrjKOkcMh8se!BD$OTQWsRC`
+~gEG<&VcT-0rF_SvK!RE#(gwBPoB1k&w{;D3wEMLrX-%Wf>#%Kf4R`YD*#)_17jOP->@kGj&CXX!taa
+N-O!-RPc%>1BI5Tky}|Yw_f%W-bbpWTne|MLQx^C6>B0uVXevH_nJ`rxKHX)(Q>T|nSK?G*1B3+Pg0F
+YE17>lB!5ay-nJDTwV!A({kDp6h7iTI&8@x_4@jNTB$d=F4YWTglnt=+5khbA4c~w`wyhY4*_Is>b>V
+>25+a!WiK|NSLfg|zvbtNl*hDTB?Ix+Ev~4>E+Pl|H{cPb@(Xd%3+m?xr>E!Gbn-Kh34YAS!uNHN%zk
+UBN(n5UkJW;T-effsfg0liWk)6Ht3g>QH5nx2|$^3N{JqzcHj4L`zHCm#Gi&9kR5)n7bnjXBcnSnAvm
+WO46Zedx{mAWh|+=|vnsCr<rS3ipGPPmP*iD9Nq)CwAn1YRpCmd2vHNsJDcPq~UQD8>uW^}uZGRqS%L
+-r=s)=sKpDIa6g_Pwyor*86!Avk)92xNotEbtT9oNL?c9dT?xeQgP|@5z>M}-XQ7o>`HO6v!o*ND*L`
+sVMt4E4U(^Tz#cN|1BG^G@y0F^>)nZyu2gs?PP$s*mN@Aezo$Lc=o^=`ZxmQMvf^I*EAt#H6mQQ{R)!
+o$r`X~>#Q{FXPiBrZwh%ZZ+Hg0s?dLkDOG~RY+QJYux2TodChqt9#Nr)e@r-Ezi^ArOXr@_lk6l%RMY
+nn4j1A`l#pRV2AA55NL;KG*r}d{=OD^r~cAFIDQ%4wk4p}QczxC7hYf+wr^QSg<Px0kWKdg6^Q&@cai
+32Ic(Udi(Qpy*-1)dBZ<7o|=)YLv>p>*~O-rL@CAf(l;Wb`@S$5UCAQZpQ-ea>>fNL#tQyIA_BTi;y1
+2^Z^DuiNk`_(?*8GBG~oeA{2WU%)qa+;u(4XW~Rq(Fsh5KPNtFKQ*bDy~R`Vt$<>8A9N$l?3<sGP8%$
+lb-a+9yyhFxcxkpGS!gtHjzfv{w9Q?(c!={yE4>PzL!Z^h-Nibxa5ha^^@L$A5(+#SNt_UF_ua}0kCM
+@+D%@~jr@5Mn7zH=$6tlLOKc;!2L^sm3+?Oa3Qn=exQD~cW+-5yZ=?5<W2SkMLcT+H$O8ZH8mfdVb%s
+lPkR#{>_SBT3yX7nxZj#C<N(&_t~92@a1Df63)7$q+9si(G9bG8ebw&%6kZE|OYo_x2-qbZJOD=alpq
+>8W0e()%nbxx9mLxpELl$}w^Yc~WZ$qpSoxa#h__^2GubG`&$&NQ?CYd)|~^SV?zC=qu*cu|CW+xEh<
+`DOOT7M;A)Jk2ZLg&@KkKPvpF@ngbI0)EEgCue>MhB5uDhl@Y6=xp0*K5Ox+aHHk~@#n{Q_p@FrJnLm
+?Z`&Stvh0GZ#j~XC{22FGYgOTCH%p_rZ6o?we;YY`hwb?FlZ89H{f-8f4z?XFJPSXUh*;}yWfh1+DY^
+HzRy|Ta6s&B@?@>{0U4>7c152?myin+W=Q<XK@3xPf>%1WRa_t4-yX+ATZ~M6?s4kvpa3Q#nSO`9tXw
+Oit&y$XkcCq*;+ApD>AffUVU&vUS$?NCQ=kOW9IVSEhY-|7F3GszXiKl)46Sq52F>0=|I=sBv`P(C`M
+aEa!cP<qxR?ThkSDqZ&x@d^Y{nV=BOyaw0tzmVF_LdJ}bqg#t)w`JGE|#+<G5ZVLd2!4(6pg#n9`O5~
+Q1(o3dtB@R50rLHyU%s4IBWaGukuOHxy>7yio{;&%M_*6ZdYbM|9H(#>D|)#mNg$rG{aF4OD*EtS}a@
+^U&9?Io^~&bxP!7HKB+=2uIm|A>*I-TW%3PlV}d<!S&zju%x+vDopE}9;my-iKN)8C!@GUL<24rmA#3
+|EpkApQ+tKK8v@wk;&f3Fjx&Krw+--X*_B-xK9)_DiToUmOO^G<Q+ur<Je1c;ahq{yuy@-X~5x5-%6j
+XkL*+!@RI4<H>Zi&EG`?I|LD)0NvYc4*2%J(#0_7dO5ZpHvPagry$M_ndvvmPsirW%*9+Fvbp+%vGW7
+zz||kkdp=evr#OF5)=DXyV1prupv1%en5h9Tl#&-MGOG0gitwv=Tr~y9BvUZ<K$OIC{utt>o4!1UVvB
+bSXS8SCUZ}g=5!Fiu3F3x?<e5;bCoNZ&$1wVN`gKmsqb<Y&G7<pE8rO&6}K9v&MS$YtEIcVZ4fPQ8rv
+ZtCA{5cgjJ!psnAMlIY4#+e?OC!Or%F?3;^}<n23(YD$BO!CKkLdZq9trG~?J6UD(!b|m^_t*pKh=b@
+LwkUl_`x2uay4D(w{wYjO>XC8bq!q>Gl$!p^bcdw62rCoMMs^;9lIYpKPGReWxn@pTa)CHz1vpfLPom
+oQ*V%!UCP3On?`##yK;Ov}&{VsD`RdlWG`dGn!*9v<<ihF)ViyEJT2MdZZcCa=U$C!=Y0cLb&rHuM86
+LY$lR2*vlYN2DjGPV%a50{X)7~70`3QuuXhu8yxRoudht<!>|J*7K_5{HR(?Thx87UOcui?#u+15X-1
+vED=J&hNJ^t?<{j)7;af^Fk{6e}IX=Bo{=_djM?x$|C9#v=AdK-E9l-owV0Rcb5v){Gru@C%McY>Nz-
+Jko}^#McYRTXrb9#bhUnT72208XkQ?yNw(dhOYtf$L$h|I@JxQ$g~k?hMWgvIO8rW9BZ2|cuVfFSekF
+Sh`jy561JTtap{q$Ir|dN9YO=GatI3{9T}}4v9P*fe?k5}F&op#D`8o49q7>(gPr75RtBUtoVr)msF5
+vw2HqQCwH=aM$dw#xmto2gi883@34H?T_EiG*Kmb95a)qQ@xdyMrmz9De4G{I30efq{&uMZ!SJbK4Rm
+;w78_S-+mwp|Hu@7y*bK$*Q(68)@~N0dYL>U1C9p!spWo_-Y1%dPCbea*^7ls9e4n8UTP;(7%un>A-S
+dlikxPKBxN_Ul>rQrOdeNtn&;KM2#?{;e=u+rJd1kNpr#t$nX}RoP30>1+Q$nBDB_gxTG`R+wu03Sny
+Oi-p<VzEGHY`&?m$*bB1IikSFRliezuqU@Q%>}NL%GtNF*m<jfw!W?3c7v^w#v@l26Lxnlk9wf|Edk<
+kww09O}hW&nF=GgBMrp4YunA7bZ!klUUJySwKl(79$Cce%u5*@a%;yXwCSHg<>i0uc3b)jfWgcV9)`v
+=0h7}mFhbt$YXg>?n2&kO5nSmy~V_Ho$fWI7HOn40N07GSclLLX)yE3D{p?L&q2fT*wv$~aTN9wIz&)
+Pdb!SaFn>y^FA(g7rROJq@e3u;PqiyN9sag~9?mHa1=q^b4yvbl{w@Rtoxs^@^ZhSgQp6!YZ!S`#@Mx
++wE@&D~>v~uM}2%6l;GzLq-|58`(>Q2R`Am&lc9!u;vM?53Cb~6-&$Z(ZZ^Nb+EAd!rE6@yTKYFthj;
+B?k}urSi1<T2G;w8RS&DTu!g|uA*?1?f13<z6s(towI8hKCd(+t;q43IkpSx_!ip=l?WMvx9M%oOItt
+d;g>@{f%S9qSV`?3tZW&=~Wb_b6Ry0nq&zvl-?sYeMjFMd@NiH<C8!NjEmt53FkGZl-AK@}pV+@*iQ~
+L5;1DDzvc@^P%T3esyMvqiEUP~z@Z==UV*`<1taA|Gy$dFwwNG?7`j~v<Mxa88_=wXpvc1tcQqsMgFW
+xeF$YxI~YyR4L4^hS?`vdcorCB*3QjO;R7axob_7RxRfl1r4)W2x-&h~(1G=&?d}=_g#K#u<ZF%hYQp
+IZ<D0FM(~Sk2IA(I=h1{y0vY8jNZrMX`)W+TH!u!+wND^&$xY-3r&4|N=8=&kM^}T6Q_ma7Pn|$Taa3
+uL>oN18^&KgUeA_7$L+ogGh!MH`SLID1$CwIhS>c$n4D)vN>@c*FRXIS)y<Cz_YB3EoYU^M;}kaAPU8
++s`#tQy0c^HNuwPFn#L2129)>ev3QIjpqOagg&nv;BuUOtp5+_aKd@~$%<||z|jJj+K($Ih>F;NO2hD
+cvvf9nqEf?{zDXmPx^<sn<N3XWK#3XZPwdTNz~czfrF;<_8#tSaM;*}a{R*8+KC*|dA@FaAoHR~v6wC
+hlnr%nSE;R<uj78*V{fI-H&YhSMmJvZ!>*1z)%(CI*jIS=|#8D;jaDO5EgDm6Qm|#MULEAjy8{198aZ
+6+141u|-#nm~TFZgTLad13$HQO?7<PS~F+yUWl<^xB1bPNJ&n&=1{l<`+2bk)VIEA%Ai3`dSVEQkb?H
+kQqaCe3RIAS)<O!rYowq(q<};Kn~w5lhs@!XxKvEzT=pZXZY}#86K-MO@#2$~kLJ6FCro0A_Iq9sgKY
+2zlt2R73pTbQ>Jk#gfq|(K*AneZHYv=y=wQ}w<LY77cMHHQU%{+8Y^o5u7~0pzrkX7vNi^RuUbeO}-k
+91#+T7t<;zNZ0OL}oD^yK0y!u+Xs+f6f+solBhVroa@M;{<DW`VLp#H$#+X&P0{4{_^Anz3xE)_B?CM
+aefyN*reQULu%azdBvnw_(44wB+p@@>zHo6H=@$#v9XA_Wjc_HAJpiWJ9=Od7Fp0k9KY&c~6RT9IV(T
+62hg^u@Od`zYyPiP-4<B8j{Ah9+X%zy1Cc{a$20uXlWfhy17@e_&zw^)A)&HYl%z6hti~?u<B8-Vq9T
+Nv#_E(E-nXt9Q^@EiC!#7DIdw@iA0_IL7x`vZ>{W=C=4y7*6pU%!`>a5HN0R#Bkc{^w*!~0Rb!8Cyyq
+a?<{}mcEN8LzXJ4(`nx*lfyWd`#93Aeo*S51WBl$G$&24Hc8}W>dmAL!v=z+;%F{>zdzMAyvbC`!JSC
+h82=T39JnpB`s9_k`rPC9g&<8sn7Yh5gDaDND{CdCO`b*?7GrGPlYdy;G6=}RM<?QmRs*xml`?3(*YA
+J49JKWY9~(y9}+_I|L|+)rA_9QTvD%J-A{eNr;Oy_hQ(ly*5TZ=$QN7$9G{7%GbIPh3!XPa^h{53*lA
+R5LfU^gzC&J=02jD(=S~?P_FayCIxLSKHtIQpp!Ll{Ry*t)j7Qhd3P7`Kr>^#ig^GCEzNYTo-BTjOv-
+-kPm*3MjY-S&g&Z{-v>aCZD@0xN9NkJ)Y7DIyNlNw?!Fa2(*S_FH*zm5b9u6n@Y?O-waVQ$?<XR3r)r
+F|jM#?wy4z^7aDBTt5+cOrYH+dkq)1&1DK6H|P0v~GFSC0aB3!L)$+49o#m(wnSms{1-M#5K>t{Ba9P
+e0oU+Iz4g{3Y#u5TA7GWtB}O1MxmKM(>G>D2Y@*i2|yoo2&nkK}QXTz4b=U6lUT!ZO#2xe7jaMxTuf!
+kR0D{XD-9mnaik*xl%VB4ufss5*heBXG3pDpaB!N>!q$RX1roAsx%A+zcKZ-F)g@RI$?vgPEm?i4ZeJ
+H_!dqc|VpoFEP;;@7-*lB_5c}YQ8Dr{YN&cfM=Jc=bmhA=}MS&BFqZ+xEOxOZDjavnyc&ghCO84LAKq
+7)7ECVgX>7(KEr8C6Kwh7xvtm0vXza*d2zL??*VaiWt^{fv%NU|(b@!8aa}*N-J2y|KO=7gOZ9E-CfU
+xo9UVztyz?_e)c=h~O;egMf&_v|1k(wgBzTEnJ;7muiv+Ic8S^2~5X2IUA;=<_O|Xz)1;Gad`w4Cm-1
+R+U9SM387zpAB#t>Kt9w&H#U?agX0y_cwfzl^15hM_dBCrq?5iBHliQr9wjRXe?z9G0m(46LBJ_KC|0
+tliA1{0(bJWB8k!RrJ&3BDotiNL*_u?Gmc5NHTY1hE9e2qqF(2yhk0a|EvtY$y1F;2gmff<`nqxc7p@
+zwgiA%G0#2CibJz#7d`{*pOFDY|Jo|!>`vzCf25#>DEY2&-3538VVD&ehfj*Hv|V6i_FfAo}O+=$ju&
+a&dXygljX7TESu#rGyU~ssVsy37*lZ*n@%vE{wNq5%7(B$tOwIO+5MT8#j$9S-%vJy#jr>=kVOfX{`5
+PT+@dK&APb<BqsSH~!uH9?>yv9X!%Q?=hNWa!g}twnJt89`dpyGCGAqks7M4z!nwg3qm;QjSN~G0-H5
+KL%N_!wnq(A2{l5ZkmHh}f5$A`+AV#Tuv5u!eSJcEf&HBk{LTzgQs2*Suhp)*+ynJR*8`n3|LfMdMKF
+^+8DY^rce6X|A(uoH=zJhG+GZ=OhH9PxYxxujAWx#XM0CeT|Jg#hJKMG7@(!9QL2q)@zc_NW{#o7|fc
+CzSLNE|q@A3xBhmr;2j3iuB>HqI|POuJFsDzYIqVDaD@T6C<dUQq3gSJn@c@lPO&ZchCjwaz##7az&V
+IqFg1?%Mjru%2Gtm3ZHri+Q7?n3S%amRjeE9MXni?+CzeSkb?0JoSgZYQZJ{n1dfoN@uGBc1unU@Vnz
+v^;wf&rpi?PxrMv?i<()+_EJ8lA1h*y-tt!ePhvJTx%U4Q0gG`B9rKBXEOec=k&By-_<m%S)Haqb<lF
+}I>mrJh0N8)g{;2P@ne}!Dsr9<tYL%~VmTYVIy)`=fLZHp4$DoS*m%*ESr0XzVHx7ODHru&QKH%(A@`
++8Pie%il~AEY$(FXk8I$S+5f6w;ka;d3dLRisg0%~9%NO^Ql-n!kX5vPjj;rVGq_$r2^Kq2!cOycK_c
+7JS?C&ZgYVGR9JU&X)br3QiT$Sbw^;<p|gyrSSr@R419aeS7ckBtP}1lPR}y>7vHvid0h_ZNTI|O#iR
+QOYNLe1;10|)RlB=>LGZVE##q_Xh$cCJ_j}3?BJ~=4Y$@uwNv@rIerKs$so!sQ@mSh;iq;gpF2-Cg?=
+Yc2qne5TjW>m)Ly5FGLUMI)Z^sPd%B3J61`K7aLE<#3KmkF)LOhy#!v<OP2w{6Ze(H#et4frgqTYvv`
+1<(Tfu#=iQ%%!xn$lAcQSD+PU#*KYm0FhneC7$nfH;gaJNZ(AZH@;0l1UdfeigFlNjL~-f3bFl6iVNj
+mOB`LgoxISCIKAnMGtm2{4mPEZ~eHGhWn<T+vUX{{?NsMBA^l3X#l@{#3-d+M}yDvW=sd;PwnbdsCSY
+J;fhmA;dSNJVAuYl>1&OHqx3bxH*k}!{oB5okO&vMm#9ePN<cdLD2kv{ZZZW*Uj;FtKY4E{YWBj_g7T
+UU+oZcoc>(y@XtB^V@`jrclf8oRsB!1|NnvK)-s*=-z@<yHGg;d)RF*~nm^Y&{Hu|G|Mc&V=*M4c`g4
+AB_Pb+}Wx8v>-zmK{bk}tecZsrsIgb?<6+iyO+>$44^X4yjYT=@PKmE+J&pp3*$qO$oUG~!Q6)&$`wR
++7fudaRV^*7#p>+N^ez5Cw#>o;uN^udQ8ZQio=<87s7+js2TwR_LrefvK-@ae%rhmU-A^w{TLe0lt<6
+DLo7{mr+h&z$}4-1+Z+u$Nz`xcK8wmnwh$<?@xQ*REIn`rD1_-*0l};_Bw^(WtR!lcvp@w`l3*-RiEp
+Ti<hU8=toA?rVSl107TkcI@Qaxl7k>4?XPHy+_YpYX5*hO^{Zn4>lM>Lc_vMDdWbcn$spsOrJD4BQq;
+IXG(6K#X5D`^cjzi95s4O^4Ny`|21>g?EL>}`v0f%|Bv|}(K|9Kx=-JJF|qyQ;s*>&7&JI>$k1U)!$&
++K@xNjJ|0nqGLBpnIa*KFB+1+LPb53?#nd~Xs7dY9Ua<VUUvM+M7|J%v_w38jzC@c02PIj!$H<8j4d-
+w44qdx<UTrhUY6DwtmMvu~8cZM|%=}+ZwnIZo%;oeKQ_mW*0jfdie86lfdV#Xn@5n(ogX8?_urq*#AD
+6exUE{SC3$ZH9TTZ9;4Tg99cPpp`~%y8H-$C2{R5vx!c0!NGm$1^PD_aXmrVojxHd>AWM8fsi5d=(rO
+ycB#C9F)A3e5HJ5IPzBVRq|BwK{_d7cAG2aJ`xUn1hzHrSm{8BJSVp#F{;lJs~w8#5DMk|9xvuBHE|L
+JZqDy1f)*)1B_AbiB|Rk_B|griG-PHP@`P}t($ifPPmc<g8T7FD_+F{0y=I6^5~$cC2%^m6&6(rOqHq
+UfPj%P_j<;mPnrP0kD2@@<3D!Id_Qqw1G-9n;q97x3a??c_rsR$n<!2t3YfZ_O!lYygE9HaqkuK6iT1
+W?JP`nvP1Mv|TaadxC#hRO%BGTxSn=WiaCR%gzWVa-9YL;1k8Df=e@!46zA<-(EDHg;<JcLI$gn>WnY
+gX2#lhU(Nv!^9mQY`7&!hL9-Id|Z=N#^mEn5?wy+)Uw-MEQ_kTuPoLV!S1NsyQJ$$C@MDq<1M97EdXr
+OfdJ(o}R>pQpsR-y6(DkHzCQG4L>BkXIV2c$l3`1)FXQ{w??dytG9b}bhHPaAT`xj1XSr0hBx+PI<-0
+}p)n2LRHDi796NC=J&aAynNE)<g<y@DrzaVn^w(HvR~i)*JYF!TfHH7<yx{T23R-)3P!7e##nb?*)dd
+L!b18JUuKI+e0*X(WBqU&CoT^*F+yaWlD4U5BCwh7|o}QC4Ehp!e^#7~7kw56hg;rny0rUp=?7&I|85
+qzrHG_n+rd)=kXPUFEmI#uo6iZ5mDmpheJ6DySrHV_jW{sa{#=LQyRNqt5Gt8+fOSURI$DE}yPdAUZT
+2jVkK<I~N!2-WDDtJ{&mMSHO0;N)T)B+NSo=#zM%_*s>wA}1W3N50uP7g{vi_IQKc{{{RHN|Sqosph3
+L6s&lzr{b#Y_T|bvph>Gp`L2Z$w(hBg;0Ei9}!x=C;6FkbL;o3!FxO*ZZWHJq?)3#<{|5@o%6an@n(Y
+ALaa%*sK#ffnpN3pN@&dGv6dyZ_0iLFs6t6GB#u$|%*+(B5!a9`k<qm$@wh8&c@xd!QphnQJ<Hrv7B8
+nX%(<EASt)`ML?R*EV~RDK5G6i26;neA4RMMt#haFHnFv<pSyFPHobMpH8a@cBWdC~TRdAMM%{iS~VN
+=b5jn4iu+lLP6(?fr2x?MYURi((7gZs5ot_P2<4|XNGGBUEKiL&f;3%l!y(=ZR}Dc9Ty)=YDjB~R7OJ
+e6uqdYa0dnPZvp(CzaXXI3dfmjay}#7+?q!Guh6W;XRB9a(4UNjuW`rn6XKto<%M{n%fBuu1IjHzqL#
+8=M%G_-c<$v8Lz~aZhVjJ=$i?nX{NMMH)t-kH{M}j0$wdDC$ShS>%n%OV7;7Fehio?Z~LX<_vR6o_SQ
+?p)rZVOKD+x=A@#9S=fuQb^eT1PN*Lje9r^(m~1NVU-_77`Ipx5-*%trw(-ZPZtuUZ<L&(qb-KO(JHE
+C3=Q{b{)y2fRbgAusxK8}5-EW`%cY(G2k30E?YfLOsb9?@D*ESp<=2@M|VgK35UN_w8;2OVmPWHO^4h
+d;EzZ;<@)-vq&{D>ZKd;cE?*Y-b9$A88U$zP;3#MEFqr-$A?pI=7bKA$r4?aS?_?A!Zq$i02~PfWeNe
+^kls@o&B8_W2)JRoj1W9sfmZYWVo+>W29k@T!T~&pLv#<JfmmO2fBvHEzF<o2iV|`~0)d<?q?&($>X!
+3(6BNG**w4x|BHTQIgmc_e8Vv7@x*k(zu_EbHT=uxb$(kn7gcHkzyRAOc<yzMOxO}T<YkQ^nZ0vN=di
+$$<9rrjy1z9r!;{=W|?y-c4T%|UUr5#(vq7IO&uf6qwZx9d3os*vg8zoWY-|TzC{i|Kaz$6j$JgZUCA
+{*JJp(D?nC2_0V$b823zAc2&2Y&DVg9FZ_Tiz_nu)f56MnSPc=tQq*>OpGDVJXG#XAt+KHCbe#9S)QC
+(f-`HmPMQ_JBh&zT}pQ*#A2A#QQmDXB7-D1{#2rxTe{O1aF=qU2$wSx$T~i_FN*Q{MVV4;~O5r;OC2T
+@o$1^dCc%CH9L;ib#kN9zHd+OYz8mKjxKSP<DD24Y1fD*M8=d9I@$#eIo4A2A_0^wp=QiAv1Ci^YJ>;
+5W$X$@U<m^vGGz&8eCa(;E}{i-IeiUO}QL(ADT5$Ae9<DeLPK>DJ~TdP-1Jqm4?0DV)CNQ<E#@Vm~#_
+|tq_c#STv}K%uTlt(=rmNd;~7RZgJ+6spebUzjKYrBBzv$^hfJ@Mr5TX=A>suW?L!U3zV)iw|v|OTQY
+`b(LgYj9U)F;m@T&?9`0Pm#J64ULma!N_Q3AgO|=(qj(t|U@g@r55@AQVQH;sv)R-)qQe+D0&jVka<Z
+G8xof<L`Go~1tj8r(srovWYclJmkOhqkVpNf)`B%11`+hB?-JXkjJkfcRo0lSP`;0ZZkY4FFJ!V-4(t
+u>4!J4Lhy(OFZ|bF;I6fFw)Q6twbCD%Fr|M@?fVMD497ID1v9s|n^@A!J$OCB!J}r;+oJ^zoBrOv`R<
+V~8O$ggnm3vzRk6k&Va`w2+Ox=bo5hHs`RX<hl)yB$g{uNr33GiLggRCNQQ$Dg&eAVQ)$jNM)9uDq0$
+vBeP>tO(1EaK0&G{Qk|yIZ#umbn<&H+;Gt8y;ZEtv_-5z!F=HLatVni}OP>sD-bA3*+nSa}V`#=c6S)
+mGOG1^1)S|Oe2c}6S80%WIQiE{R%DBZv#uIvirNmKRNfKn7lIrhL|9Sp@@L+$$fbUJ4-)*}s3h$*YBb
+Z`Jnk@P8u0O8Jt<JUlUH>jb|9SpTJdh`RTd&BDn`}a7guOVpaueqf;BZ6eb8u`L^f{ig*+e#*%6`q9{
+O*<G!t|2U^_JbR|K~0l;QpZO9wfg<$nTNzdx9Kiu<V{Fn@Mu|Bjmg@WcN(Q#O?^_QLtwySH`uG9KJxt
+d5&y8Ci@r4{zY;+xGWee+%L)RFU$C>mh*d6cHbo9cTo2KQug<0Eb&6&nZk2tll@8ehW!6*_=f!dCm$|
+9t)`jSoxuP0=a2SFxYl{x<VT60@TrLnK4cPnD`<MVobK#Vc|6_L`Mqv`5-FSgj+<B%fr%i5Ku@3{P!n
+_`P!aeLG$&vLRbQIeMFKm)IfBy!rwEP{93wbPaDZSh!A^owf-MB=3Dy#<AXqGiUq~>Qpn$+akU=n(U<
+g41K^%dJpgVzzpox2pjQh~LH-RSsBdGeq#3~8w1jh;X5_~|gnqVP80YL^qD#0j%Ap~&*Q3N3b8iMWwz
+69+FS`#!UsQ#SNBsfKI_;V@zPBK3rSWEB>K@mX?!6<?u1aSm<0&jxqV<t&H{_`TO_bI(*`di;!*sZwY
++yLc)oE|@7V#ALTk14LY%EbQbAMXYq$zRpr^WT8nf5X$Two&qLSX=o&i}&C8{9l&=`T1Wi3b>x$#ns(
+&HT8UBADi)*ta@gzd@GqRuaV3-%THsAH#5yH5awD6U$*MRDpdpN&bcX>CfVF@hv6e+b0dX6wDR1_cI^
+C0`^u3Gr$75;VH-pP>Om*QoKUgk6O&E^+#3Gq@o!H5%9#IbDdutNJNF*1S*IUDeZBM3Q-%a;YZID9{2
+}Da&kzbfgx(thrR{c+(^uMd1rNv5-=k3jtorA<%_Hr}a})EeZZsWf$;4cx0CTRn()7eBTw}ri737Zb*
+au`H-eEG~ZzmJ;GzwIJ!tfyzb1}6`-E>^UPKJK<xFh|s^|<4i?$oh6yU%UFeQ^WsYa4L?paJ*24Y(g~
+z};StJH~!h<nGzgh1rRD=j6?hmA)<vhcYSeK3!bcTF;vI(w;7CqDRepHx2oBu6aK^g5JGr-ltEZ@a{G
+5r?XtdK0xKYU$zT-#3?@<u&N?32`rTzn)N?Dd-h~~`}So?Nl7d{J)K#tR;i#03h2LpEnd8sz542_?86
+T~WTmC0?8`5|WH)c#WO7w45P|6hCuGq?^r-^{&6(GpY10mg*F*eLbwTs$Y13xTM1V{DK=mPh;5vElp?
+6PuxkPd4ecCj<+YcQ$a6r6c%E3J8=Mt|@6aLd?R(f8f$b7f_KJAca72>C*RZH*r2!G&ETH2vK@_V)9k
+29{S@lNSilRq8`lP{$&y=Pn(;SU@*v|DnxF4F%N`AhGVKj*eXP^!WW_*EbJ^;>$o27iJ7W7SU{s6KEF
+Y2f|l;88IYpgM!nr+1Gn`&vJ=^uQq^Ar1WTJpR_4yDv#|Qk*5``Fn9dtGEM)D8xLl%d`5m7yR{70P$Y
++>7msf-#j3~0?7ShL<p~<=wc0k@ISa}$bqWrLj|QG{2|VNAG)ooT6iNo@wa?w6&K!u|470vc_X|yXW_
+WyEz%db$(w9F=gN=d&8j<;=AU*e2qzKf-~?_2E(J_#Xc_<)Ih=F6@CG2CfEO?q0yl!j1YQIkION=0mi
+0f9bB#vB;^X64VqzkD^wCFa$@s#B3)$<hzs}aLUoT|*i!Z)l=g*&a$axNyTh>t<x^7crl*GSs{>3+@Z
+DaY{E^SMjoXzs7h|;!kp1-XknIvIy{!dqvH<Bg)rSl^>zmYa;-}#|Khi*%Y)4V}u^5obl13BN2%x9N$
+-8OP)GRx<ywrv=S^gdjG^z%9Y@%r_3Oi1y+7@OUp1NUy+fipK5*lj=w(^v*CiBI5>N$|<DVJ6YjxpQa
+c=jX@NYBdWD4P_A#5iBMqM(}RXph0ZduwiV(h!Je`=+SI;Y9BUX!UQ&H(j=CdnaOf<b4B|xYt}6GvNf
+E&@n|S}f3}eoPKajnCP%VovLo1wxhA%Gj)A>Z5W`-WGmdRqoWpiM)t?<+HJNq$n6vKNIrHDmS@=%Qg7
+$M3a*(r8pK=y?jI;hHI2-gWXL$re&vG{M2hPTy=4{+W&gRUS!%9j@*rG*?*z?an&z3A%!j>*w%2upc!
+Pcx<Bg%K(x^-;Bh7F=E+p=W~JGx^Xd+cY<HdJ!9Yu7HeZ{I%l>8GEvBS(&~W5<rM6DLlvufP79T{?f9
+9sPx~GiS~SxhO9$XICy=VBh`9S!HD<yMFz;kQF?xy{LakV5|`djpstfnv&c!qsrcrDzJ**#X`A18_5T
+;8GH&`%oni@{9Sg8A5q#<)|%pXr1)xzA4>7#DE<hFKY`*;=dIY26n`njf1Bc$QvA;-{yC@kT`0bZ;y*
+(1EfjwN#ea?BZ>RW2DE@JZ{|&|ej^dY7{7V%7np6B<U1+=)L9$N6&4m`m)-9kh=xfxc?Vw8dSu4hm`!
+oLaAjZ#4Vf?}(#w*`t{F+nz))c=Z#aC1OFp3{f@hPp!bc#QN;y+38mr?w8D1I5m|BT|FcZyGgj|8g9I
+W*Y7eMsw?QhaKEDrpeIRd+ESM)5~c{6{JN^Avw0#Xsg0pS5HOtPR!K2iZdAN7Yu#4w2+nvKYo&J;Jzu
+4&#F!XMD<1#uvTE_`7=<Kk}_pd=<slQv5iIKaS#0r}zsf{%VT<KE?l-;_spOl;@&T6#qQMuXM!kPAT-
+K6w)b$Cn$wAl)_F*;S8lv*^0AY{5iWah_h=`IJ>@xvtQrk?8Xu9h<_i&??LgyDgI!JKc3=GrTB9x{t}
+A+3dLVf@wZd_LlplM#jkLR?<sTO9*Tb-#eb0EccJ**DSjBmA5QV7Q2com{}qbAjpCnhieLLlmL6xP$j
+IIiy(5HviN!tKt+Q{(j$QgXd?R|t#>7NL#YTpQM?@v}=+?DsXWzbkJ1Hqd#gI4s$B-o|D%tNL1nApWC
+Hu$5M@GknQ}WS~5#hZflDi{7=gu7;l>KAkO%bsXkx`KpA9(fb?&lYlT!Un6e5~^HxzDFfa?hSp3cej5
+RCVf$_>_KZY)p7;^8I(W^&x)>&`l1|C7I%fM<8-!Z1R0~-`!U77dZNMR1uOfj`Wl7X?^$I{X<0nKM~+
+T72-!m!8Zc_t?$0Szl>Vk!^nZ+Cx=JG4~UJ8j!kZd1lszvX>)J$=FQv3AqV)!i-*#0S1Uj=*Z}{?==h
+k}*!bx94z&W1f7gda`n}_$Vx!}u;`=<%?jAV+{2%JpBaTVwM~W;It|f;cfXqLUzP~Y^SR5Z68!w0uIV
+k)m{F9^nO+X=*(rDMG&)s+5O#vMK$=XKAre4vM!2t4qpiduxLVJgQq?d=6zNI=kHY%RD+^2&e;{HCec
+!V1wA_CmJJk;UI@yYQq(K43tigd+9YI;ZZa(8JuK=uHyVxl4({>j0;yqh#~acR|$Fa(P6vEd|0HU1I0
+Aa!69H_y9b$TPAx$+AWh8Cm0>oZLHNps{6R4=q_ElaYE%R89WLkq9!-*uu533I37cB(AZn#vlA40W&p
+g)pB5RWPG@hWQ{TB^rfetcdOv|2;yUmlQ+_j65iyl3yd_z)b^I-htiKCL5ztGGSu;QN}pI06Je<1&0=
+dm@pZg8FU_y=DhNMK1BueVXFQi*57axBD`4Su#&fqHrzqpO!Gi~j@yKj*U&q+v)#+huZhACZoEyp3KN
+ii_y)c{U_R^Sw+PQvTayIO&G-jAi=5x<I$6kE#MYe3&GWPPzFSFIFSBtU2`|rOm#)BVy^pO}Vd|vu0d
+y&Q)>u9X7bLUQW@Zdo)HaK_oTXy#BS$6K+IaX0o!7g6B$bR|xk{BCYy?T}X{`>E2(RI$=rS~H@oZ8u5
+4^l^`qmGX1<-+mQ(aoTa?kVc%USX~HdgjkRW`p=%HiaK$i})$_E<aEGccoK&>JSrXMxR5S?ZWO9UrX^
+#6hD^YkEHlhDE<=^e;LJpkK*s6_%;39KjV~t#wq_>$0_aPHlSU*c4Dcvt<WmA^Xc5HS1+85+D_H3olm
+<5AMW7YvgHFLXKg!n>)g3(=U%P6y&h^o@9iG!)U_*xZROpemtQYG*1qGz-Fn^Mv{x$%(6U8~2iiZ_xm
+&MhO}jlHJE&CM9`5Do*}NOYdFZZ|E!z3CdAL_IPtT@JJUp5|<mJ`Qy-~Y|?{DVW6b`+-JKWRQrE$kzc
+<yiFf%Nb7alM<~nssX0q=_HZuLnEd=hmKlDa!ryPWk(_Z{4+%U$0(%xD<}^f7s8@*U#@kKS5u6dZ06S
+@Ih&1I^T=25G=yE$<UA@&&4SW!egUE2Wfo>*Aa~sK~%SGXYb%qwsbUd0uMRY|BmOcs4;;rwou&Dp+g5
+S6@X9Tx-@ChgjyDHfGcrI91TM)h1iCnaQyFj+~jnd|4~5s0bkKQLi|<BmoHDEerNNgOP9X?>8GE5puX
+qqk3ar+p3HBrT)A?I=B4LOojUdIo;`bPE-o&9At52XWSV^Bbg(4_eE2i`Ls_F^zN<@@E}h5(uUg(=T%
+VC6M}}+ZA^Uw_e);87G>_)nwr%4-{O|*({*-_5#TQth;@^JzEvNZ6hd<H4sh|DYZnu{ct!F;_?6a53%
+F6P4_wF6Yq;kR*oHk6_A_H7du1#ftUD*<ZtvB~0Y-VO=U{zJsZw$@go#Wq@_<e}#payB^bh<9UkMI)s
+@87?lfB4~tobclxeDHz5`~3Oy{QK{}=Z6m;=0}bk5#bPr#t3jfM{$<F`|i85Hf`E8i-v5`g@uI!b8>Q
+`7A#nhK;vrQhV4OqZQ8U6EGa2@Bp@IFyz_~Qit2`R|1AEOFJI;~mRZ!PQ>P%};erbnF7VRQQU{+89Xi
+Cn`syozCzX%D9rn|wPxF%}Pl|WYqyUY1gh?`Y<>#M&t|Ga+L2255`|UUW%P+rN{`~XLKm6{y@4ltHen
+mK<^J%JYL4?Bs#Qn4Q(|8JbIRN-802&lPnJ57K6*?5V0(EIofN*vImAM0)-2s0E{~bGa%%gJbNHS3b8
+n$oWF7StZoH%hpl);%ZXT&>jM4g~IEX<=vj|xEDKzYDKxuNbL&L^LIBJf8UQvK&BhnqKVa_5KMZ;*R6
+{HYEH{2BbOUAxAq8xr!b*Xz4Ljsd{`(@#I;n>TOfROXz<%%Tp1|0s`r`}PR{?tAy{6?NtK@#De{8qfw
+n-cV0aM!;?3Pn?@ia2|e;bIm@^d+owKs+{*M<$Umd&R;*z`PFOJj#6D||7Y<3_~VZk`TF_>(HMRKwU;
+OlK?Cpw&%syFgR%hbu%qn1`Q{q|u!HB|H}HiVfEM5l{-canUgSLL3(ifSac(@sx$XexfqRLDot!^RG<
+4a@dB=}9UvP@^>gwvHe+GYQ>m3066;R4Tfxl8uP!Aw4N#AhZ_c-T~pA-K<1C@dH6V3yOhO93+zgEq8+
+7ZsxA3IbNcZC1->(?R6^8y0{JCZCEfd(pvTWA3;kR#w&A06O3c#blH+(7=(?$Vr1AT*Hh?|)LFq4zP)
+!w6Rc(GWy(lXG0cKm91@zFVX^ai{oGpTU*(AM#KA5eL*Q6GhI!SCj?h3bLWlfjSO3gWRKDC?@I!p2Rb
+p4<H(1N&fqMDbWymnDgLIB^uOJ7rJlfyxX=vk3Y4=i#m7i97N@?0QC%QOMP@e)*F&9@E`u@7Z4BajPo
+<>9Mw_K@HOW#L_;*uu<l2GC(q9WFEoVzufP7vsh_C@pg{qJj{3@^K00pPxWSKnxQwrv8OvXo63(B?HV
+Gsi`JVHk-*G<ZH0SZ4;VZd5lgxxD^?9FEpV20D`B<vYJ+?@7np)fg4dGAaJWr$1bfosY2>3%z>e2zZh
+g{dCL!kw-4mtYvz<U1bv=F|^8p>bF3llWp8C}l#2$_b2Z#j=6ocev`RG&eET%UWQO`<lnGqp(%5)B<b
+klI#y=G2FO$BrEx@=tv{`uSVR1N9gEfl?Nz4|rE-0RGhO@zUp0`5QAr_$$*w`AVW;8PR|zh1$<iM8j~
+xW5`*F2BkiO2BkiuP4eGEeBZ(OL!g29ulkVc|6Y;I!TRv0_Fl+8)wKl}524N}bSSid2J{CF$rkWOJGo
+&_1b_QqM*cd{u!d-O*&^YecZu_{6-0xb^Q7}qeMXxUd$OiJg9fEOg9fEOqfP3zStN74F8-)z=wE1@<p
+9v2KwUcOt0M|6csAt+^Y>;N_}h;f`D@dOhN+?aYUKs~<!=%V$wb4*ABYC0`W!3Q=ZK?Ho236#>YtT1s
+VmXYX>+~plE9zF_w#~+f;!R|r3m<=U4#jl0Sy@QD73&0eXTMsgCF`^j2ZBJR1nNJ&JO195)E$=4Qq*p
+ElaZbl}qJ!)Yz00otac7dZj+gV~nmJ*N;Ei3q}47216H=g#xIrz#F&$cZC+T{b=touR*(sz6yOCo{t~
+X^ACxJ4YLh=-Ap5YlW3@0pP!{RX;EebpEtQTpPL@Z3nxVK{M0CJvp&MV`SP%!!Fi0)Qyyb9gg>?SLjD
+P#1;874!30jg8}$KrD`kQ<-g$hC{ueYt{&&m`<6DV_4~T~Kvxo+&3#iYaVWmvN5}AgDpkY#P{=`J0Au
+Wo}PK_2c;6a;Ya2jI-?W$k?(Pp3z!#D!4cI{gJ#v5;NjEfX-E|c5Uliz>;ogaQFg_jlT_?9_D1J#B1i
+H3LN`iwT|WosB;mK(+w=Y;cTvP}G`j0kQc8XhMa9wQoN5e<0ijxoj@t-t*N{v-=R{z<NjK+EHgKhEEL
+^G!j|ozS7sg1Gi0@AK_NI$laNY$h5+eI^>vCMorK1+_^_bHn-bIVS!z(XfDMc#>!+COMj87Pp6rHYvh
+sjPZU&{p~;SSLEMlG<E?Fz;Eu{xeglY(s4Ule*e9ipDBBR@0u6Mw-xI7X1PAU=Tx6p%QU<w)9`PSqXm
+;AcuBfM10FHPp#C|IVA&6%|NVsB`m{xvQ~$eY(4axFSy@^8fh+1G>cH&Tvjq*#^7Th$0uyzr?i{PW=g
+XaMJH{BO&-3N_T<5v%_y_o-?ECfWhjQSrz4n?Idtoe7Qc}XFPMzwY;dXS~u1xTtO<F(u4(c<}P)|G4(
+9;%tTDWjw8MU3fYuBzkIXRiHS+j<tU(U<R;}(mB&zm<-)a$Rm{#vv<7z@=$3-CtYfVK)_A&f^cAHak9
+eCQP`ul|kZ8>^-|>a#M&SR&Wwg;W>*2>&fxws_H4XNZD-j~+d=X=!O$si~<I)Q*aBSh;c~A2@IzPfSe
+Y^XJd!&pr1Xf99EI1U{JG17Fa9z5xA%LI>tBpaJbE<Pd!!^4MvM;-9?={IBu-tEbkCF{Vmm4Af_|NiR
+%~2l{_F{|X5SDT$Adp9A?JSzko+{-6yTHoQr7n3Er$K7Bf;I>GVYzkh!*M%cA$myieG4qQ=2id-q>0s
+0_&nCD<zhq6F@K%Ex#naaVrKEGC{J|Eb$9OJ+j??iZlgM*)0vSf)^2SMI)UFHP^1$@}BVZ2kPPW+*V9
+^y2%7PtTp)D33<4GPqy1pt{+(nj0*>9TQrOR<UiXK9Rq{`rkrk$ltB6ZpwbHu7s%uO6lHzT@?W#fum7
+qN1Y9ixw^7k3II-)khwAglA-A2)@(2Qpgf$K)nE+zzuZNr3Gsc=!YTq=%Z0rupWVVB-}7YLfL_qdLAP
+G*Kfe1KfC^e$W(V%<mBXh`>%if>n89_N=g!dI<3&~@WT)DY15{OazLM<tnDat0AJv*todSY02B2Abp-
+XHZhc3e?L7Cu{F(j9^*7Wl$Un)_%X8+;Ifi-~8ym}K&YUUA0W<*r-o1PC;lqauca#BWPyjS2^IpuwFu
+sP|15cQcA<*V5TNqOzJhdhN_VxAEu3x|YDa;}M3jUBy0Ava4&2R%=SaY^ot)l)$Mn-az$;5m1>?z6t{
+)khTj{0OvsT+z3IYN5?xk7u4vQySK2q&o9?o@vv@F%+GX|>vp#KR)g=dD|}3L3y4tQ%7O5HzHur0}@7
+I3ZiBSFaZMgV*3W?7$oK0PPRlF*b$#V62ULfI5IWfppOZ6Ye)DogyC}9}|`P%AGrLx{lEKG`>^(k+1X
+kN8u{~^F`o8Wy@)7fnCL-K2Ujck~bj(=%c`6C62P5h;|e_Mf?BWd++hL-+o*4f$zNYj)(_d6QAtVwvD
+Dfawg%vo!oE}IrJWPs=pEAH;jMK{!@KifOph4n7{?)jXFzmD#j9-nVF((rMfQqH?%d7Z$%DK4^S^rCX
+f@90or8e@eu4t7iEO<Kz~m3;)@!zn;v(Fy90k`S#_3SXW0gBpbIjDy70skPjG6pL_h8P05=5?2ki{nB
+0P{E%!vTN9ds>Uz8q_eD_mV&|19nf{FU)P@w*5#DD7f>bb#MzgQ)Ko^90Bg`r3Nd^acJX8)Xauy$<9a
+vIL%^4lTxJ{y)$6TKGH5KiY`8eQaG?U}BAZ)TmMX`RAV(^6kub1rQFh1X;&B`T*dr=tpQ=yX-%}UEuF
+L|E0cX0pt>W5a>|W`_X=&twVq0Ob2+oapOk*!V515c~<H$0CX#B^B6bb9WtZn52($2)y>WAKf&FBzjM
+FnJce-|+uW{904r9k5b;45!h<&83)w>330f4~X?*(1UzGP-@CPnbzl8i#|6Qcazivm1vgWDi?@%^yLp
+dlqIg}aNBIpvJg90tUo$~lA?aXcQcV7Qd=2EvS6O8XM{=qx+asbE{%-y?pi*^@0Kz)V01Ao*5lE>A5B
+klr!=k-r&CmicPpaFd+#xH39AP4A=(078zm~(>nC<kSI7v+F=wE1Xj(T>raLg+P!=D!<vfj?yTc5>d3
+ygRo=@K@F?z-Q11StmKVNn->%l@<Ecc7HSOcyJuaz5m64Jh1QKZ#k-m7f|Q2ZQ8VI)|lT6pEm6@W8sy
+ONw&Y)bdyaN*<^o{=~5?sr+f=!v-I!4KVLSvWV*<vn`}0gO)uH(Ae-T`nJ=4MGF@QS{%%Lb{=$R(J&k
+4XV7D17V!e-GAHlUc^F_Qvqf&Q0u%QQe`Ow%`O?`f>*=!y}<NYXV^RW#Z+s-iNzK1!<DboJBiu0*wIq
+&y{q+`QcFmwt5J2)S8kn=Bo5xCqA2hiHy!^1;IbGs>2r>oGO(U|ipwYTNeE?uN{=>(0>SJK#a5{-fJ4
+TC>HM;dQjns83ilVL3sdOoAPmV4|n=ZAjgJbb6f|EJsJe?PV1t0>PZXMMV|*F;%Am&g1!2`*Ecdz9ME
+kB~5QrqDM+&jtMo*2J+E`}q~l5C3vUy50_GryqOlv6N??d1e@Jz}N}%2lS6<*U=ZDt;Sdu;}rP8j%TF
+3MqdaW5A>Z_(}sQrYx2;s{Zamy1JE2ai{=ZHl=(g8xyTpeXz)at^I+WM{6Js!$Pbdf54y7TmGv!M)Os
+$}kq42#^WKNLdmg|O^j|-oKfzxu946K=7szXX<9_5k`GTZ3O*|{<eU<$K&hiNT5cG^8WPV;-COe={q4
+{eHjdO=N@54a(V+;&j(En^+k}1}}upW#xc_n|qm<y8L6Z$qGkH;ll8+1I7N9d@a>x7<g#3v%^UL}9@<
+22vTnl)?IBxilMGM<HrvGv=tBgI-G)^o9r=$t=vuh1VS)saV&tfPUh4>~5uqu*AkeNghp_{UjST37!L
+T=1-u^%JY6O6###E?wYrey)uJ<Z)OXc?_3zrh&3N*3r|6{Ar$)vSi7UVa|9UZ^$`JJkYmcO%?hDtO;*
+?KIad0h8KS0JeP0@JtFDaP$!`8bj}~+iWxIzWaa1QPjc410tc*{fEReMz7Ac}Te7|ndM2!gLLQ-Gf^M
+Tw)-TN*FTrz<r3)N{JRYvmeb>#ut}eK)4jAQM_C&bQcR^Q#HBGFIEAohSap+f|n-DrKIsaLyeMDY;o#
+Zj}K<)C!n5?9vBxUK+rNdAcG2h4d9CQO0@Brp#tI~zu5c(>ttG*$xZ7-Mg3#b#&k3d&gV2*O|0FQGU4
+c)D?{G)C?`skxs)c#LW^aGd|U~UJVpzQ%KuGqig`=3q_dIGFZuano;S3A`S=$UM?o-AL+0gsSJ%6AmO
+sq3}tzcN2VJ;a#bS<m9Ey91y8NN03(`#VC%0$p7Z;V@g)H99@Fw13FoX0xTd^wLYipc}$?9C#@6YGvO
+b@BlB+9>EU}^tW$6T2m+LlSe06tGE5f7<=Z-nORRf@kA#0@YGXJiFOa|F8V&S|Bwan1G0#`opnw>ojt
+~XJo^RJi8}I_5#i94-7bIhDOl&gnl5;Z`4!bg$69(_U6@ifD2rW-5_sjgWBk&0U)0E>rA8iKoIL{B*W
+3S7`mRd;!-fqz6CNHe)?J~SPESwgPd@pinBM^>r7cj}N|X<HbJ>1^AAET-|FAGZ>;+KdapjCY{Jm#$c
+*VI>oHsE32@4AgG?`2@K*#Lav$MyI8@ETTR`bb|CyRC91q&94wF_nL1D?YK|CPOs;78p)84uE=aG#NE
+-FE!fqeqXSRPV(+5Pd!LGSDs2IE#-QIa1Kw&{}idvQheRWp5=VkA2U#|G=O4kGyI8aRItStQ*I~#EA7
+*;4o#%6v2122a1kFkpX25S&;|oC#o|tGA58L=g}AmA9iET|3`DG4&W8>e&NK46L&+$gEji0Lx*yd1@v
+pwFFW)F;JYG+${rELq`vAm;^)68t>rX7|B*?bZ2ZyFL0RKR8W`Jw-&o6q3}P)EdJWVMXFUSS0PO<e0T
+=KB?Kav>;JIzvwx`&g=_o|~pEqw_%F8dmJWQE$qnv@mgb5S4pPwHeFkpbdO<DI-^g{>-xl!5+>`Q#_z
+4ve=<~?_ix5yuPqWvi>ES!Y&uqKZ_A8ihF3!_Jm7CL6s3xvf39w=+P7%xMof^j?1u=G#l?a06G8kX~#
+1p1@g++5K%V6UpOu88siuR%B3<fBKAzVavY7WpF|YHLLMUmqUI82H_H-{s4eEfZ_SXdAKSr05j?V%{Q
+u<fHVzkR{NojKNX&6ul&HfQ&)Mf%T_Nn>H!?1pjK@4*Kt84HWGR+CG%?x^?Rye?Jm0N|>~M@>d^xYvT
+PE0rl&T+P4Ou>JA0rr(J4&dkgrZZ+4x(-F3uc64X6^<xhOKt7;HW>JK|K<VW%txqSKZ^($Adyhd%*Pi
+xk!`S&ZYyi%*(dGpOTFHsuVufP7faHlZtt5&UwS+Qcp$8Wv$mMBl94?{Uqo&Lf(KJ;m2Wo1}ry+LW5C
+i%O9dWg9(<n?WugCw4m_CbZy`1-}OBB@PEe1wHeV+^N^XOudHwk)|^nloX1`WX#tG<#}|j~V)4<b}GX
+%!!=Gf+;^rbKp(C+|mtSCD@3z3H1oLD&Kb;-m;o6%9h54<K?*%=8u@aVy=L>ikjw1O9(IMS}`XF?vQ`
+v``YXnF~+$}bFde$NNHd$j=2}+ewg=R-l?Z~C&n-sQvq+ZTX-;s!FcigXQzlXoS%vt(wYv|5in;1|4>
+fogOoj}n;(xB<1LK$FxJCZ6JuS>H{M)YBGSN|qaWdc@*6Q?gwS)N4Fexe?|hkWEC?0zNsQewZpXX<bE
+jG6z9NsrZ#iE}e0lN37sYrGdqkjDLs>#z>W;+^m#q`y{#oN4V{Q<;0sNzWL9|=Y$zlJ1(wE|ScXk-Ra
+Qdj=C+1Ma=_3CtO8G<f(AGmvQI>-T4;DN{JB_mZX8TJ1-eY~m{?BF8`}6%Dy^8o7l=vTi{IM9TDf@BI
+Zez@a@<4m)j0^1O%bn#P;g#}N_9P)K^pnUR{SoE~NE_`k_5JzI@zG8LZ=?fWKvx8s&}U;k32`ycM&G@
+2=gwMj@yu@gW`s8ZbCZGH29z+3W$=Q#3v0|&DsOSx6HZ)n!3mt=4d<=7GMuZ4lLK(tnj6C@oOr`oYwk
+>~mRcvAwB{j)REcxe8p$D5;*_<<EF>i_&zw0fV}@#aW=2+CXh&;qmLYHaL~~|JUXRT5@wwS~*=d#@<F
+hjjDS4Sar}}qPWu|1Mr<wCC!_2vP>DgJK9sPT%JBGDrs#1kma;<sd&XhY$El`R_G4ssht-0xz8S*>1<
+eH~gDHn5ULT>uh^bGR^bDqP~**jWXNJogonWvgFR2ldU?U<4mlQlJavN^Y-%9<WA9#=+$c1%mj$TN2g
+3+dHB<eJQTHJp1$uUfc<^l}hQ?;*VueugDPBt%3-#SG{>e7J1+=l$<{M<;xm>Y0bSnffku2Xz;<pW38
+OP>)xyRBu;*uC7)$@_)ep7yrQllLMX&cs*caz@dOs0lx%z1$GGx4ICS24tyeTY2b%}Uj&{Hyd3DI@zM
+0u1Zl!G{WXthk~LE_`I;il4$WDOTTo=sprGkNuLZpq^kLA!pbJ4vtJ99uPS&o|ey8oJi_(3m`%UMg@1
+|d)PYNC#oEDrJ{8X^Jp}C=h;bB8BLxiEfA;FMgm};13c;4`?VT)m>;h5osp~B#6>}ecg9BH%~XBuBM4
+hR_%k`}Tz<a)^OA<aX-4#&^}SA@Nx-k~1tf5d-Ez}UbTO_}C<%~j3snx;Yb26YVT7GwzO9TXq*Owf{`
+RY6yR(zF@cT<vu2Y;BR&rv0~eiFSqdRqfl_4ce{RUD{8zUun;3f6)G@y{=`tX1X@I_PXvmf1OSjs!P;
+8qD#?D(9O{;)GgD!ru$G=syn1Rsk7^v>mSf}*GK64>!;{f>)+RJ(Vy0P1wRmc($K=#-Pqsww(*GZvhk
+)-6A~4Y6_Ot^FXZWvRUsQfJ_>0X+CH>8v{BgIVS~bkhnd4Bhpi3U8TLciZ(;u71HxB^Goc|FOKsUqb&
+-0hdXu`Pe~^EKe_X(ffP#Q01C|E7A5a#sC*VTB&jC#X9|-gf>=9@RObuKeSQY4_>8Ke&<&>())lAdO)
+XdQ=(v)g;YYuAe32GNKBxpp?*dTLIM$nX?{Gii8<w2K%rfDD7KC4}+U8~)rJ*YjQJ*~Z{{Y862>!NF_
+yO%iOt9w|d)rII{h!-Pt<8{Tl7l;Q;-$<|4YxN=e!TL%1h5Dw!J%V+?*}>C-OM`a@p9($`TpoNW_*!s
+vu$!TYp_ReMaKE9mp{K!M2scC-VhsZfLkuGf$%a%zo}t9>6iL8R!)n88BnKM}TMY*cM+{#YP8*o9k?{
+dzgt4D7!B}eCWjtv7+Q>pYLYjs=7-9{1G~~sQ6(PGqJ_$JzGCg!w=u@F@g_ecx2@MX53mX%*CTwfip0
+KlF7s4vTZiWSfhldXdFDCB(1pb<EKQv?Q)zRuf>gUzV)UT>r2ZRQ!58NCWrs<;@sY%ngXdl*Ub@96Ib
+glJEf(H_|EsX7rPa9VltyG#FLPt}6n?k+AXM`^gUlV>P92ZEer!-ru@1v>yH1!<yo9dnFvsBx({{8%?
+`Y-Wc?tj|<NB<T9T?0l0%n5iS@L=Fqfz^TiH7hmWYRWY(K@SIw3MvXp)|#~+>psyP)3woe(T~wj)W4=
+*um4zoNPj~Ag5eFr`-Tq<A5&dX8@0y4#z&32jh`96H#P~mH>6|8!yy47VIdPjvWYh@khHxM@_opqkQ*
+VI(9qDB&~c%Kq4PrD4=oMd6?!yuIruPE!nuR`N#em4^+EL+^<{OSf1-akO{}I+^SWlg=9s2J;~f-8(!
+zA7bP@V^{Vx5tdb_?|@TB0S!Rv!J2k!{p8+;)6aPY6e9SosVy8R4sRI`T|h8sp1#u{EXtTU`Pd_eWPq
+p=5-<Up$1!;Pbiv#4%=Wjt*>XS5qH8Y_)gj8(>JW1o<?kOZpn!$bCm+#7195_O@v@k`jG@S^Z%!w&-I
+9LCm?F-pBey-uwP@TYccdcb1=3j>w}tP1!t;B3If0FOYgz>a}^0%rxj7q}}hNHbXTBDD+;1-%sXdeGL
+OL)2FN67*Y8D=OhW+JV}k+A-Sk)IKcHZr2{wdQc0np4zUnRL84yzw27+@71@blISj^y^nsN{t<mLl}f
+IDmi`0%XZnl!pA1(GHw>GMkAzGQc`3v-v|Xqvl!-|d?i3lW9_W8Mpi|($z?{Iupl!Mxx;<37hjd3NkC
+VDiREuKt6ZEU}pXkQ~Zw(%6C@|z0ml%IBjtw=0H4Qh04+{Tx_`9HcA!8wA3{V%S*Q&etKk5IL{~mw4f
+75_OY7zPb4hp;)I9}66>#A#~3)A&A*o?bFnuG?0CWV^9aj@@nw3+_z_`mPJ$$zu|Hvb(I`=tL*{@mX;
+AUPnH+LhM>eh=sz7#8>l^#JPww*~$fcq7nV6QvoZ`L|}1W~b(&=7FFtL8(E*wWGBYw2QRwQk(g+wzs~
+Yeu&;i6m$sg61+0_HEQGi4f%$CM#^v<(ND&D^=|cX^#$sCI{It;`}hy{pX5K&e}VrB|M&cN`hP)G{N{
+gmz^H($0lNajHRCilG!v;+I<7shb=M8mk0NR7K&{8a1~v5>_afa=>HgXwl+q~wvHq$4IsO*^WB$ke&l
+xTnt{AEf?#AZE)>L18sV&wRLyS?zIFh?j##Cd5(L()7k#Qc<IV{arQ^O{Pd4_ij?-xEXd~W#K@SWj%!
+|mYtX-V(ysrFTOQ}<I(R9n;wsYU)keT?X-Qv3Mp{i7)V0-~gpYBvi|1(*WHQY$?#U@dl4S5kX`yUNu*
+>TW^ZgVaHqAbn6skSQoCs9#WAP=a=dcCYq;_OSLCQG1Ho-g8<zwZE0xE7~e;HT6dBI!|45owu&F&WGA
+!6}86QsBcp1G&()C$tLP$`|09z3A!P=;kr?}vAR^<L|uk1M^~UL(#_S)qh9M7-D2HR-3r}m-CEt7x^*
+PmALzDF3%*mgS9d^nnA(}+RIg9#&XK%d)K%)PkknUG`|hsy)Hm0A>s#x6Nd8rNU+T%{>KE(RlGN`dsj
+t#k>shdSuxGG7R2`-XGf`hQDr{^Rc$pEF6J`mU9yT*<ZrHrAg<;QxEe=~6wjyjb^>lUT1OGh#JpVlZJ
+pVlZJpVlZ`Sbq)P)h>@6aWAK2mtey7Dv>_3eOd_0001R0RS5S003}la4%nWWo~3|axY|Qb98KJVlQ_#
+G%jU$W#qkkd{agC0DjXXZTjMdM<6^Z5rU#ssaqedp_)q*xRF#*K&yhH)yl4@Y<nZ9Ag#C2hB3SBuDY(
+PyX#|j@vXZnEvVSE&<D>_QHm&3L7Z?w3TPiZdVlB4Bx!lL`~Cg?{qdo>cV^DK&YU@O&Y3fFi*8>b7zI
+Hv!Jn=RLao66xrP7zuLu4Hja@TH_)qHQ^J@+M&F9aW|I2d6!m@|&FT49eN6Fm}J@l~RxaU4cS>PeZFC
+TJvZk*|O@Zo#!yDT#^%^54>yvtv<-9F>k)c9Y^c@Ipz1D?|6;;A0?ylv`L?D^o-ned!Bao*Is80^uhM
+ixIe2LH?0^C9-U`Ija0vAmya`wXuj-1}6r@ciz<v*U4{!Z;y4DNPXSA@C-T%ex4EtQ;i<A@-gm2xj~d
+`kz9Ffgy3kegnj~E905skF&$4y$W)ML70p$3k|~SGvGPTAnZ#W0MKd>?u9C>dj(<4{~ntDe;tDGLekG
+CU#8r*Sb^ttb+HCv-%Y2(IG{C`mEC){ayQg{B`;IB3jP{ThjYXK%Q%=&orf8r!NcHr@+`Qz%NBA#)<@
+{8kcRae&MvR4ysQM?8BPE<!p-m;buPI37Ca1qmmo2>^05#WoCP=K=P~jB&ws%dF^EI%CtvwOGp$9TLV
+HnYx?|?8a^*o8a3~Y=H=;q1bJxkCDrb%fAvdf+?E1omb~)^K=6J$W^juvId7KV9`)bG%&ivvaa1-0aR
+?OcHMf~GRC?Z%FxI_-_Qm&W7w)F=wX`^mCxfNbQ=3opUokcE#NNv17Y+i+6;aSccX|+`_2)Qjy9_Pf`
+bU`Q$Epa;Z*3~$`y6tL!-_)>44$W~|<dEXD-YTmH9mKq29F$6q%j8JzE<b7Uho_tr*S7jd3;lc$cBC1
+qRI-TqJqEW$av6N0XQxk(__6`mlYdfcm5JialSzY2<~Va?b)DlbUXK;)vCG(u+0ra&wsebht4t0;y>3
+5=Leqz`rrTcn1r!}F%JJsz^M~%sp$9F3AdyYMI!No$1VMZ@LKpYxx=a8j>~SXLw$ayzA##dx=x>Me7k
+^2o6kdB0z=FCY$|t?Kbz}!!It=@O*_JpB64@cungAU>vYCz;%$_o7rIeN3jvXBf?dZTdhG9YpOcuFB_
+hp}wjVGc{0v<nt31}i4sUefs3RMD6JQ0Nao&B|@#A@}B1|Qi0Sfg)P5q=96`WVy(y@S3@hQ2|iP__76
+gg$=?S^>4o<TI%jIRf2x533&YW%oe~XiA14WH0T+inEs<tUQc;>96&7LwT+7KJLJ7=2#gnp|-9A18bW
+IwcQY&ldTuIzAhUj)|ih$a;cPWRMLvF_dsO%QO#IH_A(q#!v2KkINd(7&PVRZ^O16ReO&gDz0zIMozh
+%s&fJDgXV8{gCSS^=SGRq)2`W~{3qY4S$Ys|W*j$2^H2cX$ne2cZ6V-d24xrMDHVXo22O`hVZKoig+L
+J7Lb|ZlmbNZSA^yNnSQV&iHwgiTd%u=A9V2fhOZPRVbAxkY54^R^uVl=qKX`d~J7dR_mx>gw2bp1s}Y
+`S_ELKAb_a$9uU6PvJ;-VSN?3W3i_AcGleQdwXlo4UHq2S7k3gDaOfXCPQ0g-tIlDYpes{81D}ZOaK=
+uPo{mat0O`FGjb0x{=ZR`v6Ls=_>%7+lDM+A+U(~^c7e-R(I*T4#-+KpH18!Scch%F8U%;YS{J%p7~z
+Ly!s~q)@>~us2PC*9OyS3=sE=YkpaEVfm#u01_ugppiw6<l=)AHT!Z~-^1cnM1S<dCMn3Yze!!)S7+L
+6rt^zXLG7MRsfWJ8c{7e*p3SlrA|8a1hvv&jQt0#)%-30ag>$t9aU=-)eWR|lS@Zkw%{UgT!pzR1s{L
+Xp0?b40>wOHK%jeakdP>eiohHk6jW!ibe?}h>)5#n!!_)S2A1V*(OsBk`v{6b(piyfTxz~C=5vcbPHE
+y1hj8L^p8$olCxXmM$No?;H)*r%n4HCtu1&Lq}sLu9=NtUR~N6E3wN|4sFWFKzjrbKm(%+E9sX7i*g7
+{tQMMgM8}xBuT9C3Z($M*B`#}lS>W4CpaZ@TYTgT4_U9<j<;ZC1+PI0X$_<U(2MXYs|S+g@H5UCNjMc
+lSKQKQ&dV|gGw6HR%Pr9BDbay*_}*R|HPysux(|ue)m}cLB0p0REArEnG+B?xuI92IG$Rl><PYMAkAN
+aD^M#PvU41nUsXP0GpEN)>R!2_>f`@DkEpSeZ5&S%8DcQnUaVAz?(u0`~^yqqNNLk{>dF3w6SDbDIm`
+D+@n(g%KbcTpD84yvL9}oijOVy>qc-Dbl%GvoFP=F@JF!wgkkck=Mh}(yG&S!akiscCm%(M7hc>s9g_
+lS(mx@{{A>PO=kr|Ce>{)^K5g%;(K()?nJa)DSQTh>^R$L6qd0>dEdq5ZgE`<)iu_It>zHIgkwK`e9M
+evYyqyLH{CuCtWp&lLjWfmx1mvIb^+&l|WM(sA6J0F=afp8(1Ko)_@}fM`Q_)gPwC31%G<Oop=*7}`D
+@r$dnAYRYpg>&CI?4Vf8>djR5c>wId94(qg!oD9!MDn0t1Oisz&+x%e(V<GHgVOekS8zb?UzB6JV=nK
+!WqyX{IsF}^I%V><odfei3fCG98_>mt-BF_(uI>#T*^iaequ;b5Uveh5D7l~y72sU-ei2JRy3dnU6&P
+CwS^nsH+aMr)lQgLh*ldoh~<}1ktAuw2uM69LV5nT@?9cz#yE!OB}5oUG2EO9H&WhgdBhFTq77+EH{c
+ykX)WW5Y>+H-X<m?P+KMgtEMh9j0o!)tC`6qF5t!L%_L<tqr3dh##kp)K_F5uE1()z835o=LA~W!RCs
+e+rP71;){<6UE}F3!E`I=*JMfVnuG9HWtJsFEAj!=Kq?BC_RYU<4j0t!Ue1t7qfY=KIP!D2`kyAP41Z
+}iup(#h>bGY3H=Dz=*Ce%zgShsPH%;G(x5q|+6q=F6VLV|2M-yMwoOoX3jGs8;mYT@qhXVlTI#A-2Fq
+#$riTG%$6<&Fm}C-Z$gOLL)d`h_Gv_uV6Z~{N8+K(Rbj{U_wKVItCu5Ny^60Ixk~byt(i&J*a828_j`
+7~rNY>HwvM|8khm65@4o?dltW>}<?HbEgL;BPR{@fi4Z;3tc$>NJH))>7K3zS_O$}eRC29sOaxbKt+>
+wXfpt-&I@_LLnuQwHVWxH7Q}P|66v#ZZ}~1DYY{ws_9yz?_=~<dxNevdO?=$xa1i`ON#_o!&lyO<fCi
+{uYb~IvGv8+Q>xva(K`u069?A?GT;>sr1Y7n3gq-ffUH_&<35W18PA!R)?Z!W)hZt$jK@bVSUYQkzlU
+twgWB@^y|**E0euEK*D^YFnJpUF_<UhOAqN~V?v)mYE=ua8etF&VwC}^YBG<*03>><CyapC;4U$^ol#
+omd>5|HZ#H0{6K6*bG+3;9ji*jwsn0ZULdO-uPr9%b6a9o@I8Rm&TbKqQyZXu&(){xPG(RPfr0vIuyJ
+RBjHb2YXPRC$kC`AXhlG%9;mi&u`n0A!e&cfp$>~|dtnEfuQeC#L3u-g-5{>(|#8EcF7A`Ho-^=wG2@
+SrtJ#I{|+qp~pSb%<Kw4enD!j-7cU1p$JA3!2P>hjXOxglQv@fo!D>CsBOgs@wKQkSt*c7UGOvjB;sQ
+7>4n{!D@?^_963wcNjK9w73t2z1$X%^bFDIENK%Hw;+ntNAoczF}Iq4#9$`O1FC`it99gGPzW8$^C9(
+K&0zf`0{D4<JWvEkz?OQpo{gYnI76LoTNPoFQZ9snTbw?JL2h${d<P-`JvM=(8ZwX0hX}<+<>7qxr_*
+cU4OpE!WI;7AUd9=^B+f6jw`0+gPniTBY>`E8FEq_I9`)9ifP)?bN?v^)VlxCQGwTz54@FNHZ%{8mM#
+L@@ML4mcE}U=P0}09?WC|^#`I4`_Mc=g{BSsEPUrW5oq){6}Ujr!mn?z)MmM|4HHo1$Sl#IN=iUkBDp
+WYn!(bwHBkFJx)M;bPPj*q%M^d}FL_ldqxVW&?mp#l{zVOm9AOwV!5zGdJXbD%;?j>sWD%C;MdNN*7?
+PDka?EayV3Z&VQsn5`(-6}XUg4oj>~sGssCA^|xP1a#+ISYAJNGIk%-l|i5*sLi^s`$xa4`H_ocwNF>
+9e$trRhC1GYi@0(LfUgYz>{;}jk9_45-`mqY&7Q%8L`_NdkQ<#^va}ivSsrNaJQ-$tRWT&TprkdxxRx
+n*`b!pByumJIhFFc_ywCN6N1XDLmu&I7TEwRhFv$w!hHbb8&Zj3qaq(QV$Rd$OKj3=4oK0mz3Z^aepj
+huWcFNh!(N|bDNcWP6&qd4TdjTyD=!WaSBk2;U_mZPh_Yo~sChI-qCtr7`C;O<DhOe#zUUAw{C|-6&7
+Cnq&#@$e*H&z`8)=1-5AccrkC|0OFy7B-hnb|b@h>j$-mA;hCql;Oya0Xo{RWpRh`evWr2!avA2+BG0
+aVP=iP;?KjvxxA_1j0{T2t#-AP#i^+Fr!xjh<c;lA!mOL_y?uG`^V@+9%t1K_()T96wca&Zn6-hnZ<U
+0Xr6;hGd^;p2zVDc?kC?MvCKnaxdW*JM<*~&R*&mSnxE9?w$Wu+_q#(96y$<eZ4%CpQy@>SV6^E6fe~
+aKv;b_PHz3pFVn}&96GK2-WhCduSs}{=bXh;S7bFuVlrY^IWId2<icmwZmq8DsZp0xc|4pTkW?}rJ7R
+}*e&6lOF<^T#I-dg0C5GuEVqV=)a0EsPPjY-xnV0yA2#A`P*R(VK7w>|I)@;>OWS*(Esu_hwcY#bknG
+nk*(<zn0hn2MdgIyixgN$?8PAH5ywqEm7q@VG_rmvk&OsU;R*j}qC0yq6YU4eYElShu*1%(QpcTL3(e
+mR)WkjnT2V(!2a&^M$~7$l>e<tX=g+WilYr2d#<pnALB_`iQAcexRke%VV)rC^nfCTD7!_ytF`)zFyY
+rWS6n*2u$?RWPP+1npM@d_y|2|jkhv-JD{?w9@gIX1m40xO75ZPs}RV3+*(8q7Lgx~k6V0^KC`5LH+D
+KPbrwBvs95Uqn4z{M@m?g+rB?}pt1&Ph>nB@fV{^O_O{@`-vLoGvb~H?Dg=ir90`gJcRcwY-e*yGZi)
+MM?5`()6?G_~+fkDqDJ&KV$?v%sk3FjMx>!Eg5vk8QhrtI#p=Sm?)It!3=6p)sIeu^3MVyr91AFVil;
+<DOKptrbiZlg!U#DF~^vH{aZQMUz&7icsETE?k$Jw$PtImWqT>&=3=GQd_!$u5x|V6<fCN;P1m&n61T
+a(|_E9L_xy0vNT&5J;tO!dxJyfE7XU=Us)m?j3e-)via=Bz5mePv{Q2HcIMhFet;|*P;xP#5H@<%2Jv
+Noro&TbSMWiH8E~;>I%kAKucO5_Uf@^W&w&H3R*|_S~JiN8b^pk+OfgCr(tE+pafRt58|!AG`LGiQ%@
+Kavw9*a@HHKi0yy%J2J!`RvmX#q?ToLbpnAX&b!OeXMs=$mn=iv?S${=(rXZv@X;Zr-yQ*A6Gp=NvBt
+3LW9`A17BqlpNd^J+dqh=^Z1JQZ<MC6@mba6NGgp!GZT4IllRL&Xw9zX+-c|eDU(F2)@b}Y)ok=8DNc
+DR*f^=`YMb<)d{V-#_v9;b+ZLzMQ#0E&p!gNFFOF|Ew+%+YPm)v$_vRCGHNw6ALirQDOBbu~OeQQD<l
+E|-`~As`!@D)VrQD{x?SI$B)vWqV*x?k=?!mybb@q~^bXn|I=K4WXBu?_)C;k72UaIJICfER=?TnT~^
+))wtC}Ye&Wfn%zfrosJwH2i>2}LAyaNS&f@VG@JnaH-df%I?xL0nZ!_>wt6%sbfHzxHmjD|{WnhnC7`
+;1y@#MQS~WQa%LLO6084+JAB#deBdvib#&7AB$507TQeY5YMYzo{uw5SK0)tosf{C;mca%J$cY~kQ;m
+Wm@%AJU8l%8SX!ECvhZ7Td|qezeXF~fuqut3TLh}=Y7@Vf#B|0O5xi^`Sja3r`6h5CO(0k_6RZ)SokB
+MBI;YBpS%zfMQ7{;e8jn45!ES*W@gEs3y5%wu*xdg3)f;Ci_VE0v%~auW#o%y0mbtIU-Wlwt90A~3Eo
+>AW0R2mQFTc~I$w;&NN)8&H-uP9{}O)_{<iB5XJoRO^7fm;)F^%v)F~=IhW}2=FU7yc=Sh%)=ov)g;t
+4nXPGDNq`Jr|HRt~0-$+k?0q4~->X3IHdg=~ACl4nT=aC?^Q#yJx@%GUu7rSEaC;hCi8$P~1(>k%Lo^
+;kRZJhhc@@JjHWm=f_M`5w_(BiZeW7v(?oFMh_Z5*xEk)#VBAI;T(Y&JU?jrJypB#`$bY-a+JOqXKN`
+5W-LerazLWQj$KD(|K)k+i;je!3-O&O%H$M5PCpIix`r0^uZt1q!d%Or?ZzsEP1K^X~3EKWQh`P^r0(
+pzzT2|?NuO65|ar2N5-z(i1L`z}GH_B`nEPw+<V!@2Qm4M*N)-L@N|$#j#%?Dwp!t@QV4_zFWyW}5sZ
+MGU1AF=C?6xWiA@;|315Q!WHN(LVq<h;eFz6?;;Hi*3G+rm`qfZ4r?ixre;Dhl;YBRyq_{Zd?9{N3N8
+^H(H1UeXA==AqIFnlA(jh6S2BRl-srMK>RV*5r2~vcxN(M(3GJWJtT^FlgVz^)#bBgy+MxbHOa<3q!5
+Je&a!-twerio!9$AK<H}Yp#~0TFCzXk+O9+faT3j5SYWTn)LEB{yV7AEEB<Zd3rk8zK%Y`Mj<8lWe_5
+pa758Q6y^DAtwJO-2y(;3arW*dZ$aOVg(PYPSzHM6Th^f$;Q&CKK&GXEMt=mHlbW$$RdpXdzF0JUyoC
+~Fob-pCUt#u8I%N1+Am5w^cE^cbcMGstQ^F!~7SPa72w)y*eH83b)Vo3*xdfboW9qYq@qu<aYW0S2(H
+UvR2N!O2gC8GI<yro(TaVlE1{1(KVRfI;X!5M7{jpWYJq1j=KQAmbzBVdbqTSe%ShWTqdwnEm0&Q3lR
+j4s|AUXPFtb<{w47;?A;b0Z9jOGP>Fq{Q@nPwQs%2{#`6*Z<D(Z$dThlP}t;1^t!Nru$9gO5)#)uU>9
+Rmho`e8?G9O=VEZ>H{P8-zRC|Xl9SUSZFFP~G^@$vN|Hbzt!nU19un8N=fZWV;S#z2fT_mpgHRQ_PFr
+ZD{(K8wp-D*L`6v=_&UV`GV+DvgxnO%gsqyNH0tWck@ELK-sMzW*O;TXsda>$kM4l@Ye+%6x9dc#j}`
+{|dUxq?#XC0}^SZdfR<38VoPCc&Fa=@M&<d_Jl@cPlw+Pnj}a?OCLZpqC~m7VkA?W?(G@a{JU^1X{_I
+NS~D6saZZvg{<n9BrH!Ntx#AgYG(})8*CG+e#F(ardjQsx$IHpS5o#?v1%Ju6-`&B=`G5&YSgY=s7BM
+2k!r6g@QfGo1pg_Z&ey7LwMPGr+1DISi6TdGcQv`iAaWgbt2?@!LA7g3AUY}Vi28k>y8ooQHA!6aLyE
+Q#3JTl<g-GeTR-#5{DL1OoLCR#P@?xxegxZ@d21fxcb~g!iz>lwjS_WzPps5D3y`))O(=|xT#!wau{V
++(&ia}-oq^ODFn(s5p%@X-SGeJZKc0bnlpb3igqQbovXO*cR(?GJPU<9RFBjbmk(LIGsQV;%)sh<tW1
+A$9bSAiH@#8VOl*w(QE%zUSjCbI(AXr@6khG;hc$OjC>5(C*gB>`dukil9ufc%4jq{Kk#rtoH@@yZ4P
+nDw-#c=B__;6Y|s^P^h73B+UI-na>fo2@ig;sxvk5Z!ze=vu(O=Z6CJkavtH6tMV10jOv_Jgo3Hkt@9
+*MklOlIj8fy(`-&Txx2#C4fHJ(6?1hoGr9_Xa{*|b;e*)zl5Sv5cMeA$F}YJvN@dVyy4-d(FuU7r7b;
+-H{NZV*)E?_1YxqVhU1H|ugZEkAZ4)pf8Fmz;qU4jh?k8D8ha)#L9m6t|44Je-67FwkW*MnXZ;Cz&om
+^dv)8gqXxwRYj@6BJ@P?pH(LAiN0&gM0L6LEEKdzTHsdBDnQ5QJp((s2h_UH^ozPj#LUG$$1BC!X4ad
+376k7p*8+RR}`HoIvp#okp@MOIWW^*#)`W%68qkfIfXpOdzaa{wfUv)PKM*vma-E0=%`MUtgIGyh9GZ
+?X;qSuJT}~nF%>P=(2&Er@;;r8bm?$0@1!#2c8^8edu22LMDbQpr_V8i#2W;&X=-<a@cE?!$nrUm5c(
+NZu{UJ?8)Cz14oGumN~SRQp=0n@_2P@lNnY_H<mgN#m5|cD@M;02u#Ehi_lQucRE18YyK0=t1&VOTCl
+yLATK!{TFFM1g`kTIfnXFl<gL(cgWo}+jP&R>{cWyC$hz&|+iY~KK|PS6p3p&x-bh#GVhLgFyqvp<O5
+ed?g<o<ac3`On*`z!Nm+3ioaXZRH&OX<S00~YC%Le)qE^@ZEw_<TfA+UY{|6cU=>HNE2#|5o23IuYV*
+#P+7=|ffMsBRnaHfo|9{U~~aO0*f#b-o|PTz_a5`u&`#rn7ODVe-x6Zb*0POdeMc=E=<Y!kgQ|jMjj}
+Mg7Jee>n5&ACSTO$Tm;dZE>Y3*?OJN*sAtiT6Q!n9a4J+FG{Y>M2WOA;(?5{O!@8w<#}pdgZzR-JP{M
+e1-&qZ4Qn73R>8xEIRmV-a$gNhPZ^N@Sv-9hOHbIv_a^tlyqACp+3AL4q%+$e568Bw{NWi^7%&$!NeU
+$;jMnGcf2=&%4&zyrTUUg>4S)<k%9*tKk4)Oev{+8a;z2?}9R{#$$GsM-uPNd%|6!zNiNUPs2j=CU&b
+OFD3QR3D8iyxzB67QOclpRkb(zHwxIivx<jkQ_4rf$ACoeOihLQo)F6y>d-eh9)1t4^)O;+`Ub4d&Lc
+j-5BlO@~&Ak6`}uK}c2;3Df_0JUA`vY&kFWO`KAd}s#&c9|`MusT|O++sk(7Kr87y~*TrO1?*$Kz`C-
+mGv!w?U2d!1Ex)9Y%LvBfk3iu6A_-lYz^!^w^2kc0b$bIbqE!W7T{{lK>l@aAYM<$(QGDamF!&0J$ec
+@GWBQ#eLF)C!Z&15Pg-1&{?%Tzah{NkFc~j>84C8POHP5HXd;c;qpK$(NcW_~{)R{&g2gDlKcE#eEtr
+-H+}^;m1}=@U=t*zz4HaArpWY9;@GtuFc4?Wu(413(mP{8w66DUTJA|2z0;AE?a}CPe)9f<PIXPT#5P
+7O`7c5eHgur0>Qy<$nw1nls^w4+Mv5Y<awiUqDGb<4tFkkLq%)GjlZ?`BWeVZoGTPAabo6k9rE4DG`p
+NCg6x!!6t#8R;xnW-2IWv1~*I)7xqgQb(q-Xw_8C%pMjWKL)7!zaYboowlym)k|QsRxq8s`bbK4kRh}
+;7<|$6sre}%1rfuNx4itkgN>XHz-%q=m|t5lOZ30SIrI6p%UvOnUBFE`V7;7COpBT7QsVX0^w2~z6-+
+(==P+<W|0L=zh!KnV+FGYkq!P5^GBZuLjG+gWhm-&Ocm(>Wvw0^W9VtYax;P@<+k|4cZ1q|iBrS*Ne!
+&FZ{WeUwXmbQ?ZtoKvPFIBDMKIygkN;tEeakAq)Fi-BhA<coHeK`7GR}T7|a#w35zmVJ&~@!GGbCv{6
+Gy8wM+bDEv`p?C=wLpf8e_Skb$8dvI<3zrl;^1v7nhf=rn+d`6Tn}k725A!}X4sKZkc?vuoSZi{+9HY
+&Jc`x~|^aY!UQ&TnR|#+W@3}Mn6HlI%IM}%Tl0akAUFvDN2H_J%LQ}Jk~|FO8V!TnQVlIYmYJro16+w
+7K3&jeLz@yU3<h}Dg-?t*2?H9<~_-Lx?o8?Q(yyf+AJ`+;`C>~u0}FH7SISZ_3xZ<ThUFf6^&N4G1t1
+78n_9Vvp(UUf9LBY&^F0Ds^L<jE19~j2IAGs4v5g1Mkt=(`=n>O41~>Jufds6iFi`)whB$s80PX;*$l
+t<)>x^)qp-2E8y}G2r6Vi@nzvSGvOSlRY_h8u)2YWIFF;2-wk0`+C5Jw$WAjoE3<9}HW@V@>iV%Fze3
++;!c#Z?db^Yi34|Lm)uffn{CTnP%*KKQGPcSkWB#Jf7FazkHvg46TF3H?=fO&}koif;s5Ia1#jdn8G<
+1ZMePPgbGqn1Md^Np_O?!x`-CtgoThOuVE{5F2A$K7L*Ct2xj-(Wbl4U7ies*4PqdH3%F6JH*3Br~1G
+fTznmG?j;nc_@#E=JU|SJhYI9#_-T$9vZ|#q$0-X%=A<Ti{M6IbNXtgQA7N95dUc=BW3#BaW+bXnI)O
+r2wBH;>Z!oY!{#QG69~pa#$@_-2bwXr(s{k{QM`p`kB_2~jpot8Xm@ZGJO-JSQBcZYTmfA@FjcM-KqL
+W(Ke!U3It=~uVA+__!ACc4w2tBRyRTq1+1JeswDW*Zf53((<u$a^jEwTFN|_BW7u`R+C^5#Dlgw@!8{
+<{4GLhgswBK%|8K`F2*1Vb+V?K#e=*E+12s>l{pIL>%*W2lB7YQ-n9Np%9m8}#Hjb!%EyYLa*6)=Yj?
+OHNQ0Etr0l4;_5bAED!9^S<l#0y8_<o@L=+!2ptiY$EFE37X**E;dZKeIW}<I6tj4?k=Ho@x=Fe45z+
+H_@FQ#@>Cd_2skI;Fh)Qs+Ywnt+KIIcD0rbMws)V?YX;<JVybu*d>Q=amcPO%6yn+zKAoG$#O(D%Hc6
+R%#2#{3hUuB%s910x6Nc>NC%ABrt-x5S>kRikxtshDgN|5)6@67a6Q(L6?V6euIp~sn`3KmKmQ45{W-
+$6B%Y1@obONn+&e8_InTG?|82g#`vu|CG=|=E-S*($crP&*2xr>q3}?VUnM79O?n!CBMKR`r5MLrsmW
+|E1b(Q&Sza(P~-!HkzE<Rg#tYMSCM4AlRgP*kdjS)$#aS!u~HO0x-hy8-x3wr5!<faV2tF0`JNETzgA
+I43T^a!lS6TPmDW#3{ABr>24h)X|-fa>w&S{<Dk>93eCl`^g<JL2QsZ`H_@&?}0#oe?^=yH)Kl#Fh#s
++xWxg58vc^ODpIsgP2*-s`d;oJA(DSfO0YM)LG)Z)_R7`40M7SOtdg3?-t_j!fuEmz4UKrQ|{tZQLOs
+GfYPXu4xffo)*w~|ViD$POmfUr3)Xxsps&>&NT}3NRuHRdnO=J7wq8)8KOSa8Nyq%-TTIkOAKJN7`Od
+f)9A?0@OTR=w=Gb2vy7KcA%tEf){`E3?1ub!Y5A|FOB|HfwaKHaCQyEvV^=oykZ*=|<SQZgr4~!)46R
+SOwADM;`TfD3hS3?ofYG#EB`@J8-$oR6?%iDV7Y?wPcXuId!<yx^^$bRrmY}$6z7$z{#faJ$h5G-aWj
+{(MMTl^;W$LcUJ<>IIWf&?1$&MbB{+I7#2e0^XfkNO=(#oXLKd5Q58qz=#^fQjBnzF<C1%hRQ5OKE<t
+GFY!?cIJkp%Hux*O3S`Ojo<CA{*N;kSJRK&+*Kim*(A`(6$C}BN4G6}84G`gkpdb3+@Es;01^u~06dW
+6BjtH$1W4GdfSwsf=}`}(5oH7q?&bK%QVU+#1r!n(wi=0pE&v|R^_qK5F-1Hk%R_$|4`eR(>?nn9>v@
+TBs}D|bK6jCB+YK?3lgmc|ul@5Wfq8f3NUQOv7dltfzBKTJ#6UU2hnvaAagEHv6A4o~<nAM~tEpV1?#
+=<)(p*`5ua}n))Me4rgL6*rSY(I#vZ22442z#M>9(i;%=Pq3)q>FwEhr;#bu@P|-DV@2tNztw#4@!vF
+_6pyXYC;mSYOx~B7S~oM((a6(&0zD$Wb|5)b0rvPDIz7UO6<kmCiA7xyweMI-F23hQcdusy|W`+^3l3
+V3+cZpA^Y{;&rb^hbO=58;QB@iqj%cWNVX`PGN#WpacF|L*8}}ijk_il-yw5;`8;Q_Qe*Ncw4LOniWq
+~fJpsk17EdGq)8i;Kev5K^_&ZI@#ThaVINw+W&nWzn#+Mm9?yvp$lRavbK!nLQ6K0w>>N!I3R(x5o>3
+RD;a2`gbY>3jWWy5uj*CWORSSD*6RS{e@wr+bDWVlWAdOBnfQ<27AutF#T7l;K`QF(?ZW1CpxK*ec-I
+jX#0t@K%kW0-QCkAmXrjsEQvTIM!+#lFck(UZ_LEr2lj#yz0=P1ktg*AG{>H?NH3Z19|c6#&>D>sl!=
+^@l29NI;}x<@BArZA{1+I9%X4E?IKbh`dfVgPSG#0KzZyian_+<~^NxPfaTw`$S9_9bjt9M6E``GDhv
+j*uKz3^o9Y=@3x&$#lD1(%BVxGBAQ(&#K1#balGjfx6`tFuYw6w~|RO>u=+?xq4T`po|PXZt03x#i|g
+a;zFzXO&=__q3L!~Xoobl<FMMC6e_g8fj)MDN%XpX*seMdKiic-^l~1W2cbYZ*+R2!Wt;)`<>)832ts
+$E9W9w+RTyPk5JhKNvmXGmn__@UNTE}24i#F0hn^VeA?qRgo3l|@&~8;*9MFR3bwJ?g!8Ts)>r>8S=g
+=DQTn@}j^_#J{IfuJ)vN5AxOM$V?=zxAo7Kbt-w~cIJzDBkp<QP4=Saq1Gd(*AUfvr><wBesHR)8*D0
+7}-=NvvwuDKWSnAjMU<InO;)7W7-39RvK{L0xE+(16_*SVlv~(5bx;bifr%?hXx=Ni`SXr-Q6xGwcIS
+GMrSPt`;;GM$r|4{&+bTY>0Ur3dQ+_7G*SZimGN{nPU(vl}4^WutEzab+HnX0>e}WlhjyfH)tuMHR+h
+R%rMZ$^_=!XSQFJAOW90uRhw_MA@<KFzPAmc-DT&=-8<E9Qj}zWc0>+YL2@c0%|5l`I$tE39LVQ^6bK
+lV!KJ08D?n95KYa`CdH#s*e`(c&W1gv94dPh0fI#rySJ{iln#*$#K6DfCi<_*F&kfIZ`0IfD@SyP534
+c9-cJ0}iy;5u#41bUVes~smX~!Ry37!;xI`O9`(Ars%;S{2?xPAElBM)vzG6~{*Pdbp8!oQ8ODIEPLV
+`DKky!#AJ>#GD!MZfNm)Fy8`GG;BMFl=%H|BbV2<j%li66|;muwx&x<Dr}#<1b1!A#bZTEXJ7)Cy}k`
+%K4axu%-cQd*2D3h-_N~wp}_UG^e!h|G>A;u&2fkr+9M@oyED2oXxp+bIyGr&bhb5IrsV)=bk|uX@YZ
+qR2=8riwAP<g$ZVRbBuE@q$_BQbEEBuK1q>tM=O|}jWXu_aPdIqy?7w=?k^9w<4ZlE1$Xrg<la~CvIA
+Gpiz(;c7X$ac2mmK>_C1siVBg6!G4_A>KEb|cATKz_IheEj$$R-xn7Z#1r4+$ybSV00O7@kiScOb+rC
+SW5;bvu$lCCy8LXtg-*jAej@XFb33SE2?YQZv*V5|%%iHuLh{gY`<ncQk6&9o;4)b44P_}K==>RyDGu
+4(os19xGDqZYonPsYG^EYPP+!azIj!jr9X_{yW*xa-(1M^2b&y#*_1m(;yuiyqij>^F|tt)=+GS?_)c
+lJD|Di}T6JPX^55nuqO~mCKleVPG`WW>IX=s_tpb8IyTwi=Idb+bQZdTEtZbJk=KxwPC1dsvTCf&YC*
+SuHJ7EV%R<1*B!Shthiq>d*%R#bvA*E<&ua*+MB!`Xm|+C-G>7>l9ufU?M4sfQQ!Xf0vKJN45X?9kyg
+JBZ!U8(^ixz{VCTI${iFv}wH@z+sut1B>i)6Q$uqr<sUX=u&{e$LbGc<D=spVow(<nd=SN0R5lV&%=y
+&mPjoy`v$od|4#;;Arp%jBxv5lTKWt(UDg3I$j_d)d_>5$ZVN)p#@_EbMOPj69%0nbb)4c)CP4a!P&Z
+=atW)#k;*FKp8!SGy!mX@^C$X9SWwvlEY|4}TXIXlWIDWrvkI%s^H}a&8;*5hhF>$`szm-Prc&q>6RP
+ADWZnjt6|?3#k^>>n=|Y@YX6cWGwgRq)OK`i&ekDr3bq66g;KsLGe72x<QwWU;4tg7`)+m-JWVsPO7)
++kaABP01C*50+gFTC@!i_OXM?tDeLu+r3bU*OBJUKfy)Nuxd!uOL7tKQd3c6i$clNci4TiET=bO(tr;
+1Yvy(Zx?V}ZZ<GUIK=aSi90TZfTVq#9Eu2%8sUz?a__|v;E;f>!y={;BscDh2Wx)Qg6D^4YeRnH`$Ux
+$(@Ra~1ENU8AWSWKF^;@%v~317)NAK6_*e()I^fh|+!NH!YJ8hnhlI%&CAm^@0XLQW`MvrfsZHp+T~P
+hTJ1#8z7`mGx%7i<Xb@g}ny!aZbiVw%QYZLZdiap=Ukuo&irU#;(Qu$n^EGKJNG}_jS;1fBy}8E6rc}
+m2x-qD+~HHuv4-!s$ywznljc?Jw9IYa&K|9=kip|<#X*0q+@cx+-OYGvVmZvm_D^Zr?-GqjEPWc(>uD
+Zh&bcTAHyq@1%2|n9;D)v888jBu>-4gb9^OfsfY{KeGl2|&E8+Jbe=&BZcAc^lEf-B2Fm)zE@K6VhI&
+Kv1E`pJ&}pK2+_Y>2w61Ij0O<AND&v&AkA=<)4T7>yT$MgWTw7(p5T1Uc4}FVNe=#z;je0ZyrRsIAdM
+LA`UN*LCb5BQy3#zIHx>k=TEg8Jr7{>|5dM?jbj~fGL#a@FYWw&cX0YBYe#xPXUMz4gGo_P29$%8Yb?
+A_a_hioh&jkx&awz1R4c+eC^3pal}qyraB-S*=%%nP~q8(2Z0xf`G`R|A>bA&1R(zpd-n#~SMvpKJ0O
+_l1_^$kBf=AEPxmIhD*(DS!BXCCGV<An)rZNOq!F?E#v+3@t8_Yn@^(Wu~(vq`W9UP2+{-tF8|Ta}*F
+}Qfa<5kP6AB=sN=29*TZ_Hf^?Z+C=3YONtTaHDN}QgLIqa8SXCgJOBWJa)BRcXz^QT5~+*zt~7skyW)
+oBcl>g1d%P|r)#cuu1A_VSyw$UEuF?ER#nwdrfVng@RJ$66N^YvhwE!5PkuG-wZKd(rkG~1B0M;~I%Z
+SJAf0M}v;}I!9*Ym_DQQ+6cos+IJI7&GRIcdWHA{i99eo`zx&{-~Sf#E&TCyAb~NP~8pZacLc*H|;~>
+reN@S0cKdy`qm(R62zM&w~LI*`s|3^sy6?qL-XPGq*O1zO{#~F;)Fp2k+q$h`O%0|D4qwa|hm4!u-_-
+9_x~Dc{?DN^r||pjkoN<-I9=<UYaO2ik@7#yKrrM8Qxq42M*)f_&Y`d-}^~79^~+c3wA)Xr0xS*U|;c
+cV(Nwo>33MvZw$&WNrP0`gG;0MJa>&hyc<O}+PpiyK5Dn3%yEYxtYBHYJPwON8SEjAKJq!FjOf9m0Gf
+?HzdO;}w^$i4Um$(v9Cm(q6ibJKAMOV``E^1pOGspn0ULO151C0}*Cbj)n|LP8NTe|X?MVHyn-3jTK>
+j@r!Y?}+bPF%!C&(E;)m#bh(5lI(@(ht(pOs~@L*-x4gIUnyjr_RNileAUbSdj)*NO5B`XT0L!sS}#X
+L6RnGf*b)-;=03xPlurak7~~MPGwHTH#+clg;!_E1Dg<WYU`0M1CNN8P%M)xnma60ePE4nM2rCPiwhX
+R_jw`_4;nTv}uYiq<KPRMtGYfq%}^t97EGQ)n$dCjDCj>4r+a|9KODr7lWJ5>q%xaB$Rsc73V!-)fJ4
+Bpwl=mz!#vgx5V)g3MBsUd{dF}yL`Lox4Slq;ip*-3jTN~)_Sy?zAAFF-+W>#$J)ZxteQ}Y@$-=FZj5
+n7$|X5GgEzS1o=Fv_H99A4K!a1xZfpy&jb^=Qvba85QmrMEtn@?Z_SF~M^~4gNI@zf!^YPKA+%0u^1%
+q-sJ}iMjK9b?oZDrNWTI`ie4ujgU{U9_Qy((m~Ui~g+cQ{lm8!w6Yh{*|zn27@D(N*oW=(hhn&Bc;mB
+2V&W=uNPi<NXV+6HBVRWIu3lnG`HT=LvPPOUJ|RlU;pET7}<bsfY%VkB2=@z0u>cRCc%5;;ky9Thz!;
+xRQFBb=z}KVM{185f>pAWwtrAmes1b#8sd|UgA=Fl8}kHEN(4L?J)+-_-+AGa#>>@BAKyf*5lTj$CaU
+FGH|)cg!V7=nk<&|b#mniX2E$z#^WrR>u{smcKy?+0;2tR1>5jKHS>XlnmO=1)j++dWGDB#+eq)cm78
+S(R{G~J6Xq(InKygLR(9x%b?MksXp=!W!0JDOd{Z6zm<n9;HOQxJ!o&73`NFmwypxD~Ys)cj?TULXmQ
+(TLJ*VpybGU91*DPWj(?h<EaZEdPLh-5t_{`1(!~88u#x0A>T?L<l#8>`+O!oN6VPE7Fu*oamhbYjPW
+YCy^)%^fW($1Kq7ueXoTQ~#!q03{B@jr=dhlE}1D3mswKEJb{|LtTIa|U?xt&9PVrqA(M`d~lc@%8~6
+FtMd~*PUnIl_lAcP3&|UCnp82>z0^BPPvNYXFqBU>Q7usTiGuDOsj|8b#noTI<BUGNDA#$b&rZ_$Ei@
+ET}xrc#4XH(hJKo)k)t$}e)@J?zkdYq%`|=y!WG!u`}tOw8AsWHXZB#3Mpvzk^d`I|rkOqu%mH=7e#e
+P(?b9;P9S@1-cIE2f8w_+lZrA4SlGLp_o*;E)O*;X^8b+&uzhp0Pvk^cT*Q=8|1ZDp9WLQVn6uYY23A
+JjiY@gTM4Fh*Q$>?Bb$#Nz1j2S|&LgzbIlQLd!gzBBzXm9XPU^svzDONg?C&GfSGZz?Q`!_3N*tbyZF
+!Wk#h%VIQCz+aH#IRJ+)5mFX2D=ps?Z5M4PA5297;rSL-*y$7GQB_rIkWg|QEr3wPNSe+qZp>@hCr&j
+surz`fizcdAgjwg&!CK;hFP(lcf6b6$C=0x8<g{)96D3r*JG$p#&wFDp2T~raI+ZF$IQgFe-D}jBE4o
+b0KmlB!7irB*nqZxDUF_y7SIlr4Cv{&9egoOI_*LSY`Z-^w_>v%q$8M7ZC`K_e)EgOjH=`_>ONQ2gHT
+KPTy%va$7CjWOt=%G)bC+N>BYche}oy8$7a;kQ1|blr7JMD%aaE)DjlMl*!fFs5WPN8KF|at44TCb>5
+Zm0#Tsx?0)Vdie1LI>`5xl@;WUf{0d%a4gBOc3MkYQhz4lXGuPm{5pI|--AQgrRt!SBz`4yndK@637?
+C?;wy4Y?COdxJY0lHZ#wu1BVF{of1K2iz<_n!=bWU9e5-qgSU8}ohpfb9roR|-Nb>0CaLGVsz~y8J6x
+WO7^L+q%!3=17*GuUO^qlguWqMy$9t9*l=w&E+OYH~L5mv*X>2i#U-iMOcg%kIthMo9PevEK=Bgl5Hd
+S@H{G$H7;QFyv00|L;uEy`l!hw2D?zT%VD*vtPyIx)gSUF^~jaSvDziySLTBvTSq=ZFRXyrq~cQ*VjH
+QI`LLqY#iNlxqQ7C$zr*MZ9*yQ~?Xk-IKMaV&byF*?%nuKU!<+B4n=A8wJ0K428rrp$`M)0!hvzP|i!
+1X#OzMwA{Y4vIng1V%BTZ|t5u&F)#L~zg@RuE9l=RjR>xq_zh$21{#K<9rm+1CBMknWTCLM6ci=JOl4
+_FJB>Eq)NZCTHDG4G&Ho<kvNmn?ePD_o96sTD3k$qnktMHQ}bfmB!sb@ACc(gR8|eRmdH`%(Xf|61#0
+0Ed_5#;Ur&u`_NsJh!j5T2O!^2aV`DpEY8~HZ+}~2hC_4?*BmI<N`cQjV$|SY-o6n(ZFi>K0M99JaXi
+cX4+)tyVxP~>}_1g0|aD+;2qmP19J}>hk=6dLW6RdT=GNwC>?!<IlHG|o^mEqe~y=!?Vrh*)pQi*<dR
+0v^P!+X4HSCCHs&QpSAK-j$^_>&Ms61hP3BvE;x2KC`vT7JCO^}0JZ&4h<{<OcR$vOZ@#3CC_tdDsoX
+Gymd44~XJ+Fuy@sS=$y$@&5x4<=|CIjDUZc|eHp(K?4au4CS4Tf<$<X$xnh7JaZP%U55z06N5Wx26BM
+b?|dRk@h5h3>qW4eHP{0U*^~)uoJvCFvb^MV~`ShTqh{D+rU?I87Hcpo5LMU4SOBYOjE}b|~fwS8BkF
+#BqehZxr}ehQ$|}jQ4(7Bt1EHr;CPQdZfZNN9n>QUsbgskOivDAURxUsK^@^V5dgX5p}^XB*D&YQEjl
+^g7qgirp$pRVv}M0*N{Av8-rZ|%VdW#ZE~to2ne}aT-!`f-o(ArM&n`1<w&@!Z<sOGO)VXLDIo#l7!F
+`vy^XoguzG|Tcyt)Vv0WLT>U08R^8Y&M&(<jJGQ&xK$}vv*C(dK4$`xP7eO8dI?r1b$fLk8AZNU;YGk
+(G&dZB{nx3KAnm=yv>5C++)UUpLickqZ^zDPeoXNVG<`M6<^|HjI$Bjt1C5<hM#_Uh#~$>Cu~WY@MbX
+z&JVPD=QJ<oUy<&i-=Q1zoXH??1(_By>Ylc5OyRv@y5N6Uy2M@0ziH$3o%T%y<2l{d%`<d*g9bCin4b
+#i}>B^Z?49Lm3u3G&MfZm3TXdx2NUF+3onuk++>-=deBD>4w}E4_>T>@u0F^|2Q%Q3O%_ACwMknGtii
+=NUQ{w9ka13baGn;T;L}@&Zd1j-bmI|gYHy6%INNS_%SS_QA=l`Zyt+J@`0N(FyQmqy+(7mJQ7<i&By
+RyI=lE${`42G?zxnCpMsc|A$J4MHh)!%Cq`q3@oOH7Y3aJ{^~czbh%a}SZnF<~uX>E@=6Ugm2Oh)uKL
+`4iyGyR+V+}Fm+vVEaSap8a9zQU2e<-UIYV&n}25<M)3bD&6IEPjz+=c|#@N?tkAO(Ng1^qrGlOvM42
+`^fu6KfLAt`lK?IPnO?$wy(SDtHJ$J>i$B5gx8R644}a+Cj;6Fwi_;O#+O`1J?<zzx`bOurA<55K*k0
+-JXdDdFU^{N;Ki(d~^4gg7C;V9HYRa*nq$C24wBQZ6tC68gWu`oeb3dk4F4_8@iZdbd)1t2R6WQL)_6
+cFbSCiwBpyi6<dIA7Go>^5^Ke6tQE(g6+cR@9|LRu7b?y=zo&<9G*E2|=n&z%j9l~B{DUAYy-zNI1=s
+w_ZXtGf59l(ZlPMQg`qxkRhHbLm1e$|WJuz&_<p8YgW+HYcKP30mB7vO~;E_k@hX1g0bvN!t>C1Uc02
+cmRu39YE3?gsSSx4fyev3ze_}v||Pv1bRJc!Cst}cC8A(108`LfAN@wPxu_<loZX}i06X|Wty+Uc#@t
+XM)zd!!nxmf{KB*W+1^*Zj8At{b_f_o5WMQ69<)b_T57je<J`SZKbtT`_voPLVC#7Y~(_I%#~1gfA7!
+9eRr*!CxL-w*k$ZYPwEe+|X}YyI=#F*6>aS^lp$V*I`o!UM}S!o!UruSEEI-K$QYic}PM-rdu<5Nhc|
+q=LrSsq)(WB6tyas;?H>e8Hqol+UpKfd0m}khxly0HdrFtv<!)SuBCuJt|gHjq;a)?8>+L{jgLs|P9S
+Bf<f>Eh3vi!PxwruD(^t+bz&%dok^;2nDiaHYWOxj6MU<?l6D>aEJ8h`+G1_{ybm?Q<|HIaOvl=@SD%
+vCkJC)(f&F4WEyIRM2=dY{?OO>Lh8Iqu9Ze_T*COS$I*Zg2rH<+L)5RbhQ*X++IPnObSaIowH+-NNg-
+mOXsypMqdDo?}f($HjbKAqPR$MIDysPf<_(`<MpLi7dZ4&!xuh<BY#ie@x1d5L&8p{kvO8yo&`5tYan
+9?r>Y1GwmUJD4*~kf8X>hL$vrkGLX%Lbg-luIg0sp^SK;(21_G=PVFAq}xgc6dJfjF2M_B@88KBRR;L
+?fS%WgwwylQ_G&rXQTb=HuG4$qhvY^7Y>FwhYn7Os$cl0Nw_@&K#ejBvXEVE<yF<4vEXQrnz#BBZ8Bv
+A1DcVbtYkeSDs@Uq*QqhEFf>(=7W_OTu>Mq2U4QpD}9<M33T?F-c(|WKvtkzS#!~$|_`Z*huS0OELa>
+~`-RxyYwkhe+~Lw^TSld67F0wmo_*5mfvzYoGddYchTBkA%c&_VMMS}B5tY(1>&QFLx1>2i4XcHV~4(
+9-R6bqDiV98sz+-99Q{l}lzfBfO8h1FVD6QB0w?9OO*t-#ja8!Z>Kc8l2Iq(M%IO8ptHx_VM)rQ~D7t
+DZLX0y&lNDumGiO<)Q-oe1bBr06(69)4>go^9%5rex~M{gg}<3+FPuRSdHiGV-%C-seZ6nGa<X-Gg+*
+98#l7Lnt&#k)`gZfi^0EQ1ZiTFHa=2-6ed<>LSzBsCJS8=G5EM4w#SGksJyQ49<fU5Nz)^j!mn8j&PC
+JZ+M;3)`CbPV*9Jj&2B+ffk=|y#4<!6S=#_z(23*{50asZz#gS8RLEL}rcX1Rq^Rw>s_B{-5Vilfe2T
+D;!BaPf$xwQH4N1qCTOi$R|hux0uLDjLw+g#8G!a!gYjKSz1fPP<HpWu7sM07j$3fmJH%jyQML_a<dt
+HIWX8?g@-GgUa=w7>9FtpIO<Tns%IpBjReDd@fUbRTOi@6@9cSf|9QV?2`OzIQ^*0()%~Z<hpQV5_B1
+jI0J<$vVg@hi93az1i#N(92?;CC{MHY3%U24lesrGvPSJw@A}H%kL|WO@oSk%g&;6j|G0^lA$uGSHF!
+-gEmOcXe0nIIB!{mTI)JeES|x6?!2X~0S9C9qs~2X{1%HN#(tWRn8e_7xK31cLJOJXiZl8z4>Nui=S2
+xN1Wc)WEb<~e>W|%($PV4szA%Q5D|OrChZ&cV*1^JCGTKKPm?;bzfHp8}z?6!6?Up_m$OG2_0hQrFbL
+`m$*0bJt&$h@RQyOWCUfRnHGznMmB_O^3sU8*1xDCL~WKtBQmf+oeI=^blPM0<GFOBI9xHK{&v?>m`k
+xJRAE^#gvKuzq9EMvE$A-O$^DWBr4fD`eoufsz&u=_T5FzJ?liI+3CL&6o1IR}<9JcKQmh=6N`=&-Hh
+Gwx%F{*}a3&lo2WX~fOFL(0%IQ`Dos()EP(%zYO>Pm(8*2)jGcOIrOPUI5M+jo`^Y4}mdj^MTT?pXuL
+v4!s045+A!?5CvVbjv1ylFtxfRFpAz?pV-*@Ap#x8oyh20F|GR>wrS=g+vp8np!@_9nm4~>Sl~K2yIZ
+Uob8V@5LMlsx-`f|NO4SC3Sd&t!_OPfP7FFkv)utp_->kSxLCbTMy4saS?FzAG5IT)I=-6pEt3?(NYu
+c$e4GE|*DV{40bD1&MCb6bjtZB%Oc=ABy!TT3r2-H_l!a4O{G#73Q%&o)YDP(89xo;~lhef#Q_*ks}k
+^cVI#rn@}h1h?8_LqfXO~%ktwU_m(m-Q;*ki?oHrRs4Ocbvu5JA7(=l232OHn(7#Hz@1GntHLOCA*nK
+Sdl=#bar3NsUQ6T?YVUg{DNj>NU19+kcl@o%e<Vxen=5((y}9QTdEg50KfE;7MMvrQkP)zlWjn;#BC`
+mnPKW~_Zzp#CC&cv+k7Pr*OOsZ<^?NTWaD&8mt+)`3~`lYR69E57yQO%f9?h}S+cE#M_<Qz1b8YU63F
+ixaKJ-&n18@g0e;5m`v!jSCT%st7=>V$`SSn3nxzbjU3OrD^+gKHN%TFC)A>n<-j!$F0^aXW3&SF3Zx
+f&V3pb2f%I@xpq$pRRTXp(!QyO5R8ol1SB1wu^N+3d|Nu?nJVyY`*QZjjziN%=Ey*3gx2X5joM$o$<U
+1o{Q#kLc@ZxQtt;7H8B7V2OXAj*$nWGVj_Z{Gm_TZbT&Pr(BHWLBC5igd^!sl$(Mx5SgyiLpJ*4_E5+
+qYv(OsBCN$Pf!jlZ-rcW96kHWKCE>hm$94DM7t-=EmuZRwuNSZdJ#p4S~tT_2CvUAHl+H|k=IZBX;zG
+~L(pf%I6KBUFm57x_+oqq##4;v4bz)3w$snNeBbm{QmnyO*~6R+Rcs{p{9WD0ntMk7;ctb{*nH*y$$C
+qCjMxClYBXuUAh7|0I8$Pf;sZ3Je}L4e1#!v{XXqcG6h1)0fGYlv<Ks!|!5)Jq7{vjy`_WYzVgYf(cr
+}v69~vC^oDEO8enx-U_;l*}B0fGa78L`BiH}cY!1(xM^&x(H5Wk%mM==fu!ACK!1LN8;t`*wFMhJ)RC
+M(9Xmhl0?VIKV7#%B$TPp58>NhN*^3MZqnQ-{^M(s6pwetIxxU@$BdOSQ?^xv*`?`{*@4qZ0;`Sdr-g
+1n{86JpwY>8J7O2ItR`XSZFHo!r)H50>4Vd&j9}sB>=TW@AMc_2eixtVatG~#l!Y9!a1jf2exkad$HD
+K#9D_SX}o!v@#e+ic>6ZQ+vhP<&ts;tNW)pa#g!9T%1*5GQ8}DBf}J6bC=bd+jW_;oHBSi4kqP0gZ&8
+M;`FKOave((^V=RH5z;kqj*iVO;I{@NA4uOSI?|}Gx_O8ZzJ`F1a+9i1gMsj_~H*FkVhNw@G2IcIn)W
+G9r7<lmLYb=Px(Ea?qm>n)O|L0w{JEe?LS0fs*T}RxkEeJqYBXIcf;<X&n<^Y1{PwP~M$V6l}d}3eB^
+mhoSE(3IfY<8n%K*}PP(&WJVK-MxBg40|CqPM=rxPIc!G#n`j^H27Z!G2epG6|0|kPY;*Q)qf_Q^xur
+@XjfezOTXWL4?ikzmK-QR>e%OVVZWsfcpSq=cs2#n7u51+T*es(CF;4T3d&6Ss6sHWa%$pP|M~gvmg5
+yH?70$9KK9!Z+(F0z4<YB4#4&<K##SAr#tBUR|gz;Kli?mLk{0IMkdj<21ZpIJ$O|#{cJNzth?m!QmY
+i6o(v*GgC{gM!ylS!Dn0g=94?sh0gmCR<)*RKe9vi1;Fuf^XBkj!grDW6e6}*l`Uc#8I@kVJ{7e&<_h
+!rCm5cd>O%?pYrfGP4CcCg{A=B`W5T7abDV;0s!w*^+xc3Tva{w>!^E($-8}vG;)YVY-({hjVmPU_rA
+@pyF^A_zQan-E`kGQJN<EmdYT*_{hNZs{ezX?BHWcZhZe!GbgMAGt<s=B~uWNHSxz6p;YF2q|p$yCsF
+Hu76KgA0>lzN`FuLJnFfv-_jm0)UZQGT2T2Vedk`*<a>%4|bV&Lv9P&3F+-q&jB99&)=P%Lt-}}#xhN
+qiljb!p`Sy_*<bj>FJnvO@C{l1>_(}}Xi!osQmxB9X?wLn5KL066{!wsxyQ1%x)PD4r8HLZD?dSEP#Z
+u<-56-ruER5vH)H`G{wjst4z*|GA`27~p5o9lBn%CLQo>U@&L~5#M?`;C)+Ceg!q}ZshhF4IN-OxCQg
+$CH2Lb%?i9WJ-ZbSby3v}*UcCmMy?ymwF3~xD!(LFHX7f^@M7VzsSo?gxTH2%sDQz3?2S;zhbfKzbuW
+pt;W9vF`E4VggvS_CtF?yp#fdBeXj)6*<3F@MZLFrI8(fueW~6KV&{nJfO9I5MB`7h?0(jdyV1U0rST
+Bzk;K$XbW0*onTtgeLQ#b$q{3x2?QK5Gs}c@hCF~%K4gCJ#i{9q{+OmArAQnKx*676DI@1(2>s3WNwb
+75w}VEh!>K9Njd7@SRrgGySZPCJ!aMAA1pJE{9A0cu+K;wr9PcqkI{qYxN&;`&k;q3C$S^h>p#FRG{{
+8rk@eEYSYdu1cj1$an|#KDc)CyRHJ3#zb$SZV?itzn?<jlkX2y)Ix~=JMKL7SJCXDyBK;(<D$n`AphB
+0`V8gdp|iKfUWOrbk7Jo)#6!t@l=(_wyHkT=J9fpTeHu|b(AuF4Zv$p%;NB3t%WDD&zpXi!C($!FZ|%
+ia|D2^yA^XAgWPho_#m8kd<0i^XSL$I{3RrpoB+Xq~+fgf?Zg8qHKhy+tvh|7fiVNK_kII|LsYwI&N*
+O;bA<LZPV{o=~YpGluSj)%yu6aJtpFtN@#1cANaHtLve|!P-840_^iMJGQbJiGJ3DfY_{if#>@o-<m!
+_lkbgT&&5_BX_oTr%H?^t8I()JRf9o|F}PY6jf4sQi!5omQ8I3qEC!4%GH!CUK5`IsgS?S}tv<ERQe^
+zfmwhns5#X)zFm?@Fs*G0;o0Jji;Y=l6-x?V85%1bZylWrvu6=}E^Jjkn=!V8xr-zqZ{NtKrHgYC0rh
+)jfw+A|rG|G;1Cj~!H4$R^U|A-R+W-(Ww+;PkMz;BeD`5ge#E2*1xTD1nVc2es}eEtPz9ob6Pp2UsH4
+p*UxUoo)Sfk)LH?1HFv^e_N=xrePj&+~cOEY&iQmyZI?xbU-rWD~Wp?M}+BbY#H4L12G)Ld}~f(51qn
+%cFd*O*sGmjOQ``!Y{fpc0I)oONznc@aQr(yoc8su#1G(vO8C{(DOgUYg7tNA@dz|cxSLBWWI&HWQ5E
+&;mZzx*!=bqXb@1kdfdF!CYNjwYdq7frBbL$t}ZkHOVqw4J21WSIFiQFp#ZYnGjhz7>MBFsBnI#e8h<
+KvoaIZ0%OzWvSB=Cq6Cqmhb8=1EZkWu&-o;j1uuGYicjH~Ee-XQs)%_0Z614fjf9txIjW(+b01xlIfq
+-8r?C9<D086D05^mV$TtMDo{EpOr`FsM?lhuMN@e4YEVXM(Al%@jRH<`z7Pq<UW@4REj0qO4d6DEiU-
+p3`hnOg|>w_Ud5XA;_C_XPfRF8|gNZp}ou{Hb59KJ9#}+`UtN1Mlzzjd{g#tE|^mwJCS4Oac9=R_|l~
+p76oi&;C_{U|ob45!PA!#tm|Jn=f~pZ~SKp=(IC>Am2?@UCJ~$a=;4j`S3m|_I^IRkKnIqRb7GAz=sS
+V#K3PK`#r^ePqN<=?DshP?Pb3`4ept<2Y8OofDwD~#RU76B?9`>Q9p@N(<)@ba(>p(wQNUU40867nf?
+ea0Dr$Dz8fx9{eelowr>Nm&5X$HF`MmUhFHw~cQA<e8N?PjWM2CT7V^UFvGi#?eQ_fFA1r-wJpDqRo|
+j1f4NEVMr>ES33Sm2buk{ly*pCD7BMJl8Z8>qcJ@^tb7Xy%nAIrG$6MngidFm(Za?fd>@ICc={H{I9T
+$aVj@@8K83G1(J8+Lo_>vmnrWOiGo{slfRX0}O*39=}9<uQw)6u#*v<O%wAbO-73INgRovbg4uO#?d4
+jC_o>ZB?$&ZEJ56VpmDDCO(QvGFQd`hU&JLV*nO*Q5XL_)1x0F64Y3V)k8KHvBL=rgu6DPlLqib@%PO
+7H6yVXc}MJ?DQ|9@pLx(JgHTUMMGHVysV6j;*1gkjzV*Mub~c$PUh2so3~~+{%s@Uqf+xt6OTD4N+Gb
+yNGYH#4>6DPk082m;n%B$-6h$B_0?}W+6F*9@GPh29C!wd{vF27hwlGNMKWzaA08GM`jlh--!9;UuXr
+xB5jbq@AW({cK8*leF(NA{zNxe_))dORSvcH5zC6yM2OheI9h>e<r`f600=+E9(2#d-n;DSaJDlrW$9
+*P-FR{Pi~3P>A`rrY2{cuzJKnO+*2b5kG4n=`Z(*or^C9qUpwpfg9Th*)ElS^wJtKg!t=yvIdv?)NYL
+d7S&-?(b0hTm2pSzhKrHZ-!P6h-->{t97wM?M-?jQ|&bdh7)ruEH~Qu#M}(eP-Z=bhSD8xCD`D-Z^aM
+m#P9y>nqqhRNIQsf>SI>WJ|$hyCg|5&7K31xRBe&<Bym+lvxsY3=+L!nODAATHEKzE;-go=wkQ1O(%p
+Qd*GD?Zm+s|Jex0g&`DT414We!SI$bdZcP;z=XFl)ob7#OEQkUEAW|tX5=^irPM<`=vN`_Be%$jc)tX
+->)7obsBNXv@^R`cij7CMOCg1#PV4h&IU7Y7FOln<_>KjOuZUCXxNE%HV@d3Bagp5wH$)b@cM3*on&I
+eknqY~}YFUG`jDF>L0l9|-2`n@6zp&X>bQ6+V~-q!(E6y&y2)XBpUcA#fW6eIn+--z=IBn5K?5QO#|W
+L*Bok{C^1*-%O0-OK&h0*poN+1vD>tSHxs+6asgppvh>9-c@LK`@@;fDY`BO|IQY$ja1#pzSUNNHypV
+dm5F%Tdc41+4geP@=*HA6sU2*asOW*Z;v!>J3m!Vt@GD6G;sX#Eg1lvci!k?jn0usL(t5_Wneo{OJE}
+G%uR^(m7Qc~b_IE4K-E4_8Mx80><JE?j63@q3)8tt`P9jG=fHyb($X;kdX!iTws(oVBK6XZWZmskY`-
+BYWdx+j(mstH#GC!cwF}Zlr5TCeq-4t=vGZx25+%FO}qeM2yB@sECQRgeEla0+&#Z^_QZb+3P)P#dD5
+7bJFSTz;*DoL{!7rBxYwk=!rjD<h1h&_+R;&;cMFU6i!$$T5viA{kPs6F?Jzb$9?j69TtTa}VnmB;S1
+DHN-4%~gB6T*Fa&rioQq>}95+s6EAE)fX&uJIK^Mck|t4wWma^dK=kMHhv48r1;SF6uF*EK>P)@^8)c
+xsCdnH>E%QI)CBZ|!clhpww`Dg6VqooXGIUPkM?HCf9GW3=mrJsbBL3=*XLN!+Ys^}O81emlB<t2v8#
+MUPXucM2&)DAlFBS>yW$ndq9@D1i;I3MoK;ePPUS+cSaV|!`}i{62wg-PsN)(o`1qkrC~powdn+xU)&
+n85(h1dG0G?YH&4V^V++vKADqv=+o86cokz+w!44s$6l0B$RMCUMM-zPp}G~&zsPqe8$zvSnk)t-m=h
+2(0_!`#TK_AFFN)Shy&Dg_x-Pat;TYR_UZ*Z>99UWjd)p*OST5%(yPfU9@P*&F3;y_$o2LTB~7#t@*T
+>CK+J!RQ|v0jQ(d8-WGL(Z}&JfBaYtqC{ku!6P4#*avL9Rfis(W4fu)<D8KLQpM3~kMkz&Mv1v`W18E
+Wu4yhGxeifD|2-szFtO^-`02#1^$rCV&#Seh(A9AaM)Awch#>w!8kksq<`Lxy(m)E*M@D&S5how*GB!
+Y1$}sqKD=9_dr_*}05inHnu(G)1q7ryFdC9aM7S4oq?NN)byB)h@!BW#$sbxklsSE0lrm(ABwRB1Su3
+K`Qki;8L07JbOuZIo4jA?kOU(5InfEz!k=Uam<%gnfeI2P|af%(}YcOUVSy6}`7wI~0HBx565^EPVR*
+-8-(VhNzIc(T!{97S7iP!p?0FvFXYTH(qOtC%jPZ`2{N(U=oGfvSU5syKOnpb)8qH7`HbG%Tu8$8+yb
+Tu|ABo*AG4(GfdPt?|AuGjCtgPP#P<W<@w3eyA_g{72kg1LTQoPs$}-09>ovT5m*gwy45Gwz4}iK}=i
+hCylt_4X+;18c}!zh+4f>BBv_9M}1Z~FSjlq$_@;NVjG`F(XX17GrNKbe5~Q4f;vn83rn9rFulTcX~4
+q90*#(9tFJ!>5{7~fzisT3DEYITbHw27%#Y>&Ved=eqO8{c&#=hI;($uxKJF-~C}=1yjLg_TkhrCl2F
+M~50>O-8xu8IaBc^3#yOovQx?L-CtE;$cnOmE;l}c-f7TOS(Jpbo=&UuFc!K`2Rw|xHh9bTTZpXYhbb
+N2H-hstjVN=skp1Cbj@#Z<RXXA6WSOw-AmP!mM19ySrgSmUHgS%O(i%Al0i9F|UrhJ8&z{OxVU5#R*z
+?bypk=htV%0j_oGYC3UY(0)w^O1F9;v3gqw{r74dHQf0kcK;XjwhCrUvCS^yi_)x)pUq-#`~FHX6-nG
+6H!}E9Y-&3{AEs)9eXwKd2I@RQMcz=nq7bMM@kH+H&WL`M`1oQ?sL4@hrV&OAZZA^UN<_7T%mp$u*jQ
+Fq)B_0HIya;&sXbfgVl*WqgvEf51ODz1k<o1tMP|{rnW0Kc_FL4Wp2^{yIy!V}Wy5e110z)Gf_$jeAu
+gGtriz|2ag|!B!-s7Z7+GM@-{~&77=m*m)+Ku}0&zc$Nb(YA8%_VSY=h{34s=Ldm}g%l8swVt;*mM_0
+O~qn^{<{M4(GfbSe+{k#_9|*EcXyi)u`e2_QiMTB5B-CBW&SRjUIATgm53~a{tv(JqYgU)Sm%TbK4D&
+8riD=sa3^5Y*dk<v=tzAa5xKuJ}CuqY%**<EIKIXtp;cUCvJp+YxzRcplSJGac7p~<MN(bzb}Ra9mG`
+BJ!t1&U8PdJ1gD^iFAT67O|k_=bw`hZLn$W5MF@epx_~Sd{yWiWS1hm|LzwDYM1XU^%3M8BOQy3)Hk=
+s1Q6pR)by0B5>&(d-18&Bu@Glmrm?;&qR)@%lMw^NL_oAv-d?C!%ll~91wWI&xhO#=bKl8S}`a5axsy
+-zRy6TT$Ag23NAD2!Z)dzo*Tr0xA8TdCH{}L>YZ!~ITNB;x`biVh5YX~-rj4@I6wbgglxu5Y-pTB~gQ
+PgtG=%UUW1vwvjLi-*K>nAnSh{aK0@hG%ym_L7eo#@7*5FGQ^c-a7x_&-ijYYw?Sf(_fqn*9hCi^x%z
+ego9pH?RqA-@qE5kRQy|KkoWs4|iwLlxS}B=-2<jSYd9XZ(v{iNAusnLf24L=SJVawiP-6v~OVXJxK@
+WYF9SSXCD_iTjf`<3E-7uPfZu+O6l&*YzX@EV`|;0`R`zjP398EPIJCB9Cz(rkE^f4s%S>>@iJ{jVeE
+vH(H5aRp$FQNw0~K!p$|ocQmv1@yR$WA-XqHS#bd4_&wEU+4)4NM4vzB)jxVhh-j8GDZr25GQ_0ue@q
+sD6bx2CQjZZf%w;7_KJv}AHqb}yy#hBuY9^xAwoxL@U(}fP8hfQ^6gWhNhpoZ}+p~>!5Tmku(^9>)l!
+v3b%`|%#}623h3s-oaN&}#E47EUj$7wb+HB)3+_vP!v2YIq6y<l6(2IETN%ux1Yr?i?=@TL=v}AK6;R
+l-3zav8%*`>*E+B4or`8e1b`ng+~6Ri5@uOTW2UM#TK7mL}l?=U&ES8Ospt_m;Nq3f~_)@Ea;HLY}k7
+0kgzPFcdFDHvP_Kp)5%L*kyjIk3GX03A<B_))ar)qiSyiNg(ljSe-~X48h;TL*9vX&RYwqC5hD%~26g
+h#@s0>7?og8lH4#8wzaoy+(Qse}b{bM-vn7tsN1VW^G;!k)-=jV_dEBa0w-`#V*U8PKB>ABj5qBK7t2
+q1*qDz`C2mli;j=eniDyw8-Pn;t?ZwmX(lLQC59McUeyfK@gE}dbaHA(G@XlX8_G^yK92@ulJC}Bw(5
+8i&QOnW~Q=}gxdR=ffa8pH4dHFrQ}gyeYGyBW54S|Uzmmel)ZmV-{in!Q422}xZ7^zajK3qDd@T<h#M
+2)Eu+aQp0%10Fa?+CyoQaU9M>)EMXafm#sC5*Z6p9D7V*`)Gz**thx{63peE)cXAW1;XqA5%Yy<nc0v
+)V)=#XfY~QWY-!Etolt#NNY<wod~#g<t%q{0k&>MEg8U9!2tG;jMQnb4l;}7uBo3de2^s84J#oe40JG
+@rD_%1~!Pg;^W3NJ|pJ};Q?>N49l&{@y*;OWdEVp0mL+@JF3dSX8c{|1mj#zul!052dDD-#jH-}ZCjZ
+Csu1bi*-4e{S)M&E=R4@7~Wc&s!yLU<nBYrHI$!PLWg;&Tz1kTtB<nuePlqxTzD_+zZ#*t=bDQy^zpi
+XOfCXW|FSz)*HlBg%>`Wdv-Atf3~u7B76J=YayY8#2qUGU1$|IB&R1yqV>#gmuZ;i=c2%%M^NF0fi=u
+q%iSf%M=dKT}mNtt*H*uDY~##Z4!6vi2Zq70jyCpxG_ckByBqF2q9jzOJ!YhZUg08u;0B#qc-P<9TeO
+1sK--r4eKtUhUuZO-4Q2_*P9&&HH>BD-em?C>cwmh^}kLsLsrGKBi4#F0SIkz93Tdr+8`Mubh1jmIQ;
+eIu>Cx^zf>db=V_yXdGy7_TWj<@*dNMiPr@2(a$JxEQ#(Ktd&5#sTtERH(@>}kSyMYZqvX7|{W|B94>
+o*<{-Drxxb>{i13gZBlYa=MVx>0NC{VD7b!i^_oV;JH^wkS$5dTrldg>R}Pzu#WLUF$=<?ezAuU(>Nh
+_U&ZiWjm@hSH%FMC|b$VB-GQQ4fk9wR$wUhmkyB&8`C7Acnu4@07#eWu3+F7u{x~o8GTr4Ds;;vd7-^
+Kr?zY<~?_b9*wN$TGpfSXuC`FXvi@{yLzQZ<9b&v&a?;I^l13DS9&xO6*_$y0={y8OL{cq`YrdKg54d
+*zl2gl=j?+@(<_NVK*@C>dfc)ch-F~1cv&lAjpQJDEujuJR%pu*>g!!P0!q$av<En!x?e+~W*o6&P;@
+}nv&4?R;+CP{lxJC!?^iIQGwd+y^(9p?HEo?~uISF%HR_6lP}jGuIlYZa0Rv74oL}tPo(jiuwAFFWu;
+oWXS()hP-gF+_j5W)FhnI;R)zi<&FGPl6+_q`6(9VhZ-cjdso5h9nnx@FLo3SVB(iEBK+2!LJ&jiOOr
+sc6wy%&%2kgI8kn7dhiV<M7Fbx(3i<0LJYPf}+n-Ogl^omG-UU}>Y0><x>qagsm1M#ABQ-k#gOn;z$R
+^~ZG<^e^S=+(K`0s)OdfN8}ZR&bq!rFBZ)b#VzsG2snb;(nVve6*;|o^9lN*tE>(K^xf&K4j;p2y-?c
+fQ`5$<BauPZe8RI(XoCA){)w|u`=TlKf&+#v`)gvXX%n!rb+LScp31y0rIT4cq$b#!cC8*d*@Py~BAV
+IyWR}xd-Nyrw{Gi->%Ms_>eYGPlR5gp|$%FU#SVu3CN5;jN*AWn~Nbb}g(pg6YR6Z%b2H4Zg-o2&|SD
+do1RoBeW)V>{-h}xK=dfNMl+nK0o?jgUiO~I-}miS5mqX1-^M9n}&VinF^pMDli-OMU+&c2sWj&9gYg
+m{{xGU~-@bgf#AmUnyn-LTm}Q_?DD6_ZWcgQ92?tIUpaD5^7+oYgG~H%0%rAPj01h=8(>4EI+iI1X%Y
+Ef@Ae#R#7V7mk(@X{g@Tnzlq}xr?qX=xdIet+N>&!U0GaN{5TXa*4HSvR{okv@_&hO{?G~m||4#Ay%s
+Q%8{eQ!Bl~>%m<|MSxc2b!(sK}!PTSW$C0>bsODLkXIUZ+RTony+UN|CbNe&0p(eT8{1J`OV{iz7I>a
+0MVENE>^vCTx0der>@TF&w^8aK?E$>ZT<=l!Jfgt;t(Yx&4C4Ss(GwS#Dq6}NqUX)=A_M-0px9>&8%D
+t!&G@fwVjOr@38uxi%H!2vrQSG;l#kR8W7vG+g)h#}J1shV$?ME3ei2W#Wm!iY_@jJBH<1tUS{iq^q`
+P5>n8mq*1)Q<_|l!S}5X^P?6ld`HksT}N2IiGo&WO2nk+Md)_xhHk!J+5h{{vK^}XM+&Zb#6jB!xz+9
+icxppvKY36nj9xAj&l~rn^+se7Eh+7>^nng4_~pbQ(1C^S6qLix#E@{*bPl6w-iHpYJF>=L~>uIDQpi
+F{=8rsNYqW0ZidYX`l?H|cy?Jm4`O~a75A}k_SczqReRyHqb#budPC_CNYHV}f|J(qYF8(*wS@J`{)~
+dF6tpPVPww`VB`Kt~r4zByyA1cV^}z1PFi*A9b6DHynW$4cJ=?XNo|olLkD+vz*lCz<D817|?)FR&yF
+HI;yFHk^nMnZ5I|rf}7%`=Hj-hNOwwLPm#?V0DTWl$u(i=+ZyzqJDg5EP+OSsj+;=p68xRU+)gmTCam
+B9#Yt0-Kgh{%40R~dFPb4A{}51C2W&Db!aF=@?evtehH#qo_;0K@HjUE4Y2rcuqua&^;DU!9Cb=*(WR
+;p1uX`*H;Jb#abvTjjn{y(@1^>6eDm$#Ua}${$-M`<5d$?B1+|>MC>8Rff_)4juPxsQF#xOg%4`YlZ%
+p{c_G^j^+#Aum0v@<4ma(1eGSCJRkGS6hVoWvn`Tl`RUkpG%uUdJ0l^j7%guCRRZ+1=}>^~EIg}no9~
+CXIW=CH<(6aCikZhOj^C;m;=GY%nMmH^S+i|6!#CuZN|hYOHq<dkE!GsV@nqVG^=V9EtksWc8&Oy#6p
+^BLEjq9*TMCTWr4SoZII_7dPq{v&Hl<R<rqm0FX;)rQfV!<nTFQQ<#4AkysId}DCFS7iymDlogEi+pH
+U9i)rg=mlcC9Ks$~bnd_E<&fR=+^gV{J?7Te&4Ax1-P)>;P$&ifp5$^wj(XnI)AIp}D;G3~Wz<y|>id
+rtMFinm<v5Hv4^5lMv?^<0^4y>w3jeLuoOw1lP`6DsD2OJiIe$T;fsgL$jnDv0=5!Q?gQSScwEp{|s-
+ucU5I;ohYTbW(4Hczn~Ttq}Ng8h&mzmug2HcbfLC}K(KIYm6$eO>|c$qZwTnViS@0Xk{6p;r<!hJwKk
+QUIzuz22^Vo^V7}bLI@<Ccma*AAEKifRiS<r`Xv!<fj>=sutJuY&5;2#3Me=_^XrpUwjtVoBdZIn&Z;
+|AimxWdTO4Y$0lxdExwvROl+^Qd>GK{V_?ZlpxVM0ApGez@7{Tg<$Ccfd?#WIv;{U#6gpTTtATH|SUa
+B3cCWEQ;uJ6WpSOoko5$?dF49;_QCHeFW9y{yD#X2(%=NidCf_FEnAn1#aKV>G{1TUnhgYM<)jotUs!
+Mv8r^Rbrn?)Br(&wwZNY1bW0hkZ6};qN2r)*0<V@R?$fzOlX(9Wm||=u^JcsQEBRIhOJ?$>d&Y?CmlU
+(?ZoFs!m*<0bFr<}8ar9S*(}xF*?JdQ!p@k(&QJ_(Gs|&M>`A@vd{%B|ZFOyC&2Zby`s9qrMsMp(|Gn
+(NBG9&Q(VdIfS=d1;`_4{19AA`bb(}ZhMp(-#+3+OL99}j=G>)dB!B4-)Mipvl7WKh~MiCan<no~NR2
+>Z*Jby!ds$0BWpj|UifRZ<H_%o)amkUvmeZxl9Ftw3Ifyz!4B?DR8jS^X71G2BzVU&#aGU9~kb)&3$F
+=|tbnr?B(<)HK3d^x>Wn_JXfBbba(Jq%l0NJH9daCzCjlKMDXQb~Q5Ep}UuXt``5;zsY1`gw-3AP?C9
+`BnSi3N+IQz3{WQLGKR_|LX6>ko$08^=T~M_<dP@Ql}cNOjAkJaQjsRiaa7k6ffaky;+B}N}?i^=TMj
+DFNW%e$a5>YY|4$g>*cU!yR7(Ze`}u0iq;7#CRl~_OX-rVL#u0)mPtB^w4oGd70lwK4+>5$>C#YB+iZ
+6B{#6u0^$~mvXe#+_fSpD{qOlYNi<*b}i(UkmRyi(`i&b2_AoiuU>{I(v*6NdD9aQW~S*za?s3N_qO|
+o%xOHXZ4;m8Pa(Cf@eu_Fbw%C4O%N>|&dDrRy#V~XBm8&gu>%Qkddm@Kef+XhNQdm-M@yJR6vqeS&J6
+OnYyc>1rai2?t8sCh0q>`@b8E}yy#SmwCzQhgyQlY@#th!V*VKFiL+Fi|9oVWJonmeqr6G}miIqc$J=
+Xmiw5F~Uq-r)`SvwmK>>yv5YhJ6o;_g?o!tA#tC&+p3WBP^oLgdP8aRBUZ=$nwMJm9?9_`O^jzp2i$x
+=V};4qw6oTVfs=9M%W($UAa`B#hDJ!5^85+~J3ibVT{60D9&RD)gU{IbA)6mRWbY{MRIBmd_A7w*wmP
+9tF#;Fvh?wPFd?G4os?)X9wTd#dR4y2~Zlk!&JVl^Nc-|fz@ah`EGh1AUJn)4zNdLUh7Qnr+hDEvk#^
+iF9h0qP*RN|x4a)E9?H*`BUgD!48TN*A9{*6fW_v8ZO+e%y*7nd}Hv8LlztuqBXZ<3*$LtPPT5()Jo(
+E;`M5$gS|X~$`lUwa>NazA8-#lt#tAYo2jmsem-7iXYK$+^^pf_<smsqf~#54b^XeI_`*#AaAR#jv~X
+b6eqy{7Uq&B?}__nLV;%LTl%PWw>})*gH|<9ieF}P97xG!=C7Mj+-O=<<-}>QIVB#o8dZ#|5p^YV;~O
+>9RzKkO>VHpVue+%?TK;}6koizIf^Nnz(6bS;~dB?xz}4;<BXA2=_^abcNMol73%pCp=rg(v0pTa<Z`
+?eYH2ymj2^sK3_p68@EAjd1H<98Gx3ftwOB|Hx9f<Hn&KT^wVL|slZFyphE^A=*BQ#7&!FT^V#X**au
+E_14n3?D*J;cdH&7}|GgoBkFn|oL@#L|JN#jdo@0gD8JlJQTOq%BW!p^TIfv|!i1RA@aysYIB2^HR-u
+4e4IFhX?e(~d#goliLiS<loYIKH4_^opaB-sK#d;92E3u&c`3>{%tW`qV&I+;pUc{ZEQt-Cd}b{>o4W
+z4H7(HEtS}I?Uq_Iv;%%gC<<G1&xo^G<;)`wOU;Ee5}O#Mlu=W9RA1f$+BR~uEI|HW3ZSSzCWh5Z%K+
+jRg22$B88gwcYW=RArcM7loAW2y&}%xRk`1+*XtZdoj3o6ax$1{i1{@FZ}S6#`YaXeJ53h2#FCm+7}$
+(XTd}$w==^lCYgPL(v8ruY20f(FqaRyERcj%X0@6@K>a$(B$|d(opRHWkhKl^%xOrLXvqe)8Mo!V<Bb
+tLe_^zupWxeYMXv%tNUK#9MvREj^zsZGzT<h-kn@~^R&S`RaIFBr9PS0)QZ;NAa>C<tsu4HnYH92aX?
+eCLoOQBd>GL<gy>}u~`RtKfcG~97tYwERMDdzSTD|^kYI=L$&grX3~ae22Rw4xlUE)aT;)%Uj4E(Ty7
+QZG)ao0sXF^A<H6>h@gZeoFmNV%R~`au&PHOBcs0OVyd~@32wdW?7JP%p&bX_daSo-)!!#rVrJ1O}<z
+N|6(>ZzqcZ`k8={tGpr@&s^-Pg2-?S?T|g;SvI>ob2{e!n{%(Nw9fv({g!k7p+m>ohkBe5p7G^CuUp4
+oiaM?*N&c7{exRtW0ZgOYELNRsV!COOx4w_+PFZZl@&uP;<o{%10ga;HMzTUk46s0ShDEuaZLpS5KI>
+(RBIiFzkDRsvuk(&o@%5zTp1g$tZjkY>Iv4)*Q+q4o(COp_U05ffDa8yjwlRR2*M`l_mD0YJ4A<hpMx
+Gp6+x&XSxT2#L&HC=DVER!<*RLl*k%#J;A(Ns;|vltQ&kn^>l%5{Bz>spD)qRurRIqzS9>$|+|e$Lw#
+X!^OVB}X~*s2_3OCs4Kb$EbK8A&IVYOFt_0sq<$F{e<t~JGYM|k%m&-5C{FL^{R$N_D9!Z?aZxbKC|M
+$8%+TFI6Yh$IXf&+%<tI`+SL$19<#FGo?av#>`MHM(D_>pAY?58D`j0*uV5@u9{r9}N5A78{%?(2Zq`
+MOH*B7v(|c5!mVAk!q1mwGnCbYrCFg{O^yvi2)dtNDP_`8c=)r78Krd#ujw?>k>5HlVcxzm7I*VFDb{
+3_p32)-QRJeQ8gpf_AUtANY`TN6P7gcO2`^fGiRsd=YrZXRQuI$4q8S@4bC{Vo`Q=qDJaDH`%Ylv4!M
+scM@jjzM|j&kCU&V4T`FjJ@?R9g*O<*M>QL+Rg`nDACP|FIIhomo|KXM1MvXV@||uwvZKOniAPRMQkM
+tY=~<sx8?Yw(PFSH*84^6d#&rM!#9uM|_R#?3N`u;Z}w%mcW|P2w`=UV7g$~?4_@{+HqZbYlX!qSMh~
+nq=X7fpq#mR8pNtSMYMU*|4TCk2eRaD$Kj@r+xKfF-v%a>x9^>Ra!-u-D4}9X?~sJBV~K`w?_?x6y7a
+-aPh#{tg*01Q@&&6+tat?4I!yaL;cBl<a8O+EnsN#Rzp-k1ibmZoo%)VAy>t64d6A{_WoZa;Zjc6}^9
+gAPbv_^s;m!(S@N<?)hZyGqX|OsAr6I|At29h^rb<JKbGkHSIVVU%lykf^<U2=8Ly<Fbmg{DFXQ(tUg
+Sm?|<5~!3pfs<A*=v^T;(KR(sxU)Ew)0zQ#z}E!l{Dj{P3ODPTnY0*X+}vn-;ieLW_E6rX3W!^>!rC0
+=C#u7gn5ND*NXA7G}psylV-?}GfSFrErN4~Gz+B{Nzx1z8BVh_L!pK<Dz!lw#?`3}S}csxj4uzI?WGx
+Aq*I^jQikEYkm6E;;jEQroRV;!l4cyccb=5yVwew0Ggd^MmD0Ql=1tPPR(yjj%~>!%D$N^UUM0;NVJ?
++s^kB{+X~uzFXO1-Ez^gMwnvcOeMVi;aY?WrL2RKJba}~_t((Ht}w=~zn93sv2F#Ah078;#Qn)NXMk}
+UZQvs0QwVE$N|#RtI0q&XDkz0!=^qn%r&ISS?%r5V>+JJ(4wPSZHoNHeatb}p0V=`b&l<`kIor8x`cb
+ZN%nH|KO|E`m8hnu}qMk>+JEM@sW5m_wy`EzDh{c^%Aw(!2p?FKONgbNwx{EMWfj7V%l3_|6L>pUj53
+-r5=+Frtv!esAY~5d$~L1`L)io1{x1x%81PPf3^Q<Z`ogStVUk$Yr8*S>(!1v@MW+x3(pO0#+4>fKz2
+uBe}dUU1Ft6D7hS#E<>bCIJxYWF5RU|6uE4bE&<XdhFo5fF6U>;28dkNOP6Ztl0`1BNS6<!OFp@5kS_
+bAOA)y|E?r)o>1u$;akX@Oe5R`<BF9qcSU%I$B$4Ah>1dnT!1tJ`ZdYd|VV6I~T0WCryVY905a0K1zX
+eMl`>f>_y<=ud1W$DQXmQk+Ph~Vb8DMt26X)1v;%~-8LAx8Tw9xpm78fSsl-zE9FnUkLbln0E$4Rpzq
+sVc_Qg+JjV|D1vjufvmkBKf{+$PuggvoIjH;R;ep_lio?dHL2yJ5!YREJROsKDa3)=JINhO){<x5z8c
+<Iem_qYsaQ3D;F53=Fg`ISU1~^9EQeOa_`Z&GW=+dP!BS^Clb+3af%rlo)78e(`|M;ltt+M&}GLNhlr
+nJ-|Ns1ijgTK!jN)u@jvvA!wF5y2bTx=ex>{`vkFjW@kNY<VF8z0&&FRc$<dELbu|n?pP$R)ecFTwgN
+j#S-tHVM`AwY5R0?XKN{}w5{tB!EcDK<_$GX<EDmu!+8wuDVxCpGXts9Odz~PBI?Y>#2gXGooHtA(+S
+lr0l%V~VdPO@%r<i=>s=5Q78vQqmAs*;&qtU-75OXbT;^n$yQ*Lt|Jn-#~b9xkKl*5vbU0b0H{D`=im
+L@-#l8a@0e2Y>OppFY<337;)Ja_<=F3lV&uB%-#0(siKogdvqh*V5yaCnm(umzB)w?@3}TsBn<muP%V
+!s#I4tjGw|R^Qz2_Eqzne5mObbGoEB2acaRW6CJC(%xHz)IHj}lF74qNoI-{gG1)ziOyI|%gMY!tXzu
+~?ohI>6Y&l>-WTJhJv2w9$zGP4?4_vD67C+(k8|7{80RPqiF16J+_a9xppJz%sbe?Xtkp5*uxH^Ec6B
+1|WI9(?XKzK-#gXrIW@bLIpK3_w6G8&1qM(l0I-`zMsX77$k&9-#3V(4zbk)4+R4;nF>cx<jL?pV-B_
+iP{_Lp7aap*>uco^KoL#X|yfXfmcdxggTM9)g+{aExAACikY<2WKT;wy<c*hw593{HTqYO0mcZgG?cc
+kGOtt{Mr#iRn_icOpZ%ubTWv%00zZ?lC6ZXjkvVoCO7r@}zQHtI9nF<sL2tvs7H>MYTNv1uRF#mW!f<
+r>hc9avKlli(L_FLsPVK68cPxt8wC94);?iW1%P>$BOLoSP}X-j~+o(PRqqy`jE}Dq&1I|_Xy4ncCN^
+6nCge*ij|CXt7B@6m@AYll_!i$m@F8|a1@$`>rvY{6K7TC#zUn*TQph$VtNksCO(*R56{(3_&CGJi1E
+Fn=?LGY@+3u^ZKRaKaS?cb=h-=&S0wi&gzlb$PdgUY8%i^poFk%I$(OYfXPyptiZu|*!cZD4g1Jr;Re
+R$&OhzcRqt|)SY$R0b>k)@qX*Lq29Znb5O5uJA9;}!0(ynA-C{Y|CuD|%m_GGzd&0{?Hz7TBh++ir|E
+Zi@l-H-U?963j=fe-=vb0qPG()AY`W41;p_xv_W9*K1h)U>mT>6`Fy1kAV%{5gBk?(cj^oP7z1moA)U
+j+oii^pVlkSz>Hhse|m*9R(TxGHfx61@)*#>**od!kXFfxz+Ke)$yTo6io{qlS8Q6sN22N-qC5xajkM
+)7h;qr^Dxk^+Z|&IaNdyPy1ct8g4gZV**ccrsju4|Xebq8hGUptT(p*ZmQN0G#D<VxN!1S}2R+JT)kW
+KbowL(GaV3prPTJZyZ<IC~I-Rup5hpG0i^-)>u5$W4&d<b(Thye!VMiElPYb&(ka{E!%ES@dyTnOp>&
+w1d)Y0tNNAZupD{iNnR`RBiP^caSIk?Dey-F@#v~`0%4r<{mf^S<OmF{XMK0W<+*Z5c~0wR)$P?O$h>
+wx7N@j1w-!`gzwx1uCt12b`{c^9Gj9;~NCDQj_v-|CzsvXB1FP__d{It*L(x)y0#Tn`M7OWPIaIO#Y?
+W1X@?i~MlWB=q*;3<9>1umP7sJ)K$haL@qtf!T2o%0hnbz}ThN5?nf84m+OUcoQl)J1^IbTi(9)$ldl
+EtbA8#rEQw^%q|?2uz$6xLETGyMf=v7OTOTS%q8dQ4fEcjP9fgWo`%W0?K|VPYwBSVJWp6lYPnFG8nH
+XBY>(|gjltTP20$nIo`^~);apXE!q%^}&elsQgy{^0Fv@Hov!672mDbrE6hn*L4E{o^Xit#PK6BM_-A
+FHA`_)n-E4kUfSE@qo-O=_Lq`$w?*!rWDlI~oIgjZ1@58E|Fk2RV?+-!B2XsB=Y)!P%q&F^Fkw2vlZ2
+#g{2K~Ufd1siWe52&V(^bsw7XH8}7_s~dMQv$2&b@&Ha4F|1;L(~Dv+*@4F0Md3@NDYMmTD!~QB0{Ld
+ra|4u^;Bl@TA6*`q@a$w71Ta~TZvQOP+)U|P+nAQs<?L5_*>n$e&uDg4AG>%U#o0X=^r(=LSH*oBmZ!
+$Hy^?Y;uy0WQhweZ8!+BWGeyg9)$VW$xII%gY&aQt_M)v751!DDX$pmfdiF}8QYF(oLMO)j;$wyA-38
+zOyyr0Qvy=k!KBeOS%63BW5zcN-RSUAvNA7$)1&Zyc@=^Eg@+tWzIw9(s#Hk<W^6VyI!e#6fF;|CAxO
+;5!33rZfE?tO*<G4fb+_bt`-+;<#Xr_N^%h;tjElgoHeZ(Q>g{)@cN*#KiyLmOHRqCbO)m&8PJqe9sx
+nmn~Xi1beW0i(AyBxcO66!`#!N6gsnj<AG(TEKoM{u|KXoT9o>0FZB1h+=$iM7L?&7tQ=jBy_SvN@7P
+obP8hQ#K_F!<j9D1}ym{3nLkHs{5+#sDujd6?0uW)v+rjb*lS1=X}{rQA)NgZ<<E~$Mc%?5<7lVTJ{<
+-yTx)6dtn{j{Mmn=|J#qzpc8PAf-wr-q+phUcPO}0!SxDOD)_O2KPjjWl(4sgBNe<}!CMr}Rj^pWRSI
+rUaJz!vE9em<VOs^eDL6pEQ3_tKV2Xk^1y?Efl7hPxd|$y@1-;s;d=wm@;5Y?uRqzf4?^W<A1ve_VL&
+1Xzo>cIA1<xtis-1*g6da&nl!9gjZ%{B@!MO^qQ1Dp=-%#*<1#1;#?IpZQ!Egnm6dbSMBn4+En5Ezy3
+f`sQ0}5_XaI1oc75q@aZxp<spntH!M?LQcHnz9)th*W)$uRcr7zx8mCES!OVbdq_ZRuV-uzCOHo(Db+
+nhJ<v@+^9bbUIe_we*Y4E3g!0+mZ_M(lQDQ8JoijSsKe@1uTPpgIGGtVK`bB9;p-g9aAukeiWQdWmDL
+AHjqWByfpg|Hk>6eiwlRT?0Oc*V%Y>{7On-%&T^TJWmAqB%t&tm{gAIwgb!f80#2b+Ca}r$a}T5WGIE
+cjl&@{Um*k9MCo+==)m)fKgl9O5pdVZYQWz6qWuuUDSUy1`y?ONCPSgU=M3G|xnZTuV;g%`VoFl?!5k
+-Y$N~QmWB9&Rhzxm{nPH7a7Z!Vinwp<DU>gS3S8q$J)w(v=%c-ia@6)un5eTf@d`Usa!|I>tjhRV}Ox
+!Fbf@HbMvc_LT%<<l?66+@;th<xG%l`_>i<XR}~2zeW&D{%*1z^*{#WG7dI$s@{*BE1|DUQ(7Sa@P1X
+P|yZmW>c69!r90|*<f<bq15^d?m-gLTsXP&GqqVx<q8}jL206N3Ir|%jbfSwPKgvZThQ6Gyob8VJC|b
+EglyyrZp|iIjg&(^#Z6P?D^t%QD5=#-O8U%k<EXprx#u_JcjW8Ra?fz%dn~0hMU_v1%SUoLPjC-)_1_
+^MP5Bn?qC>+;<6m<Wq_@D2q10Yz>DWaH&r)2y95=uV;OU;GmfkggvOMPrN-tmUn#*tapU4kVoBk*B8|
+KO{Uz8ZqolD^hD3)EM(OgZ^>SjZVT6&Rx0{>)@Y6dG3m=9KxC2c4<trRcCe+V0K+49b#+%go#vVP|&f
+3%8og;Zu-B1`V+H<l-0g%r~ShUspy(^5atG`(wnC%rkU70ebju|TAr?rI}$YcU@!{XZi=jdMyD{7-E{
+g_eFpJq54xgq(zm_BKoOO=t-+Ts)Uj(sF$bcPqau#}^@_jG|2E2)nIWe8b(!@5<9prT?=jgqCt)GbO5
+}-_YJ?iZYV5P4-y%WX~2U7)9@vFI)<QUBgDkNxy_g9tsA2E5~DcW#3pM?X8tPN!eX7l|4{d*L@@HLCP
+MZ>}{33wp!ZTDf=pA4^eibvUgPWV@_%Br0glm-dRiUYiaMI<*)4BlzqCgch~a!O4^NDe#+iM*~3psdy
+&QuWuLF*_ocMoq2Z_O7nJ>=vU6qMpzMjF?iYx03C0^JiyK5Ar?q;qER23eD(S`}$^<gaqL`2piG{CQx
+eXFOnqN^_BlX!L<Qz5LkSQR&+k^znqyJ+>yr#KWT(!K1NI$7jY9n3z`p+XD<0ZdP*Y8rlOMh0XF_-(5
+HuGy7Vu{;NcZEOq_)Fb>9#{C&;u`;_+5f-bxwK5P{<|eW*YLa2r;!Bc8h##E_-l}W|LX7e=*O=;{oHN
+tepgJgqPwyEO6hH&yQvHAiD-C=mn<zQExYUPW#!8qD=JprvugFd_pQ1Afd|(<^zb8(KKA$%>z;h->Gj
+V%``m`-|MtR*e}Cy88()6q)lIK$-m-Pu_SfI|=Z>9~yLRu{yKn!2gNNRH>+su0jvhOH;+=Qjd;f!z|N
+8Kwk3aeJv#QU(`0~{0ufBFxe^c}Aci;a|`{PeP|MF|ynfkNm&R_WLB4;`ePcQFQKKj<ae*OV%41qyy+
+qDnAszXS}PMy1S?bhAcqi3()efstb?LQ#w>Vbm>hYuMV5jkx5HBlo*MvodjW^7FAthDru%-LDlx89aB
+CpRzu_JTs2eeS%X`FGqrW9BW%DJ}coxnSX<#sAav|4--tU*^AQT&&qL{#t8X{B;S5*H1{AIBD{fsW(i
+Ue&bD&|1JCfui!uSd;Hw$LsznUx!E6RVy_^(-p#(UiT$1?_Ek;ntDD&GZDPN#iG7ntWBY4O?AT_+7`K
+vMe>Zn4{YKIV2xFi^u~I0XboINMVU0-sj4qe?>c@opVBtPkxzHGz-0+wb#A6n-GP%x%8JG54PouE8&A
+3lcYet&uWP<r>J;ptxNz6R#V$O~yUd;FAyV8kQ^FrndpD$MTa+Ez$j62h)Z<Eh>3Oh@zSv8DDWja{JX
+>gVFX!vXRYItgRY4~V($o%HJ@X+$t^40P|I;mpCpg_zY8}Jw}uy1g~N)kd9HVHRP%z$#mijup}6bkL`
+mMG?Y4Iw5AJl*Z7f-ae_mY<fsmY$Z57N6wGI&#jOk%caSN~h;)V<J6980OO>`JXsAJ$<mWB$2q7l*pJ
+f(lX}E%5Vw9^?7p}*(apg@?f2uk#EyHO!nFKLP9p)o+HwWx95t2H{};(qYM&L3({QWoH47wo?75ySbS
+=(uu{Ir3+W?mq>1#97R8^Bw2%(sBMzIKYO@!lr;2pO7i23F@g>V%Q0TG|zcSKuGu#}e*rhEoFIPBDwk
+w!wLpq3$I0%O@@E7r}&CvD$re)`*=gpgJOSNT_Ke<mW%qW;J>(-1kTU>5tUcnsUF-_%1{t2mtHdC4{d
+u~QjUcNow<u2W1ax9TjPMw`GF0V+&oJu8Xq1hBxVh54bC(sX1sHhECjp#4!x%Ql#{x0!jt?;MAyxFrA
+YvmE>WwBVi@eB*6_5l%$QAyK%^z54O@M%dtG~6_b=1QNEl|m0qQ1grE(NhTK!}NMRrtEs=qqS+Rii__
+mUQ$dMc-~cf*V5wl-rkf$Sy>r1)ZyX9NyW=3bZEb*q-n(zpE5~G!pUo6Xz{XQip40KtgI}(-lr%(e_n
+q6CF%dWySv{RZipS2>*>WlO>e@-PK6sWbWnPZYPcH8X-f8-j6A!|q~to)mYQR<6cpqY7_)PY390tnw5
+$xweP=2E?3|2rqb<*vm!FYq%qYr8v)fW<<!CMB)LdBLpGg8?OwBc>=2O6Qii6rif-<N{xPpw-bYo^g-
+W-Y{lj)@`HlSYR%_2Np;%L0xo>4GAJ9oA*Qy_AQe_@8r=H|@`ZRtctx;;N9J57ereB?v^M10Eu$Umc?
+pk@CCI?{*`TZS=T)-I#H5NP!4Q`oNw-)3jnh;7+6V_IH%hA}Twiy_k+B-S7s`Ws78K2=B=Q*xBz&6$%
+*CgLixAoBVRqH^d5Q(;!dtW<K$$<9@bz9gNDf;ri_se(yFIw3#sc6%NnPCRrg&dVl7&lI<0Wc+#Awk)
+u+(3V=@=6r?8Ht<JKslqfvzlOh*PxrJg;a7TwV6l4`RV-7dj2{@)Jb(N3>Ss(<RG@U4<Zx;D9{q^MoS
+eLQqP%-uQVcDWW6L~gq1+2*+vjBD+6s-K8FQ)TWoH^Q=H%Pv_y7I;W@Q*PAx$Mj(;%jr5DBKt$(WO;y
+0V_E5B2UnX^hrKtR6SEGd5|mdGg;LJoHuGjxFQwon2CsJE=!6O!Q($&OrZBIO7H?ocS}Tk3mOLIHNFo
+PJT{Ca*k?8W)uyNoG~dQCnL2mW5%^p<0cDlt(6&+pFWFS*(26a+v!=mbbr<^(vP*9WH=U3>6gEDfLyE
+f=)_nbdRM(I!w(?y7&p7l;KdA)t(ak`Uw%**U)Ckvz`8`XVO<JBYOktlf2?g~phye(ywioT&uPH^H!Z
+HO2lE};DqQEMV}69NuDvg7A8BCihX&TRsq#POTdB|YNokd&YvsXOP4Y+z>gdZl?)76GZ3NR>vyPE^)^
+SqsF)jTUdos3<-XkjgAc32Yz)cI2-G|yDdYxLB_N`d^bUo$kgM3}#D*7_^Xy1l#I$tm5OLP*x`OT$!C
+pAxH{Tjlz4)A6HksVlou03l-eY_S9pGE7bfA%;cq0Zlf`Rjt1&gjpK=|RjmC6F13f5w6?wVkR$j$KvR
+K3~_xU-ESd@imgnlNxv%WFX!XFAT&B!=wg$o*ly2{-HEJJ1X(%IH)!2n!cBHjoib!>UOX;mHzpDN%+W
+-u_|xyo_LT>{GZaActPe#fnsE$^YLIlx<^Est~XfEf^GDs`><f*N$}91%AiZ#T;)=84P*VOnJ^wvyz$
+a8uW=1Ld7k|K9wp6yK^=Tqhx9hALu3H!FsWT_P*sCWB8+JyV@K&7qtXj<D;rI2uNp=9(K|zh83r@G3$
+!@xyhz@CSi7MHSDZISGgdviA<lU+4;|wc2lD4Gdqab(+8qn3)bJV!^^q}*J=g-B^Jr}FH+nZ7k+9=HK
+h`;YBI`UQiFJ;gz&h*VnJiUVF8E+kC-Nkjr?Tld#U=BVI;4#{(?L(XYRx)KAxQLe7}_phtJk{DzO1t?
+kn%84`fXU}dqwI=x)5I$LiIgliXRJ!B=aPhtFB9H*5#?-+ICez#|)JL`Ti+>;l3mzK}d(fcc%8C^Uw~
+pl4^siJXYMm*baJ6D7tlB{8$%T2+3;)lGm$P7hPK><4{_V6H15VLzSv;imT4u%-GJG-O@q5AFArT&7X
+Kcd?0*@{#uRiD7T(78Ox-%M#Zb1!PsK5cUH0=6x50ZQM{m`jr05L7MhPIGd5g>({=S{U5VFSiPv3;*I
+kL%U59q8>TrzsE$W#c)iYmF&t!X`7cwWyh{}fAk&aV*2**~CjmnnlW+qMGKFn^Yo4Ve<taoHb*88VUw
+INkk9cy3NHa{pOFiFa_FY_&E<%+AL307}<Z`I<8yaOn2;?EStp8~>H@-AGo@5-#D@xh&peMj%BTH7$V
+HS1=3jde@k#JWYk%DU+`vOv`~<a=xN%g0r}kj9&f80)c^+FC7*?s^s&8OQ<)8rp5dkJ9SRW!mlV62?9
+*X-G?x-;UjgZ}<9DwLa!k>7DPD;+f<Tjx??C57Cnh(@XpgA%2GpZJ*yU+>dpSOlRG7H?hV&h@|D9$K~
+a3^#jy@JkTJ29f$j|K9RS3_R(c}HkY2U^X1cf?{S(lJ>iyK4?Sx;#lYH9U2Iz*THq9<6Ry?Oy;M(aR8
+P|bsIK{=zRJF@fXY$SS$at>Lx_Jwuc)(<=4XlKN1LVjn3vYJ(@VT+PrO1uC*+yzuD)B85tR+K;T_XmG
+F+w6vi%jtKBBi;t5ZVm+E7@MJ;Ie_uFAbm{_9oxw}~;YV={a(*?YW3vU}|Eb@Cq+=I7Vk>ZGVc4S2Rt
+M{7kL_4Z)ikvgVp>qR^cU~TX9OA_s5TS}*m2WvCbH@xG(084jUTjFsLwU>dcU2L0seQWhqt&VwBdMKV
+*UHJx4zJbhe!Y`lV4ePGj(hgL9?IPL~G>sSTif5pBZJ6N|!Z(=uLP{g}l{WbSDgH^i);iW2?RHzpf-m
+7oaHy08U6b}gs|QV?plPX=muiy=2*;sb%&0489rHs(Jqf3>@7Ra>1Y0EG9Kky1!kO0RL!p4KyFcr03u
+4{V14)Jqtb4<lEve(s0A{q=X-s0HF$s-eBMT_)TqavXnMR7I!iD;{W&RD@6bEsZvx>1mLxbBF<hJY!*
+5dP@bI20@=Nz(x|69%>ch|We_pQ)fn_-KUQ@#Y6@l9ikye4P3uk*#{&5~<>EK4`OAR{9odsYF~GdHkU
+v96&l8q(-oq-8HCpvkC~zTSIUYPM~BUcqFV1m|R^l;%*#+>8Q>9h;Y1n3t0gYb(gH(Bzfs;8o02SeQL
+KSEVo|uK@w}6><Rju{3>momlSVL9U5;>GqtA@!2^U*Qd@QGT3vT6R{%HEG5~ViS`^@_PF`Bj4653veP
+qSvr-G#{fZ)2I2zI3hO{T!(yhcFtU>8L)Ox2_2xBb4L#>;d($fnBHe);!@>0_kmnem3@Y9V<nNk7!mY
+LPkQHDx<5{u2rE7WY)Stea?Nzhj2EV{|I0{V-a2IUnA(@aTm!lPRQ?J^$uzlF7CFeooO*G4&g;9<>3%
+}3eMj8&LTnC!EBNm~Jx%#`{0h`FMPG?>_}GE8cL&7KcdPGd*Bv=yF)68OSvYHpUuKHXB3mXQx`Q&Avu
+_A2Oa(c8l_uF#w@%RYN{MnMuW5F*gmYmzPJhHT0*HD_w>yzJa`l3KVW<mKIF&mV8kO+yNnTpN}qLf{j
+dns2igK$>hBMK;z4ZZfK+i2cMY;6rRdwvE`GGdV*<r;_nZ$Vi==afy44M_evBrRHSc(bUtFn?5-|J2y
+7ZPI!J#>ALgz1o1s5!*)r6CU=P-F5jdZpHoO|y&*Nnp22R0ZL(&&5jIg&#o(Kpq`l=<yQXAeZ8VPfG9
+BS%ak5D7X_T|51MDMF&W&p_&CyVrH)IqP3fp@kjFfrC_IOUBG(}{#0C`J!lBBY8$Q_=L6lQ}zY#QmT<
+E3?wWPi43F)X=rvkUTau|_4OjT#H1w^r?@<hkl1`&`uTW`eWlWo=H%C=lf2rb)yo>xYr^l<c(I6s89*
+ZH0(Y#X^qf7uquBU|HK#C}<%QtMr<jlaZ0n?oqWL9@AKnNF@o}W4SP!VriT)2B}Q2B*N@Zl1pWloi5s
+<)Y%#AW65)p8phVinj~vIg?`1&PHdtOYrsRdmSrZDq`fc;*^jelW>O(h8!u9vlpzZ^6)`Ni=@T+#DNX
+cf*t0=6#s+&P#3m97f^EdL2q_UN-)Zdshetb)IrOQ|20j$V*xNIWCo<pV+Oa|wW0l{pG_^N6l@iE)_d
+!0o-wo9`yvOf``p@&9=ReQo9@IIgO3~_*r-C>mgR{`M&I;cO<DLwhOTowaIG3VVu(g7|%FoZu?<y4+(
+4f){RBkwz-B!JD@1fj>DZ5G8W0gHg#hIksCo4EjrGKN!J4d<CVK^Dd#2F8q$u3a1wo>7X70yc(T&ny_
+lz*v82RetbUH-VTKdJCpukw3Nxo=VU9Z~)tDE|;2$qS8V?tE`r4wrlW>hOOw{=Yl@KN|o4*9VHG-0f@
+B_pN^w{J;ME-uYXPCXc6jwD{|OkbHRRNA>-LdWtXowCTU}Xk(YyChp<qWxiJ{7^+}z1&s=JQ7}ZoKn3
+**G6ieTNm!-ey9ypuaIb=u3cjJ>Rs}aHxKY6u72KfUdIi@h_^5(w6<noYv4S=QbDE@|uHXy>rzmJuFh
+;>}1&s=ZDA-;>y@Iu872OK9_G;XwoK)_|6g;S4rGi@(+^FCN1=lLLOu-@rQxvo+7^R?5!4L%l71S%p6
+s)gTcq`~suu8#`3LaDNpn{bOZd7osg3A;vs&AZrmV(n2v?>^`pix1+hR+!ZPbv7Zf(I2`r{FRL|GV0d
+l>Uw99sk?>f0q{#&Bxc;pYUH)x&A6)zREBB>dWW**FJ$QKGNUV;`85z;=lE2`ShRkZ~64#|192r<@5i
+$3^aNEkBh{*fogrm!%Iv@*3-x*W$FB-J(T~mpGo-SGZHR&qDp*H8ndWazzr0B*V7+9ZEPX^B^M=(QSh
+}Z3~y3!Glf6;)TyUBvC~gEpSro_^cOuTOcQB>I`P9>J~XoA4{!Z&=%wM`Ir%A#0oag5GUhQgE~-@Uq8
+d-HSL3}w>ebrk+MyD@8~fM5#p?M*rF$~=&p{2>rhUOhHC|=cdADkT<$vs<TK*Qjhy8TyFu@3#<gWA8u
+}Fe_+c9>5+(QZ8NADYCUPEvzy)phe0sj_EV;FLOk>Ie7G!7#7D1zfU$}lMe7vm#q4;?EeII|ODUSv)o
+_$s|8eRS+yf&rbSIgnsqWsV_OqRgcPw{*stD9V@MkMxFm>ew#?pY9^f>j_3c7dC~-A$V^$8FvlA<Mb{
+d_jd^fcbD!V1dnu=^nOdQjghfZ3KK~1+MbN9AhVU=5A?oB=30UQy@-Bq3PW&iFPUZ$!9jf(^QSb!2`;
+1eV{!)^*_Xyvt#xb^!OiqC!gDLZq5UMzkpv&6H<HYc5**f_#%^SeA{gIa@-Ttm0|RJ`P3|}+asSnn2b
+tGhE%X`*S^%FLD8p|c=r>5Z`x6|k%r6owAA&YP$5s&hlB!cBnZG4?O(cy;kuSmQNSRI!!H?+Gd+FH61
+kXiE+RejgJU>i^0W4K!z&D4<ys8L#4X56N;sz27#Y^UJf<x%Nn#?f-O~Y|cfzl*+klsi#15UU`@-2(t
+^-(mACHEwPYoa9I0JqR<BloQY-;0ts=tmIGqp@G0V^av;9j#=5;I?QP<_&_kkD|E|g|QK=A0^WS>@r%
+02b?g5{E#of<71Sp66`Qm$q&JvF+>aTq&LCn7@7Vkg0>i$Cg6+84ETjI=bIRNncf)`w~F9sGszN}V+f
+X*6+a1nsLUS|oNJMBiwNE_o@9)|rx4t5Ey*yM0ry`k%jh7%sc}@kWS&m&Y@DiJ@l=lVo+S4R1g}bv>4
+Xr>NM!66GG`Ioe!Z+0ZxH-!D#>;$!js_n8)&Xd<_iRSPLsIxCiucM8U96ri>ND{LSc#tUUjo{4<UGl-
+h6VeCwTh|>24!fIYZ(G_~J~GPYSb@pzRjLLxR^NlRS`n6v4b?89txj%gGXlO#}}sGvK4CQhoq~XUTLz
+W<kfilxM&W8M4lX5Nwl4VZa}PUb6`&G6Qa*Hww5B9FxVE8SN6mJ<7b7U|hB|ClIWocLtg339h@H>IRt
+s3knD)GTR6~TOi8__ZI9ZlzCMWoNiO)PVj4+q!H(AyzNT130jpoh2WYZYI}jhe3G9#B|l>bK6s~$yOv
+<U0^$ScBKYV6#S?-n77?9#oq*#PQ<;+4O7JAT>nIFh;!?_w+>;0nDy2G4=5T`3OC=6~tCe{p+{<WwN?
+`y~?xy_640!r(MGL_#%TzrgIJR81^8|~_WjcWSmHR=09hVcYD4i|@U#52h;kJoj$^BH`<X%d!;{&pMy
+Ab5`)<W&_gM`E5RG%q4;MgZ9eKN-oyzdFsCJ_Aj37PLNFh9vy4u$c5ipCXBQyWX>6$FnzEqU@T!OrWY
+xeLL`>t#Aq2tKo3;<<s~xMw7uW`d7zr1B;6I)aHWOa3PjJV$RmnJ*B`e?{T|82l>9C(4na^)-?`G6Qz
+sOmlrQ8wozLS@y*n2?lMEIJ77D^%f}uPJ+eTXl_Yn!0WdYugIK4aPfA<XM$5-r*R9p=M&sPZxxv<3BI
+(G`T{a<B-o`=_HlrHcS-k9f<NscK9l(ug3I?(zfbtAAo$HbDho1yOYqkHj3tpdhhX?YDi1OPnh&aegy
+3WJ=94>Aj1PX3Xhax-74)uvJHe#4h%Pdx5bSrDcuVFOg1^$ck<9f3Bi?3g6PY6k-te}h5#K1_TZ2cb-
+2gnI%z!wjzn<s<#94f}0~(bXaEvko&QNB+dCH7);SVY^;NO)Q=etiRGhmG}1GYOV%{ZGpT$uqUC^O)#
+$_$7zy1*0gh;j$4R%Sq)v4k10yE5aP<yd9Dl1F|s?J4$$LP>nuFtN8%j6I@day!K>oR1int|ND>b8f}
+Zgdk;C41Xs<>?=_qTsF=^2x9LjT-P)mo%lk_+Kjtcb8E&OPmvo*?(WN4a9`Vk`-T?Wx3=JZum$&%Ex0
+?IamP9(E<Ms0kn9ojPN|R8Yi(4obPe{9xzv{E8|;;fs9*MOu!okCf1d{XvFFJi*kCW(PT{>8>{UB-;@
+fJ?Zr!P4H@W3UWzLLDe7-<fx_?1`=`(21Aa?Dw*RpBTrm^hoY-YFHWkD4e(_b-LyLK&m?z!jKwr$&3W
+o0G%;DZm?#ful2nu-;Rz+}ONTg;!`bGX=_84k>wcT`x9@*ge~`(KzhZ@~ft_<<k3aFicDL*56-t|!Y6
+6qoGt=E3efdid~RVHeXPJ^lNEU&s{x^A^<Vzop20zp~Fes;@`<%*+eYz8K*TAI;1>dO+DPNdFvizW_U
+>e}Vk*P?*IseQD1*Bf=j(e00BbI3v>k4Eakt<<Gg}=wYBnZou!t(X*eCtq%SI|D_j}AHHz74r#!Cang
+)93UDEZ(kHw38;9EWfAsKCA|Vs}(VzVLl6F7HIUp`6)BjdBG$`TlQ3|mF?9yL&{^21}GJvqxzkPIl&l
+eAiuz1P+Thl0BPtnDmCE<VM=_!ZnFB~nd6yc9@{@c`@^%sOU!V`b1AFb!YTks!AIHfnj3mwrXrMF05;
+HH!t=$ENSdb11ND;HOJ7LO$n=<fD<(yJ?GvY~0=)v0jq@y53BE~b|kGabF2^!m_iptn1RoChjd|2;X6
+jErQ7iHU6T<jL%gJML&C<EvJ!Vt@PF-`J*2n}m$N|Ni^z^y$+sInT%TF!lNQ8@KqNB<{?=lW)%4$rkV
+YVQ1!Td2BHiQRYt07w@b|CP}z$@%O(bZzjXy$4}qP`T5L+hfYtOI(27eLgWhslW&W^eFEpNCG$n){dV
+3wHJL5uPw#wfD$?7w66r7I{JS$}HZmc_H11o}y*m%|>CTy_df8=OEsT$P4U$jbQH<b|zGWuS)2B}#78
+Vx9!o$Pa=+UE@$z)=2adCoo6DLk&H{5UoyYa>w+02<U*`oCEZ1(Kg?ABXvWpn1tp>c76Xg?M%T*#iZk
+7Y01F`B))D4Lbbwy+hq#j-VdCiX}{410aaNcQ*QIQH<8S!~PNe766d>)5fUZ)2f5IJ<f`XG8XLHg+#(
+!`|d<)Dg~Ryv<qcyPRG3A!idm<E)V0sb6q*^VgiERdF`!Th5j&S;ET8%h~GHtJ#AOKFA(==ppv#qmQz
+6>(;Sno_R);@5YTA*=w)8Cfc$$-gtw(vu7h)`Xgtr)pE9P-#&Ke&>{Br+i$bu$B(mj-+h;T_~D1_lTS
+WjKb$_v-ua2M&p-cM$VGK^HT&g<Z`hY-IjgO$WoOQu5we2EV=(m(Nlbhvs9(jHFUgG`RrWSifsMQ^8_
+kEXoB2dGpWn{b^407${xW-)AJ^Jb)}G?`r1;?!e>BBUp!hdZ{Mi)0hzGId6#r3*{}RQor1&Q&{wcTke
+JOqn#lMN-+bI4@ivK*t-%at4Q~Z+@|5J+pCB?6%_&-qmI=A?P`_dSV=CJuRuUkd)m5nPIJNi8JX*4$E
+CxRG1IfU^~CNh5NcE-P1&3Nt0jMurvZ%^@iQv7g=PxI@IH2*wGY1PskiqEI`%PIb26#pL-e;36+LGe$
+!#izkXk{9Nd6u&jby%e9?pIREkaARA>$58wk6#ov2{~*QRO!41!i_hAyB-VlIY!9}Ig;BK~&W@7g*Rn
+XqgKlDcNIv5e?_&J+M;TxJ3ga&yWc>JNZt;y2e>lZYp!l;Wei6lAN%7ZH{8uUd4vK$(;!~chKc@JnDS
+oXh{?(Mib(BIjrEoW;@C>D}ms0qgQm75$?581|{W6iWy4yKBvzoKBFLQSOICsVGOz{U&{IL{&62(uW_
+;V@#GK&8Y#ebIKZ=(3SDgIH4|1rg{af`2490;cPohg0~ir<&wUrq7HQ2gl>|8|PMg5p0*@pn@E58dK7
+ev;+m?i3q4&NR+s7UqNjp?!My?AiBPm#=ADd|aH_93MM&tjU}_FtlI4KE1EKwwIQIIgY&PFOCdmb8=X
+J1i1EEqw<eWjJ3p%rQ|KKrm^Ep$yXylpFTZ%DF3*`7*o6{)*MUmf!Cm`!@|ZSHy|0G7_U9!JBM^g9yC
+a%(7R_3W3OuwpVE(yj~g4G+_hcD5b~!0p(;S%WQsr5gvhb+$(`G^>nQyNj=g&t3CTED`pLoV+qJuHv<
+MI;0`xE<eykb3Ciu5+*Y!GuTEYP2K=G5uni8*%kGI4pcR~UkLppT0%HQ9=O9FC$f1-FO{Z5SnB!dm`k
+F_Mm#m6UF61z7FK>qy(i1f!Ln&T~r=EU*cIt8l$@b4cwFoDVRV?`Dk*OEsefa0G>e@Jv9u{hBZpD2hB
+IcWSR{FBW?Vt_(CrO|2p_;&5uQ2>{J^6*y4F@r6X!S&?dZTxtFLKl~Rtijt5)h65$Z%!mGkMAys=sI4
+B$Jmi3(@;-?clg-k#N@;{i^9^S@hC1fa$M|SFP-o8$^*QLGn-ug$s+~_wr-`<1z8D0pqLmxmISH6-*n
+Be@QBu)`nGZ889R<-IWjUfw!uF+d7NoNbQ>S<;be?WM(T0qhWwLb5oAJifQL^E{A0(GxW=;vfAEI{EX
+FUW&4lFG#IeyNYmB+4FCS}Q(1=76@iETL8|j;cH@ROE5gQ%X*jvgErEexdjI#_I*~Hr|ePT_VX=D>`7
+T@?uY~sy%<>Gon@z^RFNL2nc<GIDnyv>f~irLsE<GIU^Q?&8iq)C&+cw|w=wXU(pb46p=vTO@mTM)}O
+Ew!+X4=-ZZ9HcP?wR6@FIJ@BsIc6v#_`m}Xuty$wggy4yW9-Q%pJeOTuNPy5S6_Wqj0d-G-!8@q?^Qm
+>9-;BZMj9*Z-Mg0^IdVjd4NiUW8T;akFW9M5r&vu*4g2=nZ`n^j{vgH%zyA6w`|Y>i*y=N!y-fDw=iS
+=b!9A#>yM{VCs+X(MsH2-t9o;?D(LKw8_$D@l?_d-8L3TTThppxxvzPg4>c4B<;!}s1L^Jw)>TFkCP4
+S0Q{1}QKPw{W2__tI1yD9!-6#o^9e~98Y^mG3ir~GG}@_%)lqUm++)Txu$3hyWsKX(f0GkEY|-1*qa*
+r`)Uryc{k2exU`jpVFj&(J=7`t=zc6lmxlK=w{OdiCo^VS@s@4-Oj~#=7(z5IVT4@8BQ`&?X?DTbCYv
+LI?Z#hIUg9Mq}uJ!Fs)aD8=dDwoO2%kPZU|`|0(*t-Zbd`x^|Myjpb{(A7`x3x~mh-GhB}K0OEH>Dt;
+G>0cG%(T;3>y?k4@4x{?jqfckgF62v5x{{sp59`vtU$3yigTtU1iSi#17S=l~tVfujuM0gmqtT;>-1%
+Q@U~Ci?;XKvb(z2k=EeyirV0U-9K7{X^Q$!HsW!u>+c(fzvt=wL3mFs`U^Jk7&0bd;X4({H)JD7?WD#
+7};ZrvI?ySQ2s>c62foLUNTY+U2`U-fvZbp3xXpnAYpbdL~!;}cIjF^&42*MIoohp)c>{`;?~@A=}p@
+4h=t@Uvfj`Q-<im!A6g<BwlHaNvMLr_+Uv8Z~OLqA5hBgCq3d!yn-v${HPW+rEAK_96&gwYkE$AvfQA
+^Vs3^kh${*AAE2R&7=9wojdv0Uw_T1KjrVg|2`I|_-CJe#%cb|;ZHPh>SsT3I-S)->*ps<oOpcKu3d}
+AjT;xiWI5rJfeuU_=?5+-S6}tQtbM|OPX)Rn?3_7sBI@hw&oMNESB`&2;`dRigONz%nrp7<3;YN#f&Z
+ItzR9<3+r|k$zIE$Xf%oatr}<Z3eZ`L*JI0S6KQ6){4vi7uev0Be@$$<rFWj<a%R(BmSxQPuCgkVmn^
+&$}nMC7i;D*l`!a8*55K&%Ue$&vQL&3Wcv)LSqbpI&+KmYtQr?JfHUcGt^BOb2&=9_PLWo4y{&qt3Q<
+^THEzXYCCJ_2``tE#H_M<0D8?4U_|Y0M)a$=okL{`g}($=!KM(|PXPIsVg6KmGjPd+%-g^2;wjqrA={
+9MO3n)wf}U!%D>cqxjQ!3VFF+;IF-)L3>dq+6(+OIyAHPHl;;-31`<!W$t?2-SPez{P*nHvx3U8C&@%
+9XxP1bx4<9r@!^LbiZb~8^UsAHIHFEa9TxDNcis^%>ITXK5aoutgE()!^_IXNWk~g(qZ}??)J_@6hwS
+Ic{Q~@{4iEh!_}A6daq5PI{6|Ga^@SYc1^#cp{WgF7_18I-Ij1qRsKekt%Hz<XL*fPQ2M-<;b>-yAlf
+n!d&;~%>P)|@sz-{yQoM(K<`Pd_zM;_vQ@IKDNc5yzalJiM#a{jl|oc~%^_YT#SF29Sp8~!_X>{#8qc
+kf{|hF?kTCCWq40DQr7@D=o+EPy-ADEm)8{Zza#gXiEk@P!<J7T^v3ql};WmUHv_oX4EtJo+f-*Bs_N
+;vmtmm-7KcL*IXL-g7(WD?jG^!i5Wu{t^7Ct#`e^UwgH((B3~&Pf!maFVjBd{MwV8$G%7W2MtsP!{6e
+3DAADn0q1oWIL|!JdH4?3*2ERzf9A{?$nuJah=`sf3#Fie%Ha}PfD7aZI5tNI_zs?<j376Vf3&+aXA=
+lbApEcUNYXIwUCzf4t|N(tVI(*CCnf&b?{MDx4R!wR3h}2tgKO<S<e&N@j<;!<XmSp|qAVa+kPVFv)N
+#le<R0}xgQypHCV$TP^+ZEF$^ZBdBn_jFaX#X0Nkcf*g{yaS9=h|7<4<kz>OOt?45M;biF$^%r8znv>
+n+I__z!>d3y6ny#{IeB6xC7C@CoN}M1zHB*!Ug4lIKTyA8rZ%vuDq8>Sr3gph0^zI+`n!=IA(o{yaav
+?J@q$f_VP$?PK`^c`*Wso4(?F>X)2Ptl~ToH2h1|XOfvwT75nw>oeM<zB^=n9{7f=)70WNcbTfdpUQb
+fWMpJdYR^l7Kjfq-9gus-byGStS|IC?qt6a+;?K<+#h<p1=8qSS5j5bLS<U&4iiV`mI8Pv)tp9SW&!9
+oo=Rs(bs7>udZBh@Sq5D?Zw$ihtIsAL}?CFw!>f_PRUs4{ZzvvINvOs-+U84c`Q@h72A57;j%pb*{oj
+004MKnA{G~h|4_Hzc&FrDz2@`a>9tIwc8tIueYh8!Ti@8P^ZXdwO@w^98+D3UqS9RAea3;CzIwi4qZ)
+H#g~jTX>={-7n<0{&<xUt410FWni<|3)-CLo_^TllT|@z<Ejy(ct8K+G$yz(I&-z)KH&6gI1qGgI1r>
+CWXE(k~z~9f7CPdFEq|_y`VvRo6^x-9nomPvt{uJ{>p-p{G~gh`SV3Y!`#vQ*V=FR2j?UW$wb4=UlR>
+(^*LVEXVW{fO^SM3_Rm_I)Q@QB^?I}IlE9zF_bY}C8`hJ?D5bz3?IIv#1~g#IqtOC4^tIZ!41VZuF=o
+KCy?6xQyl4b}nP~Vs(XfGNc;lfw{>u;5SJc>46P<IYOro^<ti~Aqb~KMa+6ztoM~)oX7iFQnsIR~qxB
++*K7PS3n?=i1IyNSLEeH)$~OQZNUqT#hgBl*S!(fmcCp=o`-pW39=b4+~2ZR7Z|>{wnhJC-j_H*<&mC
+jRLM#{>=TV~jy+jL{PQ)ZPpECwx`{Z{P(8oPam#1Mt?$1Z}+g_!#{!XoURlSvH3MlW5pVG;CT(G*DeY
+eFhCrDH<M9G^_#*w~pg?XAupVX1*xhB51&aHff~W7-QJJ=H(x42Kq3JBk*q6uz|nu!V4VZBJFiAlgrh
+U-+udzAA3BN?<%>5zp;d9pt|rX(eMvdpV1~gX&=KMD;UGq=8xrTa%1>CIVSEP8tx(*mJ$sMi3U7P#~8
+P~(|r2_{7Dvs{F7Xlf|k4Px{JT~;){ZwE1^T91#z9nU*)??ui=$M!|OzYsLw<L+9a($ucJ2U(Sou3!T
+cD0AJMRqXjo1(l#v`Q$uJ8<(I%PP#u%^GG~fOMe@*_Qqoex*2jI7C*)kUmP3gECEx-MCfq%a1VZLugE
+Z<oY#a~zT`4zYNyk61ph@#<MlB1QknRt1&qydi@V^IH`K<{H;i~jd5cInd*WlsI?>WLF4#^>hdz6o4W
+A5jMuEm|aKaF?&&D-%G}sit$R=AIAszT_HXpgvcq`rPEX?Dz-xqwK9#E6RaC|NQe}?1iyRd3iaXJ9n;
+&hRe}$xiZ0nHfhtME2z&zLo@A6OHW7eY1OJ#yQuBt{rdIe$;rw5nP;Bi=$8u%3%Si^<11FI5cT?#Pd*
+Xt4#q;w(E_~DH=wP;SP0`$%m?tGJ|BJ7&M%y!`Nq?8UG-TTV?3nl^D3$fzlZ-DZ@gikvCb3?|A7Ms4$
+sWY%uP>Euc3BSl*3a`J;f(Xn7}7bp3EyMD)<8rJiyniStIbl{2us%2J{8!Cp0=RhXD;}Pa%is3z5fOh
+nb&v4*1vcH`mW?7-P(pV+_=1v`G&aB?A55oqvrQHL5%@F>wjxhh%*<&HIPF_S$PNQXS^x$BT-JIMoRb
+`*qh{C&mc-_U#k$0NjBq%1Dzdtvo;<WDoNkjO$Pqs1K;qqCQhOxYy_Bo7CsSTb{r;@R2JKo)IHPta<3
+6hr~Jv@>X@37Z(@v8*aFP_v+P)_wV1I)7V<z0z6PR++Wb3y-jJs3z^c=M%()KW3%`hWiix0%P|J}=NA
+^n@-6qx<{!Panb-aL>pN85SG@kPcI{eTT3Y(^>eZ|H(xpp(z3HZ#cur1^;5*GLg)D&v)C<rF+(1WDTC
+fIzei(9(J{ole>k*ho!VP02lpSbk<{|RWehMD_(e)ohrn<W>KR^GoJMX;nBJi9xZJKydr!^V|3>d)Y&
+6_960eyzHwxiJje1X5V=8L%jAnF6^2<k)A`i?%^eeQwzGy8+<Z>U?4f0Csqmn>QGF6wD~d^}&UV1XzH
+&;b0$jT^_OPoFN_Q3jwvdqIOX@5NjU<7>!0@C1Ymfi`#9!k7x-sV%v;ckkZAH*MN<59Sbm27kyVUdR&
+Go8bn$u;y&H+eQ72jg93oF)@75ph2P>;Ey;>>1a;2w7Q`|$PwBD$Q9aSl%2M|K{#Q%?MnAA1pY+#is8
+eD_aq*cqCWrgpZ^pzfInC_r1~LfNKH-U2?+^8w$`s-FYpJi!E=~_H|hb}AGl*|3i-iU8}$Hn0CfWCq7
+5e8FH$<CAt50#RPImh-MiPx<erbumEw<l-N!!~U-4qT2z;n)IgKrFs#w$qDsN8mCS(A86nLz~(bf~uj
+)JFX|6h6K75>soFNr?zAOHA=hzDL1pPbaT&7?kZ0pY%z-0o&_-{VU6Z^ZZw;~%vDR3BHuj`{`&Tu|Pq
+vm~ctEHP)!9MQH?T^Icu+8W5WCWoj8sFx@c$O*~-ZL<4#2xg><GD3NvKPP(e#WLDW?<>UJg}=M3y34S
+;Yy&sY1sOtJxclzAIkj1$pLTzMoAx3O+8MM(cpyKR6X6B!pzDbzp1>O8Iu8$zKZ?5xe{K9v{4NCzTD#
+aB9pE?GAnLotJOMI=zP6b)eSts9MjJz5Uk7pzSpv^dht}dS{~zajBmCXvA8ka_KDH?>fLLRnF=GaQ@W
+BU#e7o~qdl3$@1X+h2eE@LR_D5)3``CX0cY(kA{FnNkm5@vHL7+oh???NAwhsM~J00Nd=FOY=!w)|!<
+XNl3ctN+eHji-=?2s94|A5-e=R7?<|0}q=@OSSQ-N!KQW1GvB3Ep+<)`|F_3*kW<@P%xl?F20v?leAq
+_D{<DCHMmus$W9>ssAq3=3keiMO*XK_U}+Oa6>t0dvYi<v_;rUz#bH60q&H?pJ`_<i@*E&k2aUOT$x~
+ekMR%e*q6f#*#g|Zf4^vV!2{G+$UE>yJs^2p{}<ve@ONMTq;|r!{sS7&cVhg4_78G^{s?_1c#JtGc#m
+?>)^|}3u%pdKTZ?v#<`iOIgJ}M%aToYQb}uLAEy=rkTLgb?-2!|DjgWPcql+{~a8g;JU+wf4<BkXF6R
+-L=zsLjU9{v(_tGt*xm!0$GU0^=^;@Elfsu&wvD<PTwV(6)$PC@n;L0yyd-SRC~u=20Kf3bpGLY;!13
+i>E$P_Vm#V-;MiAeT@F*!a60Bl{B%&iDAJ_y4tbuCY;ER~#RbP*A0+{!+;wT6yJ9NQkhMN=2!k3JG>w
+As58fl#sAEi@nCrU9fD5jf>l29s;K&Eyz6Voxpl$cIiSAaFUisN*gz=QWq2C(zJCcRUM}W2b|POqE7G
+c&Rl!E!8V{u{lb;TyX&2~=ghh1p4b1*-PzhSu_E<8ar{OcAExAsd<IcTtp`rkpp`CSU$5}_Uk?NV-xB
+fuQepF1hRw1wV(u?!E<L61KaW{f-8IW9xME{$)PlhjzWhtedg?99x^heMG93@x>lbIwp8Zvk+trFWJ%
+&9KG3N)u-o7jB(!YdVx+>!Hh=^^=MGVX{42#5(DdLTrrPpnoj9Mr--`AX4?&aH-_51fNEBB(Le`6Z@U
+lKN47c|G*c)D6^qUz^P%>N&8+!i*sPuR^@=@`ruyb+uWe1)1gwb-{uEvxs|U17Z$*y-)txBK_+-|vG5
+Vkhzk{3CW9UxckDmL*Q%JKodQ3{K#MU_9WR)U?5OsL6w|%|xFZK;)oRB3~$1`8|0qbcv&piOPA1o7@e
+&>^uLo@jkG!mqzYXx|q~%rs#vD?_T?my5<3y;J=3dan*{p_+%Y(uTukDKWtgg4B5Ep%4;_6tJV*=^$~
+msoH0whzCBqdGw>-QfAxzv*XLe`!T1vc!wdfBwS$$i21Y%Yn!KXV@$`_5dxEz~eeAcfHZUIa5sV6~6P
+$4MZ{?*uiavf^<oi{Pjg94Qyj#Vyyb@cVXnI`M5~=4>M|9H%dj&uKc8WgcIT#IC9~cw*IR6*6eNgm?f
+81DUD*g^Hw4{Si=yi5IcJ$_u^}Y8e^MF43QuHy`!AuuB`j~>#N%|sB@*g~S(C6j@+UPm2H1IZRs^AOM
+girspW=70#=p)Mt30|`QU}J5}3Ghxged3CShK8zb+qRXvu~&GYZh|am)Yrk95)R%6&O|*FeFS3y+h}p
+{rL86Q@ssUA$%E9#-U;kGm3}G~oQeT6{ug%SO1ulKh?*v~aix#c#lcs=CM3q?&~JS9Nl9y&OCPg;H+l
+St$#(AC=|6PnkdL`YzE6D4eZvbf;PsLol(->y6?Ik9scj#1@CD`s_y|~Gb71MX3}|i}4R-6+f9BSfEn
+BLD{V!Me0C@qq9Wuf8AdAt#KU#18xJcpz)Tfh9ef<U3oB(Iq?cikF93E&=9|hf~#PQC*C(nPCpD_=K`
+Q12+8@oeJGhs%3um4P9EMRrt7d$jMSfi_b%Kkxr_wL>PBS((-z=nv&;X~!sYTY1wAPejf-_gKtPi&c(
+6L-=_m#*E>{u5(w-MY1E$BrG9$YI~UebVk>ckzALe{=zPpo`FUV@@}&UABg=T@iC4MIS5j$FZ{M=;Kq
+Ub5PSo#^hIGE{@mIQ?W2LHjKruo-49Ou3xrp{{6~?K5m-O$M&YxkiVn<7yUn^==*%WzvbrU%DOAqbTA
+mScJ10F^E-G_wm{iR#s}Hl9=vM3)w$7nr6pgk1yK4JX?W5)^?Z%>-t~7ZYl`?MCnsldUS3`U_tDhURK
+0%v`Zv5@ueEXGMp*~myLYdwU8vj#ne&SL)!Ihnk?NCa^eN7}By?-q_;1021*^oomw6z*9$W@&NyJ&!n
+l)?WeNR<uP8}QN$JN?O(LL*)r+>ho?NS775q}JUB~mvoEG(4uS9qwct(9`e9w>}N>42&sD}4}t;**Ms
+ic+D=VG$$oU^hAcOgU8svJ$dyD=RDeH5d;y`c<n|S&Rkvn()ixcmZ-(dZ^Zjs8`{uJ`(c$uITHi$j^s
+0`;m>AT866e(+9B~@}`!H4pK`8*I<6QaRkNyyTEnuf-JDx*i865ckbLi?d~uNIsf<U+2il*?DVOen{k
+GR($Z3E{`~pY^5x4VZ>sL6@FC7aZ<M`YUE--zr#dw|SGb!d?H~51rKP2u{;0|0^RYQ#3v1V|l^8Sgf^
+%ueK-GAOm%&ts+r>Q`x(C|h^iykC?wSPtC=?1w+rU~?Raa!ZkTv&>P44UKJARL}C4J}!TO;lNo%m2O@
+X3=Wt;2^8%UUtEk(!gj6z`L^qz@hCf6*oGSH<AWJ%vlc13CuALH+5>nKNpg;Qi7bzyG_bfnsN{eT;K5
+nMD7Fg)DYzcKzgj8_$}g-RHsln#sOR**?i=&K;OF>DgP3nV#8oKf61Aon{}Y`>))?bGx1?`4s*zW2*0
+jK0bc*=+T!Vk;sR_M%_4e?AVWvA3r|H?!@Eqo1%|u!!YFk#W|1Yx?b4P(Q!7BNXYS2K8$e|bNY(=dho
+Oh7cNj|{aExdAoOpPc}Q-IzMc>{$jU$2>!5N)eBFLcc6CNw&$;L{F`SBL)SSYWJ@c-eGZCL&5@F3(-k
+200YxrPjG1pX1<c<aX!*&jQ=GLd&@CV{JjcsBc!K-?{qxa`8SkG14v0;gmJCQ$<zmhAEt9V7ObWrdG)
+=Ewe@8~~tV@-uJ#<?wWu=Y{A4{~vGFLFQfKJv~bBJU)IA*O<F>=un2hIsMC=WAsj?)Kitc1?#m0y!J<
+W1R3oY7OdZKYT*QTf}?Bdc>N<y5t-2Lpx<3<QxTp55{lx>eUkG#)ct>fs38i>E>*iPZGNmx05%JJ2eK
+DNg6BvYFS-EF755@G9F}&2)G(!iN2)9;=LD=GVX6I8IQTS*_V-@@C(vzfyuFcK>1SI$)+4@XrNEZlN{
+;?K}mm9jX%1Ftw&E8%M~kDNSR`%8Oy)C9<fesUnbXo9<Kj}_2#Qlu76owfA;KI8LO%FIM{7sF2)0U>g
+I*__;R=YbG{mXwI+$a@RQKTACV`}H+EV0{%!8-vD5HPKga^Ch<n0kQ%~Yr@@#zf#fujwT}x}46VFc*h
+xV{@Xww|(oH@?%dHz33o1=L=X>zwGc3hjqj_2|d`>j2sv5z@B2e8}P!y3Du^NGFI9?`sB+d8q++HB`c
+kKAYNGtQYFxy#xdEz2Jc2P)TBG<fPOE2_fTnVUmZkA+Lh0+s&og34e?s5)G|Vbg+=>dMFb;mSwr7G-)
+W{Z+vYfw208!R*XMk9sq6KKEIVCu>t^b6EZs;a&E&*uG9&84i?e4h1(gIG@FTLV?=Nf=b}oqEN6dSP>
+`<gvY;hfBi(gxRf70{AQpoP~oZIZ+50XTv%0Cy)h8V^lT31m+<3x*_j*s72!Z;PS(OHE}kI1aBAvV3n
+%fKwQ&5V#pkSrN}f4I`9=9lmliHxR$T17dGPsv_nF^mrl_5abVtrddLjdn!AP;bPOp!*#t+21;@$C{c
+z=8_J{F&wNKed5EJ+k3iW6mt`b2BuK%y(ro#;tuo>}bqsChcP9UalGXfoOzJs<6f_D1`o1JS|gNY_|b
+O{_lF9BYlW#SX+eVqLLhtUGo-))VWE^~VNcgRzm=SWGkK8fiwl;W6eJUSo-oXA~GkMzOKZC^Kq|dZXE
+BHQI~=Mu*X5B#mz4ywPtA7=y-$F=lAyTr<r~H$CP&(`zm<^UMOX$SgM3nPp~;S#LI*t!A5f!0a%)%%s
+_Eo;Q2UUbEjEFbB;MbIjD@bK`08^tdNJPuzF6xaYBWy13ICai`7VUfU9y+`y9Gv+wQfjr0pSjYTwluA
+Zi+>mGfc?$wv*d3u44^vd)ay<TtDTlF@5s_5lGd(i&>?Y~e<0|XQR000O8^OY7yP+wat{UiVYgO~sS8
+~^|SaA|NaUv_0~WN&gWWNCABY-wUIcW7m0Y%Xwl?LGZ>+cuKF>#tzz%_9{{%tv#*eSYiiIf>KyHnII|
+r`=u0Whe=<8A~J|Nm)^L_kX|n06>5QCE0EF_TDXD8%q>000x7>d||NT4IdA^aW+ZP>pgE#O^3h7H@%(
+Sj`u3dS4DDtQ+dIwzW4m;vu}p*&kOIzB#l?zdHgBKLn!t(8H=<O6R*m=>PC2{tLi38z4L5ZE#pFXZ?Z
+)?iK_(O!3R-zF|?OfMKW4c8GO4ggqVx8LRjZQcyEth9Uh+_hSlfFi_?ks>g4$1?C9nDi<7f+d<o@x(;
+}ODQ8Zmti$X+^m(265r~rNfLlMy|sYDUa%3e?YFp5j@?F;n{%Zy9)4f-akWG?jgq^uTIGAl#;mzxsvI
+GL$d=Ata)YZ2iu(KMOqnm2KIGfPJ5EBsL(Kb2XkKF_l2YrwYpn(2<@S($v!<LXAeC^Y2qhWetut#qAA
+%yY8~UlRVx-40ckqv%c)B}_pS4ZPPdK<MV-=ds9fT4dRAHiyQ&x(%46X>vXA{+i@(0Or7(mVh_F-2j^
+XTt(F?$Ct?rkS$>VE@Q;Cf^w9WRXm%C3ARZyUbi<FRgC3T*?02$z<YOiaq#-!;vjl+^!D)h;N79DfjC
+g51MJdC4$}-oG(d`i5mphRZ{WQ;|M5)3Kx_2zrvk|VKE~r4G3a^iXN<p0qP)leV6_@}*P@E(WrU*{z)
+ZxGNPNbY%K;&Mk`#T}Es0vyv*WCo$Ft;%nEc2;47~SgxybQH{qC?RvZB{RT7VU?uZSXqhTqb=Ad1p>4
+z%6x^{6LM=r~ISc6Y}cLx%vEx4aRlmkKcv6QK1eQea$FMF6um@cew0uk^$x=`{2Edo=u`cc;<m+2Na`
+KLb=h&;6d5L2alN=Cin(!U#f`I2i0Qscr)QQ+yY@>wa1}>U#U|%XpUft{MroRtnc|WB9H2<KbC!{{9W
+W(qtIYl+efME-nJ!7+RW5vdZrx0@AGVY5;qLth|V?=kXr=24n(ts9~!W|55p<^u0$k3KL<Md(W?Wr+<
+OMgs7G8?HV<E(0l&L7=~4#cX0OV2l(CQDKr*^te_tj0=|qz;17M6wa7Q#gz^nQdU>=KALl1ff{}nlk#
+oXdYZ}KnvF4$gN(PCgbbGqUfM%m_Ow~k8y$IG+awnpAlmrq8J~_4lpf0h_z(By2F2W?z(f89;^!o7S`
+|o`NriL5Fc`njP;7_1CTY>xO=;DXy^e^uY-o8C~wI1etTbR(r_s2)CPF^2I=SP1%^!rHUFV=xx7@!LQ
+8&&H0e)uU%(trx~jXALJDzQB?-}ZWn7e;4?P@xdSuYm^!h41^l;E$(&Nax>O{ye<e?e~WG^)CJH!(<o
+VJnr`bdNnd$_5aAl^3``Amb>se_z?1k{$mMYl;BVDOI+X=E`%jMgv*=R?|RTUvd8e~`{R?d!&e9Ahu+
+`uJJ7((lk-C$T<SD@JwG}*{`ES)P0Qy$9KJj_{=U`dsPI3Ok9WSh{P{!iA-#GG6Qzp2hOOu5t*ge*0o
+4h<zWn*?tKF~v{^5ML-~UkV2I1rW?+gg1f4TVK<QS@t{GG3OHTjJnf3kjE`oy}kGAu<-tn-OvoK+T)O
+o01CJRePBFWd7nV@bLT=+(?12M7?b(Hqa=vh?18I0oBckc~cxan(20HgLS8ilU$tv#GIY@M{Qr8%kQR
+-$3io+~59T{|@qme68WdlL>(uc(^I!W;XCZYyepixNEt#flF263SicC+D(Rw9L2Z5fLnK<5OZMnjXoI
+d)|HU4wmK2!Ssv(Wx)$(k?{?tbc}Z$Ouo(eN=PmxO`?=SWI&gjKpD%B(>TXYybP~<8$zrDZK3&Xaj6d
+woK8gXxFrYD2U`?BaC>OIz4zxyrCZ$$6Y^BM+_{X}dvn=lD>YDz+7RyySdsvsaR5h>GblGeG_y6^XFX
+9B`(<74AND2c^oyMb?K>Y=AU(YJdH46rMT<$Id;e>>Y$qGs(<Dj0hdXn{6TtLDoLI9!wZKg3v_^2IL!
+3c{o2Q7yMglGLGK4wB~GTwLIGY1k>n|VCGMO_9Pr3#l%uexSEy2-LzD5p3`%~O8D1!HrRufP79{=T9D
+fZEX_nN3J6NmwcHNP4EKs6>d;3r-JDy%*2_vma^@^nuo+lJ=kj!81K(RJo#CoHgK^<xTwjKfd*A`m|U
+YsVV}IM=J<sCb>@c-+-D3p8<F>UGX=AcGW2GIRa1#2=+idams^8$HH5bAh&}~8{ENflfG^wbOIr2z=m
+;>;ZNg-n`|+gMBEuD!5{20gvUEl8er?Re#%$;{*oltEBi+fB1l3@VeEeMw_<!lKgQ{*`5P!Yw6gYbhX
+Sc+yqpA<%%leFB_%Npo7xD51mI5=^ZCke|6Wh6|1^B&C=lsJB&Sy2*RX{-GBI2B2<LG%z6n(2*0PzmR
+|;wY0B9aw7a43e$r#q0-)|K&he?WqpUw?Ts%OVL6GS<cAX~{S8^<$G4Ieh^nDkjU_!>*x!d>cL)slOJ
+BKxqbYX;SD<x%SB-j=v_;~O7IEcH@jvcYXDWQOI&Jw7;JhLF2<u4rah+jGnrKFsEurjEcrN@<;X)Hz`
+9zO#16<E!56d5;R(7S{DnBE#Lk_SzyO#iJ7c2J)AV*)*rfIXl#YUzKt@DexE5o8nqr4s`6RQ15LW-DO
+@T0Vms3U<SuE;=-;$RsKsEe+)T{5!;l9O?LiLZ)$R{jBCoL?F|<>=#NRZC_PjrfIp_7BRYYh{%PXQCL
+ELR577=`m0iOJ-5Z9&I$(fx(t0jx95SBmBJj>Az<6zxfR}ZV9Z#|-Z|I>J!i&7P1UzkGeKoC_q}R5Uv
+~=u8<q*v|L-il(skl}HP3Z##e4vrzGz+>P!qEV6Gw^t*9Rwsk1NSML<b%uGSnxI4H1D*SZfkt1Vp3ue
+Qcya3P{r4+_EDzg$yahReY0r>3*qFeBJjD*#;6byfG!1J+}v)J2v{dg%*k#ytgpKF%0TSGV1sRLk3}-
+7O429tXp)9WZHAl!oFf9*TrEU<-jFP+P+Ni2g_i}|fUqok8c=L1$Q}*42D(@bs--0%E$l(9DEo!q$93
+{$Y9o|IVQo5>ivx{S_AkMgsm%(PcTm_abLo@tJ(MPClPR`z3PhwKfme$hx_g<VRgm<F5QtWmcULW$%B
+uMXDZ4v}iKIVe`L}mvx2!=m+O7h#NL8%b_0;gqO;o#Hn3B|NgB+8#cNChkFVhgnH4R$OXxc_o_c~Zka
+Tb_7g3+`M8y~GzDvt;WL7-jjFB>%k$u-`e#ELZ8E@k(}c(xEy*H!Wlkpl2PF^(5d<WY&@`xe5GKTk^3
+cu4w5TimH>A#hM8>tbKb75&dxl=qr;|EC`g58pPI>HX%M=DL#sv}Rv=6;Wbq`WFLp-X)?*o|7f}8yG2
+$>sMu>_hed@?P(LU>L4T-)EGt^l{aD(r`M8-u(mNMR3U1kuhbx@j45O_GzLM&gL#NXdwWV`Tii?(Pb*
+QV_R*(<EOZZQU2%a+__$}wxI5k{j6)Q6XfjyAo_D=KPdA7^2#bWn1WhS93|iP)AM|tND+4j@u-tGs%a
+&++X|HuzAmT~}CZy4M3zoV8L`%JHS<NNNC7f;aYrsiK=5P;XTB)p*LH~02>}rc1>*~I1=8)v<A?;EPt
+E}2cWtCmWY_E~7$Au(?5N(IS$p26@l#to9St9jYm;o?<@SdVV3ZK4%I_;rCf95n)sJ{hfPyt1%92a{4
+a@Tu)g}w#sAJlj6PIU`KvyJX{8WnvPZX3RlU9P~GZZS4|XYC(o<1^O#pf>tBOyRXb&}oh`?S;gR^b1|
+AB{X0W+MA#=<Xx9>9!5v%`IKAoLB-(?!m%-TnI?=V0fOwsv+Jw?Ry$Yb21k3Ew{kC;+W`Z=7G)JUig6
+@NxF}|2JQdOC+ZQ6mO)B6L{jk75jU32y-v>Ap?-BTMIZl$cuBH1kXz}YM23<r2)Gw49h`*HTgN)U{4j
+EHOTZ<0b2#lh%wGdc`vQlJAWRkLpHkER{kpE=M0RFevV2)C)bf*PsM=ZpQtTF8lH-zg3^Z=c1sGs`%z
+8fW_#0hTA0}tqc#12%7(BX>>nI1A(2Tim!Q$%rxs~d6<e$*wp3D+il+qo4(=W|4+)tiQFgR?}k5RSAM
+@xJe1T9N@wLll1++ba_(3vc@}{z3@|wC3%d8=o@yVoEGf65j-Q)1yM@Ql9t#N5qW{2!fgvHSd-HRHM~
+DJA8F=_SztO>Yhc}nx=C#i-vK;NmETpQ#E26RW#nMLF6Vmk?A7(0&BJDW$hxPF0pM1q$&f)!oMD!!gl
+`V?ZL(2Yo{=`Es5a6;Z49rMvLpf|Eb6-(ln}!^o^925TB42=WS)^i$r!Hf47Hj3>#yz;_FiSbu4#LbB
+l0T@3;cn>(~*6;#Mxk$ut30EE_;0-=bteuKp35t?`S|SQ8ZG=1GcPAr$b~&^)Ej)KI7K4`I1?E&8SQt
+`lmL(XBuq(G2J{;2^xf2m-({$ess2dbObfawH5p!jJua-*ok%&rm|#uM0K&tYw6NaKTQ|PuAG##WaQ;
+Akt1K4vxBV7Lv}WgO^7gze|S{hEJ_^{nlNoip8cLHQZv34mG`Fu0Ba8navbb8#q(QmM}gI-T=m}UNl$
+<w_4YM??w&F(rTKqjSBF>tWldIC5`Z|K!k9~6KwSSfoeC&$A;PsU!5BFR_eK%>+w#ff7|iiBn5HrYB7
+b4^d>>8W2wFUz$1pzlU=Vy3e*6Ch5~_4odoi~AuYVbkL4KToT_Zbhhgjn)RLe(7#jvKC+Wg=x=70|Bb
+zYYfLohx0V`~|bz4=7EUR$KD#}64k)ZZ6p51oUQ+224o90^XLey2<lVvooy)$vs)3oolP2p3Rd!`bRa
+^}R{wbty0il7FFc@PitHI1*|9J^}CNk!hKrkxl%&T@{Lzl@L!#b?nZh~~4k?9+VK$wD1vnYT1dA8oN{
+eHWM8cTsJ)+=Q|Ij<aQJ^)*-Cz{{!|GHb*sZg)m?B~&qpdIywCh8a~$vTa5oJ!EOX%wrXtdi=GGf*sJ
++XmhTz+?$CzkOZ=H24q;qccShJwhg<YX#3`Oh?2_N1SF!0%B9)TxvUfDIIT{RF6J1yF3`_gcF@}-H5c
+i`VThLFo1`WM*KBqT9UB;Stp2XHs3zbCJAIk7-MQ)+0fFDPO*LgH<nanAU)Oy_kYZeA#R?Q?5UxN49?
+;^P;^7g$X93&D<#y23iiS2XTU1^uK3873NGgi(nhBg%*ssT$d|ajzjd$RLHFg2LP!Tr|_zMW`z_4?7z
+^A2bXl~moTC7z_6}N5IbMwc-R2~>bu%h)`gx*0}0!h*to{{jTK<S`Xh9bo7qJ)XtI4pUQW{c~adi^pA
+_l;6of3lUz&iO)Ir)swilr{tyJ~}?XIC%T^@C?74|8Q`2NUx<~zK!_qEv=?F=eD+$n&z&#Sy98M<+Ho
+AAlY8HJ@3&Zd^90R%#M2lEVBM-zU|wNYyW$Z@{yQ5np|M4GJw%M0c2M>0~#D2mBUk3682;TDO%TsudE
+4e`f{}s7=_byK1&@Re-U3}Tf<uNY<bpel&@3MoxYlS_j7K3YNJDADg(58*}tzEFFSEBEgm1awZ;|fE-
+2yh0tk)YPe~vK^upc>>L>kApjp!NhPZleP~AlR$eYKvup~=7l2=1%87|5oXxoqaU6!b3Z&83*)j^%Od
+wzlKk4~QH8T%y;raHET3>;mijix<Pm8&fUGS<|#6(EXWjE^7RF3}s%8Slwqs>(hfk2f|91EF^WvL(Xt
+K7NEhu>3xLgvC=2MU@|*c)$^!$YWz~4A3#~cpycpWdg0iFJn3}$7EbBYjYks-H{R|$vCc*eStftnFJa
+dfKiv~^d-tBx|jx~kF1fPMiVz669my%g7G;(9^*pfaXMb9VzQPIzI1_YjX~TSVIVB;9|>$FY$ic;KsU
++Y!$xm>E0o3CfMehgC@?|+jIiikfL1>sfsA?}4H-|QfkN&-Fiu11lqU`cdgxN;17c&*pWO)bb1i^q_G
+|*fZ3$ORCDO2G3|c6HyF<@9<7<yXq4z#Ik34=f;)PS_a!no|KV}jcu7Q({3~6JGhbk*a!yiAQi2U)RZ
+B?p8>0%WX3zQ^@=tKFv=9NUT@G~z3wkq&TgQKdhHOi^iurG7qbnn+${r^&S3GM9P6f@8YMKN|HYj4>I
+skNg?|HH<5+F)I`yhSL|M*DDAERq=$;TR%EMJyd^ryT~<jA~)5Zx;<g(C+GD|D2enz^eMyzDqD8{=zH
+CfdG0Rw5tuQUiEv;mh~_jQkzfST{eO`?C`cBP}_sI@vyh*O55<2-M89BWgHEop_)`;5OQanl=1-b3GS
+dodY$i&rc{Axx7+%q{ea!Y@IjFdikcSi<|7AmnO|{Pd$>g8`Sf#pIo8IkX0$aeRA(Jy2{-ESmbI}ZWz
+=E-7K9z*{IEf#Vp;D(pd)Bg4d!FWS13Br9^sdR1_1QCf?&CzV+%>uT0|M5(>#wK$Zcj{{%x<jEDssMP
+!qR0Zm7(#A(SVIY&ILk<6DaIm*?9}7^5*%#O)ami%me^j##=Ktt!$08Q$w`fd{)%aSig)od99&ZFVPS
+tFLTe8E`_W$X;GiMI<F=rnLWKx`qZ$umoeBum>fcs0b2iuS<-$5qy?eamy=7C2a`jw-fx_umu7hMCDb
+m&k8h`xa>|lh$m_F$Agy9EMBBzm{F3o<e4UzHq#2n^<;LAx3at;kdpj{1OjWvs>G5*nJtR3ko%?_i=!
+BKTNbmdclbC1G7*(uNC<<K3!DN!l2@z4yaGCa+zF|c`+SOefPRuvk$oVm8VM}c7|9k<-IDy|EZZFy(*
+Z??RWyTv&1Z+?OgtkNW0DhEHGucjuW+&eQ%s-kz;(7s0*!bRsxe*^g-9!q;Q0F_Ls6WyM&h@PRc&BT_
+unuwK9O4Lr)A51Vm_UAFno|x?`tlrTX8dFvf?f;pI@=9wJL&o>LYUL<1Wlay8F`=ctcSI^Q5HUH6q7*
+oeILoXCYM&qtjT6eSEg2j~q?I;{CoXtz?+}whUuF)=sPXu`U`}J<=>S+US{x26fOzJ5*!f*})`^(aYK
+}S2V))-Rhby3FMbe7(Q~+^pthYnO1)&20*U|Beh1L_V$6647!ZN_}Fm~3j7{95prCUk8msBKuq|oU13
+QX$sKCn3!L4qrHl3cBzly;%*7DcZ`O@LXX_eM-H3`PnxnuBJSbxrV0cA50W>}D?z`OBn@T84vIO|bP8
+}3<`X1Lac@VNhoU-@F@`;7(RztltpzYhCK@CjJZ-ZNMEh(%)hO+osXsAiAlaZ0hUQt+g*B;E2q?CxlR
+m3_#Fq#h3C^pWyHo0!Bpv6Rxhd|&&Ba7rPDr$!<K(=|a10%}bLPr8OuzHOi!j0^w;Zoj&A$+!G-RglA
+1&Y8g;Ns?hHK4EdCC)bY7@uu9`-8Z;dVCObNhx^v2BAUmMzN8UFlc{Fb1#dUEHYnhiY#zS$IS;lm-}j
+Upt=*OBsCkmr=I2#0g&q}$)ILEQ=5B<iwXtJYxGkra>5Msb;y0?Y=F}^nU$LlUha6K1!c`c`#Itkl+2
+qrFUg2P3lub$wp+gW$URruqyzt@t2<)i-{T}GKY$#{6}FFh>_avWI>@GYGDa8Esbh;Dujj`2pho60xJ
+pgU?PS)Ke`O}EI@2K#;4?u}mpO3W(KphtL(81BJ@{t-up^~5faAZxI$X#hfeIdhP%~as=%T?SH$vwkD
+7V;sD2dF(tkSAuNS1CacZrGY13)<u73>cE5zX2z6&#;#dff4(2U#d@iF*n`J1xiqh&GCRg^h)n<kB$V
+ltR=lCDW^7!$Fr2hHJpeE*T)VPmVS6Wf7n70uf$^>bznHEZDGPZzite@rrHk=+VqDJBwWQ#7ZV6XL1W
+@x<efZ#HV3C<^)odV4z`w?vkKv0Q;W*EPUE=GKYuv9yU!mIxEh<`CuBcH6})p!bTJ2@U|*3p=t*K;j?
+i^vN(sxyG{P>K1$KWOs(nm1(=U9F%3I&^RdD|fti-sOrU-AdXa3}hw-^(?@*+}1s@@_ZKNW~{KdoDP8
+w2Uy7bW{5<OClq2YP5d7$Qi@9MTMZ?8g9vdb<zn(HZf*n86Fb{FP_m?ogs-i7623ZJ%66y*b`xC^JVR
+yfG!F6=@dWE6^our+|m<z1)}$931bpFGo)9Nz16VMx1bMM_ose)(kPI_E9$O<<a+LMR4!Pf$9X`2iBh
+{g(NbPQDh5jZVMjV=Q*uTPVXCdjHt_TqoaK!@$&oM=T|@`z=cHobpE%*N+DDP`zB!_OD81|9yUP>{;Y
+;Kk{f2*Lrf<3SE+^Di+zo6mhef^ZxB0-KYYlydDg4?e!q2%XWu=AsT?!qhFfB3|A+&ZhO|&L68;zJIK
+EVy9E<&XKVP8ay3ZSLc!A5!~=9$Uxy)W7==T=uE(%>!<C6K8#57t7Czr<AJPU(*o5F;xeX!M&1<|v*k
+_R2Ro^W5Zz6%M2@W3_`h$Bs$t8R23*Efmo47Vw+#b*P{1RMKC)VM6pa!=+tOp{$6&k86nmVj;oqpsD*
+9!8Ewst%^m+Wyovb#I-{1PST;4np5Rl4GC;`AD0H%s=nQbBhQ#cX}RhO3~C&gkshr7z<}n#V4zk{%~5
+mqDZD4JdGv2y=GPqy{pQ(&^t<53Gi~n-W__s0}D*mt*Ht%8u&rKMEBpl|7*xXatcw>tEg5%+onkosgx
+pr?IvDcjlruA1MZi+9)cAWE!bldG1tHg$$f<n1X40>DhHsfb5X{_`Zp-m{)}m%7$bdz>R2kSfNRRlFl
+@<%UsX0Q9R>&74%Eoq3I?A=NVU4Z&M1?Ko2j)w`N{^mn9R9DTNI^^`3{H_sFy$0~vPkN|L9x7Ed=&G?
+;>YqeIXikQdpfF%0PkzrUF#jQasD_r7TahN){B=$?sy(gf(=C6O>fY&6z#?Ekq$XLV#lx3%H$&liWsn
+1A)$4AH+whBTeJ!pMDa&qzh5esgc5=T^zPtfb<W*5WoaNt)rbp3PO|uEQ)0afgMZh8|Ufa7V)OR&$YS
+eK0J(?Svt&_59gVZVh&g=mAwwKET6cW(Bux4NuWY2PmC*va_vkmUAZT-f9m~Ug%;weYuVG=SR&^%gm1
+0$Oc&p`dq~0N=CVLtBPyYbHWH33-OvF9u=k#=Jw%ZG^hKEwymx%JPP#(jIG5F0Ck=Ry6gshhtCgwA^#
+zz7*lv$Z%QCHcUM<}#}wCtU$^K&ja>ukclBV`&8Mup9_uhTSxyaZ;BBH^d}Mxgb?S#nnWpQW3H(q@Y;
+GdWR69u-@!d_yXtp+6?Q5Bug6#6@SWCi*X}PvMbb{v$7AYCbVaJ2D=FV9m`{QWe>f9Xj#m5iq8=o2lN
+f{Wk2IP%aUIGffK2x@?d!$nBsDruQus-TBfe+|fxOn-x?l3hX&kOPVwX(uH_GOi~dFy@J%1B3ntmHqi
+P}$%u^AZ!9v3CIzs9>%3)B(;|GzxLc)+eGY>U6u`0e)w%>N(udDQJEk7q>iNl%HGG{KfszXqUH0w9j+
+f4m0RYJmaHkGLwUE+ykO3Ixm#qnRf`rD>dO#YjJM39oh9%U6AO3rh})h-ca?{#UxEZv2`M$>n0NUGhf
+}H(q-WN8UkaloCo4nS9K9JRsz!IdGtEO+g@rx%y}QnBCXffZ@HQ@Kx4dH(29}qV@o>G6Dn_-anELhav
+5FLO*8E|;6j_gVvn~1rzzkevoCugXJ5}I-%ataptAh?+}FR)ef<;8eeHM`C$CTR(OIJZgzaJfua83A=
+lI6fM>igLY-5`v8{N9rLyl@dNBuCQ486L#8+upJV<IzhQ&<MVxu0W?7NANHYJ`lpfIRp>$**yk<lzTN
+I^&wRK0xxz50Cst2S+v^8rg7Q<bj7p9u)4kRiyF54~X37aL8|ZFvNK%gv3|AYLGR<c{V|BYQb7PZo@M
+9<c6dY?+FNL*htuv3_LRwUObISKKf+rE#p;4YgRQT59?`QRu&1FD1g%^aRP@iq9rcs#N&IUHQ$Proij
+lu)fh4dlCB-PsSQeV3=!uY8I{>=Q8DXa!vL2y1)icYI{pG2qs+5(LU*Of^d;C_B6R);O7%nou4OPaaE
+t;h1Vuwg|5+1*Dvd?yQD7UTq8Q-O$XdD-OWh>j*n@JddWBaZ0KJGhxH9DI79iT(WD`oxQL6&d?u3m({
+iJC3Mdu8O>9}WB;5;b**??wEgR6`=)@dtJ$~H<#FW6NWQ0#M7mZ0sgBsCg~$(U|vl>^`T&@X2l#c;}x
+9~HH7k_jU>38g9(OLHdK0HaPna2)4w$VuLo30hIHQdT6_=tin*pkK?KHKReumW2j){7Mb0M&W?7+K>s
+Rk#|JS3<F_8D{=E?erO_9mFY^7Arp;D(T+smXpX6Rv<um0Mz!!L6WFzaL^(Gr@RBD|i`hzFz^9#m61&
+o7hII?4fBJrSbo}OovNp+PTp-tilLJu&+W9N(aqFu|u);oBeU)O?kcBy2iOOz(C2hp>jBDImB^zj0#I
+fzc;yWtYxQ#t%>5m#s-oEN6r|NfJr-yWkx%xXqNZm!4ncZg;!stK?KHYMtzD^r;l9F{2$^fNYkBPVIJ
+=-%cG3)a8<BgufXIB_f`D}|0tFTYy;!N)2C$Db-0};zqrT8XNr{&woc`xUu!w_zpYhH0Q8+RVoN=hd=
+W(l6b!&^j$FJ3(DZ`%8PMb3{fPp^|K5=Ea5Q{a5!UI&hX#>IIOTzh;RMaoGH*vX`%<P8i%HX$=*t8NS
+=#C`tLu3{rpB43jOCBuGHP^xYNI~%A$Z%Y5G_Mm&Yu<vbRQ5D(T4qH1qK>2P04wG;gOSZSID4Zb=c87
+ldJ6l3W$BnA{=CnRAKf(5jJwCsUa*t1^g6}G9_$$9~Za*En<0>vUefNQ}dmRYYazm|$OKGB+IOR;t#e
+5`hKT_MRRYpmabxtI_oqB_S)@bDosR9xg0@R9En99$T3GFuJTe=BK7BE`TN?mhvP}N*HXwNegP*LGwm
+1TmN8imZUDsLXB#q$;bX=RBlf#{xikiBexA`K&GzJx{7M103;-|-G66AZ4F+pm3-CyWklOn8AckGtax
+d_(SY4!1p;9q%=$EE8d9ni_}Rb>?aeEelfXaYvr1XF8#?(Gt%_lAe;1^ziwYeV^_Vy?6K^J{(uG&ntS
+!2pB#L6dD}XE^S!T<5^aUVCpcqbn{?mc#lHg8t&i4|BeAzTxWMyY)nIR0<0&&PWdV;b;=9sX<j8j+oM
+`5Dt4${tt%<E=7?(+8CPqkRBe@XWMZ`#b!UoG3qYM3RpM%6wXIiy4E@9-z2tS#4Yb{mQn^!1u-8L<u8
+v5E==xbnG|C8e#&z1cepPzYbrB5~B$bU66{Q~!&!Y49Z{8gJnH8aV{3%2EuYRxm8z?@_lQg62Zwus4f
+f3Y62e~gnUmAMP2J#>Cwo;t|RV6W&8qXy)$T6h^y?>>$p}+`@i<?N5BStbnV`+rFNai`&N%_5fUJvht
+rq^cT6dx}RY1+%N9n~#WrFMZm@58A2x<g?JDwnk3LK6oskI?MOx1ZEtsbn3j0Hq8F3Il0<1lf$#<bp7
+<oK~&A$OT7)e)Cye0W#)v@O2Uw%f!5uZcdhcd7Gdgd|2-Gqu|5D_!r*%4#tIWE$4sZib0lN<?PVR4Ai
+C7^<9IuBtXQ0H)&OJD(#=KOFSlo)4JnB<-!Kh@0;I$OE>Q1W0!A30@8S##?6DcozURV=ih%DefwfdxZ
+kV;3;+SpeL-Tu&7IvIB${kz_W%i>xJr!@9>^ALEz$(naolWgd4nD?p8xOmT^1f<9^_hpK1r}0TvQ$(R
+{xRa>LNyh(y7ywxGa&z_dN2$$#5pJ#dhw+yy`SBa99gyh(k09(QiB6yR58~HFyksFIyBUz+MYv$Rtp5
+q?@}HHsmGY$PsEpm=}X{XKW-jq9YX(4UXQOMyF?oz^%2xp#;9&@7p=yWuI9BOf%}(6K-%K5Tr=Qs{tM
+LuOS*g>CPtuGZA)uDTiv?x`A)&E@O9@E5cvAE6R>eN|56{eK0~KKAH_koB%e=I*1uH7r)K;{e0#B79+
+Rdc8r<plQ>a|N?l;t>-AopygNO(IC}Z^FuFMS{+v&g+CmA+DzoP69!oHG5T^7(==<d)Qgj9^Z7q=nDG
+tTFbdOY%eSXh{`aD@N-MQf<m5=T^%PymLQu2GK2Hh9L$;;sygVvMWzNW#xp&_AAE`1xHyvd9fI<IYhj
+bGI2H{QZG*Mz#_-RoOn>^SEC0Z>Z=1QY-O00;o{l@>=M?`QnO0000I0RR9g0001RX>c!Jc4cm4Z*nhW
+X>)XJX<{#5Vqs%zaBp&SFJE72ZfSI1UoLQYC6B>Q12GIl@A-;R&JYB>@drI1@dqfIc)NzIQ^gL|{yi?
+e&5S&w=NRKhud|V&^ea=vI{J>!!?rFsK`l$oqoVOL@?g>@tbKsRXh?3DO6by#6vA05|8kw4mX=k0)5}
+<=6yq-L26=gU#)A5mzLs2mu6<bASxW-WzABY!n3^vHR#-=Fw|ppgzhA;OmW#p*>f%9rO0N%LTQ=<f|G
+c}QuuN|D%<`gE{R8ynn~EuJ+!_|T(>u)iph_Xe82$iIO9KQH000080P~d=N3V)6WzGQr0Luda03`qb0
+B~t=FJE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|kX>(&PaCv=G!EW0y487|shy;b5Eity9h5|XPK?VdE
+(xFLD!yw2qooG`eL!!6%?<d(&>!#Rr5l!+vJwC}(SiD<+w3RZ4J7}q1e2N)1Wm8z$rgQ3WB*<4Yxc%_
+)7WPMkZyg=2ft{`Ck8lWIY-=h(%9<j8c8KtGvs!O=>w?Y%!c?$&*zO-U_fPwW$6ZW@J~o+5?uGo-SVt
+ae>p+=G{Z>^gG)OJHN8e-X*2u{1i-2HEogxCPPm%9DW1I`EIfo^D&!mt?<h8RW?rrNNW^)KhtwpK(xb
+|qmz?l7DjFIc^7=#r85IEt&`-X<$Sz<%5>FaCftQ(LO)^EG>rsK8JI1lBrJuErzcg|-6C@u{4EQfkOY
+9=!XMr1a7ZgEJhGjJh;_YpHzo#qNWDNH)I;)ElW{e04Djf0(O&Q*eqW*IWMEq{*GUZg0mj3;4aU!OnY
+XRpk>SR7<oU!<?3>@$&5WlMNvV(QD|tR~1Gov!Nf$*ExuWk={oeIuo*>BVpTFBVVk{~X0dS$J*50V$?
+KNO<b=;or$;v(Y&ouh@k02>Y%XM$RXAZo)kb>f@aU0dp{x;Konj<`wpOQDAv-sNg*A;a#!6P)h>@6aW
+AK2mtey7DqIx(aK01001&%001Wd003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mb7*yRX>2ZVdD
+T2?ZyU*x-}Ngx6bueG^u*R37I$9g4vyDGj3jHrkL+Fqff|xcYNFu`GY`x3-v0NiXFq0!lGX`uh(6dPr
+@OkUy53zqr{?iDkIkxD=jHa9>DtZX|G-~PPEStFi)w$U^X+wOk{2^`_T=fA`EYH`+e3R@mF9i5X>YUI
+npag<uCq3;$`mHOx7PgW^^2E3ynmUtpIeiaYxCmG4<FvW{{6=fZ{EGfA7R|dWHR~K=0)Bf%%&?>IK45
+Oss>#42SA1<X7%bi|AgN*d0`v+ZJpP4)mHVvw6(QOdUEp9Pd^c29?rD|yw+7YZ3&!d;c0D~s;gHP7r8
+O8PzAr%W@p<Bu+I#T^yh2^-~h9elYF<Y>ef_^ergW>W1F=no4VS;<8+_3*G3*>%Lc#dxJ|js+Limds*
+1ugg8GxK>#|}{sRC)U8i_qQY3swY69a$pVEN^YzAk?M*R#d@k8j_;dH3Pv_vYMus5^UNKd<b*HLq#x%
+et=WXXB$^WknN0r`7kjYh7k*+x=~s?T$j!0RZcVsvLmY*82d|w!Q}x4RD|qi>xRX3;2F9!JR`+RLj3j
+=Ei;1&%0{vKI4w)XZ`<dGN(1<kA6dm!RNc`6a8QB^cO-3KfK@DRh|{EU?ur2g7WgSUAeKl?8YuwMhOU
+zqhA*wWw85AZ+dOFdDGguHw-6t<<W-`2wbEq_u1;kuMq%_9`&XMOZ0}}Gx{N*Cf}A-Z5JTXFrjQFmnS
+EyB5N8$^d~RNPkCLHJ6rOO&Uj1rFhU3Rd3kIOXxzIjZ|vIKUfUA%nhZc&feM?<+~lD3)y7nNTa%eFiA
+}eu8j2ZA%2%L+4Z$iaP|$9Vf(GzlaAq1tgKUV*%?wB#Jm2SgJ0pb`(sX+th-wB{>~?6DR#jK5Y4xQw7
+Ql3{egOHM&DOsPaw*o&npIW`+!}yHFmX5QgTN&v1O6OCRX6_-u<|ws^M&TonOd5Q6tP6$PUyrOOz<Yl
+i-vIUdjBKrP<7i78mj9*0Dl8b4~IByU)zldv^?1~^=fW5>!zKX!j{|i`uyp)-#mSC_V1n~vgc&5$=cx
+5$3=c)4FCY^Y7hpB&hP<%MZ6IXNDPR=(`QaQmfZ%h!nrB(IN{Z2?s>l9t?f3?LX9#@7=7b@!6nk$I&W
+<PBWFzDq5)1IHVIJ&Lxh-9^f!6gR?YR?d@S?N>4_|Loi(6zbt3L%Zl(%jI`iv0_Um75+paFvDW$`8-D
+ugPH}8WT>Om1V!H7OD3UQbFB5ToTG|$ZH+OBTMW#dW)*F>daDg=s>kb)`Mt+K|tAH-K8a<iE^KaX&@c
+NS7Jo5d2;=Pzvbi&$7hGy*}$@7NzA0~JUN-sI|0b48lLA0Wg)#UbL!F_}lk!<aGekUg8y<fXaLN0)bO
+Vbg*b0<q}Ol1=%r9XmB>Va=|B*(mV^&|HD4qQu(Ipt;d#GSChzY;qqMhEqRYXBf^@T`T(@+R)~|fzE1
+mW7`G@vY+x5gkfnqi{Jofk%;vD;`RGiuV1}MKsB0nfnIwSuju(a@E6STyou~J@)=pfnvC`f%vs5tpVr
+Ii%w!FzvR}rJBHJ>()lu*@MrPa`1(JpY9FsJBW|n!|+#zmWw*imUHB1S0I@*6!4flj#pF5uZ`OWt)Lz
+cR6Fqxn-rTokxuVBZJhro-VgRE{UlQ>%D20X>i0xbS-Q)Js_c8tPt0v>{VpEn&Y7xD(6(r-+e%op4Q7
+=D?JgB!4Vj8WqgiDDEOP<?$wVgyUdvpZCp)}dj%5~IpoJfhwxnzz?IIDl7z8m$;m?f^)<eDn5)Hy<#<
+2Nc2o_Eif2r8L(3=A09b)K594ie6;yw5rGvFZ9V|wI`84d6=cFpqQAgZPVr@2OPWXfRRk*^Eeq8=!jb
+Tyn#VUY?0S0VKm}zRW3TMzbwIBpsirXauVuv&k+u_*;9RXFWEhHLDC=pt5F}D1E#(g^(WuxC(npil4p
+ba1NtF4MUpxUG(m1P%}Qnh3LH)i7y%3+^{oJ8yWZHpceaGI93uQ}Q7s=g?V+G(f(FqHS_l0tEdmAy)T
+QXw>~<Sv9R*u`^f`V)mNWqydDMDAzhkm_>0>xhKbehQTLJZSF`S>;&#f)jiKP1}AO%wrdNx-8Gq39s8
+a1{}A~>@!aOxA{MU$ATn+E3Ouq*QNra7m8Z7!&tV}L!U@e3`BoReVBV}+Mx)>*j)Fxy28F32Pxp{X0l
+Mj*JL5IpM@VvY#BAGGU0cF}-k1%}uM4ox^(e7zA12vZVpF4YQFn_~{d_7EhcmQ)5kc?Mw)00W5uNKI8
+XvN(Xjh^!(w5UeOM<g*Y+TX&XNwycVLW%yx^f^6A)afJx!!Fo|wpn8vwkEp~6-+}J33b(k_hPMMpuBs
+9Q1Po-w_;D?356auJI4=)w4T8YypuhvGbGuHJGRk0e;*#rHLL`NJ<&<MRVlDB{u@yK677K09dJU6>2<
+4+iHajNgr6hsEeh&on5T{wr$eB10hJ9T@45-`iKUGIo0VF)$k7L5d`vZ)?{Xr9Ws5*}Bva-eB8=xC)M
+0*XKb=ahkMz%TqOnAXFaE>5j#%?y4@>NWS%UO6J*9E0UK(Pv=5lC<kW7vvqhxt02tQFY)Y*|>^mM!Q+
+32MiThv)cI1r@2k0$siGMsV)R6)Z)nA2fYwG2Q-@H@R%Ex2AK>Jjha9#V}+O2?DRC5;sSS%oc^=0kt?
+99Si8TGX+W!+ZDD(=J9{Uc8-U?&v7F{EAL3Iy^RpT7qBXOC%P6@X1Ek<YzCABwgk5*Z@lEULI7}Hl!H
+Z<ZFg;9bFoN*(&M<@1J_(*amJO0mQJrmc04kfM>d|JszRYtVmDQ>HV{Rqs)W4`>&u1M8mb@T)~XE(?F
+v6^92!k(et$qmmw{=TOQb?ufJn^EvcrNq%hD$D(jxASPgxt-+zc3NO+sZ73icos5BzyAHc@XG2$75Mb
+B!Dj8Dug#pyLjgzlY++b}LFp`;*XPO4<h~VDS}Dpr~4Avk`Od9Hk5AqOl_w0qBi;60Oot220qA&ieGM
+UeUB(2<FJ}P9BgN8%ZuZ9yv%Dq7V=bQzFZb1nc-1#Ew=$uNn;>jK-W?OzseruvG>m40*1E41o<x2l{u
+aqM(M}`KVmnn7CuH&m(A}1Su29nKgnCXJN-2$v8gvH`vM;VJdRmVhy4Yr$w4faY)0Z<}=o0u2i6BrE2
+H+T7e1C-8jNYTMQ!p<GUS(e-TU{25JD<i~ahGj&0qk`7t1HP>dmU>PlZ*r(mQ#>}@lO=tDSg3f2ZfW@
+5N3f@K9ht`leCs0NEEhL=j>ArRSIsq;|5ih`_ue(kbr*u0nlY&$fRhi@s^+~(rR<se7zm+wcDbpe;{_
+XuumzaWE$Su@ECA|o)8ng#10s}X=4MMd_&4ztcQc?nR#?c&1oXmC%~_~=)UT}`&a;Lc!irJ)QK&j9df
+ML_(T@?y;AFpr5Dl50#lUW?^tlCttCo}_wW8hC&vMZg36PFc}-PP4Y4`1V*<^Li7*MdpGxnybsfP+k1
+UcixuF$&{DgQ0Oeq5F{V~^L-8bQ*bqz21}NsPTwS`N}RYckst~StUKV@7an!vb26yTGtb#0;6dk{4!h
+yo4u1d6LDA^Zg*dIK*QS@=Q_`K&bKLmvP?}UA2@4L6+HPOiDA;TcxY0Z&nWaNEDpM#|A|VxS;g6ibMk
+$O-1BXSE2yRd>v2F*FZ&0IqlJ2CH?Qp#M!@+RPY@o<PPmZ4l%I4A_ETxM0;ZYk(6&T%R4jCJy7!8=fW
+w$k_Prv>4KZnb!VMrz~_`DG1OLRldLBl+DGG<hQ>Zni~<3AVqi+47*=iGE52H8S#6lj6DuBsc2=X(W_
+D8!TqF5>1$_(|*x>40bUC?QTYjuUZ5$hhf>Vl1Td6{b64Iw^o75`45Hz-C$l?|YdcApw{tp@g|pmhWG
+`OGb`Y#5(GVJqzh<W8mioMteXGF;?x1CnmP^cM;Ot88uWM$+69e&`^W+D`kM@9FqV!uysY$KwD4EbK`
+!WnNL}rV{L--@C~FP*cMdhd$y@9Xe0-|MTV!luuc2g!fdH`okGspH5kZb#cg(Iun~q+`K&?h+3f-4ve
+g=k7=6?w6wts%M2D?|A~1cc2`{I}jos_PDQ(`c&K<f%90Hbu!hm4q1DdL=<VdTiR^0ssMCFb4VKn(JF
+S1(may;KQlijC@HcZ4NyD;e`Z{k<NP{$<@xU7FR4-#qhC5SOru$p{r;|fbUC{B_a?AIlI0qr=7Q3<o5
+v|GZh2s?0^T=3_d^%H~zb>2ec1HdpaL<y-%)+7<E#sYKNeAV2^ADq<H2KK2&F`-x&LPonIj+DlMlga>
+#$r~S-;5mgWvgjT~6mmrZK@mq8!gJVcpsAwDB=);3sm;z(!(E!DJOWHS*6YpgQ@X<1%PnpwPLvuIu9Y
+`UXB#fs<vWNRFa+VHw?)-7@C^CYbM(OVV!>x1YdO}w=fLE?1Af-VAFbI@qvKLsB{m(P5Lu8TDlY{r?#
+<oX8jFjPw2+6$$^&S18V&TDJZ#7CI_yhw0ja|Z!eZo{r^gJ*nib4gz@mK(-}hBw`ly-qWE>CO9jtL{9
+8edJJQib&WxhQ;ar;;-;n}hM8xSM`kQgs-N{BA(xB<;<Y_Xvq(th7Y2sS=&QL=(l7wo<4ZRAcT+~rb(
+L&lg{0Y|%1%Q@Ogbd(jS&*vuWso`28$xqlcqdG%va`h?&E7V}N5&4Ip9|7S0)=MBf3;|%f(>(wXgAoA
+YvDS1Lp4Bab2q?ua$8Ek&f=URvCaDA)csA$SJASFFZciat8l(iSxm3b=<6^shcrVycQ`$hZ<s9hT!Ap
+FsBa;0`d+3R_o8(~T8+W2KRv;A=c*w=s<w+ke&z9~uTT0V~d;!y6kFkZ@cmH{8A$b3vY@tS6kfSA}JP
+F#y1xJzekpe!9sQ^lIKG}ZB_mjB+=d!FCd;SE}s6H&}u7r;RvjTc}3&Eotr;?n>=c3wfZNH-M0(6<G2
++1CNSiYpgQeN1uXV}`!p%hiQFg^3ffnB+_TqT$))>H9*Q-v|*U<I5MQ(+Qz+jJZ-o14iJem$E|!jFjQ
+e4=4WK7C3wOrukKOgq6rQk{tD<qikYD-1dHa6(&B^^}GWtd?C~v~(JVJGif_PY`^q<qSw-6c3l6614C
+HB|MAi+I`o0Zs{;?0h_x0otobQ*Hah=Bs+%hi%z=+fS^5Kvxiu{wQeWS%~Ab{J|&ZM`-NFHNta*FT<e
+A^RqHWAY<!EO>J+TM2w|U_pQpGz(`Uf#FVi`Tw3$wSq2W4o0XRfs0S_3WS`qi{2mM9yY8O-5^XVmkf{
+$lYH!xf$8kN#8$wR0QN$y>E5`byjI)iAEj<PIAXPAx9;hQ^2_UB`YwE`JbAKe4|6_V=^yMBr9FImxP1
+K=`SL$U?m2}iF9v|%C2+{sShCnIVse-mH6erA4t^2;RUd5MD3zD;exmyF-Z9EjW$B@SgTq#*?}=zz>&
+vu6<)(B>eJgKrcog^SGG&X1gZ1YqLWmaWjZ?ZiKRo>GE4S)p}8eSL&i211x2NOJ6K5eJQI?#wNkETZJ
+VQwGe&_~Ze2hB!`NI3S??vdUDJ0N(~obUu(Q3ZuUDPcUd83nP=b1)HbPLjX*r(}<4<;3yMiM#`(ezvE
+9uRJ?f;*xf^5Gi}`w*qs)hkmSB|ltGdF3^owid9HVhQfh?_r4M^z{}-d<M*{<+pD*tyZ^4{2Tdx!EZ@
+CHTJmkEe555LuV+6(uQa`I_yh2`xrnq~oY@*UxP@`f2mMvHqzAe;FdoM5N0P5+(<|b}<#^NgQ?$)}Vl
+N)RITz=+txSooMt}<GHdoo~c?jyi5<>YbAhA0mH`(ysjgD4_cgR*yRUPu9}(R)<i>+kM*%GewT@^4F>
+LSKea8~vPIOgIMexb*p^{Jiu$lmDX#Z-|&*iusDk4LD`)z#wEnxx2i%1_#pKS_o4oR7t?9F^I)FnC=)
+qMF**MqY1eaB!EsM?0Vw!Y)Jy$9i9p34Yg}jY@Dp2X&6&r=J%}UDJF7<$L3Vm())N|%@6~+et{L(4yK
+nFF2s`&d#qHofL~Ui-^0Z^zT-HVfo6p)4g*&KJ|4J^#;Ny2pN`67apo|_4B{>~`>wcalA;Ku#(cz5jc
+`n(#EDQe(YvC;wFHzV`vkj&yMoQigx6~@UVnZ4mS#<qVFU-q)1f6Dl;OT_@UE$;v;&U<U(j8&5mdU6Q
+zqTQwyFv(NV<jREGNNbuQZPK$k<Bh)$@c^4y|Vt{e_#_3lx|~4~PTYq<{S&0GKbEubZxgQO|y`NNkAs
+nr~0hHJZ>e@xX?OUdl3|IU3wAl?@_v^wOD7s6P*~-e2!soMw(TF@5d+a1jn|%6jd-Q|W3lmZv&mN38h
+l<UxCjihr(*N&lMfvG*g1K~8S(Sl#~x0mQij`1<YQ`<Ji&^c?b;03-a9Qw=`n5UptDF5jY_Tadilthj
+NTF;WB!Pw(8^@tz>6b9zn0i)K_Ki)*cl66xl!1xgs|i<o4_N6vVAT$Ve64BMYy9RYuz`1JoGaSDWIGP
+V|CGZuBlzH0IO-HSiG{>#h7t2gid{QSdv%v-s%u>FF`&@+=nHJG`%NWxr=bKKeGoC_CIEIMTMWAYKRx
+l}zK28k0{0>vN4U@;_f4=nPYmvr~pS7}_?!2#p++yN7D24FZ99alIS68Iy@d!L6FLNVs#52g8D%^Twh
+hZ-Yy)045ns<zh#eO^48w9CJcfo}-A?-EA5?9~hSP()fn+*OaDUnG&fDBG;+eD7P;)V=EB=fD#lTFa&
+9i?hp_(|G^0Hm?VNea@$#d^@22GF%~TfZ8OwgCS3($!Pp?z96MNGuWEb*4YAkM5Is{kaj+uhAd}*O?c
+ItfeJIVD%+1qWRP#vkujs~$(%V$nNOl%S*@XxoiEpU)CX5{I2;qNhvk?V?_U$QLSs7(Ujy>ZOLDmAn&
+u@dcTCYzq~gKlNPN7s0lD1&t_!J55iq$3F)HDXNpaMM?PU?Qu~`kCxn30UoIx9%-}K<s9_b4qba0FK!
+(lH~ttm#^j~cz1E|y(6gh_bZw>ezeBRDq+?ZlikcXrpR0ac~7YuXyRjHVks^-VCU!+9AnBkrH>VZ;;q
+=wOf!UPK?y%x})m#*QM#(Ue)-3I(nKeq4k?sFk6CZN))kSjQBzXKqNnkG_2)JZf3n;>c3cP!YTjB+TY
+ig6pbkF?12jpplGx^_kw#%m=C8R~<+M7KUGfu}vx5!xSv5wd5V9cw=@a4|D0c+2v)2*6SbyKBi0J8@a
+^fO6=d6DL;2WV{7-!&?(f$NL6CO1^o%RM^;zNwkgt>D5lEFX}?`0vPa~8DvWo1JISVhnDL#-ZHkqj_O
+Q=aRIXTEW1>bU#lcETyP?j9pw1b>NyDR?!TlcT)P!Z|z%zWc5hUr{mWg--AVMlI9iqa#-Q=H<2+Zo;O
+OaSQgVsU0-W6cx$)IB(zsj<LNLW&7PPVG(;3nvwIhrMh!ga@?le@x6FC=k%%)(-Cbi+lRS98}<Qf%3q
+3{-b@yXN2?d;)w<oEcRv9J!_P;9PBOm)#bUwd<_hA_*)i9AxVnKG60>0iSW`p?r}0_l)bY{F{U`@vA6
+px}`&R=AO{<z()AY({66X$^AT)<sj6RPY`%AweS;vkkhFs8)qHnKH&4Ab>zpFVU|8}EIc^&>B^fDwz+
+31n8WBE>8rkC1Dl`ELqsl~_zOTlA9)MP34<X|gV%5{7cSr*^cgVN@l@9R&AFrj{V5{lmxJ28K4;0lbm
+syi4D`h#q-4Ab_{}#AP#ko>etxfHKTx{wknm-;qV_uxa#gcI(+W$ce;ftWE}?WON&F_OugFN;TZ+Ci0
+|_Hr=d$`Cg5Y!{BN3645C0aWCEOoBOi4ce@b>wOKmJ1*$%0<Mqa2h%eiG6!7W@RU0ZzwDBpJP_t#K)i
+MDV5X7KA}*Kb2Nj40K>WYAl4yeH$H1P5K@XHxJOQUBOFC0iOgcJi2IvesO9Waqk$rA}fR|(0xAMjo!7
+B;d&SF5%rP<G5{it0eR3R5J$efF$2iGp6zCfr|yq9sgkC;9)~86n#nYp#U?R+!52b$Wc&_4q!4!(_5A
+U27FV7BX$ek9qh_kwzvA!GPPOfN{MB?Lnt43ezKb6ZO2c{SI18sM*H?Gt;a46zd0COWR_`7Vs8t04Ot
+Y`J^H<`mcrhjJ>fuSfAnlX-4}+`M5W!h=qdLY<bMIfs+bJFn>3Nx4N%DyUbu+uPQ7;L)x7q~`o|0_^>
+#GY_ez&gvW$O!lclFOJFMOpn=ee<YblLBHvm-Az$b*M^7-|hR*EVuH!KqAmeN)~wnEKbV6yu1BHv>$&
+n!+7UH^MqPz<5cI^;Hk_R+%U}L3O*rDOcLDtn2b|!pO0kuZH&y+@z75hsCdq^0&_AsbDY6(xY5|U_#t
+pwZVx6zWJ6_7BlcG9eRa=c@+%5ibU!kIRaB>qHmy_ekt;0{HDygzGYIZ3HfYI$)>S~&;y;k8PGfM34Y
+PZBi9w?PpB!F*SWmN_$ibhG++igPz@CDI0HYU0Zw{{Fd_xiP~#)faKx#+8$>n%8>g$HYxx3BL)R!v5P
+G_unWmCt@A^SDbqEGs&C}n}i@D4+O{aRVZ0v5gTu_gm02I6$)Vl)^2nnPHyxzXZTUu)H8b0JW!+GecL
+tW(gqo8Sd*9DFkj9o2vavJ{8%d}{(`K_DL)(p?Y@D-iZxy0DWd;A8&MsKUpd^vZ+Wk;ZoH6{6}dPp|*
+6ipif9Z$}pBeu|3NH^94jRlPU9rj3VztDfnFK1t8J799P{9C^nEatix6CLp(tM9QW5nCcn4^Gx7TN^u
+Ri;Tb#gj1moJG?_Sjq_aQIJWm3&&c`Z1+{gi)UXR5;eSZzdOIa&en*cm-LWGBI}YK22aW&+KL=or9X`
+^;JI)?^em6XNyx@l5Be>q1VjMqlTwq63FN)~-iLcM}d;&I_?_udio3)*7a?{4{4{?G`J|Xz-?-<1I#~
+#;Nsl+p_y-K`ju@9(S#P4)?B*o~B4K;jhxVVep3L;mX15oV98_n0(_q6TbRyDl>+Q(V$N}-@n#9sZX(
+tc3RH*ae9#v(2x#HleQSOW~>&ETjVka87yS5xzng3-M+c?OjYbDNEnD>8kHnoQPorsR8pB@%_NEx=|p
+$Qy#nW%!B$+4v|SSheF7+VEjqxb9UVM@x4DkJl^8_Ow5&$@1by>awH=gWBvzsm(^ovYGz;<d>;yr<Ve
+4(B?BzrG0uCJssC6tbMNYm4f7%XP2|FUaM0)Ucnp{-J#w?ZR1je?tBx}6MJd8=O8lgE-MyRh5QB$>#B
+oT_~ViKcFaUwX2`}PTN<*|mG+5y$0nQwZQXZof)=3v4^T@31QY-O00;o{l@>>D767gG0RR9w3IG5r00
+01RX>c!Jc4cm4Z*nhWX>)XJX<{#5Vqs%zaBp&SFLQZwV{dL|X=g5DW@qhH%Z{5s5WLS<jFf9)q}+4LD
+N+uRHaS@q8lZvJ;%PKJY>X`D+tWPOARcSjW0Z^K>F(;9Di`=z65}N-9i(_UsSo-q*%Y(N8INRjf(~L&
+4uKC^KhYU9rP}AfpyTGm-Wkktuc`8VOP0j_66wbdOE1`;In>%nB_*fvTo!N!>(OQnM3a0t{bC%Nr~v+
+`EqU!pRz;vIhNQnqQERmN=zytmF@y!h?M3-?Pz|X}=`T6}AFIT8(;q8#0p~)l3XDcIIVtwWLthhqVf@
+8%l~;zl1GQ?n3K{zo9NMqJH9?V&QPtewV9gnBO7<nPR>rQ8VYoFML0S8p?6s`J7jP`YA?M`$O_hc51E
+4u=0g11hDFQ|hpGp%yn+&)m+{HG*qm7Fyfq}a(tm3aG!n80McVb8Qfq%!2=10AgD|PJBn@bW;Z#X~kd
+EQ=kbnMuy_rQ&*;>DTn0pXz^p`FRJelG93gm+0-Tb~M&;(8L+MrK$4))kg@y*6ipp1Q(J6!GVOQ`Ibw
+@&a?a-v(PjgDKk(>yEt(KXPoqKYF6ab`Gd<^6%9){b6%lcJ#-L!cszSxVa8ka&m2Yi;Ekviw>@07Xmq
+;rlB0W*u5&(jr<?YcSMu;5~fc6`G1pp-em3uD<2uR&!!!w?9u)gP)h>@6aWAK2mtey7Do|3NFeAQ002
+&D001fg003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mb9r-PZ*FF3XD)Dg-97zx+cuKF`>()C$s
+?6WbmF+aoLjfgv7Oe}B=)o1G<&vJp(w~=Op#iG^vCvkfBVf001_ZU+u3&Sht+#+BoY`5fcc&Q+-IX_B
+Nk<|IKA9vWicPU#wTn2wLaU+R(E-PxhR;wH((ntUT(0{1!u>1#Ue}DIGYzYVb0mxtW0NN5oc)toyMHA
+cZYiiAI1kkaa*u3ow2>652q)GZ+<yFIvL|bXt&nu^>(9(3&Dy^f%F6*lFsAHGAD2>PUl&^l%S#g+FHC
+^WqE<<@<`}kS@kXF`kS~DYx6u?!pmS477M1Huflv5=R;^2Cir}<$nUn-7<?+whyXMbJYVCt5nmPTkX{
+|+d6rvk_o(eLwPm5O`>w4`CSj6HCTxeDQ7`Y37ZcfLvY5={gs0(>d&Abt>o9je73Sk*c*PNj6B5p?`F
+nzmMDwZrAPEaxa_brG&E%Tr0`|dtBccUgavLnozM3UGy?~XN@j07_aLy-s-F^JuK(1!B1^CH%QRXSL+
+tFLK8AoGdBa9xjn?NCR-1~sv@~AArX~Jd0$??h2e;w?dPTuT}4`4?WSp+}~h`!&z&IU0UtnoxBNc{$U
+7KJIBa>kdd;*P=aVSagi_TqvqfFh=xrz~28=_Q{LCn(;Dg@RS41Or$Arjf_|8omRIAtG#*XP0@nWQd@
+15)GtbPOvM{&CCacaUtLV_GLRetd|x&LZG*bre&PWdca&Zz_Zco?TaAEZg}nw+^g0ekQVVv7N@?|cBm
+K)5b3kyz4edd{|H`oqI7<~iq6lkT`0YL@u%a|#BKNe{?_KbxY2^0pNGrY)}|B5&3`45l6_*K8IiEj0|
+B$LISNYV_r|~oE1+mm^oC6RO0p<SdV`u<<UDEgSfJ#%7n$DsY*r>*0Kj=VFu)j$cbb=yJk{^FA@B-Fz
+vI82{&e(V^5&Prcl)petqDMrXnu){GK1Hu7fA}9iZbWbTRqpyB%1<TRL|GceA29_AJ!<6_z!}di>%B+
+(P*hXkJA~*XWeWXMpyV8gkf|QUUJ+m5^0l3kc(9k7rqyGL-uk2OkMSwNGQ-)e({@N26a<F4Hp^#%f<o
+z<9i@Sq?`<(g;#e71?Z2W>NjL|OAv&x#loxF-^8Hbbn74#6H&lQEd2rMBw_E_WW4v&!TW=8fNM2h`C?
+#FhP7vQM<g^U?}^yFRVL!w$tnjGbPKDaelIdmLk%*qh84W<B^c-vr%_VQIClR{<Pnhj@9T%^+2D~dP-
+B^JKxls9Q85h#|9w3GFxEb3Y9pEj0#qR3iK)ATlkmpW?Le!OkGm(6lY`@T&@|^klr2}tJGpoMn}7DV-
+!7j0FgX7mN0S76y8_`ckpwGz30sSX7IkYnR$IKl9fT=>vh_U$!dz4JsKPmj3%(RaQ>iU0g=O~vp#XTR
+Wl_Tzu=Ep!745owgxjv)TH-{%2eDb<U{e6VhCNbJLW9j#*lWnH!=xmdQhn{N_Oi%6z8Z0;U^wWlHKKY
+br&u&0C@kchRkNHgvup0yi8ZCV)9i-HjRw=1k)Q=Id5j8OFrb4t1*H!YLxqlfi|i@;y4JR}0rjkEs5@
+*HM}==~yy`J9NS%l<=fE2gP%jwJ(IT6EX{?jfbxgkvF7vEh`7Z~}^`~}#V1-3{xs~LvTAX2@i#11+#%
+M1Lq@6_JoN0@?I=8iA+d!04zgiN#-!OW=$*fu?AZC5`_VCmD12zM@7rl$*j=^T3{im#9w%|EPp|}uCd
+Mcpiz-~ZQlYo3m3+czqcoD`4!pr7tPuhS(Rtj#ynxMDCkSO5Ug5Rpgs7NOt4^GC1M;~Cjj1Ga+PNbKN
+Mw9VRM<=It)3fbYjy97IN0a>{Y-+qh)AhOaU<13J9Kcc-hI0zjtxRR920%(4d^*^Bpe<U?ld1!FhHnp
+l#BD+${2@#E+T@q<!HG7U=!te9%Bd*G4I-~#D0z+^D3Sml@lf&`s6gb(&)D!Yx_h+oU}s-}_+?3a#o2
+EHlvPkHpxy6MheAgs0dlY!R+;2%NsDAnAmnAZVxQ}gKL?V659jz6ykQ`KG;@Ife0iwsd%^>Zf8`nk8v
+C_JaqokiYjACisnd9?UpIJ`Ck7DMNS<EDd6qhX)UO*rI+@;h*~y~P^Q$y!!GTFh`W4_|f%l2ufzxr->
+AUtO<=ZQMN9wL_8{i{Dufb14(A030u(54sXiTEK2L6DGGX_3`<`;kqjkRW*2t>`=m@z0hCt?5F@7gxN
+S4djO50+vunpum4uZFA<KJc}YXwSd0u9T=Hlv;j+CJ1H!p~aOJO@mLutl3{qwM0Y8P_t^5dU0B^!en>
+FKeRp;@c;m|pi=x!sHN3@`_Jd8PHuAW(H~*~wOop1h@SX7EE64i(L`!HuWD~<4ErQ=R99`qBMDGLBrD
+UJZ=!CS`X=6LOanjMWHh}sVjvbK_*-tU5)v4t@O#qLKv|EX9vn<0ZAG344&70rnP<RB!q};L@FsOxo~
+-OvS!*{7P|o2rD+}gjVuLS<c=H$>E8(#p9LtC?fO^!9S?nGk@9&=O_6Df9d;i-r$wg1zNh<oKA#gidR
+ttg6!6ldGLr=|2#k+Liju4-P`AwV}$j@__(@mCN!3=DXp!ebJ$?p4uUyn|H?pYLHb!=JX?#bOZLH8tG
+6NPnzdA^IMd6?f}=s{xvPoK_$r?Vb=+5*!2G%c&M?Tw3i&D!X@(6-Taqp3qInhRtAR0n5$9h{ZYXorZ
+rGFHgFWFdR6T8~6=mvC|vlv%M$20zRJR)UmF7^i4aAjowoXjfq@b%VqYtO>G4!ju3=OBh@e0}e7g1{u
+gZ357J6I6$9a@Uld^6>VA_r7UGjQK0V(25iVueq+G1t(QJK;g?|Kff>a440@8Ffr%p8dZNt7;F^e+aT
+21z3-f{=Lj{1$vn-jZ<)BCwEs7Y(u_oHnnB=#%L*Dbx!qGo=NB@1H{(Uj}&&kE}!MS+01ONHKvw@;;i
+p2n&mn|51@BGQR@1H?$fN`PzB|y&x=gs~b&i=o3bpPGp+sEj1@H}|^E6s}f0X-qRRi;CNY{gT9-gqjr
+Iy)X^bwJ#444jLdo+#nVWms@7qHx7~6eyb4C?w#yA`Wwk645W;FdXm!nU?s%vJmh|Ai(q~1R~~peUAj
+AdY;ECe?S`Qp#Z@WeG+4n1Ja*9!m;Gjvf<JKm!T5Z+Iz@0Y<CvlUTaLOH+}Z`$rF#<H$b=GIk3!Ue1N
++9%9diqawP#NP3?Qo6|{*NZBhvKo>0ITd$i@A+Rn3-tiwkLN-I;+oL8KJ)z=Q2skF&QlogH4i9LVda1
+1*dlXo^WFmBHOi6d&~o=5|bR?@SqiXq^1c)rRK=|Q7wMUBi@ND_;}SBt2u`%2$`tlANagv<kv)??C&(
+NP&Kr31`sf+R+X1-_XAF~tTmkk8lPyc13c3N45NjR^&+SOBeEE?6C-Ro%xc9>uVbQK-B^DGrN_$q1l?
+Qq7m_7>X&NrUToZU-MLfipjY&nt(`_Vw;Q=jaH~|3Y3wCFJ}{T#e8q??YrF{$GBVX_N%XZ$H(%!TE3e
+F%4iTIe${!vzGp8CivqY>o5M1sShsY00~N8iLPJX(8CTLc5IoF*+oko3^23z1_S+`EEv5fmG=!@Dq^5
+Le6YCr^**ZMje2ESKPIu;nOfA55!njM3`--?-{Gj2e1?gf(u+vZn!)AvJT_OJW67(|55k+Sgjl9gHbd
+THR%-2H$8^(py!pI@pY#t^FB}ydqQfIUh?pp;jC~&cM4?ZnA?O5D7^Rc0&Uy!E<n5gSC2eHf9tL+QtN
+RG&HV-{F+;CfByOOf*Y5!;C$dw)8hb_q)ZrHZ_&;V%ZR^?g|G-ZMwE{SjX`h;Uv|?u&TvYzZ5UNxBPX
+Th0Hn?e<dcpFhATBO&lyg6rCJr9r7p_u5#}rHZw<THe3Lof@_|9n!XK5oC?@IL)g+ZTy{v%H!wa2HM%
+Y;tG}eAZ~L|mj3*c42hqInDW8Ss81A(d)w-u4QhmYMYs8Jtl1-MGq4x3%Tm+|9aJ$GEolVK4i7E^)|1
+M4=gB(wWa}&C#eeGn^raL9o8{RG#W46y0OE$~OgwnZ!=%;a5xacf?)E8MzG7lk=5Ypd5G%0iL>(zavs
+x{mGddazbPq#7)W04gu!dkI{H`u6<6Dm$RwXZ@ObVe*GnMSGy-aQWiJ)A~z;_`TXob=6rSig^^-+H9c
+%R4~I1p8bWzxXX?rET%+3uWpBMRj%o>1U=Rf=~Jim(9r4aOpvUEwsIC*h@)-BqEPS)5N4p?mVt=&5)r
+Joc3Ntvt?<f*l%~EC1UGwHeQ=C!_1bd`}eM15Ulcun7jHpr`0n@sBu-p(BP2{bpt+F2Ej+{~`B#NtGq
+&d4{VM&^nN3A)}JVN8`g!I3=XCppGjfSvaGvL~Ka{#<U(@4x)ElMY$XVK+P-}-;CGjFDiO0*HV=M9+4
+DBl#Qibic8d?aSD7F*9Be@MRH!YjVdqn(b-ecgMHK^nT(|A1LJ&H#ew7X8q6dI#Q#5MJ?1|Z1B6D(g4
+)+48phz$D6D<xFz8;$)tyeW%fWO3pB*q<+`E+IXi*SCH*W?O@M9MU`tyJNLhG^q{}=iwWxI!;hAK0Ne
+&7w*!<Qs;sO}XvA&Ju~j>R<~7i;<V-lUE`8;$U(;HoQv$~n&Znrk>}L1eYlkWIlMyjk$H-ed)eqJ3&G
+a!tXwN-6yX(l+N&k?D|LxAdb#2Y8U<pLS0U_C2X~@X?$4nI#a?Ed66b;lpVy2n4uB(@?~bo?C@@thAQ
+G@9@q3!T6NWYl}0Z;hVz`?zR|KceFqJu=nnl{e$DbHk!v)@seyiKB7imu)3Rh#`A+u2kHgC<>m_|+Ua
+Fi%HGC(|M0{!b7+q(YNaCYRXI&yC3lYx1^kwK*5uLEz0qqeIsP4@*N+X$Qgu%|`rMZ<7)5`r(C_mB!{
+GCIs*C~ft@ykRVEydb1?$sGHxPKgeqLpNWS|Da6?XI%gSV?Hdh6R+%&ed`wy>K*<EvWMVx^Iu<DV6Lb
+^2Bvk;_N;PDlieNE2pX?O1BtR>!I7#dprkJ-cdvI(&p9y|~8!btujI4A5EyKf?UHFI7qJ(+JHpLnt^c
+)Z;xP-`8HN8j97?VPMuRZQ*H4M3<eZdSWSOwE&eh**s;?Ucpd?A5G~Z6&!XFBx!0JQjISImLkgwKJ$&
+TQeAmBFo&;CPCR;WsiW<o`|S4i7UUGitpjiFCQLD4E3iT=qDZ!hVTF_wfTB@v+r&@TYwS2{1PtLlR27
+RmC^8(=S%TWdSr`)vD+@v`s*>%WKg*w=KclcTtVvy9K(PUo<tT7KnP0~<J|oF|6DNtaAMWY|s+%nH9K
+)VEzimtF?pj(o&6c~?U3TN3Y1N0yY24*8&3*8tSM(=s1_=hckW86+|HMXE>!xPbBp->JMr)u#^(}!$i
+)U740N7vA_D{1)1B;;^Bs6t5>R&+2I_0Hp8I_~}vzA{Yb99pc^#I%Ymo&ab;~J*D<Y6i>Wdeg#5g4Ne
+w>n_f_8q^~;C_wMS#~4n+{gPcVn<^jRu2<`%Q9LpvBZ8@vs)+>Xwt43BRk_<$;1&d(Iev<vW_sxvMb!
+qawW+wjA}Sl#jte}uAWne;i3;m7zLG;3Nec;TZT=e1XCF}Qe=I=qAE1bXH1n+2+~wC^M&hVRCAIuv4D
+qnNT!SMntyA6f?3T&EZCE7Oqg?3rwJXx+<;H^+A<Tg;dE>xtqL<GC_Ld(7I7jrl)n%-Cv)a0X(<h9Do
+X8WXv{*kHtJZrVBb|I;u?nfgB>Pf0jkGs-#^?r&1PAvv$JT|4GN1}9<e0IZFSQ}tVezBLPKvD4ESl8f
+HsgSAa<e3m-U0%Mv->mF8J;-c(^Xxuy6GQm3BuOXj;ny%&CDC(gH;Uk44Gz>PT?2ATlj5lbVT*W;tZj
+0XE?eT;N<Kd^Mo^fZ*xdqCm;w=}J5*X`rH!DNnK+DOzy$Qk7U73aWWtrt(ArCHhF>lvKIXJf8~9jlNR
+r#KlPW56p>~1@qs-<Tiq>=G3e;uW%<qvdRjr_4nkVAy0?*O(^%lSKYeFv~3A@1F(H;cRK@ZJXM`uW1)
+q7iOx@kMpif#SppO-)jkH*3k-sA^gC0I2c;{SFX2exoZLxYw-pYeQ+7r~hq0fs8&r@(HY>3>-rPPekZ
+OjcbNOwErx%83r7r38H?U4#rqUXOwN6K7+z|Du0gCh+>ijunKZ<5dNo}2dIuJF9>CO<1H#{0ufa%^a{
+5ajq=w>E;Abm2{<z=>x6x+S&41)QXGGAF?9HfNJ>)Lu}GK?nkmmM6yk%R76v|(&3uF;KDoQm8@f8vr=
+OYDH80wi_ZCt$lmfTdwP!f_Nlq8bO7Y6!vuL)RKdO}9a-muk>(L1h(d7oRj?bn;#MYze)y1w^wl){~X
+74dHXe^OPrA!AjVr^w&X+;_QE>7TQK({_9{fcx5BI48LT#0Rv+iHsGK-Oo}IV+NEMPgzwmE7h!;zs!Y
+>Qz?LvC&R)I<Hirxh_~;73jIcU}c4-vD{PQepmud8M;8#`yVTp~Z2BMBKLx}XSRHa9E*rC0{eRhh*cU
+{~>0cWBi6c%(IL0OGx76YcQR9?pcO6r-Am1&HN0LH8oVH6rcDbpTb9b=FR*r;bh*`N!ZTAhqVU}?FWf
+=LfU%y<sN&Wt>%T@wbYeGjA+#*v_1xk84!;Yrd)R)35VEU?$=A9d)wqbx<*Gdng<;PHujTf@HLsREDa
+;#@pGzj=Nxa6AV8GZKam_Se+J&i=;Ei;HKEfYHJf<C2y=ho7!|`fj2P7S#Wg)B(cC<C%0Fa6aITM+Mf
+>No<LPT-2VLRZG<#y9oNj(dogqBx_k)7ouz|a&R;v*x>0M+g+`wJQ0}i6O!~dP6^*ft{`6qx(L*}VWQ
+G5bX+t{FU#-}4~oV@*|IarpgACs{|GFPt}#f@Hri+l?VjxYBx&g}MP4K%tRgJ~KnFqn+cH~c@2}%Hv&
+TQnbtVk??{n;fMN$B$vr9LlImqb{1CSmtO=i$AR%wS=)rAV6<~hu$>55nzib`3~ao0-FYayPp@&3<}Z
+dE-dc-O*JG1x~e%rMU~1h&QkbLu(;e@5~?0>Klr61JO5$ZeU2v4bG`PUXd=)<wV(nq)HOBvl$NfT*;R
+3?Kzr?vgLlSZ1b_S)4OtX}7l3I#e&Qc-M)))a514Pv&o_Bd(-c#M<r#p`s#-4oIYh@3OKA1b)I30*_e
+}We?aCPbZxWMsienFBQql(%f2C0OkAV<L3kDCi}O$K3uemA2GYSzjRlDGR<;%PlgX&@2T_rLsCty;-4
+nk6u7UEZ}|CRGL9DEtL2>8t7Rdeyc@&Di~o5tc%;n3&jUhCUae$Q2Vct@EzW8z&8l>}t!^KnOP$XA7J
+XAWoOv=;W%Lr!of)u#t*$V+gz8HA$>cYsqfx*lypHfni9<P&I&iMD<G}a?t$8t+Kk!sg`rz+n4A4=O0
+=2c8)syYY2y`_!QXR^rawWvn!Fwt4W`IRJkJVX)jzIotEpO4YjMIby$Y>V<q%szZs>ev+nTmNFHKH3a
+sUc~Ex!V@CorV5L0K?v`;z-6%WR6lI31(Jx-xjJu?T=}-Xjz>;Ua(VwJs3-o?A4y5H;`f@Oz>>R<wAM
+kVN{s6+Uc=jDjexjXl-~j{+98}fGGO#L`7X&<HrI^LRVp4#8H`qIZlQyIl}yjkNp51Jj_e!f{lMXBtV
+NWpW)Sg1F2?kkauxf-qHwHwzJzFLvo`YN0Xh58c}|rB37CIsQ4zqF-Gw>uea<cVX|6;%>SWcKas&eu^
+4RxTO(1Xo27mc=ke|6o8XoCduwyF6>R8?j7;HRhOx>vr^sj(65#)j=q4}bZ*5M&%Fumau5=X`?CMp>Z
+eMRrw!p7GObedimxK3Po2Ns@i)dhiibE%)Ls*))T$)W^Z2)9Ii%P8o8!=6wNQ6T>R$<h`s6fG6yd8y#
+v@ifCOC+lR_E7T{>@^h+%g%ysj;f$~tx^{=;+z)NiE)v}b31a2RAN$Jt&~*$XNUC|E`x8Jf^OVcLKk1
+@%Rj0{b8NT6MlYS`pUo*$@4vVJ_XfQoi)+A`W^3Go5<kaNO$vkUFV(eeCNi=cED4c`=S0e@$jEbcjph
+E|(-{e;?(3RsbC>>7MMlwDM@vUnF=kjzOoGRZ9rW3!PoK<#?@jsf1N(4_q((H3Fn5s=hXw$h1{Li6*6
+S;5a(ui;Ob|SbIvCh_OO&IU!xD2)?AKYDlf1w@F7?QSOrS3^Q4F=aJJh0RSosEKh)$2C!|H|*$FXUDB
+LbtsTHd;cS2E@xiUF?b?PCx=0-t7?VR1QoGu~f68iRZ!xu1urK<`Ku#xoyzp?8d+afvX5FBYnjM$Umy
+q{O=-u==dzx?w}t6H?#mW@t2dlZV?8xREz7$c2tZY=3h>v2+r#J(DVS3%rR&(AhE)`|Csi07e1#bRoe
+i&oJ=-lO2r=0P(BWTMZ&;(V8SBHa^}xkp%NYlVDiysTj!$U0-j!LO@qzv}Xg_AQQ%H;<+XhOo4t6Exz
+e(8yweX<1z(u_y&8ArCMPd7E@Lm)>CSy*Wg(KkMHsB0f{e0ck6KnAk1G?^m?|jePO2L=<u9AB?@F#rM
+%_9Bh_uB_ZW6o`pR{6gtZczawSmpSyN@wD=)z=nsWJmYqR&4=CV6hU18u@^;dGN(8|VGfPx0BidVaVZ
+b_ig-KBj?MZFQ~l`&#6$$8_Hy?1s#3of1;-l+6Kv8gKTvv5%4vTTZEy}jDG?8dltuSVb4W>U~)&Q@g(
+LYfQApLwwJq0F|BK};DNihoz;73RwfC{=BP$5zl{g*9o{l%OHrdcV7OH2yT%KRAAO^w;+XA5JHyyC**
+$kQ5iZFaQ-<AT4Xm)hPQr!%Rs$jgz>z12Uv+4r%Nq<`G0lN#ZEPD2tsMu3|ERU1q8dN$mp(NGxGTUsY
+x;Z}eGh*|`hnT6jeBOI2Rm@;~am4Wny1bEq22phOpJ&&~tc1+o&Y-Q*^`6POD}v$oKOkR&yz^FTW5sp
+dayfT&M&&p7CkB`wihoz+q<${co$!b9kzNVOf<&Nu)!h>)h)80SasBAtMp@K9ET;XbdZ=!otwm}L=_6
+7g9B5X@LPg)UO`u{x3bPSsz!DD$j|54QP{V`vE|GEBouo<E|}caAy{PltHxV;Xo}-SD!$2jn;A`E?A+
+M!ox}=18;hoLO6S-olkR_*I8DL$s0c<Qj1<n`XB|`!KUndgzMl@kMwI=1SfUz_hB5zw*WgJ7Si5-(B&
+$?~f_XQ2Ziz{xKHuG~sF;zFWuF=i-_BP6qn}z@!)cy&pXLVes9$`h`)LK$b~5j=lqpf*;b6c?zN=6Wn
+nU^>NWVi^RRalbPT;nrEg)>6)G<fWBPb&b_O5%Hn&(N^;b8ljWo^sS&gKx%%A8qqXXSmzlWjwX(auZ!
+E%dT!aDD!eGQV@SG_mnLMC$jyo=6#boutN@qO*M!K7MRq~PBLP~}ND2|#sAgN<{X7ah+%7uY)1vvU1Y
+;%O7sDY8d;y@rXsvXoacU0W1+7Z(}x;v_~XP)lxU@BhFj&{}tG_`2Gk908e{*`tdhsHfpQBZV3fk5l5
+kB)su9nnwS*D6TIi%jXoVBi=IolKCSvH@BpFFRRIO_-XRcUS8VnK%jsycz_}db-Ma6?LVcA6mvPkTRO
+`2&j1LOY(TQi0apX2c%y;3>}~Xw6BC~dh?G&`|HQ4iQX|**IfYecw&8&#_!T0y?wgp_6lz{)`P0sPgU
+$k>^#lM+@~MICV|p?D|B%?al##SESMjYm+;8;$mnLAo0T$@4;GCy)l8eThV*J<geEM7&++6JUYk>2m{
+XNFHR7fYK-ghx(*)*XJ`8g#!&`wT7D0Y*JbU(P<H2oH`+Lt~QTmAj3deht$ic6ZDz@MvjXpb$K@o=1a
+HqA>CG*F}dmW*=x-<Tr7P&txYF^X#N9HPt`YXuO?yDBb@-aL7RU=$r<R+}YR-!W$I$_f)x7L^Tfq_)0
++hyYdiyvoU5gLDE0q^Yx+tIHtO&#E#@e~Z@9B(5~!W+BkD-ujqjGhsKK(2UUuLF(kdr!qSqvQRAfMKD
+j_vgh^K|u)7qh<ys)!BQ@o)}4){TS>j(6eYXnMsH0sw#Ogby}ZNQez#fINo+OoSpU=SdHbmW3}RqE{)
+x=I_{}_ka>h#omr`&I1ka7VAO4V#twSN^3U2B=UX1?QQa2ix$n%yKM$h+Dg+6(39a>HqJ*1T`L+K8P)
+h>@6aWAK2mtey7Du5&I9p>`007fz0RSif003}la4%nWWo~3|axY|Qb98KJVlQ7}VPk7>Z*p`mbYXI4X
+>4UKaCzOmeS6zBvN-y`KLuAlIhGPxYdJ4Xee2%iBu?t5j-AJLHtD9hDkae}H!`V1QgPhf{qA?(0OF05
+oV4fO^V4o)i3A1%U@({&3}!aBzx}hZ8C7v{na-2J;&x;6kAe6+U)&by?5c{o&wJ7C&dzRh92aHuYFSo
+sGEU~v4|DkaUo-W4Fin1hdI$4)MD@z3NXn%6oJ<EBoABaIQe3BHndVs(rDYUdB}Fp6jb=rhRmrp;MVC
+dAL{WYjO|If%mcWxLkD@rcjTT8!<{7}q$5otWX*P@ED4GC<SUjq(0A872RyT2xV2x=Mmt{UlV*ngY^U
+3l$$*Q<Y2?mZ6G~cbRpls({s&#tOUNlYOIbfI}Tqtp_;YK%Ub(JqGfB`+Ligbby`&f1|UrrG#RV13H*
+D1FsU}Q+Jg=Lxa0S2PqkFN7+dWnA%>hfYao~PwiKbof4!gyK1!xA4(k__r$=l+@(QJKu=3?s$P@F*DA
+_IVhvxdo1U70Do$+{>G*{2FTjjJkt3E0@b60|a2)sN59(D|3JbIQ%1-RQ!~hy3FVE{04eWcx2NQdt2_
+&Twg$ec$|MusFzGeQI=QG3&sm)j4@-1kay)(4CE7y5icH9Km?`&pGse`^UE>NTvnwpQH#8wc5w(0E!7
+*Qxr?6;qw~|37w-<v4x^*<=*`*b-;Q1!zKA*p=Mg;U^rLr27eAf8y@;U5*}=)hFVX4C=-}j+DEj&6<O
+R;t;rlmdhv(<f=~;C2`pxmt;fsEBbn^W8?Te$6S5fo~>YkilM8`+3k1hc6#c4#%3cRDkbASObqt}OL&
+wqj^2hWa<k1l?JF@1S-ae|QH8vu_EqUg=R*~QWGx5o$YGkWv(?9J)<A-p(w0WeNZPmWGro<XCBuMbZy
+27v!b1W%*r@Ne)tI{)e5_?X*{4&Fki&HysNeSZ4pm$Rc+KV1NfpH7co9OCn5hk)?Gv*SZ<n>zgb_~7V
+uKYDTS`ry?e*F6PjfC?66qyfgepAPvcG=BgX96Y}`Iz6E-K0iIVID_AP7=^Qo8p6Ay^F!$G!P(I{pbs
+!!o}EHVIP6dj2lfQ(o*XhLoDzHLpa}kcdyZdmJPCsrhX=<1?;NX}#c}R03n1{(=yF*ti)1v4(rcuYXi
+=nD1y3_tyBiywPUkF%rx6uL%DgGkDnTL#jtV4RT*p-u7n7^>b5ahl_{PR)^cj{CR0pv8QRhF-lFupr{
+1131ZzBA_yPd)JgPji4h?l?@3aEfjH6Q(TxwDfzLSEYScxex-O@V`9C93uxFW}c6u;SrxduMlh_i;4*
+Ztv0V-p(+3umcP_>b@+}egw~7CSxkri~a+lOg@+Cl)xaAqv@WpT|`fxK7RVd0{WPN{yn;ej&H-Dz-pQ
+&*W+a|6L>`etG(pz(|+(dk51C$I=M}XK7N}!{p&|(DRT8`bdlcxAIqi#7_3cmzd51_D$R#7XXS0FzUK
+NHG+L?eNl|3E`cb9VNnNN|PINJ3Me2KzY;07;?cN5Ctvs6*3t+%E(~acIBw18~(P2^K1($;Y&|@e8|D
+NO-fPfvT9F(`$^ECSu!L+|ivuS=)BAEf70xEhk*olfbErD^J<P|`KflP3}Bd{A%b;K<q;p|7<gGx9*m
+Fb5n_G;ueRipGG>JA?cAN61#0**OOxNTse%p51cN0S1WI-mm_H@eE_Q`!X>(0TH_NPx!9Z?DHW>^R4O
++M6Q%43siUuuuGrO8b~U0Mt!X`aLy}C-HS+4C4YeXB;|Ob3%z~4({Vxna_!|K6EbP;w8KUE{$K0vdbL
+5rsYU#$qRzr`M9wG-2`G8sO6bLg%3L)qaUJ&)<lm+<7GOpQe41s8Oe`w1IRY93hFl-qkNIz<ftbD`b~
+od`&1Hb;qNWnK?P?ju<;GT7z=f{v(jEyeha9Rx5tQk=hvN16#8rub!1E2Wnq=ZlPW1To*kb)|M~pr?}
+t$J@o+a#X`W=W>I!$>MUhVu*vUZz8qf2|r*h-$@cHT43#nd%x(}Sjt6e0MyqK07uTI{MVDCA4PRJr(D
+Wb^0c@sF&ES*HNY)NB-5)z0Xut^@hcPsw-Wd}YTv)Upr(=TSljpM_UR~J8xP7YopmcyMLuZ!35m-Kph
+Z6F(y0GR<vtLm@^{d|Y&xNOYoAYHsXdXE4eKVDfK=V?(Sm(Y<*V6)Q=80c3Qzq~o5o`<Fvh1J<|j=LG
+)y>Vcc572{C{P^T&U3wUVPOPsBOCp^nYBl#Z&VPBW>+J^XmBM5Qz{c~R&UE>Q!Sb3gqjHf<Q1qL^zB@
+^PPHPk&1*;NCy47AB)h#{_R-2|km{nffZoE8td8!LP@e89;ahba<pfsdweHW?)i@Qo^%Y0e#XyXdN8l
+&UWlUG8O<IZu%r~)O3gy(rSBPueh2qlg?KQ~pO!B$nyLEJp&HqJX1Vya5H0I?(y<lg`ELU-p&aKvaO4
+u;zxj0~gXO9lHpY(7^YU;#gQ|LRy*pRKMwo9AN~OR0Q*dW_QJ$i(4&$E<vw1JMHib(`IIdwy|nHUcU4
+^}z+ya~G-6?(qAkPoplekDkbW#FKR0VF+tAR#M^3!TY*`&o$j{b{Fv%-Pnu6mj`c;FY5Yr>)*bW&^G&
+rY$qVUBdqFn0dhoj1ro9XUoICU#qDjJzkTxtbb!MbBb={ulxu|Z^rHj)P1s9+!yl(eSO_R2yi)s-T86
+>mYPIzv;U)b@I76_wyVClRyO3I|@zUvS1ja&%UKPnwXo>))Z!*;Z@@&-c+J?q&yzbx{NT5CsYqwH+hE
+1dcB5U3HoyQ`G0PIGk=uy!07g>LdP7Wejfg3~|i69rnO3EGZk~um&enHrD7IAfjk{$9Y^}S4gCu#2*#
+ruvsI-O-Y#kVv5vqZ*7zv;^w_~CHHmzTiqVZz3@(bcrT{iaMR#tuiNMiCx&!)UrKn@yt`BiL}ZydHzZ
++9xYV4zpG8-VSS&-W!C(f=ek#y?y=c@T^AbpEN3+n1*OCx}4{6#Us_31fE?7p5Zv|0jBEr%=t~zKo2(
+l{f!*y8EBAb3M2atlIV3lO}OLz=yN<@CNyOEq>AT*Z)xg$boBC=_QjoixQkL@&5CUy1WUsD<B~geNlP
+lig1}s;;La1fAPXBJ0$@R4UtI8!U9c7}uoN&YF6CfYi#-NN;DcOvXctB)WMkvv{O!?;D54UNc1)>s6G
+=D1x1g^jUy{j^tRNTXuXKgoK!sO+g}+_AmkL9tf-?F79&gVM4_;_IBD+*f3oXCNV2{3d`|j-M;!q)Am
+1KjAQS|Wr;d9-zRg@7-{#qtzu`%krsu6)%2qH2o^5p_b)`;xvHXx#k)`-A7tUyHSoEnJiJPMBpG5{!P
+AhNR?Mnn}g5ZM{7K!odj+tN+56m75R?_yH_dRU;a=j8x-C9d^uC##ToPJnZ=9)5W8{FKbB`@oy3%k8H
+fLrFJYqSil4Dl+ZgmKB;xCOKN<yS;Dh>v;~i0h_v9&Ld?u{3hXPW13t7AJ4kL@L8|xM+)0MnzHhul-_
+qbo#%N5TPN%j!urvs6qBaDU7__ni+RVY&@xUI`hlv-W*Quv0l`nYUNcmrNUCL#MdgP*M(1PnAnJ<7xs
+51x%U%x@_mj8Bj66t>i2OQ8w;{@1;cnSTF9_+&G@EuwM`b*LRu<7;_M`3Lp5Fecdmr`|AFZAYrb$D81
+^=|$fGuvsyazFMi`(i7PbcVwjU*j%pxA&r^i7V|g)$w36sB4&QP^^z_JN-&Yg-C5&;vnnp5F{q2TL?p
+K_4eqMVDA%ukNe1_^A)tqhKfQ%6xR<a+0PbEYaCir-x@HogH}cw5KbIN$zjB!5TI>^&xv<bd<pcoKAl
+tL4`_nJ1IXC=|$$GUW^K%9sLV?pW?DE;%t_5nUH{@hCR0%Km4#CJ$0Tv*az&V-SR^U<T(6jk!r^Chy$
+46N!biUiT$U25pMTwEh;q8*0ic6RnL+;(WJ1UednQ45{u-&m#}@8N_ImSW0`s-^2>tFF9ixoGJ>rfp0
+Thg%LLa(pHIeOd|4_fXt@CJ0;K!&{{1e)*dF$J+rxnt?WIx)A_caqFq@I|NaAKDKuGcgYReKd+Qr4~A
+Uc4B9@7yf@4$!*%-uAd#1-ijfEDT!=>(-9fm5$9l;8AuV5IPq<xawNG)`yRpqQr`iy~Q|tRM%o16WSs
+o=4r?$4}f|s|B+Y0qp<<MNgyq_aiCe5LroD#l4;Tc_+GOmgz@Xucw%!ET0~b8*)0Ix}h~W5BiI#nByY
+Lq8~xmfBd8whk9+*E3ex@wFt!T8(bwO0iw!cG>iS)v0-gS@87?V-htGdUW525G_X|y6<)=k(|lRbQ8Y
+~P42Jh}tz<Y*XksV%5*Qw%cPc0mh69h^@QKP`nJwbUr*7vMn2S8(2lEaMDzLE)Yy6QC;-ZLeyS*j`(K
+v49Ak9Fgthze@JNi$c(1+iB*K<(&@kf(~m{pjMS{nrOk4ZeAj3!r~%H?(09WO5pP62O#&ha1+Pff0p2
+^JE5ex2Y*!cdDI2P+s}UL)ZHy#fI%EVSreDz1wF7$U=?jppVm1<?$q!O-*4O9dpYqMMk^gL8OAb6Ubs
+Cnc=e37XACC@_`$5~d$Y=(#RYhN<w-v3tJE=$P{)d4JKP5#W9bMhrilWzZI)uR$=9%Cs>}L02wd>B_;
+9Y=fjSD|HSA6v_}p1q2^or8AI2p;xF|gLD<$BnBF|k*eW_Ep3?zE`YM`3_Y<~v??dF8S!F#fb-IID0c
+Yh=`)~F!0y8yeE*2+-6`_$@e@^K`1r}*<Kgbd9@EcVS{biyWgKorsMP6H-)>z<IA7eN&<he(w=5>G8J
+3k2EBEn;(%k(Px9T8)`7+r;q;>?gk&M(&fTI`x(WR=FmUQmfXV)8?PN0FbAhTixU+q~*b-n$4lYeplo
+FW3cE(H_^!qi2vOl)od1BQI4m<&*3>JFdWAMWfvve^Oh%p8yJF73KhAF6$_o?>4~ry?fcqDjA>y?rT1
+Swmp*5w=Lr`LL@GWv^q{ErH(mVR3xE&Zk`^+e=l3iws~~vplYDusf%7R^58M2v67(<aJPjJC!X4wp_F
+Ul64kf!s42CYIsi9)tuDb`HMAk4tly+6N0+HABUnjGgt$yMx!c5!5emYdfn}c$*0c{U{mS|BkPKZN4?
+p$-#k0kb;kCa7e{C6&D4JL{3rcpV!t_hd8*zlIz4zL*s)ziUPT>{rTbD3-;v(0>fPAyP|^PvnRigiuU
+nHx3gWr(hU)0HzF2s?YQf`G3m&fok5@h(ubS|9)q=-q3m&H}c$^9zf4g`OcvL1H^hiOxFy5Sccx;GTs
+Hmxn|Ae##ECAa+*(nX(RhigPI8;=-UEF-(|7$M#wq$8v`3KC?vJzoP>%E{Tqu;|v%`2F&fnV69UN0wN
+C@JuK+<R&3wcq0h)+FKlpS14+FNQxpU&O`-@nQk1v5UZZUJ09X+QP950auo}nRPmYf8=S_#WFD7lX+Z
+{s|2%vL*+zL5BswiScG;i8R#KG-x02T_@zSeiXkXaHGo%1mK30H)eb5+RtZ-UgFpx@x`Q>rJhWgfGvt
+CeL8N{uC$Rv`=lM12I_Q+LF6j7koX%<XCv|0zJwb7u2HQAC@$q_H@N!TGB$>mM#NtAkAxQ(2?RoksQI
+r~N%`^@8t=W@DO^UGGJno0ri|W<{YkY6&M}l<(bPy<r4;gB((>nsLRJ}Ifho>*^im$QLwEzy7U)BI_8
+)(8bvK*Jme=k8&yCZCJ(?zfAY^vLF4%(dtTTP^7Lk7htgZ0T2$G<N@v1zZ!{TLnRH^<56WRCRNWpiSU
+I=tMT<69nRF^fXy`D~gj?W!{<S!nwse2yxVMCDkjtk5P;PCMrDkFsClz%cgcOe6r}3EEU@L72Ya_A(l
+w*?ctWmdX4Q6xsMXA@A?R5dVlxtAjlCfempFt>mYpvlpi)$G_MrJZy>b_T3r2jsC^Ip2MDcp?+MPy*+
+te*ZcPeZ0MsYv|Ys|9>{icG$fvmo*kVWoc+>q6mdfDU;7M`i_{u7^lEU4o{>2_$93x-q7MG#$&+3^7?
+XKkCNvU86Hu{!4zP^!ByRG+;SprdRVJ{mE(2AiSPg(BNP%FtS-;|ndV3l%OZDjMIlUz#8Xa|&WbXsLd
+Kxd#(l$=2n<U8;|1_1?*kZ7XGRa-ue*ye>eyeI`Nirof<5m1QA%cY7l7*M?@5$c4OSBX^OmxenYTSU_
+Js?p8se1N%x<CM9_`7RuGidA+bGBX?2O$jEax_k4Z;jDSb&pPC$_mz4ZGqq?`4S`*7z02hp>I{ZNaSE
+MN~*~~8;xY_5fkJL#An#XN^28GMzfIl?Om2zpL&X}5`CrZk{0gJ6-$1svjd_EAM=bwwP`JZ5wJz2MDL
+UOYA!SN#?yB3Q&+S3o53n<yO`hJ%NV3G%XX+bi*n!A{Vmsuc$krl0YXuRzFuh3F$rOsRLP{?6|WMfy2
+rd5`op95rpUkCcIC0%ml{n~4bSt`)2?9;rC!sCA%6C0X6c~AnFwN5Tm-+`FOBBZSBH}c?lnI4dm)&JT
+!QJV_db%=$1Y~DwA8yD2Q6fOQ+ngfq<8q^Ol@cOd&igQy;{09`#1kSPsiT-ChvFm!&<y2+IA2q4*3!_
+7xpvP!feOaDyZXBXFrS&k4!O<_xSbUZWl(?hov+f9FQU~9vU1o2jron+3#H+kgwbXsuSbo`mIwl{(bi
+=<_2p0;Zp-J?=UlKkLHx^LMEYmWhU2nstX7Vp8b9IMWUDtV0Guww?I7)0pfA;77TXSWtx6F=K2?bB*1
+rNsX8nkKQ#E}Ml?<4vJV;iiy?FI4%K0ZogVGr8dZL}*MLCR5OEx$blQWB>PWe5@OCYtFWdY{7BDz}%M
+QoGub1sWPv!U@-S7Lxix-D4j}I;mUj!W~8$a7MfY%4_NAI2;U7X(yf`!58_2KK$@!{VNkM9O#r=D~4l
+Pjy3yKNqJ{eZ_`cmj_l2KZ06*X!A%H5!TWvh(ZC!|#6G9X|YZXSjp^b)p9b=m+GEzq9-6!|%T9SoT!C
+;SE3xDsi^DodK|W_moWUi~IHW4}04WYdL>2I(zwi`2FMEsElt*JbAfZmK91=(b4(o_S2`2zuz9}L#mh
+LfNmU!dwnu7<1^H>%mobkAr#4C9-}i4pQ)}Pjsvm1Bkv|Lt(EF%iv^7^RjM&+-w%y##GqZ;OUk}}>&z
+Z*)erW?38uNhzd(ak?DEJ`H3ch1uM!iEJ(%WF9)0BiA!;#R?gb8?B{wYfn@FXs{@#i(jm>~`-Ll$|wZ
+jOM`YE5w(1^(UUi70S6a~gzu~UO?=xofa_A$U^<-kFew_5qQLY*W{4W85RIPyD4-?iZ@Dz8M1F%g)8e
+2x~e6gJ6A5N~hdVp^^szyPl{Pm#>BdS|`q5W`=Kpv(zTgmpPzmRH^0nk{%oVlC6tI3Mmtzup4G58n*W
+*d>94K3tJ0sn7bb_u=j~8mdvLnBjfW@v;g4!dZR8empk?TAd4&oaX5ytw1-N#AT)KCXlI~K|c=w!fP=
+DygA>;>5f(N<NQm_Gp^&?s58&=Puiiv^Fb%iOj;YX9FY-YO#=Dkkp@{q!s}=RX%HGFK>Bl@f+}BpqT_
+oYzl@#A3ck4?{l<IIZz9o=`;()AF|3rvz-p7w!n6GROH%snhls-4`1IiXXSDO>Qa-%~0e>xE$ZZv{m+
+<L1ZZIpwx1i`2RIx;EbA%_Q5UiA)v<cA`oB*(DeSP_9nwSz)Nc?ej3`5*Dw)QIilw?dDh|}N|{6pp^_
+US+xiszNG^?Tf6M4j*M_K^3~!iuwoS+S^(NIIlL@F38ITz``kz(WlfB%csaUt2LxDf~ka(?{xZ9?v3i
+7_{2-_F8)W#HK5%{h!ez8>=sKy6_u-3DOMtdbPHv+oU%LtQTnWsoiFi&jSHdM57xEDQvVxC`JY{VGz2
+)dX#;XSK~8$^8Yi0b^{AslWM(ut7JY8tlBd+4%hP07GJakK1e1j;fGD?j&c_h-c&bNCdnre&fxj}j@{
+~cQnfT$k*hFBAj&fjff)w~)PM3F9;ef3lWd00&^bEGX~y~EzUw2{;+$gF?OWoTJ&lf&p69V*%}i~A55
+lU;1UyH4rJwnNd9K-!v5Lm9bC5u$?HKG%Yw(}O$#c#UOgAZ?Y%<ttsF?E4%r8yvsgP<Ck;fF(5#*rNG
+{X=_f)nk$><NEqfG{Dj=j-9=%VvpF8u0DNRqaEdK5gsRkg-QACQzDg^sORro_`hLq#Of0xssb#{sC1R
+LHL+j7`hr;z?%y}7@L0cu~JP1G|nrge5E$(%kn09j?CWZQmCnFHEU{hhNp89{Sf*3mN`hP=JRfTazTr
+w(Hv?cwJ_t)w6b2FHdrn_;X<7>8{*Y8uHsIBt4u{L!ud5{PJ+Z>AU$P06!p)blvNiJrBh=YBB-yTD4{
+0g8v0aRyl3$~JCc#5Y|u@yIOCh5eBE#@%CrdTYv@VIwtpy9nM&=QXRm4UT)RQz)NMxAS+8^S%Li8O`S
+1BPGVleyliH+bCUlfmHl3xZ*1aRO=-fF(i^u~l+A98af2+6pXj7f1?b$<6pT0F6U<IdDhiX~t^j+`hA
+y~n!*jg{<5ua}#SLFv7kfCDBr)@@cZJ^58Z1%g)k3B%o{(klSun7X5*|_)<7hhp?3TmK=$PA*=g!b(3
+yI)0R=zT++!th8@Scuw01lB3`urFi*>fG>mB(G8~+>on4wZv0&RXvVw^5Rom<V&c-cOHyzh9C;o$4DU
+DLI+UIRFxT3zAn&RX5=hj>7zY7_W%ZAVp+Fr`q#w8i~=`0OMpdC<e{cF_KMZvx!SC=M#LS*KRy;SJCL
+tFz!~U<IuZkb<Vw39VrCxM&~KOU{o_9^*q8^#o|!LfE&P3#JGlDf%_3dWaCX+R0Qz!Tux0C@ZVlG*Wy
+NmwCAeD^@lDHar6?!3UqRKiZVP|}qW2c+qbc+NkcL5noeZnmHU_o7;TuEr`{o_)&Z4lb(b{|%;ryUM2
+Buob+ImK#ZmXFq^z}xC%{&XCf!b^ACy!%*7-XjOJlO>UCQ6#fc3vMav^Nr?w6Bcy-Vg>F*P@BqB3gVZ
+F-$3YHj`jsYzfU(1UmBMqb%r1d4SggYxBD72r)*2NJ~09HrX|9PpV_EZGBFs=ozP@(BR!ZT0874hj&Y
+%$WnGNRnfh}zLKrymzO2_lHmgA^WK1A|6Hwp9d^&4(B?;9?3c3P_?|w>ghb5Qqz#QrSWB;8gSL+M6p1
+l?G)=mu1KM@GU`sBKA<V<?Ns-HD=$y|&*%4Y-zW5$S2*$Dw01;H~)&gsH&d-c-(Qi>AFh`vv0@WGM=R
+yYcN7Yb+<p}Un7J@FhKJ`_FdWvaE18c{uizbe`hBW>_s6+bYz)-z?;LNT)Iu<B%npKe>YB~w9$hQ9Jd
+b*1iTSj>=z<MZtr{O_j2q3DKE8Oy>Vf%CdDM@x{Q;d_5A&zW$xR_kp6cz}}J6U8W%Yt+yv5wxs5Sw!B
+UUQJ@y(=~DkqDe~C?N41)MAxOsvM3h)TFz%{btv(BgSm8sP*&a23t*fiu-6zwCxOrDi{#PxaxJ9b(zK
+*fp`LlDdn-yMb&<E4b5%VMeIJ_5r<s>|Ktn3D);ru_+WFbj~);l8-#$D{q2eQfneUn61l>;tmd7-4~D
+^Vdf%_vtRbqx(|~!rp$(Jk;z!+=HKz;u=g*Xb503H*Pc~X5nu~SqVch2N4V>PZ?go}b&8RvSzTnsj2n
+<4dUwY_M==Of_m4)R4uZ(HQ@#L9}4SiD#f1Z+P;R!kT%JONq&UZ6)uS>4|*B3=fY23cmC-=*6q`E!(I
+<%#?wyCTVOp(6ep4u$Nn8KS1>8-YbMiMkW(tzynBU-C>sYeugrwSBL)DgTzn^t8gs;N~#^l;E{?dSR|
+pPi+xcQVi>rj93oCAe8{#Q16)u`7Nb+KbIy($ZO43LD<tZQK01o9&ugQCvmWd07RIG>CSbgDZ1pB~}Z
+b<Z=iAwnES6yumHADxKqbm3WxpCGM|qmhvK`9R2mwaMXhT%bbg|LCc0^*!?cu3+SzTZ4J(-ALgwVp_p
+%*8KDK$)$GH-%?hUH+sd9a^6piKY{5z*GAMI`>saG(>2{L?OQILREssv$iF&y#DbNtb0GJN-g(jvVdI
+n=bc4dq1=V_Lhw7)$JP(J$dNvH}e-$M36=jM>GKX$gQT)*mbT;+vHikVtnrXEbAL)$o8MA7?eoUs0jw
+upIBF-HHBYsFCQn5uNBwTxVKhi3a!*mEt|L<4S?-0)zcS-^H8Tl2hmZ4;fQGFcRXvm(J)PM+vq8+M4a
+)26fP^+&>KB|Iv386h7H)7W{L1n%F{;E_!0Eq^C0UJbOCBWnX3!SPuU6!2|~J_kY_-ZwO^zx2&@TQ+N
+mc;--6va&Fx6nTu`Vxx*pdSbbpJMIrnq(~F9UmGilj|2_F_+~GlO>k>Ut;bScuer#dq!s@;+~F>|HnO
+!On<V>rIXFK&{CRYKc=7G3_n*@*fXc|?!Zq*>7;6m45ZBLj{H05s-RT+Hv$=-`VLyL+=1CoFC<?F*uU
+%-<SzD3du!f)!>O~OR`n@?kd9h-o0At+_F(t1J3;QO2FKcea0Yseyc3hz&D1iBDZ^+EC-n{`cf<Ta1Z
+AHX!Djua(QuuejC^|_+QUE#RqCxd@Qs@S^$~GqV2Qf5%U-lT>Eybt9(n`BFHR<4S1RX?(&o14QPlCa6
+>i{qq3*OVo@gYW9gxQMc){!!1EMh5fnpAN*FEP#B630?uZtLsO48CU^WfP;csF-~yX6WHnA?X<n4p<p
+d?q^BGP`kapQP+M~cKHUM@j{ePc>&N><z-0+@6kje|25udAC1a+E*ZDF>VUa}dND<vx1!&-hssB&96=
+7tpy!2(VXvS%G3-MCrpf2)_#b(JrSN;2;rBp6HY{K<!^D6;7x9;o#0?>w$u1BCVWfH*aj_8}icrN7C8
+4S?U!>*^4cGtoI~`}08^<d)2@LpFdCHWQFN6>+%S03@?@h-t<g*7caOg|x5#{z_`fRjz?Bq`#J<_FF9
+MJg^OS=jXK;&dL*+hM+h|;NDauzB{!O6X%VnMMZn_ns`u!A$wnq7F4Q(IyHS==BfEt^$GM^W4{>d5+3
+F(B<4a${d*FtWzczrwF*n!HODe9BhjkNMImSP!6Ib`~njL+BS)E5+#MrV4n$FW9fxuZ3$WSc_grExVd
+FteYc>6l!ojc7dYgtD(vxac|hEE6a{VFU;BwwQ!~&PK>A8mWMJ$Y0cFg{h?+C0N|hhrn*L^46~w`4~{
+aU054N!i|pv6`I*F6rRb!X{BA1sIeSN4n0^jw_5th!AGI0e^kh?o(x|}v#x>g-T!3C+aXeTmP<pLyyQ
+<B;24?azc&YGgLrNzXGZC{Y_H_a>7eQmeSYT9j7g_bZZ#7~uVT|$Y%RkDW?uUD2bgw|e$-T`lKT>dJL
+S!_eEq^qEZ-~Wc)Yl`}PuWsno{|DCl<Tiuvl12$q^wiVaO9q7%9(sI5O!E0;Zu4iEECb!o;|-#Y$n>E
+jUp0;Zzul_K2BLqy;7Hj-%g2{Q)1?okn}9Ngm0hMSzjWPb30{3KG=&aPVE#T(ZOhoBI?*h^eWu53C!(
+zJ(4vucKhy>nFY#dsbtUQy@6sRu<Y5)R+sv>r8H6!N$%}ORESjGI2EOV-xGQtjT`!Zbr0Xoa%d=l5}w
+kHWej6M4(g5acL{~IuX8Z$t6Uf=)xmzJT*8+bh?Pk>i5E$y!OCK64R+Le%K1oP;9Vb+z|!D3QOnS|Ud
+KVxVd%_EY|XbJSQs8r8aCM$x(GIqal%$w>xSy+>?@m@Wy`N@q)zm<vXOd&a~ipuI;`$PFb<};8yYhvo
+Q6&{F-hfmi=@B_ID;CKM*|-^lw%W@NMz*Wv<LxBgIrcKhL0FKz(}LHY6SEag`#<$)7@>y$WyoCwSWOF
+LW`0UYIZ*M?M13?(NNw{&^_j$P~a;YeAsd}k{PjP-K75zp}Es((B?N!CdP(0r!wL|kdr1GoHM67HS3B
+aw>!_VdE7KI`i(Pdj-ClHke{w;G{1%@)<-FX4kyrSL8mr|qc>S2vD7?l9a1qkOo&vzcD>V`qzXjmjLu
+ao?N{gTz|eenmEb16?c@0wn!_d4of9Ke6P*c%@24*plHuBv_V3PbR2f^PaJ7s9#;-TGsm<sjDJ!-4T}
+rM%(6b~F317YVGD)bflBQ0l<t=1@3~-ow+ZdlTiw@48A06olse6X4aD=}r{-XloXA;ib{M5D|$v9{ba
+jzeBEkllxn9x{$J>SUEt!+N)%fnp)H*;)3_#9}-H7Z`I3|__<`vyi1{$c2KcoBp$rKi=ce>z6VakFJ&
+w+288ieAGZht#Dva@Hw%kZn-GTZ=?Oi1bG%{fm)3$*LrG7vE+nmOi994c8crG5A0OgEU$<xMn|1rbYF
+TWie^oy-AnO&0I6<&^g}ziF7Fg8kHyKDUK(E$%i<D31yWGQMsgfLGtd3<?<=hn(<w1m_w#Qv1H;;SE#
+;>{s7X84k*-$%Esf9Vm*|e$6oUc!eBK|yjbEXeGwK6Brw7OPs^0@^-hv5l<M=S2HXox0#JAY9b=`y4Q
+{(P#&kZX&7^^MTM47g&9!ml&8x`9J~hM0?CC`9S4Q=IB>A1!laK>(P}baBZAq+N`#NG7L;NqYL$Du8X
+vgIo)e7`v4jnLhv(~1IBW<O+UQ0ZaZbFOek1VI(?o%Sgr<SeU6L5n@EIiG{BABlX*BpvNl2;6Kqv`BF
+eA-JQwK-dK^uaapN=Ue0lG>iFWFPiq+j?_$`10sI<$=>B`4mjbBW(WqVS7jk^gz3I+#wWXDYpj&YqBw
+Hb03iHa-p8#=+ItYma`Osg*%r3*sX%5uipf88)wS737#Aw(h_fGP8=!?l06GnYiba&BMPdkET+ATg*W
+p84~<4G!KpSm(%?8DPb@h%xvw&)>7?WhL_s#^{pf==c$h+yTsvn;Rf|O(*f4+?16{Pmp46%>2WdOK+B
+g^Ke1fetc>_bofc47H?pR(nLaZ>{U8{!LEL*FL_1OiK+%z(r;ePLF$P8G{3gz_00cg*LDTS^*BXti*v
+hSO6eQRq=v#E!>D_gQ_&+b^;9&O_!bp1)Kk4#%D*?5YPLXGI-$8DynkFJ_-MO?c#Ez#Jnulo0K50(5Z
+xYmqo0Q!*5O_ujzTUX!vNcsNk2I=1F_QNNSD4lHQ-p-T!*Bxw?VT95y64Zx}ziSDy(a!4@@h!+l)2{q
+&iv@-Hf)ulI>+z*Jbd5Hrf}`>1ctEvfG%u!2C%^5f!8DHj$cVV0$;C{;<z2T?ct6;W>TnGpGS)$rBwM
+dDic+sdv}v+-kYvN+I|t5Ra|3bhBDgcUrkkKmK&cDip*42v#?;1=#?v)a|MTCD8A03nI)lFs{`z0X(C
+cI1Uaqx4+5hcxW`0LJ`lEPo(x!hGGuoyIl%Bl(fcxoM-vgrGU5tzX_E%qlvE)czMBI_pv=WdzT|wBTn
+g(hiem@v)@9yk8tw#eW!IqCh^g?<ls$sAIO65BWV#cTB_6F8ypEXy&DJ>}QMdGTm@KF*9bB6|51au_a
+E9quyEy;)?tr5^?FJ=x<6jCWy=J?hCM?pKw=xSP!{G3$#oLGf2MT2Nw<P#j?ZjbZufO;J^kd0H4$K1O
+xSjia#TUQ>WRRSE{(J6yQ6RNKHHfa%mfVo5??gdh(x3<4?kzmTx(ziP^6xT`p&5|SW)uXHY0r>einWb
+4~<Z$Cbu?+yKP4qeM><;~@`ZY5Y(61bDsRA97Z}vEQZhWEsa-YRKRwQJLVSTemp`S4aopbUoW6yshJh
+wt|Xit)4I!{UpGhL#6mqY4a)oIRo-2gdGu8hsqhs9&DZ&Ojtp8WGlo1lVzu0fcVeycunVb3zU$OiEzR
+m2|;skdA3zX#FQKK=V^wq=2!Tfm<Er|qT#)|g96XpLC}?pIQJ=Y4>DmpiP8M%GBAD7{E~+O|RRRVTuj
+^NQ5)!9G6VQGCGnCCrWK9c9}=MKCU=oY|ar$v5#AP8!w`Qvg~3>GWWlTlPUChZYfHmM{TvMs-B``Wny
+74SUjh`*e-L*KX7@V$FMX@EB9u$P8QJ0~!5q+^PdssI@n1-I?b78m~N|KLKsd@MV<{*X$k1x2Ix54CY
+vtEnd=QT3G+y4BJ7x1b`hgy`LRfSOfdZel0(TfCdu)cHH!SHgJp-Hp!P+)y1R*h;M5XP8z_DpXASEMp
+~iDs+9Do;iIQ}!^clPS|~^<_AhBlRBodYNE+A~$5XA_n@I5OVW0%VKOCLqm0(@5inp@|iu=blJrgU5z
+pC!f4pw{m(JsUus&yIY^YDpNW@eWeKG_@YK61*4Q>res`)F_Y&;_KtQFW<@PdE#=RjO8PuPj$nd?%i!
+V@b|EeEi)<>%HMBrv?kk?CnCiRl~J(>g_&xytn)P>UuM`-uK__J>30%Wj(_KRgDK!+k5!%yN`ZBaXQr
+nA3__C9vcN~1FfI|ulF7|P>I1cPeJJV+jrz`BnCk(hOk8uI2gz)w`>%bY>TFttRtDn7+^7-Q}PR7b63
+_;L_K%=(Sh<4M!{QAcbkWnG4bN$WgVR1{sp~2M+)(4m7e62$#Rhb2c#hDvehlKrICG)ePJhwy8I;fRw
+x%M*%lrtn9OsRg?am9Gdjnlf8@z~m0VM`c}(%EK|M$~H77u3ZGi!)=mUX)Q5yL9n}f4+ho$kY!G}+uI
+F=}KxR1QJ!cKJPM}ecM#ynTrTUk*X-Yld*JkfCXqc17p2M;c3v$1MX4XJ8z4D}Ipq_(u*Mc9(g>+n4)
+xRAmnAk;}XAxRRiKES1wQVXml&*y~yqo;0XQCxTU+@q%t_a1%!*ewW?>fDLI`yukRZ#3A~K&@4ImR#q
+bQ6rcxvuT`FyrNWC0JSJnlNN=R(h}afN{q3E==D~hKCqzGX16>s<O}vZL5q`Z^ufc$`h-W*uxyKHOhH
+0EZ^S|>KfvQ4_*cDg1vHEV)-E{7B@`B(<VSnV@Q}!)5`x;%I`N}|cZ4m8WKebDdw9`WjqdZ_K2Q=L34
+5w~&878|z^0+srrRoAVC_ZQwMY~g%mFhZxhtiHOI-I`visig!|;ePU*;J9ot!|5L}kflRik$OhuOpxO
+zJ+*6j2lA_Z3iblZQaU&IH>LMeWB^FRM#Exs5i7Gdk1y3}Z;a`jIRZuyQ$;xUG2<PeBj^DhDE9K=*Zg
+gK`iZcn_o3pc@Dr%xc4)QI*+Kd0VIN-jgT8L4+9yyh|No!PZwcZ_pOSSLyIID=%yk=?f@cf@HM9F<>w
+_sEmcMh0SP}n^eRi)YIG`tq>l|tx~B`H|cy{w@Y{^_zwrsS?yejCP5YcnjK=D+gr7b(wsCaxQ?UiT;=
+M*C$Pz@%G_6FLQLAREssJ*$f1zVqO@5Ds&6r&`-|^2=-S4^!HLBy6}vXemdG{hHKvM^ud;Dzs(sh@0g
+Di^3NCg-yKHC=gY(nlgR`Uak-S;i3$$+rTJjvCy^7;yI<GKmfW>0!nOE;yrv44jG;BgrSeQb=DXBhX`
+HeiHC2t~x1dGeRlG~f6=(;MhoPFCW%w=niYz0g+s(Q4Qme4+@g{~(_w#)OkZ{D1qT^zm$ZJHoreo7ZM
+hcWqrZO>C-#FTRnm8oxj8>tg*j-W$p8j`>pZ8>zdO!f-}!FMVL07fPMVwjtv5Tv#FEgeqKl>~)|eJZh
+XPjojogzXZUT7zp48HxVPXNisCZD#S!R>F@t&N<6ObZ4>cl4*;85S|}P6nmMd2_UwtE`yLu4Wn&xG*6
+tTN-Ca<Au9O`nuTz~KoK%yoG-_Gz*AY447&G<%(sZps8Q-t0TW9LP3gElW>=BWjLP_0%|@8Qu<{_ZD8
+szoV7`jg`$Op1UhX2WHS>D4j|7_YEf`<39uSnf9%SoSi{~23v`ofgzNFyxg>pY?JcxUT3C%c}<pfw3O
+fPT>Qo%h{8rh$ch7G;ARZ-HOi?)KLD^HVoxPXD;bH*!x5D}t#;_I?nK3k}f3wb}Ts}|tLjq{5iM}yxT
+{-3DA*KvsH)}CO{5C!zlRreq7n7y;FPJ0w|cTvIHa6J$YR_)p5<$tvp*4YOa60&C1qR(2+_CbW2Cgr3
+^$8>4J98c3bpot|=thf3cx8bDxn|8|s^W=w)SWDyb3iQNilNMK!qN3vLFSU$ChRv=T5t7_ciLeWpM`~
+RjG26UnaetA+U8f=QLQFs$Y7x9w)(zaJ(QXoz?RalEX}G8Il92={x{Q{kGi3U_Iae_8=6QVwo$czlzI
+o?npA5|OTVN&?BZU&dE49}K8VKHAyxe{&S4yUsa5e&IW0Ke|DV1fB)@nIx-vmao(9vPJI(@%JqQ*=B;
+^-jmB``Ej2PtpRT%u*<g3=|%xT&X_kJ&K{;-E}0(oJ_<bbg&aFr5;*AO4?TvyTsYzh)ge=(7%%7=YxZ
+^CL!@_Xs*QOgayxssjpi(H-_E=|$+Y9Ry0rYec6o<Ael>n*<XoZ{bxk94Cp{bBp-}U`9!mCr9!QyHs>
+c<2PB}v-xeLRsN+MBSSwVUK+ergFn6%KLntKxBxa{Qc?#KogbY=!<{FOo<uOMJ3P=7Cy?Ra&_Oyqt+6
+m@b>H&lAm2=5q^!|4Ywhd4*+Z`Bdf$`3T9%|yi)X+Ve{nwv2hc!klN?uLM&MFz@4#s8nZtB%8r>`R@0
+A!q@m?gsp8JkxOK+ertFdllZ55kprRqSJ(k`s5`I3+)QJW#KUrt()fd^*JSsW5caiF~yaG^6LAVG~`>
+YPRFxp9GJOcA$X?I1>|qrl?n!s_Woux9pQ$?O}`=L$<rjvn=r1n>j;#nLndTr^9{B>^hZ6n@Kk?2Nj-
+21_Tlmku4CW>qjaw{n(2DTLvme6ve&F8BBWg?GVJy$=+)?Z~|5!1JLwDz54YrB43``T(J?2ooc+Jd0;
+joE7mDbD*iBDAPs20;Y4x`Hj%WNrlH;IBb~TSS)nqrvWk<2T<}QMmi%a8XYwLupbQvyAP2##f+oaGOj
+Y*B+B}7s{>3i<D_QA!p0T<?k;grRj}>AhQ${lh^aY%JZ~{slf8jc@ExL6VEouZF)$pinvd058IC6HtZ
+0d)AZRy<1MQM>jK;79i>Vuf8EuvJj96oC<5QCy%S6oW$(}+hwW~RSb_C?VL7BQqbHz`py)=;pV^9TDk
+Vh|%4v$})`^!X8aRXZP+B}Q)blM<-tB`DWKWoQjodF89-?%Z$aD7ycZ=Q}<$H`x1=K0@d*AX4UFdGC#
+e4wcglo8De^DT5*uZV6EM6H(C$NQh5ILc%@gQ2}fY&*PI0d|I6969?UVBH6@)tI=EPud@W+|D}4?fA%
+vjA&!%NjTPM;(ZtLi|PzHgjQ>Dy6M8j-OTT6b#1s-*M`Ba*#`qc8V$!6?WY#24Ar!Uxi@C<@{VtMb8X
+Xiv`KsLf|f{^$97f|8h7*PGT0aKNv!2Ome_oDwu$?8marBC5??%@EO{eEF`iMiPXYrVtYCG%FL7;ms{
+23kvg=9Dsf=+jLciBO7;&0>AZ?0~fWC*rm9ISdXfdH$D^@P!hSzP2T@^(jcWH6mewb~TwiHIz@%W(|2
+&_P2Xln|VjuC8?5^Ah^uD?&xwN^wOKMig|8QM^6wN*^R(q<S9zYZS-pcNxkXg!o@>XqpZFPwCLPZwe?
+EG>Is^Xv|rX7}4CcGz(Gs4)J3z5Bw~O1?OJ;e)$-ejL#4@NTEy>3v)^1pMT~-rnxVzO?sYh`-u~V%S3
+HYaAl1GbY0(Vi!XY$F2;FlzX>vU7I_@?bkOVWfaNLz8?+UlA^rv4>#W_T_WK0InfWQ!u@F13y8sK4}`
+aclW=o@qq`g(aKw;xx>66Fxs15JyL|3s+WsbLbGBS2HH*krU1_B4PmQ}1v&c33vVZ)dNIs|e(z;}31=
+euQ-wo9HbvKF(y$VGylNVAbMUN^<Bb#v@V2BMpjxy8?n<G%Jf-Se&kuVtk`VC;V#y`Fh(L9@!&VY{k_
+Vu&Fv&IVeX*@N<k5U~ySX#U521Z`vzTRP&*^nQ##u;Op?=T$unw9C%>6Wyoz_Av(34^Rtg{IR4TD_iX
+&G0>CAFUo}^caO61y=P7f{Rfvq3m3RJ5tLRmMU8;!|T$UUlHhdl*Bx4UT||SO4^qs8JCd$900pUM~&g
+D548FbU2Ji98>RJKF3<-Guk%n5G^5v-%TdWwF-j{-=1&H*fokFD!$+X!{YcsE?tCepT3vY?-Z=tiUm7
+b@qY4?oukIU;s(L_J8EA>3tdzsE!&h%XDP+CCs7{8EminS?7&XvadR2pUa9N{bd3(K1#q(cYI~C{Ir*
+$eGpZx4poLsGi>hqsmRLAqRT6}iwws@KPv719RUmm?Yb%&>RHfgA=>_qjEmpLuT=XL0yE1m5t<og<zI
+#0J)1vcv37Qv+27i>TvM{vrJVI<4WI0M8bN*d@r8y6~_Md_u)9xw6m$BN`HpUvnsrx~M@Rb*y6Q8&w@
+>vC515bndMi{@eat-uw@CHzN8U-$~S0S-+9lvkPDm~%iequcd(_$rxp#M!rT8>)d-;vCs0qbeT(0h|F
+NlFqWcNJbcjqjWJb5Gg;Ui!$Q-f#|>@fpKNgo5x4Zh^VM-rQz)>SP%=HC!Z6%Hav7I0v0>SZ&B=HX3N
+KuDDK9H4A0IQC|qMSwHaX0#T>3Qr9Z-)_9*V=kJt*sUjr1Lf-NxL%2fGXlhZr7RPaWFr_|Cv6~s8Q<4
+FrBcBrQk018iaFdYzwsj!oxs^O{+BUKz~1G7W*76sX-+haU(QBe`<Q@VQGl_KVK@z6Z3;JZ`2b>dWtk
+|%&9;|fL<5L=xI=z`M6T*jCyOug5x`p{oBO00*ViErP^eB%MJUIrjFWhCKDns|Y7zk|_$q(ylyF~8}D
+MFG+~DQ2u)d15Xid87k{`_P&KhFkiPZVPBsfz*p6kZ<%9G(RMi?vnBx770>j&*W&)Z#V>7gXlNx{}wW
+6c9n^?iY*vE6GzH5%3>LM4}t2(S3L+&KiZ17=#mAgn~}QgU=%fBaYiR-<%TLvRLH|Dxsi83N|?S?NL@
+FV<MA^iYr+#6pcx0uEn_9D;@@OWse_b^sE>z^DSEghQ!Cjpd{ymYA0+s$2~X~uOp^*T!DQ&JSdX4@3k
+761-EK+rWuax^gu5c=ui|WOdUHbO3VPmpr5k}`kh@_-;Yp_2s@f^y8%Lk;<K0mO6?g?kkLQwSr{lP!$
+vtEIW4{>5n}2pNrhlgx<6&D&Umwu-8~lDV?)>`$mh{8+$#8i3v(fB6Lew$;^KyXd(tv2A>^gxG)knt)
+$bfXPq{{0@{-{E&-sE^42K2-4N4mh+E>S0l9?z?6lrJPMQQxTN7;NZy@};V)+Hqw315h1$l{3%M*j&X
+WsedX5wIs<1jE%S+D^ri_`A*I=9xw{i7XW6Bh5~8PNkbp4kTW`oN3^deI3AajIE6zwsxFh@V%rLnxtW
+(+zL`gtM;S)W)QLx|vr5@)8@;j^y4th(i&<aocI^P_JyE`yAWQ+U9gHn18ox|%mKfI#&yo1dpC&{tW3
+)pvL=n%6iVG+aLnJG+4bviFG()6q%|UbWhjz<G%W?`-{HL9Wgyq9klf!C(Er8<i)e`NB0W?M5`iDo#x
+I=~Rs&m?lmc;CmC6Dat8l;NXrC!FRbI9r>JR4!ulez^4aygG@*jLhR1ke>;oF6Ai#+jonAojUwgVnj|
+v#B$+x`|Ddd}BpLWm3=bX`)kH2^b@o>Li|ADYrN&oBK(QeN60>ho!z20qR;Zt!K_NSb1Aq5wxn(;1s3
+gH*}9lTFNm;1&-V8dFRpY-Wu9IU*qL;(Pkv7iZQ^UqvvsQse)dyGZm|dPVy4<X^Y)tbjeL5%5)PZkyf
+K`&4EpZY<bs^F`^NGxq#H^ia=>K6x$(cni2ep4&x_$>B4B=Xq@!XW(-(FL_dC(5HXCE<$;eMi+oVj^d
+wE)nVM15saZpyTAd=Ok990_HE)f9z+H%9&<+ovHRdS)CwBg9uWap!;N{xp;r8XUyz-=<#)Zy^aAw?qk
+Z7m6HDA-z{aKg%pC$ABhKw9!+Ni@csKl_b1XQ0>234|ROMD63?hWhIrdDtW3Sago5m{fpS9w3l=RB)@
+&C#ocbvA*tSdQoE1U0$;OO!fFL|85tNzvIhFkqKWOjgULG8jsV0G|mUAd~-IBGyoMY}Z5$F{T*U>4a_
+;D}5TboMox@i)&y9byNV-l~3Oso{U}}Ui>82=vDXek_EItE0dh&OYUV}hb&M-q`X>T(p5vyJo>$=or&
+GEsmCVT=w=Skte!x#MKVJ@h;l@r@)P6f%jY<q<G={KF>PEV>ggB)(mP!2Mz5$sFlGnMr;b{?!p;Iq2+
+s=l&kmoTp1t6J$oKDmx`~Tf8M10vRt!Ud?`!oPr)(kd0#xV%bFIXr{uTM6NO96_g0z?v;%qHdG>)KtT
+<qQ7qHbCy1a?J3-(?^e6iy6cv7zj3=^q{xU?X#&tk3AzY}#)e2(#a}njjD!v)|K23j*WccemQAkD>Z*
+V|_6b#MlpAUk`e}wpQva-e&#^3)GER94t_3D%A>T!s8}13})r_VYmqmgH_!h274U;bzje9&g5BQK!R(
+MmST0O^bVBqFNZQH4nj=?VerQ6InbQpiS6-dfakz)#wd0MWgQ?lYj(>Rf2aPs9chNJb~i#GI%b*^+0^
+xww?->gAjL$^m#%=bA{I{eJS9?fiBEQb%eV%Y&FE!{vN&GQpq=-=Y@WEi8B7iPfN#t-Xp7)j0{gB`*)
+V32=I*|HatYG~a>q<u42Zj1RX!+Sf$PKRrM8MS>c8qkF(*cR|7V3(*eLGHDI(%5rZ;orG4>a)wusDKE
+d%$X=k-#hC%qOS`Ys)%QMIIf)$_3Usq-9JJ#{4Z4l!H47Q{*8hzyXpmlI^am&^J5w&VH4?~!w|x8D(h
+__SZs3LAlyOX8mIf2Qil1kzU^woYN&NezJdD;5J5(%Eo`{+uN?%3C4?kCRJ^e>_1i5nfbIHM01dyw+X
+<80zzXU<HJ)ukg&d#D)1Z__~xSXcC6^3QFK0ZP-ot1Yb{Ffa;ye|JT<}{=fbQCSR76eYg?QI9H!<)JG
+e(X2ooOKN_9WH*W+LhQ?Tm)N1!V$d4+H2j>ChR-1THNSCZ}4JwFw3~ZcQ4;HA7#Ljc5qFKE+0xQFG8R
+r|T>CK;`B}k0r1hmE!l`}qLXi+UxgGNq_CdHyfiLgWuQ=j0w>%mjO|L@P?>TzFdyo2L6`+eQvQzOJ@%
+-^?;rz?xyc}U@NptJ|IbIOhKuO0Wh4|{+;_(t371*+fJVe2_Q7`%diUYd4iB~2VrEs-Y<)G-uG6Co`9
+U4cdz5MIvp36a*^t=-&Q>j6=uxo34Xt1kwrVI)sH(X}W;la0d)B?{)648j?(qtk$?RHSaBhbVimC2qC
+Z)ohV1T;FG_p4>$<!V3Yxd5)(4BYm;)bXkA>!(}N|3&#Mp>Ca#pp7Qm-nr95wVHz-!9--HaVLd(oFxF
+uM>*yghVT_*refLd~f4Qws`)wx$UxGE99Dd&&Kz)Z-xWr)nY8$I>V;o)K9DO`=3GvsJtH)o{OsUSIZW
+pWUWwjX{%;#EfQHFE0rW*!XypzdftMv&ys;mhaCF4E%LxzJs0d@tMy7=sOD2;z~Y1D`OeY&mz6NwGTd
+hc+J&@!be|Nb)r3X5gmK4VaJTII02XQW}L*>qfqF|zDj1(ub{gXkzDPLgahNmVyFOl_8yWR>CkbK=HY
+SR9{?o3($Z_Q5ooGnb!nToaO>{VUPU@^;{i>g4Tlbl=QBcR$+g1<s4=(<-N|qbOfuFxBNkpY`f&eA~V
+;52IjF*qWTYTBGgp?hY|)m4U_v-g4={KoK)rD*L~;KeuG#YagE)rj?+t=b7@_&*xgt<^tz(wX~g7R`_
+E-r>{=G=7!3|y@^lqm8T}UGtyUbL}cTRiRL9;B(dEf`q9tHEuZ}2`42L<DM5-AbWER6l$6@c1V-VY@~
+qC8paEinY!DMT@j{_}mOyY=Ui0!8C$2O4b&dgQ7TZ<6Jw@xJb{95AqGql3IsK>}L?^qS<$C4sZ_LhB|
+JgD|g2g=!X+z_MCKVg3;@#q<<<DgqU^GVjdF2LTyGw9*V&!P#c-PP=sEIq3yOP*ECE`q}E>*N1)r37Y
+E%A;=l!m6;3zIz`((mE(O?-2cL^qg@0BzJ`3>Q~p?k@|f4cnT0HckH@3Q@$KAmj<;*ylo@&8S4`#%|q
+Q<?}%2%o`%ojx|XXS?JrR2wtR|1rw&{h-SlHAEAk%%8Vd~LNkb_7oH#K&IjezphaIz+tODP)JLnwQqp
+&AhmIM)Oj+I8CY2X<fC-6LxIV`d7L?bI%wds=wTp9?&&cy~aEZxdF&+{!5CX-qCKnY5X@lsOe2IL`<{
+H!V^47}hA_#7khBK#fJGuo3cts|y_+Z}Jy{A)h@Zq~1;t2kmjL%k7E|N)lnertd<-}kqA`V02Bh5Q5K
+it`^fI9Ec;oFeY1uqHqAqhyWbd}nx953QJIa=3|`9qGs2K;Bsbgo0gQCS&x*({oR3^n5kisVzB!-Nt)
+(8Z97cLf+lvw#^8@?c!%^JT?RCAq3BAqrwL=eeMlR(ECo!t7tY<V>~t)wKBBH{K~%V=`z@3uvb+FKcF
+5mzHk>nV~M!E|Wz+vTo!0#pmHHEGPL*pCzw`ur@|n^Mboj>qHTzbGR4wbF-t=5~~rfHz{ah_7*ly>>I
+7v0>94jd*9CC0b@`hRz^jMjaJ@X<E^qXDz9RW(hu50KL2cL=Bw;3h7WoUJ4uHy==Ah~tLqLOvX#eGK^
+~u~1C9iNpYY*~RW(pE;6ZHGAYP}GX_6eX-1I;*!hf6R%4Ky;G2a*zv^%og=y71-)!=+|bbfaDs*A<lu
+mc$%y>ty2@T_mboF<=xkr6%ra&o+`>tz%PAMGai!q1FYx39J=@uJ1zYEk@U-+c9f{(KAuLZaw^ocxj+
+<~v2=TpOq$Oo^>%|F>5_4x7vix8}!~5g)%b2!{1m>3+$+zwE+>{`j!t(G|&1j#rG=dXr`kcg2S-p5Mf
+`Wwhg$l`cqPHSaYxgzbOW2@EKH{EpQ;%AjnM%w8Owg~-gw(-y9acXV4STi1#G+AvWqmCXy+>SIhHEI}
+j_eBv!!vytaNwRO+DW9`>{cHD-bdCi*ZHsn(wqrAaEU$*QAC|ft|2^b3$mIp|K-bOI6cO&n$oYrDrym
+y>9(qIDcz$-L!3(br|sHMpTav@x2yab2}`d>q_!rA)c=TzMB`z3iOO?HZp{C$w8%t~3S-Wf!tzM5TI+
+&I>q@DZ>YO_dn!W{Tqc;s!>uYYcS{mafff*s7^?SRXRpIX3Z`6^lFIqmo)eEyQP5%nW>HUyYA9_n|xd
+e!LuOw^v~f&3kS9f&&^hcgo1P32cDAn!91)ZGUcM$-;@1@oRJCCQAVYN0JyRNiojLWFO5!ZU8MzZHC^
+gc<!BFs+;|s>-!d)o94@k%{~H#Zj6$pxlenGtB`U~T}xzVX?lT;iRFiPnG236kVU`E%V>MPwT%`%OOs
+nxizO9Z6!~;HX*yJ~?F;&GK!f!rTzVCtqJjf)v8zPaWK=!8Og-J-inaoYAgEgBUisI1CB@CQ*IkVfuC
+?Y!upUv&-0HYATJWe1Qv;6Y(K9P^q>caqn_U?0a1T27ru}=<PJ^`NevB@wRnYfHyVr(#Sh%q<Bb%AYe
+b<<h>Gr*yUH&J+fA;i0&3(H^A?ye9d;%IdeCt|_z4u}7$w#B*4Zm4y|9cRj{$<Y%CDG*jq@7N$30NDY
+THr8PO#8fU|Jdn!Tg$qXgdt_x!Hb-{ej8lR2v!7gt6@fREl5Rzwqg*n_nCc=r~1^eFI7nqFfScUr=I=
+DIYm*AuQ_x|oZaf|kgkcTtFskjlbFaI#(kXQc@g#&u&O45u${*QvzE!H-OdA_7j=?WPC-F(zaA$L|Hr
+o~Mu8;QkSGH2=RBos7Zw*UlZt%Lf72jnsEFsoAjyE!J`mR)kaR<)yh5i>6PWBzI9F`Sizin)__`SC1t
+M$>zw!Jzu<b1-Mwf*Jnyh_eVO-3Xlqa)}R>elq$ra`{vM{r&_E|^Ll}`B9Pe24FTY?Elp<kyK`&w(<9
+nDFG7=Fk&kbR2n#U@a}u-POh&H*=ZfzI0YNt=2&z%Sj-vQ(EVai;1KPqVTDb*CY^`3p?-Js~yW9mDIp
+2Y@x6P0<u2$ohEJ-R;vMG}6(Y>ibljQ+@Lae%upJ$##g!RDPX|^C>KLT0yAdi!I)q%7LG3N6nLUNjw}
+jV1K`^5;)fL`-I3XTDs(Rrv@{W6Fw^wPm^t35p1jR&am!V4A~~!)?+XWy}FlgGYp`avvsz1FJ9eUOfG
+~A8f6+q-2P&9?R-eDjZ2{JG?xtDs$JF5mMQ&8GG+H7&ZZL=W^P6^rLV_vtX-Qz(K$XSfzTirHzja>ug
+)z59_)CIx3xZwwyhM7j1!;;2Xf%dT{fVA2O8dsq>Me~0CgQ~qJeFg&2`oK{70UuZeN1bWjz-9rZ=rJu
+f|V{AzA|AKh}^0<2`soV2VNdKDZX9+D_1vh~q5s-=wN9?>MoEOyoprvFsZhew$r}R4}HLw^|lCnrIO|
+a<ZmkEjee;a*5SoG<I&3(=2+IW>dVTs_gxshS#{W*rBYJ^>q`nSGiGOwu&*wibCNhN{b@p{9TfgN=sb
+W=R5D<H5b7P-+b%ao?86q7}9L|$uTLV5nZHj2QpDVo#6kgZjCqH6gPOwk!1GXDm63VtaE)8n<({|X#3
+e7c32A4etagTHchSVrDLml4H2rp#Y?C5OtPJHTnM3fqk++Gian}}HidA|%byaUPO5l5*P*JbtNqRZ6f
+3D4U;&3*$%=pCN~JnDpad_u$%{|UNwwB)B<@WW<u@6|{-)FLbRrq*Qn`A#z))o5AX+-9(Wj<ratRC(P
+hU%r^RYA5kv9~*q%)tYZrzgmb2TFH6@##G?>Ouk`GCc9zECcHu`y?oQGOIAtgrBpFgrrw;LTN`U135J
+K^^SIKHzkWQ&P;cPql8M_(9p9$Hl~vE%i|uUnULl#f;J`<A}6!wT8mVq`#AYw_``1-i%)4a@bdm(5PC
+P$n?>V1-*Yz(VFIx;%Zrt2MBBdnZN9WFlqsX(?M$R`<y(?8c2%|o+aoE1}q<yAah_s^FP2+nEYyg9$$
+~AaTM=G@xT`6%;E(Dw{v5jCwTq3z*2f`>Gf3KS<AxMIJ9GwvFQU|;7}MY1|;9`@R*jVHLj4ci{TX@Ta
+UBA4+Q~-!b{U&wyN;t%3$*ig;gjUp6<3-GD<}=LRtM74IA$1diN@*twtkcgD%77-o)qN!oGQdD>mLS$
+<sH?Yd)@=Ue;vdHSe?pR~E0x=V>_?m!0THxj>mmcxkE7#`qj1-fLYJl-|yS2QL;mF#Pd6(X6<(o7LtW
+id*97WFaRj-Un{OyMSQNTgD(F_iMxPs>{pA1V<}Lt`CsBirc_4n`5q!-}JE82N`Qfd4*%9NlUbM#Mvb
+2s);Qt*U#VAqs51m|Fn5>)K$5xodPXcs4zHeZH<drBJR5Jr-V+Ndkv~!z<{e>j<DiLfVHv=WzlWBL!r
+Hyk(fr(alO7*@uM|PM(XvWzj;?N4ztf`k!NV2YGM~bRuK>#eKrwh5opF5=oQdtB&dg|iIWWQnl0TP`6
+HN<SRJ}G*uH^h&mXyVCc7?!U3b*(%2W-_SQgVA`6v0=68lqp$>C!bzgxl5P?1)($#PvJGYZs%)%tcqP
+<*t_R~J}6r<h1Aiv}R^aY9^IGhx*bbdV*}42^X`wc{1-!N!oZjyE+8&^SkX1{pL>)XZ`hq9gnLh^N~e
+2p!b|71Z8JcWyCizFV_L6}!EDBn~r)`Mty+6q|xi;+;4g{|i|5DqEcCO)ko5qU8qPdSDsX*3M$LY2LU
+%svxI`GLldaDkOD91{&V`o%$wG5B{(Y$d+WEP6tjg?HRO93KBCRs2v?d<u&<#E<pyEVVYhLmEwZ#si+
+gSNnA>ZH#`iWlOGhO1JDdLG>*D}W{-AY%}usV%X*A!Y#T{e$2PN}g+dnjnwn%!rLuC3gcBBPR4hacrs
+i04o4f`OY5-eSeIy8!8kvLG?*e!lRN%7d!3Z<+nG%LpAlufIWunS*wo=7qnNL!pQk{2G*t~fzJGnwnn
+Z5&WJpY7G`GB@>I>4zDES!$Gxu>4X97Xnb<Q&Il%RP3r(q^$gV8vD31r3+c;_9|cC-L0fS2*Fr)Mzg(
++M20=1J^C^F-NVjT+P_CdW`P*R0LRim1e9rsB=ekalmBso37hEc0$>4Ye(`DrP>%{HCiW8(h;_os$PW
+>P&JknKY1ujawoJCJRGCThYK_^gj8;N1#e12GKQbn=WgAYb-Ago(hxLW7x3b12~MU(hu9N}{RjwSLAp
+1GEN6WNqmok?20ELV$vN#)f;br1H0)gK@o8I<1Hp6q$H;MoVnXqhiB`{c6RK^wSbLjgyDN0pKFtcv-k
+RrEGIn=@MVXG8F@u+PfwrD!SB-4)`A*>PFy&hFTp#}0vWsp&gq2lo1gWQe=1nTnEII(iv4Dlz7bv#Tv
+B57ZWczA@smnWtHLn|gcI@5SNJE>BNbz)m^p@{j@iz21jitW3KlV7!h_aAT<Up<@P{ct=csJcBR<Lx0
+TG&O>Ye~Nu<zydL9z*dwt!|CXJ8Gde?#uQ*{2GdrF-n=B*4$S;BM9GOGVJhre>DUZ{7F~ouI1te=7_e
+TVD@af`Q{Cz&(A*zN751pS>V6YG(tJYLGbzuWdg|Z!QtWiqw|X<Mc*3m(CPJx5x<KNQ|8NJqVLMML1p
++Y$23%{(8x9GiVc=GggJPaStWGcJCIb)aj?KUYZ6v~j?dG~sT*`3M@^lOZi={Wp0IK!)u?hiZG5n;Zu
+nT%#Hh7IRlw2%D(!l2EawnK8;X&C1$l^SbrHInk|}l8zDNO|xLQq6>&pj?{!H9B;VBMH@V?2rxMP{xE
+%n-TZ{|pg&eGCXt=AtrOmVi}((*1se8Sr9g$H6bN@7`K7iK-yG%R8~giARK)Q(`|TfBNuCD$FN(<)Qr
+`hBjG%;ln91F+BOWNjWJ+qg3rSw8KMROG*7G~JO3^_=t`VET+uxoQybBPXin0c%+J4TykL_h0+^>e$O
+?$3M5sl~?@vPiHkEH1iqDESghNEE}KDm5NbIo~`!+>?tRK@@{k9Ri4;AJ{<99S}vf74F#kOc1kjxgS%
+Qiou}90AJn5r60pUi;BhSk1`Ji{VfxPM)O8oP3=5p;GDK_v=>hL$G98ElC=^n=oAccy(5>mNvlm)+r2
+vWwF<+F{(^LEFH%X#RhNSZ(sbxCfKm_<W8gS_7slkJf{QL|o%_{9O^QzO5fbFKmT(!o@l==kOB1>N~<
+|Cb#v&HeFVdk)Y=yX3|ow(DvT8+GO)@QyAkEwx@TfK$XG`k!8IaAX})vGQRhS>NrQwcpJx&U(>Sn}yq
++h)w2j1|Z=dD^*hy%HO-GH{vUru9%a?O=<WsegqLASKImDz_VfX5>MaoYi++M)WRG(Q`;G%X5m%GtEK
+H9;}|snR&kxsG+{!NtE?r_l`F_X9j_p_k0@>Seh4JO$3%-hrrT6V7U$g{(|<WWKN_Jx>kxG<bT@upf}
+xRKbqZz<564;U=V711HG=3m%GXJmq0LUqv>^8USF}qPiQgKA>$|F-jB3zuO1b%ZOzp~qPXRt`1K3PoU
+JXwWXnB149z_a-?bwxnL^y3K8$8WYaB&3Al+dUeZS9d7)DoW-Xe6|p)Bl_Io`;^sA@Zy9##8#rZLKNT
+k~sdAnT5=Jv^O1$r6W?N7?jRLyKVv9a=9CUhKlT`G&SQv(p!K`d@LRH^ShmWJYpkCdYGZ2k|2K>A0}#
+xLC!up+(F(bvD69Q_FR21k$Cg0v@P90z*01ySDv8<7qvjP6|QGm%KSsUZvdvtR|Zh54OchxLel1VB)_
+O{*q?u)m08gc)4lk%f0u?OUI6+nY87)tNr9__xMoZV73tzwK$__Hveb!jruoN7_020o;;>LO{o$C!*z
+(+BU&>+es!xI&amkOCpOx?5ZmA#ChL#v`Z?YM`0ph!`PJ>O1D11L1}rZt^6IY2+SzpK*^s|76#3(iKl
+wRMs|py^RWiq;apkaOcs_)}%j%6+Zslj_Up&VId*p9xZ(Nj13h%#LDQwXK#<$?n+&j*z3Z2@}|6yS%9
+7opZ%e6IjWm=YOSfwzT6=qKa`D6hqa@FJ&ahR3MLOg-ODTH}0>iYKW4l6#Uxse>B70Gl|DHH52-$cQX
+#{sEn9lENUmv0~T6qVrxrZ1Yv+>G}!A2u{693hsf?=agpSL#@da=l?H9&)ZPLvw32{-dUb`E}riLRi7
+Ep&6CESQ=bp->ebf1tJj`-h4#y6l^vmfRV6J<jdKW#IUZ?!cGQFO5Z%sKWT}|^$b(f7o^Zep~039B;T
+^rXM>o+F3ENd-$vd;W56<-IN;_Ie!~gqibb$tL!2xtyw!t3WnYD9tT^{-LBjJ^>0Q^PTQcP$&!%+ZE5
+M}SS42K11}_o%F-{L1JySv4=(*P;I$$1+;2V<0Xk-bd>X_C~4sRxM@H1ICO^?wctUAt%3ahi9wgzWtZ
+NA~lcChrhs$3e!^UYcvh?VgXPcU?3Lo2wdCBm<WG2d#Z#H<5!Cs+8LzbtI)ksUs-*^G%<VE)>He;dt=
+3=O%iBY20L49{!q)M_=-h_eg!IGMytI?UJRcIVDC0Qv`BK8)+506So$9Uii==WF%J+V=5dr(8F-Wy_p
+Tn=-*LcV+TKv`AcQT&qKxYs}q{=Dy{)iKC9v3b^nnz*OI`4>=AZmO=MG&nq*u^i@3Zkcx+Zx~4?ddT1
+%}Yd~dI)yyS{$^x%KNknvA&g>q%!&}Bsc)Xm=C}u6kG^+!wB}LJaDh6*>{IIX$q5=>bJr14(jdEas{T
+il;<p|bXXn^gwX-=2x>#73dOIitte#DvRK4VgvKIW92B;$DU$pEdx?lp!3adQ}i7Un?~=@Swa>wh{xY
+cyT+B@+w^Y643wQU^+k_v6rHBZqf%@f(9tZReNUdcjM^4ZOI($)~-;v_CJ2`etM$-QL*HKZ)v?;$?J!
+M2^%_cy6bQcJ74=wC-Rqu=#SigaJ??ZFE*yS&ErC&1_w8Hg<K_L`+F^zTUDXmCZupJj*xxHA$;Fg60a
+Qa8|G5l?<VQm{CM(gRPA|(8#siK&(X%7O+GcvdDy@sfSeu+ycn`TMI;>n~YqA!g141ztE(fzl=7A!=0
+T+yWgXi3C1@l$k8q>Cd+HS6H&^cwo(0xR87`rOx2`zw~MCBg71ckAYz6@=ae^e5u5iuHQUI%KoJA(lU
+&CzUdjB{(p&UGk+}}DW8bPpWy8{|;Z51F<eawD(sIkH+Z6fYNcFje1+8h0qlT3VRugpaROtxcw4N}Ak
+u%nb6@4OP2O!ej*L2P<_|cnNpw}$g9eml?2wDn&4<%p314X$~mJLO59i`<+*&mJAJM}7}sBcQ$BF&rQ
+YteO=I?5JM=Z&5yieM*4s#m~`@d6!1N0fdVH+I!pu_lSGuBw<uX09xk6&sQ1HjL>38h0kgY<Ocsy9r7
+=8}eNi^?;uJzfem91QY-O00;o{l@>>)n)@t@0RRBX0ssIY0001RX>c!Jc4cm4Z*nhabZu-kY-wUIUte
+i%X>?y-E^v8el22>gFc8J>`V@ms3Am;`?4^)H3;A<sA%}%Mlp-X1>>VOYW+cbieEN=T?`}hUku`cV@2
+6*Lt<}^1_b(VNCkj1ya%}ko&L(Uj2FOQ*!_$xb;SqFE?zBoHHq=pLGsRHDUKFJr87M)EiTWVDIf6rI;
+xLNNd4!qr5%7&t7Nga*i&JQUR+pK43%2Zs(c^$2Cz#RuFD-UdCL(iWU~J+m3SvImti;h<?$Mt<Sq-s8
+4h85X$5BQ(a2#jhOtn(?Z?@RLlx&aRl@im)8IF1II|)H`a0hY3aHEHlX=5`Lpt>CETa_76&d=g{x&BR
+etF+{)I*tPv(@l9|GzPZO)}`pDjAq?e(9$a<iLinxP$RU)Q%1HvZ8H|T-8tKJO3CecsZ?v~+w#D+FNj
+-bA#{@@Dz`ni_L%ol{<)U_6VBGZeAXS7>LwFEvYy_rAeV|###ry=qId8|U-@(edO^FqoEb~uBaE&TTp
+#^`pr|iq`meaj=P$V}j`~~u)yYr!4^T@31QY-O00;o{l@>>jmU58HNdN#@Qvm=W0001RX>c!Jc4cm4Z
+*nhabZu-kY-wUIV{dMAbYX6Eb1ras&An@z8#j_J_+7t(quE(WXH+)tMVixnR+dVaZe2XOxNUcPy~)fZ
+nXV+0m6@z6Y0muj1LOto8vv3lb@x8I{q$oMBLYAG2m~Sm0pu#rw!PneTOG<n9{=`RFWK(1yzB+bB1;c
+t{9FC|aW6`)lOoA>k9&t*5@u2STbh({9;C&+drg_5%#+>v-o1-=&wGF9z0G#9`dj_=U-x?IfA@EXG(8
+J9K^}yv;Qj9^XoY#JFg=6mB|G*1{h_Kb4z`bbdAv`9FphdRNxA8L`uFG0pFjKN{^PpZWOp5;Npz-p+G
+okG<eFdA$euy!%Ij*Wf-uYTI4sl8XLUPgs_#j3D7dB%HK_OS+ebYr(Q7zU)t159t@A9*&vx-mn(X4Uc
+vmLnXUSee)_eNC*O2vUGP~EWpQG|Ljn_eXc9ZO)?4~#y3@1ZR<rfvCcPQE_nsU9}VY^hr{E*A5)>d&I
+@51<zX(tSJivMhP9p~klDir5Q=xOI|jcNE+Lw!i2-Gj2XjC)OC#Xy<S1m8#DhnSg9|Nd_eHO-Ua)vkQ
+hpWQxkdTfZ#R#|>`mefe*aiM+`y<lgC!BqAgGvsh#n2^n#=^@Py<yjGL)p|&?-4O<&q#BWU*UZF&#RF
+4qe7jcypoGq<f2rzTXXRO(t&Y^(mj0&qBC8w7R=rh{#!4cKG7h3s?cAa6o?@l9Q_H!x$__i!xC^$bx;
+qVrru@3Wpt?J2lAYHanu>x_DI>*qT%094RZn!LmZw}DkzTP{M9V;pl~QY_zbM?+_n?T=)mfNkh0<_mt
+29`jWv1E(cdVA{=;=D#ZmXhI*Ujwb$$C?sE!D!gjD=>qXlvE2=_2(YJow{-?wNOLzdFQ4sg(NJHpnmU
+Qv3fz?Y)ODKfZb0i?X=T(@9IHIP9zaV4r7UTvWZ)h4MqF+bX_!)XCT#SSYe>TyAuoaatHDcWP{>O4oe
+%v1}(tt8!;Jd#~2JOz&{L2mkTyA3ZYbYtvf2{+2#LMrZ}!tpTm8zN!VN;RpZrFbc}xQSrc9*otYrYJ9
+WeXSLe?HZi8kVcje#DS+1SbPPoA!daPBGp?l9jLXLE#=flCwJzFGJ6^xBH`thwRkt-qhkcrafvUvdxu
+$1%w%;o@4H;FFNn^tdverjx1e|65wWIupUY55gZ$lUW)!oBCV>+vP?%vSprEFK-r5zu{R|jRp>S;aft
+`3=})d&7A{Xx%dTL$g5^+NGW;rYr|gd}yg=8@B2RsHuaO95?Eg}T;)w?}p}RbRfk9c%jSWn52wlpU67
+d{zy?*DmU+hTg-!-G6kd6|FS?!B!reY-E?!4HSP@3%(gqPFFJ*jq}oI@H;ARy-Aq0^T#{P_?=p@C;Zz
+ZZJCy$px7w0O&R!67M7p)eyiZY+1UfFrXD<d_w?h>KU_TcMtPj2wUH73%Rp_MHS3KW>2@^xVYc1I(ru
+(J{7b|7*_r0OrFH8s_R3fLdm5+;9&3brNw*?+kPXt7yyJ~qVsN~UYJbt4`l`x$56>QX#;Y2fzhk%_sA
+;bUtCE0fzVE;~wehvwpE;|U$D^M2q73e;E$%SJm8|{)Ra(pT^q`7OrtP`q?8T0dJB#yeCZxFu5H)cjT
+B+`muj{&_>qCtH5v-F=X&UM<MZJxEt>xlxqMk}_;BFImhuXfXBDd4^%qg8JMzy2<3#ps5mR0^vZ9y5I
+!6R&q{dp&jZB9Khd%PYYvrfP3u}gNlINy}pl<WBEr=NNiqViwWzUk9WN1JY2t3AS)7Kw7Db#rHVmI;y
+7z@A=0g^q+R<H}oFC3&HPoPqL{y=HbRNT-GL;+T-q7+>zP8~>E&dA5tQL(wflHaB_#y6hJ{5Vi1_X)2
+B|*4aiiI7bW&GkCp+|MpO?o=3%_zo#8ffXEWrySGtS)o96NtK2u$pe+zbYj5<YmXU`|1&?~VhF+Y;a`
+TY!vO7c|PHHw!XWQ1wo9t|(e5c|p$h8ft^hjLlUY46+cc$d$<SgmZdcDTCNm1%eVS~s@?6upzerAqTX
+2j`Chbonh*nX}$^cB*jW+hO@R<P5nSoNgtz?<y)M2lY|z->L-q>7&T#^6njIyPHY>EcN7!dKoqZnZcT
+C#FU8s`~M^89fRUHdS#K)Sqwf!i7G-sa)tT{UaF2)Sj2@g0!u=_pvVC3yPlVc6?JKOp}p5E(n7rFnO|
+tcI3Y4J<D_?^q|9%<-Z&s##%<dqHbHNO{H0NvJ{CmAE>ptk!NiFMWcw#sN6p)U2SQ5e%hwXEIW&XJhh
+IX9Mx9Dv2p>ktXC0}-l;flTkNjB)^uOjGoNm|bANnub%%c0^A$wdz6!Mm>v$J_J{x>9I8ozulBG%r3n
+dQmzBhQx6SP;G>N*eho8F)b(y5)IZJ<{-&<oPF68v(rJ;K89pTffMA7NqiPhny7_p>nmX8ctwj5}Bu-
+<<__dpoVsNOZqepY1ZAy8gIHiq@Xs-fmPayUbaL#B*!Yw7gd#Amv?maNdyn>>P7!pi1}ls%39ztE;`U
+Wfpz@2OHH@bhli4BF&apw|SjqQI>s$*{j<lfvKC-$Aj^$wj-+J`MT2HHjBJ$Y?3I7wKuarlqW~K%ABY
+UR<(uOtIO7LH)`3}%qq*br>fU9cdE=a8SXHCUW4l&A3q~Fcjv?}H~%0XVVtISlHR6rMeZLGwxYg5nsx
+IYO`!dHYSGhmYVT&FLk)R+bx3lIQ_)i%L9ekmk50iIdjAz`_L#BXsW$TL<{#7=s)p`i{)+bQ-p*04Mr
+rY|QVjK9U5l$il0Y?0HSnq(A>r6n8j@psLaUpOve2e=9pT`FE;-dlQ+1hLDz#?@*4q^K_OuInvhnWOl
+&v0Vsuq}LSN8S$lzy(|rrp-N^eORVbb06KOP-v;_&SbGj?rm;s!h3$t{hP!U0ZQwp;h6P<H{0af3m4g
+p?gH~^)+qF5Yinq<v*$2Ht@R2RW(It(A^!-wm>W4Pl@6#(U{Y1e$^P;>>OEeEh)3$4;yz6dt1)8FH2T
+>4^80N8{weEbz(feV&=bMG-zp^X#d#qA|7g~`w9;1Wp`(>xP$P21sie&-f=Ge-(*2A+&jx9)_Nmvs)(
+C(r_V^bvdHcp%0HU0#!dV^kx&}Pzo*HsjU9hQb8UuP_rB+ku9Mr~o&le<F5LQ>U9+RYI|!~@rIFDtF2
+g+8m-y@J_^pa`@~aku+Az-2U>DsXHHMk4OSTpBf!uYLM8<s4j`3NU2T7_!<DNYKj}5NZ;FSEAy&z4q8
+y%zE8AGNO8lStaPXOBnsaGv;9Br~{6RqjQpI&>U*t(iWG{rL3EuGy#L6G%T7wYLkW;TBM>8G>9E{a#l
+PFJ7jnWY>WRIfty9+o<kR2;%fonNo!c%!cX=<|_PavMil^ZfU{d%D3&j}*8PQnRZ6E%)Dj`|Zum%{Mo
+rZ?b&-?O;Bie|uZu+vAL&+#I2vefvfcRR4MNx-C;&ul)sv{eJ&jjco{T)s$Zz<D|m3#9eIs^yYQVis>
+sA*%e1vsqo+4*W*-#*<Q(CBN=+IR(bX%-l-M&@UK;E4>6Q~t%vZ}$7-wH?^CtXA3Z)o69!de@;FiT$5
+D!HkY63*_&8Cv{9P-vqcpYtJWdjwz;C?xMp`OvlamzLNecg%F-38bqFTNuh=QHc06`U>zU`O>Ew-ApQ
+7iAd$GN49;vzgsP$p$P55GRTcTb1-g0lR(WbpW0El;46^$GFKq7%{*Z;#_wN{Jq)@C%DU@3LinoWd^$
+KyQ_LKS5IF$>oWTDw=F}Vz|l^T|L}?R84rHc84TLzkar(D36O1{k@A?3i9}b<S5fH2~Vktph!;1$5L6
+{rzuWL$#Pxj?6?y|X%=4AdBn%*w1a<~;)Kj8gSR}T*6*bILnQ$5iN?b0g#0Rkb)Fser>M5u;M5RCr|@
+y9%(W9D7$qlq6eZUu2u}1kIw3Ccb{U`O$tsD{s5rG!R>}I1pAe^2mYtkKt>aHnXfNOnBqv$k3{K#OC-
+9>a`0)w+<OF_t0<ZIfPEwp2@XaawNuw(fGxD!G+dZz6k&<n&j(f@kZ!&kaqjD%sTvcgz9mBt0S?Ni2S
+ufZ%m9*ZBNovv_!eL+CFw$kqAiR96T%lf4nwx&wIEeGd_mA<aIXcmu?TOv1Ix9FqQY6Y1xjjylp4ibV
+%l-sKm8<qou!+@JA5Kw}C$_>}a6(z_vOB1oT~@E&Q&f$M{|{UGr}m5!n}trZIH9oiX^`wXO>B^zQlEF
+}aS?~7_tGYsbHa@Ke9{5BgO7CDf;j0$sewCfhLw76`Mdlv!k^Tq#v3_8azdRRPKevhDU0fM>zBw5=c}
+@#b>UY^1lM`x!2O`#7PzZX?YMyd-*|vkKWbh+Dv9_f^ydF(-gni(Ao~4Kl+WwYdtxs8BQ8~id%g?8@=
+)Eu={;-$M0$1_;Nu=QRHJ`=^jMV7!#qpv@(u6~<u`e}l7%bC9ZI^*-nGqQqmvc5WCgiH$y$O$(HeA@v
+ej(_QB*^Ym)xe`Rqk9>PR9uw_s%M%=by`@!EViEs^2Hd)UiA&R0EEesserw^)on++uu$B1(QX-Ya@{l
+O%XFFzOp)Ec-VCdbd-c@g+6?^D<1chI~t^q9`~+vMUQmIy#DiX@1fciSIKQdqFSns<tw(1|F5IrZ;~?
+J77riQ(FMgZ9SFOs<Nn`FRQqo`^Vgx@?>}vS^$a+_UoGKkSA3-^$;&rc6dzGc%?8?(ueZwfN41@`KQ{
+D^MA<YG6k(FQ&Tis7)W^46dwr|zn~LJMv?X~|7x}c`pDJ=_Ntz<BtE&{nkv3|ANB5#+os?d}8uRGhCc
+b^vNu-c19WgtsdI^ci^5|ixgr)lTo2q{gA3b{1EfZRt!sdA;PcQXo{%h~08sqxyj%L2}ov7wYlxiu?<
+LKU|rlBc)SWk-bB%A3`K4bgW$ow@ne@)C^)BE>SyZQv^n1s}7qyD=MOEf>@>naf^JLCxF^I1S;ueYt`
+?O#jtmv(d7sw%6z`B59R&5z3PGVPlakJ<xjen=KdeSfbO(NN#V^Y`vSrGOti?mcMDy9c^NexyiM{e#L
+Zeek&XQ5nSTFJ&0Df7Tvp`>Sy~@UwO>@Vlv}VtSdW{V4G6y-N2HKlhV7SHf}~>`G|L+G>_``q)Gbuc6
+TcZ`D+6gFOg>LzH9|NpL7L<wtzJU&W_(rP?(~L0>0P{B<ST15!~W(l2dQra<v|7lOErWl3t)s~yp(iC
+gDMWD#u-X_@S+z4m^T6k6fYP_!Z~%hZX%qJ-|lCJry_KNX*Wsu%Sr`Ic3vIo$+N42sIXbqA<8EVl_&W
+MW;`zz`owR-<f}enz1>ss1iSjPJ#Rpo-T)$**a%iK})O(Y;$`;eqD7UbS~)vrVIwRmo|v#J0^5xKsV2
+P9r23lS6q&UUiZG)(X6Oc`J#FztnHXRZqw18w0fp|E<yCzg6eJ=DzDDxQ?rr*L1Yzo8GHZRitnG)JM-
+@(7YBGfRAtP`5J0>g4t*~e%#aXtN!h*-&6k`_MbfM^)5cWf7ko)>c!iNYC4&WMvoh+0XC{nZ=YQ}fA#
+j_SzXs~Ff(-x>$(O{da9bYFJ52tUcdXx*}V6kpWc0Z^6}NXx4kz{-v7BOH|`I0vAWzym+KGas{Ct3_q
+O-p&6C%!d#^7(e!O_!d*1uYtB)^xFW<fUbJOH-Xqp^1O<weR&)$9d{#cW<;izxQP3m&PX-`%B{?#w<-
+hO=Yx_9yV^{XE~y!z0#gJGIB^lG@CJ$dos;(bdpF(l^=$-uBel;?v!`a7%oJAA4tee>?wtLLv&FPqLj
+ef9q7r#H`EU;Na2^7QG&+mB5<W3WE2+ZicVeo!Ueyn3ygGSfDi(7-;a2X@+T2lnZcA71H>Hm!ZAhxX}
+{_wU}e4b4qM-!~1-F`XCBKc4;Y?$ujW*Nfhh3g#K<(P#!9p4L1}h7J#}UOf5u>HS6T-GwV~&$<L|G#j
+Aqi@NUfXC3|f#qOVI<9SybOf~Nx5&da9R`Wq4hiYo|AhiSL6~n=~Db}tSHN@{<y?A-N+|bCteAJYC+B
+DY)oWIz>6l+(@v$naT<<Pr!wJ3JK|D?pNmC6CCZWhQuadz=mN&bhIEsa^M^Jaw%6dM<BKyyBV$j+O!@
+l5rs)&d`2y?#~;$ZR}AUG*v$XqG;{fAZ|2;+&Vk2z9p0VBpm+o<%UhNVkgsI;|SHJFS8_SZG(l$YSBh
+8kiqh12kam8o2Ow&RGGY^J&kNecqIvb*!JC>(b9&J-K*)q4lD&J(cTx(QGc3xtx{@Z7i?re}e74{&$}
+h)gSsOcTri;G0Hbq>ywK#sdA0}b{kGxE4>0P9O#=LH0b}N-wO!{!s;=&s@On67Y=xtD<7o>ANqs7@r<
+5?Ngg)T61Z@opW6z?gT8^T@*4U{dE$d;(l-v)lXb3k^rn`+gf1L#y59tC^`ki$-v(h0zCpi>8t5>X81
+L>$w$65-1}oWqCAb{T3~IkmKR-Y0s!)AfWq%3=w42&+I}yNz11&-^7UOvcRFSMr^}`7`E0Z*82Wcds3
+kQ6tbIKHK%&>(G{9gHeMH&<v)8b$XTz+kaE<PvlO>D+=)CWCJyBU`6<LWY-sed#9Cf{X;G);;YIT(gO
+mNd&ow7$262(>vgH#aC2>0xAM^m{W6vgyNiA72@U$Hq&3`jUD$Gz0SV-48$4;~LBvws3&7<h0aHf!Le
+oq9H+(GDx2#tLiD`GEsjUcE=NtDev_&jiCt&JPm`Kr91>8j*?bD&Lwo=g3TJ&mCOLocFC^9#F{1dG|E
+a8FrkR2y85)4)!QX1s**&WwPkLXXnK|Vy0uA+WCWm9T&A(9MKLs=OWd1ecPN0JO6(6QBPSC1G1<n1MW
+Z?;=O!uQr^;8)4taCHkz!DWM5@RRdHVTT^>)~Q>O>x+Mh1~fXHlRx`Z;8@a4z7cKyW?=Vz<r@G0hdU)
+>Ao&!xbUCJsM&;m=&j{2OEU~=UH?JQ7QI-r`bWz_32?Vn(S$w6=viEV$V^b;OjuEX+%(7W@9tp&wPvO
+nG&Qt*cdf8heCVyC(;7w_OlNRMn%6c*s%XBSg&Iv+{#A+B8SWe1THqDjLp<OvzB-a1Sw;X#~9=OIYC+
+3g4$NLD3PemR+%hekUk+ZVmbB@U`Z<7JNFXk_3(ss<1mUmgfEOi@c($QAF&ha3PK{;W-tPVAu;LItVS
+_BbGDQvP_4EKW&|Vk(<ctH@<P7~i(S1Bl<s@yT3bmvf2%d-X7?n|vm4V5NPbQ6I$o6)Mc<`(ub*?X$i
+^<&$2j&0V~tnZ#%b()HS<%s4W6$8m()i#mt;QHhaM8*eP|Ekr@d9dz{Yz9dLn6ljPpt!m7Ro%BpSQ?e
+B|J$=zQR!Z7x@$c46&|NN*{e8Sx+Eu*}d13uH~DM#-j$Z5l~1SVn#V?#*P%H0^xYEv+0PtEq6%K_S{Y
+XCg7}V+$k4GY|!Xu4U!yKww*^Q(qdbjQ#K8?F$1P8!^B5__AKrSdj{}aA54m%_Vf<faS(8Kx5>`N#Me
+TlDTUp_kyiEcncTyW?`1r1-o|^bm4&OV<hZSzz7F#Fa&&Ge*U1-;6ErOo10mltd@dAtxGRi05ahe4$!
+i*4;a;cu`d!>V`}p>OHpY2;9SCauTr+ikdR55Ec4{Bg{-47_E%tTUlhp}4H@~o;62?)^aVV_CBqgDP(
+P^$o41d8Z}-Z~tTxRHY?`Kl=PWd4ojtdu!wBkg{r!~4JAa|1>6}0#u+N=|ih#<Wq-6Z`fjH!eQrS6d*
+^$vM&&7xv@C#uAnvr?&QN#X-p9}dr*luY`Mgkrg1nh<vG8Qv13it(iY6>wN0>Qr805!2Sh=zG356y_Y
+u;<E*?~64IYcqi_oIwYl)sr8&G=8QNANq9+Gk+K@Et2PlG_`6uw@8|Q%t8o;7J+Et+%Enh4R)oqX6=5
++Rg0DO!rlp}ZSuKHr(QmJ&*u5ijPJ`JjLo(&gbV&MsH^$E4M5B@mf1^knBnffOwy&2@DDn3257J&{J0
+3<Jo%zOJCnXn;8sqsa$fS{pz7r2H#9rM%fq%ZpY*@beKDiu9FNV0`ii%rff1lre=-44)e?N|SMPXznq
+~WnH_%8z7Y>;A$yfjvE|i!vyT&VCUC`FAXo_H*z2ZGGl+c9(-uh|~+aqL3!S(BI`R7+q*5I*z!OCMWz
+v7@kNKnf|Su!eb5}Dy^QGNP*Z<VhDEja8?M-G9Awe+W`LF~>5L%g!}&lqE!@g8V2)+-iV7-z3a_8<pC
+jMCwf<!K7I*-KXpTiC!qiRC&P15Sct*!z>Me~nE3$?2+@{y#Z7XaZE6GykSvf0F$&Z2eipIZZu(7Q3F
+2i9ZMXJ&m#vyg$oLYBU9cdJS^B*JgtM?DX2m#GjpBn^s;6Zq4AnuJ-8vz|V#JEkm@3A-@i`%ZR$U1Y&
+1)#dG9cy$-6c?Xg(_ll3(Rc@VgIEp2ufFR!hkMB_Y^fNJkse|6;h`dN0cHxne%4edKPa;66G@(tSKaN
+|@;Q$3BVrw>}}s4tqYy=8~RsidjgGs{0j^s=R~-NTWeKzN69rg@%bC_jS5(-9Myj~xPwN9=1>ykr=RE
+R5xRjxDt991Gq)=1HJu+SI-r`q12Qs3oS5JMU63QAa&&J_eG+GfjO-VTTJ=KD3x3Auo3E&_#;EVjzN!
+$d|@dpD+kIVqV&6tj5N&FNaaUBpw5G%6Pl<xxlh%8UzDI`Rvk&2`r-&y|(o!h8s-cEbMjajuLvfO@uL
+ns{FCOiH-M#@6MUXM=)gqhdqiYws?0UDVC^-d<0Mf&NhREs?(J{DswIfe0l;r3Y!D7gp=375>hEmXzc
+Q@$(=AgdxK+*ISwA)@YZ0~${V$j9Jay}=p)?rCQg;Y7Ot=vgEy8u!EUO~)x_}`T+BDlpqiESM)=k!al
+j^P*nTTEbJO#;a<|5Wy_GvDq7!fBPKpDcw>(^CfxV5_L77~~zF~vM(^7rhB;n;9h<2xFbfn`rT~Wr7-
+|;0svnI0@3`x%0_y)e6xhnD2o@pa~Z?o@JW0!^_X!y6;u0pBpo|wK3h=80-cyMoVU=qY3K|N=dLq^Nf
+ylF(ZBVqU{kvWmZIU}Vm@7pLlfTzV3sUn)n3ZIP$=gip3MM?(F2QGy#CgQNsDI;B!GD<Rc3A}-luG*~
+4iKo3I8sOCA3kx_zUQ-ChxJ4nYp`(?_mW71MH5FSJYySEc+OY>_7J;>5Jxq4k23|^Q9#|NU>5?FD1>3
+Jwl-1%;{*cE?+fv@dtna}}{sVV9T0*zC!<Z|4A7ASLgtM%9&gugvM^KhsBrGXW-(gMATCA}~ssq*^4$
+A_b&T!^-mFcYNh90uL?l0xLxC}~4tI81*TD%RldXI(<g{)#G+B$@x&d>*T;K9o2UV2z=2z}G-tI!f{&
+!F1lG#hz3=Nq9Jt3ONQ4{VgBqsBJgRo7&dG&EKl+`M-?LnBTc1Z6325XaeXId71_WPBL`es{r9R`^x#
+D4dF8(RUn9B{0INaIW4_IF+GfiW$B7E?TAmf@nCgerKC2V32~Ukp*2iU=cqZhkz@i%!~@g_1!iJb3XY
+qoA^7&<~3%=yM4Ut&H)P@GK;{7^*g9&WQe|VgdhMat-e%Qb$KSC3kS>+Gm+4R2TbAG0NH5oOmH-sGHl
+@hmD(n$?&J8V!sKKEk0J+0v7-^978?6Qpi_G`ae@3&I$QfeKLJK*KSN}4{lM~PX7vYK%#3LN!1;)#TR
+#xc$g1{|C=Ky;X;z+o4?#IC@m{GtI-~)=!hSYV_yYuCxdhPKc+UeHW$(H+-lKibLz~D_y)p1gCNQ5EC
+@U|pPt9igEEh)ykR%|98~A8HIAV{1C}<LleCw+T74$ws78b~!ZX;X&DQ3cq?0-6JnDKi5J;i(HaGc*?
+ylC8{m_Q2$CcRuN`1d08yabq22>T2!Q?Owq^}TeK5p{bno#hFYpz{GV0J()K>$1w0Hi0km!6y2&_mIu
+uKE3C&HL$SBAO&W@)zKPnlzc8}WJG7^B1N2h;^N)N5pobKDko4bIp~O#pIbz}Q28Wsi0oMTu!BMbQ)R
+ELX!%kgMXY;hqpV=})W+NVTXj3(?vWQS?RGDT74ViF^hUgK9No}(*bOZbJ7^ty@nWc@XxOvW_h3bFIU
+qqtKID6$#2_U46-w(+cn;_Fy)RUb84@8*0#k?+%kPlJ&*C)rjH@K`mFfD2m-4Q~90&#*P4(g3^N)-$)
+9}I4okm@MXiZ7Aae`WELWF%jSeh^ff=_lQR1<MyI{m@kRE+q4*r*-uSZ-)VaO~8sA~>qBDhfMy6c?yI
+Bx_1f!YNgia#df0!0wBYcZ^*O@l{TD00=Czw+0g7%_E?3@VnAJv|QoyflcGM&wc`{nxUlnv=e+nf*o@
+JGE<TdhZ9r9w1GlrSv|E8mcvSwS<d+R(ni^yxQ+LjfRRmPd*6iLeB1=S4gUkS?jb_UANbM+Lv7#PMp+
+|pPIxhJrp}Kq-@Sj^I3ckBE*z-7NNbh~((FD8HH5Bx6wBUB#>b$U*%A;q+Boq2XsI7U2=#+VxKMh4+G
+@rO%E$JV1en=l!g03D3~=XEW3tl!#W{@Utiot)DIv(5^#x5v0_lmEOay@yE&<1mPQnc3`pF7$O#?V*$
+*zDB2XZ*u{lqblKtdNTnA1L149?U(0z<<L-lwI=+=&U!nW0lNs-Gy#vJ}9D3nkexBmYT;S+MtivW3bF
+_a_ds%q4W;fJK-EM^XAC5XO-CO+sY|VC5i13}7PA-XK5(5|OMn5^!;%Im6J4G&G#Ie{zHllhV28HW+c
+=V`w2=s{j{?efLr3g!Q^h=*nK$>JtIC40S?U_8)a7a<5BJxxM!ceJTii2Fy7q$e)jf4CWks;uW{AwXm
+V%_DaX@_emxJP9f!pXN3$~*uWp3@pWnhBYs?BmIHX}KXSCm^y$Y)R5BNE?Apkd`jKW5w_|>cb859eAa
+dAd7TAe0k8G?tGyY#T=0mI>A|r?89MOc9#?sn-!ut-rjfVKqRzMgOXX%+0{-ZO~7@KQ3#-^da2%8><W
+q*mIH4U<1{4X|RsL5f>h~Qrw#?0LRWY=e8<tK<gvIc*08ZjF3C#MlJmp}6(4Y+6cGv_knzUR+kvoKBn
+%)4JCp$i8rhjj`#Rc*TZvpooAef@0r7e@g<JN-42_p@25x^W|vzpx>Pb?}RH8*vW)i!{+-5c4%(*jvA
+F+7wLiU*bIbOA?ivt^|)?geeBL_&z-Pz`i`rgnn@b!|26;<_(8x0nZzOtrtOz2pKa$sfZZB3xZaMaZ~
+`eaFIh6z+}NGXFw(kLk-)Q*}!`NIiUGK@_i7SXH}p&E8xO`nlYs}J_1`R(Qh(>a4a3L5k%lLUKO7a=)
+{{C3bWsM;r!|elvp5NJz;oi;)wwb_Ex`GhqqyZCiwyYx+#q(E)+E2cT^^XAaF?FBF*-=B#do9p>UBM@
+fZqa4-ryUP*cYoj*^T43i541{0}4|2vFC|LM?%sTc{AIz(OH*g?+w35#OPOS^*VVs5MZrh1vkMA{571
+B>Wojgk@}D!1$b5C?I@8_>&sZUOW$J>epY$p&8+PD3FiqpoF8j(FElHVQTh9mjrM`f_*^FQh?}xuqU$
+uGqq135pV>nSC};T0U-N4yY;f65&R+yD#x2Y2>b{1<_|#~bqrP0jPCyc;PciyAR5k$VD9M57Kt4JpV<
+=Fn6k*~4`9j?@Wy`Q1$EpIJU|?0DKh#De@o@1sk*Sn;T>sIY?<)L{ngmLJWrf3j_$q=N0Ei0p$Z9?Uz
+4%CWQ2IwXd)5NM2uSICLNM|PYo^w3LWYi2HvnRoYB=GGhN4av#efKLeD1>;d^dr8S7inM)%I)^DJX_t
+Z3}32by47fLAt8V7MwU?Ojw&c@xL*vk|RW+S{pF%gbaPl1{)Ol}5mh98O|Ugo23h<`p7~NGfrIQDgSx
+<_Fk7HA<#7^qGg^irNZrB}}6zygMaHN!Dd^^rYQn(*cq{P@wL~lKXLx+>Qx#?R5YZ;-<rGSzZR@X3uf
+UOb382Q~it`ZN6|=D|FFy1-rtQ)-l}#cA3D-frz~{I<eo-kQ^{gbu5L7W<<0zYmkCm%k*+EH1zW!!^e
+OR2tFb>)suy${t2?-mW_Y>2fb*q1t{><A-Tpv7}$77=?G~8OJ~~z1N7V#!X64*W?L`frc6i{z;Jf^?6
+v6#9?sD4_kfQXo&r8)cn&!6dHWLJK7&mL&Sc8fy!-_CoZ%7R%+AXL;LQFf>Q=;X>Q=yT>Q=~b>K60!B
+ftX&qq|eC=0~tI<<a>O>`Zxhe%t`QWH{KwX@G_kn=67ZVcE<>=0BqGnJ}EjhZ~5{FiYrdgogXvVesE)
+@(oQpH@l%}XX(UCT$7=w9@9QzaoEbY-gqeX;-R;gFhbuC1LpTY!^HD&pkZQu4>U{yj$s-wzt<Qh9_?!
+qBbL%@5+fG7Yhs6cc8zHm@$g-P&7ok2*aUs29oF%_;xfNp>hHj7qY{}OHFV*C9s3x|xQMrjX&2$GWry
+)s5SwiH6<O2WzcC1&AAB1P-xN5IJttLpz%wF!_(&RzV+eupzILF&@NNPDCpTs#;8z|*Xgw-<4g}go0)
+>LICyzX3g;?fn)Qq_dRTjXB(g-fWZL`INAg@9c#MR#ptGG|doQ4o7)*|Lc&timJTL5P$v>wGH1m3-j&
+n0x>g2lU+7!B2BYBdz_0x$7kn+wHzm>4}U1Cxb$VW?%h4}sBNfUlO4fGvE$IFCH9Y=)K<j(AGAA*Y09
+SAR{wEDa>WR`6|*`*H9vXa?cPPmt}Wpk`^zVaM})P~L<F)5@4|<zX6E=TU4#4wiw3LYR<)<x-NgyCG#
+|Hi!?#yo8EqP_0{E?K5_T?8FS`7DtgdnLva_VPk(sS3BX3_X)K{{3C$3q!0z4Im5CJ)5Frp_Y_u21wS
+OE4`p5?{Z4?0*|9`ic`@zFAddy8Zt?#j7!(^EHVt0E$;7U!5q9?JKq7DH2^8$V(ovr<n3Wsu9$LW?oR
+fr(1y7yUucd<RJ|R9gGB;<~!UrrU@eX2<bG`wB6CppTdQSAt4vaCRy)eK70ve1MFHV9L9J>NimNb~iG
+0-KA=a6Ld5UCy@Wf|9cY7C?own%u0_jE}=CU}7b7f~Cj7^SI1;AS=K3yw12ogD>L`zLRc#I4*vr;OnJ
+lPlvPnQ_}VTn$Jl54^7i*1md>l5j2&NDY8(80y<6@zp+$L+@Z#*y0fShg4xjI6@Y*0uo#<k9UoDk0o^
+B0bl!c`_v_{R6F&)i%ALtGJj4q6cT(R;8+Ns)39u}P(^R%F?vE1f?HD2OUeX+V-#7h1^0nSMa*pm1g9
+FIe%e?1gFb^1mO$!fWXFhBz)+SDz1AC9rWXouc+<=x4v*;B-N^3lfH2f8JX9H}#Zq8T3GljG6gS~{L^
+~B|;llU@WJIeX+wKA@tE7Nb&5`*S+13<_Yyv1VdZR|eefjuN62QH|je)51J>v!=H|Z&33oqQ<)`$2cY
+w=hiO|bq*wrP7s%r>KRi-$cFENKrVY-k5@wv$eH$oOn`>qGobqJ>yRl!jSuI2|E$t`K=<l<o-LL6oIw
+v;6)K!0eqHi_-h9wSuy7(-&~o=?E7os0>%oGR{g7u0YH{OT37|4g;!C-xqODEimOyo))PHg)?}VMYPx
+yQc3JS%XUKbh9eJ!)V(Czxusf28Rc@~R)i1}XEv+IF<j<^BH_T?nNWbfc7SoQ-tEPR3JYaL@Z1C@CV0
+?DgI9n<^bWkz@k~y1*s$J5JvSnZK1Z}9`ojG}fiKCL_xkgdLB-D~>y<R5_+$cvGUgPKxr8oUaKWzzAk
+i+iULjrr0pDYY_;F0RAb|ibT&VaA0_HYmpA{e86t5Pe_qekL|2AHUF&P4GsdiwHYXM&_=9G|`gf3h#V
+hTw3QBZa@kd%#g`~$o~6zj)(;vAW-2xi0xS`FZo$0;>BA*g5+mB<&EQH|at&3NI+tAcY9zJ<E>B8vTB
+WcGd>aaD*h5TXRYV2Uz75wVJQOpN)HFF#{M9rcf?*;9@r(p)vTfNsH|dn}Ib@Sux0dtFW#%qQX!b8X;
+IBJq`oDG;YuolE74BdB;*QNi`)k9C>*WU@x4{Q3t8k51UH5q}_9OtBECEBiQ1!0}y<0Qq|H)wMOiXgL
+?F76Z=;aZUp-zWxKA3b9M~0rw9T+l^Q$Wpmkn)hE!xfgurN$*_eB%#j6ve4_X^tZDxRkc38>R>SQ+4$
+FWqRneQYF`o4H>BCi^p9Nb>Bb)T%iZ!lTuq&l5P~8+U;MFR@gA%JHm|U#{oC>4@fhKH)jH+Z4X3|%`x
+A4{P29^e7FGm22D$v`s+!@KAz#~}gvUbn!)2+Leqe3vQSsr-bBv%L%LmlE_3=Kx^9Bj_jUO#3)@juwK
+t2_t?!&;n{7IfhS8{Ip=5~>j6hCGzeg%><QEfay50x=he84ycG42hw!Ku{Y~MvPicV+mb&!Boq+KoCQ
+sZ(}MkW5k5|76=4YB?kMP5X=?yTc6fbPT3M8*d8m#`ZeRXMlLQ6#n%_G9cHFyYk7=>ZAChQ$VORPuhv
+IEAfkl}iEHaEd!ujH;+O?jk=GnBK&#@(db9Q@o`k5$nm%@fYfo!ZVN82<<r}MJv%^jl16BB;h=~OV(y
+qm}65u7Q8j%4rv@kSTV?uG)WVBBsi8owHUEA6c7{s}eWw2iRUi)7w_Z+4q9_d&MFC6nlmq$U14@U%0l
+<PFWz+qz2-vI@~JCbbhwqwx?7|f}i0)==q(s$N$K4F2cyv?#{0GoU5Xns`Xe#vSrUegDMTsJ;Bu;w%F
+0JqaLoLmD4MRiE{NBmq^zgcR}2~3%`ctvI{+~J7m(;vu@g;lsNg$WoSS6JDY_Uud<$EMqi5M2!9@m1>
+olthISNLGJkp8CHJp)eg&a2@BRB@)(3ycS|AksA>KHj=#wlAK+X0El$u?AV)No1}=Z;B0vl<Q#DwQ#B
+kI9y3^6L7g#%?g64{x>!bW-i`b+g^_?wtZWntKUxS8vLu|c!7v!OQN4-tkUSd1dp5R?LtU3eEFw+<bt
+6$|C=(GIbTiwQ2y6}UBKs!GF5Rb;u@Pvz=M@bkP;MNpL1pD_K6Jn_0WeMC>#jUe)@M45z;8Z_6c=2|-
+hAF~kj6{L2MNcDA_-kMV54a0;|~eNePRJzxKOeSjFcuE^}%UiqCKR#282fw<2DKC3YVy`91~lfjASGn
+-3cXh;ebt)kUqYVsQ<CiPSoNU8AF%sRtUb&BPxIoT_|7H2u$+3_&y4TTYf8KNpM(F6#mrkls*DOAI;D
+x?SA-6nX{=reLX4J8b?Vj-E)BV=Gd)I$JKP)fDZ`NkEKDZiJFFJh^T)Br{vc;4Ax|qu~kWo@+>R~8uH
+6Q0U_cwh2Q8dUKN+?ER_cN#`t>yf|Clx$pHb5XOoPrEif!Uk+K5Z)+uP7Kam1GnshZ>?c>chw35(;19
+sFBrlasrs{&)o#M|r|E+X&~#$emV3qlnfv8C0B5!b$^k$HxU+Qj-K3>XrhPJ#cKAfmHk*un<>E<Xt~0
+i5)hY3_Gh4H+5vofjEBn)kai<wk&iccukg$xp?=VEtY8*9`Ne`1lK?x`I=b(&&pz>r*D@Kv2g~kKJ*@
+(d9bFrIJ1!eP4>CH8Zf6!a*?ted&x7Q1&?C?nL^8%>umekqV=Z$jcx>%o4{SsdyO>?|Y<VH%<Y>DZ>m
+^h?D^%xvBLb8?YGIXWC<+)b54&;D9O<AHP9>Ar&tW&KOKL3<nHHNKh0Y$87@W)gf#4>7@WJTqv8heFm
+GvCHjU<1z+KbGlU~Fh7l1Xpry&8(};8rgy3mU*-N4X`4n^b6sdTlaS+f9z);{8q+C%8cs4c&A_miw-I
+4T2(xni_5OBK5c8(WM(xrKr<-Y{1CjKkIF0t>D<SKZPLQe3CSB*IUgC1C)NWzM-^h!tw3=w^TC=HvGH
+t^0G2wX-Xt_|Z-TPiQ+g#u1CIv9-dNSyMX6XsAbYD|su<9Qf)3U@)K9j}AdL5pK~VewM<=ER=wWYH&m
+i2@mG^bsD7H?2_Gz&R6m9H05vPe4^n9g2n#qFFjL#nmBMTTYhfO}SAUh?Q+Z)ll#Yx|3sv#5l(Qh+8{
+$NX-|1C`Vyt1n1ZxQjJnOb~vywh~41bG=<`>St>vwId;gU3Oi0SVWf!1Oc@hj(K(_ROpRkC?`jIk#m+
+^QA|L}Hwo<Yno2!nmd!@E@0R_7DHNB>|CB|CET6`-;!4JNbqTx{5xYv_zmMPsPgnS9h1uW8sKTwC*2x
+;|I*-*&QpnRxQbYPAEaGG*<3%;b3%CAA;U@zTf2L$;>OA8T%o7jisAQ^;S#B%2hmQqM43W%N)^r{1V<
+!x5PaxwLpuyjI#b9@MspYfi_^>{yJHL>x&FIAm0(Kgr@So-IykdeO9#6q@r6KBb&Xz?<4E1(uA^<hqG
+z%iw1I7IftkPz1U@sRrLhA&VIsP{KZ`<=Kn`(gwYnQGP@W{JTg95`lA+m1bC2m-8gQivj@n)ayNUT9F
+)Ic@U1W#w`KVsMa}-6!85r)4>W@0Jz;n<QjzI(`gvm-q8yX@`E5XATj=$ul8_u9lW1SO}wOCg5Z=VV<
+lHp@Ekw2Js*Gxv+loPC6wppJ!l%%8u=;vJrgdfn_&PO{{?Dw0GDiAXHZ_4o#>Csb6GQYA`)tF=jZD4v
+}h^9`Z<<mjR(<<{{d44>x9(sUQwFEaaAx4To?(b=<Ln-r@}##3BWdQ%8mb#sla`gV*2~k6s82d1h0Bl
+JayA!UV=?SdElgb1ac$8si94--v@knBWT8Hso(12e3Z2?1>n?yOr7dXgUsxLmo%gt4L;%Z*40F)3L!I
+Af!}%*rc8iL}4r7!iCanOCy3hPueILuRR2W6hbY&rs8u6UASP*lSY7|g5$X(-1n{!^y=75A`TdQ3`!c
+(<)tOSxZ+MHQQO_#|K-DN7EyZTki^K@CLs`eUqz1kMz@Qyea5Kj9V)`}!Z19y2d%c~Rxs{&Z4Z9<?gi
+%n{7rmXX(zsI|2OeTC55{dNKw%JAS*6u?>fH7it9N$@%>V}YW_f`#nWr%$@-$&&huJ6ZvO17H$4d<-;
+e~8!4l~VLtM(<SzoLi10fzjvsmZnvE~Hj_pz)FS?^vsSR*%z3$9W=f<>4TZx>9bv4G=X!^OZ!$9d&$S
+?n8)-)Gpu1s?l4;Lg4*Z=uDdyAgrKw?T<sej9WOgt!TMF5vPisLcW2?!$DldWYmo^$vb1*n@$IMWh)l
+#e9}D#|e~1P=r(z@V~aZi)A9L)io=37oCgsIXgI@5;B9<u1{vJjwQnPCC#0^uNhsv6NztDLzF9qWZ)g
+Ag-P($v8tT(Fo&Wzf)sW(R>zu@*ovUJ4=nOXNK1-D4^m7Ua^3>m-`c^YKo-d^p`*2spwKJ`3+zZ7s0t
+Ch0I@hJA@wg#KCJPQL4K->cV-MD8AEtIaOVk=1_J5J&WMTP%3rI90oshap+JYCIEwUrX>=$3G^~rX^i
+s3i7zYB5kwR!=CyYDP?wBu-#nz5PtiZfzC1HH{I~Q<1{k4Wcqvav6hu}Kss+pd{L@1@k;MN4fBnrc8A
+{20L#llc4vBXd-kaxaC0;^JkgEXZCR*6IUXRwO?CLR&naW;lp57rp%vsAnkfqA8C^GHus(C|Rg!3a8G
+vUgr_Z(P(C(_0;Gxm;)k2$l%8;4O^`j^|lNBfC10yEeDimG3x$>K5xo!f8Y>mGr&<BJm0;29OaLK>It
+WJICgSIZ)`&_;jqz3U^F)#1=eYFxgRK0l)Ghx)|t?=i-W=ilEQgRv0qkV(KV;5N9@KFwEZ2)&ZiKnSd
+Yrz-@!4&N^jD!~2>I{j;yXlXG=sp6M+KL{c@F`e<(_V!#jp*YIQCxoz;=N4u=(qup%D$=oPOFVEs)?2
+ccaWiTJCP}QEt9(j*qJn>JPmG>z~UbcdO>%amgr<s8Dg85eh#JG$#z?<<3!HK)rVy>n0h^nt&iUW6u_
+%-``ku{6fXA3X{*Wwn0U8+s_w&7MzA@;0mHqqvNDk6@b?fTkKtP7W6OB<hy)<%So1)YwGN8myWdn4Q=
+%+-x_lSYi7okkS!f3{?IvlIS~nfJ{1G*m{ZvdBrxI8~Ox-WR0!TS4@6RE8W{q`_sBL(5QdK+TM29j{J
+h)|ZzDK)~b=peXpU-63u#XZq4TjyVvQ%3|?-X>@<Z8R<Ca$ZWq4HBtH_W&UTBstb=N<5XP+<G2S0B^j
+r?&k+F4Hm&gSx1lrtGf<g)Y6vrzijES!c&cfRysM0}ap{Z68J$%jTP>5Hw#LMYPg~;<BeOnjO+!!H{v
+2=ZWY%+=K-kU#A{gQj5fR>OAJd~tNJ!1(vz?d#GB+TB^1UMr8;;om98M}I!t(>1Kvr<1Dof0&@oEBwE
+j++&4O<G>!UZO4&Wgqe=9I-TA+Ys*1q@(LpI1Q7`F0U?guV;Dnn~!w1>-W>5Kj?gYyeCobm0N>!5N}W
+H6oysVD#1G)UE_<;Q+5`k&X~q6E^9Dpg}VYQ-&>kKxt2N9kl%fP~EnVMsRk{444(NS#4@5gpDHkH-aT
+iX7)D(+aj&2k9*y`mgpI11ukPejNM1Z``%WqV4UA?oDvjCzIX&-zcJ7DRq}>m+0dhaQ=Dn<sd8|il;N
+i1D)gS)1v_c5!^@O(*`-F%HJF$&xKu5QH2UV>86rMC0kc7)==dpSxF|7XOk~VRdf2Bz(7Duv5m@l&qN
+Z*0Y#MH(*U*Iz%o01CGN!yiG8qh+{no8U?{FfH05%<!Po)mKs8Nw40bDpxO8p7|gj2L6QVR29Bawj%B
+!kJg1wlh^i*aZr9B~VA*t~@2Z%2kY7-45IVV|q8?1sVCs8Aw3X~rlK41@&AH=Bn7PG>X3!0y7^I45K=
+znv`6vzD~1)Q%dYq^5^4qu8z)4Tg*=4*3cwn*NMVO(O=EnHSMDP=}m!JhEZ-PAmq4LQl8A#@`=yA$x)
+bab;^))A*JDBG{F)mduD;$%)1Of90@imiW~nDC1mz+mfC;jTkGIG?i;JSN^8}@naw>V45iKeTFO~D3|
+vqmVh&O2(Zj^Hv^Q*`xAzVTwIDUx|Kg9IvmyEzyTq}r@|*dM7Xp_AF|G!n-n9UI9qVeV7eDEWH`O@HD
+Ng4m!OKw%?MbxIo*t4C_A)624g|b&nJwaD-#k<HzSZ^V+Uaw&Q~d7M(|aNfDwF^B4h+zr5FpY#8rwhW
+B4kCV2gvofJ-4GuF3?O1a@f!--#1$l#zES(1^TC0j6-5;=cu~CjOORe3v3*xVTFZOAPK(;8H*?55kdI
+oLgVJKPLp=rJw>$s205Q9W1mc?oy!ID3w<uP_eva5eaziMQ}zVhBrov!hqC=0*h;{OUB4+t)dW3Ihu|
+`e1j$+aPtBw4iR(n0x7|)<aLy{lGnkay_-P$iHvjvF>;ZS4nsS)GSbmilOLw`H>xJ1n|%O#PE&?1Qr<
+ur2?F00lyKl9p+r0ChbHh{LK!<0?hTSq(0DJR%ms<(f(a=TP2sy?Le1cTI=U5+>-(wBQ(G7&nct6&r6
+RW&t)!Gf3<0qc^#>9m6ZL`IiKpZtx-vIDA~@&96TQfdhZ>N1<x_%NR?vu0BCnj<u=2{WW3-JtVh9EOa
+3d+-+oz?FXPN#D$*_{JF|UqUhJLfk!f3bI)4vjgmEl~#b05M=UxrjzLJ2O#d!};+a|oW{^v>yw;hb(T
+M_!6jG&1EFg^W8ifT>VeMw!um#ZnB0=>Z+WBE`B0$)y<s${x*(z`QNOeU&G<(9rTAm!M)3T>-&2zb8}
+;J6=-)Gn>>=>3Ge7u%&hcl<<9sCEBggUykF$>Cs@sIs!qCfj}yGAyd^b&Q_}0gb`%2iULC05+-ajPtF
+;(aDmN-gcgA4w_m^ohB3BYWgbi!we!Mnk(~}Zv9LC3#OI2xH{Eo;zM1qXvb~Cp{3Oeq5Cq1+IJp8Nfj
+Sb}EH>@uCn5qZ99XiZ=i!j1P{CI~3<cuai{NuSf{F~wfDn|jFk<kPA5L%d#tf#<F!c#e&uB#qraOQuf
+>W9%GDj6$Zct;w$j7ZP4jp&di>;cfV^X;diVZXc{IFtcKS~QOI%H#;!0j9{oNl291f`H#$Y8o`9?Bac
+MYbwgWKpa+WcZc<^og!9gE`udxp2${l-UOnc$(rcsz!)Ms2JxvJ}9pE16#NlR>A3nV`TN4KO8!Qtx>_
+L*w`*6vSC&N4#~r<)CwW8K~^@3)`=`f{0Rmq>jaj4h2Vy)GFC%W4xjF$O~m@Hs6@clAnCh0c)=ZUkHW
+FTpe#G}dgpr3-+INV9U^V=8?XYr0vxhb=0TN~Vt)qNq2Obfh_S6JLwBfz#0&|_=~Dz|J&ObwnNT%lFc
+%=re^~09Ccq2`LnaPx(`h)h5fEHx&n0(FYR@@gWS*BqaPrgCMqT;GMdA2^Rf4}7cjfOCAGE;S*9iKWQ
+Y4z<?<UcRhF+`o5ITnwGe$ax6A@!YS{fsq!-+s(#o@$MpgIgvh3c?74}cCxOyEvVX~RG42epVV=L}nT
+fbEONLxwFpz)=GmF>K)hV)_}%NqPhpD=B7#n0cBkPUt&luvlYb!ffa?{ydxu3^&5H$-Ai}NPD?rd4zb
+yh72c;xhAn<LlP`2H&G&ZeVyB=EB`3l>vHMfuKcWvb;*+;25u}6v@XGe%r3(mSlMM$M$o1t3JA}JFgx
+L%U^RS6lP0i;FK<=Ag$-4k&Kg5M!oF4p$asWo$?w(!Y`Mh~AcUif;Eb^Ej7afS>po6?$~dJX*b&Z?OW
+S)gdZOIKd2GH2F+^5Ng;-_uZ5)B)2k&MAUVKJ2S2Pf)YcC=RFYOz1GlBIxH3}uZLoo;9l2bpauFjgWy
+gB<vfUN>994J3!H^k$SRV=8HnR5YLc%kI`_a%ZZ7SSwe6g?$KWxS7EmRXE50;F`6bjpic*9uCXKp(;9
+RtRz3ct8ltJD4zvvP}X6Wy`D}o-kY1;X2sLg9xVzaO>L~WMi))l-8m+_Dx?D@+C;qn*bg8(X@IS0*Wb
+zHcq5YQ}nMZb6PM@t9lZ1)v>=t;l&1slPZ+hK}O6*L5^TV@RA><z|&o2zK_2ourWrsc2GKQz?@K|w;)
+j>#DYL%DW9movtH#*f^g6fhbrYwwuytta}O=HNPIWZ%<?&iy~oJ+L7aRW0XcBqaUh`!7tFg9s$Pn>A!
+CB`)BbqB<Ukq8O95QCP#SY^Ex-3S0bFDx<G$uWo+E-q>vW9}*6v~?<iJV8a|vBIVDV``EZc*)g$YyeA
+fI}|q*@=X3nhY|m0^_lEE*b@?>8A$dJyTrc<;%iSq4OE9!dsGJTi+T?1Q6O&DuHaElJ0O?LFV3!#wYO
+ADW}#ZSmG8J~7TBYj{R*&fJ+W*gU2>LjK9Y^2@nI(>FRM#7J3RvWp~wXOmH4Ho-C<7!D8;K8Q0L5=lq
+HVSUT5*+Kf`YqvOtKZs+Y34`hQ$80tSp+IOnkjNV{Y~cXy@7@`udM&h`;pF;zvOSQ4roqUDhDU1M$N{
+}-Hm8%o5Qw}xHlvX88C;C0LhUkb?cvr_pUe5fIT}ImDZ{(c+OCDp=BoG*A9m2S&RiCu`yrz|Q4blAWF
+iiKc&J=Qt(%BFHit^u?h*IJdx2~@^iaqPV({^=IRzU9<z*|t>0A{?s)CXcJVX+fLa^x-0L~vc7~)L*I
+yO)J|1W}-7R27Y&>L~vn+8JOs<duFUd#K4*8Rb2N5NxRc4T2*%eTtFo}-*FRCbim5(7I}=%8i9!h?>x
+CGktL{t}qAI>+Uz8*ylhY`L2_Vz&pUyz(R0NT#mfBnopN63W&e;BmAYdy<Wwy>TW4g}gOEG?9~algD^
+LTp*k|GV1V_k3OI{ZutX*6TEeP%SRq?T5jdnQ;pEv+8s8deanwsoWo$fWiyK&O1z~nE1IQsn~F+ifQw
+4dC_Rlt?=0Sm$1#lf-tz6Y0HSs4%+;Jw?D61Zb+?&tY;axVHWyt$P4e5XX5+YZrV@iK4x|E{RoosVS7
+RW=v|&2M*VNGo>vQjBL_V`i5=rR7111N-NY-b$EYATaYBM*V&DZ581X?&STmH<LeCCgjAT9E<Gq*-EK
+RY9D<nXibc?fKbq!q&!Ht>r~jlrdbFEUaGXNSKTjO@!V{Fp25FMNsP5{Fd-Y;s>j0L_fe7kkvu06WeY
+OzTs{1ALI<i!+{P3conxiSf8GUkd9Z;r7eTj=;Ki|9$VCGUk;37;ocU`BbTnAk<&Y?fs$mUsaj@ZNH*
+_{`~o~rWyr`>JIe#fX(_%y-%J3HfZR~BLE$+VN-VS1h7#v3+A{|!Nx7!1z?i~W+KZ9Hf>>jz|LD3wL7
+cZ7(S)4^M-CjWuLTO0KvX*>E?hvZRjS%^Ru?UG*%a_gc0m{OZVKs=KTgXN4xVuI|ehr%=m$q`KX0qKI
+Ufp(C*xfA9$X(<7cv^6s#RTb304H+VOivZOq$6LiL&P!`RQw_+ifHX8fq#cKqhVvl&0g!Tfm(o7Hk1@
+4_rn&IV-pfm-MfzUw^*_xg{z;DgzuU&Hht1~zPAL%=32Yy#MM3qyU=w!SIqn+{so0I;@=DcYEhTG$A%
+w%sY(olaZW6tH;<o42rFS0uNi@nAlkX<Sf*N#fvUQx}&9%Oup`q{wEo$^6VTQ`0#Gifwk@pLt6xlXca
+D>R*CwE{m{e5%rG-8!8_$E@F*aFE0&r-*2a{kLe5d1GVyNaP=49ld%Tx%lc0PA5G64{*^=w$Bk$ViPk
+4D;eOI@rda=Bpy%h!MCm^abT+7_<wn2ObW@xShLEUQ<ZRiih0d0~TI_83s|C-Nz*_Wd8LWlRmcm;6Y&
+on2(3V8~W86Z=40R>67EN15YvHt|G~CZeQ)jZ*xrZ<*+#VEukqir;;Dtqe2rrwe=i|A6?=Pd(w){QV2
+d(%i;BUY0L9|}#i+DD##YTC2+hAA8|7L&I{4nTbx%G;!Ms!H8MtMb2BfRpb5ndUo5gswA5I#w%5guWw
+5gy5?5gy^F5gz%d5grk#5gsWilxESpl$wuP&FCLub_~H#Qv5b_XlfB?Nz^YUn9S;Vp7ouXR@hYE3><V
+WYD3a8QE5XDe#H2Ziyt$7?BXXD3m%dwS25_j1V<D-h{Df#lBSYu;*#l4P+c<3xl6N&mw9}{M6^sUvt(
+9Hi~i$`rbO)xlWBjTwyJZ_!rg~T;Vy%;tT~kWS7DWItseW;Uu{1GsjF}`&2(t-C-tEwQPxDf57x1FPf
+!GYy<N2Xk7p|?icPXAf3KBV_4jZ3he029iEeZ-IFzfZP4zF0>d|RwkU^0IJ5Ss3GK`Wi+%IECleDNXa
+5R=dZ8}SBVn=7Ss9oAw;i2e6-IOjW+y;Ze*ekX*ZswK4IL*rWumg7pYsDf}zJIrfZ_9WW#gUqz+gdEb
+%Q#wP`8Fukf4g^(^>Q#%jzz6j>OT#}v$J%lA^oQX>0Emi#^2>5i}C1&w)p~PH(y$4$0pag#VLf(66@I
+aG}yE5IyOTE_E}?kXX!S_s%_G>$11S9use3!j=i>Hr|sBhE3j*ob!@&JTW`n4+p+C-Y`Q15+|wKGiS1
+TFrsHXnUk~Q<fh!9Yw$=?cLd$FCCfwrt{{7FORjb3}B<-GatQE154@Ndx=&8ZjK`SO^$4MpvJ+aYpm!
+CT5VZT2(O5(RvT4Xx}T?BqRrbVjKvttB)E2l-;rELN+GWNhs?9w)Y7&v=Srgpz=0)afYk#=dDKrlA5k
+v8Wxfvv>saha<Mj?sj1Yc6@EF3AVAR_zdbjURN$ghtN0rd%QAs<fINPP+z8BRl6qAxE79rjX-tmmFy1
+?8r!I{M<1BYQ#kFBb0~2&gVzQcR1``wMjmiS^Ikhb>lQ07PJ0!FyI5gd0ZQVqA!hg-39kr-N3nHlXj}
+*)KbW%)2UwrrGC|t`gKOy=@6wIeo)$>caM8`HMXlK-JahOr|*crcih!G=H*qmV?C7eX%@w{aKka%;xg
+F%o_PI~ed%{^x*ea~Z&w~MIgdI@Wm2d;uCZOtO)JN}Cy!Ne=!hh#a`hgoE5KA&;M#wx5ohWL<5I?@jG
+MIx)4^tP)!a56ss3`11A`p6$e}?FUF66hM=o+~kYg7)G02IFoEqfRMV=evxr>|`q&*0(_}!k}o{4DQf
+8V=TW|#5q<L7;h`2PK8Sy(E_{qK7Hy5xPFXer>J0e(>aS}ZWb7E=a!`7zLU2}TX*Vi(nbaSPDz9Mzyn
++e)_8$7c#MZ6H4cc@baad8WVcb*_&i%3%GGSzhJ(gM39?KKv`X@>g*1>*F578#aEe>+krVDA2O`>#vX
+Q-MhEX<5hB7wXED~)#$_P$Gyu(J>{<UE+6-<d&#c13$}5g$ENruDdTPN@R2Io7X0h|O<C^0`}W(Lo11
+TLM&D%l`de+fe)}dUH`RaMyuSads{X$I2>X52WS9m;(R&t@!RxFj{u1Q7WVe3!sfhFD??>NNo!KigL;
+k+Ho*Q4>hVfp%DD=?$#nk^BP)h>@6aWAK2mtey7Dokc9IXFD003jk0RSTa003}la4%nWWo~3|axZ9fZ
+EQ7cX<{#EbZu-kaA9(DWpXZXdF_2`ciT9!;P?Crj&f!r-?5ZO_q?+0-gRC*IXAD9cpkgHIaykSWR59P
+O;T3Wefqy|RRJLJBtcnroW8A-6H6pe01AadRiUadFN<aL>C=2&uS@pnQ<N@OMOjDjSyg1~ntkHG52GZ
+#OsllW52JOS&WeP6%F>#ZaaP@#BZyVHntr-uc~X?qD*Y_sY|{Gt(~{NkEQ_n^&K>c%-mVz@qMB1#HqB
+S-x~fYSFO4^Kal!KRCsvwOpfEdIr&+>E(T}P6X!_|au9z(RIxa745bHXXV`!euioB}hyq3?K-?Bipif
+8QUd0fUbKtgpG#nmiL|GrwW^3khe1Fd)~vRN8u&oj1U9q`XtoTT}puh=Y!>sS^#j7sgduM#2i1SjAyd
+e2sIS+VDzXKYo|?B?;3(dhhrtC!i!yk?6s#wzcMG_Tt}EaUoonVqoDwXcj1Zk+skUDYp&lBJ9MxUS3e
+3<$6S)cRh%j#oZl&+pv1ldyS2!y6~X>`<kZqY=Yjqr-^hK+AyXqc=s)4(~+Gr@_PDPF}x~T|YW0*X-a
+i6&sC4?+|ztM?kXxA}Y#go@Q*4r57wJ&i>72NYixzC8)L0h$_}+(UC^D(*>(f;P(^$d#t=Wpc*TvfZd
+?>;QiU5r+dz)97f4tf#=a`s$irUy!>*YV(mp4FAxAP3mC5BG)LS4D;Nz$XE0iC19p5PZ@zV)4tU*pk$
+g(ntN_|C%9i<!=j(iit3iyE9!Af2L6(e>CyH12L13Rg<!tlm(->hK@(MVJ!wA`0C7wJ>XSGrfKXV|(S
+T2({RHOL`B-z%R0uNtSW1~8L(s=~@o>tY2jhotsoOMrMh_RgLomM|>i${?hdJ72fWmaf<b*lCKBh+sm
+XqCjQVUMQas8T1q!9L6$pAUtS_!4fHJHFyeqnt)RFrb5&_!E&UGJ*9BH_?b#5^)9C$)aK&jaNlgrDxf
+;>HRX!V2+k7IiLoSHQZ?@J<Zb^hElO?&es`VPVF{$)O?#_^%MEavEq<7&FZ5Uuu(JL!ocMRJ3K6_1uv
++~sN`BLtY{LIaf*yDkC@yzqRlzWVbFx0h?ON!U|P{KS;Ib1B8DXxLf1s#O)O=x8gPY`MV28ut<M=vEn
+<+W)aNk@6j>IXwRFHIk|om!83Zz1jeA5h=;ayobp|_H#S#Eg7VE`%Qy+J;KW6FKw2$Wyr7MGwIH!^D-
+ctuxWpSA%L>DauP-!So@KLo*O&fqG=YX$eyv5#@Y?Z+zCA5&)bTLI>F;E3HxH(T}=ZLCHR>F=~6-xja
+FA#j&J=lP?Ic$F1jo#a{2mi2R^R%pLF?;INH3R?@ITciJB*>wKoQipZaUi|wT!G$nb9S+d9O%s{6~`S
+Mye%&$<`bWkBSm`D3eqs~{prU$`kU1`@(g&B!1%_eW6nrsStXWa)31XAnxW=BP@j_($`1_q9l;*cVmp
+xR_v3-G{P_kgS|SQM1h~L$aL{rgo7=J3tiBQRW%(Ldqgd<filo<s)#AZ_{Kv&6E*BN7d>t1}%dbJ^2b
+46w^<qtbph)xGGciayj+q+E!CHH(OOWXLw4-gl4F#bfsQtQAKguIMl?{e<tdy)`^;lsnu(~rA;jEYoX
+lw7Y1Y!xuepM8!@qzXJ3^013J>_fO68t@&wca6h@(1`%*&~|u(3IES5Lfw7$7OvIFJSVCg#Bw#dyy3j
+dWw9j$g?d@OhQ^E->;%2kfp-gMnA68@}e3=_oGpkE@6Eok@6@n<S&zam#erIB(9cHnVY>Hbgb#`C6za
+;0mcG~;n)y-EVv|}sd}7eYFZHhWHb`8jK^FX{D3cY3do#omiuWm-r&{~*6PtwbPqN<(2=0Zy<MAZAh;
+T>m<{%B#398Qtd3-h`xOqIKXsK3>V_NH_*tRl!x|TDG7=2maZX2P4E7WDH;_q2A3aq7f522~EF&h%So
+iJ1eMu!@btA1RrEIiVq^qyd?oOk)n#R~;W9`Nj-|nCSZbQavKGye7p8?`Jip@*h^jZD(KjA&HMgIPOg
+PgU3(QO;n1j^bP(eYgD8YY(lDd+@b9Ner)B!llWX~AjsoK}D$VKc5c!vVn!HM)dRH8NDmy}79Yg1}=?
+86N*X9K`=4X$WBeb>&43)uEXs@PQ=SvU++)?Uu@9uq+@06&@i2+-qp;o}p<8C5Dh@JhNyP`#Qz(O7xs
+Q_}H@bw8D8eEjFS9jFXwtIP`NYo=yp$u#!h{4t=M}lg~iEAYdleQcx?AAcr+ulhf&ln~z2x-6I7NQS^
+R<EK5R*6=RX3QMKW==xGb<BWzxAUS+U&$KEa8wD2AHguU?ynbCbzEJlZbmendtXM~sgpYh=z5IYB5t5
+J^D!q%aNK<q)MHBO!1*ML~i6diUs?<^1LMu6F`=}aT97z?zur;<V|C3iAao{rkL3TynJ+hJYxj3R1fA
+(A+2iFZoz=Xv5O%-<+QpB76TvvU~yl~VXwF+(-iRam@I3X^H#IZ8{e^6gud8Ka6qrEsE%G2}(Lj5FX2
+2|=+8Kxj&-RruOlE}^z%1c$SAKK#tqWeMYb!$qrBQUvCmOVT)<Jp9-QLe^VaAUczC0DseR6!o#fX`lh
+MGr>?D0R6BmU}V8KYO!@X5;N+uxbebA<}ADIYkUuGN{*E#P!<_|JZxJ<ReMa2FQEdkrJsK}u(btq>Yd
+ne%T!_j<Ba8F_cR{hz#hOZ>E*EQ*;I)JxJSIs>h^61G~SI)#-}uR;N3Ymu(6yPooRGd5=Nra5t7X4BP
+|kGf%CHX3Dz7;2yAzYY`Z$gZ35ThB45Vk1#Er?T>}#|1(3!D=R0)r!-H;cV;|aG+j94St%VaP6?pK4)
+vL?JsJr`Onw|1X_yjj7)#mrm+>^W*cCQHh*dnt>K=CarHxQD$#fjKcP%-(Q;#-%JdX0|z8&uf<S35x7
+MiXiap=CI3PC^=Avuqq_+o5#>%&;z~GrLRMqW+*p6`^5=CVA~a4XUEYF4N1a4{A~sV0LIyR9BlcEhK*
+KD_I6EjHg*qkvQ+{0~SUOY*#p*UF5|kW66Rgmga#UbEh5Uz|Ag;G?@rFc!CD02`CXMo?J|-B1@BXWm{
+#f&(ECKJ=QtjF9)i*{-1cVf@)_5GIKNsZM!-LO((7IRnhaeFBEZIAXMtf@<n6LR*Y+0hHI+mW9Aj6pt
+-O-8Miz~rv<e=#XKQN_w5VAE|JcC?a}m9Xsb}I3}U<FooTfPOLjH12wU21)L^~UZOXF5kcSE6Sf#?&i
+9ol=4>PdqW(eswK(-cXwves@8<u4gemF>n9*Q}HA1C2?6Z#pbZU!)B;e;T}jq(7EngF<GjCWBvhC^dW
+LB`)WYlefRfCkYdvRqd+9i2(g+?<gosLit0qJ_=K>eDECIp<C;^OPlryiNmL9Nv*l<xnBq1z;*eN-E~
+CPPfsMx36(XsF0Uvc*$bEtHW-;o}HsdNQ#Gzc-E_^(OiFZ8dhkQ<;jZgOeOH#!F9UeJ})>>hNxI&P?8
+0C{F#{eu}Nu-`+-hzmIp?!vxVMUG0JetoQr<=D6=Wh(o}m4G|>!gVsZe)y^;YM>RJ+LYjo5MdiWuY5E
+*vF*O-w0@xeX7z`fsA#x<=B&cZ_dUb<+!A!U=s`9>-%Pxu{;t4E`M{p(+obq=M_P&%O(K<H9$Jp!$@C
+D*$Vl%}~#6BeTt0%mg=U$7P+{LB;d!r4ys0rT@Fxx<a!*3uP72jsMJp!BirjiNwH+XX)nH;j37+TLpP
+<L9Rhu#eI2TX2poj&Dc84}>-)ZM6~&|4|avtf@y7(Rv%uW+;}3<_eI`rQE@75-4H0jbO%6q-tf5$K=|
+>)5nh|4?ikmjC9o@V^T9v?$^gP^wzD<GhpEq!EHiEAUYA5&<w513}h(f!QbD%8vUY)1Q&<VB^`4qP~6
+^6fRzh!BT)xr3oQD0FF#6CS@Y<^mO~Sk*@X;jeE7!?gHfS1G-_<zLbe=f?o@_U+P)+jP)j2fn5EI1YF
+@G**RcM#SQ;O^XdXC916xO)EX%$VKaG;JIGf>7E*8V@cw%Lh$<oydXk`s^Tv0)I@D3jQogSE_xl&xs%
+5+uBv5AKt=%H(TvR1v#m{|#*x#}e8d=7kD`k)de@<&hQBcleY8gjJw-8EYF8CZOdXOva3E<tEUB&Q{)
+D39Bh@8!$)@}&U<a1A8^2G-&~ppLs#gIE63iB+;!m28g1==*#4Zk8$IBn5=gp$f_iR9HMbX&!oNHNB7
+zoc*|f;j?TR1F3$-!u<DZ{@Ym=;btsf)aPw^1pSIXv+8VDXhN!Z;q5#A!!A{=K;>hy*az{;E?1|^rql
+_2H_Gt7aUOu>`0r!>+boNo4Y2tF$+0+J@YN+B0@1N!dT7*IlK=ZTOBd(V0{{It{@Yo$nG62YITQTVCQ
+UeE`OkmSPpf2gA=Z)txVjMLD-ZPQLM!Ynm9Lk@47O2@Vfgh{{4&ZR_h@MVf4OHL-r)K&J1ZV{^;Z<pN
+MAhjQoeX^Hsgj*bQsq}^!&$h{V!jsty=2;_)7hxmioU|DRd<$FIk*{CQc>AgJXK&D~&=)O|q$0haSJD
+$5u_E-%HlwK|XO}0+<jX0sdO!hi6;y&KfoBoh+`&w~-gYpHJzhvt+U^+j7s~hgFJmUS!ERM#xmWIDh)
+Qed?(<2e50wpyjg{@|jTwv9|`1U)^ftGv8h#-?PsvU<`Vl=kl3V2R&oBD?2ZbzhCp;W?2*xi}@T>H7b
+ff-}0YU$s}QyDM_ciX!5N2ZIw;y*?A3;LdA>X&lCD-mRy%}Jfr}rf!Lt<{ega)Ww#Ybd>OFJb-duE>B
+%ek#3*0KXBoSUvo-Gp{rm_2X_YKN-TBN*;a7_$`QvR{!lF!c8)&%`{&I7t%am=-3Tg|#J}Imn4<GYJW
+T6xO%P9AGb{>QNoq?1I%d#Ls|NN9c`yihgb+(Q=+nzc<6$NTytC;2;e;wz^JNVTU(}ne-OcyDLH*|^w
+qIi~8hJrE0t#@pmyMX9!R#io1hh_J2A&z*}a;GW~TZ-2ucmcwe|GR<Ms(@bJ&<d>Br!dW+BXFt{;VxW
+X5!G|viL0Vp0ds9AFS&CpY2R;1r}kjGJs2pt(b{sA)m`umQ}x|QcSyqk0OGM|%vcsxooyp{2n#9&ZZ?
+g^FVW^7XH~(y(o#AXP{2t`0I}3*Yo9<*Jj<ysXB+I4LxJQ=8<%RD^CfMus)WCN%CgLQci!=?<nP`W8|
+&3sk!<<f6Xdf`@TUbDwxPZ<^P;GG%FN=G+=QT?;)kUPW{a{|uSB6DTQBn;=z(jKP#QGfw|#bXr5&~Ft
+#p3mC0@v1W((+QhWa)49>w-Nj5SB2SepC%aL8(7xB|6aFu8Pp(F?H6r<LlyslFtK#>DXA(3*VUFs-O(
+L3X@>{0O4fj=CJZ?tpSPdV0=g7lauQsghLK&CoJiofqpY;Xdc*WG58M%@F~?Va?2=eKB~+9>;QB9V*0
+iDCpx*%%bD;(TxqerJ7@epL1%SPrvK(p>eoufqs+bYvyR99gXB3c6F5?RDT6o<5YtAXtQWXyaJvy<G{
+ijl55Ea-eBJXLIcgPRC5RA_uQ{t-3PdZ98DT}4CH7uqTs-E7D3(PljHmEkN<h{{9h+z1V&?joGpN**X
+PTojshu~h-zr(7d2Jb&38c;YE_~#Q3pwi0~#F|Lnb=lZsgL1+&p+k2R8*Uy5dN<4$tx3!U8;Q=z{_q^
+nuC!{GIJ{<DPVlY_NyuLql=AW~kQVhAOgRVTSUGsd-f_7Odp_qr}M4q!Ph;yghOF4O=dY;USGkol!J6
+exhY7jp}1kJ`N+0z<8(`d^wEhVRx)q6A(Xb4f@jDtxPkHXHz13O9W~E2gWFA@n9-gAPYHkjJSoae)=>
+em9Ih<i1A1aaYXJ`5qbj>l<52Dfyac7QYsWdcXaE$E98l~k<SY{0uUzJ7ICLsz`KV}?tT3Di&dS+Ke@
+ruQH}McIVBN5DL|)1uW&{o4EuZAI<-k)`1_v2V@s7Ofz<RfD)HP{BF$8U2dG(+E5M*AJebcNp<M-lMO
+5Kc5xWjwR&n6e1fIL8+t_McE_ee0iQ-~WwR;Khlh;iq0Fd6>ya0z$^Ys(9qQ_ZiMV(;+9T^7<z#W`d3
+8m+K=E1YX0>^q1m$5b!;*N*N6g4kMU~5m1<#8~`xz1U(0_G@=<@t&Ea@F{F#|@VRRY-6VH6BMhxO9Zc
+b6V!%@&}Gxg}+DNGy3A#cG@^&y&=V+$<W^jadf=<rl?=a!LWqRd7VI{d!1XI#LwLbv~^Pcq~8_=W*1p
+jY*2PcF-X3I-KCzQXIRO&BN#bL+0pSFVsXjJGEEYePu=4d!hr0Z5O4g>`UlRzNk=~a;54qI_|vU(_8A
+SsESV%C<iXv9;`-UsiZU$p{no8VZs4YK={2+<T}b_)RWFK46#pCSr5P(vH7CK3mdu6?HB~^F=x9YK4C
+;L7QM!t2rQ(gv70u2q%XE{(Dw7gb@&kh^(R<?!V+p2DAT^Arxh?=4<I!_6Aps2vQyd71fi2S-1AEiT0
+7)hk9`G=oqQ8w?8|dwI3D139J<u11%%9SVT82feD{Tr^l+KG@rB?|=>xk~?sY!WKP^6$<dRLk|{@Tgo
+xW922buua(K%0CIc*&zg`Hn*W8_8}4uR@Be-Hpozb3W;vxhu!x(UJO6(g(3V%hK7)q%9<$m=VBqQlS=
+<PmF?MduYpv2WEHx`c+f+ekWHpYMf1fl&CIW{6*6xwJdUu<^_U|&x&>ZIAe==ww>@mdzYERzc-+FcJA
+63#3<A*yMxdaB&%YCixT9NdOXn^NH{VFD^yyNUzOleD~8lDBSqcJp!8&JcyKdQR=di~srs06+Qy?l+`
+nI?*)lHE?Ed|vPAG~Li8{J`P(FMZJ^brm|NP+LgNOI;`+V3(ll%AM<T9>`c^zeSqJa5k`XGv{4NE{C`
+9stmi}nDkt_&h&^;`k+2!Lell4ViPIK1^94X!70<Luh3R(;pjhgKU{{vjJw;hTXCd^3H7F%EGywAQfl
+gG%%H7&eYwNMM(F=XHjSM<cUg!@2^2UxB!_s=JJJNR{rgpaz%eS}-f|BO>>foQFB~QSLIGl?Ad*4gyn
+`lsT(`%dN7a1Qiuma&Xs!q3r<-9dz~Z;9fxA>~(bC>}Pb}1da|WRkL`-V255Vr?^kArlEx6uPdm#A4G
+AlWT4=0NyY-<x0t0EZ!m<N_k*C_HUaJG;T~a?EQ>#fz{FYlaUBCv;L9d1nSfT*Xn*1|%AckW9zO;V<*
+yI^^x)y+$E|pP$|JS=ZD=zN=-&$He+K#I!H&>Z+Xd=8#`Y|+(Np<HqgvCua7n0I=Mg7*62vQwQj7ZE@
+nsx9ZXr;DX?OF@Ox=G`#Qfvr!8emf-wvt=(m_(j^B%roR|*g*tV#omSHpV-loZJ${-O0qt4Kq39K9}D
++R7AlS|BNyg+D#`^TR<y)c}B=@a>}>IiRV&`6h@^3axa%1i4{nHJhEwJ-PS^m{J_b5B+tap5s_S(~y1
+ne_W#w3xx*5^3F~nzWh_oOiz`zD)%6`qHgFSwuie?=|huD5Bzx;RBM7W^N1KrQ%<!s$mDv$Oj)lULvJ
+>lX?nKA<bL<jZihX)fBEp?eRxQjm>y2l-c8|kpJ77J6KJLq#1Hu^jCaI0^$Iu1q4;hqTKIO<9zqaTA&
+Dy!h0DM+XoidwkPg$2dIs7!1SrqUq<oqgFwVF2IpyiEEa$%Nj=4zZ>irpfO5(7$nI|!mvvoaL6!ip?$
+5kUou0ah8W{v<e?9b?Gz=_D<juJ^v>UiO&OZDT;!LZkX_L|lQ4-;g`TQ^Ae8R_t$74zK)t<4X~4;`K~
+OT~Tea918n^F#DX8<d@B(>FH7e*C^D>M<T{r>x2RG9JlffK5d0fn#{#OCLQ3m8E|-D(Z9POgnH!`Vxs
++I5lA3*fGmKj)a0)Vjxzo#M0Wp>tTnh=1HFA`?{YI^ztgJ|KhlF+&Ht6TeEa(#oy(yGoOYv2rf6_lt1
+C{LO33Ng1DaB?I#G6Zft6-2k_xP@(lN3c_)0d{N)|8jp0841Vfh{O?e>Kv$O(%WCo05d}@uVVOcsLcy
+?f8znONuFg(-I_0hrfj6O>DWXb>J>$k+<?CRPf^q>KD)9RrL9$kC3VZ+h-V-AMDG=>t$tW~T!_7^b}-
+~Q6a%5Rjz?qw(r?iJ|!qWUlx+pBaOw^F`!y4v6{!uLSK<Jw&mkw78m%54cQN35NMh%`3^2z59_o?mJT
+jrI0ryAgy5msl&dtauL7Hxb?9K2TSa)s@}lW&Yn}XQ?r3gIW3nAG(U8&l%77ffJt>+(v=lDWgZj*Cn7
+g1y2R?rr@zvl;gwMj|Qq%pVxNx1hbvcc7iEWiky0Mwy27Ap70Q$CC0?!*DTF1Mh+l)Tj)>w3Q9iZ6`j
+4rg95XU=&O6XHtlHlU0dt40E1n2&p@ed>&V*mjUA~DIsTLKI(GKE<ic38IEzSa9lGTYQFjfn;hXVJ>i
+#X>X&wBy-V_rgn`#n+1_7!HC=-}rV^W{T`J~7#XJgNb+xI4~ZsyaKb$?G^qEhWT472OF=x=*}@uOVbU
+<7lU3a2`|R;o7bVvC1dpRTz_#wBwo)c|iq_DojOO%g%gxE+n>3)WmaqFyl}+xTefjr^cMCYLgL(UGj0
+PB$i)DC9D{lG-Pw{zvWDM091s-7MXRxEslFVz@zEMSnkeG5HI3-A?h&yYqN<f!E;@-^;eqhRIM`>l&O
+ECi2Pn4A2QYEBZUXUs&W;JfveD%wm9{Fj~C34nt(+!6XO#{ziVOU0feVFmR}hedfK5F_;1a=`*?k#@j
+1O4oQg)St6VqV1&mXp1yoJsn#ooQyXoH@}inLLC~#2N9w)CQ;EGNTg>60k%~;>J>@NqN9%e%`HTAmN8
+T-FK(}x(#xA%4U>mb09!6XX>n=Kk`U$LupOV-%gn{d^x58@Zh-y_Eaf)=E0XGzcz;F0Tf$lcv4EJ$-(
+gY|Gg>n%P6Fetrh2BDm9rGPJ1QAQ?83P}|9{IOBZqU#VmABOXGv|#~Y7KY1t+Lbaq@T(-To-17bh4df
+npT+Xa_3jBF_Bmc*cwHsb`zG*?Y%-heJ)I$bm@1rDC+3<)hPPCC*49Q5=r@V>G`JQdyUB4Re`q6zj$L
+wZ>sd*6Io@d-9`f$BF%%8Ue0kBYk5H|nJ<P3@%ZkBHPB^pk~?hhmK@?O0-DNCpdb&t<?loZRlAp*%+~
+UGsbe|C{|(Fl!bYnr#PG~nxXGGgRFL^ocGA~LNym#?xQeYC7ld)Zv3m-`0~F$xj9xYWMOiF;D(y|F>{
+jhig?&7BThl|#UY?IzUB6M=`)`W4(HvF@<m=k?jh<U2hYK>@=53Mv#o2Yq1NJXmb@!>s-Q?{TU-nnqE
+*saENwKac#eA~dPB_oFlHu8n9Zl#_FAFh`FnVyN97*`7P_4gP9xMEa6h?*!HYEObTC!zv$;JrAtt%NR
+hW22-a!qfrJal7n^M7Y~(!nb%xl(S6SM>mL=C<-OT+<)wCDr!&LbZOgoAbEdEI`3q+{dJX6*@{!V2xM
+z#hq#sw0qpYyW%OWCU~dkB+YS@4*Peuo#=lqS-Fj7W&G1tzrg!?W!AgVe=@cL`MqRwemg?SbCXcs#5j
+x2QFcHnzL=w6U(1^#^XR)($-Z~s+<AQdrpVuw?F2f@d`W3~@Yz%&_BsXJ=DcmFsQZp%hHE=tO6-r5q{
+NI>cvWv%r!zi+xJna_p{$r){J1V^`ZdG+E_AAt06t)H+$d>Y;!Oa&INjJYN=PK0<IPjNqwswpvr+P2>
+(b01DS%>LOWrnaTjE=jMGzMtpQn6H>Pbuu7Sy1bvr^6+o5_Z&HOzQP9BQ5Q&m0=~**h0Pv2VmL`G)Dx
+Rq)an=S-|h#wo5sP1-uEg(dZhzjDt$WmY$49-g_|%2JvRBJrfQ+QNoj*34OX)Lb}kHup}xa8|PpW{*8
+<lgKVz@P){!kS&Qi#7d>UbH#HRXBl65WqRQu9C%mc1g@s)YiqcOr?~c_HZSNT)i^mnE8R<WUDrfY`l6
++$o)x%SWqdIJ3I4cdG-947%IfJcm3__2y`?hFUVL7z%r5+ExpI3L@T9GVhXFqHbQkt8q1e*QoH3%JbV
+(?_VA}<wCAINrzFIr~DAJ@G`#^7a@f1xm-St5e3A@OJy+W|y2P)(oDIFD<BYlYv-NpuM(*Ne3@^neL(
++#-z&|MrPhJ<~#D!@hnkKJ<wi%Q^KpC<9#-41OCRx`96_?$<Y=~T@-jZcY*aADSWJB-L=uUd@{^wiyY
+NsPR7G_!i|sZumM2Ylmufm!*j&p!q4^tDRkT}<xcbQ7;RnoAGKDqGr&Z$WCtTRATyuWBl?K_614=|h>
++`H+%JAIdz??uw;7CXk#k+4ybtM%5%84#q3zsG?r0eeFTIX;^n7=ky9UaaJsDQp}OUL9fK)1K%O@Xvj
+yz^vW5$tWU}qt@LtEd1>4VAv8RrA6<foThwX~H@q7P-@d}c46*lX;)X}aAOWOH+-T9&wcUk?0~}JQFw
+jabVsg!GY=72dx^I4l7K3(}YV^<{S)d;BKaX5l`QYtTrsbkS{Fg9#J=D{Bw*@@8yoE`{HoWq@+AJS)s
+I4c$0m79p9;q(|L3hPew6Go`B8#$SiPe6arPyEnkzV~#P<5Hq50H<49-&(QQ@T=|FvJ_thQ|X&WcYiC
+Xd*c7Vx8!5o*KwlG~ND`BtAmAqUl;*$=c~&uGLry%9bVc8>J<RZPRQ#LoY0A7=311JW72{w~4tiZ0Cy
+S!;-#YT}v^in>^*ksBZY?aHWZx+kUkw;(Q-hjalm;`tB(D#$4v^l1EU|6N%J`NNJTeA@WqM17V3#HkZ
+aa%y~eWai)=J_aPejTe0L5YYAsw7C*5Z&ta?$klQN^5isQVLVP^JB8i654R{bdo>t*n4k1L`oQa@*Cq
+!o$?Tou!@_0FKD6Rb1>*sFtp)OXDu<3>b3Oy@})f=|KOAk9;tUeS=MmHcvW1h^lm~)frVf394>AvTle
+&0z|@B4^^Q&<)oGoSG$&gmBD1X_osM3N5P-#~XSYGs87p5L*w!$R9_zvVVwb6u0aot6;)zw(hgyMdqU
+a?+r1O2Z^}7`2+Pds6xBA9WY>^SBazg4X?63d-=$>BGmC8!kTY;!!2P2en+!>SGU!ad;V;`xYfs=Dv$
+ADhxVRZ-Z3jMa4(a=)cwv;Nv*+<99JNE^yLmHO@BiwxW!~QNGSHbSh1OgB2S)`3v9GJ^Xe!UYs~Fl1!
+T;z<Z;_<>Y5^LR<<Y3RGF*d}dRt=iJCeu5rE{pQ5*HliJug&oC>{hi=xvN@fP-k1o>d@f!%>M?s>|^-
+P7<JLd_<t_~XAN8`?R2RLX9Icnycr{NHQgp`SE%59cIQq4mrb>P99n@vd`i1}`L*iQGgNMb1No0&S|y
+Tg1`@UweHaDk$Ltl$s(vw}B0@2PEM26xg(s3*)WbkZEf?LaFxM;ERv_zfvIayy#=3~tJj`iHao&<uPX
+l=8i)CB*!v=V_LBFnDST{P@{G1=cHX{LY_(!rwWEud`mbR}F27@5PIXpG$TN1#RbqxcxH!FoQvHd);M
+RrXXG(Nn@yQn-V5H+tFj?^y~<@a!cPg<XPueH5T8bhByK4B7`gIhlj_(&K`<El#+zQ+D@5DJe&~C#X%
+WI_A#A1OzFUB)r%`Q3p6CDvm=e$yStL=WkGqRYYkt=E4m#lOJN31@z2L@g&&O%Sd!ZylBB<UVwC^I-4
+jHL(ktVZ46j9@WS1CR=Hv42dq-NU0*V;2)c{ihc~R-9f4rkN>8WWH16io9FJR4WuH#o82pA{pRmOYI{
+jnNDqd%}QvW+R)p_e$2LaOhwB3=()9g1MbV%5-k^=(BusQfB+mC$Im#$U&{GSc+Ty>+{-Uy@X<M8+}T
+{`RgeFK~U@dPKrqNcFg5D&jdie(pHFXlBK$){jiP8ER%Nw5#Dxq<X1_WoE?B&m#izM#v0wJ_|tF-#+Y
+{C#p74#V&rVkJ_)2?&2D+03Oz`??j@n6Md&5-VR;aBIG=r>mq7pqOQ?UHMm>o%Xdv3>g1Z_Td-SN!2K
+^e>FA)WJ9^|>dis8Dtvfz8yRLHYUCpXm+%{g9skk~=x$AMSR`%T}gS&0+x+?~XM>jRYt$UL)$Kv6Rvn
+)s0lNM5a(P56<;{nP5i%XxSy~FkPhRp3j*=hO?w7Dz6ARC1f4gXrMZkLpJWlRq-(p)vO1^HD6;LlE){
+<}77`Ze25de;1n8pT44qJ&eQMO1e%zfB8vPwAuvpx22Y@_+BZiw4KtfZYcV_8s`3Bv6!b$vRW=nmx2q
+r@Z#Zu4^WFuq)jF?V|j<Jc$t9{(Z!YMM=S`{9YaL^gfYiu$@R(>bR{DZMed1PgSOw3c6(FufTe8s}>&
+yiF7AtDntb7bn3XgJXD0lR8{ySb_eWPpPxrEwULw)WsJ)xXPfrf!W0~}!wPv)Z_S~I6>4IEcgT;?p7k
+DS>|h5_$}At*6F>TR;MvPXz|Icm#|c}(CPOn~o|nkrAp77r@TA7!g@ffkA;Qoe|MM><;8Dy&0xSk>18
+*Un%32>y|FZVY4wNCH3-(1CZGWBN=6$`P2(X@z^*$I`e8xKOzTg`1t6~;s=}&ZpW@N5)oFr&yxvE1Zi
+I=peML^J}#jTDtAV%a<MVSpN7MHLap3xBnkmxjePUl|yd33?Ho1#oApkXhT6h|ei$)EW?F3hec@EvHZ
+tpF=K;v;!1dS|Q<e<n?kqE}nnE~Aotm)}XkGA)0kle&!=8UoKbEouRIM@GvWc44_9_Hkb;tNnUC9M?B
+OpH{+`*)^|nU*p8JeA|SK(fo4h49i*4k;?Fjeh0qJ1&cuB)3V2GN=9&Mr+rVB@wYx2i8xveS)%CJNQT
+DZ6CM~l#_hRDfyZ)6e|G>?ufc-wwlgtiz>yj1LLzQ<__?h``U285*<=PDnth<?;#;ERhmovd@ZpvYJj
+L&{bp-NUH;ktu5cP^sifp~if1tqBt3jeM@vJ^qg7d!ETy;z=%V75jSQcJm6eq!#jZsR4((j2I0^4Wl!
+YKJ&HS=<P=5!`)Yb;pKO4vYNat6TJfy)vfz=6<|Ni7bF^SSz&ItKS&Cvy;ejuTARjg7s2b=-(aeSCt~
+f`(YS$h)g4jtLZnK*C}33rGc18_?~T12Co*ZF52|7|;Doos1YxL{z4W^P2p%@C~Mb#+=b5B`0?r_}q=
+oDQ|cixQmWT?8i0U3sF#|&&w>YTItfHTbGE~MXLcGx>vG*eYm_<L$XE?AO7*fm!TodckvqmT@L{@89B
+NI&!}+#q(gEMl63|sLl^jx<rqdtS6|9dUQzNqzGP9XflJc)oR!?^1Sb#8448WEe?}7X+bG6M3>G<%k8
+XDlO=qGcAakWQab;^U<6MJMorp-*CM}JLx83ceV)R{JU~Zv2Bs1gn$emh$q}9Ar4AA2vuVSP(!VcX2n
+?A)laB>_OD*<k|Lzu5CZV%l)j|7kCgC`NYN<XV%qR&qFP;>Q%ay1?*)4CLQ^)&>(gE)Ia^Lpvi7xxq-
+HR8u%Aa9c<^voMI?lg<{MZ-UO?AURd0UIvd-Pr+J<~iK*rg34OZ%by~+6|hUd+!EnFX;+ecp`6sKavn
+n*MQX4wEuB4Hwh#~30%4+d83h16#2yq&qIQpoo}-j6giWNyUAV<=d``|AmHZ)NaNKdCIw)KM-7cRIED
+P!Aqt_oDc!c=N;}@>=juG={3j;XSh1MPis%^FARdQ;U?RW^uMH9q-88}XYLZ{VHQi43p>Qa70^>eMHy
+6LdEDoK6e@E9dMQ8Esf=+iZ%LqxxN($a4Gq08|Sd3N^QOLfBc@umra!<(~I6~e2=N#8sx2)qA0pQ9uh
+cM2q^(EXNQ?AFlC?+f}vu$X6K-U2Gv@!P#DYPXA$@WAIT2PhqXpiKo$H@UXSu`v&9^pHl?5jfvpcF(q
+dDZUVLpwZ@ao<)mndxj`S{##hTkU4ZcG`^qeYpOs0vuEp5dG-nZ5Y*P^+9MX(GgD;GYXicwu+k`JLpg
+ARI4tDzgMKpFb1<D<+0DRY@M*94&E#faCN5IF>o&dx;U=4aB!s7t>?J?;ddwwVUCTUh_@~Y2!V8Y{|@
+6@__4*t+p{O%I1UN^KvKgGCpWCEX@b4oylT6S`&z7^x=te{rj1&zXXleSOa|T)ns5a(ih+n`+bGT!MV
+Z#;OVcpd+_zC(=IVVL8+OJo+9>e0xSCB-kGm_u9&@NAyG$!=<<Ecl)1M8n=uY*7Za9uwx`~z`)<T!62
+!NLuUJXolYTl3Wp=K*1TTihDK(C5o^|V+E`Q?GL+9T~DpHHjuchO%S-?1h_K={UbVb%WwCKK}2|JDim
+m9W6in+a`lU!HN3dlrMfC7-2sVlSTg5cgFyIP*Xg2RLqgcDYwiP_jE?^@cI7-=IDRpc=54wyo(&g=@?
+KWl0e%QbodghZInmZx17(EH=sYWC;ft(x^9S#SWbg=Ga?2Zy<2E5>fA@B`F@#I<POiott*tfuXt1)!D
+yIkC9Q>ZF{T~W4ol+WaZB~&?y^PEf7CD@Y!MR;`NfXygxZFq6&CP#PgHFV_+j^MM(r);q<bV@3vwjp)
+`jQae^d@vz0JBAV8NLK{F$u6+ZtSAi~Ztf-iNs1(MB*jr(q6#a_UT|DH%Q{0NJqgVf%(w3sSrARNR33
+wV1oo$3e`$pJ=e7Ged=F6dkcrm|U?0+-Ut6+2)k2Cn}ZaoL?ORY527vREn$&%Jc4$+D40wpN=AJ5z%V
+Q+(uBG2uJV<l*$ukg)y+bWn7)HInNf0nQ#fNg&RuqK^hRB{fs(fI>@<PXsPzlWcS%;p(Jkw-DZJbZdA
+ZQS8E2u0|Q(PQM*W89@_SxA(z2c3nA}4j9cQT<LR3%&ltXWh$6vHw45d*|YhLWero+CesB1kp)fUxV6
+&p{2@;_iC6l9B*(9-mtq|-S_g~`ysLq9#3-7WZa)FI)LHyExyA3gk}dV54h-TL-vlcae7q!o{jppAT9
+>#6YM$3u%3gJ`S{7B^93Q!7)xDxQPk_0^?8&7@G0s&P%gHcF@=y|3ocGIt^0GLLUjZ7}PBC*Vko%Hs)
+<|?Pv?6ErB*$|`5i@>0h1}aic&bDuS=U`A94b_l%5Z!XBYV|xK*to8<4e-yr#iE*KK3g=j+9Is`$S}a
+auS&#MCr8FuyGds{g@KuIv~u`63T<3h}pQQERtl^?)zs&UBg!)T>GfZ-gO!PDj$m0EA-uQOA%>~Mk=I
+FAGy+aS|P<X9GmZI5=^Vf1XZs15}9L2T(%9_(vWXy6EBS0ZaR^D+}U!#TFz)KA3tecq1kW|iR~sB5w-
+u>dVzN{7Wr{L1H={`S@#-dGl|TL92EF}@nyn3D~bzLIJqpgS``)dXTv)x@U$*Xpp|sKm7Z%Drpf(tob
+oxYBuYz%_Ju!A@xYUuw4ff##R)X<@(pXih)Q&_>fyw+kj@y)H{VOVn)wtDx|vwTd|VEYvlv~%E?Lvjz
+fNuRH+VS;p#U#G_sz#&trut#c*Cu#!nmp}D|_8xvfEC34KEe254mqS7M41*neSY8S2#QL2VX*H^he;Q
+V6B0MCRYjv8fW!1MkMcRq0>BlN|isl;ffPFOHCV=wF*BW$3?!nvjP@fzP5QbM;|Al>7P1>a5RWRy3;m
+l&Do1YG}n@@L9`vs?XbKEWziqJL?ycU7w79)h6k>EZB6M1_SnPo??&$#Ij(X{k}^PbcHANCi{QfRs)I
+*w+mL=})IO9zgWD5}%wM3H5}Vuc*YCMx)6fq|E_m$%PlR1byJsKab*9_Kk5N#|ur1%YNrC8vrxpq+-V
+FKS1Lu2#%X{|+NA<hW3s9iAu^)M60}2pf2n!z57u|pgLM6KRz#nvBBM~!BxM8`(eK{8n?Q?3(Z9v23r
+5XE;>8OLB(76el7Z-Sa#7&Y+)uT{e_^SBswLaIa^PSIP%e!<a{K%j19ZF?a3szI~)9bX#p%*N{-o7iU
+6jf{wXA$F6VKxKRo3iWsZ3hN2+~n#dHJhJGW@#IvRL<!I_{e&r?Ful<91Tx}d;gfAb^x>wjC6qNfcs#
+5YrWa45j2RQ<1CNm7I+x-b@`7St+-^1LESWXKXq-u_98`Jbv|g5wujZ8xeiM)*L6az9~}LPob2|7fgM
+#}P1_#g8M0%0ZQ)r4$mxg~anRF~-k6jipQJsc58}B+mE1vRjcLxALTJyiGPXBwf6j*i?mK<qKYO|@?<
+m~mY7ahOqWS&!fi)f5s}r33blvd6XRX_X5<k}MHuB@UXS3tLqBo=emq1cG7J5Ac38;!NImtV8;vduh3
+f&@gfBKZ>Y5nPwAZ^|)AaTI4LE%(;+Ew0OV8&(R9ruM_`KofO_7!d_7eR;GjmjVB*}?wqQ66&9pUL!k
+Z)fYit<<|sOZr19^Ix_ZxIqZQ?cpxotsx}ien;Gj+@6SRwVKYWiHC%Giu<=H(+6xZCuPd@MuBwUqJ&O
+GzbBLM`=`&2Pmb@4tS=PbF^YaKR`fkY!aZk94JWxFZu&hq6CKyKn~^KRc6a1*(|XA&;iHAuHPVtJ%{<
+EhIqUhHo5G7c+eYW4!2q7-2Argrtw{pIgJu?Oit?h`WoN!7ic&bNNCo0%vW*YqX0uU!AaT2>&=YdPT_
+O3&*#w7q|8AO3NQJ+ZtlGgaDGu+@00hsRch$F{=304?v&LPejYZe%i08gaj`FUeW3Qd$zyR>R*!<vYa
+4Z8P&!u_AsEk6|_5@pU_E#Ehv^qpt?$)Y$%Z)r@1)I%%!1rR?a<wP(#p!*-htA?6Z=+TkrFET_uDp-$
+btBhM%Heb%RMJ3qE(AgAy_a`)UDAD^b?GH}S@q*_xSVm94l`LPcyOih6Vf(6;35A>L0F+XlSQgDD}-@
+G#TADo3TDIuer{Z)gBz%=ZzN$CGd9;+HFirL*y}pE4UnFND%j|@lAAiF>8>KE43t!wX$jd|?neKV{u!
+E&1Z;}m;aI<q()u1b=QKBYl3SKKl|sCr5K+f;c8h$0(eIw?F}(s~F>kRO61^0Bb?NYn+B-x5ZRd8yHx
+H5njI?Gbi#rqHswwu&9{2?)yRK)ruH+y?bc$aEQhM;AXiI)XkZaY3Q63|iDF>b1#4Y0>r~>Chc{WE*l
+~0tI?U`)e0QijOEaHrD886b=?>hCl7iF;|y!B-rZ_&&{@%?xRZLTA4r;y0{WNYs}viEyThNouiCe<cQ
+qG$YK*WWt=_BjMKN27NbeIu|&Y*|xP3CL$t_2)@2#pm0MQ6*QUFQdN{<3rKf4BJ9$BSh|w)4uy=0tBW
+jUPNVFLe&Uy${kevne!%r0hwrpoM9bYnOB@QB84w^eiuF!wW$NE0*;%6FxI!_%}rYBuEdpnceLDwOH=
+|ViC3PGYUz^<8&F{@7e74W6NJZtqB^--5-Oy-OXMBV^|~FfYu0+wXapb(2jN4M??wo}pYcGnx}kU8ay
+<y&J6TtE;#Y*e$z@R_MG<;x@&&{+lNE$E<`B{pYsRiAfhD7Easdh=yvZcNyveJkJ)!f8p-E5L%vmF!d
+7$fKX5<=fJqR<71Yv2ouR&;Gf&UV-e!i4>MPJ(vq;7tC*R*Gt1%eF%W{0!{fR2^HjcR*wjtI^bI^aMe
+%q!z7V{bxk+nu>-=P6(E;Mz1zSixspO_5qR$)}iM?bOg2=GnSBH_S0c_fR~;q(-CZzhMI&pJJMiMzmA
+&nYfy?ldi@2mcwWq`HeJiDoAX_<vGQZ@Er}gu?PSB(!FBMD-Nu~Y?GtFa1d<2oI@b6sADSoCrR2+iX{
+n4JuBCo(m*}4_dMF6k_4RMUsrGJ)A1p;#Wt&#pkq`l{%idJK8{_?k*?J>bJ9H2VfYL;38Oik)%BL=2H
+h~3dOK&AAOe>xo}JriCj)xLVT+IIDB$j|lm~Ag&2d)#<@+VK1YxV&?;*Q5>ASv-#cvb3_H*`e&pBpSQ
+g(-+={&#GY*0>*>)Ul6Y`7k9NL`KxJ02hho*M6n_3J)0rjrwb{#-?nYY!6ZwL718i*-}e+}UZ(CxAlL
+D;$Y+8uj`PT>QpaX@wcA61Z-W{F{g=PS&6rV1kAyD?qZ3WQ_7W#l&0dTBMpGH#AHK;<)rdonuK5u6e(
+r)mBcw+it5ixQn^APum(Wr=5mcW-W#O?DkG%x|HPO6YeTE6FvbyuF>VKN|$MdJ{N^*?LoPHByXR(K}(
+IC#(}n)tLHv9KAPdCVJ)}TbQ6HK>oeX2MU^;fh98v$-GKJx>_9snj~)DaIOT1(hYjG+x`9d8588w9J?
+jVrSRdH0MMv=Y$lhiV-f#sL`*NToa8vq5<Yv`>q=V=?c90|KlQ%?C4!j}O<Lftw6FYC(acuy-98}_}^
+VX*T{}jjl9YOq4nUpKqzIGewl}5QISvtT|Xn?oPUQdO;LaZ{I33MOQy2taygekR9-(3U)PZPt$N;ful
+5MlDdCA!eQu$##86;Y#hq=wSeHH0ZW&`|hmiBqE6$#gSk*%SBUw2R`E(^oUlnlVZC=4H1^Bz%hnx&D|
+dMStrGySm;WpQO_`ds`!UaGiHV^9+s3F(zt|t;Ix{v}b>b2%q70p5Pts8f9*U25J#odu6k^Jb~}En}?
+sMcB=74d;5-M8PayB<-XU2r*qZafTKDvMW2g$v+rEZ-ZKLT&DxrT-_sf7pJeQ;4sY+y1TKM&@qLmUKU
+6tBiH`FvW_*pSsK}%9VuM!q>O7Vghmyy!vbp>(bM=q?=U<Y4nx|i`D?B=&2+kP-r3aGOZ%s%($Ik(T-
+qdky-tOF5=Ct>4(Z1_B)fczN6up<jDwBuypH}JU?^i$vT!h!-E4&NIT%RJ@PZZ(C?_KyIeuh}g1H8I_
+V>eoz-`I^TaV;l4dldq9u(u51iydb~KpOkCew4-L*J%2<X^Tdma{W~qHWa&&;7U%b(Ll4so{S|s+dsT
+Ur<*o>L}L`#*EwFy&`RdyM<OB}^zsMV0=4!QK-L}&C1sao;|g{SWHdX78hf^PXqZ&(&_Y+<#W#>p=64
+JH&E#>DCXZ07LDwp}iJS+}MOSi>LCGa{HyQLD{+yVHXWCxiv~w6@CO1l;5h51B2^gfnGX!Ouc-y#h$_
+v4C5!13wd~rX^9aPuO8Swa6BIZMt3NOj(<g|Z%RHT+rA>202p;!OBffewc$#j}((k$|37LLuOyvsG(#
+Fawh$*o;WU)m}&WCpB}s4oT?QEpB-4VIE_p>B$s+MXR30phPyJH6q+rrWM@I@EQA)A*J30ji+$nA@$W
+23|d*1H_a{*Qq@kN?=j7a_*pv87_-xH?wVIYMbXKCeNja=Pr}%K3hM>)~`d+`WiyAdS(1>3;Ui7<F5J
+i3$T)Cze<zzmkHMj(P;NCW_5*MX%Kgf#nONbi^nog_Xj0<lb;=K?m(YvcTe#%G?Bf5vZ42<$j{JwKZd
+J?*BpSwT|oLfA1+J}jP3byQAH#&-0+$99yxGTELoi{>CWIzUOYspx|v9;4}i_tO=af!f_lcNaNbk!)}
+Ji^Dmbqxcq^VeD^-L}vkFJDTf~-$`$rdS3)>7y)a)}<WU+ncfH$ANe#uSKF266}_9{EZtO&vnpOdCS=
+#20RVm_`%McGNc=e-;ttzT;jc!9Gk=YZc?QP@w)p&@rH=?tOM$MB!+<+7-VZKLn*b29!O`~6+>y|Qiy
+m=2vn9CqOgIS&?Kdd_WpcnFPjy7vBuH^iC44Wo*4lPe}cB^chdXYnrE1R(3}n36c4k4+w6XBRt-9Ssw
+1q%#Sl)8f%TR<rUz(Jh>N>09az=91oThTUfJ_HS7a$fa^K>|!5Ip+m5wVM_Mji=;lTdY5k}RP1~1;W$
+fc*kwp#sha(z!@|{zTyK(bAB;yXHrr`2>AgbdWgW#?RW!qD2mPgZ|J~@x8u#h?9B<`8D!N@{#Rupntl
+m9nZ|r#&>khXXwT2y(OhpTB+srPFs59}^B{<$^n&Ex&c->6Uc{k!Mw-#szje+M{K|YiE<$kwU*S<Ccs
+P3-Upi=L8a>YsZX3nn6wuYQuU2WBKUkSm`U6P&>PCFo!PvuI1;`cRsotY>OJuuoQdMy?^7`fNw1~h1<
+x@T(-)@N(!`R&gyijt*^oV@pPj~$v$c8axB#xd*nyexiVu)o*i)6p3+sIzoIf3jkB@#DIviMdJMb{3w
+Ma5Q6hZwmLDNC-+o956{Q=?}S7B;hjLh~Y)6@c$X(q4E#u|3~!yH{oE;zoi;~r2qd!|I6Te>TOE@EyE
+B0Mj2Jf(h5(o@GBs9&HvUh{>_Uah~v&C<Ylrd33286jL=`vf6F+_!lA)VMjKUCmj&+~d`mC*6}js({$
+~{quifxylcRC2smHL+ENkHP$T0{xPX4{F>JR_;0Tu)%tnA)$R#`l2X#VF>oYjkLyE;dO0KWe~-@8%J)
+Pcs;YR_2T)$dJByk9`A`VxQl)y>moT(en`6(!Z>zn{|Yu)52F8v>dKuZw`}pt3Msj77H`w*XF>XFV`x
+S-JvAitvoz-^CtxGo{X)0fFTSR++OW^tHDJp$B?ark{B|!q02|AfWbgRpdY%e5|aLFvQ~%`8W(x1@o*
+xpq~LX^UET;WcncZv(RxQ>3qJ1Rgf;ui4zf!XZ+zS`7i)V)vT6fypmAbhp*#4cEO?4u<24?8W8w|I?`
+W%o)v|lffw|(zs5SB#i9m$Jr1jJ*4B6uR%6lD_#v!D+17a9Ut<xks|o~`vurIY{ve(_!6&`-VTCRP{k
+^2Gy)~Aslr>({*RUFBTTp7fp<TYWW>K!rVR0_DqH6oQw{9_?gP_9ez2)C~>#XqlHiZi5I=4U?#IweA7
+MJ>h`XHW$uc&HOs9pE~KLdLJI;lYMN!oiA7nWa_XKP@YGg+(s-B*{3kHy))$(YnUc*`I3)TZsN<QpS>
+f8W0=W@*mia#6-f3QPrS@+UvYCq4DCSe&W#`IFH4=o}#TCj3r&(=WQEpglh2$^=G3sKSJ;O6bLpw9=>
+AOV4#X`#;q+iekwZFJ3&;v!s)g?O4@9<|Rs1tbY5S9xFHAsf5?QbX}=L@6D?@z22O5%kmy9wdgy~Ro5
+!McD+|d95TP@GMidAJ|gBUGNHPb!sYOWHLBuN&OwRuoGqZHrXh2B!M4@-;2`=R5!d3ls0i}-Jf3kWKm
+7^4D*LeRQQXn-q0!qxOBZ}$GUJNo9#Van1-X&C?-+((HU7IJU-NBsVH<nXRPr6#M3H@GZiN=<moQXdG
+<4Gr?jId}I~WEXM7#6Xuh6MtNE?os)Q22k<#*dH@Z(8UJXaToG6g`e&5!DI1On{&G7E9Q<r*Bvz%j_)
+>Djtw&YRkKuR?oJT`oGCw>3vklkN^f#s1#f)r5VfzpAo4Xhg)bodfSUp$od`w%tas;?oRNmAo5uL%fh
+}(8sfjyx3$cS+Img<xMI5yzibZls#YO3HuCCGH=3~gJ=>xwCW9F?Alpdj}vwH*;0^KFMhh*#MR#Q-Cr
+Hx{>?)|DS1R%<@ahC`H50+bcR>wAoF6&D#{toQCx1FaHM5cch&9;{zd-R<&|z#?YUyDv#!f+yyNaQeI
+~kA0a6G~h<_aFsiy_RSu1CoPc(O~+cxFL?Xcf2k;xIP0_S`GQfO-1ms_iJCwJkF?beMB6d%%jZ4byFX
+Q1N3p26PBR{hwLDLEv3)v$g%2m10Sh`!}#wSLcTt?U;(Ue&uOYGDU&>df<Q^r0?RGz+jrX|Gm506X4^
+dN(@ZjFdcFSrVPGS-b}3L-*REM{5C(BA$JNyTBr^{SJ}weI&$f10vmJ24g2N5-OlqG~LUIS7dSdAlpW
+`il;W}6sbV=p89?0V*n3KQ~nSIHDD)1&2SRq>f+hcc1?TYNF33BfQl4$1L1bp;(DmyY|m+D_mA&(J&V
+y(_CU8omFWg_B^R+4NfQFd!?gp{oKQqi1I^~(zLsH+zDQrgq&Fwg!hEyeybaPQ^!0DOuHYIuI<tbrrN
+5H1hO>BH@K($kZ^ZLkBNGJRhl6Uc)7$gwkQ}Zxa;O>bmx~<=-Ng0YIyXQ$QM=!x<Olnu?q-t9=4+KB)
+M?v$5JSL(U+r#)UJv5{T*%?8sMtkZ2n@X%Tjt+p1Vh6JRQ}SgJr|Z<&Z7;xS2pQ5+cuY||D!rVX~T>F
+JJMXBM(hCDwkQ>qXdLI;B__W?KtS-D6j=GRdrta<D@j7vE?ez+At-i(YmkHpHQ)R?g`lrlK=D{HzKCG
+5lSyO$YJHzjaMca;OUD2jqul!*+GC^`uBqM_N8LYW6sG!{MRpK#>t`2NFmnc*7L#JO#y8iRTiTGQ_nc
+bVtFSfH51QNAx}GeGn%LlPCe2W6H9Yn98n+CJ={=wxH2y*9#=e|s@7UT&eq}6pm{9uST7}Zz`1Tj%+q
+Z$kN&@=|#N%sa>R$`{y`#*?&kdvzvb8kMpu$W3jrd*L)aZ<%OXc6l%oROpet3%Eq&CIihgnR_JGSO{&
+3$}Cp*Mv{b|wqIDR@#A6DEoC0_?H;ZZ}Lcj<6cxTzQQi*6<QNee9Y%ANb=)4U4x6oNy2?+h2CUry?>N
+i;3@!LPfS-=0ET>7g(_U?P<ChntornRuHpJK31;N(woG~IUcJC&9&DbL=Fc%kD#N-O9fZj6T5;Q5K1*
+1F{0))i;?O|mJU~{b&t)nJ`<Kw(v}*`!pCOo-Yz&4+Q&ZG#3X&HEB@C`6o0vM=MG)qV?_Sq@&*>OU9q
+DP=Q<z=#Elc@5jthL`3ABe;rMt&PV9n3rE{e|+F21q%e|C$N72u}Xk2B4{gABZ1in|N_~avom~y7XyU
+Sg}A1ko*E^7XskJX+c8o03cuH~A~KhGFXCCIxu8ja`$+LMX7CXtjA;1v}i+2sENP)h>@6aWAK2mtey7
+DqbIDGUV^006~6001BW003}la4%nWWo~3|axZ9fZEQ7cX<{#FXkm0^cx`MhaCz-pZFAf<lK!q=!5weP
+GqEI-!&m6mx69Y6txB?cd8tjEr075rw8l4@(G4kEcS-*H0Z$Xayh(PFyWKCVD)#`5?nd{62GAUiyUqH
+}xw+{chKG%~xp5YE_v_8z@VZ|w9|mzl{Zpr1ykGQ-_3G4lSS^}$D{hvHL2UT4KRFq7HbS>P@XczmdMk
+%IW&XTbulj+nhF+U5_`ttd_x;~U@Z_ZCy~uC*#(CpRCX<uP7muzRcxUP(Os}1v9B?Ay#Fmr0;1rSJ6b
+U$$;*gzc<WyS5bD`z=p5gcbaZ1CX*}WjqcDx`3C$=&P*~xvw$qR5Q*KjJbT!qF?1v_&u)-v-DcIIhfI
+9Ucx4i9C^Poh!XPY{luq-H1|wfv;SP-^1xQy-kX)U*2q=JE+Kv{K#3b)y^`rK|0fb7)n*wiCF(CvbB;
+SKwmyATT%wA<2YRCM21}Gj<sK3G%?m<S}s)%gKmi?NAg!nHf7JSq9D|L+c(?OmjjX{)C~9Jq+2X9#U$
+BiAki8rU*-uY+*&5YOXoa99of|YBwTEn<y|^QB0i7EO(Tp#4(8!Q5r{fnIf7!D*Vyas0g)i8Dgk3t}+
+)K=Ce_l`Ne35g#dv?CM8%x!bxs96*$Z!En`R<mYU>eDST#`tEb53S~!bn$l%P19Gs#s>IF?vS~mzxRv
+Au8QKW8aInYWySKCQFIF`;$ZlrTKmior|)SnZ_w;ZzUn=>Kx6XF=3(~yBfxr$b)hhj1q2S%MROp{CtC
+xsR!FjHpZA}~`6W=deDne{oNDKhJGMpI;ZP1CFx8){d=PDY4jb`NHRSmqha86lQg#4<uG^9`yQA(mOh
+GCv}Yo;~w*fLVye%|f%dSxA%G9LorwEJ-Z~+cUE{mNA-?nS(K7j5zva%UEuNM|E?yWOTA+sacdPO^9P
+BO-U;=XF`_6#4+oh6NWi3JRuIWa*JW^!PVSim=lJ%MJ%Tr%Yhi#iICyBg(W9ga=Z0%f+e@G<OEA@vn8
+i&$!%`rlpDFpjl9sgnwK<bX%5(e0xWEj6ck`#11u=O!UkASfQ1dGpl}KsPDxilIWI>yN?+d>C1q6UoB
+N^+pk4-MEy{${letz;0y7lTP-2GUC3S*^&cl-7VlA0DS4+yD(k4ln(iEvaMawiC?WD1hNl7NNS_N@TT
+$zcB_GyW=fSt_vLuYttFS3e4u7cQd;9(VMR6TjdovDvp!{WIGajNl>@?^C#4tcJcU4Cd(WiupBY=oH+
+&S*%lyKI)=cylf6=ZHc!%8?Vex{>RK9tw>cVE{W}sO{)$RJJM%MV=mteBB6jBi0S$YE+O`Y2u10(%9E
+_Vy(`!KWU;HnYNQsfN5pyV86?cMBB+yEu7^T$_*N2Ha02^LZ#y>Q89WY(@El$v~Z=VE~FMXm1mZ+@)F
+|cqtdJ9BokPffMkNga6*y^^_o@+UTS2tUR78aVE|smRy`s0EHE5g(DEbK2D4K8lLO}}x57CIn_A1OC?
+TuDw;YlwG80!xGNsk3$Z};ZSEN;qZp^*;Skuau+Jo{VGehHnHm4d}e(0jhr{@CSW%}gz-IO?$KKXsx7
+rr;w{-ilpr-<cIpdK(z7Ohyn-SGR{MMKXVQYfHhW>q?7wlAMG^sIB5W6nswS>M$Q_3WaaT6iNLg|;4D
+e0sp}?P2HB>FL|kGe~fO1zQy-qZ%|dXLm@KTxhSWkRA-etk5NFR_URf)OAA3R$!`9XhoqOiei1DMOaJ
+uVXlX$D2StXkt{W-7%k1w7L_q^63d~Yw$zCzt5W#GTzytDUtj0U#|^{+U2L&HAES((8CYoMV$qyRSaX
+*!x;a>)k9C&ix>4$ehK13&%yLsGFnYjX_5p*@0|v`As;to01EVJ&R_eJ@cTVA?{y1qmo}RLyCr#<-PX
+qI)kOnbvY!yh0lA4__D@@XgZS6>rrY6!<MM~-!Fig$OA)7QsZi`xqE6vRm87)d?79}%_lF_1Mfvp0`=
+Pa<bBl(;KTg4w$ilMpWvV;n|#M&WOv)rJO<)oDxG_stkQnLh^S%RE=&fTqIi^`_JtZ5!Z#IYqBJ+9{#
+-aMqTDKtASCs*^tq*hL@=JpoP$<^FuOHQul7S+5U%Y}KsC|s(jtj`7cTv&Ju^0}}+7vyt6@TyiA>(a_
+Ir4`jZzA1A{TBb6v^@^vfu8f530aDTdQf6kqmw7^kiwTGBE&0JlH!7cQU)hLtgXu=88#?(bdg84TvpQ
+805y#Y66+QM>Y&>aYD>g$^B&8<Ds*DU~u@=t8(qT^3lKjw@id%Z-#7^nNt|$t5W3cg5XDQC`cJX#GY=
+7hA)3-_XsW75rJBloao?9dPtpbba`8P7>B`b+jnnE-snKZH-l1Z&hMlzYTlhG-gS(%(<a%(3iJGqr9N
+T#rMO8S1O3~d(B*9&D~I8>R)kMVvJkAqmX=2la%WaHY;3s*NhXZ#9k9E!&JQ6^(FHu6Ycr6|3zGCtj<
+zCOC-iWGFGmxfd2bf=f(&r%HI9V-PU=kL~w)$~$-Yk2BxUFX-clam+D-xkB|`eAT}TXC}DcVbVI%&D(
+6WeS{1_C%61MQ$WHyxdPuoWnnjk(^OYQ7tz8@WedQtnzE4zvSGAb2-WVVkxGZ$&1J7<yHGrdRATk7@P
+(_&nB}`@@u|&EB3{&E)NvW4melW(uhoP(K(GY^v$r5;e5OPV$ly%#pq`j2&vJt^m3ZZ#USp^dntQAoj
+Kil<0yu+SV62m@9&q3VT$->*HGWQS8CMWmnX9q2Dxtiuxh{LLvt&2D!%N+*3d@A_*OjLE9L2CT-7ZQW
+*0jva5tJErE_$rYVz5R*rFRH7rnDu56)NXmAE)q%FX+8et$2Qa(a0y9=~1xc{Px&oseMY{OxA_aNkd7
+*RumNa<ZjZO*ii^&%EoI^X84?9nMAbDGB90H9mLd2XZ@LeY<%O2NLy0@c%SiIk^hZs_jnA`(A7Y_(zc
+`?@o3^woq7aS`veAGrQ8O_2!N*7qsh;OPlxOuoxaTD=9T5-JxVNHntK^b*0sQy%|Jn1&(Hu8nH2r@hE
+{yiQvD!p{9G7l?&N)(fV*J_mAxV0Wj>jqyFAG3Y0@UwCLqxYt^;2S5j@yqQh6MW0jQ1wRFw-gI;mv@~
+i#qutRSSl)dX6(Ra?PZBG}f`)+?3<QXWOcD;J3-pugj^1q$_;T~^a8hxtu4zJhmg*qgi?90D87t@nF{
+&+LK;gNnU<W87W@=qR$UZf9{yG7s2(}ec-K9D=ucQz6k|L^2VTpYpo(mmalQ=(^%cfFMdrzRD|>fugo
+_#o`6T&V4K)UHksiS_9&rrzEx+n$cAAidoJ<EZ_870k1xbbWkp$ZauvyZ#zSmpquCL-6@sAiW;bUhU^
+G+WoB3-ip5A_kw1XQ*NfC>TG6{TF68Bm+Q;8<=yGLI(pbiqem4zGw}$|<npurdg6R0>kYa*)zLzLnNw
+LdZshf(fg;6)9oWv&ooRdAf1_T1FPa5k=5`yrai&xP{GiD175Q9|SBm^mkslTLd2+N4pufqb61i67Cq
+<@;%&4xgsjL=JE#`p_7IyMgor`FkZ^ZqQH<a+w+SQeW8g^zU+c1!{+V`^VxtFC6-o2`=jZJ>CTCcvpT
+Ye=A6}i<3mfgzY<>i&E^3G0QU!48v>gw|B>gtF8xVrlB?CP<O<iA8Ux~s=FzIyE9s|VNf=V$ovK|6E;
+7nCU)cZxOv2AW3E&O@hzN*7T_3D4y-hx!PV&YUxJYOZL5Y32<^TIzJF;|DO{hv-BY@skj=25lAPb48)
+<w=slT1`44TLM?`cr)UR{1J?yr!$F94h;}jug{2@-dO;JS(?p<*Ivlj64#Y%*j({18J^++K$5kiv)ih
+xU(_xr^(ny36nlxnSAg&M?g|$$$0l)D<;Zlf+Ls6q6(CK{DiRMZ;@-Y(mY9tD21b9XfCXOO>YOGFFGf
+=Q#Icft%1^7Zh2(v^K5d(<KRZ`3+makxE0fZTVWnoAig$0WMKa12tve-o@fmi}FNlPphGGj<6#uOHeD
+>qo}fg%=G!%9sv*O<9h@H4`PVaXUR8N-sXhCFLA(k9e_W{W8W`6pnxL)LNE1NEsBfY#JeI^2Vs+*5Ay
+xpI^HQ1Kz_$Dr_%10U`yFL^)~g9wyShl9dPjwInoOdetjasbC;#K!>~2XH)TK;aMo;{c2UFkVB3km5j
+!0~`)iIHAI^Pq?6P00Hl?L^W2rM(nlQ5=%{0Q|vWjuMvBV*lQm^)_w@esiP*Y0~$ftHHBRx?3%)^L+F
+GFb)XYsGCBonU{F)?)Cpz-26a*sO956h3@~iOnkAsH#W2iC9s8{2A$4L<vce%&!%G6LMsn2+maT5!MT
+3wDzZzLpQ&!ai9m1~xehu(zaOgCia<2hm4f3QRkQ(Gh6NI1`CRmyfQgnVa*v1V3(g2W#0BHb713(%e(
+Ey1C>Chk%8j7buJPiVA5J*FTG}!nJou>`X&<45D2$~SN(NJtHwnvNDTEx~OwidCq6jh6;T3Y%R!L<mk
+rQq5y0!^r+=4vUp7QwX?oB)nuuEqtOZUU#9z||t?JQdg%BJn{(Vnv_~loYXJM1uJRgD}!WaH#VD6nY$
+f2|y?~^f)HO0WE5>fumCZPSM1mG=S4ov~aHTV$iXpM!Gq5kOrL}fhM5ifC8@zz@n48i<OTZ?!zv@&C}
+tw>KN1!NXIdKS3|Z23oGx*qfOB!LZ?Bei_uY+uji>Xl{5fNKsjiusHb*-q%|m5KGXsa0|5pC3<MYmX(
+04LBkI5|mP~#KMWZ<=ri~&{2t_0mHJ}|jF<gux8<Rzb85xbM%`7RS4!q|Lbs$!|=+u}--F=e6?}nliP
+!Xge5Jg8(bnppxpw|K~!5}68tDq5qk<No_SanGSbubdpNPv-mMu1iaWV(=pHlPZgE+(NELhAC9v;{@T
+UE_gb0JVwyfNUF}(_*A;sDlwfVjVO)jCYDPM^?<;mO8=(RXcL-%|p<bIt;X-P7B(hqui90pF-1jRVqm
+ufhJ(78pU^gq7hgDIxIv-<*=kRbsEqP9S*Us1_cYL>dX0MC%~Gpjw_qIR-HEH3V}}kUB8r-wJn$Nz25
+UR^M1Yjk1LaP{TpBXzgNK+{)g1RpIFoa<S3vHPQ#DYu`PUx1QoTIY_E20K*!pZ%2fdIn`_gZ3X@DCGE
+-sxwxaS#oftz_*NMNlLWF32SQY;E60iXs3%$eI?(b5_eOe*5Q(}EmJ#|<qeXK0nu5@;b8xJ(tE^(s2c
+X0zBYVrXov0v#>b%MHMt2+5-dGXKk;$!8-uPY)B%LkOsdxe884}L{q@WT0Kad*EI8)vx1e{ztO!e1Zi
+MT>V;M{0!0<qubn(v?TIkpEJC_CM*_zq@Db>g?*}boT1>{Po3~KmN<_K7aA2-`{*jukD&8?|bLZt8U$
+kp;{~lv6-&xccL-h5z}{qcVaahn{qSx?((X|+raQN{CP5dQ>eZcy}4Pe7Q@ZWv=__nR2x#h_ixneu_N
+Dbwwv|6tc6zn7wd+jBzl9_ch8BY!?5|Pe^Ty#Bi>8ji}gdj6b0{Z^Yxty=HnVtHXTOka@9U5KL%E<H+
+Pi~t^K0)je48*^aS6?HC>8-dsq(wt4b3{*zNbT61ek2Oymk2GM`W)bhb5!vp30;C`rvH$BvuWgKf*e-
+pHN{Pr`_Vdt&T(82iXtANEI%`;hbIR-ydk&o-Nzwb(R5fw!n11~DeJdK*u)zn6;0>)Fn0lc&~Dy}46*
+=l#GpL;qnh+)gKtO6Fp+_X=A7C#Q4Om`;ojz0>fOdKX)Zw~LjdOgOMGIo<z%PrL7s$M5l#vs*7@q_fk
+b$s=9eOJWnSdK)yk9ftdh*RMZ(_;CKgKVNU&zMi|T`x>si{uRNfw-)Mcf;3=f`gJiKykr{RV5l&M_4w
+E|Rg=v2m%3cy(EGH7{snf&4KUhmWlTpB=E*&`2PR$(PtMLJ3NvNv@OJ=vfSZjy!riP_1DDFYeJA5u4b
+n<~D?XY+h)D{llSO`*S4YYh^&-mAucEflQGy3(n63OgAWxnEy8+`Zrov*QUKvl}TC;voFPG(wm``T2j
+{@|qcpUb@`R<iW==@`t9K{3EN<^G3JYG0|k@>xG<b8golH9%~{}jk@a2-cWb^k)|<j9Ym-@WKJf`?_q
+dAC@#e7T(N**ls)(g*d{P&xnomtQ#d%GAeqr>P>;>qfPrZ-p!<4A(5^l-@V)<GK?Sb@pCYZcQ<JYCj!
+Z4C)l#vZ!E~PF_xC>USRQvpA6cUY_)@@BEjV;8U{Yxu~QYr_Up$96h?p$db()dm`DxY-^Rbe^RE}`o}
+GL>}z3%ybC*ahu`TO|MF`5p_DPbvm|%FEW?zMkD+NMGLSwC-4VYIFN;H)hI4WN(2k5^Y%Po}eBswHJ@
+SU!Y(cHRwY1xtciY<kJ&eC;pFRo2r|;J>iT9CzwP(tv_EX+bqQl3X%f$RDV!zzmuM-Y%*cQg`aju_9o
+g@16D-q@49(4LgJ66Spr+!)S?B3n}*^bJrZCC9x+fH)r-+|+gR#A-VB~m8G)8}Ee<vD(<cXYX|&s*Y0
+S78^M$#<W*;rHs-IO?~6yU09&yL})4Sf7I9P7u~B_0y@tDh|IKTG8(am4rI<@Oz)x$^QaSO9KQH0000
+80P~d=N1bYj3DX_`0I_=j03!eZ0B~t=FJE?LZe(wAFKBdaY&C3YVlQcKaCLNZbaG{3Z7y(m?LBLA+sK
+jM^($bgwnWAxmy+Kq%PH%XxXx?4{J7*w)~P^pNMQv6SOC;xll|}4-Sa@t0~GDu)Lqqum3YO$Oixcwzo
+!SBSH&{AxR|e+RV6MiqI6jnRTCwXy2w_IxRBom(JZ}A>$J!R(JD`;#Y|jeX(Os6tGDI|qD;%t#kI(1M
+K!9^TMefXx6MU!SBhY-(XzM}weEY_H08xKOGVynZK*b#X<F-VmDrMSM$;m%n<Q_{;FI^KgQzZ(sW`e!
+s$>ed)q^Ohr)m0DH0;}#vJ}-csYMqgk;9~EzJLKAMVVB!IJupQvVp~`A-d<PNn!c2F)eiKr>26@Z{BZ
+hHPzkzRs<iX$KhQgYUm&cZuEv)PpYb@WOwyEI@ko(YCad0n7w&_h->NsIn9!~b~^Wp$<Jci^konHl+b
+BwYdd;Z<U_U1(KV0`mR?82JZfn*FREx&izv;b=2ApuC5A+*YRlOJS_`ZM(Ee5AwP<>0z&FbgQCak2+L
+O3X0Pz7rbU>W<xA;DO4@5p*gENfq-2e>)07*F4uk%GF4gv2g(IChmG5*PCn#8Z-K{Sb9#sCD^KEF<~b
+atVIEH5tV6>Q@oX#~KF&%Un4<MHm0{_~vvJ*0n6sA=~U|9y5!UtYkM)8pgu5dU+6UrtX?=-<<?Ul0m?
+*MG^jJ=3;l+8&v<N2cwuX?twio|v{LrtPU|durOAnYL%9?YU`tZrWa$wil-DwP|~8+73<Ip=moZZAYf
+<*t8v+wiDBKV%knk+o^8b9qTsw>-0Q^t(r>(FsfH2N?U!A6}f1)tCn)L9en{gfhQ5D!YYaCMwBQ^X+B
+FU5I>NuD9LBhv=X?-?eOR(Z7vlEWQWUY1cC`#rv{Y(QkT{ijGJPW&7h^JirH!^0A`eCnOGzlgMk9A^f
+sUrm08jNS1;=*2f%6_2)$lSFC$!z8rxzuq867;D`d_}fHGZ{X(oDW+4!N+H|stEAoO<PuU}Wk&&R;S@
+LwQ&ywmS*iL4g;zZ(w_x4g(NCd=|dcf&qqk>*l%Q9Q@DBqf7L{&9+b?CF+0-9i#F*kD<*D~)-qp*+?t
+Pjm~3&t~AB>M>6>lxMo-nQnQ$Id;$Wm=_w#3*GWsx4hmQyF)$ZP(wM=El0ZLcysKI^_UY4<wUof>Xy^
+Zv6DhQ(0`qx0w%qFQLgf7vr15|JeE=__=Qx#$N2Bwn7-_h@_qE0zC0e|zfTV7%TxODOn!NGavt~5${r
+<CFr8>sreGoJFF9zFUVp3o@=+{KZejL+{05f6w&36xh-7J7@Hmd;W0YV7TT`bRbVbrcvx0>DGPxGf#B
+>8i1ok_vqT(jsA}F6P_46ntpC}Vc@W{jrY+jUr_NW_j1uj?lRgFpn?LVNlT9S)IlM$ewL6C5YWs=_^D
+(F&yPRp`r0%|TRZX!@*ps3OYCwkw@|MaK5zFvsLWhUm}n*x~T;-XiJY(7wHVm=Etk7O$fE&X9XRI53N
+&d%)zWl^VuGdvpZ0wbKGuA`Mj`D!^4)c}?{2Sw1L(}*l1RJhwzpuQNJ?{Z5K3OX|_3TOM)v}fb<^L>j
+`5zxbfC=+>)#`m|JC-(T=c0H8<|2@ZUEvmH;SBh%T&hUlZ65u)_%$Ex0#Y8a{xhTDMV!3`;9)i}5skP
+jjOOOjfny)oI??*4yaC?;s<nSOg#_E~kns>W5E!F#T-mV(<f2LMF!L_h;gT*F0*Uo4oridsBf^d|SNj
+Rpf$N*5!-OA{|TTdSiyQ7NS9SjTuUOeyjzY{fW89N7_Li`}rgdW_An#}|bmupC?W$z3zMpxMJ+uJpqb
+Fd8G@W#xNCW#KXD353xoz*e;qXc~KeAdH}{V;>zo^-Phk)9}TgHWr)MrQ?#aN$){VwsT2(*Xr_XdhqO
+Do8HmO+FL1kt^9_x7&LyhMgKfN+AdOGRTiEnh!vfZI)5$!RhykNqmc!m>sjdQ~b-2P=cvH>D&eTQf>l
+=){lnPs`niciX>q_7aw6)$T)L|UT05hF!TmCc^`7f=N2SivV27DTgW$}=7Br+=V#k(@j8C0Yb_-l%9%
+ylHOucidc@kc;Q{Zv72piRvE%lsxBdGa2zz?Y9@g(~xdJ<h;}Losz3Lj{Xq4K&e*Wq0o4i~#TK5#IYA
+OaM!E^8q0vxc7n78;zG|9o|Y6aiX|C))0zA5Pjyi;&iF!wPP1`f+tjsE)i^M{Y3E(?h2nM&A>{#I01Q
+BoBvkh~9f%_Z#fWVML4_b~k3m)2_BkEU5si<*3?X_4nLrW-LhINAP>Kf>60R1%y~Q7wK<!SNCVt0gix
+k22Cbh)xq=+bzkzu!zf~PMW4ND~NIWm|I9T)?4vNAv<WZdi^b5HrB$h#<sLoBP>&`w+X#NG~2ja4!<N
+Rgw~${7G=76@lldvPjOpj=`?M!I}GA55;m|frUoxo$HLMrjeT}P-cd;TEEHNb8+VxINp-i5^BB_^e(M
+__zhiFK*Z)uz*Xc~u_Hxh-Cih(DoT8>6wnC_%iz|_*zk>PHzCO@aPTC|_0BV{*xWk9HlvAby=ESg}g1
+^fwnZg$ph);_OQxT*VhPC)*1tSqT7zRvOM5RV|T4bxG%z0#KE}{n0WdKLvWSsvCkl|#NHAm<Pe*&z5p
+LWN4kH_7yf!v*TQvlW*=#WBiucgaT3Zr)CyVS**CIL9K!&ktD)e_+S3IUNKH)UjHZz}}A{OIHkBzKuT
+$<hg%N_GZn=NJnK;7Oo#63xMa59Kz4_AjPMMooFL6iD$zu`2_F!CFCa7xXn-1<VCQaQBugh}K{VJyeE
+*m^PXw+*TrG^zcy>y@BAN6w`E`irGMpVG<-GI9T0mz}SBQT%3uT^28YN9lrkwk<JptV<v#hDhjB<bH?
+OSQd1f=0z4DuzEZ5o#L>tJn9{dJaYckpqL(=AmF|x!vytY3X^k8+TQubsX9&w0p|;p0iKjo?(I-T{&6
+^Q5D5g#_RF8KcI%0YS%s~M-bauwKX)u90t46K`G@@L<7!cK2Ayaz<hBljzC%Xue2bh|yGU+~aH{a=eo
+!dLJlF%Q;0u-d%pX>vA9C4fFjQbd!?Cx)l0zI7C&0mf_J^mK7Sgu9o0MDEx;5lUhTg+Y0?0Zn$`%V-+
+mrUW@vyeUnq<epUKy62CA^m^${`~TR*R}6=q$M+d;H}ET+<@v3z&$slOhNjl(^X}MJn-->Jp%`G%p7t
+<i4!d|2T?CxHS^)~7<_{%1OXsau@hclWc10TTd<`7m(8({_K-?oF162NbZl_qON9=mO%6a%1MSKPj02
+CDw`n~TdfU&=IrU_A2kF<tjm$t<_7wp^E0vYFPK#Bol3Y#z2@l_`mYSmDAXI7NYJEHg7PgVbPUhR38G
+;iW6<KY2&)d<l05wl8%+bf_?J3z%c3@D0+XGB-Ep+;6f^KJ0rNUW*3G@wbYjt3@h;M4Aas43A8fs;`B
+5Aw0LEOyfMc-ufV#XZX(QBo5El6C)yH4*8_TO>xD{TVNw&MZiqI>GIS)b{3rbOl<TW>{2dWxblb;S_T
+%-^!Ld6a8@qh`=PtGwy$e9d<T(bz!Z*+QBK2br7!=i7%0pc#Q2iY*_Cc9Loijzae&XfODvgJ`(xr85b
+QBkA!S`277)=U2Pw?E%s0!>Grne;Dn0Nt_<1!@z;W)RDC>Hiv&CNHxqwAV9ABT82T-R_J_~_+1IRN?>
+s=HG?$`Y*yeZ%wGiQ4OpI>y~P?ecL3vvk`Z{`*gEkfkS%T~SKyzo1nyySoupYZ$;2p<i?yhK^8OS}Oi
+q7*TLQzuGntJ#gpuY>$Q(&#HZ!N_S?l@H^AeypYKW6KKY9B6)q$h_ZQkGsU6g7H6?%O?PoroFi`cTI&
+CaW_Ce|&ftmkK}mvf+CgXG^}|2+MGknOy+#9wL$vt&wB*jen>8p4+NaWOZRlHJ@;$}!MfHw;2nsFP_C
+m}~C;02?0647Z+#%tV9`xYtHbDvW`nWLg!qXW%sp`u5Q#FvlujOCQSgF&}Y{+11_13rChl0B-2<Hz6Y
+ph!&OwIDOa$v(RD)|94Nm7@x@`kfpbE+B<sXy7#D4F;QJqGPzEN5w6gwPOwwA<73n&<6HD!DVa-;Ug9
+HkWqncXri7l(8=}Zf&-bx|w_!nNf775qod7u0{wTeFU1Gp8Do)3D1q5?n9**II;y45TYa|0IgV5-#OD
+7L?L>ds3P9PowQq5=JkvnKIs%QYvx%!%;^Zu3l?HxT$zp(gPJnRW@dvfBcHO!mZR>}zmm4c(u?H<uV5
+|XS+@`dQp36oddF+!Il$uc>$dq~DSew|2;6^x-N?TGv9klPC*2XTL~6lhe-l%jjw*PE(<m`MeKDh<O%
+{8zDxFss|r@G32>oUC5<GHIs7EgcKSf}Ng&k>-AY>B|`AL(+U@X9ElclyuXqqeWG$%3j|Vae1IEJJd~
+5HNC#W^dDfen>$pj0%-yN)|XU(gswecoa8y?f+#&*N7%u|T&7*p`jghpACgqA@(iZmrvvY3?>u14TTP
+g-bV>=sANZ7j9lijf!g6$;hfVjnIMPH3%<KV6=Pf+gt?ud}A7}h*%AVdh$u}fdyj`9{x7+&aK<i<uCo
+}C*8K0h>29Bf6ZZnAn*LpWV_U}PId}<aHgm0b35@J<6qdlj#0Jtd0X%PoBndd1>=iX#T?Zmn3HLprc&
+`rRN1q<DppnRIGREGSH`P{%DJ1wvsw36U{j_gL2pEbY|oyr6Nly=o=Ny1|%&37<idRfe9i>Z*0LJx}h
+)21~|?IgCV&h!(OCBl4EgjAsai6)1R3sC3Kgq8SZm4cNH89!5y%%_yWqgB6j)(h<iJIhoh%l{$nSiQ2
+LE++YBVzOFXT=<Mcg*Hg(c8aj*$s_F^s=X<erR?rKcaJw0bPTUg5zpeUUvGaH@7>1H50M%cn9X;M(0S
+8~48<X8n*aWxM#%L2_fXNTP5Yrfe`o-6+kEZhz+|}q>J-<_Jk`6Ofq4_apJWH<T%i*4)JAuyQ5{h)78
+wjY&)%ceG9120G~N&`FJ%Q;gF)v&F@1EC)cFp|%{0L?mK%XSLsYEV!Zg1Ca2uC|Ihmpi>2)s(d9Xh6)
+Sn4xhN}%!9;h+2s3TNozJ}MUm2SmlkqH$(o0TiyN`Rc-5m5p#q<*6GVuDvMsOm4BA*l#XXx)g{hD&u$
+=VdJH(rq7}2P`Lk0)d_g+M^Ivi9#)PWI4c0CR-^PW0A`lN*5KH2>eEMikV0T&&6%yWMDU6Xjt^`Peo9
+Y1hJ%SM0n+`*fN!bg$<@%vI9#WtUy*9W&kbc^?jb*IVmBKJfKH$BTt?1JTG4qO@pB^h!NIh4v}VPpct
+SDl_%7zC6<FOU<;sSj<G1(zWJ)6obD6}4L}JcJ8h9v-KY5>DLn9slhlqPXat3G0WM&s8q@-n$nNMwEp
+2wtt}hBOl_`W|@@Oppi6ZG6EYct^7ON3p7n5sBYEM#m#)6MkFgLI0XuHYo0>BhDv`+HDj1nIOdVeYg(
+a-1rfL=v*Blu<;+N}bQpx_zD(8Nfy4wp^GbA;8yP;%MtPC=emP}Jegxtlh0$Kgj6m93E|Dut9eF;)oS
+7gL1GBmV!?MRXQEd{1SQg8c6)iGNo~bRDpLOl~4|;5(E%rld;lK`rYQXu<Nv9O-^MMV7f&!3{`AbFc$
+wykby-M3hwu6tS~URdTcWxpP=aj9j^~;QEO$`)Hi*qP_#3>1p!ytz&YwOf&2aqi_58ojE9`B1$3{I<n
+}YX0U(y0*Bmqvw`M9n1mACL~8~4YDx8y#2N?Dlij@yk&t)HKv!TPBIg(RKq->&WJmHLRcnnyG*J_g#}
+pML)ocY?-sgCBZO@=iG1#8OHz1|OO+DP*d$JeX#{h^sqtTqE)1`q?4C*zn=?ZNIUUe2*^@bRBXCT(xd
++B*hffeQ|f(d0J9sW^EK(usgFIWcHF5(D1DZoWkWQ-hn*u1L3s@afNsz=~Jp`WU%+1+*Zd$w$Ba--^=
+&yGqx@AU7JQnrwP7bI+6kf5%O`^wNu)u<zCc|l12P4}93RTX7rx@NL(q1CSX>?nY6m|t$=hE59RB`U0
+<Y0VqhPWJe-u|nilQIJ<+-3?l8G_yeY5;==G&>|?E(3!>PU^VC9pJ7CF&}w@&u?$QrAtM`_N*D7PWVs
+rb7kkaz3{pssJB84wYsjn`ve{b{UXdm__IJ_`_VOU`Xkh+p!58?K6*?@0wA!43DjdjCEF8EOS2b$?+a
+?ZjZf7HYNX*GapNSE8DfsGwB$Fh-(<S-FgfIHRv3lSqYdT({&^aGW@q6ZzhoQB7JxG5HxC0+jAeAKZd
+US-WI*n$*3|l*~Ifc~9eq`>@xkf#l*-^0_EB2Rw^%be$AeAr)-NUm`^in|%)$BVi;kgVc<{+4zJxwpV
+j1izM6E4#iGzz#RXFK}Yj=zHraY^fropX{6IIiR#0I41H4$vM%^CZn`a;i|-DL2X2Fai1e4w>{-jOCS
+a|K-zaQa5R{YUDL%JWT+lfv&81$x4fWZh}F0=Q&R6V)*>|lNZC?c;9jKe%lJIX@u!4kO>T#7_d$S$@-
+kRFwQ+=*52J{sbn2O<qP4Mwp`rk7XS9G?{)2-V=C4+F$|1$-zW;<;5j{be;|b{>D_&b$NloJ;Y6@6nT
+Q?V!=6x81b5C5Vx-EhB$H(=9O^+}{p0ido#>#m8Z1`sDhppLhn8b~FD`6FK@^A*bVtd3kx(x-a3tr<q
+%rw{!Z3L@)Ui~)>wVX^Lf6GF34Gl&Vp%o>TlT{wFFGnoqYgOnz(A$al|u*$re#@ngG#x}2&Y=6xyD?3
+SxQ<cy-pr80rfpDiu-PmHfjVi5qG2~#qv0vVi@@kM@Lck0&`t_`F#3(KiVPw+!?Itwfpqq?TPA19KSR
+0dmOSJ6R{TFqrDd1qrDJ3=yAw;06YWkH?AT7QpoiCBF%Gk0!>kaIi~;te(YQ%R!OR`)qdP}6%D_GM<b
+0{=H7F4$HINRnKIg!=>dKk&W#FFsA|hbbP_?m&h{Uj_oH?ipv>?&i%kV4rXN|VrD%<VdouY>-;t3#jK
+Ci4=@SV2w)?b8*ZZisXgzXofDmr>&6TzIZ9t4YB*u0KRW{|GWkE%wPU10~(Urz-T!W7GO;XF8$)J@j1
+_%@LX`_?g0fFyGwn;T<WxEMaEc){!KI3w9#C#NhgsQCN%i>smRV71V!zXw2fQL}Q$a`~V7O_|-CLP|Z
+e8Cw^pjY#O8a#jU9y`N$L_$OsF`h#|=-Par9T&b4@7^$V=AsdiuYlmMH~?D9D-Xjgy)vm4d7&0xh*lR
+<OxiLwMP|89D!S+*Uw$QvSEMRa2fA(fd`-vO(SlxtMfEQ^Q#e?kw1Bzj7#|IiwxuwT;E|=BX^xPmBw#
+E}vKKs0E2H4=JN59kU0*QA)=>bYb{YGf$20ECb~SZ`7bmQ-uzEE%@cIpC-Lh7A+xVSJuV&kCc*suaFg
+JqrWBP>=?%PMmiTTtN<r`gcWczky^wK@3buMRz)^S$umxu;?dkZed#b7)7aJNTwmPr!=Gcr`*cRJJ!t
+NSVQzd;0VZ%X?AkZs9&I)OU>VBaRv!0ej>&P3n0Iv-n2I)l%`!RKFqMG%flZ_X9o26w(4r@i{d7g-f0
+D2TfrTibvWzHcr6NFL6k-drQP^3+?;V!scHZSk}P=3v66Ve?}}B`f}@4?-`Vv~BJuEW#v5g1^*D58#H
+?h>Sth{>TCwjU+|-WXX}^hx#6~fhH4FC3`!K!vj+_UCZ_SDVC9t6Q=6$0tJ6QyLw&p4IJx18F*b#7>v
+g@dJo(()H<YjJih!RNb-M<98`<dzDUY{T@tVuJFC`}5$$=T8=IPg7wtLNQ4v;XkpIi?A;>a|*)^JX+u
+Kl%Fv5tEx%4+$e_58kNAKMrm8ytkiH5)ebPHsXIU#KKj<d@jYES89KSUw+Qnzb;YlDbTqp#(@6ANFda
+~;TYv`XcG!FIQ-x^6e@=hd?OTx_sergYBVGgB&>M2qwq>n&9mJDs3!JgA6e?GDl1Y1$?guFzOcX7I>9
+*j|pKWzXprMSyaA<SXy`>UQ+^zyEz-9xTECRtu~x!}9Mpm;~9u^<&LQo=$;#Zjw7$I(<v^5F=MYUGed
+6%LavP?C<Oa-`XBu0_P$s;t$z6>l#78A_gq58yl#&$9*BqPvG?GAPM=T%_juPrYN0Y9gD3iaB?ocKvx
+wM3}wa}#R;fSg?c~;N;bEFWFudv6uTD^l@!_`PvDJmERM2u91q+awJjr^;}i@rvO4UPMdscle)(WHj8
+zQ^ggUcK92H9{>=}4nkx+JDiedeZKia*3HR^PdiIaTxIavhyKd}3g!V7f!^Jd+`lYc_xh10iW#SJ2v)
+^82;?>k-zQ6i&Bx_g&j5W<xJfmHV)0FX^`U%0dsl163b3BUyEbn(-9(&!^7Cl4GN=}-4TmOA{gWNy=G
+!*^)9=TsRi1|;xOJEsPj`P6=X$Ku>TYKPoR`AQ1e$8s$>Y}yuOgeLKD_=;chX_>w&yrALSZ&prX$P;$
+;Jixe7SIS7#XzaXD_0bco<sdLvSxgnXNWW-NxOMm;70Yj{$(y8hO&4bFa!SnABB?-CkU!LbdIM9#{F}
+?9{!k^0W#a8R9)AKRCytiVkGpSrVA7Xg_aUQhrf%IL?^OpD92}q>9Z-m`_4uAwAWuCzuY81vho5lOu>
+z-FygG^T{dhw<V2s1E!|6vOchyKNi-g4u@tt&WaHy==QKPB&!l8I)O*{X%ro;i#InKJp?LMT$gGC660
+z}h61#_m$xJ6MT*q=@{tW!r1Lg(?m9Q$3gp`m{mMdSu|P((fbpp&~t@;kDz@^fteKvAn7UUrDS)(j(Z
+kUH!{Iu8KYh=AX{Pioq4*ef-{NW}V{`1!Wo)t|My%s*iT;eLEADba9DjO(<l$>lh14tAEeOuJx8FU*B
+#Ql6vWn}|DRqVl+|iS#UZBY+&gb~g8g8r+U{nNaW7S6oA)0&=n+(yfz>l&G-~mo()4%Nomckr#AtJay
+ltFh`C;OM|cOp{1z!ze(`q8EA_KPk63Er&-B#T2!j0*nz|xRo4tjn)n<;%Qt!Bz&&io0q%9jSLVUgMD
+T5P$p57nD=3{xt@Sc?sjQFIWn4U`a!C3$5p^{Qy(}yVikq8)9>h_SEs6?=zVtwqRe>+y(WkvHY2U0~U
+F7h^O~3aCYcGAr1wR}52+~#2ZbG*z+5lYeWfXk#*2uN(iCTWBHCyN)lb|G2`!Jw$A9^u?oi}M+EbEm~
+1I`6_q?@)Ux|LTS(J9E=5J>=2>_$DhXD`!`VTSB&^7}+T#@yJcQ?scl@Wk6I_<IHNU6C#pdp2j|F{b>
+*qd?I-Pa`^D+7bbz9ojd$SQ1<Rlc;SqtI>}c#c^Zqb&XpoJcwlqQO(*FEb$g{K&G389t_E+ftOcUd*v
+&nb#wP{<M4;2Ub&~j_;FvFt=HGb5pf5|z^`V7MH3<Evdn3FJQ{*}PcY!U(ll)Ay-D5Il@NuWSdB<rGQ
+GCdRehh?eEUH1f<5y(d$A#b)og7^OgBa^Iz-s(xH}HX+zv9sPV8PY*oN*S`?lsUtD+&UMHeZco_%pKf
+cAnc99WYz>XFy1<<FoJcJI~z#?SgaXxm6}bl~>bYyL)gz>k^a9-%O^J_*d)<>UImonI<Zn(S2^)VB9=
+=r=5PlVz@x`;g50>t45BEdeb<cd_gmzO=Nr?WcXKB3;X4-=SDHVWK}?v%1vQ#{qt%Y@G<)pT_qqaP%w
+f{GWYc?=WLU9u!MEZkU&}-Hwj%5SpUvOXXftZ7RKHo3bTNf44Z*dK_>Ob<QPFGc1h+JF(gA=o7H<{GA
+eBY<3VbNfLeMMiMxQ5OK+zrFFX$2;u)+N*90p?$vCwtI4dMcp{n`A@cU<kd!`rt*&a98evM1fdkWqBD
+WLrb_n860Fe9397H(OyJxZZDYBV!^k|O?SvHS<40;`>5N>F4#%A63=kybK-NxhFl)-lNB`0KOvZU?zl
+cV#1#pWayIJ-41|00TQd?d52-Xi9`oCaArOr?7<^xloN1`n(Df`0ZUa82DD(K1}+kI1ORAE`rnQw9En
+jlQML#Z*P0N{JFYbp$&r1ig-_9ZqA_VTZX`qhnY5w{+TDHEzX$#~Oi~J0rlI|3t4#H+8r8n0f`SRk`f
+XDsJ13(<CndPLipxGxQUY@=cv_`v#7_HqLH>!CZWz!f*?=3W{k&@W*8c&;J2XO9KQH000080P~d=N69
+J+`$iN10Q^J%03iSX0B~t=FJE?LZe(wAFKBdaY&C3YVlQ)HZfSIBdS!AhaCxO%dw1J5mjB<Mf}=DmDV
+CI^)15i%#K}ohPp4;-CMR~=nUx$ZL_!j3zCw_))bW1zy$^r{C6QEieOeJ9fqQ|A`}pA!%rZ+NZ$4iZd
+6CKa+*47SWVt66Ithzh&bdDF0<}?ECGp5BV&x}+oQEoxnF#gSndy_svn5j7M&U$@E&gBRD%6&y)V@e1
+65tZI)@nP6l8r2z6<HXn#XJ?6mY&d9r}7!MpXAA%jMX19vmO3^^Zwn9y=fZxNvv}b=OtC@b+bz)jQ0$
+l<}%LbIn?LbYU^FX;Gs{Hs!$HHe!qX_wf}wYYVgQ=FLHnF3C}R|keT;Vq-m&pk%K5-J$;GyUUiWgy#D
+yo9r3H_Sd9O8Gybn<<Nui4o<FTuhbNCL-g*97WWtAa^t6OUj{{G}u(&E-br0$S{(FH0pz9@+ongQz@F
+D!a5L*7_yW#M4=de8QVZTo+gA+(T^4@4&MAFOG5*AqJp3+_*a?#asG<nk1<eRPrphTmNHlTCAUVZxh!
+}Zk=l`<4slQLh6Smmh9@6}etgJki$^z-2bQS0@3p62JIL|(G={(+s&VO7^~x&HM>T1dFB;!wrX)A=q0
+OUm<1Ex@Gp1ZbQQG2#0%&BY>=a~aA=qP<*t)4u4ByuMg0GP?R%67M1^1_6jsQ!$gGeS**=SAHm|wN?Q
+aia;e;SkRAJ(Irg$yDvqOQxoP2Bh8bzgM%;PjiBLxKS60w5vC!DhOd|+x+}hh=CaC?BJCocL=gxQd!W
+v&GF6sCrE@Ct9U1Z^7vSxvxa=BvphTF^<O7xQWkVMxMbst9Ty9`KW1SS4Pm@?G83tex9SoLg<;VnT;N
+Gc}Bt|+*a4CGvh|yY#fS;_NcMxBHOU?dFzu(dCztitu=$D8n`iW?cDo%@>3%Y~!o!qS?jrndt(hbFe<
+r_+Pz_*At4LTAjU1VJFl_uB9Au2-9OIC$gkkn(j&3SeCc2eYKz)6}LU7@n-R7q-?vaHiASy_Aef<IK!
+BBDkbjM^-Z2J7>?R#ZnKq|t#cupBC-(5diw?V!XY!P@VnDJAi$i+Di>sd=X45~#39f*lozVGeR)!-uk
+pxCv@h2laeSEm6a$KqJjbz_e@y&1)m*{svq{(!XU76@gQW!n${gL@79Y%<G<I3H6Sa5YVWgng`X7NC~
+u`sWhiWiI`l%zZ;fil!C*P0T<VRCX%R)Cak(kF(bK9;hwIvNL>1o@gs80T*3utrVWqH+C=tRE>gsc6X
+EJ`fkzKo(q;^%ft_re#L`M#Z&t?wLaadE(kZ@8LYBIzzfW2$#5TIgM69jT@k5n<G>hn~%!G?TF4>QQK
+*<LJB22YxB8;AFyfnfsE8|#1dv|n3<j5zj!d*&i*LN~&95V(`k^Pv7KuLC&c!yOlyEAG6jvu1}Yn|4B
+p2mjyaiXACJmd13gu4}MG!`uj<SIg!dDiSE`V<q;+!J;$8=9uRR{-{`@v`CO#<;DoD0I2<4kD-k1FJv
+@2e3s|8XJUW?`RD!ybJpDD-(ubx-tc(QRE3C@lYC7gZeV@3q~DV)e%5|_Cok~=(O2ME<`9|R*l8Vw+g
+#hfpbnJAQVZ27fIG?ES5AitEg1f0Mudc7EElN^F6>a<V;kTBSzc-+FrKhDtZoEWm8#0ERgX^no4G`$#
+U7vU`?&KgIPI$E&V%obtP&-Jjw-W81THi$R`Yf@M;tc*Hie+h&M7aP^JIETUpr&<k!8#*4*&A3`<;=i
+j~v=oNe9MI~1i2SKwMi5q1PDk3gNE$XDVsjpJojnOLpt=!0Es*ud5aw`W`oIyqDh%fK=Ah!DScq$^zF
+Y-b?U3h=N&XN1$bWwe%7|JD#uOA~@hzyNCv&*_@UrM-f!5WBSrRvkP5M-eFoPXzNK1Gbem*bLl(6^n!
+AmU*ev9X#xiEa(ma&Dg*YChY9+>Vw7SwsOh>U^@+X#&o(j7qgm?*qRF(nuQtQkd4b<C*2$(#jpawM#}
+19gHoJWvB!yCi@@Bx&xFC6vIA*)3`nCQGLa;wFa|~ADbhsY#=)3FnrNdx=@)*S$uJS!a{xaXvA@SSl$
+2-~U|^8M;f~88Us5UZv5}musqTqD)6iH$CchLaV>`<vq4Ny&*l~e?r?kD2gXyAPgzV(CpRgV>-ex8sd
+~?%CszlJN2S!6Wcz|#@>RLdj3)nqoJ1(ZF-3EZuQ-Hd*T+UWq`#1#85`v+@T~+K(QYS`O`zm6dne|N&
+*vSYxI25cnn=%wbsgPXAlqI%F!h&^oQzF?-7KNSU?^VF&W0iXwg%@Pib&$UgI0z!xsg+~%aTxLOYV$y
+jcSE41tEwAj?Twg&Mw&OFIVKO`AuR(26-!M3g%+WqSJtf#FJ;{BW(mEPw6Jnt`t>qYRIVOpl&_V47aN
+5OI~_S~qUx9l@01`1E2IbPT@R{THk>vFu<bX-mB1!2GsaskMoZbOPz!PP0<Lc`i<XsI%Db{qZrDw+jd
+X0~p;am}k+Te74=n*k)B0BGYztc$<=ut#=Umfk3@9!1xtUdOFbUQUDqPS*cQ<xm8<^!9SL+Tp3J5cHd
+j_5%R~utEuytJk{_z`!(}#&Jls~o*L}=gaH%hiY8%-;3+|a<A2x1ENBY;uGYP(n@<nb1A#Sw8qlJvnY
+-rL-qNXzLoQFRchO2>BdR4OVJ#+k%jZE41YHmSI6A<TwLKQvHIdLYU$y<Ph*h?~hp!4#W4AUz!?sGj-
+GwjN_Vvc?Si(nxU-rE04!nI^1@RTtMej2+XYy6Y`k(ii<D&3j2BSv{v;O7hW0w%kgu%wF`DSaFtP4Q{
+**#JMOm$<!ZX5p2fWstP~QpYIqD8At#bB3&yAzjw`~0a$)Sj6(x~%5_@$bEw?_;AanL@FQ>7$8E^awu
+w~W^e8bR*Z*G5i0eic?IeG*z5?AR1Z6Ns5gPwV7q$y4XV{>%rFi>}N`VB8j<PJNw2>LgVhpHHwR*a$X
+)S7i3r(~fKj(6ltaw*K;nYaJi7p}@nQi17or1IdgzqZIDKoBH{=;1V$yY>*ZyCQg5#&Zc6ovw$#H%fs
+H@=qC?%lsZ!y=RBUORw;h>;twnK5i-%obXx2=Hrd3mYyZ>9~C>%x#=S;%scbh6uY_JFBw)#94%9(pgd
+^cJJgk5CCDbOzigVSsf~G;xhX!aL{e{d!k~eYlQE5Lj@P-W;wqOdS#r{I~3wOu*X7h>cT=`&Q6U*?62
+9E=Sef!+)W^s(3f<zAfH8f%@g&%YFje^^OD;sj)CjS-pKVhMPJh5Pzgg5EwC_cR(qr5{r4OT@pbw_g+
+@W@pn{P=GwrPt`L{Ij?Y9;50N`90l{PmrKTE=vxN;1@aF05iDN&2vhPWW6kvO1z!~+K1Tv&gM<r1PD#
+XnGO(XJg$d`A=iBT1q+aqtn4pG{-}P?^IT%$1%OnIhC=CunwdHqlk(BHh$NwPCJf<1(+*m{+edLRB`Q
++LT+Wdos5|+QWP_1rZ{lIW(nd9HO6bT461}$h65QwLz0=xX{cD$w_G+jeL7?@q`=;#Xo&lIgQ%<a9Z;
+)fk-%C+sTPA|IudV8(%oIGTna}(eUD-5|jKXGVaKVqMiQXI{_#Aa+tG6&t_LEr{!)ZZ=Ck=q`a^7yvk
+g&#QsoG!Q?AV?FdjVKh9W>IY~X<%WY#l6e(M8b?fU$W47;rFlBWP?mDrxOgx{!sOnNKYoM~~1*-$!Ve
+_{U<$Lik5hYeWiBmIZ|0+0s0w-rvFHyb5&Ni0<R@DGO+|pz)Q&BX&DXTM}mSeHBc|oc!Bv=S%4DE{}D
+pcVwQ~tUYqPRQ&{RuXIER3r&23(Je2+xpJH_{@e=+*h6y`nE^aJhv<Hql0S!e$bv7DzwI%B81Un2S4c
+;VleUs6d&hP9VNwvPqOL9o?`AvlEEluOSwV%V@}G;JEd}*5)4Hk+AI-Il*f2rJ%g@A_Nb>g~S$$6NUi
+h$8S-<3#`qR-ge`0oaBE0T#e%s5NXO&;y?ftIpex4{I%vh`pUr8RVMkJ#Gm-Mo-rAPM{PKgf(to;Nod
+~kK!i%n2>}@>PLb0K?h&2&*l|6JSoNH=dq`Q6PCm5zF=kU_Cbe&~*48RDP~#M{3e8LQTrx*`N88DTfN
+m->6G%#^N-DB8=w&A{j2=-I46%Emf)PP$TH+L&+uwmy0Do_zab!zzGW~LVhckm&;XC4xCnbY#Nz<1(a
+a`xsl{|rJ18dAN5)k;wkYdSs*;SI7L}eT^y<CPdN9KFEpv`+b&25xYKWJ)bFC{C8Nw*6{YMJR>Oe&s>
+u6JR5ty#=ws#&h-D$HpKd?|XwX*fqmSz-$D&eN1b30HnkrBhp}WmSRcrd4ebrPg~sg^;RB!2QV6#ey<
+)z$FqpFOW+-eV9X-$~^EPp4kU!Jeml1we-qUx;$o7WEr?8PmUc>JU=)TbY45KQlnMNn;t*haDL?d!}(
+s)wMO#*gEiu_gKm!VZgA_hE(a#uXx_DMu6-OcoBFW!)T2pr>?+&CsdT9Je4R((`G}8p12#dbUb!xDl6
+xxl-n@JF;kT<F=2!2o-e3KEb3MZIsa9t|!YTDkikx~sa!#u5Q+RW8^XaGWf4RB3o_N=YvkVod2Y5@5j
+*ADx#uIe#7cdJ1A3WqdLZ+$o)l$KY#m-Q$9Nu1E&p&?p@bT)?%}-a?M8SRZpI86<?Zc-Za0<Z3h|n|H
+o5|s5aO^PV`LA!@&40R{|MKZ4>Il+`V>?28iR$4K?XN*_x*DFL=Li6XcJtxwhj-U5f5X?b_U!RsImg8
+r^k4Gpk5^uh%wqxYbguJaxf~3=mru>3{;=%qUB2@EEzDmucEX3YUn5K5;fC(A_q*5#ZhHal!G~SrVEC
+f<>Mca|I}aSJ^Z;#o-#mZz?B!FjKa7{6sUbB3|5EX#<NX6I!{vyN`VTM8N?-a2l{$%{1P_9EyvfrZL{
+NY20p{fnq~%_qmP?5NH{6xooyV74nf9>OyDgijrP~S?3=b;|s&5V^VBUH#G-tS*2~*=~b^rFPnT11s+
+PlV``i)riZbvSq-c=m%-Il;oYagZgj(Y5k)v}-VFh||H#Sx&mC;MLe;(${oa|n6$*1TTyJ96e#KhiHl
+E!)ZAshm?O%CIiN%bLu4Z$Uv|{Jq;c#}opZLt=kGW-vtPtW?YTm>jLFjqJi*+a5;m9!iy3)VZKXLEjv
+J8yidqxB??@Bf=qBMOlU3UCTUWKDX10i}CZ@mew#Nq<gT;m2<nTFBXmjA67l;8MwoR1Qyi6IC+}nVJr
+N@K&0YA9y~<y_H&|uzv2NDGU!eJd-m+vv+?ZNch6?e|N7VQ>{}@4+w&ekms2n(|5?fd8rl?pZ->JP3I
+hDt0hLF<9?uSY<~R$gQ}u<7_UOx`YP8jp&S}-^6GMK`n}J7O1~8{y?{GAn^t2yOO>ALMqjlH~7Uk$-R
+nI0Ir|h!Tw4;O=#|hwHAVbfQ=Lw9!P#N1c6OCZ1`yX{pBK*v0fP^sp*8H^)syfUiY9@c`xDB;vLZ_q#
+eRoLx;ZcdzFxJ<Npxyi$GRVzl*lvOj<I2u5*vo`7{bV+PpXujEU}DqH!`t)Mv*GDh2KyTCYg!+-%Kcz
+9I3BHz=AA7Zt&Pda&zGVhyoPg^A_LXxe=eszdGa+=aQ=7_F1Oo#n1FxP+Si)y&)S?nYgtP}gxld#vRE
+vRg00A)-lQ(sS`fj_<g|=Fw+F$9Q4o=vs9Z*ffT|%M51j`Q{iNvG9M&xWI)c2MY4lz`eXL*gyvKEra|
+AosPR9duzdyk=$6(s`9_!2AW8H&-H@Gu9B=EjNj@fW$QwDoWvqRRsmyfj>WnH^R%-@AN`#9wot(N)P*
+-ln6AM|hu-i8T&*w8x$VI8`AAOkQ4?4^58U-b@wtOe0u2barUFo(Bg1nqTCz3f&4eH8`ApQ5oHm&<XT
+tyvH2D;bY7{zs)^V@R$dK7X58`~0}>Q)im|Db$DX(;t%GMTcovxVxN{C}86vb63<DbAUL<!d|cUc`&^
+gy?FF^GSlVPw12vNnB9+O_YeK)OwaCb&-)KOQ|dj;2GdzELhv*j4xfAjBo1fih{pMJ`hYvS%fWPvbfI
+)H{e1D__Q~J{lpnq!+Fg{(+xTWEM)y-K4O$iEL&*6Q1YCjhQpEv?eb6&}QHU2b3}QZPU}=U<ln#C9Q8
+)@+kiA8Rhk?G989G7~r!)~M9$g<`Lol5F1;oa4Ga;zH=<jckELWX|Dj)R6{o(Z4tviP4{^u~}Ll?@Cu
+TIOz?)~-c$>ZC@S&(nzR2Filk(<FCiE2h}wnGgcHN5q}2s~>Y@}ABO9!){T#dvvp{*bK}U{=BT<JY5E
+@C1He(`7b%{V)su^=$O*|I8+@O=;jq4-)>{KB)L1iXKw`A<ZAs?Ze}T(ZdWsKSzBv<7${2{w*>o?hc+
+F-`e8e|4$E^w6C5m{J(48z9$4v#1W4OG<?QBsQn8Kqa)wA8~LF#ozE3Kna@i{3i69a<-bdC`EwEjz@W
+X%dzp*No2<ZpQ$kDh<$=uk#-V6QGQQCAiW3!hqV@7`0@h(vJ$nOj)O|o~GU8ct=5O)bqx7(gM~a3SO!
+H}x#(%WpUUFWKza*|0c$gr=e|q6w>>PSGYo$H<7dP;e{veS$?&q+AGV)X)ML3yE%GNbuPCCTpNXivbr
+_mpi))6_C65`A7%w0wZt7cq|?9f3__(T;wn99*E_{UxL(W;a4IoJRkNu%K34nLna0sMafP)h>@6aWAK
+2mtey7DrMibL<Np004n30RSNY003}la4%nWWo~3|axZ9fZEQ7cX<{#QZ);_4X?kUHE^vA6U3+ueHn#s
+ip8}<4YH4e!PItGDv`#09?P+G4G~2j$v)A?b(j+9Yrbv|_?Wk?O`#lFBNRR|YQleflIh{t9fCJz>e&^
+r-1V({BCD+%ZD2xJneNCL{%nw3h-Z0;dLV7L!J|ebr=P<|jj!5J=mT%K**9mE0y6n-TFql7iMBuR|0X
+g9X;KKnKx#lhX`U&y;-_1w#y+vmsd2LSVSrGVvs4+7c5Ex-6EZ=ph6+#<K_QIyWM>PI<Xu%Jm>4nL!Y
+-U>Y#l#Fu3wpxJicv3gLWh?AZZgYpexxDNI1+#TGMmxhiy!@a8kE(tov{-_`3Zf08h<?d>Ab9T=-*QB
+Vm_l#EHuX#$)9Bv0y=YHbW`ZOGQzmD<ISRwg#k6EviuJhuYP=qU%%yF%aUQ>P+8;xK0kUSxq*Lytni<
+n{I37#(Id+RiEwhflg=MB==nFlfnfSVVuoRmi;2UCC>er?;j<Y~;*Z2Qoj)9J%t;I%@hX2KjJl(t6$J
+q_#p&|z3HiZyZJ;rsq9%MYLxQwLQFl&mC=#0mzC{_Mc9tUWETkDyCU9;<p{|7_Iprx0C%%n;-2gAVJ7
+~xr4u|C3yLUNaoO6QO8{a``K&2dKiD1T1@<NcX=?IL-@tpAbx`)(`h#+?a>Ic>WV>o&3d-MntK4A0}4
+b78_AcDUm##Pv+q4*+hUwK?qL(63J%;oxk78F-betH&$f$}|XB;YNA)Dn`)FhTUWf%WnoR{E-Tv!5j=
+ngVj-L)zE@c|(JdAAn&)2f@0%J5XV??(ee{bmIehy01$r%F(N}<acU^-qJ9dah*U(Ii@T16$YTHQcRy
+R%bd}3*Bk@kb3nR$`z8y|{U|`jFeq|)mFF~&gzxzuwi%jl%i2ijE1kW7ZpLO18;39j3#1fMJ4;42Eg*
+)1PYXl<X1$C>AMslKVWP7Aj2Z*h#A+Pde8%qKzxxT8m@qDL>{w)C0$_S<nD+lal-NE2=78ozrVEA~n=
+lR`oCJO}o^S%3Nnp30jZ{5yPl4JU1w#%MoFHh40f;Z)CFTxwZ3Q;&C*W~F13^of+E4_*=JIkLeIMp1!
+Z-r-FDyh@QHGgiEwA+Nh(;(S?vQ$ySAA8hLKDz6ZFNZgg98C?Aeu0(3AI&?xOZGv4koKs2cVeH9n(fE
+MN{T{!T!6xKNA%}I8(<1I%%I__%2mi=qX|={EkLGr09X!lLFWX#{Ox9;G2vmircz0-k^PR&uv^C5d-x
+ZN)(9>Xa1~r-2YJ4gOs0T&F6|CL6?p~RY}obMpODan%>ah4Bgi}hFypKK;LK8mTT=O@_1|!GoBIZ0AB
+<0%V+23=cmLEIGZi$Ysn2@2#5h)QYs&tTFw+;14@J6H|3w;tOLrx{!DCPNM1rv;W8g$1hDP^ypr*)L7
+WkZOQnK)IfB?Il;0Ny&aKMsRf^+7A2m(7wu~w#e}D}FG}@@cJX}eB2u#~~4_`k%PAy|3*q9YiN+F4PG
+QTH;zD!$1eu8~iw*np&MSmeAh*YzjXCer|fWiT3nckSj>N7QO(Wn^&ssI8CE!+1FLo#uo7Pq^U1(t;0
+SVggwUO#6z7B(bDhX!XMJ5ew|E6amqJW@7RKl{7Bz3jNb4~7E!Xo!Zc3_aFs9<9Jw993Dc*faJL^yCD
+8=o!ZFH;7VtNuT;hLI&CPpfMB1r!+FANqh;Q3uZukGzF&0KWz+!pbJKejDdb>OI{|#t}=C&Rrdsaim{
+UBF#t5_*mMUtX_;YIH-M;Q-~pQh#~Se1cW!9nE*&jw_YS%Oz5CNhh6R5mpFey^HG;ap$zJdH{pr`o$K
+<;cg{$xfd=1dk2QbOC-Aju-g;I}?&#_!z%W&h@2=U8!XpCh!?*VRj;z#H_W)!rVhj}=Um{7nT=1=Lwz
+b8F91n)fc?`UFvXlF#uzOuF)%kKF2__0QI@b%Xk*@5)<Z)e&nk9FdXt9*5?t@2H2mGje6ol>2jp6RP(
+Od6(#e?Y`W!`>zD@AD(9dVYQu|9g)AKIUK45+FXJ0<ZanB=v>-=I>2u{ax1lhoton`OQC@()y>Y`K6@
+wrTpfrrnJ72HNTd$zLwv7)0EaXvgR{M>zVxKc~e@?Wz83o)(iQ~X;WIKvgVnjbtb<#Z%XSt(WYZbOd#
+4-9@6kx8oFxts0qaJ@-4z#(tdrY!zanFsSnH$gZbxiR?P#@!{CLHUtYW&d_msK!wJN$H<9Do>`-Lnz+
+d<tOvLW};Ff8~VI<)2c$YkX^=7EW4}x$Lqz)?E&$u)HW4e)=<5QnIq5{@{D+4@>8jIcMU!-?Oo<4;*w
+cjV7$zNW`Up^I((a!`wry>C7WBOK&z{yV-@!UHMlO+)#Vh900cnT5kli&Gt4?@Ar%nZY8k>=oo!@?rz
+k@5;7C-TV&2K-O9;gYjjIY5#6Q8-|9>R7((dqz&P)L1owNIS%UHk}%XIV#GcF!jnzy5is&L%ul8h3Q9
+3!8kWjNKg1ISF!d3@=_#VKmamL2ei$|g@8B;Q6gYtA|c@UB7-}}pnPhEIGaug#f5?4pBg2zj$$}|rHU
+&JpD&NE&@<+;$~f1{7wN&g00i@$afI#m3!;H4J%uH5Hi4Ys^9%L2lambjJSJ)cq?S&{%wx?=M}e|&VC
+2K3pE0|TWJJ>WE=Yk<%BOzeA;T#i4kMo7W@z9~`FvQ0>r%OkoG%1eX@>`o1#6A>$klg-#y5zDF)gBni
+^{fhoXED#MEH0kLc@_0usFwGhzYS9e3qE+donHVlV7#wih6;HP)b2e%{4<mAkRYLQWH9bO2o^7iA0`#
+1ktUuN#6DQi4SvHk7q{o?zKdnpbr{VmIfx$jGD9y3?}CV3c<M;!D~MrpXm7ZTt6}B`xynTxAR{7Ly-X
+sIL0$w{RKP&Z~#wLoTsZ+Tb#%|3&q@p#~LK|$Z>j+Or7y06d6*_#6*GU`H-B+Rjw>COrx;S51?dBY3%
+wprb{3Kam*Xg6hp557+IHpkfS6MHcBFa>##qaVIu;?1)Q6NMK{1R%+}zd(1hk_#ej|@V6tD}3qmDfn^
+FJ=0G?|M7$wl3W&0^|IK7-LCHWhTF*zdZrmlnyCnmeTrE_+TF7q0~gpu=JlVQ<N!{-Ln<F*x0$_hVCh
+R3|%sWFBLXC^W?IJH$olB33pPtCdD9A=aR=X1p%kVLVKoFv%{>}H`9lStF^_IU<c5JQ2R5D?+gUydHg
+{8Qkt!=1Rp=hha@N=+NmiuU4_oQ!zl9+e8_@(H3>(G&&6AE`G6UY!7fdcCv}ZSeXpIxh)bGri+0EP2<
+Ulu%s0RsqwZTwiq|qkJ|KI?Bi3uu?mKjEB?Hj)wz8CKAlV)as*%Qk5C^is}NrF0sL%1@&XE)3heWPbW
+s1$uHp-@Tv9<`5Jn6nxQ34<uaAR)fzhe*-5d#8J<14suKxpPM;M&uPF8Ay*wC5#tJkczPf;WDaLt)mU
+Icm1bi2bEeIQ+iMT_>DNK=J35&7&Z?mP18Y0ZpFRR)>&dEn*Wl;?0gR<xs$3P<PeCZ|ZEKWi->wobsk
+a&TD#Zw>%i_k}j7)Yhba~OQR-dM0UoSh4+vtZ?Oelm;`7-dv|tl<kq`kHljbpQ_TaT!w&!&V3mJllo9
+2*-eQLJ=cS++)Pjbx1~)rMhI%9X+^UHq3e~Ts!CS!XYm2^M7PgX@v8`cs(APWKdjLM(6F8t)PeThJ>>
+HKRo2IZMpl$SF$f+zYbp!Rwn%DYeHcpT#Y!uXLeNe3x-j;%cMlHFS!o{(kI!!hhboP%*6%v{4TM=#5U
+W<lamapoq6_!IX14ekwK_JK+6$9vq(VBmQ1O-Y~XkRqV@n7UJ?kzy~iHDaDChku_J@P<_4DN(qQD8<A
+JXoOG&r*H@Q+J`+Iny1M-}L%JKOZ>NHNb{D|c@95J4a#f=70ywZ!1kkHCw0sgRfMcWz0g}7hR=8}|73
+)nj?Y)#_Hq=e3Qm2`lIKLMK-5&1R3Ja1cB(R$fXDSOAeGhN3{Gx*vxXw7~o?yhM+VjkpIh&hh{*9*H1
+U}h%Nh3_kYFwf4-eYKm26q*&G04B%-2$0-)4DyK&k!U~nBf?-30P{VbACD25k0?iq31=y0urTcuGuV7
+Km2d0d`%z8&m6My3;uJFWDj=B|)D5XSA1*Die1Ow1lH{!XtYCEP7tB@kasxc#`N@7xzj*SCM;pMvXlA
+47WOonZhPopH;AE)@NKTwjx^v`2R=|bM*mLfViC}<eAxoNXt;2acuXGb++^-;KdUNvKCxZcIC<lY5Fj
+nxv&=saARXz^1;$4p=eCYxF$Q*gB_x+ZPZW-zEo+(m^Er%zQ(@-o6hk5kXgnacp!oo%aYh!Sq<cWTkd
+!%1_KU*1FZi@V(_dh~degVPgTW7q;3A7Gyonw)Xg>M4VJC@D~bWWgi0=1n$IY_SP4-}xU@CfF8NZ*Go
+xP%(`E!wIH;2qdjfNh&juCcA{m6<UsaAsj?tkt%jr_FV$UcVoms+T=YRDI@}Ai`D<y^EkXsOfpF+^A-
+isD7gKRZ6F(<W>XMPb$H6Y6iE_?37wAhUuV(Rms55tEZ~2&*;VCv|?^)xMEceDC;K%xSN8f<7=plSP6
+K-+fSFOE^Ad)bK*~gK{l-R%)0gbdzac{YD*FyDi{fRfJ+^Vr17+{N$}mfcjP7Cmg5I=+#GAk&2hNj1h
+)x~;Sc86_~s_68sEw@B+sKTN6Gea6AP#qz5<F{MZRwbWtwe^P1b>-t9&hZa1(`tu^$cs+)p*Q0b9ML?
+O{)fE8jgW*>bxuQIwU>_YYV;{QX=&lmTK)_3mYCw2Iv~{y1VU{3)&i=Gk7{jfbZM>;gyDp_}dSv*xU}
+fauG6rFMW{uEHK=QN0Evr?9U@^)*29rnOEkl%n?4hsxThrd#aVg&|UZv<^G_d^v7u!ALOBGVwSFa(#F
+`94?;|T^AeCY?pfRGv*lnlYa-QZLBW~niI=h37!^a+%?^#BR`<Y7NMokp|Fpr?ke-9G{zs{_K}7DtWj
+80J&91TSw{K$o$<-;5{!2#eWrN}ykzoPR$sG{(>vvdD&VfLbov>uP$b*M;=qz=)^6@7M5b)Q57_c-@l
+w&qWbvy;xHsCyBM#;wSRI+3HLsY_mMk@{Ho4U*QAlxd$_(buqtS>TZe(b-HfoalBBapGSzFH~v=+YAU
+zX4fah*Y2UJN=#T`UP5kgh`#We$E5q9}D(Fp$8X1&*3kQNv<1%TiPj`z3pQG!mUK<nOJxNAXf*+TB$H
+`uces-Ox5L18C^)KQ|s6hZ?%>rw@Wd3qm|S7Z=XKR>t2Jv9wvR1b8bm<hed>7=}w0&vzsw5xv{Xm_*q
+DQA-57AC;*6+_$h~Icao($zC{>E->i=lT|s?1tyybOlkx1g6&Sb5uBtQERRnrHzBO(9zUthK`Pzk2Q_
+n(-=8=tx5=)q&g^_NmGA1F1yn1Er3<U}!X<WLRToyR%Aqc-+DKTnEF9+|g4F`6LHhyDV9EIA)MS%d-g
+FC`PWtvSaNEYw>?pVGkBiHToJo_nM`DuQOC<IC(ex#fdTwpGS4IZ=^xjChB7Zby*_q~!nPyEUFJNp-_
+Pn}M;y<g7tiE0DiEVDoyjI!4Tr!XvuTqqSJe}$1`_Y+xE2h8u>2=Z1>MVPW{J@GSZHRly1XCPhqb%^L
+AKkcg&r|d(XyFzU_D%EkYKQkWZNIXpx+MNyBw)KFewV~wl|x+;f5S=qWx!b<K&<3P^M`_wzm!Y&dH@e
+cXaRlgg+FZvzw!{|5`Vz0Q7}W3#;O~5<v6Zawxxu(u^_c>cv{n_wy4A(L;5~5o~1H{>xuBIJKN<T>-S
+@+<$;a8t||naero4U?$(j22{P<Src%${1!L+E;=v$9BUU#~Sa#sjEPc9w+f8w4Pq67D0G4ijCi8Tl*M
+Z(PKyM@KM=M#iK6$><nv}|cGq0elZO^6K5tZy{mlUevcZ=zuTA21=l0r=txopK^?MGYHyB}dGQR*&Al
+tR74KS6NDRTWRo^&hp>`Vm$9cJxC)h(qJ>2(KpPH!G}w6f5}hBubJpMSq?!wfumu4NGtL1IdL9kwIX4
+OxcBl7qe<odG_LRq2SsTO$)fi^39>vrRE)=0p9ufHtq{=BpEP(k6aDSp6ae@{z)bB<eWo#;c08I%<r!
+~P7atOpZ!|q`}6)nZ7lCBN_e#BfcugIu19$EF|v~%YsYWgNw2!(mlRpPcSnOzT&QCb+p+TJC&w3LgR8
+}3?5qj3@HR(&+s=fFi|r8r;CH+YGKf6J8$}DsQId!1NjnwI@ssjQ;yNtbvZ@7H^{9RElSbX~{-*{T99
+nd4`-|3!mKTm;#HWF-<~)3R&b<jQHXi`waAIlhwdvcMX-}Nms4@QbyxCG*Q+-9gP#z<nWwDSS*%|o2K
+)^S=-VOK-qZ!j7xxlO5CY-k{!Mz+ch5*1%4COpWX(iXY`Q#40597xgvk<Q-!=voY5yeaC;ycZ9^bnAf
+4jvKs2Z0rjv&TE-;MaiS1#=vMXDC?#y8OCmXG*h&CKoP=#<A;fh^~~!3F&E-r>f$j+oiLLgtvX8f*lF
+AaiA|H{sgEOM<EBia3CkV6+IwqCTNt8VMjC9v4A(Jjp;6+#^(ltiCc}kKX=}9&Y3s<9W5N|?~GEHS|a
+oXvp#(=sQ0G3JNa%uZd~!O>Rn3)bP><m=cnt6fL4uexH0cohGQ2d^FVw6!B3q_6!vK%UkY-&jo!Z+l?
+Y{K#w0Sz|Bn1{;gVsEyQQ;qfAMrQ^TX8Tz_aZhHf;AT0B@b~Z2Nwd6i9do*Wiwo+PUZb^esI2d|W=8U
+QY3H4(24el=%O)<Y4IU5zNPM&KP;W^A6`#Vi}GV=7B47YX>|`nW?Pb&9u+i#Ytm5BCoa~ppHk#%u_1k
+d95p$1-?ZY6SrW$^b%@4h~pj(K<$R%<a1HNY;3Mq0}eKuwVMWc{~qe=5fQh>7msPFVLpWbU^Vlp5r3@
+HdZ7KzdR81-E7rrfTJ5~`9Ij?O8K<?w_n$R1qNk-sv{G)WCvp^==i84cBQL#@PhFE)TYTBU(e~iT^92
+;KO=D3R^b|&;?rQv0>}r%ry@J80a7F&gV@IPb`vwy=%fr_$?J3)yYAb*RT__}2%R#Dz3yN3UKi)P<uW
+{&=nyKFaU7|p%#nM`}&!UdfuyK{&*^<=R{7t7#{Lu>9&wOxW5sR-$eMri`j*%~?`-K~W^Gbg}UYWO)n
+8c&^#1aMLJq@_+YwX-n;)KLx@#?(kx?&qx;!YG&5x-Rxe;zpj3&~UB2dKBCr(C+qca<@|c#S`qQRq+2
+(6NBBa%buRZ=P>wbHZVjjmCaE6C1}x@t<KpXD;}_p7hK|Nr04=FHxI}SoHhs1=lp2?3K}+nPHZ1#C8J
+E9^4W#;@8N+k6c=b6z(UEHQ_r;9QZLHra}%+o59t84|zDujzN!Ib!<tiLQ^xijb^9Ra-rv;<9nqCb77
+0{gLMlnTo$`>rQ&E(7K1YFTaHNyDTyM*!W$sfR1;RzIf+LjS;P3Cr<Y-^l~`Q<@HQQ*<#!zJvqRt$Ga
+KT}!_&juObm<$Ov%>y6|^l0PMk#V&_^0S9rllao`s)Zh|eK@zJSk*L9cf>$AbSAm1g+)jrdIQ^Vwnk9
+iOEnsFfn_)Pga{@#xVxWkz;Co%$5F(l{eoaRntg^)3EwO~0C)*<{qbQ=S;J=*D%dmo`*-?Ryl@YMQf<
+PSbB{60-aNF$Zd)%G6P0TMVwD4)=<7c*#%4dZcNOZ)Xb-=TOo(oW&)Fpg(lzgF>CT6s?RkEsvcV@|(V
+KVnjYUDHaFdGb1;AEekDgdw?S7Zuqv6XZXL1<kO|0*~`|B(ZHCtVlY`0<Q@h(0ZyY@gQ^^Hi}72!9A}
+$sLfOC%2C=3as@nz+?9!C8Rj<FLqMP|MRZN;_E+3aI_Q3^zvW5c%N*!3$NhJH3EUU<pZE;(L<Ha~Vh^
+0{To&Ca4S2~(+>si7r48C?IvF0MWwUmLc^VASs_cZUUe<bET=<K_*?`7>93@AS0dkt2;N#lNy{(JkcP
+wl#X$4g)h*5gaA8(O65s`_>AtZjF;kPkbL{Cu}PPQZ57MAqj5RzH5HK>*Wg;IQvD)Q%CYdWm;C>rOYO
+H;$K_;ti2#PF2pQ1T>$U-om%LRyl>Ro#FDqH6CuQ`P(kSqcX;ABIH5g?b7Wmur4cSF!?p2cw#mCiR4<
+NUckvuYU1X{J?#KKe1xGG_a?miI~<UK2DWX?>KfPP_u0kEhr9k}Y|Ny3hg@aFCs!S>w2Ms}*RAz4&7;
+6Dt_(@ElB_Lh2C>vepi;G2@B(nY&ia&nW-_Kx<Xw1IRjbI=FRJc}O7F7=71z3v+V8^J9W-v!?Iz-?;c
+ZJrwaDXc>JXWdVU_;2=<K2jK5)FR;nHjFAa1*_9VnHtxc(dZJB~^su2(Us9UR(A#jKBV`GipU+d2CbM
+YU?(rCQxsm+Hj4n>Lkg&UrJ(dCYK??M?o@OMDjWI>e{A>(_{o)-MKb)HQDK^K<dp#m_$)Rl9!i=@||d
+ekm$i`1ysQ+wF@_weIVNU2BJLmI39P7V#-=T#TD5-Mom;VsHM2Z^Hc-o!|R`<{%*X==Q|olRLA8As^q
+iBjh>K`md0Wq>G<JvD>tskSP{E6BVcU`PD-d@>{;Ix{;7G1rErw!uv5$+O*>txcEt+2RTH(9Flu+Kxm
+H6r8)TH+2DTx)0mI7=7&&wYz8;xm>-duzAh5u7k^&{PaG|fvK?lbflcjiiSM~{UKRZ^3zfG~rZ@w}b4
+m}N9>#C&<j5d>%xo|`juZ-V^bT_;neGz2%lVne#P~sWmajs8O!*EPt4aOO%$q)~1X#uW!Kdox`}&bd5
+vjJ8)qE4bxd4oCMh*>1Lks1g#GN>Bpeis}Eo)bsExR(0*uPZ9It5gnV%65)%0xkbVpdjk$@@h-H&=81
+n;&A`vlq2z5Urs<JAQVef;aJ@qx)cL9WXWF)^5C*-a5A`0u@<yQRcT2JT`MoU_*DjkLgVXOLz8#rw$7
+rC>AbK)#_|)UB|zXGZQ-~>7b;8l8ta{4Ny{P!kf*Yg!hzsneIIE?6>im53*mZg;`D8tG|cF;kM1bdB~
+d_rqN`&nU}^&)8B`{pgQyM4*$Ao<x<niMv(bn7VJv_dDrl7OK=1I>ENb=o91w{c*%Hct8E1v=vLbrv8
+9VXwzJx{SU}aQZQA-fyV~}U-R%yfh1TLsy1dX1aH|VcN?&cPLXV9ux7GE>`<NqFz_iPecUZVvShz@4Y
+cID|dj3r=w{=j`K}n%j2PL}>C6y-pU{bW53VuA1{YncM`QrO`oRChH`PotA?A6PQ<#I+F(x(h4=ns72
+WR#=qOXPEzUUDK1k4G}u#;|1B+5Yk#n=2!C$bX$mPxF=V$Lbfg;=2R1t0DLl-?J(*5Fg+n4Z?&Lbf-`
+xRuz5^-{6ZU;%11es}g&ZMdK3)c&#UcFRrwO^>2gedn$=bhGLhMQ~zU88piN92-R|`c|cC7>5_XVoDe
+T^T{58oC4tFr=m{sLCtuDIw@D=Ru<Q#z^1^sDLks^%E+opBaa#5%InH(;U;lHkiNFNA=_MsKuj;Kgw8
+f`>cpI~n54_G@OtGRYkNT<mZyKxPKT7ZG0E&tj{|``00|XQR000O8^OY7y9v<BwV+8;J!wdib9RL6Ta
+A|NaUv_0~WN&gWXmo9CHEd~OFLiWjY;!Jfd5u<KZ`(Ey{qA3JP*GIM6gSC=VGEW4TUui+(5*mI>_Zv|
+v~;%F$fQ70iQS_AeeWpQmYt=mQB;xc?(z8U-jOqBmsAw9Hnfh5g7mVnE|8jfTepFW?meeU-)XO{$*DE
+Ev=tY%4(wFzN28fwgmuGy6aLm#ZOvEs)LR?fjEg%qm33pUR~x^;t5AgK7To%LMELcl)v_LQaLnWKRn1
+E_;R-LLJXJhXZ5@he9r3E<CeZ5`^~yQx9)bBL%4k#+s;)u61zjh5wj`(IEr*4zUTWW{P%hZXH|^iISZ
+=NYzf&5Y;<EiO@k85{y#!;8)h0;fGO{yKWw37DKOTfPlD;o$Yq;2g`Q!$vKB0HD3N!1Lo{SCDJ8Ls1a
+i^U-r61duKcAc&<1v{`GV%-C))h?|Af;;Oi$K#gg$3hNZSPSKAI9|B(437_7bflLz7Ha0*3|TC1EKY@
+=7+1v@##;(sScjlpgZKVoo!l$d~o>Ub7WR7;E2#SYcOiYbS3!sH)W*bc>@L{u4mDpJXd5*Oj106D=@&
+=?K4Mam3TOo7-MP4<w6<l(U_KItrHuuFIB^v@b1lzcdb{fP=3Bsb^0v%*fEzp%er=&MXI3z<AX9Kr^P
+DY^>22*E#DLRhsiKkJ$E`Rs7;dX4w3&Ngy#JD^JzQx<4+wC*3F-5?^`}Q`SF)6)*zwwFeCXX@7aALt6
+m*la*y>CmY6`tkP`m;U)L+wid@PXYtx}F(n^=1=ORgxmw4+4I3wS1snJl4vkh|A?9WHh?{7oe=%7`I<
+K>pu9?U7sAQZFKK1km%I?Fl=ZBru)v6R-_0Y_-I5oZa-YM46&Z%LIU``Dq<wjvBrFI|Me1?A|jrL5C)
+ZB!jGWVz#7-{SN16C-)Yn2W|o&!y~9;7||YATC;gk_Rk&Nt2niBK4_qNp_CM=j3bFM_fna4P9JNG;~P
+6ZzfU)PxlKAt(3}$?y!HP;4B0iQ`xwx0A)=vr^g%0J22QW)P>ffNJU9`x9N*C+w0U1k)A@T_R0WCCOA
+5YRV7?i+JS?5Ep4XfMqDALjWE^Jwqbx3?sXX_>rBZEc~>)(OO?eQ9M4)*o}J7MY124o1aHdtgknOgsK
+R^zhnj`RwupMXQ^YV=%<7x11wdrflJlNG)Hm+=d^lJ|$D)i>JTvi0rRI6pSvldb`pgD&$DYgfV>hb^t
+b_{2LyyBO$Es;?@TIAQ^~q(~Uf51}{|hqM;Q&ND#|H%;hVMMZHr}+)1^oqC3|nT-LDJlUKuZG>IyH2w
+!E@)6@8M?gjCyaE7}!DMp0LIQHl^s>O9xdL192+cvv!q%NTF!kVuF9Te1ArFUY;)i(ZtzE(^l6N?irH
+e!Ks^8DyTlgKD)GL(|s0&oyp0tN3C^LG+&?GWc1qwovdW4#sg%}^lLQzF_}D$aJogfJVZF%BFMQr>XL
+u#QZU>Fq~HqF7sW|WR;MTU(GQgFrFPsWozW5gdLbSUx+mRB<+x9>@0p&e&012f?#f2}b!7WYjm^7*On
+dVz&>MB9a(Yd-#@=TL`%{iwnF_%+o!^v2wH5TFOC|Fe_bRfO)p2s+_E1939UuR75lUy>v!|nTxm1{|V
+C2i!U9;{5r*ci`wvf}2a&p-?I=a1=zXlDgXT!5{cT0tKL|8?UchezBvIFgl83`oCQ(yEMCiIkc%WfgI
+VGmW^gG|2%Wtek}!@wDku@BtWb6~%gn453p0$(}0wiI*|alg-}pJc|)zJuaJXzW9#L{W^}22IGRU_J2
+KPZmqz+avoLjrQMnr62tZP)h>@6aWAK2mtey7DoU80006200000001Ze003}la4%nWWo~3|axZ9fZEQ
+7cX<{#CX>4?5a&s?VUukY>bYEXCaCrj&P)h>@6aWAK2mtey7Dpn0`^1$10015V001Qb003}la4%nWWo
+~3|axZ9fZEQ7cX<{#CX>4?5a&s?VVqtS-E^v8ejzJ2;Fbo9meua(+gg&6qYri2JM-D2ERV=yhuR}~Rb
+Z^?#jueX=fEcUQLWsb}TZ(~SRZ=y=YhGaE4=YPvpk@xT5hgY&KB;pqB(DljPR*HQeNnLa?x1O-*yx<u
+5GSP+7f`7sP|r`D_U&@v(PKBZ8@0EGR-yKBhBKT7!aO#2GH+IXr?WrkAJMl)N?&zfP)h>@6aWAK2mte
+y7Ds;$kWL^0003|T001@s003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?XY;b5{Vr6t`V_|GzbaZ
+lQVs&(7b1rasb&pMJ!!Qhn@BS4crvyUSxzO7>*lCCER)SGtWtci~kd@N>``Jl5+O^fmAMYbQ>e%<7bv
+Mbxq&3)n^eli)@NN>)>%9bHPc~RTlrRmp^@g;wLX0jb)ukTQ71a#}Qc8H<D2TQEw%$2vB{m4x+!q^SG
+Vit(Mdy}AfzRb<$L#rr^=r_?;4@aK)?o<nVO<o;m+{qlDb_BAy0~J})jAL4y36}A!83?I(g1dVTef;d
+q_W)#9Wt3m%IXB?*<O(48-KDAvRyyQoX-EqR4Bp$wzpg4#X~BY4cJo&Cv+2~5h3nQCclg}D45k%wCqD
+(ln)JF*OBI}!@e;H=M9_>Z}k()Khs09sH5-V@sPW-iK&ITOU;&?S##<aP)h>@6aWAK2mtey7Dp9#c{;
+cP006oQ001)p003}la4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?fZfa#?bYE>{bYWj(Xkl`5WpplZd8
+JlOZ=5g?z4I$9s%SwXwCAmIOpd)Yw`gTKFa#^cCbmhsit^v<F<ut3EFsC9_?UU~@ty}Ntr|jU)!9x1r
+Gz)F(w4A$qeN$c_V*bn`7<|M$&7T87pep*xCPCGiJ}TI@<S-SX9l7uDg-lz+;HLC68|voZ_;f<oRv_y
+#9VSqX<|TB88NEU1!M%I%fRJB@I)H}<g;Pv;EPZ50|vPo;G%I}hZ6usS&U^CTtwWuVbs9NczD%-WwcV
+AEZzHLnt~hhN6AqIEy$4jZWj$tE2W85k01$`Ck@TkgJ$<FmgEEPV!VrY69--eA6NHZ%e1{^5AhV^;MQ
+YE);Nq@s29gf>f?N`r!kY6a2ZB2aRV{Vf8Dfp|AqWG-81=r5Dl$h1(2d<+JGf5+}4B@wqxSR?ObTh46
+UtgDLi%jnHEY~kd}st-Z=5XY#b7mV3nq2s7Jg^GxE$t2gqIMqhzk=UGAuBOp?x5A37hvP%Bbdub1-?X
+jm<m-F$^;e)0%Bp5!JBX|oJI>EQ09*PR>BMB<BJlcRB~7fMFE*T5b>Zpt@<<Xw95HiS8N&%m_Zp3_U@
+^n;$4)o`|Ma4K2kb_o}YPLS)2#NkDpo=E6ZT)js_t~M6_*`Kv-#P7o?xw<eLa`7z|Pu%;nzPv<y%c$@
+p-nnzlr2*YLx@)Tl;PxO0GaIqzAWHJu@4)S7*|~A}a<-v{Y^l+HklX24-&Q3Yw%aJ7-(0H_z7Ev&48`
+eoO3&NMDr7m=^nCETvpMXVrPbc2{mO^MYa)CTSBn87NrKw|3NVK0ed=*4(V2Sq!aj9yrl)>v!7p!BoB
+T>=9WliUy&f^YD+sjjisGf_0+wg`sUfV>$?^Y489Vnk;AR<oMn4Rv<sAxQEnX#FKIuE{&28Pj9q%6f2
+T)4`1QY-O00;o{l@>=3naaSR0{{SU5C8xq0001RX>c!Jc4cm4Z*nhabZu-kY-wUIW@&76WpZ;bY-w(E
+E^v9>SM6%sFckgor?~SdIFRK50%MdegF#oy+Ks{}Mz!Tut0Ngn>Bq+2efLVT6DM+-bQ>G<pG1~)bk6<
+Kb*0rD^1NEuh30t<V%{iifn6C@FD%dfcnqbu6GkXGhJ_S`DtTTD%QdS_l2n8-X}9KA%<%ATQYdLGlh(
+AeMae9i)qKwJ<rwtZ_)dwk9VLSxreOs?x?!3X$iXCQV|Wc|I{B)E%+_W`Ns<&bGX{=@v@@-h&Snn<Z!
+Ah1O%uSU!Rc<<J5l4l%xRkVL0DGu3hhQV$n(tbx(e^<Q4N<l4>FL=m&<MnwOD-n0WDa+tJyViJw1ARc
+z&36YbvF|s#`8WNLW33GISt4_OaoJ<~DvwX_Q{}7KqBPLC`EcvrOA_cAbtPoy{BjNdMOm>5*){f=Z(`
+)PTI9>%~&oCMrFe3By<X&`|=ZERpoPTV$9MG*pN^g@eqp>yb&}0KTXAH=R17KSnWX4$cS{Lj})h;TVk
+75w%fXjuM$c7>C7ZvydGPy*mw>-SJfC6tLm!Gz#fmj)v~M2~3uOtn4Mzj;DUcEf69XG^cFiB`^b>b6h
+0s;{jf;GVNk*$uzk88Ej9Ke04X(xIJkL<6cz9y|!uMIJaKPPgsM)(uLY4Dy6p%!aG(koTma2s)gxg{D
+A!fIcl;)i?Mr!m3=Uz9qAC$&N73ZU7<A`*ijZfxLJ=&B*f#ooz;Fpc(D4WayQXRcLUV!eg`r-<XP_yi
+&Kh~{k5v!J}zHcX#Gs=k57VAOgA+{1Mvqt5Y*Aj>iF>e$@%Q!JWY}f<Yk)6GM+2D-Y5TW(f$+F*_(U+
++H9z(<-sTY64t0Di}GLDgLnKs#_=SxgA8lq$c&&=+&gE^Y;m)^VsM2!1lM>CxISpWZpBUEaLFSgY+IY
+PC2Wt>oYY6+_V=_n0sqMk2P0>IrEA`n4K1~AXZ;>G*2Pf~S!SKTW_HLrTDVu%J?nO%Q--sTW_LUt;~q
+^kGNF0C7xo6Y%Sfl#rvvrHQ)kPkF8npK#z_Rr($9ocHk+Ruv2A$Ju&}5&>UA766fJg!IaH|y_E8uWYj
+js?0sZOf9SxQ?-;xS%RpD**H$MpiiJiU(Tx6MD`j&s>dU**K7Z+2wzhM@QiPg|YY%fXS&DKJ2%CzB*k
+}CvU3s#G7;Y~z~|F-P|nn?ZL(ECZ=N_8I^RY$-nb?FvMtRCW{;C1P@CO-jCO9KQH000080P~d=N70jS
+2+0Nj00b!j04x9i0B~t=FJE?LZe(wAFKBdaY&C3YVlQTCY;<LEb1!djbZKvHVQh3^XLBxadF@$CkJ~m
+7zVEMKorBhZ;9@tALy#0Gg5=msatM$H1})NSA+qR_^ltp`J2R9lilS`Ek8KVWAX$&$a30?bhvTJIYc`
+)Rw|1-Le9rRqMrq6V!l+_v<=j1|OyrNb$yJ%MZJB3E$a#@lsd-^;ZkEVnRu7#oxREzEH(9}rVW08>$Z
+qN7%sous-Y__XTryMS0=^nqEUUM8Y_xpL)z;jx_xt;Mxc^I)a!<5WnpyQjmMkwBGME{))tPMA;E^vqZ
+n<Hlvc#rMg~u+Jg=nTrn{la|r!~SCYfl)ZKqcDa56wg>?#(lwqmAeD&_JkrJ*bi{Wu^RS8(TQTPTP9g
+-A2~pe4F6Y<a=l?NAsRdK3lHs7ydBm+6u^SD<*<lKKag+?hw@nnw(_H&0K9PV2BrUgUo9lC0~OesizX
+^GM#oOZp_zpi7b5SKbE3b_NYX%6rnsM#aJOsFSZ=@-nm_KpiJOS{(u5>%+au|+(-`RrJ$SIo#2UfoS;
+!0M+8T*vg;ygOZbBQSu%VN(#y3hZDJS+gag@{@7O{zwa%@Tg4nGjTjtup_f=j#1ibRn0Q88+=UE3IXJ
+*4Q$+8vKJOgy2z?@}jja0K1Gc6%0l}=q83-aGiJ~+xr3J66`A8JNsGTo1MSg6znI3z2{MOSTxuP?5(#
+}l>YW&Jb@27Ued^(}jn43em#sc3v7L^W&3P_8$27Y$o7&^~U#LLZS7?;|j-touc8<tcGg4R_dc5kmt|
++DX477E0_Qj=<NKc7)G!82r+Jnf#f_jg3rtXd0Lz)A_~*ykH(<cZJ-OV&vlZ5*AZfJJ(X=+{$8yai75
+h%u=%t#quX>q2p<#49tL>!FDTdz;>`gR@R&KgXU0F_4@u30QV4f4y+&z_=O_J6iWSI+(kfR0QrbsVGt
+fbS;CVhT_Ws4PqaV;Lgn~osft290gM*Ay_;f7*9-jWJ8L!w{&kK@9Ooou32{lL1J;OGICi2ZQ^kW;+f
+5{b>Q9%BqL8mc9lWhyq=e*(W%Vv(=Yp>Taw#fbz}FS{wlf#uIYj?>bPr95qfrTVvKW6gIwKtSc|GEwT
+9v02ma$}7&qfhuEdcizZN@%J$%4{m7=STof!$;{T-zz2gIC*=?(H@5z3paVs}G?RXaa7BQR-$$kp&ki
+W^}t$Fd`26Q*!aqthq-M$>7!yUj~!3K?P(WxRw!CzMk#G*fQimIC&c_lB$0AmY`w@VJmUPL_!3A_ih-
+Pp@<_GN;%a*2~sL)PB*A{(o>-KE>Ygc5c`SM$*It9y^PlQxWNc2{L9t#x2~{Ht4cSm3a#n!O239nJn(
+$t<4HYOU!S|sCp#25Y7bU-3uQ`P?b?AA`Cub7q}`0stA3T8y|JLbW2j{;^RTtjxJs0kCMw;c1F>7$co
+{<8tQ8%sOg*5#raY!nIAvHhxAi<xGcR=?RjDBz?;n4KxU_=?X7!$IRH1r!LJIe6v_Me}WZJ2t8#IE7R
+Uu-5BJ5yCG^FDl;f<w4Aw)kmgcvT8I71{6h-9w^`7^K*crdad^C~hCwn0t-&`~HJDGOu!m*E*~bBae9
+7eY&8pbpeF&k9(5IaZFLCl`gve*Cjl76`Dn`Ji)w-EUr~2mIC43WrOPLRBgYVPpplSeHm3Tq?az+3F6
+?dpKXh`3lb0aK1@t<Bn7-I6<3j$fe{kje==5)Cz?ChRiH=;4NRE{NSA}d~T<&fXO8wc}iEQ3((5`h|<
+afbc;Sn(hi%xBWe6Yc&|J}_vE87yuh1o;3NG|`BeLq?#Yj1IHJd?2)m=}JzZbY^%Y%T)Ah|5o(?B8j;
+@Fm`~`earBjSyMg<X|iFy+bPgEiyqVQ&0jA>S!w&Gw0a8+@v5$(Q=DQAHEtYXHSE}qvrJM%ENyu&}l{
+;2DscfZqPT&A$fI2@R99+zD?Dp&YL^VK0h6Ke`Gy@~Ep^zf=fpHy|=FpyPmu2=s#MTReI`m~kp`ih(j
+vBuNSkz2+!Ne{cE9?+j)#_oXwPCYx~U!*xt|4dO66#sGttaUZThG^7EElYK~NYwHh)|ULucj_ft>8_!
+)?<4iJN~#{);9aOs-R+eeYqy2n#K?{~Xqp}o$EQADoXeM&q>?|yag`2qTL-ArzXAum#%of?OY;|%>oW
+(fSTAIH6MZG^pny3qFMsoTs?<vT9!VERn)x34XWKnGVI$%CKbZa_Fg56Smcr4xw7rVv>=6n3l-3ZALP
+=LwEgL=l1J<2iQ;l<r#>UH6097l~{0mS^0|XQR000O8^OY7yIIrZPu>b%7U;zLCDF6TfaA|NaUv_0~W
+N&gWXmo9CHEd~OFJ@_MbY*gLFLPmTX>@6NWpXZXd38=p3c@fD-RBguY@zf3f?IK6SGph4W&(q0BKe4V
+dYh&d3;GrV^L{+--3W*}#pDSQc)U3uLH5C!6bZ}UfZ{s`cD8|Jd2ou*a3n8H5JKmUc8y{$11($|q~*v
+j@~e8hbf@!5cKl!~gcytr0S;`Y#%j*D<!OHS>z#lJiuANQZ$PX98NKis+IfiXN*36{cVGK2kx>$R$kJ
+yx&TKW`dSho@65EoDCzDY{6mL*V0|XQR000O8^OY7yW3^fp&;bAdb_4(bDgXcgaA|NaUv_0~WN&gWXm
+o9CHEd~OFJ@_MbY*gLFL!8ZbY*jJVPj=3aCwzfO>5jR5WVYH3<@DO>zejj$RPx`*U-(Ouw8`K9w$m<$
+w+e2^w)Rfk7VUd3z>spzTSH?mfSkqfe`Hw28Tj`>U!$}$b+|f2q-4MfJQwjudFFxFsil<3atV<sXb$J
+k3+`V2v;sB;vhZFv9h+t2Wf&|vaW7qkd?*`DO!N<WsTSO(#e{TJzM#337nS?R+(&-$r;<;TwPaN@uT`
+){iH&NMA!97W5##qe!t;xzQ(66R%_`!d{deN*{HUhoct?eKsNT=IP~bAaFI#`G}9k69z(X{y+d9A5BN
+Jshm!AM16uizr32>q1leFq3y;eaBJ-$Q;)2=wnTZ%azXn_DAlOGVpbQ*~EdK=)Exhy*W^CN?=<$70px
+U{H_PQnI;a1*p_?6TwCQSp`tGGqajvLt#Y}6GMl<#h9r7Ib8a|vHql16>3t1``~D)KK)|AG7TKAf@Pt
+tLXf&7EF}iD;fJemlNDWjljg^Z~3%3+c&Nf)942Qzkgu3^h(S$iwqJI?9gcb3CZ(1UdyIeZW67&e0^N
+y4T>f)mru*#iTGx@_EYpqEOF^p;r@c@JTn_{`pHgq<@Kl<0o1A;gDUxMFC?S`wLJ@0|XQR000O8^OY7
+y0x`qn5CH%H>Hz=%D*ylhaA|NaUv_0~WN&gWXmo9CHEd~OFLPybX<=+>dS!AiUtei%X>?y-E^v8eQ9*
+8lFc7@^6)Spy2=M?U)KjG%+Fla1+$;x{u!^zK+HTaoz6Lj>QGKz-J3BMG9@tJG<q&*eloDu~EqhQ$XL
+ax>6E2~rPjX~U3Bgcjdz6|yveHh7VbQ3nYp-X-i_&N0tv32eJ7iLtzLBrQ=fmsEetQ){^f<sq%gM|kI
+xlifhRc<eFj$_HZ?Z)(LNqZ8nN#y-4VD7#&Te<}ly>&6s8_}Vv~63%ezZaNa6#x)HzN3v+~Z0}>7n^M
+N_9eii11^D`BEuXG7Z0>plKlGs<$96irkNx|4+5DF)rxV$BG=nyV3!lm@Vg-|C$k|;5-}=R+rSr7=O-
+ys~Alm<x||WyyCW?Li_+wO9KQH000080P~d=NB8lKOLh$a05>uK05Jdn0B~t=FJE?LZe(wAFKBdaY&C
+3YVlQ)La%o{~X?kUHFKBdaY;$FDX<=+>dS!AhaCyxeYjfK+^1FToN>5^{k>xaZ{cyJBnOv;)GM6-&$e
+ByW$*>R!S*ZCc0m@P@_up?907Z}lDJSjBoia@%aNoPw2f(h1EFnR#DrH%)ARs)+vqBQO6j@wK7U<s-6
+7dZeJWD5}OnI0^EQq;e1&zgMg(PyGPlF9hqpX+;zOCp;wv~b0<!m&P#qMlG;G^JIWg5yXi$w)lu&4~#
+h;2ibOY%}J&Wj=|G*V857-?41X_%!#(p2t|HyMxSF-sV%O~?}HGMYY@vfxWl0+vKhL-t}#3mSsdqL)w
++MpV+86#UI4M+(~{o|_*Pc7;?*UYP-)rh;zqKebwcFcDHTLQ4)ZBc3<?HRGw@dNOVs3~e4-*1F{a-~x
+swQqqNhTd-BZ@)#)T2cp-Wl^0p6|54B=jsT;D(i^c@umhlLnZ(LKJHyl)DTE48Jqi+<1Mt89qaJY)l%
+e=5EC8nOeSH7)>32_mfR8sOvl;nL>zftiZbCMMrz8e7{mKhd2)RV~<FOVDKSxB|@th=OEcr=TnQ!m6J
+se*V-><mSG*bLGV{%Ep8<VWq!&M(Y%_MQcKeO562ZBiQv}87~4fzHP^w?`c!UIDFbbWT#VY&gksIZxe
+60CenC3{PGah<&Yd*%=&5^x(>wU^p-vLb2&E4h?jsl!KT^eGo{cFzib+$DNMatJT5?c-RUlWc?EUR|F
+n;|QMcm=^I)`R*}I^79FKd@JE^Cc|lmR)_n|<`c+#r{gDLE7FNA6L&rYv?Uy<1Zhca;*jC93Gv_Sc%3
+UAbG<*z+I2xw5rbLPa@&yw!?3>j)bg$|z2lh*y&3rdvLI_bKr8SG3ALrm1QMlG;!U&VuPl@|_!WdM<Q
+aa0wE4+laZnC9bRP`zt_5#}<(_OBu`Qr-@`UW=a52}R$i>1AMq3L-=`4Zlbz{_+80t^Rvrl7}P7RYD4
+)+c-i~NvTt_z3a?#<Gc2scjHC0@tDl;%08>hD*^9p8sJdcH>Ka%I4&vkRuTO!{HoWe(2hksM;Sk_Xa_
++a?wab3*`q%nJ-%DDJsj``)?N<GnW5a|hRcOE=^_+CCEJ9(hFkHQS=%H_y&KjmND`37ttw{Hjg@f$@Z
+xaH~PP91UV$WmJho=bL6R^_}%@(wmXSRnm%Eghr!L7(?4a{&D^K)q)kA#{754tva5;x(e@z(5g=%zm*
+wm*;|t35?U=?yl{;L2or2*T&fgRZ+iU{6@=wu?^qXX!Ga|dGH5(iTf(rE^p=QSD(SYtD@(Wx^a70o02
+20Af_)BY{&%3Q`>S9tcmoW%%YyP4Gzp-K<Wbp_S#>%fAZ!#Tq%ElGo7yJP%~TM}fNLR|^vz@4$~B%q>
+}2uA>^RQa%n~j_7RNMY83<x?q;bBcOD1^;c4??x6Xz+qHAtAG%H)Dc+bq~u<-z?LS{P-3rb%cD0{vbu
+YIkS}VA1zFVc<=OSNj)#Io$3|+MCep`WSHseCwd$axqdF^sIwU*O5r2;XhnxyUjtV$3ckg@QZ^$+dWD
+h;}vgQ_ay;{3I+^(!Q$0~eEaR)JuPm9l`B2Zdx>~zx3~8GbhHQiC)?daso6W(2kI2MSaQo@j3O&u2$(
+F=m_Y&&IScuUvuF%a7RF`7&T2vq{bm?-pWCg$zt&8yp=;yV01Adv9ziU~ood`6GD8)4$_T)bx|zmE&Q
+`c+QH!#$M69~G>xAgA7{E<oSozN+IJu@0=%>dx=loXkVxJi;GC7by<9-jLVIeR@6eSp|Ee>iiD<Ly}F
+1=&V!hbKAF}0SWRu>C+CnrzImGKV;0W*sbYz7dvT*k~$GEQr$rTQ@nh6k(L;C3iN&j1k^u1QFNC@NJ!
+0Tx&RSVibN$!Jt7+TBGnW%JLiCde|pMj;o-v2FeiVZ9L_N;9m7Tjj@;fz0rD$qYn9u)zSvX;vgOwuqT
+H6cvEU;QM7!f@vuHD|a!g#cZdCgJEY7NJ<lsraLrh!Ldt$!U=LT17Boi0qI*7j7`ZkY?KeLU*Tw^w@y
+@3QF9UwL3$t}kI|F(BhSRSMNoM*rK=TeW58eTfFT4K>^c>Ad&qW*I_QytB`~G|p@gO?Gs1k9Kyp`c1r
+b`4niBsScv@t_ECHQ_z{rbZ3ls^{DmL|sIb$$h1GH(~mkksax1Sd4tdI=0i9sM%xdqm^Wy!B4<VMU<t
+=P5q67FKQ1;c}w7d*{NNGV)U*mu#)tG;=c5SO4T&~Cw8)^>MQJGbp$>g0@DJd88JPE?--Js^aw78rU~
+lS+viyHqCz3wVI#+6g{Oa=Ej1#u~^fDu@VC0%|UK6tQ$XCG#nysf%@SY6(ABlHDG7i_I<SpuNiz*d-i
+hGl(8dN0$ds5VNjU5Az6$)X0nY7*-8aVwl!)5)hQU(-~ek&1~smYO&k!3)~tiP|H-%6~n`fGQCT)dz`
+``VHRZw)>8**St-HM_L;<jG4czx_ut2SiE<SC>PE3R{}SwhyWNx9hHn5)d-&8itI8^1z6!sVp{IRuEB
+lT?-@uC2#(?~aPOu0O1!lDz(<uoC)I5U`N9Jdzr#SXZZ?g<`_!KtBX?|K^o!;|1ehQ_txUw<r-p4+gI
+j(E)F-oU<rRJE1ebm%Fu{4cZm1QKPkWA;)CMOZdv}gErV%VzTS})vofX3F;sfh1SIjtif^+2DGHXC(p
+4E3~VNFq>rYc+*1213K-TW3`oWKy4yTN2>Gj}iiD0+e&G)dZ#%-#z@qeP;WBX;~9(Yqd~1|G(%~9Z`R
+UI-=^tjV@Tk?`eF;iV3M0%znyJW~tZUUY*+5y(n~D-BHsSNVXC76xI1DLOL4xrc&nGVZ~!?uUzoe^}r
+7bq=8(kigK#wzDxbusz=aER8?Oi5V6k6IKo_`T2WID-k5xLO>bL_kJPSB=$0pCLfC4>Lk>v;Dv2tBP`
+_f>eb{q1@v0zcw>*RPuzHlQHA6Q{tGYADxdJNwM8@4pGblm@sVdDMm*R*#w=0QNY7*?dAON83%L#Vln
+QAwU92S94g$j)#8ykb2s!+lnLKUcv<~_x5P<<Q6V)yliXp|k9-NBrQlfWLLDGla0prhb!JpAP~Zwf#+
+4KpJiluiZFJs^$r6TDly_~z=(i|e1?&dDQzC!~fnjx3&)NNoBVB8SnEhc6@VlluU+;_+lgj>+5ie}DD
+z1@VB#am}PA;W*f>If)$%qRE3ed1UeP;(Gqt8xZ^uITs}OKaG5t?#JYEMxOQ`Sob4)$KD}i{ODv*l@E
+1&<a-N6i|`0%KD-*+&-hE&71fZ0G2b{Ks7K_U1AF{5I-%y#Mg8eNXYY`x4u{*HhL;`*{G(3fYvf7SbQ
+6pAxEqZ&&C9OjJI><Bs;gChVbEFxb;uz(j@JP76*(T!Et<FHt~(2R>$SmgPN>RvZ`AGUJ37*sLLE(35
+p;2SFoc}p^FC$UTo3&hr>NbFtLN9xdt#zvG`}Irm)ya0N$HcB7R6ydEO5AiijQ8H<B*Eq9p=Z3Ri{Ri
+zYa=dBEq}S(THfyTQNm_e^gYxx1l_R7h0O4eT>!P<+_FIxjD4rpyxjJfQQ312NkQMR%m~yc6YVpa66V
+BHL(P!rs{0;w4#2G&3Apeh{dT9n&wprx3EX#`pwmwGx8j&^PV;lWv<F4)N}Wh@*UV`&)*5LsUVb84~s
+tI>tk~4NF_rUcY~Li3V!I$3atCFcf<O3)teHXw~5psi57swSmrVF$NgDcMdATco>vnZxO45@BeQ`jQu
+S2bTSU+vrP>Uay&A7?YgC`%Qq-ZPU-$B3nk*wqwiB{DBU=>8-=U>8<IWY$!B6|$swYr<K6=M9Ft3`+`
+mKafyP2A6UA#w!oTkofA7I(;>HDx1TD&e4uUH;nRom_%HZ~>S@;t5#B39}+PGW{R8f55Dl;2dqL$6VV
+{DJs#%H7wh*dLXVk<XqfO7I7m_O)1+jyAkPILxNy?PKNbqrd!Z$lY4KL%XR<9jt-W)h)vT@_o6)?&`h
+>zx}G&pULpBEgmF})h*bsi`M5at^?@arcjsrp~LmB$mHa-$8W<X=%wc(*R>V2=)J8T#`!#ru($il)~>
+fnJJ^Z2Uv2x}v|{}Y>ggkKS+}SIW_*z)inwF!*ZMa4H<umpi5WRgP6kH~rv;1v^^l50zChD&PEH<)la
+ov9IHH<82BQaGnflu3xnnzkQJ_%H&z<K72(y2yRo}gSfmO78F!?GP1q-v=6rM(dV~3*Dd7DV}dbKdt8
+{<6(B--y4{te)|PF(u7zCrgMo0sUEO?mIE4agn8xj4Cu=JGxsuNN$hSn&mC>()8A^Zb5Uz4fPF18<7r
+KD{14;8x&aM91T?<KjRYKx?R8i|{^t_(10G-o1H8Ue6bc=ReNLPxJZJ{K{**rH)YevH*`5)vu6{gx)d
+j!~`OT%B<K~_nr0KLG`|*c9~^5>prTbC*^{XMMLJ%XoQM17yY@qy60JHg!Md6J<ykI1~gK>tnT*I|B3
+NmP)h>@6aWAK2mtey7DoU80006200000001oj003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*<WMOc0Wp
+Z;bUtei%X>?y-E^v7R08mQ<1QY-O00;o{l@>?jTwf3x0ssJg1^@sk0001RX>c!Jc4cm4Z*nhabZu-kY
+-wUIbaG{7VPs)&bY*gLFLPmdE^v9ZR84Q&FbuuxR}ebcfaf1jpob!BfgKhU$Oa530z<LsRE;bdlH8^k
+_TNYOE4GthJ#;$QmPkH6KG7_#sUXYB#y1wT3}jUs>p|?CQH{r}+b2-SSLvkD2{c;froc=|k5(whSQ#*
+ml}epBru4Qb3bX^%o%dGm2+OTE->O>m{%M*U?Yz+5jY*4_#`y<hQSS9AETp_*Vyw1U%41fEx|VtmxA6
+N9D~*NjJOO2Lq1K|p1T;NeEph~@ujNW#g=)}6@eKo7vh0CCI?%?$+Gy-dVa=02b=xhG7*jFE3M|2!jI
+@c~ggT&2;92NGq3x4!&R0Sy*xdd4qT~UAgz>2T;08P4umGdMTf`0nx)+7E5WQzgdRqV0<V{8KQQX?5Y
+Rn{<vrp+dtFiDi(8t#Kd*>US;l*ss<;?+g!0#xY@ces?q)lso;$g?Pqmh*E?6<(FfYy2n4J)W3b%Wy}
+sv(GyMSK%z{t$bf0Kczl{~o^MIfkH%cEn-XlTlBq5MJ<YGK;you6AWSi=WAOuP|KAdJ_#PM+qq}OwRZ
+={t%B@$cG|Y5#}7H>W|vpoPiCcpe{bYkVPr+g?n!4>EfK+e<b!#Qf>3Co`n@T4MZ2(Zxgajj^fK~^U-
+78f7eQ1nJSdFsJ#}>O#&P)THZON+4?|}nr0yXsSnz`>HaTme>3(uV}i85G$dlSaGH##zfem91QY-O00
+;o{l@>>!Oo!#-1ONa{4FCWw0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJE72ZfSI1U
+oLQYomNe6<2Dez^H&VKSkzuP0{U?>;6s`X+69^v*md_(6b3DgZ8ntXawt1#(f{5VQj%pkaf9R%+ZsMT
+zV~LR(P(rHxw97YU|a=m4W%n;iv(-uVO1Mjm}&!sS+CJ!72v^C1xa1-h_LpqR6j57eu2W}b;$*kDYbB
+rd(;6z$Fc~6K0<1!@Brayhb_nyo?OkpVgbQ1jxy!WqF@2x9=)~@woHO%N_{(lYyLIJ+T3cQhUi{<<7$
+eyksT%kn!(40*i=5r^QpRVW%}hdRb{Z)A|aGD1vu^=wg{#Q=+`<IwraW3gjw%Il-;`mVXSS~x_Yx^DI
++pe{4>E;??r-6JVj<x=}>!&q#56MUQ$u;bsn6b^3f1Z@KL^H4dIDe;A5y+5MC5ypHe@8w&Y-iN8&7`C
+eW4R4Sb{$b(w;bZ+vV`iY52S!fu(43Gnz=Z9IyN--+0L|J>?N&Iycj&4DRk!H<4y9<82))1F}%WWq<&
+L79s8H-TbGPLGw8>;Z@cMie9Xq2<8G2*I0G9gw|m19l^}!&P)AMnF<iQzt2`TnMa}qhy1mL3i;~O)xH
+^#6UOMAk}z!aYa)@!cw%pSlrZ*`V^9Ra%xxM@!TbHo{E{;U})pZV4snrQ&9x<&K-{zcX8q)w_JPDD7a
+GJPvFx!cG$ttR%rOTiYw~Ma1m0V)PXVmkx$#aN}hgU54AOav&o4TQ-v-*Z8Q#BlW#%iIqukSov7)mP`
+)9pfm1G{M53!rB_HpbB9M^eTSnq8;T~u<ECkPa89<783ul(E)IpTwMaN^rQ>f8sq*Tn>a=EUh&6Z1vx
+AT0i6*<d6TBh$)ZpytO&gxVEawf90Cdja%1|!SrU@Wx&1+H~%!xEFM;TYW;oimL&{PV9;ipzjq>m_?E
+rHj~qr>!=>XV0hb;lsm0`wdNI3iyu?@e|KCebQYiLeCDm*uw`CwsBa8D^`W-olsU0uid=~W2MV3GI42
+~#9i>XwFw!6up$%89w!-mNpM(1yx!kl+nPyek25uVHY!{>;ytffjfxG;p??W{{Pk`i24}EQSb`DU5U>
+5y@L1Z6!LyPx#Q#AiNijgn&gewjUMBR+BqYGcog<;6tC#3NJ0`@fD{CHlH^wVZ1o1>^&gVTeFg&;R+E
+~4^sg+&<Fj&vi{#**n<w=RS<|+x;SHpIIQ=Uy#VWk6K+T5Dbw2E=CWbHR}=0{vUxN5Fod{cY57%ksoy
+PY){=cHt_HB6>M%2JnB;!Iw@KqtMt95;&6pF|zA&6y+@Wn?{`Hz<qqMizEGHt#-W)*ZO_C+XblV+mUl
+C)Z$p%sNi};`uxses;n*ji+uf@f%qG={w-J1s;pLG!=O6YQ9>UD8hK>ah)|cG7Ce2-Ca#i5SYQhDZZI
+ySk2Cai#Sb3wuG9-JgXgG&YvxO2luyMZ$CpGhbq56a0KPE7|AROxh9*zFI!T*N|v91q&r6nvUr1*cd=
+%C%AhxT`)?v06562^v2!kY5ks<+vXi2r*}u1_f3OP2HLqlmI^%z7yw%b(n@(@OH<}d}U$!fC7#nsgI=
+s;`8l1>$!*5+*<)*sz-uW>%?QfOG+-)i&bFcprGkQ-Wxq#t)r+Dg&hT+ghAv-=+{s&M?0|XQR000O8^
+OY7yy9}waLk<7{k2L@QDgXcgaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWny({Y-D9}b1z?FVRL0JaCzMu
+{cjt$^>_ajtb(8tCST$f`-m}ZXx(HpumnMEYz17;piY#;=5$n!yeMv$|9$U0ejIs6$#T;lh6$)6-I4E
+$@0+j7Ms3*L-Lf@pBkt~)+-y~27@uoZwMN|8-&3}bAEcJ5p0c)<rCNx)N*dAdN}ntd#8z&LyAPsXsHV
+{JqlaU}M{{THx8lSuD~eLp+VI+FzeJa6E9=$EN^Ar?o-)JdmAI_6Xv}Ls)V-+rM(8asg+4iX!mihX_6
+oM)%{$R(#v8#T?Gp>OTZ@{(KG6VhrshA1(lFs+!G75Fz7@hFWTmJo_`&Oif55i}ICjqvc&h=hQEb}?V
+AZgtYBt;$KmhQFzfmRlJNP{ULSP{Q0swvV1kn>voV+Fm*o?hVHAt$G+IX&C^DR98<%Gcp-=4EEAo^JV
+n`Yb--DJvM!GVa$l$MX?tud+|9ooYF!|>_NxRdS8Xl*vt=yn<aOl~KA&`9EE09Mt=K|21EhtyU%@<{F
+}eS0XOFNbK!Z?gyha}TkGBQDKRnAO&-8r5zOOJeU{X%A+~uA5d&PClKSoRk&U8nk2~##Z4b=LC5nmSE
+ztmgeqm3=}S{DY>^q_h^LMSR+8SHJl&X46hlO_y#nY8L&0e2vLL`2!NfE5gNX-KLNWcB`52XW=*?l02
+?-K!HB?Y>^Ap9EooiZHuMwMv$1MO+0nr0?Lw99#=o8fcprGxdMfPqEHu8|R#IrRT=YluRHF$7K&9G{)
+`kaQ)r|!8hjpingZBgLU8`z=azS3F4&Rnba6Cw6w8IgRuwJfZwRl5_5g^(RuA|3}pbYH=uqHLzwAutn
+vM#F@*y2?c90~)(umS!PpeUL*vbtvqpfy$m=z%-t?v@o_AsycYIyO?H|Kqh#E$~C{&6*o<f;F29v}LC
+1AzPq0;X7(pL5eUV8Kp1WaXTNEyt1Oqy@`YhPViIuG7{;G-Q1=x9gp-PG6HQHX9JP;cUZ0rdf^cp6+b
+Cik8eg#_3TIZ^W$j5o?2UUCqFsDd!bsy$|CC{Hud-}&;<1uEWJ~JWlHgR0<{qk$!j+H^Ha^9>c0@(;b
+f-i&JLykAGQ}x56C1ID0+;cM334GW(oPi1)ie!N@(4QSF&k<Rbmb{>kKAiDp{3mxYPpl%3N-?)>OpeW
+z(ppqrxT(*Wec%5eshk)II)Iz!{1eDrS~0T#Iy(TXB9eCThyIN=x+B;1lNr4A*q3YIsBTM-0X|8G1fN
+06|ExOZn2GD?X25izSl#6b^0q<}(gp>KSx!oIC=sjLR$7_eig1|3v#BI#Ig=08Kuv!x>=paX(T7jo7F
+U;;`m@PirjWl-dza+yYpCO9ccABG-7~V0((h?kzn}$$E`Mq3eH$9Mqlwqf-`&Cn#7U7NC<_SvQZt-Ff
+_ls=tJ++>oxKUUTgN6bU3g(nReQ;D#u<<M&xfJYhfl@B_pgy;dz8rlGhd%vzygKrRdvZxH25)L2&hHE
+cvLY83!5^&EfdvO0ES*Fvj1PnQiH;V{YyOqDXIo<}2%Ag9KG1vmh;1D3b<S<%Y^A~(0+Lnfr-kZAKIE
+GxPQhcTR1*D54OO>8c|R>Rg@UnqE9oBdpw1_M;*WpGoZUk#o0lr1sYwVA~Z@IICzm!f`C8u>xI3e}S@
+8ph}&qNLRxW8Or?`<7Q1s->Lbc^n*x{4ZwAR#mBrpfb6n$*a@N`T6PDZMw`s@+)U~VE!|2py<o(w^0v
+|?j=$?cJKzg#Gi<--_6)rmaK^N8T&d9hj3d*=kexqmXvxtfnq54PMIQhy7y=HpRXdG=pd{;oKfVzZb2
+K6U|QKd%BCGuRGs93#Yv%u);&80oO_CuQ_#t4@POa8pzoqdt0z$HZ@ISh&)hD+)`b-*$JlOV<4TkZf_
+7@10(}QZ-0H5DnJXx&Hyfp$vj7Le!)^lQj_V+#zhA%p2lIAAyIc)(a92|208Xq64sLL3w3mdH;S=_qF
+UV*|MgYE-VV7TLi2=p4g=QB&6V@LCBjGF&#bcl(>?ZO_p}cDNs>lA)JsK|GQ+`uXkH;9=H>vUDmZZu}
+CJxYfTbVb0Io2<0W47ndp6zzKV)u2Sn$@$j7cX8s`$+wR13CXbfWyKJy98!DiL)4L0;S=FmMZWip(`{
+j)cBrQ0896ViAiV5KCOT#g&`G`SX1h=7JTv2yIbtjxgRNVT=f<zJ=1mHlJ~f=_dZ=<lj&EGA6`?>aMp
+J<R;1oqX6bLzl~{^S{V(e)Y-~DZff`{Y3>J}%<hAkDNF;*GCEH;yLTjIOKy2G#R%OaHWpxFrRJR7oU~
+F1;5bR1`-zP~6WP?c|FFwL)QEEQJ`~aQq*i$zO4t*3IPu$AHqUr+{6EoK9aNao9Zo7?CJ3CIW+uCSh1
+5%<-y%-8Sj;U>8yAU%cI!d%KesuIhGQu(+!kF$0douAljwA7NQ6VLc+kZzi*%$~c#R8b@hul*pI6Q<%
+Pw~|LC44$vqljP^r}Ku?dW%{cn<w?%o3=E2t*U3d03Nh}vU`BXtCectx2=OzgHaG7mo{Rn0fR0f{E9`
+t=6k~QR+MrnOA3^}qI=&;BSKP41@sW;08~j0u}kB5)<!}dv``%&s3zLgx`<8V3ShCbbF62!w6J4@ZU`
+`w#gZDxT4MwN*RfDV;)sFZgPtmvaSTZKij)aCgM<w&(U$(9Isi`)?gt8XrI<uvot{OcpxwCga4#(raD
+`pY2v3@O?{xNUxskUqyKaxqcro?2k9_qN+uu>F{~<isK|LWHxR9V*E2C#9>W9$)krD}fJ~W#rw!Yn=V
+$6jNN_eEt;TKeF!(<wSb2`kihY7u8r~PEyargUkI2Gd<0G!yi7QCq>rir#Rrkpx;5XT-&Pju*M&T~}!
+u}3YH4!MPY`gL@~o2W*HO|-e_D&`3ydCg^A-J{IOfnrUb)X80<o+a-^&yR{YY}#P!$Qu7L!t88x&PG>
+gO4ocfdd!s)#VgoN^xNj>V|P<}lz^ZokAj08!~mib&ZkLMJZI07rw<()9+FFYve3@@b`=%8hhb+cW@|
+Q4F9q!2*l2T<CKg+@9Zz~TaX5?YoXeh$z+(^(^R$EP<*wN-)}nkzE&Mu2TYLlup}~2Wov<M(?)h4p;>
+5_|>TZAN&e{hNdmCP0QYf*ThxgAeosf{HVb<R-gsH0&syl}zT>c%_giQD33A0=x5a;8UvH{ZOqU2a}p
+$9i#Vj}8ZJ3_JXxaQd4?7_3`GH)G4Uu}uf15KKtKc@^0w}n&@|J|F1;mW?yqTH-&tADSQzeh0Yov33C
+Pn{XnoPM}rY!~k@oP0}W(8)>aiKOotmi9>e&X%=%sC4&t^!Ud5$j<w3+j%9+%SE@qKZzFUd&qJ&@go0
+Z6vZ=FTt^jAsI}cSn+H7&5Rc*70UB_9VOs(**HhPJ?O*SJX1MmN)OO|~QHyYqIXEQriYMu^qJPO%#J!
+2J6A%S~+et)psvY^GLT^ILVqbzyWZS{Fe4}YWS+~P9H*V4%Jm)&9{b@x3!F(LWylK8wFxjW<aa+e7Z@
+qU@dh45rsBeQwbfZ2C5PF%`<a_QW?JaSyevbg@JRpDln;gRxr17QA<Ckq+QmNx-h1@YjN91plI&q^{t
+tfMJSAH9*YoT$F!j2;0D|GU64sMmNAjcY9y!TlF(GjJwTGN9}%O^>!ve}lEPIpk*&e6kQ-Vmo&%0qg$
+4OmlS?#G!^oaRnsVC4y#U*q9ig)OzDDYa5KZ4Gm5lPMc{4xid;)(ynrJ$KSA`QNvqtOv^nk-b~?My7o
+xI@GJj_&2nI&a>GeJ2#vZo5W*+t-q)l2!b@l)ueld60^4r`}Dq#cxXm(OK0wH(Ai~eEo2i-1PNYiY$N
+P=ZEVw`2Z$Ok;8iw!CCeZWkVDh(dKGv7@MEJ@I`-g&2U<(`Uwfy7)!th-*Dy{mm1H_!kdL4p!o#|jmr
+=Ak8D$8UYw^>`&`bRW$~%}A$tM!s_yk>)<I*Bl($7mYVld;-r0RQBNWsV17xDPpgP;BOaXgH}hSYC@C
+MQhtM)Cb$+fJV4zCH(4JPdkx(f>DWg<*!GG+t*o?Fv>y?MVqKw{3ztA8SW?0vJD-IkC-CyU1SQ^;4Gq
+SdP7V%OXehrv@-&IZ)F>CIv|{4n%~3DTddj5vWF#un%V(K_>%BZ>`-rdpBg<%nKY~Rbe9&7Or@-5bI{
+2gd~?hmfW}5(Z$|z_x9nK!6w_x%vH?Pdg*FWKfgRMu9#5R@IavpP{W44tyszIg2g!5lvKFeks!K82>y
+werllo=+MK3EL?ikf1>A1-bI49ly!{Qsc5N#1u>OdM(L*&nm8>Hh1`Js*#K&>KO#SxT177Jtyw3+kO}
+r(w{@D0&O3DZvv5{i&vR+*CmG3%<k2E3N4@WNXJ;Ik;^)OI+o3(8&HT3^K&@`Uc13N*MMj^xiI^WH{k
+4K9I<hi4T!M~Lpb;IlN2t0hJKaKrsMw8*J3SHtMf-h#C!%3zHNlq*67j~?LDL6%)d)WwJXlJ!v^zN)Q
+zt=?<TYAv&l204C!ZZ~UQW{|^D+Q$77enIq9Ghn+aqfP^V&KaC#1^_!Swonj<TqR2hOv?uupeG{7_gs
+dh`qS0kX%jy?BIMt=lJ|!gwwNRDq?(~i~u_*29uC}_aHCxFFL-U%rRG@fv7Ei7R}zN&alKDbUR1cMFY
+4lLSmN_aJ_FqGa#F|#~RNNj&2B#cARztb5z#b@CEVHUhip#4*!p!qbB733s6e~1QY-O00;o{l@>=~Dl
+r_F2LJ#SApig<0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJy0RE^v9B8fkCaHuSrH1
+*;;6yedN1kHJzPP13E{k`%2wY)AuvvFMbo4o{?W4EynWq)3XSNZE;iCK35AzJu>jS6y49qF8PCrXxjx
+XuWPbj_?g@s|_bb|DK_e-cd%|CPSNsF58k673HME6`RdwD?#L;uJRIoD+)DjD7<Fq0)S^~@t)ux9a$-
+iT*DSOm=U#R7ME?qIBq!88)iX6G`J>gjhDoLY&fmVcHNenioC~5-gb}b7YJLD6}lz-w5{K5XjPIfg+D
+*k>x$H*;h4iA=Z7-{53HjjG}@iX&1HodL&uzVe^AEL_U1cTa)l@!!KNrc3A`v$MygeYNKeE95|7x+)f
+TW*f6aVCpln6R!|w=~u0fTM!lspDkd5Ae5m^q{HB0C7+z^`k3>e`8$_Oizo1r^gZODYcIiMYDKB<l4)
+2~P~Ek9|}ozCD<?w8|quTlYV_4+!KpjSU%&(W&w<O`yWgQB0E<hXX__(2}0#F!*0as7i8#cjYOVd@Uy
+Ti+xj!VpOu(}qn^rl49zWyCGDW%&l;z=M-gV9E#tIyZqr%CoL(I|q_fj0A84C-{b0@C1F#i3>%CuDm&
+NvGSbI8?f^*eBQv`g%rTX+ZM9sVuH<e*RDy&AJYogHzk(1hX_t6?C*F)h7yDL<<o~`9`sG~;HGr7W+N
+l;@Qb$bO^?@W(v&CPKzZ8ouIi-pLLY0~^@kH+@$k0^N<6J-FGw2(I#`eguGp5C<4i5TBM^_|NQ&!9!T
+~{vU^uq-4e3tX<wm|OJAye8goPDTL3TcJV~fk!{~Y2rXezk7G7hJ+DTMtxswP1;s2;_3uuBHPvAP%=C
+@!*X0)rivI$&i-rjFF@9ogb4pk14#q9>T5BiC+Q6l8O2c9dB-j4yM)De1r;T5xjzQLcN^-vbyK-8>Rm
+909K6vD8bk>-*%2cC2T*`za-%aflmPgT50$k_?tcW)SVl%S?7Ym`YKmma%2_Je7@TWoSrbWA7OD#;O!
+q*s2&H3i>P=$hBi+MStfMW#I@ws0RQS9EwXfio=WNpAD|)ijq=f0#I<ODZE0-VFEwpI}{dCD&Z?)iia
+W@#fcVS7oU6a-ax_Q5Y493N#s$*+|v&guM#Kaa7swMICzAeP#wR{T-Ws6c3FXuWrm2mzg<>1!Vk#rq1
+_>VohGgfsHXsOg80-WGm2JWt%dO85J9@?i48RdWf31%yoWaFD*49iD!I-E5>Kqq3@w|4Q};;0Ju{gDI
+d$BkBVHY6t$5{JsS^H)zznHHN>l2FP1L}rm*;NZmd;hVpsR;es4YXo&w0pns%OpNKo5^S>rnOvqWp2K
+(>HH_kjAg8`pA}BSF+Sg-1uH^ZYsL`P!2$P#RFuI3@%0+P_HD`-7n>9Yv_inNv(JY@)&@iP(7V3#2Nb
+_)xu>)o<L^OhI7)G9cqf<d{nl{?)ym35yoz*5Z!`MXJ)`~jih34@E_gb8jlVYpekE9aW)j*Ko@T+>D^
+l*v5^&#5l!jztd~i3I5vDZ5GSdqdtU{o*Y=4gaLvhz&bukw+SijTs&~6G2dPeaI(<d%@mO{Eb~2c^lh
+^isuSku$=bbtoLv+VB1`-SZOgai#^<O1ZthH1}@G!z;<upoNNn29L$gcs{=pc?ex?b}UUOwt#CmOfD%
+nn72*cX=v{PH77749#34<5VsSjb84yF|=O^xN^t?}rFiReRsJlZ~vBG3zUNAcR^l>^EXkjD#3bqARy&
+m!r-7LJ}ziMj<XEwjVE5Wh=Hg`)g5LT%MlE(vuEW%}1oRlvY6;(EKoH3PQwd)Hsd`kJLTEm{KsDyufu
+^bt@34la+#VR}HEX>CpcLVsjjo!$_yAC6JZ*y1PK%#56U1Pn*V?Hl=X?*W+wq&y5x`j~?}gKfOHpa(;
+HPC>DQzJd^qx+t<d2BSt;RblbXKfJVwx+A0;ty~nCDLWGhe?jb|j;j`O<+1+U$N|NNiJ@(5B(1*|j+G
+8l$V~PCn7B<n|4sA-hkM_||$_$yKv|w5Coc_fU*%SCJ2stG}fL)wbj7@;a+`sX5^0%<7S!`eGi!XT={
+r8`ae|;jO16=?-iQ~!T`EGoq9uE$LYaJX+8Vkt&DLc)l$YCzPZ9;ra#J^|p4|xm7l<E`|n;47QbYch*
+m=jw=kVxuMw=En7i9m(Zm=AH#6Ok2tfW|UJ`+C=j!}PE{t`SQ*pTsu*2uE=wdM$6Tzf1(@gW_?<6<6U
+Ij{F2A3V;qbw<Hx`zs4ViVuLTu#5%Y)G)rkb(JPe+jJUq^(~ss9w0w!vWrnKd1(TDwi6Y06blUYAcHP
+e)^Qv9qD(pb|**=JSl6EYuEAMi?fqEU7egcC})^?nfX>^ooKZ7gpgDdZ`Bau7z2X9V$cSf(!o4NZP*X
+ywr*b#c;tPAn_F*gWihG1r36`JIGg^~o;WzxVjUQoTZG^JXW<ZF}U-&@+G-JROipHhXH<=kxc5-p%|$
+4kD!6{-ma7v!zzi4azw|6k1dZ~f=;4GbIlUjeD}{{iN+{{c`-0|XQR000O8^OY7yV9$Fe1PTBEO)&rf
+DgXcgaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWny({Y-D9}b1!9da%E*MaCz+;+iu&)^<7^v%fJYgrP*v
+AgO!1eFNJ}wV`Lp5h2j!pX(Y3yNP!$VcDMQWp1F~8Iix6W(w8og*b;f}w>fi$Ue(oxWZ7!RcQwm0l5e
+(E%?Z70s$$1kroJa+nLp%BUX=;imHDDtvaHBCt7*|pR)WMf-=^6EE0<NBHu<BW!`UOx_|uk6Ci+edA0
+<X=V6r>fFnzDRncq{u-4y=)MO8MOmb@_!+8YL<q#M?3>4LQ&JDwNq<4v{P73_j8cvU~?Uy})^>#SyL_
+P8Z85D}<uwt2zg==XU3kFU3{reEXq)ijz;CX*#wku~GzH#KALc6qU6b$nj14J*00b-CH9FO*Ajt#(Br
+?$0j3R&4rqLg2FCfnP7S4|`f|<VUcli<asqaYUPCG)+OBi9}u$v}p`vEG?a0f%mi=;u88~SzhKm%i@L
+=t3;YG=?po$s!AI}Tmmr71PDkXe)T++1T={JjKDD^o~M!#TEQ5L-_Ug|#dGL9?`mb5P919bs(VE8h6w
+fF2K=O40`6u9KC@M9=FvUh6wz&B8N$J55DFtGdS>h;CvR_Qt&AwdMXbEIhgcQ9ZvbX~i$JUjy4DZf=&
++5CMu-<2;|$0+kjx-du?WH~WTQmCA2nZ(nur{c*fNp$=(@9JP}|B5*Hf-x{0eX^#hwxBI)qatS{Sk&j
+l}orG&FWAk0*)N#EOFUu@-MzS6f!|r`V85S_OSR`nHt8QXcc?v(ot++M<(jpFxyNj#Z4vfoXKWIEFw3
+_=WR2zk{`~*(-t3oJy3?-X5uh(DsH$_60cuVw@57m^VBx*A^&Q<#oe7@K2vUkz>N|S<T3XK9M^{DE#2
+CMw2h_omXU8!SlRH)6~ITRW<n+dm?#B3g8uEs-ytW#M5b*kxN#f%FhAg7UPqU2U_fyVArXXLNBoa1|f
+YYCD3=sB_2XR;&V|Q5m4iBuQmYA{0)?1@%yLSX;@RxN@EcNM7&hDDuZj)qNH@Qg2?ik7lB<YBwlPFF?
+ib6?!7VCmH9CKJTgXHms;S_`CDwU7+Ow*tIeVgewJK%mOF4Qm_!N|YZOa=hlyjpPL;${%`@LUZBDC_n
+@+-0Hj1JT%6hyOLAeqsL`t9|5EXw|RtYF1T3G<cEPq#3g_k!1T3{8dbe~K;`RH~FU9&QPuEK7AhM7E7
+Sg`~hD8Uw3Cx8PlAGNK6z;?w`{&~m1rOc8q0o7Mmp`dxWWRE~7tR&M^K8rP8k2tRQig_#%72juSQL|0
+;z(yI_@Kz|}O((l9#B?_h2%6`Ta+)|6#X4)zJXJhK0tc;*0PC;2z!5KKNQgqMjP_JF3HRenitqJkxf>
+w;z1|CQDdA*#NrI4F@-xi~QmM3qy$V+;XR602h3CgV-KK)h2&G%t5sG)PxboPt5$buTEF-=*TeiqoIa
+|um32@tDgQibt3Y}^OP3M>Gk0I=pCU8Pt<90($t#FRXpv6N=dT6-u_oHEc==VjR5(5bmh%k^OJ+MYbr
+xYv-$O=*Rur;XL{)$0~f{5F_VA5?1b%$!>ij~&tJj7}%!N;^#Zo?*E=YrHN2qKB0n<$-^UUgMf@Ki^l
+DY5L!wY-lMIP)m&*0fMS3`!=YwZ%|T5&B_7uIkw6xQqyp9$-8|fB_O9P$LCfyiN}B*stf9(CubG>0ZC
+tGbA%%(qoz70g}joU`O+@(PeN4ZJZ)1hj;Z(4B^BM1^PHbi^6nzIkbIMDR4YWUux0V)aWWPG*U7;Nb&
+yH6O=PxI(i2wT^sc8EeX7HN8PVdxg!N6{9gJ@_9cda<Mp#@u`?sbajsX@XuG>B^2Oy6s@GEk1?Y0=$*
+O6k9(MHe4`=UBZ~lILei99JhFZCvNtk9X^wb|iAS0LP7xl-`XV)5iwBG<HjttWX^ofwMi$fK6C6o?b(
+1)r;xC^y;`+HH_EVlcvTVL72A-mAuw4DyKhOus+H5{tn*cujZ-x`+x9cvhI5=S^RDaItk$gqc?|M=_q
+)lK%{{rj5;<#tii_4nd>@!^-hiVudfAPD^c|Bcl-E%JY}y4{?2l<Nni(lrLdjPEsGBHPW9&tBu(9oTJ
+)KBNPWI9*5Bo~JAAR+!Zl&<LjZ&Lnc?6c@A|z_WNtx^adV(mhisE7Jx@_VtuqVD>N_K&~Imo<E8@b;(
+gfA~6mVKThBYIqGeH)fL-?7A@z84ALyAY?)5ve9qzIk(E14--~Xpgd8nyFP1uaB6J6ISYKw*{m)VJIT
+HV+1~17=@`}VNz{!u~%`|aHBertXf*y81bDBf@B$naxx~}RtGG##xs}{5r`1kZdZK-j6!z5F^KZF!5m
+vUC+ZVGzh`v76O%|*1|wfIrzpohT|KmTxiEF%2)_{4ID^2VSe@PLJyD>m@f)Y+0U=}xr4$)8c{MCXsN
+(dV$u*GJ_ES%W!_nuO?)wA|-eRPA_yZ{bF$*-*Z?7dyoEoH~`a94Zsc=LWZTG$R?PNw-Dpfb1@t#1PQ
+T(sf<!woM#rBCc)CR=J#9n4|kt-4Ywn=5|Ox$T?#}g@T>;iOY)+Q)}GNkf^xT`ZEQ$fivfy<2oRFb?=
+m=cdOo+x;GGTJ6I}NfiPTZI%bFY)m+c<uepRFGT0J|6R!i@ZLa<x#Tf+q2Om_=P;@NC7Pjdqk@yi8Hk
+heuC<<}!nV)@i@%s|u_o$hPmHTDiVq>2@?Oo`;@8V|QX5>vUs8XZdF6^-u-s>JEis>B(`a5hCIZ~x#S
+0x~2snp-jzP22%H$TzmGD`nh<z-wy*o}0hA_i%pp%rUeY*>QZ-d3!}IoIb;a#3x>e1sB3$v%cMU3hEg
+ybx!;v-S<D^s-!4u$8%x58~;#em(5c!v!(H-q#VI5A53iY}@<W_Un)D{&so#f7HGIfwtT!OMkZ9vgHn
+ljVr|B!i%v-@*B1BqK7|fOb0(4RQS2u%{@32{z3Z=c3FyYc_(XXRWI|B)=&G+6kRv$PX&_!s^F&LUDH
+pSLcY4?wDt$(nqV#(AEFnMIpqAzTDp1@ACW?~O6}mQ7jbM?io7}#nk1|XV$UQ9{qe@cP4Dj=*uHLm!8
+G%`zXN&_r{S>1l6~uO*lqZ|h8h>zuO+Lq9_x-hGt~9tRASBAF{2G2_No4Jk;*NM7S6z&dl|M+*>4`}V
+$iiJdZOvGFq6&}Fn8XH9(-fkX>Pm`rkZ-<4URTy&(PB)D~?Re2pE~RLJ*jjzL5a1=H$i&?a+Eep)+TS
+AP3zQ-HVGOma`$w=?c2x{^}y%r<3nALk6Jy4{9j(OQK$&4(;9!!TXOOf}l?Wyw2bg3$dscv=E!y$;7;
+_`m6k70SGwkuLff8uK=c#{{m1;0|XQR000O8^OY7ylb<E~B@F-o&o%%6F8}}laA|NaUv_0~WN&gWXmo
+9CHEd~OFLZKcWny({Y-D9}b1!9da%E*-Y<O*KE^v9h8*6XdM)JFU#g>5~DwSC$Uk>O+0TSD}7)a6}aj
+#dnK4L|#EXEY6<C1oCz5Mr^*$271TvD=;I|6NJa^5>LJ2PAbLGZpzo1BqVSrOiBw`Eml#SN*~jO1T8d
+Bp09!ELszXtj&TB|OhkM#-90aK}kql7=%vIoUMHdQz?gYC-Glu0_MQ+miEanX@^$&+2vA)PRPJRu#R;
+fd85%w;T{|8A%#mmm9)zwqZp*Dd>g)pEa$?vSRd>ELnZeSV1a)pqz0jRa)K`97H2R1duFKNcTXI7DZV
+D!nR@@Y0^AzB_gOH|105JmSn3efp<Hh#H!5m@*ahxpzCb`3{c&KqQcv%1l$e(c`~_VUuy#9OUexj%u!
+vW449lKgH$E(*BYIb$!-fep(RLB64Y=q`E3n4(PF1ifCs1{NF+&9%~21w$!bE$8LjC%kp4GX6#y{_f?
+zUPRbaC?UNv=7u{b8#2G^3%B`@=)X0iNxPSWfy<5^kENmFD=nX)*~YF5#lPbLcIUee|I?~3U=zT=8hq
+#49ZSOfJeO3H%Q6m6j1x}qvL{knyBh)S;mJBpil*<^XjD(<3Zu)Ykuw3Xa&S&cjaftx(P#Agd~Sv71@
+)0?<rH|*;co>UB^-fY1K!{E>G>Yv}PpU%F8(bHKln@rS_#x<-LzzTxVseBj)l&8Tg0&2h`$tmXqR<^-
++mjEA@%P%acXFpE}Tq#?@>SRS$$8pG6zM6|=pOb9^o|0Uob2M(vHW#VFzzYE*gahWnORN_f6<+FF8$e
+S80DapNb&r^?FtKuCWzP>~;RqEcYgnug;8+~qUtLdHx^%l`MS6;_l?4>7F&@zk7#)HqoJBNE3kbDPy=
+}o}9g7&CPh0-30u_VUD0*T_bW3cA?k%h8U1$>oP7`EPGhTmYRYvpd->eF4HL5$QH{P$Zg61MW7^cdKR
+moZXCd(o2;Dz?$=)!yv8ftOP>s)fu9OpnQg?|?(hL+iiV6t0NPV2hrKm%~pz<^V*eK=)t7Z$H4<PF4<
+$e54^IqLybcadD?FnE$G4}(B*!GNz`MSwzsL|Fk!gSE~s&-#1~y+RjJx_+Fckb}L5`f?FvNW@K*do<N
+oTBk9uahZY_Kb-w?dindub8^IA2IQzm5UmzlVQRDI>>xgY#fbz*JP`kT32%UF_#h=)ndT2dYmNFaG{d
+A`Ji|TybHq_G&}T}f<S7ZIE0Z6|^VwXRW7a2wDL_X)req~Sn<jb)_iL;#Of&?BO5iRl8y?QEP(a7R`1
+G%j!*ZC^0lfEf>jk@st|MNU=`q5J3~FPt96}3f0<je*tjh$r09e@g-Ys{K1icwTq`M4C8ByDbg2x4d{
+fFW9J;D<_DY}C#A1aktxYmY#fyavlc|MkOz^ncU@IS+|q{X`3#?{^zmXPR_@Q3HQW38_z;bD1gyFsDl
+`EFhy)=@7tBzrWLS-{wgY@@yS;rJMn&GGTeVar9ZO}Xat?jSYI@?ZwVY--4p<$X;bwsL~V%v9K2x)H~
+cwOf3yYX%do;2T;eYhB^B^=8ynijjZ_4fFQwps9>Tc!A}Gf{bpeve|~u`tBlD&jItqJ7k_!_LVHUIy#
+b%7{4m`^&WG-fb~{8nPYEBWIXlTmsQh5Sf>ADlYfA1Cqzpf6mmV`JdzzM(Xcq?0?t&4)nfU48UM_dzG
+6tef!>OD1)q6|m=`tv9nBkNgnT4XBWS!fux$+NI;~Uoyb!7Sv;U!P*fEiPi0f^>&8)?d@WVD977wq7I
+<3CYW>^zoL%zU*h^YEOc`F%aJ6?L(gdd|;=Ly<{LxZSVf7jda^0)|xK9;3nM{>YDx^S6_Qfh%E;&iB<
+GMIS0-pub0PSQ4`v8_VExm!3IL3CfEn__Q66exNp{6B6=THj+^-)?If?$%C}=|R4(GxWyk9vPm`I|6z
+=Rd*ZwQ3TPKGAqLBuI*P#>4{FK3jIXe#e!Y5F2Y1v&DEk5q9?9PDCi^=95+cBM;hI|z3byU)(3Tt9(e
+e-Uk>T3>%#`ndRW8B<@@xGoh#_3$O0L%Z`TnS@&4!%5$nSvuB$^c$mceF*A~zZs9`MBW>6-l=t)cAZe
+^8%*HpW9F6yjX(Tz;ihB+FmZN*^dH$SNIK;pPaL<P<jgxkW1IWu|guRspo_OJWIY;}lFBOz_S2tPE1T
+lsnN-SV(Q%GBehfpsy%|B>0+qZX#&$!HpWOb&iP`n8g;9JS&c^R;X#LNP9nj3;{Vs+!1Cfw5@Ev-%}Q
+r~B?wrz54Ae&0?SRcQB~>qgbfEZb})Ia)*Wt$QM4#@e4^^DRx-+spUwl&PVp)HC<R8wxRJByrVS^?NP
+ISLcW!KS+h*twBfMbe`|dQN>*M;NfcL&uYZpQ$o$$d(S)1mCO68E!Dy_OKNAs@|djka*-ewe}40nc-A
+1i+l_i~$T4#04`eEg?_0JrYfWLXu_nZx3<>PI0sk18n5y<5`16Q=Qx!VkT|K{^JGFIEvMOP%^#s(jYt
+P6JukjMz+md+b`)OOY?rvnZzCzm9KwMaD;#phZh&F7^-`Ut>CAD57IKC3*A#3pc?LTb$$J_l;Hka|H^
+SR0#ksj&njXSw3w=wOp--tf#9M0PXI<mMC>2O|jG7K2Tnk^U!MtsroI3Y+Yden~HU`~0oj`ott5tV$V
+Q}-pk@qeA@IM0oHWr4mnH-<JIJ=9h^+^Kgg9O_DU>+{|RA(gh^fH>|F3<~8gpMR|!a&t1;?(cUAi-67
+2LbDX_Xv`g<JVt>01Bh7gD26Gm>0CX2&0ud~3#>1EDGwkA9PV^YE1JOHuyq2|{<u0o43F^rFVC`Q?aS
+)&8^UyRl0Fll@zvk^bCOW|VK!5&#to*R+(aaiygrFXHU39JIhjL_2@upDY^@~w!px-TkdmTW{(dH0MX
+PF9K@f9l=yRvu5-?6Z?by17eIlXVDqDl=YWt1*d92qP__mQ=Icl5}z|&2&Yq>sE1I*S>hLKXItEi2K7
+a$IFRW6!%7`F$YtZ}#iQ5{TgI^>+B7&;cBR(;^yMMn--S)Xfh%z*u#Y9X8^((owVg;_^XMy4BkagFcN
+rjUB+T_Ls0*cR-%0Ko`cy3mV%b#mSEGgTcP2WS@lfj|!mo+4D9@vecMc{%mE^FdCcM|Vuvek*RxB>nD
+8I*#&;y1ZqDVK23xi^o?1{uOj)Gmb|o0R6-OGLA?oApIn`wnh{<A3z4Sj{2xvgg0S;=g)el4GG(9>z!
+`mgSmSs&YkD=!)4e3W%f-!NIUd9;Jt+%XlBy*Vc}8oU`30vIK~$vs@hq)Rnyd3<jc^I%6mv|%xHR4>-
+dtL!>j^gODlNeRxsY|)=p!)O{B--J<W;(=!5Y?`348ERu;j@mwzAKlV`4upI!S#yPm!Dpf#oG7uay{9
+E8s9OSUVEw0EqPk9jt4U>^>RO-S|uGkbRlO=I<J`w8{)=Yb#yPAf)hCZuX@D2}nIuN8PbA+MP{zN?Pq
+-qRf(QU=AJll!u|g-ST90iW?=T7!gaqsG<UVd2uCM#Yc`1FqW0X_+{{tYi?*NLL7gPsE|QHAVZe0(Im
+pjSOO!c%&K&SUl-lFgYo2xFm<QLw%enyh@xymI{{*N|2eG@)IIwXOj2*I!o5-6f~@6J5N=otqYXB!v(
+oNt7B-Uo0<XUJ#t|JM7{4Ait3Nfmk=CIj3Z)%SP*SeH%7KY2mdU@Hqjf1z8bfcG@O!;zrKEVaVk&XP0
+7*V_zA9XF!z3JmRUNWL|K8I-Qy7+lef|j92VN~EOoMIi!wCV6G+i2!}<0!n30zYaw6vK#*1k%l`n=zZ
+%z`?iz~V7bdr<>?D|kB0c6eum$Ym!p8nO8P}S|*6urs{F(Faqke^V6Ihp=?a&q$gFVn%Z5k8$q&SbW}
+Fx4N|1%XElD2)J^9`R`~NNVJ3qCsLB429Rrz>S|?fZ--@RZNfE8FY#hKVzUI%~mTW_A<c)GL*YRjIFY
+nAuiuue2zcA{pHtpXYq&gzn_1KUw!;|{_0aK<MG|^KN(sI2Ks0|4uRCCZb{H6IG&JKV&fKKsZG7z)Ql
+3m`z1##tkez4okGBA1walwqTZecwSO#Nk-Q3UqYJKQmUzYvCX_Nu0g<@z&AD;>9!#7$9cqi?loPV1n)
+!j<L;XS(RZyT3p(W<u@RvRA(#i+f&APUKr!ZQex9zNkK&D~PJ`U8d5J~{r<YGMM3{9~vZ;3!+?;m!OA
+DPs7I#aTKpqZfN^t|QZ9x)PjlJG6A<!@=fO44zm4=u=f(ofja$)N?fZ<F9}c(}zUcA7wQToJm1Z7-(z
+H3^>&`9k+Y2wmx#R2&yFdr?2QVUJHZchCk<i>hin9dgj47YSBU4;@Tk#Xb2iP)h>@6aWAK2mtey7Dt~
+peKm##004#-001li003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?VUukY>bYEXCaCyyGTW{
+Mo6n@vQI8_l;>TF@yV=xye(rnnn+G6#(4yj=<5^b}gMUAAAdPV>H&LMS|>^j@a1SFQpbLV$1bQp#&Ay
+rcH)C!e@+Q43ArQ`-SN`tUmGb{2f*qv78b_bc*Y`EsRg*%aF1{ngYIiJCn=MAm8<GBHmA(=>-#0BJ6h
+C`uIc*--lJ>UxWl;Ur$1Ftm+f~A5~b7na(Aj$B+<Q><}90oam0DCAn8S3>OREeu75|v~s5c@*%J=xc1
+SGS+gMTpQGiNs6X`or&_DXGfu$ySxeXcn`r6oX=(8LkDBXy!B6b94lCu{(0KQo#%uCGk^lORqZ50?lo
+yb4Qg|aYPSBQA&a^41-{!)gIRCO^Hl=y#_QuX%uvef=kQS{`(BWe=iK$IfF76sml0TI@!_$!Fr9pA@2
+fi!Y#M0i{T8yqR=9@^p>eTeQ+|JUeF}769&IBw;@08WwjYV`-DX)CGoC;pt8;!Ol5hJ)D~kXjb#|PdZ
+V;LntC_m8&*oYcJc#<9B<+kOA!tW`0_Of0;GbT0}&A}@z`h3oD8GmTD(>{E<S&L_kh7S@ujmEBl<HZ0
+CY|{mCSfd-!m;#X&j79jU#vvb_chmkajL|Fl9mZ22Nb99Oe$QIoo^q5q^o);ZgIyN}&;1h%m!ZhQY83
+EADxmz&kIFXWf9(bI)XnMlj7UgW;Y(DO9>dMR+C&eogiw7x)RU-uyZU#hAT5uz`qb88|^6{JD>rJpz#
+RrN^vDyNT%h?&~+eKNre)z>nm$5BEGIVJ^iTipdec8<uj2?sD~zQzAboedmtkgrI9LXz8jz+WAK52ZZ
+9pIT0sg?`W5tdsA4k$HdOM&b6##MxQLi%HU`BUPsW;_CtbH(N(FbTFL{kqL2a)+Z99JM7V{J%H;vU^0
+KPHM_g}I_^RB+AWyalG{z_0kSKjbTsLruD@C;0Kz`A@CQ?0MCdS^+O-qt1i)RRfT)u4db!m|<stZsJ&
+-k)unoF+*gcl~cFd<w(v|iKNdOd68#66C6RUOyshzR?e7)z9fl`5(F+6~dHdNc#mZ)!Cw<V3EdEh7yZ
+mtD6Oiv}5eXM7E;t6#U$o^3#v={!puxKG*9RQR)vXgJQZz(f0#31jsrV~{<<0G0R45poA~i$PoIl0Jn
+GA3j{d>+AQ|zd`Dn%nrwwnR}-ULM0h0aR|muCjra<?L-dxj_yek>`&Jei(egG)HkWqk%j@QgbadB;-V
+Jaz6D|{O>qv&@$YKKG)u8gF5S=&`Pre$oPy^Wy3)`XngBe)U9Bcs=bvM0!)dDor{$ZFzJ;q^);1a2il
+)SUS75R3EDaKPWKM#W57(kfQN}C_S8)eEW^l%+dn9Cxf`i~PgD>9jOBfjGwBTx(d{QEhM)KmXBY`L4*
+w^VCsq5egOaHTl9tVDOi+qP_N1V3^C3^t3*=@_7&QL8UN>$pTw5=;RwNu|;!1rT>oIqSp`{U5_&N6M6
+Y)ejG?+bgN_W@|0;z+%9wSS0&N&#&d0$#Xs{e5Ej$)cS;lV-!*ggc7`aWzp#CZ+p0RALIvH{vs&fFC_
+ML1avPGnTp<@sDBOlR6eLqHQ}RRGl(6vo2#d%Z{t8#Ps-jL>@tP7Nfc!FU$+4FN6@DSBRQ9L2+MW1-9
+=N@WbG^8BT!W7Oyr2BI9Ob2So}dHalWcI>WIu-}WUEUnf4knDiSBiiw)I%|I%=^EumQe%_`N_9`PMEx
+{Yq-b8UciI7Ip^kH==9s?a0`6KXY1dh*~p5w@Agf<eU0X7MoHcpShsu$Jpj)+F!xiE{5`0+73f}rY<2
+0(fWhRe-7mF|WysuA_z(Ug~oqx4Xm6BGxdOT(RKW8O3PuGF4d|A29vHc#er3^>f^Q`yt+j`1;FuT<*W
+$%#67y4DrttrY1m*%Z};-J!Gm)tjs3pKq_p{E#q1|6YW2h9_=l_0#wC42HflkKt&Ydkhy|53mFfS0_%
+LMZ!O}+;}Q8rWpQZVE?~iZC#s;!LjNvkpiv16G?E6Mc?!*KBwuOux!Gv<LTHL>AsQuM~mK>i9ejW`|6
+l69IsVBG4vM4tmA_mn4KzdD&4Vr4qLpsYmw2fSxicBDynNj*tKIALCpjv#CxjK*IFwbg}>+ZZ#OhbTK
+7|(?=9BfXdIir`-C6!YUcGpii3XuP)h>@6aWAK2mtey7Dvs)G_L{%003qi001cf003}la4%nWWo~3|a
+xZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?VVqtS-E^vA6SW9mkHxRz>uOK#;RjUf^Rj7Nh6(DFM2aVD+FoFfW
+OWBN9T!BkDaRUGM&hWM5zT_x5<WOF;BxgQ;GaQbF+{$`K@_c(x2g&lB@ZG+aiqPw(t`3Ujravbozvm6
+F#he@jFY1!z6<18ss!6s8vFH0me$PZ%%SFRKIUL14sa!qm*_<>=a&e<OO_Jm}ttwdWj9jG`m*;O@uGX
+7$PVjs4?$ruEH>-d3(CV+1n?1jL=|17-#pWG;y;=XczI?k*=Lu<Bx{-{%rPUpiD6_7`->f*uhTpSb=6
+zz=f7(@x5`Hb~HQ1LVjuvplfrabm>&uH(Vu|)9anML0e>aWaX4G2aCT!QtS@YbS?OHu;uUFGOGh^+Cs
+r2_}w)5<d)S9+f6t!p+6{>OO-PgQaRcyy#4EAMD3wC}>B`sh>o21i6*mUus<|6CN%#tK2*_Nm}$8F94
+>$;-iF6Y}^)PkLD<blnWdRNjkeJkmH&m^I;K8TV)kSu(300se?_e?uq=sw%+)q{;1P}eYgOTdao%Pjq
+N!)UbdG7JPF8HBbV76BWpnqe7)O^gI!LcD9o6%0-DTxYGSRcn$ys-5y$nRgd&w7c?JYO<NJaMVvBi{J
+#!NG+WwoOc`@E#@YTTn8gQjmWI1Xw#7PXk_*E2UaLc3wh@`7hL6e*05>|{s7Uy6*#!SF^CuZNkTn{7O
+NLJkMf2AMfGC0H^Iotm6Ww?RT*~edb{SP@A)q&8jvnY`kF~jEB={j2t4v7RTO4G?=e8cpxV$I3o7W2>
+8LiJb3&Dpu-Uh?IltvqDVaEX4mmEg6#Rr?>QzS^9Y9CE2G3zQ_~}^}xPvjS-qVDPnq{Z*nh`3>=uo)R
+HOc1*TKBX%bk&Abwm>xN><HMa)d?47Zx#?tyC$2>5)bjfSycw1u(ErML4X)A=2Da~!0hO}%OUk^cQnj
+C_2q4ZX<m<EHVVQg&NGIpJ}V}+(|;frL+a4+k9wrAis`J4T5zOP$D;QC5dCpOw4bi*j;6h+YGc;_b(m
+F#1-~L*eZZEyP+6AsRQ$<^&}IWjadXnviw0!Sf2Vpf%;2INJqbVJ5wPmxp|H{hO$EYJlGo|c@Z~Y^t|
+{cSZ*jWs(7<jK7`9uhwkBg2%K}Tv%(}@PFUtz61{_{g2)FN7nohc6TV-XhmK75QjQm3I+d$3Uhp8A9h
+H`Ll6yvpm(gAfJyH=P*M(E2U^d-N!_51_M%ZRfXoMt^p*eFJSMS8_k9iWwi=qTzP{!e}P0O_5HbF)xD
+rOk#~Hy~Bkg(<LoMwpZR;kx3*i}DPQJWzOOAd&cPQA-K6ZFoP442bJ<o0-P{)4B?7ai`?_#}pk+r|A0
+=p1LsBKQVm_r2$7T5N$2!i#t+*c+0ylZhaj{+mSe@E3O`b9h?lSV<pE_s>OA}x-kBa4JaMNov1$w#|i
+G-o8h}(P|LkBI$By2f+fk%t+Kyn4ZlWY?PqZ9x?u`${1;47UNr%que(YC%ySOuN!O*{g0cpez2*=f;^
+tdmUQwuVwPh!xj9o0ju?`zjt-qN1`H$cN7D+*EAZpclRWQqFuREB|UIY+vwPPN<T4non_3L{-di7h7^
+RWeY2GG#|@<}8B$y`5`oShN-_O$FBx8Q!^(&A5c!C8!^UpO2dzE1VQutZb!9q{2c&h`2AfPIaYg4UZ@
+(LQ_iAqMX@{Q96+d@XOi+Jk^)P!(B+FI~fQ`$qaFf+;FTD&k%Xn2-3a>$@W;K1RAS{l~2sUEAm{loK{
+i1&n=}oD4u$EVRKtg=ii$Cjsrh#MAKHdu$&ub|TgQU6>&U20Ywf!zhud7XWNqpNNCR5@hGmyfREJg40
+i!T{eNlXsW{&XU<mZ2_(KEsk(Mza&_&LBpdkTXeM&)J7lC_S)G~CFHg!&u0^My95}ecF2d(>|1Jyr5%
+_d`y*LQ`llV}(mW<vF&Wg552=_Or9gglVIx9dq8cu!gf%*w1j3?6mO3uKbs)_9T4^T@31QY-O00;o{l
+@>>xuL<M%0RRAv1poji0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7cVTR6WpZ;bWN&RQaCwzf+iu%1
+41M=k=+OoQf53n|1dcldTkRrs+6}`XC^p>+l`oLwph2;JpQI>q9JlGF2Vcm;!y}SXExHB~QV(hnKnP+
+@-wB22PIi1yK#u<yRcy~B>spM4mX%!vgfj(#av21*;dpI$T<NzX$TY-x;**W+sMN-w<Wx#j2!Kb*UqF
+P`5ocYyfpQR%?O}9AUjn381vPpCWoTERDC07ez-x?JZE+a0SZyt_c@&2p&|+b=#H&2n-lkbWil28W$?
+{b?2AL&5SD-y=G++b6j*B+nKKo(ES-Gy<uRKTv&AJLa74Vq?)}qi#DAFH=&5VzdlGU<UlKV6%)_G<f(
+G^W58jsWuln-z<?`h}h_tb8DzGteGkZ6J4{!GkuC#=gT=S-^5@)`@<>6wu63mjw^U5GsqYCenC-CCYP
+jE>MaqgjZSZ5+19vZp0%H|sgESNkiRq4AM`cO~VSwnx9U1Z&#c8eQ@LmvA+M9z?*Ss%w0%LvDgyrO(D
+@)aZL-B;t?LJLEH!$ur}Xshp|xKXp3EZ$IetWRe{0zlO;Vu|G#=gY0cLo8?#Ue1Dijf5L1Cp6u+hF%#
+L_>|2&UW_YSBIKLiln2^fW?fu)q@mYe}V<!|DysCSqPkH<FIY&E!CnR@NKx;3n&pCRJ4gLa9O9KQH00
+0080P~d=M+5NmDCPtJ0PYb004V?f0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1!9da%E*Ma
+CzNWQE%He5PsLMVAU6?Q-!A8Q&tb?5~nDzBt_<78;s0gq_a(g76p=WV+{T8yCWr8wxz@=FkqWrER%QN
+efRN>q&?Sa#b&d4Wh)J{8565gY0LPXQL?fy^WPJei?uL96$z^fk*OSJQdrPjnjo-xa~d#w<^+~0DIv3
+HYZ|m`or8w_Tx2$YhYU)~KDn8()=GQd7NiAI-YZA1J|MCi0;8v6%SMb)f}n=c5CkqnDav%Vh9XxwHR7
+QOW8uNh?51qY(q>|I#|_kDX=R0MQ(URMl5oK@tMsP+KxO!1reOgOB~s9kW@=Rm2~qf0H2w4I?W_1}l)
+j3?I0%9q=4=6WJkbC@RYD^9Xe?m`g{2`((SPBjZ0ytQ%p?Aw^}g+yTDezDaMvxZXQGgsJd9JMA2{bsa
+$^{o{Kn-yGIK(tSE{&ytkOoTVLKUb*B}hTk2qH_y98EpZADf|u6KxQC^eXdYl5c2soF^(X#uTbe62*z
+DkF*o`nu@krU5G$OD-(1I8Dg7VYWgL!@dZ+bOh#Dnd%GrwQ!j#ECx9Z+L4o#P0$o!HBK<_EVlD+{k$l
+0cwlPoaBCx^Ad&7Gh^4TG65jEBm$DDcSf1UJCoeKESSF3T*&2RNhI7dmETmEl6a3^thS(AeHTMAuENO
+0$iYL)9rt}#q$Xhg$4N=_84-8NZ(9Xq;A|zoqx$Gs*qLaA0n(cdCu2+b84g_E#19G0YD4hzR2N~ktQD
+EknazqA+Uz_-pv6t)<c8^#rExQ9pK`UjO3faMN9WQY+x`$0tt7{T3)W`)sFvrktS`;kW)pCzx^Y}9Ij
+h;@%zfTfMlUu7J7n;!G9##hW@!WEZh<32ANug}C<i=rpQIOdpjC+{XDD;sc9a1QYdjN6THrbwEpZ$6{
+zM9N#uCFJQb~|u!^Wh`C^l4D%UpIK;lI#To*$p=uvGDy1w4MC%dHiE|1SlCMH2^M3(koq)IAt7#WpyV
+-_6fJ8q%&$Z#=?q5pJLd*{|$2WRL`z2UHV(9`%(JV9rsG0Qv~s&hC)mn+H)n@n>>JciMtGKgNDGW739
+?N*dM&0V^I`bZ`!?S-*?4rw!~`L9zLlXdL$fb<xB=@n6JQ;JR{n!)tqg*(5fm;^sH`ZkPw76vR)(Gwz
+E5c4%EedF=)^^-+MUBabKfo30ren(T2^WFxG285aKSy3(N}n59|cPG9v6@KzbgRu_Zr^Is)Pbn{)+@-
+p27QhIO=iafapXL?Cq&r@$j<yL~zSd0ZF!&Uz0wrwf%Z35An2rn97Nlukd^XT^ok#-1(lYV%D;MNgTz
+`akwK=$Gwjkc4?Hr?>v?VB0<ZfT{I9sQi9v2wk&tf<Ag)4W!{2^(2khv~0E8KnK(?DKAUBBDcmz@R+_
+qvEFou^fvo&A$Ns3xN=}hzX&`Gip9UEj(Qo_Ogz%4UX9*`{;w>?(;nTbrF$hWaxcJ(QQa8dR><VpU&v
+_5-a7?r^Xco8+nBv(Cyz$z7*7e}J9cvG!qVG6Q@WoWCf%9;Kb4EO*e3EZ3;TQ}2a-Ip9KAiN(ezKL%A
+V`OGZLwP)jc~8@0a*%NVif`_hYgiiuY3tIuKH2T+)pzX#Od<bbmY0m8|FghiLErggE#cP)h>@6aWAK2
+mtey7Dp`@u-Cx?008C)001xm003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?dWo~n5X>)XP
+WnpbDaCwzgO>f&U487-9aNS|fU<BK>Kn_`w!+<62Rs-u$6ozWksbE<$EZJQ#?7vU+N37KLn(AUpA|Kz
+QNGT~)24R?#rYtZFfoHiY46p~SL}@T==03!{;+iYzLn(QrVhja0STLbyvxG1XDD@*>M5@3J<oljwIJt
+WhtbPPOZ1)=<Zg=0p{c5+pxn8ZeK5VYm!LMuk6KrpT{{w{kn;N73HY@ARDp)m(?eFzhAJ$j9-F6?eY!
+@9!_`o!FixysL!=%yOcBOc{6_}x2^FdlYI%g4D(X&V}tsy87e=?C`;nh%!eqnw&13O|&Y=S9SVd!ZTN
+xM$tJ$xyX1Pd^a2v|`R&<dUqA|@fH09@9ai4sCRad`kG@#voySaDTqSi;_2PgK#skvqj)7)z^<OM@r9
+JJ%qUsWDECIxwSBlsy>DwAZ=&O|1FQmBuqi5jOxQ`wsWoedc6kqvQuefe&aJ`CKy5*u6X2ou%Lm_T)4
+|-X(rK$<~>GTG_TN@V6^I<ubMn!wmBwSG_)mHkrV3*%a()WWx?VFk=e-K&}LHvQIT=*R9z?sE7%g4-D
+!eZLN;;6DwQk6&42Lu#ffU?;vxj4;3Y@H5w+#im~@X-8B(<*1T@|iBcE@z4OwZpATs-X?|}BRRa_hdZ
+a=pu<2YaXbbhS_?<j0ozA|ycr=-~xCdsBs7|((?_LAvGzez7c@EgYbHvfFKcwl8s+ky6;#6&5BhvTo(
+>=Uf(pC2sl+O8c?QCo0jh#Dw2#s|u-&R9m&XYgy&jp3k8CKHeSlv@UoKg6-*?8r*M_=di)uVMpp^Qf)
+P4RD^n^QywEm9eU^oZyb2EBf-US{?n8;~<Dut9|Op`BzItH?a(=zW}|X?b{4ulL}Ao+#4kXZwXHdJtU
+_FU!zjUdmLeC+QK%`RpH1O9KQH000080P~d=M>8JS9VrF?0B9Eg04)Fj0B~t=FJE?LZe(wAFKBdaY&C
+3YVlQ-ZWo36^Y-?q5b1!UoZER(9a%E*MaCzle-EZ4A5P$byLAdBDVTw@iY3P={<O3+MrbXwWDFQ>FCE
+DgneSoCW1jGLK-I4krCEDqhr(u3bWZm8Gj`vOPWL=Uh+qJrtEXxQln_6l@x2hJcW|{q-kb>WH#p@~|Z
+N>AtV42{WNh;K0hY$^K((Im9MJ-dsA9@_k9(1N38@8});-M5hjcLi4eVwNGyluHCm{h5u3=njmNw#MX
+O^1>8lx#~?51bBf`GJDgQY;pWg6#;a6rN0J8L9Zqg20FGh$38(o4R854Spm87gXk@dB+6s!I>+iX_d1
+`CF5!0nV9Ni)ErTz1w)TT%U18seq1JG3D)pjFJrJV7gQ<ov95JgZ~tJq?y%AJEaMf|Sr#cKb_qDl(Tl
+HACRoWTZQOMZbf20Zz@L&;K)TxuBsdk*X;J6A+SMtqAc_?gm+Q+BdxntGrh(W-mvxTExAu7KCqGUM9M
+uxeoK>`B@w1_tb}M+E@#6VV>QQM{W~~%)9FL&{TaAa)JtQXvYot{WTcF|F#qjbgc#atoN;Hz)^SV`0j
+Cof9mY;hL%v>aZc^oAeHWZ0ZDXSo{L6M(6nC-wb6vRlNT=WDdR{@8k>-u#4!X5*4;@WoyESnLcHw1a+
+l2dhpRh_Ci?`kNlK4YJ%Et`zsj)Y)JCS*vdgmftZ*)=J@L8uGAdG=Wl3>^*se8PYa)Zy7@8T10n@cgV
+Go%XPQrZGyZ&EU>!N%z=sOuxcE$35DMPo3s=r5^5+#7vd0WK4Fow2y>W1KK8d{C|65%R4z+Z_HCEv+M
+XXbXh`x{ZxK<+&&vf0t5VYd-Q=Ss<m|qawOyx9`;onsQ1nb?x^de3~4NVe?FZ|q9Z1NkHaQd=Lbyy($
+gj!?I~!&;BzxBW?}5O977G?lUblR)Lpe#eFn{Qe{@IjfLxsbjLm{QSUA^X$n`UU&J^{WF*zeE|Ga&*b
+sTpn^gUT^PPphu3;gzu3r?9d+brAGwK&>AkvRC%_hduCG2hyy)eyXZWlqRqSEyex|FPhynP8$a3Ux)l
+hSlj46<?s<%{rOF0Y0+4m5Sf9k$Aid;O^xpbIJxOu<LaT!*WtJ-Ogqjsljl<?=PMfM{uRX5MEFAp-vq
+s4IRX9Ogk=MI|LQ|PMyAm6-hCDZ3vN{7dH^HONNE4`fA0!!}E=EEIVv<+X&zx<ONw{7xE@3ek>d0Znv
+&@M2H}Rl^>}vBcdfXGTg7xh-C~91EwO7wPt}QFjkNb4ZLovg1l5q@)99J6=xl|zPeo}Zg1-w&R|^ZEK
+k$;Ste&#PIPpMclrL}_jgw}>*PtOZl?*O4PfifAFgZ&QoGTZM`x`u-vfI^Sm|Us#)N|Ja0l(v-Liq}_
+tR7l_|oXciRWuIuK|IapWEwUdQq3C^#tI>`*+4|ZhS0`8GTPFxT>IsKAyk_?6>RBN8s;PZ_ZX5%wD|G
+HRQXWT!qs%4mp0{=IRNF+m@^#x+Yr&u7sp4ctzzS>8^C1TuF6)fczh?!l`)`&1|Ck*kAPF%!buB1V5M
+*s-Za+Av(nJrF&VojeF||AH_qM?hgIO;)aa$s~FM;XYikYd9QK*vzdEOMa!_lu?bobl;PVH#~&CQhmD
+&kH#@<U&tiLw=GC0@XcWT0$2~us`7UyN9<88Zx!-??;|-`Y*32-%5pj?)Al)7mM)AAP)vs5!ik!4?W<
+ZKRvfP&92B;ARFnrH8x)U>MP46eakk{C6r}fxPjB75HzQ9#0JTSWUj$P)+;P6}gf!^c&c(^Pq7gMOwG
+S@0V!Qq9i%olfJIWUKNc;EeS0G(QPJAP!pd+pzSZ$S1pyA=NS=O@@|be?xUndj;Ya(jFGhJ0ltrxk{{
+RnQ5^(rlhKMs~y)PoY*!=_Sc)@PKS{m^@6*3*!Ye)SMz#FIn>aK@)}*09y>U+-nMPG49k}%!AFhJI#<
+e;i{X&bxJ;7uG60#JqbJx`g$hms0;mmGtEj9&M}Meu7&tP`7Rqjm@Oa6;{TA0jz1?8+s0u>h1k;k3ue
+!<s%1&Vk~Ff;nF1&5XPzH^#j|&%c=0z-O9KQH000080P~d=M|C+zP`m;F0R9L704o3h0B~t=FJE?LZe
+(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1!gpY;0t2Z7y(mwN>A1+b|G*_g^9MWP>s6wa|wwHEW<vN1AO9T
+`_8W?nGoMNOIaR_P<Xj+p(Oe%Q~1I5_G5g?z^9^l&ncnRE=pA6a``RRw_g2la{<OP_*}ylx)W|lOiRJ
+U~5@I!I=R?xsIX=F<xt)m9oyZ4d?d12fANPm#ZSb$#3(;Dkb^iYPfulh&{8r+x9yDw76SNKi=fUx9j;
+TzyCa)<tdq6PnXl#Dqr4rj<O!Qc%mA5VYZe+8!C+M)jP?`oI?#(7DdsTQ?1EL0equ;11fgr*&s+R9Gw
+y>n-7ewC}It~>g0IXvye7RjWCRpOR|sx4!W&^9g~edmRC{{*jW;U34(Z;u`UNphg%KiOcNoE!yr_YU6
+PJZJfdQ4+<WV)$9eehh+_X)a&ZFXISv?{zCV`)KWHO2AcmmvrsLjQ+U6uc3D2GZFI#{xg{oCPOw4`@l
+SguSNhW!{HTxBPo`f+2)V<J0CSEBW%c1@i`G>rZ`TRv{5HpaOP*#8kIQ9KAhESAgAYUnOAXiGNcrxeQ
+vZ<jYS9iBvlWN<Yw7ziDW6|CCkQZit$j127J4vF$xO4$vFkM|!BH-1pF4oBk?cN9_6}4Ey(Xtq`7qmu
+GM9A=GVecmo^p>uNSH0lNaz1)VjZs+JpTABDgDR{GXWGOL1tsjsZbF@HV6WpOP#9;`d3^>m?M)U|5Th
+~)n&=0o91%^OCgXBVIfj_n-fl%ZEZp(da9Oc@{ats;@3iHho0a(BJAHF#0tc<$W#Gbnp9|Z=*h4s5_}
+|Ty6Wg@Db8!II@YX=NgxF7qGUJ4_QMS~(W^bXqhV=&GzqM;$W(@soslAXul!1WWilqArj}bkzwXD%)e
+2B;`k|3HT8K3)>Cuf(Ut@*LDKd(fO$#Jp0I2y4L@<qhRG>Lu#P)h>@6aWAK2mtey7DuU7-Kd5D007hg
+001Qb003}la4%nWWo~3|axZ9fZEQ7cX<{#Qa%p8RUtei%X>?y-E^v8WjWG_wFbqZaoWcjFH~<m@5)5n
+%>``bO$x_-xwu8v+OOnEXCqI2Z|G;ShYeUh(Xf4o^6Dzo<q*xT~;0ZjvNGPonl$GY>(MD2a7sZ&+ejI
+ZB$`2+4CvdNAGp6(DhsyP6;4{PXPFw!xUe5g(-!o=~TMw_yiHDk<h`}g0aBtY94Nyx11QY-O00;o{l@
+>?zVuh1^0RRA>0{{Rb0001RX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BFJEF|b7d}Yd4-ZOPs1<}g?ImoQ
+&|!bDwTnuQU?ZD5Qw2$RatI&X)GK&@;T7{d+a!&Nt+69w!e4x-r0pU6>wfOuCd5D$f`Ehf!L8zjYA&y
+8SLe^Bx!U8jh4CDBUjR)6^a-uLXF%gg}L(x^+n!AT}ypnjO9uY!P-hpy2EtIz^~bC_Pir!MeblH2qBV
+%vEV#S{+_kq9t-%w6D5SI$e^}Z$m8n6XzVJ56#!TPH8t8Kowt0-{;p-w=|B<$=~)?;MLz=#gf0onf$$
+DKPp&i?9_LR>&?YcGaOJC&sKZ!@O;p(&+aSK3j@G<^<D8tZj((&oljtVhrss^MARbsA03Lx+=0V(0+_
+Ry4AiBKcP_iA{MAI=gaK5$;vdcKH{-N>gQW<?fa{u@n%sV=qoOeXnD3uHfD}0k=k%2<pyM}vMTqlf|o
+8^PtPPe*h*Rq3@Li)e=hNl;6jqRJ=%FF%FuWp4_UF21KlZ#39l`l*sohoZZn{Ma-#V=4x0|XQR000O8
+^OY7yBvqZMrU3u|ngjp<BLDyZaA|NaUv_0~WN&gWXmo9CHEd~OFLZKgWiMo5baH8BE^v9BQp=9pFbur
+=D~OzA!0-nwu!r<<=w(lPD+)oe=|rt886us{{{4zp5<BrCMavT<&Wtz|yBK=Vx*K_ns5RJr2$8|;D7c
+YP=W_+EJzBECS1@|pgch~4jL|qMicU0I!_ne}|0=m5__mcSBL+Lv`iQ;_u_k-EKp9W0`F+5%P_1`{Rz
+lk^UnixaXq+LM?$!CSd@3MCi(PVJy=AQxq3bGX%ABhb&A~*1XCP!%UT?b);Saup^%K5MIjIFp!gk;TL
+mGi6HoifXP}SV~9b6(Bn{2$M;`bOsRGY1H@(w{i62lIG(!~6pHsuXh+L1=3p9EhEPcCZHwo0<HxB|p;
+t;Iv~m?y6`oBDUKeu7fDGF`#ZxDl`3)1US$^Rw>HMERA(CsMJPdJ{aGG@I+>6L6stF|C)sOnKH@N;vd
+q_L`V#YG##7nrz`GylMC^fq(Ra!yY|jo4?_Pyhj$tkGsCJKl7(W$;2UIXHU;B!Q&%=yjvN(Xyjl>OGU
+p~KImM&5q_xoDSP#X3x1Ep|Ezw;`-`r8NB_U#nS6QS15ir?1QY-O00;o{l@>=!Pj1c~0ssK-1^@sb00
+01RX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BFK~G-aCyB|%Wm5+5WMRv7Cup^Ahg#2I;2H=?8&zvAZTe7F
+G`aFNhQwTcd3U($xiw}QJyT$?r>%~gj;7%AZ6Q!-l3F0r_Q<n>fo*J1IqEcfQFvQlQjkOhHBfO)D+Mu
+?Xxygbkvpd8BJqd>FGT41Uv^BUOUXjvIBXn!;Fq!Qezt?yp#v!amM$K5R`}e&tKxtEX!)GydU<(w3**
+!z^lQQZIGcLrSPa*maKxB(731tW7(P3f}I#2l&Mjii$!A&t#!cK&Cpx^z*jHwgd#4dSTyXp{Ht@;iQT
+={5G&!-d$xq&gCq0gcX@VcjDkuTNue6JH^|UmfoId$i1hGHVXf*T@&R1D+ug!COD04v@v#7nW+2NaDd
+Ui8Yl7kdTn5EQB_<tm6inQVgIJWO6ihsIUpK}>m#)>xF)?rUMewc0Ckzaq-dDWzJ}~~c^beyP8#{+Bo
+vZKG;5r&30OTQJz&V(9syONR9mBKS)+Q9Qf*_WX<o+)QlpH%3{0oJnSl{R-X4)2Tq?n2A-LA?8I<AV9
+$!khf%$|#ha!C|wk9U8`mUx@9%ii<U@I+Q6ff%N8b8-<`C|{1GF&Xv)J*I=3B&>;IU8$NztlIKb4Z(*
+BK5d_6@}@@S_uthZqyS1raAhK<?x_5rLPpJUWL~B4b^y~H-yGH6_4pr>^=}gOM@eEY^t*xi#{WEcbpy
+YOO!gB{O9KQH000080P~d=NA-WC^;il30Lmi(03ZMW0B~t=FJE?LZe(wAFKlmPYi4O|WiMY}X>MtBUt
+cb8dCgkukJ~m9|E|A+aIj}P=T*|82OJjNpy{On7HET9nxG%jK%gah%~%p0l3K6-`_2p>qWoItvIkTMc
+30AHUYy5|ql`wQtXAb}A#0JZx9pO=t+`z&-CWAH5^DiJTFZEKCymluhI9-Um^{ngi7%bh!mzhnyHqXv
+KK~c%+FBUHwyI-%y{@<L@Q1vTi$(S;tdi|5lZI8Q6$}<|wif!*F16qln{xxNWNR$msfB;L)~ZB~^csY
+IF#J|rW!Z}tFGvw~&5-kJP;4?bsu8H-cuMcB-d<&So-@3VIOip)JeksCabJqHWiM+~V2f--)h}3U38w
+cvXKV}#)<ng)Ta%AdMx+P_+S6Q0-v0UL*s{6s+ya-{h`oik$`aK$3xAPm?k(9QP2t#pRqu5tqHA(3YQ
+YU!dzQP7@O;fdFCy4TyQKF~%ZJoB$?`0vp0FE>F{4&|%dB$rb|Xu{7FspTE(J5Z5zK`V=ygQZ0|DR#y
+kDyBcF9`BO4YSiXq2~t4V^KgD3oExV80-?QFznUR<0ortZak{Y))JH3k8T&t%1-*Wq#3GX-GE+Yr)Ez
+Lxe-tF$LymA5KV3w0NO^j;!HzO>!~04y?p$5aGb#W$Og_)B`3V&3s(5rK&4K&ZUq`ne|Z)obZM%+#M=
+|yXUO{Mn<)9=?C)O2^*YyAGX3VBIy`0Y{L!goejA(Sn@{M-^lEg*e{=Vme$R;WM|oFjkV7C<e7I!Hi8
+P7QKS{cqO+YAMZsjVRvOCU+^D*<qHy<F=AWCbiGQLj8}3wm?xcXR@c{%=_`B0}E$88G!*vUUap|IKOR
+H3Evg`@_L+O<Z_P4*h{Ws%n1va&pbaN-PK__A{jq+Hdst$^MCKqJ=k{fQVo<xEf8+lh0?%8O1MLvxb`
+S3kt@7}BT^x&wB+`R`;x55@Jr0>2EN0JAEft3E;8+esvMF9Z>`*O`bjw!;h%*K=nH}Ap4s<U`INQm()
+jhS&D-$<E+E#5*vg++AZqHC9fY*(S6A>D7QO6v5%@BjT(TG2n*lm9cM2NJqa9e>I)mn)>lBvupeG>lR
+F{qyN!lDpCqJ0sq>;;6ZVmBc(g^*t@hO6k`iVj!6wcpanPC-n2Qj)Lj|*V15YQl3Gmh-PgKR(oHrV<C
+FTgeZH@41D-iCirNXM3%iF?@(67HcQdEepEs<p^vB%8p=nB@TN0>U@B;5Esz76k&2KF=K497colYSi5
+i`4&mi0nS@e5fwukkPdB}sigPTA=@d4Eb(!O2!J|7Qh?ivu5_Q56`Ye(m}-3j}4#go!;b}E?Tx!}><*
+}0I2|F&$1HE)uzJe&=@4Ju>cG`te1xd%@0vJ1WYoH&YdchyhS_!=Wzt^QUcT(Dta;VA|&dqf@&n&UKh
+40A%0hz9Hg(>fgE%vzLkAxmGVkN`kyU<tJeaE)xGEsBW|^<oB6>~iL-fT*q!%Fn1`K_$9=12%i7rJ4o
+16EKhZ48Z((bctkw&eMakP}3Y01t$1w_yV~Xv`g23xQH$XsD{5M_+!fc$woxA!*QvM^;pB<HbfZV;bg
+iaURI7j(7@lxYq^L<^sd6o9)e09MJ4O;!c&RE=JU^@w0JP?kJ&#E&J$eM5(-<TJjl8eOyY5UhY><A0|
+5n45ekZiBTfZca63fGv=a0KYaCVsa7NEYGsJJpiJcBLBqs!SjV>?lN9=-49Gicz=l2VL^{d?Z_BfAzH
+vlgAhhhKxlgV&{r&(O`1QJw!=>(&Nn76QR^%gKX#O(m!`_49_13=RmoA7{;!&6{uIrtkFQIK6;x*oED
+G=>1(04}LM^HQ|o(2;OJwG6UY)Uvc}f<=O25gZ$i9y8yopvvQeOL`Amx^E1=nUNx{i6OTK7^T+0PAfP
+CY_znYfrkU0xR++QkmR%|B4I(oLe-u@y{HAPnk#?4j>->I9yp{0aPH1nP<@=sLbJoY>DCa`uDM}a471
+EJEq0>evy$z^hr|FppS(sF=ECIogF<zX^5=8ex%v?PHWpAq{$~c1pRp!Rr#qF(7jG;+9#F>GAI2fB^q
+*1XUmgbJcs*q|i@<^=HjRWXT0shEMjU5wl$93Slej~mskaoAVWzVbiTw#a*yH_zJ)hy~Lk9$Eepub0Z
+?{{pA2PAR>UXn9xJPP_$;JJe2AP~nqfb~c0W!o7S1|agjVD|Zh<;4fAFol@1F)FMrV*9oRzPY{!-!H7
+2_ac`M;qymL~wD_tZUIgQ39YglHmp$hw0|j_(#)z=fi9ecmBv;zdH;#@wypkoQz*)yV&hfmF_2NC)9o
+~V9x-^xq_)lxm4NzgEM8po5CNHPJ7gLx@Cx|+@@oiP=h~w&U!=@=MYIN(($x{4Kxe@xwPMmeK2Ct)u<
+~^)FWWxPgxNeKdQDxft1>V(j&_L2l}+Qr%dO!u-B$r<ACh2B2)?c>2K7}?FhadNWV`I@l&`hA5Dj3DB
+sJ|Vtv3^uiWUz3v<OTG~$p8{i1JPML~`$3it~<P*KeGy5?C;rSBbCMhY$JVkv5<j+3$mq|_)#?5AAc!
+rxDyt~QnNT87zVj(<!>H-18iN7oDEM~&$>o$##*a8<bihoLRV{D6|Lz$K!j6a61f3K(Z$Sq<HzzW(0r
+A>w37Qwq-Jl7^$Jt%G*bjv>2RsIN3(-YhYtpgb&b3heL*_z7x*FxGfx99ju<3}bayV(+31rGYCB7B&)
+k3E&mRjXTWF2~0P5NT}#zfwX~9X*&md_(~Yc8AvN~Al0LdLPU<5;K*6QKjY94Z3Qv$!&96FfZc5=Cb<
+6N^H1mporK8~WQ3ZdCFCc5r)2148Xy-ejMT^kpaAju?&?zzRg0GRPucY~`_2!NEyKO;CE?x2Q8?U;J^
+>&4)RP1&#LHwDDK4aNCW^yOI)WbxB}kOl;5GWn7#t2z{SHnPZk98Ly@xQX5M4JAta2`EX}7*By>h3Z&
+S1Q;R&0Q(!u`H+(Kic2`*b)PKOqr|5S)1pBr#IMygW^2T9yZnDS7IOB#Tm#Jk<bBSqH_Aj^*tk-_m^f
+Z{gg*r2B|>8>IOP_YUZf_;)~)541=<JRtij7rTl5-@?bkj|Y$BWb*}75f6BINR$s1N&P$|{3=I#eCMZ
+7`v($G{}q6G&B4)t@?^PWqUCcubmLMt{i+-V`rYq@ck)Yp+ac}<HSLlP_*d?jk1L}I;e9~z6GdM^cs;
+#h9`w7uLXaXHqyt3u|4SiS`ja<n{OpeFXS!>jHCyb?<KD*S$#?}F;Rq&qo==BE6C4i?b`q({;t5)tQ2
+!J^73Lew*T%iiQYrWxw>H@d>H<TNeqI;Df4DpT_jGT`0lbA*oQ|fKk2chBPniL+pC{~dAc;M?+F|SsO
+(UCpmf=Ur@PSepjAAIm;dA8iLXQ}rsB3qsjLCRS&juAf>&IRv>Kd_Y4zPn~!+MrwVH=YFHj!n20Z>Z=
+1QY-O00;o{l@>=r5E(Zg0{{S73IG5k0001RX>c!Jc4cm4Z*nheZ)0m_X>4ULY-w(5Y;R+0W@&6?E^v9
+3R?BYNFc7@!D;6q(NT{nOIR>zc7HEI~c^n#`M}f9hHW5m6NUDzizDrV)t(TqfVu|JsXJ=<g<w_Qirg>
+#5g=q@B*h*!9EwvPt!PLJ8K|ODHfz7kjL6F<<q=||i<m&Yo(>MeBbjP^BYz9x#*w^~;ac^JziWh>Dm6
+-+59Y2LamhF9190V)Dw1x*>zB*ijZMTYVU2qtNb_UpLg|yU2V35-~s5CFvz>F;T3bsmaQJFo+90Z|7H
+*pfU_{CxY!UD{@+gxf{par^&Ke%oNW@Fi7Hos$n2sZruMj7Tr@~z?uBaI@TIZa7~`I9W0Z0s;<f7{W8
+sFyAzsY?D<VOp|+ln!^xFiuuNYK-DG0N+=*HE{p@TPdl!K#FmVM-_^~-2GbBRz%$(T<8!kpubrh9N2l
+%Bt!GWHI&kT%>r1NwewPWocjI1u*_0BS3|h}j}qYdS>y#=brWkLzqYgvjCUJrhZj|0H|Pt^?Hz8NJA(
+~Vne&HLgqx(Zeoz-z8=W{m4a?(ZtYcE^j=0O$7wP%WNRd1>>oL!Z%MyH<5`agxUn1=A@*hGbtEIqi!u
+86J0Y+(gX%d+zYQxNcVocMiTQ!KK8Z_Z^xEb=R<=Y??nXh@tgja1VDr!MHO%=PZb<G?Ik^hUA3Ma7EQ
+aT&1Wiq-#A)Qm@;4h>;o1*tjMAXhY-ZFB%+ZS{+@{9p%zmf?X*NqdfgQTy@QGdjfF-O}Ed_GX6ZRq+y
+jl%i?c8nW)0NMl5pE}`5sNo2`W;yk-I~6_|?lRsovzbnHx_Zs<33hIUD7?_NFw9Sa_y6blon`2Xtp(y
+XI&&a>zrM1k-BohkYxB|k`pvB?y1FoP{q0)hWHZ!_&|WDD1=4Nk40d#h*bXM9E=8_WFH#hb-3RS0-6(
+4VTUEzYLQ%t$zwJK>zH@E#(Bu=_`7eQHJ5hfkEJ<Jsy4*v76FYyTp?YhMj^I|bgCafIyt<=#jpC%Na7
+ff?6HrmV1lBlX*T_jBr-IHL?C#_};=3fzy*ef%emR|V?~`O&<Nr`g0|XQR000O8^OY7y-nujra037U8
+VmpcB>(^baA|NaUv_0~WN&gWY;R+0W@&6?FKugNX>x3DV{2w<Y-KKRd8Jn0Yuhjoe$QWVIE30Pc<pN;
+fo-%fShqp@Fc`xqvAk%prBssF=D**aWGiuOr-bPxl5_Xn?@ubUsvygX+SZz98Hj49v<0>@s;n)~{CN(
+!_$!Q1ax!Totf+XqSEk*Web{oXrJ76%GLW{o@fWq;eq@Hvfp+&ylsuoqS7qtgY#;V?@DfjGM%8vcf$s
+WHYR>Y#*GeXnb;*o@hixvj14i6nE4g*rQ54Y^DDXd6?O}}wR><GLFgnfGR_T3;LQXj61*SzvVY4hYye
+#IhV|FtKyCK6oUp(m=qp;q;#aAU;JB_WdUoMwWlHtXPe^*9ToV>Y8E)hQR;hvQk-k{Ys`1EWPA%FwO6
+b@MumXtu4PL2S+mu$@ughMUGbIrZ9WEHANtB$aQO~Z+>Qm>FXSqxH{lsroRC?TDPDMg<VUa#ikE1H>f
+W@aXW8F-Y9GL~#y0Mc0w<o^+NB2Tb8=}aBP*3UJju_?t!?=+t=IqeSfFO>z7119tEUFpV8e+~#-<{5o
+!Al(1)p~9kg6L|^Ox1G2H<h$V3WCW1Fy2>w1P&n7=&A~-%+(Ds=y~fueH}D>Au9JhMmkl)<jb2yT>iu
+o6PS{%&&Cp3-CEdVY*L-K;+vA<qO7}sK#?UL|wG<_BlnNCmKW~2Z<S+*)nmX2l#lq8HdUyBZ^W#$=mD
+aQ+F*oo$PY0zCx1ACGDvUMp=@=!$@Ixku_OKsD<u%7^K#uuwu4$Sc!^UYlW!$SHs9vA-JYvG|<C`Fkn
+vH81qHn9(xr#0v)yPwAfpg@z<3e>m;aHhuRI%UYr)vt2NbHnKl7$U3j#tUC@jB7+nd#hvG4h}z>rT-x
+5A4m*T*Xbv`7Wj^^o7Rbp28<pm$|&M{sQJ9TvrFW<v`&ljm^l68g9lzSNIo192M@DdZ9t}m%-V|AM{b
+}xa^JC9HN(1l%(rYnPVmm{`k2GJg<3)&U>Ce8c%F|8XSi%Oy<iIpUkh1Exd@@nv!Cu=TLok*}z{$7r~
+r=b+zWo<~g>)K_gH{93z&|U@c1_w|!g3GP)Y#;*hgLLQb;glRFsJAWp_AhlJcP0u`TDfM{&!`Z$}%Bb
+zJixvfJRDEWEEyVal(Xr57|SE9y1(gv@(rUd66RM7Z+qB&fce^5&U1QY-O00;o{l@>>&%^(s+2mk<07
+ytkx0001RX>c!Jc4cm4Z*nheZ)0m_X>4ULaA{<0Z)0m_X>4UKaCxOz?{4F`5&v%VAqWSH*gZ$drq|+{
+HbJv#(gw&Si^T0+(H1f-Q8BTS=p(5(F8X2Lp<mkv=xg**_DMQ3B=yIZlU=M3ATq@n&iv*-_t?SD4_Km
+7Sw5by%4P@O;mTmJ$Cfh9Rq`Q|xrmnQ!5%DKsO4JA$AxA8`!72_I{NO@$A7`UU$b|jWLL5*#hU$gF3N
+9~Yr9b8@8C6~)kvlhug-|cbRi9czf23kjLPhaYjMKXs$vCSgDlJSgcTrB2LE}P9xBaJX{?s>$_i$Ye5
+LdUU{#f=(69olLKlXqjDEbQ)7gXP&8`$|<W|DkjZ8#o#DqNwZE)e3DGi)*4jdRaY+227X%-@7^YtstB
+A~lf3|2KS?V5?_r545jDM&1fWiCY;u?HcD58}vft}iZcA1<T!4_61rEJMwN<uW(XU@#mG*^LLB&BcO0
+NmV&Uq7bD8zE6^~Kb7*Cy}$lFF$6jW50DN9nN|gh<E*lk7IBP(N?XR~M&$rp?0ydheqEXHX4dtq(7II
+Nhn0o!Y@@(!gc82vh~Rmy1y9$wAkqnmeg&%F`>nEJ8D77qJrk0&_2A+Zj!=~y4+cLQBsn(*H6u>Is6K
+0a60~dpYQq9%+rS0jM2ackjy~+bm$<D|y5PB6D0$J?ncLuDvn98SnM3xR==V*{8T_*Zi<QXBK+wQSE=
+xhKC!Xyje1}iO09h}!O29D~uowZKaq!cyZWkQvl>Agl4f0qowAbP6?9AzK+QDYus6wDX`{OU!MxKH%s
+mQ=tKu#OSBO~%`!cgrA9FJxd>FE{E(ahGF==4^V!4UXReqbR%7?!GXA6%(gI@j?`m|6-=!7_nRXFOl=
+wL!m6M2i!hgjS?T4y0T^M)juP3Q@1jT~Wt1RVp>w7%)*L!mW{CA?gUFk5T-FVh(3)kQfML>j?1o_^$_
+V|6cE><*JlFRU-Cu_09%p)F=kLh)WAZ^yohb>+%db7{;1yJ8jg78nj+`*8<|atXaoMi!VsklC}{-nmD
+JR?2}?DjAb%w4u@b#Dy<>F@-<tCJoWAvo@r|%eh$QSoM3XJTur;gGh}eH!J|i=0J_`XuSd)a(gZ^6b5
+$V;eV}`_LiDv{x+fV7o6V-W60=$0vO$4yTyUE#1bJ*tURK2%iioia!&sIbJfgu1T9cZMea2Y!5Q;}((
+QC%+)Vo#fNG&{k;_v~nBU}-fbGLCb!QgWNjIC2=hXp5C)HIg{cJUGm1s1ui=1`-Zs0y$ym4zBsC$+Bj
+4UrQTf+oDdX-JCF69{1)4kG~k@0x=s(-_x03;I54!Wj+t3g<I+bkgz4n&mgOSdGN11tNp)(Iv8a-Cdx
+~T0-5$;IyhQG&`q0Ct_*Y-NPjz3&{{xwpoHgupUvJMz5fk=<@Q%>xb#~ZhZEdP)*Rv*z{ynrz6I`=WD
+Q%$%R-#J)27#_40THMvpo`8L;aYWrNig@|zF2Tf0vf4gCu=UD87QGpmA+4PRun{S1AQ1+d?kA$#+M<U
+7pRegqVy&rz_`NR5VXs5|Cvk-3!c<?_8MAsLP=KYArZ4&NSO{2fJaw>j<Bu`!<O3P(fdb!akjMB#AXC
+1*cpmoVFvH5X8_`|7~G`_z~2@VOU-dfH5RAe-JG>n6Ugh&~DS7Ku~|2j@cNTTNX{&UUWC?VGiri-AM6
+?yrNlAYtt(eTa6Lw+rYl{;p{c{Bz&ULx1d2`<fME$5EPqjj4je2`}~}b8v(YBuk$K-{DuPHlEjK+Y!{
+2wvHj!On@O_k6xjIXq*lgP-+1Oyx5?;u2#F(>o#Qme@q*japoR2*OF_ID4jN~gBwpaMjEFwP+@@j_JK
+B6h0rqTGQyS`hLR4$noG@?U2n8*2`_#9Mr9}ff|J8=gHA3R^l&F<Pdt|->m&#@n_(WA&02B?IB{|~r8
+MJ4q;~D}Kw<yA6lDju3ES6xjPcd-`i`n)iyB5qx1p0g_MrDcNPNXWO|Jg<z>cF|fCDE~65+2Nsu8clC
+$0~5RkoS5UR?uBk`@*8n*fXf$Jm_gWD01>OhQ{I%MSVZ!^QRWfhZG&hDt>uVJw|MIm&aja#JY`t71jN
+WwPL!Csycamo)FSdepq|vm~0WrH)xpPllBqrok2N$Z~Mxc`iCkKyCU68o=oZLi`&hBN0v9lIJ5IVTcx
+B1FLz59-1R)B9}aYSqzdW!Tk@RA=V~%4FSrE01%MMJj}>}-m6Ov$J11zVPICrHM8AM*Q5l|Y49BCk#T
+7>j_}fG*=h&3x-{pBva}<lXf}HP`ZLcfvEgDJH*0xpl;p~Y(3txmfNukr7J7TZ9a-TA+*@n=S~ds+WX
+J&R+lEgc{|ywMKK`e^bJ!?TH5W`LBqJtHA0zHaa&o$2X<u~ogf#|ioaOw{0Hz}tU+&_I`^)nw`zNCxm
+p@+Ig!dor?{07YGH&P!hyx%yQos84+d&J~I7{6u-jfcR11AlWahuwaW@(2wgOs+v+V__F>fEq5_{x;<
+73qLoDP8jr&m1qsCYY=ft<a!&q3O@{)9LXqC$;>Cv@1d<twWe1+}~l*T}<mZbHeOOfmHHI(A8{^$UB3
+Jrq<j#XoctS4H{sa{;kBm0VFV^j;SK@zE&J=#g#9w{bhJ(fyM1T0pweRt$PU~Kwsdkt`L(VwhVa1)oC
+i&va*I%Av-k}Pr}e$p0Uv9($&{WguLnNs7r65u+ll9Wo)G(IHM=hI(ZE6cA#`IXxGVIQ{|3n^7iiXcK
+Slb+iuONZ5;d`P)h>@6aWAK2mtey7Dwk&dG-$l005*F001Tc003}la4%nWWo~3|axZLeV{2w<Y-KNVa
+cpUHWo&O_Yi4O|WiD`e?N~`~+%^=x>sJsm3`?!5+RZKK*yxs1;-XD}pePD~Bfaquro;(Jc|8BVkBfHA
+=J?P<2^i6kZ~gebM=~oZN|I%(TGtY?jIeSeq$YH!1g|w@?sq~8_QVtuRS<Xy%}Qv#g$e>KcW(oNqpny
+k3JBmihm9upw1nTJ6mqai7DQ<o1S{h{ZM?o0`Qy)2K|=0@Hb0=SpC5MSM`G7Mp<hvBL_cu}wAeYZAjm
+mYihTUzJ=0+3-Z2hQV=#8k!(X8k6*{IGjOceuTq_7iDONqCVPK^+C|xWu!h2DHn<`+1AhC++EQ=KIRY
+EpYuM?uzCI~1NpJa_;YZu(YXnLR4Zy2`Ux3{;}A=)1Wd!<APEBv~O&ywy%@s4s#cGhnLMS3ugHigcJ5
+lc-Bnem2!_$`cv3nK*-`cgwy;qH%BdP{N8YO&QQ!_@BsQ+6XdG_ABRLMS)J`Cu(unKYVBY(&!XQJL>3
+9taDS=3FR<P;0VMSDaOkk+U^iEr2(UW<r?HWZ|RgCGFBsUpR`t86udAs)Af2=L}h#L^B!9I_M&7KXRi
+xw3g<zR6_P}XsO$3rSJ?n(mx7wtP3QIrc2y=v)L*fh;}24_O9SxS3plx=4&dWH$TM5A&q_o^>p^C#9^
+0_?>!A_zi$93kGWIcYemi2VB_dCkwC-c34Ed~nI4r<(>66cGd);wQ>laphV>_-wycu*Dv7&}L-Q{+li
+-QEr?>cNQ(XQF2~!DeNkeE=wDUr`T>S4Y<*LZcoHt?Dd+8xDk6FGY*F(%&li#*s1$zumxxJ;#q;s9#^
+me7E^y6aeuF*KKi(aI$@jbb@?jmxGI7F3Jbh|b+Y`XoZA+0Pl)&Nr(x2IARznxU;g*niOTTcmE+7T^W
+kH<2Zh`R_nKC(&y78<L((q1~Y1GyBn>`VHz+RLTB?i1xToaW^&A$M_@91|TK9LeOsh~tTgy(FB$_m?#
+wn23)8F#EE_VwKp!)<8tXcMj{d;04Izc%x%<NxmWWrgAdk+clue;URc;Dl|Q{WCjZ<whGPpNLQe-HF6
+$J1u=IHRb=F2<mk<`7#(2uVgp!;AMrwAm~F^I9<WHYNdg!C#9Px7f>%omz!bDO;P8M1JUGsP?dza3<0
+SaZIH{+bF>NHxxFCPk8wBSwTw%Bc0*8ex5H~C>Y@)L|67ZH?4Ljwmga3=T9fKlXEcREi+Xw@YZLab~+
+tG}{<OTUacS|sPpw^=1g*#0sSu)eftB3I(962)m35%3$&dF&`Jrk4DwTtUskWygQCLZ&GnNO24By{iX
+6ovky(KB`CFB>PZl$i3c6}ZA<^HPQ6@<cUT8s^Dy9@Pe|X-!qT%@$M74ZMTlB@Zf{9>QVpY>dK7Wwwa
+ANq;P3I{iRLweT4@Pl$M=JI8*`$pgdoc(5Yuwkr;>i45kwOP)k-;<L<>rgvR9=|15yYTwFDHdTAU7-c
+eRJfmgiU%-3qy9an<pZk~j5-Z5qS?&n-h6w+;RPnJ|n05yjN48yy%x7vt={1ex_p_noOHQHl``y7lcl
+g3$|36`r@TdS@p0n@v{XEXezyH)fntgYR>*enH>Y%qHd87F6>wktXR0Bg-+^-G5Nypu5rhV*4^SBtwm
+_H-mmBWCC|0O66h!fj>&m_!g7yl7fQ&3yrme{%<Epo31+Y8#p*VoV9mhmr8O9KQH000080P~d=M^{rt
+Y&-)105u8#04D$d0B~t=FJE?LZe(wAFKlmPYi4O|WiNAiZER_7Yiw_0Yi4O|WiD`eomN|K(=Zf%&#yR
+S+LnkCHXbJx1QH0OjUjCk4}c;kJz1@~ac4Vp`S<wPxim?;YFL%(IOp=6?_8Wh$u*>DQJG3%ngU;Mq%y
+!VEk$K8_0K`joEyHzb}w}h6r`TCTG6%4SD%>18PM(v;{q2m_%01SHrtz;9=yc8H76@G3t+fD3WaP@d#
+yMKa>2BQyLv5nxpJnh<<47t<@Ccaq!VCyj#k=8U{F{&s5CDhKzr@zU7T3Sz^N>-u)*_^n>39yied&EW
+|lKBO9Ft4`Ms)aG{)ncf0t$Jjn)b1YJ(~o0gDrFGfI*q?u1*k$+HeSU-7P&jo=3CFJt-YRriAQld9y8
+6{aOyBg}1160(;#jc6<o|GNCs2hQ@xiYshj91QWR++a}&QA1UhNYpY_Lm^emXwnNCRqv|WWtGbbjv0j
+8ER2&}NR81>r1+HM#=y7R&q_%(GNl;TxKmhBSh#=6bZ*%*2&X!P(+MxVgC<BzRJ-FEN@;+i239V*a|(
+~lG|!y{EEg%AH74Bqod|ew)be|{xEj=ZXYzFiE9eBaOR8Y1!lBZMJ*%DFCUi#W$fV#oP*R8#Zjyn{AU
+o}{71MUcG)?YASjJOoq<9G1=j!%ul&c=$HQ{jZxGcdZD*?D+b%wCT^S=lSS!Dv>2-oNzCN?ac4gycvI
+wc|7$Wc7?)%)}1Ks@_EfC5S2hmtv3?|U^B+TmADUZK!<$knY`qS{hD+6@r0NuYZ*kJLv~PPRtcqSy~$
+WNK1UlL))kX(nzIHXGP7ZfHExxby=deAd)-g+6|o2(JHLsO}O>Ln}V0csLX7@&OL6jVL_TL^AV-;A`y
+V_>LlUiPHjc6Ez8WdwFpv(-aC^B$uOJ+8f}wgLIWs5xXY(iRTL8K}uKe4{};=O;vZ}UZFsH{kSgQX(5
+Y~!1*@Qsb^{2W1imb9&f27#g5i}G@%%nf2NB&5B~F@^&pz>hRo7PcCs4Qc)*#RWL4P@vYoNjanJ`iV!
+@NuU7uVZv9lY=|Cn+e|GQ_vN>i2Y6`ju~H(HM*vuY~`Wlv(C$8bdzhEH4@4QFTW))=JZFrWVbP)h>@6
+aWAK2mtey7DtF|o!UnN001!r001HY003}la4%nWWo~3|axZXUV{2h&X>MmPUteKjZ*_EEUoLQYeN@|S
+(?Ae?_g9QuRRYO4Z7DAxBtlw*MU|jBrK%9JHul(AvEE(w64!svtS^uV3BSa%bLPxBv*Vn>O6lJ4X7Aw
+Y;_@19lw_iU-1)k8(OGl`cf3Stu>vnEG~o6KOU+99nKB7{Lu0v;aFt%bivWnHRQ&QCG4#qq%X*N?fwx
+E$+(OL-0zQ|h9dHSyYPH}@mIxhp`=QN5k`l$!M4@($5t<QRQ&b=0fQ^Fp&gr*{Mb~vH8-%9HGz&3;?B
+Xt4-LCW7S0r|TKS+Vrg27*&8?w3UfoalGvK=WDtOI3$H3li?RM6TP?zn6cu&Q<)Ge|5Ix6bgLcPE3Im
+66|L0F8=Ch?hBJc?=(yd6p-{@tAEtZyvVrxV*n#uD9834x4*eZPqv0HruQz`UK1M6MV_mHwhq(iQ3?^
+HbFm$<Y9zZg?Z(OCjx3U3bGnYUh|TC$;Pt=p-~4klI(y+({dXw(vrN2SOjmmV{SP4$5a~CMzv5BwRhf
+NQH1`=IAA-g1o<q+I3eo?9%vKLhD%2UvJ9kAR1{1Is(cUMBcLZP3dfxwvN#EW^=->cKZoA%`Smca+#E
+9R_R554=E0V6F{h`?pTlzA`Jv!P&@r_oB{A*HPu9>x;^;_0$gGq>@Qw?*f3(9;zxLEv!dQbglng2ZJS
+?K6O>F3<{4S*xU+7-5#ZiOk$Vn)<e3QN&PG>{m@Z_5e9VTxfR}HxjcF3h%4aI-#2E>0p*1&Z%i_zlA-
+@&<!;T-<TiGBl6O9KQH000080P~d=M_AS8w)_DA0Ez<u03iSX0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rF
+UukY>bYEXCaCv2tO>dh(5QgvkiV?mf62)oMQ==YS+f`8`ECr`gR8?q!0gnW`bU#e|?>n&AbyP0wJ3IT
+#JF~lhwdzlrnuCS=yN{n>qoimn$gHbt8!UnaJW`3$U<FQAsKFi(R=p_sH}6F76SW~F;Xb~Dn=v7rx5C
+>8CY+RmPMkn03(g=T$Usdk0=|{lTObLg>iU+1ED;8>hpEgAi5c-aBUE7pXA92nS=HBhAiUv&wf*Piau
+|kKOh#j+n`P^h%<?f=Z_{l1fz3|o&$2~hK;s`rn%gW+AbR#vih>=sVgRK<G#Yu&s?qD9$&zd$FsinL(
+8!dEjMY>)`)*KYXWZ{~0*^{a2v->-SqNWOS&~J}cuDr(ch7rxSv@_i(tWbcVD|*;UAjs3$u4E}4OZzZ
+{7BMG1ju9JGWgc((LbA{afDcn^U4t48K{*vWO^*Arjq-TjS~$*qmHO0w*x)uj?8$GhV4~MX{jSC>=gO
+0sW_;$>LAZ+XPw48AL}b^K@>){+-L4}#H}A`#759-l9mt1a)bt+5a+oll(YW`Hv#a+VP_9Y=Xu2I^88
+M{#7<D_m-EefVO-Y<ee%MRZO8bGbDDho=-+Fe2e(1MWm+b|7x*>hg_m<-G>Qw+%L(F_7mSx@w_$YV%q
+IQz#io~6{qTX`!Cz2I0|XQR000O8^OY7yXGgu8umS)8<pcl#AOHXWaA|NaUv_0~WN&gWaA9L>VP|P>X
+D?r4Z*6d4bS`jtbyVMPn=lZ5=dU;=4^1aVx^xdyraok8r=eBJRMJ(|G$98LV$ImpHlb+R|Gu-KQL-O?
+0sB6G_ubtGV_2%X)4beSm|eVi3)f0AkwapeqOid@7{e{kP#VmkkvVFxJA_5eGI|V=5Wb=|TuGQk7cg;
+v(GVJ)zbA%HHBhk*q_WT$Bnobz-~s`kGpsFe37M*D!I{huT5fkf%|H?n#p6Jsww4i^5x%Bfaf}1j3*K
+2<&!^M2Z6oG|MoO1c(SywNHeTMWlAG5g*2C|mKx07TQ^Pftx$S_}q$OipQYcspN&_o3Qp_r+wbk5mS%
+zR#VOyq=SaNQx=G(@eH0nhe)q4z}Rxt^qMFMd$f)9%%PD0{%h&P|s_ZxUv+}$l!oA@Sy^&KqNtLu0Zu
+UEAD2#eJte2!PwAt1Gh(%^HgoqiI@T?;XH{Ynr|1Qe<lWNOTK!858$mJKTrO0`EVsT`<LSKPQk8j_b2
+i{KTv%=(kRii(0ls|wPzXl$b~O<jJaEwHUog6d5BeMqJ6xuXrC)?8Y;ATviA1j7x}nIH(hJkH?Cy+`}
+rPbrs$`gZXhuC8E4;miMrF9VWW2=)Wt&B<p^jJOM+EAs$oxz@`vwOx(IkysrDC=C8Hx@BY&e-x6c91H
+Krv_i`=!Hk)>w-8==+<8wi8jbvOsgYT7q9*|l@BPf+a6dY^F@}x%90vOtca>Y`Y#J;L$#;M5`%aWJIG
+!VOej+PYR4(E3rmDJu>yX4xTuCx8?ol|t{+LT+nY7&V5-qconi6G{np=aSuXdnwX<23)CjOM)vz_*2=
+!ILZYiTO6P18vxObC)y^rLWJe(L*1t~i3brIBnTJ>{fFAArG8+)XKZp+4jf;mo2l6VqMlPtHP^oQLr9
+*Lm<8P)h>@6aWAK2mtey7Duj<Iki;+000pO001Na003}la4%nWWo~3|axZXUV{2h&X>MmPUvqSFbz^j
+Oa%FQaaCzNRO>dh(5WVv&M)t*)2u`D(67@iBM@5aWWSmAhp#=um3h$EL#l-)<v)FF@p%kTu9?AulnfL
+b1+qXlQ%6hAKQ5v|p{`47UQm`t6$TWFwoS`#>2cDwPm_Z{lRA5R3zh)`D_7xX?pwe6lxbd!GWE%$i(q
+Mc?3|ra2nr%Ty1C2(a;2LsXA>eC@wE-?5mFv3VOr!`cH)XfYp2Q=H=RJi?45MyF{WY!f<33<J!KE?v?
+PSunt;af{o>ax8+BNCPLol6((fo?UcI}U%LajmJOT!h7nQVd8q$OnuDXdrvQUNOzQq4%KwN>13QMjOG
+Zd#_0STe4S;z?spj5;Wzd5;^&DkfmyM-W5<`07VN<PyhIu==)mT)~sST>9ZEm`AW!!gLYNf>p2xX>|{
+N_zd5JaOMJ%O|%WZ*2?N9k=$B{ne{6|JQ0w~ogiIf%5$F5T%u@Lfl$Z|Dna8wjcU!cJxERRGGeLtnj2
+=iKNmxJPOjt{;y7<iqcD!`{!$rWiIx@38SmCEjlSWQ)`ePeVF(~nOX@gIS~1cdh@1;<wj-JR#MF#$9S
+7*iFeiG2wTk1B#ww=*>>q`+DS3E+4vrxu)8)GmTO3)^U448Etjr$fg0${lqVHLy@dSb&hTuydgzOU9*
+xNAbIw)$??!|HER~(;+7QG7W#rqIt(d)xXH77F_y&}#L0*iOgD8zzdx52~dI?mw>zPzPn{5wGB{eKap
+tACxOb3ginbdLBRa&&Py`h#=<`BReK5_FysO43Da#?CKLO9KQH000080P~d=M~s4+wG|lv0IzKT03!e
+Z0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rZaAjj@W@%+|b1ras?LBLA+eVV#^($a%t4u01BKz{?bfz4|arR
+V~lT(&At}R*74#5$L2m}}a6vbH1Z@+%b06hbMq7-Lyn+>ayh~S{7r>9@j-P73(US^AR9#3aw@NEC-7s
+1zAs**`?S}rdy%bnew-Qe3e(rKY5!7`obJSb;6I9jL({$qX_2H)$vh_f_!7VZaq9MCgA^#=b15Z2i;n
+5%V=W@WG}G=PYU;4)5hpl>3*D1$f+qHMlMVwFZZSjFXxrZGT50P%MNA{&<qMk^S<fNz&_T%Z`?Sy?U)
+KL32RT7`-b4YPdudBTH=&%Zr>`Re5K)e}ILhW{;1bWsGk{%INKu*~r~Pz%5%Qe(g{QL7-!12xSx{8na
+&>ne}SIGqlIBD*YCD%SvI5*KA2kC&xGD2o}aR}KJDsWj*vod(CJz2K{()8o@2z<783_P<~M?QQVx=*^
+p>lefpOPJ`EPf|svPzCM0?{Q3mGeG?p={5|-e<CCw4fd(?cH2P+dBmRJ7j3m?(q}Qp|4g!}MBUCJO6k
+o;>tSg-^)l>)5>{{n3EGJm#d>$9Lkp-YP0Vqj4k4sh3lg6ULoy$C%2NxHY%W|3Piwm4T%gaEGi!6b4U
+hwxJEd4r0u)|=H$7u;Kq!9w$*{R-$?4R^omL-LK5SKc49ss#{=I{cUMwND6q;ERCDAx;J42g>_=DJi-
+qKd-2acvmYykYou7<{XzDq2@x7b-7cmK}OEI8Lus5>HN<bUN>Uuac#HmFHPLIM@l`)9dx<zefO>2U|h
+|$yN%OJp=L$k@@o?C}!CbL@d{rNYE_!?$vj}7hmj$6(~FLSE|tUtnPyPK(p}ZtC#-^4~8HXKkBF)06G
+NZOZ*YGq0CiO!Yi|ELeD4q67~xoUtIJHom?99<r47oM#F}}mJ`F2Ibez`rc-7GmO9|*3N_%9D{XKHgS
+YTo1<I^-tQ{cN+KO%sBQ6o6&_QU6#mrPO+io=~z_GzKZk~~c&PCi83;mP90k#6A@=jF7z|k6XKt&Z4z
+|~BXNCR%DDv>_rD)UgRfHA57EC)UM(^4h(#yZu%FV;AGE6f*p2Fj^iSGdFUxdv1~pKr7E9*Xex6Z?9g
+aAB(%ER1kcNs_JD3X~2b+vT+3D$ofFU4q8y#B|6E*n#k=E`NJ$H}FK1rD$I*s-S!4L%D>}NtSlu{<SX
+AfTVzB%)&hdK~D(?8bAXZutiM<W|ZXbp_qdE0MpfY=4h?A#`UebLSm-Xo2ZWvQL=mnI4Qt67<u?FSeY
+zM)@ICV7DuxR7;eNJi*q(B#%aCf8jXK(oT9OufK1ei-!TJL#b2OAs4@dAN5O}WJ3_4EG%hQ_MifT9-b
+TvIRT<3y3@PA?Tu=24u(8UcSsz~?R0CmOkt1n*{W^Yf6@!=2u#~-H-y9wUk3Z~xeB28`0|OkMV6anjF
+5!Sr0inJ#2TdDJ^K7~3_w4d}gAnchqCXgR4>o4MZLF9Yye0ssn+nY_6UL&ch&)$8Rc{I67g&iLC$<U|
+Y14Ya;Kt&;9sIEWDF0X!5oZ)X51dzF*P1B7ns9N*3C>`hz&;8|5%kGPn+Cn-uz~##PXU1s&pr+gD=Zv
+1JmUqi^~DAJm8v<I8+VgnH+b#tW5BbqK^t5bix;5v;b~2jy_%IA80{W|0fImFJ6_yi<blNC;ig522be
+znI&}Qefh#U$zIGqrhhD41(2|a!xjSl(O}@A_L7G*aWaTlO4fGsLww_d@bYcX$1$PGr5BqkgJ}IRnk9
+cwXRnl~W3-TvTH`4S`7fq+Stc-&t?V`!)Fie%D9ns^n6ddC~id?-?Tbi?MaXyeD5oJ1$^o3Ec*1zFz?
+oN6Xxwa>*zIXC6e+)e8*mK*6k7oA$fxjs8ymHGtx6vU>lO#2D&-Tx|V93R81o5;J#0Kxx0n;!Vzs}KY
+vr9Ba3Z(S_E3rh6rvt&tTcj~Hl>2&-yS49#ydp6~zX^`W<Jx;-s~k$^^6VNyciG2Xvu`b>2XKOnpTs|
+Fc3WX;RYkX8W{+FRH6X+aINRgsnYz{joI-aYFQa8iC(eKTqxUS_@1X}Me5AwCv!3v=a9*phJOLAQZlT
+*si{o~&TwsWU;yRK%zGo_cF=dAa)FdOJHE<jt;0~uaB!d6Y`hqCVqh(wQU`aNOBR&YgbXD91k1vvm9c
+>S*3*$m?C^_Ti3}jzvRh!?aq|lNuM^OnrG&t;&jE*9#-QogpfL)qMvJrxfLyCYUJ^I=Y^!d<{+=dPg?
+YBXn#ep7HYV21WYu2$&CLk9hLj?PrLQbBvpPkn<x?17Ep(h6P4qR4g2$ppi*-3nxFZD-<=Ae(}U_voS
+ikO#hs=CIrPm<6ckghQ-sOys#?lH`zQjF&caCG=0H-5;Aq$i<RU|#c8eUkGeAI-(&qP8Eca^>!WU`bc
+axxj|O-rm(}&~y-XgJUo?%e;&0hqPpnpOrR25K!f$YEYB3fD_w*I!B}MJqDr{bX<v9w<JLC90(Di+yG
+Eu*=Y+b+B%{t33B@hTu{JUv@4&D7wpH%ZC`Q_AlW#mw2+VY)??mKEfzYRG!1{fvR51rXK`j`b*T>X*D
+4YfHRws5wm{;uov%76f{dIy*3%dR=1qDbaoZPEODT#?_Yl{EVRTNi6-OXo*RQpetqBX4hJUSTvxlx)<
+H2TLX_7$BSQ3X;^nfZv4k8(|CVj`Dk^;nUlVG`UrIc*JDh7s}0uRr$nh=yqW;+!=&SwcjVHbbbU0Qkq
+f5+$+lA0h38}Zrzw*_6QFbhRPVB2yXq78`?U7vGv8B+S8`s;nMSN+|8@!fN)`Vae~(e0zr?eo#?A^iL
+Ha4_h6$KZVEEa~ir_r>`h5dEDFeq0tN5aY0bImIBURx{v~Vxc17`55zRPJ~c=c3DC75~52_My*obKud
+K#eUaTCGYakS)HqurD=}8}p0}^}FESKXAN+yh-@khE)$7w&!7Y9{{_Dx>H?LkEoxb8RgSJ3e4Qez3d)
+A}yvH;q9M2~^<E*d{Qr=Ktxc!3^HbHE`DnC2Q)ZO54B4h-jQ|7G*z$4`2oay-<|Q<O8Rk6{=)$>t-KL
+6T33w046doN<tCFtx)m$L5hi48edvt0DGdE_SMFzaBQ-qkB&0@xa3-eSXhLn|$@Kc@H1D*at52;j4V$
+`Zi9uL$jG=zWve8-oLaW@nw}~4p+Ha6!=SpEIwK2$rk7}1<@JZE{4vT#d~<!O_Uum%wSlVwBN&+{W7}
+s>=}F?2?f$=$NDeYoaVQ9D41YUv0MkWQYWXF3>sJ+T|{bu-U-@0_KCpnXaNStJ{*3dB3l<=jHSstTUC
+gqDXX}cab*YWVUjf6x?Rd?vx|e<4YrTHV$8dl?7elIz&7HL$`xyKFTt%U^jyJ8qhPKT6<?5d8JqNZwR
+}@J62WOgO@$j*0~;@~d?rek=tLp$z#Cdqn$V?Ib5;Ep&qugn2<0{AwwOS+QlzmJ4FHlg?8mq$gxWp5l
++MEh!eS3*#aU0v#KBwSq9YL8Vc2Mkvb$FItZ=&}C-zg2ThhW^><H{If$h%OcTKWLwK1Y$w8BKOtZy~J
+w4(L1DI#%+emfApp!9hQq#B1p1N(s%MJi;l>2!=nY3SNykkWw*%-e8UXi**8`0LJAJAVD5WzvJQ{U`r
+<{@J6!3puG?WFl>9KRguPc^~qhgFPDEb;g8|{-eO0k~`m_d7fOn56_#}5=?bFzKw44=vF0*8GO;Dx&?
+sv&vmR<&GY765NRn*Y7JVc)xMsrC^s$$UVLIGc8eruZ2I`#Euejy56mORrMU(`riIV2)Xr!zEY?#Vqb
+uOmYdlL6$5-xQ6Hb{;IgbjQPI*|4=umZ^0+$|v&DE9W$JqjFi&TsDZIe;++^i1}a#vb&-7aB!2NmCcc
+BY>EY}t@ORZj*8Zns0e@pIey^nLh#kCb3Vykeb}>IMx^cqd;xOlNU9TukK*)_@pK2xPkqkO(Ce=S<>h
+T-s1v3p5*lE%X#KQSEx=7gwnS9fqU@tKUtteCDh!=)bd(j^Hyg{}{V~4N^giwETPM9sl!B(nBpU?-uS
+2(tUpcIs)g8v&FTcB}aoTz6y%vm;$xHA1m}c+a;&VZ<u7E_0l6sOu*}Fn`G$<{5mSO=|ZF+VVvKvSej
+_tWqi{Djl_~F_N-1SS_S(NyY*7M$(bWbq1gech-0EG;~OHuJ`%!<AqQv$ZZIy4EJ3l%$sDY=1~<vl$J
+WDyz0={JPRpOl<I^dRe`dh)7m6;{)>6g}|7C)i23vxy@^M_|Dqp*S&M&*7GtFR8O^hvKF}uOb+S90V7
+ABE_N1FIrCE(1ULo$xjs+Rq5M6T#)glBIhbJ%ggF@|48&%$T;O#lIvb%>9LBl!28IkwAE013xd^qAxn
+cwIW57d95bR;P-(7s3;11A-bnE-}FfG`ZGANWLGRiCMktAT^g&z&dbJ8&1f}NUp7I;!tLt-WlCopW7+
+cfbS*fMsmhHmC=+Tji{Bn#D1D$u9DONQd@{#QKdtTDJYSxtSPgl&w(l=xbiWOZ<SU@;ijj`W)MWI7xu
+y*vp99@6O*{`Di-lmT{a0zut%$z6Crb6SM%{i1vdw1@XO#PEJ~G^1s-ntJzQn4UHgiUJ#4%00%3XsuO
+8BVDuF2$;9YRUvpLe8cyM;`<mtIkXo8AIlZo&c5E&MVl4~Y(O3pB5vN?q2)AlnY?q07^rPgmLf<S#U=
+yyaleX_K@KWe<sSlEKJd#IIdgX<&&Budun;i?v@^GPR3MLFwPRc~VH0K5_c3C+F&oxCvcF8G76jn%cR
+0CQ$z^a)Fong!^krW72@LrxdTRLU2<qPX<nO)|Pl2Y$dxYrG`q9plXTT}oB!0nb!lx3C*9)3bAm7{1Z
+-?3(YGfsT(8)DLj3?OdUZ;M?Hq77O;^xyygRf<1Vx%Xbo04Jwqm<if$Av1BZW(rM?S*@>r3S98#%I-O
+!bq^S77M=`U>f6BXhrAu05Yi*ecawMA;4^-<Ocx6mY0*G&C2Y@c?opXQP-=V=;l3k(@LlIAO!O@<dHG
+rrVLdTUc*d7iuR;2LYF=3jFIrR5up(aie$Qe-Umu77SIt(ei$v6|!n^ZxZ%EG@k+4#Ph=BmLS`#UqqG
+#8|pU<04)4D(ebJq@$T&@hFesyj7pd#I8kYoB8qb~iaJ(pU6S^K|S|+^$_b4HwHXh-CA!fS>fON|o=S
+t{;&6<)SLi5=x!lVt?+7&8Nx@w{Xxsu=s?@;$MfzeX_?b0_POi+9c|*O7~!un{^9US$;+O#PI|9Mx3o
+qtA5%nrhv#yM^^~7(0Y?-flj5{;Z*Y4(9l(SCq$VirxZD_tu&TQ5v9Xq`WJ?;<Z|i|<Rn8yq;J5u2Fn
+zk5WHIkBxqDndC7SAbGs7Rw^i|<3FqKq$;mL9dI%Gn#XDw(F6ug1Q~I%}3{d4=RObR~!EC&%x;)je%O
+YcD1ITtS=<t)Je1p!#sjtIn7(D-v@TsZq6Y#1VYXSTmf`7fw=M+;2Xn)$Lp9T%`l3r|9B}?V!{CeOk*
+EV%IhJjYL2B;V%qq@I#UO;JV=&>#On`R|>AQ^2Z))7;h*_>uO*c>gg0M_LCYVaPcf3N0tcgxM0)-mY2
+vTV<SYgA5CVdSsTgMU~JWET0aqJ$3rA1EO>fp4jW1dO(~?xThTC{{>^wL)^#(0+(hwd<j0{M!aa1V3-
+mMD@?VpDKElnozz=aFa=K(i(n6PDNg4b6r9Y`35!x=ro?ZIi(`yWXMScr}BkvG}J3+G{n7zKZxpXRTu
+Zs-U1e#QSYa{9sOLzfVX!~{S$VG_rCHz(V8v{<6HLO1FqQF^H~*5(KjZX;MBV7vT73IT)J?oo_7N{_M
+{WwJ??9y+>2=&uU)q)$c~GDJlD5bc7=I-yij8Ub9L01!>Vp{U`_Q>q#KLaxtUfaA*x#d^>F{y30eU`D
+5)|lOzVYP&_hvhT;Ge7^r_3-(%=jud_&4h3@Lpvq?E{z(+TJP(H&#n=0M370gat80>IQFCN<={Wqd$h
+`C$fY+jEX*jrAqPsTCmZ-|&zK!_<f`(axIRO~F3=)YLG4Q8cX?N68EAM%B#*#bwjOHZtxN_V3CXe$Gk
+AE+UO(xtD4;`LlvmYl#;Rg=YKBF01ZbaFDC5oHg(L&^PJCe*eXR&5#bvv%mDJ5@9#2=c?>n7t5sN3v>
+7#6@krn*xly$dZFBD+epJf(6-Xx%bSAgZEZAY;t)J8yzC@;SQP!ircjo7&s>tKXEM9OKiiedfLWq1!N
+sEfZ)P3%go$FF^A{q<u)0CG8~jysVAl`1X3pKW!h>m1r1VQ$wxv$2+yEa^ol<`=BnSgT0)f>-!SRA^@
+K!FS*m?!<<P5{1^-qI?^KK07aPb?BHy9kJg*}$Q{!DB#LChP|h&mnhv`+n;_4a$`!5(nbO@AMW<xF$7
+e?ADF1TL=laA5P&KF;@>$^~j>^5!2nDR7%7s-SxN7tK_h_V)y3MV_mDtR!nanO`1%s>}9vIvt&J*tFi
+&KgZ@dK(~|IIR*#pD$oi&)N~S0;%kg^i^EGwS7c2JlkWp#!@U|FwW%=>%sq5tpa278j?XItR~JybXHV
+l5fY=tc>kZdRqY@K2U!%u{y#aYFcI>%yJig;<_i9Cazd-uqitqkXL3W%Mbx98%IvqmEyJeSPoG7uZ_$
+aLWyLlp=%P62H0TNt2rWFaoij{5A9fW~~AaDKp21Ny7;3g-xZV)1%`d1AlY<UVsX<*T(RXVi*M3>7v`
+&p;tl~RIKU7Nbu>guo9{mR$E?dLa<Y7D%k;StR&EG@Ul<N7RgtI)TFedHU)-t~C_46E}SU6C3Iraydl
+BuCj0CLf;4Hc55UBgA?8_C`Zf#!z5>al&AvNY}gW^k}(PzoWu=6FBXYb?32-cpU0@ZFn~H&XKR(uX9e
+V9lXg0%^_`4Bc<t9w%jSi=65j>bxS~jHScY86cp&VV$uZSl5Qg(Z$RT7C^XNfcVIATL;Dj<*6@I-0bS
+jhwOO{)d&!p?xco~y%wQ7Lc%&gZaa>e{{CiDsc>O-dBzNfk*whJifKUk6C+sG+wQMz^c3c;{6FO=ip5
+ZqZ-$q}h!s{mHSd35DPVC=|sE@jd3hsQT{h)e~4)cLNdgqI=ZFA$kdH+bBy+nt-JWR*Y_LyEU&nCuOt
+;2aTl%<NoR;a*{duj{H1p|HJc;2yTw1g$>Rl|+i_pAEE$PsApBXAMPM;i)YUWl@0fmGMGCmGUJnysqN
+N1`8!8`pc%>B8BBM7?_*+?P5*?$^j)D^$~_eGMnRxbcr@7%O9%jTDV+h2y+D$1DQ_76B@w2WVDO>W#o
+PK<F3JJ#2TW{@@MRWWolI1GXPlclWa@SK8nUp{aQ0Et~wA&SxBqysOpO<D8m!M--ZtQtosydu$pknUM
+k<ibd4~9HuPeXql+oG~fy_q2z@9@eG95ey|+c%aKSuOeNS?g}K`7^pUN554H4E`>p!fy=$o&WU@>tw`
+;pjxdK042T7J)@c<c?Xh<ymUjdRe*Q8@>WV1HzolF_&6iHpnr~t?9WkmNiKeo+faZdIyu38kqx+H2&o
+}2jCRo~c0sniFYaTYnXr(F5*G+0$>+Oh2CjCvr$=F=|!@P~TFAC3$RHI9N=wjve*Bv!T;AAqo9#xQ1h
+xNoXWe93V-A7aqI(GhREtol1t`C$0h*gRmt%@Q(yzu4|m9?ZLc<y~&uHF{ZO92@PfO-jn`(B_bQ$>~K
+H*dOq$ViV9}&lly|Ua+f8K!fDeg+V1NwJuymZySF}>kr;}JVo}8Vz)ZODu2MnJ&U+H)zzAvF@!dY;l~
+Ib*4^(<y(j6sz1|a!q3=|A)wa}2#o9dXNa$UQ-nGQx7WCXO0YjmHpJ6*@%E;6L(JcX$1V<-d2Wp<BQz
+v6guhwVYrhnPuQQ0u3$79XdW3>iOjfRHf+IbECcMh^cpBL~Ho;tqU;<$=>;|?O*i%7<KWxtw{l95BI<
+{%;(Qk(u^j#-kWK)E*m2MKr~Rnm>GN0Xgsh3q>r!BnTPNYQ+t8DQYoDFl6X?rqNIPMdZEZ3eNuQzc{X
+70#gL3BV3hHI~Jfa~hQplaXjL!msni?5EK`Q+j9q;O_Q^4KN6xt@*?^?)DSOs#+ft9VZ!cmeIC)*^|l
+Z+qtPnn*E>|h*O#2u94)Zc@sa^eW{yr8n&ehpB8W`$+eoSnUC;<ha4?0XqdJ)t!*jWmcFTF=iM!J1t2
+mZhVR=4;it1%=Y6@vPUXJu3>BqPOs+IDj;H7;<SGqe%%+XI6;-#&r760Q@A`QHNsji0mG&0XW;2E#+w
+aSVu+yf3?W8;Z2T)4`1QY-O00;o{l@>?n1{EGg4gdhHE&u=^0001RX>c!Jc4cm4Z*nhiVPk7yXK8L{F
+Lq^eb7^mGE^v9}T5WUNHWL1>UxCs$6S0vQJI&p-zBs9yxINEh9FOg$({X$%L_rd2ieN#?vQziJ-?Mn3
+2vTyKHs4N@i6vt3VBfIVU2u!NQpK&7(^<uy?(9BeuT{>oF*~aklS$Rv>TR(%G8K6##%z&~g=W=Eu!Dl
+9_~S+f?48i1R5^Q^?65u&L~baG{{@6wwP168%W_q*MJWIwOE!_2VB#hfMa5*!QZ+9!$@5gOrL1OV83&
+R8;%5h;MimD&2YrEGlO`SGmf=}d6?;#fESJlK8%7hQr%y6VR6cog^y={V>~I^*8uB0WOq3<l;<ts=5O
+Z|PcmZBgJ_3gsUoxc`pK5`zN)fM1Ei0K%2dq?+YRR<#%2<|_mZL=#D9UpNd7A`K70+38aK?_#BKF<E+
+0oem7(X4I|NHHa=j^A0)6;|F^P|Hv_V$#$dVBo(==|vIF@Al|4vv3j|2aB-JzxTA!ZPBf(8NEOl%zs2
+CVia=5il@OmZ7o`shr3Za^=$np9(fr*FxtIhZRE4Wl4rC!QL1snVidtSLR7asH8X1YR-nk$)Z|lF&xt
+RN>_}JN|iy*q5VC8=+}~92dvOCuP`A`3AER9Gg6gh!aOSdBUuSuDV5a^pk=#YGK3-*DXm%hC6Drn%w=
+`k>-C01o@HQWpI!7AK2gCn9C5%R=cV-ZjhOQEw(;O7zvh`7H=evZJUxTbhbIT;=ZB}qQSXw#y{GRtc}
+{XZ7vpMCWTJ1^9M(YQ_eCTMmCguOi%f9K&sbd3ADGYlQDsGcGnMizx*YUktTz@DX87-4TflhFE)YfL?
++J|=qfF(s$eE*J<Z7@etO&h9Q@QsR&i2xbm*w6sj>BKDrGAAR22M2z0v&lWHWr1*#{`aL<Ydj-vNTwn
+8I7`&!xQ%G*-kxd*2oODf0hI?Rr=NnNgv0(<D%Ar8F#@WG+k$EAjS%y*E3?aX7}ImY#|P{R=TC>0|;h
+GYe|+IQA}Z93@EDUl9e;H$VlT8+9}G5z7o$bI{XeU;Fq+D#d3y>EIu60csUym`=!VxaYrH=G(*YoO5D
+cHd0FLV4VWT(cPHC<6m1c=`i5wLU@#wzIV&-1kCp5>Qz5GOEU5b}Sn=EEfZcDyPCpB`FFLTFMYS(lsQ
+G7M_KOy?p1J`%U~^H;)VQ$#b}yaC2+es}(aG!4o9@}5Z!mSNsv{gWVlHx=XT3JhcBz$*Iv%{gx?K?N%
++{g}9Z_VjwRGKK%dT==2WG^+Fixz9usjHcHu?<Hd4UK0V18g6AZ^@)!K~&aEsD*vqUWK|zwi7Jzlai?
+&U0S%X=<O?H<M$>%eRGAIGS;|`9+Gw2<geg8S5g9pleU_S8&W>wE^H*<zmx3Sq^dRxwCM?{O@^I-V1;
+O^FatbZ*V7!*&bjHnTQtf2Q3nMBK_&DqyGExV|K?b`1XfO`tm-xW7{!KXOJQ<`0nl5p*f%1z%XFOQ?o
+0Eb6x=Zcb9|UjNfBX>dyOXDVFu`ZTg1vg=gC)K}hQnN)Yh<TVxS`xMW~xvVnR*{Hn@+=|ra_kDgGDdY
+{Zmi9E5Yr_sku+waV336xAl)hE({nGpBdvBbc5E)bccBnsn4s&b^x3lYpD>WjVIOX54)*?s!ev%ml2p
+I?8|nz{wLv5_%f%!Nh{Yr~u@;h#;yTPbh~6KL`QRYunTdKCu*JYsJRb@XM#9-BFqvn_@c7a1n<8;*=N
+D`x!3NL2huikwQ0d4*~Nqay)+kX@7YdQCL*B=SZJ15OyikoF54>3W>1v7oZRq>5D9Z0|AzO_8vJP7(z
+!kbh;jn0Spzc8-HXr2_USRp%z#LS4f%N=J4Qc21So5;|dGGNJ7CS^%V2R5r11#-@RTTh1idL4s2n8py
+*_fYFID2G>|YPDxnc$kmZRRaA*xSeP8%V<BO38UuY>sM6+1R{VWP?=4}NE>XwLO(9kBD`cCd6ezByaM
+4mNbSlau*cy%1sp_EeM>#xMJwkyqRz+p<QJce}P`u((%4;(@(p=wKZp;RwTH><1!`5v@pV4M&DRe&$P
+g~=XP#g%U#_hxD;<k@#F-Jj0QK429TZKvpKMf{0RHSU)=R!dGz=D}@l2AopzFll%ztIl;(uJPxyjuRG
+P({D7*#k~1&1FV<`yjL`tz87FZWusII1Tjc_p1R5Q0`}K$9>lDww5pZ5XCzxl)ep6O&i(*&HQqcd~HN
+7H*oBs?0qfZ143gLWwvR7<Y=a}t^Z9GO*S+PV<qoFw&ofT%^WTYHv<%mCb1XEZ@$u2zFTH?I>@xZw(7
+MCO*)W#(Q*CK{OP}3DFEO5FHfEumS0@#Y=3k4IQ}Ajxz2XArB>0wBAh&T?&JV~`95)2U&iYYy!(}6O8
+Yy&f_J{SXZa2m=(inSVAhN6;rrzBW$?`553?72emA;H?{s=+g7F;%<2wx9x#KjB9}Jdzy{(2DUkw#fi
+-*eukwDMZ$pp=Ad%$77>lY4ry=zbDc>6kOn!G|k^ydz$xYO~cPtTurRYIS5lY)?$e3H#Uzg9n+%mjMK
+g#RCARU+SP)!1*M_m7F#<g70!xAV?#Ve2Q@1NDa5vMH`Vqk7FM{=Uiu$I?{XtVu5Eqoe=6FHOSH-usU
++hM~W?zL6oWQ%)qJ_;K{Z@!QkGR|k}WTIn8MDs8cM)WM+ag(8+eDqZBIJ|&%Oh)jhPI8p0)C{E0oY-<
+7|T2czG(^<FDa+#o+(yAS~#1gHBi|@R7X7<=46h)8fn#kif!u%FHtCSY**w)mVR>)v1&psM^gZWAAD$
+#f7`T>h3Qtm?qeZzk;)oM}nUH2}INw{?bCaq*UgHT-S@B8-HAxs&YgFsUD5Wx7RVC*t;q|I?0q9JhYy
+Nn|f74o~feCurM0NO;)s0Fr`DsNM@IY6ZNb1aw$^>H3-jD$9!qctN>Kf)h^z@K8z#F~$CXF|+JI5452
+=7E0HECg7##oM%S;N3rn+$0~!{HS4J6L6EXj|3&+wg@Vfrb~$R9S&D%1l$EXAh9{mJp|4t?<0ZN+&c`
+j-8T-E-ZyNt)KSrcnk=8vUb&~$(@6ds<~a<qDMW-dilvU+P8j`y<TL_jU(}4P0SM#W>4x+x#7v^(S0!
+^J;*AY8Hxz2t+BIC@WKB-ikqqXh`ir7F^>*$P??IdZuf+u%<^gO_vh_ed3^8=QLDe*Rv3nVE@mioLRs
+|`#le2V~p~U8)ynuEyD;Vr>@}`-3bd2BO#bzoG<E9hrqQ_0%9_UME#a1>Oce=se8=95Zr#3bHuJs4x<
+7mKk<BOfkKhxp-f7#ccr2n<8T;JSq_lf%}Lo^iyYY;yn@!ETXkfdeI-Hpb4NV|BXRMy>7luLA@&R^;(
+uUUvD#bJlGm4eux18Rkg>RTSD&OUEu=syW+l9tllwrkTV7@5wXx!+Hbqwg^D4n(7pc2}_`LN~xn^TPI
+D5E@Xls8n)l&zW~DWR(PIyWx!jNH=)6F?fG3<TWQ(&*TSU@Yab_x;}~2%=E!n%Izx%^Lnl&1fKok`(?
+ciiYV1wu%*)D(rY8bteldhvJe^dpG@X&u9+Mmy~40UgTktr0vp6j$(DfQ2=G8#N#I_PaBvt>3r$L%2(
+3jE%o7Fb^CIoD$h0V0U8`E+h)4B+$ij)#)z~<1w0<5WyQps#nimY0n}BqB50bWTCFsr5&6^6HDC0ex3
+LWR=-pR=e-E=B7?P$yA;hT%OapR?Jt=pFhxT7Ah5lTEaM0kGK;x@2g#N57Ngnx^Y!WP-J-aE}+4J&W+
+ir=ulNTvzfO?I|-<F1Q-g1~eo)Xe*br6?$uSGo*)0Xn*PpA5GzqjuN`ss?+$hA$$V^DFo-_pf+l@(M~
+BrO9qmEk#DR0Z=h9Sk~2iKLP>8SlHDPq0$UMrHlXTvj-<|;{_N0F&%BUcbj)Wi7dx*irDT-W75&8A?r
+WkebolNxkJLLqFD`CwYf^iwQzmWLc4ZgOg*5waKM_HgujuGEQPy3qC(3%gj;{m3=p0Q>NH`K?k;SX!N
+Ij!;I=V7PS}|O%p*|KOK-FWq1!N%Ny8crAOlB_a<z1LU}e05TjZ6@9Jl5$pkN3G2SJXskcbA(>#6NbP
++F9g!!k#NT^Ib1czR`hg2+;ZxR%m|-PmnS*SH3=#@OcBqO9elyD%~u#YI<tQi*DP$*XX`?I>>jai4?^
+{ilA*D<M^w+kkFr>RxuZ*+bfcAi2}6)aw;|64WZwS8fwGduH|kbMN&<*iY$IWDfx+8_%Xn!{l=!pj$G
+82+AsU{M`(GbN7A^fsw7p%0?f0jc~MzKU6%qG_Q_jL%PBn)_&$@I<&@t?v8PSFmF$p5az%Vc4R`Pxd_
+CrwVtW0o8`6P{6SrdrWcuTSAjEFIdgAinv>f3Mdl1kcTAQK3JbPd=xVU~!NxkMFjwr`^$U8IAy=T6W<
+yc)E=*?n?$s`C5Mdua!j6jl3s$9h%W&f3{+1KiY)&lh?h&()?>0-TR|Yq;)X;OVwzK}aWogldavH1B)
+21diuRCs3pzD@5qHkK=St4AV9=1hp#{7_aEmM~E6SZNsubJ5Pf$$=}wPI&EyGbeh_$RLMOE2X5Tjwwd
+Xn8u0n3Kbk`fWT*w#^PGQ>!m19e5eY)gFBC{7l@)R82K6W-_G<@?hri+BpuXLwz>GD2(p!$^#BFl~0>
+vOPd0jpfiy-qRFyv=8GFnJG^71HHUm(B5#Py`QCU}Oq$Hco$vkHxOmz<<W0xhtk7=fg*UMGAK$(I0Z>
+Z=1QY-O00;o{l@>>sRHesgZ~y?}p8)_R0001RX>c!Jc4cm4Z*nhiYiD0_Wpi(Ja${w4FJE72ZfSI1Uo
+LQY<-Kit8^@6-`klXG46ZK#2^_sSn=BL9t4OrNjV19&vYi}?2N(iFazbDRo&iX~iO+Ap_14|hJv|`hI
+D7YbE_P!Pn4aqH>gwux?_RIBcaqJnv&%fG^QPL=vpjiza=dpC`RwgIca4fB$&y8NS<JFUa#1YuWLA~y
+tSIw&a$BsgiV~i$81yKqYS((N*__XdI-jkpdYi1T@;a|B(j<9*m75p5u_XD&6*O+#k7c&Zo4s|FTxB;
+oLMUo@bA1K>si9qy!@t>D29ivtkEYX6@}R8N50cv}cyN=~_!bNZheL15J21|ofw8=-5@?=mnmnnRlv-
+4)92yi=*(A{OGAoPKW&yzLu=Z$J$$4IsmoSp1fdLdHw8gGq9_ij*=M({(^Smi85qt2)ZB<|M;LECB3W
+A$$v(A_1{bsb6=9icJWRzs(JlT}^3?R4%FqPwVGRt5w7LCBAm01+af-sbAsiVF0r((tL5G?9^wrSSYG
+C4UqNuE7^3h2t_c@5JC945_XwW{hhO>R29%-557w$28_>2z<Iudk|ky4M5Z+PkO$!enx>S#RomGD(Uh
+y_TIf)nc>GC;WYHPkx6<nm;T1S5cY2*2OY6f7kY}^>&rp#xM_@hWaX9$Pc$!UBU|4_RTu8&n`A4P-V5
+S4_DWhn{~0U9aYzP>4v&Iuk0fxkE*tB0&QHt;+gNWYOw$+M*4A0ie|ki&dv9HnH38lwz|nJ7+7&{I_v
+!7`nJwij8_?+K`O2S{|Ny0*7f#qFM*Fd#YcPj$63BwC&%>DQC(Lxx7kllwy<ht@+^G{V_gEt7w5&ISZ
+_55_$SF4hO~F`*JsHX7c7PS)WF0hMR`#rUnYZRqai$f3QutpK|5I93;dV+qM>i<1x%f1Yrw;A^nHYXA
+vMF_^6FFn)%(6CJvQqamV82Vf@5CSqrH65<eg8G{3crr`kS(tRr9<*1aN>;_}>y+3>Mk)e4Zu6VN#^@
+<6t;UxWfeDCP9N*8tv`v!)9FJR=|w|4PmpNZ;@Ff4Q#vf>LZMV*S2cx{D|u9at2SA*T76iup5y501C=
+QQC?HSAKo6nKbpMz?&Rdn+xIZ+_w^><K>#K22OOx`+M{GuSLfOJVhccF<0s!dKZ?Tr8hA^-7mgY>M2W
+;wH52^PJZ5HU=&7NxH?R}dh@0m(RRIjJ-ae9H&NqajRkhB`bpebJhqS;|AO<zh7ugoste_&FT>)QimQ
+9Mw-zLp5(q{5(NGuK?!;1h7U=wFg5rxTkob+ZZ+6}$KfcoJdq}pXRg99#y<11_2&lCHzBCg0&K-uf6%
+-cIuwtm+VA(>c4xhhua<R&lYRh{yY0F+~<ySq^X$7yj<K+`?@BaA89cn_Clp3fE;kZe-{dM+106bm^8
+Zi~eNPCRIFK_@>Rh;X)6@KOUPZ7#Na(84(g-KBeV{^JIYeuDu#k8tZX`Pw375Bh@b0Ef;OUp)SSnf2i
+9ro>GkUx$njJYVqN@9?C(y#jJ0VS+@H68_r&@erA0iQon05IcKfPRQyOTSNezf(Q<eJLo#KeV8Fl-sS
+7fiq41c8Q!Y`_UoWJ{~K_27!<t$sDShCDr>GLlR=X&E@ZU$sR2M;;s(p>K?gY44){`PVGK3g#af_(Oo
+g*mz=7h6rYPY&D`)wDo{kdeVUepwY83Q{N0|J9q1k4Ret+4}X_!1fZ}j=UX7uA<d-!>D`1z0jlHrek?
+cvin+rz*7*I@t4UwinMaqM6IYe@ggUwia%(l7rt$p7ruMt<3W{CtfvjGe=EzHRe0HLz1?aHavE6x3#j
+$bNzAm+J`~3U!|1FCgy@>i*!v{9*Es<TN|@=?wq)kp3e%81_-rNxwgO`^Pu$j)rOkAaKtR#^1fk_g8h
+^KTP_w{;2i)O_AS5pB><@aG+(2gYZ#3i@q_>Z}9j3RKNe60?zjASHc1v@Dn4GkhVH#f<*?Uf%I6^(=>
+Wgl9F1@@k7nW1_uNDI-(zkt)@V0c<j*|rX%1#{j}c(%L@btZwYc^1Q5P_didlFN7{e<<mt07e)I3Y{o
+Q|bK-!1)bDCYbS%O47V>P2L8jx3FFKml^F{e(_pFjm#41U+1PMg~OxBCzJ4-vwi*JeM-%Z6oekYTdL>
+MDD5p0Be<peWCaIV$xi?wsc^+B_jb*o(oYCw+g7tHs=3=GKa_kz$;jo^`H!oKVt5bqiZ~7@+`JjTCqY
+G(}Nn08@`N<muP>vbxEU(9I+>kgP#=TI3ljCD~>4Md<~k1vB?}2l%<YLYolJ6{T(Z>+}Gq1<CD!?5^(
+jEvC{{wHmZn-kZp}2F)3TxS#SGG!RlXauBzHtJMW4b=8F>I)WL6wtK{n<+EeRyN1ipvK44T^Fe!SdJL
+4>4jozTCn)z4fsjG!0_YZGTryS^YV#P&Mv8w7Jovr8RvWgKAs;5&SY@nWb>3_iYXsZ#-&tK>PK=eRNj
+;KGllM3THUj3C*=!4h1n>(OBj6D*503(HMG4HLDH>GeST4uj%ly_#=PYJ}u25I=&5X=vu!82i3gP`Oe
+Viu8WwNXqG;uX(27?|9Fi;yaMktPDQ<gc*qRHxQntTnT41hUagoqFr0I)GOE;%g|b51u2+b|aZ@3@7!
+twDEI(pgFihht1w143Fws5Cy{)x7V>jGBe8&Nr71T?!qb3ZveEhQnE1oHIz^XJwwx)1H4o`TCRp4CXV
+tUgS6VV*JECQS_%dQ-r;(Yd+NjZsXXuRiIk3N4GfAQfX6pKGeE!3+}f>i+4fa$#jxP-yT?h1jzG!gxp
+92tKH*AHh3EYH_Qk`=Wlm?T;61hVlK^h0Q9F0VE6VWXxoH=!SGNp`k6;N5p|xQ4&W(TzDCJ|2iLbUbk
+Xq#!*sL4<8S~^=z)T09?gr{Iz^5P5?=!iM~Ofj4y8X79{D{`W{t@W4_O)$^#_02KKBgGT>~gO4OORNN
+OO3gXQ$U^2;sCpY0w7Re|U2?xP%`WmJxulr5RG=hD86GiSHZ;wQLW8`faFRHB{*SYyfZKIN<%xq0opC
+iLeq2fhx`52atzX+kwF(^+Ig{*`P?Aj}krl>O7qRC#hxV@Kx4`8|e<&W)bhwUohRZyw%lt1g%Jf*k1`
+^EZ~;G$QLn^KFI3J=FnJwurqu?jF3678?mGtl=phQw{l$K8Dt*!n`D`NES8(4wO<1_HQPv35jcI&U=G
+9!|3v{^K^I@kVvB-wg;D@A++|S`Ok-K2sa_<nvRU%xUGf((gH;>gb?1h=vlHh5@caj`vEjU<!vq;NA8
+Y5^U^Gec$IY62Q^{=(=QtjaXwRBm<uf#p`4)LR&j9{)S>56g36?c*wT8yB?1LIkK!EfB9c^$jE<l!qL
+kS&G)rIvb(JR7_K--uVYr+QG>@qy7;V6rAHNtL|n`X^!CU0c3O<m)OuU<pEzIi8WaxCHu9NqI`O)X~d
+PD48{E4QrQ+GP$cdax}E5$J3aHt56Y?a4_=b7hu^7G(}Ak<DgRJ%`3{jC(dkgoV5SDCB%oEHo9;^Cdi
+7X0yuh@psf^y3E$Is{yv<W*{*yak)_ELISsjXFFE3HqW#AwlK!#*3qGc)2A*!9(>-w|M!#6lL07h>EE
+gX&N_ZXr_f+{dieBgI7*i3WnFDngJ(l0ZpumXeYV)-%D8Mk`!rUwuz}*zG@4i7pkRMbfS4u|RA}}Xz#
+p>)1a0fz1RR6lMKec=2T^5%%kc<^qTVJe82lO^%upcZjnNm916{ulbUQ)$X0^s|v}5b)rkLk7eCHoDR
+8heD=&PJi4-@Layv#o>vz7k?x(CdwWT{ZEHs*nu(S+%XMyGztv!(YFN^|H?XuXbZKM)?h#`#D1vGIN)
+=LozkSUoClin=PvS+O9!yslU+?VAIg9MG_r331A{j$gC+e3HRFxFJsmjUg`)i3|etvAKsx8VfRO*ei8
+L8)$;A8~hm$nR2so%7KAGtC3%1pifL@pnv6V_$PI-1mQdJJuWQr=S)_8HAU>^2cE$@p1@n_>{V6*tFi
+-s=`B7sBhoQ2j&L1Mm;x*&X$O!emOvon1p%P_fUn7}eo>Vdzy=AD?&x*3{<_+fb2EtVN@RKE5u%EEwK
+x-qrt2S!^O;`rd>jf@AUz<{H;jw-4ig<Z1K|uP49Zyatlgw(-8_Mm7BahF@~~g*L$`AFB*i7RN2vXoA
+d^%N)Klh6AZU{U5}OIhFfi&MH^^95YAe9@Vu2R+V0}%SxR1Wp4GInBCxZwT^$<*(?Kcw}MuK4Y_%43R
+g-#6=y>Ot^!W;QK2Mw-=n+TP;wIRQ6zL~r{e)k@M{qgwq^S6JsU*Em??(K^s`}M`2j$Zuv&3EtZ4=<0
+tKl=8~iF`Lb_fCXW^%8P`UBK3aQ_0@~61-98mpD%P>CvL9t~V=;43VK7zslDc23VXp=GQ+qTSEW%HQ>
+xNHtTT$_;A0$L@zJBugleXt6}8$gW|Ic<@IK<(5(Q9{`!*lz5eNc7ps#12u~9jEHa#2c%b}b+1KO>Ft
+P}qa`Xv#=^fMh2wcd$X2cl7+f6KlqJ<pSAMGEH&Y|&bR?h?3swx-e5Q39unH98up5J7}B7@!V(r1x-X
+GRANn8?5~87H+qKW~UFB<G~;gLpH~(H;cExTWKg-%oM`&=^UC^%W3FBj$6XPQb4qeA7TC6ExxjN+y$s
+_!7O6qKG}T`f|YC2Eo_^_v&*D9)i<tzC9r0$6^73lQr20jK-NLkQwF8+BFhE@0}IDK%_=zK5W&wd(FZ
+2HZ1N94=TY@TweKU@s9~c%q&>D?ERf4kH^~2D22H$0C>U=0WLi=lDmNUPoVx)UQ753t^q>Xg?~ix>Ep
+Am*8%>WZ$D8U6V^d?F1VR#irhI{4<0<AfHn+9ac~g76O<N!AB*qCOm%XueK=;h@pF8k8?gXe9uQj?nK
+5KqqDR22Z=gxg!g;~{Zn8x{a%r?%aZaeN<DM+&%>Isu!=N35#N=TzPyj~55O<wp8a(iKNq58Gd$?c1{
+UvUO88QSkn1WVPlVuEc>6(0fY}gakNJMORqVxMswd~w1JGMfIfA{QR(g%gq?FNInpZhyV*)+P7q@SKy
+e8vQ*Zr1Z$@ewVTf_BX6)CImDo$H{7ah83zzxz8N1lCrjNn|>TC$#WWHz@G`+#CKgyL~6G;9l;{F?PV
+1SOe{0xY7J6MfX}CsV!TfF@QvqemE999a#hQp$&h1RyM(@>^=mgE4PWYWZq3|-ib4>@}}&sjr~69ktC
+a3<Qc~8^^5`Ivbf1h3PnaDw9?W9gglT)i`<x8PyRZ2_58oyy!B%xPY<7+?HPd+9zEMr5+{6rx~D`=`2
+J+ilR4p+$9r1ngdYx`u=Ok^F%8t;6xXN6y48j?em9whZKfRce4X33*+Mf_kf*#>-cMw#LQe;LL60*p3
+*a!bamCZ=vYKxeIR@VFo0I93MLgVyk)e>^$=Y?0J&sJob(TC}Xb%#$R*sa(f&#;))0W^oozh&0&&pUl
+mL6H%Q#DC6K8J&JmQ3?xz>we6AiT6pnJ})_sANnW?>?7-Ar95yU`FW2kf@$UT2^ZB8f7I&Q6%>Usrn)
+}kA5~p{)K=%Ta}EFNu9qmr|PvcF)oq`>d*N$FhEU2WQR?z`}#!iyfSRP%hq<>z^EL70oy`*oM)9Mon!
+)P(Q1v6GVL`V3Fx6fG`4YVdSkoC`1DM0Ev%M*b~81y)7V-qF{l(l3@m2x14=NQ^Qfy3HN47ZCzW<!7Z
+(oHIO+F8vmxwev>QHpM{4w=Z;SIftGAC{RCWF?uWt%aNadMG2r0eO_rO3W61bbPooYah;&BL9Swl!S(
+e0$eW%CmP+YymKZCOe!*Mt*(H??m{kI}cG)BXwB*4HwLKAMF+pxs`3y%Oq$(-7pDbBOX{8gIVQayUMx
+pZ147RZ(L)?KQDv_3UaOA;G>2=I{LzjPp2ofe}Tk)eKKq`eg#Ej|nBsm3cxjMh40#G2k5U32oPRc~Hd
+2L>^!H^*{n<XPyw%FgE`3m%k)0*ucNoGBn}$4Te&Kpf#QDbh=<>4k}{>yB*MX-Ef@o70^5%G2a&D<|7
+>i#w_h~XO9EfM24s7Z;^j}IewNtwNEKB1egb^t;MCzo3QyB*sLo8+k)`t6os?Lgl*jjfdfI}(_?-Bnk
+q$U!9y>kN6i0v8?{^oj-Gv&fY(SU1scJ|-r)HHZ0C!!)P#`rlHMrkO?uHNm7eiJ%+t^32cI`RbVLAQn
+w<c&J{%?bhbQnuYXe4i!YBAC5Q9J{=v6^ko&8(rr2<tI35BtlumMqgK@_i!95sNozN)Lu<y9itT4KWv
+EOkC>jXFcxE}<Z*(clh)8;2iK`sZMfJ{SV3dB8j0<|f|T0g!##lYwz-PZMC&ttFn<nAvwA2jSdV4jdn
+Ok)aJ}o?BVPNNe+{^T)y3BqXQ7V*|oH{%TK5IxL~B>rIYpn-{*{uRvacXEw47>CBFie4GF|TcnOLy(V
+VjoA=108nP7$(J70MTVOAF<FglxdXqWfAjpm91S9V^({4OAPc>u8vt~;{K%o#ui8*4SwBB1{yXfi3jD
+G}R;?o_DN4q{|;QWdk1P<^H7-kM$02(oMlDhG2v9o>^>XQOJ+^FVQFhm)y3C9<S2O7z8Em4U992NWYI
+Q{oDc@BFycrUx4{1ZCW$gzkUJ^+KESSIp}ccbK9X3mD9@%57V$T*4Y&lb+~%R)H(ep>*UJ5G(f(lr&Q
+?UP|!+@bNh_j;c<sqkN3Y5VFZ@qTz3fd#{xE7|e4v=zJ#zJwm0JWikWkhVW`N>Ccg?>eD;(TmGqdJp&
+ug;}fxsp7IsFbWAa<fVumPLhn~pk$nDw{=p<GW%QQ!Wd+{bR42G=TWs#v_&h%+XtAA<HmpSS^LFj@!0
+;>@I{=m+y;09kDo((5WB<)4Tl0w<jug4mPZT8m-Kr`BKH|lgk>SG4VkHP8v2aP1YulO3Fd0;wL4-PhV
+}XnP}G_rj8VeBkaxqKK;KsA&+#pMQ~T1CoogG45=o{C!c@Q+HeWWPS7P+j1=?m|anp;5goAM#$`6F5r
++wpO>7U8lUg(wI0$+hYwPDvy3)Q_;%~uU^1aK&4H14a~Gaf3G5-b2G{A-j@e{HLSjn47V$A%LQz|rOd
+N}nUX7dJZ~kdc>8t4M0js;KtdkeixE)(I_cy%1oYdBuZ+$59KGHuGsyNLIj+@E*p?uDjk56&%Qo&)pj
+Y$sQUI2+FQ!A!Yo5*CC8Uk&)1r8znv^%c4w~I(2wVb&*Wz6}D3mc~40Qp`j~1#Vm{rBu)ltYgBqXj(}
+{ij2`uv>?UnRGWSLdFY!=$*j8e@*ubI94WY^XM(YoS0Ja8+0X+A>Ee^qjglIySfj(sH(|C=z`KhC%vG
+^DjYjn$vuX0AOvSzZB#{)HZnZU~`=aWDro_^!+S*gCE?S%sDE9)dNDBID^`_}6!kPGEMxBsIcIW!~!_
+^%WWz5_IwsxVOl)VK3RR$kLz!0?Fk5LkN(eVb=<Qufd}Y;r$tuZlGX0^p&E<6NzvGnWjqAR`IB!_Wkn
+6bad%PH`ASq(u#5aRZrMW$3quV-1CgYFVt;7-Fw+VCPR!Tao!6H<6_QeA*C}uaac2D6X-ZhB3A=tl@M
+zjQ1shWRc#!6=d3uxHgH<hpWD#dNjaV-F2Q#Cho%T`siam+pKdnTYk%Qj)A?4BA>hW6O3pE8qVsiPPc
+B^!w0SHquhn|)FLCk914vIKExUVLEV+6MZ>Mgy}^=W%|`a6NgLJnC?&<!E7SsXMXbepd+KDVslcCd?C
+g0SxF;mkjX6+!s2q3rX#+*0D#o(VU=N4_G$Z`ijux}|KWVgLk`(@C5@*CpOP-`(dBn(w4aC8Y5r!N&!
+h+4)J`46EJa={BF%8Eoe+knFqfd8rYq*DBaOu;yhiqaO!|BQ-m@TVn;*diSqf>oBzCp7QnMrXk4M-ks
+ie<6LYC13cDNZIBSY%U_@h~p(M$0SG^9$Gkz@fzVMWGjTN|+;vdXv|affxhFW*kEuvO->G)6~o9v`@A
+Z%#3l#F81L$J&8P+iOWYIYIV+ZB}OM-V{_v$WA&4`Snh&ll!pG>991wE!EiwTH31H^m|WM%p_?s;%M2
+aHgW00-k!=4ysj;>e9|+u7V?G`Ubc>UiEwtIXW$2d5Qm6pAx>6Gp3)@U4p(#0P&d1L_)gB{F3J#7-(E
+TU~fRqTL2~0!K5jinE`L@w&yG=08y%$M<06ccj>b5a46azh%wq4v0=!vTCfWTB%36bT62?x+a{oc%G*
+UGnHLWg8{V#PL24u8aVVIQ}4U#2ldR)Z82FDgmYSb~m|%YH(H&1t38W^D}kjfq^;d_=u<zD>xzV(P!(
+`9M)_iPz9l<LQ|WC;Jh9ZU<lwECM`r6&usyHr3(=50)PRS1RItpiFcW8;XQiurArOXax4DD7@O#HJ=l
+yE{lc&$PGU*UN3+89!D@w{f>m;Y+0Q~sz{@(!wo9JJY<0=pQ&1H$(VrxS!+P%httC|#+CHdMVm;9u1m
+-CgQ&*mh!y7Pn7W5uPp1YpIup?C&)UScefkutcEEJnPLtrNj>sSehacq{H=F>5jRU8tfi$aXAcR%L9g
+Ek=9y{BS347fV!+=SGz5)HyL+rx>$`-d794_pN=5GDT<rt-eh7}IYl<lyB-fiqGeg-C=`#hgzXd$0Y&
+2qq`W|^;Gk{K1F_JNsITt~qfW2<tO+X;B1F`-i)Y$qWY+aS-$F+6nZc<-dcC>ID$+!Q#A@By<(djIN^
++qm+DZ1oN?+whK+j~}ICk&uRD;Pm3z1gDLaS(KGdX7^s)VPf+Ub8tjfqS2y}Q}V(!W^{-gOC1zAfUl=
+m$ptO<Y_l?BqgWstF6<BKYozPU@0|P~NXwdsqq9$m<CN*=3UqbN!H*%A*|7gMm{5aMji!SNcQ1Wg=D8
+$p8vA~Ds9^0cn}SKZ8b`?~m?OmWeaC3>?<rW+o#-SvaIsMwjP-g>^+-lQ&%p`Kyn_sS^E8UHviek<84
+H$^AR9rQ?n6_G7*<0{0oX(+S`_7-5#%<~k8)bTOQD>Ft^z!zh-;vo%okh8ni)o0^B$^o9GGWjH6+{0+
+Y|a#mbi#6z;-Fm*h+eeoSMe$YUz6#=$NoTVz3pFe8iOxkk>9WT+*Mc6_*4a<(l-*9bvOdM3Smew}>E2
+Q9(aXV@GAdoL6Yel1?ailE;Rp2n`FUV<19>GxyhMN{Q2hW`T&#E`@_wNz_oQ+oNF~kB+GJV!F#$N<~T
+Ol=0H-^@od0<STzv3)@eJs-s@z$EVo*EN=7CITKCH2_-m-Iikd%!tXj?)Gj|1e(vLoqIRABD2C$sr1+
+;UzZ8CS1h<JLXzun;8KiX;`LesrCvqwURh0+x4M&$-jvAwXx8YD#<FzBYH7BBcDW&`jB}rhRhQ6=O-{
+vZ;faEG8ech>(HhEt9$~vF8AX3WahzrLgd_g1Oih-2UCd_>rN>)>LLY7eYAXl?&HMP~be8m_`R^;1AX
+Qw!jvuM~RzF<d~N5T~h3aGN%_{om#s1A>y3>=C?PS6p-Xv|y_#Gt%hk5S$knNntB7MWV&ya+>5DciD0
+&{V5gp(=l~yzDb;D?=xn(#iuPlpU<nC=>yLCT1ZZwX*EW-8$-c1MKBvA?hmyoxq~4$;+U+@u)0E5BY^
+AI7MIUe&fr_0~XwGGm3%;g!f^pG!rWw(&5lW*i4IksjXlo7+dv$)=APIOmCb9;j=ZP1|ZTm<pM42Q?q
+2afKyL^p&n^SBhs;)dCR0K=sZ9W^f%{J(I`i2Vxnvh_%Rh{XO#ZbnMcOd<Wn)7QT$+1Nxq8U+9-}KTi
+dqV(zZ3no*@$3LhOBa>LXem!4^+<zvOl1y=)WbxCo*V#8S-#ctf^=C@)JC#jXli9uW(wn0aeYk*V9=r
+js*4MJOJoimSAl9nDWPFRO~&1>$KN#?x*foLxk;H!1U5Z89-NaWuwwXFM5kkRWaxXQE`gn!g9wA(T%{
+9xQCM7}Fw)ItqmT5s2Z=yiwt}#iA!5+%G^KBgJ8=$D_R(-tW$}0h0v<0F-ZHRZ|tSfZU6PS~eowOh+2
+c#v?aU6;jAiqO4P>ok%bR-YMYnFDZ}A0tmQhd&Zebq_&qtlui@lT_qA{$ZYia470F{G97tyXfcYSys5
+4YgCRe#)(FX4ILk_$#yOd0(I}{_8ST_(J-FZFj0I~pFLpZB@gazOSDw{qWQuox=QB``l!%#*STREHdz
+e|8n=Es{fg5sQttRQ8lN}`rmqa3(y17AvEm8oHL`oRBvrr@MYt*o9?Z4!W(M1UNNGonR$oX!3yoD_h?
+g&oS1IVK~w-zPSeNZJxFBNh%sbHd@1;U!LIO2J?S@A)ICGNE4*5PJM7XE?$ZFqWkc<|&b098>mAbOL@
+7Z-5`xW}H9xKFax!IwUVJkLqdR_)f6DbSJ!ejkhqUagS$s|%lcD>F2Im-z|9#rpRO)TYTVHVcvfI9n3
+oQhEIB98KpoqdyH1M!Dc)eUn8ho1IFM!AR8k$<;J9p2DL?(B+CCpnQIgyKQ}SKp_|br~Q$l;T=FKwfV
+TEVPZq}<nU_5B<y28_Ga4|OqHX#=deGp@RlA<x4^A`6q^aIXxvyL12YDbxazx~kX;SQm1x7`fkgXr+D
+{_Yfri}zabM$2$CwZggTR0zwMkl>sY4&XHF8h%Dalkf-6*&>uJi{Xjy*?;7VP2~WAt8U>0tBO$D0M5O
+DK0;@D(|HAmIkYqYkgGxC0zA=8H{D0kV<884A630MfMaiOA7nW9&mKsJ!-F?G@3V3W1>%2py*Q+r%a$
+i~yvPp*mCe?ziGLzufTxWJ`w5byjEeb+Rx<D-qD!Dt$eCRk3F+Z^n0dmjx6*`^lSX)o|5^9^$H(G*xn
+uwP+$!^<mn3T^?a{&5u4Za6QgiFWhBZO}$DX9{Ay2$htR;tKRD!r<J+z%0F`P18pb4(MtlmF*FgMqFn
+pd39MG0IX@Th8-yTR1GV|=(ZP!NeNN(1%Y+gD$Mp>RYcH-|hM@Hsd4zD1qIXXuI8?uHPc2$Rb7_w{zD
+3G3p(>)W5eEuoBU4n`5w7pwnXDF@OW2sA{_wX6?BEfF=}zva0h#>dao`8Djv^5Mq<@n8TbAyr>sV<yM
+;|jxzT+B|hlf6vJQyP>n1nv#Ae-IJu(eK_B})JEqHgCH4+Db2Ls)=5TomU`)RB!Jw1Dg3z>6b)+#zUq
+2*>PJW)n~a25I+Ske)$NDOEP_n~4v7<EX~x{WLn!g2WkVfsrXGB3NeRyU{+dev|aLR~H@D(`t|T6)pN
+aNJC+P`nGex7MvL>5!cM*A0Ig7(i?TkhCkb*IIWzKz;-UVq%(>S1Jd2BD>%pS(n7$5SozRG<<3XIqUV
+@oYZU=uZLumxVv;6BXuVwtxMiMk=?Zkw*IN}<r_OZGQ*Jcc2KCi^ZhbRuqRL3%D`Xf(2{H?&kk|*-4u
+`H&idi_{+WJG>xv`HpJDgcPd3BmxEHYFd=48Qduxu@fH@CU0q|-{*SmcFdB>{`Y#9cjEDGDNF#C}++I
+NPkf;=8o|R$&)V^HlEvn6RvaE&Gu?J)m0ahsj{-;cSYwTHHnywm%)>YH1%q&{|5~3-{%4NZ+Y}YIexg
+3^1w4Tj#Waky1xM!j+3YGSzN7b{!vBN@KLBT+=0(T6ZB+kDC||+(lX=gIZ}xb5oizC3Y)&gQ!Rpb_NH
+-JiPkz4sp~s51k77))>O_U9%zLcgfgfx$-S0MkEU@ClQCaF-gU30I~$utt7g>TN%fK_i0yIi?|JVrOQ
+Fi^EGLSb9K*706T4@?vOWqzIM*j@_nRoi6HX~h~6-%F0=FfbscQ`aI8*sH?)@K!*Ffi5A|D)O<ra5>Q
+?S*^GgnTS^|oPZ0xKywF_7+6#(0o)&q}9_l!?UzEL2!gFuN*T;Ri7g2T2K%y_QdbWSypv)~o{ur$q<Z
+3+ap^4nl|)^6U4-*j+jV{$aEo16;&BNMZy8MOdOtY=WS3i<U-yjQ$-54ZvEy;gjM1#4-O=80nFO|&5r
+q;UFfm5hTgnOlEc`%GP|W{C05_dBl`qM`28()Xzf7h3r$Q_IAJzFg<|D!Ht7>C$Lxx{e^dv&&9uwTm4
+|v&N5qN=GkF&>*^&c0<W5BMbz>lYmS?cn2khe4;r^pQIWaQFy`ZL%u|F?T}af>4cn51?Pk^I?<eVzes
+H<(mx(}nw+0gq^@i72kPQ-e6I+FO^VhtzmM;PvrNb>I#^XrQ=Ef9SyDXJ(0W$1Q5<ir9!W&m)}*Bb&W
+>{K*2HLreA9J^Ee@t+Nlm$N+A!@EFTDQBux_VIv8S8G1<9L10Q&EVG2=x~H(41XGpn_kezw?<%hp7YQ
+aCCJFUB?OdMsr7-5Eq7BOjYs6Xu5TAZkYp!9ifyIs@AgdfBE*k)SLII$tvrp^=G$JRI^_B-5D_dy!IF
+-mL*+jbg8{P!^FC5YaI3PFu;_IA^U(JQm0APGGO8u|nA)M{(2++A{4!XQ*<MMuSli>U{^^*@wPbBwoj
+H&|Xdxq;+^UYWc_cew2}C^p@Y+fb3`ilzr+~nDmDS)IYhNoKJKzgAEzL{znP-IZAZWj=Z!Nn*|w3&3x
+J|Y_^b^o!`o)4SBQeAQJS~=t-NrEG<KZiCOP*EXhQgKvQNb(BmR5riya&s3o}*cV%L6Gb+n21|?G-V%
+&Ari@$h$8}rzd()2gyJ2S>EPjfg_#}uBoBL1zLbgBkDby4^(j>qE9F*^SpWyuI!^Gc_u7&SIf#7z=@Y
+gW{?o^Mb#m5Akvw6knsPPE{FBH39E7O&(PemHe@cilI8W~xheP<i#E0`3Z}GG!-h@k5*mEwaqR8KHvt
+zy$1Jn>Xa&RI;YjejDWJWeTY4KTDtWr_-VHf9@EIU#%QJcxm1eEpqNM9Mpak&IfSBKf)MK8T^nFFxbb
+BoPJwvEV$dEx#B9StK<P}$_Js0gf@-8A$kE;)55T)U7K=ewmcRLLMCtLq4tQ+9Vm|hN6}6_R7JvC<iQ
+QNld<ec{~_wo1GNbhYlSBhKm4zi>gstsC8CY1W^=Z3C6%4j-uucoeflX0h)I$UJ!<E>9X(X3BS?7%T(
+W@NE$(+sWu(PYNaZ59evKuxyCUt)ai_7W9I>Q;uD0f$4)e%ki|uCPu>v;?>Js6^qXpg=h<qLSgy2O>;
+bohhEBKc0;*^E6)>4JCPC|5nWT|u#$PGeRvBM-d4A6?>^Wm166nzRvP(^!D?>-v-hn)l%gxa#wqaP8L
+j#JCD&SE#gBYy)Rjtev?V*!72IRjtN<MPj<RE)NJH`Tb+N;l|AO?F|_wr!B@yE_*P-;cy3#I%9<h1Q-
+=E?$X%jnN0|>HuvV&DwXud2#Ji4NiHrzQZssEQE#eWOy$erStf$)xF3R(!8OC%vhOo_DAb^32ECHgIx
+<c^!aL3Q4e+M3Ee78caw7YlY*kh)iD_62bs=94Ap}-R}_a_&fD!a_e=xg<UG4TyN(JK0S@GMiw+rcx=
+;qDNy)-W4s@m(ef1n8_=%XiX_<s)21Y%*U|OsTVea@0NDZIch+jL+Fm%_>QokBR3=pJ9tw8*_JM6$UA
+!7`5G{)-yvUOFbSd1AEjyd{$n8MX;x$0ASK%5JQYb(5~*bN&LJJNpD&>0-@_<E->#WHzT8cu@|9*ifw
+f<D#fxj4}pak^1TB1R)vXOzt+)etx+j&de5#YKy@_gN>za$;R-5@Av5qksv=(PQohihTrG*FX=V7mYW
+;_2V0BtQ*yd8s1dWAsq)<eE3S#pm=?PD0OZF_m^^|MM7(pX6vLIu7z*2DZ_YT^l(YuBpq0Gh*3^~7@*
+^m^MX5~MMs<@zSehATX0ATM(|hTq>KrUBW0VqGp9Q;2}4ltl>*tSjPGS2M)Dt{B^bPz&(JPo1E*REL#
+9<UfG7r9jPO-5uWrea_ec;C<-~D>IW8d2TVe6LH~$+B=k3Z2*0$HFsAxAuW=E`ybv~p@h!FwhjUG=73
+R0v`)^y!g82dnGkdc?Q!@8I38MX24oV8JsDwmtw7oVO@%#8T`y^6oDO}T0zK|&~ShSc|4lOg!3s;<TN
+i`TW{Mmg8_CE^Fqa&fG)=a|X?RAvdeB6h%#v9vDvb@1uA4%0G;Pij6{eYqjoww+)jVFY(=fjIg;A#D(
+cv?&Xan*;x>u-T5Vk1&l@C&Sn?ntU(A*8Zd2+1{D>;T$)3=FI@>P#95?79untA4qD>++1>d5iSgs<A~
+}?eqBrg-&xEOKUV7HkvRUoh(b~uW7rnD?IBk3#NtW2hG9_?<7ACs&l{dE$h7@#t-_oyGCJu%{`<T}V%
+7M<BFH?UVO0Fo%XP<UQN@tbI}XF76p4n2uNMXyMqK2|>?u}IHf#DJt6Y}l#*peLhIQ=VAqCrpGleI6d
++{RhrfO{bE<-=Az}boYn@k69H|4rm=HX>sa%rKB^hnIDvBZB&9Wh=4G}K&F$kFKOn#YiHnu^=YWI!;a
+>Rg&O07ZQFQsT`s1KGM|@=)?2sI)==g@I6x?Zk`WLpXGBxVS7%nGn$KT3iNE)x~8|W{U|S!UGeDnUS<
+3in?-;$Z-erH)!3{mrJUGbr$dZJvJz|a!GM?Lz}@>zOdwc>AKq(%51N;glI*8qdC<^)Nw0bZ8^CeMl@
+>feNxpEPlUKnNxh)r9l`x7Vm<Q;0LMEVraKDybr$9O#Z%}g*;hJ;pex?@OD7bJH8^zD(Cm}gL~WJJUp
+&{lOJ6$0#(?>ybB-72Y#n=}Pe6dw&iv=6zq_F3zX%61`hOM!Sda6c#Xx(8VXfd_W41Wbe`>a-M&&P_E
+@knC)kh<{-Niuvvm4wD`O~(z;`4MGMI`HQ3clQsk8Kd9G0fh`dqB#|Mjzow*Mr7Y+r&?`m;=9`iA5Lw
+;=0$vQ<b;j&Q+V)9vnf?Ve8WPAdC_vlRygLi)Z?4Ge$E&k_j&_x!=7#U(yI96i!eCtywCtML^-wic<}
+s?#UHyFt*Pd2JRk8nGPo`pz+FHjKw)(_SKWTzRFe&ItOd?a8(-&#W58pDJ7T69;)iaB(><WujrecW#!
+)NDl0D~g*O<&Id!}?R+E#zzW>vk*Au*S^2PHPe>(E~9yeKC5DUXf$y5f3C0rgs!OE2YPWJ%+ByKYajM
+O?06+NmoOpPQKWC!9Sk4Pet7`E}HE4;RfyLP*aR0VkIR~~lYWY?p20Z3n$upSSFalU>|QI{C2%_SF5B
+xf9xiC=?K#g-b+hKae>a>0TP@4rwcF7n~x)NNf0$vxo8A4n8Vtx%?Hk|xk=?vKKGn$|{sHD{&1U6{~X
+&K4Dyi3IW;u&n~^o)TFfMhm$1KNTwk`k4ySaB)vqfv%SV$X-H8T9Pp?s2S<ol)PjQC2Tqe8DWM7vBFE
+xlHLX{jqmYzE-(9ymUzuFb)*7FUzF5h>xXdem#}Sjv8fHR)BZEbRwq%e)hQb`2{`4Bk$r+9<_bJ2JV5
+gLNJ`e>mur?9FY^+u_NF*G^W#ePAJ%dzH>(8i$*tq2OM(1LIIaP)&T2ZM7=kG(O&9!X=>AT6*gse=SN
+&1)fX2O}Xz%2Fi#MlDxIXU0?uvoxH>`CVF>bU@ICW&O-VKUxIoXs=c7bY%hXQXR;fq?$IaPx?qf2-#y
+OGp7_gu>5V(?fo3+v_X8Vn60UMlxk0zl<9Iz@KI*;?Je^tpBm(x1siCQTCyin#6W2({V3MhDV+1WQK2
+In^ph$Ki^@hcFatQ<{6Z@CaCADko~um72e)G-F1E`ACI{R3WcOwgDv6#ZOp}Fx#SSHcfuW<Pr|he4Ub
+vew2jnX}055Ot%&D^e$H^ym_+!?6<$6u~H25JcnJnXga;sK+#+^ZJhMWb>H2!h-9wrTCA}AES-<1{ae
+g$s{3bwb!;CCK>YyED$H<R<u%pal|WkA%b4r}3<k!^4j<q*vYY`ZE|{7xS2|aYlYU@yC=Sr^d~Py?5P
+?{Yt_tDs2II0FfNi{_EMk?lKP~a{KHBI=x=f8J`T*f~jW=rd|6m%~I(;`e{^8C0KP7~7|L1Ssp7=;wH
+kWiI`{z_$R?ma#13seJNT~E?fhZFxgvl;)J$sMBaU#EyHHGLesw}3-3s@VvSUF=_5mJkQvebuFEf^-c
+bHmMRZncR(*d=WxM%o&tC`b7`4?nVSO^SaE0EHur9H@WbpOvH!hr?P@hDE?Z5@O-T%-d%!AbUy_)HV|
+8r{GDsk*NU~x@L=pGv(+;z^k0DT&``oZhNrLt1|z6cqthE+IKrZZd%{2uWZC(w&2rK0L?44|KGL`Y-u
+**C<%Ayzj^)bU*%Fj$)3O@CslDsn<>#uTATVV+yw{gZ#kCi^_%zH1M<w$wo%R)lc#5-G;!}b%ZQHLw1
+_n&3{4>70H0KNPxH`|zz~c$P|ezUm0hjG=Bn<%AXzuXRDrE%3FNg=HzkVuD>&Smj-<MPhXTbEANwI=e
+1#Xqudh7QBwh>Sd5G~N>}N1A49H-`GNEDekK}Rno8SCq2z=&o^;`IO5a7kx6*L?;%BR;<@K}4}S{p!k
+_P&#fy(BubU`|AC7u#aioqOww#9B@*P%C^);h#Xe#KIkI8h4|3;&3Or;lVN@?&x?O`4K-jnlO0POCO<
+-vWRduMK}H62bAzNz2F>eNvP>G<XF>blxK~Y9pcw&qlzE3c!|jz!=N@ive^jFbH}iyYPPXg>)P|%!ZY
+Xiu)Xi)wpWFJ^o;6b)8;=R%6OWam5!L8_&DwvGssG_In?Da=8hpko{0n|0Sh7H0p<3+*{@E^<6EsMo&
+jlq_y<wIbSiSZJ)jzjT2jK*JYVE;fs!v=H5`eGER`9PtqXm*$?6Og4aTzLF&&-R%v(8YIp~rr7W+Laa
+vr>{*7+e%N7O`Rd<cb8t}w~Swf-Nw>%|&?rQ+4rqH}<PwCpIXj~%<^d%0#hs-a_s9TnnTgqIWYV=hN`
+>2$ktgjk+r>cO-}vQAD-mwEUSLa21K9Wzd)py27&9GQOslH)~E?cXn%mi(2qOj!m?F<W-`jD)H1p}5d
+dCh8#a-r8D)m+DBEDiv6(K%KfmW-9E41c{4H$q4i<U{om-2!syB8CE?7@|%eYQ@Of(7MK&b10Da?L={
+C8sLX9%xBu*{K>u89uqY$#_XK64MQ&=ev_m#@+^%0cf=BZEfEN^NomxQNMJZRqb?q}<2m-YJj|E1qSb
+TAMflVurD`$+EfC3D5O^(E6@;1A}r|Wu^zx?Gd>RwU*bg|yZmq2M4b)0)zD_hKLjnqB6?m9T_6HXu6!
+xCz(>JU~+6)S8M51^pOM%sg;%gd&x4~ng}i1nVYxC%8Q_GPxtddl8Om3}Fqoy1rc<)|MleDZ%ENxc0*
+j$QbdhkFJPIjps68BSZucT3{;pYv^ST@hV7Pv4vz_SqOU*;qHE)B=gYUPXOz(D%pM7e2({0&5fac+Sh
+U$n9DF88ypRs|j5u2x4D|uD)$?RW11{EJZ#P1j?B_+URF`$u8W{Fc|copPamW{{DG?G?a2)BmMNdcSm
+ne-oE+z_}e3DVMgV*`qP_NM=#$Ve-CZw?~~{6|J2{vnp*4z7#q=k@*lr>^dG-_^y)~r{^9ub%j35(a8
+g4+0+LXJCyJz669#*SdbdiA_kQ30sDhc=MRlc=ccIBF7Ef&8dKNzUd8A1cqo9Jy643;C&L{6~Lsfoos
+-Hd@e$O97K;-@leYNNN{GLA`KIU~Wi+|AfPJoLxxyXB+z7vl%Iesa((~`N5+|>SEv@bXkpKNROd9fzR
+QPSJ^oH4a}ypSXE_7M!sO{q7W=Z_WisYpo)(sW(S9;NQOQWliORyQ|A<>VCWAM|}zB^a&q6!1OhpZ=f
+c2md=e`04S%f23y*`y+h8-Dhj(XCmU|UpGto_x@8gq0{`Zn}_bhj6rl?6hHAX(@x=Hm-O0rSJ41wQsT
+Z320PTuw%z==1ih-5%Uf;?_P%?YYbQ7!^b_DWX}=YKwi~HtI2x~XD6^&dmrzch-t6C#XzoiglRh&sbG
+eg*X;XYV1)kbmlKCgn)&#4W(PV832_s`&xoD4$ZDd0fD_3`f_{q8=ca(YmAa7>biiMt~LkcWGIDhB4^
+)Ug%jLwV9i<SmpNui;;_4!p+U+47^>gaS}`U%&3!3joGS)%Dz4e^csVY0}U=kx5)9QE$p>4OwvQ}|Rv
+O0P1he6BU0`V%VgBRGPWZ2GM{mM87^+$*M^j#mf6)5B+H8i2Hm^&08Uv|eTyG@k3$@>6?o1r`dlri93
+}{00o*=N<PO7R{vKtq<eCwd0Eou`p(bn)N+4#=_0mhJ#;Dj!u#<p8mTh#WJ+t8?w>$w6u;Yo!g{RclS
+?RK5u#1@Z!gu=`A-?pwTPCC-J;@TR_T3TU96?z>Ux>)1BB65Aqh9;}lp%Y}6AL1@p1%s>pB1dXRael&
+d0nQb}$%)hIX@{4h)ja8c7cNF-lXYS)>%Io4fn?g>wTN1khkE4|9@ueQ<4i>Hw~u9hkLV{*glP`j`;i
+N_c4)8CXJ(C4JbAHDfn!dS3?3h-#4{k|cdhLG0x3n}zD8sRE$*n(eT7FdFg{d~Uj<)1a>q!vkIKPefX
+A<oJw_JmzQI4jBkl%GcCS&N!5zN34S0!}Au&?|Uvmeo98_?d$H$*b?)y(g<Zs2uhBU{=(#4eH#AXsAq
+<o-^Hbh=z-6x<39hw=YrW^Yj}0;NCzFf4zc156mb|i$G+i6jw^<L95Z=$tVE?+UB4oJaz5QLOhVdUsV
+gdJaoRnxP6=32RIn<$adY-`~P<|RrP(a1WlKnSi+1jJM1TCr;lCtSQ-gkA?xZoFVk;Np1*A=KK9M0_@
+-<8w$+&CL&}L_dV}7PTuLjOH*b1TZKwmyPZgSZ6ZXCZ1aBzhFVEH+@iN;&EPQcVcy2W+n6WiP+Q8k&b
+vezQC<Z)vtDStAd=c{{_g-9Ol&tPxN@x^Hj(3lJ$M`L|0%X15uxvW2v0BkJ2+NiJwcts5>-+lotD_b%
+=t<(zVDo3uJGrPMj8mxA{dNO4ls+d9_ghW6OWDbbY_xPQwj~Lk>w;MJq*@K&hn5u4HdEn;R|}-#nqEp
+CCcVUL$zOZH4E^H(mFzQ6TNb!S{L!<s;V#NG^G2$LX}CF7tM=*KY?tTNB1MLKNZsLIqgWGjd~q}LE#4
+FulQe_BqD|aZ;D)M%fPelAr5HIr`8Z0phsj5ie!dNw$C^f(jmOZ;vfKQ};PGeP<FJ5u{}1rgJzfMz=z
+r-wz6>A#!2`Ysfq&^ezRY7H4Q#cmtiepA0pk-CQaCekLHa?=4=nnr;9Odq$^O8<Lwdy4)#_{qb%j#8R
+Ww8$j-=VzzeS_WcPblb7=Iyg%1_fT9y(FerZ1esp_x<67)!zuFD&mH9lu>HMS%);Cq)eL`zUC#=nw{G
+`f3^aBiI?xr*eRGAN}|nayV0Ls^pp4w^S7er9S-^PZgziEdM~>L^#*5*1_DCxK8>REUm!DsaXX5JHBa
+w?FhAup13@4w784Dm98r)J3V-Uaq+v@j1a)0IG<P+b(hw8KLXfLD31mI|0w~)W<eU=s#v8H(Dvr|D*C
+L}3L{EZwZAz!Xp&d?IwQx#lk{maKvX>Y;y1(Cp^_?4??K3H7MXb3jTM(%`xfBS^f3bj+^QRLxn8mxpt
+%QWqPiUTDV<`J$NN!YImdWc1ea66+AG$Sl(KECRoVGe+M6p^@Fpbhil5d&F01H`+7YgB99<Dx&i2=t7
+|3Jxf!3j%r&)d3fD9C|fMWmFb!GRs96WNf?Z8t^zDA!Lm79b$!6<+OJlDvfJ&6!PX1t;S15pr#huADr
+wWK~H_7G)v<Sv_$#buX#%bL0;;*Q5p4{cLl!{}bgP$IK0X{_&*%pwZL3nxp%QZK4YbX=o%8Ux}Gt;Xo
+kYptvr!Y&5qbG2U&Ki61tMm<uRnYkW0@)o+u;KlK{OHhv%_O_^JqpjQ;ty}EGdk5~nD>?R=hWmMqA))
+MM@!qq@9(GOQ-TSx4ufGYR<vZptuTu~Y%x6MA2a^sMDWugNc2uqCQ8hAPUWM`GG<S*l(P=iPPrM6&{u
+{K`c;vpu%M3(=yXw_qo72N5XE>H#|3k0m$rJeDSio|wgU$PJ9`=X#YTE1f`@MG4{)Z2im(KJ<n4WLT2
+0VriiL_<#-fOeN)=NPX<L!98Su9NKob}bb=i)S$RjZ&cl+>2yyrrly>)E1{{P9z|ECUzPgrVk_my^)^
+h`-5g3-&jDZ+DveNDhfG-QB(mT$VHgBaHSZ6Mi;9pOs&`f!^eXVFr!(rUwylXiX(x{r*set;RE+Wy~B
+v*PX5E)m^)g%Z}B^!Z$m*^-`qo+i6jyf82sPbo_?9jV1DSz}hhmm#F+{lSfnYz0sih_o@a&QjNR%gZF
+-Me`X&@e>=Ld<j!e7@s3R+fi$$z7di>W`NmNGFir-~MyfBf0=q{f;D`(B4m)dB^?Ffm-?31w@zX~4Y+
+C;##|i4d49?s^Z?nEQ_+2j`YBSUcdP<s|OHH+gRJ+uhYDhuR5*GKOS@*)HQmvhg?(@^=Hr9EZa(86&-
+)oxsm0vtjzu%xc6JQ)CuzL(9O7iMmb1g8TVpC4=@`Bacl*XkmI=6WRTBJVNr~w*&`LE_t|Dk$GPs8pN
+pD$74VN9I}E~y#xURS0+PC;SGR&_TF#x)`5xumW7o0_|Idhv()Lx~3_{I?T|`=;OPdP})9yH-R-;i(s
+d;Zt{rpShk5M2%G!lgWj0{FY%EFAmZkQ-WrzZHf`*17CWcaW)NV$yCLF0F1%`jW9YWpM_Qh_-TLu6zO
+Oxa;jy^sX!JS<Wd~#EA|^M!r%=Le>n|CgBWWo6AL$EGJuVe$0Kh^o7JLN4`?Y{6ZD|@vy_%0q8#8hwm
+OleV0Gcl$(-hNZY3S@?s;9UkDXyN7Lmwl2P;Y6BU2okwN3iWrZnN>u-Qa>U2j)@T%sM1U*_n4!id81E
+dLY6bYY5MViHmJDN|hdkD_ef3sq%!54^VwO+V_l`98K+<1#Wx3s2gW;nWz|@f5g^f!ZPSR%a47PEFH+
+pP2fqErF<W6dZ#$&fBpKhJ9}t%t{%B3)e}4=SH5M>0<4UE-%fG1FwZuMa?fR{Vhvwih8}t7AjG5*L5Z
+>g;95u#vA)8A{Le1tR|~EzbHNqG{Jcfg0emPSnHY!g69e4e(~jso7(Bm628y(R$($d3<+}}V6NBsT4!
+`rAktv{5xip;a{+!&(E6BShPOOX9dWJ6N}W=CFO|{{7Md<Mq2962tHnHw2Qpzx>#NVk@VF(a(@v2{&@
+?aSVkg1VIHX{URb>`1dkxo^)aG`MwfCA>4!kYO0%8B?n{N(|Uw{3kzvK0~s@5iHD{SP)#XEGg8gqY?c
+<M&NEI|rYFp0lpUxOucMe;2wZ&V2(DK@1{FRk=spX$4htT4fZgqh&NkDVus4}e>S@9#bjECkamc>SL@
+W3be9UsVApr9STu@57@hD3G8FiQ$XrUg2TJ`7=E-GGClC(tkKTB_tBG0$zw<h7JvhrR6$Re)Zlc=*XM
+tJDUC1Ks?{dA=B>EaAgc%x-VbmJYmV-g0WFi0CV3OmPrHV=4^pXX6UX#ckkE)D>%d-s*720P2z1Ny~Y
+q-ywNEeq<sV5{Yq$Z-WJwr-rDkSSlG-%j6>M}E>^Fyvbeyh(nXO$Ud2zv3h9^{B<GuAVXmgNc!~O8s3
+kjKc9fm@BewD{3vP;KvB+wZbq?K&kZ+UZ?As*dHzVT-QRfDE!ZG7%jcM;N#be~Ue4gt><L~e)idb<9M
+0k_0G7cf4k^%W@C>H}sx;{ErEhM_KP8YxBy|>z~wklyT?v|#OFR;+xN0bd<xJc^EW^l9qglsc7w<Pwl
+t<J=~C4Sn#qdw(b;gm+v=F}rfXH;m9xlXHL`;;>EsF2+=2>eF(d+xgH-d35Ptp_@G64?S?K8Hl&QXKM
+Sg_V9hy47C-`ZeuMEip^Xjh|m(e#S}=q^~U&xn!!6QGtUi`nnk^_;zb7F~3W5WgLBH@dx`iGBgS@7cd
+#u2jUc637W8XGPqWf^i7PjBiJGE3Si8-s{&<P(XgXcdGLW(i-Sv~RrXE+93xdPr=nFUL;7sYAcFusGj
+I{zm^-v3B?rz5c?#bH^JT<+wC~*1!;!X@aM~M8C?b{b0pYyEyCT?yBn`NdKNo>mvEhMX+A0GV?ryZF>
+A(AIl6f}jGLZ`O9o54z58KN3cs!0ciDyKm-16Z1P%b!t5%!*-kN0c>5aPuTFlYk)d4T1*FPjdc#Og)s
+dNad}Co^}LSqp=dezJl?W>ps$vk-%&)WiVa<dNbTcBqQM3xja<E%agZ)u1&@3dbC#3K8D%w_CimC4kJ
+D;~K-j*qumu*w$HyG|*`MV*(8)mf@glGseA!_t*c_5bve_VgI1wncXC6w`f%l`3z9K<7o>Vf1><<7fr
+yu_Fbx_r_B=MA7I)Sa|*05j=KuuRWM=D@ZV;0?kObu=7Jt`^I+`ESJg043*~BfNvIv-CE`t3UQOd6PG
+<bh>B0!%wy_HQCyT`Mrh$1|yR&bM;Id;B^JC_SqR8hB6)@#KM&7FE3Z`xisQ#oNpBi?fKR~HqnySPe)
+Hz5sSWkkUAW$l>p~KQvO}?2|2N#X6A@WcS$;enAnxWhS*+x$l`nn&|%}c|FPY%yS8I||{MXO+Y442_Q
+k)uHuTe~Q-FLrS4)Zgl<J^h0{_iK6GLmy+r7I;4+8ey$Hj3JjcaS<=L^{^+tI~2(xEHvW?OWhj3sAkt
+e>5ma6`2s@~(+^c5Bty1^U}(*HNbLC572-K8t#9754A{C)C-jr9*rPEBf-oCkxIOPYyXS@!<wGC?Cxj
+AnW6$x!5KZ|{>)p5OD$i+jGO4&5?k+%fxI2T3#O|3-F#NxP6NsjXZMFP>8I9w_JfYk>YOI!ayPRJwn}
+@gaY&%M}^xwIPMyEbRq_l9{j}Mc<WAd*YJkj*><UfA*7<TCe)<7_#?OfaN@MQo+sWSL?y1d4J1{QJ)Z
+T5{3O-u3D7Ak`sr+BWW6-$%$VjocH@{eJ3t)>mH*{J~#xvB;v5i1(E#~0Pm>~K9pqUmE43G2g;|I5Bb
+mRJAFFSQio-C?8=j$aAm0q(v1=o6tiVUW2x1P%8>HuuBZ<M&6Cci)|yym|XR#^#RSFl+1gDoo15QBgc
+xg5NjgqA0I>w#2DpYVABG_vu#>hHg{egP7+4GuguMELod0C<`9v5r%mimJl$ZfXDhqfD0SfFp)X}13Q
+kW@DQw{ZS2FXHfvNjsKy~hCtst*b~Wgw|0Yz@!^<-3aYzRi-q0U6w4-XB1BM*+06DfBzRDU6zUn{P5D
+Va8j*@|*I{Rk;%q)zqWoq7cWA~8}jm~8XyYpQ!Oks(ec!;j@(-#e02jD=(v*~{Mnw!tQE39^&&yZD_Y
+M^{97job9RX)4MdwS2Sn>=nMVrW<*jgYAg5p9G81Q&;8>uSyO1vxKl{Wk!hEPwh#SB**0ig8e%D*P}f
+P)Ob};I0+^#Z%;4KAo_#i#U)qlLnMg`Q?L)y2#5p2xP6qeoNH53YEwAyZBBl9f`tMSieJrnzU*!Sv`2
+KE2Sn2S=sxU4PDvm)LS})S6bqqJ~a#^IW&TuWrr5mNBle0QEpLIG54<SgoK-BO@sPuOoK*~nEXm6s?W
+xDTKDL6pLZSmCA`~T=tfFUKDLj>ckomDmU9NFCAJ=+i$IRSCaRB%FbK$*XjHSY|DNxnYRTe(Hf1B)Sc
+;Ec@NFghQ$N8#i(P|$Nt*9uQJgpF`r{hyv_aqWbA(BnF?gY9k$@?GJSp8jF>eP>N<d{G*%N`OnT`caR
+P>ieYQvH|8Q?CGgyr3aUDI13?|E)+#EWE*aIVYWe{P}K6M|(IwvofPXp9r(S`M{&^RabBV%Jd%EMy~N
+@mUz3IkZ2;)QvbxCWAG-S5UfP_n`m}Pr2!tx%ZlZcf!U`+u3eD-Kp?FZ%D?}6D8FR&)NiHR}PvMK^}&
+^^K>3{q_^yLEv4Es_DS>D_Kq!h!bZ@zM%3Y%Z;dE4pR`7^3$pLC?}AOfCl5sGf~sR(f*?86!MMJP{s|
+GlEmNu==ZS`=9WlTyi^s3i^X>rTs6XBsV^jMVs-4Pi?u_YZY&Qw51JB{??CCb)^$O1Oh4@UkZqWj7EI
+#=&2f=Wx2xH_ss;HziE*ph3_WSHT!l^x4SC0%B6B18p;MN^p=i~_{KR?0oQiF`=->^yY>IgiRgl(GmT
+JBK7j8(aHk?ZE0y4SHg+*qh(qlsMJPPY`PdxZp9osNNk+G%lRbNlk$y>mT8d^Yp!$1@2!W6{IceFo@6
+4?EIPzy2;Nc?9d*5<JwHO*1zaG6tBGND2(Mpky&EePJm<lbri6Pn?hqasm^##2Uu1w6}<*S2CbpUh3M
+}9Xn$`d70nji)uxVOLR}EhbsO|?Gz5y!t870Ob6J&N#z(MHsLUpP}?`qhOuqw&C~!HCGB^x@MsuINAd
+#5RD)J<oqd0(vv22|v#W0zraq}lDcMMUjBRftJJ_-LW2mwCU5P<#`8-5>fKAzRQEVREfa12=;(zn?j4
+mp>6H^9eVAr!hs~ExIuIZ;3DDSNK4s2xBLUlWNjas>Y>-Zr`o^}_;Jeogs2(6vDK2Yx12?&#qhh4)qX
+O*wKz95E;5f^RHT<=dEbKY$vfj`x>37x54bDP>}T5FbRA#e<Ckde{Gtw#}I_oEQpt}KcO@DQFqk5&}%
+WS+wk);SsY^FbeDcR<aW=rY1Ue&w9?o4@}og^I0+!O!e8CLG)ld0l(rrDXjy(rn89=F+cZOo69Vr<CM
+Wj4k2?oq8SHdvWj!indIiRM*~Q2KSB)Q6vKoBEb`3W+<2DL<dNB0AMwKz6%3%{1FA~q4?l74uZ5*Z_O
+y}b>1UBZ<=H})!ThKRri-)R2#Y=P~-~}QG|fyew68Sb$zLnVI1K^ZiK+H|9l%;{xL((VQP#t(ZxOW5Q
+!>Hq?HudrEa|Cp%9=6j%-B!M(=_N==ZNU*<eN0J)t*6mP{>(sTrun8l_ZE%vaYL?+qRs8n|g-zztT|D
+mqQYPUv0Z#P+F}d8L}qP)!`qV&G6D&3C>KB7pw=x{yc{imNMD%=><C?rYq-@Op{H$F_cSn`Sq%!*#6t
+?bSkCL-z3^3t$@&kBJ0n;AI-TwC)|cMT@T6;%hamq-9vGNXFEHtAT=GjP)CEyn$RrU)3q8j|zV&3YRh
+In{Avhs-{NUXV+1w7Np~>L~}DNl|~oOnqkWt5EWI?X_3aWIj5aoI4QP$7c=JP`1Ub=>y9)96Za6wGLV
+@GoOVe}5+#bVO|elma3DPmN2bq+nQUwKu+lJP<8&yOVefhyNtpIMw~(ovF>G~hl6y2z-H*Ax>)u=S4q
+ipsKlb2g-P>(}BR_Jri~0H>&ABWIwlW&H0pAYWq};W~V4L*q<jtRbin7n|nF6z)Y?Nf|J-TyiMT~^)z
+2=F-9p*qir5Jth%B1NEL~ct-`$>bPXRXAEh_xCLZjyFHI(|r3E7!HT8AE4{@DfD=01)?7U<=-}W$D9K
+SkV(71xrNZ0acpv-GawRj`BQilEiquJD3Fxs?*5o)1d*k)Bk93sO~VM(mi-wQ%$&*Vjq6G^d~iJJCt8
+wr0udV?S|R40gPEEPR{rVu{}~}CJ5cLe2({2k6#{rfAsB}lgZ2DckiRH+T$YcD0#y$oQo=rM@XoPI*@
+$G`_q=(c2i3nLHdLt(A_JbPK{qSr33WzcFoJ#qwbgn8TRncNjnF_8xyr5E0Ed7^adHP*f`XU5~}@96{
+ReY9sUEJJGM%3Jyu}^Qusk3t0*`5PU73K{i&x~y6X0a>>!ohesikCEuzpJM1dbB46As>KJ1za4}4I0U
+_6Ty^tfYR(vYM6mt?Ma3V@%qhK%EDdfe)!OMABI{1ee9n=xw6cQ==WOKu)$+AW}=(%othQ@LA>Na2=&
+IS?lG^S?*Ujtt6IvKIwi4qG(ZL`t_qbRtFMJi~^`Nb)+9yI%cZeEirLx=g)?sKL-yN(#He6gM9&uPwU
+FI+LbF{7h~Vlz0lO%Cfjhg=Qr}k|1T5H3izDmS0c_HvuV^<}C7?tW*YokufdU`;M_wEh@XPX&Hi)uAL
+C^PA2Z|L=&lLv^cp#F#mB=+++(1C}Kz{sIZ<%LD7pK0E5DqL$z%ai+oZxc7><DVjI~rZIeW+4Q~<oHX
+#;e&9+L=cN^h|Z#*PCbB!63<6M-p#fH+Kb-cL~=Lhz_-k~)o-qS##l=gNsge+IP0-e?KiRtj_>G@gM^
+2VKzg#gB+z3>;lRwD+~6gm!p#(<nE0Ju!JTuu6l9==E4W2*xPDYf+I#e$%ra>DNQU|CH`FF8L=PLdSI
+>l<)dVqk9)j>Ni?=ng0bde|E!E%?T%*)l4%$uu-6Ijy~eCGr0CC{^eDuzA45Uis%0egt@cKJT#!e=y3
+teJH_2_)jNA;m5>>Ow56fU!oNkxP@dB24jl&1N{arOvBqRHVcdsjA)OWqtoKw8@S!yFXbQ;b184_zG~
+X%lcjagsqFCO3Y2bOe$X3cJ&3^PMdr}tEliwx<PeVq^6Yu*G;$#^=8PQqMK3V9A?@=D*#3OL76ddjLZ
+9;lN`aWPKa9lK#E^=>hV7N6K4h8ioGal`*x_Un1sV;<HDN6tYvF*N7M<hg9^p@f`g`nu9-%S?zOS*0^
+sS;JF{7HfH*~2m-4tBrx9(`>X4xE03Vr7%oz-#$&Sr&ykS+`;g3uHgpp*y~{Ebe}hHk?|Htn5I9K$))
+vHADd?E8rvoO~mFE(q&4;k4`gV!<~}Nu>}Hn??aJmPodkbGq=2E_Wa0d&cIEw<Nn!(zZo830>5(ZN&)
+qakGk`X5jH+oQ=LXArKWncfY~A>lfPt*)#KqtZZZmH(D#*+=r>~Iv8mrx}1Dk6E%Pi*JUkkBhf>ke#s
+p+?QPSEIeEjLVZ%>e5>FJo|M_Uw25XOR(ESU0uQ@PCGZ^5D<Xw##338)wot8`ZZX<gKJGI=@T6yPDo3
+D2WYbCk!w1h=zgQ2w>k6mZ}g1In8-y*i3P<?HcAY^m`x{6xM|9E4dJ9)xGc!wU^p^<xwJlqAK(|r(t_
+;kI#OF!JD;)k6Q&6ao#<}asNEsO8Mgz;ts-PuuCsvw_6Pz4fb`nA2$kT<|14Edp`99!6gOZKle^{Q$(
+@<I(zSDC;|S*=|SQx5z-Wiz)a)fJ88U(lFw*k*UqBeyxVwH=v;yK3Txt?9Zbxu&8EIbo0HUI|k@twYZ
+^6n~D1e~vK$-v1o*q#}Tg93R9&$0Rjqpi}+Va-~p90Almbs8j7S6yk-kt?MTAYvDb<R8!~?{sja<@nv
+lABm<7z4NNsbe%zqBTKk?G6P`f8N79-UbX|mkJfjI$^lunnVmuAbER^<)`7?CdTo<e4e36ycJiJFT*r
+s)Wku8MW+g^qI{g~C5&5likQ_`62pxfD?^vFYR?A8MVe-C}{z!g|RzyZqe_r5)TeKa|G{gOyGHT6X6-
+iPMFU_X5@{OaF&!<6$x_SvK?mS6Hc?7evN`u*|i?~Wp`fA|oEp&c2C$>82Y-^1HCu|W(z-2UVAAUzvC
+94aU`pf@HI_4WPH+jqxrUdLYj!}zOzd<p*>kKsR`jsNj~$3q42!Q``t0aQdv1|sqL7H?hRz@8UxUcGu
+Afj+YDz4_zY=P&+z^gf1zQ`_prj#g*3)vGrzzx%e0&D)2A4=Mb2_`snO?{dd$Km5kbKFuZ>e0A~%+^J
+ul{-1-hheK7y=>Q)A7TZtg>1vD5Da<h5!ot8j)-ThCfdllbz2Hyz2L?704xhrx{qfDaqvRj><@lS|Z{
+8lgc>eBav}a~$1NzYVc>)}2?cY2Nz}~O{6EEq@o4h19A6_m#$66Kz20`)BNOoI(c~^8_sm@9X=tLISj
+0Z!x&y4>>k3x8}nV5ih1qTX7wA%FdSa3%aK)I&fShFb2*c(MqennWkmq)FG@8r4q<~O4$p=FGUPtmua
+2c9Z4d7G1#K$m6Fr9^{4@8jv`%^4QHz^mdpkd&GaheLVM2PT1}9IN3F4{V@au?YLbY8zuyTf<Ix)S9B
+7HD@Ol^7Myr!X5ULhwhwnZQCGt0x=i~PP?zCtzKvsSkO~N^6ae6)N|x5DE_=2Ql)XJfg7*>RrAV>F;D
+Wo6_{i-or-94DBT!FD0!1uQZ4Xy>2<sgm{7ZnBB5(fE*t!pM-W8nXLX5p>7@8aq*BO9R>OkQ_4QQU1F
+M$-)H!bJ85$?jBzb|_erm==S@>;0sTq)z*~=H(wAaovekp+A4ElkG)@}ob_w0IsVejLo{!@=?q{B!kt
+US59z7#j6dFf;%^HV@J{(>?#sMG@ah*r3U9CTb@h9|QW#1;{Jd@-#VU75+k-8Lei7<Mwoa`E^RmW#(5
+juAzVcV68Vzu#D{k{PMZ+b%d2wW@Ju%q&{7fhNG56?YKc&h8<V7lk)bUfvKJA90}T0+gEwTh}bn3U`L
+H#l|2PVjbZt1F0u%Iv(1x(U}+4)nhbh>iE&7;bKEWPfeWfnA(lEReg;@Fi7u~s1OuQR?SnH;W3+SnXL
+x;)oeaDS56D|K#hS7T1LbT8f_m0nZEjJMUV91SHs}hIMSBa{mau2w<$;)VS_^y+@KW_iD@(ZDguVl^5
+uuq6vQfO@o(H@*VS&!cBzzIQ_teZJ63qbW~h_^k`|E-C(6ncU`(As^GG$5yT>moj7ws#HP!$*49b4U0
+p3(tlZNneNPpw(lT(`F6Ol)rp6N?T&7cSXWuG~bLM)<0L$J0$b=3cKdPqBOI`!`4nohZ&<_sRN+DXpj
+6;)JS#Mwx~m&*yIuoO1{Y_yW-gBrndJDNL$lH~^L!jk2&PboHn1{Ph+(E5bEY~73OK_y@Tcv@J!w{WX
+0)62Trtf)e$W;i1I(7kd*8eBc6!2(gIeg3<zoEJu(qR059f2Oo@DXwpxke-*cQ{JIBhL6)lzDi$s+W1
+x2BlpfEy7M|#Z{S4Q{PzUQ(YD1_^)Sl3y#fCil??kIQOeT21m|RmKZcH|aIqo=k{^v&dB+Z(&~ox*=}
+!(bYVO+5rMH#o&4{QYf;~OHw@6Q|jYQ9{x^of*FE1Hhhiz%yp8gy3nXl|_L*!!<LlwFi3_f3_ricLrt
++XVa_V8bVW`H;8dQ7fou__P)9~!_ul_zIom9u26y~W)th{q7EA4e(P!HA(Xnu%Jg*96O_3`GX+EW89r
+0y8OOp3XhZ5?W!M(wcTToR?hEci^J=@dKlatgj|a3QmJ}TE1*1n`o^HX<?%EI^x<o!}Ho}t<I;$@?De
+(*li{V_1BumH|Bq%7Be@Ak`VD}RJUu}7}TE9?`L`!2m@&E{T5R}0AiyA?QB`y0O`$tGr6d<OS=2m1lJ
+0Y7=+8OEgep(uhSsV@3N^EvMmi91BRQRyT-E)Lg(XtTQ>wa7#NR`<VAFwW5K(Ae>C*s6HHImYU1UshI
+W%5YS=nxD49{ERrK{$m`z^57aJhQ`|9?Qi5fP$>g3PgOeh5hwBkr+cVJU70~0~phUD{CFOQaf@)lYy`
+7@2T6Z6B#Unj4g|JR$hBU?x<^0o?cMr#=w8C&V?s(;+jcwH)I?C1x8pS}q7eeYD0;lyKOY$s;arUUsp
+kbeW?i<>3d@$HwOk<1#K$e(@#gLY_ONh6rKK@P&OU;IY8*1rv3N2{%O5x@?%?uV)qYd7Ecu8<&Ynidg
+NWqtR6T8fcDxuyn31Q@r4AkH3qVPAK>@WpDad~nmiTUorLOHwAt|E+a3tbH1WB)FsqjJ`uoI=gZxn->
+=sIl4}Q2m=z4F%eCSxskERc&KG0Nad4oLqJ&Sji(K;^f~Io%kW>a|Kh&@p<UF_A0jL*8W2J>ohm$@h3
+pMwUJr;P4V|f=I7D>1V>4BM^E+k{!^YVB!Il~OYG<|HtE{=2Ems{@yg_@{Gsm7p2N&$X$iwS=>;D=(r
+@XB`jP>D|2{N&MtcjtIpn+FF9B1EY!o-IIQ{f7c<h{CZXF-Kt{f-JV@@n!2Ze_lEkn&H1H0}m<`Dugt
+atEYO8`PIO29^KV2}&doCCO3?Q{Qt!ymvBb#gQM|xC&1oplC6UdXi*ZQ0J6duSV||lZ#Dhykkn`o!Bw
+fGz*4b-$!#sLuG7m9~K}jQ~vL|BKb7?Y0Fc{)>!gcB?wysa=8^imJ4q{z*#)nlncC3iXu)}{#k4#m^w
+|8L6PR^h-@!@+NA-OZE46T3?K$gL<4Ldv(b!!4~KyUAtNJovKH9upH_MeUQqUmJHrm`OTeM|zb)A(nj
+R>9Vs9NI_jz2T_MVQ4W-{na{Fi#67v2=CRc$z+p~=0DQY$`1sWSkfh!UL;z)LQ(*|uxs-+phSEMNsqP
+wBVx()WQtRx|<)<K$ILLSz&pfZVc%+4pzH=<X*!Tyf!27QeLcxz1--WicaC&Jr1od9Lkx`>v2ZTPQF^
+=8G>LJJEsn#R4`L)-}D(7npdW-nUz3hFgRUpYRt}*+Pq>xi6`}c_mA{Dak;&%GP=uYl6@S2rLCc-T}0
+9;rwvt(5-K*>)Xp9g4s}zBepHpCd!j;BuL&>;@%<-(O)c*OH?h%>LUIq1qac{nJEgZz(V-;u53-QrdS
+TLQVua)E?-{Ty@YF!h|uv0LeVW~VCfu>|MftdEAZ3*yWW~VCxgEW^q+?mF@B!ophVk5r-ZDyf4G-030
+d8!Qk5outv1BZsKQA#p95fEp#^2t%wbjQHB1>p^u5=QOEL%-vkl&;g@t{kDp|wD9AH4iU;*=G@;H5*!
+Xe~Kvkf4I15oB%&wp85*0ea4rAKq^)gq6Lz43<x2pk$Sgt{!5u2@d`ib42}NL-GTO~``>4{iXT%m;=e
+90rU+oP(W=yMue|X5OJbcMk@x4TG+JtyexRHClPR{wU>Jnq+)!c`$U_mDIXZf#rNrHgzBNM^A>QWY^e
+{)VUKAgpFtlwQQW;tD-^gOVa<|3_H2XDd7kx_7Py|^M*J@+cn%WHxbCs6AyRB2iLLOlX<=(6XhqZw|V
+PR7JR<61vSm<Xi6meLUMZ6uAgcwynW*(G$m@`ZA@XNuaT%~6M;2ie<RYDZ_|vB8wXs*$%QrNI*UJ*78
+s9#naR5wGOB27%dEcUQU>^4QC#vQPG+jJf=%%ghB4rO?#OP&&wlckzx;(5#+^+Ob>pCRaBC(eM=Zy?w
+y^T_rw-_@UI;g4htd}g%%K_%ULKGB%$GVXgYk{I9a4Ol#je^pdm8d452J$OeA#len=*$sc*?ire~Ym}
+)rIyZH6n|<;1n);gKIfuFjLZ6HgNjslT+=PaL2*-pu98wV&(`az*GjdF;byc(@M67CDy=<9ZehZA)or
+IF${8^Izx*sX4?S;(PhM-DNN%n@fx|=l@P;4o~<!$<n!7FQ_)iFDqv#PGGB$2w(l(0X|%$fMXp<CSyy
+T6u5eat;zowW>2ENB-IW!Lc#I@{Mk{nEhUnnHl8b&#lYwl8u`z@X19y_ihHP7VRmxbqW+6z%aGFf2GA
+Lc3Aet8gjCH2Ic-Eg5mRHx89Rfo?lFidyyWGHW!QOFZ6-nT`zEq|lb16wAqDhMW-*Sn1v*%@{`i+$Gt
+>>;vBOQ2>ZGN8X6|zq$l<)xlf3=lpLVx#G{{2{<J`5sY>$%Z4?=-U+=R{~q6MTABw{WRt>=<!)fc|u^
+akxoM^Sgr$O}A|qmI8-NWbk=2RLr7-#M)AtyXQF!TI>k*Y%xzKV_urf0~&%6QTv>Y+6oPGGF=xu@9=X
+!s}<{=ZkWZIAJ2Lrt`RvpC^c$|9{QF+nuaxi;TNCmU*@Q|tvQo%Q=(RMSyJxSYID9QX7mclh!hx^w#t
+hJoC%wI(tZ05Oup>S6Fn&exOD*NMR0x~nZ;cmAG>kL<KuF00`Fho=y7g=T_ujXb9{!bc9*T~tDae(f(
+I^U{ilPa(PB?G&&rbNREaOsDVrR~)q=|wax*MfF<URT-7yL=??J>EB`Q#8K3nB8=6vIEH@QZ-*jd_D0
+sT~{;p(;nhd5TqBA)62aMdD;N|4<nUj0BcaO*(9Z1{BQ1%!ZbIi**pbo+4_BdS+{LTao?!Xuyy8g<ut
+&GL>Ijdmoej<Nu0j1&Q?s7k2xIvBNY>&sVs^J7p?oJVN%QNd6|_)!Sbm_eNq#RNgmD3NQ(>9SRB2u-}
+pbV8PGHB)91pRVUd0iZuLh>53JwuFwDWTOa2WE!SR?`3ZlZLm$;gKyUW$3Th#uND?XinfU@lD+5iesr
+uNA*Do*TBwIi<+NVYDGqo#)vKh94?dZ2Bn{1hY1xj{$)-+>CQrSbp8iOWsdW%%AgTHwj9sA7w;)9h#K
+C55QVqTGlolR$g8!A9ztdK{f(yUdZC))IR{MZ}-`!%J#|zJwUZau}iR5&!B3o#rcqXaU@L!U&&Fob)F
+8w~T`PF&3S>|Zu9F&n#hLiz~Wje<+iMD**z8HoK3g#u66B%v8&<>6*z|faA(0!YEot1J8>t#VnA1WXu
+sj#RnE@!+lr3`<!3S&MrJi3m`91wV|)5XFM!8IQ)0ZGI*SP?n4+mu10GTO>VU;BW&*97iAcwP3UHJ-L
+#XJty5H7g+VTg-e%r>2zLu4Q-fRUW@j(DBHz0Lk1$%(;{+zV`^Tg6?6fD3RA14B|yLh&|Z-51&NM6kU
+>b8^dud;|K5HqaIJo)fqDy{3}A6SJ`K6ilFg^GKJ$~9jnu!43RGs6fNg`3QVN1K`zUx<^-Ff%x?-d7A
+S8O0y;1qHA!+f*tytcv!YzM*_uRL7(vv;+Nq%H>PfS9nt6x1hP((=cVi@ub-1op6H4WY83F)d1M|L=-
+BH$n#D@-3jP8BERm7eVu|7oOSh*mW0W|1)rQOixt=1F20h5ebHlnaEJpEIpw@Z&evi%9EH<L+AA(g>h
+kjph0ge3q<&+~$OAqj3>^aj$p0o|_ay%E(kn|vZZuSnV*7ezy#$*rpZXo`}|*)jWh!?_}S1v+~kshm(
+G`wn$f!#+u{6{LX%ScayAFs06JVYDCK*RQ6{GWRtL_(<t}SDtT*1zl=G+oYE~q;z8O1ja<cXhaNhT*Z
+g*)+v^y0v(s?#jLh%y^yM%H|t`(A=fvRKHLIB%gRew3t$wtRXwLHOD*itF2X=uFJ9GHHk4V)MtNC*%^
+mT5k5N#TQNedNNx6iuQo{AEb{cRJpKr9`-)MU{9Qv>I4fE;STZ--7UjcIcAg2MLw982+DKGIzwbOadN
+)P^PIic~e6<FB~&LSN`eGHPj7fP+a{F4FIBONu`thb9i7GnMC=>7AT&)+}iRXX}#NB=9V4=_0WFc_Ut
+af1)@^a17D4NgZNhG)ZGw=+`|b>yB_J&EVX%o7rNlQ0lGx+nb@90C3*I-x3)-SQ6vuI@H7usx!)aG4b
+g)*tJX=7P>|`i^Ew|Ne#D!|%f3f15+N<qPH?W?Xt|!d_n3%SEdubi8Xt26f(Y^7%RD4Mp{aJgmeL3rY
+X$syff=WbkD8`{X?PsgGyC!2CKMKfx0ie|Lj=ha3g7QH#1Nso)E{Ku374pghJG0wEy7ywE~aTohE&)`
+_}hj+YT+ROp>9b0(RXmCdWXDLS&9O%WY$cE+dtd-5s68F1R~`&WV8@TEh1iRa_9R@Mtn!aje}ey(7^<
+^^8Qw2b>1Fx~s`WIX=x^ux2>$hM5WYyNa$E(1hyU&Xx)`G=7A4a}?TPuHgEC>g~pp3*StqtP#?o<j!Z
+h*hz|fRA!s)v36?7sa^<8;S5Tj2)6crHw<uT@ev#x<3Za-u>j*hKbRkT9xxcY_V84q_OdxG+eY3=0Cd
+QAz+lmkg~;M&=*r?bAV?8-<5EB76fv5IJZ-XZaVloR3jyysKhjdh%4SwO6<LqF*qTS*2n6A2|&MtSW_
+3(!u!f}D-Qi;0=V%)3)-$-!5p@ZIukLS(++HawuKsw{PEv8ueN_k|NI=0aPh+qU;yEUhH+%%%>gR=DF
+;QF@Tf-`2qE(&XNVXdJp%|TP_p~{ZVHgg<(dO>&BsZgD=s4*C<Vi#r<mAAnfaab;uRPs2T5xRDjs96m
+6CzmhFbC^4TpYUxO>7ke8eBS8$b@DQWCfOSyJqLk(=vbD6yE-P8ih_`i80CmkT&P7EP=!<vvF%@{Ol;
+v*tP-u=*(Xa>eo{c>qf413=3hHAM9)RUFjXq9n+VbZvEP6b6X0RRsk(RGX=@0S$YYUQA@P*zu*m^gOe
+NW@<Y$6TGsGzW5P}stAd|N}hPtUfeC9%CADQ4m%!PUxMr%9$Ayjn0R$^sALJRV}t|6s=9^#Fg9UxwV5
+0QNpqY#!|@csNX-QP+h(Gg8gOLp&XR$-p6q~c#2TUrw3fT?@;a|B{7UZo$;{l%2+db3YDurGtD0-(=D
+NWBhXY;TjF-e*z=5v~S|#n&If^&?&^23w<M&6eM#=l5x37+0KYx#DikzUm68nF{hGA15%2}qQipQxkB
+s1-y*~P(=eD5$39r&xM$kx%KwVyn4#jqSXZ8+yV+6)ig$E_zq-0#-a>X`7`iZU=CDfjT<11~Z~qr<@i
+@?s?c+!_5`x_WzUV*`HJ&lV?q=tMC@>Q-{?aBVDhLiAB}ig>D>sZ#NA#WjV|QeYPm{_X)Kcg3A{j?oZ
+CFT?zL{QGoucovMDsKOl1I*0E86H|En+e|-f-+=|HYuAZ}4ircoM81A{xoqF06n3qj*$Bux$vB4X{-{
+rhr{(;hx;R+yeLufGx(#|FV^rse#R3revM8CCVEjetd?Rrzb|J{#EH7L_-72aB=7N-eGg>a6NF#06T4
+@hr7m{p<wHLF199t0PItX&7HDN0MLD!6JofXaN%!Eu$#WwOk4XyaGIJG@GKHZ`p#^fk5<r)>^9y}Qhf
+pSDp<?009`-+V0OH}g2>rx6<juKOP)9)gA8kk0VS_xKl7!2goK?QH#WD9mh$`_90h89i>V&Pe>`XGqj
+z~%-^QMp1(5dLM(I{^6sPj8afZ{8;_o`3so^8Qc9?~?CczkKuJyH`iA-ygk9ULC#v)0>ySCrPXwCNlg
+?E0rXiY)YH3KMKh)7*jN{Kg_&uN3(Fcoc~~F-W<JU?G;`YnX_V~@64a?@_-0?qaI6Nr#<t>$5FC9<JH
+AK-O{>VBmEQA;lKVPy9v1ZBN?x(VqzC9fQN~lFEG<Hmgn5cRQ6HeKXclQ8qILjH4#ocPR2os@rxkEI4
+aKCneHywXfhU>h}K4Lo4SudZA&99f8OB#^*chMOuZ%_C*I(9Wc@~y%iB4?YOf5v{J8c4>Qr6$LE&AE=
+ocsL^6PSSQ{yg8_wT3lqusbzdBm!&o8wiYxD`JeTbhf+%4_#bOMqelererB>NtN+*F>)BT<!-zU)XK7
+rF0ldfTJ8WI+{p>l=~N4x?FXkppk8igCbK*mgAVJHDBL=0R|>$Icb&|UBiUdORfoL(%%#~N}<eVvyGI
+q441;EtkfW1@Tw+7r9$tY)UWnmXLzxVjcs%5coEb8#LMQ2S+xFM<BfrAcywUKw9cWof7`|;%y`R7PE|
+n~ebPxS*yQxFAAb-jl4QhCx=jspUDbH7bfc2wS}(t@bZfavonMj?MC9VAD~y(P%Jet+8a+zzyWAn+U2
+s>-_+M2IJtz=Bp||*s!Yd!?Sa*Y-nzBI7{Gc#H_3v<4lga6mL&i}1k<7rmmx`|xK)ZZ(uCM*$cv&pZO
+zo13jKYaHxUD*$vZZ)xUbj(gWR;<y0fQ*xY^=`7A1{u<KF=3^9S(hdvu2wl!;QLT%)`_5nXF1qIU^Gr
+^)yg%TfvPi?h+aoBt?;@oKcd0#2YBhY$V_;5`yOPLd;#H-Zi+=OwA0QFSF|$9yIo;p{esNF6!itS-b<
+@b6$Ns;MOSj+o<>m28IE({qPo)>B+nAPEOvuRq<sK|N4gMzu&`QUIUa~aP51=RpG2}+T39vQyJ-04))
+J@7&3??_NmgtWh2B<)`=I3Khu(iJg{SVT>bVpzcJVilOz>F$-AX*E(i@&K#J%ilb1&nx<M!RDuZ=oK6
+!*@vguUXOyvejG8nG_acYeyY>b<kRM<i9RDL-VeF2e@Gf3&pfLurdo(0XuuKc6%m#8}rtY{&Z2OJPHq
+(^Cz9AEGR`NFAk%Xw)0qQrzMqb-k*pz)}w?)0`#HA-<qa`m}P+G|wczLKz4-_~c)pb|c0w$2^YRbsL6
+`Gs7BYvXTvr(u~2)5zWr+&c@KNHa4my7GjEv&*>=l0~bjn<beqo*W)ToQ>wwn!owPgY&uzS!xmkvS`@
+?+VG@n8wN?usl|SR#SfCriVb~DPPL5ocHn?H+VUFjPg=Dd2o)?N-n+~!75}221HS>jTh&=z0MRlN0Ii
+U?pSJW100K3!pa?k<pABH>66VNon8d=Xr}oYDsCdy0-KcsFI$^9wna3Vp*zpcI+Ke}iSB(G$pGXYN$k
+xCC6r<W)H&O}*4#?yRdjfTF4bW;BLbZgM|BdbHvkaANssMz4__w1bKcEK$$OiZnFCSn0x>@7^%Dj9lj
+q<EspsKrR_|_Ic4sbl<$3PfsIHOt-#Z(Uz3vcA<o^^Wo?+$t#ehJiuL9Um;rpF#CMlw7j+5$WhA^w)t
+*{n)!#qs1x`iyA^OQcM)rkLl0i>|Wy7x9wqCg%>i`A-Fh;i5UDpkTkj<=DEf%&`DkK9W2Co)|=fN>OH
+)$gt=6qBzf4z~?g(?_@q`vKzeMm0*~*V=DTNufO77WY=Gw_9xP&e@50Y`r9%+6E2BonmlV)>LH89&it
+{<Q*+ai5y1qQNsrl$92g?qHKj_858T;F|DKR*oavrho0{ercFR?*v+G=nSKG4c(<#0E{Ny;vZnA=IYE
+GHoyhj;>Xn{Bw)sDxwX^cX-xFFY*I38qy2>Z-gF2b#GOb*5Z`H~bbp)%Ds)int;c)bp@G7cG*iL<ARj
+_blW9c6UDVNFCHY*tdH)g69KvMHUwgp;3}vyJ7>0<_Ot5MPVt+CL2yiea^W;BP>P=>!8F=duz)J>g~G
+tQdeit)Pt6-@?hwZ3|)9dHR*bG+b?+lM}RUJ?E*-L7CvhZj9rmYP2O0zkV0vC1*8k93IQ2oZ#2hIvkF
+DAg2#<-&;&3HZ^7n5ZSgC$P%xe9w)8Wo=c`VC2M-6!EESkRjR8~N$7G)f<TG9JQqSD$Fm~!!m}L=^7J
+wV5)nAK$Ee!8-U36e#e>Y><i%4xp4)t}NOxg&u*jC@bH0(_kp7$6#Cp-SHA->8iriMC*camn*-Ge_t~
+&+H4d^ztUYM6*m?>XIN_BIIf$sgkP)h>@6aWAK2mtey7DxJ8ag34#007Sm001BW003}la4%nWWo~3|a
+xZXlZ)b94b8|0WUukY>bYEXCaCxm(TXUjF6n^(toSHm{S>l*HZf&N9C@NJ5S-?!q6Gfmky#bo-ZjJu?
+J>4LpiP@d1^&!H!eCIn?a|L5o?Kxe)NHEd?+>LJUU`82VGXYNo70D|V@W-}({Jt&{nh#S}eCu8HuHcd
+If(k)d36jA^5ED|wGofG^UEz09W{^=KIep$p0`djH1Y60sgcDdXjs|<gsW!Y~g1{S<FMv|@%{CHNgaC
+TH5Kf-=u;xiANoGRD*_LJ`L%c-d9|FlU+Y$sy+iXfEX-YH!9kn`AJ*cXRBphf7NuDcDO2km770qo3{b
+}^W3T$vfcntg}XJSv_ixuMg7qGmEHnNt{+z(*lgk#ro79m)!3+NSCUgX#zLj7=}IRy613edwxCxkmfd
+t-MwalD!Ka~6-TgF%k|;7<`^VF%+m3akgmb)ug-_|%C!I}C>i0iF-`lkG(i&J|3D*MkkNV?DSwO#J|n
+Ebk|TkM`KHToXwL_Bb*T;qZhWW8VwyKbMGxHZZXk)=bd|v}^OKvYJ~_=ws9Xa|@Slq;i-B{sLS-RJ36
+k+8Dx$Eai;#L&Bknem2qz6l=><gvOEMd&(XIM}ehSc=pV7X0|uBm8-AqBR@dHW!N~FU<FR7V*1OdE|R
+Z75yi7>OkH?Y3uK}>*Z~${VQJjyX(NWc-iosV;&`=@8&2XFXi+gPadx*<%}isEvOFiLM1g26$ln`MYq
+LZWNegmNsppL3GO5)?g2UaYQbO}uD)udukQw2;7N6tvdc8QtEmUNX-aYhhhj)FHrg<U+IOU3#R1$+d|
+AhnxzxMz?E1DBDZE%%D6iNI>%Z$CD{C3pS!i=mib>$z&h9LRMgzJQ_1%6$>zNx1MPa~jyhzMkd2%}T6
+kO`Nu6sS?0jlrq9)|^<L8DtU-bVZFK+2y3XudQ3wM?`fzUJ0I<ZIY`Uu89t@28A#>d~0x-NXeVr4qk(
+9_G|1My0!&CqYemNDM8IzsN-bx<NjF?PH=Qe@K{NDXR`JC_{yvy@Ey+?UMIP4=_K2=w(Ue{ki@1i4y7
+8vSHKc=-jQZoN0Q?`9bJk*@}v?ZyAXwz(x@8<H>dr&NVx%w<t5B^8Bm|HG81)c8^2C3HkI0=bCe3F{A
+i=;d~TxlLA-o!Q<9x*no{21p7y(hZ!kLTaH+TEY_c_$8e2oDZ`w&$-6_weux`yz{qU5TfgSrmuXhBYd
+LM6G=jFE!z7{u?>(mlQ`Y?4=cJfopvr}6`<!Oq0O7aYL?|JD`o$Yf|;z4_b9ywccB7|``M@`6IJ1`rZ
+EaL!W>QIAW>wfNdN(rw97OA-fxOPS#C4Cc5-RSb@>Y2Q>2kKJSD~4MM*L4_9@c*Eamg~W1sK34pGP#I
+{Zz(C?4gKLRDEF$V8M4HDF9IE!^f6LFOXJq;&e_+tv=_;>^|%C@?2NWQHyv;jMuUG@>kFg3W|AF7TOo
+C~w@f)sLCwA!?G#gww6gsV3nTgd{3oTIHg$t(XP3z?C6$C|UlE&g#!nrO7f5~n=SBPnP)h>@6aWAK2m
+tey7Dodn?<7V9005H;000{R003}la4%nWWo~3|axZXlZ)b94b8|0ZVR9~Td9_vBZrer>eb-kEVi=YLN
+_E_qB4~oHDM}(hkt#{q2m(W(#ig_@MY2oMwt#$q(<|Bn3HsW8K>sOU(%D^-y3}q{6cJz#cXrO1GsB(b
+5%jt}Sfn9Kmd~IpqTXl1(`p@oo@N`)ma78Viw>NfK0SjGOZhU*;ez5u@HNZf^BeW}#d006xW7o(FIq<
+ko>IPMdCt-ViWJJ6YM3P3Xt2gq5!xULA!K>M*>zb^C{{?8r%`bma0*e%F<^r{S;@0Br*O-P6`)txv@9
+T^6cDkZoL+BW$%CYzp#~XGZ&*k}<O>S?M<KXQZzvKinrf1?U<)b*$gJ5`YQ2?ZG~mDz2;x{E8O{5$EP
+rf(=M4Qz;u>IjFm;^^b6^bM3Gs0K1c*J5L1gBS9Tx_sr(49Fct9)*5aklvHw_P|E=_+7t}!AmA{+!E-
+Im(ct=Yh|M-pdFrk06Gws>%cNHH;7eT)urZd#`QQYIgozHNA3A1T0gz_>7MAH1=k+0k`wfMt?%%YdQd
+A``J+f;TmElUN#xb`9OvkfDA;gzng$@#PHJFa`!>LPkP{E1}g=R5d2P=U`SBYx8E7FKQUN&IByS6S86
+E8JL3jM1Zk>DA>~wXP`V+s3x`$r2D30i+D`zyF{q4jge)J3|lt@)RFPN<6_{<s{k4h*YreAXXdLWIg%
+7vY(sI%#*1E{5~;y(u?Z6*d58NiqTgyoJYB<l9+gGO>3j}sou#~hGGRD1bRM&U@*vJzRb;;onDTVVX`
+WZ^6^-$r<@N3*M_imt*d0p{x`=~3hx359cQ>?#r1Pu=_`78xj=^*I^h_?-xDzZXI!;ccBQK+f-HG*an
+<XK=>x7>ix7Pt$6>uH!dB$lK1h2Q=E%Y@gFI{KFW*#vd0THX5_3}t9S2%3*Magp<&FA3rlD4Ejq!E-^
+7!<Uf(>SW+*90`2$23{WQdm;RkiM#|3v!XwbvHPrrh)B1di$KM2C{9BjINi*a@j;-@pXhOu6sbYC!mh
+p=}mV}S`*&%Q^r@TgVZrg)XVFCm9nJ$s#2&a%=TSrLZqhh>})Dht)V;osr{>M4^`9D0c(roo#yE2tAH
+=@8z7D#{#7q*_dMb0UC^ZHgvZAZo~4J5(t{`I{rzwIXX*Xh`;$h<5S^&Q`=-PXK`!L%NULNW@V)*0kn
+fMb@#%~&;t%9|_vZoMyEj`tGg)xD#zqA3ey_4skkcIF+gP?HRQ#mOkIMX@%=gNCr_8s?yiw-<r1Nj%l
+Qe1Yys5!ao}Ed#%kbNFIAb-i3m-CVAAd?~gp`G$hpKzf-WSoWYw!Gz30DlSdQYz&opZd+IKJKkgfv&c
+qDt@!I0YKxLv{21^9_MFaQjr-?@mv?!75v)^D!@uDoK5VJbqyu+${g;2Ju!wxDg|<KOv7!$Ir_AqRg+
+`3HS?8O9KQH000080P~d=M=nUVY$XE#0Qm&~03QGV0B~t=FJE?LZe(wAFK}{iXL4n8b1!3Wb#8QJaxQ
+RrtyIm9+cprs>nR4=gY7_TBsm2^Qj{yxHUU|3MS2%OQ5af_vWdu|KvKKeW7{Mx&_hw6ZQd_W(xGIpvu
+M#n5FI=cXTF~qlRSg-$LFxog{rqNpmpW>bH5S<XAtS;&ZzCq!EiHzXHT9!gPGE1t1VngOyqBrg_k$|>
+$lr|uB!1y@4pGo5WJLTudG$N2B)F5GDMN)E`&Wwl^FB7fI?Yk)OG75xE+#NUAo)cNGP?zf;;4CTGMDN
+;a0gF;Hr<hbx=wPh}cOZukT=M^4iHFgvRI_RmcMQa)&<>@@st~k!W+@O|6~U$j(5g+MKK&Q%xgt1FD9
++sytFjJMP*Na|%42iYt;)U>ufNddVhq0*?sC??*tANmoQxVxDF&VLXZnTW}z842a4|A{gaJb;ZOSGCC
+s}B2ok)-HAGh;?;yDvku1=%b20a2@mNMDHb$~=D0vESj@!hPJGHlLOCBJ1tclZOPUDab5HY`uL}w>BN
+s7+X__GuNnQhA(uk2bMAI3KM2HN>H$+5f!s&M_WWySmkOi4}8(9ZEeEP2DMDP?<GwhA8V&Qw3X6XXrl
+zZE-;uIx_ARdhKL&H2o97TJXcTEy+C=x79d_4+_jCdCboyBZMlZbj~+SQ9R!@?Cm074)c<6bjeiGE0F
+M~W;7?K!)_`&poq&I8SG2n*8jP9Ode;~*%F-otubwyrgDy#}>!v~keZ3Qvt(SIS9~SN2diKIU0nsv2`
+4EREh8Y3-q4Co9}>_E>jiuxO#)syYaQO_f`V2d~B5Y=);)cuzNa5dc1W<jc1bUcq3{E$ndDuQ#o+ct&
+1<Fs%%_^+J}=Hbw5_(8{Vj!2RPt$G2W$h^Mg2j#XXAx1-TRCxyPPA9y)7bRhdW(njJ1QsXW3nLKy-=F
+R^}H@n<Qi>ku`3_=)u`uoQ|{M3h^`|z$0@B8qf55M%`*TLuuyQuHgD_QqV^edzL5{1dVs)xtYb9fr!w
+c-xdhi2gMk!v*i&l#PlzgIt;#<Ns%*p2Al)A;o9x30a9fA-;zKK#{(-?8U^08mQ<1QY-O00;o{l@>=F
+nWIMt1ONa|3jhEg0001RX>c!Jc4cm4Z*nhia&KpHWpi^cXk~10WpZ;aaCyB}(Qe{66n)QET(u8CH$t6
+C`!ZT)*98O7hNKgRR-+vWh2Vf^LV|3kw12<XCO|qvyHvZ{JV0XKbI!fjK29#7Uv8eISgtg*(+=ERf4G
+5>DCM$LaL;if`7RXv_PD$KW4TU5-c8H(C+E_+ggY+RLMc%epi8I}cOgYS7uMxMWcZ&H3s?xHrI=TmgI
+*z-Dl@%JB!{e&81Rfdn@Vm<#bGP-3UJi?mz9Q$b3nw3OFn;wrA!LV7cOjM`6w2AfqaR^9}da9eB?-!9
+;zuyEmCe1u&SG5s{5(Uh9?q40ZE=4q~NM+%ZkSoSU8LyNJPP7a2JL5-hd9^f-ro(01^yr5Shl~FoJ={
+`aba{42bUoq9PK+9%V@N;KgHz=!is!2oZ#IN9v&OPX}HwvN&&Y=X)q}#6vhliV2PSV;ms2p6|t9toYE
+21IkzzDIf@e-qRokHa0YkylyG@9=Y`?48sVSNbm*N9qoI>cQNUR_G1?r_IHTrhXJFXr^tpeFd!2$GHF
+B>T0feq#w2DTszz8FoBFY-VHkxI@Iz+OhAE>cL1JRS*gs5|xrn2fUSzT+fk~(zdtqSWQ8<o>$s(X5-y
+6}OPYpD*@o^Yo;FQ$>7f9qWqZv-)U6VpfiYx)$aoWb4USK9R2O41$CdBd%UoJ$~aWYx1VK&PuT}eKhf
+mm-!sd1-?LgQ$WYTP-mMOB4J%LSk1LUWnq%5j`A?w47A8ZngaLF?<&hxuQ>-~5`l95xOgPRy)Yh)VNB
+DR_{`GZ9asXqWSEJk_0#4&b%IJwHn;sn8)k7@`n&?8Jg+n3X7mp3T~d=NTH1E0rvHFDMH%<<-@9blg(
+aMCMWzI--4)a=`#Q-2=}7bA(_(ceWJ^G-(g6591=+%N^+t@%0P>>N}^2vmu$tc~_`J>*sdoCB;6ZQTo
+dObJ;~|H#+VDFBZL4%LPN-JBW0gpJB};&%+&a&hkpF+MVMfY)w1s6=*j5j7sNrGt66X)$H?&kQ75yH~
+&_tI_+J|sjBAi3#9KT=dh~l<f+N+Le7h}>YUwy40jKX*uC@*_1m-g?bY2C({Rk|CA~0DUi^>tsm0DP(
+0^)_v)gx;F%FGLb9^c0#aSzOO0D%(+9U1H))`{kUH|H)=c3^6leF{uj8FN%Q2uLIzhh6I>d0HF?>OJi
+bb)<EDX}b)eE)jdJ-NWVkr%{u#IxE@^CzJX_F-b_75apGE-o%2zQj*CE)^tug%xhXoXg#ZSc>0Ks_qu
+xch*i1fv?)^%J}0Ae>JlC*<L?jk5HRhd#CQ!=Nk^1H+h|JV{w0&*&p7>QLVARGFdnGWZi1JAC8KqgYD
+~%^EXgS0|XQR000O8^OY7yiZk0@{sI61Jp}*&9{>OVaA|NaUv_0~WN&gWaB^>Fa%FRKFLQ8dZf<3AE^
+v8`Q_YUsHW0q+DF#^-UKd)o&8Y~Qpj?@@3CNNo(z^(fz|dNhO;i>IQrgV|eTJY-4tbFz1)Ah#@+28jw
+s&)j4jgf2zMrAQ6FB?%4A#0(_2v<DZF%;)%LKs*M7p^(YO`(ObUlQJzy9_Rrb?TQws0Y_kpHDD{BhmC
+{<+!Zsv52J?n!Wh;Dt0hWv$XRv>G}qLlkLlL)f8Ii7~GWD3ooDy6Rd9?H0+bF58>jNGP?zhFj!mI@4$
+?;YPJvz^se9YoU}95V4g;UfsgR<aH~H5E`SeRUr%H%UfI|<X8GyBGLLdO|4tCmfnC@ZSJfdN=+kk1FD
+9+svJ^DJMv?R83mqB#3jinFb<0>y<lTHh6jY>{sE9=?3>6^%+d_Tj7KqHa}Feq0Z|!A1fv|OE}57?My
+Di0M2aA!yQ5B`csXXt)Z^HE5i=CI!$UejiaE`q879a%i<x-l#V1T8l=Bf%K#~HzposuJb2RsSol}SzI
+gcq!(hQkM@(lQbMvTNEn$Bn>LS#6+AtFi>PM<E34SQfr=49$@WFETTx~XPF@Dx=uoQ*GI;bxd*=^Wye
+JKM126eWlt4vgoAhIxoMiuN++nk3FpBv_ibeiRlNaV`=%joFkY5p~ei_lq>c#wFhaLLeFAPBUGK{*Y2
+niYy83IsL)Avp^@_1I_Rd=EU<(zW)%TASjLA!D>}@ZD-_a1!~u5(?VA(yft!FsaBf2viru-q0Z`3)mY
+o7x3a?j%pR(j21|4GO4UIStgGBw;Ek$lX-@A-;gMwcC;)tR(%O6@;V}#bKGAG*D=qF%2WNv21}|Q?^>
+4TQG8p=nEnbJ!x-%9p&tnj#lY!3{vV^WF@>ZT&S(SSQ_bBXhsgbG+`3io5O4hz-)XNQzoj!Tv{NArWS
+1*UhsTN8e2HVHI($V_!!Pj^HImch$eeA=BKD-~iJkrdyu77~%-yZp<5C8Px?LD4Y)j#0p_5U9E`cof1
+_u<QTyHCja4Nyx11QY-O00;o{l@>=vjs?i30{{R;2LJ#d0001RX>c!Jc4cm4Z*nhkWpQ<7b98erUtei
+%X>?y-E^v8uRNHRjHV}R1R}7Q~ORY>_iXw&31lR^{H`p{A#O||}mc}-_lBkkY9Jknh`wl5eb{4H6nB)
+v+=A60k3Z~ypp-`o4_8D|`H~q;;7`p<1Ja@}1&lB4F5BmQc@|jymJ%gBb@f6~j|EAz>lQvJhkMNr@7D
+W-$_*aL<8hF3iJV7P5TIdt|_rHFOJr9?q0nxlMN#Urawb+1c;OS%!s!3s|G#t8`u5OX&7!JP(BMbO#w
+PBqRd(0RkD7Uw_AnR6X3j>@%tl?&q@jcpja<H~F*=%+^9(n8IaHaQ?WVGPM3fq~SQix8toX-c{qzC5V
+L#x$Zi@GLYP>E*W5uS-duX&*wVq%@yMt8^<=NyR3)5rDZ47nAjN{NyXma?$%XFM%#CNHAua4|`Nj>~X
+dUZA_=(<&1P5g)f~r6)6U3UW~jD{exR(L<|cV`I%NzFIH*hxvLE#C{V|*6S!6<RoxVND%d4qV!U+7!`
+}Cmw!p$IQ+uc=76B=0d-&T8Yzh-rBEZ7u61?85~g4f;nPS?wDJ>&U%RS;QWafI`4m>FhPmWh_&Lz-OE
+?|Pi2f~SEGtxd(sF;W8N@|GMO^#{+Y@}irlIigjMDziSyssc8-p3VYehjYmwcXjAP@I<tH<@qw_vRZX
+^B?I%J9DJS)N;IE6j5$$Yh8<A@K`pLoj>Al3(+=lWsd%m0mV~oiFFt)KhsbI>N5|n{R+Qi-4@xp3yng
+$gNl24ac74H=bx_-_cyvtp%$98?mhrI$hD+{V*}Ge!L%wkb>L+wT_W=g;{;dd?9yQ)xZhBXKHZ6gQ@R
+vEw{tqwI|gQUQ>CS@x*2GCl)rRo>O@+jT45i1rnfpZ@bc3={{B~jA5*gIC$<Xr9H@Mn#sxxn(&;Yv@x
+xsWTo(=v8BE$gDRL&FMabsq(F~B3I`Nr>NwNB6}C9g($Suxpe1*zbuBkUH)vdG$N1r#!n)rK*Us<9c-
+0?*z9)a_DE8z7=GCB1VIyl)4$mLY4^AtISwCGHrG6L9=^0Cs71pGFw0tI0xKq>tg>&HrEOAY+MoYAIq
+N^-aYQJ~S)<HC7MPo^gAzqX$A$nA!aHqPajNmAA<KJEuzy@RAAOKh4v8LX<{{GxALxf=SNMroI_xr!K
+FDfej<;^%a;o+QQ!^7i<0M^pR2CH34MJ<#rFZAbL8hlW>A81Qn^Slu?%~irhSq`Bt!zKy)3Q$V}1QY-
+O00;o{l@>=)>}^bU5dZ+HK>z?C0001RX>c!Jc4cm4Z*nhkWpQ<7b98erVPs)&bY*gLE^v9}TWxdO#u5
+I`UvZT_5cE)plYHrLOwY)&(rBtkt|X^3bv)3KIEsis0L6=FCX@d5?(V(d4mi+Mnx@m1GjT*<Z*OmJ_t
+}>_9<!4dCv2I=N%k;fO}#q#BR(08j*gB-rTBXzs=5k!%!^u-)#xAH&uDbFPAay^<0cirC#!jqRjghMR
++l`hioC3u9uALr&2AJ|)>Hx><t!GfBoiZ^#cTtkkpptd#IjEEtO`k%QL-UkRla-_^=Pyz^9>7OUXfNz
+y}J=GUsehAvf#@{{vfKbDbqArd>s~fo^E&s&q_Vy9X$F@KAN(k%s=fS92~)rAugqwMYH(!?(W^^W^U>
+v)%17CM&u1>rP6=i@@BbrD9l-xyLOrIMUgM_O#wPpZ(vNp%Sueyf`fAF5_VM)A{(Qq;O+T0r|&QCqO;
+TJ&HJmjm*>&D)4Ok{>_OC#$fP+)MXUvnVe6(N=J^z*C`1-V)p9L1B1%?KCPXY^$%r@g+Gul%!og0L`~
+ja&lvp5Rv#cA?sg~^wB)<SvW>r!rPvUK|tcO%1J}pH-Hev=#vr&3IFU!0<d@yb#4Z*F6%(ufCe8)f0-
+aK>IJ2bm?;`ZyaSk(D4PoFvL1~&KC>FW0OvXxkWdCtVrU+iJaJbz4DI}o1$K$dl+9!GT^{m3<KpJQ-l
+5QCUbJGL;j7dtib`|X-O?LJIdyRF)A(TMfj5qMj&l=cn=JsN5EdUt(&`R4Na?9Xh@zTqk0gZ<|A;&12
+h;>(xj>CO4w&BggGJbXDCjg~2|D)t8a;Zy=>kS~6O6-{O&G@#-gz%0IJcLr+}DM2Pufy=QdO0uMmqM#
+D#N{z-()fB+gnqbO^A~XXgEfes_xXYmA2~2*K*B6^21q8@y5z8$#!<K1YiD!&ct48nBtF{Dmm%eC{v_
+64ofz*2CYz0=SEl<h;JNcBvU|Jk-DHA16PnM`Ph&7^I@ui?KCN2STR%BWbn<%0!B{ndKP{a?v+~OP+T
+M#PCH2`K(;OlKhJ5k6eT*@_WlQboMP1W!;-LVBk&{A+v2J%H#GoqZ`-%63#EA_|gd&Q&40G*MP7%lTB
+z-qPX8H579VHorxE1b?4sxE6b3mBNMw5qI#OGD)9IRJau<F?_Ss^ljjIl-4?(`+n`)?~<};!mh*S`Q9
+)_<?4SLxceiynKkIdd&m~Wbtn;d7U?D%y?akO;LwvS`Y+l)HN@P6o=*+Xc)v<@gxNWzrDI;DL_k8R8y
+T)Xm0ibFe|ZCQ%eAWF`0Nm&F4&KNo>0orC>3yIa~9}aN9rtWl4>ssDetAFfe3qcIY3xNeP-1i#P{Kv%
+F?9;7eJ3a)s<-#ddkaFyOKWu(&4WC7ElPr!0SxR)G)VM6QHP#qip$6Hw(=sF}E4#?;Cs&@RRmyT2#+y
+Vo{{Dk<O(!;8{7Nc(C)*uJ$aQqb;<{dFxsAS0D+YkK>Ll6=ug|A^!J3PQ?kHd2$me*Ibts`YQZsQ^Ju
+@~T`0u&gEEHi1r0LmPA~qnv+j53L~SY_d8Suj{&)eeuP3%J@X168%4lzz)kQieQotV~Y`xj$vi1<N^P
+Rx}MoSR1P-wL?=Z-sv3VB!1!s;>*w9T<6&)2-KV$RblwSAL(E9Zc`q7Tksw*weA#@?I;>|7Yg}CD43M
+EYcsYgr<nf%M?8J<L;y@h6A*4hV;K<(jmJtn;dj}(a{>6PE3kO|V(ECo1wM=U$$Qo`v!vK!mN4^ni>o
+m3c>Z6p7Hn%pZBLYcgl}kZ){;4y^RJ!iG<u|b{6N-VVhOpMpKtRt;nXwp>{2)NDp~?aOP<AYzUFDgWO
+b8k90bq?cZIdh1-sgx=xgx+G;5SQ>V<<ebaSh0w#ZbvY{N*uLL2T@7?wBH)GY^FAC5KwpVx^ua<FKku
+DtS3e`DPJwwrVoc=2W3^QlSJzvV2ShU!*GX?z6H%^E7X?&Zg{%rws*ST6&m3sH}oX*E{;kr*T~y?HP#
+Qa;F~q+>>(CN19UCTCLsm&OK$$v>n6jLC}=FctOk`x4e9)EHwa%U%&(jPhmW6mQIkYRzcWmL$*I4wB@
+l5V;H|Nm4loPVl$Nsu<sbuVh{+UXCl*)7N$NGkR``5bp^YDOaiv4Sx~%HX{907IiUq9?A6|2_WIruSc
+T?)JtMT3{T2zK#ZF}mZwRGG7a8Pjr4Vjr2FVtGf(*%Z6+88in>%CYSWB`;vD@Y)WQpZNgZVmjpkkCLF
+&j=o;Z8HN)s|scE>4R8V%RNmkcu{sd>5N8j<fgx{a`FV0LFa?ZlliJw%q{sg@#vipU2D(^wN&I(!YqR
+B|!Q7u53it!&?7!a4Z6~>6`_h(=a0NdFG9b7se%#P_2vfkGuDfJb}d!kN*KbrFBThqCaKDL0aI0dgyz
+DrP0s3gA621sl0)v?YYBDN&)Qb*}dKZNgnE2%8vo47tP!#Bwwk{BUrCRuO0^UZPJNdU9KPK+eb`voX&
+30vu$a5{>OUGx$~`0-1^l(EbNWYE_X<b^hd=$UB`P`ds0R-s3ogqBg#600y0_ctk6mgT*wCK`ILxU-~
+685fK}e!UefDil`JvSc!uv;rEbRu|5Gtix}GN_%3d6UY4T-4J)3P(uVr})i|@H0u#miz7%<@d&1H{F>
+bEVIp+eWqjb$y{M3ThJq}AM$9^T0rI~#&7E_wKaTS6&7nC4rE7LyRt#j>t&akA#E#uM5YCF#g9L9!jN
+Oz=5O+TEAO>@)?gWP>MFA(kKzr~$_?7{gJop)&$RQL9a4Uj!q|tY`Grtde}x<$_~1)!y;`laXigll{$
+e-J}kV&WzEo5>Oon!%b?;TWD|2PMz&pG+2U!N0W|f!S+I$6D$Z}aeQ`ugLU#>-e26Dza4vsd4%+oZYW
+vzRK^4Rudc7o_w#l)I*|Y}^Jy%puuH_Vr3jG5KBk&@cEt#^N>ZT~@!{o1KOuxOhwe+4uD{;FDOx1N26
+G(g13v|ZpdI|DDqQ$`gS1#LXzbBv92W5CN3ZWSd=T=PKyHDxBvjb>+=NAgrBem|TR^jinLsZ=M{|2u3
+V0)8)#GHZIuAe>I`5Fp`C6tOMg{#Ds=`HOb0RC1>PIz0G&iSDcIV|74`|W_hlCm=L6b(KMF<vSgdVDz
+n<|pJMtyGAAS(7*OTjA}xQyi15SUdrHt_Y7jpfU+YHrwqe8(RJ)7cLmRGcL3d8lWaHc|4Nod%t06*0{
+F^Dh%;+FMaGefH~_MZc0O@t+b0U703*J|*8$^R+5G??{vCwWd~29fYxf1Pm^u%w(~vMb&{3!v`p2%NP
+F&V}W}TjOo6^cwf8F&%jpOX$+1jwoD&il3QAheye!!9cZ>HQ_pRHvFG_yA`4Dqy{yVN^2!Bp_H3B)Tq
+$+`wUx@q47-;qYSI=DapT)@3J*#7m_uS?n2gy|ve}IkYi74*!+gjCcx_U6F!24JS)xhoKzvm_;qu^5Z
+y83XTtQBW0Z%9F)SUjDRP_#m5H>^cUAe>Fi5em?5)YtIJxWGD(CHC#^B3sC>J`c|%)AX@Qv-c*B(M@c
+$o%M9$HehGS&#P1HDc($4HBrrp0daBq(9c`Y4*z#Y7ltJJyf%)>=dnK)NQc`|LybeV|I;(BZW36QG$^
+c2!36GQB5&SY^%(bOhSbuMD3ywUfH{inql#~u8D-7Ib#Qtc$-w{4HeoE&vx<{8jMbLfLpF-O<9VpPIv
+0e^v4EpgSb|vA!cq#$zHX3i_#CAP<oBCzvNT^2wL4CW!toNlW@tCNoBF_BCi*8VXT(<06L)Y`wf58XY
+!DC!@7;%7zP&<<DodM@O%+rWFc@2#8B|yb(uF0Yk)2~{#Ry9?3y$se_wNKX&QE5H|VG#yT;66o8VQfw
+t^#sLg}dU$)Q@r157F=r5IEYx-F|;oBC-4G~9j%Bvb>Qo&U8eA4bg64lYW6rGB!+AJE-^&3z6KmFrtB
+t?dMTPodym_#6aE+8M?F4|AsK=a~mCoZE4Z0DG{@Bqc<Q78N<!Nom@pt$*=2HtAZ&zh1oX^;|K*LG;j
+RrUZ3;L#6fjkew3JJbe|ulrVy~84M?|;5BaZ!n*gcXs{+^saVxyuXt^tOkzcb2CnXmYP&Vu?8`B~(rE
+v0+zVU;Hr?!>Coj>BVPxEI3!97<)g#Y8vpARxI2-PgF*dhpYqj}@azxk3ane?7S_S?Oj8P}I2pX1AXu
+Ou)f4Rl5n#W2u=X3_T;Sb3YSwW>F+uR({UhMEt$qHr=6_+(82NVtD{T&qrpiY&yS$KvmZO-Zj9D&S7%
+a*L#0DFCPb$)i&#uthb|HZawa9P#idbcF%wQE!vzAtC(*xt3ui0Ry2{n@iczs<D;u!ZWVvy5**WoVMb
+X5PP+tTRie`Kd;li%vM$1zt7dhcTUfoSXv7yi9(g4(1pS96DD*10zkZ1@)|tmZqN(Dv3PzH7$S#Nd(`
+>OEOb^myK#R-Pl)0ci09~&xb3!TbQ9@T>w%&7^Z#trrVtP9kK?wPN&Q=*UxhGRyCO<d#a*6jFgHTPT%
+HRC=Sr8a<=73ZHib~@N0l{HkvL4E(B0gtxMifO!4wGXR9>lus3*5g9Z1ddek?S@miC^OTA&Q(5AlN@e
+{T|;&5GW(qnx+@%4Q)0YoaCgYkM-h;~IZEzU}0-G%F82)_nZPrh@mPz@YG*u`!p!}>`fujIzQvxx=%c
+BH`*l7k9peKd-RofSSXP!xr{^J#6hiy%(nqARjam&3}%+7hI5+HN12$-^>dlyhj-A{1qBG~O<HdgYEg
+m7TvSQcIfUy2&1?KE*I%WMAYl9idQP6ZU+3CJp}NQe+SHdW<1V)0)=ZK*P;gCK*nr;Z~PBf0rGUDQy`
+<QyhA9+Op@)4`Tu~eD1KB<3DayEq@fizQ^kr=En)1rvI@FNE^OW2Q{3@GYItL4X^1Uw?GDAZ!A0p@q(
+j^?1`sIY()O~<u4(7N4H~H;0QWlM?;u@q(_q`6$D_wPK9z$!jgIqheeKOj>_xJ6=MuubSvd3luOc^Hk
+tG|Nfoq83t)-+YL3rrl7n0Nn`KKq1JQ?R(r`UzX%IiueHnD7ybxLc{2o8@fO)RXSbp`#Ti`QC$Mn8LU
+zyvE<0sR%0y=KvCx*b4U@oabua^dl2T=q!FIwl-UfQA*U>|X$;?_P=+3P-pQeW=vUXk@v>+E63nLDSK
+c)kGXloyHd<3FDR<5=^G&Mzdas@s^@K^l9G4Dq9&oD8O=9`$I`d>h%ck;eU?v$RQntQY2NreV?4YMsF
+$7U}y8TBz?+JtjrOmxzGoY=)9KkM#J#q(z#nbu+YM%l<o}L(7$e0*C3q<YpL{gpOr<<V<YY&?65Jh|=
+G+_!Ojo7s+Ju;j7t4d4|=V`}D!k&t@_Hq5L5mA4;D5CVac3FOgQ!``s4|*$e_(F56R1FS*23``4@T4m
+|d#pi1_GER7ttO7t{})59cfW;NH+?%{Vh&f&+0bzb7WN@VijH>_i96K@4uL$Z&}2SAlY3ZB96bQrR_R
+PYS)`EEaKfEVPelpo5#TjjF*u*Hf$)7Q_Dlf3W4%ugNt{LGHaLx6$3#;N*C_Cl@{@-$5`U)+i`o$3;^
+^UEFG4{>hr+8>FkfD95<wWOB$s*Ejge!i-Ijulc0HlZZ0?A#EPy6gPp6Ehc+c+dN}v6{{Km98g6bhBN
+3DBb-%sr)hM(A6K8HW2&7+Bc{3JK4e6+U0zHrUA_zpf~G>$sVmqzAZ^0Jo{{ZDLl1)Up@K!9!%Tc7!N
+8z6L{BRlZ&*+z56@pJ(@Oz2h8JhFX@Wi?hU0}o!dsH4wP2`{s~Y^0|XQR000O8^OY7y%AV%vI|Tp$Cl
+vqy8vp<RaA|NaUv_0~WN&gWa%FLKWpi|MFJW+LE^vA6S5a@<Mht$}uOQk7+kqo@c^ZVqfRlJjwjc>=2
+YXnD>MYUr&5};LJH_>e?YECSogB+answ-awWuE)**<<B$&W`eFX8au5E7NjVi7@Q=ZD|&H$m{?#fw1W
+ZxtGALQ%@#ubo#AOqbF?uF@()ka?M5j)gT~mk4jX6^w2!!XSteRc^FgENukcqz6Z@zBz)~27bh%K)a;
+l(*7O>Q6>{C3`Q_2MY6=BJ{Ux}yf__SO~x=+n&>n_idM+XgtdpkT&o;Hc0p-N7RE?bm>>vJoP+njn-h
+;p`%vku4+jUUwa^RGivpk*p`4P1)m1_+iy|eEBBdvYl1vyA#XcT*?H`6Qk+W%33N3P|(IfxYdCF-haJ
+>%^gPQ${i4Awn$*Bl#-&}sQp?f!!R8?9ET4X&qlZkuO8z8XR#v)0gV1^u&1z9)(f}{f!GHJeL-Dhl3r
+NW9Qi25~6p9$wO!^2Fj(7VgroXv)Krc!cj{yg8WMinhS{=#|juOXKxQq)hUYYykr=?$FM$F?+UNhSI$
+c%sxw;zMvw%BlP92ch?q^3R|-ryue<jyr|Ou@l{A|F9-9aZF~KPW;gv>2~;#`_Omi>16NEIPTT=tIDh
+-Rts3v9RaE`QzC6SBr3aX`7^3G4myb(8llhV=|mWr49?_&0veoF#?m!)3Fgx+JS%flm2E+ut2Jbb_z7
+?=q}^7jL4AjMn?kM#hsCcM&bdJ><X!D~9|T|HIYnSyc?#&piK4E<BGjyX>JcJsK5e01S41=r>76JNOv
+9zkvzPvi8OHB=b+|qZyD9NL3P;Cm?no<fS*F(SG^SFsI<0_Dl*&*PAxzK}$W&L<SLbkZJ(&(}Zl{B@@
+#T0rhQ^u_!e|^Q^<jsM>8?Ky$Y#XQ>K<CXf2?GEW#ytQ8J&(<m8guJVga~O`Rm3Ao0gO$jht^<kdiBu
+qlx3bvzkmUAwjCKwahZ^OwLG>_b`If(Yuq|tFy~7H8(1%L=+T=aSe>v?qrJXTBII^B7aEu9a(J|HJXw
+-Jkl*KEVsdiS>`$AM_9}@kg9dTrM^eNp6<g>T;uH5E^;NB43bPTvI~sw6?DTA^IixSbC_}=^y%*LuXO
+J^E8KK?WG=Pm(r7D94e!1Yc#mH<Ps3&ccmvirg6({6j@5JN)`$g0QQ*sYI2<<fu`$^Bg}#F0d!%M$cD
+^4^+1I7DWi%M@pP4K|r5A$*+D^|SY#oH3k6yj{W!Q|=n%j?K!fyt1IH@1HHNaMy?TN5GvyXR1_i=lCJ
+AjvPvH^Tw(()iJ$(UryaM{+q!XfryP1oU0$rR{{x!zz@vO=2ns>A};OPMS^7#AF=yI3H13yMuuI>DcW
+F5FEk$TFuo5{3sJ?+O_;cW@I=(EO(bWy2&Z4@U-yJkciHO-IO1;x+>;{WcMPaiJ7<U9|O#KfY20KDN0
+`{Hj7?H4?UWlh0wfczf{H2NuD#X9N0JH7h0c(>V6w(M+>qh31proL1r1?t}&{sUc`Ab~}3wmF@<2wLK
+C2-QgSJw@oi#{b#p&YoY7xn(h;uX`}D;&Gh2>YVwIS{_+y<)7jZy^?kN^2YfEo=i|}Y*F3vCyD9p3qB
+)aKWw}(wcQ0PgeDC4~{@FIh+s-G>{TJ|`%m1)^_RE1KZ_2@m&Tn3HetV=ys)sf5fx@``-0R|V5V3kp!
+w13%n=C&@y3zFX{Hu8Xxp*u~Bj1PgOEE)>&rQMYznfq99NbT1ot=l1?Qc*^0|XQR000O8^OY7yDP{LP
+eh2^niW&d_8~^|SaA|NaUv_0~WN&gWa%FLKWpi|MFJX0bXfAMh%~)-38#faEu3teYFm^YQHnEW!fpJF
+R+U~_@;{>(~<d6mnN=r$sSG!x1Yspvie*4V~$$ga^_ru|;56PMw4(Iim8NOiCms2(`QkgGLSY;N|cep
+YdjmP7W62DhM8y)k?tVaK7|3;&`mDFrqq*W&1d~UeRH8U&0kXYnK&bg6A&Q?56GodtF6pGz=j>LZ?*J
+Ys$E42So!hadLuD@4YuQE9sjTWj{vl-Xo_$_mLv*WiS2l<8DjpxO><i>ATDl56tV#2ghHAQhRh4v(3Y
+ZRZG#4RVLNPgp<wlXrSDTz~(N+4bdo(eT#K%9e~4<Z5nU_*CI8*VC{pj`E7`eQUY|9E|Od3~4M{p-_Z
+^5e&w56Le#KVM#-f4sQ7V5cnLWtlnKz1~hYo6Qsqx1NF`R?jr(viR`J&v#eJr?Z<oJiV?mBSB8{nogz
+M@L)6=rDDMnFr1tx=pGa{th7)$UxR5%uJxu+X>>AT@B`P}2vaHWCucw4A$A6bnAI>Q&mkRhf;GI>QU&
+bQz#t)!;;;5p7zPLW<Uj}PfbEhZiXB~$0jK;hfE8p`h7mD~?DdLd^iqJ5^9(qUs349Ai`j2tZW^6%ky
+#qEf(YSRMu*WqnOH-}lF>B-$AbI}Jz48Y22AE~CP_jqvc-fc$C_l}5}02X=F0OD>7`PI3Ij6T3StYgr
+P2_vAOx`CvrNQ6REz!o?(P#=5$Qv(L?iu~F?_xfmayfOR;HPP@^1;k=RVVzwE{V<(L?6a)OzfV1N<RL
+>?y$0{<BNq4+4Sze4g22n@9Q!nW<3n-=s?L6*%?dwIj-;dvVmTv{9ithW(_9?IRhsW9EI<CGs8C<<GJ
+0bCjhErvFowFXR$Ld{!`7(!X~!bqGL7ljKD{M;<}Y-K2Fu-YPe+`=+}&U-1l9|M;_aW1(U7y2yKy<kd
+QP`{s~*iWG-URL?4zrBDmdbgmjW3#<jWHJxDT0o7Yx;@KK_)N*_YXeszwlX#U{B(&5b`MoIH1o2XsFo
+4CN2?w4nA*#%3JtVBRTI5B^!M8(NyB<W126EBj4aFbr)%X^FN&D{L!^L-vZ$ze_BCfyVsw}~jjeIXy#
+@DlyLow32g0QhRcdZAkD&Z=uNEyI72CV@rW#U0(ZQ0xRIq4X*B*W$D?d9!l&=nS4zD~bOphJ2WZlms|
+yFhAafeZ(Sg<I-3c97w33;J&B8b>`>%O6d=P*%I%Cx*-6UVxCT)j#Z>Kr#{zczZcpwjZj0`~GZ)ih9N
+We=5S|htJi~3))2IPuM-#P+@w)(vyZh98<qB#(o30qHa((e0K)5lakP(6_uYqe};rLP4@ud_b5zb^xS
+|JeOM_IY9bNad}|saP&ABB0Q9ga(ag2q(E3Sqj?Ceh1T6mXoE;q>9<$9#&Y>$?6;+m!D9~Dnb!i@(+m
+O>~eQ8X8L(Gp~kJ|>Q_u)SE*wvnnyn=xF9=p3PWn+%BLri$GvTy8qwTrNBZwWg)s?XD5kD+*b%USQ~p
+2n|dhf!<qsJnO6^|Axk(|!r*syiqluMqWXpAJB5N6Bf(^Z5hM2wQ`LH}AHnS)abCHyG5u<hKZ-y;-DD
+Hllo*1ShF@j{k<=U{>h3QZTL+>+oI6cynKu!cL~@PNsf2IXwOvwGI9}VC}oK8$_@>u{uti7`@!ZdblT
+Rb!qeOrwhxb1p<yEgiI8*oa^|ZwAg}Cd2nBlcd!*}n9ZOzPJ$lp5yP28_m8~-C(V5W<Z}YJ0P(s_+-)
+S;;7s%l{k#m(6X9xM%Z}tU{7hwT;%~#<u<fG%`*`TE2um=|pc0xb8y}jm$<D7(or~|AsSu|!Ft|sGQ?
+TFI!G-&#tK2tWq5BHhuYD5*XcU;>Eb$X4<gAV8GVzw|#I9|Ay0#-p5eX-5yS~N#xwB8&%c7>JxzsCdM
+z)IV<zlT{v#`{?{MdUK_D;tb1Zd;=>4Elf*qE;upi#S>r7#KH@Zj<lxkpWtYy&M(B9#i}20s){*vpsq
+8?KhRl}7aHpoL-Grkg-eC<YUWXQS)+!zrm^sq0I2;q(4B-bo0%-xBVQ-wWLuiF~Xv;J}Q+2PNx`DVlU
+bB}WK|_vXRGiCd2x09;tMhv4-B2Cj|FZ6+-w<SLL$8p~i6Cp18&VnN%pB7MYRZL<<e^tb>ZZJ=?6U65
+8dey*+PWqs_IkzcHZxDUOYO=k2B34;PR#t%M0XBz^nRh=~rwQ!`Kg6))n?^~@0Ljk9P-pmnLvl0edHg
+Q&La7G~0cV+qqJ8I2bdm^Ay)A5H7h9H`t$3rl8sNAKPCX0+O^(ky#{qy?c&E@&o?PWB)!A+KfC&CbT$
+GVz@0kE>-Pi#=19=2<lb=WyX6wWyKpb91-1(nDICN2670AeX~xCsby2zH2w9tV7;$iP0Vs1yX?#=F}F
+LFM>poWCLL`a$6C?S}^6#7>F?FYfi_r4l81)`!r%C?9(?yB}FYJR1AONyB1dZynwf<3BL+`7>P(J$us
+L6E95UxLt$%p!O1{x+%p!I!!zN5=Ay>91~;4k$f#k=^cqzQbR63#vG<zpjHC{@4i#IwyU4R;6(Kb^$_
+oRC)BfE=U%X@1=|R~$Ok|<h?xhG`qHWIWl50)_YAOYUvvHBbKhQPsM_$VYjx9~Ra$R3%Ho(2S=Nd~v7
+L+3urt6cIjfAIC)z&rUgf0N9CHpZw)>a{eT>YPHj`2d{T_phML!j>4N~Gk7F9jW5gv2G^l2vVg?mw(A
+i!f6jE8z`y7=!<zvfG7vP*ysd<QXsW>`Z_(PMHwNcMdXUl?*rh)SUne6<eirYdmLTT?QlzkJ9S=^c-k
+{{>J>0|XQR000O8^OY7y(@<J|OnCwT(zFBs9{>OVaA|NaUv_0~WN&gWa%FLKWpi|MFJob2WpZ>baAj@
+my;+l^O138Y&aVjTemQOGDlrc-I}gl45=bECmut*o(u_d-`s+>%J2EqJXYQ&xr>pJWZIQ7f9B>>D^nL
+4FYx(m3`C=c++sppcU6brZJf+!-a=NxJpzYShRhHae?x&agA$z%J*Y@^u%BNrS(^xi5^s?*Xrc99k`L
+ZdK?0C!m_siq&FZkclzrSetOZ1YQ&d-lacAJ+zy1g7v_m{N1-LGZb-Lv$iFYgC%LU{wd<Yfc82u|Hg5
+*=So@jWWR|Bl&9bbmSA@8`vQ-oD=X@26`q2VKs=&E|hcz_r@#)@9eli)j3(YRfO;GtRE|wY%M4)aDdN
+&5Lycciz4pJYCPHYjiJ9$Cs`1{rUS-)4@Zx|NWvJlfQ$$_pej&U(@*}inC_%@;`pw@&ABB$gX7sP9t#
+){H9CPi&7qo?0UYIM=-D?S`g_XB^ETh$gu@VEU09Wq6-3F<k2E#7kIp&xJAY;sA!SKu)`&fecK1eELd
+#8a0?1ur0fDq7971`&_$9g2okoS+#<>02_(57umu5*W{aF$Fyw20I7NTkV?@F8aB6|E3pQQg*aAZrQ3
+U%X;{}Q@5@r!Ii-caJ5gaNQHpVPacEOR0j9TRJg25Jf4u7EO1<fo{YLU@!lqfi#M*ar>6J%90UPRflH
+ANG7Tb^1GUDHIb5=B1IMZQoQokvx1e^K{ZOh~3I2)x7VOJ@*AekBx!EAWBMH?FN)MXwZrgt(@;mV0jL
+%1&Y8+%r+RXW(1s7$=9-$TzPS-RxzZI@Vs0GH=x+jrB}@1JxAcfy7G}jXbVPo_(?RsA^Nml`hIEk2x|
+w=#hn4iY6OW(7I1|xxx~{EG&F@1g&z@lojZJF#F{pF}8NmZzXxuCH}d!`Q8I<`g>cYV^dotV7hthMKh
+4V$W^&txrj5$fi48o9mRvbd%NQ!ISP-YRi@GD`=npJUbK-1>h*DnrVRa+gCs|HOhv(t@c~QK<`EzDn<
+h71@FWLq<PWcNpxf$Kb0DWTB*s^VSQPd#&Y`-d=HLGnc+=r|QZ;x<6iRa~6sG7(w1~5cn9$4X5-D{eR
+eG|!t!z^r(Jgnj`SNTpg<EMhY)OW~na<mub!IZLUK&NttbM1?a%bM$y_LO3t|WFZ)r_Y){MjVjQ%^3<
+a=E5`esc`v&2bjhH>^4tR}B*{r73K)DB1O@;`Uf@Kz$NHN$*nQb$jpyt&rHglBCpKV3o~yFI!WSc#{{
+tdbz-33A{>N5w$;_{d;5Kcv1$DdD+Paf}{oZe5c0K!ITa*!CZQ>9|GOeN!b=x6l2ItZt4dVV;T-2b{6
+jXcpPA6K<Cnh#hwEm+{NYf$Ttp6&+dMi@7z-m&$bNj`99Y57`;^t-K@_&x<i_9_jpova6HUXUvuvRed
++9hE$<2LUgOJg+_hJcIL}`{P_iNY_;LCcVeLcI-!lkGkL>y)@&NkYnnVC?U2_L0@RB4D|HSNiFY_{a1
+w?oSpo-gzR6fcZX!#CZ|D_$jBkT8_e#X<U(Uh8Sk6c9fB1;z(v0woDC5wcG00w>q2t<>MB!&=+rC*{c
+PCy6)KnA`jY{3CI!xtpBAXx}=F$u8@^pWC=h=5-?1tFG=7i{tqGzHgy>rnuTplcigVHPa_@RJM#NHAJ
+FU$FTiA{QvOU?Ygk1Q>9-0FOf^kOv|bLN|bSl3rjLd^7;*BsiKbGVWJs3cv=S>G4_cy(aJl_-FHmP}G
+-0U8-D<g3q$4z|Zj@$s3)oC)5!Ry6DyNfG9R^zoJE9OG?@#gw-T136NFkLtEbK;IlGaFzBnPZ7(=6k#
+@R;_G<qQ$AjfWszRjd1j$`OxHkYxUHBBLV3bd~_=a_Zo}0M)1jpLQGY$~Ckhm0V0BSgBXp*NOM}Bxta
+DZu*9O^>y4$G#i9u6rO9l-D=IfhMA3X*BdEuxF&=MVXcw*o(bcLDglft?<$(Og=mz)#U|PgOCFzQK0@
+pqd{LfZsj*H{o{$?p5i-D}Fl^psFt??(@7M|H#CBo;TzlnYhpMhWsNF_j%rs-<`N3WP8aBR_jx!9Uh6
+nLsT_+2`K`U2TZc!PXJzxZSAOxHpjv%b>q@|chaM!yF6X4B7QQP`MN?aY#AM5r$|?Oby;v2^2};2$Q?
+Ddg-)?*k#;wHaK9UE$dpkr$XlJ5JvyTneMy;LmFqeV9{Z$VFfox=mgpm!S!Tuwe=^TU-<Ee-KX(qXyU
++c^2*^aLakuL`<B4V*i*IjhEG)G>b^?!3fWH;5R){?Emd>+q;!?Sng?Vo(Iu8!!KRmMxpScKRUYMWt$
+Q_v`8B~Nhd6K=C`|5TLFDkcEyEt+(qe#@J99Zf>y&be&(@429b9a%tx%YsomS?QI<C<O58DbjUX_QuI
+=ds4nZAXiLgk?L6)?pW_V!@!9!LL{6XlHmB_2lD{z0?<yI6LXFw8bGrF6o839i5n~H&2GmpX#n%J=VK
+<S?XOPC;$0ZnPhj(+Oli^>lo!<dxP(o<%iCH$uKCr;PM5{L2DYLp>@ll&>+Sj{=+Z8JPENtnFXCMi0D
+g(q45QiL%SM~6An%Ud_pWZ4pIt}!b6}-0QU*E$g@QZ7=~C78KeO&`kB2RL*4^+J@^9p0T|yD3GH<p82
+&i?0%N99$U+<jhfT!rI}T33p_LD42Lr-mGSlf44j$O<5is$S1qO5j{!4~gL54Ytzhs!P`oE1~4!Xd;1
+7+|BPzuk`!_KLP77pkPfq)3?b3xx1{i^s31ht?Xnm%dWd*r+KjsJ$QwSc~jwhM6Xj_3mEOLmRxQqk-7
+E8FPG3AAb8cWs3K47<j*?x`;l@;mW!77*QXdij+#G`S!A5E$~{kvcG{L!vr^T=C}X1&J*>Q(NJx9049
+N)rFgiyh#n`;~9b6h>r#qFM)DulOn+NVJ-3Phk8OM{6B|nQ$ckF6azU;MhY52UoR}R!6V<8WS3_1`G@
+3C-U&l6-{%D~5Aun6ra#wrlpQ9^RTFy+7VohU{<XkAK7AYQriHrA7KP*Z{hKE%Jiiqxlkbb579k{~Re
+(pQhu=3{hMyE6II$#C*D~U!vB?a5+*sj~84j9ZpZA=2zX&Im+04!!Nzc?OBz=ye!Q?ukoy|95q#c~Jj
+8Qc=k*>h^!oeD`)o#-~PyA6Cw$f&#6?en3wbkJVWgQn~zIhVWEIQGL;}*9XBY0}H@(6Sb>6ZHZQe*}v
+uM7$m)1{P%d@u8Sq}#e6_Y(XQTd2*O%Q?_0Jr7yv(<37vpEmH!g~_*jvG@9>qG=&SrS;|FW9FQ_=c`N
+=GQTB>;JG4<BmGoZ6YX+GLk{`4;_Yf~$IMfyR}#hCBkS=HhJ&25&ZS?=@v3JnF^@<3f;Al$r&cK}tFz
+{W5xLTlGvDt4tk&0RKJ;kQw5L&dN@R1V_F+e9&&168L&Mx1F>9qLsAlnFb1o9cEdKL1*=PHo@}(>DXn
+>gi{^LGNx+~y>4+_X$`0jo<UCaCPGqe077r(Q{_oshD7aU5kD2ZWT(gw{y(ZK<e9)(bglhDp*0WQaj7
+>B+SVAZD>WRWacq~w>h0q6m`TDV1&E`Uf$i3LW&A8>5Jkc$kchgb4Q7l2^EO`uzdhSWm*j5bgfG6VyZ
+2nxkW8bkI16vCp=!9!rMh0fp)3=P#2&@6zmfDp>CMGUAZ1zkg3gHGNjKp2oYxCNd-R~Ezmk~TC6(8j5
+Krw!Ri8Fc*N1MC3m5Vl|WNK74Q^*7*JTE2osUU<kP`L2t%ceJlR+C>2Jo`EGTWC#e_9~1x@d=~&R_l$
+gtdc$H8VDVG0WZaNsOm`s`%ipvips}~VV>H**2U&t2K8SWFTR`0*^_n*Di(zB)*LV0_$?kx>9e}V|rT
+z+H?q^H|zbhC(xJS}9X!ZrTAzz_e@8W}}CR)(ZMO&h|sN$BQmVtO+W_V#eWYQ|NWM}0%og+dfk^q{w#
+&#^rvm=M=afmP@7vkL}B*nQ|the(!Du~&5;00?p-#vQCz@)CZT}f-kBgv|ET}t;@bLL0aT#Q6L8D%Wg
+*MqgJy2qM}y8v~WA#HNU*i;VB(&aXrD?6eXm|8?$(QPXNs_ctGR`lh>_UW>>&bpfPVI)*1RjvI|NWXs
+v;46UqGw%R=1&}Y_0Tb=TS2~sC-jbZUA9UgLYWedfz+zR9M##-Yk8CCFsoMdn+s@!s{09DwmnJI%wgZ
+n9^vhS!t^}dzl@ssmx8Z5Ff9!8(-?zA;LNr0x=~QVRIkR7y)5%*w2xLXB+Gtl_(d*j9&^*wd;MU_ds8
+@4G=leGuVk*eH2vI+5kB}2kAU=5T7{g6*E4|ZIU{n+_|AWiRba-U)c`4O)on2Xuqp`7AQwrlLXnd^5m
+9MXC83>DLA0KCU-EK2y<y&n#ujQ1g4jvsK%%z%px5{?oRhzhZ){D#QZhtvyXe|g@C}#7q%W2JXuI@49
+3?3hEt+&xm>4<mAGCd&J<8%qjtdR1{F~P=5vQr(+b7g;iWtnGlx<u<`mT?q=&TayckiSb6E`aBKSsY*
+8_LE9zqTB6-c#DAm4N$hx_4)Fxc=!o-{0n+}CllD!4RrPL*LwIyp^zL)5**Hv1jS-3&EYHwI0z+Zios
+}>!Wa}MY2qtF!q7#YKym=GAO})_h7to12+$fp5CCwerhq|`Lhn7zd`U=vbpX@@cHkzb765-hLV((`c=
+AngP=w&|L>4jhWq~`;uUv)ygpe`@#z80=#w{`+K@vDNE?Oknf+OG$bPR__0l5S20ZxeFX(SGhf<_931
+P1gJxG<(5<I*TpSYTLW1{BgS;|@DWNRcDTOOd7o)Wr8SdatT=*6;N0`h&<9fynRu@s$CP&lI4Hy!%c8
+QUmPTwy`5G?KYyYid*nuw3FU=ciulKDOQycNuK-&qy-kV!#Q|D>$tfG{)+mJ7NAA_6=C6sF3D4z;gfn
+GUlqk4>fwF3UyT;|E5rS2w8(D__fNtJ$RF_pY2_<z#vZygZ|zapU}e*IR4&b3EJxfO_<n=hyAZdQJEm
+9#%coDQF9#VBqU((xI5MYby9-bJr8GF~@>EW(p$vp}9CwyJ9Nht}jt?8%^l8ejYdzX(>MW4v<qgp=u3
+y_x{S4ycEj$%ZY}*{NgnJzEXkJ>1&JDPdp}W&jscoR&3h$tt`#-`7$T!bbyu}kXcU>+YyNwv|_F&pX6
+f<-FWF53|TnodoE;;so^J!u(oscH((+clz7iFm@Tv_+ZuD9<&+b;vbCDpxOE(bJT&MA&Rcsh1gW`?|6
+^`RxTE8ln~2+wm~Wv)XeTAP+L*4-f1yPSXWJu{4t>hyF~ood%Pb)NHioIa0wjc?m7w*iQUJXZgAVe8A
+^UQ~SkAIblE!8ne6`L4Ua!HD)t^m_7`r7ztrdpRAO=luP|m)D4^k=^^#wf@&4^}l><|0M2D<M{^sX$E
+I0pgC|7CoqmCS(3sqiX<79MOnZBEQ`@7{T28_E1!VcgG?YC1Dl=!dV*Zg$pRp7f<YEwQ;45b0s{<Rfj
+<lIoPn|Ch=%bJ0KGts5GjOee2SC+K>_4UnoSyEieTs9K0qVnKLLN1fEN?V0>hy!0Ym^a62-wzf#d*u#
+9~m>fD_Oh`ms2`F~lUCA~=*JSx^~NFQ5_4Ch}rn;Dlu%&9U(>)d&xR&g~6=KM~H0e0LFOLS8n<8J(>R
+J@_8{6$t)CB2T^;<l9Je(kteh2fwGh&|jS$ygA0@M?9I;rPI50I-2-!KoUYPbO58Nx4K21UO3CuIrfx
+iwihJO0Se65bAZ2we!tNLd;YG;$rI7-Q@`ckFnW|v!Lu%OyQa3H@Z$(dcv1LBO5u?D)+r`dXC!OgT3f
+nfxECIAJ5ajCzFMPGfI0lsiQv4&m$#vB5#Zu0q#)!~G&#do`g00;pS17i4Ed{*_Whh8e|6HnpEKmIPT
+Kc#hWw>T`+^9O9|P->?%!q0cZV~&e%PIN)?14UTS?q1C#SGlY`Nq1il*6zINp)Sd5h|-mF%|j_Bh-7e
+xJ<bV7_7lc2-*Rjp~itZ7B0{q^o@AnTh4+OI&5JW?!?;wm=b#DDunZd^ai8tx(tZkBl;7FSW3JT$(X;
+balq?4wyWaNL=b(N8XZb2Q(=iE(rk<B3}m9%{L-c6l093<Vh9bQShF;-H@9a-GzkEw@F}n=Y5ShDCWf
+b?jgAtU*fin&6vz>0rAnXytIUaao2CFaXhWfmXbXS9XIZH90u(Y4+lPDN<^UpokV$U+cQTRwZ`@NIz-
+{-kyy60jfUY&$*Xu~4Z?Lxdv4NZri9OUywpejgssm3a$4bO6fU{n4u`yE{Y=0=r4&tZicgKl$KP%dWT
+CgC-zO*i^~*ofkAArHo3taA0FsHo8Nd!4gEBPDp(ur;<X5}^40K?)Q}_a-AY@aomOTbjeSk~=7|Z307
++bIm#Ao*F;08DzPYFnD3@Ifi-yBl-6vYN6JV5dM^$r-kRYbx_H*|&lEPS3#9Ra~YqR^5DS^|s{U^_7T
+6TmA|P(PiBAb;&Hf_xFvla!c-0!b}{qz0%h%^}T!4gmwgOsQXo&r=)lLW941sWt*X{U<2SJ?bg*OI~o
+l7X@bR464oh@1j7K)7z9|qZsE97|8K4xF@*$$#-<K1?)AKs4!7^P)9y|ccIJmw--At_W-~mnKqv(JxT
+!prhpb0^os9+C=eph_=J&cy(*?g*rqGVw<uh%6x{;G=11&VSjH+>B7rwdVn9<VeB&Dj-Qs(xc!Rh;ol
+=CR;D>eVRrAefGOVVH;kC$gv9fx+JqmR5lwS(~O^|vX9$)h(FxjRQNnI2oRYF%JI$P(D$54`b6V1-ra
+xPzPo7R=hJ;$m$i)_<&9L97U?%H}r8)ZNPYpUhL=y67dC%S*qx5QrZ%ae_+bscppXHF97UUvX6GnfQy
+WQLe;n>~A0I8r5C!xp!GZH#QaJR_KQt!}59cB`6<`+zOgCSKQsa>n*#tR3}sUbD~6=gQpK?^Y|axyMP
+W(zc~8tqS~v+>Xxn&=p~Mcki*`lFHdD>7-b1w~yyaFm?&Hw`J0yRi@93jkghkCzX=jfDdd@N4FK?;Iu
+g&Z$!FjdW(`v$J4zmqK9iOx1wHzYfe!wb>)TP)h|YSz7elNEHu)-$P|h*ScEH;6EnL$oijJDDQWi})i
+x{eO1=8fQt36QEyGt`sQBIF$+9Nj5>Qq)FTuEjE}*X`7p5l&R*~UBWSO}CJT>NqK;_ZipXT$C+yCgTb
+fC9V1RoL}JMNCT%;Bhp&za4od{nlwmeyFl8*WFjzdp=1a~SUGUryG36F1^|J0i~tJJ_kAim^WHX{ibT
+O|aMN<7yudCR3BAS@0;!fMICCbRS1me4yl*n43gVorWr8gkX)lF&y`4S;A4b($q6uDd${$`&6qrYcS6
+z6`(PGd5rl(J#w4FN()!lqRGXUdA2)*k7f66_URdDr;S?InLv-{VHkprSwC0VO~LSW|2QlUEqNJ~U}#
+d|SO(5@<~>{AiBd#bUqouvScESUI2Le0W?e{k^tcDf5%p3RXsU9qobAlbE1l%r{gH`2>F-Ueii=p6k)
+`hBXH9r0duc@!Pfa`!UfB5MT&`B@eRwAM-wmUDOFQ4t4NQOik&*uPV&OmMt>5q9=gc*o@l*PuS)64ln
+qo1W`ij0d5^4}2F_;We1W*Hc3KOdc6ox8DKujpig9U5=6Kv71eOlBaL!n;@@COE{g__jJBy>;`Dj1+c
+(sYtF?8HV4^myUmR@qPJ3nqHu@EUUReZlKdnAi*WD&}D70FW#&V#2{RvN(sqd%&0x2FDBp6r{BT=AzL
+#ba9c8vLLI1OTbYs`z3vKP}W@5KhT#%1}#;fU(%N?$p9V5x2W)-`|s(kZ>H=A=4&EaIF&ZQ%$Rp-6F;
+#N0_f?}LH(P9;MkOBrAq-|B^jG^!bh5wJhdSojg_G5d%j=3GtZEIFwA!ZQ*8Fa9$X9~rXPXr^*4|3cJ
+M8*|J^Y#UwiFiUXagOOn{npJaiJ8ClUNC@auTD;_2C@Wlyc+io^Og)7*V5a>o3ylxX?7+aM1z&4=SmM
+H#L4jhMUU3PWstGFSzx`XFJbvdd<~eU^~r%{d*I{Uu)SqcFRyGu=C=ERvlz*T-o*-{_~eGBbaFt}nuK
+hAxlUyewuIf9&JtS}C1<x;>0w-(~kO&o-uT3Uj}1F38<(vQn~dk7c2Yt5S~Fm9b~`MQ#q1or~lqxgDN
+&_vR7A;Uu>5DL>kceB7+mwWV#w6cIT;9Cyddsy|&5YN;Nr?)KClCvq*J8%o^RdDk0<2d?@T1(4@6u(q
+1Nniq9{wi@yrA>(Cs#q#VTM(5&@j-k(S&x^;~e!&Y*=SuHr`y%xEfj`mWv)G9($-V`?x55!fA*8im4W
+p&WPIUAhT=_{vJbgWvl$FAC+L^XQTs%pZH{l}Osqnr!-3hJ_4<*?f#neGAh-?odbl~}m6&W<q(n1sI^
+w{9WGZv{M^7giklVh}{npEVT@N=;7wdbY<CrPhK4$HkRs}k=O;A27-sUHq~)~hEKA(|F#OCl$PR^3!z
+ggM{1u?-aaFbt=Lj4Wm=tUKA<TuAkXtkAjF$3<ci<tVMLEM4|&+{&@-+|FFcD$9K=blX<q)s=F;)LPB
+7XC1u-?QKII$io)FIczY5tKb@EbvKjj_2qtd#SOM?H-@Ip=yh=PiE!DW*t6VSl-SJuoboJblO5e{^w=
+Y(`-~_#i7$jYu@e4XZ7XsUS*Tkd6(M$aN=kLsvY-z1Vb;wJN?oZ8r_Rzo7j+{TQiRVRQXC&2mWokUrO
+d<!eJ*axIYZw&O}EUaJ4@~G=2!gK@JS0yjPvNsFO9RYx|KO+5L>9wxw{FgwjF3e-VvuNm<`y9qpw+$u
+tlb?3i*C(Ol`Q#mz21bg<5`OS&^ys=C@M}0JXiXTj~4$?~kY{lk9r`dva>$(6<PFhi<w*e2ecbZ%#ez
+Wp@IXJYQgY`X>>k|Kj_6_#D5#%>8wy8x52o3z-l|wJ3$zVL&$lHUcURriM{42@b|ta+oZZeZ4XzqoCd
+btVbk}P=Vr0Na%OuSm>oo6WBeQK;Ixe2}2<AfW!o}%KnT^0rLS72V+YV6neyzP7zJfAGjtZhbeOy14j
+tt9y#3%oDXgT!$T2Vegfzpu3@2JS{xBi>3b-oRWLY|{AGlQf|+g&4?m}0I|Sp~u#))lJ=aRb!b7Y!lQ
+#;{l<AlHJt|_iE}G)pGa6-I7o7Jn_SOYd6ge=8Vt%aJFl>HA-j(4-`J-1r!?(aUMh$SbqjI;%GpO%x^
+d5derufTVeho=aI_+>umsY<m*+)vG#O63%Siz2APF1{T2TkI$Z}N*oQ}$%9>B5%dQ#jNq71*3I+1_GN
+Kz;^K!`7a9rK8))4=7J<$fw3XovfeD74o~2^|QG`es{8dHdn~+PS(%n3i+6<-&$BB1zxbC%eeHqOYi_
+A3y{qvb(VbCaodZr-}FNx<UNbh?Cn}$^)c$vwb$@z!t;Yxs%Ayb2f1ola>>QqSuNukVs}R?r_h@zU}2
+=v?Mx~AqjlM#<9r~5S}metOG?-DsThiA#;rt1(ySI+satu1$p(m>1DSqTM%UjP>2X~wLp1FTbe{ZdVa
+<pCPdM$IBZ_Q<UTut-8!TsDHsmv#Q178$r75?0c&>li&C0E?KONX2-`}HjKR-W*d$)?yA?4}g#zQprb
+}H<R#4qcBQEI<Tj%swEhG&iw>Dp;5@2Kq8w~%za^~$@2Oy@E3!<}<<WH`yLM~?oUxY%@Y&S&`E)NQ=n
+=!~z5YwEb$UUO{YZ?yjww!r_J*y30B`3YO#zrz+02UF32dV(2k5d>%;YhH850M4c{^v%KOJ`InuU*{9
+RJhKQo3V}dka+o?m5R;0dpd3mF7{mwA4VVZJ1`uB9v}7`eA*i3Q1t2iE(1(Evj+?xG>6G*U_y7p97=z
+Bih=y)NKm&l@h-vW<5P&SRU<tTP2iytp2aQ72MS;udDb;|2aVh$jA*Fw7w$T3F*aG?`?HV(_VT(T+|B
+tf8uP5u@hAp<+<8n<!56uf#E=E5BB;^^3xgT-YNyE@dwu0*?G*#d`Ww8BJ@5iyaJsfq#>|IgZTUW86$
+K1g78)s}WBjgaTY0qJ5t>h<K+ty&lj4vEqlbIivHHr=5ERc%YI<392#>2Tjcl|!zF;*Tfb(CbxJygK;
+%sB1mV*;eAA$2p3iA(Ct^}k>X={^S&|3IA8KHPg6xfQt0Z0&2iOC}b1a2;lqGIj+mYnzSAi(5O!Pu_B
+N$V?IIX035Wm!UM;9ijztc#>AD_0E>7CU%`_$2o>OYrS5g^hTfS_uB=J#(k;kRP5+_MVhzUa66^XsFi
+yW@#ed27t&2qQ0x21veY3w%{I(Em7lD~x#bewrT;B#LHswd#jo!3&KB05e{<h`%AUr4O&Iv((IOy_XB
+mv`0Fodh7_<RAkw<XpdCE;;pBOGd`Enr*2c2X<K)@(d0)s%nl+ULH^Y|nKxG85Dz%`H;Ko4XITogmV1
+?oRrs+m9<%VBhDS}C7EA03{;C{r9msv#mMBY;Q&+JeTQ03tZZc0fRY!DrZXG=m;IKuw^BjDw8G5Kvxl
+(f`$iq5nG)Mx~89_B~;|HvS(aj9*XIzYSqjt)hC%ZROs~NQjzT0v&kvg`H*R$2cm@#@je}ZaDkXrmze
+FZP#1zh^1qGywmrT7{ftXwL3yz@7XLZ>T`ZWaJfnztSoAJP@lmp>dG09V<llZB0C<0;S^L$kGczSa_K
+vbbeRh}u32s=C<B(5mjGF<it`z5!gETHcho3@d}ot)!QK6eFqDYln^H!?zPJ&?JkHR1<Wl2!%=f5x5X
+WcY+{p9llK91uJ&KZf*JmT$INWX-)Q3n%O?$JaA1maDdfiUDoNr5c^pi?)x!L0)d1FqtOFAo>`g)ij{
+7Z*j`^><KOUSm?_`C?aj!?JhoJV$tHc^w_wu6;$55|m?umXIzca~3GPsdqR34Zt6gz=G8_Scifh%b}j
+zD`qn&v)Z~JKya)!ufX|=f5BUkN8Y$I7-tjNpm#La3q5h7>ZLQ&M+*4Vl2whG-xBSFC$ejt$IpPh#2U
+$0-7Vq7O$ZvEQN{9K;yg;3<p>Id`W(A8Bq#TwqeQ!0a+5zA;-X!?HGqEIe}mT+?3&v{}_7GFjGQb@-w
+xR#W2Q|<WO_PQxYH-K_&(l<C7c%jW`OG9FR>(yof0HE09NkBbiA~0lmdhFabNB%FuvzAlw+p-{5h6MS
+ijhBQL!s%BC3SMYuYRRN19|SIdWCopJtGA9`I|uHmu}<hykt-(k4>jNB>U^nu~aw-^D$aHR<WYQE(>Y
+sz~W6sk#==_6@x!?xnd_mlPgp<3_xVGC#oVEad|H1eJ-4P#<f%RM#qBjJn=J+lCvHk!(H@v<H)TaeyE
+V!O9aUh^bA)9unNApyYUrjdsol4D(bvB+Qe$lyztHH>{N_3<qa#Fl$ql3$bYUeiEEK#+uP2Ty%;D8p`
+|_X&jahx{`W_{SssgA)kn5BX;%aQZ9!GZP5s4|$!yZ)1Re8UoZi`lxK!OkksB%f^U7Y`K}<TpK~_?Kz
+KIbKk7ZE2gkz6C|={$^=uks!91l(Oa^b0~CrcF(y0ec2?qdWZf#^Sxs8okWLR}6(=Gudxk<kBkTOobU
+$39O393-r_)BY^RKMDT8R|yukhp<cWQ^!gOenizOeUfK%Cbatv(fZkR#e>s?|YpPPON`!EUW<#}i?c6
+|=waJA$J^H<>AWp+!V-=GM&YVm)Uk>zwy;x0&_D>FQ)5PF+*=KGTfz?Ro0aA#{UdODjgQ++I>v)jW~T
+SBT$_J#ky%<7G}nj}2``QK=91it^7DN>Qc3c}f_PTdL9PRJdnLYh`<Ea-&FNwH>?;Dch2Askg<iKddJ
+IBUUC`d#{hWy|_!}8@7GUJ1aWkc81_jWp_B&XbS8nBIVr;>)rFg$qjrp?(X){XGx#uZrH4R#MhQz`2&
+?#IL(M7aq$j%hDfULxF;f}J+)5gADfGBZ!G4P_G#8cZ2v&(4YyD6W0`PvcR%r7S<Z|5oa_#I>S&RO%m
+cEoo{f4FNX|%4WN-+A{e@`njdCL{TuJv!b6MK6?8NTX!|K%S?rm$Cj^#c(il)wpLdsXK%2$4QMTc&E@
+RfahZcKihOGBSzfi9dF;}FLuoVm;+{Mp~H557+9@+J+pQN1JfFdl5``H=c&$`OTu?@Y`+Nzolg{fa`f
+!m|(ao2zse?An=U+OE0HVM}dTFk`a$7h+}K(LVX_MEieYp8Q*l^C#xXUmD|Q^Mt@iioiGqXE2OIIT9l
+nl0X>>SSBdJ5e%@!DeCLh%0P)HEaZ8dfkGM(dlo?njZaQqxMD1u7HMVBL7V1ZnkS%xY$}MsGSG+twh6
+EpfRPf#lU2e^^)Iq1tT@H$FcO*$KqzzR&&(5I!A@>vVDq5)v|4#8kCEbV^#L$FK<^|2eYik(li5_;B8
+N8v777?`ghFcto0fusdjk^&rKa_0+?0&^%c69j%oFK$T4$U1=wA3_F6VmxXcXrEW9EtZ|C=ZOoO$xsC
+-5ILPu9fdY$@lVX|HTB!w}Wvyz%zLmQSfZmO-<h31b*EA#oQaYUk|kad&6^@ucNrP4i=y%TB5K(f%%M
+2~D}JkXfsCm)#mJ?i1JdI18(I-}W2y0z`Yh8qRl(t(SLEbsoX<C@aCTTyvx2mW_GnP&G~whu-L{ASLc
+{yAjrks*gsZpdxjw37cgox0sbf8BkHax_H`|XG+n3qRm$0i++>sHZ5Wd=|-1>C8Y`X^S$&oU2&TU>bT
+{x_I!R^o_iz4Nv33n?N)8-`=!|Q%zD2J@1wAr`vKDB_N}<Z`(5ayR3hvS`Y}AA=1FL;z7>sqgkK+aGb
+iOh%eJbHhK=Ox&c-;aBq_WQbEJOaKGW;JG*7Desfkc7rq<SWg?U%);D$T>f+6DFTI2L1J-M7cM1sFfH
+e2i2&9=9L_h2{SYT)Uc>9GAW8M^Um3;zAy9{Rr0q)kof7o?zXiW2~c5|m>!-)e!A<~+4?y`Cu8bbrt*
+Pq|gs_@=F|XmVRW<eBcV?uM6c`TDeOk-Ul&Vo9=LFJhE*-;?VKJiJd0Ezw^sbbE-ib9>YsujzK?zT&a
+0xZ9DHjnOq^5+*@#!(q(>>i5<vwd_jT?sn{9PfJ!^KC!2e*HyHmh<ev5bV%<c^SpUr4uf9CrtPmdHAm
+vjW4|oKUdss5^wwgRmd{~xSDl?c3;`=#miCqNizD9UL|E^}<4K=^r;!X#w8p=Z$|E1|ke>g3%<k{CJK
+ifk{e(=n-&_NT9=g!8Kbq#^ZvRpqlhgJ5_m}wja?SF;zx+o7tk(POr75HM@Bix$Bj9sa8+b~VerK2bT
+MqD}>dDUs_iX_boS`X{V7@jfa%fMmC}j9#st=OU6Z;a-tDaLZM-~_buz+K-$g;2N+;T8j%f;|gEP_S|
+T-z7JDu{Rtmyn<+ygFuKq&A;crUMHDkUPQs#H66{skq~`NF10dz)FCA{PaB&H-&zDGM`F$P0{RVs&Jc
+5h6R;R)pa>&ltdg17e@&+dGObSs1z6_zceXu0My{{9|cg*>)!mb0E#H|-!0rmrj^?gzyHX)McpoaEl1
+nMt&8gRt>3;cMH^Q{N+ezAQRwS3G^#^F-+Y*uU~6gv{l(8jB#QuWP|~{XrsPk6o9>ZsjhZ@~15fMLw%
+q8V{=Nh+LDs5%)+8Aad0kkadIlXA_Upw>D9N-g74NCGgZ14A{wRL(srulbuHAcI<YMy}qF2rUOa{t$H
+6I4p_}ZpVZNPkxzNi}F=z=zl2m$89=DVQ}+mWX}tbt=nq))1ekFQq4TjHnWD0&fjRD}Ry?l+}SW_wr@
+ECANQ;TFN#U`xFK6L<(p@~rQjUhCiUmis@JX6^+A`L<_4pDtG2`qSD(NgfsDyYoKkm>|wuMaJJ)cb|S
+04uHi!Ht-EQ^iw>*SZg}!3f+qBXAgsthdIp-f!?0zjb=t$Jf|ofXU%inp*bc9$sEzp)EM?jSnLDcU1~
+-t65*0M?T^fJOwui{+T1cccDk{z_MLgnSH>m58i6YObA*mLf%Nq?Sch608q2y>dz#m-n6bJyvn^4{yE
+)g%O+c)rtf-o65zC(Hsg%2kTN8e6=&gwGY0?+Pa!TqJJyG5BsydV55~67_3ok&6$*SKf%k06=(=sC+y
+dT*~TgC|Z7M!)E3mW!+_QL$^eL;Trh56b0g8c3a^RxE_`P~=hXYUL0!wd6O5geEuP4ms6G`@}OwQh%e
+MxPC8>}`>oCg?3Y`ue(@h?ikduHf0p#MUL}Y1lHZOErq<%H^%BPTd=}hUu^Zt8MnbQ&oo*(h`v|hr7+
+$HLpGxN89`C3@cIL^leq09q$JR!`;QZxmXC-879ije0LA-e1v>1tMgG+$NavkPTzySn(S6)2hIVyfd6
+5&fbw4dvo`vkf1K-7UmwMb%W46NDs;tIJu_eVPebmn47DmTh8)Sk;thnO)-yKQxmf64gw5?XmH{3n#V
+~Yvx0@M$8d$WPajHF3C`y&gMN6LDaf9!+8h7N96u}dtFv%g1%C)z_*VRUt(O1)dT*ei7pamxxTFmEt%
+BiUP#&I@Wf3ay`na^VFA2X?bW~2NEj*{&E`YluC|AD9e7a3IlCd2#6oO-W6I(7f2wiQXEG>4PuSGHA_
+LC*{^ExaXgX!CH<RArilY1%mh7xKm=v{vBSqOaFIQE){R%!)=i7=q%MY4I)vT`U9#`$%G#m%+W3C{1C
+E2Tx&_1okuADur<^a7PB8;#vtDUW`J+3Yc9K3*9)tbOL5nhC`bTSWjTs>9oiSScSk;#4=dB8$)3nDPi
+IIV_<n<lRt`Kera2+p}S`ISob7v)m=7|zP7FI(zghYd=K%a#LwOk-Z6-uA-s|66_0L30bpqhtM&7prm
+iS$xoX0aEY`q|ioFI+o&tb;rQDSDu(J8;qok(zhvxy&<ZrDxO@6&N^vppY`QhEWu&%#9?-kXNpP2!gS
+Uh#fB-bByl>F2yb1+5+V6O7IR@mkVWNL)9@9Y(@qf}J4%w8_uD>U4{<rhieb?L1T+hD>OA`r+4-%*0;
+VC;1F-?2H7uZ+&$v^hszdRzMijLysVC;rOIvX->okcu;Wkp%6Br8@GPzGr!OTrAd;*1tN~=3=eYTX(5
+6K5>=SydZ1zVLkf$+76g|v5cCKNl8-|M)P(!^~T*^b6D0bqreEa8}*E_@_5=Vv5r{Y=FLhlGvpSi=gP
+;)QN3^YxhzV?^LlppnqP8gvsP14Y{!y+U<V>g-Gr!ng7Kqve?3@xwQ)Ni(Hr5hOG-K{#%jL7$$g^WrB
+jFVL_9r$whEtf^~el^>WSngt8dRxz=5&mkQa``TGvK=d-RT*&2DY(s10qIx4Wf{h4XZSD|c%xUF!CMK
+=^Ua%_vcEi-XOdXZ~F~tF20o&Ky~u!ZbUc+(*Xp)xjxUA>nH=vL8IPidO1;Cg=7sbj9AJ3<77{k<|mu
+TeT)3d04_P5%Mjm{Df+~DgE_zV95JIFc(-|sHO6cd%5b&veNVRu;Od4^ZBpRt)$6;CUr4VVNtAaQ@iy
+CI*U@H;gS%%W{hx{uXSQj19IktrA?c{?XY66ZF1Z_?KwHLIRReQ;e<GB#21HcFuO)~yg@lOp*rs1SdF
+IXOf_?_gL;*?<Ut%*slt-m^42o#;Vd`VEwJezM@Ap>WpNe@d=niuZifpfO{P(XdIRMj>qOE03YDMs7I
+#=CWx2;CZW`+<-L0eaaH$yS=4{2_Mfe=n_6qBzjmLtQna0)2n0b%fx?GCm`9V=dVo|Ma_}yXMUot_AS
+G%=)XJ$wom>W^M&#LU|x8hP|X9=6_x$I~tx)KlvM%0LLb(TabPl}aj-0&=p(Lz()c=wpgS0q>HDD9fC
+)?Kz39V>L@-#0jfsJWNz36@>`W3QdJ-FyBx2FpBs9RAKG_{Vy9r|q9!{Y}h;peT~Ym@g9^rzKDv3DYL
+H1ZF1!R!kXK$%uL_1Ur>CLZ^)M>?b8)Nx1SDM`2bwmBDQKJb}p{aM4o)qaZ8`7X~vdjE>+e>^O<wda9
+r9^n-!hP3a~P2W#yyG^A*_=nJ0`Mktst3DZF$c;kF3;}H`J3|n9vOok`PiMJ`3bwb84-x8+!6Yxwrf~
+&%QSzKdEc$|H(G+*$1_mx8xiurGK*$S~fRR;&Oy)s(`7X7{h7;kh-@8x&zM}PiihZ}vb0ZsTCPynCKM
+hv_G1v=_6)dhXqL+7np&+862Q`#YnQaEr`GHFP`RNV-Xph%e-r}9Ry5r~y<9W{IBbNTI)Ejp{iR8`yK
+o=9l_u@V{3+xnZkOL7Uo?cH?}etx|7{fChE4Tz>1Aktrn3+Qt9Twjq^!hBi`pY7G9F!5NDS?LxVwI{J
+|yMDG&Xv_|NhJ%@R@A$Lm#&%r>4;lpma8Hlb712V1H}^NeId)pg=}E<AbQhFZR2+}Vtgy^=ZjJi6toB
+4x^<C>2>yxAFC;H)CS3$N$+H;Uxyo}uSqsz^kz5JYuD(CjePTcjvW-Z*hh)j<FXwL-psBT@ZoGf{z$=
+rtTFZ&MhTWNN`1j>A4rvg@sm{JF=y2H-iuBiEe^J7iT9$SU(9u7}a*+HW6`QYE9cm#Gt_(al#Bb=HTp
+CNziY@&JY4SP4Rbt@`~K-K3*641;x?GDxX+7MQyGuTO$mVKsS_+$Rekg7p1hf8VzwRcpj`fcel07hB=
+R(o!})U!p=!Yci`J87;Iso8kJ5J4vTHw*NAYY9!~14wb;Po}>XT(QvIE4={{EM)bwGY9!23le%gL`72
+YEv0f+Bx}maz#03QGzWgmak$NG(xfM8(^O`UXuWUo%;s=}JJhzu6sGuSg0o>Ak_{>c-f3SRa#y?(%0m
+)ZtgH1^fUBlFzm02Sw$%edcIJ1&D+Z*h$NjBWQ07WuevxYGk?ZF}m>hJ0I>d!C_h+-vQY(q&#ry8CH^
+y4VqI$><<@_XGQA9o;i4zLUka5Vk5YO&OndKs*oma3Pm$iv=tdG506fjs#9cIM%ke<gU&8STNbhN{W)
+VEDZ4w2DTns_0}fXHf7&ZwCYb>?*>`(}AH^$|aY<{X=AbQavWr6u-)AK%iuz1brcr;(exG%M2n865zS
+SED=MX2KGX=t|yPg0VgSzC`}ETKL<(aGB8E%42rB0fqNB>{B`Nx6{7Zx$d9p=qI(yzv$$bDf{fSi3Br
+o6h37wax6@drzYTMro??fXwYpQFOuZz`Wo3(LOg=94oE_FD%YAMQ;t0b!va7yk_?oqBnr7J<=~oK4DN
+RHvo&5hEI|$=WfnoXn-P!@VZH(dE2D$&fZ~AK%%_qb;1D&H&mf7ZOko6v0uH5c@$cK<VQE}&&g-J!U+
+PS;hM=0azu5cY2lj$q#RPe)DGk}n{;EjzTjjftV%Ge3#jKI{6F>H!Q(ZBIyDqfu2y~`o?$P0M(!6f-=
+;zHPknb9QU@sVDSW0~KQMl@lg65O!9(gTX{u*@uqr$m9RXG2#!1-JC4^e1Lz;KaAHB;q769v!so7*>Z
+UE<gLs=3*gyOWP1;-r3l=47JcmdC<31g90_V<h{lV|gE#<!N;`pZAm;EBo}o=j$kI&Va;>gwDHlGTak
+{Vz=``9gRzFduvuAW?_2?==EUr+78=V*7l&Wv38DJ&FXP>snk06w36=RC6b|2=o~pw@%3QqhowYjyH@
+DZ^Kd*QzSiUNI~BCLYC?l#6=)%_Jz~-<;C6Em$$`T8*{(w0J2@YhvH+6WjL<#t-0y-JUCnP;dPO3&|C
+VSr^y##;o{oB#?`&P+yY4h+3_rpTx34z6d#wO2NhR;9mtl6!@$1nHO}57>CXy^`6`xgmHJAg#+Qi23N
+k-4+r&=~NX1||VHIIZ0DqLBjNb(^Xaa$%=QGl)uo-EWg^Guek=LltO-i6l1oSUeEnK}6QaFy_9CtA-3
+!d|q%&@Jq|)Dt!!5{4$I8xf)Pbq)+SK6~6A+7`RWfzWu(x^e$eApYkO_@9H`&sTrDQ0mK~eDt)oDa&C
+d7CFT=6B<@E0=7LmRWhQcC@N@4;sx>bo_6W9#RE2NQwQ`0j93ydM44sK!3T~4;Q=%RKJ6ZnQ`6$wX)8
+MNXWl)SB~HUYCrQJadl3frnt<gSQV7`qby;#k;|zX>bzCv%%Oi1^<qi}E+$5BPp;a)fcuM2~mjj)Fy{
+Zu6m#AAn?_Sn@psw(PXKzAX`=3GGX#=!3)J?xc-OvHXH{Nt1%;=E~VPng0JIB?%QE7uNnV&a~`)EVnD
+jIz`yx$oe@@K>Qv(de8&xZV<4?b_trjrjf?)^KFXEojAUV-}dXhzbiLbZDnV!CxgtcQpiiKp;SNxh<8
+qq&M=W;f3Rgc8qIX3*Ny+*OlHX@}ejm)56bt)=>YD_OV76gtGaNTJ!0krw)tQxC3pb9P&qQ)ewfZYtk
+f`s0;+h<zGJ+Qr)59;fQDZRxB3Jdr`M?h*O;m{oKjy;rgeE1~{*0Px#tcCeTo@><RA6?wJZ40Ivl75P
+l+jb`Ck?HLV|_m+v9>Uo*V&%<|>@772rt@d(y50xs+2v&BCTDVxmpr5|70j3>!1k9t$m>-rc?S?us=I
+z<md82*o^w2xDD?iq~IQLv#PsgymsVR;gQoq>cL#eGZd5;|$Hrr4@B+a?hnjxM*Ef1JE^VTYM&x;E~E
+RPR;hu@q=CGS}g&0H@#pN{QvP=kYgeC9*P4=bvWigd574P-9lv-LT+-o15uu5d1=R^jNLF2z+NvcHl8
+%%|zAQ{=tIjL5%xS3VB$0`#wZ{A$~~uZnm7D>{3}-jBnZQ~x(NYP#%?ec`_M-h65FV%fC605w%=Bc@c
+=7zQMO*_ljb3IKYhG+c5+!UdvV=fX~fZqN+oppw%HQxt<2!ENNGN(8T^Rg)CDU9x!ELxGsKApKb`ESt
+cv3d&7|4I(%anDEL>5K6z*Pe`Y=wQyAtJJnj90GXS1_W_^{Zh%cB0&b7LE)bgbCCcDNKEKX|g+_1xXB
+)mvE}P~%e6uZEc974J7SprpcNd1~Xv5Ox<BzmWS*gSKY)!>-oVVJ^$W%P}+oh92s9NtXoA>PrhN(i`Y
+hAdvLURCuZ5_a`&5v72A3ksyG=8*IUl046_|QKx?9Zb{es|bEtCfoUSS(fan!9<}dr}$A4ua5+*|3!o
+`MEQC+h5MOa4W-|FCUKT;a!7>(mh^V0ZozF)8J#}!Fvr)JjLg;Hm{wk6*1u*@4MN?jRhrPs?`~FHwTF
+kPHZn2^bxtMTXfE-$+dbuQYQ3<*t?#(=De-YtxnX>9U+SKxjDoE+KU=D?C4N)ha<U4hfUZ#8zj|c!Sz
+;AJs*@5J&XkHbTe#|+$^`zb|)-sj~VeChxpp}__MF&`{QcZ?y1&q{B4ySkezQHG~7Yg%$@Y2ok#is)n
+2SsbsEqsPS@>a7q&ps`>U>0^#Rk!{fwTi44qK7>O&HdnVzJl3o13wksP1C%3X_EkCn;~SMy-bH&0jZm
+JIi?184zgqDa^eOB<O??Ugs&ccOE@5=;CpTjE}w^%1w4TLPo=x3|KozbdM#sk`^$aYc{-v9ss@tmBi{
+*5N2L$gf`2U%jcwuU^$(y{X9StGfT$n)TNpia6Xu;g*jkk#^>iZdx7H>%+2*Xdhv6?5SMB(Za4M%@Jo
+VjTtNH&!uCf4>X{5S=Bvc%sCv$G~Vj|3f&z_H@IFr?rGQch>*mJU6<SFAu;o{c)g$IY@#t|$?cn??c7
+VDB*;1bOap-lmhzpu+wucX1=i22EF$#)@ObKPX2mGrdA_`**Y(-OuP4qw%_Y>i?$1tN?MQiUXU%pqk8
+g=v#n1V5jZ=QaVEa{roX7bL^`g7T4A(Y@A3K5=RcW)%*tojjmfGY+8>Nj~5Vu=X<a2L7$R{>#=;)Z7y
+e*G-rIKk0XS+L*j>%(uSzp|dd{jx&XX;asMO~opwZsiu6C*9uTN)y}CK)eD>wkcv<>8)Pk5Tg*wEA)C
+Z;<sr-|Y{y{c+y!k#!2II{>CbNm#m-VWHL_rX7OdR|=C4@F@osjX?XCWDR&ef2|gQL#;#-us@oG;SkU
+tT(82xo`6cqNa*^4`&3X+ThZ*#G9g$<$I-N%IKjeoDWD@DopKbWHN+SsVm6&B;=K~_RBo2epivPglV6
+L3W$yAAW|_XqD?ssdI^~-Fa(7nQm@*;gUy=3WFO&7E;_Hv(1?Lm%$u60y?lW$c8}cKryar$1VlSU@Ma
+h<M>sJc;ha_|V8hb%L1z%uP9|q>f?fc%S-?sJfcS*7i>I}%YNwS}ZsfhYM=vVJMKn&QIqd~xM99Q8#7
+u5JRF!O0%EdMZVyc&4@RErkIWstuVnAzlnTJ2i4;N^{dEbQiSN0!WRlUN*^m0Ntg)3I!C{j=EUdAQ9V
+&V{XKsHAq=-8ggaj&k*yXX|(;vETdGcysnSq|+~RV=V5=inh!M>)l)4+vEJCpDFZtD@}KMRQ;YKkKvi
+$@$@Z-&Y7akvIDJL9Aos6wx%CKyls<kpCqwi2K;p?Qz$8rfq2=rr)VDp_4=;gWVX7D=iR}m^6@-BMBa
+?S(2b8os9g^<(Z!CEC(U^frEJVRul6F*G3E)s=?SquZX#4VJn><FvJxv@A93Pt#4>Xk4Md)NUXRr5`D
+WItROFolZBU8xxZSx&H{=rZjG+-BmiOj<H^g}Gp55}nFta9gI0pq7n|#!iB_vV6?QhG(USmASsy(`T<
+n$nh4%2TrQnWX&aGLTU5g35^Blz;Q$F2zLH$Ax*YFiYl6aUdM@?+1JLqc1FuCm|lh+-(;rQ1wzCi3=b
+3g##IF9P}hvp3LHEc5e1Cq%3G;$tV?pL}Hhus?q!b>tgX--t1(IZb~LDzcb)!g-yVVVU58MHOkCd4sv
+s#;~avDM*>_W_rUC2{gmH?SOfh<t$eQ_CWI9Ip(6dmhZA<yVAmIY4geYP<bqSr>wzPdv|V_*&Wr-t)4
+>Yz84+KyjNG$BlTr&9sT2KP24znjUGaym(`3ungO;yRQ8&<>W;HMWbMwPuH9kSKSad1lD3pZTtHA%Mk
+g!D5n)!wuDtDV?$k$S;aZsNV@EnIHLq|rS!dfUWgmMxxP-EgBxEsn54T|}lxSkkqA@A9#}nD9GgWW3-
+DWv12TLARVQH+=mfsEYl2b_Gy6KKUWl#~)9_yXPpE@k0s?~vV>$4pchxGrY?$4SP#oDe>_?%yHpBu61
+yE|eZ(2EKJA_!;niXb38zkV~xrm|U?Syk`z#QS{_l^LrLrnpk(xVkY$kgx(X5$yS%ng0USKNCg&NAm7
+}xz<nk{dt{#mU9EeJq*xG*GxGgL6ZpZLL=ZRMY15z2CjlE=tL3g-yg#`7*wD@j|X^1ZB3_in@k8|pwC
+2a%2I3UyC(B-3dH@^bQ=3)7V@h`6SB=}L;)zfuJ0Re_1E>@f&ir2(boG4V^HQ?SB%*0jTsP+Fa-E4K$
+L?9@3aOIYvLXy0Puql$PQ8<kN+c#%fN?Z!2Sf|&;QMw`>X!?3f=LyVENU9=?mt?;x=9g%AY8hSA;ato
+OJt>7s;1{Uyf;t`N#ba`sUrh{<qndf8PJD-+rVY_q<P<Nt?T&X3O8=|6lHfU!#rR4g5hrPW(|;HRi-w
+mDijN6X!6Wx*dmKXsmqMR&yirR@Ee*x9SU9Iuh?dRM)1LBJ5wY-m~KCRE5RBylC>~MWyEx(e8X|>uOJ
+^(e=6Tl4h`N8NJ?n&X4vo{JKGkFpjB7Qu|U(rsu%HH}ny+y0m|!n1OUsOGgA|KT}V4tmjJ;JC3gjSi9
+OiZQ<O@fa1kct|yU@($ns|f8qS{+FkwQO+Z`qwp<i0z|>;bm5W1LFgvkKWe5E$>2Q@(Co0)PXyK;Cnj
+Fo%{OtS4KiZ7d;t%sK@RxOa9}-;<+0Id)`ubhbcHGVtcS&+lnGWG7(vl$xbFaZwphn$A`cuvFyi!q%B
+p0=Hb_M19V)+dIGVn*NiL$yse_ME6bM6ao<f-?3b7@SnQFrW?rcW_l;fCD@M1dbC1EEi&fh#m;pPOr{
+qOAG1nEh_yMy_hb8+}naK|e;AzYQ=gWK_OVIjrws{Kuej^IZ5GRNlV*N`GYve@15LkNGxd;PUlN9P}!
+8?tvY52zES`y+Y?A<6bC8<?lpsdA^*{Wm4gd^p5OcRV3gU<I2mPXkO1=u+H?GRx`yq6;HT0?L4Ehbay
+r{E(|^P&Hzs$b17`|28$HE_)BWFm=$^VFf^TBqK%h47lu=Dc^2k|(#{EdOqo;2;xJc1ij<GjdohRQ!W
+He|*t*zxB6%5qu&=jtl`8xlIsWNjVWFo~%bxb9bJ=s3o^~2rweJvdQT6-CKjCU(y}T&uo)x-^E2L858
+^N?Aj<XI0OzDm&U$jxNcG(Nt3;GSS%j#;D(Cu+*pDhlXbuFnb8cm6|ekyh^_38PB;uLmlCT+(3csQKT
+xuzXyt$T9bzd&ZQ4~j?nNaFt)aQ^qp{e;ioEcKUs<KKj7;H>*e|8P^CiZ^M*V4LS>15k4QG=dRq>WJa
+T3h~6|WlwKn5uZFA*Y!ejqjIL8c+I2$_VEy`geD*ySTAA1|3<&Gk=(`>*Rv4@C_KcQ>I7g$kRT2q2oM
+th<=_}(*bxTqJlTMlOgAB`0AMsI`LB6>0*DFerWy<qZsfn{pJgKUn@mLTCzyWyZ^E=Ts$Y$`>ThA%id
+`TQ=+{87;3KGpKEpEd0aPQOpgQ_3R6}2Km45}*3wMXUrYFyDKckF#?gY4Mg)jUbuAyHI=#xAZz}<Lot
+K|7YlAYx2hP!BoUOth@DXq2BtrhvPfhInLiSsm3#2b}dy0b^jLhh<}x;~vXGkL0aV=uWjtlp17?~&1I
+%#Fxx7Jrm|#z7T`p?RWCy|ejx=jk+Ph#}xd18Jz5TH4c7Y6cve=*!d#O}0bxNeTEN5}ch;7RPjC9uRi
+kbpo#hNB?zio|atR4~bEu-E~-Am55d+m_J4P(LNtBM!wu8w58rv%T4HGml6hZh1_(Oo@}{KrRgf|`V_
+e!){f7pb4L_dn%d?Wd-C^_blxe}=y)i@+mUa3hp_3NJQJZ15~-rL_+fnUJ9CgO0iq8VT)FOYGo~%QpD
+P|N1-9+on@9Zx*Vh%UEdbZhUyV|eEWGaVK$iom3Y5$3ZpL&~O=kA$<=1cxeLu-B@K5tATpw1rR<hqMO
+v!JkR-U8(K|$#6!Zq{-uYUp8xh8zI5DLD7>zf0VJ4+~Vo#*ee>F*$$FW+(UCLWk`FRU{anDzkO+sdxA
+q_HtGG^QMD1f+F5M(!4|G0Q3*1;;uC2`rwBjvnuDC{BvfSDwkT*kY`AaIeQUFmAOBXHz@n68gNeDl%2
+$jiNcr8m74D@pmhk<(puOi6!GVb>FqR?i^mofx1C)=Gq2%+jEIuJN6xx6vdwtJ+0enmzBEO^s0IhsfK
+wUWv>a4nk`wKoT~1Ex@jojkU<F(&xW<lE{S!MM(xFfr|d-z=<!asN6yIn=u|k|Xbos2xg##)6#F^^F?
+-0Di1na{$qU}XlINJ0TJ_MI)~mu3k$=c<YyVR0{cMi$`I<C3!5o$t&M0b4*5Oe*?codgs-UU)2Rwt%^
+t>M>jenuO@Rt(Qhx!ZcXU<^tUQspvOdL$!$ty}Wx*&!i)PKHP>SG?`|HZrcfwuVDJNYhRKq-Qv5C;2@
+vKUi<1`%yTNMyXVz!QM^Yvzx~pCzNv6fhC-^jBnv5dTaD4C&1p6bGsFnrkE2){~Dm!XWXvRu9~*x3rz
+a{+s?v4uaksLg~#ynn;1aT10`Gd=P^mfZSU6^kydtgs~VPHo?STmT5hRQs7#^x}<=}6dHgc1-6kM=@>
+*3MD)kd;R!N){Oc&GWVxN`{DQLBiXX{A<lObk&L&>JvhTi%A^~3CSEBm<dm1D39aH`G7h8S(a|Z<TuT
+;j;nf>)O-<HzPi5UQP_p=-XD9U@^Q1ix*hgiS9{?$wl2p9iGz2H+90Ml$~)&WI4*AT7Px&U8~LAt#i9
+J7%7z+u`OW$sHo0sV`<_*oYVaLyt9OQyE)Kj~$BBbTv!r7x~`=K;o{@QKk6`7RK9a@H5G-%C(EHf|>f
+r?79Ro$Zg4v(V?!*)J!2^>+QeB*tIug`Wqze(Em-PWRM>UF&zpl2vii(#}pK)2&WQSGj`p9&h2g;&Vz
+Oj>dvZl3$*lNTR%@z-~ip4WQIkd%>F5E*#tA8Q$(x)k^5_;2w`D0THKyi|kaMk?35^bu|h{$GhJ`i+!
+PpFu>V!IJ|8-vGv}}WDXX*M&i!nDCCH@qyq~bi!xBj>+K}D$sQxc#6`5*A>Un&sjMP#bQ{o~oG64X?=
+s>AqDvi*dAQGyp13>Q6cu`h6q8farFizu)x)3CBKed=>Myo$TkP!&VsVY4no3?f6TcyGrk8w=KFjgG-
+;uTPfLKC&`e}>kBT;92UVkqS<y4cfL+uioqThS1tyE`+?e+XsW#(fS8v&-ZU)u{%yFQ}te`Z<tojd}R
+am@B*`TqO<Sso$xCe2oHEeEl=IlK9f&U01;J<rywKKgd1C5qqJ6N_}>j<~Sl4_%MouzpnPQeFvhe=mb
+4lg1|#?j>k&5iscRz4QUkjpDskojh6}m<l=NoXBMt^CRs$=ra|?Mnz#nM=d*cBI)fOmdIVR#$o*V)y;
+NynJ`vxcc>L8mr)apWFAzvdfnI#I-|$s*bOgKy=Wfn8T0J%Pls<=nYj|Rj*R>{(Zt#$Xe*nnWmKORJ-
+R0C%)bwm-v<z<a5jwdyW1q|7pq@n=RnVn9SkJqys<vO4s^{VmY651Wr+>^rZLfc>6Zu9(DnQ>Al5Y_;
+fY(rv1woP+k|lz#$I^SCdhb*6bZ}ii;F)*r=KIe)k@;N$9&`&Kbv0L?FJZ1dP=@Dv6&gd>n1hVQ^Clk
+JzdO1)H}s6xa*mEsYaAz9L=B&((U^m%X;&?zYUp%mJl+;w@3_@0bWyhTq^EPYr}UEcHbuR_kx=9{|hV
+{w&7zCEv&e{8Hs;;Y||fb@Na{Ue{|KiUJ&2EJbb4~iQ*Vb6D&rd6i$!?je@^$gk*7&!cY<;5R4_Lj{*
+k3I*Gw}FwmezLG}(@+kk8M7CA&9oLHj>Bn0r!e5@aZZJy&S0+xx=jrPHUWFJTq(4dbG=HQ4;9CGt%#S
+y@3VSzT)F9L=j1*M2Ju%Iw70=-58*w)`?(9Ilz1+fho0Re6{0c8<tBhD~eVFNS+641~`J`H4(TYCcmy
+vcCuOZ+)t5IwN*KfeYHrU3SLljFG82s~boo73w@PLm=>K|h#%U#_4W@QJ9gk>CMr|4_aB?wn1}@w4E8
+`&rWwOwr9+uC7~`Mrygfl@Rp&`(v6|ZC>cmS5Aq1u-A<K^WgG|XO|6<zpr@wbPhK|o05R+E&qh3-w~`
+6#YYambv2X(>8QxuY$%5pyKYK6i<b&~PO$jEJ4)ZZ@Oh_1IBhi@AMk`n6*u)n%EaT6(}%gpAeh}5xi}
+myevj4%`^+WPu5)!dokLcMaZU{}3Avz#)#_(l&+xO|R&S<{*Cg%zo|OU`FWRF&WubdN?;XOh7(skd5|
+f*6j;n9jfl7*dL|(k;5-=zCov{c$GNIR;MTHkHuRFwPvLzzZWye`T^Fn8Ye(aC$E|)m1X(`zdojS4J5
+i1T3LLbU>XCu$ddz84#U#Y_tgN(>KJjo`PDvopfr*F}?S>G11AKH|Kug2~;lU-`!C9ipS<%Hch3uhu8
+3cK($z3!nEaBk`v3kaXh87fR0*O=?4dFyS??E|NAi2n({%>9T;V|{$rRzRQ!;|(8#aY0T>oQvPX7tiP
+xq#Z8}b#;6FVpH&Jwi+u;FK_&Ht}QdW`^)cp@ygh_7eV`1=?yUMNOzgMW^}K4C4=AKHTd%!HF+F4$JE
+b5ckN?8M^8#qJh6xQ#IM(Bcan4xhWwDsq&c1*M{6MnD@Ov$AsoRb@9jB>`PU$Y>aZ}kHI6H%ny>LbK~
+5~?q%n6f7Gwvd2m;GWj!KT`jOoR!%`Dbj5%$zEcerMN8@ZoinL3ttVwosGcnHXz!i#%&Xo9_ShTfMeU
+a0PF2=-U%9ch<kjwxQ{7<t!<iPA=jzOXn;z81AGU#6T14|s^AXHjHMYejd6@`@8SCaMpc&ilsgP6#HS
+nIn$Dlotr+193AAe6Z#F8s;m^a^jgMS07)u@o_MGHpU@Z;AAf*{(=2?QhlmIe`X^J9B>i;a`1z6_-|e
+C)n5FklfQ!^f*|M*bJKJinhruBssqe~HZfr^Nxh9%rzxl^<C_gP1H9=zCfp<d3D%@BAT~8Xa`O>N0qr
+K*Hu@L^KNIZcRLX+BGZuhmGWsj1Syy2;A0up=*IqN@C<b)@7zd~VxHtqr55U`}LG5}4xb-6h8*hKjj%
+gM^C1`TTU~VnOK#@HTfv(|t2lyYIi9`pa5c&KCYD6x#+z%CxCH1G&3;H4V+WRj1rKt8P_p(}`vh}%Qe
+fakE&Ey;UJWcg^idzNZU_X#+emIu%!nXx>=rg&-)p+wufnB#SXZTXG`<$bO8vp!~qh7v!9r{YJlyv@|
+zzx=pp0|iB#PU(D6#mX-8736-XB5W7GrllYeAjlzyVzw$ZC|9lpyq)qu;uwuvi$9Fdf9o!hj9a?FXc(
+LR#v~oAv<3VM{buK;;_r$b63~<eW?|KB3`er0Q0O`?QP~g-4TPAYv*=y!~uF-FfJ*Qj_JuXU{x*;pVu
+94I(q!#tVXA!_F)Z7;T##<EEXy{l(vR5l&IYHN4pQN5PIutB|;s%&0C}(i0jUYY+~%-g3I+`D#^A#*K
+0Pxof3!3OG_B(-C3Zmz3B$*bKT^^k=gG9rrlR;c1})|UTF3?wvxkR2#p}S4)*D-a9VZEtceWT#YiNgA
+0zCp9HS(uTu3vlbQFdva);;cOVt_G#_O-(<~zoY%|S_huX682H>2|ud6``iv*@+AX8#01HuurF(C3qX
+sraiWS{?M);pSY<M@P86$Y&s*FGeENkK(AlAfKeoWm++L_u|{eHt?$ytVkSQcH}Wcol84k?)vzC`+Ki
+BJ(dNwJM?*isVL6(hMiuZ&U78`U6y$DWc61|#K`0JK6+U`Q{`+fLR%Lyr=@4fksfL<6x7>uE?n`X3et
+#;NQR6yIbO3Rl*2U}^oL@4%h%xe$^&1(%I0eHrzfoLvHtRQiPzbTyJjeh<Bgw)bBDp#7jz#9Db#N;9`
+G^k#g}ewSBN6Wx~vf@eoG`h={ir<Hwjs8q<T0J_wsg2ZqtNf67_N*GL>Axl29~Q!oyx3Plx;Zm4Dtke
+V<=1T@oj`gXoj7#!PE$8E{oJl-l?Y$VeXm1@_Oe<)ce}jV}Me{@5`^w!JvmthCpC8QH}2*K`2T&o|#|
+z`Be9Agm_@AN&3Un|eOYfc_>*fFdW!03b*)08Ai5`ZO9Gf`n=Pd+S<)tcU&;nJC&o1xzv_fQAWDEgFC
+ovyD)Ja_BazjFVfY69J~^nscpKh-2VyWXs4_Xo*u$t=$}pz#XiR$%ZRv{SjJx&@p}faXwl4fGoN02*2
+OZzqTIN4gLD<>tFVBXzS0ckn#yyR`^%|8UFegv;bL`Z;$2Q-^t&FmS5h<chCav<X4=ZpD91$U`Qwj8J
+3uy(KWPo(_Nu(j#0TP<307KmQ^bGlqsRjUQxJsQs|B0$;rjNzdl&xK~-SgJ~5Doy~6fVzv+kUF<D+GS
+~tw_=w4Sum<)!ESZXqwR7l;BXK&=?z`q};n^=gdnkarhLH67U=WyMpu0q!GqJ5U`%Ht?${ZcK*^62eG
+A(qer4wr6jda4wOkmTKc<Kv8`lfelh%cpBid*OgS#g_83@gZ~0&}0*iP}0t59VS`OhKe7ah;vEQvbFb
+KBMI`Pzc^C$Y3M?XT2vj6yD8N<Wu&BK9`9;=l3!KbKH+d5n<obI@FC7IhwhTYz99^?CwH|PDAgr_u>H
+ko?j3v^?45N{?_W4S-ooF1Nzea=aSzFRiO>gC&>Ve##3;(Y2DQXHp2}ShKe0?dI2ZbQ@&`H6{Wp-q!3
+lb#x)_p-iEiJA<b7z*X`vIYO$xk0q7{X;*_FO=RhLWxu3lf^{=~yfW*<G|Iw;RtTXb6*AXv|0OUJY9w
+u>omxcdkBAuLZdKTgQYdHG3ET#88{<ySXY@!7>}ndNfNc!^!V{=RjHLwuQ{L?lJ-(v;n~MJK}ObgkcE
+b2=|p5nJ7)T1YE%%~;eff%fm&PuseG!p+my5hw`58Qw_&!Yx)@-A<2(%N8inhLh_b`UE?s%!AgR!i0L
+<a$O@1Y@)BD84yi=Xzr>Esm$q2(yhd;38_V$o|Et#T*SFOoPBScyNh5jd)=Z^-L3bRBJdEWk@v)cs_)
+%<sf-089%}u5VWN3_P$BnkFz5Pu49UCwM+wazuK5XdzCHhsu)}UDE96H14=~W1!nX~Z#zEDKi~zqRAi
+?Mh7;R-fgq?)i>TWoI0Rjj4#ELd-u=&n0Td6DtfU&{`O@I;@N`f|CvgRDW>b3zg?B*BFrht1IVj!;p>
+MSH6;H<z%L>r83eyr(+NGS>`as;-G1a17DbYt^`TdNQa0kJjO=(*@0X)$sDG8*)c99H??z)qs^<Co3e
+`Sx|kC2JtQ3`N-IT;fw-YlRnW)WNLHU*as}564E!{;TH)eGzB*zULPgA^K&S;j<A+&P88FYUFQThg8|
+=gPUJOFZ~nY1{4z?zapuqpPEP+Xd<n@tE1;YP9Wq{&!zbRxnK2^A0A!U1(&3Xi`hLPoPx}-7d@~0t1C
+2&7iYS+;)oEP8@4+b4+e@%CE*8$xlJ@L7Pn(Y*1mQm{CVNkB0(NHfg_i?p8cHaib%}lOw1&nd21$pD`
+eY-%>10`Jd#+qFt(Y>(|5Bh-zr})7H1-tk!ha2bSy7qj(SG$bOUi5;)~>^^Bu1f`3}i+Qc`J_lySF&g
+L$kTa!Yqjv+jK58}_MyI0gMfRdo|K%t1nT<-1QGZLx@zMJm_;Mf3sN2e{e3?k}Y=$-OL{d^8YY`yPuC
+9W$r)UEJX$Sq>N2!syNn?(IWP>Ns$MJv>6CPe+!S?MPNVh_-vhJ^p+p(_3d2lB$@lJZge1YkHv5PV+k
+GPYe^*-8LGc9m1ef3_1I{7W~k~rahnE+^W+Ua@GiD5G;l)Pe&p|*_uXgzBxSM2e1)&nz)b!)*IpXv2c
+UBMDflHhx_Y4PjMzrM0%~>S?3P8a#H7l&#O?rpa&Y;EAKr~Id$HPZWnu;vsY|s??Wkj=Y9P6IO=ScQD
+rjk2j+_Fvzxkha*r{wrE$_(d;z^8Ra1wZTVKh^I6Axe7N%AL@efvhp!rpuf(yy}{ZY|<O=nHcpYn&*q
+}Y^PvloYt^Cc;tsq41&xlzhPbl+K_-tC~*z3--pUGU3fDDI6-Nu84f`of$M=Jg;dqkNVc<Iumtbm&*0
+F6#|_YMN0zJ=8-XBR2&R5k~ZK{Ox{=>Cgu1zdQL4m=1jh>Y2o8x4fL_TiPEiZociMM!dDw9T)DYwnlf
+sD8Tc%%}c|Q-M6n=5flV3v&=JQ*I!dTaU_D^$wGNNd*+g^c{H-vyz)TJcM;qMcG>xN13}4_nWk=P1}e
+Ql>X_e4nHJsbjx|9r#PbgKPN-y+@1cO%rHGU>buehyR&ZL5Ea~conjr-b@x|NA{DL_Bwug^hmkEclHk
+a&Xnl(2Jr|Ph`Qau#YLUdlvV`tj=yT4r-IWEKb+K6r>23HtapnmbZlDHajjQ6Kt!k+ZY=R6c>In%8DP
+?)$qPH#-AW}U}79}kJ!^=?fjaU`wByU`)z6+Zf?dNIW=r7_YM&Coke$rE`^?;d5Y%JX3;&*jx3QItO?
+`8bRHMTcLwS)P?+9zdpwT@H8I_HKGi(fDhUkXpy~@3;8u{F!jk;mT!%dtdhK`n=)K4(UpI)MZa8F;PC
+c(6h3Vx;dk@NW8IV@f0L!n5P}s@KdA=?m92<lCiWoI(FXevZIb*Cslrm8eiT|+Jl883r^o{ahy`^<(G
+l-Rvs42zRK$~8V_`;eX}ZOo<K$vwGuX|UtDn4JKTBr7Y0kr4iaVJ5OR-OdeqqTV88|?U2A5C-jX52rb
+UXoo!W<}%=_2yJxogQoQKQVIKMOalJ-Lhg@?(=+?w)_Gi`LY*Nl7p6s%u!XEsG<P!0H9YQ3T>*zQSIw
+70ty>Z2tW_%Z&U21|cCNB`qhzYUoFMy8H$N=qRF=6-PsR7%+dq}yvW5^bh2>k(#4*;(Sh5&dDvgm2m$
+=qB<(prCxa#ye}+0=ktH2w^C83#cL>?G&V7jGFkwPyS8Fgl(fq_~y_@VqgSmjoc{E^W6OQP%x~t#!l4
+cOSAp}Ufs0TqfOWy-9FU%PIL<7On}xpv&or+|3&_&)+&cv$aMKr$h7}AA=7u{hu?-wzs=V3pR)D+NB(
+ht%GN);8|d5H`Sxzskmy$#yYMMvS3$-O7(~M2$R~cb=^2aX>AF79$0V6X<XVw&YSGb6zEDaN@4MR@sY
++PS(OVPuO57o$#GsJ7s3$HlLJF^OJ!u-Z2h%U|tuO?$!Y#Lu%#oYO$bR(l;dK#`Y{Dvq1zMY3#a|Ujm
+UnBkg3>v9zw7$g2*$G?N2OGsW@K}TKo-Lpk!pt5H9wluv8nIysbS9)5v@6M+(5T5tX#yUaP55OUDnMj
+#ym96VVvok=|vAh+9`7>pj0F0Bd6S?eaUj-cnL49(+1NCY7@fiuat}H69zB+Zb2DwG)9^_k%|JtfvFu
+XEKKL;A#UR3W?~iozTNMQ7DY*)k0Er_{kNk9jmsvF6W{vFQ5D#_^9MVA-``G`z26?y7VdC-Vy=Pgl+@
+@%A*i2APS`8zKx!76#cn+}@*P5z-bYoVc7sK$j>HXxN-Ql}CP9v5PWV7n?O>sDcvap8hGfCbrg3P7{e
+s1osYo;7yd2ytFDRErhKJR|RcKc*zSDD^5gC_1yK4qPQ5q}>y;x@Qg85bl-8t^%H_I4F+`+7molR=GX
+BO5$e_T+rzk1<u-}X3M<~R86*hl(miEqAT<g!yQSrx57m8H*i{Vs4fm}#^1i+5?7X!eVG9NaU{5+OzN
+?3D8RL!{ZZNXtw|i0JbvoJu5_3Fy?J{<|yhI?Z17cv`gQPUHFYqqoJ1v-fD09dmXcY;&qG!Th<g3;n*
+-Zea|1yYCtk+rdJ5Qw|etFNn2d=5hH{X@|a<R{Gt^KaWs(-cOTmpF6wjj--VA^e`4I;rg(EtA(P*R&e
+wqoYC%~;|^jR%v9@M(%k~Zx93t_*e>ED<e8GMw1V4m=OkHEIQtonP+8hb1J%ko(dv<(!}@4aBbGNE&n
+)`hgx2V&pLi?4`^+b7T3L=}P0p0mWZszU{cAKoJlD=Mjzb>^a$r?|*Bf@Dp=phaR`!PEj7;@@&m7;^@
+u0!$WfSf4?_C+zg7j7_c0W}_n^*67Y33W%_jARH)-BQxE~e}+6%=IfzB5dFI$I%azAyD5){Z?gg#7Vf
+q|z%o)(wZM_ZK2mJ}S-(TBSJ4<ZL$)b{}|<jb5qi&KQ{Cl1}*Ixksi}>x3Xqn00#d7>M+i9aCF|QQvQ
+M=IyLlBc3<RRvN4e7f2DEc_E_r3Dfesj7ho3-YPX`N?)oDW$B~p?q;2+GkSeYwUE_}<swAlCV2WV>XI
+oTbbK=B`)G-c-|BGES!&cCwbYB%<ANyc(V<)PcuBIyF1y!6{L~9v!}y?`X!T_t4i0n{9{Sa#V_Avqa4
+$!P%F<(}!P1g<n=97s$TlfpO~>Nn&Dp1cVI6UTrw4%x+MVa_Au2dUZ=CR?%3z&)%%8ezoxL!h757*ay
+o?hky=v1XoA*JrAMfEs>X)m3Wv;LD*?EpP$ke#Q@q9T2&%2H77Ts}f(=_CD^_aA}lgb-T4eoOZ_Eb3U
+g=zQBR2L~i^MuB>wR>FtKBKH$gO(`x&-qn6Udivr!hta559fanQvJ`Y_?1`oPgeW0oC^UeJ_xgswqsB
+gq6jcXwMI6{rjY`g+}o@48uWZT^o6saZWW=RAO(zoV=%-W1)%B`g&^#Sh^>eegVW-TGKPeppH2n8niA
+i}&1oF;+9T?}c(7Rv#@kv5xR^*bP0mf`Ki&l5){qelqer00zeZi4tHN#~Y}*V08*U$MlOs<4D19l`AP
+B4GKLug=AEhr%7X`+&xp(1>u$=1fFX>Bcjjg_fKi#+IL0?Y%TXV@5?z8wX7<|6YXOY98zY1I$uG!Lbw
+t?4*wiUR%<xk?natE#NxW1c|*%;XZ0pTx&E<NXvFNLntRqlQ|?~|lj2K~wXMYDge)&hO6qn{1b(W=*X
+-jBOyQyPq4y@X&YggI?)X&@nC%I&ZMyR)YtI2F6%3EkT=rzt@hFur(6-Swdz)M<?Xhsn-e=T<18h0G^
+YK0BjZCr5`*xXZgJPgSm0E<dk(Chy|d0lwjT<(<|IToS}0&L?(1(=UDr2QPogc+^t|%gpg?Q4D)IXIW
+nQIUQW>)`iZECa07b-B~w+_s6>0qXc=1CdVR+p0)0+AZN72&Cv>#2a6`2QspI5nx;928wD3aYbt36WT
+RZiWDVU$B{3dSnG*N>I&)n%3vB6+iS@Q^$}pISRa&Vvj1E)1_d{#UX9b<{_rBOk37KBfQ4{*ZQB<q=X
+`rvR%*y9%1Yguz^jurNz`vkze?JYz9dW;ceUdFnq`5JR;Yl|XSqzOOe$rBges}WEdi#p<V}uX=rMJ&B
+k4{PzF%gK<W2|<lTSWJV=dW+n;)3_^?~BE`C^I9zZJUzUbj|T8Lw?U942Gs_ra!RNKvU>(=KaSboZqr
+@LW;rJ^|Kl_)??vY8t18NF)Dm;6Zo^Hp4WecQ(o`qiGHr%NXLaOxnfpK<K%Ng<D80-xk2#9$xJ)^d>A
+H1elUqXqnWM7N_5&)k2h4Ado_f)>zh6Li=)_SkMw6PVV9?G8n;uPT->-MvbQ9NaUEv!i@>vE%-mx&5U
+_bCK$;frbuU<UzMooMt63?&Ts3`|T3vl#gOa$!d*P0pOF9%{oXwe@XO%b25ITIDsJMk%qwM>9VdIl&Q
+F5kdr^Rkmjw%xrT_m6WU%^J~e;gZsz2d*Y#y=M*VhV7l@Mtr5*(QsT6ciCq1hi+6O{aea7K8xZ`0!(m
+6o%Xk;?iwoECh84dK*PUQ&3%?KCz`YTiq3_(9PQg-Kbg2uk2)27{#|P3>j>L2gEiLO#xtB?=1z^z5r*
+j&B_f&!E)=*D}Xa7!1MJ?h5*9|$TkO^;+s?(1q=ch<iBD3#{$KMjl}<-VB`FIY!sjKc||-;%>I7~8}r
+{ciJ<SgMB17hS$J(+5tFUv$&6mvt!?#=X7>?mjLsF0r)*i%F;5O!l<4$PfG~vO88&&P`!x@B&)F@}_M
+()xhsYFF!-mDYy})Fz6H(|WwAHEodBSk!?jLu`AV&}A*!la+iKVRY?ZNY|oH;NletEhLJ@t15Ezx_Bo
+lHF8o=Yko)~8~TYEM^LId;QJ!l1)+H|RrKjok%7@li9f@r^T;gfhXID(>gzcqtTqYAyefo3Yd(ZQSM7
+YkJ>GJhOK!#8>L;Wbqy!Is_j+2W*_)&jY`c<tNdT=2KcTqL*c-2yxJ@H4QmC>MZWgQv0e!rl1q(wlFc
+CoM(j+?#G8Fh7W?5y*R304my$Xeds6!*DV#Jk!C<@2kY<b)##CoPl)2g@Q{(bgZ{N-t`gmPC(j<LulU
+#;OH%A<9fzj-XY)KB0*RM$JwS%#Y#m)HRS)n{#0!dsZm;x39i84|RQ=}e&0IgU?n64yk4L^d-gObh>O
+%U%-dBrR3zadFG(H&1Nk5=SFEx<QaI8zY0mZPi(mY~Wx)mZN1b$2m#}j@INvq6|tj5lDkso(9wx;Bc6
+O{u7pY%oXp$Ze|{SiCp;^EOt+MR!?TIEcg)!wLM#Ef|LiXvxNfA!H0M@6*fqT<lIOqUW2_des$?nJIJ
+Lu3^PA`kiG*sIk`l&v+{E|*aiv`R?dd+U|+1c^sl^=9`dM(ppeFHIuo?@oTm5-N{wM)df(4gx77*ZWZ
+$DTib|Nq*KB|M=wX+#i}m&~GO{GKurCR_Ps9KF{?@qm5F*I^TYrv_4Fphp2o(lfhq$ta~!F?Aa)3!Kd
+Rbnd4&YZ%1-Ow8O1?5O9-rYJywWV8|%<c2VbING113b{KTpSE#3qR(43W#y?!h$4~|dR_{cwJM9zLyX
+KZR_N0rRI_1QR=Hpnlx1`6*m%Kz#1OZ3KJulMAi3lPc=bz7hcXXQASlk_XL#>^B%MQ0|exJw)KQ(cCo
+R=VOZyEE#@oZ)wL7%Nxlbh&t#Utv9_QtfEt`^6)#(O6mxto-pxn8qghz-MjH7|Sgc{S;`GyB-N*Aht0
+7opC>SV?v-m_IFtmiIN!(O!CP;FPT`LTSUK)vdo3TR5ci8X=$T7_VkGJNuo+L1M05TzYm$`)D>NLkZP
+$7w@Rk`h-kPFxW(@VB=}utPko7A;tQ5I9?O%P8V}^^k+<lxI8mSPQRmiRp@P`x&n5uQDxA$`oIQ$eIK
+LFl6)+GtU-Q0+?3;qd5M|1XdaEcuQ%vi!tDFuvh3~P`zIMW=J!;QmK2aP-8{Sw^Ui;rbH9Hp4w)TJY9
+NPbBpW$J)}dpB5a*xK8&7z+YX$Q?tBg{Uq!fvAlQViTO?{$?{pKkI^&Xa(YPfl4=1%Hs>}pwSTwOr^&
+@5PVq>dpKT*QY^rWg2(RFjgHaQ=`MS*s04hUsPKjpzomw|kk1NN=tGFxP<#M#%D*@);b@aPtoYYn?|M
+{3j8rat)oL<crjeqh2^bM%!LhUz<QIh@XPui_YACxVEjL$9D9GnC^dQ$uB|O2g`jMx?v<sVkm>qG=(u
+FgHs5>AQ-{0>t85|kR(OY?1#1=YpjH#TLWsF^knhPpZC*f=Qa^TCqOEm+#GQc>|?VfWQ*5AFxAOK;A%
+ie*dnt}u7hiE2PQUCFwco&fOnQ4paMeuiiH3=Rsm=wt*4SCphqA9kiKJLV67B}Aig6bP_>CkU@F2gVD
+%UQL<GR7K*0PFiEfU>F*t3F@7DKDHop<<k25=B1;UT2`AT?`2oJR_1m=emzjLoL>T{9zA>oaG|0tCWf
+<FDTKsSi_B<ES!rY#Mc@RihFI%TnVfR6UE3J8e7Zb1<k)|7$IE{N@}nR(Nf5Yy|=y!CIwGR>AyzmC>O
+%fIRa6mHR?UVJY~uL56?HL6snv#4QT=?u9FOqtfuO`6@De}lV+{!eayld}CkxqWc=(6_h$_Xkyl!C^^
+vUs87nD}|{L(v!@#dxH>Un|VZCCn6yN8)cl|G3KD&#HKyl#(s}Ar$dgs_0&;Yf6sGILV}7~rd!b%Tgy
+62?hZANOlSqpk;W<PZ5*>bn!(c3&kPm26MNin7Gl{C-JS_5rx;@g@|zW{F0_})<y?;#ZuR5cgBi4;uE
+H|E5!+ZFO5cj_5>_##U|=Ir7zcKRv)vU(-VKDY?0i<2rFi3$OfQ!{?_{?PUTo{#9-|V&d|4eU&F#fng
+`dQ(I<|_E6gER}HorqUXjqt|Az!oeWo$X6K#FU_sHbC{Bv<d@D;3U9I!$*wee7a5FN=qK)Hr2nmHosI
+X>fw#TS9pUOd?^T`x391`T7s`i*V(~at5?X*nZcsF`cuj8OA)qk*FohHQe#Um=Ss^c0ZYNT~|!|JbZe
+CWj?)4k#Ksx3EjO|aQ(U+ul6`<_j2yE$2BzED}I0*hwDm1$8S<qxRC`d!lgwJtBnGJSCv+*D#q*PCzU
+4GH673iLlXwsGw>W$m%|BtKFPFyz-g!)?_CrcICSDuGZvhhg~_*Zxa{m}$LA9Lh*&Xy3WvpH@P*#h%k
+p%O?a@EL$i%*GiaPDR_$jwBL=U*e?r7FKx>IuZC&UWPlvkNhDQ_4>rf0t%PvezQuAWq0I1Ewv-ZKMQe
+jc^Rh(>6)6GOkzI4;D?lipZ2idP;8+Pp-B#Z5<nUjbZk6+?1jd7xAHF5pT3P+!HJpQ!oNK{(4SFAdJ$
+0d+=Iy0q7zK(0^O&G7c(KjAxoIznH5njO8q9!t_y|GCuv&wrNxRR2%)V_D;mCw~FA&nJGWRD3sPO0CG
+ne5eT*fw2fM(MfIN1`J>xgeaIeSd)8pQ$hh~gaJVM1;YW@!(*UYP9R|J6Wv7ESKPo-K$>6^a1bZJN(c
+ppK>?VcTLs})3hXO}uE3TiK#gU+s}KWfG0awGU<pt-`Q$B6$6!ta-!x-b768)<Pr%le1#m`@0DM<a1N
+OQEz-2(cp#F;ye}pY@^9a2BMV>R)hxQ}#oJjmYp5ry>8*<!Hnhs7_7L<RphII=cPY<3_6{Zi@9RDwmS
+<M!%n+rS|UU(5Q?a$ooe3O#^Bsol5icj3^<@;m*TX*me^v^YF8ZWeM<BNWX2+w%J5|k@DE_fNLi|&b%
+Jw^S<M{OW7v1#;KDyZ~Bzfj2<)Du{HGGB5$Jl|(eIFN_<^gOy1lo<EL#QUcgik_=2_fYY7FJsMr3Maq
+oZbifA>6G(T%a{yyoci1^f~Q0vxEKUEp{UUH6nDi_JuqzIWFyL7Xub*HoDp&I@xoGxzWAvmc!nUsV=Y
+J3Qtb%(dc(q=C~iUBL+6lwK4;jsD1N5krgSOzcwOL!*aoh1mwR?sl+7jdQqr+$vymUC+BgP|kjoSXtJ
+e(Tj&&taQgAnwXp;Oqs~(v8-uG}*EAEXp^vmLOT^+MXw#f1Yqu63tT1-WoHpFU=hW5@EV_FS(4*yDr<
+d-}Hs8{hby^F4W3-_&bxr8T^G@7FnHW;Da(hIszg!|6ROmE*4LuPfDVaZFfQ*PdK&zS*apIf#m<KYzB
+I$U0-NV|r0rXKr}r$Z+f%I9n6E$S)QY1DX}-)CB^kg>3GUJ$3;&Y<ZsWXjvmWFc;~SE3E4!(3{xzKA<
+=7A!&5{TqDtN|SoVq$ik5reBXDjT@{%*a_M|Lf+UC+0wG0*&aE<h!?$*#L=#6qW<=rSjqbdy=i8x8}p
+!$Pu8j3=P}=l&j}G{a)Qoqs#@?GC{4Fnawu_MPt3KUS+1)tja<AGy=NjF3tRWqWg2xbrF$dhx8u?E9M
+v~lh*G$wB%GsZ=<mnFZuz^{M*n25=Q!?LvChA=wa@?Wnuk<z1$Oq+qkMEp{9{4;pI!PD2R^+9uKs$(`
+|l`(0(ov0)S!XoUkC^RC<_e3fx|1ZZN^(Fux2Rqx}hQ;Q>oF-5(@{p%ew!r2~E8DAI2=;3sA&1kN3$s
+c1w4tZEH>dx%yXVKm!V1fNbpU7$i~{4T{mg{uKp1S%TRp-y25{08k9nK#)&LBLSd=6WiDvwhiHd^aa@
+rXW0##{)h(BRzsbdKNYFzAM%yGnM)z`g`m7X$rlSh6hM6{dhojEl6jyw3lQ<wEbddpXPRp)AKRWgl_7
+q3YF0Hu%4Z(7$jf4gp@Xk5n-Zw46kPzMtVHSlxo9PE<LIq7!vl4$FXCJfc*noe2WR<<!5{(okMSD@1E
+JaK0{-as6FocCm@GfYCtHX*DGTR+f{jd*p7HFvGUDIV%y#A8!|p|duOBg&%+KX93F60bf5qh1FWGs$-
+9Y2BPrs84xb?AJh^^QJM)Rbp4&4IrU2{_HPh&?bzG#pF28J%*7wCN;E^<3yfxCXR<)3tbPbBwBt)56~
+>^@Dpb-kR2XQ*al5RaGlPT_ZFk~>ih)x}q}ay7FLAifjjHD=Z?7xd~eGZvP2ML>;aM#krOnr6FyPjWy
+b&Y`*ptxmPMo)KMha8m8z8b$))8a!k~@W>CA@xt=CJn+h0NcZs@&KL38K2T!sE3Pm~kFh!INzZ37RTi
+pe6qjAn5s;VZE~8d~w5z6SqOvkY2zKyZT5%tE*m?Ixdp%M+ylEHLL)j(QE4d6ZqS4x0BKj8eK9>o*&!
+{|N4D7n@8r=Lok<6Kk>ET5d2j+r<0;{f&57c6A#WCKKPd?v!^}NfNsDzS*Iv%2w!+pHe-ON-En1Si^W
+h5?<=+M=Pd43ht8byI0eo(H0DqcFO3*|Cq+7b;@)5NhzS#GRP<}VD>zt4`KFX>V82?<kvZ;nyU5tSHZ
+y6Vlr-a;a=8cvu#hXR>@dS2MvyMUjO0Rj)l_bn6WuP@O|=21D0dcz@=cvLjyj>LP8I?XIN4)o(X2z*s
+P3-7G~p#1$e()sHlJC*Ald>{q*H4hWykl_X)q>SBj*ZK7Hyl~4JI!&p^x!PQI@9}zZGb!%#k}mL;%h(
+L9P;-42n9|PnIr$a}`n_Xf+2RKnXQX$&x6wE9a5*`}1f0LV7k1bS19Kr`HNZ|c^u6e(1+qG1n0me$84
+cm)i7h%b7&Gc6)Gwa*`nM>+%T0V6p%^=Yx2AUp-@0d*>6)$xGaG}}55o|0<xDm0Gftsuf?ROe(oz?Bp
+$}_jm~i+z;2cGTsXjZM>v^svUTe|wRXsHA11d0)c*-P#9*@D5#^=UegQJobP;8#6ahD9ct|HNJeh;Vo
+xMY;zDQH04Zo>objH$t)wlp5BtF~EZHJl`yj9rQ^@>c70;Dy;gz~;{uzyA`R|M}HFAoS-2{uxNeo2OJ
+50~g*kaa-Y;qJa}67<Fd=scuci5)=%H2jH}iWxO}P^)*RK2>_Q#y2*Gm;J-EhBQ~3Lpj1MDG!4hW;8s
+Kf$*lNS2E*aj6ONLtpPYi#S#UNWFRr%-+SU;uEUuA6y1gIZ4r1VLQx+5(SNuk|!S*!}h&S)#HLY7wpF
+zNa3G+vgHUI?7$)7-a_kRtfpPHb40qO8BgY{294Skl;{MSGYr9b$<|5KobwhR9SPy_YDFG`r2g6f5bJ
+G1{Uz#95EyW}U0I3AI`_>PD~F!gIpYQsT%9?e}$Abp@vQfe0W)X<=)#OH*J-|d)A@qDpvvum&~lO<JA
+*omXgtYJSjr0TVk=WqwB4Wba?gSZ`QrxrE*0_|&yKKW+cyl%Ia%unyr^t>VhlT&l6w_WrOhnP5+H(Hk
+?<OC1(`k9`ZJHCxJG+CB{1R2A7<G?9%`+61P`Bu8ZL!A-?A+)5g)Mw~n&7%``ySGp+uL7Gr;_bCZ`d;
+MIG(I7G)CsH)oADY2!q_Q7j0mt!pYAAp(-gg`0y@$fg&GD@YOn`ZTag2YxKmR}KuYEUQQqSWpHYh<^~
+r7!o<uI^X2wsALGYJrzQP;65A??c&XKQRJuL!1eb(uMzM!?CKkSEg;<D_easE?z&SL#~aR(3ixjgwTS
+VKQKY<ZjNcDx@gO511Snc~PwOcdSy8DGH{4Dv8(`=8U7FVpp}XcjypR{-Ga&;Cv8J^avWVwvd_hu+ta
+C*E5lbbc@ccwq(MA#~YiIDb-u{qofPDFT3gsY9kle0b5~v}kW>B;U1mXOQsuSja_59EhrsX}g0>1R|S
+?Z0pFKyu+Y1cAcM-0Zw3nIu)m;F%YtAbpNua`$~Kt9uHX=2r}2nkmoLlW0X1MlO(^(ip#<|YNI`2)ij
+gkd4I#|tr}nbkkYBPlNk!OZ;F`gZn!kRr9^(u%iao&PKC1L5WU2NP%Y~u-XxY*wI%H2Ub}~FsYUJsEl
+d5_%Fc}0ELGVvtJ=gn>dy@NzVgul3PcJ$6lve}0)~grI@=d)P8nk@Jf3oNky#<V1~gpU&Q`&_{A+jCa
+Q|W3in~{@cd>vTOA_?Ki`<8BQ~HN{F);iR$f+1k)4)-!Frj<P!g+eXwD9F0bQw2-*!K<?{{*%F#pOT1
+_Lmj@8E&t5xWXNtZax;9t5vY|-w+y9I?)s)vq0wy2U?Wa#}u^{*H_42F%n6DH~>Rzy}<;)B)biJfk84
+13|U9yf5QO075ZtS>{ntk>-`|WL<@{iuK*qsV4y5rFW#J~*QHnJ#<#BE`X9ED=u!&wFu|KK0IPwa<|a
+=7(r^}hv^7azGxR82Q|v$Dwz=VU|0mpr|F7Y8p$Xa-+>So6&Nlt%4d0-T0q#@%Ru_Q{hx7@z#ZUGeXH
+lB91^l^^x?Kd2w*SFO@EHr=)NfUhKkTE@0sGXfO6WZ-^~%5L0LLdu<j$W(IU|inbx<W8qSBm{obx`<n
+Q7AX%O<$laA<*x)<7OyyIe>0i?y}LPoD(bl+6I)yY2S7kmR5Eci^}@=*8W32NiSVgq!xJs-6)qovWD4
+-C+N4+t=B3q4agQRJ<c#=qvX3Re+rCG;TkgL3Y&Xj8NRUnxybOM>jL@>D<tmxzd7NPOvdNELsQIO2yF
+XAU?Z;9nTi>;^ICsW>!EutS{ghW+VK9)LCMr<F2j8?p*upHhjJayLzP66O`WLS<dEi4S>S41|Ph41Xq
+W1aYy!Zhx1&_K6dhc$s3KQ^1jG}PBO|iys&CZ$(-dv=oupxg+_NfwRKM@Y-8=4#ki*{*ylsfAW!@45X
+9bW2TtO;IP4bc@>bf*V|YFJkfWjJwTRljv#H^cT140n<c%A-AVrEUk*ookMiV{82j_OSJ8Jyu-*M2?z
+4C*9hb<@QgQerD2;vi%AgS8r#3geavf{55uA!ex%L1Q$O|-``$vLJHo9wK5*hh&r^lhg7^+A#Tgx%|c
+M-IE^Rs{j@{uOrroduz7#Si*4pN76g^+`zdBfJc~YF=y=zc6|?_C=z|wrTbk7KZj~SoUy*`=dSJqRb!
+5Q8;1hfSAe2%!vB9=S`(O>J@)mAMImomm^(Y8r}=s==`p645}{^KP;mX>@0s#5R>u!Vb8+rK6ZS*kn3
+*Nb4q{u(pB6E1u3ORz4at-Y)Hiwok_hwIUm7=818PN$npEg?hVsHZH8D5CgsG!n)$jEC&StOUh;{+RO
+6nu7fC&`0clpcw1cKbo@jBGsp?dos}jjO+iR~#VeN?Fp*>K-bmZ|wT%(wzV2`BJE20+7^%B8cH|Nm{i
+h1>AYdu!%Uj93~cWz#AxasU;w+__soGTi8W@qx&k@xY|=2CLvAAcLW&AxJi_|g8!A>-?LpFwyNv;NH`
+KVba-;@f<Q1%7yo@7k>_Ln0VOvFtBwxZ(sfB3IO3Q8T1LTNA;z=H_Nxks<(JsR$I}v5&npzyc}gw}QE
+zP4;@t*4a<yln4OmH~{r<WK+6EP_Q<`0%^GDmsE8EXv4)@{vL+l9@q#71g#MVxe1Z3IeUbFA6C4NDe#
+s+nrPEirBYBz0QXFP_S<B)zyx@9ZK9|2A6Zf|vL=1}>G9P|<NWr`$4AYrpJM?5vTe!N_~^@Mjd{H}gH
+y^+V2kk)lnav<eLM6G{o1KP>NVl3G*I#F!hdstuR7anzWxLgLyNY2(%JrWEZUAi;NZ`(z(M~(_1lVF*
+7aPgj^^*798$l^eotQ=I(}==aUsUj7hmt$v7m2O{(B4j5DP%RlT{rs1udL2<-iNYl+o#Yf2WeiKa0?|
+1`VYa*sG!9K|CFad!CIbxW|*X?n2K4a=amjo%m=C;9VYaN5|T~I4M4#cvNiOD234cl*##kiO~C8mzb4
+>f%-<Bsqy;Z*2Ou>U@>(K^6n%vdnc#S;w&}}BBGA|q|?NSrsJLVT-P~3t{`}6)1_$**XR>(GyGCd*y7
+QT;&{JQifj~DvNHmI1q5%2E+{Lu<9eETz2tP%XOO&;CqE)$Ki}hVCi(Lo5#3YZTr8}V9$__Q6Goi%a^
+BT({Rn$9`u~`Fvn^MVWLxx|ugJ%Hhwk2@Z)%M5KqndrBoK|@jb2C~kU&E4^~arPRzze(X095$?m0DTW
+rPSiM~>SxvuV?w`NPjH{65(>UF#yxctuw>_EgVJTNdC!oDV`PR9QU5=0LB9en}<Ut-gQkj<U6gtou6_
+&_wqniU@xuhn*E@>mh))PX$k31~YA@nTfEY3ygtJyl^a6v$vc~(2N5YoZ#;XnBGV_&u{Y)*5L%d>+r>
+-|B!wL=-qB!7}NY7{4Ng&@RZH@yN6yhybE4g_&hHFsKS7Mu22*=p@3d>BJw9ucs{SfbEQ|YQbBlIA({
+eSBGqmHsJ8!RR#tPD8)bFR_QDW=Hxc|iA_-w)vF}ab4<`Uyhg5+9FSm&Cpa5@XHJ{uki{Ky(uuo^;>m
+9h*1osP-)rbIm4|{U=;Hu!3tRlcU@P}%Cy}`CeSPEEzyPc-|`5j4A{RohV{gMX|UrsPzb+y7`EIfv=A
+<dsaw(x*=l7Y8UD*_F9|N08qU)ArClDW$b{F+|AB><vo^fS3#>o;*mILJCaD@ry{+vqY>q<tM1%iPeE
+`<rTR5d}jLZTYtYzV45}1DZgQTTK!Eos|js=w<vnD--gzH2v<%R1_B7S0c<@{#nII_zPvmvbWqmXVAJ
+`UzBuz2A+u)oVcpVSK68I)5rF3ldl!YkySZ32QfG*YNIU7R^gfL0!^((W|pjI<&Su?<@Py?Fe2w%FDA
+oJ7#iOL|8l=qn{L1K%G6rpYx;5SlHJNnEE_#P8m+J~0<3Qoy{b=n?qW8QsK<lq7}?TVhZlY_#lqV?nf
+ta?5A?pqpM^&1-mOlQT3Q?u^^o<0otct<NE8B#&=ILEgD$(Ls{`v(96Cp<6xy!G<DFt?L3!%>Wxqo&y
+W1p*C3SB@t;xx1>28<V%{X5zA9=2{fG*W-m#g(rQ>**6R@OUR@+)+IiG~c1PL)_+FF7r$>ts2WF5xo9
+p`<Q1%PhBRWPh>nh4mX*nXk}lF4p@B)&3gs{dmkz@a;<pAI^S@-Jpo>tPhl?P>UyyW)UA8k`Q1X28Ub
+F<X;pC5f}b_3sr!ShzN2Sk{p(#le1tE8N=ee7>7X@9EF&PMPWb%Tn*zO*kY1@<;+hlR4K@gNawQpl$l
+xEvz+gX_C2^ySnW>E4bEnanaqD8=k6!@%+Fw$nUY69ln7vHM8SwqLe8P3zipwq>Y){&7H`;16s+@its
+Fk-A;KJmbw-~E>@4!DvB<|2@>h=GS0cl3<m$sdFTIstTB91L*Rab;ei|nWz$%S+4ZBP;W$tC$48IK7e
+P7Rz--247mG9?$kS}Y~Z%GXCmKY(5(QYap<u?&4t%KVjk*an(58=^9wF}*~wsx2{q|zHl+Oltp)3FR9
+^+7FEp`Z`?6TptKp;HY&OBFkMg1TX%M?v-LGJklt_*oKS(e=yhykLf#(mkb?Fmz?LJf_RYiIx-vfKNN
+s#v(XZk7RkTx|^yt*wBZU<+ZO}t?$<ge`C0`7}(5tXefTp&E>-EY?<9BDG>|E?Usm9v!buAFOkKezI(
+AyN!f0%u1?y0H~_@tmFGu(V#7$h`vZw_S@cwH(ym_7sM8_~-k#X|WpU6;gF9>=5o0f=)K0*nY-}@StU
+|Z9n*4ypTgzh7PF|`Rk;n2vJ1^100YlE+*2aRXhU!vBC`48!dX<FHMzVI?Wy8g*>$t}kw?%dsdzxMnt
+L_m`d-q1!OQvp@CE_Ln?G8_kDrjzuZCt1}wQO%!Ne!mamY2=4D->(fr0XQ_yUGtA0;Z?W%V1H*Kr1I?
+*DK9(y^GXXvb(+M5~z*G87AO{GHSQbQB4+8)A5*&&65%djLHu2xRsVvARR<}TOiA6dz6@_TiJ(e#q9R
+Y&0nz1=&Q%;PW8ug;AGVCVocyj|5BP<akY&iTOZN5y&Nc4MVc|OnPXsO%yYFrkE39&!Pj=+q?NjLl}0
+o2+Qy4<7p?%Do30y8;_fFu;kJFe$i2IUj2^>Gikg&@Nua1TnpRZ`y5@tyXF8fQ+fh9&wq5GK!%k%YsG
+|Qd`y;-f#@&0G=rf5uIx1^+FAtM$IWE&nIKs15X!nX6O~N}?2`8Z{zS;SGUwWmtaxyP4$k!F-cNZAs>
+k9L}z%)NDZ7*6z61iTT@f;DVdvm+2(#_yL!bRZN!A>^8WE;f5kYY~_R|MIs8{NLTk|L&Fc{p<E{py<<
+f6&b7f)vZrT=E%-SDd-2v+=kiLVNclTurU>jiXCeKf22!E)1z)Q-QHqh@#$_-f2gcDwwxOzFJ=N+m0|
+=F>p<bNW24nE>|Q+dDP-j^m>!l-bo<ZQ%V|`X8CJ!83fE2-PDJoQftKbck5H6`&g7b9;+aqri(y6y7g
+6XJAg~*fDO9}wOF)XO8aB7Ug*-^qqWeD+`dB92ocal>zY^V6xs^uFhjLN!DBjM+r^P5WBcwMn_^P9;F
+O_vJ6QU3Xe=p4?uhB;Yt57($4ytpoJ3MaK+)D-+ZkJ$#ymO>#8BUP9iKnQiV<CZ@!-H+JX5?dP2W1*H
+b<|qb~fU;T1f1UnD0fWMRe4&ynINvv)!m85#3P~-2`=TDx%{sk@OC|qm-}|DEX?U;o_kA5>m%JDN4)s
+@Hk__!qJ18I^=X}Re45_`lCKwHQ||t^@FI6>@*}~T#JVlUJQ#%u-~8|(i~-1@#*n+?3>-WAe5#g4q=#
+GjQpYFnnt#AcrZ6lEyd{-x)v)!5!^{>FUyb%Zrq5F(biFuajjR4zBQ#SHsJi~W|OURRUHjqx2dO8ORb
+YPHS38Rp7(^-q8PNhbNymFMF^X1pA5IKPndNT$Ah3BFS2{&S7GXl^7bYg33~tcVHxpnz_K5X`4N_V?n
+g&5-&QdYvsxoDBQTDHoPQ`#Jrhgv6k-~37Hp(*Nc~K{7gxY?n7x7$MhqmRG9>IIht0iiBUrHqqhE0b&
+quuVUtlQ6hGnTAU>U~FMArmrHv-^A&5$vg&zQw<BP`xy;75v?d)m)+4O9dpZ)6JV9T)=QHaec8f*3?*
+kX<|XoFM+TPlIxCL{ZTEU$86*{!hTN=?g5Ip_us@mN`6bO7?#omKnbR%OnHwP|f%BsjkhP<7CW&NwVC
+Us&yFenpIPv_<)7ygR?u|3fWqt;(k(Y4-K~?vnZrvBOv6lWt?N0lFgOaQRisN`s0<=tM<4cdIl#>%ZW
+=gcGD=?f{*lYBGg(MD}2eU{LFQ4h)Pkkw=GX=$7@=CwUcP;ZZ6pPvDoh9^>Msn#{-4!mC?9NKG$@3g>
+U22c-r+hrnnzcgslJwaLh+}inpj>ZkG(VmL%I~JLCB9c-hPD&pSmhwoRQ^Ddq9OAf~s&rg1niiiZdC@
+Q>1}iuCm<J)U#CzMUC<eA-ndrJHjRKLk_KF4`#;iMw!PWNzKr)%H^2Oh58nx^a*?9B8KQqlnp>JpgtW
+V+|}r_gLMOERs4UrYY{SJY|eAv@KPhbk}$=%T=!570t<y84~w-xKhZ_!0XGF-0Nr7c07A~bPq><u{r4
+t@MQ0mE*2E&GM2aZtIBVwoV_^gdYzsU#G&M^%uHDoIeAIdi<{m}<#vG;5eu-f?u@%wW!l8;43EuL8;v
+!ndy0#4dp}>(QtA<(mk)-%YnA3vB=|0j+O@lPY4qk^SI@Ok8oNPf{Gv?gNRw9X(KimqZOdMX<aRu7r4
+UKzzAHATM#T3{qmPD?FDA5@y7N9#%iFdj58`ga->S*S8aI1HyTMzQ6F{HX^R}!w9wEz()^F^y!$oxN7
+K^KLHZbO{BuO>hC6>ADy$$~0_+ObpO{icIPbmMK+#_GYMuniMp`och_~!`48OG*hKBfYm2k%Sb4n=4v
+$b0AzN-=1PqBM$R`4pw8-W2ZVh!`4u7uZq&^0~lH5eFLsuj<b+xz!J-Yy5Mn-HP#N8xZGWGk&AmvJ%F
+?H&K|1*H$J9?;`fSJCcgzu=yAnEo5Kwe+sRc7ud-x@Lz~Y8{8a_Wa2lk>C^Yr>8IEmR2~FS%$gX6DY=
+W$2H3&8lOwpSp}cVHQf;s=hT`*owR7XvXM6fnD}#TW==jtY?c+YkThhYX^AlL>R?ojst8i$%*vJz-?D
+PkRhu(EGP=vWBE#8@Ne-kA<l`H@_85S2WTMR4QF)Qn3plRjp@}Py03WsxaQl4kEx!|N_m;0<nQmi2{X
+3-rV(;dnd2wiNac(;r9)?-h!T{~p{c;Ctw8rRAvep@wI93H_E@Yrt(bCV*AjgAk?ft2aSAkKQb#+hli
+z4JQvz}mBAUa;2lt%oOhy7mOG3tKMuXV#3a<nF7H0jqMpd}`#;-s;OStU{a`IJ=^~y(DA08@l)|UX=S
+`u+$(5o%L->j*3HVZ+OjJn?sbENKcXC?PBnw*_`;riK34c8Wf^Cj#}|(_ZL6*#pp(d+2Pu<=jQA_Qgj
+)&Tk2Y*d`&+QDG=l#VjOKs^E>IWYf8J*DqWS}F_STSiAOBGUhe9$xTX8$xaIcoIlc&DeA;zJNHNHnRn
+Mb;j&_M~b4tBuu6^Jnv1KLljXtycBgWk9^w@D>A)>StctNAoc3n1VZW}t?A(MKz7fXK#PM2N0%1g&w+
+u}fBYvCR}mS|)x)pfe;m=ms$(Z2Al!p;)0Nwl=wkuiEg{1HPfeG>-Hvzc&M`%rZCu@%rYv$^FlB`C~+
+w>$r+b$#x1a&zv<;NH8}^`^7SX9nzoUKQ19I89h{sW5T_+&jm7l1${pw`)ipRaj=x0Po~tEwbT!#)}n
+UvlcTsd!x<|YlPf;M}yHrWuf1(DLwQB-pYOR55tVx|FxIJt?|O;`tM8D!_@D4FJveXalgE!e<2nXdF*
+q$<$ve!H<bGqJATO|f8X&EgUMY0ap?%+LjWUV3No&83bL=D)MhjnRHd^fSoFQlCOx-ijv?sHImqDRG8
+jB0lbQ6DK++eQL%~dHF5iOE<OvK={uqVK=6;(nQ=CGs7&r`_HO|33#3T%7qO%HTM8ec{LO>*p&CyMOc
+%kYi4k0y)!AdP|7NZ~|m|O<Pn}&()zb)GJ=eA_)Ure%Z$}f0Hu)Zv+YgdwCKZxLMQQf(yCpjouyh@DU
+<Jv=weKP3QTONb@*!ma}$9g37sSg$inlD?joVN~`#;|pa!H}#G{G(nZBR+3K{L}Zh_D}B&`RUevy0fo
+p5XfK1K}_9}TB11qWS$}G(XJ66qV)#7FT-7LFwJdX$BIR`e0g8rrIwD{I26}WU9;V`3|rUoL%p9oRcW
+8kw&m28NONUUPD*;&pBOTU`B10NIwGwj?}6S8Y1ewS^ayBiecTf=nMLE_T-Q^1V6MQSQEFlDA36PK-k
+!aVMVYr?_+b@uh|w+1c)*AnmzmeyF3`b&l**IbCZKyAWvVfh8d_oZUNt(RF&u|gz@6!ZsvQ0Bx-=2XB
+eV`hubvy-RIbU$Gtc2H#@)j;f06Q3(!=v59l%1p-w?Q(twMXhiUiK>s?AkYb>w+d`;pyxfhq6Ubw4c3
+<;FQM9rn21@EBWqyLCk$fN@JhTo;ein;OJ!;<+z8u6>c9br_lB%Eu3<dMq9uwcre9Q1{ZGfyi5W7Wt>
+;!I3Ps*UjVXU>Ma6KDJ`aLS8%+<XjO2nwr<;<G!eIeEA%_-UrrE-H8O=*a2Vf=)Dj>9NOJ*=rv$Zi%g
++0HNtmWQO<9Od*aB;nlo>YljpFDs1_T`a;p8)B*J=N9_K;1cO|oqWbJI#<IqjbV#+Sj-mtuJaiI`lSn
+OT7UK5UTIIx%2q#MTf{X->f^p0W|+gcBvZruk3^=zH>b;Cw_dNz6aX<l}b?jpJ^ZCK|v;`uG!dlAz<=
+!@kXBvLyb$Ka$AQ($TBF{E$8dVvLgg43OoCuQR<diBush`rA%!&qf(-#FvwiEikDY4q&0<cV5$%vny`
+&1v9(jSC(V9pA?TU#>O;_o#;r|2x&e`{t6}qCdnc>N@;I<Kbn~*IJ{$*zet}eAwp?{5x}kf1A~ch`I3
+>1s8lgo4XX}P&bHV;d)GFv%+!$Khj|P_gO81fq@emm>(JjHJAhz5<wC&1}knk0@|dQfqD%X3hhCT&&@
+cZcc$~7wJAABFb2D#1Pse07&J-Ptk_J=ArNpGf`W#Hfp-N1Ejc^?DVn)d6f9g~Fqi_1BX}$@BQMI&uo
+Rj3TYI9-%82ibBtiX_naopP7Hwtk$U6istgoxK;zIJkK|g*7WJvN;aAKUQl3Fe-{CP|WWBkxw?1{i;8
+Rm&6jMEu?E&6$W*#_*#mztmR`6<nB{2V@M9H9cvSM80#2e$#Z1lHRffh%^w!JLfpwVl|9vK-_uMIUtj
+us`|f_~6Z|OXbBeAeM5;Jq`<g<G2jzFB&AI>m~nunkR)FZO#*-wdDwgj7j=cwwBLC%@=yNC=QB&+lS>
+{Cl}YHQ(>$!#!h;;^a#~BCvV~R>%>8~<>}HM?y?EYz|y1eM`ZMMy0x6rXma~_Vm*t|TMYN9RO}v$J1a
+9CLO8YOG|=5nb;~%d_*i%SI!Jf<MA6Q$zBCG%?0BJESSL@NwA-V!)I#)=-gYNkJ|U0ojk#EB4~-)#ao
+Sdt*j>L}iBIx`?WnS8nI<9C)sw-Fhr$!+gVLjRL4#Rwl1|s-9bqrQ7?+teTy_&7oSI(WT~pV-2?}2%U
+Z!61?V{27(=KSvWZE2Y)_Bu=m>_o?`9bu-ek-gM3rpKy#jCG5&fzrtDc^$p%!d`CsmZC;Me{tgOY!h=
+Y06+=bn`-RL64sH4{j<eWV&|#J?b90blBWXe%H&xZK+9*h#c+16yh9rpOok2%55}z>93ylhHb!#o<0~
+;qV>fRAtYH#Vlh3X0cuZc>sh;JjuzHAZ%?PGdaTIj*vHsrRBvQP?xeL$Ct}uKc8q=zgolKz%o|Ce=kq
+z`)9BG2w;6Y%#uX+Mc_81cqoxXC5~$HmZ}7)8j3vMA>f^%BmKTaa*EHgs>1+b-T4he59X>Ng28fAf&_
+l9+%Kfz7(#8E^+mUea3C2uM<gLMa@L<o`DY}Fcj)XTixbfH869d%Xw0KDErE<D%MY<W|{=qt<pX1I(t
+*UmonVj*@m;XMB`aja__J^5)|5ni@&E6dWxW<3?_phem-#o{=+4!sDKZV~>_&@AhpM*wXH40ev0h&)T
+C_RiOu$m-^VHsyO(+bd8fIs5?m3%K90Ty>`ZW}<%gi^53Co>t9i=l%9t^iI6{RIj-5x}10<g7;rmiwR
+djnlcj75Ej9$dEvC1;xRScm$mu=!ax8Yh%_rPBW-^M9yN$<Xm6F&CQ>v99k=29#a(7tipzxH2hQa_uB
+t==#yOEeG-}1OTYYH@6P*TN4AI~{pw2z?1<+Q_AxkEML3s&P!;?c&x|$iZtR0We`9k#F(*}4M<f-6^I
+Y#)d*G+u1wM6#w72&l(7Vu(JQ_{{?1?s+(kwLeG#z<F3@3m_1~aB(WUSmT6fnv#Y;sWfx6r&dU-jcfk
+q=jW>L>pEmMu^{6Ak<osws29&NlR+-re&U*LD?`ze4uNcLgUuaZGD-jk)%UE~3D#)m8uKrVAoJAHF_N
+bcZ*Ax(Sbd?;XW>nVp558ZMZW<!&oUSZAi^E;eGOS;~VTOtcL0gZLa64B>hYcTs$I+6+N@%2t=`wB9H
+A-k}OdjI@Q;lu6FTqpbMAzhDE*&QA;Dz>zIpGMKSNj)$z27V=;(NS>NZ&Q_90#MGvw#mv=@jk(hDO^p
+g{Ev(p+db>KSW*zsm6*7u)Shz^Pk|~~8jKnn=YsOYj^#a{rnd-)<NhkEjhhB}ueXsXKQy<c1+I#)}!Z
+TQTC8WDVG>A~&L~P$|V)ZURykIqwVRgPg<uxO2@9A?8fqA>d&r>M@zXZQ6wB?nh<P#c>{Z+R4BeJ*Nv
+;DTADO-adUWFNcCHkG=HJU6JgMAWYp|Cx5&+~>f49K?l9CqD&Wi%>%8xtXz1IH@zp<_Ed5{2s`-)zH!
+@j#M!%3{N7;P?9q)KAoceUoQ*HlS@(CM+Id^tgaM6*n8MTYMK8c=d{eV6S=y3l9>7K~qOrMTOJri!-v
+kJ?^X6dI-hPve$CoDzdp;_ldabZF%g?p=^)iuF(D6(DOHq%EPKIV(xy>y_Kd+HIhZSjSEA&G_*p;RE<
+46Q&UOXqb(25LfT`?os)0k!SIqsZuE(Ej7MAfs7NWUO-(d%CtA_Cm-oCRw8wgIAKZC|dk=QVE4s>7T;
+9fP5=)|EoO0W7^r;}mi(+Z0^pMtxKO#)X%R?-;=foXiR%w&d&RABczZwTS%F@NM^Ci(-u!N}5n^I`Cs
+qzCQv&xaew|QqHrJm#Y2HV_4-VX=gBu@_^IW64E$<;$#XSlvSuE^z5QUY(-+pKXJB-Obeec2t)|JW<q
+{x3%oUsYXS0&AZ>zZ#~`(YSwj#GA?c?$4hKIB1eXG4|VRGlxOT1H2Moh#=PvgMJ51{0pD+&%laA3G_$
+kSt6hPzRe9jCx+u$Uk|u1bON)#z*E6unBt8ID4_>@vWSJ@JbKpS12zcwXY5aHZh&{jvCzIk?qCA#R-Q
+xErj&rOKwvGC2zo|PIgf-}psH&QeKugsfMElc4;Z$Tg+hR^D{~Ghg}^bySZL9+zjc@7H6#H(x4#NFy0
+LP;55Y-aOr4DEC)qOu<IU6=o@`4{3m;ZJ&Ot649h7$(vF+Nj0p<N6M;lg$eaLJ4s<(hW1%dMcaI}-Y)
+myx5oA`N~Asb(_&GyEy-9diKHV@CQS9$_3Fn+OOowHimvNiq^o2|edyqm7yv0Ts1kw+f7>IKi`p^n6j
+B=cFiNwtd`8T@Ti=&R+R*xQRqnzuueZ5M$xOvi><JRaK;M^Derydz<|(A#ubSe0;D9Jx+?IF^t`%dro
+)lyWR$Il8JV>_l3bBGoS6j&z+Q5ql4mW_v*+1btlWoBY^J7ym)154t$)T-Z#|3Qlgi*-?j6LGR>kgSO
+r_#ZgDn@m<C%gkSPGKP4DKmQ%bRyDr;QI?g)QlNu9BYYK5M`(7O{h3pxVOKEo<t@EKXmx<c!pTKI%YZ
+2En#Y8ft@@(aTjct&antRX>dC8URrB+W|1|WOWhmpmqONO@?=D2&H*fCmCn;WxcBKDL>h>a?E#h!cdE
+SGoYQMVrwqWbLN%7Y9%+&~L8K8Q&RII}PIgI9s4{t|vu2Rk!X!{A?qpjky{PZIWDr|eHN7Wt=B_ScO4
+B?}CEa~ZKP-2hYdHhXwBFGGQif@eo#E+@7IfXr|(sh=ix6{Jan=<TKJI6!d5Bol~#`X){JrI}8wSi*C
+Oui|LZ@RqVf&DDD6>0ej;fPBhnD$g?y0M60JyV1O6E?=(1jmvu1qxBsOP=9s$3poJ=236n(>DX!B$-N
+s=gKkbdc-njGFZ|LiVzHA=*r=rp;j*V48s#c;F>Z4Wvtz319)ehI`hD7$k=oVDC80>PE(VWEw<CIwwg
++TzTz+XEqHQ44m7`cho43bn8KwiUX;)ERJ~jWQ(BvsGnRB=9Hod^Dw<FHtJQ?}y0>OFP+qI10rE7Y5>
+s7%_*0>$4P4R;H!VbnYci#CvsdvVy)k`D_q!BYGa#iM!p=LRRyPBI_!JK1%inpk|uNl;|M{~IY9_x)2
+AKaadd;1{M<W3en4lTK}y<l~}b0#t|8WcIXCNGl{S6il=CBdC;q9b>@xnew_4<$Bs-pTUN^O`9SrInG
++BJ4=hlzHyJTq=QJk;pR3yMA5?4tC3gHP<Oz^^YA>*D?S2uRkQ-;l<|G{;^j?Xl0MtAMPF;XuEp{c#H
+ke+|JP49{axi*K#=w!++nq?AFg&v81WLc&h(nCw;Ty@PwaE_nj@r2!_Ntk|J@IC25vpDGH+*ieYG|?T
+$gQcl6u1C@{kjHOtB}unP`7r@+VvGQkoKnt0%(LIK)zu6m@uHS`n<MH9IkCd05f#l<pEeUO^VBU283B
+q<nJ1&$|Fs!gB+oC2HtlTXXeI_`Kr3oNDZr1;DlgP~dq_NpUjDDDnU2OM(Xa+CAvSg6JT^_{Xgz)Q1m
+hZnV8NIIJv=|R}>_ex6#=+mOnyQaInA&nB_c`deVbZi^pTKCbnu~Jb$&fj#!`Q=p>4qth$VJE}tWvNx
+=fr5$+z@k2n5$?i<H*;0yC&$>E=_8(Fd%EjZp5uwYqdeF|Oz1ZQEcojCGJO11WS#=YV{`E)NFSM{59T
+s<^b4uYbDfg!HQ5$Cc_o1p4v4lgRqKV+MnU4*OI?9W7^l_jIlpg%KPMpq4wLiIdLXZ2@bAw-6yS5Po~
+w!XUez2NsWOkTY8Ai%xj`|dxi0F}+eTj9Z8uWf!t>0PTiJW?(}h8SR81dA-F?tc>>Ne;>}?_GVJ7K};
+3qm4+B=^qBc5k(H4!ardF5KJ`A9B&;i9SZhG~$00&0i_707Gt7FP%PbYt&!ak-FuvD=hAjmVT0Kk^7K
+G^!FpgI90kgDe(X#X|S?V(f0wj^11(lBgNRJ8Ryhq!MP_Wm<e*rDYN#61wyUYD6Ec6$Z<7EnByni<Zg
+bp|#s*+$dMUXuAultg2>ErO_QE{!ocx>xB>VQLPbD-YboaZLxED-@CU>$Yu4D_o>s7L6^IwNlkHa;k~
+Xq8RaS!QPLO=!)C*CJ96BO=PObj9szqzw-RQvjfL~waF&LgO9+pScDM2Vkh67krJmz)@m$}IIWYMfMh
+*t_pl!x`<s)%^co0On2;%G5Mx^X)#^$D+y^0|oG_yAz(}Bc{oxSF%WZQCsA{j->?#tUNra^!^egm_sd
+4&(D^jhNfJiYY%{hOA*4r27mG!aW+f|TMAc-wQo&TJbXyNTysPQc2r6_p{a3uDdSkL9!ZDAIs@^8!8K
+<k`5tUKluWUgZ$4FNA(q)+YW4%RrIWI#6(X!TPmUR5XW1;)jnI%@Te>3kY}+7c)%wvgw*f5<t<kcDda
+USa@=+O<#<+#SQqkx_RNO#aT^mZ9t2qu_(QZ-VAw<C{xxi)o}q<j)*ulYmYB{J6VTakeesvR7BRUead
+vuo$ki!W^5aN3|#&0To1fHisK16ajZyfh3EF6pc=6*u7#QtRIymC#KPD|09I6yA%@uE&=;ykOq0jgv1
+MKAe(6cBry^^=>!?c{UiTGe^n`AnM(%bxJmru~?-ak3&*}Emrwa+!C)LyHtuO@p934{qCR2Ouf#8N{G
+U18_c5s(mxD@ZvxJ11h#bIqlpH|*^*h#x6&sv$_+Ds%(>>1@8zQYA`XDkV%&M%6$bac<D$a@J;dlYHw
+rKSB2TKF$$!&l|4>;FrQZs`!+vi}*x@IQCrkEr4Oq<_4h^lz~PgOVtY<1~X39FAicOHu@eam=?;$_Zq
+^0CbR1P?RZ-A=f5jAyR<p)>&%*rBg2O^oj3<^-&U*<iWOq1Z2Vh@BvIaI-6>M8t|EE6DN>$13Y#Vg<d
+^msB`~Hf6A%Sb8R0v=V5`<4=w=Qeb|{Mg^>r6g%NU^fRF*)WW>O*IiwK9P!x)r^TUw#&Ov#JJb^h_E`
+~7!7K8YR`1>G13Ui3q_$Tb3Zvb|fg6pz&*UO)<L-l_>c2Hm2n?mfcny~}sxv00d>uO90t4H?AY7Eu%n
+AGmf4B9&9!JK%?a0=+#jWaXo!?vxXjs8g#-iD6+yJLSFI`Z$1{cY&TzdQD~p(AkYztM^2$*~5xWA;tX
+vQ$y6E(mb(bM(Ol>>|t8i@_|teRy}uaP`Ow?NDjMtv{y1+E<<DHR)YeRuX!fp*NYuYMOF&#Gvi2V$|k
+&pM>;KCw4`fS5CMt<MY+C6U*BeA*<hTHeRLuHSHG*;km-^Wx4BkVo64baY&51SkaxcIjsj--71XKdgS
+RL`*DUz!sv)QeBs`*-i#NM$*Wh%tA&g$$y_Q^GesJW9<SFMeyVFt=nizbS0_=&+D&nu>hWya#R9nUXS
+7o)`;niO$uIlxJhHp%^Z2l?8X|S4e3wzIAdjh7QMYp%-=7P#IYqsBB&!Kk=rbyie))8c>DiKUeA`h5r
+e6$y&#k)=Y20J{#yu+H^H9XWm)x=XC;jD*&GeA>0Ks}S9>ySE>qn{bP}Su8Rn=k}v@=(RY`sc9TAU$u
+!#3iHM)DN{KcdJ-?Z-a>(mm&SGTG1Q_8hj!1#EYZ;JvYl;GkC!PaptLJl_Dt`ouRMbR{63_?2)N++WH
+UgQ2|bhrk_$zhFq&&-0GBI7%I6kL4O|4i|q*kJyDdQ_Px63}m^Cvk~)4@xC%V#diYbp3Cjn9~z^hvFm
+-<T0O@LCpA9t@oux*$Gv&RS*0zSvO8BLBFfBiGzzzC3jdHL0wt_H%5~9>?Au4OK#u{3xgo`EPX)b_i~
+aL)zigjZQwnrmSQIiM>f*6Xy3jS3Yn4}6)w06uDd5%gxSw@t{p87fRZ|!<?SSp#VzrG=q-qnXdS5p+*
+(1au_P35Gvg>=|KD6tT=JdmbQKPLd7Rm9dFVB}!ym|gg^wcHKsH=?0ZHo%Zdo+goJ+i(UrL<e<`UT6i
+u~)IDn_m5E=34004QXMKTFl|X@AD@=UId!C<j4BT|4y%*V^qi4t^1?N|M=Pmju-zBBd{eeZ*}y?p8rG
+CR)6p;&X5GoGGE|8@_M4Oxcql>E53Hw`OjVET|V;HIexs_-;#f06ngsEEbN$aFl~TybKf2k>N~O7+*y
+HxkO7b08~=Xo*ptDm`D-4XdrPY)kfz0wbCY5eX7Cw&7GlO=D?1v4no#ji^3UKDI0f?y7!DBzJ+pUTWS
+(;z1|I;JcnNJ}b5r36h8iF?f(U6AoJ<m!bO*l^F=TDM$Y9cQU*Y6$6YcT?#&&7{@5sMjrK5oHlP`!Nx
+^Ffyvs?oh%emuj9Ub&O*b5G9J+OC!!g7kyoUF+>6@;-j2JMv)jL6Tn`GgH>vk82x1SgpyKfQ0Dp7I1t
+p2)xX*rA0VkFl?VWzQ%OSOhZ*Ec@OFi(=;Bu=JYBfLLIf8_P|m^inuye%fY#CnE_E!Y|J9T7FW3H40K
+NceDcwYK<MW6;yIIb-NS<1ga&Nk}+<j<oV*Ay~Yl{WNL%=+cw(}C+`3&T#UUoAlf_oppqhG)d#gVzDn
+iHG-U8BPT~F~KQhM=q#tD@(U&*^{}M;&r_iG!FI6O|z?;fF=acrtHsl7ALWWsk3GS)6-N(rNa#$_bil
+*<H2a`0~{#3co)r#)ZwxCZ1Tg00U8$ZJH-dOJW)sRN#)%uF&NxsuP2~829Zu#BKFpe3OD8xk!x;<$&B
+zCZ5LmO<>jIMDY(?xh9HhOVQthI_B!g85J_lj4FE5ZaOEzz3Wr&igK;;`eNk?Ws(UnqA>SZ8d+lvNhv
+HhuCXelhr~Q#{}SacS?iCm95SwpJ3IPx_dzEBxvj+A=vw?J#;C^}J}-$_<--nT6Z+$&QGw<xM;>JaOf
+vT(bsW-T}EaQc_9bnv<BV38u5V*musUSuy7Qp0op5ay+#q&+FYK2MaTJPiXMKUDz6M1&q7)lK*(M_nN
+^6(B6Y@VE{vURf3dUI3u5pxF|M3aQmtRiM)UJcy)<O>XFpR<HMvG;rIU<4*1j^f}|X*bBPwgX}R^WY~
+vgPCB3U)QEYxl_sfdti^`r?l!Xkj;vD_p5n$3UFVPEG2D2<H?C0tIu85fc&wo3O#pG4Pps)Zkq6Jm?^
+%~Cv1X(zJGrYm)U#Sl(d2l8HhR3>>D!M>XH$?*@)C)rzKA;dySe=9qu@qtYxMm>liw49;s`1SOJfg8b
+{2)ajgO>?Fql(p{W5<`dzGTijBSNk1lX>na&12<@dDGhfy~KK?o?}Wl^Q-Z^NV42p;$;%1NQ?6NE_&*
+=dq_47Iz$^yzds(*h8<~<W0xt>E4kC`b&d8<)36fj<-QV&G`1#w(-b9wrKg>0?^ISk9k9W_Ol+!NP91
+9+CGT*SdtVNwyUXcr@e~jAC5@94nkF1G9JIQ#<ODg06PKN7luz81$6PMEb+f{QQ!AjMFx@$!h^T-dx4
+?(L9n$CW!b@Wslg!yD&Z%=KP$ZKqw$i(rak=O7SkaF?U+!18#hyT=evt-`E*PDQl|?4rM63I>yHjPJ)
+;!I(7_ifKq*we%3#H=Pi@`%Gv1A%I3v>AA&_$`5II7-_JwOEi0pRi<yT~`N<%etiEoez7XxW(<GVRcr
+Z$RcV#UX_%L(JTbf}KT?AW8fCXbAD5a2Z43L!q$a1^5Us3^S)bSO&rscE&^*3ezJA0p+w|Z>gU&FJaq
+@1TxGqW)5d$@b>5&)*gY=(Q_pfz$D2m`jlo+;Syk*JcE5PIBbrgU@*f1;0=?)ObV0;z#$%E0mw=J7PL
+HP(1QORXyN{kf)=n6l5e2pVB^n60Nc^ZW`lg(`E!Td*U}~@c$$(-&OwbS`Mm8Ua9<nz&g1%>VIkj*>v
+x8Qd^fJ|h6UEtKQ(}a@d{)PSXgtwLVFEZl*xKyUC{O2f95K#61$bQV;#0W_RsC3?6YiFSnG4s7vAHFB
+1XB;oM=^uyFDSgbg|!aI@a&<OPEF%3ao?MVd<weF)7ieUTKYJZSv=S0T$KkA!U{wV(Dm=T<_R!S_caz
+BbMq~rW{{gcn7+9vfFqh8^W&(+|{h-qr_}uV_C#9er)&UPH8<P<6GjIp00|sCN@hd*#RfB3s2p6#puX
+AoT`HJxO?2ESzFPP?w`>}zl#1kwBmcja1<hN(zB<R{YJ+3cUEi;bb84xXIxpw;ihkV@H;?Uz0nulX7R
+k~PYso>2P$DJ@g`sWBQl{!tId}CQD~gSO;~n;mZW&IJ8;9M3YHIxU~1bsSHS8^kjUY}D55(Ia`CC;sT
+hFG1;7P-BND%S%U^6I9yg?YVO;s)SW?Xwk?HyCMm|jrU~@lN`O7>9UgWAjXHcYz6y=>x4v3S)+XIbS*
+M%#!`L$jBptSuH_XNDoFDja{Wt|LB5mfy1O#CbSu`XWXF?&G-??E8vpV199Lk3Iv?mZCVmk0_kBQwio
+#Vg_nL5d|5L2?(tEgG^s?1s<$O&Gv=kKd@q>=hti@f56?Pkwlq05<kt`(q&=V=p_YAI|w%_QcF4GooM
+C$7SFVmF0Uo;~@t^XkS&6%oDR)WRoVE8`nVMwX)9F2lTXDy7(GBV0%<X9WE%gYrzl7#jrJ%^XM$j8?<
+gtd{661?M=R(>OsIA4<&LNt<WnpvAZ?9)%K>8bu1i#D^)V|{?U$S!ASL;&NS=8RmYa}V~L-z*3PU)1f
+J#ER}gQgE_VO$SDmh~*}ipm{tgW_55?HUvk>%cO$A-A+WLT2qqG5&Sv7M<oAv-`IVO=HSz-;B#eI8U`
+4~nXlc(RDck#v%EYp4#`C}67F_o};(viVhd#)Qbuf%=7vgNGn@pOssL9_DP?V?H4<zky`L)upNhcHb;
+KH~P#j{Tk3i<$MCCOQAHZl+UKj8jwo@&CM(m+}AaA5L_+cV+*GF5|h3(gq-oxBb<qYNn`<%c*ZJT~w5
+Rtn>cAXJY1;gU(;j%>O@Ee1kS`N1II_-Np}B`ig3B>RW~gOV5R6>70Y%=926LN)b^BjGut*X#xW?F#{
+n_{(Vo62x0}u#RM3LA|NXSry%A)XM!f|3o=*Y5ityfKv|+3Mn)LO_57)FZWfkI=94lRyf*+z&<yH6rg
+McHGdD;EcZDZ2gLQ8A0qg%*NE!uqPQ{SZ3QP95nJN-Nk}Bl9(Ep<UHp>J20#OwFU&2GYWqkEiWGFmTL
+|=mzM-TbXuS1iER7-v85*&2OuwOgi&e@HlnELiHfjvH?&!7QuY7cc3u<VS27WmIcR<F>YuIN<Z?g_rS
+M0;TqUaUK80P@=Z1AvB=8~79kmM9boAm1~;Zv)?cM3W4xhxG=4`VS}H#6)`y{gldTuGPe&6f3JK83h4
+Z?}hiXeLUGqao?FTByBXI5~X9ICI)N!WVj7xd**Ej*!b<k`KQkY`RU>O(`SSH^l<*^vq64(I6pp{-%=
+A-_lCsu1}7!i)$&Dr8y{m;x@H^ijygwKib@HYDq@fIzVJ-#4Tas^M2zDtg<TzGLnOI*#%h__W&PAlc{
+nYu#`*Lxxgk%&%l^R0Zix`Nosb)@XBnz87M8cYE#q1jyshE%ZRMRrMj(ZqbBa58d3rqUOjztM%}!kQ^
+>9sU7V!&dd5y{--97M)M0#5b$Ay*ONNrJAx9wAputi;Ssmnke%;`q%OuD1XX^F;oKyLzM+a8(zI<A%s
+Upa(*qMnA@TGySB=X|Jei+0TTs%P9y6BbV#_`w&GSuHYqhg$|NcO_zq_t<#ono2E>Vq?=}x9+P6-(rb
+Mc~{e-lL%e3Wy!RMdR=S{;)9y_WrAwrZTC&^g;!v#==)HS^$Fu3p9r>^CI<W`0Avban`=iRf5w!(n})
+XgTseEWun#S)1VWKdlVvX)XOfc);5yzFdptxpr69zreT0=zF<}%g(LQ1&J;H4%m~;r3(@N++(GcnYDP
+9Yp$X!#O74RrfP4T*@UX8)Q1SVX7CmFzJ@b|t-h!%@g@GfYwTto_#JbPB~PQrsxgf-V?KEO2x8&*7hc
+UQB3c<A>v0uPK8Zc1?PvhnpcyVwtSB5>8CriN9hobT`=p;BF<0~|^PWs<tUufUxd1w26=<LNO3;{ng(
+2yV6hH6*tP4ov|*Ki-A}h6xcE9UjaUenwu#XcV#UT~fuHhXNm5Rtn$*?J=oW)9W)iCb;~%AH0KS1BUo
+OG(|2xzTa?>_(;&#l;b0=pJ35xkKk)NYBS|bnBKfFWNw<O%nxXmt=Ql;fZ#?E;8hQ6(!key0blP8JhW
+!+Rj;h*k3#_v-Q?kc@q7n!Q<x$IFF1HeP=>vQ5>H@Ay5k8ux3UGNvX0tn7LjkE9z2{#FaZ5aj{U20mG
+V`&3T#tNEU4QmAglsO753%858t(g2@beb9NxvNiaeYTzm)aC*wguL?GA}?^Y+?2_6&XpSLDVQ{d%{L{
+Z$)_^T7a7|7oaq=zSue7*PB>Us}`Un7CKrW6>22j|y7~7!-g=Z!Ylj2yQtg4q6X_wMCwF%4l?9BpfQ-
+*?1kF63R;t>S+}eQdz8}Uc0s%L3g`=6qfWtENTM{PrI>iTq9cT5cG6mrKj9k>@K}hJJoY2H5ZDAH>WA
+%PGgSgowl<!2fW&ITZZh)s-PF9ZtP_fySWx(Wx62xQ>nDAzb6uryNYpLW-7gM+ay^7$Jry5yX=J$VM%
+>ir0wGVO!+(A2RrZnJR>=FjaMrGr3un;jr6v@hs|!oB@N$z6@{YdY|RggOUpg5P7WE1+838KW{uWyFQ
+!?zMp&`0Q-5nTk51k-#<rf~LD^chW^4^sEYzDb9Kxo=mT~mY2~cl9(w(ZTe}}BXe;6eH=dSh@APZoRO
+9n3T>sntSGKNy$YIAasR202H$!N~^VK9V8b5Lc1n;YE_bFL9!<@kFNHOoNMOTM(rqvqpS$XSN1VF{Qd
+Olg?#%;x@gL=HoAU<U^KSCs$M_HMp96GL>1$M6@J`<yA$;4o_Ldz;K9!E8ETfQ5NXJcE)J;M6eMn8N1
+rU|h*8qeRU~Snvx}tHA$OGJQY6b~pR?(BGd4)7DISs=r7)>{sp+k)0QQCnO4svW@|>Fu<O0UlM+h%VW
+SbN0X1hrf@V^)ynA5qF^DSfE`I&O$M)mNPg%S*EzBW4A8}!fFS~_8bw8T1?&!YGaPL&UUPhiJXXKXzV
+c|WN~6)9Z(gM=3adO99x%u$FhHpqf)iG^$X}h~SKc(Tb@<UZxiA(u7$^OWI1RS(@7oMb3BQC0HP4Z4N
+K1JkLOl)5x_IZ{AOLG^J@tvN-ZV?q^9O?rEE&L~01mv^-uPl3kq|$>SnAV<KDST9{`R)<?g{Q}c;h#%
+U8+Lf&h8!B4m^vGC|CR$<tq7G91;0Ud{RvKeV5i+C@k?w7fz?G`P0y_WMC{+`?m9xCBxjqIupzx$;sy
+$DZ<meHJx>N%qZ=$ki?5;)U43ribXXGeBUgw_@G+RAm{6CUE%fZR@y?&I_sUcyp;!}TrV`9Jm>9BlXq
+RAii?=)Mley1mTn)Nb-434#l|bI=JAGI6lW7}m!f9}j1}Lm>LWocMma{-XUqs8<6C6zYhX_J5*9<|xE
+TL)T<q^4Bk~<){2j=Me1{o-2QnhxVaDHqjL3JG@pm92@*QUU9mt4$hZ%ndG9uq$#@~UA$ak3WcOWD39
+cKI;$cTK08Gi>dBHv-g-+_$CcbM^aAS3b}X8bc`R0N)P;#)HuZp&hOmR8%zA@xB)X{6f~lOv}FN%)$q
+19LAG-Z=@{svC}A2ev&pJ6u~=+r+F$R3Gl++9C-eHV^T!*&HI|LJ0Tsvl+HFI^ZL7$~SAf>t$k{Un%U
+ECN{b}sm|$k;XI~_iso|k*5Et0)`NIxw(C7&mi_4l&|Nect*b>6Yps4CgvrnL25x?O11sWx7>4~PulE
+&*y<Y3XrT&b=$ZrYhIaD3bXBroS{wu+uau!WMVMl82@0-r#Eh3o(TE6Fv0kFm4Fo1~X5d0)LByPO`GT
+3u4gSlNMhmk~rfiRDz=2ABXK)0WGV=)X*X6Y=rLc+u<z)#@36a}-mFe^;L|C1=(2NK$5Ar>~CTNKX%@
+7PQ-BT~qL$B5U$buhF%2T1U5ahNlGYC9-Ie@el=;;`xe9vo&3M)8ir`VVLK$VAnF4KnEuSVa1(Xe9C;
+j&x+ykcy|_y@w-5Lwesf6k+#GH1bWK>Tg1_e-(8^{z*voOVknEmG%~OynT%Kt};~^!8b5v<p(S}{)9z
+E;WB++%qE;*cse+XMIiI^VjN;cS!<*J2t?_GhfDT6JmN!u4^0f#-EFhkE)RzylQijAU6m?Sdv|$Ft=&
+}9CK38XBRxD~+76TC9_sk!`30TwlDuZu_oFQ$xTn?fexstLWE9tZbj@k9I7Df5%lGERU35X9b2ZiHXP
+U_F<2?x+T#yV+cQ&_lJ@^Qi0cT4$(ws(L<-@iBhp|IZtmD55)xCxGkk7Hb?}=3c1a~$6>2H#?7x_x|k
+)qH|`&_BM3+9E30(9fivyuMv<j+O07e36`dD<5iz$FNn7{JbQleRMH7=K=1^#X)S3Bc)g?R9S$B=U6#
+^12WJ9boz1u?TpXx&of$H3S7<fk~&}BoMR!XaV~KbP(LUh>(YmSgnr0grZPV3Ema*^|of9;(NCcTpdC
+s@M2Zrh|RBVOu!QXXtfvc(6?K75dfnA6o4Sjf40d>G_iewN#F!E2ZjWPv$_X<SA-vi^oCo&5Z=ZF&|l
+4sS?xhA`T=iYRY=&?Jd{0bdN+f(YXHM`qtGGYjQ#*beAyA+K^G}2G#<Z2!LxG3t8hgF7NZV;7~vYg&W
+QW(V_1L2BQG!tVA5kqoC_qvy)gpgf>(Tjiob?pDW3t|2M}|Ld<=Z8WB~v@W;vMdS}x^kVCjNIt`T^pQ
+~yOT9?D6*Hiv{Q7~k~c;R8Q?)rp52!GZcU^i32T^msk+!wfVn7u+dWU|R63N{EaRI3Z?QqiVd<UcW%A
+2kdnX*{w+_lmLTP8y`DHK3FoK7zw2FeI4uOPf{S(ywDUDZ`}^dgCwnau`d<&PzOjK5$+Tl_)`zH!JEv
+wKd(0JV&QHdsD1>CV`|r{Wtk28oMSWMoT2w3-uSn=N{9B27f8EB?_5r>kDM~ip)elXtzV2AY;X6={dh
++`L*aA1+N!B&FumAO;f7AVcoD97=fspGvTCuT;Ai}3N<nKSqNx}1$uVT?ce2L^JF44v&e)Glm|u2#*T
+e=m=20eD_lI^#AS#07wC4@%(!K8SO_|_-_DXLU?DJLlZ;%&*p)7~8EK1N6Mxh+d;T(%%IEfPkLsBS4a
+@4owf)NJg?qCZ*0+tA3bHoWnVf7uC!n(V3mg5HK3*fF4`xc$Wvs^1n&rM?DxqmY~m-}I`&<_A30)>&S
+oPZ+V)ZBSGqagVEF_D~PFpHedTCT9oGzV`3;6#A;UZh=9a6cS85x{#%{I3j$(J*$FKf)6@iev_2=-li
+O#^!K%Pk91U%hBIP$z%f(eV*^=%yM<RV}#Qc3!f$y=8rU36t|zs1&h<4%LUaFpS-L09Oh+>1Pf~q+$Z
+3#d2{#B*Mj&nVu;!sgaNCRevA)%;o{yYJ?Oxkor@2x=b^I8$=IXA`c2<;v;)M2XBGwZwUI<&!2^`4j(
+qb7em;Pe64Ha+0ACfJ@RdAgJq-r{8z*@pro^K!>^XOsH%jhgPBH~c0{_YIkiQz=KN%kKSL6F9!$Urf?
+>DOc0$rfFRlKJWg3XL}pW$1+Z&@bXUd7?A*;rzm?D23QPw9@z@|(Tx>8Mv}rT}KQtg%N3%-`)0h&yB*
+;)=r6i4-3D^vE~<)~-60Ti0rxlZNrk8~)Y@697M*ee9)8(*$_1c02&DdfaC$M0$dmNTs=huOfEqQ0M4
+zQJp(W8%s1AQ8eG`T40Y`7GKGevMBb~+U;Z_qUbZ4T{IH8%Br!`Ej!O1THB1ov3snI=@IUnY_lMm#j%
+_&374!r`gyLOmm9~kvZ!4<{+Wc~&W;d$zcx*A9J#tk<(hEHBkAJZe%Ic$(=qjEDaNkk_5$9GjIoaW<y
+szkJ1~w*L)CNBE0NvfHa_=+vrO&kz3MLn_%Ixx+LiuZdjGUU0Piz_7bISm-zx%<7i_5fWl7+BMW7Mxn
+iHx7q@`aBs-*aRhn&0-qvIepq~NV25VoWq)->NXVy5W+2S!;v+%px<NMU+Yvq!R*Hq;MaP66no;7$PO
+e!O*8cwyPe-_qid7u{mn+ggU8;5oBDPLx;+ag(-Yg>NW*Wn3^)KRYddAJB8Wrf$h{eI&!_@Kkf&Mjmm
+hkxvPiQg(@UHGZx9^M2EaI0ww<(Tlef>gR5LFc)p^9D==h>vD`%1VIjpW_<R@H6(@n#I{o*I>eID&zG
+%U`umI;A7@pUJM?zh<_YyM!iz)Qn7!IcgJ|2-LEPS)bQ2+(&6)L~PRWG5T%KaAYK7Tp?b&h^QmRKwU+
+?3Cs_gE!exVAFIBNKv7EdisM})Q8k4Q<^z#m;iO3!mqc)4H2@<cl0#BMbEVsNarjZ+FWxu!4ZKB`Ujv
+<FMAWsz3QiGgJy+XW*#JEzl$r#M_}wrCoCj4erz|0ym0rF36u{*a<H9sf{n&At8qpaq^oAn)7XM9hCM
+=3mtR{%6kkX6OHW$lv$mfMPsz`~#={6~%Lb{A}(p2A0>s@_!2cKG)<y4%N4!fDEj&hfRfPn8m_odk$<
+WlpmsDfj;c=%fOEeI`huwzH(Xiuk0tSpkyxSgAL^Hxr%>QVh8wwCZT8$fB}#dMnl<NEc@Twy~&cJXqz
+>-=PGj9ZL%&g4{6p5MlnkQ#GoBB2oOkO5W1GOu3PBZ_wN7x`)Xd?J<=o6<BQC%s@mE*Ez?th1A0Xu&N
+H0n9MJC~R*_zOmBS6=l|N5`;tRq6b--n3dUc@#rgR~A*Ixnnsu@0%zhbywerWRmd7J_=IQj#if^}3~O
+h!etiu2AQme+HHxs%Q>Nlar5h9ge;$GG}j$V>Vm0t2{eb-`FGA?*B1<MB7~b$EG3&3i}0An@W%eBJoA
+@qODAe4$40hv-i0>?Es&mRw_9%KJ*>xM7f(pH?{4fgv7Ge>9$23#D^wCspSKdp~Ly5Kw0SlfjiJAxp+
+Ht|lL=rXBm^6YoQ`-Gts$+w(Wo_9Y=kHl4o{o5I_Sz7Ll>@=hh&jWeE~uO$~PUoN)G{cB25=Uyd!o`n
+u>5Cl^(qN8rW0<&f7W9f22@Mz`JB%c@?+sX-TY*OtJvec5R&CE@sWT})Xad?swG$n<kqMMtET(Dw}e3
+Zw~o0VcRrl8J}x*kw3sQSk(s?l=WUUHrxwnPBCcslC)-Y+5XrZ{?1?(Ya~A3|2)jwR}AZoOwMsj<jQu
+;bw8X}2V@N)$X!S%lEx@s9H+&s)CPMgWE56KBcV%y2>2-1=~<No;4dXK5todC#QrgbfLWhRn4IMqQN8
+q~Zz*ntR4sOs94O1!a*9l_;N(%R%&UJTZKHOSxU8Y{X=L^LHdDr|d6zyaX{3{5=|)x(Gc?%|gV<iX*d
+_Uud!I(KhUzi?QU-A11eXa9Z+5jRgn%xIgr9K<lN|+4=el^UuC7&|mq&{Il;1^wk$;xvAK%L;!z|zk;
+gMl&&2*nHC>m!Qt~hA=wvKSaHSS0W+1&;n<!xLCT6A(GhCk8wVP$JadNjd&-cPz(qbE+fI<t^kJQlZB
+!d>F0_x=KIn&&<?oRZ_ElB)-8|QN1*^MDRH9By9U#YOU&<zrOYN4Furx@IDkp*_eK}(oACgUD2e~92>
+l;eEiZqOrGpt35vcDZJyl`lbklf53g~#TTWr~u-hI}5}jY8UmtPtcZ-0}@7yw1tM>#w_0hG@J1JG?y`
++w%Ox8_|Li@xFDRH(9`3MSn0>Xr(z@s71Ym$wyr|I(tgIbDYrKyp-=fH|SdPg_r!(Q4E3~GRR5;^E#L
+DBdTs*o%ZIm8D)PPjc59=8(;O|JuA~X`OQh=;@Z}2UZ#&f5c7}U|1A*yuRQH1Y5E_J`85<{tL7cPO2@
+~mGB3%j8uIB%pAG@y0pYy0Wq^L&=?D|RoJQi`Q5uNoE_j({0B?Z^Jq8eQ0bXEAf&pT8ih=xhLC1*rxj
+rwTybxdj9$3L*V8(38*C`Tg2}#hD2^k=<L#LqkvE<t+zWQ&i?y+GAFnLL{V|4vQ6lj6~3E>D(yd!c+;
+(rOn0;t}O>93%;{o=_|FRAc1rPCP5(1Q{3v-hkN$*Kdi7$WpJp3%CF=Wo~s^h!Ult%JTh=-qWh5tP50
+5>JqMQHUicek~A;zNN&#3+2<c%hyZ)SUL2sF8$*e=<hE5$C<J3{k6WI8N-anWkdSAhEu#=;G_zp36r9
+8VW-5~HXNyg7G@A_><hGaf~f4B>hP#KffQ+#YW>ZtR$&Lxv|yVuRIZJJaz~8u`e-HgY@`=7i1)y_aI-
+wx;^|nQhaSr|$@y8`&Z=dd$T^Z}j6Nd{H(kihWR>^rJ$-akl4iHumRrUl%J2k3KGO?Tpr8w6(~qbm@)
+3>q7yLL2<H$8`lXj2uPQbRYE7Ar}Wa9<FVoFW8<|Qad7(yC;YN}$sviC!En9z}z8-5T1hU{LB8SUvXz
+9TOcYqy?lN;vz9YC`kRS5<CP_-fr*2=Q>ow9OPF`s|QyJqC+-@<LK63&+PMxpU?PNBV9ox2o8(yZhVB
+m@FBJ%>U@Gwe_jYdi8Ktj8^1^uNZj>yl`t2;>B}S{;rs-?6d`@<@|kr=*t60>Cx@lDHQ%36sit;`Nm3
+;On5<3b-8KW$C>9&OM2Tz4fBZ51PI+f06`*{@lR}{hob?rgg=fEpuZZ={tTX>pN);Ptd|_{GJ)eNbi>
+^v%nmOvxkYRchYpf<tzW5HKVpzhDe22d(&fo56y(mQ;0ifh<K59M$`L1mf$gq`&HTb_sgddWw7KfW;q
++utWj7`SDM0CcD4*LCEaB8yJPGiYpKgxjZaY*>^wM`&_L1)jq}^C4&giJVWb0?_1bgK(6=q$nq34W{U
+3jBU__@d2w3^2}KOXy*J~&jO6Q1vP1d~-sks5s+QAm6SV##pU<gB*be;wr7YQXY`8~#)U&Fy90M2(}@
+)!gk;D`Cg=>Zz!snt>hrE;3<1ny>QUT+PuTkXRD~$U7cX<!&FYrkiSgTKD}?i?R|sa?-9T7(!^Ytw3Z
+CoToF1IQ|Vwum$vbzHR<VA^Y1ddY}B;H|xptH=)eS{<{)*`kP(m*9D0FY#Q_5^Cq96`=7nRcZKt>U>r
+qP7xZi$43B|U?UI5n0Jxw!vAo6sNKz5t0xWwXUlT{M6i_m=H}QN7q|i%(onh;IHVH=06B=mj6A4iHMO
+P*SPJ!GO{uyyJrhsJLg4IB24+UEKFtzIZlR#1jLsqP2*1Vhse1r%E<#>h$k;C$?3x)$2&p=v_AYcT3i
+2^VZRMcVS_cZ(!jLS2at%`Pk!1$Nt??26qtBxRgOGbVCfN^uhwW}pWD4aX8RV}!8H~8uU_saKD@RCJ*
+G&Lunp!XT`PWcIpbT;*Heu>??d<JOB=bumh`55Tu7{5BrV1`q{=EZo2_#d`?F*Ck?uD|oKpszmH-}zY
+3SD))^{Quf&9{R#-o-56VI_{pkOfIw99}t5O=D5+l$qAOzSm|r1F}gCeHa}NTXUuzZ%yFVTOi_bA4<<
+6V_$h7^W5H+R4$qr$JKUzDTVMOfahDx$X`=XEdYV}iyJyfI*raC}yo1j|XKGTB=t7*CNN}-9UFo)RyB
+#RUIf`-Wy<CMEE(Z@gVJZLWHJjM-kXmlZ-U{OQ;$rjVnGlbAp|+3Eyq;q=>0ycYH&o>1CA&>hqwVD>^
+$k|+<Li~Q`oMv-TS;`cL9i{XriX1pu)F<^y&VF?@Cr}sgrXu}y4!RQ^`5X<rEMhiaJPE&a29djEFt+d
+isOxQ+|qrVj#un<8?_OOnCLvduF0@Hcp)uF;q6WoO6_zyMkc|@tnehPBeF6Kh#fa)a#K7UpNllli?Og
+vUK-6A>12?MW|I3`oNmqoGbfF}bqazKJpS6IibGKN5t*P3y1(6aQ|Rhetorxm<2Sl=3n^X3eLr_iZ-?
+9RQ69vCHJ=xCyE|-Y=5RNoPHxSr*+9Fh=t^4i9ILe-P32F=+YuQ%LaEB_aP7N|EXbQ{i=k$2!q@Y`-E
+NNkW^chgHckzXLFd;!+1_q3ZQ6Q<DdlSc?3#-3JATVW?Q-5cFT4Kk#;41y_C<AzCs7lo{Yz|wIW0FFg
+p%g4or9^~dO8-=et(0R2T9+bQG&7~UnAuZ7RB)vkI}`zp9z0@I2bh;31`~&qzKRl@AtHN@8Xm2ri9~s
+a;p2@iXg<lD~>>ti0jnzRvo3Gme58vj1~^X!|@nWv$6Y753~|PdQ7^;-deugseedPUBKi%tY6mv30{r
+b-zNB9=c{=MY-H^vtdslvkZU1(ElT%C3FE(Tu%dAO@m=I+dF_ItnhU<clJQyW2Qin=%z~1f^8nSA@2%
+!P&CGvZHT3D7Km3h`cxN?+aQa>`sI323I(Yc-#4FiJ^YTyyiivMhOW~WvL=Q+QfRU<Sx$1o3Ihg&t8f
+D63V^W{N$0P0dNq(#@BA#tOJxDu6e8OiQpH|SZZd?sNyT}9dW}r}B7f{gDkF~eRbMx7gJ_s71d-GPZ<
+U8Z+CCiC9BlHkJomscXbGs5app_&my<HHnl3#_kio}i4uD$?zHz037AMA~9<c&8?s^uidA%6QPpndcv
+On`kHOibt98~Ggil6@}ZKd_yS_vLQ^_?*p&2rfFeiyIfL+<_eaSEbsYC`~R=y41{Ph}i{(dGL>gOECQ
+K3PllL5*I4#%9CUh;yk4}MLSqxv3113ji$wY!`{<pGsbnpRy1@e#sYIfS%U{EG!5c>L38zy&HJ;MoWg
+-mG0J___X^KVKi)Myf2dPW3p5O$c1?BiAnB58ex0GgBO%$`Z$^$p=#GMKb|Y+b<%FYnTfmM`arbnuRr
+Fmn&9mt5F}v*igZhHf$-$*fd4FWa;P0#<eu<=bFX_iquCidW<U1{O39hhnHk<S{2~@HV<@5b@b_Jw0I
+Rv^c&_(ZW{E@ie;dxfm*b*+)dB)KrD&Dr3YT<Z!=CJHx-oyJ<m|;u5%jL1$WU|f?IwYPP#gvrm>)0h%
++~>;WlcxvG6(wWF0$w~{{d4c-?FJQxv9;U1!Jox8tmP)D?#Ez&bh4g`1@{DOR?nHYKO)EdjX~>NXimX
+`=kxizJc$gOu^1(#;5cgr?0Obp<uVML!*qk@@y4HS!gz;!x7v(cpRr|0POwu3C8zCVU3Rs8<1!+X;=*
+pZDSBPi@xZ?DhB{dT5A88iZPCsz2&YD_7pXZdIW3Ik$&3}}b3KR4)6tr)jW9``dU#~xL=ZPOABOm-vO
+eiTLj$wig`CN%J?@jsQQLZ3O7^z(q)WW@P%!C);jK)WxFrOopb^MfKd1LiWQbKY+XZCqAJUd+%X`-|h
+pkkfj<?3x-Cm56$2p79aeHrb1pmtw$6Y(7_iK}0Kg_oNN<HzfAN0-DD=eq*`rFR>zDS6pFp9wxOvBXj
+gF<nHU>F8LaF`%S0;OP*q^Ykfi4+iXz;RHp164(0WgEkwTDK$-3;{UKK&$}=fjs`T(^rOr6lIC^qipp
+CTkDVv4ti+InW$C%5?`B#<oYXmog%~G-=m+&R#B_iH_EKc=?vr<#M;_h)|P;R<|^GuuD!^0KrKvH*UQ
+xyd3o1lZ7AYz?wqS&)$$YAfUJ{w(J!?&r5J!ca{I>0k(c|&>*I0C&tey_N|>4{^QFlpyZteKKe;b)GW
+a)zDL$XB7RsQ&A_<TRKiO&h^FG9T<8B5sYNjK;4B+~;3M&{tZkwRPkBvJiei{Nhyw_+96;8&a0W&H{9
+bNx0YWjG#LqF}qmC8^0aLSu`at_RsfoFl**2Df~v{$c9x7pr98f{4>c4A(fDD^cHaD<izlO{tybHo_!
+xCbZ8bn4<abE16<Qqn`H3XGy{hfKfiPQ*OL6|>v!rk#8uuKR9#(iH!UZOI0EUiT*ljxbTIu+6PF+b(j
+0@?+xB?VaFy0gHKcc?2cg;Ar?G<DU~TZQ}#tV8QN&?o6EN3n#km9$|Z|;@aLldu@j1V%n#H?DfkNmS@
+Gmspvgo?Z#WUt(*Hcekn<>4vy-QI#rKX19uqbvU?g?*E>AhKOkw_UuQ$>A2G`IY8EF;ge0Z)4I!N(6|
+;TO`;?I#NF&1GTX~^kaZ2ns9d#Ay&$keTE}pgNZSHn;2Ci=zmfa<K8HD(2=WitU&~b+B`A<Ydrgl_`Z
+kL6knS{uxDE!!V#wS!i@Iylij~45au|M6ClU}C`S@$57(B?;_O?c0^t!bZ0>=0A*7LgieM;)<Zh_(_B
+xw~6HvuIy$YTF;KZg|zxaEzmXbKowu(b!9G^5$MTvBVz_#B~Wma2<}f@p&EyTh^c@#Oyh;6f+(hPPYF
+H2tM7Wl3(t|u`JF0k%r8H#H_<P(2D>w!#Sn;$%`m`ux`gzhQtM~osvJ&2ilYemD@8WyB|{1Pr8O+wTV
+Ntz-P(5LA>~`^C{P=2S*_Xd)hFQQ<#w7ZbaFO%%{hDy`+X*(Clmf7EEW6uI>FSj26)wH9ei$;)6F7f=
+c_l4i@xQNp+0ZersqRU+t-#-Ctg$(P)P|e{%OP)_Auf`NM`psCG--bmJyggbarTJfI~BX2EkoS}wZ47
+A<&K$XxDTwcKD?kTw(Jq!fp3ZRaS=%;_ZPl;PYo=;WP#B7?a+okX6s$F`YcA~j6pP%MuZ@8As&+<2v$
+MJ8N)hMb|c`{<hUSF5}=7cJE|qiOMnGE;6?DAmYVEsWofs5)moM;q9s*to9?eM!}WpU)X|dyV!M^Zc1
+~F6Zg8C1Td=PdS&RSArkui14~@#PZ^1Xjg@7b~o(okt}X7#Uas}P=n6m^5rc`xnk|?SZkO=j~fe?P&3
+_bSvf#0M~j|SDEX;UJvi!Ot6|{6>B;rm!!eRXNIe|5ZPDFF{K8CO6jmsg=BL|PQJ>1WXb@JePXd)cba
+<d|E)oUu#wbXCd9)d~vCk=}II-@wAknk4m29Tl^nQzIp)sUgkt??YoY~1<Pj7G{ygGqAzJw=hjhw+D!
+)Xsw8w3(>dSTfI_Qe)M5T)u3KU`FfYW!n%m1lPILNkRRrzx@%J=_txR!=-HIMGIqs`GNUKv!W?8V^-_
+24Y}XoOf%TQ9s_d1IJ+SeN%fHR}yBTSH5SK0Uu-yrk`LUy>G+0#yuYzlFsGsRYmpLZqE_R!$zU%R0{3
+~Ju6H%Yi!zd-Lcu5kKusnovrx#FjvmyiQ{f`eL;`MacXRH?`NvgT8e>j+L1qVr|M|X*2Z^d&(C|fXDB
+gSvb)FTex{De62OxbV%=jQg>*v@!jT#=cDQb;C=1LX?&|uO*k%_K`+1X1JPc<|9OFlJz^@m@+1|yIDI
+&9A3~UW`LGZ{9NBb{VikBd0iGh+Z`7KHRh>v0rA4x#3v%9xh`dI!5s<lA-lSD}jhe`T}V(@?FJRiZ=p
+LYDpK>Vv93)rKrrXows1LQanV0r=uzE&a?up<pY$CFtH){^Y&b}a*}SLszFfT2No_RY=&BLJN$S!u~Q
+`A$EM*0Yi;hl!4VR_8@l%5nsV(l7(|Ue1+bV1O>7K<_wQrOcO*kO+TAu~q$(2Fu3R;kD%>EQ<g|UJSI
+GnFIuqR00A&4Er4>e%Y>dLA&;~`9+<V=dM2p1X!-|gp>=Le+;JWPR6K+2of#$6D?tlA}FxQb9?~wxV`
+5XHRAJl;n<Ja_WemhmUMgxtURkY12b>?yS(=D7W%wNh{v)zp23jHvTZG-!u3uOMHDhF!OIj_bT5dF!5
+=>tK~>fY{N+UK63)$$Crxp~G+;0%LWv@Ja}$}Mx1zAPL}m(XT$E{v(55$c6yy7Cm;e7>gTKy~7OqQ>J
+4W*Y-q*{%wqJeC5d*|`IQQJN6Ku~3=45R{6olI!=$P3a%bsvAh)j6HJ?(u#q(Z0Pl7<=OOmz}=BWouC
+bRUUF2e+d%V@}MR99dz`opXH1?oqU2q#ZPYWl8@~oK(5Ci|Do4u+g4B)_BVuIP3)#Pj*tPf<d;g?s}G
+<TX+}VPCQ}~Q>Tb@b{h71W|_UK#~L=&Hz!XYcdpJbnJAM(I78f#CyP*EWTzNr<VF?u`E*_a`Fx%-g~s
+q%m7Xs()hQA`V0<O)XqF%`VF{D)J-Ul^s0<t0Or5d6*l-s`q8@r1*w~*`@e*M~<gmKi?ApHJj@#t$+U
+f_gOPlJB_u_r5oj8Ji<V$OBjbZCeY%|EdRh!<%q~4TjeQu7>GnoA!p{S7_Z=`$H{%p&}1D3%O53xXGl
+Yg^t6WL>B@EW@syT0H4(6@WN6%Rll!vHzc<p{(wmhD_<u=u<G+e*q_J>a;@AM)xD&{sF6AwT!>4Si5)
+&>5m8X^14#c&Nuel@Vvf<h5<#E94#o1d-EN#cJQBV%HsBGAflAd(>fLgE}XrOfxZjjt+f_w!4=Fm-<D
+GcgUvOw8(UW1{fZ#jgIe+k$MG_uKAK!=o9`dHQjj0)3QP_%W9MmM}OLf0wrCI14C^cbimFS*+4}r&dR
+h;=0lQAr!Mc#TK5R;JTOeWpp259?{|;9_FVYD1WGPp!bJn*3}2*T-o3IKI%~U)bDW)qoqeWF`p0W~op
+HXQf{W}UT*(gQIpLxsaX%8Yo$0eEcsL#$Z?<7q1J^-EZ+~5GK7BGgdw!BZi`>YkV;ovF<}iPor%bSOj
+ZJ}9Fiyh#v5y}OW8A}7>~3wU63}hfU5#zT*u@q_J@#)jgs1ZHh}-4^Qh!50clKlB{`9|p*^zx8Zu+tB
+`=315xAyLz&hs76qbP>c6hlxLOv5zB&@_dSB#y!`ic=`XFf>Ww=vU!DMgvJrDh5Rgng)$gFiAwNLOm(
+y|1O|PtZe3p{yqCzDxAnbGd7FCuoN&p#ld(w4YVsk-<ZNz?Vl7Nbcg~zbH)G~78U+Z{LJ43ML<;rkiJ
+mRt|j4>(w%}D3XuJQK?^xV0E;@M){gQjX}&Bqiop!m5)niM@M>A!m7>8Q)e<Z$@4dt!3A3E%mo*m@)E
+DS)ZQrBM4wBD~SMMZeE<J5C0&UGNYc5>%W6k9f3U4Ah?>$rHge5@kVzu%5gRSy#<o6aS^mUK)+w{{9o
+~b4V<~2cA<6S;GhmNqA8}HWarnBTe!0+q=d{Vj9n*Dv-{ISj09lsklhuFq!KdD@5;#4>ZTEAj+9cKDQ
+^WvXIY<<Y+{aCbFP~2HIpvy<7<Jb3=r=PCDAJ*LV@*73re{Ol2uey}{s!N%REUY)vjeVVm7hP<)(KU!
+YD>bJ)i4+EEP_DNgrD1rqKW%SeBt`wf@GO@;m^b3AR(yN5pX4QSvxK($Qj=ZDzui4=+v;jk7LYVn@sp
+>T+U~9|lkK9*ESW-4*pbLynShL4#*AVj8^UW<`poS#-nC|{$G5CWnp3=moU+b(Ei`?9!6Qo)*e!KMnc
+=d1E?4r*rmRIH51C%>er|kT2?|aLGQFiYK}yDMhe?q58m2C}_C_(oev|61`Fs@{-;uCqbCT5r;ksZ-t
+_M`9_=rr@UN|SP=A_&_qe&FEP?d!KSYD8Zqi)_DEq4d+$INZ0?C2gEGO@RO?+ib*=(N>elu#Y7JL801
+uhbw%a>xyk986*Ia$~8$@24jXmyGn-9@Hy`-I05WdChGo9;dN*2}!o~x26-4xJ*2Sjok(A$&{c`jm(&
+(=s7i;p(4#aF6)wKQN!73eaEy5UmP5qEV)9m=JcSq9{%KaM`P+0nsK5QLKt?R@<v|8Ri!nj&YN{iiAl
+k~IWMdlols$Ri8~9thj$@uO_zh0ATv8|;mFor7ar2FQ+)8LuuJJQP~7gg+y$H6VIx-tQG_8Hz~b?N8R
+=*q$oUF;)<(M`34bZ`R;e4+gQANQp+}7{WdFjM`t?F<lXzmayXijJX2H{lN}i<|7$;TGeTR5<WK-tbZ
+hPMdL}mJrs^3TJV&9Ob!tM9p6a%;dj1|7V5H1gbkFIUOalr&h0*GXiyzr0QX=1t0uYWE#5Px%_hdylR
++4(eL&1QMlb+K+v_ttBWGh-X@_6HI@Yt%YJj7v~L`ur<~G*aq%FFRe#1M?GaSWr?zL}Pq(`Oy@PTXQm
+p`|Y49_4LZ1YG%w#<UGT_-#eDcU;h0yK8))nH#VIK+Z2YU2^-LdmpsezU>WufrcLfqGfr1iN(H$E;Y}
+}(DcRG(d$QqqFU$EwAdDNDp7_@_W&NWONnxXOh|_U#SR8M-q{?|CG;)9Aphxyt0^$4f%4qIzp^iHyaO
+H5nDSAH=Xih!rkKx4CI$Sz9X5q5ivv{kzlOl%(!oVqnb<Fe0AIAJ?#_fD`9lK@^ySsQxO028wTF9LI>
+c{C(W}B^fKbwO^5c+95s(n4)9~M+Uw$Zc9iG6<ATS)JC5AVFK(GE))12Mw-e$<-_8{2Y23$VAz+pW=!
+mM)((ZOZVngBm{#A2DC=Z6eNg*HYJon~B~k+`Si!E7bXBQxEe^A@EnU^^mlFHf`3Nkr0z_BPGjFL<;c
+b<~&SEiY|O0#F~U5N6~gC7D4AjDzt6eWav}eG5YqE_koB%uKWJlhlom5j|{qNTS_={;kn@^KfIf%d90
+{M!L{o0aPOYC>o^mt;k~3l-HbQV)i%R(GVd4GV|Bl29#A2ji}5P@ThQ0sQo927yX{5GcRB2Is^!Mv$t
+$V5R!02%+|CD7vXKhalGlSdZ(eW!y~?|8S|0Cb%t@*H=V=_UIV19B;<)pHziGpa9;?H?^g?b{hHUR11
+vN1|GayA++LDmL-I1nw!9sl?_VsAVe`mxVL~9^Y`;A?;-P7bZMfetx?f>}grvdk5*%mL`zL=u_PoL}~
+IQ%Ck`YuYO5CW$ViotLMg$bIXNfaR|ioqEKhbaU{37Gz}W<*n~Li#G)h=zcAk29+SE&?cg6awh}*cym
+2*zXkjwYx3>MgyXCKmbla{V1V8-1as#5Jn)FTcXS*R0N^h8ni_#B@xGdCMuZ%P9oTkSk>FpwIW3Wh0A
+4aOC*?vpb~_9#EF2xAA$lEwpNj72JDj|;4Mi8*at7WVF{=oVN1*#|0+ln`c-}JTg~Vw?6aJ(vcMY21K
+|nuZp%*lula)Yk3k|J&$|ZqjrMP>QPpWBRJ=`tTh%0eGl=Cd2*_lt3dY_W>jDIThJBor{KFWo7eg=U)
+<BZyI3TbVH*TefwFm-52t<lYp!mTDe1Z`rkoJBn{k+8Qw_UjU^QxhLW!axs4gD+2{#Z3A)<A!8y1f?)
+e@}>e*4KA3O(prp7ws;jVo2?5<u*Ed#%2jkDstgavdaV2tsS*E=-yLPY_06)jL&C5!fuC3n>7Nf9bed
+q+87+5Mo`e?@k%jgZrg#mZDx5sHF9>H1z+B1Pc*}Wv#Hn~vL!q&M@BI-TPGqyY<qeL`39P-QMd?)Gs@
+G!w#*L?G8K@=f*l4|nCHOT)=gYu=N{SaN_&Fu+o_+?j>ut!Jeu(thuk2}+jv_?$Gc+t)3XuGhcYxWEU
+007Z=Jl5`@NMWjnmC-8j+oOb)}bt+*_AXZuk~rPvMR!3u4ygkdR{kDdVwl=i%yh_8t^E*BkWKZF%e&=
+G)qej$3l)kyDZF<m3Q~H4v!Q1TU`msxDGo-ZZ;DiZI`rQlFe%mhi-qNKzXaRoC;2*q?&o+ReL!MlnTr
+z0G)nk-eS3difXoLZ5d2sk>~OlQg!`dwOrq@dDVjzQy7u3XH)50{+@iY{nT$uBm(9XD<UkX?kVWC>M*
+>GayWZ$>Cb^)EEh^TWf7P?|E(RVqaYIz{#L&s_1$j;%YhNql3OQ<%;=Ry2QIwls9!v;aQH+($$X4grp
+Qz8y*eimz&I??=%%Rkm(^=dEBA4J7nz9i*7Htp5htJX#E-C(QeaNEs2YLz3=&cdo$!7Dd~tfo~h(9W`
+`#vqYuN(V=sFp^v&)2bQEo?;RR%l#jzUqHMJo3+_hq|VToyao$s_GSk^#ebQV0>6xy_Kq<YBXLV8$m+
+&$r&(X<x#yoOjgZLdP(?l5hNl~+kMCtbU4@=nr8eWLS!1wh|rUH<dn`LCSlPr&*8Nxm1EVekbs8G@l1
+0)=rHqi}{mXbfdYY=KUkz%iWs3N$GO43&ip7ztkhk4iw1gdqX!#cSUpMOPk643G%@n#}@m@QpA3R^V9
+wjq#P{P67G}nSdt9+k_pOfo={H1J()lGmRO1b%G5S>}CMdW_fpDZ4!ZrcVbn0rdM|rdi`y*GWM6HF3V
+m3J4(QA$vRw5Wndo+2cEXeIb#YyFp2yUH18{Dew&Ls^1_(A9miYuU6FWq)jP|w{dHxf`0oNuc9OrLPx
+2XkzG<y|lqjI%M^yz(lRNA?IIdNd*8-N`Z8LV__ac6^>p+He0umtaRh8%GcLRgM&z-~i-!uv3wZvV4e
+cl<U7YJ01fX(JN^LKcy8m*-lMNA$Ms)AC?2a|{UTTUPNWh_KrPj8yzv`BheQjY_VrN=qcYO2upb^YKV
+-WCk@yuUgZ9y@Il;km0=m%z%4ttZ8FW?4m4Uk^OWscrUh`lmJM-uV$r4=lJ!Eki-qf^@T##B9w_&^{c
+~d?L8U-P$#rja-J`C~DBm1kSJyZ`?)-cf|78QS}j~SdfT9IL2?2ClA{7Xpw-Y6c-q`8rhk?)!qpiFOw
+Uiwy%v?DS<MceCkLR`lh&5osl_UG#OzYNloVVE48luR1yugm$=(Le2kk{WSq`iCVO*}8u#r?jdzEK_0
+x??IHPY3WWU9sJn^WDn{Rt-Y;C(NW3zp7CWbwVXm;3c&yknjz94DuZ!VRwAJq@157d~JJ7EufmGleKX
+Bp>jl<x0*TUmxUnr>u4j@~!4b|`nxQCKo$26j*168>-b{`<~f?fdhoG-HTO5|EG;ZPO#-Rb=eP!O8Bq
+gQR|?EV*y66(4tK0sMYQe0e)MoQz!bVTZ=))wwqO0*sM=i8a1>oo1yu?~TeA%G*T8W4G_N=)on}B!^&
+fzrDH-l}@}pjvU}ib08077WKO*3!2D-J%YPr%7SGbW_O!qFXisV9II^LUQ<*FI$Y~hRffS5@+mhV8)@
+@Q1YDHf_xb+ikq?Ul6rgK}pH3U?UZE$NCQodH>BB{I;ramuwiqBsyHADbv2VG1w;4vsaynGGa)VPm@J
+@P^BU252T)QuU-Ocs|PhK4cCv+bjD+n0lQ9R1<6eEoYPu#M@y<tWB9+%3q%V8gfwSy6L$k}tF<gZdVK
+jWKR=EYlL7hdD^H_`nsz|8;TIsXKoKRw@PB*h2<!(kYvNF0HeM3g}o3dJc5rZ60%Xk_^n{iRr2^43F0
+!5kV919MOSf7q(HwzmFb0wC&wk4skiF%bUe76s5>TP0(FbpZ-EnhTPJ3DApKP>@+=V3O69011KRSG-!
+Nqr}f>q4-MSB!17<xpY8aNCD{qjLA^|GnaIEIZL{Vz%3X}ul@<32LgjcGl9VlA-Ss8P^&m_3f_J}aAX
+1GUrOl;tDEWVo15v8y**>KY4xKzERXE#e6NB!`6?b3xW|7NkUl2u_#s%Peug$>dEF|a^olWe1~T73sz
+q|kPcZeyDMeQhea|@GwP2n<Y?~rM)S(~eekF%>KIVP}@iQjYL+}#M1{Z&{EROFMIqObu!y#`XZQn-M*
+r!o`cMZVnZ`tUZGA3lX!5_VgRT$=HgjDg3ka}$2bYYChApAoEZ5W#(Y`8aWQAPyfc8=lNj?Pc*ad2zF
+y0%MV_mG&btN7<rPv;B62c1@Po^y9F)$yQ%*~5Fy(q1B5=VPLEu3_)@)N4sMpOsHs`rFX>^u8(%#UZG
+7n~mW=&{t{WQ&jV+Xh2WC;M{D~y|!|*tK6CqW5#*KjF%QaNj+v*gZZH2$~xb!_Z`eiQY-dU>yU`3H49
+`wN=4K`ka9<tZ&G(wv7**o<}?sB9G~XrrZM1<E5a(2569(#IKC!5VWgwoq)*R#5rc{s=W+X!)$_|^bC
+igEP?l^V(JH#69eRS(k5;=0<(+RFenihn9XMQTpV9(>!xQ_+A@T9#1{Bv%PKn#}<Gs3WUY6+V3oETZO
+R7O%YO3ktW<o1rqcbv_*nwKK3Og{DGd~LNx-W?`9Rq_4S?Hhc{B^N>SqvNR^3cc3G={FPSvlu{;?8oU
+oHAT%(%bmV=cHnfF1^!e?0yzKZ*OL|bSrwuwXY51G~iWQ2)aVPpwyru95IPFiE8D1<j~rao6@S7wrgG
+{8JjfPn!40A^Fg^@^HRe&0Y_Zf@WeBPTqLNlnZ0ocbHfTQ^FAxjR`8lBI^2;gvoX4~eC_mrEmi$E?R-
+hYo`*_nOwsiT+8(bH3I(r{u{3K-@WGvb*nW7#y31Z?MJdH+Ij}3&pY7c>82Og>CPqYf_K9w7GUm@owc
+}H06dyisZ{Yb!2%%BQDlOaoA-gG2-HP2rU-eD8Rk*l84<;q1H`PwAt&qJ~MP*zw?V!!R8bs2>NRH9|G
+Rl6)oE+|D0~{$=1O$-Q)x(di{9o<+&A<D7({F!1;QMOq@(+x{G>j9>0*p9};s{RC6ba+>l3r2-L%?h8
+^Uvj28Z_zYH&xNKv5m!GLL#F;H5JA{;WJEuq8N@YDDx#CQg8DRI2eYYP=KXN2Faj+WVa-)3$iVVAK=-
++ARz?m^($=<bl88YEuVo}?6ML%1alHVaU}#jc!C7}JqQlU#t8v{HbH=}JQ9KYa@k>7C(vUF!LJA!RCA
+NnW_-cv<y6?}QyxdZ1jGu=NnF3pNm#7nG4}Qlpo7^lo#QVL!rJ2hWQ_^p-U&MV$Z4yU=O23Wt;4^~L`
+;$Fz6|Rz?LKhssjm7>&?*oiadYS)MSQKkRaW;-HJL9KGn^Q-;@?Si>o(N5EXjm(BD@Rxr%CJG>GC~mU
+`0}ydn7*+0ID~Q6bOi<oMg3&JZT29Qhy7Q%;yoRd)D3vzRk-8+1Ki{^|r%{s;n77Fg$&AS#F!i<2lB#
+=?B{M;w$IDB6OVdwnc=c1-XUBGaL5Kn8Og*q$(=NJ<@M4_Kvyc!&S`rP|{M1oi2ti4!RYH=thS{3*QF
+5dYpBrirJ{~hqIfbBh_oPg=ClXR8yu>T~)<NAImB{k=i&t6OPrczq1!<$5JC)ABM=}6G+TO=aS59FsA
+P9WGnVn+6R)CqO);M-9Q!u=E5WMgx`scl+7mtDY(7th<LEku{nG|GUe%eDiJ(ym{jfDSIQ{HLzfstiO
+md=rkl-)wjP`LQ593{1?=rsGIftNTX$2}Cm51<NjDuN9*5J%me1pJefrk$y#U7{=UYMaGb2qi*vC7?{
+|iPLNc?cj=rQ`S*VtEzMf+;LHu&W}wuVyu%mPx}8AALa{|Ux^zMAP>OY*@VH`nksAPbwe!!wsv`{bp(
+jphCn%YYNoMzOf7jxp92Vok;~Pz_-ak*oHjaA0<dwXZ5a-CiXf&U>Pz*#psEnc;|;865PX9!uDjI9kf
+hD`;rWB%Vf}+o#{rvb$4;URko&B*99=a(cr#@2-_T92|mps7)B<^?BUy`eZv^Ui;JCuplWG4-yRv12N
+T7bSsngN^_$az^bVW*O~80A7<+9hC1=hHCRADy2f&Q^3bx1iqc@AIU3ZgA=#3{M@c^ls+P~uv=6O$nn
+oZH4=a4kjklv&lHAdpIMVjETSPIYdx2>l2UTisV*c`(DBbjp;#Oy6&9s>4`?)e+A3LPF9(K<8sLZ%Rb
+hzZ4;sCLt?vaG3XnGV>Q=9T~w@!aOW3-)bX`GL}XL5jA2E;<b9!QBe_u;YpzWui!6T`oE=(qguyTd+%
+8-t(>LoFE}MPL}iEI%+}$@fr_WJrRBF>1;8h%f!?fO<9tfNjaW7DQPfjer4EAggE>P~l7gu8<kvg2%*
+{*p`IAJo!yolUPfb*(&+L;GjQAtYuOP1GP+yUgbH%Rp&GQ9r;<S5neHDIbc~E5FMew+~4v#VE|h_LqO
+qlNgtU{!rNfDF9EwPZ<%FliFVZ~A=i#%6ocyRk|@H|lF<GV+iZYs7wucJXK9-$H^Ylc-Bd$pcKTSb;g
+`uCm;wBwy{3J%*MM`BBCU4yMIyWNl8&9?tNQ}EOf=(9>BYJq2m=X~<+z_pii(uK-t)C3C#zn-OxbXh=
+YDlFSzfQ(fNzNSG~Kru!0+_0KT*b<Wi><KTT5DX%3WDSV$k42>A|q#NjiQQuS|q^LXMxPVq9Rn{yM%b
+kEG2=o|7Z5n0{|O%X1bU^Wh~g@#ftyx`;BbLKY|NWaX!FBeVu~R<*&{DyPP|i})V8zX)YU{b6ewg@F3
+D)>rcdf7J|@PU3Yy>X=tsvLUJE37Y29&S>{#*0v|ZfUR~f-k`l+6Z}PHPCCk-lMQzA_QJf^lpyW&Adn
+xYW+2)@)puo?+1fr8%vQZza9=N|L@}8NdX_isHj_-OVG{pr>q@rfr{CXhFUK>K>z43xCq<6>Y=#3R-Q
+udinX|q(p8aM!pQH(*xbuwI(PJ;2h$AD=TdRnVhn-I{UfAGpGW2ad#8H6JqRU=xceJ6Nhmt~kwu}#EC
+_rI)JT}kj)|a)9Z%#}@Y$vw<AT*i0u>jXkYM3j@3Hq+!c?^D#v9_;|(4ur%Cee+gSF*o-a&BG()$var
+exIP{vMc9Enz;C8QG>P%u>E}_JYS4HO-)|gT!J(8*)o-l;E)VdRzsd}+r6l0al*Ap*5x4<KOCDfa%*o
+}Wc;GD(2v5w-w-2~J3WfA!VQVVW~2xI4zZ(bvF+nL6GzgoF5-?Of}KmJY<-`E!|n+xU|taF(a=~-5NV
+rx-56wd_Gqf?p63^2(U1E}Z6*`G9B|~UGckP-WMwYsXtMO)i5o;<?6cC;>a|D8`?g8Y*uO+VsCp7-5R
+fYDr1qBc-i4F#1UYil#PesCB~j}Vs1I*cE~+?0c6Q$B8wZ{*o(Gfu{B*JC_KboS+DEi|wq6QzO}#L`n
+aMOR*Z3h`@+{J#K%+X8H=Wb*Zy^uMi}s2G<UfHAz;}M%_X`C27f$gRiT*hJ$21E;aEw7Hl%hx!qbYRx
+!7NyWkuXEyBt?^7A`!agK%hN`f)R0Mebg^G$&zlR5y)qjXZw;H0R>4g75*g>;cN9TS@8;qfyy+#CS6O
+)lde7!OS%OtA0m)+Eol*ffn<kS5HI=pH2MNm%*tS))=_qJZ6Gdr6iI`{EC+{c@e*DqqsSO2W0Mi!P@@
+ERr*!qZSPmpt(aneiB~&B>`6Leia!`r~S;hDVGg@%TGv9|vk6j)p3kvyxi+)XdiM}d3<_nN~;DY}PT=
+;AO_He-R7F6Pnu<LvViw~?go=oX9zhQ-Wd28E2E2-rIM%P8cbc)po)%;<bAwB-oxBF-kLT@0N{BscfO
+!$5piY1vRMU;efQ0g<Xz60y}5#E6n{jp#7W4jLe+^-uP4AG6|gvp*(yU2x9EG%h4!aGZ`)uZ;I+Y&;)
+hY#M)9&MxPy~6-iy_O=Emm-|G?5ymbH#RpN^FqMWsXg6~oEm$s%sw}Vf)|r`%k9q6<+Ul>DkzB~qES$
+;OKurFo|~S>k~_g296s!LrIxTmD-XJJ=WmSn$_vEqbOdo3{MET*lJw!E`r+m*Lm7Kf^7}pQ@R(^m6gQ
+-Ze@q(Z&lrMt?X=@d{M;XO_Iw`6wv*f>%C8x7)t)imRd@)6g{Wuugd&KGcahKI<c!ftH8(`U@8wsDkI
+{X1MVFLSILyXnYOHO0Rasrl3M48;#eu?$OJ|PX=}l<G-N7}=NK6i{(^6*AjDb3T^y_e0F%(()Y1Q!0B
+}RGoQ6UtMYlV=LWbPi?$Ov2`ll4}%DrmW%->T-jHv+H1T9F}DlRE|4n#*}7kiv1S!>NWCe=kjR>EHY-
+zj=3FtvT^c_$-@mwQT!M^yr&8lW&aij(LQhN$Bu;C-zY25!KnxhfucqNccY6<b7r56onvceD7tLxAG-
+Xsj_KvD*qO6L4WETYKP#`hcim!67kZP54%JJ8y6*{;C@w`Bgy7dWHQVlrRtG7RnSqfR6G&e`|@qx*Tz
+&HWF$ceYaSQa3+c*#4*53I9))qwZW)(xJlRWkx~QK#cSsLgsKDVFQ=QtvyEH5gYOS6ac%T$h^^dt}_e
+Nf_uJFo7S9K6Xr5{bT&9M6+ygJ$@Ki;6L_zbz5-zgd*opYw66Ld?a#$0ai`4pVZ_%aQsYT|r($5B(c9
+;`<7k&Y@aqam1`1>GO9&dkGPPX1$*;tPJc*bRDlxrL@Uuu2btEcVG*&AbScr`$4AihL5P|2)$$VPq)8
+Ot|i-&o-6&rx9m&dEl<cY>%~CW)gn;GMhWul#jpOIduNV|6ys4by^@%tkbH$x7V@)cnIc;s_-|td^Kt
+Rn;rh+-|KMx$A85C_>adw{$qjM|0Vuke?wuI`0djfxU$a6-~RD`0ps)k`R`@(+n9oFZ?AX%<NqGhYhC
+{1-^=&2)(h(X#{S#?{6BxpF#kWZsBhvmr;oFJHF@>h;ogpAlHT9)TjZBh)k_MvWRXA_lLomb!1N3>1V
+Np1)xM4>K(rxNyU+0Jv~>vqz%VuTJ4}P@5l>b_&NZ=&QP49)2v7|LblVK%z)J*>U|?K3`I&SLjsq(yp
+qIV+ej+OoDPHTeGz^SIm(U=K!E2NTAwx`qJR1RtHRzQtIWo0s=`8s-L4(hNt#j590VuTLFM|bP15&hM
+|I4ZB{v~b=0e)+}O%v)G`xrV^{pqwo?*fogv~%yvlXHxk`o19Uhdj+ausJ34yB=5#pHNx6qR%3Y?Poy
+SPbgk~e2zE-Rz00A;sIn6Yn^wRcE=N;A6=C*++cFwtT44Db8UmU${{fDX)c&P5l`DjEZcPfj;md?OFG
+4{r`3i%_Oj4}^JEEAjzE^}{m&M?r=g^AhiG?QmQ{fkA{lwW*3O+no8$C{m+F^qGg`>|q%~$Z$y-$7ur
+Dx*`fa13?3Vx2)8ReN5rp#(D+9iXJsda85PFYyL#cIytS=O?=+XuO&Vx=6+gY@eh^H=Hmj7N){X3qL!
+Pb^-99LU`HQtd0_RKj|&z&mq>0Y?ghjfo)7Z=TDfqpllNRDuoRNa-9unt6D%e4TW@6c1{R=@V=ndu^6
+-)M7u=siFFEKk%EJ$uA*;p{B@_Sb1G$EckmiUagK<HN6lqR%nWTq_dN7y`;YGb~Xl$j=lb*hx|N>R}D
+b&Fthc%GGHBHT7ADx5JX0-Olzj5IcX~)pTKaaOFK8B47fCXayB%>1KC>T{1kLvo1)i!e~iSI>8NGM(m
+Xh+PMRUO4WlTJ#`xy50CLvy0lA%<1j*nf@r^^M6x-0q&TvN2je{AncMQz?J|&ss$DGSKu`DlBwPtz@N
+v;?dScdZHr<}Mp5US3uAS)_Ih7o{BL^`&T#&209rqh-=1ERU?8lCf_T35f!YET}hEuz8PHX$1k}<{T2
+&4{9Ls$h}qEr?cPuzo<`SIxE13pQYL^3WKYCks;o}uo;KAw-diqBJ@XwR#sc_#vaA~;E>59|5olILH(
+P@4I9v(tqb*Wa2%$a?m!+G?Wf@B)fR+mM|d`bDrI!;4xo;N-)*kJPrv;nRekt5ZUw0iUoI1yvWm#qtg
+nHXAp(V@tFbghDv0wlo^~E8>~cdC9z0uQ&aTLOFrUJ;fVsUkS_EA4I8uGV##7;^p=W9@Pdp)<_Yyb^g
+3^=;n12vy03hTg<)%-A>-1o7Og|iQ8S4(|g2$Afy@MU|W*wi{^?>!jLmD<5dy&!(rN0T|M6}_aeX4&n
+Ks~J*_<?y4shWW(ZETV2h?CEEMYgmeMjCe&xZ{G?~!FrswUHs@!m~w$F}3FIPgn!PIe66nM<P+RU~^s
+u&Mjo%S^wvUW#7P4B6PlG&W8`@4zB|NVi%e+BgZ27p`(mlHNCp8r?H2meiL`<KE5^p{}*7?L4YrVkE^
+(CBKo&8#BT3ltM8_EI4jkPgw`Vfbs`$yFO=Il&Sm05O*YR2ImUcob5A0+mL<){?<N{h3@LeA!L|1InL
+?ui&di`C2TeQqcB=QDFB?QLD2g16spN)B(uIAvhfwgL(nE5}l~E;k@jV5nu~KfkrfN@yI~+os5>~<(D
+;u1E|!__AkSP@$&yhm;hw*{uCy7#2vW)8ZJC}@9bScvR3?s{*}<+b7TO085)%A8id#=0BEUK1RzRrdY
+{%g0irPm$j0XDgh6jj<r9$aMn5(JrIxQQ*jLvJ`a2iwtLp{*oeTEW^@9G+1^eoHK|i}-e^u25Vvl@hU
+MZnEna1>N0^YdDp4=a#cCKGl#mShlzso158*i!33n{8D!ACE3aG6oJYnnkSqSW}KPf)WIPF%v2caJ|z
+_I?4R$qtjMf8v8b^YKY`5BJRuIa&_fsG9QJ`1M&#3dq8)%p}Rr>3qFn265Yv8-#U)*eYIQIm|A_ytel
+z8<PYUJhbjsSh=~N%x2u~BA9Jd=s2SJ@op!jE4f-sN3lMOu7+{qa}zK;fd)#S9Ee*#=kvBh&d~)+apX
+)8_F38Dhn)|pa`$MS@gYhXr%}Z_!EQF5U&*OH_;t1fXo(dR)2+kl8TGg(Q_J1+DK+M!l8{M5I_T9+EP
+qhY?Teb)BOTs3hv^#j@S*B*d~w|8*&mK`&L(B<xePnM2-=3E8;O?o_O3cNWeGeF-|?i6hQLh$`!iQeP
+?&(eq%_NFUf(knGxN?b_mWR(FWrx*(v1D>wePLyf)?JKDdTj5L|%W~?$yNH`T)69N8?;|td6SZN~RK%
+v?eRpuasNN1Wb;j*VdX*x*qP^+1A!F5r@<o8@sS2H@-v>eHX}y2~W999-2DvNlXuQ{G@mNW74Cz1%vy
+~d#RdjXl}UcJ&|5eKB4ulKnAf>H^ok2B?_fgtMKVQ?INU6M#0AP*2S;qD3%Q4QPx^iw@>?gL3dKy%g`
+W+>Sf<Cub`M8DEoS3tY+=$19Pmfl0WYC-6P-MbW+Sz);vGOaA#0=!5jl>dJJ3LhGy){WWgnn<6M$GlC
+HJ7xyZ@$xxHDNlCiEsMV#)@l@pb}sjC9&`M>R2U@^u1_P_nMLqGIT)?cu@uk=&?!ZF|Jr2J|B?{FEz8
+5DG%NrZ+;hCxvJOEFp$2k@3KE9@blIKL#(S-1+uu8gQG`#qyUroSY`4EA*+ng*FPMgtxbaBY6;WJm7-
+0JTz%aRP(^*xDjsR_-xMfkB$Dp&E?MCV)AdG610$(1k;gvtudX6))>aSB^AHfYkz%$q=xn(-5=*fRz=
+!x@zH|hX7t9z*Hk%h1(V&XMU-ZCJmqwO@3=ctE}F$vUouJYBPNm{u|=yTW$%h#q`QlewcxIQx-u$9Ua
+e)r}!zq&pfQ-E${j!z#{^RN=}Z@)fb9W_yrhV${*vaf`S-A{xSpudAGCpe%pnUer!Mpw(vdg`CXP|Z9
+e=z%)Qr=quSanc+XR;d!2KHH#?#)@J1Nn1$TG}0YVNCPv1<kGApZA)~Z_n?vCz=%1R}|%!$w(<8$LvM
+M*K7Z+W3>s#okmpNHz8%-SHxcG-t2X0Lbcl7Esef!?)CZX54eUhZe%s6DR%`;g8yb?&w7bvx!qP2<Ja
+m5zggw*}&$8Hjy)`PT=!r6G#c?UAE-O}u!`g|5X-bO~9$cIZs*al778Po_CNwn6sN#~>?(R%-+XW`av
+UqgP@BrRkMZ6oRjL7D0W}JnpX|il}#b#JhZa+=fXUvhGysk7`34D=a+quT9>c&e|?`9XA<7rUt#Ehna
+P(ZFt{u54*3197WpD@t!+AyrbKb))h`|aVL@8l_tA6Be6RBIk~CRs|Xb>ODB(Kx!cSIR*Bp6j5WTR>W
+|&_;fR9tYOV(QP;cd~eh$~P;jb5~&uPiO<-zpQa>!_}hh||qDBY!n7gI;C-%Y_z+qhoW9g1Z*OrY$7D
+RBpfJ$bMDEA<oRA_m2Q<L~(cGK9k3J>5bQ%tAUYXAPdnYj(BsAm`d;bL1FLzIR6(9Y=%5M!go5!?Wn}
+TCK4QjJ_b_K1ULBGaoe3cDctKh7Hrqc%K*frbo5t_VVH0SCoA^n!a%n*pXJl<0i<k<7pt77bMANt1o;
+(=uVb)I`TA|>CuU`<GGPH*RwFQHeR&ppsE&9i+x!4BD+<Ita^~@#n_*qL?)QJplK{Tz=vdVsF%y$u6@
+{`TjF-WlE~Xc`TT0mX_VD`n#75!rI~UHEQ@%ZCj+Wf1}jiLQ63wVK39cs>#aaWBixct@>RopV=G34es
+`vJ`QYN)d>215*>0fY`_=nGyTtypGNy20Gf;DN8vj7zR=H;jIW9=y*3lOJw=yQM^H=*qU}vZL)+j=t$
+Gyi=T-B`ND4{K`&8hX8y>nFsdPXdKAmdpl`si2y>6q4JGBYO!XL;t&&+P5{KZEc8TO8%<?wYIGEx(vH
+IN{KALb2GfY>B)Mn7|#YtxL@TE*0>m%)?~ZTgR{*rdJ_U0_r|a^2ie#tEuK}wje)RIHe#xalhZlJky7
+1#Kfw2!*NR_PCKRUu!^d7ag<gCqgS^Rw>(FNI-TcJXe0>%S%|S6U#=H4kN*6er7^wZthK*OWQ$`)PP3
+6!8s~!aPb#c<8p#JqSMol3^$;Ds^E{M}S4zB}6!(=m8wY<(gaJ5FskILmb;%v=c(S9y!8I+mLr%8xlY
+HvsL(Qjh^W3}!NZ$KbzhsG{Rs^Yxr{il7bD1Nwl-+5}wmdvkVupi-d<^CNH5OAA=dZ>mFWS_%mu&)3)
+Nb5s8ztq`TYO-H#h6GmgBc9hv=w~X*RQ>hi&Cvf;YBjx4K7LcycJVJimdZ(nZhwY39r}k?d4Hi%LD&%
+t7P(ux;^h!W0WB7(Y!97)00P%ATS22eLSv@UGhR<Q9Yh=3}VphVsVVRWl)DaU|;9EUhZc0<eJOU?^Vq
+q@%lr&l^f+?J0(0eu(dz$?z;((=Sb{(NYBZpFf$o#uZ24km~~=3wNe&BekvlAyO}<$FAMK+pYa~9(Df
+Y&AKCI<bcW8Ao|_Sh4Uy}f-R>e^BjLV{qP;q}=|VrBB5pb<{E~@1gG(W)U+dvy4YvUq*bU2DGly=Dj|
+XIDG<Ppr{1v9l0+r6z)q&~r`5e;i2umfmsR<H~OYMI^*W6Px6t{m%*nW4^-znRVM|>u21O>wwjll$lA
+QZtc1o>rRd;x>{E|C7ffO_k!1TTOtT@DJw2om)11QpmcsAfjK=4?#5YRwXWj4ekL1<(!2Qc&a!)Jh7#
+)))+gcM=MuB;qjOhDZ)l)W7Cz%Q0jI!a2#>O>LoVAb7)o%nQ)gU~NvikTHw`>H0XiR+GVi!hv-9a;+d
+Qfky^j14%^=2C>k`g;Os4E&n}d8$iki8NPEi)td%iM_fvW?_VavJD(K$A&?Aj!_|-FRnm7(2HJ2rpSm
+HllM#JpQ4mOwNR_zBPM?_4N3j?CrbRcau-UA|UO#UeA?K$C0{?b2&<~{UtI_<1w?UsZ=cc`^l#GcRWr
+n}XT4;TXp0)*TZgMM2d%Y%uSqY|RyTgTm#Bmcg21uIW@?{Vg(U9=l@ht2nfz*{&V4f!;(VKv-9I>A}V
+#~U^GRP;-Es5yxBp;jbBvR=Oiag3rO{Lvz`+{;%xVy4QNXqVO%Pp%ODe;xRAs%NPKW`{Z^Rew>$Q!kP
+f*qRlPK5+@DH}9`o<ugHcp=B%dQBl!Gx4RmKV6>0AyG0cZg$EXcTc!JS5$<Na7ErmrmLq+fk@WdQRrn
+lG9LRFT0c8Hx>!G<XTRqQ)PEZC#b9FL*%aw(j`Ta4NIPnuyxdJ79gFw@6`CRH{fke%q@M)otl){jziz
+N;_xLL~?Cq3ONRKbulsI15M0~nO@-Wx2OrG((YHp#7Rs;*Ku2n@JTdFp$!a+E4PvJh3n_dfg*1Kw~Qi
+*8R!L8HYf{sW}ctl9Cd=~jpgNzGT-HAQByG0RCAe-9nUZU7<H+dlIQ|n$9DyLmvR4vS{aGMm4nP$I~#
++awoP3kC+;&FG!y7VR<x`~|7)m(3i`TT?tVN|w{BGQw-A1<9;%BigAFD7ze_uh1q<UU>5E1}wyo_7vA
+ayYFjeVb)PW1zbO=1gjto08LA(s{h9waq?e6n?e@X2`W6S7au|H!&q4;<UwQOwODZB_=L3E#)jYemzV
+QcHk{ivE1Ea+Qa)$++$hNsQQ3ikt6-V+aN&P{u|x~Nu*$og~L7SKDFqF)TMAX_BDBIs=x8Jul9xBcm4
+<7Cj1j`gWkk{|L437VwJsI6>JBe;61MSBi{F1iLmzr!knU{m6P@y35ug34Jwjn$(3a$x&>UQ$~Sp%k3
+AG>N_KWc<mz4Wfx9-5Mnww7NawJN%i9Cb?xHNbZdQ#43;TFgv;D*^w+WAeW*u*L2AcR=j7-dn8VKj&Y
+TW%G^3inV@AB?;Xfjt}wmNnQm%E4%XLU?*NocWJ*|f@IJdmLcp*sv`Z}9-5LT)eQZSv5E)m=;Z*emD5
+d468>%xKR-)8eJO<)-b^?6NCA4aiF;_SYCv?}hkup5|gvY$^9p&1gt5p(s)eT-=+UQ=99tL*P3p4O*I
+<4M90wBO?y$QAz~)L_>+wEKeY_X~u9n73Z@bP(I>6JSsoeVI2u^vgcS5;f%d0%Xnzt&=5gV<t#N1Ja^
+E;Vgw@b)xGWuh7ykW*>3%2E{uv90ujmiTR<O(UVdFEi-Mb{)+hUec09|8LRE9<<+cOm?Blwv?!tCw1_
+9n}-23wxPMP%D?@V=;Jj+xcNxk8p5v(RdUf2icIZo1sBga0{#4ThQ{Xt|=#^AR~U&P$Sygs?8NG?04N
+r87jZONx;5u;}9jQ!Ol;{GPtk`<G;Bx<4!bYzb$e7Dgu$u{EtrQ)W`5;o6V2h-xNCYcP)DSH~4w|S;Q
+CDwGjp-OYkQcgZ-ss4f<vf;y3bdB+sz+}{wV@^qOr0xWJrFQ4}#)m91-}@8F6VE^9ZE|sWOO^eoJN#w
+K*^ga8)A*l=+<$SJcV_per}#l70HqijMM;7{79z!v7>42)g3&OEV<bZ(%bUyZugKrRkkFMpy=lS0H1M
+|pQnlu+<nT8}xNIrN|D<0xHo(?WN(2Fkw8+}YHl^2w?<k0k6>9+`0s|(NlYo*j>lGFj$oxugXL&m()?
+-NE3q;VhI5PzVl*Cs_Is_yE(-|nQTt>bSzGb(P20D8M1IDpXQJ`3m0&0jbU_g1XD)Yb#t^92YP6=Xb?
+MH0QmUk~C8Ib;Q^vMW)4d`$aFJI^DMqlOY{?Pr^L4)DFX5(8k_sQTtsMiJhyu;_(#NRaT`;gdyj5_;4
+W5=vP*eiM&vrm{_lNeI<_gq~k8l`W!JdijdN*prp#VtT%=LuBp^zmJ&;Q0A{dl|taI%vdu6D;UgEfN1
+TB?SUGDZ{M_itW}f6{NdG=DDDcy#K;V=3=X=tb_|WyLO`O+%^oljf2hHisKVo7|yt3AT~^Q)G;(uQXD
+%SJsxAyw+1sjvFtQ6^)u-s9xY&@-anAED6db+4>>1E7>g_JQO!cJ8f21>suNX^xN_)kw$F4~p}7`M?T
+kvmJ>H8Y!X2e%RO{eyQg2gZUj>93r)(5qh$<L4E4Ukbt&psJ9$GZ5UeiPzm@45GEOkb`?Ly&X_8EuKI
+C6e$HZh$)a^y_a1QtSPF7yXcg+)UoHmTU{J1RVN2kCy@gfOWW?Z%CU4m*;O{CKom8X2hs7u->BdW_f@
+D%4s`wCP{sV@`(gvAM&8ZF3zrneQnnusYhpFSO=Z{97OTUkLUCVxybO(-3FDY0K};R!ZIBaYN%yGYIt
+`;!09KmajQ~%+8*PV(0268<#p=>4RuuvZ7q93#@cKCX6-$k$kaFHGX?`qO(&IJMWI)q=&EjM~qg`;>4
+c)$)l-xQ(0XRBVO|8l4!o+m05aiAvX}7QhsodBh$NX&YSD==I>r-d0TCH$uvV_KKWhP!qjeBOcCA|F!
+WvyZMchbDhbqv+(DP!06&#e^s1}4&6-!&)uv0n207to@r2RiSthGkNu=g7J+>{JMXc*;%_~Ft*r%l$@
+|Q=fr#siOwQ=+0C%cXMYyWU4kMq@|Q=-W|9}Cx~_cgNHme*Hzl#$!X*(Q;>n=U@YdL&DWyQ;DCBF!+v
+B^{|w?k=hCi|`p%rB&_KBa)E8`Oo7@k7F`EiVu+aE@kuf#{XJj2^5a}2tOZ59KvZhfhIcJZ@H%AUq2l
+9zyFdhx4)7%u$qhfujjq^>(Vu^p8)g!!(qGw?$<;346h_ZpbWCWD1*Wn_#sFV1=4mHMN<@lGC0h@U&8
+BxP+788Wv(6IvjPO4(gKJF0HFZ{M6QrENhGHinETqdf~<9rt5*dpfCnR6^<Zfnpd3zvuo}axy%sazt0
+6Ps;s9Oee-#g-b1(q31p6&0pCM}pAQ+U>AUVKRj0A2MW>v~dQE*0PjhQiPSD^()$rKEJxoU~8aWWi4>
+gaT>!}+ZWkEjB89Zw(dYKl}4yo7)%CXL~@mdV_2=`Ye*pzBYf1AmjTQJhP3$l=%>-{JLQ@X>o+NeO+?
+6aBm5{)-cnL)?R&C}}O?hVXIVu#=4*<VQfu#%jUcV~#nE{tX@1z*@_^RQUswNzi#XKbVawGtY&@e?;h
+@HAL6F5Azdzev)eaBruEpC@>q1kU5K=bP#}X`tR-u^z%*mxAz44`KJ8adjkD@Q~u<he9lUNej{Idzw8
+C7k~+ULit|jM$M9fiqUalfJ3W)VbcaItcyWU>wq@_LoM~7|kG3BBuwtb*&u%-#+~Kb{L)lxQA-q#^cf
+oKp*RO5ulx`jgggLUg)B5bFv360ROfE-5QlBdP5%`XE&pCotp6&KYmWai_I%I!Y7-cDEivgCEm-Y;iJ
+0;3O-@G6aMcrmsZCUdfJade?(SC5(Z^Y?Ex>V0u$J3F)Z!a^qMt?dh4{U!#P9)@>x4VSRKzYn=dibFC
+0-YVyY|6NfoOR|EYguDnrBQoseGI<wrE7GtST<<yz8GAC%&UW;%2V-0FW9$upOkmPb)$k$Lbia}2f3-
+bW7ch=64?;l>!9Re{*jeZb1d}BtdhS~_=xh4DJ%{Z!iPO{qba=@;OTtquaCi$NhDW&;n#|^g`==8WjT
+*w9?7uuAt5BBp*dX6jBc^>_JN+lQ3*<2seAMy&Uar)FR`K;{jQsy>JTEA#fYEv#$Ha8GwlpT8g`SJgO
+i>e@tQ;&B?TwtP~ZoZP~F2TvyjZW?U32T@u84jb|RK>Bn$19%^3B>9-wKTg=FSM+0dyOHkZ<g4z3=>B
+&Pzs6p!TDS0z%IX3So5kv<-ugHI@GK|XbaQoAh*ZLbqs6yaTzX{MeYM1C1WlQNF7>NRF1+@cpBKZ|Z-
+j4owo%WWSTaoJW>8NtI$w1spf^&(=5v1rrcHd*Os1=IULnW>pEwtJLErA^t3x5_VhZ5E#W#N{T=WR^R
+exjjcgb&wdzztMCL7HC|<4{v%s*kAi+y4dZ%wR`{fUox?v`}^VF@%`h3Z^o}#JjToI{2kVRI^s|H#s8
+nj`A$Ion=$_21;Aj8B5@ocDT1V7f@UZfA%PArzHFdK1V(V;%e0*=1v#k2D$WP`4;8><2O15<+B}e2<r
+px)0Lq20WM5b9u2r!>gop<Dv`h*zY(aW#3CduQ0;mQOBp^Rn4&Gg^gX18vxA>J<Ad`UO7qg!#R<+!fB
+!T{lFyJ5P+B%w9H4YbMRII|g<Qo5>8Q{@ac3Zetx$5jEYyJ+<o4^6(TF6TNdzu1+LLBP(-3wseobD7e
+r2g2cL2b_Ro%&jJccCe-`U^<@sA63pxrv(v<JS1fJ79iFoY5h1ibGJ&@hw90Zfjfd!NF2YyBXePwAgR
+zg0r}$ajvyDCvH6sdOPqN2|9tOO*~{L?wB8(%W{Ig00Qw!FfV93r0ql)zCYUU2L3#S(5I`fa!uKbomG
+iLqIT+|*Wa!FpN)R#-RghKI`Thp8W#VV)BH?n!2SEXT;adCf#Ci@|IH2jmOS-8zk#1Vh5y+N1osd6d;
+|Zy*&;t^%OkrxTo9;hS>yW5bql7lQXgr@c~QHKu-Vx<5sargR{cQR9;RDXO{V}pk^J@ApLcV;JrO}#O
+wa><&f5#K;gIr9!TU6B)ga24N=Up28|rFARdpZO9tn55FmNU9>EG(<S(4lEc+S)ra`U4QEtJD$c6(Cc
+950u(<S;1NKAAC2gHh~}yGZIaH#*2kd1Q8KLtp9a+-4C`+A_ph8~a(<EMdar!|Ar{?FR2Hsyc1%*l9B
+_tK9rz29~)o2y*s}SaBxyh1qA>l|9t)Hqedl-3i|YoY<Y~&{R&s)K{Q`F$Les^HypS-0E`Av1zW$Hip
+kDRI?|T`6(E?8G*NusTSesNq8jh2OrHAQ+2Eo`XALh-or)Ed%P&MBXu`MAkR->?NN+8uDk?j70z62NZ
+A-Tkp|?@V$>gEv}p}~u?1#Ufw;=Q36;UimF?G?gC}2-dmRzCk>0S$!%<~^qGT5zH7BY++C8GvauWIpT
+E#C9(wc#{)=%qLJf||#r7xmO!k;unl&u#hC65&^iWXRnh7Dp<?FJwNF#m&`m8Xf9@qp@kl)Fayj9oG9
+{1^@;+8@p*%bG=~CplcaA}8S-jInX(8Z>&eY_mBmPs+?68@iIOJl8AO&=IAkCA@sDg#5foL!1^uDuRe
+Aacu5HY>b5hGY{_i<U`fy%$W1>Dr<e}x)XQy-Ai#Q6->BcAJ6k?YDD-fkzCm9pipG0S4Z+mb?2`q|6r
+wuwA>V8>|TS@4VAHm<fM!75RJLmCMSN^!WU(P^~pMP@+pcTytJKk;*qADJG=5<C{5;mfL|UxDZ$7v^a
+n(;m7Vn%iRIHuDPs=QEIs8KK8SHZo6xaKGDo?gO)#SePgM=l@gK)(n@ID^K3^qEa)kq<T=f0!F6POF3
+eD@EXP*?=^HzLQodRBzA2wWW{pGfjh@aC>{`|0y{F5IJ`Ke(J!%!5y&<_M9P>P~x8Y5APSjY&Dk|a%{
+F!p6v#XJKNPz!P=6mUGCEAz<KG>?UgFgfrvp!v6sh&l2l3!&Gdo$M{7vDW-xtGracx-%FQydW6BaHt&
+ck9?KmFU$WRzjBr=1R_J$ZaHg$OR}bvEJOsxfHosYl_7yJ70@+70kz37Ky6au+xS7s3JwyCkyY8a%s_
+ymq(B6O$p1rOzl}vafwSbwf7cG#^VWf_!ahOxax2`l=@`Rb8B7AF{#HB0M}s;)Xw7#GDdv?esPhelei
+Ghj%qGHuR~)?QCj6v83?Q5UQX$r=K&%pmx2*lEK8U~~K+?(;{?3WMtMXqw3HjRv1}N*eA#2|v9zQ9Tc
+yYM)x%m*``bxbK`lWp1?s=|?Fqq2&DybCj(KjFOPC=+m9B!kr&D#en7H24awi`UMhNyXIa5<hsRGx88
+bZlqL(}BmI+q&p+ne5NC+)XGmT*!VwL%gxzUVgY11Xo7RZkUG<W?8^w@-4fmOo>}JG-_Pd4S58upT|_
+-#jWEEm4Ddl>Y<Y3v_#ceO)k(F(v#gz?!K*cdO9yO|Eb7Q!fJQV#td%27_u6BF}%pO<H9bj3~9G%_eF
+f2&|Z;ih!PS%QF^S`l}<QqxZ=4(vP(K#+NVyl+Z0C5+h@0<9p24w%$gaJIq0z3c1Z(2E6#=J^I-ber0
+{bh32oBzS{9m8$;Cs#;u~ZPjKaft{2~Rd^1R(Yawj^E=h~X}Kx;~nl_0W_ib<@xeEzt|iUw<Yto{us|
+54H(`Z=k>u6L+3T*Pfj<Sup-wq<VE9Fo{1J(}!#f|BFmaW}L5WwDhZE&2?M<}2li`P%~ozdb-}OY6Z$
+9Km*pI%~&e63c@G(3+Kb$eeuc4aV7DcOw>rhX>z|q0qZo^F;cq*~q(u^1TNJkui3K2`0nEpeaUd7jCr
+C*LObWxKB4LOxw=fjBqvrZydMjXqPB!hn<eJEP1E&W(#45emLYt#_Tuhf@l2I4yKo0+_h)*e5liPiz(
+StSIRb>Xf?2JgmgMRk*vS<WPh2?EhG@<C$9^`HthDTbJ6O;>blpUnYBY{x`}(5f9Tn$Rc`Uwuj4Y3%e
+XUqDQAZ+e(ha|-@jBcA@y=2j*O6RwpsK-PiWyRR6ezE1$(IQ*3`H5KGqpImbu1l=~~@IuV_q&!GSRKp
+*){5frPq!xe9PR2KX*Cl63UKjS(9c;yRg>YZC5JS7Q7=-j0{Sf_bVI|3%TvicaqhYW_76{^5Z?V&V6L
+e$aHJK-(J<bd19gm_P^uM==yxOmccL#z}^v3Fb@v{sps298|Rcw823F;{uP1d5e(1x?bQBKurQFMnPT
+7*Nt$n1V9|M=3p++1=NUPP=pGwiUeqczqM~mfs%2C15>?Rt7?#6Wn}_sWOyy0!NGouNxtCQ0)uG^qWu
+e`qVRv1Vio0rK_&qxaRoZWYY*ZKB$)yrB|-3hnc4!aK#pU@;$)fK?<F0F6%4-z<`-M))LXTGx-=%k?N
+xeTU;D2E^Sl2Z48OOTSx`;-hE?wOhBrU@?Lo}_3>&qIuQqOd27!5?%YQ~d3*oTlcK>!n(9c8qlM($Ju
+lZoQRCJT4AhF|#aLmiHDC|*VJUZKYiiDE}o$P%-6n)_|hK5Nq5<Y9paW6bMOGo5&o;`z7O7?=4)rK_s
+`l!UkP}61h@=hVm+!JUkDUZZR?eyx<nkhf+`ziEg<B9JHWZR#*1AKlqg+UmoYbjT!Jeyj#*Y$XNmWd$
+AG^p-WNx2NUqlUcw;IxrLD%unex^}oR*8_LH?xpdXsS%4*9LI-6jEAO`^pD}vI9{hhN_!yA+we*Ude#
+E{q9{rP!JjwFg*i3~6hUSw@)9Mqws6^-wegG!DMKcyjYnmB6m*wnzuWCk2M(*RzJ?sS!Ou4|5$HFKKU
+S&OH=2$g^~K&i!H0cUW4-WUG{*1<Rq+MXa(zIHIobSj<$_~5ffdD(e*?h%zmwnJF(PMZTzNhTD2x;Ab
+G{#yTRY#|vmw~;lEx2%zmL!^VY>?-$}0MyX$%1GnSFcW-W0dq#I|(ARF((nP0kn$*S*UNtFpPO&VuDP
+aPG6eHVnC1u8wS(IE6nV7^+;Kz@mbDClaXXNWUy=NSc!;$-HyFc-cuLH&%2Z=}p3)0+=%?>!$7OK<HH
+@Jon<^OdK}d!GaXHw&KC2Z9Z{Zc}%I3BMV$H``1I-$FeierI&89ju(1Mc4)Fkj+sO=#2z2cJ!7<xO2D
+mv9Q&=9Z|)D?JQ3D*=QBcbaL^bY_8dO9>`)+h>Ie`2{*bgSf$@<!kg<}pX#|~2k=%K_I6qXPlGXHT<O
+esMu0bGml7HQpc6mk)VPs<GQ8Mp+7tT_WO-hicBSTLaRMNW8a?{N_CM=2dc<hHKmb(rMUk+lyQJ>n5p
+i{@*mD*mNwn@4hOUF`j6+1d1orQlc*>4$^VEscj+WQXdU;3#%J&OEi9smEy2|mRA|MB1-8^_@ovA`ro
+;xvjg3sf@rVouToOfUb@7><)TL48@qKx7~mzJO%8mIP3%DnCI24>7r_J!BcsV^}Og8vYOUwU`u10uT6
+tsSAdcYd;!h^=dCxUQU3%GjO)*D6b_A3&?>~w;bs9Q@_;r&w$iqP6D_t-#XTmK&^^l0HhbIWs4xdpAB
+rq6y&f`s{^}Sd-EVPkhNHjqB8*5FagX?WDT_gm-g>fA{1Z|jUQzUws#SiyRS7kdtWFOj<Dyn+5alkUj
+3}^538S4^p|(f`6~{AP@Y<OA!zV&Y%<JgvRbs?^M^!kG87+`?s3b!VnIv{u6oBe$A%fxgPr|ankr6x8
+a8ko+LJc3%}1nMIh%XWXGHJy$0=|xLwIY!3Vlm~ds*JHaTdR)z^O9#6Z$H4Ev5$dHeH@GTnEz{XhB*~
+_h5qlJBhql33Bi<9QG~1Y1@i8=3W#|ZHj4U!?}zy`>q)+2_B5>AxESE3|F4qJeHNES?)n+@cw(cU-9W
+4{IeJR-^e+^BCnq?y*@hw2Kkwo2CN&wWV;=UWOwkM%7%KXbA{FVOv}x{pLPKs-yV1B5)@-DNz+^){Mk
+pi?R5c6RKChN>{4Nv9DIp{5q=sSW1E;nz$e}~PKrGH1AFumC3@Bkt!QMeY1pfplIVJjX~-t-K}Z!(Q&
+TpGthTb4B>T<4ZA#e=HqU!ep*o7GW74JpE+%zmP8YPKMOSSw>~`p(4VpjfqNS}Aexaet)2V#fhd9<*>
+VQA2lXe;8rxXY$ORV?TgR2S6)gkXJvXkeld)PpNx9ODh<}LR^@6GGovY5c<Z7KE>&lKrLonSJ;i!pqm
+c3db;9A+v+bcw|qEP5GXLESD;Z~DDuJ~S?6Yk7$DAA{Pq9$KM3oyWi=9ro(+y_yn=+4Ldwvq$)!3kW#
+vEh&!{L73eT$O_u;Sn^mOFYgkT0FKwR{g64QsCoP*VYxnaj>!D&y8?Yq#9VfMrT-9S-}k4Y-9{;yXT$
+?LJ(YtRL2hm-glH{XZ>wF0@bV~_pyVYtF1tV|q&p*!#u>95z0yUm%BPg(_m(WGSB~WPJ3Bxs&u}L~$y
+_m{=`1atU3@d>iq#kN_bjaIvW?p8>3NjWi>5~H)2wjIq^}7ByR(sPA!o{s%UzY5steIAzTJy4havSTs
+A$@v#q2yEVoTcHse66bYtCwf5Y{+GFXS$vD;0-kEpX3UZsFQ(rpl#A4Xqxa3ViNjW{<mOkd_xBnFG4?
+<KP-68-mSl#QsJ&vmX<AzN|>(hR3Z|!Y#t!cGct=0hgZ54V_8(b04`Ej5fKVo=)Ors5!}SD5cPeDDqB
+sYq8p)*!~w`_HW_kzuZgn1gLrcK~d>5{{DSW{r~9yhyOW2{r`~@zT^5Ip6myJN65wKWst=ZMj6l*cKN
+qpK1C2DMw2vx;xx5v_|iSR#-JAvUQFN&29f9mHIaPvup+=Q%#fg7aIr=gK>bB)SPBOSyWnqz0oX36Rr
+G1iGsjaP=?rqyGmv3U;;Z;G3=HR0mGTz=j}QQ=7gDg;#mjjYGZsiutuN?R2pgt=5H<p@d48+<(?S`jw
+Vf?P0UIA)FP5TUWCXLSUgTgX0PlaFs!jlHi97iL@Bw|FQ(DWUXRJuavOWva1^?PTeEP~F{-^?6@Hz6@
+0L~YaGkI}dicZLy8rB(=#}kHrHl7gBP14`nz`pX2*s3ZmYmpMH>rVC)ZZGyJ_Z~upa^?KpWUkrihp_N
+sGOrf%M_8CU{XAmm-wyfb5kvoW$Y8|(!lV|?gF?E7jLE2q-*{>vP*1jN9#pX@FD^WfGn7$=s8iEaTVe
+S)Y1O`A^114^RW+t<BiZsP(Xc!?9-J-{EBe7-HR0ftwyCKV+3oj`#gDR9HDorFq%Y|B*zdh0QS13EPq
+XC(%A7i<`i9r?{v=6FPuZAPqQm9M?i&xiHBc*?y~~CheW2&Zvps}ihsRU<Jfm{xa0f5DvD-oJ8A?2E>
+&&`cJMreD4d>4;a@RWv;;Nf?7Y~RXAkUK@P<!v*)(iI}a@{FY)j{@;$T;ni%ILDKJ@+Mo3k{7^Eaxg3
+#^TU5BA)kGj<%9gafNZbPPgFbs`M@m6X_nZ*S$+6Gr!m2h`-!KLwyR~cau5;Cbj*eHO!0St6UfWOh)L
+Xxb7Z&r{5&jNOI$=p6d|ztETIc`ptB9bVEFk4u}wnmTZSVTFy`k{KzPC&oJ};4U0d%5B{<}Ur8M?|Cn
+nT{P11)UCSWqb{@J1Qr=4P@^~y9<rv;LV&~_CmC+DcI&pj`kYZt)wd<p}ay{sE%?2nhv}_`DRD%{RVd
+MH$)%TM$bEmP514np{`BSa8&=Fy?Y5`7M*s%l2>JD8CMzM+GdB=KFb}a0vKbS;e?r{F$rdQu&wzBNsU
+!_*#$ytH6-83oaeb(el!Ihb`u>GsJz}cZy^xLWS%KHwEd$B#t<5QEiddO~XJL@%waXMqlV}Qn8q0)_s
+-BGT%UGWQA`}cHqB>0scvFDU}uly!;FUDD@Q+2bn!pZE+L&(c9wisJ6p<bSvWG88PGYPN!M4N4X?_(E
+1j2E#`79=<+1#0YX8~S;U#Mc-ke6`%M*B?W2STxQ3o6GlGqSjl=_%CpD-TyBsxmB0HJ|AoOKfJ{y-T5
+z*{RyuBU%%Kpl>e&>{Z_GY0mTLX5@JnFM*%i3Ms1n_dzUJKJD8-_Ru1S|TSk5f)c~7u5L{*QwIyr@u$
+f%@&r-#^ZX*n`xj-L3e2wRVmJ<Ideg)Ob0m$kY0B!z~l?AK^EL)aQE&l))C_xK|<wd@>sl`#?UI!OmF
+n;YeTaasld;wG)mkSd#s58r!YyUQqEB+)Y@Jq$U;}_Z7(5E=5vDmy#x-9Lg7$37XA5`h>?{{~x;f$mF
+hH<*UnTI%>?$Cm1a&*wT^u_UuF?&-*f-}76Emj7Uk$Gg@F+)Dw$aWrT_arpd4nhzJu>KP$zQK65z_mE
+9jr(RFeuHrpY=a?y>+8n)>pA{ziqP-o_`4}WznkOlrU?Cc0+#ShDVHgV-Ja*=!eSjxD>gSMoF?ylogU
+o-<0qbw8rDEgi*X%n_GPV~y8N>6U$Pd_S5phUbTKd2Lw)I|ggq;SRr`=|-r~sVVf0)|P_Px%lvVf-=5
+hAgy`>}A2{vnzD!i@0844?VkK=whp;!*D!!Z}KJEUBlVlUUYOVQ@j!Jp>y)DycB7w9LSA4WuX^>Dn=I
+!$?Wss%@-DG!_~zA8i`h>KZ6Ggayles}O2Qjpukq~hSU)7`t+>b+a<+iKoDxkDHXksF||K5KFODd;C|
+2P>^5B_-s>k9$5`cZV|Lib$fz$Yi;an6~z~okgPJZK81|c3?AB+T&aN4l6@=);3|Wb(?W&pby}$L>PE
+Ij9=BT3FmjOH;~&N^(%Y7@C#+z$v=6$<wvh~Z%<s@N-vNz^_C`;&zYjt(YJ8$dsaL2W8;b+JOFLuoNc
+r?9UDT~Vq6zLY<mND#x7YN`}^AoYKT+itD(wW^-l*}W$r|}-06!o0s50K%s+i!pg;M-{L}XZ`u!KCUA
+3fs6)J~V{T$qV*I8(tx`Po6-=31<m6`KlN42)FE9EHmjWlR2^fruULikvt+>iS#E@Y%Ia%dY1+Fl8;0
+PC5JNx_JDz?ve4(<4mZx`j=Ss=&F^<i7k3w=s)$2$m1GRpt6r&Sbxg0Cx`wVc^C6GCJJ-><D|CFqLd4
+)bR3XA*S2ZSLJnjBrx7v=wq8DiL%`svMoj3=dgp$IM8AhS>if5(%tTiW==Ea+MJQX+P5&GRmT`9auLC
+zD%uox5iNx@l}4wVgXSnQ&?#i{>rT1V_AMDFcusfw>%zQ~_Q2@-$aHo`Y^URqap`bdcL!hUeVrpOPf6
+3X$Ez72REDRrJkS@1RgN@1g!#A$&41DP;+u0`<oW+TJn-*N@>7W5A5QR@bl?a<(I}14Bn8tX3EEQ8pz
+s|<)}(ie!DyKIGSeMMIIb#`6b^_5wswyq3Xo$4dP1z(>kBtP3J?WAe<60X9*q}(EG(uyo&)p0SUWFbY
+iB6}Ud!J}5Lu^-)itqt9DbQ&hO8O(3sa&J;EF(SP*KNFYm2HJR1Jb!y>iV|TMOsYwK5M}F9D4z7ABL@
+U>xYmlaRGz)iS1q4}rs}-!T*Z1ei&+`EVjAa4}q4mCYp1_gJ}TQ7hin?2fCdg{{=q9t8d7w^a%HZz2S
+5YDxct2m#3I`DnGMevc4HHf&U1RI`1-RI1Sc?S$W=1n-IH%SKPcKPRI9yz+iEMd){P{M{6x-_7xNQ-p
+q=V_vg1S7qNmE|ml6!<B8bYGt0-1-TsTKIB=;OVNSK3)g|9hkJ|q4%s)V&fN))RW=9QzMS_Q%yAaZz!
+z9+#<|K1kKACzZa4TsB;L`}6xGrd)}X9(b_{>CAA3?&WPF?Q^uPyA+6Q;tAiC2yqYH*#?i8ha+_PQl$
+39~x#BmOiCE0=@E#1Y~)%MGB!7Vi>I<NQx`5ov}^7W68>S$hAfos@WWU0|ii|Y$3*#7mv>vBs4vc#Tu
+o2$@5rCrO5<;uvr<2<<OE@EH#+_&Y7jC;e9H<F-S*_!nk&7j>Rn~R>49m=dr$4b3hK0vZiu>O|HRf|R
+1I(4Q|CWyzJ(_1>%g-aMPt!w4cpRdlU%x^&!(%#s*vS6;A{CL+@e_DV4VYl}ccHQOkB8uV!0;4pAQ`D
+E~xNrt+qC5xTPLPU}01>u~2IVyAn&`8bVaaNb5!rt*>g#|i3}T<lGPW$3>1qyw3Z1oxd0D+O1o%j))l
+-_UDsGE?2gH6stM*^%!Z9HB3bgsjwIZ4P4_bf)kX+^Dk`yePKq({#8-egh27Ir}@}92hcEnp!ZUL%;K
+q~+gSf-!=dhG{@p}#faSm1Acel$#%EJu<=&Hhpou8RC0bT%ON9Dg*4S@@MIj<KAzV9D^O_ue2Jn|WNo
+EhMg)KLr|h;+Wfz=Q5rHJC<SNqwrq4G6tfJn4as2mcfqifknw(zE(OcPU_4a-_1-2*qOX7hLYTL3t?k
+3G(V>&VoY74OR)ge+xT5*?ghHj8uP+~>hsG$)|}oEczi+czqgIe<!^KR-4vnU&GC0rgnl>2-%Sx(=lC
+yzsrTd7fn{AX=7HtZM1}6mjGy);gTec<xS)v}%&2$08H(N>$ycG#=d`!renK47(z9)QOCGgv<UWt$?k
+i?dXnIrw+RN9(2wvM`w!0tmO_BPqlOpUggAd&cbq+E(*9dnzcj}drW+HnDo1x5}r?9}GC!EjXP&#IKC
+h6)*+y+Lr&&g>ZWl0{|EwN{`V_DRh8od+=Eu($W-;%?2W<+>%KvGDe(+sKbOG4{8UHWsPA=XySY4fOu
+7hJg5BN9Zhca4QyA-i%1>srKRW?tUpi_^0c6x@k%*C#JSKVn4Jv4eZ2wy}eGI$X`Z*x)>+$kg!c7t0r
+J(1+MwcP(5}uN;wWOU|MF!nGgoLL^u==H9P_NbcX9wQr7Er+&*whZL6;!4qY$7n$d;Tv15`$o2lw9&>
+BXrK3m^_>+ChcQ0wb#R=;wEK@LRwp_318lo()Vf7C?n_txVLGQs*E+1QT-(!;RT(&&ZTAf!N3cKHqks
+{*G?n*O{3&%Lk{;0h$8<q4i!1AXC=?uERNC?e1`mG~yycbL3PNebBkIi*7H^#ULDjQ>@C$A25Ii?b38
+}a?ML*PRkTM-oL)3c`J%bufRWpDM`Stnu+;|W4X8IDM<$7Zo`wwKG0SC@Ta(rSmK&jlTAIe10)3gT~l
+vehp4y^aYFy2%UPm-a~Hx8s~_=3d?Qd4ITH?BkGIO=YWI@fwE)%PgGOk0qKcH_=Tz$=OJwsWx^6imHd
+BecT*;OziT3y4}eaoLYE=j`{s&5_EnX+XFR<qi!7@hy%e4jaG@67CbZEw^HnhQMB7VCAj6nu=>+jsr7
+%k;$lO5tIBm>_eas*|KhFx^S^XYSPOSo=O!>j_)FV9yLbCS3|2=c&mx~8_5YdE{g5F1Py2p`<*#-1S0
+^e+Ktezj%mSI97I(D;$TiCe$AAbHXe~qjC;!@PDGOjFWH>0Y0clbU*ca5;7Qi8jtwk*i4D5_D2hC_ei
+ZHRt-v6qtZnBOFBt)$RzRX(HLaqi6wxTVC0;C6LPu7y(wFn0&@V^}euZ1&fytZ7XK&`s^tKMvx0!Hy$
+nJ!?qw4WTRU&lGCpMr)vUl1F};6D`XGan8F4~9Qlsry(15`7*Z4(Pq``|^N`dM#cJzIvC;I6~h%D}Zf
+-RN+T@2%euT4k<XjHGj7keEJ5dm?1=?xpvJ3r7>^ftv_r8kQ)6|CgV2jq<L)pUM8bRrnDLjs`RZ+Mlu
+OtaioXEFo5Z&E>6C~<I`fYUEUl2s==EJXDufcLDtA(ji#S+jhrBQ^MQh@o-gWXzAB@E){+N(+EP-HYL
+>9+^qw!0Ry_?@7T^2I&fj0_>H?b=INMwc#5&a520LK-#=Rz^D?*NOZSnSGZG*s$s?9mGDT|w{hkXg`o
+A(M+d<ZTTwa3n_w$J0UUe}G%zK{UF78G1r(0u57>5_|&SU7V%`n=fgAym3S6|a1PkEdZXmcphLvjUrh
+r;E$n-U#YAhvQ4(hj(JR2ON45Y@Md|&RxTHQK6lcOp-b3SYr-8$EjuULE!GwklBvq?G{Rb%)k01KJKn
+(L4u6QX>wK|&N(@-a4p%AmkGFQC%hn?`!v&^KJO$q_JDYJTJ+kCAa|GC4((kF4@QJQ!CAW+(w4t&)6?
+GD%r8gY8~%Lg;PVe%;(rKl3c-%?(P7UNJY?-&w=DDzdGyMIc;9{%-~45GlZ9pI7tzMcans#5BG(<VJs
+00|)+I{%I9Yah_lAqnmnW*|9$O`Z9-)h!ZWb=n3v7^6dlD6k+|OrpBGvv0cRqLL7t>iTN?bf*{J~cC&
+S*#nIqv)3En?_OAgAL#c1CyB)aelI^x~#D7UDc*nH}i1HeB_5M8fT{>FDNgu6E|F+dVKmkS4(uQb^Qp
+>2Qb%4lCl_4owb^>*T@q-HgblxtC0}<)W$U!d7`*>XMZARTiF~5=Bhd-N51vWS@xLv22}FcoLWU*R;f
+SgU_WD>>i;tEl*@(8yiti+eE0cc0(NsG4v2)7w;R3(HRR0bOcY;`Z^G!ry1IqB-&P>EBa>F+Fd)cm)E
+U3sryI1*wTM&|5@t7dH)Jkx7Zc0_Y#veGAFZd^(pV~0gbFn|GJ&3<KwUQ2C&<=<E6{9%fEq6mtSu8zd
+7*!-@MK{4F2XCpFxl&DTJmeilz_*!w`aj85pKvf*=?Kp;3&$X$<)?OK3q9fQZQ2b(I3emY^V$1cF%vz
+IKX3fr*JCz}@zX4m1P<#JUm$Scsr>=B-%_u?kPppxx|(Zi`X8*sIIG6b3~$%PC=?I{vG=nN?jKU6Xo1
+_AtK6f{{Ro0?1;liNQ1iqKOL}0@ZSAwPVS(bY^V|2V9o)+8h^KK@?l<Rq8+af||coGZ$#Epg-CV2cPX
+BuW9St6kHO@QgL^ZSGK>BvN+X$*Af?nH-8MkBM!;61q}80aQ@^OyL;l<95SVOK}ncja-RLJu;7U&gJ<
+8NH1^=e3q&7?MImYHd?4uZddNDnfnwpiT-9RsmG1`x%kS8KG{vWqmv3PS#dbkG=(i5`CP@GFL_p(bQ*
+ho?G3SqMgy~5jCHUup9MJ0KdnWV36qJuEdY^KIC&OI`z*sC~AV!a{%75?hx-46x%D%OPU0}J{yMgIDE
++;RR=rfkzvzWna=9Qtr*>I~-d(TgrnD_k;(DI^j(O?1Z{GOuPG3DRr#qu||R^tA~qJ@5F_X=+ooBht1
+4~&5Q=9)~LrflYDN9poL*l|1G3OsE={+AdyIaR;7$$^64`8;nbZ2NlL$lSXQr(1rN#2bR9LKI9Dd#{n
+X(<p1%-)nufy`$&k*l}$7nEOYGLL-Bj!_L(oadUjK^6A1jCc^s6Il=G;dz|7OGt$m<D~D9b3HY30!&y
+-0BkHBcHo8ktYYz{{6Sdb}WzlW^x@gI@iAuY^7ie@a%)CtujC;ZnxhqS;@Ql5D)R-aOzNX-$$rXf)v~
+uT<;licT26nAej#Uy$b-U+gZ`qgWsOpi;Ihx+}9I-1mIO-W=&`j=YJod<gfp89U^oyL1%<;ldXeg7@c
+y}V7XCBql1+os`ELubyz4`B@t<az7h2Iy`?^Y-DUFjT5v)ji}-l(b8AA5U*K9{8aQAMiwJZ{IQlHT^j
+=vx!y?v2aH!Oxf7aGTy{m7ROO!p**(;e}Em!;rqt%%HZ0akgJaRNY{9tSmS?q}<&v&nQ$r2E-Fo()O9
+up|=<Qg`yMJ=D86lQ~kIK<mjXG3}?iv=W?3G6bIi76%t8jUYw3JH0&Sdo#!4l^{5P`?+j#?+FZePuW_
+^O)429ob5Qh{=g6B^&voU=B}=B%xX1gGbV#718Q3h9o{l@KHLn-Ps+6NF4xuBT=6%_Cx$4eNFUd{CW7
+RS|yfAG1@jMZ))1Hmv6C~9*i%+k{yok}|Jh}OubuK8?E_bO8Z#e4H{w9w$a^g0TPleeN-INEzk0=u!@
+VM5XtxlDE1^>At*EnUj%e~0{D*m@SpQ`TiN$c|CJ^hid#^uL8{LcwN|HWzEVfdG)_`x|$p$Li+3=S$*
+X&5DN3`A%VhCt{Ah;af#7u^54t2?{`6bXV>*{b546CmM-Ua<?bZ9_rU4N(BBE50to0Z6&_gkQj$LO|&
+bQ-HIADw+~lmRJVR9q4JU)iujmLEww|5Bw`z6<dRA%khiR3lyN%y3+&(F|{IHMWb*KdPCPva4GViWbI
+_P7_*D5nydl11&2Y<4FT3JpadC!Wn!IL{@bqZ%5^cBxc)=N!C{RTPEd@sb8gJ#F}1xj-i7JQX!!_bKO
+wLy`IGp~Uj}@syr>}yn2hma41N}J7DvQfOwt#WVvF_n;*4CjpN3<PV)gD=eKdMtl0ZZ$eF4Dyj?64T=
+CG-=mYJbAG^*G|yrONow530?W!@3i^jP>UhgZeF0qkVX-?u?vqoJrj`HGK$;fQZRoVAwb$mgMR%s`!u
+@S|!r^ga|Y5>Ig!OHlL;PJKsU{qyL6$-Q7<^&UNweWZsXOdCLXqbmCkAL|#WCWc?BOMaEx1AR*FdAwL
+5s1Ac^v79oSPjPgmURPXO;FZ|N%g6p~iaxoyZB#zpD<>0C(I@n@ww{>3IO0xI&1DYL=^*DAdDJ&tdA>
+y*f+dOK8#MXo?)nBNXteKK{K?NR9gl;<2GWiDQ2Ix5e>f1=E89IWkW;ceIvvq2CZ&0~=H@cOZFPM~(^
+K4^#UgIle#&~|`btt38tN(Z6wB4N((O?mn61D{k*3Qe5pUR7;geJ>l*7Yw(mJ!9t#-ncFyOrUggE$~q
+F^YhA1RaU??|k{cxR8)sHAxA-rD$nO|~bdKfYuY)=En3iuSVXt1LKS1ls-@Zt814K_ohpjB`6D145NS
+z*5NZNj$n>;N9C;Y_RXSJvIj7Eym1spE_EAj<Zf`Nau0Keiu?QSXoxqoE~llr7>9x%3=IR_U&WV+asc
+*Or2rN$Ee$%58TP>_e5SS$Fk@0AkE(%r04Q+4DU`6P%@JX>>m2kj9$o9Hhyc)vAj+|m?(^vhe|qe)%Z
+>NLcYlC+B6qDZITV#Nj!g-0n_G|bwN5#AJskiGM2whVfi-u$YofmoLYSnlB!hj`1;E7bc*ak33!&xS?
+<WnIJ>F1nYCc>H_BQfn^qhFfgMPjd$Eem#x{3nWf=S>S?kPZqtLT*_4fDMelz&}wqL}iBiBK!EV3~x9
+NFa-;x>0-WtuH2wgTH#cDXCc1?;#Skb(E_;6e+tg4w;Xz7PF!hh>P_$o1fg{<w@_Ibl>S7m`P|u;2a$
+Q(LEytvmQ^RxJlCbIn;Wg8R3jj^IiS_?>_gC@PqH&k7f?{Q`k2E@v9S27P1)R>|0W<S>@UmlLoLFtuV
+maRv??!1jdY5SFLG$P$Q^3^uIT!B7?Ty;`fv*s)6C<>y*eX|epczkI-;ub!|}O}M(9&e35#;0~7czdZ
+-AY3)BRX_}{wpVq&|sNZY@uzgwD?jGVZKfN#5?zD%%Sw5d+U06?(vG>0NiY^+=0yx{~$GmknKEwm`UR
+>tM&9Y=aPDw}{v+n%0{;Mj7Z}&-D@CP5O=*dB%+&tr!VSoJAh39<c7URhn)o*e?0@trl_6^BCPzxF<;
+}aTv0*!GrKjwV5W>q|TVf`bh08D}2LB&O5cmWmXTd_UJ*--5+SUw4^$uRrx$fe5nZ-FR7tP*>y5_x$f
+ipo9sVHA0(;Xg%q5HVX52JbOrmqT*S1@85ek*Pv~_v@=lHS2#!9)9|?RB?i19-kzEb)k2Qn@2xE8?Xt
+#S)XslWb@?>TwvdY-VDjbc07vgtk>ND<GnnfCrnNng&+3|+4#XinMh8{o%nb(?~{vrE<}4dd$t%0+h@
+PFL`=i$<hTx9YIjgrW2;(pkScIp<##APmDcFptE=;(oW>cc=4M=!@|uWtM{d;~I}|-q4mGmN#7PTrej
+|y?{Z{Omo$OX=AnMUIn(3$5M%u~bwVaSGUL2LHNEqzv(nt4#dI)C}M#u`)y1BCMN)X`sl&2|k3bqMEl
+NH|0zRVl=sYxjlDPNnAA&7*T$ZHt1!%b|$X8R}~*4=3lgAsPhgJ(rtp76U;I?&ON_5QfS^5VFA1@Rpr
+cDJq!G|SZX{>BW>oRYR$D!Zk+VKh3$_l1h_!Ri0z?oE~*RkN(YIZtut`ZGn}#Ec#!0is7(Oz1=pl8}V
+pS$?audVn&kv@)wv&LZ-?ijU*Rn{$ufCL*(btGagGP0pd!0ruK^uf105I;Vu9w~%(!G<~Mru`Jny61h
+8pL``9GHd$Mp-CPfMF3l6+IFy|58jraS6Dfaxg(~m<3BDho*X3QbBn(1Pwo`vEhmUN&&sX`%)%)_}AI
+1P^FtQJ$427dGgOMn*j@bVyIf&Ll_lN|u?trn60@XBN&_aOr6R3ZYp!Y<qxcmIM-wc@c%YbA(i2-?9n
+gn2-TwS1nSUvp37m#@KoW{W@!m=|4FzSAi9Ap^CSAeQq40fHanFUURoL~X|OWu&ISqrG*QJ`iArVepH
+)5BNEdmLUVBxlfqLsp~d<+{!Ukfn{lWa96Z=Rk76eoGE^?2$_FQ9Mp=wf)o}S};EvM2EI!sX(u41^xo
+tEKf&s&wK`s0`0qGLDTs6L35NSKwk>_(wG7>@`&bWqwcL-kB`en{X<{MYSfolu_pA+LFQenk9>rPG3%
+1d-`;8LxV*;SqQ~-n|25KveuGA@Mn`3b+@o1n+%_+$;eMd5fg!e(B`K#&F3((uLng1(ryUVZ^09awWW
+RpWr`WG6+`g9#gOz%@u{f$x=aCw+_%sl+bx7EJ0pXJ(zq$#^&E#n8*bsgxo8sn7u!m>LCDwC!HjG(z?
+$3P=?^{po(54+;w@J19vNA5jq<Zcv9-)~#4<53;^!fF~r&JXXuO}u}RbdXEe~rkFe5v@@PR!Pvum*>p
+#S0JIT|DP_S;43YAs9T#2kmf>uiL7kUGY@sLYKIA2X%S%jO{GS8(Nm7Wcs@2y~n=7Pp4PX9A`pQxfq3
+ZL*CqSZ+|z^>TonPsnIvVaj@jP;;eRmR&Uj7?2!w0>`9-E&);NB#|5s`f2=emKd_DIwC0_+!e_2b?%k
+7^rYA5?9HEf?Vr3`6qmQ${v9eqkdlUWsHvYzs`Uh^~Jg?d9eXbT{z1`WzpUKaQGVk-Uy(9}85!_sBXN
++CYt^NZ)>iaqP!ql?5Q&97|EqYYW$nB98w)+OruO4xaHj!{*&Mkg+)Y}s_=>m#9vT@s^AwH|;{(gCqG
+muI<DAPP_)b<&jY+{QjDuX#fq13MhP>)AsxgGPFqgts;pmP!t0^ex8W0WMCkm1R3n#bXu*4|iA?hSj^
+6;d~kBWEM83ymm^V2&r|nusQU8XgZw@eA%kUIJ1wt{kJv%-vlO#KU4ScZiieati(BtRIAsoe>OibnFq
+Iki1qK^w^9xIYM{XY)3w3Z-_f~)zj0<KID#8qCB_9!{NMzkEmjDiYM9zWovA4jm-XVhP};!Gxl3^g#1
+aN#c;tswerjeG)T{zph<Y&=eO4f90!k$6CVCBwPOiLEdPrY{q6sl-?-t)-~RVDx<$X(|H}R=U~2S9nW
+a(s3m>)FEoy#Avu_;or2O0e)%_xrd3j%c@-M&M!H3ZI-+QF@2)Z8SuP+|^>x&iMkM-r>$kz>i&~~R8m
+<H}M6o$|!36l(gQv^!m7=;0K91Q-HH^N|mw~1wdUjbMh7+_e!DX6KTFc4(}{B>%zkBOJ4{AsvN;?*2@
+^;e5wz&9sWCP~bI=!>ABMt5FiRF?pnF)M)`1`DyD*}Fw=u5KAP6EGwgtxQe~XyK$NkZ4a}@Hf!TiNLO
+x-BBxzWWfqB_7|D}#MBG{ow(&}G74PO@&U(ye;x6=GxG}s<#|s0Ix~OdFv6~}XD(%WK+CN*ZAR_Wu&W
+F&=*KibKWc6=TIQGcl|VQ#s$W9iD#9fnIr&JFw9=r;ORW6C8_m}*Bc(r75xl?}7&NX9UoGB_LhmC*(u
+%n|5!{lKRLk4$J(5O{>4J<$5pF^M&I7MTQUX%qg(_(e)5gD(p<Cj3#lm`}5^oH5c7$|+oxnui@`K5*I
+O=29%b6ub6W=6i_<Y>BC{soLl9TvA6i>PXS#{8VTHe+_jQ><oV$5;LiA!E_N#*6bdHB;agqBnM!r6R#
+nai~#sM4+&NrZg=qE56?0PDMx%{hl!e0=XX41H!f{JrDw6@Bb_<fz%arf%rt(G5@P;g+je3)3~1$<Q_
+H?uSByE$Uz<`n)^pygogMgG4^f<=VO>7d~9~2bu_i*wij%d1A~i7nxl%oC<C}KxZMt&e`moF)bYA>x8
+Q1{@{A>^_a~MO>ofxF3Z5u{lX=ZR{=XzaBHJ8r}QN}UJMgD5$E|rCialmB{v_#I)ROjqYYI%KOVE8SM
+<U5_vv}-yBOYAJgRQw)T4+)lquSFheP_bIXpR3*5~%s91o|bcun$G5pmCXMs%C3ICr-@3&g#L#dDF^8
+hQ_G@pVL}Q%2I02@&{{h?LhTklGa%dK{ax0B@dIE-Oy1msSq%B8Rni5hv0&iRp7u(N5yQ5WM~>u$vfK
+qHs&AaeRflpg|F}+}6S<?Yj1Lz{aQgXl0K_(<s81{9ZGsd%<`C+w~)<p4tsgS5Vp53UQ!m>hTm(w>T?
+R^UVF-l2l@%gqY!d&h$>mtm$|<AlapgabqyF!+d1OXtNP3=yVp;-7uijUYVEQblsEt0`rU+d-9Q1Y=k
+<fmq(^PyTsk;3(q++Xga)D%2v>wu$}J^yA7zFZy`dFgu;ntr8Xt9TA&YJt)QVmw$~+{QTaX5j7Z<6n&
+>}X8<neB9(k&*B0)ECXbG=3^UI^ou>ocXa#!1YP~B4gg~kGGv^DZ{^D1$KCVGkd1&Nb+Y>zgxe_qc!=
+<;H-4Z)bvS^FxROH@3E&p&tO`<~qcedf#uo1ICfXCn(NcjNTqu(j@oC2x??e9WV@z@MtD&9$Jx-#E*&
+oS#HcyaXNp(qV5OT!hbGm2<HaODCdwd+hb~35CFXB#_Px6|OqWT=c`n6=%rxOIs2H@J^^ewI~d<Je1u
+$M3=|gCt!&3rFnfxt_oXj{(Y?}l<7P{-8@meCX~I$vJM!|e5A^*NJ_3*UO2;Y2At@M+T$nM2@96vlIG
+y!Rw(IJE1uaYKf2i5UZnOi;1?2l#ln_X#-Yc=M78v;4_(_7d?Cjf7@hTm78{iBB^z@)x2@EfA|Bm@1x
++?Q0`bBn(4%`wEwiBv`>1Y-%HV6bWZhgj_cs=?Ild8{=M&4y(oa<xW(6*ica+AI_zX8`8*QQ8Eu+*M@
+!ZZ$kRC4NWXj$cdKS4KhRs2iRioZcvlqj~`LchKg7`{VsxQjWdwK>cT%c^8@G14SdagFtw&FF*$22*(
+onMHW#mdphc6`po4ijqCR-A&#>#<lFOH{n+aF3%<KDSoQ2`Zh`$hq3!Fs{g|Y)1D+;MK^&F`a!HsO_X
+j-mj0!b2B!PF7iGtoa`~uVW`^a8#5)aax=+jsveIMX3ZkMTyv-@B|mq(9wuZXCe&DGA?NGVQQik+C2Y
+lukue)6;PWXF`~kgHYU;;y3y*|_<8e&fb+$1}ZNn6g3w|jK#;})^a0xTl2_w#1%Wu1SCqO<LMrEJt%q
+t>}h}7)S@LDn=?p_Z|OjgM4u20e(xggzvcTh)W{K~N>Y<m%steiJn$T|>(b(O`VnRkvV<|$sQWSg=<J
+F2cn!+t;ck%k2f-tmgAP3)erCq~cxsV6OrxIvm=6sNM;+PAJaWQnVexFIj^-|Rek-ttP=^4{x&xwFD+
+w;M8tl$&BF7N>+4Wsmx+hPYElXUq3=;6vg1XT+i${*AB9FQF~FeQ^^)L2p=97xen!$8sAhph^7Gtv(>
+*|N6bZqv7A$^AB3eI09fW3UDw^U^GcmG>R;r7c|VEIR2?DWRd~i6o!GeF2R7$po5!%8KZ2SXaZh`F!+
+FGKO6X5kS76y=5>|=>h&34d&CK#N|UQOBOr#YGo-|-bb+H_2|_H#`B}k<1PB_4J(7T7O|Re@rU1rWpe
+?=1AtU5pD0r1<K7$`Em^fO6C$e?yb6I$%fD|Nwf%U1kmNxbm2Kh^j|Lzpi6||>_{W}6S4sFk}Cd(^KY
+OD5W1;X%YCp!J;%43P{OA*-Uy+kb{mhetXUXb4d$R(;`dOy|q*N!X%O4YLW*^2sb3iMeO)U}2Cjr6QJ
+iu7%L=qDTGw)<603e0g%nq$s}V9swc6z&%_DHVaf$Vs6ekni$UiHXMb?+pUS6SXg-a7zf6Km}$zEfgs
+sTlkT}_SuRi`YIAk%e8XtOf>I|bM~br(C=<wb36MtXxPvvb_Q%gJPcaX>4G`;J5{xYc}`9A@Yra&gg&
+aGm$ugacDUAPb%yR*<(r#MOuc5iHN<oyNTd3Cs9l*6*%l_@p}mln`K%AP;11GmKF18f7A=ot`@Y`i&_
+nmOkvEyzZ6659Wsixc4cquTce4ch1(Ut<)MxPdD4s3tF>@og^BbmWwQXQ<{DQxrpi(pST9=ml7{d}D=
+N;2*&+{RVx)&}yo;@C0Wp)#-pfQ7px~PF|JEwb?_Zt`AG<%57(%ry072_WcNU&!Vs<S&%(67gyoAg@8
+m>X>o=sZcz*>-O)HR?99NIh)b3?CbYfe2%tPRIj|pWABoZe6OX(xYb^LwnXyxTFG69~DRU9hWT+kqL^
+E<GUa;^yhsKo$88MTE16euQo8Kc5eq7-;p9Rs@Iw4DD#VTfvPmx5;T`T=U!Ek<xf~t-jDs^&YB?bK%B
+lj4~N!j-mDE6R4W<ygfP`=JcZCxfa*m6H@o*HV~4&q8KL*K<eTKPReiY|9}7TxnlR2RcPeNYzL{(+xm
+nx+MO1gvzC3&oXdG&93pnt3c?t(-MqG1RmVJ1{!Oy`1Wb?11=O7bkJV7hkQ?$VnHRi)vtjm@&`Lz4^R
+oU{b`^c2b=`_dr=c<;Kem<Wcc${<moNt3z%q};ZENw{EJB*t<<Z!^{G`RHc%T37ob`PFoH55h;kKLAe
+o*oe_y|M%=<1_m3{fDH)g`r+<OGaEZ51VjW?>ru4{Em&D=7fD0?)B(E!`}WxotbRB7eQRypTjZnvCgJ
+Wansl6Cg?00iXO0UaLm?MrAU4ZQ^}(fJvUgsXR>=MA^UWwoKv-T`RY)l2S?oR!uW1D9?Ow^9eGfZ`^n
+X6PlO{)0xOgu)37*i&KXJ%4{y4KAw1la_^O8;(opqrXUs}kihURn0m)`6l)4wc*Hy{5I&Bs1)x!ec@I
+;YM>79EeS<H-h<hlWW@}Eh_CqX4$F|;%LIXu_Te;TJ?{}7VE{vb~Ir}qAvI0gGPPC*EmBv6b%5FDW>1
+Snfl6pErSfx$2eQ_QDU(F6(n7oup@h+Zd?m*9RT0lf^!I*_13N3K#>%bN7(@|Me{fC;x+M=Tg;fe*km
+5ntV&S93%fUp*b+Rhc3Jz=4cF)9d^*4@Lk*;B~WgVvI@tlC2t+=qgvq#Gow(6RUa!4Jv=<XvIb}nCZ$
+8aCig*SFu1Bm<HXmwJr$g>&r<jSMg_oid`owP$42NMJk$gWBPw7P|^M@P|<z{RA9|<vH!(DW%{!~W%@
+^e%E8GWh)fwvyy7%Z%dD{~D$zW_y0@FRvXx}@F&lS+)n}@<^C9Jq$NHFzYRNolGk#r3%%Ar6qMsPyb>
+OGNc@~<sp_WX$o;8+O(sBNzb(-#15472s&;oFDMMPp>>~2CX9m8Wk9M$H8^7I40aXo3PRC?-si@P**@
+d$PW+{e+}KfOBLPYwzpy(xr!Ub9M()93QG&G%EZ9m>rdw9;L^FIl`z9D5{sxZ%UYV&jEJ8|Ju`EK^0<
+<_bM(Kb4dwKSvB(=IJ~Th7V&VTsb|qPl<#&29HCf@Txv-XqVZl$RYGMyBZCrD>s%1WW|^T3kr8d?DI7
+}2&a}6=y)LP%Wi)(uSA#l`L;^_OM!~pKd19RA5Sc!2T;IfHO$-HcvM+L`0oQM^M5l?nf|{3RP<J;lVc
+*Q;xSZT7oL8W)o^QcsJuDzc)565FpCD7MR%Rih5qW;MYiA$Y+q)|(J074Im?N5vkgCpO7rA4I>|cl(W
+4>f9$cHp?vV^KwSN*r{4A|jjOod`2ma=!%GIzPB>8k?&(T4uIkdlvq#?oEDD5%NCZQaThe?c(&3+S%!
+mv}+t&v<#jE02ca~ChaRgY13SD|~|6635$$yKx1Elin>dO<fi2QkDwtMo`OYCPQB#^gD;#pRS`8IPmr
+*t~j7g*WoNyimI-XrcyY(9`DbwA|Af?<H43_NKo)qbna46qQHUPA6eUHX?h(6;7ibs&Z92{AM$nKN_x
+sJU2w>=Z1<(jFJ$4#^m}#z<0ayncaprW2irlCBEP6x3S89%>jM_SAN|2_jrYb5g5g39EEWdp&1w>P=+
+88hM;i@!zq;f46fh=i0{!lPnQrt2nJhA8Te`dbzYs8iIqVEsQ;f^Mgte;Rn;m=!8F^ldYrBDT<I#dxe
+Ci9tGN`D0;fj&3{<x;<}YXLXLXerXm2MlXdKe3epZYCh7M4EK>*K)A;7o*i2)W5y7nrU{VotCS(}kW2
+6~MEUq#^m1?OZaI4yFu#m;_zA{_&PaQHfrZYYtu!WHU}=DAYp_x$uJT*-bVr1dA@%1&U*Z*b+a6(#Qx
+1!0qPI%wO07~g8V`5xob_Ye<#ALBirxB%bS1eJz2WvcHW%d9St#YJ@C-Q4O8vOoZ{e1R;)KLS~x^_44
+#wZ?yC_Q#*C`0?yC`!w#p%-IRz3%t`m1_I8D+XfAT-TcKFEB_3({Dxhmg{ed+-UtEKqQqqQNa>Cmj+#
+t{4Gc#L^8MA>7PQ-ixRSSdOI$s>z9FY><geOw*dV=_R$Ufs6=I;aGm8`+e7$1$XqND~bkR~(Srv1jGa
+sf)ZblH(3pb*?O-8Z|Z0}OIj3-=f&)2OHaW7%87vriA?-@q@TnM*$?yegz%pOo>sYgjTmaZZQ_9=0ml
+9C}rZu<|K%xA;qI{#32qa7*tG0HbcONE#Z7h#t<PydR|&=^uK0(-{PoM1H`HxhDxrsGRxUF|aH@V05G
+wuyRe<au=K;OLH+t3YFnJRQRaA)C-So7-kIvzi`DJI#XPc5?XYC8$`sq4l|)ZS!p4Myv~0K{wv}UV5c
+1cZxECUn8Iqarr3qtXp3#<gmB1XD^8)*3sqQdzWl5(?I*F_cHV=v9Qwzm*d^OPVK4gZX{<JcVesFq$U
+b(qT<cJF%!@B@Pn6;EXK1fA7K3MW)QCH<+P>k{m7ZGqw!$n2@Md4N}^!5#T&Xgx535Nj;)nT-er4OV*
+M+Y@~!J4^Om2UV;oY*bu*x9ao4k(9=x36_0&OhB;L`YuexMTDPg>KWdaB0me;$Mp4>}$$dKD|AkM@l^
+g7(`r&z@D!rjSz?`wKBKvNnjNmm?1@#IzwZQM4qav%p2q2gEknB-wN^g_6svHbxS=Ii!kYi1d~R8uBK
+_HcBdN0o{`Cy{J>RcRrCX|B86^yOjNhL=jVU88HIea!m%apI3-l)SdExhL@B;Sgo`%ilt%b4nhi8@Ux
+6QM{@t24D9THj+Yj%oC2HJop^bfyp!3=?{lxA$#11q{x18vL>th_Ui~73J9wof5^&f$ov=ZZ#zrANV>
+cQ#Sh{5zjVj%Vfg1e{gL1Esaoag4uMB&RsSu6UZW~tr&2(5`V0esHe^+?gz3*~0tBGPf=ywNz9XyoF|
+o$OWlu|BTz0dB#3cq|Fo?W#0w&`y>M!y6XA*2A*b5SYrbr3{VM~Tu6|`4xW+n!Ej8=0<G6IDHYMr2q8
+4yDkY>-fZAqzAs;dR1q9k!=dmt_j@hJH85cJuO9?z6A5FS}0%*SXhc0SNj&0$H+$q>5fevYUIdH-!HE
+8QUCZ&Eok>E(8LQ@9~Hm`mY8kzI+p1c@He#({o;0^pl<1?-YAT71TA58csdxGfS*8Yt9z|%J*ff0Ohi
+*`t@Z%9|P&1y^K}4=AXF?XvG79`nw}@-#JU(cv3@fb+||e(S?eLrbWXf;@L~>E0vP1tT%24$EaP&E!S
+T54XgG}i0p$V+i6C`%J+whB*s8FkA1;IroKJ))Rp)Bt1Xjb(%yS@Ke^q;uPbY-<mB`s+pX)ccW$FAGv
+8Gs>qk!n&1Xcb#v62#ga{dqSxz|nD?5c+j;pp5#?kW`;a+iK-R7LsA}yIGu4>Tx^;SA}4b4dM+(#v1L
+%KeY6tW<V^cK^zLQtK2S~5dda!qX$-aRV9&hnD%_jt6#F2dnXbm-_o<|}Osw|xSc*QbO;=VQcfe8P~5
+cFCKkokSyGHu)l%#YRr~M|5u(HGNeVW}@bZogJ@g29EK21-ic?G_Eh(cOvgcLCqy>y+*8#AI~L$eVy|
+%=<gCb?uY4-YV)^2Hjx)t83a%^7p0$Q$9$Tp0<6R~vK8K(rY5HDCT@vn#0STtb8&a%;FRWa96eLyscm
+y=<LBM$foK{-5hNSmh6{AxExD?5cgeX9EDVpT{7Eab6l$hD&Bb{>Xlbc8L8;!$ofq3Ddw;1n_fw+Z3o
+(-}5v0zSpxJt)CT<=bdeQ9ZVf5^=IzIRdqVFj9X7qTc(zlS>=2BO;un#lUW1O8jY=uJuk)>4BhWS!YI
+6Rz_r|w=QQNML%l@!mWsqo`oDh0_wB-1Sdj$n9PPNx((yf%3G>^9IVzMb@kEl@_<+$pB=pSpa*Wmjh`
+uF_8xQcy?6&tpqAO8>TGt6p0W0V}m5UU@|8LVSyohOhO-<uJ>tbJ{S<k@LdZ8~;SA%J;2Z=G`xE77pp
+#K=_x1AE!Hhd)2Q~9=}@chxu@FNel@B#%Ka$ixiF{2!+86jo>)L0Ov*;$LUWC-AjhJ1T}Q^0j5{YVjK
+gJ0?t+j=NhFl3VdEtx%Bh-@G}E~%(4YASByXx9mQ8U(KFB{U83Uhe-wkEjwMe8opuV;y@9-Q^pl{r#4
+^yiUne~9)%yvs0&ozLXc9!IB|QX^&p;#;jIaPXR06m}EPuzV#WB4aH7-X*)&cV6oR$z9(O_@s@9K&QR
+}j=L#&`4KNpFqq{E%N##bM;`=%UV}{(L_CD11{RVc*n9?k?JNamhJfEY9mSOZri3Q;1Q-S%>I8#F(8U
+d>QK&*jcO0B|d4GR=ry(F3X(L3ygg0P8eyz_=3LazG$oNOYG^F5XJL%`T6iI?ffc;4760fp5@0{z3q;
+FBD5@dmtUMAB{aQ}G*!>Ws$NP-YSX~`+k-eu=}s=B%$Zt65Gy}=WJg3Hg59B(%M6TtTnC)#3n*8#f`2
+eXE01UXxG7u(n_BL<^JIE+v8zEf&7&0$$}v*dO?f>jE-u&GCsGQJTR;y!^xVXAS6>y%>aOF35s0JAT>
+{#4X3DbL{oQ>%5BZi>!pOG|esuAvB4RicRzqnoVUG;bo`k;>+R0NbfmsqJ^zh<pZF$+WRYab4=GD3PG
+v%f1WyvI#0EW7Hig~CKKd#h3H#dmM4VMpuOe!RDzbViPuFY1-h!Zj$G$KWq{GkokdyI8vzH_z}{Zfbm
+EoD7W1m!f|A#&qJH<p=^(=IgC?<|(0WCSCQ!rO$0^PN$l_&)l2bXW?~gf`d7CAF>euNB~Mx%;9(T^$v
+Y^{ynUcp@$_zq&6xzupIp-axC(fNu%r2g`+Tb!u7u;`f!Rqj}P}h?O+yKm|O?%mfXw!oXH^vJq<ktTf
+p0p{UK-8)Upclg*iBDOTMo$DEM57qMhmzoHg@tH(;i_E8tQdBp~|s|?gExhul?3?Dt|<WW5>>}uX5g$
+qUM0+k0uuue)9mfBYALtV~HX)sRM*kch_RyzT2YnN=0BgOk0@~&w}KxBL<$FBixk9QtQ2bq7IsB8lV#
+OZY}jk%VmE+wzRxSP=tJ(~31a_<+ZxJYM56T)+2Cn39}<S$J)`Uj|<x4?>6Y7e*Vsk`V3-feC-)Ma<=
+R*ZrKS2{_`?+n|DDcJ`ETX;tTx$d08dg60>fQoa#9c3lonjSY?cH8lV=J1}dLz%nD+NtgI3JDDpmD~N
+f%<0V*MGV;s4Rt3C-E7W~QCWd2T%Ngy%de-qIg6yRr-_Cui4-m`PAXAOTxMDIr-IxRXSe&BW__<7W_<
+U9|M833B_9LcAK&b2@cVR=A1nxHjG$l)rD&XBNE)Rn3a4?5{M>b5ooc|>nq;=lV1VxD+DHW?v;-_X<3
+QTv3}mT47bQD`keCr5w#4U^J4S<K4KT$L5VewM%@m`RkcNVGagu=IYVxx&3}#i(PR}6bk{B4>03vCt{
+w5f`U_iro$u5_5mh2IzoJ1gTTcYz4VnKJA0$Cfr>Z6@wFk6B%ApBtrkXea-KacA{`K|vdN_JS@G~ho@
+!2RpN%|8uw#w;$6{OPM8*U`k95^#~%S|2~=wog==^~D)|tp$cc=I_8n!IymJO+E{H*U$PQ&543EYzb-
+j>Od&16OgYD%f23cwJrE(jt&mK4qg8l9qNs?yi6_242$EI)^!4+_~Uec-IS^iUSemoa@3nBF)e3gMd?
+gfXD>!E-dLVZ<m?|r%t^f^yiiEgpMAKWZD>cw<#vwFt?cI_Ov(G!j**bH=a(rn!m~p7aznBdeuW9Okx
+`>rB6wh+<srsAqeUl3Io$J63=izt+ub@sF7F&%MWV(I)MIO8$A^4xX^aus1Jmv-^M+YK#xnDd%aapP<
+q^6sFX)R?-LU!`BOXj_)v}|R<eDCp-oL6;hw$X3e@S|>Gs7@QPWa)8#SG?zuX6JB8ItboMS$CjedOos
+EgfD<q%vGCy$wzo*r7vfc%qI)ab325<W^}K<JnL+Ow7aAWoA!MV7uML{A%jkXsTTP839e`gWfdCD}$a
+5)1Zoz>{YvLChw2JMn-RDx9_pi5P<W9oom+xy%i<#L$_2biSdie&i$>+Xui1-y){g$6#P+E;hNPi5c*
+X-Je<^QM96tR=8hWrT0<SLC@CERF8GWf553hkIiDK)1v^<r6P|Y)wTrAoar=>N7>;(XR8|#UaTzQAj&
+lC~kR5mPNs2a#Rzd!CN%hW0My&<|He(T;LoFF?gNWIZ(1Q7J4MKHe;7;A=7Zm4rJGwl{DOJ9ZiBH@Y6
+c`QB;oJhQ(1ITm_voItELukbdlv~*YcAMkn&JX247g%CSh>R924*(%bF%=e0qvo_O>XYp$HFs9II@w~
+LF>3V<6<X+>KOUBWP|faKew9kqPatLvbc>CVB$`_rH|udNyk~3Oy!a*Hn@mU5=Ha1e?vA&;H7*{wtRe
+f##JQ^moX1>X$RjMc)HXtKRqQT7r>Pd`3J<;teqhyd4V4^tM-HVzK>KxBfLK<V$BziE-%JtEfywy`wQ
+Ry>;L$s=XKnxA8q-5@W}a5!up@z*?Tzr-F1IR4QYlTVVYioB83qM&5$5ZgqJiCCYA*#MZ!2meQF!HPT
+w!lXbEK~3Gzc=$D4xoAfQ*DS4uU#3VJTN-RE5GB~C42Yf0A52r#IiGw?SNgVDhy=0^C+-d^2)f%zW_c
+E5z<C1jrApM*(3ye7eBX#xUdgsxNnDcGLC0oiIv1<5r=qU#{z66`?%l>h-ATb0Ak1PJZKsz8>lLI=w_
+p!IkL`R(t7NofF0D5CtT-6<s{6S`?~qsoAlsG7U)>F!f2K`#54*ty>Pt<1;gV`QQQ61QoeqT-h_A22T
+Egm}USATa`kKk7(ps_&ULTz_hp_oihjA*sBC?6!e3hs~h3G*X;^@aG$?DSeaQ{IE=u@E;{s8Y=(-Fp&
+B_T*+O8Nbw#aT3{!eTT7YXwFuO#59RnS3ieH;dmM|<;|`Ght@`>lG5($E_4l6W-wzmmAB&;S1`KiAJy
+Jaumx7LjG<sJsm~>h+g(upjQ5~K}v52bkwUAf^vZpZSU42kP;yUTB!1&yyqUTY4m}g>}Ue!v*4qPzdN
+1To7<F-Hp`EK2~3;epP9a}(UrA!TG?|4gXl^wN5t$o=zb42>tmfv$V&V>9O4xY`{L76l|?84=J1wHxV
+J%4Ig01095Xvonzof{HKQ+y!$nHhO`E)cyD%IDR1;cme11CAG5TfsyJhKq)3Yz+<alZrqN*A3x0yuD1
+npz6dyZd$P;Oq0LYwLx7s7N^UaunCTO6(H#CVa!#vXY4?@)$tO}cF=iFuA3mmXT-VtaFq5qYt-(MLz?
+?pI$Y0}9<D8z3!j*Z4^=qm+f4|2u;H86TXa6N(2!eF)|l+=MmWqZ=<st+_p@SO&v@asj&y0yV`?UH!`
+YmdoH~M)j}k3ilQi4d+lLGJ3bGL$<7Gb6GUh6O9-IEyF#}VO$I}&UPRy{oyy()b#VjV;{qcS#jxa}NG
+2gJ^!G<~qR;9=_SsR_TF6tW-owLi0;$9ps`rD$SGg6q`DCKXBvMabYrHf)ydR*J@2lS#qX&a!%usNQJ
+B+SOXpRJpnzwqKUP21;_DWXH#$<!TpPM8sHf_glWn2N?_7jlTcD=&~<KK2XNA-ZhV%A11kPEF$9PS1*
+C?wM_a(KMkDf->xpN+>-YvD%5~YV0!YQ%?|i=u4yZs>%6}?NAQa7%J@OhivciMj9oTH~_Ea`dWT5TuH
+^zl%RM1m|>p8aOxG7HD4M}j|X$r-`@eBDIwQ*Id@r&O=Qkb7L2o?dd`vt?Vvz>6Vg<3$Aw)$$kLI!6!
+S^^Ot^o2=tP!}4Z%ff?s)NbkF(4{k7t9byn*ac4o2i_s7u7aO2@F<?%TmO6-(}~ud8ol+@ZIa<|MzgY
+tXfEH}=<%JT=g93@=`H6v}qNBvZ?Im?=+}`MNi9Je*E9I`xqw>N^`DOsgK%O`cce?DpKzEOQs?BeV<d
+y{lMW$G*a|*E42lHf~<!)--yW)p^e`^<KBZzK3TxzzylqDIDrgoEV(;{Egp2`Q?m}6-I6z<Na!vu7Si
+U+=!3cz%xAOYbm0_%{_{5x|UY0c+`VOe5;xZkM44lXHlpkx0L?C@4VQjkHaPoj@9*IJ@SN%PaB!awb4
+39`@SnSSU84!Ld94WRsQqDY;LmNk`VD|_GH8F1ypvFhkBPYb!-IR4oni|NIJ|_MyVy~aHz4g$PPB&sC
+$XH(eiD{!X7RM^{9ra?n`{_?pAAwMna5og_w`aRAiRWDqaa=&yy!MhN#)ol#Ik)(q-i!P-Se_x(#hBl
+RU@OWuLy#Ym$!}&U)qADbwo$R%<C)FjOxq^1(bcB7qSnpDgjpI$kd5ELa<KhU5!U-g<P&59|!{YI7v9
+6tA&LO9O>6s%bcqwo;t-@^cR9ZQk97cB91B5bn4wV>lw@GYNL9RRfF6D8?q$(_BA(NMyuQxN)QPi!kj
+Btn8fD5I9~`LmZp(x-<PP`kKv+02R8qQPlvmEBMwO;VYjvI@v3@7><8}WcIiJ$^HHRblCkbuDEB#U-p
+0dziWV{0L-eRUzBogv->Z0^!I;jqUrDdp8Wmahrj>(FaPq3{kQ+P{p~;6UogNETRzw2l>Ne$P1z1v^U
+D+To9nJ``l_sdVQ<lY{@eZU|9)$uU-pmNqkSwp_;%n04Z6((P{YI~tM9kITeeyDu)md;`?`xC_8@k{y
+}bYJKZd{k$FiICthf7U+2_}@9;O8xYT2#M=JF%u?f?Im`;~zA*H?o5^~EHAec|X|Up)TT7YBcL<!^!?
+9Qo<>zpc+m%eS!XX89N+e|?eI`{&{P1Ndsb?1;a;e^|bz9|jUWMcY{l+8qlvrB}zwga9QYCix46foX#
+*13LOy3_RN+Aj|bB+FlSlzE*rlWL55>05T_Wz*kss@`BaL6sT#zD5zbbKnn{DE+Xk)n4cA;fcggo%2p
+T*wu}hSV1XIXFG|-67)GrXskL!Lt~T7jTbuzLhBO94n#=h|FhKN725M!X2)1^0zy(IXi?$`uhuMC$!s
+NuyO}cs39zMcV8`7+Nl-~L)ioh~T=<mqTW*3Nh2OQugmg|=_bJDwD*0FJfr?R_6LDQ(>?gGVfb5BrjP
+?fMzC98>0;u*`#HS&iAs_u$c$iT0IG{wG{+n6^kkqQAK8=ht)=3tMICrSs+%}49q>Q`XASn*cMo3#Mu
+2(zXbB`(p#p`pzk7MMP!GBd>J<+4Ec2|6YIIo0e}(jo=WrB5*Hl4=H>&<4F8$@LDV>wc%+F=%pqFXF%
+j&7KK*<38Vu1Y1S?^(8^Lg~8>bx&3Ir`wEMaI}$Jcb>Ljn9o~1E+q8i~%c(i<Sw*y@CAQp<vF`zwo8q
+-&hEvdqgMSo<VEF^ST<N%ci3i~+!?`xUXB;z&e{Ea|>AN2BvP}HC4Aj>4^9LVtNqs@U4D5@vvL?W3bL
+uV)xON+5s*~>xk@KTIiU+=*_I}BHH#gg6fFd86!{r-K+m$?Vi`%zvc)2t#X%(r%oaOX)_a%)vq`?jWU
+*2I!Y2LpRX!%lJw!q2n-|!LfhBR2&n^L^&a4r9|&27C;#QT!IaBz&io>5z`_X+g<jbi!4y!=++k$TM|
+wF=F6mV4(c=et~R93vZ*?y3Fu<wI}ZzBFfRP6uRc?oHO#FT3K@chwftbA(Pjh=XKt_Vi_^%O#Cec!QQ
+-*L${gTdq@$g3CP&%;hFg#14HGSz^CV^m3yuf54aPE;m>*7<6p2Gx*Iwf3z%Gw#Z@o;#FffqUVd#lO<
+Otd%S@17$>uNj%f?A&qzno(@tw5-h7IMAx2N?PWgJ)YkkFsGAfdusis9o`&oRF6%U>EkU?23IlS1dVO
+-<G;lQTD^kfdVBH0vz7YP1b1j(U5UR`HNf^e6SB91mRLSMzg83F@3@0xRbXmRzTn-?0<68XA`a&IdV<
+4t+oHWrPG7k@L<6G88ZO_o`PMH<&^stTlP_aoGV+LcJRm*A2<REaqk%0)D+hE)z1Z@(SJp;w-TaZIRP
+jvJ;S@%zSQNB2T&?XleS2dHS>P)v^(-acm0P7@~Ql&Jm!kL38(I>HH|j)HP7xx=+&^8DUz`@;dx*eD-
+#HaArPTFauaI-0_d0wd2;sZ6IWdSUy+_3@Op7qRN1`@XEyA9{As*Wr>clPJF$MERLoD2LFSx;;X=(PF
+!c>bJQE6Y$fFon{!9m)LyEUr)_BKlvN36XFSD9W~t5H(5qm7U_1kXK{iB>JE*QagLlxHn=IHn7+$&S=
+2Fkzq~$(86za_e!8Q}uV_ga{9AHxCgxz8XRX!iYuxyw22qc|GN-g1PZgHkv^+Mh%hU2yR3l%m%Hp`ND
+~}uNw%IRfPm+nRV(ZzsXb;7yIB>>tCmwd0AofmI3a3-e-)iGh+vR2uOwrC<zdztf7->~mB*{2*0bvN6
+Y1|obv{401BRZ(pH#_8tYUx#TM#3ug=rfEpQQ=EfDf~&=iP7a*Z4Q}g^XJmkx--h@8?Ct-FzQ^~0h+?
+eL?6?|tfu=tSN}LX<(7@Woa1+@49Eh(-WRj>zc4h0kQjziACTUk+wvVWeYVvPSmsmH9~zY37ASCrfk4
+<2@_`>ux>67m(6pk~2}EkOv-_FI!>X?U3}jbY;yD4c3sejyB@q<hn~Yw0jwu+^Ofk@6TNb9^Xg`56@H
+(!rAgVM4;AjC#3l0JXv@qa}P-_Q}Tn8Zm2j&bKa5%np>=?j}M9-iyNv(uQ1O_Lx9Qdu*Mr41%=-=(Hx
+d6&kk8k#PpBD)aA8Le#Gm9ytal?CjbIr~p5-^>$gfDl3m)lc&>6JEXRq|3CHhzx>lDI>%fLlicp&!Kz
+*w?o{8w*@zN3C*4V^tazKQ0^X`2!=wsp;>I)c<fK)6?#TlLJo5tj#K>S@0;2Uq%50&f?Rc8Q0;<4{8y
+==W+-AK>t?<nuCKAbKkZv`bt*rQL=D!^al|~iM^=oX$N7Mr(U1(7f)$Pz`{^pIgUP2C(OBE2U@u8vm$
+|S{-t(oMQysxtUtRp+W2EsDW;A#(&_b>lqq28XR96$0n{M$tG8(kzEO|owY^`(DO3GWFvyao$F?$bFW
+hMgZ(-4^lqTMwhTA1~f{M+KbLw%Ba^@coz8)HBm+L!|LLV#+hwywX0=S+J602X4y?-){E<dW??#w=XJ
+DUQhF7T%mq8vo*P=ho!yScD5kAsuk+Ud!@+=#3joQtjXkW!CIP|6+lqtaEp<AnW$oZ8VlaL*vdAYb>U
+bHRw|ZD30a_B&5RC>Z0t2Xn*KkrJ{WUG6?I`k~KcNRK!g`KKqHVUMIIX?zg6d#x<D%aH$yF#^3spHFq
+60HfcNJKQO6UC)vFVK?t5bEiydD%C|yRMGu#gz`l7kl?Zr_;%1=cyd-kyGgIec`}N9kT#REPp>@TUr`
+R7iwLd)1|M}lbWi#=xVIZf*cT>cAC%j2ucpuEohk?=JjU1#Ly|qqNH6bI2G=O6Lm6{l$phOXc^8JY5h
+Go6*gqgHx$6W%pFKSG)ZN%`P}(t$0V;+E^c*&sGS2-KHtT(`V~<W~-|*L|R*oDci$fME15{A%7Ok;RH
+Q5on2$68tB70wN3UhEve4@>exH2O#jzWML(Jl?|9OZ|b-g@n$-DK3z;e>ZNq2_&>c;~J7kPQm975aJJ
+%7uLJ3!bp^Q^`&phoOkE**a>`|ARpW`-g+f?``>ckimWxWIpvnU(!!(&5j8Mc(r8|$a{&^w++Vsf+t@
+TFcZ+4S^~%C-6;fc2Z4MKvJx+{wHdTB6PeW;eTgc|D?VL^(9)HOxa<rCCOAJM<5M8XT{3rs0;x4n(_Y
+#43<F3U%Z|=(;ze((t!zX(1{r-u01c!i>?~pKiy9}odbHCZ!Z54e0Q$R1Lcb0&pOf+byMxU1Ly$o<VF
+@IT2m|TrH|uio<FfzuAfx>QL1u3o2<C))-^qg4M#*=|)v3BxPC}8#1&=2ghr<W^nS~wA?qB6O%%O6y;
+z2xEBQN*UMvf`evhLUOQ965YF6Uys=T7}2gfETmcy3W0iJgzxmQg)Ra=+cqkgi^{y`I1wIX;nm`?~NO
+#=Y%!g%1<@nG?00F7J*8N+3t-Da+4mq`4bf4J6ze@a0trZL~V^rg=ODr2FK3r!dNh-CZQk-39s4GUj{
+xFwU{+zE~}EE=q<9Jo3C+Qxd-718PCVJXuBE-so|Y$9#we#Wb{-(p2#js}`9cRk7Krtf!s!{@`}bsY>
+IL9rvfo*<9da1GTX@mIRA6*3>JcJ5*=OoAhVun>YRzlsTgge?G_<oYPK6{&HcDuv6h>k31*j!QV~A{L
+?{(3rRzpY?l#HJ(b$+&^!1p*oof0yoEV09U(5AsLQFniL-X?suHVYnoruJl3+>3n-OVD1$7b6yOTetJ
+TK5??(Y?9B45K{Go#c1iL?F+C*rcX)=MTFvtrZeJYw>7Z&T>&@XRr8%(TMU+_&=UxlKx*Rz|T+wI<Es
+?E!^ayEzkN&&~UBzH;OKK`0_ia?g9^5XIe?)5bnA;*}c^BXwM1AcDlPA|}2T+tUG_cqrTA*OydM&UVV
+SrryY9Z;YgyOBd^wMNui<Y)ZbGRn~0BTW{9HQM+yMzV4^&6sG+Mr9n)~q0*z9M|_<3TXUBvyW35p30t
+zezN_htY2Izze?=`p-&vMHh(8=;L{9#`;QPBZXJg$0l$ihX-^%t|eYshJ1Sl83ZMOc4{n!8e-##iy|G
+#j!_i*<6hx(({<nMzZL7)UhA`AmeS78((sL!p*6CfqM<Tp$TVjr@y5MdZ-+5u`j1sqhP43v>!1ekh%I
+-{1XmT?iNH-ls;27-6X|2Pfm&=?Fj_vEV6cV2~N0Q)cjdnM?f*}W0#kJsI$EB}A_C%LKsp3mS&V90x|
+Y=e|B26N$Oe4SQDzvNqV1`@L6kV~LMnJ?$GTw1hj{=0O3i2z}d{#r&>ojo&~#bl)HuR_7fYs^pV3y{L
+T%yDbl0rq%n`cUorgC#Wx)YLR58TEJS1W!n2ovg*lDDlsc5C2e?Tn;it_F55McM64pHH5+LqKe|<72=
+PNG!nP^G9OT}I5?0ds4q=u-pZ`YGDvgGWudk%)Kr<(oP5;3*qRDFv!2a|dh`!PU+6chzK)13(fl1cJS
+=yofbw}PcT$HocThQpzLm~jP14qDE%tq<U#^!s41RW~epLY;^IR>VpG+U{Z_@`Vm_AqnxA5pytAK^eH
+7wW(6-4|@W*u=x3kW|yf|M#=%_Dc3IuaZzo&s#=$Az9>OxePlr!;%XIYo7AJJAeE)!XZ}MhL7knUt-e
+d#PN>$#A!2yWQZeCCGNfx&3Ws=LhYeao)DIb1b8Hlay$|lL2v{Wj@(o_ZM+f;sbO)-sLvWg&f=8O2fJ
+)L)U~B^VsK`blBt+!M+~Z{Ss``^DR5wGnI{<G`|n*#jl!&bAZxDvGzN(ke@HHuQ4s_qF!*&tpw9$DGA
+K&77w+=?R{e7nbB=8P`mxM=R@bJ%Tn~jp#<Jt$2LR<vu~vob?V0)Up>ZEo4u7tutV0>5a;@UrpT~02A
+$n*+WfVT>8N!C+HE06kf#&T6v-pGhP%#w7!87V)ADuq(RI*$^u<R<Rk!cm!@rZ0l9DAsx5=RqjrlecG
+qkBybg%=7uf$eI!tQa^4(Bw+$^_~9I3blbJTh0#lXy=%2%3JWW$kI2uc+WA{sJ4&p^KQ{saxgg(H{|S
+eli33Dov@m?9^-h(D(VV@d(jm<i3}DLP>EeaRd7-XI-nbnhzP7W=WZTl53lp=;gt>XCil}M@Gm>bJ`J
+b!3`MoY{i#CcPDc%V$acF1iBv==#pK1=-eY}VG(t}+}-{<ds)6CB4)p*<#QAUsxB!)O`Zt6ns@M7I#0
++whV{OVH+(acREI(hBiD!Pb;G|p-pZOW0DRXS7WSw%o}Ay8nTEb2!Ttb|2%3_e?dXR@OxjmX8BM#J&|
+8Mq$vVgXKE@A$IsV^T`SU99^6uO{PHls3AbQHHbO;9q+qUM8V)gsv2^M;n5*I~vm!P(4rZ(i9->mcjk
+I{5dbqr2w<)+o>MOGp5q>os*K-Bm;W3Mne-l=>#KAo*UUN)R3_j2COJKetZDpwQ2KBX_Zoo!41USFS%
+U_j1c&)qA*@ZD|XTgWxLTGF7ZZGH+-=Iom6p?VBsw*b3@=SaK`j@<di_T#Kx*j~{@Xgoi5yJHk7(u5t
+^OQ+|qs>Q|=Wu<=q;P47_vWFzsl|1Ih=s*pOx!VR$KHp!Rm~T&z8kLmvG%jMp!1q&jqAzZX&CauI46B
+sjsM%}VGk?Glyd*FE<X;8E*5&<~<p|evcOCSq5xJrU*K*)}Q$&WimxDG998C>0>}E}OYT7S1!zDUmNn
+$RiD>T>518r_X{@hr&2?=@lk_%4TcHU<DA_Bub?2(P=^<L=t*3;@q+CoK2xY*BUeb}<p68U@_^OJINo
+SwNt`*b56bxp_j>PZ}4-T}_A3o-E9T0u+kbw{r?XW@FiLsc`spX?051!cA`Q>bii2y%ytGQ(8zg2-B*
+6BK(y@Y=YvNqO4ZO9nW|Hg!KCsS@0&+B`Ak&c4}K%nB>UCpV|Pf<shR`L#$#uffmvsnMxXh4|HSOTcZ
+ZhL?Tgsd>}wqi}d)a32b1e!pp+EyG5If@LlRna2j%6T{7FZMm{2xY`a%$_Q6GQj^M4-AjIT{wUu!S)i
+J&$~xdAeEn=0hVwxy=y%tAhk_q}^n=C#fi5@>rBHl<&G@GT+|}Jafk88U0Y6yuMfNX-0Z#x<0FDA>Y-
+F{({~XJmBLMmUY$HLvd;x|)(I*D2|9I8qIg?<11;zk}HlqQM!&e<3CjJ>EAOgAq0GQ!GUvR--WDH=~0
+v0nIaDovA@C+8n2L@qM;2cb+t2ysFLV8}ce6qEkvg`riNCp(<Nf-z<$MNr$I1G@XmtWnvWR*QhQpfV2
+J97;mERCAo@jH*(y*L)DcD@TYic^W9#wG6q`sOMfCh0p1#&fr3jvmhm&{v1u3b=HcFMU=QWc6lYuQ@+
+1`*?J4aOj^oy6Nyd^jqbNABFvr`4Ld+&b=D<<t4o59#@YLdI+`;PI$OE^`>%fYFL=^CHK(6Eku-#8k6
+VF+jC3w*INTC6?_x+%J#XLo@91bl-rp#A$PLP5{-9PwY=SsYsDJv=26EFAJeG=m+`qrl!v{@hq1xD?1
+4QG4$aPbAY@p%8Hg0J(i1*AJ~|AF*{Rb0`miU~8`BEegs+2qNAbKM30BQ}Vb=D#KwNnK*0}xh{)rK?H
+X(zhHWmDeTR6U&XMf}sM}XCQzIStw>f5ByB|+$1qPIPEZ(Q(c#D_!Grj>esNX7VscD-ui54~yFUK}L~
+3%n;T8VB>yT@D)t$5@t){Hdzo%WFPXd!^kgJJ;R@*max-nEuM#{Iq=Wu6pw;(VOI(=nb^eJPlr3Fn?p
+m$Jben{h{6~Jj303y{mttda;3vX$KvufZixUN_0$99<9mI>H(Q|2F<`>=4MP)-Q#*>Oxre{=w-^ugWk
+cUT;MP-PFZq-w9ce$89#RL(`8<iQ{!Rn?b0e~1kX#hq`K4i{_+%5LN}zwLwkiI;sh=fntmSQF@$hDe{
+G~X%9meaLx%8c!P`Fl3jQDF-XzOWZQB;C(~494e<)svJh@~10y#)R5{W=iBS#>Vkc(bK6QTi8idt0Ph
+<?T1n429>%#Zsy@BJ6whr64ZgkVc1?YZV!bInyY8*FX0qL_3+L~NZfizhP)^}}wZwGHZ}S^cDTw2o{N
+tk}GaxN~GpkU~^_h}>PXCJ>KygW!}B8TM@0t|pq_%-g+=rcx9fQoD;+LtO1g<g&g=g)}<P8*SHE;#BL
+$I6mZqk=0Rv5szzGq-SP7JNMiTI66%V;iB&-Hw;{fm#%w+>*>*}sEwsq{u$j$zu)}0-|OPrwtjf~;rl
+kn&kp!#X#BYIn_?=CvnT`D{im}UacnWU$6-;F<lx>3O=1?sSG4pO0y0f6_0q*x=5uDh?9^loZDFvbGa
+n!TheIGqLReU37iSnOTBbCT3%N((73iP2&!jjMd2s~nKZLy@ENt-)X{Z9ra99RLmu^TnmXTasW|(Ds6
+#);x;{}_d#le<anm8iZy8+;W<6wp${I$iK3lq8}{*DH+?HK&$l~w-xNv{$D_T0l4IW_EFR7{jQ*e7F>
+XWn{mpBF1=4ReL_kd6*!elcB(VG{+8z;9p@`^|Oj?Z%JW${(-z{RNTVx#C~--Xq^N-#hy(UdKe*(MmT
+f?9Pf-m^-M<lipfXThG#cwJvUq%-H%FtFL2$=mp<S{_cSHhcGI$qIk`55BOGpFI-}cKk<p3=v<qKxe`
+X}$k~csOv*XKyUXlqA&n$EmB^Ber;kA}od|sh0qfZ}nJ1f9qo|$}L9NOsNw$)}SOnNqGJeEaBT6nc6P
+|+u;;ySG*lr!SNru{EA~CmPIX&<i+m%;=-#$~N%1ezZ#jJZgDxSNEj(WTldk`xU=SD<S?S`lI37aXY8
+JvT2!wyY64|P48`EDYHr-ha3Q{?f{7*@6)7TtE=)Aj09KD7YN_sGE5IWC4P9pmOb!PeA6m+!>-sBcq(
+oty28y6xV3@8emRzMox)d^Nl<TXuchkfCfX7pyB=s@-<T2*TZZVjGL2$qbL~#K>PUJ>*-e*M6aTZ!B;
+99m@+ceQNB6;5f{_=8eX;@&RG!$URK*&-FWUi!tq7HO*Rzo_Kb3y4?|LQv!=0cMxDBhr^aqhgC||<cq
+NAo-RFt3l5gkJw!4#&b}hOP<O0*e@oCcQQdUs#uL@Kp>poJA2g)TrCYya1(#I_@If^WYZcY_%eZ#c{G
+Q+;c%FD&<HR(-C^9|=${G+N2X*h#i_!Aog1k2T`l<WI`dIJI_w&xdx}9Gr+H`VR!RCZEAkEO~gKICiY
+1SQzM@?S$PxYWao%<@=AyI$M=!@cBf{Dw{E1xsP4bvY0z`wjMc2&BH^1}6Sm91>7)%LS5&3rDiFZH&w
+PDP2}8^>hy)8T-UR*>0Zx%Ii`LSC-?O+O#*Z0@4oHb0z|>uH0N4?xYRx}N0Ul<yq=$0mG)x3o?FQ13(
+9-0Jk!K?1=)9@j5DeBbko-+aW!B*U+7|Ks2O_~hZ=bj^S22*adH_xO^p4c|1s<YHvb!9+63{EH!B!4x
+n6utg`IE0N*YlyI>W%a-yf21}k`YX{+ewpflsH4X!-o9vs%8373cpf?Qp6QBWvg<NcCm;_p#0{8$Y1(
+--JxCL!SDTrRwA}$7K5-uHofbXOkti;AK)G1RLEZ`;_Y#xNJHyMOCG=XsW%l3E}THinV{siN5`HuA6m
+cKno-`SkmwKDo1b%jH;ES!TDn0@5AeIk7|4Sh29=2;jc^{qD!iEv8#pdK@HC|_FP0nHG;h-tsQ&5(`n
+XSqI1!x~5R-EU^vZ~e1SM728ywxC5saE)uKgN~kt<&VmnHh3qGm=J3M6wyKh<?9etd*Sc2$v<!+Ph?s
+{O8TS{_2q(-VPNCtKF;{95cG#<uNk{;^E|K$-EESh^q;Hg@r;GWR!^x(NH>ejuB$DQQFeu)vA%YN(Cw
+{zBNp>r8y?wou}zunZkg9xaxw4oFb%|!*^9^MEJjs)u8){XQHb8u&KNiQ(T%!`+MMrFRR~Jl-UpAhP~
+~_o&C4}9Wm^wR?P0%<8%l65qP9L<O^b_1L^)7#sd%*K<Jww0rWMD_<&hYoh%nQ(z8>nw%V2}8WS-933
+)pYN%*O_C#YteVyA&N_dUBcEN~u@9ip8EiU^h=ZO?;vWRyW`6Z?F3{qF8C`i07?<vmMKwwkOImvg+1d
+@)}t`HziepC|f%C*7(sTpBlOvecSnNoxHZKkA!AiWgifo<1<7{6RFmem^hzs6Vo5PH*R7HiawsU*1?d
++Z}}^D4}C4F-%9+tzS8IVww<pvL)6#Z`r1FnZ`J7iLA>_|>AE^dPw+j`rUz*_9K;UX@Bsf>6uhDSh8h
+)F;7qzcEiE7pQq$h?M3vU{PI#6~_~57c#<X~+ZZfTDcKM5|o!zY!9dBH(vG$&C5D?;&?m;adjjoHVs6
+36*zG)h+1zf(C@>yP~DZzIcUIga$+z#<ZKGxA4-RroU814#5x7Yd%n2ml;M0q>ao03SYaNv|o7#MHL<
+YPH2gD+j~tD9WN#%qnP(^!qy-mbrcUnwH(<>oxaD{`jSqu8#*U|3zY!K&Nn=e2k^<q_6DSkD;y*0Pd;
+G~?P!z$<$uDmz@+B_d#Vg?S~L`K4x;YTU$X_}H{^BcI3_%^4P}XIm;aRd`%=!nw%!3BQw+*lrH|(cC6
+Gq`9~Ej@)fkKC3&dLus22GDeRsuKD|FR5iW7*=!!0OC2_^pY@{yBvN6g(8uxmEnd3b|6Bau|G-g{{o~
+tv{_PLt-uw~1J$z1+KY9RR_uKXL9mo3r<NEJV`?s$BZ9_hbLXOA4F=;GClLSU^EQzudj&dABu`ES>K9
+eM(V1^Dh<Iqr)ilLBGQ7o*>GAPWs;VC3z2?;&c(9f;fPy#mMq{(tf1Z7hp3WZcOw~Q|1Aftq{h;fLL1
+P+sq<lEri<Y$s65?bHD86)^&BbR(J!+9(3f=7ZFB8?$zV-%Fih9L|cz&GHz0G!5X*!LeN@O$Vyhrxs)
+9YaknAdShdB~NQOlcW#-Y{(C%{wmb3^wbI5B?a42e$;+2_itg_kbu<EcjZRcn3R$~y&HW?r{o%8?`|>
+I-hYwP!QqK7yPJa;Vo`2w!1N?Q$K)RlhTR=%MCiHy(b@ih?rRuCe*o$&l0w0)^x^*9S-3mA*=w8mN!v
+W&M9<Xzd1pVewDvDuP4WCia7BU>j!QTHhkK4e7;xNzqtAP%!l0@*5AtPqNB&Gty3SY7zXi~*1s?b2+b
+s&4s5bh=5q10$8qECNe^ogGuXGTF9}b3nBv#G6boIl8fDSw}2d~dnSGhjYDp;OIk$2|1`});ZgjWdXF
+5jy6p~dw_3~~{K<ZRs|V)IBeumbQZYsUQ7(yniEqx<=Ct~b8s?9T`Anjouw*cIgKhRIDL+{<U6lT*Dt
+k(EIEfUlI>wV)2N{knu2zvZV*EUNAecR;*t*nEAek)jQ+mrSBD($3>8cT^woTBscNm3A*#BPzo#8BNv
+dEuY9SrC-7@mM-cm@!7)0ANdWT#=?jt8hu$iVe(Sx>>Ou)(%P^S9I{jCeJ!5yU03+{6e$FgnHw{Gj)b
+V&#dtZcJ!Df)t<RfR=jyJqbhW<9W!g<d66>gP1{|hd??k3=EC8dOU*?IMM+%Q7mM+}bbmDi2RB!v_>a
+EX2x;j>saGG&{WA5>da!<N_zoBopYu6aek$D(fSy>Cu=~=d#$>pac0}&Tez-sU*{!3}sPjjOn{y@HIf
+j2Dbb*Bp_Hxg|>HTbMVSBYF38ryxJ4H3sL*$@y8b(3{Gx_5y|Zx`-HlWm=51KzDp=luG3tdc&|UmS?+
+w5Lg*<)+xOp5q)6_PXCY0y3Xyf#q3w676}viPXpQJ`#9Q#9T&|cW=XtRC=|G?e>x$Wu5FZJpv&v6G~c
+gl`3<OR0F}alaH?od9V57K_IWsts4>X;3P^g5qmE<KS7v|>x^I;=d(NoVGL+^XlioWs1m|^1y`Qb;wq
+HF#+_OANK5=ZVI`9|?(!nGD}I0MUxJ_d70x!fbV0SYq&z|=Fw;M2h`nhIk?l4|f@r%nu`9iFqdteOpX
+ibY8>I4jif2hlpn;4;hEcM=Dl5+7+w=1oinxljkLp6kax+mg$zrQ}UhEpxlDppy3%Vj4yYvGX-yOCA5
+JKnfw|oD$?=hJ9<C{BvHw5^feXV!6|BLH<4eksE0Gz;4n!#C|A~85&jHWRf#~{8_(596{Kb_zKNHv#0
+wJ~6%&^?ue`A{5(s7$0w+w7Z{4YvSg_IcCHGCeGc7sELM+ONioWqF9fUf?MD7r{aWRz||)FE|o*>7+0
+VjNv~4_mG6`x^NZ;3NyzT3v;_5>R_<N3}`W4WodX9_{%QBEjS7_f=~{w0sDV5C}xKGE*9ny;LukL#vd
+>k_+@%myueL6|48qe`)sVw`&xF`irbXfxu%)3TLL1P=K$Qx!_hP)eins7`c6%}0K3CGAHc3zcmJHAf&
+FF^w<~3Un*KZc9=59SSiHdvDb4|S6$xQocYg!e;24k(U{i_rs|g^Kzjpq4L+Q%m8Merh7yK$e==}u6(
+Ga_lFZ_N!NFX+F@|Q86sJ*j@xa99L4en`{T|+S56AcDjHS+bczfyhe<QKbuH^lD!O8~KnLL};`{XT9%
+eipcZxaDIk2v}S6{u+Ia+t8H=fwn~ETYT~|z?LZUz6SAmE>XDaD_c^Qc~O`AoR{yN7w5O$y=97>>e=d
+2glj}dqwGFzj*^+FyrP2baAWW6ZDp1>d3|3$U$MHvWMX|Io+;TVZ2iP<MSh(NPP^6po<<(wqhB%4IW~
+mbWvgS@_7Jui-i<vwt)3T(UAOx5(5%&~cE2=Next!bem3j<GYwAN<-Ea5w~$yV>Ve*MExkpgV{VZV_;
+uyJ{^hyLFggoOehhMb;`aneDv9RNSuM5)j`PLsbw2G?vSM95Ads!`gij~!QlD&(!Zp^XHGg%)VZHY64
+in5HIEi7yF;?-3+r_tY?rGC$*0H@!V)qF-2gJ5%P-lBG_ZzIgQ}`v8hr~a=s26`t%aWE)yZmpsfBt+!
+BtF@Aza>Xtw;V2U+WsM3hD!@>S$`0*|6~yOuUX5lLE>8j2%G_NB2@5FG|gf-$FV3yL2#gP25>)s8zjS
+gN&&&nibZf<1lV8M@LwDWnG*?JMPXBAwir8*5zLgrhM3Q-OEV1a)Ng~T!O37+mw?{f&{l2fbOGm}-a1
+$)$Oz*QrdR0*oc<>$0YxZ_Wh(VHHamfQ6f6duJ&R$P4-V4CmOjn|ibP;dF@~0F;Qu!_HgI|NO>#^@V1
+hu#z=IR;bmCu*rQAXaIDL$zG<{w@cW8K{8yl^=V7FVR=IUn@u=!qb`fH@P8{!92WXLD%1d3;I@bfi(3
+37N29x)DX{{&(n!hlUa|HlyXu2B9xh?(CY2A~OQEZFvzEk+gxIG93)E^vQB%=bd$Pay`b8u@@2J0Cai
+5HoQ<verd24S#lLUA7)ceVvz4d_Th`z$<@>O<zM3@=vhoD`=XLzmwE%8}_z44iT;lNvgHc?UvL?9=EG
+Od-W{s-|EsXLrIwq(*8mn_fv4Ir>EzY4}Wk=obQh4&Un`CYZQ;WovbC)e$66&FkfOuzg#UvQkAvb72G
+-E?}{hx^nEqb+4gMBWH?;5iOd&!G(H<!^IC~3p*@A@E<+-0MTg|}a&w29-JmId-bFUy+z$k|&Ma2$#4
+sXvR@lff=SJnZ6$5+A%af@_K5p9_!d)7l5tYq(GvH}|UAvCEx&jQXDC72`whaB8_;IrqSGsgOXI4lU6
+k0s}Rm6ABtHEt#E-E$ded8<@k7;zgL7flYIwHc&9B(XFzihR~GrRb=gEV7~eD^J-4XAK`2yVNvI#tNw
+-TU<u9R3{`M&5C_@m|lXD#lFetXCzuO~kI`*D3D(IHGp<r^&&CNAQh%ZTQ;0#6HCd()DRl^$EjBVz^g
+TG~6sj%cjCd#h!bD@1NT--SR{u(KEZ<DY}Q;#ZKyN&pZ<1!+5!!1#sZ-%8t+0c$meI9nhIKW-BuN%Q}
+2@*XC6Y?QoW!b#zFvs7E@s+y$(*xe0e41Z|RS9w(ot%=r{4ERj$g<GZ9(Micm)-1%{xyaE)rPc~j^p;
+|C8vOTGmUQUk((~4{p#GCQ3;rQrTudG+z9||$n9=vdKj-54z%wu{yAAU2~4DG#ehRh74W`^}44Lm<Fw
+hz{d9aUh2b6ls*+7~dr^@n8ixr!Q8HXd)cIJwlr+@9Kix6+FgfRA%x@oKH3`;x`@kZygN|DTgq#{VL%
+%lsFlmH)G@|4-7&-!M-Wg|_E3;GP7*Vkk{9ERKOF0%aKrV>pt=IGiLtizxs>q?XQCXyckdV^<=9rBuK
+g0r$(6#BPd0Ef51IgM2P5N?^RelCX6autE?opl_AhcnJkk*k=oa4h-5ta2(WsVvD0x^fRAVxTFYLv;q
+oCEQYZmx`>0AFYXWU!6{TiF&xwoWs3m>w#>Z(cX1I^%3_NfI~-j}FVn9u$alfLH2q8F*}#~h{FohSf_
+)mb`8!VzOtX<d94;|ZIRD5z>ra{I-$*ILj56{irA+*jlrqdHBma?<GR!C=={se6ODPkEBad&@<&Tsyh
+-PGfa6hJ$zXT=!Jf#eg9|5%VAE%T*!lHkgQikCYV!%<~0<Q|Mr)nD{2fIoIyB7qWzwo%u3xYS%M^sWy
+$7^%wwLNUN<hIiOy5TH0-XZoKV_d1Fqbuw3tzbS{_PQg8t-UL?lWN4<i>zmxJC5_i3biA}ljLzLp69c
+Rm;ElhBHd=!MdB-?IZV2QI%}gBO9N-zbZgHSX-@w|n)S!Qi{^KO7X>%lyNTsy?X`WPQkabb;M6PGJ-Y
+^fR}oQsd+y-D5|78cp_-zh+MX9icQK)N{qP7cdO2#8=2*}2Wmlu<ue<g<m(i|L9pq^;^(`4EZMV%IQy
+!<OrtNcmJY2(}zF93d^b#o@9Z^{oH)9js(8%lB-V*A=Md(3b2B7txTRZc`G-j3e`0Wus1%9>tZ9H+o7
+P$;h`g7>+U%6+49sT_gf0D+JbG+@&`?MSHP4y>N{O%FI3pM|XE$tnJ{^qj2!68`2q!>7>7-dO{#W;XM
+G{I0fO_C^qF*wbB?l%r0Ct3UjF-QQ(Wbt(%p*#qtD={eh!N_GK74<WLE3qg~0b?a7s5Js$!QilC6`Tz
+GA5t(i32p>{f@ZKV$wW|s3iF#k)g2KKuK<2vOA-~1SYF0uB`I83x_AK5ONunfpr8n#6fB1>&JBR!GK=
+dFKrir@Lt(2$v}kGpfI+LqUvt|Y3S4RR1BXnZkpoc$WKEW&W~k(%QM4blNME?^;C#YuXWsq5qAOJWT$
+ia+9~AD-ff+kQo&pbco_-l$^}ZJ$g0n?{t+Fwk`*?Vl3eED@v~77D^7g<l%=rQ#B^ct1P`vN8DV&9+E
+gGaDswe8}&Q$rudB|5+|7K$F-*P<vp78#J?{c5;-7{hcr+318aBKG!z75lQr=yeh#i#&+xAN5s`aaVU
+(PiqIXbD(SI~_`%{6Kp9tJ<;h<8R^}?2x}gyZ1Kb1?;e|um0zh>5u6uzu~$5DF;SABf%3c6X!-2_WSO
+FO<uIdim()o605~Sm852^?`i6q2M2vt5UzKtA_Al;8@eI7h4TsJJ0tI6`mBX#)z#*TZ7Wl|9rDAf!nT
+vBWm``@8kbFxx1NS*IBjGK>*<Ojqi5mhu9{Li9E~)ke4J;*7WJG_oNsjzH|fX|X(i9;L7vIMFxA^iLv
+}j`=dwg3dv9If+s%0tb_9D)D9x3PN~X1&(uZ-oOHRIy#*MwD&gQ)CDO2sLQc3p6S}?5Cce?Y{!S@?h=
+QG3HnEu)Bnn^o9>X0h(;OPVmr}z1wjm&s^T1MrDy)H%N(LtIG=ki8|i|X^#yXzRB-c+a(<-KC7JB}XP
+bY6JpujQZfPhQ|%IlQZ*kRRkxKTPA2RYOHpQEXMHM-)cc5WpMxOe@J<X-C9!gtMWXJkh&$zL#8gc=8B
+JzAyxK6h>d!j-<pFcUjTbS9E%+jgB8e_IS+I=41<bi=<Q48{)~+@d8aWp5DD+%X%{GZp%AwUf#Yn99q
+EPF+L3e&h`O*t>3k2$op89Wc_&9a+;l{ojdk-&A9m*8pN9V*;e*t?3X>1B+~N7dZ#7~wXq1FoqFeZ<)
+oa5U%LwH!TZ8r->X&ql1e#E4)}4dhbwM8JgYsYY_oZ;kTx|ZJr0Gd8-!A$iP%o9y>Om<wkI6{3o04xs
+)B#luxBqHd*LMIMr6#>F+O7c>M$eZOWSOu+hc9HwKbTLoCy1v-gDMaaZFfbe&<Zbb(tqh+^8E|=nV6Y
+KkW14EH#aQuMlC<Ml2Z;%AZHf*^c=tIFt9)dU&eOVMdcKF-f{h$FuY}hUMxwssSL-%>xfjkJB{^xmxB
+Si*a|qzu6@r%=x*Gvj+G5q)-a#VoqtV8itPzYiT`Vo}Qz_AOyM^XIrw~90-IlaM90XlB!ncT6~11`8P
+8EAHl)9U(>OVu9x_C7%TY?0vP5$99#V_xXvE}t$(n_ucI!Sz(@j4JR@0*A}ExmX^Oxwj-@CR15p@FqA
+2yLStAj`Q7Gx+pq0I`|8SX#mMvNT3`RfLLZ)&2U*zW%1#nNY3{n9h5Su~gDguK68=Jt+A})b3R=g;}@
+dSE4#@xS%42rynpUwZIxy34E8OR31rU)hw;$>JiM!@<9h_0ZPjD`gUaBXr?v(bw+3BkcM1{}>zE{bvr
+W*+cmh7DK*oQ(ZzwRZvQ&g1hV>e`XgN+-vIo^yII>G#S{+x8Ro|N4G-=HHFDV8Dg^lZXqj<&Q`W`CAd
+!=`@<-mxybgz7E?2DOxPR_!&@V%&(`1|N5%B@E5@Z0;7o~ewja7rr=m@>@5QOGG2SmN5hfHxAEF1$}8
+VqK=}G0|B)B)>!<LKUO@QzA^(vV@XK%EpTB_c^+VoYz@+3&A&<Yj({uq&^~g<=*Yo{_=k1$&d$6bOQP
+m^ao%coBY;<m&Yc-;t?wrE!cdMGe>HAu`csJTL!cbKxcUwEDFob)%>^oVSuE#(G!QU-6RCR8ysPG_$6
+v=j5SQRgLJ?!y2$Dn%MITjYwl{yV_$`c#vGOp_~kMuDQ*`GRjeX940OkdxQK5T8(cyp9}N679jo_n(p
+7)m_4mcN1K-9><(h9!y+<q<^N+MiQR%u~5KJL)}bOP^{+cEyLCrK{q^s5UunEWboT%|c`8KJ31PBMA)
+Q&r1mZHph(!=1Apk(Tlo;-gnuNo#7!>M)Z%GsXCk5lw?AJpsS@K6@%%KZ?hvgZ>T5HQ2jy@^4eGKWQq
+kMLT)p5f48j4=G&ygq+@I*g?KcZeZcO`=rDUVSMfv?$%Uwy^wWfjfX&F+68L?)d59?1CI}zQfVuTcbM
+>}9Yy0VS%hcDSND0j%h9ggzC~ZIQ-GjTR+Yaqz{%=4!fU?~^UPd+CUVr?zH!xD~*W|zdq5Qx9M;G4y{
+r_qI{r~EQfB!#kFbDB|_%`bnV06AYpt^*~zyIGAIL)Vp_W%AJO#fG3`yG1!&UL>{v{NLB!||dRjeRQR
+iYb^o2bh;-Fl7#bY_U6l%55BKr~pg?U?O6e?_k-_b=ovkX91L?7OcdUQ=;UO1n1!3G-$f8RPGZL<{7}
+%I0jB;;C^OJ3E&nwB!{s1He80HWhlgH3WeuNQK%|U@C8y+=z@%KP+kBv*cdDbz!~2x)RxmUToZ@GHZT
+CPBn^}N+%Nr)Wd$ZOh7T#1YM<nm;(7BkF(+U>x39T4U_Kk&b!6)c6_SN<)Xke)`Dfu86Xw?id%)=d-I
+k}p$J^Y-19Vr)1oB}}HIBWjD*ebj=8?wGq&*bT?b?K%QjN^M(Z%A^qBZhfwcak?m2V|*ejHn;c2P&=%
+C}(xN}t;N{oM!C!nrmOtK4cm3rT*sYV`#f^XY94!f8B6`t;UI3~z((%wKK;$cQ|?8;)Qrr|G?k6wXEv
+<qa07${3vYs9~K(d3^8EnIQNXNQ8suCwDdAz;OBk_THkiyIMum9_W}obcq&8caNe%{8|LYVGdvL>AdC
+2k<S$l73?~j-wi=`U%mQp_5paB`SyzoC!)*A5UJ)vmm{S*|9I73)!>nD@(-ew#tgNKEJwO$PI;w-9xV
+k$E9XAot`#Xf!Nz+ZU-x6F8(PUpje}ejHq4Z+8)o5Hh#7vHxXoaMf%uSK(%M~{q)GUgx=Ww#xYi#2?9
+&kw@6|}R#&gol2p{m}S|-QmnJG5-@y2*2d*78C13#{mhqP{YC5O=<p4eED$V9FehN;`}l|~3u!?hIR^
+4K*=D%T85p~0AzyA#uz#;Wy23^8nO)=xhZRlB?pf!kOlvsL-Nc%ARhC!0;Rr6T$+8MAp#?S1c^n3zgd
+W=OWNS7_ncd#xDN@|2ZYD_l_Do|%q5=o)|IM>ZmT&ebLy5jI`3Itd9cIoj@V#crvm^YmlC;*Yj@i+B|
+_mTfP)I|>4unYX{L!HW_PVl#m5d@jqsqlcT1k6X1C@qRB}M}e77YurFi+)BbI|L2AHZMSBM(R&6Xdos
+m*6l7fp(}1d3;i6at5)CS)Zn15X(Sj)+PP=u7Wg>gGalOiYeaCs)q^UINJ7tq+kIUWZLccqlg4ijj8W
+)k3$X?@}UTj+Bb+T5*&N#@Gu@6h8Ida?N`bc{p&03|c0!*>@&xmwOLzhM@AhWGCi%0|(>$$k;&HY_+q
+~kzf>v>R<oFO(=P3Di`TAZSLu_OA`!*a`eV!Op!P5bPyVP{-H`dt|fjYn;>Tet^%o*kG{Av?DLHKv*H
+cAG>)AGL#3L)Zvu%hBO!x372M%m>;2-s8x*RLSkorQA)+EHO-{gTJpLvrht#KY5Rxu?<K6sXA;h_;YK
+OiNggJ^Sj;30GaJB47)#)v;kBX{C^E^NqYNUV*Tks-$DF;=tS>${p*u_qp?HbAM8`>XlPbR!2&?WLZK
+U+S(Gf6Md;#ENy46Dz<@~{;w1Zd!5ctexcECW5OSe-EoPv;8BXhFppQkom?{C*3IQ^L(48zi82j0%C0
+MSA7c&hKt_R>S5y9#O89{)BEzcwj831BK7fgC-zDAcJODXJqj)+Cfw-|8%y38zQ(qLr~4GR`@_Dhe<7
+c6))A0j%L-(0-%he?32uporr+eaggB&UWjzI#BrC&DWHvzZ5~`-16j_!Us09(?T$yB)XYyZPkC0l-*5
+m2efD^&G${__}F*kE#NX{9dN@`m@8yc<mR|%%?qeKO+)sZ-O^EP(E!;a6q{CowU!X#z`yr_`_1#-J`y
+zpt@=SfWH8a=vjbVPDNB(*Qk^mL#*C`{_<B(2@36yZ^hiur0QqQNcvkdQVyGuz>96T0fmFO?O9U0+s-
+4K4Y{L^x8U{cl9D-5vc5Yvn3f+_GH<KR5$|QyoYdf1m{<9UUT9wEOxAuN{o#~dPTg(r6rEWUHmQm_6;
+-}3mQwQSyH-k{dyc9+`=PokYu9Hvk?l`7MM^?998X9<^&V|7=P7?UsM>9=!=V&Mm#~lVl{$;9A(;6l-
+LDh3x!T9#kdB5X9G{}^Cp{|-GV(AolRKNM2Vk+{aT{%t9p3O}eY~D%|8lvej6OE&oESTm#>$f84&26&
+(qMB@bZA6{P*!v!HiAox>-)jLgZ)c6p0m>>!W%^{f?UDQufPlI$1K7IWm5Bfx!B#q&ACW#;Tk}TDtR)
+Q*o<dnk>_$_aq<(RDYDvpl-kZ$iajTy*DY%U&Z=3><Pb@_edwZt_TX9X5w$)CTk;at(dcK7b)R_wdu;
+GiuWN@n$j8)BhJX`S=aq1066VszRqLHE_OoLfUp>AM%B3liZRiXW)#Fn~RS*k=Vs2>%r|8{rWij{HaV
+iaubTAh0&`H~%WO~@$<tJBq{(#U=uyI`^=b~>4R?sh9Gn-iGv<+tK8{1?bI6lRfYJI$>b%uE$9E()2P
+Qz3t%hKg7lJ2SPeD&5^at=By8dxn_i$skz48c6T+Oi(WJr_(|i}vgORr~wrDPP?;xnCYCW*^dsKqvha
+v!@4m_EqWld3I(s7k{1w2EU^y(;s8%-4G=Y;xP}*Z>T@=rv^cI3m<G=wr62u$gwIYr$p!Z@i_CGkmrq
+%!^ZAd<Sl=CC+o<LNQmS`jUWyk7RIi+k3gjQNaV%{pFk*n1)(SRf>o{ESs0*GLTY2Mi{kJ_2lyAFhmC
+SK?y;-+w@a6)?f#OZrP;Cmy=>m;2=ZNX890e}H&2h-_OTW6tI(54=C^aju%qsZu3#}QoZD!<UX%mX?O
+rw_1|h|@Vx!}{n);*VTs1rO`FTZQ`p{1T2#~U@N_g$y(xUNvUI?#K3`e9@pUrcCebglty{W7voC!`6y
+)}#cF*TOAR($i$pU#6hmf6~Nm|zS~5tCRabzRvus*&^K%yJB(2X%H0Sn5#uadf=xQ|e{T_T@3rSGT99
+%U;zn8EsF8t0(n-2q!G593Nj@WAxl<vqkb-3CK@%&M$s<w)rd#nH8q-p7QV{Tsl!}Y?CC`=uOt%ozcu
+lJlD26W^A`vB|iKevZ4u*ym)fk5!G;}f@m|0?7qCc1`v48#p)6<#%iw4zQeun`Dx+mNl-1kJ8iAAc$;
+>}u$iX#^-xY$t;{;ZS#*M4E$}7`xr4$ek4+2xfs$xC;WsKE6@_xP?RAyg)@8pxR5cRGdHSp`wveh~j6
+EZQPa;vpI1oMT_2kAXqWB!Eb9P+Ce0$ZZambbaT!p3N%(v@MA$zQA?2c_+^1*v$NE2rlM8Sy|gWn{fk
+i~s_*p3@94)+g}s!pawTyO_$ov+v+h?OEDJ|h4|cIr~{84u(68MvpJn=5m&^QV5kN0-*DRu1dFHg{J)
+x~~6-x+vJ2!@+*0TOvSS<KtS;K|RK{a3^Bg6UZHNKkRN6W1pUmp?RyZR`HV&oypj2;y#-=qn2u#UTyi
+mQ`rG=lyqq?If%yIzJ~vM`@NC+&yM-9Hv4k_FQ-Hi8MF%HXqc~0@h?*`vL$%QQm8gWmlO%UC=b)mZD#
+OrNlGjkepnz#p}+#qpehhVXBoca|7lp0pcd^2y@)WF@S~9NLlnm0@D%W94jQe2$SYn3fJE582m+QbVi
+Lw;(9>oyE@u{Bb((?aB}r(s36>3qZKlu^4ayNo7>I#2frrPxjKX9Ib}<j>uckz8zl*>edzKvp`cf^i&
+SY%O+Xiy=8H6af@5s*kePUs3EfrR3<J`BYc~}0KRYCa4&|wJk<$y2w34b(Z9cm@!gJZmTojKp#R=%wW
+`F=@%cSXqeOZt9Ae`SUSz0i<9&Co=fD>WhWORffy1+ERR_<Wv>IGPhLqEB|A7Mq0J@1}OWn-tH_YgKM
+!WIwG6yRtcwkY(;<p2>2}+Db8;Hxw;K4{QI@%b+rfR+_5&P&^fr1oT}i-0dw99=9`6m(%GoSJH7@D{~
+t)IiZVBft%PDK_+Gy`PF=1wG2h!_pGa1EN_}iIfZs+f={sQOUOBvx-epCz}|9NXM+B8x))>FcE;Tu?z
+^Nr*}GkctCCt=BH#7c=XmnxMn!|V-D`!1*z5{#oRx9uVwp<!q;vGt=inVzCDz8F(Y@-d3AL^_=*q}1f
+E=s$!W2%qBnDB=ZXbwJocWEuNw!l64nE=uFOCFylx0=$0(`H0V1LGrc{cvu0FN@-Y{Cup5=D<1#SA-b
+_MR;V)5`x*$?!J7<E>dHkxg?jL|l;ZeGw4?h|m=@A~t;R_MYftyjPX_Ly!O>hqy=nyt5sATpokBD`bM
+gj%SDO?qZ*BegF1R9(W5ls--mWqGlv~aitI5LUBF9X|dV+34H=KSikb^lfM_2;GSsKc(}0eBK_AFMZg
+UMJa`9|ea0==19aQf`&T6K;d@L3egEKFU9x*Yd#DbT-+Ax{CcQ@Fr(SLfiUQJPH1puEDX#M*KR%khAX
+$226(YY*V|K#{H1gWkcy=>KgEnZeyP>0YrOd~^TQl-Q(YKB6kAt$V)|<;3^Wtd8`H-lq>cJS)Dt4wPB
+Fh@jb%(IMO<@(xe4`Ay<1z&49@i$3vA3$bMf+^dIW;FW_DV1pU!?A@gF24)<8h6M0>C;<*rqdG9xvj9
+5IHaBo6dB_@s&9MdW_oQ`9nmJ>L6~t6ZrK`3ct1LzS_M~0g*4UbDqQ^X=!kh>^aWYf~P$H&?oIhJn3u
+gdYAl!^M?z1UJnWmMPs{hZDPYo%auKHR1YhCwK8V+VfJcqTA9zlm}H&|7}2_=BIj0WDJn)W-u6_O|2|
+l5`o8%$dkb)ox8H|+bZrVCufDWZ+E#u09d!QQBi@1Z%h6xsD93RaixDXM8J>pW;(wOF7V(T-+Pg#8kQ
+Xtq4v#NlNP004C8&Q<pS#15D6H0#5j+RRKxBg!E(}Z}aLXj=ID%*ll>p>2(J_G~hlu?b%l*tBG=}wtl
+!O0+v%~ZV0d@PZp<t1Da?}FY7_5%4OG<`a`v2G^{{hE3lJNL!L3(t-S&V|H3LUY3S;&_uNW@wCBdc*B
+sonaAtj2<=$ro#@kIx*!o0XvCS4cv5@R9BV@ARD^ma7=HKI-rC_US5|z8al@B-;K8%fXQc_66LXL&BX
+6i+|hhV7Yiqw#nIj?cDwDOJ-v-q<jXriW;RcBF6hjMk$Bsj^##h8`!~Q-w^z-VQ>TZ(<WBChpFwGUF}
+}K2V})^0CtM}JLm?cYb-;e7ajcBngh>;`gpKWbyx5Yi)YzHzT|iz5Nr0$#o`MFkp1_qRuEoSrhQ4y0v
+DBrjyl;bep&5sRHIF9oWgT4*uM32#Nc#3qNu=<*!}Vp1kzw%dYc>s#2$i+LUfs9-h0$o?erCO<SUw?M
+`k-%MM>*?T&8kdIz+z4x{R)!D_{WY`pg<)Ud;$EJe<c%D-QDD<x}4^xFfEti7wSZcRW8+`Q~zL+DyPb
+r--Vs-QDEXh+5om_Ey%QAv>`rR7G2n+JHah%MeN3akpBN=qXM&*K9ml=zJa4gXa*TlgRGg$ezb3&{Il
+|bJ;i|$0%2hojPA*!hhv!(ZsW;qmH?L*ZW~`?_2#qjZXJSUsC8irjCr7eMvmpmai@t+3pa0nqBV_L{c
+n0DNizQYZ4=7_KVfZLFB^`ghuN9XGUr;Eb`t#2YkO@3MsH6XP#8?0_B3HFc<6sJbC7idBT4sBh*FkVb
+Ub+uJJrLXJ86fUgY_B1Yb3sPhjJm>hqx~G`uSFzA2#a{%90>_%+F;3gOj}4|Ad5H3L2Xd^P#R6e@TCx
+VXcMC_KRoe?I+qVibW7T&@i605^2JTEP{7E1urIYVa$c!xcpE0xHVy;ECmyht{mfa$pXY1|AsX@XRe-
+>avm$t_0i<4s784Q4W?4z9wHz1Xs44ARL7mj_LmD#SxUk{Rjg<6hZ86Z=XPb{A2uL$L(8qlp%s>j(W-
+GcY)3|-4DMDKIZU6T?3nYE_(tFLIsiiCUJo%Jm7>?T2gvFs4crSx3Pc3*8_dVSBCKRyVpk(><uCR*|E
+sS!Jm|FsLzH4e$ig0tn6Qv!vzrmQ{L2Rcse+eV6)Bse6bwub(bwbz2$4r7$s47ZfrFz10r#;VX<QukN
+CEql6Akzkt(k73g_5daoTAtM#*QGydR&wRy_7)WKF>fK%mRNHr6#+Z_c{sGxOc$yK-Nu4M!o`FvVJ8B
+-PaKBn4N7pc;)L-&F4Ik!@9ajoWtJ&#Qe(8#|g|bv<fr@R3R-op{_e8@YvCC>PPTb_X`X(Zdw-Y0=ge
+s=WOC%9WD-2%p@=eYErL(uM0l?hmi~^VJ@z!g$b6<O$Za2iT%J{rzy&U&kzH_N1?Rh36AT-U<d1)pxP
+P$+DR_wVVdQ%V$E5@7<;k(ExFxLACZR{(3#*^6$7VX!l!Qfq#D-?``){{D3nyzq#kTz~lEH^&Wiu<Pq
+OjSU!zISQr^V9U-@LLcH0!C9p0*;_w>~Ku{rcw7~%*{=B*Z^%5a869?>{U1E#`hp<m3&>JUOLIx0ufR
+F+3><C}N99XLP`2@0T={aN)NW(!C1PDI5n4iZeEcnDURA!*~qEDn@r3O&=l!4`x48$ZD45a|&rx)iMs
+BL6nnj#`$6Jz{46UZb3LSpe@A1m^!{L{%}*7rqzxt)-2+Xssws*@(dshh3{15`ig5}&^J04V4W_vgr0
+Rh0LkoFK+H!NNUeIL5SJ8v6A2+t9tEzhFb(`)ZJ{m-A0ngM7W5Z&vf0UH1swgqkBucKx;Ht6O<*oT55
+j`I5L%WP?%be9JKx+t%zIj^9BblQHMxcsmh+it&%xL`t_Q+<0#mP4xhzti(fU3Qgk<E}-Za%zMn;>FW
+9y&tmVy#O6m2zw8^byzi90i{u+}oUe|UKAW&F(6-%N&pN%%j6GQxc&ZFDwyW5Dn_seIeN?p84o|h8+6
+q(3e0bC!Wq|}f8J+EYePAwE<ErV!^I1FtYMneB@ln4BGn3pG?~_z#8suOwj*6M3VM`lUb_%89klW2gy
+Dk^c<<m@CLEPVVZCDX8!<sxX8NQ3Lb!wEJc)JqAIvqM9qyX!Y6$~}$dR-t->SUd9l)YoqKF5YvJJs7<
+Z+FGJmF&wOH`0$4|1Sl93Hb~*roVs<sfyg=mbnz%HO>82L~2>qOZmm(-Oz8QUZn7c!U*BcA-$YsHsjz
+vy}o_)QA(*7oVjf;wAW76Ofz=&-01|uJTCVu<M&-1->_}4Sz9j<Z-=RQS;u<)3Z3;PRGfN>lejshMW<
+~_9=99x?r#_@z6!|wVhD*;xR-H7?dtTR^Cp2m#vMAv1*xyjsbdX=oQvQy8&Rbt-za6|bRjL|qqt2rEg
+~0A<@<OmZ^X1vM26i2gB7f{Ec<Ak_1!uYC)a9^dyU*jyOV4)u5dFggVV$Qba_4wNM@+hiq5>wj)yDV^
+`t;tP-|>8)VZv8v~>?=2D@CHm<+QMS4h&8@O*MJX}*BIhUkLu^$>V}2R7_9?wfQGn!~+Z<T32sT>OvE
+m2z(y_V4TX|NJx`Rs0`M@ykpxoLafa%~Bld&cL?IqA<If;gC<JB*YSE7qbj&#y`&#11=ep|Kc*J6bBq
+LBcL)be4CY#LcRE#79d(?Q_vKw=7UWh!~PW!Kl44!poAbzm-);oRLuhv!a|shhG4@j?dywy4g|c#x;$
+BW)KM0)YH|S*GJ{A7NIQc=RUK@5#GxfB_G?6mVDx_Vew8WqKQSb4koUqipn;b+Lvr%7mguikULWQ+F*
+GwrzEDC3@W(PaZ?0d|)=QU}iNd=47poZucHZUVUOM4>DS@4LFL)dB^^Jy-|6nxzl}cbMzcz1X<(EJE{
+gBTrW&RX|e>j~>GC;Jz+hcL<erb$@rZ^TH<s|<${J_0zeNLYb>jfCauyZ!;j9jq$$qJoYB}5j1WwvgK
+m9i$XX76W@l@n#htR<LS!XK8ucClBckL&ZO+!Dd_$WdpJg6sR&s2;{)Ur%>e;rN|;XxvNtT+_GRs-il
+ZH#X%gMWIvDw-YW!ulD%bzAmpqaSkqGB!H}}cfJ3pRexTsiNJW~bb<Glk##I}Fz?<`@8ez_wyqfIN_V
+mj2`%SCczZedgF{YR0uxM3anN8|+xhCWZe?5~&Vhw()pc_`F#J@PZV#xqijJbiK1@A0y{0|y^XdI|L0
+BV}ycj&Y{&F*#V_nOlI>qQsz7Iyq(uC|x7a#ER<E7)Hhs}!~j<+~RMDs~M9RVN|>}hrR*7W6@tg*K)_
+71bX7M)3>gHqN{aV2KKx#P$sje`8p0QWb%#NQGWTd_C@LA`q>p7v5>VYnwuR9z+~uW^|=GTZ$+O(<0l
+RV^V|P24<R55pUzdFzYBR3qCcj56D`?bD%gM6!u)kFlBj137Nm6}qd{4F+fm2p6Owp;j8Y%iDObOsyw
+3OsN+pa;8r4u)-U?ajz}u(YJg>HmOUi<ik7^I44CD<0|~FKWzs$<o!Ivu64RvQJdF#(!B;Tqs=Me#_O
+D7BzY%|(!6d6Z_jvj?eQ1&KDe8JKBL%+E$r8AC9Jz^gRWzXvCrA+kWt9v_;_7O%aOvfX%vN(T&q*WuN
+y{L@ulWUIeSp*BWK($=$_J2sX}S&ny0(1aEMBw-Xo@+Y|I0bQkT7aNM42Bg*3m`e;=ZFQI>S^V7!ry!
+~gj5)8X_#j$8dZ9QxI<ALB5-KJFWvP88=zj^G$LeviR0j{FpOz+TM0$*pkcBLdg}MJ>K1Df|ZP?2ynb
+HB21;S&1@5Az%S^fiFxYqoMYUW?@PdD$NLJngTEhW8ow{YEfac3s|r}>qv(d-iv$<oR<sXDX?s5j9Qd
+v90_?9w}{L`4(3?_D1ysFyOa<<3qWqTXz8C?n(dhsCVR<c>@qZu3x6pT>mcw@A3|>3_X({iTOrR+pHr
+Hz23sKfIPT5UUa{n><e=yq7m#EFHdB1jXG<r!+Zg8ZiTxsA4&INSkni%Z&R3S#(*cfOD$SR1UFghmn=
+yafwyXqsU&=pS3G%*_A6BC4;!m+8U$8`cqcS-F6Kd;%kXKC_vwbOr{u*%J6#@TvIqORGB3W~p5pN<NA
+Uj)J)b2cmhn-e=yBmX`T;AOn^tET$R`Kvvmzf1x94y>SJKj8dEQ1mAl@oKbp~C(k@!P!FT~CfrYFlfX
+800Jj=d|c0pEv1aeU@fZONpoBNxSEP=qi~h5>J2hM}Lj&mAofXA~jaFhoK%G$QSB}s7+bZxotNkYW1!
+n9{qGQd~ZnIXT0u{!}+l}n(J3{c;Z+264ZUF8yHm{Fy(T+p7<@&p>`q@W-~s|jb3=Zyua)w)6O)X%PG
+^3YIa4XA<uzmeQlOpvnoa@oyGlJ&-}3RBjn6*w_Y~`%sLM#+nf7RxxAuLe4;^CugNgnnCLMO4^0+Tnt
+5H@_nbU3)NSq99%tb!Vrb6paA4eMJDk=>lkY|$DjeNQ%Hspy9)g>B;-3Xr2&r%pfj^5H@07FSOxw-$9
+Ly7vrSYp5RxFk&hrwgde0e)d4_7?2F@Nl<+Ml$i$SK?PbV=GSVRmy9T!-PVJ+{4!opl7aGn|m~s1+Td
+NcY4ocvkE<TAE4do#6DHzd4H$n|m5N=$E4(8yyuH*B$$;p0DFeL$+KTo{ssA7CDKWpB%0v(>kW>({-S
+T6VXvwFkDicsi+Cp&*!nK#s~LW#k|d|PI8zc0Y9GFF^Q&lGe<{RJIkj^k-R=%D^JJ2(yQj^@4RT--!p
+8B8Sm+4;aQ5pW9E;@r}k{$=OMS@OxkT@N6|T^)#vqi5-4JSb+(iFDWNRBlaN1}W3hd8o32add-yzYrg
+n>Q*Ju=eL@a(ukLH2|g3XzygxrEjZr+gOaW{U|V3x~YcdZv7T&zH3J0Ot_wVc;7!m(Rib66}=;CplkR
+IdEV5Jos?{IKIY`{e00l9}TzC)WzsJ@ZiOj@6ZUJ~ZPcyL-8<oZT%QkLhVU%iBnk0%F$<_x0g6KL@gK
+)OjY5e7Gxn=UD8+;yfNQt~{~pYTg`E)t8bn@#)~KXC-5%rf9H5XBkw_UJn(W;*`B}wke|1ma+nzSZ2h
+Xy3>-mJ0%Zvvvwnf@t97-j$<{Oc7M%JF7})c#__lnS&t?!yZrG)F9-!<i$qY?&XFTE=NwYGj?z<Z`{!
+5{t)QffOLxD=8QZ43XkIa=5bx!TtG#xzKvZ}^E{}k*tMh|VZpu*i64h(>W_KF3fm8%iqDb4=pL7<)5N
+vy1<xKY^j(Kz{3ihsVFUAcJ626&N_{pPRue94~+pW44scvsyrmk?<O<Yblvb)6s(=;IX3wiJ8RZ4eMa
+G%S@HbCywzLXXHd7DhnJM7{|W*5r6Jyd=#ydGV!>WZc^ldJ1;wnnq`iUPzVbfcS!9(KEkM&{@1nn{~&
+fJ!j{j*>`XS;wPst$6%n-GKmG#{zsa9$adeR!`HEO<ij)FRUASFO2tys#PFBHU0DTxZNvSXPHf1wyC%
+Wq5DBSJPdI>%i!&F!#qvCR_2&<x6slIr=V8$<Jln%D&CWD@+!?H@0-^(^}vVOYV^=NKl<S=wZvSJ;CD
+Pfh<jK0`{+iU4|RGP+cvvt$rcGv>QPh&=Wy(C99>COV&z%YuC{Zoxj~UWe!C~|v-bDD*P{OI9JBv~vw
+ht%-~RoU`R`izP=<oRAI{P^$x;+X5hzXK97m!whY=`;lb^Qsr5Vg!XNwptT{6RQvUI0oFngS)&?^rkV
+Feuog)#ZLOFITr1N1UAlY`QHSZar(lUW#5qHn?2GD$gR;90?s(bAbf|7;|4NWyS3TAYf&>EdM`8oJz)
+C9skXqFCtcM=W*rW#Af!G{KeP7-k?s0(#1WM}Z%+CD_eE*h~P;mIydV=2u>MN0{6WPaxWUSw`Gu9N>D
+=<4K~jy_OsD)2Q(LeQNo)8oiwu1a0pwvDDICjr`O0>bJ<Tf?jzt%t?G@Z3jcgd^E~Gt?kb9&z+Qzzq8
+!WR*U@Ba&<NPT=}N%5$N`GF}Ff)N}ID%r)<%0dCNxSJu930p>%2PX`RGl!H+xCIR?0;9hrRNYp*l0eH
+JnzgiXs&9GOwH(+79{dD(N=^QGKtz*GtSHXkixy15ft-UbKVjY-08D<eGU>dKeamln}6ZrnOy_41=z8
+{G33cDNR4%ndg@Twg53;3iy2(c}H5RE~P;bFWYyU((L-X(2!2tSaz=@H#y!MG>l(C~M>7wcA-EkGgf+
+Q)(8U=_;by<{nc3#PChggV(#LVwY`YmyaqtDI;;Z-mGG0;twm?R@J5P>YI0tO3qoIyucUm`xc$4^E2K
+cxON&bZgce|?1oi^n|Rf_%3NJUlZRf{;<aHmFX^J%w|g5;S3NvYO{RKlX+4ee{bK$$*EKG2b8pl5kM!
+T{8Ej?uj|v-(q8!*RKMvS+$@fXvzy09%jq|S`_)Qj;L@_{%afYEWj$&AnWIlBc%~+_k&6aH<ilHV5Uu
+Hf)UrrYGDKpDhCwlSWh(7lY0W3OMRI<<{bQwAfM?;1%#S7D@1hf-npg$X|2y%-F9J&~jvguEDLn4OST
+(ISVGeXC=5DJv=co7n1OJOgBy52B@dw0r0ip(%@g<zw^QRoU6FFxc1Y}Q&@p9xrI1ba5j8vnY{S%i|K
+;iHji!yB8GIa&v6W`mzJJB#m0TI5TX_OF|r|17{6(g}O`#AX2TaH-1^iUcA#ZRY=vx;I;P6zQ5o?|F*
+4$GXKld}e*YED#_u35h#q#3aNFPk%r==gO?i`uab1?37t%?L-iS*i!iN6>GUO<>!9fWAV)ilG8hA(|8
+Czeya`sR&29{WLIedekdt2d>5|*h%MEdPV9?Yu;9<=YjzjW?E9?Q+O)+n@9ZwHjkIVh^X1^T@9$6E9`
+H}^?@!(y@K5jWPu?C7+5f^z>Re;_LC@RhB_XFIHOBD*6cJG=<HKuQeQLd7SaO|7T*M7t!r45NnSdjXQ
+J(zq=86%wJoyZ<`|N?{+t2Hv4}mbKeeyy+ljosOXJmOD-6NvL8E%i4%4sA>*1RO-G9g>8fzv{W7MHfI
+4Vhgs(GA<!smxfF$R7<i#-1Cex}Lnw&JJm9U`HaX2n|fYNpzknb7GpghU<R5I@9?UPmz4UYZB10XEBS
+l=CEv@zg~^ha)R^>@EbYIjzkoY(Q5Eb1h>(G<Z`uxhmtJISi#3RYWsi#=3*^b<LHssgqkcJKU`W_k<V
+9(yuKXcXhd|-2(Ke9JvS)I2E0=eC`#|E0afW?8ejlyoiB_t<yr8TncbhspzR;!uWp#O%2PpN6c_Ch4z
+R_YGpT-6r?!K9{zuE^ujc|kIXU>Cyn-O|jFp;f6I9-4Y@fXF(-%vvGWb_bMEmR%$iQQ@ziAyhfy-f{4
+-@n(Jxy5DgTLM?%)t4nQOS`Ss0^so)qzDwq6eHKdA56=Ud=K}<6|R{9IC%A!*ei>Skjhe7RTMd@ySg-
+oVA+7qAr#SAc}?*D-EX$p5e(i?sRq{o|)lbs{k(of9OF*jLL*PiuD4jhk~@OkDF{yNsT2PN#a1i2<Ey
+y>1)l^f+EM#YO%)`5=xyWT+R@8kM1!$>dE<;ET@AgW}rj2`tf+hEZQf?5I`;!B5lNpTOnj|)J5wf;vN
+SUvZ9yeR4Wy|2CiV=g7q;bXl_3V$d9(!PPRwoGu{J7O^3}F2D5&E+;}J>%V1jW@<Y8f&I{%F#8S$%=G
+;HV-}rw?!x-0pou0*CLKzo*`>Y?u=aSsQ|LgcztGNHm^3&2^9^u9}_AK+)GucO(KK^<SQa-kynE!442
+-E*>-QzQw3;%xn?@Ebbn8HYkCJ7v;DH?_#oQ6phf}o8Kp#+M(nb1%BcA}j{?1UlMw@!V3-z6pAMkISi
+8G0K-C;Ja?ACaHe_q;uvceN3ENAumNIez;^k-MrFh3(8`J4nVmlM3LS#cb5(T_pUgI%8tzGtgUuoWOU
+16!=|!1ch&FlW?~hfx|t?y_-`Jd)9onfqqk<y@u%BcD?`b7B7#8{eJX2Wgg*wpl-tVJLorG>WmFjsq~
+A+@Cu29QRCu7KXm=icW7bY2Xu6tZ!JbLEiv7>^igvO5WMjhmh;`Y_P1|gbo0t!;}IL+6{%t2wweo+#y
+>w7?5>^#f1LDS)d1<z9|rnbsO%#`YDpy#xK~IP^~^)&EOyI$?}qrI0L}}O&I{YHGIV~^B;m_3;M2h=`
+cZLKl;;!ARG(Sh5xRDs+e2YR55C)_MpbtE>;V(KBA$zP8wNMBAPmle%@>{_K35WHh|_<ZgQXTix3cj4
+K){zuX1C8|A#36aE+dY$?{3uHZ{^-Gy88cs>%iZDUDGUCA*Nf(mbrsV1IC7gHAy9XTf?rLhB#8aJI`7
+q4qo)g0W{B!Ve>>d^PMBgEGOadPJz%%dNs3kx`YA;CpWMUEqrk~K`u}1QG1llgSqloVk&JwJ>-0TAlA
+bLiJpU=BmvoTAe$0M-Dx0t&y&uw?i#b)hg;6~>nT62!WiRrvXMF$Jpy=tuk<NjkOXdTr%P#x^L-RfAO
+pYHA)8>H;ceQ|!O&)8CC@{}6pw0hCghnbM{?o-8q(wWKukK!dGQ3=>)`RKrh|MUhX&%M3nI)W7UwoW(
+AR3#Ms?kuN>kA10(x2t420m3y9~L!*ha~T>q`2;u~fkRA!sEG3i$m(bmwdFy+`CX_znDS8~6J@-os$3
+X9rGRroE8f)q4uSweR{_AIe|kZnyE-!;LI$7t!d*R%@^`A*t&n6!My*s9u0qap3&(ct}sBN$~I^!U~$
+J9h`FUNW`MiXSP69TD@Cz@Vl~B8<RUN-Gr~+p|yM50uIwU(aNczs@Z<wqh%6I5?$h;8ii5xXu{)}JG)
+LV9!X9I22}1ITsHPvj;tPV8(9YqAVRWj|6*H_u#uaTj!<071I)*`Jdh9il|CSl(5VGK=*7_sv&O@&Q&
+WN0)UOld2Ao_z7JT10vPDRyk}Ky1q&7#qr;Hl9jn#M<7*l+@cs!;s^TfupWn%QLy=hX1&ixBW6taw>6
+H;PHu*zX;tS^B(f$c>f(TjASvzCh-wC+Lf5*vfor=xJelh^IfJwD23;Fs?@p!0YCaK8Q5S9K2){n4c3
+zZu|*%f`>o_!b~B3dKnXMK<<}(<Dw~2(kM!zjeZYruY@TmA$dI_&9{WRVwj4A-FN(ec$yhyM@L(EFwQ
+w{EFT^HXlOcdyx{f<JCq?x3MvNhq4gfAr2w-Tj9ZfDE!T^H*ouvGxG+}d#UWl-Y>Z)E0MQ37vb-qyUl
+PTyxTzAaQn9KyO?!@;f-u>F#4fyvU@oD{~_PBoJ8IoKlFZZn-d!R787rKn@I7cmsMq%_kXN>&1~-Uw^
+evo|CwxlqiZbokrw1;?PenLc(26n+X5q5(AixZ(aE)W@1Xk(E?g0H0itAqsPCAFee<IB-HY36pL$!1N
+m~U>v%gWL%s-9+Uk>K%h6K!X{NigB9RrDe+}-(`f}$_lAC~s1?-lsc-1_ZX$iFteZV3IuYu}?2@Q<`p
+o64wRakfq$;&@|H(ifJ;BR&{cL<rsfaa1Y5Ocj16Pp8^26GiI=3l!6|2r}o@X{i%Ufesf?Ji_WpyQDd
+!oe1IvlXoY}SbSJJ8%U3_#+^k4s+xy7(=ph1PZTTa(tadtAA(ERRjvWAXv@olbw8)`O(Nkzg;FzJimC
+!m7q2-V0(IdYChj4p`!z({qv4=OJ3SBhqAo-T(i}M+9GW?A%RczvP6Tc@`O^)tH3AF+OV1q#HN>&$3e
+Zq<B_*zjIvAGaOL=+byfg|<Npt{B@eB^KP*bhLTrFMTG*|F|Tl=_?1dR5G<;N|ZOy*^*5S8TJHN8rO`
+!X(mFjRbjipX45Vw!#LU<~|}Uj7L>$%n~8!6%>=PSF;y0aj=KL6Z;E9H?Je7;osL8v4jMrxsk8b)<36
+0ZgYN%al<Jf7w2mzI0;3b*aWGQY(VtkfC8(n@)~1kG&2d5}i^i5@Iw@TLkZdNo#;S*KjjDpK-Ok1*eu
+0nxJ#2dyjKwH+W_a(%VN_irmZdjIFDaO;q_jbfiC(=hv!mCxA%U$b2qW^w!)@>~lOuq|WdWn}e_rM3H
+?`B9KBt;h5cSkH&`}8rKxU)Ff12qtUnlRVXKMO6IEZ=(%j_4(rlh4eV0fwuP0_Z&pBN>I0_k^2i%k!I
+p9KOs`5?GSah(!w%49ia0MfD=YZ(d0C`bD>HSr2(s_50SyXd>L_*qJ&~h&h7iT@OtLMSnAf6e?Zf5>x
+ICTwc{S3|Kify-M}I}}*BzyB-%%1DZRI!ZGl_S%NWc7xlf}On0@*kdL{L9gD*a!K^QDUEza8T{foKRL
+5fVda64^~AQHuJh2M3A1eTK1JBOwm=oh={Tx3YX<-ye$KCAcB=E+B$7MEm*v)?1HiqgiOOpSuyg;B7=
+4<NIdQzN19$s6zXD@)d&K(DZIGO@5^Y4PpC+*v=BEeJnWHe;}|Ov#4YrX!{|e_9~~1#G!Air`>Ub+R=
+1F;cXu1dp8S(_Mi6-Qf$|O-fc9Y|HQu)Wst;u-90Q{DxMs0BFS-BEb*shg_H9Q2?BN&WL~w|$XVud*7
+EG^h7R=NyJqI?%2(gJG36gd7G4upa0Y}&W%R1++b3~%zA&It+87d|<@Z(R8RX1=NyftOOee2xfOdVGT
+<lw08#fca9MnYN?}bZ_JQ>U<K`y@BeUNsmiQ?|=YFAGBox|lFc~=HY{!QucH+G|vUio&n+(2x@WyO53
+te(q=wi=V3r&F5B58xMy4cxBSu${0AHtaPOr?MK(#F&$mFT>c8){uecoj#(0KI3JkBvxZ-1si!SOEyO
+*rNIQVY20f(_7=<Ga(BUdI%v|fr5+p;m(-h93m9S;8W8#&?cA^nOVc@fU7Ha%$H}U=Nws|^;Mt2h^g7
+gp%P6XPiWM*Nz-PyVeu+HP#fb!{wA{OQnBWS+^{+Db%KbZeSqV|rG<`lOMIIesOz82A-eEij9jXMnRD
+lT{OlF1g2cR?b#O7EkiS3z2<v?*4J7O*SiJoWv^*Rci?%ftfl$@mZH!u~tCOYW%wrgZc)(#nPBdBzIu
+}FdjCCR>|yRCsZIzShKx#X`C?pGk5E#7Fji`<kv?N^#gdzvfF)ukJd0H~<SpbbGo=Yr-}43nv1%%|04
+Ml85S-Wif1s)fLUIuPTj*Tni($j`99X@M0k3bz2>!MKZ!MjV9X4&5HEW`e|Nnv<JX^>?iF6^=y^{XoA
+UqLN>)=r-KzM-4wu{-u7jxeo|IcT>G85*AaUsoMilz;i;ymq>#BZpaqeUk8QddUb=N!-Du`Egr{0O@`
+?h(8q%foMdWtucN&fr>Ym#Q_v)&K{>KgfhSrW>3Vs*_~<H9vql`FoQY+c_0}maC2NSMQ3MFRU>|(pmz
+Y-=kwa$FJb>vz8lBV`*HVWSob8{WdvwokL08u;yqbLlL)m;_9?mrb&er89K~^rtP6#UUG<<HI!H%OQA
+3Keezl{uJZR2`dWUyC||Ds;`lQqUiqS`FicxZJKV=gs$jWj0gkNT0%nnGPUw)r$by4@t!WGUGG+`m*j
+$<jsRsP8*dANu#5sg>6N`>OK8qVl1Hy|=5bZ1|lhSI!#80%IKJqjT3Y*b&%UbUo&y2l(A-ySMPB?8r#
+i-cUO*62(O6$8nuu&`+;td*|+bV#{|g@Vhx!5|~{IYzuE0jbUbn4}Vdoy)C?KL_aSTgod~6*GRysqPi
+LL8l&c(ow}61T1$V+`LWa@6r6c=#^Jsw^ZO#5mr|>f8wOd(#0sxeEoi{6aH^uu{Xx69PtYq)x7Eb#G)
+^DmK|$ORobT6}!{_Ym!iU4FFiE)3+7VIi)<c0*7?4$-6K2NTzCPEXI2os25isvw5vwQ*BlJSLLRwKHe
+mUbcTXH<ME$i~Y1>)jgY>a||%i^x*XwFX!+sfiWa5CjwF4L=3kQ-X$%M%HLsXy_h?^O+sl{{Fe8ZMSx
++49KMumIFQ@OFwNN{Q*Hde%wZWka2`wl9ZBA@iAbW@U@k%>yl~inCj~NWxs4#+0!y&=D^HE4CMuP`l1
+qnNzm0c~vZ*okdYz4!vsTcqc6k$$Jefn1?2IW-~G(-`zO|dbC+HhyWUm5%E%x^r+LcHwm-g>Nr`151f
+3#=*<k<V;nhlZPe)Fiu5$9RMh3JJ&7l0=iyZYyilCdAjJ-XHh1Hl*M)d#FWrOfx)?R1<58rmA=3P3|1
+>+_qGYNr#%FREJPQFuC<UzEOmGr@#>j0EP0~8Qik^tFRyi#<S1^z5wYXE3SZ>edtV`U}xE$BZ$>vYoS
+vx$!6)+$!71y;T@?i>Jem*#jddobSR!`I-FV^fKNqdx$k+&FK8kz^LG6S3R<k7Qf%V2?A)gFY<Q`gXs
+z?bF>h${vaei3TybWxD&ABUXs8rMzyzr$zs*ZKd*o}gcZzpVew{J5|152t+&7{8wKea4@}2^1o5jD}E
+(MlcwsaU3NGilAv2gSNQvr$z8v=odhHcAvuc5G?ZdZTh`X9ou6=l-}bhB-!IEg8UEp^N?_Rouu}ybOh
+g9xnOGFWykzIQ@?i!!|y!BTQ`di_iP7@?{!Qu{l(_(@3w6C9?gZ}u6aVgL%;1B2ze*$-$hVchzh?Iy5
+Qg~i~i2UAnzZbxAGYF?%}21Jy_^lY-9^*BkZG|{<lT&)SgMEt1tO}M&(d<``p~fL$(}T6`t!V`Kc`%g
+Z-HA7oAmbKH<i$o~l~cMzl*-ltpc$x!<KRG_EKKz;p4*v)*;8U)?owVcoaf&%BI%xNCemwo8YsGVJCI
+_G6jBzrOS58w2~D_t5SSBRd5`+Y>(%U4sQrvAVv1E|de)R^~FQK|vg~7L3c-kS>q#a-z!XOrtRv;r1H
+7jLy^_anW?n`E{%ng*kDhe+K3#t;+M!v(7?)>7S>tNAk+N`Kf<8(#L*K>R544s7p-CV%(ZFsL&v;GnO
+zOvQQI8K&|fP9hn_)U81n#D0Mt=Jq_v6pb_vPhJ2NH(D9iKI#CK>M8^bFx{9tM<=V`(;td%5^QqD<-F
+dae(h-}azHt|h7w<u1ngo*;$~h~9akcAhZMs@MWH^-(aIkGc>)07Quy9vuy79sJU?}R<sIAj9VxiStx
+?C-a**@E0PU;#9#*AVLu<mJm#$R7Yxm|jRxPXAtI#-HFsEn-}@+8Uq77u6gWqm{<#J*~cqXQ0o#G=Cm
+Cy~sR_!+q>`(%&R0ty6V0Ilj2wEZxjk*6Y~L_$4&>=TJf6VLNEAaKA+7sLLv5ano|oxlhWS@ud!kn~D
+MVbK9L*Zt@7Ru;UQ-pk3gM?Qa!PhM(`E)4X$x&Z=5cRmk}v20ROG~M}5tH&x?nZcn{AbaE>)YdPIew7
+mxHNwCL8%TH$5GF%C<lq50N?h8poT){iDL=ASI^(UwQ!NoiG7A{uOOFFkU7_*v$^y2Gt=^WOpokxx&Q
+;5Fa2fVw@W~T7AT{{l@#w<qnhw&5iQg0uIBFSsfCrx^<AHU8lJZlR6yies8rdORWVded)%1DuKlL|e<
+GU=6lmp;ZG_#xV1~ps0&Ch&`Eo_d>bMdGN&gU-|_`{cLPZsi)bKm&YF8i};=*H+K^{Q+<K*`n8Yd$EV
+(3^<3M$)y3AFQ*R=;#(JZ5%6g)^s*jmIXe(qJ?%YM8uPXU88s-WAI+r4sC-icNZ^%g%$S!?;Er8Tp`t
+LW19gpZ3E7L#<8ydLHjb*iLwwiOaJ!#rLXLdb}qo*ePyB0Gl(bkxRSkcyx^)d<1KuRq)Gf)?hE)Rt7o
+w$om{J-wFloa%S4o7jU<DUC6Y|(_ie#j&9ZGz<CShix~(@fC$)1dy8%%8MlCxLXVtTO9<O4eYFb{3Sg
+SZT+68nHq<$eRZIND%q$9IydboGV>ro718u&N?_*3<oANAl1Wv$6f5$A$?zV;wxP6O&rN}I^@bXU$pF
+ydoNy)yg27$=Qzli9P@=?N(M0Estu;O`GYtuM@YeH?K|T?m*QjydzF!VZN(BMUZ~Fji27XKP_RxY#7Q
+E``epQ2CqNu6L-{AQI$HPZHFzM>6Q9wfeQ1fd)Hg_)0vA$E;EvN>u8-IUFuaOcaUw;%wl$jB#dMaNkb
+Uy3Sr~V2j%Ap^(Q{oy8I!uWLMJIG$-l5;C|q<<3xW&l!nLF)pM6P_uKOGk6t4RqH8<VRkxR-xT9c_!~
+>`X-h)y=es@;{B2?*={24&!NRDo<brTWrv!kLyF<ml`XP61nFte0hhVdLEP|95jeX>}?fWR*1!K9g@Z
+sF}VuzV9NRE!2_V<K307k8erEt0C`Fv*Vr`MRYWY>vti(OWJNXNr=Ak!aq8+lIydMpUougW<#6WA*HC
+j$btuTl0<-Ii7>g|!J&DDuiS#KY3-rltrk<bAu!$E185b2kVzq!Cc+0()+I_7<t^0Asncx~r$xd%>UU
++kGfn?|L~5=##&zk$}Nhtmp8IzEo>);QZ}F@oywnGrtPrw)^Xu{KY-1DsIF0V-M<oeYo|W{}03c#rfL
+{$)4Q%SqS*wj`2mJ^*7glR|*QhjpY%H!YF8q7)ki&h4FjY2DArP)LT9uiuc$K3ii@==v|J!cTQ5f;tI
+Muu>Ukl+(Ja+oqY7)s;z$TDNnb>gxm2g+}ongEuh<O;!}HV$SxD>@1n10_^ZSrvEObBG?TZF{1)s*$$
+uhr4;S|e`FA373u`fCk2Nu3pKV0#b@{uTK)h!Yv3TDW+oIL&;C9PhfgsuwqT4@1=yy@#(Ox-@wd1d)p
+r^~WN)biplm5^4r#FZh{uZNMZIRc6FFLA(?TW>oo|Cz3^;{>tqt_*@u0zDJnJ1ZxciX}}NrV9T_*Rw5
+?=D=1)kik*lR4*?6Hm5a?H1?n!A(su>^)jERxM9aKr_2Z`rLy*E3C1w{I`RBjuKBcQ-9uOlLh^`o%7g
+zdI4FYtL?xTyf4zr&s$~cC)eAr&6{s0xJAng_^Y}6M$f)ExZy4KW9ss%Iof1=%<w3zjed8{4Ka=Mv<v
+T9Yz{;l`fWigv^|i*J1XUF%d@Nae<VZnEO4)jSCvcP3ZHTyU(RbZQ=S0fBQYupQ^{_jHLCO4a^AviX=
+S|6`;I~(^4r_|cCgf0a8}KQW`ox7^KM^iAlY`(HhQh40dn8|P51jncf|Rmio^8s+ow4F5?uqo53jkA(
+fUbW2F5L(L~wjKiLW1|?11QzO9Kei>E=-emG)#fZIhpu>>d#A8hBPk(Qv_wH-sLNMSh%^+aNyX_TFH3
+v7_2Unb^t;xSp6ry4K(k8*dkvlQPR3vF!;&E0&hIlli13BR6L7F0oLQ7ng52<MC|s41*+yL=1o*PTtc
+TB))(7T-&MlD<e6h6enoVEz`j`rs$=z8TzQ1q98sxxW7DM@b;Pw;TX}L3E-)I*kG@;-3%V$imD6D3ln
+Ft@VURUg$hzj6h+HH>5xl-rg(hQW5pm(w{wpQX`%w2ARfc!+&bE&<!)Y~>oVS>cat+<#0;&b&EZ!|UT
+Mz)oh_{EZpURzh7%&`wNXL14_Fb{c<HEJwA}$F4v+EWoR?>ijqsF5#!~9L0|pA&V%01acgI?k1I97U%
+dsnA$}Rz?G$X}GPU3^Z+Sp{t`Y9ipa!d)|WLmb<%j*kAQMep}QyTd<%CmSPMyAf~EZ-zL0ECfd_VT6|
+`+MAC^s_LCJ6t>5a*HJhq;W%v*GO()kGe@lGmg_@KvS6@@%f~OGY+@~<mvbcoi(X%4<@cCuZ1y&xrG;
+(!k7hwC%U>w=>=-z@dY|+UtX&$8tw|52WplBi%+mOHia>TBgcHc=*tODhVfF@Vb<gjmL|Q7<N|#H&k7
+xJ@OUFrM>DKPpygGzg_+x6LjY2kHfM<N#pPILgMEX=O(!A!Hr!0NaC=Xp$}Im$6ax6%5(-*Y$CD=*Jk
+|_Yak50&o^70BaW1Ab);{&0S}d}1EFv>s&IkT}L6xO}RiuINM+*$_;|lX{7Z~8j73SYAFu;#1%)eb=f
+R7dC!EpwcBZUGssx%<^&pjG1#^?6h`=s;CvL~B?cea^(PABI3{@SDTrN8#Wqya6q_bm1s7F6&3qE9J{
+Unm=I7^yH;yU6hL(r}6+V<NaU@o|>NoTg1riHYbnOUdK{7X^e|#E<<U;fE_C6U-I{F4_fsIK-`+FHI=
+Hsy`J_nI6%XdhzdOs)4D&o99NRIT66`IN3B99cKEX9o$zEdiin6p+hPj6pd<C(NNGqZUax1&JN`g`3%
+NI;FxYBz0N%hq@`cK2$u3$ZKWG%q9m$5TSk6rc)H*Um})MGe!5BYAs=&wepvM~K!>XX$_P@G{t*zLGN
+BQ@FQ+-0w(UzDHd@|Vu<2{!nXW^%xH4z34HGaW3HsP6eF`@`FP>lXt!{M$Xe^clP5i{VGTS^d_^!kj?
+_!{BM?CMz*A0>%>s9ieT;X3t@TBE>plPw9Lh+p^G#;48U~YA4q4bKC1}hvBpAj#-sj8JoM~}P<AEg{S
+p|E|)`4&x1em&}<9zdY^5->vo`dsE_@ifT28g>tnllq3CxLP5Zr*=E5#?Z|&w1%gZl~0el+CEJB!FNV
+T=UBR1?H7<2eL>qK-7R`88jvttn0_)!^P(!9m~f>`$9#0QOSP-FFKxuluTiR7-f-7PS;2e^B-!KMI}S
+<L6+~P@Kc0LZq&XF7$Kz-|T27*<RO*?_TzEs}p73przv&tRgEZqj%oI3Fp`gXfg~C(`ZObdQc5`uU!`
+?`*?d@K2DNL%*<iVfr(#YM7z>XtQhq3&)Fmu9tKm_G~-uU%VclOuk0Q^h+*`LS$|J?}R*S-Df>hD{?a
+16#_m?Tkn`<tRL`lpp|^gHfP-uYb!-pj3#c&}7LiMOa${En5Oy|NnGRkwbwI_>Z0#_`)o65p%LP<#*i
+DSGdE4d3ctKD;N0@nBEkCd6K#?QbmOS8+d#?p12rDA>Epc28B4d$rs)RhZZXT{aGYV7rbBiR`rmTa*v
+IjjG9DzZvp&oTid}T6--7wO_T(FNk;k5&l*p<!IS^+%J980#@q~SYH$Ca{fi!-~InE?*FC{%RYWCuj5
+!A@TDzAskZ5TTeQbPAB|X_%N6$ce=jG`wOQxcce%oL4As`Xocz<lZy(IRc`m@u9?ZXaF2K(o%)faqz|
+S7cKR=i6EyI9+_6*~bYkw)uO^=v?VIzkF+h>)kL&CkhDo0|DBj9v@oOO4Sv<H5?kGjID8;bJ&9EJ<+r
+sWDpm1tf-^Sa!UTkfAjT?|7<i;$=tZ@NkX5(yD~IP_0y7MckeWYakew<aj++r43B>|s`?9^)-6SH^3z
+nD8?-I8ui^MS<>@3j(ZjcDbu^4rw8|R-#(n4;-(vOxNTg?`P*HS)AI6OIzn^-N~jJ-8=;Y%zw!ec`QT
+>h@uO7NZD&AM-IWohHk=|Mj*F?5)PGY9aOE#M_RgXU&8)=$WN7=-9^0aP2+q^@<jrU&xbaSCo_}jQrA
+<)J|f$%G4fBh=Skv>>AA*m{3_VQ3S7`2&Tev~rS%M-?Cy4kBm|Hn)S3A0_k~l50rSd0&RyKZcd|pY<6
+#ILpHM>;s%!Mf_{2;XQt*Ufh~NYMIz7=Vuqa40xu^lDw27ZD*tDid+wbm${+J1RRN%7ar%8OCk-Oac-
+DKZ_C&82l^aMv48UsOGblzjOGMyER4h!+vS&z|lbYLrgJ!P`W!x7by)H~M|8X&_#vc#jk)Qb_s*@bQa
+LhAgB<~}+|B_C4(-loi&5mkVh>pW6sfnOe33Z|gY49{b4Aht!%^b>m0&vPTtN(M}xL|WUrl|zzdL2KZ
+f>cLU0P?mKs1Yzo#sPiF0Q7h7UcZ)~v)RVr%4ty=^hVPy!uyVaxEXC!EZ%8V>c4<8?|E1Qb_HA7Rz_J
+$3g|G0~k9NQ}Z9BjYbscOrcZOTevDinLnzQoJ)8wO@{%}6<-32d+?Oqn!ZseflzwcKuug*KA{>fC=J{
+NGdMe7aVam+JBuB9yLa9As4P#fvS1-6yvx`qpzZHvv@c;qMX?zo=^e4JR!!rRP!AKkGzN5Hf^gtS%0^
+i>En_KPf|WjK2{|8+3nZn3sn2!m#YlVW@pH^e{HB2VAM{_g_k9})@=4&V%4S))q>-=KW`7xDh*a3A<@
+<NY6(w6F2L%*hGL$HgP}SkIIWID2~~!idgK-9I!GpUsMLENVpXU%^s(z;1*i3~h8t+`xg&5p%i6i>Yw
+(+<S?3hpA|~6Ny;kK1i4`qo7?Q@RWSx>)14J&lR}ApsPv`b#mfMrF8UkUR64Bx#DTSP=Fz4(Oa}@e^m
+YFt&wz-n4WBVJ9Lq^g@NW_29o(S53yM!NaD&%3k`f3Rp&HoK#9IMvL7kU)FN5uh_JFA4kTZql`15+)N
+VXRy{>?dXephGBF7hJ`pAODk@YV$+ZwMDx>)JRR4^Pij*>fBK6m&0il6LTeq-~bIMK@z0rzZ0+S?j6x
+e)j5@t8cF3VfNbemkP+$FW%B#e2{+n<RA)4fm42F^K(aCJ$L0UW)+hRr*a=@Fa*rIFSvH*1Tosp}5fe
+@Or(DE^>Jmpwhl{3k&P4l|Tr`#f{ApuOsZxXMnkop5IWEW>8Ni73u2ysJoL=)OC$n7y0@M*J=^$cB5k
++Rrm8HU+3zSh!QIw@V0vb8l*O+(NIBhl49#1rddgI_mb*L6M3A-cuFoB=8}OH`F7h>DY<N8;t$1LpM!
+v<F)&h`Rq{rFEiw9-@zEF$HDSkr!$SRYEI6|3?=IiWqm<{bP8yF~nmqd{_iT_QUWB(xs7>pv>Gph6GI
+gmf4#hN1M-jnOV;4p8Gejf^W5yS|sR;6V-Ik+N7gVD;uzw!zKmX$WC|W)>nS2qZiNDIye0IN;p6@gFq
+K%(Q@BUv7^@RZZ;V|D)fuE|pZ;>?W@1hR~vWNRIu^0SOZ)w5Zw&h*ju@_~$?RBA_OFv-b9#Q+j-c*8o
+$TCp-&cPOM@6{r2yS6>^{w~)DkzE5KiS}DjzY@aPOGmJM+hM!)HhLWFG$N$l{<zrQQWL#RZP2&*OhUd
+ZhLQI^K)h2F+}~+K{MH)Y$iPMt{BUnkh3UP_B>HXK&(C{R$P4+Z$@^3PT#ikDq9y|f{Kt~KU1_tBxcI
+kb3@YK98jR59N<9xy6ABSC3nXWja_0UyNdyQ^G@9=;RPm9^{vt#3mO}dgj3qk;3oGo96kA`~Y2L~+z$
+fXMQWo~|<ZRy!hT`vycky@2%y(ir8mjHm@!KEZvszAi*S^oBl~?;4-y2z&B6b^`2oOBeOl=ZAW!L~=Y
+V2+58>;BV-JMI#d`{9AcOCH<K;|B*;~!!B7fs@MuY3ILVD~f;D*f~vzG%&VQ(X;t7Q5FtKLb>0qV1e7
+hP{^jx75_ndv8>FzNh&NmIXx5qavLyXeyL<!aDJQKm?*JXzUrYXZ(3p>Lq#6Pb08j9E70UeO_g@GaQh
+G$br5z7Zolhd0R20tJh*r<_H|NSS`9UxN{pz&~}kXAe#Ix+qil^yo@sszE0pHyS@?{Qz!E7iOlUpbmu
+3l7^GQG3&1BD<2|;p+bw(jIhm50*j+ZKJ)aZIb3H>eEU`3px(t1kXc+~POzRMqb+1ZOF|HXP6&9OwOJ
+m^o93>_q^`7Tb=!CDhjE=1z*Ew@KhvfW_dD4PWO&b>RUTCGxD6TR|CeZW!bjs<Wiyz^G5$T$?aRk=;y
+Mi3lb98@^Sz?asd1gEQ!qly0i=prMD<*vO-T+@(L;tY;;K%X<K;&P3W`u^fJP`8W)2t&7vL-o5dWe7B
+d&7AM4C8Q8-2m4O`FkRQ=jlF27Lv&_e(~-xL=%2E>UpEmeBch`C6ed0=Q9pTuO!GQ@NhxOE{X?$?vq%
+cdRsUWC$pDsU>gpnS!xTFsnbjStl_rNaj?a8y6Z6KR@<nj2sB>UH}^K#Es)CRwo2u`H+cwA_#=DN8S*
+BV&r_9<3eo!P$-7eiMaZ8@qr$?$xL<Wj3UL0ElXm_D5XoNEBjy|!^;J{ofko0AvYd=F2NQWDI+R=IxA
+k(0bC~3Y%PD~=9FMoR&|MLUznlT}!i@F_MY}jT+e9u@P~))O^^io4%!6`xsIlAjo?^N;+^*0RUO<rs8
+KCNJmP`GT0c=o&v0PCiTj76WJKrXc?W6VwSoif@I}Wq@Sw7Rpuf*&B=Z5{plK#(z{4JbDZyQt!-knHx
+t^0T0jG%W_qU3EsN)WsC2!!t{_w>(|hBsi_6%L5KUvqCf4c-o_AIkRl4zozKqh0crTH5XwVY|f%lx&d
+ut0K#{?|c}%WfR^WEW0GrTk4Lc_g0z>z7cpAzuV><qPxCv6uk{lgFUyl&4!@fN=op1qx~&+w+lH1@1C
+FE>dNux4yT>_TR6@Cr{Gkvnfwc!9z`9x?@Y$Mb{@w%o~u8DitYj=YP_4)$wE2Y7i`{>H=mO|r|Z{iHp
+=)tzW!Ty1%AZWukcF!5xfFF;_Khw)p90z`V6nL9ZBN8Sycv-kzS>14)_v2Zv3*|-p^NYeS71R%;#{T{
+O+P3;THHE-u@15fe*Yz`^mUa^z%UN9+WOl%ZLZ#*z1Hci0Dci?dkjEbg`Y`;at$CTLtLz=^soj1!~n)
+qE&9LBZ8Ci8p&g`pj?!dAc?q7y9=@ke^_gniQ_f8^zDsumBwVPhvQvoHG_fU**oB(HCTu28kQj^CFw1
+^*6s0;9-|GaEfw$ji&b<Nz0lFhn$ELys}EUl)wTQUpaaLL&xiFDfrlAqlq`!hs!9tsr$}Y-vqG=J*5T
+`$rXL3N5sKb6fy0f|6VK+chKzI|DxYywV$gJcxYwpSbqDs)Xb6q+@|-Bg3no$~C=D;`x|)?|o6kYd-K
+j+StM|a19s-VD;3&Nzvtn&j40gtQlA6FNDK5hIN({e%Ti|ziD;_6J%fK43*~5H<pHJlUe1=5J!d?BhB
+8IPc%b0HREbU1jOQj&I4CFGqcB0gEZiDbF5cqL3YVLW;9iTfp7-gHUI<}I>^Ikdey@SGgA2d#CH8M-8
+kCTUXGggH|t~k8%!1>yF&U8w^ygHx&mPZG<eKOApC!MixhjkPnv^4DwF=#0~N?}Tw^;2hVsOX-f9)CK
+Mh(C*&<x}>OqySYPoLAZ&j0{3a&n`W&u;gQiL8}a&Wf3mx3*pP)yiiy&$171E$xG56XLdohsONA38ue
+h5)Acl-pZ61UGRCWkah5nkv`8PL!tG9Fc8Qo1!>QDajw<JxBDeNTnnZNMt1wV$%{{N77um30h}gJZo3
+Y{_VJ~%tWG${Qt0f(IIA?AR{TFci*|g>VE!_Q|8~^VB{HJ687KveU-#6Ne(2{*Oh=AY9|G^tJ>D`b#e
+(StqZ+Su=`6&`_D7#TfDtQ~PhP&q(gzYke8;Ik{F0DZL`<B_R%s{;vC$%?5?3QWCFOV1~_OZ5rV2c3!
+cMC2>?!481OC@c1ykRg6?IA*#?0*OFu3ofvSU~&DU~~^GHY7&q9q>0qNBo^g@3nXvi~l_mle?5dAO9A
+KEB^mWB>vF<{-hUFevic8SlRr0R0jTu^?pHRFa0Gd1OLQ&{{)qRZ(;f8sN7BG&spJB`5u*#>jFgE``j
+MZzh=aLrbeMV+PO1cphG_B0P{*1JU_(qd|VIeic_h8Kn-y2(o15msE^*UJ6$06(<8W*6O*d)=-v)Ub|
+^{%3u@o&t*YU(FuvSX13m|bK(?X^0jRlg+sA-Jtlh4MuEORQarEwkpZE7tE|1rdDxZozIoQR|j)pTKa
+m-37fnqr0X7CBHJDM--vA*_qCe(V1tb4l6+j``WxS0gpU88K*klR$-?O#D0ZP+p})QhS}RWl?pkeQj1
+NZ$FmHvKEO4LU~<ym7`fnm!Vw*yh&Ft0;nnoU5cY-jvZ8#}_4^1zs1DeE|@a$hKPTTy^lMQoAZ!pw)2
+c_{xDAAJ1_XoiK9PRKy(aYFBD9IX|dT{FjVaROW=y!56?p;dELb^0Uo)t63MpllXs9qtIWz5FvGRo~y
+)LG#LmcJX;b(K*hsde;##&?OD~MQ#1^He}kmp8DtOca@n{nzk)K<O;&VV>g#yO;zhr)z!(!&uqqJMNS
+sO{oKe5}QyZp<gHUnlSW|X;jY;$>Exe1A!{w;j%WOz_7O~6a1+Tzs8iL4}dozg7<!RZCZc<35uI5DMg
+nWte13DUph^}1R+@H=$<)KWX$4fK?@xm4n@DTV*;@qRdOdhe2JjnDYflv5qzX}eALetDi=n%l<$@0mU
+D?aq)fv8rC2(ni!H?9Vt7lNK{EKcOBe_!0>;6708JWn9%o_YjMJyMPrwQi78HZsq?<{hZIqDNo8tgKa
+XI?!1U;?LD6lD(S4ZilY_T(G?X*k0`M^@SgWC;se`FB%g+J^ee)bONGK9H&W=CNOx{kN9aFIQ3!C=I^
+B%@mpb+@^@tO-%7gp+Z&C-K9z6;Kd%GdPT9C@@b=`y@EtPg@a-8M?mlX}wlDObDBMf7iFn7?kldN)?l
+nsO+A`-;LHS!rIQ*Cf^oDb6zjy<6=)>tJewTT_u^)e{QlP|+&;A?x>35EM*EWXUN=V5*8=w3weA@d`n
+Ok23BqB7Fw%EVvZqrXykWBsu0g1QV_$On=T^5&v#OhrKcr};krbC+fNqc6Oa9};w`>058!uq-b`)VH`
+@V+gV`>xo1pqem!=$;FZw(s@5Epv9k-W}Aw9DC0M_?z(gznls1c`ARK$-i;`q_h6wO=0^qZ!r5%)#D^
+B7^;hGVJfQHD~f5mYj;Lp%Y2!OjhTuGA-tl1A5ItGI1`fO$FJT(@Y{r(rWYvcw_;I)F1+W-(p~%0C2a
+NZdT5N6a0o4rK}(T6Eht~qy#)dv5~)jj<tWg{6V_<tjx-4;chQCbdF?QqL9Yb0bc*iJM+iC~S&C$=Gv
+a)7q6@P<#8_^X>J`{xJhU7G4#kyVlQUf*#kH;Bvn*W%{dOnVNmcX%dNe`uN!{*IINz3%uwY<0HaOy(H
+qJ|jhYqW3La$QW9Zj`yj@dM6l^fytV&6*k9Pd4h5~)e!h(mciDBP<Bu9NmSF|ZQdK=h<H@Q9H)%HX*6
+5B6)dcaa>dkCzv90O}dL;9hkdIU6Zu>R)AN_m4U7!{_|%zs87<j%JMW8Z!D}YH3(~y0@jGW8~qvLT&X
+dgxWW%-qvdgxH%UodthKl%2nck_A3a4Q}b$NFLb(y&>&(^#J&Gpw`cK1-Wzds5?DHuX3BP+o&#gBV)r
+}eZ}k0a)coUhgCyk!)z1VUNmYv#eZz3r@fp5@dw%p_7@7`u!G0uLp!FDgz@x15;+&V8)LkFOiMo&TnQ
+EQ&)hZ)2y&lf_f}Fv0Ff}#s@<*e=o=mw=pka`n^K=U6IBG8y4DSA*HL4(wc}T($wsc1nndUwo)sr7WM
+b<gx(R5;nM4pfkmU`9AUQgdSpe~!U7r0KFjmarsTq>ZLIt>XCv0zqp@(rU-r9Iq|Gxaa**S-M&J?^Jx
+TWkyHA4AMPT=FxB`Fi?y5JTf6N+A?NB72)UN#Z}%DEcs63;i7uwndwMO9^4`jXEmaMPhdmzUbY}y%D+
+O=MCxfzOEzhR3;Vd1%G>X75Pts?<8%Px7)ZGykpdMd?QvLdyu<j^sg8i<nQU*9isdl0XOzVz9sjz%eL
+$H79@1X4m{esn6}}z+iW1W%PHYIQw-l`Y$&`Fylp~Iy!Rq)mn3idIQaKsdp|-<B2dEFT%F2-e&x#cKM
+OHu?`EPKfoOdfum27)>GEMR{Vl}2kNwX>%=a_-HxSdywx-SD;NxcJBck;@5BZ8#j(v8WRH@agYYN_-u
+Dxxw#^4FHlKWU;mT-Ka3vgGU$?g^*i{);eiTIHFt%ioMsi$z6J!yT&2)@s+CLzB3`{*6ij%e%BhZEZk
+gTV(1PH(F=mLoaTjwOiK50rSpT8pYZa%uE!9errWmz*DU^VM1>qB4XbaBu4PFsrL&s(>L_vqUG^^t>@
+>#vCp&f^FgREW?*@MBH0I*SK+%^dvy8uttDS5vygIq??|p_>^e?Vx1+5htqMT4XB?g<0{1>zdh2EK3$
+14$UvwE>v71LHh;OkB85E*MzEk)#|u_R(uPqeLMta$AJIU+h)XhRn%E+c_zw`n&Ib#}SAVLA2Vkhy6q
+k(Q9BpAyq5mt0`C{?O$mOleJmRIPG~8D~wc1>*8jC0(b)z4;@fCxha0v=KIRiZ}502>gx`QJIDx@T?q
+jq+k)Mvs^CfuXi04uby1q`C)tc?KG7kOqo(YrQ}qFD#PxI;4nh7eH}8El9Ij98e**ZLA8Bk6R!b&Au<
+kQh{QbNz*vu9IETVwAl^ydC4K2dL@u5a_u{$BxoV^G3zmRCJJI<RYl6cs}Z)7IWgE3VMLk8W%$zq=_7
+h<@~%pdu#xn<v_P{ju#@YhhmK>$VR}ZIAzy{hO9XX*n;rdD}C_9F>NJ~Q!arhB=a**j;4oEz>LPDcY4
+_9!D~`XNvUGvEN9qfL`yufcce(?5PbRlNO%7o#9Z3AdJNSc<IAtk{~5S^I`=!jPl`s#9aA6@#ZVmCQ2
+nPeuGqdlLvQzz57lsl+KWZ@^6+=D%bo#z7lZFY<v%Ac5%uOe=uS*xXqUU&SV92rZKfOP*)5ooU1D|DU
+?p}OL*6uJyASd!@<P7*Ptjy=g+<B#5W&tqAad_o-R8N23%(c2Z1;e%UH5K7z1^ESeiyvQZ;vN>hn@}F
+5PV0aJ)8ONct!W>oZm{Fn0q2<`66vD2lvS_TdCrPzd1W;JF*x*s^9&7J^$*~%(MF6ki`hAYwzE#{ohc
+<X4VhlVthrFk7HkE0sJ|t{CpO`Csg@5i$C=F+-*K*R!xA{^#B?X8qpqj!VZ3i*)vwP>zz8%#FN)vs0^
+hpf%O3x+lcK=ewAPHQp&SyU$ZC102GOiPO{3DaB{b}!tMtNEF3h+jM<iyXRMr*OMc)UnK7y~S0133c_
+$y9zWA?ajJF;D=6P(2;T2CF+M<^-mO`#FL-8V?MJ0%dS9xVuMtfKbV&)s9J$$;F!TN9l3#!#y3+Sjz<
+|~T=9-qv$C!wo%?Le|AQ1_&w)4nG5kCpTNiX2||r>^ykM`&8wj36Z0UW@}r&6rR0H5o<C67j{mE@c%V
+LZ%tvpg2_|UDSbE`Z-r!;%r1pbXZ5VIT`F7>Qyu%0TMbN^bIAEOSv}7x<FzwoCK`8VW8nN#W-B@kR6J
+l%5k%yg~!ai>a&n_-Jw1t{vZKY@JztZD;ALrPZZgK?jN9xK=DWmNFZh*6%&PYHGJB*SkVw<xe8Got;-
+gu%HR>SfY;HJYC$P4FC(6ekavrFKPbuC)9!M$j^ohJ8lxnns5rGo9-amiNl^Om#EK!SnzR6XfG}d1*k
+eA+*P_^d0Taq7gv@vBin(+}+?1onV+L+?{NgnnBdub2#Isp$lLV7}fP-tnwH34@=hVO6@k3xp@v^!eA
+9$ma_Zdrrjdi-5+Ki*+6AFuGBNnh;8WA>t>>F^NM5rv(lsU_T54Iv%pSpoOfW+(d1oai^>gv>2-|OFg
+Usj`_+i&)+$$e4Lj@vd{;2~^Lkm=9)pV+f}Bk%rw#*t?}J8N?56@rX=ff4v=eGTj~YLAQDL8b3_GXk7
+s7=M+KB+@t78GY0|r?RP1_e_`7R6|8!gqW*w%`)g1_Ldc2H2c0>`l)`<Sozd8{?+3Yj}-RfbcOXMm76
+X|T8+$I-A6AyK~P`^8T#XqCRgmT9Ij2Wde7dBSq8hc3C;r4*<Hf15z<`e2oAo}u&>>7kT6;<<i#0OE2
+p}N`o{ScEO~9Qk%j03i=3aGH62{u0JPIaAsO@W3hiExaH0gE^I0S;71yW6ufl+cXkmuIInCtmiq`}z$
+VyySirZT!PYMAjFIP@X;>z$L7l#aXIC9MCfgOb>$7S1M=p0WVHo@`@;m(&+b5#US_s&k>K8ZO{4Pd}3
+sQ@3rVo*ebzOZM)V&>RV`f?~APkTiVn1J}=^T|;KmR47`cdyGJ6y?xUbfbyD0KzL_p=M;)H+-=JM8Fd
+R17nCa@DaXFmQucDBgx`*XYl)UL>P(8y8Q4IQ+aNV!2Ri#D_cOR1g<UH=JkOktrL1>qO$H^<x+sp-fP
+`uI2-guRmW|0e-TrBc&+aC$@eKhI{ENRB%l<TTi@aBnqKD#Dcd0ydoHo(SIHt=-OzCsXIIbTDqB!cmo
+an0nUcXj17<HpqTU`w&=AY#Dk!QGtg~_K3LbmnIGeST07-+pa4$x`AO~{tbM%^vnGJ#Yfrx=q%pn6)J
+|%Oy8~;WF<Y$46z;XXN82Z^Y{}2%E=YL1HF$hKwjD|>xfVcl4#7~2w-8SuQ7K!8g=HeD+?2ah(PF*(w
+4x@Wob&Ghmz$xBnc=YqqBx*M$-y)YcgWj{Sdkpn<Vc8h`E*%%{viCl=2ROU?TCner?iEG9s`TAE^WMH
+NdnVc6>2{3m(<9!zop=ivM{m8k-TM;SLoSlu#qSg9EjbtOw0c)K_V?uShv^JO?co!=8>s&-C}IzLP;_
+^{W^|RXe01R<E{%yx@A4iLtvR%<pW0;p;E4kKHWc})Hot`;(RTsS9*F>-LlIfH^2Yk^r<*mNnYFx_^}
+XFAH}`%zXb+V3e!9=cG{JdqQ3BuZQ5qkO_iuyq{`<RqlH>nmn0_-{@1q&zdQ{*LHB#rOl!05qfyIgjq
+)pMgyCvKLqx^g^WR^{xHZ}eq=H6?|QEc5Byyq$Eo<3W6v&Z-^@F1KJR=UF*AtYf0;*I(_`o)^1$f(N9
+iriIoPXFCKMvlk`%wmDi`sU~6bU3ZFYL0U(QzjDmjEE4T%cPa0)M3A;UX`D&p|N(tIptTp4Gxb69cGt
+$I(`J#wh|9d<I-Gvz7lhNbu7kap<zG<@>0~wbZ#aQnGC#<Jobr*)bZxY!7C>H%&RJ)jT+NhUq|9Tw)*
+SZ03PO_)WO`if3?)iOQ_|Zk0vLI@ss4xm<|sjclc7{nj^jq@SSco-*7r#mf!Auvvaf6aUy@=8ZHtFTd
+ie2?wRcY^{g80f&q$eNx29aLW-4#&cRMuy=JN5^;KRNi^L<6t`w%gDx#A%Q@lS{$ieqBm_p{uD;y{dD
+%zCoWLAiGzG@`VT;3047{b%}a6gf!Atx{!9#5*3%(|I7|AeL}@5gP$c&{IM_8G4OIvMu=^1FJ?$J-B;
+8Sh5px2*5SiGM>B_<!qFey2M6)fK*92r-(ai7&gGx0Nn`mt_(Nh|{6Wx0+0X0`48Nts^8@agYLZBhz2
+&dTgHB<_B&*-u4rFvz>3JZr&Wqf~9bC0yp0;#z0jF9)peXM@=U6Z}wMZnSg(j0drtJGHN7n^N9DNMic
+KkQVi5;ChveHiU3y@tch<$OCkoVB!h#?WN!vfAW%YsLe0(l-ENb~|1#7#fntz{{kisuyyS6Tb~UR%@o
+BWY37M`oYrj|Yw?T4ksdIFEZhmHa!m263mggC3L7B$S-EM}okRk-?7P1g2zb7T%{s*Mw-`6lKn~PR{7
+K14MyVe7&Qs4YJCAer3*=B0?Os@mb?)XNeS#Ard_5wAI)@tJ41VP?S%Aebyu;JmKnvxw)lxkDpKD+nM
+K}g>7Yk(xLKb5PL&^P9e&J@0p11{UQ6Woppmh``n0R~M9+GqLY(u$wY<6}iWw&(8xpg<_)s{knURRk0
+SK#ASR-f*3opW<HD3N)T&n+Qpcr|;t)MRzPZj04)MkRQPPUDU6qQ;sDitRM}yIi5`v2YIE4Z5CgT<Wk
+D#UYr-VsTid6T=&)7G?}4!$}k}>IwTB59u_nS!bcv~MZZ3<#@C4RdCxHWJ^dPzP8U^?f8nrDQ<+g^s(
+*gEk33%jAL99E<gC{*a2uJZ9yDu;m%gd?CbMAGnIVDPZ}-%;y(&@I&RFciA%cgnqool^dK0?%d)yPE{
+Y9*v*iJNIg2-F+0;3^slcj&7{g#xFiE^#s2}g!!r#-B)C6Ycv#M!O_ap_^eD`}Kbq?ly!7**FU&Ir|f
+HM+l#$UEv8fp&I|aYwp820#xVY&Jhzl^>|U9W5JhgWoY1c8DxGnVhGsOBQlQ*>3?*=&K0mmjO^czV)t
+Qa~^iR?bsTT>a0e|7c{7qM6h$(&mF5F4fS+;X$pL4JPJ+Y7P&y|8^oq_QOrmu80w)HVkdZAQ(N>AwJ;
+!!1*UKb4OqU54t{?0OjQQrnSX@09qHqU-or$HK(8(;S6bgXWfx_f_GF3fbj_~}v}ccMliaLhwczXGUR
+2|HF;h3yRY$ZA%&NF?OF1Yqu2i&i$!$-@&3VMD{<-Hv=5d08o3*C4t0<$kILYn#tPwBl&QEWu?9w52;
+cV77UWd<Zd31+CTny)-*H|dS92ppk1|j&V>oymUe<XWa?A!-C71wg;xW>N^z7UYR?$*hnrfmMxxO!+Y
+5ae~d5KN%r-fRA6Mz!v4*LwKvzkd5r^%(!J|MRzFoYq~wnY{1zZ#E7=5bQSs$61zPk)PGC{)a<-ZytX
++%nx)RUy6kkAUH}hAnk_WK&yF!?kNjmr68}x0*By3Arg@P#a}1lasarqx7rByR=q0TCv6rDXWogucZc
+BS5MUHY>1|_YIIv%z;v;{ujzDU<02A2YJh06aX!OJ0J&EMIhy$e8-mHYcX+VMfqk*vnHWC5cgn%&w96
+$n^LF64n1<uF@0yekhFKa#I4kY3T_m5J^uT>wLpDc*tZ}1iRl!gMbQQr*QW)abQx=NAB|86bpcO7EQA
+?9k5wpWs-XfFGPyL_@vnCgecyKi=9^At=_(f6tAfy@->ivubnWcvpL-{v{2fSbB0K{eyHQTu)4w{7$3
+r#_2gtNo<EWwp?ES6$?l3qE!Zp4fo$$;}TC&DHiUgza&w>7M5Or}7AozHJ$_P3yV26e}Rc0G97<&`tZ
+U$$OwaGM&92i86lX$$)+!%GhIaM<gCW?bP8qP9`kmBcdU}87IqxY%syX#Czti=hjr761fn~36fFH?5=
+7&OJsmgSv;wCJ0ntyOqw3PlbijarS(-I3wc0g^Hl4cM_y99-{aOptVs)EUc^ns#UgN7n@+POncJ5!;e
+mNC944r6Bor90rIYK;!;Y9N>$|ga@7PD`W?ChNIn*D>`{PVfQRBC8?{=;i;O!&DLRhu%mo;NDYTlh%W
+a87s=It9Tz_(jisP`IEfG7vFvtNnWd5Df&gq-Bj=I}b(jzprD$DTZs*kh$aT`aFPi&>2+V%(8@-aW~+
+RV<i0b%N-~ok%gR9XtvcM@Wdti&jY!UA&^m2%{&Rn6&-DBNeqsa~V66>24giTR`s@Z3+YkV_=>m4=2_
+(9YQrQP89~mlqqp@bk_BFKPI6^@?GLrr-t9|qVb!{K7`lYU~ASXD5!Nkq3w;^?R5|$=fKSDp)Jf7)z|
+`IlQk!;omCp}u~xB8q8)OoU(_>``}JVAx{XRu*P-qWgF~ZgVfA1o%M#lY#<LlARs&7rq>vxXLAlxdvr
+q1&hfV~+7D6FTJJ>v_JcQJ4V4xNi$HpuYXSr!>K}Y5qm8p`%k?S65ioG9OZe20eohGl#{XB)?j?Xa6X
+%QA8OI|BmqGT6Ez=!_S!6*FqI^`7J;HT8$&vGd^MHds6O2B!;`b~|or>^+q$$-L@KmSwN>+cYR1vSRG
+!Y(d>9FOcwlWcTp=5@F~29b+CWUo01)MuOAtO)gk&r3PnRxR!=SMqf}b^B5Y4t(Oyu$nW}VSGON<rN#
+8WUyw4V*X`eWt4AD7t?#P19W@hJEBZ9`@CDiN0(`B89cP!8}idjIB8;Hr8~hah$*>oNz#QoS$jqCad#
+ILf1=MOWUI{06r)}Dvb=GFL9)sL%1gkBh!ic=1r1;6#3=R$46B7^bK6hwNqSJuokPw9{(vZyl>>2zdh
+K~xuKs}@1v3gy<l5*R#tYG`5=gg)eP$OcD#Iixh57j@yQQYN-XpPtFv}-eg>CYD+6g70k&OQo5xvxxB
+e#30C7=2oadM6l-G^0$^4kjC)Y)M#z_mVgj|RHoCT^X0R_e<&LpWkS+;h`1UgN`F(1#whgp&9ZdiM?+
+D3K3IAP-^^9E%}oo_hm*LfxW@3+4&ptE#K;oGab*8ew>{(#zSTYGTh0Yml%vA_}yloX~4#X%mv~?Z<L
+wPoe^m($O(cPkLMqvgygE=Q)wLM>kx%aPf);Z+CDgKlRAV8;n_0((W+bRtV_@tKRV#KA?LhWnF+al;B
+;h%Bw31Nx~wKuDfTH;d8OQPW8>@;@jZI`oXC<n3%9s;i$elm*Y8svcWLyZB^k8LL*Mfm|R30qXYzz;z
+`c7xBG&iaHbhdRbai61bbtDK#s5Ttk7t&t=Z7#GLc@C?orx1@S9u7BqbM&mq@Nox=tiTGfQV>3hDBC*
+geY;lUo9};N+C;^ZM!s<ln|=pOT<T-sOGV)?+;W7Q}7drFGoJfMMYmJN*B3&^0CCegbR%PmK48*#7fa
+Kj?k11c6fo`DK|dh+d?i#E8s6M3*E0$l7oZ`oWJtfhwjf0gx!&@YB~_F7bx_aL`l&*cCEBwML<Uyp5q
+iH5T?(7zI^PB#2g!G)P3V={rbF{Q|my@~!+$(fk$<2P&vI=#pXI_>9g$`%3&)8sDH8&{;*^Qr;v0d1(
+43Y5NHFQXo0LLCfv*?H3?<ii0lKzkqH^4d|Mq-vb+xjYMtyr%snQe7pV3N(T9>RD4^00elOOSTJpPrb
+?6+0feBzRUXbE3F=;_lP4WO#N->CQ#OZd4;|4n{}Z>*A8Myk)+X2Bbqbez2B^&B^EPlWZFs0kt{<@r=
+q<**au~>XnT_);c=6u0n4bT!t@-oRKW7g8cc=b2bLhV__02#1T}1@?iRYOA;5n86&oMA@ivAcQ$xENw
+t=Bo+$zBsO-pYwF(LJ}n<S-OouU8w_Fd12II9<~Qo>Y!>M$V4z_+^dksM?%!QLTfMV|#~ZY#fV-y^k8
+D?4w$5Ah%5B{f>{OqPcs`9v2#xKOYYLIVZ7Q>pqGjQufb7l$n|y?R-s_HEorH3@LXToKsq8qLg($dsb
+j`ugp`9%2j{iT~x0KyvNRpBO+2V%6C1Z(XNCAk3^|Xv~wQW9o=5GUl7M+(*8M;WI7-xnRD{xnZ#_SK2
+Vm4&HL2(k|^0^e~rd`A2-4S_BaN8r36jeroUVuwWYY*GZc$*4PUn<XnMMQUM{LgnHMLzpVX802zf)>*
+36M64|&U#%OA{KCjAcbZ7<F1J0Y;`={r|baAvLig9FOs6h*%N&H?p2;nr+)d=JGg*=1L0dx^|na{6*0
+gVH!3RK1+LLHYquT7!Z;0n+AeO$S%ulwII}ezWc1%G?f6Z$HYDZQHY3zHJXTAj4%2j{x;CC@uLrr#L{
+4?DD-_nKN(ys~VSaVE>7W9lil7WT$!idwa1rn^JPJ#z7}X969yIY1}q|;)DN(54kd41%D^y$3WW!w(S
+=%V8y8p*dK!9pafhI2%>?Jw++c#5RKqOhtoM}-cH=EBsoA)tpR#y+d!}HX9B&aqJRUylsGOe#fnqRZ&
+NqH3G08lHR#KGQ{NBieZt2xN<S_DZixf`pTPbJTmTI4sp&R?dpv_3+dU@pcQaGI-HQ4iu>B?y_dX7KA
+MoAB@=re%=*Ov>e@wO94p9ByuatgMn|uHLohlu41|x1Ge>`P#8(CbzEo~nY4hA~CA94w<jDTC%7XK$F
+FkCio`_+_#jFO)@W!O&SCh)MbZy)3N{qz~|aOZ7iVAcMC8%+s6z8BE;#ds6LrOJ3Wt{HZ6$u@nlO}d@
+*V+-|wB8cd!YI#0SEFuX_dbq$ZDCa!J++e^5TsODx6Ec^l2luNJ!11^6b&0<%_0U&q{Xbdip|95Zf3n
+m=A8WlL@Zu<wVfpJgrEsjgXNbNeP0)KQ&0<_m(!PUQ5pLsssLg@LNy@dBYUXr!1<pfVbZR*5vEqm>7c
+uTpAv?r=IAO@L4B0AN8dy_}5lWc#R$@|kyl=0ZKeEmJ?h5V>OP2L=i_}9S++!KH#Up|otmhzketRS;Z
++}))8s)tbV)qnoX^g(_u_!tmN&9k}mGm`wlyIthXK7!3onk1Q8x2uw?O|~B<5_y5zD0-_9LZ2NrQW#<
+;BztX9cioe9w(1&#(1IdysD<BQBHX|sT8s;sgsHyd-nWPuQN*>RrXO_w>9w=pG{njsj7_*XVrS7LX(u
+KcOSYDR_dl3tT-*Ns&1)L${bfkW}WPKS~|M86{h(Q>J$F!oiF>Z&Y9x&zYrW08GLNoVCV3!-->_z=GJ
+MI_f=kx!|)o${F?t(Uw_Nn{4)0c`b~_-ynE*TC9nG5lxv~_DEsrMKNmRszje{yD<po}^#gXsG4#uXAi
+)AC8RsCHSP<_@2of~5WekAJ7y-a<!T_8LM2^3vJI;V~2pFcoK)w$&9?+l@8?Y3=8w6<b8->}1V*%Z9@
+xD&{I}ZPIh3Gr3nY?=xNcP_e`{qf*LF-+DfdFTLf&?G+rcP!VkkE|Zao6-++p*zvprb;Ah-plNv(We(
+?F?v%`0L(-6(j`v-@C&_E}Z{Fvh<DS*!l^4@=zeknkk(=RkC)D`+K2-y5WaOQ%;j6TlW)Iwdu8L<LJ1
+icneZ^<Jsl!P~!6Kw#xLGs!f`7thOCfwyi=j7TnUBSCeqBna9g4f3#E^BG2>2w@M2mB#%%Q%7X!-k$)
+T{{(R8y$9%*~QIIIKR_*m2PmA=!#<yb03HeD-K=w_)oR6Q?79#t;2n4PI7SHrWl;@f#eXMO_WxE9>xM
+!-h=G*>rh_;&2j+-UeR_GB)x8si6BD<~D7)Xlt=dgNhgN&z3xhUR8c!%c(Q<9|dTe-%Vv@&`M=Mlt<|
+2W1pp<f*I13Ldt;2h!{VLWU`gWK7T&G5WHxZIl0j~j{h6GjWp&BOhpSKah-^keJDj6}nTM`=fT{mS_b
+)cOL+<8`|)PWsai70SA@Gw0fsf2?R6?p{UD@6(rTDCUk2`PFrz^0eW2NqVO6Q9eU!C-0p7q(5q}5D=r
+r(3o*Yy%qNp?yPiHvgTFdj0064B-f`~1im7*KA8PgcTpu&AuQ}8RtpPKH0ymFwJ*`8W+vltd8fp~%!y
+az^)O=9D)~Aihg@Af<039ZtvWJ=cPMnh<@R;Q)p#k#L$KH+8xe<9W*edygj#!Qa0gE~z9$aeK;f%2nC
+5uu!+5iEOFT6LgyndUPA@o^RhtjO0w3kF=C@bq8#=cd4*69$P;x%Bfo-0<N!!=-(S})&ALRo`(0x&=@
+@KD>vp>3Eg6!xcL90cE?N#w(cY5tErAZ*S^;jRC$%($^nz?;PlKJWLyV1^^oo9>(PG$-P((PH<KGus^
+Qf-vL=1|#Tnyvm?5|(f+Uy0H3=JDDOPDXp@#)}N`$mp4HGa{*Z>|luTb_k8I3a2JL_k3=S#GY%1T(kS
+SkfLramGJgF?z~)6O5dOHQ9oz<NIag0_6EDe5t5Z=3HGJd3$ZJ*)V|wn^nhP_<YAGh{W5wj^I~al`hk
+q7)zBH!0aafu;^o+rg%uaNp48j3=~WpOv5`rbD=p|T8*3b?9QZD!D#ysKRv_l$PBHm#xMEKup!2<d8Y
+pUe#~{&~7GL0W<?^87>cP_dQ(I1My#A9CoDCOnKeL(qQ0B5(NRQJ(&iXG<FynuQnSQdX|AS#ZE#+Sh@
+t2uGz}3%yHn+|G6fEei1_WRj2t92E2hBlaHK6}|3jxsVmu539-tuBJpi|jQC@2w3-(emKB=TY$1g^H9
+bPU?KLAN*iF0KJ#q+it#7Qk+Av&vCWCWF442RFlrzcmfe1k?&9`M*gDFv1`?C<OxYvM4Bb+-&SNRlt0
+fgI;v<&GJGfV1A&GaT|*GOS72+vw5}uDpOeeWHS9=m0akPyni#9`#_TS0V3OmZ}H#pfEgl}{f^B|_pR
+6RVcZ1ShXP~+?BDdb{-T!Vvoz+dEo+MD3*_*i50P2!TWySw+MZ8_&DYcYYPQfnneJD!g?=^Ne`2)eqS
+J{xXZz%%$OW%Hx|PgTRK|p%<L9)0lq?*6vJp3-NnN=SVnx~kmJg1T%M!$;I-;89;#6m~^2|ExH9eVk?
+LOcS)hULbWI0Jnnm;Q^uFB9^<jyJ7(it>99br|4bUZSOJv@z>l4)z!+}TCA)tJlkqccYnaj2gApmYrb
+)ssGMLn>8w+yAF(6|l0%Bj{Mn=U^TB1m66FeJk%Oe1Z3QQ}YO}%C7^Vp4w|G5;xh~9rrw@vD#}H?4Iv
+;3{3A<sE;uBRPGZ!GtnDq#}0NZqr{-hCqwf(YdK0!F7V;0?a=-O`-gfhExPQPT?wDM?c;~=aB<JY)hH
+99CXUOAqaR0wxTv2->s@g9wGH|^WiZEwEf3cwqm1ymKjf_|D#xY~#PB+OGh)!sRty-igB9S1tS{>1p^
+9AP$2IR<dTMq&jND&cQK)w}#}p!6$OXuh`u%0cK1k}s6+|ms$J3Dv4z|IpFRYBD-L!WW@u3vy8W$C}w
+@D4vTl=+}hMv#b0gCd(p;_y3z7v721r()eT+dUT5VoN+S~ve#!@>TsJ4y=WCPX=1+m!Ard`-zgr7#g{
+tBMtS-?vZRqT848Mv}DJ-CyC&Uh{DG91p7_Jc5&lBn6D2WLW5u*6NeUtaKqhJ18FR>M6A-*s`~m2PQP
+ltE|=u7evf$;=bb6P7(aIpsjv~%9e0TyVt2g>T-QWOK~|vr#c_bt`Q;Ys*oq@<ul8E?dN&d4Yo;&G@W
+Vw<;bZ0-!65x|CcKXw)s(g`J4N8*@oW)`(OWu;fe1$PB%}dw+#ZEBUATjSb(h&IP7~%_W!_Gzmtaj#{
+>V8AdE`i1>_mXL~KTZ{BXH2P|}%Ez=D8AJPdSsXZgR0uU$a00KIoi3T)tJWI&B3{w`5xKv{bFraD32y
+wfQTk}ROTj|CnYP5i1R6n*n<r|+8f_lVg$QIeCO44!%GtZ!ajR)B;~`sV4R5Mcg3Y%xxKs~E*lkai(r
+aCh`u$8)oQ1@)J<ECaTzh5v#ejL&{D4!SQ&Eo2JjXFw|5kf-P&pXH2B>3?(e(ibm!FJkcIqx5s_B;;c
+O5Mq0CSG^_AqG0;YIJ~WjgGk@8Z2B+z?Zt5`D0YEFPy2b}9Q{<b@3!0zv}CGm$qvF=n*8a($rcGJFq6
+nE9fX+-Y4MN3_cA$yr?yrL1ZqGj=Qm|cG`Y47^jYiqrso0%@NW`iez0$G0b~dI;`cVS1Xp2^QMYgI;`
+ziI%T5oU)_}Ead_8NgTl5qgp;~dAOj(zru_&Lb(N}tCO<Fj+!!Ya-n5A>{T$~nZGrX4-jY=~j>vN}*@
+%TWED0a@AGiOQY+&ba8a|r1km{7n)xHTQ+5+D3?mZYMDAMuVbAL~<+6M_mJ=QzR7cmL5+Q`+?L{hs+F
+HwxDQmCK6*RTlqvH8+J5GnG`y$Xj89!g8Y4d->T~!M4OnMRJ|1<3-5@IyoJCswx8g604m&hBkC-x}r6
+zS$&nedbe=V)fNw$`B3g{zp~^xZk|lCB$2v9b;%y>vN}<{kedEZ8By1rp?J2O*+m$6L?uf|<7{)>e$@
+;oa}W6uWi5ZM7)MhHkxU23K1ez(uD^1+e$h<u2ULN6qTe#@hb8q!D-UVz6+>wV7o#}ax0G(%K`<Ws2<
+vtC9_IOtQJ3s4L+Ne5h)wUV3fzsl)J(h6rDKL@cY9se!EiC=W$jdz;a(k|$jG{xf9&i{`(&xG{1Bzk?
+NSzs2iq6;xxiI1tv=oG{60XZ=z<pm%tvdj2DEagUy+BE6q{cr;bLPwnx&^}#X#c@YwDX@_KFVc^HsGD
+{DVuTL=R3IYT)qoWeto^J;v^+NY~eqZoGirKaM2g!WU6~L8yNb*wScOe%BY)H4NfzM{7NK^^2Vq+=NH
+z8Ff3Use3&>V;9LwjJ-aCNSirTJEl9xZMq}xUyM6_!pk_w8vV9LH|bXB8M}uEuD>Dp-*&{*ylwN%O#H
+@e??Zmweu=y6w_`r!ai3PdY27f^;{<HT@Xd()h9M}rnXG?uCUC9(^~+9)|F7ooX*2(u*Z)gny4f~Bt+
+KgG39xgU14XmI&njZzY60fW0+4*A2?&9vUpt0H4&t5^37p97Y~(!*%K*tZ3I#T8vyE5|e6lPCr3eTMO
+dN*&-mLa#$1nv3lqmj<lE4l!$(tyf1&&_)R*R0`m803)DsLv0DnPa9HVKS=OIW<MEH?iW%K$bYiOm^&
+ccR6Bd7DQ6%9u(fsBm1a|G}90Z??_$Cku5%wFNqgyzptFY@})vWodzh(ImM!g-6h>)o6-50e0$hv{@s
+-#RW(tI}6#Y5(>SIW@mo5S;9}<2R=6+%1@TbOgo1QNEh8tuWdyH!log5hqgbe<O;~3gY_KBuM<`Rp@z
++jw@CIjbL9Hr%N<4MtpqJC-&*_MO%{;VNUJ4YQ1gS7?7h+Se&05iXaA?TYUY~0Gdthns*tpfUrz0B%}
+jb1u{zTagrJ_xnIiG2vx>V)Fo4Wf9B$8P0~+TIp@82{A$i$uZ5sd!s`P5z05$1`2{vT1J;acmKG{b%G
+y+uLmiI^fg_(!GwDWrFake?#pRo;J#glRNVOW?Sfka!==~%ULb!b-1Af>|!Wv8p<=bmV>x?W2tsrIRh
+`xfEe9xgd%8tT9oyKW^bDOL7bW<iP&ej%89QIUJ%u!oAg#d{cua}tT*#LjG)JKWnlc{vGBi=wW`s?wu
+-+IQX()2J%zEF4Dp@KQ@7PNi;ul5*9qO>VI}2|G!{)zI`DQJYnC*yb)vGf`an{i3Ou{a_VEKMnF`UlM
+v^Ui{H%A0kAPHWeSE<#fl2t9KP{Nro7=r@uHOZR|u%7;kLuJmSmpYISwLUVJ;gv$9p4T=EgQ2S}Dg6}
+PCt36I0bUlZ<XjeEsC91X3d1~SXIr7_!=Rl%Pun>Js20$=^oHRXnHX8m`Q{sWiI{Vof;qgA=JoeR2ih
+sdn-c;PTPu0J0?xNLHMJN5A37?TSH?nQQK!^vf7PUpy<`R$D{uE8bqIHfF|2}<yNw;jm;exUPtp!COq
+jx&86_vf4o^o?n|pz(J5j*ZiS*3GKP1ouLu)<Rk5elz%Kdyq<|nmIeF>R^mAv)1EkxRn?n1wxeVwWbz
+k6|c>4_n7I;w_GcD^3v78;OEN&uSY+ZxW?Z}y9uS+{o1o+ajvPVWFYUsdtIVYojtYPUJAzxCJ@{#yLv
+mY=}NpvbUz+A;a;FK2V=Sxhm%B{_w#D4&kgB7nKnd6_iV~{MciF8cJ<Nbyz5tP(ZhU$W<ywvO#{On!U
+$|)?v0&gRxI;)kpaV{uK}{U$L`iy6ie**@%*S{LqC`#W6>`oJ$IAz^!<o$5a!jlxTWoMYG8JzM>n-?b
+FLd{P)mo|_1;>8xDswDBxegNZ2x)OvpMni^Q!3>xApKguz5Fr?|6RnJboVF{_FlP?q54(!{`i&AuL7z
+oGkqxjr4m4^goXBz3rwj8f6HArZEEBej+5oeCZmqpiKhFfayfuF<CSL9&GUuM19j>C+|-0g8Fy-wTea
+Yc6c{;7!X{)>ku*K-JBi2on`#p!I;0hCllbsg0y8060Y&D#9%iMoyCB+%e*_jSqd~-Qg7Q0_(MN=<hD
+VP1hjlm@4ZC=rk%XmMdSD#Tz)r!Y@-2Ry#S3OfL!}8U1Ry(E=m0!=GB7vc5D$tBABnqKjZZ${v0=OHb
+l>v-?*|(-be4rX2DI@oV9Oqq#}RkPdMPtZdV{fnhGRu)XWT-tBKm1XKRWV%v%5Oo4I+;H_L^9T8+&pz
+2m1q$NXov8G1)!KdrNPd6__Nso4zjCu8W3+kTk-yW9PLGJP<6=zlVOFnj2qF!tP;M7q1zi(&6t#SCX(
+x1NyiO@+|*`}tB5!l0!$zS>=qP@Q~khab_KI@cn)`60C~5)B$3lJiBl+_?}97%kDd2qNMaqnY|bXx6d
+50->bM6@Klwqe?a6<Q~_cnef9?RWE^xyE!RvN^!5HjP<3_Kvovgb-b(}NIlBuwz-}ChO|UIBgkV{H>O
+}yYr(S;T5wUOi!oA}i#`>ld{68i-``)_4l0Fo4QC3k%O&I2RC{5on_trUC`R&X&QmiY4?4-%bBPwGK&
+pe|vpG(O=8$pd>E0($zj#_msb+nAZl7wx-1b9J#+j<lUVmG!E`Nv52)*;D3+r@z2e-F0KPVA)TgvUJo
+g9eV5XVEjV|xkawKQtP*&r_DQPqqmjB>OW8(p@j52>6iFHpN|!(uK|=84hA%wfW0D9M>tD)dOX>?;;|
+M3goy5?4^?lk(a;{{y?OJLT|HnBZZAxhC}L<RTK`duS`O<&s0X49}Fv$}0muuh@>REDI0s!L#4HQa4e
+|EIkhH{XiTAwKn~r@+HE-Y!fY{`8-;L3_U4R>6jW_bEiO;R1u>`kjcIk^ZkYq#`?TRM`9nAGwG<ojQC
+`a$S_?O<OmzYkaSS7K}??sIZNhl;em5~Rf<fbzvha~u!cTfU(;(hNn|5al<*RKyqm=7uxo-FhD-T{hM
+I%E%mJ3&xzW_w>q|L=XovhBq-ab<`Y}i$8>d?AwGOYY^r>5+e68lzya-&AIKN6ItMfNCRlc0%8|V9Dt
+=U6e<VrF^bz#xAD)=u)6ReT@x((%PKJg96at4&+x;iVu)%9OmXrHqBkosq-zG8Bzix<YTG6q#MZ{!<Q
+D?RdoW(ch`#&i*pBT~}{)C^W*)Yf%SayX$APFmaY@z9X{*+Tu1>be-QE24GNaC^r5B`bGztcM}Yig1h
+-;tGYgv`SCB^@2I#y23O(akH1g3WE8La%_(6tS>sg7h?TYK7BIAs(KfbHIE#y_}LNx<eJD)PC8BxUI?
+W%_@RlX5i?lzYUHU}I;vD9SpT}%#=WBhACB6hKsk%^H06R^*ao`KtsopdhSNKtRIP(C@EmrOY1h6}+#
+q_?tK|1>h0H0xVQ9>23xq6^;X&+Tfq1<lC_*;$eD<ha{c14v6i*WNvL!fyD^xqFmn-o+u#ZfLDOEVQQ
+6G{CqKo4&G6c4xVEY0U=~HbZ^@cqxW=Rbq+iQ|S#KT?KdyWwgyUT*DyU8~P4(;&ME}23nc?j42LnjP_
+K0-PXv3IP|rc&yMa6#0hTHCysMkZ|;jyE}D50B+!sD}gR?}>KGISp^l*WH@8NHY4+Sy=abIxsFB+3r|
+#B$C#Cs_?~e6~~G4tGz+ZZn6tRIY0UQ^~gGE7cX&1(UH2_?|sO44afb<$8%{p#=W?%a+S#k`Qh@>ZZ3
+0dI3b6oU7TZYvd>vLdG!7qNGWWo+qHt&gN*L*IM)@i9ufaZ2?ygv5yp*v$y#qxNx;C^D--poYRkW$@^
+~jGHc(~M*BVex*#7UE<b(U$7fJemaL(@;`9JUd9<JdOLgEyG;53P|EP@j_LTu2DWf+zwNeZV?gnSpL{
+RzS0Z(j5Kos*{$AQ6HRAk|KhAjN^cHOVjp@YGWDugHGz1k|_T%)5vUf8#%Z_JA%JLxGSA=;*-zoxMXR
+1qnhwK==&V(BUr-+y=XnH?<+lzS)LIFb9yge^=unZ$L=CDS;^lWdA>8O*ZHVR8dk;)`nnUI}r4w-taP
+itFye%qO-qDZkz#vyZoNqaFhie$66%!mWjo?LS;^`GW<GuFGD|NJXEKVP9}f)On4l=@iqVyW5ywR2h<
+ebaS<Ajv{)YnaR3`hXUG3Tz~mb+<A4qcAo_Wkp7V{$UHc&a*+AO{0Z-_gX!qxE5uz=MxcQdfqD3P5QS
+S!&g*~Mm<LKC)MJa_${`Q$`@xGg`;>&-?i8#LqWPmsZ^rt|E88|sf&{ifEi?&+L{W;zk4!agdfqJBHs
+hNlSBRf~j)j9eW3~}~D$=#dy^bq`dYOf<4>cK7cI;ApV&NVNjDI6NnwbomGI$hGrC)tJ#pZOYLMO~>-
+?Ww8QwHKA_+>q|!0uy$9{_GB7d5`N|dvV5K2QSai^2b+3n&(bAh*m0LWpP!jrh%UBMR*);DG^L$Nmaa
+S%yD|cyVK;4!}Kf)bC*2OUN;Nb)Sjj+?Q#K*EK)S+$X!+rLXJLjLF5aP$FR1fHK(<?icM{=P@PMx;|(
+{iJ!V-35xKG$#>Ry*sYX9-?r?Q+S{|}{uOfB^#h4fUc2ToOZi)+02KKW0%H4}P?0A;0$R`x=wmJMjp9
+_7*pIdwulY`H{<XsBxq&7c|k>mPRRgMcq?=RwQsLtyj0S*X6IHu=)*+*9+K%`bXos2Y}UT2Onv@rd3S
++`1_b~^-xHaM6n5}Xgh#5A|r-@RTXcFB?%hIu1qWU4!A;m(A;vQU#WTrUh$TfqxE;C>g)*AQA`5q@4c
+GvfCFytjo{-QA<@gARy+u%4N$T;|()Ci%%mLr-%rj#(3f?)P!TN%3$XAe7f$utSSOHoq##84_kxMEHJ
+n9QMEjb$c%nOT^UtekQXb466fQbPqYOX-ZV-^ejOI>Y+DSI56rGXgB6Q-js$2QQP1r?hL<1ZO@pes~T
+)3O{86%$6DmB-~3&#L{n8VGSr;;0*9%BHFlF)Oea!>9i2E55=RbhaCpkW*d8zUtfzb0?zC(zPHoLo(J
+CmZ?noAN&d2SgwxYHlwe=+Z{faW6mNg2uU-Bk>)7^R>VvN}zS@rxku`cT|ZhsKga_h3&R_QO~zW%pd<
+a^Dp|8#-x4Ix2(N%Q+=z6G6%1jyGH={wm?f#d;(gF-cue#=)P?;QNsVTu?BMb(>m!{1yJ1P02l(*pED
+W+>=l-0Ta^g6%8=XqBjsoIKDN`Gt|oQIP&;-x4GU3xX%bhw9in0l|JS+sB|o6NrV<AZs7LJ0F42H47p
+YGzsRvSyvhZ0gCk9;0dx7+g<!+(m}02^JDl&2H*ITL3?NL*`Ms$-V>#8vk=awL9_hLk59X!m|#fhsmF
+`aNWCdsJ}pT7Em_}c((!)Yz8<G=E}<VQ(0ot<-6hv{I<L~*6w=SYd-?s~-=72hx@_$<+n$7*-UZp*qm
+QP@^@nX(*zoMncKUr^|KzryukP!g+!plJeSKZtrhQr72132UM}3<H>f1JREAq89?4Qb_6lz_x(S(|$_
+)4!vID4mUm}9<9EzVp_{Z^hY(A|95*0UtY`Y<O~Y&lry>qhwLEi>6sG<)O)KX=n(c7nDW2S+KKN1ka>
+nENlz!utZUoxlx?4YDI=@owoGAUDLcu+UTEZYJWsQ*XP8_B=;Q^KcC`c4`hGz9D#F9d(!73DCaYQRDH
+HrcHpqnk6HLzS`f0*y_$HmyL)lL@=F%WzFrf+`NtIoJS@njcM0W$MNJr`lBZAk4bcoc*{(?jN}ws;U0
+$*YKB#)-^J=etwwOY5Bomjii-}**r7j;*x@V@kyS(L`jnmz-V5qr@SP5SJf8X!PuS%AT)S-A9m1Fq<@
+nK)mDYN91E;c+Z(WEiZWHQanh^Jd&lFq@Uc@Jx+nM6=JzI5#H}lz~Sly!RN1DUuiG5{Pk-uvwMhe8b%
+QcUr2?w1a7_%P_WsU>mnC_!~yZx&nKVsTYi6|-qD^v(qb<|0!IBRM4JLRxr*h6n6clBmnt}vurs(a1o
+p?AEw!}Su)&ci{UcT&65yQv%Ruqvu>cf&EOw2<z60Q}S=Y#)&kf6i9{ht6xC?H-Smv0S6TwR#=mqNNj
+}CmyAwH)nWhl(7#|zj^J)>hefuNf&2(m9F9|uVEg-C~MliPIJ2p4gAzb;{#y_is_dm!?>r=qV*J4mGe
+lSNn;XACisJg)03I;mg~-%2)((ow%1vDbGgj_SO^^YJpdvR2E9Bkjy$z8c2!ODhQ!jrabo&q@*nXJZF
+V+D9&_Ix3;pR}P2<ClSVzfkzxx32`pEWWllOc%fR<E=J1MBO&5YajL7Z34M)_k`p!P?%>_=1J$D*>P*
+gTyub~JGQnJ8_OyW|X=mD6l-KMtTPe=z{oPMZhuO2Xg*#UYUPBWEyBnKFtX3<~f|_*oN7O%=8kDAYc*
+6_IbnNNswx?qKE8x9t$v0qUuAmp3!TnauYSmg4e!{@qv6q-8r{e+M0exw~EG8dPV2N8)X{18LCaV+rF
+Po)UCm<ZrG;^)1i*&=mHnsmI&d<=?7>8JLO7f06-xC=`O+>5mGHypv$tmGMh?iq2IWF4gI}v>IqT{O%
+b+fAy5XvynF3deY7scS;Cn!>b=*EKW7WQh|g}P+U$?U`gA&9H*D-_WNln+x@oNL-f3#G!;K*4E>Qq{=
+%TXBhV7p+;$zlKa#~kkqeUDC)iVuipkuSo(#>QaV&gYf66xAa@N%p;f0(ZoiFggK4P)Fax7|gY{x#qm
+{#}vD;FP8c(w3@y7Qkd<i$wjn3eKu#+_fR;#m3%Zep@_A(wHtU!tCim;T|^c<s8kh{!ou+|}@C9E!``
+7)A)OQ_Jp4czvD;_Ccz9^U+vdVlParUafwB;p=Oy^Q1OS=~kSKFsz@iY;Z&b$IgnCLigkTdFgepHFHg
+#DR1n^x+eME1EGqYZK1NnJj{_z#9Tyk(zG6wdZ=Kf<)!F$;Pn>T-qaaMGLOMHf4T*m&n7b}9#ry+i*j
+tH9k)%1c!`k|Gq3Pis>g0%;c&*DEve{%3>}bG3|J%ETmEp{VaLYJQ8FKV{(d*}mqObQWCnR(%#?dWWz
+Ug}9fydk19;M7q|zbOVeYhf=4JubJf-Aiyg>GRdw3RoHPl??vPg^xE&J%3Jh0~~IPrt}^yimuI%Hy8A
+-!^znefMEUoTaAKh(ixoYDeU%~~%QA>;l2>Rj0Rh9UPm56chs*_LTN>J4f}!6ZTZPL-;FT(+m*D%g5D
+WoAssUb6O&yV?6S+gZF2V1$e!wn$zb^isshe4|cv>7NPX3^AF&h3BVXW_2PG3-0y7t$ywYwsveIT;?S
+@9P!F?((OG$@IhcFn?SjCib)c!#O?|eudrn<8${F{9&7%AywqQM1;4BQ{_Yt3Q@`TN5&o{z@RKqy$$n
+X^#DG}UhRWF-WC$7T8yj81fC?t+ot*@kLkysJiugJ>h62B#$bdtzA$1CWYft5%DtGfqHfMtU;L(mj5R
+1hBO{d@=DBk^*JP1%fj6r~odV4MGo7xqWKW!e&2QMuPqIvY2>2C8IHpk>+*LGz3ri<Nt1#n3qGX=bg^
+d0vr-nC2YUrGbZClDM%rr-Srd;QXH@cuh~!(a-xPmJ7r1^s{30#Y7V?F0S;(Oz#mT?Dcr-0}hczY)4N
+Ai22h-0=UG)B8KKgZ|U>{?qI}Q9SfTm&#8lUchSaobYs7T0JIA@P;EeJIEi5eM6%?v_H4{lJTK)${o*
+@%RBF?HiNT6Lm}tcdeFIaa2erPMZ8Q%dXCqZ2IVB{#`EN44I=a`?7P9L3c>A+?soHqzGm6>Tr+9#8XK
+*}5@UUaku18)gq}BdLK`^e=n4tKmHX`kNZk2(@hb4vknlrNsF$LsxqM|?t*cgr_N?)wE75gAS64U}Ea
+UVOhC9>M(plqlAa{_1T$Gwv#7RzGSj22AW%0Npb-+adO4U>AkkLavxtX9L*Q5=T2fMm<rY&kH*1fre(
+9!i!Rd##&mSk<C%doT@sC|HqWt3U9)jbbPKnypUd?bSn&sWJiYE2Py`f_WyY0@E8ykmMdhH|yM^xl3D
++gi@;8R-h|pK5hHg;X%qXvxg<-L~5mC(ft-Ffr<LGip(~SWq|P6|O&MA|zs3+g0ObvGQo2$*XUuS-~I
+KN4U2|Cw)|Lgq=ponxA@QPx~jKt9)ldh)5WnS?-Nzo3!0s?d+Cf=D^AJ!P+(3T*zFArubkxWxjb_o6X
+d`-7KUmO+9>bw>hAG6HN;Ige4L-$=a0_jmpBqA>Z|R*-ZCo?druq29BdOnVLNsLE2`l*NXt(vASg;HD
+(dJ*Sgu1@<SIk^5go<>wVTOX1r2)Nx`-vj0cvy#{v2}Xcmi0g?_{OeHlx=T0-7bFZM19lbtl9^Pd@Sz
+oJq9zDgSUs;0_iLc~#~Q8nyVRf6f*#QSRYO|kzI;jIDvPEn^?(c!fQt+$6Qb_y@kaE?0CBN26wlCw(F
+QBFt4bSV_u%|#};U%d4U>ku{|lrWZ5SYCF~>N&=IaL*MIKhH16VoI|qH%Ctj6n%=}e0)@o_;{D@`doO
+4f7FTy@(byHV$bM$OfGxklEJ3-au0ZN&4fdQ9+6dFHajiXm^14ZQ5<herZuA$sYMd)vayAz9d<(xQRq
+>}B+DBaxe70AZsGlDq^DMQyN13kQC=NvUQe;HT8SiecDtQc5KZJBb%@QZQN%Qf4{`y!Za(QNAl(<~E2
+pPvh{#N3lCp!#*RiKi6F=h*PR`mv<<W$i_Nlms)bh61w7L?9ZcPwO2sp01v-*J)TVH-rmfDxev`p;vL
+eP@vrJYuJG^H&eS{*K-p+WQg5wFJ}v-K`~K1**rGj3e$ozr8b<+VWQzCzC!=fH&Jz`l!S=Y^AL1Kve#
+mbBFgidK=~bn0m`lI>kz)SZf}Vl@)m7M8S1b)8g)_-cZbwx62|qLvX6?<h0wPNW`_Eu<}aAP8F8GGxL
+}gh}+<cNK9xb}Wgb<5zU<j*mshIO-@EH7*I>O*~^)={m{hi?%IkRTgBKp&rGW-~0-@9TfXYN8<*lgTU
+0i={kB>;#NGb=H*pZ(p~FkPH5!ZaCTfXwLLW4n28^o<0UN=>3KAF9v97w8r$--dF1`|mfT4iKb*$aii
+{$0h%D3oHlE_A*F7GF--H(Y#J;Qi`rO00|HU#mO5r&6bM@FiKKT<x{o=$Qxb(kd&7j_N^Vq*XO0_oxw
+ZSAZ2E|qYV<7L!Z3ZBa?HBCpDsSS=D@>Ds;y!!#0dId2^t-l<0SKy~K&ck><_g}>6q0~0EMgm*{RMDh
+V^E2<9f6S`(7VAbG66+lWcF{C01)UyusVHH+kwJz23#+p0OuU&_ew$j5;Xh1Tft%kG)ooln(^%h3Fa?
+>QwRbGi1J63_n#`1-oVNJ37Od5DrFmR8a^9peWwe^RFPLu2j#yI(iY9dYRuJa(q&tN^G}io8QG(G#8)
+Y9f`IGt{>7|+&L&C#FukmXBh0m&P4rV=?T2kM<TXEL6TJ(ws2-PmikisMA6Ih%bF{s3HNDent{M&mPN
+44;PJ}FK=Tl^X;_Ko4zo`9~S^R?U_j9d3{J{5X6>9V>c%D7tFk)8h!9(NRo6J&8Zp5h|<%=~<8Cs%74
+^h=p$$m|%PN6qCHRJm@qXw(I++smj?KCKpor`(1DBN&2iQ08u82Tye6K>Au;t)UvVPnN~=-4yWpAMbJ
+oG~}k`ir`chma8OW8o|nWgT~B2H!&1_VL1R896XX4ORpeg-{(++;j?h)KL<vR3rjTcCd6+f{xo84?c~
+q&Qa!X`~9()4D-miEq{tF{`E33G*uVKY&pm4c1U$U43%ZuPifjkoNV(ep32(87arat-3^AHmma?~l=z
+f-ByfVlTPv1!&X9A>>ZmKqTN@4I*xqH<r+UO2-670L_0|=3fTC1y?DL|C=ROe;%<~>~xkkgpv=<(V`_
+!7Ym#nxAgt9hQq0P^SoTw=MfQo#Q)$oio_xYi6Tp$zZtz`Fex0Kp?ly7LdEtLL%OUK(T9gD*$z|EXGJ
+0x%HJiKZ0Q9cau@l_Xtdm#C7?hCz!izhB$kJtFj?)})PA^BK!{N=tw!x!o$=1M4PA0_nt$}ul=L5+hz
+#$2Eug~BMP8-C(1eDkQ(%Wg37t~$EVVdvHBVP9R?&GE;ck^i7}u|9GR#Ya|P5sZ8wPQIp|%_K_5(X=_
+V=@tEH-KIL7Zx+P5K%wt$QJp5b*;9=cv_3Hfi=WlOglRNMj+3c0`O7+82Q|oZ+EKNAU1g0<c;Rw@e(a
+V0MOD`KWnJc#%V55~+zj-1Usq^)X6DRN#EnA8zeo7-NASr<nF?Z)$85J6Y3%hm;lw@75G6e)ul7#g@9
+J@vR|zxjLJVH~<Umc=S>0!nAun{l90&><APz&O)TN$_ZI3q^F{L<1cF5zEFB{UMvdBpcu75g1n4MKgY
+gR3g?xuM!s|Tw*7JP*4cA8s@ln6x9Zi70gS;BgB5*}I37nuI~>H|8%NnS-MaiL?=KF9NcjCm(y%ZqK?
+Ah;OnG;7&mMU^M*N<GP?BC#Ql$L>j2B-NLDy{^(eds0K7T`q>i?8aX2yxZ$`ITtjfr)N46F!F`P*hp2
+0)GG4mwD75`=G~)xoQzW@b{BJ(_K&^Bzh*MsF<ss}FHK+I34=t<6%38<ozqR5?Wv7f_$&>Rjt12BNY!
+#*mY8csl+8=%?iGqgv8SGF&o)o0eIfn;sf?@n6p4%DHO_|6Hh8R=#%M4fH1~L{I1+6y8SALTrNOluHQ
+)E_W4%$Y;VL4xm*GGfJo#aFzvB+PbL4!8(bmCY`%Hv1+&D71f0+S(*Ry<k)ZvsFuE@uVebSRWJ`k2pL
+*&RZNv#NTSdw1io<ikZuhPSbhRvUv-6cN;$TfYzy({MXE0<E}`FMXE9;9yW?mM=E@PizC;r!B&!_@1|
+L|<;M;4N92>Tq84LWPx?utU>0ofTw%XM_Fa@$xRQ60?SWIjqnLMdQ%cp2UJ*9$DfDqmVz@oEHjpzr0S
+?=52(*gZulzrypSp{yzarf8*q@!4m$r!P160@B~N=u{r4H{y<7O0~+8M=3S+pzjbJ7(8isoUsH_5po6
+ZU!B2|%R``xkAohb2pfx>t7tU`304gvNB#&eEeW_nXneq$_w?Qj#C<a=?u^7~N5gGVNC!l+sc&iO<*l
+W9FjsapbP_;#XNERRjX2G8z^z<f5Vez*n(i=(<e_4X{{}-_Ir_!tMz!DHikbeS8AKU)7gC+C736}II7
+VduN+jhXTRF*Z8Vs?*&Ir7-V3Go<c*y<kDVb7ePv+;`AXCLHQ?VK)iZLkD!$ddA5UsA&w2`RtYu`C7Y
+P^>0VTy8RAL^dDsuiMO~xCB{vdh?F;k?ePY|3nuf;$V_Vq<T8cI`>4fdtKE;77WeJ+ZidmUvpcV?3?%
+MpC@Brp>h4cnR}CEN3m^Nu+LXSGt*@6i9%GzEF08FqOMVo8bm1}%5O=_`wMyd*?Z^xSAw>04|fmu6A|
+~myq4zX?%J?`1skn7W;I5`vKcSd&fAe=4^@)mX&sL_FJ7wNs;{q@Zy%E~&t7AD$Kv9{BR*bf<Td87<^
+dpkc|aA^vzoAm_ofI|Zcufq;g!>AQm5Ok^-b&oEqtz&cAs?0ypq|SedTl(lUFnXjtFH@>M)a*f|=yaI
+Z&=*!u3Rka;sF6J@fo(xxGYKx(Lq^3XxNrz$4Tg<zcc354h&+y2{TMJ3`OLf(6Y2Ght@gb|zSo1q(+J
+Cy)clh{5fN<7GCF3E5r~=NP1QVOl=0o+mI=&v%I|O`#A_=f;l=%A<n5y!3M>Md}3uGu#ij0?%?8<@kF
+CGp#u1Qs^q938;{*8<Xwa;LAbSd|Km9T-ZuqyeFafP21onwuQ+`qN^5h3r}b03jztm#lA%3LL)4IoMv
+9;Lji=%=WWy|uiI8~!Wt)JeimzornquhSEMLzhfL-Mez-06MXa^d9Xi9!G~i0IGn}v6I(fgE?&Y;8^x
+c0!84DGUJ0=ix;M8qTgz@vr-6URC9f^{q=XB?Krk4Wb?}H^SIj!lj3J>_HAEptoG90~xjH(GE`**<-#
+ZY%Zw`{JIFO*-i<LU0m(0b@kcT*p({1}g|%Rh{>vaDSxON*vu*M=-;G<=0{VfUNyb%4m!7qZVJ;yFJK
+7q#!Rl=0AGxXQ)#@kAftfxmF_G(#r_ABel(W@zag9t9a)JK69|;4D1*BXg#&@<ABRRPu->qqEk9e=1^
+=u4cxF+(Ob~#0mM*$~T6YUql5V&AGKXQilQYB2B37Bgi$Z%gZ)<XnqP{An4#F)iO@BAtSzd2vRg^URO
+pSE)~l&DU<65s1W-AP*D>RU8Z^6*5>L~p4$ypQp%GsX`bKbRQGriH`Ta8C~r|gmK6dPd7!z0D~49oc?
+3WN2~7U@d|enq3d)GVrU(iy?XJuLjmvyr4!C*e6tnds>~My!W(dKrJ4ZB5z9QHF#I<rwO1dgf=}BxEI
+UigBY6<no%e}8=u5UaFtyJ*Usxn7-LPT5Elv}Xo^}J$o$pK|}HWZpWU@w{#41z6DN<Kmy=^PC@97L&=
+@aN)cN*Jx>fejwPWm`!c*o!z_d1WUEKvYil!XdM7mXzk<lQR$Zvh>cDDBec7zS&hQND!-ymeY|e=&V^
+#NfdD)TLG)2kp%{vtP<raBq@1O=LgGbY?7$!nJ6Jycwx*nz0>aN`N9Xk@rHaeN?>FMdHVpZJ9vIQfYU
+L<2Xa;**jnrmuOE+BfLY=|Kj(Fvm4fMx;&6-p2JPH`vyv(?ljt|0YQv!VfGuAzjJoaq<lf<$*Bc%5$&
+P2nB1-zNw5z|5`rzwfl6~-x%yj>sUg>M8{J*%$pYqm4!8?`%?vbifvggW3_|1<R?{Oz0*%ch4)4T0|!
+)WQxxqi@h(K)=speWq4gBuo$;=L{&M)qhH{;o45<1ej<5cH;Qh5ivkzf!o0_Zs&2oqL4d2=SEeS;Y-r
+ZOAc6_e%Ti0=q0Ow&(r8cllsU?m;s&*~{edWG~_0F1+Em6nWRtZ=ej`TPnjpFNdGr`j5lE;H~T5^aR-
+tpvU0s8Mv-g_lbJWmHzsM--izyqly}O2|Q&lsn|a6r4-Kk$!aRDR1AF5n)2Tt{K*;1?p=U4@B9r_k1U
+J4{&IHSR7a_HdIB*N$ijE$PcZihqrTCA+*|XhMYU}BBV{Om5!ijF40octpWokKxjo=t-QQoiJ>Xy6-@
+m%OPhz~E<s5&|T42cdrcHiv8825nOPZihL*#Z*+|%p0DgePr8HN;Rr^3Rz(ql_suH;M8BF!*gabeM_B
+IFHEo5eU?i`k*b+j%-rsH!51$4*DUkQPgGg{Ren$~aQl1<F)IBsif>DhTy$rDJt-Tk_(x4+2D;4>Hy<
+=DOIV$MT$c0`i9k7fX-A4|#Qz28;41a?b6jxbV*u4yy@%P(5SqnL57Ez*~yAqBtLBVd~ZkKcK)Zp?XK
+oGZ6MY?I6kytE{Dd#=3^Y<9l6*XaK8M3|cM<DBdvir8-$-k}jCAPp^8(0M}4*$m!W*7JZU#V6dA_<&a
+{p{yeMT)8;SrYo4Ew=NvH_&k5Ezy4j3<Wpf4o6@dA3bte-7sACMPu)qZQ^&#-ulUf9OJmTu|D*&^zyW
+Wv;^ZiM<Q5V3Ku%W!d{sm1BR47b##+<A~56_++rae1&b@{jiWCZ0%05;8qK%?$-4n!m~ZtRi20gzzwC
+R&ddT~8J}=qeIp`usID=n6j(ZbrPG@66R2qE00H=Ryw2Yp}E9NC(K|A|QZe^@wXKgidbK<=EARM7KPN
+a(vO?A%4^s-2oG)JLoLNn2T@70-kH2C>j$5L8)f}B%~iQYl9N*Bj^u=t|j3!B*pW&Or=#OX!$1H1XV7
+^tW1PE8uWwlWO)NiZABf<bp<43QfRk8ShaJlFwmpy;6Qj$)2V_~TB6fpp${>^k(DnQM%Ut+Se!yk)LG
+Xo68h8uR;RHYuiNh55rOBpA8Ju{%k@mY?cq0f<Ft=>^~q+X>Y^``z8v}=?C5{)OrLi0PtNk4sf1tzJ`
+pg1;TQ~pFbNSjM8en!fe3;)k=q}Xz<#PbbVA-*>%`8TgU34~-RW(O(swaFoa{y@3HNsVZ2<OjgBp>$I
+S$`#YMXH-!2Oha4hVS{%;0;j2}bwR1aA#_{H^^MZ5HrX!42ds{~iW!(V=(s4uQVagz$I5>CM+pe#kYG
+dnPD))7fr-VAo(q-?ja315Bd(Ww!Hd5Mgg}3-=HBpE3k+)NW9RuLf0j&MexB+Dn^gd1)K}gow5IGsb3
+?hJLJD=MC!{CE{$l>5iP)pY<r7vEE!~*=si=_*HFxAs|}0R%ZOqw))iAywRT&u)Hl#RNLQ#8a^7?zBM
+Kn%J46{(uFQ6PpNh)nK}FHdj3*_BY*wh%`;8DQ|sQO2FO;l$g7PiRu#zNkK8^E|E*>;OINhL(AH<EWh
+`zcefy!?B_o5)m<EWm%4RlQzD7()-Y|bwo2gWrMP6<uZ316|Iz~3}*|(vSlIF8&3gC*0;C{Hd_D|Vmf
+nNv~ozF7cpJYdRO!28o6%HSJiPcE(UJH2dN{=&HJX_aZfMgg{9z3`()!}7uxrcHhTMUYhSjiuSDyn>_
+IBZr8j}#+~84In~r_7D{D8fg(8&x1|UKMdK=IMG~aQM0eDxKQ|2UqE_ucQ`!c2m@?XASihaIQ)4==DB
+!ojqLlGjsb50WLq1YjCgIbe_gDW=7=Tb;y)I?+8uWg%~mk*(?s2IqpOv9;^jM8IU&(B~a?AA7em%sYH
+=qtXi%whnb2k4_DHhbXk+1^3>0=`3Mg=!QBrXQm`I(JalU7zZ4CYLqk?L0Ad_?c_s=pqt{z)DW2>`*+
+FEOa}nSib|<V;Bq>!d2E6oDu*lze_M>-!IPiBW#J@%dXlZfp_`}JUBe3d0Po!M6Y<?O<n>U$z(@n&ic
+{|AL^i2h|%zUFm#8O|>Ux0U=_9TBhko$HZ^KCu%RQ{o54*0!iZiG>pQXY9FzCg%pkJxL#`NwAM68K=;
+XCKRLKCr51cG|xG#qBv;ydXHhNV4L!^HB`}+kU30v-{+pIBmxBJ+EH5>m^#ORHwLuXuFl|2YP+T)w$C
+e%R9yoT>v;#*w%lLGFZ|9PiI1SIpN6P<a2=v`l&5-23fghhDY?Hte$1?JjJw7w?3=iw0T7bU@$9%wd)
+J#M~i#7Nwj<~J@p1gs1fsBW>qPU!#K~ORyRG__=!H{H7f3x9!fGYJG($Z`O@oglg`ZDM((imv?AU{tV
+7r@gg(c!H#YoAKRQ3bkvLn|Xf&2*L@w0b7Y*V#0)oR_e0l9I7h9Y9J|=1sF6C5?Bf*_-;NS3u|HuDnT
+rI;kt>3t}&Pdx7<)<+%gWtqrt&`VpX7JB{tAgd9{~rDG-}`_5`|iE|ejr^`S@GLH|L-b!760?!iu$)P
+*|&Xs7XHWoGbT+{{PW+6FDI)uEBhPu-~Z?T_1}N{-=hEhH|iNw|NQszx6RQ0n40+i-Q>Rd-1g_`ziZ|
+D^xd^*I~H;B{J<9bv484_Z;t8at-|2WBSc_(>1_b+Ig*I@5L(%5Jz(NJX7g)*u2;AjX#CC97h=2j8%M
+iM-#|lv?5EjwZ7%)hPH)4AHy`|tQpla-2l<uMG>PvVzWX5TO-}SdJs<7>BnWmi0D}A84LxkgW^?fIH|
+hQ6n(rke*jtzIJ$S=F>6?#e_vW!LGS!=#_h-04+k^d!`zHkQ3_Y&gQD(tU3lBT88Uc3svhB*T9A@)O-
+|{J!adYLKAC9Tf-L}uZNuL9+d)+*J!w@3Zz;m8N=Ij&Z3`E;MKXSd>_uth4>1h&l7k&EPC6}Gk<b7Ij
+-mxciHUSedOvh2|Gq-n-#NAmor)?%+5V>jKf?2$Q4ZW`V&z}uKCF0r{LZR7aQf7cDY~H8qfZ;BcV$!t
+KyNOg#t-q9+zKdGP<{T_L!61cv>6AzIPI<-RPTP_*10O=y+<T*Z|Jb-w|7IaEYb!;5`qxTju?>Hb9RA
+|ike(UtTm^`+Y`6jKf^X1B0CU;rthNW4&lhrYz8kK0HLmxzzdy9#0}g+l*>}bC7jmSM9|D+Pe2Y(He)
+@vqW@rDEee)Y*0(Wi#ljVS3n5$T5_y(?CXYp2jJj^dYb16G++roP8wwZKiF7J{W+Q!dSRBzA7#Cf18P
+T5tEx1HNn-F2I4zcUj~%KFy`h=C7g!U55;hrC=JpHG4-zC@(?wAxFoDKc@+5V{!(mQ?(ajX5OI{Gfci
+Vrv;J-UqH$KWgwJ3K0dqBq#8EW(Vw<PBZmXuBX6W5+&<x!|bcJdtC%CBMYR<_3>)g1u@wIaKMf2c)qX
+eU^DXaWf{8Ve&{J^!;}!@d&V41q=X$c;?h3X8#M(D_z2RL6Z&&8CITS7mf)J(0{PG;Qe{P6F^rTrfG`
+Y?2I_k?63_58o*Pq_$xc7!7H3-cbFkX$X|OO2IInTCUayZhjr?$Gtmh-PudIci5(@W4I2)V#lJ`X_rR
+CKm480vvGBH9i$$Eb!P#M4x#HHbbPXvc~@}6F1xu-{-fG7E15D+sBUtas_Ig2IWhll&|l;>sQtH*Ak|
+1JSB@FQ{H7v4a9Gteh>m|AfAvSv@tlz1M~O&hi@$6lKcT;n>ncg<XYDp@SdTO?P>rSbi%bA#OLb*?3a
+!kkV9>BW695~D2?adD$ZT+?giXa#C`<fDQQXCUC9D_!_VL7mlCAG%T3r~#4&{#_`Y{4jVY2!SgEF0>7
+g-!Ldksbx^rGk96o;K86kJsUU0x@qfCRlN%Io`&nd3cNsuGf*71ngm%_dulbbdAL@*;^3~2*GNhEq8m
+yd0G?5l`18cMXyZf<>D*kSfP!mC^R=)D7X(EX#&BDb$B%vE+4A$DjP6SwYc>Q4&AtJTnU9xic)wQpL(
+dv^yq)XY{j7L;?!?+bt`+LqwZ;RqD)KLb5ca=)7}M_p<U7OeZac}%p0l4qX>}<6fD!)gv7aXVUmy29H
+z0z6KP8jd>F3|ol^FRCBHoK9gSP}QLGFSoo0q*8D7-~fka%;he;z$2_S%Oa+;z&3H+u~7Zj2}1M{gcB
+6z;KWnB0TmX}VtrO?SJS{;JRZ^bQ;Zr*~)kX70f~c8|gDiqf~mMo(`deDuu`i0#!9$-4`mO!h;Pw=Qv
+v?G_#(_8UarjKwIv6aPnlf!udrSLd(JKFj#p`ZK;Pnfp;Z%-{jpuqST%Vn?Z&`;;jAj3EC`H80EJ&S~
+<+V14@RK=>|wpw_osBYS?Y^r}TA`~>bT@tbdMPbw=zij@c3w&lldz|W@jcV-3rY+65`)!$*pRn*1oNz
+6EqP`zlFS{z-Qb1#bZ6asQQz!Tj*REp({?Nzm@BDL(Utyp)pNx}B9D03di#P;s}^^UA2m+M;gOpVHwE
+-=D!9H^Aeg@w;)=GiA0c$wnWTPup@$uRAg`&<m2PUAPFwOmtEP4R9BXU3vCUQo^aIod4Qc}8_KB2A=x
+(f}4yYoiASj=X<KXGszzVZ7d`>*9DaORAtSK%(MwSNt}wG$ZPdCn$5cO)bT(pM_oqD*oYAN~hPOc<jt
+qEeF;`y|Srj98QdhuiYVU?8o4U2U^fxML7{&Fk1qJaYfLeMh6`8CWb|Y$fAdfwD_xE&-~5TL39X7XJ#
+M|362uJt^UzhW?Y6c+C8@6?jdX7Pm}H%67aH_%E~F<Y$&6q%hO+!%SjWs-8KP#G)@c+esp=K^jVbTvv
+`)z0@aFGmY#1|C>VLfwxa|aT~s@FAQs3aG0{@;zR2{|XcaRgYu%y!4SYk(i*=qpUNU=;za&ETZSrC-+
+pDb5?;nbtU-G;&S@zZneSLlReFh8_CNr~|ZSWfW6&c?*P*DNfG~f{a5+U0#R)px2j?r|>9t6$(zES(e
+K;TCX2OTyzJ^IL{2P9a3#UF%9X|zw1C-3j?0z|Cdc4BY6r0?&7iQc37k948mL<<fcys~EmGo#exu}ox
+Dp1yPiZ8|=u(@#n2WbP2!@}-7FOpzsLl9Q$Yf+$@hrO$Xj)Y+6dJSiT^W%SU1@bLvxc%x}oF}WmxQrw
+$cTIm97Dus9eCG*6S9SF3@(ME8_rylbdz<qx)jiq(H;o>|i^e#2T>+m$w=UNKo9iFTk3eEme@azi5<s
+);t0xM%b6x}-c7tV<-h(0cIDnxmYoh}xA4NHr;D0hFs*6Ow5f??YNVl=#m2aya<r-cBtvs(vKTeAsc)
+dDl5y<b+RF85GDy>;hHzJ^MH_AhO`84%Cfu33=TQIfCX%gSrS2QF<sL5GL2S{S<Yj2>1`!E1~Pg4RQO
+IREu%!LN-b=lrK^^)C<qb94RW&_7u0pURtVHZ8*TC^#JLL3KEOC#p}o{><jYq92_2(9WiI+TG}%T5J;
+ADdaY@yr-SfJt@8U(mM&x+d+=sqBA>34Y~W<n|Hla{qHn8{~&(lX7BB>=s$Mp)i<yI=5lZLmcVusy&b
+vD4J3C5J9*a_f^W^X?J^r+NRj=#yV}iZkK*qHbf?|g?;W_v4j4|m7V4i`Y<;)b%XbzV{Hev}slRWruX
+n2bw2f!`FIa5D@qb~p?MY#8KUi&h0``qRo7!KU8StyA{d#8KTW;WAYS&zvIcJS&C=Q5xWz^A|xD`i>@
+zRi4mzqh|H9&RGE@V!tm3dife={s5($ML2i<T>L?=os+C-Qma`vZAHFZS>v<hg{~>~IY;jfrItKr9XN
+Ut+!*O^4^XX7R;v%5j(Fd&hlc*dXivjmGaXeuWAzR$1wItAyUFV8U#V*aqx%OXia~3)t|*hyy}~2Mc%
+oIXkUFWSOFsr;q1pFz&|e>cOc{pc6vm$PF|>MS?2>=y0^|+7`P$5m9)GIj=DXHHXm^8s=ccz~1(=cI*
+=sVaF29$-Ah!MDLBc$Gom)NC0A{AH*x`v&75H;0uAbFRQvSor6gaU-$fjjc}J^H#`$$b9zN2z`n8Era
+tX{diIyf<rLK}%DLc{_9peq7_}X#ypjMAmAU!3TglLf{?&3jo8>k)%PqENf!qGlc|-O$fu8`iAjX?eE
+jL#IL*#-=^{4sMhp=`)2~<?=_ZO{_?;qv1yCL37aDfPst37M<-T-p@=jCms9sm7VW+_zS2-09aHH3Gw
+|9+i&eY5O<VUhQ56G7YODz3@<hx!uQ2iyBEb3tZYX(lh}HWrO(pLM+1@O`$xchiNHC1l920K`Mynii@
+5t`=5O8QM<l5Q_&pu(!VP#Q^Mf{}=SQySokI-Yd|qrd_B+z<IVP;o3pd+Qc@)XMXfM02$aBlKqlN8hV
+B7@WryXHGdxHN~rljf8qE=8W*a)*c(*2-w;L3O9Z;kTokWK528TRUtf2T%ZaLcK1qVGDtGK#qvFps8W
+a6P4sor85iJA@TP|-SiU@Lh4LHGyL?TmofH4Ol_SV#cE00oM1AM(L=kldPqU$@a10rZ7t3%9?lm<?Sl
+>vb=*^w#@9@&e9Hvuqri2$A@7UeFl<C86J+)2F*F{Lm8#GD(C5tO^i#E^K`9^vC2H)y5B984EJEg`pu
+2V9+L99y0q{Pl=6P)2vS0<DN8J!EIR-s{WYj1#$>s6~7<b2v-93H^1N{^8X=i#UjXi<932*wLFBKPc9
+1YxNt~zk;~^%}JVMZL6SZckYQ|FGLG|Q{H2P>W_zW)n@AVD18*XY43(E*)3es#7Pqs!G6F;ki2O7e|h
+u|z(F$^?b$}#=>Nzx_HcZX#O?FDk?}SK%~1De^$pJW{Yu{#1pEw4B-k#9zL{zW-V^#kvK!#|T{cIcJF
+1A1w?x2OjP4hW3^=%R?r%;#e%g@){MIf9gSST>@0|-cx)bqlrzg`roWDm2;vJ~`QYa6JcEZ2yU+A>c@
+ulzXh`ntSf0x}wJNk*<Y#7^YLGY~-fWLJEkhd`GTNYqv-HUdCq3zlmRN6xs@E*<&|1@TQ$?pt9!`RoD
+J@Zq#jLkk8r&+sG#20K}DfXL6h+G2K*!h*p+xI!``#Y!2U-H0fvzy@bXKtsA<;!rP-J6S6Nlo^E@1o-
+mF?-jrIQ`4A&e5|h>U;RvRq|-%ZZiGBQOD4-ZV1)7uYX-E5ZN|p+DV*3mV;&XD^14BhCfQye3MqUlkA
+IX9)2@h;JfK^yOg;CZm!Q;u1~h*N&lFc#I2nbdvq1mZ$hhKyQRU0s~k8B#y7=t)fg?mcosjN+_b0j<X
+?dAa{Ku$-&W-9p><S*III7zd$5NaOcqjetA20tI4|mUtIGEIi`@Hp`bZ_5SH&u!ch(^0=F6Qbzx{PGI
+_L7RGKP(@UC0=9k>0Zg`}DsUk^L$<v4f@kzC~`jv4OBlH4!t%ex$Fr7saAMXmfk{?KyBW@H=Sit+PWf
+A#$s3t}#Z%AD%@SB64Q)7flBp)A{n-0{-f+>sJr-&AalG7s-;hSN{tm%GKr-))p`0`=PSm_oe)R34d(
+jbrJHa+A_uq568((9d2_(NlFu%CfyGzr6mEJc9<!3<!9Y`g#UOQ&y0h$3fERI%!yE~!#rnrW@*c$l+D
+YBd)QFyGjkB-)*mk0`am;Ynw<$fk0DY*1`2T^>c(iNu&)o5eaz!z&C&{U)=(VBrek2a?7>4H2*p);A%
+p-FiM>G$i5f^*s-1(Lo=-0ciPo}gbK_BT%ypf~oMYgd?KUkqR)fzVp2zS^7eIYQ0+&dlkJqlr6R96xJ
+fUfi5pwaP4T?5tolkvoNoTEAIh7C|`t@==$a2`F8G<6=Ic7mX%knN%?l#JXrwk7kr9DLW+9G@dAKSX|
+ROq#U*TX}D=DNhD>$w(S@WI>`-n6`+fB`UsVx~fo>Uw@n=c_u(EqlG2Lv$4Fm%fl=orI1$=7$Fz;B0=
+-dQoosn3Fy4p&e`y03Snsxoxk_U}l~b`KrrNG6V?_#d0sXC5Yg9*G3sT!sR5DY)p2C=+f7&f+39>T#o
+=&H=$!*9ea7$ulVrd?*$#Z;eB4Futb-~{i<DIcOrNgju3ISP7KxPYlw0KM|cuc10#0n&TDb1UFODCH%
+sm21Aks7x<1Z%6X?$)JRUn%xTEB;K+pF^jhwhWIt8j*QQhFcb$h=H^;ma2Ubl1naJf{9Y{F(B{7hw8-
+S%VmgompTr4rAF&?$aEaJn3$^&T9OWCX&8j-0Zn`C5{a$=+Tk&8?LAZMubFDPd2yP@CH$v(X0-&o*Q(
+GZ@1n3KV?=m@27`N54SfTd;SCIayw9cm1GDmZnY9_Fu*V?ofAg_o!7V!0iOzD<;$IT%I0Xb=0$=gqFm
+2u4m6;Aj8?eWDfO(^w4A|JKFgRSs<g$*RY)9d0<T{xRY^3o8<xaCmS9>OxPTVpnTUoozJv*=iaq2U%u
+wht`E1%RCOE1FW;*(_f4Gay{PQ6rhJ#|tCKrrhPUgs7~`4WQ<DxB?$AE0iSIqs>c)Q~4!*66F}r7yx7
+BEsjdJ2YJl8Wf)7*~Qft_M+vvu1bdb3b`FI#W+#J79PEs(Vjy17t(=Foh%8)41&-_92JcDj3lcU!KPJ
+-_S2+^nxO{kh<()%Kg(GtPUVxr=VMgl_92mjSmo!RxlxZ713m%=K5#;>VMJRC0f#WBoQ7HGhxxhgQ<-
+=3KnJmYaz-A$%U<?Oose=Red41MebX?&DqHHiIp5Cc`$HefYuPw6rbIE+cr`V%t)a%v7|cFZ+wMy;U-
+M&4kJ4{qrrMvg_tGPP$!If@HTZdEE@iYOl&Fzm%1RKVENw3s?HJ`%S=49jE3eueM3{K5`!4R*9<w(^h
+DMBVK@A@)x&&ER*9_l{HwqoH@fe4{yJTQL}SErFjm$9m@Rz+e6*ZN`-x11o-YM7KMCMeYHI!edp|*F4
+ON-STZFD0{k^tJ8mADGW~SQ*2g;_niQw*&7G_-(DnWh<-HyC%?7rYeF0x6HaBpA{*~uYMuPlZWEc3N1
+oc5=#Jq0Ua=i^F^I?&gKEDJZGS$SKRKq|Re7amRTxD$(Mg;HxH5n3{0F+8qjO_)Biaow+oauT4hedGi
+%C%12kz#8l!w>anIR}ZKW<?--_^6cBt$0+-OaqAF3QOI0RvuDjx_TM7ArMI5<ZUGxPQEw8{a6&tYt{!
+XVo#4@e$98hE?W)pH-~xz+>ofQ=)Ln%dGu+VdyF|k`mbI(KnLmRXgY-qBOfCuS;G)}a4u_`L5Vv&;0b
+C7AqPaKiSn~JSbAERQjnPFW{^TL#}xD+O?DEf*l|GVO6~6h_T+qJo{JJQGdY>(@FLbgC)O7?HyfU(c<
+ZWD&Pw%D=;YOFI*6?~s<xz|G1d2`NgyE0;vRW7pGa=amoo3X73kIU{5tT~c#~>kTHGNSTeOp2-HE&K)
+JRm`Q@fKsXsy19@VKFfo?x`pSm~xbpF$3JFu~=)61Vy!;C5hK^G7dn`eA<7iWQpo2VyC%N-xXPXDtZ1
+l6Ae@{6j_!AP>`F90IjbTqis;JwDW&Y$h>P((rHwjpI0gbF7|A(mg`#Dh)o`wUQe}&*VkASU}xhb+nQ
+|+ew?}R>TMWQFeD!E0^ryyOVgy*ke_ZZT>7yYu`Di>o9BH!G~5^b?=Cu$P(u>M-lKxIxpi5iD*IM#4j
+qNY*QvqaoLSV5NgNqD40ymK^lo1^3fztbT{|#wf@V_Hj@`k@Y{KB+G>*5Z%Xo-+Gn|KNZV)qdm(!A+y
+mL)RFi$k%x>=PUp~PPKJ$O+g+JZr|J+5tbGc!NKyVVr@t?WgyC{A9)<yyULE?M2KNjpC(l<+F5PspY#
+FBjsg#Mgx>FrSOb!_j1`>wVJ?W_YZwmZJtt`8x;y>1TNGw11B)n&UZl>EZ=hN8F59=0db<8W{M-(2a<
+8HeF_9sm0po9m6hZ?(X5kNEEl{lS}8AKCZf;qLkGk^S_|9F^`#`ORnF+t~0w?OP-FIR5J^!#!tS2<lf
+QbHxw6U4Rar<~_Lm+1z!tm3Bh_`7ZGKCS2Z3>jo{VSd7Q0?sr+)dILqTPhIz&;r=~P-Y}P;f2mDp(aq
+W0`~q{=-!(R8cTZu`>diBd?cnEr@>@+Vq08@{^Vg#FYIF2>r})08{pNk40bt*WfxTbtwAt%PS$A<hCj
+hpM>yI1dvvB>vk7KPnx5LZN4uVdHH!q{!0FL=?t-I{`%goyDH-^b2ShlIxx9y&x?>Ytc7Byp~`On;jR
+w^y<cOKa1^V-}__j`=yZTJ6$h+)U0zf|_q-^zaUTiH+Ns2V|60Dppba_veT6d_wV*hGOXcl)6lm1Uj{
+>8x58h8}7bx{)THI1RPF?8#ql+j81X)bR+gam{hz^#n$*5TS82VEOWN8?jh@wS&nxB;jj!JL%mljQwC
+NaHUB31&)^c1XYkT1FNm_y&buvIa`?+YT!hdqs`nl$I*ugGlg;`)qPbu=&)E;J{Z!i&djOf=8NZ^bpp
+ugcJMA780J-)&T%)p-WV*R067#k!U9VB$QZ6V*4KpX<AkfCLw6lv<htfH-P=8|xdx3`y3JiTYJCi}>)
+~)d3$-S<lO2)mGw=BOxqB`hIjV&}JiREgC#Ro+{4l@J(*R^T$KcVy^=d7AsQ&5oN%aWJ(}vuC5SKBCs
+O-_1U_<}mXf@XQfO5GOORF-j_1AbIf24(cE77FlMN*@bg5hgrp~FL9O|$JEY*ziTtp01BoO!Y{C?mj~
+gkPO>mhsnQhd$AEm;AC!%k~7d*BQP+Y2;aFH%38E<6pXpxYtQ`n4{q2vL?r^0HCmqi!K4fL>3=Xn4<f
+~U2}c+ae-vML8!_OAUm-NkxU<S3El6vK*kTzy1n!mc3Np5iE<@wkc4~+NR}|!A>dC&9tBQ!pR7Cl6jh
+{G<4H8N_<qcSVR{%*Q1we8d!l@Dd*D`H#Hq^4IuR>va3JZ7vQPyQ35~Utf9`BWH2CLpoI(_XL(5-jR5
+k`4=`G<on$I(^w&c({(2+uH5WT_|!!eTaacaA;KPF=HjBxqNrto1&WA~{A2yPz2v)PEM6gV?wEx@!OB
++oqbIxNNY@^|Q<r=stpw*K0q_U3N>+^E|B$4{kTzSNKZNDTd-Jl9u>=s%q2J0%$qBMA_FtKX6!LY_#N
+AP@+JArMAj@C3pbO#U>dj)uGJEk^D!<X|&%$j&afn_Xfr{U)$ISDn0Bqp@Aq_UB=6e6OC{Eb>kh{T>d
+cdjxvhh@Ey*`O&@>68iuW-EAfj?1C}LuS9XvH|gSDH6QIQ;?TRoEqHH?-#PN_V%u3ZGasXSb6t4ar?=
+DiycrRR_aWfB@Gg2+<wM(jLHmH66zETL>O8#Xh>5R!KDyYQT8z6dXmVszu1(xLZ(cv=_$hyEdAkQJXM
+D+Q?orLNjl^AqDEANsziE^1E%A#o3-EJkmJiQ;Go!PxQn$~*J69=4;3?}a9Ppu9uW!cuL%#jP&RP^K-
+}Svj?j&1A@HVsWZT4nnKeow&{#|qfplYR+jV}USdxT=jtr)J>Xpa?d*OVFgQ&}c5gsOSR+P_g40Nxhg
+d{>^s+bvYl%7rprenw@?Z=S*L9eXCVd}`qWe?<(pvM*|WfeiE|Uoi0!oZ%)+L`LOD?g`u{kX<`XmNM#
+`VEua6tWH;b&$<rDz(uy)L<_PxWJsP%9ulMnw#1Jdc7er}za0)w0DP992lp5O>+srNr*&4^rN2uU?iH
+GO%8^6cLn@0#mz#WqJHM7952arw>YYT2hEf0?{KMvLA%UNvt<7f_mzOH5YW;kfnaRCOY{2jgcYMBv{2
+0!oC@{jSQf)IVpGNO!0L2W)HXJ4Gz>3a1dzf<T4l-BT9ej3Tgvl2aJd>1tG&|9K4t0~lHmV5M&N&Es8
+3M3i$D?W9iA;s$B);OP%#Kuo>bjmmXM}n_i~zg~7>|X%v94llFMfzDP+(>k>bSimz-uUHA<J6SWm%Ch
+^?ObiKBp@<^*A_2K1){*?4Oh4k3@9BPcfXMUcb(V)j!8&qDTO?%%&nS*Q0S!t0()6#6zN{ajidv=aZL
+$xTL*gd61ZJV*6~Ma=(aKIb~v;3F;~ZfkA66Pbe(4PRfd;!a7U@xqM}?jE^8SAFCTV=HNjrS~O+E<>4
+OZV0;Kh139Giq$D7SmZ`cHO@5RtvtlC;xlVjzCkw9oe)pKVwSLsQpMQqy@ZvkCL%19?f%WSJcakaxN=
+zonkoO9Zxc8T_s)Tk#PHS2@T<2do>6#dk;}pn4TW)yaC>DjSAh>9`nsw|E)B%UO@{86~(`nSuiTEsgp
+!1!i;b$#?{?``3oTmK=;MqM=8)vzv)Ao)vzWFTE=Wk0U&NAiiC)yxo^tEgP_@EgA>oaq`n55=()FTf`
+yx>}qC#-4sqteXxV}b7m|5`nFVV>7gAYSCyUc4f#9_eudkmkt%(lEPeO^rOR`J%u<C=77Vt-U(J($px
+2B*A#gq)<BP+pL?T@YURV{UWaV1JoyI9f2cE-%<kR+fz@P^i)yzxk*!d!AxR8J-ABlcb$O!$1TSc`J`
+S$WF8)T2i4E}wl_Zm2A8y!*2$*cU8nqvJUB|8h<gt5ky5Q!XI>v^MJnEb8WYr$HuNzbUvYlx8!fiXZU
+*{icOj!bf>H-vF7<kpc{x^9a5PJV*Y#F>p}f(s{S)s_Y}-E8j7AZeP6apW18^LHK*%Khp!Sn{zTj9G-
+&`Sko(#vl@TMsHRcSu;$voeErydPM;6<G)mGe>SkAxQ$lm$#J^EjLHtm?RahgKRS1@+uq1dE2<XGzX|
+!tvMJ?M6yN5?hGIT9M+v`uZ-Y-I*jCfWFVf<_LiLz}GL%=a4>dYl@Uiu`T7sH5zP10=0Y$j#I4_l!Hq
+=!EAbH@Oo?+)CFFcQrQWJKW6i2@f?fshbw&>hoZ(J94FgDe-D_0!Ync6@t19V9hk9PqTDe9=(g<ui1b
+kFl*`l3dZp(o+6+i*@%eFjc%u<_8Hk~ke}k+Hv6$z0iKaY!m3NHO8YL^F83ssZ)i&pd1o1opZ=A`g&C
+c9bok;Ihn}<W}^U_7Dc%!o}SCh9I=EgsA*+bP-Nz^k6OlEI3k022UuV!E64C{Y`7U6pownst!7lOn8(
+CL1T55Ju1JJnqnCUFENQ3S^!m_&%}!wCb44LY9iZ7aG#M40?3Edn0yunsxx5sf&0Gh2sywIlKtwMC%4
+M*=4H;7XAEs#IccYJm1qgpXc`9k#)Hlj8=`z&CIt;eGoC?RGuf_$|bn><kQ8_zR2}lY1}4b^!8LNKBI
+bblYW4JC(u?2XS-{-(c~Z{s7yd<SrtT?$_HH8lrc@<NLlsXphuv^AF)2O@iAU{W<63Y0p*>#V1BIC{4
+m{>IL*kRXG})H%OE4Qe}Urir$>P3AnA_2XK5io4GIU<}P;2@V~>lFQow=bqDjz1#a8{dk*Ql?2Tu9N7
+^GBr%C+CFR|S0{EpttkI)40C2+BqMt+eZ;U?w2>++W1X2VV&yh877kj!Qu+wOGpiEe@aD7^hdx4^I9t
+$3%RPk75ae}uPtA?J>80r~!OHp!nR?PUuQP13`vxF-=h_HlX!?5xTRCR<yiQ<?GUWA!50h95WqYF~Ow
+HuRXbW*SYmLzD7!R5@&=<iJ^~QJKsK3qVVh%KGWfKci}y7jW>Y#mgUYc@$esp!yC%z0D~%yyw(A*>Hl
+@gOojWVvx_}gVsc#WjI(sI`3*0jr1hv%Z*))v(njDt2`O6Lb&Od_NcL=dIZO04LKCi8aun#bxJ8qX#n
+Clnp}|Zf@q*9ci?&TWh#g3Y{Yk&wUy&D*UVt)j~yWu4*`N*P)*0D8TU&ds55y4&{S$~Mdn^&TB_EfL1
+p3sS|Q8ec%XuYe-H7w$*QL|%G(_d;ylbrqXptE0M~1zk3bRE=t7iilfL?j7d(z01ZHkS?dUUUGupGmm
+)8pdf!M-k{@sct<JLst)3=nsh(!rdV@e1rB@=@qe1K~rcun+*Bj}y1NtfU-?}P^|ICM1%KCVI^FwI0f
+ma~L<(JM*=22gxqIEVv{4$s3=(}Nl@BJ50*WCh1YulHbndY;_}dx2xed|3B|y?VEfUf@BqZw#6OCyal
+PHEW}*r!2kE-s5;)6uFcUbq=-aaWe0rNBATrp$>YFLfo`&>|AReEc-6c(17haBhitEAhQ~UG3KGUNiK
+c5jfRsMLAzmgif#<HnAc2<vaXt>0Kb?`z;~FmndltA6K8f#Dx`@*dIAr}G{@)$6ujSgB>XA!<)82_32
+)w|cfZP6*LP8%iOYP^KRY1hY+&-+&-Y(u{MjFq^!(<w(cv9FTRt>{Wk`pmoe+dcHGhnty~hH-82n4B%
+8Kn)x+6_<r6WkgX24@S6gr_J>BnVr0PXuT<L*T+-LVe8-)j(d?r?%}5c3e?VvO%-GLWh-*(aoZg(P7C
+n)E!RRp2v6#cl|Qv+5IZvQEx8`E`?6Qz?b^)SV_NKaMl<<ZL;8BxI*ANLw!x0|M@2fXewm6~4|@KCGl
+uZH=VaK2+l###M<*S?6ZO(N+bHg&f?QvO+kzWNn;mvd6a*&<xX0Eh$9XW^8!W@qLj)q*8@2aw$*5T{)
+1lhnJoc>xy5mr=!>lssp`>T13f30)z$wVY#u2JnmDo;>PyzAiWbN)!AI))dTLH8GI;*Zu|C#)b?U#ce
+K&?{L&TH3whN5e&G|u;f^pOYfs(qnHc1#Z8_0i3XEXBF18ej$#8j7xOGM)@l>MRX=*N3e~I(yWCG2T&
+=%d+k>+vlt##2b_i;h$m+XP*F`aHf<i&$U5^EHI)16Av8Z#k;%;V~zVW^`3Bh3$IZ@wvLqtnk7rE75E
+AKonp?m5CPlvvXSqQv&uz^{H2Yll~%^-GVjUcM}DP6KWkU#Exm6kgHwu}+H<WmoRDrm+%Eo6{37Qmwt
+LkbfStVvyUIAOl#?52^1u53I4yKzcs176esUe!Q`M6U3f`<40b+&FZ=++^t}uy6!48o$*p)NgN-=#PS
+EXA~Z)srVuzE;If}VH!2nBtn#<Cm)fTp9NY14P*rXKj4k>i`#@v{w718+e-8e8|D$}|pN;*Rq5EOncR
+9Bch{Es*#kQ3e2Qg^-|ECeZ@ZF07L3=)o4BkmMa_6N8-?%A)_J|pV?`^PA^ba`rDVihRTom}}A1Sd@M
+ZoVu)CAqbdg<vO7__&|><usB9s@+N9UyK{ZbN(#_N#~=xpQB@$bNi;>^P2$cJd57*lDcxzM6Pv8c*My
+xgfR+Vh7+GzL7g`<xUPB?<qk1-HNl}xV^Q8*h_p*;6I||PtjbkNBp3#5x)y1%WOTMdMTXRA3ff^|6Jz
+JUqfcVM@5h#$%eeEb$+P`>bBR62RHQ6?-HQj?7MFJ_Nt)nw$QoM$OG_A$<T(@_D~r8IpBjOal6`=V$V
+fpqgk~mRAzlhsosp<3-H6n$^IchXoLOCt(5VF$iZUD%d)4kB6!Z@nwx>&cW&Ub+;>#XS>T3fIn9Dad0
+*p=J7na(?EeDDwu|lk8$XwQ{)t}PV?yo-aw+Z|)Fts;EQoHD01Inmjrx~N@wP?JD;g%S)uFgWrzJ+nG
+|i66$pYCtC9e%QcvlY98a%oo-WovF_1FUH84v4&Lkz^CV2HDxOiMu@(MvFKA8NZNul8p@d-}-sWUNTO
+r#!g<M0w#}>x;c26p-qNDk-I#+pxD0OFhb1;Tjv%7x)m8E0s8RVZcx4YsQI)K-9xyJ=YL3KXDIwqUt3
+9&RNGVCw;bp^#GH+IE+<Rt@V71(&T_PB~x5y$wn%Zxpj}j^+6m*qAbb<3+01l5Dm~qusRqiezEXNI4T
+I7fhJiY;;{NU<XA3oU<(7h*GO&!zH=QfPYa6d;{_tm0s?DS;JApbtA`n*xE#MNX8QI#=771>!fT!DyL
+yW;GMzl6PjJ}5_^gUN8S7+h?CPPOu6N)WmF_FTW3Q`DY2KpgQ=;g=qOym3H&9H*ss$c}G(?L|0W&LVF
+_b6qMQZ4!fnGKXAk<xw^6=_$)QSlyD-B6)sI6fb6Ld4d#BjOoJ9Qqq8@PI|chb{xSWx{aVzMlo1_$6#
+oID){n@b^lYLs`2*pugyVN123rs5vf7HO^3qg7Z~g<s0}^>~sha-U#!gwm1$76wi;jBEluXk^E8R`50
+M-9$4~^xo`;Xneh6s~C!_y!l1!(m5Km95~XdvlmQDG4L3r+Pb|iXk@o-_?X;2$X@zi*NX#wl^ERff`$
+G7)r%aUk8!9`&Of9!q1`<lJpHl!nEzU4#a<<{gRV<>vQdBORk_8OSP$1WSdnD3hoU%e50sZE1ANko0n
+AS3-%6N)UtUxaRC5YZqJgdnSA{TiTRKv7qr*Ki@AG2ee0rd#!xJ(jgvHR@s{#rzQ;(1}H?(Gwmga>6P
+mOqbwM;yKmla;=fnHdV4zWsx$X3pLurK0pbjct#UYg~#fcUs*z7mPobXi)=jKkbDoxGBnC)=gU`DWP{
+0hbVxtl|*4<%f<#<S0{Fmq*5O?PwsW)kn%cS~6HlFop{$^dH9yLEne7-bdCI-QGgSv~SuGX_W?BPl-T
+3XU;7k#`Ec#0VtV%TAq3W4O!HN2Q1s$@1*Nqr<@1To7vxe)6p1Voa0Vh6aw{YNpc}df=)a|-<A--!v|
+c<CoV+&Oikc2HJi|sF$Pg|=5BzT>^w#DNcX-Yc`sgMGIv!@>Cm+Cb#t%|Gr+E~-)R>!a-$=A&QIBS`<
+h60TGr~JLmpSBn1aVOh+Yo7`XptYui~?GSN-n6BQlL8z~i;h+>5!QX8*3z&g1^NhquSYm-6!3NrG}T4
+mzkJ(P0lZ9z2#zmt<~8;gx7h`rv^8dBjay!(CC_%2Q5}E3VjqcEtt~H5LIn-lbwD6eGkjD%O~CTAzz`
+s8VDuuf&G`03Dm=PSP6eIQdW}bj0$|lnj5qB!PH=>m|SsIYBlco=DT_0F})h9INW0WxOi=N1+n@H=xo
+V#{L2-eHROZKoo}&6x^^jLLvx4z(4O1g0Vdol!ou7;5Tz8gzuQ?gzhvP2=QhJMfNdB_zw{IDO3v49S<
+ep9;pi6;&91nPgCw8G;%+|E{2Ql1RbZh<jLO5jPJZ$_^$}I6L{|;ItAMm_vjY>CLc@4H#S1|Sk?*N5g
+AVIns(t^UN=tv5r%v0D|}<I4LM=&excx<i6!H`gE@VZm2G$Nx1rMIdMcD*Dti9@^Ty-<VyLwI0V-Mc`
+q38iEmZp0_+Jf`&VLIk6)mAtOdRl@3B7nNWF|yS042K2j)&0)>?PPLOQ&3QLLV>GW!K<{Oi@D?9@S)r
+0OUB#7RWJ^dn5J7n_@n_KoJ4WHABoTg1MhU4SyW^wg;&K^Btoh?!w{Wk)6DjAbSopAH18E(}j{nO%#8
+hQ=v!Bzzi?-GnVHLUt2WcYv?866mHPBsMMd?iT8<s3hHt?#?nx#gmNJ_&)n0%lj#K^9VUQoWgym$1ej
+zTFK{p2!}=MC)dk}11B96Z-E(ez7naDvUbC9jQe<55)ZO3=(d4NCEQ@!@%Tp;;zMODyLK)ZkibeFzMF
+~HLxm-CuCSia&^WkKKVsZMsiu$k^9c$?KrwZr-<~-A*P+g&+I+<$f9+IM^6w`&)Q!|)iudP$6J*CVUV
+Pn)3#OD^t2v9m{77~I1k)&?%ko9z^ZHUqGtJj?8D959mewGO_81%e?uGpev$aA<C7^}KrGYrb@1>aUO
+xBz<Ub7k9hR~LG!`P#%D$V586PHI9fa}zf%vrP3Zdp$W0@0&V**1@f0T67j)nZ04H;&^M^<WAGMKyEg
+GqDr#T#XK?~_FNzQH1o#NxaMJgdlpFbbmn7;8Ny9M*^8a3Mh`TStZGJ5^mpBa8f<xRH4b`ht4mnH=f@
+Ofqm=VByrjBH$IeQsWdh~cYZ_9@pji;`d^n7QOfcuqxYz4!<IkJh|Gx+-y>89vi8D5t$QhJ9UTcZE9f
+J<~0R90geN&O{3tq!EH%mKbHGXU82osR5?lTci*jMKj$Hx)G(W|Wb*P54s7o(fShF!O%4rwIsiawX*a
+zbSxD!S2oQqbzFq3<l=<1Vx{0v{UXp5pV6=vmbGj}#Ev&gxzT4qJ}X8zTSE)z!HVXnv6kufb_DznI0L
+?Qi&cP|6h=<fGApwW*SF$9821RAZbE?8sg5+bNXvdw66?5Wgg`aky1M(UY%dK3hC~AbFuDufz>E3P)-
+$B&<#)b`5|-Qkhcw%DBf!kS^`wGaVI#9_X)ybzxmyaF>9}Bb~DIa5t3K0U4jN!bVnb<e@OA0E>UkOOn
+mwke%qV$b~_AfUoUK;bg(_M5$Th)i0tHLkb^{>wT_8k$WM8%Ewd{gkS+Q=J=x1n_yt;YtmR-JxD#?-N
+KSqRI&0g3}?scIk@Lpz=%>6V!?D4CWnw2gof7{0kSo>RMy8gY}p;$UIn$D!#EOwV4F_P+gJSmt?as%9
+YvPyJHMj1fp-&e9tw{z0wnAk-UtaKfiPcxLA!fT&)lAK&VAV+R93rym06i<HAEG@B{;-IV~JQ1qsn`C
+_YX-Y9#BSjLNZxesWc(4?rBV_8&cbQ^95&dXXo(4)y@nC>h@r}+vntRPe60W*`5y+=YYcsetJQxacU`
+_(VBi}r<Rw7_*A;HZg^o<F@!vdVpP3dyBZCZ%?$Q%6aq>C>FKs2^)fOuq!Y_aeB$p795R}p7q7~+qWU
+7b^SxK<t4ANA;iWJ`1mkPM4XE?5+H+O(y_x>^Pzmt7=KwX@T}1o&Cq-IQ_tU=trGH=YCrtV-^%n{j24
+hHM!$ky5FceEM2=Y64lp#Bk*!AD?J;Zv$76`kCk74M(*GFd`)emgn@nf(*kl)juviM$={%rf9<cHs-*
+aNq>k65w0kyvzh<&a+ls-JB;65riD=s(e=Z}^PLcPAE#{gI;|Y2OdTN%9?Aq3kXSw?VB9_w4R2WRLYm
+Kb$Wc&f5SG`Wf~<X7oeqlJ8z6g!l>^`vd)t@JK;cp68NpJ{C22o$&r@!u<*SF7R`*8}Yn796$I20@HM
+~T~qMdBX{<?`Ij6sT>zPVMq18BOy97PiT`FtaeiETP0+7g>+bp;0)~G0RxSo^tl3uxn6Sa4Rs6nYi0U
+jhz=nQCMDwmB*qg;}yKQXzl7r1plj4fQ*C-p&G{Codow#3vyua&?c)PO4e~ow&=n;=_=stqyatNZnM*
+>!e+Ba?yqjRHYrf^n^cQmWG4MZ_)7Q7V9z3E8rao0b;bs?k-wS_q`BZ?&!SX}cWxj5i$9HlIOnc8_QL
+@w>lQ;`j3Y2XZk!N-I7z=TdeJE7k}GFEq1-XM;=uwAO_fTj$qGC*ar+Sw2qwS0EeCBMt!GV?alcy~s;
+WY?hL>UJ@E0JVzsTuU;rz==Ech}L<T5XW<n1UEZB`RE|L!?(DYnkL^nDyP7eXp!?{q+>!*pVbpm0W)d
+R*((mhxR(9xo=HIWiTX(*@npLZa)SZW!$eLSy!{^Rhl32)iD@28!U?1fLx`q&tY;ikXN4W-B$9QMA5O
+fgfF~n8<1U?If{{-mcnGpTZrH<Qr|07&9P_6?leq$2sY0&1Zf!Auivkr`H=>uaxDr@d)j6$5sK<4_>Q
+6h?o7S~1AoPaj`J$P{z-E}BAwm;d4ym6@*dp*$Tcr+z_7<%4IP0;FWwl|<FGQFr1IAAJn3ARhqbD!C;
+VXq&#tFLNzC-H$S+CjyQUs=7-I=jWumh@fXYwlIMMDniV0Q>v*9ET}t%qR0WaTipgLdv?C=L2+JZvv%
+*)X>U+K~;B;;LR339eR~Zq9yxlEW;iGW+?+{Rpc?cBUq~Maw?Ud#i;;_Z8USFb~f=ieKTB&`+<^yjdT
+rquCf0sRrm%`w60-h-X{Q%bv#ZN~gjORp^^b-4@04!o3ag7n1fB{D#CuYC=35qriAaxAp}^zI09ecGM
+AR39+irZtSZ6O7ym;)i3NP$1wEGCTqk!7ZKSby8g`eTLD*9=66pCX09o}=b4(Ae)HD9Zv9=(6@6*&HN
+9Wa0E4{o!I785XgC6X|5HHx-{Z$jO&LyBVJ`Uv@`}i(I;AyVi=i|Jc29apznJt&Zaa_nU3@>>Gkt5Wb
+oK<sPhx~Ge0Z8<=ufhGmEXURIEn`j0JhDcK(RqGaSM>IAlhaLWE-e}y8_wWkHUV0(vm|y1xl_5vH?PS
+Lz>%XX5vP`1K^fh#S;SZ5|n!bk-Z<Mt=x7LMF82;){+!%ErM>p5KUYQWCDcK-k|tvkM4d9_l9K#B!GJ
+ZIRy}HZ#`^1<)0Rft%q&M+t6d1yX_;{DpdQ(Vp~}EhPQ^KeFV`y(yiP!g|9E-&(KAngRh3pHYb0kU;_
+jQAus@#fgl3Y1{e@spxNLv#eE7kkc_?M_D2uh-f{-G2?!m~C-4SJ3bKjA0Z;t+jSP^LTLYgTaow|vzL
+QnA|3G++6$kl{9&SrFTmD>7IyScv)~<>C#mx|4o8i6{9t`PAut1rd({?XR=Le^DVk|rIU+D-xOSvZ7Z
+E4@)Js;V31}OH4w;ddtSZ=WdH@=7)e#DSO@^)W*F=c#4VR-B{3V!6}{)4R%d@W^B+=(nod;V0k<?lQ2
+pVSYw*ZMJ?K@Z<y*Sw#%w;svg6xyA-Xy7d~>&-C9v=+foj1)@lOq}NX-Ddi%hXfiC9rkv>Xt&4ucl3P
+RA~Z7?<7lDVygC@frej?+srMLzo$Hz^@dICk{257TrgWj>!uOGQf%PFmAGg^Ix5apNO*dqp^{V$b(q+
+ZGeMfO8K5f&354N~jk*i9(wNnU(8w3nk)B<BO0w`I&P#Im_G@11$IwaUT{{%u-ve9TTLV|m(wAREUMd
+ir`Y{SM)-V4y%I~<kx&b>AmPFxYd56|NfecwfrOG(RJyVe`<6F6SZhxypP?wJjavwXxgyDIMO?F9{YY
+Ph5Jq?Q(^+@&l0z7l*NhPbV|Pu|kGYRY>BGlh(}+j%`;m5;x8E7Pdvg4MnP(Rt-LBBpMz;7}cV;%TKP
+?|MC<jnSf=W81f>6f5L`CKSWmd9GKu@?HAqjU+@zCo4#$*1kWV4Kd_h|Dn~Rdp}Rv>huR>7SBsA$H_=
+|K{cJ8mJnX95}ntD7UbU9mFd?CpbDJ<_F6m(4N5c3iAtxCAVsNik`}rvZo!O=Lm0@%0lrPqvscQaa$g
+?1z+bW+W(mshLwF>+B{)Bu>O@O7IefP@*zTf6II{V4DCnq2ar;g%x+GQ3eL3jUS`PSFTWE(VP;Ys@J;
+#x5ov+^0Mn+l0m-HBx+*r1h!uTQHi$&rH$l#e`_^64H{_kZad>;JJBm70C=>k+AnE$h%yZ`!e`R|es|
+DTHAat!~s(qH2isox4w7wqo;r1L%fGN<<VJv`lM6u-2e_f2AYcd!=4z6;I%0~utxyG03RHz^8gch7Er
+Gkmu;_J~5}lkE9P|D=#Tg`^-q+6cuT6!{Ov2g&X`(_g(8^sc=`eg!ywXrCB*H){60P;!req{-cQB0rg
+>1i2eb%!m4M-;YmruQBrR4pX06Nd5<!{72vHM__rm{WLy!K_`A2fav~>>EDym8QSsxw`0?DHUlK%E;Y
+JKDg6omXG8f#VktGa?q^cR_Nte%-5>iy;BWdxi|L0U((HLB=^j%^7_0fKAu^j6mxMkNOOp6Estsp*Ua
+6D#lPzOv-OVToY?l~pMEr$@=yicO^vln>f1wB<{`SS9PO!%eI@tUUcWMpkyWC%l6X-u!Cli;wRf<@}r
+0Xi0AkDiXY6^OsXdmP6$3lWoWIj3WUAILI6&vRzr$?k0q@uF5fZpk~TkCvRm(xI3B(}W0_^q9y*Jacu
+QMnH16+(bsxK`vnvGBAUI3?tnr_o$dx9PePu4w5MhNG<e&f+`k8${w28$vj^Sh!bc1z$l1H$A#zeUlP
+=gq<tKoHWNKU}_mc`-rp#&Egyrdee=0J|0ppz%nK$xYe78-*3--1zOb#Ri49(dmoZ`B?4NOA8D}4O04
+7O{OS#t)$e=-6^m3#W}FohdRrd_uO-`KzfMw!ptSDlcg568QZ23RO|RzOaib-eQ8AsTOk{@Jj5&_AbM
+Om?k%%6P)u%k#r1N#aphbJtSYTLv;l!Z3!WWXL%aN)x$#__vagQDJ)imlW0^k;6B6fM0^02b(Yr$Sg#
+Me+8>!+1{@Z^*ta+LHVFF&+^x}9|Dq`6%c-LJ)qIib8?<mg~4&f9okV_2J*pqEK#K^=hm<><k0)OWbx
+X<1GH5|%h_;7LGNJ6T1(W%N3USU<d8#(SmI-tpnYyj4CkeM4-VuofeCK2heOZ~XawZH#Qvi!*)H)pvP
+=b^a<P)A4y956pXcFh0ZC4I)ixm@k(k6`@CXZj8raq;IdFrnMQqt<_as)W<PX*H>jOupzJLHpi<rD;0
+_B1XYr~dg9)bqz>v0A}*3sT=Z1<ng6;NlY05R<dpCQ$p0cw1;M`~fBO-=7p!M48xOvc1?`Ys`H-;vDW
+=L)lJ6`sD`r~i5W19X9VpSGF*&NTlZ4-}mpt=wKozm~D(&G=Bg0cHj+x#hLbt8+vG=dy&3>Hw$LkL1R
+d1HbK|~dhKi%R0yN}E`2+No{63>eoc5C|-SIu{GIkf6HOf!L^lqS#zmzXJEf)wg)nWqWXEW%*CT)w<o
+kDXT+(>#{D_ir>~Y@Cozs_)OkC<b}tN!*JpR&$nX-|Qeq#V)MuOr3yb8VH0_z!Ty9Rl@ltq3pUEj|9u
+7nRQrJ;)+Kc-s&ac73aHsVlt9GK=-=MSw?a~4$iT1T=c-lZKS@Ca750Xla*3iw$@DCRED{t%3vRXf@4
+l|G=@`Z`xS(ec3cbktlsVV`S5QTckC}R-++;L(x%f~%OQW)EEZEI?D$+Pz?K@bndZ(DA;Z!4A!>CRaa
+&7mArJHcPD`m$DR(5OoByJU1q?}`I>qfZMQ}9ceO;@x$he~*KJZ&jTzu&M(x2sA7xD9z9A&Vk#`@ae_
+8OQiS>%V!RHlzAV_fOVKff-Rb6H~f?MLg8vmU)hHqaEKYy}KPRBBnq@6Bvk3wLzOXMN&l-JpQ1!DJI&
+2`{JSnX<FYD=gBhG)AP?++U$sR&1w^d(}Jkdj1;c!+@`kkQ!`bz2J#MqO%J;T+gk`QP5#a?1(@=n_YX
+#pYY?hY=(GZbH=}w*7{#iO9KQH000080P~d=N3Ah0j%EP>0A&II0384T0B~t=FJE?LZe(wAFLGsZb!B
+sOb1!3Ma&&VpaCvQzKX2SH48?bT3ifWeyWq~Z76CF8MTP*Kg6x9MqHMM{<x)}-FZt||{{<*eZld_e-+
+TLS*BteFW4V&N;xMK*2<-84cfmS1mgXLF@|W*Z2Vl3`K~0)sHH`4F7Qjo(5&PhBN2rvtO2pI>4oaR>$
+)O`}bC>Yv51jPuxweV$mc?~c0X%seLq@AbWa~e+DVOn?{$`3Po{_4iI-?JY7JsqicXS++@+~J0f^)yO
+VjFT$e6R-TNz!><T3f-owbIZGRZv)X(RqApN$GgP6jt)GDbirI;B_M`FvUOt-(aT@xF3Qh3?&GgT7ab
+EJzy!Xq2DaxIyK(_dar3n_%!z(Rf8^y!n08<jj76w=LP5A9RCyEgtN)^_)6l!G`ajDOJql1QGQLXW>*
+8%@4#~09*$LPPe)@scVx`zjECAzr>lYb!wG;r8Z*rpgAWgQFs8RG#vB$0gR-RKYU%`^P)h>@6aWAK2m
+tey7DrUx#oRCg006xO0015U003}la4%nWWo~3|axZdaadl;LbaO9bZ*6d4bS`jtom9b&+At8k`zuCpS
+hfmL1(ym5_0aaPhiWUKo~kM`juXrp+iQ<o^7oxM34vXxy3NHi<2Nspc@L&Ae>VqVOJ(X6M6Bi?+6rTn
+$%MHEch(4da2!*CRo)&TcU(O2S_V)DXFY=0abHSgtj|Le7@NYpdno#rh{9Qo!cAk4A0fn=`<xZOq=$9
+eno|^%HH9*jCE%)Whedw;$W$m?vmoTG?fCKjC#=`7xIf1&Pq^g^+>&tUn-v3khGFT{av!LX2WU?wk3u
+?x*+W8n@!tBZA2%iU9l`Xxx3shmV~H=i#CsZMk%2<MowvUO@N=G?Wh(`bL4vIy-sO3I2j7h%NhD~6(s
+Ld1^MTWd<0{jFH%<ywDG@H}OjXeBs~b3}7roYMJCHp`i!y^~`XC*J(=nWiL*}(K?Nk}m5}mh4C8^v7O
+|fkp58>h^j(hs1{xw6MXrdY8y;UX!!O_R2sFaoG^~-@516ArRc8IRfD&YBMgtbpf5_oL>lu85OtEFB&
+-fqy7?;jqA`yn3@C$Hz-7|XF>$@5;8MX3Z11dm<qGnH5(*+!*NnId3vH+GtD8zi@kd3PH-)jI?aGNft
+fVocJVi3jPGD6nyfWu^>mPFquFP-)ANTuxi)wO_u{KL2lN1_QjwHP`jby?l=JRX1e)7n8N%OX>IAw?E
+=5K4`IdR}Z(`pzLiQ2&=UeNPkP=japw<KAo?Xo>7!_m-m0sx>xxRP)h>@6aWAK2mtey7DvoU5zOBb00
+1XL0018V003}la4%nWWo~3|axZdaadl;LbaO9bZ*Oa9WpgfYdEHy<kK4$R|2}_3V~hbMpN5;{zT^}P?
+shkib+X=FIOAI^97E!et(o>vB-2e=)&=?BQ;&X<lxCdU0EZJ`kHqe-?y9cGud16b#P!Q-QMQ$8?$=`I
+ch|qhCl?o&mzNh`xBIT>)t#zUf5c8w^sTs)qPVN+>tLkVmTmi}WUcP<uj$(#3%wNw)jtR=|2jz1oBZO
+!e;s;N8=>}Ht9v7My4{NhspU!-qFfeQ?uMGj+g>>>=7N90GP<Mt-mAR~&+A&2y=ohCL1goimM?s}UFJ
+#*y6y^XBz+qi`~7tbf9i{iULV&N0zPh%2Q3Sf4?TP#?zLWt?zpRqM`_m3tbh;oNg8pfWhHm2QMd}x?!
+?f5gesAF64@mY2_)sGQg*#~MN|Dw>sBXoSHpgP-1zRIFwxkHi?S|^frXckZ&<#I_U;1+VaY^5Y(MIvG
+olb%zX7)A7WZ$j_Nv5IkON5OSH2N;3<=ymNZxkUoY-gise}0pOF=cg)Vrdj{;jU0#!1RnYv@xSiR8X$
+ihF5yLv(GeN+r!os9feNQAwb%SB(^h2ib#Sz~XGzvS?62jO<t9UVW4e%qfXv)$R+`7!)`>a#|;frlQ|
+GKH{j=>RvTP?R&D&1aN?&y>6TOm^1ISl8tRJ9cY$9#ox8sYn#;yoTrfoP~`%+HsYfKq29}W0}nU$;bO
+U^HIbA$Epado+>4^?blYiF^b%+OsU3(YJiG%-JBmXAiy(5$i>0_ld4althTX}!J>+iFnN?^Ln+<B?X0
+rgz-mM(NRz2{8&-0BIH?D2e60INl{O#{)bARk4Ja+c9u+(yRu*^*fyveW@Tt5z=UeOXj5`%66!F0q!3
+tBxtVt(32_H|pPM0m$Hz~VW2M8?m#Oi6%Ksy8sfc<c-DJ*bN#!TLw4A~Pf2zWG)t)=F>$T09h0ll7>>
+^z559lJ^U%JpKU;Y}(%KP;Pet7r4l9WEQPr%moW0qWy|iaTJxRXnNJ7HuPG_k6^8AEMTRu?p_ZiFOO{
+-w2KYQQ$z8<u8$V>ju-XbccZ<jPN_$<wl|}BQEK4m4sHdozL~AJetYpQ5du$Yb#FyWUAc!43?8B%y4V
+~46Leo6S97Fxy13ti7w~QGBh64|Gc+HiRy&0;@q}XC>LJqVhr(EsvR2s?dpRA1PtlK##0=hR)D)TM$a
+Kb&kiRH~y1)6ZZDyvNQ#BqpnJH?sG?$TBimFOyf0W0S_*m4@oZzQ?L_wK=vWr^YL+spxA0%jjj+O$;9
+%4aT{RZixvfdTtBbvvt9U$_8<^nf4`aRE2p<wuTZ4Ys^mbiPeqTsr?<k;mMwRvcVx*|+dRiLeeJ8=0!
+1HoKrG62WR@^ZxET&Gyv!0{j_-Zm2BJM8x!Y=nBI+ZTt9GY)II|Nn*2MC$(N$vs0yiPl)1m!}ARrb8W
+fKS}p<ymNwn*yWyvKM#H1A$0lX_V$O@(ip%Xa|kGhT~*Dlg<lQ><#~R1DEj{tUlm4`MO}j?gI{Vv!O&
+S*FNbu!QW5;bM)3I{O&<|FR5%pJ2v0>Qs)Zp$cmH%Sx3i@t^%_7auoDY~%qX4>W<i`|`NPkeyrHAG1_
+CqkLeB%>t#0qz!5~oAM~h(Gz<41eoevmd;;K;IXBB+Txy6Pt@u11DJnBvTKy#3Y{M75Bbdbty`gZf7&
+<ptIanj1~T+pcB@vsP4M*Ho$BgSh%iXJFiSz9^79BVNHr^NyTej`g1xK;ow3Skks@tu}2CA-kI0F4#x
+p%G)4Z?SDUh8MXOtn>d)o9wkE8sN6kB9R4n>7wuT!cka>Y~#T6#>1W^f{76_;ii1rd$Q*?$wFyPdxN)
+^;H{NmD+1&0!1iKY9kIa?9fSgk7pF-M2;12@0Cylli^?LEr*IHyIN_vjKuCfC7+dy0c5SCIej4kK&Ty
+XZ%{|W#>X47L5eJeQI&GbR|3p!Pdj6o>YJda`lBFoC5J}V%fVIeSTTZATxgb7WjtF@LnEH2dP}S$;hw
+H~^n6A$%s<mXDPqHCKvb?g)@u}s${U{x&yW<8;cELZ1V~aTmkrIqS2-iQvhz1t@76jazkUZWU?Lhcq@
+rr2CHG@C*`6KNu&@$FqId}vcE^7%6V!1;7`+m_Z^TbG~6{999!8?Y*oGH=@KwCT)LFd7;Y<fzV?&gM;
+nc{lr!z;vjkxkqk`veBq@%$hiX-6YZ@mTsDM&i%uxRj+FEv=~*i)6M{Okx#t;=tg}FO|1x<*{p^9p?4
+|vZ9`j3}K(Q<?FWD)vDym$>Pn>U`2?21YNkr`jRN%Uw>%5&+Z|n1Fp20Sg92x<&d-+2*iMXWaZ4;ZXY
+g+2)+g1f|^U(k{kO)HDx_i(rt^=B$)NyB*l<BbQrJ{J5Na#=6IU;`o&C;SjCmD{PobI5MRUofJq0YN2
+NO?{~OeSVlM80S%6DpP=j46WdndO==kahR(aU_hNyKw`AKv$k#*tDyI@4}0MZ7ROp>>C8%l;r5=TL%Y
+(S78oObVOFM$04E7?5IyH2->F3?!o{yE~Pq2me~@FEap7A!Dk#7P>~APoh#!QwgU9~ZLVTZ5<%#MeVg
+|6wr19NT;%Wpbk6$Z3G99bx$_7hl$OkOo!MY~pcf^&^qO=wl05ckQQ?761Wo<sVf=YH9icghb`8?KYI
++z-!C=o|SaF{r1Nnq9)pa?e_PomeEryi?>a!2sCPWKh&_FkzP>t#mWg^I#oV`VS>JSYz$CVH&{DfljK
+0eg+4~|W;;XUYg)Z0R=!{Sa!I=iwlH<0HrSoaTA%R!meg-yE!V^r(puV8)W+)th_9p7)~9UNlk>I<>e
+tpEB`ylXCz_#Rl_mf<7<`I8Huww*GM>^Nm{5?Ax`-1Y@I;bcd@j<gNJUX_Nt){`)$WtPX3UH?O}nWEK
+u0B*r&+|olVum`Ft!r?CwYv@&?CZjBLWgPqn)BOb|qfEd_15SPi>0%zq~22gkN|+5}7kEd|-&-Tn@NC
+uo89OV6_2lv*3735=jC+J#q1PEao}%lb8Z4KZ}<U6hF$A))?X@e2^`dGh2BPuLuNYy~@IFAfJVd3XCN
+Hn>{sPvc!wz0g)EfMovd@cyL4JRVB^djK{l+GBjB=jH<gBwjbqjQXxsy&3Cdo$7C>kP*c%Etb!fGb&*
+@Od+Le#{0>oo__Jh+!~oJH0D$nJmwUvuu>z$C5POYRik~B;H4%Oa3_i)+EMo$Upr7N(^Wuqlp2qx4c(
+Kw)(p<_7a$Tl7Vo5!<Cw|6ryTYBGzb#fCKl7%H>T}Ov@C0*yXC{1|*_xE6=WUFef3!6`{;8d@vpi`o*
+X+!C2r-|#F>ZZ7hq<gyY}2!tonbUFpM=NfZIb5=fnR$tHhNsUBXleydIH-mBB#Z)&|`5MB^yApQFUF2
+?$|#7Fko0q?!nfM9l5Sh+B~fumhnhCuzh2L;Ec@P_Kb457LQzzkT8KJ+-#F*n>L|T;x)KtpBg`Zw?mF
+>K>=bR_+v><Q&$(rZO)nup2QN{c_aoZ1AM*^XxJ$caO3|<00f0B`$TTaJQpxh)>EKo8K;hcZ3lzSXuu
+n59(bH(k2P&#J;qS62s{Nh{PzI)q-b&UK^gfkQxnCB9icF^qfzS_1&WXD{%OTxHo26k3Gd;(0)nDp2_
+T^K(1%<B1HR|vIk$KR1rJNH-ly*e0-x~xG>)coUgzezIzHEJ!Kwa`x|hiRkza|!RZ>BTxc}>*G@ZGnU
+MNZrlZyn;-rdEt3YKo^&5dQ3o$w;Ad(GCj&V2%PS1uzfn(;QCuXj;I!ZW=jCXKWsjEc{9Km*V(`y-yw
+xkeJX;@(Ru#Yr_HDMH*nXoy9lgpab!#Fnu<1$*1XSrZY%)#Tz;!#iIVMur1(S3aT9@)Ks9s<+9vb(WZ
+8q{mL5mGq7!cs9vh#!@viQk|a7j)dz6{Y;|8+FOmsZpToSrg{&E($z#gWU?|2h0%g8tIf*T%*i+rm`k
+YQ#48a{CF`fuPS&!QQ9+$=U=>y;xKh5Iy&|^Ff?T%S`3x7J&Iqxb&x4mq1;QnY+|EBBvBa!$wiKc04h
+EfM#U{EE86?5FH@Llgij|mf9$FXK<y;2{3ZZ$F1xNixt|8YF{4nI+*<b#0nZYWuNh~n~7{?SL^1X~@k
+}RL#J_u~+Dlii(EQ-P-h(qo6Mjuv7Sli<qrD$keR075_zcZeABh%snjdDAbXvs)gX-uGDQ^z~06SUni
+$N#JlmglMxPe{}XiNzC{3c8!Qdd%4%?TgKWadP$M>I=L8B@yUbt~r4KzhdLNCr5QK@)xLmr^0;h!B&x
+~ns~Jf)kE*{OM8mK1vJ{|74r(scdFNX7+pTJbU=UygP8GnN;6*AUC*Jbix5kKN9@s6rZFe@x#>dwiS+
+O}hibOpvmwR&0olyq+6-uzAB!JX&zdB!)JT_@Ts?M?Arm)O+Up#CC5^k%nYa>j#oe*$jw0pfD)Lt?o)
+T{fd2Bdh__GCg1m8=}gz!f6&=A5|^RY=I<C-hoibkV6aBOV+flN^Or&T@4tWK*u$7V0cpdKzm*tJ7M3
+@4j8Ts)ERq+qz{ONL1aHMevyp|PDN)%=z@fbftY*8guxkAifpMmgFVbEW4988eQqFow@)-O24)fX}8h
+cVwL1o}SKw-j#?<Y*bE<KhX{*@g5jEAch9p05fR%fpo!N8CIn3b<6kr8+S(Sy<t+TcXQ01&$z~al@CF
+~hgjZA3l&Gm!5t*>QQx!95tEx@NZDU3rc`*d;fNkz7iMtCm#UjUO1$~DEg$3SOP*%lwcR4~H@#};H-o
+XT=O}H6d0lajpPA)^RR_&zo_NzoYj-7}Wb_E%P*Xe#1nXZN$w^l66?c;Isge)5)b7aniki8nknn_;e9
+8|n-Sj%ca?~l0!02Q{xQo3Pw7~_qguvd@vr7f}AWWScs97@POI=5Y`{^oChBRZK{zCjHGw=kJENX-xd
+>Od0XYQ1*O?VnlN4Hmk%FdGnsMmngDkB8#K=Or?4)Sy(0;v7quKw9ZcO8kIr7<G8V&u9%jg%v~Q(cM#
+|D2;cf9u5FKD8_11K0*{d-+>6bOjM#@^OdXU->H|D_cTUbh@y`sVuml`L^sp`4u30HQbldvW$rFJrg{
+CZP3%|qkqL`(c7ylc((*E2^qpFu%bd-QZm4cz8qag(9+;-mK#cM7G9`88xNUpei14l<XTBSp%<?EhNT
+t2N~`TnhEdh?Cd;B;=#;ZBq9;a{gO)c`Rt!(&r%u5d#{`ur3tin{1v5M?+dY=$Mt$v5e79xP0(ASEzh
+&R_eTVP3WNWgezegQ*<*$DEi~q1|vfkvbs0g{;P-Vhuh`6R-EX}2}M%y*$s?@0SS*mkOagX<N8vi=Vq
+dXp3UHO8iS&J)^jbh&IvEz$i3nY5Qryq7g7t?q-&I!fm(A@Ga!_C@w?ai>Nch|P{B5u##r(<|G&yBJt
+_VeTD^}(XvRBcZq$AgkGM4pY-3u<$Ay?7zuy=OnlK0-Rac=^)p6T5)Png2e%9YB}O@TGyC?ww<cf1xh
+E1&wf~2M*kwafFVKUwkBk5A`am&oKs8ZUyhMeevnKxR=@c_1FL~x`9>a_#bG@zfFfQ`8uLzhsM-!_y!
+W2u4X8bT@?WSs_Uz}>#Lu{)i>*_*Xyf4i$A=+%}%Xg|43^{qA@j?UU$?qnSYheyF_#G-29J|gWImeI3
+~vJh}+*leAd!QD<*xHQx`JwH`Z0TkxnV64$1x<`9~g?<Lf}Ft8)ZdLC<>2satY`@gg`LvSja1I;pWcP
+2|AO5;3p~x62-Rau)>LaQhpq4uX@749<r9)z%G0MLy=SXa>KpLI<0I;~k{~<~HQSgRO-uH(&=Y%AsHU
+f)kE#x{$tj5Y4z2zYRyoU)akCV6K2e(3Ht^1HPLQp?4S-F11~XFT-;rgKuiZuSoQ5Lc?>H(0H}uQ?nI
+^dPKRlHafZc;cDrjYHrgtc|gx%(kQN?4VVJO&@_)(QxY4#AsQvFiHaNEV5SzI2%WkNo}P31>(S9pjq`
+jjJk&FSWG*2{7cg^J<!4Rc;0(lz=I3|RmUBTPz{I`O_tL+;;9DJ`2qS)3RgqRi!cUJTiC}f=4Vp*?qa
+rj%ou{?WK4ueeS61~bCHG?BM4w3Fp|m~W_*8kD(aC?Kz1f`nr*@#1+v#|i*dAG57F2LDqgj!$FXy#+&
+g&#P;fVzI6LAuJ5SisNgwVNloacLTS2d`j#a~WSqEJFNQon?RP2CN>oVk}8YzlA_8J)MCnRESl?T?3_
+Qu2QQP)h>@6aWAK2mtey7Dv(^96)3P007kq001HY003}la4%nWWo~3|axZdaadl;LbaO9dcw=R7bZKv
+Hb1rasl~zr2+At8k`&aCwhd_q-(o2V#9@;dWW+rJfg!T%;T4NPka`h2H5Bcp~$(F%Dq2+=RyYIdIl;6
+Ph)iq>_3%MMFv-$Nqf085@7Z-`fUk;5mDZXS_TcM;$UJpM>GA)FGN^w^rVM)t`G++w^gBDZ{lNRU_wn
+%Bik!w{!TC;4$mS|F^%Tg@f?l6L=YNaiFnNA;XwN@IKfsf2!caS7WRx)EE&297@+<glM!*K%iA-ZXS8
+evTcOaiMGV(FAKTU8{Qk>V_KS|f*bAt<7PNnTQZV56K4NNr+LA3!+Zn8R!)q_DHuz+jn=;EL&`p|7je
++CQ}#S{>c<9k~crir;vk9@fGZFmJi$q?r3(rlzFe9Ms|`W>lM_u+a?OLqng|svca%<I53zRT77ty_cN
+sIK{_(gCOMqG@Kq_iW~+VB=v_>Hdur}{Er?c9d@4aQ!vA<)g8(RI<nzTK9A_@#S_oB!MGarU_alZ;5A
+IUI__7n>$9%&4luTz_CW?Sz|gX>qneIsVQ!R^R6=q)3$|l>3h_~^mrVn<2fm9{d#h70Pfz#fK%_T1g)
+tQsDrZjwMY9+O2`pK2Ap5kLryFLo!jGKwo28*yi?W2w{{ahSi(ogV$sNyMk1VRR)!youcMW#JWrB=%_
+JOn=uOn59@$IE(6azmKXS-Ug-h?QXUz;AsKic3>DeL#Aen#CUC2rAXNl_KK%Lr{JMU0l4VL5sfjRbxu
+e<#MlQK=0ja1n=;rj^MJaAp|@8Pu_|RMrwVtCW*r#rw#54e)((?*$8LjFX-HOxurpYri9O>)`{a*ov6
+?yEZnLQV*nQf}D(L37+aQ<&j9gj>bKt2Vo3V;3+GxVn<fO4^}!nyhMUt6P#dx=`w}F+8U??{@V<FEL8
+*Dqq~zK-2b9`LiG0wb0ul7H;@xcle9*FImSTx{t+HxIMh(jg8N&Ku1BLPEYz9`9^lgc2oz4Pkl)JYz2
+V&U+U+VN1Yh5*fdFhVVBtcq`HLWuHb(m^(~{9AJjFcfzaDIbd}cIrp|`jP!0k1eHMqScXf?8m2#uqe{
+TtJCQy*)O7BAE)%xA|G-aSf!<{NJn!29z_V|y;6H_ljkZSo^7&_Jn{B7;0c=du0(P)h>@6aWAK2mtey
+7DpF${k)3-001-t0012T003}la4%nWWo~3|axZdaadl;LbaO9gZ*OaJE^v8mkx^^HFc8Pz^C=E}Xcg`
+A5PTT;Fc?hOd=sg$y|%%cbV=%vv3>Skl2+}uK%4e1|KI)ZGKTqd4w=e@EEmw)V*cPFjEzPkrt!T+V@*
+;ibu{dIuo&A^!oW%8ZH>@qbrv}quoXha8-5VAuz~`G(mwdw!N9}7T#Ut2!r0@Pi<%#Z;S&qS_Ir({QP
+SXkzhFRXL;5}jB}&xXVh)8?C*VCf38oo)TCbnqU<uo(*F-xC=U4zeysd5OOU08w4~CZT`Q?9|VLYR>A
+S8HJICed{_1T@Pu=5#>w$(Bu85_f9y;?7BhDDf)lJ$jN*jo{%;Wn$hm-vX6SpbAB&AH_>m`;x$T$iSE
+O8u=m8BLb^lLX4FP?Bp$r(h|_9y9_os!4SSl=#8i_ZoKU)T!f1NQwf;hikjLib31GDk;%Azn!QPR@`u
+Fb=>t3rD?|NI!&X=4Rep$`j6d>;l}R!`CHzt^g5N!NBrNI1`=|0r=nYs%EAlXz>wU#>ftvH`+d71`U6
+l)0|XQR000O8^OY7y^(yW7<{$t7_;COL9smFUaA|NaUv_0~WN&gWa%FLKWpi|MFKusRWo&aUaCyx={d
+3zkmcRS2z}eFymCB6cbh|mV%5IvEt#{iblQ`Fz%Vi{5glyInsU=7`-cIjtzxM%v07%Nt&duFvCb5N&2
+jJo1eFHuc#}AIhs><^6awghlef$JV4h{|v4-abj_g1P#C7UXfg*x~z|8sC~ew8Z$m2DvbM%ko!sYG)n
+#kS5jX?-iIi(loc!Lqaw+v-Nv;++9XLmcFrZB;kWyC|S#UX{ueWoaXud?ODI)^)WJc_nnk54Vj}XK$w
+BL#f&=7Rl_*R@NzY=f=rZRb4C7A(QL0Et&-t>ISN*+f~!n(zbe@DtT5aS*pCrck)HPYV^=a+BR3F*^l
+SvZ+}QtzIq0Qx_$+K^9~Y!{WYx@SiQ&q(o|f^X2BJUE171po(lNBy)}i<TtnNYtLyYqs-&%pBENW?ta
+Djp7P>~@D=ZxEQOIqj8q-IXu*Gt*0W9R(v~Cvbs@?#C(=h;TlNY8lbWm~uHDitC=at;j-kC1(fq>5oi
+KD)(>k1b84i<e5+gY`ZE_qv5O$8gTzy4IMXb0coa8x$Y1CGJRt<ar+&XocZe!sesn{+D9%3WII*@t&O
+O~v!8w!D_v%aW;_J5c}UK(exIXqvVLOG_q2UGToCWx9c4)ouWeJ2OO6FsVz}9JRm=DASGPeMkCXS^$(
+y1)i&_L6qh^5k(^`GQ!saXmSgKL4BEH!BjNW;#%G=U`>l6S3pd>B}_+9R9-6vlsE8Q75Pf4sh-KNswx
++7u%`kJ3$~Ue0W4Fb@&ybHOP^Rm-fYvxUl7wLZ1hyX?AwyYrr*`J$Q$~N>1&D$nTiWMpM*Am4Cj<*rW
+V`V)2UcprFABu7tRaXwmFbwx!{g)0{!`@CT&`^K;X{lf-eULM>BB_oJPq;RBIvy!L7Lgvz)wy!v}msq
+@{pOs5Y?ZRaz9c*nL}9fG3q+0Qah8me;WB$-%pqFV5b*e15)ofBx+JOw27704OlRoy?}f`y7eKCy$&q
+>#8k>+MGIVDqxcJK%2)-n?~Y#fr~E8<fhc-k4_ud&`nwb4F_$W983;gynOZS!%yc6yP%)Wem*;g1wB4
+Fc>d<~`ODYmi|0Rnc>TA<`?LRe3FVJY!~^*6^qYgT^Ox`Zx<5QRJvbmz%G%9VK?#0I=*z*u!Kz4=GMv
+erw4d|Oc^R_^n9OL_e+HbxoSR$v27DPP8IA@}P^@IJ)~)aZ!r=#?jf}+!G!k%_fo?^sWI~d|w1mzuos
+XZok}Z5U<VkRmQ%#(D1UDDk1b?xm`C61!LzUe$oaWKP$Qv|Cw_90eF;+Xk0R<rV5IX(lf~Nu>wgV^nQ
+#2W?qn^$nwFJ>siYQ8ctpLZ}^8SoxAYW}{b5&&)$qUW@;pnCt6(i&KQB7Nhc!qijJOmCK92l|9<x1l5
+!X8OC{4aS?06SUX@maTUoV0*(0o+{4622=XGm$C)hGI}uHT;G=44_(Yr^ijZEr6WG8{iE!@CsZYC=<{
+%=fSbecC;OUeKaDiFY*S@iHmAyB55s=^#L)sGfCX#AGlp}5F~)bawQ2-QxSJuZ8Gt<PwxXsQd0)3axJ
+b>l{QVSAw<C5v*@4fyI2rcU-bJc5a&$#ZaIi{aXl3~+QUF{gj+wn?49y>F>V^33^OmR9-Wo>?2t!%+^
+J8!TAbew=eASBU12gi+;1H7bydpYRvik=AsKCkpZGdq8jT~;a-&JG=xdT*6W9VJ0e^deiQOH*b}q2pW
+HPkzR&2(+jH$>fCI3D@uUk2BC_>3`y1B=hM@>Y<^#4@!PkzT_FRJX8HYXKUPke}a;xQdSZ4BIp3(#Qy
+{~3VC0i3Qe5+1D~JaK6b+xhQl(XxITAHJfMZd#bt1zJ7TF3)6^98UZb-D4@v>AGu(7r1yD0jC9{q=I|
+mD7J4YZx-52@EC$E8)5{w8NdBLaT?=l`fa&fH||<P0f7fzlNNZOc`$!L!MR9_{j3G+_wp7T3K>gU^tm
+2$m9!Y-Z}(s+N8&|QMxbG)NZ2qPm|w|^pQfv3IKbQ6$Dn5mY;##5o!1p?#uz8V5q-wMf@GYj?O=U4_|
+Cz#`{}m&PR18gyM*nEF<zzjkf+5Qi;Kot;1{z$k7$t9?j}qyKv+H|j)b=9sj2)7r*@U|aYTE{-*c3Hx
+Sf9K)lg|~f-!NBDY2ALtm_UiB<zBi&&BERcCEpJS);bO>s8aa?*KgB2Vlx883GbA|9;0QFWB!q+;Lq(
+L*G8d>7l;RZ_CTtgF_rXb`3uKk=Av3JA}?PF`PNTz0X8#;{-LQI5-LQ8tPm(NQ_IFU$cChQ|~(jxFx7
+LVv-GMHD6x^l6swN(rd?W60g{0uxM_#(6#ZK_fKZMcBYO3b}C#5u#8d9Ox!{1EB<{@p%L~Y`nm=zFjN
+g{c?m=h92Om7WFSq2+yNxO1eEv+!dtS~K)9P;Ab_w{66(zaf%#o2$m$}0+a2;Pa-4V?gxbng4l4(ybd
+!VWr2)fciNmkdVI;m1F_pQD#ivaidFnb9ljp#%(b5k{vzzBf0{O}893f&@FK6JRQmPM9er^aPv9v2h4
+_1d$9+|IRBkmi}=$Gu=3+ZgxwwAlRYL&B*fN<JK+vgn-mlCLf;_Of>q?yXk*a7zARf-&)#oCAk)r2N)
+A0M?uKT3dNhq`h64)x^xb4X@T8>WecXCUsK1&tt>@H2EI)W<8}FGzVa>Ns2K9^!lb%hRRyVQY2q18TE
+Ph3MR(UYXjSZ@DD15Q2iIOCaZ~e07C-h(d}6Fb#RI>Yc1L{2A%3f_3>ByS4a>@qM_>z=W6?zX*WLyh_
+c~W~A)`O_4u-ZHfoGusjYr{AO6?jOrO3ziVX?pjg%=K%iEs4-q-ptTQj5IUJ!Bkf0ZDtO{|q)_S=f7?
+5Pt>8T(qL~6GZFa=P(8sN)fTcj%=Bj}Zy(f@SV?i^Uej(vf=%QByd2bVo3A!-oyj-i2r5ce>HK)nYA{
+lGJUfdgPBWmdq+M12S;aZ0ArM37eEb<)8Ff9zU3r{ce+HBQ6?AcAF*9i`1SmokVi2Wvs4_aa?gb1mT<
+I2Ej`1dY2@>7}$s0Dhm%%!z;c^r`X8ns|{@14SU@Y#_-T%;+yK&m-WWS532>J$#5iF7h&|>dS|hvax<
+J4I$B|EjH!h*%BWC-lwMr=5TjL@FFksX0c#aX&DEfs5vT$T_GH%pv0&Jekl=tP)2%(U&Is>6e0lxMSA
+PPp>Y#}GNfuMn#?+O3pNW!w}jkmn_JpcMVm+FHqRpQCQ25fYgZ#o*3~^{lraanF@N(~Q&EU5=<wU`*u
+i}Fy6D0HOSX^x>UH5VA2Zz`9Ov_fpeUbh--#dS<V6Bw$MW^e5;A(JNdh8%0}Y2nyf2GMw4n-$-{!9EM
+vXNOp9{xJV=LK%jR7`)(iWj1)~MQSkG~64rFYreO{yTq$i92O!tO(WZK|a3=bVVwuP|(9Zm9UcIeBnI
+Ykl$@^cm0f_x9+1r9M3+oz6j&qx6`w-;WPqM2@1k>WjoM3x+x^)dquxfDmBCdR{<EP2#$P&<YA5IApW
+6oXH~(&0d`K_|^z=8nf|!jo~=%{`)PM`*Z~0*AgGHB;eN(`><5t*J)<ukb+-Ne9O{;UuPJYOA*1Uh?#
+W<v8$bgbg{KMT!{6d;L@~j?Q|L*MhDp~xoH~;@_hk~u$IFU)tL>bdZ193pHpD0Wb<;6k@S%ZIj+?GMV
+6SI0GcC3HD!*`KZj;Vl}?oNo3v~(V)=h0eYyXaNnq}(+@aq~I%=^CTP2EYWDE114t$G`rza<_I3Jeh4
+mUW`sxsjG!f3YPR+zRvKlU6&28^Rpd&*){1z?zShcJA_iXiH<9CYtEybppR+e@qAEm85O^{}}@bK+VJ
+xTC?QBD)T(I|p34Mk7$~;lgICC{p@lAdMCsR}U9v@L*KxJJC!H??TXhG*2IGBhq|OW=uV8uha5;?QT%
+3GoMbo^RzlgoAb4}LG8_aTrJL&k(-64%;{6WNb<TPJMw~H`DMPNn2NDDd>;&RZ@tO9=-7zU!8RCnr-T
+$<)krIir&#|SX{8Ym>z^a7kREghL`S3$9V3kqy)nrfK>_ZLo?lmV2r#G2X=~#!z*UJ6gVl;`AL|gKoL
+(dp<_I|&6MCG%U`myELzWqIys7GIbp5AVfap=Ofgl*3RX9jLe|Uc`F00_A;m8TudwTq;#`Ki7$V8fD+
+L1Pbb)sqdfAR%dRgW#-p<Q7HS_teeoyYP{2c%QYJkhX6r=sI_F7r!h3KT)`c%l=nVZGjCB3qYDg?V(b
+$H&~MYkp*TlRG=jR5}hbpqm)91*gpAGkcCTA796(<Eff^?F54zT|XMi@_{(tl6Je5^#GH!$v1S$EHAj
+%D$u{-E&*8kvlY4Pyu{C|bSHJtQXu_G)Ra>8bGm(hkr8mcmQO7l!;2y>VZSIGr*q*R0vI@yrA`%fI;V
+;q9wZ6#a#ge$3(78jXVSsY*t>|FY#Znfr+s@1h$_Y7<g+&`RF4#>NteKd+Y3yQdw4C&60ll>CijnrM#
+p)$1zz#+^z`u)ch?*bFQIoR`e*GwH&XW`+Lr{NyD(BH|Az;|Bs)GX7((>Zo=kW%12$FOvh#{ImigkEV
+zK2hr_wQ#1XK7LG?7i37frRCI-r)w#g{@hE6Xs`8Z+O7ldVbW?h<CPprk?L0d>Hz8%&9Jubzv?|Muit
+*auW~9Rx<R2yJp)FxqKI_K2whlZoRAW(d}eLJ1j@K0D}*?;F_o;bZOM=e-kp)VKv~!Rkv2KPZ|3=6gA
+G+xK@9>R3BKRe<s}0qT@`PB{w<<X@d$ZqQ<wcL}z!C+D>1yPp?}v>qmfP+X-;dyOB+B&{!%A0Z(JCrx
+jtx0gd3A8KGkC%>KiOa1!{_zibYZ9YXJ4n0UipToDZzDl=B+*U2{wG`H@Esx($e*e>9g5KS<*`K~+u^
+l9?@}bh_Wzt_QqW=y|c?<ojrd47$yT(HhpW#V*17s?0Qa1QfXm=~lUc7#0lws`eg{ym1LL<!ShYhCZS
+(!$YFkBzf!v`kJp;$<`v<R1Ug()v3oFQIikzU9miM-W32j<S2P7PyguL>;$*c_ehLKC6w1Qk%Gy+UZG
+Mv;ems`jU+yL>$t(VwFb(ASI$7X}`hQ3${lz9t@rt-QtjfN>c<wUqmVEYEJRN<s>`U%sbhuKTiE%*n^
+xhkj6-Yf&oGFJTJB9=N>hF}QXo_?It$%lv2<s>zXsA721b^R{7-CNq|PegRO!DaYVCoYef<gWesYa<#
+C|PSz9s<k-*_!pOGm)7$-llwNnK(O2qUzfz=sYt(eG{3Q=3LKrw*uQ2b~xl0gt>G|<TLyLxXc(2(!4J
+6Q~Nnn6=;<BeK8e@<ZMq?7*OIbAfewa64;*@={-5Cr6cXQ&nCkv3{PAD6#l@r!y=n4iEP}p5h!FcueC
+|LKHz<@4I_ATS4XXV~y^gE~`Glg=lJ!Ao+zLKkJ6OBPn3EnRu=OSKI!F&Y#%~4-5t_oakD(bd`Q9whx
+yn+G{xP`g@;2E=ESvPqZp*jrGx&t)i$O$r8yt+b`myJ!ta<`j85To`>05d|-stxyt*fa84>sfX85@L2
+IbmOT7->pGGRTS|@Uj~mQTKis`AcapSg?CwuoI^GhI-v}mTMlV`a;DSM0xX*B-5k!)4rP9P4*1a5k)F
+Yn^NY4I--m9&O-6?4?S@6rYzVPkj<M5aAZZUjtb73doNWLN$XE1TsW{>bR5C-bxz?gNdWE8T(>CPb#<
+do$f>YpWCwf%9guOYO?wS1<>Dyh$KT*%<6RgL%Z4@)W`!Hc2>^Vk`*0Me}kqCkut@xH%w1=e}iJvi5;
++S$JaQ!-ALM8V^aokV)nuJoUpYDB+6`EKuZj3pf<7u+46J_XT$zZ2DvY=6eyvLmh_p^`v+`Z%xC?3Q4
+e0+0rb4<x6Hd#N5e9{b@pbU&@i?%DE<RkGc%gmt1G$>-G0b4?$qG}Re$?2%G$wTV!ri@z_M=rp~&%x`
+7Xm94d(}IkKDB4dh^g3w*AO~7c1O_-@VMv&mxs1nbHj{Luq`ZzMw3?u%kwp=b0ahj0B|e#o(@B3r?#l
+!S5RMQBcQm{aPJ%>0TK^~~Dm-@1G!X3IK!*C4@vdM(u5;JyzK!zz`108cu#K5gJkRT;Y~paJi8=d&S&
+e(<7o9|XVjvejlYn*P&!gymS`b;5Ls}5&G@M2SlN~rRpk{w=Y7En2>Nq-~bT$@n)><{54VY>${f$Jwl
+}>%=UE+1F6}nw?bK4W)m$$L`K5;zR;T-lEmW(^nFL)o3vKZiSa{3YHv8BcF3V19s+I-a^wbxuv+q>Ex
+KcE46?5ilM8}5L)f0%`rHwv%ymUt!H_eW^=00$UCNFTO%!=DHXLDh1N$*;6i1bz@C3mv*rzgQ$Kfrx8
+c(i`NR#4F99ED4`v@6E$4!yC}{P0Wi4+=LtMMcdpsyjMw~B;(!Y#9mg7w33e+lUVA&@N#K_zlOps93I
+F7NLuvjb=SBP@uDU7x-E$mEKB8=crpCSgp@5Xa;WRON431PxeP3~(gNl5EvE>pDf#y}Q^HwFXELNEd3
+X^P$DZNqO4?jaU}kjR)LqOMXKf_nS$PXA_(EYt$@h7>SOKhjxeT@ta7oLgNCof~U>$&XA=hiVQr(uIP
+VpWdGV(jGr``ifwWRCeur^qZM9+55)^*Ob$&_(6#s>U{c<-X0Y-Q?4tk+%-??s9(QtKFW&hr$S>v?5<
+U~MY+Ts!OYA?%M*Il=@xX{E#IKj3|!ps4meP-yh7!^T;BUW`iT3?eDwojrR&ks^)Se{0gawMb1(nnUh
+}jdfs6oKreDT~c5R9O)k@q1Nsem2c2L+$ws8fYUV5Vst>cnT(Xme%ys7O<<CL!a;Gb_u(BW9$ByTeu;
+`2eL5k3;v~*<29AGcL?=^q=}J0H$0qD_2~#@7-984x`G=ev65$ciUD0c<S)rGqY!A$0fl8oSEHDApt|
+>C?IqFRG$ay5fE!Onq7J(f0TI856bL$qFBy+b67#w;YoDzKiypBb>1w!)s(ZY1mr@OP5NNbD@QuXmlZ
+0j(m)ZhXz#Md7NYw;&NTw<GbbT<=c(B@KNHhD&jmp2GhDBhd*gj!IilFLMVbMg(6-|5LokdNbRK+nej
+W1#QsIPaWi;)R#~W69~qFh>gdzM@Nj>4rQj%cbQR#^AC0^~tbs9jtdH2*6<%V6`UsY1LP2!b4|Enmop
+6rq&)$pqVom-^Ua<)meOg%)3J~3>MRc>uiT2A>(AHiHCp>il)6>*69t+^6zb4o6N)IlASknXB=uT^m{
+0!celdfGw0??0Kg!Zd4@Mwkx6q5ivpi`!$+rI6_n=HJ=x^p1ZFg}-LG=Y+d^gA1dZHX9#5ciVVI4<(K
+#H(Nl<jBJf$1wluQ7&+vg?#Y43>)z<p+Kkl%xuas1ltPTCPO4XvdZ+_27J9$~TjtJ4u=nYm!vEhXqBl
+;%eh!Ye_11<qP4RgrPZA${9mxDY_&p7r*XDOAtPe+mU($pI9hGQ(`_u0wwK8i?T)2J#Q_uphqp+hMrv
+3}+C#-ldf1=2D8y4&)SB%=HUk?$=bjYv=mipL7Frq@Bb!!h~kOZ~~eeDND*sGzq&JUoM1HCw8WFo#Ty
+iy_wyyq-O#Uawdx=^^YVSBJ+@qxiCbp3ZeF^TBCu`#PFU{8BQytDY8l_HKdyfE4l0{Epv5+tHIcgS(P
+#3#hJIVoW`z61;tbv(qIuWw}J&hhZVh6BSlyRA7q-?-tZb=GuQI=rm8_LsxMoLwgm@J@5S6c;g(oT#w
+Y!){9*;4GEPX(@HMg-KN3+7vl@d^mvC7cgPw~bOb+A~3;paDYFYJdgS+sZ!+;Pm27jsX%ac{?=NHz}Y
+l<QH3#11VbWgKhTzXiD52$y#N$-v@9UZTEj8mi+$;iQ`{W4TW9v|PAL6Qk7tt-*0WVV@?nW%g$PU$%=
+*Nrp~kfSCh92c(5ou$*y3-)jG)iqg>nqy~`eoq3_3=~iY!{DMGVxy_>A6^MM0IW!(>5DG8n2dHm57W_
+@Ja89PRrJAuQf#i~Hu`Gr4CgjmI&hXq{L!1gMV_^zXF`Kk*X~edRsKWP)&J6H99bI}iu_5O8pIb|odY
+a%=86exjtxb?^@hfePM?^!uzHV;={wWT42EwjQ!EQ_;xBl~aW5cepB~(E8#ySXTg@XA%awh=Z2(8zee
+Aq5(U{lW$fBSKg%Rdf1tA5IbB*ydSa`&Dbm$iiU-wl!5rdeRp<O#$>KhxtHdR&Zq@AByI?`}pN8-q+e
+Nh;MAZgaK7@(2==HBS`VFpg`!Ml6o3(b2i%NPn~Zvj_Vl&VN-5kveRT2$;j8Q9Icpa9<R+kKPKm4kMU
+nM}>XD7QnaMtw%N6Q<qN`ovKKAQ{9DFoNEBfo~=aELOKj;Ge;-5i*mHorUZewB7;K-cXE(E>!*xIe#?
+OI=p$%a!%*NpZ!^*9h~Gi@e~wtk_zz)>i&+s!}sWowc{*;#Qc<ZKiyS%rbxo}<u&^r!PcojaoeEs3@6
+C~DnPyIW&w&8DJ!%tf$P$$mMGiJg(QWS?}X(byRY!vIa5kAcbG(bdbNM7l=KW`ofo`AI@ApSCEnyl=@
+@JgJFGJOZYjC4-23ph_1QYWJPwJtvq5BgafsVx+F49_)2_~(?$Vszqp*}k(c7%EHA9`tB`l;7<2VlGe
+2)X;=SaNg_=TYO1(~J#vDseRK>+x{Uc6oA$bt}0n1bStJ}q@>l<)&=;}PRqe3OL%_u6Rb;Js`+kgaPz
+oHH@>Ky57hVRQ}Lc~$pr&;u{r?g1UX(R&~G?pwJbXbslSSQ*^y2Dm729m;%geJB0x2E`w!5cg<!V!Y$
+?Zo}yXp>DfJ(-ZJ0QnT@`zW`YMO~;Q*6n%YS$QReC1A6?CmMC7_98<Ry2G*-$@CmtrM~fXT<_MkAZGr
+JO!y45FuUXIvl#!Fhw6LHhUPN5zMsxSM;c#Lu!aJRs-#NDZJD%VKAxxz|*f@gBZHErBhgP2f2H9q2to
+;u#GxgYbM_*ocgIZ}=<YgBb>||l^vTjc@a>M{k<$Qx+266`P5R_BWb8vJNaO}g-!ZX;00U>&)@a%SJz
+Yy&S&kkuxf+tPRv>(O4&{TtRidXYNgr@62nAk5{?b``GP<?*y=_SrC3^0GQ0sdF_r43C_X7s(8Xod-y
+#$OMy1|m+q(R~m^K<E&|jtReIY@6RFscQfL6Xig19W|xI0{rdc@uN@ljhEq-Q{7xc#V-S?<92_jSF)|
+P@qH#dwy*dhnnI$c=Q+xu>^Nq@Sm~YrJNJwG>1JaJxU1auUZG(3neRfJ;!H^)>;-}S;Ejp+g}H(#i(M
+uhBti^oJ)=x=pA3K3mdGSnM2w2Q=-g4AIb5GR@w^)})paU>b*T(IU}<LPg^cFnWO#yMm}@Y{Z#=&*>`
+vPePx#I3K7;5#iaw#dMBU9_%so|`!4(_~*7lSG)9OfMbZ@Ve{0KlT3yE5>CJwEDeMc65q#cWVua1m9t
+vGwKA6fPdK4o#0w)nCe!Qx~T)68|Sm?@5(4vfNl;nC0x);CsckjNStIx%Z1{MWz0V4-&`jlp7pESK7P
+T`s-68pm!4ef`csM2olH(6hsA-KeUNV3k!Is<!aV%PepZ(0zqa@dD%Nw~xLx0h>ZL0E^x=L*JG)(J0o
+}q?git|JYu;ye_L7*tSb)ldDpU9pIgES})4m##zjifCuI#%Jb#2wn$tq(JugdR_CBNQ1T5$2V6I{6VV
+uvx<^2ix8xf@_!Opcvrc=OL2z1~sGJw2>)%gw+zo@yHB5kv{QGOK%nK5jR?s%pF}p5MIn=`>Jq8?-m_
+JrH-JS!+e}Rkmn6a@5H=KUbAHK)8JA5haZBDEb-EFf(_M+xdEy>33-wKR>s;cXDtM3HZ>jINdtUMD=L
+&{o0A&{Bp9=l_fvb?0fW?{2BTry*L-Cx)c_?*&K0<*zGR-=WHgBpm%Ncq0_I_gc!PT6GUbQ24=YA>Ys
+6>>H}GhZJ!uaf8C8_j$1-otiJ=XV(*vOr9m*XsU*2iG@r_jcH)4p_#<$2y<9i@&WM*Y{MXjyg<CKi>D
+B5B^ibpNThg&pIt^B+ljq&|l}E%LQ=y5&df9Si7gr;@PB_dagh5G<?MvuM2gX18gA?e|`VvHSxl4zCH
+aT6mcfwV-EBv+!<z8n6(R)0k{w6ua2Kg#WAo^po8PbrwN=bJjt2>b%qw$h2J4#C80(2lw?o`(5!!Ko#
+q8HYcy~Tn^@A?P;f2r0Y-A8Y0s!;y#ch89AeCdC&?JI@c-tHkHAptcFB6&lo)kyX-e#N+8fQ>A~u6e9
+J(BH|LB{=j)Q4<<RkG5zIl$@MZWBnF#PsA*c;ARy^yPvGD%P+!PU{;j)22g{8hC9nMf~VJvqL}`S1L!
+99JoD1thFpUKQktAAaHa$j0_{%0ZTAs61UTXq3(ppOe79Pmt<C!~@`V4@SVD6Jq#vcT61&`9ioGNMJ7
+JM-(Xf6HfaAar8%UYDG^wQEnym7|^VkYj{W8=5zLw{}GBWN--UWt|{rnCR0<;lZyivH-pO$IXhJPf1p
+agA2b=X>#nt37v?YG=UU#fDS6Fv2K6^0y}T>JyuLnF7ayVPC*8Nl!JW&ky>Q>aMqm?Vp4T!N3pNV4B+
+OHUX|-zWS_Y}V_};;Sq{?EWkWWPZ-STfvPQ>@~zG?Km_`^wH8}&hbrNr|hM-jqjR_GhYA+)KRzvEts#
+w#R$7@fhl_nN_bK5hTp43L;SphF%_i|p8MJGJCLcl>i?v=@~cdN}eO8&E2+psZ}5(^geQBHo~()Hr@B
+ley8UyPU<2T|iO7htiRwn{+*$uj2jrj+KeLUZ#2J$oB{Ms?|)qXZc8hA-bmv%+y$ZUDkYe=_PsKE^W|
+!V=p+Z87(<{h3~%sP)h>@6aWAK2mtey7Ds(S{wRtX006pP001BW003}la4%nWWo~3|axZdaadl;LbaO
+9rWpi_BZ*FrgaCz-K?Q+{VlK=G-*h!@%<z(8KncB^%;#_$g&%}HAI@{U1y=+RNNXX)tA~ghQM>CasmV
+1_avg;231VBo0c4q6UZdX5Si3A!ze|MvS2jbw-fk?_UE3T)ash0;&@X28C)mL8)D*00*Rjnc=mCDLu@
+L$1aFt}P}O03JY$)(s-<!zQqC1P>O9YneKQ6{yhOR<iN_*#m%NX150DpR#kvaVrRXrEN2Qg%2oO3Lyk
+lWHR3rd~}D@JIMLj(A$xdQ(=lDAizDm1~%Mp2Hw8oD%xgt9Y{k$oxKyYguP&X<nM&1AZOh80Ot<5vwe
+j;iH+VE8QX~*PFODZFq8dwwQ=FRVpi)KF^YRBAP1yvCInkvWY7tZEsqP={_0=`0&FY<7$SDW+_3CAL}
+Yk>Y45_tIHX*n1~CFm{WTGM|`xF)wMJY$9g27gUszfy4OU!tt8AxruM<}v+qydy}X)ToIO9kI6J+Xy*
+z()ex-q|0H%eJ%4OW-^^6`)M4Bl;M6#max_wh;?U*p*8@Zj`#`!GIa3~8v#(*jS0WFHS&Tb{**U~7G9
+}~IpXvlcFD$7@KvAvLKRsoR#z<B}Cv-C1q$#pytr>my8k?C2%JbYGFC7|cDENWTQ&!zL!U}+O4H^7Q2
+0y+T|zlml&9CRTQ=$~)jDVZX$I4h)K2bg%1)J@gW^fXrTyil@G87>X^!E#p`Z)#Y%Y5L;o>g@?X8}wn
+2myM!O)^gTV*+g8+dM1n8tSXC{76pD-$hu-?od~rmn>?K@wwqWf+l+u=Q{rf1HCXjvJg`fN>qY?r(sn
+WQV=#CimSqKOmT!@Wu>4f4fY~Q7H~Dc>D*YQ<i3OnaF0N9+l9Da5Jgc|;Txodj^6KR3d@!)TPft!?oX
+sxJ|MN^7i^oStM}xrt2G*Reb;U^Q2=g1(&6=itfTA=@a=^l|Bc9_ap#s$h{(mk3|7%zjD4z`=Yg1*Q6
+^urrHO1l@NLh%=NKGBz;Jd5<Z5Ag<*%ZL<D@pJGBYBo&R;LNjef0`-hV6}t4_ey>S)qT@^doV;6fuFa
+4N#BbV&HXLZ70}#EAFy97YoT)0f;3K7swzQg?;Aj;^%g~^d(z{n8E>GmxW}+Fw+53+Xb6;wtw(^Xa8U
+v?*Bl1N1Ta(in8{WCPp&4j>N63vgLMchQn4P3!5Io8i8SpM2<RC*BXC}c;3~i!#k!Ebr|0d5sY|?gky
+)_MuGMVXE`4CK!}<R8l+C&U~uTG@pvKE<*gLZSTT!%YDcC)Ba8His2y_f=w>2r$H-{`uqdnLT8*3)1H
+Ny&1jP@?ZZPBbHy`$(Vx&j)ClIVzk8g)XTI0}qksAMgCr=%aJ+e5`*2$pT8moIKg0~?1q~i##5{nX?0
+qcmM&}yiHphi4F@_KOIU?r477GB6CZj=;vt5#+3M8xs2_+<#jVgu$y4yWS%hffo$IA~b0sALQ+FWsR8
+*dMTY`8M(<><F^9)J!!2`1W2zpczK(xMQ5h`wu&roceTRy)%UO;+{iz)9O;VquW16PT-OOeK80#-odN
+vV^)mV=hSpYmVpVDIXE1ZaUwu4^CcQGwZXqX!B{m=V14|3oGY+!=pB|#eT<8qc+%j1SYsVuRSk4XWTk
+r;e^vi_^ho;vGqjP{$`LytDmofqXHeN+XhcTv4bg+ot|j;#aSfO+m$re5tX?O2f#@CCUn_L2kq)3MZl
+v4@^eJz!JH9EaZQJo~m4VxiXoxap(c{biMmvu)O&tIY-@vKeY)9jPuWFYtm1p8;+CkX^@s6~dnF0xxK
+RHXBsM8xeMWCu>k&bY(<4%i0-p%y7NqOS;ZYx@-nZdE`<S(MGw%z$t&xCzSTn%`Hs)2|?mB|fgUR4S(
+{YhM)Wd@Oz;12p$h}~N;y1%yyUOzY(z=-U-?wC^*-$lT==_tj%7!VL7X5?P-91CRrXdQoS<)H_7oPjb
+6o$t}`4Avo2a6BF=hhjtVum@@|JX$Wo4Ea4U(SluWavs^M!WWye%zYMM<mcdVh71R;EU_b!mE()|!@N
+vl1UvlTHC=<7RHmAF`A0at7r>Mx4m^LH+Fb?>8z3+S8&ZJYkW+E-{i%5T<md@#A8z&SIK(qd3|;H0Rt
+RA<JUkrsP>_N|5G-DXi5iK;F*fNkFgF}NRMUqFaXxY&L_9}|K0BDdLQj*|z;OaHYF^%f<Cp+NqR7WMg
+LUWBnGR2d8=@J2-ywA#i0|VB0|69#kP3?WL`xuHW5O6TU5?X%d2I>5fB5>F$58-{y~N=m1Q@Hsx;#X+
+-c*Sk3iR**7OIiqs;)QF!^5;(L&z4@dD?A14gjA*(2{2{h&ECS$N%P^pZtCz?j)uvFb)J;jN(NMintE
+xjsA<q1;k{q!Y;8bkugVLX*_WwJ;Jg-IS8EOE?8mD!Ct)`ai=i+?ot-;5d*83wE{yGxkSgkZY3Ul&8#
+Mo4^d&P31Ds=7l1pf5J>5x_~A@`cicO}?pkRO17eoo)ZS|#A4ETpkPS6U)Sy{Zl*xJ-mDTm(s$S=Z)i
+Oa=d!Sg94}Kqg8+{utiK*p;%IN1%lpKLVn3WWOg>i@ZXvG8T9lkg_dETc7tZo|qaCS8eXaBqeI~`M~3
+4UUEr!bk2mY7;mn+$_ih;C8{zEvp3Ab?p#kt_OI-yD6dSk1yHs-JGXYg|I4n-cNDwS4JPKw*|eI>F&$
+@%GK-6?&HJKBg4RvH->~*T;I+z;zsO<E+EwksH3qevT^$ffjNV-)11;2o&S-m~s{ne4H!QNb&d&N8f(
+Isxip&Rt;~4IOi{1b%#N1<9;iI%&LG}uK^Ry0#lENHxg`Ry{cqZ|9og-lfw*Gr4An-J?SuPXH}@501+
+cz8U$aPvVy3^isnQwx+}Cj><R-6wa0SsQWn?s3N>(dp<2Cn0pJJ2%3VF)hu}X9IxsqY7iEf09lG3pYY
+ntdSLj!t_y2IV&g3xZ8u%C5!TVRaC4a5Y*eey%`i%3vz-3!uvcw9_P07h26IluE8`-3B07vJNz@atqS
+tPzQ0jV5<)m=Aeg~39Cc!2Ak!B>Ng0G3TyHQG}v!CS!Kk8%xQ5<$vjF|5UwQVtF1<1`9T(U-*@b+A9P
+S$+z^j6XZxFFvN1$(xXUuzB#kUG)P4VfT!BJl92&0Ul_pfVMCo!rKDgF-1z=OyEFq04R=)`J(P7E87{
+k-Qn<{czto`o{k(31=D>4foYX#f2FLl>#T@#%Nl`m{Zo%*41zXlW0s;Y2<wSOe(J`_I;Q4--Pb0_*8j
+T=bac)Cy00nT#y{OxCjR!a;O>XK%{Efeaet>-7`b~50<7=gdY3&X9Xz{S?<;ZfPsif1_h_5RJf-#nC$
+r%&2PZZ9hd?dj=N;_#f5(i~LShEbAOy4n2>LqSiaYd*LHnSyxFPn(tQ0%&g3|jYk%b?VB0zh6TV|<%c
+#m@C2|&h<faZv;wzh&`+hv|-*R<CH{n6^SXl92IBL5tPDH#2Zb{{sSQrRMx?U?OY5aB4t(Qz031x7(f
+I(z+0G>IoLJ&?7Zb#MbZxz_+bz)U}27h)zC-{j(;0$-I#vpXB8G3GG{1uNl0`k(&}P@)I)CEUpWtQ*x
+C&WM~vjRVowj`xm`u;VHRqeZx!B8Is0cgG<x`>w~w@C>exS;rZ8AR^iV6YK1HRg+~B>k_zv^fYFm8-=
+zMt)9VciyX}VP{ZS3GmVzUiB=Hax@r{dzfmy84u-R03&lxe@6VuFJ49od{thlVIog$SV?K5Xx-laCa_
+)*<B@g6noHt-V6=z+@o`HA1%&wb?xoJTU6xb0A6g@%LyOC@3Rc_-di$MZaq&XY+BSOx8ckvQ{-C`0R;
+95AZ-^|`#y!nqmGs6?7@0d*_-e8z@mtlVPjyF8k-~!{uK#OSXMjInY`x+OF1CKx!6P<Vhm4<4A#@cYc
+Ax2bU<e(f1Xzw=~^x_ZnSP6|F#z-^*-{RN@9;a#sW<}+2o!&C|TPh~gMCW`0snw|>PY_y<MSs&7USx6
+e&f##Dg<n2-DliZi)ZW;>cW0U=;>$^}#jV9Mc-oukK(Zf){b_T8SK8H6)TgU<987?xQ|sb|kx8uuBfR
+J5AI82R=wXH3=D<^QeLWp@*9lF)MXykSPAIy#E2~r^&D-rCIjd@?Y_YyCaOoX@cQyP#3v-^jE&Ip1xe
+sptLIHG8elzNh-%*n;&BA@5QQ!Vhy8(D&3v28)JVSqL3Ypk(GX+bsNN}IWX08t~S4|tqxMW{lF`(~oI
+pUp)ujO=VX8!EiGgAy<f_dkS(wRM~yE5w-yA62PO((VR-y$n0qXo}xZu9G-BeP5%cQ%Xbx>B<l?hRQS
+4mC-J6EW0YDt<)*;D3!*{7SoU^enF8HGN~xeAxQULv+WvpIpj1AsD6r2ETJGfZwc|aJzfk2l~Z_)(*~
+QS&`MV85zLw)bRtR;)FE(LDm-GxCI8d`K1*rn4^WXQMQbel?!G{{4<?v#t={SmuGYCqFvrh^>s4aw@>
+w#XY*FalWuI4dr%hTrp^K2bABsMeD|(yHaTs=wpDdNQ&<6fyWb*$`DpLQ7iAp3#oU@pBv4`2Wm4uQ6@
+hjYu-aN{%Feq$ejyeePQ`FpmeC@vrr$jN`pNLqSd*YZfAWh+5X`={6RsTs2xqsLJ6hp(4o+Qf-20T^o
+q4~$Eu@0SKQM=C%Q<tbAdEfif5{?fGsfXf^uu5k7;WLEOor+R+!q%bn0Rp2krQC>HOx1oPAN}HkVIlZ
+Q@BbN)h>cANetWclG)kR*a)Do&;k^Sl6^pzFU6lw3m6vdhro&gAmC1U2LLJ!h9GzJ#}<8j`YQgIt(&z
+dn)O0f-Y&A)P$H+4b7@QoNjsRTBgA>O&ah0&k}ll*oC{l{NNH=_Ax(pcdI}6?yra<4!vKeQ;L?&xq`5
+lo^}qN7wBA(@GEamp5G9hvpe})7023l<s)}Q5h$LylfWmT{bJQtEA0@Un?YpfZ4CUjMu2k#i^|ne?Jh
+g_*B<(tFp3Ns>(bO(HEeV(u<*k+Z+?L!%w&Q$miw#jJl12>KMlbKUuiAF>%O_Xdt<H5erj6wawWg`)J
+a+4UN8^AMus)E@G^X>x<~fz0qw((6y^WoTzA1o;Si>NxO48qy!$<}tnacq)@JI^WB3jEe7Jhg``g>}y
+nBJ1tQBQs5*fq(}MOLj%HbdCbpM2@H{%8&OL_7+rVVG#{Mn85k(X!?xTf0NQ{8$&uGN?9JIdU9SKZ~c
+q12FBbx~;3YP!w4r2T%fvYjLugRre0KbFqq*Eorylbe*P2mF;sdOr4@G6$LbC3u}P|j>g=HPN$^n4O%
+)v=|E>L!Kw3Zn<w>527*3pSJNM=w&G<%PxAy^6wl&_@5&OiP8Dxj4b{Tb`{z!frp=f3$bAqr?w9dyKL
+3GuMHP3nbW_@=n*~IhwXnTvdq4f{zv}3>(Rku1qp?{ORk5|Rx|Fr6_5t}63WZ5ejVn`bjG90?YEL_Kh
+93Cy9s#=D)u3qIIEFr+`x9ke-muNXK*K;f9PPL<`L7U<m{~dYcS)mWvq1&ur{-a_V`pUl<uc2qvajiv
+-vFIA`yPMzuYXi!VPE49|Fw45k9#W@BU{npy8B)wYR^f5V0G-Y?HGVxPF(jc=#F#=I_m=hhOj{g&5p-
+D(+8_Y_xQM{^ft1!n#QSiptm&F*1+4jaJ4y8n`)QZ?ivw(5hhX3e`HAFKQ+tppE7s&&j@4wqy7TWaMy
+_HKE3Q?cR<FR|4eU;`49DOSFi|MQ)(X(e^RaV=S^SN$k?<o)$?$9bCHs9v(QZOX$wV%2~W*8FjmIl3J
+nlba%vK#oQLJD>5fSXoi+^u%YZ1p{A)Th(@e#vBu_og$6g#)iN%(3-fb2@Z=O>q<=a6H_RK888FYq5*
+1XTah;cJ2RV@Too<M$XFk}O7FnsWvK{u(&U8`A=e=1(IITadK`x9R{n3mMeV2HFfmS}SxzhWp7|2?ak
+&xhCqQ;x$awLZwR8+pKktNGkFH84Sn#sRo45k!V*HhCGRow3QLx<g}jr_Jdkpj~3UkqHxkZ{ApxNj!X
+>UBkK#Pr-tNKS?w|D4nsi{_=)<QQnEX#EqO%+{GaMhOw2bK=~NVc5EdG$`XT(>+F_S$Mu?G39E#FnNC
+WL<~nRLBcp>J`>^yI@f4G~hjDs~u3j2p8Quf^PCc8?#*7kYN%eLk+ZEBUI4U*kAlX;Z9s(};j0|dR-1
+h0D8hD@%ghg5A9WBGUeitY0Pn!8yF$uH|4cSU~L)@+SuoqTeuGcWdxR!W4ue>Xe<-D4qz{XR8Co)kki
+R^q%37vUYOxPBALuV))h`RZl4zJkEjXjY@owBeP=tP{J%)Wc~`uWQ<frYqKB~ROD3wvO=D=P>%9R8Bj
+@r0ouv#MMqd4?*S=7G{GM$tyD$Nm(~m*NU}Wg<p4L46~)!!dx$DnGVyLI7Q**D09gH(p3_3Y4n3+U6o
+Gvf%<7;|BM*ytWc>r?boZcfEcnyfR*9*9(T1JN#eFeBwU})VlsJyzY9wHkqiu^z_w10{NSJqqIKv5|=
+{GWRL%dPK&8P3iAQ*-;F)OeatWzwTI5lK?rhcozTm;%|V=p!*a#8J%Xz%o3yy_2_R?kUUXNaZWhwbC>
+5!$F2X#mH{?(&JupSRMN-2+S3$erN!$KY--vAu9k1AR*n^TUTs|IUa(Z{S;{4(y+J7bYp3wH_QQz^WO
+L9M{y0@)e+~tApb~}HS&-j-|f6;TBiZ@#22>96`JLp4}^SPege2xc!rP`r4G-932;|`T3WP&=ukxt4n
+Q&S;vh`1KDtQgh((fj7@)%ly(m;XSqgJO-7J!nSNdVd^aN&Mdrg7pcry%?rL0=v<>DV3)o+StC0azBT
+3Iz{k*h4eiU--P2m@{esC5e6~cFzvSw{5}uyw`84OHlR}zE{!Sc#xy2BqnA0FUA*xbhIse)#Sedb>b>
+hDULVEqW|+T@W+zvtFaANb-;`>IQbV`T5H6Lu)F&zKW$2!ty*#@*`+s5TdU40=+he%MBlh+o_o2+*Bo
+pk3MDAu9?*OGXJU1u)I5fhb8E*;7e%ARR)l|BiHO}wit)kjz2N<6gs_58dTONXav6iGvn?&l=16RIw1
+Ct&nz*cWLWg+8z9ZZ3kTLrSWgAUJa(Z}A#ZE>1g4&=pM=eUv99uYvKHgtZH3&!Il$?&WIcsE;?<=8~(
+9p-x=erWK9qxd?;eM$zVHZsA|6|s!797#f_scoO`&ZcFTfER*YO+J4ACoJ-zs?5>w&t*egP!?vY0R$B
+douwx$ncT<}%Rf2Nb$8&*wGZvN;LM7ETN4oMv^pT8I8FV{Ker`TWOCY~2Pjo9?(C`y>h+x5A-l!8bZL
+r@Ht(pj1{ChHHk-C&9oo=s21~lxB)BDnwnxm3XLP62lEu*i47_3(Q?u!&+<ZPLv2|6t6P0yp8Eada+=
+#c~=C0`n$(q8w$4Zx|pu9IuD$qREgrQ1dU1JjLsruOqyvcx45Mo55e9YFAc^n&iXO4}vON*&u%Urb=H
+FZ*yccZ;Ykya^V4vFW*$%n3zb>Uo<AizTA@dhgjX`24D!dbTiMW$EJRK)==!yU?Qg{|Wv+cdeZ^Y@sd
+qKlB6d-9wcawa;(C8`~3d8@BR3@Z}wP_MbV#3_8`PP)QPn+ncFnrlw5dH{>d_#hV*m6&8)U6vjoEd(3
+7FsdC)1fP2ybmL=fdk(qX6N&*4^hQePO9fbLQ9SKcIjsVQN39kLF9E9htam_d?BY~d;lXuXSJ(=uy-?
+;oVb4V)jQ_!Qaufxfo_Oz_WV&26%Ox2pz7n&dQ}-E>wvu4R^f;b%=_L{{1n^qLT49Md0JO)l##Sb@f5
+9y7b~oc>1Y~psSPCzQFe(T9HCw|DB9Z!=4k!o)7%IfzWSMglPv>GHZ8-H7NYGDjW^}WUt%J5Fsq1*Td
+lR|A_BvXkur^cQ4uIuEnZ7$jm1}#&p9&njA7a|PNvPb{7KDK(N2Ep$c3^vC_Um>+YIjSA%n=r^AvC$i
+(HfmDcRTtwxjA6E*T|Vq&lws55NfWMAn96Q^g#?4z5a1DMD{{DMV|pI!hZrmMdy+SU4EP?-ur{$+9%!
+Pdib#g%cD|7dP;1cFV6n$-Pz^U<;-qt4H0J@2<<0?a0*j&d-oKlVZ|=;hIR~kHaGkNpO%q5N7!_;j)v
+dMaDA^gZ!zHaF%r`2(Cad7zXzSrc0Kw~Y=1Ff!qm#2b?v9Ej2h2qDs~+x=jdTd)A;0VLs&1z3V(P98w
+aqV8-<(NK{2q6SUIFac5|hiWCc&ZWFIG{%h<ZtwxHMwPROcwxe(pUG#a}XNHN6TOTY++vAIyT<)qgoS
+rSmo;D-RuOb-rQ%u{&bU8Fnu>UG&y`^?8B`3e|Z-0-0qekC3{w@e|9xo9^i0Y<fn&#4x_1&DHtm9kB;
+a&JHlv3T3X8lRL1_04U!WSFa|?J-%87y;q0@mv?qs%P<hbd>=(IgAs#5<Ey+RyE%;lS-9H*5f6Xg=3A
+u4Fu>p-C^S2*ypY5J&*9A^?KW=S8mBQq8mTK!C+;1(?Jcg4vaIp8H=a%MKg@!VNgejcUE{%eO&tUf_5
+K_`(%lLmK<JqHQ*p(c-xHZmVwh_dow7WK%<+B@r$MpKcj*+zBCjc*ag~CYhxee`=K9I(&<4Oq$lL;?o
+uEKcLB;@z=T_P<?KPRJT>4rFPO<h;K>3P?Yu<dURoG+?X{h%2Q}1h9PrMsDc*Od)?NUFH6=x`&Rvu2y
+OE23exQm8eO0S6rx)#s%l2N&u#}x<L!RBo`hf3>CpXB7jV4`tBJzI$P)h>@6aWAK2mtey7Dp<Y$=>Az
+004jt001Na003}la4%nWWo~3|axZdaadl;LbaO9rbYXOLb6;a`WMy+MaCvoC&2A$%48Hp*1nj|fkz^D
+9WRn1UYI`WqQ`=(zL7t&)Av4mfq)FlyeTp7>>C^Q|N|72#PSRX5qCSxne?HNl#N~IFqOq+uhX*l+{pD
+Sh%w~IMdvWE18=EjX@Iv>4bwT`W?bA5?pqnt8Nj!%;U&SA@nZUnK;wQ7Wu2(@@qq-{+-dtZlh{e3ICT
+KIl{9<{L<Tp|7Uvy}WXk8A@2HV)qSCCbN;6?-9Bc|F}lhPwJPlMGal${lw8J(8FTG`onO3WR68^H&8C
+QnCJ?4f;%K>MUl@xSUi$rAo5(u1{a{C<42pX2|3|M{zEqB}JXRDd@7wBmcHrh(yHPPZ`DNHvC|bvj_^
+o<V91V^sa|P6}`_x?wFc#u%zttgg3l2eSvEYzh{X<F^!K<$_jS4NIZ|{1Sov*aa;;`ZM~7qV>?hTM@1
+GJA^Lk?kFf72CF?u2eG{B;H=0m-OJG@aVoN3pw$kV7y~Y`H779JX^vK7#bi}$PD^i}p_Kz*{0v^Pql}
+U;rSo#04^YDPb~J6Jg|g)Vqzy-KR4w$80Vxqd_SjH+4fSD41zsiA`Rl3LxGin)ZjT}!Ahw=zF-Uy#Xu
+3%A6|$&!L@~}+t`yKd|HXP3`&e6z?J<4<QjwrA`hp?<?T95KRw7uj1L1&73T?#t>Uv7c_nk%);z#GK<
+GgG*FFV!B)PGSiE+_vsoS4z1BLBUL*d{f<I`!=Nw-H@$xiCpqw5&N>Zl~K8Or;_xB&AZ!FH4p;oRc2V
+b}J(a+uOddje42!x05<<Wwq;|GWI2r)$56x7CqDrb?UkE8F0QT@M7<d+n1>;=pO9Ixlf-Nc%LYC-|43
+G|K^6`f3T)<^M*p+fjNX@<yhWMlnbb>)#UwN>2C79h_msVb?s%mMmRCYDtD+2Z)#j7;x`~}6XVB$`9S
+tqvPy>2Kvnr}hkCd`vYy+@7-F3=2cR5#jIrNq&YgG*A<>7}teMgbuLHJ`I*U(6+$G{dpFj;Zwd?eyrK
++~3L+RNv>9LC@Q!M$~mAssx$al7Rs?Dz`&g_(IVE}_Y(U6*ImC-5S*0q3zfc)Qe%&lxU$qSwWI}Qh@S
+~x50BrC|fca?PB2OreZiV8W_1rpY2Vhws!CSk^|sgRHOdVrHiwq5J%iW2j8vgzbzdN7gW25UT|x3)su
+bNK|MS7hcRS4t?%y4f<daq#6X!c1#lyu?=%^OjyxN8=1VrEi2;&%}Vwm@vvZ@|f)4fM9$XJ-QbaoC~J
+I_uThHyt3)6hl**-gZV4CXK*z$Z<(2KZ~}*un)HN_5E0Sb+x2Jr_{g))-;?nzb9ZbL9bb*e$W<9`nZ6
+_UA8;6YQKxDvJ*4g#pJ`d$olCAP^8rd&(o={qZ&M8)Ll`7_?PH};_fAX^)z-U3FBS8%a$1>4_ufha2a
+8irsK$paGrLCW+3$5j!S(?k+KRZgOu;*4+85m-vv#`I$cC>CK+7KFy=<Q8Il2n3AvPw5@6Xw68m!B-y
+)deWuX7%<c<`PtaZI;PEaK`yAXjwYm+@8Zv=0jgU1^*O{9?I$Ae#|SzzE|L4|x>8sZfRBNS{v%FXE2_
+SZ0jg3v`(G@aF-Jh;ei|E^vODFN>=GQ+I_q0k>FAYX1XJO9KQH000080P~d=M{JV`qiqBL0HF&203rY
+Y0B~t=FJE?LZe(wAFLGsZb!BsOb1!pra&=>Lb#i5ME^v93R@-jlHV}Q+R}cvnk^@^s--@6q5M;OL0=t
+W%NYRIEVA0ap=7ts(l8S3A+Hdd7P#4OMx2+%ISUht(b7rVZvA9}@(p0M6T#MGP7eCOGB)Pb_NGv?J;J
+nM7w{7WL3oiNl^phlSrI%s^3BQ$IG-eN0{MP#jE)%tDjP)2%Ybd=kwM&wu)Y3U|Cmp=69n?;F^#X5I>
+9Yuze{8*6Y4|9c2D6=ACjvj@l)i68iRKG6+KZy7&_z)!w0Z(zR$tH&ra|cUyGFwfYVU;9TI|4YO@;s0
+l`suh$pM8~Gq@<uXHgVFI<W_Ig5MM`b!^ZaiXs=c+8OrISo5MPpfgRpER~0yLrBQy7VV$lfEid2@MnX
+;pc3AQ6%el&$mQaG3nR(GV*a}oSYjL42@!xf({F)<dh*!&06*vIiUI@M3~BPRE@4&_^R7k;pV9At*~L
+HXcsM#4K<r?zu!PdoUaC5@Vr&Fi=i(Qv`vvS=pdjo4OJZN0iDM@ol|l!~g<_&^US9{MxKLGzn^)yBi!
+l%BZCOI&(?>+7*e|V0?(BD8oT<|(RNmda`}LlnuVIC_6!*4;!A2{G;#Ad)@KK;1$H1SB#b!#<8WFh#S
+z*5(QmPrXw}3hfUiyeKVgfB-Z|k9>FH*PMQi<m2U1#>LH$y9&wgy!RA~>JpgN?M61{WAb+pXENBmzRz
+atC6MVl`H6RE_dKpsI|8s-KOZAWSld%~(o4Z<|VbAPG=a&#lzT9}u-`q*cgK2saK7DS3l^se`b2C2S?
+Cfo;hn4+06u&4-FtfEa}lPy%gT!CKWrV*RM&Q?XpCTKVNNbD-CAfvL%xPo_qmudbf<(r#S0ALv7ve91
++k5~`_C)S9g+AUPPVI24`Sh_8Q5eFZKuI*kG*QFb_RG#^8P$iY0cGs^2y{EnUDaV089@)Zy58VS(GJh
+cr{vub^45F;f{ztoWn!b@#%L*Ra42#3jEtNsDOJ?%6US(H|s{pK|LDD6(9=lni={}}tKK!3v*o}P6Sd
+!dEXxj4-vW9E9|`X6(#!#Rd343U~7obyB6UX!vl)MsPhSr$2n?4o^sY>SHhKzC2HE<-=}%5VAR85$p!
+WH0f!xQQS$HsI>;4_1L}&n5_>-~=^=9C6WQHb5qv!1K^KgW|GLbvQZ7^*2ezr*kp6DknE}|6$;n{T)I
+KaM12(AUje%89!X%P({;l$NOk<bLhh{rMv-{fw*@w-sRPh#?fIQg(5TuS{C$jq-+<QxS~7%jCfuv^<U
+L!p*b&kT+P{7MxW!Xf0g>c%Y)+(YJ69sN7x%_58Z<4-;4O=Yz#ixu5@Mac0Jyye3#tt`FPOc9gvL)%o
+#e%2cQ4F$n?i9$Is~-_c{G36>mf~1pXfz>r(syG;VkkuJLQ(x7M_qEnbNhZwzO`3UuU4w%XH66P25su
++BYO5~a&!jxoamWopK3=y|mDJEzA4e%obdG)w*gP)h>@6aWAK2mtey7DwZ>xD{O+0034~0012T003}l
+a4%nWWo~3|axZdaadl;LbaO9tbZKmJE^v9(JpFguHj=;VufWQiC6!9FEvLCuiPCi)r*$`tvx(jAo$F;
+N5|U_Bq?RCUt9|X?e)9p4phP+Dws-HXUsFjSFc=Kx3o`@mh{5B52(u_oXCqM-)4?-r>2$i?ZYP)jDy1
+rvR~B)iI=?plI-Rq5ti&RV%0!A~p54TeR06xk#Y#-eG%VsQRic;&g$QyfN+lz48yE9974J3ZPW3cMRE
+AxqWg@0oF63Py^E5~Rxm1;lB?9vpuTH#JW_cmPSzLY1A{nasSCUAc-ZaIT`Ic2}%OogZs)hNTOY=>ws
+!l2kuVqpFD)KU{e%%In3WQUg&NR;!!V|jP_itpbV0zzQdlJeV7R_y8wphaA4KhKlq(3i;rJuwKXrhGA
+ZTQol%OH|@>qz%sWmd?(DD#8{^u=UVNYzJV`{Fc*aWZ-phXr&=b>CR|%hj;o5p92yGERy(^=bS}7O=T
+@F#^OdD}S<D21?O<Wy&*dffFd$;~Xf|s}Rqw0g}PoaKRBZe;dGb+~X?9eH`IOfVLjXq%ZS&_z}QANtH
+}hT*No>Roz$Kg>uR4r~96yH$f6d=kMQiI{v&=b6Ed039bR!Sh$;r^g5ll$7k=4{MRRMjz3@@$4h0Ahf
+ZHOK7WHguZ~|Ip1(Qsj}HB>&fmUzbL_u6Jo^T^(?)x@bD+Ho{W_7H3S5j|WWK`L(onilZ$y*>rhYGBX
+@G8!ia6DrZ~D<dSWJ+wM7fNDLV6?yI5eJ$d7y%#$lVAqiwn6>js}Z=5#SPe)QoswQ%+NfNMohSrOf?A
+rf#OkSuitPEk5*p8>$AugEkPsLGY+$K;}VvYyW8sPcnN*k*`J#zTz&hp3LK~upsk{HyotgI|psC#4VT
+6DJ(2N!ZqO__tpawC7X{8s>++HA}Cy*s|Wn&tN16`iaiJkuvaN<{C9-(W}f&8W%1+`IQs*M!pW&;zO<
+6lGqBvA;TvdSj`M@En7bf%n|xEuWhG9cD3_p<u`{XCK+HfqQWRK0@<G6A9%@j!?qEcRe)Pp#{CJ1rew
+^o7-l;|~YM_wjus3hBRMwAh_9-y5uw<!8?TuZ=za$HBo0W$^L71088i8rZUFX-`pZ9)oJk;8_HN9SAt
+mcBh$rYiv33#Uks0cykra_ztkk(ZE`J2=4jzK?yt_0f0DcCIN*a8U^<82v7G*)@ygOmc*_h@%_QqEwS
+RWS#XmF2VDSgBGD_x7JY5r1icgrgJ;n}KaCC#c<buVo5ko#!$xe%dv5nBl11{bx^}Zh&DP>NMJcOY8N
+56;Uowwzo-^#tuT4jSiDs2G<>nwGSuA88XO|70yzio&%z#4%pXJdR0$&W&sDPB7d~WTMayq0SRNvh}r
+FlQFfMs`LbM0z|2-0Ya>3-z=Vzgmd6h~wFI}sbw*C_V(+rEUCFSZ!BF~iYvg+#%9BisI1Gv`S1wM`TP
+HUUZ(}?bRPuj-#50hIUD8CiS-#0#)WJtWxO+8Nz?}4z*zaxGe&On#8o*`W3>{u>C`)%uqWj|Hl)kKS$
+c{KMJA4aXNkFa&C@>+hvmgZnnsx!)$rcy66c7r~sU*dODzv`_^Hw@ZBCbl<Cv^>5%5+B>Zz}5!4>?>e
+k5gC|$XO6x!>3ZrX=Xtuq_Fg24naA`ld_QOYhsYBSwWnXPZ~a0NH?2Y)tdDzM^B~5xKp(b>?krKh*qm
+G3Y<++r#O^4j8Q$Xd6-;*fxt%a9FY5a#(=og6O;>3EKb4Jpi6*lecN-Q4%mvj|EIBVUN{>Hi5?I3`T5
+H>yQ{ST!ljks|BQJdN9$Uyd@wuYY`SEBEJ}czgpLU>ln?@&Rv>K@C_Pw8T#y!>;I=QpLjy5Ey$Q@hFl
+ztB6X$7y<kTb5tY{gf%5dH{xCbf0X!x;cS^LZuqfsr*FJHcFS<Qv(z;E>1pvmc3f?&bCr&l{vhxf;+`
++bj8W5Wa73@ce=ATn^kNyWY(k|M>>We%@D0sLpeWyHAr@>>u#TnL-OQ*P*7P1f2_<NnnQNTd3~CFfrs
+Bqmvw^wBTv*{l`Bpo;26yAEt9?kBjca2nIxVx9l+*>FI<iE^60sJ$*`KFyopE&so9=84t2<~~8EF%)=
+0{9fK$$96kUtv|UJ%r<WLO+fDF*Wa*p-hU#Wwa#sY3B2!u<r39xJufe7%%80%D2pS3U3bK5bVL?$p~`
+uDo$cPr{Q4(Z&V*Y<J}f)g^N7JWj`-^=PWG9mQpmQER3Vr(fHzr*_pgt{aPR3e@I7f6V(2k~Cn*93SE
+WqwP)(D-au<tWpd|bXPzV?%9Z|`e$wV$dJQOfH9}DI@uUG`&<fUO!0<|fA5Dl1_!0Q!<%_f+oOxQYwS
+y}`!iV)3AebX!*aDQR<2ZMg2VJkc?z|;g{5l2xXVdUTW5pHiT7ugNUa9Lz<2mp610Vgz=!lBuCCWZ<H
+8dBW>{H$vQ>j8x^!r;{CBa>c9L-~*FjSwHvb~6gFc;F3ib;Y7giWpB;MZ7>{K-+|1P=$#=VB8DDfYeM
+5C~7p$=F)IXB`SeHR*Zr?5~DByhh+2-N&QhP6cpf1&Txth&INtZ)f6OXT`kcU{1ml;{n*jY?pY>6aLE
+c#mx95u-;A5wz(52u*t9-|;;L=X1dGbWvZ9p<@aaX4kgFCv=ZKa^yGu;3btsQy^)X_CjGA*dGz(A>=n
+VpR!HNyflDI9%ADfY}hS(U1hu#+>aEr!c{yx|r(GREVG+kVRJ2v4l4btahIE~yNfqT`nbQ|L4Az!2s_
+2}7(fgk@)oj}o|PwND*OrYocnzfceoIgZQR4yb4>5a^h>xr^pIKi2xlPlRqiytwPw27HB&9ZTqIC>hZ
+c^7@xeB1>LZe@}f<|Ko(T4KZtxu2qX#Fcp|s}72SHf|NI(tPbY01??*!*qIJHE>#Nt0qSC$3kD9MnT(
+XvTzgx=!6#jHim--I0PTjG;K;;V|8dkag(yL@O6ZTy8qnrWJI>^mxs$6&h&pHZwUE*d84`8<?ZKR{!M
+vv#ts=@7=Q!UkNF7x0v}wOIFbDvtwo%cbt9jIj1|Ul<`6%Iowts_#sx8=&Of%wx`Eg#>^!zr+Ak2HOX
+Cpyf#SX$5sK5BL+AK)0B3(q@f>#TJk<uX;xKkq1je&50TCpq9W*m-&hfG5Y5RmCnMp;Pr}+@i?}<hY8
+4C{5;3|M+oaEW9!mtY^ZNj2N>q>A+3JM_G=s~Tlim+{T`AkIEZSgG8&hozOHTHLNdv^eOtXT4kmR0m=
+8PoWV0~63sWo++_<S`V~mzdj<T3Kk(0KUe^9&aN@>7j?bTPASO%NwWi9n6(Xf(bYtD#L&Q>LiPHtjb`
+kPL6f9D_Eds5i1J>c(54<QIH4pyxpVGkD&a11f}Q|cSRRr85U{xU74NjAR0yNm{36-)S%N7&<s42oc2
+7N!^VXLBAjH|H91cflt7%nJrWpK=;OGQ-6t&*2#B%JN>Y1wu;g>4<3biJIGiOxDC?BRL?+p-$Wm(-jJ
+UI-I-TNakIrP2fNL-S7)Bj>in4M7G*zJS=Ek<++*O-iacYeRal7+lS;vHWBpMjlL=O_wRG!1+37i)x;
+=A+Y^JmR)Cn~^NYB4;fwbPK}Ge5$g)0%Q%@ztboxnmy(I6b?yi;c#z+C%L$6U7v&WSSdZwWONRAnx)e
+Ss5lWQ-P>bVNEFxuSWGFbM6Mp%DV!2MVJg9LaF6hyBhe2>?>QD%FOT+lrI2ZQO4?j!QVZjg7r_mYKso
+m7)^~JihPrJv+2>d`?e9MUZti;8@28u4p0S7oHDP8f;{Lo2Gl5w1V%r!JIC`V3k0#RwI@x=UJWavG*Y
+Loy++;D;}jy0VAx=x@XAUz(b*bLc!rTc+ocGOJ~zo#^=D#nU{v?kzR4P?_}Pe${_MP1Y}AC;Gz=!|?%
+@~Lt@y<`K~t<#U<ck_|DKgpPM|(bdg3+kp;N=6*j=1*q6zjI4$+`i=&;B3F^W}Kk{L%W#WC-l;qd8GP
+q=7NEm#I)q2_eh&C;YgQ)Zy-ydL<pRrgmIrwVD(A9$ROI-okn(`t^IJ(pAk09wY)oflxK0`Kvcy2tpU
+`=fLD*!4d9vWH(^UV_G$Cc#XNVb}@p`Z5n@i{L(BOeqcQc?JA_uy>231?E3r{P-n20jOp>T5xOzSKNB
+J-ZX}za*|Z+Cq8Sa&uS^3>d44#^y;-RK648@D$3QL59IrA&d%P6Z`j8{p>A@%hBdNvZVTtPYfwVi$D<
+UGSVO{$vFqq(1AJyhz0e%l!|sXC+1)5+6fCefBAmsZE>2<3-|kmW<mml!P>u88jk&n_qUw5yMIG_d?K
+(epyB4Sdh(U1f#oRgha5{MQ?DH=M`;Mt)D8S(Z)h97B2wSa|8K_UcOk-@ogL;n1yS~Wlt^7b^1dl<*Z
++bO~EOM{fn2kuMeNr-mn+ARrOOSi)v$hC^&vWOb!?BJDcQj7n{D|?mAkmNA8i$@sxi>X-v|pgT0WzlZ
+GQDm`CBMOH5QgUQr%T-%hpGIg4f*`0vkk<=K}8U!K{CbzeU;#{0aZYFqYyV=C2=T~%i6y$=qJ{i%!+x
+vJtwpZ@T1^F!(AxmVNSeN+HIL3v+l8ln8#R?1}!hBs6waG*0JH{3+Ok(zMnNPsH5rhY}|NgDGbe9tS=
+15xp{kqhBi-d6f|~d5`?r_8cb7F<RkH3L$q8inALQzz_7B=F_l<GI;yen8LPqi$yO57Vd#lzfR+%^Kz
+G<U;@DVCQk?@jzrzr>66!zE4i`w^+OOaUB|X^z;;%kI@k))1@3A+zXDm*ofK11G3Z%4N1hxhMdg6XHa
+3lYJ&KuHFDzmmi6X&cU>D9hAtTXCtAap-6ebKJKG-_u+tu?$%ti`~ha0`ak%hbf%#rgXa`_Ss*IsV{_
+XHR>b^SAGhKODdR{`i&u;rI+_59Izy0s|Jzw%x<8j$R$V{`#AfZ~yk@ySJzB{{H^M+4=WB{Num>Czym
+F>$7=$b)78IZ27NT73IzC-RdWng3m;EZ-033<nyQh_3Vp*=l|L@{)7qa)EK&D9@m=TJRL|C21`78uP`
+nYBB$ska}uU}NI*-gD`_i$m1D?D_BfaW5^{s4j6)aHu`fW-<Sa<Io*+vHhaZkkPT2j{skEr+XJZCQQ7
+ukvB5#i@I};;fd63SeyWb}@O9SlG-JC<lg}R6@FZSR7Y?~PzG4EjvhoaG9?!_ud(sJSU*155zL<~!G!
+g=n(xyYUO#s1UwQZOsWxdDO`i*;eB+tuH<ZN*49kCf9DkHp-nNz%&+ksM7n+nmx61Ca;Dsc17CT{f}b
+s7hQL4Lf0t?`$7;{}|&*Io?$kn+??5iyEA`H;<ca`(APi&nkfd?yQnsz<(xIO{F$cH9NwuJS%4yeK8W
+U%kaWgh{?4~>q%8|`2=R;1VN=lUrNSP#R|0mG;p@7$+{|-N{Y_=dMjc1-9PR8`O#k-xA*w7{_g1c_{+
+auT>iS&9fSGJ5$yvSEV(0eB$f?I(bP$n@vH}&VYtj1V`S4-UQtHQJNK%@*bWb-wQ!@+eB|`jwIvO9x9
+Fiht$VydtA-}7#lv3k50dk?7#7peo15In$Z7&EzCv1=fG}(%g<zZUI@o1QvQDG{72#S;9+-FwC!~@Za
+DNt9^Pzb|uE2gYgRZcMiYYj}D_26(G7K=f%~g~nE0p~9woomP*C}MNN3RYq<yb=&r&wr*AwoA^_OY6+
+7MK>n0M(q`DzVB+Tv9lf=mk#EgE@Ieu}^d?C}63f!ip#Gl|UVHuy~~LRg^}fgy#~tcK?f^xBv8+x9{z
+<u9rolb#Hh0q^VjA7)mfa6wcmW4XFy*FOeM3i)-~gu61&q@f)YFFH(3h!13W&k^-H2RE&uup%I4ycf@
+3K;J2|kyPB<`X%VRFheD6C0yx0eqsUGm>|U=iDUtSp5H*f8Ui`)2(}-DU0#pylyt!tcy4gf(n>f;=Cf
+6KsdR7TqI(d}4cXxu_VPufIyF1VN*W?e_N*V{8H+)hvpV{x>=bwA#-(F){y$gGHQ~f9YDcpVW;)OUEi
+h<yz4aB~^c$;2nkp-?MDwFi(iNl*)MO$JJn&r(CUrUfaq(NJ+#hcvNjJmh2t#mip>N*m8+Za>!xk85Y
+oFB$fZh-53tGDEP3A#+hl&N8v)rFuq%4|xERgFGfl@T{V9tV>|S~;yIZ185wl$jh^W&a-UUNb*3@*0d
+<P%id2%Y3)FKn<$cju}n#LhPeaq3<tY*B>%=1LbYI)`IxnCgSaz*}9=kzvlt_%_l&8mC4r^oSHW)=+h
+kD!B9{J?^c?_BM%jNDAEcnOw$X&^BwcSSR}z>5(VOVgtfRby@~TIrNA8s5*AUfHF_l+xpKM!f{@^-6~
+nPZGo3-EVl&L&@?8Z}#Tcu;?rhSV0<S(XtXm0I6R~5POvx>6Y%7qM>gUx|P2+k&u47tj<;<5h8io71_
+}&Ue@}M<W!dUj^>Tc03D8jiQ8*TT~rJbfj)0+UtF*0Jw(_>v>G1sLLX;dX7u?N<R*Y@99s`*+c-UO!I
+oStFYzGIyM$Y8eKG_d4qUEx)l&ivu2im%uKJWGvio&@4`jb(<lT<*~Qw>T=F7`QGyrfu_}fymRXi%%i
+0$pqV^a3}a41<6kf{nom*Bgt_9;BT&CV6MTmJ6<)N*$9za3Vah~l+X>*VhccYy_QSu%~vv>a@rhb@><
+6=^b7;yawNP^iUDHgF2$0E9&UuUNu&`{hFxhGsg;az`*$?n%$O$Pq!BdtGHWxB34RPBM=_QmOv>48g-
+ba7ApU_g;mbSvqN>u+Kyco1_1DmQO4K4xm2tyDN~8>4c=E(49$Z4AHLY+d!+08p8jy7Z6V%NO#p_b66
+TqY2$byH!^e%Cc#BH!rRE3?(8yO@T9-NQjB3>Y|u(BeN3tfoZFglt9;|4`sW~_#z;8qc)%F&wzv0S?;
+Qh~D!9dIx=Z`hl9+neUGPA!pwZ?`t@O}Tlbv%tDHW&jKWdbS(!2?J)Kq3gL?ZDf7Bx4xw+r$Lz%KHiP
+<gBhlMsr;zRWq|_|+3mI<uM@ddslB-Xq+trWZ$~hzwfN*tQKEymc3fS{);H+ZI=FR6bYtjPI7iIBR)N
+J@FLO1!Wg`7VUn`Z#mMPw?-mM-4-+81w`p*%MgbOMFCR(nlyWtqs_7=ev@ZeV6yX|iausHoAfrWY2ND
+Q_2%OwTm6Oh(~3S1}?v{vDA50P~2>0(Ql{sX4H{KSrZ`v>p@Dxsqchk*+E8n1-iKj#Su?;pX7Tj#Z<`
+5y9~r`KtAn>sC5eV4vAOV;$<V%@A>`kK;qFWuxeLq(@+rwj{zjYb>XH6*oe>WZ#r4G^SVEk#@vYQ12g
+w}s!WF>BQ6ZzI<OK$#`=Yy9zAPg8T6ERLzw<mI%yt-$@cn$S51)i^jr)#!}cbg+e-a~LAQ2gf>@f>(p
+UH)UeE&g`dniN65|?3Z+Un?rHL<K4%O_24K=Q?3rjhu2ar2jE`bNZscbvq-xXzz55Ht=6m4H%u7~!TT
+oZwW(RCOqy2a9(ed~;Q2_r;5*cw=j~2GgbMF!DQ7RA0~M1oH9V(E@^KfOftlRJr`^6DM58btWe>vN&o
+_qDnN0dZ3$ZeJ+UxfzmxrNXfh7uc#TqP`(jQX0tKGhM{GzMNmG66r0a$3OwgS2v0MEOPqUKK=-Z#MD*
+`z*?5UJ}LJJWR0g>l_WKBk%4ix)42ReoU>$O-7Gl%vM-A;}r*Lc*8<nDOK8Z%)&!-94|?0+Z+^G&ZYO
+m1ws<Z4#tPZQ_sh%U$r8l8kRl%yMF$>^a^jFsIO5>H>doa4<iQ9yR#F#!2R@#a9Z{OIb`ieu_}A{PS*
+a&*4H2I8+8IjAMtCe3cl6!JM1JebC<c0RzA?KZf*J47usRYC33KYc_McQBVYW+jhjaA5PzbQ%_ztRr>
+0>VmK1&Y3n2e9RMOQJk^0BFb4(Dd@C$Hruda{^AHWb7iI1`#5ulCfUP2DZc2UT+9AOC0<UxPv7MLiiN
+mCt6_m(Vr{DF_`OqFEJN=5QrN;u7b}@Q#S-Hi!j2i35xReIQ`{!q`4~G7m<G!dHq2a6JO=sD`cOV@M9
+l<z4M|1TpYR+*nI-mmd<}}aR0X`e3c;>7F?2GR{z#gmsVHe97vN8w#6!q388eX<6X#eTjf^_57MeRS`
+x+vT?+8GRm)1*2sNsLwDV|5#gy)7$sT6<gZLu+BUvax30;fQeCSA324AdmPmCp>D4{plqQCemqvyuSf
+@M+{SXlZSYMVW0;<PAtef;#C|`3Q6<jP;jOgXbyb^iSdAv;p+*%bxItxQsSV-_F%LXtApXz^*(g`y{i
+hWRUHi5S7qx+t{r5z0MslfQeTC0aIE}z>Z6{dOk;xSuv`5^GpFM?UsJ9ypoj;>KQIYMA?Fq0L{~!LNJ
++!`W&$hC$WK@yyfW8Us`@3PFmyMq<mRZTtJjtBMQ}hOpOxqhdVm(|7d@#7Ao3*#Hidlw=ieL*+!V`BI
+a6#$JsYbvVIb4NT)9Z&@ETK|fhzjMl@zX`Yo#F1^d>bExUF8S5@=XD)@l@@I8+L-H<@xr0a#SXSTHvT
+gA#Oasq`d31Taz7yHa+jonOu}t+8YrBI~UmWcVmPR|j(o-BdH2O)oP2NH#kNtQn_%b(rh^CR#UYb@62
+(TsH;qn&A|~T=>c(4*b?HZ$VpxrOChuX@M8|b>*#n1(M&Oq%T6F7f$C6^S&0(Dv?3Yw)K5A4bO<)#WW
+Ts74Pyu&p_@~uVyn(%v8N**sA$blYU(&)!UrQ;ed66@brF_>eUKqfF}{3qc&9?W>unXypv{1#E@3Udb
+lnT-<L28@`&E;fzfxWAvTG|ppFPJ`6)ghup^-1m;?l&WEvSv<ryKbQq3t{btK+|GYd>fH!%<xW!VD~2
+&LVbl3zGdWLWWs41F7`wX0^B4H$2IQ-Pt2^;$fO+B}x~w4BU2QomfSCD80q#u;oW)Z2)%H^%Am+qJSh
+^U9af+<*a=gbxs)&9{d&HWX!r%P{$6H%7ggdtqxLgvCM)IKM|}R*DjHJNkP57K`%ncPXGMQKhw6uG0;
+Fr8}h;)kV2~S5bk$^*a9xP)h>@6aWAK2mtey7DtaM>g6*4002G!001cf003}la4%nWWo~3|axZdaadl
+;LbaO9oVPk7yXJvCQUtei%X>?y-E^v8EE6UGRh>uSzEh#NZjgMEz%q_?-Dp5#GD$dU-ElG`s@VU6sK<
+e~h(xpW?IhjeuTmVo@0|XQR000O8^OY7yacoNbjRF7w4+Q`KF#rGnaA|NaUv_0~WN&gWa%FLKWpi|MF
+K}UFYhh<)b1!3PVRB?;bT40DX>MtBUtcb8d6iSaZks?5z4H~L_z)veYA5QYZYp61C#a5X8M~2B)&fJ>
+cD-xuE_V6*4j7VDNu+j8Ff;G*=FQOSy&W(P@1i-(qDeT9Lm1B%lW*`&Z_nw$y5I%`Pc+jN&||I<!KmD
+xn8p!Qs)UqD(3o*!HBU;5z%4MDeN>w8dzJCLs#B%RP=hTHEb6@hl{YVU^9{_<7^>Z&M9l;&%S7;$z%!
+nrGziSVq0X5CGf1iiH?4)ltq_<hqGy&XIRfNVufKgnZ72g*{;>O6%jkj9&}X*RuR$GZpi81F5RBAyNZ
+)zOg6Byy;Ig4D)Pcq-h>!+7aUmc<C=KSN@Q8tW;W1j@FE(ok=396SR;ysX-j1l(7K*BP#4{7$9|R|2n
+o2WiD-uow58-NZPl#X~&7$?TRzHo_^DvHKx>!K~%V4#RCYxEXg5_qlT*RRd5F>UL*Ji$D@{`3mQSKEv
+nW1G|nDgjcTCX92B7=e*k=C3dKaxBKsXA2uwL_ugk7iziJR3R!o`Y1@gOlcTJ665kF0nlC*Mmr=z6YO
+%)Wqa3K`V#}KIJ)4PlZz2gRwHUZhZ&{JGdGS2OoylgCW2srp$W*;r#X=|BRmF>~`&y>~?Si7gzqZKe%
+vmt@dClQ!i3!+g;L*%Un5*lVM&vjH&IjahbcH9iSi!`u&%k@CEwU9t`T6q$4w28p~vg4alQkiKi+<*Q
+G;f2)g*2<m=5#7BcxYQ&L$puG{LULo-g>`p!g&q1H<GFT>vl(%US(X$O`3q=eTnvQA8!y>y+1s`0_^T
+%ev>858H(Th%4`_NwA<y3B2x>AUA{U!y_$^p+!LXA|2*DwM&ttkITQo-w7c&L2=q0|XQR000O8^OY7y
+ah$&~caZ=9qCx=xF#rGnaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT49QXEkPWWpOT
+Wd6d0fuVuH9VEKN3#Q}`LxPY{L_9ht-yKAZe*=1J?<FXA|Mx*AXke1TrM9Z1#RH_X3ub)_pJEsO{H!$
+Oe^wqtG+}va^K2`*Ie)OZi{lCZ0e(_IV{QB{$FMj@uU;pkGkDvYOH$VSBAODNb{^;dLkFWmp=JA^kZ~
+yJ>yVu`7e*XTOH;=d9KmPgs|9JcDx3BxZ|NQ44-o5?$^~d)g9$$a^_RYJGkFWbPpS^r)`o+7qAK!M%e
+|+=rZ@zv1-J1`O_kVnR{2P7t=YRd?<M&^`{_f5HtJ}Bl-+kZz`Qf|wAN-H*`0?BKANKFR{_y7Y$2Z?R
+{_t0O{GT87`U8D^_t$>ZpFTc*{PVwi9M@Id#ozq+XOGXn{r2%CpZ)&v<(u!{eE9b_-|RQ}`TKVtKfL|
+J5B-`y{{HX&Q~&1i_~+Lj{@dfXZ@>Mw4{!hc_{pP3`0?@a{o}WP{rIQ%@9y{SsrvTqA3nVP@K<K-k00
+K=d3^u=k01Zi1N-~OU*G@m`1<v`$A>rHy#0Q6-xKli@%r62KYjn9+yD9fH*f#=SAO-wyKnk+81=_DAO
+8IPr#bwmU;qB`t2f{GNFV?7=G~hQ{Wib-;Sb-w{kj|f>h0HW-hKb(@%sD6cl_@AKmEr{{gQ$F?jFeFm
++!lKXZP<PZ{GIbdG^1*`S5-J=5c;@e0tj5<Euxn^^aeFWc<CF-_h{zdeDD;eA}z>;Zw`c{^k_?$D4lh
+c>B(7|I~BeKXixvqJL?zdi>$d<A?9x{PBlxUv&@t*~fqV;;aAio8N!+`25%Z^7yZxfBEI-zy9i9{=Pr
+kGu?mf_1itY{quJ%g5Ny)tv<Yd_wlbi@b2KBfAQtd|4X;{{AXYM>Wi=bh0*`=i?4qDi{Jh3@yp+Q`S|
+?t+t0uJ>WiQM{#T!W`S|Vczx?fQe)o&d9*^I>dGl$Cnasa6$^Xw3|FOIL^ZTC3Z{B=-{r21M_pAR)FY
+os~z;C~K{OR?-zv*TE`pw&xsK@KxBH#V>|JNFR`~Ka(`M%xcKPL3=A8-Hoc=!I}tH)nHylvTO>HD8nw
+|n`I^?lVs|Mh3D9_#22y?*!KzU?LWT{r*b+dp>Kzx?+7`wy=kKYRcEM?U(`pLg5mc^uE59OHRB9>4!x
+|K@-7zsE0r{g3}2mCBD^y5pa{{kO%2`0qcwdG~co<?BCu%UD}Lzk2=lFR$PI@2zBdBfS35L;UTB_kVu
+R_<JAz@b2rk-}VRpk3W6<_}zc{=}-Uim%n`W=9?cr`}mjtRL4*M+)Y0G^glcQ$61fZPk!>BAJ5O`dB)
+MdI#+!9)yi7@Y8~gNU*+7tI-cjee^uA0NyF!?`&U=48b08QZ{DCjK1np=`;uvX{KKcte){9%C-;wk_x
+OkNGj-3GJ;wKc{u}Myb;BOcPk+4T^Y}^s^mqU8{OowHy^;R<UF*ZY`MB4AU>BV0e?z49px(dhZTkI3H
+q)Pf`1((eKebX*fwa2$@*lr^dHLBF|Fr(~tAF|J7eD{}SO5BtU;OT?FTeQN@4x!uH^2VZFF)`9+pn;C
+p??0S*B=n97OMaLn-6Lsmuh$L`Okj!i+}yy7ys8U82ZTQj(c5c^VR$RXz>q^AHTH1JYF63?0?oXUiEO
+-t7DGiRjcHB)qlPEpE~&|UajY|e|Kc|=bq=Q5!KCl@m`&E(&Bi&dZ2=jco}ufZZyWLac2M23*G;j{e;
+JyuUdNf>(MQ*+5a4sua1Mi#=2gOEBWvluf|-*t25Tv{f_hHU|9Wue)?F?SCer(W<U5mkM-&?_|J9qI7
+UD4xPr$#it+N(s#o3PlNY)AujAbP&gyxH(ZBX@M?ZZWJ)&@w+5MhpfAr|F^#jMNbMn_&ez-@ndeX-8I
+{4gVGVXq3zVsY9d4tz|GV;+a_xoM1&h*8{>P8VgqS+&_{>Y=ZHZ#|Yah!wUoILBEhkCyBYxK-K7q9zd
+J@|=d^{0B!$2_|?X5=`T{iidXtjJ`%J+*sAn4sV%`n}?%oA+QA@^JM!_dl#SLUHkaF&F>zU*m~99IjD
+6dJH;G7GwW`|Ew(!{R%HVz5O5OC?32JwhN)?7rI6-#q&B@=N?D)*pAur(7&5>-XED0DU6=4tLLlN_t^
+cOy>3VLub)?oj<b3&{AW+-QPcN(vg}V<@*@5AJ*s{}b#IUC|LhO@4oupo@y_}3xUR)ZCU4)J_b%!__|
+NE$`u&PU>5-3qn@2K;gSlBf`{zhr?o!y8o0c2<jps{GE%MM3dS*}FF?vQG5sF6-|6**<t9x6#crRQF+
+vL3vqtya?(5#>D@VA`x#(6n<mm{3ByX=m7{gEGbbc;rh2!Va_A_qb=`U#Iw$jD+@T3UJv`g4=Ly3TvN
+*ZI;i=+vSWT7Fub*(Tk2cf=C2?}L7;rTaLqo~7~ZL60qG$%h&K>FAdIp3h#lWNgzBF@)%0^=$FZy^P1
+v^V=(vG~y%E4aUq>_ffsRy=hS#z1%I~vHLyeOTRe%o;|C1_B)^b`2(5mWoOXz7VfCt$NkrX0k-V*k3D
+D>YVewMxaqf?o%8w9@6w{nPVLVR=B*d%=r!sAD(Zt3=XX7|Jyf<Z;|;c7%Q(%Dy?)kFXjb}N@zTm-&~
+FbbFGM%`!ECkObG?>}eY||}lgG?~rs$uFp&o1<=dfDNC$eY1+52s>ArtL%v8G2Lm%TjwEQI*t{aR24l
+id3UIq%=~Clvc)@%lwp_uFfJ_gnSSAHYhukI+C@x&741-BGVQf^yIjCBbeSY^w+s6pa!{yu0Q+_dJYe
+uUqs0`wduF-ftexG@pu-CtS<}lRS9wr+py0Pc%h86%&mp@HTn(dp=vAoott$yc7E}Sgn4c9+ryPE^8t
+q{g@VZ*Rpz-^|G`8`d-Cyquh_Z-^PfS9+skuSwK6@e!pH=4TSriokwq&wtjvw(->6s<fET|U6_wkbGP
+@~SZ9xS@M{ck@6uKSr_-YHQFl6xsKbSFpLCgiq<>n|eW8VE;d$QsxZjugb;WxDDtpRSKcPjT2Ys~8Xw
+|zIIsGE02ljRKjI=_+X!Q#Ae-8ffS$DtfgE7nfIEGcH$_ItVgJ+=&0?UjMPt-*g&tk8%C^I7$W?XgE1
+Iya|&gZlBy84<C&7Res5&exo|M5{we6dixQVS5`e>!uH65&Au^_2Fzo?EX^{F3Nl^$Y3XplgxjOh4~G
+y>3?z|0q`QbZL>eep&vC7S&N;E%Vy@Vzyt>T`~_?@^;GEsfb6iWXX~RQpkc1RStH(4Mx96zhRI3KtJg
+)VNK8|^ow6IdR7NEr+4LnS<t#a(VYmJ4$HL5e*OrxBi08~jOd>3pT!EQDd@NTl9pwR7i$`Y7C|S5YJe
+5T_Tm>IUFdb>aiSkkIVUsM4@80X=lZ969@;Ny0cC86&SJF~R!_l&#$xv}gl&pT)u7=)E1(nSKx)e_7k
+}9Mc(z~C#um}RmSwTQP};-kX2}-L0lL2Z3`Cp_v(O$%=SQG*dl0RVH1=E07JkWz2e!~%2P>Xo=lyEYc
+BpkNny?qId&64yLJcM3>UC>35TTfuHjVe#{@H53WH5w@#%QZI*a*1FJ?P1f?#Ezk^&fB+v!`k@-tG;j
+s|EI84to&p{Cm6;J?U<kjuKA^hjTK?2*n5-i(myuKlMPc+o|k9&u|}OuXF&OuKwL``y~esXK!JAXiNn
+f{=o6>B}PRR<~-awSP5*eW}iX#l3|{w{tGYs+;(%}m*6@)F4YDWtRIhShC6}jelCngF`>A8Ji1>5|2v
+%#Uz4G-VOth=zwMWt_Qngf5b;7o)xg{LQ2Pmi8uG=vCAO^J5Odd4)owNN@bq`=n%l1q{E~hxTzf{jTw
+PoOKCk0n1SKaT-b=%aqaRLZ4)vCrah$Z%`P%Z^KETUlxA#I352*nRUNmx;mdSWe)aGa*>H*>yKNU7~9
+7Yajs=Z;}aP4`B_Dfn;m@Qm~Tq@T^BUJ-#E}P?ERj)(qcd?0>+TkFG7y4Pf578rQ@8e$BwnNLY`B9|{
+{fl36FhC-ngS|BrQ(~&t5rrktYd}U>+JpC+<DQ51OHQ@e<>yNU&6a1PE*vbB%EKkYi#_quPqhBja_x6
+GoH>m0Z8uvxwtheg5>G#IWaD)EsR?muhYl~!BIcY8(*wl%@%#(5$#5QcEsyh#WAll9yI26`*QjZw6Ll
+eMJ;3QsRg0lY2~YavjEYgEC-cD0Gd_;F{fBJ7<bjSkv3O^?XB9S{j`;WXTf_z!$}r?+8rc_>Dj!}Bs}
+=}lJnw$-OV|Vbc_n`mjqz5-PX`Vx4&>mKh?#pq(J|U4+@ICWdbtPhfTkK-9#X#~_%)GN8w@_L9b%W^2
+t5cWil;_<R6VII2Iixls8=ij3LhUGi?sJ!>)KwoUc`Z0JkX&F|99wUt*5$+xbS4L87lVTB<xxk)CZnr
+Yu1w`!ybQ{hxSX3y>WU7Jr8OpHsr<zJi$mgSglLJo~Um^rgAr6;+go?HT<;Ytnf>gMy7>$8XnBcJ;3`
+NsuC2P;pdYlXgp6HGJ=f67bHy0iHkAzK5m23@o`JRp|_<Um8m$fP_8{nP>Y+F;$iHA2Gq=8LkQ1WA@x
+Y&_WNqEZa4ynZvVtvB_S_L*l3#|xt%N{BcfMAv5U9KrAOD#hxxQ4gL_chf2j6Lx)J)JmHUYWZLNpN-0
+$o*Q7(z;2D=wwza}BpQ^6v{QY*27?HKKT+b_Y$p>T2NPZb`j0rxWY-trxiwTBEbEf>==CPK;H9@Fi|T
+gTqV)qcsi$9u6_-BG!ZdAeE;EYqLsS$*mnPDjq#tE@}<qxI7vz2~9*l6e~7vD*u;Y)!fxICc7@+x8Z#
+rQYG46Q4L(<cVO1Pd%BX9Uu4Rduc(M_+5&EHb<~@A*%uhEHhLGyL~+ELko(5BD6Si7{=h&pL@I)yaZo
+th$<d9-c0u?0Kx<8cVwsov_urvo$wA=OJSG6KlXJQ@5a^-7rZ34KEYm~V<yYE4due0D`vltLj0{%9Wy
+{S;t=1UI8IbZtETNgT=*pj?7-}vOkOv~C75=}^6;i76MrpKgU->Vg<+IKxq+~hR(;D^hp?<$f;*(0s8
+mCFAOMDM8cFE^PNK{fZ~ltQH(d;M01+3m6nETm!AoZF(SeN`>@qbbn*#!R;2d<9$kfF~n9PWDKu1~`B
+`m?3wTGcTV+XcB+2bAfZijju|F>1nMP!Xp7USTUMml_C6>B|*dR@w=P%<OebNAbRNe_lSd!d*vwvZt#
+jh@QLqnDkC;8YC_u?XUUws{cgv_5ss4Q<x$xBZfCge4u!Dj^x6#i3t<j_H36`W1ztZ2$v9bccu?PeX;
+|C!D$C9`7Q4>rr+viRp%Z;JD0Fj^~Wv4$C^wlie?`iw;M<4KW8X&k-7d%}ZLuPR0u&y?nY4F8;yw#qe
+y`7g)ff9}I8^oC`&cTlYZk^kkr<=C%86zodn(7Zx&p0At!EApU1>jp0la2k^Q<cry72zCyR~AO6z_Rc
+Nx@7ewqYJyhOws&^zZNV1cR0-O3rm$uCL0-9)rSxd0NncJO##m>EMtx#SL%;O0a?7fIVL)Ws~2M?SYo
+`vd#k1k;10q7irB?tQV#7`Y51Kf7LKh*Z^K`g8SD@kw&=LjbG=<_M(@(Iiht^?u8f%n#KocIR9GT8oW
+?~4xX+s>c(iJ0!hv&<b(u#dGq#9o*DfnXA)+G~Vgg2E-39@qArF$}ro>}bDaF6ZI!B^{R9)Tn|jaS#Q
+y=nlfa2O;Une=dT^9*(jH4mq_o_dK*;(vA2{Z?J{^ZtczPA%*gMiUV)}2vj<FVg}L9m@yIsC*<*Ang$
+<X-2M*XLEBBlWSqV;j=+9X;2iD*vBZN^%Yc$EjS<kg^~MuLgRm2g-qTXMU;L6oZlpbAjuMBHxa?tGB8
+(`}=4ps0ZXcQ;p1_7#EHa+^!@!w-2l)V^*9hT_;7~NkhzKnLhmcw7j@Z5I^5;{bWLCFZg^1%K*Uksv?
+ZF==k5fCbZj;r*Q#(<;&VIti3Hcvr=kw5+n^I?Vr%w1GmcQq>{gUnjntP~gc^87M!HycPc&aR6UpPr{
+%pL@@iYtj$8b-PBg!3Ht{I*|mOn+_2!h;hmf+8Qp`0FsdCbIW;%Z0+k@4#w*1P>c_aGZC)?U$Ua+XUg
+QV{bafp;^MEE-~451=!$dAUx(t>^{&BhtaE9yvV(7qy3UY!;aJ*_^`{M8Wq*LOPq$qM-7SK>q%ydzmi
+d!AjEYj`+GQB|Mt{$9E$;juTL1`f`~6r^||wq2hkF)dV(vFt^s#FPCdW_jSZ|NGjJX|FVfw!ZmL=3SL
+X604#1g#mz;)`q|y$v1&4bZoJPFc0b9_ezK6g$cfWyso4f%|+PbtQ3;_>?m08Sc50&&CDpDJ%jw2X3K
+^iBa{1V{aJe3p<{i3Q*-7vyGq!vASTn>7<5%AOmt%4tv_2D*A(cwq!__+4I82BZS*q#TxGJGcB%`rBU
+C3HK5wG4l`^KiXGuqMencq+?cI&SkFsBh~@JRsfe$E5~9BnhhV#m;n9%_$Q<0`w<zVC(@-|EfNdL~lD
+Z<s~p8*kyO*r-*Y9`gqbuIvGYLIL67GqwP!$;b)wB7y7f{6Z>ty<it;09jPFaL`_3~&dzbLP~@z5zph
+(Yvp{%H@N@+8i^17O;MNZlUUKk$!(@tqqng-^q$!3|Nn%ws_E^i<yAkSvh!36iOB}NIML+muF!u|O=5
+V{>pAAgrfn&Pd<>wA2>2j(f5yil+B-(tZlX=5+&jY+<xrNO0hEiY*Xeh)6TfQ!dWs)f*HrgR`?VVNQb
+AkpuTRa*1?cXpDYLUQnyHFE{0R#=<I6@nudh<n)ZiBrLc;L99Xv>V~+;X<a;~cKIfC5@ihT;H(on}xU
+hdCIOgmR9jWQLju=EQ~!H_=lwdi$vznzp|~Xo{vEPbU=n5_;~$Ln)>sw7^vKU(shN`&Sff>%ucZx87<
+0r2T5WI6^m6YVT^^9-154bo3;k>Hwb%i2q<;4!7`A+X(g5C}T+MxBZe~hPK!jPjjj+R66Gb)kdDF{Ng
+E@lVfTPkEW7=*%t|dsO`7dZwqTl>|<>Nsn{O9VI{+Nhu|>+E_<jtT|96)X!ex)sVpSf{l=4ZD<==gtv
+(}24XCRQH&DK{7(%>5Xi^-*$bl#UpHa10Zq94ZZ;;2CN<kS|0ZU8Yh7~XUW!}#;Ji5r7XYm9ai-XMJ!
+FbzdXzA{L+b@}vfXt_<O~C-_E!p>>grc3v$DW6P9E$|nAL?XW$$k-XH3W>$<L(z;A{i&CwFF^Az`gAF
+;rM5!juC)$NIOwwC;lw;Eyr~lf{0WEk$b<jB(?ubkeXY5(UZU!(oo9;+JKFiEnURe4|8YBO~ez1>P=I
+H`KY^J>q}4wq<4n9_tGmvmGSw9=oOHCn&1%!1aUu%YHSWaG)A~HvHgc=zvLjb+3^}CYbVQaL<-AMvPH
+rIf=OJ-HjH>`u!G(Nd;|_pFfvRE-}WNS;~-eh17XKxM3a6WX!8SvH@5LgX2})wG3?pXEDv-7Wj)T@9w
+&YYxw+#sh`UUQATXwU_5c?xlYdy+%RdkZy32)Pg;1DFjjhkpJdT8nr}L608uKK@FxUf=cb@l%@Xu0Ah
+R<(thk{~#3HmH{-l^aI`O=%j>ICdJ0<fetcyYsY=M<_sp=b&n3w%;aY!-%coqB4QyWs9O;gg3S(BHtD
+g_xd3>`!*cMO?^>gQX`GIs-mBtzAe~gl*a{hef*Xe%miuyz{bd<<gwXWKlGDBZHXvKqo(_GCGjGNtif
+Pn>ZHh+&(5OY1fJM#_l*^Ff)fbUt-D&39M$1XjxSfroTgqpxY?-Jb7`vUR<*aP5j(-ApNO{y+BrBaBI
+IQ@aR%^5q6>aO0;vS9r4A&q&vPVUhKpxOxJhUfwaORkFz6H3N(ZZr&*+8&`k1l?R_*colI6&9X}45LK
+y|mvc=wSg<nF!!%6+aG%d)AuTzBpg(4v|8Q{ZNo#H?EuS6ax=--ji*$aBlFZCsa@~7s8XD)IkC=N!%7
+M44b2C~S6{K?$VJpBkH7&jZzKkxiSE3Eb>&-B-WRlpcg?PR9B#K-t&81{jtSP0+*Gd~7J5LTT9D@gjg
+|Jmcc$m0xa=^%x9IbQp<5BkMj0)w6m`oRW2Vwc!pf-uSxR{;IHKIw<6{gM;=(wjpYb;09(0^Z(~@M2_
+e0}-)7;&Z@j7Te?`&wCo9nM;V<zFqJV;;ix1^Vmge2Tw=6puiN5X~m%gpB&y(go)E-%@#U#v1Idk`wt
+hq1am&roN9_gXB8$3x?0fe!(`b7HDei1@oMe?;v=MjjcfR69;h!tbb8Mi2qw$JLLo_0W{D3pOlp;u(a
+xO*2*6X;f$T4L<Cfox`jUiiTNiZb=`)zeUM<Iqv8Bia?eQQ$7^by{X+A9eX&ounjM}CLMZ@iv%%=;$<
+IoC2Jr#1&%VDwhgsV6rhdH@votVz@fziTLKq96jn10(YnJ9||B3-9mPHA?L8cL%`KpbR+mS}&O_cB+k
+K25ZZIm}(VaQAE7>uGf?N+R^Y7^N1JNVUpM%7&p!9QmOxx-4_sqRYfnZ8mpYNbz*X7vg3Rztz0rP5iz
+=E#E_E@sQx4+;hK(sdncE>x18kh>z!|d4QJyIl2-4JwcR`WD)_VW0Ea6m(C2v>(KTig=CbbrE-a*paU
+>A2dA{8`xS;5FtOFh-H&Nn@M$FvYCg@}xqmF{^nobLr`r!_8D4VEdmivhT<5SpiZID6Av%1LFqv%d9t
+T4`&|)aMUB8JtOgK5nE)0_g=RLpFy`IPRmt7w8j`auhtNZ@k;txMN^=Q}^Q$cZWqe@ZW!<@>lYok_?d
+e355xLnLb5g}6Zcc3h%mCKBG2&kbM=S~<0mcTMQM}kwiHm5zQdmSZVkgb`?AU~tO?9MY~Nho$4N*6+G
+=|#cq(M5~E{XDEdsJmb4UU6$4$*Wl%z)qc1<IK~%yDMlR5@~9oxo=;(2htbp3WW}B7O&lJ`z0gQHc$7
+bouk7wEjz$O6O-t|Nt&)NJokL+W|I}d16+p2h>`tLUqa=1f1V{HS8?ioT=IBxFl;||BTRTK<9U<>4$d
+;>usrA7W`D06b+7X@Lu(-$P6X8KWYC8@m6+q85^NEQ^~jYLz!s%A5{{vQvFQGmGwNO^{@KB{BAi1Ip;
+lx=Ql|&EoZRbyQX7DbWrBrhWl|`(PV33=nk3BhOWiAF95dvvLs3Tw;;x0|z`}ccQXuyPpRCge+(apGa
+N@pO{(tj_le$;3aA+1b6W*+(2bb$`wM5WtE0WFp6^4Raqd~<Et<#l$Y7@4cQTIAdqhg|kIMrB83qg@N
+4V;<BE)kYOvHirs*thvqZHMEvV&Pslc*zu_Uou?E9CT&~|0KaWp;i%KP!AOIz^$1W*@c63n)t-R%-H7
+x&{!`6$7Ku?UfUZ0_^JlNWj7B=MV{JjScrs*)^%|w@mK3W=DPbmv2R0-#k-(Nmijtvj^pOGhE5<4Q`Y
+fRJExyxes^2Mb8}iM?RvjqUrZQNBYhBnOo}cim6S{804Hg{wR`yNf#o}!4bhn*NpE~HwmqiqwLK*lz(
+#h-`=Rj8{X1fmr4aiVUR))NK`YI;+78)^^X|9(lBF^pWb;sEleEJF6*Z|4I^1AW5`daDj-9fZ!-#+!G
+5Kp%*}3yWr**HyPbXS$QVY7!Vgvu|6eL@~m1cZB)BwgeQEPNZLCXNC41)i4ZXQLdnejqh5!lC}WLPMz
+oka;Mx|A(A7)l>bY6*uY6;6+6ER?khv~u^`e#t?y=&%ytFxTc`5W?kIR#>=3-~f~`ym4CFxp#C)+O0>
+2SnT-Xr0%uDN~>V-wiNYCs-oY=8eN`JVKWiBiM{Hu)`GGH00N%kW4YhvJrC`dOz{ED5BV9ga$1OqA9G
+?xAAsS5`C6wj9pL-vmaux&LGTe=Nc(NSWKx^)m=@Uc;>k0Mtk9*7QBN{dk}jvwXj!Lb#=|-_643bT=k
+t~a>Runi+^b0hY!pQvgJmCR$1#*-7X@N~3OT`K=OGh5t^M(@I0d&~GR@;)hkz`Tmd8G=vTKF#iLqrM_
+(@A#84M3w=kgzYFjb_`anCPxujlXx3WCUD_o9GkjpXoE*bQs!vO`arm{=wt*Q)7p?0PQi3GRNWd-d!9
+d~%JgB8`e!g<@+C|6uBZ^G>B-A|b>d7;a(6BkPNsWWVwfroaj)osOr;X(9q^@N3VT3ssB~T*%lQG$ON
+xnM<PmYx6S@6LLmfrsa%ClIn#`x6I5wMU$6hPy{c#b7E|>8z-(IIjg`2T08%CQulg>b$Hfh;Hf#6l&e
+0d5u8>jn|QQZ9ATeik`JqlcBRQFZ^V7S<$=1_gMj$NMLdB*4}D{Zf4hi?!c(L{A(CgdhLxXo?t%gb-D
+>IR_B>GcdKlwUOnOqPG14IvUcCs0g?cI@X?Yc&8_t(o0`JX|Z8z53`+~aH)A&u4`P}6+EIa!RZ);J9J
+UtPzbU6X)hkf7;ne3H)5z~#4_jvJ3hHHBGrp66}|3t&8Tp1CyLMPU59F_ns9YW$aLrvl9<@4`;7krXs
+w@S@qiejq$hMSMHMo<GGb0>RaJq*#xLA-F{VXq<AxYv!k*R=zc%TXd?L{g-V#}JmPKoNi5fmAE`G|$P
+wHKqZ=U|@Q0TmMq`dX$B^hsp|70(&xqe40bV6f?FpJSQPn!NG!hEGl*fMU;bBbv|$Zfx6epMmPnic;5
+%xWDsOfokmNn-?Vekvo)?%3RqS6ASpqLY&?C}&CkfY_dce8%Su>^eufJAxu1?-O<ZP4JW}wWy5~Hs@F
+4ZaxzGt_?!W`8w7KVL-77TtL2l)dmT-EUk}xr;yp*6{=Jl2@!X9M9AaTRvOt=T9N9y91U+P|uVfOFxA
+SPbq>G_t$yfLe%Z^~0Wk2xK_|M)EM2{K)7|6T9NLbV-|ac=#G1F5?oR!G|10s1>^FSXIi)KmSiv)o4I
+gv&Q4pzd`d<qJpaw4BhExKJ}Zk@EBy0uqX`A7*)=sDo{aEgxGvhuUEKebl|4PxCX%uR47A6G10?g+ZS
+~9W(3=fIxnTxO<TnUFPY^3eDR;EBz9DfK9h#O3P}0jJ;_RfD&1rQ=&(Juq>QH;?AWGV6`mze|{P-c^u
+UDf-yeK-x|PA59px_hHNoVjNyV3)hzuITI^&8YfjiVZh4^Y^$}aSYIyLO2(MXyiQhHVu(eAvl@a!v9y
+Xhf30<<5G}znx?4<5>jV;#5gP7rDk|$4KS9*4r!yrpFOZDoB4S8r^JvDmC>WnWo2M4#boyT8C(JWNiV
+vVNmFf~?lc;t)pi>lOuGiqGdPIQU1m(z`i@4Wv>-Rl579CAF*VGq}N5$|EOFUu!%wan4t7<>GPnzWOW
+*T;0KJp8cFQBic>_L%)?E&ipLdh|m1A=)N%8`c?;)}Ed=3)$YOz*^uBLiEt~?N?j(N|pUFEx0v7Xo&#
++4kr*6xMz9@?&*#~d$6=?$k!1gi&w_BHG3aZ_qv8#lVAsN8hFBw<-y7=Vz`n-a%qg@W@eY~QuJ~&D?I
+Gbb@xksiDyx*B*(}cqQOHDM=h-920enp;fe#&bHZd#@|DM7JtPsIRUh~HmXo^ILoP>VX;9sGoT>{ek4
+XNQS0f=bjy-54g9!?!TZRtqVK~?G9`EJ4;R)($r1feN8E6qPD`^KnCM9?l6{r)fg;lLs$bpe{2~s(OC
+$>IIHUfy&Os71^LY0l|5l*(`5+B1B7XihAS2oH+aQnju#m*NF>s~Fv;F#usl^qzFgDULfR*bBhXZ68I
+R`5MSBY;y$6oh#8s(3E-x%<U0!Dm@u;U)He;4_7geb;_lKZ*Mhj@;0k$kUZ}PoXv~Tf4dEu<o@iJy0Y
+KbV+r~4m=)7pnrzD;gC}Ziu@eU(mfa^9Fh24&o!ufJ&A%Q`r#qO5-AV>9USi+sWvAoWMv}o^FgiTf&P
+6ME0)=q(m>Vj7r%skvGCZ|KBa+EC*d_|h-x@J3}`NM+fw55u`|>Re>;`*vN<ADyXB0SRWrh=fa!S#4E
+9mhP#9i&Qq@Cn?DEusoE}hNZOVVZyIY?P>R$Uz5o<Qx!}{@4@yBP{4|KI`$<_fgd-x%oFB8B9*w%PG@
+A1M*2y{<S>bZ;&Tx@C#s3#c9phVrNUj$Yh9ty%x*A8KcO$-q3b))X}+?fY`P4mZ8lQGF_=m8QD?2zAc
+tV6hQF~7JN<S?HW<d*Zb*Uh?DUg*R;4Jh&0RX*|(Wm*RSFaU{N<dl1Uk#lY7c9DU2=AHI^3WK`WMST4
+542Ko7J2Jr$n4&Xv21e2HcQDSW@}UNQK0S_Sjkx6)`@V`n-Roes9#qJjhaq|ibUYD=AvvY`?ClFHY~h
+lJ3<(R?_Kw-_mo#PT2kKr=Y3?@ch8CCM3HUD+Q<hz-FY}?JV^`!c$r44*e|WCLv=aStPS^@tPbNaV&c
+n*Ua%@gA(Z^{cVkD1yPwTT-)$&9ag&9zRvGcKmy4M51wyX)WJWmj36n*Y(%}i@U@bTbQCoaZR6mZB6&
+sxJV*WCHoLEY<#ayu-&H8c%R6@rhE@<lo2Vg;u)#6)+K+Lv=zojr~7^p)7Fb<Z#7aiAZADy}s>BIXf(
+N{tMN`TPTjHgTj+Ho^ejFJ^>75+Yv-658v`-7n{H%t42z;jJz5(CIYV#kaBmQV6i^CaE_=Sj^qisxvD
+i$>1K_ZsK^qoQG*H*3Dfuy+Fom1vEiBQtsWm&($Fw9AjBj^bgV=Gqz+hzn}VTzvLA5&%@FZb~JgEsfs
+-O$u2QQ9=T1QPe4AET6Y=wSX{X0=GDWxS5E*u_k}wS#+)uu69MD)3Xz{_slVsGsA=>-qcF(YU2NDK=i
+M*oajt!A7A=AcQ(KYpgwxJ_Q%uld-qiA1*e%dxA5I0cOhtU3Cxb)7qcBg0%VQxV5d`@p(mD(J-Ys1MJ
+yC>>D2&X+k<KBmA<X}{&q***y)TS$DQ<Y0+%pw06?$i|MVHuT;NcFIeG>buX=Y%0F3KB*2$c4DkwM+-
+Fo;>EKeSdl!NVo?{XB{0JWzSR93ZI(Tuun{nyPek{oLofhjp*R(=wL7E89?Y4)L6X7KiGa(%<0?N|Xo
+>ue>R#Qd!~Vbp`lJ_dHnlYSCqx3PnuzickL&{~j9xvpk4^&!?thA1To+vIIJ?66SrM0{02Lj8nJ9i=d
+{WPS|sKc@oCJLJFZO``DUm&-)X~A2{K|v%`2pYmR$<&EpWQSjc9HuKJgnleLqa(qvTA+T}6Ms(znR;I
+!BQW<T!vE%uuiB^hm#TjuP*INV?)b10P<<xx}jG1}3}PgKa_1n!!~Z>p;ucVi=4?^vAfsi@(Z8j5wpQ
+_{*5Rh~v4lqgtnZkQ<H_NEw4pPXrA|GfL9z6AH`ap@QmZ+cj4njt1U+EpTW!W<x*7U&68!}!8tVcbPU
+hJ>p94%f}Wz_*`vWK4xOfOVfb`ECT$e8iSZE_WJ*u?{%F9v!{*{V;>N*GbqsJl;y3!qR=FiuU2eUmJ!
+{ZU+ye6!CHCL#v;xZSp7?7AbFkox0aGO|*L~<1m@xkxblYlj)cDDd6Z?u{AyTjafpESk&bWRm-Sm$H$
+Z%b{y-`LUs>;Vx&9OdLQelQV)exp<2}IL_&+?J3t+tR=B~#?ta7jOE{nPRJt%Sxls_3eEKX-?x7@>7S
+|0%?$?AorKyA1dU`T)ZGMLBeL1W_U*0hw-ZZj4TsP-|ok#H%&|+@>fnW0Qq`<C{_6@Nx`#k!v?iB(#@
+E<56gu0?YtSa$Buw7YTRVwC2WetS3GKPp`5>QQ}girRuy4R2iP^uOREGLnto&=H9+|%gqoPB>{roA|Z
+Xvu4zrm__GJdnr1L0;3k*V9AGo>Sl?+G%MjONME)B+08i9#koNOrNOyAiJ>kWW<&;>Ry+4oFl@Ee-4T
+^j>Do>(vrd(uE03bECk{GcC)=;#S;Yp1YOy6$8XfVKD>6$TY@g(9EF%<!VU&FLW>a2pwfaq!{O$*)`<
+stEdS9Q-2GDbx;U8dFtjL(0)Y?&8NEijxvn!JmbUb#;4#?k!#LIwb1nYXlhnN~%Yw0yhbNy135S&sR-
+p{ngXoTA<TOp;wP7CYoE8SdgpC#R*1y!fo>Qe_N%62HX4lGs)7^_lMTU=D{KRM-USVKyrqgE%AN6(|H
+z(M2ub%%!b8g~Kd`uj0ramT4=+leF3}%OUd~0Eg$FST$M|kyo?z+rD-Rp^=THZBAG<d>J5AV_1R0qC-
+H!`W9K2+bb>1hPD-_^y?Te#<e`Vz|qrl;(;9df)Zd#SYbj_LPsmfVB>C;Lqw3_*SF1jv>L>RumKag`k
+rSx<vm5qN692?o6KX2)4*1^r}fnV0Q3H-~y}*BcG%UiWPSo@nys6L$}4s4R9-ekmwq$=396YAK573~i
+&5Nn{k`9&h_46DoNcoQaRUWQY|%D8)Xfj>xo@&+G;y7&$jfSo`K8d}4X^r+)EEKy4>!tD*X&HtzH=9v
+;m+43zde;ALU2kb*PzMJ()h&mNimy!|NZUWaE1A<~25y)`WZz*n$J7&rSImJ#A1xS7o^59|BzAMhEM#
+kcE@v#5K0l!d0J6D95@&`qm408oaXKohbzQ;d%kjKd2x%p8_oQYJLEUrpWXNlJK>JZN9yhc~jkt_9I0
+nL~`futU?kxXfDY5-~F0)DJD9+<DyNrSA2yjm(l)YdLflSuLVAYM2)x-lSG&4ok+D@8v~JR3e{#r1)p
+=3+i6a^n8mKJ^<$|ZkYS&K9E2gC&D7tWo;)GiJLR#kT8?ZSe7H-ekygZ)05ex7${rd1Kv7@b0X`N4<b
+L8SAm=i3!jN)JS_FMf(DDftVX3@>R$1o&(o9PQyE01pH8xh1|w%3lVBE4X;^jfv<k+N1phcy_qvhC8E
+Ve$krN7`F35aODlcbB`Mg1tuwZ(>(Q?p;QEH5kOnkQc+zbb3d%U<cyvs>#(I6FiT>E|&ug){is{ml`_
+KO?OlMbZX;IOj6GXtA@Qls7Rkh3cI(2SAaWaq8r!C93RI?>w`mGclThad5kP)LWlu-yGpU&4b2$CnT5
+ye)!Q`<670V4HmM1{j!6sm0&~+_JSe6+1lBM7h+w>+q&^uTS&XM!OOY)Q|^S@G|CMu;o=Y9DO53Iyra
+t5Yqv}*V1t`OZmL#mpsn$#5e4eg1(caz#-x~U7k}N&b0zhufgTfJICZuA%x}35x4ApF|B)5!+L9zM^+
+fjDZ27Lv1=a#@PMiHo}5lrh@rlkaSqg+I!t#R2+k#TQ_0h>W(qVn5;DN$PQ4Kk;CURLB4+jnQ#+}gTl
+yZuU~kvMOzK`oI={?=wGkE`do;>2ol@_R*1@VRHuJEcC9HPc)8}<;_$9aeTK7ssH-~4jw7sXN(@5`Q=
+e=}=619V>tT}Y%%!boZnDRp60n;-#KbzFOo|JR1rGlQy6#LT&^+s2A4b1X5_GA38lBx1xRlT;yJpQ&9
+le*W%<qFGtKFFpm3#d#)<K;}ryu}Ja3my4m|Kmd8gBjx!i000lPVakNo=KxXf}mvXW5(&T+AH%SpN9z
+{^r`v3<wc8lR7<CT&bQH6_xxI4;!zl*1$K}3&Z550wEh=Aio%*nM&+FQq9z<6d{S#+t>&Om@|-(A&yA
+QJXL(7$>Dau1mW=D=@Ua(%?}x&Nwvu06-oT*0ZY2gF=LoYP$KCJAJ-u4orGo>o^2)kY3J`2m^<#JG6a
+YwE2lL{d7^YIzwDa2asoWXxa^RPcDdMA~q=!}ZnteJ_F{-;Lb@Fb^eR|a;pq}^Kmv>;OciheS$=2(8Z
+1A(8lw)|<Z)BSu)}mfoW@?KpH-R86Fvo<(EyB-b$rnih`=!3b+AYhQtZA_<BTbGR{-@K37M)@}LI7e3
+t81=(2{DH3P^z|^QTKYz{VAeBcZlMj>TqVzc_8Tw4Lqkd(o3NTB3X>ZY1G9*)S0*5vF;Vk^1xaxPI*p
+g4a9a5Odgk0s|JE7;!!P6LND(N@=9ZGV8Di5`<x}%oj6N!p<qq=HDQ3jf&;Lnr{z_G!^<$jTa~EWUOQ
+f3B;F#i^DV#|?iE(+Vr(tPJuUbm|3+Z9m3DdA9ooZlzrtmLUQg+*E$le*$2||^aXfe6t>A~E!+})Krd
+SXQ@EF@@s4PtlIaE)Jd-IjYPg9+|?FDtOC)w+18UEOFPtbW;>}cl2CBvmAh|+!b0KCN5&>}pVkox-DZ
+c_KUq(GDlodlTYX|RLF@=hmH*i$DI4X|&Ikqa_F)CH%4c{G-5*JV!YUO^f&ExI(+G&7psyzRx1{*}jY
+Y{6T$$dwSI^=oQ}_Eo-I8FK3f>RuPqe(F|GWpo(e?fXEyQUdQ-9`F6Q;NwZYa(Sskdiy3`w;*!ud%q@
+iuji(c)_Fq*rV311#fiX>OcR=Ecn^dKGe4>jwZeW3xt(3NGO2q#DMNb1zLmufV27kH@K^CFM}bcG-P-
+34PN$`%mS&GIHAIp#c7AhG_j-^Vo)*^d`9*R1Nu~9&bUeKm5Sv3<a9Q7tLq^RkzvDC%wgzYF%;R1+&I
+up7a?-<TZI5+CUcb$@-j|9w7=Xv7od_1#0IRk<&G$5avd>>k>Rt!+b;AodxTod;SzQ)6TW?7gcHX~py
+lq!P*M;krEjsdrL9Q!qd7$ofkXxDX$<rIMvDJ&T!$_~9L%N=pmO$6iL;*1<#&Ag<z32v_xnkdE!!@O?
+*ZtA~vH*mO=jqL)Qc2Y2_NF<Na4#N#>rfOK1WqN8@<Eh4$L;S>_lncQ*vPx5XXs3(bHk3Mzn1WuVbi_
+y)-&R)5Fl{MbnNHruWvb9p2xWq1y7lJ89h?t8Bh-8fJj&9_$$*Sd&4W`*@$=Am#MW5`qMnXOOViIzh3
+)6(q)jm9NwiQnFofS7NwkKxixLx8H0~2bNA-VyN;hT#4n4OYKag5hYQL#O$s2Z%ffQs-cuqN%G2X;@Z
+6am<}$ItaE_fX<lNrN^!o;KrvQ;3FS0Bz7z7|-AoOs<vShc8$`Up2ow)>^r?=Oxo$uzXLywnu$mmfa@
+?NU7e{!*TKf^gJC^CIM-6pt(q7u*75X(?+wflvanBv7ToIur+q~RdfcY0;Lr^*THPobvjjSygZPmlhu
+7ryPI&vWZ_>Runh2u8H<)RrX`oUWOkskh8+X+gb)KtMvO5}#sl2g~sGq055UeV&YL`^&?^8r2qeKpi-
+|%UW&;8JX|XDjbg}A9^ii;hk4#;UVqs+CZ7y{-kxUmmA$<agY<iC~9`;;CP)L+GBd*&*f#H`yi^}nRb
+P;UBbTm{xi_)%kW}GPc#vp%=9if{E}&o-+MT5PFx;J#)seGQJxgI920pFZ;9LdfiQ`6b1>cygD>W>!0
+@XX93U=<AjKO$j9J+i$9^xD(ONLvnOmPx_j+(rX?f;@kY##hhhead{2GXT%$PStV;;v={XUSDRs$|iA
+Kv5TzE|&lK8)Y245#)mWR`wS!08|8Xdjuxx?9u$c_h8IC}|s>nVq-4L*45M$r{5NCr+7$ipI^cpMch1
+nrv6xn}f_A`I>lYc8<35RvP*(?!!|Z5ftq!>w$A5iRLu6W%C1B9@J5d%L~4-Lzl(3z*jc-@>>XCBJ@k
+$>+mR~H=MD(r}u+T&5hsGaCmVn2-EZB9BUF)d)NE6Z!QoCg}csXQun%Wx5wO9!}8$NU+$+{KyVpei!j
+q|f=(RX_ey@1*jA^@@`C&Rok`v6vF$AGiq}_q3GeWRKJt*bI)gHnd3d9sMfbv>aO8yH67FH=+g_Bs1n
+2Tn@2JQvs+AoOav$Dvx-VbE8xcU6Avaw={qhpX)c!h-ZQrbWjs5oQ0TV*bR$58!l~5jxbIF+V^snqoL
+ZJ|l6Okav<NUO4+^a?<!V0~NX%!$KL-Xtd%daHBHS>Et$l3etIiW(*cU6Hi@tOBL@clT{Fs#_0r|N58
+9*U|yRV8fj;VBhF(R1T|Uq^aSQu!rDb2r}hg1Xmp7fkAF63LOIAD8x!d_bBM@RmcbL>WF14McL<M7ZY
+@yka%>eRz|)*CE#Pu<(?pUb@e|vtMYUjNsrnUx0dR7rc^iZMNxsFWy6c_X{t1>b-bLt;hXb9uoi(TQf
+RZ&|Q;@|6-8~&XmD`;D==MIc~j9-Rof99-1ug5pbi6O)dWkJt_}_Oy_~%UwL#3`~4=QJ;-LRxb*{duY
+=^k;WrOh95g*rM{bf>Vwjb(0@lJiFI8YxSE?)tqcF15;4q)}c)3?iPoF?xc{APVeJlGrb5Qf)K4Pm2m
+yuP$C8}xdi?GVJ{-b_a*1hJI?TcR{0AxrwAUC~VZr}0^IZ=r4L6%p;l(!s0MZK2(u!7}Nzwi<YXix8Y
+*k29<(Gw|qGk<6gy~fkmr95zZZ`dI>3>0jl*H>=6&bL{-oJyC(2S;}KEk%A+$K{<ovT@(W3kHNM!vnc
+&$@2O56qh$J+~b9pq}Tahet_-jfpzz>C%eKC`7Cw_`F4p#N_IVT%48hdZr<Z1kAo^ZfMe4$C!YVcgve
+qH>KBjuD}vPjSXC2P)7~)DLTDA&xX0Um$r3)UJ#T<f4=9H-h~&H2)=+o_lJ|HK5_)mV)CXcWdVAv7->
+bnTxGx8G;NkUj7VBCpV6m7-NIhUGx%S&Jyb_Sua>3M=D9n5QF26qMy8Go`HID!q%#vJNxwFC055soPG
+IAgQ6m{Yc9B{T}K`Kt#vS4U=so1A}y}!g+HD>i*GtX*zV2J#i>OugO`^(RG^zcp|{NKy3Um(cw?vZ_u
+_uBWpx=FP|7HEJJmi!ff;CYSmq9#ctY5?@H(%DyxG;JQV*nSjO*7bN5PO5y;d-l=(>0*2C0QF*!?2YB
+#`tI?>%a`-ikmrd_oot@UO=Ru&VGN*F6S+JM|Ae~+R^E?jnVHlMnHroQU3kpX+p{FQyApZxKCWrv9H3
+wL<9<Vk#SvcPA=|tB2XQh05=*;`mLE-@iU!1FUt6DXCr-zk%cnDC(=S|OiHUa8#ddFV4*S?o=s}moq1
+HsG70mT{x_;*axCn)FYN|s%wNs1U_T@=gE6k8pA5A=_wKg-$$)IKMMASCIn@RGX2hQU-zqP+Q3&^8_*
+8hF_O&^Y&7bngluv)3=wGITEun%KH*K*&x$jt(6H<`RrcNpLi3e~RGwm0u#!+4gx@zk&n+*_8myr`7w
+p1b~m1M4rd{0-g!)OS+eHT{wVj}%k8H4oikn3i6S4vrVTm*pdxC1S<goR&kwy^ozn#Z+UIPM%*%r4)E
+!3ah%mFAqbqlsG;Nijpx{pPHM)aES5carqrs9)OW6cu%Ct-Akc8edvq(4KdR%)^b{aV#CSs=6av7$2&
+YHOa_tj>(k3PmBz~xc`CK?34YO}o%=RSJpMy@dknF-y5+h32aZd%ed9Me*L1$NDj~4)8Vww*r4NM5cY
+1ioFB=R6TMg)z6q4@qn;fEfIZv;^v~rEG(lRV7k03I0?pbohO$%zWonOc6P<bVF@@?Nn#^dp$Z(o1^(
+XS%<(J%h}&4<5!{L|Za|MvL9n{VI$g)ioP&o{|F-oE?z=EIx*9A9bs&6|&JzV>@Yzx(0CckjP{!<XS8
+$XIa`Yv~bgV{Qva$h5OdC-!FfWuFoR?<}RlXqIsSp2eNa<QM^8R_OFn#DLoM@Qrefj+#)_Wtp%yB_Yb
+oVnXC@xm>K&^7U&w{S<J&EFQOZbDTHg@kJoHi24@p)>2};3Z1LSmS4$%Jzrd;wYVZ`aoT>>E;ge=xck
+MdP*=IET~vvivm_y~jEcApaB+>zqT*t4gW2M0o5jrx%j;WERjY2}OY!XDr0eqDE)H2O?<Dao4rR29I@
+?8g<FZhef|x}_ybM}ZZGM*!G54z5n0aZeh4dbl*8-vu7C3A{u@+2WvF8^Rtjn*g;1JZ}2*Tn_((*(LA
+?>Q2te`lo`$YvH^^1cm%X^nN>ajR|u)M3s8zQLQUKV77yq0A{6xA$t(Sp{lJNZg&QuoUS!MrXs?o!<d
+J&5WT@%SS8UPKBDFLYsBmRB373fUctoawr5r_@Vh$$~A?Udv>cM~wZd4?K}2IfNc9FM9S~ZGg$LVjUE
+*4EKrZ*L`Spd1)+R^dj6{9wxM`!$8u=icn(_7c9MCU0l4|r9+8>xX^wJ&uv}%5Gd*H?iX1>_bru-_ZW
+MtqFacn+64()6o)J>o>`RMFYg}qYF!J$tyH=8m$H$B?S7FJZZcb5zh(uksW6=Ag;iW!=de7}#wp6hdB
+4Tsg+&?dqJ(={vUcqv!DaC*`K5)kyL8?#xl5uWHPJ8kdTFgaHfX7ZDePUijEf49C33s!UN@3KdWIS#z
+HDM~55V#}6uiybuL$r~d=C6CPGc`hd;`!FT<Z}O&jxiN@!W@c$YHz$mn{S*@q<guf{Vq1OL2mFw}8$C
+5Ft1)7o>E8nw0=V2Z%TDj)HJIo*Tje>$~5RmM1MwTAs8#X?fD}q~%G=la_;L4O$FZ3|b6Y3|j0WD>!f
+WJ7_s*IcPa)IcYg*IcYg*IcYh0)}+Ox#iYfe#iGUSJW@OxoQV(WzJsEy;LLb%5IZ=S6dXJV$}9sS8I(
+8#a53<Ng5vX_G(4zMjpv3>5U%bwXqmK3S|%-%mPyN`WzsTfnLI0LQM4#p6fKGtb+22oZbi$YWzn)|S+
+u-pdC~Hs<weVjXI-?oXmQcvq6IQ?JnwaD=O(Ck3%F$fgo2u~pdKtJnF@+^f)bXXrXoPhL8KD+*@6EY)
+E5NB0CDVO1<`N!i>#1i4Kk$xunmB9kkty3R{_HdAV%Q22VQ7^x&ss(AejK7#C_>J@l^MVtblF<f*BCO
+0N(}FEx>F6HVd#>z^4NI5=84kkRJs3LC_jEq$fz|evuV|yCC!mqMaZ<3B3Hkbq~DNz=I5YzQD-~JgmT
+D2^@gHTE}rayA;m`XX1mbZ&2JBoc#@sWCX=tL5euYgakookO&Nt)j^SMP^ub~ss;oq?k`csAMJjT6-r
+2gbbr8cf=D6oz5+BAV5oo;1>h!#;DZo62uFjkF>p5GxbAhkST|&a3vh#Ln1Xw2gIrQ@{xUdc9ONznG!
+>M{2X18Gz6Ax5L77uf>=5^ty5h6E1QB6yfnIP*H8?vNToM{wml4#z2PI>HuO8f66PAhsZ-Z+b0#q54`
+Nz4FO!zF_F9Ji^L6GhblIB4MDM;!D*G~j@Is}{{III~Q(+;q6a1=PG91RZG#JMj!!)NJ!5g4NCpae9?
+;0KA}Ai)^m&Y)&HD2@*5FM~S5Ad4Fmn+4ThL0MLu`%)ZymhKmUAq5}g`+|f{kQfOv76G^qU~51vf><w
+3rvO3WqK4prcW@a&oOh5y@oZ4gAC$HRMWaD6Xiy9qRNe)t?;vp+P_*Fq{w&u4X`yO403bnef1JDc5TB
+*{MQSK>gst-veIPniJOp?@z?cCK2t4`VKAzyHVZet1)Dy6p82iu(KFdpRZ$WVIDyS?EDsF?i*`Pvg`W
+2setU=~H$Y=)HoPbCMm?k*R9K>C-_GJ(FEZr}XM5;VEQ5c-Y3r^z&r`v+VFTpvMpd3Dc^g#(<5NZTyK
+7i8!H;u6mP2sb2zX%cO)gZ$%%O@a7qzD3J8j!dE6a-Of5S;~~LQwD&lqv=F)-mQi-u6p^ymnBb7F1dV
+bv;3{BB;L!=wpBsgLo>aTn&ozg3^?rbR(#k49W^)#67?5mjv~BK|x-Syb5Z{f&!@^fg6+*1?50N{ZCN
+96I9j&WhDXA4(dpPN{Sf!GDLiq?iWd-)FLQ^2+AIUl7XN+AV}>8>D?e{AEf1joO_Te50c<P!aGQF$Jm
+!p;j?tV$PX#vAPpO2V1s;WkjxBHjX|0)NcIKEzJR?3`Jynd!wIq=a}#sx9ekFTAT1N5TY}_Dz)}PJ9Y
+E)R9|t%$V6*{y4QOdVL<7(^iD3hL8S}iyi(djp3|LzL%L4Qjps#?M1VAc4P60g%P(TpK20?4=E*&q@G
+PmBrXX$>C8jg$w_2j`BvEV#daNH{>xC{y_g3`pGz%MMuDu<8Aa11Co1{C*~{Nl58zlaX`zJO&0)v7@S
+U{Hn<q_l$sQIJvz@+3hnAb`k092*>s3CMfQowUJc>3$I%&b@@kS}wmH&GDxH&<8@~g;(A*vtJxB;t)w
+t2_L|bIp@7@_$8bR2@ZG!3F?5b1y#{OU2ss>8I*qnl@~$cG(3G!t{@5t#ud<lSli#hXL$*Z2Lu`UprA
+h}&<_gqgF5`6u0CL%LFIf<>mJmz2Nml<ZFf+q9n_h{+WrncOZSURLG}ZR6)>iNqXYmU-~>U!E~xGe3L
+S&`wV(hks4feNv4YHbtnKgMvvj|R6Djy0OC2P|f*e(l<O#AgL7F8<mIMerAeRA$446^CRD$GLtnKgMv
+vj|R6SO~IZ9$=GfT4l{(V##yOjH4%!Y{}z-vKp5*=A6u8PsXU{Us0hEZr|MMU`eyR~eL41|^k26=keV
+k7$lyQT!Nwl}b4`NEandK|MsQyyq9cgdBE|J_&$$kTDK&0RaFGzmuSxTE|=d1m*V1=l!2_fGn;3WeoT
+%-7hjl<y}xF7nHgMg>6BNOOUP%Xn#;G78HdArC>n;SinJpBCnuQCSuz++?AK0_A02V3d*R0GOD1ADyX
+0eE2tnX0a^;Gq=FKt33v&rpMs=ZkWh;_?(yQ6P%0GE1_hNsLAgkPn!{5gxSkRAYez^I^*lipPf%VH<m
+-d#nTT!Qa92pS@%iBpXoQ?WvVwY+pl~H9TnVaDBCG|pc!BArRwSqp2`WUwgFf_&UlR6<LfB=K2!+S-%
+V8mVUMpV?5CNn{At*%%^6Eh*JV+%+ta~2tOUPaZIl!<Kp?o!DFPs7&gX}?210oub%g~?n<_mrDQ1<Tq
+X7MZ_A;_Eune!lD9&-P3%8){G<Kfqk=+eV&12P+=xre?p>7$4pzu}Gac#%Pp+d*<WNJ0l0+aMPj<duU
+IaggW@a<@T3Hb}mP=TG5m5q7?luXrObL1s0`tOohiAb}dBOM_f!kOmDhpg{&S$ae+_&LE>1q$z_8Wsr
+W1JBf!Q()}WXWEq3BVvqw2GJiojFGxoO$+jRz7Ual+d{~eV3ld*Jx+_R+#a&E|BhvjMek8BLOGvPNL8
+2<iQ3c7EAgdH)m4c*EkTeSNMUj3OIWvbd=RLppC1hU$)E#74f(%QLq6m^JL2@NXt^_%hARQ8<LxTKAk
+kklL89^!|avz4p5$S$0l%zC*97d3x2yzlZP9n%j1WAY>?+_#vf}}!_R0vWDK`J3gAw=%Wt?)s*UxbK^
+L69N{)4>LZqzGa&e&3l5rUC=p2jD(n_5rRBAbsR@kC(&7#+G3C0J#UqJwWaOBo82Y0LcT89iZm`S_f=
+8fY1Si4$yPdJ~V~<@e<JIfIbKGIiSx`y6P+*2s%K|0eYVLFUA)H4O34Bmp*jqZ+{)nqx(gs^j<XG8X1
+j&+FTW>PE;$YGt?CBcQ$UgFWnrqi{SA$&}V!enSw|ML^>eS0g(=fbU>s7A{`LvC<i^=>NG=^%3kI@Ui
+=cUbbzG;EFEC!D8KA-|3n`M7Hm3T(*c_f*mS_81123X>8Q;K@GZJu1PeADu<3wJ2W&cE(*c_f*mS_A1
+2!G7>3~hgzKO~OMXG9F=8137{UTie>hO9T{YfZwK&b;t9Z>3kQU{bepwt1S4k&d%sRKG4cXAuwqWeXr
+pwt1S4k&d%sRK$KQ0jnE2b4NY@d-RY)d8vwP<4Q+<9goX#V-L>2dFwg)d8vwP<4Q+15_QL>Ht*-s5(H
+^0jdsAb%3e^R2|nYa>bA6evu?l^;90<)d8;#cy++51702Q>VQ`VygII({H3kUC=aV$lz_v~{UTEk?0{
+TPU3KtvfUg659pLM@teBjBn2z@6_)i21TN>BqlQ<0BFX9Bt4p?@;vIB-4FzkR~2Mjx4*a5>17<Rz01B
+M+i?0{j%wfQ7&LidYIT`mPO1<MW?cEGR$h8-~MfMEv=J7CxW!wwjBz_0^`9rxuF_yyfBk_5{RSa!g&1
+BM+i?0{hh3_D=h0mB~Q^ARIJb^x;DGBLf2?lAw|Z_89dwG*nHQ0;;5nxO3jZ6`=OLD~t@PLOtjv=gMA
+AngQc=l!Ki82y(7Z6|0uLE8!1PSAFOwiC3SAngQcCrCR%+6mH5kamK!6QrHb^B!;eB?;P|=$M3WCwx2
+M+X>%J_;$j#6Rw?}q(c`aa66wi^FJre?zoWR*#vSYkUN3g3FJ;7cRrnrMaxCYMa#tl7cCYo7A+Pn7A<
+Z+isIP>awm{Gf!qn?P9S#zxf95pK<)%`CvZETo@*-pnlwwACC%=*kmA{dbSI=cA>9e-PDpopJfVEQqI
+vlf&5NhnZ_%u1Ry4c)b&6*b+@0X=1a~L6JHg!v?oM!bg1Zyko#5^ScPE%T!Q2VvPB3@gUy6wle@Sq6g
+1Zyko#5^ScPF?z!QBb&PH=aEyA#}<VD1ESPeSvAb?052jOp%v5h9p(!n_maoiOi&c_+*}VcrSzPMCMX
+yc6b~u<nF)C#*Z~!{=D-?icxic_+*}VcrSzPMCLkv<uUgfbRr+C*V5)-wF6mfOj4adLOF9R(HS158yi
+i-wF6mz;^<^6Y!mY?*x1&;5z}|3HVN;kHo`Ioa;Px(I{rR`$c|mP7*tifbRr+C*V5)-wF6mz;^<^6Y!
+mY?*x1&z&iondF)FIG0xpDQUmx-z;^<^^RQ4$C*LO&eIPd=@C1P;2s}aH2?9?Lcpf*8qIfoO;}e^jAn
+*i%CkQ-2;0Xdx5O{*X69k?h@C1P;2s}aH2?Ec%7z$JTk|6K|fhPz&LEs4jPY`&5z!L<XAn*i%C-gg^-
+wFLr=yyWB^St#A#j^?hPUv?+zZ3eM(C>tPC-gg^-wFLr=yyWD6Z)Od?}UCQ)H}~zw2uMqevumJcS64t
+`km14gnlRVJE7kR{Z8n2LcbIGozU-uekasB&)cu2cs8Nm3H?s!cS64t`km14gnlRVJE7kR{Z8OfLcbI
+GozU-udgpoj)fCSr^gE&73H?s!cS64t`km14gnlRBI|1Jb_)frg0=^UAodEAV_m}BmXS-iy1?HW8kw7
+_7C>p?b0=^UQoq+EId?(;L0pAJuPJnj;ymRdQJa+aa0pAJuPQZ79yA#}<;O+!>=g@~<{*~rP3z$2>+z
+IARFn8XU7Gh_+U!(@yo#5^ScPF?z!QBb&PH=aEyA#}<;O+!-Czw0I+zH{%y9ge`+WjImknV(ZC!{+e-
+3jSVJf(zmC!{+e-3j5&;qZ}`eTfCj2<Eom_Dj-hg3CUTADDN-yc6b~Fz<wUC(Jux-U;(gn0Lau6V{!u
+?i_KCxBZfYc_+*}Vct1>F!BTQPMCMXyc6b~Fz<wUC(Jux-3jYXSa*(HWQ95Hevu!TcY1F@`Qiu>;5z}
+|3HVOHcLKf>@ST9~1bipJI|1G~H=o3kz9ir~0pAJuPQZ71J_b~g(C>tPC-gg^-wFLrz;^<^6X2Zy@4O
+F(U_`rLv<=`p0pAJuPQZ5pz7z1BfbRr+C*V6_-U;(gSa-s@6V{z`$MYD`?icxic_+*}VcrSzPMCMXyc
+6b~Fz*EYCSh$7_a^~-5-TPVV$OY-8~fS)B0u=csbN+8VczMX_WLKwA)o%0K9C#0cLKf>;GF>Pyf3%He
+s;ge58yii-wF6mz;^<^6Y!mY?*x1&;5(-ikK6#hCvjmyy>o6piOKAKjlrA;Bnj}HfbRr+C%8Mo-3jha
+aCbtu6T+Pk?u2kBggfVTj~BlL(w&g*gmfpQJ0aZ(<W3-W0=W~&oxtq`ZV&e$ggYVJ3E|HB%cn4zFA3>
+RNOwZI6VjcK?u2wFq&p$q3F%HqcS5)m!krNAgm5Q>n?o%2OYv-iyA#}<;O+!>C%8Mo-3jhaaCd^c6Wp
+EP?gVoum^;DT3FgkVFZIVzcE3mxxI4k!3GPmCcY?bU+@0X=1b651?U5ia3-k_rqWA=O=i2@bhO+xbk^
+tWc_)frg0=^UGo#5^ScPF?z!QBb&PB3?Zxf9HtVD4OTj~BlL+@0cXeSUCvg1ZykosjN?bSI=cA>9e#P
+6&5GxD&#i5bj*NXcR-){USs_?gVltkUK%!3EEE3c7n7Mq@5t`1ZgKoJ3-nB(oT?e-d~c6q3nJUBG7h%
+wiC3SpzQ>0CuloC+6mH5kamK!6QrFW?F4BjG&^G-Zo*K$Bvd>7Haf^jLbcP2L(3;1Ng(Y6X(vcKLD~t
+@PLOs&vlE(~vFj1Bk=-vs1l3Ncc0#o$SSCT+3EEC*b^@{!kez_+1Y{>5I|11V$j;bt7Ur@0MUueS3C2
+$Fb%L)Ge4XJt_!#5{UY+pjgjXlLI^op`ug;rSV;;L-<Og1z@ahCrC#X6>)d{LjP<4W;6I7j`>N$M^a-
+;qSOy_;+F-EcbMScL(37byXbi$?+ES+HK1WPAGIw8^tiB3p#LZTBAosj5^o%hEmcE89EL^>hT36V~Sb
+V8)l<3Q?gh;%}v6B3<}=u8jEFfHKdyf2l;9=;@4I>FKjmQJvAf~6BIonYw%OXvPV-}EOsLX2S236oBk
+bmr!Wn8NNCA%aaOY&v1n37byXbi$?+Hl48PgiR-GI@6m%94cxEFrB&cBACMN7a@X8Cu}-l(+QhS*mT0
+C6E>Z&>4Z%uY&yZw364&1bb_NZ_a!Kp!k1xK2b)f>&;%wXfI8E`=nd`vq$6YsN}W*Z^lCgqCQx<e&c9
+&*d%Or1s5(K_393#|b%LrBRGpye1XX7`zsOYD8?pqdPEd8`zN8KV*!?0?py~uwCzLv&)Cr(Y0CfVW6F
+{8+>I6_HfI0!x36oBkbmn!>FMbJZI$_fZn@-qt!ln~8ooMb<Q^2MZHl48P335#UbpoanFrD|MPFTE`1
+W+e{I$_fZn@-qtf~6BIoe=4SNGC)(A<+qmPDpe@q7xFGwK*ad?`1-G6D*xz=>$tBSUSPd36@T<bb_T5
+ES+HK1Su!vF`<VE;>y}}uNb=?FOnn{F>E)!&>hyZEYs>f8gbH=sZUjT3XjsVr)Aya#V-LyConpJ(Fu&
+s@=H~H0`dcqPKb0uq!SXIkm!U&CnP!{(OG$q7rz7|oe=4SNGC)(A<_wvPKa~@qZ1gNK<ETQClET}&k2
+7{_;c32tQxD<{USeb=!8Qj96HO}9qb$-0!Ak=I)TvX7nJdubHC>0KXkD3WLUMA1WTV-yo60BY&v1n37
+bx^bb_T5ES=surBex;PMCDUq!T8c_a!J;weI)O3H5J$9>D@oCxAKu)Cr)@OHYX<fKn%vI-%4FrB3K{U
+c0^oTh{%aew5}27Jxbd)Cr(Y0CfVW6F{A?>4Z%uI6A@6364%kbY8c8qwX~!(g~4Hh;#y@6BwPq=mbV5
+Fgk(J34~6yqp}BtP9St%zQ}FgsC!LdbONIj7@ffA1V$$?I)Tv%j80&50-+NKoj~XWLMIS9fzWw>nKTx
+x`$e39(Fu%BU~~eb6Bs=eXNYt{q!S{YqDe?}LZTBAosj6fZu>^vYeJ+GBApQFgh(eaIxoe^w?UFFMW!
+XGfY1emE+BNlp9}t6&&?6BTQ3ENE;w|-p$iUOaOi?V7aY3a&;@@k_;bOZxp<79Xm-%-pxNe#n5^#ir1
+?p6uJmK$7DT!r(gl$&h;%`u3nE>R=mJ6)5W0ZS1%$5q%R#YL-S6;qHQ4kq=wr~wpbt)<qZ$e<U0~?~O
+BXo0z|jScE^u@`?>K<E*8)oySh~Q{1(q(bbb+M{EL~3@xM;a};G#v)!j}tL_~@M{!!mWhLCc_J&@yNl
+v`ktiEt8f>%j8)}3+Fd!k+eu!)IGl!>sGWZS{5yfmPN~=Wzn)|S+u-()<uhp78flpT3oc)^|TnJ?)Re
+QMN3cgg_*Ga9ay@+(gl_-uylc?3oKpW=mJL<IJ&^mb?nQ5FiI~4mM*Y#fu##9U0~?~OBYzWz|sYgE{O
+DmrxrN6z|jScE^u@mw|%4TwZPH^mM*Y#fu##9U0~?~OBYdY;lC7Ey1>!}jxKO?fujo?UB^D;g$e3@5g
+o8}L8Qy`)?QD8r775S!KMo~U9jnbO&4ssVA2JXE|_#3w|%4TwP4c)n=aUN!KMo~U9jnbO&4ssVAFMY?
+??I(njt}e=>kmGaoacQUJHC&;NyZ-72Z~1g36t`SI`Nyx8i{)r0GZyBPex2r|Y=65p}Nxr7kFSL8%Ki
+U9jnbO&4ssVABPgF4%OzrVBP*FzJFx7u2K9eQ5yp<)vWL1)DC|bit+zHeImkf=w4}x?s}<n=aUN!KMo
+)T`=i_Nf%7oe%mi8*mS|B3pQP_>4HrcY`S361)DC|bit+zHeImkf=L%lx?s|Ef2km*rTayGVABPgF4%
+OzrVBP*u<3$L7i_v<(*>I@*mS|93npDK={k3Q9@EnOA~mq-f=w4}x?s}<o37KFQ|yC<0#Fx#x&YJ#ps
+sW4LjDl<c-t>2D0M-p3rby3>Vi@il)9kQ1*NWC;%Jx14XC<6)di{^%h7FKjX~*t5h8eX!K({iUGVCHR
+~Njx;ME1ME_ijps|#K|r-Q!yhi3ac83v{MMTmgb1+*@pb-}9(US06&f>#&3y5Q9XuP%6X!K({iUGVBz
+agVqCk^){A(7J%u1+*@2H-oAa<hmf&1-UNBbpfplXk9?-0$LZ)y2g2rxBZd=UKh~1fY#-gJmPv5<hmf
+&1-UNBbwRERa$S(?f?OBmx**pz#y#HlOA3Nr5bT0n7v#Di*9EyQ$aO)k3vyjhvw~a~<hmf&HGD5*Y3w
+f{z;1NE$Q01J;ME1ME_ijps|#LT@alqBmtXOQ4?XAp9c_^%$aRfR^V@z&4c{4=@;Xa=?gC#I_`1N?1-
+>rub%C!7d|lw{0$&&Sy1>^pHaEgbbiarb7`wpO1->rub%C!7a$S(?f?OBmx**pDxh}|cL9PpOU1M`23
+`F;fBtftXa$S(?f?OBmx**pDv@W1^0j&#YT|nze3{jBlf?U`A<vkdPmx5py1iK*D1-UNBbwRERa$S(?
+^6PGZx&^*2@O6Q&3w&MR>ze01Ui=a;c7d@Aj9uXC0$-naj0M9k7<R$13x-`V?1Etz47*_11;ehpZX1i
+x{US+f7Fc${vI~Y?FzkY17Yw@~*9Fxn07(&Kl%AKCL%lP%UyU*7evu@lX1^r1402tN>w;Vt<hmf&1-U
+NBbxo%hk%3$n<hthet1$-MFQNmquIVMAWh0vV6Y>MJE}(S*tqW*f(^*1pfYt@HuKTiTj6wH{`~a;BXk
+9?-0$LZ)x`5UNv@W1^0j&#YT|n!CR~Njx;MH~WYK*~4!K({iUGVCHR~Njx;ME1ME_ijps|#LT@alqB7
+reUQ)djDvb?Y7KUJF!Rpy~ou7pS^G)di|9P<4T-3sha8>H<|4sJcMa1*$Gkb*)_&gbC<=5g1T)fvO8s
+U7+d$RTrqbK-C4RE>LxWs!Ic*Dg{gzV7k`MlcD&#Ujzm~T>$C=P#1u@0MrGbE&z1_sB3u-Li(RHLt3E
+J1)Z+7^JM7!?iZ;6P}lPFT+~QpUr_3TQWuoEpwtDWE+}<DsS8S7Q0jtC*V=VKX!`CKsew`#l)9kQ1*I
+-1bwQ~MN?lOuf>IZhx}ekrr7mLjf=*ZLJQ+&<rJ&RWr7kFSL8%K$J!=1F@K5qj@ej=r8BleBs%xL=PY
+>YV<HaulUKh~1fYt@CE_ii;stZ(Ipz1*^T=43GR~Njx;ME1ME_ij_yc(6>{US;5>Vj7nyt+Wu1*$Gkb
+%Cl2R9&Fz6Ysd-)djCEcy+<6>*m#H^X?aMf>#&3y5Q9XuP%6X!K({iUGVCHR~Njx;MKM7Een4~vwdC!
+ZQlKIg#|!o0(1&|UEu2iUsu=&H586A{0My@Sm5gdUl-)MVxN~rk$1ny6a>2<*A;e-OhK*-a$S(?3cYE
+cfM9{I3w&MR>jGbw-^oS4_$6TM0%I2#yTI24zAo@}fv*dEUEu2iUl-)MAlC)CF35F3uIs*p3ibU`!0Q
+597tp$Z)&;aKp!KQ82)QoEbwRERa$P{{0$LZ)x`5VoUy_gd?tT#`pmhPQ3us+H>jGLA(7J%u1+*@pbp
+fplXk9?-f>)Qf03G{$7Mi>JMVx@v1+*@pbpfplXk9?-0$LZ)x`5UNv@W1^0j&#YT|n!~?N_6@yI&*;U
+S06&0#z5Nx<J(hr7kFSL8%K$T~O*uCkT;AeIQo)z6=J<-TfjzQ0jtG7nHi7)CHw3D0M-p3rby3>Vi@i
+l)BR1kQ?Z9-Iu<hxw~I<5|p~2)CHw3D0M-p3rb!4>JyuL^@)8TJn-uB(j&lS<*oxkb9cW;641JU)&;a
+KpmhPQ3us+C-N^GH*QG*2#}wqcfYt@HuG)1VXzrH+UKh~1fYt@HE}(S*tqW*9Aa4b^F35F3t_yNqkn4
+h67v#F?yvK`Q0>Lf_b^)ymXk9?-0$LZ)x`5UNv@W1^0j&#YT|nysS{Kl|?z&9$b@z)T0j&#YT|nysS{
+Kl|fYt@Ho&ethS{Kl|fYt@HE}(S*t*dVPM%`-xuM3)6<?A9&P`(1mmDmefRYECfN~tHM2m~r+(15R9H
+?KxFcfUxIrckSf;xE)&sos=JML{WW+4l-Md?W~7J&dwz$MdM=?iX<aS{Kl|fYt@HE}(V6s|#LT@alqB
+7reTl)CHw3D0M-n>%IgKwfs^5>H<&~fVu$G1)wefbpfagHeImkf=w4}x?s}<lP+*{furl%@jQCC`$cN
+N(gl_-uyjGB3nE=!Uan^emM*Y#fu#!^UEt^fM;AD{?k@#G^}bB!2sT}?>4HrcY`S361)DC|bit+zHeI
+mkf=!?D2f%azrt8}0hfuveUepnQx&YJ#pe_J)0jLW=T>$C=P?tv8Z33V!0CfST3ou>R&Xb{ayI<r7Kw
+Xy<s{D!O2oaRJpwtDWE+}<DsS8S7Q0jtG7j(L=JD#WRwV>1mr7kFSL8%KsU6&K&V^Bke8vyD8P#1u@0
+MrGTu4^lwEoaod7L>Z6)CHw3D0Km-3qV}}>I*=90jMtk^#!250Mr+N`T|T}fa&Xb>mBM|F97ugpuPaq
+7l8T#P+tJ*3qXAVs4v*`1)IKL(-&;|f=N$c*9Dlqp10ni?)8FAU$E&5HhsaSFWB@2o4#Pv7i{{1O<%C
+-3pRbhrZ1TE1&O{O(bx0VJJh{i5a|mTeF38{VDtryzJSpeF!};UU%==K2z>#eFCg><guZ~#7ZCb-ZjO
+jb?S2<67cCbp7cGO9LCc_}7OePO&^&lx_@6Wjni1pI?UzvZdV!@cu=E9%zQEEKSo#7>UtsAAEPa8cFR
+=6lmcGEz*VCyjS`;mAd7$p~dOD7xWzn+!@0#u%*^wpM&9L92xDnTq#|30PpxMZf94VyG{2gultK{&U?
+%vGe13Ov7$7J@J={3`9rq@ibnO-wJR(g%}8tF9}nU>I*Z{F$WUehu<Eu+&iIxVBqGCD1z(=s|Oqth}v
+EuqsAIxV5o5;`rR(-J!K%{%?vYg$I9Wpr9br#*DqL#I7-+C!&3blN|s{d3wsr~Px<Kd1e3+CS&}vg%H
+xf8#ba?V-~iI_;s;9y;xz(;hl}gKj5;w1iGe=(L1ROX#$EPOIm9-v`o(^l#iorxkQsL7z_UX%C(D&}k
+2y_Rwknoc7Ndj?mn+e@^@7w13WQkJvf%Z`?+wJ#<<@rxkQsL8ldTT0y7Pb6P#8)pJ@sXE+mo`H^0&k(
+uYay{FGxqtkcs>FHqjt!xFIR?uk$omS6j^_=0}Fj^zi>N%~R)9N|jmrZu&{2RB?X$75D&}jvoR?uk$o
+mSB4i(*<mpT6*={d3wsr~Px<Kd1e3v_9WS^IzISr#*DqL#I7-`Z|#I&}k2y_Rwknoc7OY|D5*E(RM!l
+k%mY5ZtvfDNm@pyWpr9br)6|nMyF+TT1Ka3bXrEIC3IRsrzLb+LZ>BkT0%$b^PMmM#_g2TGCD1z(=s{
+&F~UFe>J3pF>9mnfo9MKOPMheoiB6m7Xy=_yk$>YhKkO9MbA_~#P8;d8kxt)V9KNEp=MQN&oz~K69i7
+(E5syP-q+RFJDe`aJ#;LV*T1%(3blOOt!;dyot)<giI&GxWCOU1R(<VA?qSGci+Rxi}iu@b5aS~q;+i
+<p)PHXA3mQHKwh!pU!aF?1Xar2v{HqmJl9p$^dPoKADs*QBoNT-c-+DNC3blOO#jda>brzLb+LZ>BkT
+0*BKblN}1`}wm@jQ`ReI_;s;9y;xz(;hnQq0=5ZYzp5~v$TXxOX#$OPD|*te@^@7ct3C7iScjT#;HAY
++C!&3blO9wJ#^Ybr#*DqKd1e3+CQiLbJ{<r{d3ws$NPEvPK<x!Hc9QFPaD0oj84nww2V&6=(LPZ%jmR
+(PD|*tgicH7w1iGe=y<;muM^|nxSft#MyF+TT0y53bXq~D6?Du2o10e8Y3rP}&S4YUp0>t(tMC0i4u9
+t*X%C(D&}k2y_RwJqu%6+jWpr9br)6|nLZ>BkT0*BKbXr2EC3O6L<MzCyWpr9br)6|nMyF+T%oD|p;R
+v0OT0*BKbXr2EC3IRsrzLdG?|XcEUb347Jp5?CxcK7d-(K_^#MxM=k8ca=w3|-5>9m_pJL$BOPCMzGe
+SPap_&08Y)owcNrgQkXM{ipT>a?3qyXmx>PP^%}n@+pwuvubHR?}iS-{-443I2`SbhV&PyXmx>PP^&T
+hBqy!(}FrJsMCTvEvVCiIxVQvVmd9RbN0Nt^WWdNovd0=rv-J`1DdcM9uXdbMyxG$+ES-2b=p#=Ep-m
+Jx0BTO`RY!6f8#b{?Ud5;CVejvrfTLLGTr=Sp91a!Yw&pnJEOF!POIvCpRev*_cw08nm#<mjaaMdw5m
+?4>a?m(tLn6>POIv)s!prwuoW`DMD45d_0^r`{>E*@+E=H2b=p^_eRbMbr+sw}Ph)Rj`|7l>PW$S#uT
+J~ww6D(BS9gZ{m-f|ZU!C^VX<wc8)oEXy_SI=$o%YpfU!C^VX<wc8)oEXS!m)Of>dtU~<2Ft$t<%yvE
+v?hi`h;85OtraAo9nc>PMhnrxlWtww7E{3>s-5@tdrZ{xJ^<U?6kQ~o9nc>PMhnrxlWtww7E{3>$JH}
+o9nc>PMhnrxz4xgT_?A{aT}sG*lBZ}HrHu$oi^8LbDcKVX>*-6*J*Q|HrHusotD;VX`SnPee365)ABk
+kt<%yvEv?hiI_;~|zB=uz)4n?GtJA(Z?W@ziI_;~|zB*rD-C69PU!KXAi(b9?X=$C7)@f;-mey%$otD
+;VX`PnVX=$C7)@f;-Yd?S13G825UZ<sXT3VmJv(LR3rx#Z*?p}~{{_gF?-%;CKr_FWRT<6<#sngcqxL
+t1_u0C9Jj3%k2by`}drFB|br+szWSEqe-+E=H2b=p_w>#I9Y{f*oFw69M4>hSsMa045nmey%$otD;VX
+`PnVX<wc8)oEXy_SO0N>dsF8PV0-bv`$Oww6so3>$J2^OY5|>PD|^wv`$Oww6so3>$J2^`|5msbtk63
++uJ0yrA}Mww53j4>a?X!Tk5o>PFw1<rA}Mww53j4>hM({oH6&rhH-md(pQAEs!prw+&c~R>JJwkqnV<
+w4$oGDbu&=sqW0BkU!AY7?sW7wZZp-sI_;}Zc#0dWmey%$?LBJK)#f^Ft`#1%My&O9T3_c`7wJs&FRi
+iD8au7A)A~BCuTMLow8Kt2?6kv9JM6T+&cnYpU9GRv`a0kDfpjMN8@It~jh)umX?>m6*J*v7*4Jr$oz
+~ZBeVx|VX?>m6*J*v7*4O#I52W+Y-?*KaT4Sfpb=q8a*N*;Quijv_zE11ww7x#!1~y&ou+t7Z?XdHF=
+TZIKYuaU}U3S`Kr(Jg1VTV`zcGzi$op#u1hn;rVX@?#5kqQq%Q}umGe&?URaht9d+G(MkcG+o%op#u1
+hn;rVX@{M5*lCBIcGzi$op#u1hn?qpee365(=I#hvePa*?XuGjJMFMf%e=J6PK)fc$WDvww8&12?6kv
+9>+5{q2hu6$Z`{U-Z|4usVB^&KI<2qM`Z}$z)A~BCuhaTEZLZViI&H4QtoVH&NavV;X@i|M*lBZ}HrH
+u$oi^8LbDcKVX>*;H)@f;-mey%$otD;VX`OdHwsXwixD8QD>$J2^OY5|>PD|^wv`$Oww6so3>$J2^OY
+5|>PD|^ww9fZE4V_v3#%+FDTBoITT3V;2by`}drFB|br=@k;SEqe-+E=H2b=p^_eRaORx--k)xQ$Nx>
+a?#;`|7l>PW$S#uTJ~ww69M4>a?#;`|7l>PW$Q;9-HsHx}SSZ%j>kXPD|^wv`$Oww6so3>$J2^OY5|>
+PD|^wv`$Oww2)0}&3rv#Cy>8!o0>LMq0*pI;B7naWBVTT91hr*Hr{md&WN-Bp3=K12xqq6=c_wy{0pa
+MoUU*p!s{C^W4t!;%E8pn+{rx2l*g>Z?>oQN;ni%NtkcHdxXlmI?PGM>T&F*f{lA7NTSN9(orYRur$u
+(!VW%B-+F>W(>svqfns(V~mz{RmX_uYW*J*v7*4Jr$oz~ZBeVx|VX?>m6*J*v7*4N2?4^iidzj2!+zU
+*UfTjSLFI<2qM`Z}$z)A~BCuhaTEY)eK<(+)fBu#@lgt)F`hZx7mKr(Jg1Wv3l>+F^$dyN^TKVW%B-!
+oDXDKho<qP%X0aeSdYQhQD$7fd;FEc3Nnsh4wl7cfI9JSKI8g%}(3ww9QU<%(>A`Rle7^e(p7GwbNER
+ZMD-@J8iYoB0DXz(;_=9veP0vEwa-hdwy-QdpTT+%Y9AmXJ7lL!{1$8vj2JadHA*2?j>We0XW;u{n~2
+x(p+-g;kv_hhwBd4-8Ds>DS8|+M)MVRzHZNb*y8N>`z{$xj3!1CqlwYj)2?NAFS#Y`l=^L}(0QXrF=M
+a^-Osz5J7btJ%ov9;4r3g~I2`|Q-NSVc*F9Xf`w@O$*xnhWhdB&%G0??87Xw`kbTJScOSTK|B?DdDh>
+PpqT=(X>?61>GyqDi`x_jWSy?8Gf<7SMTF>c0q7~^4#hcVdlxSe<}x$eVt*&w&QcrUr`0&nM)9_M9@m
+oZ+(co~C@(Atmpk|gh6Fwo0D>`ULKyq63R1_%RW=gWSswtr%vFwp)l0}<3As6$YPptd^iC0iX5&>^5~
+d;c2(It29lICrO+?m5%#q1g!bH>8!#X?`>NHw;8thqMl9ZJ*vf-?$OtMi5i>hW!oM{65{?38#l4qeDi
+AjPAaRLq>;;&hF-G#vq$)7WmuU_l9f^*&MRjlHENY;4ptDNvBQo3BMFWWasP#!3;@c=j^{SBz0FOown
+2cGlP@YA+PLN`J3;JWj~PHKk?ZIew6TjyP^`_A-r?#zcLWvWe>gVgFu3Z1kY|wNpjoE|Hpqa3H2x>cu
+4S&;32_7g4x9MHzfFb@}AwAl_QYgA;Cj}ThRBC>+<nTCL#75Vhj>IyOkda{vMINn}l}=?-1UW_SQx&d
+&vM{0Ft}kYQoIZ!w}vfylwE$d3IOq;Zj_(1$n=+>pc5&7ne7e50`r-{*`&QO+H6CodVc8>c_62KapUO
+V3A;vV3A;vV3A;vV3A;vV3A-EB=Mx}cYibG^f)9~B$(a4e<Hym!6L!!_zycT{zQyLjOG0s1|Y^_iWPa
+)`JS8ZclZ;zW#_t|$gRk&m}*5@*?0IS(kjv_W>+!0ihzoMihzoMikEmMnI4CrvcLOJ%(5b*BBQ%~s>r
+CAT}3uUHbpi?Hbpi?Hn;O<;$&v|*S!n-fb5SmB#{-76_M43VxN5?D<Uf<TfMtSJ=Z0wBC2}#x#{PE`z
+OXAvLdn~vSPj!X%%S|X%%TL4D^!gl2S9pnt+;snsWgrl7Icv;qNXk*&mdQnv9x^nv9xr0h3LWO_NQNO
+_NQNO_NP?F2Kyu<B(C4QIk=VQIk=VQIk;<HZ}nLna9~=(`3_R(`3_R)8CWxGy^81-i=PlsL80wsL80w
+XcggP$0VR8peCRupeCRup!==kOc^~4K{Y`&K{Y`&K`kkGUogF20?cSc*4O?uH;0AD@BS}C-TQmfj~Mq
+)3`uU;knCr2YjSIHYjSIHYjSIHYvxoF&>F+ZK5nGc&n8Wr4Vtu?w3@V<w3@V<w3@V<w0iFi6RY=5?p+
+|ICZ$D(6K~MO$)Jg>iEI(##QVRyCoQ9qTa(*EZV$OV<n}PJ9`bs~>mje__uU)L1`oMC<o1x;LvGoR_V
+BbG!polAze9M>?B73JG5{0nA++p2`8)ca8_otR5+}RyF$q4}|7Rc)%x<N>LxKwvC#3Qaxx>@J7IDAB1
+95gR@2jUc7d#~Rkl;gt*%It`NbupTph|JFKS+WP3I6UXrv~r-G@~%jo@;++%$eXJ#)lXmV$2?nzr%cc
+m~Ri^J%slV-ore5$n9aCJ>>O}*F#><cP?N~1rNDB<o1x;Lv9bbJ>>R~+e2>m#B<LY4-@OY+V0EhA*$b
+f`_2W-x!@tPhsYixdx-2IvWLhXB72DJA+m>w^^np-N)Hq3A)tqVp6u(5IT<_z^&+SjLA?m-MNlt-deI
+&71YV@|Vs^cL`!@{0o^d?-7g4?DyEk_;aFN!Fv|gl@UC9q~(=~g^XhilRvKNtM^R?f_JbRJSi<DlZ^!
+m;P%<14oS})RiG2dPU^&+SjLA?m-ZuiaB^S_JSU3>os=tV#;0(ucpwu1amexv`L^X^|UaqrJF8fm@uJ
+`vQ5pkB<g7XkeqA_965(2Iax1hgo^Q8*pE2<k;pFM@gz)Qg~A1oa}Q7eT$4Sj!~l+p0H07g4>4>P1wq
+XV23PXM<G}@9)(`ZZC3sk=u*hUgY+A_GcJ~++O7N+DBs#MqV%SdcFI5b2uHm$n8aLFLHa4+l$;@<n|)
+B7rDL2?L}@ca(gkcUgY&AuQz$Uf19+!>EKOnZ*t3K-M^d2-bD5$viEPdnP&{rdUHZ>lhT`%-lX(qPQ3
+{z8yo*8zq36KLA?p;O;B%wdK1)}pxy-aCaBewv%k(aDZO{Eds2Fn()E?Iv)bWo@Fuc1k-drRO=NE(dl
+T842;M~YCbBn?y*VMcnNDv4dK1u_fZk{CO+SF$KQRVDy$R|~P}!#bcN5f`pt4>ddl;ruHWL4pUi3XTM
+D-@B_jfLEaXNSt*_+6+SLu%pNB8~s&BS_>)|<4lY5VVHV!fGIZvuJ~(3^nX1oZyS1uo78Z-ROgRJJJ3
+J|3j?e)s<wXrB(!dNaG;r1U1G_ued0dXv&S-@V~vknOmCH)*{|>rGnuruLn(Zz6m1uslST{jYxyQ|v=
+jt1~BO|Cf){eD{S@!H4iZg!du558-_XFMCq|N;cg!od0E<+5f+|WB?NUFvUJ3_tEC;;#BY<!ECPldr0
+s@g4y`^_mJR+6M~0#4rl+Gfk^N}f*<DDhtNKR_ThU5_{HmC2=7C9AHw?(-iPo$r0U_xdiDX?JGDn6yb
+s}hn64f|`w-ga+Bxm!Z15qx58-_X??ZUm{r2~e+lSmf%&rfSedq=DFif!zd40(1LtdZnT;S$xu+nqd?
+w;%oy7!VH3GYLAAHvHXv%iP%K7^M|Wq%K|>qBTCLi>Dg)oxA(A9DMU+lSn;Y4`6TVtZQtklTmcKIHZx
+x9rdQdzf7x^7@chHlF@H`R)y8gAcPTdl~;8BKs0qHZ=XcMD``JFSF~*?D`T=w%Gl#A@1)bpf3S^3F!N
+s3*4L!zGU<zqc0hK$>>W)Uo!fV(U;lvC7UnVWaIE38;}29viXwDmu&84rTj|uIK=ZMo-gryz59VP2Jw
+8EQ(t2F63drZzQpn+mM^h<iRDWyck@zyhk6|1`I4KLc)q0JC7v(we3?^UV)+uwmsq~U@+Fopv3!Z;OD
+uOYQ+{oF9A?ya_cSM&FUfpK=F5x!%OuK1#=n;wzU1&Fhc7vN$>B>5UvjvcoAPVZ<B-Ri(%Dnamr%aE<
+iCXRC5$g&d<kQH>16NmUWr7$B=RMZZ@zoO8R1JZUy}KfO!gK3y(IHxCVffdOCnzq84?*184?*184?*1
+ncsJBI3*0p49N`149N`146pkkks*;Gks*;Gks*<#rn4vCA(qk3I4>39(!i=fi04+HTQhF;6khkYbqTT
+DckbbJ|JuJ`0J6Dn%<{dLdO0QBw@7;`zNelcqx*uquXFoavabg_QSY1<vbi%=pQ*ENBEAEE^XJPsVF+
+pnY6xlwY6xlwYOcL@w?JX8$-S4{5>gsc8d4fkns5GmIVTKh4QUN&4QUN&4QUN&4QUN&Wk>U0NNJcgLq
+J17LqPMrmwGuT3_%S+4YOv*X!hychk=Y{?+Muq*$mkX*$mkX*$mmt_g?DdoDjGg;A()Y0j}D+a(TGCx
+a__}_x|9+<;x`l41YfiFbpvJ?xUY}@1GcG7^p!V^ol*}d&wAL3^9fnL)-{)U2$D;U2$D;U2Q+qPruJ#
+R)bmf-ET9x|I0vTATy8|$P8o#GB@Hdz~Nel8*#YqH-GlS@ck2G9LCtU`g>&tI{g2|K>ZLt(VX9R$!Hh
+1<YJVIQ7&%D*Q@rU@h9JpGTJ`wH#h5Mpqqca8R%x9n}Kcyy16Ac*L}F|!!6;*pG{gnDBnLZ(6j%-Ko0
+{w4D>M2!$2<sz1)bG8}V}8m+QV<_vN}T*L}Zx^ALf3h{70Qj4(zRBa9Ko2xEjX!tqHdYBi|UpjLxg^%
+39W^t^D;t3j^@y&Cjt(5wE3OU59mfYkt2Q?T;8KTb+ft3j=1wpVTLdl=G+UJZIR=+&TCGo%%@8q{h~t
+3j;>wHnlFP^&?$X6RM<m_4!YVMr^4H4xT7SOa0rkXEc}u&TkT2CEvZYOt!QRrQkV642Q$s)_G$_&pgi
+Is^}#8gOdBsR5@3oEmUyz^PfB+8-gC;M9OqGxv9QdSW<p?@uuh83m^XoEmUyz^MVJ2AmpjYQU)hrv{u
+FaB7D4OIt%U+dT{!9Wshg4MH^t)hzSv-5{ti)ldizFAM`y%@9z8Y7nZKeD~%h0|a#lDnc~~)l{g=Te?
+B02B8{+YQ&rhPEB#@?6DB4L8xX|oDl4PjaYAyh%7=i2-P4|gHR1ZH3-!pRD)0rLNy50AXI};4MH{2-k
+W}czkgy3(uz<GLNy50AXI};4MH_yIt8Z&oEmUyz^MVJ2Amr4jatOG$04ZT)PPe1P7OFU;M9Oq15OP%H
+QJeU|AGNXDMB>})rgJLNpTNDTE)|>y&h>rs0N`LglZ6~L8u0y8iZ;PszIm*p&Ep0wDV^J+~W{bNNOOd
+fushK8f^*)DkL?K)Id@LNev`5kkmj@BVwtYKbzeihj>C#qm2ylgro+N8c1p&sez;hk{U>AAgO_*29g?
++NbmgF*#7IEF8=Q3^5OF3k}(J)BsGxKKvGkZGX2HiQiDqkE;Vd-yB}XCsyzzv1g8d^8gOdBsR5@3oEm
+Uyz^MVJ2AmpjYQU)hrv{vw-Ts!S_Bg~7ml|AZaH+wi2A3LKYD_F4sez;hk{U>ASlY5@Ke*I<F4d&=Fk
+}>$8eD2{sllbj#M8`>Xw;xlgGP;aRAOmj2|x`1HU8$$t|sW87=w5MQ3FH`5H&#508s-(4G=XZmKfBS?
+M|7o_lN}4&v$QLB0x~ls6nF!jT$s+(5OM928|jtYS5@bqXvx{6HowZ0I2ade~Pn03~FjnXTBe0Akqp%
+4G=Xz)BsTfL=6x%K-3&k3P242HD@+!=A^I?bvV7BGkeKE%%4Ei08s-(4G=Xz)BsTfM9m?fhk#;FbH4f
+0oD^bEgFy`jH5k+!f(kzk{50^>z)u4|%^{$$)4)yxI}PkK=bJyfGNFHB41$V04e~U|)8G?BIX<A%9I^
+>E4cIhb(|}C_HVxP`=X((~Cx)QYfKCHC4d^syA4xI_It}PFhirmP12#>u>12P9fL5H&?h`;Bdlb?NKM
+nje2R7Y-i7W;+7}Q`;gFy`jHOR&Q2SZss6q$3j%WnvK7=p^%$Jvv03~Df_Ik4&83(^Wi4G=Xz)BsU)2
+q*wG0MuN+y*G!mLLh2@r~#tpBB&VDU{HfW4F)wA(<%Hk7ukfJ26h_QX)dyP%|17W(?aBFkf%YO26-Cf
+X^^Kuo(6du<Y_Lli8#&0;w_#^*lA#=*-d;2UJpZ1;irM027Vg&Y2c@Up9X#!_-Wv$xd`Y*K*6Q~n+9x
+}-PD)h^*96-bQ;iUK&Jtn26P(GX+WpBm_)&*0h<PF8n9`uy;tP!y7$$fiy}_X<j$VR!A}D}%|&EErva
+S?befCl^dg`*)8I^lGY!r(IMZCucW*c?#G3|h8oX)nroo#AZyLO5E+$c&X>g{&nFeQ?I@4+WaQI-<T-
+)V0SUn0sz4qRG|ITOx6?qzTW00pop5`W?h|^S@yl=>0r-7a3Ue|A~{`F5Uf5$-m_B$Go!Jr0%8VqVMs
+KKBHgBlEKZla1k4fZtH)7<**4JU;d)L>A9K@A2qH<5*(27Vg&Y2c^1nNAU>L7WD08pLT3r@6h)&Ba+E
+@-)cPAWwrl4e~U|(;!cSJPq<RH`6KNG>Fq6PJ=iN;xzZQNxL{JM4sj*t)SC@PIHr1ylL>Jxk)R|G&s}
+XOmmaXdmnAG2{Fz6>}%V_Ss~OkP}4w7Q))WdD-cw?X>NkLZ%Xz&?IxfQ(?CoEF%85t_q(rcHz$Qq(?C
+rFH4W4>_ojzQ^yVd;B|z`Sg>2$XgEP%dHX){gm}VCNk(ht|)9`oV(p(-cFD`E`A1+@m8G}g_Sel1KVo
+HN44W=~D?5n}eSs}JGwI%Nq12D})Jb|SFmIhcFU}=D*0hR_>nul0oO7rkZlY392ZcYjTrU94+U>bmF9
+x@6n4X`x8(f~`tVj(^hG3e5uOM@;Ax-`3Dh`97H1Qlf(lxa|=L7C<usBG3iQ#sGxVdhkbX&|P7m<D1R
+h-r2K5kcu;NGr-TDAS-!gEGxSS|8GiDGjDH&mNPMVoHN44W=}h((HmFGScG^RBUOmrFjS{rZkw+U`m5
+24W=}h(qKx1DGjDH53AJn@ZWjI!%5+@k1&%cz%&5UJOs6{bh5XXw4zIcE)BXg=+dA|gDwraG`rl0aQy
+3^9{%p-k};S>c@Utcd5J8<G!WB3Oan0u#554oKuiNM&8zR;a83v{4b(JH(?CrFH4W4>FKLCC24WhBX&
+|P7n5M+!P7DUaX7?s03Oz~}aMNoK@-)cPAWwrl4e~U|(;!dt5?RD)5T`+$25}n1Y2L4kdN?ISo(6du<
+Y|znL7oPA8suq^r$L_PWllw$25}n1X%MGDoaVj1=Z14a<Y|znL7oPA8suq^r+N2@+WSmeU#3&UX<kwa
+HVxP`VAH(Y<@b5-aY$>?>Fn9%OJqT(0i6bP8qjH8=2Wn0z@~ZkTBH<g8n9{J{fzXmx@1770i6bP8qjG
+#rvaS?bQ;iU!gLBY4cIhb(|}C_HVxP`yK@9j@xT6wF{WqfJR-=`AWwrlO$aLJG+{agn+9weuxY@i0h<
+PFnqBC`Gu*=vRM2Tar{SZNgEtM{G<ehS`M$xK24@<aX>g{&nFePXoN0F96VGsuLo)HE!J7te8oUX(8N
+6wh61L3WZhq@@oN2cI#F=Kh#ocFu=eCC-p8KAAPrLVx(Z12y_a0#q-4m>R1&5esC+?lac2e5;V}BKQ-
+wB@E9%pYc@r0TNY8t3%pr#3vD8w`n(?CoEF-?i-Y_*9q4bC+A`Wu#)4Bj++-`966G735k=rq|Ovj56J
+&8EO6flUIN1U3n5((X#ZllrfJy7{|@%a=>W7{(aJ7{(aJ7{=)QDV#|-lW->COv0J;`{qy9mk4hX-Xy$
+9c$4rZ;Z4Gu#M&s~Ov0JO+9*Lxf|vv`31ZUE`#ahNCjAp*m@y7x9L6||aTwz;#$k-Z7>DB@u6wxd#dR
+;PdvV=se@9tlB7jK%lK>_GOk!D-FePD1`cVZv{PoR1H#g#DfSUns2Dll3FEijt>|t&OdKl>8#ypJiFv
+i0e4`V!x@i4|7?cusF*L}I}%XMF_%a<GQB=$HjW4w&<GRDgoFJpu;!WdzUFh)3jxNf*^xNf*EVVphx6
+w6G6EeTr^wj@?W2~!fLBuq(|k}xG<O2U+cDG5^&rX&_YY4-e6EHn|UBv?r-h7z14I7x7lhEF;_`;QDn
+A~7XlN~$Rx_(8yu#^1f^iJ<@{0Zamz#Ih)1O2U+cDG5^&rX);Bn36ChVM@Z3gehsxcW-)TC~QgClCUM
+Q`bn6QFePD1swo|2A^}SRmIN#bSQ4<L!qPeUGSKrvRhfFpXk@g&<aZx-f{HE)T@t#a;mhku0K}x(28y
+2}s>8P?@Wk~XL>6ySy~*!BpX3&F((F^RcY@sFOv0IjGYMxBE1?82X|~H3i%bMH32GAjBl=H4O`@UD?5
+*CLy3aAWg_tBJQFKY@lF%h-yL_?8L@1M30wsV+0Fzh(C16Rwl7J-vOMI_oFS#z0D6k}8Nx+h{pT!f4O
+eB_?@?fA$LYagziFHsymxL|}T@tz^bV=xv&?TWuLYJgH|Lk+|@9?rEs7X+hpe8|0f||rSC_zkum;^D2
+?>yrbPdJlsCgDuloj7<BdK7-11hXPac$4rZ;Z4Gu#EK~4Ov0IjGYMznJC=LNb;+x?i|TXG!!Se_KvOL
+Wc@pv@<VnbrkS8HeVr`TVCm~KkoP;<Dagw%+5{pcPJPCP{_6gcYaUY7kuX|HSYvt+W`(JJZQH7lZI|+
+8u?lR58-op@C_(|m_oxP2F&&VwXB@9X!lrSh^P+~chuqR<p!k)C7PV-p*>z^2d++t9|poBq*Wl@5k1V
+0IW63e1gaWdyzf}I3A33k%%LCpi)!;o9}N$`{4C&5pGp9DV%ev<j53BTtUluUTBCt***o@9Ubrl*9$p
+oBpQgAxWM3`!W3FeqVAVp){nCz*K`b`tC)*h#RHc8h8r)gFhm!cT&q1V5?#WX?Z@K?#Er1|{z!_5CXY
+5K!z%*pqh0Y97xX<}awu{wQe$q69<<h!PMbAW9~!@RL{sCD=)@lVB&oPV#pyz$z2LPlBK1eas0e{3Q5
+E@RQ&t!B2vpWC991iKS0MoP;>(eCGnJG7<75<VnbrkS8HeLY{;?sq%C<RTlgt)<22$PePo8I0<pm?r+
+Uw*uxN1<VnbrkS8HeLY{;?33(FoBvw5MaT4Mr#7T&g5GU>TvGMTrILxiclaMDNPePuAJgM?z+IGQDf}
+g~yC&5mFodi1xcGCII1rBF~@RQ&t!B2vp1V0IW68xmI$03{WlUVB{*h#RHU?;&&I{UeNvCu^Dlgdwrw
+<p4&gh2^|5(Xs}JPCdh3!d1Wb-oD-aS{uj1UBjHXW7L{6M;?wopkt|3Hd^vgggm(67nSENqhuVh?CC7
+f^Cn8+SviNixLY>MB6920U=M?Q<gnX*_Vg^f9>89<Vh@j65=GpNr;mWCm~L{e&4;}gb;ZW@+9O*$diy
+KAx}b{gggm(5^J4=I0<nQ;v~dLh?A~w{$!zvkS8HeLY{;?33(E0odh}wbP{Ww1U3n564)fLNnn$}CSB
+echovS0odh}wbQ0(!&`F?^KqrAtVuh2yCV@@5_*4nA1>z*?U|rw*$x0I;PePuAJPCOc@+9O*$diyKAx
+~nNlW->COv0IjGYMzXwOzhgYa+Z!c$4rZ;Z4Gugf|IqQoYIh{t|2w*d(w?V3UeX&K~!hKUr!b<Vnbrk
+S8HeLY{;?>DotxSmI2=nS?V5XA;iD)}*tmXmo$`Cu>cFHwkYN-XvBy32GA5Bvv>HViLq8RyYY=61pUG
+N$8T$CEee9DVCcEWfICHlu0O)P$sd$N$8T$C80|~mxL|}T@tz^bV=xv?zK>{-b5&qP$r>FLYagziFHj
+vmxL|}T@tz^bV=xv&?Qxu&i*!+n0gr2n+R$W)Fh}$P?Ml0K}}+DlOQHROoEsMF$rQ4#3YDG5R>kEe@9
+twBB)7Flb|L+O@f*PHHp<sf|vv`31SkfnS?G0T@tz^bV>JqmP9N#5y~W#Nhp(0CZSAXA(PN0p-V!Sgf
+0nP61pUGNi1Lzu%vrGOCpw>2w)Pxqyp2)cZCkbLMEY0LYIUt30)GpBy>sWlF%igOM3RRBx1#hP$r>FL
+Yagz31t#1mxL~fl}iGa1S|<y60js-Nx+hTB^8!94(EmdlK>_GOk&ZJFePD1!jyz52~!fLBuq(|k}xH)
+SV?e_p8bvpvEoFql3*pVXi0FA;3UCGf|CR%@x`;f<oJXUNfMGIBuPk;p8YJdSaBjmNi13tk|ZQaNRp5
+wAxT1#gd_<`5|Sh&Nl22ABq2$9o}JxTaUw)XtWpw^q)O6hJNdn>$CQLA2~!fLBuq(|k}xH{KH%(Y?X%
+wzA(op6U=qM27A=WIOTv_dDG5^&rX);BELIYnBsfWMlHer4NqYA?BE*6d!AgRa#G)m^NrICECkdy~wj
+f(pAW1@!gd_<`5|Sh&N$-A^Su8jaq9hhA2}u%?BqT{lk}6534TR~Zg((SB5~d_fNtlu_CB6Ga1Y*63S
+iU5dFNx(#0+9qF2}BZzBoIj;l0YPZNCJ@rA_+tih@|&>|IUIFv3yA&l0YPZNCJ@rA_+tih$Ik6Ad)~N
+6_L*NEJ%`&B)#AJcNUzm&s8rOXde>NiX;h15|Sh&Nl22ABq2#cl7u7)NfMHzch}O16(>TJ#PTH}Nn-h
+uKqP@kDk7adA4HObBne3pk|ZQaNRp5wz2E%Ff)gQ1V)>GgBq2#cl7u7)NfMGIBuPk;kR%~VLXw0e2}x
+4+J9fl^6NQzZ+yK@v2~HAgn1mz=NfMGIBuPk;kR%~VLXw0e2}x4E`I7}FLX^bvB_T;dl7u7)NfMGIBu
+Pk;kR%~VLXw0e2}u%?q<r%y3r>V6iIq!y$!RYcgLooIVvU(VB!Nf*kpv<ML=uQ35NRj3?*r{(!HEzh@
+$qLNNkWo@BxzfXeJ8(f!1u&&e^ZepAxT1#gd{0@oUi{3Cx?iVU_Bv8LU*)4ux<4Ax7+q@CAYoW7H8XQ
+ZG*Ix$i6eqyT79>I?=w3+7s`6p~jE&1%p`=KT`eZ?3<h-(%E-9NRp8xBS}V*{QKt5E;%{89rEwHytur
+%e7JnMWT0W7VW454X47zz;UvRJhLil;b@gJ!iD4zfN@n?z;UvRJhLa2@8BQ{sWH`y?B=bhun36FiV@l
+rd+vBG8FlHb#kQvAfWCk*09L6||aTw!pBM#R+T=#I@!*vhW-TBijIWe|mY{}S?S+rzK$(WKcC1Xm)l#
+D4EQ!?w63?~^*GMwb!ewNuRIWeqcSjntcGMr>M$#9b4B*RIDlME*rPBNTiILUC5;UxdqS&m!T<GhUVG
+RDgoFJrun@iNBC7%yXlF~ae~b;EVTb;EVTb=Tj`q7%bPhLy}xCBsRElME*rPBNTiILUC5;UvRJhLa2@
+8BX%dmxG=f8dfr_WL7B|PI5WP{Ia{IboebYuw-D#z>-;~WK7AJk})OEelJOHV}Ez{9~e#CEaHhN8B;Q
+*<XyBBPL&vjfTBxAmy9mCx^(hAVyrkZ)MTj1P?Mo1LrsR7%<?5eOoo^YF&Sbq#AJxc5R+N9<lSqXo6+
+NtR+PyoldDVzls0Raj4l~nGP-1zFBw=euw-D#vyU)Q-L2QT0X+<ng_X?mCBsSPywl~Rv-1|5WH`xilH
+nx7NrsaQCmBxi?!?Zadl+&HE18u`hLa2@8BQ{sWH`xil4sLEZs8=)-hFNnoaEh^oumF6HjSJ8mOis+$
+#9a(N!RbY!o%guB?A#&Ov#v%F(qS4#+0mm|8CZs7+W&7WNgVSS~8243?~^*GMr>M$#9a(NvF5{+7|&}
+$-t7e7HZa;7+^BMWPr&4lUcrGOv#v%F(qS4t|^&UP6JB@mJBSpu;hFRV6}-^$Yg-Y0FwbGvyjP{k})M
+?O2(9oDH&5TresXXn36Fi>;8_@b3$WF#+HmNnKevi4U>^1BT23#`RsxBelWYjNrsaQCmBw%_Om2rwTa
+70*S@e~OJ+5bF(qS4#*~aH8B;Q*WK7AJl3C4UILUC5mG9oXB!I}mN@g{a;UvRJW;K(MBqK>il8hu7Np
+dC0yzeobWH`zGefOrPgoc$2E14xthLa2@8BQ{sWba5s7EUspWH`xil3BoHB+1^-l9=TtMwHA#CL>8kl
+8hu7NpdCWTzgYUE1YCF$*g8Fl4K;wNRqwl>dkT!BT8m9laVANNv<SazkL))E1YCF$#9b4B*RH;o-*t*
+(zQ|yaDDftXM~283@e${Ooo#TCmBvMoMcur8A&pdWF*N*l941MNk)>q`^R&Qf7hO+!AfRHlUdSaB*{p
+Ukt8EYMv{yq8A&pdWF*N*l943ucJlme?+zJdA(N3LBS}V*j3gOJGLmE@$w-osBqK>il8hu7N%C$l&(H
+QaWRx{bMv{yq8A&pdWF*N*l942{hRGn3K_r7n29XRR`LInCKl`tLVhrL5A{j(7h-47SAd*2OgGdIE3?
+dmsGKgdl$sm%?H-EC=#4KNO5$RleBZ()H<Vw=f-b-duILRzzGLmE@$w-osBqK>a=eswY5+X|bZGrnmY
+x^x<-wE<gV@Q&bBqK>il8hu7NpdCW+Eacw$-6f_|J=inQCP{WW-^>)Rx=q%GLmE@$w-osBv+Eowa*aQ
+gp&*>`8?mf;e-%YGOT3QFd0rVoMhH88A&pdWF*N*l941MNk)>4B$>5K{?<~O^(JNwlR+edNCuG%A{j(
+7h-47S)Xt{ZGNplee`VfUdEYhd`QL60#NYkv<-^~-Trvh31(6IQ8ALLOWDv<9l0hVcNCuG%A{j*TZf(
+!s^*Ce{L^6nE5Xm5tK_r7n29XRR8ALLOWDv<9l0hVcNZzgP`MVy6WP(TrkqjakL^6nE5Xm5tK_r7n29
+XRRnH5WHzcss@z@HzPH7CZ8j2{_4GJa(I$oP@*BjZQLkBlD~KQew~{K)u`@gskJWY(M*KQew~{K)u`@
+gw6$#*fVR;u$(JbY$qr(2-fEWY#GeE%NuhcxKIsQ6r;9MvaUb88tF$WYoy0kx?U~Mn;W{8W}Y*tCNft
+`TJfxv*^UAkx?U~Mn;W{8W}Y*YGl;NsF6`4qee!Jj2f9WNk)tOeJ`F_bz;=WsF6`4qee!Jj2am=GHPV
+h$f%J~Bcnz}jm(-PqeZ@VEwWj4V${f}kx?U~Mn;W{8W}Zm)#%!?0y;8uWa!8&Ofn0Tj28L+-oLZ##Hf
++0M%OkS(2=1dLq~>=3>_IdGIV6<$k36YBSS}Kb&~J<yEmL1LPv&<3>_IdGIV6<$k36YBlcY!cB&h;2p
+YcearpAM*>6kO1>~$h;5eKeLPv&<3>_IdGIV6<$k36YBSS}qjtm_cIx=)*=*X-|^1a_BX4ah;Ix=gMj
+2am=GHPVh$f%J~Bcnz}jf@%@H8N^s)+8A%^0VJ1W)_|pH8N^s)X1ojQ6r;9MvaUb88tF$WYoy0kx?VF
+9LZ>rpZ)y2S$SgA$f%J~Bcnz}jf@%@H8N^s)X1ojQ6r;9MvcrOB%?)szSnG)o)|SUYGl;NsF6`4qee!
+Jj2am=GHPVh$f%J~BeVX<Xpz75M`rPfQ6r;9MvaUb88tF$WYoy0kx?U~Mn;WHv1-)FRip2n&-1<KVey
+HfBSS}K5t0ET14agn3>X<OGGJuD$SguKi;#>F86z@A<Zu0vS$txw$XJoFB4b6yii{N*D>7DOtjJiAu_
+Ckd$QTjZzYTlb%(taM|JxW~MY|wx*YxZsX7_`w-+Lyk$XJoFB4b6yip;7bV?@S?j1l?W&wQBGC&r456
+`55>Mu?0M86h%4WQ52Fky&+QWXQ;nks%{PMuz<EXFkm86C*@Mh>Q>!Au>W_gvbby5h5c*W|@(ZAtOUZ
+hKvju8S?veMl3%uLS%%<2$2yYBSdDIk-;H@LuQ$gK_P=e289d?85A-o<afWz)+|3UIAn0h;E=%~gF^;
+~3=SC_GRuq%3K<kKC}dE`ppZc!?|$C>FF7EOxR7xn<3h%Tj0+hTGAoRX2N@4C9%MYoc#!cR<3Y}L?q>
+aoaUtVE#)XUv85c4xWL(IsFfuEQ3<VhqG8AMe$WV}>AZN|eEI=_NWJt)6kRc&MLWYFQ;vz#qhJp+Q84
+5BKWGKi`kf9)FKXYtWpcoP|BxFd)kdPrELqcY8k)a?%L56}11sMu56l5sKP>{dxuQMx93<(($G9+Y3$
+dHg(SHx!E!;Y)7-^9ijdCV^3-_N%0=Q;OdnEUY`yq4o|lDGx&c3NnV(V)Le{TF`f@xOon`5*t|fBm=r
+`QP9F`XB%M|NP(o_V51zP)h>@6aWAK2mtey7DxVo83Y>x005K*001)p003}la4%nWWo~3|axZdaadl;
+LbaO9oVPk7yXJvCQV`yP=WMy<OVrgeJaB^>AWpXZXd6iU6Z`(K!z57=T>Wc%o+PI4X+YNe<6+1Q7vJJ
+^bk&{77Bbz8i8YGoK{q=qHSF()*?CN9==RMAQGoEhu`bWdj!(>L&Niv${BN|TU$v^Z?Znx8=<&J4?<y
+LB;C^03Ibd;K(QYnFRT{l`5!W&D4lB_)y$cZ{<=tN5|fxPD@R%XwZOhx_*C-P_PT_N`TidGu!aI|}44
++jv_!q{WHuq-^6v}q5FZ=Vu2aP3<>we!?VZfNl3%U__lnjTTC6lEdTQO1t#6PHINiP7H5O#_)dck?$Q
+M5(YpXd%_s%9=hBD)i(HEn2@b`lKILDY>!2w!yW^GLtive@0<<)S8A0p~-S7-H{$fcoMq&!&u;JQ_89
+hp+=XG6RP%XYj^hWVYZ?vJ5)*!tl2`%qS+`}06diiYsVxU?L*A%UYr_-Lh@4~G&Z0=xO+z|aUb4(V(S
+p0PtoA~+KI7G=ygx{P(Ql455OA~-I78#>=_wdwctf*Nm3tZckmuRKnS7F7%SQ^HIA!B^}&E#`aW5Ho3
+EA>&mQS}oMrKB`FMw1IE`EM@5m(UJw~u3sIo%)7KMW#9c9TkAmZU<I$1u3>c^AiY?S9To@W%(BF>hRW
+HpU5TCB3gJRe0wIrG`#VCFTGFBVsztPPwj*$b)MQTij=>rkL7X(yf-Z7x`1qDbH`vTy%a50%l|gI+*h
+480>+kv6_h&sJh~Fnw>j3znDu_A&HD)Thq_BnkaPp#?eMV_AWEtc<aJ8XD(AY8nH3`)M$^{W$pZc0jb
+s5qa;U(d_;|E|qSlQ(03}RBwu%u#WxSnho@$f8kG*Hp<Ik>p8(~&VF&YJz=n2q~1D@QL9C<lbX-4<Zz
+RRoAh{dhJfZWcVVVafT7%ezJC>uv5i`XO?0Wwx%zc-o~GDUs?+HdN;r3j9gFK-uNj*HivnX&(RwYhZ`
+SLcV^y8v;kyou{+)iw4yR~cpQNVr^@{4=t5V+}-{}1b-@m?VMejEPi>>GpyvW+JU!R(|)}rR$<yrQP)
+$}1e@S*byP)h>@6aWAK2mtey7DsuqS6MRz0077d001)p003}la4%nWWo~3|axZdaadl;LbaO9oVPk7y
+XJvCQV`yP=WMy<OV`yP=WMy<^V{|TXd6ib%j@vd6eb-k^9R##nTYCZWB0vvCw`l?dL6B}9g3UrpOCy_
+Di4;i6j<MK(?+hvV(!HSdgDr7}hcjnp4*lkrv)<XWrP61pH_+|;+UR65nIwOgR&^e>YgMkn%X@TSG=j
+0v8jDb?2BDLF?Lg`ZEOy3vb^*UeWuZckrN=5kU7AYil}nO8f6BINFw2qz=<^oJwY1R@oM|y&@`E7^%W
+uJNG-a?=x-wh0xVU_GNiTj4rO~yj=nRDO^xfWl50HU$rBY9vFVgcQ`B;NCV-l28XU&G})f|)`QdX3c(
+p~SNwx$K=DU~z{1SMgmDC*w#7K;K@%N&4QI@9zXi}*e#SfxF=>C!8s=g@0Klwi>)k5)EB%(&C8I%yq_
+?{4SfA<B&4rb9iPJdih9ieiJ-(E&wqVo&Kfw#X(jO$8iwd>ai{gCR+h3TrBz3$<q~Q%@VE(ak~27IP5
+@5VI^W%`EWmS0n~BNTP>QT@3(Bexy1utxLJ2(vGbFrY7f5Z90?@8s$8%MgW(_2FZvG*+K{)gKL5ckKh
+fgC~?L<f=uHczw2<^@{tMVlL>mc^XQIYIiU<b(>I%kXsAb;21B8EHQ*fK#yu3}N&Y?LPsyClf~_V@Mx
+ibmq6*7lTUsMlf#2xcr)6Ut4m&NnzVQUzr|~hYs(LWoh%t9@d%(1@6yo9hF@uMT$0v>daQP@w3aXWU%
+Gu1Hft&B){?}&&I-DkMV=CA<d?7{}z_{-us@c$rtyFqSW3kCUllTm6Cxi&UA4qr_YKPLaEo)GqnVkC;
+wU4qyZ9?s`=ANReNlSus+FOjB!Kb19_M7J<8%(w9uvE3eY7n;ZtOcKyKBb8ig#(?#-5m{Y%IG&2+3np
+OC<nbG9HpH{SsgcPjNxbmPO^Lhe^IYLpJQ+cl3}S&M)^p8AM9U(Wx5aZXt49i^?#N0Q$2?t&o{?j<|l
+{li3Oz@EXqbYmp=*c3TsKFcOS6n-jB|Q{oF{6Dt)mImA1K{Rx!;6#q22aR900nQZf}mt;NeNyr#2q@t
+$+tu4&p160)Tg(>F}=J+jDf&bH-Bzk<glJY>eW*R(Pfb}448ns()K@#`x9RU{$5;f@LVdizRxi^U-Gf
+_gOQR4(=_FP<@Y<l^m=)Oa;>K*P76xTC9M7LVqrsiTGn!Vkyb!Ys~`y|UFTYivjZ?S~ns*{M_IsGz&)
+EIWxST!+)ichiSU*Y;X7`$zu;$WFRV$!)ONrSOHS?qCAHJ(J$#kL$0W7FXc6io{LEOCd~pykwS*)c0{
+}sGv@*v}}UU3vft{PaAipxAERjd8X+Vwo?1hJaZ2DINTSY77O<Np(pb&S@}?2w05?4k6irg!mVjT8dQ
+*FkeDnAexrXFc@Cl|SQLvwL{@`B&XRusP)h>@6aWAK2mtey7DxDy!T=5l000Uk0021v003}la4%nWWo
+~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OV`yP=WNCABa%p09bZKvHE^vA6SZ!|_NfQ3fuc(W4I
+=ICc9OpK6){y{Xhs4GfFN%Dt2n{n0bTUkno+gC-_4`y0FEh;8m}I5(Nrx=M^i)?>S3ULC2M5_7sx_{f
+ExK;j8?8=*YS*{*5A*=-LGFOMV?noCj%48YR9BuL8PbjVL;Ajh=gDLiNY^n+lj}<n7;-^V%B{9G18F3
+7UWjkPS5u)$4asEi1bs`G&~>IF##X8z#G{@nZ6Cmx?v#$!T`e3VJQ^%4$FwL%Gq33aqQ-{u^#WB+PhW
+#YCV7dfzE5qY4XG_cp}z?)I!RpxM$5qr$IOR?GprEZIQk3SNq?l}gboQB8ZwIR7G|u1<@sSMUk<dR7i
+_hm7J@=GG;=8Sh8Ai@t`m?Jo(v=QFoYo|@Q#$m{)zJBaKTHnz{7D^Ya{d|T-ord)uU?>LQ}dDfzUY3-
+E81X7n`r8D}qpv6VjB|!tusZm#j!<sR&&vu(#HILqg&`%l;;Gh!r|6RaVlL9E${B=N-fP!<$pySb)+C
+^5G4=+F8mp!RDkM$-uH5!}j<AhB)XPu%dyWSty1xzX%SXrH{?-yW3uus;ztaSZ%kft?vCB(8AJq3;#u
+&<YWp6dW2JHCol^r4hA=kcKsc8RBO%aX7`@8zif6}jZTLyZ`)L*yK1}JtoN>~ZMy5V?`}Jd5>ZEpm5J
+GySw>!)cnFpg1xtFuIMNTJqu;~5Ar$mI8av+v-0TVoM3I9KnJ)J7p|66GJukLw4El!TkOF0jG}jW?0s
+2zj1(%I~ivYb_D$=V8h@9Yy4;OT>{ZbCW`qEcQ7pbN~!=yJ=>@6QxD&@n<%W{RN*TKp|A8NENo|se)a
+=D>a6Do<BYvu@wuk1MvpM0ZN?|!V09o<zp#n)R6*cZvJ_Tj+mM!(biZ=;x_2R=*6`@3endfmThcDn6m
+t=Dbdw)*WVz6u-3U-Y)*5A7_;AKFiq%#=GNj|RstE5`}ZS2bQr$&QwvBz#S~Q~W@VUL}Oru#gg6YhJy
+2ir|QDr_j$bOvrrS?4(3u?FkYs)lPY?noK#JMB?V!nf8_Ta=Aw9ws-Zee^KpL``vf#M(5q_^#z?%rF?
+vn>-5@<{(Yl^=W^-vG<t5`+Q)JwchhV&Z+bWB%`bDgoa;Lw$`sCYvC40VwkoRIB9!5oJ&Y%Y)bC3aoq
+j(b3V&EQTOSfh%9&0jV%#_b-v;tX7_JvzXCiQsE7(Y6Pv=6)@;M(7sY#5{l`z(#`9fj6%F;^x$(GHbd
+rJjko2ttibmSjm`JS2DN8~GKppeL{jxmuo$K#LlS`Zp!Ui#+vGePsQbjSFur3fSCeJ2>roRK}@>US~5
+3TfZg1=sQ28OjNp1Wbq0gBe7WmSf~&W2R?ENAvl-G>JNrlG3B2saBtbYr-QL82_jjNG?f^&*u+?xex{
+~xk8Q2#E)^~G(MbZE`ZBSUP>8fF-2G6$GzO<%$jLSjTXY>3%RxXqlGQszj*P&uA_o7TsRh7U%<y)QDI
+FZKoQfjIgDz+(04{`b>0eVcJ&OE*;}2>)>`%+nhCa`>(6XCA3I@;*Fd0hP8=@<f(zbyPM3}!rU9bN(v
+}<>7680rf$6yD078PZP;>B8cnO>c3TBf*)C0!I=VQfHbPMfV=TH~9H3(3#j<@w-Ag|Tf7<nj!XKh?0r
+#9pNOmr=Rq|KR(5xdeNs;*xGH4h**7@2Y0?5MLVHgtYY$7hNBbU<^2N8m4jTXX@45_BcUhl7Oy?l%&E
+Ywno57<iUWmtlgRT85`RC}O;C7xH|ZO;8KC<}7pDVI{72wE7k{N>Zo2`3o}P*16W6KtM|`@p8@jw^Zg
+hOJ#2m!-MqpfmHOn<@71NFVo#t&$YkVKARMg8C_<kgIF4&fx~GA))(L53(hfUkDO$fL1Q@8lV0#Ivuf
+RJNT&SBNe_&p`Gn9`B~#i`d>}?3usp&!I_#Kb(w_K@0}I(Gb#%PQE<{PwEoClCSnhFQ|2R*{+2RIF<x
+{F0Q9hq$c9`N-NIUi4yV7Z)unjea*{VXPVX{20&c*B=C_MpaQMh8B&$CYzyL8TF3V9Q=Mx|W-CxtUDH
+d5mTgv1!yl<A8=%|_!sV<uh5n})*$O*uU7B7O1P12Jm^VZ7muyyj_V9?Q+87?(>Pr=xPU$>}HzLZd7p
+YfL!3au&6`BxhXKX<Praf8J7N6O;_6DBWywXySk$*@E_f$jcs@l~^KQ2NsVZYBk_<HSjpf*vr8v9vnQ
+}!2+nyCaP*%v}`74Su~1-!YsHo@L34!7&n>iLhw$_Lz>KdBd5@9#m-L5PNMw?$DFIV&aDeijGOIoO56
+5CW?eOVcGAe|_VN$4frZ_D;6qzW$ql<3yR13tu|{uu!rG(n*)?+Q0WEI8Fqs{8Kdv2D<SfjNlD3@)O`
+@Y98cH1+A}+O2>OG_KclU%25Ai(|3EEHqYTg#DQ^}wWR8;Vylpdf}0OA;ZC5+4H$j~#nV+rV>or>Blz
+Q0&AuuAo9qkg^~KWzE_z5J;V&&DxIuF&7>Cz*--^aMFGA0s}rv<3QQi+$x&!M>|*3VFIu$OQ9;_Hzbv
+YBb*g=ETsxADADV;(q|lAKE_$=0W*?70k8r)4^OT|7Bo~?n0g|nlo>E9)ol09%v6d@4Oe<6VMUArviE
+pJpT=FE-r;Wi0xt;k8!x?(|9k~aXTO9Yu{8Wg?&RI&f^DyfUotQZ3txCIX)HuTep#Wz<<a6<euQ?yVI
+Y`<<1_B$;mQKeic-F_OY-X3e~MX#txpTpm=bQtGxO(BQUBb*@1z@|GZ4FN=L{KGCwKr6?L8W&Caupy7
+aC4V?md^kKZHa9>1dBGvvb0v6=9PLhki4Zcc|bjKm(@)FuLpwC1FqX#}RK(d){x8pU4lz0&v7%dKLxx
+;L!6ew`5*;1i31EJ=;>1Ckp1Notf4fiEkMU2$$gZkuKF{b&SlCn0w2<<2YArAYfGJ}|w%`+ubWg^|vq
+()+ugEU$m_sPvQ}DOUd;8;@e;?cu@5d0J2SCs0cR1QY-O00;o{l@>=iiKU-(1ONcf4gdf<0001RX>c!
+Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJow7a&u*LXL4_KaBy;OVr6nJaCyyE-*4JT5Ps
+*c7zsrImH42ilP*p302ESGAP9)6sCPn(Jr1jyU2Aup$oJRZtZgu0yMcQ;KjGP#Z@>BGhpE@gALtDpMp
+GD%`h)3W0KM^J{~J{J)@yZGZV(<7f8l}!(3c(p*D#U)@E~CHzTHNGyUa)hZop9(aLLD^z$|toxZxD~1
+z$0cAu5nQm=aL(pQ6z&3-JJ#OQGpig;FIi7I`LBYIhYfgC4Bo1hIG(Qx+?X>8g!^M!yN2+uJ`0qSW1i
+ZV<rS`f8Y?M)itbmP%g=qxd?aL>s#Ki7WsUrhdYV2QP|m;RcAJ0V81+n~fB;R6G(Z;A_QHY<2S$B53K
+GH)3`Nv5dfF0u*{&r|_hN0VcfPq@wt(^tc~eQzSgf$I>>awt6Sx!}JNpsEH~(pg=|G%%XL`T?!s^7ll
+S(8bWK+n_Q})C9=qfz)%u=BJUj_r*nJvD=JME_~>+Yw`GVeplUamvHYo=p$%*j=@<ekhuTH%o8VZ3JX
+H_4NN_i#KK&p>%Jhvo3)TpcMn4K#gh0OVeYE`g__T!X^clW)=kxA#`FuydBsHB<1ydxx4XFz~ptKYdC
+MM<tFd5AIUn!#78;wWHXG?!LT22Rx1q>hO(1lrdz8v+R#@#v0p60X1#lQhrAnr7_YL=<I*Vre>tt2Tu
+G>iv2%l(-OtchXZ!G^se71~8kO$Cg`B#bYrVj#s!k}!qjirxYDK}gerH^r%YsQ(J}C6v6vE$VyMX~7>
+Ia$@3VKs8uU_>lVqJq)B&E$B&Ytm~vpvF(qYPWz_wd%FYhv>;2>1B2<8GZ#y}R`ZqIf@3RY=f<UZHdH
+ZP%M3c!nTxw&N}~ytT$4cAai5F}G_%B5tJU0qX}z;PC>e$6QRB#w2AAfRisZv;#cBRmtA@tF&+PU&@^
+_BC_D=TAS#7(np=wT}E!;!<^e{!xboArz)3Y9G^AJ(RQz!D7N`bXKIYrCI{F>Q;%XzeHi|#ZOCnWPr>
+_Z)uIv$(t6CPgPtU6{10lIyuA}lx+Sbn&N&ZP>ypjl;&u@Y>Hr(&g3QGs@xSK^c4N#S!3g^MStT>xeu
+g(gxWC0wt_zL?)}MSisNrpfTiBb|a0&0HlP(MyzR0qaGF`drivHu<WKAkhpLCuKg0$=cz}P;E$yEHI&
+;5HBr<IiHP1z0a47gULc1l^|5sC*@RC?0pl_1?IGWFesxim1a_+%IORZS)k7tqMO4ZI?Vjk@iQ%j;;j
+dm3!4EHsg}mPmwwgx@gV1ecQVFFH&lY3UD%98+qu2n+s`h_KDeC=yr%Y`zl6Tsuuft1S1hlc*Z*Pm$S
+co!<>5Mdsc4E=JKE5nPvs>4FdAXy+9?Y!JRjgX;g=HoOI#P@O4D-vd@sDWtl!V9kk$2-h8&Wvy3)iAu
+HH|oT96izZWhSxTjw(!7nvP&mh1oL?R3ExD{t@9q1{o*KVnF$=+p5x?Sk)Fz2w?oebf#s{4Y>T0|XQR
+000O8^OY7y_!;PfTLS<9ZU+DWHUIzsaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4C
+QVRCb2bZ~NSVr6nJaCx0n?{6A85dF?yF;YGRbg&0GRVOv-2OyB5BY;>?m0nLP@9r#EH8!%nNw<IfJ@X
+?}2}rqH2?>v9-n@C^nX9YwAN2<JqX~^i{lO$2P;Wf#|3w#kuWDDcSTjwnTuChy^-azs9X*(zQYk@8Eu
+Yvr++=HE9ec{zbLNeu46ae_h%nO9OSs+f6DzZ2OQs-y1{(eo_AV1!hH0}YwakQ<Mmrqs*4W(vz%(;<Z
+_g|X&pD;l4)JX&VFT8-LaMc=dcQ$8?REzcXWb*}Dn;|)>uAo7?GxwwO8Q27E7KD7)m`HwEJP2&ey5pK
+D=RnDA~5vi4b3XQHhSj~uPgk7GU495)VovYAP^)~GPS~1LEXYKlQV^X2J>60Oi7v0WI2~^pC3wj5<36
+gSj2BkE{iIdN}Z$LP?l%AaYvEwCrcW$Ln(C6nk~}L%2de=!DE@Rc1*(2HrU+yB;_y=i4R0*XaK*H|CU
+(dKI9JVhlOsU&N15ov58u#E_~pRc5g#q1EecbXqP=kMrS2BRhpC3JJ>avgFgU7q0e}Jlroi$i&8a#0A
+KntT6~@^7u20R(wFXh-kmHSZ{dqp;1&(uBgxHHNdTiNE3~gb8~_i4dH*vay1mhOw0I2choi-05XUr}&
+Z$eY?tC%oFUQ?E&6e}oG#*4~F7r|1Am%xdUnDL7xiL@@Ln5Sd`_doLUI&6Ir?q%uv^ir;RZapgWLy2G
+9x9_(yLu6F67-g2LE88xJzI&lgXj6CyFfYhw~499q9%Rlz)9%u3N45cK9mKZhsqe+q@Hm;xIT0d+rH^
+^+O5tX?GDj0hUL;ngUQ`Hm(NwLR#*&86dD%BD#NsUG|bfw2GFk6G9?_A1NKkMer8R|_G1lW2bBv-5{d
+avlDcD6H1_uJZ4py@@v@k5|Jo%Sqhu3%yjJ>jN*?juZpTTzIw7psCx;c7XtCkfV3vJpHBF4>Q&7R2?;
+M*XdgWz$0o(-|7f^p?`ihd#h0IxJmqOc7`xib^VK9?5R$r1rHKSE^o?kdZ6U&h+(|USc|NGmIcDwcM$
+DhN08oymPDZQraL)7b(!T2WUVMY_ytFVF+#U5t&-t67hSJC?U|5Lv&FV@ubZh98<Ur<W}1QY-O00;o{
+l@>?9*$_0~0{{RH2><{)0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJo_HX>Mn
+8bYXO5ZDC_*X>MgMaCwzg+m0GJ5Pjz>O5$N)L)anFN}Czw0SHV`xQUA*6pfJSt_CY^8`)-9_Ure!ySZ
+eBNwR*T>vDYRRFx^0uODi49(qIS_u8G|v_q}_sQrari7l7PG}|zZtXxYi6tzvvBps#ZU#XO+8@r|mwt
+DFp+SAfYM1JBYR%XkVOhWz&2I7b8T_m;)Q=0CyjD(j)JAB%$vDpWJX>4q6k1Pw%F|7_6;>)Xq4OrU)u
+G)C2w5!zk@ZoPjTn(>NQz@E+SVt3fY@ayJD`^|;tz7NAu@zUngN0}y>~|VVwYD;)w*-csyrJ>IZ;USL
+hcrsAtgwfmQev6OnZ)lw?4Ay0N0HEEIhHPmhY+5Gj{h_k`KgIzatNlKj!{lv?b)X8sNutKPJMP@N)N2
+rqMGq;rDTNUzKmEqCgErsY;JQ_svC$*3nJ7tfX~#uCzkMsyPw!PSm<57alEZStdX%)5kBxoH@6|N3ep
+Es=!QLJ)~`x%t~4g8GuREZ#}5Fa&^^Y8R!lp`$xhXPfLQw4n|&V5XVe@n=xcK_X%1(Ld&EL%c+tNcNv
+2yR0gS4w(Eb4802p*8?a#<)wtD^EY!TRZd$VC@I;HMtLQNVsC$nCA-fvE5JfDn5(@vde%6ybKh<Q!ql
+f(%iQv)Sq_ChL`m%c!I9SEwJHsXoV=7=RGiUd|<d-z{HR7S6}dXaJ#^qyow+V~pnt;FnL`d)MwDChpx
+F!WKqMt?OBB=mQM7EF=el?kA`${1UtmT^9W4w}fleb;E*zHPj}Z4k|;u)KDtGyL?Mk4U*xN~}q#9$|?
++8s$Rg!us&k3+qqbbQ^{uckQj%E{%;#rBbAX3y(r9^%@DD2O`=?&37eiC3=w1aw)Og%Vou}N^0cNYVH
+dsnCi=PfLBM+NBEw5+B<7m`_{qp*f@mTz7o;i2;0Z#y@;@QJA9>DI`PR3!S|9#0hSd4s6v8Ms^)+E(g
+kNxOmqQJ&PI<>J~kWad!{vqJ;K`<M7jvXq+q?mBP6Z!S$HkP3*}6flmRH#YrnZj8!z5}xg*iqB_UVjQ
+CMLXDReayWoC_C#u#A~i;xpS_-L!3;-&82ZZdi^u~K~V`0d!+N>qJ3WrFZkg^m)PRrtN${j7h_xaIaE
+yP|~0OVz`G2K!ARba=BV>r$3gyX-{hVD391L?Dni6_;18mHEdty}Ba8AJEO?4>g_7o1@y*U(DE#&}Z5
+x_RQ?@7klyMBQ;JTmi^A^YuBA88G0<E+_MZ4W7eLp#8sH0P0xU`?a!Gm(|rGbj#gBwMT$C0{{c`-0|X
+QR000O8^OY7yAp6}pQ33z}g#-WqF8}}laA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT
+4CXZE#_9E^v93Q%!H$KoGtAS3HReNDhTWy_EJ~s7b66K(<lDDO%g(dR4P)?Jh+A_Z{;QDkZ7fz1TZ5@
+9mqfUhn;bX?Pzkv502jG6^wVtY$y(L2S?KVWyQcGTS?C`gZVzEkGm`+f5;Cu5p7#8waPcZe697*P+r%
+TGTQ#!nEMu(h$Mw(!B^n6SR7doJkB7(w^uE(m|-=lQxXs=|YxmXWFZR3|vKUWOf$1JYMdX8!X6L_T7^
+rBWkSoSuJx0FJw;25`;z5*=$7xvi69ZcS4f05X?2x3n#UjfF%37w-012TW~uZUtT*i`p^b_;X3^Wy6J
+!e7Tuy2tnPvwzFUH8rUj%r(pI|Rv??a#L|<fGBO~lBmHXOf29Cv3ls>FBDT3t|PeB|9%XB;8SgtgC=|
+CqYxoc|4#GI88%C#(<2_D0E_P~f>8ZDx9+o_*N=`u_b%vUi2tb;g>W}8J2W4(#jt0Wu(3DKp*T`}J)@
+@t7prrc?+WI;~I+MZJ1a(gWctP51)K-}h><bmZ0<hp79tB18#Kacq`<XX@PWC^96j~63(c6j>UbeCDK
+{q+&4eCXrzn1h7+RdWjxhR<cm)bm<v<729=>p~v`#*S{s<I&CdZZrlq30poo2$$dgai4mg$DQid#kSV
+tOWFMl&vWxERjKh6{W~8cUNzn{WWv%j4-USe&mW)nLFT%kfk(Baw=r4k$ok;hfcXn5xj>?M=4I`RGv(
+hN?KY5yRVwR*%Be5>SN1cyJBHleD4a--)~lF5{KESUP)h>@6aWAK2mtey7DxP5z|Xb<001`y001%o00
+3}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OV{dMAbYX6Eb1rasl~loQ;xG`s^A#g;X
+(h@6s~&dI9#R_GXd$2^sNz(aWB{wiMz(2{{rY_l&>l*q-AIw*@tZd@@7Za$FF$k_KO`wll2M%IG2KmO
+qu=xew%uw|v1OWBxsh5Z8kveoI+~h4QYk?TEnnF>?37z!9eb+SbLowx63}kz%#dj5C1Cgb%F687l3A0
+#Kn;A#-j!m{2<>(UElc60(GE+yH?~=TnC8YF<E3TcxuR9wFn%s2Y~b40D7E!;HM*ujuh$2~MR=DYr6>
+znM;SY|uUs7`85!-ZTpcjqmAn3l2+>s77n)18v2sT@gbqD<L-X2ijcyS6w#F-z3H#=K-bSI5K#)|)l@
++!Q>aHy_IkWa}=zK`EIZ!DyS+1lz)`wD_gs#3Bi~OCb<hl-_gRU@d7|XNWxwFU*>4GNgFbX}eW{dLk!
+%E2#$rD+!c1*(2KE&MiL&|YzBtJDmV*~m|{vol%eyAPnM}!95{<+$Tv4dIZO8C$}?B0jOYcySxf?f8U
+8LYgZ!N;f-N!{>n;SN5)h)LgY{%FN?aJ)XK4j6!?r=)nCEeeX#B|Sx17Nx~<2rRt7E*w5G$=zN_5MwM
+Ww6D=Q2&Qp1dPGKamrRmk8QLEwMH=Thjb|A}G>@_(87(GJM)O5BpXG5E?lPY>4r*Rf`Ca2RD0c=@;+6
+=h+-deD40h;HRkRha45LfNy{bsyjO^?Ga#0z*X$D5hhoVE0YtqJd=*>!;9vsk5{(|Mh;STOa+3nD6A0
+VM$6g<e0K9*}xkCidDLwCmc0G&q2?G5_<-cA2|uTQkd5&7z&IKBVJ-%q>MdaihN*w8QPwOV}N{9L3yu
+DNM9@mTa*oEkr6d=7xiy|Dg>iLHg`4I=UGjL08nSut7^&&6XFV==i8=<bi()?ZLd0|XQR000O8^OY7y
+s|F^<OalM__67g|GynhqaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4CYIW#$Na&KZ
+~axQRrl~mhq+c*$?_g4(+7YA_ExVsB%8uUR{?9_;FkZcrrGH7XJ6QxLlq!OrKzekoYY2pA|K1p*qJac
+9^?e^tE{lUX%O5;&9m?i`2k7v;%eGuDjwP~?snpwG#S}2N4#v~mT=9g4T1jcSCg)L|uLq}SAiO73?W@
+UD4$>iiOU?6_V-lby4Fcn3qWh%Th+TqvkjNShLnC8Yd_SCZQoYA`4Bfh^%*cxkB;i|2tPIN`R+uMHua
+WOogP$`P*SVu8Cw$Ge3l|)8+E7zqrw&Sk8!a_6=_9xAy+E`i8HG!cgZ)jfmt<i`2A&ru2D{NJ(lv`$U
+CigEOc1M*dDHWP5XVNwB5W<tt*=J*sUzkkhRc$JDhH`4wo^9cd8a_;yG-d~;^uU@es+pH-B~v7iWy;z
+y2}iry=C)_0hBcAoK!k<{@OyRdh$Z~>-Op?tEc7Ml9dA1zcF9=k2w(F@H+Oa76-Za4&<%Uc3@%D=t~4
+X5_pn=Nj}HK%&=-smt(i*4d8xWUKrH<jExyl|3ks)C^dpSpaJqQ9LoAer7yWCHWU*5cz^KX!?JE!mz+
+@0d-;ojaN8{1rsb)VMEvADcq2Vm1kmg~$7)8r*7}I<i&u7UXAWE2z64zp068R)?4#>hl$&9^_$~C1wp
+}h_SRYqI!%xH7U5)(xNPh?m9Q4f{Tn|-}VISYD6GAC_(mtL&I>|pxdbr&e-{&q3+Y0#yAdI%Ewr$P%7
+qz`2d=%F&kcByZiuR|vxa&Nx$dN<d-zixU&%LJB>4h^RF|8t47Tdmxhf`Ve5ZiRL1ch;;?Kl)ccqDiT
+|>{p&6coX*Xz4wrV?IQKYd5l^i(yi2dOpErOr1nf2&oKosr@5<V`j9YKMvw9RD@TZRR5`4o6FujO6a4
+)4@7rWD!M0MZRx4G)xyIP=>s+RdWr1CR$;fH7l2|#bRmZW)5BB<8P7HnzKSkGn!Kye^JE3nE3h%rodr
+rlMoh=4m8`kw}6y!=QK6Id-M(A<<7<aLu_G9#$WQRds^DD6CVEz!A=WJQ~;4RH}^8P}fzkMuy=S`s&Y
+uLkh5^=+Rb(or!76rc*)3PtErce7CKehe?P)h>@6aWAK2mtey7DuKn(m8Pi0046g001%o003}la4%nW
+Wo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OWpiV2a&KZ~axQRrl~!GE>NpU6=U0r>7m#QcdbQG
+RyM3S(TDA}np}Oj>R>&k1ux?^UwrMZ>>vx<l2!w8%Co(>BJagtFTwK&Ya6fn)xiB8}2d+1O`|-5@3}>
+(xjSE<85T=T+xnL3WWq`mn#PVMrMa+?E4L;eNMgbiOZa5(y@C_rGpaL0!so@~Ji$?n_K{CbhR&bvgE`
+_F7l}MGo5HZZ8%Iv;EW-x%&E@eEuiYd#L?C7eEfo8u2-RtY`L{STOpch3jw^$8x)TrJt$TI0mVH96&j
+Z{tDx*-d|gsGn}<I!627%qV#G+-pmc4i|*o<B(w@s(m~XH^OnB4`<!cM9wlc5(|o6QD5QI-@5c3@{P=
+CKdU|GT`CPnzkaKbZpcHRjdmQAKe!iqozpV5d|vBX0}~L+$ZNT_fcpBrXjI5z1b@@w1IeeATX3fpQ?K
+c$m!hP{f0`D1+JWKaa+#V2C1OQjE$eFnOMgb1-*las-X%`r&fY}rU7t~(r!q5`ay)0=sUF$tPr*u!)?
+?i2ExLR(c)?PvVflZ3O{=DdCy(E-V&CSrc<h4#>C?!;zUeYDJIO00w;pWVBUWskKX-gJX*Zk=!c_)JM
+cUhPUp~rS#Q1=^<T!lIm}+>v#B?506fG(Vk>5y$fd*~QO1&#4A3wh=`8hEDzByhqX0JS4XMmNa_T5xG
+$P6FOErw7Sf}}t%U;l1;2{WU+VHM8^$vC4Np*>3U*9%0z3;T)_b!2$_!&_NJaQlMkf?`|l&TH)QX31M
+^vJh!)$MjJyWcupfESM}=N=fi51(8j7mY@!WDJfin4KD*hS^ZTbZ$rO>$o^<KK<oQCbT0Wir$-Eb*`@
+Z?y=?1Sz&2F&6A^^=Q*mCU*vC?(r6MTS0sOaVV{g1nptWrfHa9>O+gxrnZMxzOLThT*^^0jQo6*6?dc
+5mu2`~^Drhtse#Eqff%YpGH7jqlVr<+2hOk_6T2#wrQ)3k7cKcNDP8C&x?3=TU^B^a12Y*)tI*ooWcd
+Pw>(BpD^+M_Jjr#%izSAhSiW~pdln`Wy~3YQr^RP6v#BJ42xg<dotM-tvt0?y{I#NV8BAGNmOVvCKcF
+@rnIH?$F`DhqWEQlxcM(O&AARy0D<JPPsz{(uI3+PVF#;mOOD)TyObYc$Jz7?y&?cr2SjvsK~@+YyZv
+ArDaaCnO|RJwa8r{H~63eO=ly(s%~d>Fn<h5rg#BHn98c;oWIfZ8@#I%0z!CUcWuxq$l<h#po+Z%hY~
+Dc5V7xTR*Z2@}^a3%bg)ABwy;9T#S!9C+mWm?y2$48|jNA{(jo1AxbWRzsxdSb<JXHHT_nrE`|!j-F}
+ZdI8aKebMYK^|Bz96Yq7`Oet8GoTHwC7O6(tZ(5a8$fO(&N8M-UYq&^z^98Q95_e`i$buju+pOMj%Gx
+a(jS^6O#nUB0q2fdU#8&3O@1-;C_WX+bH0YU%5lJB+D=TffS4u^pM08mQ<1QY-O00;o{l@>?VNd3&o1
+pok_9smF?0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJ*IMb8RkgdF_~OZ{kJ}
+$KUfQMwb_tNSI=q$BX)caFCEA1Od@0_oiCxb+FFZbZv5?Rlj>@`~a8?1lntRssf1|cIG!byX%>M%-I?
+Lf~(GTZvg#XyE7Pf;HrP${sen`&kAQSnMt@;?vv{a588n(!3|*^{N{R|C<W>XEF3EpGS{B(MlO1Ol+V
+%&7E(drK*YpA|ACA`ODrS~&F4$swM66weuy8{B2e)M3=G3SCF7QoB9b;tS8>FbQwc?}7Au@;7QuP@0`
+z9HfgxD;60|%IM&uj9NQP29OFLOfJMbgrPM1-j&clljI0A4d)K?g~-jj0YpaEtWK@`An70m)aT|aJ(=
+T4PSE7~PTNeN-#MBgy6OIQU<u!Ijv+HRPH$Bl?U`1VhMLVh0DuCt<{rElYMDC<b7d6@0t_22>eGQ>>b
+TKZDqYKF_H=UNE&T}%3*1QEi5#=_ZVr@EBLI86k)0fvuv_Y$OwcAEQHst`xODCya3X~ZG~3eH7D`QvF
+W2)w|gSK#4ksEjPJTd-NF4Xz)Do#Ec&9~cl9`VDU*m`YfN(ph>%jDWuIxi`7Ff0#gP@Cct<qfu)xdAv
+kl+-bDp`6ZFu`NDHCFs@1oKU!hp7~rlmYTqEyy6W|NlSj(F+nWqJ<1uvaN6>;{Yc%P#ANs8k3?D|r`*
+EiPFqSggV%kh@lh<49V90rZJ88>ExL%kn{Sl8h!~{JXX5v}mF<a8bI|>Ar$YOQU8G3>L6t5SN&4ylr>
+wq6bMfj#%ym#>KdvUrLWplnoyy>k{5vn>m3ID5yM=(ab>pB>^>ji--!c`DP<a*aawruEnS<}t34)8F>
+k$o@d3_c#&M9vBYM+I{zSy;po9#lvtN97~Okx-bA>?lR4$X*oQ$wJ}g%P*D}!q0GC(2M2V8vv6mWBrx
+ug)(Prkj-IjI&pf&>3JJ*I?oxz8T@<(aR$$s#F;#25@+&Ug}4gORfwzbT$Q*g&sB-5@?4F$8qd{;tMO
+c&xO&D_@V8ntG8=Jq2hT76CR>BJMxD12*WkG(aZR3U64&H8L7d<@L7d>ZDREPtn-VwWIg2=p=PcqZp0
+kOwdCn%z<~d26<T**4<T-~phvyvP9H(#*S7R8(#hBJ>R<86ii`iiQlDMQ7Dp?H+Hb1&^iEE#~P+4nO)
+H}vJpPN6<t+JV)np(XOk5+T}*K(h#xyv@+(6Bk=N7GOFOWwBTjk0Fc*L<2kyK&rw+KgtThtZx1)tCN6
+Og%}bg7udBiJiuGcld9XhxXs!Tb>BR*!}$nNLp(#NhNzFE?r^a|2ybY+cjg?j^tK^FnSSSE2-FLJIh^
+XU(Ctr-OU&6`bslQ-FWv$q0k%O8)d_257xck_@R2g`49E}%KxDE@7U7bZxr+I?cUGY(%#S6vfj_y(%#
+S6vfj_y(%#S6vfj_y(%#S6vfj_y(%#S6vfj_y(%#S6vfj_y(%#S6vfj_yHobpSlz9ordOz2D_q4E70y
+up*;<A>2qeWv^LJ|q!eYhEU2{@=-2lL;Ofa;F+9JT&L0{;5^W?3^|kzh7jc7A3<GxPVvYf7{&No#1YG
+UfXJj%4sU)!gHildJ7Jcwf}Kc(Lo-gJx2Oj7E^w9&BmZ?eJtR%WCj@X<41oGLwn<ceSjRDp`ZovL-$m
+ylL5+mi-7?hDG_Ewd_<$+oNSCJPsYwveyM}N6S)4Izj|@v}`|7+nqS~>9Cf)z8i<N>?kdawd`aOT%Ui
+XtYKf<zNe1X!kir54qubQL_&^|L%n>09>!I0k{;&Qc&r}Y4!891)962cwllYEn`gVqi<sZ%*{+(iy*=
+B#>EVy3hgg)S>*2ru_1-}}O7-x-{D(yDsAs#wU(dyQxV@)Gd`q|XeN6+Bwt18W?0$83NCM)6KS=`e%Q
+{v9K8;8M@YeIhSK0Uge(<X({0UG?0|XQR000O8^OY7ytbDvh3k3iGUJd{NGynhqaA|NaUv_0~WN&gWa
+%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4IfV`^}4a&KZ~axQRrrB-ck+DH)o&aW6XA8K^e2dyey6XhgA
+NJ9$*@uDi~MQE|duvdE*?XHu!zy4<Z!i#O%^tzv5UY?nGW_H$RXY~iJ`X2{l7!G><@vIM5!@J&RIAMF
+%ID`2H;ZE^27c7LH^bxp*NPg#G$Xuz`;E~L26s7~g4JXTMe8f<0QGpD=)L@YP7>)MW7D*IEvEUvvTnb
+Hp)mEzX4*|nOs!Z-FWCnd$B`IPx6jK&!n^35Y0jJl5&W8_w5k#%I3*9h;skPNGMUCna{j8Fn6h`q?Y@
+~8@^PD6ABc{H=gokUzBX|Rpp#dXdl9-JYdHqC9$XAM~#Ih79M9?xYPn6gNBr*n%2~g;Boxu|k2AJ^wl
+#2W#>GL45qFDG;j?LPjigdx@$MFJ&s3}wUhyoQ=Gl^Fr_sDt3Jro*&Y1mqs-t3vWv5Cy`MBqjee9G<x
+ASb<z{fJ7F1m3!xB3h2v0;#}Z#^z7m+*-#bC7nP>-B5+6TT8*dQXjZTVK>A*{UAUp^ohm@RtT}iAP!r
+EK(_E@Fu%Q9%%MBJhcDgfv^$>PUyv;^O<L+-hQy<7$O)LLQcReH5+{IBf7-hxkM7lAIGEqt>~9A1aep
+?0o4YA=VbYz>2ff9xJB7(&I=P$mU4R*4L9r#Xj^rK10YOHRnDo&w9_p<0dup$y1VbM->=CKW9&(x}V0
+1;c$uIRVlwzIMOD=mtFMtOiq-nvE;xs!neU<JK%D%rX8hX!d!TSywG4U;=7R<=~h6e<F6H2LCa3!^|r
+lT(Tw%>L-?Khow?GC_VMv_wp`s3>tE|IfFgF0oei%xVSP%;7+a*0Rx+EDKbd9?C2OldS*C09gIZZ?WT
+!><y9J<`t5Oj2#`LkozDHNhB+n782q3%6dXnOV&!(-bZQvl~r2_PIlU(fc&HK2UUNV<nmw>HWL)gn2%
+)wyOf4P05hz&^kIYoQ+1b`NBq{;e|}=Oqdno)YnIghxQIlSpdr=r}ed5IvT?um)leC=pLKztnEK{8Rf
+K$;If7x=P=odYlWNd)TGPPyg4<&_k0~VmuM{eDO%5lggZhq!OPe<u>DCk)i`T3%X}o4f<;&ksnEnqz$
++@^&8Nw$y<>prr^YQ{SMi|>8`A!|gb;<3YIX{{SGZl1bXV8tv|Fj^fu}w69^SwEe4c{Z4a;cB(=M<pF
+lo_ilPbRQhg3&{E$F_}&dBYKpuBzaDWB4CxPo|Drnkz;A)80uw2O`aF87z&#lBKf9Vfo?DU6mruHtpo
+C~Z%oU2W(QRBpQKQYPI>Gn%!|mGcL@hJ2xH=+Qx&9MA)lGWVL)G`J3bIoLnqb=5fMub}gQjIlH$tk|#
+UYv$XL6vH2KIxu>KRk}{cO&@}R`TfWwQ>Gf(wj!J5^6aFIij_i<HO$U>l`)FRZKU>z8OT6~OdETjzjh
+udQaXz5haWkV@AU2_9)Puq<m;RMgKhV#a2V>Eru{74?8eb&X6KyK#C0BiH{mMa%ci}2c3G4+>8?%fU7
+ONHM-d<P#?<oNP0n^pvovehXwWk>M(5>i<D4LDEzJ^zjMtkbefBWNew_N{>JAo&6an{9csBOm2cc)Tc
+hh-qF<;Jar+xY}ylyuC&vh%m4dsGMkIO!t($hj3^K5#-;r%LAUMgqjnt$6I`qqxdNq{`{h&?++8m&EQ
+v+*BLO9KQH000080P~d=M=Inlk*`w#0GhJ^05Sjo0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgi
+MXkl_>WppoPbz^ICW^!e5E^v92-EFUINp&FT@Ap@v#1~sC(`QGlm&mbtB#d!)sWG4e)beN~t0*q!s*A
+eJt-@~R*Ux@df<2Pm(hQQ*bxvl)-aBH&+Y@o}e(!t#@&6uw^P~Ur`A;4{{``kO`pM6K^!S?}|MZ9d?e
+V|(?DyV%@9~S@zJL7m(}#ch@XeQBKYsY}*Y6)6zJ2`t$N&4o*I$2m|NOh({o$JrUw!%A$4`&1zW(t3o
+9`Z9-JkjXyFVxW{F@KoeYjKp?fZXz|Mkb;zyI|3_?yRf|3RMj=YR74yKlex^7rrmU)g^6_|3QXkDq@3
+@zeZpXZ-lt$4{@{fA#78m*2hr_2XB6oE-oCrM~<muiyXizSM8Od;G-@|MGD?pXbivKm7RjA3yl|>&F*
+9`|ab4_usz%^v~~q{d$rge*EUUPal5uhx;-A;@iLc-|pW$9)JJkr~mQz*@v(H>C=bbJ^t!(-{E(U?>;
+_$_Q&sj`|+DUJ^x))Uw`=3r!PPKQK|jr)BE?2Z$JL#yMMWF?5`hx{P>5*S6_bf`1JnQAHIEMe;34ek1
+xOZ^?&;K=}!N<kH7x#n?L&1AHMnZ{W$LWyZ4`d_wAp{@W1@zmyaL6|MtGq$A5YM&HGRH)BNlYzxw*aS
+9jtcfB5SCH{ZU0eEIF;_kQ>7Z~v{N{?3j3{7*OX_`8pH_EWll{doW3e&4nK`TeJF@83Mu_pd)+?N2#=
+_PFc%FTVWF{ol3udkO#YzUe<czP@YY)1OnSssEr1{_RnJ{rK?B<onyZ@bCY)bG)DQU+%bi{ObMV58uB
+3%^$x0?9So-?Bl<G{)_+m(_jAL@q?fI!{fjI;EOMQ@RMKs!(ZQ@y^H#O_pZQKP9J{v`#T1I{kR|N)0f
+|T_s9Fr?+pI_M_>H#zuqZ+@He0T`14=<gWLbR&wufgAN~C2kH7or7mpu2e)fYee)0JafBEAdeDV0%FT
+eQNPk;WS?>`<tfB*i^Wvn#+qf-9YGXBk-<?lY;MfvOZ-+lSv>u>+`?Ei3A@VECJe*NpmZ@>KK_jg5q_
+5Q;hQ;#q2M)LbV{{O1QuRnhCPxHWcl7B1FzkYo9&EuPo-+lJ@mroz==(*$X-_`G(%fHp|XLs0t_5IHt
+dEFoS@|*wh^<4))zmxynhu_>;|J~OgKYse`@i!m8{mw`K{s(v3w{=}_e|7D*>+$&I&+p&-um1P=(NF&
+N|6{lEy?1xUKmSr~-B0zKPal8x`0`h3a=rci9pV4;yT|W@(%m}0y@TYdum5oO5MSTVclQB*xJ&tNAa?
+=&?)QHXZr{EC>bKu~{QBcR{ZXdZI*-R+{nh{ec>8`o@1IxJpMQR?`{%sYzy5qmg0~fa{>AedA<bKyfB
+V(*Ic@p#U%&tI@wa~tUM=Gb1^W2Uubgr}{?p?xuD|-z4}W?8zW4W+?7p~f_kO^CpZ1&kL+<BJxOTq%)
+&1XpIr*)A{<-!1@%MKx`1OBu;m5moKl<el|JxV;_=|t|+2=p}!H@s(Z$JO}FTVKvZ+`iU&wu)pfBfPH
+_y4;ytM9-4>*s#(H$VQ-KmPpl|KmsZ@9zsP<@Lkge)&n%+!6DC{q)mxGGG4c>-Ue(9<Nm1|Ha3Dd-p#
+-{^Id}zI!~b{n`D0Z}-c3`|R4!`s@+s`t0iKdB5Dhd+c>Sd(_+Wv&XuY-(8<Q`mOKJz1{Dw=X`E`_IR
+BB?A@J1`&q7Uafj!w&#vpbf4APYH{$-Ux)aw)|8-}bZ^s+yrMyfA$IYC{a4z8GzRmmPzWf#UOSzFP$y
+?qT?vl;EZ)Eb1b7%cnlVZElWDuXddpzU5lzYE_&igZQfBO06zIuM=9`^ma?)xEDOs>b%-X*XVS09D+x
+})cv;dR|VKkui1ocrxNYd3f94?fy6Du&B7JLDe<Hxy*}h_uHo)Rf9yknir&b<qeflTQ9suIxc4!@0b4
+ch%fEtf|2}!)o_GcO5>GZ+97Xx+S&Hxm?9(QhlV0q>JR8f4zP;pOZt%q1<)3>$=mP`*ZIedv30Ly5_&
++;0_<>si^MD#dR0jd2{P;=^15MvYv{%>*KD8Qa(A4>{2S=y1d<&vUkx|RP9PVbu_=bGt8;cmj6UZ%ep
+7+(<2|DJN0-!{2d|_4kFphn&&=3si*qyY*hdKVE28UrxY#MBe1VmhIi@i*DpgPc;q{X?+l+V5^DbbbW
+WiOk=k+<_Ulc7tGvG6+<H%j_l-P-;JM~*q<g;kv=1uH)C}6)^mBP*epD7uDV}cODeI>UV_MjAKJ(P{w
+&#AjuerJV)7M>l_kSrgH+SwQx!&%Qx{kKImDG8gmpcyc*X=*Ui`#s?Q(oH3QCm;{^_2BI-d!}&b2%Tm
+riTy+Pc7wW^yj*>(c3&+<xY3}PH1_?X+l#V@us%i|G6fao@BX&YqHikoVmPKe&!wh(0={yuC09Pfi$6
+W_d5^S+|NCI(R^gyP|Q8eRxQ6R_n-?}*W~Kvw6&x2Ion0Hk8JsCGt>7?fn3tQ)qL*0Tur4M75r4X`6?
+h6)yKp{f-WI0J!(!_=w;`wpK}ZM8&4swCLWr{UCY;8UJ>Y)9$WMYhtN0eDNVoIn$UBe@xI6-Qs+>vB<
+@l-s+)dy--94nf<=QkTINH{9lh+nE9DUe2(fv0Hp`Vfo(A`(eS{7pJXqXybU(7*?daV?L)}XAZOt7jf
+jep+uX?;M{Lni;CUSKc_wVM0#aK*V_ck4@XB0L@pu53&c>T$0-Tg$G&hF&grcPzb;<yi8UrtCU;kV<F
+k2Z92UPO1gP5rM(zK0Gyv5w|Ur&#kj7tx74{Vo*l){eY!Kk|gBs=vIvX;(*|dCEOZ4mH0^Au?}xM*ZQ
+@A1d|h=>N{0^}D;L((ybeIHmKT`8E%rfCTKi=8X_uvgvulEiGMIPo-%6Yr@88x#m*y&cu1okFGqur3Q
+V(z}-^{5xGtop1GX-=fRF1`&v_(9_1+UX+xsuD87dlu=O!IpGU>KA@G!3rRFHk#peBn&f)RUCA{*Q5z
+ajbpk;)6zD;lG4yTXw#x$X=Uzi?S|8dVcWO&!_T`5|{42loIa@F)Z6YL&E9G2*N>ep6JHuG}d|8-5jJ
+U<>6nOfGZmRzSnKLv|9#r@oxtU8)%jV0@+f;2$nT=Ui3h2`tH?^?d*%1_n$)B@g&%QF=*WL`$4YwFg+
+Uvs&~p0+$?sC}Qgx%-iyms);mB%x2!_1<-6NEYl*y~b1Dx_WvRxoXA}(+2vxkj#esc%7LpXnHemdaC-
+Tye=K!Tg@<Mj90C1Z!-az3ky8moWi|B)&0M&<TmdrbBFpS&E;tbsN-$1sqeo3OM{*pUh@*}%jaCv)Zi
+{xEyI}m+^;c3Yn?7mo4GU>-QGPhGAc<ov&k^-Kc}eH#F+l$noh(W8L+j#`;qlh(=S|iyM3J3jm&MT1%
+ts;E$HO$|5D5HdhVGW&AUoPn}3>cUdH&#czYRq?#^_AG4xy-#B<)A93<8to-uu$`xmOuP%3Ad$C<XQS
+sRd+p)XX*Q&l{5<ukvXsns)7wRD%BF|YZsV%8p>y3FUy51$79r~dJ&Q+(zGtg4>sF+YrXsJTea%wbX}
+dQtQ=?7ZpW-byX6HE*jfPxkgU1B*5@y{u*ydWh#t7oQVPVpFb{KJuv`b%J+VQ;{`Y(1eH%_NgsDYkt(
+ebu`-qZLJCCnYk-#Px4gO(=*mo)ZC%@^quQ;G<qqYiRV<S@oCSe-Pc4~Os6i2yc_yDPjbIl8JN!9Ek|
+c0hou>$uzZD9Pc6GI4?0O!Or@BA&h&j|ou2lZ*qjcwgu|OL^3qFPlB{X(lf>L0YkCcrt63J!4bRP~Tf
+@n-r$2Xxmr2B$72cj%x^!<dQB@Vs`LrUP$D4Mfm#v8>-Q$cY&nd&1V?WItpE`>*(d_}^^<Q#$O)l>%U
+7q{Q<Xa2QYZ`zm6I9o<t$E}#?QtGp`i_Y{#V$^+*B$EVpJs~Br_MB{&?M7>XQ?#vkLPom-g8z8Qz;W)
+h9*^U_sse^{keLZhgx%`S8Telap0jd*m}mLr96yq4_)Z}$ohQqk+r6m^}93usBUPY!<E+b@%PBFULB<
+&;LPNES&gP{W1GoYU|eqk=sGj>O-0S!OyHT)PgUR9tZ7l&_ru`1G?M$}9`)|m@Sihonk>!4ex~L3r|&
+mRDPI4j@hZcf4rxs`ipI<*M4Q#;9fx}Cy36K%TbRxVFZXaw%*jhJJZ3BnpS#DI`$gYvBwx!NUDG9-+g
+wJ**N`v+>Yf~|<JIF}#L8Kfd8ljNom^dbx$xb)EHm|G+Im0oT;T30*V_zzli`{d>E?!qI5fwZG35--I
+b(x6yu;@Hh+OaLxHH@yV9yZXMrv}fn4gH*GZ8kgpFZ+Aot7)@$;O4J%3_}Lm7#P?uHgZ^v-EhEhPs~f
+JFRD8M-bPHj?>6{28~HOouIH8p1G`8^^}jPE>U~OlVYD0s=gvL(QC|P))wOg!m`>k&D7r=K4vBLHnSH
+Idd*b9FnT>_wL87|^fr@DSx=!&^~d!7^IzW8Qtns9nJP(l{BZf_Xwx;nGb`NlUwTw8*K0j61u|)8QEu
+6CgiTBv5*RT-mh)l?R*LWSG7BaVKRvsp%+h^bbETRW>y1n25Fu=8UIv1uBlBeXrdiJ}A!5ROcOo-=a5
+*uMXD(+7JaKowdq484GF_#;{c1?w?x|bl)#_0QK0I=cQoTxj#*2xk$>24NPLkmtBhajBdfryptpuhX=
+WYz1=1$A1Y+30n3z22X@HnqZIWjyFPlKMm<JIMOW89;rXO!XG$Ydx6E{o>Z*tVu)DH-ZuXBiU{1p1Td
+qOaHTQzt)hKJh#2nWJu>ZsF+`o>OKf(P2!9`##q->t#LhrQ~!amy!K`Ly_h@sA`>ckchnW71vnHB%4`
+zuHil{MYRIS`^C(3uIIjHf*|_tH}pWy`LFgemy@-=?z&szx~45_8uyF5E}y=8u650vaq`zyjf$+9plN
+#a<i5x(h9(%L(2-dN-x=!3tW%cN$z#y9XH8;EnXxXWKb(d1Ttv*7DlCKNx~6ER7`8#`p?)m3*R=foCh
+Hkzj&##ctDamlE1Y-rbrY67qmZU2t)<vqhBfyv-Nt-+o@cH%EyWnT=W_GnZnk%4+Vpy4{o$;SP1+a0U
+8XDdiww2=ht6*IERH;*LHANU%dJ05Xrm_C^zl$>SV9@zpBZshB7>r0dS?7Fi<Oyz)x?vfe$Iv6W(<@W
+{P#=+T@E`hGv;d&v(G!RlACpxmU0cF>g=xR+<I0Xo<zUqK_4-}F;nN-lcAZN>EnKzcNCRA?3#kqeO>c
+-uN$7<$x#1v&DG0=r^~q?@y^hYql;U{fptx9V;Wnp-+9KlrItg>p{ESzzZB9K*$lU4U-vH!$?mWxmS*
+y*p-yjLQMj&|k?;9Y)@Pi{eN1GTg6)aTGk5r!t;*|+QW;{v)=UQWRDe01e)BF@xf+*|&3&7mRu6PN$H
+{eHGZvLz(2D#1(HQJDwbn;WWzG~#FQo+Tkx-X5i!|$?p81Fw>z;*=;r?z0Qz>eBrr<qSo&|v6Q3rlaF
++3-TRlo#Z&CT6Ux6H`Q^WNRBT8f$hzYlZNWd~rb*NjXbGo!YdyY{iz@M2hNmV-5mC?mAFxC~uD7qrGr
+2XBlsAsbk<px7c_3kfYyv#k8Dc)crYy^;mxouQ7$`fOQFE%V|vGc=?9nh{4Qe<vo+dw!HyNa)g*eqm{
+oOQX9p+!D|1XbNMJn2NlLW%f>l+ZK#Vzc8zx*~nqA4|Km^|NSzLYyy%@AkNHIU0#=9C|_$j0=*O*yoX
+UAR`WWa2L-#E<Mc|CZ1$vP)aYrM6Vqzpn%RYU_Y_|U)w$*@qQ+ZaZOz(f*5eih+Q>af_1uU^dzpiIYf
+SZ+<;;WXKekb6={0oa%WPt;xnV8VCdB=s=`Dl9!<5+g_jtv`Oh9HqX$Y9!a^<X_X5BbI=yCLi;|cb&?
+vKncI!%3A`NMoyx3rb(><_$6MNM}+b)<)T%(PD@V!U|hrys9Ss;Lz7rXIkZho3Oj>FyyLgWxiYIuji;
+fr`0E%g}>Pqfq|+TG0!PYqmdPdV}F!NqQ_t5`z?^yccO&&x;b~!Q?gV+pN<h@!i9WU>UF03kP+kYZg&
+*7(<Dv>VfMC+E+HNsdI4uYdVLFY0IWj;9;$~n=@~#sA+n`$<xBjrz{?q<-wXNd#qVt2w#gP^MiW^JB`
+NNR*l|W?Fim%4XEI^Sx#9p&g4~xu~C@r_gdrpCMu?Qk5>mf^QN~~gP1E%L8?*R*JE^G=9SZKCj?c$0q
+8NicSG27;WN^HrXPKJS|Or^t?3#0<Py&JGEebM+0P2^k=~d=P%qx|<1873&5U)fa=)~f<3;!7x$j$a5
+8lwjZIi4$YsrU}sF)wKM>~tOrTdy4*z%w{#k>7k^{e2AF=e*qQsLSKyc5f-$MXyo#?n~}Yb47S-n{_#
+c>B!d-KG$KprnC~8QzONjE>LNc#Lz13(KOlU%L}3;lBEPhdNkY$<j3L7a!EY8X51;C=k8uo<YhS<&m?
+GtLC}Z+r+>$^4am3?((rG!}+wbnPEC6e~+SqC!R_luwUEYI=plxlkY9!jqVU*J~MYb4ONeX1E!Ia#5F
+sodwv{Z9L<`|Lr7w_{g`gLjC%Jk=N=S>{(VAl>(KYy;bf>EwI#A>%F<~qZGO!(z_zQS6Cz%JI6ILz?t
+ambW==FPr)QawY9?{}wW}p)XUc5}XRdkQmu}m@wbNQ}W-ohYV>6#NF7GKD^RU@9o0%w?zQfBKHOt89m
+8K7yIf_oCEqwMoN=-AKwlEN6FqmAG+>DWXW@NLp9D$rJfr-bgLB`2nm101e(+BaoymzjV#QAYv+^apD
+c+P*#^43KRCpP@no|WdzV2!sA*m(!1C(|?TX`UuHyJv7S6`62;4s+hOgqN<jX_LCg+40`9xuB)2S4)}
+FR2Z`Z{tCfq5NC35OB3gF>&oysWg&KQdXh73nR{&g;hyd7d6cOcJ&;IPd&=Uq4jN~k6z)s^<P^tp+Th
+MrEl&qx7k=4MT(+LpEV;PE!-0rp^Lq__+W)(YW@<h><0SK=0!habmQCR`+e~Yg>(>k?rkORPsyD8A<n
+kg;W_Ef_^$*O<KA0saOuG=#H78z-3zyF5B1YHXydG)U4!&}pWNu02&5o;y={@ww|2bzx#t&!3Jx5{Yx
+nDEunIKqqSrO+e8y9}%)n1O5;b{f(T63CX+bCSN2iMCwXMae{P4{Ttea+l|Ho3-EOQ&wrY1sf;mR-vh
+#beF#)<(s$N3mx78_9zIOCPof=Nh@1*}UHJ;T+Io77w!*H<~>CvY>r;w<d*_GyPRPGsDcu+K)OP^tR0
+87R_2y3%0w~aLh=|a4596m$wRS`Y=m~yOib`eOkE1YX^DwQje<J)^3+;-mO#qLdl5m(7mhWS#Qq7Xew
+Yj>zUoVa!%Hh!~E#Ura-2w4UD7Eqp|%q5HwF=(6Y8%Gq<rgUDlS5!H7TYV9c_@GHj+`HCG=$GCQ62VE
+f}Svk+^nYiQ48V_Di<IptjQqbc!>n*%N}i6>~?hrzj~R(ooBw&LZr=g^qDng4RI=o<2$U3dfIm2=i?V
+$59G)O^jHI$q~_dNNc$x}cfc%zU9Hru8fa;>UAhdg9eoZHDBS8NjSdrv_gic4o;TnPnim(Bw7ZWi(r_
+nm5?w+|m@yOwe?zQ>1Uxf@k+&ipJ)`V|K7+T^F;Oo4j798)oxZ4o0sz7BmM<N-sN8NPXCY4m{=*QqQJ
+;&dm;znM3CETvI@rK~7?kY$5lUja4xIc)l=V%3?k|m0)Q$WSX-~(g8k)g)Ie~LR&NQDP!rJESNb;j`3
+czX)`B+<^a`1ygPh6=Z<Hi`}p)EnH<VXdzm+8L8fP%&F@)z*}a>!wk`FQv%fs#F`eD?Qqw_ce-8)5mW
+FdDk@Yn%ZbbJu$dI#M>dDoGXL&2M*2KhA=9Gwgc-UcDIq4?DNusN=!?*IaqW3s^g5Bq4FTgRXdje|0`
+I^?sdQV%EjdkxRx08;`nTf@d?iac0i*m|(V1Eul^_+`*o7L!SR?hlH(=)ppczDcYX(T*mnQElX$!pf7
+W3H|lCY*D^X{MApOF{FIdYyIh@G)R9pLSmcK?B#tWUkl4&kA*RSZBRFU|(-Tj`Oy}DKD`zo8N|j$DB&
+3*|{NovlW!)Sl3vv)~_SJdmG&<(q4{CEhnMwm(jV|w41D5N#{0OnUC2Mo^7kyo*k*2N1WBDjn9=+UD_
+ksTTa!jeChT4PKH{rBPVN4XE-i+O;r~iaPI4|Um1$fJ=+I!ife{iQ<0pzHb&ZqStN0d&e;LD<`|U){u
+<WiN6UzCXE-yVnHtVk$IK56^m_tt?(j_A>OV$)V$PjRHlktnM&D*{I_G$d;bo37K4xj-MvRfT`T?mQX
+Ru(7iL9ASPP?7YscJ*abSwtryHaK~H&e1v$=Pmk<;k!{2PRmavp~{q?>Xp-QFH2+`OMLpl%Xx-W%0FU
+QsNZXnj?5I2Uv5Kt$<ZDtU0SSWig63Cx$1PUYz~kW%!)8=NWaTbIQi7$_c<V{h`^)Wjk%nMyIoR%bMg
+aqnR<y2G*QamZ8&Yj~PbG{Lms{*~nWqGuA}BmDEEJubI|3_q-g1xtp!$R#7Ie%hueTp-u}(t~mw)#MU
+&uKb;SpGfRSM99x`dE^ChB=4{u+{OC$^%Jglnrw?<ydDZlpv!Hp-km-Yp>N$6~CdMYk{5Wf2ccfe$bi
+MCT3$|mqYHnoBa>UfCrjfhhDYV%Ym>%1r;LMo`ZEFq(l+8&yl=tjw4(UE8!+F~S==N$r$FNYd+xgl$8
+=aFlCu`57Q>@p2iNNpuX%{`~-S>X<&+k9|@w?ypV}f73|N7&<_(PMA{xIV4;hXQ?e|rDzck?G2zkdJS
+`>*B*{qO(q>GvPMeSbfvPQ=V`Ij^v4%2kKtVb@T-o&Hr*33@b#otKkntEMpZO#0!aZ@4p@Fwr47HMtx
+>T8;uPLc3~;Mz`bK_NsYoJ&xj84iB!HnlOBG<U2z>iR0PJ(aGg3%9>r+k#BpP%ldLTI=*J|;*9*7{RY
+RR*6hL81mDw4-3hbhIMlL@zZ^_kWMs|y)$!9cHgDE(%>d+>-!jc#_LmljTNV_{nDlO$Yc6rVobO+bp)
+Uusms88j0n9a6I_pVCeV5~`i+wCKxU8m@t?6YG>&{RNJ0QCpHC^`F*PQe>jxE#tHC=!MT5C2P$ofLV%
+aC;$;FcqzYtHVk5l)NbrE^<OE-X97%Sv|5tY%PThLoPfqQS9{W!Gexg|Ar%&dK=YaQJfeb~!Y(`W2;;
+<h)2HCXCX{F}XFn&Gv-WYye=iH4_KJ@|~eM^|b0a>^?^=ET7g)XdK~PCVOkzsdMeip2QkdH9W1+aH6&
+5Ap7d6Gtpt*wVV=Jm}^;jF9*%98Q2{!U(WWfIY~ZSmyYZ%XEc|iN6UbAXDCjb!d;Hau9+57;bnhjalq
+xI_i{RWIe5HgW78()a{7BY?7T?fa$NS^a@24+kF?Bruh9U<xR+zx%c<+-B;}f3+Yz7Tu)%U{dpS3_9Q
+|9)*{wMfv+N2i6XxY8>vD2v&9M_3ertqzI(nV#atv=dw6&b7S#$Wq?%*<iTF#f=843)?f|lbqYxWnZ^
+O`ZrN#o@_@p2Ap*(qQ4pcdj?m|!^$xESJ^zkj&$^+D+bhGTzgj{oK)79Df)Hzyk*qC;KNp*kYEY?-f_
+Lp%9<-=V;m-5isurQcjms$AE&z&y7Qag1#_kg&M;ng#H*Y6tR`BV_B${KHAz<)rR%q;@$axttzcb6&_
+yVL3Fr9GYFu%Pz-dmxG(j`MhQ6y=JC9XZM#QugiheHRo~$gq>Ag&Z;hFRhP4>%URXstm<;uaycTi9D-
+R6f2>)ATW{YPiV3Tj<t)&0wqn7?MKhLZ*fJ|vhOFg8=W?82*#%!CdowvT8@<TqouR-OXqt(y!E8Buxt
+zUR&R#C(ESGbZ%aOiiUuW4FSaW=GCb|%8IYW78C^{UnT63()NrE*3WkI~2^J&pxbYC+fI~%zii(C#rE
+@yL=qZ`Zm`_530Ve4<9=xg?a2;FiTaXC@A91mP`;K(_aWuJc830$+QG*c|6^44<{(uus~@Y=Esx@;ya
+ZoQ!10w>F&Z_VPz%xyVSx16b4&eSbu>Xri=%Q=HPLy>^m7TQ@HVp)$bXW5oxY}+xm?fltx>TEj|w4F-
+WcKWydnT-!OzIeAyXtv{G+i|h&u+e2Lu^ndFPIhd&!P|b^ew%qw&hIQ@xBM>|EE(XW?cCRP?rS^twH^
+D~j(u$>zP1xz+Zm;8mwMaz-EWi4mSRh>rPxy3A#rE8CE1dMBq2#i5|V@@AxTIQE)r6N6gh3)yD6CyC)
+@Jr%3+W6>3N>lXXNnuog7jQ<(egNxn{X$Nl{YVadBrjBgrJGd6BNAYw23Lmae61>1Lrb`M1lpWbMa0L
+hcNYbVs^b^-X;oiI2p`Z64{4bVs_U%RMD~O7@iEDFv_J4vcJD``gy_w!gS-EpB^D+w6U#$W5R&Y}!^f
++e%|!W3D+Lk&Fv$>!9tt&7PBJ7KR&hZD($_GdFwws_;*rJ6<FfCr;IDhibM%HQS+%J412e$jo+RX4`k
+!j?8RFX0~%O+roJ}9ka>Bwl}#QaM^a2w{3-OS9II9zcUmkHf6VsgUygP<K6geTMKW?;B5`Ht(Ue{(tb
+Ut0(VqoBcN@|;LcEB*tOnHgzRe`Szy@e-6(I{<=tR++XvYWgKRVI?HI_mTeR)qZbu*P48?@gAKN-;&*
+_gnk0&N<T5tQ++y3IVL9)sBwzS_CYTG(`bFw|#jhk@o7aPodi3uk*u07=?Ff8G=V;URhZkn+zz_!B^8
+-Q%b0`{Di*p{H%L5a;)HcQ*~zPIxb+j)p>IlFE3Z98JyS%(c(H+SDQsJG4M?U=)zp-8}E+cAd?ptoI_
+?U=)M%wgL++KxGF#~il94cpe@wn@4jZrC<%_8e~5YgR-xKT9VZEaSJ$=#BWd?YV8|Y1_cqsCHwz4Usm
+$*;r*;Dc>0i2aDfrIk*i0d)lAV0NXCZw$HZh{%`oc?Nx4@_1o^?UUM^|!3N)*p>VL_zU{Sd+w9vm`?i
+n1ZJ}>|v~7}a+v6KKY`al=w!(Ar5)HP(?+k^5ZSZXad~=~~M|#@;-}G{0f^Elp+wtCZyf;za_O-VS>T
+PR!ujw3YMsK^%+wSwWv%KvrZ#&D|w#BxAyloqA8^+s)@wV-@?YeGzZ`&T!rf=`It(tA4W!pd5c1^b3i
+EWc$1MSVIH-Fy1d9&utL^nj(_;3@mO=Ip11%Y|%Hq+Xs6MF`qX)Y7pc22iF(`{>X+w9!-b+*mUZO3KX
+?A$gx?+n$yjm~YObKB_LHafSB&TXS}+v40dF}F?3jb%4&-ymj#*KJ#J+mXC8RR4Tx^QvusaWnSobze0
+v(uo0Egxl`mCfM7?;I=WiZ3%80fp><2z~0Zcsj}_$ZF_y&2H(a#+XmmZt+#FKZ996~j^4JTcfHC&49s
+TUwwbqi_qLh0?c;6xc-ubSwvV^%<8AW2?cQy>ciZmWCgj_e-L_@7ZP~rs$Y$HA+jwl-ircp0HUZxD#<
+sn&ZEx(FO0oI1ZFFsWT-zSko_%4pb!R9XfYE+YbWPKiL}1w2+BUT|aoo0fwymsfD{I@t+Vh7t(@yPV-
+5H7wy8zcjg8i&*KWoR_jS#UVb>YcvUuxsfZC`5Jm)iEFHb&cs?9Nb}*pu2eqPFd)ZTo54YT9<0cDy7>
+lGlr@1q&M7Z0iD{mwx%qaMmPt?{Mh0Z?x?j?b)%QquajGjwyz@O#u{>qUXbs&F6YD6h?FUDqsI4u~4#
+ev+dbzdp6si&9-N=?bK}hGu!^mwm-A&%Iw*dxu*HpmD%=Uw!N5bFJ{|t*t1#xHdQ8`?7M7REE~RW+ai
+0mJ4o)f+abgI)Al^J{f%vFW82l($bH+a*fuM+&5CWaV%x0Pwj*}V-h%2uQrm9CyG^0Dors;Y-(*#$m2
+cY-+a|=Goh?$lXJ-o+Z+j5i9>lf>vF$-@+Yfh!Yu=Vvd?EM^r8iC9%y`4!4Qw}?-Qace!p#CV^4pkh&
+q1Y~bH{T_0*YU31hMJErUTo8bz7WnOVVwnvu7GUv+bF|nz3#();mM7G}FhKc+F@(gW!x68qS`EGl4DK
+wiw$JV-r*pI};&l=FU)@yb5T_Mx3DZjnemv(&u-AWlmwf-W4$<eFO6i%y-UIgnVzTy@~c_)Ehl-^t`F
+_=E@r+Z;re<@@B@H6mLkpdGKbS?=}m*=5B!S2ErQ%Zw$OK@W#NK`)=&JvG2ydoA_?9yTR_pxp#&}Ad<
+UD?k2e#-EL~Tf$avio7HYsyHV|>EH<y*y!M&`sp)M9?WVCCyl(Ki!RrREo4IbZy3y)JtDA{#Cc2sEW}
+=&k?wY9x6x~pCL(vUIHx%7abVJcyvx+(MhIHZrjBYTx!RQ8~8;sr=3Jfy3$>=7dn~ZKUy2<D!qnnIwG
+P=p=2BRB{ZZNvR=&qTH)D*@P!Q6vzAfua%ZZf*b=q96^jBYZz!RQ8~8;ou+x@)E)WOS3!O-45v-8}S~
+$tx7yP;^7l-Sed&S_K&0V044gJ>SnU+dS7y(-`Syq??g$M!Fg4W~7^u?l~Vekqk;VDBYlRgVN1OHz$3
+!LFopi8<cKFx*6$aq??g$M!Fg4W~3XBZali_=%%Baj_#gO507p<y7B17qZ^NIJi6x#M)vE!q!S&CbTi
+V;NH-$gh;$>;J44ZdNB3)M|IB|$CpsAEW~7^uZbrHp>1L#xk?t3fsysw`XDCGQ=*FWPk8V7=@#w~*8;
+@>0y7B0q@A;VN6(ik<bR*J@Nbd~ANl#(V&CR962_D^ebd%9dMt4umixV)q=SwyAY^F~N8&R8)Ueo1((
+hW*CDBYlRgVGI3Hz?hpbkEmh%(Rb|Zd$r&={~c(o$tVy?hBc2TDocJrlp&fZd$r&>87QdmhSm0>*?^2
+>7GAL*7X_nd{EfH(+y8IJl*hg!_z(AU@?6pLEZB;7Smf|)QwR$M%@_ozC#hjQ8!239Cc&VjZrs7-Di$
+X#k?z>06O!?b2IaX0Ai_kh9U^2ZkW1Z>V~Nsqi&3PO}EWbH%r|Zbz{^`P&Yx{1a+TL5rn!C>Ygu8nC>
+2<Zj8Dy>YndOm?40rZi2cA>L#e0pl*V?&!`AO-3WCf)J;$~LEZDE1T%<W)QwU1naZ205JhLobxLy{LT
+_`;kaOnplXzw-He+T4!m8IS1<>j>>jA#H=L-R5WdK<>WZlo15>wVqSvO_flyy_qO<6Z(-IR4x)=gPAW
+ZjT;L)HyhH)P$<!8FRcDeHdDpT20psCA>(jaoNq-Kce=)(u&&S>=<~YlczWy611*H*ej%b@SHETQ_gr
+ymj-|&0F_)>kPlVb@SGZTQ_dq^Y`hmaT&Vq=akYE&=iX(g0367Zs@w9>weDt3n1vaq3dR@o4M}ite1J
+~Ra2%0Dd@VP>xQlyx^C#Yq3edO8@g`jx}ocyzk;4M8+g4l6hY*5lh;jNH+kLUb(7akUN?E&<aLwRO<w
+o>soX66@av|pcZSdTSvs+UUpIc;_%-lr;Mc&ffnNi^27V3v8u&HvYtYwsfnNi^27V3l8ss&|YmnC<uK
+`_yw+3$w^X|Z{G2aejaT?6^&T!3tO1Gq2(k<z>bX&SD-FBtzN?VF8#g<}Av88}%SIxbLbRk_x7t+PNA
+zerp(uH*CN-0H3ky4}-{-7dIYoOLZt$|u&zM7||fJ(ZOuB5XPtSPi|xtfp6kJ8qZVY^;S*V46g?RqT<
+Tf<(#TZ6Y=H5Je<e55_n9%;=Sg0}{54c;2_O$(-(0bK*S26PSR8qhVMYrM@xo{~K!d%B$Sz5!hWx(0L
+&=o-*9pld+afUW^uW6+f>1#}JQ8Z0%KYcSV$!CQm325$}C8mKi;YoOLZt$|ttwZ{DY9QX^|8n`v)&->
+@gL;|`7bPebl&^4fIK-Yk-0bK*S#x;j!a{fy)!9rdGyasr^GZZItdbj4@1q*%+{2KT*@N3}Lz^{Q{1H
+T4-4g4C{T*SgB@M~OCEG3CJ!LNZ|1HT4-4g4DTHSlZT*TAoVUjx4eeZ4ajBKS4%YmnC<uR&geyass<@
+*3nd$ZL?-Ag@7Q1H1-!y)zUO7WZMw8hYZGz<U+DnBc9!TZ6X-Zw=lWxHWKV;MSOLUJKqDyft`h@Ydk1
+!CQm325$}C8oV`lYw*^<tufzIQu8^R{sCPBx(0L&=o-*9pli(EmeH*suUAdhLIS@AehvH@_%-P3ouQZ
+@uR&geyass<@*3nd$ZM<_Q|2~fUL+F@_%-lr(APUd;ecNQzXpB{tP@-%%=lx?9g2o&B=|M(Yv9+wuYq
+5KzTO!M2mBiNHSlY!*Et!}z{y5nfY<=B0b&Eh28az18w@taV1vO1gAE273^o{SFxX(QF-Ns(>O*vZ*Z
+{EsVgtkmhz$^XXDB*AY=GDRu>oQO#0H2B5E~#iKx}~60I>mL1H=Z1jrn7MAh7{r1H=Z14G<e3Hb88E*
+Z{EsVgtkmhz$@MAT~g3fY>`jBMFEN5E~#iKx}~60I>mLubTXY1c40j7kn=mT;QZYKzD{>0@Q@{O;|33
+iG7%shY53-yoM=Qn0<vgR~XsDkQ_7MO%JrEbC`O>v9V_)nP(IeNH&mcAlaBd-KcqZae-t5$p(@QBpXQ
+f&QLfo*<iAPWCO_tk_{vqNH&mcAlX2&fn)>829gaV8%XxfP$b~kps_(?gT@Ap4H_FXHfU_n*r2gNV}r
+&9jSU(bH1=KK*ub$tV}r&9jSU(bG&X2#(Ac1{L1P2N#{9(r1qm7(H1^JLeijZOHb89P*TAoVU#}T~7;
+G@uV6eepW4`gICat&_gAE273^wp<;Mc&ffnNi^26+wg8sznwzk8gv%wU7T27`?;*kG{1V1vO1gAE273
+^o{SFxbGa*F*z@4F($wHe!CK!@#X~hN6SF25OC%=$MBW9lSMoYoOLZt$|vDv<7Jn$QqC}=1-4;vIb=h
+${Lh4C~HvGpsYbzgR%x?4Za$DHTddfiWHDF=2Ul3)&Q&lSOc&IU=6?;fHl}^P}QKSK~;mQ22~BJ8dNo
+?>bt<IfmH*m238HC8bmdKY5>&$s)12s{y>0{1xpQ#8U*!1W_O13jyU0{!BGRF21X5x8W=S&YGBmBsDV
+)fqsE-m)@*~N-WduJm>MuOU~0hBV5u?3tj+iXs0L6Cpc+6mfNB8M0IC60?+ir;Q4OLRL^X(N5Y+&x0a
+PRB9z-8d4WJr8HGpaW)tIjb4Wb%EHHc~u)vM;=1qN0Psv0@BY5rMLDuM%^2Fwhc7!dK!P;^kMn6t?>#
+V15iOwf)%9DyYQG6Y%(Vi24lEYD-Uu8djU843~0;;_&SE8DQL4U59CxC?8zu-J;3;m_oICfYMOp6RZs
+Va#dTm`TLU2xb&FxLss4rq7@LcDmK+RHh@Cb~IrlPGZVuilJsqai8(ZL3Ci%z^Xx11E>a24WJr8HGpa
+W)c~piR0F6+&4iPv22l;78bmdSY7o@`s@IGNR5hq-P}QKSK~;mQ233ukF@>lGQ4OLRMD?2Cq+T!5i4L
+k7R5hq-P}QKSK~;mQ235T?6c|`Fuxen{z^Z{&1FHsA4XPSc^?DVJxIn9cRs*d@%@;bq3Qb@D)&Q(g^9
+?WaP@;pf#(Xd0HI=9TAgw`KgR}-|jrjw!psYbzgR%x?4Zs?JH2`Y>)}X3ERb#&Tb7r+@HPC9H)j+Fvh
+N1(j238HM8dx>3YGBo%szFtQss>dJsv1-^sA^EvpsII<q64c2Rt>CrP1wwzXV&XoiNy(D4Za$DHTY`q
+)tE1E7GrmYf(5V!U=6?;^YzR#o20BkS%b0$WsNzAI#UYL8l*KKYe3dJLlHw+gR(|XHuGu0LRo{d24Ic
+(>%EC{$QqC}AZtL@=vk*w)}X9GS%b31DC;y0${Lh4C~HvGfUE&o1F{BW4agdh_0G^hGffJ$25Jq|8mK
+i;YoOKutkF}q;smV*S`D-sXf@7EMc8Vv)fiiyhRRlht;T!>@w8jO8hka-YM|9XtASPntp-}XGn^qu?E
+%&RtN~bqtp-~Spc+6mfNB8M0IC601E>Z|jWbh`GhdOY<X~#R)PSjR=3eLK=5m6Cr3On4mKrQIFlu1bz
+^HeI;)J6HM-7e|7&S0zVAQ~<fl&ja21X5n8U!^6Y7o>QsCR~<1EB^&4T2g3H3(|J(}1S|PXnF?Hw|tY
++%(QSou1^(It82tHw|tY^F^`KXTsB%uX~=}lb{AX4R{*xG~j8#(}1Tj-`N`UH0Wt?)8MATO@o#OC=Es
+$j5P3Q;L*UNK}Um*1|5y(*cTp+`JOgCcremnr16|%0}2F`1}F_s8lW^lX@JrIrNK$#Ia3i(8jLg;X)w
+}Yq`^pok;WM53>Azt@Mz%Cprb)YgN_Cry)zUOcr@^6Jm)qiv6vvEK}Lg&1{n<|8cZ~pXfV-WqQOM(48
+;T#4JaB=G@xie(SV`>MFWb)b4ox+;LyOKfkOj_1`fS56cao&cxdp@nAs5z4IUaOG)QR7mqN}Ei9-X21
+`dt+K1jpnouM9M{*Y)!P%;{1G{|U>(IBG%MFWZk6AdOBOf;BiFwtP5?-Gh86iq0aP&A=v&bLn)p>w`C
+#4w$BH1TNS(WIkEM{^FRvb#IOOAePD?&wHHnv66VX)@Aeq{&E=ktQQeJeqVg>1fi?q@(XHi^622$w-r
+tCL>Konv66VX)@Aeq{&Eg{_xbiB0*^)(wx7;Nk*ECG~Z?qucV84k$4_-(xr4MUAkOKmXcY()a%`nVM>
+?MrF7+;lCGpH=}NkJlDS+-QBsr?Ek#SwbB8TSOVX0GB=EfEZd$sQ?nrn1?zqyC>_~PbJCd1-BpFRInq
+)M|Xp+$+qe(`Sj3yaPGMZ#G!Dxcf1fvN?6O6t~GMZ#G$!L<%B%=vMbB^Adza<$>>(VsANidpVG{I<s(
+Y&r#Ezf^SEIROL;?cyTiAR%+CK*jKnoKn3Uvn@?OfZ^YG{NY7hkJf0o#-H=Nk$WjCKOF5nou;MXfn}c
+qKQKjhb9h99C~N?nxjgb^LRd(GwYPsJnh`b>kT=i90Ug)O*)!%H0kJ_q3FP)iANKUCLYae%3`iGS&I%
+lns_wvXwuQ7qe(}Tj@}v0=I-m$(uod6nv66VX)@Aeq{&E=ktQQeMw*B;5osdQJ44aINRyE!lS{M7oG<
+jMc}5`uN)wdke2bS^deYLQrAbQ@mfp48^BIuoQMhSx)8wW(-_u2P5}xLK0|4Jif|>+132GA5B&hEap(
+Z^|c$)Au;c3Ftgr^Bl6P_kFO=OzLG?8f{(?q5@WSYn{k!d2+M5c*M6PYG5O=OzLG-+wl(xjzHOE0UFM
+5Z}pn#eSf>1D-o0nmh}2~QKACOpmgju?=YYjO}V1U0EkQkR_Xlgd{PA_k!*K}~|11T_h264YE%)>F(=
+q*IWSzlh<cxu(Ykr^!u|n<h8S`J3jN%ZnIzn(#E?X>!x#rpZl{n<h6+ZhB`ZVz_B?)8wYfO_Q4@H@&R
+c5}qbJO?aB{G~sE&(}br9Pv0dyO?sN}G~sE&(}br9PZORdJWY6-@HF9R&etZe!6c|Tf8L)6H4$ne)Fh
+}$P?Ml0K}~|11T|mZwe0>eY7*2Ws7X+BPDmy~O@x{VH3@3&QT3W1r4uI@H8E;p)WoQXQ4^ykMoo-*XD
+Bc@YI4-%sEJV%oaLV4o2+#rmOE)imNQt+h&CPSbXWSPJ3}!s?QfdiG_z@36LYTu5)<$=;c3Ftgr^Bl6
+P_kKO?Y}|C>-c%!qbGO2~QKACOl1en(#E?X~NTlrwLE5iD8181od4a)I_L>P?Ml0K}~|11T_h264WH9
+Nl<hCSr3c;#Hfi;6QkZ4j-%H6QeuIDQ4^ykMoo;G7&S3!V$>w4Nl=rZCP7VtdS@sm=xM^!gr^Bl6P_k
+K&H2h{@Q?&G32GA5B&f+vlba?ty)zUJ+%&mqa?|9d$xV}+CO1uPn%p#zX(H1^rin}wndXpb#+1W6^4x
+=PpruJmla?keO<J0?G-+wl(xjzHOOuvn%$O4M4SjQYF@a1InI<w#WSYn{V^%D4IdQ>Flba?tO=Oxwri
+n}wnI<w#WSYn{k!d2+M5c*M6PYG5O=OzLG?8f{(;PBQWSYn{k!d2+M5c*M6PYG5O=OzLG?8i2(xjzHO
+OumkPUiqh6O<+>O;DPkG(l;C(gdXmN)wbOC{0kBj5HBxBGN>pcZPz1ktQQeMw)mu@o3`F#G{EvuW5vg
+G#P0!(nO?*NE4CX8Hxl(nv66VX)@Aeq{&E=ktQQeMw*N?8EG=oM5KvGGv|xaW?lwLlaVGPO-7oGG#P2
+;+?UP)k0u^XJeqVg>1fi?%;_A+Xp+$+qe(`Sj3yaPGI~u%gGUpOCLT>NnqV}+XoAtK=^V&tlF=liNk)
+^5CK*jKnq)M|Xp+$+qX|Y6jAqTMwdY-TNJf*4CK*jKnq)M|Xp+$+qe(`Sj3yaPFq&XA>0dIwBwTlf!h
+u?4&B69F??@*ms7qp&1Se^!pBDIOah;aDY3Y}id1-BTXDB9S-Sy(p`9fBeK1+{T3d|gProS^Cof%oZJ
+|eBSm_gid`|hHkIRnykm(w#&S2gYSPwz@M?DhID(`&&(OOuu+ElpT@-=PqprAbSZmS(*=jQO1_PceuS
+WSX=zX=&2Zq@_tqlat;ViW5+ppfnk2GSXzEiANKUCLYb2NoY-q`-PDvB27e^J?j)knmtR|p0~?OdpTa
+Do5hMJqooN-6O?98#GLsty{HsgnzS@&Y0}c9rAbSZmL@ID`D6VlDrB1TZ}Ta%Ysw6sCOo|}6hZVf;c3
+Ftgr^Bl^W~;J|K-90h?^!iO>UapG`VSV)4P^c1vgD@n%p$GX>!v<raAwx+l=SjG`VSV(?q6;OcR;rkZ
+B^*M5c*M6PYG5O=OzLG?8f{(|nO>&woiar9Oo_Wg%=XA(B1iFi-aS>Z<vjbgy?M7CYQDdum~BSnP1qY
+rp<$Dn$g*(}btVO_Q4@H%)Gu+%&mqa?|9d*>g>?b7oH3^IzIBJ<a(?+$>hFS*&8z#Hfi;lb|L+O@f;6
+G~sE&(}br9PxDM~b0!C|15cBiCO1uPn%p$GX>!x#rpZl{n<h6+ZkpUQ&-6CnG`VRa(?q6;OcR+VGEHQ
+f$TX2@BGW{siA)ojCNj+-(?q6;OcR+VGEHQf$TX2@o^g*e?@A{`xM_0J<fh3@^UOL0oaX%7UDN#GX~N
+T-FTJ1MfS@KpO@f*PH3@1G)Fh}$P?MnE8Hx^sn)5GjO;>Wp%tXSOi%hPsH|D|;zg}Eem|)bLe>W>RYI
+4-%sL4?iqb5d8jG7oVF=}Ge#Hfi;6Qd?ZO^ljz$}~A@&c8b~9UDwdn3^m#S!!a`#Hfi;lb|L+O@f*PH
+3@1G)H_4bflw2nCP7VtnglfoY7*2Ws7X+hpe8&`c$)Au=burUJ*h;fiBJ=vCP7VtnglfoY7*2Ws7X+h
+pe8|0f|>+132GA5J40W<tMk65@1B1JX-t5qCQwbFnm{#yY68^+sySb+H@!VoO{#ils8hkJiB%J;CRR<
+XnpicdYEspts!3IoswP!Us+v?a=ik~%teQmivTn)w(#~1Cu+?O%`Fy=Gzmr&qJm+!d=0pfk%{gV7L^X
++O64fNCIo%ggEudOJwSZ~?)dH#oREwn+MlFn5bIP<hYH`%!s6|kVpcX+bf?5Q%2x<}3BB(`Bi=Y-kEr
+MDE_0DifGXEB1$ze%1QudT&OT6d5B;FEliMJ1LX}8O5$@bh~ND`8SBq2#i5|V@@AxTIQ?l7fDDN>4*B
+BjW=!;~Z?Nl8+YlB6UlNlKEEWFDfVC@D&clA@(>BB;P=ans_a#Z8Nw7B?+qTFA7JX(7`>riDyv{?S2m
+jpC-oP45hkbVs@)-BY?LzWLx&x~D5WC3(8iQ;Me)0)d+rH!W`ZuE1$=)8eMZO^cfrH!W^j+_aEsA=5&
+pWolkVp)v{;GOZ!gqNPPki<TBGEm~T%v}kG3(xRnBON*8kD6RSD=*+B&mKH3%GZX~0w62+UJm*J=MFK
+J{WLoou?IvTzO^cfrH!W^j+_bpqouP2xriDz4mKG>2P+C@%Wu{o5v_NTr(gLLgN(+=0CoN8TXDA#%X@
+SxLr3Fe0lolv0P+Fk0Kxu)}0;L5?3zQZoEkt@}C>$7RG16kB#YpR#^{i?Ir3Fe0lolv0P+Fk0Kxti5k
+d6!1HN`UNgabAz{b4cEVx+}L3y&5aEj(Iyw7_UF(PE;-MBfz@Ego7tv`}cF&_bbwLJNh~>oBQn;?TmO
+g+mL677nd7odXXo9$GxKcxds^;-SSui-#5uEgV`nv~XzQ(88gGLkoxA843a(T0FFPXz|eEp@l*Vg%$}
+d5?cJT=3j9#hc5_P5VRoZouNnopann+fEEBP09yR>dX=VVm>v#-76dH_S`f4#Xw4VlKfdQ*QN8mO6)?
+15Xu;5ep#?(=h87Dg7FsN{SZJ}(!k~pg3xgI0y)zUL9JDxSanRzR#X$>$76vU0T5IZNo>x3z&?2C<$I
+R`<6$dR2diCq=^ip2h%kd(yj)H|23oRB}EVNi?vCx`-c(Y(=!O()C1w)I276&a1S{Sr2XkpO8poKvTg
+BAuY3|f2o2n1RPv=C?^&_bX!U)9|6JVFD576vU0TJz6d!@|O#g+XgiAHhM3gBAxZ4q6<vIA~$e!k~pg
+3xgI0Eeu*1v@mF4(Av{SaM0qQ#X*aM76&a3S{Sr2XkpO8poKvTgBAuY0#kIN_Vf`}@@4g17K3HoR2Cm
+)K2fF;^?EYVfI$m`76vU0S{Sr2Xw7%D7Y8j4S{$?xXd%!dphZB7fEEEQ0$K#L2xt+|BA`V;i-5i>1X}
+d->PajT5NHw5f}aIH3w{>-EcjXT$NW84TJs{=^brWO5NILLLZF2}i+~mZEdp8uv<PSs&?2BU0@`!yIt
+l`MXDA#HXd%!-poKt-fEEEQ0$K#L2xt+|BA`V;i+~mZt(ZOnfffQS1X>8R5NHw5BA`V;i+~mZEdp8uv
+<PSs&?2BkK<^Ai0s<`rS_rfdXc5qYp9Mb)eir;J_*w9?;Ae5q;-1Al?+ir(_$=^Q+_Si6anItO#XXCA
+7WXXfS=_U@XK~M(uNBt4y({`z^t0$^(a(aP1wRXZ7W^#uS@5&qXTi^cp9Mb)e%5^BZ_&@9pG7~5eir;
+J_*w9?;Ag?lf}aIH3w{>-EcjXQv*72Qp&+22ML&yv7W^#uS@5&qXTi^cp9Mb)eir;J?pfTk=4|LSa?|
+s)A_0LG0xbes1hfcf5zr!_ML>&y76B~+S_HHR=zWJG0f80*Ed*K!wBTpK&w`%?KMQ^q{4Drc@U!4&!O
+xnrp+!H7eir>K`dRR^;Ag?lf}aIH3w{>-Ebdv{v$$t*&*GkUh9Uuc7WgdgS=_U@XK~Nsp2a<jdlvUB?
+pfTkxMy+C;-2pcd=~R8<XOnGkY^#!LY{>@3wajuEaX|pvyf*Y&qAJsJnswz0rM>6S;(`HXVK20omKNE
+NFE+}7V<3QS;(`HXCcq35v!i35Dwt8xMy+C;+{o2i*^?6EZSMLvuJ10&Z3<~JBxPScPI!zXMxU|e}}x
+MltcpUEZSL&v+!nN#6pBc0t?&~uB-V5(re~z7_R7A@vWj*#f%Dx6yzwXQDCB&MDd400cG`FH3f3!4u!
++4#%A3yGx3>k&$N2xoimr4`H{In&4gga*XiS@+n!!_zURKCkxW6(gObf6n*}usY8KQis98L-cxK_u!k
+L9L3uo5+yKu!bi)R+ktnthl+<0d3%%Ye@F$-cA#4Lze5VIg=LCk`f?=FMl^|~)T1j;OwStzqmW}(dMb
+=RVUXBNpUl6lR5iZcsm-ghWEcxLg;LYaj!3uP9{ER<O&^O}K}XBN&ZoLM-taAx()9C&8&%;K5FGmB>y
+$}E&wD6>#zq0EAq1u+X^7QZZhS@R8~)${KUO?8P5idhu1C}vU20+_`wi(ghx2@8&%r=7bN96hTGfLQ>
+u0A>Nq0+<Cbi(MAFEOc4GvVdg)%L0}KENhN@_RJixWns(0mW3?~TNbt~YFX5>sAW;hqLxK1i&_@7ENW
+S2W)9f0uw`M(!j^?C3tJYoENWTQvZ!TI%WI}c=(5mdq05?YAT4%T?6TNpvCCqY#V(6o7P>5SS?IFRWu
+ePLmxV41UDkX9X<^I4mW3?~TNbt~Y+2N@sAU1m0+t0Vi&Yk@ELK^pvd+vLU}eF|f|Ug;i&Yk@ELK^Vv
+Iu1n$|96SC<{&&oGdt5^RJW_oh&$6aI)ZJohcwa1e`25S#Yx8WWmXTlLaSh{tkZHJfVD7h_Vo65y~Qz
+1t$wm*8J1%(>V~zdQQ66kCF)lLRo~eo<k3avJhnv$|96SD2q@Qp)5jKgt7=_5y~QzMJS6<7NM;9>-R#
+G1t$wm7Mv_NS#Yx8WWmXTlLaRWP8OUjI9YJA;AG7|%wBY|;AA1mqLD=-i$)fWEE-ufvS?({$O4fCB8x
+*7hpgw!zCdJw$O4fCA`3(oh%69U^Dlo-2TLQ1Miz}M8d)^5U}QaK<^Up#K^B8723ZWU7-Y>q-aTsu5L
+qCyKxBc)0+IEccSQq-yfYLIAhJMYgUCxy-!!smWYfr|kxe6;MmCLX8rd|mVPwO|cTFRkMmCM?x5?|0<
+dWo)<dS4bvRq_Iv7}g1EGd>0{_dscpQ-N2VM({8+tO|6wsc#%E#3CH?MmB~wiF>nNZ}}FgUAMv4I&#v
+Hi&Ex*&wn(WP`{CkqsgnL^g<Q4%r;CIpm#TN>Y-PBqd2nQj(M;B}qwAE>cpo6fH$d(NdT>G>B{v*&wn
+(WP`{CkqsgnL^g<Q5ZMf}8Dt~KMv#pl8$sR~J|%fd@|5H$$y1W2Bu`16l86BY*$lE7WFyE%kc}W4LB4
+AS*$lE7WHZQSkj)^QK{kVI2H6a<8DulaMv#pl8$mXLyfak)46+$yGstF;%^;gWHiK*i+4!;XW7Ef`k4
++yNJT`c|GgS5DvB_gY$A*p#9UD3}bZqF@%(0ncGsk9*%^aIKHgmi)RQ1rYp<_eGhK>y#8#*?0Z0Oj`v
+6*8t$7YVr9Gf}z{6ljM9UD3}bZqF@(6OOoL&t`W4IP^~Hgjy|*vzq+V>8FbjdzBso;NmcY~I+sv3X<j
+#^#NU8yhz^ZfxAxxUm6Qo3^(3$Ghf@%^RCHHgD|LLCoj;c)Uob`k7-h$DT8oCW#Fm8$336yfb|LQaV8
+Z9UD3}bZqF@(6OOoGsk9*%^aIKHgjy|*v#?HP!K@JhK>y#8#*?0Z0Oj~v7uu#$7YVr9Gf{db8P0=^M#
+=e9UD3}bZqF@(6M=A^Ty_ljT;*`Hg0U(*toHAW8=o2FAQze*w*5W8XGk>YHZZlsId`aBgRIIjTjp-He
+zhV*od(a<DH=(V2sTen=v+HY{uA(u@Pe<#zu^d7#lG*Vr<0NYijU0l_CfjV>8BPjLjIEF*aRny4YZ`!
+D55O28#_A8!R?h?D<zHd%x=6+v*=LHePJJ*!%T{bgG{&HeGDG*mSY!V$;Q*FAQzG*m$w=V&lcei;WkX
+E;e0ky4ZBF>0;Bx28#_A8!YyGVQ7=ZhKdap8!9$aY^K;uv6*5s#b%1l6q_kFQ*5T#OtI$+LmMhKRBWi
+&P_da}GsR|#%@ms{HdAb-*i5mRVl%~Nip>=74AnnWY^c~!v6*5s#b%1l6q_kFQ*5T#OtGz@+p?=IpV|
+`W&QSfEF}DdqPYY&>%@ms{HdAb-*i5mRVl%~Nip><8DK=Al*HE#cVnfA-ip><8DK<`QoY*+Aabn}d#)
+*v+8z(kSY?63qsOnK-lf)*8O%j_VHc4!f*d(z@Vw1!siA@rlBsNKGh<InH>LFr7#D<6s5gQ^lL~MxI5
+V0X*L&S!N4G|k6HbiWQcxR~UA!0+shKLOj8zMGDY>3zpu_0nZ#D<6s5gQ^lMC|!~&!&hC5gQ^lL~MxI
+5V0X*L&S!N4G|k6HbiWQ*buQHVnf6`LsbtE8zMGDY>3zpu_0nZ#D<6s5gQ^lL~MxI5V0X*L&SH@51Su
+0KWu*3{IL09^TXzc%@3O&Ha~2B*!-~hVe`WtKWu*3{IL09^TXzc%@3O&Ha~2B*!-~hVe`Z0hs_V0ANK
+fR^TXzc%@3O&Ha~2B*!-~hVe`Z0hs_V0A2vU1e%So5#}AttHZ^Q&*wnD8VN=7VhD{Bd8a6d-YS`4Usb
+N#YhK4<VTi(>LsbN#YriM)on;JGXY--rlu&H5F!={E!4VxM^H0=2r#HNN#4VxM^HEe3w)Uc^xQ^Tf)O
+%0nGHZ^Q&*wnD0VMD`rO%0nGHZ^Q&*wnD8VN=7VhD{Bd8a6d-YS`4UsbNFIo`aH24VxM^HEe3w)Uc^x
+Q^Tf)O%0nGHZ^Q&*wnD8VMD{7gOW`Rn;JGXY--rlu&H4K!v=;83>z3WFl=Dhz_3waqrygocZRB-6*en
+uR@khtSz)unW`)fPn-w-IY*yH)uu);7!k(|=Y)sggurXm{!p4M+2^$kOCTvXDn6NQn1HuM`4G0?$_Iw
+>@6T&8hO$eJ1HX&?6*o3eNVH3h8gbfHA5H=ueK-hq==j%9|5H=xfLfC|`31Jh$CWK80n-Df3Y(Ut6um
+NF%xJGR~5Y)7wAwXNe_N*Icr8bklnE?>OCWK808xS@iY(Ut6u-Rbu4D@iY;b6nThJy_U8xA%cY&h6(u
+!&$3!6t%D1e*vp5$v9Uo(47zY#P`gut8vRz~+F>0h<Fh2W$@59I!cHbHGM`-80ZLz-EBW0Gk0e0&E1>
+2(S@gBfv(0jQ|?~HUew}*a)zD26_hA46qqsGr&fGP5+wyHT`S)*YvOHU(>&)e@*|I{&mklkN+C~HU4Y
+**YvOHU(>&)e@*|I{x$t;`q%WY>0i^oJ~Pnczs7$J{u=x>_-pXj;IF}7gTDrU4gMPZHTY}r*Wj<u4D{
+r$$zOxN27e9y8vHf*Yw*|Lufbn~zXpE|{+jtU^XoGMJ@jkl*UYb(Uo*dEe$D)v`8D%v=GV-xnO`%%W`
+525`piHN{hIkT^K0hU%&(bWGrwkj&HS4AHS=rc*UYb(Uo*cxGtfi7W`525n)x;JYv$L?ubE#nzh-{T{
+F?bS^K0hU%&*T3^w6)FUo*dEe$D)v`8D%v=GV-xnO`%%W`525n)x;J>*2n}eU1AX_ciWo+}F6TabJ_Z
+CVfr%n)Eg4Ytq-GuSs7&XP_s2P5PSjHR)^8*QBpWUz5HjeNFnB^fl>g($}P~Nnbx_peKDz`kM4L>1)#
+0q_0U|lfEW>P5PSjHR)^8*QBpWU(YebCVfr%n)Eg4Ytq-GuSs8%z9xN5`kM4L>1)#0q_0U|KWCsPeNF
+nB^fl>g($}P~Nnb<0hI|eA8uB&dYslA-uNhxIXP}3C4fz`KHRNl^*L<(}Uh}=?d(HQn?=|0RzSnrK@m
+|kgZXdpP(B46N2kjlSchKHJdk5_uw0F?nL3;=79kO@G-XVL3?A;k&l3bEpl3bE3NtPr_k|oKKWVy(aV
+o9;2SW;{$4D^TU9jbSz-l2Mj>K&?gsNSJ^hw2@wcc|WhdI#zqsCS^=fqHj_AxTJ*lB6UlNlKEEq$DXx
+x=2cqQj`=WMM+_xKUD8fy+id5)jL%0P`yL-4%ItU?@+x%^$yfKQ13v!1N9EnyE8nJ97&EON0KASk>p5
+nBsr2C7kNtYl;SDHQ;Me)&#9EBBu`16lH65)sNSJ^hw2@wcc|W>dWY&AsCS^=fqDn(9jN#2P`yL-4%I
+tU?@+x%^$yiLRPRu|L-h{TJ5cXHy#w_Q)H_h`&QR4;y+id5)jL%0P`yL-4%ItU?@+x%^$yfKQ13v!1N
+9EnyE9bvRPRu|L-h{TJ5=vby+id5)jL%0P`v~74%9nP??Al+_3jK+J=Hr@?@+x%^$yiLRPRu|L-h{TJ
+5=vLy#w_Q)H_h`K)pLdRZsN})jL%0P`yL-4%ItU?@+x%^$yiLQ13v!1N9EnJM*ti9IAJy-l2Mj>K&?g
+sNSJ^hw2@wcc|W>dI#zqsCS^=fqDn(y*ohf0KEhB4$wP5?*P5S@(#;8EbpMagYpi_J1Fm<yo2%%%DXd
+E^(^nOyu<Pi%R4OZu)M?a4$C_z@1VSc@(#*7DDR-WgYxbSRXxi)Ebp+q!}1QxJ1p<8yu<Pi$~!3UpuB
+_f4$3<y@1VRpLsif64$C_%@36eX@(#;8Ebp+qgYpi_J1Fm<yo2%%$~!3U&QR5}yu<Pi%R4OZu)M?a4$
+C_%@1VSc@(#*7DDR-WgYwQC7d$NQu)M?a4$C_%@36eX@(#;8EbpMagYpi_J1Fm<yo2%%%6oTM-eGx%<
+sFuHSl(fIhvglXcUay*d57X1igzg9p?HVl9g25nsOmA^!FUJb9gKG{-obbW;~k23DBhuXhvFTIcPQSW
+c!%QM8LE1WcQD?;cn9MhjCU~J!FY$_9g24--l2Gh;vI^2DBhuXcZRAS;~k85Fy6s<2jd-#cQD?ec!%O
+0igzg9p?HVl9g24--kqVU$9M<h9gKG{-obbW;~k85DBhuXhvFTIcPQSWc!%O0ig#zI>M`EIcn9MhjCU
+~J!FUJb9g24--l2Gh;vI^2DBhuXXU{;7@eam281G=bgYgc=I~ea^yhHI0#XA)5P`pF&4#hhZ@66vtAB
+=Y}-obbW;~k85Fy6s<2jd-zcPQSWc!%O0igzg9p?GKhLF|L^4#qne?_j)x@eam281G=bL-7v9I~4Cwy
+hHI0#XA)5#0>Np?_j)x@eam281G=bgYgc=I~4CwyhHI0#XA)5P`pF&PRu}$@eam281G=bgYgc=I~ea^
+yhHI0#XA)5P`pF&4#hhZ@5BuB81G=bgYgc=I~ea^yo2!$#yb@6P`pF&4#hhZ?@+u$@lMP@kMR!1I~ea
+^yo2!$#yc4AV7x=|4#hhZ?@+u$@eaj16z}8=^ce48yo2!$#yc4AV7!Cz4#qna?@+u$@eaj16z@>HL-9
+_|K#%bb#yc4AV7!Cz4#qne?_j(`@eaj16z@>HL-7v9I~4Ea4D=Z9V7!Cz4#qne?_j)x@eam26z@>HL-
+7v9I~4CwyhHI$&Ondx4#qne?_j)x@eam281G=bL-7v9I~4CwyhHI0#XA)5<P7u}?_j)x@eam281G=bg
+Ygc=I~4CwyhHI0#XA)5P`pF&&irc=2jd-#cQD?;cn9MhjCU~J!FY$_9g24--l2Gh;vI^2DBh_V=rP{G
+cn9MhjCU~J!FUJb9gKG<-l2Gh;vI^2DBhuXhvJ?2!q9{94#qne?_j)x@eam281G=bL-7v9I~4CwyhHI
+0#XA)5)C}|(?_j)x@eam281G=bgYgc=I~4CwyhHI0#XA)5P`pF&PR&4%@eam281G=bgYgc=I~ea^yhH
+I0#XA)5P`pF&4#hhZ@6-(R81G=bgYgc=I~ea^yo2!$#yb@6P`pF&4#hhZ?@+u$@y`5}-NASV<NZG!+p
+!75KmY{6{HqWsa_C5dx=~;!78341vG<c}VPW9Jcw#&;o)}MzC&rWFN%5q3QamZ16i<q0-k8OBVmvXP7
+*C8R#uMX-@uYZCJSm<OPl_kSlj7NC=wm!Fo)}MzC&m-wiSfjEQamZ16i<pL#gpPm@oY2nF`gJtj3>qu
+<NfFP7-L=D=ka;jm+Sr>;|EYn0|XQR000O8^OY7y1+&KF83O<Siv|DyGynhqaA|NaUv_0~WN&gWa%FL
+KWpi|MFK}UFYhh<)b1!3PVRB?;bT4IfV{3A7a&KZ~axQRrl~he{+d2@u`&TULivu`n+=mCWFX%y5?9_
+<AAlWE#GH7XK6QxLlq!K8<zC(YdZ5-fLCuzAmoS9v1yM6hg{@{KzrST{kOtS&?$Ft;t-pOsZ+O*g*&8
+*x=EfgiDV3Lk1^CFcJk+B=f5sO-<)RC56BJ+-)SeYGLG9~#77|5Tpce&UxLRD33nF}wCcKEbAWA`5br
+n#|)cy3vEE@<8C5#L@VY{1$zxN7UElUz~n%a_l9xR@SMtQ4go*HOxj?GqOVC5h4A%609H?YOJ|5Fwff
+`-A3EZLF;5n!wPLH#Bej*65>tSfk|H3flyiO3O^nl>QmSZmBUf<wBF?Lb?MyjPN9M@zGe+SEi6<6GF8
+vASbZ)Y%6#2@P4|aF*`7&d)92BW?rwA%uzg+Icvux9PL8PZC{-l1|r#!2n`M3_wL>jOPq(hpV&G?=u^
+}?-F8ImlCjhgKJZ63ccJhKq#IJ`hCNkA7cF>IT9DLx*e$%r3jiVX8Dm6irrL2?t1b|bOW#L}Z?ols;^
+`xOkJB`sE*@`@3#W04{vD8HwNnznP-TVo4Tu9^GDwqesEGTc@o4cF*bhgG=^)E!I7=y}d7Lgr$#NX0G
++(CkSvH7>GUk)T!OTl0pDiu{Ss6H4uoqIfgY-wV*MXoaXe*u=ZO&O@qDbHu*)@OFLuK@4uNNh+hTf7a
+NgLm#XDcx~n7%jN1<GrGyBPXB>e9bGBnkaPp#>Srhq46pP#I&p)HlwD)Jcrmn@_#o&2{gen;y|JL*%`
+O2GhI$xkTEnR%uN|QMJyu!aDXlYu3<@{)L}tQY$a}jpqcX8T<ME^jL%KBK5|3j9Mk~t<-!fOZF#OIGG
+$yP9=aj&s~`5W5HmVJfwHO9K={hjl(KB({rx+Ejyb`u&q?9)ykD{?hrii>s+RdWr1CR$tY>Hl2|#bRm
+ZU^kMZzaE{uLpKWB%pXjL86PU!0e;hkT}zA1ep`32s8ecz7G9|#t!(Ia>kwqd_Ic5<af#eWL4>}#v(1
+8%r}NI$fG0#Hi>1QY-O00;o{l@>=OPoosyKmY(ZhyVaG0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>
+VP|D?FJow7a%5$6FJ*OOba!TQWpOTWd6k`SuO-Ei=HK5>QIfS<TWaIVKUEoJuSdcd&!Dw20<4kujn%-
+#w5I81ZZ}5kr{DPfqI$g2c%)rk9QvMfDl029GUACRp3L);pZw$hw%>gC&mVof{r;m5KK%Hz54Yd^{*N
+F0&+WhX?oS?mvVH!|<Mzj=7k_>6>iNs<gV$d@ZZF<!e|Y`BU%Y(zJpcao+wWh!`11MN*H7D*FJC;qdb
+@p@-+Axh-K8JBdhzx}UisU{zdgQu{oUi!_WJAX?LTNUzyI;$+c#f6|L*bsYWBtJS8wvqr|(`r`A0Uqe
+e(LL-v9FH@%h`wueL9Kw8j5?tLIO&_1%wot8d<JKmFimTf45vCjQ~q-`n1Q`EvV|@4neSeSGuy^tZ>a
+>PbF${p#)0i!Z*<$NcG=pZzl5*|tACfBLuWlNT@l`t;)4?dMze@a^{Y_4dh+Z@+o{>VEzls+TXmczXW
+yBct~9)8pgz=JnTaf5?vgYWwl^_uH4xUu{p1U%hx!&F4V8-JZYt>KCt{^7?OIfA!+)A9?HhS6}7h(Cf
+F4Pv5?IH-`WG@t?NeKfcMHZvXuF>hUR`=9BNgc=_VXy!iJozI=T3=5c%eX8Vr!-hA`-k@_7S`Rwk<_P
+f{FyrcW8?eRr^&b|Nk`1B^<+4_6!-QDgMpKUqUKYjj|{^x9d$Av%3PXD;Q%-MK)cjbHka0veXq+e|>U
+fJw7IrjOFY%m}6hcv70i^uKzH;-R`|MFS3kl)?@^`p=K<&S^*e0%@nKX3o~{->Y5|MBO4{#AZAM>;>v
+`Ky**eEVIR;8$Bd)zkA=Z-2~=X9Iuu@Y4_eC9in@Hy{1}qtE|L?|=8v=O2Ig*=O7D{`l$k{`SfHpML(
+)2Y>qg`=4%~{OQwA{`lF4?`_*>kB{$$n8Ex<gZv*u{B<_@?du%LuO8n%fAR87J^G(>df#LRUw*ZH^Za
+j*In7@_zDSGOp63$z?#KVHIehv0)n9$yyyWi#`m625*W0VtZ=Y>HJiSQUN$dMhvzx8_eSV*%(SP~gv+
+Zd4rRT5y?PX5EXL<SWUVNQR|L*1M*H6#3-@JbFmaqQd{k(SXt?fT=V{hB`r_b`8|LVW(!;gRa|L9bH@
+{kRG_@@s(|5sMTzkmPu>dQ3C=U=>htd*7qgn4}Z^!nTFix+=AwjW+U{Tr6><M#RU7e73I_21J0<sy0h
+l->U%zxA4D%PYTs_2r9~`NjY7&D*!%{qiS2{P4qjk6(TN-rFC38SNL}<`qxB_$Th&dTiUzfBsL~{@(2
+UZ249{&UbGeD-PZ|T7UP}n)O!O`@G+ZYsBQjea8LPwT>80{YPItKHq-(&eZALr;Pvh>vwJa;-~HB_dk
+EO{kp%WQ~ES}n2-FQuX~jjW^aD+(_`-K=lQRn{d#||?MJP!AHPdS`SKs#?)euikAD0&Y>hs>++M%R_5
+0>6tL)qFzx-zVCcT}WCH>H+zy0*#!Fl=1=YRg>qYvKy{a=3j(Py83`q6Lx^!Z1B{P-`QzMucAXE+$f5
+59T+gw3VB{rexEbWlv<_Uv6F@BilaAO7XDkN($(c~Rup-K}%o=db^M)vvdoKBPZw&syyJvv%y~v+VTo
+tj*D$rN@qE`RSD}(fQZ;Y>l?(2W#c`_Wo>~Ex(*I_^ijCUv7MwGqYxY7;7AP(HPH0pV?Z@cK*5Y4%?j
+1(zr+dtL2qf-mABm&syWt^SGXkIJtE{pN+Zam(Mw>-6I|v-O4ZIw??)x_cQM~^Ts{fe7243)^=R^UUu
+$TyH0Mi1^u1*uOpt#e)%AL%45>*nmG>n)qK12k{6A(p0%@a(aHzuJ=<;Bx4dEYcjounjGZsfXXD7>8#
+|rueKHtVwR`Ok*$tj~^KtWAv2vv6Vlevkj9ce}=TP-HIz7rOW964~<uh{mUh8(pjvcle`}vTMv7Yt)$
+cB!c`N+$+GyGQGuyN%@_gnTTpWcyX8i)OD&+-B3)0v;1j1BGf{;+uPKCkT7mGhA=iwDnnz8KYW9=zn>
+ox8&r%u&8K#$u@Q4zoVcZpt2F#6u2m{?SKp<B73d7)5r0XKwq|ndj`sN^@#+?z}f?JfCJxTFW`lxx1L
+(nzuM|-rCBy_ba>LsJ0xVzUM=xN%?#`)1KyWElf8*$X?|gG_jR`=9dp{!(+bd@7f=>E1S%R^y#w;M?P
+Oq-@_nBK3~Vg@*#7c^45z^G`fG}nD?<5^GjpP+l-^^A>w?<p=Dj<SoW2Tw~-^Voti23d(pT3%GM4>Iw
+vkCV=$qt!6Rp|t;YEFM)$kzUb({C$l1!l$}ec!Gq22%wv8MS>?a);4QbtZhiwEl!q8%8IRugSI`e^#U
+j4mVJfsZ{h8C|d7fXI|cUD7V#_7jNyXj}r7hshA;)dBq+8OqU{kCrJY8_uA=WRK$_UOQ{Sj?SnZ8?pt
+rD5gFco9aNgOLkNo61KDe9MdzKaw+@y{>kT{UINmc004%b9~cC^1)d+__$nx_!w8zKKQiJK^#(kxsN0
+7HF(U$nD<_GcAe)#K1;eXJY0UC)xeTyhaQMQj=_xQ<mEcau4-%-OOoZ6HqK>?QjT1U93|V0{?Hk>Y?t
+X|+<e?C4LsKz%Y=1&&``P)eCNPZ<h=r$YAhYcFw)NF*vo!_+WF1={>o`jm&3X^vCC{I??tz;LOf;y4E
+)H&Jmh=%<&N10lc%RRnDe0B{OWuddZ+F>%FA=Y8<#Jx%sb@dV<?R)PaG(wfEAu66N*PUv3OTa?{~W+=
+k3f6<`ZOBwZWNJ=8)mp(x&qc!8mwp9(=bAJPMI(&oD!v#uRv(wW{5Dra$X0SJvlZ2D1zKK>6p%gV<zD
+Y$R`!mcVS~0&gpABn{B#a=N)!>(%brAF^ATE(?JrH1qj#Vgq|Pc@U;d-x(|zy!p;BZu-gXVB}aF8G`Z
+-XSKT``#XFahcIvhIXx3wP3JS|zRyQPX@Gfa{<R!AoX>P!3?fV#&tB_zq$6c~o$-b1%Q-r@F}@&8+7N
+rtb5}I$E&Dc%E7xw&TKuU|T-?hz#A?X6iBHmZU=IyPvoYM+4_8z^sgtspvK8D5@0g~{h#1Bv?kYRBqU
+<4^Mf!FAXheybD=J4MzkjAvOaJFr36x9}kCdaW`QsSz=7-BPm+&BD<|OUaF1Y0EjyR38TnQC9PZ-;9q
+Vj_aBMy8<cWSY?^p<=|s1hh-fXW3stKGyU*(8=vB*zWp<6$0oo3tsHHFwB$(ecyyug+xTgRdIUg@0mL
+@N5JI+Xa{4owC!e%0^f)x&wL&jnJ)WkFvFK>K$?EgyJjj-}{lHe=I$G*+WZQlC~Utf6zDvgHJqhOieH
+&LMiLQuY;6`Tmy&OoBoVd({l*&UuD1fln-f~t_Gr2Fuv1eg^R*%0T#E*sSRC2P9Es3odn8B+}^_`{h(
+F5iA$KhG#&g2vz0@ZkH)g<ETQcH9CYIMIWTdM<-vMe`Sh(37Mb$~8XNOo#~oaf(LODNWd(Fdgc>xSb9
+v&~5@KLZ47|Y}<5|OXaRCDz2N(SXk{Q)*;*$K9GXpqDCy@&w2Llv2#n3A!Dlccsh@~s=8I53+jwXQ{w
+nV4TCG4p6mTM^2v8y3*-!!IAf)~Di(EUNU>EPc13~a*7E3IYF)e$*B)7az_j<f8cCoX9K&RoK*UP475
+Ov*uwB%np$(v2qJ%fM%ViwK5$5T3jE1!rAxC}$^e3D9A?bnYOJ*-1CBRXlt`5&%PRC6JF>=YwRs!Wrg
+#x`YQ`Nn5OT6PI)s^+i7hi<#A)u3^W$9NYm+%4{*n=_@Z_HsLOZn*@cNCwNc5RP#2#CH(~I1id_P2zY
+p2TGt6T6B?FDI?H@;jJ-Q@`jHc_07&cF&E0O|lF5q7iP9lp8A1m`0H9+|@dl_YjVL<^Fy1w`i(8YP9Y
+g{Q`<(YUBrZvi!E^#0*3`MCfv@R2=j4f$GDXYQuzVa+XAR6Djyv#5hgWEOce_CzaM(+{6CfY7m(~U7p
+@YZ72_G&}JS+pxFgAS500bw3+QGy`q)~o-T+G|WvPu`3^QQM_7y;u~-Y};J8zEO@HRSR;c%;SCV9A&`
+fIt5vD6eWar#Srs2yB7$7Y-MkI>8&HqabGiFCGgwFed{~hS)$^m}h><_qcVhJT~1b@eSl5q&+49P+W3
+~!%YOW7Mv^N8^kx@UBm0<z_V8Iu~=N@P33oTvLABcFwfb+d|AvX!(CXnvHXaoq5#Im)EHQ`i50Hhhg+
+m>OvZJ$3ocoeqHFo!sKNzAu$o5P=EX#TJI@XvJBdiAaZ}5<cm~|y9XF@A<RpazGYw-7SfOFHe2}AL;@
+W5ue~SGs!bgzg7zuFri7q-3>7KWXdBX~@_tDI!PkI@qwM;RC0sd@aVp5hq%DS9T%#M-n`ue@bA+7Ntn
+_w20ve`5&7v#d?T!YNaGA}f~Vt}UHX+eXPl%L{q4!?J|3obc$3_|e~hRw@0fO~1W5<HeZZf8ZY2D|Y!
++vsGz%j9rPAjYV;nIJ#&<8;~yARUYY&#jxqx}19;$)2N>)(_V)XbnGkm7U{;rVfb-y>Z`hFmL$Z<6w$
+K9s<a&a|grYiTBIQ$nW7)Ph9CCqQgbwCs#->-2F{l(nz`25Mc1x!lq91y`={T>~b&|8S{1~ityOU5GN
+^NKBiEB1oyhzO<a<;%31^~9Nn*y4-xE7?iEHq;ye+d1LV_27<ktYZX1-<q(hFg?DtGuGRoJwQuWu42;
+pPrUK)0gQPR~kY<tcVibk08hPUG@+q?cIDoej^WWojmRCeS`MhaICi>YHC9f}KkoVY`nh+G5Nb5=+?A
+MjydP*HZ48PY?pR2)Bm2)vVh!5!jboZY2i!Rj<EgT=WI5sGdI?>tOn!1Yma=!7rnOi%05B4n9Uz(5GX
+Ob-TGD6pH~EXRS=W;yT<nkyPGcNYFV;Y&{a$pD9wYzdkLWO&%T$myXC?0o5t=^(~42vxZN%xxK^8~2^
+y5*&Zp3Z&G;KU^T6;lG$r%&s%=mncg(nlJq_WDf1-!gTf1H4f<~m^XkES)2UIWjqFx?gvX4P$>c&@-@
+aB%){Ka$Tkw1<HUCtZ<G!r=WUj~vnokXPPnS-CE;dWHpwtvpjeZ=`jdEkF-jNHND_D6ekWmM;>oFF1o
+AQL2N?l2l9{z(%={E`Lv%budp;bQoZmCkfblw`#y6woL-)orM=M_%FLoae1Jtl>elQp+rp%-W#+C>QB
+Y;1(UC{iyaY$UU3_J<Wv$adhz$Z4=1+0$Amc-$9iBnH}rwgI;flRbt0nOieOFE&<-*A0oO-=SAxckni
+f<If6%Q+3Ln16-Vi-rgSxL)FMIzN!aJ9|i6vH&jWoN~z%85cIVW&vK(<&(vapIptn)Oe)>mGB@eVi|-
+?_$}K_Oqlo=2Lk$TkTF}6#vyo;Oip=ErhPljANfjjZ|!BKbHoctlcVM>5|?z?*=_(1vE#tNK^}noZC1
+$aEFEZOL4i?t7nPBa=Vb#F-J}g@H*rZbW50@#CPM^eu-fzKaZxPNBbRH37+!#$2FIC$nomeX?0}8@6T
+0b+^8qf|$c2~D^{^fwIVX3^%Z@L~g*#z=H@x{qh;N{`%r|)v*AHUCjVCjs%Q#=?8+1AZ!6p>=bu5`d&
+Zhxo(Igb@kUJ%fnPl<#TxJ9pe!A=-5|>Pq^~)t;h=mSbP<Tx75>r42=Q08F=Thsy(KSU+P1_r|WAeA+
+f75mC4>ZmfFiFN9(qcjfWPh=d-2r0F5;VZynUG+1VcaIF?ed7AmK#4@;u6e+z&fD=5-a$mw2NQI9cGF
+}^4l>i)`}~H(nUM%xzr?a2r9vL!6i-J$an||OM2bu+h$$j3>P><V2Xz+ZV)fwcwN!}Oaai0@MXIP^?-
+t&k~d5l_H(kL513U5j~oZh?xb)2i?KWzG&u>vk~NK}dO&)z`Opb=c{>?i%?UHL2EsgF%hf<K+oeH?%s
+bf<FrO594zs%^EnoikN?ej{fcu)hRyG3z7jKY~h2^acp9n%AOUO<*(sq9lG=Lb;puPM<I^7fhU~8l-t
+cyhmf@sWPaZ-Z0gd!%#CTUn;!HnQAGJu{*rcN5ZX4SLOh4+WvYCimeV@YSq9b=)C2M&y=l}*vNvT4GM
+P=Lh4wDv51GQ&Www<$h@++F%rc)4ZKut}#$d=tdG=pR3Vu~BlGqpq;449i&6ZsL-3Ry>-(>0p5kz6zL
+X2H>Vk*R!zHf)VL(|G*z|+ht^?deqyyaahb-uC3`5<32mlb<}9VrFR@N{7$o4&|KS~|5=4DQsYDHp+V
+jK4dUDMo?sz)vG7lZ+lM@id8P%pfx{&st3xKqx~#dhfY<a7lS*yAW0Vkv!69R{JuJuBZZ3qvHc~aPYo
+zzRxlAz16&6FeqT+Fevcx5$ZpUK2ME^!yY6McLmOoCOUOM1#9>mZt;l^F@g@-w1jc-Ej^pgj}C{u|0h
+RtXUa`waTgF%SLBw%w?*j<2jyW_1{d1)y5{CB(5ak`}}@Z6-f(RLvh>l5TY1yd~b3@-fHu`_)POg{}3
+l*MOUx1BX{3E`i@)P@bh)zLVt&Y`V3zIX~MA1wM&OBhm&2;PAyi)qZ4S{IEv4p6S&3_8xz{NL(AcR=l
+yE(dOL_()LTq-TYR0g04<$TO>5amgt^X;JCqg|#JIgQ;ZPGAIHxSt|iI4N{w<X3!$h;(K_d%ea}&f07
+TGdl<Y-|H!ilwim?Zgd~J2*|6G8-!-kRFWm+C0{tB>(^d1<6PJw3x|$#q0_dR0!xc?NOD_j?i9ycV<<
+}1wH-YJDx=2_L_^7!?wM!jmTID!>n*hP8EP|v-IO87<M^##cDFY#0AzsHd89_tJ^=>zDNqSu-Thz(Wi
+c@98@7Bm5>QmBBptl%)Nk?P*^n5cRpB3NI+cR%G-(@WZRF7DolX7>87@Hmw=470eP(a{zlDKZ!nRzp}
+ZRU?<za72=dvAb_NpWP&TADavOMq0dE|&=-(utNAevb)V5LHb!5WqBVy#|c*!=wZ`EI%KDRWnLBS?V~
+m8Ig)0VH~<Qq=eLouYuWxWqtoVs@=pT{gjt;qG%2H-mu0TODvzdlO^c@Zyg3|lStOv71r@xrsA`*-~3
+08FKD&|1R<<(Fz{zko}{ZAYrA3o8)@Zc5rv{*gK27{r0uBjs_`AvapW4*VO->PfYp9FTiPn_xQSH&*O
+rd2P#u9E!RrF#2jziw-TrKVONiguK~)EYF(BVkKXZwpot}#}^yd05+Aw&YvV24ia&|N%wo4snmHlGxq
+$d?!O**H=J(5%lQkKoE8xVrJss|?Y)DvI8;oF~O`t`6;*%?qUU1px_d}D=dmqQEtvRmh6QfQ-F>NzE$
+!LCq7-K2NF<MHhJNuH0@Zr*^_U`i$qGXk=e<6yl3t`8FZX<Rua3kG9OnrL=8YaAxHWS!0jXj=cr7(uW
+#N;89iy5T|=Rjf|kw1WaN51|tSw{k&{YZU$NFSw+ezkz5@Vj1<f*~A-m-}HJof=(U;W0w=a6hiBUTVx
+#0+n?oH%yprL0y1w0Bz1xR$t*192(}7ro(@-rjQnPr;_ZCkSE#`duljk91ANJKFa&bmE=T~}u+ll@Qz
+w^u8~!}SHoiA_VWbH&S5uQwxa0trs04s)8ofAjXMx0uD100y#11^J>|zNMK31CU#FY*I1v$9PU}!gS$
+--k~r!me`V>62;qnxCxJP44eVS5vXVJIzuqgsb$=dK71Ph9+2VkFm_N*cIyX92H^l`oY?pvas>2m~1e
+vS-t-3+@RrK7khvu>9m8X_q>Vm8ipdGq<}gnLnski@2^M%yd~rtHL9+cz$JBCLwq?3gOd=1MOPJaUHX
+ScERl>CMCpoml}myDvd*qia}edxQ0>@vJY6h?r`^e9H`@1yfj{rnGDjzC3eJ!7QTL($sMY#6yp((0qy
+x^orEUN;<)a1(U(LwjbJr&o7CRvIUc@u$Nb;}*h>xDKmr=Fgai+?s^U;}|HO2OCk-eP^;dH~uuJl32D
+!`F;?IU0;34+}fW_j6f;R&CY&CLazbADZ?Y&jmKI1VnxLD{*Fb=T5pE80LOh`@(J;56l<3&`xyrR}|T
+IfUXrCws5wHm9tbdVBnTUIr}N0=$h7%x1bk%RSv&S&Y1&U<`M_!3rS-WXY2SF~mac{Cp!<J8S}8GM!>
+Dwv5qOfv__jfDdrSLp^5Nv5CN<uwSD9RSyqT7t+ytP5}6ES6nVKJd;NDgcQJy$o_g>qhK&`-c;K3HWF
+`tFVg`9L7z0S!F@%Syplhk2p}BJFd4Q2<SqWrgE=We;4|aWqQjKG>dD+pJ6p5j4I+zfF=pWhR|RPpWZ
+T`r6F~sLz4e)H*v`TS#{iD>tb$Tg36ThvTumiPVWGSnAZ9b35b}Ru_?G0POWqxQ{C&;ur8u7Yo_6bGW
+Pl>P6C#X3jE^ohV6}3kxtPDF2@=sP;wQ2HcR)~RmVbZOjneByI4o~9Y&Sf!$3PQC@18oGL6D<pe%&*p
+jtfU%|D=f&5Nv=FpRJn(wmy83!&S68JsCt#VDpK9PTe>yPR&!`KmaC?scJD%Zs3PtlCO@QE5Enk|+QZ
+LA%M01wRW5<U`{sb)&*y8?Sjo_qr%pj4pO;YA^>6r0mAJ2%vqa^>zJFP&F*kOUUIkT@0`j3Fh5y;*wT
+^2lY_^Ju}rq?qIm?oow}!E-(fIsRm@lXyqqK&-ik?^G$TG9d+11EH-%Xg=gt_|Lg*(rfz_ByyWV3jK^
+rQT-!jR-Ao5@=ACDudu<r_9L1fIMW3uZOpMfj5@aYG<m1AXAqVbN9!TdiP2i^p^e}ke<B+%{zfT^Z#L
+)y(#RAYwSrPneB1xK59me8NdI6uhsE{xC-wx=R5cIZ-?lqKTPKhOq2$Qtfpie$5?1tWhk}c(d!n%|cj
+}l?vq>6^EcG0~KShE3&>vG;liC4TFpJ*sn)5uDtlF^hf?ScYq9~ZI?MHn}JK=<0=cP2%Q#qg{{SB8TQ
+(Erqu$w85$4UDpp$#ueFe&E^$*PHYncf0T<t(F@6nN>HHN;D4Z&?rOYGh--ZJaEVzN^q1=92Q~dJ5RI
+yvNLqAlVVDv2-m=U1MO^=j9#TL5hub&AxCJ&UXtlbfVzJYJ^=DSS=&YTiUyL5fNz29;D6yu8i-~birc
+s=!qM4;k}!|A@B<kFe8M4%e3XAc_quHWrwzK9B1V3Jh)X>*4#h68_2Amjrg+lO^;R<CZJ<)o#KpMVy{
+MaBPOO~<G&7(l;SLFBpDNUq2%8pMu4B0LL%-v_m_bdvaLIx06&8XlkKB^HxAFjLa+GH~+adaCnb<@{6
+(XHzdQ3&Kmgbj>^KKWu1ow@yi$DcmatI~82xsM<2z3hB-Vj@<q(r|%A!fm_3|3yBr3*UHy$(c-V>tFI
+0QA?ymrkJz9(q9D3>NsL_|?Fj3nkowI$MWO9opjPce|(SO;?65Q2B#{Ryo0LiX~IX+8z+NgZf*OVpHT
+NuBSNOfLh}!{rZ9KwV^;7m)L?z(Irl0K>|O90(<Dy94c?a(}eSeTAO^n>+LY8yMBD2dtK@^hJF>1ofS
+4DnNwU6#f`vZ?aCyzTFZQ<saAX)2arVN-G{o@CXH?^G1v#$;2|?a@dv_eNm8=>maNXH^bNnqrV+^PK~
+A}-kl1b~zFoRQ76;!_$Jxm!W$zS`%Ox2C0X=;gs_-%WQjO1QbbzWZa-e%{h+|qe*zzxoGjSAhh^cPRI
+U<a?=foupYj>D3S-<Y;(SsYF)h=}$5HU*{@8~^=2qErc+|;zIl_!6bAq(jqrc9UuQMI}JTgBr8-Rl%>
+b(6SV4ng)qRf(9C!QfteWgTKRcf=r0a;P>t!B^I}=Z*b16a#Vbn643t6)j7wTnRsESrF2=%l$*Q46Lh
+(dM{8*S2bVy$phUh)hCdvI{*xxkUaQn3L9g%2MQ|TP~t<lJ1oWOPo?_s`sePg%RhAMIEU{*E*y>nXm(
+oC1KTv|RX@c5SjQ~ag+gha64}J#5RMnFKG3~(^T^fo04`>FL;>KG42LRVUr?j*Z7HgCy}>9dkc-lD$&
+Z#jsCzZ+!U-(ddbUV3*qX)?p2X+{JBwP43I+LjvklF|vEj>8O;P!s(!G)zOGRB(YKF`sBVbrIs)R%C0
+P1<1Qj^vTmk!CNVG+42?6}7_amiE-M7gFBXBVoD1^>Dfv|J)Uctlv5h94P}1t(jga&5AhJ1z%q@hE=s
+K=;~Dx0|TV0I4-Yyo`ZQ+9l_Reo~VJ%1gpdCKwZ|^6*sOQtKGqYhH6={?KHnc@B_d2@!dZwPM_~jH(j
+hlG!CPQ@$j<q)+y)>=)f@V=GyQ3GHlf8f;1G(|Q0)2;oV_THyX^LZLaGG${!JdQ)aRbrQAS(7kpN$7Y
+z`WwPktCHhBe;kF;kvu2*a@?IPq5QDuYO#)0|@F@M{f$nvXE3LD{z2apouE|_LnTtzc{Dt1lvcoVl!W
+~<7b^#a@?sb9gwZUoSq!1M0<4a8jGgUiB6ew4wL6kBTV;wII0E;6cEj6|`Z-1bBU8hX`w9}Xv-jPy3K
+8R!P?g3XG$Op!B!;iQNhumvpN6G{Nj&|Dvy4Mzxuv#PDv6)`G3#N<IT(gWcG}2`bINdfuCrCes&~VpA
+${x_Y&Q{eMq$Pa+w9Xp}i91(PdV?84JvyvVGnSjG!hB@DqVe#+_xPfFZCKZ={aK3tO?GGqeAbwXbsD8
+ENfTV83Xe+QU2010S?Z<tJk-6CgKekv88N>_MjQy}zr*S`0`;I8GM1fyFoUQcNW0no$TT8qxa}9+Ya8
+yIU<j1%V4*1d88=pxzSgIaXRG&^S|QP4+GR16?v!5RK=(R8`h)UE7x(q*7Wkgx9u8p&pV50ZiY`Ot-U
+V1?Giz@a!i^4<za8ja2mJ3+B<1$Cx)M(ekGJ7+TDQ|08&Meiuue0%i&3R+tCr@f`p|*ywYzx`#mho=v
+e1DnS=376*cHqATok>RWj^GIL**%%(Ng{FCX8|0!yzsK%-r+g1`*Y71bd1NC~+G)t=>&sL4_0>#^tUS
+l0$cmO1JOD(;nzvCwo4Y+f^+*9%|L62@q>Gv^bV9@lBzO5flLoOfN}>JN(p^-s8Z2oU@#(fd}6;+{Iw
+`aU6gd@{z`lpFxlt7lk=fb5i~#uf1?@SM94i(7i52{g?Bm;=9{@+XZw?=_$q>KEV>?>FzjS$E8%dOE?
+o`EAKwgy>@E+qZ^OGbEL}?c(;ltt<8&|J@uY?b7Y#Q3S1W_E{FQXwyK_WpnDzWxVv7=5;~-&Nz_zBtl
+ph|ObfO6f3*&vq0SoI+;SWKtN616-Rl$<tHHT6Q}@kiZ0azerB#QK`%KwO<(HT44Iu~1q=h?hs}6and
+vzVTjxQ^c>IACOctRkQ3X;o&SdAdSU%Ug)Mfdm<3|9LK4|K2VwC=>DSPr`d9r{`_BjN=L-=Wk=X|-g<
+VL&@vCKxt2W7a<R1KsO#Pw=62>93b$B6zYX>O!UiRG+3(OLvUJ#b7P3?fPkf{2k}fy*7|@C&3#gq^!5
+)-oT~bQRQzj$BKg#(DDP4S-0k5zqKi{>Np3w*Eucx8AeW3CCs=rE^<~=9NjFb`PPfqAg5Cpb}2DOx^p
+;QwJy}X0;CuH7)znvWoLK~s!vkBqUtgtFqcL5K$=jryo`QshN*q-hq_nw_LLt+Z4Lsazz;UdOI?P}z5
+2D@h-wh}Ds!T|qq!G=yLit7-Roc%?>dFg?!IQWFtKeb?KgCf66{IF#b8kE=)#|!l+;b9T?D%B@kRGaO
+`(Ubm$(TwUg-7))v-m<xu5DwRQ>=U-RMBEV+e%UlGF)R>((nD%l$a)32Ul4s5f_q*6mLUy6aD#f!MH{
+G94<;si%Qj(0Gj@3wN(Kb+7IpI`uoDR1UpS$WNkmv#@}}wV-(ly}@G5JWHO-{iD+cYQ3R*oss~{(m4E
+%`;Q5>jcxsd%w#%cDy2vcJHm*rrZJqBuw#Cu>;c^?B6jEibxgVvfD<-lS)0w{bLv20F5HjK2Ht+UelA
+x&b;#Y_u(giSy$%XDov7U@rnH*uoyc)`5%vJfNbzUZT;d}6v(^3&DNhrGb+^07-%c!Fpar$Q3p%ttQz
+4N1Y1-aY2Xj}+Ha<YIJ*b8Z=+*fj<ACmUDn+#Te*)+ge7VYu<eD7?H1{Dcp|9G<(+&cWS$=4-#&!Eib
+g#?cajL;_ApqB)nUVkH4KXK!B4W~pVz#??f%^}XwJp?$tGBQHkO#Wgp-PQN+SNjp-(lgnW7yrDsy0zB
+PvmHuuD7X$1pwe0h8s?<n}0y}+CgBQ?THhW+LAu8zeRMCywKk<mFP0&ZU!V0%t{rkNSgFkeg3%IC(PV
+Kh79~eZ}N_;K_qQ+np=`Es%TPqzl;6_EP@lay&J|?`E_)!4Y-{>@&&H7?!esFvK9!*PO$%307>H7T>h
++g_gaG#*Sh^Wy4MZ|*LJB2nLkM^cmj4QFLre=_b?-nTg&CA#E~0PBk0q;wN7L4>nFO`!JrI+!2&rq3v
+N}5!ca)SHb}H-<{3gb5FHY{#&B<nfN@Ck*72X{UgxybV(?rdJMGtO7i`79<iX*0npI=B8xnKI?vWuXB
+c0Il>*sDANB2T%jKpzK|7!5&y>dmh3#^OffiN`~jHO$4<i4y?EWfg<-NYpwD7WMcPfH>=T)_!E%z|N9
+q|n2yuJRf~It?GEtQ3*d&<S7Ve@}I<CZu~ExI>d9Hs(H_`hfC&bOl3)*_C+9dr^P|zZh<j1kF}Dg>zS
+5Qrl)N*s>>rVVTYhNfF9r^euR2qN@7XCY!<XTiPCTj&NYs{+1KnYexjoJ&8uq8ew#x&Rqy|7fOi1j@=
+>0S!U-EBUafpdonJ0#CxB_iSD(LZ4YWkIXy%xl2pi$si0b!BsJPmf_a%VPHfQOp6>7i#M~P=Y8=qLj;
+^+ybpiVCC1eYynBjI`@pR9so0IGypb~zPb7$5v8+A|7ez!{<#{|B%o3N}<(kxCR=^QE1hWqN6Y@{ulw
+RrcCK$T1dlkN}OhINssc29J8p3&j@1RF=8bQZ1^R~)lvnk;p!E-OgVS?-wm9WG%2ckK&3(Y+4JjT?@s
+d5VncH<X_ZhGPIVnh6_fgAUc$mGg+Lq#t#3t%l|9FLj*m>LHn2W(4{j(94pMaYf<J4!6(gN35(3mp_Y
+{T-A-#QTX>n_u4IEny6sqBNR3g?k+bydEAAP?WQ?7KsL^lP5Na1rn@R0@$BLfu$VimlsO!g5hyES385
+A|>%bjyq*Iv#j+sGLSi`F6OOH}|#1q|XFAOU%6dP*~=4GynsoTO0ussLc+b-dnX^D~w_rRL;mOk}F_c
+}ZZ<gyxl`Dmyc<yR;JEsF@lf59)&H;OLJZOiT<QFv&~yKeu0?sY0i1@|=-B<|rj-6>_DcGvUe5a1Xkl
+su0FEy-m$n-atDq>nmh<V5$nmi7G0O&AtaJdlfI%RGxUAd+zN?e3{Yh>+czUC8pX&fqN7_@a9q*5;;B
+5GxQJ!iaRJsBX!}J?#=^9!`ai=~0oxVjWa9=TAiO1n2Pl5>Z)!MHd2q7gW?0(fA;WZH%~ZhVXZ7xf%%
+GySbE|E<NIj?zPFZDD87gkf#z*o}Gwrhh*)k`0DbRJxYgt1S-oe3!hFN5vJ;IC%V^(e0R7vZkh6W`o(
+gha5+QTpJ#wk4Asx5xL{U0;>?Na7YQOySo1c-CD@PjEd|ZBgbr3v$pN*Nih0=TsdbZRmwc`c7m~F0gS
+*{}^`@7Wd}IQy+NVGfu2O%e%!h<Q@(?z?nAESQ7eGxe{m$(l(7i785=`tu3DFMs1(^%8z+mQ^vgGAiC
+6ZDeL!&ZLlqE2C)8HN7(7kS+Nuv_sa=+6w#lmAv#|A6Vhr3&nsv9N$Wp(*<&w^yQk)!g$6W!~AMV&Iz
+>W$oQ@A%?RMyS0Z#FG%OCHycz2|3MMmgx@peAP|QS-6DZQ9bXmR`Q<MWfvxd2Zi`AW(x+XPN^XJPzPP
+BHq&DJSK%{sug#U>mI@Polwec#xvS8DK`%CL_XIF|X--A8izlo-yKd@zH@==<V*c-@IoVwPY}F5vUl9
+u7S=ZoRa)8B#3cX#*1&kN&NUU=^PIRvW%DhXey7L|&(%sQtiXxn=vbb3<Mo`%SgiB)4<+a9|nm2T>RH
+yM6@?wxTqztI?*K+PD8bUsfVi_+-i4vW}u%kp#4c4yh#nYbXUbmwT0>rvpAv*VZ217iQr;2m8aU0z&m
+U;9U0c~2kR_auGxf9*%Mtp0Y`6k;~$Ddr@L4=@jVh@+nRG*ooOp0Q;iHVFv*T3%Z^;|XVS1$&RnO!J%
+2_o2#b?GGhfLz!ThcZn!z9_GCn{(HKDjuVI<?uSD$8*0x87iXHe#5MWbym9XISr=4-A!#)!0Plj7rl7
+0{DZnz;~~2RH`0z_6^op%FYN)_Q+DG46}ZT4mB%Ei$%0L9IC-h@MfbYGZJnx3t=V{z9^+<2AVnWxnHc
+E3OZ(%CILK$XrLPW)3>&ZbhVHegVUUWqm@<mf(wsCRoE3Qs;Ccx;A<^0{4X}HjnBC#i+s;n)CAb_Tje
+-)@7mD;1!pp+|rWPn`63b>K9v%SVQE>(r7Dv>1Oeeb671C;_l2<bVU_#jg`=y;IFq<2h(Qa)Bt;W#Xb
+#G&)!?fIZUEM37m7Pz_u0%3(@98b15zB3`>cNLBHK4Zp%r0R({OQwpS=kx7*Hx1;JqJTQf!>roUZgmZ
+UCRRz4y8lbbhry0d`5)v90TTN#U1C-y-qA?_BzDL6B7umpr^~CGlT5j-OCER`U`nXXgApxZG`GokK$o
+YbgzxPr6CULhr4jV1<Rr=WK)x%G3+{2aMRsq=GGz~1p6IIdEDo%oakP=@B^-4)#f0&uf!ZR?+VqcIx9
+#Mi|OoU9wg~gY(?Fctp<d}@(<`<yIarHI7J<T=0S@f>^dq9Am1G-x21SAB3`UIZM#4Lb{`)5-7fkP3#
+$~p8v(7k%O4=!VcI)n*HpIP*$s!r9v0o%szQ2X90VfwdE;MnubYBJ<rY$}ZZCi=ZK6}OP>Z_hJgVn7B
+*j-%Xac2oWmODXohNppd)-)?3yOciJ}=LX0X1~<-rAWXI(4&zN<SERw|$b{_o060RP9jq5Y}-3F5Tlf
+cfqyp0ucfppeZ#9Zw&`Gd^r7S@C>Y8CGILW?mQOV>tJS@OCH{_?QT@@M9xcRNt1d)iiXN=%0|j7>8>a
+vxUsX&c{<g-Qrw!|Z5b{|73ZEmcG%ECx2!2UM-0Z{aVGM_zDMw0uAjTzCC|mEP9F%PXS6)`YkHz96Xj
+kksPd&K5uNY|i3!M8b&E#7dGinGUOCg6&$tM()_vobXN2sIIljoF@L>67IZF<d^BgUb7wW-7smM|1V4
+R-!diX%9ektUs!5ju7UEd+`W5oB|4xY=iRovkx!`+1#eo*6ZsV~taDP^G(8_H9L`-+FhCV0pvXpiq%!
+}DNBIYYA7;n6Uq@aT8D@Fj=BiyA0;<VHFf22Wn$!GXv_T2X>AJZTxg!rXL}IZe(q>U_Bq-RpK4Y<<$=
+`7M_1w8mJP`=AYRpRjvN-HL-=dUT69?gNMVIF7qr_TyN(-8JyqpK5W~h|e>eeqckGg6C1g^pm434|(>
+q(C{3u(q*3LUZ+d#a3irt^;o(<lj}J|@O^`eraN=kRWoYQk(yh$4-IhnY2gQSugltoWjXxLw6e7-f@1
+2blvSWk&t1eQjlHn5l#U5!tbDGhJZqtQ?QX0LH^2Jez9`<q^1GjgeS`ZFM7Z%!7FCN6U+}*bH<!9s8Q
+w$3PvQvXUXXLMcJyli1S5)^>@4O_zbs2*X^4qG9IsLBdafEFvozY&r7MgHNorJ0!#yC5;Aufq9ucq{B
+7CRT6>_#}_v9Qy&q;9iHBiG-u{=OwXk7+z_ec)=YXF8KfKBnt4@ckX%I$X+y4Q|9G)hU$nu6zMa6VUu
+h?~2TOvcv@OP-qt_q04@PB}fy*x0SS7r(yHz4p`NWz4QnzI&XAgs-PbUM}I`u`28oAYO6E$EY)+6=S2
+L+SOJSu3qR~m$h2AJBP7>Z$YEaI#8p|2SCVHNBl8p1u<A5?%bx7R@+aDw_oaBLqsDzM2gOgZ_1CP9)L
+@Wa}TB)r>GWml}A<_IKz0k+g3OewZCPddtGkkw+P2Bu7*8KPvY$Y&0UZrIFXUTk}0yH#vE#TE*%{Pc<
+pz);1b0-aI!<zfz)`p@w#el!|`PyLW@)WZl`IC!P8ynVUxPeyIuAP_d3?kQ(63ihlCS1YZ_6r?H;)>`
+4penH4pDEx5+cG`Rms2EOf68q2BCuoL=KI_#NB87kLso66gV&z2F-+N*5haId*r#u>-+F5^f#HLiaj7
+!?#4sQ*95kb$R?YVa)S|mbZcOr0CvEln}+RyK;G`<E|0cc`FOu>+tAm0@d(T04UFOxr2o*HY7Wfn)$k
+%`{ut`<rIvqWLz~Y7yr831(&#Q<`A~w(uvi}Q0R_+KxIc;IvL-->R$LKvPM=-^Pn4d3}2;VTIgP<XWV
+*piB%zFeBBL&P+|+tXR@7V0)%>ZD=#*pW(fvq9@QNX%B=N<?se|&8}w*evjva5l}%6)?Qw}7E^(-)@B
+keb2M~@y_NYU%gju}j>h8y>^Yc8b)Whg`=1Lh@h{It7@FHvKj*{E|@uC#&yVRM#-mq63TIybxtH)jbh
+exj?R`6`h`hYYM`$bB-LHsmA@dW0#T4gmXe)(>feZp5=&SVd7%w!|XrDn7)Pej(l+#0qdNl7S%v!%Cj
+tBUKV)*HIlDQo88)atalX<7*K<qoV(dQ=hAs0Ql+on_q~ajla8R+X-TGkmkZ;95^Pag(9-1GxXrKEQd
+u@F3*kY~)*|+FV}Y2QDlS;JbED=go;?Fb@0c31`e36B<fDRM*s@ZI;z_IiO8Kwx=?7S9#&orS8?E#61
+@U#&n@0TJA=Ik(q8SRNGI^F^}>it{P^9NVhy+baz)m+3&E9qwLW0rpRtcMViOxlcG=0b=W=LKq3RI;_
+)EMBQ+0?snXa<aBDvfXQSo1AS`(90clTHJ1Zv)&$ZyWx)mVs?<990;ZeG^8x>{vuD}Y+R{QlhNh$LHH
+@qn^uevm?o#Fx;5*T}|0H(m7Vm4*YVPyckb9rceyW0hqH2DY0``zPkPTd>%Ub$X!3Al8nPW96ahXc<%
+oHi+(@I0uyUCu8d>1v3a*CkGcia-3BfI7j5u<P+{?$xCJ=E0E7ut>hE#OJC8?snlz;CB{P@#e`OFopd
+x3QOP;^9;6Xo}sgvXJsSFA4_Lx_+s)#)ulPeA>$@{;N409fjMfY6zr068aH2|a~W>)fFYa4Z7tbDJL|
+yK-Nvh3bgvy0*=wMdvw+KS`AIPPJ13x)!@Ub?U);23SyUM=g?P_!yGmEVChPPM?!)%SEvOtSmtxo3L{
+hOlV##gi9x~@C#U6fuq`9Fu@9eH$6iC;te_(5D`UlUl5EHuOg#^sFf^`#77@6)-{2U83{gnfwrL3KTS
+dXt5rPpBFVaEA75r`$M_{oztJ)uty?$pkRLv>yp<&35&_@2W;7}w3$ekXRAJ&fI-C&6vG1M8B_^{_~P
+rGw(IiY;T#;XZ7Zt0%g+o5O{LDz@$I9tU)<RFs#sSMupTT~_|o%%Pb=_YeZZ(CQq)6CdIMT$!6rA?!v
+keKq?_9$Eo`OZ(Be8+P%v#zTR$KW^bsj&)a?q}t_vbP<)PwDLma@(%&28St3_7da^O3=amw?+k;+eFm
+(*=YzzYDB+3t<b@sZIow*a=G|ZPCCl<}9TM)zq=P%Ka1Zb0vZAYiG>;dzQshQDq!%HlCfB{6ce{y8f)
+sYBeIZeqZb-Gte3;|A8<eE4&J0g$39_p(>L*D29_ifW&k2-&K=;~B&OCC!Q-ZoG!2DL!dBLimEn=KXj
+c`)LQb;tAIipJP>GI)fy`g*U?h&+B3<jC<%Gh*wk2t$1V)qyg1+-8PQ=v*@S{kj%4xfxj@rY=*avV&h
+31}XE;BT5tPij%N>n5!<q5#aq?zxubA8c122M{vTsr|gDc4*fwcqN0Un15OQgV3q&d!-|y29!~vU;)_
+`(x4O)WAAr=2lFOl2{(e+JPHPaB9R$SM2X?ildZl2=*ig%AW1WO4N1y6CG>9BI?nF7d?2vHHXLs6Kz#
+EItf@#9eqCAyE>!(I)KYi~6rOnY;^nBqW`8{+gHSO%4${VlFO2zVKFw`B&Qx**zcZaEVYUZ>dz#RlZ=
+!ph>dZY>#S$wlQ1=J{H%Onx&4a=7gc5uwg}K<>u_o20lX9cv?I+Q_4o^3|ToQrRNDYRjLs_t{^e2MMc
+csdmX*`NrA(I@5>8cM=Z(tmldoy;Aszhm5OrehP<X+S41DjS(E_1GD^)yX`i#38ehZB;S`lfqS)b+YW
+!Shyd64NGSeZ8xX%M1qgIQ<H?RA*(m-SKXBGH+A6M3J1aBzCvuHR}w}dXh2W2X0i9G+`S3)r-kUdt{S
+D%DZ+wzeJyO_{``nJ<OGR!B<bD!m;bw$R%*@Apu6|Z7S*tH9GD)DE;K}yjPgx@Qh}~y|v>J#+2g3R4X
+5zMJ`i$@$_1)i{o&ReQ^`#-7e><LF)y{Z<y_drfZUFSaK}Oh@k`Yx>YHXowXL`50Cg`I^`eGz0R%@K+
+t}<nTcs{x*VUB5j@@O$nv}VVKxSX-S}>C_gs0!SMU58y4NO$QRnPC7$yq!yCt?u+=2$(9a?$fdInM<R
+|sGgwRn%(e@x1fxY|jnZn44WWZX`!v|Ec$i6LZ0^Qad$?U_k3XiIYzMc?g$O9YFOZYHx+rIf{Y&-HbB
+G^{$83fFMiI5Vgh>;7^RHN?`{fCTxSE0q0;ZN$}!$u17XbA9bEv+Dc?&)|CZ+;I;r>i&kEAKc^1K4Ea
+zgt?eyp@2H;EpWHt>EWK8zVzmrbMOovSknA5KEwD{c?+R9{n=t@fe8fmc}PbPFVrpo!RPLw9E}Gz`q+
+kgaLR|5)2LzBdLx45I)*re+Y3}aL1}&hRvVllN32o>U>q(pBcBULQw?R46jV7|gnzYam*2-B+vWskR2
+}dNZXC_<7?uZQu3-_vGc-NX#beXavW!{hgPWHwzH;}MeZpt?81lWt!(=_E6S#yMB99CYt?YHBFE-A!g
+g?kLc@Ml(`~w#Hu!Z^=gFWG<)!7vOq?<p1L|2<9%NPRfr3ZrhfiHs>ApN|@0bGJ?YwLwK>@@JFd5k^&
+?DB>bCsd+&^0xSe8R1O7s6hk2wBo`2WzL^`__xQWAK!lS;?-ZbFCJgM{(&Fsea)}SZZBTFeSCU+^VXl
+a{p#`U<Cp%#=y%^gefRp!V`i%A;4Bk_{r2ZnoD_fkh}(5N00T;XO67a*5=A)6I@X(|exDdd&-jHxcwp
+-XgAweEho__g?{Hw@QI&woU{hqUDJG!NgTtwU-K)X2pJ>I*2Z~u<c!$deeH&akxDwQfkSwS>1&n`yS%
+L~?u-77x>j!G$fTxI7J5>Y4EHC*EZmT(o2Umh<AfSQ@S5U=@<~SVO>*NYTaXkZAyJ;;|`3MxVyzmZJY
+LYlXeKW2v@DD*y4v5i!ybpB$fp{;Fy#!!9sGvrx6ZZqfEHAvnmBE!RIeZ7j_dr7&h=7AUGANNn4LmEA
+5F1UKVwJbrAs;AadEuSB5+zo^tp%E)KwT5iH~~rz#313a!kmK$8ic~2S{szDqF1B|6tleW4p(AR$N_?
+ieNb`_%HV<T3W}{kbu}mu23cEB-3V#~0bv%sPVNa5v%KUx*a{p`z_|ouNWcjMkSjn%K`aSkN#OVc4m;
+p~gA^}7>CtQFMxdDGg?G3TTY>)!n9YF247kgHZ4B7MfbR?Fw17zqc(H(#3MixKRRIbVv%K&QS7IyV|3
+R`IWa2?49;DYn+8iW1L9!KOFhSlBqyPaTk6xKnpqS-_ceoN;5srdH12`BU!4NKicLMYhpqBtx1ehW~4
+FLd%e%<qSF>lz4{s#qLpsx!gQh`b%C@Tjg%K#t;ST_Kx0l5%R3PD;Mq*yU3LkkqMVXB2!54=rq0$1?6
+H^IrsL8&>|bQ*>mgaJWOAQ0vT(!M~G5XceY-U%EiW_jTquEbW@hZi{4fQ$$Vu)%R_!N#6IWF4G56%<E
+;gKmNo5Q5FRF>cR|VwM-);Yw@;kwsA54eGK%!8oWT2IPIf=7yOOu_0h&f(;j8c6I+kR-l;Wg?G3TTcH
+jY)Tsg$Z6J9K08XF+4Ag7^ITz@d0=ZJaQU@in7^R2{6tleW4p(9;RF;COMZnAlWN^Us1|(s??FFP;z_
+JB|QozFm>`RQe=j~$Nuod$9Fi$5t4^v*U(jaRKQzsIeAR`D-5kWkRn*_>0h#9k#Yk^{x7v5RC1TS2Pj
+gSon>f>PhL_p66XUqi0zyu1N;5?8(FBcql5wjFffnt^y-r-7Y#e@RkXppuC;)0+!9aIg1a$Hc+2>@Zh
+p#&*buq`|8ljZ}(EHAvnmDmcqeuMqD!T#G|A8WATEl|k?J2ZnmhrzDGKuZ$Ti~`j~sFkE$m=;p9puii
+bhXR#NP|pc!XaRv4WM}~u6!JT87-0rXJPuUHfxI<lDUt)lEHC*Ewt|i_pcDc*W1u|@6m)_7Cy?ZXX$(
+c@0AU9u;h@eM)EwhJnK@9*^1?e@iLIC(kl2Kz3=k%O0wK9U-W#MWVe$ab3xH4{LW|=@j3{P#;hn+D<m
+KSy<Yn;^yl^Ep0>cnOY5}jH4qy}Fm!lZ;Krzb;?{FoyLPa|$ItNwbfM^VgtwD`6sD}mx!k`2g6#N3RK
+Pa=sapMvcv%K&QS7IwL{sCDTu;l@n7jR_(5f)@z0q_q<;DG21xX6HpjN`^7C}w%dcd!)*qX0gKTn=na
+z^VkSO2BXgY(~HsgcJc;e2}pR*>fB>E<rKN3-53xwnC~Gq<TR%7Gz^#)<p6Wq%1)y5+)v`8$r4e<OXr
+vxCF&)h$6v+fmSCF-2|eAppX`Dq9LU~u{Ws92Gyyco)naZf^tSsD2U_6B`9Wj;hnq^);{2&gXAorUIQ
+jHAov2BETqIBc>-!DNEm~pFHADd8<(J%<%M^+5?hfPgw_jo9tC>OpfnzwauOU^66}!-G^>F)GsxJ3^e
+^sTObZmVyyQFB3I&5e!yDAZg35D1Py|3aP?H5Ri=h4%@Q6X`AMAGuWc_j8xCF&4FTBH**b1eYV9#Gru
+@4gvPF)DjQwVk_1&1F5l8S%~5B3@dJ7wd%aS4i9UU-Kqu@!_2fi@zj0SEP@pvDuVZb5PuBvC<?FxaFT
+h^hkGAkG_?pqS-_ceoN;K~o-R7(&_!ekIty6YQ=D_S6KMX99*dq)pi|5o~3Mdj~+EnB|3cxDs2T0uxY
+vflMb*WCRL>pd=lXF@qXcz}5#8ULX+*O180XT!LbjmwX3XK|2u01_GuzARK}kbWp7f3Y9^9F{s=HmAj
+C+fL{-&^;kD9K{3k<?{Foy0worZSOEbPq?JLc7iLQ+27(%RK-mPfwx9+R@ay@~erCIgOCEl*ef{$Jzd
+gn;Guiw1=MVn}P)h>@6aWAK2mtey7Dp|jI8+$}004{z001-q003}la4%nWWo~3|axZdaadl;LbaO9oV
+Pk7yXJvCQV`yP=WMy<OWp!h8cW`oVVr6nJaCwzfU2ob-5Pj!YjLHj<h#Oj6scqB;<3NfoU&5d$PiV2n
+u<Cl(+FcU4zrJU|khURG>nD45=Il9V#?x+JeyBgVA5CdIN(R$(K>hJ7`Jdj%ZMWLA+%e6p+)6DJB_?N
+*j!N@GDkUOgx0E3kwN9xcExknM9Y3)$6<aa|`3o4xpR#wEs2HKL+-sQ$FO7Eiw3V@k4*=8L*ke4iEIj
+A5sSk*+uM#$3RW+{KdFmur)cgGT6Cf_8M-(eXi;(MR!H(?{=SL-p(ca3<-W%I-S6>hznh5)y=2C5~Ea
+{rS(33Ybul>&GMn9}ka$|+9gG+^FCT9x&3}Uxbn>}ShljU5xBRq`oBy|4XSk#v$mqi^yd!0j0VC~tK?
+&RV9bVXx!U`qF_*+R{Hzfm$n@mOZ89g}cWg_zsDI5iAJ(uN2P4d4&%-V#fkhr6HHIz;G0)H~hQAa==E
+>IfhBqnlMIyaMT(6uMzgm7!A?Jb00slhg;?9n8lI5TW!Lb3_}az2jo9x<EiKJ&cy$W~(K|(?@!U7mIk
+he7r?2ti~xCctnzAr6hnM%L?slFbBY7ut>h4BJPjIqvd0uKO8NmgEXb#Y(X*2<Hd56tj6(z=BvegmJT
+AKl=<XwaPyMOXO9a&mIhYl?1faW(f)`AJ1|r^?ZgwK(HTq36bbw!RsBz0R7P(Pf>H9~=q<^DwDDbfwi
+45W`Fqu0puBXri?Pq5F8$X-lF;83dXS=gC<{Ojl`*zUedBycoy4fU`Oxd#T=zcS^oUj|B5yr3nBM*68
+fmv$g*7Eb<tEz+>)2P;Y@i<v48PH2ue|Kno)es=?B|EmrUv^(>aFt_wo+s}srgiv98S`3GC7`{N&s^n
+yfD>G!C;jvAMSoRh_R4rhh=o8=Un+)b~>40U#V6rQ^L7p>`1S3nKiZrmIWrGp!Hf}=d9Nq$Es-J;kjH
+G{hof#4qwr_Y{X9J>jmANU%9?2eWmyZtpED98=XH8EOw(u@GNZ0e%&;3twqUy3bgF^R?|D&aJ_tZ*ZK
+)iO9KQH000080P~d=M>&o34eLSx0IQ4u05bpp0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXk
+l_>WppoRVlp!^GG=mRaV~Iqm7QIW9mkQT@6WF&Kv=*A+Hz%PL{`-pvw$Sa9%5ukpkxfZHwHzvtj17mO
+_Q>r`Slymlhk^F@c_HJpvXSw)X9(dc;k&Xvp@LYAOE*~{ORwXeZGD6?2}JF|LW82<IleM<X^Y{=Di=>
+KG?qg?tc5?>ESOAZ(h9KK6!j~zdgL&{_yyp53gUpsDFR|{f}=RUcPwu__V!z{c!*0-S)EH^Wp91k3M_
+z@a~~L`P=)y-oJkQ;r?lR{C0cy51v`?|NQ>l+m|nXxc|Q%`|$YYZT<7<hsP)XQ4ij}e0*9zfBAI(;@$
+nL?VF!`;(z_B7f(Fvho9=JzI(U*;*(!)?Yho-#6NudhucT5UvJNO@7wM9{oDJezuv!EGx_B4&AX?EZ+
+@(4{^IQ~|EYemZGU+2^zYl353m37^zi-mtF1=(ZhQB*efiV7?;hX0pMNdY>xXZiUOfH8s(t%(f4{wb{
+Px}7YGA+FetP_Id->wc_H_U1;q7{SEyTO+#hX{ZetfFWfB*RE;oG11(T{Il)pQv3yZfi_-~POYzyJJ?
++h_N0Yoy!n@88@%)oi}}@tfBVFYCjfJ-obs^Y(sw@pk)xpS}I=?<@5?2J+SWfo#8ftjD{$zuE2|>UF;
+RulG-H>nB_Pu>JgP@1J<L)n5PN#XH7dyZHkj{ACULr|os^#?#NA{O}(x!QW^4YI}I&W527lufNm-Yod
+QEv)aD7-+p|1|Lu>j@9GKl-tFI>ef=+A{PFAUqtE}e{o6;+pMUiE*MIs=y|-4nUakFGPkQ+NhcdxeTg
+~d}#hZ6O)xhfkfB5wIC;w8P_~_$jpFR8fPmKO|&%XZr)33hTe)q-m?W66>kDh=1?2|u!_R;h0%RfH<@
+{6xN{czj9y1)N<iCN5lw8;NwiNCE!e*ai2`Re}Ni-*^5*VO-1+xxZ#`1;lM-HX58*EYYre<+LEUX+Xc
+@YDa-9=?8j^B3n^ANl)&{$_jlc6;;q?r!_r(?i)#S>J!z-FnL3_xG-h{^f^vTef=Bi#PxNy0+k}`uOi
+2zO6_9?)Bs2)7|#*<J)(<^$#D_XZPOP{;M|jwrzj>s($j{{kMJk`EUOpgUScDdhqW*?xXcuwzu!_i~s
+nsLdWZ$*!Nf4%kN%1y?BW&y?gP^>-*(_^)n_|llzY^o?iX@+ICl;e*Av>?my=-2M^AA>h`N&{m*Ux@V
+NM!f2kMR-sAlXSAYLP8}&lp<NW!B^Zg6^tQYE$>xK5=zW@9}cR0^p-4i-~wq`u#IZM3#vtQRn{`wc~S
+MUG&%lhe9gLq!fUBmsaAAD2qVd%f!eld>B3MlojU;4O~$MU70eyEW655M)rH#kDi|AAGlu|2*iPks9i
+fBOE%m)~vQRa`TMl%qZW?ep7h?ZBVE{?nJwKKbagKmYdGS6@GW_VFLTe)h%Ze}4W^{m(DI{{F^~KK|^
+}KY#V?e|^eyj=BH*3S0B_<KH*tpSNG!+C19bcEokpBF0@GGw#OK>yL9k>YrohpY5(4d;KhCy=7kY&us
+pYWur&G8?AoU`<{2VdO|(0*Asf?dAG$;k3Q<%+f^SPZT9+E?02!x{jOd0k$%ltf5dS&^4#nB{Ozizj5
+B&Yu(thfJLh#5y@s@12k$w~yPVz6)i+%^Yy9<1Q6_@h)L8RkaOZi~YS?%E>@}0vue;mUgE#WZQLnV~s
+7dYhrR_L^kJQZ0Htx0>&Rwhb@T?l^UQcaV<BF5->`~93HJDn%F>7Y6W_F%6+kVa(X4Vt3#??o!`8eBN
+j~Oj$Ehf)zXV#F8`r<KbVrSN?4D(j&Rm&3nWHEX@VXqM$qt<z^5BHilFPt^5TDfsCiBWUfM$KvLSAA?
+}k4AlG&7n4BYuWGGJnO4+)EkdcGs~>MZOMS^(f#W6owcy_IhKY+s=3!=X02=O(Ov^iHod;{R+G(oV?8
+xG286{#y@@d-yFBkAc>ZY2yOydmy=|-Y8uizjPQPk!gT*~Gn;Q1${1Nrx#`o1|YKeQR<-;b*1_Fzz(d
+=h^UF@|MM{Qp13{Nc!N#<D#Iqq&_ufdklH@=?1G4>Oiz{FczHN7l5EXSxPv@*Og(-pvgu+p^&&CP4Ih
+FOo<23uX>rao66X$LE?*92<6YkD2~t*^R^+DJB-eXC_C6X<+)ut?=rwf30J$zwF#dUV@sXM!Qs_Dl?~
+zL{<9?;W$=+%&w7VY4)~O7+?LrZVi>l-dP$Z`9W})@xoY#(@Xmt+SS8T=lmxds)M%7s{h*5+}=W)$)~
+X*4MR;Q?z<uXMbvyFyf1;Ob1sJh^y9O!-#`#a)S^4R{PKUlE>7u5`WmsfVSxfG06%EHHL%5J}>^Mf8)
+24O*zUs$|5l(JiW7nd#|D6tQ7%j1$G>tpOx8QtGI0CzxtN5oUq|x0}rV2Pofkr?0gQNWT{%Ray#FCoc
+Mfwd(@)UrkwS18394XU8n`xMr}aUWAAdu#96*UaHzOh<C>EpVCMv++zB*UepD8R9kz@8C|fF{V&zV}s
+Rp-g#K@hcnq?DhFYhd0!Y6Aq6;rS_0_=uGUii#j)>giZgV%=O^amrXKLW2#OluHK+u_dFG7Ljz!-{-9
+f$SvOu);kJU(r}ycJ!*rmxY%HkJFf6e_Ul>We&`d0psb7NMA9GC!B6d#gCnB)R!y24K{$O3@oi4SRRH
+$FwvtHvJA$@G-71%b$CC|KQM$NeKf-?w<<d%EF}|7{4vY4+Eu$ymexp8^|NS34dWHdTuW7pR8MVe`eY
+$5a~5`3?!g!?ERDf2?@kgNeDlza8+q(xtr>9BjU%y0+!2p0Cmd{JJ#Y~7%WMn|7k<lBb|Tk-C-Tj)*9
+zp&oa?D<PhfcEiG(?3axwXH5FBbuWi}cC6VONJX#gzy+S!yCxY<qsXK5zcYp^sY`*G9`#*UZG6JKuZ&
+)jQ8Mr~$AsX^YvCa~Ye3&$WPmkSMo`q1Wj<(>*fja4t(2|l}L7`|#Mt}YR&Y28<S{YH+j_w0B8(T22h
+)kn65?VO1*vJ6b8Qd=hu1Oc|bFU~TpK``p1{C?=S1WMny*Orwz)ml%&+~Aw)huFy^@AEz~W&?_hgAh8
+C4Pej6F6G7KeIgPoTz<ur*-YHOLK=~Tb=`qF7vP0(RZ#<926k-3$L%Q3Y3@G&s*_Ge$01Jlo3PsW68x
+_-r?|>W8`-6q0;W^$<`0}C7#Cr+0yw$*a=q$}&a4x$NrIInl9*3~BXeA3*iFy_jHwYO=&O-xE88^oyL
+N#mFeJ9HCQ9nS7`JW5^@J?s*G2%Z&{Rf5kSdeAl{K0T43Z=VJa`a2C);zKqBeGcNfE`%^G;%ZeG@xh{
+z1qgu91vymHG?`<Y2t@BCk!o8WTSaiYMrZg~TCh<p_9$%raAa1_Uq(#P#Z}A~9J5L+9mAoDN`9!8FYB
+BI^+$%4uq%WerWssvN_r)X&Hb6()?!-K<CIAR2&WJ0@Qy0aOS#6T@En*f2|s@f6z-bnA=Tg=KY8QN<>
+FnP5kxtoLAA-FQ_~xlF$`kVXtErxV&@UPg%KmI3?9<jTa@w;KO#W3~+stC%8EF_96-7)`reL+r%3W<1
+=*Z-6mB7DkOpVyp+28STU-b{SkY2r37ecapF>k!2hBWXHN$m8k>Z4*<u}fupC!h+!8hWaBEn2PR%A%~
+-LM#mS5Ws9w*ifJ%f&-`sh^AhqJQjkP|(JN0VWA-0rd3AG&u1qTygqNKi|(-ZspFgjzURexaTe5`&}3
+(4PVPPL1e4O_UeRFx4Jp&7!e;}Ehk;0vHVnS7aBeU^z%Z46jR`p`u|owuQv6FWBM-I{2X%>>#u7No{c
+2o*))veypm!#F5`S`b+3J!Oa1DM?^U;&dktt?j`bcf3giFl(1L9@xziQ#1^u2-meXJ-TqA7W~%uv+_Q
+6f11)jFIG*Abz)x?1PBCV4b0{+K^TxQUt)I+i>Kntoe<h72sYkWSc$aVbWcL9lVTUN?n$CIot%@<L<+
+m8)OOMbW>R?>!vn&UBOMraZSXlTXHvP5{t}xV7LSaTB$dg$LAvJe-C}4r+EASi7FM*~*nq3TSLHtv<x
+YgCtthZZdDy8a(oz7gt4~y322rM=gNSXooMDYfJ_js|+AcO~cLVqXbFQ7C0<KTQWR4ARf3l>c{6UPY@
+YT$9)SE<+296n)eiwxazD5C4PFVjmsOS~Kj2$~X4j=8;H3}b=op9f&0y-br4x#RjIRi&mI>AbJYjjq)
+5&`uSCc!YbDVVGg21u@=MhC}GpB)5n61|wK{t9N<K^zQjcdG~K5YQklo?<`payRQ;l&um28u3C2xEV3
+IpdvViluTu-om=yCY7kP`ARiTCW#<7)gGDP3#smpfF-@>ygT}gwObkO*tsN6dcV0BX`jh%D)&g`_@sN
+spkh#VRdr4t}{hpYXU<21a@ikC-2QZhJLtU^c*iKcS9pm7E)Rx;taG15XWf#;%R9CPAHBl^?5PguLfk
+Tb;=~R%7gqnPDX9sJFjZLYw8OG6JDGNgDIPV7R02fy_#He__IP$=VPu@HZB0B-^<jn!ivUQyvJ|;y+i
+xmMWo+caC5UU+7^CdDF&_u*|92$sJ^Q4vxYCSe7Nbe&g9zr@p4u$AU<f;v4Tlr!NzU?yQJ24g)%c7VI
+vUXwY8q!7$TE(?^lmcHYXpuuo+d-HI$Oq6<#)v0&TtRk&dVEseQ14f8fI%Sk)rRsOmL)Bk>vID%*)Z`
+<>gBA#1-YmZL7UJsuow!@azFC&WPh@|kkoL77xGgB;sg1{ZaN{|1|@vWhtg>WtxW21+%d53lkhbg*_f
+0-vTm$E**!x7EXC;t!$S~!v_2Ac=VMJ=)IgmFG3>zgDm_wm15)5)DiU|<BBo5XKSfoIFrmc{t$~z0S=
+`G)gz@7AfU4VenbNvzNH%W5X3P{+rIqex1Smjwz~M4epG6}lLUV1Wl_suN!AORXVe%2?T@RJtBajX2a
+Y{?9VTd}3Ye~MiE-PoMVrtyuB#nYr2Q1Pi4(o)M!^q{zQR@Lu<0O<{12=}2V$mvJ5Pb9u;e&}DL@;pU
+(M9gN?hm<1I38Lg`6Fd;h*xd5nkGXBOrl|E!Hn{hxFpWBKJZ~M+et1G$X}3MJ2rg~r)S0KGN1|2hF6(
+#Odu0NS|lmpYPzQ&2oI3hBHDDTnpGq4PoKcj;N+VYa}H5Cae)v3fd}B+De16wt=2_b`%OHA6t(2VUAm
+O-3OOwO!MgUv@nS&alzs@en92Y{9EL)exIrEOP1ep!Vi0ZkrZYvSwXPTLE+oR=HY$%YY|6Yrd5CLl*n
+MBb9PHnIN=CT?2SJNufLG+ex0t-f*aY7MhYm@-kw3;EhLoxTVh}5;^bTO48HWWK764WrqrMJcH?7nlY
+>$8yIdFA|rwv}BK1a=d34$nr!}1(BCkqx#ba89edILkiUPITTqQ{nk61!7@ZLskTsM{&Pb94)9m|gw$
+5)hokL%s>x12jM6yD34-mkHKQD1zPZ1nbiQQ_Us0=zv%zNd=sB13y=IKZ!dsS7vDQ0KtK0bzKqayMf=
+@V$VdwP^R!)1uiU8`R7Uo;1N&`WJEbW-rqzHau8FTZ>3I|gs(}pQchL1D4r@q0J5~cz=gnz{0&9A%G(
+3pMy9Mm-WWUNtl?YB<_=tnP$g+hc)X<GQ=*I{i*HfK)k2D*re;ItCH|&$3Z~2RTNhn)sW+ZVfMrt#P_
+4EaOE!ivgri<f0cVGU-v}ig6xt<I7tI1xpgmEE(rS^0!{xVQMT7v-VQmUuLfoNvxYb^}ALX*}`5jBHU
+0|uG6f7Bm_ycIwL_VaZ5k?I%8UcJGkpzZ(t6(J9$H<7-bJ0d1W+RK80``$zZe+q3?l(jLacyIz+E|=M
+MeI(*8}QooEkH1WofPn83b@$sL%xTls--E5AW#z7FGzM^emw=>4XFE#bq36KIKa+|S9ULNT_(5#rGZ=
+fu-er?fm^cBw5?9LE>m4#*tU3Nk%&7rdDAqJp5!xmBc|G6KnkGQV#Dyv(5H+Ce?Xi}>lARzfX`1nC4h
+zlj;2isD&lA}QL^8ignE$tj|)GN#IWwQ_f~@eaLRL0Osp7#e~_f0dip6#4Duba7M&uHCEY|J01o)1W<
+P7Ob23fTVl_Qn56&GXal>r3X^5sgNRrKA8N3{hn9{7hi8c~!P*ofJVdVi_${<X!GPE=z3mJ54vN}_On
+17NWE|@Wh*bOYZ*@@t1Ro)WwFHtUOlvYgzJ2Q|Z0O66K`#$arN*OsIa?fr-2ByQIX(*O2+Z&~zF`J!+
+90C*@)u)DGtdO1|UtBsg*yaMu(zGeq1fUrrSh-b3h?%z2^vhT;{QD$M2VjguIly!QU>@)sL-5)$>`|l
+6W^kYy?PQ29DKok_oWP0aCl5RzPAKNbu20H98Rl%&&`!evIuYX5F`f=8X%t_T*JzeZNor<`DJ9%F)pb
+Qn>Np@IwQ9vSNfH3<uq35HL#}8vAS!i0y|Mqt)JGwL{2A_I?}tp%+G=2uZo9OEjgVS6(n8hC65|IB2R
+vo5TbG}l*0T_?i|qxx;t-TAsVR+B;1d@x`;Zk$$iYq*O`?WQa3?vJF-p@jd4dnIXy_zFn@-PQ+X#w);
+|j!tOrlNOEhccYX=bF2#VZY7vmBaKj#n73%s7zv5n3rk@?pc05VO{>+395KgJs#Mv(sL$bYI8m?OLzy
++CluViDp`mKLlennmcVx*%3<R8=YZ)0cTUTcg1P^gVZ-c98Hw4Taek0mCu98Cod22;VM!2h%_DqYsER
+3@Oo<C3svw@L0)z~3<UVHq;!{YwD+Sg!+?;;dKY2JWOUo$*fi6lJ}a9BqXK_%(aH+Q7vLgSBjmKx!2@
+>;Rt$VCN(h`U4`s2^HD_|9_*04>q-#$Yl4syn116?ye^mj=KuFG)IbJih6t_o6CM03gtq2hZJj$?%-A
+y($4$=nGpiW`sDlyhq;R|Kv1aL(d^s?w4HKO2PH7hg$E+-8W_9I9TfSv~LJHVf}E*RLIPZQ<RhaqUuJ
+nr2PDt%ne8+i4=XdAB4D4hlj^M;P19FsK!nshrKyFv|KCI-~*2{VyB;3PoLfWrz<!XN;40Br$GaswlB
+$^jizX@Z%tRKa3U$9LP?W5`$0BtJ|sN&@hLgZM#EJSnz_E}KM9^JRJ<O#nh|S)rW>1UsOPLZlUB9H_}
+2ixao}wD`3~ATI06W4HZbi9!fH7@@=;3D3@ESpSn9p`Bx~u`p2RI~qWuM!?_pl>#vAL-41x0Ixgc81Y
++`F}(Y*ldVQ3><8fmrar1&*Nn;><Ur#P1iKpP(~?LqLo#q5300)l4d~X-Q*#!`vwYGlv|%WQ3M|Rc@A
+jG?>{l$uZ`pZDtTt>1Gc!_0$ezG!hU{g)6|ElaOnGe84Wvv1r%p+wLtT{gK`ZzGMJVQX30zs|scYjiJ
+kZvAC0I7aHnLR!h~HhN?i%c9`L{f9E$$(MBb!OL5VC7vhYbQU3^BBNsZ(~^0MJIRoa!975ZOlS*$FaP
+=FIjJh?^}vaYbJ+A0C3vV`H$bDL#TZ30-#eQ|!Xy&?<rsxhb5mTP<KVR>|1iu}k)MWeFr7ko!``FthW
+Ns-kwK=D`!2gayo4l{=<GqCYLLUE-29$-bgZC}%)BpngH-N|AwWcZgUjsSUQYLtJ;T(`G?Q`?2ac3SF
+CYC-Lxry0UfJ2#VXO70HU=DP1uItR9#Y?Z5_Q(=Qnu5T(PUd<xRjErt!*jeKqh1*0})BG42;^^Vy$yJ
+iqgU64z46~KoA6FWte8K=u10`#bHr<6P!N46}8r{V!t2V=K+f&F$}50Y=lZQGDR5z?mkFx4vH(A3N2n
+S4$<;}i_AM|P~5#;9V0x|Ay8;Ab$e6;RbafWD9$G?qAc%pe#Ic%hn}$QS(9t&ObcNijo}n`%325R(#x
+x-u<PR*f2W91F<<5_hRFqXrZ?yc0rzhWaiMRCT~{-|c6alrDv1Y4}ntg>P3bO}neKNH*G=quDA2wWWm
+#{D^^)O0eLGvn#WK-Wz25!iSp3vRXFNBt?aXb$5V#qZQk1_z@gBE6`}2b?C%I?-us2>0nfny2Ku+HNY
+b>?dLAXPjHZ8wh4@3sPCX27~PJH#hk&KoC4<uOU&n>_D<@jw3&oZZTcm6-D#*DmU(u0ZS}Kn5^)3r5P
+mzC7|)8>N(#z9rCQY{h{L;-hk>8HCFgdfNQQ08yu-@Iv_l&%q*XCp6%Ukjeah#m-vf*-rjHE>(Dq5dP
+!vJ8Vq=%v<#N$n=qewFg#&Y>>hE-4QcqND)a+VA*#NQ{u{DN4s@r+Qk9HeaT@e&XOGo7+#uGA0KbHtm
+;6iMI3bU_g&nRS$i8d4~8#GQwq<IUQi6r8BKCGB}5=aw}Z`c9_fNT;-P?H;+U&f-x<TcfYS(mP|f`Pa
+T7M*quEYi_LDULjC&0W^nm?eNvcTuW$*#kKQJ+wb)F*GXH07H(@9L!XgA|I8s_P6Xw5ne7;jan8Fd+i
+$`nbK_7>K8V{Mr>1ck)jwZK|)WgJe7ulJrMphyLUD;<ee@xQ75i<+WWJkf2b1>aZnV{Z40Y+n1I0WQW
+=WvQ&Ay|nq(kawq_b_fY&l~+qIHPuqh<sVX1lA<yWtXE~MTZ_9fN-8lR9jG*^Wjf;<NFqJYj^YKl>lG
+c`ss6`@=WoTTvvZ%PxQ9joKRTI+xEHW*JJ{)#T`rKow(&!#Oyn&^m!JJr=jk79#VXl}|by~HFGab0$A
+C2DkoB_>_6ARq=PM<EEQH4Zg!DkL{WP?R#eEVpc&><Naw5T+dsvdwCXE;h*2!3z|f5CfalJH3;xi313
+mK*FX?!ohY+8+>Sxxx!{8e;e>GoU;K`Lq<@K51DXD7|3$T4oIYNNjqO^MQCVUHoAA|4be8)n0cRS5<|
+82g~NgUsEFdSw|msjRM2!QbDRF6Ld!RBS*qnASEtcHPhqR=Eg5Zu88&DD2_5pR4Ga7!wwX57M4<pGN^
+?q$Dlja$%y4W9xH6<eU15YQ7ER`1YTAPDq|r;Ok{u@w<w9*@H5xC6Eh;7MK<OptGI`pTX5~WJt*P6lz
+Zz%OaA=X}PRJjfc)LmUgvBq-1?usR{$)&CnXF3MgS1<3gXUTxlvbl2M!#V5>oHgo_=e7CSlg$4Ljqn0
+Yp{c3DCWgh;2Ce53MvZ+d*R5&8_k%$+iURAvWCoL$qOg~sbV&W(oJ8^X)_wo9!si%nF`aIG8Fp}qzR0
+;918;_AyMUrMGZ-Cld&@@0W0yDs#COy?i&F!t~~)m(6bI70FkKrHrZtQy<1mi#-=w(B}g9eVUtlEih(
+w|)-r@$*#mh;sH0lnUIzX_7OG`HG}NUf2qM$8IaKO8N1zGjyzf$6^5i&#+920L(}n8pHXR{TY}Dh4od
+(SY{=9X|D+LStDgX?ySnVmdgUdEsb8?nA01Iw)c+}6biSXG~ig(ThDBzOq@c4o)b){&^Kj>mQ{#{zaM
+uLcpvO-;1w~NK*p{nX2z<znK0k1Y~V>=y!b-NVyN%f@4R(c4AFV#g$LCRaG7)h;_q)Z)3Fv-}L@=z%u
+TXih9G7GAVjXq3g%i5`;kcleFwb<H~x>>OTr8n?Zr`sK{PhFMUYF36!2|6Rgi#6e4Wlq<6w+6AdO98H
+lsF{#ZfP#ucvFGZ*k<t#i7CWvM<%TBF9r+3Ghf&!;&A09b+s(V|VnhGnvYjb+xXu7vY}cw?h--Z3#W3
+lv#nJ>fRsBsRhKC&p_r%t2PFb2)(;N|7o}5ew)fOn(n_>;hwE-$@rzsNdt5GVa#4IswgQ;#dZ4Nuz*r
+AX@K357bkW(_-k%}*k&}@Q7avJJ@0&G+Ty9^uVfQcUp$WO^~Mx!($ZS|{%(a>0Hi3b_S6SE1KjkLE2d
+D<-;RP(AAQ}!nr2n~YXW;!H>j)ZVq?G<Ln*`|7+9qRP_g=7I}d-dL_h!2+CmIZkLey5*$0U=?7Hs@GN
+s70apy$$90D7A;G3!-i#vo})HMmx3Hv4k9oX}eCH9I;jEmh>^~tFBEkqC&+COnyTHurb0xvvRESJP^c
+|VQQT))pm%xNf0EnZi{MR>@XCr(=u47T9L!HD}i%2Is=hBWTVCKoqg%ItP{zO;M6AiLZq~*O^F2`SVN
+Dr5aKYAcCLmQHb=fN|0$Lee0$zv*?{;^h3}<nplZeB+t4d$?BG2^Wwta3AMT*S*p<tB=t4aK#WgNe<?
+T`$cs>b4V~djDV8KLVs!~I6yvGzqa#kd!64>)XA>&W$@(?VMUI5h_?T?<T{`csGaww9U;P239bO_auw
+j>Gs{E;5<Y7~#LWFs;<k^p?^Iw2wek@%C&2b5SS(542Xbh-|OM{o*!SA00cNkHNa7`0vU%)`TRl0J$~
+J<Y<G91_U{5tSZ8eU-$Iu$6e&7rwIc#FAI@m?5!-YTyvcY;e}ygM(^4Q}D8y9so>5%u9bLFoGDeDUX>
+pM7o8G3GyDOQWk5iM0bZwJCx)s?m!4k`-7q!QWqQmZyF|8LWu_%bjL0aJR}sbG!hpC)rRhJ<1pmd)sV
+^6su-tb)h<<<@=!!d(nMG3<Ka6#*Oj0%ovllI$YU=lE2<Pw6kIbpSPV)*-6|neyIbUtQ-~Ex{OHXLsP
+3+RyM`78=$KMmOqEe)vzoMUu!ITvxPu(oy7B7N>;-?MQN>J3{76j@Y|R1&oo%P9acfQcHM3KWY>HLEJ
+7DFGP`fML^VCaUQUcb{0_n1mN}FL9R-#!Ni>rVx>u!oIT#Ga)a=%10>E?NAU|Q<|>Fr`sa1)g_Xvdne
+A|fKT6dHE8Vb0Cg+>*FsMG9pcA!xXrN=A3j*jb2rzy(<X*4LqkHi+3xT?=ro?+R<Roy&=O0!%x>ikj*
+!V9as_(TXRz>`>#Q((O|wpG~n}jw&hAV-FI`a*CeZ+dYT~XQ`T6l^z|RMhmIQE?W#wQKt=u6|BuEGEy
+pzKxBt{Y4~(NG=x+?JVuCit}R8W0-M#3XE88-NdTt|t{+Isf}(trUTO<T8^Dc!a4V8&v3o?36NSt|MU
+cqX_NHazx!PUUdf3y_2`d95oDd97&lZNN`=zQ<#2|ZZqIXgfDazVNzjCm4#qC6~uAV}|d<U#Z1Bcx@w
+fLqM(9<86ygYa}JqZP%^%ykSv4ecJTuOG26_WSWa8hBj!ZkueoV2r=K5K^6?!ppe+C8|kl$*eeLwU9y
+MO?FC388cByGaUoda9P9j_}Z_C0Cb}u?GqGv-O_R#{^INItpq!n%eFpE{{TZ97A=0Mga9j1cId+EKtw
+YLkD=-lJszktv|SrZdcLsd8a}$k4YZNQiu?OGa)NPN{Tavex%G=yaPRo<DUjHLq1RtoLA^;fe1w_yfR
+d70$I%@(?eXgWsIZbYS<GQE^569+c%e)5H$-Q-lPuMq)vBu44-8m*JE<Zl&M5uj)z4KEy?ztgkiw;Q>
+5+>S?TVXc^PiPws=9U{UP??l<6U4k%0C%nq)cXi-z2}kd>Mxg&>DgV9H_)_EL}5tWXKecFrl9ksHUZQ
+q0p=stvGQ1tVxxIkL@)9E!O;<0WrHK8Ls2*O5C5UD6@@Ha+m7E;2kX0t>d<X-z01$4Ce@v3T-(wZTnI
+5(wBihi7(`By0dgP(MAiw?Mlm1V~bxmfDC5y{WFXbuF7>$*7nHP{LCOCee>|bXmhDG<o(<g#guPv(XL
+4>umJ~li!;vg_IffWDMV_!!;Q-g^7&N<Hn$nwE}vIR}P$>uwK_3kcRA>Pl+0!Z7L_C9F!W73BNoQ<Pj
+6Huif)`D4b{znQ#yUC_4ba<lvtjBC8p9=-h3m$Lb~60At(q#EC2dR;pqg(MsdM`lhszX6Ue%^msO=yL
+l?}5<qQu0SV8G7}OouE<Y6e$$bHOhh*wh!7<hS8GmWPpk#m>ZgZXgWxqXYzZ+$t=oYW#SqH+G!Xi#ZG
+>D+Knyub&u=pM_p=WPx?)jJ|Vc}f?lFKNi(^E&DLgGt77kzQVfUyNY4z;UHuX&J+P<R>uk#-F0AbKF+
+NXae4=jF?W=*!byw&JA`)b!D#w=z6DTqf_RP*fp{%b-piR4zp>!+BZDuwEefO|<RlW80RV4n7^+^Z<v
+$gwwZHm0KeL;eQ>OPNe{wL_(9LG!UwL%?0_-_8m<$JEF0~c-Bv<+%Q!-Y3k|Vl**UISDSS_-*VaN0P(
+MW2RP#p(3-GS?ty4n^;0@@w-;*rhT?Io+SvH+=_T5{9#=84tm`V|ra)01jIqev&0{JO=<v{LnjQ^H&(
+u<UwbhhRrD;1y6^vb22!uoFuP47jwYwe*>Qu6=rgAEeQO=3f7<zxBG1+WM+kFYodJdtKfEEm7jP!)~I
+_zb`0myAHAQa65Qq)CyOxZ#Xsm<6Gl_(|XMj)zW!YZXxYo0+h9}y^FfH~Ggyds{&%3<nTJ<4OV1T<{%
+XaHYS8(i)5M2Ktcbr>FB@Yaq?=ORBHDoU+qacFBj%R>*UVUZM%gT%F&eJMO6*U?YL&#|V>n7o=};fZn
+Lm6Mw4`>lgXx}o~q0~Vffm0W4`MF?L>Nv-(AN}5*M);v5sm1(g8=r%mHJZwUV8HLw;F_Aac!7=TX(h|
+5>aE_!-?_?tmZA7=t2Il2y1G$ziKsWZ0jXg=w?Ke`00(`9&@M;dc@R(*rjDV;6Zp#JxHTV_lrSNK#cM
+MBv`~hqU$1s{IJ#U;KJ8xZ)!OHB4h*(3~ymtN%^-q$gP14GfLmn7saMEL^g6Q2NidKs#$c3Jg6DpL~2
+~|$&5;auH12a1*xm`>9Ks(#JW#I1MG^he>XrY;~C#7n{J01vWRF&g>3?qO$3F6^WJPd&lvs<}=vSMBG
+V2_OnNP9KaYHknGMzl`jSZ7r!6xBcB<hwu{7hRIh%g*D9Cw7p%Lqr9mO$jfrVLoPF@F9G6=eTsY8QNV
+#&v$d$N=<uHIwT3cy0^?0VNQ5z%Tt-BH+h`m5l?_wDq-TD{jwcsw_=zAR6uy`1X~Xsu&HL*D7F_*PBU
+iRmE%cDV*IY4L+W-Z%sd*j4x-8x(vG}jsaR(@j;26bf}0c8Dh09gzV2-D5)XFSTUx^N4|L5=m9z>W3A
+G_5zmE3P=TQr9mH=Tc%~HK@qK=y6x=9;m5|^h<j_rf(^^3RfXu;n;`1G&$Pd~l;?%~Z}wr}oVKmLt7<
+{r5eZF_k0?*8eX+r=KAxR~w!rT1n1@Z-}Dk8kg9NhqJ7^05FEnwZO`nMI8!&xVrMgDX}}6d?%H%HO87
+DXTo~VWV#4frjLIH|~;9S{X};hHiV27P2!O=PCD2r0`P)4N-1@sS^~F?B^|6i%Dpjz?ks#4e2AP?Gh$
+5C~BtEDh4x1_KB+nd{IKTCS1Yn9!=k^y>{jU2pQ`=$ykFb?Bw}Q>fmR-;9Ka^SJIy7OOxU-NPo9r=Yv
+Et``Rc!%)XWPOp5$Knx4D&BrtD;f-@BAPOeg@!2~HKXwux0*25%c1&J{@JdyO?l4PG$vY}p<8Yj{4C5
+6XetU=Q+XbKEAC1~>AQtqD=oE+1b`1~XZrrJVikASI4@NB{oW-Og3^1y>BmxZLK-oc=^pnW=}P{_jxu
+azFDg-Z)MJYi1)$;5yh2*{>@Kn=9RGn8S`+J$br1#mhz-xjbB39mIVhap3N^W-`Q#}fb?p`#ON^Rz>Z
+6%lEtx03`dq)B&hf+G|BnW#}GM;e0Q7O(~x>eV^20&oaW(_{@N5jmOs(1C*jV3K<#U4aR}3?TL-YQ%I
+twJHH=kxBqKt2n8dCz)cvJq7?d)05<|2m!N@mu7>~ik@AP0Vd3PQ1(u+-=uUIHouS!CvYTH1*z+UlDS
+PnZ6dEr1Yx0i42+T#^aBnnU|Iw2JZzYdcoW4;K-MJsrl}Ii0xvwP0~<H#d!|Rby_1XfQ*!n>y8ex0+5
+v?>n(1m@EZ?cXQ{m9%2!q*oEQW(h+&=<c9K^M7=+Kjqj=?qb348)3FVj;YU1J0XQ)i_W7yMMfA5B=oN
+na{G220eQK(0L36M|PUFU|uY(wo$PL4&94a4K~5J*<7R%;x+6w)0+a<d<Ms-mftHy5yfYA$9>sk0rup
+!~ts?t|1lnpa2^X`)LoD5_jS_$qo*Crig!%0z2Rek}gbwnr;c^&0TcIl+ld_lZw>Mm>y?_l1@k9tx0h
+exy&9<NnHU>N#Q1OXhJU~h&9u%w~gCf_FyagI$WE$mhvIZDeL`&95@9J0|X~#lG1gW+bDh~X<?cXSlp
+!WOmv)6jWJ|qT<Z)PgJHKo0iz53jmcDb;e}y3J$y^Ta7|S&o+DfFoSnxpgW7rmTPJhMupbUw2#Sut-z
+RSzDDQ&~`y_v-e-Nb-#2p;vPScJT0^0haJ3Bdtm?#u0IB-4Q;gLAz?HK5_CIwioxX_$!#|}wmp7OkmY
+c2?j<dSmt$JA<tz#NlH%EqM4lb1lzz7+q2#0?N$(rKJf))SmPL8FOOBv{hhB#kE4J)0@hs{*GPfh<mg
+?_6tt@?jDlHn%%5^a-w%LCU6b0zH#%YQj%tm^ahj5}5>vFm5)?S^mr`9?YA3NlwoaHa7uJnd^aI4|bH
+p89|Ad3RY>R&y{B`6Bj9S#X9z_YvN)_-Rf}41k>S~dioz%*Kz1AaZq=n)*Y93d6}BZ#-{Y->175aROv
+iAI#qQeP>7W`u}EC%vNz3sqkW!6REq071yzEVa;r&_ouK5T>yt<(3fr8h`gymB5?UqM`Dotmv1ZGVw4
+LUc*vF%8lh46B=~oXstZBv;RYA4QT#*tV#lxn7|H4jiauJzeiDr7ln$zS}!!Nt6)i#A6gj(vL2_KfEi
+NKN*tUo{)iO?!pi@ChBx#<{kHz<;%5cp1nW5JFl9qBwA&2Enqi`RP?19G%U;XG)NB?vQzDMsH#$Ue*!
+R11TyVtBZZY@A$glPI2p5_%4uOK=J13L`epqTfCt(G#k+M(7s|p8#x1rvk}J!p43RI65xp$lBxNL1QS
+<@J(of>E)aB2r4*W6VzUbFlHk4?qW^!jFVzFff%Vfs$4%^Brd~*K}!&8yl)#~HtI9!XNK_l5-NmK!N2
+)th_VD>Os~K2YKWC=2niexPH5~2mvWmz;>*iL_&yhsj$YE+3tF$!E*uqUG*4}?0Ym4dG7y@dYu=}82u
+wRYvCn)?pD_4?%t$#7&zq_}5T^#YE}=puO_bSu6$b+i)fy(7)LAcD#NJzRt#>nvZf!vb4IFN;d_gGee
+kZm+yf23V2cB}8^+Kz)3xG{`e>PdUMxyuK2ah4#J5jJ-dL36XTQ9q9rT{lQeIp^rTTNs%*-^w3gGUEE
+R>Fzp;elQU7yR=g&w#_IJInPh<Ajis%x+-TlDP=;5OC6+K=U0+=3!pKriPWPgdHc=CYrR0HY6nIl0+b
+soP<d3xS1k3&}y(i3H_O9ToYO)k>({t$wbPZbft6Ml9OhePGBA^Rbqkw950@_42nE|x7Z@++N4qIXKN
+HTE(ku;yFX$@N+ty?58Nzw!}ikZ=z6}J<i`5Yy-N^ZOry<ZXkz=x%W2Ki4aX~3yWhGx9}%8VJq-0{aE
+~E~#?Fm(&{av}Hzcp)FinV{VD2Hxf}?X`QLuXQnEhd_5a$_h6>|~=X{#qqlbAlxb_bCqfs`SAi2gIvY
+LqZUzD=Yk6FwmTrb&N4rpV!PFIi2_Kcu}x<S@CL=)qi^e;A0p5^6Pww+ToLXtbm>OQQdjgW0_`QL*Uo
+{*=S$21Yfv4r4i)$`i@nO^lsD76hpxHF31{VQo)CDbZ>Kd{M2T=xcaAnoW2&-8tf6JPTBt1dN=5N&FV
+TF;!rB?!(iXXt=5xB^q${c@FCv?Mu;Ku!UYTNYFaW!9;&3niw7pvO7O){jgDNN)ke+#&5IRE6q5rkkY
+h-y<l(w9S|s=!b8OpBBn!hK8b`OJuI5gv;pash@WTEz793njFlCzK;ET`NnBQ~cKHyPB@EqMg@|B@gf
+WC*^kc*H&KwiSorizxUU3WtX6+HFPY-n~I)o{nYSGE$w_(4RPWd27C2UZF&C{k67o<)~nbfP%V@{7Ml
+Pq&sz*txj#nDpKE=yP(c<ZDBllA0h)4)uKCUPYpcXEZ@^o}%m>s+@LYCCIJDIS4*>9OS1?hhyGj<O3Y
+Q(;Qe!I6L(uO1$sq)Y|hB8Ql;+P6f38Hf0at><)1QoT<kifQdl`z+|GOqvM;>SZ?T0Z;X^^i&H0@la;
+NKvt3p3mR{X;vfcjhW<t(;z|<|_1|3N6G0ueI+TcZ-?FYQ@~rj6hfU?w5q$k5<s=1Jn$p<i+}#h$)O4
+bVeJ(Qx&;vW1<h9Ht=q8?cmjZ5Ztr!M=vz{fat}2Gy&{p`O5Mk)9rU%RtY38J5G+CvvLqg#m<h5j%W7
+;IOn4qB~YL+l0r-WkBbj{FAPkcL151vBayw{JI3`j@^_|pId>88|TNWZP*sZoi@yg!PHp2z03yvb7oK
+rruku<ruFMkX0_$ugw_r07U<A@0_8?COdf-cJQ{SkJ<M?6IW(1U6A9Oua{dj^oENMhujqoYSDmnTrYz
+(hooY=~ro;5KH;D!+PIk_c4AQ!IUkd13ISH^Kdw7Nmvd~N~0^fR1*P3><8iBi3DqU8qwp27LVfYzOL`
+2PYAk$>mLe4(r|Aa>MMDAy3)%f<zpbph>YeUfMY-g22{laS0pGfy-C`ed8Zjv)HCGRErx*|G`0sV=t2
+AlaK?mu&$JZxssmsE`lE><j}(e{(Waa-XK5yEY<N!s1;7a7iE31!r&o=Nfx8~dTfEr@2pkZqWrxNgDH
+~g!mi64E%3+ouq$RcF^awSozobhtcR3T$_MzU|+jJn)F3UG>p%-RqX-j(|OamtS7EEL}46SHG?53iFG
+)|&k&<ugZ@OTnY*Na?<5MJ^u;_rgL(8+;KfY)Ok@->Bd1{7zKRi%=eo{{gCNlM8*#&oEw2K2Zo#KTL@
+lA2Ain#4H8Z*rI+Op$a&Iljf2G%x?R(83&KuYomzwq?TdObZj}pP)~e^u~gN7t>8|Yq_ZCVR>R2j;EK
+l82CxQWFR=2xm(tIL6wlzXX&h#mMnt|Drfd8N#Q9e-lYQDB~m<>-$Z~|Eq-MMM)f|SP|{E+Z7wsWARV
+}V;}?SPrNj=#B{~Ap3{<$`rH!X-(BYkO#uzZlx~2_iKpMr}6B?%;D6}G|OgS0?e_Di4z>xQ6szm_Aak
+ausd0#xyT$iE_kK!qqLw^=j0YRf84~btQ1VwuC)IhV6h~p-O!gRJCa~6+Gui4o}qSG}bkx7jKk)W%Ck
+e1d##L-wL!ltV~;<BSbAPWQn$x)Z|l9tnh-vq$4n~H{ZLhIz^ohP>O!`cEv;KMek#f<W+Q#!-i9)<`#
+X+><$#}fUA`HwDvmp*w`u}PdwzX`BJNPrFv`aIJsL{4FbWj4C=keo=b$mrTMeG-fX=4yH(ZZ5>3F=B!
+Ux?$qdE1M4KF=NV<W+B{@FeYms3O>=Z29?g79<E*Z6R6+~*SiG<jWr2+;Liey=4C+qbkF?t3MSM;l)$
+?!chYnJ9vQR*@_MW;Xw!TNNF+guxdKaRbfy%L^=49VYkAt9uv(PmwthNHX81CU7bmx-!I^Su;w}}jsq
+5H}Nhf~NDNmS^Ny(VpixJ)1Nd3(tL`FJ;lhl;Z^3!v{GJBl0tX-BaL)5YO$|)@OR3U*zH)aj7^NF5na
+*jG<y#NCR2u0!?Dc#p(mYU(rJF|T^OFZo4;1HJ>*xz`=^11U<*YIj}a|jXCHk|R@-ILgYqlw`YgzDsq
+!y9Y7w5lt`^z5@y5byE0AvN^-UlKslvJ1F-5eh4?FG^F&)P;0$CFcSA!}vjNSgS&R&Tk+7q6PllL_OX
+}b=cq_GM>hK95uvdzR%qvyflAH3Jy)3Nd69hZ>^!U)Je=Ylu(17CK1Y!DKiHB8ZtPiZ4#AL(0$%>>}|
+#;=}@!js`QqtvxV}Lr}zz6fG^7g&^VgAkyksEc9vAKVv!RCeNEdf$WW3BFTAOMS0+h)3OJ1rsIJqik#
+{o=bL9A3s+Xj(Hm7Hd&-HtPR499S0_cSh+P&bA;tthA?$&iABnvleU}|F|h_nXrDz`+75X9EW*`@)y4
+|uvc<;gw5=Pj&oYwI397&RKtNfRn@y#zEQ^}ub&D)@U`=$t34LV67{;=6II`;qJx5(vDFlpPHDb%2)`
+rAe8AzLFz@!(U^8&rFVOPx@kcd77)a6&fDc##)bVi`R}@=(7ED8sRwgmto!ve<+ivoiL<J^g>V}xlOr
+9iMhj+N%0_oP8ke7PpD#CPx6lPGxbNaIQTPcq-#ZZ9XmtcGk05tn1sCs0kgpe$E-9eVQkn2pG=8<;mV
+<%>l*xV+6e}0QV(=XAlVZJ=gDZ|4pjgf+<l-f1<=E4hdKwr_wF|lzPuA}F~UB9TF?4eY}Sc3-Q;7{FI
+BRFsrgUtz+abSsB)MdX(wl4Hm4SeiPH(5^eJwGwHYWK13ol{MrO5TYVBSH<GpNzJ&7i37cr^5GW;9jH
+5n0xjj>3f8LzyS)1C`a#5M-C>)4IF_#a*U7!nlSdi=yQF5XPmyVly^?|_X<nA!XiR*tYu4`B`|y3=A?
+cciPuGJEy)YO$daOcOs{p^({~#tuQX4LPsGAj*2Z4HV;~I*ITWJyVQ*1UM`hwTd>wZ-GeZVmd5xx@Wk
+*AP&cYBTE|HaoVgh&|4*gvF%A*3Mbf<X<mT58`hhr@DXZZH<0nbDbUuBh(I=uWUj{Y9tLCk!yAK7SIJ
+*iVpj_e)0LAx?&cJl1QZH$%G+?`(R=ZuizN49OI<-kXl~we$R6PzFv4k9%T@|aj{#7bkXv)8<)EyW<{
+XPZ3OhdRUMQGjdZ9u2+ij>8!xd(5r?0X-_J8r2!)3S}E87TY4}TXKXF{jk6ueNvWQ=B!jvbobSK*@#P
+&xs1CRFj1Sy;ak$cp~(`c|xcu?m!o-mFOFJgLf3ba&Yo34cA3v-O|{j}c7r?U#fkmvDvhT*U4Pk_+U_
+2@V*${S-wKnAgIQY`<7}+Bmqp#LrK_5)YG`$`#5Ayn0Zy%@t3?g1af%@)J+*zRKo-BTtE5Rs{%Lo3gG
+#!%oZ)6jrpPvy1L5%Bd*)I&a&c0t<JG(<+ImXAVi}3n;Q^LP8a(X->~Tfe2{i#T2Vaaj=HuwIscdrgB
+;HTE8dDyF@8(!(~YeA>3zpPl=_nhK=Zk!e^!OG~`e++GAmHX+}>u9}-FVa{3FYoRo;1nq+OT1GWXD+}
+r7S(XA&yc<L$R;b(yXA!w)eHX7*Va9?RX0Q)rUCNTa8k*>d@N$6=0Wa&UiEKCQ+qeeLORz_p1KTIOX?
+aW04sBW!?y`})lMrZ0NPFc&PO+qC#?V*RfJPZg<IC0Og)U+Jo_Ur_kr)x1?9|<D}9ZosSFfwu|W#V;V
+rS7;m${$+HLX%=Ax7voBjpyBH#++7p!%>7w0n%`nC(m+$7=w2TtkEb%W5D1yNTu2R5@|@P41Z6WO<&^
+^-N@VAZSkr3;n9e7@{+W&Evh-o+Fu51`-YBH1M*{97I=pP71>tX0qV-e4t#63TmZ-~ii?mI_Jmd6c@Y
+g^RE2<yW?!&LJ3Mp<QlZJy+i9A05qy}OgDD0$XPreJn7d72&sPXSL9CJ(bFK|=smL~qI#hlGtDwA6Dl
+8ll!NKS+X0&Coz>y^hl1tm?r-?@RD>qDNr+=1c;3f^yfFhXw_R+Lk=y@%#l*hbQPs-mtX8&|5{vxx7S
+BNkMr0~hzG80B{bgxl!-!LV+;+WF#NEZ^wfR&<!gx5hVA7%~3wtAgW99HC1G}e59P`kbu0idEG@(d?t
+rpO^QHwxdpgt0^^D33&-kY1$g9iCde{U9?f$Gj$u&dKH0lPhk6f*9V$aS^$gs6(<y<1pL|c_BMd!el5
+dt3Lb0bFIH?2Ge6jPkNtfGf&g|B|2i$d4a@|cuG<Q%v~PBga@4{An1zY)T~I1Pj$mWe9HW3`OH459$j
+LZ?&Y+2Fy;lP5vuKUOeA=r#?HHgbNGvBtg*<4UA-gzH~sZq62$)6TV8O}_0!O``K-i-nk6i-(kO`5R)
+^~h*O^^t2y~-Dnh-S;eO2Pisi=VJWDuhBGOHdINTgBJkm-C>gWCvGe2Lg>0x3h@ADA4cu4mz9yVic}=
+~$J;$}DMQPN2IB#7w?~M#c0ufrxyxH1VhT387kon8F&JzEM)(GeX@TB(G*$5Tq$7OtlexI8g&JphOjq
+<09lp#xHryX>edU0UKf4eesUomKLrdxei~r6x(+9gGVbLGTW<R*XNN7=DvD07#Za05{_cx{N9ZT%9&o
+?q%4RL&ai%!B4C4eugsG5Tx*zKIzx;hp?cHM@>ZjPH`*(Z^>?U9OX2Qa36qOtNG3UZ$|VfFhcr6YVPh
+4YPELC+cC54$QXg?A5eZK3ae?=p{uZ3e+XF$cGdvPEB`w_reTZ_$=9q0ocx-RwBwEhy<uP(oz!(L~u!
+NJ#5-K?0YmyioZHwV;DBwO8ZAy6{TA5G~Qyb$<L?}^YRi-Af6pOiZuzr{&R)O37Qj9?6cz04#zJ@&zv
+hf^iBgO<Q+pw0DoePV5TbV3DYEafLOSCc_mt;H}9IAemtWmuIBsh?eMC0k53n{Wzt$~~<iMk<Ku(UgD
+>uEy5E0m6eC;|VoZkNK8G51$AO>@QW&1GsQ!`Wi6B%5<B9|I=JUbX>-?FfHi8+O6=pZ;b;yA`2BgfzD
+oJ7Oo2_&ik|`eE5}xdHB5?bcz_NfBjAo4bP;_7aoYW@6ZfEl>~78*=1eu_xpt6OqT<rG<BwRCI@pvFa
+W$n|3*twp7qa3^QlZe4B@j3Sed`=ob|ltO+lt5s1McjUK$E>Scxc7+(0Y7U{v8-7j^OR115d40xnDJ?
+7*Q07)y+iqt)rD2Rfyo95D{3U&f2TuNhto0P047la~x$>l8>NN}tFzyrf>y8gbR6u55<gp_IdoGM*$U
+S=!ZXY<}RU$c=z9khEs1>iD|CE~+46(|_pyb%ZG4Vh%pMw|}rROH`O>ESLSIXkBcc$%KV*#TuQ>ZLD>
+0`<R|NEbBN+r>$w_L#(FTwZT+{h>?`Yu9Oejm`A*M>rbx!vID7?=eUZN_Yx$*cC7jC?y~`A2Lhp%uo9
+J2>h0VR}>cc0-?k3MZICubYH({0XKFp?ZDXs<_KH@8qy)5i%l~2R29LY;@M5I{(8$x$Khm{`$j<-CI~
+rqX*-n|9zj3?G){lRm03e_LhYQT4a3|$@sP#^e={y^pYCV<cHQIhfqsi!es<a9VD$F_USc1sITRWYTe
+Hih@<8u8lX*Lg8g{_#Z+hYD6sw8Q6hYb6-(6BhLxdpWg~uMI`NHE>_K3tqG#BDhL$5i29EqOuF#S$V%
+K=jYnPU8fC719=qv9>*3mH!2(|M>aQ+I3JA#2bZolxJ?hQM$~oufyc+<79XbqeV`3CZ(NiE-#=E856I
+(H^uU#p@Ik+9|RjFg<C*f~Yt3z@;#Tp84!vU6cw;4}bZ$w*`50i-GXQb;J2OcuL-cO|j|k{nTl!00kQ
+1xPGnT;qZqnmA<SFcHI`srU=FvoI<!MmX(h4My<<V??j)<V=bmp8}P`A2Z1x2YW>qyBAra9%mRf6knH
+2)_@Wh4FvWucMu?xvDbxG)NDk{|auHzsuo~t0&<%4jP^qzd(8uc!6#4+UjggYNr+9$^Tz);}>b)BWN8
+MFj$hVqtNFq~IIIRiUJa-U>_LiI^Pfvsai4rQ#Rqf(%k`d^6M1?^5DUsFJ*+QVmnk}UaKAGyi0N$P58
+*p+dMbvHepZ-ow3q|v*PbIv_ge$U|uBJa@xs(`n<%xWF$x1C8F8{#r)O~>~sEDRlNMK4qb1%Ir4z4BD
+puRU7t%@{fVCLxOR4#9;#u^Svb{>n7BHNor{jHLEETH7D^{^B{G;-H3OILW{Q8fKqBy_l%gfvpVIvse
+MDGpN;<!8yD;jb~6yQP7rsysMhayT4_mh9j><wU?RQ;8w)VEe?E!dm&46fCX#^{78s*xldsJ9J}}l^P
+zCKzxY@&rmI@a%?>s<YwjJ8J|n|XnTG|H#lX)WuYF%t8!0e>3+l`WP~A7z-}qSo4t__EL%`y8G)~`d-
+6hum<z);#rUfRSw62(TNIr)BzipB8<iE|E8y{dkkb}Be>CM?IEg2R4kahnE-Gnd5~oK92}mrdo@kq|H
+sP1I+XvgXuV4K8{rOMZo5%lnar@s;O9KQH000080P~d=M<2D@_8J2K0Fed&05t#r0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoRVlp!^GH`NlVr6nJaCwzfO>f&c5WV|X4C;#mxY{_y0^1F
+GkQF;M)?bip6ge5RG_r|Oq(M>%)L*}kw(M*gcd@IJG@N;S@6Bks-RlnxM~{;^O()4{o{eZYT_k_eFLJ
+w`F0J-V3oCb03q^@3n53i1{E$kC$k-j_h((=q>O@N~k@>*StjvKenUee!4CK$*yIdR?p{lC2%!QXmJA
+B%MvBwVp)6&>eJhv=77qo4Ti0`iwHeekZT($SqOKxcJ`STxuxSAeOtQ4go*HOxj?K2lAC5h4A%5Ck9?
+YWyj5h0oh`<<3j?X0ZmBY~kOZ)n;0z0s|HSfk|D3flyiO3O^nl>P<8?x`^~<wBF?Lb?+?jPN9M@td)z
+uS_A!CWLBTKu%!o*;elC;p2QwQ+8lVkF423&9dGqnWK0rbJmVYI68!w+rK(B4n(q+2#pQkkM7<ROPq(
+hpV>M@=q?(ZZ)*|zWGwZB5B$;1Lnyof>4p@#Vb7JJQ&&8Am0FP0N8CNk#|sdl^aXQ7Tc+A^S*tz}kW1
+ettFMdoisJbbeT&mHp0A$nkqfJFiUyvLWOYyyz>sBy_6?W=U^YsVuc(NJlj&sj6zGp9tNAF)XuL=%re
+&P2Cdqmlr?gzB%SASdh%)B0$HC2OE?+z@0a+PXS+EyUxmNoV8tlMO6|@)6j7H}yF;gV)lN_4=)kS6W?
+kE@~ua4f6EJ+*Rrxz<RJ($0D{RPTvhx-`&JnGZ$10)IkU7-gV%Ez(<^jH~V`!qDphtye&+S|Lq;P&I-
+)9rw0ogwn8heq><|F}lFola>@MNzfQ_rf~%2Wz&_j|PU{XjUsPhmGe1ry2X@@wBbMevx|TJcg|j`Ce*
+1mnFxOES$_vC+8BtTm~;pbzAT_y!$k`d-%smjE&SdY@-W5=c>2q*=&Z5r8=El3Fq42iDB<LaV!k%3k*
+j|n~lWU*=%}_RoTYFbG<Nnn|{d-U(u#&_0H(a73sY<Y~R(s6a54BfB6?jdOtx}EJ%;wMcR)2rtRiNi;
+91iXxZ0R(}&X)A3A>nP)h>@6aWAK2mtey7DwZfttM*{001vE001=r003}la4%nWWo~3|axZdaadl;Lb
+aO9oVPk7yXJvCQV`yP=WMy<OXk}t@Wp{9LZ(?O~E^v9hTWxdO#u5ImUvZT`h@6tF<kW5I)Xh+`WOE|Q
+8cD9l<9akl;7Fpv3xfkhv%kL2?%n|hl5*^HrkY720e5@*zR&I<&z|l7QK#o`CRgfma&~@od#+9|-<|!
+Uo>=?r;F-Ex8uhMB7D=vCbymbiC00#~|0HRu)%!eoG^NFxXsJtUDixc`L{(9$2unu?p28$gsszhkn@5
+utD^sdsuBttxvA+4aU2gD?PKyjruZyxuirlKGG`cdenr)cCE@fRBP+M(4d0DC6SzkRrK7IibdzX*YI8
+D_Jt+ndLSW`ZlSQtMm@~TW`HN@%J{!3<Us>=s;ourF0$<$D-z?G^Bb-k&UMef9fg3@GG>T*N6nwQ2XT
+g<C982d?WidscFSEY#)E94<}rF0(usVK2OE8=9np+|KdLmc9|GG%7H%HCYPSC__uQ}xE=ri3)t^(;*y
+Y`#n)lUt*-RV#XCm#wBQh>=^zh`K01UXx0HQYOKBI-87%C+hj=#GmFU4is$K(-rX#<E?08A53pl3Tu>
+p=V-4AT1jJ-<V<!6-QyP!grI9UlbRV-TQjfI0VrUt`Y^eB`|kan8ee@>AI3K~<Ey)mKVdC2jkmC$P)f
+2@nt(8*Ds^6Mz&Hp@&u`A&V#oM&ayhyCNZemc?yk;nZ`H-S8#Pwf<D0w5+55}!jk<n+bN%l2d<2g(#;
+cf=xlhTBisvAi70@JHUMHy)(Lchx77V7bTIxpwGe-t75-W{}SZ%(`hG~&6SYB*t75bA(<|;3$fm)Xdd
+<WjQV_i^b?K^<eN27syae_rUe@J11TWr5b<{*8M7DYKwr-iL(>2!>}$Inkrj)x~d9G@ul{uWQ3`cdas
+ufH*sJUcj;mqn&VE>o<^Vg~79Jqn>SJiIm4H9zzV5$zQ$YW*T}Ra8@WqIu0pCdn7g%4|iRB+J&NUfma
+EeBeFN9TgYG=0}LMsH%0EZpb#pB2WGURl|$(MXeWx97X$V29v5uMINlx(xjAJ94#_FX&oNxP5Rc%O0%
+Z$HK|)>4@h;5g_GE5yS3csO~(t_OL5{A6T-zb%;i>DWxgPkD28M^<)p*O?YrTRKfd_e@W(@Bq<<zB(z
+*BadY$BPv9`mL=PzE+g3Dr&M4c5af#GETiZ8=VN5dumO)Gx>d211#Fw3U}T#5)Dwmg%L$omneup9IaJ
+>(T{Cdr`>suf7332Y6oMk<1yc3DS;+~$N5S+y>ZVCHk|$}3VlnaS4pH=oYg015{a!7(Eh(nbi+lia|}
+am*%g1r448Dn4Z)b`}&_$6tOt9v*-B+XZD=TVzIMiH(qNb8;-UV1_DT>Gj$mv-xGBs-az(D4BDp$BB*
+*#7SgXy5RVuj|u*;%OiySLRU2eh4yyX0W%RX6aR&LHB?R`lTETD)n!MvRuP<OImhT$i&Ta5YJ~ukq&7
+wsBMQW&LRKp&P@Lf_^TiR!i%=c}4FWhaWp3gDa#@O)sj0>^enG}-U4coIhLpn<wPUQQ(K)!0g5hG1nQ
+S5fC#;rwW~%lql7Tg4HI)CT(;C_UWqSYouYRA?Dya36JWA`B&Y3eLaj*I<jERd3Z{TKLRgpp6s@6to5
+&4*x4)Wtkg0z#Al&nbcNwpy}9ZHNIZoN_&q_iTV6#XxpdyvX-R7~nmBzNFodQ=KtLM>35YdS(ntJBHr
+iCaOU=yXxQB+G2X5;R+_F}6Jh-be0O>b0eU%xN_>bG8KIDA_G6Ro0|)iqpK|EhiV{qzZ-+zC>-NqX)1
+R!+vA987Yi7>?A0)klcuks6!7_<Q@nuabo+FY9zZg&Y*qsq{Lq+UU<JkSP`)CCwx|)*2^L_^4c@iyaq
+7}uYKEu>NlMMDxaV|T1P9exx=4D2FF`@H5!eSsiGrwNN30FR67Y`pz=DSGQr%P=L^DXqGPE2QS{`zfb
+87pSf{p7hmX#SX!9O}vC|&9H2tKn?Yh}va}G#H5gv2o6%Ii2GV&2;=#i_C;+J-XqMghVKBL(3)`}8@U
+8Q>CXz|g^x?puIAQ{MC*ozzAr2|k#Vp@Uyh&tytAoM+Ib*YnncR`KcIHPt_C&iLZv%(6du#hNapoP2u
+Tj#Ems3^IC+l!Jrlr;q|1h25xJW^%@pA#f90)w=3Kx#wWW!58K>*d%o52!(6XFJDffufeFdd2z6saDU
+5M*yM#PZ01Rdu0ZhC{Jvr9&(g9T8TuDS{Y<u3bJlA4!6-n<Ncz)q54qJ9~@1{+$16=a%tPz)$3)_iAE
+B~`4#j<?cpBWv#Lud+qfQ(gz6Hgb3u5NfP0f1H7P>6mvWFWN=!`#t{m#64nassi0&anseK#C40j<@UX
++<m2SEh-U}-&R3q>(zwDrs(oDSL`?RhYo8}aIyQ=tb3l1sTd&J%7(x3LeMKzX2D#DF6>h~mh*eYn@_7
+cPjDRZ$V;W1)jx5L1*}Ro#U!bzBHd$2GwlY%Owt1M2bu^n`9`&4tLSUr~Wu(xVog!)^dkNCD1|2&<J8
+5Fk}s-i;~;VAz4zZ2C~Jk$PP>4H0=qAZaJSD-L@Kb0`o^m62xZ$%7KIRpU$CkRitBb80upngE+~@)3&
+oWg;Fy$e(Ja^fc<3NA>iqAq#C%rifb<0W^Di+G&RzHgx_e&!|Usc>tjnEm;<v4`7Bg<3=Qi4NyiCsF8
+W{U+aWX1Ua7}B<=?TAcYfe-;_nY66&tl?_<Sz+h!N0Bou6<bn%9VL`tP_J!ql~CX<{ahX~S&;e9-KkT
+k_2fm)ICv`LChrU3}uC?iw_JwtPWtk)p7p&&P9;YFNIToY~F6Q4f!N=PN9EaEz%u0_~Gsa!)Vjrq3JC
+8f}nzEmayH1J>q?TV(Ga<Q^VD|gdNDuQ}}a^*`@8#GM`8vquqhvVC`$)ppT;ZnOoLG&0WCrC@UpX)LV
+UUP=dH^|orKXOpQDs>bd$Ea9QoTWy{jhv(ow#_9;8I5h;ccluFD%65>5jgdV+7*|1Kxm1`78yL*y^8O
+3TIW6z2e)?~z&9N_FQeM^A;~GtP3dn>SrFV7ZNd(MP~-^7=LDogHH900ZEvF_JzzxDObWvMIzTmsEhG
+TB1PS>ycdxTLNgZr<&~TePp{62b&1U*RL|y5m92Rru!5~Ah7|M0(?kXXK*qlqTYDG3pU}p0Lzd%Oxe>
+KhM*h4(3><lX;!xH&UnM%8LF>`8mitdCURd;kDK2>kF_>>td!k83Fi%BR76UrE2z&5bS){6zz3dfhvF
+*2y!<_zH`Vu9RSUkDQWZ)zTm!^W-goqr8HVy)9^YbfKUP|tV*XC$>s1DYBORd+<!=~{0n{s9eusE<d-
+$K+Np_*`TG9jGPZA#V;$QnnS_wxcG>V$xV4aC2J$Sqq2U0t<MST)nOMm9`EU@5sph?I%}<R}@9E?~ua
+f!_rMq--O}o29~rDU<@_!(KHN4l6+X9WaU*;i)estffoE+ET^-aQoV!$no$#Z<<3N6Dha<3q-iAVh%(
+p_j*fE3l+K`HxL-wKbakr@X?n-(14pANNxt-iy--VR=%+HSvd~C^5S6R6G!K)Reqa?@DyczAi&PNupm
+st|fWoUVP-lzZ1BDKJWNblKSR(}VqXVvmL5%-NwXZv4Asohjhafz-m|Tr7?|&Izs8{Ovi_r&H?{22yl
+bKsGJ)g>h*e_|G7`No={gphJ`z8L#{MaqIK9|3L@GJa-Kf48Yw_`Mi{OE;W6FmD1NjU`^$j~pj{^qgi
+raH47F-0)V;fb&4JQ(r7Ix~Yp%t?NLEJnz^q*10VQu>c;33o~JjE|<+M0z^8x)%w~-i~kXUr%oD##d(
+~+>5WXEbjBa@(R4hkTDNpX+M+GcdyScL;jG}P7V(KIk|lg=HH&5-kg89zZy^JRp?&_m+#(8&UTk{obu
+q{AWD(5I#Ji_RL1_x1A^MInXCJIXr;Qp?@?-W&*`VLe$-vnEaG%}pE)(UeDF$LA**k%5v6}+2_nH%z5
+YSNnLOBH%Tc=^M<5p6(YI_m=<&&ga{lhR7BBIzDeSALU2wT#RibhhwRL!H5Nyw`@s4v*c50AQjQmZrS
+GfuWs^RLvxQ_@*qfAd6atNqt>80U1NtTe8yCz^}fNvRqyNpF$$F{J1@+)<uj<(+foOR!`_|~)qji#LI
+yuH*6q35VI4oq{;9)9n3vY`eZIrvon7+8JRiraqE+58(06jE;4O6)!72**tnoGIzQxxDH{eX_-;Cif0
+hlVkm_$ejMy>+{y;Py7VA^K*xzyvU#AO59KIvbwW6PCVArYEcw11<LRuOnb3(;A@@}(KI)pb(ETDSyZ
+MvQau{+qlN7I5riN|r2#ssvRF}RoLBz(n+_I;8~S3qR4yCb>_M6t@68uL;imi`x5lp1x^y%DmR6)&5Z
+sEy=Q6$3%4l|Wu;^yME);mm)Dog&7Oop`ni70sjStpNGdS8(wTBbehGX^@J?HrZ(zlzt;9UGEUqG0pM
+%eRIoer8Ax)Ke8b+3s=ZivUD_M_`!bu>^%=%sYFI^si*ru7VI<{=D20(R&qXv79G3+|ps5$@`!J>#0M
+P{B8Fa<L}7D~PHkH8Z)HCzZ<-wv`!Zb(>cJk-AzNG?3P%l?+`)Kun;u&=H@b6!B|KH{+zZ(QgG<i)99
+?1uf0*9Aga*2?A$sOzZ+ipVsqE%ya@m<M)kC1RJ{)GIVWNgG7VPm##Nzo1tdZ(tJ$d+VZD5qJUb{8~M
+&?%ircjn#z2RTvW6dElu=b;!vWKz8Z_a(?<y$mWYHCC*y!zA@Lt8=nf9@ooN_t_|a6mV&75h43kow#A
+N!;ZA4@1E~yUcDvdPu9I)1xC$KkCe>@3IAkZgN$Mf%!aW|?ad6zGjHT?igyfY+%AQ`Bs$>3mhK5&u4N
+p{es<US~&7;j>(o2lAnf$kt2L+4*2!yoEw>7M%+>Nx7ucegn$qn?E>6eund3*t<>*KbS+t92=TlL><%
+g)@Qc*1Iw?5^W#-@Eg_uUM;I9eU3Tr9?&#utuouRzJp;TyZ`?r4;*nTLS6U@`ls~VabUTtilSPe8miy
+2qzc5hJ)KDL_q@D=wS_owp|11-B^daUCwwvhhZDRpmBZOIJAfku<gN<Y%tKd5*10nGr7lb2IOWb&yq^
+Z%G*aP6?o>^>i{KjR&_yRba5j`gw=+iGuA%|e5710q6`8Kweaf7%0Goz7p}!7iNe5E*N+Y+6I4YCtjh
+32>S^IQjz@sv9%^81WRc_p8kHjmUKUnt}O1c@TPt)<WD;w-La{-JC093ee1+A)AuiU3iZN`-&`D>c3*
+@31x(NTjevJqK=KA0g-EEr8Cc^&u@{kYmEr?kva;R04=hD0~_G^@RbTJDQH-SoJ!wd=_aQO&MFZ@TP4
+GJ5xl0etsWV-9LE`2y7gw4sMl|E?`3Hxuvd^eXGL34eIv7&$73PYkPK*z`;^lXb9F4u%!OuEe|D6ZCx
+AVP5yH@l19QzN6mX&!&)OukWG0ei}Z1NjxxrpFa&xUWOs{`4g1;Qr<mD?W^7AA781Hy*ts%waj;$fo~
+V!`91-*YvkuoU!kLZhk#+%yd$4Ut=Z>?SHvSZBB%Ps7x(#HueYZ`WzXyLCo@m+*?Za-c%$l3sE7~upU
+|$?><^;Zuen95^}Uq7OKbZ&@__$+YuzevauDRp3+$(9ckDmbL?1xk`8qsC8N5cWN|8i&vYh0rfS6=1)
+dL2IQ;$>HL66UG?T4WaPNzic2B!BkM%|v+JX2FY9rkGarMq<8sWm?byWHe$?=(?nX;SUn2V*9usyvWM
+98Hyjn#DA1J!>_+KZ1u}vt(Fd8iN0LrJfi%zT8(I_KffLlKuNDHT)jByMx2fQ6|2e?I^24W#!wqcElQ
+tEeU1tMM<kkek+>n?Hl57*=?_8di}leGTb-Q+1c6;Rnr{&54Af${XM#F2>xAE{S6equH{<}zP#X>z<{
+Bt`=TkB-8j!3s7!BWbYpddJ(j*>?_$YVZ$|q)gGR-_gS%eqvg)|~1i8=u*MZEi={qL*#)`fpEBX}Ox=
+!iOPWSA-nfeBGcXEV)v7O{_WB4Oex>J4R|E+>Q%J=ui^L=-249IDDM@3Kj;|>gz67jE4i@m2ja(DAy?
+K|YR{j|-O{{>J>0|XQR000O8^OY7ym#>-*zE%JL)3^WtFaQ7maA|NaUv_0~WN&gWa%FLKWpi|MFK}UF
+Yhh<)b1!3PVRB?;bT4XYb7pd7aV~IqmAzfBCAXEPd4GOI0Kxz^Dy`o9T8r#LHz2FzYSs7)l8lD?rjVA
+7%85#4sxniq*8KY6^Ds0Xpxi*WE<$<s-efSymy2t?>m}o(kN)<5*DpW&$IpLz{rdAyKl|;MpIyKF_3u
+9YzpwwrZ$Ena==$o<&)4tXz516|Z(hE>K7ISm^Y!Ze^_#c<&#Tw3U+%xZ`|gJ~ufBfy;qANY>({TI-+
+Z{f-oN?y>8GNfzj^iH)h_uD&wqJ-{r3Ckch}o*uMdBt&i(t}K7V-s^~>*{|9{oKdi&=6{`1}UZ{PXHu
+K4=>+jr;luirht{P6tE^`{@L@ju?`<vZ&7{>Oc*KYzG>_UX^Bvew*H{LP<#e0}oz_4Nh6eSdxN{Qmjf
+U!K1?Px9&8Hy_@;`qK~lF@N^{=l^+sa$Uc9`R-q@-@khOFYjJ`cm3kpE&OnOczgZ+#}9vg`{sWB-Bqt
+&{psDycR$i=-@bc(zTUt6_QSvLhW-8Z<J%vuuV22o-aUWw>iwyHcf^P5<(qH*)7y8u{C97^dG+m&eDu
+ScZ}#KR>JQKFzI*>uAO7QS|8V{K`TcI`^^ebQp5N`K`TY-ndj0C_UHI3pzJ7l5{`q?O{`#KJ-v9ZpJ@
+qRZ^5xx->sN1g_4e-HU(c`h_uTt0&+p#vPp<lL`RQ(VjW4b})<1jsf%fmw{GNh8-;Mt9dc8;E-A^Sy{
++nI!*C+kvdiBO?|GfKt|6^CUAN1chtgb&jUq8Hm{`QC0FLn+4x7UCF{Hy==yFYw&ee&BsUjO}*FTVKX
+w_pA7@Aq$aPw#K{_??<wefRx_;5XNPs&_BneE4xUd{^+B&%XHdzwHvA{POc(fBw}UY5lK0|LV7&efj0
+}tKWTbeRBQ&lP|vd{L?@D`jaoN-~Zu@-~aB*&py7cFQ1=(>S8+cZ*}tj*TvuND!+TXd-9v-4=-Q6et#
+bQk9&CE?*_j9=KAx?zdY|@{`&dVM%49kPm%9`{68AQ*Kgnai_g1@{Ix^>{(AN8_2%t|7uUbPd$qB%(f
+1!lch~aQ@qMvD|MkZ&u2J@nUcUL)*Lw)Q+{J(O>f2rQuU@}>`|ic{%eU`8@YCOXvdcbdDUV;2_9)l&h
+cEXh|EvG5&wl$4|C>hTqo-Z*moFKt{Z!w+d;8t>@=pw8ef(zQ`+t78z5_^mZoc0b`TF$_+djPB&$lhW
+54)51^w=Hv-S<BLwhzx=|M|_^*KhyjNBjJrUVi`b%`-#t?)l$-cz*Ns#{0`Zz22C8`TfDh?xR1w-4FZ
+aFaGKCFRkmdKYaR&fBO9~S3hbp-tVb=@cH)Lo_8P9-csjv{o)t@^?H1q`B;5_R7d>u(HvPG`=e3nPam
+xsZBX!0`%fR`?nnwg`uypm)myRYpMUd=G5TqKF!#P-(60aTQ)SnGx_(xEasTo2{duV$53Jc2`!RMW{>
+QR!_K#@NE?8HLzl-{Jv;EzSU+fQmZpCGtyMOiL_uJh5t^2>&&-s4=*3bXpi>IgEY5)AyAAkS(r=R@#p
+a0?WFTeWY^I!hqtIvP;+kgJzll{M4W&8L+cYXTjm+$E7jm3Za-8+pJ<8Zw=HGJ~RUw`(`Uw-~SKHDXF
+i)qD_`ReUoOZ=DXXHVDFA1}7CEnMQoRc76n!Da3*>KZR<sk#3($BV1Y@}lusZ;$?>Jz~D-@rW0##CXw
+0*(I>tC7$*l<;AY7zNooN?qB7LQdWCWYmFD<kq<tb`!-o#)V1P8seRvO-<Ho7KiO@XV}B7*PQ9%@l^T
+`rb_1iV{nOpHwuhLz*WI@E*mqs)Mac)vZ~J$(&RzNHTqY0Fx$Kra`dx3IPy3JhqCEDU_GK5^pK)1SI+
+y+Qy)`boqQ~yEVsTo2<xHMoKVcpA%{cW&f2!qSAMF;!zHy0N?{0qM(zy&S_DFJ>FZO8pueFQq4jqp@4
+sG$6Yd?tf&d1aK*r5oXo`+nI-L<=?OQ%DNb?mXMV;8Tp?l0P7KXakw*>-dKexw>}*V6KC?~145Cv`u|
+evIIfv|~SM-=Nq(-Qxj9>Yi#eb%KJ$?|81sb4{LW@?0~lx0FhIZIQoXbQh1d@3k)s=%VDJ<f7!FM;0X
+(CH8=Ba6PybF7?z~=hOb9@k!@0xCEEvGPzKaZY+h88zbF%OQBeySfN;<Sjkgw@4MbY@j~%R@k;Rx=Y6
+4MrDUZfa0Z3@LWxR=Mu|p=MhS33y|KrwG)gu~HcB>1HcB>1c1m_ic1m{c)hW>_(J9d>0gMXpq~45O@1
+W$M<Q}L!WE3A19~2)H9~2)H9~2+lFenxjV`7z{MD$bd+T+H|-WN&+C4-Vd$)sdbGARkB3z#k`3ilWd=
+36kM=kCMZj|XS<F`sr9fx+F%GIwJNINQ})5%*#?l;mF60&LN~aIcNMLf;hdxW{78DeiB*wLfj(4`#Md
+#|&&>?e5)xUAuwT+Cx_RgDD(LSEumqf;|jO;YQ0BpY4I)Lw@kE3+#G>dND73Lu~(Q?K!aT&_!qg`32+
+`kiUP;g=g4^-|JZ6cWc|pUFCk5)4%(3?9O5yIfWKhtYPr1jo$~xb@yPycI{!?#i^}fi$>X#sDaTvb^{
+A^>_g4^w12Mw*|jBg1ok0!v-W4Sl9^ZR?qx-N_ZMdN+Hh`k#x@5PlY<4U$FA2opBnaIo5O7tn2^^sxD
++k-p6%D}!CeKm(?w&C!~U^L%HXrkzp$s&OMg-CUQ3>^!uw+@Fa8T7yzzUrWxWlfO=B)DjT-{fplw&B*
+q%Rog>WG|IBWIsgq5I5J|6yy7G2cl>eFcI<q~L>`(LB74uTA(Q^CAlU~fOsU2ofVa@f>8>=$!<<9)Cg
+?UJprZrN{zXSR6QRraNw!M%2m6wLh9%E50N_36T5?SI;XG__Jkv6K12*|4m)upToT=+)g6iZM;Fokbg
+W<AL>m|FKI}Y-q)X)@?)grE%$81{ZqTPId`Q>fIA;&k}lsR$`<s?qI!hk6UG#))HL|C+qx$>ANy$Ww%
+Otnz0P}Yk#vlh|00%`vGaWJu(l@`h#&}Rcj0}rgvL6>ezj`;p0HDk^?pj<HouRH9fL>x;?<&m2_Hg!*
+<sB{TTQ=j6FkvC$Yz$A5eS%F($$;M5Fh-*iW?or5Pv(kMx7RM59L>JnRK{_k->dmZ%k<N)Y+<d`9yxy
+NX%EQnBl$znB*+P`f#Pi#_XiKWx8b--#9t0d8$R{q_cMp)RaV#lmje727_Y*)T!v{<AUZAH#u9E(Z^N
+`z8AiK4xXCtc;cQqOvH`tbG9wMRVc5D9(ylSrIEMVr50FtcbOq^{wNVR942y%BZ7PSsC%b_Jxulw6Ze
+dn(YfWWJP2w_a(4^{z<WRw|Dy``wxn<JYux>h2kuabuW(<hh?L=&|bWj{anq}q;7aLT*>_}D#uvsXns
+Ve)~EgZ#BJ1pqzQXr?clOAjGeA+6Bmw2+KaOWtB>2!8LeF<4P*7K<KS%Im(;;eS%51GFpl8>6#K%Xu>
+e;`h6R|hA9}VG(_e!UEWDM4x5nLG{1S}ozEF|{xcX=;z?B8KvH)YJ_Jxuxqm^Z}VmQF)zEEOPVxDmu_
+$9c-0|#<nD9Pegao4cA1NU-YC~2><Xy8&0f`Ne>Iv9dc?ca6$l7U~+FgXpA)95^$&_VDp@RA2U=3wm~
+I*=@ZgTWj0{-DqIg%W_Gl{4Q4eo4b)Yv8hh%LXnRxMT^~7fRx(HSpNLV*`&3JT~yyz+(fC?Y0j)9@+J
+R%LXnRxNP9E;i)ws+3?gFP;5Z40mTLs8&GU`Y7I}V;i<J7XR-Z~{RbsGCBaI=Q)^(PfsqDA8W?F{q=A
+tJMj9BwsO<|Sz(|WTZn6E6fr;D~N`jS!lh(jU10xNLG%(V@NCP7cjNtPRy!pXmzb}-?Gj1^$H?TsycX
+_+$Msj<W&HdB;FE85%VjLQixiT1y@MteEn6llbZBB6_+OcPWU$U5LtNAgFnL4X9b)&I|BM1~5P;5Z40
+mTLs8&Jfk?F)~z&bqmyklkJ&*??pNl6VdKLP;Rmn4}#jcA(gSVh4)&g8MSK1efG;>`rdKWdA`)AlZQ=
+9_zkP5=eGz2Fv5VP!cG1o~H949Vm97*nwiN$KT2Am+U_%2_!p^>_D;u$qpomXa|Aa(9xq;_k|Kbu>-{
+p6gyDt_j-}rFWG-k5=eF+*?}aX-o8+h;GqM>4ir03>_8E3dS5636gyDt<E-<!{gV9$C4poIk{w8PAlZ
+Rr$82?=*nwgPiXA9+pxA+82Z{uzqu>5|;+J$F*?}Z3)V@#>NOmCEF<TudcFb1CY;}C?4n{f{>0qRTkv
+{Knn~WP+>0kwieqSgFMmiYjV5EbQ4n{f{!KEJqj0`X`z{mh2<M!9*_Dl93gHM7>a+zEx2}WQ&228}jk
+sq#Qt}6qK3@|dl$N(eb_N(XiOZFd>1S<oq49wO5Be<jcLP;<(z{mh21B?tXg6py`lmH{6opGDnFWG-k
+608icGB8^Mj0`X`z{mh21B?tXf`hd$lmH_Gj0`X`#u>M{{gV9$CBe!7D+9ANz{mh21B?tXLbS3k+zX5
+hFoNT>FO&cyBhI)@{E`7y23Q$jWni`j7#Uz>fRO=41{fJ&WPlN9j(wp77#Uz>-0{W4FBxEEfE9wMeW4
+^68DIo&eqSgFMg|xeU}S)i0Y-EkT<qg<j~jkTfRz9%0agNg5?};gY~T(Lc&-5jG$eFjQ-<+5;n^@A)6
+2qjq22{wf?qOlKZe#(!x&&Bz(|0R03$?x`@+4zNPv+5BLPMNj6^%*w(v^=tOQsIuoBpl03!iL0*nM02
+{3}y-xo@NkpLqBMgok$WmzwNNr06AD*;vldlFzIzzA;czEBd31Q-c05?};^Z(k?@M&jll7Jf;9l>jRN
+RswqxU?jjufRO+r0Y(Ch1Q-c05@002NPv;J<BNq~5@0333PH($oZA;lf)P15*sVcmK8T(Nk?+u{3@{R
+4B)~}A@zBCA39u4iCBO<a<h~Rxl}qE&xePABCAmy4lmH_MM)JgkJ7U=52H&<Xlmsh@JxMT<U?jmvf{_
+Fx2}Tl(Bp69Bl3*mkNFE*$;(XT&RuZfvSV`<jf{_Fx2}Tl(Bp69B0(-b8BPGB{f{_Fx+54#%za+s*f|
+Ud-i9LZY*)y1uU?jmvf{_Fx2}Tl(Bp69Bl3*m`aq7h{NwAV&CBaH!PY5*!G{?SB5{x7mNidRNB*936k
+pv?NMl$2nyZw^=2PMHuf|bOcBp69Bl3*mk2yFI%NEvuT14n4!2yH7w2{4lD9yk0F2&jFbBv?u8NrI6C
+BMC+(7@1&Xf{_VE;E48Ra0xES<&0awFPUIvf|UtYCiY~4kqJg77@1&Xf{_VECK!Q;+ZRfJkqJiT@k@4
+qzsC)#cwZ<9RwniYHe*{<N`jFIMkW}UU}S=k2}ULunP6msk$L=*Qt(SASeamDf|ZFqnP7w%dtWFCMkW
+}UU}S=k2}ULunP6msk$L=*Qt(SASb;*^7fNDJCK#DuWP*_iM&R%Eg?oXK2}ULunP6msk$L=*Qt(SASb
+?P&FpB#^Nic%{yDyXkBNL2FFfzf&1S1oSOfWLR$ej1M;g?LXGQr9OD-(M%!N>$76O2qSGQr3MBcwC-B
+K6=>xKu8U%Ne(dU$Vf;0xJuwEbPewBMXcyFtWhN0wW8IEHJXb$O0n^jKF*DW$=t!#V=W4Wr39iRu=XI
+7Ij}J2}a0E44A@!r$6xU2Oj>=!(U(oW_7INmz0WMvcSp$D+{bF?8yQngbe#aNied&$O0n^j4Uv+z{mn
+43yiGemz0WMvcSp$D+{bF?8yQn3ydr<0^z$almsISj4Uv+z{mn43yiGemz0WMvcSp$D+{bF><N_ezEB
+d3EHJXb$O0n^j4Uv+z{mn43yiFJj~jl;0xJuwEU>b$Cku=$FtWhN0wW8IEHJXb2zjSL%4xv*4hXRAmf
+YjE{gVAh;giaxap_zJm*A3ICKpO>j092JzEGl2qEMnx;`k+HhvU26LdinOLdi<WO36yeO36yeO3BK-D
+kUl<DkT~v8YPZjQg%4L>ur>5lx&o2lx&o2lx&pjl<bu3+^bWfQ=(I%Q=(Ji_$6hB<GbEY$wA3M$wA3M
+$wA3M$wA3M$-%t_C4v$`iJ(MK;`k-ooIQ5ELCK(GP%<bPluSw{C6kg#$>d&1iKIkQA}NuSIDSdl;rOn
+1QgTvqQgTvqQgTvqQgTvqQgU&xMTtd;MTtd;MTvEf+hW`nB^M>Z3N&QEa|N7MKxYMXR**#tIL;u87G%
+)^PC6hdgDhH<+b`L1@>7sV3leFSp{Vp*$4|XZJ`{?U5U&(-53t{umj>Ywq~3zm+XeMd%DukrIQgkV`&
+698iVxDTB0=_~vG85Etqu0@A~q=Ctl^b4g5=7SY!FWsKB||KcPcwhe!5sjD#oW0vtG~!mAIi2z+c2j#
+Jew;ocq=rH<j@7GH+^z#Aq9Ltq`hM@AgYB{M1S?S#Wv^Tv;KKYtBkiYJ~vdVx=oGPDEyfUb+ZjFCxjp
+GgMdv>)n1y!Q@m})Qg3uK)qfNY=wDrVU;d??TSYQ)3jT;>uoS@)eQMXY98*bGEJ;^`y~ZGs<31>@|y+
+A-Ee6ek?MsXUT~%^7T}8|zTxX$Fpn2sRD6UA6;|$icNyeySiB0!$P4GI5Yq$+tc#er4BJ715JOW5gD-
+rsN-SKlq7`DTvM_W=KkMCoNyC6t=0JgAE-VlgrZUKXHKtmJAFNQs1unE;Pn#({;?j$><V7TRVREgvsZ
+#(7yN*f(Uf^;XcIqNvyl6*ZK`U6yg7qjYZG})F%0V)@JE`)+gYVnb-486z!<--W!YEc~{|dwkxz|GUT
+rq+TKkg#tsF=a(!tSaT=28#;yYV)k8U#>KWrYVX%<+OdTES%@o-eG?7Yk3pA*`_P7p`n2cqzCp7a>4d
+H_o=tMPAP*pwXT0hVyuktpse79PJ=Ch>8H7qOd$)4Cu8?dyrp+^{6<G)Ene+2)V1-RXDrCVpLct3!!O
+{QLpeu<ze=STjQ2@BHC&auK6?pZf?7?G4)i*5Lc{efiAp=LMjR40=renMi+avns9rVgK_qnr(>=%lL$
+I%KY6m<BJw!oKFO1nuGRq-^I^7Wx1$boM&~wxCW21JJ`l`ac>mXs(X6JEkA2ur1@8&`3-~4V1!+=E2@
+^0DmeB&eS%wK%29F@85X2RESMnzp^kXgeyx4K_whx#&n9w$yWlk%NO2xcXXH)@&pmva57A?nIVLkXUJ
+ov?|vEJ>MTr{Dg3ApgF;F16lCWm39FWbY6Iu((MXijG0^NQW8nCMcqZ0*dqKwkpGdf8NB_Y2poLI+fO
+^)l4~K~l+JHhl#I7Z-V%YGMJ`tPnTVcE<rbPR6b?WTb0gM+^RXA>UH^5x@W}ycV*h0RRf|CVhw?z{xD
+^CJgr(w>?Ros+nA~{U{NxIN-!S&~lOpwYvK4rT8Mx(8y<zW80lp=%t5?Aq06p_}y{xQ-#mO6J~mo17=
+#0)VknK3r<8W67Niy6zq$HRC8GZVtf8n2!V@ahxKm1qyof(H(LmUFK$y2ArzLo$~ad-rUr>s@fsSwSc
+R(YacF!DEw+31yC1e+QlY;(<WX@gA*odu>;kz_A-NlmTZu!OMvZZ+cx4@Tq7beZR+tX^WWC!jDWR>yY
+(Z5N*yoFQ<RU_=<RJ@b-%5aR!9W%=zJ-{%sO%zyE%^FZyZzLOz67VLsz__DbPtxS{q!BTrb0V4vKxg=
+O@W>-D0BMpcQ9a(q=j@`apqAk`Vy2kb=Vj+Vb}*^;ffh(-Sy&34uD1OmoU8Haa^e!Lq*8R0-tf4=u3t
+If`z%?^Q}Tbm*Z_=zFDX*5;uj^Y9;Ym;lVpemy480VdZG#1KQ*06!z-QxY4UDEf=}OOFaxrMIBBitQd
+sH1@&-2X<t)=MCJ#=xuQcUWc@BkDeJ{AA(So<O9fp}3B97D*O>G#wthHqaqqgzxq=I=-S#tFuMP_HtQ
+R{@-gceNhJ@%%d!l5IL@>)U_udF)?9otE#|@^kK)Dtd5;)d^w_C^2_wDiM=mxP5M4)g=m3(Z0Dyhd;c
+{p(y&<C!-9`@E;!4ccIC=)zY=<5q6&3d<AQV1|B*}oRY*R)p`v%x8`zreRCaXdBVPwH&!drc>&BB|z_
+y6Z(>qJ{xJs(_>mO0HlmFLDaSX^o}0bRyJhLNGQR7O3Y!pjH@+C^vq$NAXm!>&?Dg$4jS0h+^QpFS0^
+~)JcJVE=9;N%gIy`_+SgMop_2+-`2bRlEQl2;K2*Ruudwrk`KLDMlZ5m4WM*b=T=UYe5;=t7DI}Npyk
+#v?T+cpW3uhcW3oaGTX2ys3Pg4VhU0>xuP{?(YCw4q9O?2<zoY!~p<=@R-6OfzH~Od&fLw5*751*2K*
+i6x<Aw*u$_NoxNVRmb%awTdYT9W=WFDQu_D_fdP<a{;p;GJE=K64L()sf8z_RhU0<i1PCeEj^L5e-xj
+`cY8qI)H&)9@&oQAJaPh7*CX2T2kwVo}st1<9m8D)G$)WpuIn)iCTk`EFFwPt`f=+MwD=(VPpgUMF00
+AvNDvH;c+Hz+Om!wt9RT8r?Oj?bM6zl~}Z4v@66w;hu$@Q6=)MFg>NK-R&$I2>M9ZSMqwHq6!E0f|9E
+(?*83=NxgrMp%G#{Ga+OlX1?J03$#<?4i$&2!G&JBvdAeb6vf4QRqNyK-|d&QX`n(x2^n4tc5pB%dX1
+txYD@*ySV@=^e7wR8y)^F7e-#3*f<5X*_u8Of3S@Lq4oeQYz$O%Z1*8uZzPJ~C1uS7d76kxkouM>Xo6
+0ymEn&p=cfc93ej{;Sm=e_}6Z+yJ0xsk)D$2Er+@!#bT-HmhS}>&-8iQ+|^`fGCZOq=wh|V>VdscLYr
+75*^_JY-+`R<r929E^gwY;!yr|PMB*BjW$RO-n*o~|?xx{;|_@Yhd}frSNKaUvsJDCX>i<N!rVp=_yV
+y{PD3FSy=fm;_%^@<cZsCkgYy1sOmQQ)JyL3XcoQ6+r?%7;7V|iK;N_-F``J%By%>;|tUuwHoo*jRa@
+yFaZnHviblgR`-Icu#yVCbQJRN%2nMfDW}R}R8j5JA!&kNFb&-vdUk;kBp4t(L50)9`{NWKTPTXvbfu
+|x`z45NfD;{R=jmJ{Cr3zemGd<C#1Df3WMdmGtvJE3n25hfJ<{g~zvy1u@js0tu_9r}g6@fbU9NBaSY
+6f-jgK7<3SgoOZp!6ait@;Mw_nmBkqRqmK`B*1@&%hvS@tW&y~ZJ9urb&t<4y8N*Kp4O9zJQ@%Hm?Z+
+b^kFEK&yri9&VV!jFLk!^~>7+|NMY$R$k&8KobD878`tUaR%^lMDd<8AH}xFK9UiPi5dbu7e<k3WIQ%
+$IB{s;*cgo50p?0tDm>sLHAl&M2m7`2tmXa%voj>Q-YLqA-bwKSe*$|(RT-9*_63Wc`Hl<*+lEbFVP9
+aZEGZd3f)-4Fj%qf!lcs~I8JRC=qsdC@FpGd(0A1bIygi3dI6`pq`#!(nyi^S8{B_k7_7y@5h08~v2a
+1lcP#v6Fd{t1y>1@eD^E`{vccd~4MNrFz_LAza+##?>sK1G;6z~h>%#yHdKZ0asb{|k>jpYqED_NaK6
+E9Q*-*w@C>E~JLC1^4Sqxb|mlRZQg$}K_I@dhD{+Y+<UMusWYDn8r;zBVoT`{R8o%b|G7Ay_#(RSkRw
+8PvGUAcw;XX-`wT3r+&0Seis=E?{MR}2}@1r;(n1!c+=k`>D}gyDuBLoiex;_>)V$akMA3ckYnR&bX(
+v~6dC)vl<PDp`9QOKU*9RHwC^EQ#n204pl83)kOzw_nl>{|Kd9brENNk`^o^ZlH*H8rdIzA}ympnmBK
+%-ggC;yt>=F{gQ$f>B1{(51(}DRCX={Zx2O3f_Zlqko9Y1H8C#qK5mM!UzZ*`^9|i=MTT_o(}L?;Nuk
+$t-WzA7y$=bX^<bsLudeVwm5#5FwS|oDWfQ4azQm|vxfr=*;YzX|Ba$j^Q&JdApnVziLT{Fb^M~3*GC
+HP<RJ-*q);C-Y{rjS>SR+V=1oyI5V_Pmwsz$m+2S+kU(hvFnJS^C0?>Kx3F5*L1u2To$lv5KD3*x;Tv
+Q*Xscl2<aLbnPJoGDi3$B6?fy4O<GuhXIEY--U<c83xH7(8uN3XbtXI>L~_<#+j#MQz?jm+1IA=w2&Y
+^$VSDQD#6^9wi6QN0&`mGu<!)VyQklsKCxvV|+V}S-b9fgE+t`(Q$4uc-@U*MXFfOokate57Amtt=-S
+QGwaoX#RqdW`>DO#FDZBwjqd5FO$&@yA^mbqZH7TARv#y+42vJ$h$+bvLNr_`7OsBsrWM_5nXYe|K6T
+!1IICDagz%X8=FTp*S(N2wwZ={mO&;E{!Ss^pIeFfS?)7R0>-c>r5lF7Hc=A)01;U;(Rf8_$<ALJ_P&
+C^>%;g!mY%Mn)(7hJaf(7=ubmLQ7i8EtFK;rRY;klM%Of_)TI!j-(c)L$c1A2Hwmcl1QF)$){<TOiqw
+YwGOgxtijxDMEzYCZs~RGE3DIq7xM9?q51zvx~I9o5?j`rv<w^(BCih&A|<;>5MRI9<gV$}GExf(kb4
+VwGx!)zrKFk_(|qohRwyP6!<rq7@#kpA8q-f+fUYhaZx2fPHDrof3lh;0)bsT|%tMQn;ZHA9|W)!M&t
+eC`v1rlID@L)5{@Cyb<?O;-uYvHM-ZUO2?jF<ymx>iXI~^!ahv5wh&nld0Gs<8z@MGROIXzad)Y=oka
+IqApsiXc_Wp785O%LY8kUOJCq1wxIYUqf79<|onP+Wa#WeMw_lC!wYlp|t1#S@!zfhg7I#KEpyas9;n
+#6VcGos$O-X%RVi~MmK@noT+b^lZfXP4*qdkqWB{iY-iU7c)>Ly|WdQ&(X6g^}(Knr)c`M#R^CGaqk5
+H6Gi$rnn|izT*T;V($d+RxAuoOZntmIKxyY1lkF?Cx>feo3%B@WS=HPW}Lg6JDVqMQlhBi|HlAInCAQ
+V$oP8@(2~^)k5@B`^gtpbgxB*0gK$yG^V_q=KBF1A!%Y{f&n$f(e?Y-`bP4rObyb2<hgkbbgxyq14Ud
+nz=R|$%FfYY6m%;U4y8PtjnceQY-a%mD7v?hQtigWbf2*6#V}RJqrvDEC)fcSPa_LOV~Dfh-B>8`p3d
+q6#0BNq*HO{EUfrF`^h|T6vP;jD)0Dvpryq+9L%`H2Mn+ZBO9?kdJF&=IT5qub?Zl$^C3yd`hYttJXg
+f5K(x%urR_l98xLV2>$Z!)q-|-u|*A|92$N3@zfJ>Qf##V3NC4BLeFi2;hj577LX)5Bn0{2!PH*bXQw
+W<5Zkd|tcxuje4fv`L7p`*`9k#&~^7|`Num`V;H(huiz$Fb;MNk(F)92tIkXeYL7fjC`0G4e3xaFZC3
+PMT75@x~p;5xeTyk6zKe7PCm^GTFaVFs{{*1y>o)iiHO{mMXQdWTUTXC`J%x_U1Sn<JezxuSG8dx1v;
+YQV*MeCqdWB8?mYuwo6qa0v;18isvG3;nr2D@~v~M=w3_8S``z)q&jrv3<Pl?O>H89F3=(s-F8=wTWy
+Z{%3;)~?|jA$-RosmMib@Ovqly3-DjFO>@0Ffc`qYiOh~yC{rJhSmoQk@IQgN9?)5@fR$PzyS4CkKl8
+GmurJ0A6V)J0Z6Ru%EeDc@WUsfddL-rHLHgvBw+|FJMh4f?i0aVa55rX^(2EU?aW5*m5!2pvmhD?g=P
+o8awk5eyv33=1v2Ib~nRPcj3tP-u1IQ8N{CJxq#wpp!#7`tbiDFc!XRjWk3@Fm2I#UVahdF_Xx905~p
+HpFBg69LD+;f6Jwv@)eb?CD`Fb+?y14nn<aoE(G?MAs@~d6<xriC~q0{RtED(|j7vr*Re!?6YRM>%}i
+2h0@?{plw+w85z_2?CuHGHFme#7@G<fvzlO9;vHwhtwUqp^}?4dv#|7KJrWz>jAMw7|8fq(i}iBSjc6
+iF$YWWT$uDO#rpeg$BLX_(0M<7%@VG{1)a57fK`vA+RXr7g7L<E%b)Maz4umPp%rjGuK*W04CtUR~3D
+_`O<!m~|G&B<h*skW&xKL|KX)1il<u-EjPAp{2O~}+s9tWdcki!-hG-4#yEx3l^QV}4DDIA(NQXG0-%
+9I#Ygi{6np*tn17rrFVi4-jH!)!lvi^GE<*Vb!tT%b-yy+{sn3B3#a7dh;sM<grr)FnDy6wSYxL3N?(
+W{u$otBg$3MJ1yn+4fNB!ZV~9YQoQTw->&IgzZIIu(JKKt69Q_Z<;s67zKOMJnn)Ozu_wsD{qowM$c2
+(!s%ahuSFl4lMRAPg+r)CMjX`KSs~J$)xgS0WW5+a)<JZ)t(tl1cfICuLM{+)LI$$QbgSBd_nf->EO$
+Hu+0tsxMt*5ZnxB4PtvPWayW}<=w3Ei^#}C)w>Is{xmTobb<3@27K9BJTX9#P*BRJ*sPXD5Nt>LtSdX
+SV?vzd+zvABgK0;_nGSBANMxP0Jj^dlgvI1zVy@k`hvc<Bov?W^Q;3(s8GXi$p2f?KM)o<1ISdhUWFh
+>IEuR2=Dc>fL@xn7?HeY-s5&My3j9jW%p+V^3OT8Lduhe6docV7h(IB1=!#<5zPU3URmGgq+f%kRz_G
+)f2@CA7)c=8-l{4k#X^+<IiUVJ|_+!xZiR2G{>(~>FfdO)#aQG#4=8j_rt`aA~d`wmmqLPXB0y*6*tt
+2zJ#3{wVB=@L|0~V@l!dYqvH5?*IslTy-5ON8esbC5`gNRobN|Z+t9r>vXxcdQ9qH8tYMCWdYecZdI-
+dp<}ct8iy=SBhHI%W!Gx3(2Q+lA7Ylo}<ZvsxiD-bRY2Xeu6hL-6gOCslPrB;rCkI3diPLMwITwXPWo
+WNVvumFmjh-5&ntp)i@8!@#u(+Yx%T<^XF1PB*hSuQj2lx^a;+N?&!oRCqsF;4y1-B##fU2|VV#xU7_
+h}2)$ys@b$?;3jy<W_>f?l|w8R{%v^>L_pGb&76Ge86x&7jv@lx`K#E2+%_U)}2QqZ+!`Ldk*$){J;m
+v3jV1Iym})eV+SCnO4M)mwXt`bsNX{kn)!j9P`wR?$tDL>I8QQApV7YF!CEFxZyRLygim;x}Dwwn240
+62qgXT_Dj&cws|}X0B+>DB&czE7$ZWbG7P7x#3A$r>+8euH6MU!K-yjNp2z53FY|BgyDdwn$A}%Qg7@
+YTL`WGfRA;5TCEvUZ>y<5GS3)VK9d5gh?zNgcM3_(&%bKNQ_uZvR*nZ<Igrc;NodV5V)Whf%t02)}p0
+}SktEqcEENWsTR8@zQ)YCoW`*idaDKV|NIA=WyWGVD&($}{4^aHxr-efFO8TA6XSY3Hf^2adq6~eiag
+$P*c&ck)w{D6F|*!AX$IL~=Zz~reGPoqjl9LI%vLnld09sB}LOggF6wv>E9Y<cz*TwV1Ps<_uTbg$(s
+>`811g99DW(Cn8y&KI7rCta{!DAb1L2t`C@-Q*;Yrnbk43mdxELayLas*G;>Qc@f`9TV=r#9VjLaFtL
+KVa9j&(v+SG=PCip?Wdx9EpTO5YS%GfN)(u+Eu9Tm*slC%3St6sl=o`VWO1t!U14|QLEWpwx30Z3q`V
+Dkw&sx?vcBwms>6eM!v5lsj{M2sUuKo0>^<Mmy%x5G)M<ce_Qot>XmGLhM_{k=5+7c9f#>85lY2_8m;
+lw3_+2k~oaIDM`v~Yr05y~Uj2qB_JZe~lW^f9p)s{3j+c7-IjU9ny-18XSYgsDzG%q^51~x8t7+hBC4
+xQF;FE2RR!a7*bILQ8JyH$Tb?*83=Nt?&@b+nkXE^V$ale3Acqy?5RRn)^K1NQX9sji*&NGdtugEMun
+RSHiZj}ukEdsRM+bf&9`Q0^z|;qDB=+TNttoa*uf3Iy-<7~ShNoX0p$I_QW6>NtvM7j_8`O%rpaSsQ#
+`mBPgIn;W(J?)XC8>ypO7Y{4E@gDAvAWD9_Td3OnFB$;f&5YyzKJ2)@Oc1`H6lYeXKUb|-i4s8L*kWi
+mS70d>FAjO>Jr10I4K#*+0TDgF?F?~7D`iAbcz{Iwt9z_tT>;mfe)eS1M)6^@~zEm~%#6BU|2oU6OsB
+#>=QA787O~qwM8a&U7Ai2WPSNNR@4|6ReBW1Aqf<rUJpHkW~Q|y~5M-S7`y%y5d?HDElc<4%nDmds}9
+Z1)d+JlljCspVPKTZ=DPx7EAuh-DMUehs^%0#*0iDl$c+hI%r_SF5*%~i(7)*&d&0v_9Nt{}8+M{m^B
+y~>TKAkm9+kBo=lAfdlJ?#MWjU1aT{K+pa<vw?+V*!!+mr@d2siQ)A_rcmm^4KS=6^av|hHQLsnC3LH
+(aN0Z~9;Tn>7?1AWr`D$XakOHr19{F_vibNsDndeINw$E{obzewEz*J+t2**ZFF?yZ-_X5Y%D!(&5VY
+y>*PXcfm~IV#$e5n01efHrZZ;Zog)HPDztiu2NcEGlF92Y=zSw+R2YZ^Qf!-|2<R1q&xB}BelVOaq1;
+^pc<0)S<mD1|y(bQ#HTd)y(VIS~VtE?RXd_CHMrB!fB5~%5kA6-R5_gWvSX!*{((@BMz)13-pF+RgVa
+gBD5fu1k`FyFF_TRZ{s%;TkBqHC{)O%1%Ky%75l4Ah6?7J8e8HnNvEZY&>=8%+hS=Ut7HcVdeKJPeVz
+p+AYgu4GU-O*Pj?)aE7#^XSc^;!Kl3#-K&;peBVGl5NzhUvicwC^>$mY&H)GthfyJNAn#H8IA>j9i}I
+ky(oD~$~N5g4BacyERUvsM<-aBg~D8;ZMR6sd#XajARQW%AQ_b@)31f8;B~hbzXZOoz|1$-A)`mRJwq
+0^BI*S+*P$YRr=nF4ltc^)EM`|oD1C(VkbP7bLG?yzFvFnU6cwzb={uAK7o=3dIVtXS(^+07Vyd}Rna
+XzR#V?_bYQ|G-1dc#EEGm5ys^rXku<$5{z*00K)E@RZo&@pcHP&74_Diap$eY$&rH5;}QhV1tQJ|*}b
+HWCvS|yi-or*joGb*@sHeKB-SZ$sycv#<uD+fn}5t5OQEk|mkH*Z49Y0^z2xLr#<I`BCDPDl4z?0Y1n
+4eeid%a_6n68F_DZDvrF*H$yL)(c5mO!g8Vqeg1&@B|&*YeAAzh^az#nAvMc<IBA_RqjhJ{~_}6TBfe
+AG>2L&A*P$XPQ43WSvH}Xl~JFniq<WQ`cc^WrRvNFv4M=Lz&37DEzbP8^JGZ-w*6I@Aa&*j5lPsiYG!
+#fk>?uXmcB5J%9@^%z-j?-U0{*N(Z$o3>~C?W6pONcDr+ySS(WZqo4k%|rswWagW}9QI+F<Y5K^Jt`^
+kDSk8_egSb*xn?(vm^6OyodLzV@#=djsb-k#*`P!(Xk1z+&Q-TmlZ8E2>3JaZPqAErk!60V?f?r!-qH
+VD;I#22B;6QFwV=sEwa7xg8|rd;QV2HUZJ?oPlKuzN~f`n4P_jT-*9QLa)38dhW~``upn5>NO&f`fEn
+l_JGERT~nzjdC*(fvQJgXK_QzP$+X416Y@%f9U95n`tY~>nX0nrX}4u2N&86Mc|>UO%)GnwB25zU+*l
+_MXqtzyZw?XX;D=#84Jr-<Fu2FZbMU++AbMhLZPBc4&%Pui0X;cjQPpGb#$-QV4VbLSKp_<gW|9mV!8
+i8rSs!(z3!{?#B1Fj$DIDs%UXB6_$BP~sf3#KY>Y@H4FUlCFh(iu#p-ubCoGgCBU^Ewk;zE+Q5{`;_q
+<mu{Y3!ZmG`TiMbT8)(^FY}Y8sL%%Y(cp(ce_y>is*D%Axa)-SNCvBqPnOQ8LogFh(l=#OWj+P0wn(L
+PZltgUetU6v;U?=jy2!-D^>MXpRz-Q;s{q)9Z4nRrgd+rKxy!MFk^7T$O4hS}6dk-RIe`=VZq%EP$q~
+>xrM#LP-bU*(va>4kHN`vpm&KBlti1d5M1q{@~2>UeRO~$kM6;mcX^?4d|kpo@M_~!HbO|T^QaFXXE5
+14dM*Q?|wjE!kXIPayZ_RWx?gf=mzDH>!usJxitc^qHo2P)TZnwl+Vegx4Yh$*Y(lsm+wD##N|hy{pI
+=Hk01Wb%K-lL{QB*`^PY&eyx8D+_2$F#yXXBo-mvh^^M~iJJ(vFbAKrcc_WkoyRmcvn*3ylGWE}_`im
+FS`UMSiZjv#<22wb(mi-=+<wTM~N1Fw6K6sd3o0iHnMvIRH|1RH?`3-aZiH7E#3f{b5~atL}OATfe9K
+A1&OZzF?vHb|%kQCBedfj&FP&;^80m@LNW4>ATpARFxJ4Md}X3^Ak;*j5<#roWDOHdv>F)M&u52aZnw
+_+ZEbF-NdcH4q*M?1Dg`u{%FlM1l-qK#7M(B2({nZUTb4<Cz8_S}1oScN=6=;+#e5y*R|8s-y_+iaV_
+t9m=}Z^5$J{CyB?N|78y%7AHOkobK>^dRV`}V+ddqRTYg(sBCH22!BV0@El&#5zqF+V~4fDxHg+l;Eh
+{$b~SJs2C~$K&a5>zO#xO2kU$g`)e6pG8XfU$a|6UW6synd-FaqC*{IT(F6!W{8tG3v!=HF-oDflUk~
+SUjY#UOc<WR7#K}M!COQLG;;kc%VLoHxhbGU7ZBmo;%I{NUan(QOO?B~$f>F?=5pMF{g?MS<Ya6t-`Q
+v!%BqIz6E$BR^}v;vErO)?$vY=aN&?$0*Kj?oVfIe@}xLyS!8@HXfnWV?hI3h5%wFka=5$Q|))pvegq
+lh%Y2^Aj;rkiqXw04|LTJqX$0@!<%n8uUcqNeBC%qCM_<gCLN8L-G>74}!&^;mt&I3X`;fTL}_tK^Cm
+lBa`+sOgCAo;kMj+ULc+gi1UshC7@UX9x$4Us68cuC?oKsJMw^*CvHzZ8(eT8rr6^)M;zA?&qi~1L-%
+7UH~0WkP-?Bq@X=MefNbLmSxV24IpT9h#GPB~1g*TpV%Ll9fG!Ew7W}~=EbgADfo~Rga2*|1Cm$EhUc
+#6-BFzPs`L^z04e@L=Cjuk01ePs#BjB_)I>QHV{)<)u1vtdcZ0OQ_hA?+h0U7_U7m+K`Z#1h!U62l=E
+hj+_9Tx7}h3x3*TRwb{ZoS-fSoz)F!MI81rr%Y*!}E4qHPDsRBE8Vb(zN3ELIpgmhz)QscF|4*boVdL
+4H@)a6ob0pD<>P;7=&#nOCvX_GM8si2<?Lpp5`2j025d5dU0;B1@(mZr&npTFSfu8>C8iQSIj_z0_`w
+?frH;5sXDrs=1CUqkR$Zl?gvf}!f$N&#ewG(coU)d#yWQsBV+)&9s^c4G_Q)Iv7Le0?0F99=!$1)R$%
+oS7B)0s&BsmubfL!hOj~Xs%fC!RINY$|kixCq_6+fCSBp1o0!DDUrAdC%Q*a(0>_e1{!=Y0!Rjy*dbp
+LVu0{r$o<a9?o+ws|37g&NKog6#^#{I5b57}^Ah@(1!>NfdmuEn>`w08FcA{7;19Kg_;1eT~_jC>;k>
+q|>#1LQb@f=;m56?7S-3^_q;R#|s@@k^Qy#QSPU9EPDuI`1*sP=C=hYL2RH)9SLc694toG+_1b!-+fM
+**1M&!ti5CYX6ph(&x)n)jzG=Z+GPwq!7doo*miGp>G}WY*+W<=@n?^0wHCn2Q&RgZYa81VSGdd<oNq
+RtQ&+&fwvspp!Hsd5YKk9l0mo`XyZGve<%ZYS1-YEc+`ed4b^=Z48!#WJY5jGML&Ih<^|%}K&}y8f14
+RX2;9ip1-qUC?LnaDiEfgB?uK~m>9=l?$Iy|Fi{9>j&@Yi8o_<H8napTI1J^mafbABda&O2&iFUJvwG
+q}VAG6=}B2qyC-4HN_`V?kw2L+9dtbsJ8tD7}3(N4XrFvH3*7e10dzR3~Av-QkM8Z$&KP0;!BMIfdGF
+}NCXT<_?LLu|hK2@pi2-5z(nh-Yh&a#!?>7DY%FeKD9>l^@8kJ3LZb=U)5^5T`w@yS6;I<M>gCXFDnT
+-V{m!Q^moEh#M?cL(vi4&g1Ab2~Ftl)-_;_wT7jb*rt`A>Sa^U5(O9<w>0TlVzI*6K*wXBxM0JZwRs0
+dmCDOGz6DU;BoX4-U>8kv1z7rcEOID6`T-jrHp%<&AOS+~AQ%tA#?a9;c!4JG+3)_{eo1%Eo&#lU!n8
+^xQ-mjMPOhlEd$IW#3HP%juZfd9!vRR4cn8CXXQK;Um3np*F%?RRfP9ZGJREWFzcjs8<Y%-5!vSRw{T
+#kO5YL9Yte0mmbfK^x5;`u}+}TO71v_8@_OrXu9DXQRqeI>R0y;REEKF5VFMbKCz(6_?NXG)=F(6`sN
+H@@A1~U6#*HetM4q@EsG%`&AZyV%^dmi#>AfD~+N}<O)HnIDD9i?MUSu=)1_K_`aJ+<rl8F0Ct?B{lM
+GiW95dhtufDM<r4<tODP(e{xl@8t4>$h@Hk3nZFdUyvktkaz7Qx5Hg7<A~sS1sR9p@VFQnv`pcSSk*)
+jF@YQ>Ncsi2x`0>?Na1j=sj3+EYlV@~ICh6uWbO7cl6sNDLFu;0LeyawU}MOpclT9woitn>R|(SRF?`
+@7PUaO^dmif{;9CrD?Bik;Apcv~UZ(KyEh@Nct`{DHGSt|Z{=|gb>kx0p+U>=`@z4}DRrY@T2gvkch_
+(34if9p&V>q`5s698t^RXZ9aYH=Y(GHR}-oNstM~;W1<-eHGu6c*b!i$Al4?Ma~7Pq34hc9JD$EjDp#
+P$+wK@|p^T(Fm`GRrFDPdoj4zU5FU0gyfjV@U?$p`7E8^t>2r_k*4aYsv5p2TOD8?+IR(PDfA&&!D2v
+U0v+qQUk(j-N+jDeh%B=rBc)j-ME~`RK)%@W6+_mlGaB55)HZu$<-aTSfHHhuJcMw))4{mDxh62yEaG
++qut>LFs|;}NdL4Dy+pW?j+=;I`q~aMh;!!O{eXD3b@v)+v^6;dD(5Ub{-u&a?}AiZJBJYB?}SKZ@Xi
+T?u&?*#yrFm&Bi{)4(f;Da;cdB-4HpV7spF;uo3q<VG*tg&@dRD&Igl}k$6c?7vteW0M9hAg%8rLX6%
+tBb+;!iz+@~blj`6=Cn;X<a6?>_TJBJ+b4wt9qG*o<j0u@jTc^ObPfo`gi#EY;q7<SgHe(vw<G!#fJN
+AS!G#Is$OOHBbZYBPu<h1idz9%q%@TSc4^Fz*3t9Z<0yPpy-*4=B*~_^DnFjScN2_tbWT*3nHhip{#U
+;mS_4r*x3UlEcG10_<Ua3G%2Mf0B2&&<{Kx{*LFDO}mk(2`8=NCx&hqt6x{s+m)Gm7-IFuR*4UG*3IF
+ev7vLo>=kU%ZyqWlf9s(`aEncDk**4lQDC!Bgef)*E!%)14*xI|&+@B4tI%)`ngS($PYRVUoQQOgEH}
+*T^z}fbrOr7B55HweiM!tIm$ZIvEZF=314}1+S}&=sAgm2Vbi*Ix6%r|TM8Odbi@kaL9p2$WlGL9H7$
+?G@N}dV}>jA)VTvU%a;R`&k5X8D)kXLAI;y!$TcxWtX^dLJMWWgdFx3Do>lvof9gmUe}(9y}M0d*};?
+oXr7=Dgvdu_64@S!q*6mlbhCAKb^aS;W{{T}Z!>$&;7E0=9b3W5lyfs#rEI^pB<}@H2p7cuz=}=O=z)
+y-ZwOoj4cQn&a;vo(=QL2mY>aLK24zLMJ42U}-`|<`6Hqe6TN}s0aGG8ph<~?gzxPP#Pie;>Hor9+NV
+cb1@Vm4hW}Oy3xm@70yri7Do)`q~_sEc)7|`Kz9U0MjQ@fieLYvUYH94tANdDdI69SaME1--Q>a@&L(
+kQuCnWeCc`TWBKz)EeGaD#bFm73!qu7}nFuWih$D5nNXTNJ;`GBw+VFA}>ZMBDD|4Z#&`=2p)V47lkx
+86b!f3@YnwH=;=O=W_NrDW;vj&2YSRpOK>e@Z}+AxA(pnC{5E@z|;8IBNZZ#So*OQM^dM+DEyRd#zFw
+==!{7o-%k_w<ebVjkn22Ht7te45%fa=`EF9=)C8Zm)jH^7;I!Mg?!y{>4ec8wwnuZdj`mgVzwO_`&kr
+Fiqi_dncL3IW%_qK>psQ(l-1k!y|nrHww@5Ex&W1n5L<lbQ&gj?s1#0Z#XV+$VS#0eoueV!eX&HspLo
+<92@}LOE?ieql6<k9m_K>5YGlPwQ`6~FkOS`+6j3(+Uo9U<!6F1t&QhFVfO@cuor+W07~THcX+wVuGc
+Q$S=@qpdYUQhGkEs-X}*odl+n~C<%zJP8PnN_`z4;dTxI*!)IpWqqtp3m@PqDTo|<DOqQ|LAoxupT>h
+T4@^90jarTZlhyj<le@SH;*R=(mmM5^ftk033YnGEg_ZW->r;ns5#(z3MV-1^`F@oY?0(cupWCczIIV
+mfUL6m@|bqd#;p06Pi4;MkN-Q~Sej&u90z!Iw-o6NVpCCGNyS=b-y1?gU-yMwF@R0ANX)$2pvH+0*>w
+j<XQY2HL?Ohun}7wuhp-;ja)568@=LgOLu@dwuGp5g^cg%{Q=iALQ_!yj+EP?M5EF_)~AAh=G;ak!k*
+m27<Gx7vO29%n*3mu92EY1kcM=cD<MlScgtecQsFXr)E0c%;!n@uG^A>a72wTs<DS44k(1(?f)X4?Tk
+^tJ$2}$KHWyZ%!6@i1YiOC6H{>$vmCl7+;Sr$uUbJ`zW>x-@;LZMy`PdnD=p-`u*59X91<6}>Y)Y%c!
+W-{4(#;S1|DaV7cW=AkD?Bo2<&7(APPU@`2=A4G~CeHa6Y3-;s6jA-v8co&C6AG|6*uBUauk_Ys1k!F
+@1qH;c_+38h%SR+TDaPQ;y6M*qz56A0wXa`VVYqZH6b)+3u{I4x{zbK<o~Q=tfQ>P$f1q83eek+P}B|
+i+Hw%UF$pZa2;T^o5j-xvY_E%cNo3UcV+4X!C`0yQ40rRl3wq)5b<nG%?lO_iB4yN1$!1_i8$cAG*-;
+F<$)3D3?2Mw|4#nZx!moAFL5Wan^D8p6Em$4iBurjh~2}T_=fGiN&p>qAeV{dU9mfE16t*-cl#yX_dz
+UA4j7IgdhX#s5&sXr<*vhu=x(zyxWfP?5H5CS{{51Y;i0iCN`aUolvAXy4Pf1J=g0EEOm5?rCXER>kd
+8&^DqZ8Grs$<}m>e%x+4HR#h0t5JylS)?qs4=Fe@{*$ygwc94Y>_8X!xQ-klFh=l#G|FJXOy*)0bx-R
+`_y*<!zdBC;G-5S92IK-x^C?bqrWn<FJ$*-255h+3p)#VNtu54O7+?K`c8+aWd9JStwG~F58McEjY0x
+IQ^?@=+@0gA)f7?3P*mtOUxo|isf-64!Z`l`^@wbwoCn*s9l0bbLAb*<~&}mLcIdOw@T6kCeyN8m={Z
+AU@kl*JVlpdj~Q}rR#g@y#P9eG@oc~VHaBw07A^aYB?xjOmUasI*r;4C33YEnSKqP8ExnC4>-g27coy
+?QCWDI$pp~mlb-BUN*uUaeP#{c52N^@B1ZDZGCm!PED!aYu4`?7}mXv$fkdtK&Qo=!YEJ*u?IZ{%UO|
+Q@lqnS83;noko!^>6h`zVh055wHxQevIYs1hd$?muAb0}e3Y?_xP__%ucukSl#2yUxp1c6(XGr6o9_J
+&B6*VVtwaD2Y6iptB>kNy5K0#FMb&6p<uyXs~v9Z}JKcjfD~EzM%u=E%D9M4WAAVac>LfF<Xi7Hk+im
+b{$PmmmtEf_xt5Cyj*3EoB7-GG7%!x<rr}X6VZDt^Pz7Qj-Yf8ou(yLv9LQi@elJ+?ta*QNqX?0>0B2
+@_{qe_OFS_4rSLCr!$kC;^7AJ*tbD6;l=R5)qj<T>whsWH+dY*VS9-8guVg{<GtixOGM(lt=9~Yr7(I
+86ms+P@^d&54ot^vnFa|tqE<Yvga{H)6D{EfTgPl!1d0t9^3Wp_#^PVZ&PrdLZNMX8!pjkq)7QOlghl
+lsFHjms_V&iT63BZ!~WGrPsXWaF|mw0Fv<XBA)f5rtran2CVOhn~(r%D=V@1I7~(}aD)oc2B65YIMMJ
+CHHy;b;%tKoQp&6n(r#w{-fl)7_nC=v3Y*29Ywn!?C}p-!>jZ(x8sPm=1~$ic6U%RSxQq8wppsML*a|
+zo5z?TF$s3o=wj5&4+Mec;fsEo=Hf;*y^D?&Vi18OM8PVgZ2jX2KSG<-tCv<L%<3@!Im`Dbu(<B{Yy#
+PW`EgBnn4wa!-WA&=4~Gk&nD-2Bqup^H?86>C0j+)U6lBE`EW*wvq|OWCsgkF`1bzfcSk&nUTxRQgk+
+3baf_zY6}!riaV`-pqC=bSkm2Eo=c~<Gf|!wpyuWpWD8Zh3cN=Taz(tD|En2i_(V|6TwzW%Kon*O5mW
+paZlMp&VW}=So^~5cqcoykFItSRGl*Q1=q%)J0vo({0PCKh#3*k>V2jZGz&PiX%$rpLQbP1u=w(CC2q
+`0vM<Hp%s$@y^U$z*BllYW+5z!WnY-#LA0jv0?z2Z9!A`#TIVTL^pgZ30GlhA~VSS3lzP?yCn}?uc1C
+QphZFLq?-hap*YpayVP{urrojZ8PRLk(DMog(Pd8Cik#U329wn43cdk9Y?yeswI0V`aFk~ATZkX%AkW
+9HYy}4@>cBv9X+Q+_fPJHx999Fwzv6(e^SBWH4xoAB{5Z!sZR2yNzx_BAM|P=d&Cx5xmD@|*^*AnF$B
+dx?eNt|OSb=OsOPwejA3s<(IUWEXD#zvyk2H6(vto-wfQIa!bp^}$pDSWZm(DgLGOcuspZ}M0(p7Ut~
+)vGU%%kFgx#V!1TT*xf=BqV>vbEIW4in)Ywxlj@Pr*pCJc$)EUM$NHY8MA_X{V7%l^3Q-F``uhfH&c#
+RGG2<;+XCGC$lc2Cy`s?yuE~7NILfQzDxIJ#itz3+kowbi<g!?h~Sun;WS-41hJA6JGe1XBLVc-5$;m
+AkOl2$A!odcD>{~@RU-Cdbjfg0JIacF^l77cl$d#blG$KPR_}*UT*u_&Ev>vVP1;4K^uK8_B0dh{ORi
+F(V5n}Te-AFSS9F%tmn{8)Bzi3z{Jc4BD5sDPl9?>v6L)aDxWd8uyKUTN%&h4QTBC8c$?F|=w6e^wtF
+4}9&QrCCN5voU1Kq7WJ~+9w&L)x$YFH}C-cNIzV7E9H+8STuPT2eT4@BS={$Cz_$&!Q($iegIwX#CCn
+Rkij|1JG?&s_$&r3f2sS~8u@MSd2qh)(NpMxpyVGG2pi3^qZFNxQJmWB)L+7#^G^^6<3*Tiw*_(FbfV
+;BXN8?1j~W)lON7{*Q*+)-k5q!+0PVGbsfsK)KDqkB!^w|sD8H5~8ZKy`~#<=Id`ak!^(=8vubE$RM*
+r@8Gfy4SwW@4Z+hd{FpP3(<VMmb0rNZ7)A&`Z>#VTTzf_z2^nG*H+KG08Yi0%WGi8K)x^fW8rH+tP`J
+S6`8}@C{euws5&oCBPsdR6j~9EceE$nr4W@TTXU$5MhoolHZ;o<;yv!1srE29#KXRI<|O`h|6)=RCM1
+dLBoUnCZ~IWGih1#{_37hkw(zUr1p1J#!T3nI)}sp|5BJoO!n7&|g|6yCr{<~BLahC?cZja)XGo2pS+
+t_MDfJvqLB4E{TcsVCStK?I)DuXyu;qA9Rc1;kfU^Stw|mjQ!^vFj>4RT%ul?X>YQyo|1<dsK-jH@+#
+ZZzr5`Kw-JW-H$+^X&}a}E){@qq5NFF}unf^=>lLQ>X>g%U44tC%iU>9_PglNm`+hY86hd384Ll7HIm
+1t!SP6Zv^Iw|3E6O_vKyL!YFwb2yYlah@p76P<asGrs(J4!;X^ujq&pb$OyLPlV;&^JUO?q!M!!Ma}$
+$(**0-ka?sBVA6-#JOS$HUP<{pWpg$(zoQU9L}H%J2l1{%ZNBCZS|*zi!vdkzOtj>Qmb^bs{+Ptgt`|
+e2k{t3T5s{}mEMRc5+2<CA7FYb0ZYw09f#0dbkA*~6-iIlZTiNvz`T63yb5=WRnI;m|ZK|<F6_uVp<v
+tXMqHNyP+Hj7bjItfxh{VaRmti-1ZnUege4Wp%ji<rxG_~BUbnUFxM7eICVr_zB&7al_bgwCUh_Om&y
+*x*D0Vm@TeH-F{mU{X=4M7o$VITIPh6DBWIESE+VtA?#yWMa~T#9ltHPDI(c2d){Y(B;W-FBkePSx7J
+`DwVm-u@1{*JdJG4h&sT`i23fT89nmnAz@rWPLnT-F>-UoyUZl1W?E!vYk!LknOu4(sxx@2YsLZz<&`
+WpNh!+65=)8{b>VnaBRLWW^|Cxz5_UN>ZOmI19IfpS|rdNA}*W#fbl^zoT{Xk*2dZElu#AB{N1rT>Rz
+#bsbm@oFVUwa3e;>`7IpkO9o`V<XgIAgA<C3gnhYoSz0RY1?a+FuWYE3#Qafs?mk)_0_P`aDV2G(YY(
+^rf%OjtIJR+WN>l{u2ulv-HSEce&GLnhrt-{MC3b#bcmEBbenjt;*mn?w7XG2Xif9XpQ?n4&gRjC)Mf
+`!#h;^+a>6BL&%Vx`ePBN3gX^cv~TMyyypbYB0Alvmz)9OzIE1IK-a2f;QcNsNT_OKF*N5=r{FozECD
+SB`E={EVfT#iiG~AMi`;c2@qRqmt*#Q%ChPr)%;ryggrF1u2G%F%e5NPpm#CyxhDIy4OVEkczzxY|tb
+mB7~-^(~UtjjNNYyPY}(e-=gk0J<VJ_JT08zw(IU^007lQyARa-7w~7{^PQ=_DazC_E=jlh-26onImx
+p3o2P|?1HU|Tv)^nv7c6mi1hIr&OX!&>Vx-GGQOwTpK{{P^c%$^tSZKghFpgldE!k1l<7G)qq{n!r5@
+CFbeiTBq(kLkXi9a5vUigx3HqYHE_#pi}q{Xe=Z?ThvN5iv_^jLkD8x9L9$d2cg_u(H9MY~=<VUl*0<
+jjGpr(}zmjoq$d`YGdfa|iTwc7bwU?#OZ*cmHm`B;j9E7S(h5!dQaE7A~}&?w0bzMB}tKeU+n<BR|#k
+cbu!|5T8`{T5SxEyUrw~S$G3ba#hj$Fvm0;Ex)B+d1aqLA52g9%wt%R-CplxjG#T4PB4U4@Mob=oE)u
+h-%a<VLHa4l9PWHNmBUjUyAC7qRHqheS|yR6?>c13@;%3>>AFX(sREbZha>84gAbFDEJj1@=iOfT62u
+T)uh)El;SpvlyNCS_kGwNlvN&kr^cf|gMVY1pZ!mrMsPxd-B~}3^G<J7Cfja;YT-0fIW9z#D)3h&X1P
+g>QFrIrF>fZ4iA@}ZImVdSF>--d60c)d-gfvCs<+N~k3=psd9*JSF=TZ!N+1EKlm|%J1f%VY93PaOnH
+r2qB??}TVqQP$FLw6tEvrf}h7(c@!74mrwK_ODzb|<Wt&I>CS3QjNzsYF18%TJP^Aq@tqOo+#3>pNqy
+N4e{TFTu4>eEA&byX1n6t3Ig@3C_CpZtxtJ8fu4JP2yaqUi7GUz3?SNCv@3m+(BE5-*w(nCO6K+C1IR
+;miULMFNhyC%>3X&<*kb+-g+7jwGMLnjuDHI-pe>PJSYj0nI^?fW1SzG=xJ@f>(OGzPNI9wp(ly~Vck
+sEO;)gE1xr^j)|^4|DLoXO%xtnY<gjlr+t}E=4=W)k+4ZufGrf~pnM|-`_6&1ZOoSA$z;2MrN)_@*hf
+-+?X~sNbe*AR;ie0ZS{g4>Nr9Ej913`@1P~j%_PLKF6?sZ<CMk^0^SiJA~hVC^<S>_PL#-dnVazQy9#
+<UBl$jbK(hpDu2e&=wLRKs1n5ADWp-}UZh4H9m7f&p)%d2>*vzvr28`ON02yoV}Dwdd}o9CpyzH;&`I
+@!(v<R0ID+cAkjM8$zpo8tn|(Xc-UZg;Aru<dnn^_zL1W?|#^R$uJ{_usqR}4{~!2J~I)P53+NqrhJ`
+$`2_+P7CCx}-Nsy!FOGdc_nL^q69IT408bU)O!+}_uiK5GmJs`Y^xYm-;>l8O2_;A;ZP&-qk>Uhyzt1
+X}@&=f`s>atJp*0&M4Kr*iQ$5a~mhgr=CaP{X0@RZi!42E(#XmEng3f!Gb42W&NW2qOccSG^#oKODkY
+qx<-H7osoXp;)-SxtkKpH3fSsKrquE9e{-{Pvbhd?gTVW$%8VUO%NJCuk4o?+}4?d}J3ukuldtlM;;^
+y<wd_zX;IWz_nPZc@OmXPm=FcsM9{PiPxwpCul`ZZFCzoon4^lxrC$eq#<)KLc^oI+zGP5(WXyNuo6A
+zErT@>pZ&GB)K&-{P_2&|6n2-Ob=gYJiQ^7g52a#Fn?BpW#u%V#WRi$gyn|Afh<ldu^k?A=nG`A8NA<
+z&)9rheOHm~<<=Sw&*8GGaQNfISs4DOW-bWUwL#o<PyS))9*@%xn3wd1Hr2LqVZI;nwj>%3$gSJPHt+
+S#{M)3C=HSsI>4h8OIO%AHGs<bAwNbc_8^~yN&scaIeJZAx!*=+oQ^J)TGTh7ZwA^YPR5?L+6Td92N-
+E^7mYg)Vp2z;Gd!@@V9O8*hY!;`zKAJl@H3;V?Hi8lk8U72=cB1~y`=zcJ!R?=EQk@9P3;R$^o8j3#t
+O&D(C_E8`50mcUELXp?7>s5u$4;Vq9prBl)p(*BPqgA}jN<~qNH3jt6@@>+Si0MQ4suftY-=}fnzua*
+{Sq<{2;YY1OiDSz9VF`V?s*XpTf_>QW+|8Vr{!p5gtI!<vF`Swd#xt=O$d3=0Z>3)*tN-sgN6w=q&o>
+uG01?8hdUr1enAP)n&ocq_DhBdFU0AIE<I7BCu;P5%skT-U3#KN&(!1%|7A-wO;mvK%X-%fUxE-lJ<(
+{LlG;Cu<Kr>TX-Pa9Q!T^>iI0~o8i~$)JhTRfj{>#3UhE&r^F(K!>ESOP?89_LXP$6yDf5<Eu~U)XGA
+tOAaLoJNUi^~ugkT377e@Ck?Hj{H3{v{w_%Fy{6NN>(ztNX-$P=Ke%Ufs8DC~N1W>62MJM3i85q=EPH
+64mJ5ojcejB(@$c#-ZbBafblFf#R8?zk{Lzoh!H#r-{Zz^IUo&J=NC1al$Xo-#7ae|c+W2#);Yyrhrr
++3igSipGQ$QLiV!I9G5J+}y!pp9C~bufb_8bK<%C(sX(9Ej;+PlR@7H0C0!112Fv+)azo5&QEBdFKcn
+r3X3N(bEeZo+}!p7-D~Rg>K*!48>U~^{Ybl(-DYv3HsneVrUdaLUO-MEPE%PMbDsSo+~lc;@5$0C5a2
+a~7oJp0j45kO#AK%C)L{u{jc<l~@EM!<t0m4nM)#Vir4ylaqJvI!(21%!Q8lNU8na#IMTlsVP$-E$lB
+Ftff07j`=Xpsr|KIh3hw1Hz8&I1jqR&K^nW!-nHD)5jOvILn>@krzCJM$x!I-EObDn+U{D0TWWI|4u$
+o>*(U!vtp#CwTkFHz|wD!oLJm*y*_?x5Y0KtGL3?|tL^f7i?Ujs7c9eI=r<L|K)Js!%E=`lLj3lxSp9
+c??pRRMmnQB~f$atvBNTyIw#q3naOq&(9i?NGcK`MIxF=)DfvVLM4PUEL04s+CX{1y!~o)uSq^XrvnQ
+RP12M9O7lq)>ZYHi(Iv=b77Q(<8g-L25_g`L|39VFl}TK)wooexS&#<Rrc8-PQpaECT7Ocv&Qu}mcCt
+q2t<&ZIyI!zj&QIIrK_q6vG#vN8C=O7_SfwH!vP~(YX@bePWu5#*qI*qKKIU7<sH7PcGbH9k$b6&;3o
+{fZAmIKJYCq-j;q4R3KGz)=qI>1SCzJ#*@bL+up7P*u-YJm{bDfgb&?hM!4HKR6&vMP{_IJ>|rVK02U
+qU%1j7aK`K^&%}pEMl|UBbqt#2O@8N}@@M$#bpS-$D1vg-<96U?6W&nGoDeDqw(?N$D0T6*&|Fij?|*
+@JJbhb@JU4-7A+Dlc0VQwok(ANoYKYg(rdQB;K4vnUnZ&67fwUxk-#RiOeSP*nFIIexiHj!Y7n0lq{6
+2l&qAjl&qAjl&svVQle6#Qle6#QR46q6WuEpKA~izWTRxGWTRxGWTRxKWanO;5}gvA5}gvA5{Flw=w7
++-2_*+52PFq32PFq32PFq32lpD37?cP~1SNtJhbNfmUb*lIC4-Vd$)IFVGAWsqOiCvAN=hUpk`hUYq(
+t81HW{}`$w|pc$w|pc$w|pc$w|pc$;rJIB^D(XB^D(XCDuJ|i*Z|&T$Egt+*p}t<0jg;i8gMch?}T;C
+c2f0V><CWCkdWOCS#HmnC0wenCM=)@ChZs3hV4-jhtxMCK|Sha&00}n+VP(Dzb@+Y@#BYsK_P)vRTeK
+PZQlM7e1jRSV0{&(S%JjVG}vnL=HBQgH7aM6FJyK1~!p_O=Msb8Q8qfDVykCx$p@k!3rX<iT-P%|C;E
+(CPJ@?25BNcn&?m_X}(FuW5VoC*w%TUlQYr1a@n7N6*#a7r7`hUCW5Sq3~M5}nn<oDYO9IRY9h3nXsa
+gLs)=xF-iO&wbgx|agpyzdrPM?uHPJ#%v``Z*)I{$zkvdIOP7{^WMBp?LI86jj^FB9eqI>1SCzJ#$NS
+G!PripNAB3GIyl_pB1iBf4IPMYYFCc31F5NV=8n)T?xC%RWId_qaEg4AfDGMc0gCSszAOlTq#n#hDED
+wv4`WupF=WaTHx+gT6qInljx;S)-N71)7EbTx^jCQ6=(l4qjinJ9TCN}h>yXCmF1NOvY`ommgBG10wp
+*`I(F6d4mS#zb5((LYQi4HL1#L}M_~6HJonlT7v`3p`2oPI9^P*4a#SuUz<ql3<0L%_J)`NxDo@D3g@
+PB+oHPSWNN}lYGF0?w`;n6Iy&<%FnHL`z22wUEjWb`LECOKVNU&{@cr^{})h80|XQR000O8^OY7y8-?
+V^OA`P9luG~rF8}}laA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4XfV{UYKE^v9(Ty
+1mPIFkOZUxAx^ID3!r5`4Lxm(5l=j-7bcv0b)PbE#CyMN5#yjYMimDjwgjKY+l4Zjf{~+1ahSiDFp-@
+!I|L(~TzKXV0epiI>N(PtV2K>Cy4|^|5$)c5(Dq@kHHcvuEOFrNl*3-j`Le7Dx3$iLw=M>;Em+>q39u
+Y<5*SFS@!B^L44JPRw<kgBhBhR%KV}mao)jwXV0S5%p4Z6MgFX=c;Sx#a6Ly%erdy*JfKc{TJP_xU3s
+nK5tafsfD<E?0Xy!RWzor?V}!Q)rq~MeIbAN;d|Z1Wc34axL%7ZQ@0gYs#VQrwXiEWs;jOk?{<2bd+q
+*@x<rV#Me|8qmh1bb+=%Bw&#)6+EiNCsRb9FDYf-JsyQXL!jntNnQlhPw-GiRkPvWuOiFr|pMlH(LcC
+Q7|iK1Hkv2JwxO}!|WkEUo>E%b8CtUJ|gTFCJA`8#o@T0K+oT2-pitGV3Wt;@M?d{)j?)hbc6Vr$CU6
+-nyEOyt^4M4Z&R`(C;~303OrX6(<ZX?2N64kSi%Jw`(7davlr{Ixc>rs2Mx^rKj7Z8T_kFew6B=|YrM
+-|b4vUVqRX>V-aNScy9&cCA|O){*W(*A>5<-n_YZcOwqZ--}-kudWWyZ{Gi;>uO2st6G1yr*gB^5L^h
+os-~#AM?G=f!Q11jqc^(6;mgyr)0_8Z_9v$|=f~IA;^g8=9E!`stDDoKcV~xJ;_}_q<;C^!fe_b9LBv
+LwQ$qG4Ug|D4wU*>Ubw#;u?b6?C^|pF~>xEbqpOsegT$LJ8qR=L?ef%aJuIuW)U$1UL0{uypOHtKbBp
+#YlV@IQJq+Q($={M4#pC3dbm%36>eOhZ3T<hj1<x+QjvaahU5-;nvGgaRn>b7wrW&B*Gu@vInwJv$;p
+Ey2$^_8UZY&Kgq^+p`b>&@1vebd{{LsM*T>t->Voxgi~dvS7mba-?8`r_)ncp?6K_UhvN=Jv1W7r*Fl
+&*kj+{Nml~H@8>EXSX+Ru8yzYT%5hqS7aQ|-X8wXRJp!zdVcy=cm3+{<}kRL&45(>e^oZceNh!6c|Kn
+ijcC>1cdD9e=@xfu4SplxYPY%5`qI)<#e5~^N`tmh+eULnrMaW*dRy<?J!%v0)P3DN&i=Mdj&zTPOVq
+EO9(QlH7f0rmMDf4mC4XbP>GpCzn(ZY~razjW)c?-<m${Pm-%|bAezL#Q{+e{xWt8`y@RRpFrG07Af0
+F%s>fdEwlChG^3CpyyFFPySGBXqXEpqs+a{Ss?Vkc_mp1U8rj=Yzsl}+AD4Dy21AUo8{y)5u8bH8OTf
+2*h1$=JNxUX#8)N!0e3_dmAOynBiMzkk_@5;pNUcTyWmj%1p-SI#7zJ6YsCeshLG>9LsklH84uN8Cn8
+XBMN9WAwMa{@oaj38N&<*h?tsk(Bgnl3v1gz0%17D>v`Z1IgVYP4?)s?}_^y_x0@v7+-pcLKg|x;a`u
+@f0Jwpqhxu~bEtH`!KZ0N$d!y!Z424IGRLy9Yh~@->Pz}%Ko3dZN66!ZdRt-^-(ytziqwD`4HQboglk
+v;2tV<}LL_7ROWdMj51Yg<%EH4cI%Wom+4PXI66G8^v^=brTRTZKbJuexe@rm-*Wkn8&r&y?bTk5IoX
+la=dL{{srVP-a<}6Te7W}}aO4q3zkXP<qs_i9l?}?KxoN68o0Y#LTdC(+eOi4smCmn`tkOB)$yi71GY
+&3C9mwGutr5GkOGame8u5Sws8UvzWQ3BCZNKqMEB}tUjNvb8Krw&`G!xvnsboCS0f96ZRr23qx-V=Vp
+CTUllI0hptr61|nNm5RulwC`m5x~wvxuy~6E%jr^ekAUhQa7K}hIuH0qB*Hcr&<{p7^abXESn+oh_eC
+^7khXR?E^TpL{9h!X8@FpOO#|3dwv8)Ak8EqYLTvs#N!Op1#3v(pJW*2E&<%jrAiPZ59To{BMSAb1Tt
+^$_MO{j=Fmn6!xSY8@MM$*whN_aZUSU<p+r+AQ<Um&Phm9m-NsIBKo4v_jZ#)GiQt(tPehm?Svtv*s0
+xgc_<G<LB214_E=`gs$E0Kyk5B{B0D%L=VAYUEDP*Q?$<XSt6|p)YKzNCg?C^ugiJMu%KF7W$Py}gZ{
+u|nJiw;8#_<G5%$%y<CTqyJP(j$U%8L*joS&<ac3bGuA*WBvKQG^>k;t1#<4?cm|vLTO>UGkkKx(8k$
+MN&E9Q9Gb?rP56hI^gmJ?SS@aN5P{2#G@&9q1@Le-g^1aKoy0}NrbHi{mvZ1tzO`hh=!!ccj%cYdP1}
+i`_hzx5}K&=19Oi<ERaUzZ^*7QhjEJW$!&nArN_UeeJnPlC+R^WUA?|$$fGn_3d+JsL0KSUqIiorNGc
+1Y1*M0?A4e2=lQGeTk%!y^9*x;VL+d8Yk@Z3?xK6%1b3B>&!KXJEiHAdIA2JG=SHhUEqC7%S$8jiom0
+DQmtP_fffYoqgoH(45XQItr>OZHxHup@4xB8e%G>nai%DH(-Z@*Z3!c>X)D2!O40lwi&g3p|`s3+nP0
+51(wI-Eyy&!fbr3F88IDdGqCEV`aMZ=d>iEQN>(7EW44iVRjGiARTlGl(~Flau~8%EApq;=*TyFBpXQ
+6cJX;<VPF5Aw6N4KzBOCC}0c;0X?+agy)9=Axt6u<hv>g_jpQ2MsOTxoibX($Pk?!eGrdQ6h8FRi7yQ
+?M;Ii@#U5KSdJ3IXEAgV?Er;`w#G^xoi=9Ub-9fmsk`4}-vfXwM4URJ@?G2=p?euGOg%S`v2{24R@a!
+7j@^R&oA;GV34=z0w@jMnn9&Sv0OO6?3L(fF(A&F7|E2A;dU{A%vGy({@O)e*b6tOAS0l8I1gUFP~0Y
+zB(AcGlCNSAM&iF6<hPF--sBu@%y@(7VZn5D^mzgA>vn9><WFg(8Dsd7v!4kij}$(Rk(M3Ky)1P1{po
+G5J|$=44EpXc!NKzjLLqM4U6Cn8E7<PPb?uN<MYbqN_W33`R`_D&!q{5*=iVt7;^JJOFMQXY?}!oZnQ
+hIy1lgx@g2CwG}fwBL*)awnO`ujA2hb2oOO^ph|tLl)R36ZPkAfl0)3_+&an`{;{L6xzhn9Sadg`A#s
+ry+zYXnChjM9i+!G3d%x0fWjOQJdg0vBR-RB@Sct6X&hxk2|TPtq#sAfP=UhXyD%VPDcXyHwm1L~frj
+ZM>R}K)(MDK$WKcMP@QwmFa-11k4o{X75;H#>f@H}ifQ@;`qiD~<!BF+Nx4hhY4ciFO)G#d$G!fe9K2
+z#JK%of_^JFx|qdBXAFfSkSC{YB*Cq*?JgWy>nFTs)0BKac7l}f)Xv<?X$dtVq+G8K<XXBs>i$y4NX-
+y=*Ka#V5?qP-J;r~FD1j1;pab7Et6?!pZiJajTB8wP|xELkewnEZ3k0Hj{C45ct8vOJK-Fwy}{(cL@{
+J<qG~h>0kP?`nCp3B_`*#kK`zm~dkXH&??%F7Y1Td0LY8-vkPtP2pC~f9yoLkCSX<vU}J2Q@0o?G98x
+>ztb=@iKj#2FR46c5*}RRJj#=dq3SsVC~+$T8Nx7#@L_^6>p6shZ5t*xDN-Y23J9N@PM9C1j57i@$|i
+BEmyz_q;DOyxzM-#5f@ScDa=u_zAI9(FT@=2{QJL*(oc{i{7auzv^)fIf%H@;UBn%j&X2K`3OyLb4eg
+hIFNr9qdZ8+rM>#Y|=)|yFFvWixzHd2zF(eQ9V<{Q(k1&YLxsvuvVM5O4k?6Qb64NoU!UL1C$G*mrT<
+#1>Hw59&zTHp2>XAk0Gm`ACVjL8Jjq?n2niHL}-*9IgpD<ym93q(|fJ9=c$WQt@t17jkrVpWuIAS9(h
+ZIoj&NP}Y$G{v#J;pL!1uV~LEXwN3fDFTomB6n=3Lz0Hnr)cTx;eo!`)Wba!#f9NL3VtIDdz!>6n%~C
+Z4;1iSKlkZzM8+59@UfG}?R0D!C$f1&Pl(;a`<^`1pk0q-OBrsW$&pC**;)X$h6Jd@x8zIAh7%>4=WI
+xuj3h!r7D(ov3G$HaGn{D{ijZ8%EJ0PFF1)K9-(n1h7>0Y4HI1FBQ-3XqXj_s;l%JkTjpMP*@r_JMbk
+Ghf<xlB^dnO)ikk8>yJ=$TYoSx;u;~TgQ4!00KB|;mLsjUdafqnhpxhL+%crq*WHJ}tIZhV)6(hjuqB
+rZsqr097lFb+?<6R{2UOxTXV9KxJENfv8Q{6Zi*+R_0kaKOa1Bv;x*67;ZPM3gWJoQFBmrz53lJ`I`k
+DewL*uK?L$0)lRs5Jo6!luYHYVgzK^Z-z?)9VJYPaEs)-(kPr->UF}LO6ZyL(GaLk2nttA55W+^;t2W
+qFYQ8kI5U)ZGOEEMe0V0{1cXC@o0YH(<d0k2c?4e%R8v%I`=i_7qzRZ9F=p)9B8!M`<uH*;MinM%xC@
+1g<K&OWGBHHXe&?PurV~4SO1QIU+geT8@_d{r0pSyu@)RU^6xkDgS0st(h9i#vh=vd_LfZ6<Gwu_TXu
+uSXZ^sg~9V7b=I^s_?aw(I<kh;xTDIWXhdw<-Ug1h98x2F-$&Bjse>p8+F#Nj88c>V~SNkIqLmghzA$
+QxyhbD!5EUr%@RW49ds<O*OHW}I^#l!SMfGrwA%yC5Uui67ed5rtdh$s&FP1O6RxJl)U<y&})QCQh!u
+#{&a=yDXVXrE`wqJ>=Z2odOiT#U_)Z)Hxg}ZBXI}PeDR<2-@uklz@=``0@78&(NJ;iY@VM1b3vg1^ja
+2k;lNqqnYoQjXgcl>8#1rwKN!rHoA}=4CpB_qytgJtQY=D#?ccu33`rme2bBu&j;nl0W+i#X9*b*{|=
+vJ9^MNLdDJeH@o2bb;`9{WZ=z|C3&7nCP!qTiY)s4%CeVg~Gj1{RnAZZ#sD6Joo3D$u6+ai-qEfAL=i
+CmfV*S{b?T<5||1H!~+}@T|+1=jmwQ9ZGx8>%a`Fdc^&~<ydGt{;PQ&n#_H+5I6uhd$+5U~)?L}$L!i
+9MXS^v$06!+2s|@AUMVHnK<fO7rDjHMYE^KFX>;&gT#D4Q8MRzb-c0wbE1g5PuYZ5QD>uZ0vF=w17Hs
+_t^FQ&UZ~?4ou(emP>P*ur+5n`}2iVtzMerqN}1Q=6Zq=>+L~_N~wi8^!cf(AH+)kY|JTK)2=ne?Wb~
+U#;MF{Lp#WD`sccboBCQ;G$#|!cN-(gVWl%Y8*}-hF^7=N@k=wlIow_MC3=qAMtv^pUE5EUP4aYU53D
+ZN#l2aeR;8KQO|w&xIW4Q^=2&WfFu1OiIr*#=P|er7{_OFpXjxx(N>zG=3pJeIE7SCaI4RbxBHS$X(t
+6lLqIkJ8?`NuF5YpOU?Wx@SYgD&usu>&Htk&sC3;tf~Nzd78s4=JB)gtO=&y=O-`Y2j$3q}Y1@!)Qyz
+v#jEqWV7D197VHP_&BFZx&TIqI;-4U@R;+S~#+61H(?s^`S?;)=h|s9xfIzsB4d1`x&#|%vyT<==tbb
+Sxl`|qYi}lMfJzl=c~GI`$NZWv~E@Vs)6Pn%=y?}Iz1QIMUtHA-p+c7(jv^U@3u5o3cg6+s6Qxf>}6A
+YQev~yHoPc}WQ^5D=Ae6Pj)On;tJs#+J?o{1<e)28+Z<_M*Z1YTpRX=!>d#7SRkN%cEy=k$AOLC1J!M
+9|(!;9M+)#=?_wWl*)>}{1PeflsZ}+OZ?XT<^*nTK~EX99`<l}zORF}O#!|=jVU;E3%V9Z{ojJ_1l`$
+kW-8FN5TQ)$y1kEbWIXDQ-8OpE<qrhAh;+6v<?*c3e3>!r)die%Eh?q9^{IQaA+Y#_%chNPaXde^Ap)
+0bxJ9m9w4*!PF2<$wIpi|OOX>9swfgT4(Bo+hPRRn@!u)$O9_ilNK2oHik#siX9)R}Du^$;)zK3~Sga
+9h?8#&T-a*W<5hO#d^Loe-J`YYt3?<c&M9Ct)+zi?<R1In$+Lqsr@Ow_R#XJpO3W>f7};;91i0<8{^n
+@?f?8hC>d+0<YmV4el8=eQ5PCr|9pBq2#fZf2$%N1GF~zypuOsCRcs7szq@{OdUA8C$N6rcLssug(BS
+pHdad@)Q1qpf(z8M!&@4+$1m;h7AOtd_{-<-OYgOHMD_?tAHm#k$ULO8NgyqJVv7+Icz4+JfrTFs;-b
+R0mfBoTPzyG5p@vq16)b*32{Q>t7DJMgI-QzPdt-t>hdOtaQqW34bc&NOi%OBF@yDy-TrX_iF;>2tMR
+m-==4RrjkS#UXN0r&FT*VZM&{#)(ItHrdm<<JL>M3Kaa<9H)-vv}U0mBkdvd;C%5=e^dvbDT|?mA__<
+>-gQ#&zIkBUB3@#k1%%(%pFc5{^a;80Ik-#>ru}qPc=+`x4_g=$~-cyz{bc-zaE)~@+*<~%<OzVsqcT
+(Ao_bT`#(@i0|XQR000O8^OY7yNKy%m3=04N@G$@YI{*LxaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh
+<)b1!3PVRB?;bT4dSZf9b3Y-eF|X<=?{Z)9a`E^vA6T3b`wNEUwAujrc8)+AL5qi)&8Cc6)Wfef_-C?
+GYJr!8SOpo)+@lAMIu+W)@ak=*Ljx)>AK+Lys4Y`4zkJKwo<L+RD4{vYxF@Wb(`I5|EzJiR;=?@!JS{
+wW4zUwN;@)twe+d3+nEQ6dhqg%)ukK4yQ$NfP1v{rxJ9=TVvEVxGi0EyWx%JDyQJPUA9$=CAszPO_!W
+MRp^~9-ELq)nze{mYSK3v$Vk1e3|9d7Z?`jSzfQta~+j>A+8@Pi(OYyPPUd0aCKLT*9UKeoXw^%(JQ_
+q_LD?hkZd6?bfNRFdQnGmkfmiFU#}46>*CEGR)qK%<zK{koZRN|y?85tuoPt`&L7IVEM3PBs*?CRkMa
+j%?Izb+6xmJr4Zz-shioP0Q7Ur1h>O}hh$uyrE`G^!=-+3H_~wC@R_Oxa5b9Fr_l4o`!|5k+q6;7uA9
+Sj7M038nPU1O~PvW^w3oW8TENQK{vrHWkkjpg?ag@P)#qK+yW85d-Uv*w!MW`KVysa(z0=a%2m4qMJT
+$17&fPN4OvXL9jon8uTqze(JmDxLRj}I`2K)<1^#I+WyLf@<s9~K}hem}nY?d;Q)*gyR&e&4^i*gw7c
+{0_2U8h4Su+ERSKL<uegqROMRd;oA5_;`46@EbJt-yfeGUwtO@N5@yEhnJV)=<Gu5i}U@9tK)-DC;J!
+T{L{tx+2!Gm5SLmTiiylVk{gO|VDdf#lM7u&aZ=Qwe@1!>08AF*F8ZpG=DChhQ6fT%EFYef!z4>@t9Y
+Se34JHx8<A$EFTUk5Y6sQV$u6we{Q4;R`HnBf5<*e>B|!=<p?(zK!1Pg)Ww|fjXGKY(ANQdfC@F)tG7
+O{;pDwX7^dk;Wf8BDZyz*dJjmLX+TV;uu-$i*eNAVPOA*_m3lq3&gk?Dd06#6^rh6;wttkmDj%EAa1k
+m@+P)xTWma+Rmma=0gohx=>TeZ5?z^Kuo{C0dzoOkV4XRB-k!%NLcR+7kgfc}DmS3?%gVav4MND!NYe
+UNt<Bf|9;bMORHqRd*dBQ|Op)aVphVzF#G`5wN9X=_)(R(ZMj~V*tQLmLj}#b$<;9f1ixVP_aS<eT|Z
+M;dv*3oR0rBU_<;vy!Jq#zuh(J{VW8s8$7{G|3zjbnF;;pTx3R(8Rc(x4VwvTG-7wx7qbA@%$EV)cNO
+00^DZul@Ay1L08@$Y3g0J?9pSsG%!~pLr4Nw_ZclNGR7Sp(Gl)QJhII+mDa^_7Ae!+S%><q%P~8QR2|
+P_`Z3<OFh0im*!@CT~G@*hFOp9GvnVAq8c&u*Wnvn{54zWh-_&kF-1gjA25Sm?>Aulk~s|H>axKKc(f
+J6ldESTWh#Wlq>tISLh6u7}`zy)!jP!i(A15hdnaS3s7r4-~8<P_u-<P_voT{pA9BleYoih_!Qih@cA
++R1Z>dm+?9sD)4qX$`!L=pFLmv@#RH3`7M)1w_OMvEsHEfl8{-5q$>jV}OS#F@gzFtxS<#P?@<VGZ0a
+9i&SxIgwG?a5q)DaLF+I#A!Jye00o(z(7j1zW)3p~s*=EwIoy(zM3_MQLQ?ENil7C!L-XK*SwZS}mvy
+EVWCorDnSlzGouUh$6{*7{I1`Fgs7|m>jX?p8$ZU;9!_0)1=>m-vC!l#mHAL^AendggXHY+;+L2YYn?
+55mH-Y!Y^EOYmzoyb(Q|X7NQhUm@$BU#fvSGxSsYGqD3tlEf8L7#t&ZXR~M@nl7wMS1gm8R7c;uA<x*
+G(x?6K5JVlc<_Xvw9w^r_wIsn<8715R9tk<egY`A-c2f$ZjV#Q|bA`ZT`gJM)zsBp{n_CTfEG0(?2-e
+2q_zG^;E(MAAdn6?0lA$I7AovkCjg6y4VAX;BQ#JiE|tx$5Qcov&qgZoGE)BT{mI^;Z87-Q@WjN+~un
+D6YQv2t)1#y9NKWs=MuqA5GXirM&?PB-qvtwS_Xdj$V+Wp@g7q*ub@#@>)*2a4Fht(Pg}LSio*60LiZ
+)BOZMF9N^TK?>cvM0$b}d5c#>-jf!}wxgFvl`8G6Y<1VN3UpM%GIo0n&-4`cY>BN%30t!u;-kgDiaO`
+-=8s4dre>cDueW0xI%)Ea>r;y^wZf$X4hzt<AAcl9(5`g79L3vO>ppEIKx^U8lRf-O1V{jV4fGe#uK!
+n%<9VpA+?+4$PJ4#IW*+g)0JGYI4+4#FOvTX4{>0sGGJY}E^W@orrQ28~>EFMTr+6br3`Ksug&uoCT-
+QAcV}gliOP9ldmh?o0N9jWldLOLkW^sBA9`>`c$<#&$Los|mLkUak&;HRta1GxbM`CM&Axl1H+z$!Gi
+J*=+Z}f;9r(8R{_(`a5p`fnvUR{E{&fG~H3x3J>pP>R{6eK{oy9&V`Lrc{~2sA+)CupTCm%w}&qG{h5
+{6K~V3yX74pSQH`MM{RKE+-Dsm$?6;W$b`scW^k^?Up$-g!(6{xc8ecpsJtS_Q@qsMx^AGQH<IPNjp)
+j6_py9xtha`)b_vFw<{}R1mvfZ~2D+h_$nfM0y{4BKVAZYev<=bPYJB4_lFMB>PyN*321-{vtZ6z99H
+B+AQ%|YH$We@dM$$htEI`hExo{d^E<2INdm<JnAY4-1fa9#O|`D<r^<5Fg8v%|z5Bluya+s@{j7qZH-
+Rj7PRZs50Pl47nr>z9+|3fW=C|L9tiX!|hm1Lo0^XXi7gSgqZ7ih1KrY;bT!+{#df1A7WJCo|RY#aE<
+nbHlQrn9sgEv`1=_1($=66{+DsF-&e@tvTTSc<Ol3_WeJhsRKKc&FRP8rA%sFlMRAyCA#9~?<dI=I~<
+s4(AO@?5x4Li4%(wrH3HpC$QB~!lx7(Sm>92v&_TxPKy5(4eY?hc5V%CS^I)J3-1;{N0^hB__DF5jf$
+0Tz+Ht612Z7l^SRL@GY`1!z1&*M1BH4LlOoq%`d#u#&9zRAYCg#ovp48S@w+m)JRvl&jU9mNWKsl;GO
+UT90gMcTH4^!^d{%^}Q@)D;s!#wXX>q9nSxm{^$*5mQHRo}NeyOpp;z^lGZXa8{RsRNfTH+}qDPZqjW
+zTL~1@i<*P=dS3MS?E)+l=dNLP31Z<cD%QV>vp58Cz5Ru+m%*$I4}o6J7to2*|;-t$bs{_&A0pN?9O)
+Amo1;|N^E68b`W&yefW?k?SHbmFE{E(5QHt>+Z?!O7S06gMmJjNyTJ@1a3@fE_PKM|E%`ME-5TK8=uX
+5Q4(gj>>vkXHzP0E7)yB`nF5?__e<cQW9oVz4OI5e-H`D=dU%st!RQ;@Yfj?Xa-PH{osFp(i=RpMBZ1
+)Ye0R$GVTRd)V+}5@RTb0<y!xnWQ9bfGzALXD=Vf$+Q;hm{5@H}<ajl=&2?GL8Og1i6NTkTRBGUxH(*
+*{u&k1EGOl?7gbZJ&Kf-ALk7Qs|ryc=>W6zU2;ei8svtmT7#RI;(@>7vE&M8{hN)oA<^%Y1%xWV4h9*
+w<pBUbLaW4pZCP($5MULS<++93wr+HV^rqxcW8AVeyAkxuO621JW9^9BBlo?F6c2`hzC2f2|bcg34PI
+ed6?eP<Lf87EOicnqbMn~ZvYm$JdN%()_*#_JbOExj%RPjKY9Q0yx&`ot8I?$k;iub-?0JSBKuanC5%
+;c^uS6IebLiBk!F8H-hTm5O9KQH000080P~d=N1UFwWBm&N06j$j06G8w0B~t=FJE?LZe(wAFLGsZb!
+BsOb1!gVV{2h&WpgiMXkl_>WppoWVQyz*d2(rNY-wX{Z)9a`E^vA6TWwF=Iu`z(UtwLeYAdCz?T~qCw
+I3)KdQo}PKvnhAD$E3kIwW(G&|Y@6|9zj6#Ic=t2Byqas|q4R;v65Jm*+fohRN2J`-i>Te}8zwjt=+s
+PtNz*yQ9;+4{V9-mbb+&Zen(tC)Y_D&DmZyi&;{z<Lp1ld>-NY_VzAKrcs&YY&uWkv}9ArY<p_xVVaZ
+)M*bdujpx}S&RKTFN{3F!pTuP`jTW(?Hp$Wgm-!;gs|zS*XIWnNPxCk`;~BfWuQc|jigMDmxW`mCC40
+H|iiz=f1Qky4ZMHj~vkA!-Y!Vl7{xzP}p6q35nJ1TbaPwvH>J54rJC5=%>@1mI=gBR5&0t~4%8Z@emp
+573_>WM{lgm8H?<s0mc^tDMyDGoIu(#|!yJOQRWqCYHidsDaQL-qV{WHrk{x+K>SNGI)m(Ji0SzX5YZ
+J}cL{^TP&iVIlE-p6U2!<)0a%Xu=z;G<+3r$x-7f-R`8xY3b1AVba@L)bwE^;LA=vN*wgn)_><7wBPf
+Td3I@jXp!JUq&U_k8Cbz;42t?&*sQRu10P<L!dpKu_Ue3-XQikfkHU?4X|RDF}o|`tGl@m4UlD@4ln*
+V{dmE4Pd>9xyOYW8$;IclkVT|%7x}9-CASMeaK_+O9;M|y42Oc_{mI@R7_s~A@aXX3Gg*IdcyY3Se$E
+a~Cv2CU?M^NZ_dXu&PT1MU$=T`o{x)OhajZg2!E^<=4e=FJ-e!p8EH0yDUevCCMtTbvIG?eb=xdBLPv
+ZoLVi8JYalcj$=UIAP`HLYspl?}n#nP<w*|$6ac7VQKcA-V**9YjQ+dc~ggrfAz94R=*@Pp(Est@K_m
+iz2oR+J=qyo<413c+8CfD6Vxo}*{k58FTa{gI@y<w5a>)5Fp02_TbR-$mCkVLhHxrQJk%G$kAzM~g)Q
+w=bg0dHlws0h52z?ThR*M>*g=`xkrZ$uRUE*LIxejKNL5h8lMxHG<Rx{!<#Mk)%fYk88`-1hq9{N21Z
++9kdW_fHp)Mp^beJax{S^eKv#)4Ie@dF(NfP+!G;PVpxb_A%=w*Ho&j}h7F*80QCb59fV7*32STE5JQ
+HTX#jOYTw{p9tZYcv2qUSf&WGru2KB=c`p1=;0r%JdRt#}T-B2M$uw*1~4y#5GA5kCFjFww7gi~W!GK
+TmV;$w)9VaXUXkD-1HC&y!7a*ig@5NeL$9Jt|7%LkR35uD;fT#nEnE+8%-E+8%-E+8%-E(U0j6Oa>-l
+aP~;ld@7Xh8hWR32{j=fSAM(i6N5CF(kl{07C+(4j>yqHh}5?a>1}t6G2S?@c`mG5Z{3~k}r{biR4Qp
+Un2Pu$(KmJMDit)FOhtOB~o*V5KGdCOBl&u0TXl!#zHmLQ1p@i$QrC>aF{WYA!n!%M5Sg5HSnDY5~bv
+kN}LNChK5)%#DpOxKs*{`1Nuwqp{jx33~B&3Mn1v`2KX^J#^3-WgW(XP^4lRyVL0N!c1Rxg5ib_hYGS
+AfNQ{QiOb|zm7=a$hW&uGF;sd(I-;kz51F`{KV2f93uDJKg^S&jiK-8Yu=$Vav7B+e^4_OSfF8dJecn
+|STMQba#$7^dmXzIGH^Fdvdt7;t9c>T%_YH}dbpehcVil%C&N~emaDkp;3Aqy1eRpd0*yhnsZB(;k31
+X<8!x}YITB2i*BVmV?sf-^B6F<`}d!-^@1U9gQ#((svFp2_8zTv~E@=7ySJYZCpQ&0ScL%WX8>yU+2I
+olKt@=9ytO#V|j<JbwX>``!mP;)IGIekesLhB<t9JPMz;FVE!iw{2et@9UJeNA<l<c~>iL*uWfwQx_I
+d92FPQ9MwbEd@e#%M@2?-NA*I6Q}I#7Hay{(M4@=1Dy#~RI~6~6d84Y-0k8POTSg#NDOV(-iNwVtURC
+eJyj1hVzEt<bz^q|m^1nki)h8wtlO5WD(MKo555&~O5yaNS6U5lW6=6d(MEOKOv=mS<2clvuk0`4m4W
+e&iBU%@TP{M{pXhtFzqWS6*p#JuS$Yn5iCYNV&`E_<KPhPcufvwL^@ocokj_JQp>OW3ftjx-2&WiZYy
+EvW3#T#UZ{{#J3NnVryhcxevhuebz7y7>7a>o>q+d(iI)BQZgZSz+3!?y+ELHE=6Ix3T|u`R;4Ip-KZ
+xhsk!N{^!Sy0(|-kNd#`2QXrs*MDWxfWn7{n+LCkse|K?N`Z6^kj*fcm9-66WVvr1(&uX)tpzR{mRlg
+3h-}6J9noc>Tb3!*uiFB#ehik)u$BelDOoVJ))^7Li9D4j-rle0Uo#7?*m!-P%^9WPZ4F;xfO%~sy6!
+%*n4}d0ZPTMI5RbD!Azb!iptGRPM0ISg8oSZ&=S4-`yxJrm^lLymdTJbKR5)U=x(?UCfo!;#H_;YNkE
+#XHShJ=D55_=YOwFRBVb+3ZuGT{0nptpFqsqHF3*1Qjt}JNecaJ&>B<4cqrdFmt3)NLGd~Rf+c2ASO#
+>FOZz?(VyeyZH_HclEB%C1dVAX^J$gMNib-p*rJqiC)+#YmzP1G6H7HU`p)5O1U7tvjQ`f<6xP`d0^5
+bAoyO_wYeC65Q7;q4FXcE1VXrJ|4L;>FQS*4_Y45d{E=Tu;4M?J1wyN7M6CkKT=cZIy4k)APcfx1(g5
+7U_FgQ(bk2zPHbwYYDPe@eqcmjuL~NDn?j<^iK^0T)T!T!2Sq#kQVYIwWwH^dqM<TwV_#+AyJE0pr>Y
+~}#eu3l-p0k;r#hclp2P>D^;ToER;qV}Y7%|*bWT*53)&Rw*sD5NsbB*Z==>@q*fqg@-o`*0=k=Lq#$
+8zEb~ED6kWmYLLA6r$Rdp;-RHZsKb&K8^H+~Dh<wT7w**S!Da6@68FHdwtx<k4Hxo_HKJ-usN6dE-m_
+0H6+%#H<p&+UNO5inw)I?Y|OwC3b3Hk55@xHK)`h9#y&vL|6?!V?Dm>R}oYYHw`gK=>veJe#o0_?jVe
+;joQ?{X}5SXshTWvCV?CW_01rR$?u#LEB{_!Tz{OWkE$rwu*XLa6xzZRh0$(T+*#|UHyS03RVo<#nR2
+Yj*h9u>#xlw&blDC!@Sui7L!h-F+&}LZt`@<8yuKb-bLmz?;9iv1BtvvgPjFaLE!6X7+J8fW)6`Z1zo
+1_6x-NrsH>k_b}9U67d<xzYTxbcL?0cVUHvXtao-i<)`D)myC7pmr~B6vEh}qpwXX^peKKjYU@ug57P
+{8B)EJCpCs9R3?@rCR^^^t@b;+2Sv2oDl25z&mih)A<@{ec@3%WkLuiR>QZGpPoU!BdcDj8w)8aS{S&
+z*CZ6V2LauaD-_fzF3nS_<UV+PJ(Hx~ny}uG(7Yu1BW9vZp|Ek-G2cS-P8hlasm;F>s*QM4N5Y49gSP
+xM2EbCafFnz8ax$pjSkt%7(U$e}@Hq%()TSAKb)1KZ$lZ(R9GvJ(-E_?Gw%UZT{=__HdxkuB-o{EVOH
+5*UPo4QO~q|7FYBw1y4Dw7;xWS8&@0OGLnmx-R3^Z0+a9TKGPn(b~v&itU0^ZEww<+XmgG$2R6O57Ia
+CYE54GIrEjl)57TM~2dgbuo(th~^TD&bz;%htML50x_PJ288cJUOiDaF_cy@N9z#S2@$6J4M+q1yjJ}
+Dbqb<q!KWx@CRZ&<*4R&b-Q8v{GRUVrs-hi}=@!+|^QZu2VzuRQNVmW*=sjfDStjJ=pzU(<T=hPhtir
+7wTHSkT+iGy1B-aa88XUy$v7&7qRJy|`Z_(`bH{6$yPqVnXjVLzr)mhxF}>O6W@*FZR=GdguKpF3UKF
+z(F)GVqY0m#N|nJ8>9cX`bFg5y#IOLr~0)%_5JwOuDIU;{K3w^s9E-{cukg7E*(|x5<Si%=X*y#*rCJ
+o)7PU>IDQ>g?i}m47gqbS{56Lk?2zFBJbLSShnKgb!C<vF&Tl>b=*(0$Raqey!th}idfy8F30w$kU~R
+FhdGsY7y<utgXXO1KP)h>@6aWAK2mtey7Do{wLmg!a002}m001`t003}la4%nWWo~3|axZdaadl;Lba
+O9oVPk7yXJvCQV`yP=WMy<OY+-I^XL4m_Yi)02Wo#~RdF@(lZ`(K${;pp!*kG{@pk_tNj_nlpA<ZV;V
+ACXUQWX7kP;5F8sz_c*Zt6SSf4_&4DUy0|okoj$K_f7d$>B3I&kTpOp}oEKKYDxm?);K2&QDG+Z%*m$
+#ns6l)FHd)?9uImpsO^z591)BlVmO^%;<gck1&b?e13df#o;W-layvrDB_%EkU4PF)blvZL(Ke5d=XK
+y6e%SO%3ClYe<|{87A%DU8zymvkLfZ=iw^*%>m)7vXQ>EsF{itAf!I_Pq!MenhN_30UY+a{n@q-lXca
+%8<0zu3l+9==GLe1}^U{-(B+k?DZUr}AW&20yA$lLApXoY`?$hv*UK1?LDNpEnoj)Y;#(z*1g?DL?t|
+e=WR0ztFMg9cC-q1Q((JY8574tAF;Xy=BK|KF8NiqL1nTLzD>{`WhxFfC3Mf#X24&PmVpbL?~QhFz1k
+;0qn)m;?MF!>^!i8vD!WVDoh*@I^4Od4{tF@(+%z!&VkArWF+>i#0q3_ZjTn9{aEbP2J3739)>L~|)8
+?!)LcMTkbKW*)Rspgo;a7#FY);2sYEgriSLE4mZ3%EV$7xj=v{eLTPY{p!Ol9bbN;kH^#L@#XEOH;@I
+>7)AU_r0{Wx6r2;hN`p9G!*Bq+Kb@ZZjv2>q&o9nzKS}G)&TlVIZ*J)9YD&j+eLTHAKlyNRJf-Uo)9b
+67(*vR#Ar!@u%r=rMiWh)<Ou*z^<Utr^rR$#%-V6ptb9xBA2!weiLSz&LSR%{yvtk$}@qOVhrf5RnP`
+IEt$z6I%L*x#!uMu6K==i!w`q_a?Lk6KB{v06$H<*4FE&zQNB}wYi+a$}S==)>L^*CeRYvy~5=)(<qy
+8h_&^0ys}%ANyYJ{%sE%PNUz_7J4O49Sy~iLlC6K@_cNo`_63Na8E<Mkb6*vs`@53xoj>5b7|v7r#zL
+zDnbw+drag{dfo5SIbpA%U3~}q6KsXc_#`|!P!%i&Wnlsh&-(1iL~!TnS^z|T!xUm4elaw<XiwP9{pX
+8-zHZn)+Ge!Z}iH6OYUxM(E3aSzP)F_v^NREB#iIAq)8YqVYs_n>xS`5YiP_}=6PrXmnO1}@yuMt4sp
+jA!}EBA`v_wk@)&1wUg^}pb7(X{mz;*KF@_GM_yBE!Dd?ME+(X0EE^CGxYevv8@&F?n0~GF)Lo}%oaE
+DOCWgn~=p^Y(ii2e=5hte8pAU+<-hT-8DqS6`JBn+(cq=oJLNSIMS0U!4RGuh;g5921@?zu7=SWX2k=
+{FdmfuBQA?SrcU+6d31&9q?&!8}QO33KN;s6G&9vSeWjx(9N)%pLg|V@N#$G!4)vVSK0<$S9%tlLBUj
+Ncr$@Aag>(L0mdGK;KYCO(uYxJ}LsvIG|+;BF~5eo|W9pVGV~EVVDD1=7tm{sa1;M&ZQ_ClMsNG))Vf
+&yblT(0c%Ja26QI7po!#kG%htt%j7f04$(#uPO1Y8A5|1DJZIl=p2C=+=SMaAQH}o3s}agZ-_=No{HR
+7fRU@3W@m(%bCnbE%N^%@$Gx6t2#50i{!2<7R^e@5`$GIWn=GbI>z{f+T$FT|rh5$Pld0sJ`3XGal<)
+3%p`NN4Ej>SFBXgG135*TKK@z2gX9F9L<1o3@os;rcr`?9Ma03;qRzjm?B-K$2uP2<J?vd8f^NZdf>J
+L#@0&^o>9Kd(KNYP1c0rNnuTwl>=wuh7xPfoDANT93A?vfu?Aa0|0}NeNcdt^ON%&_fiwn%-ugIi^@C
+IAA+lz_wXXqN)L<iFH_10klQoIaebS>!0~1M?5P|yt1N?)#zKuW=@(MG&6<UdC-dme8Yl@f~nJURrEb
+8TK!ny)%0jBddzcmJ>c~y)bs1RD-w4-%kyTn;;y+8`{TewwyTh;R;Fgf0@tgtem#=|JN9Ot`_>q@dib
+XGQITFu;`6n(L}6P^)clxP$y5~l7StDIqPbVwW6U+{G2bkJzRm~rn%9;GTEF_-c|K_4<vXI#o{8FmEo
+<SK7L>KOenK#7lF5N`mKU@7S&ep0G;2>=Zkkoucl)l&CV{5U^{Du+Zvwp7f_g==t>MPl){12A4d1;Q%
+}AS>XJ;S(%Q&d-#mX91Y3IiN%{Pwrn+Im)_bdH>eIDqO_qNr@H^)(KaL{>1`p&(PS4^nN?^|HLYaWVO
+arJ#lW3IU_?DL9_Lg_uz06kdHRu9yA^DCXa;)EN_^vQu8JX4?EHC1CKz>6)=mv8v2>w;#ayjWF@Hkqh
+Dsua<QTEBaSOl5)kE^c$cEY9_0-?i=OD-RUYyylQ?fgPdp$x)=J4*e~Txn5&x{Tt@S1tqNbHzThzlvf
+}MhL;x9qtImE<iEUIQPBIb9d`|&oMaVP2L~n_CJbv>(2<GX8O9d_aDxF&qVL)jQ8`r45yxcTELK{m3D
+dB^Jpb8yU)w^m!5l5zp0sUR8zkyex^|>F)efjpm2GO6Wwz#}%MB}x&7WB66Iy?SsjmfZV}}`yZWbKHJ
+g?OnGmNJ1t@}b-J?Lh?B@eijiAra4A8MA9zSe?yGHho5w&|?S0jua@u9+KV2pb&e-vjI@sC)aWR8NhH
+#A}+GF4)P`LZbbZ>stpdr|xfEugXc@n&`De@~cH-uR(n`&oBM`md2|LYnJsU8?4sXQWI?`oeA9d+!BR
+4MfG!lX@|*B%kIoJaQdWTgE#VEoA*t#w{XzrVRt=HBaSA|^t~ITUNsu4vAJy6E*y04KJ9yi#es>rjq%
+R4&|L3zZB46c7PLpfu0vgy!9hL3>VNl^TeG0PWz<V-JC)Y&2fP8YJqs#;mt38@^S8)r0PHrQnQX>d`1
+Uf=%2@T})HSpPb7u~=N1)wNr#-&r>foJ|cO-GU8_&!Gy*{jp^ervhMr-w;UVXlO^_R-qG_%^1>2$+_u
+2kyUIsPlY^8C*O>^qy6N{e?B)O!j4bclYLS?_iIazw3fpSn_XDc>)i%hw&=2YDKPg|PjuLm~BeyIzK~
+Ai7SnP`)EEmG8zv)H|4r<=Yp9&}Si*r}4dfXZ%9sxkw>!7DSnFl}(w*FM~&c{-4fou3nGF!^!K>PtLy
+_=VSRs+77O?zq|Gy^#vx)lc(&pw5#xDj}}qzS&WY;PW}v>e*;iU0|XQR000O8^OY7yAEJ8-o(TW|HY@
+-DH~;_uaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4dSZf9s^Vsd47ZEs{{Y%Xwl)m
+mFm<4O{K_pc}&bTlA&IPP{55=N^P!+{yJ+!R2gd0LsI0c(t%V>`oSSNq@hD?3iNZ@H0uSXv$1-PKiJU
+Av9Ex7YkfpU=KrT+`*n>Dl$&8GXLIIsFH<(C%4#bpN90Cib8G&<p4^8Y}V>x{Chg2Z2Wqq5o6G31&tw
+UYw|u#wt~#G>T~i(nG7@a1r{c53)biPZdNH6;t#?X#;D}FaGZQ*C>RnF!i3Nn7EDN)Q`f1MzQizHKxZ
+|#-q;Mixn77W?=r3(*EfINyq7eMI-qkodf|5nKq%JN>uz)jdSd$5FY!FQ)slG92{rpuDtjM-TJ|E?7z
+}SngA805#7$xmnh6A?p|i-UJ&tow9qiK$($fcUc4~$Ug$iEA5~JPqy4`h%*_Xb2mWL1#WS}3Q>+vv(N
+p>cEkDsLn$pM%DOO`Y$+5zYDS6@ek0{3a*J$iN&3I@Uj$s=%L8{`{q;SEP>j%123Dl-9DpWBb+)f_@e
+}u)C{z!$1A}^r{k0meK?B@*RZVm)`fW625`9#XcJj>?bzzE4hQAnF}w4pEUdnw~bFid#i0YJ|vfNSGo
+<zb@<w5DV7!;I|<9Elq+graW<PI^={P1Msgu)zVe^zGvQ>&?SGom_vXZzsdy$@TsBPoRa{FpFTwIr*=
+Xzz1V!6?<Vi18^|7IvbvT#fp>97nc|J-x>Y+#r^f!-5s6Z4C#b!Plopwrw^AWL%Mw!-rn4u9m3B_6((
+k5HW_(g;wP|tjbKTnkmm<UuKIVlHvzz4OfTL~1vifra&Al>qGU4L$cI4`K4<!3i8kmb@}DS-Qk&jlAF
++c_u6Y++w0~`6(CE;nt^ko2{s`cLJ1jr<pTPP&h@#l0&ry;x>D39=I#LMdqi`KT^l*ofwm&+%{&UUJv
+}b{_>~@dy9F6i4<c$zKNgfE(Wa<UMjK&f23ks6@g}C8>;V?_puQcP(0SCC+_ny@sLzPbBFdKG{DVe=K
+g75xh8jjMbmxpM^dcpEhWk!X~-lBM%EtJRPAe{${@0%ZvqqpRv08j*W_&kLa&PWw-aVY?!q)aA0_}qJ
+sfjYL{{E%1tlL_vln;2;e3i=!ETNt+2>z&4XBDllZfK78}HiFr>_Io<Bk<3Qg>-BavZjOd}1DgiIrXy
+VY4(=tcJa&YR=U(3yLZI_F`VrO)pin<os^eI61QH1r{T`-q?H$>|k?3I2?_epq<Ko(FMbn+5=|bXB2P
+{}JSanz;v*l-ugGM-zJYZUk_iztyQD=~`=>biz2T2a68-_vFVRpFoSxHu^k82lf`mJb=97{;V0F;mbE
+*-vyqz<#iGN*@rgwFQ^TS6Q%79I2g^ibH6f{ny5p+X08JFEuoUGxDM3dYIAY%OL2W&&3UX0WD3E}IUL
+But4(;qU|&NFK)`5DOqf<YSCG1oT-!W^!a(0x$(H&K5yyjzx({*TF)-b-D9A7`tpK(6}&U7pg%x5U{=
+&=&*Bn5=1T}xR8X!1ZL!dDLVC_7KBKbg#tC`Ea~zh^bQk2G7KoeOv0@az7c>a;UCHKu#fEUGnl}g5}{
+xbv0uRe5DB&*Vh6D#I2BeVs7J&|fPf87JdS4qE!4J%!xn}jSen>Cf|)@Dj#n6w2%V#aI5tI=HG*w5=w
+;Dh9dKE?;sR6@8yt$F6#GS@{+g<Aq>^JWaDK=5lOLmAc7-(O2K~d1<8<^qQlnE^eApfI1<%I{)8<t6=
+NvlyBc2Y`GioaKv=RlT9nt;W`l=pd^>&GxnWN?6=h$vTlZ~VKIJ8Gce4xYruFWj+2a!$lH7Ai-6Z0k)
+S7$!QlvcB()lJTuCFYPcXJ^sG0;oAse%cy=TSnk!+L;K%8VH>25aev-T(DJGSC*_>w?Q3=*v1C3<N??
+8LLph$%QW7?3v%%*7r$DhtlB`>vPEVc&DL?CgFHjv7z1sRb{>gr3&%uYdO;de>X9hdMbK6c@;PDn;~j
+C(8i{fZ6v8fyU$cQ33GLaa(XQzQS&2qn;4EiR^F-zxEm5wBi3I|&v^k|;r}Uc@l&Wp3HE~tjDi_Q)&j
+nqPHfby4Kp54)^hUAmh~{iu76)bPtUw^<Uav+$p>9hyRuR-?u0+tB6LNtkN9WrAJ1>+yZdza0V_g-O*
+2Fb)VVVBIut9-I?+GO@h!qH`Nz{lyR^*k(nleF{URbpgu7RMM15FhvpVQ$tw}D2m$?h!M5SUoph1CmO
+=0+gPHF24|@(D_}#yD_Fao`##^iiNzk?JRdw%A({2h|)X_CeRcpbx!<7es>>*1@xKUnuVobw#Xd-CKT
+Et~aY{0a>zn@hleF%Q>1-9GDfUDIQjMK`#>(7Ry=XBCX{zRyV8I((C)M`P_c!-wlGY71|E9Eetk8(D=
+quue93Z=4)U1^{;r}%w9ZoSeQ~)TO*4D9dXN=iO75b6?GeI+N@;~>#h7!W_V$F2CP8fw#sWqm*=A}<6
+vt9%@wIo%glym5gU1-iG}4D6{qZh>JFfz!Ng=W@BACtp!!davW&LMT(UuZPcltr8ntc_t4Y_Uc+~$YD
+SN~+xou?wVYfxTDN|>lK9J=Wd4D82l`}{aigGn7D$=eH=p$y`mCKhd_QrRg)|bg^I~A0S_`YUU>wa~o
+uF7uAg|cZJyP7!FP1iA=76(o9)l1su;m?eLdP?oW2CIpdMMUkCW}cXI9Vo)u{D0$oYuh}0*6>1E+Oos
+ut`JmhAnkH630vn%4MDXIJI1|9T=zt7*rji6YpGzqY}NO`dU0Rrh5FJJ2<p4f7CTd2dUa>2rn!EybcJ
+6h`!}$`+FsaxQ)@v`4Jos%=nOS%&{X@{-wrlFAWKU%0$mSE2zG3PayFWq?2<Ux_PE;;iRGQLzSU@J?c
+N4;1jW{(ZBvZa;$UZ9D91rrdV?1<#HO^>Y@3m`WaIiO?S5x!-WPP;uI0kc5&VBJkYxs9F<3qYJ9ag7$
+|LiG=DSs>*Tl`TW6fNsD`lJyHj&q=LLGr-YaWIhDw26pHM6p9Z(eJ6ewPXxGiVyDo|u}?iTNFWJn=36
+G%No)q4+Z4-xkq_QR!o?569H}6&HU5@?pZC@s0VLhbu3Q{a>K2eD9Evz246z{>Te%qr~SgC5HSB8fcx
+vL7zXu$cTO@H8~5P`P<A(m8L2N!MPVC$}UhPD!ukz6~=#iGd@WB&H4|24Du9sKUDvi!ssv0`Y%vR0|X
+QR000O8^OY7yP`8?X#|i)d_%HwfI{*LxaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT
+4dSZf9t9Zf9X~X<=?{Z)9a`E^vA6TJLY$I1>G?zhbb#Vq3t?l9Xf1Degm>P21qoBuG*$`stw9Od?c~y
+pr71cewxk9xYKMCE9VeJ}eHn8mjeUIDGTw&5#bXx99&y?@m6Pp3~Xs@yYr13B5bJIQ~1WvE2*y=;lGv
+MUmVmS)9^wK9iJ`^eO*mlBO}PA0HQ4GL5UeplO=OtfDDw_5(fiG)t-kBY%_MWSY-qLHQk39-pv3msL5
+9=hAYU<XMT!VxAYP3pl3Byr}O_3mI2(Mz_nA$Np4tp?uAknChXTSI4i3PbLvuc+L0eC{5`~*_L!AOId
+uAvpSOFJgbW2c7ZTom9GzRhv-vWe5K1Iy)TkSdP6{1QI*r>vU<p~CVo(rCbvagEEQ{ag_Kn0chwVsy`
+^QoplO^@A!kWhy9W^!#o6rFyukR!e3slT)vZM~LpTa`C5y*WbNJ!>Go8s2Na=&jWPxZd7q@9L#o)7KD
+zj2jT+&?KD<2F~?-h{i1_-^+;eN&LTapQ$tGU0)qQo5%`&`e~cpMON{VJ{$eq?j52EGR9C8fwlp-1j}
+DKL@FD9KiCAHY3M;1Gd6K~{7tX;I3%MLK{7*wXK(Hy<xP-_X(d7yA9^>gwqH=F3~yf@wTO{%TLj;~Wy
+45uz&MtXcv%9DF*tI{t_eNAFJ0PH(;_^zTn^&QGqd>HWnO9ns~{)y?Vg=d+_Ly8L`~d2xNRPjoG%rdW
+~bBe|vc4lW;aFgcS|oTOzP`WK|P1i*Ag5AipNG*4v$MNy0rnJ-_I!!*zCSMg$qA@nUJca-JTfS!s3+J
+X9d*@YLA-vLBF-5<~ahf$n;O_73Y41b^8!S(wz&x--Q%gaicemcTfCOBtrc*r=>=WE<q_m57_f7`OD>
+;-Tv4h|0MvdUAMKEy>lg?P$Z2#azNr|FVrxhz$HB)>y9N-#=hmHb|<JPhFhsZQd1`RkRe7DcwY9UfA-
+e7uF<SMx<Strl@D(Ut4g<*i(i3eKMLVzwG64vC?XCko%OhJ-qw&l6bQ#J8zD49?&!6aV<Q$nIlIohie
+c{GvdC!jk?*uL8t2*zOp5&xt@hdx0DOq1<rgCLH|8q1*`NMhvz)*1HL7G$f({4H<Me#5aJ30_V6MPH>
+KAA-<JeIKn-SZ`g&f8;%D74RLdfn?q%wjxmgJ3^8y7!x6q!^lPxx2naKB7z#M!7))>|@Xc{w00{wsz&
+WXB%0`WWbBcgK;9{hVm7(HUg#-s2gGRU>Dd%bs_aop3lK~$w92|$h5#ku)IKVN&F~$+$fZZ5&2$zpxH
+-_DK2nS;rj$t^4;TVQv7)CIRU>LzLf?))Q5$qz^MX-xt7r}1A;9vs72@EGNoWO7b!wC#02unGiz<4ql
+2*z+A3<fe8;4`R{9e{(edN{%{#u4GbHTV!JW{@aAnBXwx&?2}HAX$KL0geO+4um4?00$ro#WcbYAXW$
+r5*Va1GgRjoDAY4WE^I?g5^^|zXaw>vlsFDxpacT)2;>R~M?e=si3vp0NDWrDcn0AKh$^5+r3r(<s2C
+~)FpShgq!Vll%uaw$k-7#iky0USKs<riKtQ?zY7~$qx(6P>P&rZ|K#c-&7E0Ek3KfHdy5?<_1h0eODa
+kl&{@+dh(rzODr@P6Y>>|2}1l=y8N>OzZRW&>4CZjF7iE0A7=_X;_O;jEK*Sd*n6#Z^8Y`V!o(@hi_(
+R7p0>L#jDV0I8$cN5iF#=dScd~r9yW*0xEQoU8d@0`5CezKH*EMzv7<sn4Ie#ZT~q`-dafD8NCWVk;L
+N0E8XnHX9I2m4|qLiM<icxn$-|JZ&MO@``mChxIH!j{*zVEc@*$<IOC>dxZqzJ{y192<liKm3{Jf$yg
+9+!2jCzB;cz>O3$HeS^7mZCt2twmO3Z*x=891dbH~z6Jqz5QG*2u^R-uM&Re(%*~zqno`!F^&$wLiQE
+1-2!!+9&rO{#(Qr^-dl7VJ>Bha51D_svGo!Fnp*aZGaj-3d9tU0F_K}%0)*uVCU)%GhENq1!Y*bdy$u
+0i%Id3>vqr~nk&meFkw$;y_I?<VUQ7vr3vZfZg-GO(W>1)>1%ZsaVqZ0j^@YTmN)woHCViN*;HoF@LC
+j((4yU<qR_6VL)q7Oq~C3Q85AMXbi0uyC7-*w!4V0w|Rg$)Qc>Ce*>`GEJL?AL;+M`qOm`>}Ol3j{jf
+rU%tw@HWt0Am~@3XtPpRot=TOS=M)%1z-HaSp$qN?OJfU^5$7Kp$p91CT_bcYbA0EmqxJLTBudWt@+K
+lu%3(Nn>A;<SQpGZW>#jcOuGMu*1WZ=wOM%a%G9p~(K5qZ74oJNaSOvPcWFikhUvN%O#a2dRie7uRTf
+;uhl8#r?ERVD6L+D5`i9AZzS7-?wROmvKHzSMcGDAmI~1dNb7e=rlihlo1-%k=XV>4R8|a*9Mz%u0J!
+cjIVP)H_5T+WrHE+Ey;cZu5%fSnkXyP%|7PdKOR_tCKn4zL|!`g+zG#c&3U}zRs?OE3WYn88!ZEU=c;
+D>c!YQc0n^SemT7dkVGogy%u-_%^#u0OgzYmI0GX6e}#0+UFs1H*x6EnG~N4$R*JT$#E9`@>gKw%NB8
+&)T8P8f4sPw}EgVy0dZXS=f1^9R@~!tW|`b)nKc+u(4PO`ZF@?f9?dc1p+(AymRHMv02(gXZCgMn+$a
+j=-t1my|9he&5vCNreAtFaFf+%#O5RrZJb8u_H3qo2n&HX3$_m2Zsg~qLBOq5Q2!P%Ixq;_a9BHCV1u
++7w!?Bo)XReLCmiqHYIJ*|erk1P!$W_reqrhJfIHn!?|_Xe4F{n!)X74>TwNU)epu&uQwv=LHt(CV;H
+d?dgMK@6F*I4w!qGcHdq-||V=i?D%a3<u;Y8i#z+}P9Zfc^*Lcb@PIQ<;h(h7`UQw8=)%$%Vnsci^0`
+=$znGoRa1dK`of2O4mHkh?qC(3O45hr!d+fxY|a)<Wpbg1fv0z8YBHq3yuhS};(%jg0l?>~pil>g;~p
+1halm>?+aDPq)c+s*$&;vejm^;AX+%Kn&b|z^!X;Vd$=n7Kz(`JZmHGArKA%SEJosLQ_nAmff=PtB@H
+7`k`9`E{?U;Xcj3uPGJpn`*t_2fv^m_D(iCK>cC24SM%#S(#C3Jo>_&1K=(pnU3qlCI{$aunrCg)(d)
+mF_^Zw>AtUU-Wx2l}bP>3yoGiFpchkjtPuu;0uNJo3A9RYz-G|ux_rcp|yZF`CC(Xss&xDO8@a+M+ih
+HIO{8DkX>2CWR1im$)yP*0Jv>|j;<Nn>)ov*Kzz!xiX&ib-mi@=`2_h(Vxy`T%<Mqs;Mi=ZD>UyB=s<
+gPS+4mKiK_fvv7XLdH+`@8xJhOd1v@72aEbah}j>B`%3@H%MT>}=mp(C;SvD<Jx1>b#fr%OP!k4RoN!
+%+(veGxh$%r?@JT?-<p8`C(=Gc(a@*(>T4%%S636ai!i^g*n@w4Am<cE2FPc&QG#?^(Ob3tSVW+;HFr
+}fyP(L>O6jwxc$@V^~IYgI+(l>KL!5|g5NuDnQabj^E+vOb!b30%b&_O3gRlbJ-SQduQEEMEdL`8{sT
+};0|XQR000O8^OY7yOFe&G<q7}*3oHNtHUIzsaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVR
+B?;bT4dSZfA68VQFn|WMynFaCyaAZEvGU68_GwDDCL9iNt#8#<Am!?n5S<%tn_tB_6HjBeB~F8UkLxG
+vhtof4`@JrmHbdoXlx&5C;0C>ZzxytBVZp-u3^Z52qi`uju0Z<n-$9ls;TspZtrqjD6?5qv@lf>pXsl
+(<q^nY_2FS=ra2&PLc@EPfx2fo<(Jr(=3TqTG9+-2A-LEp2lU2nZK#8D#?~Ar))uGk4%ieQe`oVmWqj
+uv$VitzRdFK0fOl!%j@=8uA)-S>3&^F+*B3iTGnz6RgWd@o$QmCOb#KUcldyglZ0;d*n)0Vq4KY4UR!
+dKrDY!9uVCh0v44aXqRS}%LN{^pkjGE-3jtwCWkxsa@-a&r`$1I_-{(=j)~qdZrKrdj<u?F(PwQ+&vn
+Zup&EujL4<bs6()q7hj`>g7JYKAI(<+_A9F4kE`BPyy{CM?=E>r=e^iicMhc!2=`y`%W@<lvTX`v`8X
+sO$ZN6XZi26EQ`p|cF)D|X+LiqWrie^q&b7Lo&Dv^5fag0J_YQsaj=mwMtpK(8r*H*zy`&`W`}bWU+v
+iG2k3xPd?z`VC=4_lj19TC5Tu5-^tjIG_H0{b@?aSD)#R<J;TgtLf+W7z?J+3;)%U;-@7-a89r)kJ54
+t;1F<mdVBIaW*mPwzc`<M*67d9r&p(UcXW1rOUHC`d^<fq`E+r7OE;fxZ?5l72Sj&D8HzQTeI&OOFCg
++M1Cw)AMsZTqrhkTe3jj>!^ca0raPv&Xh$xD%M3(DU`7p`Shss_|v4p;-ctL4a`t&W25j%*!4evsV^{
+<bhpACFE5EvAtUlO?B4%5%#1w@}ES(f|sAuCEf^zs;U11UuCiwFZj^yv;Q+y2q%)o(i{m3JNl%Y%cXd
+Rb)&%^ssXnjv_KIuKUHDoT<y%`;VK14(^H+~|POVOFZ|WhG$<32=2BJ*Z!ARk_O3syRHOV*PXvxqHi1
+IxAOE9io-!R^+{^NCju#vV2}mlt&a`B~QF2cP+9c$-c!Hm8)Wvl$E0)B}RlH`=D3NbRaMC><L~?;-^?
+Y77^l5(Vn>6A81i0#yX^o<x-2AM)!$2@}}o^BN^!KX?C4sg`=PTNqZj1@pn6iz4t`$T<{7p{ihZqw3y
+KUkyDG2T8#8}JGYAoYc%3e`XU(N9ODe|tlNe-5AZB}n&^A<AL{-?oD&~$j8jx%hJhyz(R_&JN#GN%G{
+IB>u>ztd0w?Y#`hK_-%|VTZMnj**5+{ZWeGlzIi_-|vekgniR6>lQ1}Fl|86TouR$@khN0SkT;1QZ>3
+^dx9K%el!L{GzTAd<m$ZpJ_ZJ0<MIqQ!~fFos~L1S$zc5{M*FNTJ&>R%%XgFEL(9-;)yZButoS4A6b3
+pD|HlqQpe02V-IgB_Sq-P!eKH2nFE*1cW1i!Nh6+fJ@y0@M9fLxIz%Y$%F|=L(mRILyS8>-@(}TXov=
+Fk&eG1u7F6@AWYEY4l$6>uE~^I5&A}2Aeh4vfB>xtjY1<q#|RzTc0CZB&`_V~6B>=CTaSa_5w4>U0zd
+@99*yYK=18p#=MZh+fI_WJa|2G$CWqhwLM713p+=yi3`EgTU!heKq){C}faZj>MqEcY$6BCvt{$TWY7
+R!43{8}_4HHNDipgUgtNIGT2yF>2B>0jLEJ3DOJ>I@fJC~@d5|e6B@?<5pmO}lxQt3<;M=&w?2ig~Lj
+?{V}B=4KZ!6-Z&*?z?FkPR3QLNOleZe+%i_*DH*3<5-m?oQPMvM+W&J|Y+d!w|x!NTnB1`cNa)i4WoE
+hs_b-sIT^yA3@t@`@Ep-szue$vS}5~UHv4RGpI)BKCA#f<s}4RGqn~bt2-}fr`8yZR#D-Rl}1su2hDH
+5MhlI=Lf$;2bC{8m^?6~VF=!i&-J;PRwz{_>kPLxv(73w!RW^5?og~^Jq9RE&q>A=lbo2+7gV66Hkv%
+$PW1?uqRstl`C|b@r^MdK@9v;`{S{SzOwEDSUY7CwQ6}Bjh_ico7-6n2Y6Rf^&BuYlEXvD}yylL}{^|
+F!L^|@%LZXUvLu!+FLyKpQHc16IM=lpN=f{iRAIb^>!B7*kbBJ&LA0YhML7$o5q7B)C=z0H@F@kcivL
+KBH0(`j605%lY?aeHRT@cd(1_}bt>6*3`SAG<b3g{>z0-0^~By7^=`y>4b@bMLuwO*B}bf{ESCNP*1<
+-K;1Ya$Uch)oG%%U5$LzZSuY?D>sp7q`BLN(P9x8V{8JFwHLUyey{hhMYa}9G6{XNp#-hHVMD3GWu8U
+T>KU#LZIXFK^m^XK<%LX>;h(QXo{hP8%y#6-cDhT}wk*_*StlRx%wUr&7jCcZzF-2x@L*S=-7VxZ7@S
+D7DW2yj8wo)NOBIPB1LbDr%~;%I#n~IBZ}(<vmT|1x7IG7WZ<9ISm7lYGg9FRFCDSq;wv<|$&Ec8BcI
+&o_=4Jg2iL%w`RtE0gXgp%ob-Z8~N!JTI#evO4hENlQHKn}ta7?hdrjPfRdGL7E8Thu`wK2%ib$4gkU
+#W5qo?nx~APAWUc&*GUCbMtYc+_*8eSz0T!Z9q^f~Ij}*xKyw!=h;yt=%?9I3!vl44&QAK_kFTXm%}F
+kv7D5wI)m)7;SDH!u{b|AGS}Oy^&{Mqc8CJy5w=`tj2Cd@Us6I2gZTCK6QtL&kF3VP+oI*T_@U#)U8l
+#iEUF&vpaJH-CD&x`r14@u|If(Z&!*@7Ip<}-+_1Fz<ucoTMfJtF%eNC;3a5VXqv8|rRr4(`0I{+C*!
+dxTJiNL8;hP<8n)T>%k~(l=Xlm$o{{YPk=-vgx4dd!aJ8A`WB04RH<yh#aHE~q%A6PJ^cy2~TnnO|uq
+6(7ooG_4eGRq4yNf=sb>jB7q8H=9Xt9~e?t*T@H`;lU^ZK=lcH&M5%t|o6wOZ^8piTNbMw&O*KJAv1=
+Xvm~T>EZpb@MXZ8pRW@_q>Lky})09ZLw#IUbhJ6ucw_i7OVB;sA#q}tJN*Gjn5^UXrt1OU`h3D2MZ)#
+3GGg2Pb9t)f!SJRkA0>h;iZoB`SvdQ^TDPcw!JU3f5DKxt2yYfZ{x80a@<6sY<2Rt^lnD%<-qdZg)G@
+q{mVVnL0}xoWp`tvKUUf|*IH+{Dzj1Va$wp-{pQN0zcwa4(Gz={xwmV%eP)a4ZMBkT|Bh#+@@&9GKU3
+la&H&MghOQaB#j~}KXtnWL!P;$^4_NODd*ky(W`)4iAQoWP7k&P51U%QhgBK*@pr5qow>QTNyX{3C0Y
+6e?ZXkG*0OMP`Bk~yBR`f|Oz;y6R^;ITprc}NX_}@!5%b<|g%6|)qn=#=Ccw;2;!bY-R(eX;)^H6K6v
+~4@iZU#7i+1};Y^w^I_;@`d&y0m)RS9>3|JsIhTIdK1bWD~55Xv-_l{|Lbw1-y2GEjIpN2)g@tPeKu$
+!S3Cw5bR=Jv$@*+mlO3S4qi*@3trgaweNNMdIaoy%|}`K(**Nb!oNMDpJ&cjT0b9A-^W|N9=X&X`pxy
+X4wq4x$KUl>{-HxP=xMrM#<M87$%<HiA91U{QNze!Fp>Jpi)zpprIx4ZgZ_5&LY1Y;G2kpp3gsI_g(|
+P2Cx!N(faezg`pNr`{`g~&L|@e55vAFmk@sIvO9KQH000080P~d=N9CHTnR^BR0B{rl05t#r0B~t=FJ
+E?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoWVRUJ3F>rEkVr6nJaCwzjTXWhv5PtWs*g3qAm
+QoB!NSo6KaB#~U0vrtOWHKGbu|?3tSX36taDM&Sm9Ja_p24wK`|Y<kDUwU2{TH-5-@AS2b*)a{=|H<T
+u>OGqx+S9o<2i$Y<j*{G1F(e8fJbmI{^LR5!X)IcOh!23&0QHW1wK>EQ$m7=s#PPk&<(lbsCLI*Ss<2
+7f|!BYV;c4QOhul%WGGrJRw4IX#YGt5t6U1HU(hfNg^cB%WUgX9Jgv3GT`E_St>qf2<_gNz1=MRb6HV
++@uR<#bU_`nRj9A3vEA!(>ED<WnpH>)OIlB0Y5`cSGzQB+N&yp|T6QG9*lz`z{%|)m+ocS7mZXjq}Wb
+x_@B?-cm2K-67a!uKtNyZ=&Gxdg^-@;m~z;i>8%;!<ekC{^7hW=+E5x)>VpRGx>3Vn>1!ct5wqBP0h`
+xEG~2t$GIEMyYthpVT6dkF4vkA)EfH-aU}qIsT5n;dZxN5B?nUZ?*S7{_tqrt+f%>QyskODt9}Rw%oQ
+{Ks-H33!1{uOYy4$rM@Ln}R%2A9$$E&apW7fCe$pH*7z6Vz7$XY!y_{0_wuA?)b-GGKN<F0e-bcqgH?
+XaErQF3LM1($CmhF8E`a=QAsycYjhk9+;>LS4@9)u-ClS6K;GNkalhj@U=K#nf?;bk?pl*xYXrl|XgF
+{>RV*%JX^JVC`;wfcc!nky0yBw|#N|O0hyH-&jnKishq?R8u*@FAS>=O^9kN`XRl`7p&pKX&<O#h6K7
+&xG3cN{<y@Tz!Q(d%@*SCVR!mC!`%0wkMd<n1w4#I6dL(_I3gsebYM2b}JTZpaIO|$mNY}8DEiGz|u7
+j*h}CoVoE!<b2&G}UBnSxWJQ(PQ;!j#(&lm>Ozfs));h!iq+z66*_f&$7|QlDa1zdXw&QDx_~1_Nepk
+)M|~V{mDIigG<Ah+}Ze011RYkHRB=kjluW_3QZCU-TW=bLP4DbZsc)}lXS@y)+Jy{>2U|g8~J#&aD#l
+*`blJZ)Wh<r4E`$KSb*aL9KYH#p0X4Ex-cG_PtXpbn>(X1*6N{eNw_zpqXZfD0vG!$GK?N}L1<1X#ba
+TS6cx(`u4RB(sGM>mtF;;~c(vyFxEsRRXX8xbGtcKUiO)QruTFgR^ZDw;S3jSxL41w#`5MI6DDYwDzI
+P+sCGlOxd?!G19PwS+XXCDj?`kjK(fF>2?`k7H=e--@u8HqD<$D*B<B0FtJ{#90zUE%OqwzI~uUUu>*
+QZJv>EJN#hWKu_`vn)4;*++OKWQBC-Nbx3)%$(+h4{Yg@MSLQr<jkPG^xfB-<R{}LyP!Y=g)^0@wLw9
+YZG7le7-jEwUd6aepZg%ig~e!&uX58OK%!Sd{)Xg$jlyqWPaWe-`%@>gKa<Wi0>|)52SPWHb`!S>kwb
+3c@i$YX&mu&QoccE_5dV{&n7<mUB1D#pEmK?wsB$5P6xgMZa&$w8{nIXZ@7_N<31taFDzK2td6o!$c0
+vDqzcJ!{I6C2Prb+ee~|rSEz!&%UunUkMS;>yIVQKr7p+yR9RF!6nP%h2uS8rYYNAJbHO=H#)N~GN)(
+d}26y2%p)?h=^++sHi>_3lc7X2JhISsw#fw(B~R<gXYvBX0j8N@vxOs5==iqmO1V!<qt)3u>r-PFpI=
+&dU8FsaH&o0^OTTlTc-bkTDoMW)m3S6Z!t3U1*4Sh#7Y)6_S3to?=$P~6NOaIwIeyYkG`G}&iQLwCWp
+y^>6=WC(w~@z57<(I>NhRsSo8&lvMHqylbxRRq#2dZ%5U@c@0D(g9`4!e=^cPUG!xRMxNzMMou%PJSf
+LjnU;5lCrx|eN6ZDW$)tm1`bcve=<lu0UyhI6aE3RP4dNtnM2pd+_6nwJbT5|c%t~aV=5tc6rPFEyX3
+&Kt@R|C`(o_D6<XktSboDtJfrN>-0r8$TVuP%8bJLJkyGXECK2I``z-W!S_A{#hEmXRNSl!-Otosw%<
+1Dp2)4><QQi&jqR11g{;dscbw}nNo#3jqY>)*JJEB_NL;2YFjSK%Cxk)pvj@FO0#^>0}4ZjPN+W8ZGF
+F}B(y7<FRK}pQ$Qwnx7L?Zq&qK|j+HwHZRtt1wqU?l%G=Z2u%7Y0koh2&~oHTL>Yc=>=1H5=Qlk}DYh
+15ir?1QY-O00;o{l@>>(oN;rV1ONcU3;+N)0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow
+7a%5$6FKuFDXkl`5Wpr?IZ(?O~E^v9JR$Fi4MihSMR~#u13tE=KR((m7N`#OE6$s*@Rg^|(u#aIBGlO
+QvNxi?m-<Uf#Y1&$f6rb~*+jlPb;-dJWoA!ODPu)(d-5<5-ru*3XgU(<tN*6R<Fg+SMmrBIc(jk+y)Y
+Cs@91EH#`NqaV6D)+W>?mZ%fzyTpKx?I~hmMj?0(Q%99P1Su(h<1=*TDDL*+8rqqUADCG7wH`W$|iO+
+GH<qOapD!^1!fg9MUY!Jmy0Q<9)4CgjzT%w<`3#UT?rfA-qP-IHsY;S{kxt^Ty%2l9pD^$XSB=%C>R|
+2~kg&R~pE8Zsd}_5i)e-G!0U>&?-ZW7AgKjti4|E>dhF+00X0Sw9-<!D$)tItbylP&Wtdrr#v#uWOd}
+;QO*^mI-x))G8{^~P73rn5-R*f8`v*(D5KPi5*303U#nxYv|Hxy`xEN2MSXP7iVboaB(qosFz(8Lm1P
+o^R$gWodw%bHBBM<rbf@7y>&X?dgx<&YGXV*GuQj&OHXf@WfXc%8{4oP7Z&*RnDaGia*;*D;u&*>EsW
+P_<bPWH%AqsuROwx=gu^c6F6&`@4AD!{T<77<D{uBLZ4u{SD_~{B*^aomWeeFpuSFwa+RAq#6DH4Z+U
+VGSjfJO7B)9s9(eD-&palbtp(cR;anlxw*$DP)s+Z@thG8{aP+BI~Xc}uY;vxwv!#SvUCHJHTa2pQXT
+=}%~{MS^ii3-QKibHG@&kOanNmHw+9##+s@dSS9B^onFeN@L{Sjl}F=W^>(zmwkWNvstCz8UP9P8lwd
+xnBU0=uJ2;4O_gr6bspMl!nXds(WrlG{911iO-7KMd#K&N{p3ZuD3#DDzhpMGrO05ZYP69AxCRcr+sZ
+dLX=U^b1Z_Yq2uJJ&nXptU1+lO=Xi4nkP3rhaT|Rol!ed#{r--IgiL)`CmMzE8M(&>@eC-&u1Npbwba
+}g-W^miYMM!@l$!qFs#ivl*Myckb-WhdQSmz@{{amk7{iU=^)Zl)(kFz|0><}1@lhAwXg1k09itLN3E
+c8_b%=!K2g6Hg}o4!w#Sn_G@D|CC0j+ny?Q6+Jc9O6V5cj201R^?2yQ}}(A{`_DE=3GX253L;66s(DH
+xM^^5%9i54+|+ty=KEQ`RF15{-jRdr{bbj;ZHzu)aA&gxO+%g~^E@b{Fts)7@QYw-+2UvA*K)K~3XOB
+mSNcMm{aWAxvrmq2jb~UTsJ;@KT9%)8_NV%i2XFY>(s@8eU%MXX@H2c6ydFt8{*cp-vpvq!bv|x(6+7
+<#M~2L}d}QB>^#GpEPS&Wz7*^|s`2o!{hM3Yvs-KyG<28D2ydY9Giddx&3<`I2w?hXWtJ@0lsc~!zsQ
+y%t^|Ih2s&OeXERC1y=jQ74+2wZbu7HLuA=w>3#FtX;xcOGBx43fB)!XbN7!A8T-Rts4Mb5RGveL<XF
+~v`=DEkkiu&~5~rPV5uA*;Y=@%up%qld@gxHTD1#}C6cUb?rH%6}R0orz(W7?msi<n->-IXS$C=I81^
+si*WeP)h>@6aWAK2mtey7DvvEk2_fd006HC001}u003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvC
+QV`yP=WMy<OZDM0{XL4_KaBy;OVr6nJaCwzgVQ<<<5dF@t7>O@Lq8!kx)LwKSj0=>OfMpv+(dmR1dkx
+zxUTb%q$oK2-t$`2&mF_G>if7)uoq2D(wA;5I8jYv31<hyvcoB_hG++8Z=oi>_t4;Ba(^8AA$eAQxrJ
+RJJ2lbDTlF=#`FI*dBk{#2ATS~d*iB*~sKzpqd!z>q80QQx?aH$HeNo8bjPy@f<)+DUp2<`V}E)r&i$
+_-w1p>+KMVhWT##1qY#<tc5dn(_K5rX5#N;i#RZj(<nPet!UpoA4faQc~!!hC*(*e&Ok15?|$3i%p67
+I_B;VM2H@kex^XktrmOwK<LntRTNZqr}7#R?JE2+sa#&Y&08x}0|Kp7Rw$9%F4{>>szGNdHk#?mNuFu
+W$*9b}Vw`)bR7nZTN%K^gLsO8?63f%ylt%qtr6Q|bsLWG{;AXX4@6Ad1>0(85ZZIEBdCoO@3d)TX35w
+?;;kn_&3>7YBc4vJju94{22u&2|YftWp3*_C|ZYL0-VQ+9c?a0`L0MudD^^YAWT;Uy>u1LZ|eX6`I!F
+ke@L|)VG;28b^AqIWLPSS=`X?Rx3E*OBN$65Ssxr)hKJkg^UhTbB6x(62iAPd(IOk!V10b)$0S#B#d4
+uXeq=zl|nH=507@ssO*GK&}ED5A+SB##0wjA#C8?u8Vr!eAMVdvKidlf+KUTOz+noPlz$pd`K=CZsux
+{sems8k8yR*b9fv3CE|F62saQ)&KHPs(f3|i;}aT_arjP6;|$53+xVd_R3wboc%rQW|uw<0Al&Ggar}
+GCn5v&L@K4bG*ZSo=!1va{%|npe;9o14~SL~BENcQy!iSbZ=`msm1%rQz2k+m)v7ECtu~MV7P_y`M<(
+KS>M}u{IhK?*``m=C;>qU*#QYSd35mYXq6UPQ$<K=u$1DGP&;V^W!;gbu6Ez*V*d^DqzT)~3Hb6=7<@
+YZaBL)aHKpV0B)WnPsxjf9iUL6)c9zm^EBALP6KJue?+I6nw#Lb6W3k#dkdM$8&uGbyIWp<3a=enrZg
+uWr&TW`HTfDHXff4!yLb9Sh6*KK6Z#$0CJ*>RbA@4=0-lk%6TcY0r@&NAQNJBiyl&--_Ca*ZDti}b%x
+O9KQH000080P~d=NAcxnnoJA;0ESEe051Rl0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_
+>WppoXVq<f2Z7y(m?Of4v+DI0C=PSC3ha^>ql>mX5R32;(&N#u23vi|KG$ll1Ye$y6lCZ;W?Z5ABwFI
+}N+d?*(&F&<?q>WCW=5|XuhuhsuyG=jX>E!*z6`Nk1O|Iq>b~^oZ_K|7owp(qsxZ~_o?BDvK8?duz#h
+IV5`{<v35V+kazGWU9>$Zxj7onf}Q1e&*$b;yC$1Ga2lxPF$U-2~Y+y@Sa?(a9D@42ZTg$cZkAEG#W0
+}Zq5D3-^)n7b)ovE?>vvHDSNEZTb5!bjbu?Dg3jW{t-VG(lGHvXdZSGf_8TGoJAHk*{P=&Z01l{pBW&
+;@8QWU*HI1mu~zAyY_?I*uQ56g9uZWM(lc<-bG>Fe;As;U&d~{6{EC{IcG_<PM;vKw`?12nCFHp<|{v
+u&BH*X%net6i(=s4M=O856^AzA3c4erPI-Kv6l3`Q>V{2u0+F)!JmfL-=6bUXd=JP|-{WDznVYZ&aV)
+vpkJPycWS$4Y&Le0(8{M~z`+$qje&lfiM_8|G6`z*3*kMu3Uc0G?AI|23Aija1w=96O5f{uZIRyKju9
+zQY&ECP-!ynKfbo2=>BevvhlkoK>=s*ilmwmoi{PyW)!A`Eeu+Jy6*~!)7%Uh@mBMm5=KiQIh{}A}lF
+!U;R!*mOQLj#wS+1YQvI61wTUM#+d=+7?}SCjdioqw9K6Lx(vTU?yoOiyO)`et_hX+G&PHs`z;VlkN1
+An%5F4NcxhFp?{tx_*$zu782kn?QiUiru-798R;xeYm2S3%AI__F3mJh{D^fzd+d!=v(HmSs0}q_7wY
+Y?ZDMnIlIux{`_{}qW8KTW?N9n4gUz>6wHBs?ysTg^B{`i4m*vKR8+k@0j}AzEYq<1rp4IJ9FFK7Y;y
+H$LlJ4WTI)EvXI&3&;uKC)BF>z+efT_eV~B@O;#h>jFVf_aw;+zwi}!Y`CI0>03ljE@y>7uR$gGa~WD
+G(!;chfX0&crO5Ir&TD;$ISAmFz!x{q$K;T5(C&y4K|_FCJzC0e&e2sc$+@GU*RC-}XQ9vA$cp5GVzz
+MkI~{J!M(I_gv41Hm7R^tj*;^n6?JZ9U%>d|S^S3jR>f9}50Z&mRf?h~mrsj|6`t`Gb!76u2Y!&Pb07
+zN6=l1%IsPj|G2R@cYVB;I81ilHaFr!FShMd@1-#J%1_qOFiEcd{58!1mDy1SAxIN^H+ku((}3Cb3LC
+6KG*Ztg1@HtW(EuTdfj@Hi$#*|v?3RkTw@BUk?j;1RkTghFh@!gd6B78eNi;w7`dj9Q5-JXLWM_JxBO
+fC?9Pp+JY2ZTfQ$ZtbXnO8?h!MuGrzoqFIEA-|9a&GZjxlRU;V-g5$$C0pogq>?t1Vo2kP0?3ZT|0SK
+3KZq8SvOs8<TON(kM1QSp_i`Rd<7?$55rgK?{s6`A1c6`vGwvqr>8)i!HXTQB3!AV}RV2qoR1ghUGUU
+Wjb;WU^2{K`L|c2{uJQ2X;~{2^F0B1gX7}V0MPelMTxUc48|D6`UeSbu9=Z<;nJcI@xnCr;;$z6Y>K)
+{j3tk!e4qIBW1&pf~hei%Auq}K7$}7u@S*U8kmWkyhC!8Yr~!LE;~8$#<FD5lSN+^16kOz7|LQK3r7}
+X@gWZ}fAphG!m^v5y(831Hrvq)>p&KEQAK`aNAx+XMTzPQ3(;owmu$0vG#@TRoAMhd5vMBBWMq__3n@
+6<T;oCr#WB88ZO(4oT%*nocULKGTEelAd&#%Gf}=IDoikM2q-<lBNmNmcthk4$rc^7d^n;)jW|q~JZZ
+^3&Gd^6mT2OwtR`M50`1Aawf)}!lGZc!CGn9g<;3z|(_&7r$ag?D@IL=T=9Azka<J0qThC<;uLm_dLp
+-?!^P)HnQC=`w}6cR@n3T4YUL&2Dpe*?xD3dP463W=i(#U|_sXDF0i;S7bsafU+TC_|xeoS~4oQ)9@b
+428r|hGKTqXE{S5ag?D@IL=U1)GlQxQV&pyA|HLlJo`Hjt+5)EqtyFpjoYClrR<;TC)dRx(-bO>8dl!
+VT^o*(%M(+P#+KRcnMQB48{)F_^a`u#XSQP&^|WrY6Yhg<GdyHA#W_E0HkHt8%tkuQ-fce4vMzm`9Y~
+%^cJo)C5TkHxHzbbihQhJkkT|j%3deRs;>d0&9NP_vBfFvG3fm2dBfFt+Y&Rs1?8a<tH}r(qpKmur{`
+U>-MxXuZ`PgnK9NUeGiY&0~H=qdGDY06y)lP{Wp8cZqn&oa&M5Rv-iW+s|EOqXnI1d_#b2dPY#90mcf
+H;4g?ZtWVIqTBmvyuu>=N4<Ta%Kr_rubN!3#Ni2ZKn8Gn@JpLGlgSqCh;G3v7&dBHV^+4ZSGf66|Bwb
+bf8tyX3Bf8Hj_BgW^t^a{r7_`R#dbZ1cA_IN}`EkaCo_*abAFzD;lqHs9*3=BG6>=h|VR{Jix9`1E{1
+R&jWB2&zvS>?Ve-w_GgIjDHF|IiJYRST4z{N`wtq{sDWW+S4|_sQlqaitgi;&ct-jpE$jVhuW$8QEm<
+~Qe*eG7C#ZWCdsaVFBz->dV|f-eeWhFTk$iGZ2zi!%2}sY!o<-r<vq&6y7KLNaB5~wd6plTM#F1xFIQ
+A?ON1jFD*t19+c@~9Z&r;EfXXT1;XpNm-8j$zb_ckphUOamx9s`YCG&3z?j!S1@>Sh6CS}$88LZ;Pbi
+%9IJb3JbpgK>1S9by3wZ__)m11<9#Hq7=RLN&SlkM+bRxq;YgdAiBA<@t%&XU0PA@gxyj`O-*i<?~|y
+-Vep{;F70LJl}?wK1D3?Lr?5IliW?hSJ^JPb<F><;i0!>Zs-Qv1h6O!wjHtOkv;Kj!{(zK_$xoW1vYo
+RyKFxxi{0=x+mIJ$I~t#O5Omo*%G*!eb+-F0;aRk)=rYiIOw{k@*Qs{X#O~GGUDI0c-kV_6KbzQh*}!
++V5Z#>W;njYStir}+b!p=BJAa-2{^4TYYR%!_zm$tp_|I)}Iv20b3huxE2)ti$aq3$Vc5w>FE>7ae#o
+OjrFm1Sv{Xp)}^h1{X9o~abL_!HV*(Ts5y65b{z2ym8MLdyIFA7sv)Zc(oEVn7|vd<iJ3iKvZ0l6=i3
+;h%Vbn8Fzu&W)lA8F~UdA_@!4|rL=o6R@8!QUNn5&Ba%=e=aztMT+@*>88d^F+UDcjw6&O;JtjF@-=M
++MrimYf`->ALD}?^vW^oH|XtLM4HOY*}P|*PFgMT$LT~d6>7Q(n~Iqkf|*T7Ql>)jv8l+TeHBNhLh-S
+wm>u;3Et?9($EHH!$W$mCn+k~|Q=xEdDkP3fg~G9^kT^0G3dg2G;{Q`iO;sGrJBcHCr*JIqB#z{r!m+
+%QIFfe?$MR0%-)U#VK<sQ#Q^Q;lK;cW98*HiSqoTm+(Qe1Xe{*v~c4$Z!`ppe>tb$CnU)kJH*M?@SP=
+ct8sY6Fkwx4H+2ji$+!t0wxf3!c_+HDr5{9{*9>wJ43ArhOzJ1t87x4Q<(##9fi_JAT4K6tY@H>~051
+liy;x54R88~ka5KW*@*4eo4kI;n2)Q)PCfy!(DGXqO6lda~?-8W!<R?}F+GI7*>uU+03A=&5Pgyr{Tf
+qrTL+;8gE|J;ep5^)5L5rY<;rh6_%g>4MY31>v0oeOsJ6&3~d>`hl+d!~H~;!hfV4iN)-cGPj8Qk=j8
+2Eq|g*ZICbTda2;ZA1NIBBZ-&%@q4Zy7Qdx)&~NInKkkw79!LIY!L4Nxud+{Cy^hr{UW1Nhi@R9>s$B
+~4(9a6ixE{+tM23XDQar{vZYjp1nvRX#w@>|K_qhe$1M}i0_REPc{1F>oPJD3_JDaR?Q?)2{M7rcrW6
+^hve$5$a62-tY21iCQaEyU=EJk!uYQi=R`^YG4$FL7yowZR6O=Eau6hp@tYR6(s7o{dfrZGA)ijiZC8
+sDjO6pm>)M@Hc|hNBw`DNNO()Wp~{#z#gmc8u|}#-bX9YZ~s6QMiubHXaMQiKS^QkBnmJ7)x?28vm%Z
+?U{ymR1~c3Ifl10H+pHvL@)WPB>Bqz&j2j@F}`rAVcv*7amc@WA6huF4y_#JK74THsN5y}%2AdbK%(#
+;P)h>@6aWAK2mtey7DxIT6}Wl^006lX001}u003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`y
+P=WMy<Ob7Es?VRCb2bZ~NSVr6nJaCyB~{cqbi68&9&#Q;HJd+WM(cC$dYn*z3@B*yvcB>NV*o*^(Y9T
+SyBm86sU{`!5C<S)t2wZQEf2oRAok27y(o~Vn9(?9CCcfBF?dtG<vyVU8Ay8oavwinI?O_rQSN<4{}M
+bwpZPD0Z_{#Qg1qiHOjxzgAPmrQAHXwD4}jZ_rEwBc+Kda*D9W;gtqM>64x<buozXxI<A(IHDXmR75E
+EJ9|4j5QurB31eTU>Zx6t%r&;!{_w4NfFCOF=erm4USqGs&;F1*=)7|anig&?I<G8+G_H+=IWWxvrM`
+&HcC9MkzZBUe!~jUfT=Gu7SWRuD|$zW(2$Wd-k7C~Qwx8&!5@pH-Iu$3Q-*SaKxrf%6;m6_?m}@+S}x
+2BBL7Gmxu%fCr1)Iu?0hg~h{f~wQo(;E=VGz3OY3-!d~I5WtCil8d^em@pKGK-cRc0_$B);KkqF`37a
+@-|C#ET}YkIk-^4138=Yi0z1bj;WN8$qe_H0{ztk7knwLL9ItRq*dGGpUMxfAPHL(m(FP%gFgY@DQEp
+Xr=LoWd?q9Q*@7B>IB(qeo6_%@^ya4g}cJAHB)_XgZ<x@PYnldtQ4udH4uhlmfdbaE2sSNhAP_tQ3pQ
+27v=$;CkJAc(gmcesA(%qrdG<hOY0^?Z~4xjoaR&*PZs;9*w8oc;vec6qoZY#g@!dB=0C*0CFY4B!+~
+ENN1@(pu8FZMsr%SXO1$598)zXh7L(KKUBj=#!qR!aM=_3k;H;xY3lT%1bPSUS*k8j_Vuk}D#Avcu39
+i+@s|iC@Zo+d7J$Buq*Qh4NNud?pbg*VWvkVE*ZR5HBAWVGIrk%Xc=Mg&bKy9sl$|HN(au6)ej1dogc
+y8-+HKc!`AL*~1VOn`7mGeO<8*OrC|8MXq0Hh%u_Tac?l^vXFz&m7-}|>q*YxWzj^7E~;b?kyA9!v*n
+B04=e?RKqV6)Y{{KY}}dz0S3;Bi0lCf(`e$fVi$aODi$yY_1)%`3-$7<5Mcpxd4VBk#ubuzq>fbcWMG
+$Mu5IZQ#5Aw7Xb8I5Jx(8@(fl+V0fMs_8gR7%{EM7x7cXI~&7~c&Q#eLDZmFXwU@(fq*myK~?i;QMV$
+)qdL0N&Mlu`-?GTgc+6HjbG;tQnCJfXcZAzGLaXhJ>{BCH4J<gKO%K^^LKeKKYn%6lQ_L|E=-fO2JI#
+=agbB43h}4<ToL|;S@c=Im8)s}YI5=jMiW4S^t-py#SPjZM$&Rc|5sZYw%2g3~xXT~VdoqsA6y74HT6
+1>XW(z45=LkD6If&HuIh1Lhy+Bb{lP<B)6weG+ee8}>Q`=Qn#ciK|?PB8LsA2HAIisJ-Jymp?NW&s{>
+a?{^ZQ3l3HZS%O>P){E4C0NV+4q8GR*a!q(3vM*M1(|r-2v{{d*UX@2R^Wr7l}l}l&Z7ewLitpTE%6n
+MhSXPK1aX5hLZO1=SuEvF)Oou%nP({K1r;zE<_7$E}(qjJaI9vcpdk-0d<wx@!1vxtfXEG5y2wJ-*W-
+a{DLTT)YbI7oipp>EqF~4j|&@l9CrVFnwB33uQ?>f&PZtMY4MI-W!`>eiY78`%>hd)v(H-@u7<VQkw0
+R}AbzK!R30C6vJ2lrtIxvi_5Li<uKP+rIaq1X{s(R`%5;!Uf*%y5pkSv_Q+p6yErs;cVMsN4n=x<}WR
+)xNam5mQDVg&)<O7C#**DZo%uZsYQ(}jHtJ7AQcY<Xf^0wTnc0sRCCM8d2Qq1))ORB7xpHs+Lxp*hEH
+zT~HP6wkz*Vp?SO@rU0-%t?6--_K^YMxJY=AKe+lW?aOsmRHAA=2azCi&nbSbw5dE;*;$&^qq&2<H58
+{dB%xajR=+18!6!y_Z%iZ#3=WLbPvm-M;$MYcZ>w1uFe2FCxYj`9E=m-h97T^%WtngOFO8s5FhdIczE
+EsE+A{x@SsWJjw|EonE;j{%r(5;<NV!*Q+O7{h#@du#B%3WkP&enI&#3%caC8%p3iGX1)IXP_NPX!^I
+m)GvNEFJ@*nkzLe~Ts&FMy*^R$dQi0=PZWi9WR{W~1Mibh1LW2%J2xc{UUqURW{Jx-ptH~x2A&bUR3t
+N8Ae*aZwwNe;rAHwdn_`dYYE$Wq`PY}pPv)rQ@As;>z30BUZP)h>@6aWAK2mtey7DuGq<-a5Z007$z0
+01}u003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<Ob7Et2XL4_KaBy;OVr6nJaCxOx
+ZExZ@5dO}u7^z=CVsnHQz13Cs0a&1Dfg%E`s?`da#35FVjcljg+^@fJ5=v6S6?&GC$jm&?ygRwMsXov
+fjK(vVjQfL`H-O&cq5lKU*xuA`U>Q+(P;AQt;n0@>1*Tyt|6!aHSPAw*l}3#p5v8etfEwx>slZ25r*>
+c%3uYLaeWx#!%RN;fLogN8Xg{My`(#g1wA;mk`NS|OG(M`mROtf}!(6IN?kh?R4PcX`jHj-MvRwNFT}
+1}!{RXsKEf*;&%^m1+4hw6mVL>%jFEq$9=}Tc0+r*e(T{r$h3BZ)7XP7g-Rcr@e03$SDB+L^NNs(H3Q
+G!3hrPa%~eA5c$1;UbYwoybSwz{FB6toP@D@J|?iHyM~0u&9H&irGg3=k3gAr<0xGGJk1MX?AlUz?Vp
+YNroH9?e!Tp&C<x5fxOS`+2<K%t!Eq`BZ2MM8n?7baX7`&<5h=fxu8AeOmrIpbYiaw{1U4pzXK^Z#iQ
+VbA>uFHhygP-U1sKbOIdPr4C4^vINIW17IShjj%cR2MICJR~$dsP>3}RW8Optw1vm<^8R79gzoGK9=n
+T0ceZ@GLtAVGYO%o?6Wi@MLt@NI5n&Px90{g_MgJZV-QIXIUOw6AhvVgJ;CV27SU?x%-Nka;Uro9Tn6
+DP|2XEkDb16NP*cP*@$ZsVMk#Z-olDH&<ah;|9gzeQBAP*oSFBIGCQ{1Wm2u{d8xvGa;ifx)NLXHK!0
+~UgirU|c#;q2geo^%&kj{R-oR`^a6IxZR!@yxLW9>Rw#MCu`zQZ=C`wXvqtE@E44*KK`qzqVX}m4}jZ
+2L`k67hXO$wOXifX`K9S*(w?D_eyTC1lU5mXnoXp)QqfVo@#Q4-sGUNy~o6s(_Ug|jx2#V5nCT`3653
+8oud2zt3NW~wmWV=S5B?62|pzJrn3Jv_~1;iu%h#hU`qV5<U^YAX7}y(Hiz4XPA7e66jIn?U!P0yr_T
+QzS$Pi468d@+74VywZ;4_=oN{z}2VIHBEsG0WQ7q1JweJtQ5?8A%E^y)Qvl7j6?A^9oOBA2Dr5i<GOU
+4;{=RVaG?K9i*oMcw{aMWr(Ct8Eo>wASstDg=Zz7lMc0vf`4&G5ytUe`6{VJ^4l6y%gm-z@Euv)*M!8
+vcf#rRg{`%9W$qXkG+8&hv$UilM#``F`>*gcZjB3dCVYu1Kn|EEmE~HtMPxmKW$Mm+q&qRV#Rv+t#PB
+?^d>e`t|lNDl8w4eQ_7-$MxWHi(iKPzHI#8F&DT0<GZ%{3<<a2HpL*1LA@{?|DTv(sbc!c$f_eMN*#P
+3uG33#on3djUQxwnMHc67C5QetR0Q>(QHh|6^`oRto&IXn%5D{Yj2j$*e*sWS0|XQR000O8^OY7ymtp
+W${R992v<?6OGXMYpaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4yiX>)LLZ(?O~E^
+v9JR&8(END%(cuNWyGYE<fjRwrE><s?E#!W|HViK?g<p~W7<UhG}8yH4W%`kS>4IJQaC-hP64d1mIB*
+;${QG#@x0UX7+O9u0<5ZwTk(o5A03#P+0h0*ej8jpA!ASOfzZAaD(_{LZ6@xl*mcCz;#YnT`ZEoGdT!
+5hJ-p1u_KFfI;?CG}>odBvBkEg8R&HDK!06TdA@?1Pn8&a=EXN84O^RW{CArOj)dLN})CeoIx9UpFjU
+W5RK+8^rHyo)>gwDHL6Dp@=69$7{ynKk;>8SFC+n&F!ca49<3FR;R8^H28@JRYBo|7^%FG_Un!<i%Tl
+NiLCerQQDUc%$^?8SKw-dj4o^fFU?TWUD)NtIz{Avv5)n{2Hfw__);kWbrnfLgO_{<K3RG0hELla|C+
+9KuQD_9FVQXc2b71PyCgK%|z@;SkjNMZ}PI?>r5tSwhd~|!eXa!;iqymQ-n?H4PYaQE^bP5r5L+w1>M
+hXs<2EauIyCLrB2LV!{Pc%lbLP#`*Nz@?(vW2gs#r4hY0{YWC_}ZV(`_skUDcKU!q^17lNIc#~oPen+
+#e_*IaRQhO=YwnV=%0_qqs5)g{&KXK4m}SpZ|2a4S%1D54Q|K%Im~Y7vm0;d0(gi!iY=K<B=0E>2{M+
+%WPpb8Nav;BQF}Ed7zMClk4SCyk<&y0qbssaf2xO(6zi;Baybxs3OocMO$VM7r`e(Dt96%94*l)W(ED
+x&e(#YH6Aux!z$5od9uo9rB&F)Wxzxs*PWt5A{n+buKlDCzdjPi{Nsb*DPA}fLL{3^Q>Xf}MI@PUE$r
+#*t<!_kMXtqkOh>F5sl0=4|rv?Y4_0Y^RZQ(<UhKsc}G<7laH(cP(ZIEd^tC{4QoeRP2N6?OU;o$u>@
+-9m9J}stH^IdlTZhc{%%f@yg;IpaNQ59OpWjk*&q4gFO`klt7?PX{}b*t6#Bc^p`n-}La*Gx-?b`DKg
+2+Jj>)wNtY8l$j~+tcjmzBJ!k+kfsdE~uKoSp#cMsf3zmjj!+2x3i;EIt`%@93Fft%vRkL?PWv4JsY{
+;ZB&$Oe~?T)&Kk|Kn9-$RF;+t=G>H<&;A-9_<Pb5)&^o}*lQJe7(`Gw^2!)er_6oakxLu%hi`VFMJDF
++8(;olOPXej*+ZB}FGP^t(_BVdte&J24<psdmVQO6*Dkas+XnUWX(K5hQvaTCtq^H`|hTb&gW~(bJzl
+o$K=h}G>@1WQl8+t>~CI|GMtY`p8^&V&%9sYK(f5f}Galzk?&Jr0@X(m{+U(v_O!wV@!uW~vvdV+Pjj
+>paR`pEqM$Rtyy9@(}cAHeGDWQ|IcLXkJj4rrY*ipg!H?vWYDLB~uRdrQCd?kQ3>irxFy9I97qe|^ee
+AL)y%$p6cI=WU&@m)<-wuk63ZWID&;FwzYz|535|eWgk5m8!J=KmBx}+1Z2Kzmn>kDlano0KF`q-Y-y
+}oqZP7HN9_ecRwxJlB9_D2V<JN{X)rCT)Q&z7iiEoG|gYCOV2sB!KGQEkjZ+pq>m})*iYV|T3u<6NfG
+h@g>Pg3Sqkl4-^>?-+r@HmJs;Aa@kP7+E4RM!;VBhddASbhRNgJpm}fHx%h$Z&?JAYCcP+l8%D#=GaT
+K6PJ!a3&E{)CswcYv;P)h>@6aWAK2mtey7Dug|Hab@a006ie0024w003}la4%nWWo~3|axZdaadl;Lb
+aO9oVPk7yXJvCQV`yP=WMy<Ob#7^PWpZ<2Y-DA0Wn*-2axQRrwOD;`;y4rkpHDHj)rz18atZWStFF2d
+P|~8K6am#etF}TRF~!;tN4B$F?(MVRI3Yk1L%Z#r)k<K`{5<b7=Hx`bpwqkT4`I;n_J)%lbOtxwU*Q1
+lN#z8lO9D5XKGM()pv!y$RKPX+od$smw;}yTxWE%{>2g6N_(T#<G7cV+nw1QpA5uw??1FqF0gDI+wg9
+PtM*1O<!gC{nTdP$ZQqPr?g#y3wi1FkL0>g;$bl>B|mBfenIzcR7<#L4;t?{a*1fyGnre)a(p(Qt=9R
+x5|v;xLN5dMw$sU}?(N>1l7>Su`B8SVgFyZkGRXz<AC3L1bCN{|djYq?}$LNQsc@!t)YI%ivb6ANt&g
+eDE>oV$Fjth?ZZfM5&xgpz-PHH*P>L*T@xBE26iCBY5-pBYE~74zw0t+wLOM}3uAlJJ$tjC?t~g#i($
+3S5$qaJ+sL&jacq^MHCJ6a-uWk=hf>ohcn9$Ydi3I1J$v^M3+D@m$@l+>blZH0|uR4PqU21;dp}esp)
+F7;7l>8Ul0|&p1u333i(LK*I!fiO#_f2#AV4VfewEKrF~24(f=2wD4PhdUbO<h4%0Z{MH_i+r#OXPe_
+Zdz*BT^ibPjYKoJ<V;%+F{C^!OK_r~2T<Y;&LgZ}i3lHTc0hrP)JoSQMUVbmT^``z0?dkmx7@#tpKGt
+s$(WF}U|)ERke;srvk7+MmO#HE2qRsVwS6)0ff!_xgm(9Isftn$Id2#MA|<imi4kBPp>vNPx>pbH3@t
+iuzh7&{oACGR52&cAic3eT*=FE$dn;nx6NFhO>QE)d!Y7~^&5Fd-G`bsKrDrfpjdyJgt`w-eksw4gV<
+cwzB5sZ<smlg3mIQ)%U4wo7!FsBAXGdizl&lP5L|7+^HCBhKb%tqs8-*&C&MaDr9}efA_8cJr6!hHW+
+X#3N^Ml;^zebSIZ;d&j(r14$e6H5yYDKG#zLjpZ*BK<JSsWh6nv$ABDxBErjq^n}+%!U>T(l>X$V(X^
+Y*IuPlZnN85EmFxbne|>vBn_i83ldGG-1)PIrHmwR7#hkQ-N2%JW5QNRX*%fky<u8%x9GaC%#S2^^cG
+do3Q{>MoSl%$27ciSqtfRA;AxN;OrTc2BujXutXdNmuHU#Cw^w=t*0Ws#)yZi4St;YR#$9bsM3aqI9A
+vWXQYPJ6S`;S_QX;re(ISg4yiexz&fkbx?c@mE0UmCT_R?l=przD<WP$0%1RT5$}qF<|L$*py$V&pEU
+kNX7oEYtcc&+8)<ayo7xN-|EQ%N6XD$ECANCNEs+9%Ai>Q7n-^apTV4MY4e_q_PAO&8yYzDb|bw*4$}
++#N=DsWpt|gWsw}}J&*^E@;F-`EC}%v7u3P+#QToOL{|39V{sy59#*z{?f`qiIRqp$5{!Katdz<5afA
+(F-V=Ei%*(WP`^rf|IZ~w6xpQ-k#hAd?kUgn(hPC>3>g1RaUrBa%)_Q>R^G$s-k#UBUAM+Qbm-QUzbf
+Dva!fJfn-c%wz60h$P*2biN`K}nY_I}u^MS`u2ry3bs&Do%*lhbj)>49abzeLm))~$d_iFJ^KKE+YzH
+Bz^lgWkU(bVn<6DlW?HI6I-|^zm&HcU~j$yo#l=r^@C1cgs@$Rjrv5Su-<tCymoO&WEvxadKeTWh72G
+YFb3e!DyY@&DwJcl+M-4Y4!hx>O{8n;q*fzxw4^v-_`Y;MZHT@FEB)EfYcaHPiu&c>yoH@)(}nmwDq4
+DLIMB#hG-UJ_*lv%ach6bk`3Mvy7q@3OtzAs`ZU{1FeNA^*xRH$lwgNDX|B&^4JEzfZg;_hdVPVBJKV
+eF0Y0lXU(<BTO|Q>gqn4(>XxjLGA3rPh3+=FF3gU8aX%t#zLI3=ykWGJOXBx&cLa7&?uZ{B3JeNsU97
+Fz>w)K|^n+>idpYF{yNenjZvxgVhH^VELcdy+apNRHk&<vu*Eu*IQV|k)V2A{+>X)bMELncXHZhd(`3
+Vnhcy5y0_+2+TbS?I2aQPUT#y_L_eqrh82qxP3xT3vo5p{Ms&7OKa*b!^Do^HupZP<Z*FDT+_H7g%Sv
+_Mf0e?IFkb!_)Cb&FHnEu~l3j9?`3#L9Kj5L@y11txSiCWVYs<xL<cK0xw{K6klb}J_hy(pPS^7WM|W
+vPv-HXCXCj?6cQ&1ZYIC87}b!aqks03&QJGYwRTLDlJc)^2S;36brpH_+3uanwvY4zarF6^MnHFaUGY
+7x*2?`pDwA1-X=qEOgLrKcDfdSQ3b|TnC(K+;ENb*R5bTF@caQtC&Io#u^wugO9%tP$6k~lykxV(dyF
+Nqdm!(4uTOWm6(6@J-0@TqG^Gr}5&vI>sN1J}J#CQCPhIAFL@X?Rh%Owl^QpUd^M(?Z#bM>FZYgvK!0
+=)eQdM{97c>jEnH{R4C4rEEKR`~}|O9KQH000080P~d=M_>COYaRpu09*<H05bpp0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppofbY?hka&KZ~axQRrrB>T+<2Dd|*H;WSFLvvwb{1PCn`Qy
+qu@j@dL6Wh^lORhYn+Qc3B$d?t`VJ-eCOeC6t0!x8&dixJLnc>Ommla2o<>s`kNShDKY-qN*8dYez+O
+355UdeqimjN4IP_(Lz%*p?Z^n7#O0|NRVy?4;js!D|kdOF=Ty9W-Ou<~pLHH>e9Y-6aP?qI_#gSoBXn
+Iu}sfrg$40EZ<c&soon80#dWIQhwDVuAvrBZ7HjeZk4cXzjx;v(FIF6ZD`tOgG?sy9r^PWn<9#g@5|s
+-c@-DFQHw)GN#xUn!QsHIPCBM#6k+)>2gclQo<zRiw7orBoq;mZ^CsVfV0=ImD3wg$dJTdNRU5B$D5x
+B7P<lmTqk*7YVgvRU1^9-g)>mUBDPMDTOB#sHmHHzT_+>@|eXaG(x0dV`F-KaB66U_>~YCN=je2dk@I
+yyRH3(N>c=G+|I6Am9Yg<K_fC&KTUIEiA@r{1y0jYJEnWlf<vbXFj1skllSz45>lh@v_`N*$Tg-pZ&3
+!q!uL_|JX-|NoxZ^LuIF{9!OJ~i$!YpZ^D8s4Y{MBPrmj>Z%$9^xg2}+^KNF+d8;wW7i&Z}y1=E4=!*
+J$77v^0r81)z9t_SmlH=p?f7r;l{S!~U`Wb)qPlu~AroJ`O}jO()X7aFf7fjohA^oBI%7#VF8M07+p+
+n<^tmts})OO%75_rOvR(zM`RG1?v4zO(64mcx8owDhstf-fBcMdFpy2z;UsSxTvgTuRk~p47%dCtc#U
+Z#td!b?0-t1F-NZ^3el>>Epk=M6MhsRWbwD4$PjKm=3d{f$1t9uOp?=Y?NG*D;20uK_AV$2=0J%K*Wl
+;2D2z$Gl6?_@x`}aCgqokk}YGcvmI;^3~&9(gl-`2IJ4;>q)qX{xi{;<1GL>Qj^o5U(z*bar5h(g=q{
+kEfCisJ7&5w1!my!{r&ZiOCwTW1wFlWZH_R&86RLjRLE}hmjj8CiGL5FQ7gGj)sN@Lpk&J!b+V^Rm&C
+*HF=CeK>%C69a)$~FUWms3B(Bw+Mr`)91x1SEY6fr5VEr@#gF~gp%*j^PjehAwqJ~%`w>e2=NWx`@L8
+gcW4M~CKw`fyl47Y9m7bz0}B@m;Ya!c>|Gp5d4Dr|I=1iu{LlM@CQZXX+NGdSw28Wr`_tUYR&`ON;5|
+N}(vH%uzI&@a+L^&gn<ukGJL52k1CXy@pL(j;dJ`_lO2#L*>8LN4O^vI%Nrp*s{MJk^SeH7xWiF7(9C
+edKo|d7k}%!TNhkg{)3p>>gR&9S1hiTySp<7PF?x~em|9JPXV*1fNt#-HrlNVtmb)l`<m5{U*P19JHc
+1&b=)@nGgIRC-;wLuZdT|D_YY7@0|XQR000O8^OY7yc()cWf&%~m2M7QFF#rGnaA|NaUv_0~WN&gWa%
+FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR9XX>MtBUtcb8d4*JMkJ~m3{_bBv<Zwug<%D$`FyMd#)^*+Pf
+VK#FDfUrNY&sE6mRw0r>izmrk`>!SyZS>AisZxBhpW}9=&e%n@H5=O{$x?qcTO}2mC+h2FO7!I7$x-)
+gs#EjUGGR>#fn@D&PDG}#&Ql1uzsy;iv#S?TXTkuh$8Ev_?tV4f6Rqx^yQlTl&nxYOpfoL_TlY})=|L
+p_KLmwk^QnRilVWm1vVuV@&Va)#(LNkK;Pqjzk74l??}JBzCV9WF@f=b&Z!4QnImOv{8C{TY}}}5q{7
+ZPe>KpRsgbw6^0E`w^Tyb=7G8|@A<<T7aYUP4LJIHG(-wBtd>O7%7o;hj1Yyy2lmKS`P|?TnZ8Yh<RA
+l$L$0@T`ydXc(FsY++GXl;gNA&ze#mv=SwrKh*%07w9INlCf|1k(Opc{Bb4>dM|IswWYk6~R-G=D{vd
+jsNZWDTni=K9!}Ue_zQ2&>7~v0{WAoeKiK2e^UVK<lO9C-&<Pad0t}9AWhN&@1(raEmlEc#4|0)*5@C
+2Cjs2bIZ*L!bZ7FcHyH!09)xd4pp<Im9LDgXZrAU1XmOpgL?Un{t_zZTC}v)B@=a>L(RjMv}Pdysk!q
+sAInv8Rd`1AkRktg`}?QA#-M>}ah*|hv3C|e_p*9XLx73YglA9OZ6Bwe`x8P!X7I^j)2rZEqyi6pic&
+<G=Ga^9qDvl-Xngoh@Y1tIA~$p@q=Ob|I5lX*+J%q@T2oCZq~cs^={aZJ5H!@Fh@RU+1hmvwKcZ3P8e
+{|c6cGTu)W%{B7fFDcj*lS#OO3Byh%YIhR3BZ7{~1Iy%vDX6i+*huR?MOQlb9^LpY6%?vcBC2R|T$=4
+sO%jLa->c=p5x&x^-F3a-6Tj7-pfSknaLcLR+Zfv^MM4bxqw6ke23aEKBzHaO>7^3(E%BIO1#_D9adw
+HwuM@zGJkKYQdu-6EZV2lDz7z^uyojY3)F$ix?an4Vb2Z!;o(PE3U+LgrkryXY5>ZSZ`;ymHh{H3!>|
+2_z!#AV@YA3=+uz~?v(K1z)s<7qt_nJf-cel{RL@>#yjY?;0#<4UJyH4-|r?i>X(^)Bl31}38rt6cO-
+c&oj54uIq0Sm9*@Qj53?xYq>sqo=X*kolvnD?m+6eo-M>+)JZd5qRhpA2i+=%7O9KQH000080P~d=M;
+Ssahs_KC0M;k~05t#r0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvaV{dG1Wn*+{Z
+*FrgaCx0tZExH*68`RAK{yDcP1i!5eh6S-gUhAOHE5d)()2^&8f&h^wOMN=Rn&TSd$|98&kRY4dhy!6
+{SsLk4(E+C&kVD!%Z*6OJXdL>%fbk~smi($zsRc6#m!v&vTfulSMKkVraoMp2>jNxu(_?3%<$eX`|@*
+{-kqp@s;WkO<aa;Rby;5s@j`sgWwS2p&6%lGs@FQb5T8oN!w7jVbxvC``!tuvSax<l`ekY@mP=JMs$M
+Q*qw@8v`$)eEi%y2*d)+wdxqxKL=1?j7bT9KQ9-CUGY9-UVNp7q%i+^Zt%R;<P-h9(pV8V3&t}|7u?0
+cOyQ<mt~CKdLSH_9ZXU9!xuW{S;#+~~ch(y+yyI+!^>u|jzM{QGwhe7Tf)zFdlhxQxE26n4uGzZsRiF
+N;R%Le<e+L_dE0`uRsVK-IKUbVchvZSx$f%GIAxEuP_(bH@~uxE~?ThNq{e{O{+wyw{n6d!3`tj9e>0
+ik{{APQieVkJ#Pn^j2)}2fZ+2TZyJ*CfC;+xi|W6b$u;JSt899F)Vi!d~zob_)CdgNz<IwPRz3{<g^P
+`>QENhkXd45<)V_c+z9u&i-BL{UT?M=QEWFW$XBj~<t$XLHu%>-KD;7n`<yaus?w<CdRwF&yC3dV4Ke
+jb30aW4HbNVr?qLdu>CRJNka@fE`a0wovzKSE1(v69vC0x5-s6E{*qL#s;ldl$ID*zE{LuRDeQx0AFw
+#!CgQJV!;5g$f&cB`a)LVQi3l*FjqE42M8ob?8|9i*)G~u5o15RCdEbN<5c7$e+I!Cqu9cK*++fEl)f
+(+iOgP;h6eu2n7L&zx{C~^UjLS;lQWwwUgOs`duQ4sJWc@2kN<Park8SQj)+lkW8ChYLVP_3jYtC*ze
+(GG|%L2!k&#G*%s;M>+VmO^dKSlhX<QPauf0?#HvFy~YMBu$VCOJnpZKUgN#B71EbvX|utJ4M(w>Ma~
+@IkW}56tk=>%acsPtWE~veO!wj<(RC98Yd!7TwlUDk!<wMZ6j{wy&@mC$_&_XRuMj{2+k_(&GyW$)Qv
+6*;_c$2)2SZ=YcMO~fi5%gwtsH==BD40&GMN25KAOijAY#`KFHjtS!WvTJwALAA>HSSoKlpH^}xm01H
+DAlTiH8rnaYoxw`0L|L^pd*jw<w+#A8X}k1>`wCZIcWQ-17a&q<I{OKlWTPXk0?i-^h?7R1p<ERdX2N
+)!r25oLESyLi|ZR4fsE2-B7Fm<g3MiGp2IL~8vUU0$FRR0mO&nlKgDGn5PAVFV(%!1_vYOm7?n9C?ds
+NRG1=cMxP`nxls~v4TI#w*ZnZR&?RS7$2L*1Ax%M|KokHZo9s(EI<@N9G{7~$fZpZq-IBOGbFef5V(`
+n5wu6k5x8wlN8#VJB-j+X%5C0wqV7!#?)pG5Cb9vRbLi%li2`fC;|f$02&DYC)+*1=tZyo*Ym=afKmw
+(-;zSc0?5uGDEU_r$Mw!n36n1I3!FI^BsWi4Y7e+Vw9*3Y0w(fSaz6qttGEK)w=3_}Hz$-pd1w9h3f-
+$yLCYufpzH{#Zum%W~V$L*WO^Ke&C9J)STlk^VML={Wg#wggk7z@((C1vsB2^rG%<apE_u|d#@4ih$m
+l-OLrs+Jcp-^KPw(KaL5xTG+$*N>(c@1}EYdEQYGo;S-br74-+#I0vo>S4T##>{JNjuBNt0UB3TbWPp
+VD;j@hAiz-eQVkXCaQEIK0wTXv*i_XM`Kf%26h%Ha$W=MEDE&)pMJbTWU@1AGd?9U%dW4P7q*llFU!h
+l3uw@fJ*QE--;^fwk-`T4vry293Bl@Z3NJ%Z+SUis+<^iB3ym+Fzj53ULpB;rxltCOuPm#)G>sGR?c2
+BhgyFCb4&`h}LYl_Wuc|qFUzT@TQAf8f%f(98(F4Tr@>$vvujJp0zqb4RnEEoJz(<8TL#exIZX<$Q(V
+J*Y_v;T|J;y8l>P47~mi>6ud&m7}$K9@U{M%M#q7`u*xbv8cSFdaW2=cc}gTbP%`k=6Q>pG-EYz9buj
+c*inbRc6#J|FmE_CU2k_N@*}LWX{2zw{}fvImx`Jq(hKmqDIkk{hmZHh4-C5TX%V%zCA&qek`Hp}n8Y
+Mcl4^MpFfH>MFu2H|9MX%0e-j{0TflmHP7h;_4Z&vZszCo!T;B*p<r1IeOYhSgOBTW+l=<9k7EVDdK|
+QVv4z+w#_&@5Xr4HvT17nomwdq%_gA;<NU|BpsANV3j;R%9iHk4?2?gd$Ov(KcRuIHySzKUnsJr6JEt
+tmE5%~r$zD74FYjJ^y4PG1EbqWzg?i9nZfDZS9xiL$kVlI^`(voYzGo5|gN(l8C4aOzd;HzLXOfe^u%
+Cbb?jo24c6N7IaLmYm>5)Dpb@$C`w?s>b)pc(g=S@c~QE;{$pj-GgfgPhL`rdX>m-7tnID0GZ=dkVu^
+<B7WtR0nc1Uj?ZMz*dM@N<#1opadIeq;pgQ|Y=yor56sx8*^d@><-Wj26zjJ8FP_QU~sOn2=hcED2=|
+1~V3&kR*Le2}1LXi877Ooci*$)VUGaR{42NRqK25tu;q7;tF)43t1m*!GL+huG|8_sXg@zb`nH=5kW~
+?>9?>&$miD*eP9!jwz{~d(I71XepPu#){e~mFKlsnj?v9z?6}yQXH=Fviig?Ud9b>T3kq9xDqVQk-)=
+doN64HLq6I91nG1y>`m0pd*<=D8=|0W;sNUUBpLQb*^I$;(YykYHVRK9{+z-7N?jkNrWoRPQf?D>LYa
+-V)=#x41)rUYX^m!iM)9)xcBh(Xh$ZJ4v(btEvbg*lh>XNWi)LAC-Fe(a_Y@cC+B^Mz`6BMNOJD@U6X
+N>*K_6WlFC#5QP$~PMvC4#;}-T`5kcED93-YKdj;6Kg4@NZ>De_d=j_QOuIbCMNs-pbMgHm!gzh0ybG
+M$yvjq$RSWT7^_2xfKFDr%#gP1>)Ws0ZexU(h;*c_wW-aj>m^C5D=<}6&!!Eh7mWty?4q^4kdFM@u5u
+;BrAev6s1o<fiJ8v%`K;)6|ru%72kAF{?v&|pczsf)UysUB^*;vF#4oyvjWg^@5kDXSIP2nNnUSjuKt
+W74YKKY@HmYPy-zPm;LC5XII_&;xqS#hn**qJ<tsv|lb9Vr{V#<cTc`eJt4;7Es4NZ)P-wnjPl~-gvP
+H4WweK=n(QOKJHatpOPuKNcbLuAl?Bj_c5YBDegO+Qbc)LMPph6hgG$3Rp#anScKyEw*{0e>S?C;bWX
+(k$ZN5g|gY|s&kb2@E)m~lGtP53*-R$F^1760eIw&KoC6S{7dP29>70B(=_=si3*5P5;kDM&?-OhWC(
+0P|>j#Q4u8pcVx(+F`554|P+NGuu2L)T~Jf`Zth+t=nB6+sUC;l&8Ul15AEOpzidM%@UpuKC0dHp2HL
+o8qv-@pQKPY@|<R4Ph&Faiidg8=?04CQRr0d$9ed|fWf#FNvDU#%Wc&)EDQZf)jG2SnMAa!x^$#ARdc
+XRw7p>=EBk`ZWjYDZ-xTID&uK@GA`*v#F0YO_YpUXK0-gv4&(G!1KIob*6&(~a)FoSwyb%ACDLE4mh=
+*v6UTL(!2e$KV(^R=$`Ie}IG0utp16+ihk>27cP19)GQ2AFuD_5&(>e9o?div7DFU{;!ycBWQY$BDC^
+OwuH?HoDYC^2kQeWQ>Qd);+6T$yedk017_k%T+Vqbt~v|Be8L?@NlpVpwv?+h#;=h2T>&f&~v$TbKS?
+GmsUAMox1A1-&+Y*8i9ctT$@#nX22R%g%$XaM0{XTG^{AI@2~%_&S{aFG`W-TkaS$7(u5@iZ8u08u*G
+fc+Pq4Tg_bsxr-n^4`VMKcsL)zmA<Af+@7o|w4^0t9T5V2y6R^s`&3<9HCT&d)3Qm|h1XZ@NppGMt^E
+gI2W_B2nYGL)XqGO4#A(jT8zXO!&8DUyocaNmeE#5za@o@PROFd>7#AAsg{YYNb@GPuJOh8%nG?`2E4
+bs`=a2THV^5Hge8Izgw{5^nt=Br$e1D&auXMHA!pmBn+5do6%bkti?q1)^7jUwo+Kcl&{m-$+-o}I*J
+MrkI$}j9PX;3vpFFa^(iVwGGGP-A@AE7k*FDJ-Lbz=ym#&N`P@JAC3M|<=OD*d8uGSAMhMufpPPzL2*
+d;{k7A4P-*-n%wUt*dx&r%OK?k?LX_jLjy+uo@r&nmD?E?B{(>ZNqGvr$Cg%?;OCHHhGzDa{fP1O9KQ
+H000080P~d=M^e;8_>~I)05c;105bpp0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVly
+veZ*Fd7V{~b6ZZ2?n#aerF+cpyaU!MY(Oh;6z(2kupt)`wE*H(I-y0*_&n(5>^G(<uYYl>h2(6X-6?|
+!>@kN_#i$vx&YnVJNF-No*2A6PVBS6nEPQN>igW`o?k%dKo4d3wjxU?KS0J`?)|7w}pUD#?xhFf#*g<
+%WtfFPGA+jVqeo(It~^xy&C110}W-LY|YWty=Mt91aNl^!$}lRh$+%D;tJ?o6W9Hcv-Sk<uDoeCgIRn
+PnkZXtism;(@*3~FP{p*#Ux}<<nw=<_+XG0R7!GMuh-jlTEyqS!PCfadiGATj3`brreKs%vY>eZL+yf
+-!l@*Frq@(Jb<nEjgs&^TWRt5Exw$$2tDc3z+;{>Xbp_G0oUUb|U%#i40R^oJSOLSadv(f+JZ+81u~H
+(R*NW-)k+D@l)q;!lOIfirU*ze8TyPXD);xeHkiOt08|ZhVC7*MIFO4FtrgrSyfx=994m44LC4b4QG1
+;)8V1#BFK#ZtWE>X*B#YjolOaj;#z?I~Rq_iY+W;MYwBV?;4r;E;rQ^y5RGsy8II+%Dn>nw~uvN7Wb%
+uQ%qA@XIiUStfQKiq7kVrv|KZ(Pa_v$Phu+BSouj;F<7ieybyx=L1DDpZW)7>+v0YLypyQ7D*Tk{9<Z
+NtsZIV86?8FpLZCnULrs4gtwV&?+(U!VH22aQOlxWug*!sZd~_vtydJ5FX$_GT$aLT``dUi2R|64bgd
+r6Y|aexP6x}@cQ-Pc<|?7a6LVlUd)oy<JlAzx$qX_Lv*nJ>h+ksf`6mNi9=V9dafTj^|NP<H)RCyU_(
+jVi6P)Oq!Ro-&mign3fY4a6t^dps8a<XLn?Ey7KF-7zD&W>smkYh0ahgi-7*2lRU5`i!ix;vXql0cZQ
+#iuQO3r{j=>F(B`wGu+itkX0CEB>tH3UVf}_+n4gvQ?zO2!8oNG(M7us`L#TzF%P7$aqpp}<tQD<y2u
+-JY{Zf{{bd8%%2Cjd#NDP=sZ*ATg>)&u;TXf;o6Yf*rr4&#<p#d*<D+!YMn`t*4%QkH<P1LUOPqck7$
+h1f3yqLSH2Ed=Hbe?yHg#xaBt#U>Ln!>ED(mL(UkO<e9gJpMW+X9~aave*(@fr0ImgZPa$#9{mpXhDN
+L;l{O{lrBaRJC4dg5$h7{S!^+QnT5b^fr7aeEF0s*vXMJu3tAV-n6uzCLvhe~Zf`^N#dc=Wm56LsdAf
+2YlqAa~ofjZN!{dT33pNB6#gap4S*=MV8L*t4TqPHmr_=M}Ul2sEMj(HI8V_NE>YB4v4$7g3{D-Zl_q
+U)r_CVKF!Nw$CkS(tvrO6c%WF-i~kPZ2fv5FiQ`8~v{Qpa~q*#>u~gmFU_$*l9`O6edwnY0XDmV8n(<
+V*4)ScSjHn&sy5Jh{0%`EfcMlV&LSaXP&^K0o_;icw@7EdI5X;8Gaz=oX40z$4ZHTIVd-44H4W@CXJB
+6YC1yKbH`HS-gzLVBrtH-rkPlCi)CKKqNY79+5CqelQ`@3iEtIPA%iUiGjN%6}!p6(Cz>1>gxJ(cA0=
+}8U=W8!2N@4fj}MY9+A%?)=2z*#Xw8KBzF&FkZaIC4EQjG!EP`$p_@T`H=D?1V&`Qbz*|~K)@Y3+NnY
+kENdVl%VoY98v6S%V#ofls*#J=}QRAy$4*M0V``vBgidWIlM8M%#mp!8v0I*E3mlESZ5W^7KDP#|*V#
+taD)TxW`mQ9i0F&%^&QYdQNp>l)*ESUlUf)7Ja1l-0}#x_C2#uuD?N1}r<c{2)Xn)vHrYtOs&FGj9*F
+1T({K<+B;Adwgoxy%FF9Aob5ki*z!a+j4w#^gQI<(X7S3{n)<Ya0l9Boz#D2l_*YiiZObrCzQON2o@y
+1Fn8S7m%l3G>Vn*pA0`x*`c&Uq493xhPv$=88ZoUha-o2oA@(yFCFlq{IM~<nFEpBr{b1iajUiA1qrv
+N2Z)ZVwL<$JKxjREw}%FRy{RiEOi=_*!-<anyTF%waCq}&D^~|L@kgA^^GHBOg0|JOJTHW3N2XF?o`O
+_Cot8ZqU@ArrD|yDCX2NGgj?T2X@)FQQFjb2ZPAhEFrxW`un`d3JC3hRFJpT-Qs;`krfF8H<Ll-~4;=
+!xGda#4KZ=}bm2Zr{j`{ONjJX{5&PzA8a(}Z?Dmb4?$j_im$F!)+zVfGo>XWJM+BRe@gh}nZx&@u=m5
+;O<!?>G+i<uI6f=0z#Uv0q5tn}E{1hO#QM`*8h=qc7y>8TsPrE!p`aPj-VKtt$_^m0(aIvlhQ&J;OtB
+V-c%p&)?9;6IFwVg+7n9bve`w5(^-VUnQy9!t{_t_c`@wV5*<&?X8xvnH6)f+)K^#o?!QT`-`vM%n$e
+JtFI0gBMg}4n?!Qa{>y_ARO}u-4AnY%f^h5D2PPyNl+W&R=nL)w<D?U3XP2j!6Y@5HAZD^XmS>#FatM
+D}#Y%mOYLx{3?(%q`LCY>0by)mF#a*vV0OjF61gmx7)2Bvj#lb%qqJO_NrzO!1MAl7=98I(ug(eQ6pg
+|BEF};RO6oyI5=@V)?>594?m5f0zt~x?nj4>y}k-K2_H8n?Y_ulgXVhR~M+c}Ia)RG_0FTX!NPfn+Ak
+KdiolG)i$)5~|Wrf_PgeLMlQGL9WP^Jj-Rou3oP3ot=w(Kc{f#$;$8!wHQN&>7vcp8~Jh?{$s`LP*Gv
+451juW8V*)L*-14t7=*<^OBKQ@wf55JyUV4R5uv5QVN+{depSt0fxWVOe)=_T5@9hruYpa6ds$;q^uc
+ve)!ebeN1u1Q;uCV3xNL}Sc3^RU}2ZgYv<^h<51V*j%Hib|7?r!UFg5*!T+P36~Ehwtre6>+BSer=GY
+)Low1v9??lifcSpVHW4S)19;CXz6^wbEEq}P*$_g5uXcs6#G&zg;&vnB_Lbt#Q_t<<Zk?ZH#Red>+Y=
+r)=cuaqa)oJtGnbK<5J%$rHyMTF}k)6{+GxP_abe8AXKJ!nI_JnDm^Q2FC#ewdJW{IzhUhgyt)ivrb4
+^g+H%W6n?1D+6`@|t?)KTP03brRGhUbF15vj}6_<TDGCuyylj&14vy(4w#hlLWmDrNg8Wjr57I**wX2
+9{hq$%~`=W|Brg3C3y*^6ma-!ni}T12j9RZ_O`<&5j+=XR8igj3&rl5-`}Qp!jK83(1b4*-R^j-ct*F
+;h<r!99=eYpZ*hneci$J|L)5R;hvptbXL0NlZs4$n5=3(b`IV7B_WvBkrYWnN3y}Bbpvf&6_DL0t>UK
+6pdf!_K!*hZhj$*u_qAKz>G3p1--pxudBRChohiu~#O|4sX0pG@2f&&OloXdkcU(~=WOYr-y;MXSG<T
+I1)({&U6+LY3dQ=dq|C7~dif7)6I%Al?`LAi%dT-}Dcqg`Qg)IU7;^FRpJ{`aUc1GdyE#)MAb^Sq=26
+c2DZnu(h2Kv8e6JNdUkgcTs=j%@K4Q)`_}uV=}{<;9fzqsPiLJ9gyC+(aMU=<J;DnLbMF-dz~ka&rTL
+Uu;{k*7sh=g~EGKND$GelOFK*l;h)Xc;e;Pm5!C<HY9&LCJPAa=vsIurf;iO3?8uhrsOu|>oqU+Og&U
+W%-#_jG&dbZ7K|#q>C(3nNLH{lzc($pHT?}p%mv>_eMJU(g_EdYD4(8i{q}?$eDms?M_K5qUDxA~;g;
+b3^*ja<_vDKHlW+FyP1jzIb-mns{p}kqbO!l1FJwF#J_4l9t!{5lXtD4fH$uyecLM}?yXWqSDeRR;yX
+qJZnb*_5zdO60o_^E~fZJm=nnkK@q7KkuW#O*)f1-Dc;2mjY4qeKmJCo$zmG`?(6CibGM9=TfdgL@RV
+2#&2{l8WG3s6e~1QY-O00;o{l@>=wyhb?lAOHYwb^ri30001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>
+VP|D?FLiQkY-wUMFJo_RZe?S1X>V?DZ*OcaaCzlD`H$PynZM6pLCnCA<jNc$-J}?-Qn<F07`0>9M}tK
+YNF4D=!!<=}`KTGIi`~C{zw6!9Sd+9xfgJ>nM85NT-+eq)n{C_mnd-WxJ*oIh-LBVFvo>G#UYn0MMc2
+R^eR8tu+D)e0<tNo=l^vt)vO;I?>G2EvI?eiOquO2Xo`2v6c654pa?*GEi!3|K-tPOWw#lBHWbj9`{M
+SzHl$qpJwQB!@52o3R&9>i9vu}5GeWE@u)wa)GQX`uA!VU13BL=|6(t?}AdgrvBy69JJw|StqYFVwS<
+wf>m)s*cG0D+;9^W;_AXfwo^#(P$`T9t3xwq{t+!?#`Ax69xWts`poeXILtS4Fc{rT6qzyDV^`zZUgQ
+dCy-LpWi8fU^vfb|NA>tlmdG1>HGJuyl?6ImyK4-U8mkDI0fDR$mc&jnaI|+#qv|JRyx<Zo^OhNc{RU
++l{dx40{ofkdW2ii^NORMrkSoj%bR7}G-`?448e&X=a>7rUS6q<;`Ke>Z8rO7wu8X#!-u!tGy4ACfAC
+k;!HQHFzp%Yum=m35e=4-HP|93m-iv`l$M7)=7I3{*fXk(}ZS)p!!~zB<dMD2eD((8J9soe4=kDRW?A
+q;Cm425FPv6ZBU;MX*2Iak>K(EEFtIgZB>gfyty+3z|fY}TWC*QQ!aL)Yb<OFyGJQq%pL!VdhQFY__9
+M66}pPZb`pQ}}|t9xkIUj8SneGCsz&VZx<)#Z^sS=NQt9$fL|m|sqqhEGpV>Hl~V88=@cqp9o6gVYxK
+>1n2S%PXK=>Tp4gFBW--5IDmkE1EKF@IGph+sWwzV;$_}hqh7lg!sg>TEjETRJd)aR@r=BHB~>KkF}~
+-(+nXxB@~|F0J1gWI;*T{durew;$K%(u;G;C$EQEE)I1ZGq{{qsa$@_QWmkQ_t*gtdY?W?Cz|gBtfOW
+vWuT+!Wz{h4;?@E<jb_FtDph3X2?e$k#QI>$ET4_5Nu0E&9W;AE6`>xuKM?d{CnuLuAd1kal9}3W3fZ
+**J&~?`}+321g<Dj#9oxtW^HyLI3vT?uPD(;jUjG6|(x%JO3dg_)o@fW3<UL9T*J`Gxb_6`!5{Tx@vp
+RS8q9$k9l(4<S`{^N;vEbn;uZ;KkvwQpg%2Ykjwvxn{Xu&S!4Gbc{sg;7tpGMT~6YbXW(Nca$uAZ4i1
+qZLiaUsk%ftYINnikjqu!)$>-kZQ9JR~6ui&idjLAQ7<+M6-+Y^Q(2fC83?S-THjVpU+oSt<JwLip%2
+b$B)$I^5OZb>hheWJ+wcoAOG9qllf&04}gQrNdZAp(yAZ-MBnlkPrrNm@`q_A-+p}a@zwJ`y?XQP&%Y
+oeVNQ^e_Bi}HjhJ!7R6bmFAQ&I$Vx?kkLR<!DmGPdzez7gOViOT7%Ia(fqMmy62q;{Z$JDa1%GY^z3c
+D?8?0h<z5@?}M)%2|$%5Ws1;D~cA!c`zzyH&hJ`uoBvkoDjp0z@i6T|hX*!#_N<t8rWjR&}@4AY)j1>
+v>kdVl;5=@_k?QJN>F^2<tBubG8yWA0nTo*TAIU{jxxQ1x;W~e>tFy6&b9!Drr;akMf5Z?DA3m$nXAJ
+V2Xcvj;vT+1bp~SQ}45zw);eEQq<GOITHkJT>&#es8;Ky?PzwZ#fBgfIqZgKjo9kQ74jay!jdJNDnOQ
+ju324I<*x9hoy-o<vO@vPifmQ4MUQ|104Na0Q(*ZO45E?ABdpC#j3SCLM3fiDU{Sh)L}&oC%~ECU%EA
+!z1OVX^RvJW)SjZ&n3KWEF-|n&vfWZ#7f`fUmh9hs73H2RH|7!r5QJQ2?I?tZ25R|wuXb1?kTlTP)t3
+rF*1n$GgF=Zr&|BCvi*dvA<SRbns;aO~o&${}Xa%b_wZgUBf!)AR>2?(o;YvhsuSb#U7uR=1*v)8aqe
+9>f&a24=ywW~b}$~K_=R6TJR*t@R!9f@+-=*v~MSP=7DEU>RuM2Rb8@Y4`}{%US2;ODS{B@Efake6_j
+0Ifa0Ks5qh;(AM9Tub8Z`_Z)up+}1kU_lFUkbuR>dOmb$Cp9gth_|{}2;^YlLrWtLRP>H=>3H+(N7Ox
+Fq*bR>D2rg^;<~76ltxoNd=y(eAg;`gwF3cJ!-yWE2HfCXb*fec9gC4vg`5Zg9&{bGT0s{?d{4H4WwP
+AWMR)L|P-TMkA=^Y{ia>l*Fx!OD!;>QT2y}uiZUL`Ihk{<)PF*A41yXHG)SW}Yf(z)hCtLUd_B2}C4J
+?!dyU-74y}PCcwJmD_c%2!80E;x~c!c^43~*WX^*)>xt%pi^vyugYTUb@=T}PlWJHFhb?m>DzEyOGW@
+tx?AbqRm?`4(+aPf`*2X7n`s`0f>GX;<y8E)4<`3YAp{;H$wtM?M5wLS0TJOx+N?12@f2*6-`00SmKR
+oDQsJo<RC3Oi2S2-JZ9`3zUYpFVdrWx7gpX$e-`u$HwzdH{JrvvZD3XPVDe0(^}wsm@OcQ>8o2Nk0s#
+r2&l#;(!z=deaKK6p}-xnC&cz+dnsJNu}0HWbc`A4!2|Yy2$GpF{`~oiZ=ZgA^<n<u<?9!3K7N=Q;+#
+F2GG{qBk{r5BEA+wN(#DKY(l$h|LtX6gdfI!ax8?_7G4aUM*KCs>yadlOB)f!Rh?+KiaBsyBt+YAWvA
+}a`JO-1{KsCz5%)HOhESQrLV){;k@dc2`oe?mRy(=$g%zn)2May@S%BS$P8$>=u-AS>Z@n>Vej|D9vg
+O)G^+OiCah9TB4lhYn}XAr=)u@69@`S1Vnt@UqF(4p94N9S&v>6Z14vIPNw4w>;JLuXL$2}R^iAXjQP
+IqGzCWW(&EvAYVPY@v{EcRd*df$QMIl|r+zL(!&zv$3WKNQNsRvr%2{$mx@9cik3jOr3|@Z+4rxFZ^c
+N!?b~w8p&Is3wR-)0t9jZ4tI5yy=cgq{h?UbYJ@7`y6u38Z)gM918`xAOtQFc!HU?D=L1+}vEG0<CJ~
+oML;gti2-3u-roA!d3NTwzNzqw?B0z+p_`+MZ+^2YkH@1JLwspb!STsjnp>3HQknBANJxnGTk9dZ;%r
+Kf}KmFo6H5zq8aF3lDzBMl%h$0dZiA6Njckpn3H_IN`7InMMt7g@XPr>5r2xDMTY-4yT+xT8NxybJ6)
+9hZ#7{^1?Or1w(;$bVcFti*<7<MXoULX=7))jP&C&`EspXV5HI5&Lph;c+>d7O6&%-=s_$92TZgU`Kl
+MSrY4R~t`>y@>uveFu101lHn@6{(AI_=!UZQCmC%RulKk;+H5G4(sgfN=0^bv0$c2Ce30&!gaAAogU_
+OjEa!YpI*!2tYLzGhm2<YdoN}((DO}Ep+dH5S=~jf0~U&FLN^1(vHXs)hU1)Vc^HOS7<wm(fj$#_jpK
+&n4eT4Oro{m&5}dV8WWu#B;$}W#6RSoMzzU0#Ob!)NQ7hrFr#)aC*of>mrtq2*f+B+lT+UJ4N)Ye#t@
+I+dW9V|0`3QpHRt(RMOw4e?5b`|Vao%2DREM4tz-W+o)n>QJ$N)nOFqZ4-k)tIzUf1mn@6~UIUM2+Qn
+=9b)bv=cDM;MJ@^}8Uh=b<;Ezb|87uAJurZ9@$*08K*IWtzc$K*j^~0J+y7LCQ+YYu`!7z6#pvV2Egp
+k0J%Yap`v;>U(0-(%f5tFldkQCK#G{xMfLztOXz*%{|RV#jbBbXsHGy)}u)_1BsvF!I@{Zv#>5h2!b|
+bP5sTTXYa>;U>xJ+oA*2DUW4X@&=EOooi`w7Ds+&bN`+S4z(BAEBzbaK?&-vd8=&qnNt*j)J;cvZ>my
+J)G40E4P{n*6ADx=dKi=*_-kx0+h$rZ+b&mX)g<|s%=AKiSonzJ`xZHIeA|v@L5OfsRDIAB6!F~aT=o
+N54yR!vu);=}jYY_?*pR*P<`dvWP+#xNA_s^3J%y3N5Vp(*hM+28?iH>GewvY>{lKB9#0CKa%ByC4PF
+$79z6PWKLsf-Ip4E{?Bn04%3I=Bg5q{zBx57uoru#sUgk%)v)ZD-Q*OxO@31@TZTzE<5Hm<`Z4tg(cd
+_fs${8kK)RRzzxQA~y1bA;tq@T17kz5xMcBsB2QBf$=n<m_QzZGBjaZl72Ey$nVs{XR}JiCV?{oAYpF
+FFdnn>*tIt{uNnC<oWcO<P5Fp{t>iJ2P9?nMpN<bugadfnk<8#3$S1(3u3BGN6{xUob%4PJOwMUmSg*
+QC*wMy=d|S}Zu4)VB3y==mvBRr61EgRy189^jag#{z_w2-Id}I-#!-Pt1xb5tFRc~n{APmIi&zO!<<M
+s%JzdO|HOu$8FCQBjlMVLsi)BbC~`*G;dINn(zBgTW7*9wR@rk0OrX-Y(Er#U3kpObsVdASkNh=@QNp
+bszG%@%Cg%c`#W{WKcShA%Lcqs(eRfNp4wU-ahCsgddyotx^qX$B`?PY^MB7uhK<S&~Y+*s|XN7Vjla
+0A2nl)(S_){k$cVD+mtjYD^D-77*l_#m7Ly#>r5!d|LS_zNE#J8A2f&cjf^q+xPSY^`dZpKD&6YIHq1
+)0+jJU(7%A+U;`Y{4g!SPOBYl`A$C=G@tQM=KS|;lfoX|3R;ae15b3YF_9lqy5m0^DfYL<D7y)KPk@O
+L(MDRJM@3@>e5dY5x+B4eLw$-{qi_-6`d7BMo5WauKTjUTLs&u|f1yNkHzt%#g1@2fiMV+HNr%~)EFI
+(2Yjq=d0d{!oT4VSyM_A?tQt#|6t!^dAg_5nPyn;ud8mSH_XTpg7ahYOG@YXznOL1hv&^phSR=U@HP;
+7&BWDLyIVi}M)!G~19%Ii%&8VU>q$v-|f&`8Gp_{_Q}GRUIZ(deW*z&jf|5GG^hv0wp@CmwVFgyg1&G
+N^hVy^C%5Pw_&g(mF!4A#H|5*#6Q`ei`Y}7J{aUs)SlEvio~Mm#*|7k4;^F>%fvrV!pm-iOW4O9g{G4
+5{;eL8K#)!cWBoylKh|?xynl~yw!!|}os8B&Oddw37Yil>LKp{0pgveRHP9i~=#?~9vL|~<8B5{{)Ft
+xx1)g$XEO1zqyrn+(o#dx*u=K{~HEyfJYJH^kk!lrd)8jy>g=wi1WQ~dT@OJ;08Hi?HM=3O6HtXB{6V
+s$38&3!5Jm8m(q&_gPnIbMROQrtIhwCI|Y(6llsbNGDN&wK-l)rD@Oby2u{scJejbRYhQrt^a6Ll-2A
++N)vG-Q#>YYM11%E&`xFe?ez;2?YRH$ncC!89A3aU4zQxe1DLK8$|;D}Sb^+imuuSyz&lfH_mBH^2eo
+*yy?llz_-1gKb9BEb<`p$W9x1lG$WE=0vP|hzdjgRaO@%sf<!ffxx5TFpYql7Ns+l=oq#wvb)aZvh3-
+=K{KvEVG>3Y4JXlcgS17IU1puRs8w-GOy|LM$9~{5)<I1NS|qT&-G<^^u-gM8vl9Wg6AveR&A9F;MLG
+MM>RO8pVxa*dlA+`YhuDP{qQs!b?0{}CD4OrAF!=K){=Hyvz*r7M+tVczX(3YO<aZRqfst%&16}6DB%
+GKGBsMfrXm^|s7&!?J&Ju1YpxLFWu9cjIB<PcFK$NPwYtKeX*mqR4@;Lu`%1JW2)k<}E%7A~8W5;9bW
+WA25$m|^XM2MJ2V>{jq{UQ-ySU`N(Z#_4^Qz!|u+q#TQy<=y-lOUS<r9VSb>FzS*FeWB41eoG5PrODK
+JJLAtKu2o0`?+~Fi+SXnU+V@F*nwG&DDCu*UeY@{q$n6ZKK=Rhp3dmMpHKP!NJOFZ@Ta>&DtmHBIp@3
+nrXgz8toy4;5_yap#;NAFM3(<f>E>tIb7@yBmzdJ#IoE6>z~(u7+me(HejK{t6RHKt0uvx54f7~0Sdl
+5b$lHLQ75@g^d;Vyln-BjEN6SoL={b2=E%YlrNA;h|a-8*d=p;<!2;s&C)dH1yPBbk*buxLb)~sM(Kk
+JEsBIFrF!{-Ilz|gE&YfPFOkIqNKo-gv~`ug*m=Wi}BsZDYw3b1u{TZ%TI>~wMgS9iImk}?vllr(9^k
+zz0}J&I|WVTDlg=lrOZO@g$U8+J-^o^_Q*{hhN_IVK(^QQ9mNii<RC;fJaCbd;_f#ucp*I`K!}^(UEE
+;jlcwZ6gd$I2eK&r(mK;9A{gYmtQEG0bpVnPY^I*ZkKIo^55=~IoByppqz1@F?u>-uNKwi@t55Xh@VD
+C*oi3}m@F!JbpO8LYlr<3aq5Iq1&GVgxfs_)6J*ajkF4*J!tgQBR;{j8&1ubuXrcjH=LcmAf%1$&WMe
+k6*Kdz3)p0UZQi>ZmjlQUAJy^Nm1BVjS(F55wlt|lSV$>Eh7P<-})soUn8wL~yy?bd*D`<6Hm7zHgD!
+MJ13v&+_w(`zI{$y*z<rV&*3;(DVY--?oL0x9~t&wCY@C~#V^xQ(%7W4!3GJws%EPCOG1Wm3s8h^)hM
+F<<dK=W7Vv(PQ5%f%l4Pr*Fcv6ps;Z~-laPV|5yK6OqpkV69YvB{jm*g*BW7azc0fAb#y#Y|S!FY`$<
+EYe{(=<}j42%S_@a*DRKI;eFvmLz#%fM|-vmJ&|trJ7@2b6Ru-yaMcDtRjt_QX9rWi(iuz7)dHNE!-t
+zqYbyyeQR5q4LPL4P|1U*Ycw2#QC_YdAn`wVjW!@d4IRhK5QwDxY<Vv0fcFOZ!@+vWjWivMQx`DRr1x
+<hUt8N+anm^6bCPn8f6pZ0p0%XL7NnS@R?J?+j1*%@N`h15PBbL?zXLf=L817<Gnh}pGe<pSu|P0^+C
+Zg>XvuT^1$zBDF`Ei&*yP;l3rlgu$~moL;in%ARSE^ekr%P7&=#W7H86R!2cJCM1INtJscOJ!H{&sR4
+k>V8af645vLsTmvDBt%rNqL{Y=RLq85{epM2UHqClNX(X>sOkB{u<eU(OI<zoPL^V2T`7&4t$Z1{f2v
+@_L_VPix)UY6$|1L67i$0CYnc$*U6#x38QzY|<Boa`X@q7MYJY?<p6tIwy2LL>s(<ix_sIChUL}0H5N
+3*cHI4cB|IFGPxjd*_Hs-Cl4P@;h)FhKA${%f=|E3fBrDZvlm!tXE(lV0nx=_%4-*^1c7PN%KhO8K*q
+o^!(g&;n{F;va*|D&{J|*?i;@f2Tqh-HctWJEw_f^*tHBA^g++9dTZZlv9*`YJ@%<7~pe2WcB<>d%;v
+Y&>J0p3_SYmN1JUjVVt{`u!)(x$5fb|UqQn2RCRI^Ws(7UQsIuOXAR~0BCNni9lznfz3*F=t8XlC+T0
+wC#jeC0FUlX5W_lb9m4b!=6Nipxnun6bffA5SV|Ka?1QJ6|mP8G${Uh?7Zqjq@qFup>qcFTcuV@}h1d
+N&=3Lx^C4D<mCD)Te4@tA)5##c7s>d$0_kNM3gEO?m5Y?;i^U_bU1UEL=osp$f6m=W$aL06v0*SX<)H
+EYbHn^+R&37*=QDM2?nDisw0u>#OeQ{Pyy6YrH47lK;%xba^+7s(q=UwU;htSHiJz}gJlsR$70dt{*j
+C)fEH~NZU`D#Sx%WKcLBVoHB}skWzyt8WJEwPlQXo)U>!v;8%>FqPZk68^mAaRh1hkt1WwXq`Vh~>7I
+Bdw(N+AbS2)c@TwjYrUJj%64OT9>S|2u{`Ri?!RU$>ET<VlMOoBovS|A*U>tzjRCv1dC&ipE<xcJIp$
+e{`>uf;4o_7sCe4)Q+H{W0FuvCy-A6IyNH-U=OCw5maY<!3>p+a1iuJ<DFJZmnD%uT{^v$lqjzR4myj
+V}wjlq`7vxk8(}SsbkrwEBDFpDhW)~p|x_&78#5gI<k~J$J7&_7o5Q!R<#(pC|mdFdqSa2Tw%Kp@(De
+!4SI37(DD#UnmytuQL?sqwcBF!d7M_S1xNcDY_RN;QrjqWb_wbd@udP5hGf|zU+5~5D0qzlsvg0$UOX
+NdP@wE<Nsjv1&`5aU4GS(WN44vc(@fmDXQm$VfCq=l>p~mh&gldmZn@UPa*g}R7tWKDq~l%l4Cur)<y
+P~_1hpZA#1)sNQ(Vl@)h!X<aOhP!dg{vNt%hl!oTU&Qz-v@ieHViWA#)*7lF}0%$3Q@erw;Oz(hQ|L#
+6qHzpLvJyYlb1mC;sCcpDF1uY(RkCC{zd5t>noLt3XKqi^U3a*6RI*u@j@gd>ET(mr)O>SdmF892wXg
+TCX=4Hw%X|m8Fy}fyS}7I>|NU5*LZ6mF*6Q`++ox1_RSMGR3VR5#;sszqu`7ab<5F%=k(&!Hbdh6hG8
+X&^L#ffj1pyk#~YqgHub<`Jm7r$9VTEFagPac89_x|IeM!EP&Ln7%KE}$KV`FGRD6T!{rLYIqs=O%!4
+?sFV#gU^Nn)V$JNH%K@k^{3=C70*!`%`*%{>_P*MZ>sRx+-;aal{Z&ahb9V~JjDt8-4cLPQj9S)~uai
+BCNOkfU0WR*#w5(T3$<qk<VCbb*#QFs%xLyC)N8uhuiOoB6bpdkoc^jH=t6DS&XZY9Gd=_$if;$Tgfc
+^TI|x<fHQ9IF({=3R2!=&ZJHt?Gd#>nQR4vyppOD4Qc@q+PL8h~)q9O{VA*T(=qDI~iP}>YVKdG3(%p
+DWr}yFtMDG<TUMPH<9Z^=zm}&asraCyUfa6XVRDm)0`^A`Fr`Vhh3_oxCH+=#{?wDp-TY>)-a~K3Uxe
+s#)W_;Kb8;!-sYek#d$S#GV?e%l;FT7lY?`+>t11c2Y!&y9aiA*%m)lW<JqPHG%O?tlS+s@XLRex-Q~
+|5Hn#jk1TfQkjbVa06^2fJdi+aLP)5tpHK_mNM6Udqi~_t&(zsE$xWlDCqD~>zTOnSsX73%jQG8+?IP
+7xbHM~vK9hO~k<D9awous~SU@-N0Tj5P^<ioPpZYc49c-h%&)jNvdi<t(7OA6kFb}Y%Hv?40EsSAy?Z
+E+~1ut2uB&x&<Xg(BLoo;xM40?t3ahZIEBVcy~n7fYXBb}eS$gM1y`>qe$V*!d+){m*2u9+CEBvIvs=
+fvLM`$&B|!RCex<*u(JUzL5nm4YLURFUPFI%Qo=8#90TvJ^g(cx%lIW8;zgLYq|TC7=Z*Q*wHQ;65>z
+N+GDOnH___>$_g^e(4+9=;ggic#5Fz3M&G^oFba7<VvO@Xj$~tI1y=p_8QNgBV-V0vXLeT&<(zETHuK
+@Al3i<}5XlY@al?E#^XC`R^Uz3B_jLB)8$%efyXXY&Kpjcnm{x{2BUv}M_zMU7B?&mkQPppyq+dlnHq
+GtE7HYGY@vgNLybj4hFj3DzIsw<7Q!fi}rhz#I4`&f!9Q4xu{jfWhv%~22EiIh4Yj`kax*dyE13dTtD
+IO&R?p-S!T?jr-vtFXRgx_O|B4$9{a_{(*5(#K{34Ku9RotT$r<GK-(Iya?FE(v?vCz6+WbkPxmt?Yh
+w^JRs(taTUsnTdI_m@R;A2Xscw`}PwJ;4Akd*IOJ==R#*FHTMO|4ND7Vj(yH`rTS}ThM>{h1@)7-<h8
+dSn&1iiv>PF_kjdQ+pgpc4zvEaxcud6a4_g9LCHq?Rw7vOQUsT4i++&B;*;9Xu|fr1Z;Qo}7ae>Bqgu
+<?VvbKVuUK}2zl9)~-uRsE4+XPyCwCru=NSZFDd_`?>HG_fqX-toqH%n5dCfUTmV$b_?YaX>j<s8_)Q
+FroMME{Te$mr`iyQUfn;X4DKKkm$lwgCq9Pe^<V>*<7m*YSD>PCYe(#hh7{~ZzJP=A9m=1NP4EQfkT2
+rV5`dOGH^N&9vnhwtDKnu|B>G1=od&f1jwsuKNPARa^hxIx{nY;zKJ=6nEI&Bw5TJu$3zF`oyT;31kO
+_&I7K$A~TDW7xnuSd3%1Z(+0OaG`1RWU#q+>aK_PaNzci<E|8W%dLrz0DE)Xd_mUyG3>~WMYjxq?sQp
+KnnfAk$%IVMhOYQFYJVxdy5{*`SdbqOHj!?|HDG9516;<IVgvlR4a*!+hZyt4K1BE-3f<br@PYuPZtD
+^Af*~9_qQB2I4&c?mg<la$M_4|1@zvd~k?}F{g~J@i8b4vP=rEz(Uf*ECS|(Ct2cgTo@Rx1mvQ$dG!c
+>9_;YQX2IMUh6w-?Cm4pk<(BER3+rvnAuz1%!zs_ER5*a19nJ>S5uO&z!!BAsaN&E(S%`u60A$iJ8U`
+z#wjo@QT7v)@m`OpNeq>~Pm#<-b2rOX%hu50{YGeJAm!FLa#_EGx?LFro9{9;M%K7png&5uw&QG{|qd
+6!rb!t*9yS-n-MSsD3d~sJ)px=)$MKgQH*38cM&h5t`Fy^b;MP1D&_>@!cy+kI3p#&Aj#6#ZxwBO!~r
+bq>Zl!n#&Im+5N`vrS{3<O@RJ`nG(^>An7z576>LgG$FNgzk7s#ewIc6ZoZn<n@^NvZ`^;i>uF=Q9%h
+5Y?&*J}d(89>nErUcmS@tL4Bx!rv3&BhSX?Km<BBkLRFGjth(o$DJ>GT)Ls-4MAu!Nyf?>8rwxDYEMR
+1x!oX?w#URQd4E!t94|0=)YND+Z?dS$@qV`DlBaeGHWd24hac{WPBVumK(wdci<V}l#;tZmkK<yU!KQ
+B>x=QWrymksW$*e(v%&ixR+Gwo6SnGO6YqFEP{oIsHl@R$5*4n|d@!QGfV{4DolQsJz*p+nf?ZV8XH7
+`C&^14Pt#qCAEdkJR~8~ICn_o@CklKrviJy`!y+d=c&E|!e2^myBxh_3DJ+;WG0&@EaWtGqBNR>-mnN
+uXvoxa9kssjH#=Yy@0TYobz6g;LQEdn1jT)^SO!x9&F_-9<>mZiROqph(%x|JpAoG7?}2E#&0{dRS29
+nQ-tPg)M!iZ#JJg|F=2#(S%G{*1l6QfGKtGV@bh_Bab%ra}kMT}kd@iv3%dchp=9_N}-yg!Kkz6abZr
+e3Txn;XK_ihuw&YjWZOr{$JT7X%Yh6V+XD0&uV5G9FfFH2NVcgWM}$tBjphzzJ_sKR+;95=-O0Z>Z=1
+QY-O00;o{l@>=HHY}u+1pojR5dZ))0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUM
+FJ*XRWpH$9Z*FrgaCyB~U2h^c6n*DcSVYkhiKP$whBo_9O1hEKGy=ALBxdFUR%Yy}Ka!xT{q4Qi_Lvz
+M67o`{Wy9FM$3FMm<2${J3-&WNz^1PmR8}ZydcCaR#<1^;#luu<r3Y#3YTRS^>H8H>KV{B9&Q?2Sx5B
+Ve<*tBH-_NWtP&3}2TuUK0gZO_d-b;A)ZnQeygHlD<U)t%3{jHT|K;wpG+=qV=7+o^XDhv{%=dfnWrI
+5ldmjeUEdgw=otOR5D24Zmf(CCN;*|ndO2GUMO2a)=hX3Q>4pIx#+JL*-hfvzEU8N^i$zzcSRXQ)K>R
+1ig#)0Hw{Mrncu>$D6TTP{68%jJ*_?v;cgqwlLk+zkG5U~J$nvG@jeX^z39aLcy*85l-Vncx=L{4u*T
+?~&;AX(n+DiNx>pI3XVL*yiqkpswEFA&K+(-3jr{xbPC&f}~k218v1xWJncTb5R&Z%9CCzbCxM7AtS2
+WB99u^>UDPpj@?2d60?e=O=|;USLk0LQ#qUwn>(vYZbgPf;}Rlq#c33QCQ4e6_VZ#_!D%Qwg;m+fYHf
+lURRWbJ+JJ=D3eSThTV7Ql(F{cXSqJg{q`_2ZRtMH_F-CPuUg9F;poPm;&N5yIzjadeif2#gIXRkwDl
+0tTN)*EGM!iWQGAz7q2O^jYb_aajVVb}3*BMwHbJnc1`n3I{#FhM2l&)mbl`GIptx1wv01CzUN`xHpQ
+PBFh;+mJf490BH&U$j9v88l`Ib*-C@#DX!;X<swjP?t>_fa<X2d27vYyuhF2qi;DxJW-0eJs~FF*$}A
+C01}YH%f+;PUfJC9YupcW2B>ZhH->I*S-V~%p(k8lENRoG#X6;NBWqR#A!{azXQU-E)5$@czy3&>ML*
+4DG+@cySUmzH1pf-R4v;}1@T=OJK1u%f&8>kS~Vzf-@;h$V07<nz#6Ram=3RJPTwFll5H{k=uBQcDc4
+^btTuRC;GnN0RnaC|04@HAu%>^1l$e8zhD=@8+e^GHL<!2-xkWEMVJ7`PYZ6DkK%^moLOx^p=O}4%Vs
+DW)#PcdVa;*q<9u7z6%AUZckY3|VWtr0^*6xJc%v+o9Y{G;WQ54ve-~d}G<a3Pj96!#0=S_Iy*MB{H{
+Ou9XmB%%)h+E6p*N&&(9!IJ4jt+eYF=+l6vUhcQNJAh12Sq{Vk%4$726ApuO<I+-0`xQJwvD4>AVqC1
+bA_6as%}51>tNhko^27Gc#v4vh7v2}7PPPd038<N(T%n5&f;M^$Z4JT<0q`mm6EKw&laGkWo38!4)&-
+EYeV{y*AA~hkGO8Qz6nQH*ePpdh5i+Vdcg~$19ehh>yw`iTe}P1@Ssh-55C4<{^IgKx7@H5;QY)7?}E
+KksBqltAG~m}Zufr<*?XN0o(aXU?+jkFqHnSv&Nkv1XOR|!tekxd{()-~;g5IJg^eb=^j3%JGolm)*R
+g}c<W>~T)6fJz<+()!?Dp9k3>u8qN39*TUiAL!MY|zRPFe>*j1+5M>7ju-;cq{P1x`svtywN4=G@;hG
+L~b!ca5v6f^G@_I@M}_VlIB2-cM(@ljU?a`+bHx`D7KUmCIcU6gFZ$`RVhA>E!mNbN=6C-OenjasB6b
+b!yE4D-t`o`en26;#r@Lex@q|^jei9EE5+p|1ROLjZkA9qPmrSHbh6Pu@11i@Jjv^ZzLzt9PrfuN5ac
+MEVwQa&v)2$mE=wzfHHoOw<=#k&cu!ym9~OVNN=3&q2LyCUVZ?PUUpC{;l)SEwb(cvBq)M;^JmmLU!h
+kj?`#pG>&Uf2?-e#nPCh|CXf@(P)=|E3&HggLb7o;RNmS{a%uA7c@qs0Y`{CA$%YRTy0|XQR000O8^O
+Y7y^G0R)1qT2C$rb<rF8}}laA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcRUoWo%?~E
+^v9RSZ#0HHW2>qUqN^XByWzoCjC&j5U`+2w*YHb^yR~l1U4;E4jV~SMJkOqY`=YXBqfoOoOH$XB@xAY
+?(N~-(Y&Zctq3b9&!a~1T+X;zDtN)vzG8AV%W6@OsG`X&-7!h*2FdSdvsubAvTK->>qL}_m1>PbVa9V
+-(t<4sY@ki9;(%6F&J(J*D4&Q#F?B3e&1exURx<+6d_LEIe?c^&ZWx)rG?2$10CG-5MkqnxXr?!=Dq7
+Qmw5kjWJ|94~T_s8w`wk#Bl9xNODnz>4bS`fqSAq4Q$G^x(DTY`HEy<P{T`cYVM*SQ6zlCib<=KSoHB
+(Jp_R)__aJ`@|E`PixqHa~p9JzMMBqkGeLDl+((UjF|?M-O1a!2!q4Fdlw%PUrstSJ*r5?XEm2rX0cx
+e<y%0x(`VBL<UbOr}_EbMltfiYHA@>wFKfWm%IW$Y{GHUv7AE1Agvq6hZ1xUTTC=l*ec9UtU}o8|9MZ
+{MO`?pzSh({BjAXc`m+4^6u43a&mI|bV;bx0J7NuOBGAWc5l9(K7UT$Gvm~==$TKSF?BR;fEq}c&fq1
+wu4*I;yRQHY@F75uabv6~^S#F!ZM_eymnM*_THNsz0I38mP016HGOKMwnNnar_8|n1mlhHuY~F+U)zw
+vfRknx9P(vaSV-g(6czz`30XZTb54x?Y-uH67Y{W`5DGaDgI1l>$>^@<YA|H*(=X%$9U5naBsi>5_Of
+K0_iRP}+<@yOFCVKnF>57o=$lJZTf&88HqQ>{w{OwF^RCfH_n%M4e%A4J{uCoML6Y4EXGARb}`aV9A)
+8Vp4>lHX|Ne(`eckDAzR=wgZPeZZ&1UwlWvNMuWMO((>af95|MvQDlx_8dV*Ao@aLyM82ZohtkLSGP9
+o)|AtrMG)rlFmvZDm*+i@5`!X@nNmz#LH=zlqc#~pKB8&gtG#8s(8s2r@1L1Izmd9^iD?`O)PTjHEv(
+8L&@@NNo>~v50{;_@f%UXqSc_^ob!Ql(OP>IYw^LC&gZxT^mpit##*QUp>-)HfBoHk;n_Gg6A~Dr(7r
+AV5q{Dn18&@iv~wSu^IA1k&SaS6a%sXeuJUf!3=vlvZngQRsq>s~Pa`cUBbUS*lF>wodav~mk*CnL_f
+U$p#xYm%dEZnxAV?%R6tM;hFSgjP(*{~Ppbj1Bam)6WvZgRfa`xtDy^2ZE=0xCB_d?)I^yzR15>1WQ7
+q8C=cGZP-<;xALndVvd2_ag_Mjg~Epd-7d1qiKHGcScfCPc6z!Isv+a`1)TGPD6chqz78G3s6gP<!m;
+B+*U#DOe2TVN%LFdBAt%Ma9an->`__-!xnd5zwR5f%sEHY<by1*tHG;OydQNd`~`AY&Wt98a2ez0(w6
+5Ukn)`7Jc+^!8CF`R<wDMq-d}LvbO(4ldOfq!(jrEik1m$i?>eT!aq&05rEB(1&L#F>VL;)$O(6XZTp
+HRG;UpEm`Oo=2IvnA%}#$EDRhbvma*9b8b;qe^PKTOA3!r6!e=NVp;JQ!m2_;QCm$8S^K}7zoWr{cU6
+W$>W#JlN+rNV|>&tu1N;|zOuf#u8+<w1fb<I<{)syPMvG$Q+7N5!Hi_iZsDLVLrX|YxC?GxiLA~wj6-
+3X@RE^Xj@F(nT~{u?e4PP9MvHwJGD-rf)bDs&>?`Uy8IjEdnI)_TPXxEP#AyQr^HL3=^_nM)AiXiq?F
++Zkop_03Pj8MJ1gCKMdh8Qwyy7T6!4<KiuhcC8It_Qu`Q`tHF&$~L!8<N4jw`3rJ;0{@)AKT#BIrZcB
+4f^&I!-B&>%f#!DXnSG#2x?7fdt{B$JX}Cy7HFQoY=EY$PUN~CWQT3f*bmvV(D`+@rXyz;TfV*5Amx$
+m638GJem!Y9x&sK|J*`k3D5cHkiX?_l9$z>+Y=^=V3pTpE{)6oxmFz&2j6w&LGv|ILU#Geb?_uqv1Q6
+#wb;@o0lkURWQl-;2*+A*k*cI%S7qR>1R;|ypA+6=%IXGQE4rx$fmFWcqo#efNxD<)<3rQ?NXm?hH4d
+Iv$@_YS-23mo9qgWesq)^*F5HW%p;3H7p}q2BM%aM1ls)oj~d+VjC)NWAW-mnMB8eQVDxS%!BQm!Yr0
+2sy!L2rpD5N;><7ba(EEAZzX!M3`0zi1P_z&zHmB!Jy3dRt-5mcW-&_{qcd;>jr7uA^l@Dyq5O2+X=u
+nmJfh-XLA~OGW_1*nJfm^IAx7Vfz2eqhH;EDSMZ+ze~6vpc<B%7fj!WEM;|dQ3<EqI-EKYQE%CNuN51
+Y$Hv=v_kpAs@@rdy{S#B6GxsS;7b<&Y*b2$4KP)h>@6aWAK2mtey7DsFm5g<AP008L-001!n003}la4
+%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kW@&6?aBp*TE^v93RZ(x#Fc5yvuQ&}&O;Bruh
+7c;+%fyiSFeU+SfEp*cu4axMY-d|v`0bn>CvDnpY>}p=zVGh7yZd|*b)$`gQdd~oBv~0<L)(f9L}&2t
+@4925`@@TkL%X<+aMtkhmfxW5%~m|@Sr{Gl)*{+g0%bM$e8oR4TKCx>Cds`K4h@_~UQB9z+cxQ>b*tH
+jN%rIHSCS+ZuAp3NZE;!XR#n_=(rlgpJtrq8;s14O0PBJqZmm!^ax=4(Pa+s>BrmZ><zS@^)S^bGwX|
+$6A{)gs=vt$?rlc8L<3r@(=`KS>OI`fN(%H1DZ;#M|aNHs2J1Vb3uq0T%wT*9t1u3ipy@K?13U?W}mi
+SuWs)9;T+-Z3AAWNv=Ixi*FH_!jJMoLkf#n!Ujp5`%4Pec%BYKe7Rl^D5BLx#vWfM^0VIp(XQzj`PtA
+W&KyV?Azw1dH!&H~^tQ?4S?RPe&TqVd0-mh!JqZ`8fX;wHt<ctu4DArmus4b}+I>X2lp(*cdEnVAXsM
+Z`u3gKSTV!M&MN?oY2b8@L7|An%~ld-C9^!wW{>aA(}3%mgtbdf;xqqTGU#Exd+YjL&@b+xL=EM9c5~
+tFAj(iUZ_R(a!JJ-l*Wi&AO6kD<EYZ2j-GHE^cJRy%CC%N-AX4KZrqZfb;TVY2=zDn{EJq&H}cEi4Lq
+=>F%Zm`pXZ%(p3?wmkI<|Th7@mnikZ&=SBtAlPp;-JwsKAgUPqGKHhdbzkE@W1+BO_F)91}D8)xdv+v
+ZxVKxx{_4VHoekcx=Dq9P3qQg1!rUPuWA!VRh*@T#kJ2o#0)=&sDidMI?Yp`{Q%7HQUtWXMN)_cPcs9
+W3XEi~ZO>`OnhrNwDxaNsDwco1NO9=BJv#DWrYutQVPox-U=a-LmKxVlsoOqI7GzhG=Y>WrItY{CP)<
+`5(z6YtGxIMBMEyw-~@W6CfGDE4ZL9=RG&1ic;4NccKtdxXsg{{S9*X;FfOF;9im=g@t5Ktka#|b<m?
+-orH!)<6*mJv&S1=YUSt)9O%XdOpaWukJ~$YYJH?3&}cb?+&Ad%xNI@arlau5KTt~p1QY-O00;o{l@>
+=^s&pGR3jhGVBme+30001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK};fY;9p~VP
+|D>E^v9R8f$OcIP$xH1(BkVTqx=z#qGj?3-og7wzxJelI$H0*+Qme+U7=<bRw0+SL}b^89qc(lGAOk{
+t!zfXE^Vd!*_MnNK#ar4KFv7iEQ>WLLQM%d%3Mjaxx+CSvS>=v`ta)Wm?llFv2w$to>f`a>DL&R!j0u
+t)4Ya)yx9GQJ{(1O|!fz3YJS=mBJpmU^y#gvHw-D)oE2q%1hRm^LbU4`gmPcg@t%~b@l1A+xY}LiCVi
+17azodf3oF16Zw|ym^rFh^ND+3<rEkGMT?dxW)t#(-d`|~oq9Ep4f}7)gmgTq*AEN?yAtM1D|wNEKsp
+joP#G-v`we1tXy9csnPeF)iYy~@avdROA81K8tcem5eQK)vz4u<@dxi)RbMzUYk?kxms1QszmI2uprM
+GA+WgX4Nr0VvCIdIuO@yBEW_5{d`Rizsy-@_wo;w&raj%C?&GP%5bpZ)XfKmY!C@sCS58%H+`h~x!Bl
+5+;C2wp&%LcP!o-{DzN;(sC`itLs(0w{^#hKXX3c+!bOB}}!I9vvO2@7G09-3hX;8bX^TmkkB68!ZZQ
+w*_(tES3Vaq|B4tFjfPDxSHRPYOT<KESL}o>&%Rxz%}Z9)ut15a0Ynjj+E^#Q`@tSqw{vRWDOu3qL3B
+IDQJr<8DL{%#YIk=m7)Sx3r2<P@HEDcNDGdn5QJB68C`+A+{JJ80^Y*Qyl7X9+;O=jU~8Qu64I6ha7Z
+e+u(3EmWQ=V1Eh{@@?7l8|&f)&>*595yacn8pvzt3d!|T<G;|?u|I?tp<ued+~G>l3H(S?a^2R2jMo-
+K5oI_dd%8XD;W*b|bLpeaM!**)FW1)I$#9mZF$UXh(P957Jdj5?LgUrdAJc+h}Y|Bs_z&aTjM(Pw5)p
+KhvZQ!pTJ_cZ!{fOnJ^-;Q8Uw^cQEdBMuq!YbC>_KQj9Ch9@O6S(TUDw$qcu{FsuUUHdbv0%kIaaKKl
+{w%RZGDnbvjOl;s3?OvUw1?QBa^&H1Iz$Kr4i`fp?NRgbbmNpfj5H5?z#r=hV)(i(^Lasc%M~T{jMQm
+fz;ZmD`Vxa%Sz|c!1ONfQZAwLBaQfa81LUPl?rWx>ZfRkbmL48083+kY?8Nm5QwXAeLkmdC;7$!YZYY
+OmTJ9l8NT7;?^SR8|Crm+ll$GpGNy?C>lL=_55;9R))uYJ<Tl}1AKc)~e?>hhOJL;06*F*es=z!3bxg
+~Y(dbQ<(?3B?Ec(TEQ=CXzGwYO0NqvRl}ZUH0S(z@8W+>;q%n=L#uvPpEINB}pgX^B{N##YHYiFb3*D
+<bTW0Ub+5Do9QZU#(cFGC|%7S?&1$R5g)~O337CG}7^ueXYMfcMvyswAtYK18&hB<ZEKH{@f<~z|g;j
+36|=;XIS__w!rmxN&NIa8#s{04GvhxH+PAOB-2UX%q;V<(>%rw4eR-lw(y=0`BFUF)p8HYZ3qY)@(AQ
+rt#(x6ibIx^Xcjw_<}kmYtM)DHj>BeIf$^=^2*AtZb-_1V=~)xx#$~H;?cMn$^B6l;W=YrJ_sV<<c3A
+l?Ms-f?c<$LGipU1YPFg11YBkTbjt_|F7Y2-d-3QTmd~J+6;=46jEO37TX;zex)OA5ZxYSYsSAY->Yb
+)I$AlAh~;{ljfU@Ky=Ee>V$uv}j(LLvHgj#@yfLp#(lZ&=O8N9UC~PDIUezUFL|9(mT;)yn1pOkRuD^
+IC{ebNvOoA)#0e$2LU%{A?QfIuyZJ43C6RM}VrxJmV;;{3c}W5$XV?8sagukX6N$T6R?CTZr>qNUR^L
++AH2Pn!vPi-vsN>K+*t8wN!JTK;+nR7sd;&J#iQyybv(y(e#1dV9rq0Uyl&<Y-D@I1b8)se@KuAY4g(
+7h_rnu$I7*3<Fkp#qe1Jin}R7DFNLQAu6W6Jyr2yRq8U~Z2TR(~ytMREnALNQZ4f4RI$B!8MIf~AEu>
+*v$fnI9vU)M#a1k#-eWmkzdt2(&*NYBKfP73?E$YEG%`pmftcn_lN`=cd^+IKv>WF57Wt-{;FXRGp^Z
+=tjo$-|(6Szuek@;2AGM%FiDHlXRsIaBQ0T~bb?$q4A2%~^5A)vIWgg90)%aVf4H_ugou1ctW1QaAfN
+!)Y>$8E!dh=p~G@gEg)F`{}Y&~$fp&#9H{fC!A~bSbK$m6&l_h%0hSu~H+nhK8YTI22#sZDW82F35pH
+Q#*0_&GvwIj8rq<We@ndxx0?A8iU|2aHxU65yk~w9{b0vQbx8R0!F5#iBzJ3+R5*REed<)vt_|71c^=
+uD%GAr#e&$d0#HU^8Nh1q2-DS3FB>xGTS~!9+hpx64(wbRw(IYKk8ea`+fvXDx9B{F)l$j|lam2WXU@
+ojMUM$38=Uq^QIqorNBY#N0AaB@LLQOJwpN+4VFdu*GV<%idp*9fIB~A^AmwJv83zHPJ23TcyLfX-et
+L28JRz5+EBRacGJWZ@)3Gijm90Q6o;*7V`7yl@RP)&L2brni3@dpk+A$YLW+fU(j0-y#iOrcrPaLNM(
+HWF#H&spqU5|t6LZW@}IL0CK0d(dm<t4OhJLp8?UZB-tnA?Yhs1!a&bMq`y&!dB>Y<Qy*slH0pff+;s
+OX)~8f=B+E?Pzz`;{EIxIvft{i$ejC8~9&W@zI4dHu6}I<5%$ah&(2TjA}gj^}TQa*Lk9yn0f=`*vre
+kdFkKTmfnKae#iJ<@Ee!R{amhFBAtHxF(TxLrfN5OfTxSLH7{{YiMJ=Z3s4)UAJ5OvPOq$eqZg{VvvS
+ICw5Uhjz_8OHW-d5rQ=5*Cx*x}0Yz%%d7JUs}qH;_#)ddeh;_T>CWjfp00H@NFR2p4M-j$HSJOct!kO
+AE%VokJUh)zMOw9Y8xPa~+rOrCieRB1{APpxPWy+fxEsjOdtC#O&(x5dgbtmLN%>u}K)KugJUGl>WWq
+QR8Vj{a=k4lKpa7}2Z>)o-0VfARBR%K02qv=Yp>Pe@;U4HmzayiBWRGdAJWu)X=_^YD(72k#IMU(&q%
+DzpR@L)mm_2s=vRNpA!@GH~ro|H0YoaFiLC{KErUKU;X2!n=7`xb_l9cNWvsV-yGcdCVgWYTaIH8#1?
+|Dzm>lhjnmaGiS}$RfyfmK`OL*XnOg;YwmkZy@nWT?Ss43DM&VTK~Z>EZ1x&_T~!hvAG&|6>$XJKZ{l
+d0kl%tUqH~~I=+eaoQah!1EKtTiN^hd!%EoywG`KBmBKAo44&mg>9}d~ja;~FHLAcsAyyGxLHmG`oWs
+ifl##H4+qePvbx?c|hNx9j+b&mr^|6?a^!OYcYakM5q%+Dwp1IO@Zbc4p_2i)OwVR|NS!QAQ=2Ay;!p
+neZD>4qX<ygJlHGpHY|{gsU2?0&gdb_uoC%nt5yockv#6Ti+?MTlL*+sgZe)F4U8N8Kd*cy$oC<pw*V
+fdqPB5MiM32l+3NzAc)O=*iP3k>?yGlaL(;J!-1ggAk-vLgTP^Tt{zhuW0~%$U*hugQji0f<`<Z{EB9
+b!k+sPV-Gv)z@!jgPR9otNjFgFj}g)byLzAFul2T%$Qx7ha8RYL9o?_l2mr;5Rz51({eW-dOo+_P!EN
+mo)_QMDbqZx0i=ZM@RV|mS*!QrY*&-}lOSC=-RX-h&TLxFFAM(>N9eRD74Ub7M5(x9o=<r~~1AX87mS
+8bDC>V5quEju`3jZS|cC!VoH*;rDTy}|i?oA^81W-!@1QY-O00;o{l@>?l-NJu>2LJ$f761S+0001RX
+>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFLGsZb!BsOE^v9hSYL13HV}XJr#NH}vQt%k
+*qZ@k2-0i`&@@5gVh>GV(GnfAkwlH6;<!cj*>^`$lw{d$iWU=sNT$g9f4{qvR@><a@LDOEL#?t*ETfX
+Cii5~YsWh$eB9SQ{@u!5B8ZONGnNm^>jt&ov%-d)sGG59`HyiBRMP6rGAUnNSNtLHev(ad@Sg<TxEMN
+wof)D(knpgUg>$Oa);0|9W8LKMjzZlEqXP)SZ73k>b$lOn5@yH8-yg20INfdxwz?|n5Y}S&b$#~8S4G
+g9^(wfF7x-5ASLvBciP}RxWkYg20&EaYMuT}T=D3L{hjD;*pDYNK&KEFQoOV_v*11nN<d~lXd<g8%#T
+t&3?(mu!0$n3vX@=>HH=#sD5qd>_?`kXz8;vN#2=du93EpdZf0Wk(;ZQ>-aJm-`rhR|j$lC?QQN1EL$
+!L!slgeYqXb;X?wHzgiji3A<84egRMQ{;{>oF`GK7(``ctaxEaxh=-j7*K{0N~O3VWDll*ZqaKFA3wZ
+<G1;2z{MXq$g!8kLw;{Yedv`WJn?&E_zLe?q=hz<;iDR-0{c3DeS47B1DIoRDX7ieY^|g<2Yh*7wiEw
+>&g9==Kq<<&#)AIx3dy$=2S+NCtNIUKcL1@^BEQ3tS2dGPu&xT`JDp@MQG;h5}k}*x5Coh65c%u?q%0
+~`*BYk+Fr;)EE)=}DtdQk{1SSG%31*kj2Q(RS3r-xaAHD{?=1b3a=1tKkqB&$=7vTHO8%HOyngu2K$r
+m#e&s-Z!CfqQ}Z>WT5EG82i=**5ZNZ5}!?w`i1^q1*NNQ((g-2qC~2q8mjK-LJ0a7gz6Zg1gDcCB+IB
+3xQfL7UPO%tFRGe_8!ePX_cavQ6uKe3yg02`rOiTxZy}O;DM_lP-q#VeHNkT;5FVs#o1idc&J6mrL2p
+TsqKOD$Hxzw$rRu>I9XO&u|)eViV2K|_flUpkjK;U(W%2i{-j;f^l=qgTg-MSc{E_zyN=MKLV?^A5PK
+kPkTg2#Sl$K0K5N-L-DfY<&k<dao?ey)@9Db4h(<8)C-5$`o0v37u`h?@%2SJDD-9~KWEG-?v}h5>R%
+BeqG)rB|&^2FUO5-0xi0)J$vm1|MZjxw1q0xy@#u-Y<a`daFOvQRov#d>l6(+Qf;6g*f3akW%9br~Z#
+k3N-W>je~sSw;V4wWXTpn5@2?vR2mS<*gA%oH_-lzc=0ATjMKhw>g7Hfvr07BS3H3^rQ=5!yU4XU{z)
+AUyES0CXF$m^&PeV@)78<f#l7vRzL_dwuMzIcox|E=#V)lhz4h)$Bn_ne^QQ8Pi@YE3RwhHV^e0?~71
+-8)o4*t|pw_{fEx>g1?QBtl}@$guL*pRQV}gc9hwLg|6klhY-IU6FNEH_6VL9?Z7jUPjpPkRh^kCzT1
+Cg0{ISFnGm>6_6Ih`t>xYKDDs0>GYI|&;8ojNkL}hr6uORYs!`8wFEhFck{`n5Z+iY`Yudr6`G0&()_
+A$Icl$o)YftfS0@gU*YZH`VjP78#I0=b&v5U9O;Lr?H(ZMMycEYBF@op09YE`mrCRw__5MtrdRP1R`h
+~SF4v<)^7`=yC|JhnU=s{TZP<gt}ArRvSo>pd-W(C6A(?9%t%zCN3tq$!1_RFN@E_lt(XQb^se&_RVh
+VwEh|GTXMz99wZW<4|Xf^})8GTI5ucrHtSl3(_N3rt{9(7M4inq49T1NZc35(-%jp_0>tO*B$Y1ur^@
+@r&-LEV3u{G&Ue;S{2%!C%_GZdD&q=iS8($FttUlqNkLs>Hx|JO_#d|47}M_psf9ox5W?lf<(V<(1nf
+4nmFmZV-$EoB`6Bp}>hlw$wr@He^-|JJQ8V~D$kbpZWiW+c$y6}R<6woENh0EwF>R&iNIz+6&YknkDC
+=GYlRY%h6Pc%hd^$7?+_f7fSo>Hyp6sJS3a1@1UksV0C_EXr=Ba$IxCJ7|(f!TmlHc!@yO87<$Fiqw`
+^K7f$8yg**nxh;yfw3;@qY-V`ApER5IGo=b5}xPqVyuHo0KLdCSAwFvZnTwzAfN1V8LT)Fizwmd1yaG
+Af=^}n+hvHC)RPe;I_rZIu^&SL|~B9Xjjz)0X#Lx2EnN{!RtA?B5>8jjJci~CO8hPYNLe4o%l0hE@xA
+YKEfDuy?DAsm9cqMnD#P<rkuDO^7QP-j}9!Nnk9V~Xi$D<_9xKiyF}aUH^iH!yq#2O3hkDcVVUbD{lP
+AvnK4mZs*3$~pdCfKlWFS*9Oe|m3Q0*CxVIQ~U!R|Q9rC_T1D6A`ML@vV*4pVHqU|_I-ZLKpDe-JMyq
+6jbhS?7rOGH<=4J@zQz(Dh@VR4JeM&^?RS<Or!Tl!uR1(VUPOuKb%C=XE#n;Nn(h&DU?k<qnIq4(=-=
+%-1K$FR*l$aT=5g)gapeVrQ@ezM-UL$dU6U{B-YJLB<QYE666Jr9GVa~kI;G!7rTg~?!VvtiIhnuz#*
+_u0^yjQ#^qO9KQH000080P~d=M?(S``VSHS082ms05Jdn0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h
+&Wpgiea%^mAVlyvtWpi+EZgXWWaCyx;{d42CmB0J1z-gx=DUlhkeV6vs^Q9ZFxA9Dq-LZX_cG8Rlk)V
+W{B6T2WMg8mVy$65<NJw^ixlZ_lEs@~E`@;K{R)_f+gG+9AmC|gZ^)5*ZDN4<FO<xPSIuk#oVyD?1Ex
+J{zQiHc}nf>~A@3XAVa#d|vU8PS#yBD<FimIV49_YcNxp8I|CyTTy3Xy7Am9=~4o5#mb-w2)w^<JjhE
+;NYw%O-@_CspYRfDo=T_Fkk_ChV7c0AlxR@6<a0A-=2h&88`zM0Pf{8|--c#Mh!uYWb7fqSi{5>xDk-
+M2#e@crDDm8C%Jcs{=iou}^=wvS8AxEDb$AIkyh`8T%SwJUsk~5VG$yS{8}2sJYi<y~uZqw5V!8^6V@
+vcwGZ|dBL?XRH{j}`Wx&uou3gaGLf^zLY7i57L!^O`P9e`FZ`M;a;a*)(CWah*`sPi?{UU8hbJColWA
+`W><BAR?nwqnY<GZm4L4z-hr?QEuC=mg%vi~{!b3(0-Kdh;Rg)I7UD!eDwy=N@Ll2P5Sy^ei+uihbzz
+1jj)jd{3Uu;qGsGQK|XEC0Aer>jLkNyF$Zlyhd#9`5$zvFWJz>8W$)*O}8Io<pA-JciVe|`7(Fb&=vV
+BW{RClbSe5JnFAQxp+6ftGz&m7>+_KmJ|r{!yhqO6c1!+2^cJ?5|ZzU#}@=XM{fBwa6^Xw(!?(2%F(I
+c74spYvAg5K#<RCOND*Xvo6zCJZQq4>72+ax%-G#%<ecEjp#ovY7Hy{JE*xVg-Xm8UxRtwbCuQSNDkL
+>RS2dl=TXMtCTs;hLvI9Yt>SXIs<Oj!33id-6%{y$jPWvKh6@?1%JWQY@y>oDN_u^{M4rK0J5}w3(g%
+2*@iYZ)iY#I5abv+5I`LsqSIk3kFZG5%eLCn381-7i20#Y`q!pM+Kzt4ipD$&#BuMb3DZnKl9w{#wpp
+pVTK&%qL+zkB)2fBF15x&uOCJnyGHSv>XRSsXdCV;sDb=z0!$#U}cC#^W!alEaCs##tsfWP6-vT78m8
+mbFAc7SU6oA}(dM{7<na4|A-L3@|F)0exPoWv{;1kj6u0KOCjQf1!;y0QixBa#3vvLP3k1REJSo!AG?
+nb_`hje21gvw9~|nah;fx6Xl^RR;TvjW<TA1B)?Os9S)OLOuzr{v?NSW;A;|ZSk(3<ZfpbNbx-#!uDZ
+<KDRx-Ko4vX(o!k8Ml-iiEl=J0?~U@wo<{E#hYc0#*jm83AYCB;&*M^R0U8yVF{y!Q;KQ>hDQp8S$uc
+b(U=vEnp+x$SiB+>^yGHFGey9_62k&+OVY%8%K&mN`)TBMf^x@{?caOK<Jucqg-h6z2d;b8|^#{YuS;
+xsAY3cp#`@8$wo5#h&<GaWIdbp*vR~K(*?CRnY|6Jjp-{PO&{dmNn&q$%J#{k_5@)~`^43C;_>g$Ucd
+xo2!PWTZHP7R-E#lXQ(z?N!U*VF22qhX;6nwvu?1L0bPc2!l_k6oBYH=MV!7FgD(G#n28#F8}YMYYq2
+TenMrg?)t<g|A?d7ee4}(_Zjq_zA1chw!0gF+8=O!pDZ~@YH<h14gbBVEhn13|S6~!W)Bl$~YevAcPO
+e@<gBHyZHTqC}G*)-T>BOb<m<-ppl047lCXE2ecZVkn!G(O^&1o)0nUwE%>j`1JpBkg#UU^yzJuI)4>
+424X-I+F-VP(h;bAH;#s)=8}?3tsX_~gwwzp&$&=1&eL!~wj!E&d&Ot=Z6n_;WX<rzGrrpCuA#&Z97X
+)Ge5S7SN?F6En$=L1K8GoQ_JWYyfPhm=j{rjpMquWct5hC(C0dfYf4``?8evv2+uDjMGLXD<6lXVIX9
+UsX~cimQaUMBE`lzcatl66Hy(PI<JNe!l3Pe!xRbn44tWEnBSr7gIKAUcLoPUeyGJ+L357Ko#Y1IviE
+JvWx$+BfGHyO5{LBWowSB$@)bZIKZ@Yf1OITnTmLgbF(YkxhE$HT>#D%ow{Wkx2#WyC|xZqZ0R{dly&
+Y^{}a$B16S=uQ40vDU2e&>X<e+v1~m@X6ntMd%^8vvwGwOz*_^Ayj#>Wjd)QUg1bHQLS`+P1cFOcU<^
+s~J^;(W0FM76oecA48AE_JO`@!TSA1(OZLToz#K#lR;A}ap#$IM?7n|eT3Jp{QY(I%}Agds?Ci7J(?@
+ni_Vpzq@AN*}}x0Mf~Uy=xHqRA@p;U8Sc-{ol#;`I@K!#-g2jLAvWB_}587hDw*>|CP?h|R9pMe?sCS
+9?Dhh$M1Qf^;slwe4H^?)W=&tWutC!l1=FOIj4gsiX68h4zC?L)mOsKo1y0@^26x76nGo;PL7C9#oJ)
+`jc~&ux~k-9GRn`$0)V4)3A_ltEQwZ#Tg8PGXPs1ARiKXGhZ(KV3r6m3Qv2t;5cka#?gfGEgBP%tY^%
+nz9O5(PeZ`pz;S!gCIy==S3YHBgg$R}ghLyLpevP-jSA9nfte#$aa`Vj_GvL6q$+4Q8epukVUS*=aE#
+6&co1bRwS30N5_Y=(aKnCkd3E8zd$4+am0Y%WE%ZeTJ?1e7*;3qLwkqhT1)qT@$Rw}*jhw0J=La#@(1
+me9p`s7dug91*UJ0ciuFqIrH1);-J0e6=y&xe{PPgNzK8QRyx(XpltWOzeW}$%?7we!TCi5O*1UaC4S
+o^Y7uVB@SRlv#JM_ah=>VZ=%AoQ|<Us5OVQXG>*ua-U;&@;M+vjDoAbpZz%&HgTP_8Ulku$(g*!pONq
+1JtGaQYRz!n*lvDCZ>Vm3>3jD0=-rut0q4sjGzJ@vc}bW5yIy^2!z#S=t#RVwDF^0(v6n$t+x4OFLJU
+;W?=Wp9Urq?aw`k2kdxN>#wrTN2pegby4Jp8Jj)~+8Q$d@U2SQO6|?{~u;Iwt<uX8SxlFte%#=ugxRB
+}JRC~5<(v7KZiPm=j(ui1sg&<q+5t*T$%uel!HK7Pa${P%GHAKdu#@KkvpTNut)J1?v2qG<b+}q9I@Z
+jD#xW1m#wLBWoaja~GS6jNG%8{*KdHRf7QIy=)s^CrlVR$TU_CG`<;P@##YW^&2E?~VfX>E<X$Fd?lt
+3kv_byH|pQw`M?UjwE{Eu6qG<ZQn*B_LTGP(rzE3z<n*Qgs)|599<4$Af#Q8k@pUbvEgjXHjlIpe(^*
+V$Bo42f%m^YRRzH3H!qPbeN_9frRSgG5H8#jgnagc@b>9T!KD!<zsi%LMPMckupa+2ps~)*)urSbB1C
+W)IK5=k!U~|2iFgmLo0ag2kdwa99Oit1G@re;v2f_4ND&=X}bH^9yY46Is+DcD{y)F&dMsfv`<OUoOI
+W)3g)ihWJG&nCvgHQ;%Hgk{L3}FXj5Wp;~+~u6<p_)+Mc@_^_+d(tk(xuK&$bjn65z*g>E=|gB@OH4_
+zWd*?d_UbU`%kL>`B8Vh22t21o{n*47X9Xa}s5_`)9sSeQ6<aRMMOkZo9zNXV)Spm-LLy)(@cn=)e13
+!I8e#DIiYLr`+^_TOgg^5X3!n;=s2FTPkc>pC&;l1i<=kagXNw_jfV`|qZJzzLA15rTFFV!brRWsC{I
+Tw*w0qv670LeD<*L4xT~TYRx)CISa2)cU|pj?21LI$C$+B;XZ24Z&&~9|j{8!=nMo?cJZhy`8hWOi;w
+T7vszv++ObhQe4-9>JLp|v=D);Ew|V~Xyi)@k_NryWWpid1ZRk@nHn6_46&`PA$v@)+23~3)dFVBghO
+LoLlY(7NgJK`B@2%!{(wa!Rt_0cTS7Rfs7BXfDKMEYr3xCmcX}`2L^`nbS1~h6rB;RtXX;EXreo4YNt
+LGwgm6aTb4>r9k6K(hwdO3Mx!qv*sHD*CZ%s|mP;XJBfc_Rv1=Ti7CbD(>puM3NW~>m{N8-<wgb;ZTd
+Qt81RiKSgf>Fmcn8bAU|1P~?TIQ(uj7c*JT(i3pBX^)n+*6}2j$<$>G#TG?c3`EdCs9JCqQ;PXh%np0
+E7li)9H0<oe<%d6NUAq^O+Fh!0fjmd@O4<(B5XM7LR80QV4|}bZQwENmUMa4;8`MTGo3V<9>FcB7<#F
+GjjiylmjhjX_SxGnqe;+_eCM6(p~4Y&ja4kno1hoN42Ee?1xdv_GS9ZPTY$lY$W_ZO_;-P!DT!?`bY9
+_ECm~z&6?hHUh9Cr?yzf<N;|Q`}A|(Q+<dcd-Sh-@MDs+DY$kaTrl%uU3GWp(?AKaS-q_{~N6;OhdHj
+q6Qhp=7^egb_4h9agN)K%kXo22voh&!e}2UlE2Oao{oF2v%O3uS&qiB?R*+?lXlk`NnIHSqYNEs3G97
+IrCylLKAuwL*pm<{(9p5zwv(G>CWi8_aOXnRwHI6K9?xV0~NUV%_UkAUHJiGLK9!h1hqd<_|PaV~${Z
+?C~%yY{)rrHHn*|o-xp%&pvZ@W%0B>a_avG?h?y*Y);_gMUEwNZCN<lYPp;_wddYpRc14)l{sLx%-;@
+T_MqClvSZA>*m6h@0fIM+gXL^naRlW=tqg`h+Tj?i>G7iSvRf+1ESJOC1BJzv7(2T5ze=|C+E-npB0q
+Bo0p9!cm#aCZHYR+(8p!&kiGu{J&bIE?%t(<tu$Qj}n*=J;PBnNH@_@TWMAQ>=6xgmQnRU+Gq(WQN@3
+MV2!r;dJKt5x0b39`g9X%)(SmvoOHQKLLWR7G&;I$LPskL_OMcEGD$+d1UM!EToldJ9QjIz@lFJM8FH
+0IyQl@X0SxjsDU{aPtLGXj1SnHiLY1RrDUiWz)6Iyq(g%$DQ*wojxVdNzE`8w^E*ne(O?geAv1tHDcC
+N0Kc{L8L*UMLSE`?rn0z6YW)F^W*azZDwhV@Sp?g`HK+xYwAv6QAQNrsEK<W&WzcjKXB4((^(>31m+^
+lvB{aD_M;EFi%L&8QlFyEKY?>CtDnyu$}ViDW-<ya63~RnF%>3VUTpUv1sF|V;=#zQ9=*U&i~C8MLp&
+dC!StN!0d@>^IJOAiO@N(68aUlG;;L#OSMUi2El-u)fv+2R!M|yeoMtjAd)tSM=mo)eNymb2jBC8$%<
+XUnfMQ`|RZOAme;TQ9JQEBI4))riUKW_4q;CR3ZNlATsVg_*_)Bhx(QgpdVJ5l?$B*z9uAAa0_(hae7
+bmo3^~1b{@FVPnA7U`tMP4Vj*+C>I7H-z7T<d|r->?t(8-^LyFCd`L<+_2$34bYM)?-4%&HPe3spY+F
+-ohV1*i5OdHJP6u4QDgF!81?6qV;w6l_GqvR&2|~c-uQ@b`}1!lVGdy?;&P*JN@C}$4+AX()(~44wPk
+rP~g=s2c6*m#u0+s;qO7-u)EUCCAo2~oh@^;9W4knSC#4&-a1zjZT&j08dg>aJzvlp5KUnP-KFyb=O}
+D$)F1gf)p~F*wA^3RJQs_$dFt_Q8WEzmCH;@POAqGdl+I;UoBrU<{hvNC+bI*Ee=a{qSTRl>9cv`Bj#
+U0fe`QFonGuQ8JLpdg_w3&}$30K;g?1d`c|8F3e?|14#OYTNiHQhyFo4ki>;pUv>1QkAKa=)0c=nMFe
+NkJUc*W?!tEUW3P8UF;ermWNHe2v3FAAOP3Aas!d$Cv8HP@b#M;}Jx2@)ef^J^o+B-eN_b%6$>fTCK<
+^yJWPEV3K^wskV{!#ITD27+V5I4138!)fcedu@zhY^xy|O(UJ_pI1O$*cxqv_1g&rD&uc%-@PCAeViH
+VUeWJXY+$wSq)VX!zDIGCsC%ximCFq_s30t@jAZI+D>l`h#6Uk$rk{mTJx9rBZ2MR*$Sj^;MUMB7sHj
+uzHSSFv4|O+|$Hl^@5MCsx83uWHt$<mf?+eXY;J^pg+f?;}J_fCPp?G*Q`^PJ2d-(ILMqH%5Z7Ce=qC
+F~qRevq#Zd@Nn#a`s+>moSIQ9K{`B-U?w09|3V#0~)#(s3!;YeH*`c#Vf$ry=h#DkqGt*~+|iOky54z
+Be5WhV^sEe?dQg?{%{&Jsd?wkx%?U4X@W~Z>*2=zfem91QY-O00;o{l@><;00002000000000v0001R
+X>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJo_RbaH88FJE72ZfSI1UoLQY0{~D<0|XQ
+R000O8^OY7y2&0?NmjwU-n-Ks2IRF3vaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR
+LrZgg^KVlQrVY;ACFZ)`4bdCgZ*Z`(Eye)q57QUsDZRk7Fg#egsbnU@Aj)1q<F2PY6@>1?x+MUAAQX2
+bsb?nud!ET;j|VVGVlk;jjB-`#zrkB*K;SM$qvEH3p1a?O)i^L)(;Au^vOBG-~HOARSoJ}{8{E`b!J(
+lX9RqYI@<U@s<PpDBPT+h|=(Pfik%La-KMok5U@?MZP`$}Ho{*C(77sFM_Q%(Gv38cxQeBlKXT<->Hu
+@L5T*H8??%arB4rZ6Ty)Z|Cz5*JmQnA<<mqBlwy?q1lC*ok=O=-g?~cv9-n~6YDhvTHCBaU*ZTzJDcl
+l+f32>$PU+WJOcayIU9{eh|Izk(2b%z-a-_4IG1FxQj7@iFLMt-c{U>Ahkh~C47Ny#VsXv}ITJa%$Hd
+UDSdm5JYqA41N!?+Z7nN)f6Ww))Gv(OP6jm&Xc+PbcxeBtC&q@W-XGP_kO%dB?FJrk@`1A6vqPN=JuF
+Le)=8U7;467zZ8b5u!Wath$k98x_ZG4S2nG{lJbk>#|#Te{>$OAH=Ri8QHRI2exie<da9vHWnzrKHWd
+U17gL$gFZTM4<1bqiHg4N!sUsDw<(#0=1y;9wxCEHIhF)KTqcD=*j)w$Dq8fObK{N7yjapO$fD0u7BP
+z2KuFmY94y6DX{3?K(F%j?X50dmzU#auIfxEylM&Sx^Lfww>K-%gJru0@(_YvQBB_P(@T$rZzfxf5AS
+0B(g2JrbaiIa<z%?z~)#%QcAG%ZDj^*PPHJCJJ21pZK*U{0=uJD7X{?3+VBjRgg;P5s1YQl&~6E7(04
+m8w^6%;Vn2p#va`S*4N_Q^YuCBf7&R$@ao6-=w`Xz@u;bKfn?i9rR>#U=$2+9<aSv^4t0UM5rTtz3c;
+0A-v4NOG6>0wK@BP7r)0UfaOP%^VK=JA{<M-g~jFFMZE+C6j+(JnHr;&xOkt#Y?RaL#}C<r*MLlgj-?
+1_>~QG}g%NCfRSy>$Y5!LA`sSq^KVxfQ1^C>5_^5MJv!1?Tp7bxcYO-$sRlL;^Y*(QtG%JAXgFIGxX;
+ceCs3)AO0zZ6MD(l<SYSaGK4hHFo{!_2LieDs!Fl+1%+T&u3R62Y<MbN;|a6@c<ygu?E2H8EjIbKjn#
+b@r&$g822%!EecaQ)}?W}_S>wtqdp;{%dryq6EJ(hTaUiIB#m`!_FV))a9gjU-BNcl9(%pae}t{L2h-
+pZRv;ly@Jm>=Hyw^#idykCOJeFV)8<Jglu`Z;nuV#|5X&!EiOj8lRWaTT5cLk5p#yDHqQ`cyDv)hTSH
+C>Xv7<Jk%+h*;lvP}B@RUk6ah5^8hC$I%`s-?q?&GX9*|r2OI^A{KsI$);@A1WrUyT8sL5dEs;WpD=U
++@)Ei|HF|n6T~<XF3q1L))pvn`x`=2s1RogOQx-<H?vK{WEjtA|Y@-<?ywz2Q^lO?3Y$uSqCJtecTRR
+{viszp`7epclMKGb9&Y!4|}whANVbc*OaN7o=Zki@W@lOpq#xuy}X=VowtHAU}rCxTSYg$fij(bn!lZ
+0%`eWn;<WGGH`)2-vpcrG7sFl?z83}WgwIFlr^tKabm*Q3O#=R(GXLMn_u+Ho*>dhJrl{z*URNYqH`x
+y%`)NELs8PoPCF$q!WGF`i38^Jm!WXL}bsnayk^*mI&XYyu?C$rdo6SYOqqhfl{cONhv$rLr&B>FRbv
+R#-95@GZ(B7sP28;h%xY|J1YYDoPxv8!8#*MW|2V4DLY>Sx|sn1M>E=Bqf_R6&WLLkXOg|CP0DIw(&y
+$I*BL`$R&q>dE*OEp(K8tz={F~Q9Z_2B97Z(LtZ&`<rvYG@b`@N5ODz1*?&Y@mkHX9Mgf!5jSpP)h>@
+6aWAK2mtey7Dqx+F@<#u003?z0024w003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=
+kV{dMBa%o~OaCvWVWo~nGY%XwlwOMU*+qe<_&R>C!=SGxIX5ysnOx5ax&ygqU*pg~F$z0P6O_7j<Gez
+(K(6)MW{q5ZaKoWeBlipl4_DCcbyNlgt-@w6Ouv%S{C#H5}HJ^oNvdD8T6xnb=mIt-t8F>?sE1stOiK
+rcwl(0-HQN)VzOcJ&s2VRgTUZjbavIj~OCy!LH&4D22m<q)<ERGZf;>_VW=ZcPyB}wQeDpEyKwic0KK
+$vjvG~?<Nez+V+yrc00%eH^jLNiJe6bD36k}y;#N<kIzpp}SHmcSyfp#rQElBS!$^X_&`Y93N+vC(@3
+<|L2ehiI$yl`2Y8nv6Z~l;np+PBYw;51bUp)@FMhoWC8y<1E>p?Uc%8oHClJHqqPQ%}_ImWOm-+5d3-
+Qc|XFQ;wUS{qE>rD(bO;I`+bz5QUpFZ%c~XnfrE-trX6jJT-Y?&3S9T7t=VJQ5onO6-gr}F@%a8fg-F
+3GZ-ba;O0acM2?$vB6OC2KGQ~qnbx$^e@3o0bKgT?w-kJiMsCL<!ZUF@Q2t|?icXvx7=+6b%LIy<p1N
+XlMNbn4SMj~>5FPWloAr;@Vf7APWZ_kq=1?T`mBSoU@KseY(5XJVC)Ux1do~O7wz=aVkNg+}2{e7wN{
+m6nAWRI;ul{k#`5B#vb4u_+5s0jZjOZh{+jy}gUS7fGTOoia07Mw>?dfptO?qFe2K<tfS^W#c;5=j#X
+T<pPX2NQ-Put`Fg0_XrF%7BLG_MUb$(^x{IGXeNBwv;3iv_?C`#)ZKM8VgNh=}44HPIi$An4E)W*dry
+9C6v(326jpKh9pcz>lBwlth~CrUU>l6Tu>>|5l#*uzND1gU*67crr=5iVRs)6LHf)ll?Z?fkqjo?ux%
+k|D_ck{&TbGk6SEv~APWGD9F=zSBvpE0<@FvPy_;TLe7L>~LF4dpwtPRm4X<abyZd`cBZ9$pD5!N~jP
+TLkk=|MB<9TBsCr6l#0pYYqZEK~b`XDX{NGLNlCOVGCuU^qCcw!GMrwNM!F1D}m>eY%0$hYA<+S72BB
+^K~{bu2I>jAg&ZMtdOykHm#w>JZZBJmpM5S{e)no>v8h4spmXfs<VVFkzTe&_ZYaGUjaTtUmnk_wWMj
+F~0x_s}IZN;`Z(ZUd8&SFiKUJ!Hy~(a1-Elkt*CL;{Jz7(&k;yhSTbhspwgU6B1IRw}=?Tj}vM>g}qt
+!Hn9ir?`fq>x(qOV#X$mIv4n8R@e=sLS7<LAYt$jH)GjjiI70kF#f%^U7(&V82f(DI2{$aITyJEga~L
+mFsTU@g@+VD!!ZRGex%M{j&Zw@%?I(p|spo}Y$}|kggnS<KB%;BH4Cpf`2lsJ(b^nX!onX!&JcGP)Dk
+$ccQ(atj(SM>kC&B$h2bXL*2mtPu-N1W)u?msh<Om!<+D()jL|7d#^p{o5#T){#g1Cd!C_sjy;tXnhf
+!sqI$A~|v`;rOyMY|n?dwILKTU;)#F(4n`ydAsl4$QsqW_tI2@ov;z1CjZ7-dv(@RV#jVf}^8HP~_Ny
+_`h`Y=uxB8;@4pb<qL&*s!-G^4yG`;lCalu_j@_L1?w*6)24`z)7#nAkNUx=NxN9y%@*^E>t6chbb8y
+Ozn%W$!|ZnY?kIg##af^5`xabX%&x=P6)2|HhA2Xt^++Cty&|@C(y0=+dA{;Ij|;X9ksz!wr|IpbA*k
+afL_SX<%0nBM6DO#G)<fVA^@sG9bmN}E4bL9v;S9hU?<y$kF0KOe*%}hE$^2!*9SltKaa=U$)mBFL9n
+RK(q}-&@voEwOML%TFIPV>8v1G3g#P)uD4c<3t`4#*2Y2EP|=znr@qGyD$X$;{nA~I#$oq~k4F1BYI_
+6#<(k)}&3W}dR9xZQ8y#VJHhtBUyD5>lFGFt}LFgL6!!Dcu7HQ_yfIY9=~a)^S}VaqS6KdF-ZOw%}0h
+_e}RNJ80zg*sHEC=FOofYV_gz=l~7YBOO_01Xt2WY;T=$g=-opBztN_KwtJm2iWgX?1`bFTQh~z6c}p
+<MyuFq^1#G1^wX=H#uy+_sDc%?;#o`=o1l91J^5D^%yk_LK4Ris0nuf3&p#iL^Ve_wIQ+eRhd|y^6*d
+|JU$+}*IqNg1Rx}bZ&Psw8+Z|#bG9)Hp{XEH}=_sHQe$jWJiouN8UytfOTu@brtXgiIyrmfxQL67?t!
+V+vboYoBhW)OHvaO?VfSakbgGedBc*9{W5}pCiBlJESx`gGP;HRdOI{>4xP))}+HR&HFWYEO$VBAR8*
+c`L0usb!Tt;?`6hpeqjpuR=8OA|4GALxh@-2A?wCy+U6;vz>u(?=zi$my&C^X*#>qhp1o`@voe5j<)5
+YFO_I-D6tJ%$46XhQ$bzdN_jE@NvnA^rnqoE4nvPBtTjmYE+~&(=3O*JfM@yaC)C;P0@0f<-H#cOFe%
+9A~efkXF2FzaloYpni>GfeAVOm7j?%^83X}L)-+3eh<v{xqS6lVY)##XADfFm31pJ2bWQM0|LdgR<?O
+8N_*e?_2zk=<!abO>m>fuQSyh`e%{?%(O$rM4(F4UMI}~&Sb77bWo`WH9N`_l7R^tVvY)q~Oy1J#^LJ
+p0#)E2xH+i!RRxhg;8hoIEdNWvTyLak&f#2G5xjGP)dvK{|O7YiJOLlJGafH%rFa&jjM7eV@Ts$Se^N
+18-+Ur-m;`eBbKB<;D~T-Y=hEO}jT5pQQ)Z*bU2k&aG*B^=gEORm;Bdx*CYl!6Ubn#tLD+W;*szMqiu
+wmfHW>SGEXNV%lOytN*J#*dc#G!k3D!|Cb66aI2kXdD6Qm6+rvZQlf%S(}ZM_H+~9X!8&95+<qkaO#B
+YapxgkA@o25nM{a(2K5$tIGEEx8G!9{aW&{kBGy=IK&pG2rjqs$<B<H4{{N(c-)fFu+k#^yQ<n;r@TU
+wy4U~6T-cB#4vyanv;p+W|yLXFE^Z)lAK1G?jg}JqL;HK9mBk~0QV9tr1xC8U$cjSC_@cv~4O@;Y)qX
+0H;2YSedeRV}7NAxcb0{p?9KCXi)8wNgM+E9E3*M%Akzr`L-bbbY|o%Ln%2K3hvB<o$7II>qSX9Q<eN
+$;di3FRb&J_sfNJo>eaQLe-FX$JvMFrJ%QcA~Ydf;+BY-O_xUrt62vC&3!CxBCeJDtMR&1*HQP4TcUr
+zP))N<Qs8>AB!ceOn*UdLaNK5+4315X9?t~m;8X8OY4ErTlXs_iMQR_HM~u%`1Ki=j=$7O==|L-Hf4T
+k6^Or)^Iqt7{9omv7hvBf;0!BJr5bny`*Up1t&P?!i^L(szISpJ0ZT{c=RdvvOHcK{yjXSlb^!F94%I
+pqSNHA>A2g^P9eNj(@Q%eHS3F3Hecq7W0Y+=V)$DqD_v3OJLa%?`g){2osBeY-(x5A6kHMI9#rggK-)
+-?sH>V@bFepbqjH_XAIUkOOP0_8(&U8aQcLfdJ%~v2mpC5MP0Xsnb2>bNz=xO%pF8ESi{put&(_6Oid
+$hZ$*XjQBH^SavOb5<yL%1Hd?Wtmj?*j3x%8#>W;|{2eLWCJN-w7nm-N<@6#2Lz}Y<+KNvEskBoLh%M
+K7NhJ13jP~lQ|@bkv<P;U4n)`OLN^nvP2rlmISt^--+zFROqo67CMKun#$@Qt7p<|Q((kUFopV^Bi_%
+>#g}1uvQX}!HoM&;uq}T9%1SaEvTyW>;hOHEM~1I2VbZ`c#Jt>tp+nC+_-@y{CqLBZGU_=1q`9H2dt<
+-kUTI||k2_0qp<fBZbF<PCHnyXEfa7EHjJd%Js=URH-#VfGT(N*+*v3~a$NKSwRvg8oJW5I)DL1d1q+
+c!`5$(yBdcb4dtErT@L7+0Pi3V$EzAC-QcVa-kBef_irx{|UDeSw-QdJmdce2dzC*+-eQ3@RmzWj4uk
+t$C1Y|9j$2f8muiO-~jq;&GO%s-1Io|<1SZkD&x)oQkw<GA+x<~4jgR}Y>W3w7u#T~_q?8`biSq|4Df
+32}*k<ec8}hSbpwC0hc0U}vV|VM(jGE3oFX((!9jZY!Xh)V8FCQkepZ&-!}BE|h>l!owX*R&ocq(0>s
+#Uzr=i-bN7j=1vE2`MZn-hX2t~pmT)tJH$X`ra^^z{H$R*d1##VG>>+4=`b$-FHlPZ1QY-O00;o{l@>
+?8)D0j%0000=0000w0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK}UFYhh<)b1
+z?CX>MtBUtcb8c}pwG&sB(zPb)1cElQ1#SIEpQ$S*2UNK7iu&nYcQjfe2LxY9uC^kCA(#X0f0i6zMy@
+frEWC3%Utsa#wDP)h>@6aWAK2mtey7Dq<Grf2X9008GA002G!003}la4%nWWo~3|axZdaadl;LbaO9o
+VPk7yXJvCQb#iQMX<{=kaA9L>VP|D?FK=>WWpZU?Uu0=xbS`jtwOVa&+c*;bo?k&UC~PkjC+Xd`*x<a
+l-EE2scDE_oUU3H_FldRkd6q;iDcSWc_P^iEkd&ymv{~TtA(6=8aNhXL43%^CBaiom(j`+HcCQnmMe<
+t4<#fj4lpDj!9WR-#a>G>4-kr)F{CP3|YXrZ(#a}P+*DJ>JgcYaaG(S6M?_ixUju<8<z(tsnnM#&|C}
+kci4ZCs}XFN?|7r|0aR8^7S12Xza6(=pXyOO=Qy84d2=ci1;>VJr`l=)WZh-r}uZbZVW97HEJzutXhz
+sOkRMj($}Sr#{!m$6F3oX4d+z#hPV9;@t9i&z%ITz>oN<;(B>a(Pzj)6E%!pN&=-Fw=sAtQnbSE6SBj
+L|!s(*s6Xz6Q5&Il<bbEZnaiAV3AZ=c3OQMqt5OtzK#`G#;y8UoXjUFjuHFB(@MDKyHaHII4VW~zBoH
+Ov%U=do4~J`rJ3M>R5{lt=WdaSY%R0_M;W1UjwxI_A^LOnLk=@Lp~0UCLryH?g<*RTl9h@bNW{4#{s>
+O#P#0PqKx@z+4FKcWY8AmtDsuY}tGwi}Ilf`Q09D8i1cX_L0hS2}vIK%i3oGIUKf<#o{!wl(?lF9cxR
+e>}xlx*8E?kO%V|XUeCpfRgwo17sX~{AY^=T<o3Z3(m8If-0s}<<q5TDxSNM*i9Q!>sHRjtz#jespuX
+<BN<Qpa}N9vOqE)ukb0h#aIo%5nz`4TO%&MatkHWbhoa>&~%U9N>_EEe%Ux4Bas;chWEeQI^ULC`p#!
+=~jWacG=9cyXEnY#9>HXDu^O96}Pehbb#l!AP|PSxM6Jh?)~kjh`qi2*T;zceEaVQXS_sgz`NkOa<!V
+G=_2+G*INUBzS$oW<06uxax%FCI>}S{UqlV-UKc^u%yY&*$ZgKc3S1~n*NZi)A#X-(U6tsfBk+Y7*j8
+vF8x6U_GXMwyLy1UgK5G&4%22dO-8~y90M~0-YLx6O)T4fuWT+`>JY(Op>qc7=lS^R!@l*&pz|)ENT!
+<LG1ZOf+VCd^c(1>k8%IC?1J#To<YJN4C_#YRiTB))gccAe@S!xN^5CLusEMp7i{wyEZ(9d}jaXG+kT
+Q^v_ym`RI_!u$!`>o2w!_bC!o)+x)Kh8SiHakq)K@-N7@p$6G3p^ZIK#Iu!o?wA5^MF1KJ(YwrQyH<-
+jQwq~_@1d`DIXT6#--=%gH1<p7;<rBxP-+3_>|gch|bM%C*cTf6^@n^sSsOuawzrDK@WK1v9icl_R!N
+o41}9CMq;r532`@2_>%Mm?3a2T#vV*jYy%Xbac+7Z0+SEL3I0F=@e#1Rdgx6puOFJ(h7GuJ3A>Wtk<9
+xY$7&v-TY6q=ITui??_65$X1rHx!k$+f7T%>Wf^I!Mgid{p_()#O;h2Co@R@vDVQmvcDFFONI!6VbmL
+}i^b2%n0bpUUzPu6jik=!Cm5BO35!iiWIgGmawnksg0c%;m{Z8WD$V~qv?kn0M#9U}Xfi_g~XE$7S&R
+ZNH1S&!Ch+XOgb9bMD603MQ~b5hR%Zq6AW-=->=HMV~w4&zE|A7$4~{D8qzVJG2G5O@PcJGxI&qzTb1
+U$mpwF{LSB8fc+0T|#j=rb#gGwDBW<N~5d)helU{MlsaNdPo&JF=H3s6CguceNVAUWfBIMu{9}>Hd^&
+E%*F+&hZh%N=wVwBPG{`}RxX<?AU8Y(js8SB@mfA5`%(2=i;B<^*sw7y9HQVDBF-&7YJjd*F$2c)bY$
+u29uRJZEew<|IURQGtj?wZ{&isd9lY5p#V!=sO|f6@-rV~#WMi=!>md*VM%kOz?1rb<8jTew7;OXDfo
+c=zLR@LmPDCOr%F|@_H4*Q`YjY>u97!%7UmVtq&NmsTA1NKAsv`glF)qmUz($CBUXbg4#ExNRYrU1GC
+8&jE;u@S~JR6-nTF==Gzpz1xAU==+Tx2z|<Qtfa3(7Sc=r7eV?4lAy?oUKP#Yyg}58vuQJjGONUtBc7
+k6q3XsL(h?y6+LFAA(?2$)qbIl5|g%1C0UJ9!^5$c5H<G5}DXS1W^jj?LaDkM*_N`?d(Krx84CA-Puy
+s`_CR1K#y6oo+B}!D8T^^jTlo0ck%Ivc`3_w%p2hH$bQbGWBUzToYWhxFm1-Nrs$~snC<Q4s2D9*Skk
+A8gi!3n3Jf&ZYIGsp5#1uen=pg^lO{k8cM}BX^pb>MLuS7vxPKFX@ZAOmv4gZ1B+y%yJdd$wfsWxOkh
+oUn_6DogJz~i%+Ox%JN6ooEgZ6lLc{Q9I-3Y^b${JQW=K$0}xf9c{C5GUNj&u#)-a#!5tqM3U<_2`5y
+9a3;5K(D-(}?Ccz47NS<oGq+^@}fb*Uoh_R`R{=%BaVF0qNXGN^U8ev{-b1Ce5|M^m)Rz@*wgVHssS<
+jwtBKp;))+6=$9-9A0#WHvnvU<#%khz$gX-;Ra!%JZHZG8@zVgYUn0lReEAa0b|f*7*j#|27O_hwW><
+?OrejS)vM_hxCXUT%hZcS*VcXo$r0p9SSF2+?I}^&{%zJTB^^c*QNM0%0DWOe+Ub!@4Tpe3ap(7$V3b
+e3u#-OuPv5XxN;u9An!ZvMo2JKEAO_Xvp;zd(xzMz~F@aI{fVT?*FXPvADGb4dP^u*q?v4Vl6EF_e8X
+^SC1Ak-ZS!P`C$-ip-AA#QrTBUHSHy0aTrRBn}87hgV`(>0o5Oy9SUjv&$#CRc@kauixOsNbZKUNyTx
+KMe5(vi0F5dX-bwr|>~Do5nQ_oiUp*pm}kmmI^=mI(1QCgUY?kiI*|SQyaORle;2u?9*<W}I?%eYlk~
+AYHrqQV<RKXmvhR1v*BJr;I4w*pS08i*-_Qu%Pi6jBf=DL<zJq&{!zsYk_(}hTi)dr!XTwZbOo{Z>KQ
+Ay*6J`5}Wiw*Y`#C&3z77{Qk#4OJC@)oi93<6ESqeD_gD0#MUcO@DZntkTo&r+1hzf;mLd+bk{BVrRR
+jIUypa-qupD$&2xYb%26(!2nALvc*=PuR;y?>C><<&t{-1$Bh;Z&!(}5OlcpU}@hFVw*!Esh#bV$B=D
+mv&{FVpy1*FV4-Nhqj7Bb#LGqnu@j_Se%TP~YY@1b2723ZOc)QZY5gi`J5(s`_kQ{dowF_E-BopzwP=
+4a8)UQ@IprKT8+jE=!0HnoKpgkHU{cizX=1lK{R&(LiU=}GlAGdqr@8PQ_JH_%(OHwwFqztfm4KI>hv
+8m1f@r%*cjYSN{+9_&me_pj&mk`GIuv$*qC90xuK*_&``gsAHNP;<fz;u;q5TLqOJex_todspjVgFe=
+g1S8O&;H_F}x<O!1xL2hU=fj!Z<5d{z;<p;7C@uAJr6XxRMJyKh3G6486<kY$?YaU*ZSi&g8gqvIBx*
+#C744zf-(4`aSJvOjGF}vP-|ZRTr!L20JS+AqyWmjF1|u75ND7$60w1l~_TD>y5-c!napnekX0(_7Jp
++c;&Bds79TyLs^>$ypE}@UkIh4|svDa|*|3|!PU6CB5I1V-T&+sn8&-oK4%8OJ&v2JTC_o>`|UqV{Em
+4Q=ezb-%P>frxPpm&!7W5dn9mm$1U!H=z9*xi!%_HX_<VMv0(F5e3f+_F5u3bd*xM*V*gPKy5lP)h>@
+6aWAK2mtey7Dv-q9<f{w003+)001=r003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=
+kaA9L>VP|D?FLP;lE^v9R8f$OkxbeGx1*@Q_e5Z9c$>9ndHbtGp*|?Y3sqMYp?R6p0GHr7si>^q=@fH
+2=of%S3%TBtj5kw^CJsggPq&(02U}P+f6q?Od%C;%AlLd>^Ofa+LhNYWCGnKH1oms-;jPYc~&OOg-wN
+9?odY8(@(l9@2v$MCSZ{NVbQ+6qmuY4teNEnf7l>qL8NLNy82|T1`OOcA{jxAE27%>ajJQaeeIg6G&U
+5J1g1#)(5P1H<H4VMY(#X+<64lr9MW(j<>nwu?81!={#R*~eukj+%IS&77Og97I=2K#)o6wDht3a?F4
+W`f5|Cdf{(492z+jNO3oLYq`Z$Rq%)DBjFaiwqpgm2@JIoLr%SYwKjA!3b0}U@J9~b9@T2Y`vMrQZEB
+GlgN9zF)*lcFcJw;fhB*0nb0DRkps?*HMh`@kWl+II@LHgp=7pQsuio)0$!SL(gb7*qM0f1TF^QHnO{
+X@a0v0|Dvs6G&V4eIXq5g5=4}EnK2=YG%(1hbC<EGBC2$tj#oRiOUUE=>DxA|mGDHImwN5gVQZ!l{n1
+2Z~uvRHa#26@R-$4y-du%+snSAVydaOTY52N8P{p;Q}^SWc`djb2{pWF^VOc-E{x`WAQHoRfo!Dse!e
+{dbJ-lvCAZ#-thQR}3C|8Upu!C-%Ib@$=AKX}hBfp9RKu)F?!e*#=5LxvI^uYPZg-0pj$t6S)HFZ*}>
+$>)IG^d|#g+B&%zj#!sHbVrl^)rY(8h&_B5Jq*V^P~aN)4*G+e5s2yC_Xd*=h=noM`vp2|eA~UdBU#-
+Kp#R8fcr|?ZJnFx{ov_>C-E|K}FMFU{_wugCNEX<2b=U3R2kg3g-+hnzjo1*_j0nx?_wlxeLtya$NLc
+r3(jN}clB?lhGJ<XZhK?pV?Z^JO7qITAKSrP2jE2A;T?!O{(jTB=gF(--L4UK#v;YqFKa6{YHrKuG9W
+WcC(Pd<qQ@O$kX9@y`8<l2#z0+CK?AAiJT4BgHu-4KrWEafa%PZ{N)D8V*P5G&#(#4-WAbSEh1eB#a?
+VNSqdW@a0k$936r$3xVfBIoA=4TL_EeJ9;NjDI!bGC)JhE)S;j}lIi`OZ77htKE04z%oK>}4`nzrOv#
+E-u(Pm^EjB@>7e!4;Oc`2+0m4GHF2L5)$4*q&Wck77)ddzw%*x+HoVE4Gmi^G&Yq9Pj}hqbZ5|42)OT
+l8u$O+b6T$WvzEV!mWZ`z)N0DLutUwt`KTR>RkCO|=;)J@cB(p82%12%y{Gjik@!Ao&>Zctg6E|0I_3
+smL#qxXhSX@xv%dG0KXI>Z^~c`{`>UN19&h<hGsv}?ou2_~qdPfP;R&$8_fOfockKMM&EBw6t6||3%d
+~I)0GUid8k@!fv*MCN(nJ5Lxlb-&zq`o#54%ufGc@f&BzcE@@)af4lE(O(i8%|ySU^bnklW`qF#b|uA
+W<`?Y7RE(u8Ipx{7-(noezrVNURMTLR8KniTBb}rB#v>aL^lYP-XB%7wx;r^7`?ISMmDsyvED=A!eUo
+#gRYwY!<?++MjQdD8QGt^9qy~-C6xDjx_;8Ozd^A0zp(9=!D3H);c^h<~Ear@c!Mz!cX{06x{nZ%UD?
+8Jdf)gUp{t}@i`<J8i08<z}e(N!;E=o`L;^aM+=WQ=wA`|1@y6z5#I6NcGp5jX%Ql=A~vAHF)la)R#F
+##IM~54FcBYRx`imnfG;e_b%_#6m`!YOts#pLx+;DWe!IjP{A)<1ppX+d!ggteOz>R~e;k(@+bm+JYM
+_2$(BV%?&UBtNsVd+LDB6nE0TWMh%(^6|UDA`hXQk#YyNAMKc5i2}aNi4l12@D_Qzl}2F;GdxjT?C5_
+$8)Rm3AlRQdO#)&`V>$8U29F_g66j-6WNV$z+HNgo*8u<yAriGfIYLQ&-C#&T+MH&$zU!uIw6DbN-j!
+M7HEn+rSZ=O!BWC&%4d;&INz7$5HZ8uZPaN$32f&P25rc!|VYmv-vPmrUzj)DAGLN4V0OveKRuF4)@h
+z)MWL`2IUxsyjs`y#b9-*_2O{2W|%Il7cM)kO3RhRVqj-^l_IQf#7g_N7_p0+s*yb9W4uEt!)XTx+l&
+<wF`I}r?Ppsdjgcu7t61dxYU`@%;_;#}RZaaN>^`a;sqlY3(heHwVMhut7s7|9Q7YEhnQmZQCe)S%j%
+yv@C6o2WAX*qY`Y^0BOZhg;@oeJ&w-sL>SIZ&b;{c0Ppqt&)HYf&DPdX9WVJ5Rp)I?1c0BLrBvOF8Ln
+K?l5obqJRB&%$Afn*y8c>N|{@Sj7#2e2r3xVKydxvWlEUWN->^|@TEd8%!oTv7kQ_L~V{4=)xJwa^T)
+-GsL^W*tUKJxcMFrxqPMv7BND!JPv90tanX!)A?GA0S=DA(VY|EM+NHG4*$lTk?@kj&hJm;hEdX{oO;
+}Bl7iOH(BLl2@*1mE0$YG6XhlBsBR~dhp|YXtgQVJ;-PJ~iO1FZewAQv;3W=YSYmNaYSY+De4+;#J=!
+%~YOZaoiQu0bu_2tlQKJbZ73<VF6Pt}VT1Ah-=t6huj}I$q9&zhRxD(erf)>ssj}@(g>%8S)9S1u`j^
+~L5iyP?evnk;mV+Hk>iUAY%%VeqRPh3A^SHU~VkfyoN&QANjeigi942^syx7tTWj-$t@AT!b7p0-m`>
+1I5N!J~7P%q$Pr$mc1Uy`Vv?(rBraTl41e3P-N8k-ciCq*saZxQNvX4@(Q*UeQSA?PDTJlt;b)SPeGw
+_&<f)TqQ27Zt|96pbliTjC3Wm<_j@ZE)MQ(|58=rqJ(wata_Z9$7-Y=_T7t-yGdh6Fkz~u$`o&aTGt|
+V%A-xZwlZ<=%g{9e>q`soN^I>6JFj}x=#JW4<yxvv1EO*JYg@$PafmIQk&Dg_?%er77$R;MvhSJKAq4
+LW7SE-E*R#WIcj|#BDmdcfzv^iUPhh7mR(Ou0<+Ej%pJ+5{izrT8V&`O}0((|W^;l&@5G!V$s#Sxa%4
+t@1CQy-DYzW=CR$g7z4z~6V#?YBoS_mmVg59M%fyodY!=J^j;fyThQj3}}-%G$-n`lXV1NQsWW4~u)b
+<jqCl=rK!M9eb8`V<eHLux_cR)XQ~#lo<KFbgD1x^p8SwsJy(f!p~eHrm3oUcms%%}22#I6l2%_=(3G
+3M|jIN+6skf>6UNofB70kt=F8SppzzHX4$54p~weXfh>hH=0xQ4w#fWvs$e@QuyqNCy^jjym_F#^s{N
+Xz!<>1mLkJ0s49go&HmwwG#zB)w_ErCrgf@Xq#$EFw%a6AwMk}ScB<*Sfqe-MV13jtw72`$V&fh0IOb
+ExTmabT!jqkk@dZc_JD9v4aR;3*7rFzcA%x*xn)m>Gm8vjomm=hwP3X92vO3!xd}dJXTr`tIzHK-(cZ
+^BT>{JPHvm<sV!np*zl{H)W$#atlrFRcxt@py%3sMJVc=M|uFtW>nU1nL10#ukAkT)(CRj|exbC+Ipi
+UBBKx?e1}cA3g8vcnYCN~FjHWQ(tnCSQ;>n$ns)OqU^LZ-H!W`m=KWcVWiz1qNx;9-`BlCcn+>jXF<v
+`CB1hM<6ac@|guiGHBy<M=sW``qv6}8RJ_-Thgj<N-D3F?St}x)u7GRT&CZq2g$T*?B$WEulLuQhRrc
+fwC}+SRwi#wy>?EpX(XTlcMVs$-8}um-d(WMpDHU7DzP)XcJyWQlB6q1-<LtV`LZp)N|@qmI(uewuI+
+6Lx8G7wjaS_L*e?+17WVA>;;`6hQI1=?ba0`AA3dW&QUwaQQ#^=~3+D)T%`6r5vST8=zEYj6N1#h2&m
+gW-OM}fbaNOVSPSn>}irRK4bi~&pQ?RftTBc>4E9#XrFEq^t3rq5RHw`dk5tYRht}!L69KInq?mjE<Y
+S=kNvXd%TtBD9N;Ih!ofWQwy#3S=aDyY_?s~}n%0W;J3<pBsJ802)5@<>d1^ayV#2%1DeMxHhmZ<YYB
+jSCvzOiQQA1)9@m4IVNF6en1|a^k|^S^$vkKF(pl7O|RYcP|pFNY~8*clH1s3QYTv6@C{(C9htC!g)4
+u9w2Vb2lVsQUI*fW#XCUi`?cFvOMhXJJM$3xJk}K)5QB;yz6Ky@!2`N=7U&!T7nOE=l&xb)>XoW<5&7
+T%eCEy}6zfVCiFvdk?yJJd&UyvhDltPIHVpjlc)9>--+lME#a4CR@KdNnY~N_JgKB~(_V1%<YSv(z<<
+MEHH68cksTfX`HZw?hl?yLGZ*-DP6_vb;-{%Q`#tpCKUA)DDWg)esGnL391*I=M8a_Bc6#Es1Wm%1{b
+*CgP2W@$R6_a#z#tno*G3#2?Er$^>oaUlzG9tQ_yl*$aOLhQikfqB88*2LCXFL@g3dEXgh$UFv^bH1u
+3f>&K{U2(&+Gv9xv!SZ^<r;V2(RiiLB$8RpazV#cdzK^OD=i1q|F`AD@|BjOrmwIZgjlni5^{;1OknZ
+J8P<VYmChKD(8nG0*&;0_zg=aEx|fZt<v&y>T~GqulfLgvz&Ym3*Elr$O;f=i%ZeUW64FI1lhQ{}zW0
+yBt1dK3{ACR09sB&IV2P|6IRS@;#kL=AcDWK&Lx}F<+aPxe6xj|YLI&Afhy-R8N-^@d-p^99A&@D}fH
+GCT*kRyON<K6MYC!7FOBR>@16zyRxPvOvZs}cW&NeMn8bFE2Y2WE%%jU?7%12v3aRrkM?`Td?w@pQA7
+|H4DwL1cQZy>gK=L3u1N7|qruz&r#-TDtuO9KQH000080P~d=M-Z@R<=p@P0L%dZ08Ib@0B~t=FJE?L
+Ze(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvrVPk7yXJvCQb8~E8ZDDj{XkTb=b98QDZDlWCUukY
+>bYEXCaCwc6!Ait15Qgu4iXZk+#nOY`1TV@Syr^#wvdPp2lT1h_yY}tvrYckrHpj`#_kEJ*`v%L~;cb
+?YBkua!n>raX2TYTP3yHJfiL$uC87;KWnRADr#f)m?Numza(5Im!c&_VpWkt)6WA!|0^HG63Kt{q)q7
+k>4t574&$sw>Uo_??D*Wvqc>X$0Dm0uirru`SX_Pv&jBSp)ZEDWc4ThXgU<tb1UU9&GWH#@wrAQD-!9
+S%ic*_1QL#dn-WEjSTU=A*OJH%e*mmBItA+Q0C&L%YSd*UcADO9KQH000080P~d=N3GPH8w3Ub0KyIc
+08{_~0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvrVPk7yXJvCQb8~E8ZDDj{XkTb
+=b98QDZDlWCX>D+9Wo>0{bYXO9Z*DGdd97FdZxcrl{oQ}X$Wi1w<6H=7DYcYJQGgbSCJ_dT3e;NP?Zs
+QpyQkfCjQiL3&F-CjjssL}gG8MDn2$Ga-j4hI{_DA<h4A@&IycU1v5-kh<x=Nf8BL`%3%XwWIX<6e_%
+|ZyoYY>4idJ)}Y3Y={p~>WB*6;Uwy#u;5UXJPYTsbOLB?-Tmx{#JUJY7$oQ>AjLoh0GHQf><VZy{au#
+%p!(2xHC{m6~NlH21z9A0HPccUj!djJ-KNJ#NfpbHAv1J+-Kf^<-tQH=W)}>yX8CN@p}U&A!VXW{(EF
+US0|3=t5erN|g&QpIK{c@>W#K_%R*#;E`$^>-BntENNP3$Jk5*R7s&n)WS6ae4nQ)RTa6gg*!WGZUc^
+A`7pyH^4!>hKY2s1o?p=8lSki=$jONEV}y3v3%8myy)l)`l=NlDl5;z@WzN?Qnp;QlR^zG(*gK^9{$u
+z{m86ZQLdSUc$4bAnnx2cw$>{HZu7Q^<BK8s3(<8UmUVI)mCn{O`1+pE{(8`5Sp_*qfoVmJEKCy#rkT
+wC>5WziLtr6^oQ|}+fD7zNp#>x0&jBSG{w%Y4iZkF07gJEM9Np6q*v|Ea8-Usx;xuv9olOKM3@^DPz)
+~JFCsX=2$rbH#|EqFw;H3BLlrxyk++FI~)p~+~u@kcd+F0Hs(pdUcdb8<_2E7xpKIxl9Ga&uX9Ow<Ns
+U6WXt*fIpRR+6s^uBan7H!IXadFIIsEz-GcrEo^WD!OHE5Nx(Bt5E2tJwk8x14lq9z@yh4yp*PVdx@T
+2PKX&jranD@zuJQ6Y1$IS)e^BSYi?F#K)Ha^)v?(c?9%R@Dm3f^IVz-Sp)+S^n>K3KT5n(I68yQGDK8
+gvk~Jq;!SYH0MVkC__2%VMy1aT#!sAdKfDAoLf@&*Sx)|VsC9G5^ATU_XRX*>vRqhlxp9?E;k5fpwwg
+B0|OeJQrB7>7Ql7jCrKaT>x!*<p}v86pEXgKIn@;8=Gtg%9h4Uw3F$~#$=Bj!PfJ-F3qdCq7}h4B?sx
+MOO=!p#~>K_=>2>LMAu|0DbOplKLcb)w2ZQ?%Sl`FS$<tdEWcDH*%1$^LdXfp3=RH0zxHPi5-Z!K-8C
+m+Ay4<zgm_0=GGAR2Kz0KWb{$wezN3Ecu9nL#sm@ic0;3>xE%q3Jq~TtX5{y<)W5J3uoFw>|__GE2hc
+{>=}h}Z%V2+rNldDAvt(lvzv@otJUzIdd$$KmB=NO_QP-h2V852<ED>9Q3SA}&a$qxWN{$ajfTk4jw*
+zErHRtU?Xgkl@i&6v3$0_EfNEopI|pL`$9FzpSs8rD2HBrR>4dGMap`x2j>9j0zPx(%?Baa#EbVm^0|
+&GC@V3lMn|Jxn<icvUeVMlp@mpzCx#n*5G~`1=N|0G0dH5(eTNvVjBs#>>Y#r`Gq$ei-Bz<-xJ@n7=<
+s?lvMPz7)t-~Q+0d9&%Pfnfy)MC>iF_~q>RR`XssaWlb5zUsKUR-mLEYDSOo8=P|gRFgwt?MA7dsph2
+_u&yJh=(?zswL2*D~(o!A%{|PpzT=K!Pc=io>8)$Num*P&bFi9;hEWvNl8EI5&Ub@eM$PUdd<w#8A%*
+E?JS&RJd**0;!jA&dF3y05FALF7UHZw$@PeC*&DMESGSQMJnRNd#iP|x)n@a~xE74;@uK?^!Pr@Dqi=
+a0jHF$3+7shDD%WJiMKQWsL!KlkRK$7N2l-NrI~A7`&YRGTcHJ?;>Fyx2*<)O=)xO+2C`R~pbGrw}<!
+nFw1A6!F-TsuyJdVr>PZ$P(oGVNhaRAtfY-?(<#90h4OVBaf+wYVv&Jm&V`yu_?N`9rH?a$XMTS=X?C
+sK@*CwCiQZ_ets;n1O_{_fOO80qw&dB;hrFg4)GIdr=R4=LGxp<o@461fm{!h_go6LgxUI}dp`Sb+L}
+Mtf+#5@YKXbNs!}-j}?C_JG|Qj8i;wZaKg*awnYKCcxK8Sz&5j$snQlQVE^;8&FFF1QY-O00;o{l@>?
+5sq};c0RRBz0RR9t0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFLiWjY%gD5X>M
+tBUtcb8c~y}?PQ)+_MfY<GOKccvG<QHq2qeUU!a7P*cN(>ggY9;h+mq0=(CqB@{r#=n!4omm*7aA*je
+w&uTJ^ObyxvX_nP%T0_%RZzfOb}45^O*TYJ?6M&xe+2gtMa%(5a_T<lx2pHBG}5BQ{0N_%)&EDjjW)r
+bF8x%chV>LGYhj8;u)CHWQJ-toZ%_=~dP1BL&58J^j|t50$CjUY5d&AggJ8xFARNp@`BZ8fG*u(-chh
+?Yw19xzZ5}$c-!8XoOsk$>dn_s2`Cg-A8g0GX^yoF7qFYEYEGXD&N+z_goY(@vwKhpqh@PB#)K_O%j!
+0F@0OQk5~Sg$?@<5P)h>@6aWAK2mtey7DxJZI#dh=007+#001}u003}la4%nWWo~3|axZdaadl;LbaO
+9oVPk7yXJvCQb#iQMX<{=kb#!TLFJo_RZe?S1X>V>WaCwbZQE%He5PtWsI3y2|3|9!chXDfe;C0RxEK
+P#O1qK9#MN5>;g(d}(O6z~$JCc$t$1Y|dku4s-yZi3$BgL-K#(~x4D|ZuT_W1<xQyaYl%Ox)zh+xuaD
+QEEXcz=KQFyTK+-Z;4POABMP$pJ2*Afo~*&8@m}z<&tqKr48B{uh|4K)WqB&`9QLZFbhXqAhR(w@g~@
+U-Uto2_*dT7U#kUCMN=!H_bwAgyLE6A%b@iOePhtLD*HPmBIy~)v7YOX?T^;tJHgViYa`s4KGD4N)LB
+&ap9j|xND8Furw_PQ3GZT0~~@wMD(QCt!`xnYqXKt@+zBnki21r?fkxhj=Yr2+Pv60*GRF>{$4JhKJ~
+~2==Fd2H6lD{$8)&V2aJOhuY6!(hM{3J-vyR{$?t4$!T8vrC_EiS0Zw}`$(_%mgMux_E=PG8UhYdN)C
+Nn)l*RbDa&ix74z0~b6j8$==Ns;rbH+0}gDZM_H4BwUy&baWO)*OU_B8wXkWx=oOl0V*1E>eB0@A=8=
+5IKw_OS?%*tXmPv#{PnWIN4{!KuKL4qWLlBty00%g|x}Kk|3c_ba2(I4}zyk{8PF+YDaf-*tAKzFdFn
+cf@fC!=7<JFQ?#!v@T&u4f4i~jh(yZ&fyH>kD;mDmLY1}`0*4j(9~dgiH;-`vdIkJ;1qI`kpE6e!XkA
+@QY$?J{+5D3?96Ya1V(hC4cy%L46<5Cu5^-)Q@DhM@G4U=$9%wTU2{VfU9&2k0lGRnkJ))PCXtG|RsF
+6~Hv)l!Mg>xvJ%vvWp7w5wW*@WP!8)83W-7$MHZsiY!EsBJ3CA;-R~4c-jnrANVxt)P<>Wvs9G@aUuX
+ICxZIO9}DzRG~KTxTJRh8k^;!KMj*R8vWA+7$pfBa*9zxs1=JO6sWTrKZDFCM=xXQNz)kJYUy`6{B_(
+5TE{9D;utdB=f{Lqr)&<F=_+wn!kc5dg;eg!|sO5?qzMr!@mO370VV_`$UeDo#-(TW#Geo*u@fMUnp6
+AHWmJrdK9h3t2Gf0j80yDR{l*)ZAVVZ4|t?P91*$EEE;B3`4u-*uWf6$@mVj$&KzB41M2`3>-MZ!qTu
+v>>U!|fU%K!%_LxHSu0)d6{GNgAvJ6hl%t%;$_v!aXdR7<phOnM7F*f-osO#+d{g)ib}e=w9y$yHt-6
+N5Yv8n>%Y&pbtF=I$Q59M26Ntzi2RruK(LA(f7^}Gg!Y4;pS2TuRF0OGDq_lp=<1@Ly=>4yl+;DFAFG
+Gl^FVT6p3mAiMs5AS}agC-i&H<e@XfIT)4-!R{?Akkfe*E;$>UsHPG5>s^Dy$9$Zr|V}G)6nrsgfy75
+Bmkz82Uw5!;K=GZG9*wl{*?m&*71raFtGu<PEEe3vZ&L?x9-7;z<6+W$f*~99<36COd-hVkQFbUS^OR
+;f1X=5>@IqDkbuV1#ySEH^y0E0IRV$M0p>-Jj#OZM6tmU+MkiX)T;cQR{0aa4~K_I;xj#VGw%O0IMme
+1*fM=rtSoYwSeBkTCdg*U^2AX~W3w7Z5PEj?KIm{wA!7Vh<#gEorjn#57rXxJE+^#aB%)yg=M9&`5YZ
+LuXzehE<l<n8V&*6Y3P||&c)D44k;Xgp`QGT7BJZN>y0*KT9o@4g{Y0Z7O?0%+M<Yk=Ku=suruZ+N`~
+*--0|XQR000O8^OY7yt*Rrv#{vKVDF^@nH2?qraA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY
+;0*_GcR>?X>2cYWpQ<7b963nd8JidPunmQeb2AB>Pv{01g0T0MFMGbXhNFUBzW7V)JblMM`CBT0~Ex6
+$97uhgBGT(FBIQ<&OP_DOTlZ%h{U5qpmoEAg6!xJS<Vaeyrr?maYOPOa*Z;U^xk=S-lzHe?E6)Eb}|3
+<`TXJw9D{%TK%20@WknSFp6BVWP?H->OC$vfxp#xuq@<#^cznSbhCuQh8<pCKlwGf1TZtmoA(`qPJeY
+p8W}2s@RplDe2!6jxUpyC7lB~isCwbY$3=+#Wo6YRs%TlM4w@Sj23(z4^5T%uXz&|ZY1#9WCXWf&A2v
+S3zN8+@dFJZB`5-lzkI<KOT;OCq%%oXJ<v5?3@{59v8a#D$&cTN~&2m~_DD<mw0ib@rNmMfLeuCa67#
+0Mn^lcoz26rc2^cmJw+;Eq<6&RfB6P8k-Etsv)hO?tOW8>Ms9LUeNqAktL)mKIoy)9725ObG-w0;RT?
+e^hCiL}Yzo#ap-|Os6H=ywvm#JZn~}<Sc4MRnhFj{y}^+8%6AUKByV5G(@8DP+G2u#7K=ABIO+y#kzF
+1>At~oK_5uhDNCs5KK31-@z~n6>Dc|N1j`fshmg^rzdn@4BhsU(1g{ccJhCs7sXV(UbyH!Ic#iAj<Ye
+bIn-HVDot+X@6Sn_8K9~iL;&HGNegghrDMIl3c`&w~z<=7UVR}G}kYT*e9&PM~c(e^qqie8c)wk@iGt
+lMk#nd(`DU*t@9H&A;)7~H%T`tpk_kh(Kc(dgbf{BkgxuGAM#ht=~K^%#1u}n_6^In(Z$BQcd(x3^Ax
+2e~*3*d+bEjApzuY+$ptqwkz9s?iFk0aQ&jC~@*4eK~g?UwoQ4t|cx<bDYEvb~-&2;u_mtKX_+^wAGo
+;N7>th2OH~e=lOn|B93y*PyTC@CI}W+#Tph1gciB4Z?c{P)h>@6aWAK2mtey7Ds3Q;WQBe001@u001@
+s003}la4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kb#!TLFLGsbaBpsNWiD`em65?}!!Q
+s;@BWI#y~Ky;19B^+&{HYF?KQ0Bm90jRgtShYzprEkyO2_-E*8i$^Jcdv1v(y-KS<*u^&8iHHoFSasq
+1?FK8+-g5iTR~NC6NAeI^S^8`>IuBuEmHoNS<Yh-%jDJO(`F*W#B?JJP+79mt#^me~2i;kpVR3I?1tL
+c##2l$ILJ>bAYPUGORvSs<Q*%|W8D%`fyN$um4M2i;9f&mp+5W_gV94+O0W!O|K*I?JeSnvQa5Le|Zb
+x-U8+|F9c~V<I$!2@IS~Tq91Ab-Hdy3<6%-_H9qmI~%F_wUzRj;MH0BU$cNZ5`3VU%0HX>-C}1bFQX@
+>a7uNSotKo`cI&4422e`_1QY-O00;o{l@>?P)Y~Lb3;+PcCIA340001RX>c!Jc4cm4Z*nhkWpQ<7b98
+eraA9L>VP|D?FLiQkY-wUMFLiWjY%g+UbaHtvaCx;_dvDvw5&z$xVv!&a>B@{8_s~{2HFXl@Vl;^ZM~
+5qLxKb-}CGkRWRX!rChWpt&GyC8!N#`zyJ0Vaga(8y-H}9dd-CmY8tFxUr$<%c&H=C^3oSdvnxnofzZ
+WFPuGg(wjuLw>Ud|t|;5J|m&t+J`-Whu)sJ$T1&Z$w=lom(GDS<6J`&aE54)BfHYQSIR*p>KtTi9Js~
+@r|%7RN3vx$qBH^W`^NtBkH$sDas&@3%(O^9KxC;=T*gsp+K&F29Coi9rftZBXyf4vRG%ErsODPbV5(
+(JlV369`m{uyM4_LS)Q|`6ucIUvqBv7c10B)*IU-?Q+S-R&pdBLHD;A0YV}sYF}z?a!79EM`4MP0l}P
+nzNGJp8^paGpvjV8_3J!uFyvb{}mL>e!OPNonY8QS=y>q5TOq5n;-;CiAPiN2Y|1>M<UTC*%>v|7+J^
+;0Myx<6$rZm(4ht3g7C$Jv3MWy%;$W{lV@8*lqn2qq}^yyP^%Xj-+M2Xxz9fgX+>yqt7d8T);;IPdAR
+{2?!WtIZhfQu&|{zeHyU&OCJe{6u6DOk=YVK@wRR`FF1Sg($350(Ok#Fop~Jg>xZ`TKD}1T_a_+m$jW
+;NVd5{T>*|3N|HUoi6{^lzE=5E*#&BJg-`^rpQHAowz2z%T}B*l2d3W9GFI66W)+`mZ)W+<XLNr(>*W
+w4)#M>kTuVz7J>!-WJR-EiIT~+J-C*P=eayYY{5Ul?>$(XNJWtdQZ<usdI?_E6m=D~h<+4!0-*g~763
+M0mtqGyfZMD>udHg|4EW$h%GMltg)qQ?aiVAmY0B)X9-+f*qy#im!I5Hxhafq;mOF4iQB>I~Kb~!~%~
+ts-JhQZTLe$6_n?l^~!Er!2BHai=vLa^qXPKeKz)drV1}aYEQtk)kn<(MdJj;=J)Wte0!5?Vf&<<^X_
+(N-yj)mX{kz=_eeZ{hft!wKIeHH3>&?@nbJg^-vj^+v20r+Q5QA;SxBt=kUD;(w)TLFfIMy8i3s{#7~
+tdxZrKL@x0W|BqS-pLw~FTondmY_1v1JJCh1~jvUa92p!Rc08)fi!MbyO7krlnL0pd(zjVjdrW-;kXx
+lxG-i8V1@b7!-W=VAbJ>Sd=M&)5ntD$^dLHLA0rJ8*eL#VqK;a=<43>;T%!WRJ`?LTXzn-I+Am74%xM
+4t^N3J@m<~GMMeJ?1%Ub{Hd4<|yiFF<1(k?-Z;q1*~@u7{glutRUYF;-O4N}1_Cg)@LdWK&Y`1Kbi+c
+0o3`Rnh4$RFW~pnuxC6ZKZ6G1z`B@~m>8#uI^=29iOEktS7ms0|E^V}+*AYxJ0EE1NtW^3oSalTgRFP
+!?2bXrooMD(!h_pBQApqR81KxU!{_6)6-w;I?2E!4Ak}kkyPiR!TW!w@_#?l2L$&6%&Asd1#5zB2=UZ
+Bal<DDb!TubV&&<dNqH2`Tg5P{BFK@bNwp*;my@z{`TtQ;voo?MZ{o%Ne9A93ikjaA?H|54>{Tl*7XY
+rl4Za@TP2WaCS&>ht#b)SV1|<Gb-d<DElZa*{e-C}<S%=)p_Ql)LKLRVggmCRze>+lF2o-Bb&C=F-1!
+9W*(nR2v46ARuFcStz)haMXJ_o(ltH8x0qRdNajr?1KJO^^BsxbBfV=6sDU#_jB6gMzYnB>j%pYL`RK
+1hv8GZj5zN0Am$;zlX!BnkC6as@glpwh*Hcm<hbrrsqyu18Y{NnQEKd)cEUOu2L1Bk1zpi+)J34Lv?p
+vq)D=bf@2wgPJlsGk%jZna|~U{=}V#qAcVsu~L}%nnEotjh0zn9?@L-joH|oEPEx6(+^HOogh8+gl(D
+*hHpjQDQvgG*F&G%k*C;azq{cXlak$%r9SInX0SR58u;&ujX&(i#c9he^^{yzyFAri<`@r^U+V*P##Z
+FoNU+ZEWVfx6+2oXF(?NPK7Te*dq5*Bjxh_wF(@EkkCi3P&L?A2cg)a-bdzE(bp)lKo~_fT8#-qXVKD
+B6h=)k}7C!^-`tf{GHo#mgSCpdDSY`LwB%HdQl&>S@Y%?XQ|47RW5A~J*n2L!RVbRje)?Rv~w4zK2k1
+=<qo=|qfj7|IpU9n&V?LX;qVjuU?H!bKQ3k!R<fJwBz(BIirAMEVj@;7wC(X6Ab4&ws?_U`#xx+;Yoy
+<3mj;`-I~ltHh~B1i^s?h#xQJf5e|TVpT8LEHK1>8C^3Dusd#Qckm^4m_18k7zB&H&5dwuK8$hu_oiV
+jHf)GQqOG$wc+pS`Ol#4{XHFD8c^Z*%O*J+R;0hH=ZAx;Z1;p6RYYn+6M&Z7845;btN=-iVQ_k?R_hq
+}59-SmWLD=mnSvNs2T5MpM&D71iW@pPQ2=u60*6m80GtCa)9NgdyFIV7RhDP<(GEhSuJEwM#&Vn25r%
+(5B5NCP2nI;M8w<>2$V^yZhrLv-7VcsK0}BXY3NbGPWffXGhh9FgB2sP(d#53WdW}0avF?Z|H!#0Ls-
+p3HyA+%#ZkYy#f<CBryVF!`qExJ!P4MWYETEdeM%Iv__I}Eql<e&9@coEAVJ%VTys&2s&coKtH=>S>N
+n;XJ*5YgRHE62IIcX8~+SE96q4`TwmRL?}3N1#{5>0QaDoB@@tBB!{^(|!GK}Aej@`htI>zEA&SE?OP
+PgyX0j4B*EN9?jfEqAJbv42|FVn@Z|5MUpvz;!D`J94d<npi<CC~l!P0`xMTn<E~q*+sY3#!8m#jp}*
+bQBnP=L}_u}?ZChwKIF$}P!Wc(MzLoQp{p{p7?d;_Qc_#(VnL+<(t8}>;;f3>S&h<&PBw7e0n^MDv1C
+7p5p;Oa_m<FQR1M8&c<l~VQfSvP4B)&jqq$pe@<0;_=<#*mr03Um6f%sGHV`Lv0H@=jHwGO!F{}ML2}
+u;0fKLq!S8$TFLTiH4ODfJm2;2O%vpzL<>Y}m|8!$LMZ9?<Q>aP+r=)KzT-+zRT(3wuBDFvIOmReb;E
+n~4C=%8>_xwib&9g+|sY<`}z;04VQZ0G+}^};skJ461VXw$3M(1p~^h{hB_Nah2r%HaCJz|^Ta_1)px
+63=rmum^P)dU)EFY$(`Vf*0Ueq^X?&_Vcb*%VVr&%HZdLQ=u`x8gmLUul@=jqJ_K$>nsO*4NT9|E(pE
+gq5QIa`W0YeVD<>JZ<*7XWkpho9mG=4+H@1vl(}ukR6DAFl2_{M7~j#DYfwh=sV;dUhKigU)AbI&FSE
+P|GtDBFg*R2zExTc8N`ph)D=T8uV=I1i-|k1aA?(^ly!GxWCSvEH-U}JK1t(1AW#zKV>D<0yA=H3jeZ
+@qf<pX)srZlC24Yp2OgCDBTv_$RL0HT|Y*x(*bf?;ai<hlDMVXA~gs1W$28lo(h8n5AaX8hjoFv$y*9
+3F3v*(>K|6ZALuUX=2}nVN$j)3h)0;irdHb_-zAbwGtTtqirDd1mEItW1mDvgtB!`8K7k&#-!Y1{G6F
+aG(bqtbpu>wv%^bf#ZSH$=k><R0n&pq9x25dbv1iicf_+6eF(;SJp%Ekk}il2^4}|CF!KAyz9C55a=3
+lhQ|rNQC;qKh|k_B!>VzSB?Y+;dOG@(r$Rbm03WB87)A!u^aHSv8vj^K%n=U*LMwg~8a7lQAU0%Sv1-
+gN&SgS>?hp*XqAm3yXxf?7mw4B!di+~Euchp~S{k`xba8*ou1v1LsJRk~5M*&~g1s_=Jo|(**l#8i2u
+mFjgB1p+NVS1*YU(7EP0wHZv#E!bFeIk;2QATe^Y`<ctC#Wo=H~jQkL-}v6w3qdGI+qp5A&B-ugwXO8
+XE+?&G>N&t6AqXC*qnu_-Fzbj=BcsSYH@n`O~Y;;ObXSdAc`3FFIN#Bd`AJjwr}$z1e&~l?+uq+HJ9r
+@E{EcX!o^=GjX_b$`@7KVA~&H5jA>Jjl!uJI?w@@O_y3{-f%o|N9d*4mwJTm2&1<IqdT11m_K&950ob
+E+T?iOPTTL?bOrZEJ)0PjdhTrMx_7j6=jE*Xa1?&gw1BNyJ55v7F%9bQ53(NPh=h7$8h$Z<_!U4Ulzr
+y8el@;_YU1;G^f!>+ds%4z-svJd`9Dxg0|XQR000O8^OY7y-RBv$f(-xwH75W7G5`PoaA|NaUv_0~WN
+&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cZb8KHOaCyyIZExeo5&o`Uv9W<j%E|QEYLZrsQ
+8=<LXW-6uEgud+QAjItWo=E7DwniQO|QSb&+H4iq$uA7{Zb)_$>r?q?Ci`lFMG=@U9*KuxC*6Ya=l5j
+oUO#CST1)1RwVYZvn09|SF2bq9N9Yhe!$d<pXvXffB!?rkOhJG6pBqQ(?mJi>Ga(s%hGJXt`jA~A`^E
+p&zEVo{>ZaLCXby?2Q=d}$;GFfU9g)p5uLY})7kXq8n177tVAcw1kc4R5=&mh`7EqcckEgs%aYW+Yz=
+$W>EzjN#Mm)=5GrR_nPg&1FoZCw!|=0Hl#2DM-V9#c_%`F4nM%Vakq_8-a{n;9xxJYT*xmi@!|nL?-3
+$z$&xfsZ-A=InTdo)$|I#s9V%M5NYiFeAZn%QDTHaP(w|7|BEH?07hVEs2`~L2JGM!%E-W*)x5D)0`^
+{2Df`ZvJixq0;Fc-%Fyrs#Bz*(Eav%k1$47@MX=77C@+y-x8>CUoYUY=hyyhj&vJ%FRk-YScN#gcG*P
+^Nkvvo^Iun4A$wtWE}G#%^pvu#9ih)JY>%zn@wdTW?)dCwcQ3#Rm>ME2-EfHCQJVz!d#s$aOx+UjEA{
+|K&LW^#HV1DujAVE)h-cF!CIX140JIFS3HXZS<uN2dpX&P#fj4Es}rnqVue4|!auA8dlZStIL->DndD
+5;zOcYuCYB<zdu6JSIkb|mc+S!#eb|U>2{}nou-J%HnTc@6dRK2Jr;{=M-ydj38^cHsu@rJJdLaESM4
+nqtq~atLwHZ9wvCGNy<d^Y#GtUi-e;yy050c0nD%5V66-uz-)nv+G#7iE>3m!hP?Mj9#Y}FaSqJR^{>
+DEk%q~eQMu$vF>-Z5U}E9gar+5`sd{nhvE{pDEcrLLw^Es0x_!QB-=B<NgC-duio_b?k@-@TpOPhogH
+ZIWHm!z=6pyBbwbZWNd_s-E2F+4&#ss2O^D{ma`?*YI<%`U7or<o@1IFCBaPsQ+qedeB7QaN4C~_Knb
+Q-(Zv7PX7gu_K*(%@ji`2meAl~jBrr315(g?7y~psKD~57cHh^C=b-jPZA2)SG90nHUA}_<owIM*FgV
+jJ=%<Pe@l`V=0I$ZjN!dN1#E*UU(*>;<koDvAX5`H5C)OJd`Yg>#CWn5YW9O|*`kitzz=O>&DD&B@r$
+oFQz<|>{4bymLr}Rg)rHCkSG!V8)UL!tcA5@7@?3u>}p%WbkCu@s6749z*u0;4WTcs*b_*#@vX^##IB
+WBLlI7blSoP#o2v@qqfP(bA_itQ1kHj)BtTV`YGQpV!o>?g5%@q2a1CWiXw5T`uS!p*`Jmx(<Nx8#6X
+-fidM$>knjlFdG~EN^ugrv@>U6~eu@u3c%=k<}S{#_jf`-)0(@8!Y9LC24Lrx4Fk?&+aoWVPg-wjnEF
+;Yjuq7uLvj1nka&R^T;TLLFxs~S|n_a@WZ<8Y&Z)GmBW7h&=SDq0iA*so5zg93iH(QKfvZZ_;COvfSU
+t<0T<wejU(i-M2HBH3h2Hpl8}_iV_<Wz&#3>Bb@>{upN5xK+0$@2mI<__!(EVtHcrQ-cL6=@Yr1>M@~
+jX(z#3qrXhmA)&s@e7o&rQ~j3HP)BR;7XJ#~vLM)o^2aDm)&DIXE0a5R)i@(pljlS!y9-sxzz<~tU03
+Yaat$UxyzdMa2YA~a5pgIYqOfW2wSm`Ee7++6x%B@BRM5#nr=hDxVSA~~fqRr#q4tvdB-;dCr*dcLtS
+!zwuF4)%x-W%7q@;tAXy$r^s!U)e%HcT~fM{Z+dTPaI{5M=hil2})A?et_#N6aOq}JrHjp2dKOF;wHG
+bQ>Xsi1e3!+@`X?x{P=1gY6Pm6r0zo2c`Vlp-wy}uyYI{y)XzmAjD2zWAAZm3)jFLHIXSWuQt%S?Au_
+m0lJ`jC1Llob*FHKr(tpPkZ565PQNkyEO88ceU`!db&_EeWfD|z#nPKUHftb;TXMC+cRIo9w$S8|l>?
+jpCs1W$Gy!EQdZ#*F|S>+l40N*H-Bl##{TX6aW$+WHjGUzc=kZl1hNMoJLx;^R#WP5yL_7RKZF<%icj
+-2_Cs#Kr>XPZ<hNeMKS-z5lBniCbF^S5II(Po}mgUE?`w&t6f%boo&(zSazaQ=E^>iBYGcs{Z<y&3y#
+r1g*B!hZ`iV1%OA9d!q++wBKP|3%g_W@e<CnGGVsL=k33@)^F>!(h+10ZI|gVmNrB1P<2bYuE;I`rE6
+{$HZROZRmCV^MZb$T$v#2laW<-(Cxc}_$W-Q6G;f4@AYw{fM2RdHoM{ZpV>CeV2-5u-MI$%&2Shr)iN
+{YbWycV{M`kPw_Vij0rdWM{#M)Y-))9Z{$uT;BG+Ej56pP4(i|zNHsfMn%CFKI*;J(2X`c(&<P)GFhn
+|jpd;R%nz#j4G=(n@aM=cqF{RJk1^;$GyB<b%XY~$yqdVA{k9k^sdrSY?vl^EWG3qovOCUXu`?hU1JI
+N(#UrXpRtS%T+Gh7^re&J#eBDQm4aQ&WZ#p;Yss=^pHrUU|9}K+DLa2pXJF<iX4fBp7<)m8m>*pdUd%
+KAP9+gSjEQcA&={#7J$w5CUS77$g%dpeqrWH9b4g$vyCHzznPFHRe_hawxzQ<BJ7y9i3O0HG9)kCw#P
+F1OGcVzn}c|!}a~-Y7U+%M_NH^&9%7Yr&45@cac>InNuP<r)+Hw%gL%uY=zx{+fGcG%Fb-W=-=47+em
+c;k<4IrNUi*{@XsKq9W-2^v;kU)gz@a%1g@$ob91DUdSxJ4IekFV_x;J_-;W~ad7ky4*#Yxtb&gunN^
+P9`0vzDe)e?2ttBPa4DKs@JJKlRq*>DCgLpkUUv3wHy-t+qv+3bM+zo@g6>wi<JZ9CiS+O?f~o`!X4V
+oGDJLi7F-sM)ICO|;3HSK;6QF5|caDFw)XHZHHo<=28!vQ0_IoZNfnxx##I^Piu0uGKN9mvE-c<fxBx
+2OgR$#W>xT)YCQNpuOonpBo<FWi!NK@P}6cyGF94C!eq@)A-YEDQYv@CN+i5FkPe4Qe}JQgiQru5*-D
+gLdO@R#w4350-c6B4r>Uhwh{>uP(UQQ!D#tA42#mysH!wIBD?yi$Ytzx`)4Ipb)!JT!GVMCH|fhB&9@
+iqKYFIuQa3XET1r)EyAs{g2kWU~p>mlQy6m6Ny@vaIZt@La>U^%5&*v8GQM4n>$1(xLF4AYwucyGCty
+XSB{pv_4AZt{1&B~SpghTCW%i^tsDY~wije+!QIKIVeO`Qm222>7f=~nV_MR`cMjP89e(tK6&(rPh>O
+(sI^b{%wh9NYVXCA-~-gi2;xKh^T}()p?leq0G1i?A1bD6mlzC%p<lmwC1wZ8sZLF}mb5uflX0_vW?b
+tqD|%Pi+ZR3T-K972!1@A)Mjv-O0@7gT~UHLE&R<ux222l{8yaA5A>1-$Ee!iIJ}R2Y@luja5ZtfYD<
+#rU;!C+EMqLD_f)hHiw|X%x>JW%6A(Sn~grJ3pbSmdcADVcqx0Koi_}xOyZ}uTb8dX1ydeBAfQD0K}{
+JAq)F=KW`tq}4ysg}EXnWQx?|T7u_UmeNLSF+a8<y#ThM02YHR8q8=xd&!Ih+3Crh6w34}Xfkg_5f3G
+&tz&(_N#)?Xq{*>qZ{J?8(u@bCE=>kO%ez$riL>nivqaA>XES_$moD;W<x#uoBxcm`XtT{SkozkWYq2
++*7=dxA0N8)w1mS0109M4Bkr@$mJZ`iEEdFFJke2|P+?d+tPBf?!FJB((KnP{clDh4|w2iPscyYx~V*
+*Z#yt8#h?}y1?}tgL)>`Ds=rSqH7Z+*VWXv_Oj^1z&8lgz6eZ&xal^jVd4N79L7aNPPO#QLNXK{=ORl
+$_bfbSPAVWBN(y)VCygWS>Fb3{6_<`#oG{5)BiGR#r}8So%flM4UrNhdYtT+<YdyD5Y{>$>WNx6p+Z{
+C0JN>tw<Js#za|v3Lz}&@Fyz1$oX62=X7~j!}ZdZVhLec1maz9gT`l(_gO{gzgi=+wts|9__YaB*I1H
+Ha*4S`ZrSB_R;wwa39O;r3Z2_sI{bXPIbcbTJk6#(Y0Jx7UOLU5})_31Ov5JJ!Zr5q(Ql!zvCi|TuCP
+v^c6GB3R*pY!Lfu(>DgGM2W7;F1!PMHp8s*_2B2GG@SsJuE*K?cD&$Mr~V9QbR9_QfRUt_aaU;>&xYg
+=Q3aH%y=$j?cV;ano-WTDq+f-@P>3j`<<U-P{jgwFMg{#R;0zRXkZVRyXLA_j`i-BS094kZa6dqioF4
+%+@$%Nv`8YetVwr~uWOS9$?a5s@EDj}k7q(3DJ7xLJSKxF5|l>Tlr#bwSE7HpMDa0`iy{}z-CFKnv)f
+@X)QGVVyEKVv+r@#1z^rs}VU`RgH@B0UheKDHZK^51mWOOHchBO2<~8#E1+wE||CQ9Cy}4vTr<bO3iU
+^dN^}JhnKTI@Xz46{}2|IgS`kns*P)h>@6aWAK2mtey7Dwy~a9}zM002lO001=r003}la4%nWWo~3|a
+xZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kb#!TLFLY^bWp8zKE^v9(T5Xftwh{iWUxB$B&q*^1?;6)l
+%jP<9ZO6@w6MO1&`cZ;NNJ32!3ILw0e&}!S?&5`jNS)7VJJa45OCqpX>^}SK3w_D%wu04S&AY}jqpFX
+>GAnmNb#~4=BWkufvaPlIMy{AuOt!VGxE0K9xn->ii`jvj$t5#;QOUJ*+boC%5qh%LY8Qmd-Ck+S7O&
+sD{qyrTi<hsyfBxak-Qw<tA78!wa5wRfQR>9)D+=+c5_>CEYr^h3cj$Wyn!M6l=?R?uoEgzt*@}kU^Q
+IG@Q+Hfh*aEUT-N1#PP8Kh{X>OTX{asXcI-5+uT&(J}hSL@(Z#pX{4E`=IF6iIC3ah2K7bMd!q=O5vw
+lr+rwH026!RH-XrDmv-Y+2VzSSbwLVpC)?wXmITofLQGWHPB5ZcH%7)G65O5+1NhwQITQG{-}W2|f4E
+46As{Rs;dCT+P5NJmO)kG<@4D)s&^%hJV3?xA9~+poODvzb<a(pee6!zbS5JaRb5!R=)!|Kk}Augr0g
+CxBlOLtrlhvjux;Uw-s9a(t$muSFc~)UCr4Q{#agLi%)#FZ$we4-SySX>GoE$z0hCz9c+5o%4*Bhz0g
+|L!jL!8o8fo7+bq4@uQ`7nOmZ6x@|&C4?2MhI;j6|$&IpT+X1r;fGF<bWutFPz?sEB)YQ=JyYS*g8dF
+$hHZ+`svZ`riJQhy{O4#kzEC#Yz7Q^rFG*|2p5Fmkr`dNJPCq*V|JQQX4)K*c}Oy7XBTcaUHdu$MtTQ
+nU`2utuycx|9(&WXD)5c6(c}A6oo9N`gSd@Wc3DDZ2$ghaJI`s33RuVO)!z7v_ut02Lgd|A9@YmDvJM
+ftrzT{C)Tym{in7?^Dk(<a6|!e^lDzUU(y;!32cOoUI$hEvbb|Gk^G7r5+J%$3Mwkw_|*#I<SITJ1fF
+b2QEW^`<=*0-iWw>gY7j~y1%(%rmHFe+OxMi>9CvO*4;A1Sn8Z$2CI5;Xr-fp^TLif+iSIg@Hnz1KHW
+6%Es^JC08hGHQtXdW+P5CW+YS6`x4?5sZ>}r%`FbrIas9iSr>o!A&;IcAn`ghjej``cQOafW?CG;VEf
++-;ht_qsK>$MNN}y06XA!kP(+v%0GVcYKJj#EFvM+MpO4x%CEuLaT)meEjl8}d(Qb32?<;}GRvL}KVJ
+kG_vKQr$<;MX)}Lwq`CMhX9iPR*Q0{&?7PMFEU#Mas~S;Ers~E335Qe7;^&0Bl7di`<*&8DDLnAzhx2
+;y}P(?(W`2&n3L^J#6%pJ!kL8Bv*#L{OLU|Rt<7wt$>O_L#SQEF>)xHJF-<2r&{MkXFm28zUX)nV$bw
+8bd!*2=^nDJny&5<xRskNd-3iA1G@ny(pPifdq<Cei4B3iYg<Zz;15`NfCo}(X^%kk_oA8i>S?A(rIp
+z7dst%<Qvl=hx|Rs_c@IxyXs2jQJgNm4?gTfT7JW|ffpj)_GSkGM_vBXrowqI>^a0iDjn;qA_fB1J$l
+!k>W%R5~L)t@tJik-0-tRN>%(sw-HJ4Pni;6={n<&=1hdX2#mmEPCsX^lEXwj8!M=Xz%+Eu!3ygBfr$
+=IUe1`@hl$Hc*m1xxPGe!>X`muiR(xwgn#Kw7G;L3uvN{$QZ5Z^8l>vSdgtHx!JR)8SCn0jN>~(n`xe
+_Es}^CRlza+s+C~vca+`fY$7&I?Xl;mP)s(LqEZ7AfAJGzD6?Sjy|F*4_Lod4WJ_{66QqAP<d*Gh((b
+58?lQ6w-tc-0+e@5nw-ZB#6jY9I=XKL=)+~1b~~={hoyVK2>rOpiroMV>WXOpN!JC1;kkZn;;Ec17D$
+zg#ngyqJ$HEaC1)r=L$l<I(C~FuCf;b)#Ud01xBffmC2MHXLZKNdS}0BZr>ohC2(sGQ{rD6%<3Fc6T^
+yd>NsnPG{X00-SgviNexC`Pg#hs0K*^nbL|;8Iu~L7cy@~LHv*Z&!yJAn+G;SjpK^}7Aip8RZT)bG!a
+~sYRd(4v?`tIX9>JTA^Z&lsvIka~JQAp^RGdw2I9{b1_TTy*<Ie_=|uZ3t_bx0S6k;EjPd`UwhOR8lu
+3?klx5~q9ce76NZ5mhGJ`%@DUk(x5&(eMImR-&;ZvYNnA0r0;u(1e=4Fesgm+_6EbOCZ2%_z_6NV43I
+D*+?WHx=ShrS=X?i@n@P!lt?V3J{A;#I@?fGxbC7aEPy+u&F&zv8*jUv(6aKTYe0MSMa%vnv?@|Z#m^
+wfEr$Ie3d>Be2PHVZ7>)Q-DO5Uvit&e$ouel>&gqF!AM<5&`zfvJqs<L`*z^v}MuwaxMxC9UgGNstPc
+QB=s68<>>v;kQUtw~zY}a9;6X=MGOw{1-LL$R!r(|(~@rj`Hu@YMR(R)SXeamjj!6`$fpS%9SLl*K2=
+$+KpL45?%XOt$z!&a!-5OaMa%~KdXN)kftX6*7|iT74?RtfpZ7o_az|Ddmi$Q-3f?2#4@p2Uw~3Ub1Y
+z`Vdl5T)V}2X53}K|L!rygKI8myqO;7Ek7_(8QqI+%&}?8;0d7A946(FrCf)u%)`6&O*H!ibT(n6)B9
+oQqrx<rPq4dS6{B4Ai|I5<0SQ7)?Hdo`^Im%^uH{ZNsWPjdpN*B+NOigVW#i`90QNrE$(oGk+9*kL5o
+#UsD9jS`pzxNiKHKlN(+<ccu;xXOey1fOx+~~NSJN*^-(`Z3mAi{u@$EULk6o#0SHc#pjWg9lK&l=Dr
+j1)MDOB63R*WXKF&mO%t;|7c(sk?Y%4^KKv+ZBxE)9fC{Rqa)GyIq%mdgvNbIH#P{d<NbPY%C<Cr;{Z
+bd6Uc$Hse^Go(O>^j;ji-AMpi&}_1ozu5I+_we1G0LOghwI7Vm(-7)DTL?Djg#Ye0napUTpl5pOt&t<
+zcw`f^j^W^u{ejBfRdB?j-ZqjdFNQrm63xA5K)$utz&3=j|(!tP&?ya0M(s#-AkmIFzrg+>}nrB$w*$
+=6oYp)B_>B`x1yB%8ctnbX=>*{MBIvcMtp{)S%bzno-KdI>UCd#Aqk9?5Cc8??Bp~-CH%RWGR4K``h@
+&t(J25%_Vm8ni1_28_OEcpT=+WhGz}I+!}sVqBiqWqhN71Yij|n<%4K%iax7k7c@fn64+50P_P^qNxG
+)PB^<&E%2pI2~`M(eYU*P{2L@t|t=#y*a^m;@Q#ata_`>D$iIiVuYpu)a7kB;v=-5>jG;&Bq%x^b8g9
+AX=W9C_H%G{(P-N~hZSTonJPhKMgS*j^clgJ%Z_YGOh@?vKgR?zF{;Osu9TFQ>x_|4LwZazy5f{GGxO
+oS;H12TJT1V<I_iS(byUU<^ZUW-tz&G=wwvWFS0&AD%jRKtDXDrRsGW)U-S4$l-K8kj(@JxIs#LJZSo
+%W&aA`6d;hip=pIXDPOb_@&haR`Qi{~SiL4i?QZWzcBk(f_#UsIB?M}7%%^y{zW~$o;dXdXi@j)T9DV
+xPZk*<2BRm=<w&8yVoV?2_LUVs|j~L{ubfygT7GE{eZ1Dvl)ZRdm<?^48A<Sh6x8B(_-5m>!pVP1&ch
+r9}SiX$+!Ca%+C&PF!-cfj#$HayIL85gngsa)ka{e?2K*;wQu<&oVY%Ki7x4GftXQgM$#-yJNGA{8o1
+`g3jUz`r?k^rj6<a|lyBQ&KHmc8mup6>Xk>CL?77iT)3oknYv+1W~UV5D@G&<sd7welnVHcFfR3XfGk
+)yX`>`ed?y0Z>Z=1QY-O00;o{l@>?a;^|rc2LJ%f761S-0001RX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L
+>VP|D?FLiQkY-wUMFLiWjY%g_kY%XwlrC4o`+cpyZ?q5Ny5lBWHdD8$z&gf=0O_N@MpxDbL=!eC^OG}
+hZgc4Pf+Kunm-^`GbD8IxlDqz<VHSe5xW~kEWisf1<kvpkt%VgDP<5<lrQMk5IVlpX_Cd<UPTr?x1k2
+>eLaLbJq?~T!BGMThSt+;c>vMY9%*nBH0k<3}bTerpEt+oz-Q3CyPzy4<{%nol#!#9<vU2+f7a>cD>U
+yMozi_!$1fwI>$n=K~{u8WHc`u&AFZk=g!*BZe}ZI}^_5f+5Xdc%0l{{8v$E&KB6W5&K%Q7~>92O1!+
+*&|miF_EZc+1B~;p+OH<pzt9ZX|hT?bTpk!=(!LjTdivGv|6RPvUBGBzxrL*V$LuG;TEG1ZZQzuE$;|
+OT6|G>0$CR!dJMkfB}<Y34i>D|jvg}WxN-JLx^0>)l872FBEMqrf<32){%wecZ5v@yi1(a<KeOzJbKW
+oSlJ~|ta_GZ}i(DJL3ZcAdj0W9q*9R6AU>2lSs4{W{Hsb^A@tWsfpSUUPi(FS=fLu!@-HxH_m@Bl08T
+&&dkQBAd1yk~?U~bD@*6nBPIVj0c<tcNF0K=_=tdK`6db_q-wNCgD%Q{zWfyw8#!aK|$Lt$a08brZvk
+Y-{7uV8B?@dndNFF;z_WlVzcuA-Rdu&dX&s{_Mi-$kH1<y<YC()kg;ei!aZo3BiT+K*hrU{UU24#aMC
+X5;$U(qKsP%r2i5346xUKJ4>>XZE-E2DbZ%?fv~bT|WXn$pHtPhVloNAwZwE5L_&Ma?x&es|r?WNF3M
+_B><9)ZtLO&Y+>UQEC59oAq7YgdzJ&%z$;eq9c|rOF!7D&PVFc!EF?E#5#A%2tee(3;BmT_bEs#|)~#
+dGb!MW_KU)bn2F1kIUi7CA?^r6bO-8V3dvRntXKTglue5Klq7>WmN5Ph0bfq6fG0TR?y#K~4z~CiYE+
+?a{eDmfF`vTe98<r+p=bGhWvC(>?L<Uv4sJK)BLWhhQhCg0C$$ZG$slw*bT44|)mJRd`NYtWhIVb_{U
+tf-4Da$e_vC}<hcG9o|O*fsI<?Bn5cunG_y{x0Dcm_lF!7;!?gcu6(JHm)5ARGhZ*hPd77wk!}Cl35>
+3#AGvreTkpM$|q7MAf(skt|Cg$uHTs^00=<)bGKLLf8pp&shRdN@4-%<5_kh0_K|$D`CqyRzie`iSqd
+zQ?ewKbS6D;*hBXj{!!NirnA_r25k1Q95MIXe3pNT{2lobWLNP(0^UK--UGxKONkz7Z38`XRZ3%B3LU
+->N>+Any!ur>Lf-I@E`<0nQb{KaUn@Y!(zD?L4hblc0g56Fkzg3E06T+ufiPpAygATa0!xE)3rR=Lia
+W@wBI584{#H_;NaP$TV?`(eLrE*G3W(ZuUN4{nv<S`+RpLM-rPhlzH`i<aKSYP?#Z`o1^ct5ViWuY0N
+qhOjt9@zlYxtuv&ztw(9AMqT<WdW6R2XjRwpt6*2i|7D0e%;PE1G^4Hyd$CHQLll)+<?j1292t{SuMJ
+izu&vI0hln@JY-zD`j1z#i)+()lckloXEMxX}lGq>87&+_6#**xAn~7#aP5(GyFez)0tj)4acQ-OC$5
+T${3>WMRe<T%fNOI>3tXOpTAtfW3PDP>uKnQ@OPq)TAZ$fZ0<P<wxp8}G<s-n57BWfV?V>t`9hSX2RD
+)g!w)=GG%0uiIF@P5Ye%C5q{Ig)RN+wt#-&Hdj>7kPm>1B+P!4dG!*S$rn1;&sgHEN3<wG}5KYYK6{?
+;&njXa!}x_(Z_q-5Yp4v$MokTb_#K0Bp-5CPr!xX$Qcc0?l#4$<-KySf%o!BD;CteWHJKjKe9=f6T!i
+G9Q5p&RJ)TdDB;0_mLVLO?^e48}F1c$i78E0kO(wm<#MaZCjAs?E1Sh=sGflBI-fz|z*Of!SXa9(ZZe
+<#nxhgqNP<ZBa-Zi@8#}xmS-e1U4e>zYi1|Fv>!5?V@=D@=P~QR$K)^&=cO>?%Y<_Szn@<o?z`zi&OR
+<XOsQf#DE4!4D&{WQ8YqC&on$l&omdr&wNZD4O{)1Z|heP8cYF<QFCWHjXQ{pBm2Ag&JSwvKcild67`
+(DoOQxJ$k6E!*d^3Bdkb!8gGboU!T_*v7d}c%SsJRzX$`e4g)1{{HjAyR)WVectAD-zaccc1ofogNAA
+IT%&Y-WW(AW{LqsBz<?~(<zIQ%zF?q~hZ5J$W(p9BDusMeH&Vmt?hh<Yvl%fhO26y54$c5@i9Df`I5d
+wf+0$OlLz0in@he#D;M)B34-h^-01y%~k}x7)`bJB>(-sSUq(m-p-nMyb1GQ0@otgU!14@?t8Q!tRq!
+$E^-#Q6?OEwHikIy*!9ajfw!t$K5Vu@oG5t2J*vW^%TdV4Yn7r5i8!(aT5-^VgqwoHlhzYku;OGkQ*>
+;?@JM0hD+!yhhP1uw~eTSo1krPsus$MeOrI-51#-waKz3bsR3BD&RBXRnX^|%bq$3HNjJo>+wAcc`z;
+uu4(8~=2r%IGUp}D?1>FtjT~<sfE5>@fhzM}}H#?si(@_@xr#~#9u<Mh;I;4svT}P7sk!v{9Y=omEJT
+lV!g5OyoJw!oYR*!tG0IRJNyckcP&FI5HpH9KlCJPvk-^0RW&}7}`K)gqzC;tafO9KQH000080P~d=N
+8pYFHH-oP0M`cq02lxO0B~t=FJo_QZDDR?b1z?CX>MtBUtcb8d3}{li`y^|hVT9rBE7_rn(cKVr=?2^
+Jrw#y3BhQsaUxchf+UlqrT@L7FWZWp&0*J?Jo9Geor$tqt%|R-d8G#+jgppf$>o7iA;^iJFw}KaGE=O
+OYqjb~UAMjMHPkiX-AQRn=!;RJw@`<35jf1QQnq_I39wwrl=L)2o&vOx)oOr0kTRbA={|h~aZ2nbe7;
+W#ZfqjI;PZXb0M*SgvETgZ4f?3g1B=i1NiL0b7DxDVINs-bd6V%Y1@7mZj{pPz%N-J16bY&sZ!Fo8zX
+ie9Y$lbY9X!tC8;@rLiD(uJ*kl$FnnVJU85^N27O~OO1dWk^<W5E^4+ikoz=+<413X!h$W0;vnHn6KX
+=qdYo7%241C74#7K&c3Z^80eZ8Mh^+?`iDo6@G(W33EYasXSK9)p^LS@VNb_`N@J#z1br>mD}5Hv-I`
+%15t+H^pxbf*E3P^MH|cY@0Y+Ht9+sLHJq{+v9eb%H1$tfVW9D2)b9(F>awV=vnP#>tyA+nXdAn9xX1
+t00;w%Qdw^0qw6vh(JGzSTQ`ClL%19SMoFwVGl#hTvb-w1F{b!R@V@1;hhm6copEHn@WEBGYdRX<aY6
+Oyh07bK@HDZV(B7)|jnW#hQ)jhr(p?&ix2@nW>85bPjZg;aj@sr3CRFD|w*Frw)ERU#DzK>pL}mhcJJ
+IY{xT4qPF!5Yw^hpE$*vOl=cMto8!AA6%Vl=vRyDJ?FTN0Kdq&g0(Y2_OKmZO~2*L+-2-{;~D&fwxQb
+|3cTfWf7KlO;c?=;5Q*O6LmiNH61RldyyMlhF&mD?o6}cIg8jE^Qlpih=OGO5LQr-MB8czLT=}4^T@3
+1QY-O00;o{l@>=65Bi<>0ssKV2LJ#X0001RX>c!NZ*6U1Ze(*WV{dJ6Y-Mz5Z*DGdd977#Z<{a>{_bC
+K%2EMcK<n2?e8{p)s`ip7$uw1MWl=B*D_~@sRY{fqzOzk2NZLuA#s{$Po_l-lY*N7spfs&jEf}Sc6%`
+i>q6f+IS}|((gfw!wktEe7RaONLkz_G1il|KXc4NCG<l|^Dq2uYd+1JVPdiotg==FN!owkDuSFTx}dl
+JyP%9+Y|2~tHup(Yktr9Al;Bt!q&8EM_q#dv-_TP`rnGXZ=Y%x&!qO;C!SXlw6!#A3Ahd@}ob6S~lM&
+6fV0&Oa@}UjMn>-Co}BdT@7VY)&478f#eCZ`ZGYXV)`2`mvnuX_<sq(7yr?JhCHAehhpL2}`3oSKxn!
+liW2ay7mD$+e3Y6UB*Uv3f5)jJdSdB;vzu~^#^5wMG5$$*oqH=xI98s`Q`;$i~$mI&c`tmu4^6nSe$o
+;e31t0`0D<MjacR2e|e389U&ynBPsRBHM(Z_+~(^IO<)9%0H>&80`}9YI6N?@B}*XXt+)*nXkxW>85I
+nt%4t=Ok;@uQ#73*$?%ldrXGy|J^sp2)BSwUs$HSPFnWEH{EKdh;8Hts|&*kG&Q;-dh0o}9G2cV6-;N
+aufnjEa-BB8ue5@Tz8u-~5}f+#TyeQr8!bbQ|&F`PvI!5MUPkr~I(k&5G;67y236NHh4_K<cp3DXL5>
+Q6M9<j|QEnL54<VxBO=vhn4;seo;h|Mp+=-~PY!w<i0xzq&0|BqS3-R^nSVMbMStb>)5-94hqM(Ghjo
+OpOI&=XL*&<_5=lCbAM+uh#4=5uO_HD*KIdX^Pk%1Cx^$=$SdccY{Z^k*?Q;HW_P+TV&gT_f~G|wW|!
+n;BSK^_MVsC{lP_+?u!UotN{%>yxJee%UP|Mayw42+eb5^pGvdgp8l;a;MuW`@s70%G_`FVcJtb`{#Y
+*ANpc%Rse}m;Jc&%P?g*MOoXqFb`4Db+4Mi;#RQgW4kawknSvY`HIl(WUm2QXAZV?{&15ir?1QY-O00
+;o{l@>=m_|e-o0{{RT2><{V0001RX>c!NZ*6U1Ze(*WW^!d^dSxzfd8JlOi`y^|z57>)K(UiL-kukHT
+39HQE)<p?wu{i(V@E`mTuDwg{q-G5c5ElkVOt*}X}+ZQW;9BrRYNGPI@@WWl<=li+7k9=l;|wbu#Tc$
+ZcgSrZ+RP4&K)P&8#7R-reU%iVmyT9Xro%t%yK17R%w7gFj$7wXn5QKh(Y<&Ego30gD8p$!Hgk~-oB5
+Y^yB(IBKWOVD?dJUw(Tt8(pV-0l%!?Fp4DJbL^S;FxP}I#HKbCyVK$4r+u4d~Ac7l<p6I|xfdID{4W!
+|gAAlG<94eJAL1(kAC2N4>Bo44*FY63OMd4sW$fs7Tn!KpiGx9npOowRn$@za6LfA^Eni{KtH3p+3D|
+ol}cXyb%%ve0DUZvz(whr)1sIVHi<d)LJfT&WkVY)UrZ1%^V;&4n%W-?1eN}R(wJNr*PqtO$MvZ5&|A
+~zW;{RAmU&KB;Aw>nPocRRrur!$ihjLqZQdRDlwd2FoGaOGREPT2grk}%gaVA+A`Jg&I7^xA=F^VPEf
+Vs{WS?^59wHI~F>QZNAV_W>8HSs?$d+<ozuSnrb5ApP}_HBmnC87AeUq*Z~mipz2<xZImX&YYSqsRzn
+f+kz}_aQ5<ggR$oy-<Ak-a#x|mZR_9-Wt<{#UhK$`3qjtHtFpu*x5^V5UlSJhyu=^pMFd`iDwtTLWJx
+nCAZm4YtBM|(FtCLDYZaQufot0_0rG&@m?b-ANG;SG6SzqM2B|0&k;?X|Uql}q#A$oo5dMXdZD&9)a<
+QQJe=44&I1T6~eQZF2G`VfJ*j&M(nZs$BMrG#OueLoNT5Hv{$(MAFc+$vpC;J}OH`$c9jx_0Fp>NVcN
+voB>Zs}ehtWtQ-9=+-zXfTXT)@~;pMymZgMGN;fq3`%mCH;PxSJ9H|^kS<qMtCIX{^*W|QA%Fed8#H&
+B_9G$WOK5|t9`Z|MRQYM@>F47&Zb^8w{lsT8#&3`#_V}ZHPrBaHtI!VI}G}{AICo#3?I?6*C3#cDle*
+N!gW5GPb$T=7=#I~mIABEW;3IAl6imZ@cdy>W=D;6mw4~bB>p9_0{;R~O9KQH000080P~d=M;;c?ovH
+x<0M-Kl02BZK0B~t=FJo_QZDDR?b1!IRY;Z1cd2Lcli`y^|zUx;EE<p~YX3wSRB`j>up{KA2#U9)0S`
+Q?dtpEMWvgJf!busdMFU?GAwFJi6(T^4x10jvJ9{APiVf4tBJ0b0iGs09??y%NUa@8d8X}KQY{O@#nJ
+)iGj{H)P<p_O~WzFMm-A*3F-bMS_PSqtU*-TxvGPEi!+Uf-Y(2xzs1)!KzZ1Sl>MjzDJsjekbRyZKi_
+0PKv_9sFLYe^SAX;clmy{k=y;Ndz|7f*n=qaA*&pmj@ky+s>^q=d(RJ(bJRDoi(EN2k6i<C!`p-WvN6
+q6>q1z<a~Yr4Wcm|g`4m_C>^aqIOqd;zy_{UbY@H_;Tfw8w+=(la-V6{vSDk(W`)bk`>gMKOc~*ZpfC
+ugUTpMblJHkG_!)<nr?eRMM{NtR)kJ$YRoa74c{_5IZp5$+$8TFyCY?RWIMQ{2aiOF7Q2MsXL$PxOh2
+3WVp0Pr%XqNl0dKEVY!zxnArz^swN07*s-!vojDTn34VYc2Sg`YsH|5~UrsO}#KDdaKl#$^lpFt`=5%
++yxK3gy{6pYlQQssCS4O9KQH000080P~d=M*}W(qBjlz07E$d02crN0B~t=FJo_QZDDR?b1!Lbb97;B
+Y%Xwl%^O{B<2Ld=zk+bFu)Vdl_jm~E0=wDmU4TOoG`qJCxj>*L+EycrDoG{r%l-D7;fF*@lt0owoKSQ
+vab`4}?-?o^!S^IhH;rlpOH)$pYc3R_Yst$-vDDmOUD@X)-)@U)>u!0?1XWzL_tO1RY+rARlDT`iYgA
+FXyB8`dAS17oI9y*5xZJ{yvGR(&W~^4^RzLe91Q!NKuaXt{l~?TQYJ;rTMV$!thr|Dk{m~SH?OCPnnd
+(5+RI-fk_q56*pf_fv121_-RlzIifS(JY8d|dIIjwlQE%}<33z8R7;kINdvmH?C2ozP$ULEos&7SC%{
+ellg2V-{Wzh$O(d0R<E%ThC-H@_g3L3?*6*mJ=f`Kc+&{Bt49<Ewc~+R@AQIX7?U-m3X^0IZUz_Ph{1
+fMkud9hiF&%RtwMK4{KrSkdfuvfcA%mfu!)Mg8~hM%jHdv<`WYU??rSJC;4AHC4Nmzf``cjN&cWcf7f
+w0KG83HG2Slb%o~11Xzf*(`3ukzx1<NnpSiV7Cr~CtfW#pIa}GzhW0QwRyUbUe7%s`s8bST@A^uwEy>
+;C?v~8ZzPQ=}5848loRdZZBSdu6iL6;xYzii1Vv+xSb5Cl)e+L1RMwIRIe}NsA?MiMnP_P~V7eIf{6(
+f>2B4dP#Y*#!Zn1(}>przzQHZ_V6^l$cpoM|CXJpN!~sG7+F{H@4#`1<D}XPOeN2&3`<4tyYr+npQ;1
+yEixuqSX7Ug=%J@|w99u{;T6iMjwaP3`j3@YzU^D?D~kguH{7Z^?tT;z#mRRE|HB2dk8&7W=Y=Pm*MW
+(ozJ(%%<N$=wZo#ujfqq8vOYrLK&zQjs6(UPgbKy`$G$ia?%L00fQ9>lxW{d6D!-CZ6MAURiV;!CRw>
+zkoQz<CH(XL>BXA5p#?#G7COg*AjUjt3HYd2Eh@yD6j+wP2f`=reh87!T>pUG&AKR+Wbi*FXaV||&FA
+N0vAj@pU9t;Me9d2grlTT;OT;;f)Gp#RU8?B<{=ZZpHU__g)tpJSnwlk~-CN!>MW3lyO@~XMQ`(ejh5
+0aC*|D--4Qv6t5xnvzAoO!WrU8QC^V7l6Bx+Yk@=^eH#TA5?x}*?T*ej$w9W^jSeAsXdyht}n&c5lJW
+MsK)>aCzT!?-Y@<}qm4i;{aO)Q(i_g*?MMwLUti%hZt@b>xs{N=|!sOD*XB6O;R*s&im}sq$u%&z5XV
+eLAi(SmPT%mP6**FUVvlI}Gq!DsoVoM4Xo$RZ|H`K>(417o{ij#kn%|{GydWhH3c%hms<@)Rn#&-B@#
+qDkQ!}GbFc~N>C(Am1z0^#%aD;J4MHF8OGBAXaJ;)W(cn$pxmOw*b_BaYo`hMip~TfJwx%sPq3y2mLQ
+(@)xvb~5F<umb2WpkRAmRG;dCdV@{-8oo4sMV;ZQUz1#{UrCQ}WQj=`KMTPW8mVzdM1x*#n%5`-!C3}
+D3u1r4=@hzr;4zp0msFLSC`GQDiyx5ko+Z3Rp(TOWvS-vFg!Kxgj%R#VXi)KcoUJ!{2*zHB{wesWiyJ
+3(&(Mj}tTl!s#9Xjd*thNcVh5ic^jMzUc@)ZQyuii@c%)Z2V`;u4>A*1Ln+L34sco!r}E`ar<4$pPx-
+SlpaIqEY~`2OmDv1(a;%f&BM_ZV<~43#`1~eZ0H-{PF9@cmJ`kf&XHZzC%UHUt~gN_l)^$<veS!5HTG
+Ta)TRBzTJQME&cTI{!5e^f^8by2V@v*r{<Fj<<GX;sQ9T&hf5dAy)|hH&b-A+5sQfF280PdR~Ao9_ax
+v9do9t}4s#C0tBIUu2N?8_3|;d^`C0EhwlMGYi;^g*+4qmX{d)WBKd#9qXjH)rOm$<)LZ>POG&mA@no
+7%+dqIpIwwFb=Aj77K?4dD2Cf967A-v=MUm=Mo2+tR5NN^sn6>&lL$$we3(P|DtuK=?&Ei29-o|*9?o
+pBTs6$Dy>48)b@yehe9vy<gA<NF#UIEmWbq_ew8L?DwmvfJ}lK#T49#$b{9V3h|Otq<Ac><XyAhl-!I
+jSckTm+ZrE`<ZLQ%Kj|VUIfQTt4AvrM14pxZ$w)c74)bV;P9$Ecvu(tPAx7@9nM^SLG{FaBr6-J41qQ
+jOC_TaNO0hjgVU6})-#E=H6dLu6|Kl87yxmHpvEefMCul{!$0Lep=>kYrV50l=NGOU_kj2GiCG%yR`8
+|{MHf^CNI}Dd($KwqO$DUWsMUGvSY9xp!WaH+;c+*=1kDLz?#(nzPi`|#y3s>cJK(T8aj(P48cs4KO4
+yrtbmJyMZlI`y07qR4BZeTMHI(nQC~(omLsv@J!E(|P(Mbz}@s8(2|Dx?Y%qAPC;>j9n_hzkUk<4VsP
+T{69G|{v?BvDocQ;n#?>2jz{<UNHH`6O3PsokScr&!}qm1ewBf|nh5@Hj;2gu3$N8~R*X@|Tpo>b%iW
+4Ijr)nhNBkJFgy6w-7_aLU!bB{!)kvp#UoFJl%1rIza_b$KC~zzy^X25uSzMw(m>3_N}LrF@V0;X}ew
+7e+Qn2&LEnqZ~8c>^K3nsfk|FI+fbg`xNgI(g_+0j%$VNw4aWUUFVLZNVQAx6N&GR%b+ENI;`Ev<h+L
+Svqtx0_T%1(93^X{17>cEYt|k6E8^Cz{dV`4**cM{$Mckm1LezMVg<ZqLV{B7F>l1Dapkjwmvv~-Hm!
+Jh?rol1NYygPY<c3zdu&9|pZ->z5nlrZ}J;5_w$+E5NV?!pOhTS?+doE;w`K+i+tn7GChM4nSTnVI=K
+<EXxm^rS_1qMId6t644d&Wb_v>UX#lf;N3I-tue)rTFv%^iv+`==a#&*o9$0nV8DbwKmJoy;7Lp0COn
+imqGyj1%x-(F=)_?g;j%&H3k)Bj)y1lhFqrX8tS58rwPB+jV{UqMJUC*cxV~qH5Tmu&5p1v(=RR*jJW
+kG~t-T^XlG0&#Bvt2pA7tAh1s#;g#+2#j6rD?GR%22>z}CLcyEkPN2HsGc#K<gCl+Ncun3(9B?hB$?v
+?VX59<sed+kDJ|2(VdcYfU;STc?{4vz}#G9jQY_fx{c+H>!awo^&KWL9y-)pANGN0jD5gVjTaB^ZkS4
+ha69wNLD+9(cnik;6un2?$v7|5pB0wmOGDi*RPnxUlEZ{Za?!49E36VV3~x_q_i(B|kiAL1OqKD)jOs
+Mzvy6`=Qu*`sA_K+R?}C`lBaqt4KIW}Xe{HUTYlm;ysWrY11zT6rh#Eit`>h;6CSs}%=PX@Kcs(BRyE
+_>xG#v9@oGZ!FjV``w^#3J!srM_YhC1KYK3(FP!7ARh}jP_d)+WsT6tdY-854ERLP(^8Y2eUFgI3yHm
+6o|v6$nvi|>h1RGsT`dSa5>Puj8UVX#3%T7uG-X+1j?=&`j7>H<U*K?4&!t|l4H+SYkW)#P|0J7|Zet
+#S*HHT>B5xF_{es7b()@-WnhXoII9L8<Y=7lDg7&_VauF|YiU*FXfV=Q3qPT6f1Xm}xvvuOCS2xCi{T
+g{S2)?6e_Q0#fu=jCbGvMvoBYhhO?Uy;LvHIsIdfV3&UYitIixcS`Jo&Au|9nI+enH)d$A20}>`H-Eu
+QV9Z@>qA~U-iLG?^;gm{fPLWKf~nM$;>`T_k2vbUf^DNzhizncw`1Jyl(I%Y0oX<UeE(F?WWg#nhe%v
+EAt&mf?xlfWd9tFA+4CN69O(02*5u3a_IAhcbuOLiMHP)M&34y`6T8PWW#)=W^RrKs&n$2yMDm)q-73
+~p_)Rz;(7pFvR9&q^wJ!r=n#b8*o{*$xoKermYNpqQxB!|mwq;Nz2E_@{n}0*p@!S-ecBbf`ig;a4-;
+>yC)YD$9K(cx7cj$)2_@iq%{F+f>e|&&w3oFjX6z?}aibpL{&@1SmVOk*WD2YO)x+a)=&-*y3LcjsUO
+y1;%oz&drc~F^Wo>AIU8~Wwt-FuV==xm!wII@x`0?up{Ok?CCBb8(V{$hse1>(pvsHa86Y*)htY4p=9
+Qjw@*euh>b=+$WFVV<xSUe(Y19y91_ZAhxzVsJ$TtMi*$YK-OWH?#GgW|)fWbLjec38RZx)Z7t<~;?j
+GR3GposaVgxpZ$^$T5(O{!;Z1?Cfw?<GOV8fE)nogmDyhG-d|`H&~kEmS#Y7(SCgj3Dq9EOpgTO9wlU
+JfPWr#ySX1%WV*!ZLzLK2TJ{t9FYX)i({HY+9FORZrFJ%gH%C<M&Aq0?4Hyvr2+pQMJI-w|*0}df*n6
+*@ZM3&<$ssPNgqwIc<zJ_5F*NdVf9x0-CHHt#oE?Gl<$2OfLV<fi(h@&|XS{}NTYHWE=|Y_Q!Pp`)Vs
+)3EJ`PqOCF49OPW}gSqp3FaIT^??aensE;7x5yo%o5!*p#{ZvyAAtH=F?$Yui)z{~BdB1%>GWXMec)F
+s*?;U``)<(<nV1!N16;a0*-kMP$OhBN7}fQN-!ZtF<=`xnGcYWI?HfQ|lh-k;vTBlWly*)pX9-$X!!A
+tL}zG@4C+}4VMYIw|@c=`9#qt6@34!TUYlLG+InDJ)V<$vUo!g;b!GyLhNS#c=ULt@bUVK1*-d#LY@*
+QCCH{30Jp~k;S|_?WPXB@IcX)e`JBUyo=tvlwQ~kPIg_xb{QpUh=(y*r(foa0BKAr!Ho$vBdgXY)jKU
+le8x9`K%QbX77x+QNMk5<Zj*f3e1s(3FxB322kOQ+HT=XLS)&BrcO9KQH000080P~d=M{$pn5HJS-08
+<<Q02BZK0B~t=FJo_QZDDR?b1!UZb963ndCghfYveW%f8W1Cqy*bnujzAO`_Pm?;JB8y(1#-k#nx_o(
+ON!9PO|@fXY^rP{@7f&9Mlh4TbdcojDGVWWv}FsP`d9-r#PjgKDJUB!tS*co#E7dZ#I5h$ity-4x7y$
+`P#ZI=o4=$sRCpBc%X`F*{PE25!pS{yp+cyYpOtc?LG$@{!;SR)Uwfm<o8+|Ro{0w_(_@@*^TMzrs6M
+w^hT;9GuN^)N{XE9kA{{;(7hejIr)>7kL<v2kV)m_Z_ZTtFvwE;Ymju?Xv2i~i+}BE#gDu(0~u%{dhH
+I}(Ap96idG|CVLkk$wWhqP4Z9b7kn#M$xd<};#gA)NK8y&;V<lrh9N8nMZj2t3?An}I<K4$*v%!7git
+NG$6`<QkJC;(~up?}324tmRT9c2Uns<Kcf3vF%f#>4l!u}!!=}jZ8^PR?3OVL%Z010;KVq;mne>Gu>6
+9N3dS2u)_5_WzIvUsCwQ$KN{!6KNH3O1sMmPJLuh6LFYJJM;82}6N`kjqxd1NrP7bQdOc2X?3va<3>~
+Bu>$a?*Z_2QyWTC&BZ<^ADB9TaUVWBK6^(vkuA7y<gA<^c~<n?W4=w#2iu2xr9l^@FT(enqF`0!w>9l
+2@kzEa$>Hx-cBW$Btt5};S&2NmL>mcFYgET41YjiVgxTFNp?N&@z(u=rP72a{ACVMdaJs=+lD$Rq&S~
+DYMxyW^qj`}2u9|bFd0_uBng{6waQuarpwQct6i!JYOD5i1!!)_wyP$olmFbw^%@Z*X%#a7B$wA0_CS
+VoF?3myrX{52CPLfELSJlyr({?Eqvtjwc{3u+`_+Z;<u4cArvr85xzJK5TXv7I)rR~gsx4|fI7%sbo^
+u}>B-?E$AR=qpU(u-T0r4eF$tGM8hfe9Agia&87TP$hhi7Sm6v>?Bog2<d)ENtWj)FdL&2YV<wnrMgL
+Ucg1Pm;I|NP(BgP3RpU-UoNi}EmPpek%ZA`fdxdFSi~>+Xp!tKY++}zQAxP(YEe<YzjSZ=1YaL~u?fp
+{N@&zsqy3|TF%&66t`*1h!~$T56G&JKao?&MCyj*Qs<jIt-y9W-(iDgGtciP=7)mIfin@8!JFjgq48<
+TBjM$Cri7_aIe5U-xaMdu;ON@t?Sh@yaQSWPBO_+v>B{ulGw%T}VjTLb2VLn(7V=*~I5;i6DL25IQMW
+ZnTY!qfXTJaWIT;7zYiG$Y&Vlf^kJO4Y26$GE^nkm$<wHRTm-iN6oRPC$DGJtxJL&J3H>4(7JVk9M0&
+=o0R`V}dmnypCbEAf&Ty~c&{0Cjxe8T)ClFLKz(RnYR=?mG`DDktXDVw$!q*sus`oC**Hje1CC>0F%m
+zNa9+TG=kYRDthmmNs2O4*6#<)7d0G$tCnG*PT+3jIRiMe6*U_*Uy*wE}LZi@VM1o?_fK7cho3)TgOJ
+vMU0$z-Vtlw9a09Q_0WbF{pcO}c>u?YQH%}@OJHuKBZxgX_Fi&(Xr#iv&JuEsF2W`#Pl_+HZqi|aEL%
+x*sOsHNSf&B%rl<Hp#GE<erM3m5SQz_^V276X?c9UyLo5hK<`}da4XqeGa>F19Sei^tDa?yWvN;}Sbe
+L1&{I`xVG(D_?JP^bytF598pXb^<xWMxj*5|=K(g?Jcek@koSIk|+J*m%UEAxUh+lo!CNT&lM^{wZ70
+QBA8?kqAEvcb1PWf=-{I(HD~Lxx>y2PYn|S0S`?S0rFUbY%hoZ_*5%2R|{Ry|ADqUQz(paAOg?U)U+|
+fyyU~%oOMYYwpuIX7NsDw{RU(;7y5z<Qe!r@>5Pe$p-RcttDo+p(P0e^e8L79Ihjqccb?Oq)$h%*6c{
+qHII#aZssQ=qfT>E@j>VTrw6Ld&T23^h4-BN1IK|0I>Yl5fy0}X0WGC7{K#1|KB<Hn1_VdwcUHu7?bg
+UG*4RaYI$mYj7JLu`b<@qIxO=|E%e`f3>yl<oM9;z++0+<y{!%Hrm@G8gb321>E$}6Sh<t{OGT%^q{5
+2wOPkgbM#(>W!3olF4_VN*gxm(C(6yp<=TQ4c`a_e+n-DQ0MVbbtW`v(l#Y}lvVzBO1~1j%!6T^2f$b
+0C69q&B+NQRKtPQY~FGoCCp#UswH|;`lk$TvWbWjJ;s{r?~SBed&5@^gVUr+iJvX^<i=hcDEM&76uFE
+nogSRp>h8|8*6tzXzB<tLB6XuR(PCS7C2w=kh}bjoiQhjO@vam6YO|jF^klz`57SOt|iOgQgwZgIsU&
+ziUSJ`C6-?dEIh_sLeq7rvxHodGZi^Hw5*eb_*<tyq?kpLIK3?=hFEc@^ecy|Y=Yh4aDu`nq0^t7*CM
+CCyIwqXyzi-X;jBt_JF<Fg4Z0Pq#g^D@{x=0t;LJAv0Z>Z=1QY-O00;o{l@>?Y>*thL1^@v35dZ)d00
+01RX>c!NZ*6U1Ze(*Wb7f(2V`wgLd97GmZ`(E$e)q2+S_G0>M<}rEC71_vX@(WZ(qMM50iGc+5^b}QN
+R^~wdqw~I&Y?)^=5BqM1K1LI&c}28NM@^KMY3$$m`1TIBfP4mGK4;ASvH1c?fY!z#!I=|ak2BS`lS7%
+VMlXNw4Tkj2wC$weWaS@vZ|;kJnnV-Iw#AA>+754vJ)=kL6njP$$MV^S(U2JSBeU4jR>+_6zmuTPxtS
+;smAb9`>7o>nPIBpf|i-)f3uNX>fG`2h$~D6qn#i-=Bzec3O(}SMk%Q}Ic=zEbe7A41<AV)*>7)G*T3
+E@vtK?u+`a3hvu7p>sk~tQw96FJvQatH6RA}5V_M1Q4)|yzljS7`duOv5`oR=g`23{c`K=v`vrNzmGM
+j){UQ(^el2MiKofonDO|E7HJ}+LpXs5{*ti#Zfn&wY*2WubpQZs^R1m<)Ddg?@SDGcQhogY6nOr3s80
+m4FA!3Bx5#f$7%qX7v9y$m52wURrsmTibP9>ojzRRG7?Wo<&^yj^KDv!VstLLqa(O_s%)mD@RaMb%Ej
+&#R|{^RLCMAufX`%n2$aY0&R$c8VaA=2ek_wK}DReE_rLAc3Fg^3v94G#?B@S(rt1Zl+>}K2x=b9)7=
+_C~av|ngu$Y)~A|hjVgyCdsfzq=x1m(0{TR5Awp&^1-WnIL*CnFi5(QVa1Tk8%!Z!TRB5K5x!~J4*Rq
+|GO&!0P51miCkgG<xXuBNF3C6A$Z@wi$8asM5FkMj&R`=T}j(+1>V@E3Xso{zhsMb_a<s@zkiP!>3(x
+}Ue9f1<T6~ds6)}9*;6V~=tJ+{KZ*bc;r8e|(NugdDi0i_qLz$gb^m_6v+Nx}Nw0xJR?6tXcX*VLF(o
+D3bGAWVoVPS$TW-IBH9z)QdokT@NqJ@XOBV)&Z$?Fmkd!q0&7MPTC5@PxZU#$6!Wq>sYc%Q}`FmMggD
+W?SA45(f^x+tg%_4KIsKGsr!(lLsSI6E`-BpWhMyd%91=Vd=S7PX_8356~QtXu4$TnW=jvkEcFBSZWC
+2ss)jJVSSE}@uDjqlE`tFANc}9QbozR93UL<Ge&n*fVTDmH{0LUo{^GkL)v`;o#7ggXjz^JZKr?+z#D
+C&PX1uzK%s-eoQ3Il9rIv3Xit-9Be4a!D7j!MEDY-a-@`n34;mI{Y>F_FKtqrNBRLg7`79lnAJ$!v&t
+L2avN`w(s$^KDZVQ3IbsOAGkHr1PU}t@-I|1i}7jUnAgdCjn)0yoNsR{<2w(Ch@J6l0;9s7J(h#uq3Y
+`}JCHc}oq6n3d6cG6=W@RMuQ*kC{g)A;d4g+KG@Y*%CM>5Pm|CkU<G^X42B`hr0CJDmR#-dL&GzxlB4
+R@<bsuHpO=yOAD);^!qs%3wutTT76K-5&NxpSU?xbY0S%g;e7)(i%6w;oC0N3lEaHq4?|8jZ$&Wl~7c
+;g|yHAtz)BTHLR(#)>lSh=TdMQRt4Dutx?hl4z%EF<p5MAv1JatEPapJuoOHuZBtQ)WgAWdB|&RGo_Z
+m1wq#ad;Y7N_Nhrl<S~d)du}d-(=c1(5V?oLBiX4L*orGc-I<{D;#uZ0PWRAf4OFJSvw`bX-Xbv}vO1
+Nzcdup<m;h@WpJ3I_|p<Oi0k3pxsIu%)viXP**WFqd-OETu7Bq6WK_w7moj`(HCimb<A>pF!4>_?Uxd
+(3VRrnc|<C;Idt)f4av>~LoDzf&Hb#-pfDK%?^ogaczZrLUI+Z)~=)+N)1k1Ilg7O#m8_Ua-U8`BMCf
+C%y4iN*dXBc_u%X<cFbN@)aB6=r0jLk|OwfMdB&aYw}%^q#q@Rb84&ALE*lJcZ&-R3ts4mT(k&j`LWS
+v3YN^t1jDG)Fskik3=TV68RG)Un6GS(A@kFUnsQRn_VTct@VJN*ia$2?0)mLiMAvTY-JN#4`viNbVRn
+%*dG<F>WX!YL;18OT_DHw|JipjDK+*fV<?8M2?ajL@NC1gj-6Wb1P|q(t-Hg6%m2U!{K-|7v-7HsEIB
+p@FrQOzO|4~351jqjY+z9)Pmzwc@sr^xJ9B&_LmnTQ90-7x5&@x?vIcE3rbIaeu60w)LthbkT^xc}AB
+(r}2P)h>@6aWAK2mtey7DrNv&_*E!003?i000yK003}la4%zTZEaz0WOFZbXm58eaCyC0-EZ4A5P$by
+LAWR+6}Hf4W8N0zqbt@nL6YsoFjz*SZPl`<l2p=Q*#Eveilju#O52zDA+o9a9q;3J$742990;YGMmLg
+EN{T}*q$cb^iL%k0w!bHn4a)HYE6TKHQgP`duQ<<d%*w>Blw!Lrs;&DelttG?ojx$dGjTYuDt8Lb+n=
+3`(o8psW+Lax3Elm5etvm(HzSv~x7W9wEPdovF1#`IeoG}+qLCSgZ6|07n7VW}rCY8)n6;SFiXFh?1j
+MqEDMjvf;>j8l+h?+v5co`|Q+!E^Y9r)<=|WTxA<=+y6(_L$K}Zg7!Sb@?IjLE;XIrjP02&3j6<84|&
+=Q%IMu7k<ISE2u*HUcBO4O)ct;sJ|%daF&y&~0sUGf=qjaYQTifCf6Ip090i>lC+#)_Al8F|CxR>9-V
+{>i%9Zc;;ldf}K66i-r*?&CTb$fVgJr{IE0S#G|7VX>FMPc+{|Gx$5-6eU;DEO-eS*URYC8<wG}Wu!E
+Onrhkb0d&KfQZKJqsd)Hm$IE)z#!K%w*)(NI%0fZ*Hob6}T?vGfaF*>{^wKDqgdVo^PXWlKwl31G6ip
+q!orOV2y1>h(vN5*_o)L^;FY3jHR6?88MPPnQ;`E8h3Md>$rUVw`rsPme1X%M}<eV^PaZ5IE1D^`*?<
+<oW3ca#r<QBg++;9r_?!1LUf`BSmSDeXgM;)OkvMx5D<k<XL7L}$J8r!FLIyFH~7T-6?F50?>8pR;KV
+23YM^L)o3(Gmi$p<zK%v0ZeT&LQ`*cxYOa$PJntESk`@9D3wZ!O9G>keM=rykYMOX_J!oM?8}JYg2&x
+gc&u^tH_pDTrv$Jk%zZckm_$b0-c7QfpsKG`F1-;N87%UgVGFY;5iK(?(A8DNT$1*Tg7z>9Qgt$)A7<
+$JVA9QGg2pp?KN&3G|u!%$URVvDz4XUhE0$S9{I59D>6z9=Rl8<TN?;2qc6C;^7gG@r**sUe(ModGjP
+bRdiiz7702%-`6uis2#ywJv%uA`3q&@RPSRyI6mn^HQa<gWm`g8+t#Telxb>WyxEdW!I8N4`nt%N_qy
+RPjXh}Z7f#P2rfWA4??06l*R45anD&sM3F*{1&1+2XX&Rq1kbX0Y($nE9%_3ecsa40opP9C6_Flm}%i
+aHJHybUS<g9XO39aGdv#8Im-I@$<7Wl<U87!aJn9z@NX4mv>AP|1dJWvc43&~Y@2lGWRmLM4XSXjqQ}
+g-Y>z`b88~9Og7E0R#;7^t0o2NjOp98P~IrB|~*T5Vnt(_h%Pp_h+X9*Q3B7c73D=ayb2t6Mc=%@Tq{
+qqQL{Bs3V1kim|n8FF%suQ+?}%)h_m<={~9FXHp1Oo9_RAr11M;;gyCSR)ckVG$wtSmqIhtbs{dN5v~
+H+XboEX3qL}=n?K*p-+#Kg{(0(k$Znrk<aaG{vA{_hJIb&p`2%*x9=SalTjXa{j{wZ-XWV=z^54213A
+YStFCNR@og+?8iKO^FcE)xhPCvyX67=*TSt%6ns{<D7`7`n$j-0BwJ40ZCwWIja80tRJP8|5WhBTopY
+rq}ZeVdz`6#g!YjKd^(oB)?%umJNK7>=2U;YAR6tZW=ipqWcO&kC6}g(g~%Y{#>`@g_J!Jcrt08#n37
+#3%wi5JbP&MdrESrN3R+$t)cEf)QqBW^D@PPW#iJn_SxI3kRRxUFPMH4ZLZpJRE@R*3=kc#vpSM7)&C
+8qAun`_&r=P{A)Xcn|X6OpU<bs=~o{ePyTLsvLLV3l)Ux@bb?9Ah^kL8N%Y~XcFb9ih@aSS<tLso&|O
+R6p_Pu?W5)gA$UTI1ydMLfnGzTxZIQeTYb$3AZ0l-F#fP@ohG<`fV?cX2gyHqG;#hod?cnENSn%B;w(
+6$HbrM{p!h$(@8*DeYGViC!saD^WcA?DG4C{Z`=FJ=hM~^EG4;zXOwkOsv#HUzSb)1B@49Agyl5<v}b
+tag-f1-4g!8C|Xc<4E+Ux?8q%FkQ*<4_Hq50BLEm_F{my}ll`mC1ilO9KQH000080P~d=N2`e$KhOgJ
+07MG_02u%P0B~t=FJo_QZDDR?b1!vnX>N0LVQg$JaCwzhZHwbJ5dQ98F$5OJhu8G;a=tIy7D}O%UP~#
+*p(yq^7As3WN#0!keMj<poa}OfMAm3tAB|>I8QlP5Rp&Z`jDcucZ5;5U)v|NQ`gIfy^j5S{<wfWtJJ*
+PEl=#;#o_SH@%wX`_xc!^5j!XF$e(Qw629--;vG|O%Zn?3@%uKbjsw1~p=%(SS992H|%b5qi7uY(XmA
+$gcJ@FS~w27jqklb4MM~QxK(6S%q<S7CgyWK9#6B{{Kyzmp+ED9TB4}>M0B!bh>O?-{E4u%Dmvl#rY^
+jX2eM{e+`SM~^h68caI>pCZ-oun3u&cfAjIJ6g6YXufv*JkZyWH=mBIM<@6gFXO>{1xGdwIIBdh6Wuk
+x#Ql~5*?aGC?pB)pc0Y{osk@vtYw2+p9>>e2WP|E7R}O~<K>1kCBCB6?ctEEKvcXz$RVCUiUYZ`6e7_
+_Hh_oL=o5U=JwRXK<AB2_$g=DX&!d5l%ajT61c6bn?dW99#d~n$IOF~ZO04J_2qhe2dyBG4;R81(OVf
+v!^N<z2HbP>5ojxgenIxN3EQM!T;>n7pWaPcgco{w@jQzynNBn5wl>Q$VVa4f+r9|uUcp3H;124SZJo
+e|N%8hizJL&Q-U)OY>B(L1$v4`+VrbgN33;g`g)i`IWq~xW%5Gs&Q;uRIF&Lv*y>m&7sGJwBpx=KA1P
+;f<gOUXgX8uC)K{)UOhAJy#Ak7Ii2F}>h5R6Ukk`Us(Qnku6+-Azb#KWi0Jy|gb-X#<5;l`u^xIH0RV
+T_{mvNZD+cL~HLu-7^_+qH%-!>|}J;?te<x0VZ>{nobRgI^2a6e9kRWHI$4X&W@dsC9`O)hh`8+$ZZ&
+CPpi-<6pDdSG|p&AH^-M(Dkb<m`!#Gm_dDc(+b7;fnv#`@&>UFd8v@DC-C;DIg)LSJlU|uy>sjOw`R_
+>Ax^3(?Y1PL{it|6T!gRB5P4UKVNmW{E{+0qky^&M0=VK@%fi=3kWw3`hALcE?0;_!Sy=F|VPpGe}bm
+9E5KW&s=0T-^ug0{uYk8PXIe0yb*+Qn@NKfEL955w|K^f&pF4Rs5PFDfH6qbtiww1vt`g|~{j@dmjc_
+GsvSXoL9?k^7AArpP$%fR${6^5eZ6pzk$)qN3M=+sM`2vbP~LTv*(`w{C+M|ED`D?(|UdeFEqaLhlt?
+(QR9r_K%f^y+=5)K&1w6^$@Ra=U-#K98c`KB!&liP!%o^@=yiC);kGw9r;wi_q=nO4fTCWacUb$^gmE
+b0|XQR000O8^OY7ycz{pFGY9|x5*+{l6#xJLaA|NaV{dJ3VQyq|FL!8VWo#~Rd8Jw1kJ~m9fA?QOxF~
+GrENvfyMYO=R*~Kjo9D-gBc?bf5mMEKDSyV~NUiYuR8IqDneK_l$9&DMM8P3O#Gei0(61|H^CURcw?@
+6PJ=sk`s3Mq~xO^Zf1lBFr($682D=uU~U(JZyU7YjFDiv1oWv_FMv-_BH4FrgM!V$JJ#M-|J&@kpz@7
+5h8;v*D5*S*3roGd(%4a`xI1ex}(A-Loeglsyf5%~-9us8mbn#5_tV<d%Gv?8HT*er<S}f8^2(_w17W
+IibmanC?j$&C5=>{Yr&QCFP23NhRcwmi#Z4)>Iz`T=5Q9ZFyT2Sf+)%3`uiV!wf}JdX0|a#Pa9rGq2x
+&weIZM&IiWImh>zB_>E=<P|3WT#XLu~gxN6{)?bUo$H%9C{`b?D^y|MKA3voZpMNI_#4koe;y(nh)}e
+80n9CZObGg})m0A7(o@J}eVzEFNFbM)29^yUIpUl`gO)Gi?Bq3Q=Ql*@>Hcsm?0yg&x0?%@}G~dP)%R
+{Kl4*bLvDTE{!(MTeDc0~|a;7A4-sWQ$~yf6|hFNb-85z$d?!GuzdRBsKRC<-PO(FaCoexj8|0#QJOw
+Gko_x-~DsIJ>eEg$9r_k~b0u*@?*u;g!;~EMxKn8Xg5sGfg4FTcQ|4@kW}BUM@0qPYznw_jh+@2oOng
+Ai|kg$o(Cw?n<he(#{soUBIfCZfY$=sUDJF;`i1PE69?sF!p9(MIctzzFp3hPOS>P1j1f5s}uszpav-
+ojS<wkObg;rUFH15^M;m|s&zJo&AedL0T31`D>Kw66p?hHCFJK?iaq(}F!`N)@Zw2S%`#qaCLc%~$1@
+y}4442KykyrHzC{eyIc18##2>I5Nh8Z2Gn66&Q(G3?5Z8D<IHaQNyN<Z?jcNetfEI9%g|~H~b^Hj|@y
+c@LtbokSE3VUYtyo!X$vZ0d0IhfLUe0dYSn)M%Jb#-Z`pRMxckDjnqGN>gkb(^g6hr#B{XM3LX>MM?+
+;ul;5Eq?SkRPJRm=r;}SZxD>9Kf7dRHWnCBkYzFmC0&~v7k+<lTqFZ$quZnll3z2E3W{zA3)^87KfzS
+h^;`(49h@-eU_^a**Sl>^HN>B-1LiWt~#a9v@vkn-L~GmnWqxz-(=hI3ZQiZkEqJa8@UFNj!Y_u?Bry
+a4T0lUbD^G|rWuYVD_UbhUrhvg`S_|Og`%);?AFKR#1Q>;$yts+S0=y3^~Fzm7{<3cCxZ`I1OTLNa4E
+~Vrn{2ez~XQC8`yGju1Z1kG{fqD1EZ4JZMcHMdW&K<^04KQ=O$3?OR=NnHB?!O8CYG#%?~i=!^$o~9N
+`4EFoksR3KPf@YDEe3g>b26<-?D-CKzFOlV_ksoH15GPsvg(n;}7)G@ob*{pwVre6F&x$r+S37D-kzX
+wqFHbz-HcO(llbMwzIRUoWl5?R3(_h66JS)Zr>uM45fi3kVbF4e-lAvns%nIUls;$|F9~7v^N7dnuYa
+^rJ^<B^a8}HKzF0R4OJX)%E5<2z=^r!C$tXcAHz+T1Ra9k`Mbjdr4h>$WzyeDboRXMSpNM8Gy$&g+2;
+Hi-TiRh-JTrbXwRx(zSMNZy1v=0;2v3a~xU=*;Mz#ZDCF#wnMfOm!5aqxB6ud)+*?NGKQhOZdgG5e;V
+aA!w(68hT5Dn&}R{^3So9Xs1gsXny}k9G>V#fFKNy^`BQdtbJ!pUs;Jg-9T)~3)kjd{8MMG}$#;#(j$
+ZunF%AmMUXS+Z-!px@*O#p~-faGqj0uleuXOMSfv(wg+jff;U-|8>X;o)-Nl>u*Vyi@Xa_v>>dM(!iI
+hsj6yNI(awI$BhiJoncpWpz1ApRES92osh{?S?#Gpsmi&){Ri%YlX<jJQ2`#~7pa>Uj@Gc07w3LIwxK
+pA|Oa+m$`g_X~J+e5~sC#nk#?rx9w=UiQTJG6A_LYPloBjUb2e$STs_dq7DwXIF0OCg9TCeSl(9sKMl
+8FQ{Vj8K72)Gog(ZaG2*<c`x>?ivpp4EBL-ah;u#F=P!*y@p3zwnYw@3PN8(*sIKz~eKeZ(u3P9%*(;
+VIjkm&?>w|^510I-&HMK(?p}39B*!?0{x-hq9uwHPBHjWe0r6sRkRMKkF4~-pUuTN5Nv6iCjS%Tp)TM
+xFRe;t!S9K|`sO9@%gUV1n@$JSKPap2uSK49!8=2&rO&#dZCU1JAGyH2WiB#ez&pkY%0{q!J|?n#5uF
+;guCR0NfFH(i%O%;pHjq8wZj_dSbld>kghE{%6MYphl4VS`4XXpBJ|CL5b{Z5~LxtIJN9nL@@c<qt45
+kcS&1yj>k{q_OS8$6A*7W|}QEvz_tr&TOz7HCPkgB9C{N=SVJ-A7}?hG(+Yc+2)D6^o$(;7S#mW@Zne
+@SnA)BcJe3Yt_pE=5cb#?IC#vELZQX%-Y?<?(xb>{c&?(deLNH;-upG;z(uV--9^-hae#lT88+~uZqS
+5u5dP_C4~T5@;`K5%H*Z^`?L}RTm#06p`z;!~rgES~4E5JnUyZSCb%z>TjfHA?nP&LOT@Kr8w`u)%?W
+TACQB1qdE+4vXmeBcdc0Ra;O2XXGp6?-XefvHyBD`|wWQ4=lglwj_hYinpXSG>xru}NX@rK^sf0j>_m
+-eQ@1_g2gY5$b40h&6%&uMG_X`B8-ZCdG3ZAb{_iD{&vaZ~*Wc5~C>o$H3HTyfxF4;b>MPVo-|JSY4Q
+P)h>@6aWAK2mtey7Ds#jtz(o0002r2000&M003}la4%zTZE#_9FJE72ZfSI1UoLQYl~!$U+cpsXu3vF
+bP{{5aIZOJs3>nZZ?T{g`i=;(16ahnvlubk?1(LEWU_X9GQnI8t?$CU2MDp%<_wc!wIF4^^mFHkr)B+
+QE&b0v{n@=t+e5&nAN!Z|lP|^UE49bGqT<LYhgt1yID(jBYIF6%St2GdkSGLle5D@E9X$y2=R8d(@!g
+_)iY=koyMU8|q?U$)d6zr6uOv#2zrgUn=x7L{D-z>3p$)m{X`aA;s^YVpSE`?mCnUc9!w&F}=_K&bbO
+)EpX-H3n7cxmCvi+|8s>7GsJL#EaxwW(A2LDQXsjfN;GW11Ut@YbEiz=Z~eeKSt#kg2_UotHyZFVgO$
+q9n~HT=cfDKE3~*isW{y^y3qiS=f!Dh!9#7$b}2|>J7yEK*bY?cgk*28E!~fXEa-Jw?^ENL7Z4ckaF=
+P7?RYA4G%jIFS#Z7PAgIg=GMltLM%x6xb!CPu4b3>yIa(~N2f&Yh?X|ICPL;4e!GCt*#usV1AKiS(D~
+20;vFvnu4q3+_7^S8UY0lkVpOFw9`VBPAp_RsbJkbXP;2!Fk0+4q*-6I1Mn2H%o_SBfy~Y};yXTN6%+
+9Gpx-%vDcTs9EVmyc8+l<V(Gd&=)`_mi?g9MW@o+-vh&N@LgrYcKF$|de1HO4!ohTC7HaPpU4>j}l5v
+zt^lC+`rootGU~MrMNYSi~}Z@sT1FJ%=%1tV_~ZDl_f`7q#X6hz?zL{4T+FYs@CjPV?=-hQt-h%qCr6
+t&yllk^e!nQ~9x=j^L*8MPU9frP7?i@i9~ZRIm!ZeExX+4w)bm3po|VVL*1(_Yf%l4R@eC7jWcUx60C
+j7(`GqGqS8PhxfIqU>RoZKrG$|!?JW}Y5WYsH?%-bqv%>>S~ZgJ6W|qTx55RZwSkv#`U-!}-kh9_@&C
+^!@bV?Rd3v$4Io%zGnEDlH<USgIU1xOvZ-S~jNerM+8lLh}aRX8z^r|S-7ISW02>EEb2v*jX=6pI`3c
+IQnt|Lv$x};O=4b!qJis>(BuaB@DXO;1ps$ZX-ygfNN3-&*`$7k5(@%{5kX0BhmHXj-ru*!3wZTcBHg
+XizB{&wBo8~Dpl0h2H%n3bvu1{_<Gt+2G$uv9>|v~Hp~*HmDUg86MoRJdF55VYeTZKDHhXMu1Fj-XNm
+qmNaO2nuPJ%*ky2aejS0ze{>7$QeGYGN~%L^oUQ~YEEu0=d<g7;NP%nH9Ioq0#ghhAZX`EZ#&A_I5pP
+o?!@pcE08emtmBo-s0N(|nO>^hy}y}V&Hg+GE7lwoCiIGDkFco<$u(UFTVqkFT+qhCmOBW5DcDXGv~K
+p3&#?l%aG|BbINbWg>aJA<<GLHu@ZR7V+IA4H=!U~X8|uSAd5DFF!e8+ejA2fNYyDHVFOBro_|6(*ge
+?+v2tE#hYQu*$qpv+09C<3dFayT?3U{I?JS0sLI<MWOZNkLx!V_&^D!g2y(FThddxWCdBVBSo@>*=eV
+PXg@U(-6E=8Io}Bd<%EHMf*eOT!yBncy5+aJ1)Yx4?J6TuT}?&XN>D=HU%kd9BEN&SYK{sn59OdMzX>
+EvUg?q|*_&(K;<u7OowY_37L52ke$%08QIUgZ1uf65LMiA0{rQ`t;5DP%wOw4b^6pTwncu`Qi3+G9HH
+*a!&PbqQ|PHMH4Bl(b}3m^58u>h~*)3{bmA2AvT&Fhi)D9CM`GRC8ejHYlvI36q@JB{lQV<bOLW1Huu
+b=*ky3y9equxULvIoPwvP5&9(vu;jQto`9d~9D4kWv!nHmvg;p{wqh{_y$NaR>hU$o}t#LdZ#b`7hV?
+pZkW*`k07dVv_BJtLd*OFp(yG@BreH{G<P)h>@6aWAK2mtey7Ds}jrI$?=001~z000;O003}la4%zTZ
+E#_9FJx(BbYpLBW@%?GaCz-KYjfMkk>B+zCQ@7kZ5pwCANHb(F2$87n<<&nk+i*AGQ$FaA&C_TZ~;&f
+?<)WOx_cfN00WY;y?eV!P%euEW_o)1-8~JSu<<g?82s~?pRBhmnX`P!+08azCNYbW#UhLsuAjv7aN!1
+_pNo%dl?0oJcaBb2%GW7;@p-@=!hFfrJY9uZ7ACRF`Z<Dz=l5X%&zC&qY@VcS=J|IIUK(UD-%nO+FAr
+y76y{rcDi^=!DMG>dA>7Rq_Qw6rJvxCo=YVP&E|xia^Xk=~U%q+u`W2gP*~c*Uwru466s}#?jUqP0F&
+P{3jHmZJ5R-2-f(Uby@eXahgL?|+@Yjn27KB-zhO<r1fgZ4jEScwkF{g)Nj)(_|zgh7(2jl@1BJRaow
+%(-cBopJ19?2$WbIy?txP8EKk$Q2?1Eig%OuqDTBD^<C?m6Kj_7^94=)>*xntLe=W9GrO;Bm+^S^_5m
+pjiTxCm{8yGz!^J?7IpTAY_~MI!SZjc6nnRy$s<Odn=AYVvrgp13Wh4NXvk541jlem?1DA%X%}5LLa#
+cDCAxk<9#5#rZFr7K&0qtN-OGRYwm~h(C@H^G|Y1zOMx&14Q7Ar54u0HQTK2C8+OqjonLkP??<eAb;U
+k)hr{k*-0zLpr~ddI8}{CIhZk&o&BpKgBR0Ce9K(=ahxG^NS066=gSP+<C-&dpT=jbwCV=Zp_P#efe+
+OT?-}kTj;~!afaKSG7<3VpU0)Zc}!8PlB><z|j^bUYjF#Ep8uKL~YuX^nAdI(eD{!Q<^-@U@sFZ#pY`
+Iy0j^XtK=_un4?3Or&L-S^$MxcCqegE{KPr+3})=o(fV!lp(auEu~nyBuD>XIIxFLiWQ5_I1%6cM<B%
+@EQ;ubzoj^{H}-40N*bBJ0JJ22RI%U9uMJbhYfmfuljF$gYzCvy~goCvLOuoFp?8GtUK(FaLwxvV*m#
+8VJLtED1)AWNfd|4hza4)8^X@tcM0xgnFH?8JWW<?I-PIwP0FWJ7OqexK`F8%0#%%f??*@SzA{fs5YY
+&%g}R?|^(ouxoB4Llvm>#@8Vtnrp2q=*ks2939>E^wk$1=c^AsfdXYc5!e}OEx?8DyA0IG|&DvY?(`u
+nZ-@_(ii{QuR<KTjvm|Jdr_fPVYv=qTWGcJD=DFa_O?b0=$`9x=F90HlEEuDsk|!b5r%&Pj<@VkBOv7
+lIl2=tUdeOVcEES_2s13CPMu(gL6yJUwO4(iVHhB)pUZ31c=+H~fgK9@+W%Cicl%!DqlSjD@~RI<h2=
+w)j2eK7cI@FleS&4<O~~R^Uj4R5K+Jr~Z=rcUSzLM@Rh8=j)vH=?U#X%tPuJ<D3ZpZx{OpN}%P)axeC
+|c-jG^a$^W>@E)j1PxWmc!CWvY5+pm51IiO&4BaJq7n^sdbaw#uEh|s3Gvh;i7bg#~TtO0FENsEEzQ!
+$moEXSwys|@Da|~iDm%P$wdofakFL;Xf)nlz1!sgWyB7?>@UNoR8<t4p8B3a?!Vq-Gm@#2Rceqes&Wf
+{60w96^_jTFBmn;7AO`hhc7G6@j^8hIFjE>3t4zGdWs(d_#IGI`5v6XxD5;waS{fDimYAD}_FhzkV?v
+6MzNJ%b<u$b%tC)?pF266p{(>j(hBIshn4CAI;;;6HCdSOz2;COn`s_A*J748Di{l95VMD5RGIzYce4
+<8oyJ20+sU{E(OPXe;81o(FR!aaabfD}so}gFO>G2%0vjmm|A!6<I8kC?H>mj^EEWUL>|!j1>V21s4T
+QF4!1ih{2=_po4?NkZN*#eB4zClTQyx@x64bP;_C)QK6C31<%8ruclKc<I%js?)cVFIpPmUGQx4~gAG
+~AHc<|P@tJGx$CVg41g@9?j%cnJFGwsGL^~DE=fJBt@30K1iL{9MBN+5>#Z9ZBXP|}%9LfrX=-dLP6E
+f!bylNQGM>yh98)eZc6S!j#{0bzlvs3NxD#njdF54E%LaVuJ54R|3)-(ukhY|39O>M<)G&g}g1!FpR%
+Y)}<gCyonJhe~xH&cH*OkF4Ir!v((<!hLF2uuD6XkN3gh^2#Groq1AqmpG+TszdTB7lDz*59f;C~g)C
+6$X!IFJCt?vNY~rP~Y~c69IK{19VlP=-F3k6hZj*D)l?%NM+EW7Vvurvd;~wILYiMZbq5(cPuPS5w}u
+oE+B}=T4bA8M_W;yXugS*JIY)Qi$0uR4?gyW<LOOzJnjt#<;**6{oVQN&0p3jz<c~J%)ciu+NXc|6W+
+`=bG+$3Z~w8?7O*~D4=)-4ea@acpC4Yd;crXu_u{&F+0Sl$e1CA!`=NQptuf>C#8{^Pw!dSc;DyGYBy
+FmUH{@Jr?0+@KT41bO?3DhhF|9(7TK;@NkKu3S@jnFg6ZVO-6%`8~JP;X~ZFq%iU=Un%7<U)0O!1>|C
+h8UR4HBI*&`0rQIyKXYFb;FAazZu?ku|gpAXL&AIodY3q@ha0^{ODdNp#QUqM5Ot5>&i~Vw6t(VAn#Z
+GuS<v9-Yvt@)M+$I%zAg+Cq-{=vtScDk)&+X6q=-otE1wc0)zdGa5o};#KiT*H+XurvzIXCq;4xA+#&
+wQZ`S$1sWOzT(=Fh>Wec5sIV6_wl<`;dKC*Wq(}ybzIO;nyEZ|VeY%QXM&+k^?c3Fp*Ow-Yo)?9chB}
+mf-eLI+X}rx`7z=_sPn`Dc>(fbT7?yF&`p>x+OY2X<^b8Ad85UNY3>2naM(iHkknh0`r_&C7$y3kgcn
+UI|0$f7DQv10tMB7ktDc-^@l|=kP7*rkXr6d>_{=sxGl6c;bCw>ySrm#w?5jT_uNuQ^eH%!QvEDzInE
+T5sXB6#}dN(sGbz-7f0CzRar3SBX#LALst!L#EJG1Z!k8q~A&7+6;E@Rm}Xz!Cr4u3R1mPP-x=K{<qg
+QHwO$tTT|LSCjHkJoX8}v<4jlSb7G|5)j1Ob(PUHF}#$OBqnL>nupj1yKuCVp@vDx|Lw$OH$L<<cm%K
+|oSMgqYPq0>=}aXK7<4zUR8RoMH80Y<e|0rlO!JCiC15w<gv^IY%!ANzvznoXgei!Yvr6FqD7@p*)=s
+-R?r}5bguO6(x_rDJ(H$gd=G=bGAY>XzX)CN(m2`J?TVJhbSX-6lZDGNLhPBOnm=KoL*%=td)jF@mz=
+2`-_8C3>Sq3qxk`4HKu~{ajjKFOySVPcmps|iTJW!u)Wfj{<jJyaeYwYFWJ<nV=l0gjBr}0n)50eukb
+mEkZU;)0UMOSOv7~h!nZ9OGO?D37%KjWN6lDizqwZ&<9ZIn@`R+mcJDxqM-GV77nI8cqCbD(|rVbaxv
+pHb&&#*ZpOzZ`xd$wLG`*Tug+F}LJ|b(}@o{pCp4*UiF5R|L{iI$&*|qLRTK^8M@Wf83SU(VnA{<Vm}
+2!OU)Yg2GjxFgbOjDKbPdX)W=&Rkuq;q$1I(=37)27t=cwVpzSb9^Vk>QeG-h5uYunRMAnvu1=uD6T5
+d%oYun}u}<xLPefU!bt8tumN!|RteVm_(0x?|z=jf;FNuC14|9w3CH9X*A=r+&=Y#lcOEI1=aKi)+Q=
+XR7Zndk%Wa(wJVZXy#7%fN9blMUcsvePq!Q*o*ETCtT&#Bhpx7i=g#3{e42b3A&D>kbz*DtU@JDpX~b
+X!%XpRjW@vLxK%RJAyP&-5tlZCYfq2=6#|ilVo7lQvDg!$eJ{zhSvq;I3^cPWjIB=K^SnY=m<)V@xRn
+?5HADEJnFl0)wckE}@weG_hF6oLxU2u9QQ)Y)Xcw**$*9WnY)-JP_p|eTXakAfDEe8+0pP=1I0Ww^_H
+c;LEBO`j%2vt6Ju&)VB)ZR}BxMQs5cqsxL8oQ<{O@O+51<1;+j{;n@r8N@utVeq9-fnw8WlQB`~cgq~
+eMFJ~bpC0o+zw^ZMQ7_u*AGR}>IglBO}^<!<Z6-9tNE(xU+b-msM)$;ZJ)J!&@;R%J9i3v4kXElJva<
+QLsYE_FWs%ty}CBF0^04=8V-rtOWoL=<L$5>DMhrX}dG(7+3O_=hl#J_WPlxI*ti*@R%rTUTz+|Zr0)
+NMelz@%>2PfFX9lN6sttIk~CMy<6#$V1swkrbQLSUIv`<Bj{B)vaMgL=v5<BDbe+CfHQtyy>v-8uBY;
+e{fN?(2ksME(TXyarV!|%hn;K2_lL{<e?7CAJo+%lrW6|;lt0(8QC{V5f#p7bqd@f0=qLk1Hx2WfhW!
+v9Z_c$I6_?MS<MqRU8=om|E>+ZU2sC88?4?LwtGpL=8j~^mgE4_>pg72YftJa4Zh;d6NLz`mQfTGUbR
+{m#0yzow-NI09ur||o19_a_f9b^9ewes03<P%P=y$EsGrczkS}1wVGIx$Jca&;(LGI)yh3!m)hOs7*!
+cYG>5EVt{O|vToV4wy5L3`!5wwD?-2et*h6_*mBLr(0CzXA3_3^Y2c0mxUj+D%z>qp#6Eool>W~@3IG
+xgikNgeD7gSbi*LFjNz6ysj1+6M+Zkoxgd6m!5?;$CpeHbqoG$|6-fc!DYylfFROipfH0t7qGMsz*3a
+w`n^T`jrLwDKe~4We1st6E@(Fpq>FpM;H-T4-g7~1<?ji>#UV9Dv}}YK1D3Vj4zQ1F|$!1=&@58^;_m
+9;b~{(E?(-uS29zx(a>HjCrCP2Ky|YMi0{P(gajF{pLJM9ZyZ3zcE`6iKqxy=lssSt$#Z;>B}QB5vm_
+0$se4{d^}catVG317IhUhmHiY#%rK<>ED{>_tIOSlpIiOR~pgcae<}_6bXqlEbeqFKsg6QzD=)@3P6(
+ClnR?C1Xvlc)PT$(M_;}PNeY2s8^`e(UBy$R`rWsrn&Dc|`r<zA4nl_yUV)(O6bkg9h3m1_Z@7s0pac
+rNLO?fvvF+aWm60Y!!zS>QTpvRN#}+i3iutSB(XG9Qv?e5(#$I1uG0dD<bE7`Ut}l$FPUZTW|#m$x!V
+Yvz=tnj4}Q)?ibhuo8dkd|dMZUw)MDH>^{BA10fug8XcgH%>>CWtwc=d7bn!z*W%@eyxo$A~Y<lQCRA
+(yx4=SxMD|ikYhm$d%mdaHRe1Z37BzdP2i7sV-Pq7X41yd`y>oxoQTW&%P<PGq$k+#u%>qgqpf8&s*N
+7)vQMH6E!QTTLVEF(ds%3)2xNZep?EQ+ymXz4ib5`ew*;KnL)8K~zT<pN@MwM7L0PbOa+Z)td5jZFyQ
+7yzUAK-G&6pO+<83|3$M-THNIW-dEi{nZWHxxeN*b!_J%$$TC=Ov~jG{hpi$6NMt1;r7Mt4`S7wp(QZ
+XP|4cuen*#g3ZU3_ISe%yb4PsjtnGa0elv@fvv%CCMH0l%b(6fO+0jxl@<u2XR97ml6g|=Ypk6BX}E5
+Zck~uRKC-j-CZIn37Cs*xN%^D++CN^TW_wNDb@<>yo0yjrg-r?EnC=<R*^rGKT8bTg(f-WEo@5`o*A=
+Yjx@HJKycT%(Yj|x?;OS(+7G^=CL#_FVSvqsp>LjT4O5PQJ8YY5Se7Siu}+ZCX$jz~ypaXcyX1l2^Ry
+$Q6<H-{HjN!bKPTU8fwoSQ8O-03(|jOj<Yn~MWE|+u3Oc&1<AU}K7TIs6B$LlrgWtS`kM`ngI9Dr_^s
+HIx!~j<nY84#^*W*!dyl<9~E?E3TWiXx>dJF7WITP;>+s0BmK(&yw7kfD&P>^jY76r30goay+evwNQ$
+p{d?!7|?Owty$eTt-M2&r+}*UZ&2#XiT|fpeQ39)HP6K+2Yl4^X#;G?28QoO=py{zd-Z+76(sIbWm-v
+f1T78sgE(WEiJUN+5RSo?u@85rQ=oYu_J?IVKD6ISk|Cg$0~})7u9pyYaE0Oih?aEls~_*_EPwSwDe(
+v3v9bUn$A~wQO~g1*dfQjgHFZJvQrbfHMSQ&3Zvq9KM5$3*FW;1An@9+wbY?G^!fl8+>4(8)ugU2ijR
+POYf`BdOR6<bBIchdsrXv1*6Z0wQd>#aYG8u;=l~2UZp=1ZBd3(^QmYh9=@7WRJs7nz-htXEifCV@ie
+F7!S?Dq8g|~oMt=2wLiOrUy#-=uvAdtk#Bz43$3^wHbGNHPGuBcF{PLEggQb`^zUv65VS1}r^wN}fcf
+V+U_Wx+#N>xHZoPj2TEs{uO1$?S42p5CrmRGk|EXj#xyUGFeS(<kyfMXm4-HUj|nYZ?T?sUIFfWn+Od
+d5a?&ps@Nf`6URAxp;xc0Z!b^3G~Er#ZX0rCeiFi_O){eL129jQ6ujz-QU^LC+Gi4ZG8%zrq({%dl5D
+4ut#^)9ae>8?2_8YT9tb32^(Qclox@Elt0#?WL{KwD1nkK;iZ#;D#5}m+stSUhfwL%e&Wb|57~0MeJ8
+#))B#$#mx3{E<Q-m(p{`O331d=K7k$PsxvdVhH2yy=3f`wXVxgy2-AyH`qFk@O%SPwDj2OKi-|S(-$_
+t}qlI>=M!blI+6xZFA^&ww~@;19l7JHBeX8@9)9fY$Q+l;av(WH)qtA6&aI%jLrt>Xu|vZbN+kZ7JFd
+5GM(w>lt_uSfBsY!2(v5A2Efrxd{htQAQ+tx=h~H+uhek^AnbU6Ae;DX}8viyOj5O7b+sZ_V^?_UH1Q
+b+7<NfUG{100?I&-^{nX5~u;~?>0kpY$2SGb0eZujngWUJR}-bDk(!Kk)n5)<rG#%qq>4a;i#-T9Tbc
+%n_DrhK0uag0Lynqm}fKp%U>=URTkc`+{;=xx>_Z%ES$-P#R@sa&5qeednQ_e?XPg%@D#<WdZb7`Jj~
+>Zh$7WsFhq%y_@z;u%IvR8RYJ5KU6uPqShD9(5f&z@p+^}liar_DryKg$6wRSpk}4I&+tW#-2#LbJlA
+>i<SoU`?)br({-`P?q)q1IQtEN@u9qsALu`b`(f>W~!YH@89EP0eXtPnygO8y6mteIa`WN=TOrAyW-I
+)H6o6^ACZUtO>$*$(w8zfYs8$R>gHeVgnTllIBK30{A;(U{7#W}^Be+8$2V%4vH+)k7#Mx<Re<JPP!?
+D(5mgslL4;e{!@Ks46e|C6+_pUnzcLr@Wv7R@8T3OaBK@O9KQH000080P~d=N2(fUyg>i}07n1-02Tl
+M0B~t=FKusRWo&aVUtei%X>?y-E^v8EE6UGRD99|(%gs+o%_-K)%u7kFP{_<J$S*2U@C0#m6ap#(Jh`
+~I;^Px@a^m9^Y!#xFz%oiY3Q8b3rC2TiP)h>@6aWAK2mtey7Ds4KGHu}i008F!000vJ003}la4&6dWM
+yn~FKKRMWq2-dd3{hpj>0euy!REkHAt)Ej>H4F@q?(e>DE;mN3lWhd*V$&REYH{@yvMa88a>_K{D>@<
+AB)^+{}VkPv(+CfU|-nVP=)OOkQ=QYTNz&o2KbQN2HE%@HdU;+4sELH>%X&N97zuVCOXXF#d>i1*N1X
+Ch8!-wjDhO>?`te6EdX$tYy&3+3Kil8@#Jip9YSx4B$z(6>fK1QMxDiDiQEQ0LjgS@n4ZGEGw*{eg*Q
+*s+q|1EtKr}lH<qOGeXo)pH)=CKiyBqvOrD0otP8ttrhoj6i*lp=3iSDcmPmK0|XQR000O8^OY7y000
+000ssI2000008UO$QaA|NaZ*XODVRUJ4ZgVeRUukY>bYEXCaCrj&P)h>@6aWAK2mtey7Dv)CZ&hRj00
+58=000;O003}la4&CgWpZJ3X>V?GFJ^LOWqM^UaCx;?TaTkQ6n^JdSTkAyYT%)dk@C9IN~=}1ZC^TyE
+C&vW6$dl6nIu#7zxUYOOae?=?ec=LzjMF%P$+pKloqwA6;Mj}sglYNw$oD7259pg1ns_%`#mrB-KT>7
+q~cXjSZ26LGkL0*>6mGg3%;Y(ajyr6g4gc+OQ{VL;yb+7T)_!SGr(%Yh3<fRFqAi7$Z5{CQGAC8S$d4
+=8<e?J9SusQ3bd?M2091=YXK;-=}bwH=~uTGQChMSIw1~%XHv23$o8O#kpwQ8sB<7rMxg-SfymlawN=
+!9(H5bRuV*d!g}j6sOkEi%g$`FFL=Ek$*B!zwDwRXg@{I!0K5B5s=9?^G)t?l+%&FkzQ8x!@O2!10rM
+R{mwFc#X>m#qw3wyXsu`+D0H=mi%-T`XFYGLSGrf*0%W(G5m-%^|p0D@*LJAk8z*TGm>^o{p{5qjA=g
+j4RsOm;wor3~CQFFdu^y_gYNflD<mAR4f7L!U#1i2!AG?Zu<OMkf?*BS3gL&X4pg)e)0|V$cSWsEI|V
+2d3!>49l5eQFzNgH5U`c1J#rcDO?P0L?e&`TK+b@JN_0AdQQrSKJIO@2y4^e=8+NMGwk=9&?4;N2dc8
+lZbbIuF$q@=A}3)|kWV@!pB%P{WK*)t6)J||?|<&1uu$2Nm)*m1b?3sR$0o<-rd0nMhoPeSl^1K<%r>
+t3d{*)uO0SirwrMzv#!<IBtHlV6PlrnFR|8;3h>@>fP_NYXw5_X^*S@g4-ZB_*=H@^o>JWLyF=5)mX@
+NyH&X_hIz{YPH(MpR-X&f@OVXdMVi+|SnJ`UeZ8EqAd!sl?2F7g@!D|4?;q#BsY4mOh^enfUJ^S(~~C
+rp%W<w@eXD_qX1F!`$?VdBiLI;qQddmns2Q#3R-?upBc4=H#<fdp_#Y9&4-NU$*}<`Kkkd>>2uG*#|A
+rP?q};xbtpUe;|h_aDrNTStcpxadwCB#?8%b^>mD>qqRS(e`mkyK%3VU)S4o$qc|d?lDdR3umlu&p8#
+%BHV=2QY-`7G?DvZsOcab-}#)hOy=Y4U+CU?o}j<yyTv@zzxuuPG{L<G=6(EtUo7PR`sC-CIF<BW7o=
+TRp0U{tqbV;7Id9j~@7UfdoK%_=+(E|hULxFt+R4cT_B>SD4&jD8&GR|S8q2x8HP(1@Kh68VZ_SU!_H
+m1_g|j>-Zbtd78NR=vlkEI$4nY3={?#too8<X|J&5I+oS7=oIq@`bDg)at(#OVm5ihpUeYzO1R_20*U
+hWcmS-Kv{Py)JrMR43f<uPFGp9f~=lAVoy?ZothgEn7~P4`E3(Rk1p`EDC8Uky13vP3>qj}tfcX!!h0
+{=j@Fq!99KE5N&cG4h~*U>-qEc2^RPNpV1Hf3w!Z$uQjdI@#E3{C|WS@gu#{HzsPF(&H650#2vww96T
+}tjWb0lA?n6Tj}4V)Bf}P^)FCM0|XQR000O8^OY7yTxrr7fB*mh6afGL6951JaA|Naa%FKZUtei%X>?
+y-E^v8ejzJ2<APhzKo+7xG-e3+e+s<+*!GQ>Z);5XvABzZ<X7`i#^MBxy0WnmqOo$NkQCPwMq@=2ZT^
+kcFZecnx?xH@WLB6|O>8z0{)2<UMq=TD)52>5AOpXu}4a?zK2zdq#Vhd|MU+^{1#@_#0ciYINpAD_+-
+e*9&Y~~M8O9KQH000080P~d=N8H>qutp020LCQ%022TJ0B~t=FLGsZFLGsZUuJ1+WiD`ets4Dr+s5^G
+{S^mi1;th2(w_!s5C$}HHV;VbCLS`tahVf&vTmkG_KviyHvQlC-hCfGTz9C@#^T+*ckla)Iy*aCd|*H
+IT(ASHJ4trDVx*-)^7?LZ2D^)$XbvRLcYW6jmgj^Y+D3GQZe>&T9n1CeVqw-BX}<|(zsY^Y9^HYqL;T
+EOZh4#K53DX5kxBlT9hwIwvtCpc-`cy>c|s-8%ejW12e)v*Ovl8IR(PAm#aj<qHpjYZXlViNSeK&&@}
+j9bkcU4h*iZO>&THAxs<HqV`Y{lp2Vsu|YdhZ5j-F*G>bG#u6*6)zL?coU*LS>fAax@SwBkRryru`wv
+)wKZWuxy{Eb<&E0R?TyO`;@}i{QHCcCq;Q`k&|T&hroFa7M7KXbvq9u@uRV%YE0iU-2LL6$Oy4lNBDh
+NEfd!-@QA3e?0-U{PF8&nST2s`}3cY6tMsM>gw{t_4%9p^6L8H^8H7oWI^ELsRU?{n4laeS`GJD$ve8
+O*k~i<W3*W}pp=q5jyBY44kk0&*}+y`@%ny(hDV?-p9cjIH^&@KF;UaGZPdf;5uo>3NcvjR-B=b5d}h
+A~y&w0CO<p{|Nvk70NkIBDZ>J!)yr$wb+G*<Q<o3YAUJ$cGx`u2ql1Rx)d<b<oHWje0AEwO3xLa<XNB
+7@V&6c`tO+mM3Bq^Ef5(1_Mag03Bs%JB|=5NkFUgOoxGR+W*>>5ED!fP^l9<ErFY)8f(wnWE4FI^EdF
+eWzdn;IU#VVKO(bIpRGRzE3@CO(o$38x$m>o%LW7|vEisWC5Tv1jT`x>lGVWMAoM8A)8nby}C?#938N
+#4VykT^A617YfrFIHU(+nOfu8747x}!~>o|n%($@EQUlOtTj6q20MabkW(I(UD4v6-m$mpWCZMm1_&+
+%zXN@Mb_&6j@P@ZXutvW6=6@&)v1`#eBjWWWfi^W{SG@hG4-N>l8Ugr=9qB|5S}~aur0@5&>;;I92{2
+AtJC#2zzd*z55qWAe`-K0##uHZg>M!3}-Uycjtz+%)q#Uf{`qb*YvyANTY{IHja^O-i^_?ZSB=_ueY(
+z<@xa;xQnw-Um5<@`Z*Hdj*xw2-=uHbdUgzL%i&J_5M|GIJxV*W0ID<HH(5wNlMWU{SzrxurKbb#MMe
+#OfVnpd?RANg;?>s-T5?8<xIg1GM3quzDSZ79K5f*X0=AGS=a)C0yA1`7;qf?2rl(sgJTIfvu`7qp@Q
+mz0VEb#xU-Cc>=UZ8n~`PbfnhR;@6ju<{&KE-Lx2saGK|53C@&mZNoqQ^z9MvoZB|GCLDs$8<*#cMw{
+Kfph|V7=43St0@M5M-M?a4_z8b{u{a8gA*1Mg&_M111%{8qa6VJMM(flZKHNy=BO~+mg<;FX*&}n)zP
+=ct|SJOP8mAYVh%7M6eeh&lkx1HK42ywY`0@Yr?620=&`b#%y=u6Hvrj59h61W>XhD@uoK*xkxLck?2
+Xe{$$_3!bpR3ppcUEn9pcu(OZj9u&1l&`Mf`v9tFURFpWqkhf0A-lrvUW|+XM%WdeA!T7}AklOY#9sf
+VW^Nwq`+0OImgNroV&a+6!Q^Q1w4@qS(QZA&Kl;tod1)YK>qUg2z?NE^$dKQ}Z}7nTfmwY3q76ZG{=h
+CKh!ShNW%Kt5Wx2!o*Z?U<$lpRMhJk26wZX>k@2m)R*W0guZ99mnj{bC%zzG3@<zEhdPxmL{ohsM-EZ
+|MJQJRDU`jc_-qGj5x5&;c(`^FukngInS(G~n8XL<9h~<*SG#6I#VXAjtbirvUI;p);_0PogLN`m1}H
+(DmOPU+UTC`#V7M4VQI%ju$X?NuSX6g=mxXd4{ovB%8%f|lMG$;R6Y>r5IW>7%Bq-jXHenFXU|l3Lq1
+I8PV>Q(YEbu$4GAk{wG%lM;>15-oFT0c<kPm>eo~p=DvDJgS-8gB4&E%dH_qHX^wN2`dVCga&DkKdI3
+5-$)iZDjd7M*R_rC1<LUiSEg{PJs*;8e>n*DR>!A|@Z$vLHxWmggleI%wO`rK6P_Ja-#I56IC+w19_z
+4sNdE&(LOcu8r>Sc0aJM)pWv-6Q))1#(werPC$5^c7yN3YS9@UBrG!?7+D{yw5|9Fg8aj*(8aGp1{lK
+CA-s!Yzb$)9ZY=+plbct!QH3+qN$eYw`U#oX1m9N^%8JlETSsO<XQR}puNkN*+HZtj3^LDp5Cmd~*dx
+%WFd$kd2v36SqO+R3JP3cAfx1zHPvZ~Bd=7fduR-)_R3{7lsOkwJsW%UhPjT)Rvh3z`nPgNHdpB7~W7
+=L|Jw#PEj%^B&7{EY}{6QxPNuWu)pJOK+rsBXAQ3Nv#dorCRA0w90xPBJq+CZGZ4Hj%8jvV$$j(!g}C
+C8-P$PCY9@Py=hiOnk004T0!p{K=gefj2cO+M{Uinkt2|4ZPZkWC>;m>m4euW9myKuN6T*GE=We|Lyn
+H8s(7Pm=ALyr-MExsWij&*wy8`z>hZG~KhUoR*PfouM|Wbd^O6%RB4kt{jYUn-b0KN;R&F0B%RP+^7@
+1OgZ@^Qh#h13Y3Q2q-uA7;;^goaEQ8`tR2%4DRx4Qlwdq~26tnA#*^hqG~;77aE*6HcMOK#@;3;@^Q<
+nHldxkk#8dAlsCSfq-Jlu8zJQ;b_^rtDA<X~|I}v5353ab1@!ihR)YlM&`Hr)47>zzKe4zwA6;5ORn!
+TDQ+T-}6$o3c~2=}irh9tke_|LoZHQ^nE%N~3ZnxmQ*46ECF###buD(-QNh*hwJ2C`rS3}!F5!uFmJn
+sN1|S|wFefRbD{;t{y8-8EBl=qx&}Izwxu{?1`?oe%Z)nQkTi^}%}>`!!K{*I=lXYVJ5p1m>2NrYc5&
+qBm#RBbN}QCsC&3irum}d49^IycjZQP0+^-J$g<y*MYjR)c1G*dSazVGjc>-)T+3*t%j;u^(&H$kWXf
+Ka->kWz+wH;77;GZ{HJ#BZqu-ZTB$6E3y3_^4TKtaxtRwQ_e|&naNP4^uS$iY5u$Igdf1-yZSk<7Ywn
+rqxR3$UHYX%WU32aOrSHx((9QbRjbGIYx|ZIGvW{BHpg0$uJaVv|<Ykf)3lwj_POMp_g(oUUB@LrKg)
+cTI+e@@fyL^?JL0Z>~ngEm98W97OAnw`EmbDo~Lzs0Wybl@`wM7O>!cKVthtvbA=^2kzcBV&SSNa}Wm
+ieAlt+6~a5M2an$al2ppi)!=^6~8N+v!%>=_^8VGBGhi>&2{hTw`W|mR{0k3WHSZGe(H6b@~2e+at#?
+ktfV&9GjEcNsZy_5-@M@6YC;JL0f}HjS-O69ymBcvua=dXLjOcNXZfLQnG5dA_sYw4&Tho|3qMk#{(5
+965XIl;a!Agq}PqD1g|l04o^W$1B>|=oRD_F12&6114GH@grEl~2X6&?;7zY5+p)LWHzGIQ^X$70dtu
+~y7&8<WxXQTL=g&hIgL4Y+IHam&>s@~Q7JhU|ni$^+UfzuG#1$8Q#@_5PBFGbNKcXzd5Zh|@oZ0CXVh
+@oOk`Uyr!WJ1I?`-M}>H(~=qSDCCD0cV?Yeu^tkp><4^{-+iVTZOmVX!l>tTePofKMw@?QDqujYuZ@?
+J`O9>?j?c3&atXG`K$1Pbg7qKIh-G#f<7;1r^i@eX#0F7QR1#IvuhP1an09GOSEqD%?qg%ikHye$1kW
+G$&@~Ks+Qq7M)ZBX!j$fW$^PF50aXmS<6&LsX~YE{{c`-0|XQR000O8^OY7y8ZA@;-zfk9Sf~I1761S
+MaA|Naa%FKZa%FK}X>N0LVQg$JaCzlD{g>Oualh-YK<|11m?NQ&CT`z3jN@8Tsux?)ldQPu-5CbS<q<
+s)fC2DM=QRI&XJ)^@NXk{(z8Abi<^j92yR);i^R=_<zTGB8vEGflo)tw>ZM(J~lX5Y%^=@PZ|NGG+@x
+E?vZmQ-+{@D)lSI^|HVY3^nTK)B)ii~XAt*crUyf6C(V5#pvRs8d#HTG#$4I=^?a{O2JYq=?#8&>pfJ
+37S@-Qcoq*3}LF&bDP$=Uv$ktd~VzGPZg{Z$w*N)#b$<YgVl))7{<_Jsa9xzhuKS8LAIn(Hz^Zt3|#o
+`&-tl9#qZ9`mSey#UT;j3iZ+l1F-XLd&d+2-v3VYP<9Av+;14GjXp2iZC8%6T2qeI9V>>hpC;eEdj0&
+5-@VgCSM7aMx8+K8awv9vJx$=BVr&ZlvzaFNFaAYephVd(HwcoZMS)@;maH4Awox6<9wqR+YKF0_>k>
+b`==&DB@g62P@N(F7SdXo~!vRenO*HVjU6ujZi>fKQhiTI98l;(G*Q{7y+*SR!D{I!=O_MjT-W1PYeE
+s6v7sWSUzWu|Cx5dj>U%z<$<u@;;RC@==v|6&LhPMI}+STQQz(&6vd&Z_Pi?{TqU>{(@ffyF$@^;r1t
+Ewj~$ssOkM(-r3u@KD;)d*nZ8~h94EpN*kq%3xGQ`A*+D;nLFw~V*evzjK9!&^cN#jsm+eG3E$^?FvW
+ijjR7X?CW`OQ`sgeiZ~`v8(D8?;`_*_eS4t*eE;ARzQW&!M5>Gldl-iE1wOCrnd|Ub@i%wQ+5CH9bdn
+0HNhhEYFM#mEa0s!*s~w;|1gW98?vyXmp6cccjjWbTdKK&pZYy53Hb#(SI`?m3Sfx{%wKXG{m{!?j9x
+<clR(C!M>q^%8FR_<a=_Zx^e!ukrrZLvoItS^TPL{Wst(9w?8_m;$(vjdqPI}JXTTrE<tFRX@3Z`Tl3
+iV1et&g+eSUR4`Nx#6xe1l#^`Xk^wqf+9S|?i$2_K@0rY%&tZGO|YyDob=QDwYu&jjpxV0$3pi%0YsI
+Ml9hoDx$37sk7+%VCg9{Vn@(SM_WQ6P&e+|76P%2F}SJ(E>zPjYW|StX?C*{x}8Jwq-+s^8q}Sm@AUy
+97ha|tf~=L(fnlz-JPoLst4DJC|eE%tQ=7?*>$UOWb<$PodKq3IhW!!d}!SR@=u@>QBSaH(Le<oAi!M
+7CJq4ORJSab8zuk%7YDj1f$?(?6ODgFNUo5d>FI+BuLfEI&5~t=G29)HzJTeC4;>o}Fnqp##Q8IKtK6
+6tzVjiZiB-++0j@_m!&V6n9sadn>sf@a`VY5|96uG3f7pdI4L~9Nhl43Gp-@Erd}N}`op8V*tsxszUa
+5KVm%ly=w`7!YU)dE3W8R^#@t0i=DHnMO{Kx*l=@>roANEHnSj;7DI*sGrQ8jD6eVdE}pVQ>q4IpCE)
+~g{Ip?fxwJg<<d7CU0^eDNYKN37Fp0y4nChqA_P=OHQYK&nElI5a)LZZWJY*i{w}d;0T9x`R?uT`k8U
+xvyaKWzzywkl*CSMjNgKdEwC}uUk-Ezo!hhgR*}}Xd6wWRN~+tC0f1F`e5D<(2cQ8hOzA#4f>{T7iA4
+ygPj##J|M-2PcF1!)9&h(0c}&>v1GwmlPqBtfHmdGSHNYLZI8f2P1)0A%f?N+%AFopD^}J8??t%J$O}
+2<nWBBs7ie5?+LeP({uY4o9|kzs#1R7owOoS?oD91qN-*nPotVO*4N!b3s4!ar8hzwCvO?0X6D3eM)1
+PE(5be8m7%Ci2-_|t>aT8}0Ms24#{(WF1&yffRC5WxtMaHs6=_-I(`mEWCVpVJP6ZdFjNmdQDNg<b)z
+mLT)$Zx>^IX$#{787rY3e(W}z?>No`yqUmKY5AYkx0PFP3`@-9Tbv$vSmFO+p;4Fi0u+;mAiT@K)Q?V
+vGnZSm;G%x0%gzUtb|3BTt7&`@N4AUstn41q&rE;r~t{c?_qWM;s=RWunB8)gtsL^i-848DuAXy3Ml(
+=K!%m2DBfPAjx1npriNmd!EZxEv_wI#9uckj3=A4~?|R7ikR$S&{ux5JlIuv$3KSr@4lztU4V5tFOaI
+*0mJ<Hl7okdursR)MAqg<PaznLDf;4_lYyrlmA>3(UHw(xxFdbTIb84BBMlw7UKD0ss@eVoCQg!G0qe
+bG6l7>%CPQIcI4dja^pjz-1S&ClWZ>r@c*;Y535%;6g7`3V~H8xmic!XwABTWZDOb09#)Gk~=93X5A@
+85o%o89>+UO+NCnoMYkI7^btZN<Pkn6UKoB;B(NowBLfg7K|t2orW)J`^o4;@|nu)zz5ks?+4@B+#NE
+Uy`QLWp*iO9;s0jx$uZwL$Nlz*Z{qP*jLqMr+uz~SjB~`+YUMY%A2;|5|fiJ%Edr`(`;x+LIStDXldN
+X&8UDw+=@Q$8E`k%O$3YtE|%30ssZRX>n5-sRvx<XQ6&w6KtdKc)cZu9P9{<(cUr?a^{rRXsz1Xt6J-
+TV&a?4l!NT~QJXdS*td}g!Xf03BTxBfE<RlIY>#o~fv&v7BGa6>F?8JL(PM2ZcgkjocS<PqNG<gPbAu
+y+>Dq517CyP-oOx<y0r6#`vbk%KpJ3!}efx{WR3Zz*Q%+oj^ES#)b#_>%i%QPkhC7%kDrj|ISz{g2rs
+uOOSBqJnHCzLkV7y`7hcAESfP&^9g!uCl3kUbCXTEWWs0Q(E_jyWH8it7apZUe(<%=+{yzses2x2Mu5
+TllkN4l9TWbF2rM4mCuJd*Tq-Dc=-nM8UxT@lgrCi@f5T8#8$mk|ZXi3B$H2m*-tsSKO@rQWS|%FhR|
+gDL&h~?p>Sxo@IYF$i-eJs*)H3=pm$pbTGZ3k!^7`M<MYJu4BW%$EQ-hVL&J5^RoeYMaIEM;Q@aOo3G
+0NT<(p}DJVaD$su4+v`8(!xdF+bydeV{^|Rq*kAp@d1hj<13_!cD>Keor6s=I$Np}r)Y?TJpGJ8xk?`
+ZLnO`EH(Gyx<-$ClN)V%m=8kB$Q+%&PYvrJiclz_aX<JdgiqqpJAtaniRdYk!1bJq@c0A$fa`Xnn|@n
+id6+@RFf13uLPzP0piaeDL9|PCN^&E5>u1It)md)QtrTW0Lk-N3}z<>ey%Ut0!|P>!eTnLvjv`mCL|q
+Lkeq270yjo9#AN;RkN(s5~n0f5R;!R`(acI5*-7%hj+_#+hrac<x-q@&1sswaPeZ8;hmxfP4f6y7IFu
+`5o~8)^_(;}8ml*(u>l!a%fx(65OaY}397Sbu4i3<=tRl%H^A|^#r6W=YHk0l|1Gv*Zx!4OS&&9r6uE
+SzO%;_+rnV`5qC3*H-3}Q9`AdaYX^om$j~<|#zFawK<qL9r0xvGfm|XJB9=A)-97&u^&@PC)dMZkxOe
+`c#y%Jxyqky7`cw0U|pE)}x8ck-{#(UlmghF~Qi6o=p4lwz&VNq9>$FL%5t)HNK!*nxnq~!RY!e~#RO
+rlB{2fDuPSU)}}G%M`-bIoV3R{HPnxERL9QS3EVeZCPGP)?JtL9X3(FcZYAg;+4f;Ys6c?v2^<vBbw-
+gnTRk-ei{b;OD$w{8h%cFe+rLdRF%c)<4Vx@+>|=16Z-e?j}p$4`_%m2hXb^Cz~4ZqLZVx_I)#i{yZ%
+M*d`yvB*WWB7{LO#ZP1QxEq}-Cbp|Z&{5ZC1`)kC4QSP6>?6?l1Jyt?vvD%f6pYl^;jV8Cct4dh7WPS
+lbxYRbb3zCYt4PtFEI}S($yC}6lIuDZvkgbO{xo5(o1wgU88nOfEk3cUN0;C+DT%IP!7`z7go~4{2la
+JW041Hc#ACSZ2Kq79l0ITqvd@_zL-`=k9cb3lRGc4e=tGZblaJ37^n+e9BlaHM{xkdV>KuOkQ531XHU
+>E!BRBjc6VZVI!@87(*NL~$Gv4I&{5VucA{zasUsY4P_UHu}-HaE2OEq>|^XbnS$C~UVDvoP%`JjN;u
+tpj{)Zc1ijWT@KQOMFz<wkkV;QCXmvK)>wGf3{VV8Ie?c7toSXn222V3LE)1Es$uCp3prUqL9amliND
+lOIVcys*lFf9FmOXH+gt<PNvc28C{sW24gPoX;me1v7yJum}YOu&{`A-<O`X*(DmdXL1<sNJ7PoNqDT
+SwU)$6V6#52a0~!IRdMoHbtZIZe+D>}~OtrsXv@*P42RN&G1u`t_M~jildciWW1#Ojau%eZ7)4M<45A
+c}qgofRF@B(z~b;w$}&ayROx`9f^+K<yO6<tc<m_z3Z-{-)2SUDj5W4Iskb7Vp-P;9efzG2i+53L>=-
+T{-ZSTIspy2xT=pFqPA8Wgq*;8bu3@g9%$Pwn>2S)5Gw^i4xDq7*2!nu3KbKvQcnCx^(uEbNr^w<$k}
+!EzVxffbBSiiowyoj!?FD&FZ>XeFQK&?qXYg~7xF@-=!RitfSfjJZS&Z+CL?x)qLa<TE5Qal0dyRJBJ
+Cl5Am81gm3fW82mP?T+n_+MUUxtdmz%kd}%opS{zST?YR1R^Exxo$YQQ|Ei9N0cw}gy22qV#_hXpPR!
+UF`}4#MD?wDRd2EYz13mHxz>BEBeIy-r3&q*V7!)k)Dt~--4gVo+@$R%9Wy}k<cWL+Q`dCmB6$XAny6
+bV>55qf6-aL$(wt0r<J>XMO$ha<~9gOX2s%5*ft#Y7?z4`Mq!nmcg@s4n1jiS&|RdeG}ba&;?oHf`)H
+arX%P)$+V55tI347^7dndwbZ!Z5F_1us(vIBunSMCJR?<9h%;>05sEAd!TckQeOYY?vqsiI%_KC<1;r
+-Ybng*^{ZWNa8f`iF+xnyT^5v3?i}hOrexI+~0L54a>gjMkTr66sn00m!luRx=GbQoBO*L-@boSy!mr
+cq!>@$3Fa=rI3Bk>fZqJ!_XQpeaR50X1NbaJ)F_r)412poqGcc0az`s3w7uC9jWz`RI%pe`9u41Ipx~
+YZ;FH;`X=fN(I9sl7PHb3e8|A&w6_d+UeoO5tr^$B=&7A3&bi!kdV)Km8KCZc>6ke^i9jEA!Dxr0J(*
+*Q)!RK+|N(WZ51r86|R71p6HpV^y4NVh-hi5}<K(N~a;<lbMcaJtJF+juSJQk<IK~d1kqY%d>mHix(G
+<S)k)b}guGc=hAH&D>;Y&s*z_%?N0vInxfU&#d$U|k_>S$pDDaI^VYJEiW8g*SLLJ#OQ(G2<Lx@)?&!
+<>xLIeeMJunPX>swzX;RLGD|%yD@f-WZ7=FfUcrucdVYv^F7aB{OkMQ#|y96u$-qI?Vy^wss(5}7;!@
+lJLGKJXK=o3=Od3D6)tN|w^)F7zRBf@IIwhb9SoaH<bkGu4YkEEvy=J!<cOst%Q}UPGa`pProN1L!YS
+%9*lll|uYon6M5L*<3%`#e%P)WJZXUErMeI9bvXymAnk&krMk~t(_EtP~Hv$Uk2jY-#N)U41@ePSbFm
+B3004b@l87z&v3M1X2V*ikIEuNeqrxP*ffk#f1_qX?DziMC+QFUt{#Ys9jd8EX<d1CIF{1}{;@pD1iQ
+W=r7{3vt;)<5w(w!r?YpBNp16MHL8IQGbt$@oc@TkV{c?Qn$4jiZ;0&(t0AH5->3fqr>B2Jy+m-fW_)
+M%EXEyn>K7WNLrR?|b|@q?R*ESkO3|!1==+k)JR|OLEWfG$ybHU5A6g*jqfQTCOq+gk>4wMeU`;-w9J
+cfDi$w-o~UXZla8!P5MSoJuze;L^VS715I{wcS0Va+>(jo1AH-p_+5^mAIo`!oe6Z|CdKFr)=V-<MUx
+zjFxf=QPghs{)fE;@@jrs`$m0C36x&N5LC29GmE5kk&T=f32UmnL`I`T6;Q%JTwH*Jj#ELD9k{Sy%Bc
+G5R!FcZj#v>j+;A1rqSwRxKWhV)M3tPw|;|YxgyGbNro)``^B<f=Ez?7*4ym!hRt548wb6DcTt^a@^6
+yB&r#h)+h$)OD#N)5OsS3x;M+`sWbn4lbil|G|;jl^$gV3-QIydvkreOce~Hp4-ASeFdd4W5Pumf2H7
+;liS~qC7ShFNY77$hmbj3yQe-oF=b;m7(JY&mW*ezlHs$T_->Mfc1a)0ZjwwuG@mPG`gyQhJrB}1MP`
+5+Hw&js;>mJ8Yxyo=q7!;v%&*xIN3V}8}B%F!^PvrH=v2`7P!AX?jE}GG0gYlZdccjfA!h3JUku)qu3
+!Fc*yar8+_iPdc`A6u~ylDXoy{>E!&FU)oBX$z=N+<-!{PjK>1*d_(6M;!zYfiKUwQV5hbjYMMZp`i@
+gE0$bBYB+yCu*cvKC1%%O1U3*Cb$w}YWB9}lE9pCiV-hgR@8YE9vDntWLgZL%_{Yuo_s-@tzvpetIFB
+G^gQohK<4O%p-W^2LyTf=()~hVhE5S!I`D;yH{n4o<H-Z2sD>X0g#VRaj$j{T{{*e7IT3GY^`zUq5nr
+HXS68H3Oo+$DDFVUkhu>W2yI6On9m6Fgs%9#<uA}v)>5$L3MK)Q0z~RY5OG9wquh)QI+u!iY@od0?Wm
+4Hp*IoAr(niEpNGpwe3h}#_u`z(mK$PU$b(kK<!y@FYgj}WPYBwXIx1_2_NPWLuZ9DBo1l0&=42~#V#
+>|d@TC~IUg`$52%@(a=X53K{v|XX@<cn0cV&bzfGR**`Gl;=*RFB-?E1}=yZ!!nS8iNKERf;!(12o^v
+xs^`KwO`)FU3!K|RZ@&Oaf`Or9K~iLgt}j#IEEdboUYy+5uY0Merq3cI`w8`{w(pi?Os0VYD;w1r!em
+D4wW`2FnF>z98_J@FR52QA&vvopB-Bs_jlgokui0Z%M-%L@~>Wm}_jlbAie1C5leG8!RFTbg<(?($^~
+Bq?nKX8h#<iuw|H3cTc*VWdLVr&pI*{>v{WSJ!FOHR+}7iG%<K)B6|(ZauS{(Fkp_pasu{81w^LjZCC
+7_slM-_+;2RVa{cFekv?tEjvg8XlMdn0PNvx;K!rH$dNn9CZg_*h||ME-Y4_UyyKR1&Za><HM%HIwn|
+0QI;npwLF;)5m;-}@38TWG5uVXOA71AgV7owmO$WMdB~WSNel+ZU+?;$>HdtAtlIR&Q_KHUhjTH7_yi
+pMsu$=_F&%5TfY44kpm`^L>=d~H`o=_i^geLTHkION5X3g{Jqq?-DK(0ZEfv})u-i;%=n4W7JzdV02O
+->f&3J>62Bxn6e^eBfpfxN}N@lSV4a{E#y1oy~?IDHJgv5p;M+6@rZpfoD3$g0GfOaQYs2Jy}A1XL_E
+7+KwF+*8##z4|hM0W0TayoGdx)xm_qKA?3p4#kHc=IzNepxmYJ8vw{i0Ft}{g{8i&6U0`M5iglg0J&E
+iq(AMpm9sT57q`rKB9?Wz8{2}?5BA&8Ieqn8C0`*K&q}qj-1R-MERX;>D|_Hdv7LIvi1m8|!Yb%xATO
+>_{v`c_OaMIyq^pY8=f;s;Q%k!yK0o|m_V<S$kR2dLGJwBjYi>ISm5SADWE!l1+3v;xb1snS0zJH4&(
+_rkfHqT5#?5ZfxZ<n_bq!SerFL&B@FWIf?4p7onk3smpu#i(vAU6IceK4AF$8R!L>`7Ck_cfInwd-_X
+pGqjZd3~z7|-&ysMaEnq3fTQ2R*%vjeB^tq5^xbnwn?5+3G|QA{o5x6=jHxs20ovmS2o`<w5#p)+Rk*
+;+1$rSA+!4z1%<<!Zun$C8q5PnR^ip`equ&1I2atIWY0RJo&3uICDJIS~|Lf?WKTjN*SA)vBS~~J|Sg
+O5&SqO{3wx?6bi?XVinMkU{ArbAV4vsi*b!xWldFndqGLobWj;{xYB7rO%=jrMJ83~2XJ~c_F5rFno(
+fmu-tmo`c^Hfm*`3<NoiGzBCNt^vQL8>u6zm{1-4Gn(J3b8ponr>-aCsUX(qISMQ^fCb7^EQk`bvE7Q
+<$RU|t4NV#0N!1mv2HPPw7>BY~ij?rP3GNtx?%IZFI`M2`r!3DsUldAVA}Xi6ZEzFKaUR1wsS_-j6X7
+~MQY_#hQ(Ih(XMG64ZR%exY_B83~`QU|8B@%WoL%2580XiJvTY~f?-3?Xdu3cn2@+s_K3T+DQGq9ntI
+I<v@4ehZL>Z=Fi*5*C0+j5nKMBu>p{Vgbz1Zr3-^>xbF4Y+xm=9?xbyTf)+myP*qijcd&jgu>H50)>%
+{C^qroYGkp1g{?E=#_HpvN-MT#(M%MY2P`bkc}+yTH)^Z}uy`cja4Z8p9S^#Teh<>Vi%g5S<Dg)&h>>
++tx7K61K59v5Le~N2)>!{16n&_4a-O6Z2K=*XEmJFgR?wQgi#aZNk%XLV3;{_;?O!N`mf?+)Z{SG=qQ
+Hc-dP>q53X2vQm2oM<1j$%BJ{B@ul?l(pE_bV3#g>G??U+nSL5!ZH%U!I)vYXUu02-aO>B@erkTp2_s
+i>TvNC`)qlYA{ZlqGtfsvX;R?0E~#tRhfnpLQ9CaRyOM8wi%H93G^p%qh<UD0$wiak;_FC5rWv^IpbG
+3Mb`IV@5K-+g<4$TdZ=H8*w+YC;4Z~r%z|y!?XM|1en|n&gb6xe%vPQAO9d!8ABAAW|j4t6m+}p<<sk
+U_0H!K@(<yGcAy;>{4Spq8=Hy7a%e0)NG8~&%<UscqxoYzhU<>qqaa5ja%o0<PuwY>Z54xLJedrd?om
+7(lJzE>YKVs>vEGqDP!`M98w$0dq<j;Z3N7dw%4|^c#KC*WlJEk=SCaeA2Av;@6SrOz%CZJqK9uuK3T
+Z~>IPTUmdtnB%CwmC;h^6l!bq_%wLy<1D|0|+&aAK3<M=2usL1-d2k@3KOio(~)@pId7K?*L{6$8>$d
+=LNmM(@{XfWSR%)81Q}8l?-FC10Uk;v#C|9^<GxrA!yb9>-(x2O09OjVEELQ<UNg9WNk17CZ-^F5+Wa
+oG*}~o2n^ehGKP%{+KsJ#HIuvyud=78W%M_LX@>DJEtx12~E1~S>zDpS%--)o56U*l_PNp^P`#|fo@T
+bsXyJA)v2vnaoil60ihwCf?u}6Q`D<lSMqn|nteqtJnyEiMBIAa`6Msz;>bLizTnb(pjZ32B&D997IA
+FCiI}AhB4TPbXUTVEy(1l4Kmv-}4oAT-aC<Dh;&TMe;uEJ^A4q_9@j0Hj0tbAI5$>hnSVMbvlRdp40^
+b{f6j$;77X8S#WmBy&Z0P?%hi2l)79IzU_a<flmN87GcVKU-cc;E_&QK(ZFKkCiAnw%w7|1tde9a}yy
+%{;gmdfJx_jFXflNNUhKQx;<5ki@1Vvag`@iag+e#((@i2w6(ayXx6lp??Do^(40oVh&RiOdrV#Dg9u
+U0I}$74hBa*)TgB@}H_MwS6G|*_1nVkjDcS4EE7UNz69)C1)_fc~Rp-2O9ySdq}L?t^)Ghw;Pd{6_dS
+Dp*+c|$qnc+FtF@?QlU#YTTJMhtRxMj__QJytbReow89w8d;DkN?u*sh=qup23WPLzGi2Y>fDg&s7NG
+k8ASe1HeU2VPq&`YA6CZ=mCtDR)X%#%sr$wMoE8x)b%)GvbY!BGLK!Pka{a1iSORE#PErJ3$^&vichs
+&!01Pc2-H#1(CElo8dERwh9-bNp+LO`0hWibDbs(V?vmoeaDH$!x&>d+l?FTeis+ZWIM-UByk?*Z&*z
+nWV-!5^7@AXVxAZZcsvhQK+gz$0e`HpB^lsTmflj}rKalmx!)qYxqrch6Y@`Bjqhp<}b6lf({>1@2p-
+?Lmgc5c5P$v8-9y6fkXtuuApPE)j`KhN65@4Dxs-V;+fRqgX;#CH==fP`UK76zDGnz)wCreepc`&2N&
+YpP?r6r2Xe#|2kHmA6LWVB^=n)3N$ovtIiUc0nt$rUY|Ug@3}8AF8|6bJHZ0Rv8M3!#lxTR**AYW0s6
+iP<XAHbhv#nMG6fZ|X}%XMlXmCyU3o_lh6lL55;ENWb76ybiiel@@`jtbpO=F^o59AeVef=Sch-|Zk4
+_9A0B)cZ0C>1pW;L#0=wHSq43`)@ElNWBPcN=RrPOgiqkmI-Rj6D%TSKHKi(?_uLC0iFb))Km6o=mJs
++n1RpWrC~5TjOovxA|e^-d<C7I8P~W|_<DDIGAO8%_d)2%iiyBTRKv<$<>Lh9>G{6QKn1IxbgR1bM<!
+D>671@TM#A@F9NXY_+KBYJA9KNU514)gP^UEvJ7XkI;z!1cKf}SAlu^t3ZiiDtbYuQ^<KmC(PQljGLk
+(&%(DQvnO=83Rje$zBPbgZFSZka~SZYg|A`GMJ6zj9bPBnqYcD>@<-8#Ovy@~KCeciuKyV~?O;W{sCu
+mkl@SNG(U>I9+0j5t94OB_aRZF0F^AIXk%*(tc*=ukJsy(XV5T0nsSk6M&{T-O(V~^5b{Sxemxk)%&~
+q4qQ%Haa&~+$@iqpk8Kep#A8%&boC09{kC^4Fm0xyu*lZQnFGJEn5MQqcvKu=Y$ih$=tBly|D1Y*kmX
+7CfwTu001W!GUWEGDLz94zIl+g?_(sXFEVxjf%JKkGjac#o~~<sW;rJOD3#+FYMB7pKf86BW=LOehq`
+1d?ThZ2S{fmvR!ZD>F?Q4kd(-oKf%i6`uh)-02b~j5!#ylll!I1C4K)v6iq_%2}`vlA9^yy@N*pgw-Y
+ryJ?JqcHU5ZSgjg_H^#^X;Gt-*?g95WOah3e7;%Ob=2l%1Po{bPUhRef-$npway@T=elf4a5l%(>?3D
+^MCg!lLcPq*bgogEFfJMe>Zp1eFEqT^U5~dRSQToC(n)CcXiZ6T)X8Q3_>PcF`I+*e2s62f~WB{537p
+Y?}a~5{!?jS20@mn53CrxAO$||2ngs!czL^>MA(ZY{hr!oF=$`4=C1vSZRR@V3B!!X-5Xb=(7!aaq%k
+QihndJI64q6bggy=y34Fv=EqfE0y_Kv-sMw6;wIPZ|Q#XLx4;@S-eWrWAvfe2#8iKnIs~A6K~;Vs&-f
+{Kj+lTih2VYVqVSynp<pv*R4cM~8>F(@udQVJ97ma-_sZO(P<QvKBOCgacNM`m}WN<6tCnT-SH9r&&{
+@b4y37FJNcC{RjjfeNoz1Dq|%%gLPgzjiO4WqZ12n&vj{v<wc8x4+`QF%ZwpHc4g{45n78$yXm&){k2
+3&PKxhc&?Qmi9Qk4oSz4bcAul?a{kYY?1z{QjFqRBnASoZiCBdUB_OMnD{`uUrgoI5<a_CVBS3Z;op1
+d6Lrn&v@N{jkcucy``IVSiU$&%~9L8p!hZW@VjkIbk8nGDy_X%&{T6y#~%u)-X9U$4weAF#?$?DR7De
+Dr3ieIR>x!+tyVjM)m5VOkS;7%2`y$pOM(gV$F#qvOtVt^q2|+m0)WyL9u;Q-G{>A%%GrJk?vfDfNT*
+w^d}4qqC@Z)rHZ{1zyX&H9Cwr<Y1X$_H8=WqQ8h}e`fUC&UWy7*07#Jc87kcYDmix4<*wHy(w^MMNT{
+28_q^9qCU^V>GC6Y@>r?D>5Y>~&zLgTZ0L4IkpQQnLQRYygutx}JP_Hyo;dR-%r^Fs#HpCp2Rb}MCPa
+P6hx{1i{%e~9*a#fV3ay1b`KjWjdH1}KD}aillY?>0Tnh6s1D{xL(K7x#25)Y2v&;vu5Gv>Tp^eVCCG
+8$XE;n#Zx$e33LpI&a#PEKdNk#{Ubf*738`c4h%oQu{@ow0}(_a%x&N8;GEoS`vrlXByCAAYkhlhtrp
+@U!cn%5H3zLr?lwD7gaCqFlTV+7nMFtp)w9mt0hrs{x!O)M#HU;~0*v2IAC5S_I=nfljUAD<=Ix|d*|
+;;lH0dz{+eeqN%2V&%rR4$AWj1y#BaU?b{)v%S3ior*MY7RY&Nre6!3nNC13?%S@ffEU#V?!!5B?S>s
+y<JqwNuDMBq(fDQ}Byf|QxCFzl_uQ!Sx*cDljpc|Q24RVez+VY0*<fg5(by<XrXT3OPh?^|R%F^f)3l
+7R8yLmQ7HADTE_U4NYDAq1(Zk#xWI$oZW72V>YxIVCd}J@Nhw3Dz)74LQ+x&MZwHs*%<jFWh5v58nPi
+#fbz$sugMpcrHw&b4lC9r^o-*<K<(yZ;fy4oJ39hqojJaS4#KF4Gq`Ive2+}?pSQ^JkB?}AA>N5q+s%
+(^108J_--+gIGdt-ZdE^W3ZCGZiDagKnNy0Ii}p?{j}S*@ZUkmhnPk+4BYMfKi@l`?Ye3%xw>eT}s_-
+ZOMS<%}xqglA8pz7;&XqoDGtKHzp#%x0~6_<nBf;hfoT%6vPB>!m%PbC>6gQwI;2Per^DH*1_YLn2h5
+ac`!qQ)J7CL6G-8>Rq3=M`=GPuAppAvlglU9F1f~WW>Z#lSkAl~D|xQEX_t}XpUWRG&6Fg)Aj}qBJB;
+h<1Lh7F6Kzh=J1j%Dvx{;`mVh91H%*@8&j4BDQZNNsNs0K#+XR>8vFu7<;8Hz5>qmL#cw*74Prs+EbJ
+-Uc*u;O$u2%UkC;vze3$Y7VB4S5V<f-UlyIzZq&1BDV1N(R>0M*QI`gYf4A_tVZ)ci+@6t2U;3%W41*
+!6XW8JsTo+Q0+JWXcpL=#lpt*`-b?yA;g3d;wHTC*XyO#QUK%R{VkhFK5rL5mJ_p<qf5TUG!yx+3)Z-
+ywk5ziv`kE2OPpbaayAtQoMS4&2z27etDZlsy(~jiw9Sa5me%l30bOuz&7m)ct=C2n0anZIvj-dDq)%
+WamRYRs=7CmFq8d>VM=_NdsCi2r??SiK1$xd{W`ZA@7WFe&^hS~PcE;%IQ_+S_Qjju;ClG&<@djsU!P
+Cr_<{}|$u}nxO^PH_b8wW^9wV=a?kvJHb!I>sC45+#hYFA^gcqx7{5tq##u2Cq@|4cI>$T!{(DBHe(c
+fgbK=OiQNMs%-c<DKBRo+qvvxsO!9VuYeWaA};2Vmwi?1#&!;6El|aBBup2EPGB88Q@w(B=mP%3VDcc
+gw-tN@2F>;MzbA1UZ@3-Q&$@q(Yd%%$r{=Np*%mfDnuql$LccU&uBD-O{HomxwNyXEWGIH#*!k=a{nl
+;_>4}U2gFIzw6oEDe&I;%NEqd{&G?Nbo~oclb;JeeZgNUSotOTR|@LawIW#h!IRUOXyXf)hF}?vPLo%
+7OFVgSW%_y@d_{s4uSM6@3m%ATjKAdzWy{wF5hCZX=acN}^78wu>+AEYYhte|MIBPPMhLtvnzm3q+uF
+R->b3m#Y$Di@DH^(fFvp*4&vwCBCH+*6jtS2)zF1YK#tnY1P=EXdgwORozUmfMd3YI<zb6>7Z?vSH?0
+HBWw|W>+yiP$F7Pkn4Vo(nJnk&-HF_nj0+?3LTRDMQTa-ygvuM%v(VjnY*t7gD@Ew5)bV}B_#<V9*v2
+QMB}bbU-BP5V|4d&a!aAAN@pK50>qS!qN0oQr}IFZc*2r021g|MJ)4Q>+>ca_Wpoy!^{wIjV7IknBPx
+P2@KwScpY&A~XqS!v&t&av(Ug&6x|7JwF@hjF(f~SnKlPu5n2Wn66Xg>B01zX2%TP9%|J1j9$BQ#Jri
+zGl06I@8E^qO<0Usg~21_iZ*VSMy<70DP`~qxa3b?y`zo=az-rJO~tL}#6ywqPeITa;or>-`~&qDKlS
+IKlVTp*mXBCYBB>?kadys8lnUkD=Mvk9=~&`XjVtoF7h5(Am@)>Y7cbqO&17HZXX0jRoN%*)Ufdvxk>
+Z*wnIBZ1M>vNKl}8`QT&ChO5Ao-4?S~<z$P<nHn!64S%FKvwh|RDyB7qLeWT$-)F+tQEsMvG3MNJ7s$
+UWiR#lha#gR&7zJw;r&62*mBV7B}N?YYMp<klrdglW1lC6HWYo_~Rh=OI1BAF=`8OICgS>as^H_Brw*
+V-lf+r&#(N_anN7my+`bmK6y~Z@9+il45|J+WdfzT6s$lL)TK};;EA6A%)eHQB@*F$}r4%uN43O8&FF
+F1QY-O00;o{l@>=`7|$3%8vp<_YXATe0001RX>c!cWpOWZWpQ6~WpplZdCfg*ljAmW-}hG_Gc}QPrQy
+b@e41o@-do#sTe)*w=S|jCds93}ge2ZjB#$6R)?M9yPj>?#@uW0%QVGj0k4T`=Xf*l_c-J*Ymgl>nA3
+BletUR_&*E7D6O*Ql)SHIu9*<rhFQ&nQymrX5A!$R!%Q1wN*?ceBEyP@9pO;btpqU=TIyr`P}zO47=X
+CpQIwrsQfMbt&p8Tjq<KJSEVhHfilvQ;PkG6>n*aEj(sR}C*rljYD=dEewM?+<G>)GgmW=kRVdbrC;p
+MeEFN`Jt9QuPTmjzv;TBTeIK5K>sB=32)w2o#4e;HxkAA4V!tsE@j`9n*m{c*Yw{uLtU)d_nmktn?e3
+&D68V@vZGNfq+n|JZ(gcF3}$-FKfuJxP8>zuPhDVyYMKJ!RFtx<_*u^$e)Rw_A0*DB7i%VJiR)jKFu}
+U%j=U=WDya3!gPZBaGw{p0fWdQkJ2iaSj>E&BZ{EDYKB8lfX63WJ=zmA=mU&+DBXHgdnr$mCC9~!vm(
+AwSz;UaGH)<MU2h3X5WuNCuDXJZOFyzIbS^g}}62|%b<h@4*h4+8^`veG^`an7X`=GX9P0e3;1w5AM@
+Oc{LGjw}mJ77-u1IS|md$MP-ZR(!Gs_L1nahn`N$oSTUVgr$)#|9$|_`X({T98VbE39kp0(x-Rb$Xxi
+wuSArguho2GO7tas$pzo_8~GU(vEQK{+(#Ma9tJMaZML@@`2sS1-oNQY`c73tr`7+T|PfQtyYTe<1}f
+BS5H0;3x~!2b|?S7LZFK^TV#K3%6h5BoLCaK?z;_aUry3*;z|U1b8}<9N!AQK>kopR4nkDyMFQ39d-b
+CQR5@(2U0K0=U?-H@OilkIbm%OUXcoLa_XpZ&S_W+@!Jt3m_`Rx{lUcxfb`tD3$ewNBD>myJASp=QV_
+6GA$AZpqzZr@wVIb(d<@KTm@!oYXt?JAUye=x$6Y(F~ea8!ICdDe78FVjd(eWN$unI`VnjPzg`o1rZ0
+uVx`67{wOL9T~CmHh!Jrl*Lrg=sTh6eZG`1JOWC^F7c@V|r?asxaLT{Dp=U00zX%2G^Fx+MHR@AT(e{
+5JS9!jR*=2Fe}VhBMwOF3TRb>kbe;{c@W3g5+#{ri=si&K$p%zbEshvM;IuWodu+Idf_Fl+5(6G^tor
+XMij_$I+WW3HZvd!A<GV1PXtCT80U|q0;cYV)){F%E(5P*W7fg|_4%OeTr^|!A8BuZOiO?WAx}u)AOO
+T=QzJJ<mv}Y9CR^xBOk%4<rZyE5KedgdwL6US;F|_k`Y3u{@SbO8+a;6&3WdL*uGiD@m4y2hB#ZlEf{
+J=lL)@XB>nunx{2!Dw;c8jP!l4=InPiCJD0z4(1p~GJ;n-G0eNm9AC-@nTw3-G+QIP|+^AQ7g<Znn_1
+BH06w=ZnHUK5n5Wh$k;);|N8H;BoRKZ~4e@utv;5*NUK3oRPhX|TZ93wAOy9f^{LENYry_kadeyar<Z
+8?`f_kAQc?C@_xVdLS^_f66?8$w}C%a#xBXhmGEJ1dnTx3rze>jW~&}d1xA!2c;Jt*gKGcHE<^|rzi3
+!p2PowMs=x>nQ;Pr_d)*fb%bivOjIEt#2?5|#NKD6%xRX9Q9j}O#3=H5w2a0x06j9;hS=Qh(4(LdSUM
+(rhE0i&%)Xb)&<yeBJ}ua4_4%KkuG}ZL?As4N{{EYXL*KXZ;qBW!tp2dcw$1Tvdv5t#kne9xDM8V{{o
+;!+{(0=z!u%~dC_4R2IuPGKMkfUuR-wHLFlO?AY7P|-NX>oKY@h?FMNv3r9rn)-T;@hJE*GN=KIh0XG
+V6cp7c1A)Q)p%YFZY|fi(#ff$eJ;*AsE7mk(~Z3pCJ&~mCF~S8EBeZ?Dz0{*DP1FO=0oC>&!Ll9C^Pz
+2nlW@Ic`gf(T(%=3t2tNx0al`a@G#>S!U`FqQB$|t-$~SA5uw4*=^My#t&A%v?cfWEm#X8;;xo^@cx9
+EfGBiB(DF!k^9zh2#l79*cTGJ5$2JH|XP7xGNGJ3dNmRf&B?mP}57l&*V06LSs9{-qhRxi9IsN#t8=`
+1vqtA3F;hEbcNliet+;ZR=YU1kO_r`#6S4gW9d>+8m=f}u^4-5n==xzzlbajum71*TN=}<A{7pScd``
+*Fn)RlcNz`AWR_F=~mBeajsWN1zw$vU$~V}u5pfSCjD2XsYn3TPqP?csNe_9AHO8br>s5G~qbO1JWEd
+jK1m{1XQph*H%g^9higI@41hVU82GC+mz7OdLI02|hrvCXI=&3Q-BN@{dPQ$uL8dn>(1l14)H{Fc1-6
+Fk1Hq^2^4lF?zYyR=HFrYU3gp1Z6UtYi!A_85uZ~=H$v}WuJXKw?bKE0ejwfl3~S;rNZb7WAqj1exI5
+S$+91?;RCw~0WCBliyKdHyTWypETGKBzuhKU;-I!ue_3kn#~*)uaQO_^E8H9m%>1t6EkHc-x-Yj%??K
+xmKU?sCNnq!1Ko1p2%#&E)HYk+`11wCA;pBObJO-jyq6=Ks7O>G>2L=SPgFn=QO9YnXVqE)0=FZi@`O
+%q~+t7H<k4EykpM7x)?fh462TyoHd+$x46DMcy(T%LxX2<V&OWPbAQ(l!E>~RoqRc=QL0)RQSq{z(Q{
+_f4X#-2s)4etl#B1&Q+KrCFK8Ex2tAt`qQ@W#kJL_2=~r=?9~zabzhQE_i8!E5la!xl;b!Uy$-H(y&-
+;ac0j-KLZ3gm%^sSOoAQ8HG^q@6`v^P&C?`lNKD0MufPZN0kT-#=kwXn@Gsxs2^>~6j52ODhhY)qZcz
+?hHX4!Eea%j=|vwc;XF2{UoI<4MMk~g$c60wOD8yyH@n>W><2krhO|-~%gXiB^Bl~@Fl34Bggk^Jqq@
+NVmUXx#GaXZQjb^<LsN6944{Hbr<BXo10l+KjdW_LNR{XdrIKF(K9=Q3ksyT^nxe6@sg^6)~2Go~2yR
+fp+MBq=W%YoTf&+?bz4|^1Fh9Rdw?WIY0lV2|W?M~`A#J{q;!j6zE<qD(AUoL{hxMM0VlILWW=Xqw57
+Hd|C8poFuiB<@%EZU)q<&2>s{S)i0(LzIMimn3!jaZxSio#)LE*W<sx}fol%Mn<h9(ZNc;9n^0{m?K4
+lxiVRoMHsYAqBag+0-9Q7Z7r2v28gjPS}q%JHk#WTM$252RQLY*T@kdd{q2)j45IlifC@h7HlvkbV6G
+Q_9JLe6~LMNP6r<OplSv!uttxHT$Y83V%wSz1#M>0g~F5WOjTZwLY8XM-!;7sw~+-}9@`3q1HGY8TZw
+CeGT#km3@tWvsIbPY00+yj-iZ$FGyrIsp<^g&ih(<76v!pFyvB8av0+#pQ~vw}Lc<x{U))lXOyP*q7G
+qTuCi`r07J{(Du4doaAg1nQmmPo^|E$8hu=<KxQJa@o=F#Tk%rP&t#IwJX(+;kC_Y7Y9(u-lJlwx+`T
+@9AY^5z4Y4QRt)l1TSqcO6sldjiGI&Co@RVC%UN^-I|`HO}^C9bh|~;B`?a1Xdm;Rj%-9+N5@$(ZsL}
+j%Y*Q+z4%uXgA07@QjgY2iul#jSGL(NU}I{y=6mJF>;k)%i272dfQN#7pA)F_?}YNcF&{*Y0{DwigEh
+d96dI0qoDZo-T5I{Pw^9Icwi$E40_r~BU^|gYVOFYa)AB2IFrM65iAN3rw!0?=}Bti8qzj%`r`|rdXy
+LKhG06>7|sG=xC&8HPG(Tx5IURqEWU0A9Yr4(at|(5jVhBZjlcxOdneb#4v%-;DpaNTfI4*)ppCNXyI
+T3gK_;`?h10o3iD}~(v<M87&p6hTE0iK|bxihz@|JHfT}FCow0Vhk^4E-glkKxL%R#<p?EcqLo6fRq*
+grf*_Du>|<86JmK_Kpq5H%0zR?x#}oJIEnKH+{yklNy+II6e6reri2-Gmx^0h--#1*qy~sC&_At0D*=
+O^~pA?Zjo`(|QVbSQXJAmr?&<q6c+za?+q|>boX=Li*GYCIL;SFqIt&n!B`iSA0qdO2Zr}WSJP}9Z7%
+_T1qzIiMp3aXO9??@s_>kbyH*H1;v9l&h$#&lYX~ni61w7``n3L1Qd8?+h?#0fL%kZY>W2Mp@YBqeG@
+hSW!Z8Gk?ny99j?&x`#kk|x>oflDo^G=re;F(zwH_%pu%LD4boEy{@Q8cb})PYArO(eYZ&;k;HJb%L_
+Me(VPM_&q7-M++phq%R&@P2HRV*nRHc)T*LjXwE+shIQFN3aPMHuXq)R!59ejK#R_lfQB85|4D5H@X8
+a|o$sReC~cG)s$7AU!?6A(v6I$XC(_{62VwwbqGNukx{;yt-pC^a#yHO4U+3??X<zX*5AtL%mwZv@QC
+Q%o*07F*oOMcg{Sa|#$ElnFe2i$`&ReR=SIvJsQZm#|E(1$=Q6hwx|3izh2kLyK+f+T{&sdQAi6i}xl
+NaM#_yDmet5s6W(NaB8~q0b{LAy(_z;O3iuHzbFs_qF>VXSZ!I>L*UwG6K)Px_77jwQ%vqy_W9J;4ws
+#H^_;tzU_EbDR_9d_y~jAd{-O&1FgXU=2F(x(lm#&z-THz3R@LcohPfZtJkQt<Dxua>El$!hwqi%9`g
+P2i@-CWtI2EB%Z5kyAf&wvm!rT|dDz=!>iAMx(DU?KtV8tjgSW*}M(v$^jn;u}4U_>2<p7-MZbO6Wmo
+-jc}fo+DpW$S=^prMq&_p%N8%~*$?Z4baDuEeTLGCry}R%@$4lp|MeY{0{3<+Hb4fTqKhF4G<{38E3#
+>m9A`F%Rs4u}jGQ9y*|6dbDT89$xL)Sf)Bt{Wkvnz3PCQA0<1mk;;x^?d2%l!JwLI5zvEu3v^IvSe5u
+r@d%ZYxhc9pH(yFwOx5knM0-Eq8QsJV%<+^1=<E*pRL6*I@{NSE#RD0|xqL3$DJ2yxTa1(aJ=xSj-I$
+d=N6;;X;l^$s(dokUsJ6(<G4hLBZp313va;mff^VT=`l(Ce;E{QItOt2kh03^lx8&@R6jOs#;)TNtG=
+j8ru|?WDOwO~dcDqd3qCGQ0oT?Eht?Ly3Vb07-!mHryI6G&Pk!%6-=-Cu+$2ovYBl>LyUey#)RxUnmZ
+iN8Gf>k*e7A|&$u25y$q3cxW+^W?kN0(26*5EAtpsYoRDa4E-0*_D(T_tHrdXkf{55`h$tZo7$ik4c9
+>&EN5X6Uizm&$J?hO5u4#Df~;?mU)_8rQKM^jfJo;J%oIS&vbDOcKK4p&~#+Vh5qjMZymjzH4L)8(jg
+^*l9|?wdAlVz9xcOdTlDe>_Ie{+F~Tc>2StTPh1+trm*#QPSo5L8ET6dJ_dXYA`{mdJ|o|#vC1@DuF2
+G9EqA%L$yc`Q>C)9+o`-;&UC-%}EyjjyiFuRzvA%vn)eD^&L8rfl@_i%dM9-Arjz<q}*|#uZDly$~>1
+Ah-B}_%VVTlNYz7xV;dRljD)sc~OnH{@bGucF7lrWl7Y9Md;nZQLN1S+7jj8;AbYLqHuBQ6p?(NM2>b
+TuRurrJJ&FOBUL2;CxlbQpN%gjgC2k4J{~zHjae*mv<DDn7xY2X3H;SnLE^8Y7D9*_FDmAL+=NY)Gp7
+W#IA<Ws(VkK(_&;5bj_tMaiWov`C?x;b{&+C+=bObaSd!BMuw$o#~#R4riVH0c<HprMww5HN)tvc~k+
+W|5mf2OYu}pQ4Kme2k3k3Dm;G%P)G}!53mCgAf_%VX=itfc5(KKn;@D?7nsLC5xAU@ZL7p4!^an&dKx
+k(Z}E}+aTbf2K~tfhRQp(x#dyQhP8&am@e<vbfK0}lZQVJx@ywYMP)^1~jyY=5*uODtXiiZXrz_=V(c
+zz9Pk7#z0G1<0I%xxd3MT2CRP4xzT}j_;@T?+Rnqv;&4;kBIZ47^ev#c60<<N407UsCH6)wNUlY2+Bb
+jI``9WG*SR5nzuR%kqw{CZW2=nC5D)<i!eI%mcY&v)RX18P)`zW5KKkxD@?0)CibwOiMwg;gMHSD^Jt
+g<9~^7|2ogVeNx!P~e(VS7a2U&~t^QDv+PuLplQ)#G$b4HdekE$^}=eDGw>?09~=aM}O#zkI>bIm^E)
+3Gc9CQQF$T(8bw?482mv*1~tjz>Uwd3%*u~pcU>}NIs;(63e6FlGvgRpB4o_e=>xWFn>z8Dhq+KhGPU
+SW!kl%wR-MopC<zOJr6gb^q2Tn-wwQ<N4j!3x>OVGmMmGS&`a4_u`$@tPd7Nl8ostiM#HkQ~em@GyxY
+!sWkOrMj+xs!lTXuWlVYhw<<z=>LWwI$8qpPJ^q{Hh*xK@frR$51FFqt$R{#x2~=8?pN3qs8+H$yHbs
+Wl~gLLkmjN8q2!=O5=mWbSF7Xr-r*Rh(<Q>FHIvlvauzF}xrjJqk<*q+Y-0Z`oI0;S9;bIZM3CU_4E1
+4scMZR44*&r-Ptnt>V{fb*6JX(u^77t#UCU-J9)p=%n&6sK9XBmEZ&M9^;bAYSIiJ_jBo(!<3!+1spp
+bm<U&&3J|yo%VA}S3Nzqc5DVCu^-1U8@fZ%<qotNH<6Otw>Ke@?fS_xak;Eiyw7az?4Fw()<>Ppna`E
+TO7RHJgG8j+F+0hXUL+ws1sK_x{FL66sergfuU_Sek&#5CN{}kIeUcxk~Moc>@8BPwdW(zvVowesh%E
+wV2Wc1FK^zb)$jzq=n$W+FlN9k6@NlJdQVxZ;b=^DiwjL+X2vieNQmUnWMsdz8^GXn<0iC|-z0TC)q$
+ys|Hjs!*##SE;B4oSgy8=-PmvPCvDC8AVxD&=sbKxIFN71pq*6I`m4g?(Rv4Ps7pVP^`5!bJHVZ0#%!
+DzKP*&1xSijzYPpVA`e~a9h#o*QId-=c7co?2`%M9!gz04Akm%EiGtW2deFb#x~*=#=FX_Eyp5lEOv@
+O+7G-dM6n`7%x=cw-nzkRXHFM0P!Nf>_^fMwYa7%tv_B#UKpLyF{jl3q31n3o-+!45XnsI{gw^6?<f0
+OLI#d;lcp-Kw2o;*Pw^q$LH$_v>9?8S&Uf9*m=7~E}xO_p=WE|2f9|@FtB24rNOS4Y=EX@uXg{egCE!
+ELS+Ma-ZJXnUAZq+*<d(VUt<2M$JHxpcl@E~_xm9lf^Nj#?_71N=-KVb-COexYU6$_aG1S}3Lg+4Xi{
+*Y=ITaKC2Nw1(!dX{*c|0;5*nS4?tQ5`4@1Jt0nfwpQUg6ze7@bA{USta$nl2h<WJ3-z|oYG4|ohF1L
+3yn83-UU$J8RY9HkuJ8JZi-7Q_{f6KsGIa50{5(b1k-d^b1>dMBN*{Xl?d<jb$QuuKYsk4j2App1RrP
+p)v;_WiG)>8W<uPyWWST+S)p`NW6|t%)&Sk%D@mSi2a}3_j4S<D6}SCQP8nBXoHvDI|ML&OQ<t43`vn
+#bPBxkqyOIL`f1O`E*GXke*<V5Y>yvBKmZD&`j$x_Gr6PJ-4>%K30uSV6D>JO{Rc|7dH0Jqv?zozZ*H
+_nJknpBU!?fBq<uQsGVlLG49wYWGOqh9@YUSSQ5$>lu+#*nxw_p5A@&xLYaoz|;(y8<GV$4S-bRXwny
+{Y!S4n;NLU!0#)U|yZ0v&i~$^K|$Gxw?xz@cEeuy-3$rCiN~1e(eUkl-x7Vyv1=bEN-4?c-$-8NK15r
+5UuNYm{)7z#M5oPJV+`$8wvZiyp!&2eOCv2y=GcbxKf{ly~)+st?imdJ1s|$rqj|(Ic)HRMI@axZ0vL
+UQ*_c=t@yg)IdKj4nuJEY&3K)VbB{v2t$l$|aC<@6C6taMu}rb1Iu`03D34FpB~xG%=reZSf`6jZ&VJ
+q8Mu7eiRIy_-3{`>XLNX5y25~yEQ{@_^4j5np8*9rf5Uz%{^^JdQCWd}2*u2oCy9=+b2`f+NendLHga
+MI|vtq>c?JGK(A7meWHEGZBsx%m^Z^vaq2y2N|h|RE{2{C?{{3inEP~ZW}^V5Q4v0g+&qZ+Sk?Pq-jQ
+LeoU@~iNUhg5b~ieP2+i0qy|k}%P)uGlbWCz)%Iz>*8lnWe8eN1i6q9iCYcO4YYr^J1<RP~`q2eE;KO
+MzZ5%)&{8rMMG=aTp{}^qS|z0|2RYED~YP)CeoB3YtE9(2zCi3_5`~xdCavt{jtT%qnxj?`+3Z9ykxE
+vpXmyfSO#*8Ouxzk)i+t)-`CB(5s-jVAyp(7<Q-V!c<uS(2kYvg2$<>Gq6LwdY=8g0QV_D@h4LxQus@
+)W(>F~RRJ>*C!a_y?dvOdspSf$3F49tmphtByRYU33uuckirRw+{ukD})sD+MrjW6Xaq;nyyBjgs;k1
+knmvn;cYpj}9TM@|meToX+?X9}r&yeL{{@0=4o-a($|U4T|mqRO2#9afdvMfkA>sxG~4=sIv-f#h>k7
+lyJ&pJoK5b@Eh8u1n`JJqTJH_85O=+w&Ih6U0O7pnkhe+0#APbT`T*rqtbP&zOqUHSc4nJ9#AIqgD1^
+nj3^^PHTOyHQxEm!4*I7dM^|TdSnaCSPM=8K6IY2l(;1OnkFMvS-a4bqWcEcb{fJ0<`}b7`Gof-F~Pg
+4RF(+XU8SJv=84*RjSLtyj&pQ~UQLIJP?lPI1Gxhu1;tQN$z=Rons;5X;ZYlO>gZk!+}ts@NH2RBv~U
+Q|VBY}!rvEvBa|{}qKSeBYGizwO3dgggihK0Qw%%WH!fzbT>K~l>q>-m>Yj4@<Di?hMWv#E(HNPL1t2
+O%-@AmNJSHF5b;a`dKPTJH$+1!m+^y^d}7@12IygBd}bC^QkPe++GKaP#;ZAv0m(czq%%GLT<@@VB0E
+lwZOb&A1KE-c$omuv#I2C0O@YF)wA>C16!`8y%<<k^qcS2SsG)8#{Ta(;UHR%<Uxak&sHZTP9A;h1u_
+W%vbeK8??7V$v7Llo~K`uocra;W){}qz%!)O9!{SJ(g_f?!9z7wIS1=^+vstm`Zb`0?g$oF~H1Kwjr5
+Jc;KjmZGqQZOl1-ybyHEwWqR?iiu6SYpKE-QX13qYpzRz~j&{PdM@X+^DDCq6{@3RgIf6Y)ig_~+pZP
+08t^Ys7e*;iU0|XQR000O8^OY7yxkxk!UkCsI-x~k`7ytkOaA|Naa%FKZa%FK}b#7^Hb97;BY%Xwlty
+x=d+_)8fpI<>ZFeIU4x!t}LE(S%h-2w{~*+sJ3JUD^Okth$>8d4=GkEccdd(YuTq9jh71l2=463KJ@&
+W&l6Jd&zv2Qw&ERfHdVsSKfeExW<6%Drzk{(C1MAGvr8uTqC!`aJ$L?AW)wW1CHj#CzVCwLJFJ1mc5c
+y6Vq0tq*KVKK%IhZ$Eu_za{p)!ZVeYgR0qdp1stpH(UxGX#6U;(1vy$#m_$|CDpWjFuWUg=IUq^14dL
+oZ3)*^Cu`bmNyD|NrdP!TJu<!Bkn|&@I?|5+n^mC6p;$00y<q}VE*d5b(0<%(Hn_$V*#+yBkIekTe#@
+&0xmHyH+v<*LJt_7xuzAl+F89B&+7x$Iu&b-9zY@*B@-0Cd6BI@hBgvkD3P<_O8emS$fstWmB0NDY4a
+NR7aM%k1mO#&bYgh{?;DVc~$~Eg+XCKcMAN*K4Zab79FXl^?Vl6l}z1W)rEd1xc^RLJOKI5PHf~L=`Y
+B-wgtrTp*$SPR{QSLu&CJlO~j@%g5cpa>(CLMyd_`G9)ctL(q10%e(27BbsOxQC369SCMi4m&6$bl>v
+dL-bieS4G*Z_iuuciMsW66mNC*R0$mxhlssz%F-KniQz1s7Ak8B_i=bOd-u2xcO+wXd^xjbR`$9%vYG
+cdJA+$K8V|EO&~z8^_IxNXx=cw#4}e?AWY!<)jBXn&SkG8V$@=Jy>A{llfWe^fY2-ww9gu-vX;Nh*NW
+Mjx284y(!}W4$*{D!014~u-BL#U2U7whK|rEDAHhaXdS@9GOMggh;{uWtD5diSgGr?LoI~;i*TkVI;7
+XL<Atfv_5G1kP2Vrzz-17$F+vi+J^O!p{WL!b?L7vFa6WSWcQJIGXYi{h8(!7`qqoG!ef}q=oUIUQ>i
+N|F;9cvdXqWg{;_F|kY_kZ|Ab~}=l{W&94G_yL8PzIG))dWYXn0dD9J*>dH=1`&FTY#@o=h)GHCE;wp
+Qhe4yVbfet*f|nLEI{fF3GXf)FtBnTj(|O)d~XgvfXtlfghc7%)<jOxuR_L1ezc-3(6xzTM9=f)_ih8
+BE@H3*f$wW64E5#a2-O43O2ly71CpTJSTZ9K!D^;8Rp<C1=<Q3Grvija@?%SG=`DW0MWBo%!bc<F7ix
+g;v>AFYF=>w^wxnZFLm-EDXZ$u*kON>9q_gmh6SG~Y()F#C%2x&OE}nP61-bHxop&7*YfvB-s5s~M$#
+x=l(P1dW&50v;k-J6qOyAjq%lE)++FU9Pc<J*o+*n@}Hpb#FNouIlNXP0m>Hn!nKzCYBWFwz)qfJps1
+2y#_oqYYdAnk9Gi+l~;&<$+od8JWMuE)$$b_rWdQn+I6rJ#e66(F$X>gaR&4D0Xg<_VV%4^u9Fcp&I8
+;*Y!5Djx*o$_eTZ4>1ck0tpoBQ89y@hlvZ%dEtMxevAjLP+wE|zo#OLYCp-Ybyh-~<x9eeK{T~;5ZLQ
+X+sbdO|EYRCe!3`V_UpVgxw+PN*>;VLHS_5EGgBIzGoFfSq4nU@qA$%oW&s!LmSmhDozaVkp!Z7b^>q
+wEJJG`ggB2({XS3yX(#9vwI|)Ch%s7wB1ZNn6^6Y0NYX;qHe!dmOUr4qkIY_i6naGM_sZ?A()&j)LGw
+dpDq@mkDc-I@_OCGvPEDN@;EAFzjptDT7GP@Tdk)B>R18*ZMSO;9F&(Z7=2HEoX)Zp)24=qT(%WlTF$
+S%%{mmjD9E`#Cm<`o-!#_NKeD&M?1<G4JV20&z=hV6&PJbM@1WLp<t2`WPrNhr^d5PYUi(pe2*CGC4A
+mNg-M{A}&p>LB?A)g$l2@A8>;1022Jbll-{`<mQm6Cpm?`=Q3Bq#Ziw6Go>(WZ0Z@!MR_bdcqSIB-wR
+)T0bpkJS;zc$u=|ffr>}S%K(~ZE(Z;A9x24p{v2xBs;JCi45bHW=wyjtcx`gAMxcnq3UaOgOionWsuN
+l1|5y_iGu$kzpgj<&R+Oo5K_;<CO|!hvy)CgjH;Xy;Ulvo#Sn4}kyHb@5oq+#9f4S$&ac}V+pfm5^VN
+gkd$@MarzNEtD8-anrem6o8$|0CjNYF~)J=GpsAE`CpL*{!8T8M}>yxfgjKHHs`WiDOwj2jYHJ*!)?X
+=<8XlqD{-5+mGVg6A67FU@+2awr~&i^p^XiBJrgokc-HV($zEsERiQc&}oU@3p=n*Q0b7LdDv{&#$Yk
+b}=wX-_D_}n+oE{?y%-gT#hV<X8TU2c>*WrnrgNjI^U3=1o;3Rl|1R~YBx5^kP?ly8O_ne<}wQSH#WL
+8`1ZhbNysk;02f|6NQGs|Mo3W59H;@gb*(r+HQZE3q#RzNjze?9ZlI5NqL50rWIq@?c%n4+!cL<s$m2
+v+6A=pX#CY-7)%F4-#!fVoIOE*^{?i;k!I<_hR4q&zEYCiE#LnwBE0XSvNBq<@6FKWbfycx$1E6iDCq
+PYnZCfmR95KTpNT&7~Or>ykaij4D_FISBdmNH|R3Am=X!PelJ~=b{rD@^2@@1Y$JtAAI<Fv7_c_LRV0
+BrxpZoy%D?`42Ait83M_J{wVi={&>KkUct!Oz^G#9O0{0hZ@6b$d%M>WVsF?3|bGvgGbuC0Rt-%x0nO
+ao`}o3nCUELM<DXUk#?c`P0>+p?Ck2LjH->#U`({U_FgS*)%3j7cc={JShk^=SFAq*L$9E$cTOX4Sh=
+%vhLWpgzgEl4PI>vcc%{%F7{Ay5PV~U5}YT;OLW(`$+2G}P%8}knt1y%+pY|@x{a|H;Qs?qO9KQH000
+080P~d=M?@=AP_Q2W0KRAd02KfL0B~t=FLiWjY;!MPUukY>bYEXCaCz-LYjfL1lHc_!Ci3|J+7u)?$!
+1F%*>P+or#i=SS@L-|wgQ8|kVFIm2mtEg+8=-Ym?r>AN;Y?OA5Mua5}4`f>FMW8_juk^D-p-@b+>M09
+E*HaS4}69Sz8tBPR9IwYfC*(tFn{VU6If96WKInrN0zaniSH0TP*T&p}#ifMb+w0b=BtAb<!>MgLb*@
+^1^)UlFocy&+4X1WotUOH|ArPH1ph!{5h}b>((5nSLbykuTxofc~!Q0ipM2DFNy>o9yLwX*v4tK0-S`
+MGY~_@T@|-oGX<nFn15VfELz(IXYPRyw*>sOInc09(u-sv+o@Q$vWfB8R5a_d1meW&GJ|KAdDE?dbmi
+q#*w1lU#fzevCB>AWT{Q?UlZAv|tE9Pro;c6x*2H2GU&=D88jS~_88@=6)(wzu&?*A*N2}^m8ocweUU
+#GIbc;9tcrtw7$ga78`3SSv@EzD@j?E7Ud+L){y2bZ5ootU!MAC}Wu7R${CtI>;<!EEmVk?ee(ST78#
+fP9-b&Zt4R0LVx)<tp?BP-$e8IXKk$64M4Ogi-wB<%&hKQ8C^tE`$;QskdyTqY|51YmOH@Lsn$Htb;O
+K2xi_jdR65GvJKjFwmgAECHPbwZ}z@Np{Mv?KPzl<3c7GvIBt<!7^$6M_!jzav^D|u(HU&m(rgMQ^D)
+fNStAd?H00&JK3!AGAYcA@F9rTqStL*f+!J>VWV{ka^`$P#x`G5To)^(JwRzvqlU>qgtBVgVTVQ{<Hc
+Q*q!QPQkdtMLifB0J=Wk#9$MJt1#YcZXJ9>S3eDeAfo1eaW^XBC3+0jeyxz`bArWOl^zm!c2Vj7%pZE
+c}`k_~W@QdAK%eMQg0m>43CC(!KspPx9;p?MfYK!<4d`3a&Bb+gaGWb5bSH-lC{J8E&YETF~N^S7gL7
+M~f6{+%??csO0?&>Q2i=%Myl_+xB=46-;WF#?o@cp%E^w}c*#k?)U;!`f9G(<r1&d03`!l*!q85e7K=
+s>;@d6nP6sfSI{WauOk!@$bQ>H~iLCCg*C7an;04lD9HM%?Dq2L{#0d#8Yvk4Z+z>4QlvN(kwtRKYDa
+=g`Xyjx1k&UfGS%zB^XIHKs*YWC$NbS1P8J5#}E6%tEpHi2vL?5R@>X#RU6@h2sHq_ZN*jIEkT~d(b3
+<JPtPJ~GMoT(((M_Xi|WZKp^0i(+Ag5CWkjZ3d~wJ&KH?z`T;bw5DWtUu3CD;C05$Hjq$On(g8hzQ)|
+Vgl&L?1ZKptYSYIzyQQGJ85u#*Op-gvwg+Yei@Lk4JDJVZYL?7K|Y9kP-;x$t=hi!%d9h9)@`NObk_7
+!2<<eRjsE6L2q_fdUvJfcZTTZ_%I$3HswkER$j`S_uk06JQy{x)xm}zEqFet?$VCg>6^WFrjO-*vZY|
+mwS7AQ*o7mwx8$MGCTZwZ*M}BV>=eY0!>A}06i{aiHNm_KO|rg6@Kg*MVlLY-#jI#8KI<ihYW%nE|W4
+VWTQE6<W)Fdm+4gC+D(zLpiSqMWyzCLywAZ0xB@=zmQtWSXmL3suz(_IB+xR+cB-<t0pF<-S4&yy8Bc
+&oa%kOEis)z&5Xx#Ql2RC_4fRF0Y^tj=65_oS;A^Ezcmht^Qa0#KnVt%e8c`&g0T5s&mQ^k1>jGY&HO
+|YmG!g`?3*576jSM|Nrp4Ka4!B07>jsQy2VD`mK<HbRv(Zy!;ewB-8Kn4ka$lp<`1tM7^Ou&#9^lx(M
+;$T^!rCRj&K1{m2@pc2xCZk6?eW>s#DN0NBoVx7@&(#*f$K!nBDl2#>d^aIHsqU{zSz*w1eV`fc8n9&
+mvGn1>M5olbMzr`IMqm8CIwIz#Ul|5cv~tC0#!tQBKnBzydb{mRi>#4F+rQdQkaFneqaT({y;w`1sxL
+V2N0}Sj3yckY6WHOiDN;_h{Hqi#1(deOE5AJ-HcTd=rQ8qo3@iHS;BL`H=K~i0y9gsvFd}MrUnGiD_I
+xaE&Xw-*bf%C=~`q%oDp;dT7YCcHWO}TJsDsfL;*CxFCQROaC=S;&RrEo(G{<s^@>@2fS-F9fIhwm>_
+YyKFTnH&ln7i+RMeuH!x};lnRXSZKUgk=L~0M3@Z!R2EyiJz;gkhJp3>h>V5}~k=mjkS|9z#WfkL=M@
+B}_{l(R&c)03CidE2#!FHF}ocj!@y#~RuQMCb(eIV>kGNqWE(bs#igr&<jO?s5zM`xFF-B0DuEaWu;r
+C^<kA0=2xUN~C`nydhTw?Br?u{KXk~Cuqk4PzvbfhZD~rI#76oQCr8YT=W6aA#!#Sn-B-;KW{H+$y5s
+%8Ug_v;8LP?MjmvQR0|@E+tvU_wt4}1iT3;EeL#uVC-L#?H}B2{X54p$!hx*{URNB6g1rRC<hlmdlbH
+zr&4mi~wwwG-{I-@YLOBo*+dw=Vz*3X-p1X>V=LYOT18VAxw$L9V^!e&=0%89Z7|4lB2h$uK+)Zm+gU
+i^BO#2@Yr&|c&!)8JRKu}=3Q4n!?3_Z-k9Ee~DjL0EE-3S;F<BMOb9Mpy4jL8P7_%?v4K)k1c0lTFII
+7=diP&%w;zXC#*&yGd160@AbE3ncf`%T>vw89t2L(~r0HIC(P;83@+m;<}Q+!|j*@C6!wdNqpBS+kaR
+qfLI4J8UU84y(E#&9OnGaO7q#jH0^>#$edu&S3{#7I%(9ByfA&IY_?;7^X(`88)WP5?F8e1tLJ}Jwg8
+imJ=3+R$j+*)<**8B*T~@0hg~Kzno+NxEqZmsH%b>0od5?TQn?VwEqSQCTxNaza%@K&jWPuqGPS&{9T
+(6|58}%P|XDywd+|J>;MdGs3lgzayE9t8SY3aNWgpaRXi2@;J1rM^lM0)htdB0r|(J5xcx3QjP}$A6>
+Ak9yv@+^iy^F05Swrr=^q&go?mB%q_)_LKImY2u*21J_}*GhLSlWwx1W#?Tu%X=YzXMk8AwoDuWN|x$
+fq{WT|Q^+rp$fb_J$gW20kEAF@UmOA*nU?Fatf3b+jya96Z$hH%P`%95qe!re3)0R)r-7{bq`Q6Rb#r
+ni|`Rqy}HSX~}P5mp#YWlV755RSFE1KnSZgN4j}n6(S*WGJr{!AQs<tc~{7%AAb4v{Lx!^nUlZsF?)O
+fTuzu_u+!6nw)Yzp0T9pGjR>C|e4Bju<>~pOXHO^3PS-QNKgMvB+!<kUw=qNf=Exp74-jjV<^_7ZvtW
+X1U|tU>J+C7Se!z$--#b`$^PRs2da%L1sLK_~2}ak>#xbrlUa*A@cER`3pFe)I3;{1tAyF3$3`2VDJt
+8=BSSo@dzI7iUs0l<~BSAL*5H#fhjB_))_T&}<{HfS?<lM#O){fB5={X|bN+JS9)ZV@_5wGnFwPj%tf
+5pbo3_5d&Em{l;6d0x#E;sxBc_gE$WA9NVpymhf1r)JW4e%_%?SXU&zNU!`K=ZF4UaK29&#z%EUqe6R
+b<;?SDvm$JjgeK`4tZjnZ(Oee*M)oC;w<Qgv>-lSFeOCH@7zb7KlotO%P$W7)^}jx8Esm|;Q@eBeegB
+zs-9x{30i~x)naMl?kVoYG}7$a&`XAc{GX#)R|wcZok!R*7(adbbPSzfcilF-?J{ZPZd<J+@xpGhL%P
+5Ajwhlh8kHaiLQ;|=vqRzYZrh~0FEt}M!FUiwyZry?i2oZXFQMNqp7PxT$vZ~-J?OefdW3Ta?!$JsS7
+NjNhr@l^friyBK)7yifPNTl&D^q8!whwwPKvqhNI;RMYqiQ}E$?ocC@twea)va$`RniK&*86w0g8(UR
+ND{!s<xio7Vya5(&5KZUDe^98++O=?mOJWH28l2o(j&vsl<&Sx5q^75UG}r#RqS#&$Tghp&_{7eCQ|A
+c9TO1S2H<}*$=Y-hd#Smf6~}haVg{WW>w_nMSF-UuBW_SsJ+sJc?&ot2Q*XA2xXoo;Gy7F;En;GO-=5
+CP`*sGHDGs#+aQ*P#X^yF*qFn354XVvoCW|+OksI?#1i+~dm=DKMYF%jEgWx1UUxk&{8fHox5{*zshH
+vJ9cuk*J{NeR2z0A70Z^`K?+*n>=U+tr044^4wmM{rWSQ|cr8=avQ)FP+_`}Xu2j{%GX5Qj+{rX_%D{
+ptwAfwM8_?$Yd3RuZfUJa+JTTFPGd`Qu9M+!93rfORVY%lXvy1V(utGd7q-HtZH#jI)?m{`WC1}@-;I
+xNA1m;x2^!dX042#A-a5H=%mr<VA^4QJF=A~tf!<sQh<E9Ovjz=J0lcK%y;Ap<SPk+I`>Cvge^J~44n
+uxsg_3--3H@OaDwLzL~)V@M|wGO;21ZG2V<?i+59Yo86?UdaH@h&UaF+(IV<M#=86Zl*kHx5<>eRQ6E
+uv<cI7@{9tUZNf0x_7#9~05>Jzgm(vpBg$;zZsYgf+X<UUDg(BN4GHnv?Ih$4*_V0Cr&-9qcIU-pH*o
+%yI+r3(T+>`_%s6D5DAGPJu9BP9478EVJuRvz;Y}gdNl-i?f39VWIu>)E{@8TAc>-<-jnb<k4-k?*Rv
+Ndr45KLaRyFRQ$n;>C1g^pCyiBh@Ws1B%2Mtgwv=ML-u=YHsG?lo9=nCS8BA;n})mxb!;)bP1s6g*-W
+ng!110Dt?LK}R&Ngub=!pshLfmKUA&1Y6QpPjrsIS}&%-DNs_$|UC~p;FNON(qV`UsiMRLsNa0<y-mN
+8k}s@>g*S(yfmHgGTy3@zX5v;-bL6uMx<#fQ}N;4V-V;TX;hq?2b)~v&bJLoFk(C>CckFvK;HjpFY@}
+($u7LRM_#7II+JDz+Ix;<+B13-YTn(8nBR67=kG)mKm}YO%gZBrtF@Z_(<e)*Pqc+dG7_p2RIu}OlCL
+$6leWpxDfLFj5aZaPq9Y?SuFO7tLXyLnNwZiZ2hjc{jDe?O$fqz1a(;HuAHxg*Qn}<N=)+D<y7-$=*w
+QYan;~jvX^75~WTK0#Dua&p{xF$VH;Kmj^(kZsv$?NXvW?w5dLo^Wir9@^5+H@$&y5C#)UKR0n*6Ne-
+5Af%jX1|BM7Bei^$!aB=M}V;J*M}e>Bn~i9LMHf{n0=U_20zc=&#xM0Q~OOVaA5I9qW4l^@wR0#5Scz
+-tt-7y<v3I<?b=ngAbfJMW)<KQI4*v1`|Z$R(2kwyvHbmE$%G3kT=?OLBSrh=r8TY20j%B&D`mFFV1!
+T-eM7B)My=#ab>CZR*4q)C4!zi*pm~FklR7BDTq*qc~?1V4cvZI(s+h@ctMNlXDLK5O<pO1*q@B~aT?
+!_6FG1@o9Km})VtH8xAE!m*%4)Om45a*b$l{mcxAQ2iRt%)>;w5y0lAUwAA{ncXK~gYL)NH8Rb605HG
+uYbl!*QS^nS2Uq0)md2^>WZ>mtJ=nkvg^TcLG!NQuVi!{`M9*iVf8A?;}v1T-yf=v5iG-w>Ppx^6qo_
+q^BIw0xdFuORyH-8Y;RZR(ianfnAP&}^)1AnZva0gv!Js?2&`osItnXXy9orI0ZGu@7MY#rn}B8f7z_
++hE*<Df@>N2MwB~9NPg4OY7P@Qsin46yZqOe>!!>t+l{NYvAJEJ8M*M$MvFm>3Y<@Z|<v&A0=mHAyPY
+fYK8$hJtX)2?g%=S;Lhl<kQ%5qrfOxXqkd)o$=v~HnY@%mRqq&AjzCy3Wa;ogajGVuNa~zU`-L3_1@4
+Ni$W>BO2INIqT{#C4Jq6hId=9NyF}rK1WR3Y%uLhC4e&L!T4}Hh4_i7}<;UQTfx^)dWhSyVZGeLg}jF
+`l%(gn@rD$f9~>j`hjz=NgC7t0QwFq#mRFB8nZ0@JE9d-Ryl8B=P_?iKC0RB2blKfXVHefo1acMa`ee
+@8_>^R(=Wu@^K<)sDRa&RM6v<xLwtYK7+!7k!LmE-iZ%C-g%IM1(kCJk{eyXU8Wmcp_5~>_7SPtH9SJ
+{oSZh*)eB>YS9tRLlN#x#ooBEc^%ZVl>J<qqDSC8Jl^#m?GGP4F)T;vFU>7$GS67G&=EQ2_!2QP+{c7
+(C-K{tC$C@qV=|<Cf8D(n2&kx9>Ae^un#pV*!Ig#y`w4unj$a>Pk<F8@8Ab2qi<4LHexe$jzwY_lD45
+umHXruSp_lr+cdqvgRCpGzSD2{iqz@~_ro%USkLN5Yp7>tkPCT7IlWbL(|ACHCCy{5t<tc8Aq)<m9G>
+@SUOw4!u%flBRDC}&tlas?>mDe1iq)wJ&rxfq7r#VO!AtghQXZJ}%`ZUooYKHlWC2Px18vLgS_u^PuI
+fWiKGchw9hNNwB%sCgFm29I;)GbUb-tViCsoIu;IRHT5)NwbM%S)L}jIO8j`9?0*Mba?ZE4i9URa63y
+Zv<CWFccRu+1{yGG}XGEqELX-UVx)a83Nmsy)GVKZ)4_5kz*p*9GD$EE35`ZSZZ1kh-4=j5wy%#x{km
+Z8<k{<R&L$m;_KviXL37L-6D4EP1C*@MAd2Q`&CX!jr9fb1=fnz$T1_SD_a9e#$`U#^`+6zdHn-&3&E
+g`=;{id{pBm>5gQV#T#3U$0tWPqa6sHN2`6kT6rY*iNE*ywN0Wi=@iVvgMO>ZV;_FCH#E+1YE@A9B^@
+bc7UrAxw^q?PxUX1h5t)m{poC^XCN?-`x^R67>Dt$~aM|*hE+;4@4m!)$sg^4;qE$<oT>J0IIAfDTej
+lo-oTlzx|{o`H-_%j)j$+8$sHn^Gv!!`qxVLL}uA`;>Pj=)Bbq1fD)4aBe?{!pcg;u%bcZ_wevq8ah@
+so4L^Xdfz!p^kj+?Pw5u;0mv+H0B%9rKpmn*(GJNON!*#XghaMT@-Wr_UOkDpR(krx|_Xfe}8|R96TV
+NzL6SctVwNYl&PSrhtMscLaps4tcp-cD?LU^$x%ofp_qyv<c)Bz`sSN&#7^H<ihuIFl(ii!I_V&@*4m
+)yg542=HZWmyPW&&KFaiWt>`$^_1TInMQU6CR*#89+rs~w({ATyCe(_mp#ar57^|7NRm0Adz19+}N^j
+k;qD}+ZOVZHQyKn=1B3<%xbRlusm_$p~|lf(~9f)`0iJGJU83B%D9@)70BQdQqv(FJQqrUoe+$xKEj@
+`&+3aXHjCPeeWUfppT)eZbo5P9CT={`T-m^ffHBY;nh`l^_;6AWmjflbBr7nLe;IA=fdBtnT4heDEm2
+azUO3O_{$_xQ`X+vQvT+4$Q0D^9z_hprkgJV#dtweVp6Qw~x^ajL<7^h8Y@ojO#`Q9%^`9)cLP^FJSZ
+gzDqBQ=ML}15{?tvM;wrTjd#z<a3Qoet1$>0{RO0V4AvYs7h;AI@-Hsq9l8Qap_+;;DhIz#$H6}kr_~
+B&scF_V-Mympz2@M$sj_t{X|HJmgs`n;n$L5}R{-MxCS4_3jr%FLiMjg|9P<vAMhfIwRlE^1K5Y4v{W
+2-?j0(m!AA35&Y?+g6&v>74#79|j+=sly|6@fHsWl~o&PZ)s%J4tsjjD9_i{K~!zqbFQYl}aQY>(#34
+#A;AWn%=Dwg3B~1eHFPR`m@|ubTZDVo=JEe`nOdoi;`ex4e3wmxDa1TVjEGc>Puf-tE}`NlyKPKjy{v
+YWDxA505h5_u$PXJEMD2sv0Nm=r$AQpkXAT+hnSm${p=JhAtSgw&eHnAiH>tO~2M<v-{>>)-ouP(OV~
+Z8>^2N9mmEwTXCb~`eJJ6;kQ-$b?5>bd>uEFujSFLo${O8$#K227^8@G1Yu(8qn+6Osn@$CsDJg2Pom
+p`yG^sfaLaw*rhNsN0KAKZU%~Lu+pT`vPzdH?_W<wd(8(=i2vUsX*st}Bhq+7a5(1vql%ScbzswWU!#
+)ZF!09ojrX%Y|>!z6KBZ@vHT#Nfa{BZpDpN<Yx_VBaei&e77Q_<!N2y#J{x3-D0Rw)F4XyL{q4#vS)s
+Sp%NSybS)s&wOrtr6WFTm>{&lfU(fxP#P&FKMT1rBumk&$!*G8%81;uEHPOgLhX9<>P@TS6parj?nB#
+I;XpL(N_pOP_OmjW{pzXpd%hOLHO+8<NL?BsVcVNbM$DkO=?XF{su42hmXI&Uz2Bp%J(KXy#d*{hK4k
+uf4*nWVsfB>D5+;Gxa1g%gcTxKE;m&#jfOP0*wFwgVV|08G2f_~WuT-kUncWE;ZlD*Z=KI2oo%QK26U
+!aWk5ir^oH%sms`#-3cF_e<0Je}@O@1@2*!SfOfK5$xW&JdElgM06#RthN?pUo>4JFq${`+(jr%^tCU
+I59j1QjZ-jZgBg>4{I5Kdjn;!xkL_VVcacRx<eOR&=EEhCbf8>{msYG<pmT@b@UJeZ&@i?IP5R}WIQ8
+#d`3md-^WRJkQyPT7dyW9fh2_P%Zh+xqL!7okRAF}5GJ+b9+IJyI9HgaBxY#hu$dzs{hoa~JT7ZQ{-O
+5V7U>({!_!-q^LZsPT8Vn$p*X=Dp>V=*{sP&Ztr94t|B+(oV(pTa~k~o`V|L_Lo?x-tv;25Pg7%9%^X
+M>fOb5(lN@*XkBMXCqu8r$fgS7+{2!{=st<*>Q31-hfd4WvzI6D&gf0ftTt8jIOJbGhoYpwpDChuh*|
+VJ!@HvATW!WR3pAJst}A1obwF5*O+4yuRO$2$CEqAGn@ptETHV*+3l9&EqV2Oaso%riJ*Xj4Fk*0Ynu
+M*w$zU4<hrw4vNtr>fi?M<5SJbF0R=fswQd&@`%VUyQzo~ciAhz;O>MfwYRyS8vPQ3@fHdNCKbH+7TW
+ARz%#y>v2d$34}1iT0Y18;$|B0!I<cJ(r$MzrrheT~hgM+ZFM8IP=k7ZnypbyC|ya!@Sm;hh5GY^Q&Q
+LEznkDh*~apNPlfEkBk!_xu@Nf#YL#FMmgqgl3OQT+EXkli2A<K`Psuky{5w09>Cz_lF|%2-aU;GPsV
+U40nU>+7a;)TqfBlV#AfX8!f-j<UVS{xG8-vZ)B0JOPFLaMjL+oGPquZaza#`fu80g-SUx6w&XNXzCy
+yDUYS0$YILy#-%f~iSU@Y8>2oN3K^s8}WweNZ4)y+%ub<Fi6>f&9>kT0$0LQnK?S%8#kcG<qDd)_gBi
+w+N;1ipdl7WhZE>#hHMJOY~-8eI}#CsAHUt(@4E@C9qHrG`JI*`DnR3wSdv|h0g0gAe1g^^t(%?$4qv
+AJcv47z*Y)1k5j%sm}j%9``kxbT>-?3k*Hzj2e_5ZK&&$HgKr2+v%pL3GKW2~y91yd`@|A%nm4eId|o
+P__6D^fJ67`pc05#=(vq8zHusZA+Ij;=QbN(2nOFe31?~=dXC#z`?>KVcJ~6Om(T!cXciy^iwPzQ_cQ
+tofjEJ0_Kxqa<>T7zT+j@aL>2fPT#-v1Hhmhl>QxD@0+A{s&UW+w(8amRXRwVU`;p55n@CDUQnsoo1|
+%JE3d9^yj;XM&dc0hOhv$*;Ggf!T1X1&3R>#|$L4nS<|#9H@8+q&@{ewM>RJq}S~es0&QX{xUIUkpaD
+xNH^p6PKfU!=ysy^=2MVh18g?Pt`Lmp_m@kT8(m|4Zs4oI=WC97o<>f6qtCw2Xf8gWXw8H0+|TIv_31
+(pXi`9eK#hJ;s<^9DU}`>l@{G}dx{OGH~rnYGF!L|S<HY#`^HUMH;m4y+8!sk<?V8^v%7aU2}Y2tcgw
+?7u${sgGdKK;1Pe+*Oy1v$Y8sKs;($68&!(2nB(!ESuek-c{wh6dLTZ56Hm>YI`U5P^j=uB|hIEZHU&
+790VSzoCI@~y*6MVaV7g)ukzV$yRLE9YgY>x-j}74vR|mp0W93|69W{rR1^nF7!!D~?070%5^S+(j=!
+DyJQ%s`<-nhe#9ueL1eJLEu!F7c&3L$@nSBgzNu0#d7yO1Vx@G^h;=5mU6s$)i@(%!^(d);{Lf_9AEM
+-wtHn1SpWHWK}+Zy6HB?B;Zy6u)Od8IxSj{IrstEIEY-hsMH-x`AyJQR4ZMQ&gdo(uhonHVqdE9o-i6
+#%6CoRkh~xGWIAsO0t1Fu5j1-qGcZD$@qNA6VkXxp6rHHwY}4PKP(o@oEj(wdS&BA~$0dnbkU_@__@f
+?7F%=*xg;gFl!Y0)$Xk7x<b}B>cj4=sAju+(f)q)wR!|Zeei1ko39NKH`y(`2zg+(;;c&PADGY_&G%m
+i>V*?$QhR)hZW8?RFJW}<ZJhl&McwKb?7-9Uo1}2-9INmt+ejoSZdB-vV{(XTt(pE)a>(hGG3;a33EP
+_Id^PhSHpl7aDWLknOeWI*_`x}Ds_3W4Oa1Gj8PI4cwBDbmk``Y9>s)VJAFOx$D^&tR=T^yOmGcE-Zh
+0WHxUCz10g9u;+pQW~$IZNbC4-{iGB8e{#akgrIpF1gT~!sWcHOWV+%H0OtHNt^f}DwH`}kg(8WzIU<
+fmhW4ND?lcrM9e(k<6j$#oQ1LMN{jAXV{5S?w~xi_P<uy4Juch8*Ddp5L9y&f4rDZM_<NzUpM%xI;3}
+=2Y(K496GU0c{NI7fYF5aQ3~rDncx9E2Z2t8c-dWQ(f`_uQEMfOE_IK&;_l%uE$|8s?9M6Ld+f%FY{j
+Je*;iU0|XQR000O8^OY7y^O&Vq2nqlI)gu4^6aWAKaA|Nab#!TLb1z|VaAaw6b1ras?OI!N+Da0B=U2
+=sTLrkVaGaAH6?==qcI-O7t#RUP1(z~N1I)3|=x9a(c5BaX-|iV*L0p{8R-K2X`~Ya?>&tY1-96Z{tl
+IsAOX3QTkr|al(I*y=Q6#2hqEsk5?e>_fNj$V&F>QBU#lN$@qKdU?7<xRCga#g&3XhLC^J+>E8FR&mh
+$9llQ79yncFh7g8q;w`v5F(+ID}6_5h+54QuwiAj`>`xCEp8~zLN`CGrL0`q8cGp>F$S%>yw*@eyvvX
+*oefEMUG3|33EWT251=2DQk9W1YQv;fp6{@XaF|GzE1#ZLO^LXVS4wz44Dh2xJ1JcfFUCh!E<mD;Q8F
+8ii^OmX()VIi5QE(62!p|M2sqy-!DS=!q<{=0nAk*;tcR3*2!S6s7r48<Vy$M|IxnW!-z(Ub`Lkn_J?
+j5UW1t9Z26~v&pXNcKiaki&zF(Lmy?eKTts7EHM30*Jn+O!Ci{Aw!6#5m4nClMXvOgW08Xf)gG&K=4>
+G)g*25Te6QnrWdJ*^wh=K8#Nwl2sK!1JJKe;BJaHa7QoS=`8L^7p#FqUL2G+MI!Td#j{a_L;&^tzW$_
+xARrd*A(*Ics4P9kux9u|R33np4yufe7{^L8kzWr$9U~K_6uUu@g6tNXG&5$Z$cv>CnM-7&+gXHUU$m
+22<wHz^X7->8T;06jL^vu!xbpIPh4sr!^l#f&^L^KC($4R%skIr_<19GCyM5wg*?@pWOH9;0#O~iTU6
+*5+e?)1|O1lMSmAJcHJo(^y834ps9I2cxfLbXL5USeQ|T$F>V^gfve8~e8B=w5=$?)(!j5F_IK*X%+_
+#e=I~LPaJdmL$Va9$%#cc^6DoVAv+D>_*~WKvAR^4&Ny8a4<;-~CjD!!4YgiQuSj}dwAf)Sgnp5yNb%
+G)7^hznTKzq1aC;d0Ug9g59ktAbVsUu8AFdys#vav&-DoFY%IXWViM<dYG`XPhaJOni{v>MiO4qKbed
+!{L=1(H2iQ8)?~d~aAl5Kgb#J3n>KZ>~<Q)*2%Wvurz@XKQa5+7Po=v_R--RyML@OLF_@jDo=ly+4oL
+Ge-C+<W35&ixIgN0Vw%fERFXeq!gPL!W5uV?#cIBiBT4542AF+4O$sf89RY{0y1PsndCl-wU@yvEhAy
+zk|qTPOwG;N69{+;G7>IEFQ9`YL6)dQ3z(XT=n19Aq27?<hY4l1j3ae%(*kF}Y@l!nMlJ%k?x7f8jCl
+b3$jS>oVA}y*hhpvoWZF~iMnZ~_(hUd;_G8)hlT32mw{PD3^6q`(p>MxDXxdbU^Wzd4>WP+1H<l795R
+Q&Z8DmqjT9Qj{+E|f`uROE6c~B6bX6#V5l<G2$t!rXiPC#y<coWPqFoXxSifyIWUonl5K6Mtikg*|%I
+7C{p=#wwEq!9>}9$^#Jf%1PM+;|H0)HHSdq%;@%0W3XRm4G!xPv{0>)eWg0MIFeldgy*+S7kDQ9kqH(
+D!sFFceDalVd8qQJF~a|r}54p05Eb|WX2$@eP7IU=3`@rw5DApP0%fcfpJov^DXRnQf{%<g`Ks%_PZh
+gjyuYk22@CDd0NFWZY#_C#_F)OW6+YD&6WDMQw6V*Sc;>H(pTS{<@IK(gpii1?8~YGy^9`6d9IfZw7#
+}F7fupt^j4iVm0jTHiYe-OWfn00998xwjbE2y{QTjojb1PeuIB`koq4YXsa45h)x>S&JcA^&Cu`|)BV
+P~D=4{(%E)~t^#oY%+jq(%cMGZ9;JI{-aYoS<&T-2wqMZshS(c+~QH66_`<Yp)fM>}X=jFu3Dx~oZ=2
+{=uO$aZtLSqm-SJZfneJ#J|csdw;LGZc_&6?1IqegY$KJ6~53PGyS?#A@{ZuX%Mdm7<LLDvNI;$u;*p
+E#s!mv<8)9#K+b11a{Rq&+T|mr1Ab7ESE{zFiU6~i=%`dF3>h?d6Ideq(vH!KRi!VqWyoKST|2deZ3*
+e!|yKBg9*io`h=BZuVPcRq#tZIdpFl-7aun@oOF~noXeW-&}$|<u;oS|?X-V@wyKG6BO?`?vx52S+cR
+S~-kz+2J?C!ZMZz_#7>W@a)5yaNl3cL@(Mer9kzOS1l#H3ASi0K}7a?tjJZzhIZoho{>eaF4&1JaNmN
+218D2~t{U`~WkZ3D%VMA+92saN1i`9Hf=&qQoby4~jXc4g|dnR2nCD~A5b#a;g^e=Ka(Rx&75NqJv0!
+7qb6v*qgoEt*EGXvl_cZ>htp?Cu(8l>2{y*{y?j(&LCuahE<0N+#U%7;uUyM|*iGsFVoGXk@$e%L93v
+NIKVGz!9+`vj}314t?wklSP$~Wv!NNc}5Sk?M}LPb9HrdUA&Yo0_ku+B40P8@8fzZsv9b*ceZ2JH^pj
+wRa*HTSV5!U3-N^906twCZgX#3qK$PVhnqlX9h0C9o9ek&z|19-@&OhtTsc{gCRu1K{#rqy^So7jY_9
+CaT$`hPOA6Q$Jf_l46a<vFK4zcFZd=y&v3TQ6{TW?I?NpSg*}x<LOfyhXXUz9kPSrNud_()KzDn%4Mw
+9#ge%2<XB{rQaBs?_T>{7rw_zS56H2^@OMZ=Zc^PP&pJ^kT~k`eX&A$6aKZjJcwz)w)HhcFr0dQQTAP
+{t9%GmRz2PTCo!QgRX>$26iaS21iRG+(<SLU#aD$r&@fF~}n(tM)VuYA2T}zxi1g?E)djLvjMnOs&*q
+;E4PyFBIkbrVg=^5!7leuUX1vSlScQDdf@G9px*l4h}_FY0B&W{WW)X5+qM-0ZJxT*f((Mzp_Psx060
+N|Mm1APVdv*-Rbqc^QnJ&mn#riedtkVP`d_KX~#N9f@08%!MB0>43rNcF*u!vzKB>f=%c5gbwf`{<4_
+ap89%p(qZT16DEIf6MT^f^olfTs(7Uzj?`APjbE>Zn!x?lGj*CL%Y7m+)(rd#Fhj{pA`{})Uz4g2f->
+p5OVLSpAjH0$1F$z6S9B}+c2nU{xfNowM9R6Z#PnO}0fbZMdL9Y(&{`p6yjJIzgeIFWTSSuLWO6Io%t
+Zt(u<6n>yF$<t}d3x-U6O4FWmUM7KwoT}F1~qm(8FEEj=$&Lf*SyFknqfSaHr4-;A@M&lbQcF`Gthr*
+yJP-0?j5~*eenMMu}QKCl@6jh2nojP$*-1Dr;9`eTILK0nuy42xJ`0Q4qv}6Q{yri&XuJFL-6TST82x
+jb083Rxcez2SmncbVleySX~0?Z(Uo`<*b6)~0KnFLm-hZ)i)g5_872(yafyzzt+m>669K$eaFO9Fqf(
+yV<($B5Tc0J#4V245MJ_blX%vZqleVLksK53fP)h>@6aWAK2mtey7DvqtO)dZe001Bb000sI003}la4
+&UqX>4;ZVs&Y3WG--drBuOg+b|5h`zyHgFb8n`0mHJxu*)tz73firZK`1_OM)b)$%_8_DA`WzpzUEZ0
+z?wY$Hym9%G(}<P(vI%3IWtR>m$e}SUp4(<Gv`Sa~o<W<9^;o?42TwMWGUoBUcZRbeLg$A3d_QXtfLh
+eh#F&U*xfk)}Kn-Jn?G#ttf!M4iyt2Ls5t_pjH(a+2iic8m!o=F^(G!fa$w|-wIxke;&cu2#<6?1JVE
+<ou-x|baT3jkGprB7jT!boDZyvWK9Ke&z>*%RqMo44w`GXqZ66O`iwv-lLiWqsOFRt)O2(qKNg*BJGl
+1)79YW^V{YI9!K2jaCSaecX|R<;KnRwD!YS~*1lY?1CgS8ToO{Q5T%m<iugN@Pmr9Z>3`1UIs{!u!%p
+;=dLh@*>Q7KoGF7q0y_2goxm`GB(Wi&&r)buAafy}r$LG;Yc2bK<;W#mXwzt1Y<0_&AAOS7AMv7V(U=
+WeX~=Ps>F=TXsdx8c6L&2(=!+wE1J|8AfwyjAGGHPO@opY_cry^}_#o+L_tYr{EcTr5Y=qw~|~Ek?{E
+zYGR8-(TP0^^1S|v8k2yy^Pth79x+f5WGs+gs3hIwo7=vW&9Zb9J2~JE*=9swiO(t9{4qg%>p5Zwi=}
+wT)9X&A6+G6Wfw}UWgXwEa#6rvP)h>@6aWAK2mtey7Dv9ThG^dc003wR000;O003}la4&UqX>4;ZWMy
+!2Wn*D<X>V>WaCyB|O>5gg5WVYH3@QN`TpxOBLtO|>4uOyaH_$^VVr7k_EvsFzv$E@d-`SPqSh7rdC=
+~{4cV^za`BWUo(GAolZxw>8gacVOsz&VyoH0u3lK%m<$XEdd9bhl4=4%3xnbtv$jm52S(r8*mG2<21)
+R3k{>spIx>Jg1~tWHL?4%6-&Mg6@pWyyHMae$AaXsZMfJj>?x)yMZvob>;T>j?O1Sn+7E!P@LFzt?Q}
+anPSdS;8~woL|mGZvlV$H^8@d&zrliH!t^3kKbSoKjyeEXC41!>v!;dD8}daU*Mt3B;0F<r43G5DIGG
+JQf2o2fqgw1jV#WwfKy3QBatX^2}~PHV-HIxq~Z>!`HmX`wdrow9-+GmuYF{)2vU#)+PL7R6ZFD)KC{
+~XnN?3lD*pLv*4=420$79-DjGADL~RyIpS+^Z)_XDL%ymI%2|)lgxRpYw19&QV=ytTQujICZy;KS+p>
+s|b>wqbE25|y)GGIfL1ckBC$YwRt?XlOxNUchA58*C}z4Ga(#*1j;=cVEr@ULlYV+fgXOJV#gge&i`7
+S1tzg_et}$)U(SV};K7`5mKqtDeTNUiv`2L~@6+7Ztdyj;=Fi=hL>I=3p;0Zzktgk!Tte76kbUk*Se8
+2Hc_H+&T}#t4lJ$K2yLe>MTj=AY4OyGJw}`MKOokOK0-q2Q4f~!5+@LfVzTa2(}wojx&V2LwlH@Y5eW
+{J^8Y77JYqu*MvyfhO@xIGE{BYm&tb0w8ZhA=f)?@JGt9zo;JsvE8L;-ZPsV};y$7YoDYp$*O)tQOJ2
+~t_8oi}*!m9xlm=Q(wDQ=8VAF>@L1V%}V=FZLY8fWO<AxvC<<h*pIc%?;zd~z`O-?Gj7-^0Nd}2FnmR
+aj_%m1^My7labjn~8AGVK^AeI80>GNI-PMZ-qExOB%0_tLlGSh7>>!Dvp%HX|N?08mQ<1QY-O00;o{l
+@>?xL+!w%0RRAl0{{RQ0001RX>c!fbZKmJFJ@_MWpjCRbY*QWaCvP~!EW0y488j+-r7S`ATxFz;M1^S
+!+`bB0Xqh&Ovl=6(V(Qd`TNnb(ll^&Fl6$*NAihooLC|_RPKDMClmXs>V!v3ZmKPh8bQe)DIONUA-T4
+ys+Ky~p6tIh$g?NeE!X?c74+QVM~9ivvcHSy%`?GZHYY@0d5ArMx{hpgnp9PAr$HAm30n$bzd(FZo9S
+*C@Hd4B6G9qEliHkC(9&6}b)s4<r#e$y2hU68U}BUVtK5ywKw9Xg^tc?Mn8f3^B&u?75_)!=0vh~cPd
+PUCZXhhv;Y0I^xQJ-2VqZPbiwiNlwc>nk@g)zg=q#m^&f=nZQthV`Y!V?KkpfA5MO@`e^SgUA4L)5<u
+kw!*S!V{F*|&r9M|3O@=2a8UgQeArK0$`i;`Br^oZb<$CXhDRlTfCI<%^J7#_VFxDdOXIbb~q88+Me_
+w!ds{Ss*p{?LMt;Z5Z@oX^MDw{3^oozG8X5Z)OOz0hHvWsv36%B4urL3r#&Yc3H&S-hTQ2b9nf9>2u3
+Iuf~3T;RdhFyd1is-i%bn{SQz}0|XQR000O8^OY7yVN$wcvjhMD{tN&B6aWAKaA|Nab#!TLb1!UfXJ=
+_{XD)DgjaFN4+cp$_*RLR)hf1wS6y4h}5AzatGvGRa9UvHrK%gbc=0=eMNhNWA{SGgZC|g;p7t7?ieC
+K@UqDsjc<$2YbR?$31tZt+-h#a)!t)aR9pCsYDkiyVc!`Y!b;qrK7;&|~FpNJ?qRk}NuI!TN=|B@hh!
+kIZKN=mCtUkln8w07q!rKB1OmTg_1^IjYg9qA^O4WVHTE@a&h(<wc!w|DoC`{BAZjO*@}3T*=}nTl}r
+qWLowrBtYonoC7$lH8z;G#beYm8`0$q5>>gxEd8ifLu+z5p!aCj+iKT>k#kH+R)lXh9EOwD2f$#@vqz
+ulfuL4+@b~AN<ot($sxVDHpEak>r;TaEC^4t=o&Lo0ytugf#^rj{e9pLhu4}2a-=FLX@$IkwUCOoUVO
+?thS_Z9ewQ8fC@7(q6Dv;Wg>eqCx0(VTYUxL^HBAfbN_3#XEG<#xB0oE0R;s32gB^`%&FnoBnw21L@W
+}5QA$?dNK<OlU_Uv<Jl$V}MCJ5)z)f<+6@|F^pgd;V1beV=EC<@&n>@6`yrGuq8dKZbmo1>2zSSmQt8
+m-+0(DPZU8uX}2yXNRmz`k=}eOK-<)s$Cr@K{JyW&`>zANIM0z_d#@fDk27Wl#o-b98{*J61e01j4pp
+J9fIf_tp+)U`(N+Rj!=l+;I9#IiAGOIV_>NuEN4ou}rGYX80e;MEbSAwNE-jGxYwNjSPK2oIxkV!nUy
+NAs`=2W8H9O?6FQCR7(f%Ps3}VwO0y7kh{LiUleK5_wHQ>BJ8a_ZfKb@(M0qFQ(ZWmX_E$3!2A`2=Y2
+cly*YD=cdPyCAzyAb0Z}j!?Du7KYqi)nTJ8<Bd;OQ6t9>brPP=ltL3dD%21<eP615E?$Re_3;6Wh<fl
+yRBsS|mo!-#WZx9a~7A>Q6^?st3OY|PaSdVpDn1f`|!dmaJdFe2P2Jh9`6>V7l$qOy`)zQ8#iGvCFv+
+U@Rl^AQ(){-Dlm`tGu9KCS$?+-=v}KmSkt@6~2={}@|%a&MGtIu-NN(5N{SB{O**%3C`I7Is2M1u@AF
+o>a|}UJDW*)|TB86C~Kj>SC`7$ojCBFhC#VBNIP=N+XeM0IpB6<)zPw6N&J4B~yfQvl~ntheAO@PE|?
+(Z7-+6hN}KdAg@W(E`;ZO2<e;Q1=CE!c7Rb=&^WUBWbE0<n58qeh`aXc1ft}_POidK_0Bc)jWK7N0G~
+SV5O!1?`-y0`6{48tXvXZ}d6qdIqbuw$(rhwKc2r#e0_QI6@OiP^Z`TOMYyBNWQe!HfnUVs-2u)`XtK
+AnsvVd{B-0s)2A9W4u+Eszi;^4DqJNA1`^!Jxqt&h?A7`Mvg3W=liti~mMkyE!A?68@4XoXkuT|E{KM
+!h5K)PzL8Exd(m*#W-y4e!~c&;f$_H@Qp`i1!oz4{CSjPTDO8Uki-W%OT%=Oydcm_!A<R4HcK2J$&h=
+P#Exme8TW9NFAE#Ks2@X>`8XD9Df7?j{NpfeIc(Kn>vsiefqe+BhcfS=<*%GcNqQ72xLUn<!!%FT_J)
+CN`H+kCjS9YO9KQH000080P~d=N45Jl@sS1q0J9YU02KfL0B~t=FLiWjY;!Mfb#!E5bY)~NaCy~OZEx
+E)5dQ98!KpAJ54QG#VFQBZLy)9v&@?TYtVNdv0wbMcCK9QUlpUk!f8QPTVo`EZ18qM{9}<b=9gp|&+(
+XZ`%2^!GOIvD)V<z%KY0LPHQCVpruD(a3dOYP8tjMAHzB5WT@0q&274o+ER3<98T{NF&ZAPOx?N*2)i
+kBc$rCBSQ+|NY@g9WG}x#y&|S-J#mgp!OK*3QNCU}u9w<+ytzWR>MvY#>oGHB<KC^!)hEhl|Z>Ayas0
+7QbD;J>5*9Y22c2(@g($aq=!cIy`!L5+9$wn=)NWNj8+IL&r;@ZOJo`%Z-ZCimVj}X~@&5&{K8`HWt#
+LZ5gEPA$+-r{DRK2kT)ul1tj+|IX6}-33@adU7Vb~K#PuE#m9%2C-IBdm+XKA<Bzj(J{zZ(<Cpv6*Zb
+p(zXM!LaRQC=b-*Gt<IIhPaV&X`X)wWMNyd#~@7wcSKsG<3oi8jVL8xx-AhDDE5yK~iIVO>i!p3oEaA
+7@84}C_4WauQ$Y}~ufj_6Ja8RGGxLgW0ylm&S58OgX)MwDOyn$u`|#?BN^nOy*b2VpE_hEv|IuzJ1Oo
+Sv1y9aRA`++xav5~&^X1%2z$Xfg&9f~R3CF=-{>A%ViO@bt}z)(WHho@XVv_ld99)A{N1sCA*?OK?U`
+S+OoelMz#vlv;zdF_m6N*)w)%F)s>AaJJF{6{->|uheasI4?3+TRZs!1F#C7a*FdoTINMK@YScm2t5J
+jk&T@l!M=AA+*;4+B^;*jWV&5h*Dt8fbJIZL^Khs=)s`u&t@s|+=HOD7+vcI*np>sKK^RN}^wfSZ@m6
+ih=Fd#s@a#L5Odo}U|53?YZR-7cm-3miSFrxhh2+9SWt8)KU<A?VFl-<zEUA@r&O^w?@F~4+#l)ORHN
+@kvi4;l@o^Eww?<g9u0n2XC&ef_hVQ&d_-$;E+m@s4>4&SVC2ER%J=r0t>GIpyv>i7aM3AjxAC&cFC5
+Tj8lz%Y)Ji3T({jW!W#JJ)Up9DNI%YM4QEhA%JhW$RwPFEK2QfiB_A4l-Yz^)4TG=@ZVCnb}r2y-fDT
+yG2852GZXQXLrZgo}FLUSm@a_7q@8If$J~AbH`uXfSRzGpn<{?w_q8!8-#U2VDdc1=)z9URQ38eJ*wj
+E$GMi-+8Y}4ko`2iu40M4H1t*P9RxuIRdZy|T(YttO76ahGFAaO6S=SsDdrmfEkP!0rsh?R8LB}g6JQ
+0Bz!kuK#GDC)B?g^_U;-FOoD|Y&BRxE^0zqiw3r6I<!bO6dJ=5dG3?qe_)O_yNREg`<sd_{d%p_VMD7
+Z>(PFDq?Pl_?)x02<M=wjW4F>*sWJk+_8gCJCX>Np!W7+fb1LbQQ!ptRbqTNWryyHr}eZrarJ<7v04p
+>EBhNORYU+LzKMQmw*C2Uw56g3g5mIzBcl3DhGL9uq@v7S7dvv&VH^sGumi1$85(4&fs}Z-rmA8%<j+
+M&v0R@@xmYO5;`6p6g*{4^gYfy1HL@Jnyy_dS0D%44$y(><{)M`}H^c^ZU>4&k&M&VfM`S+;rXTckep
+xZ&T3=5Z2FKY4BbQ{m`wrmfdK177X&a%4t)OZ52|`eQxM)y46OM@Px5(sk5P<i6P)N2cLq&gvc0Tf53
+|(6V(m)Cr2!MoO&`aB+^_Mh!K%omX{{<*Yq5EH85l8&}`|_4ly6n;bG{>u(OA5L$WKJTm>33;OK<l`j
+OiMV$#7QRX!A$3`T0Y_ZUtrCy_8QB3*5VCx3l4`{mkim(Ltc?Q%RsWITF~X}~M69@2owQQId-?Wluvx
+w5bWz;(5Gtjz-#Vr%_WR2e2rH&??hq(1MPfY>suuZ=)-gI;SF|8UR~_ojRF4H;&-#Iz&mvI~@+xt~x1
+>~!pdkf~a^zQ>nJq-eM)!Sv)S@`BolpVAeH+9^Ch)JK>4$D=JsfhS4wlzw>ejo5+ub!Gb!)$cVC!2;S
+fEQa8tDjj1lO>K7zIb$<r3$+6L?u=37f}t9|<RarY$hH5Gq|z)@nWYR-)CdeSQjJ^9Q`%xFxI?xrO7=
+*7)iBv?R*AS~zlVzQ95Wya*x1XHr>NW)o~N(>yQ`_ZKe)`(zxrN!;{M?_o7mNHN#UmKMJ2dI`UD8%hA
+0})&9$m$avnJ@!%8-r-!hfseSv=h+!*=JT=nSgWb_|UO9KQH000080P~d=N1-nT+Zzc00CpMx01p5F0
+B~t=FLiWjY;!MlX)bVi#aT^{+c*-v`&SSS8f|5?$|Q3O(7+~{>~3RcvxDtk<PZb~Em1bpGAWQ$yFI^t
+U-3g<wtJA7!}^f6rDCz_RlTYrQyaZ!>vi3^&WQDz$$hJhV|-(E(>bvYzZdm+u}VHlE45lI;sfb~aauP
+vJ+Ma`7F)Shyh#hgubkc8{{8mTea<RT^R97gsoZ+URn-Wy2<uwe7Ny>|+@-ajZ$JI~@6UI`>(0r>rnj
+{=d+yd&{w8J;MWeS{skTXSD`>w;D90W7&`Yl$g{rg}Y<)U;SF}cNjj*=Z@LupeH#xgir4f5l+Tiyy3w
+NSvh3W17Y=B*({w1H}sVT!M+e)QGzvW%{xmY-Je7j)yT=JsSjW&GG7cbZ^+F7P+M6a%efvHRwqhU(P7
+3B6JC~-S!b0$^UbQQsNceGCIKawazh@j}!tM*Tdg?K7O>)1#C2Y3c^(zfh|ebP!S77K)$t*vN8>DI}u
+b=h!hSFI5+$cQRuHE)^?FCVg?MyOJIBW@^c+&MD{7qx6e&X!4LnftA>g<lCPYjzN<((2l=2c-|}utPH
+S8p%yYDj5DsmE(a>SaIDn>|1BS1#7##h{_zTmB@T8EGe{I>*4K`gg!yQu+9on!rt9|xV>dakisvPHB)
+-nI5<WhBfu)vyPWL)pc?{-;W0&vG`8AWxq+$WjvHP&${+Z4;E18qe&d65yC9qJiRyYgMr&`_m)?pKTn
+W;B>yB%H(*RWPpm5^Jt=+K|t1Qc(UHa^zuX96wxM7X7ZgoymyW>_^==$<C>w|i4j^uGAJnH1{1d?kbf
+ChyZ^}xR-o`xs%D+M8Hi|}qGIKx6Z$vR}lL7N9V&WVr?ArXK*aV7>lDyz9FB`8WeQaZ%Aht(8hMo}i~
+eV7`Kq6dVA0QFp^&kVrgo0e)d_?i{o6|qVlWq~h&jQ<T;`O%Abo?gy6zpSz1L}Ey!#bW&)u|Qoz=3L<
+LwEpE(6cMzAe~ID<n@xxMfF$;%ZlyQhpe53Lxm^0c|M0buAQH-=;{>#^VGXh?X?LrPl8!1CI8rgK*n>
+E3G&g`d?NNkWxu+;XOdl**dr*z$l_k|1F~~v(5l459HG)6-G!azYIP%OQCqr;ojeHPnCo6OfR(8gsYG
+zQhLmwG$7rez6EBCVDrVn<+3dz19)z=o0Lf-rGDz|_q)PGVG`a{k(9hD$T(MQA^g_XT7-sAz1Aj3cqu
+fEpgCMm;4KzMK82BTgV?ACd(EOq1pn>yr@fA_|}tE4S~UeGhx<l8fg9hrJV+X%F&V_5o=UrK-pcuP&?
+pn74cD7Q;!BU14iNHgM!8iO99*;kUoPL?}F0vy>vDTKvSi}ZkGB^rLjUxA)gVk;Ra3+e$~Bez>3K37R
+orwkU*!qdcHmU^6%_$Qjx2j(xp0ahPVX}~Atj7OO_M)2y`BSD=C1x7q^`d#!%?7#R<hY>@%^?F6go3o
+ePY|&I-zI-^)w`?F=N`3m=k?-W4hGUY(rdc3pPx3AnjQM&rC1F;lWXnB;vZ6O?Zq8y*L0n7Qr;!Y-4}
+;q(&23&Ryv4``VK>s@TW?&D#cE=GZ_ZPPdX&1ey;}z$Or=sPP|g#Z29c4zUytA@+@P`=YA=Z+a89F0N
+LK{<QTFpq1LlI!P33j&c-sn<kCYLykVUbX=cI5?{XVIqEI|tZ4J@jVZ@Xj|;tvCS+FMtQ^xeZH{}7cN
+CCLv#$pX}xU^^HeXWdy$^N{=_eSY{bT%!50f5s%f9g15LgbU|`B?Hz&iai(Cvjcca;#hNi@|@4u|3u8
+5OWMG-pbMxLL!$VgMWjNU5QHt<ZKzhF@*Q@FgfP<@8mSsJ3frqwbOdOcV-8b7n;4axKgO8Ubw+LZlm~
+NIvjM>gMPe-=72O^M1oN@Dfa?bu7p8r0BWgk#^$-yGDjl241>EO2r*K5PwE|CkD~#nDY1oMKNXr3ZOd
+lAQE`T|<_xXylnu5>|3O~o9`)f2|HY<F8c?|(RtM>P+;>3kL*72<XXI4wLWG~rjc92Ajeqe7h`ggn_8
+2wj(3QOFvcOmr`QOyaKCbZ#1>m>@>>nZy^eQVo_Q=Ko^yGJc6_M0%eKfcHFUs0S^75<1b*A4rz_;FfC
+qHRm8Sw3g*Yxb&FM2IY3+2tk6(GMJs8#n^Ytxne9)p;}Uf?e4qyXxiEuA195?#pyh5tBx`oT;1FqwT1
+@J`p3Nn{IcM0+e|=t*Dq&+)8B^D3G%HGa`pE^iL;q=rvIr23527yr(widtEw|5{WUaidW$JA<nDp-KV
+=-M&|)L(&uM1e{@U-_JZB%cxD;T;G}LHHKCaNjpM|C8ZuH9&6;*b8Af%cM3}v|I@K|<F_1I!B4DFF*=
+nSVI)$JS!4GhSM`D9R$xyB6+!gRX(49&cX_ureLa-dE3&+P$Kzks4<f&^=YQP*hx<R1L{lU=ciQ7WyZ
+oBhS1nL1sO26YWfT}b$!V${0TKP+dB}HU8BV4@L^3zQitOf+mbn$U0ZbBp$vwAm9xbGPLUa${4fg@`0
+n<Gl5Q;O_Htw?DXXRuwGRr{Nsy{<Z(pvN(b_{C$i;Vsjh370M&AL%Xvk<^|6z^}g<GWH#&w0jNvetNU
+&1+vMyz??o+eDuQ+BOM7j&3X7*jyf+Uducgp(`v4bX2`Mg6B{xXINo0tMXY$pDo?JNK3-K5Febi6Bj9
+qw_nV5p9lat@TTC|>?EHgX*G)I+;r|b(y$#R$oUFVwJiO;~CY%}cCc95R7p8`E7m`YNOh-CjTUY7M`K
+QCUu)JD<lO@K4th%}iMaXt_EuX^6VGzhH8-9j9?jH>eGNUef!QFT#{HI)g#_jP%b)GJupU2=kyd1S?>
+Zxmg`FCa<2uC{WFAGDvTn1UQ%+8dG_zouY|3guf$FhkNRk&gt@;nX<zN?Sdk0$!At#AOJTQ)>{GAo9$
+q?hu_lFZ1>(fp7Q^CI!rOiLpbDY?E5cY<FgUdNrMF{XJH*d?pzwhu$mRK{C!c3HII?5gl?c=mOEm2e1
+Ivm&Fj#s2_MO9KQH000080P~d=M?a&4R8b880PrmU022TJ0B~t=FLq;dFJE72ZfSI1UoLQYr5gQj+cx
+%h{}n`v!1C%TYkR;Qilx_U+U*8x-LSN~AG$)JCEDg9iykT2@rwTM_ueBZQD2VJ89^*l<m3DPBG1mw{>
+zIj7l!dXXTM!tvuG!@k*Z*+DoU;L88g*(t8{sGb~agRwP8uJtjbD@Bw=!cZx~+~l~<)m?C;6MjSH;{<
+$vUAwUWik|5V05nRQjld}4ub<u*=sqR5nvjl7FDY9~xw={%PUzulH=BN#W#4kz?GSTF8Uu`PiH<5#@m
+*_GOCfYfFdZdy->GHJFsKLD(9J!cD^-d5WrlX}il;7nqw#a63S7&D)+;g8;wT8KH|bxTkZaVJe_W|PS
+zNf1WDF4>3aE;Z9Ro32Ee7@a0s{8>pYHliq}A7LtTNobhq*cHRXA89N~l7er5>j*K;xiRdw)LdKPL~8
+MeNXyxC!VaLWU~hc@IG;Y#kV)6Di6In>SrZDf#IN-V|1bX1_+^&mmcDldqJt|lu>_IJLY7Gq8IdpDCi
+r>8ex;oN<Bigy-eoV#HA=@yYN<5aaV=G4SVy}8Nag@=Hl`j4g&S8!n4}P8u2K+h3Hm{(#BRe`0OyxOx
+rPm1T(YO}KjVMQ*|Yc=%akz1v}9|(6O4c@HD4ju>y>iJfY-b%brfz5%xKLA?z~ln2$(R8te<}zU9c^X
+Y$L}xBM!$k!)Gla0Hgat&;qQnpoMWQ`M93#AiM<qg}ur{4G)zZFf$|Ya)1+cfbMu+2@}mmVS>e=l8(X
+r;P#B&JW>re#-yXc<MnZD6EaB4$?iUZnt!YZk8RQxqg(D}=CSRG&9mH&HzLx`8VD_L9k~f`0|hG-s-P
+hygO{LN$c1S~nXC_p_goj?0ix*@FL0p;PHy@fy$FDa8?O+8un2}*<6L4nf|6Zz>9C>o?t@?Y5!edPL%
+3#QQ30mj8;FdMbo_hXV2fY^m9C0D$1c&l<_u?Eb}S7!h$P&DekSrR350$<N#vFxeL%H_-wUy3N&wY}l
+LXmN%@!i%;4VNY%(`??RU&aEAqY+bR$;(!{3MAxhJi!m80rFqF;M&GU#+5+Yhl5vB|Ue%aAO@yFqR4)
+cxf@1G^E&l=1jz^m_Zbadl*eGFz#KH>H=P0fNi9=sw$_mLCh#9q)B<$4#;yv3~gn*!VQ40xcuA@K=_K
+z%`A@N>1dn|X>ka6chn46Jq;HjX`uhg#P}5dp=1$_0%|V>uKHy*Cy4O5LtCG|*8((TBMSi{19<>#7+B
+D-tU-+vI80<8G5k1J7EbEAIkdt-MRYnyfE#K^(JtvL&ea}ld-hNZ9t>&DhzBhKv%18SJZ>6Z;Vv!co(
+9)TLWJF-aSTI_xXHaxVR<+K!+_0$ye&xPtW927lWZq+j-}J7CEf+kdf*ZJ5jcd#?Q(VSzdjP8S(Iy8!
+)I4P=GBDDPNLCddvnZfoKOCW>lFm8ufDq7<5L)Skq<BtAi@7`defq;m*RAx28c*QLd`)3w#vv->4QTG
+2S$7zd~otzU6zpT8fHoknLeea-W-kCw#;ZRi)T+6&}!_zU`eLIAF?Qyw}Pe5fB$`{@=WMq-$rxitmHB
+eTm}>vvZPlLVc%5|9E@p4gF8k6k~548v~=PGel5U=jHv{B^yHg=JtHyi*9R-=jerz5V|$Jf4tSnbsmP
+wYmFu$Hn&%f6kp8c$MVzY5#rCk}7f=&j5JYo<5NjcnninSLP~C~u3JM_|L?8F7RS|p#n20;R+2+84*M
+IP0r7-7V3xEUaW_%0+j5q8I=w0OE;t8bccV}@g!LsmUU`@_bj4)W7Bw<_>cVtH=MZ-5Vmv(Oy3S%9Pp
+!SxE9y4J|SrDWeWaANH5rcN*2Pi7`MXq>e$&pY+&q-Gx=wW~a-Xo2)+6>~OxrEsNR+T>>x3F3e*&D5u
+ZaX=^3=Ck`&2{^6-Vi3K(i&VxNaFine`HYn!q85V6IdLmipJ=POWLu%y>M_pfmkNsIt*W-BZ^wi@$4E
+<IDn)eOo5g%&Ne(pUbwsv2#VQRd&xVe5+Ds&@h$N0#p7=w^)FSb@)w}5-@>XF^X@XrMxa`KjA{+DHV4
+H10!A)=I{at5eFRE-Nl(qf#`3`LLS@0GkEeA+f?ehv^3LlwVR7n%2ogi49RZj*doug*q{#`bgM|=DrR
+$v8_QIc)K=xpXXdPxyJL=p5gNM*V>|i3rkXj9wZ2FSWMwsp3WOuQP$c1Q!;7dZVLq&K0s<5-8qsULEv
+z`Yd-odtQ^+hvyNVW<&oCie8Is>a(LruXJ3j8p~HbbdFbBArpi4^l}zZ;Mb3%2MzW-%6h<X%a5K3|)&
+-&-7<z5=s`mtaR=29$4s8uWZYHwyr~n;c@^jC)do1fynAfl8mtzXYwWL+%KR-ul^KRA|-c_rZmO@T9S
+JT@`;;s-(QQbVk2XJN(UyLk$PKy3BD9T)SDv!|th;YM>tkU66W)d~N8l*bp_QA?FGGe>y_dm0iQiy&~
+^}q}kL(X5bZN2?{4yz$u&Vr!>lQkkE1ono*=svmYbD3cJl|L~6z^ofY@qJ)H$KsRhJxpI2gZ7^8#dIr
+unG*8<Rl*p?HIfsSgM@zM%`iA++|1A|I$!NFjra4X1pH;zM9I<=XL517878lm+5Sb}<=t^+FOwZ$gNH
+cUt8x~7$N;KY!5bAu3WZZKwUp%^Oh;pPSecXRW_7!sF63&Z=7drymyu{_s;XNRw;%0gxnvM#7oi~|e<
+fPVv15=Igv3P;&$f4Cm=2pPWsvS<Umb~A`Mcp!m^C$GhSk>(Hw;z_s0f2ScClvY)02oOq%C1gvH^#;^
+%HZ#U+2yM`F_8rfGe(<(J?|~D;O!A8$g5cKmIK=GD7Dow67_T}-aL0~1Fx#t#A+(Bd%lVd9A1JZCY9v
+7f_nw;&>aBonX;#axg<!hwI{dUM`q6?N@+8;9u!POYZ)4Kzb2)VN*x5Kz^%Ih)@Knmi;52g)&Bo`4Cm
+xpc@wrIl;LAR{0;(zSj}!ggyF{S-+#Ky_JOtM^AI9ee7Woj`Y|dx>RNq7O^{v+w^q)?1@ThWh^ut!9s
+1rxIW&_(%CY;KwhX|l$8xP=Y?Kq-oWM&j`<}8vkjQik}qWw&6@q00&(>Zou2MplQQJchndphI_b5=ri
+P&%hbX8PJEhbR^ocv|KQt{0O~Zq<9NAxSDUu4v;@xleL^9*6ur##MMf!ZL7$7Rb>ycW>h<)^?yi6pkR
+Kxy*<n)wv1F%|UfO!ib)Rbx%Re7OWBr)Xu4FH2oj;Q~{Yx{hPV;-qQBrn~(P?R)?if@XaQzC2|?;eaS
+!%N40F+NltCnReMOp_K2T6#gtDq<59~!>NNW*XAeDX+BuSNcI>gsIXS?&M<6t`Eo4n!%b@_g2j+fU8u
+d}lYmo~VoWKnFd>d$_)A|2M5;2x64I{W1KoH^Lg#>`m+)fzT?IZ{Girdo@uz9argOtwetxu9TUFM)`8
+&2YW_32D;MVo&e9v5Q8bXqUrDi`2zfVi}R6sl;t0py%ptvgHw8z)plpmf>y@gQG@8b|e80O6kf40QlW
+qR{F}|G0q+##NgGy@G*TBfjpC$m{0NXDN#;Df!A=QX+l)c#yk}3UUDy5i4%j7z{RIg%BAOx1|aM)o+1
+%e&+|~U}5UgAZ8SHr$fudmU_sZPmL@vh4}pPlW(7Vrf%LR@Va<5LVoV^*Pq;BW@1|fODXi{8rl!bh7%
+ZlP|u*;hEM~t#5X`Yyki^gJgp_#Zuh0Np|2+%dP0kOSQ$a5uRT3`k1v3ezN3asg<cW*cQCGdR1Zbl8(
+7_3_6>eghf`?zQnYndB&ogE<?jz{GElL#+9#&Wz;|3)fv5Y-9$^g!HQXry4YH0WRhh8s24}kslU(dXe
+(A4iy?*oR{rB_wCCDT_vZJ^wHBUsblUfzsTMjKYS7@EsGdI=30k2=a!?PavGZHv-fO&h(w_BkZ*K~;A
+xw&SKk$INcV*&u$)bbWGvxngLM5^VMxY^Xn-*K5^?`pUuu*vY|O`WiPWRG#Iem{23+d(wyN|6M3Qc^Y
+`%6VI2CxHpYIB!d<@zwU4A^Z87v=Z+lc4~=7_5x)5=IzgK-oC>laRp*b*JK%VqUURf&axj0+k9brr86
+ieya2?VNGsbqlyse~FAn@b7!omUjoCzqLqDysR@U5)tS_mMbM$&~uBZMKq<@QGxQMcVffy~mw$JsoOh
+k~^z1z~BwlI}~BN5qTG8v0>qcP4Ij}HX?R?B7Yno9ecKs)^6Nq-x_^LrJyvmlzqqj;ZMQuIGH!Cwxd;
+Z#?98_7Sgrc14wWc4K#f2Rn%f8F4AU2NOCnD&80=ce&JsC4AF+?m!;vum6e9Z?Jvj6*l%C~u0nY01k@
+9OJ+jP#(En!1Zr$11?N`f;(s5%aXa?>rX<19Y~(r0dy0u_+OlBcYq#prOgeC_EEb24^T@31QY-O00;o
+{l@>?qnThqw1poj`5dZ)Y0001RX>c!gV{<QJVR~U<axQRrl~-GD+cp$__pczD1(Fh3ZQ20?0(cnKbwh
+z-9g1x)Za^>+WpR;2m84>)zkcUXqC{Pst$r~thv)j8i*h9^LTTQbR&h#+s2ZsZVfR{=t>LuWZ#MmTDG
+vuxAKX@Iw=%qHa#3=(SA4U%Ab)=R<>M{+jl+E*G!eCtM2k~W$w#h}R+Xi=Pa38)-x&3DyCLw)QKRn4X
+=5n})BQ2+Uhq@O8$;e(ns-V`)iLNF0qy#`jg`=dCTWc*wWC*+QJlvl6S^tc6A+tXBz;T=LRiM+Z?3eE
+^;=mRCCia6lgGN0EE`of1vQd_2Aj<W-NzN#IX@-`Za&zvkkXn}5P=BD(voRSer8`8Q{hxn)Y*u%z$Uo
+=ssekE3FXd`;ZX5L(t%X5IxV^joff>}8jeG|$DidD+rR6=n8sggx6!&7mib6ymlICcr8@zGtb#lToQY
+Z(N<+=dJjST(ewQ`){EDdqIQ8o4;fSl=NULevfO9%m#01|($-o8}EZ+`KFUTKHrjRxHaYtS!KP2DB<W
+2I1WRmMTFr;9QoV1#2nk#mwcnxq?q?R=;1XoO@g-BWHar?v%LK_HQ*^v9z03)C_gr6GOp^Wu@C5ZC2v
+GMO*kqm4^P9WyY7!~@U1V2tx0mq_eJV)Tqf00b+6Mizh&cb~Fe!q*(zna@Oe>lVn%~Gb1R4G$t0EC`C
+FJ8PD*6&VT4JddIT8lg_j8spAbr+xo`URI6*GANq;lO#7FBTOF$OfFc`jS)+8Lq=%+i8dpgv8qhqtU2
+y($)=2A410>@*^O1f_VwHLqK)7Vx5aP7AmW=qd^-g_P`;)YOpqCb?+}C3`wO@JT>Lhgj-i$Mp>hguJB
+FNS8YvGSyjx&W-!$~zl%wF%y!PYc*0=u(}g@zZ8F)K-LI_Fe3YI;Aj)#4L9grRy8#vYC`?-PsK7YdER
+G@Gk5o3O*(7<FmUIjP`>Zzt*?1z_CS!(AMEq~=O<R@-i0zl)j$HfiRz=Bs!$j?g^0+&*0DD#tWGxnP3
+P<4tOmHys-Y69w?}IItRXDs`Q1z__t1}4!Sa2q=){{#e1>_R<!y67^dsfb~>;qq8YY$Ya^nrS*7_Nsi
+_F2s`q-$r1sB;-E5E#5g3DD#Szz!_B)R;S$I#_}gWiAdqvCB)pNn6FEDNXcW8P#{gRjj~nZ{2{5{hH`
+wz_y~%9T%jKCKo4LEdKiR!L<UGW5-ZfNYEA74!Q}a@20meU&1ZRU|=w-*jwp;XQ2p(i8f3b%(5a3I{v
+}3i}a(zbpWQ~WHS03#=v@zzp(s#JAg~=Qf2CCL`MLrz9>RnN-hgx4sEy;HHUH0vP~e=!r+Mx=Kw^qo&
+5vDm1x3g;E~k{`GkSRJ0el$qznEnS%G5r3!quzKrj%_<o5pV<qi%%t&nwYZRDUP8UvL3o4Zw7m!DK<i
+oU@xyIvaN@{9~GeBR92y*Q(kB}RgI{BiV{#@<z?^8grX*9r(G^7jFd*53w+LDx>k1QGi!cygkwVFy%L
+pH~(rR?#|B6v@6xPbYa=k9|NR%Vz+M*R*2LB7ubhv~V2MLKTGj<nwWRcO8A!S7CA$eTTyUaG1sxz%z1
+K;sow+lo}|KgOY6%-Yfyq6`uv=>m{W#IbltMgCrpC(dA-{w{8<H!kLNGgzo*U%xaaw@t0OW>4Z#lJz8
+Lka(<6-ddV={dICh7Y<|;TnYzGJIJ_hAysM5WutJ=a@(2Yvn$%e+rML4E(W^0(*-4)9!+|zR{sUv8`#
+wX{5<JH}^0##&*g~9dqW!B8O=+vJHqqH^GGyqps2s?L92v=dbA2HlYAyAAKz?rm{Op12Y1xG_j5=G;3
+72{cjUOhOaBg`#@ZNhey)T&M+_T0#gYK<*5i?K5zA+07!sDMV^#zdc`sWy^$(wj85kJr$Q=O>(bFgpD
+(dJ)JO9KQH000080P~d=N7IP3k3R_j0Jt3h01f~E0B~t=FLq;dFK20VE^v938C{R#w(;G+g1E&(@~tf
+26>T3}ERgh?^a=z`kxSbL#}H_VvY3@bg{16V6#4I+At{Oauy=1Cyc9K@8P4~Naw#g3Bzdb_$&!Tdsu5
+BXdQ_rpHB0RCYUTDzaXRt()IDleH90R?_bgQ0P+hE6IihWN6DMa@XF|q`KgX3gGZnY8Ecvk`)KIAyp^
+Dh;#5}+KxMaVzOlc2++woXvH7`|nAmgej=}UraMnqOxGLPIj?EzsKk^eBMxTrsgT1!!`Rx2O^=eZ`2B
+ZlK{9|79>8i;!Xcu<Z^0*Di6d}8_svlk{wO)F5v8o<($Dn<UzbvWi^y;~9ZLqNb0oVX6mM#g2g7x$Zz
+H6rP@(^AyT?yIz56;rSqnrHkos3P<4q0qYV*Q$F^s_2#;INCuF8BD;s;fCd4TD;~uNkYZSJVJ>){u4N
+e$a^YJAmR7#pDwt98}&@wk^c($qzILwtusPvLK~1Ft2K&(9NUuz3Wym&_IBGXm71p|i%%7;SuEsfYge
+hLK(|(co79RB<+J_x$3Oq+!-sW`j_1hXu74iH6p@<gQlt^FmIFp6Um}uAda95yqimzy5EknZ2Eb~O#9
+++o!gUb_Mq2AJB?Al6;<QW@XudGYL3;b$ret-9%eYip@+J(n!Fqj|ov+yip+-u*h5um@YV1pBx_>~dX
+hepI_2Q`*EUly|3xi*NL8|}`eQsxG<~u;hO44~s7*3_{?l)7wdX9|dn#rUoTa=<xK+fwd3?70tc_1IJ
+khh@w-EjT@&>mBZmv@}*hf}-58`zE(q|$Gxwt;{EHXjjmpLN`O6L2$kp~36eGsZCIbHD)AVELZ4y?gi
+WyJs803uc@OgM{K*$QMHGE(${nv&_H<cx{mM*Z@V34%0vau?K|LWrVnT%5WV98_NtD1cca)X9vh$+|~
+_EpTZ8s@K(#0-PDb0p?d~AXWA}#QU_;(tXn>yQI5SEd5~?LK#YJ`k<FF64?wZ$lW6q@jE6nrMK;v@=H
+_?PNS1Tl@8}%nT2+O(BuZz1=J9)4DlqBvl0D9^4Hinva&mQ>r#vC`nl`+Fqn;DdAZg{44m*+2n^|@*$
+0qOz&l&{-2*=<^92_~aTu4T`o1-CM5M{NZn!ypY5qeTRHjo@SN?NBFQ!4rb2G24gB!}d-#q3Opl~UXv
+`H4Zv0i=-rm(SmRBqBF!iw4dj1yp*DSs?Zq@7EpQ#=}vQkVZimxeD>>o+nvOP)?Wx9bfk<AoqyFkN+M
+duLlBSecZu|O6ZQ+v98R$$i6?iA5b2KfXzxE_8JvxOsX`J0D(BsU>JUd<dl|TW7M|(h#_v&P<_me{@o
+|hmYMN444J^ln1$dTs{trZ+6vC2ZB$&x@2uU8u&tLk^-OXwWpw7WaQ16W*Mzxc`<*#1_^GIKenOwL+K
+H{;F?|IF*TFiSwT)YdoY@=q{w=`p?=j&cnZ&Xt1NTT&d#ps(mTY8(ctr~^y5jQf?#TCCLS36P5DB;;t
+xw=srcSj1Ij3!@ofY1?ib2rbW9j;L<mZB+Miet{exbDn*X<)hSkdDPVNi#6?p#p?DQ;9Wd6$?3uHi-J
+qVtF~vp(<T{=;EGQrApuK>+4rHcl5*X0NNX7tGG7iwyewN_C5)ro8sl?P<6(Z9j6s9ttL$%J!B_jjPY
+zyEX@d9SK3J_i8<jb@QMwK^%<q9o!@m_#As`_9};P*`*^N)p{{X@j4fCBMCl1D}%x3UfnBV?ihIJ9mZ
+(X@mU4k3Q$YYz_d)(ZX5wbbMU<XnyThfz2X8_()A449r>{?Uq~h(BKOI8?0|6LJcnM#MvKqiefzh0lV
+K|<FJoF~x8jMFhDOSkR%1_GsInGiwg`oPuZGsECp-UKh8PP9JQvGZQ-@%;2R^vu!#(S-`myuP>-Ds29
+B#_tc7B?aj91FVp0rm1nS=@`V%up=*zQ$(mG`uj*jguEEV^+AfZCXO23`Y=3$x1Ca_f=2#&xc4b}zPg
+1PwP>Hex}9DJxeb?5j{)K&rK?U7<c9h3R=?zk&pon^rGfd>`PoHh^`ioaMi_3jyW)zu$+7A2=;}&9D-
+*>~unjO}~lnR5<ZHdn1e%IW{-|Vu8befMz~Ut1bkB-(K32x7~OMM>F%FmirMQKrwHv0jtHv%fkoDJq|
+8rhsMdo)xryO3P!W9q|zUGD-E=guOrc&I4sD@p5V$vSd`}i-t)c#JOFN2JWo~AmCEO_YbcqTxeNzsf)
+91Y1;^CU*;-_Z_QaU~&O-<>aa?VLbL(`u0fFOET$l_;2?0layQ#1oW-|s1Ohj+QP-mP@Nh8ItEY+Pfk
+=q#e%|L10S?<PIF65adW~LO4a7OmJ5XeRma48(+^UiS&c4q-0*eDZf)GVa@g<P0zd9ga@HO?BDcOLFh
+LA}nKk~s_*xin^R1tkpZcK!@_8t%IC+_V7x6`><6n}Qyh<|$4I`WFUia(qb~TeAQIH;6i(F&zpCRep%
+~in(SL#FzDK+Ty@b18`(QVs6$GfW7^9u->!sb?R&CHOzP9A39Le1~gtxDFc}q0|S|{r{-;0CY}8)j!L
+1w-PrL!IJI9pi;Fezp-QY95J0tN@E+pNWE43(%`5IHBS<!5XQVqNyP5_Dfc@rP-2vRA;}qQ_@`W3X3C
+t@R(`}g1;wZcJ>wlS<4VbIa?UDTZrymrgnkPnrRx$ZaA(TeEJAB%jh=Jb&2nGrgC{^sX+hEYt&jm$Iy
+5N<OBTH$EAAd?+i1t*V&A$-b`SRtneNdtTQ%8Tv1XFdOdt?_OpDZD8Aw$W{Z9!r2DaM&*(H_?^`731i
+&N*oZ10K<1!fFP;z>v?ZP8q^to;rgYYeG+0JDQR11)4`?=3GwxT-&)8ie`B_1(qJ52s#h4#pd+;0#zV
+AP0Sa8-3)f2S{-Ultor(yy9vI-7aAlyHblSUoDP<kyH9t@j^qbQ4m;2aZW;I6=`B>V=Hzs6;+N=Gt3#
+JF+^5KxJwvilVfXl};6o;e>}N8EGMI)y%gy!S!lX3rnM27~hV9$|XZCg_8F;7SXVy<-#zmc1FAi@&KI
+c?{_)i=gaTMcv^?y)H0|XQR000O8^OY7y7@M0oj|Bh#ZW90i6aWAKaA|Nac4KodZDn#}b#iH8Y%Xwlw
+ODP7+%^#YzQ01f^st?HP1BDD3x$x<LYo#2`eko1iY?npypkiyyBA9SduL?JmS6Vv(xwXsmNc4qX6Dh1
+bcK>flBPv#TE)_o@M9yDA@seLb!%AKjW?TqzLsUlMd^l8yD!6zO~GsC#)@q=1?p~ileC7{+NsXDZfg2
+DZK$bYqK-x}Hj19IK@dJ;@)uK@OYueuqhvkG<?<|QNpmNR+EOFaR(V3{iHTgQMDxewC{Juqok>yfvY|
+>dwb^XYE2hZKxtEm8{ATCEG!^s+fr&sYtEtxHcc!vdaaxB{Y|-6@zz@YiQ&5$G9p_xRsl?4G*2vV&X0
+;Too7Y*zj(~DULfjvtmRFTAjrXbhZ>=l$WvJoV*2)1)Ym^m)J)*0jW_1yRV;KlZ(SyX_&CSi*$1ZmjB
+fpdumF7mO&xCf95KLmE{mhv*Tv#20GSDz+!~Cb<D`0n2NRA(K{0@U{hXwIMTDE}FZ9{ZPD;k=844sN`
+)u_)S-a%ZoBF*ISNJSn_R`ItFsLCpSV!`P2Att(#=Tw_qwr2M;tu>2DcFuQ8lLa_g*E6#nNj-o=koB3
+Htm=b|F^Vy0Kx=kNWrN1G7jm2Tj(ZR%sS{*v@<DPDc6+UVJ!5QgRpte2*y+<Zxu*qtV<-N!6X6r_>oz
+8e(LD6PL~BBtnJ_?rmT}-LZEH*+_&1e@5L84qO!z8c%Q=V$)xEkD8F9BrS8waAxM86uIa*+k8EXs)|G
+0mvlvMH5`FHZ3Wf*3=Xzjs<sh#e0Cugbzob4rLf;Y0Qb0Vba(>We-|4hI^xYc*?7xXy>L-9nJ2O1c5g
+}M8nZE|Xmr@{79a3HUI(scAhQg~$RHcZI2&a)L`O<UK_l&}0-;*;+U1uBpDJV@?T0DG+|sIXY8Q<(Ay
+%L|m59lp^jY~I80uvqVV3YJq6LZV=fV`+wLMFF|R@e{e3S$I-?;A6F(K+VoRre1vv*Xuy+tBO`B%0(g
+9_z4{Xm`kb>{JGT~6N7jmh!z)<p3&u^--|8m=n|vxx5AOGc_Br%LQV+N2LP2GuB!QJs`d1_+}*Hs?XL
+8TeSmGDt0%TMVX;eGr8`VRaiN0@rW0)xZ>C(f=2NayltQk%ViIBzol>9!>|JBRNugB?E@%R!21Aoz7+
+T_4(w8Fw5_&Bp`v6yh%T037<*TyBrl+Nliqu>%!i6=K0Jpfo86D1?#pU!!vDWUuHRv_iwL=wz`-k&G{
+*Y`Buc8P25{5Z^-6b!-$9@i~rc{C$kE)j41=jFBGDsw)0+NQWqqS4ACi&x15=`D0-rSJyto+ibAE1Ob
+&=>O%I^)UZEyh(hh!z>nd1gYlf0pF;ii)hdlIKo{t>$`0e<%7?$@f59&cD<A1P7eRH{!um-7+oRI+&R
+XUZcdS^aWb<mBUh_+qcdRFM{g?=>)ED#M3gPiR*rsIRdvE#l$H246eY13wG$P*5ggMhKjxW8#H|R&!w
+_=YmRE&tF)rJdX{Te^545AWX}E%-z==DZ(3!r@~Pzt-haY9+Mqj|(Z?9yCOH=R`IBOHrwW&67TAf`TN
+F-edB#Tq0Hw;{g}1`T=3NlYFGW@{J}?8@kQ+H(p~^CCl>7i!Z}*HvtO?HJ7`5ZMhnF7&^>wuW;jji}Z
+qNqm9b72ZlIc4+5Pa$BrSvZu^kiEb_6&XXS|J>E_(gg(6TVYjtF*$?XuKf5nm`kZ%aeryF7l}hG$!a)
+Z{aowm)vlkmUz&;_iP^gXUY<e2Y(e2{xbmjACU!s>x7?+Z&3^vfG<?-r4P@4c&*{d*$Dy7&Cky)i2ih
+|Ux3)I_W)TNFE|!YjX*U%Wf~sYC7cO3YQt+X+WZ4hO9KQH000080P~d=M?<@N5j_k50OTkD02KfL0B~
+t=FLq;dFLQNbc4cyNX>V>WaCy~QZI9cy5&rI9!R}%q>DJa}uPE9W+1#~ht_YA`i?qEK@VbI*(YDUA<R
+dBXE{gv5&J0P36lEv(p#>@g8=D*shx77qX5@`%4lK(yt!f3&GFBdrjZiFK$);)*&-C}n#Ee(Xc3ak4`
+?Znwli-ud26-LJW15|KT{I$<<#T#yPF$v~sH$>hxgT>OIm;!}!x?>d)|Yj`pDppnvflSottu;Nr^G=C
+&Zn#><*~|NfREY@NK+%RU4pD@mBDCFcv^QH;p3G3z=bTE`dw2i(NvR3CQo(te5f)IHxryaz-iNUb2ye
+2k0Tg-xQZA5yjtESS5cy=1wVz9@ZnG@#fuqC!<xs!Suw_2M1TAAan9oOHgOo2DBK9`Co|<QDC<o_!jF
+k0qDT3aWjU0nTRAS;8yJ2Yr(Y#+?(h?=noLmHTmYrfdb;K6BaOvbR_6!sFeF=7xs>cL?dqg89s3$j<`
+V`F(gUjqWQk~?D1{wMyFtN^O=gGJJHFmGt<qBxz8<)QiI|v7iPx=^JNt2`)Y1JcI~pJdNoiNUVQ>smM
+=W`@nZoJTxdM5Op!i%}U47yZL$zcvsAI~kR+A}v+Hp|ds!=<Z!w0DZs7AtyZZ)WbgZI^q>UgID>5g%9
+7bPi#lYA=Gj`mND9)R;>#{r+{cX@$I;%n82mpQwU5xeOacBG)6W2-<b#7wkxhK`xnMeK1~M0nz;Kkah
+r&C6Z$lu1<pyNB=cN^*aiKP!=EygrozT<c%a$KQVchiJ~CchSFl!le6j$kloWGC8r3N}h}LF4pq)q~(
+SbMJwjsp4$D=r(D#aVgER!pm%wVOAD^JIFvPK5MQj>FxZq?-$AuagQYIS#GU<px@1h4g0I?b9Q{cvUF
+~wk)>?`c*Fadz*<+2Hvf)Yb5#&|u`=$n<!@u1uXbBWXYDl-DX^-)Jl4_?pqgMw!{uj!%+x>rNn4;EGu
+sh+axkm>6jQ7SxTzF3;AQ&etAYjQ7Bb4QO6b-2w`dDzO%9@yT++3ir&|;$(fk{Wp$uT-YeRIUM2!2T8
+jCAnS(;Gz+!bMtKyfO#U-bj>^)JQD4({_9iqMRExPJ9mn-)dxv_D%V7DV2;z6lR}m=YC*C!(|--IF@^
+kLXan+F&ShRg5@<MhM1Xzd^$vAv)QH*YtA<1Gh7R5SHfk#0wzz8iZpcv1}O?&zzOK+_vx<!ZaU+L!=v
+MK8Bg(9MA~1XCA;;kbjuc=PM2YP1bFLsAzlvao$*5Sp=swSb_++vft-s-GdL?2vRpd*wZ|e?+}rPqx6
+*6Jwi@oztW;js&Mpq)|4tx)hr;*y03J@6!6r;GSPcHxA=`~i@Q#J=-5L(2xyT`Mw<F=noiiu)<8d2KT
+~^5`JW~vhU^DK3F|FaZ59NRq_YB>>-}W=WO=h|T+9Mv(Wo?jr&3^h~Cq#4Fc(G1WqS^|6<Oo9Rm#l)~
+!SzRoe}Y>G@gbG3PQKk{8<B4hfMsAbEJDy<AQf{ahUkWOjyoTs+8Q$4vTpfsWi*RWrpt@3*>@lQ@yGX
+bz}c!I_+n3u*kgWKvmCCv^*-NnwgWVqAve5GFj4#_;I!xDiL)sEv4IG4w@tyArHWxLS<Gkm%LFQ@C&=
+G@D~3+qv%RDtqIe)XrSLgq(cigNkZW*@`(*KIIlPzYuJ*jahULZUxcjX0b<)xye=ZO0p^t0ylN^-1fm
+#R3RPYn{&bB2KwqZi;GQ9?|6rVGd?}3y`ObPoORntH*YJ8rY2qV=6D!uUodo}UAb(Kr1a0NuamIf>f*
+XUDT?a$qx7FTS+gno%04@^Rh<LF|(gq;`5v;L}&RyV-6hSD83M>J{cwJzBzxHe_GLSB<0^Hgwb0{FFy
+?@hp?<TBgf`E80oo~cjot^|xQ<WVvfe}fF8XFZx@n1F)2wXF*FgeMbq7%yGe4iDE|+Ldz`W$2529dn{
+nB$J`EIEukwjTp*@6q!!*^<IF5P!E#iGCy(hUD3d)VQvtgMjd7yI8y%&iiBJ!T~OLBMzr&@9D|xV3Nw
+AmbJ6QChtmBSxi62b^ufUKc!<8(e(klH4CCFB_%UyFjGA{2am{v(f;^AJ!75KT16aO+>V%O;niTieM5
+*o!%<t}C7V<z>UN_ljj{t^!YO0+xIg@{Y5eE8>^d8Yb-`dF3dWgWtt~uQ*1oMpH>Cc1RHTwWX7Z_NY7
+6h55X)y&Z2}g1hv^qbYk{$D^skgE$xWk7aEIqzpgzU)i8rZPeLn3G|8oNJWf+{N{+(0D&JAgK;5mtRW
+LX_L?9%m0Byguy!_Qqn{LkhbL8#rc*C>iERxl|XoBBFlYZeXD2Oxh1d?$<o{n~Y)3-+6=@6gj#O;3nw
+MRDTr8CWHv<U!>v(Dytwp8exShCN~}`wLi?|q8*>BcLaW@U@}=}Y`lK@;t6ub7_qdVi}7ll6@UOHOm-
+C@Z*mo8jQDtF4@r_>B)IjR!ExB_%!SEA7d8Re+4$)i#Qb^9O)_fw#2;#i>8hd??}cbYFA^2O4dj;R{P
+WyxmS9JG<9iI0N;x<pWXym`KCr|%=RwPFq6E|$;`os6^%v6JzX~GA$WuZ5#fSVDOC@xk1}QX$3`O(Mi
+Q8lS`21@9SOXK^ngy?j5X=Jq(2v0+Oq7N7eK^SoI@%?_^lu|DAr^OwvQ}}ic)d)t-%`SZ(qedmF^Yt}
+Wv>}zUks_R$Z&H?x-bI`y!qz&Pz|x#YpM19aobTtCZn0jNKcLA&r8Sye#onqlPAU4#;qv&V1<HsLymK
+HBe2p8xsSC0aolLN@87^Jcvf+TZmCw^0oCWc$w&!l;Of-J3$-^a2b35|?yvE>u|1u)59~hubwC3O{cL
+O6K3$7Cn$6Hq7yBppe5PlOPY66(!DFvPtjS>{>3f62^;XZL5?xmQ;N?wk1>7;<=f{Nxtq`d~v)I%ErP
+O`q#d6|84bBBEjXrUc$puHyl&=EF9?xe~zQwaj1Inla9P$%XZNM=vSwt1R!kL{SZ}D<E);{Ycl5M4=p
+Xj0?5Y(s}NXs0c8+JA^_mzZ;H8=1#>@lMj(AY!@vE$E!oG`EFpy*EfcRvF*Bx#x-v3C*2uuONbMv`Ts
+?6r%oL+T);F#3h1Q)YGUoN2`&hatjDOYZf>X;w?@jibs)BEykVL&H2MP-9E<mXrh58;y^6APVHIxF;4
+ggPFH>ui>4APgm)Ekj}b(4P~hSx&W54Tva12lWW`(vu1@^bGZetXbxQM=-o0T*!g<@60QmqY4#f@)H;
+Q~Lu=YuKUkz#1FxT2NVc%o;`PA#bi*268n>T2%#Y2We>#%lQv3s<(flyF`=rc{*<ip`HWzRmxo>0bUU
+cOsnjaxu9Q3NNM`C8|`(0`q;W%aEN#CjpJ49Y%xa$jzp3X$w*Gu%*I6_q+hQR;Bn;!})6-lr^c{vn<8
+xZan9?7Pys?3UrCEbmj>G!bl@Icc(-dz9<C0dERhU%51(x1-?rl;9F{sN(9!U#4{t0UK-J?;v3zr<oE
+QUJgEXeiBJag@zxcb^rbz>Dpi9Ny-}TAe$GL2kevIp3MirWTg^nAAHV<Bl#g_h08T-A!cgpJ0{E18K<
+sUVUH<mclvCWP|uzY~|ZWYu<gQ3WD$hHTGAWAuOEo3X3r;E&*6ZyZzo-#-~H+@iEu|CcHD&GkP+wO2=
+3Itmqi<2~&$}_WK6k3hwLX3BV**2+pP7fc)%RYwS{Ce+XYOni?2?hyl5Gg}>Bj*Z9LII-R_;fp|x{Pv
+$IirD#;msI5ycMLri9p>oC=WYj*ok>B3PYgq7L*3#bkruVkNAL2NT^YE>hR`&3fnEy&`5zRzI<QjZ^B
+MoRxQi;D0a)0=RKMvDK-h~-#y2~Y0KOz<YdadMCEk|B+3krxozo=bP&%Sov)LL3ZF)5!KQQ%dS_QN8z
+6C)DWT)`cID;D?tb%HNwzNHs5dfs=#yj2JTddl8O-$verWVy_V3$7SR!^b2?Qp<Br_ne>PkG_XF%W3c
+fhj8`iO6RvH);gAM+}5|`7_-i7(~f>=8Cdog8CasI7e_PbJHmDeIP+<ZuNt;+iFh5_Wbz+SO9KQH000
+080Qi*_NBv~N`E>yR06hZ$03!eZ0B~t=EjKPPE;24;X>)WfX>Mk3FGNLCLsCglR7p=xE^>2pm6E|u!!
+Qhn?|F(y+>n~wD+WSCpxr7o&Ma+B8j&VZ;uh`gaoUbDG=a!1vH#EZ9|sfIX0u^z-vK{V$LgL(Xnfy;Y
+oTQ~9b!nOl;Mb-cSTUmyVAz#Vn`4RJ(w0m1l3{(t#6X>QA=>rDujfx8Da#7qQl<a9$p!GZ~2>#+_;3w
+S_1KEBJ-IHNicTJ54$<8&`@Y?!UPeHK?&n?5lX2o$d&a~hX@b88E6Rn1+ipqJLNQ3um39K;3Iu6@no<
+YYF<&>t5i>qf-IDokWoweE~NqZKGL9h?%x#B<lM9fU&w0SD>K<;O~P55rO^)|3GKbMFcGGg({GI?;-D
+<J@ni~&{{;nAw6N%>Mt0BJl3(QjCu1KYfb+Bj6wdTDnq}u1j0wR_Pj;0hUl%KzZJZpZ!E4oO5W#82ms
+6sO8t_~p<_V{D%lE9kh)+N}2m+%TO)465QmFZloxZE1a!t3PZfOSs`&p3~m&G*BCs0cR1QY-O00;p1l
+@>=C)Al7Q0000)0000a0001RX>ct!E-@}LE@WwQbS-IaW^XTLZgg^aUvO_}Zgg`lba-@&PR`FO&d*7W
+FHSDXEGQ|C<toT5P_R`1l2*BinRy_tF*;w*1X+X&08mQ<1QY-O00;p1l@>>(4j9~H0ssKh1pojc0001
+RX>ct!E-@}LE@WwQbS-IaW^XTTWprU=VRT_GYIARHtyEoa6EP6{E0&*%M4dJ%REh+mP$VK18j-xA>g3
+wni|L(h{E;+@^546AzK9g@a1uP_GCSTG?|OH~;VFTQ#@WcHY{&gJt(+Aak~B>Z1@{wFs&eRoFRlgr;H
+#8;oQ_7`c;UBR3gbq6|C({-Y?3Mw`^S!3S?J3ZFr+dKYiOK@5IJ(8MIj0o<<qqZDa!`z`a>OD_EFT%)
+eQ*cTH*)SblGG^df@vz#I#en1)D08DjqdN6^u`KF=AhTUVQpKn=fa#-$aOs@5yF=S4`YWtIN4fkiJ6y
+vB==<jh}S(jGv-aOCAUl7k@!tK*t*J<XuQbqR(CxgYlnLNM?=I?5}z$&c&FRhq}`V{M#6XjqZ@Ivn3&
+_kujPFL!H1*@$1qUsw7(^nQ~;)DSN|C**rL%6%})YfqN_s9-mo!IT|#(%G!WcK<w)m&Alm_6u4c?*p<
+h(w8j(oA3d->Ow6V8MiIVn4{0L<uwyrff6S)QZ0TInWkyGJkZo&h&2FTvv#dLrc~@`x8V{t2w?m1S!-
+$iCG%?WOL=4V&F^o8CeFIcjNi8k0`Igft;W`MiJck4^VU7-t8D}JH4`Yn<D;9tzr#yj#ePItSFk@qqO
++(tfo|ozo9YjIiYOoMw!okH%_u9l%ZB_scJ2?~M3vu>}NRP1HxS)#*ka1h}St&?oax&!=L`d1E!<&G*
+rk~#%q_f`QD$_E_>S#vB;;h>x*;WC5WfP#^Ro(T8akxCOju<r07<nnfPTs#e7w5%%nQ9^xc+#P*Q;+<
+vTUHeML-l_u?4JQpO9KQH000080Qi*_N2NX~o-Y6Z04@Lk02}}S0B~t=EjKPPE;24;X>)WfX>Mk3FK}
+XVE^2dcZmU+xERHWq%}GrxPF1o}C@CsU)lpDN&n$^AOD!tS%+CV~Dw$iPBqk>(E7bx3P)h>@6aWAK2m
+tt%7DvY)m%;`B000C4001EX003}la4k13F)lJLWNCABEop9MZ!dIja9?a?c4cfXba-?t$SmLj08mQ<1
+QY-O00;p1l@>>bGk<Si0001P0000P0001RX>ct!E-@}LE@WwQbS-IaW^XT7NJT|V3(rVR&Cv}@Eh^5;
+&$Ci6)HC36Pt8j$N-W7QvQkJ&$t*63F9)en&@j+5GSM^8<O<5qFVXcZ)(t2vO3lekvQj81Doy1INldp
+=D5x~j&CAbA)lJN+gozj<M7RJ@O9KQH000080Qi*_M{ckU>FELh0KEqQ02}}S0B~t=EjKPPE;24;X>)
+WfX>Mk3FHJ>MK}11RL6ue8Zqq;zec!JbiH8D`*S!FhOj8IAm#A$TH7Pu{8+&T6u)WJ(Qu6oA+HTT>2%
+0=OGv~~GW-rl+OgIsJM`NX`6FB7iY$Pg7KugV*;$wcm581?5l`xA0rUk-yG46xGR8<w$8D`*=D&bxkk
+hOK9EM=V&$Q7yvEz+MNN6XnaRbiy*JAuMEZId`AM_ezo;8Mk`?}=bq52Sz$=U6IDadfH(D<Mk?udy7>
+rEy-ASkFJ^MtN;{u2fzkr?}XKOtH4)>T)<`=eT%KCbJ2ku#4L+NXVph-pSH}Mdx)$!UE<wtSqx}DI6t
+MC2%u3A6@+zv9nTGEAJ&zsDm~{s)B=w6V6*m5_k&{jE%~C>ZpP-DP|?E@rJc)3)lCV#5%=58?5quitR
+E9h^xsE`dS+`Cu-8<_NZQ?OqG^t08G@qdk_ZK0wwt;FUyQzl<*pnqRx$|s?fg+QRkkz74Tgz>FaP;Jb
+$+{<9J8L(XM#DYZga*cOP$dWDF}^VrY;EQ`73k3r57b%+N-ke>)=*aA+MFVb}P}OGEMjO>wgmq<9Y)P
+|)kaUI*OUM6QM#UY_<2_`&|G=g@3wq;^5u(vF_Rs#N-OD~^R5)9dvZ3+sNjc~RKO@<g=D-kuSnM&6={
+a!)nk21Vuy%v5T*GI@-3OzTQ6;?x^MrR0UH%G0}B_N)WNUc=q6)wex3*><PMiETDIUfEjuAqxYQ`c)g
+Zn&^;c1q}kNW&S@$H9pv<F1|`v_!*4D3@xd7BMiqc4i<u`4nDNjBTNk?8uI=ZBbEVcJK_zM8p6#Dw2~
+Uyaszeq;I@+ZuhaZk6rAsBNQ;PGfR^6`GQ2)Z;6+dX750lktQ8Fbse|>EHqh1)jByUK3Qy0Iw`UQGE$
+evRBt8>aj$G4bLHA3XFgdz)25&QL#x490+^yy<(hMS}!JkVTrSg#>+8)>WhnW#|TJ*YSj%|7*8{5g0&
+L2=q0|XQR000O8_>~q%jg!*R`zZhbR96518UO$QaA|NYH!d+QGA?9kb960fZf0*UQbj{gQbe_U$&#bW
+66L<l`cK4b=3&tb#2|z~3=$x;!I<Y6#PIcv>fWlWRA$z#*VesAQ!3N!93tGq{kTU2P1^h~$4RSnaGd`
+%%dSsc7{mW}NvLfqcx}hA3B=QTyVKO$0#%1~ANLb<p84_?MJUec#~lQs5c)s=NAm-zbkphsl8K)Z;K;
+EY-wcrNCOU`9Zf5WfgtnsYX#Jx?<;=R@R&Ixpf8%&=ANqA!x>fK3Rmay6HGpgg=upXq$(0+r-~!9x_7
+}q#qyuHKBwL#9@O&s7BK`qu+^!G17p!yP6GlbF?f?_>u!qr0b&?#Ux!;vK!QuFP)`JT^u=?G84iLxw!
+t%?YZick3`WLKIU-6cbCpF9~x;Wbtb!AP;?xB|0^UD}Ex_GHULX>542cZNA{e>0OQ&rUN>j=+|EuOpu
+>L2_2!U+QsAV>R<XckxtxJh;guF|ls3)(W@ArOV&e<6jlAAWi&GKm%m&#J*{2sg~QoaN;ZO#>b}9(g-
+81a?1~y1rK);0}VZzb7lLf^dF8iSYFRBehwgJ{B_xJTnp08S4Xvb8)tAY?J{)K+0Z1qdSx!(Z6Ae+IQ
+a{2v;uwm$(!X=dg9k4l6*<`(Qp-J`J&^CgE3!4aNFujM@&z(Z5}x2^$o~1~-0%%7Wum%G2l_@#%^+6R
+}pJDi1+IS^3$}QbnKeYNbfxCht%b{`;Z&!5zll@#`Rb1>^CNJ>Z9o(9b7LG}tO23GLJ#A>8PJ0oa3+$
+~&kgH<rJHu)n8#O2V*sohOvC8z2yc_Hx!Eiz|cmHmN>)>iI)`_15J=(GnjoxQ6Z!5F-DEaUNk6)ZJH0
+kY5Z)n#V4d<24pj`q8^lagk*uE&|ayseoE0R2E$nk>0@=NPYxN8#f5LuzwvWv@MJT_RVe@vW{}#F1}3
+ns}@MQ><5kAPSc%(Ce7FC<Bp(kjQR-Kg}`6Gy+Epda7V!atP{Bp#F9n_kPd0fQ*S9W@co0HXzavc{2{
+b=1cAH*4&9Pg@e5v#L{Sl8j$DE_|0vqYp15WR>a`4`CI*|^-VB`|7R;;Mj-m+oBVeDty6$?KE#{Pk*r
+Dk_{CKp^u_loKc@U{?qQ`;**~;3V)-vICFtvHyn@LKy%fqfny|1hTC-I<<Hmh`LdFLsBJ2QEwl5r(sl
+yK*UA_vy`$~`K`-eJFi{_D3_E7>F8fI!`k5r3@PYCRwlc!wjFnNhQ-5Yu48n+UVC108n=MZ6iwcl~4w
+oz>9Q#aGYG;-yten!y<drUx5~&56TsF84y`^b0?#QJOWDM56Ke>8J2-z}>)Yh7U0{OjpYiC#5=I*K0j
+Xz%AvLN#!Cqhd}@c0gS;%e4@Fb-=PqN{|5ZWmlx=HC;$^^I_h*8DD%OyI<u#iPEff=`|xrAlc5KFtnM
+C`Z>|ge4!JHHcX)NVtTjFDaGThYocldLRMH?~psCdgZLG2Mcwk;OcWXS#Tg-)i3;pA>_?T!G2B+KuT+
+3|{&crG>Lz-k@CzaNEsp1TIOvGW8w@71bi3Wnv-w;G;5l)@kG#?nUoB<C?-FeorStw>z5p|j`!i<S9b
+KBGN**ge0DU>QC?_dZge?yRjW0&?r>c2W9tm2_oV||4_aBf7*l}B>&5*24-bg!4xMq4L?TO?C8e}{q?
+@*Dgu2d8e4uCJ!WxP2u!gwUDmULe7RZT$z*!#3{wkJ;@N)`D{v>Gv(n?Qn?voxgvcn^U`t4sx{|485^
+qkhmxy@G$gPIE5C`d!^s_rN5?9d82nI^}DJ0@umN2NsU%z42%2_Y)3LAS8`D2s6)IntMLh2g-BfVYTu
+%4wZ$w1|K0Qd&G9s|^S%Q3o*iSvn+#e*>O9M$!#WqNyN}>#GA|XB?oaYn!Y|9g*dZA5yOsMlO&11f|B
+2_eOzoUsC^=`gd~;Q7XUaMbgsborEYqH{r$X6#Dq&Clj(U&ieqGd^Te@FOlk6VOfq=N~p}l!ZR{<sl*
+4f>k)!C8U9I<kiLJ=oEAoUK0v2TFgs!v})wYIy|+^sd0rtMddNxVxtYe<vpXRj-RW=whgdLul4%Revz
+zAvs<)S;fz3V~n1_mSujfbHUSP-YLMXpRVcGOjj!f;H<H@?{cZ^?51HUfxkK{yybD!Jjkn^!omFni+k
+Hvr}+6B<UlG_Qz|pryh&eo3W2y7v}KbTXQMf`9y8%45Nr|u-_lKDajyF2bW<x(t=IG13gnTgmDEsQzj
+{V#g&K#Qr(2n9R|Vg(d-3w3+?5rcst3?gJ|s<a1JtY=NL~l$-7Oa(xd`d$+x1w91cnYLGcbF-ra9=wB
+egXnrhJ_T$2emXyMTam6-|GCQg&w3>1Ct-KMx6N$e1)v9{F*(AT)1g1WCYfl?qog=Wf3oV446UD+&P-
+F9AkFb}GO9b$#tH}o@R;W+p;Zr~2?vwV0hLS_^aVSl<=un2nhN9>-WZO%)CjmDJG(Hctq0&6?U-vsgI
+739~*?>f6R5K?6HBeoSrcR(wbAgz6t!^i+VJ%sg?U7RZz?0dC{cQ8m1_}8#Mba(whPlyBz$`{49kOIN
+n2#K&`(;IfWLR?%L^L3E#-~#PS<ecv?5J8Erk+)hbjl%wQl0_clHcibaYSmltU<5ZN$c}sk-17(}*>(
+c2{(Q>DD&2e-!QR8`KgjPADAhaDnwrI&viu}oZseKMDA<v8d1`dUkIsh^!vahKY4Q$-$#0yk3<vjVz!
+zZQ(a*85H-b(Kk*3l$W?{_X)ZMVdaRMKi%B_#8WH4_5cL<3<=-0@buCB+<|4>EL8b&6fjE8Z0c!Z#e8
+*6a69v&vPAIQ$N9JX*_iRl!p|52P`UqknPmo~#I@Pa)kjT0v44n>jyaM#SBRtKoGdvV&2DAj>wXcQUL
+Ht1GeLL~Av@UX>+a1MW>c8bX2A#3L(c@?oj9F_qixdN!{bK0QBqq2{z2yf-ex!;i#NPc6x0Y_g^TXwI
+F)Tj!K8SHr(r_V8PYpBY{tkp^R>QeU1^$~Dd_)H!I^^N7mA$_f_paFHR3Ml!vHMR^*c%aI?(@Pds0kN
+BJ;B_2qkdzz<nzo>ZLEk*<V{@{tBz*zis*?M>(qN9S%b@NR2Gem}OASk}k2>sjM%PS6nCXVKr6h!S7k
+@v2r;k%Bb=B?Go&ei}hV=p#^HM%EHI<88yIb@g3<Vj>DhVv<d`Du~ds6y^`f+Lx-l;|FeYpzvTi`dYG
+&UzGtV)d~9QM}RH&SI)t9A@zOGYUCUJw34eXCpNhkG8d!4{zQ2mo14lm~*-VnHSk9>UCl#Ufsl_Y1f!
+HG~4uZ@kO#Tt9E>Zulz1d+o&uu8M$<7L^|2036q-QL`F<+|jvF??k>tcM*P^-Dw9C-%Q6p3}xE;uM*D
++{o^_C__H!!6cvgNJWhg)Sh);-L8HAMbAxMNCoqjSpZmsy|DgBDoA=R1N)iy6>a|FnV7$~*pj)P4AP+
+OK<d5#*8Q#5Lv&i1cNDRlmNhL1~OTQ_xD2@N)NAXXVe!8FLmeOr3q&h^sF<r4ajod$!AhquMs~~aPG?
+J1i89Vq_YwJ%){uv2j1-1%K-bALW0EIMnIMaY|GZwK+)+2p5IZ~AxJ!+hf8wZ7b<3j(C3~u+-s;{Qbm
+g`wL-#x(QZk@nO+^k$iHy$oZt-8utg$<*gr<`uNACl>99LZ1?*f&C<cO;^}8|39aEH>oZo0uGV(DG>*
+y0K}UJyPz1%lk}X$Q|(>xqks~ld`3(>O+LxW6~LBWfi~+uA!zzIisO}LryaYRgUk;*?0(WdI-~*+mSf
+=E_#1q|M=xor?IAZrAq?oU@L=>^kYc(<82DYC2(W<@|b23ER5!0cXqskAmUR3{so^V?hwx9t8dCz2=>
+hzeR3^;mW<p8&k%tLjiD0Ad?F|M*r=;U`f+sJLHH;6^bd5^j6*+kq5G=&`W~6Gp-?=<tj&YP_xp9QgQ
+hZC-3`}81pDhP3OrO=(5*7YaqJuP-hC?I^~0<jbU^bV<>Y$FNg(bp3`Wyd=Y<ag%A{u9n@E}>E-X{vr
+X7e+0__*}Q0HOwhK(B$*|SFeI9E6hdH(*gw+-eR-PTJ_aSL-zRX@X2I<t>E0;gc=8|-Hwc=Z$YVm_cW
+rfT}`0;~by>=pGrFOI7NaY9<{n5!i|il>>6cL<DsXUA>HY~uRMQW7OtPHQq!*k4@0xgJXkz(gh-wNTG
+X>?}8;bw41<W#o<BcQf(}{!O^9z|o-Yw4F7**|()R5cy~^IFjiQl(2G#GiLV~-I}z(9sJ&({DkfnP2z
+gtkos=n4nvoE<M2TbMU5_bD}H4{Y`Jmyp<LubBtbsRt^TS5y^texRf7g4X`6=oZn^%XORFsO2j?m296
+xz3u~#8F7sUfQ5@R(d{&K%Yh;;2A2skD1mSeBPR9^2hu^y9Vw4Vv)-^NyK)z51qJrry3a$|iP1(l>f2
+Otv-2%Z}^=8g>j0+wg~VnnD^al6ln_Y?YG`v-3E*3w4nRhss(th;kar0pC=lGd<EgQVSwu!WkE;>)3b
++--T<^dkoToB6+5bn@e1^v(kwTuY4)NjCSBelb!Zn!8rsV$)e&f$$O<wxR714E`5}DM>ezH?IHcWs;{
+DJ_D{AwGtq5I^R3GQ#vp=_X?rNw`?m1eAYr>$=K8?gnq~It5Ni2YkcN!yb7e1dp#Fk<&Kz-v4>7@6mn
+5ZfWZaJ6XzzqwESj~_io@<9M8Y~^Yt}M0j?>Og-(pJ4nk0-fZ=1yLXX@O8mU8+PP*9gohVXsQ}yts=D
+rJ4T^Rn|F#Z3w@IP?$?rU|4)Dil4xQ2bCQ=e$6IP@mbgwdo?F2p^i;#hPYOAF&Il5c_hyEO4+@W!+Vz
+8S@BMQ`{XV`)eSu*0IeD4K!2I7I~>80W<U&-;-Ai2j5aI|K!PH(CGCyoIq_1oC&&H>aa^!3VAn#UR6`
+lV^{<cV;S&?yLTQ7Rou@Eb>{ie@zn9uX!(fl*6Oq^r4w=jqupVX$$eqN*gv!uXSB0*V$p~+jQWLI}&~
+G=zYx)KEJ*8$_G!u7p+)K6z{Aeqsq%-wwhvfaV^AKZiTk01}&_6o^OtYzjtbWL*f)4HNuMQtEhzKe&9
+IIDLj`BpB;(<a6MeUXfCEf_j?zf2-V-}M-cfP)8rQUH?}Wf0Ch1H4dip7c`z=Yk4c>2RD+j2lma3P3o
+6?Y={4Glv@N0k3Co-5sp>Y|hIsS>>%<qQIWbqu?Kn_KAIKFo8N#s!YAnok5AF!?TM76|@zL;-ha=~4C
+50h=<m33blm;d#i;;7iI1;elRaH91<}=zoH*$Y#lb;0db`KN>2}o!N_#wMqb<}6}a)RZ1XMa9F+TcnY
+=8oBm#F3YGDE4jadZmcNs!!5S>h0+5`L2jl0Hc7-xX@aH0deP#d9^>IKE`2$Du`@8BUHbGVdA^A^~&`
+0toH%AB|bQ!kT`r(LLjpV(Uo-%hn}6T>IqTZp1A7!aOHb+vpe-(@qbCvj79O$FqDO-<YEoR>MkbEO*f
+#<P1hd=!EGDuJ_UQmhcC!2)X-;}_j{5w{L*(rAKUaSHXTp8Op*{4AN^x*Yg6UN6x4C0IvoTkI3Pm3gQ
+4G}zxk|nu*u7z8TfKW70^9NB8INrQB&$=d?XjJifIIY(Eic)w}R+9$@l|0aGxJ9*jvVEFHwHi$E(Ky?
+3h@@EC(h#2lw-R6Deh7M*{62>nuMji|;MGfj7$w@N_FRsk`eDDlgV~iNO*{NT{TaRU+xcK?Fg#JRa`}
+K-QZ$C*I<CQTzF`Ec|Mw2fZ&+<}e=*dZal@PjfX*fQCEhUT{p!1XS*wEjjbC0(TVj-ctXCyeYK&=a<*
++w`6FKp~CmlIa;{|GXv)$ka#&`iGti3lO3U%4lbeoY&75~^$mKN2Wj^OJd2e*{V3r4Hardq79>K1P@$
+Uu#3{Y&J+0E69$9<ZF3BANzPCGn!8d8}1$fj=3RaJE5XJ^k#{2~+l!tLTdM$amN<IoDg2ko>YN7K@FT
+cm0U*LUPY^v<5hrbPN<~3-`uUiUG@s6b1lo1GPKNZM%jigD2`i_AVPu^h|3Vw&bl;2Fg0u``5-g`*bM
+(ZHxGa4)!Eao11zN{yDPMZtzIAcsQJSQJ|E21|UnY(dX#hcf-UOj$v8+#2p3xEo%T$nw(bpAQNdt!|a
+DQn%DI&IY~S{PeikK87X-wX6t?9UtdCRqU?*IeTLcq>dYDdaLSh<jc%eb<e>CEw2t95blU?<f%e7WvZ
+_#8gLhOs012tYa`Z5SZd(dOHbhj~x<byfGlPeO$!!fj-aQeE-Z_WFP%u>^_uN6k<Q(j^kh<<{Od@ZVT
+8<jBZx<a2#|&ae!tNi7V)<Te(1j)OYB86Z&bCe(b*+CUpfNCqET6O{$krJ|e=^-d{uqJxM(XF4Chb{P
+d12##SnV?`IJHLI2!x4Y)&R-NifJhY>W(^Ce=LFs|X`f@mb}Ens3Cc{2y-!=^?c>V1tidF=e^zJDn}=
+OsCr&LvPAzM!S@q=aaUW_cG^o6eeH=qhL`3RHLP#~Cs7U5GSY9Y2rFe1&aQn{T0#!QSgffUZ$(+I3c`
+`&jXDe-J$FMs$tSkcCb^&rZBs^e3|CzE&8XK)`I6?BiHDJnsDiB@xPk8-OAqhgL2O84~w2nKdNd=9&P
+BZ$SNetUjK`Fz$Yp<D`)96-Lii7_&&Z6l@@T!23f5^tRROKzfO^U=zyNdlTdra1s{HNAGaex+YQflGr
+ysgz0uFDJuiw0g*ClFWMB~O$^GwK72fG-a(<?0QXH=RUe*M(#?5pWC-Bym?(z8Vs~jOSnAcjSFUZ$y*
+DqsrJ0Jh`38c%g}^Rs$FLv1v>b931<9xMyMt(qW~VCHQAMAul6(xL5{fHuGN*Iv-phDfE${WuFXW%q)
+oV%3g@Fq6#jTy$$_`RgoJWR37WjNk*Guw{oJ*u4n~n&dl@$qr-$6I8+uAqsUL_>l<FJG9dzH^yH#%z-
+(W4T8wIpzm{6adNgdREWF!X&d=^xN>NM6$;3lG6oxd;VdTbg#f=Ga}moU&fWoPIy+c^}&H3{?b(?r`G
+WaKBLft>W*tNEepBJR&DrrzPuE2sA2+V8Q@6=)s&jDeZY(yT}W*ih0mzg}f~c5=Fj+-hf|cSHEEb<X_
+S~1`A%6IPC)oWs6K&f#aPH)zXK9(o?jiqp|yxQvN}G56wwSFejm2@^eov>q(`>RLKQRRO@p?(Ih30(K
+Sd4rb_Ki=f4+tFSvD{eu&CO2^BZGF$@I+RNR2B${5^6$hXbgo<9>r1mOxlr$>6&3J3zCzH_uM4|J~gL
+`cJ1tV2WCdXKSoD5aZNS*W^)K8%G$5oWZGrM4v$7<=C$_=TNpPyHd2JkIHu>Vh6%+Jf(I4%k2fa_)_l
+ts&VFTwaH1UP7U3yd&Z7{I{%w@I$lIdCYs{j%aLdI)d%r`Ihh07J;tiRCjV{b23Pw8f8WxxPvL^eM9>
+faKFWhw~5HDXly*|>=?-i@F$h?i3K_@k3~CkSk^5gjTvqL+Y7jbOcZ|Kk@*Mu>+_(jNh8RB8bfkS?}>
+`0_Jv72D;twev>ql6qHPgU)s(^74uRhraWCj!r0o=0WY=ZA0(IiEcnbUzlCx!ak?I=kFzUt~t=f0m;k
+;~>21R^l!}regl3;F0u~c<%YT90G10I|Fb{rB|Q&LD0lT^6OSa31DO?yGex9jh(%>8v{b#J+MW-{5al
+5WxWwa=lnOFIn#FxOey>f~{-iQ9TA(SG+X+&>dMo47&v<#yq1yq*|a(Ft+tYhiBhS9=rjlDWvN#~zU$
+>OsqC>jSsn${LvZF8F<-^3oWKwtv5xnyjzE3LIoYjLB>7S|VHG*h6*{Nnnt9^zhaYkxq$wZS#HX+mQG
+r*OuwL5BcqOqi_ZQaO-4S2wqCSN;Kky9()L=?&SSg?iWTy8}3Ze&s_1xWPg8#{%eejP?(~O`vD)V07;
+R~IfF{j@fqCHl*k`3RHaK+qWRMLaeVl{BYSomo*n;>+Ris<UE@2bzDl=HWLT|Z_1sVif}|f8c}*@%bs
+$ZNF)?zhb>Z(;{Fjt{IK*8&HlHRHEKWILau|e1u{>>(aX5z&-%84SijEv*MM2&iuQ;Wgj2%LJ8*#rQ{
+ls@i$r?VTN(LkRx!pXIZ`VY?N~jG=q&t$Ve=T9V9r9w664ZMu;x~MCuqozeND52WPtxi`rB7m=od~8p
+Q8UYlW}IqC11gnt=<pQoNQclAOFmr%klew@`vHjGk-lX{tSs@R6Q)g^i`)TI=3EtvON$kGCYcR)NRU*
+G`F-Bx&z1uAU3~h2=<~KKU{p3~5S&V^RCHYekXl*<+jIGa=V*gYA5@(L5NkpGCY=!EkJq{1Y-PhIyYc
+cPkd{3Wg_(NO`>W@>gmi*bL^mFfzArQ5ytrJnlIxc9iT5)_-?N3i|B0)`uD*5(%`M$ob7v8k2f1X;J<
+qt>6%_J9!JK%fk~&!W-Da&np2dx;e{G8qX_0ZKGg;iO2`Tb7)woPW>d94&>v-O`X!ww3FJGgs-Vs|}@
+y9%)pM73w*s+N&Tt<6Df+YSy4)7p1NKm0bkb%$moR2Ws1vY;g{T+h8x0`;;mUnM~`HJ=K2`i7~ayej2
+tf@oa;59s+#-bjX$u428P!>KHTjSX#{V&Nr4b7^Fpt@sRH#yZF6@U_R3prJ0WfYC_AQTgGaStswv-f9
+Yes6gFmTj2chIVj{;NGHAXabdU7#h>jZcV~5K*}4BLeM6_bQVmvEr{a|LCN<!4*x>-X=r0}fmQr4Ua>
+J#=9@hMx+wWqf=1QrSi{8WV4MupxHm`bIkZ0x%CWZv8u)E&G92?k?X;S+?~BX8`AMf*7}KfTopXH#92
+Zua&^iZldvg)+eRJmD*gg%;w5qJuT@tZz6R-VMcIU<DHX4*nnTmm{HK*2yJL-b}qx$+&x+L_v@bg@fO
+;8(AZ{8wDLid#zgw8&kF1P?M$I8TTk_<<%;uA@*e?+W5UT|63zs=;`mrxxGb3yP_c#}g1H#g;dpPt(L
+{Z7;SHJMUi)N`fjcPN2;TLONjYW!+=-5Xq@p=?0S<#An9u#4D}M?x|%nn$}yg^|z;8EqS_3@O{7sLdn
+)l&Ns5*c*%3?NYb;Q6IdHZ&7=ipm6zOCUy{6iOj?&%g@J#KGD^wwR{d?3M9<%dJ}oXpNpX<tG{$<QKb
+GmX_Wf`rabD{Xn;d^W-a=z#<|^JbcTiNvL0ye(Pl*{ow#RT`~495?@8k>4D)wJO^_D^Vg#oiaKd1Qa_
+kuH)ShP8nripdO|Z?__WlG*(~oVw|7vpo^3r!ol)e<$V?Bv#6{Es7b*69{bejQ@Yoj$`RS4HDi`)gM+
+Xe~!7h>TD<yd`f&&zEZjWt0j$KQwYE$I_o2)d-mSMW+Y&D}DcCTRDkGoSzHcK&gkd9n_Po4ykQIT2(D
+5*$udOdqNN*^0tiJQJ?Z`j%e`v|4A0YXn65JZJ#_dCV@{A+4ZKYad2PQBY=qboDV1ZTP%ZZ4ay`Qcw$
+#!<ZLG0}%>)@gDIGgWnIpe@|C>-ziP1Q<#i3`|=_;cu2HxzqD=+!x>R@o^emaL=HnZ_Dl(U9yj`9J8q
+2q*IN1_=<8XP8uk{+qC}HT!U(1TT5;>eEOVY&O|FZ>%^{Y}h}8Q5r{6Pv5H82lB;J;JM9>O9*I7B6Gl
+PS&#1G=B1d?MkyF6EU$hhC~E%j*|^n0fM+u6LeV4<+BSh4XO7yDE@qWkvBr>a-s2mrd+?M!1pVvfY7C
+<yAiUeZ^L-^?bNNQ*$~^Z{_|WM31tbMjNmjP)9Y@0_!8!+1VGqO99nqKE&yGU>N$@rlf^l`@stbr|ep
+eqn2650VDw#_2<BjN6J1qm^3gHB~xtD}&xIBz{jeR_U{e?-qX*m>({GCIAP|`0c{1#W`Di+d6=Jh%ym
+$IRx%;HO?a}Fiq0Pn`+w1**`YoI7H;90TM%Sz8^Z#CbGM+XHgP49OOJz!Rt)&4d5RhmQd4uS_ttD#@`
+PweI^^bEkk(7mwo=AVWb($#(gc*-Cp+0>lU*1EO~jHfMY<3Jw~Y4YE#4g4hG-P{QQQ>e_jss-}`!7$#
+)aVGe%cPJOOTT!O6Bq7R7l?p-6`O)hH+V_9#w`_PmOY{j2SM@@+*|^%vc%9&D%HL@<cHw<ZXZkw+-K&
+wI;#zym$?GfnFZ8a#neI_;=GP-vgYn!0`s)cK)k=E;W(-JL;^h~R-Yt?U*?%rQ7xizKuH>*8*CCZ4+{
+2=uR_=_ljYO;Q)|xoO|d`;*ePr>lE3X*m#(460bYO#yib>MUps)be*I@_yL-d$K4Ei{O<A#BcFFyFR$
+Zv<-^}G6!FiD!R!{037FguwF^5X=pyOw|qb%{~AyJAu7VA{&3Ic$~Bs!$_iN2s>bP3hjN7J$DSM|=Sg
+Aaf%L~9>9(32+`;g_tK6SdPeYY%wf*gNp=lFjTn7E7Cb%wOd*w>Z=-DO??ehf(Rc3Q++UAtA6(;14Lf
+>~pzi0dVKQBz@tdh*uKi$oGC{=S4%q2<Ct<3K)0%$sA0T0SyK3z`wafcDuACIdG`)2cs*9OXfss%2Z8
+kN~MuS-YJ*CH&?NBz>w7nQP$A(lr36M1!$n>hOK%KgRdpRKU}$7lNfov#{-@@=8<FtyK`qkIhDk=jq?
+0(xakhOB*hX!rCc_4n`u`|Q~tw5?`--%0#;%4VrI`PlcboOOMc<*1>2F6|DBr29t>h=4W?G&NuwINuk
+*38N)NT#Oxs{Cmsqmuvo}5zU>LE3IVrLDe0B!N8EXk;w(iP%r26GfCX;$`#mRcr^CAKVRU#{!ni@s0#
+xpNI&#bP0dx6bCj5J?~!{Pxy8g?Spu;=a!$5}a$=qlHT3a1o^~Yl@7=0TydPg)U;v#0So@B@km@XiZh
+0y2IzKU4tMU+ePSU#vw(1>!zTpCb{-AL9%-0uA=?;G4BwM(t-15r<JHBGc#|o;uSgdITYw+f>Dh|N<c
+&gkeHYsox?j_1|;ChF?pVI%oe2TPeim(i;!F|hJ(FrDVgh(U;U<v9$r7|1|@%cn)kUD`f(VDu1c6r2S
++9q=Ty(#qbX`b6DUk!fk_RyZco)yEIW6pfsx0>=qj+_D0W^;wEd}-BitQ{hj-l6YD=KqE6%jB$HXFpe
+?e&~3UR_VyDAUJpLHW$@e1u!gA!)ABft3y*iCF36_l^=|s1Mq6YGdApR>m!z|m`84U?l4>q+(?bwY}z
+*5rL`&uIXYWn0pG9Q|0~_6D6Gkyvfz}Z=5>g|oB`TcHtFYf9|04@^W;Ia=o!j1wtT)c<G-8FA1@PmJy
++rD9-=Xj#L9v+lkuKq4}5z^f>V!sw%<h7T}1dsk%#A-MBXnT{0rIBYhP;%v}^#L21M5%h&$4A>l{`ZP
+Kni2tT1pO;!M0r5F-cAv-=?O$D#KJ*^riDJ-*rvmRr$eorePs(j1qt+&Q7z+GSMju{FEjnxr^}vsL>i
+c}HRBe>b-6%d1>e{lzJ^Coi=msG}+@ttGQFp;txs4w`9CpI!LjG9B_m=KO85zu(>TW%wql3T?tZj`L-
+IbQWF|^K;Vby_ek3BpNUh9^nI_T^0AZIo;1R^$YR2Wl@B0>xK|>DUA@82em_!$*z$J51z~`5jl>f&#`
+c9=?__N_yyeR$)BaxFU+V5!w>JIa3-M|1^dRJCb;e;mXIPgiI0wFN%$%7nRs0_%DYW$ZtP$L0pIYRXJ
+%dtK~%7EE~pNwLvwqfs>oPW;cE!qt~dR}*SGdoH=c68p6ea;bLZg~So+*KDPC=&9tEE41pR9BVR(t`(
+8^9Ug@n_whE*-VLInq`T=3wye;!}>DQ12l7U_p5kj`;E1})*L5p*&&1>U<0&Ga6?MZ81qXS!l}5{U(;
+M0W&?y-!{rx}E=e%w$!%ecdFh<@8Q0$UNPqp0xs5A&o$xCLnn}dOAQxtYSP<2KtVd)YI#(!=A_&^0Gf
+m3s&;-a*N<_NJ4zoHceb~a6%qf>+FVHPVP{Oct_jn*H7~|tfaq{#G6Dgf@(nYM-yn1fIFA7yx-(Ryg%
+M87Ye4x_jfQ!zC*rWp|uz{k{$IR%qc=^UK(+Gk$@+&rxBRTq!D!JtNH-bgZR`V=s)&t+%NaP&t>9QqE
+Zq7dbu&;z8cne)}%{{4q=0~Do_k1NlRpQC7-t_KH@!9%cqN<f!>9M%37TtvX_;=Vvv62OdCFoGGVZbj
+~(O087h>2<j>>NKi|yr*O`v59ud@g!qI7zokJzJt{BszjJC4fg-af`e@3nX?7M46z$&-<dFA$RwD%Vu
+G)Ckpj`KiDmdXIQa+qRLFJxmbGg`pYaatbMKptd9f9BY~qroObyT9Hq_WwwDzW{y;24P8>6U4yDopR0
+Sa#)Tgx8Y-_)2i>62^}WhdU)I!<N6&!{dAWt%6|214}QAaq+|M!Wk4%WZ%HzS<i+VWCqXL0KZ6z(-aw
+9TH&guFjd@~iKhE2UTf@%p!aAICHlxp8R+0zoSk>}9PAv!15r0;jF%9_@)3qIi6W~X%&+9@0<J!04x>
+=7R(v~-ur5uh?K|@n-tjwRHia$1xvk5G4tDS$j$xpzKck=Gnu#DG!5IS;-%LKmLInZg9Oi>R#1pPC#o
+&fX|oRG8sypxBbAHlv_w;kS40?r-9B)oVjl~zZSOc^Werqbm1QbQYPCnMnGl3SpHeoF8kXJx)J#nfXl
+)sw5+!iVLS9PaHMMHHFp@4*43TIxO?JgYC}CbM@iPX4@4@)w@lG(q~|Bp}-dTqZ<0e>jW8xny<cVrih
+dDz|&EwCpOFXPiJvsin3k5B)r&{1@7f8$7Qb+?SHxMHi;Ll^Iu=mCPsbu;-eyf06VM7r`*Qi@;=w+zv
+v1-bnZhD+rq|^xvhkI!sy^pELGShF7P=u_o{z)rrf8YvW<G_y`xgKvcZcwyX<b9}u5z*Dw7LzP51hoV
+g!u8Hw5sc8XZxE(vNAk;?b$fkg>)HA_cEWDmRMTRlX*;uUoqe|+tmhfG5rGMP`qwldOYr`jx^TqS}KJ
+X{LTGE-kzM=PL+-XXur$iL7wX%x6a_&R<4tvkETmBr7uK&Cbk06}SBFKkJKx3%`X!|FMPu6A>?=}Zj!
+;3(tkIm~z@gd^(RK`t!nIiUeYf~)vw-rO_c09R;py|bc|GSJIAocNi19{!j9RypwPq%B#nN{b>6aAOT
+c-`=iE)xg~mCDSw7JH8>x`SE#O<7YATMDyI${cvhH^i4>+oG%j>F8quN&aCcz_#88bE-{wUtfhW+O{}
+Yo`sD?PPpCNkuu6;gq08y!E02tq8EDvVE~Z{BWtgET(vME)7}x7qjD)<~o{0PhrwluPeBPLRJtEc|q^
+6(M%c|wGKKrNRd2twUN=A_C@}=|SW7McoSdH~du70BRqxZ$@IUZIxkp!P#GflgjRofbdz6G`07>R0h-
+<Q&SE001;UVglU^zR)IfcOveANW7dGfF^OMfLw;xeKEo)$36?|EKGRpXeEJs2$M6vSd^5`uz&m_~Gy%
+Y{H8H_R8Ta!6i7VU;Sn)=>I+*{Rw+3QoF@z>hyWEWte|~LqLdHdxsCn(h@p3g>0>MdMvNJ1T02p)XRX
+hJX{tdd-vUQ%iNV_=_~z9XPg?8L7SV-%8`eaYVzRy6^`d^{-_wutdQr;K%v$$MK_WAcN_2%>aVxrWPM
+%zgf0OcVr*kAvkV?ifimP=%%nI?pmj<cZr=@N+4x{gQpTnzsc)bguls;{rS5Yha;(VRFDxl($Jnt2G2
+gyOV40r`vgZJQzdmx=-~D|l`xB<lF^bOac3=N@s2isUADbL|nfrAd#}Lh{dJzF*Qq@~bM&WuPOs2buT
+1TtI8lK1L!E5mj{TkO2I8OSBM?ZAX3a<_8&Q~18yaMC3**M{6wZ9fUJPeJ05Mv~RZqFAuY^(PF08mQ-
+0u%!j000080P~d=M}*7(0hSE_0Inzi01p5F00000000000HlEc0001RX>c!JUukY>bYEXCaCuNm0Rj{
+Q6aWAK2mtey7Dr1-q-8S!002k=000jF0000000000005+c!wmocaA|NaUteuuX>MO%E^v8JO928D0~7
+!N00;o{l@>>yz1OOy3jhFlDF6Tx00000000000001_fg=w90B~t=FJfVHWn*t`ZDDR?E^v8JO928D0~
+7!N00;o{l@>?ZFvLBk3;+P(C;$Kv00000000000001_f%zH$0B~t=FJfVHWpH6~b7gWaaCuNm0Rj{Q6
+aWAK2mtey7DrVyt(^l9003<{000pH0000000000005+c&L;o>aA|NaV{K$_aCB*JZgVbhc~DCM0u%!j
+000080P~d=M*_3X#M2@G0Kj|z01p5F00000000000HlEcH~;`}X>c!OZ+C8NZ((FEaCuNm0Rj{Q6aWA
+K2mtey7Dw4?P5ssZ008L(000pH0000000000005+c09yb6aA|NaWq4y{aCB*JZgVbhc~DCM0u%!j000
+080P~d=M}lBlphYnN0Oz^@01N;C00000000000HlEhT>t=ZX>c!TZe(S6E^v8JO928D0~7!N00;o{l@
+>=!`MGmE3IG7~82|tf00000000000001_fpm@l0B~t=FKlmPVRUJ4ZgVbhc~DCM0u%!j000080P~d=N
+AFROtn~!|080@701*HH00000000000HlH5m;eB9X>c!aWpFeyHFRNTb1rasP)h*<6ay3h000O8^OY7y
++pKr?eE<LeoB#j-6951J0000000000q=5pT003}la4&OoVRUtKUt@1%WpgfYc~DCM0u%!j000080P~d
+=N22h{*OVjx0P1xB01N;C00000000000HlGip8x=GX>c!hXk}$=E^v8JO928D0~7!N00;o{l@>>tv|7
+Pq1ONcc2><{W00000000000001_fo{YA0B~t=FJE?LZe(wAFJE72ZfSI1UoLQYP)h*<6ay3h000O8^O
+Y7yhqJe*GdlnPD>nfE7ytkO0000000000q=5#>003}la4%nWWo~3|axZCcVPs@-Wpi^baCuNm0Rj{Q6
+aWAK2mtey7Dr$n2aRR{000F8000;O0000000000005+cZv+7VaA|NaUv_0~WN&gWa%C-cWo~3|axQRr
+P)h*<6ay3h000O8^OY7yA$z<E&j|nk^d<lR7XSbN0000000000q=5?t0RV7ma4%nWWo~3|axZdabaHu
+VZf7oVc~DCM0u%!j000080P~d=M`CG)PT?E?0N`@~01^NI00000000000HlEr5CH&iX>c!Jc4cm4Z*n
+hlX?QMhc~DCM0u%!j000080P~d=M|TE-#sUEV05k#s03rYY00000000000HlE+Edc;<X>c!Jc4cm4Z*
+nhRZDDe2WpZq3VlQ7`X>MtBUtcb8c~DCM0u%!j000080P~d=M=lfGq{amR0E-U*03iSX00000000000
+HlFpE&%{=X>c!Jc4cm4Z*nhRZDDe2WpZq3VlQoBa%*LBb1rasP)h*<6ay3h000O8^OY7yocgHmt^fc4
+E&%`lBme*a0000000000q=9EN0RV7ma4%nWWo~3|axY_HV`yb#Z*FvQZ)`7LUukY>bYEXCaCuNm0Rj{
+Q6aWAK2mtey7Dw@rbX@ra003kV001KZ0000000000005+cS2Y0uaA|NaUv_0~WN&gWV_{=xWn*t{baH
+QOFJWY1aCBvIE^v8JO928D0~7!N00;o{l@>=5s>bjv0RR9N0{{Ra00000000000001_fsr}^0B~t=FJ
+E?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`ybAaCuNm0Rj{Q6aWAK2mtey7DvLpK6;V>001`u001HY000000
+0000005+c{5t^vaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJo_QaA9;VaCuNm0Rj{Q6aWAK2mtey7DvlK
+7a~Fo004|9001Tc0000000000005+c&O8ACaA|NaUv_0~WN&gWV_{=xWn*t{baHQOFJo_RbaHQOY-Ms
+TaCuNm0Rj{Q6aWAK2mtey7Dqt>BlV#J004Lh001Wd0000000000005+cR7n8<aA|NaUv_0~WN&gWV_{
+=xWn*t{baHQOFJ@_MWp{F6aByXEE^v8JO928D0~7!N00;o{l@>>v9aWsL1polm4*&or000000000000
+01_fjUhA0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%geKb#iHQbZKLAE^v8JO928D0~7!N00;o{l@><~C
+W4j62LJ#q7ytkz00000000000001_fi6=40B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%g<Va%o{~X?kTY
+aCuNm0Rj{Q6aWAK2mtey7DtPF#X`IQ008m<001KZ0000000000005+cI9mY#aA|NaUv_0~WN&gWV_{=
+xWn*t{baHQOFL!cbaByXEE^v8JO928D0~7!N00;o{l@>>y9aj9y0001S0RR9i00000000000001_fi+
+wK0B~t=FJE?LZe(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7y!K
+ViQTLl0B%nbklEdT%j0000000000q=89Y0RV7ma4%nWWo~3|axY_HV`yb#Z*FvQZ)`7PVPj}zb1!CTY
+-L|#VPj}zE^v8JO928D0~7!N00;o{l@>?Sh{jZ=0RRBa0{{Rn00000000000001_f$w4g0B~t=FJE?L
+Ze(wAFJob2Xk}w>Zgg^QY%gPBV`yb_FLGsMX>(s=VPj}zE^v8JO928D0~7!N00;o{l@>=YN2j8v0002
+t0000W00000000000001_f#GBU0B~t=FJE?LZe(wAFJo_PZ*pO6VJ}}_X>MtBUtcb8c~DCM0u%!j000
+080P~d=N0+05?KA@b000R902}}S00000000000HlG&WdQ(iX>c!Jc4cm4Z*nhVZ)|UJVQpbAVQzD2E^
+v8JO928D0~7!N00;o{l@>?-akwnw2><{tBme*-00000000000001_fi!6W0B~t=FJE?LZe(wAFJo_PZ
+*pO6VJ~5Bb7^#McWG`jGA?j=P)h*<6ay3h000O8^OY7y?qbWv3IYHCJq7>(A^-pY0000000000q=8s-
+0RV7ma4%nWWo~3|axY_VY;SU5ZDB8IZfSIBVQgu0WiD`eP)h*<6ay3h000O8^OY7yv#FkMF9iSq0Tci
+L9RL6T0000000000q=BM!0RV7ma4%nWWo~3|axY_VY;SU5ZDB8WX>KzzE^v8JO928D0~7!N00;o{l@>
+>ovL_)c1pokK761Sr00000000000001_fed>A0B~t=FJE?LZe(wAFJo_PZ*pO6VJ~-SZggdGZ7y(mP)
+h*<6ay3h000O8^OY7ygBg4GT>$_9MFIc-9{>OV0000000000q=9sR0RV7ma4%nWWo~3|axY|Qb98KJV
+lQ7`X>MtBUtcb8c~DCM0u%!j000080P~d=N4dg6YELQv0MwoU0384T00000000000HlEqf&l<<X>c!J
+c4cm4Z*nhWX>)XJX<{#9Z*6d4bS`jtP)h*<6ay3h000O8^OY7y&X<4#el7q2bie=r9{>OV000000000
+0q=A^M0RV7ma4%nWWo~3|axY|Qb98KJVlQN2bYWs)b7d}Yc~DCM0u%!j000080P~d=N8$#~TnQBb0Ps
+-&02}}S00000000000HlFa+W`P@X>c!Jc4cm4Z*nhWX>)XJX<{#FZe(S6E^v8JO928D0~7!N00;o{l@
+>>zUvLR8G5`Rp!~g&v00000000000001_ftK(A0B~t=FJE?LZe(wAFJx(RbZlv2FKlmPVRUbDb1rasP
+)h*<6ay3h000O8^OY7yp$jB<Ko0-_9yI^}9{>OV0000000000q=5n>0swGna4%nWWo~3|axY|Qb98KJ
+VlQoBZfRy^b963nc~DCM0u%!j000080P~d=M?Dd)0*wa%0E!p@03HAU00000000000HlF`G6Dc_X>c!
+Jc4cm4Z*nhWX>)XJX<{#JVRCC_a&s<lc~DCM0u%!j000080P~d=NBEuZi)$kQ0IHP$03QGV00000000
+000HlFPIsyQ2X>c!Jc4cm4Z*nhWX>)XJX<{#JWprU=VRT_GaCuNm0Rj{Q6aWAK2mtey7DsQxo_Tr;00
+4I>001BW0000000000005+c@Ld7`aA|NaUv_0~WN&gWWNCABY-wUIa%FRGb#h~6b1rasP)h*<6ay3h0
+00O8^OY7ymRfBkJP`l@XEy)<9smFU0000000000q=Beu0swGna4%nWWo~3|axY|Qb98KJVlQ)Ja%pgM
+b1rasP)h*<6ay3h000O8^OY7y;{G(?r>Xz|09*k88vp<R0000000000q=6uN0swGna4%nWWo~3|axY|
+Qb98KJVlQ+vGA?C!Wl&220u%!j000080P~d=M{H`n3{|)Q004dg02=@R00000000000HlEcCIbL)X>c
+!Jc4cm4Z*nhWX>)XJX<{#QHZ(3}cx6ya0Rj{Q6aWAK2mtey7Ds=j6_hMF002J8000{R000000000000
+5+cj@|<RaA|NaUv_0~WN&gWWNCABY-wUIb#!TLE^v8JO928D0~7!N00;o{l@>=C7H%S}82|vETmS$b0
+0000000000001_f%6vx0B~t=FJE?LZe(wAFJx(RbZlv2FLq^eb7^mGE^v8JO928D0~7!N00;o{l@>>S
+MSulFrT_o{P5}TL00000000000001_f!i?z0B~t=FJE?LZe(wAFJx(RbZlv2FLyICE@gOSP)h*<6ay3
+h000O8^OY7y)W-_X6}12W0B`{S8vp<R0000000000q=8u21ORYpa4%nWWo~3|axY|Qb98KJVlQ_#G%j
+U$Wl&220u%!j000080P~d=M^Il|Ed3+^0E3tS02}}S00000000000HlGYjRgR3X>c!Jc4cm4Z*nhWX>
+)XJX<{#TXk}$=E^v8JO928D0~7!N00;o{l@>=M?`QnO0000I0RR9g00000000000001_f!(qN0B~t=F
+JE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|WUukY>bYEXCaCuNm0Rj{Q6aWAK2mtey7Dum&FJ;aF007Ga
+001Qb0000000000005+c=d%R>aA|NaUv_0~WN&gWWNCABY-wUIUt(cnYjAIJbT4gbb7L-Wc~DCM0u%!
+j000080P~d=M>MI?%19gl05W6%04D$d00000000000HlHVwFLlhX>c!Jc4cm4Z*nhWX>)XJX<{#5Vqs
+%zaBp&SFLP*hbZKlZaCuNm0Rj{Q6aWAK2mtey7DsOu0Il``0021(001ih0000000000005+ch0+B8aA
+|NaUv_0~WN&gWWNCABY-wUIUt(cnYjAIJbT4yxb7OCAW@%?GV`gViO928D0~7!N00;o{l@>=4KS&_x9
+smGNX#fB!00000000000001_fyC4W0B~t=FJE?LZe(wAFJx(RbZlv2FJEF|V{344a&#|qd2?fLZf0p`
+E^v8JO928D0~7!N00;o{l@>>#LpWPwSpWdjX#oHz00000000000001_f%NkQ0B~t=FJE?LZe(wAFJx(
+RbZlv2FJEF|V{344a&#|rVRB|^Y-KKRc~DCM0u%!j000080P~d=N2Z$lEQ$dD0LlUY03ZMW00000000
+000HlGPO$GpPX>c!Jc4cm4Z*nhabZu-kY-wUIUtei%X>?y-E^v8JO928D0~7!N00;o{l@>>jmU58HNd
+N#@Qvm=W00000000000001_fn`qy0B~t=FJE?LZe(wAFKBdaY&C3YVlQKFZgX^DZgg`laCuNm0Rj{Q6
+aWAK2mtey7Dokc9IXFD003jk0RSTa0000000000005+caG3@GaA|NaUv_0~WN&gWXmo9CHEd~OFKBda
+Y&CFUa&u*JE^v8JO928D0~7!N00;o{l@>=j&nXNA6aWClKmY(B00000000000001_fw0~N0B~t=FJE?
+LZe(wAFKBdaY&C3YVlQcEVRU79ZEP-Zc~DCM0u%!j000080P~d=N1bYj3DX_`0I_=j03!eZ00000000
+000HlHQ@&*8KX>c!Jc4cm4Z*nhabZu-kY-wUIX>M?JbaQlaWnpbDaCuNm0Rj{Q6aWAK2mtey7Dve{3;
+RYC008_%001EX0000000000005+c2NDMWaA|NaUv_0~WN&gWXmo9CHEd~OFLPmTX>@6NWpXZXc~DCM0
+u%!j000080P~d=M^Yzq><b+L0D&w403iSX00000000000HlG8CI<j;X>c!Jc4cm4Z*nhabZu-kY-wUI
+bZ={AZfSaDaxQRrP)h*<6ay3h000O8^OY7y9v<BwV+8;J!wdib9RL6T0000000000q=D8$2LNzsa4%n
+WWo~3|axZ9fZEQ7cX<{#RbZKmJE^v8JO928D0~7!N00;o{l@><;00002000000000e0000000000000
+1_fpkd+0B~t=FJE?LZe(wAFKBdaY&C3YVlQTCY;<LEb1z?CX>MtBUtcb8c~DCM0u%!j000080P~d=M<
+Rgx#FYR503HDV03`qb00000000000HlGuNe2LMX>c!Jc4cm4Z*nhabZu-kY-wUIW@&76WpZ;bUt(c%W
+iD`eP)h*<6ay3h000O8^OY7ye-4mNAOQdXZ~_1THUIzs0000000000q=A%52LNzsa4%nWWo~3|axZ9f
+ZEQ7cX<{#CX>4?5a&s?XY;b5{Vr6t`V_|GzbaZlQVs&(7b1rasP)h*<6ay3h000O8^OY7y6?b_$xB>t
+Gx(WaQGXMYp0000000000q=5)b2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?fZfa#?bYE>{bYW
+j(Xkl`5WpplZc~DCM0u%!j000080P~d=M-Z9Hz@P&F0B{fh03-ka00000000000HlEtPX_>SX>c!Jc4
+cm4Z*nhabZu-kY-wUIW@&76WpZ;bY-w(EE^v8JO928D0~7!N00;o{l@>?QlWz#g1^@sADF6U0000000
+00000001_f%8%a0B~t=FJE?LZe(wAFKBdaY&C3YVlQTCY;<LEb1!djbZKvHVQh3^XLBxac~DCM0u%!j
+000080P~d=M>wzKp|Jn}0AK+C04V?f00000000000HlEiS_c4dX>c!Jc4cm4Z*nhabZu-kY-wUIW@&7
+6WpZ;bb75|2bZL5JaxQRrP)h*<6ay3h000O8^OY7yW3^fp&;bAdb_4(bDgXcg0000000000q=Eig2LN
+zsa4%nWWo~3|axZ9fZEQ7cX<{#CX>4?5a&s?tXlZn1b8ul}WiD`eP)h*<6ay3h000O8^OY7y0x`qn5C
+H%H>Hz=%D*ylh0000000000q=6P)2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#PWpZg@Y-xIBaxY(BX>MtBU
+tcb8c~DCM0u%!j000080P~d=NB8lKOLh$a05>uK05Jdn00000000000HlF$UIzehX>c!Jc4cm4Z*nha
+bZu-kY-wUIb7gXAVQgu7WpXcQbZu;NWpZg@Y-xIBaxQRrP)h*<6ay3h000O8^OY7y000000ssI20000
+0EdT%j0000000000q=7VS2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*<WMOc0WpZ;bUtei%X>?y-E^v
+8JO928D0~7!N00;o{l@>?jTwf3x0ssJg1^@sk00000000000001_fq`rX0B~t=FJE?LZe(wAFKBdaY&
+C3YVlQ-ZWo2PxVQ_S1a&s?pVR$ZZc~DCM0u%!j000080P~d=N1#lH<>CYY08I@504)Fj00000000000
+HlH8ZU+ExX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7Vs&Y3WMy)5FJE72ZfSI1UoLQYP)h*<6ay3h000O8
+^OY7yy9}waLk<7{k2L@QDgXcg0000000000q=61}2LNzsa4%nWWo~3|axZ9fZEQ7cX<{#Qa%E*=b!lv
+5WpZ;bUt(c%WiD`eP)h*<6ay3h000O8^OY7yVJa~kmj?g<6(IlsC;$Ke0000000000q=A`&2LNzsa4%
+nWWo~3|axZ9fZEQ7cX<{#Qa%E*=b!lv5WpZ;bWN&RQaCuNm0Rj{Q6aWAK2mtey7Dr&udnW`6002!f00
+1fg0000000000005+cc8Ui8aA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWny({Y-D9}b1!9da%E*MaCuNm0
+Rj{Q6aWAK2mtey7DtnxCHo}}007T6001ul0000000000005+c!juO9aA|NaUv_0~WN&gWXmo9CHEd~O
+FLZKcWny({Y-D9}b1!9da%E*-Y<O*KE^v8JO928D0~7!N00;o{l@>>zHhndQ1^@tt761S&000000000
+00001_fi$8A0B~t=FJE?LZe(wAFKBdaY&C3YVlQ-ZWo36^Y-?q5b1z?CX>MtBUtcb8c~DCM0u%!j000
+080P~d=N6o`DuL1@D0A?Ei04V?f00000000000HlEgsRsaXX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7cV
+TR6WpZ;bUt(c%WiD`eP)h*<6ay3h000O8^OY7yov#Vw`2hd`jRgPzCjbBd0000000000q=8MZ2LNzsa
+4%nWWo~3|axZ9fZEQ7cX<{#Qa%E+AVQgz<a&s?aZ*4Acc~DCM0u%!j000080P~d=M+5NmDCPtJ0PYb0
+04V?f00000000000HlG7u?GNfX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7cVTR6WpZ;bWpr|7WiD`eP)h*
+<6ay3h000O8^OY7yEf}!Z!2$pP<_G`)FaQ7m0000000000q=C4$2LNzsa4%nWWo~3|axZ9fZEQ7cX<{
+#Qa%E+AVQgz<a&s?dWo~n5X>)XPWnpbDaCuNm0Rj{Q6aWAK2mtey7DqE4*c~Yb003wg001oj0000000
+000005+c$GHapaA|NaUv_0~WN&gWXmo9CHEd~OFLZKcWp`n0Yh`kCFKl>iY-MzEWo0gKc~DCM0u%!j0
+00080P~d=M|C+zP`m;F0R9L704o3h00000000000HlFCzy|<uX>c!Jc4cm4Z*nhabZu-kY-wUIbaG{7
+cVTR6WpZ;baCK~KWN&RQaCuNm0Rj{Q6aWAK2mtey7DuU7-Kd5D007hg001Qb0000000000005+cK*I+
+BaA|NaUv_0~WN&gWXmo9CHEd~OFLZKgWiMY}X>MtBUtcb8c~DCM0u%!j000080P~d=NAzNalYIdI0G|
+T@03rYY00000000000HlEl#0LOyX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BFJEF|b7d}Yc~DCM0u%!j00
+0080P~d=M<i99sipw{0Gb2<03!eZ00000000000HlG&#RmXzX>c!Jc4cm4Z*nhabZu-kY-wUIbaH8BF
+JxhKa%p8QaCuNm0Rj{Q6aWAK2mtey7Dr1@Zq6M7008d>0018V0000000000005+ct;YudaA|NaUv_0~
+WN&gWXmo9CHEd~OFLZKgWiN1fE^v8JO928D0~7!N00;o{l@>?!f28$T3IG7gBLDy(00000000000001
+_fd|S50B~t=FJE?LZe(wAFKlmPYi4O|WiMY}X>MtBUtcb8c~DCM0u%!j000080P~d=M?w%8Hy;B409g
+tE03-ka00000000000HlGP)CT}?X>c!Jc4cm4Z*nheZ)0m_X>4ULY-w(5Y;R+0W@&6?E^v8JO928D0~
+7!N00;o{l@>?dx-=4S0{{RT3;+Nn00000000000001_f&SMA0B~t=FJE?LZe(wAFKlmPYi4O|WiM@OW
+NC72Z)0m_X>4UKaCuNm0Rj{Q6aWAK2mtey7DuGbAQDCh002!G001KZ0000000000005+cvD*g#aA|Na
+Uv_0~WN&gWY;R+0W@&6?FK}sOY;R+0W@&6?E^v8JO928D0~7!N00;o{l@>?mQhD|d1pok~6#xJx0000
+0000000001_fjHy`0B~t=FJE?LZe(wAFKlmPYi4O|WiNAaY-x05Y;R+0W@&6?E^v8JO928D0~7!N00;
+o{l@>==Q$=h%0{{Ru3IG5n00000000000001_fs5$}0B~t=FJE?LZe(wAFKlmPYi4O|WiNAiZER_7Yi
+w_0Yi4O|WiD`eP)h*<6ay3h000O8^OY7yh-{tOM*;u<F$4erA^-pY0000000000q=5|W2LNzsa4%nWW
+o~3|axZXUV{2h&X>MmPUteKjZ*_EEUoLQYP)h*<6ay3h000O8^OY7ySk>pY`~d&}iUR-uApigX00000
+00000q=A#~2LNzsa4%nWWo~3|axZXUV{2h&X>MmPUtei%X>?y-E^v8JO928D0~7!N00;o{l@>>5N4=Y
+{0ssKz1ONaa00000000000001_fzI&<0B~t=FJE?LZe(wAFK}UFYhh<;Zf7rFV{dJ6VRSBVc~DCM0u%
+!j000080P~d=N3N1NwN(ND01*cO03-ka00000000000HlGu^alWNX>c!Jc4cm4Z*nhiVPk7yXK8L{FJ
+E(Xa&=>Lb#i5ME^v8JO928D0~7!N00;o{l@>>gf||7z82|vUZ2$lx00000000000001_fm8Pf0B~t=F
+JE?LZe(wAFK}UFYhh<;Zf7rZaAjj@W@%+|b1rasP)h*<6ay3h000O8^OY7y=mr%YMh*Y~tu6onAOHXW
+0000000000q=Bgr2mo+ta4%nWWo~3|axZXUV{2h&X>MmPc4cyNX>V>WaCuNm0Rj{Q6aWAK2mtey7Dt#
+=rN?M+0082j0RSZc0000000000005+cEguK~aA|NaUv_0~WN&gWaBF8@a%FRGb#h~6b1z?CX>MtBUtc
+b8c~DCM0u%!j000080P~d=NBUZEjFJQZ0M82m03ZMW00000000000HlG~j|c#8X>c!Jc4cm4Z*nhia&
+KpHWpi^cUtei%X>?y-E^v8JO928D0~7!N00;o{l@><>ChsIh1ONb&3IG5b00000000000001_fvA)S0
+B~t=FJE?LZe(wAFK}{iXL4n8b1!0HaxQRrP)h*<6ay3h000O8^OY7yE=aa)B?ABe`2_#~9{>OV00000
+00000q=6@y2mo+ta4%nWWo~3|axZXlZ)b94b8|0aZ*^{TWpXZXc~DCM0u%!j000080P~d=M;w`>M+gJ
+}08R@403QGV00000000000HlG5oCpALX>c!Jc4cm4Z*nhia&KpHWpi^cXk~10WpZ;aaCuNm0Rj{Q6aW
+AK2mtey7DtLR+g|<x002D&0018V0000000000005+c&Y%bYaA|NaUv_0~WN&gWaB^>Fa%FRKFLQ8dZf
+<3AE^v8JO928D0~7!N00;o{l@>=vjs?i30{{R;2LJ#d00000000000001_feEAt0B~t=FJE?LZe(wAF
+LGsZb!BsOb1z?CX>MtBUtcb8c~DCM0u%!j000080P~d=M^Wr;Om`6g0IER%03ZMW00000000000HlHK
+rw9OWX>c!Jc4cm4Z*nhkWpQ<7b98erVPs)&bY*gLE^v8JO928D0~7!N00;o{l@>?Jp62K~1poji6#xJ
+m00000000000001_fugwx0B~t=FJE?LZe(wAFLGsZb!BsOb1z|VX)bViP)h*<6ay3h000O8^OY7yDP{
+LPeh2^niW&d_8~^|S0000000000q=6Q{2mo+ta4%nWWo~3|axZdaadl;LbaO9Zb#!PhaCuNm0Rj{Q6a
+WAK2mtey7Dv-iT7FD<0szvq1OOiZ0000000000005+c&c_G<aA|NaUv_0~WN&gWa%FLKWpi|MFJob2W
+pZ>baAj>!O928D0~7!N00;o{l@>>>F)xm00RRAH0ssIV00000000000001_fmcBb0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!3Ma&&VpaCuNm0Rj{Q6aWAK2mtey7DrUx#oRCg006xO0015U0000000000005+c_(BT
++aA|NaUv_0~WN&gWa%FLKWpi|MFJo_QaA9;VaCuNm0Rj{Q6aWAK2mtey7DvoU5zOBb001XL0018V000
+0000000005+cWJL=AaA|NaUv_0~WN&gWa%FLKWpi|MFJo_SYiVV3E^v8JO928D0~7!N00;o{l@>?R9v
+nbq0{{Tk2><{h00000000000001_fq+;G0B~t=FJE?LZe(wAFLGsZb!BsOb1!9hV`Xr3X>V?GE^v8JO
+928D0~7!N00;o{l@>=AcKy7I0RR9r0{{RW00000000000001_fh1fD0B~t=FJE?LZe(wAFLGsZb!BsO
+b1!IbZ)<ZdaCuNm0Rj{Q6aWAK2mtey7Dx3e?fB*(008)L0015U0000000000005+c>Rk%}aA|NaUv_0
+~WN&gWa%FLKWpi|MFKusRWo&aUaCuNm0Rj{Q6aWAK2mtey7Ds(S{wRtX006pP001BW0000000000005
++c41Ws%aA|NaUv_0~WN&gWa%FLKWpi|MFLPycb7^mGb1rasP)h*<6ay3h000O8^OY7yDw@gO<pcl#fD
+8ZtBme*a0000000000q=D3#3jlCwa4%nWWo~3|axZdaadl;LbaO9rbYXOLb6;a`WMy+MaCuNm0Rj{Q6
+aWAK2mtey7DsH838QTU005y2001HY0000000000005+c`<@E`aA|NaUv_0~WN&gWa%FLKWpi|MFLQKq
+bz^jOa%FQaaCuNm0Rj{Q6aWAK2mtey7DwZ>xD{O+0034~0012T0000000000005+csG|!2aA|NaUv_0
+~WN&gWa%FLKWpi|MFLiWjY;!Jfc~DCM0u%!j000080P~d=M~^A$<ud>P06qW!04V?f00000000000Hl
+FHzY73xX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7yac
+oNbjRF7w4+Q`KF#rGn0000000000q=CA>3jlCwa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=W
+My<OUtei%X>?y-E^v8JO928D0~7!N00;o{l@>>FoWC)5kpKXqLID6V00000000000001_ftJDx0B~t=
+FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoMX=gQNa%FKYaCuNm0Rj{Q6aWAK2mtey7DxV
+o83Y>x005K*001)p0000000000005+cTvQAIaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB
+?;bT49QXEktgZ(?O~E^v8JO928D0~7!N00;o{l@>>NvR7F%1ONcY2><{y00000000000001_fyh@30B
+~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoNXkl_>Wprg@bS`jtP)h*<6ay3h000O8^
+OY7y_>jT?4haAN3M2pkIRF3v0000000000q=8Ia3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQ
+V`yP=WMy<OV`yP=WNCABa%p09bZKvHE^v8JO928D0~7!N00;o{l@>=iiKU-(1ONcf4gdf<000000000
+00001_fw5%_0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoNXkl`5Wprn9Z*_2Ra&K
+Z~axQRrP)h*<6ay3h000O8^OY7y_!;PfTLS<9ZU+DWHUIzs0000000000q=9~F3;=Lxa4%nWWo~3|ax
+Zdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OV`yP=b7gdJa&KZ~axQRrP)h*<6ay3h000O8^OY7yz}XNq-
+~#{v4haANI{*Lx0000000000q=7AN3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<O
+V{c?>ZfA3JVRU6}VPj}%Ze=cTc~DCM0u%!j000080P~d=M<Dy%IZ*-t0EGkq051Rl00000000000HlF
+tatr`)X>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FJo_QaA9;VaCuNm0Rj{Q6aWAK2mt
+ey7DxP5z|Xb<001`y001%o0000000000005+c1a%AmaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1
+!3PVRB?;bT4CXZgX^DZgg`laCuNm0Rj{Q6aWAK2mtey7DuZFCdNzy008y|001-q0000000000005+c3
+U~|vaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4CYIW#$Na&KZ~axQRrP)h*<6ay3h
+000O8^OY7yrY+JraRdMWa|{3gG5`Po0000000000q=Be=3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7
+yXJvCQV`yP=WMy<OWpiV2a&KZ~axQRrP)h*<6ay3h000O8^OY7y)=2%#$pruapB?}JE&u=k00000000
+00q=9LG3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OWpiV5Z7y(mP)h*<6ay3h00
+0O8^OY7ytbDvh3k3iGUJd{NGynhq0000000000q=9~h3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yX
+JvCQV`yP=WMy<OWp!g}aBy;OVr6nJaCuNm0Rj{Q6aWAK2mtey7Dp=NE|ITO005e^001%o0000000000
+005+c+lveUaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4IfV{39|a%FKYaCuNm0Rj{
+Q6aWAK2mtey7DolM#^f0T004^y001-q0000000000005+c+TRQSaA|NaUv_0~WN&gWa%FLKWpi|MFK}
+UFYhh<)b1!3PVRB?;bT4IfV{3A7a&KZ~axQRrP)h*<6ay3h000O8^OY7yB~POi-#`EWIEVlMG5`Po00
+00000000q=7}_3;=Lxa4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OWp!h8cV=>BaV~IqP
+)h*<6ay3h000O8^OY7yEuuJ783O<Sj0OMzGynhq0000000000q=9rK4FGUya4%nWWo~3|axZdaadl;L
+baO9oVPk7yXJvCQV`yP=WMy<OWp!h8cW`oVVr6nJaCuNm0Rj{Q6aWAK2mtey7DqXa^bPAm0066u001)
+p0000000000005+c-zN<KaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4OOGBYtUW^!
+e5E^v8JO928D0~7!N00;o{l@>=IwcGX@0{{S#1^@sx00000000000001_fg5WL0B~t=FJE?LZe(wAFL
+GsZb!BsOb1!gVV{2h&WpgiMXkl_>WppoRVlp!^GH`NlVr6nJaCuNm0Rj{Q6aWAK2mtey7DwZfttM*{0
+01vE001=r0000000000005+ch;9u4aA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4RS
+Vsd47aB^>AWpXZXc~DCM0u%!j000080P~d=N0+af4!%|Z0Mobt05AXm00000000000HlFNfeip~X>c!
+Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FKTIXW^!e5E^v8JO928D0~7!N00;o{l@>=Eh2+
+Rf6952|O8@{b00000000000001_fl}BF0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&WpgiMXkl_>Wp
+poUaAR(CcrI{xP)h*<6ay3h000O8^OY7yNKy%m3=04N@G$@YI{*Lx0000000000q=D+|4FGUya4%nWW
+o~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OY+-I^Vs&h1VRC6<Zf$R5Wo#~Rc~DCM0u%!j00008
+0P~d=N1UFwWBm&N06j$j06G8w00000000000HlFW_YDAWX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D
+?FJow7a%5$6FKl6MXJdJCX>4q1V{LC_Wo#~Rc~DCM0u%!j000080P~d=M-d@I9c2js08}vm05<>t000
+00000000HlGW0}cRiX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FKl6MXJ>L{WovD3WM
+ynFaCuNm0Rj{Q6aWAK2mtey7DpeVdkUTj001^D001}u0000000000005+cUJVWaaA|NaUv_0~WN&gWa
+%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4dSZf9s^Vsd47ZEs{{Y%XwlP)h*<6ay3h000O8^OY7yP`8?X
+#|i)d_%HwfI{*Lx0000000000q=8Zw4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<
+OY+-I^XmxI9VRC6<Zf$R5Wo#~Rc~DCM0u%!j000080P~d=M@v0_UF8Y@01GSt05$*s00000000000Hl
+F(A`Sp>X>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FKl6MXLM*`X>D(0Wo#~Rc~DCM0u
+%!j000080P~d=N9CHTnR^BR0B{rl05t#r00000000000HlGdEe-&1X>c!Jc4cm4Z*nhkWpQ<7b98era
+A9L>VP|D?FJow7a%5$6FKl6SX>Kuaa&KZ~axQRrP)h*<6ay3h000O8^OY7yrJQkdodf^?#0&rcH~;_u
+0000000000q=9)f4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=WMy<OZDM0+VRCb2bZ~N
+SVr6nJaCuNm0Rj{Q6aWAK2mtey7DvvEk2_fd006HC001}u0000000000005+cY&i}9aA|NaUv_0~WN&
+gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4gUV{>P6Z*_2Ra&KZ~axQRrP)h*<6ay3h000O8^OY7y@#
+SZlObh@3hD-neF8}}l0000000000q=6ef4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQV`yP=W
+My<OZDM0{b8Rkgc~DCM0u%!j000080P~d=NBSBSxOxTv0J#zX05|{u00000000000HlGmNe%#TX>c!J
+c4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FLPpJXkl`5Wpr?IZ(?O~E^v8JO928D0~7!N00;o
+{l@>>&+~vO{1ONcr3;+N)00000000000001_frL;F0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpg
+iMXkl_>WppodVq<e>a&L8TaB^>AWpXZXc~DCM0u%!j000080P~d=N0(vnR{aD30JIJO05bpp0000000
+0000HlHbR1N@eX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FJow7a%5$6FLP>Xb8vERVr6nJaCuNm0
+Rj{Q6aWAK2mtey7Dug|Hab@a006ie0024w0000000000005+cOj-^AaA|NaUv_0~WN&gWa%FLKWpi|M
+FK}UFYhh<)b1!3PVRB?;bT4&oX?A6Db75>`Wprg@bZ>GlaCuNm0Rj{Q6aWAK2mtey7Dr$EAZs23003M
+H001)p0000000000005+c`(X|MaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!3PVRB?;bT4&uW;k
+$iZ(?O~E^v8JO928D0~7!N00;o{l@>>Mw-zsg0{{RA2mk;v00000000000001_fof(B0B~t=FJE?LZe
+(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvaUukY>bYEXCaCuNm0Rj{Q6aWAK2mtey7DpLEEQieu0
+07n~001=r0000000000005+cJ8BL9aA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR9b
+Z)|L3V{~b6ZgVbhc~DCM0u%!j000080P~d=M^e;8_>~I)05c;105bpp00000000000HlFicMbq>X>c!
+Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJo_RZe?S1X>V>WaCuNm0Rj{Q6aWAK2mtey7Dq
+?CMmX~z003`x001`t0000000000005+cL4yteaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;
+0*_GcRLrZf<2`bZKvHaBpvHE^v8JO928D0~7!N00;o{l@>=HHY}u+1pojR5dZ))00000000000001_f
+r+FJ0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvgcw=R7bZKvHb1rasP)h*<6ay3h
+000O8^OY7y^G0R)1qT2C$rb<rF8}}l0000000000q=9a#4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7
+yXJvCQb#iQMX<{=kW@%+?WOFWXc~DCM0u%!j000080P~d=M{E%hAUXp80O<$-05Jdn00000000000Hl
+Gyu?_%mX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJ@_MWpHnEbS`jtP)h*<6ay3h00
+0O8^OY7yTdH&$HVXg%z9awuGynhq0000000000q=8Ab4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk7yX
+JvCQb#iQMX<{=kaBpvHZDDR<XJv9OaCuNm0Rj{Q6aWAK2mtey7DwjY!he7V004Iu001xm0000000000
+005+c(ZCJ>aA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR&wadl;LbS`jtP)h*<6ay3
+h000O8^OY7yLjoE44-x<XOF#esF#rGn0000000000q=BEv4ghdza4%nWWo~3|axZdaadl;LbaO9oVPk
+7yXJvCQb#iQMX<{=ka%FRHZ*FsCE^v8JO928D0~7!N00;o{l@><;00002000000000v000000000000
+01_f&JPJ0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyveZ*FvQX<{#5UukY>bYEXCa
+CuNm0Rj{Q6aWAK2mtey7Dou9o6na8005g20021v0000000000005+cR@)8$aA|NaUv_0~WN&gWa%FLK
+Wpi|MFK}UFYhh<)b1!vrY;0*_GcRLrZgg^KVlQrVY;ACFZ)`4bc~DCM0u%!j000080P~d=M?z6Cg>?)
+70B$4z06G8w00000000000HlFL;SK<BX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFJo
+_RbaH88FK~HpaAj_Db8Iefc~DCM0u%!j000080P~d=N59k!AU^;A07?J=06G8w00000000000HlEt?h
+XKOX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK}UFYhh<)b1z?CX>MtBUtcb8c~DCM0
+u%!j000080P~d=M@GS>XYdLD0OukA06qW!00000000000HlGa?hXKOX>c!Jc4cm4Z*nhkWpQ<7b98er
+aA9L>VP|D?FLiQkY-wUMFK}UFYhh<)b1!dlWMy(?WM5=yV{|TXc~DCM0u%!j000080P~d=N7Gmyv0M%
+S0BkJ)05t#r00000000000HlHR`3?YZX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFK}
+UFYhh<)b1!pgcrI{xP)h*<6ay3h000O8^OY7y5U^<F-2eap%mDxZO#lD@0000000000q=BOd4*+m!a4
+%nWWo~3|axZdaadl;LbaO9oVPk7yXJvCQb#iQMX<{=kaA9L>VP|D?FLQHjUu|J@V`yJ!Z*z2RVQpnEU
+tei%X>?y-E^v8JO928D0~7!N00;o{l@>>>)SMdx1^@uU4gdgD00000000000001_f$Iqm0B~t=FJE?L
+Ze(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvrVPk7yXJvCQb8~E8ZDDj{XkTb=b98QDZDlWCX>D+
+9Wo>0{bYXO9Z*DGdc~DCM0u%!j000080P~d=N4%-@gaZKp0OkPz05$*s00000000000HlFo5Dx%wX>c
+!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?FLiQkY-wUMFLiWjY%gD5X>MtBUtcb8c~DCM0u%!j000080P
+~d=NBVU-R15_G0No4#05|{u00000000000HlGq5f1=xX>c!Jc4cm4Z*nhkWpQ<7b98eraA9L>VP|D?F
+LiQkY-wUMFLiWjY%gPPZf<2`bZKvHE^v8JO928D0~7!N00;o{l@>>>sw2I}0ssIh2mk;z0000000000
+0001_ff^SN0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvwbZKlaa%FLKWpi{caCuN
+m0Rj{Q6aWAK2mtey7Ds3Q;WQBe001@u001@s0000000000005+cG#U>8aA|NaUv_0~WN&gWa%FLKWpi
+|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cYWpi+EZgXWWaCuNm0Rj{Q6aWAK2mtey7Dv$3+ayp7006`$00
+1)p0000000000005+cnHvuPaA|NaUv_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cYW
+pr|RE^v8JO928D0~7!N00;o{l@>?c=NYzw4FCW&CjbC400000000000001_fjTG;0B~t=FJE?LZe(wA
+FLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvwbZKlab8~E8E^v8JO928D0~7!N00;o{l@>?r3UFXL3jhE
+}B>(_500000000000001_febbe0B~t=FJE?LZe(wAFLGsZb!BsOb1!gVV{2h&Wpgiea%^mAVlyvwbZK
+labZKp6Z*_DoaCuNm0Rj{Q6aWAK2mtey7DwCS=~@2=007Ju001!n0000000000005+cnL!T#aA|NaUv
+_0~WN&gWa%FLKWpi|MFK}UFYhh<)b1!vrY;0*_GcR>?X>2cba%?Vec~DCM0u%!j000080P~d=N8pYFH
+H-oP0M`cq02lxO00000000000HlHENe=*UX>c!NZ*6U1Ze(*WUtei%X>?y-E^v8JO928D0~7!N00;o{
+l@>=65Bi<>0ssKV2LJ#X00000000000001_fvHRn0B~t=FJo_QZDDR?b1!3WZE$R5bZKvHE^v8JO928
+D0~7!N00;o{l@>=m_|e-o0{{RT2><{V00000000000001_f!R+F0B~t=FJo_QZDDR?b1!CcWo3G0E^v
+8JO928D0~7!N00;o{l@>=H7SNrl0RRBj0{{RN00000000000001_fkRUd0B~t=FJo_QZDDR?b1!IRY;
+Z1cc~DCM0u%!j000080P~d=M*}W(qBjlz07E$d02crN00000000000HlE*RSy7gX>c!NZ*6U1Ze(*WX
+>N0LVQg$JaCuNm0Rj{Q6aWAK2mtey7DsW9lMpZm002`Q000yK0000000000005+ci(?M}aA|NaV{dJ3
+VQyq|FKlUZbS`jtP)h*<6ay3h000O8^OY7y+3V+&R|Wt8{1E^E761SM0000000000q=D^f4*+m!a4%z
+TZEaz0WOFZbWnpq-XfAMhP)h*<6ay3h000O8^OY7yQi;$;AqD^dZW90i6aWAK0000000000q=9&G4*+
+m!a4%zTZEaz0WOFZbXm58eaCuNm0Rj{Q6aWAK2mtey7Duaz89&ei002Y_000>P0000000000005+c%X
+bd|aA|NaV{dJ3VQyq|FLiEdZgX^DY-}!Yc~DCM0u%!j000080P~d=M|glw#xn>201_Pl02KfL000000
+00000HlG^dk+9`X>c!NZ*6U1Ze(*WcW7m0Y%XwlP)h*<6ay3h000O8^OY7yd;YCslm!3)N(}%2761SM
+0000000000q=7hu4*+m!a4%zTZE#_9FJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7yf}*9DO%(tDI9dP
+z7ytkO0000000000q=5j64*+m!a4%zTZE#_9FJx(BbYpLBW@%?GaCuNm0Rj{Q6aWAK2mtey7DuWYXS_
+iG002h-000&M0000000000005+cgP#uoaA|NaZEs{{Y;!MPUukY>bYEXCaCuNm0Rj{Q6aWAK2mtey7D
+s4KGHu}i008F!000vJ0000000000005+c_@55|aA|NaZEs{{Y;!MZZe(S6E^v8JO928D0~7!N00;o{l
+@><;00002000000000Q00000000000001_feN7y0B~t=FK=*Va$$67Z*FrhUtei%X>?y-E^v8JO928D
+0~7!N00;o{l@>?RF>h651ONb#4*&od00000000000001_fkdGX0B~t=FK=*Va$$67Z*FrhW^!d^dSxz
+fc~DCM0u%!j000080P~d=M_g&r8Grx)02BcL022TJ00000000000HlH5r4ImbX>c!cWpOWGUukY>bYE
+XCaCuNm0Rj{Q6aWAK2mtey7DwFNGq6Sr0071%000vJ0000000000005+ckERa*aA|Naa%FKZa%FK}W@
+&6?E^v8JO928D0~7!N00;o{l@>=DEmQ*EDF6Uir~m*K00000000000001_fd;Y<0B~t=FLGsZFLGsZU
+ukZ0bYX04E^v8JO928D0~7!N00;o{l@>=`7|$3%8vp<_YXATe00000000000001_ff?Kn0B~t=FLGsZ
+FLGsZUvp)2E^v8JO928D0~7!N00;o{l@>?2NHhpv2mk=z8vp<p00000000000001_fr|GJ0B~t=FLGs
+ZFLGsZUv+M2ZgX^DY-}!Yc~DCM0u%!j000080P~d=M?@=AP_Q2W0KRAd02KfL00000000000HlE*01y
+ChX>c!fbZKmJFJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7y^O&Vq2nqlI)gu4^6aWAK0000000000q=
+5n;5CCv#a4&UqX>4;ZVQ_F{X>xNeaCuNm0Rj{Q6aWAK2mtey7DvqtO)dZe001Bb000sI00000000000
+05+cJSq?XaA|Nab#!TLb1!0bX>4RJaCuNm0Rj{Q6aWAK2mtey7Dv9ThG^dc003wR000;O0000000000
+005+cY%CA}aA|Nab#!TLb1!6NaB^j1VRUJ4ZZ2?nP)h*<6ay3h000O8^OY7y@<Z*wqyYc`g988n7XSb
+N0000000000q=A7i5CCv#a4&UqX>4;ZW@&6?b9r-gWo<5Sc~DCM0u%!j000080P~d=M`2RBVzUGQ0R9
+XB02BZK00000000000HlFhF%ST7X>c!fbZKmJFKlmTXK8L{E^v8JO928D0~7!N00;o{l@>>}`!?~B1^
+@uF6#xJg00000000000001_fj>470B~t=FLiWjY;!Mfb#!E5bY)~NaCuNm0Rj{Q6aWAK2mtey7Du5k1
+=|}5004Fx000jF0000000000005+c13eG`aA|Nab#!TLb1!viE^v8JO928D0~7!N00;o{l@>=oql8pZ
+4FCY}EdT%$00000000000001_flEdZ0B~t=FLq;dFJE72ZfSI1UoLQYP)h*<6ay3h000O8^OY7y>zRr
+5%LM=cOA!D75dZ)H0000000000q=C&-5CCv#a4&Xab1!0HdSPL5E^v8JO928D0~7!N00;o{l@>?Sh_s
+JC2><}N9RL6h00000000000001_fyY@80B~t=FLq;dFK20VE^v8JO928D0~7!N00;o{l@>=Bo0~X~1p
+okU6951d00000000000001_fihzd0B~t=FLq;dFKuOVV|8+AVQemNc~DCM0u%!j000080P~d=M?<@N5
+j_k50OTkD02KfL00000000000HlHQXb=E!X>c!gV{<Qabz*j9a&u{KZZ2?nP)h*<6ay3h000O8_>~q%
+{ba)VbpZeXJp%v$BLDyZ0000000000q=92~5CCv#a4k13F)lJLWNCABEop9MZ!bheQ$tcoP*h1zPA+n
+DbWlqH0u%!j000080Qi*_M;X)hB`N>_07L))03-ka00000000000HlE#cMt$@X>ct!E-@}LE@WwQbS-
+IaW^XTLZgg^aUvO_}Zgg`lba-@7O928D0~7!N00;p1l@>>(4j9~H0ssKh1pojc00000000000001_fr
+WPv0B~t=EjKPPE;24;X>)WfX>Mk3FKuOXVPs)+VJ>QOZ*EXa0Rj{Q6aWAK2mtt%7DuH%DV{F?001rk0
+00~S0000000000005+cCwdS7aA|NYH!d+QGA?9kb960fZf0*UaAI;UYIARHP)h*<6ay3h000O8_>~q%
+#~+u%1^@s61ONa4ApigX0000000000q=Arn5CCv#a4k13F)lJLWNCABEop9MZ!dIja9?a?c4cfXba-@
+7O928D0~7!N00;p1l@>>bGk<Si0001P0000P00000000000001_fz^5t0B~t=EjKPPE;24;X>)WfX>M
+k3FIPxKMNCjj0Rj{Q6aWAK2mtt%7DsNd4e99u006xQ000~S0000000000005+cYkLp?aA|NYH!d+QGA
+?9kb960fZf0*UO+{2eL_t(RP)h*<6ay3h000O8_>~q%jg!*R`zZhbR96518UO$Q0000000000q=Alp5
+CCv#a4k13F)lJLWNCABEop9MZ!c0sLr+pfP)h{{00000>;UWlXG{P9!Ke@b000
+"""
+
+
+if __name__ == "__main__":
+ main()
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz
new file mode 100644
index 0000000000..ce23d66c73
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/files/default/normatives.tar.gz
Binary files differ
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/check_Backend.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/check_Backend.rb
new file mode 100644
index 0000000000..00bb50ebd8
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/check_Backend.rb
@@ -0,0 +1,19 @@
+ruby_block "check_Backend_Health" do
+ block do
+ printf("\033[32m%s\n\033[0m", " executing BackEnd health-check, please wait...")
+ Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
+ curl_command = "http://localhost:8080/sdc2/rest/v1/user/jh0003"
+ resp = Net::HTTP.get_response URI.parse(curl_command)
+ stat = resp.code
+
+ case stat
+ when '200'
+ printf("\033[32m%s\n\033[0m", " BackEnd is up.")
+ else
+ printf("\033[31mstat=[%s]\n\033[0m", stat)
+ printf("\033[31m%s\n\033[0m", " BackEnd is DOWN!!!")
+ end
+ end
+ retries 12
+ retry_delay 5
+end
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/import_Normatives.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/import_Normatives.rb
new file mode 100644
index 0000000000..dc8e4b79c9
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/import_Normatives.rb
@@ -0,0 +1,16 @@
+cookbook_file "/tmp/normatives.tar.gz" do
+ source "normatives.tar.gz"
+end
+
+working_directory = "/tmp"
+
+bash "import-normatives" do
+ cwd "#{working_directory}"
+ code <<-EOH
+ tar xvfz /tmp/normatives.tar.gz
+ cd normatives/scripts/import/tosca/
+ /bin/chmod +x importNormativeAll.py
+ python importNormativeAll.py -i "#{node['HOST_IP']}" --debug=true > /var/lib/jetty/logs/importNormativeAll.log
+ EOH
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/upgrade_Normatives.rb b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/upgrade_Normatives.rb
new file mode 100644
index 0000000000..1f71f8cf32
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-repo/cookbooks/sdc-normatives/recipes/upgrade_Normatives.rb
@@ -0,0 +1,17 @@
+cookbook_file "/tmp/normatives.tar.gz" do
+ source "normatives.tar.gz"
+end
+
+working_directory = "/tmp"
+
+bash "upgrade-normatives" do
+ cwd "#{working_directory}"
+ code <<-EOH
+ tar xvfz /tmp/normatives.tar.gz
+ cd normatives/scripts/import/tosca/
+ /bin/chmod +x upgradeNormative.py importGroupTypes.py
+ python upgradeNormative.py -i "#{node['HOST_IP']}" --debug=true > /var/lib/jetty/logs/upgradeNormative.log
+ python importGroupTypes.py -i "#{node['HOST_IP']}" > /var/lib/jetty/logs/importGroupTypes.log
+ EOH
+end
+
diff --git a/sdc-os-chef/sdc-backend/chef-solo/LICENSE b/sdc-os-chef/sdc-backend/chef-solo/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/sdc-os-chef/sdc-backend/chef-solo/README.md b/sdc-os-chef/sdc-backend/chef-solo/README.md
new file mode 100644
index 0000000000..ddb0fda830
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/README.md
@@ -0,0 +1,37 @@
+Deprecated
+==========
+
+Use of this repository is deprecated. We recommend using the `chef generate repo` command that comes with [ChefDK](http://downloads.chef.io/chef-dk/).
+
+Overview
+========
+
+Every Chef installation needs a Chef Repository. This is the place where cookbooks, roles, config files and other artifacts for managing systems with Chef will live. We strongly recommend storing this repository in a version control system such as Git and treat it like source code.
+
+While we prefer Git, and make this repository available via GitHub, you are welcome to download a tar or zip archive and use your favorite version control system to manage the code.
+
+Repository Directories
+======================
+
+This repository contains several directories, and each directory contains a README file that describes what it is for in greater detail, and how to use it for managing your systems with Chef.
+
+* `cookbooks/` - Cookbooks you download or create.
+* `data_bags/` - Store data bags and items in .json in the repository.
+* `roles/` - Store roles in .rb or .json in the repository.
+* `environments/` - Store environments in .rb or .json in the repository.
+
+Configuration
+=============
+
+The repository contains a knife configuration file.
+
+* .chef/knife.rb
+
+The knife configuration file `.chef/knife.rb` is a repository specific configuration file for knife. If you're using Hosted Chef, you can download one for your organization from the management console. If you're using the Open Source Chef Server, you can generate a new one with `knife configure`. For more information about configuring Knife, see the Knife documentation.
+
+https://docs.chef.io/knife.html
+
+Next Steps
+==========
+
+Read the README file in each of the subdirectories for more information about what goes in those directories.
diff --git a/sdc-os-chef/sdc-backend/chef-solo/chefignore b/sdc-os-chef/sdc-backend/chef-solo/chefignore
new file mode 100644
index 0000000000..ba30af6cff
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/chefignore
@@ -0,0 +1,11 @@
+# Put files/directories that should be ignored in this file.
+# Lines that start with '# ' are comments.
+
+# emacs
+*~
+
+# vim
+*.sw[a-z]
+
+# subversion
+*/.svn/*
diff --git a/sdc-os-chef/sdc-backend/chef-solo/cookbooks/README.md b/sdc-os-chef/sdc-backend/chef-solo/cookbooks/README.md
new file mode 100644
index 0000000000..86ea46bfbb
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/cookbooks/README.md
@@ -0,0 +1,54 @@
+This directory contains the cookbooks used to configure systems in your infrastructure with Chef.
+
+Knife needs to be configured to know where the cookbooks are located with the `cookbook_path` setting. If this is not set, then several cookbook operations will fail to work properly.
+
+ cookbook_path ["./cookbooks"]
+
+This setting tells knife to look for the cookbooks directory in the present working directory. This means the knife cookbook subcommands need to be run in the `chef-repo` directory itself. To make sure that the cookbooks can be found elsewhere inside the repository, use an absolute path. This is a Ruby file, so something like the following can be used:
+
+ current_dir = File.dirname(__FILE__)
+ cookbook_path ["#{current_dir}/../cookbooks"]
+
+Which will set `current_dir` to the location of the knife.rb file itself (e.g. `~/chef-repo/.chef/knife.rb`).
+
+Configure knife to use your preferred copyright holder, email contact and license. Add the following lines to `.chef/knife.rb`.
+
+ cookbook_copyright "Example, Com."
+ cookbook_email "cookbooks@example.com"
+ cookbook_license "apachev2"
+
+Supported values for `cookbook_license` are "apachev2", "mit","gplv2","gplv3", or "none". These settings are used to prefill comments in the default recipe, and the corresponding values in the metadata.rb. You are free to change the the comments in those files.
+
+Create new cookbooks in this directory with Knife.
+
+ knife cookbook create COOKBOOK
+
+This will create all the cookbook directory components. You don't need to use them all, and can delete the ones you don't need. It also creates a README file, metadata.rb and default recipe.
+
+You can also download cookbooks directly from the Opscode Cookbook Site. There are two subcommands to help with this depending on what your preference is.
+
+The first and recommended method is to use a vendor branch if you're using Git. This is automatically handled with Knife.
+
+ knife cookbook site install COOKBOOK
+
+This will:
+
+* Download the cookbook tarball from cookbooks.opscode.com.
+* Ensure its on the git master branch.
+* Checks for an existing vendor branch, and creates if it doesn't.
+* Checks out the vendor branch (chef-vendor-COOKBOOK).
+* Removes the existing (old) version.
+* Untars the cookbook tarball it downloaded in the first step.
+* Adds the cookbook files to the git index and commits.
+* Creates a tag for the version downloaded.
+* Checks out the master branch again.
+* Merges the cookbook into master.
+* Repeats the above for all the cookbooks dependencies, downloading them from the community site
+
+The last step will ensure that any local changes or modifications you have made to the cookbook are preserved, so you can keep your changes through upstream updates.
+
+If you're not using Git, use the site download subcommand to download the tarball.
+
+ knife cookbook site download COOKBOOK
+
+This creates the COOKBOOK.tar.gz from in the current directory (e.g., `~/chef-repo`). We recommend following a workflow similar to the above for your version control tool.
diff --git a/sdc-os-chef/sdc-backend/chef-solo/data_bags/README.md b/sdc-os-chef/sdc-backend/chef-solo/data_bags/README.md
new file mode 100644
index 0000000000..0c15a391fa
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/data_bags/README.md
@@ -0,0 +1,63 @@
+Data Bags
+---------
+
+This directory contains directories of the various data bags you create for your infrastructure. Each subdirectory corresponds to a data bag on the Chef Server, and contains JSON files of the items that go in the bag.
+
+First, create a directory for the data bag.
+
+ mkdir data_bags/BAG
+
+Then create the JSON files for items that will go into that bag.
+
+ $EDITOR data_bags/BAG/ITEM.json
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM". For example,
+
+ {
+ "id": "foo"
+ }
+
+Next, create the data bag on the Chef Server.
+
+ knife data bag create BAG
+
+Then upload the items in the data bag's directory to the Chef Server.
+
+ knife data bag from file BAG ITEM.json
+
+
+Encrypted Data Bags
+-------------------
+
+Added in Chef 0.10, encrypted data bags allow you to encrypt the contents of your data bags. The content of attributes will no longer be searchable. To use encrypted data bags, first you must have or create a secret key.
+
+ openssl rand -base64 512 > secret_key
+
+You may use this secret_key to add items to a data bag during a create.
+
+ knife data bag create --secret-file secret_key passwords mysql
+
+You may also use it when adding ITEMs from files,
+
+ knife data bag create passwords
+ knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM" and the contents will be encrypted when uploaded. For example,
+
+ {
+ "id": "mysql",
+ "password": "abc123"
+ }
+
+Without the secret_key, the contents are encrypted.
+
+ knife data bag show passwords mysql
+ id: mysql
+ password: 2I0XUUve1TXEojEyeGsjhw==
+
+Use the secret_key to view the contents.
+
+ knife data bag show passwords mysql --secret-file secret_key
+ id: mysql
+ password: abc123
+
diff --git a/sdc-os-chef/sdc-backend/chef-solo/environments/README.md b/sdc-os-chef/sdc-backend/chef-solo/environments/README.md
new file mode 100644
index 0000000000..50ac48db2b
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/environments/README.md
@@ -0,0 +1,5 @@
+Requires Chef 0.10.0+.
+
+This directory is for Ruby DSL and JSON files for environments. For more information see the Chef wiki page:
+
+http://docs.chef.io/environments.html
diff --git a/sdc-os-chef/sdc-backend/chef-solo/normatives.json b/sdc-os-chef/sdc-backend/chef-solo/normatives.json
new file mode 100644
index 0000000000..f5c9276e2e
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/normatives.json
@@ -0,0 +1,4 @@
+{
+ "run_list": [ "recipe[sdc-normatives::import_Normatives]" ]
+}
+
diff --git a/sdc-os-chef/sdc-backend/chef-solo/normatives.rb b/sdc-os-chef/sdc-backend/chef-solo/normatives.rb
new file mode 100644
index 0000000000..12904507bd
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/normatives.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/normatives.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-backend/chef-solo/roles/README.md b/sdc-os-chef/sdc-backend/chef-solo/roles/README.md
new file mode 100644
index 0000000000..b0ee0b4d21
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/roles/README.md
@@ -0,0 +1,16 @@
+Create roles here, in either the Role Ruby DSL (.rb) or JSON (.json) files. To install roles on the server, use knife.
+
+For example, create `roles/base_example.rb`:
+
+ name "base_example"
+ description "Example base role applied to all nodes."
+ # List of recipes and roles to apply. Requires Chef 0.8, earlier versions use 'recipes()'.
+ #run_list()
+ # Attributes applied if the node doesn't have it set already.
+ #default_attributes()
+ # Attributes applied no matter what the node has set already.
+ #override_attributes()
+
+Then upload it to the Chef Server:
+
+ knife role from file roles/base_example.rb
diff --git a/sdc-os-chef/sdc-backend/chef-solo/roles/catalog-be.json b/sdc-os-chef/sdc-backend/chef-solo/roles/catalog-be.json
new file mode 100644
index 0000000000..e8928bbdc7
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/roles/catalog-be.json
@@ -0,0 +1,24 @@
+{
+ "name": "catalog-be",
+ "description": "Installation application - catalogBE",
+ "json_class": "Chef::Role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "chef_type": "role",
+ "run_list": [
+ "recipe[sdc-catalog-be::BE_1_cleanup_jettydir]",
+ "recipe[sdc-catalog-be::BE_2_locate_wars]",
+ "recipe[sdc-catalog-be::BE_4_setup_configuration]",
+ "recipe[sdc-catalog-be::BE_5_setup_elasticsearch]",
+ "recipe[sdc-catalog-be::BE_6_setup_portal_properties]",
+ "recipe[sdc-catalog-be::BE_7_logback]",
+ "recipe[sdc-catalog-be::BE_8_errors_config]"
+ ],
+ "env_run_lists": {
+ }
+}
+
diff --git a/sdc-os-chef/sdc-backend/chef-solo/solo.json b/sdc-os-chef/sdc-backend/chef-solo/solo.json
new file mode 100644
index 0000000000..ce096b3931
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/solo.json
@@ -0,0 +1,4 @@
+{
+ "run_list": [ "role[catalog-be]" ]
+}
+
diff --git a/sdc-os-chef/sdc-backend/chef-solo/solo.rb b/sdc-os-chef/sdc-backend/chef-solo/solo.rb
new file mode 100644
index 0000000000..06c1af4592
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/chef-solo/solo.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/solo.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-backend/startup.sh b/sdc-os-chef/sdc-backend/startup.sh
new file mode 100644
index 0000000000..d795ddde67
--- /dev/null
+++ b/sdc-os-chef/sdc-backend/startup.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+export CHEFNAME=${ENVNAME}
+# executing chef-solo for configuration
+cd /root/chef-solo
+echo "normal['HOST_IP'] = \"${HOST_IP}\"" > /root/chef-solo/cookbooks/sdc-catalog-be/attributes/default.rb
+chef-solo -c solo.rb -E ${CHEFNAME}
+
+sed -i '/^set -e/aJAVA_OPTIONS=\" -XX:MaxPermSize=256m -Xmx1500m -Dconfig.home=${JETTY_BASE}\/config -Dlog.home=${JETTY_BASE}\/logs -Dlogback.configurationFile=${JETTY_BASE}\/config\/catalog-be\/logback.xml -Dconfiguration.yaml=${JETTY_BASE}\/config\/catalog-be\/configuration.yaml\"' /docker-entrypoint.sh
+sed -i '/^set -e/aTMPDIR=${JETTY_BASE}\/temp' /docker-entrypoint.sh
+
+# executiong the jetty
+cd /var/lib/jetty
+/docker-entrypoint.sh &
+
+# add the consumers
+cd /root/chef-solo
+python /root/chef-solo/cookbooks/sdc-catalog-be/files/default/consumers.py &
+
+# add the user
+python /root/chef-solo/cookbooks/sdc-catalog-be/files/default/user.py &
+
+# check if BackEnd is up
+python /root/chef-solo/cookbooks/sdc-normatives/files/default/check_Backend_Health.py
+
+# executing the normatives
+cd /root/chef-solo
+check_normative="/tmp/check_normative.out"
+curl -s -X GET -H "Content-Type: application/json;charset=UTF-8" -H "USER_ID: jh0003" -H "X-ECOMP-RequestID: cbe744a0-037b-458f-aab5-df6e543c4090" -H "Cache-Control: no-cache" -H "Postman-Token: af08ca1c-302f-1431-404f-ed84246e07c9" "http://${HOST_IP}:8080/sdc2/rest/v1/screen" > ${check_normative}
+
+echo "normal['HOST_IP'] = \"${HOST_IP}\"" > /root/chef-solo/cookbooks/sdc-normatives/attributes/default.rb
+resources_len=`cat ${check_normative}| jq '.["resources"]|length'`
+if [ $resources_len -eq 0 ] ; then
+ chef-solo -c normatives.rb
+else
+ sed -i "s/import/upgrade/g" normatives.json
+ chef-solo -c normatives.rb
+fi
+
+while true; do sleep 2; done
diff --git a/sdc-os-chef/sdc-cassandra/Dockerfile b/sdc-os-chef/sdc-cassandra/Dockerfile
new file mode 100644
index 0000000000..6d2c55d4c8
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/Dockerfile
@@ -0,0 +1,22 @@
+FROM cassandra:2.1.16
+
+ENV DEBIAN_FRONTEND noninteractive
+RUN apt-get -y update && apt-get -y install --no-install-recommends apt-utils
+RUN apt-get -y install curl
+RUN apt-get -y install vim
+RUN apt-get -y install default-jre && apt-get -y install openjdk-8-jdk
+RUN update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
+ENV DEBIAN_FRONTEND teletype
+
+COPY chef-solo /root/chef-solo/
+COPY chef-repo/cookbooks /root/chef-solo/cookbooks/
+
+# install chef-solo
+RUN curl -L https://www.opscode.com/chef/install.sh | bash
+
+COPY startup.sh /root/
+
+RUN chmod 770 /root/startup.sh
+
+ENTRYPOINT [ "/root/startup.sh" ]
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/01-configureCassandra.rb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/01-configureCassandra.rb
new file mode 100644
index 0000000000..9313aa87ff
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/01-configureCassandra.rb
@@ -0,0 +1,49 @@
+cluster_name = ''
+cluster_name = node['cassandra'][:cluster_name]+node.chef_environment
+
+cas_ips=''
+cas_ips=node['Nodes'][:CS]
+
+interface = node['interfaces']['application']
+application_host = ''
+node['network']['interfaces'][interface][:addresses].each do | addr , details |
+ if details['family'] == ('inet')
+ application_host = addr
+ end
+end
+
+
+template "cassandra-yaml-config" do
+ path "/etc/cassandra/cassandra.yaml"
+ source "cassandra.yaml.erb"
+ owner "cassandra"
+ group "cassandra"
+ mode "0755"
+ variables ({
+ :cassandra_cluster => cluster_name,
+ :cassandra_data_dir => node['cassandra'][:data_dir],
+ :cassandra_commitlog_dir => node['cassandra'][:commitlog_dir],
+ :cassandra_cache_dir => node['cassandra'][:cache_dir],
+ :seeds_address => cas_ips,
+ :listen_address => application_host,
+ :broadcast_address => application_host,
+ :broadcast_rpc_address => application_host,
+ :rpc_address => "0.0.0.0",
+ :num_tokens => node['cassandra'][:num_tokens],
+ :internode_encryption => "none",
+ :cassandra_truststore_dir => "/etc/cassandra/cs_trust"
+ })
+end
+
+rackNum=1
+template "cassandra-rackdc.properties" do
+ path "/etc/cassandra/cassandra-rackdc.properties"
+ source "cassandra-rackdc.properties.erb"
+ owner "cassandra"
+ group "cassandra"
+ mode "0755"
+ variables ({
+ :dc => "DC-"+node.chef_environment,
+ :rack => "Rack"+"#{rackNum}-"+node.chef_environment
+ })
+end
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/02-createCsUser.rb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/02-createCsUser.rb
new file mode 100644
index 0000000000..627bd6fe7e
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/02-createCsUser.rb
@@ -0,0 +1,8 @@
+template "/tmp/create_cassandra_user.sh" do
+ source "create_cassandra_user.sh.erb"
+ mode 0755
+ variables({
+ :cassandra_ip => "HOSTIP"
+ })
+end
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/03-createDoxKeyspace.rb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/03-createDoxKeyspace.rb
new file mode 100644
index 0000000000..92a81eb7dc
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/03-createDoxKeyspace.rb
@@ -0,0 +1,8 @@
+template "/tmp/create_dox_keyspace.sh" do
+ source "create_dox_keyspace.sh.erb"
+ mode 0755
+ variables({
+ :cassandra_ip => "HOSTIP"
+ })
+end
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/04-schemaCreation.rb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/04-schemaCreation.rb
new file mode 100644
index 0000000000..7c40c509c2
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/recipes/04-schemaCreation.rb
@@ -0,0 +1,35 @@
+cookbook_file "/tmp/sdctool.tar" do
+ source "sdctool.tar"
+ mode 0755
+end
+
+## extract sdctool.tar
+bash "install tar" do
+ cwd "/tmp"
+ code <<-EOH
+ /bin/tar xvf /tmp/sdctool.tar -C /tmp
+ EOH
+end
+
+
+template "/tmp/sdctool/config/configuration.yaml" do
+ source "configuration.yaml.erb"
+ mode 0755
+ variables({
+ :host_ip => node['HOST_IP'],
+ :catalog_port => node['BE'][:http_port],
+ :ssl_port => node['BE'][:https_port],
+ :cassandra_ip => node['Nodes']['CS'],
+ :rep_factor => 1,
+ :dc1 => "DC-"+node.chef_environment
+ })
+end
+
+template "/tmp/sdctool/config/elasticsearch.yml" do
+ source "elasticsearch.yml.erb"
+ mode 0755
+ variables({
+ :elastic_ip => "HOSTIP"
+ })
+end
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra-rackdc.properties.erb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra-rackdc.properties.erb
new file mode 100644
index 0000000000..23ddcdd545
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra-rackdc.properties.erb
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+# These properties are used with GossipingPropertyFileSnitch and will
+# indicate the rack and dc for this node
+dc=<%= @dc %>
+rack=<%= @rack %>
+
+# Add a suffix to a datacenter name. Used by the Ec2Snitch and Ec2MultiRegionSnitch
+# to append a string to the EC2 region name.
+#dc_suffix=
+
+# Uncomment the following line to make this snitch prefer the internal ip when possible, as the Ec2MultiRegionSnitch does.
+# prefer_local=true
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra.yaml.erb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra.yaml.erb
new file mode 100644
index 0000000000..d4b6032d00
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/cassandra.yaml.erb
@@ -0,0 +1,824 @@
+# Cassandra storage config YAML
+
+# NOTE:
+# See http://wiki.apache.org/cassandra/StorageConfiguration for
+# full explanations of configuration directives
+# /NOTE
+
+# The name of the cluster. This is mainly used to prevent machines in
+# one logical cluster from joining another.
+cluster_name: '<%= @cassandra_cluster %>'
+
+# This defines the number of tokens randomly assigned to this node on the ring
+# The more tokens, relative to other nodes, the larger the proportion of data
+# that this node will store. You probably want all nodes to have the same number
+# of tokens assuming they have equal hardware capability.
+#
+# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
+# and will use the initial_token as described below.
+#
+# Specifying initial_token will override this setting on the node's initial start,
+# on subsequent starts, this setting will apply even if initial token is set.
+#
+# If you already have a cluster with 1 token per node, and wish to migrate to
+# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
+num_tokens: <%= @num_tokens %>
+
+# initial_token allows you to specify tokens manually. While you can use # it with
+# vnodes (num_tokens > 1, above) -- in which case you should provide a
+# comma-separated list -- it's primarily used when adding nodes # to legacy clusters
+# that do not have vnodes enabled.
+# initial_token:
+
+# May either be "true" or "false" to enable globally, or contain a list
+# of data centers to enable per-datacenter.
+# hinted_handoff_enabled: DC1,DC2
+# See http://wiki.apache.org/cassandra/HintedHandoff
+hinted_handoff_enabled: <%= node['cassandra']['hinted_handoff_enabled'] %>
+
+# this defines the maximum amount of time a dead host will have hints
+# generated. After it has been dead this long, new hints for it will not be
+# created until it has been seen alive and gone down again.
+max_hint_window_in_ms: 10800000 # 3 hours
+
+# Maximum throttle in KBs per second, per delivery thread. This will be
+# reduced proportionally to the number of nodes in the cluster. (If there
+# are two nodes in the cluster, each delivery thread will use the maximum
+# rate; if there are three, each will throttle to half of the maximum,
+# since we expect two nodes to be delivering hints simultaneously.)
+hinted_handoff_throttle_in_kb: 1024
+
+# Number of threads with which to deliver hints;
+# Consider increasing this number when you have multi-dc deployments, since
+# cross-dc handoff tends to be slower
+max_hints_delivery_threads: 2
+
+# Maximum throttle in KBs per second, total. This will be
+# reduced proportionally to the number of nodes in the cluster.
+batchlog_replay_throttle_in_kb: 1024
+
+# Authentication backend, implementing IAuthenticator; used to identify users
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
+# PasswordAuthenticator}.
+#
+# - AllowAllAuthenticator performs no checks - set it to disable authentication.
+# - PasswordAuthenticator relies on username/password pairs to authenticate
+# users. It keeps usernames and hashed passwords in system_auth.credentials table.
+# Please increase system_auth keyspace replication factor if you use this authenticator.
+authenticator: PasswordAuthenticator
+
+# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
+# CassandraAuthorizer}.
+#
+# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
+# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
+# increase system_auth keyspace replication factor if you use this authorizer.
+authorizer: AllowAllAuthorizer
+
+# Validity period for permissions cache (fetching permissions can be an
+# expensive operation depending on the authorizer, CassandraAuthorizer is
+# one example). Defaults to 2000, set to 0 to disable.
+# Will be disabled automatically for AllowAllAuthorizer.
+permissions_validity_in_ms: 2000
+
+# Refresh interval for permissions cache (if enabled).
+# After this interval, cache entries become eligible for refresh. Upon next
+# access, an async reload is scheduled and the old value returned until it
+# completes. If permissions_validity_in_ms is non-zero, then this must be
+# also.
+# Defaults to the same value as permissions_validity_in_ms.
+# permissions_update_interval_in_ms: 1000
+
+# The partitioner is responsible for distributing groups of rows (by
+# partition key) across nodes in the cluster. You should leave this
+# alone for new clusters. The partitioner can NOT be changed without
+# reloading all data, so when upgrading you should set this to the
+# same partitioner you were already using.
+#
+# Besides Murmur3Partitioner, partitioners included for backwards
+# compatibility include RandomPartitioner, ByteOrderedPartitioner, and
+# OrderPreservingPartitioner.
+#
+partitioner: org.apache.cassandra.dht.Murmur3Partitioner
+
+# Directories where Cassandra should store data on disk. Cassandra
+# will spread data evenly across them, subject to the granularity of
+# the configured compaction strategy.
+data_file_directories:
+ - <%= @cassandra_data_dir %>
+
+
+# commit log. when running on magnetic HDD, this should be a
+# separate spindle than the data directories.
+# If not set, the default directory is $CASSANDRA_HOME/data/commitlog.
+commitlog_directory: <%= @cassandra_commitlog_dir %>
+
+# policy for data disk failures:
+# stop_paranoid: shut down gossip and Thrift even for single-sstable errors.
+# stop: shut down gossip and Thrift, leaving the node effectively dead, but
+# can still be inspected via JMX.
+# best_effort: stop using the failed disk and respond to requests based on
+# remaining available sstables. This means you WILL see obsolete
+# data at CL.ONE!
+# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
+disk_failure_policy: stop
+
+# policy for commit disk failures:
+# stop: shut down gossip and Thrift, leaving the node effectively dead, but
+# can still be inspected via JMX.
+# stop_commit: shutdown the commit log, letting writes collect but
+# continuing to service reads, as in pre-2.0.5 Cassandra
+# ignore: ignore fatal errors and let the batches fail
+commit_failure_policy: stop
+
+# Maximum size of the key cache in memory.
+#
+# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
+# minimum, sometimes more. The key cache is fairly tiny for the amount of
+# time it saves, so it's worthwhile to use it at large numbers.
+# The row cache saves even more time, but must contain the entire row,
+# so it is extremely space-intensive. It's best to only use the
+# row cache if you have hot rows or static rows.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
+key_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# save the key cache. Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 14400 or 4 hours.
+key_cache_save_period: 14400
+
+# Number of keys from the key cache to save
+# Disabled by default, meaning all keys are going to be saved
+# key_cache_keys_to_save: 100
+
+# Maximum size of the row cache in memory.
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is 0, to disable row caching.
+row_cache_size_in_mb: 0
+
+# Duration in seconds after which Cassandra should
+# save the row cache. Caches are saved to saved_caches_directory as specified
+# in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 0 to disable saving the row cache.
+row_cache_save_period: 0
+
+# Number of keys from the row cache to save
+# Disabled by default, meaning all keys are going to be saved
+# row_cache_keys_to_save: 100
+
+# Maximum size of the counter cache in memory.
+#
+# Counter cache helps to reduce counter locks' contention for hot counter cells.
+# In case of RF = 1 a counter cache hit will cause Cassandra to skip the read before
+# write entirely. With RF > 1 a counter cache hit will still help to reduce the duration
+# of the lock hold, helping with hot counter cell updates, but will not allow skipping
+# the read entirely. Only the local (clock, count) tuple of a counter cell is kept
+# in memory, not the whole counter, so it's relatively cheap.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(2.5% of Heap (in MB), 50MB)). Set to 0 to disable counter cache.
+# NOTE: if you perform counter deletes and rely on low gcgs, you should disable the counter cache.
+counter_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# save the counter cache (keys only). Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Default is 7200 or 2 hours.
+counter_cache_save_period: 7200
+
+# Number of keys from the counter cache to save
+# Disabled by default, meaning all keys are going to be saved
+# counter_cache_keys_to_save: 100
+
+# The off-heap memory allocator. Affects storage engine metadata as
+# well as caches. Experiments show that JEMAlloc saves some memory
+# than the native GCC allocator (i.e., JEMalloc is more
+# fragmentation-resistant).
+#
+# Supported values are: NativeAllocator, JEMallocAllocator
+#
+# If you intend to use JEMallocAllocator you have to install JEMalloc as library and
+# modify cassandra-env.sh as directed in the file.
+#
+# Defaults to NativeAllocator
+# memory_allocator: NativeAllocator
+
+# saved caches
+# If not set, the default directory is $CASSANDRA_HOME/data/saved_caches.
+saved_caches_directory: /var/lib/cassandra/saved_caches
+
+# commitlog_sync may be either "periodic" or "batch."
+# When in batch mode, Cassandra won't ack writes until the commit log
+# has been fsynced to disk. It will wait
+# commitlog_sync_batch_window_in_ms milliseconds between fsyncs.
+# This window should be kept short because the writer threads will
+# be unable to do extra work while waiting. (You may need to increase
+# concurrent_writes for the same reason.)
+#
+# commitlog_sync: batch
+# commitlog_sync_batch_window_in_ms: 2
+#
+# the other option is "periodic" where writes may be acked immediately
+# and the CommitLog is simply synced every commitlog_sync_period_in_ms
+# milliseconds. By default this allows 1024*(CPU cores) pending
+# entries on the commitlog queue. If you are writing very large blobs,
+# you should reduce that; 16*cores works reasonably well for 1MB blobs.
+# It should be at least as large as the concurrent_writes setting.
+commitlog_sync: periodic
+commitlog_sync_period_in_ms: 10000
+
+# The size of the individual commitlog file segments. A commitlog
+# segment may be archived, deleted, or recycled once all the data
+# in it (potentially from each columnfamily in the system) has been
+# flushed to sstables.
+#
+# The default size is 32, which is almost always fine, but if you are
+# archiving commitlog segments (see commitlog_archiving.properties),
+# then you probably want a finer granularity of archiving; 8 or 16 MB
+# is reasonable.
+commitlog_segment_size_in_mb: 32
+
+# Reuse commit log files when possible. The default is false, and this
+# feature will be removed entirely in future versions of Cassandra.
+#commitlog_segment_recycling: false
+
+# any class that implements the SeedProvider interface and has a
+# constructor that takes a Map<String, String> of parameters will do.
+seed_provider:
+ # Addresses of hosts that are deemed contact points.
+ # Cassandra nodes use this list of hosts to find each other and learn
+ # the topology of the ring. You must change this if you are running
+ # multiple nodes!
+ - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+ parameters:
+ # seeds is actually a comma-delimited list of addresses.
+ # Ex: "<ip1>,<ip2>,<ip3>"
+ - seeds: "<%= @seeds_address %>"
+
+# For workloads with more data than can fit in memory, Cassandra's
+# bottleneck will be reads that need to fetch data from
+# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
+# order to allow the operations to enqueue low enough in the stack
+# that the OS and drives can reorder them. Same applies to
+# "concurrent_counter_writes", since counter writes read the current
+# values before incrementing and writing them back.
+#
+# On the other hand, since writes are almost never IO bound, the ideal
+# number of "concurrent_writes" is dependent on the number of cores in
+# your system; (8 * number_of_cores) is a good rule of thumb.
+concurrent_reads: <%= node['cassandra']['concurrent_reads'] %>
+concurrent_writes: <%= node['cassandra']['concurrent_writes'] %>
+concurrent_counter_writes: 32
+
+
+# Total memory to use for sstable-reading buffers. Defaults to
+# the smaller of 1/4 of heap or 512MB.
+# file_cache_size_in_mb: 512
+
+# Total permitted memory to use for memtables. Cassandra will stop
+# accepting writes when the limit is exceeded until a flush completes,
+# and will trigger a flush based on memtable_cleanup_threshold
+# If omitted, Cassandra will set both to 1/4 the size of the heap.
+# memtable_heap_space_in_mb: 2048
+# memtable_offheap_space_in_mb: 2048
+
+# Ratio of occupied non-flushing memtable size to total permitted size
+# that will trigger a flush of the largest memtable. Lager mct will
+# mean larger flushes and hence less compaction, but also less concurrent
+# flush activity which can make it difficult to keep your disks fed
+# under heavy write load.
+#
+# memtable_cleanup_threshold defaults to 1 / (memtable_flush_writers + 1)
+# memtable_cleanup_threshold: 0.11
+
+# Specify the way Cassandra allocates and manages memtable memory.
+# Options are:
+# heap_buffers: on heap nio buffers
+# offheap_buffers: off heap (direct) nio buffers
+# offheap_objects: native memory, eliminating nio buffer heap overhead
+memtable_allocation_type: heap_buffers
+
+# Total space to use for commitlogs. Since commitlog segments are
+# mmapped, and hence use up address space, the default size is 32
+# on 32-bit JVMs, and 8192 on 64-bit JVMs.
+#
+# If space gets above this value (it will round up to the next nearest
+# segment multiple), Cassandra will flush every dirty CF in the oldest
+# segment and remove it. So a small total commitlog space will tend
+# to cause more flush activity on less-active columnfamilies.
+# commitlog_total_space_in_mb: 8192
+
+# This sets the amount of memtable flush writer threads. These will
+# be blocked by disk io, and each one will hold a memtable in memory
+# while blocked.
+#
+# memtable_flush_writers defaults to the smaller of (number of disks,
+# number of cores), with a minimum of 2 and a maximum of 8.
+#
+# If your data directories are backed by SSD, you should increase this
+# to the number of cores.
+#memtable_flush_writers: 8
+
+# A fixed memory pool size in MB for for SSTable index summaries. If left
+# empty, this will default to 5% of the heap size. If the memory usage of
+# all index summaries exceeds this limit, SSTables with low read rates will
+# shrink their index summaries in order to meet this limit. However, this
+# is a best-effort process. In extreme conditions Cassandra may need to use
+# more than this amount of memory.
+index_summary_capacity_in_mb:
+
+# How frequently index summaries should be resampled. This is done
+# periodically to redistribute memory from the fixed-size pool to sstables
+# proportional their recent read rates. Setting to -1 will disable this
+# process, leaving existing index summaries at their current sampling level.
+index_summary_resize_interval_in_minutes: 60
+
+# Whether to, when doing sequential writing, fsync() at intervals in
+# order to force the operating system to flush the dirty
+# buffers. Enable this to avoid sudden dirty buffer flushing from
+# impacting read latencies. Almost always a good idea on SSDs; not
+# necessarily on platters.
+trickle_fsync: false
+trickle_fsync_interval_in_kb: 10240
+
+# TCP port, for commands and data
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+storage_port: 7000
+
+# SSL port, for encrypted communication. Unused unless enabled in
+# encryption_options
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+ssl_storage_port: 7001
+
+# Address or interface to bind to and tell other Cassandra nodes to connect to.
+# You _must_ change this if you want multiple nodes to be able to communicate!
+#
+# Set listen_address OR listen_interface, not both. Interfaces must correspond
+# to a single address, IP aliasing is not supported.
+#
+# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
+# will always do the Right Thing _if_ the node is properly configured
+# (hostname, name resolution, etc), and the Right Thing is to use the
+# address associated with the hostname (it might not be).
+#
+# Setting listen_address to 0.0.0.0 is always wrong.
+#
+# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
+# you can specify which should be chosen using listen_interface_prefer_ipv6. If false the first ipv4
+# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
+# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
+listen_address: <%= @listen_address %>
+# listen_interface: eth0
+# listen_interface_prefer_ipv6: false
+
+# Address to broadcast to other Cassandra nodes
+# Leaving this blank will set it to the same value as listen_address
+broadcast_address: <%= @broadcast_address %>
+
+# Internode authentication backend, implementing IInternodeAuthenticator;
+# used to allow/disallow connections from peer nodes.
+# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
+
+# Whether to start the native transport server.
+# Please note that the address on which the native transport is bound is the
+# same as the rpc_address. The port however is different and specified below.
+start_native_transport: true
+# port for the CQL native transport to listen for clients on
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+native_transport_port: 9042
+# The maximum threads for handling requests when the native transport is used.
+# This is similar to rpc_max_threads though the default differs slightly (and
+# there is no native_transport_min_threads, idle threads will always be stopped
+# after 30 seconds).
+# native_transport_max_threads: 128
+#
+# The maximum size of allowed frame. Frame (requests) larger than this will
+# be rejected as invalid. The default is 256MB.
+# native_transport_max_frame_size_in_mb: 256
+
+# The maximum number of concurrent client connections.
+# The default is -1, which means unlimited.
+# native_transport_max_concurrent_connections: -1
+
+# The maximum number of concurrent client connections per source ip.
+# The default is -1, which means unlimited.
+# native_transport_max_concurrent_connections_per_ip: -1
+
+# Whether to start the thrift rpc server.
+start_rpc: true
+
+# The address or interface to bind the Thrift RPC service and native transport
+# server to.
+#
+# Set rpc_address OR rpc_interface, not both. Interfaces must correspond
+# to a single address, IP aliasing is not supported.
+#
+# Leaving rpc_address blank has the same effect as on listen_address
+# (i.e. it will be based on the configured hostname of the node).
+#
+# Note that unlike listen_address, you can specify 0.0.0.0, but you must also
+# set broadcast_rpc_address to a value other than 0.0.0.0.
+#
+# For security reasons, you should not expose this port to the internet. Firewall it if needed.
+#
+# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
+# you can specify which should be chosen using rpc_interface_prefer_ipv6. If false the first ipv4
+# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
+# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
+rpc_address: <%= @rpc_address %>
+# rpc_interface: eth1
+# rpc_interface_prefer_ipv6: false
+
+# port for Thrift to listen for clients on
+rpc_port: 9160
+
+# RPC address to broadcast to drivers and other Cassandra nodes. This cannot
+# be set to 0.0.0.0. If left blank, this will be set to the value of
+# rpc_address. If rpc_address is set to 0.0.0.0, broadcast_rpc_address must
+# be set.
+broadcast_rpc_address: <%= @broadcast_rpc_address %>
+
+# enable or disable keepalive on rpc/native connections
+rpc_keepalive: true
+
+# Cassandra provides two out-of-the-box options for the RPC Server:
+#
+# sync -> One thread per thrift connection. For a very large number of clients, memory
+# will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size
+# per thread, and that will correspond to your use of virtual memory (but physical memory
+# may be limited depending on use of stack space).
+#
+# hsha -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
+# asynchronously using a small number of threads that does not vary with the amount
+# of thrift clients (and thus scales well to many clients). The rpc requests are still
+# synchronous (one thread per active request). If hsha is selected then it is essential
+# that rpc_max_threads is changed from the default value of unlimited.
+#
+# The default is sync because on Windows hsha is about 30% slower. On Linux,
+# sync/hsha performance is about the same, with hsha of course using less memory.
+#
+# Alternatively, can provide your own RPC server by providing the fully-qualified class name
+# of an o.a.c.t.TServerFactory that can create an instance of it.
+rpc_server_type: sync
+
+# Uncomment rpc_min|max_thread to set request pool size limits.
+#
+# Regardless of your choice of RPC server (see above), the number of maximum requests in the
+# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
+# RPC server, it also dictates the number of clients that can be connected at all).
+#
+# The default is unlimited and thus provides no protection against clients overwhelming the server. You are
+# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
+# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
+#
+# rpc_min_threads: 16
+# rpc_max_threads: 2048
+
+# uncomment to set socket buffer sizes on rpc connections
+# rpc_send_buff_size_in_bytes:
+# rpc_recv_buff_size_in_bytes:
+
+# Uncomment to set socket buffer size for internode communication
+# Note that when setting this, the buffer size is limited by net.core.wmem_max
+# and when not setting it it is defined by net.ipv4.tcp_wmem
+# See:
+# /proc/sys/net/core/wmem_max
+# /proc/sys/net/core/rmem_max
+# /proc/sys/net/ipv4/tcp_wmem
+# /proc/sys/net/ipv4/tcp_wmem
+# and: man tcp
+# internode_send_buff_size_in_bytes:
+# internode_recv_buff_size_in_bytes:
+
+# Frame size for thrift (maximum message length).
+thrift_framed_transport_size_in_mb: 15
+
+# Set to true to have Cassandra create a hard link to each sstable
+# flushed or streamed locally in a backups/ subdirectory of the
+# keyspace data. Removing these links is the operator's
+# responsibility.
+incremental_backups: false
+
+# Whether or not to take a snapshot before each compaction. Be
+# careful using this option, since Cassandra won't clean up the
+# snapshots for you. Mostly useful if you're paranoid when there
+# is a data format change.
+snapshot_before_compaction: false
+
+# Whether or not a snapshot is taken of the data before keyspace truncation
+# or dropping of column families. The STRONGLY advised default of true
+# should be used to provide data safety. If you set this flag to false, you will
+# lose data on truncation or drop.
+auto_snapshot: true
+
+# When executing a scan, within or across a partition, we need to keep the
+# tombstones seen in memory so we can return them to the coordinator, which
+# will use them to make sure other replicas also know about the deleted rows.
+# With workloads that generate a lot of tombstones, this can cause performance
+# problems and even exaust the server heap.
+# (http://www.datastax.com/dev/blog/cassandra-anti-patterns-queues-and-queue-like-datasets)
+# Adjust the thresholds here if you understand the dangers and want to
+# scan more tombstones anyway. These thresholds may also be adjusted at runtime
+# using the StorageService mbean.
+tombstone_warn_threshold: 1000
+tombstone_failure_threshold: 100000
+
+# Granularity of the collation index of rows within a partition.
+# Increase if your rows are large, or if you have a very large
+# number of rows per partition. The competing goals are these:
+# 1) a smaller granularity means more index entries are generated
+# and looking up rows withing the partition by collation column
+# is faster
+# 2) but, Cassandra will keep the collation index in memory for hot
+# rows (as part of the key cache), so a larger granularity means
+# you can cache more hot rows
+column_index_size_in_kb: 64
+
+
+# Log WARN on any batch size exceeding this value. 5kb per batch by default.
+# Caution should be taken on increasing the size of this threshold as it can lead to node instability.
+batch_size_warn_threshold_in_kb: 5
+
+
+# Log WARN on any batches not of type LOGGED than span across more partitions than this limit
+unlogged_batch_across_partitions_warn_threshold: 10
+
+# Number of simultaneous compactions to allow, NOT including
+# validation "compactions" for anti-entropy repair. Simultaneous
+# compactions can help preserve read performance in a mixed read/write
+# workload, by mitigating the tendency of small sstables to accumulate
+# during a single long running compactions. The default is usually
+# fine and if you experience problems with compaction running too
+# slowly or too fast, you should look at
+# compaction_throughput_mb_per_sec first.
+#
+# concurrent_compactors defaults to the smaller of (number of disks,
+# number of cores), with a minimum of 2 and a maximum of 8.
+#
+# If your data directories are backed by SSD, you should increase this
+# to the number of cores.
+#concurrent_compactors: 1
+
+# Throttles compaction to the given total throughput across the entire
+# system. The faster you insert data, the faster you need to compact in
+# order to keep the sstable count down, but in general, setting this to
+# 16 to 32 times the rate you are inserting data is more than sufficient.
+# Setting this to 0 disables throttling. Note that this account for all types
+# of compaction, including validation compaction.
+compaction_throughput_mb_per_sec: 16
+
+# Log a warning when compacting partitions larger than this value
+compaction_large_partition_warning_threshold_mb: 100
+
+# When compacting, the replacement sstable(s) can be opened before they
+# are completely written, and used in place of the prior sstables for
+# any range that has been written. This helps to smoothly transfer reads
+# between the sstables, reducing page cache churn and keeping hot rows hot
+sstable_preemptive_open_interval_in_mb: 50
+
+# Throttles all outbound streaming file transfers on this node to the
+# given total throughput in Mbps. This is necessary because Cassandra does
+# mostly sequential IO when streaming data during bootstrap or repair, which
+# can lead to saturating the network connection and degrading rpc performance.
+# When unset, the default is 200 Mbps or 25 MB/s.
+# stream_throughput_outbound_megabits_per_sec: 200
+
+# Throttles all streaming file transfer between the datacenters,
+# this setting allows users to throttle inter dc stream throughput in addition
+# to throttling all network stream traffic as configured with
+# stream_throughput_outbound_megabits_per_sec
+# When unset, the default is 200 Mbps or 25 MB/s
+# inter_dc_stream_throughput_outbound_megabits_per_sec: 200
+
+# How long the coordinator should wait for read operations to complete
+read_request_timeout_in_ms: 5000
+# How long the coordinator should wait for seq or index scans to complete
+range_request_timeout_in_ms: 10000
+# How long the coordinator should wait for writes to complete
+write_request_timeout_in_ms: 2000
+# How long the coordinator should wait for counter writes to complete
+counter_write_request_timeout_in_ms: 5000
+# How long a coordinator should continue to retry a CAS operation
+# that contends with other proposals for the same row
+cas_contention_timeout_in_ms: 1000
+# How long the coordinator should wait for truncates to complete
+# (This can be much longer, because unless auto_snapshot is disabled
+# we need to flush first so we can snapshot before removing the data.)
+truncate_request_timeout_in_ms: 60000
+# The default timeout for other, miscellaneous operations
+request_timeout_in_ms: 10000
+
+# Enable operation timeout information exchange between nodes to accurately
+# measure request timeouts. If disabled, replicas will assume that requests
+# were forwarded to them instantly by the coordinator, which means that
+# under overload conditions we will waste that much extra time processing
+# already-timed-out requests.
+#
+# Warning: before enabling this property make sure to ntp is installed
+# and the times are synchronized between the nodes.
+cross_node_timeout: false
+
+# Set socket timeout for streaming operation.
+# The stream session is failed if no data/ack is received by any of the participants
+# within that period, which means this should also be sufficient to stream a large
+# sstable or rebuild table indexes.
+# Default value is 86400000ms, which means stale streams timeout after 24 hours.
+# A value of zero means stream sockets should never time out.
+# streaming_socket_timeout_in_ms: 86400000
+
+# phi value that must be reached for a host to be marked down.
+# most users should never need to adjust this.
+# phi_convict_threshold: 8
+phi_convict_threshold: <%= node['cassandra']['phi_convict_threshold'] %>
+
+# endpoint_snitch -- Set this to a class that implements
+# IEndpointSnitch. The snitch has two functions:
+# - it teaches Cassandra enough about your network topology to route
+# requests efficiently
+# - it allows Cassandra to spread replicas around your cluster to avoid
+# correlated failures. It does this by grouping machines into
+# "datacenters" and "racks." Cassandra will do its best not to have
+# more than one replica on the same "rack" (which may not actually
+# be a physical location)
+#
+# CASSANDRA WILL NOT ALLOW YOU TO SWITCH TO AN INCOMPATIBLE SNITCH
+# ONCE DATA IS INSERTED INTO THE CLUSTER. This would cause data loss.
+# This means that if you start with the default SimpleSnitch, which
+# locates every node on "rack1" in "datacenter1", your only options
+# if you need to add another datacenter are GossipingPropertyFileSnitch
+# (and the older PFS). From there, if you want to migrate to an
+# incompatible snitch like Ec2Snitch you can do it by adding new nodes
+# under Ec2Snitch (which will locate them in a new "datacenter") and
+# decommissioning the old ones.
+#
+# Out of the box, Cassandra provides
+# - SimpleSnitch:
+# Treats Strategy order as proximity. This can improve cache
+# locality when disabling read repair. Only appropriate for
+# single-datacenter deployments.
+# - GossipingPropertyFileSnitch
+# This should be your go-to snitch for production use. The rack
+# and datacenter for the local node are defined in
+# cassandra-rackdc.properties and propagated to other nodes via
+# gossip. If cassandra-topology.properties exists, it is used as a
+# fallback, allowing migration from the PropertyFileSnitch.
+# - PropertyFileSnitch:
+# Proximity is determined by rack and data center, which are
+# explicitly configured in cassandra-topology.properties.
+# - Ec2Snitch:
+# Appropriate for EC2 deployments in a single Region. Loads Region
+# and Availability Zone information from the EC2 API. The Region is
+# treated as the datacenter, and the Availability Zone as the rack.
+# Only private IPs are used, so this will not work across multiple
+# Regions.
+# - Ec2MultiRegionSnitch:
+# Uses public IPs as broadcast_address to allow cross-region
+# connectivity. (Thus, you should set seed addresses to the public
+# IP as well.) You will need to open the storage_port or
+# ssl_storage_port on the public IP firewall. (For intra-Region
+# traffic, Cassandra will switch to the private IP after
+# establishing a connection.)
+# - RackInferringSnitch:
+# Proximity is determined by rack and data center, which are
+# assumed to correspond to the 3rd and 2nd octet of each node's IP
+# address, respectively. Unless this happens to match your
+# deployment conventions, this is best used as an example of
+# writing a custom Snitch class and is provided in that spirit.
+#
+# You can use a custom Snitch by setting this to the full class name
+# of the snitch, which will be assumed to be on your classpath.
+endpoint_snitch: GossipingPropertyFileSnitch
+
+# controls how often to perform the more expensive part of host score
+# calculation
+dynamic_snitch_update_interval_in_ms: 100
+# controls how often to reset all host scores, allowing a bad host to
+# possibly recover
+dynamic_snitch_reset_interval_in_ms: 600000
+# if set greater than zero and read_repair_chance is < 1.0, this will allow
+# 'pinning' of replicas to hosts in order to increase cache capacity.
+# The badness threshold will control how much worse the pinned host has to be
+# before the dynamic snitch will prefer other replicas over it. This is
+# expressed as a double which represents a percentage. Thus, a value of
+# 0.2 means Cassandra would continue to prefer the static snitch values
+# until the pinned host was 20% worse than the fastest.
+dynamic_snitch_badness_threshold: 0.1
+
+# request_scheduler -- Set this to a class that implements
+# RequestScheduler, which will schedule incoming client requests
+# according to the specific policy. This is useful for multi-tenancy
+# with a single Cassandra cluster.
+# NOTE: This is specifically for requests from the client and does
+# not affect inter node communication.
+# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
+# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
+# client requests to a node with a separate queue for each
+# request_scheduler_id. The scheduler is further customized by
+# request_scheduler_options as described below.
+request_scheduler: org.apache.cassandra.scheduler.NoScheduler
+
+# Scheduler Options vary based on the type of scheduler
+# NoScheduler - Has no options
+# RoundRobin
+# - throttle_limit -- The throttle_limit is the number of in-flight
+# requests per client. Requests beyond
+# that limit are queued up until
+# running requests can complete.
+# The value of 80 here is twice the number of
+# concurrent_reads + concurrent_writes.
+# - default_weight -- default_weight is optional and allows for
+# overriding the default which is 1.
+# - weights -- Weights are optional and will default to 1 or the
+# overridden default_weight. The weight translates into how
+# many requests are handled during each turn of the
+# RoundRobin, based on the scheduler id.
+#
+# request_scheduler_options:
+# throttle_limit: 80
+# default_weight: 5
+# weights:
+# Keyspace1: 1
+# Keyspace2: 5
+
+# request_scheduler_id -- An identifier based on which to perform
+# the request scheduling. Currently the only valid option is keyspace.
+# request_scheduler_id: keyspace
+
+# Enable or disable inter-node encryption
+# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
+# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
+# suite for authentication, key exchange and encryption of the actual data transfers.
+# Use the DHE/ECDHE ciphers if running in FIPS 140 compliant mode.
+# NOTE: No custom encryption options are enabled at the moment
+# The available internode options are : all, none, dc, rack
+#
+# If set to dc cassandra will encrypt the traffic between the DCs
+# If set to rack cassandra will encrypt the traffic between the racks
+#
+# The passwords used in these options must match the passwords used when generating
+# the keystore and truststore. For instructions on generating these files, see:
+# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
+#
+server_encryption_options:
+ internode_encryption: none
+ keystore: <%= @cassandra_truststore_dir %>/.keystore
+ keystore_password: Aa123456
+ truststore: <%= @cassandra_truststore_dir %>/.truststore
+ truststore_password: Aa123456
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
+ # require_client_auth: false
+
+# enable or disable client/server encryption.
+client_encryption_options:
+ enabled: false
+ keystore: <%= @cassandra_truststore_dir %>/.keystore
+ keystore_password: Aa123456
+ # require_client_auth: false
+ # Set trustore and truststore_password if require_client_auth is true
+ # truststore: conf/.truststore
+ # truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]
+
+# internode_compression controls whether traffic between nodes is
+# compressed.
+# can be: all - all traffic is compressed
+# dc - traffic between different datacenters is compressed
+# none - nothing is compressed.
+internode_compression: all
+
+# Enable or disable tcp_nodelay for inter-dc communication.
+# Disabling it will result in larger (but fewer) network packets being sent,
+# reducing overhead from the TCP protocol itself, at the cost of increasing
+# latency if you block for cross-datacenter responses.
+inter_dc_tcp_nodelay: false
+
+# GC Pauses greater than gc_warn_threshold_in_ms will be logged at WARN level
+# Adjust the threshold based on your application throughput requirement
+# By default, Cassandra logs GC Pauses greater than 200 ms at INFO level
+# gc_warn_threshold_in_ms: 1000
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/configuration.yaml.erb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/configuration.yaml.erb
new file mode 100644
index 0000000000..e33ece4890
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/configuration.yaml.erb
@@ -0,0 +1,459 @@
+identificationHeaderFields:
+ - HTTP_IV_USER
+ - HTTP_CSP_FIRSTNAME
+ - HTTP_CSP_LASTNAME
+ - HTTP_IV_REMOTE_ADDRESS
+ - HTTP_CSP_WSTYPE
+
+
+# catalog backend hostname
+beFqdn: <%= @host_ip %>
+
+# catalog backend http port
+beHttpPort: <%= @catalog_port %>
+
+# catalog backend http context
+beContext: /sdc/rest/config/get
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: <%= @ssl_port %>
+version: 1.0
+released: 2012-11-30
+
+titanCfgFile: /var/lib/jetty/config/catalog-be/titan.properties
+titanInMemoryGraph: false
+titanLockTimeout: 1800
+# The interval to try and reconnect to titan DB when it is down during ASDC startup:
+titanReconnectIntervalInSeconds: 3
+
+# The read timeout towards Titan DB when health check is invoked:
+titanHealthCheckReadTimeout: 1
+
+# The interval to try and reconnect to Elasticsearch when it is down during ASDC startup:
+
+esReconnectIntervalInSeconds: 3
+uebHealthCheckReconnectIntervalInSeconds: 15
+uebHealthCheckReadTimeout: 4
+
+# Protocols
+protocols:
+ - http
+ - https
+
+# Users
+users:
+ tom: passwd
+ bob: passwd
+
+
+cassandraConfig:
+ cassandraHosts: [<%= @cassandra_ip %>]
+ localDataCenter:
+ reconnectTimeout : 30000
+ authenticate: true
+ username: asdc_user
+ password: Aa1234%^!
+ ssl: false
+ truststorePath : /config/.truststore
+ truststorePassword : Aa123456
+ keySpaces:
+ - { name: dox, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @dc1 %>','<%= @rep_factor %>']}
+ - { name: sdcaudit, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @dc1 %>','<%= @rep_factor %>']}
+ - { name: sdcartifact, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @dc1 %>','<%= @rep_factor %>']}
+ - { name: sdccomponent, replicationStrategy: NetworkTopologyStrategy, replicationInfo: ['<%= @dc1 %>','<%= @rep_factor %>']}
+
+#Application-specific settings of ES
+elasticSearch:
+ # Mapping of index prefix to time-based frame. For example, if below is configured:
+ #
+ # - indexPrefix: auditingevents
+ # creationPeriod: minute
+ #
+ # then ES object of type which is mapped to "auditingevents-*" template, and created on 2015-12-23 13:24:54, will enter "auditingevents-2015-12-23-13-24" index.
+ # Another object created on 2015-12-23 13:25:54, will enter "auditingevents-2015-12-23-13-25" index.
+ # If creationPeriod: month, both of the above will enter "auditingevents-2015-12" index.
+ #
+ # PLEASE NOTE: the timestamps are created in UTC/GMT timezone! This is needed so that timestamps will be correctly presented in Kibana.
+ #
+ # Legal values for creationPeriod - year, month, day, hour, minute, none (meaning no time-based behaviour).
+ #
+ # If no creationPeriod is configured for indexPrefix, default behavour is creationPeriod: month.
+
+ indicesTimeFrequency:
+ - indexPrefix: auditingevents
+ creationPeriod: month
+ - indexPrefix: monitoring_events
+ creationPeriod: month
+artifactTypes:
+ - CHEF
+ - PUPPET
+ - SHELL
+ - YANG
+ - YANG_XML
+ - HEAT
+ - BPEL
+ - DG_XML
+ - MURANO_PKG
+ - WORKFLOW
+ - NETWORK_CALL_FLOW
+ - TOSCA_TEMPLATE
+ - TOSCA_CSAR
+ - AAI_SERVICE_MODEL
+ - AAI_VF_MODEL
+ - AAI_VF_MODULE_MODEL
+ - AAI_VF_INSTANCE_MODEL
+ - OTHER
+
+
+licenseTypes:
+ - User
+ - Installation
+ - CPU
+
+#Deployment artifacts placeHolder
+resourceTypes: &allResourceTypes
+ - VFC
+ - CP
+ - VL
+ - VF
+
+# validForResourceTypes usage
+# validForResourceTypes:
+# - VF
+# - VL
+deploymentResourceArtifacts:
+
+
+deploymentResourceInstanceArtifacts:
+ heatEnv:
+ displayName: "HEAT ENV"
+ type: HEAT_ENV
+ description: "Auto-generated HEAT Environment deployment artifact"
+ fileExtension: "env"
+
+#tosca artifacts placeholders
+toscaArtifacts:
+ assetToscaTemplate:
+ artifactName: -template.yml
+ displayName: Tosca Template
+ type: TOSCA_TEMPLATE
+ description: TOSCA representation of the asset
+ assetToscaCsar:
+ artifactName: -csar.csar
+ displayName: Tosca Model
+ type: TOSCA_CSAR
+ description: TOSCA definition package of the asset
+
+#Informational artifacts placeHolder
+excludeResourceCategory:
+ - Generic
+informationalResourceArtifacts:
+ features:
+ displayName: Features
+ type: OTHER
+ capacity:
+ displayName: Capacity
+ type: OTHER
+ vendorTestResult:
+ displayName: Vendor Test Result
+ type: OTHER
+ testScripts:
+ displayName: Test Scripts
+ type: OTHER
+ cloudQuestionnaire:
+ displayName: Cloud Questionnaire (completed)
+ type: OTHER
+ HEATTemplateFromVendor:
+ displayName: HEAT Template from Vendor
+ type: HEAT
+ resourceSecurityTemplate:
+ displayName: Resource Security Template
+ type: OTHER
+
+excludeServiceCategory:
+
+informationalServiceArtifacts:
+ serviceArtifactPlan:
+ displayName: Service Artifact Plan
+ type: OTHER
+ summaryOfImpactsToECOMPElements:
+ displayName: Summary of impacts to ECOMP elements,OSSs, BSSs
+ type: OTHER
+ controlLoopFunctions:
+ displayName: Control Loop Functions
+ type: OTHER
+ dimensioningInfo:
+ displayName: Dimensioning Info
+ type: OTHER
+ affinityRules:
+ displayName: Affinity Rules
+ type: OTHER
+ operationalPolicies:
+ displayName: Operational Policies
+ type: OTHER
+ serviceSpecificPolicies:
+ displayName: Service-specific Policies
+ type: OTHER
+ engineeringRules:
+ displayName: Engineering Rules (ERD)
+ type: OTHER
+ distributionInstructions:
+ displayName: Distribution Instructions
+ type: OTHER
+ certificationTestResults:
+ displayName: TD Certification Test Results
+ type: OTHER
+ deploymentVotingRecord:
+ displayName: Deployment Voting Record
+ type: OTHER
+ serviceQuestionnaire:
+ displayName: Service Questionnaire
+ type: OTHER
+ serviceSecurityTemplate:
+ displayName: Service Security Template
+ type: OTHER
+
+serviceApiArtifacts:
+ configuration:
+ displayName: Configuration
+ type: OTHER
+ instantiation:
+ displayName: Instantiation
+ type: OTHER
+ monitoring:
+ displayName: Monitoring
+ type: OTHER
+ reporting:
+ displayName: Reporting
+ type: OTHER
+ logging:
+ displayName: Logging
+ type: OTHER
+ testing:
+ displayName: Testing
+ type: OTHER
+
+
+additionalInformationMaxNumberOfKeys: 50
+
+systemMonitoring:
+ enabled: true
+ isProxy: false
+ probeIntervalInSeconds: 15
+defaultHeatArtifactTimeoutMinutes: 60
+
+serviceDeploymentArtifacts:
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ AAI_SERVICE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ AAI_VF_INSTANCE_MODEL:
+ acceptedTypes:
+ - xml
+ OTHER:
+ acceptedTypes:
+
+
+resourceDeploymentArtifacts:
+ HEAT:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_VOL:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NET:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_NESTED:
+ acceptedTypes:
+ - yaml
+ - yml
+ validForResourceTypes: *allResourceTypes
+ HEAT_ARTIFACT:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+ YANG_XML:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VNF_CATALOG:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VF_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ VENDOR_LICENSE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_INVENTORY_PROFILE:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ MODEL_QUERY_SPEC:
+ acceptedTypes:
+ - xml
+ validForResourceTypes: *allResourceTypes
+ APPC_CONFIG:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ #DCAE Artifacts
+ DCAE_TOSCA:
+ acceptedTypes:
+ - yml
+ - yaml
+ validForResourceTypes:
+ - VF
+ DCAE_JSON:
+ acceptedTypes:
+ - json
+ validForResourceTypes:
+ - VF
+ DCAE_POLICY:
+ acceptedTypes:
+ - emf
+ validForResourceTypes:
+ - VF
+ DCAE_DOC:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+ DCAE_EVENT:
+ acceptedTypes:
+ validForResourceTypes:
+ - VF
+#AAI Artifacts
+ AAI_VF_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ AAI_VF_MODULE_MODEL:
+ acceptedTypes:
+ - xml
+ validForResourceTypes:
+ - VF
+ OTHER:
+ acceptedTypes:
+ validForResourceTypes: *allResourceTypes
+
+resourceInstanceDeploymentArtifacts:
+ HEAT_ENV:
+ acceptedTypes:
+ - env
+ VF_MODULES_METADATA:
+ acceptedTypes:
+ - json
+#DCAE_VF Instance Artifacts
+ DCAE_INVENTORY_TOSCA:
+ acceptedTypes:
+ - yml
+ - yaml
+ DCAE_INVENTORY_JSON:
+ acceptedTypes:
+ - json
+ DCAE_INVENTORY_POLICY:
+ acceptedTypes:
+ - emf
+ DCAE_INVENTORY_DOC:
+ acceptedTypes:
+ DCAE_INVENTORY_BLUEPRINT:
+ acceptedTypes:
+ DCAE_INVENTORY_EVENT:
+ acceptedTypes:
+resourceInformationalDeployedArtifacts:
+
+
+requirementsToFulfillBeforeCert:
+
+capabilitiesToConsumeBeforeCert:
+
+unLoggedUrls:
+ - /sdc2/rest/healthCheck
+
+cleanComponentsConfiguration:
+ cleanIntervalInMinutes: 1440
+ componentsToClean:
+ - Resource
+ - Service
+
+artifactsIndex: resources
+
+heatEnvArtifactHeader:
+ ""
+heatEnvArtifactFooter:
+ ""
+
+onboarding:
+ protocol: http
+ host: <%= @host_ip %>
+ port: <%= @catalog_port %>
+ downloadCsarUri: "/onboarding-api/v1.0/vendor-software-products/packages"
+
+
+# #GSS IDNS
+switchoverDetector:
+ gBeFqdn:
+ gFeFqdn:
+ beVip: 1.2.3.4
+ feVip: 1.2.3.4
+ beResolveAttempts: 3
+ feResolveAttempts: 3
+ enabled: false
+ interval: 60
+ changePriorityUser: ecompasdc
+ changePriorityPassword: ecompasdc123
+ publishNetworkUrl:
+ publishNetworkBody: '{"note":"comment"}'
+ groups:
+ beSet: { changePriorityUrl: "", changePriorityBody: '{"name":"","uri":"","no_ad_redirection":false,"v4groups":{"failover_groups":["","","failover_policy":["FAILALL"]},"comment":"","intended_app_proto":"DNS"}'}
+ feSet: { changePriorityUrl: "", changePriorityBody: '{"name":"","uri":"","no_ad_redirection":false,"v4groups":{"failover_groups":["",""],"failover_policy":["FAILALL"]},"comment":"","intended_app_proto":"DNS"}'}
+
+applicationL1Cache:
+ datatypes:
+ enabled: true
+ firstRunDelay: 10
+ pollIntervalInSec: 60
+
+applicationL2Cache:
+ enabled: true
+ catalogL1Cache:
+ enabled: true
+ resourcesSizeInCache: 300
+ servicesSizeInCache: 200
+ productsSizeInCache: 100
+ queue:
+ syncIntervalInSecondes: 43200
+ waitOnShutDownInMinutes: 10
+ numberOfCacheWorkers: 4
+
+toscaValidators:
+ stringMaxLength: 65536
+
+disableAudit: false
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_cassandra_user.sh.erb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_cassandra_user.sh.erb
new file mode 100644
index 0000000000..6b972244c2
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_cassandra_user.sh.erb
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+CASSANDRA_USER=asdc_user
+CASSANDRA_PASS=Aa1234%^!
+CASSANDRA_IP=<%= @cassandra_ip %>
+
+pass_changed=99
+retry_num=1
+is_up=0
+while [ $is_up -eq 0 -a $retry_num -le 100 ]; do
+ echo "exit" | cqlsh -u cassandra -p cassandra $CASSANDRA_IP > /dev/null 2>&1
+ res1=$?
+ echo "exit" | cqlsh -u cassandra -p $CASSANDRA_PASS $CASSANDRA_IP > /dev/null 2>&1
+ res2=$?
+
+ if [ $res1 -eq 0 -o $res2 -eq 0 ]; then
+ echo "`date` --- cqlsh is enabled to connect."
+ is_up=1
+ [ $res1 -eq 0 ] && pass_changed=0
+ [ $res2 -eq 0 ] && pass_changed=1
+ else
+ echo "`date` --- cqlsh is NOT enabled to connect yet. sleep 5"
+ sleep 5
+ fi
+ let "retry_num++"
+done
+
+
+echo "pass_changed=[$pass_changed]"
+case $pass_changed in
+ 0)
+ cassandra_user_exist=`echo "list users;" | cqlsh -u cassandra -p cassandra $CASSANDRA_IP |grep -c $CASSANDRA_USER`
+ if [ $cassandra_user_exist -eq 1 ] ; then
+ echo "cassandra user $CASSANDRA_USER already exist"
+ echo "alter user $CASSANDRA_USER with password '$CASSANDRA_PASS' nosuperuser;" | cqlsh -u cassandra -p cassandra $CASSANDRA_IP
+ else
+ echo "Going to create $CASSANDRA_USER"
+ echo "create user $CASSANDRA_USER with password '$CASSANDRA_PASS' nosuperuser;" | cqlsh -u cassandra -p cassandra $CASSANDRA_IP
+ fi
+ echo "Modify cassandra password"
+ echo "ALTER USER cassandra WITH PASSWORD '$CASSANDRA_PASS';" | cqlsh -u cassandra -p cassandra $CASSANDRA_IP
+ ;;
+ 1)
+ cassandra_user_exist=`echo "list users;" | cqlsh -u cassandra -p $CASSANDRA_PASS $CASSANDRA_IP |grep -c $CASSANDRA_USER`
+ if [ $cassandra_user_exist -eq 1 ] ; then
+ echo "cassandra user $CASSANDRA_USER already exist"
+ echo "alter user $CASSANDRA_USER with password '$CASSANDRA_PASS' nosuperuser;" | cqlsh -u cassandra -p $CASSANDRA_PASS $CASSANDRA_IP
+ else
+ echo "Going to create $CASSANDRA_USER"
+ echo "create user $CASSANDRA_USER with password '$CASSANDRA_PASS' nosuperuser;" | cqlsh -u cassandra -p $CASSANDRA_PASS $CASSANDRA_IP
+ fi
+ ;;
+ *)
+ echo "pass_changed doen't have value"
+ ;;
+esac
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_dox_keyspace.sh.erb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_dox_keyspace.sh.erb
new file mode 100644
index 0000000000..701d320b67
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/create_dox_keyspace.sh.erb
@@ -0,0 +1,77 @@
+#!/bin/bash
+
+CASSANDRA_USER=cassandra
+CASSANDRA_PASS=Aa1234%^!
+CASSANDRA_IP=<%= @cassandra_ip %>
+
+REPLICATION_FACTOR=1
+KEYSPACE="CREATE KEYSPACE IF NOT EXISTS dox WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : $REPLICATION_FACTOR };"
+
+keyspaces=`echo "describe keyspaces" | cqlsh -u $CASSANDRA_USER -p $CASSANDRA_PASS $CASSANDRA_IP 2>/dev/null`
+if [ "$keyspaces" == "" ] ; then
+ echo "keyspace is empty, probably connection error. Exit..."
+ exit 1
+fi
+
+echo "run create_dox_db.cql"
+echo $KEYSPACE > /tmp/create_dox_db.cql
+cat <<'EOF' >> /tmp/create_dox_db.cql
+USE dox;
+CREATE TYPE IF NOT EXISTS version (major int, minor int);
+CREATE TYPE IF NOT EXISTS user_candidate_version (version frozen<version>, user text);
+CREATE TABLE IF NOT EXISTS version_info (entity_type text, entity_id text, active_version frozen<version>, status text, candidate frozen<user_candidate_version>, viewable_versions set<frozen<version>>, latest_final_version frozen<version>, PRIMARY KEY (entity_type, entity_id));
+CREATE TABLE IF NOT EXISTS version_info_deleted (entity_type text, entity_id text, active_version frozen<version>, status text, candidate frozen<user_candidate_version>, viewable_versions set<frozen<version>>, latest_final_version frozen<version>, PRIMARY KEY (entity_type, entity_id));
+CREATE TABLE IF NOT EXISTS unique_value (type text, value text, PRIMARY KEY ((type, value)));
+CREATE TYPE IF NOT EXISTS choice_or_other (result text);
+CREATE TYPE IF NOT EXISTS multi_choice_or_other (results set<text>);
+CREATE TABLE IF NOT EXISTS vendor_license_model (vlm_id text, version frozen<version>, vendor_name text, description text, icon text, PRIMARY KEY ((vlm_id, version)));
+CREATE TABLE IF NOT EXISTS license_agreement (vlm_id text, version frozen<version>, la_id text, name text, description text, lic_term frozen<choice_or_other>, req_const text, fg_ids set<text>, PRIMARY KEY ((vlm_id, version), la_id));
+CREATE TABLE IF NOT EXISTS feature_group (vlm_id text, version frozen<version>, fg_id text, name text, description text, part_num text, ep_ids set<text>, lkg_ids set<text>, ref_la_ids set<text>, PRIMARY KEY ((vlm_id, version), fg_id));
+CREATE TABLE IF NOT EXISTS license_key_group (vlm_id text, version frozen<version>, lkg_id text,name text,description text, type text, operational_scope frozen<multi_choice_or_other>, ref_fg_ids set<text>,version_uuid text,PRIMARY KEY ((vlm_id, version), lkg_id));
+CREATE TABLE IF NOT EXISTS entitlement_pool (vlm_id text, version frozen<version>, ep_id text,name text,description text,threshold float,threshold_unit text,entitlement_metric frozen<choice_or_other>,increments text,aggregation_func frozen<choice_or_other>, operational_scope frozen<multi_choice_or_other>, time frozen<choice_or_other>,manufacturer_ref_num text,ref_fg_ids set<text>,version_uuid text,PRIMARY KEY ((vlm_id, version), ep_id));
+CREATE TABLE IF NOT EXISTS vsp_information (VSP_ID text, version frozen<version>,NAME text,DESCRIPTION text,CATEGORY text,SUB_CATEGORY text,ICON text,PACKAGE_NAME text,PACKAGE_VERSION text,vendor_name text, vendor_id text,LICENSE_AGREEMENT text,FEATURE_GROUPS list<text>,VALIDATION_DATA text,CONTENT_DATA blob, questionnaire_data text, vlm_version frozen<version>, PRIMARY KEY ((VSP_ID, version)));
+CREATE TABLE IF NOT EXISTS package_details (VSP_ID text, version frozen<version>,DISPLAY_NAME text,vsp_name text,vsp_description text,VENDOR_NAME text,CATEGORY text,SUB_CATEGORY text,VENDOR_RELEASE text,PACKAGE_CHECKSUM text,PACKAGE_TYPE text,TRANSLATE_CONTENT blob,PRIMARY KEY ((VSP_ID, version)));
+CREATE TABLE IF NOT EXISTS vsp_network (vsp_id text, version frozen<version>, network_id text, composition_data text, questionnaire_data text, PRIMARY KEY ((vsp_id, version), network_id));
+CREATE TABLE IF NOT EXISTS vsp_component (vsp_id text, version frozen<version>, component_id text, composition_data text, questionnaire_data text, PRIMARY KEY ((vsp_id, version), component_id));
+CREATE TABLE IF NOT EXISTS vsp_component_nic (vsp_id text, version frozen<version>, component_id text, nic_id text, composition_data text, questionnaire_data text, PRIMARY KEY ((vsp_id, version), component_id, nic_id));
+CREATE TABLE IF NOT EXISTS vsp_process (vsp_id text, version frozen<version>, component_id text, process_id text, name text, description text, artifact_name text, artifact blob, PRIMARY KEY ((vsp_id, version), component_id, process_id));
+CREATE TABLE IF NOT EXISTS vsp_service_artifact (vsp_id text, version frozen<version>, name text, content_data blob, PRIMARY KEY ((vsp_id, version), name));
+CREATE TABLE IF NOT EXISTS vsp_service_template (vsp_id text, version frozen<version>, base_name text static, name text, content_data blob, PRIMARY KEY ((vsp_id, version), name));
+CREATE TABLE IF NOT EXISTS vsp_enriched_service_template (vsp_id text, version frozen<version>, base_name text static, name text, content_data blob, PRIMARY KEY ((vsp_id, version), name));
+CREATE TABLE IF NOT EXISTS vsp_enriched_service_artifact (vsp_id text, version frozen<version>, name text, content_data blob, PRIMARY KEY ((vsp_id, version), name));
+CREATE TABLE IF NOT EXISTS application_config (namespace text, key text, value text, PRIMARY KEY (namespace, key));
+CREATE TABLE IF NOT EXISTS dox.Action (actionUUID text, actionInvariantUUID text, version frozen<version>, status text, name text, vendor_list set<text>, category_list set<text>, timestamp timestamp, user text, supportedModels set<text>, supportedComponents set<text>, data text, PRIMARY KEY ((actionInvariantUUID, version)));
+CREATE INDEX IF NOT EXISTS action_supportedComponents ON dox.Action (supportedComponents);
+CREATE INDEX IF NOT EXISTS action_category_list ON dox.Action (category_list);
+CREATE INDEX IF NOT EXISTS action_supportedModels ON dox.Action (supportedModels);
+CREATE INDEX IF NOT EXISTS action_vendor_list ON dox.Action (vendor_list);
+CREATE INDEX IF NOT EXISTS action_actionUUID ON dox.Action (actionUUID);
+CREATE TABLE IF NOT EXISTS dox.ecompcomponent(id text PRIMARY KEY, name text);
+CREATE TABLE IF NOT EXISTS vsp_component_artifact (vsp_id text, version frozen<version>, component_id text, artifact_type text, artifact_id text, name text, description text, artifact blob, PRIMARY KEY ((vsp_id, version), component_id, artifact_type, artifact_id));
+CREATE INDEX IF NOT EXISTS action_name ON dox.Action (name);
+CREATE TABLE IF NOT EXISTS action_artifact(artifactuuid text, effective_version int, artifact blob, PRIMARY KEY(artifactuuid, effective_version)) WITH CLUSTERING ORDER BY (effective_version DESC);
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.schemaTemplates', 'composition.component', '{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "name": { "type": "string"<#if !manual>, "enum": [ "${component.name}" ], "default": "${component.name}"</#if> }, "displayName": { "type": "string"<#if !manual && component.displayName??>, "enum": [ "${component.displayName}" ], "default": "${component.displayName}"</#if> }, "description": { "type": "string" } }, "additionalProperties": false, "required": [ "name"<#if !manual && component.displayName??>, "displayName"</#if> ] }');
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.schemaTemplates', 'composition.network', '{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "name": { "type": "string"<#if !manual>, "enum": [ "${network.name}" ], "default": "${network.name}"</#if> }, "dhcp": { "type": "boolean"<#if !manual>, "enum": [ ${network.dhcp?c} ], "default": ${network.dhcp?c}</#if> } }, "additionalProperties": false, "required": [ "name", "dhcp" ] }');
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.schemaTemplates', 'composition.nic', '{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "name": { "type": "string"<#if !manual>, "enum": [ "${nic.name}" ], "default": "${nic.name}" </#if> }, "description": { "type": "string" }<#if !manual><#if nic.networkId??>, "networkId": { "type": "string", "enum": [ "${nic.networkId}" ], "default": "${nic.networkId}" } </#if><#else>, "networkId": { "type": "string", "enum": [<#list networkIds as networkId> "${networkId}"<#sep>,</#list> ] } </#if> }, "additionalProperties": false, "required": [ "name" ] }');
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.schemaTemplates', 'questionnaire.component', '{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "general": { "type": "object", "properties": { "hypervisor": { "type": "object", "properties": { "hypervisor": { "type": "string", "enum": [ "KVM", "VMWare ESXi" ], "default": "KVM" }, "drivers": { "type": "string", "maxLength": 300, "pattern": "^[A-Za-z0-9_,-]*$" }, "containerFeaturesDescription": { "type": "string", "maxLength": 1000, "pattern": "^[A-Za-z0-9_, -]*$" } }, "additionalProperties": false }, "image": { "type": "object", "properties": { "format": { "type": "string", "enum": [ "aki", "ami", "ari", "iso", "qcow2", "raw", "vdi", "vhd", "vmdk" ], "default": "qcow2" }, "providedBy": { "type": "string", "enum": [ "OPENECOMP", "Vendor" ], "default": "OPENECOMP" }, "bootDiskSizePerVM": { "type": "number", "maximum": 100 }, "ephemeralDiskSizePerVM": { "type": "number", "maximum": 400 } }, "additionalProperties": false }, "recovery": { "type": "object", "properties": { "pointObjective": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "maximum": 15, "exclusiveMaximum ": true }, "timeObjective": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "maximum": 300, "exclusiveMaximum ": true }, "vmProcessFailuresHandling": { "type": "string" } }, "additionalProperties": false }, "dnsConfiguration": { "type": "string" }, "vmCloneUsage": { "type": "string", "maximum": 300 } }, "additionalProperties": false }, "compute": { "type": "object", "properties": { "vmSizing": { "type": "object", "properties": { "numOfCPUs": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "maximum": 16, "default": 2 }, "fileSystemSizeGB": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "default": 5 }, "persistentStorageVolumeSize": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "IOOperationsPerSec": { "type": "number", "minimum": 0, "exclusiveMinimum": true } }, "additionalProperties": false }, "numOfVMs": { "type": "object", "properties": { "minimum": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "maximum": 100 }, "maximum": { "type": "number", "minimum": <#if (componentQuestionnaireData.compute.numOfVMs.minimum)?? && (componentQuestionnaireData.compute.numOfVMs.minimum)?is_number && ((componentQuestionnaireData.compute.numOfVMs.minimum) > 0 && (componentQuestionnaireData.compute.numOfVMs.minimum) <= 100)> ${componentQuestionnaireData.compute.numOfVMs.minimum}<#else> 0</#if>, "exclusiveMinimum": true, "maximum": 100 }, "CpuOverSubscriptionRatio": { "type": "string", "enum": [ "1:1", "4:1", "16:1" ], "default": "4:1" }, "MemoryRAM": { "type": "string", "enum": [ "2 GB", "4 GB", "8 GB" ], "default": "2 GB" } }, "additionalProperties": false }, "guestOS": { "type": "object", "properties": { "name": { "type": "string", "maxLength": 50 }, "bitSize": { "type": "number", "enum": [ 64, 32 ], "default": 64 }, "tools": { "type": "string" } }, "additionalProperties": false } }, "additionalProperties": false }, "highAvailabilityAndLoadBalancing": { "type": "object", "properties": { "failureLoadDistribution": { "type": "string", "maxLength": 1000 }, "nkModelImplementation": { "type": "string", "maxLength": 1000 }, "architectureChoice": { "type": "string", "maxLength": 1000 }, "slaRequirements": { "type": "string", "maxLength": 1000 }, "horizontalScaling": { "type": "string", "maxLength": 1000 }, "loadDistributionMechanism": { "type": "string", "maxLength": 1000 } }, "additionalProperties": false }, "network": { "type": "object", "properties": { "networkCapacity": { "type": "object", "properties": { "protocolWithHighestTrafficProfileAcrossAllNICs": { "type": "string", "enum": [ "TCP", "UDP", "SCTP", "IPsec" ] }, "networkTransactionsPerSecond": { "type": "number" } }, "additionalProperties": false } }, "additionalProperties": false }, "storage": { "type": "object", "properties": { "backup": { "type": "object", "properties": { "backupType": { "type": "string", "enum": [ "On Site", "Off Site" ], "default": "On Site" }, "backupStorageSize": { "type": "number" }, "backupSolution": { "type": "string" }, "backupNIC": { "type": "string", "enum": [<#if nicNames??><#list nicNames as nicName> "${nicName}"<#sep>,</#list></#if> ] } }, "additionalProperties": false }, "snapshotBackup": { "type": "object", "properties": { "snapshotFrequency": { "type": "number", "default": 24, "minimum": 1, "exclusiveMinimum": true } }, "additionalProperties": false }, "logBackup": { "type": "object", "properties": { "sizeOfLogFiles": { "type": "number", "maximum": 5, "exclusiveMaximum": true }, "logBackupFrequency": { "type": "number", "maximum": 4, "exclusiveMaximum": true }, "logRetentionPeriod": { "type": "number", "maximum": 15, "exclusiveMaximum": true }, "logFileLocation": { "type": "string", "maxLength": 300 } }, "additionalProperties": false } }, "additionalProperties": false } }, "additionalProperties": false }');
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.schemaTemplates', 'questionnaire.nic', '{ "$schema": "http://json-schema.org/draft-04/schema#", "definitions": { "protocol": { "type": "string", "enum": [ "TCP", "UDP", "SCTP", "IPsec" ] } }, "type": "object", "properties": { "protocols": { "type": "object", "properties": { "protocols": { "type": "array", "items": { "$ref": "#/definitions/protocol" }, "minItems": 1 }, "protocolWithHighestTrafficProfile": { "$ref": "#/definitions/protocol" } }, "additionalProperties": false }, "ipConfiguration": { "type": "object", "properties": { "ipv4Required": { "type": "boolean", "default": true }, "ipv6Required": { "type": "boolean", "default": false } }, "additionalProperties": false }, "network": { "type": "object", "properties": { "networkDescription": { "type": "string", "pattern": "[A-Za-z]+", "maxLength": 300 } }, "additionalProperties": false }, "sizing": { "type": "object", "definitions": { "peakAndAvg": { "type": "object", "properties": { "peak": { "type": "number" }, "avg": { "type": "number" } }, "additionalProperties": false }, "packetsAndBytes": { "type": "object", "properties": { "packets": { "$ref": "#/properties/sizing/definitions/peakAndAvg" }, "bytes": { "$ref": "#/properties/sizing/definitions/peakAndAvg" } }, "additionalProperties": false } }, "properties": { "describeQualityOfService": { "type": "string" }, "inflowTrafficPerSecond": { "$ref": "#/properties/sizing/definitions/packetsAndBytes" }, "outflowTrafficPerSecond": { "$ref": "#/properties/sizing/definitions/packetsAndBytes" }, "flowLength": { "$ref": "#/properties/sizing/definitions/packetsAndBytes" }, "acceptableJitter": { "type": "object", "properties": { "mean": { "type": "number" }, "max": { "type": "number" }, "variable": { "type": "number" } }, "additionalProperties": false }, "acceptablePacketLoss": { "type": "number", "minimum": 0, "maximum": 100 } }, "additionalProperties": false } }, "additionalProperties": false }');
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.schemaTemplates', 'questionnaire.vsp', '{ "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "general": { "type": "object", "properties": { "affinityData": { "type": "string", "enum": [ "Affinity", "Anti Affinity", "None" ] }, "availability": { "type": "object", "properties": { "useAvailabilityZonesForHighAvailability": { "type": "boolean", "default": false } }, "additionalProperties": false }, "regionsData": { "type": "object", "properties": { "multiRegion": { "type": "boolean", "default": false }, "regions": { "type": "array", "items": { "type": "string", "enum": [ "Alphareta", "Birmingham", "Dallas", "Fairfield CA", "Hayward CA", "Lisle", "Mission", "San Diego", "Secaucus" ] } } }, "additionalProperties": false }, "storageDataReplication": { "type": "object", "properties": { "storageReplicationAcrossRegion": { "type": "boolean", "default": false }, "storageReplicationSize": { "type": "number", "maximum": 100, "exclusiveMaximum": true }, "storageReplicationFrequency": { "type": "number", "minimum": 5 }, "storageReplicationSource": { "type": "string", "maxLength": 300 }, "storageReplicationDestination": { "type": "string", "maxLength": 300 } }, "additionalProperties": false } }, "additionalProperties": false } }, "additionalProperties": false }');
+INSERT INTO application_config (namespace,key,value) VALUES ('vsp.monitoring', 'component.ceilometer', '{ "ceilometerInfoList": [ { "name": "instance", "type": "Gauge", "unit": "instance", "category": "compute", "description": "Existence of instance" }, { "name": "instance:type", "type": "Gauge", "unit": "instance", "category": "compute", "description": "Existence of instance <type> (OpenStack types)" }, { "name": "memory", "type": "Gauge", "unit": "MB", "category": "compute", "description": "Volume of RAM allocated to the instance" }, { "name": "memory.usage", "type": "Gauge", "unit": "MB", "category": "compute", "description": "Volume of RAM used by the instance from the amount of its allocated memory" }, { "name": "memory.resident", "type": "Gauge", "unit": "MB", "category": "compute", "description": "Volume of RAM used by the instance on the physical machine" }, { "name": "cpu", "type": "Cumulative", "unit": "ns", "category": "compute", "description": "CPU time used" }, { "name": "cpu_util", "type": "Gauge", "unit": "%", "category": "compute", "description": "Average CPU utilization" }, { "name": "cpu.delta", "type": "Delta", "unit": "ns", "category": "compute", "description": "CPU time used since previous datapoint" }, { "name": "vcpus", "type": "Gauge", "unit": "ms", "category": "compute", "description": "Average disk latency" } ] }');
+ALTER TABLE vsp_information ADD questionnaire_data text;
+ALTER TABLE vsp_information ADD vlm_version frozen<version>;
+ALTER TABLE entitlement_pool ADD version_uuid text;
+ALTER TABLE license_key_group ADD version_uuid text;
+
+EOF
+
+chmod 777 /tmp/create_dox_db.cql
+cqlsh -u $CASSANDRA_USER -p $CASSANDRA_PASS $CASSANDRA_IP -f /tmp/create_dox_db.cql > /dev/null 2>&1
+
+res=`echo "select keyspace_name from system.schema_keyspaces ;" | cqlsh -u $CASSANDRA_USER -p $CASSANDRA_PASS $CASSANDRA_IP |grep -c dox 2>/dev/null`
+
+if [ $res -eq 1 ]; then
+ echo "`date` --- dox keyspace was created "
+else
+ echo "`date` --- Failed to create dox keyspace"
+fi
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/elasticsearch.yml.erb b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/elasticsearch.yml.erb
new file mode 100644
index 0000000000..79d11f4610
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-repo/cookbooks/cassandra-actions/templates/default/elasticsearch.yml.erb
@@ -0,0 +1,11 @@
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+node.name: asdc-01
+cluster.name: elasticsearch
+node.master: false
+node.data: false
+http.cors.enabled: true
+path.home: "/var/lib/jetty/config"
+elasticSearch.transportclient: true
+transport.client.initial_nodes:
+ - <%= @elastic_ip %>:9300
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/LICENSE b/sdc-os-chef/sdc-cassandra/chef-solo/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/README.md b/sdc-os-chef/sdc-cassandra/chef-solo/README.md
new file mode 100644
index 0000000000..ddb0fda830
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/README.md
@@ -0,0 +1,37 @@
+Deprecated
+==========
+
+Use of this repository is deprecated. We recommend using the `chef generate repo` command that comes with [ChefDK](http://downloads.chef.io/chef-dk/).
+
+Overview
+========
+
+Every Chef installation needs a Chef Repository. This is the place where cookbooks, roles, config files and other artifacts for managing systems with Chef will live. We strongly recommend storing this repository in a version control system such as Git and treat it like source code.
+
+While we prefer Git, and make this repository available via GitHub, you are welcome to download a tar or zip archive and use your favorite version control system to manage the code.
+
+Repository Directories
+======================
+
+This repository contains several directories, and each directory contains a README file that describes what it is for in greater detail, and how to use it for managing your systems with Chef.
+
+* `cookbooks/` - Cookbooks you download or create.
+* `data_bags/` - Store data bags and items in .json in the repository.
+* `roles/` - Store roles in .rb or .json in the repository.
+* `environments/` - Store environments in .rb or .json in the repository.
+
+Configuration
+=============
+
+The repository contains a knife configuration file.
+
+* .chef/knife.rb
+
+The knife configuration file `.chef/knife.rb` is a repository specific configuration file for knife. If you're using Hosted Chef, you can download one for your organization from the management console. If you're using the Open Source Chef Server, you can generate a new one with `knife configure`. For more information about configuring Knife, see the Knife documentation.
+
+https://docs.chef.io/knife.html
+
+Next Steps
+==========
+
+Read the README file in each of the subdirectories for more information about what goes in those directories.
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/chefignore b/sdc-os-chef/sdc-cassandra/chef-solo/chefignore
new file mode 100644
index 0000000000..ba30af6cff
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/chefignore
@@ -0,0 +1,11 @@
+# Put files/directories that should be ignored in this file.
+# Lines that start with '# ' are comments.
+
+# emacs
+*~
+
+# vim
+*.sw[a-z]
+
+# subversion
+*/.svn/*
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/cookbooks/README.md b/sdc-os-chef/sdc-cassandra/chef-solo/cookbooks/README.md
new file mode 100644
index 0000000000..86ea46bfbb
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/cookbooks/README.md
@@ -0,0 +1,54 @@
+This directory contains the cookbooks used to configure systems in your infrastructure with Chef.
+
+Knife needs to be configured to know where the cookbooks are located with the `cookbook_path` setting. If this is not set, then several cookbook operations will fail to work properly.
+
+ cookbook_path ["./cookbooks"]
+
+This setting tells knife to look for the cookbooks directory in the present working directory. This means the knife cookbook subcommands need to be run in the `chef-repo` directory itself. To make sure that the cookbooks can be found elsewhere inside the repository, use an absolute path. This is a Ruby file, so something like the following can be used:
+
+ current_dir = File.dirname(__FILE__)
+ cookbook_path ["#{current_dir}/../cookbooks"]
+
+Which will set `current_dir` to the location of the knife.rb file itself (e.g. `~/chef-repo/.chef/knife.rb`).
+
+Configure knife to use your preferred copyright holder, email contact and license. Add the following lines to `.chef/knife.rb`.
+
+ cookbook_copyright "Example, Com."
+ cookbook_email "cookbooks@example.com"
+ cookbook_license "apachev2"
+
+Supported values for `cookbook_license` are "apachev2", "mit","gplv2","gplv3", or "none". These settings are used to prefill comments in the default recipe, and the corresponding values in the metadata.rb. You are free to change the the comments in those files.
+
+Create new cookbooks in this directory with Knife.
+
+ knife cookbook create COOKBOOK
+
+This will create all the cookbook directory components. You don't need to use them all, and can delete the ones you don't need. It also creates a README file, metadata.rb and default recipe.
+
+You can also download cookbooks directly from the Opscode Cookbook Site. There are two subcommands to help with this depending on what your preference is.
+
+The first and recommended method is to use a vendor branch if you're using Git. This is automatically handled with Knife.
+
+ knife cookbook site install COOKBOOK
+
+This will:
+
+* Download the cookbook tarball from cookbooks.opscode.com.
+* Ensure its on the git master branch.
+* Checks for an existing vendor branch, and creates if it doesn't.
+* Checks out the vendor branch (chef-vendor-COOKBOOK).
+* Removes the existing (old) version.
+* Untars the cookbook tarball it downloaded in the first step.
+* Adds the cookbook files to the git index and commits.
+* Creates a tag for the version downloaded.
+* Checks out the master branch again.
+* Merges the cookbook into master.
+* Repeats the above for all the cookbooks dependencies, downloading them from the community site
+
+The last step will ensure that any local changes or modifications you have made to the cookbook are preserved, so you can keep your changes through upstream updates.
+
+If you're not using Git, use the site download subcommand to download the tarball.
+
+ knife cookbook site download COOKBOOK
+
+This creates the COOKBOOK.tar.gz from in the current directory (e.g., `~/chef-repo`). We recommend following a workflow similar to the above for your version control tool.
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/data_bags/README.md b/sdc-os-chef/sdc-cassandra/chef-solo/data_bags/README.md
new file mode 100644
index 0000000000..0c15a391fa
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/data_bags/README.md
@@ -0,0 +1,63 @@
+Data Bags
+---------
+
+This directory contains directories of the various data bags you create for your infrastructure. Each subdirectory corresponds to a data bag on the Chef Server, and contains JSON files of the items that go in the bag.
+
+First, create a directory for the data bag.
+
+ mkdir data_bags/BAG
+
+Then create the JSON files for items that will go into that bag.
+
+ $EDITOR data_bags/BAG/ITEM.json
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM". For example,
+
+ {
+ "id": "foo"
+ }
+
+Next, create the data bag on the Chef Server.
+
+ knife data bag create BAG
+
+Then upload the items in the data bag's directory to the Chef Server.
+
+ knife data bag from file BAG ITEM.json
+
+
+Encrypted Data Bags
+-------------------
+
+Added in Chef 0.10, encrypted data bags allow you to encrypt the contents of your data bags. The content of attributes will no longer be searchable. To use encrypted data bags, first you must have or create a secret key.
+
+ openssl rand -base64 512 > secret_key
+
+You may use this secret_key to add items to a data bag during a create.
+
+ knife data bag create --secret-file secret_key passwords mysql
+
+You may also use it when adding ITEMs from files,
+
+ knife data bag create passwords
+ knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM" and the contents will be encrypted when uploaded. For example,
+
+ {
+ "id": "mysql",
+ "password": "abc123"
+ }
+
+Without the secret_key, the contents are encrypted.
+
+ knife data bag show passwords mysql
+ id: mysql
+ password: 2I0XUUve1TXEojEyeGsjhw==
+
+Use the secret_key to view the contents.
+
+ knife data bag show passwords mysql --secret-file secret_key
+ id: mysql
+ password: abc123
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/environments/README.md b/sdc-os-chef/sdc-cassandra/chef-solo/environments/README.md
new file mode 100644
index 0000000000..50ac48db2b
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/environments/README.md
@@ -0,0 +1,5 @@
+Requires Chef 0.10.0+.
+
+This directory is for Ruby DSL and JSON files for environments. For more information see the Chef wiki page:
+
+http://docs.chef.io/environments.html
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/roles/README.md b/sdc-os-chef/sdc-cassandra/chef-solo/roles/README.md
new file mode 100644
index 0000000000..b0ee0b4d21
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/roles/README.md
@@ -0,0 +1,16 @@
+Create roles here, in either the Role Ruby DSL (.rb) or JSON (.json) files. To install roles on the server, use knife.
+
+For example, create `roles/base_example.rb`:
+
+ name "base_example"
+ description "Example base role applied to all nodes."
+ # List of recipes and roles to apply. Requires Chef 0.8, earlier versions use 'recipes()'.
+ #run_list()
+ # Attributes applied if the node doesn't have it set already.
+ #default_attributes()
+ # Attributes applied no matter what the node has set already.
+ #override_attributes()
+
+Then upload it to the Chef Server:
+
+ knife role from file roles/base_example.rb
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/roles/cassandra-actions.json b/sdc-os-chef/sdc-cassandra/chef-solo/roles/cassandra-actions.json
new file mode 100644
index 0000000000..8d8931c2b5
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/roles/cassandra-actions.json
@@ -0,0 +1,23 @@
+{
+ "name": "cassandra-actions",
+ "description": "cassandra-actions",
+ "json_class": "Chef::Role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "chef_type": "role",
+ "run_list": [
+ "recipe[cassandra-actions::02-createCsUser]",
+ "recipe[cassandra-actions::03-createDoxKeyspace]",
+ "recipe[cassandra-actions::04-schemaCreation]"
+ ],
+
+ "env_run_lists": {
+
+ }
+}
+
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/solo.json b/sdc-os-chef/sdc-cassandra/chef-solo/solo.json
new file mode 100644
index 0000000000..97b1efe282
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/solo.json
@@ -0,0 +1,6 @@
+{
+ "run_list": [
+ "role[cassandra-actions]"
+ ]
+}
+
diff --git a/sdc-os-chef/sdc-cassandra/chef-solo/solo.rb b/sdc-os-chef/sdc-cassandra/chef-solo/solo.rb
new file mode 100644
index 0000000000..06c1af4592
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/chef-solo/solo.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/solo.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-cassandra/startup.sh b/sdc-os-chef/sdc-cassandra/startup.sh
new file mode 100644
index 0000000000..61a5109fa1
--- /dev/null
+++ b/sdc-os-chef/sdc-cassandra/startup.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+cd /root/chef-solo
+export CHEFNAME=${ENVNAME}
+
+sed -i "s/HOSTIP/${HOST_IP}/g" /root/chef-solo/cookbooks/cassandra-actions/recipes/02-createCsUser.rb
+sed -i "s/HOSTIP/${HOST_IP}/g" /root/chef-solo/cookbooks/cassandra-actions/recipes/03-createDoxKeyspace.rb
+sed -i "s/HOSTIP/${HOST_IP}/g" /root/chef-solo/cookbooks/cassandra-actions/recipes/04-schemaCreation.rb
+
+chef-solo -c solo.rb -o recipe[cassandra-actions::01-configureCassandra] -E ${CHEFNAME}
+rc=$?
+
+if [[ $rc != 0 ]]; then exit $rc; fi
+echo "########### starting cassandra ###########"
+# start cassandra
+/docker-entrypoint.sh cassandra -f &
+
+chef-solo -c solo.rb -E ${CHEFNAME}
+
+cd /tmp/
+/tmp/create_cassandra_user.sh
+/tmp/create_dox_keyspace.sh
+/bin/chmod +x sdctool/scripts/*.sh
+./sdctool/scripts/schemaCreation.sh /tmp/sdctool/config
+
+while true; do sleep 2; done
+
diff --git a/sdc-os-chef/sdc-elasticsearch/Dockerfile b/sdc-os-chef/sdc-elasticsearch/Dockerfile
new file mode 100644
index 0000000000..2bdba96398
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/Dockerfile
@@ -0,0 +1,21 @@
+FROM elasticsearch:2.1.2
+
+RUN apt-get -y update
+RUN apt-get -y install apt-utils
+RUN apt-get -y install curl
+RUN apt-get -y install vim
+RUN mkdir -p /var/chef/nodes
+
+COPY chef-solo /root/chef-solo/
+COPY chef-repo/cookbooks /root/chef-solo/cookbooks/
+
+ENV BASE_NEXUS zl999y:ChangeMe@10.208.197.75:8443/repository/maven-public/org/openecomp/sdc
+
+# install chef-solo
+RUN curl -L https://www.opscode.com/chef/install.sh | bash
+
+COPY startup.sh /root/
+
+RUN chmod 770 /root/startup.sh
+
+ENTRYPOINT [ "/root/startup.sh" ]
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/attributes/default.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/attributes/default.rb
new file mode 100644
index 0000000000..4287ca8617
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/attributes/default.rb
@@ -0,0 +1 @@
+# \ No newline at end of file
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_BI-Dashboard.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_BI-Dashboard.json
new file mode 100644
index 0000000000..c740a94f5d
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_BI-Dashboard.json
@@ -0,0 +1,13 @@
+{
+ "title": "BI Dashboard",
+ "hits": 0,
+ "description": "",
+ "panelsJSON": "[{\"col\":1,\"id\":\"Show-all-certified-services-ampersand-resources-(per-day)\",\"panelIndex\":1,\"row\":1,\"size_x\":6,\"size_y\":5,\"type\":\"visualization\"},{\"col\":7,\"id\":\"Show-all-distributed-services\",\"panelIndex\":2,\"row\":1,\"size_x\":6,\"size_y\":5,\"type\":\"visualization\"},{\"id\":\"Show-all-created-Resources-slash-Services-slash-Products\",\"type\":\"visualization\",\"panelIndex\":3,\"size_x\":6,\"size_y\":4,\"col\":1,\"row\":6},{\"id\":\"number-of-user-accesses\",\"type\":\"visualization\",\"panelIndex\":4,\"size_x\":6,\"size_y\":4,\"col\":7,\"row\":6}]",
+ "optionsJSON": "{\"darkTheme\":false}",
+ "uiStateJSON": "{}",
+ "version": 1,
+ "timeRestore": false,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"filter\":[{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}}}]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_Monitoring-Dashboared.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_Monitoring-Dashboared.json
new file mode 100644
index 0000000000..41b9c00c6e
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/dashboard_Monitoring-Dashboared.json
@@ -0,0 +1,15 @@
+{
+ "title": "Monitoring Dashboared",
+ "hits": 0,
+ "description": "",
+ "panelsJSON": "[{\"id\":\"JVM-used-CPU\",\"type\":\"visualization\",\"panelIndex\":1,\"size_x\":6,\"size_y\":5,\"col\":1,\"row\":5},{\"id\":\"JVM-used-Memory\",\"type\":\"visualization\",\"panelIndex\":2,\"size_x\":6,\"size_y\":4,\"col\":1,\"row\":1},{\"id\":\"JVM-used-Threads-Num\",\"type\":\"visualization\",\"panelIndex\":3,\"size_x\":6,\"size_y\":4,\"col\":7,\"row\":1},{\"id\":\"host-used-CPU\",\"type\":\"visualization\",\"panelIndex\":4,\"size_x\":3,\"size_y\":2,\"col\":7,\"row\":5},{\"id\":\"host-used-Memory\",\"type\":\"visualization\",\"panelIndex\":5,\"size_x\":3,\"size_y\":2,\"col\":10,\"row\":5},{\"id\":\"host-used-Threads-Num\",\"type\":\"visualization\",\"panelIndex\":6,\"size_x\":3,\"size_y\":2,\"col\":7,\"row\":7}]",
+ "optionsJSON": "{\"darkTheme\":false}",
+ "uiStateJSON": "{}",
+ "version": 1,
+ "timeRestore": true,
+ "timeTo": "now",
+ "timeFrom": "now-24h",
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"filter\":[{\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}}}]}"
+ }
+} \ No newline at end of file
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/logging.yml b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/logging.yml
new file mode 100644
index 0000000000..a78fb3c0e3
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/logging.yml
@@ -0,0 +1,23 @@
+# you can override this using by setting a system property, for example -Des.logger.level=DEBUG
+es.logger.level: INFO
+rootLogger: ${es.logger.level}, console
+logger:
+ # log action execution errors for easier debugging
+ action: DEBUG
+ # reduce the logging for aws, too much is logged under the default INFO
+ com.amazonaws: WARN
+
+appender:
+ console:
+ type: console
+ layout:
+ type: consolePattern
+ conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %m%n"
+
+ file:
+ type: dailyRollingFile
+ file: ${path.logs}/${cluster.name}.log
+ datePattern: "'.'yyyy-MM-dd"
+ layout:
+ type: pattern
+ conversionPattern: "[%d{ISO8601}][%-5p][%-25c] %.10000m%n"
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-CPU.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-CPU.json
new file mode 100644
index 0000000000..45565cf0a4
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-CPU.json
@@ -0,0 +1,10 @@
+{
+ "title": "JVM used CPU",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"jvmcpu\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"timestamp\",\"interval\":\"h\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"jvmid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"4\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"hostid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"monitoring_events-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+} \ No newline at end of file
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Memory.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Memory.json
new file mode 100644
index 0000000000..c030df4655
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Memory.json
@@ -0,0 +1,10 @@
+{
+ "title": "JVM used Memory",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"jvmmem\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"timestamp\",\"interval\":\"h\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"jvmid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"4\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"hostid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"monitoring_events-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Threads-Num.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Threads-Num.json
new file mode 100644
index 0000000000..28d0468e9d
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_JVM-used-Threads-Num.json
@@ -0,0 +1,10 @@
+{
+ "title": "JVM used Threads Num",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"jvmtnum\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"timestamp\",\"interval\":\"h\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"jvmid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}},{\"id\":\"4\",\"type\":\"terms\",\"schema\":\"split\",\"params\":{\"field\":\"hostid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"row\":true}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"monitoring_events-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-certified-services-ampersand-resources-(per-day).json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-certified-services-ampersand-resources-(per-day).json
new file mode 100644
index 0000000000..7d7ce6e4d4
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-certified-services-ampersand-resources-(per-day).json
@@ -0,0 +1,10 @@
+{
+ "title": "Show all certified services & resources (per day)",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"TIMESTAMP\",\"interval\":\"d\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"auditingevents-*\",\"query\":{\"query_string\":{\"query\":\"ACTION:\\\"CertificationSuccess\\\" AND (RESOURCE_TYPE:\\\"Resource\\\" OR RESOURCE_TYPE:\\\"Service\\\")\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-created-Resources-slash-Services-slash-Products.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-created-Resources-slash-Services-slash-Products.json
new file mode 100644
index 0000000000..37e06ca3ca
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-created-Resources-slash-Services-slash-Products.json
@@ -0,0 +1,10 @@
+{
+ "title": "Show all created Resources/Services/Products",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"TIMESTAMP\",\"interval\":\"d\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"RESOURCE_TYPE\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"auditingevents-*\",\"query\":{\"query_string\":{\"query\":\"ACTION:\\\"Create\\\"\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-distributed-services.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-distributed-services.json
new file mode 100644
index 0000000000..5d375733f5
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_Show-all-distributed-services.json
@@ -0,0 +1,10 @@
+{
+ "title": "Show all distributed services",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"TIMESTAMP\",\"interval\":\"d\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"auditingevents-*\",\"query\":{\"query_string\":{\"query\":\"ACTION:\\\"DResult\\\"\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-CPU.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-CPU.json
new file mode 100644
index 0000000000..3c9f7d5425
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-CPU.json
@@ -0,0 +1,10 @@
+{
+ "title": "host used CPU",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"hostcpu\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"timestamp\",\"interval\":\"h\",\"customInterval\":\"24h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"hostid\",\"size\":10,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"monitoring_events-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-Threads-Num.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-Threads-Num.json
new file mode 100644
index 0000000000..a6a7c09395
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_host-used-Threads-Num.json
@@ -0,0 +1,10 @@
+{
+ "title": "host used Threads Num",
+ "visState": "{\"type\":\"line\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"showCircles\":true,\"smoothLines\":false,\"interpolate\":\"linear\",\"scale\":\"linear\",\"drawLinesBetweenPoints\":true,\"radiusRatio\":9,\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"avg\",\"schema\":\"metric\",\"params\":{\"field\":\"jvmtnum\"}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"timestamp\",\"interval\":\"h\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}},{\"id\":\"3\",\"type\":\"terms\",\"schema\":\"group\",\"params\":{\"field\":\"hostid\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"monitoring_events-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_number-of-user-accesses.json b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_number-of-user-accesses.json
new file mode 100644
index 0000000000..fdcd88c16f
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/files/default/visualization_number-of-user-accesses.json
@@ -0,0 +1,10 @@
+{
+ "title": "number of user accesses",
+ "visState": "{\"type\":\"line\",\"params\":{\"addLegend\":true,\"addTimeMarker\":false,\"addTooltip\":true,\"defaultYExtents\":false,\"drawLinesBetweenPoints\":true,\"interpolate\":\"linear\",\"radiusRatio\":9,\"scale\":\"linear\",\"setYExtents\":false,\"shareYAxis\":true,\"showCircles\":true,\"smoothLines\":false,\"times\":[],\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"TIMESTAMP\",\"interval\":\"d\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}",
+ "uiStateJSON": "{}",
+ "description": "",
+ "version": 1,
+ "kibanaSavedObjectMeta": {
+ "searchSourceJSON": "{\"index\":\"auditingevents-*\",\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"ACTION:\\\"Access\\\"\"}},\"filter\":[]}"
+ }
+}
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_1_setup_elasticsearch.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_1_setup_elasticsearch.rb
new file mode 100644
index 0000000000..854898c1b7
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_1_setup_elasticsearch.rb
@@ -0,0 +1,16 @@
+clusterName = node['elasticsearch'][:cluster_name]+node.chef_environment
+node_name = node[:hostname]
+
+template "/usr/share/elasticsearch/config/elasticsearch.yml" do
+ source "ES-elasticsearch.yml.erb"
+ owner "elasticsearch"
+ group "elasticsearch"
+ mode "0755"
+ variables({
+ :cluster_name => "#{clusterName}",
+ :node_name => node_name,
+ :ES_IP => node['Nodes']['ES'],
+ :num_of_shards => node['elasticsearch'][:num_of_shards],
+ :num_of_replicas => node['elasticsearch'][:num_of_replicas]
+ })
+end
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_2_setup_logging.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_2_setup_logging.rb
new file mode 100644
index 0000000000..5e462966ed
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_2_setup_logging.rb
@@ -0,0 +1,6 @@
+cookbook_file "/usr/share/elasticsearch/config/logging.yml" do
+ source "logging.yml"
+ owner "elasticsearch"
+ group "elasticsearch"
+ mode "0755"
+end
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_3_create_audit_template.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_3_create_audit_template.rb
new file mode 100644
index 0000000000..2d882631f2
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_3_create_audit_template.rb
@@ -0,0 +1,238 @@
+ruby_block "check_ElasticSearch_Cluster_Health" do
+ block do
+ #tricky way to load this Chef::Mixin::ShellOut utilities
+ Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
+ #curl_command = "http://#{node['ipaddress']}:9200/_cluster/health?pretty=true"
+ curl_command = "http://localhost:9200/_cluster/health?pretty=true"
+ resp = Net::HTTP.get_response URI.parse(curl_command)
+ stat = JSON.parse(resp.read_body)['status']
+
+ case stat
+ when "green"
+ printf("\033[32m%s\n\033[0m", " ElasticSearch Cluster status is green.")
+ when "yellow"
+ printf("\033[33m%s\n\033[0m", " ElasticSearch Cluster status is yellow...")
+ when "red"
+ printf("\033[31m%s\n\033[0m", " ElasticSearch Cluster status is red!")
+ end
+ end
+ retries 10
+ retry_delay 2
+end
+
+
+bash "create audit mapping" do
+ code <<-EOH
+ curl -i -X PUT -d '{ "order": 1, "template": "auditingevents-*", "settings": {}, "mappings":
+ {
+ "distributiondownloadevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "auditinggetuebclusterevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "distributionstatusevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "distributionengineevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ROLE": { "include_in_all": true, "type": "string" },
+ "API_KEY": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "D_ENV": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CONSUMER_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "useraccessevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER": { "include_in_all": true, "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "resourceadminevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "INVARIANT_UUID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "PREV_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "PREV_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DPREV_STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DCURR_STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOSCA_NODE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "COMMENT": { "include_in_all": true, "type": "string" },
+ "ARTIFACT_DATA": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "PREV_ARTIFACT_UUID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_ARTIFACT_UUID": { "include_in_all": true, "index": "not_analyzed", "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "useradminevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER_AFTER": { "include_in_all": true, "type": "string" },
+ "USER_BEFORE": { "include_in_all": true, "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "distributionnotificationevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_STATE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TOPIC_NAME":{ "include_in_all": true, "index": "not_analyzed", "type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "categoryevent": {
+ "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CATEGORY_NAME": { "include_in_all": true, "type": "string" },
+ "SUB_CATEGORY_NAME": { "include_in_all": true, "type": "string" },
+ "GROUPING_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }
+ },
+ "_all": { "enabled": true }
+ },
+ "authevent": {
+ "properties": {
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "URL": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "USER": { "include_in_all": true, "type": "string" },
+ "AUTH_STATUS": { "include_in_all": true, "index": "not_analyzed","type": "string" } ,
+ "REALM": { "include_in_all": true, "index": "not_analyzed","type": "string" }
+ },
+ "_all": { "enabled": true }
+ },
+ "consumerevent": {
+ "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "ECOMP_USER": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }
+ },
+ "_all": { "enabled": true }
+ },
+ "getuserslistevent": {
+ "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DETAILS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }
+ },
+ "_all": { "enabled": true }
+ },
+ "getcategoryhierarchyevent": {
+ "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DETAILS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }
+ },
+ "_all": { "enabled": true }
+ },
+ "distributiondeployevent": {
+ "properties": {
+ "ACTION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "CURR_VERSION": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "DESC": { "include_in_all": true, "type": "string" },
+ "DID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "MODIFIER": { "include_in_all": true, "type": "string" },
+ "REQUEST_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "RESOURCE_NAME": { "include_in_all": true, "type": "string" },
+ "RESOURCE_TYPE": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "SERVICE_INSTANCE_ID": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "STATUS": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "TIMESTAMP": { "include_in_all": true, "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }
+ },
+ "_all": { "enabled": true } }
+ },
+ "aliases": { "last_3_months": {}}}' http://localhost:9200/_template/audit_template
+ EOH
+end
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_4_create_resources_template.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_4_create_resources_template.rb
new file mode 100644
index 0000000000..4dc264153d
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_4_create_resources_template.rb
@@ -0,0 +1,37 @@
+ruby_block "check_ElasticSearch_Cluster_Health" do
+ block do
+ #tricky way to load this Chef::Mixin::ShellOut utilities
+ Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
+ #curl_command = "http://#{node['ipaddress']}:9200/_cluster/health?pretty=true"
+ curl_command = "http://localhost:9200/_cluster/health?pretty=true"
+ resp = Net::HTTP.get_response URI.parse(curl_command)
+ stat = JSON.parse(resp.read_body)['status']
+
+ case stat
+ when "green"
+ printf("\033[32m%s\n\033[0m", " ElasticSearch Cluster status is green.")
+ when "yellow"
+ printf("\033[33m%s\n\033[0m", " ElasticSearch Cluster status is yellow...")
+ when "red"
+ printf("\033[31m%s\n\033[0m", " ElasticSearch Cluster status is red!")
+ end
+ end
+ retries 10
+ retry_delay 2
+end
+
+bash "create resources mapping" do
+ code <<-EOH
+ curl -i -X PUT -d '{ "order": 1, "template": "resources", "settings": {}, "mappings":
+ {
+ "esartifactdata": {
+ "properties": {
+ "id": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "data": { "include_in_all": false, "type": "string" }
+ },
+ "_all": { "enabled": true }
+ }
+ }
+ }' http://localhost:9200/_template/resources_template
+ EOH
+end \ No newline at end of file
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_5_create_monitoring_template.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_5_create_monitoring_template.rb
new file mode 100644
index 0000000000..dfb68c1dfa
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_5_create_monitoring_template.rb
@@ -0,0 +1,47 @@
+ruby_block "check_ElasticSearch_Cluster_Health" do
+ block do
+ #tricky way to load this Chef::Mixin::ShellOut utilities
+ Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
+ #curl_command = "http://#{node['ipaddress']}:9200/_cluster/health?pretty=true"
+ curl_command = "http://localhost:9200/_cluster/health?pretty=true"
+ resp = Net::HTTP.get_response URI.parse(curl_command)
+ stat = JSON.parse(resp.read_body)['status']
+
+ case stat
+ when "green"
+ printf("\033[32m%s\n\033[0m", " ElasticSearch Cluster status is green.")
+ when "yellow"
+ printf("\033[33m%s\n\033[0m", " ElasticSearch Cluster status is yellow...")
+ when "red"
+ printf("\033[31m%s\n\033[0m", " ElasticSearch Cluster status is red!")
+ end
+ end
+ retries 10
+ retry_delay 2
+end
+
+bash "create monitoring mapping" do
+ code <<-EOH
+ curl -i -X PUT -d '{ "order": 1, "template": "monitoring_events-*", "settings": {}, "mappings":
+ {
+ "monitoringevent": {
+ "properties": {
+ "hostid": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "hostcpu": { "include_in_all": true, "type": "long" },
+ "hostmem": { "include_in_all": true, "type": "double" },
+ "hostdisk": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "jvmid": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "jvmcpu": { "include_in_all": true,"type": "long" },
+ "jvmmem": { "include_in_all": true, "type": "long" },
+ "jvmtnum": { "include_in_all": true, "type": "integer" },
+ "appid": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "appstat": { "include_in_all": true, "index": "not_analyzed", "type": "string" },
+ "timestamp": { "include_in_all": true, "index": "not_analyzed", "ignore_malformed": false, "format": "yyyy-MM-dd HH:mm:ss.SSS z", "precision_step": 4, "type": "date" }
+ },
+ "_all": { "enabled": true }
+ }
+ },
+ "aliases": { "last_3_months": {} }
+ }' http://localhost:9200/_template/monitoring_template
+ EOH
+end
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_6_kibana_dashboard_virtualization.rb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_6_kibana_dashboard_virtualization.rb
new file mode 100644
index 0000000000..a3b15073a1
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/recipes/ES_6_kibana_dashboard_virtualization.rb
@@ -0,0 +1,57 @@
+cookbook_file "/usr/share/elasticsearch/config/kibana_dashboard_virtualization.json" do
+ source "kibana_dashboard_virtualization.json"
+ owner "elasticsearch"
+ group "elasticsearch"
+ mode "0755"
+end
+
+
+
+ruby_block "check_ElasticSearch_Cluster_Health" do
+ block do
+ #tricky way to load this Chef::Mixin::ShellOut utilities
+ Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
+ #curl_command = "http://#{node['ipaddress']}:9200/_cluster/health?pretty=true"
+ curl_command = "http://localhost:9200/_cluster/health?pretty=true"
+ resp = Net::HTTP.get_response URI.parse(curl_command)
+ stat = JSON.parse(resp.read_body)['status']
+
+ case stat
+ when "green"
+ printf("\033[32m%s\n\033[0m", " ElasticSearch Cluster status is green.")
+ when "yellow"
+ printf("\033[33m%s\n\033[0m", " ElasticSearch Cluster status is yellow...")
+ when "red"
+ printf("\033[31m%s\n\033[0m", " ElasticSearch Cluster status is red!")
+ end
+ end
+ retries 10
+ retry_delay 2
+end
+
+
+bash "create Kibana dashboard" do
+ code <<-EOH
+ for file in /root/chef-solo/cookbooks/sdc-elasticsearch/files/default/dashboard_*.json; do
+ name=`basename $file .json | awk -F"_" '{print $2}'`
+ echo "Loading dashboard $name:"
+ curl -XPUT http://localhost:9200/.kibana/dashboard/$name -d @$file || exit 1
+ echo
+ done
+ EOH
+end
+
+
+bash "create Kibana visualization" do
+ code <<-EOH
+ for file in /root/chef-solo/cookbooks/sdc-elasticsearch/files/default/visualization_*.json; do
+ name=`basename $file .json | awk -F"_" '{print $2}'`
+ echo "Loading visualization $name:"
+ curl -XPUT http://localhost:9200/.kibana/visualization/$name -d @$file || exit 1
+ echo
+ done
+ EOH
+end
+
+
+
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/templates/default/ES-elasticsearch.yml.erb b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/templates/default/ES-elasticsearch.yml.erb
new file mode 100644
index 0000000000..613e6b046b
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-repo/cookbooks/sdc-elasticsearch/templates/default/ES-elasticsearch.yml.erb
@@ -0,0 +1,16 @@
+network.host: 0.0.0.0
+discovery.zen.ping.multicast.enabled: false
+discovery.zen.ping.unicast.enabled: true
+#discovery.zen.ping.unicast.hosts: <%= @ES_IP %>
+
+cluster.name: <%= @cluster_name %>
+node.name: <%= @node_name %>
+elasticSearch.transportclient: true
+
+transport.client.initial_nodes:
+ - <%= @ES_IP %>:9300
+
+index.number_of_shards: <%= @num_of_shards %>
+index.number_of_replicas: <%= @num_of_replicas %>
+
+path.logs: /usr/share/elasticsearch/logs
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/LICENSE b/sdc-os-chef/sdc-elasticsearch/chef-solo/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/README.md b/sdc-os-chef/sdc-elasticsearch/chef-solo/README.md
new file mode 100644
index 0000000000..ddb0fda830
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/README.md
@@ -0,0 +1,37 @@
+Deprecated
+==========
+
+Use of this repository is deprecated. We recommend using the `chef generate repo` command that comes with [ChefDK](http://downloads.chef.io/chef-dk/).
+
+Overview
+========
+
+Every Chef installation needs a Chef Repository. This is the place where cookbooks, roles, config files and other artifacts for managing systems with Chef will live. We strongly recommend storing this repository in a version control system such as Git and treat it like source code.
+
+While we prefer Git, and make this repository available via GitHub, you are welcome to download a tar or zip archive and use your favorite version control system to manage the code.
+
+Repository Directories
+======================
+
+This repository contains several directories, and each directory contains a README file that describes what it is for in greater detail, and how to use it for managing your systems with Chef.
+
+* `cookbooks/` - Cookbooks you download or create.
+* `data_bags/` - Store data bags and items in .json in the repository.
+* `roles/` - Store roles in .rb or .json in the repository.
+* `environments/` - Store environments in .rb or .json in the repository.
+
+Configuration
+=============
+
+The repository contains a knife configuration file.
+
+* .chef/knife.rb
+
+The knife configuration file `.chef/knife.rb` is a repository specific configuration file for knife. If you're using Hosted Chef, you can download one for your organization from the management console. If you're using the Open Source Chef Server, you can generate a new one with `knife configure`. For more information about configuring Knife, see the Knife documentation.
+
+https://docs.chef.io/knife.html
+
+Next Steps
+==========
+
+Read the README file in each of the subdirectories for more information about what goes in those directories.
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/chefignore b/sdc-os-chef/sdc-elasticsearch/chef-solo/chefignore
new file mode 100644
index 0000000000..ba30af6cff
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/chefignore
@@ -0,0 +1,11 @@
+# Put files/directories that should be ignored in this file.
+# Lines that start with '# ' are comments.
+
+# emacs
+*~
+
+# vim
+*.sw[a-z]
+
+# subversion
+*/.svn/*
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/cookbooks/README.md b/sdc-os-chef/sdc-elasticsearch/chef-solo/cookbooks/README.md
new file mode 100644
index 0000000000..86ea46bfbb
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/cookbooks/README.md
@@ -0,0 +1,54 @@
+This directory contains the cookbooks used to configure systems in your infrastructure with Chef.
+
+Knife needs to be configured to know where the cookbooks are located with the `cookbook_path` setting. If this is not set, then several cookbook operations will fail to work properly.
+
+ cookbook_path ["./cookbooks"]
+
+This setting tells knife to look for the cookbooks directory in the present working directory. This means the knife cookbook subcommands need to be run in the `chef-repo` directory itself. To make sure that the cookbooks can be found elsewhere inside the repository, use an absolute path. This is a Ruby file, so something like the following can be used:
+
+ current_dir = File.dirname(__FILE__)
+ cookbook_path ["#{current_dir}/../cookbooks"]
+
+Which will set `current_dir` to the location of the knife.rb file itself (e.g. `~/chef-repo/.chef/knife.rb`).
+
+Configure knife to use your preferred copyright holder, email contact and license. Add the following lines to `.chef/knife.rb`.
+
+ cookbook_copyright "Example, Com."
+ cookbook_email "cookbooks@example.com"
+ cookbook_license "apachev2"
+
+Supported values for `cookbook_license` are "apachev2", "mit","gplv2","gplv3", or "none". These settings are used to prefill comments in the default recipe, and the corresponding values in the metadata.rb. You are free to change the the comments in those files.
+
+Create new cookbooks in this directory with Knife.
+
+ knife cookbook create COOKBOOK
+
+This will create all the cookbook directory components. You don't need to use them all, and can delete the ones you don't need. It also creates a README file, metadata.rb and default recipe.
+
+You can also download cookbooks directly from the Opscode Cookbook Site. There are two subcommands to help with this depending on what your preference is.
+
+The first and recommended method is to use a vendor branch if you're using Git. This is automatically handled with Knife.
+
+ knife cookbook site install COOKBOOK
+
+This will:
+
+* Download the cookbook tarball from cookbooks.opscode.com.
+* Ensure its on the git master branch.
+* Checks for an existing vendor branch, and creates if it doesn't.
+* Checks out the vendor branch (chef-vendor-COOKBOOK).
+* Removes the existing (old) version.
+* Untars the cookbook tarball it downloaded in the first step.
+* Adds the cookbook files to the git index and commits.
+* Creates a tag for the version downloaded.
+* Checks out the master branch again.
+* Merges the cookbook into master.
+* Repeats the above for all the cookbooks dependencies, downloading them from the community site
+
+The last step will ensure that any local changes or modifications you have made to the cookbook are preserved, so you can keep your changes through upstream updates.
+
+If you're not using Git, use the site download subcommand to download the tarball.
+
+ knife cookbook site download COOKBOOK
+
+This creates the COOKBOOK.tar.gz from in the current directory (e.g., `~/chef-repo`). We recommend following a workflow similar to the above for your version control tool.
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/data_bags/README.md b/sdc-os-chef/sdc-elasticsearch/chef-solo/data_bags/README.md
new file mode 100644
index 0000000000..0c15a391fa
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/data_bags/README.md
@@ -0,0 +1,63 @@
+Data Bags
+---------
+
+This directory contains directories of the various data bags you create for your infrastructure. Each subdirectory corresponds to a data bag on the Chef Server, and contains JSON files of the items that go in the bag.
+
+First, create a directory for the data bag.
+
+ mkdir data_bags/BAG
+
+Then create the JSON files for items that will go into that bag.
+
+ $EDITOR data_bags/BAG/ITEM.json
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM". For example,
+
+ {
+ "id": "foo"
+ }
+
+Next, create the data bag on the Chef Server.
+
+ knife data bag create BAG
+
+Then upload the items in the data bag's directory to the Chef Server.
+
+ knife data bag from file BAG ITEM.json
+
+
+Encrypted Data Bags
+-------------------
+
+Added in Chef 0.10, encrypted data bags allow you to encrypt the contents of your data bags. The content of attributes will no longer be searchable. To use encrypted data bags, first you must have or create a secret key.
+
+ openssl rand -base64 512 > secret_key
+
+You may use this secret_key to add items to a data bag during a create.
+
+ knife data bag create --secret-file secret_key passwords mysql
+
+You may also use it when adding ITEMs from files,
+
+ knife data bag create passwords
+ knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM" and the contents will be encrypted when uploaded. For example,
+
+ {
+ "id": "mysql",
+ "password": "abc123"
+ }
+
+Without the secret_key, the contents are encrypted.
+
+ knife data bag show passwords mysql
+ id: mysql
+ password: 2I0XUUve1TXEojEyeGsjhw==
+
+Use the secret_key to view the contents.
+
+ knife data bag show passwords mysql --secret-file secret_key
+ id: mysql
+ password: abc123
+
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/environments/README.md b/sdc-os-chef/sdc-elasticsearch/chef-solo/environments/README.md
new file mode 100644
index 0000000000..50ac48db2b
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/environments/README.md
@@ -0,0 +1,5 @@
+Requires Chef 0.10.0+.
+
+This directory is for Ruby DSL and JSON files for environments. For more information see the Chef wiki page:
+
+http://docs.chef.io/environments.html
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/roles/README.md b/sdc-os-chef/sdc-elasticsearch/chef-solo/roles/README.md
new file mode 100644
index 0000000000..b0ee0b4d21
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/roles/README.md
@@ -0,0 +1,16 @@
+Create roles here, in either the Role Ruby DSL (.rb) or JSON (.json) files. To install roles on the server, use knife.
+
+For example, create `roles/base_example.rb`:
+
+ name "base_example"
+ description "Example base role applied to all nodes."
+ # List of recipes and roles to apply. Requires Chef 0.8, earlier versions use 'recipes()'.
+ #run_list()
+ # Attributes applied if the node doesn't have it set already.
+ #default_attributes()
+ # Attributes applied no matter what the node has set already.
+ #override_attributes()
+
+Then upload it to the Chef Server:
+
+ knife role from file roles/base_example.rb
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/roles/elasticsearch.json b/sdc-os-chef/sdc-elasticsearch/chef-solo/roles/elasticsearch.json
new file mode 100644
index 0000000000..239c1cd04b
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/roles/elasticsearch.json
@@ -0,0 +1,19 @@
+{
+ "name": "elasticsearch",
+ "description": "Installation application - elasticsearch",
+ "json_class": "Chef::Role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "chef_type": "role",
+ "run_list": [
+ "recipe[sdc-elasticsearch::ES_1_setup_elasticsearch]",
+ "recipe[sdc-elasticsearch::ES_2_setup_logging]"
+ ],
+ "env_run_lists": {
+ }
+}
+
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/solo.json b/sdc-os-chef/sdc-elasticsearch/chef-solo/solo.json
new file mode 100644
index 0000000000..2c6b44f93f
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/solo.json
@@ -0,0 +1,4 @@
+{
+ "run_list": [ "role[elasticsearch]" ]
+}
+
diff --git a/sdc-os-chef/sdc-elasticsearch/chef-solo/solo.rb b/sdc-os-chef/sdc-elasticsearch/chef-solo/solo.rb
new file mode 100644
index 0000000000..06c1af4592
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/chef-solo/solo.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/solo.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-elasticsearch/startup.sh b/sdc-os-chef/sdc-elasticsearch/startup.sh
new file mode 100644
index 0000000000..b9e5fd6edf
--- /dev/null
+++ b/sdc-os-chef/sdc-elasticsearch/startup.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+export CHEFNAME=${ENVNAME}
+cd /root/chef-solo
+echo "normal['HOST_IP'] = \"${HOST_IP}\"" > /root/chef-solo/cookbooks/sdc-elasticsearch/attributes/default.rb
+
+sed -i '/^exec/iES_JAVA_OPTS=\"-Xms1024M -Xmx1024M\"' /docker-entrypoint.sh
+
+chef-solo -c solo.rb -E ${CHEFNAME}
+
+/docker-entrypoint.sh elasticsearch &
+
+cd /root/chef-solo
+chef-solo -c solo.rb -o recipe[sdc-elasticsearch::ES_3_create_audit_template]
+chef-solo -c solo.rb -o recipe[sdc-elasticsearch::ES_4_create_resources_template]
+chef-solo -c solo.rb -o recipe[sdc-elasticsearch::ES_5_create_monitoring_template]
+chef-solo -c solo.rb -o recipe[sdc-elasticsearch::ES_6_create_kibana_dashboard_virtualization]
+
+while true; do sleep 2; done
diff --git a/sdc-os-chef/sdc-frontend/Dockerfile.template b/sdc-os-chef/sdc-frontend/Dockerfile.template
new file mode 100644
index 0000000000..66ce1467b0
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/Dockerfile.template
@@ -0,0 +1,26 @@
+FROM jetty
+
+RUN apt-get -y update
+RUN apt-get -y install apt-utils
+RUN apt-get -y install curl
+RUN apt-get -y install vim
+
+COPY chef-solo /root/chef-solo/
+COPY chef-repo/cookbooks /root/chef-solo/cookbooks/
+
+ENV BASE_NEXUS zl999y:ChangeMe@10.208.197.75:8443/repository/maven-public/org/openecomp/sdc
+
+# install chef-solo
+RUN curl -L https://www.opscode.com/chef/install.sh | bash
+
+RUN cp /usr/local/jetty/resources/log4j.properties /var/lib/jetty/resources/log4j.properties
+
+ADD onboarding-fe-__SDC-RELEASE__.war /var/lib/jetty/webapps/
+ADD catalog-fe-__SDC-RELEASE__.war /var/lib/jetty/webapps/
+RUN chown -R jetty:jetty /var/lib/jetty/webapps
+
+COPY startup.sh /root/
+
+RUN chmod 770 /root/startup.sh
+
+ENTRYPOINT [ "/root/startup.sh" ]
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/attributes/default.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/attributes/default.rb
new file mode 100644
index 0000000000..6e9bbe36f5
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/attributes/default.rb
@@ -0,0 +1,2 @@
+#
+#
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-ecomp-error-configuration.yaml b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-ecomp-error-configuration.yaml
new file mode 100644
index 0000000000..8982b2424f
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-ecomp-error-configuration.yaml
@@ -0,0 +1,48 @@
+###########################################
+# Note the conventions of the field values:
+# type can be one of: CONFIG_ERROR, SYSTEM_ERROR, DATA_ERROR, CONNECTION_PROBLEM
+# severity can be one of: WARN, ERROR, FATAL
+# alarmSeverity can be one of: CRITICAL,MAJOR,MINOR,INFORMATIONAL,NONE
+# code is a unique integer in range of 3003-9999 (3000-3002 are occupied for internal usage)
+# The above enumeration values are out-of-the-box and can be changed in code.
+# In case of config and code mismatch, the appropriate error will be printed to log
+#
+# Range of FE codes - 8000-9999
+
+
+errors:
+ FeHealthCheckConnectionError: {
+ type: CONNECTION_PROBLEM,
+ code: ASDC_8000,
+ severity: ERROR,
+ description: "Connection error during FE Health Check",
+ alarmSeverity: CRITICAL
+ }
+ FeHttpLoggingError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_8001,
+ severity: ERROR,
+ description: "Error when logging FE HTTP request/response",
+ alarmSeverity: MINOR
+ }
+ FePortalServletError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_8002,
+ severity: ERROR,
+ description: "Error when trying to access FE Portal page",
+ alarmSeverity: MAJOR
+ }
+ FeHealthCheckGeneralError: {
+ type: SYSTEM_ERROR,
+ code: ASDC_8004,
+ severity: ERROR,
+ description: "General error during FE Health Check",
+ alarmSeverity: CRITICAL
+ }
+ FeHealthCheckRecovery: {
+ type: RECOVERY,
+ code: ASDC_8005,
+ severity: INFO,
+ description: "BE Health Check Recovery",
+ alarmSeverity: INFORMATIONAL
+ } \ No newline at end of file
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-logback.xml b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-logback.xml
new file mode 100644
index 0000000000..2f793f386b
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-logback.xml
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration scan="true" scanPeriod="5 seconds">
+
+ <property scope="system" name="ECOMP-component-name" value="ASDC" />
+ <property scope="system" name="ECOMP-subcomponent-name" value="ASDC-FE" />
+ <property file="${config.home}/catalog-fe/configuration.yaml" />
+ <property scope="context" name="enable-all-log" value="false" />
+
+ <!-- value used by pattern field list (| - is inter-field separator, || - unavailable or not applicable field value) (m - mandatory, o- optional)-->
+ <!--timestamp(m)| requestID(m)| serviceInstanceID(o)| threadID(m)| physicalServerName(o)| serviceName(m)| userID(m)| logLevel(m)| severity(o)| serverIpAddress(m)| serverName(m)| clientIpAddress(o)| className(m)| timer(o)| detailedMessage(o)-->
+ <property name="default-log-pattern"
+ value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%X{uuid}|%X{serviceInstanceID}|%thread||${ECOMP-subcomponent-name}|%X{userId}|%level|%X{alarmSeverity}|%X{localAddr}|${feFqdn}|%X{remoteAddr}|%logger{35}|%X{timer}|ActivityType=&lt;%M&gt;, Desc=&lt;%msg&gt;%n" />
+
+ <!-- All log -->
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender name="ALL_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log
+ </file>
+
+ <rollingPolicy
+ class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/all.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="ASYNC_ALL" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ALL_ROLLING" />
+ </appender>
+ </then>
+ </if>
+
+ <!-- Error log -->
+ <appender name="ERROR_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log
+ </file>
+
+ <!-- Audit messages filter - deny audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>AUDIT_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- Transaction messages filter - deny Transaction messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
+ <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+ <level>INFO</level>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/error.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Debug log -->
+ <appender name="DEBUG_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log
+ </file>
+
+ <!-- No need to deny audit messages - they are INFO only, will be denied
+ anyway -->
+ <!-- Transaction messages filter - deny Transaction messages, there are
+ some DEBUG level messages among them -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>NEUTRAL</onMismatch>
+ <onMatch>DENY</onMatch>
+ </filter>
+
+ <!-- accept DEBUG and TRACE level -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
+ <expression>
+ e.level.toInt() &lt;= DEBUG.toInt()
+ </expression>
+ </evaluator>
+ <OnMismatch>DENY</OnMismatch>
+ <OnMatch>NEUTRAL</OnMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/debug.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Audit log -->
+ <appender name="AUDIT_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/audit.log
+ </file>
+
+ <!-- Audit messages filter - accept audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>AUDIT_MARKER</marker>
+ </evaluator>
+ <onMismatch>DENY</onMismatch>
+ <onMatch>ACCEPT</onMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/audit.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- SdncTransaction log -->
+ <appender name="TRANSACTION_ROLLING"
+ class="ch.qos.logback.core.rolling.RollingFileAppender">
+
+ <file>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/transaction.log
+ </file>
+
+ <!-- Transaction messages filter - accept audit messages -->
+ <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
+ <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
+ <marker>TRANSACTION_MARKER</marker>
+ </evaluator>
+ <onMismatch>DENY</onMismatch>
+ <onMatch>ACCEPT</onMatch>
+ </filter>
+
+ <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.home}/${ECOMP-component-name}/${ECOMP-subcomponent-name}/transaction.log.%i
+ </fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+
+ <triggeringPolicy
+ class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>20MB</maxFileSize>
+ </triggeringPolicy>
+ <encoder>
+ <pattern>${default-log-pattern}</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Asynchronicity Configurations -->
+ <appender name="ASYNC_DEBUG" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="DEBUG_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_TRANSACTION" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="TRANSACTION_ROLLING" />
+ </appender>
+
+ <appender name="ASYNC_ERROR" class="ch.qos.logback.classic.AsyncAppender">
+ <appender-ref ref="ERROR_ROLLING" />
+ </appender>
+
+
+ <root level="INFO">
+ <appender-ref ref="ASYNC_ERROR" />
+ <appender-ref ref="ASYNC_DEBUG" />
+ <appender-ref ref="AUDIT_ROLLING" />
+ <appender-ref ref="ASYNC_TRANSACTION" />
+ <if condition='property("enable-all-log").equalsIgnoreCase("true")'>
+ <then>
+ <appender-ref ref="ALL_ROLLING" />
+ </then>
+ </if>
+ </root>
+
+ <logger name="org.openecomp.sdc" level="INFO" />
+</configuration>
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-rest-configuration.yaml b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-rest-configuration.yaml
new file mode 100644
index 0000000000..ecedafea56
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/files/default/FE-rest-configuration.yaml
@@ -0,0 +1,11 @@
+# rest read timeout - means no timeout
+readTimeoutInSec: 0
+
+# whether to ignore certificate
+ignoreCertificate: false
+
+# the connection pool size
+connectionPoolSize: 10
+
+# create connection timeout
+connectTimeoutInSec: 10
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_1_cleanup_jettydir.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_1_cleanup_jettydir.rb
new file mode 100644
index 0000000000..29301a9418
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_1_cleanup_jettydir.rb
@@ -0,0 +1,49 @@
+#directory "FE_tempdir_cleanup" do
+# path "/var/lib/jetty/tempdir"
+# recursive true
+# action :delete
+#end
+
+
+directory "FE_tempdir_creation" do
+ path "/var/lib/jetty/temp"
+ owner 'jetty'
+ group 'jetty'
+ mode '0755'
+ action :create
+end
+
+
+#directory "FE_webapps_cleanup" do
+# path "/var/lib/jetty/webapps"
+# recursive true
+# action :delete
+#end
+
+
+#directory "FE_webapps_creation" do
+# path "/var/lib/jetty/webapps"
+# owner 'jetty'
+# group 'jetty'
+# mode '0755'
+# action :create
+#end
+
+
+directory "FE_create_config_dir" do
+ path "/var/lib/jetty/config"
+ owner 'jetty'
+ group 'jetty'
+ mode '0755'
+ action :create
+end
+
+
+directory "FE_create_catalog-fe" do
+ path "/var/lib/jetty/config/catalog-fe"
+ owner 'jetty'
+ group 'jetty'
+ mode '0755'
+ action :create
+end
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_2_setup_configuration.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_2_setup_configuration.rb
new file mode 100644
index 0000000000..64d71768cd
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_2_setup_configuration.rb
@@ -0,0 +1,13 @@
+template "catalog-fe-config" do
+ path "/var/lib/jetty/config/catalog-fe/configuration.yaml"
+ source "FE-configuration.yaml.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables({
+ :fe_host_ip => node['HOST_IP'],
+ :be_host_ip => node['HOST_IP'],
+ :catalog_port => node['BE'][:http_port],
+ :ssl_port => node['BE'][:https_port]
+ })
+end
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_3_errors_config.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_3_errors_config.rb
new file mode 100644
index 0000000000..278c3bb35d
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_3_errors_config.rb
@@ -0,0 +1,7 @@
+cookbook_file "/var/lib/jetty/config/catalog-fe/ecomp-error-configuration.yaml" do
+ source "FE-ecomp-error-configuration.yaml"
+ mode 0755
+ owner "jetty"
+ group "jetty"
+end
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_4_logback.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_4_logback.rb
new file mode 100644
index 0000000000..674febb47a
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_4_logback.rb
@@ -0,0 +1,7 @@
+cookbook_file "/var/lib/jetty/config/catalog-fe/logback.xml" do
+ source "FE-logback.xml"
+ mode 0755
+ owner "jetty"
+ group "jetty"
+end
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_5_rest_configuration.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_5_rest_configuration.rb
new file mode 100644
index 0000000000..710286a8dd
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_5_rest_configuration.rb
@@ -0,0 +1,7 @@
+cookbook_file "/var/lib/jetty/config/catalog-fe/rest-configuration-info.yaml" do
+ source "FE-rest-configuration.yaml"
+ mode 0755
+ owner "jetty"
+ group "jetty"
+end
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_create_jetty_modules.rb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_create_jetty_modules.rb
new file mode 100644
index 0000000000..2800fd1808
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/recipes/FE_6_create_jetty_modules.rb
@@ -0,0 +1,45 @@
+jetty_base="/var/lib/jetty"
+jetty_home="/usr/local/jetty"
+
+###### create Jetty modules
+bash "create-jetty-modules" do
+cwd "#{jetty_base}"
+code <<-EOH
+ cd "#{jetty_base}"
+ java -jar "/#{jetty_home}"/start.jar --add-to-start=deploy
+ java -jar "/#{jetty_home}"/start.jar --add-to-startd=http,https,logging,setuid
+EOH
+not_if "ls /#{jetty_base}/start.d/https.ini"
+end
+
+
+###### configure Jetty modules
+template "FE-http-ini" do
+ path "/#{jetty_base}/start.d/http.ini"
+ source "FE-http-ini.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables :FE_http_port => "#{node['FE'][:http_port]}"
+end
+
+
+template "FE-https-ini" do
+ path "/#{jetty_base}/start.d/https.ini"
+ source "FE-https-ini.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables :FE_https_port => "#{node['FE'][:https_port]}"
+end
+
+
+template "FE-ssl-ini" do
+ path "/#{jetty_base}/start.d/ssl.ini"
+ source "FE-ssl-ini.erb"
+ owner "jetty"
+ group "jetty"
+ mode "0755"
+ variables :FE_https_port => "#{node['FE'][:https_port]}"
+end
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-configuration.yaml.erb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-configuration.yaml.erb
new file mode 100644
index 0000000000..f864bb9dbb
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-configuration.yaml.erb
@@ -0,0 +1,79 @@
+# Needed for logging purposes. To be populated by DevOps - currently dummy
+feFqdn: <%= @fe_host_ip %>
+
+# catalog backend hostname
+beHost: <%= @be_host_ip %>
+
+# catalog backend http port
+beHttpPort: <%= @catalog_port %>
+
+# catalog backend http context
+beContext: /sdc2/rest/v1/catalog/upload/resources
+
+# catalog backend protocol
+beProtocol: http
+
+# catalog backend ssl port
+beSslPort: <%= @ssl_port %>
+
+# threadpool size for handling requests
+threadpoolSize: 50
+
+# request processing timeout (seconds)
+requestTimeout: 10
+
+healthCheckSocketTimeoutInMs: 5000
+
+healthCheckIntervalInSeconds: 5
+
+identificationHeaderFields:
+ -
+ - &HTTP_IV_USER HTTP_IV_USER
+ - &iv-user iv-user
+ -
+ - &USER_ID USER_ID
+ - &user-id user-id
+ -
+ - &HTTP_CSP_ATTUID HTTP_CSP_ATTUID
+ - &csp-attuid csp-attuid
+ -
+ - &HTTP_CSP_WSTYPE HTTP_CSP_WSTYPE
+ - &csp-wstype csp-wstype
+
+optionalHeaderFields:
+ -
+ - &HTTP_CSP_FIRSTNAME HTTP_CSP_FIRSTNAME
+ - &csp-firstname csp-firstname
+ -
+ - &HTTP_CSP_LASTNAME HTTP_CSP_LASTNAME
+ - &csp-lastname csp-lastname
+ -
+ - &HTTP_IV_REMOTE_ADDRESS HTTP_IV_REMOTE_ADDRESS
+ - &iv-remote-address iv-remote-address
+ -
+ - &HTTP_CSP_EMAIL HTTP_CSP_EMAIL
+ - &csp-email csp-email
+
+
+version: 1.0
+released: 2012-11-30
+
+# Connection parameters
+connection:
+ url: jdbc:mysql://localhost:3306/db
+ poolSize: 17
+
+# Protocols
+protocols:
+ - http
+ - https
+
+
+systemMonitoring:
+ enabled: true
+ isProxy: true
+ probeIntervalInSeconds: 15
+
+kibanaHost: localhost
+kibanaPort: 5601
+kibanaProtocol: http
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-http-ini.erb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-http-ini.erb
new file mode 100644
index 0000000000..34a73fdf55
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-http-ini.erb
@@ -0,0 +1,32 @@
+# ---------------------------------------
+# Module: http
+--module=http
+
+### HTTP Connector Configuration
+
+## Connector host/address to bind to
+# jetty.http.host=0.0.0.0
+
+## Connector port to listen on
+jetty.http.port=<%= @FE_http_port %>
+
+## Connector idle timeout in milliseconds
+jetty.http.idleTimeout=30000
+
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.http.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.http.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.http.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.http.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.http.acceptorPriorityDelta=0
+
+## HTTP Compliance: RFC7230, RFC2616, LEGACY
+# jetty.http.compliance=RFC7230
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-https-ini.erb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-https-ini.erb
new file mode 100644
index 0000000000..f8ca5252b0
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-https-ini.erb
@@ -0,0 +1,15 @@
+# ---------------------------------------
+# Module: https
+--module=https
+
+## HTTPS Configuration
+# HTTP port to listen on
+https.port=<%= @FE_https_port %>
+
+# HTTPS idle timeout in milliseconds
+jetty.https.idleTimeout=300000
+
+# HTTPS Socket.soLingerTime in seconds. (-1 to disable)
+# https.soLingerTime=-1
+
+
diff --git a/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-ssl-ini.erb b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-ssl-ini.erb
new file mode 100644
index 0000000000..426e0e44b5
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-repo/cookbooks/sdc-catalog-fe/templates/default/FE-ssl-ini.erb
@@ -0,0 +1,83 @@
+# ---------------------------------------
+# Module: ssl
+--module=ssl
+
+### TLS(SSL) Connector Configuration
+
+## Connector host/address to bind to
+# jetty.ssl.host=0.0.0.0
+
+## Connector port to listen on
+jetty.ssl.port=<%= @FE_https_port %>
+
+## Connector idle timeout in milliseconds
+# jetty.ssl.idleTimeout=30000
+
+## Connector socket linger time in seconds (-1 to disable)
+# jetty.ssl.soLingerTime=-1
+
+## Number of acceptors (-1 picks default based on number of cores)
+# jetty.ssl.acceptors=-1
+
+## Number of selectors (-1 picks default based on number of cores)
+# jetty.ssl.selectors=-1
+
+## ServerSocketChannel backlog (0 picks platform default)
+# jetty.ssl.acceptorQueueSize=0
+
+## Thread priority delta to give to acceptor threads
+# jetty.ssl.acceptorPriorityDelta=0
+
+## Whether request host names are checked to match any SNI names
+# jetty.ssl.sniHostCheck=true
+
+## max age in seconds for a Strict-Transport-Security response header (default -1)
+# jetty.ssl.stsMaxAgeSeconds=31536000
+
+## include subdomain property in any Strict-Transport-Security header (default false)
+# jetty.ssl.stsIncludeSubdomains=true
+
+### SslContextFactory Configuration
+## Note that OBF passwords are not secure, just protected from casual observation
+## See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html
+
+## Keystore file path (relative to $jetty.base)
+# jetty.sslContext.keyStorePath=etc/keystore
+
+## Truststore file path (relative to $jetty.base)
+# jetty.sslContext.trustStorePath=etc/keystore
+
+## Keystore password
+# jetty.sslContext.keyStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+
+## Keystore type and provider
+# jetty.sslContext.keyStoreType=JKS
+# jetty.sslContext.keyStoreProvider=
+
+## KeyManager password
+# jetty.sslContext.keyManagerPassword=OBF:1u2u1wml1z7s1z7a1wnl1u2g
+
+## Truststore password
+# jetty.sslContext.trustStorePassword=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4
+
+## Truststore type and provider
+# jetty.sslContext.trustStoreType=JKS
+# jetty.sslContext.trustStoreProvider=
+
+## whether client certificate authentication is required
+# jetty.sslContext.needClientAuth=false
+
+## Whether client certificate authentication is desired
+# jetty.sslContext.wantClientAuth=false
+
+## Whether cipher order is significant (since java 8 only)
+# jetty.sslContext.useCipherSuitesOrder=true
+
+## To configure Includes / Excludes for Cipher Suites or Protocols see tweak-ssl.xml example at
+## https://www.eclipse.org/jetty/documentation/current/configuring-ssl.html#configuring-sslcontextfactory-cipherSuites
+
+## Set the size of the SslSession cache
+# jetty.sslContext.sslSessionCacheSize=-1
+
+## Set the timeout (in seconds) of the SslSession cache timeout
+# jetty.sslContext.sslSessionTimeout=-1
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/LICENSE b/sdc-os-chef/sdc-frontend/chef-solo/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/README.md b/sdc-os-chef/sdc-frontend/chef-solo/README.md
new file mode 100644
index 0000000000..ddb0fda830
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/README.md
@@ -0,0 +1,37 @@
+Deprecated
+==========
+
+Use of this repository is deprecated. We recommend using the `chef generate repo` command that comes with [ChefDK](http://downloads.chef.io/chef-dk/).
+
+Overview
+========
+
+Every Chef installation needs a Chef Repository. This is the place where cookbooks, roles, config files and other artifacts for managing systems with Chef will live. We strongly recommend storing this repository in a version control system such as Git and treat it like source code.
+
+While we prefer Git, and make this repository available via GitHub, you are welcome to download a tar or zip archive and use your favorite version control system to manage the code.
+
+Repository Directories
+======================
+
+This repository contains several directories, and each directory contains a README file that describes what it is for in greater detail, and how to use it for managing your systems with Chef.
+
+* `cookbooks/` - Cookbooks you download or create.
+* `data_bags/` - Store data bags and items in .json in the repository.
+* `roles/` - Store roles in .rb or .json in the repository.
+* `environments/` - Store environments in .rb or .json in the repository.
+
+Configuration
+=============
+
+The repository contains a knife configuration file.
+
+* .chef/knife.rb
+
+The knife configuration file `.chef/knife.rb` is a repository specific configuration file for knife. If you're using Hosted Chef, you can download one for your organization from the management console. If you're using the Open Source Chef Server, you can generate a new one with `knife configure`. For more information about configuring Knife, see the Knife documentation.
+
+https://docs.chef.io/knife.html
+
+Next Steps
+==========
+
+Read the README file in each of the subdirectories for more information about what goes in those directories.
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/chefignore b/sdc-os-chef/sdc-frontend/chef-solo/chefignore
new file mode 100644
index 0000000000..ba30af6cff
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/chefignore
@@ -0,0 +1,11 @@
+# Put files/directories that should be ignored in this file.
+# Lines that start with '# ' are comments.
+
+# emacs
+*~
+
+# vim
+*.sw[a-z]
+
+# subversion
+*/.svn/*
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/cookbooks/README.md b/sdc-os-chef/sdc-frontend/chef-solo/cookbooks/README.md
new file mode 100644
index 0000000000..86ea46bfbb
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/cookbooks/README.md
@@ -0,0 +1,54 @@
+This directory contains the cookbooks used to configure systems in your infrastructure with Chef.
+
+Knife needs to be configured to know where the cookbooks are located with the `cookbook_path` setting. If this is not set, then several cookbook operations will fail to work properly.
+
+ cookbook_path ["./cookbooks"]
+
+This setting tells knife to look for the cookbooks directory in the present working directory. This means the knife cookbook subcommands need to be run in the `chef-repo` directory itself. To make sure that the cookbooks can be found elsewhere inside the repository, use an absolute path. This is a Ruby file, so something like the following can be used:
+
+ current_dir = File.dirname(__FILE__)
+ cookbook_path ["#{current_dir}/../cookbooks"]
+
+Which will set `current_dir` to the location of the knife.rb file itself (e.g. `~/chef-repo/.chef/knife.rb`).
+
+Configure knife to use your preferred copyright holder, email contact and license. Add the following lines to `.chef/knife.rb`.
+
+ cookbook_copyright "Example, Com."
+ cookbook_email "cookbooks@example.com"
+ cookbook_license "apachev2"
+
+Supported values for `cookbook_license` are "apachev2", "mit","gplv2","gplv3", or "none". These settings are used to prefill comments in the default recipe, and the corresponding values in the metadata.rb. You are free to change the the comments in those files.
+
+Create new cookbooks in this directory with Knife.
+
+ knife cookbook create COOKBOOK
+
+This will create all the cookbook directory components. You don't need to use them all, and can delete the ones you don't need. It also creates a README file, metadata.rb and default recipe.
+
+You can also download cookbooks directly from the Opscode Cookbook Site. There are two subcommands to help with this depending on what your preference is.
+
+The first and recommended method is to use a vendor branch if you're using Git. This is automatically handled with Knife.
+
+ knife cookbook site install COOKBOOK
+
+This will:
+
+* Download the cookbook tarball from cookbooks.opscode.com.
+* Ensure its on the git master branch.
+* Checks for an existing vendor branch, and creates if it doesn't.
+* Checks out the vendor branch (chef-vendor-COOKBOOK).
+* Removes the existing (old) version.
+* Untars the cookbook tarball it downloaded in the first step.
+* Adds the cookbook files to the git index and commits.
+* Creates a tag for the version downloaded.
+* Checks out the master branch again.
+* Merges the cookbook into master.
+* Repeats the above for all the cookbooks dependencies, downloading them from the community site
+
+The last step will ensure that any local changes or modifications you have made to the cookbook are preserved, so you can keep your changes through upstream updates.
+
+If you're not using Git, use the site download subcommand to download the tarball.
+
+ knife cookbook site download COOKBOOK
+
+This creates the COOKBOOK.tar.gz from in the current directory (e.g., `~/chef-repo`). We recommend following a workflow similar to the above for your version control tool.
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/data_bags/README.md b/sdc-os-chef/sdc-frontend/chef-solo/data_bags/README.md
new file mode 100644
index 0000000000..0c15a391fa
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/data_bags/README.md
@@ -0,0 +1,63 @@
+Data Bags
+---------
+
+This directory contains directories of the various data bags you create for your infrastructure. Each subdirectory corresponds to a data bag on the Chef Server, and contains JSON files of the items that go in the bag.
+
+First, create a directory for the data bag.
+
+ mkdir data_bags/BAG
+
+Then create the JSON files for items that will go into that bag.
+
+ $EDITOR data_bags/BAG/ITEM.json
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM". For example,
+
+ {
+ "id": "foo"
+ }
+
+Next, create the data bag on the Chef Server.
+
+ knife data bag create BAG
+
+Then upload the items in the data bag's directory to the Chef Server.
+
+ knife data bag from file BAG ITEM.json
+
+
+Encrypted Data Bags
+-------------------
+
+Added in Chef 0.10, encrypted data bags allow you to encrypt the contents of your data bags. The content of attributes will no longer be searchable. To use encrypted data bags, first you must have or create a secret key.
+
+ openssl rand -base64 512 > secret_key
+
+You may use this secret_key to add items to a data bag during a create.
+
+ knife data bag create --secret-file secret_key passwords mysql
+
+You may also use it when adding ITEMs from files,
+
+ knife data bag create passwords
+ knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM" and the contents will be encrypted when uploaded. For example,
+
+ {
+ "id": "mysql",
+ "password": "abc123"
+ }
+
+Without the secret_key, the contents are encrypted.
+
+ knife data bag show passwords mysql
+ id: mysql
+ password: 2I0XUUve1TXEojEyeGsjhw==
+
+Use the secret_key to view the contents.
+
+ knife data bag show passwords mysql --secret-file secret_key
+ id: mysql
+ password: abc123
+
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/environments/README.md b/sdc-os-chef/sdc-frontend/chef-solo/environments/README.md
new file mode 100644
index 0000000000..50ac48db2b
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/environments/README.md
@@ -0,0 +1,5 @@
+Requires Chef 0.10.0+.
+
+This directory is for Ruby DSL and JSON files for environments. For more information see the Chef wiki page:
+
+http://docs.chef.io/environments.html
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/roles/README.md b/sdc-os-chef/sdc-frontend/chef-solo/roles/README.md
new file mode 100644
index 0000000000..b0ee0b4d21
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/roles/README.md
@@ -0,0 +1,16 @@
+Create roles here, in either the Role Ruby DSL (.rb) or JSON (.json) files. To install roles on the server, use knife.
+
+For example, create `roles/base_example.rb`:
+
+ name "base_example"
+ description "Example base role applied to all nodes."
+ # List of recipes and roles to apply. Requires Chef 0.8, earlier versions use 'recipes()'.
+ #run_list()
+ # Attributes applied if the node doesn't have it set already.
+ #default_attributes()
+ # Attributes applied no matter what the node has set already.
+ #override_attributes()
+
+Then upload it to the Chef Server:
+
+ knife role from file roles/base_example.rb
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/roles/catalog-fe.json b/sdc-os-chef/sdc-frontend/chef-solo/roles/catalog-fe.json
new file mode 100644
index 0000000000..9e023762d1
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/roles/catalog-fe.json
@@ -0,0 +1,23 @@
+{
+ "name": "catalog-be",
+ "description": "Installation application - catalogFE",
+ "json_class": "Chef::Role",
+ "default_attributes": {
+
+ },
+ "override_attributes": {
+
+ },
+ "chef_type": "role",
+ "run_list": [
+ "recipe[sdc-catalog-fe::FE_1_cleanup_jettydir]",
+ "recipe[sdc-catalog-fe::FE_2_setup_configuration]",
+ "recipe[sdc-catalog-fe::FE_3_errors_config]",
+ "recipe[sdc-catalog-fe::FE_4_logback]",
+ "recipe[sdc-catalog-fe::FE_5_rest_configuration]",
+ "recipe[sdc-catalog-fe::FE_6_create_jetty_modules]"
+ ],
+ "env_run_lists": {
+ }
+}
+
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/solo.json b/sdc-os-chef/sdc-frontend/chef-solo/solo.json
new file mode 100644
index 0000000000..09dd642e6f
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/solo.json
@@ -0,0 +1,4 @@
+{
+ "run_list": [ "role[catalog-fe]" ]
+}
+
diff --git a/sdc-os-chef/sdc-frontend/chef-solo/solo.rb b/sdc-os-chef/sdc-frontend/chef-solo/solo.rb
new file mode 100644
index 0000000000..06c1af4592
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/chef-solo/solo.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/solo.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-frontend/startup.sh b/sdc-os-chef/sdc-frontend/startup.sh
new file mode 100644
index 0000000000..2a3d4d47f5
--- /dev/null
+++ b/sdc-os-chef/sdc-frontend/startup.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+export CHEFNAME=${ENVNAME}
+cd /root/chef-solo
+echo "normal['HOST_IP'] = \"${HOST_IP}\"" > /root/chef-solo/cookbooks/sdc-catalog-fe/attributes/default.rb
+chef-solo -c solo.rb -E ${CHEFNAME}
+
+sed -i '/^set -e/aJAVA_OPTIONS=\"-XX:MaxPermSize=256m -Xmx1500m -Dconfig.home=${JETTY_BASE}\/config -Dlog.home=${JETTY_BASE}\/logs -Dlogback.configurationFile=${JETTY_BASE}\/config\/catalog-fe\/logback.xml -Dconfiguration.yaml=${JETTY_BASE}\/config\/catalog-fe\/configuration.yaml\"' /docker-entrypoint.sh
+sed -i '/^set -e/aTMPDIR=${JETTY_BASE}\/temp' /docker-entrypoint.sh
+
+cd /var/lib/jetty
+/docker-entrypoint.sh
diff --git a/sdc-os-chef/sdc-kibana/Dockerfile b/sdc-os-chef/sdc-kibana/Dockerfile
new file mode 100644
index 0000000000..59e659660b
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/Dockerfile
@@ -0,0 +1,18 @@
+FROM kibana:4.3.3
+
+RUN apt-get -y update
+RUN apt-get -y install curl
+RUN apt-get -y install vim
+
+COPY chef-solo /root/chef-solo/
+COPY chef-repo/cookbooks/. /root/chef-solo/cookbooks/
+
+
+# install chef-solo
+RUN curl -L https://www.opscode.com/chef/install.sh | bash
+
+COPY startup.sh /root/
+
+RUN chmod 770 /root/startup.sh
+
+ENTRYPOINT [ "/root/startup.sh" ]
diff --git a/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/attributes/default.rb b/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/attributes/default.rb
new file mode 100644
index 0000000000..4287ca8617
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/attributes/default.rb
@@ -0,0 +1 @@
+# \ No newline at end of file
diff --git a/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/recipes/setup_kibana.rb b/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/recipes/setup_kibana.rb
new file mode 100644
index 0000000000..ee5d5c74e6
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/recipes/setup_kibana.rb
@@ -0,0 +1,19 @@
+directory "/opt/kibana/config" do
+ owner "kibana"
+ group "kibana"
+ mode '0775'
+ action :create
+end
+
+template "kibana-yml" do
+ path "/opt/kibana/config/kibana.yml"
+ source "kibana.yml.erb"
+ owner "kibana"
+ group "kibana"
+ mode "0755"
+ variables ({
+ :catalog_host => node['BE_VIP'] ,
+ :catalog_port => node['BE'][:http_port]
+ })
+end
+
diff --git a/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/templates/default/kibana.yml.erb b/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/templates/default/kibana.yml.erb
new file mode 100644
index 0000000000..7179a96512
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-repo/cookbooks/sdc-kibana/templates/default/kibana.yml.erb
@@ -0,0 +1,74 @@
+# Kibana is served by a back end server. This controls which port to use.
+# server.port: 5601
+
+# The host to bind the server to.
+# server.host: "0.0.0.0"
+
+# If you are running kibana behind a proxy, and want to mount it at a path,
+# specify that path here. The basePath can't end in a slash.
+server.basePath: "/sdc1/kibanaProxy"
+
+# The Elasticsearch instance to use for all your queries.
+# elasticsearch.url: "http://localhost:9200"
+elasticsearch.url: "http://<%= @catalog_host %>:<%= @catalog_port %>/sdc2/esGateway"
+
+# preserve_elasticsearch_host true will send the hostname specified in `elasticsearch`. If you set it to false,
+# then the host you use to connect to *this* Kibana instance will be sent.
+# elasticsearch.preserveHost: true
+
+# Kibana uses an index in Elasticsearch to store saved searches, visualizations
+# and dashboards. It will create a new index if it doesn't already exist.
+# kibana.index: ".kibana"
+
+# The default application to load.
+# kibana.defaultAppId: "discover"
+
+# If your Elasticsearch is protected with basic auth, these are the user credentials
+# used by the Kibana server to perform maintenance on the kibana_index at startup. Your Kibana
+# users will still need to authenticate with Elasticsearch (which is proxied through the Kibana server)
+# elasticsearch.username: "kibana4-server"
+# elasticsearch.password: "Aa1234%^!"
+
+# SSL for outgoing requests from the Kibana Server to the browser (PEM formatted)
+# server.ssl.cert: /path/to/your/server.crt
+# server.ssl.key: /path/to/your/server.key
+
+# Optional setting to validate that your Elasticsearch backend uses the same key files (PEM formatted)
+# elasticsearch.ssl.cert: /path/to/your/client.crt
+# elasticsearch.ssl.key: /path/to/your/client.key
+
+# If you need to provide a CA certificate for your Elasticsearch instance, put the path of the pem file here.
+# elasticsearch.ssl.ca: /var/lib/jetty/base/fe/config/cacert.pem
+
+# Set to false to have a complete disregard for the validity of the SSL certificate.
+# elasticsearch.ssl.verify: true
+
+# Time in milliseconds to wait for elasticsearch to respond to pings, defaults to request_timeout setting
+# elasticsearch.pingTimeout: 1500
+
+# Time in milliseconds to wait for responses from the back end or elasticsearch.
+# This must be > 0
+# elasticsearch.requestTimeout: 300000
+
+# Time in milliseconds for Elasticsearch to wait for responses from shards.
+# Set to 0 to disable.
+# elasticsearch.shardTimeout: 0
+
+# Time in milliseconds to wait for Elasticsearch at Kibana startup before retrying
+# elasticsearch.startupTimeout: 5000
+
+# Set the path to where you would like the process id file to be created.
+# pid.file: /var/run/kibana.pid
+
+# If you would like to send the log output to a file you can set the path below.
+# logging.dest: stdout
+
+# Set this to true to suppress all logging output.
+# logging.silent: false
+
+# Set this to true to suppress all logging output except for error messages.
+# logging.quiet: false
+
+# Set this to true to log all events, including system usage information and all requests.
+# logging.verbose: false
+
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/LICENSE b/sdc-os-chef/sdc-kibana/chef-solo/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/README.md b/sdc-os-chef/sdc-kibana/chef-solo/README.md
new file mode 100644
index 0000000000..ddb0fda830
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/README.md
@@ -0,0 +1,37 @@
+Deprecated
+==========
+
+Use of this repository is deprecated. We recommend using the `chef generate repo` command that comes with [ChefDK](http://downloads.chef.io/chef-dk/).
+
+Overview
+========
+
+Every Chef installation needs a Chef Repository. This is the place where cookbooks, roles, config files and other artifacts for managing systems with Chef will live. We strongly recommend storing this repository in a version control system such as Git and treat it like source code.
+
+While we prefer Git, and make this repository available via GitHub, you are welcome to download a tar or zip archive and use your favorite version control system to manage the code.
+
+Repository Directories
+======================
+
+This repository contains several directories, and each directory contains a README file that describes what it is for in greater detail, and how to use it for managing your systems with Chef.
+
+* `cookbooks/` - Cookbooks you download or create.
+* `data_bags/` - Store data bags and items in .json in the repository.
+* `roles/` - Store roles in .rb or .json in the repository.
+* `environments/` - Store environments in .rb or .json in the repository.
+
+Configuration
+=============
+
+The repository contains a knife configuration file.
+
+* .chef/knife.rb
+
+The knife configuration file `.chef/knife.rb` is a repository specific configuration file for knife. If you're using Hosted Chef, you can download one for your organization from the management console. If you're using the Open Source Chef Server, you can generate a new one with `knife configure`. For more information about configuring Knife, see the Knife documentation.
+
+https://docs.chef.io/knife.html
+
+Next Steps
+==========
+
+Read the README file in each of the subdirectories for more information about what goes in those directories.
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/chefignore b/sdc-os-chef/sdc-kibana/chef-solo/chefignore
new file mode 100644
index 0000000000..ba30af6cff
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/chefignore
@@ -0,0 +1,11 @@
+# Put files/directories that should be ignored in this file.
+# Lines that start with '# ' are comments.
+
+# emacs
+*~
+
+# vim
+*.sw[a-z]
+
+# subversion
+*/.svn/*
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/cookbooks/README.md b/sdc-os-chef/sdc-kibana/chef-solo/cookbooks/README.md
new file mode 100644
index 0000000000..86ea46bfbb
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/cookbooks/README.md
@@ -0,0 +1,54 @@
+This directory contains the cookbooks used to configure systems in your infrastructure with Chef.
+
+Knife needs to be configured to know where the cookbooks are located with the `cookbook_path` setting. If this is not set, then several cookbook operations will fail to work properly.
+
+ cookbook_path ["./cookbooks"]
+
+This setting tells knife to look for the cookbooks directory in the present working directory. This means the knife cookbook subcommands need to be run in the `chef-repo` directory itself. To make sure that the cookbooks can be found elsewhere inside the repository, use an absolute path. This is a Ruby file, so something like the following can be used:
+
+ current_dir = File.dirname(__FILE__)
+ cookbook_path ["#{current_dir}/../cookbooks"]
+
+Which will set `current_dir` to the location of the knife.rb file itself (e.g. `~/chef-repo/.chef/knife.rb`).
+
+Configure knife to use your preferred copyright holder, email contact and license. Add the following lines to `.chef/knife.rb`.
+
+ cookbook_copyright "Example, Com."
+ cookbook_email "cookbooks@example.com"
+ cookbook_license "apachev2"
+
+Supported values for `cookbook_license` are "apachev2", "mit","gplv2","gplv3", or "none". These settings are used to prefill comments in the default recipe, and the corresponding values in the metadata.rb. You are free to change the the comments in those files.
+
+Create new cookbooks in this directory with Knife.
+
+ knife cookbook create COOKBOOK
+
+This will create all the cookbook directory components. You don't need to use them all, and can delete the ones you don't need. It also creates a README file, metadata.rb and default recipe.
+
+You can also download cookbooks directly from the Opscode Cookbook Site. There are two subcommands to help with this depending on what your preference is.
+
+The first and recommended method is to use a vendor branch if you're using Git. This is automatically handled with Knife.
+
+ knife cookbook site install COOKBOOK
+
+This will:
+
+* Download the cookbook tarball from cookbooks.opscode.com.
+* Ensure its on the git master branch.
+* Checks for an existing vendor branch, and creates if it doesn't.
+* Checks out the vendor branch (chef-vendor-COOKBOOK).
+* Removes the existing (old) version.
+* Untars the cookbook tarball it downloaded in the first step.
+* Adds the cookbook files to the git index and commits.
+* Creates a tag for the version downloaded.
+* Checks out the master branch again.
+* Merges the cookbook into master.
+* Repeats the above for all the cookbooks dependencies, downloading them from the community site
+
+The last step will ensure that any local changes or modifications you have made to the cookbook are preserved, so you can keep your changes through upstream updates.
+
+If you're not using Git, use the site download subcommand to download the tarball.
+
+ knife cookbook site download COOKBOOK
+
+This creates the COOKBOOK.tar.gz from in the current directory (e.g., `~/chef-repo`). We recommend following a workflow similar to the above for your version control tool.
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/data_bags/README.md b/sdc-os-chef/sdc-kibana/chef-solo/data_bags/README.md
new file mode 100644
index 0000000000..0c15a391fa
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/data_bags/README.md
@@ -0,0 +1,63 @@
+Data Bags
+---------
+
+This directory contains directories of the various data bags you create for your infrastructure. Each subdirectory corresponds to a data bag on the Chef Server, and contains JSON files of the items that go in the bag.
+
+First, create a directory for the data bag.
+
+ mkdir data_bags/BAG
+
+Then create the JSON files for items that will go into that bag.
+
+ $EDITOR data_bags/BAG/ITEM.json
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM". For example,
+
+ {
+ "id": "foo"
+ }
+
+Next, create the data bag on the Chef Server.
+
+ knife data bag create BAG
+
+Then upload the items in the data bag's directory to the Chef Server.
+
+ knife data bag from file BAG ITEM.json
+
+
+Encrypted Data Bags
+-------------------
+
+Added in Chef 0.10, encrypted data bags allow you to encrypt the contents of your data bags. The content of attributes will no longer be searchable. To use encrypted data bags, first you must have or create a secret key.
+
+ openssl rand -base64 512 > secret_key
+
+You may use this secret_key to add items to a data bag during a create.
+
+ knife data bag create --secret-file secret_key passwords mysql
+
+You may also use it when adding ITEMs from files,
+
+ knife data bag create passwords
+ knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM" and the contents will be encrypted when uploaded. For example,
+
+ {
+ "id": "mysql",
+ "password": "abc123"
+ }
+
+Without the secret_key, the contents are encrypted.
+
+ knife data bag show passwords mysql
+ id: mysql
+ password: 2I0XUUve1TXEojEyeGsjhw==
+
+Use the secret_key to view the contents.
+
+ knife data bag show passwords mysql --secret-file secret_key
+ id: mysql
+ password: abc123
+
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/environments/README.md b/sdc-os-chef/sdc-kibana/chef-solo/environments/README.md
new file mode 100644
index 0000000000..50ac48db2b
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/environments/README.md
@@ -0,0 +1,5 @@
+Requires Chef 0.10.0+.
+
+This directory is for Ruby DSL and JSON files for environments. For more information see the Chef wiki page:
+
+http://docs.chef.io/environments.html
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/roles/README.md b/sdc-os-chef/sdc-kibana/chef-solo/roles/README.md
new file mode 100644
index 0000000000..b0ee0b4d21
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/roles/README.md
@@ -0,0 +1,16 @@
+Create roles here, in either the Role Ruby DSL (.rb) or JSON (.json) files. To install roles on the server, use knife.
+
+For example, create `roles/base_example.rb`:
+
+ name "base_example"
+ description "Example base role applied to all nodes."
+ # List of recipes and roles to apply. Requires Chef 0.8, earlier versions use 'recipes()'.
+ #run_list()
+ # Attributes applied if the node doesn't have it set already.
+ #default_attributes()
+ # Attributes applied no matter what the node has set already.
+ #override_attributes()
+
+Then upload it to the Chef Server:
+
+ knife role from file roles/base_example.rb
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/solo.json b/sdc-os-chef/sdc-kibana/chef-solo/solo.json
new file mode 100644
index 0000000000..7c37f61156
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/solo.json
@@ -0,0 +1,4 @@
+{
+ "run_list": [ "recipe[sdc-kibana::setup_kibana]" ]
+}
+
diff --git a/sdc-os-chef/sdc-kibana/chef-solo/solo.rb b/sdc-os-chef/sdc-kibana/chef-solo/solo.rb
new file mode 100644
index 0000000000..06c1af4592
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/chef-solo/solo.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/solo.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-kibana/startup.sh b/sdc-os-chef/sdc-kibana/startup.sh
new file mode 100644
index 0000000000..b16aa87a7c
--- /dev/null
+++ b/sdc-os-chef/sdc-kibana/startup.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+export CHEFNAME=${ENVNAME}
+cd /root/chef-solo
+chef-solo -c solo.rb -E ${CHEFNAME}
+
+/docker-entrypoint.sh kibana
diff --git a/sdc-os-chef/sdc-sanity/Dockerfile b/sdc-os-chef/sdc-sanity/Dockerfile
new file mode 100644
index 0000000000..8c1e568efa
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/Dockerfile
@@ -0,0 +1,20 @@
+FROM ubuntu
+RUN apt-get -y update && apt-get -y install --no-install-recommends apt-utils
+RUN apt-get -y install curl
+RUN apt-get -y install vim
+RUN apt-get -y install default-jre && apt-get -y install openjdk-8-jdk
+RUN update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java
+
+
+COPY chef-solo /root/chef-solo/
+COPY chef-repo/cookbooks/. /root/chef-solo/cookbooks/
+
+
+# install chef-solo
+RUN curl -L https://www.opscode.com/chef/install.sh | bash
+
+COPY startup.sh /root/
+
+RUN chmod 770 /root/startup.sh
+
+ENTRYPOINT [ "/root/startup.sh" ]
diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/attributes/default.rb b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/attributes/default.rb
new file mode 100644
index 0000000000..4287ca8617
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/attributes/default.rb
@@ -0,0 +1 @@
+# \ No newline at end of file
diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/recipes/setup_sanity.rb b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/recipes/setup_sanity.rb
new file mode 100644
index 0000000000..7f0843d598
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/recipes/setup_sanity.rb
@@ -0,0 +1,68 @@
+tests_path = "/tmp/sdc-tests/"
+ci_test_suite = "sanity.xml"
+
+
+bash "extract asdc-tests" do
+ code <<-EOH
+ [ ! -d /tmp/sdc-tests/ ] && mkdir -p /tmp/sdc-tests
+ cd /tmp/sdc-tests
+ rm -rf *
+ /bin/tar -xf /root/chef-solo/cookbooks/sdc-sanity/files/default/asdc-tests.tar --strip-components=1 -C /tmp/sdc-tests
+ chmod -R 755 /tmp/sdc-tests
+ EOH
+end
+
+
+template "sdc-yaml-config" do
+ path "/tmp/sdc-tests/conf/sdc.yaml"
+ source "sdc-sanity.yaml.erb"
+ owner "root"
+ group "root"
+ mode "0755"
+ variables ({
+ :target_path => "#{tests_path}/target",
+ :catalogBE_ip => node['Nodes']['BE'],
+ :catalogBE_port => node['BE'][:http_port],
+ :webportal_ip => node['Nodes']['FE'],
+ :webportal_port => node['FE'][:http_port],
+ :titan_file => "/tmp/sdc-tests/conf/titan.properties",
+ :tests_path_ci => "#{tests_path}/CI/tests",
+ :components_path => "#{tests_path}/CI/components",
+ :importResourceConfigDir => "#{tests_path}/CI/importResource",
+ :importTypesDir => "#{tests_path}/CI/importTypesTest",
+ :importResourceTestsConfigDir => "#{tests_path}/CI/importResourceTests",
+ :ConfigurationFile => "#{tests_path}/conf/configuration.yaml",
+ :errorConfigurationFile => "#{tests_path}/conf/error-configuration.yaml",
+ :CASSANDRA_IP => node['Nodes']['CS'],
+ :CASSANDRA_PWD => node['cassandra'][:cassandra_password],
+ :CASSANDRA_USR => node['cassandra'][:cassandra_user]
+ })
+end
+
+
+replication_factor=1
+template "titan.properties" do
+ path "/tmp/sdc-tests/conf/titan.properties"
+ source "BE-titan.properties.erb"
+ owner "root"
+ group "root"
+ mode "0755"
+ variables({
+ :CASSANDRA_IP => node['Nodes']['CS'],
+ :CASSANDRA_PWD => node['cassandra'][:cassandra_password],
+ :CASSANDRA_USR => node['cassandra'][:cassandra_user],
+ :DC_NAME => "DC-"+node.chef_environment,
+ :rep_factor => replication_factor
+ })
+end
+
+
+bash "run asdc ci sanity tests" do
+ cwd "#{tests_path}"
+ code <<-EOH
+ jar_file=`ls asdc-tests-*-jar-with-dependencies.jar`
+ ./startTest.sh $jar_file #{ci_test_suite}
+ echo "return code from startTest.sh = [$?]"
+ EOH
+ timeout 72000
+end
diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/BE-titan.properties.erb b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/BE-titan.properties.erb
new file mode 100644
index 0000000000..27386d47ef
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/BE-titan.properties.erb
@@ -0,0 +1,24 @@
+storage.backend=cassandra
+storage.hostname=<%= @CASSANDRA_IP %>
+storage.port=9160
+storage.username=<%= @CASSANDRA_USR %>
+storage.password=<%= @CASSANDRA_PWD %>
+storage.connection-timeout=10000
+
+storage.cassandra.ssl.enabled=false
+storage.cassandra.ssl.truststore.location=/var/lib/jetty/config/.truststore
+storage.cassandra.ssl.truststore.password=Aa123456
+
+cache.db-cache = false
+cache.db-cache-clean-wait = 20
+cache.db-cache-time = 180000
+cache.db-cache-size = 0.5
+
+storage.cassandra.read-consistency-level=LOCAL_QUORUM
+storage.cassandra.write-consistency-level=LOCAL_QUORUM
+storage.cassandra.replication-strategy-class=org.apache.cassandra.locator.NetworkTopologyStrategy
+storage.cassandra.replication-strategy-options=<%= @DC_NAME %>,<%= @rep_factor %>
+storage.cassandra.astyanax.local-datacenter=<%= @DC_NAME %>
+
+storage.lock.retries=5
+storage.lock.wait-time=500
diff --git a/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/sdc-sanity.yaml.erb b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/sdc-sanity.yaml.erb
new file mode 100644
index 0000000000..91c09db818
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-repo/cookbooks/sdc-sanity/templates/default/sdc-sanity.yaml.erb
@@ -0,0 +1,82 @@
+outputFolder: <%= @target_path %>
+reportName: index.html
+esHost: eshost
+disributionClientHost: disClient
+catalogFeHost: <%= @webportal_ip %>
+catalogFePort: <%= @webportal_port %>
+catalogBeHost: <%= @catalogBE_ip %>
+catalogBePort: <%= @catalogBE_port %>
+disributionClientPort: 8181
+esPort: 9200
+
+
+resourceConfigDir: <%= @tests_path_ci %>
+componentsConfigDir: <%= @components_path %>
+importResourceConfigDir: <%= @importResourceConfigDir %>
+importResourceTestsConfigDir: <%= @importResourceTestsConfigDir %>
+errorConfigurationFile: <%= @errorConfigurationFile %>
+configurationFile: <%= @ConfigurationFile %>
+importTypesConfigDir: <%= @importTypesDir %>
+titanPropertiesFile: <%= @titan_file %>
+
+cassandraHost: <%= @CASSANDRA_IP %>
+cassandraAuthenticate: true
+cassandraUsername: <%= @CASSANDRA_USR %>
+cassandraPassword: <%= @CASSANDRA_PWD %>
+cassandraSsl: false
+cassandraTruststorePath : /tmp/.truststore
+cassandraTruststorePassword : Aa123456
+cassandraAuditKeySpace: sdcAudit
+cassandraArtifactKeySpace: sdcArtifact
+
+stopOnClassFailure: false
+
+#List of non-abstract resources to keep during titan cleanup between tests
+#Only 1.0 version will be kept
+resourcesNotToDelete:
+ - Compute
+ - Database
+ - ObjectStorage
+ - BlockStorage
+ - LoadBalancer
+ - Port
+ - Network
+ - Root
+ - ContainerApplication
+ - ContainerRuntime
+ - DBMS
+ - SoftwareComponent
+ - WebApplication
+ - WebServer
+ - CinderVolume
+ - ContrailVirtualNetwork
+ - NeutronNet
+ - NeutronPort
+ - NovaServer
+ - AbstractSubstitute
+ - ContrailAbstractSubstitute
+ - ContrailCompute
+ - ContrailNetworkRules
+ - ContrailPort
+ - ContrailV2NetworkRules
+ - ContrailV2VirtualMachineInterface
+ - ContrailV2VirtualNetwork
+ - ContrailVirtualNetwork
+ - VL ELINE
+ - SecurityRules
+ - VL
+#Resource categories to keep (including all their subcategories)
+resourceCategoriesNotToDelete:
+ - Generic
+ - Network L2-3
+ - Network L4+
+ - Application L4+
+ - Network Connectivity
+ - DcaeComponent
+
+#Service categories to keep
+serviceCategoriesNotToDelete:
+ - Mobility
+ - Network L1-3
+ - Network L4+
+ - VoIP Call Control
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/LICENSE b/sdc-os-chef/sdc-sanity/chef-solo/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/README.md b/sdc-os-chef/sdc-sanity/chef-solo/README.md
new file mode 100644
index 0000000000..ddb0fda830
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/README.md
@@ -0,0 +1,37 @@
+Deprecated
+==========
+
+Use of this repository is deprecated. We recommend using the `chef generate repo` command that comes with [ChefDK](http://downloads.chef.io/chef-dk/).
+
+Overview
+========
+
+Every Chef installation needs a Chef Repository. This is the place where cookbooks, roles, config files and other artifacts for managing systems with Chef will live. We strongly recommend storing this repository in a version control system such as Git and treat it like source code.
+
+While we prefer Git, and make this repository available via GitHub, you are welcome to download a tar or zip archive and use your favorite version control system to manage the code.
+
+Repository Directories
+======================
+
+This repository contains several directories, and each directory contains a README file that describes what it is for in greater detail, and how to use it for managing your systems with Chef.
+
+* `cookbooks/` - Cookbooks you download or create.
+* `data_bags/` - Store data bags and items in .json in the repository.
+* `roles/` - Store roles in .rb or .json in the repository.
+* `environments/` - Store environments in .rb or .json in the repository.
+
+Configuration
+=============
+
+The repository contains a knife configuration file.
+
+* .chef/knife.rb
+
+The knife configuration file `.chef/knife.rb` is a repository specific configuration file for knife. If you're using Hosted Chef, you can download one for your organization from the management console. If you're using the Open Source Chef Server, you can generate a new one with `knife configure`. For more information about configuring Knife, see the Knife documentation.
+
+https://docs.chef.io/knife.html
+
+Next Steps
+==========
+
+Read the README file in each of the subdirectories for more information about what goes in those directories.
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/chefignore b/sdc-os-chef/sdc-sanity/chef-solo/chefignore
new file mode 100644
index 0000000000..ba30af6cff
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/chefignore
@@ -0,0 +1,11 @@
+# Put files/directories that should be ignored in this file.
+# Lines that start with '# ' are comments.
+
+# emacs
+*~
+
+# vim
+*.sw[a-z]
+
+# subversion
+*/.svn/*
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/cookbooks/README.md b/sdc-os-chef/sdc-sanity/chef-solo/cookbooks/README.md
new file mode 100644
index 0000000000..86ea46bfbb
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/cookbooks/README.md
@@ -0,0 +1,54 @@
+This directory contains the cookbooks used to configure systems in your infrastructure with Chef.
+
+Knife needs to be configured to know where the cookbooks are located with the `cookbook_path` setting. If this is not set, then several cookbook operations will fail to work properly.
+
+ cookbook_path ["./cookbooks"]
+
+This setting tells knife to look for the cookbooks directory in the present working directory. This means the knife cookbook subcommands need to be run in the `chef-repo` directory itself. To make sure that the cookbooks can be found elsewhere inside the repository, use an absolute path. This is a Ruby file, so something like the following can be used:
+
+ current_dir = File.dirname(__FILE__)
+ cookbook_path ["#{current_dir}/../cookbooks"]
+
+Which will set `current_dir` to the location of the knife.rb file itself (e.g. `~/chef-repo/.chef/knife.rb`).
+
+Configure knife to use your preferred copyright holder, email contact and license. Add the following lines to `.chef/knife.rb`.
+
+ cookbook_copyright "Example, Com."
+ cookbook_email "cookbooks@example.com"
+ cookbook_license "apachev2"
+
+Supported values for `cookbook_license` are "apachev2", "mit","gplv2","gplv3", or "none". These settings are used to prefill comments in the default recipe, and the corresponding values in the metadata.rb. You are free to change the the comments in those files.
+
+Create new cookbooks in this directory with Knife.
+
+ knife cookbook create COOKBOOK
+
+This will create all the cookbook directory components. You don't need to use them all, and can delete the ones you don't need. It also creates a README file, metadata.rb and default recipe.
+
+You can also download cookbooks directly from the Opscode Cookbook Site. There are two subcommands to help with this depending on what your preference is.
+
+The first and recommended method is to use a vendor branch if you're using Git. This is automatically handled with Knife.
+
+ knife cookbook site install COOKBOOK
+
+This will:
+
+* Download the cookbook tarball from cookbooks.opscode.com.
+* Ensure its on the git master branch.
+* Checks for an existing vendor branch, and creates if it doesn't.
+* Checks out the vendor branch (chef-vendor-COOKBOOK).
+* Removes the existing (old) version.
+* Untars the cookbook tarball it downloaded in the first step.
+* Adds the cookbook files to the git index and commits.
+* Creates a tag for the version downloaded.
+* Checks out the master branch again.
+* Merges the cookbook into master.
+* Repeats the above for all the cookbooks dependencies, downloading them from the community site
+
+The last step will ensure that any local changes or modifications you have made to the cookbook are preserved, so you can keep your changes through upstream updates.
+
+If you're not using Git, use the site download subcommand to download the tarball.
+
+ knife cookbook site download COOKBOOK
+
+This creates the COOKBOOK.tar.gz from in the current directory (e.g., `~/chef-repo`). We recommend following a workflow similar to the above for your version control tool.
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/data_bags/README.md b/sdc-os-chef/sdc-sanity/chef-solo/data_bags/README.md
new file mode 100644
index 0000000000..0c15a391fa
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/data_bags/README.md
@@ -0,0 +1,63 @@
+Data Bags
+---------
+
+This directory contains directories of the various data bags you create for your infrastructure. Each subdirectory corresponds to a data bag on the Chef Server, and contains JSON files of the items that go in the bag.
+
+First, create a directory for the data bag.
+
+ mkdir data_bags/BAG
+
+Then create the JSON files for items that will go into that bag.
+
+ $EDITOR data_bags/BAG/ITEM.json
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM". For example,
+
+ {
+ "id": "foo"
+ }
+
+Next, create the data bag on the Chef Server.
+
+ knife data bag create BAG
+
+Then upload the items in the data bag's directory to the Chef Server.
+
+ knife data bag from file BAG ITEM.json
+
+
+Encrypted Data Bags
+-------------------
+
+Added in Chef 0.10, encrypted data bags allow you to encrypt the contents of your data bags. The content of attributes will no longer be searchable. To use encrypted data bags, first you must have or create a secret key.
+
+ openssl rand -base64 512 > secret_key
+
+You may use this secret_key to add items to a data bag during a create.
+
+ knife data bag create --secret-file secret_key passwords mysql
+
+You may also use it when adding ITEMs from files,
+
+ knife data bag create passwords
+ knife data bag from file passwords data_bags/passwords/mysql.json --secret-file secret_key
+
+The JSON for the ITEM must contain a key named "id" with a value equal to "ITEM" and the contents will be encrypted when uploaded. For example,
+
+ {
+ "id": "mysql",
+ "password": "abc123"
+ }
+
+Without the secret_key, the contents are encrypted.
+
+ knife data bag show passwords mysql
+ id: mysql
+ password: 2I0XUUve1TXEojEyeGsjhw==
+
+Use the secret_key to view the contents.
+
+ knife data bag show passwords mysql --secret-file secret_key
+ id: mysql
+ password: abc123
+
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/environments/README.md b/sdc-os-chef/sdc-sanity/chef-solo/environments/README.md
new file mode 100644
index 0000000000..50ac48db2b
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/environments/README.md
@@ -0,0 +1,5 @@
+Requires Chef 0.10.0+.
+
+This directory is for Ruby DSL and JSON files for environments. For more information see the Chef wiki page:
+
+http://docs.chef.io/environments.html
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/roles/README.md b/sdc-os-chef/sdc-sanity/chef-solo/roles/README.md
new file mode 100644
index 0000000000..b0ee0b4d21
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/roles/README.md
@@ -0,0 +1,16 @@
+Create roles here, in either the Role Ruby DSL (.rb) or JSON (.json) files. To install roles on the server, use knife.
+
+For example, create `roles/base_example.rb`:
+
+ name "base_example"
+ description "Example base role applied to all nodes."
+ # List of recipes and roles to apply. Requires Chef 0.8, earlier versions use 'recipes()'.
+ #run_list()
+ # Attributes applied if the node doesn't have it set already.
+ #default_attributes()
+ # Attributes applied no matter what the node has set already.
+ #override_attributes()
+
+Then upload it to the Chef Server:
+
+ knife role from file roles/base_example.rb
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/solo.json b/sdc-os-chef/sdc-sanity/chef-solo/solo.json
new file mode 100644
index 0000000000..5bd228127d
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/solo.json
@@ -0,0 +1,4 @@
+{
+ "run_list": [ "recipe[sdc-sanity::setup_sanity]" ]
+}
+
diff --git a/sdc-os-chef/sdc-sanity/chef-solo/solo.rb b/sdc-os-chef/sdc-sanity/chef-solo/solo.rb
new file mode 100644
index 0000000000..06c1af4592
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/chef-solo/solo.rb
@@ -0,0 +1,16 @@
+root = File.absolute_path(File.dirname(__FILE__))
+file_cache_path root
+cookbook_path root + '/cookbooks'
+json_attribs root + '/solo.json'
+checksum_path root + '/checksums'
+data_bag_path root + '/data_bags'
+environment_path root + '/environments'
+file_backup_path root + '/backup'
+file_cache_path root + '/cache'
+log_level :info
+log_location STDOUT
+rest_timeout 300
+role_path root + '/roles'
+syntax_check_cache_path
+umask 0022
+verbose_logging nil
diff --git a/sdc-os-chef/sdc-sanity/startup.sh b/sdc-os-chef/sdc-sanity/startup.sh
new file mode 100644
index 0000000000..c9faae62e5
--- /dev/null
+++ b/sdc-os-chef/sdc-sanity/startup.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+export CHEFNAME=${ENVNAME}
+cd /root/chef-solo
+chef-solo -c solo.rb -E ${CHEFNAME}
+
+rc=$?
+
+if [[ $rc != 0 ]]; then
+ echo "Sanity failed !!!"
+ exit $rc
+else
+ echo "completed successfully :-)"
+ exit 0
+fi
+
+#/docker-entrypoint.sh
diff --git a/security-utils/.gitignore b/security-utils/.gitignore
new file mode 100644
index 0000000000..ae3c172604
--- /dev/null
+++ b/security-utils/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/security-utils/pom.xml b/security-utils/pom.xml
new file mode 100644
index 0000000000..79feeb18a0
--- /dev/null
+++ b/security-utils/pom.xml
@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>security-utils</artifactId>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java b/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java
new file mode 100644
index 0000000000..ef424b95de
--- /dev/null
+++ b/security-utils/src/main/java/org/openecomp/sdc/security/Passwords.java
@@ -0,0 +1,163 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.security;
+
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Random;
+
+public class Passwords {
+
+ private static final Random RANDOM = new SecureRandom();
+ private static final int SALT = 0;
+ private static final int HASH = 1;
+ private static final String HASH_ALGORITHM = "SHA-256";
+
+ /**
+ * static utility class
+ */
+ private Passwords() {
+ }
+
+ /**
+ * the method calculates a hash with a generated salt for the given password
+ *
+ * @param password
+ * @return a "salt:hash" value
+ */
+ public static String hashPassword(String password) {
+ byte[] salt = getNextSalt();
+ byte byteData[] = hash(salt, password.getBytes());
+ if (byteData != null) {
+ return toHex(salt) + ":" + toHex(byteData);
+ }
+ return null;
+
+ }
+
+ /**
+ * the method checks if the given password matches the calculated hash
+ *
+ * @param password
+ * @param expectedHash
+ * @return
+ */
+ public static boolean isExpectedPassword(String password, String expectedHash) {
+ String[] params = expectedHash.split(":");
+ return isExpectedPassword(password, params[SALT], params[HASH]);
+ }
+
+ /**
+ * the method checks if the given password matches the calculated hash
+ *
+ * @param password
+ * @param salt
+ * @param hash
+ * the hash generated using the salt
+ * @return true if the password matched the hash
+ */
+ public static boolean isExpectedPassword(String password, String salt, String hash) {
+ byte[] saltBytes = fromHex(salt);
+ byte[] hashBytes = fromHex(hash);
+
+ byte byteData[] = hash(saltBytes, password.getBytes());
+ if (byteData != null) {
+ return Arrays.equals(byteData, hashBytes);
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ if (args.length > 1 || args.length > 0) {
+ System.out.println("[" + hashPassword(args[0]) + "]");
+ } else {
+ System.out.println("no passward passed.");
+ }
+
+ }
+
+ /**
+ * Returns a random salt to be used to hash a password.
+ *
+ * @return a 16 bytes random salt
+ */
+ private static byte[] getNextSalt() {
+ byte[] salt = new byte[16];
+ RANDOM.nextBytes(salt);
+ return salt;
+ }
+
+ /**
+ * hase's the salt and value using the chosen algorithm
+ *
+ * @param salt
+ * @param password
+ * @return an array of bytes resulting from the hash
+ */
+ private static byte[] hash(byte[] salt, byte[] password) {
+ MessageDigest md;
+ byte[] byteData = null;
+ try {
+ md = MessageDigest.getInstance(HASH_ALGORITHM);
+ md.update(salt);
+ md.update(password);
+ byteData = md.digest();
+ } catch (NoSuchAlgorithmException e) {
+ System.out.println("invalid algorithm name");
+ }
+ return byteData;
+ }
+
+ /**
+ * Converts a string of hexadecimal characters into a byte array.
+ *
+ * @param hex
+ * the hex string
+ * @return the hex string decoded into a byte array
+ */
+ private static byte[] fromHex(String hex) {
+ byte[] binary = new byte[hex.length() / 2];
+ for (int i = 0; i < binary.length; i++) {
+ binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
+ }
+ return binary;
+ }
+
+ /**
+ * Converts a byte array into a hexadecimal string.
+ *
+ * @param array
+ * the byte array to convert
+ * @return a length*2 character string encoding the byte array
+ */
+ private static String toHex(byte[] array) {
+ BigInteger bi = new BigInteger(1, array);
+ String hex = bi.toString(16);
+ int paddingLength = (array.length * 2) - hex.length();
+ if (paddingLength > 0)
+ return String.format("%0" + paddingLength + "d", 0) + hex;
+ else
+ return hex;
+ }
+}
diff --git a/security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java b/security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java
new file mode 100644
index 0000000000..b32f6ab51f
--- /dev/null
+++ b/security-utils/src/test/java/org/openecomp/sdc/security/PasswordTest.java
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.security;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.openecomp.sdc.security.Passwords;
+
+public class PasswordTest {
+
+ @Test
+ public void hashtest() {
+ String password = "123456";
+ String hash = Passwords.hashPassword(password);
+ assertTrue(Passwords.isExpectedPassword(password, hash));
+ password = "1sdfgsgd23456";
+ hash = Passwords.hashPassword(password);
+ assertTrue(Passwords.isExpectedPassword(password, hash));
+ password = "1sdfgsgd2345((*&%$%6";
+ hash = Passwords.hashPassword(password);
+ assertTrue(Passwords.isExpectedPassword(password, hash));
+ password = "";
+ hash = Passwords.hashPassword(password);
+ assertTrue(Passwords.isExpectedPassword(password, hash));
+ password = " ";
+ hash = Passwords.hashPassword(password);
+ assertTrue(Passwords.isExpectedPassword(password, hash));
+ }
+
+}
diff --git a/ui-ci-dev/.gitignore b/ui-ci-dev/.gitignore
new file mode 100644
index 0000000000..6405eb7c05
--- /dev/null
+++ b/ui-ci-dev/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+test-output/ \ No newline at end of file
diff --git a/ui-ci-dev/pom.xml b/ui-ci-dev/pom.xml
new file mode 100644
index 0000000000..b84322c8f1
--- /dev/null
+++ b/ui-ci-dev/pom.xml
@@ -0,0 +1,255 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <!-- <version>${asdc.version}</version> -->
+ <version>1610.2.1</version>
+ </parent>
+
+ <artifactId>ui-ci-dev</artifactId>
+ <description>Selenium tests for the SDnC Application</description>
+
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-java</artifactId>
+ <version>3.0.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-server</artifactId>
+ <version>2.48.2</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>asdc-tests</artifactId>
+ <version>${asdc-tests.version}</version>
+ </dependency>
+
+ <!-- <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>catalog-be</artifactId>
+ <version>${asdc-tests.version}</version>
+ </dependency> -->
+
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <version>1.14</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.3.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http core -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <version>1.9.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-distribution-client</artifactId>
+ <version>1.1.2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.10</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ <version>1.4.01</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <version>1.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.relevantcodes</groupId>
+ <artifactId>extentreports</artifactId>
+ <version>2.41.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.sikuli</groupId>
+ <artifactId>sikuli-api</artifactId>
+ <version>1.2.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.sikuli</groupId>
+ <artifactId>sikuli-core</artifactId>
+ <version>1.2.2</version>
+ </dependency>
+
+
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+
+
+ <build>
+ <plugins>
+
+
+
+ <!-- ============================================= -->
+ <!-- Create the JAR file with its dependencies -->
+ <!-- ============================================= -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.5.5</version>
+ <executions>
+ <execution>
+ <id>create.jar.with.dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+
+
+ <execution>
+ <id>tarball</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <finalName>${project.artifactId}-${full.release.version}${build.type}</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptor>${project.basedir}/tarball.xml</descriptor>
+ <attach>false</attach>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+
+ <!-- =========================== -->
+ <!-- HP Fortifay scanner -->
+ <!-- =========================== -->
+ <plugin>
+ <groupId>com.fortify.ps.maven.plugin</groupId>
+ <artifactId>sca-maven-plugin</artifactId>
+ <version>4.30</version>
+ <configuration>
+ <scanEnabled>false</scanEnabled>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/scripts/CreateVfsFromOnboarding.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/scripts/CreateVfsFromOnboarding.java
new file mode 100644
index 0000000000..b11f8d19ef
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/scripts/CreateVfsFromOnboarding.java
@@ -0,0 +1,67 @@
+package org.openecomp.sdc.uici.scripts;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.uici.tests.datatypes.CleanTypeEnum;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.OnboardUtility;
+
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+import com.google.gson.GsonBuilder;
+
+/**
+ * This Class functions to load mass zip files to vfs through onboarding.<br>
+ * It uses both BE & UI APIs
+ *
+ * @author mshitrit
+ *
+ */
+public class CreateVfsFromOnboarding extends SetupCDTest {
+ public static void main(String[] args) {
+ CreateVfsFromOnboarding manager = new CreateVfsFromOnboarding();
+
+ FunctionalInterfaces.swallowException(() -> manager.setEnvParameters(CleanTypeEnum.NONE.name()));
+ // String folderPath = args[0];
+ String folderPath = "C:\\onboardingTest\\onBoardingZips";
+ File folder = new File(folderPath);
+ File[] listOfFiles = folder.listFiles();
+ List<String> zipFileNames = Arrays.asList(listOfFiles).stream().map(file -> file.getName())
+ .filter(fileName -> fileName.endsWith(".zip")).collect(Collectors.toList());
+ Map<String, String> filesSuccessMap = new HashMap<>();
+ for (String fileName : zipFileNames) {
+ try {
+ // Before
+ manager.beforeState(null);
+ manager.setBrowserBeforeTest();
+ createSingleVfFromOnboarding(folderPath, fileName);
+ filesSuccessMap.put(fileName, "SUCCESS");
+
+ } catch (Exception e) {
+ filesSuccessMap.put(fileName, "FAIL");
+ } finally {
+ FunctionalInterfaces.swallowException(() -> manager.afterState(null));
+ manager.quitAfterTest();
+ }
+ }
+ Path file = Paths.get("RunResults.txt");
+ String stringDataModel = new GsonBuilder().setPrettyPrinting().create().toJson(filesSuccessMap);
+ FunctionalInterfaces.swallowException(() -> Files.write(file, stringDataModel.getBytes()));
+ }
+
+ private static void createSingleVfFromOnboarding(String filePath, String zipFileName) {
+ String userId = UserRoleEnum.DESIGNER.getUserId();
+ OnboardUtility.createVfFromOnboarding(userId, zipFileName, filePath);
+ GeneralUIUtils.submitForTestingElement("Vf From Onboarding");
+
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasElement.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasElement.java
new file mode 100644
index 0000000000..c23d05ab23
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasElement.java
@@ -0,0 +1,33 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+public final class CanvasElement {
+ private final String uniqueId;
+ private ImmutablePair<Integer, Integer> location;
+ private String elementName;
+
+ public String getElementName() {
+ return elementName;
+ }
+
+ public CanvasElement(String uniqueId, String elementName, ImmutablePair<Integer, Integer> location) {
+ super();
+ this.uniqueId = uniqueId;
+ this.location = location;
+ this.elementName = elementName;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public ImmutablePair<Integer, Integer> getLocation() {
+ return location;
+ }
+
+ public void setLocation(ImmutablePair<Integer, Integer> location) {
+ this.location = location;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasManager.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasManager.java
new file mode 100644
index 0000000000..5ef8c7a56a
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CanvasManager.java
@@ -0,0 +1,206 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+public final class CanvasManager {
+ private Map<String, CanvasElement> canvasElements;
+ private Actions actions;
+ private WebElement canvas;
+ private int reduceCanvasWidthFactor;
+ // Offsets Are used to find upper right corner of canvas element in order to
+ // connect links
+ private static final int CANVAS_ELEMENT_Y_OFFSET = 40;
+ private static final int CANVAS_ELEMENT_X_OFFSET = 21; // 14 - 27
+
+ private CanvasManager() {
+ canvasElements = new HashMap<>();
+ actions = new Actions(GeneralUIUtils.getDriver());
+ canvas = GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.CANVAS.getValue());
+ try {
+ WebElement webElement = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.CANVAS_RIGHT_PANEL.getValue());
+ reduceCanvasWidthFactor = webElement.getSize().width;
+ } catch (Exception e) {
+ reduceCanvasWidthFactor = 0;
+ }
+ }
+
+ public static CanvasManager getCanvasManager() {
+ return new CanvasManager();
+ }
+
+ public List<CanvasElement> getCanvasElements() {
+ return canvasElements.values().stream().collect(Collectors.toList());
+ }
+
+ private void addCanvasElement(CanvasElement element) {
+ canvasElements.put(element.getUniqueId(), element);
+ }
+
+ private void moveElementOnCanvas(CanvasElement canvasElement, ImmutablePair<Integer, Integer> newLocation) {
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(500);
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, newLocation.left, newLocation.right);
+ actions.release();
+ actions.perform();
+ canvasElement.setLocation(newLocation);
+ GeneralUIUtils.waitForLoader();
+
+ }
+
+ public void moveElementOnCanvas(CanvasElement canvasElement) {
+ moveElementOnCanvas(canvasElement, getFreePosition());
+ }
+
+ public void deleteElementFromCanvas(CanvasElement canvasElement) {
+ GeneralUIUtils.waitForLoader();
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.click();
+ actions.perform();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.DELETE_INSTANCE_BUTTON.getValue())
+ .click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ canvasElements.remove(canvasElement.getUniqueId());
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void selectElementFromCanvas(CanvasElement canvasElement) {
+ GeneralUIUtils.waitForLoader();
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.click();
+ actions.perform();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public CanvasElement createElementOnCanvas(LeftPanelCanvasItems canvasItem) {
+ return createElementOnCanvas(canvasItem.getValue(), false);
+ }
+
+ /**
+ * Creates Element on the Canvas - use the element name.
+ *
+ * @param elementName
+ * @return
+ */
+ public CanvasElement createElementOnCanvas(String elementName) {
+ return createElementOnCanvas(elementName, true);
+ }
+
+ private CanvasElement createElementOnCanvas(String elementName, boolean addPrefix) {
+ if (addPrefix) {
+ elementName = DataTestIdEnum.LEFT_PANEL_PREFIX + elementName;
+ }
+ GeneralUIUtils.waitForLoader();
+ WebElement element = GeneralUIUtils.getWebElementWaitForVisible(elementName);
+ ImmutablePair<Integer, Integer> freePosition = getFreePosition();
+ actions.moveToElement(element, 0, 0);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, freePosition.left, freePosition.right);
+ actions.release();
+ actions.perform();
+
+ String uniqueId = elementName + "_" + UUID.randomUUID().toString();
+ CanvasElement canvasElement = new CanvasElement(uniqueId, elementName, freePosition);
+ addCanvasElement(canvasElement);
+ GeneralUIUtils.waitForLoader();
+ return canvasElement;
+ }
+
+ public CanvasElement createUniqueVFOnCanvas(LeftPanelCanvasItems canvasItem) {
+ GeneralUIUtils.waitForLoader();
+ WebElement element = GeneralUIUtils.getWebElementWaitForVisible(canvasItem.getValue());
+ ImmutablePair<Integer, Integer> freePosition = getFreePosition();
+ actions.moveToElement(element, 0, 0);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, freePosition.left, freePosition.right);
+ actions.release();
+ actions.perform();
+
+ String uniqueId = canvasItem.name() + "_" + UUID.randomUUID().toString();
+ CanvasElement canvasElement = new CanvasElement(uniqueId, canvasItem.getValue(), freePosition);
+ addCanvasElement(canvasElement);
+ GeneralUIUtils.waitForLoader();
+ return canvasElement;
+ }
+
+ private ImmutablePair<Integer, Integer> getFreePosition() {
+ // TODO ui-ci use better method
+ ImmutablePair<Integer, Integer> randomPosition = null;
+ boolean freePosition = false;
+ int minSpace = 150;
+ while (!freePosition) {
+ ImmutablePair<Integer, Integer> tempRandomPosition = getRandomPosition();
+ freePosition = !canvasElements.values().stream().map(e -> e.getLocation())
+ .filter(e -> Math.abs(e.left - tempRandomPosition.left) < minSpace
+ && Math.abs(e.right - tempRandomPosition.right) < minSpace)
+ .findAny().isPresent();
+ randomPosition = tempRandomPosition;
+ }
+ return randomPosition;
+ }
+
+ private ImmutablePair<Integer, Integer> getRandomPosition() {
+ int edgeBuffer = 50;
+ Random random = new Random();
+ int xElement = random.nextInt(canvas.getSize().width - 2 * edgeBuffer - reduceCanvasWidthFactor) + edgeBuffer;
+ int yElement = random.nextInt(canvas.getSize().height - 2 * edgeBuffer) + edgeBuffer;
+ return new ImmutablePair<Integer, Integer>(xElement, yElement);
+ }
+
+ /**
+ * Links two elements on canvas.<br>
+ * Currently Supports Only elements in the default size.<br>
+ * Will not work for container type or smaller elements (cp, vl etc...)<br>
+ *
+ * @param firstElement
+ * @param secondElement
+ */
+ public void linkElements(CanvasElement firstElement, CanvasElement secondElement) {
+ GeneralUIUtils.waitForLoader();
+ drawSimpleLink(firstElement, secondElement);
+ selectReqAndCapAndConnect();
+
+ GeneralUIUtils.waitForLoader();
+
+ }
+
+ private void selectReqAndCapAndConnect() {
+ // Select First Cap
+ GeneralUIUtils.getWebElementsListWaitForVisible(DataTestIdEnum.LinkMenuItems.LINK_ITEM_CAP.getValue()).get(0)
+ .click();
+ // Select First Req
+ GeneralUIUtils.getWebElementsListWaitForVisible(DataTestIdEnum.LinkMenuItems.LINK_ITEM_REQ.getValue()).get(0)
+ .click();
+ // Connect
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LinkMenuItems.CONNECT_BUTTON.getValue()).click();
+
+ }
+
+ private void drawSimpleLink(CanvasElement firstElement, CanvasElement secondElement) {
+
+ int yOffset = CANVAS_ELEMENT_Y_OFFSET;
+ int xOffset = CANVAS_ELEMENT_X_OFFSET;
+ actions.moveToElement(canvas, firstElement.getLocation().left + xOffset,
+ firstElement.getLocation().right - yOffset);
+
+ actions.clickAndHold();
+ actions.moveToElement(canvas, secondElement.getLocation().left + xOffset,
+ secondElement.getLocation().right - yOffset);
+ actions.release();
+ actions.perform();
+ GeneralUIUtils.waitForLoader();
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CleanTypeEnum.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CleanTypeEnum.java
new file mode 100644
index 0000000000..f0691b89fc
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CleanTypeEnum.java
@@ -0,0 +1,28 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * enum that represents possible methods to clean DB before and after tests.
+ *
+ * @author mshitrit
+ *
+ */
+public enum CleanTypeEnum {
+ FULL,
+ /** Unreliable should be only used in dev **/
+ PARTIAL, NONE;
+
+ /**
+ * Returns CleanType enum by it name
+ *
+ * @param cleanType
+ * @return
+ */
+ public static CleanTypeEnum findByName(String cleanType) {
+ final Optional<CleanTypeEnum> findAny = Arrays.asList(CleanTypeEnum.values()).stream()
+ .filter(e -> e.name().equals(cleanType)).findAny();
+ return findAny.get();
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndImportButtonsEnum.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndImportButtonsEnum.java
new file mode 100644
index 0000000000..382584a48d
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndImportButtonsEnum.java
@@ -0,0 +1,7 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+public enum CreateAndImportButtonsEnum {
+
+ IMPORT_VF, IMPORT_VFC, IMPORT_CP, IMPORT_VL, CREATE_VF, CREATE_SERVICE, CREATE_PRODUCT;
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndUpdateStepsEnum.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndUpdateStepsEnum.java
new file mode 100644
index 0000000000..ad0281dd74
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/CreateAndUpdateStepsEnum.java
@@ -0,0 +1,25 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+public enum CreateAndUpdateStepsEnum {
+ GENERAL("Generalstep"),
+ ICON("Iconstep"),
+ DEPLOYMENT_ARTIFACT("Deployment Artifactstep"),
+ INFORMATION_ARTIFACT("Information Artifactstep"),
+ PROPERTIES("Propertiesstep"),
+ ATTRIBUTES("Attributesstep"),
+ COMPOSITION("Compositionstep"),
+ DEPLOYMENT("Deploymentstep"),
+ REQUIREMENTS_AND_CAPABILITIES("Req. & Capabilitiesstep"),
+ INPUTS("Inputsstep");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private CreateAndUpdateStepsEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/DataTestIdEnum.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/DataTestIdEnum.java
new file mode 100644
index 0000000000..64db12a422
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/DataTestIdEnum.java
@@ -0,0 +1,428 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+public final class DataTestIdEnum {
+ private DataTestIdEnum() {
+ };
+
+ public enum Dashboard {
+ IMPORT_AREA("importButtonsArea"),
+ BUTTON_ADD_VF("createResourceButton"),
+ BUTTON_ADD_SERVICE("createServiceButton"),
+ IMPORT_VFC("importVFCbutton"),
+ IMPORT_VF("importVFbutton"),
+ IMPORT_VFC_FILE("file-importVFCbutton"),
+ IMPORT_VF_FILE("file-importVFbutton");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private Dashboard(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum LifeCyleChangeButtons {
+ CREATE("create/save"),
+ CHECK_IN("check_in"),
+ SUBMIT_FOR_TESTING("submit_for_testing"),
+ START_TESTING("start_testing"),
+ ACCEPT("accept"),
+ APPROVE("approve"),
+ DISTRIBUTE("distribute");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LifeCyleChangeButtons(String value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Artifacts Related Elements
+ *
+ * @author mshitrit
+ *
+ */
+ public enum Artifatcs {
+ ADD_DEPLOYMENT_ARTIFACT("add-deployment-artifact-button"),
+ SELECT_ARTIFACT_DROPDOWN("selectArtifact"),
+ ARTIFACT_TYPE_DROPDOWN("artifacttype"),
+ ARTIFACT_DESCRIPTION("description"),
+ ARTIFACT_LABEL("artifactLabel"),
+ BROWSE_BUTTON("browseButton"),
+ ADD_BUTTON("Add");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private Artifatcs(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum InformationalArtifatcs {
+ CLOUD_QUESTIONNAIRE("Cloud Questionnaire (completed)"),
+ FEATURES("Features"),
+ VENDOR_TEST_RESULT("Vendor Test Result"),
+ TEST_SCRIPTS("Test Scripts"),
+ HEAT_TEMPLATE_FROM_VENDOR("HEAT Template from Vendor"),
+ CAPACITY("Capacity");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private InformationalArtifatcs(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum ArtifactModal {
+ LABEL("artifact-label"), TYPE("artifacttype"),;
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ArtifactModal(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum ModalItems {
+ BROWSE_BUTTON("browseButton"),
+ ADD("Add"),
+ DESCRIPTION("description"),
+ SUMBIT_FOR_TESTING_MESSAGE("changeLifeCycleMessage"),
+ OK("OK"),
+ CANCEL("Cancel"),
+ ACCEP_TESTING_MESSAGE("checkindialog"),
+ MESSAGE_TEXT("message"),
+ DONE("Done");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ModalItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public static final String LEFT_PANEL_PREFIX = "leftbar-section-content-item-";
+
+ public enum LeftPanelCanvasItems {
+ BLOCK_STORAGE(LEFT_PANEL_PREFIX + "BlockStorage"),
+ CINDER_VOLUME(LEFT_PANEL_PREFIX + "CinderVolume"),
+ COMPUTE(LEFT_PANEL_PREFIX + "Compute"),
+ LOAD_BALANCER(LEFT_PANEL_PREFIX + "LoadBalancer"),
+ NOVA_SERVER(LEFT_PANEL_PREFIX + "NovaServer"),
+ OBJECT_STORAGE(LEFT_PANEL_PREFIX + "ObjectStorage"),
+ // NEUTRON_PORT(LEFT_PANEL_PREFIX + "-NeutronPort"),
+ // PORT(LEFT_PANEL_PREFIX + "-Port"),
+ DATABASE(LEFT_PANEL_PREFIX + "-Database"),
+ VMMC(LEFT_PANEL_PREFIX + "vmmc_work");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LeftPanelCanvasItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum RightBar {
+ PROPERTIES_AND_ATTRIBUTES("properties-and-attributes-tab"),
+ DEPLOYMENT_ARTIFACTS("deployment-artifact-tab"),
+ ARTIFACT_NAME("artifactName"),
+ ADD_ARTIFACT_BUTTON("add_Artifact_Button"),
+ DELETE_ARTIFACT_BUTTON("delete"),
+ MYATTR_ATTR_FROM_LIST("my_attr-attr"),
+ MYATTR_ATTR_VALUE_FROM_LIST("value-of-my_attr"),;
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private RightBar(String value) {
+ this.value = value;
+ }
+ }
+
+ // for now we use index to work with the breadcrumbs
+ // any change in the breadcrumbs position will require an update here also
+ public enum BreadcrumbsButtonsEnum {
+ HOME("breadcrumbs-button-0"),
+ COMPONENT("breadcrumbs-button-1");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private BreadcrumbsButtonsEnum(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum InputsEnum {
+ VF_INSTANCE("inputs-vf-instance-0"),
+ FIRST_INPUT_CHECKBOX("inputs-checkbox-0"),
+ SECOND_INPUT_CHECKBOX("inputs-checkbox-1"),
+ ADD_INPUTS_BUTTON("add-inputs-to-service-button"),
+ SERVICE_INPUT("service-input-0"),
+ DELETE_INPUT("delete-input-0");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private InputsEnum(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum TabsBar {
+ HIERARCHY_TAB("hierarchy-tab"),
+ SELECTED_TAB("selected-tab"),
+ TAB_HEADER("tab-header"),
+ TAB_SUB_HEADER("tab-sub-header"),
+ HIERARCHY_MODULE("hierarchy-module-0"),
+ HIERARCHY_MODULE_TITLE("hierarchy-module-0-title"),
+ HIERARCHY_SELECTED_MODULE_DATA("selected-module-data"),
+ HIERARCHY_SELECTED_MODULE_NAME("selected-module-name"),
+ HIERARCHY_SELECTED_MODULE_UUID("selected-module-group-uuid"),
+ HIERARCHY_SELECTED_MODULE_VERSION("selected-module-version"),
+ HIERARCHY_SELECTED_MODULE_IS_BASE("selected-module-is-base"),
+ HIERARCHY_SELECTED_MODULE_ARTIFACT_NAME("selected-module-artifact-name"),
+ HIERARCHY_SELECTED_MODULE_ARTIFACT_UUID("selected-module-artifact-uuid"),
+ HIERARCHY_SELECTED_MODULE_ARTIFACT_VERSION("selected-module-artifact-version");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private TabsBar(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum UpdateNamePopover {
+ OPEN_POPOVER_ICON("edit-name-popover-icon"),
+ POPOVER_FORM("popover-form"),
+ POPOVER_SAVE_BUTTON("popover-save-button"),
+ POPOVER_INSTANCE_NAME("popover-vfinstance-name"),
+ POPOVER_HEAT_NAME("popover-heat-name"),
+ POPOVER_MODULE_NAME("popover-module-name"),
+ POPOVER_CLOSE_BUTTON("popover-close-button"),
+ POPOVER_X_BUTTON("popover-x-button");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private UpdateNamePopover(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum LinkMenuItems {
+ CANCEL_BUTTON("link-menu-button-cancel"),
+ CONNECT_BUTTON("link-menu-button-connect"),
+ LINK_ITEM_CAP("link-item-capabilities"),
+ LINK_ITEM_REQ("link-item-requirements"),
+ LINK_MENU("link-menu-open");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LinkMenuItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum GeneralCanvasItems {
+ CANVAS("canvas"),
+ CANVAS_RIGHT_PANEL("w-sdc-designer-sidebar-head"),
+ DELETE_INSTANCE_BUTTON("e-sdc-small-icon-delete");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private GeneralCanvasItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum AttributesSection {
+ TABLE_ROWS("attributes-table-row"),
+ EDIT_BUTTON_FOR_NETWORK_ATTR("edit_networks"),
+ DELETE_BUTTON_FOR_NETWORK_ATTR("delete_networks"),
+ ADD_BUTTON("add-attribute-button");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private AttributesSection(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum AttributeForm {
+ NAME_FIELD("attributeName"),
+ DESCRIPTION_FIELD("description"),
+ TYPE_FIELD("type-field"),
+ DEFAULT_VAL_FIELD("defaultvalue"),
+ BOOL_DEFAULT_VAL_FIELD("booleantype"),
+ SCHEMA_FIELD("schema"),
+ BOOL_VALUE_FIELD("boolean-type-value"),
+ HIDDEN_FIELD("hidden"),
+ UPDATE_BUTTON("Update"),
+ DONE_BUTTON("Done"),
+ ADD_BUTTON("Add");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private AttributeForm(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum PropertiesSection {
+ ADD_BUTTON("addGrey"),;
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private PropertiesSection(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum PropertyForm {
+ FORM_CONTAINER("sdc-edit-property-container"),
+ NAME_FIELD("propertyName"),
+ DESCRIPTION_FIELD("description"),
+ TYPE_FIELD("propertyType"),
+ SCHEMA_FIELD("schema-type"),
+ SIMPLE_TYPE_DEFAULT_VAL_FIELD("defaultvalue"),
+ SIMPLE_TYPE_BOOL_DEFAULT_VAL_FIELD("booleantype"),
+ LIST_TYPE_DEFAULT_VAL_FIELD("listNewItem-1"),
+ MAP_TYPE_DEFAULT_VAL_KEY_FIELD_FOR_FIRST_ITEM("mapKey-10"),
+ MAP_TYPE_DEFAULT_VAL_VALUE_FIELD_FOR_FIRST_ITEM("mapValue-10"),
+ MAP_TYPE_DEFAULT_VAL_KEY_FIELD_FOR_SECOND_ITEM("mapKey-11"),
+ MAP_TYPE_DEFAULT_VAL_VALUE_FIELD_FOR_SECOND_ITEM("mapValue-11"),
+ ADD_ITEM_TO_LIST_BUTTON("add-list-item-1"),
+ ADD_ITEM_TO_MAP_BUTTON("add-map-item"),
+ DELETE_FIRST_ITEM_FROM_MAP_BUTTON("delete-map-item-10"),
+ START_PORT_FIELD_FOR_PORT_PAIRS_DT("-1start_port"),
+ SAVE_BUTTON("Save");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private PropertyForm(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum GeneralSection {
+ BROWSE_BUTTON("browseButton"), FILE_NAME("filename"), NAME("name"), LOADER("tlv-loader");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private GeneralSection(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum ReqAndCapabilitiesSection {
+ SEARCH_BOX("search-box"), CAP_TAB("cap-tab"), REQ_TAB("req-tab");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ReqAndCapabilitiesSection(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum OnBoardingTable {
+ OPEN_MODAL_BUTTON("repository-icon"),
+ VENDOR_HEADER_COL("Vendor"),
+ NAME_HEADER_COL("Name"),
+ CATEGORY_HEADER_COL("Category"),
+ VERSION_HEADER_COL("Version"),
+ IMPORT_ICON("import-csar"),
+ UPDATE_ICON("update-csar"),
+ CSAR_ROW("csar-row"),
+ ONBOARDING_SEARCH("onboarding-search");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private OnBoardingTable(String value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/MenuOptionsEnum.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/MenuOptionsEnum.java
new file mode 100644
index 0000000000..4c45858de0
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/MenuOptionsEnum.java
@@ -0,0 +1,17 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+public enum MenuOptionsEnum {
+
+ EDIT("Edit"), CHECK_IN("Check in"), CHECK_OUT("Check out"), VIEW("View"), SUBMIT_FOR_TEST("Submit For Test"), ACCEPT("Accept"), REJECT("Reject"), START_TEST("Start test"), DISTREBUTE("Distribute");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private MenuOptionsEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/UserCredentials.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/UserCredentials.java
new file mode 100644
index 0000000000..0d94529b89
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/datatypes/UserCredentials.java
@@ -0,0 +1,29 @@
+package org.openecomp.sdc.uici.tests.datatypes;
+
+import org.openecomp.sdc.be.model.User;
+
+public class UserCredentials extends User {
+
+ private String password;
+
+ public UserCredentials(String userId, String password, String firstname, String lastname) {
+ super();
+ setUserId(userId);
+ this.password = password;
+ setFirstName(firstname);
+ setLastName(lastname);
+ }
+
+ public UserCredentials() {
+ super();
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/base/SetupCDTest.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/base/SetupCDTest.java
new file mode 100644
index 0000000000..42e9f137ec
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/base/SetupCDTest.java
@@ -0,0 +1,408 @@
+package org.openecomp.sdc.uici.tests.execute.base;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.log4j.Logger;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.rules.TestName;
+import org.openecomp.sdc.uici.tests.datatypes.CleanTypeEnum;
+import org.openecomp.sdc.uici.tests.datatypes.UserCredentials;
+import org.openecomp.sdc.uici.tests.utilities.FileHandling;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Optional;
+import org.testng.annotations.Parameters;
+
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.api.ComponentBaseTest;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.run.StartTest;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+import com.google.common.collect.Lists;
+import com.thinkaurelius.titan.core.TitanGraph;
+
+public abstract class SetupCDTest extends ComponentBaseTest {
+
+ private TitanSnapshot snapshot;
+ private static CleanTypeEnum cleanType;
+
+ public SetupCDTest() {
+ super(new TestName(), SetupCDTest.class.getName());
+ }
+
+ public SetupCDTest(TestName name, String className) {
+ super(name, className);
+ }
+
+ public static Logger logger = Logger.getLogger(SetupCDTest.class.getName());
+
+ /**************** CONSTANTS ****************/
+ private static final String CREDENTIALS_FILE = "src/main/resources/ci/conf/credentials.yaml";
+ public static final String SELENIUM_NODE_URL = "http://%s:%s/wd/hub";
+
+ /**************** PRIVATES ****************/
+ public static Config config;
+ private Map<?, ?> credentialsYamlFileMap;
+
+ private static String devUrl, cdUrl;
+
+ /****************
+ * BEFORE
+ *
+ * @throws FileNotFoundException
+ ****************/
+
+ @BeforeSuite(alwaysRun = true)
+ @Parameters({ "clean-type" })
+ public void setEnvParameters(@Optional("PARTIAL") String cleanType) throws FileNotFoundException {
+ this.cleanType = CleanTypeEnum.findByName(cleanType);
+ System.out.println("setup before class");
+ config = Utils.getConfig();
+ loadCredentialsFile();
+ setUrl();
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void setBrowserBeforeTest() {
+ setBrowserBeforeTest(getRole());
+ }
+
+ /**************** AFTER ****************/
+ @AfterMethod(alwaysRun = true)
+ public void quitAfterTest() {
+ System.out.println("closing browser");
+ GeneralUIUtils.getDriver().quit();
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void beforeState() throws Exception {
+ CleanTypeEnum cleanType = getCleanMode();
+ switch (cleanType) {
+ case FULL: {
+ super.beforeState(null);
+ break;
+ }
+ case PARTIAL: {
+ takeTitanSnapshot();
+ break;
+ }
+ case NONE: {
+ // No Clean Up
+ break;
+ }
+ default: {
+ throw new NotImplementedException("Enum Value:" + cleanType.name() + " Is not handled");
+ }
+ }
+
+ }
+
+ @AfterMethod(alwaysRun = true)
+ public void afterState() throws Exception {
+ CleanTypeEnum cleanType = getCleanMode();
+ switch (cleanType) {
+ case FULL: {
+ super.afterState(null);
+ break;
+ }
+ case PARTIAL: {
+ resetToOriginalSnapshot();
+ break;
+ }
+ case NONE: {
+ // No Clean Up
+ break;
+ }
+ default: {
+ throw new NotImplementedException("Enum Value:" + cleanType.name() + " Is not handled");
+ }
+ }
+
+ }
+
+ private void takeTitanSnapshot() {
+ List<Edge> edgeList = Lists.newArrayList(getTitanGraph().edges(null));
+ List<Vertex> verList = Lists.newArrayList(getTitanGraph().vertices(null));
+ setSnapshot(new TitanSnapshot(edgeList, verList));
+
+ }
+
+ private static class TitanSnapshot {
+ List<Edge> edges;
+ List<Vertex> vertices;
+
+ public List<Edge> getEdges() {
+ return edges;
+ }
+
+ public List<Vertex> getVertices() {
+ return vertices;
+ }
+
+ private TitanSnapshot(List<Edge> edges, List<Vertex> vertices) {
+ super();
+ this.edges = edges;
+ this.vertices = vertices;
+ }
+ }
+
+ private void resetToOriginalSnapshot() {
+
+ List<Edge> joinedEdges = new ArrayList<>();
+ List<Vertex> joinedVertices = new ArrayList<>();
+ TitanSnapshot original = getSnapshot();
+ takeTitanSnapshot();
+ TitanSnapshot current = getSnapshot();
+
+ original.getEdges().stream().forEach(e -> addIfIdInList(e, current.getEdges(), joinedEdges, e2 -> e2.id()));
+ original.getVertices().stream()
+ .forEach(e -> addIfIdInList(e, current.getVertices(), joinedVertices, e2 -> e2.id()));
+
+ List<Edge> edgesToRemove = removeFromList(current.getEdges(), joinedEdges, e2 -> e2.id());
+ List<Vertex> verticesToRemove = removeFromList(current.getVertices(), joinedVertices, e2 -> e2.id());
+
+ List<Edge> edgesToAdd = removeFromList(original.getEdges(), joinedEdges, e2 -> e2.id());
+ List<Vertex> verticesToAdd = removeFromList(original.getVertices(), joinedVertices, e2 -> e2.id());
+
+ if (edgesToAdd.isEmpty() && verticesToAdd.isEmpty()) {
+ edgesToRemove.stream().forEach(e -> e.remove());
+ verticesToRemove.stream().forEach(v -> v.remove());
+ }
+
+ }
+
+ private <Element, ID> List<Element> removeFromList(List<Element> listToRemoveFrom, List<Element> elementsToRemove,
+ Function<Element, ID> idGetter) {
+ Set<ID> idSet = new HashSet<>();
+ // Fill The Set
+ elementsToRemove.stream().map(e -> idGetter.apply(e)).forEach(e2 -> idSet.add(e2));
+ return listToRemoveFrom.stream().filter(p -> !idSet.contains(idGetter.apply(p))).collect(Collectors.toList());
+
+ }
+
+ private <Element, ID> void addIfIdInList(Element e, List<Element> listToCheck, List<Element> listToAddTo,
+ Function<Element, ID> idGetter) {
+ Stream<Element> matchingElements = listToCheck.stream()
+ .filter(p -> idGetter.apply(e).equals(idGetter.apply(p)));
+ listToAddTo.addAll(matchingElements.collect(Collectors.toList()));
+ }
+
+ /**************** MAIN ****************/
+ public static void main(String[] args) {
+ System.out.println("---------------------");
+ System.out.println("running test from CLI");
+ System.out.println("---------------------");
+ args = new String[] { "ui-ci.xml" };
+ StartTest.main(args);
+ }
+
+ /***********************************************************************************/
+
+ protected void setBrowserBeforeTest(UserRoleEnum role) {
+ System.out.println("setup before test");
+ GeneralUIUtils.initDriver();
+ setDevUrl(role);
+ loginWithUser(role);
+ }
+
+ protected void setUrl() {
+ cdUrl = config.getUrl();
+ setDevUrl(getRole());
+ }
+
+ private Map<String, String> loadCredentialsFile() {
+ final String credintialsFile = (System.getProperty("credentials.file") != null)
+ ? System.getProperty("credentials.file") : CREDENTIALS_FILE;
+ System.out.println("credentials file is : " + credintialsFile);
+ FunctionalInterfaces.swallowException(
+ () -> credentialsYamlFileMap = (Map<String, String>) FileHandling.parseYamlFile(credintialsFile));
+ System.out.println(credentialsYamlFileMap.toString());
+ return (Map<String, String>) credentialsYamlFileMap;
+ }
+
+ protected UserCredentials getUserCredentialsFromFile(String userRole) throws Exception {
+ Map<String, String> credentialsMap = (Map<String, String>) credentialsYamlFileMap.get(userRole);
+ String user = (String) credentialsMap.get("username");
+ String password = (String) credentialsMap.get("password");
+ String firstname = (String) credentialsMap.get("firstname");
+ String lastname = (String) credentialsMap.get("lastname");
+
+ return new UserCredentials(user, password, firstname, lastname);
+ }
+
+ public void navigateToUrl(String url) throws InterruptedException {
+ WebDriver driver = GeneralUIUtils.getDriver();
+ System.out.println("navigating to URL :" + url);
+ driver.navigate().to(url);
+ driver.manage().window().maximize();
+ driver.manage().deleteAllCookies();
+ }
+
+ protected void loginToSystem(UserCredentials credentials) throws Exception {
+
+ sendUserAndPasswordKeys(credentials);
+ WebElement submitButton = GeneralUIUtils.getDriver().findElement(By.name("btnSubmit"));
+ submitButton.click();
+ WebElement buttonOK = GeneralUIUtils.getDriver().findElement(By.name("successOK"));
+ AssertJUnit.assertTrue(buttonOK.isDisplayed());
+ buttonOK.click();
+ System.out.println("Entering to design studio");
+ Thread.sleep(2000);
+ WebElement enterToUserWorkspaceButton = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//button[@data-tests-id='Design Studio']"));
+ enterToUserWorkspaceButton.click();
+ }
+
+ private void sendUserAndPasswordKeys(UserCredentials userId) {
+ System.out.println("Login to system with user : " + userId.getUserId());
+ WebElement userNameTextbox = GeneralUIUtils.getDriver().findElement(By.name("userid"));
+ userNameTextbox.sendKeys(userId.getUserId());
+ WebElement passwordTextbox = GeneralUIUtils.getDriver().findElement(By.name("password"));
+ passwordTextbox.sendKeys(userId.getPassword());
+ }
+
+ public String getUrl() {
+ String url;
+ final CleanTypeEnum workMode = getCleanMode();
+ switch (workMode) {
+ case FULL: {
+ url = devUrl;
+ break;
+ }
+ case PARTIAL: {
+ url = devUrl;
+ break;
+ }
+ case NONE: {
+ url = cdUrl;
+ break;
+ }
+ default: {
+ throw new NotImplementedException(workMode.name());
+ }
+
+ }
+ return url;
+ }
+
+ public static void setDevUrl(UserRoleEnum role) {
+ String url = SetupCDTest.devUrl;
+ switch (role) {
+ case ADMIN: {
+ url = "http://localhost:8181/sdc1/proxy-admin1#/dashboard";
+ break;
+ }
+ case DESIGNER: {
+ url = "http://localhost:8181/sdc1/proxy-designer1#/dashboard";
+ // url = "http://localhost:9000/#/dashboard";
+ break;
+ }
+ case GOVERNOR: {
+ url = "http://localhost:8181/sdc1/proxy-governor1#/dashboard";
+ break;
+ }
+ case OPS: {
+ url = "http://localhost:8181/sdc1/proxy-ops1#/dashboard";
+ break;
+ }
+ case TESTER: {
+ url = "http://localhost:8181/sdc1/proxy-tester1#/dashboard";
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ SetupCDTest.devUrl = url;
+ }
+
+ public static Config getConfig() {
+ return config;
+ }
+
+ private User user;
+
+ public void loginWithUser(UserRoleEnum role) {
+
+ setUser(role);
+ String url = getUrl();
+ System.out.println("URL is : " + url);
+ try {
+ navigateToUrl(url);
+ if (url.contains("https://www.e-access")) {
+ System.out.println("going to update designer user to mechIDs form...");
+ UserCredentials credentials = getUserCredentialsFromFile(role.name().toLowerCase());
+ loginToSystem(credentials);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void setUser(UserRoleEnum role) {
+ user = new User();
+ user.setUserId(role.getUserId());
+ user.setFirstName(role.getFirstName());
+ user.setRole(role.name());
+ }
+
+ /**
+ * Current User Role
+ *
+ * @return
+ */
+ public UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+ /**
+ * To change clean type update configuration.<br>
+ * Do not override this method.
+ *
+ * @return
+ */
+ protected final CleanTypeEnum getCleanMode() {
+ return cleanType;
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ protected void quitAndReLogin(UserRoleEnum role) {
+ quitAfterTest();
+ setBrowserBeforeTest(role);
+ GeneralUIUtils.waitForLoader(30);
+ }
+
+ public TitanSnapshot getSnapshot() {
+ return snapshot;
+ }
+
+ public void setSnapshot(TitanSnapshot snapshot) {
+ this.snapshot = snapshot;
+ }
+
+ public static TitanGraph getTitanGraph() {
+ return titanGraph;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceBasicTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceBasicTests.java
new file mode 100644
index 0000000000..e42de862b7
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceBasicTests.java
@@ -0,0 +1,147 @@
+package org.openecomp.sdc.uici.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Arrays;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasElement;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasManager;
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.ArtifactUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.FileHandling;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.RestCDUtils;
+import org.openecomp.sdc.uici.tests.utilities.ServiceUIUtils;
+import org.openecomp.sdc.uici.tests.verificator.ServiceVerificator;
+import org.openecomp.sdc.uici.tests.verificator.VfVerificator;
+import org.openqa.selenium.WebElement;
+import org.testng.annotations.Test;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+
+public class ServiceBasicTests extends SetupCDTest {
+
+ @Test
+ public void testCreateService() {
+ ServiceReqDetails createServiceInUI = ServiceUIUtils.createServiceInUI(getUser());
+ ServiceVerificator.verifyServiceCreated(createServiceInUI, getUser());
+ }
+
+ @Test
+ public void testLinkTwoRI() {
+
+ // create 1st VF
+ ResourceReqDetails resourceOne = ResourceUIUtils.createResourceInUI(getUser());
+ assertTrue(RestCDUtils.getResource(resourceOne).getErrorCode() == HttpStatus.SC_OK);
+ // add LoadBalancer to resource
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ canvasManager.createElementOnCanvas(LeftPanelCanvasItems.OBJECT_STORAGE);
+ GeneralUIUtils.checkIn();
+
+ // create 2nd VF
+ ResourceReqDetails resourceTwo = ResourceUIUtils.createResourceInUI(getUser());
+ assertTrue(RestCDUtils.getResource(resourceTwo).getErrorCode() == HttpStatus.SC_OK);
+ // add ObjectStorage to resource
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ canvasManager = CanvasManager.getCanvasManager();
+ canvasManager.createElementOnCanvas(LeftPanelCanvasItems.LOAD_BALANCER);
+ GeneralUIUtils.checkIn();
+
+ // create service
+ ServiceReqDetails createServiceInUI = ServiceUIUtils.createServiceInUI(getUser());
+ // Verify Service is Created
+ ServiceVerificator.verifyServiceCreated(createServiceInUI, getUser());
+
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ canvasManager = CanvasManager.getCanvasManager();
+
+ // adding two resource instances
+ CanvasElement vfOne = canvasManager.createElementOnCanvas(resourceOne.getName());
+
+ CanvasElement vfTwo = canvasManager.createElementOnCanvas(resourceTwo.getName());
+ // link elements
+ canvasManager.linkElements(vfOne, vfTwo);
+
+ // check results
+ ServiceVerificator.verifyServiceCreated(createServiceInUI, getUser());
+ ServiceVerificator.verifyLinkCreated(createServiceInUI, getUser());
+
+ }
+
+ /**
+ * This method tests the following: <br>
+ * 1. Import of VF <br>
+ * 2. Certification Of Vf <br>
+ * 3. Adding deployment artifact to VF <br>
+ * 4. Creation of Service <br>
+ * 5. Adding Vf instance to Service <br>
+ * 6. Service Certification <br>
+ * 7. Approving Service to distribution by Governor <br>
+ * 8. Making sure service is ready to distribute by ops <br>
+ */
+ @Test
+ public void testBuildServiceForDistribution() {
+ ResourceReqDetails importedVf = ResourceUIUtils.importVfInUI(getUser(), FileHandling.getResourcesFilesPath(),
+ "valid_vf.csar");
+ GeneralUIUtils.waitForLoader(20);
+ // Verify Import
+ VfVerificator.verifyResourceIsCreated(importedVf);
+
+ // Create Deployment Artifact
+ ArtifactUIUtils.createDeploymentArtifactOnVf(FileHandling.getResourcesFilesPath() + "myYang.xml",
+ ArtifactTypeEnum.YANG_XML);
+ VfVerificator.verifyResourceContainsDeploymentArtifacts(importedVf,
+ Arrays.asList(new ArtifactTypeEnum[] { ArtifactTypeEnum.YANG_XML }));
+
+ // Submit For Testing Process VF
+ GeneralUIUtils.submitForTestingElement(importedVf.getName());
+
+ // Certify The VF
+ quitAndReLogin(UserRoleEnum.TESTER);
+ ResourceUIUtils.testAndAcceptElement(importedVf);
+
+ // Verify Certification
+ GeneralUIUtils.waitForLoader();
+ VfVerificator.verifyResourceIsCertified(importedVf);
+
+ // Create Service
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+ ServiceReqDetails createServiceInUI = ServiceUIUtils.createServiceInUI(getUser());
+ ServiceVerificator.verifyServiceCreated(createServiceInUI, getUser());
+
+ // Drag the VF To the Service
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ canvasManager.createElementOnCanvas(importedVf.getName());
+
+ // Submit For Testing Process Service
+ GeneralUIUtils.submitForTestingElement(null);
+
+ // Certify The Service
+ quitAndReLogin(UserRoleEnum.TESTER);
+ ResourceUIUtils.testAndAcceptElement(createServiceInUI);
+ ServiceVerificator.verifyServiceCertified(createServiceInUI, getUser());
+
+ // Approve with governor
+ quitAndReLogin(UserRoleEnum.GOVERNOR);
+ ServiceUIUtils.approveServiceForDistribution(createServiceInUI);
+
+ // Log in with Ops and verify that can distribute
+ quitAndReLogin(UserRoleEnum.OPS);
+ GeneralUIUtils.getWebElementWaitForVisible(createServiceInUI.getName()).click();
+ WebElement distributeWebElement = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.DISTRIBUTE.getValue());
+ assertTrue(distributeWebElement != null);
+
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceInputsTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceInputsTests.java
new file mode 100644
index 0000000000..b3e8e023ca
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/service/ServiceInputsTests.java
@@ -0,0 +1,124 @@
+package org.openecomp.sdc.uici.tests.execute.service;
+
+import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.assertFalse;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasElement;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasManager;
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.BreadcrumbsButtonsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.InputsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.ModalItems;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.FileHandling;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ServiceUIUtils;
+import org.openecomp.sdc.uici.tests.verificator.ServiceVerificator;
+import org.testng.annotations.Test;
+
+public class ServiceInputsTests extends SetupCDTest {
+
+ public String serviceName = "";
+
+ @Test
+ private void testSelectingInputAndAddingItToTheService() {
+ ServiceInputsTestsSetUp();
+
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertTrue(GeneralUIUtils.isElementPresent(InputsEnum.SERVICE_INPUT.getValue()));
+ }
+
+ @Test
+ private void testDeletingAnInputFromTheService() {
+ ServiceInputsTestsSetUp();
+
+ // clicking on the delete input button and accepting the delete
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.DELETE_INPUT.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(ModalItems.OK.getValue()).click();
+
+ assertFalse(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertFalse(GeneralUIUtils.isElementPresent(InputsEnum.SERVICE_INPUT.getValue()));
+ }
+
+ @Test
+ private void testCheckingInTheServiceAndButtonsAreDisabled() throws Exception {
+ ServiceInputsTestsSetUp();
+
+ // Checking in the service and accessing it again in the home
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.closeNotificatin();
+ GeneralUIUtils.findComponentAndClick(serviceName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.INPUTS);
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.VF_INSTANCE.getValue()).click();
+
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.SECOND_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.DELETE_INPUT.getValue()).getAttribute("class").contains("disabled"));
+ }
+
+ @Test
+ private void testInputsSanity() throws Exception {
+ ServiceInputsTestsSetUp();
+
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertTrue(GeneralUIUtils.isElementPresent(InputsEnum.SERVICE_INPUT.getValue()));
+
+ // clicking on the delete input button and accepting the delete
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.DELETE_INPUT.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(ModalItems.OK.getValue()).click();
+
+ assertFalse(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertFalse(GeneralUIUtils.isElementPresent(InputsEnum.SERVICE_INPUT.getValue()));
+
+ // adding the input to the service again
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.ADD_INPUTS_BUTTON.getValue()).click();
+
+ // Checking in the service and accessing it again in the home
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.closeNotificatin();
+ GeneralUIUtils.findComponentAndClick(serviceName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.INPUTS);
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.VF_INSTANCE.getValue()).click();
+
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible(InputsEnum.SECOND_INPUT_CHECKBOX.getValue()).getAttribute("class").contains("disabled"));
+ }
+
+ private void ServiceInputsTestsSetUp() {
+ // create vf
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "service_with_inputs.csar";
+ ResourceReqDetails importVfREsourceInUI = ResourceUIUtils.importVfInUIWithoutCheckin(getUser(), filePath, fileName);
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.closeNotificatin();
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.closeNotificatin();
+
+ // create service
+ ServiceReqDetails createServiceInUI = ServiceUIUtils.createServiceInUI(getUser());
+ ServiceVerificator.verifyServiceCreated(createServiceInUI, getUser());
+ serviceName = createServiceInUI.getName();
+
+ // go to composition
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+
+ // drag vf into canvas
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ CanvasElement canvasElement = canvasManager.createElementOnCanvas(importVfREsourceInUI.getName());
+ canvasManager.selectElementFromCanvas(canvasElement);
+ GeneralUIUtils.waitForLoader();
+
+ // moving to inputs view
+ GeneralUIUtils.getWebElementWaitForClickable(BreadcrumbsButtonsEnum.COMPONENT.getValue()).click();
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.INPUTS);
+
+ // adding the input to the service
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.VF_INSTANCE.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.FIRST_INPUT_CHECKBOX.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(InputsEnum.ADD_INPUTS_BUTTON.getValue()).click();
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfBasicTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfBasicTests.java
new file mode 100644
index 0000000000..918ac934c6
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfBasicTests.java
@@ -0,0 +1,234 @@
+package org.openecomp.sdc.uici.tests.execute.vf;
+
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.retryMethodOnException;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasElement;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasManager;
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.ArtifactUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.FileHandling;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.RestCDUtils;
+import org.openecomp.sdc.uici.tests.verificator.VfVerificator;
+import org.testng.annotations.Test;
+
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceRespJavaObject;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+
+public class VfBasicTests extends SetupCDTest {
+
+ @Test
+ public void testImportVfTableColumns() {
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.OPEN_MODAL_BUTTON.getValue()).click();
+
+ assertTrue(GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.VENDOR_HEADER_COL.getValue()) != null);
+ assertTrue(GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.NAME_HEADER_COL.getValue()) != null);
+ assertTrue(GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.CATEGORY_HEADER_COL.getValue()) != null);
+
+ assertTrue(GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.VERSION_HEADER_COL.getValue()) != null);
+ }
+
+ @Test
+ public void testUpdateVfCreatedFromCsar() throws Exception {
+ // create vf
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "Sample_CSAR.csar";
+ ResourceReqDetails importVfResourceInUI = ResourceUIUtils.importVfInUIWithoutCheckin(getUser(), filePath,
+ fileName);
+ // update csar
+ fileName = "Sample_CSAR2.csar";
+ ResourceUIUtils.updateVfCsar(filePath, fileName);
+ VfVerificator.verifyNumOfComponentInstances(importVfResourceInUI, 4);
+ }
+
+ @Test
+ public void testImportVf() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "Sample_CSAR.csar";
+ ResourceReqDetails importVfResourceInUI = ResourceUIUtils.importVfInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.waitForLoader();
+ assertTrue(RestCDUtils.getResource(importVfResourceInUI).getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ @Test
+ public void testCreateVf() {
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ assertTrue(RestCDUtils.getResource(createResourceInUI).getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ @Test
+ public void testDeleteInstanceFromCanvas() {
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+
+ canvasManager.createElementOnCanvas(LeftPanelCanvasItems.BLOCK_STORAGE);
+ CanvasElement computeElement = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.COMPUTE);
+ VfVerificator.verifyNumOfComponentInstances(createResourceInUI, 2);
+ canvasManager.deleteElementFromCanvas(computeElement);
+ VfVerificator.verifyNumOfComponentInstances(createResourceInUI, 1);
+
+ }
+
+ @Test
+ public void testUpdateInstanceAttributeValue() {
+ // creare vfc with attrs
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceReqDetails importVfcResourceInUI = ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.checkIn();
+ // create vf
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ // add vfc to canvas
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ CanvasElement canvasElement = canvasManager.createElementOnCanvas(importVfcResourceInUI.getName());
+ canvasManager.selectElementFromCanvas(canvasElement);
+ // edit value of vfc attr
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.RightBar.PROPERTIES_AND_ATTRIBUTES.getValue())
+ .click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.RightBar.MYATTR_ATTR_FROM_LIST.getValue()).click();
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.DEFAULT_VAL_FIELD.getValue())
+ .sendKeys("2");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.DONE_BUTTON.getValue()).click();
+ String newValue = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.RightBar.MYATTR_ATTR_VALUE_FROM_LIST.getValue()).getText();
+ assertEquals("2", newValue);
+ }
+
+ @Test(enabled = false)
+ public void testAddInfomratinalArtifact() throws Exception {
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.INFORMATION_ARTIFACT);
+
+ ArtifactReqDetails informationalArtifact = ElementFactory.getDefaultArtifact();
+ final String FILE_PATH = System.getProperty("user.dir") + "\\src\\main\\resources\\Files\\";
+ final String FILE_NAME = "Valid_tosca_Mycompute.yml";
+
+ ArtifactUIUtils.addInformationArtifact(informationalArtifact, FILE_PATH + FILE_NAME,
+ DataTestIdEnum.InformationalArtifatcs.FEATURES);
+ ArtifactUIUtils.addInformationArtifact(informationalArtifact, FILE_PATH + FILE_NAME,
+ DataTestIdEnum.InformationalArtifatcs.CAPACITY);
+
+ RestResponse getResourceResponse = RestCDUtils.getResource(createResourceInUI);
+ assertEquals("Did not succeed to get resource after create", HttpStatus.SC_OK,
+ getResourceResponse.getErrorCode().intValue());
+
+ Map<String, Map<String, Object>> artifactsListFromResponse = ArtifactUIUtils
+ .getArtifactsListFromResponse(getResourceResponse.getResponse(), "artifacts");
+ Map<String, Object> map = artifactsListFromResponse.get("Features");
+
+ assertTrue(artifactsListFromResponse.size() >= 2);
+
+ }
+
+ @Test
+ public void testVfCertification() throws IOException {
+ // Create VF
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ assertTrue(RestCDUtils.getResource(createResourceInUI).getErrorCode() == HttpStatus.SC_OK);
+
+ // Submit For Testing Process
+ GeneralUIUtils.submitForTestingElement(createResourceInUI.getName());
+
+ // Tester
+ quitAndReLogin(UserRoleEnum.TESTER);
+ ResourceUIUtils.testAndAcceptElement(createResourceInUI);
+
+ // Verification
+ GeneralUIUtils.waitForLoader();
+ VfVerificator.verifyResourceIsCertified(createResourceInUI);
+
+ }
+
+ @Test
+ public void testDeploymentArtifactForVFi() {
+ User user = getUser();
+ // create vf
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(user);
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.waitForLoader();
+ // create service
+ GeneralUIUtils.clickOnCreateEntityFromDashboard(DataTestIdEnum.Dashboard.BUTTON_ADD_SERVICE.getValue());
+ ResourceUIUtils.defineResourceName("serv");
+ GeneralUIUtils.defineDescription("description");
+ GeneralUIUtils.waitForLoader();
+ ResourceUIUtils.defineResourceCategory("Mobility", "selectGeneralCategory");
+ ResourceUIUtils.defineProjectCode("012345");
+ GeneralUIUtils.clickSaveButton();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ GeneralUIUtils.waitForLoader();
+ // add vf to canvas
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ CanvasElement canvasElement = canvasManager.createElementOnCanvas(createResourceInUI.getName());
+ canvasManager.selectElementFromCanvas(canvasElement);
+ GeneralUIUtils.waitForLoader();
+ // add artifact
+ GeneralUIUtils.getWebElementWaitForClickable(DataTestIdEnum.RightBar.DEPLOYMENT_ARTIFACTS.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(DataTestIdEnum.RightBar.ADD_ARTIFACT_BUTTON.getValue()).click();
+ String newArtifactLabel = "newArtifact";
+ ArtifactReqDetails details = new ArtifactReqDetails("new_atifact", "DCAE_INVENTORY_EVENT", "desc", "",
+ newArtifactLabel);
+ ResourceUIUtils.fillinDeploymentArtifactFormAndClickDone(details,
+ FileHandling.getResourcesFilesPath() + "yamlSample.yml");
+ assertTrue(GeneralUIUtils.isElementPresent("artifact_Display_Name-" + newArtifactLabel));
+ // edit artifact
+ GeneralUIUtils.getWebElementWaitForClickable("artifact_Display_Name-" + newArtifactLabel).click();
+ String newFileName = "yamlSample2.yml";
+ retryMethodOnException(
+ () -> GeneralUIUtils.getWebElementByDataTestId(DataTestIdEnum.GeneralSection.BROWSE_BUTTON.getValue())
+ .sendKeys(FileHandling.getResourcesFilesPath() + newFileName));
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.DONE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ assertEquals(newFileName,
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.RightBar.ARTIFACT_NAME.getValue()).getText());
+ // delete artifact
+ GeneralUIUtils.moveToHTMLElementByDataTestId("artifact_Display_Name-" + newArtifactLabel);
+ GeneralUIUtils.getWebElementWaitForClickable(DataTestIdEnum.RightBar.DELETE_ARTIFACT_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ assertTrue(!GeneralUIUtils.isElementPresent("artifact_Display_Name-" + newArtifactLabel));
+ }
+
+ protected ArtifactReqDetails defineInformationalArtifact() throws IOException, Exception {
+ return ElementFactory.getDefaultArtifact();
+ }
+
+ protected ResourceRespJavaObject buildResourceJavaObject(ResourceReqDetails resource, RestResponse restResponse,
+ User user) {
+ ResourceRespJavaObject resourceObject = new ResourceRespJavaObject();
+ resourceObject = Convertor.constructFieldsForRespValidation(resource, resource.getVersion(), user);
+ resourceObject.setLifecycleState((LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT).toString());
+ resourceObject.setAbstractt("false");
+ resourceObject.setIcon(resource.getIcon().replace(" ", ""));
+ resourceObject.setUniqueId(ResponseParser.getUniqueIdFromResponse(restResponse));
+ return resourceObject;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfCanvasTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfCanvasTests.java
new file mode 100644
index 0000000000..73b09666ae
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfCanvasTests.java
@@ -0,0 +1,80 @@
+package org.openecomp.sdc.uici.tests.execute.vf;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasElement;
+import org.openecomp.sdc.uici.tests.datatypes.CanvasManager;
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.verificator.VfVerificator;
+import org.testng.annotations.Test;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+
+public class VfCanvasTests extends SetupCDTest {
+
+ @Test
+ public void testCanvasDrag() {
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ CanvasElement createElementOnCanvas = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.BLOCK_STORAGE);
+
+ ImmutablePair<String, String> preMovePos = ResourceUIUtils.getRIPosition(createResourceInUI, getUser());
+
+ canvasManager.moveElementOnCanvas(createElementOnCanvas);
+
+ VfVerificator.verifyRILocationChanged(createResourceInUI, preMovePos, getUser());
+
+ }
+
+ @Test
+ public void testCanvasConnectComponents() {
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ CanvasElement bsElement = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.BLOCK_STORAGE);
+ CanvasElement computeElement = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.COMPUTE);
+
+ canvasManager.linkElements(bsElement, computeElement);
+
+ VfVerificator.verifyLinkCreated(createResourceInUI);
+
+ }
+
+ @Test
+ public void testCanvasVFSanity() {
+ ResourceReqDetails createResourceInUI = ResourceUIUtils.createResourceInUI(getUser());
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.COMPOSITION);
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+
+ CanvasElement bsElement = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.BLOCK_STORAGE);
+ CanvasElement compElement = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.COMPUTE);
+
+ ImmutablePair<String, String> preMovePos = ResourceUIUtils.getRIPosition(createResourceInUI, getUser());
+ canvasManager.moveElementOnCanvas(bsElement);
+ canvasManager.moveElementOnCanvas(compElement);
+
+ VfVerificator.verifyRILocationChanged(createResourceInUI, preMovePos, getUser());
+
+ CanvasElement bsElement2 = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.BLOCK_STORAGE);
+
+ canvasManager.linkElements(bsElement2, compElement);
+
+ VfVerificator.verifyLinkCreated(createResourceInUI);
+
+ VfVerificator.verifyNumOfComponentInstances(createResourceInUI, 3);
+
+ canvasManager.moveElementOnCanvas(compElement);
+
+ canvasManager.deleteElementFromCanvas(bsElement);
+
+ VfVerificator.verifyNumOfComponentInstances(createResourceInUI, 2);
+
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfDeploymentTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfDeploymentTests.java
new file mode 100644
index 0000000000..47344b7c68
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfDeploymentTests.java
@@ -0,0 +1,340 @@
+package org.openecomp.sdc.uici.tests.execute.vf;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.FileHandling;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.RestCDUtils;
+import org.openqa.selenium.WebElement;
+import org.testng.annotations.Test;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+
+public class VfDeploymentTests extends SetupCDTest {
+
+ // *****************************EditNamePopoverTests*****************************//
+ @Test
+ public void ClickingOnEditNamePopoverIconShouldOpenTheEditNamePopoverForm() {
+ EditNamePopoverTestsSetUp();
+
+ assertTrue(GeneralUIUtils.isElementPresent(DataTestIdEnum.UpdateNamePopover.POPOVER_FORM.getValue()));
+ }
+
+ @Test
+ public void ModuleDataShouldBeDisplayedInTheEditNameForm() {
+ EditNamePopoverTestsSetUp();
+
+ WebElement instanceName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_INSTANCE_NAME.getValue());
+ WebElement heatName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_HEAT_NAME.getValue());
+ WebElement moduleName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_MODULE_NAME.getValue());
+
+ String moduleNameToDivide = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue()).getText();
+
+ String[] dividedModuleName = moduleNameToDivide.split(Pattern.quote(".."));
+
+ assertEquals(dividedModuleName[0], instanceName.getText());
+ assertEquals(dividedModuleName[1], heatName.getAttribute("value"));
+ assertEquals(dividedModuleName[2], moduleName.getText());
+
+ }
+
+ @Test
+ public void CloseButtonShouldCloseThePopover() {
+ EditNamePopoverTestsSetUp();
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_CLOSE_BUTTON.getValue())
+ .click();
+
+ assertFalse(GeneralUIUtils.isElementPresent(DataTestIdEnum.UpdateNamePopover.POPOVER_FORM.getValue()));
+ }
+
+ @Test
+ public void XButtonShouldCloseThePopover() {
+ EditNamePopoverTestsSetUp();
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_X_BUTTON.getValue())
+ .click();
+
+ assertFalse(GeneralUIUtils.isElementPresent(DataTestIdEnum.UpdateNamePopover.POPOVER_FORM.getValue()));
+ }
+
+ @Test
+ public void SaveButtonShouldBeDisabledWhileTheNameHasNotBeenChanged() {
+ EditNamePopoverTestsSetUp();
+
+ WebElement popoverSaveButton = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_SAVE_BUTTON.getValue());
+
+ assertTrue(popoverSaveButton.getAttribute("class").contains("disabled"));
+ }
+
+ @Test
+ public void ClickingOnTheSaveButtonShouldUpdateTheModuleName() {
+ EditNamePopoverTestsSetUp();
+
+ String newName = "testName";
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_HEAT_NAME.getValue())
+ .clear();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_HEAT_NAME.getValue())
+ .sendKeys(newName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_SAVE_BUTTON.getValue())
+ .click();
+
+ GeneralUIUtils.waitForLoader();
+
+ String moduleName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue()).getText();
+
+ String[] dividedModuleName = moduleName.split(Pattern.quote(".."));
+
+ assertEquals(dividedModuleName[1], newName);
+ }
+
+ @Test
+ public void testUpdateModuleNameSanity() {
+ EditNamePopoverTestsSetUp();
+
+ String newName = "testName";
+
+ WebElement instanceName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_INSTANCE_NAME.getValue());
+ WebElement heatName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_HEAT_NAME.getValue());
+ WebElement moduleName = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_MODULE_NAME.getValue());
+
+ String moduleNameToDivide = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue()).getText();
+
+ String[] dividedModuleName = moduleNameToDivide.split(Pattern.quote(".."));
+
+ assertEquals(dividedModuleName[0], instanceName.getText());
+ assertEquals(dividedModuleName[1], heatName.getAttribute("value"));
+ assertEquals(dividedModuleName[2], moduleName.getText());
+
+ WebElement popoverSaveButton = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.POPOVER_SAVE_BUTTON.getValue());
+
+ assertTrue(popoverSaveButton.getAttribute("class").contains("disabled"));
+
+ heatName.clear();
+ heatName.sendKeys(newName);
+
+ popoverSaveButton.click();
+
+ GeneralUIUtils.waitForLoader();
+
+ moduleNameToDivide = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue()).getText();
+ dividedModuleName = moduleNameToDivide.split(Pattern.quote(".."));
+
+ assertEquals(dividedModuleName[1], newName);
+ }
+
+ // *****************************DeploymentTabsTests*****************************//
+
+ @Test
+ public void testTabIsBeingDisplayedAtDeploymentView() {
+ DeploymentTestsSetUp();
+
+ assertTrue(GeneralUIUtils.isElementPresent(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()));
+ }
+
+ @Test
+ public void testClickingOnTabSetsItAsSelected() {
+ DeploymentTestsSetUp();
+
+ WebElement hierarchyTab = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue());
+ hierarchyTab.click();
+
+ assertTrue(hierarchyTab.getAttribute("class").contains("selected"));
+ }
+
+ @Test
+ public void testTabNameIsBeingDisplayedInTheSelectedTabHeader() {
+ DeploymentTestsSetUp();
+
+ // select the hierarchy tab and check the header
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ WebElement tabHeader = GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.TAB_HEADER.getValue());
+
+ assertEquals(tabHeader.getText(), "HIERARCHY");
+ }
+
+ @Test
+ public void testSelectingModuleNameInTheHierarchyTabShouldSelectIt() {
+ DeploymentTestsSetUp();
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ WebElement hierarchyModule = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE_TITLE.getValue());
+
+ hierarchyModule.click();
+
+ assertTrue(hierarchyModule.getAttribute("class").contains("selected"));
+ }
+
+ @Test
+ public void testSelectingModuleNameInTheHierarchyTabShouldExpandIt() {
+ DeploymentTestsSetUp();
+
+ // select hierarchy tab
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ WebElement hierarchyModule = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue());
+
+ hierarchyModule.click();
+
+ assertTrue(hierarchyModule.getAttribute("class").contains("expanded"));
+ }
+
+ @Test
+ public void testSelectingModuleNameInTheHierarchyTabShouldDisplayItsData() {
+ DeploymentTestsSetUp();
+
+ // select hierarchy tab
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue()).click();
+
+ assertTrue(GeneralUIUtils.isElementPresent(DataTestIdEnum.TabsBar.HIERARCHY_SELECTED_MODULE_DATA.getValue()));
+ }
+
+ @Test(enabled = false)
+ public void testResourceNameIsBeingDisplayedInTheSelectedTabSubHeader() {
+ DeploymentTestsSetUp();
+
+ // select the hierarchy tab and check the header
+ // WebElement tabSubHeader =
+ // getWebElement(DataTestIdEnum.TabsBar.TAB_SUB_HEADER.getValue());
+
+ // assertEquals(tabSubHeader.getText(), vmmcCsar.getName());
+ }
+
+ @Test(enabled = false)
+ public void testSelectingModuleNameInTheHierarchyTabShouldDisplayItsInformation() throws IOException {
+ DeploymentTestsSetUp();
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ WebElement hierarchyModule = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue());
+
+ // get the module
+ // String component = RestCDUtils.getResource(vmmcCsar).getResponse();
+ // TODO idana fix test
+ /*
+ * GroupDefinitionInfo module = getModuleById(component,
+ * hierarchyModule.getText());
+ *
+ * hierarchyModule.click();
+ *
+ * assertModuleDetails(module, hierarchyModule);
+ */
+
+ }
+
+ @Test(enabled = false)
+ public void testSelectingModuleNameInTheHierarchyTabShouldDisplayItsArtifacts() throws IOException {
+ DeploymentTestsSetUp();
+
+ GeneralUIUtils.getWebElementWaitForClickable(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ WebElement hierarchyModule = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue());
+
+ // TODO idana fix test
+ // Get the artifact from the module
+ /*
+ * String component = RestCDUtils.getResource(vmmcCsar,
+ * getUser()).getResponse(); GroupDefinitionInfo module =
+ * getModuleById(component, hierarchyModule.getText());
+ * ArtifactDefinitionInfo artifact = module.getArtifacts().get(0);
+ *
+ * hierarchyModule.click();
+ *
+ * assertModuleArtifactDetails(artifact);
+ */
+ }
+
+ @Test
+ public void testTabsViewSanity() throws IOException {
+ DeploymentTestsSetUp();
+
+ WebElement hierarchyTab = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue());
+
+ assertTrue(hierarchyTab != null);
+
+ hierarchyTab.click();
+
+ assertTrue(hierarchyTab.getAttribute("class").contains("selected"));
+
+ WebElement tabHeader = GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.TAB_HEADER.getValue());
+ WebElement tabSubHeader = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.TAB_SUB_HEADER.getValue());
+
+ assertEquals(tabHeader.getText(), "HIERARCHY");
+ // assertEquals(tabSubHeader.getText(), vmmcCsar.getName());
+
+ WebElement hierarchyModule = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue());
+ WebElement hierarchyModuleTitle = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE_TITLE.getValue());
+ hierarchyModule.click();
+ WebElement selectedModuleData = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_SELECTED_MODULE_DATA.getValue());
+
+ assertTrue(hierarchyModuleTitle.getAttribute("class").contains("selected"));
+ assertTrue(hierarchyModule.getAttribute("class").contains("expanded"));
+ assertTrue(selectedModuleData.getAttribute("ng-if") != null);
+ // TODO idana fix test
+ /*
+ * String component = RestCDUtils.getResource(vmmcCsar,
+ * getUser()).getResponse(); GroupDefinitionInfo module =
+ * getModuleById(component, hierarchyModule.getText());
+ * ArtifactDefinitionInfo artifact = module.getArtifacts().get(0);
+ *
+ * assertModuleDetails(module, hierarchyModule);
+ *
+ * assertModuleArtifactDetails(artifact);
+ */
+
+ }
+
+ // ************************DeploymentTestsSetUpFunction************************//
+
+ private void EditNamePopoverTestsSetUp() {
+ DeploymentTestsSetUp();
+
+ // clicking on a module and opening the edit name popover
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_TAB.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.TabsBar.HIERARCHY_MODULE.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.UpdateNamePopover.OPEN_POPOVER_ICON.getValue())
+ .click();
+ }
+
+ private void DeploymentTestsSetUp() {
+ // import csar
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "vf_with_groups.csar";
+ ResourceUIUtils.importVfInUI(getUser(), filePath, fileName);
+
+ GeneralUIUtils.waitForLoader(20);
+
+ // moving to deployment view
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.DEPLOYMENT);
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfOnboardingTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfOnboardingTests.java
new file mode 100644
index 0000000000..a81d854630
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vf/VfOnboardingTests.java
@@ -0,0 +1,63 @@
+package org.openecomp.sdc.uici.tests.execute.vf;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.OnboardUtility;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.testng.annotations.Test;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import com.google.gson.GsonBuilder;
+
+public class VfOnboardingTests extends SetupCDTest {
+
+ @Test
+ public void testUpdateVfCreatedOnBoarding() {
+ // create vf
+ ResourceReqDetails importVfResourceInUI = ResourceUIUtils.importVfFromOnBoardingModalWithoutCheckin(getUser(),
+ "mock_vf");
+ // update vf
+ ResourceUIUtils.updateVfCsarFromOnBoarding();
+ }
+
+ @Test
+ public void createVfsFromOnboarding() throws IOException {
+ String folderPath = "C:\\onboardingTest\\onBoardingZips";
+ File folder = new File(folderPath);
+ File[] listOfFiles = folder.listFiles();
+ List<String> zipFileNames = Arrays.asList(listOfFiles).stream().map(file -> file.getName())
+ .filter(fileName -> fileName.endsWith(".zip")).collect(Collectors.toList());
+ Map<String, String> filesSuccessMap = new HashMap<>();
+ for (String fileName : zipFileNames) {
+ try {
+ createSingleVfFromOnboarding(folderPath, fileName);
+ filesSuccessMap.put(fileName, "SUCCESS");
+ } catch (Exception e) {
+ filesSuccessMap.put(fileName, "FAIL");
+ }
+ }
+ Path file = Paths.get("RunResults.txt");
+ String stringDataModel = new GsonBuilder().setPrettyPrinting().create().toJson(filesSuccessMap);
+ Files.write(file, stringDataModel.getBytes());
+ }
+
+ private static void createSingleVfFromOnboarding(String filePath, String zipFileName) {
+ String userId = UserRoleEnum.DESIGNER.getUserId();
+ OnboardUtility.createVfFromOnboarding(userId, zipFileName, filePath);
+ GeneralUIUtils.submitForTestingElement("Vf From Onboarding");
+
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vfc/VfcBasicTests.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vfc/VfcBasicTests.java
new file mode 100644
index 0000000000..ef21e40c61
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/execute/vfc/VfcBasicTests.java
@@ -0,0 +1,219 @@
+package org.openecomp.sdc.uici.tests.execute.vfc;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.http.HttpStatus;
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openecomp.sdc.uici.tests.utilities.FileHandling;
+import org.openecomp.sdc.uici.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.RestCDUtils;
+import org.openecomp.sdc.uici.tests.verificator.VfVerificator;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+import org.testng.annotations.Test;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+public class VfcBasicTests extends SetupCDTest {
+
+ @Test
+ public void testRequirementsAndCapabilitiesSectionOfVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "mycompute.yml";
+ ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.REQUIREMENTS_AND_CAPABILITIES);
+ // all expected requirements
+ assertTrue("Not all expected requirements are displayed.", GeneralUIUtils.isElementPresent("dependency") && GeneralUIUtils.isElementPresent("local_storage"));
+ // filter requirements
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ReqAndCapabilitiesSection.SEARCH_BOX.getValue()).sendKeys("root");
+ Supplier<Boolean> supplier = () -> !GeneralUIUtils.isElementPresent("local_storage");
+ Function<Boolean, Boolean> resultVerifier = isNotPresent -> isNotPresent;
+ Boolean isFilteredRowNotPresent = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The new property was not inserted to the properties table.", isFilteredRowNotPresent);
+ assertTrue("Filter problem.", GeneralUIUtils.isElementPresent("dependency") && isFilteredRowNotPresent);
+ // move to cap tab
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ReqAndCapabilitiesSection.CAP_TAB.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible("endpoint").click();
+ GeneralUIUtils.getWebElementWaitForVisible("initiator").click();
+ supplier = () -> GeneralUIUtils.isElementPresent(DataTestIdEnum.PropertyForm.FORM_CONTAINER.getValue());
+ resultVerifier = isPresent -> isPresent;
+ Boolean isPopupOpen = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The update property popup was not opened.", isPopupOpen);
+ }
+
+ @Test
+ public void testCreatePropertyTypeListForVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.PROPERTIES);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertiesSection.ADD_BUTTON.getValue()).click();
+ // fill in fields
+ String newPropName = "listProperty";
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.NAME_FIELD.getValue()).sendKeys(newPropName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.DESCRIPTION_FIELD.getValue()).sendKeys("desc");
+ Select typeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.TYPE_FIELD.getValue()));
+ typeField.selectByVisibleText("list");
+ Select schemaTypeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.SCHEMA_FIELD.getValue()));
+ schemaTypeField.selectByVisibleText("string");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.LIST_TYPE_DEFAULT_VAL_FIELD.getValue()).sendKeys("first");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.ADD_ITEM_TO_LIST_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.LIST_TYPE_DEFAULT_VAL_FIELD.getValue()).sendKeys("second");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.ADD_ITEM_TO_LIST_BUTTON.getValue()).click();
+ // save
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.SAVE_BUTTON.getValue()).click();
+ Supplier<Boolean> supplier = () -> GeneralUIUtils.isElementPresent(newPropName);
+ Function<Boolean, Boolean> resultVerifier = isPresent -> isPresent;
+ Boolean isPresent = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The new property was not inserted to the properties table.", isPresent);
+ }
+
+ @Test
+ public void testCreatePropertyTypeMapForVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.PROPERTIES);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertiesSection.ADD_BUTTON.getValue()).click();
+ // fill in fields
+ String newPropName = "mapProperty";
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.NAME_FIELD.getValue()).sendKeys(newPropName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.DESCRIPTION_FIELD.getValue()).sendKeys("desc");
+ Select typeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.TYPE_FIELD.getValue()));
+ typeField.selectByVisibleText("map");
+ Select schemaTypeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.SCHEMA_FIELD.getValue()));
+ schemaTypeField.selectByVisibleText("string");
+ // insert item to map
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.MAP_TYPE_DEFAULT_VAL_KEY_FIELD_FOR_FIRST_ITEM.getValue()).sendKeys("key1");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.MAP_TYPE_DEFAULT_VAL_VALUE_FIELD_FOR_FIRST_ITEM.getValue()).sendKeys("val1");
+ // insert item to map
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.ADD_ITEM_TO_MAP_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.MAP_TYPE_DEFAULT_VAL_KEY_FIELD_FOR_SECOND_ITEM.getValue()).sendKeys("key2");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.MAP_TYPE_DEFAULT_VAL_VALUE_FIELD_FOR_SECOND_ITEM.getValue()).sendKeys("val2");
+ // delete item from map
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.DELETE_FIRST_ITEM_FROM_MAP_BUTTON.getValue()).click();
+ // save
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.SAVE_BUTTON.getValue()).click();
+ Supplier<Boolean> supplier = () -> GeneralUIUtils.isElementPresent(newPropName);
+ Function<Boolean, Boolean> resultVerifier = isPresent -> isPresent;
+ Boolean isPresent = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The new property was not inserted to the properties table.", isPresent);
+ }
+
+ @Test
+ public void testCreatePropertyTypeDTForVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.PROPERTIES);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertiesSection.ADD_BUTTON.getValue()).click();
+ // fill in fields
+ String newPropName = "dt";
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.NAME_FIELD.getValue()).sendKeys(newPropName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.DESCRIPTION_FIELD.getValue()).sendKeys("desc");
+ Select typeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.TYPE_FIELD.getValue()));
+ typeField.selectByValue("org.openecomp.datatypes.heat.contrail.network.rule.PortPairs");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.START_PORT_FIELD_FOR_PORT_PAIRS_DT.getValue()).sendKeys("first");
+ // save
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.PropertyForm.SAVE_BUTTON.getValue()).click();
+ Supplier<Boolean> supplier = () -> GeneralUIUtils.isElementPresent(newPropName);
+ Function<Boolean, Boolean> resultVerifier = isPresent -> isPresent;
+ Boolean isPresent = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The new property was not inserted to the properties table.", isPresent);
+ }
+
+ @Test
+ public void testViewAttributesTabForVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceReqDetails importVfcResourceInUI = ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.ATTRIBUTES);
+
+ List<WebElement> attributesRows = GeneralUIUtils.getWebElementsListWaitForVisible(DataTestIdEnum.AttributesSection.TABLE_ROWS.getValue());
+ assertTrue("There is not any row in the table.", !CollectionUtils.isEmpty(attributesRows));
+ // display editable buttons
+ assertTrue("The Add button is not dispaly.", GeneralUIUtils.isElementPresent(DataTestIdEnum.AttributesSection.ADD_BUTTON.getValue()));
+ assertTrue("The Edit button is not dispaly for 'network' attribute.", GeneralUIUtils.isElementPresent(DataTestIdEnum.AttributesSection.EDIT_BUTTON_FOR_NETWORK_ATTR.getValue()));
+ assertTrue("The Remove button is not dispaly for 'network' attribute.", GeneralUIUtils.isElementPresent(DataTestIdEnum.AttributesSection.DELETE_BUTTON_FOR_NETWORK_ATTR.getValue()));
+ // click checkin
+ GeneralUIUtils.checkIn();
+ // enter again
+ GeneralUIUtils.getWebElementWaitForVisible(importVfcResourceInUI.getName()).click();
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.ATTRIBUTES);
+ // the editable buttons disappear
+ assertTrue("The Add button is not dispaly.", !GeneralUIUtils.isElementPresent(DataTestIdEnum.AttributesSection.ADD_BUTTON.getValue()));
+ assertTrue("The Edit button is not dispaly for 'network' attribute.", !GeneralUIUtils.isElementPresent(DataTestIdEnum.AttributesSection.EDIT_BUTTON_FOR_NETWORK_ATTR.getValue()));
+ assertTrue("The Remove button is not dispaly for 'network' attribute.", !GeneralUIUtils.isElementPresent(DataTestIdEnum.AttributesSection.DELETE_BUTTON_FOR_NETWORK_ATTR.getValue()));
+ }
+
+ @Test
+ public void testCreateAttributeForVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceReqDetails importVfcResourceInUI = ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.ATTRIBUTES);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributesSection.ADD_BUTTON.getValue()).click();
+ // fill in fields
+ String newAttrName = "attr";
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.NAME_FIELD.getValue()).sendKeys(newAttrName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.DESCRIPTION_FIELD.getValue()).sendKeys("desc");
+ Select typeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.TYPE_FIELD.getValue()));
+ typeField.selectByVisibleText("integer");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.DEFAULT_VAL_FIELD.getValue()).sendKeys("2");
+ // click ok
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.DONE_BUTTON.getValue()).click();
+ Supplier<Boolean> supplier = () -> GeneralUIUtils.isElementPresent(newAttrName);
+ Function<Boolean, Boolean> resultVerifier = isPresent -> isPresent;
+ Boolean isPresent = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The new attribute was not inserted to the attributes table.", isPresent);
+ }
+
+ @Test
+ public void testUpdateTypeForAttributeOfVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceReqDetails importVfcResourceInUI = ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ VfVerificator.verifyResourceIsCreated(importVfcResourceInUI);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.ATTRIBUTES);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributesSection.EDIT_BUTTON_FOR_NETWORK_ATTR.getValue()).click();
+ // fill in fields
+ Select typeField = new Select(GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.TYPE_FIELD.getValue()));
+ typeField.selectByVisibleText("float");
+ // click ok
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributeForm.DONE_BUTTON.getValue()).click();
+ Supplier<Boolean> supplier = () -> GeneralUIUtils.isElementPresent("float");
+ Function<Boolean, Boolean> resultVerifier = isPresent -> isPresent;
+ Boolean isPresent = FunctionalInterfaces.retryMethodOnResult(supplier, resultVerifier);
+ assertTrue("The attribute type was not updated.", isPresent);
+ }
+
+ @Test
+ public void testDeleteAttributeForVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "VFCWithAttributes.yml";
+ ResourceReqDetails importVfcResourceInUI = ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.ATTRIBUTES);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.AttributesSection.DELETE_BUTTON_FOR_NETWORK_ATTR.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ Boolean retryResult = FunctionalInterfaces.retryMethodOnResult(() -> !GeneralUIUtils.isElementPresent("networks"), boolResult -> boolResult);
+ assertTrue("The attribute is shown in the attributes table.", retryResult);
+ }
+
+ @Test
+ public void testImportVfc() {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "CP.yml";
+ ResourceReqDetails importVfcResourceInUI = ResourceUIUtils.importVfcInUI(getUser(), filePath, fileName);
+ assertTrue(RestCDUtils.getResource(importVfcResourceInUI).getErrorCode() == HttpStatus.SC_OK);
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/run/StartTest.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/run/StartTest.java
new file mode 100644
index 0000000000..8232738660
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/run/StartTest.java
@@ -0,0 +1,394 @@
+package org.openecomp.sdc.uici.tests.run;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.testng.TestNG;
+
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+
+public class StartTest {
+
+ public static long timeOfTest = 0;
+
+ public static boolean debug = false;
+
+ public static AtomicBoolean loggerInitialized = new AtomicBoolean(false);
+
+ protected static Logger logger = null;
+
+ public static void main(String[] args) {
+ // TODO ui-ci add jar building
+ String debugEnabled = System.getProperty("debug");
+ if (debugEnabled != null && debugEnabled.equalsIgnoreCase("true")) {
+ debug = true;
+ }
+ System.out.println("Debug mode is " + (debug ? "enabled" : "disabled"));
+
+ enableLogger();
+
+ Config config = null;
+ try {
+ config = Utils.getConfig();
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ if (config == null) {
+ logger.error("Failed to configuration file of ci tests.");
+ System.exit(1);
+ }
+
+ TestNG testng = new TestNG();
+
+ List<String> suites = new ArrayList<String>();
+ suites.add("testSuites/" + args[0]);
+ testng.setTestSuites(suites);
+ // testng.setUseDefaultListeners(true);
+ testng.setOutputDirectory("target/");
+
+ testng.run();
+
+ }
+
+ public StartTest() {
+ logger = Logger.getLogger(StartTest.class.getName());
+ }
+
+ public static void enableLogger() {
+
+ if (false == loggerInitialized.get()) {
+
+ loggerInitialized.set(true);
+
+ String log4jPropsFile = System.getProperty("log4j.configuration");
+ if (System.getProperty("os.name").contains("Windows")) {
+ String logProps = "src/main/resources/ci/conf/log4j.properties";
+ if (log4jPropsFile == null) {
+ System.setProperty("targetlog", "target/");
+ log4jPropsFile = logProps;
+ }
+
+ }
+ PropertyConfigurator.configureAndWatch(log4jPropsFile);
+
+ }
+ }
+
+ // public void start(List<String> packages, boolean exitOnFailure) {
+ //
+ // boolean success = true;
+ // StringBuilder results = new StringBuilder();
+ //// Result result;
+ //
+ // if (packages == null) {
+ // return;
+ // }
+ //
+ //// for (String packageName : packages) {
+ //// //List<Class> classesForPackage =
+ // getClassesForPackage("org.openecomp.sdc.ci.tests.execute");
+ //// List<Class> classesForPackage = getClassesForPackage(packageName);
+ //// if (classesForPackage != null && false == classesForPackage.isEmpty())
+ // {
+ //// for (Class testUnit : classesForPackage) {
+ //// testClasses.add(testUnit);
+ //// }
+ //// }
+ //// }
+ ////
+ //// System.out.println(testClasses);
+ //
+ // // tsetClasses.add(LogValidatorTest.class);
+ // // tsetClasses.add(AttNorthboundTest.class);
+ //
+ //// results.append("<Html><head><style>th{background-color: gray;color:
+ // white;height: 30px;}td {color: black;height: 30px;}.fail
+ // {background-color: #FF5555;width: 100px;text-align: center;}.success
+ // {background-color: #00FF00;width: 100px;text-align: center;}.name {width:
+ // 200px;background-color: #F0F0F0;}.message {width: 300px;background-color:
+ // #F0F0F0;}</style></head><body>");
+ //
+ // Calendar calendar = Calendar.getInstance();
+ // timeOfTest = calendar.getTimeInMillis();
+ // SimpleDateFormat date_format = new SimpleDateFormat(
+ // "MMM dd yyyy HH:mm:ss");
+ // results.append("<br/><h2> This report generated on "
+ // + date_format.format(calendar.getTime()) + "</h2><br/>");
+ //
+ // results.append("<table>");
+ // addTableHead(results);
+ //
+ //// int size = testClasses.size();
+ // int index = 0;
+ //
+ // int totalRunTests = 0;
+ // int totalFailureTests = 0;
+ // int totalIgnoreTests = 0;
+ // int numOfFailureClasses = 0;
+ // for (Class<? extends AttSdcTest> testClass : testClasses) {
+ //
+ // index ++;
+ //
+ // StringBuilder builder = new StringBuilder();
+ // String str =
+ // "***************************************************************************";
+ // builder.append(str + "\n");
+ // String current = "class " + index + "/" + size + " failure("
+ // + numOfFailureClasses + ") + RUNS(" + totalRunTests + ")"
+ // + " FAILURES(" + totalFailureTests + ") IGNORED("
+ // + totalIgnoreTests + ")";
+ // int interval = ((str.length() - current.length() - 2)/ 2);
+ // String substring = str.substring(0, interval);
+ // builder.append(substring + " " + current + " " + substring + "\n");
+ // builder.append(str + "\n");
+ //
+ // System.out.println(builder.toString());
+ //
+ // logger.debug(builder.toString());
+ // logger.debug("Going to run test class " + testClass.getName());
+ //
+ // result = JUnitCore.runClasses(testClass);
+ // if (result.wasSuccessful() == false) {
+ // numOfFailureClasses++;
+ // }
+ // logger.debug("Test class " + testClass.getName() + " finished " +
+ // (result.wasSuccessful() ? "OK." : " WITH ERROR."));
+ // List<Failure> failures = result.getFailures();
+ // if (failures != null) {
+ // for (Failure failure : failures) {
+ // logger.error("Test class " + testClass.getName() + " failure test " +
+ // failure.getTestHeader() + "-" + failure.getTrace());
+ // }
+ // }
+ // int runsPerClass = result.getRunCount();
+ // int failuresPerClass = result.getFailureCount();
+ // int ignoredPerClass = result.getIgnoreCount();
+ //
+ // totalRunTests += runsPerClass;
+ // totalFailureTests += failuresPerClass;
+ // totalIgnoreTests += ignoredPerClass;
+ //
+ // logger.debug("class " + testClass.getName() + " Failed tests " +
+ // (failuresPerClass * 1.0) / runsPerClass * 100 + " %");
+ // logger.debug("class " + testClass.getName() + " Ignored tests " +
+ // (ignoredPerClass * 1.0) / runsPerClass * 100 + " %");
+ //
+ //
+ //// List<Failure> failures = result.getFailures();
+ //// if (failures != null) {
+ //// for (Failure failure : failures) {
+ //// System.err.println("9999999999" + failure.getTestHeader());
+ //// }
+ //// }
+ //
+ // addUnitTestResult(results, testClass, result);
+ // success &= result.wasSuccessful();
+ //
+ // if (numOfFailureClasses > 0) {
+ // //if (exitOnFailure) {
+ // if (exitOnFailure) {
+ // break;
+ // }
+ // }
+ // }
+ //
+ // results.append("</table>");
+ // results.append("<br/><h2> Tests Summary: </h2><br/>");
+ // results.append("Total Runs : " + totalRunTests + "<br/>");
+ // results.append("Total Failure : " + totalFailureTests + "<br/>");
+ // results.append("Total: " + totalFailureTests + "/" + totalRunTests +
+ // "<br/>");
+ // results.append("</html>");
+ //
+ // FileUtils.writeToFile(Config.instance().getOutputFolder()
+ // + File.separator + Config.instance().getReportName(),
+ // results.toString());
+ //
+ // if (!success) {
+ // System.out.println("FAILURE");
+ // logger.error("Failure tests : " + ((totalFailureTests + totalIgnoreTests)
+ // * 1.0)/ (totalRunTests + totalIgnoreTests) + " %");
+ // logger.error("Ignored tests : " + (totalIgnoreTests * 1.0)/
+ // (totalRunTests + totalIgnoreTests) + " %");
+ // System.exit(1);
+ // }
+ //
+ // System.out.println("SUCCESS");
+ // }
+
+ private List<Class> getClassesForPackage(String pkgname) {
+
+ List<Class> classes = new ArrayList<Class>();
+
+ // Get a File object for the package
+ File directory = null;
+ String fullPath;
+ String relPath = pkgname.replace('.', '/');
+
+ // System.out.println("ClassDiscovery: Package: " + pkgname +
+ // " becomes Path:" + relPath);
+
+ URL resource = ClassLoader.getSystemClassLoader().getResource(relPath);
+
+ // System.out.println("ClassDiscovery: Resource = " + resource);
+ if (resource == null) {
+ throw new RuntimeException("No resource for " + relPath);
+ }
+ fullPath = resource.getFile();
+ // System.out.println("ClassDiscovery: FullPath = " + resource);
+
+ if (debug) {
+ System.out.println("fullPath is " + fullPath);
+ }
+
+ try {
+ directory = new File(resource.toURI());
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(
+ pkgname + " (" + resource
+ + ") does not appear to be a valid URL / URI. Strange, since we got it from the system...",
+ e);
+ } catch (IllegalArgumentException e) {
+ directory = null;
+ }
+ // System.out.println("ClassDiscovery: Directory = " + directory);
+
+ if (directory != null && directory.exists()) {
+
+ // Get the list of the files contained in the package
+ String[] files = directory.list();
+ for (int i = 0; i < files.length; i++) {
+
+ // we are only interested in .class files
+ if (files[i].endsWith(".class") && false == files[i].contains("$")) {
+
+ // removes the .class extension
+ String className = pkgname + '.' + files[i].substring(0, files[i].length() - 6);
+
+ // System.out.println("ClassDiscovery: className = " +
+ // className);
+
+ if (debug) {
+ System.out.println("ClassDiscovery: className = " + className);
+ }
+
+ try {
+ Class clas = Class.forName(className);
+ boolean isAddToRun = false;
+ Method[] methods = clas.getMethods();
+ for (Method method : methods) {
+ Annotation[] anns = method.getAnnotations();
+ for (Annotation an : anns) {
+ if (an.annotationType().getSimpleName().equalsIgnoreCase("Test")) {
+ isAddToRun = true;
+ break;
+ }
+ }
+ }
+ if (isAddToRun)
+ classes.add(clas);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException loading " + className);
+ }
+ }
+ }
+ } else {
+ try {
+ String jarPath = fullPath.replaceFirst("[.]jar[!].*", ".jar").replaceFirst("file:", "");
+
+ if (debug) {
+ System.out.println("jarPath is " + jarPath);
+ }
+
+ JarFile jarFile = new JarFile(jarPath);
+ Enumeration<JarEntry> entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ String entryName = entry.getName();
+ if (entryName.startsWith(relPath) && entryName.length() > (relPath.length() + "/".length())) {
+
+ // System.out.println("ClassDiscovery: JarEntry: " +
+ // entryName);
+ String className = entryName.replace('/', '.').replace('\\', '.').replace(".class", "");
+
+ // System.out.println("ClassDiscovery: className = " +
+ // className);
+
+ if (false == className.contains("$")) {
+
+ if (debug) {
+ System.out.println("ClassDiscovery: className = " + className);
+ }
+
+ try {
+ Class clas = Class.forName(className);
+ boolean isAddToRun = false;
+ Method[] methods = clas.getMethods();
+ for (Method method : methods) {
+ Annotation[] anns = method.getAnnotations();
+ for (Annotation an : anns) {
+ if (an.annotationType().getSimpleName().equalsIgnoreCase("Test")) {
+ isAddToRun = true;
+ break;
+ }
+ }
+ }
+ if (isAddToRun)
+ classes.add(clas);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("ClassNotFoundException loading " + className);
+ }
+ }
+ }
+ }
+ jarFile.close();
+
+ } catch (IOException e) {
+ throw new RuntimeException(pkgname + " (" + directory + ") does not appear to be a valid package", e);
+ }
+ }
+ return classes;
+ }
+
+ private void addTableHead(StringBuilder results) {
+ results.append("<tr>");
+ results.append("<th>").append("Unit Test").append("</th>");
+ results.append("<th>").append("Result").append("</th>");
+ results.append("</tr>");
+ }
+
+ // private void addUnitTestResult(StringBuilder results,
+ // Class<? extends AttSdcTest> testClass, Result unitTestResult) {
+ //
+ // boolean isSuccess = unitTestResult.wasSuccessful();
+ //
+ // String result = (isSuccess) ? "success" : "fail";
+ // String fileName = FileUtils.getFileName(testClass.getName());
+ // results.append("<tr>");
+ // //
+ // results.append("<td>").append(FileUtils.getFileName(testClass.getName())).append("</td>");
+ // results.append("<td class=\"name\">")
+ // .append("<a href=\"" + fileName + timeOfTest + ".html\">"
+ // + fileName + "</a>").append("</td>");
+ // results.append("<td class=\"" + result + "\">").append(result)
+ // .append("</td>");
+ // results.append("</tr>");
+ // }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ArtifactUIUtils.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ArtifactUIUtils.java
new file mode 100644
index 0000000000..91c9c07da8
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ArtifactUIUtils.java
@@ -0,0 +1,70 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.retryMethodOnException;
+
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.Artifatcs;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.InformationalArtifatcs;
+import org.openqa.selenium.WebElement;
+
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+public final class ArtifactUIUtils {
+
+ private ArtifactUIUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void addInformationArtifact(ArtifactReqDetails artifact, String filePath,
+ final InformationalArtifatcs dataTestEnum) {
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(2000);
+ GeneralUIUtils.getWebElementWaitForVisible(dataTestEnum.getValue()).click();
+
+ final WebElement browseWebElement = FunctionalInterfaces.retryMethodOnException(
+ () -> GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.BROWSE_BUTTON.getValue()));
+ browseWebElement.sendKeys(filePath);
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.DESCRIPTION.getValue())
+ .sendKeys(artifact.getDescription());
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.DONE.getValue()).click();
+
+ }
+
+ public static Map<String, Map<String, Object>> getArtifactsListFromResponse(String jsonResponse,
+ String fieldOfArtifactList) {
+ JSONObject object = (JSONObject) JSONValue.parse(jsonResponse);
+ Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) object.get(fieldOfArtifactList);
+ return map;
+ }
+
+ /**
+ * Creates a deployment artifact on the vf. <br>
+ * Moves automatically to DeploymentArtifact Section
+ *
+ * @param artifactPayloadPath
+ * @param artifactType
+ */
+ public static void createDeploymentArtifactOnVf(final String artifactPayloadPath,
+ final ArtifactTypeEnum artifactType) {
+ GeneralUIUtils.moveToStep(CreateAndUpdateStepsEnum.DEPLOYMENT_ARTIFACT);
+ GeneralUIUtils.getWebElementWaitForClickable(Artifatcs.ADD_DEPLOYMENT_ARTIFACT.getValue()).click();
+ GeneralUIUtils.getSelectList("Create New Artifact", Artifatcs.SELECT_ARTIFACT_DROPDOWN.getValue());
+ GeneralUIUtils.getSelectList(artifactType.getType(), Artifatcs.ARTIFACT_TYPE_DROPDOWN.getValue());
+ GeneralUIUtils.getWebElementWaitForVisible(Artifatcs.ARTIFACT_DESCRIPTION.getValue())
+ .sendKeys("Artifact Description");
+ GeneralUIUtils.getWebElementWaitForVisible(Artifatcs.ARTIFACT_LABEL.getValue()).sendKeys("MyArtifactLabel");
+ retryMethodOnException(() -> GeneralUIUtils.getWebElementByDataTestId(Artifatcs.BROWSE_BUTTON.getValue())
+ .sendKeys(artifactPayloadPath));
+ GeneralUIUtils.getWebElementWaitForVisible(Artifatcs.ADD_BUTTON.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/FileHandling.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/FileHandling.java
new file mode 100644
index 0000000000..b1549741ed
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/FileHandling.java
@@ -0,0 +1,30 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class FileHandling {
+
+ public static Map<?, ?> parseYamlFile(String filePath) throws FileNotFoundException {
+ Yaml yaml = new Yaml();
+ File file = new File(filePath);
+ InputStream inputStream = new FileInputStream(file);
+ Map<?, ?> map = (Map<?, ?>) yaml.load(inputStream);
+ return map;
+ }
+
+ public static String getBasePath() {
+ return System.getProperty("user.dir");
+ }
+
+ public static String getResourcesFilesPath() {
+ return getBasePath() + File.separator + "src" + File.separator + "main" + File.separator + "resources"
+ + File.separator + "Files" + File.separator;
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/GeneralUIUtils.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/GeneralUIUtils.java
new file mode 100644
index 0000000000..4414499f69
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/GeneralUIUtils.java
@@ -0,0 +1,346 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.retryMethodOnException;
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.retryMethodOnResult;
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.swallowException;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.openecomp.sdc.uici.tests.datatypes.CreateAndUpdateStepsEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.Dashboard;
+import org.openecomp.sdc.uici.tests.execute.base.SetupCDTest;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Platform;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.remote.RemoteWebDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.Select;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+public final class GeneralUIUtils {
+
+ private static final int DEFAULT_WAIT_TIME_IN_SECONDS = 10;
+ /**************** DRIVERS ****************/
+ private static WebDriver driver;
+
+ private GeneralUIUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Finding a component in the home screen by name and clicks on it
+ * Uses the search
+ *
+ * @param componentName
+ * @throws Exception
+ */
+ public static void findComponentAndClick(String componentName) throws Exception {
+ getWebElementWaitForVisible("main-menu-input-search").sendKeys(componentName);
+ try {
+ getWebElementWaitForClickable(componentName).click();
+ GeneralUIUtils.waitForLoader();
+ getWebElementWaitForVisible("formlifecyclestate");
+ } catch (Exception e) {
+ String msg = String.format("DID NOT FIND A COMPONENT NAMED %s", componentName);
+ System.out.println(msg);
+ Assert.fail(msg);
+ }
+ }
+
+ public static WebElement getWebElementWaitForVisible(String dataTestId) {
+ return getWebElementWaitForVisible(dataTestId, DEFAULT_WAIT_TIME_IN_SECONDS);
+ }
+
+ public static WebElement getWebElementWaitForVisible(String dataTestId, int time) {
+ WebDriverWait wait = new WebDriverWait(getDriver(), time);
+ ExpectedCondition<WebElement> visibilityOfElementLocated = ExpectedConditions
+ .visibilityOfElementLocated(builDataTestIdLocator(dataTestId));
+ WebElement webElement = wait.until(visibilityOfElementLocated);
+ return webElement;
+ }
+
+ public static WebElement getWebElementWaitForClickable(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(getDriver(), DEFAULT_WAIT_TIME_IN_SECONDS);
+ ExpectedCondition<WebElement> condition = ExpectedConditions
+ .elementToBeClickable(builDataTestIdLocator(dataTestId));
+ WebElement webElement = wait.until(condition);
+ return webElement;
+ }
+
+ private static By builDataTestIdLocator(String dataTestId) {
+ return By.xpath("//*[@data-tests-id='" + dataTestId + "']");
+
+ }
+
+ /**
+ * Returns A list of Web Elements When they are all visible
+ *
+ * @param dataTestId
+ * @return
+ */
+ public static List<WebElement> getWebElementsListWaitForVisible(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(getDriver(), DEFAULT_WAIT_TIME_IN_SECONDS);
+ ExpectedCondition<List<WebElement>> visibilityOfAllElementsLocatedBy = ExpectedConditions
+ .visibilityOfAllElementsLocatedBy(builDataTestIdLocator(dataTestId));
+ return wait.until(visibilityOfAllElementsLocatedBy);
+ }
+
+ /**
+ * @deprecated Do not use. use {@link #getWebElementWaitForVisible(String)}
+ * @param dataTestId
+ * @return
+ */
+ public static WebElement getWebElementByDataTestId(String dataTestId) {
+ return driver.findElement(builDataTestIdLocator(dataTestId));
+ }
+
+ /**
+ * Checks if element is present with given dataTestsId
+ *
+ * @param dataTestId
+ * @return
+ */
+ public static boolean isElementPresent(String dataTestId) {
+ final boolean isPresent = !driver.findElements(builDataTestIdLocator(dataTestId)).isEmpty();
+ return isPresent;
+ }
+
+ public static void clickOnCreateEntityFromDashboard(String buttonId) {
+ Supplier<WebElement> addVfButtonSipplier = () -> {
+ // TODO ui-ci replace with data-test-id
+ GeneralUIUtils.moveToHTMLElementByClassName("w-sdc-dashboard-card-new");
+ return GeneralUIUtils.getWebElementByDataTestId(buttonId);
+ };
+ WebElement addVfButton = FunctionalInterfaces.retryMethodOnException(addVfButtonSipplier);
+ addVfButton.click();
+ }
+
+ // this function located select list by the data-test-id value and the item
+ // to be selected..
+ public static Select getSelectList(String item, String dataTestId) {
+ Select selectlist = new Select(driver.findElement(builDataTestIdLocator(dataTestId)));
+ if (item != null) {
+ selectlist.selectByVisibleText(item);
+ }
+ return selectlist;
+ }
+
+ // Define description area .
+ public static String defineDescription(String descriptionText) {
+
+ WebElement resourceDescriptionTextbox = GeneralUIUtils.getWebElementWaitForVisible("description");
+ resourceDescriptionTextbox.clear();
+ resourceDescriptionTextbox.sendKeys(descriptionText);
+
+ return descriptionText;
+ }
+
+ /**
+ * Clicks on the create button waits for the create to finish and the check
+ * in button to appear
+ */
+ public static void clickCreateButton() {
+ GeneralUIUtils.waitForLoader();
+ getWebElementWaitForClickable(DataTestIdEnum.LifeCyleChangeButtons.CREATE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ WebElement successNotification = driver.findElement(By.className("ui-notification"));
+ if (successNotification != null) {
+ successNotification.click();
+ }
+ getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.CHECK_IN.getValue());
+ }
+
+ public static void clickSaveButton() {
+ WebElement createButton = getWebElementWaitForClickable(DataTestIdEnum.LifeCyleChangeButtons.CREATE.getValue());
+ createButton.click();
+ }
+
+ public static void closeNotificatin() {
+ WebElement notification = driver.findElement(By.className("ui-notification"));
+ if (notification != null) {
+ notification.click();
+ }
+ }
+ public static void checkIn() {
+ waitForLoader();
+ getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.CHECK_IN.getValue()).click();
+ getWebElementWaitForVisible(DataTestIdEnum.ModalItems.ACCEP_TESTING_MESSAGE.getValue()).sendKeys("Check in !");
+ getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ waitForLoader();
+ }
+
+ public static void moveToStep(CreateAndUpdateStepsEnum Stepname) {
+ waitForLoader();
+ getWebElementWaitForClickable(Stepname.getValue()).click();
+ waitForLoader();
+ }
+
+ public static void sleep(int duration) {
+ swallowException(() -> Thread.sleep(duration));
+ }
+
+ public static WebDriver getDriver() {
+ return driver;
+ }
+
+ public static void initDriver() {
+ try {
+ System.out.println("opening browser");
+ WebDriver webDriver;
+ boolean remoteTesting = SetupCDTest.config.isRemoteTesting();
+ if (!remoteTesting) {
+ webDriver = new FirefoxDriver();
+ } else {
+ String remoteEnvIP = SetupCDTest.config.getRemoteTestingMachineIP();
+ String remoteEnvPort = SetupCDTest.config.getRemoteTestingMachinePort();
+ DesiredCapabilities cap = new DesiredCapabilities();
+ cap = DesiredCapabilities.firefox();
+ cap.setPlatform(Platform.WINDOWS);
+ cap.setBrowserName("firefox");
+
+ String remoteNodeUrl = String.format(SetupCDTest.SELENIUM_NODE_URL, remoteEnvIP, remoteEnvPort);
+ webDriver = new RemoteWebDriver(new URL(remoteNodeUrl), cap);
+
+ }
+ driver = webDriver;
+
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ /**
+ * waits until either loader finishes or 10 seconds has passed.<br>
+ * If 10 seconds has passed and loader didn't finish throws
+ * LoaderStuckException.<br>
+ */
+ public static void waitForLoader() {
+ waitForLoader(10);
+ }
+
+ /**
+ * waits until either loader finishes or maxWaitTimeInSeconds has
+ * passed.<br>
+ * If maxWaitTimeInSeconds has passed and loader didn't finish throws
+ * LoaderStuckException.<br>
+ *
+ * @param maxWaitTimeInSeconds
+ */
+ public static void waitForLoader(int maxWaitTimeInSeconds) {
+ long maxWaitTimeMS = maxWaitTimeInSeconds * 1000L;
+ Boolean loaderIsRunning = retryMethodOnResult(
+ () -> isElementPresent(DataTestIdEnum.GeneralSection.LOADER.getValue()),
+ isLoaderPresent -> !isLoaderPresent, maxWaitTimeMS, 50);
+ if (loaderIsRunning) {
+ throw new LoaderStuckException(
+ "UI Loader is stuck, max wait time of " + maxWaitTimeInSeconds + " seconds has passed.");
+ }
+
+ }
+
+ private static class LoaderStuckException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ private LoaderStuckException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Move to HTML element by class name. When moving to the HTML element, it
+ * will raise hover event.
+ *
+ * @param className
+ */
+ public static void moveToHTMLElementByClassName(String className) {
+ Actions actions = new Actions(getDriver());
+ final WebElement createButtonsArea = getDriver().findElement(By.className(className));
+ actions.moveToElement(createButtonsArea).perform();
+ }
+
+ /**
+ * Move to HTML element by element id. When moving to the HTML element, it
+ * will raise hover event.
+ *
+ * @param className
+ */
+ public static void moveToHTMLElementByDataTestId(String dataTestId) {
+ Actions actions = new Actions(getDriver());
+ final WebElement createButtonsArea = getWebElementByDataTestId(dataTestId);
+ actions.moveToElement(createButtonsArea).perform();
+ }
+
+ public static void defineVendorName(String resourceVendorName) {
+ // TODO ui-ci replace with Enum
+ WebElement resourceVendorNameTextbox = getWebElementWaitForVisible("vendorName");
+ resourceVendorNameTextbox.clear();
+ resourceVendorNameTextbox.sendKeys(resourceVendorName);
+ }
+
+ public static String defineUserId(String userId) {
+ // TODO ui-ci replace with Enum
+ WebElement resourceTagsTextbox = getWebElementWaitForVisible("userId");
+ resourceTagsTextbox.clear();
+ resourceTagsTextbox.sendKeys(userId);
+ return userId;
+ }
+
+ public static void clickAddComponent(Dashboard componentType) {
+ Runnable clickAddTask = () -> {
+ // TODO ui-ci replace with data-test-id
+ moveToHTMLElementByClassName("w-sdc-dashboard-card-new");
+ WebElement addVfButton = getWebElementByDataTestId(componentType.getValue());
+ addVfButton.click();
+ };
+ retryMethodOnException(clickAddTask);
+ }
+
+ /**
+ * This method perform submit for testing process for existing service or
+ * resource.<br>
+ * It assumes it is activated when in the resource screen and the Submit For
+ * Testing button is available.
+ *
+ * @param componentNameForMessage
+ * TODO
+ */
+ public static void submitForTestingElement(String componentNameForMessage) {
+ waitForLoader();
+ getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.SUBMIT_FOR_TESTING.getValue()).click();
+ waitForLoader();
+ getWebElementWaitForVisible(DataTestIdEnum.ModalItems.SUMBIT_FOR_TESTING_MESSAGE.getValue())
+ .sendKeys("Submit for testing for " + componentNameForMessage);
+ waitForLoader();
+ getWebElementWaitForClickable(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ waitForLoader();
+ waitForElementToDisappear(DataTestIdEnum.ModalItems.OK.getValue());
+
+ }
+
+ /**
+ * Waits Until elements disappears or until 10 seconds pass
+ *
+ * @param dataTestId
+ */
+ public static void waitForElementToDisappear(String dataTestId) {
+ Supplier<Boolean> elementPresenseChecker = () -> GeneralUIUtils.isElementPresent(dataTestId);
+ Function<Boolean, Boolean> verifier = isElementPresent -> !isElementPresent;
+ FunctionalInterfaces.retryMethodOnResult(elementPresenseChecker, verifier);
+
+ }
+
+} \ No newline at end of file
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/MethodManipulationUtils.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/MethodManipulationUtils.java
new file mode 100644
index 0000000000..28b39e86f3
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/MethodManipulationUtils.java
@@ -0,0 +1,15 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+/**
+ * Class For manipulations on method
+ *
+ * @author mshitrit
+ *
+ */
+public final class MethodManipulationUtils {
+
+ private MethodManipulationUtils() {
+ throw new IllegalAccessError();
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/OnboardUtility.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/OnboardUtility.java
new file mode 100644
index 0000000000..3d21539e86
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/OnboardUtility.java
@@ -0,0 +1,477 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import static org.testng.AssertJUnit.assertTrue;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.file.FileSystems;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.validation.constraints.AssertTrue;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+/**
+ * Utility Class For Onboarding
+ *
+ * @author mshitrit
+ *
+ */
+public final class OnboardUtility {
+
+ private OnboardUtility() {
+ throw new UnsupportedOperationException();
+ }
+
+ private static final class Constants {
+ private static final String VENDOR_SOFTWARE_PRODUCTS = "vendor-software-products";
+ private static final String VENDOR_LICENSE_MODELS = "vendor-license-models";
+
+ private static final String VSP_ID = "vspId";
+ private static final String VALUE = "value";
+
+ enum Actions {
+ CHECK_IN("Checkin"), SUBMIT("Submit"), CREATE_PACKAGE("Create_Package");
+
+ private String value;
+
+ private Actions(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+ };
+ }
+
+ /**
+ * @param heatFileName
+ * @param filepath
+ * @param userId
+ * @param vld
+ * @return
+ * @throws Exception
+ */
+ public static void createVendorSoftwareProduct(String heatFileName, String filepath, String userId,
+ VendorLicenseDetails vld) {
+ RestResponse createNewVendorSoftwareProduct = FunctionalInterfaces
+ .swallowException(() -> createNewVendorSoftwareProduct(vld, userId));
+ assertTrue(createNewVendorSoftwareProduct.getErrorCode() == HttpStatus.SC_OK);
+ String vspid = ResponseParser.getValueFromJsonResponse(createNewVendorSoftwareProduct.getResponse(),
+ Constants.VSP_ID);
+
+ RestResponse response = FunctionalInterfaces
+ .swallowException(() -> uploadHeatPackage(filepath, heatFileName, vspid, userId));
+ assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+
+ response = actionOnComponent(vspid, Constants.Actions.CHECK_IN.getValue(), Constants.VENDOR_SOFTWARE_PRODUCTS,
+ userId);
+ assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+
+ response = actionOnComponent(vspid, Constants.Actions.SUBMIT.getValue(), Constants.VENDOR_SOFTWARE_PRODUCTS,
+ userId);
+ assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+
+ response = actionOnComponent(vspid, Constants.Actions.CREATE_PACKAGE.getValue(),
+ Constants.VENDOR_SOFTWARE_PRODUCTS, userId);
+ assertTrue(response.getErrorCode() == HttpStatus.SC_OK);
+
+ }
+
+ /**
+ * Contains Details Relevant to Vendor License
+ *
+ * @author mshitrit
+ *
+ */
+ public static final class VendorLicenseDetails {
+ private final String vendorId;
+ private final String vendorLicenseName;
+ private final String vendorLicenseAgreementId;
+ private final String featureGroupId;
+ private final String vendorSoftwareProduct;
+
+ private VendorLicenseDetails(String vendorId, String vendorLicenseName, String vendorLicenseAgreementId,
+ String featureGroupId) {
+ super();
+ this.vendorId = vendorId;
+ this.vendorLicenseName = vendorLicenseName;
+ this.vendorLicenseAgreementId = vendorLicenseAgreementId;
+ this.featureGroupId = featureGroupId;
+ vendorSoftwareProduct = UUID.randomUUID().toString().split("-")[0];
+ }
+
+ public String getVendorId() {
+ return vendorId;
+ }
+
+ public String getVendorLicenseName() {
+ return vendorLicenseName;
+ }
+
+ public String getVendorLicenseAgreementId() {
+ return vendorLicenseAgreementId;
+ }
+
+ public String getFeatureGroupId() {
+ return featureGroupId;
+ }
+
+ public String getVendorSoftwareProduct() {
+ return vendorSoftwareProduct;
+ }
+
+ }
+
+ /**
+ * Creates Vendor License
+ *
+ * @param userId
+ * @return
+ * @throws Exception
+ */
+ public static VendorLicenseDetails createVendorLicense(String userId) {
+ final String fieldNameValue = Constants.VALUE;
+ String vendorLicenseName = UUID.randomUUID().toString().split("-")[0];
+ RestResponse vendorLicenseResponse = FunctionalInterfaces
+ .swallowException(() -> createVendorLicenseModels(vendorLicenseName, userId));
+ assertTrue(vendorLicenseResponse.getErrorCode() == HttpStatus.SC_OK);
+
+ String vendorId = ResponseParser.getValueFromJsonResponse(vendorLicenseResponse.getResponse(), fieldNameValue);
+
+ RestResponse vendorKeyGroupsResponse = FunctionalInterfaces
+ .swallowException(() -> createVendorKeyGroups(vendorId, userId));
+ assertTrue(vendorKeyGroupsResponse.getErrorCode() == HttpStatus.SC_OK);
+ String keyGroupId = ResponseParser.getValueFromJsonResponse(vendorKeyGroupsResponse.getResponse(),
+ fieldNameValue);
+
+ RestResponse vendorEntitlementPool = FunctionalInterfaces
+ .swallowException(() -> createVendorEntitlementPool(vendorId, userId));
+ assertTrue(vendorEntitlementPool.getErrorCode() == HttpStatus.SC_OK);
+ String entitlementPoolId = ResponseParser.getValueFromJsonResponse(vendorEntitlementPool.getResponse(),
+ fieldNameValue);
+
+ RestResponse vendorLicenseFeatureGroups = FunctionalInterfaces.swallowException(
+ () -> createVendorLicenseFeatureGroups(vendorId, keyGroupId, entitlementPoolId, userId));
+ assertTrue(vendorLicenseFeatureGroups.getErrorCode() == HttpStatus.SC_OK);
+ String featureGroupId = ResponseParser.getValueFromJsonResponse(vendorLicenseFeatureGroups.getResponse(),
+ fieldNameValue);
+
+ RestResponse vendorLicenseAgreement = FunctionalInterfaces
+ .swallowException(() -> createVendorLicenseAgreement(vendorId, featureGroupId, userId));
+ assertTrue(vendorLicenseAgreement.getErrorCode() == HttpStatus.SC_OK);
+ String vendorLicenseAgreementId = ResponseParser.getValueFromJsonResponse(vendorLicenseAgreement.getResponse(),
+ fieldNameValue);
+
+ RestResponse actionOnComponent = actionOnComponent(vendorId, Constants.Actions.CHECK_IN.getValue(),
+ Constants.VENDOR_LICENSE_MODELS, userId);
+ assertTrue(actionOnComponent.getErrorCode() == HttpStatus.SC_OK);
+
+ actionOnComponent = actionOnComponent(vendorId, Constants.Actions.SUBMIT.getValue(),
+ Constants.VENDOR_LICENSE_MODELS, userId);
+ assertTrue(actionOnComponent.getErrorCode() == HttpStatus.SC_OK);
+
+ return new VendorLicenseDetails(vendorId, vendorLicenseName, vendorLicenseAgreementId, featureGroupId);
+ }
+
+ private static RestResponse actionOnComponent(String vspid, String action, String onboardComponent, String userId) {
+ Config config = FunctionalInterfaces.swallowException(() -> Utils.getConfig());
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/%s/%s/actions", config.getCatalogBeHost(),
+ config.getCatalogBePort(), onboardComponent, vspid);
+
+ JSONObject jObject = new JSONObject();
+ FunctionalInterfaces.swallowException(() -> jObject.put("action", action));
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = FunctionalInterfaces
+ .swallowException(() -> http.httpSendPut(url, jObject.toString(), headersMap));
+ return response;
+ }
+
+ private static RestResponse createVendorLicenseModels(String name, String userId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models", config.getCatalogBeHost(),
+ config.getCatalogBePort());
+
+ JSONObject jObject = new JSONObject();
+ jObject.put("vendorName", name);
+ jObject.put("description", "new vendor license model");
+ jObject.put("iconRef", "icon");
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObject.toString(), headersMap);
+ return response;
+
+ }
+
+ private static RestResponse createVendorLicenseAgreement(String vspid, String featureGroupId, String userId)
+ throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/license-agreements",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ JSONObject licenseTermpObject = new JSONObject();
+ licenseTermpObject.put("choice", "Fixed_Term");
+ licenseTermpObject.put("other", "");
+
+ JSONObject jObjectBody = new JSONObject();
+ jObjectBody.put("name", "abc");
+ jObjectBody.put("description", "new vendor license agreement");
+ jObjectBody.put("requirementsAndConstrains", "abc");
+ jObjectBody.put("licenseTerm", licenseTermpObject);
+ jObjectBody.put("addedFeatureGroupsIds", Arrays.asList(featureGroupId).toArray());
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObjectBody.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse createVendorLicenseFeatureGroups(String vspid, String licenseKeyGroupId,
+ String entitlementPoolId, String userId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/feature-groups",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ JSONObject jObject = new JSONObject();
+ jObject.put("name", "xyz");
+ jObject.put("description", "new vendor license feature groups");
+ jObject.put("partNumber", "123abc456");
+ jObject.put("addedLicenseKeyGroupsIds", Arrays.asList(licenseKeyGroupId).toArray());
+ jObject.put("addedEntitlementPoolsIds", Arrays.asList(entitlementPoolId).toArray());
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObject.toString(), headersMap);
+ return response;
+
+ }
+
+ private static RestResponse createVendorEntitlementPool(String vspid, String userId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/entitlement-pools",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ JSONObject jEntitlementMetricObject = new JSONObject();
+ jEntitlementMetricObject.put("choice", "CPU");
+ jEntitlementMetricObject.put("other", "");
+
+ JSONObject jAggregationFunctionObject = new JSONObject();
+ jAggregationFunctionObject.put("choice", "Peak");
+ jAggregationFunctionObject.put("other", "");
+
+ JSONObject jOperationalScope = new JSONObject();
+ jOperationalScope.put("choices", Arrays.asList("Availability_Zone").toArray());
+ jOperationalScope.put("other", "");
+
+ JSONObject jTimeObject = new JSONObject();
+ jTimeObject.put("choice", "Hour");
+ jTimeObject.put("other", "");
+
+ JSONObject jObjectBody = new JSONObject();
+ jObjectBody.put("name", "def");
+ jObjectBody.put("description", "new vendor license entitlement pool");
+ jObjectBody.put("thresholdValue", "23");
+ jObjectBody.put("thresholdUnits", "Absolute");
+ jObjectBody.put("entitlementMetric", jEntitlementMetricObject);
+ jObjectBody.put("increments", "abcd");
+ jObjectBody.put("aggregationFunction", jAggregationFunctionObject);
+ jObjectBody.put("operationalScope", jOperationalScope);
+ jObjectBody.put("time", jTimeObject);
+ jObjectBody.put("manufacturerReferenceNumber", "123aaa");
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObjectBody.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse createVendorKeyGroups(String vspid, String userId) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/license-key-groups",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ JSONObject jOperationalScope = new JSONObject();
+ jOperationalScope.put("choices", Arrays.asList("Tenant").toArray());
+ jOperationalScope.put("other", "");
+
+ JSONObject jObjectBody = new JSONObject();
+ jObjectBody.put("name", "keyGroup");
+ jObjectBody.put("description", "new vendor license key group");
+ jObjectBody.put("operationalScope", jOperationalScope);
+ jObjectBody.put("type", "Universal");
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObjectBody.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse createNewVendorSoftwareProduct(VendorLicenseDetails vld, String userId)
+ throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-software-products",
+ config.getCatalogBeHost(), config.getCatalogBePort());
+
+ JSONObject jlicensingDataObj = new JSONObject();
+ jlicensingDataObj.put("licenseAgreement", vld.getVendorLicenseAgreementId());
+ jlicensingDataObj.put("featureGroups", Arrays.asList(vld.getFeatureGroupId()).toArray());
+
+ JSONObject jObject = new JSONObject();
+ jObject.put("name", vld.getVendorSoftwareProduct());
+ jObject.put("description", "new VSP description");
+ jObject.put("category", "resourceNewCategory.generic");
+ jObject.put("subCategory", "resourceNewCategory.generic.database");
+ jObject.put("licensingVersion", "1.0");
+ jObject.put("vendorName", vld.getVendorLicenseName());
+ jObject.put("vendorId", vld.getVendorId());
+ jObject.put("icon", "icon");
+ jObject.put("licensingData", jlicensingDataObj);
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+
+ RestResponse response = http.httpSendPost(url, jObject.toString(), headersMap);
+
+ return response;
+ }
+
+ private static RestResponse uploadHeatPackage(String filepath, String filename, String vspid, String userId)
+ throws Exception {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+ mpBuilder.addPart("resourceZip", new FileBody(getTestZipFile(filepath, filename)));
+
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-software-products/%s/upload",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "multipart/form-data");
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ RestResponse restResponse = new RestResponse();
+
+ Iterator<String> iterator = headersMap.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ String value = headersMap.get(key);
+ httpPost.addHeader(key, value);
+ }
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ HttpEntity entity = response.getEntity();
+ String responseBody = null;
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(instream, writer);
+ responseBody = writer.toString();
+ try {
+
+ } finally {
+ instream.close();
+ }
+ }
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+ restResponse.setResponse(responseBody);
+
+ return restResponse;
+
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ private static void closeResponse(CloseableHttpResponse response) {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ } catch (IOException e) {
+ System.out.println(String.format("failed to close client or response: %s", e.getMessage()));
+ }
+ }
+
+ private static void closeHttpClient(CloseableHttpClient client) {
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ System.out.println(String.format("failed to close client or response: %s", e.getMessage()));
+ }
+ }
+
+ private static File getTestZipFile(String filepath, String filename) throws IOException {
+ Config config = Utils.getConfig();
+ String sourceDir = config.getImportResourceTestsConfigDir();
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(filepath + File.separator + filename);
+ return filePath.toFile();
+ }
+
+ protected static Map<String, String> prepareHeadersMap(String userId) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+ return headersMap;
+ }
+
+ public static void createVfFromOnboarding(String userID, String zipFile, String filepath) {
+ VendorLicenseDetails vld = createVendorLicense(userID);
+ createVendorSoftwareProduct(zipFile, filepath, userID, vld);
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.OPEN_MODAL_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.ONBOARDING_SEARCH.getValue())
+ .sendKeys(vld.getVendorSoftwareProduct());
+
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(1000);
+ GeneralUIUtils.getWebElementWaitForClickable(vld.getVendorSoftwareProduct()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.IMPORT_ICON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForClickable(DataTestIdEnum.LifeCyleChangeButtons.CREATE.getValue()).click();
+ GeneralUIUtils.waitForLoader(300);
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ResourceUIUtils.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ResourceUIUtils.java
new file mode 100644
index 0000000000..477cc0c3bb
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ResourceUIUtils.java
@@ -0,0 +1,299 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.retryMethodOnException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.Dashboard;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+public final class ResourceUIUtils {
+ public static final String RESOURCE_NAME_PREFIX = "ResourceCDTest-";
+ protected static final boolean IS_BEFORE_TEST = true;
+ public static final String INITIAL_VERSION = "0.1";
+ public static final String ICON_RESOURCE_NAME = "call_controll";
+ protected static final String UPDATED_RESOURCE_ICON_NAME = "objectStorage";
+
+ private ResourceUIUtils() {
+ }
+
+ public static void defineResourceName(String resourceName) {
+
+ WebElement resourceNameTextbox = GeneralUIUtils.getDriver().findElement(By.name("componentName"));
+ resourceNameTextbox.clear();
+ resourceNameTextbox.sendKeys(resourceName);
+ }
+
+ public static void defineResourceCategory(String category, String datatestsid) {
+
+ GeneralUIUtils.getSelectList(category, datatestsid);
+ }
+
+ public static void importFileWithSendKeyBrowse(String FilePath, String FileName) throws Exception {
+ WebElement browsebutton = GeneralUIUtils.getWebElementWaitForVisible("browseButton");
+ browsebutton.sendKeys(FilePath + FileName);
+ }
+
+ public static void defineTagsList(ResourceReqDetails resource, String[] resourceTags) {
+ List<String> taglist = new ArrayList<String>();
+ ;
+ WebElement resourceTagsTextbox = GeneralUIUtils.getWebElementWaitForVisible("i-sdc-tag-input");
+ for (String tag : resourceTags) {
+ resourceTagsTextbox.clear();
+ resourceTagsTextbox.sendKeys(tag);
+ resourceTagsTextbox.sendKeys(Keys.ENTER);
+ taglist.add(tag);
+ }
+ resource.setTags(taglist);
+ }
+
+ public static void defineVendorRelease(String resourceVendorRelease) {
+
+ WebElement resourceVendorReleaseTextbox = GeneralUIUtils.getWebElementWaitForVisible("vendorRelease");
+ resourceVendorReleaseTextbox.clear();
+ resourceVendorReleaseTextbox.sendKeys(resourceVendorRelease);
+ }
+
+ public static void defineProjectCode(String projectCode) {
+
+ WebElement resourceNameTextbox = GeneralUIUtils.getDriver().findElement(By.name("projectCode"));
+ resourceNameTextbox.clear();
+ resourceNameTextbox.sendKeys(projectCode);
+ }
+
+ public static void clickButton(String selectButton) {
+
+ WebElement clickButton = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + selectButton + "']"));
+ clickButton.click();
+ }
+
+ public static WebElement Waitfunctionforbuttons(String element, int timeout) {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), timeout);
+ return wait.until(ExpectedConditions.elementToBeClickable(By.xpath(element)));
+ }
+
+ // coded by teddy
+ public static void fillGeneralInformationPage(ResourceReqDetails resource, User user) {
+ try {
+ resource.setContactId(user.getUserId());
+ resource.setCreatorUserId(user.getUserId());
+ resource.setCreatorFullName(user.getFullName());
+ defineResourceName(resource.getName());
+ defineResourceCategory(resource.getCategories().get(0).getSubcategories().get(0).getName(),
+ "selectGeneralCategory");
+ GeneralUIUtils.defineDescription(resource.getDescription());
+ GeneralUIUtils.defineVendorName(resource.getVendorName());
+ defineVendorRelease(resource.getVendorRelease());
+ defineTagsList(resource, new String[] { resource.getName() });
+ GeneralUIUtils.defineUserId(resource.getCreatorUserId());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static ResourceReqDetails createResourceInUI(User user) {
+ ResourceReqDetails defineResourceDetails = defineResourceDetails();
+ GeneralUIUtils.clickAddComponent(DataTestIdEnum.Dashboard.BUTTON_ADD_VF);
+
+ GeneralUIUtils.waitForLoader();
+ fillGeneralInformationPage(defineResourceDetails, user);
+ GeneralUIUtils.clickCreateButton();
+ return defineResourceDetails;
+
+ }
+
+ @SuppressWarnings("deprecation")
+ private static void openImportWithFile(String filePath, String fileName, Dashboard elementType) {
+ Runnable openImportTask = () -> {
+ GeneralUIUtils.moveToHTMLElementByDataTestId(Dashboard.IMPORT_AREA.getValue());
+ WebElement imoprtVFButton = GeneralUIUtils.getWebElementByDataTestId(elementType.getValue());
+ imoprtVFButton.sendKeys(filePath + fileName);
+ };
+ retryMethodOnException(openImportTask);
+
+ }
+
+ public static ResourceReqDetails importVfcInUI(User user, String filePath, String fileName) {
+ ResourceReqDetails defineResourceDetails = defineResourceDetails();
+ openImportWithFile(filePath, fileName, DataTestIdEnum.Dashboard.IMPORT_VFC_FILE);
+ // Fill the general page fields.
+ GeneralUIUtils.waitForLoader();
+ fillGeneralInformationPage(defineResourceDetails, user);
+ GeneralUIUtils.clickCreateButton();
+ return defineResourceDetails;
+ }
+
+ /**
+ * Import VF
+ *
+ * @param user
+ * @param filePath
+ * @param fileName
+ * @return
+ */
+ public static ResourceReqDetails importVfInUI(User user, String filePath, String fileName) {
+ ResourceReqDetails defineResourceDetails = defineResourceDetails();
+ openImportWithFile(filePath, fileName, DataTestIdEnum.Dashboard.IMPORT_VF_FILE);
+ // Fill the general page fields.
+ GeneralUIUtils.waitForLoader();
+ fillGeneralInformationPage(defineResourceDetails, user);
+
+ GeneralUIUtils.clickSaveButton();
+
+ return defineResourceDetails;
+ }
+
+ public static ResourceReqDetails importVfInUIWithoutCheckin(User user, String filePath, String fileName) {
+ ResourceReqDetails defineResourceDetails = defineResourceDetails();
+ openImportWithFile(filePath, fileName, DataTestIdEnum.Dashboard.IMPORT_VF_FILE);
+ // Fill the general page fields.
+ GeneralUIUtils.waitForLoader();
+ fillGeneralInformationPage(defineResourceDetails, user);
+ GeneralUIUtils.clickSaveButton();
+ GeneralUIUtils.waitForLoader();
+ // String okButtonId=DataTestIdEnum.ModalItems.OK.getValue();
+ // ResourceUIUtils.clickButton(okButtonId);
+ // ResourceUIUtils.Waitfunctionforbuttons("//*[@data-tests-id='"+okButtonId+"']",10);
+ // ResourceUIUtils.clickButton(okButtonId);
+ return defineResourceDetails;
+ }
+
+ public static ResourceReqDetails importVfFromOnBoardingModalWithoutCheckin(User user, String fileName) {
+ ResourceReqDetails defineResourceDetails = defineResourceDetails();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.OPEN_MODAL_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(fileName).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.IMPORT_ICON.getValue()).click();
+
+ // Fill the general page fields.
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.clickSaveButton();
+ GeneralUIUtils.waitForLoader();
+ return defineResourceDetails;
+ }
+
+ @SuppressWarnings("deprecation")
+ public static void updateVfCsar(String filePath, String fileName) {
+ retryMethodOnException(
+ () -> GeneralUIUtils.getWebElementByDataTestId(DataTestIdEnum.GeneralSection.BROWSE_BUTTON.getValue())
+ .sendKeys(filePath + fileName));
+ GeneralUIUtils.clickSaveButton();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static void updateVfCsarFromOnBoarding() {
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralSection.BROWSE_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementsListWaitForVisible(DataTestIdEnum.OnBoardingTable.CSAR_ROW.getValue()).get(0)
+ .click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.OnBoardingTable.UPDATE_ICON.getValue()).click();
+ GeneralUIUtils.clickSaveButton();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static ResourceReqDetails defineResourceDetails() {
+ ResourceReqDetails resource = new ResourceReqDetails();
+ resource = ElementFactory.getDefaultResource(NormativeTypesEnum.ROOT,
+ ResourceCategoryEnum.GENERIC_NETWORK_ELEMENTS);
+ resource.setVersion(INITIAL_VERSION);
+ resource.setIcon(ICON_RESOURCE_NAME);
+ resource.setResourceType(ResourceTypeEnum.VF.toString());
+ resource.setName(getRandomComponentName(RESOURCE_NAME_PREFIX));
+
+ return resource;
+ }
+
+ protected static String getRandomComponentName(String prefix) {
+ return prefix + new Random().nextInt(10000);
+ }
+
+ public static ImmutablePair<String, String> getRIPosition(ResourceReqDetails createResourceInUI, User user) {
+ GeneralUIUtils.sleep(1000);
+ String responseAfterDrag = RestCDUtils.getResource(createResourceInUI).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ String xPosPostDrag = (String) ((JSONObject) ((JSONArray) jsonResource.get("componentInstances")).get(0))
+ .get("posX");
+ String yPosPostDrag = (String) ((JSONObject) ((JSONArray) jsonResource.get("componentInstances")).get(0))
+ .get("posY");
+ return new ImmutablePair<String, String>(xPosPostDrag, yPosPostDrag);
+
+ }
+
+ public static void fillinDeploymentArtifactFormAndClickDone(
+ org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails details, String filePath) {
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ArtifactModal.LABEL.getValue())
+ .sendKeys(details.getArtifactLabel());
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.DESCRIPTION.getValue())
+ .sendKeys(details.getDescription());
+ GeneralUIUtils.getSelectList(details.getArtifactType(), DataTestIdEnum.ArtifactModal.TYPE.getValue());
+ retryMethodOnException(() -> GeneralUIUtils
+ .getWebElementByDataTestId(DataTestIdEnum.GeneralSection.BROWSE_BUTTON.getValue()).sendKeys(filePath));
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.DONE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ /**
+ * Tests and Accept resource or service
+ *
+ * @param createResourceInUI
+ */
+ public static void testAndAcceptElement(ComponentReqDetails createResourceInUI) {
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(createResourceInUI.getName()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.START_TESTING.getValue())
+ .click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.ACCEPT.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.ACCEP_TESTING_MESSAGE.getValue())
+ .sendKeys("resource " + createResourceInUI.getName() + " tested successfuly");
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.waitForElementToDisappear(DataTestIdEnum.ModalItems.OK.getValue());
+ }
+
+ /**
+ * Waits Until resource changed to requested lifeCycle State
+ *
+ * @param createResourceInUI
+ * @param requestedLifeCycleState
+ * @return
+ */
+ public static Resource waitForState(ResourceReqDetails createResourceInUI,
+ LifecycleStateEnum requestedLifeCycleState) {
+ Supplier<Resource> resourceGetter = () -> {
+ String resourceString = RestCDUtils.getResource(createResourceInUI).getResponse();
+ return ResponseParser.convertResourceResponseToJavaObject(resourceString);
+ };
+ Function<Resource, Boolean> verifier = res -> res.getLifecycleState() == requestedLifeCycleState;
+ return FunctionalInterfaces.retryMethodOnResult(resourceGetter, verifier);
+
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/RestCDUtils.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/RestCDUtils.java
new file mode 100644
index 0000000000..8b602e305d
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/RestCDUtils.java
@@ -0,0 +1,167 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.retryMethodOnResult;
+import static org.openecomp.sdc.common.datastructure.FunctionalInterfaces.swallowException;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.http.HttpStatus;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+
+public class RestCDUtils {
+
+ private static void setResourceUniqueIdAndUUID(ComponentReqDetails element, RestResponse getResourceResponse) {
+ element.setUniqueId(ResponseParser.getUniqueIdFromResponse(getResourceResponse));
+ element.setUUID(ResponseParser.getUuidFromResponse(getResourceResponse));
+ }
+
+ public static RestResponse getResourceByNameAndVersionRetryOnFail(String userId, String resourceName,
+ String resourceVersion) {
+ Supplier<RestResponse> resourceGetter = () -> swallowException(
+ () -> ResourceRestUtils.getResourceByNameAndVersion(userId, resourceName, resourceVersion));
+ Function<RestResponse, Boolean> validator = restRes -> restRes.getErrorCode() == HttpStatus.SC_OK;
+ return retryMethodOnResult(resourceGetter, validator);
+ }
+
+ public static RestResponse getResource(ResourceReqDetails resource) {
+ try {
+ System.out.println("trying to get resource");
+ RestResponse getResourceResponse = null;
+ String reourceUniqueId = resource.getUniqueId();
+
+ if (reourceUniqueId != null) {
+ GeneralUIUtils.sleep(1000);
+ getResourceResponse = ResourceRestUtils.getResource(reourceUniqueId);
+ if (getResourceResponse.getErrorCode().intValue() == HttpStatus.SC_OK) {
+ System.out.println("succeeded to get resource");
+ }
+ return getResourceResponse;
+ }
+ JSONObject getResourceJSONObject = null;
+ getResourceResponse = getResourceByNameAndVersionRetryOnFail(UserRoleEnum.ADMIN.getUserId(),
+ resource.getName(), resource.getVersion());
+ if (getResourceResponse.getErrorCode().intValue() == HttpStatus.SC_OK) {
+ JSONArray jArray = new JSONArray(getResourceResponse.getResponse());
+ for (int i = 0; i < jArray.length(); i++) {
+ getResourceJSONObject = jArray.getJSONObject(i);
+ String resourceType = ResponseParser.getValueFromJsonResponse(getResourceJSONObject.toString(),
+ "resourceType");
+ if (resourceType.equals(resource.getResourceType())) {
+ getResourceResponse.setResponse(getResourceJSONObject.toString());
+ setResourceUniqueIdAndUUID(resource, getResourceResponse);
+ System.out.println("succeeded to get resource");
+ return getResourceResponse;
+ }
+ }
+ }
+
+ return getResourceResponse;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static RestResponse getService(ServiceReqDetails service, User user) {
+ Supplier<RestResponse> serviceFetcher = () -> swallowException(
+ () -> ServiceRestUtils.getServiceByNameAndVersion(user, service.getName(), service.getVersion()));
+ Function<RestResponse, Boolean> verifier = restResponse -> restResponse.getErrorCode()
+ .intValue() == HttpStatus.SC_OK;
+ RestResponse getServiceResponse = retryMethodOnResult(serviceFetcher, verifier);
+
+ if (getServiceResponse.getErrorCode().intValue() == HttpStatus.SC_OK) {
+ setResourceUniqueIdAndUUID(service, getServiceResponse);
+ }
+ return getServiceResponse;
+ }
+
+ public static RestResponse getProduct(ProductReqDetails product, User user) throws Exception {
+ Thread.sleep(3500);
+ RestResponse getProductResponse = ProductRestUtils.getProductByNameAndVersion(product.getName(),
+ product.getVersion(), user.getUserId());
+ if (getProductResponse.getErrorCode().intValue() == 200) {
+ setResourceUniqueIdAndUUID(product, getProductResponse);
+ }
+ return getProductResponse;
+ }
+
+ public static Map<String, String> getAllElementVersionsFromResponse(RestResponse getResource) throws Exception {
+ Map<String, String> versionsMap = new HashMap<String, String>();
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+
+ JSONObject object = new JSONObject(getResource.getResponse());
+ versionsMap = mapper.readValue(object.get("allVersions").toString(), Map.class);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return versionsMap;
+
+ }
+
+ return versionsMap;
+ }
+
+ public static void deleteElementVersions(Map<String, String> elementVersions, boolean isBeforeTest, Object clazz,
+ User user) throws Exception {
+ Iterator<String> iterator = elementVersions.keySet().iterator();
+ while (iterator.hasNext()) {
+ String singleVersion = iterator.next();
+ String uniqueId = elementVersions.get(singleVersion);
+ RestResponse deleteResponse = null;
+ if (clazz instanceof ServiceReqDetails) {
+ deleteResponse = ServiceRestUtils.deleteServiceById(uniqueId, user.getUserId());
+ } else if (clazz instanceof ResourceReqDetails) {
+ deleteResponse = ResourceRestUtils.deleteResource(uniqueId, user.getUserId());
+ } else if (clazz instanceof ProductReqDetails) {
+ deleteResponse = ProductRestUtils.deleteProduct(uniqueId, user.getUserId());
+ }
+
+ if (isBeforeTest) {
+ assertTrue(deleteResponse.getErrorCode().intValue() == 204
+ || deleteResponse.getErrorCode().intValue() == 404);
+ } else {
+ assertTrue(deleteResponse.getErrorCode().intValue() == 204);
+ }
+ }
+ }
+
+ public static void deleteAllResourceVersionsAfterTest(ComponentReqDetails componentDetails,
+ RestResponse getObjectResponse, User user) throws Exception {
+ deleteAllComponentVersion(false, componentDetails, getObjectResponse, user);
+ }
+
+ public static void deleteAllResourceVersionsBeforeTest(ComponentReqDetails componentDetails,
+ RestResponse getObjectResponse, User user) throws Exception {
+ deleteAllComponentVersion(true, componentDetails, getObjectResponse, user);
+ }
+
+ public static void deleteAllComponentVersion(boolean isBeforeTest, ComponentReqDetails componentDetails,
+ RestResponse getObjectResponse, User user) throws Exception {
+ if (getObjectResponse.getErrorCode().intValue() == 404)
+ return;
+ Map<String, String> componentVersionsMap = getAllElementVersionsFromResponse(getObjectResponse);
+ System.out.println("deleting...");
+ deleteElementVersions(componentVersionsMap, isBeforeTest, componentDetails, user);
+ componentDetails.setUniqueId(null);
+ }
+
+} \ No newline at end of file
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ServiceUIUtils.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ServiceUIUtils.java
new file mode 100644
index 0000000000..abcc3b3ade
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/utilities/ServiceUIUtils.java
@@ -0,0 +1,147 @@
+package org.openecomp.sdc.uici.tests.utilities;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.uici.tests.datatypes.DataTestIdEnum.GeneralSection;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Service;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+public final class ServiceUIUtils {
+
+ public static final String SERVICE_NAME_PREFIX = "ServiceCDTest-";
+ public static final String INITIAL_VERSION = "0.1";
+ public static final String ICON_SERVICE_NAME = "mobility";
+
+ private ServiceUIUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static String defineServiceName(String serviceName) {
+ WebElement serviceNameElement = GeneralUIUtils.getWebElementWaitForVisible(GeneralSection.NAME.getValue());
+ serviceNameElement.clear();
+ serviceNameElement.sendKeys(serviceName);
+ return serviceName;
+ }
+
+ public static void defineTagsList(ServiceReqDetails service, String[] serviceTags) {
+ List<String> taglist = new ArrayList<String>();
+ ;
+ WebElement serviceTagsTextbox = GeneralUIUtils.getWebElementWaitForVisible("i-sdc-tag-input");
+ for (String tag : serviceTags) {
+ serviceTagsTextbox.clear();
+ serviceTagsTextbox.sendKeys(tag);
+ serviceTagsTextbox.sendKeys(Keys.ENTER);
+ taglist.add(tag);
+ }
+ taglist.add(0, service.getName());
+ service.setTags(taglist);
+ }
+
+ public static Select defineServiceCategory(String category) {
+
+ return GeneralUIUtils.getSelectList(category, "selectGeneralCategory");
+ }
+
+ private static void defineServiceProjectCode(String projectCode) {
+ WebElement projectCodeTextbox = GeneralUIUtils.getWebElementWaitForVisible("projectCode");
+ projectCodeTextbox.clear();
+ projectCodeTextbox.sendKeys(projectCode);
+ }
+
+ private static void fillServiceGeneralPage(ServiceReqDetails service, User user) {
+ service.setContactId(user.getUserId());
+ service.setCreatorUserId(user.getUserId());
+ service.setCreatorFullName(user.getFullName());
+ defineServiceName(service.getName());
+ defineServiceCategory(service.getCategories().get(0).getName());
+ GeneralUIUtils.defineDescription(service.getDescription());
+ defineTagsList(service,
+ new String[] { service.getName(), "This-is-tag", "another-tag", "Test-automation-tag" });
+ GeneralUIUtils.defineUserId(service.getCreatorUserId());
+ defineServiceProjectCode(service.getProjectCode());
+
+ }
+
+ public static ServiceReqDetails createServiceInUI(User user) {
+
+ ServiceReqDetails defineServiceetails = defineServiceDetails(user);
+ GeneralUIUtils.clickAddComponent(DataTestIdEnum.Dashboard.BUTTON_ADD_SERVICE);
+
+ GeneralUIUtils.waitForLoader();
+ fillServiceGeneralPage(defineServiceetails, user);
+
+ GeneralUIUtils.clickCreateButton();
+
+ return defineServiceetails;
+
+ }
+
+ public static ServiceReqDetails defineServiceDetails(User user) {
+ ServiceReqDetails service = new ServiceReqDetails();
+ service = ElementFactory.getDefaultService(ServiceCategoriesEnum.MOBILITY, user);
+ service.setVersion(INITIAL_VERSION);
+ service.setIcon(ICON_SERVICE_NAME);
+ service.setName(getRandomComponentName(SERVICE_NAME_PREFIX));
+
+ return service;
+ }
+
+ protected static String getRandomComponentName(String prefix) {
+ return prefix + new Random().nextInt(10000);
+ }
+
+ /**
+ * Waits Until service changed to requested lifeCycle State
+ *
+ * @param createServiceInUI
+ * @param requestedLifeCycleState
+ * @param user
+ * @return
+ */
+ public static Service waitForState(ServiceReqDetails createServiceInUI, LifecycleStateEnum requestedLifeCycleState,
+ User user) {
+ Supplier<Service> serviceGetter = () -> {
+ String resourceString = RestCDUtils.getService(createServiceInUI, user).getResponse();
+ return ResponseParser.convertServiceResponseToJavaObject(resourceString);
+ };
+ Function<Service, Boolean> verifier = res -> res.getLifecycleState() == requestedLifeCycleState;
+ return FunctionalInterfaces.retryMethodOnResult(serviceGetter, verifier);
+
+ }
+
+ /**
+ * This Method Approves service for distribution<br>
+ * It assumes governor role is already logged in
+ *
+ * @param createServiceInUI
+ */
+ public static void approveServiceForDistribution(ServiceReqDetails createServiceInUI) {
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(createServiceInUI.getName()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.APPROVE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.ACCEP_TESTING_MESSAGE.getValue())
+ .sendKeys("Service " + createServiceInUI.getName() + " Approved For Distribution");
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.waitForElementToDisappear(DataTestIdEnum.ModalItems.OK.getValue());
+ }
+
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/ServiceVerificator.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/ServiceVerificator.java
new file mode 100644
index 0000000000..0306df0638
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/ServiceVerificator.java
@@ -0,0 +1,55 @@
+package org.openecomp.sdc.uici.tests.verificator;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.http.HttpStatus;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.uici.tests.utilities.RestCDUtils;
+
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+public class ServiceVerificator {
+ public static void verifyNumOfComponentInstances(ServiceReqDetails createServiceInUI, int numOfVFC, User user) {
+ String responseAfterDrag = RestCDUtils.getService(createServiceInUI, user).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ int size = ((JSONArray) jsonResource.get("componentInstances")).size();
+ assertTrue(size == numOfVFC);
+ }
+
+ public static void verifyLinkCreated(ServiceReqDetails createServiceInUI, User user) {
+ String responseAfterDrag = RestCDUtils.getService(createServiceInUI, user).getResponse();
+ JSONObject jsonService = (JSONObject) JSONValue.parse(responseAfterDrag);
+ assertTrue(((JSONArray) jsonService.get("componentInstancesRelations")).size() == 1);
+
+ }
+
+ public static void verifyServiceCreated(ServiceReqDetails createServiceInUI, User user) {
+ assertTrue(RestCDUtils.getService(createServiceInUI, user).getErrorCode() == HttpStatus.SC_OK);
+
+ }
+
+ /**
+ * Verifies service is certified with version 1.0
+ *
+ * @param createServiceInUI
+ * @param user
+ */
+ public static void verifyServiceCertified(ServiceReqDetails createServiceInUI, User user) {
+ Supplier<RestResponse> serviceGetter = () -> FunctionalInterfaces.swallowException(
+ () -> ServiceRestUtils.getServiceByNameAndVersion(user, createServiceInUI.getName(), "1.0"));
+ Function<RestResponse, Boolean> serviceVerificator = restResp -> restResp.getErrorCode() == HttpStatus.SC_OK;
+ RestResponse certifiedResourceResopnse = FunctionalInterfaces.retryMethodOnResult(serviceGetter,
+ serviceVerificator);
+ assertTrue(certifiedResourceResopnse.getErrorCode() == HttpStatus.SC_OK);
+
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VerificatorUtil.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VerificatorUtil.java
new file mode 100644
index 0000000000..9e983b44d3
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VerificatorUtil.java
@@ -0,0 +1,27 @@
+package org.openecomp.sdc.uici.tests.verificator;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+/**
+ * Util Class For Verificators
+ *
+ * @author mshitrit
+ *
+ */
+public final class VerificatorUtil {
+
+ private VerificatorUtil() {
+ throw new IllegalAccessError();
+ }
+
+ public static void verifyWithRetry(Supplier<Boolean> verificator) {
+ Function<Boolean, Boolean> retryVerificationLogic = isVerified -> isVerified;
+ Boolean isVerifiedAfterRetries = FunctionalInterfaces.retryMethodOnResult(verificator, retryVerificationLogic);
+ assertTrue(isVerifiedAfterRetries);
+ }
+}
diff --git a/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VfVerificator.java b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VfVerificator.java
new file mode 100644
index 0000000000..a1c7ca002b
--- /dev/null
+++ b/ui-ci-dev/src/main/java/org/openecomp/sdc/uici/tests/verificator/VfVerificator.java
@@ -0,0 +1,143 @@
+package org.openecomp.sdc.uici.tests.verificator;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.http.HttpStatus;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.uici.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.uici.tests.utilities.RestCDUtils;
+
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openecomp.sdc.common.datastructure.FunctionalInterfaces;
+
+/**
+ * Class to hold Test Verifications relevant for VF
+ *
+ * @author mshitrit
+ *
+ */
+public final class VfVerificator {
+ private VfVerificator() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Verifies that the resource contains a certain number of component
+ * instances
+ *
+ * @param createResourceInUI
+ * @param numOfVFC
+ */
+ public static void verifyNumOfComponentInstances(ResourceReqDetails createResourceInUI, int numOfVFC) {
+ Supplier<Boolean> verificator = () -> {
+ String responseAfterDrag = RestCDUtils.getResource(createResourceInUI).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ int size = ((JSONArray) jsonResource.get("componentInstances")).size();
+ return size == numOfVFC;
+ };
+ VerificatorUtil.verifyWithRetry(verificator);
+ }
+
+ /**
+ * Verifies That the createResourceInUI is different that prevRIPos.
+ *
+ * @param createResourceInUI
+ * @param prevRIPos
+ * @param user
+ */
+ public static void verifyRILocationChanged(ResourceReqDetails createResourceInUI,
+ ImmutablePair<String, String> prevRIPos, User user) {
+ Supplier<Boolean> verificator = () -> {
+ ImmutablePair<String, String> currRIPos = ResourceUIUtils.getRIPosition(createResourceInUI, user);
+ final boolean isXLocationChanged = !prevRIPos.left.equals(currRIPos.left);
+ final boolean isYLocationChange = !prevRIPos.right.equals(currRIPos.right);
+ return isXLocationChanged || isYLocationChange;
+ };
+ VerificatorUtil.verifyWithRetry(verificator);
+ }
+
+ /**
+ * Verifies That resource contains two connected instances
+ *
+ * @param createResourceInUI
+ */
+ public static void verifyLinkCreated(ResourceReqDetails createResourceInUI) {
+ Supplier<Boolean> verificator = () -> {
+ String responseAfterDrag = RestCDUtils.getResource(createResourceInUI).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ return ((JSONArray) jsonResource.get("componentInstancesRelations")).size() == 1;
+ };
+ VerificatorUtil.verifyWithRetry(verificator);
+
+ }
+
+ /**
+ * Verifies That the VF is certified to version 1.0
+ *
+ * @param vfToVerify
+ */
+ public static void verifyResourceIsCertified(ResourceReqDetails vfToVerify) {
+ RestResponse certifiedResourceResopnse = RestCDUtils
+ .getResourceByNameAndVersionRetryOnFail(UserRoleEnum.ADMIN.getUserId(), vfToVerify.getName(), "1.0");
+ assertTrue(certifiedResourceResopnse.getErrorCode().equals(HttpStatus.SC_OK));
+
+ }
+
+ /**
+ * Verifies That the VF exist
+ *
+ * @param vfToVerify
+ */
+ public static void verifyResourceIsCreated(ResourceReqDetails vfToVerify) {
+ assertTrue(RestCDUtils.getResource(vfToVerify).getErrorCode() == HttpStatus.SC_OK);
+ }
+
+ /**
+ * Verify the resource contains the deployment artifacts in the list
+ *
+ * @param vfToVerify
+ * @param artifactTypeEnums
+ */
+ public static void verifyResourceContainsDeploymentArtifacts(ResourceReqDetails vfToVerify,
+ List<ArtifactTypeEnum> artifactTypeEnums) {
+ String resourceString = RestCDUtils.getResource(vfToVerify).getResponse();
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(resourceString);
+ List<String> foundArtifacts = new ArrayList<>();
+ if (resource.getDeploymentArtifacts() != null) {
+ foundArtifacts = resource.getDeploymentArtifacts().values().stream()
+ .map(artifact -> artifact.getArtifactType()).collect(Collectors.toList());
+ }
+ List<String> excpectedArtifacts = artifactTypeEnums.stream().map(e -> e.getType()).collect(Collectors.toList());
+ assertTrue(foundArtifacts.containsAll(excpectedArtifacts));
+
+ }
+
+ /**
+ * Verifies The life cycle State of the resource
+ *
+ * @param createResourceInUI
+ * @param requestedLifeCycleState
+ */
+ public static void verifyState(ResourceReqDetails createResourceInUI, LifecycleStateEnum requestedLifeCycleState) {
+ Resource resource = ResourceUIUtils.waitForState(createResourceInUI, requestedLifeCycleState);
+ assertTrue(resource.getLifecycleState() == requestedLifeCycleState);
+
+ }
+
+}
diff --git a/ui-ci-dev/src/main/resources/Files/CP.yml b/ui-ci-dev/src/main/resources/Files/CP.yml
new file mode 100644
index 0000000000..48b592265f
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/CP.yml
@@ -0,0 +1,65 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-ucpe-part-number:
+ type: string
+ vendor-name:
+ type: string
+ required: true
+ vendor-model:
+ type: string
+ required: true
+ total-vcpu:
+ type: integer
+ description: number of vCPUs
+ total-memory:
+ type: integer
+ description: GB
+ total-disk:
+ type: integer
+ description: GB
+ base-system-image-file-name:
+ type: string
+ linux-host-vendor:
+ type: string
+ linux-host-os-version:
+ type: version
+ base-system-software:
+ type: string
+ jdm-vcpu:
+ type: integer
+ jdm-memory:
+ type: integer
+ description: GB
+ jdm-disk:
+ type: integer
+ description: GB
+ jdm-version:
+ type: string
+ jcp-vcpu:
+ type: integer
+ jcp-memory:
+ type: integer
+ description: GB
+ jcp-disk:
+ type: integer
+ description: GB
+ jcp-version:
+ type: version
+ capabilities:
+ vnf_hosting:
+ type: tosca.capabilities.Container
+ description: Provides hosting capability for VNFs
+ WAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Wan]
+ description: external WAN1 n/w interface
+ occurrences: [1,2]
+ LAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Lan]
+ description: external LAN n/w interface
+ occurrences: [1,8] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/CP_LAN - Copy.yml b/ui-ci-dev/src/main/resources/Files/CP_LAN - Copy.yml
new file mode 100644
index 0000000000..224d61f2c9
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/CP_LAN - Copy.yml
@@ -0,0 +1,13 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.uCPE:
+ derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
diff --git a/ui-ci-dev/src/main/resources/Files/CP_LAN.yml b/ui-ci-dev/src/main/resources/Files/CP_LAN.yml
new file mode 100644
index 0000000000..a96084ba34
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/CP_LAN.yml
@@ -0,0 +1,19 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.LAN:
+ derived_from: org.openecomp.resource.cp.CP
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink_in:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualbinding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/CP_WAN.yml b/ui-ci-dev/src/main/resources/Files/CP_WAN.yml
new file mode 100644
index 0000000000..1bce457d43
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/CP_WAN.yml
@@ -0,0 +1,19 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.WAN:
+ derived_from: org.openecomp.resource.cp.CP
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink_in:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualbinding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/Heat-File 1.yaml b/ui-ci-dev/src/main/resources/Files/Heat-File 1.yaml
new file mode 100644
index 0000000000..d332078d35
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Heat-File 1.yaml
@@ -0,0 +1,791 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+ be0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/Heat-File 2.yaml b/ui-ci-dev/src/main/resources/Files/Heat-File 2.yaml
new file mode 100644
index 0000000000..d332078d35
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Heat-File 2.yaml
@@ -0,0 +1,791 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+ be0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/Heat-File.yaml b/ui-ci-dev/src/main/resources/Files/Heat-File.yaml
new file mode 100644
index 0000000000..d332078d35
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Heat-File.yaml
@@ -0,0 +1,791 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+ be0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/InValid_tosca_File .yml b/ui-ci-dev/src/main/resources/Files/InValid_tosca_File .yml
new file mode 100644
index 0000000000..4eea0a15ac
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/InValid_tosca_File .yml
@@ -0,0 +1,34 @@
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/ui-ci-dev/src/main/resources/Files/JDM_vf.yml b/ui-ci-dev/src/main/resources/Files/JDM_vf.yml
new file mode 100644
index 0000000000..5a7edd4aaf
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/JDM_vf.yml
@@ -0,0 +1,57 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vf.JDM:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/JDM_vfc.yml b/ui-ci-dev/src/main/resources/Files/JDM_vfc.yml
new file mode 100644
index 0000000000..b9c9ca0c4a
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/JDM_vfc.yml
@@ -0,0 +1,57 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.JDM:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/Sample_CSAR.csar b/ui-ci-dev/src/main/resources/Files/Sample_CSAR.csar
new file mode 100644
index 0000000000..fe95e79473
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Sample_CSAR.csar
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/Sample_CSAR2.csar b/ui-ci-dev/src/main/resources/Files/Sample_CSAR2.csar
new file mode 100644
index 0000000000..3001fe8222
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Sample_CSAR2.csar
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/UCPE_VFC.yml b/ui-ci-dev/src/main/resources/Files/UCPE_VFC.yml
new file mode 100644
index 0000000000..ef3966b68f
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/UCPE_VFC.yml
@@ -0,0 +1,65 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.uCPE:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-ucpe-part-number:
+ type: string
+ vendor-name:
+ type: string
+ required: true
+ vendor-model:
+ type: string
+ required: true
+ total-vcpu:
+ type: integer
+ description: number of vCPUs
+ total-memory:
+ type: integer
+ description: GB
+ total-disk:
+ type: integer
+ description: GB
+ base-system-image-file-name:
+ type: string
+ linux-host-vendor:
+ type: string
+ linux-host-os-version:
+ type: version
+ base-system-software:
+ type: string
+ jdm-vcpu:
+ type: integer
+ jdm-memory:
+ type: integer
+ description: GB
+ jdm-disk:
+ type: integer
+ description: GB
+ jdm-version:
+ type: string
+ jcp-vcpu:
+ type: integer
+ jcp-memory:
+ type: integer
+ description: GB
+ jcp-disk:
+ type: integer
+ description: GB
+ jcp-version:
+ type: version
+ capabilities:
+ vnf_hosting:
+ type: tosca.capabilities.Container
+ description: Provides hosting capability for VNFs
+ WAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Wan]
+ description: external WAN1 n/w interface
+ occurrences: [1,2]
+ LAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Lan]
+ description: external LAN n/w interface
+ occurrences: [1,8] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/VF.yml b/ui-ci-dev/src/main/resources/Files/VF.yml
new file mode 100644
index 0000000000..ec089900ad
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/VF.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vf.VFF:
+ derived_from: tosca.nodes.Root
+ properties:
+ vendor:
+ type: string
+ required: false
+ vl_name:
+ type: string
+ required: false
+ capabilities:
+ virtual_linkable:
+ type: tosca.capabilities.network.Linkable
+
+ \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/VFC.yml b/ui-ci-dev/src/main/resources/Files/VFC.yml
new file mode 100644
index 0000000000..853ed35374
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/VFC.yml
@@ -0,0 +1,77 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.vRouter:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ management-v6-address:
+ type: string
+ nm-lan-v6-address:
+ type: string
+ nm-lan-v6-prefix-length:
+ type: string
+ management-v4-address:
+ type: string
+ nm-lan-v4-address:
+ type: string
+ nm-lan-v4-prefix-length:
+ type: string
+ routing-instance-name:
+ type: string
+ routing-instances:
+ type: map
+ entry_schema:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable
+ occurrences: [1,UNBOUNDED]
+ \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/VFCWithAttributes.yml b/ui-ci-dev/src/main/resources/Files/VFCWithAttributes.yml
new file mode 100644
index 0000000000..133fd02cef
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/VFCWithAttributes.yml
@@ -0,0 +1,43 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.VFC:
+ derived_from: tosca.nodes.Root
+ properties:
+ jcp-memory:
+ type: integer
+ description: GB
+ jcp-disk:
+ type: integer
+ description: GB
+ jcp-version:
+ type: version
+ attributes:
+ my_attr:
+ type: integer
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ capabilities:
+ vnf_hosting:
+ type: tosca.capabilities.Container
+ description: Provides hosting capability for VNFs
+ WAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Wan]
+ description: external WAN1 n/w interface
+ occurrences: [1,2]
+ LAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Lan]
+ description: external LAN n/w interface
+ occurrences: [1,8] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/VL.yml b/ui-ci-dev/src/main/resources/Files/VL.yml
new file mode 100644
index 0000000000..74a2405af2
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/VL.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vl.VL1we23:
+ derived_from: tosca.nodes.Root
+ properties:
+ vendor:
+ type: string
+ required: false
+ vl_name:
+ type: string
+ required: false
+ capabilities:
+ virtual_linkable:
+ type: tosca.capabilities.network.Linkable
+
+ \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/Valid xml.xml b/ui-ci-dev/src/main/resources/Files/Valid xml.xml
new file mode 100644
index 0000000000..0d67e48340
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Valid xml.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<dirs>
+<entry loc="C:\Program Files\Java\jdk1.7.0_79" stamp="1449076618518"/>
+</dirs>
diff --git a/ui-ci-dev/src/main/resources/Files/Valid_tosca_Mycompute.yml b/ui-ci-dev/src/main/resources/Files/Valid_tosca_Mycompute.yml
new file mode 100644
index 0000000000..8fac5e16a8
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Valid_tosca_Mycompute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vf.Database:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/ui-ci-dev/src/main/resources/Files/Valid_tosca_ReplaceTest.yml b/ui-ci-dev/src/main/resources/Files/Valid_tosca_ReplaceTest.yml
new file mode 100644
index 0000000000..90e771dab1
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/Valid_tosca_ReplaceTest.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.VF.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env b/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env
new file mode 100644
index 0000000000..b494d8c270
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env
@@ -0,0 +1,6 @@
+parameters:
+ pcrf_oam_vol_size: 500
+ pcrf_oam_volume_silver-1: Silver
+ pcrf_oam_volume_silver-2: Silver
+ pcrf_oam_vol_name_1: sde1-pcrfx01-oam001-vol-1
+ pcrf_oam_vol_name_2: sde1-pcrfx01-oam001-vol-2 \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.env b/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.env
new file mode 100644
index 0000000000..cf7cf710ce
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.env
@@ -0,0 +1,18 @@
+parameters:
+ pcrf_oam_server_names: ZRDM1PCRF01OAM001,ZRDM1PCRF01OAM002
+ pcrf_oam_image_name: PCRF_8.995-ATTM1.0.3.qcow2
+ pcrf_oam_flavor_name: lc.4xlarge4
+ availabilityzone_name: nova
+ pcrf_cps_net_name: int_pcrf_net_0
+ pcrf_cps_net_ips: 172.26.16.111,172.26.16.112
+ pcrf_arbiter_vip: 172.26.16.115
+ pcrf_cps_net_mask: 255.255.255.0
+ pcrf_oam_net_name: oam_protected_net_0
+ pcrf_oam_net_ips: 107.239.64.117,107.239.64.118
+ pcrf_oam_net_gw: 107.239.64.1
+ pcrf_oam_net_mask: 255.255.248.0
+ pcrf_oam_volume_id_1: a4aa05fb-fcdc-457b-8077-6845fdfc3257
+ pcrf_oam_volume_id_2: 93d8fc1f-f1c3-4933-86b2-039881ee910f
+ pcrf_security_group_name: nimbus_security_group
+ pcrf_vnf_id: 730797234b4a40aa99335157b02871cd
+
diff --git a/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml b/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml
new file mode 100644
index 0000000000..6636eba210
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml
@@ -0,0 +1,108 @@
+heat_template_version: 2013-05-23
+
+description: heat template that creates multiple PCRF OAM nodes stack
+
+parameters:
+ pcrf_oam_server_names:
+ type: comma_delimited_list
+ label: PCRF OAM server names
+ description: name of the PCRF OAM instance
+ pcrf_oam_image_name:
+ type: string
+ label: PCRF OAM image name
+ description: PCRF OAM image name
+ pcrf_oam_flavor_name:
+ type: string
+ label: PCRF OAM flavor name
+ description: flavor name of PCRF OAM instance
+ availabilityzone_name:
+ type: string
+ label: availabilityzone name
+ description: availabilityzone name
+ pcrf_cps_net_name:
+ type: string
+ label: CPS network name
+ description: CPS network name
+ pcrf_cps_net_ips:
+ type: comma_delimited_list
+ label: CPS network ips
+ description: CPS network ips
+ pcrf_cps_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_arbiter_vip:
+ type: string
+ label: OAM Arbiter LB VIP
+ description: OAM Arbiter LB VIP
+ pcrf_oam_net_name:
+ type: string
+ label: OAM network name
+ description: OAM network name
+ pcrf_oam_net_ips:
+ type: comma_delimited_list
+ label: OAM network ips
+ description: OAM network ips
+ pcrf_oam_net_gw:
+ type: string
+ label: CPS network gateway
+ description: CPS network gateway
+ pcrf_oam_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_oam_volume_id_1:
+ type: string
+ label: CPS OAM 001 Cinder Volume
+ description: CPS OAM 001 Cinder Volumes
+ pcrf_oam_volume_id_2:
+ type: string
+ label: CPS OAM 002 Cinder Volume
+ description: CPS OAM 002 Cinder Volumes
+ pcrf_security_group_name:
+ type: string
+ label: security group name
+ description: the name of security group
+ pcrf_vnf_id:
+ type: string
+ label: PCRF VNF Id
+ description: PCRF VNF Id
+
+resources:
+ server_pcrf_oam_001:
+ type: nested-oam_v0.2.yaml
+ properties:
+ pcrf_oam_server_name: { get_param: [pcrf_oam_server_names, 0] }
+ pcrf_oam_image_name: { get_param: pcrf_oam_image_name }
+ pcrf_oam_flavor_name: { get_param: pcrf_oam_flavor_name }
+ availabilityzone_name: { get_param: availabilityzone_name }
+ pcrf_security_group_name: { get_param: pcrf_security_group_name }
+ pcrf_oam_volume_id: { get_param: pcrf_oam_volume_id_1 }
+ pcrf_cps_net_name: { get_param: pcrf_cps_net_name }
+ pcrf_cps_net_ip: { get_param: [pcrf_cps_net_ips, 0] }
+ pcrf_cps_net_mask: { get_param: pcrf_cps_net_mask }
+ pcrf_oam_net_name: { get_param: pcrf_oam_net_name }
+ pcrf_oam_net_ip: { get_param: [pcrf_oam_net_ips, 0] }
+ pcrf_oam_net_mask: { get_param: pcrf_oam_net_mask }
+ pcrf_oam_net_gw: { get_param: pcrf_oam_net_gw }
+ pcrf_arbiter_vip: { get_param: pcrf_arbiter_vip }
+ pcrf_vnf_id: {get_param: pcrf_vnf_id}
+
+ server_pcrf_oam_002:
+ type: nested-oam_v0.2.yaml
+ properties:
+ pcrf_oam_server_name: { get_param: [pcrf_oam_server_names, 1] }
+ pcrf_oam_image_name: { get_param: pcrf_oam_image_name }
+ pcrf_oam_flavor_name: { get_param: pcrf_oam_flavor_name }
+ availabilityzone_name: { get_param: availabilityzone_name }
+ pcrf_security_group_name: { get_param: pcrf_security_group_name }
+ pcrf_oam_volume_id: { get_param: pcrf_oam_volume_id_2 }
+ pcrf_cps_net_name: { get_param: pcrf_cps_net_name }
+ pcrf_cps_net_ip: { get_param: [pcrf_cps_net_ips, 1] }
+ pcrf_cps_net_mask: { get_param: pcrf_cps_net_mask }
+ pcrf_oam_net_name: { get_param: pcrf_oam_net_name }
+ pcrf_oam_net_ip: { get_param: [pcrf_oam_net_ips, 1] }
+ pcrf_oam_net_mask: { get_param: pcrf_oam_net_mask }
+ pcrf_oam_net_gw: { get_param: pcrf_oam_net_gw }
+ pcrf_arbiter_vip: { get_param: pcrf_arbiter_vip }
+ pcrf_vnf_id: {get_param: pcrf_vnf_id}
diff --git a/ui-ci-dev/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml b/ui-ci-dev/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml
new file mode 100644
index 0000000000..564104174a
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml
@@ -0,0 +1,80 @@
+heat_template_version: 2013-05-23
+
+description: heat template that creates PCRF Cluman stack
+
+parameters:
+ pcrf_pcm_server_names:
+ type: comma_delimited_list
+ label: PCRF CM server names
+ description: name of the PCRF CM instance
+ pcrf_pcm_image_name:
+ type: string
+ label: PCRF CM image name
+ description: PCRF CM image name
+ pcrf_pcm_flavor_name:
+ type: string
+ label: PCRF CM flavor name
+ description: flavor name of PCRF CM instance
+ availabilityzone_name:
+ type: string
+ label: availabilityzone name
+ description: availabilityzone name
+ pcrf_cps_net_name:
+ type: string
+ label: CPS network name
+ description: CPS network name
+ pcrf_cps_net_ips:
+ type: comma_delimited_list
+ label: CPS network ips
+ description: CPS network ips
+ pcrf_cps_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_oam_net_name:
+ type: string
+ label: OAM network name
+ description: OAM network name
+ pcrf_oam_net_ips:
+ type: comma_delimited_list
+ label: OAM network ips
+ description: OAM network ips
+ pcrf_oam_net_gw:
+ type: string
+ label: CPS network gateway
+ description: CPS network gateway
+ pcrf_oam_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_pcm_volume_id_1:
+ type: string
+ label: CPS Cluman Cinder Volume
+ description: CPS Cluman Cinder Volume
+ pcrf_security_group_name:
+ type: string
+ label: security group name
+ description: the name of security group
+ pcrf_vnf_id:
+ type: string
+ label: PCRF VNF Id
+ description: PCRF VNF Id
+
+resources:
+ server_pcrf_pcm_001:
+ type: nested-pcm_v0.2.yaml
+ properties:
+ pcrf_pcm_server_name: { get_param: [pcrf_pcm_server_names, 0] }
+ pcrf_pcm_image_name: { get_param: pcrf_pcm_image_name }
+ pcrf_pcm_flavor_name: { get_param: pcrf_pcm_flavor_name }
+ availabilityzone_name: { get_param: availabilityzone_name }
+ pcrf_security_group_name: { get_param: pcrf_security_group_name }
+ pcrf_pcm_volume_id: { get_param: pcrf_pcm_volume_id_1 }
+ pcrf_cps_net_name: { get_param: pcrf_cps_net_name }
+ pcrf_cps_net_ip: { get_param: [pcrf_cps_net_ips, 0] }
+ pcrf_cps_net_mask: { get_param: pcrf_cps_net_mask }
+ pcrf_oam_net_name: { get_param: pcrf_oam_net_name }
+ pcrf_oam_net_ip: { get_param: [pcrf_oam_net_ips, 0] }
+ pcrf_oam_net_mask: { get_param: pcrf_oam_net_mask }
+ pcrf_oam_net_gw: { get_param: pcrf_oam_net_gw }
+ pcrf_vnf_id: {get_param: pcrf_vnf_id}
diff --git a/ui-ci-dev/src/main/resources/Files/myYang.xml b/ui-ci-dev/src/main/resources/Files/myYang.xml
new file mode 100644
index 0000000000..0d86d213e6
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/myYang.xml
@@ -0,0 +1,8 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <bean id="muranoLogic" class="org.openecomp.sdc.impl.MuranoLogic"> </bean>
+
+</beans> \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/mycompute.yml b/ui-ci-dev/src/main/resources/Files/mycompute.yml
new file mode 100644
index 0000000000..c8a0c03384
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/mycompute.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vfc.mycompute:
+ derived_from: tosca.nodes.Compute
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ properties:
+ propertyForTest:
+ type: string
+ description: test
+ required: true
+ default: success
+ # min_instances property should override property from tosca.capabilities.Scalable
+ min_instances:
+ type: integer
+ default: 3
+
diff --git a/ui-ci-dev/src/main/resources/Files/service_with_inputs.csar b/ui-ci-dev/src/main/resources/Files/service_with_inputs.csar
new file mode 100644
index 0000000000..c4d4881fec
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/service_with_inputs.csar
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/vADTRAN.zip b/ui-ci-dev/src/main/resources/Files/vADTRAN.zip
new file mode 100644
index 0000000000..3ecde2c7ec
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/vADTRAN.zip
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/vCDN.zip b/ui-ci-dev/src/main/resources/Files/vCDN.zip
new file mode 100644
index 0000000000..51e654a841
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/vCDN.zip
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/vFW_VF.yml b/ui-ci-dev/src/main/resources/Files/vFW_VF.yml
new file mode 100644
index 0000000000..100883e399
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/vFW_VF.yml
@@ -0,0 +1,58 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+
+ org.openecomp.resource.vf.vFW:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: version
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/vFW_VFC.yml b/ui-ci-dev/src/main/resources/Files/vFW_VFC.yml
new file mode 100644
index 0000000000..d0814c43aa
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/vFW_VFC.yml
@@ -0,0 +1,58 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+
+ org.openecomp.resource.vfc.vFW:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: version
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/vRouter_vfc.yml b/ui-ci-dev/src/main/resources/Files/vRouter_vfc.yml
new file mode 100644
index 0000000000..95ffe959de
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/vRouter_vfc.yml
@@ -0,0 +1,78 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+
+node_types:
+ org.openecomp.resource.vfc.vRouter:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ management-v6-address:
+ type: string
+ nm-lan-v6-address:
+ type: string
+ nm-lan-v6-prefix-length:
+ type: string
+ management-v4-address:
+ type: string
+ nm-lan-v4-address:
+ type: string
+ nm-lan-v4-prefix-length:
+ type: string
+ routing-instance-name:
+ type: string
+ routing-instances:
+ type: map
+ entry_schema:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable
+ occurrences: [1,UNBOUNDED]
+ \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/valid HEAT_ENV files.env b/ui-ci-dev/src/main/resources/Files/valid HEAT_ENV files.env
new file mode 100644
index 0000000000..e576c0f67d
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/valid HEAT_ENV files.env
@@ -0,0 +1,54 @@
+parameters:
+ flavor_scp_be_id: m1.small
+ flavor_scp_fe_id: m1.small
+ flavor_smp_id: m1.small
+ flavor_db_id: m1.small
+ image_scp_be_id: CCLINUX
+ image_scp_fe_id: CCLINUX
+ image_smp_id: CCLINUX
+ image_db_id: CCLINUX
+
+ int_vscp_fe_cluster_net_id: int_vscp_fe_cluster_net
+ int_vscp_fe_cluster_cidr: 172.26.2.0/24
+ int_vscp_cluster_net_id: int_vscp_cluster_net
+ int_vscp_cluster_cidr: 172.26.3.0/24
+ int_vscp_db_network_net_id: int_vscp_db_network_net
+ int_vscp_db_network_cidr: 172.26.1.0/24
+
+ SIGNET_vrf_A1_direct_net_id: SIGNET_vrf_A1_direct_net
+ SIGNET_vrf_B1_direct_net_id: SIGNET_vrf_B1_direct_net
+ Cricket_OCS_protected_net_id: Cricket_OCS_protected_net
+# OAM_direct_net_id: OAM_net
+# OAM_direct_net_id: oam-direct-net
+ OAM_direct_net_id: Marks_OAM_direct_net
+
+ be0_Cricket_OCS_protected_ips: 107.239.15.17
+ be1_Cricket_OCS_protected_ips: 107.239.15.18
+ be2_Cricket_OCS_protected_ips: 107.239.15.19
+ be3_Cricket_OCS_protected_ips: 107.239.15.20
+ be4_Cricket_OCS_protected_ips: 107.239.15.21
+ be0_OAM_direct_ips: 10.250.10.33
+ be1_OAM_direct_ips: 10.250.10.34
+ be2_OAM_direct_ips: 10.250.10.35
+ be3_OAM_direct_ips: 10.250.10.36
+ be4_OAM_direct_ips: 10.250.10.37
+ fe0_SIGNET_vrf_A1_direct_ips: 172.26.4.1
+ fe0_OAM_direct_ips: 10.250.10.38
+ fe1_SIGNET_vrf_B1_direct_ips: 172.26.4.5
+ fe1_OAM_direct_ips: 10.250.10.39
+ smp0_OAM_direct_ips: 10.250.10.40
+ smp1_OAM_direct_ips: 10.250.10.41
+ db0_OAM_direct_ips: 10.250.10.42
+ db1_OAM_direct_ips: 10.250.10.43
+
+ vm_scp_be0_name: vSCP_BE0
+ vm_scp_be1_name: vSCP_BE1
+ vm_scp_be2_name: vSCP_BE2
+ vm_scp_be3_name: vSCP_BE3
+ vm_scp_be4_name: vSCP_BE4
+ vm_scp_fe0_name: vSCP_FE0
+ vm_scp_fe1_name: vSCP_FE1
+ vm_smp0_name: vSMP0
+ vm_smp1_name: vSMP1
+ vm_db0_name: vDB0
+ vm_db1_name: vDB1
diff --git a/ui-ci-dev/src/main/resources/Files/validHEATfiles.yaml b/ui-ci-dev/src/main/resources/Files/validHEATfiles.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/validHEATfiles.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/valid_vf.csar b/ui-ci-dev/src/main/resources/Files/valid_vf.csar
new file mode 100644
index 0000000000..01bf159071
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/valid_vf.csar
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/vf_with_groups.csar b/ui-ci-dev/src/main/resources/Files/vf_with_groups.csar
new file mode 100644
index 0000000000..61ea8cee20
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/vf_with_groups.csar
Binary files differ
diff --git a/ui-ci-dev/src/main/resources/Files/yamlSample.yml b/ui-ci-dev/src/main/resources/Files/yamlSample.yml
new file mode 100644
index 0000000000..10ccf71d51
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/yamlSample.yml
@@ -0,0 +1,5 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: org.openecomp.resource.cp.root \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/Files/yamlSample2.yml b/ui-ci-dev/src/main/resources/Files/yamlSample2.yml
new file mode 100644
index 0000000000..10ccf71d51
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/Files/yamlSample2.yml
@@ -0,0 +1,5 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: org.openecomp.resource.cp.root \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/ci/conf/credentials.yaml b/ui-ci-dev/src/main/resources/ci/conf/credentials.yaml
new file mode 100644
index 0000000000..63a4280264
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/conf/credentials.yaml
@@ -0,0 +1,48 @@
+ designer: {
+ username: m99121,
+ password: 66-Percent,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ admin: {
+ username: m99122,
+ password: 98-Degrees,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ ops: {
+ username: m99123,
+ password: 17-Diameter,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ tester: {
+ username: m99124,
+ password: 802-NotaGroup,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ governor: {
+ username: m99125,
+ password: 142-Officiant,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ product_strategist: {
+ username: m99126,
+ password: 1910-FruitGum,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ product_manager: {
+ username: m99127,
+ password: 747-Airplane,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ product_local: {
+ username: pm0001,
+ password: 123123a,
+ firstname: ASDC,
+ lastname: KASPIN
+ } \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/ci/conf/log4j.properties b/ui-ci-dev/src/main/resources/ci/conf/log4j.properties
new file mode 100644
index 0000000000..3e159ec8df
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/conf/log4j.properties
@@ -0,0 +1,34 @@
+# Define the root logger with appender file
+log4j.rootLogger = DEBUG, FILE, stdout
+
+# Define the file appender
+log4j.appender.FILE=org.apache.log4j.RollingFileAppender
+log4j.appender.FILE.File=${targetlog}logs/ci-log.out
+
+# Define the layout for file appender
+log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p [%10c] : %m%n
+
+# Set the maximum file size before rollover
+log4j.appender.FILE.maxFileSize=5MB
+
+# Set the the backup index
+log4j.appender.FILE.maxBackupIndex=10
+
+
+#############################################################
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %10c:%L - %m%n
+
+log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG
+log4j.logger.com.thinkaurelius.titan.diskstorage.cassandra.CassandraTransaction=INFO, FILE, stdout
+
+log4j.logger.org.openecomp.sdc.ci.tests.utils=TRACE, FILE, stdout
+log4j.additivity.org.openecomp.sdc.ci.tests.utils=false
+
+
diff --git a/ui-ci-dev/src/main/resources/ci/conf/sdc-packages.yaml b/ui-ci-dev/src/main/resources/ci/conf/sdc-packages.yaml
new file mode 100644
index 0000000000..dcb78eefc1
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/conf/sdc-packages.yaml
@@ -0,0 +1,2 @@
+packages:
+ - org.openecomp.sdc.ci.tests.execute.resourceui \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/ci/conf/sdc.yaml b/ui-ci-dev/src/main/resources/ci/conf/sdc.yaml
new file mode 100644
index 0000000000..bdca2ab972
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/conf/sdc.yaml
@@ -0,0 +1,80 @@
+outputFolder: target
+reportName: index.html
+catalogBeHost: behost
+catalogFeHost: fehost
+esHost: eshost
+disributionClientHost: disClient
+catalogFePort: 8181
+catalogBePort: 8080
+disributionClientPort: 8181
+esPort: 9200
+neoHost: neoHost
+neoPort: 7474
+neoDBusername: neo4j
+neoDBpassword: 123456
+#url: http://localhost:9000/#/dashboard
+url: http://localhost:8181/sdc1/proxy-designer1#/dashboard
+
+webSealSimulatorUrl: http://localhost:8285/sdc1
+remoteTestingMachineIP: 1.2.3.4
+remoteTestingMachinePort: 5555
+remoteTesting: false
+
+resourceConfigDir: src/test/resources/CI/tests
+componentsConfigDir: src/test/resources/CI/components
+importResourceConfigDir: ../catalog-be/src/main/resources/import/tosca/capability-types
+importResourceTestsConfigDir: src/test/resources/CI/importResourceTests
+errorConfigurationFile: ../catalog-be/src/main/resources/config/error-configuration.yaml
+configurationFile: ../catalog-be/src/main/resources/config/configuration.yaml
+importTypesConfigDir: src/test/resources/CI/importTypesTest
+
+
+titanPropertiesFile: src/main/resources/ci/conf/titan.properties
+cassandraHost: 127.0.0.1
+cassandraAuthenticate: false
+cassandraUsername: koko
+cassandraPassword: bobo
+cassandraSsl: false
+cassandraTruststorePath : /path/path
+cassandraTruststorePassword : 123123
+cassandraAuditKeySpace: sdcAudit
+cassandraArtifactKeySpace: sdcArtifact
+
+stopOnClassFailure: false
+
+#List of non-abstract resources to keep during titan cleanup between tests
+#Only 1.0 version will be kept
+resourcesNotToDelete:
+ - Compute
+ - Database
+ - ObjectStorage
+ - BlockStorage
+ - LoadBalancer
+ - Port
+ - Network
+ - Root
+ - ContainerApplication
+ - ContainerRuntime
+ - DBMS
+ - SoftwareComponent
+ - WebApplication
+ - WebServer
+ - CinderVolume
+ - ContrailVirtualNetwork
+ - NeutronNet
+ - NeutronPort
+ - NovaServer
+#Resource categories to keep (including all their subcategories)
+resourceCategoriesNotToDelete:
+ - Generic
+ - Network L2-3
+ - Network L4+
+ - Application L4+
+ - Network Connectivity
+
+#Service categories to keep
+serviceCategoriesNotToDelete:
+ - Mobility
+ - Network L1-3
+ - Network L4
+ - VoIP Call Control \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/ci/conf/titan.properties b/ui-ci-dev/src/main/resources/ci/conf/titan.properties
new file mode 100644
index 0000000000..94d12cfba0
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/conf/titan.properties
@@ -0,0 +1,7 @@
+storage.backend=cassandra
+storage.hostname=cassandrahost
+storage.port=9160
+
+cache.db-cache-clean-wait = 20
+cache.db-cache-time = 180000
+cache.db-cache-size = 0.5 \ No newline at end of file
diff --git a/ui-ci-dev/src/main/resources/ci/scripts/startTest.sh b/ui-ci-dev/src/main/resources/ci/scripts/startTest.sh
new file mode 100644
index 0000000000..27933699a5
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/scripts/startTest.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+
+TOMCAT_DIR=/home/apache-tomcat-7.0.41/webapps/sdc-ci
+
+function usage {
+ echo "Usage: $0 <jar file>"
+}
+
+function exitOnError() {
+ if [ $1 -ne 0 ]
+ then
+ echo "Failed running task $2"
+ exit 2
+ fi
+}
+
+if [ $# -lt 1 ]
+then
+ usage
+ exit 2
+fi
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+LOGS_PROP_FILE=file:${FULL_PATH}/../conf/log4j.properties
+#############################################
+TARGET_DIR=${FULL_PATH}/../target
+TARGET_LOG_DIR="${TARGET_DIR}/"
+CONF_FILE=${FULL_PATH}/../conf/sdc.yaml
+DEBUG=true
+MainClass=org.openecomp.sdc.ci.tests.run.StartTest
+
+JAR_FILE=$1
+
+#TARGET_DIR=`echo ${TARGET_DIR} | sed 's/\//\//g'`
+#echo $TARGET_DIR
+
+TESTS_DIR=/opt/app/sdc/ci/resources/tests
+COMPONENTS_DIR=/opt/app/sdc/ci/resources/components
+
+
+sed -i 's#\(outputFolder:\).*#\1 '${TARGET_DIR}'#g' $CONF_FILE
+sed -i 's#\(resourceConfigDir:\).*#\1 '${TESTS_DIR}'#g' $CONF_FILE
+sed -i 's#\(componentsConfigDir:\).*#\1 '${COMPONENTS_DIR}'#g' $CONF_FILE
+
+
+
+mkdir -p ${TARGET_DIR}
+if [ -d ${TARGET_DIR} ]
+then
+ rm -rf ${TARGET_DIR}/*
+ exitOnError $? "Failed_to_delete_target_dir"
+fi
+
+
+debug_port=8800
+#JAVA_OPTION="-javaagent:/var/tmp/jacoco/lib/jacocoagent.jar=destfile=jacoco-it.exec"
+JAVA_OPTION=""
+case "$2" in
+ -debug) echo "Debug mode, Listen on port $debug_port"; JAVA_OPTION="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${debug_port}" ;;
+ "") echo "Standard mode";;
+ *) echo "USAGE: startTest.sh [-debug]";;
+esac
+
+#cmd="java $JAVA_OPTION -Dconfig.resource=attodlit.conf -Dlog4j.configuration=file:./conf/log4j.properties -cp #att-odl-it_0.0.1-SNAPSHOT-jar-with-dependencies.jar org.openecomp.d2.it.StartTest"
+
+#cmd="java $JAVA_OPTION -Dconfig.resource=attsdc.conf -Ddebug=true -Dlog4j.configuration=file:./conf/log4j.properties -cp uber-ci-1.0.0-SNAPSHOT.jar org.openecomp.sdc.ci.tests.run.StartTest"
+
+
+cmd="java $JAVA_OPTION -DdisplayException=true -Dtargetlog=${TARGET_LOG_DIR} -Dconfig.resource=${CONF_FILE} -Ddebug=${DEBUG} -Dlog4j.configuration=${LOGS_PROP_FILE} -cp $JAR_FILE ${MainClass}"
+
+#echo $cmd
+#console=`$cmd`
+
+
+
+if [ $DEBUG == "true" ]
+then
+ $cmd
+else
+ $cmd >> /dev/null
+fi
+status=`echo $?`
+
+#echo "console=$console"
+#echo "status=$status"
+#tomcat=`ps -ef | grep tomcat | grep java | wc -l`
+
+#if [ $tomcat == 0 ]; then
+# echo "Bring tomcat up"
+# apache-tomcat-7.0.41/bin/startup.sh
+#fi
+
+#`rm -rf ./html/*.html`
+#`mv *.html ./html/`
+
+
+if [ -d ${TOMCAT_DIR} ]
+then
+
+ cp ${TARGET_DIR}/*.html ${TOMCAT_DIR}
+ mv ${TOMCAT_DIR}/SDC-testReport.html ${TOMCAT_DIR}/index.html
+fi
+
+#echo "tomcat=$tomcat"
+#ip=`ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p' | grep 172.20`
+
+#echo "Report url: http://$ip:8090/att-odl-it/"
+
+echo "##################################################"
+echo "################# status is $status "
+echo "##################################################"
+
+exit $status
+
diff --git a/ui-ci-dev/src/main/resources/ci/testSuites/fullTests.xml b/ui-ci-dev/src/main/resources/ci/testSuites/fullTests.xml
new file mode 100644
index 0000000000..9f912e58b8
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/testSuites/fullTests.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="uiFullTests" configfailurepolicy="continue" verbose="2">
+ <parameter name="clean-type" value="FULL" /> <!--Valid Values are: PARTIAL, FULL, NONE -->
+ <test name="Vfc Tests">
+ <classes>
+ <class name="org.openecomp.sdc.uici.tests.execute.vfc.VfcBasicTests"></class>
+ </classes>
+ </test>
+
+ <test name="VF Tests">
+ <classes>
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfBasicTests" />
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfCanvasTests" />
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfOnboardingTests" />
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfDeploymentTests"/>
+ </classes>
+ </test>
+
+ <test name="Service Tests">
+ <classes>
+ <class name="org.openecomp.sdc.uici.tests.execute.service.ServiceBasicTests"></class>
+ <class name="org.openecomp.sdc.uici.tests.execute.service.ServiceInputsTests"></class>
+ </classes>
+ </test>
+</suite>
diff --git a/ui-ci-dev/src/main/resources/ci/testSuites/sanity.xml b/ui-ci-dev/src/main/resources/ci/testSuites/sanity.xml
new file mode 100644
index 0000000000..99e8765038
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/ci/testSuites/sanity.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="uiSanity" configfailurepolicy="continue" verbose="2">
+ <parameter name="clean-type" value="FULL" /> <!--Valid Values are: PARTIAL, FULL, NONE -->
+ <test name="Vfc Tests">
+ <classes>
+ <class name="org.openecomp.sdc.uici.tests.execute.vfc.VfcBasicTests">
+ <methods>
+ <include name="testImportVfc" />
+ <include name="testUpdateTypeForAttributeOfVfc" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="VF Tests">
+ <classes>
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfBasicTests">
+ <methods>
+ <include name="testImportVf" />
+ <include name="testUpdateInstanceAttributeValue" />
+ <include name="testVfCertification" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfCanvasTests">
+ <methods>
+ <include name="testCanvasVFSanity" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.uici.tests.execute.vf.VfDeploymentTests">
+ <methods>
+ <include name="testUpdateModuleNameSanity" />
+ <include name="testTabsViewSanity"/>
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+ <test name="Service Tests">
+ <classes>
+ <class name="org.openecomp.sdc.uici.tests.execute.service.ServiceBasicTests">
+ <methods>
+ <include name="testLinkTwoRI" />
+ <include name="testBuildServiceForDistribution" />
+ </methods>
+ </class>
+ <class name="org.openecomp.sdc.uici.tests.execute.service.ServiceInputsTests">
+ <methods>
+ <include name="testInputsSanity" />
+ </methods>
+ </class>
+ </classes>
+ </test>
+
+</suite>
diff --git a/ui-ci-dev/src/main/resources/images/gizmorambo.jpg b/ui-ci-dev/src/main/resources/images/gizmorambo.jpg
new file mode 100644
index 0000000000..c9a8fe8a64
--- /dev/null
+++ b/ui-ci-dev/src/main/resources/images/gizmorambo.jpg
Binary files differ
diff --git a/ui-ci-dev/src/test/Completetheform.js b/ui-ci-dev/src/test/Completetheform.js
new file mode 100644
index 0000000000..7e8b7572cc
--- /dev/null
+++ b/ui-ci-dev/src/test/Completetheform.js
@@ -0,0 +1,3 @@
+/**
+ * New node file
+ */
diff --git a/ui-ci-dev/tarball.xml b/ui-ci-dev/tarball.xml
new file mode 100644
index 0000000000..9262e6f02b
--- /dev/null
+++ b/ui-ci-dev/tarball.xml
@@ -0,0 +1,66 @@
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar</format>
+ </formats>
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>${project.artifactId}-${project.version}-jar-with-dependencies.jar</destName>
+ </file>
+ <file>
+ <source>../asdc-tests/src/main/resources/ci/scripts/startTest.sh</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>startTest.sh</destName>
+ </file>
+ <!--file> <source>src/main/resources/ci/scripts/postinstall</source> <outputDirectory>./</outputDirectory>
+ <destName>postinstall</destName> </file -->
+ <file>
+ <source>../asdc-tests/src/main/resources/ci/conf/sdc.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>sdc.yaml</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/conf/sdc-packages.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>sdc-packages.yaml</destName>
+ </file>
+ <file>
+ <source>../asdc-tests/src/main/resources/ci/conf/log4j.properties</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>log4j.properties</destName>
+ </file>
+<!-- <file>
+ <source>src/main/resources/ci/conf/titan.properties</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>titan.properties</destName>
+ </file> -->
+<!-- <file>
+ <source>${project.basedir}/../catalog-be/src/main/resources/config/error-configuration.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>error-configuration.yaml</destName>
+ </file> -->
+ <file>
+ <source>src/main/resources/ci/conf/credentials.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>credentials.yaml</destName>
+ </file>
+
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <directory>src/test/resources</directory>
+ <outputDirectory>./</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/ci/testSuites</directory>
+ <outputDirectory>./testSuites</outputDirectory>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/ui-ci/.gitignore b/ui-ci/.gitignore
new file mode 100644
index 0000000000..a3b2274a53
--- /dev/null
+++ b/ui-ci/.gitignore
@@ -0,0 +1,3 @@
+/bin/
+test-output/
+/target/
diff --git a/ui-ci/pom.xml b/ui-ci/pom.xml
new file mode 100644
index 0000000000..f5236e40fa
--- /dev/null
+++ b/ui-ci/pom.xml
@@ -0,0 +1,260 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>ui-ci</artifactId>
+ <description>Selenium tests for the SDnC Application</description>
+
+ <parent>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-main</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-java</artifactId>
+ <version>2.48.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-server</artifactId>
+ <version>2.53.1</version>
+ <scope>runtime</scope>
+ </dependency>
+
+<!-- <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-firefox-driver</artifactId>
+ <version>3.0.1</version>
+ </dependency> -->
+
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>asdc-tests</artifactId>
+ <version>${asdc-tests.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <version>1.14</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http client -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.5</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- http core -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- TITAN -->
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-core</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.thinkaurelius.titan</groupId>
+ <artifactId>titan-cassandra</artifactId>
+ <version>${titan.version}</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <version>1.9.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>2.3.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>sdc-distribution-client</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <version>6.9.10</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ <version>1.4.01</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <version>1.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jci-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.relevantcodes</groupId>
+ <artifactId>extentreports</artifactId>
+ <version>2.41.1</version>
+ </dependency>
+
+
+ </dependencies>
+
+
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <!-- ============================================= -->
+ <!-- Create the JAR file with its dependencies -->
+ <!-- ============================================= -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.5.5</version>
+ <executions>
+ <execution>
+ <id>create.jar.with.dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+
+
+ <profiles>
+ <profile>
+ <id>CI</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.5.5</version>
+ <executions>
+ <execution>
+ <id>tarball</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <finalName>${project.artifactId}-${full.release.version}${build.type}</finalName>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptor>${project.basedir}/tarball.xml</descriptor>
+ <attach>false</attach>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactInfo.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactInfo.java
new file mode 100644
index 0000000000..4de9ae8436
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ArtifactInfo.java
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public class ArtifactInfo {
+
+ private String filepath;
+ private String filename;
+ private String description;
+ private String artifactType;
+ private String artifactLabel;
+
+ public ArtifactInfo(String filepath, String filename, String description, String artifactLabel,
+ String artifactType) {
+ super();
+ this.filepath = filepath;
+ this.filename = filename;
+ this.description = description;
+ this.artifactType = artifactType;
+ this.artifactLabel = artifactLabel;
+ }
+
+ public ArtifactInfo() {
+ super();
+ }
+
+ public String getFilepath() {
+ return filepath;
+ }
+
+ public void setFilepath(String filepath) {
+ this.filepath = filepath;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getArtifactType() {
+ return artifactType;
+ }
+
+ public void setArtifactType(String artifactType) {
+ this.artifactType = artifactType;
+ }
+
+ public String getArtifactLabel() {
+ return artifactLabel;
+ }
+
+ public void setArtifactLabel(String artifactLabel) {
+ this.artifactLabel = artifactLabel;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/BreadCrumbsButtonsEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/BreadCrumbsButtonsEnum.java
new file mode 100644
index 0000000000..21d5f25013
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/BreadCrumbsButtonsEnum.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum BreadCrumbsButtonsEnum {
+
+ HOME("main-menu-button-home"), CATALOG("main-menu-button-catalog"), ON_BOARDING("main-menu-button-onboard");
+
+ private String value;
+ private String value2;
+
+ public String getButton() {
+ return value;
+ }
+
+ private BreadCrumbsButtonsEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasElement.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasElement.java
new file mode 100644
index 0000000000..818b488d64
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasElement.java
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+
+public final class CanvasElement {
+ private final String uniqueId;
+ private ImmutablePair<Integer, Integer> location;
+ private LeftPanelCanvasItems normativeElementType;
+ private String elementType;
+
+ CanvasElement(String name, ImmutablePair<Integer, Integer> location, LeftPanelCanvasItems canvasItem) {
+ super();
+ this.uniqueId = name;
+ this.location = location;
+ normativeElementType = canvasItem;
+ }
+
+ CanvasElement(String name, ImmutablePair<Integer, Integer> location, String canvasItem) {
+ super();
+ this.uniqueId = name;
+ this.location = location;
+ elementType = canvasItem;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public ImmutablePair<Integer, Integer> getLocation() {
+ return location;
+ }
+
+ public void setLocation(ImmutablePair<Integer, Integer> location) {
+ this.location = location;
+ }
+
+ public LeftPanelCanvasItems getNormativeElementType() {
+ return normativeElementType;
+ }
+
+ public String getElementType() {
+ return elementType;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasManager.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasManager.java
new file mode 100644
index 0000000000..d5c76d1ef1
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CanvasManager.java
@@ -0,0 +1,250 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import static org.testng.AssertJUnit.assertNotNull;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.verificator.VfVerificator;
+import org.openecomp.sdc.common.api.ArtifactTypeEnum;
+import org.openqa.selenium.By;
+import org.openqa.selenium.StaleElementReferenceException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.testng.Assert;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public final class CanvasManager {
+ private static final String LEFT_PANEL_ELEMENT_NAME_PREFIX = "leftbar-section-content-item-";
+ private Map<String, CanvasElement> canvasElements;
+ private Actions actions;
+ private WebElement canvas;
+ private int reduceCanvasWidthFactor;
+ // Offsets Are used to find upper right corner of canvas element in order to
+ // connect links
+ private static final int CANVAS_ELEMENT_Y_OFFSET = 40;
+ private static final int CANVAS_ELEMENT_X_OFFSET = 21; // 14 - 27
+
+ private CanvasManager() {
+ canvasElements = new HashMap<>();
+ actions = new Actions(GeneralUIUtils.getDriver());
+// canvas = GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.CANVAS.getValue());
+ canvas = GeneralUIUtils.waitForClassNameVisibility("w-sdc-designer-canvas");
+ try {
+ WebElement webElement = GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.CANVAS_RIGHT_PANEL.getValue());
+ reduceCanvasWidthFactor = webElement.getSize().width;
+ } catch (Exception e) {
+ reduceCanvasWidthFactor = 0;
+ }
+ }
+
+
+ public static CanvasManager getCanvasManager() {
+ return new CanvasManager();
+ }
+
+ public List<CanvasElement> getCanvasElements() {
+ return canvasElements.values().stream().collect(Collectors.toList());
+ }
+
+ private void addCanvasElement(CanvasElement element) {
+ canvasElements.put(element.getUniqueId(), element);
+ }
+
+ private void moveElementOnCanvas(CanvasElement canvasElement, ImmutablePair<Integer, Integer> newLocation)
+ throws Exception {
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(500);
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, newLocation.left, newLocation.right);
+ actions.release();
+ actions.perform();
+ canvasElement.setLocation(newLocation);
+ GeneralUIUtils.waitForLoader();
+
+ }
+
+ public void moveToFreeLocation(String containerName) {
+ int maxWait = 5000;
+ int sumOfWaiting = 0;
+ int napPeriod = 200;
+ boolean isKeepWaiting = false;
+ while (!isKeepWaiting) {
+ ImmutablePair<Integer, Integer> freePosition = getFreePosition();
+ actions.moveToElement(canvas, freePosition.left, freePosition.right);
+ actions.clickAndHold();
+ actions.release();
+ actions.perform();
+ GeneralUIUtils.sleep(napPeriod);
+ isKeepWaiting = GeneralUIUtils.getWebElementWaitForVisible("selectedCompTitle").getText()
+ .equals(containerName);
+ sumOfWaiting += napPeriod;
+ if (sumOfWaiting > maxWait) {
+ Assert.fail("Can't click on VF");
+ }
+ }
+ }
+
+ public void clickOnCanvaElement(CanvasElement canvasElement) {
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.clickAndHold();
+ actions.release();
+ actions.perform();
+ }
+
+ public void moveElementOnCanvas(CanvasElement canvasElement) throws Exception {
+ moveElementOnCanvas(canvasElement, getFreePosition());
+ }
+
+ public void deleteElementFromCanvas(CanvasElement canvasElement) throws Exception {
+ GeneralUIUtils.waitForLoader();
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.click();
+ actions.perform();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.DELETE_INSTANCE_BUTTON.getValue())
+ .click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ canvasElements.remove(canvasElement.getUniqueId());
+ GeneralUIUtils.waitForLoader();
+ }
+
+ private WebElement findClickElement(String dataTestId) {
+ int attempts = 0;
+ while (attempts < 2) {
+ try {
+ return GeneralUIUtils.getWebElementWaitForVisible(dataTestId);
+ } catch (StaleElementReferenceException e) {
+ }
+ attempts++;
+ }
+ return null;
+ }
+
+ public CanvasElement createElementOnCanvas(String elementName) throws Exception {
+ final String elementDataTestId = LEFT_PANEL_ELEMENT_NAME_PREFIX + elementName;
+ try {
+ WebElement element = findClickElement(elementDataTestId);
+ ImmutablePair<Integer, Integer> freePosition = getFreePosition();
+ actions.moveToElement(element, 0, 0);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, freePosition.left, freePosition.right);
+ actions.release();
+ actions.perform();
+ GeneralUIUtils.waitForLoader();
+ String uniqueId = elementDataTestId + "_" + UUID.randomUUID().toString();
+ CanvasElement canvasElement = new CanvasElement(uniqueId, freePosition, elementDataTestId);
+ addCanvasElement(canvasElement);
+ SetupCDTest.getExtendTest().log(LogStatus.PASS,
+ String.format("element %s is in canvas now..", elementName));
+ return canvasElement;
+ } catch (Exception e) {
+ System.out.println("Can't create element on canvas");
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public CanvasElement createElementOnCanvas(LeftPanelCanvasItems canvasItem) throws Exception {
+ return createElementOnCanvas(canvasItem.getValue());
+ }
+
+ private ImmutablePair<Integer, Integer> getFreePosition() {
+ ImmutablePair<Integer, Integer> randomPosition = null;
+ boolean freePosition = false;
+ int minSpace = 150;
+ while (!freePosition) {
+ ImmutablePair<Integer, Integer> tempRandomPosition = getRandomPosition();
+ freePosition = !canvasElements.values().stream().map(e -> e.getLocation())
+ .filter(e -> Math.abs(e.left - tempRandomPosition.left) < minSpace
+ && Math.abs(e.right - tempRandomPosition.right) < minSpace)
+ .findAny().isPresent();
+ randomPosition = tempRandomPosition;
+ }
+ return randomPosition;
+ }
+
+ private ImmutablePair<Integer, Integer> getRandomPosition() {
+ int edgeBuffer = 50;
+ Random random = new Random();
+ int xElement = random.nextInt(canvas.getSize().width - 2 * edgeBuffer - reduceCanvasWidthFactor) + edgeBuffer;
+ int yElement = random.nextInt(canvas.getSize().height - 2 * edgeBuffer) + edgeBuffer;
+ return new ImmutablePair<Integer, Integer>(xElement, yElement);
+ }
+
+ public void linkElements(CanvasElement firstElement, CanvasElement secondElement) throws Exception {
+ drawSimpleLink(firstElement, secondElement);
+ selectReqAndCapAndConnect();
+ }
+
+ private void selectReqAndCapAndConnect() throws Exception {
+ // Select First Cap
+ GeneralUIUtils.getWebElementsListByDataTestId(DataTestIdEnum.LinkMenuItems.LINK_ITEM_CAP.getValue()).get(0)
+ .click();
+ // Select First Req
+ GeneralUIUtils.getWebElementsListByDataTestId(DataTestIdEnum.LinkMenuItems.LINK_ITEM_REQ.getValue()).get(0)
+ .click();
+ // Connect
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LinkMenuItems.CONNECT_BUTTON.getValue()).click();
+
+ GeneralUIUtils.waitForLoader();
+ }
+
+ private void drawSimpleLink(CanvasElement firstElement, CanvasElement secondElement) throws Exception {
+ int yOffset = CANVAS_ELEMENT_Y_OFFSET;
+ int xOffset = CANVAS_ELEMENT_X_OFFSET;
+
+ actions.moveToElement(canvas, firstElement.getLocation().left + xOffset, firstElement.getLocation().right - yOffset);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, secondElement.getLocation().left + xOffset, secondElement.getLocation().right - yOffset);
+ actions.release();
+ actions.perform();
+ GeneralUIUtils.ultimateWait();
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, String.format("Elements %s and %s now connected", firstElement.getElementType().split("-")[4], secondElement.getElementType().split("-")[4]));
+ }
+
+ public String updateElementNameInCanvas(CanvasElement canvasElement, String newInstanceName) throws Exception {
+ GeneralUIUtils.waitForLoader();
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.click();
+ actions.perform();
+ WebElement updateInstanceName = GeneralUIUtils.getDriver().findElement(By.id("editPencil"));
+ updateInstanceName.click();
+ WebElement instanceNameField = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.INSTANCE_NAME_FIELD.getValue());
+ String oldInstanceName = instanceNameField.getText();
+ instanceNameField.clear();
+ instanceNameField.sendKeys(newInstanceName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ return oldInstanceName;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CatalogFilterTitlesEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CatalogFilterTitlesEnum.java
new file mode 100644
index 0000000000..1335b51788
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CatalogFilterTitlesEnum.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum CatalogFilterTitlesEnum {
+
+ TYPE("typeFilterTitle"), CATEGORIES("categoriesFilterTitle"), STATUS("statusFilterTitle");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private CatalogFilterTitlesEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CheckBoxStatusEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CheckBoxStatusEnum.java
new file mode 100644
index 0000000000..ffa17ee607
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CheckBoxStatusEnum.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum CheckBoxStatusEnum {
+ CHECKOUT("", "checkbox-checkout"),
+ CHECKIN("", "checkbox-checkin"),
+ READY_FOR_TESTING("checkbox-readyfortesting","checkbox-1"),
+ IN_TESTING("checkbox-intesting", "checkbox-2"),
+ WAITING_FOR_DISTRIBUTION("", "checkbox-waitingforapproval"),
+ DISTRIBUTION_REJECTED("", "checkbox-distributionrejected"),
+ DISTRIBUTION_APPROVED("", "checkbox-distributionapproved"),
+ CERTIFIED("checkbox-certified", "checkbox-3"),
+ DISTRIBUTED("", "checkbox-4"),
+ IN_DESIGN("", "checkbox-0");
+
+ private String value;
+ private String value2;
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getCatalogValue() {
+ return value2;
+ }
+
+ private CheckBoxStatusEnum(String value, String value2) {
+ this.value = value;
+ this.value2 = value2;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndImportButtonsEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndImportButtonsEnum.java
new file mode 100644
index 0000000000..a49c5024a6
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndImportButtonsEnum.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum CreateAndImportButtonsEnum {
+
+ IMPORT_VF, IMPORT_VFC, IMPORT_CP, IMPORT_VL, CREATE_VF, CREATE_SERVICE, CREATE_PRODUCT;
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndUpdateStepsEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndUpdateStepsEnum.java
new file mode 100644
index 0000000000..ffa7400338
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/CreateAndUpdateStepsEnum.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+// public enum CreateAndUpdateStepsEnum {
+// GENERAL("Generalstep"),
+// ICON("Iconstep"),
+// DEPLOYMENT_ARTIFACT("Deployment Artifactstep"),
+// INFORMATION_ARTIFACT("Information Artifactstep"),
+// PROPERTIES("Propertiesstep"),
+// COMPOSITION("Compositionstep"),
+// ACTIVITY_LOG("Activity Logstep"),
+// DEPLOYMENT_VIEW("Deploymentstep");
+//
+//
+// private String value;
+//
+// public String getValue(){
+// return value;
+// }
+//
+// private CreateAndUpdateStepsEnum(String value) {
+// this.value = value;
+// }
+//
+//
+// }
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/DataTestIdEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/DataTestIdEnum.java
new file mode 100644
index 0000000000..16593fc62f
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/DataTestIdEnum.java
@@ -0,0 +1,380 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public final class DataTestIdEnum {
+ private DataTestIdEnum() {
+ };
+
+ public enum Dashboard {
+ IMPORT_AREA("importButtonsArea"),
+ BUTTON_ADD_VF("createResourceButton"),
+ BUTTON_ADD_SERVICE("createServiceButton"),
+ IMPORT_VFC("importVFCbutton"),
+ IMPORT_VF("importVFbutton"),
+ IMPORT_VFC_FILE("file-importVFCbutton"),
+ IMPORT_VF_FILE("file-importVFbutton");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private Dashboard(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum LifeCyleChangeButtons {
+ CREATE("create/save"), CHECK_IN("check_in"),
+ SUBMIT_FOR_TESTING("submit_for_testing"),
+ START_TESTING("start_testing"),
+ ACCEPT("accept");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LifeCyleChangeButtons(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum DistributionChangeButtons {
+ APPROVE("approve"), REJECT("reject"),
+ DISTRIBUTE("distribute"),
+ MONITOR("monitor"),
+ APPROVE_MESSAGE("checkindialog"),
+ RE_DISTRIBUTE("redistribute");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private DistributionChangeButtons(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum InformationalArtifacts {
+ CLOUD_QUESTIONNAIRE("Cloud Questionnaire (completed)"),
+ FEATURES("Features"),
+ VENDOR_TEST_RESULT("Vendor Test Result"),
+ TEST_SCRIPTS("Test Scripts"),
+ RESOURCE_SECURITY_TEMPLATE("Resource Security Template"),
+ HEAT_TEMPLATE_FROM_VENDOR("HEAT Template from Vendor"),
+ CAPACITY("Capacity");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private InformationalArtifacts(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum ModalItems {
+ BROWSE_BUTTON("browseButton"),
+ ADD("Add"),
+ DESCRIPTION("description"),
+ SUMBIT_FOR_TESTING_MESSAGE("changeLifeCycleMessage"),
+ OK("OK"),
+ CANCEL("Cancel"),
+ ACCEP_TESTING_MESSAGE("checkindialog");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ModalItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum LeftPanelCanvasItems {
+ BLOCK_STORAGE("BlockStorage"),
+ CINDER_VOLUME("CinderVolume"),
+ COMPUTE("Compute"),
+ LOAD_BALANCER("LoadBalancer"),
+ NOVA_SERVER("NovaServer"),
+ OBJECT_STORAGE("ObjectStorage"),
+ NEUTRON_PORT("NeutronPort"),
+ PORT("Port"),
+ DATABASE("Database");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LeftPanelCanvasItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum LinkMenuItems {
+ CANCEL_BUTTON("link-menu-button-cancel"),
+ CONNECT_BUTTON("link-menu-button-connect"),
+ LINK_ITEM_CAP("link-item-capabilities"),
+ LINK_ITEM_REQ("link-item-requirements"),
+ LINK_MENU("link-menu-open");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LinkMenuItems(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum GeneralCanvasItems {
+ CANVAS("canvas"),
+ CANVAS_RIGHT_PANEL("w-sdc-designer-sidebar-head"),
+ DELETE_INSTANCE_BUTTON("e-sdc-small-icon-delete"),
+ UPDATE_INSTANCE_NAME("e-sdc-small-icon-update"),
+ INSTANCE_NAME_FIELD("instanceName");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private GeneralCanvasItems(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum ResourceMetadataEnum {
+ RESOURCE_NAME("name"),
+ DESCRIPTION("description"),
+ CATEGORY("selectGeneralCategory"),
+ VENDOR_NAME("vendorName"),
+ VENDOR_RELEASE("vendorRelease"),
+ TAGS("i-sdc-tag-input"),
+ CONTACT_ID("contactId"),
+ ICON(" iconBox");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ResourceMetadataEnum(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum GeneralElementsEnum {
+ CREATE_BUTTON("create/save"),
+ CHECKIN_BUTTON("check_in"),
+ SUBMIT_FOR_TESTING_BUTTON("submit_for_testing"),
+ DELETE_VERSION_BUTTON("delete_version"),
+ REVERT_BUTTON("revert"),
+ LIFECYCLE_STATE("lifecyclestate"),
+ VERSION_HEADER("versionHeader");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private GeneralElementsEnum(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum ArtifactPageEnum {
+
+ ADD_INFORMATIONAL_ARTIFACT("add-information-artifact-button"),
+ ADD_DEPLOYMENT_ARTIFACT("add-deployment-artifact-button"),
+ ADD_ANOTHER_ARTIFACT("add-another-artifact-button"),
+ EDIT_ARTIFACT("edit_"),
+ DELETE_ARTIFACT("delete_"),
+ DOWNLOAD_ARTIFACT("download_"),
+ GET_DEPLOYMENT_ARTIFACT_DESCRIPTION("description"),
+ GET_INFORMATIONAL_ARTIFACT_DESCRIPTION("Description")
+
+ ;
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ArtifactPageEnum(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum PropertiesPageEnum {
+
+ ADD_NEW_PROPERTY("addGrey"),
+ EDIT_PROPERTY("edit_"),
+ DELETE_PROPERTY("delete_"),
+ PROPERTY_NAME("propertyName"),
+ PROPERTY_VALUE("defaultvalue"),
+ PROPERTY_DESCRIPTION("description"),
+ PROPERTY_TYPE("propertyType"),
+ ADD("Add"),
+ CANCEL("Cancel"),
+ DONE("Done"),
+ PROPERTY_ROW("propertyRow"),
+ SAVE("Save");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private PropertiesPageEnum(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum MainMenuButtons {
+ HOME_BUTTON("main-menu-button-home"),
+ CATALOG_BUTTON("main-menu-button-catalog"),
+ ONBOARD_BUTTON("main-menu-button-onboard");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private MainMenuButtons(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum MenuOptionsEnum {
+ EDIT("Edit"),
+ CHECK_IN("Check in"),
+ CHECK_OUT("Check out"),
+ VIEW("View"),
+ SUBMIT_FOR_TEST("Submit For Test"),
+ ACCEPT("Accept"),
+ REJECT("Reject"),
+ START_TEST("Start test"),
+ DISTREBUTE("Distribute");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private MenuOptionsEnum(String value) {
+ this.value = value;
+ }
+ }
+
+ public enum StepsEnum {
+ GENERAL("Generalstep"),
+ ICON("Iconstep"),
+ DEPLOYMENT_ARTIFACT("Deployment Artifactstep"),
+ INFORMATION_ARTIFACT("Information Artifactstep"),
+ PROPERTIES("Propertiesstep"),
+ COMPOSITION("Compositionstep"),
+ ACTIVITY_LOG("Activity Logstep"),
+ DEPLOYMENT_VIEW("Deploymentstep"),
+ TOSCA_ARTIFACTS("TOSCA Artifactsstep"),
+ MONITOR("Monitor step");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private StepsEnum(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum ArtifactPopup {
+
+ BROWSE("browseButton"),
+ ARTIFACT_DESCRIPTION("description"),
+ ARTIFACT_LABEL("selectArtifact"),
+ ARTIFACT_TYPE("artifacttype"),
+ ADD_BUTTON("Add"),
+ CANCEL_BUTTON("Cancel"),
+ UPDATE_BUTTON("Update");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ArtifactPopup(String value) {
+ this.value = value;
+ }
+
+ }
+
+ public enum ServiceMetadataEnum {
+ SERVICE_NAME("name"),
+ DESCRIPTION("description"),
+ CATEGORY("selectGeneralCategory"),
+ PROJECT_CODE("projectCode"),
+ TAGS("i-sdc-tag-input"),
+ CONTACT_ID("contactId"),
+ ICON(" iconBox");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ServiceMetadataEnum(String value) {
+ this.value = value;
+ }
+
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GeneralCanvasItemsEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GeneralCanvasItemsEnum.java
new file mode 100644
index 0000000000..0e8f9cbea1
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/GeneralCanvasItemsEnum.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum GeneralCanvasItemsEnum {
+ CANVAS("canvas"), CANVAS_RIGHT_PANEL("w-sdc-designer-sidebar-head"), DELETE_INSTANCE_BUTTON(
+ "e-sdc-small-icon-delete")
+
+ ;
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private GeneralCanvasItemsEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/LifeCycleStateEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/LifeCycleStateEnum.java
new file mode 100644
index 0000000000..29274179d1
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/LifeCycleStateEnum.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum LifeCycleStateEnum {
+
+ CHECKOUT("IN DESIGN CHECK OUT"),
+ CHECKIN("IN DESIGN CHECK IN"),
+ READY_FOR_TESTING("READY FOR TESTING"),
+ IN_TESTING("IN TESTING"),
+ WAITING_FOR_DISTRIBUTION("WAITING FOR DISTRIBUTION"),
+ DISTRIBUTION_REJECTED("DISTRIBUTION REJECTED"),
+ DISTRIBUTION_APPROVED("DISTRIBUTION APPROVED"),
+ CERTIFIED("CERTIFIED"),
+ DISTRIBUTED("DISTRIBUTED");
+
+ private String value;
+ private String value2;
+
+ public String getValue() {
+ return value;
+ }
+
+ private LifeCycleStateEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/MenuOptionsEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/MenuOptionsEnum.java
new file mode 100644
index 0000000000..d23ab18f10
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/MenuOptionsEnum.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum MenuOptionsEnum {
+
+ EDIT("Edit"),
+ CHECK_IN("Check in"),
+ CHECK_OUT("Check out"),
+ VIEW("View"),
+ SUBMIT_FOR_TEST("Submit For Test"),
+ ACCEPT("Accept"),
+ REJECT("Reject"),
+ START_TEST("Start test"),
+ DISTREBUTE("Distribute");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private MenuOptionsEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyInfo.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyInfo.java
new file mode 100644
index 0000000000..385093e5ad
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/PropertyInfo.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import org.openecomp.sdc.ci.tests.datatypes.enums.PropertyTypeEnum;
+
+public class PropertyInfo {
+
+ public PropertyInfo() {
+ super();
+ }
+
+ public PropertyInfo(String name, String value, String desc, PropertyTypeEnum type) {
+ super();
+ this.name = name;
+ this.value = value;
+ this.type = type;
+ this.description = desc;
+ }
+
+ private String name;
+ private String value;
+ private PropertyTypeEnum type;
+ private String description;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public PropertyTypeEnum getType() {
+ return type;
+ }
+
+ public void setType(PropertyTypeEnum type) {
+ this.type = type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceCategoriesNameEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceCategoriesNameEnum.java
new file mode 100644
index 0000000000..921493f932
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ResourceCategoriesNameEnum.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum ResourceCategoriesNameEnum {
+
+ GENERIC("checkbox-resourcenewcategory.generic"),
+ NETWORK_CONNECTIVITY("checkbox-resourcenewcategory.networkconnectivity"),
+ NETWORK_ELEMENTS("checkbox-resourcenewcategory.generic.networkelements"),
+ ABSTRACT("checkbox-resourcenewcategory.generic.abstract"),
+ DATABASE_GENERIC("checkbox-resourcenewcategory.generic.database"),
+ INFRASTRUCTURE("checkbox-resourcenewcategory.generic.infrastructure"),
+ VIRTUAL_LINKS("checkbox-resourcenewcategory.networkconnectivity.virtuallinks"),
+ CONNECTION_POINTS("checkbox-resourcenewcategory.networkconnectivity.connectionpoints"),
+ NETWORKL4("checkbox-resourcenewcategory.networkl4+"),
+ COMMON_NETWORK_RESOURCES("checkbox-resourcenewcategory.networkl4+.commonnetworkresources"),
+ APPLICATIONL4("checkbox-resourcenewcategory.applicationl4+"),
+ WEB_SERVER("checkbox-resourcenewcategory.applicationl4+.webserver"),
+ APPLICATION_SERVER("checkbox-resourcenewcategory.applicationl4+.applicationserver"),
+ CALL_CONTROL("checkbox-resourcenewcategory.applicationl4+.callcontrol"),
+ BORDER_ELEMENT("checkbox-resourcenewcategory.applicationl4+.borderelement"),
+ MEDIA_SERVERS("checkbox-resourcenewcategory.applicationl4+.mediaservers"),
+ DATABASE("checkbox-resourcenewcategory.applicationl4+.database"),
+ FIREWALL("checkbox-resourcenewcategory.applicationl4+.firewall"),
+ LOAD_BALANCER("checkbox-resourcenewcategory.applicationl4+.loadbalancer"),
+ NETWORK_L23("checkbox-resourcenewcategory.networkl2-3"),
+ Router("checkbox-resourcenewcategory.networkl2-3.router"),
+ WAN_Connectors("checkbox-resourcenewcategory.networkl2-3.wanconnectors"),
+ LAN_CONNECTORS("checkbox-resourcenewcategory.networkl2-3.lanconnectors"),
+ GATEWAY("checkbox-resourcenewcategory.networkl2-3.gateway"),
+ INFRASTRUCTUREL23("checkbox-resourcenewcategory.networkl2-3.infrastructure");
+
+
+ private String value;
+
+ public String getValue(){
+ return value;
+ }
+
+ private ResourceCategoriesNameEnum(String value) {
+ this.value = value;
+ }
+
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceCategoriesNameEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceCategoriesNameEnum.java
new file mode 100644
index 0000000000..d39bba5594
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/ServiceCategoriesNameEnum.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum ServiceCategoriesNameEnum {
+
+ NETWORK_L13("checkbox-servicenewcategory.networkl1-3"),
+ VOIPCALL_CONTROL("checkbox-servicenewcategory.voipcallcontrol"),
+ NETWORKL4("checkbox-servicenewcategory.networkl4+"),
+ MOBILITY("checkbox-servicenewcategory.mobility");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private ServiceCategoriesNameEnum(String value) {
+ this.value = value;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypesEnum.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypesEnum.java
new file mode 100644
index 0000000000..1bff4d51e6
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/TypesEnum.java
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum TypesEnum {
+
+ RESOURCE("checkbox-resource"),
+ VF("checkbox-vf"),
+ VFC("checkbox-vfc"),
+ CP("checkbox-cp"),
+ VL("checkbox-vl"),
+ SERVICE("checkbox-service"),
+ PRODUCT("checkbox-product");
+
+ private String value;
+
+ public String getValue() {
+ return value;
+ }
+
+ private TypesEnum(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/UserCredentials.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/UserCredentials.java
new file mode 100644
index 0000000000..870d2879bb
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/UserCredentials.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+import org.openecomp.sdc.be.model.User;
+
+public class UserCredentials extends User {
+
+ // private String userName;
+ private String password;
+
+ public UserCredentials(String userId, String password, String firstname, String lastname) {
+ super();
+ setUserId(userId);
+ // this.userName = userName;
+ this.password = password;
+ setFirstName(firstname);
+ setLastName(lastname);
+ }
+
+ public UserCredentials() {
+ super();
+ }
+
+ // public String getUserName() {
+ // return userName;
+ // }
+ // public void setUserName(String userName) {
+ // this.userName = userName;
+ // }
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/WorkMode.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/WorkMode.java
new file mode 100644
index 0000000000..199fa6990a
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/WorkMode.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.datatypes;
+
+public enum WorkMode {
+ DEV, CD;
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/environmentLocal b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/environmentLocal
new file mode 100644
index 0000000000..ea93ddfe85
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/datatypes/environmentLocal
@@ -0,0 +1,10 @@
+//URL designer
+urlDesigner=http://172.20.43.136:8080/sdc1/proxy-designer1#/dashboard
+//URL tester
+urlTester=http://172.20.43.136:8080/sdc1/proxy-tester1
+//URL Vagrant
+urlDesignerVagrant=http://localhost:8181/sdc1/proxy-designer1#/dashboard
+//UrlStaging
+UrlStaging=https://www.e-access.att.com/sdcpstage/sdc1/portal#/dashboard
+//UrlAdmin
+UrlAdmin=http://172.20.43.136:8080/sdc1/proxy-admin1#/adminDashboard \ No newline at end of file
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/adminworkspace/AdminUserManagment.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/adminworkspace/AdminUserManagment.java
new file mode 100644
index 0000000000..19a0d6414f
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/adminworkspace/AdminUserManagment.java
@@ -0,0 +1,141 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.adminworkspace;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import javax.validation.constraints.AssertTrue;
+
+import org.junit.rules.TestName;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.AdminWorkspaceUIUtilies;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Ordering;
+
+public class AdminUserManagment extends SetupCDTest {
+ //
+ // public AdminUserManagment() {
+ // super(new TestName(), AdminUserManagment.class.getName());
+ // }
+
+ private WebDriver driver = GeneralUIUtils.getDriver();
+
+ // Create new USER_ID user
+ @Test
+ public void creatUserIdNewUserTest() throws Exception {
+ String userId = AdminWorkspaceUIUtilies.defineNewUserId("th0695");
+ String role = AdminWorkspaceUIUtilies.selectUserRole("designer");
+ AdminWorkspaceUIUtilies.deleteuser(userId);
+ GeneralUIUtils.getWebButton("creategreen").click();
+ AdminWorkspaceUIUtilies.typeToSearchBox(userId);
+ String createdUserUserId = GeneralUIUtils.getWebElementWaitForVisible("tdUSER_ID").getText();
+ userId.equals(createdUserUserId);
+ GeneralUIUtils.getWebElementWaitForVisible("tdLast Active").getText().equals("Waiting");
+ GeneralUIUtils.getWebElementWaitForVisible("tdRole").getText().equals(role);
+ AdminWorkspaceUIUtilies.deleteuser(userId);
+
+ }
+
+ // Create new MacId user
+ @Test
+ public void creatMacIdNewUserTest() throws Exception {
+
+ String macId = AdminWorkspaceUIUtilies.defineNewUserId("m12345");
+ String role = AdminWorkspaceUIUtilies.selectUserRole("designer");
+ AdminWorkspaceUIUtilies.deleteuser(macId);
+ GeneralUIUtils.getWebButton("creategreen").click();
+ AdminWorkspaceUIUtilies.typeToSearchBox(macId);
+ String createdUserUserId = GeneralUIUtils.getWebElementWaitForVisible("tdUSER_ID").getText();
+ macId.equals(createdUserUserId);
+ GeneralUIUtils.getWebElementWaitForVisible("tdLast Active").getText().equals("Waiting");
+ GeneralUIUtils.getWebElementWaitForVisible("tdRole").getText().equals(role);
+ AdminWorkspaceUIUtilies.deleteuser(macId);
+ }
+
+ // Create exist user and get error already exist .
+ @Test
+ public void createxistUserTest() throws Exception {
+ String userId = AdminWorkspaceUIUtilies.defineNewUserId("th0695");
+ String role = AdminWorkspaceUIUtilies.selectUserRole("designer");
+ AdminWorkspaceUIUtilies.deleteuser(userId);
+ GeneralUIUtils.getWebButton("creategreen").click();
+ AdminWorkspaceUIUtilies.defineNewUserId(userId);
+ AdminWorkspaceUIUtilies.selectUserRole("admin");
+ GeneralUIUtils.getWebButton("creategreen").click();
+ ResourceUIUtils.getErrorMessageText("w-sdc-modal-body-content")
+ .equals("User with '" + userId + "' ID already exists.");
+ GeneralUIUtils.clickOkButton();
+ AdminWorkspaceUIUtilies.deleteuser(userId);
+
+ }
+
+ // enter Special chars and the create button disabled.
+ @Test
+ public void insertSpacialcharsTest() throws Exception {
+ AdminWorkspaceUIUtilies.defineNewUserId("!@DER%");
+ AdminWorkspaceUIUtilies.selectUserRole("designer");
+ WebElement createbutton = GeneralUIUtils.getWebElementWaitForVisible("creategreen");
+ Assert.assertFalse(createbutton.isEnabled());
+ }
+
+ // enter invalid macid and create button shall be disabled.
+ @Test
+ public void insertInvalidUserMacidTest() throws Exception {
+ AdminWorkspaceUIUtilies.defineNewUserId("k12345");
+ AdminWorkspaceUIUtilies.selectUserRole("designer");
+ WebElement createbutton = GeneralUIUtils.getWebElementWaitForVisible("creategreen");
+ Assert.assertFalse(createbutton.isEnabled());
+ }
+
+ // enter invalid userId and create button shall be disabled.
+ @Test
+ public void insertInvalidUserUserIdTest() throws Exception {
+ AdminWorkspaceUIUtilies.defineNewUserId("ac1c23");
+ AdminWorkspaceUIUtilies.selectUserRole("designer");
+ WebElement createbutton = GeneralUIUtils.getWebElementWaitForVisible("creategreen");
+ Assert.assertFalse(createbutton.isEnabled());
+ }
+
+ // display users list and sort by column name.
+ @Test
+ public void displayuserslistandsorting() throws Exception {
+ GeneralUIUtils.getWebElementWaitForVisible("thFirst Name").click();
+ Collection<WebElement> usersFname = GeneralUIUtils.getWebElements("tdFirst Name");
+ for (WebElement webElement : usersFname) {
+ System.out.println(webElement.getText());
+ }
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.ADMIN;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogLeftFilterPanelCheckBoxTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogLeftFilterPanelCheckBoxTest.java
new file mode 100644
index 0000000000..39755a9db8
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogLeftFilterPanelCheckBoxTest.java
@@ -0,0 +1,272 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.BreadCrumbsButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CatalogFilterTitlesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CreateAndImportButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.TypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.CatalogUIUtilitis;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.RestCDUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CatalogLeftFilterPanelCheckBoxTest extends SetupCDTest {
+
+ public CatalogLeftFilterPanelCheckBoxTest() {
+ // TODO Auto-generated constructor stub
+ }
+
+ private ResourceReqDetails resourceDetails;
+ FileWriter filwriter = GeneralUIUtils.InitializeprintToTxt("CatalogLeftFilterPanelCheckBoxTest");
+
+ @BeforeMethod
+ public void beforTest() {
+ resourceDetails = ElementFactory.getDefaultResource();
+ }
+
+ // filter by Type Resource in catalog
+ @Test
+ public void filterByAssetTypeResource() throws Exception {
+ List<WebElement> elements = null;
+ List<String> validValues = Arrays.asList("VF", "VL", "CP", "VFC");
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogFilterTypeChecBox(TypesEnum.RESOURCE);
+ GeneralUIUtils.getWorkspaceElements();
+ elements = GeneralUIUtils.waitForElementsListVisibilityTestMethod("asset-type");
+ for (WebElement webElement : elements) {
+ assertTrue(validValues.contains(webElement.getAttribute("class")));
+ }
+ }
+
+ @Test
+ public void filterByResourceTypeVF() throws Exception {
+ List<WebElement> elements = null;
+ List<String> validValues = Arrays.asList("VF");
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogFilterTypeChecBox(TypesEnum.VF);
+ GeneralUIUtils.getWorkspaceElements();
+ elements = GeneralUIUtils.waitForElementsListVisibilityTestMethod("asset-type");
+ for (WebElement webElement : elements) {
+ assertTrue(validValues.contains(webElement.getAttribute("class")));
+ }
+ }
+
+ @Test
+ public void filterByResourceTypeVFC() throws Exception {
+ List<WebElement> elements = null;
+ List<String> validValues = Arrays.asList("VFC");
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogFilterTypeChecBox(TypesEnum.VFC);
+ GeneralUIUtils.getWorkspaceElements();
+ elements = GeneralUIUtils.waitForElementsListVisibilityTestMethod("asset-type");
+ for (WebElement webElement : elements) {
+ assertTrue(validValues.contains(webElement.getAttribute("class")));
+ }
+ }
+
+ @Test
+ public void filterByResourceTypeCP() throws Exception {
+ List<WebElement> elements = null;
+ List<String> validValues = Arrays.asList("CP");
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogFilterTypeChecBox(TypesEnum.CP);
+ GeneralUIUtils.getWorkspaceElements();
+ elements = GeneralUIUtils.waitForElementsListVisibilityTestMethod("asset-type");
+ for (WebElement webElement : elements) {
+ assertTrue(validValues.contains(webElement.getAttribute("class")));
+ }
+ }
+
+ @Test
+ public void filterByResourceTypeVL() throws Exception {
+ List<WebElement> elements = null;
+ List<String> validValues = Arrays.asList("VL");
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogFilterTypeChecBox(TypesEnum.VL);
+ GeneralUIUtils.getWorkspaceElements();
+ elements = GeneralUIUtils.waitForElementsListVisibilityTestMethod("asset-type");
+ for (WebElement webElement : elements) {
+ assertTrue(validValues.contains(webElement.getAttribute("class")));
+ }
+ }
+
+ // @Test
+ // public void filterByProducTType() throws Exception{
+ // List<WebElement> elements = null;
+ // List<String> validValues = Arrays.asList("PRODUCT");
+ // GeneralUIUtils.checkIn();
+ // GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ // GeneralUIUtils.catalogFilterTypeChecBox(TypesEnum.PRODUCT);
+ // GeneralUIUtils.getWorkspaceElements();
+ // try {
+ // elements =
+ // GeneralUIUtils.getEelementsByClassName1("w-sdc-dashboard-card-avatar");
+ // for (WebElement webElement : elements) {
+ // assertTrue(validValues.contains(webElement.findElement(By.xpath(".//*")).getAttribute("class")));
+ // }
+ // } catch (Exception e) {
+ // System.out.println("No Elements founds!");
+ // }
+ // }
+
+ @Test
+ public void filterByResourceCategories() throws Exception {
+ List<WebElement> elements = null;
+ List<String> validValues = null;
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ Thread.sleep(2000);
+ GeneralUIUtils.minimizeCatalogFilterByTitle(CatalogFilterTitlesEnum.TYPE);
+ List<String> categories = CatalogUIUtilitis.abcd();
+ for (String category : categories) {
+ validValues = CatalogUIUtilitis.getAllSubcategoriesByUniqueId(category);
+ boolean bool = false;
+ try {
+ GeneralUIUtils.getWebElementWaitForVisible(category).click();
+ bool = true;
+ } catch (Exception e) {
+ while (!bool) {
+ GeneralUIUtils.scrollDown();
+ try {
+ GeneralUIUtils.getWebElementWaitForVisible(category).click();
+ bool = true;
+ } catch (Exception e2) {
+
+ }
+ }
+ }
+ String checkBox = GeneralUIUtils.getWebElementWaitForVisible(category).findElement(By.xpath(".//input"))
+ .getAttribute("class");
+ if (checkBox.contains("ng-not-empty") && validValues != null) {
+ try {
+ GeneralUIUtils.getWorkspaceElements();
+ elements = GeneralUIUtils.getEelementsBycontainsClassName("sprite-resource-icons");
+ for (WebElement webElement : elements) {
+ String elementUniqueId = webElement.getAttribute("data-tests-id");
+ if (!validValues.contains(elementUniqueId)) {
+ System.out.println("assert error!");
+ }
+ }
+ GeneralUIUtils.getWebElementWaitForVisible(category).click();
+ } catch (Exception e) {
+ GeneralUIUtils.getWebElementWaitForVisible(category).click();
+ System.out.println("No Elements founds!");
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("null")
+ @Test
+ public void filterByStatus() throws Exception {
+
+ List<WebElement> elements = null;
+ List<String> validValues = null;
+ List<ResourceReqDetails> createdComponents = new ArrayList<ResourceReqDetails>();
+ List<String> catalogStatuses = Arrays.asList("IN_DESIGN", "READY_FOR_TESTING", "IN_TESTING", "CERTIFIED",
+ "DISTRIBUTED");
+ GeneralUIUtils.checkIn();
+ for (int i = 1; i < catalogStatuses.size() - 1; i++) {
+ GeneralUIUtils.sleep(3000);
+ GeneralUIUtils.createAndImportButtons(CreateAndImportButtonsEnum.CREATE_VF, GeneralUIUtils.getDriver());
+ resourceDetails.setName(getRandomComponentName("ResourceCDTest-"));
+ ResourceUIUtils.createResourceInUI(resourceDetails, getUser());
+ GeneralUIUtils.clickSubmitForTest();
+ if (catalogStatuses.get(i) == "IN_TESTING") {
+ GeneralUIUtils.testerUser(true, false, resourceDetails);
+ GeneralUIUtils.sleep(3000);
+ navigateToUrl(getUrl());
+
+ }
+ if (catalogStatuses.get(i) == "CERTIFIED") {
+ GeneralUIUtils.testerUser(true, true, resourceDetails);
+ GeneralUIUtils.sleep(3000);
+ navigateToUrl(getUrl());
+ }
+ if (catalogStatuses.get(i) == "DISTRIBUTED") {
+ GeneralUIUtils.testerUser(true, true, resourceDetails);
+ GeneralUIUtils.governorUser(false, true, resourceDetails);
+ GeneralUIUtils.opsUser(true, false, resourceDetails);
+ GeneralUIUtils.sleep(3000);
+ navigateToUrl(getUrl());
+
+ }
+ createdComponents.add(resourceDetails);
+ }
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ // get filters title close.
+ Thread.sleep(2000);
+ GeneralUIUtils.minimizeCatalogFilterByTitle(CatalogFilterTitlesEnum.TYPE);
+ GeneralUIUtils.minimizeCatalogFilterByTitle(CatalogFilterTitlesEnum.CATEGORIES);
+ for (CheckBoxStatusEnum statusEnum : CheckBoxStatusEnum.values()) {
+ if (catalogStatuses.contains(statusEnum.name().toString())) {
+ validValues = GeneralUIUtils.catalogFilterStatusChecBox(statusEnum);
+ if (GeneralUIUtils.getWorkspaceElements().size() > 0) {
+ String checkBox = GeneralUIUtils.getWebElementByName(statusEnum.getCatalogValue())
+ .getAttribute("class");
+ if (checkBox.contains("ng-not-empty") && validValues != null) {
+ try {
+ elements = GeneralUIUtils.getEelementsBycontainsClassName("w-sdc-dashboard-card-edit");
+ for (WebElement webElement : elements) {
+ String className = webElement.getAttribute("class");
+ String textCategory = className.substring(className.indexOf(" "));
+ assertTrue(validValues.contains(textCategory.replace(" ", "")));
+ }
+ GeneralUIUtils.catalogFilterStatusChecBox(statusEnum);
+ } catch (Exception e) {
+ GeneralUIUtils.catalogFilterStatusChecBox(statusEnum);
+ System.out.println("No Elements founds!");
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogSearchBoxTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogSearchBoxTest.java
new file mode 100644
index 0000000000..efdb9532a4
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/CatalogSearchBoxTest.java
@@ -0,0 +1,130 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.FileWriter;
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.BreadCrumbsButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openqa.selenium.WebElement;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class CatalogSearchBoxTest extends SetupCDTest {
+
+ private ResourceReqDetails resourceDetails;
+
+ @BeforeMethod
+ public void beforTest() {
+ resourceDetails = ElementFactory.getDefaultResource();
+ }
+
+ // search by ResourceName
+ @Test
+ public void searchResourceInCatalogMenuTest() throws Exception {
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getName());
+ Thread.sleep(500);
+ assertTrue(GeneralUIUtils.getWorkspaceElements().size() == 1);
+ }
+
+ // search by Description
+ @Test
+ public void searchResourceInCatalogMenuByDescriptionTest() throws Exception {
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getDescription());
+ Thread.sleep(2000);
+ List<WebElement> assets = GeneralUIUtils.getWorkspaceElements();
+
+ int count = 0;
+ for (WebElement webElement : assets) {
+ if (count != 0) {
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getDescription());
+ }
+ if (count == 0) {
+ webElement.click();
+ } else {
+ List<WebElement> assets1 = GeneralUIUtils.getWorkspaceElements();
+ assets1.get(count).click();
+ }
+ GeneralUIUtils.getWebElementWaitForVisible("description").getText()
+ .equals(resourceDetails.getDescription());
+ GeneralUIUtils.clickExitSign();
+ Thread.sleep(500);
+ count++;
+ }
+ }
+
+ // search by tags
+ @Test
+ public void searchResourceInCatalogMenuBytagsTest() throws Exception {
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getTags().get(0));
+ Thread.sleep(500);
+ GeneralUIUtils.getWorkspaceElements().get(0).click();
+ List<WebElement> expectedTagsList = GeneralUIUtils.getWebElements("i-sdc-tag-text");
+ for (int i = 0; i < expectedTagsList.size(); i++) {
+ expectedTagsList.get(i).equals(resourceDetails.getTags().get(i));
+ }
+
+ }
+
+ // search by Version
+ @Test
+ public void searchResourceInCatalogMenuByVersionTest() throws Exception {
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getVersion().replace("V", ""));
+ Thread.sleep(500);
+ GeneralUIUtils.getWorkspaceElements().get(0).click();
+ GeneralUIUtils.getSelectList(null, "versionHeader").getFirstSelectedOption().getText()
+ .equals(resourceDetails.getVersion());
+ }
+
+ // search by SpecialCharacters
+ @Test
+ public void searchResourceInCatalogMenuBySpecialCharactersTest() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.GENERAL);
+ GeneralUIUtils.defineDescription(resourceDetails.getDescription() + "!@#$%^&*");
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox("!@#$%^&*");
+ Thread.sleep(500);
+ GeneralUIUtils.getWorkspaceElements().get(0).click();
+ GeneralUIUtils.getWebElementWaitForVisible("description").getText().equals(resourceDetails.getDescription());
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ChangeLifeCycleStatFromCatalogTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ChangeLifeCycleStatFromCatalogTest.java
new file mode 100644
index 0000000000..a75240d2b2
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ChangeLifeCycleStatFromCatalogTest.java
@@ -0,0 +1,103 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.BreadCrumbsButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.LifeCycleStateEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openqa.selenium.WebElement;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ChangeLifeCycleStatFromCatalogTest extends SetupCDTest {
+
+ public ChangeLifeCycleStatFromCatalogTest() {
+ // TODO Auto-generated constructor stub
+ }
+
+ // This test check the status filter .
+
+ private ResourceReqDetails resourceDetails;
+
+ @BeforeMethod
+ public void beforTest() {
+ resourceDetails = ElementFactory.getDefaultResource();
+ }
+
+ @Test
+ public void checkInFromCatalog() throws InterruptedException {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.clickASDCLogo();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getName());
+ List<WebElement> assets = GeneralUIUtils.getWorkspaceElements();
+ if (assets.isEmpty()) {
+ System.out.println("error elements not found.");
+ } else {
+ for (WebElement webElement : assets) {
+ webElement.click();
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ Thread.sleep(2000);
+ System.out.println(ResourceUIUtils.lifeCycleStateUI());
+ System.out.println(LifeCycleStateEnum.CHECKIN.getValue());
+ assertTrue(ResourceUIUtils.lifeCycleStateUI().contentEquals(LifeCycleStateEnum.CHECKIN.getValue()));
+
+ }
+ }
+
+ }
+
+ @Test
+ public void checkOutFromCatalog() throws Exception {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.clickASDCLogo();
+ GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ GeneralUIUtils.catalogSearchBox(resourceDetails.getName());
+ List<WebElement> assets = GeneralUIUtils.getWorkspaceElements();
+ if (assets.isEmpty()) {
+ System.out.println("error elements not found.");
+ } else {
+ for (WebElement webElement : assets) {
+ webElement.click();
+ GeneralUIUtils.checkinCheckout(resourceDetails.getName());
+ assertTrue(ResourceUIUtils.lifeCycleStateUI().contentEquals(LifeCycleStateEnum.CHECKOUT.getValue()));
+
+ }
+ }
+
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ImportAssetInUITest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ImportAssetInUITest.java
new file mode 100644
index 0000000000..8e9df40068
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/ImportAssetInUITest.java
@@ -0,0 +1,480 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CreateAndImportButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.LifeCycleStateEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.ArtifactUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ImportAssetUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.PropertiesUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.RestCDUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class ImportAssetInUITest extends SetupCDTest {
+
+ private ResourceReqDetails resourceDetails;
+
+ @BeforeMethod(alwaysRun = true)
+ public void inializeBeforeImportTest() {
+ GeneralUIUtils.fileName = "JDM_vfc.yml";
+ resourceDetails = ElementFactory.getDefaultResource();
+ resourceDetails.setResourceType(ResourceTypeEnum.VFC.toString());
+ }
+
+ @Test
+ public void importAssetFillGeneralInfoAndSelectIconTest() throws Exception {
+ ResourceUIUtils.importFileWithSendKey(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ CreateAndImportButtonsEnum.IMPORT_CP);
+ ResourceUIUtils.fillGeneralInfoValuesAndIcon(resourceDetails, getUser());
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ ResourceUIUtils.getVFCGeneralInfoAndValidate(resourceDetails, getUser());
+ }
+
+ @Test
+ public void changeImportedAssetFileTest() throws Exception {
+ ImportAssetUIUtils.importAsssetAndFillGeneralInfo(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ String firstFileName = GeneralUIUtils.getWebElementWaitForVisible("filename").getText();
+ AssertJUnit.assertTrue(firstFileName.equals(GeneralUIUtils.fileName));
+ String secondFileName = "Valid_tosca_ReplaceTest.yml";
+ ResourceUIUtils.importFileWithSendKeyBrowse(GeneralUIUtils.FILE_PATH, secondFileName);
+ String secondFileNameFromField = GeneralUIUtils.getWebElementWaitForVisible("filename").getText();
+ // assertThat(fileName, not(secondFileNameFromField));
+ assertNotEquals(GeneralUIUtils.fileName, secondFileNameFromField);
+ }
+
+ @Test
+ public void duplicateFileTest() throws Exception {
+ ResourceUIUtils.importFileWithSendKey(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ CreateAndImportButtonsEnum.IMPORT_CP);
+ ResourceUIUtils.fillGeneralInfoValuesAndIcon(resourceDetails, getUser());
+ String nameofresource = resourceDetails.getName();
+ resourceDetails.setName(getRandomComponentName("SecondImportCDTest"));
+ GeneralUIUtils.checkIn();
+ ImportAssetUIUtils.importAsssetAndFillGeneralInfo(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.clickSaveIcon();
+ equals(ResourceUIUtils.getErrorMessageText("w-sdc-modal-body-content") == GeneralUIUtils.allReadyExistErro);
+ GeneralUIUtils.clickOkButton();
+ resourceDetails.setName(nameofresource);
+
+ }
+
+ @Test
+ public void importInvalidFileTest() throws Exception {
+ GeneralUIUtils.fileName = "InValid_tosca_File .yml";
+ ImportAssetUIUtils.importAsssetAndFillGeneralInfo(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.clickSaveIcon();
+ equals(ResourceUIUtils.getErrorMessageText("w-sdc-modal-body-content") == GeneralUIUtils.toscaErrorMessage);
+ GeneralUIUtils.clickOkButton();
+ }
+
+ @Test
+ public void deleteImportAssetFileTest() throws Exception {
+ ImportAssetUIUtils.importAsssetAndFillGeneralInfo(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ WebElement deleteFileButton = GeneralUIUtils.getDriver()
+ .findElement(By.className("i-sdc-form-file-upload-x-btn"));
+ deleteFileButton.click();
+ WebElement filefield = GeneralUIUtils.getWebElementWaitForVisible("filename");
+ AssertJUnit.assertEquals("", filefield.getText());
+ }
+
+ // Add artifact by Clicking the Place holders button.
+ @Test
+ public void importAssetAddInformationArtifactPlaceHoldersTest() throws Exception {
+ // fileName = "CP_WAN.yml";
+ String artifactByname = "placeHolder";
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.moveToStep(StepsEnum.INFORMATION_ARTIFACT);
+ GeneralUIUtils.getWebElementWaitForVisible("Features");
+ List<WebElement> Placholders = GeneralUIUtils.getDriver().findElements(By.className("add-button"));
+ for (WebElement element : Placholders) {
+ Thread.sleep(500);
+ element.click();
+ // Placholders.get(4).click();
+ System.out.println(element.getText());
+ if (element.getText().equalsIgnoreCase("Add Other Artifact")) {
+ ArtifactUIUtils.valideArtifact(ArtifactUIUtils.addInformationalArtifact("Create New Artifact"), false);
+ } else {
+ ArtifactUIUtils.valideArtifact(ArtifactUIUtils.addInformationalArtifact(null), false);
+ }
+ }
+ }
+
+ // Add information artifact by Clicking the Add button.
+ @Test
+ public void importAssetAddInformationArtifactAddButtonTest() throws Exception {
+ String type = "Create New Artifact";
+ // fileName = "CP_WAN.yml";
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.moveToStep(StepsEnum.INFORMATION_ARTIFACT);
+ GeneralUIUtils.actionBuild(GeneralUIUtils.getWebButton("add-information-artifact-button")).click();
+ ;
+ ;
+ Map<String, String> expected = ArtifactUIUtils.addInformationalArtifact(type);
+ Thread.sleep(1000);
+ ResourceUIUtils.scrollDownPage();
+ ArtifactUIUtils.valideArtifact(expected, false);
+ }
+
+ // Add New property String Type.
+ @Test
+ public void importAssetAddStringPropertyTest() throws Exception {
+ WebElement prop = null;
+ // fileName = "CP_WAN.yml";
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ Thread.sleep(500);
+ GeneralUIUtils.moveToStep(StepsEnum.PROPERTIES);
+ GeneralUIUtils.getEelementBycontainsClassName("data-row");
+ GeneralUIUtils.getWebButton("addGrey").click();
+ Map<String, String> propertyValues = PropertiesUIUtils.addProperties("String-Property", "string",
+ "!This is strig123456@#$%$", "This is description.", null);
+ WebElement elementTohover = GeneralUIUtils.getEelementBycontainsClassName("table-arrow");
+ GeneralUIUtils.actionBuild(elementTohover).click();
+ int counter = 0;
+ try {
+ prop = GeneralUIUtils.getWebElementWaitForVisible(propertyValues.get("name"));
+ } catch (Exception e) {
+ ResourceUIUtils.scrollDownPage();
+ prop = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + propertyValues.get("name") + "']"));
+ } finally {
+ if (prop.isDisplayed()) {
+ counter++;
+ }
+ }
+ PropertiesUIUtils.vlidateProperties(propertyValues);
+ }
+
+ // Add New property Integer Type.
+ @Test
+ public void importAssetAddIntegerPropertyTest() throws Exception {
+ WebElement prop;
+ // fileName = "CP_WAN.yml";
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ Thread.sleep(500);
+ GeneralUIUtils.moveToStep(StepsEnum.PROPERTIES);
+ GeneralUIUtils.getEelementBycontainsClassName("data-row");
+ GeneralUIUtils.getWebButton("addGrey").click();
+ Map<String, String> propertyValues = PropertiesUIUtils.addProperties("Integer-Property", "integer", "123456",
+ "This is description.", null);
+ WebElement elementTohover = GeneralUIUtils.getEelementBycontainsClassName("table-arrow");
+ GeneralUIUtils.actionBuild(elementTohover);
+ int counter = 0;
+ prop = GeneralUIUtils.getWebElementWaitForVisible(propertyValues.get("name"));
+ if (prop.isDisplayed()) {
+ counter++;
+ }
+ if (counter == 0) {
+ ResourceUIUtils.scrollDownPage();
+ prop = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + propertyValues.get("name") + "']"));
+ counter++;
+ }
+ PropertiesUIUtils.vlidateProperties(propertyValues);
+ }
+
+ // Add New property boolean Type.
+ @Test
+ public void importAssetAddBooleanPropertyTest() throws Exception {
+ // fileName = "CP_WAN.yml";
+ WebElement prop;
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.moveToStep(StepsEnum.PROPERTIES);
+ GeneralUIUtils.getEelementBycontainsClassName("data-row");
+ GeneralUIUtils.getWebButton("addGrey").click();
+ Map<String, String> propertyValues = PropertiesUIUtils.addProperties("Boolean-Property", "boolean", "true",
+ "This is boolean description.", null);
+ int counter = 0;
+ WebElement elementTohover = GeneralUIUtils.getDriver().findElement(By.className("table-arrow"));
+ GeneralUIUtils.actionBuild(elementTohover);
+ prop = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + propertyValues.get("name") + "']"));
+ if (prop.isDisplayed()) {
+ counter++;
+ }
+ if (counter == 0) {
+ ResourceUIUtils.scrollDownPage();
+ prop = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + propertyValues.get("name") + "']"));
+ counter++;
+ }
+ AssertJUnit.assertEquals(1, counter);
+ }
+
+ // Add New property float Type.
+ @Test
+ public void importAssetAddFloatPropertyTest() throws Exception {
+ WebElement prop;
+ // fileName = "CP_WAN.yml";
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.moveToStep(StepsEnum.PROPERTIES);
+ GeneralUIUtils.getEelementBycontainsClassName("data-row");
+ GeneralUIUtils.getWebButton("addGrey").click();
+ Map<String, String> propertyValues = PropertiesUIUtils.addProperties("floatProperty", "float", "22.5",
+ "This is description.", null);
+ int counter = 0;
+ WebElement elementTohover = GeneralUIUtils.getDriver().findElement(By.className("table-arrow"));
+ GeneralUIUtils.actionBuild(elementTohover);
+ prop = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + propertyValues.get("name") + "']"));
+ if (prop.isDisplayed()) {
+ counter++;
+ }
+ if (counter == 0) {
+ ResourceUIUtils.scrollDownPage();
+ prop = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + propertyValues.get("name") + "']"));
+ counter++;
+ }
+ PropertiesUIUtils.vlidateProperties(propertyValues);
+ }
+
+ // Edit property.
+ @Test
+ public void importAssetEditPropertiesTest() throws Exception {
+ // fileName = "CP_WAN.yml";
+ Map<String, String> expected = null;
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.moveToStep(StepsEnum.PROPERTIES);
+ GeneralUIUtils.getEelementBycontainsClassName("table-col-general");
+ GeneralUIUtils.getWebButton("addGrey").click();
+ expected = PropertiesUIUtils.addProperties("BooleanProperty", "boolean", "true", "This is boolean description.",
+ null);
+ GeneralUIUtils.getWebElement(GeneralUIUtils.getDriver(), "table-edit-btn").click();
+ ;
+ List<WebElement> properties = GeneralUIUtils.getDriver().findElements(By.xpath("//*[@*='table-edit-btn']"));
+ ;
+ for (WebElement webElement : properties) {
+ webElement.click();
+ GeneralUIUtils.defineDescription("This is Property update");
+ GeneralUIUtils.getWebButton("Update").click();
+ break;
+ }
+ Thread.sleep(2000);
+ GeneralUIUtils.getDriver().findElement(By.xpath("//*[@data-tests-id='BooleanProperty']")).click();
+ Thread.sleep(1000);
+ String actual = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@class='item-opened ng-binding ng-scope']")).getText();
+ assertNotEquals(expected.get("name"), actual);
+ // assertThat(expected.get("name"),not(actual));
+ }
+
+ // **************************************************************************************************
+ // change VFC version
+
+ // this test return error 500;
+ @Test(alwaysRun = false)
+ public void importAssetChangeVersionOfVFCTest() throws Exception {
+ // fileName = "VFC.yml";
+ // resourceDetails.setResourceType(ResourceTypeEnum.VFC.toString());
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.checkinCheckout(resourceDetails.getName());
+ resourceDetails.setVersion("0.2");
+ String version = GeneralUIUtils.getWebElementWaitForVisible("versionHeader").getText();
+ version.equals("0.2");
+ }
+
+ // change VL version
+ // this test return error 500;
+ @Test
+ public void importAssetChangeVersionOfVLTest() throws Exception {
+ GeneralUIUtils.fileName = "VL.yml";
+ resourceDetails.setResourceType(ResourceTypeEnum.VL.getValue());
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.checkinCheckout(resourceDetails.getName());
+ resourceDetails.setVersion("0.2");
+ String version = GeneralUIUtils.getWebElementWaitForVisible("versionHeader").getText();
+ version.equals("0.2");
+ }
+ // change CP version
+
+ @Test
+ public void importAssetChangeVersionOfCPTest() throws Exception {
+ GeneralUIUtils.fileName = "CP_LAN.yml";
+ resourceDetails.setResourceType(ResourceTypeEnum.CP.toString());
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.checkinCheckout(resourceDetails.getName());
+ resourceDetails.setVersion("0.2");
+ String version = GeneralUIUtils.getWebElementWaitForVisible("versionHeader").getText();
+ version.equals("0.2");
+ }
+
+ // @Test
+ // public void viewPageActivityLogTest() throws Exception {
+ // resourceDetails.setResourceType(ResourceTypeEnum.VF.toString());
+ // ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH,
+ // GeneralUIUtils.fileName , resourceDetails, getUser(),
+ // CreateAndImportButtonsEnum.IMPORT_CP);
+ // ResourceUIUtils.waitToFinishButtonEnabled().click();
+ // RestCDUtils.getResource(resourceDetails, getUser());
+ // GeneralUIUtils.checkIn();
+ // GeneralUIUtils.openObjectMenuAndSelectOption(resourceDetails.getUniqueId(),
+ // MenuOptionsEnum.VIEW);
+ // ResourceUIUtils.lifeCycleState();
+ // Thread.sleep(1000);
+ // ViewPageUIUtils.validateActivityLog("Action: Checkin Performed by: Carlos
+ // Santana(cs0008) Status: 200");
+ // }
+
+ // @Test
+ // public void downloadArtifactViewPaage() throws Exception {
+ // importAssetAddInformationArtifactAddButton();
+ // ResourceUIUtils.waitToFinishButtonEnabled().click();
+ // ViewPageUIUtils.openDropDownListOfObject(resourceDetails.getName(),
+ // ImportAssetUIUtils.getAllObjectsOnWorkspace(GeneralUIUtils.getDriver()),
+ // "View",
+ // ImportAssetUIUtils.scrollElement(GeneralUIUtils.getDriver()));
+ // ValidateViewPageParameters.ViewPagedownloadArtifact();
+ // String myheatfile="Heat-File.yaml";
+ // //Assert.assertTrue((GeneralUIUtils.FILE_PATH, "mailmerge.xls"), "Failed
+ // to download
+ // Expected document");
+ // String dowloadedfile=
+ // "C:\\Git_work\\ASDC\\d2-sdnc\\ui-ci\\src\\main\\resources\\Downloads\\"+fileName+"";
+ // int index=dowloadedfile.lastIndexOf("\\");
+ // System.out.println(dowloadedfile.substring(index+1));
+ // File getLatestFile = getLatestFilefromDir();
+ // String fileName = getLatestFile.getName();
+ // Assert.assertTrue(fileName.equals("mailmerge.xls"), "Downloaded file name
+ // is not matching with expected file name");
+ //
+ // }
+
+ @Test
+ public void importAssetcheckInVFCTest() throws Exception {
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.checkIn();
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ assertTrue(ResourceUIUtils.lifeCycleStateUI().contentEquals(LifeCycleStateEnum.CHECKIN.getValue()));
+ }
+
+ @Test
+ public void importAssetcheckOutVFCTest() throws Exception {
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.checkinCheckout(resourceDetails.getName());
+ GeneralUIUtils.clickASDCLogo();
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ assertTrue(ResourceUIUtils.lifeCycleStateUI().contentEquals(LifeCycleStateEnum.CHECKOUT.getValue()));
+ }
+
+ @Test
+ public void importAssetDeleteVFCVersionTest() throws Exception {
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ GeneralUIUtils.checkinCheckout(resourceDetails.getName());
+ GeneralUIUtils.deleteVersionInUI();
+ Thread.sleep(1000);
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ String version = GeneralUIUtils.getWebElementWaitForVisible("versionHeader").getText();
+ AssertJUnit.assertEquals("0.1", version.replace("V", ""));
+ }
+
+ // @Test(enabled = false)
+ // public void importAssetVFCPrintScreenTest() throws Exception {
+ // ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH,
+ // GeneralUIUtils.fileName , resourceDetails, getUser());
+ // ResourceUIUtils.waitToFinishButtonEnabled().click();
+ // ResourceUIUtils.openDropDownListOfObject(resourceDetails.getName(),
+ // ResourceUIUtils.getAllObjectsOnWorkspace(driver,resourceDetails), "Edit",
+ // ResourceUIUtils.scrollElement(driver));
+ // ResourceUIUtils.waitfunctionforelements("sprite-resource-icons", 7);
+ // Thread.sleep(2000);
+ // WebElement element =
+ // GeneralUIUtils.getDriver().findElement(By.className("network"));
+ // WebElement target =
+ // GeneralUIUtils.getDriver().findElement(By.className("dropzone"));
+ // (new Actions(GeneralUIUtils.getDriver())).dragAndDrop(element,
+ // target).perform();
+ // WebElement element1 =
+ // GeneralUIUtils.getDriver().findElement(By.className("network"));
+ // (new Actions(GeneralUIUtils.getDriver())).dragAndDrop(element1,
+ // target).perform();
+ // ResourceUIUtils.clickPrintScreen();
+ //
+ // // the firefox not support print screen.
+ // }
+
+ @Test
+ public void importAssetVFCSubmitForTestingTest() throws Exception {
+ ImportAssetUIUtils.importAsssetFillGeneralInfoAndSelectIcon(GeneralUIUtils.FILE_PATH, GeneralUIUtils.fileName,
+ resourceDetails, getUser(), CreateAndImportButtonsEnum.IMPORT_CP);
+ String name = "";
+ GeneralUIUtils.clickSubmitForTest();
+ Thread.sleep(2000);
+ String url = "http://localhost:8181/sdc1/proxy-tester1#/dashboard";
+ navigateToUrl(url);
+ GeneralUIUtils.getWebElementWaitForVisible("w-sdc-dashboard-card-info");
+ int counter = 0;
+ for (WebElement object : ResourceUIUtils.getAllObjectsOnWorkspace(GeneralUIUtils.getDriver(),
+ resourceDetails)) {
+ if (object.getText().equals(resourceDetails.getName())) {
+ name = object.getText();
+ counter++;
+ }
+ }
+ AssertJUnit.assertEquals(1, counter);
+
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFCanvasTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFCanvasTest.java
new file mode 100644
index 0000000000..51454173de
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFCanvasTest.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Action;
+import org.openqa.selenium.interactions.Actions;
+import org.testng.annotations.Test;
+
+public class VFCanvasTest extends SetupCDTest {
+
+ public List<Integer> getposition(WebElement canvas, int width, int height) {
+
+ width = canvas.getSize().getWidth();
+ height = canvas.getSize().getHeight();
+ Random r = new Random();
+ int Resultx = r.nextInt(width);
+ int Resulty = r.nextInt(height);
+ List<Integer> position = new ArrayList<Integer>();
+ position.add(Resultx);
+ position.add(Resulty);
+ return position;
+ }
+
+ @Test
+ public void VFCanvasTest1() throws Exception {
+ // GeneralUIUtils.waitForContainsdataTestIdVisibility("left-sectioin-element-QA");
+
+ GeneralUIUtils.moveToStep(StepsEnum.COMPOSITION);
+ Thread.sleep(2000);
+ List<Integer> position = null;
+ WebElement canvas = GeneralUIUtils.getWebElementWaitForVisible("canvas");
+ int xPos = 0;
+ int yPos = 0;
+ position = getposition(canvas, xPos, yPos);
+ WebElement otherElement = GeneralUIUtils
+ .getWebElementWaitForVisible("left-sectioin-element-QA left-section-NeutronPort");
+ for (int i = 0; i < 8; i++) {
+ Actions builder = new Actions(GeneralUIUtils.getDriver());
+ Action dragAndDrop = builder.clickAndHold(otherElement)
+ .moveToElement(canvas, position.get(0), position.get(1)).release().build();
+ dragAndDrop.perform();
+ Thread.sleep(2000);
+ }
+ Thread.sleep(2000);
+ Actions builder = new Actions(GeneralUIUtils.getDriver());
+ builder.moveToElement(canvas, position.get(0), position.get(1));
+ builder.clickAndHold();
+ position = getposition(canvas, xPos, yPos);
+ builder.moveToElement(canvas, position.get(0), position.get(1));
+ builder.release();
+ builder.build();
+ builder.perform();
+ builder.moveToElement(canvas, 200, 300);
+ builder.release();
+ builder.perform();
+
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFUITest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFUITest.java
new file mode 100644
index 0000000000..eec04bc297
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VFUITest.java
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+
+public class VFUITest extends SetupCDTest {
+
+ public VFUITest() {
+ super();
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VfTests.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VfTests.java
new file mode 100644
index 0000000000..5550287aba
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/VfTests.java
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+
+public class VfTests extends SetupCDTest {
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/WorkspaceCheckBoxFilterTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/WorkspaceCheckBoxFilterTest.java
new file mode 100644
index 0000000000..792d687a72
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/resourceui/WorkspaceCheckBoxFilterTest.java
@@ -0,0 +1,120 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.resourceui;
+
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class WorkspaceCheckBoxFilterTest extends SetupCDTest {
+
+ // This test check the status filter .
+
+ private ResourceReqDetails resourceDetails;
+
+ @BeforeMethod
+ public void beforTest() {
+ resourceDetails = ElementFactory.getDefaultResource();
+ }
+
+ @Test
+ public void selectCheckOutMenuTest() throws Exception {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.clickASDCLogo();
+ String Status = GeneralUIUtils
+ .checkBoxLifeCyclestate(org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum.CHECKOUT);
+ Thread.sleep(500);
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ AssertJUnit.assertEquals(Status, GeneralUIUtils.getWebElementWaitForVisible("lifecyclestate").getText());
+ }
+
+ @Test
+ public void selectCheckInMenuTest() throws Exception {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.checkIn();
+ String Status = GeneralUIUtils
+ .checkBoxLifeCyclestate(org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum.CHECKIN);
+ Thread.sleep(500);
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ AssertJUnit.assertEquals(Status, GeneralUIUtils.getWebElementWaitForVisible("lifecyclestate").getText());
+ }
+
+ @Test
+ public void selectReadyForTestingMenuTest() throws Exception {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.clickSubmitForTest();
+ String Status = GeneralUIUtils
+ .checkBoxLifeCyclestate(org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum.READY_FOR_TESTING);
+ Thread.sleep(500);
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+
+ AssertJUnit.assertEquals(Status, GeneralUIUtils.getWebElementWaitForVisible("lifecyclestate").getText());
+ }
+
+ @Test
+ public void selectInTestIngMenuTest() throws Exception {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.clickSubmitForTest();
+ GeneralUIUtils.waitForClassNameVisibility("w-sdc-dashboard-card-footer");
+ GeneralUIUtils.getDriver().navigate().to(SetupCDTest.getUrl().replace("designer", "tester"));
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ GeneralUIUtils.clickStartTesting();
+ GeneralUIUtils.clickASDCLogo();
+ GeneralUIUtils.waitForClassNameVisibility("w-sdc-dashboard-card-footer");
+ GeneralUIUtils.getDriver().navigate().to(SetupCDTest.getUrl().replace("tester", "designer"));
+ String Status = GeneralUIUtils
+ .checkBoxLifeCyclestate(org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum.IN_TESTING);
+ Thread.sleep(500);
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+
+ AssertJUnit.assertEquals(Status, GeneralUIUtils.getWebElementWaitForVisible("lifecyclestate").getText());
+ }
+
+ @Test
+ public void selectCertifiedMenuTest() throws Exception {
+ GeneralUIUtils.clickSaveIcon();
+ GeneralUIUtils.clickSubmitForTest();
+ GeneralUIUtils.waitForClassNameVisibility("w-sdc-dashboard-card-footer");
+ GeneralUIUtils.getDriver().navigate().to(SetupCDTest.getUrl().replace("designer", "tester"));
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+ GeneralUIUtils.clickStartTesting();
+ GeneralUIUtils.clickAccept();
+ GeneralUIUtils.waitForClassNameVisibility("w-sdc-dashboard-card-footer");
+ GeneralUIUtils.getDriver().navigate().to(SetupCDTest.getUrl().replace("tester", "designer"));
+ String Status = GeneralUIUtils
+ .checkBoxLifeCyclestate(org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum.CERTIFIED);
+ Thread.sleep(500);
+ GeneralUIUtils.getWebElementWaitForVisible(resourceDetails.getName()).click();
+
+ AssertJUnit.assertEquals(Status, GeneralUIUtils.getWebElementWaitForVisible("lifecyclestate").getText());
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Import.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Import.java
new file mode 100644
index 0000000000..c5017a4f34
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Import.java
@@ -0,0 +1,177 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.sanity;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactInfo;
+import org.openecomp.sdc.ci.tests.datatypes.BreadCrumbsButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.InformationalArtifacts;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.pages.GeneralPageElements;
+import org.openecomp.sdc.ci.tests.pages.InformationalArtifactPage;
+import org.openecomp.sdc.ci.tests.pages.ResourceGeneralPage;
+import org.openecomp.sdc.ci.tests.pages.TesterOperationPage;
+import org.openecomp.sdc.ci.tests.pages.ToscaArtifactsPage;
+import org.openecomp.sdc.ci.tests.utilities.ArtifactUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.FileHandling;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openqa.selenium.ElementNotVisibleException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+import org.testng.annotations.Test;
+
+public class Import extends SetupCDTest {
+
+ @Test
+ public void importResource() throws Exception {
+
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "CPWithAttributes.yml";
+
+ // import Resource
+ ResourceReqDetails resourceMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VFC, getUser());
+ ResourceUIUtils.importVfc(resourceMetaData, filePath, fileName, getUser());
+
+ }
+
+ @Test
+ public void certifyVFC() throws Exception {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "CPWithAttributes.yml";
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+ ResourceGeneralPage.clickCheckinButton(atomicResourceMetaData.getName());
+ ResourceGeneralPage.clickSubmitForTestingButton(atomicResourceMetaData.getName());
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(atomicResourceMetaData.getName());
+ TesterOperationPage.certifyComponent(atomicResourceMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+ // GeneralUIUtils.clickBreadCrumbs(BreadCrumbsButtonsEnum.CATALOG);
+ String cpVersion = GeneralUIUtils.getComponentVersion(atomicResourceMetaData.getName());
+ assertTrue("V 1.0".equals(cpVersion));
+ }
+
+ @Test
+ public void uploadAllInformationalArtifactPlaceholdersInVFC() throws Exception {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "CPWithAttributes.yml";
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+
+ ResourceGeneralPage.getLeftMenu().moveToInformationalArtifactScreen();
+
+ for (InformationalArtifacts infoArtifact : InformationalArtifacts.values()) {
+ ArtifactUIUtils.fillPlaceHolderInformationalArtifact(infoArtifact, filePath, "Heat-File 1.yaml",
+ infoArtifact.name());
+ }
+
+ assertTrue(InformationalArtifactPage.checkElementsCountInTable(InformationalArtifacts.values().length,
+ () -> InformationalArtifactPage.getElemenetsFromTable()));
+
+ InformationalArtifactPage.clickAddNewArtifact();
+ Select artifactLabelList = InformationalArtifactPage.artifactPopup().defineArtifactLabel("");
+ assertEquals(1, artifactLabelList.getAllSelectedOptions().size());
+
+ }
+
+ @Test(expectedExceptions = ElementNotVisibleException.class)
+ public void uploadInformationaArtifactMetdataTest() throws Exception {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "CPWithAttributes.yml";
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+
+ ResourceGeneralPage.getLeftMenu().moveToInformationalArtifactScreen();
+ assertTrue(InformationalArtifactPage.checkElementsCountInTable(0,
+ () -> InformationalArtifactPage.getElemenetsFromTable()));
+
+ ArtifactInfo artifactInfo = new ArtifactInfo(filePath, "Heat-File 1.yaml", "new artifact", "artifact1",
+ "OTHER");
+ InformationalArtifactPage.clickAddNewArtifact();
+ ArtifactUIUtils.fillAndAddNewArtifactParameters(artifactInfo);
+
+ assertTrue(InformationalArtifactPage.checkElementsCountInTable(1,
+ () -> InformationalArtifactPage.getElemenetsFromTable()));
+
+ String actulaArtifactDescription = InformationalArtifactPage
+ .getArtifactDescription(artifactInfo.getArtifactLabel());
+ assertTrue(artifactInfo.getDescription().equals(actulaArtifactDescription));
+
+ InformationalArtifactPage.clickEditArtifact(artifactInfo.getArtifactLabel());
+ InformationalArtifactPage.artifactPopup().defineArtifactLabel("artifact2");
+ }
+
+ // @Test()
+ // public void updateInformationalArtifact(){
+ // ArtifactInfo artifactInfo = new ArtifactInfo("", "", "new artifact",
+ // "artifact1", "");
+ // InformationalArtifactPage.clickEditArtifact("artifact1");
+ // String newDesc = "newDesc";
+ // InformationalArtifactPage.artifactPopup().insertDescription(newDesc);
+ // InformationalArtifactPage.artifactPopup().clickUpdateButton();
+ // String actulaArtifactDescription =
+ // InformationalArtifactPage.getArtifactDescription(artifactInfo.getArtifactLabel());
+ // assertTrue(newDesc.equals(actulaArtifactDescription));
+ // InformationalArtifactPage.clickEditArtifact(artifactInfo.getArtifactLabel());
+ // InformationalArtifactPage.artifactPopup().defineArtifactLabel("artifact2");
+ // InformationalArtifactPage.artifactPopup().selectArtifactType(artifactInfo.getArtifactType());
+ // }
+
+ @Test
+ public void verifyTwoToscaArtifacts() throws Exception {
+ String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "CPWithAttributes.yml";
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+
+ ResourceGeneralPage.getLeftMenu().moveToToscaArtifactsScreen();
+
+ // List<WebElement> elemenetsFromTable =
+ // GeneralPageElements.getElemenetsFromTable();
+ // Supplier<List<WebElement>> supplier = () -> elemenetsFromTable;
+ // assertTrue(ToscaArtifactsPage.checkElementsCountInTable(2,
+ // supplier));
+ assertTrue(ToscaArtifactsPage.checkElementsCountInTable(2));
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Onboard.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Onboard.java
new file mode 100644
index 0000000000..713bc4df41
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Onboard.java
@@ -0,0 +1,222 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.sanity;
+
+import static org.testng.AssertJUnit.assertNotNull;
+
+import java.awt.AWTException;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.ci.tests.datatypes.CanvasElement;
+import org.openecomp.sdc.ci.tests.datatypes.CanvasManager;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ServiceCategoriesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.pages.CompositionPage;
+import org.openecomp.sdc.ci.tests.pages.GeneralPageElements;
+import org.openecomp.sdc.ci.tests.pages.GovernorOperationPage;
+import org.openecomp.sdc.ci.tests.pages.OpsOperationPage;
+import org.openecomp.sdc.ci.tests.pages.ResourceGeneralPage;
+import org.openecomp.sdc.ci.tests.pages.ServiceGeneralPage;
+import org.openecomp.sdc.ci.tests.pages.TesterOperationPage;
+import org.openecomp.sdc.ci.tests.utilities.FileHandling;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.OnboardingUtils;
+import org.openecomp.sdc.ci.tests.utilities.ServiceUIUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.verificator.ServiceVerificator;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.AssertJUnit;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class Onboard extends SetupCDTest {
+
+
+ public static Object[][] provideData(Object[] fileNamesFromFolder, String filepath) {
+ Object[][] arObject = new Object[fileNamesFromFolder.length][];
+
+ int index = 0;
+ for (Object obj : fileNamesFromFolder) {
+ arObject[index++] = new Object[] { filepath, obj };
+ }
+ return arObject;
+ }
+
+ @DataProvider(name = "VNF_List")
+ private static final Object[][] VnfList() throws Exception {
+ String filepath = getFilePath();
+ Object[] fileNamesFromFolder = OnboardingUtils.getZipFileNamesFromFolder(filepath);
+ System.out.println(String.format("There are %s zip file(s) to test", fileNamesFromFolder.length));
+ return provideData(fileNamesFromFolder, filepath);
+ }
+
+ @Test(dataProvider = "VNF_List")
+ public void onboardVNFTest(String filepath, String vnfFile) throws Exception, Throwable {
+ SetupCDTest.setScreenshotFile(vnfFile);
+ extendTest.setDescription(vnfFile);
+
+ String vspName = onboardVNF(filepath, vnfFile);
+
+ ResourceGeneralPage.clickSubmitForTestingButton(vspName);
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(vspName);
+ TesterOperationPage.certifyComponent(vspName);
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+ // create service
+ ServiceReqDetails serviceMetadata = ElementFactory.getDefaultService();
+ ServiceUIUtils.createService(serviceMetadata, getUser());
+
+ ServiceGeneralPage.getLeftMenu().moveToCompositionScreen();
+ CompositionPage.searchForElement(vspName);
+ CanvasManager serviceCanvasManager = CanvasManager.getCanvasManager();
+ CanvasElement vfElement = serviceCanvasManager.createElementOnCanvas(vspName);
+ assertNotNull(vfElement);
+ ServiceVerificator.verifyNumOfComponentInstances(serviceMetadata, "0.1", 1, getUser());
+
+ ServiceGeneralPage.clickSubmitForTestingButton(serviceMetadata.getName());
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(serviceMetadata.getName());
+ TesterOperationPage.certifyComponent(serviceMetadata.getName());
+
+ quitAndReLogin(UserRoleEnum.GOVERNOR);
+ GeneralUIUtils.findComponentAndClick(serviceMetadata.getName());
+ GovernorOperationPage.approveSerivce(serviceMetadata.getName());
+
+// quitAndReLogin(UserRoleEnum.OPS);
+// GeneralUIUtils.findComponentAndClick(serviceMetadata.getName());
+// OpsOperationPage.distributeService();
+// OpsOperationPage.displayMonitor();
+//
+// List<WebElement> rowsFromMonitorTable = OpsOperationPage.getRowsFromMonitorTable();
+// AssertJUnit.assertEquals(1, rowsFromMonitorTable.size());
+//
+// OpsOperationPage.waitUntilArtifactsDistributed(0);
+//
+// extendTest.log(LogStatus.PASS, String.format("onboarding %s test is passed ! ", vnfFile));
+ }
+
+ private String onboardVNF(String filepath, String vnfFile) throws Exception, Throwable {
+ extendTest.log(LogStatus.INFO, String.format("going to onboard the VNF %s......", vnfFile));
+ System.out.println(String.format("going to onboard the VNF %s......", vnfFile));
+
+ OnboardingUtils.createVendorLicense(getUser());
+ String vspName = OnboardingUtils.createVendorSoftwareProduct(vnfFile, filepath, getUser());
+ GeneralUIUtils.getWebButton("repository-icon").click();
+ extendTest.log(LogStatus.INFO, String.format("searching for onboarded %s", vnfFile));
+ GeneralUIUtils.getWebElementWaitForVisible("onboarding-search").sendKeys(vspName);
+ AssertJUnit.assertTrue(GeneralPageElements.checkElementsCountInTable(2));
+
+ List<WebElement> elemenetsFromTable = GeneralPageElements.getElemenetsFromTable();
+ GeneralUIUtils.waitForLoader();
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 30);
+ WebElement findElement = wait.until(ExpectedConditions.visibilityOf(elemenetsFromTable.get(1)));
+ findElement.click();
+ GeneralUIUtils.waitForLoader();
+ extendTest.log(LogStatus.INFO,
+ String.format("going to import %s......", vnfFile.substring(0, vnfFile.indexOf("."))));
+ GeneralUIUtils.getWebElementWaitForVisible("import-csar").click();
+ GeneralUIUtils.getWebButton("create/save").click();
+ GeneralUIUtils.waitForLoaderOnboarding();
+ WebDriverWait wait2 = new WebDriverWait(GeneralUIUtils.getDriver(), 2 * 60);
+ wait2.until(ExpectedConditions.visibilityOfElementLocated(
+ By.xpath("//*[@data-tests-id='" + DataTestIdEnum.LifeCyleChangeButtons.CHECK_IN.getValue() + "']")));
+ extendTest.log(LogStatus.PASS,
+ String.format("succeeded to import %s......", vnfFile.substring(0, vnfFile.indexOf("."))));
+ return vspName;
+ }
+
+ public static String getFilePath() {
+ String filepath = System.getProperty("filepath");
+ if (filepath == null && System.getProperty("os.name").contains("Windows")) {
+ filepath = FileHandling.getResourcesFilesPath();
+ }
+
+ else if(filepath.isEmpty() && !System.getProperty("os.name").contains("Windows")){
+ filepath = FileHandling.getBasePath() + File.separator + "Files";
+ }
+ return filepath;
+ }
+
+ @Test
+ public void twoOnboardedVNFsInService() throws Exception, Throwable{
+
+ String filepath = getFilePath();
+
+ final String dnsScaling = "DNSscaling12.8.16.zip";
+ final String vLB = "vLB12.8.16.zip";
+
+ String[] onboardList = {dnsScaling, vLB};
+
+ Map<String, String> vspMap = new HashMap<String,String>();
+
+ for (String vnf : onboardList){
+ GeneralUIUtils.waitForElementsListInvisibility(By.className("ui-notification"));
+ String vspName = onboardVNF(filepath, vnf);
+ vspMap.put(vnf, vspName);
+ ResourceGeneralPage.clickSubmitForTestingButton(vspName);
+ }
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ for (String vspName : vspMap.values()){
+ GeneralUIUtils.waitForElementsListInvisibility(By.className("ui-notification"));
+ GeneralUIUtils.findComponentAndClick(vspName);
+ TesterOperationPage.certifyComponent(vspName);
+ }
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+ ServiceReqDetails serviceMetadata = ElementFactory.getDefaultService();
+ ServiceUIUtils.createService(serviceMetadata, getUser());
+ ServiceGeneralPage.getLeftMenu().moveToCompositionScreen();
+ CanvasManager serviceCanvasManager = CanvasManager.getCanvasManager();
+
+ Map<String, CanvasElement> canvasElements = new HashMap<String,CanvasElement>();
+ for (String vspName : vspMap.values()){
+ CompositionPage.searchForElement(vspName);
+ CanvasElement vfElement = serviceCanvasManager.createElementOnCanvas(vspName);
+ assertNotNull(vfElement);
+ canvasElements.put(vspName, vfElement);
+ }
+ ServiceVerificator.verifyNumOfComponentInstances(serviceMetadata, "0.1", 2, getUser());
+
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Vf.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Vf.java
new file mode 100644
index 0000000000..96a22ef2fe
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/sanity/Vf.java
@@ -0,0 +1,377 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.sanity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactInfo;
+import org.openecomp.sdc.ci.tests.datatypes.CanvasElement;
+import org.openecomp.sdc.ci.tests.datatypes.CanvasManager;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.InformationalArtifacts;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.ci.tests.datatypes.PropertyInfo;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.NormativeTypesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.PropertyTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.pages.CompositionPage;
+import org.openecomp.sdc.ci.tests.pages.DeploymentArtifactPage;
+import org.openecomp.sdc.ci.tests.pages.GovernorOperationPage;
+import org.openecomp.sdc.ci.tests.pages.InformationalArtifactPage;
+import org.openecomp.sdc.ci.tests.pages.OpsOperationPage;
+import org.openecomp.sdc.ci.tests.pages.PropertiesPage;
+import org.openecomp.sdc.ci.tests.pages.ResourceGeneralPage;
+import org.openecomp.sdc.ci.tests.pages.ServiceGeneralPage;
+import org.openecomp.sdc.ci.tests.pages.TesterOperationPage;
+import org.openecomp.sdc.ci.tests.utilities.ArtifactUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.FileHandling;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.PropertiesUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ServiceUIUtils;
+import org.openecomp.sdc.ci.tests.utils.general.ElementFactory;
+import org.openecomp.sdc.ci.tests.verificator.ServiceVerificator;
+import org.openecomp.sdc.ci.tests.verificator.VfVerificator;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+import org.testng.AssertJUnit;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class Vf extends SetupCDTest {
+
+ private String filePath;
+
+ @BeforeMethod
+ public void beforeTest() {
+ filePath = System.getProperty("filepath");
+ if (filePath == null) {
+ filePath = FileHandling.getResourcesFilesPath();
+ }
+ }
+
+ @Test
+ public void createVF() throws Exception {
+
+ // create Resource
+ ResourceReqDetails resourceMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, getUser());
+ ResourceUIUtils.createResource(resourceMetaData, getUser());
+
+ }
+
+ @Test
+ public void updateVF() throws Exception {
+
+ // create Resource
+ ResourceReqDetails resourceMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, getUser());
+ ResourceUIUtils.createResource(resourceMetaData, getUser());
+
+ // update Resource
+ String expectedDesc = "kuku";
+ resourceMetaData.setDescription(expectedDesc);
+ ResourceGeneralPage.defineDescription(expectedDesc);
+ GeneralUIUtils.clickUpdateButton();
+
+ VfVerificator.verifyVFUpdatedInUI(resourceMetaData);
+ }
+
+ @Test
+ public void vfcLinkedToComputeInVfWithArtifactsFlow() throws Exception {
+ // String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "vFW_VFC.yml";
+
+ // import Resource
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+ ResourceGeneralPage.getLeftMenu().moveToInformationalArtifactScreen();
+ ArtifactUIUtils.fillPlaceHolderInformationalArtifact(InformationalArtifacts.CAPACITY, filePath,
+ "asc_heat 0 2.yaml", "capacity");
+ ArtifactUIUtils.fillPlaceHolderInformationalArtifact(InformationalArtifacts.FEATURES, filePath,
+ "asc_heat 0 2.yaml", "features");
+ ResourceGeneralPage.clickSubmitForTestingButton(atomicResourceMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(atomicResourceMetaData.getName());
+ TesterOperationPage.certifyComponent(atomicResourceMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+
+ // create Resource
+ ResourceReqDetails vfMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, getUser());
+ ResourceUIUtils.createResource(vfMetaData, getUser());
+
+ ResourceGeneralPage.getLeftMenu().moveToDeploymentArtifactScreen();
+
+ ArtifactInfo artifact1 = new ArtifactInfo(filePath, "asc_heat 0 2.yaml", "kuku", "artifact1", "OTHER");
+ DeploymentArtifactPage.clickAddNewArtifact();
+ ArtifactUIUtils.fillAndAddNewArtifactParameters(artifact1);
+ ArtifactInfo artifact2 = new ArtifactInfo(filePath, "sample-xml-alldata-1-1.xml", "kuku", "artifact2",
+ "YANG_XML");
+ DeploymentArtifactPage.clickAddNewArtifact();
+ ArtifactUIUtils.fillAndAddNewArtifactParameters(artifact2);
+
+ DeploymentArtifactPage.getLeftMenu().moveToCompositionScreen();
+ CanvasManager canvasManager = CanvasManager.getCanvasManager();
+ CanvasElement computeElement = canvasManager.createElementOnCanvas(LeftPanelCanvasItems.COMPUTE);
+
+ CompositionPage.searchForElement(atomicResourceMetaData.getName());
+ CanvasElement cpElement = canvasManager.createElementOnCanvas(atomicResourceMetaData.getName());
+ AssertJUnit.assertNotNull(cpElement);
+ ServiceVerificator.verifyNumOfComponentInstances(vfMetaData, "0.1", 2, getUser());
+ canvasManager.linkElements(cpElement, computeElement);
+
+ vfMetaData.setVersion("0.1");
+ VfVerificator.verifyLinkCreated(vfMetaData, getUser(), 1);
+
+ }
+
+ @Test
+ public void addingDeploymentArtifactToVFInstanceInService() throws Exception {
+ // String filePath = FileHandling.getResourcesFilesPath();
+ // create Resource
+ ResourceReqDetails vfMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, getUser());
+ ResourceUIUtils.createResource(vfMetaData, getUser());
+
+ ResourceGeneralPage.getLeftMenu().moveToDeploymentArtifactScreen();
+
+ List<ArtifactInfo> deploymentArtifactList = new ArrayList<ArtifactInfo>();
+ deploymentArtifactList.add(new ArtifactInfo(filePath, "asc_heat 0 2.yaml", "kuku", "artifact1", "OTHER"));
+ deploymentArtifactList
+ .add(new ArtifactInfo(filePath, "sample-xml-alldata-1-1.xml", "cuku", "artifact2", "YANG_XML"));
+ for (ArtifactInfo deploymentArtifact : deploymentArtifactList) {
+ DeploymentArtifactPage.clickAddNewArtifact();
+ ArtifactUIUtils.fillAndAddNewArtifactParameters(deploymentArtifact);
+ }
+
+ ResourceGeneralPage.clickSubmitForTestingButton(vfMetaData.getName());
+ GeneralUIUtils.clickASDCLogo();
+
+ ServiceReqDetails serviceMetadata = ElementFactory.getDefaultService();
+ ServiceUIUtils.createService(serviceMetadata, getUser());
+
+ ServiceGeneralPage.getLeftMenu().moveToCompositionScreen();
+ CompositionPage.searchForElement(vfMetaData.getName());
+ CanvasManager serviceCanvasManager = CanvasManager.getCanvasManager();
+ CanvasElement vfElement = serviceCanvasManager.createElementOnCanvas(vfMetaData.getName());
+
+ serviceCanvasManager.clickOnCanvaElement(vfElement);
+ GeneralUIUtils.waitFordataTestIdVisibility("deployment-artifact-tab").click();
+ GeneralUIUtils.waitFordataTestIdVisibility("add_Artifact_Button").click();
+ GeneralUIUtils.waitForLoader();
+ ArtifactInfo artifact3 = new ArtifactInfo(filePath, "Heat-File.yaml", "kuku", "artifact3",
+ "DCAE_INVENTORY_TOSCA");
+ deploymentArtifactList.add(artifact3);
+ GeneralUIUtils.getWebElementWaitForVisible("artifact-label").sendKeys(artifact3.getArtifactLabel());
+ DeploymentArtifactPage.artifactPopup().selectArtifactType(artifact3.getArtifactType());
+ DeploymentArtifactPage.artifactPopup().insertDescription(artifact3.getDescription());
+ DeploymentArtifactPage.artifactPopup().loadFile(artifact3.getFilepath(), artifact3.getFilename());
+ GeneralUIUtils.getWebElementWaitForVisible("Done").click();
+ GeneralUIUtils.waitForLoader();
+
+ List<WebElement> actualArtifactList = GeneralUIUtils
+ .waitForElementsListVisibility(By.className("i-sdc-designer-sidebar-section-content-item-artifact"));
+ AssertJUnit.assertEquals(deploymentArtifactList.size(), actualArtifactList.size());
+
+ }
+
+ @Test
+ public void distibuteVFCInVFInServiceTest() throws Exception {
+ // String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "vFW_VFC.yml";
+
+ // import Resource
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+ ResourceGeneralPage.getLeftMenu().moveToInformationalArtifactScreen();
+ ArtifactUIUtils.fillPlaceHolderInformationalArtifact(InformationalArtifacts.CAPACITY, filePath,
+ "asc_heat 0 2.yaml", "capacity");
+ ArtifactUIUtils.fillPlaceHolderInformationalArtifact(InformationalArtifacts.FEATURES, filePath,
+ "asc_heat 0 2.yaml", "features");
+ ResourceGeneralPage.clickSubmitForTestingButton(atomicResourceMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(atomicResourceMetaData.getName());
+ TesterOperationPage.certifyComponent(atomicResourceMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+
+ // create Resource
+ ResourceReqDetails vfMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, getUser());
+ ResourceUIUtils.createResource(vfMetaData, getUser());
+
+ ResourceGeneralPage.getLeftMenu().moveToDeploymentArtifactScreen();
+
+ List<ArtifactInfo> deploymentArtifactList = new ArrayList<ArtifactInfo>();
+ deploymentArtifactList.add(new ArtifactInfo(filePath, "asc_heat 0 2.yaml", "kuku", "artifact1", "OTHER"));
+ deploymentArtifactList
+ .add(new ArtifactInfo(filePath, "sample-xml-alldata-1-1.xml", "kuku", "artifact2", "YANG_XML"));
+ for (ArtifactInfo deploymentArtifact : deploymentArtifactList) {
+ DeploymentArtifactPage.clickAddNewArtifact();
+ ArtifactUIUtils.fillAndAddNewArtifactParameters(deploymentArtifact);
+ }
+
+ DeploymentArtifactPage.getLeftMenu().moveToCompositionScreen();
+ CanvasManager vfCanvasManager = CanvasManager.getCanvasManager();
+ CanvasElement computeElement = vfCanvasManager.createElementOnCanvas(LeftPanelCanvasItems.COMPUTE);
+ CompositionPage.searchForElement(atomicResourceMetaData.getName());
+ CanvasElement cpElement = vfCanvasManager.createElementOnCanvas(atomicResourceMetaData.getName());
+
+ vfCanvasManager.linkElements(cpElement, computeElement);
+
+ ResourceGeneralPage.clickSubmitForTestingButton(vfMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(vfMetaData.getName());
+ TesterOperationPage.certifyComponent(vfMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+
+ // create service
+ ServiceReqDetails serviceMetadata = ElementFactory.getDefaultService();
+ ServiceUIUtils.createService(serviceMetadata, getUser());
+
+ ServiceGeneralPage.getLeftMenu().moveToCompositionScreen();
+ CompositionPage.searchForElement(vfMetaData.getName());
+ CanvasManager serviceCanvasManager = CanvasManager.getCanvasManager();
+ CanvasElement vfElement = serviceCanvasManager.createElementOnCanvas(vfMetaData.getName());
+
+ ServiceGeneralPage.clickSubmitForTestingButton(serviceMetadata.getName());
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(serviceMetadata.getName());
+ TesterOperationPage.certifyComponent(serviceMetadata.getName());
+
+ quitAndReLogin(UserRoleEnum.GOVERNOR);
+ GeneralUIUtils.findComponentAndClick(serviceMetadata.getName());
+ GovernorOperationPage.approveSerivce(serviceMetadata.getName());
+
+ quitAndReLogin(UserRoleEnum.OPS);
+ GeneralUIUtils.findComponentAndClick(serviceMetadata.getName());
+ OpsOperationPage.distributeService();
+ OpsOperationPage.displayMonitor();
+
+ List<WebElement> rowsFromMonitorTable = OpsOperationPage.getRowsFromMonitorTable();
+ AssertJUnit.assertEquals(1, rowsFromMonitorTable.size());
+
+ String deploymentArtifactsSize = String.valueOf(deploymentArtifactList.size() + 1);
+
+ OpsOperationPage.waitUntilArtifactsDistributed(deploymentArtifactsSize, 0);
+
+ }
+
+ @Test
+ public void changesInVFCInstanceInVF() throws Exception {
+ // String filePath = FileHandling.getResourcesFilesPath();
+ String fileName = "vFW_VFC.yml";
+
+ // import Resource
+ ResourceReqDetails atomicResourceMetaData = ElementFactory.getDefaultResourceByTypeNormTypeAndCatregory(
+ ResourceTypeEnum.VFC, NormativeTypesEnum.ROOT, ResourceCategoryEnum.NETWORK_L2_3_ROUTERS, getUser());
+ ResourceUIUtils.importVfc(atomicResourceMetaData, filePath, fileName, getUser());
+ ResourceGeneralPage.getLeftMenu().moveToInformationalArtifactScreen();
+ ArtifactUIUtils.fillPlaceHolderInformationalArtifact(InformationalArtifacts.CAPACITY, filePath,
+ "asc_heat 0 2.yaml", "capacity");
+
+ InformationalArtifactPage.getLeftMenu().moveToPropertiesScreen();
+ int propertiesCount = PropertiesPage.getElemenetsFromTable().size();
+
+ PropertyInfo prop1 = new PropertyInfo("p1", "v1", "prop1", PropertyTypeEnum.STRING);
+
+ PropertiesPage.clickAddPropertyArtifact();
+ PropertiesUIUtils.addNewProperty(prop1);
+ AssertJUnit.assertTrue(PropertiesPage.checkElementsCountInTable(propertiesCount + 1,
+ () -> PropertiesPage.getElemenetsFromTable()));
+
+ ResourceGeneralPage.clickSubmitForTestingButton(atomicResourceMetaData.getName());
+
+ ResourceReqDetails vfMetaData = ElementFactory.getDefaultResourceByType(ResourceTypeEnum.VF, getUser());
+ ResourceUIUtils.createResource(vfMetaData, getUser());
+
+ DeploymentArtifactPage.getLeftMenu().moveToCompositionScreen();
+ CanvasManager vfCanvasManager = CanvasManager.getCanvasManager();
+ CompositionPage.searchForElement(atomicResourceMetaData.getName());
+ CanvasElement vfcElement = vfCanvasManager.createElementOnCanvas(atomicResourceMetaData.getName());
+
+ vfCanvasManager.clickOnCanvaElement(vfcElement);
+ CompositionPage.showPropertiesAndAttributesTab();
+ List<WebElement> properties = CompositionPage.getProperties();
+ for (int i = 0; i < 2; i++) {
+ // WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver()
+ // , 30);
+ // WebElement findElement =
+ // wait.until(ExpectedConditions.visibilityOf(properties.get(i).findElement(By.className("i-sdc-designer-sidebar-section-content-item-property-and-attribute-label"))));
+ WebElement findElement = properties.get(i).findElement(
+ By.className("i-sdc-designer-sidebar-section-content-item-property-and-attribute-label"));
+ findElement.click();
+ PropertiesPage.getPropertyPopup().insertPropertyDefaultValue("abc123");
+ PropertiesPage.getPropertyPopup().clickSave();
+ GeneralUIUtils.waitForInvisibileElement(DataTestIdEnum.PropertiesPageEnum.SAVE.getValue());
+ }
+ vfCanvasManager.moveToFreeLocation(vfMetaData.getName());
+ GeneralUIUtils.waitFordataTestIdVisibility("deployment-artifact-tab").click();
+ GeneralUIUtils.waitFordataTestIdVisibility("add_Artifact_Button").click();
+ GeneralUIUtils.waitForLoader();
+ ArtifactInfo artifact3 = new ArtifactInfo(filePath, "Heat-File.yaml", "kuku", "artifact3", "OTHER");
+ GeneralUIUtils.getWebElementWaitForVisible("artifact-label").sendKeys(artifact3.getArtifactLabel());
+ DeploymentArtifactPage.artifactPopup().selectArtifactType(artifact3.getArtifactType());
+ DeploymentArtifactPage.artifactPopup().insertDescription(artifact3.getDescription());
+ DeploymentArtifactPage.artifactPopup().loadFile(artifact3.getFilepath(), artifact3.getFilename());
+ GeneralUIUtils.getWebElementWaitForVisible("Done").click();
+ GeneralUIUtils.waitForLoader();
+
+ quitAndReLogin(UserRoleEnum.TESTER);
+ GeneralUIUtils.findComponentAndClick(atomicResourceMetaData.getName());
+ TesterOperationPage.certifyComponent(atomicResourceMetaData.getName());
+
+ quitAndReLogin(UserRoleEnum.DESIGNER);
+ GeneralUIUtils.findComponentAndClick(vfMetaData.getName());
+ ResourceGeneralPage.getLeftMenu().moveToCompositionScreen();
+ vfCanvasManager = CanvasManager.getCanvasManager();
+ vfCanvasManager.clickOnCanvaElement(vfcElement);
+ // change version
+ GeneralUIUtils.getWebElementByName("changeVersion");
+ Select selectlist = new Select(GeneralUIUtils.getWebElementByName("changeVersion"));
+ selectlist.selectByVisibleText("1.0");
+ GeneralUIUtils.waitForLoader();
+
+ // GeneralUIUtils.waitUntilClickableButton(DataTestIdEnum.LifeCyleChangeButtons.SUBMIT_FOR_TESTING.getValue()).click();
+ // ResourceGeneralPage.clickSubmitForTestingButton(vfMetaData.getName());
+ //
+ // vfMetaData.setVersion("0.1");
+ // VfVerificator.verifyVFLifecycle(vfMetaData, getUser(),
+ // LifecycleStateEnum.READY_FOR_CERTIFICATION);
+ }
+
+ @Override
+ protected UserRoleEnum getRole() {
+ return UserRoleEnum.DESIGNER;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/OnboardCSVReport.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/OnboardCSVReport.java
new file mode 100644
index 0000000000..03c9b0281f
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/OnboardCSVReport.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.setup;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+
+public class OnboardCSVReport {
+
+ private StringBuilder sb;
+ private PrintWriter pw;
+
+ public OnboardCSVReport(String filepath, String filename) {
+ sb = new StringBuilder();
+ try {
+ File csvFile = new File(filepath + filename);
+ pw = new PrintWriter(csvFile);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ public StringBuilder appendStringToFile(String content) {
+ return sb.append(content + ",");
+ }
+
+ public void openNewRow() {
+ sb.append("\n");
+ }
+
+ public void writeRow(String... content) {
+ for (String str : content) {
+ appendStringToFile(str);
+ }
+ openNewRow();
+ }
+
+ public void closeFile() {
+ pw.write(sb.toString());
+ pw.close();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/RemoteWebDriverTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/RemoteWebDriverTest.java
new file mode 100644
index 0000000000..e5fcafb664
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/RemoteWebDriverTest.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.setup;
+
+import java.net.URL;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.openqa.selenium.Platform;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.remote.RemoteWebDriver;
+
+public class RemoteWebDriverTest {
+
+ // @Test
+ public void remoteTest() throws Exception {
+ DesiredCapabilities cap = new DesiredCapabilities().firefox();
+ cap.setPlatform(Platform.WINDOWS);
+ cap.setBrowserName("firefox");
+
+ RemoteWebDriver remoteDriver = new RemoteWebDriver(new URL("http://1.2.3.4:5555/wd/hub"), cap);
+ remoteDriver.navigate().to("http://www.google.co.il");
+ remoteDriver.findElementByName("q").sendKeys("execute automation");
+ remoteDriver.findElementByName("btnK").click();
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/SetupCDTest.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/SetupCDTest.java
new file mode 100644
index 0000000000..a72168e4c2
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/execute/setup/SetupCDTest.java
@@ -0,0 +1,538 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.execute.setup;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.UserCredentials;
+import org.openecomp.sdc.ci.tests.datatypes.enums.UserRoleEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.run.StartTest;
+import org.openecomp.sdc.ci.tests.utilities.FileHandling;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.AssertJUnit;
+import org.testng.ITestResult;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
+
+import com.relevantcodes.extentreports.ExtentReports;
+import com.relevantcodes.extentreports.ExtentTest;
+import com.relevantcodes.extentreports.LogStatus;
+
+public abstract class SetupCDTest {
+
+ public SetupCDTest() {
+ // LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ // lc.getLogger("org.apache").setLevel(Level.INFO);
+ //// System.setProperty("org.apache.commons.logging.Log",
+ // "org.apache.commons.logging.impl.SimpleLog");
+ //// System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire",
+ // "OFF");
+ //// System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient",
+ // "OFF");
+
+ }
+
+ public static Logger logger = Logger.getLogger(SetupCDTest.class.getName());
+
+ /**************** CONSTANTS ****************/
+ private static final String CREDENTIALS_FILE = "credentials.yaml";
+ private static final String REPORT_FILE_NAME = "ASDC_UI_Extent_Report.html";
+ public static final String REPORT_FOLDER = "./ExtentReport/";
+ public static final String SELENIUM_NODE_URL = "http://%s:%s/wd/hub";
+ private static final String SCREENSHOT_FOLDER = REPORT_FOLDER + "screenshots/";
+ private static final String SHORT_CSV_REPORT_FILE_NAME = "ShortReport.csv";
+ private static final int NUM_OF_ATTEMPTS_TO_REFTRESH = 2;
+
+ /**************** USERS ****************/
+ protected static User designerUser;
+ protected static User adminUser;
+ protected static User testerUser;
+ protected static User governorUser;
+ protected static User opsUser;
+ protected static User productManagerUser;
+
+ public static Config config;
+
+ /**************** PRIVATES ****************/
+ private Map<?, ?> credentialsYamlFileMap;
+ private static String url;
+ private User user;
+ private static boolean localEnv = true;
+ private int refreshAttempts = 0;
+
+ protected abstract UserRoleEnum getRole();
+
+ protected ExtentReports extentReport;
+ protected static ExtentTest extendTest;
+ private static String screenshotFile;
+
+ public static String getScreenshotFile() {
+ return screenshotFile;
+ }
+
+ public static void setScreenshotFile(String screenshotFile) {
+ SetupCDTest.screenshotFile = screenshotFile;
+ }
+
+ public static ExtentTest getExtendTest() {
+ return extendTest;
+ }
+
+ private OnboardCSVReport csvReport;
+
+ public OnboardCSVReport getCsvReport() {
+ return csvReport;
+ }
+
+ /**************** BEFORE ****************/
+
+ @BeforeSuite(alwaysRun = true)
+ public void setEnvParameters() throws Exception {
+
+ File dir = new File(REPORT_FOLDER);
+ try {
+ FileUtils.deleteDirectory(dir);
+ } catch (IOException e) {
+ }
+ extentReport = new ExtentReports(REPORT_FOLDER + REPORT_FILE_NAME);
+ csvReport = new OnboardCSVReport(REPORT_FOLDER, SHORT_CSV_REPORT_FILE_NAME);
+
+ System.out.println("Setup....");
+ config = Utils.getConfig();
+ setUrl();
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ public void setBrowserBeforeTest(java.lang.reflect.Method method) throws Exception {
+ extendTest = extentReport.startTest(method.getName());
+ extendTest.log(LogStatus.INFO, "Test started");
+ setBrowserBeforeTest(getRole());
+ }
+
+ /**************** AFTER ****************/
+ @AfterMethod(alwaysRun = true)
+ public void quitAfterTest(ITestResult result) throws Exception {
+
+ StringBuilder sb = new StringBuilder();
+ if (result.getParameters().length != 0) {
+ for (int i = 0; i < result.getParameters().length - 1; i++) {
+ sb.append(result.getParameters()[i].toString() + ",");
+ }
+ sb.append(result.getParameters()[result.getParameters().length - 1].toString());
+ }
+
+ File imageFilePath = GeneralUIUtils.takeScreenshot(screenshotFile, SCREENSHOT_FOLDER, result.getName());
+ final String absolutePath = new File(REPORT_FOLDER).toURI().relativize(imageFilePath.toURI()).getPath();
+ if (result.getStatus() == ITestResult.SUCCESS) {
+ extendTest.log(LogStatus.PASS, "Test Result : <span class='label success'>Success</span>");
+ extendTest.log(LogStatus.PASS,
+ "Finished the test with the following screenshot : " + extendTest.addScreenCapture(absolutePath));
+ csvReport.writeRow(result.getName(), sb.toString(), "PASS");
+ } else if (result.getStatus() == ITestResult.FAILURE || result.getStatus() == ITestResult.SKIP) {
+ extendTest.log(LogStatus.ERROR, "ERROR - The following exepction occured");
+ extendTest.log(LogStatus.ERROR, result.getThrowable());
+ extendTest.log(LogStatus.ERROR,
+ "Failure is described in the following screenshot : " + extendTest.addScreenCapture(absolutePath));
+ extendTest.log(LogStatus.FAIL, "<span class='label failure'>Failure</span>");
+ csvReport.writeRow(result.getName(), sb.toString(), "FAIL");
+ }
+
+ extentReport.endTest(extendTest);
+ extentReport.flush();
+
+ quitBrowser();
+// deleteCreatedComponents2(getCatalogAsMap());
+ }
+
+ @AfterClass(alwaysRun = true)
+ public void afterSuite() {
+ csvReport.closeFile();
+ }
+
+ /*************************************/
+
+ private Map<String, ArrayList<Component>> getCatalogAsMap() throws IOException {
+ RestResponse catalog = CatalogRestUtils.getCatalog(getUser().getUserId());
+ Map<String, ArrayList<Component>> convertCatalogResponseToJavaObject = ResponseParser
+ .convertCatalogResponseToJavaObject(catalog.getResponse());
+ return convertCatalogResponseToJavaObject;
+ }
+
+ private void deleteCreatedComponents2(Map<String, ArrayList<Component>> convertCatalogResponseToJavaObject)
+ throws IOException {
+ final String userId = getUser().getUserId();
+ ArrayList<Component> resourcesArrayList = convertCatalogResponseToJavaObject.get("resources");
+
+ List<String> collect = resourcesArrayList.stream().filter(s -> s.getName().startsWith("ci"))
+ .map(e -> e.getUniqueId()).collect(Collectors.toList());
+ for (String uId : collect) {
+ ResourceRestUtils.deleteResource(uId, userId);
+ }
+
+ resourcesArrayList = convertCatalogResponseToJavaObject.get("services");
+ collect = resourcesArrayList.stream().filter(s -> s.getName().startsWith("ci")).map(e -> e.getUniqueId())
+ .collect(Collectors.toList());
+ for (String uId : collect) {
+ ServiceRestUtils.deleteServiceById(uId, userId);
+ }
+
+ resourcesArrayList = convertCatalogResponseToJavaObject.get("products");
+ collect = resourcesArrayList.stream().filter(s -> s.getName().startsWith("ci")).map(e -> e.getUniqueId())
+ .collect(Collectors.toList());
+ for (String uId : collect) {
+ ProductRestUtils.deleteProduct(uId, userId);
+ }
+
+ }
+
+ /**************** MAIN ****************/
+ public static void main(String[] args) {
+ System.out.println("---------------------");
+ System.out.println("running test from CLI");
+ System.out.println("---------------------");
+
+ String attsdcFilePath = FileHandling.getBasePath() + File.separator + "conf" + File.separator + "sdc.yaml";
+ System.setProperty("config.resource", attsdcFilePath);
+ System.out.println("sdc.yaml file path is : " + attsdcFilePath);
+
+ Object[] testSuitsList = FileHandling
+ .getFileNamesFromFolder(FileHandling.getBasePath() + File.separator + "testSuites", ".xml");
+ if (testSuitsList != null) {
+ System.out.println(String.format("Found %s testSuite(s)", testSuitsList.length));
+ args = Arrays.copyOf(testSuitsList, testSuitsList.length, String[].class);
+ StartTest.main(args);
+ }
+ }
+
+ /***********************************************************************************/
+
+ protected static String setUrl() {
+ url = config.getUrl();
+ if (url == null) {
+ String message = "no URL found";
+ System.out.println(message);
+ Assert.fail(message);
+ } else if (!url.contains("localhost") && !url.contains("127.0.0.1")) {
+ localEnv = false;
+ }
+ return url;
+ }
+
+ private Map<String, String> loadCredentialsFile() throws Exception {
+ File credentialsFile = new File(
+ FileHandling.getBasePath() + File.separator + "conf" + File.separator + CREDENTIALS_FILE);
+ if (!credentialsFile.exists()) {
+ credentialsFile = new File(FileHandling.getConfFilesPath() + CREDENTIALS_FILE);
+ }
+ credentialsYamlFileMap = (Map<String, String>) FileHandling.parseYamlFile(credentialsFile.getAbsolutePath());
+ return (Map<String, String>) credentialsYamlFileMap;
+ }
+
+ protected UserCredentials getUserCredentialsFromFile(String userRole) throws Exception {
+ Map<String, String> credentialsMap = (Map<String, String>) credentialsYamlFileMap.get(userRole);
+ String user = (String) credentialsMap.get("username");
+ String password = (String) credentialsMap.get("password");
+ String firstname = (String) credentialsMap.get("firstname");
+ String lastname = (String) credentialsMap.get("lastname");
+
+ return new UserCredentials(user, password, firstname, lastname);
+ }
+
+ public UserCredentials updateUserUserId(String role) throws Exception {
+ System.out.println("updating...");
+ UserCredentials designerCredentialsFromFile = null;
+ UserCredentials testerCredentialsFromFile = null;
+ UserCredentials adminCredentialsFromFile = null;
+ UserCredentials opsCredentialsFromFile = null;
+ UserCredentials governorCredentialsFromFile = null;
+ UserCredentials productCredentialsFromFile = null;
+ UserCredentials productManagerCredentialsFromFile = null;
+
+ String lowerCaseRole = role.toLowerCase();
+ try {
+ if (lowerCaseRole.equals("designer")) {
+ designerCredentialsFromFile = getUserCredentialsFromFile("designer");
+ designerUser.setUserId(designerCredentialsFromFile.getUserId());
+ designerUser.setFirstName(designerCredentialsFromFile.getFirstName());
+ designerUser.setLastName(designerCredentialsFromFile.getLastName());
+ return designerCredentialsFromFile;
+ } else if (lowerCaseRole.equals("tester")) {
+ testerCredentialsFromFile = getUserCredentialsFromFile("tester");
+ testerUser.setUserId(testerCredentialsFromFile.getUserId());
+ testerUser.setFirstName(testerCredentialsFromFile.getFirstName());
+ testerUser.setLastName(testerCredentialsFromFile.getLastName());
+ return testerCredentialsFromFile;
+ } else if (lowerCaseRole.equals("admin")) {
+ adminCredentialsFromFile = getUserCredentialsFromFile("admin");
+ adminUser.setUserId(adminCredentialsFromFile.getUserId());
+ adminUser.setFirstName(adminCredentialsFromFile.getFirstName());
+ adminUser.setLastName(adminCredentialsFromFile.getLastName());
+ return adminCredentialsFromFile;
+ } else if (lowerCaseRole.equals("ops")) {
+ opsCredentialsFromFile = getUserCredentialsFromFile("ops");
+ opsUser.setUserId(opsCredentialsFromFile.getUserId());
+ opsUser.setFirstName(opsCredentialsFromFile.getFirstName());
+ opsUser.setLastName(opsCredentialsFromFile.getLastName());
+ return opsCredentialsFromFile;
+ } else if (lowerCaseRole == "governor") {
+ governorCredentialsFromFile = getUserCredentialsFromFile("governor");
+ governorUser.setUserId(governorCredentialsFromFile.getUserId());
+ governorUser.setFirstName(governorCredentialsFromFile.getFirstName());
+ governorUser.setLastName(governorCredentialsFromFile.getLastName());
+ return governorCredentialsFromFile;
+ } else if (lowerCaseRole == "product_local") {
+ productCredentialsFromFile = getUserCredentialsFromFile("product_local");
+ productManagerUser.setUserId(productCredentialsFromFile.getUserId());
+ productManagerUser.setFirstName(productCredentialsFromFile.getFirstName());
+ productManagerUser.setLastName(productCredentialsFromFile.getLastName());
+ return productCredentialsFromFile;
+ } else if (lowerCaseRole == "product_manager") {
+ productManagerCredentialsFromFile = getUserCredentialsFromFile("product_manager");
+ productManagerUser.setUserId(productManagerCredentialsFromFile.getUserId());
+ productManagerUser.setFirstName(productManagerCredentialsFromFile.getFirstName());
+ productManagerUser.setLastName(productManagerCredentialsFromFile.getLastName());
+ return productManagerCredentialsFromFile;
+ }
+ }
+
+ catch (Exception e) {
+ System.out.print("An exception occured...");
+ System.out.println("->exception message is : " + e.getMessage());
+ }
+
+ return null;
+ }
+
+ public static void navigateToUrl(String url) throws Exception {
+
+ try {
+ WebDriver driver = GeneralUIUtils.getDriver();
+ System.out.println("navigating to URL :" + url);
+ driver.manage().window().maximize();
+ driver.manage().deleteAllCookies();
+ driver.navigate().to(url);
+ GeneralUIUtils.windowZoomOut();
+ GeneralUIUtils.waitForLoader();
+ } catch (Exception e) {
+ System.out.println("browser is unreachable");
+ extendTest.log(LogStatus.ERROR, "browser is unreachable");
+ Assert.fail("browser is unreachable");
+ }
+ }
+
+ protected void loginToSystem(UserCredentials credentials, UserRoleEnum role) throws Exception {
+
+ sendUserAndPasswordKeys(credentials);
+ refreshAttempts = (refreshAttempts == 0) ? NUM_OF_ATTEMPTS_TO_REFTRESH : refreshAttempts;
+ if (!getRole().equals(UserRoleEnum.ADMIN)) {
+ try {
+ if(!localEnv){
+// GeneralUIUtils.ultimateWait();
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 3 * 60);
+ WebElement sdcApp = wait.until(ExpectedConditions.elementToBeClickable(By.id("app-image-SDC")));
+ sdcApp.click();
+ GeneralUIUtils.getDriver().switchTo().frame(1);
+ GeneralUIUtils.waitFordataTestIdVisibility("main-menu-input-search");
+ }
+ else{
+ enterToUserWorkspace();
+ }
+
+ } catch (Exception e) {
+ refreshAttempts--;
+ if (refreshAttempts <= 0) {
+ System.out.println("ERR : Something is wrong with browser!");
+ Assert.fail("ERR : Something is wrong with browser!");
+ }
+ System.out.println("trying again...");
+ System.out.println(String.format("%s attempt(s) left", refreshAttempts));
+ extendTest.log(LogStatus.INFO, "trying again...");
+ extendTest.log(LogStatus.INFO, String.format("%s attempt(s) left", refreshAttempts));
+
+ quitAndReLogin(role);
+ }
+ }
+ }
+
+ private void sendUserAndPasswordKeys(UserCredentials userId) {
+
+ if (localEnv){
+ System.out.println("Login with user : " + userId.getUserId());
+ WebElement userNameTextbox = GeneralUIUtils.waitForElementVisibility(By.name("userid"));
+ userNameTextbox.sendKeys(userId.getUserId());
+ WebElement passwordTextbox = GeneralUIUtils.waitForElementVisibility(By.name("password"));
+ passwordTextbox.sendKeys(userId.getPassword());
+
+ WebElement submitButton = GeneralUIUtils.waitForElementVisibility(By.name("btnSubmit"));
+ submitButton.click();
+ WebElement buttonOK = GeneralUIUtils.waitForElementVisibility(By.name("successOK"));
+ AssertJUnit.assertTrue(buttonOK.isDisplayed());
+ buttonOK.click();
+ }
+ else
+ {
+ System.out.println("Login with user : " + userId.getUserId());
+ WebElement userNameTextbox = GeneralUIUtils.getDriver().findElement(By.cssSelector("input[type='text']"));
+ userNameTextbox.sendKeys(userId.getUserId());
+ WebElement passwordTextbox = GeneralUIUtils.getDriver().findElement(By.cssSelector("input[type='password']"));
+ passwordTextbox.sendKeys(userId.getPassword());
+
+ GeneralUIUtils.getDriver().findElement(By.id("loginBtn")).click();
+ }
+
+
+ }
+
+ public static String getUrl() {
+ return url;
+ }
+
+ public static void setUrl(String url) {
+ SetupCDTest.url = url;
+ }
+
+ public static Config getConfig() {
+ return config;
+ }
+
+ public void loginToSystem(UserRoleEnum role){
+ WebDriver driver = GeneralUIUtils.getDriver();
+ WebDriverWait wait = new WebDriverWait(driver, 30);
+
+ wait.until(ExpectedConditions.visibilityOf(driver.findElement(By.xpath("//*[@method='" + "post" + "']"))));
+
+ WebElement userIdTextbox = GeneralUIUtils.waitForElementVisibility(By.name("userId"));
+ userIdTextbox.sendKeys(role.getUserId());
+ WebElement passwordTextbox = GeneralUIUtils.waitForElementVisibility(By.name("password"));
+ passwordTextbox.sendKeys("123123a");
+
+ wait.until(ExpectedConditions.elementToBeClickable(driver.findElement(By.xpath("//*[@value='" + "Submit" + "']")))).click();
+
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void loginWithUser(UserRoleEnum role) {
+ setUser(role);
+ try {
+ navigateToUrl(url);
+ extendTest.log(LogStatus.INFO, String.format("login with user %s", role.name().toUpperCase()));
+ if (localEnv) {
+ loginToSystem(role);
+ enterToUserWorkspace();
+ }
+ else{
+ loadCredentialsFile();
+ UserCredentials credentials = getUserCredentialsFromFile(role.name().toLowerCase());
+ loginToSystem(credentials, role);
+ user = credentials;
+ }
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void enterToUserWorkspace() {
+ WebElement enterToUserWorkspaceButton = GeneralUIUtils.waitForElementVisibility(By.className("asdc-welcome-main-back-btn"), 3 * 60);
+ enterToUserWorkspaceButton.click();
+ System.out.println("Entering to system...");
+ GeneralUIUtils.waitForLoader();
+ }
+
+ private void setUser(UserRoleEnum role) {
+ user = new User();
+ user.setUserId(role.getUserId());
+ user.setFirstName(role.getFirstName());
+ user.setRole(role.name());
+ user.setLastName(role.getLastName());
+ }
+
+ public User getUser() {
+ return user;
+ }
+
+ protected void setBrowserBeforeTest(UserRoleEnum role) {
+ refreshAttempts = 0;
+ System.out.println(String.format("Setup before test as %s", role.toString().toUpperCase()));
+ GeneralUIUtils.initDriver();
+ loginWithUser(role);
+ }
+
+ public User getUser(UserRoleEnum role) {
+ User user = new User();
+ user = new User();
+ user.setUserId(role.getUserId());
+ user.setFirstName(role.getFirstName());
+ user.setRole(role.name());
+ return user;
+ }
+
+ protected void quitAndReLogin(UserRoleEnum role) throws Exception {
+ quitBrowser();
+ if (localEnv) {
+ loginToSystem(role);
+ }
+ setBrowserBeforeTest(role);
+ }
+
+ private void quitBrowser() {
+ System.out.println("Closing browser...");
+ GeneralUIUtils.getDriver().quit();
+ }
+
+
+ protected String getRandomComponentName(String prefix) {
+ return prefix + randomNumber();
+ }
+
+ protected int randomNumber() {
+ Random r = new Random();
+ return r.nextInt(10000);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ComponentLeftMenu.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ComponentLeftMenu.java
new file mode 100644
index 0000000000..d795599f24
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ComponentLeftMenu.java
@@ -0,0 +1,25 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+public interface ComponentLeftMenu {
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/CompositionPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/CompositionPage.java
new file mode 100644
index 0000000000..e515ce9d64
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/CompositionPage.java
@@ -0,0 +1,56 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.WebElement;
+
+public class CompositionPage extends GeneralPageElements {
+
+ public CompositionPage() {
+ super();
+ }
+
+ public static void searchForElement(String elementName) {
+ WebElement searchField = GeneralUIUtils.getWebElementWaitForVisible("searchAsset");
+ searchField.clear();
+ searchField.sendKeys(elementName);
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(500);
+ }
+
+ public static void showDeploymentArtifactTab() {
+ GeneralUIUtils.waitFordataTestIdVisibility("deployment-artifact-tab").click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static void showPropertiesAndAttributesTab() {
+ GeneralUIUtils.waitFordataTestIdVisibility("properties-and-attributes-tab").click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static List<WebElement> getProperties() {
+ return PropertiesPage.getElemenetsFromTable();
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/DeploymentArtifactPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/DeploymentArtifactPage.java
new file mode 100644
index 0000000000..4c4edd8684
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/DeploymentArtifactPage.java
@@ -0,0 +1,91 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openqa.selenium.WebElement;
+
+public class DeploymentArtifactPage extends GeneralPageElements {
+
+ public DeploymentArtifactPage() {
+ super();
+ }
+
+ public static ResourceLeftMenu getLeftPanel() {
+ return new ResourceLeftMenu();
+ }
+
+ public static UploadArtifactPopup artifactPopup() {
+ return new UploadArtifactPopup();
+ }
+
+ protected static void addNewArtifact(ArtifactGroupTypeEnum artifactGroupType) {
+ switch (artifactGroupType) {
+ case DEPLOYMENT:
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPageEnum.ADD_DEPLOYMENT_ARTIFACT.getValue()).click();
+ break;
+ case INFORMATIONAL:
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPageEnum.ADD_INFORMATIONAL_ARTIFACT.getValue()).click();
+ break;
+ default:
+ break;
+ }
+ }
+
+ public static void clickAddNewArtifact() {
+ addNewArtifact(ArtifactGroupTypeEnum.DEPLOYMENT);
+ }
+
+ public static void clickAddAnotherArtifact() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPageEnum.ADD_ANOTHER_ARTIFACT.getValue()).click();
+ }
+
+ public static void clickEditArtifact(String artifactLabel) {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPageEnum.EDIT_ARTIFACT.getValue() + artifactLabel).click();
+ }
+
+ public static void clickDeleteArtifact(String artifactLabel) {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPageEnum.DELETE_ARTIFACT.getValue() + artifactLabel).click();
+ }
+
+ public static void clickDownloadArtifact(String artifactLabel) {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPageEnum.DOWNLOAD_ARTIFACT.getValue() + artifactLabel)
+ .click();
+ }
+
+ public static String getArtifactDescription(String artifactLabel) throws Exception {
+ clickOnArtifact(artifactLabel); // open artifact
+ WebElement artifactDescriptionElement = GeneralUIUtils.getWebElementWaitForVisible(
+ DataTestIdEnum.ArtifactPageEnum.GET_DEPLOYMENT_ARTIFACT_DESCRIPTION.getValue());
+ String artifactDesc = artifactDescriptionElement.getText();
+ clickOnArtifact(artifactLabel); // close artifact
+
+ return artifactDesc;
+ }
+
+ public static void clickOnArtifact(String artifactLabel) throws Exception {
+ GeneralUIUtils.getWebButton(artifactLabel).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GeneralPageElements.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GeneralPageElements.java
new file mode 100644
index 0000000000..fea3843b7a
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GeneralPageElements.java
@@ -0,0 +1,139 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import static org.testng.AssertJUnit.assertTrue;
+import java.util.List;
+import java.util.function.Supplier;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.LifeCycleStateEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class GeneralPageElements {
+
+ public GeneralPageElements() {
+ super();
+ }
+
+ public static ResourceLeftMenu getLeftMenu() {
+ return new ResourceLeftMenu();
+ }
+
+ public static void clickCreateButton() {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "creating...");
+ GeneralUIUtils.getWebButton(DataTestIdEnum.GeneralElementsEnum.CREATE_BUTTON.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static void clickCheckinButton(String componentName) throws Exception {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "clicking on checkin");
+ GeneralUIUtils.getWebButton(DataTestIdEnum.GeneralElementsEnum.CHECKIN_BUTTON.getValue()).click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.ACCEP_TESTING_MESSAGE.getValue())
+ .sendKeys("Checkin " + componentName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ assertTrue(GeneralUIUtils.getWebElementWaitForVisible("formlifecyclestate").getText()
+ .equals(LifeCycleStateEnum.CHECKIN.getValue()));
+ }
+
+ public static void clickSubmitForTestingButton(String componentName) throws Exception {
+ try {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "submiting for testing");
+ GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.SUBMIT_FOR_TESTING.getValue())
+ .click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.SUMBIT_FOR_TESTING_MESSAGE.getValue())
+ .sendKeys("Submit for testing for " + componentName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(2000);
+ GeneralUIUtils.getWebElementWaitForVisible("main-menu-input-search");
+ } catch (Exception e) {
+ throw e;
+ }
+ }
+
+ public static void clickDeleteVersionButton() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.GeneralElementsEnum.DELETE_VERSION_BUTTON.getValue()).click();
+ }
+
+ public static void clickRevertButton() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.GeneralElementsEnum.REVERT_BUTTON.getValue()).click();
+ }
+
+ public static String getLifeCycleState() {
+ return GeneralUIUtils.getWebButton(DataTestIdEnum.GeneralElementsEnum.LIFECYCLE_STATE.getValue()).getText();
+ }
+
+ public static void selectVersion(String version) {
+ GeneralUIUtils.getSelectList(version, DataTestIdEnum.GeneralElementsEnum.VERSION_HEADER.getValue());
+ }
+
+ public static List<WebElement> getElemenetsFromTable() {
+ GeneralUIUtils.waitForLoader();
+ return GeneralUIUtils.getElemenetsFromTable(By.className("flex-container"));
+ }
+
+ public static boolean checkElementsCountInTable(int expectedElementsCount) {
+ // int maxWaitingPeriodMS = 1000;
+ // int napPeriodMS = 100;
+ // int sumOfWaiting = 0;
+ // List<WebElement> elememts = null;
+ // boolean isKeepWaiting = false;
+ // while (!isKeepWaiting){
+ // GeneralUIUtils.sleep(napPeriodMS);
+ // sumOfWaiting += napPeriodMS;
+ // elememts = getElemenetsFromTable();
+ // isKeepWaiting = ( expectedElementsCount == elememts.size() );
+ // if (sumOfWaiting > maxWaitingPeriodMS)
+ // return false;
+ // }
+ //
+ // return true;
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "checking number of elements in table");
+ return checkElementsCountInTable(expectedElementsCount, () -> getElemenetsFromTable());
+ }
+
+ public static boolean checkElementsCountInTable(int expectedElementsCount, Supplier<List<WebElement>> func) {
+ int maxWaitingPeriodMS = 10000;
+ int napPeriodMS = 100;
+ int sumOfWaiting = 0;
+ List<WebElement> elements = null;
+ boolean isKeepWaiting = false;
+ while (!isKeepWaiting) {
+ GeneralUIUtils.sleep(napPeriodMS);
+ sumOfWaiting += napPeriodMS;
+ elements = func.get();
+ isKeepWaiting = (expectedElementsCount == elements.size());
+ if (sumOfWaiting > maxWaitingPeriodMS)
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GovernorOperationPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GovernorOperationPage.java
new file mode 100644
index 0000000000..9e7eaf1497
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/GovernorOperationPage.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class GovernorOperationPage {
+
+ public GovernorOperationPage() {
+ super();
+ }
+
+ public static void approveSerivce(String serviceName) {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "approving distrbution the service " + serviceName);
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.DistributionChangeButtons.APPROVE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.DistributionChangeButtons.APPROVE_MESSAGE.getValue())
+ .sendKeys("service " + serviceName + " tested successfuly");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible("main-menu-input-search");
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/IconPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/IconPage.java
new file mode 100644
index 0000000000..240272ae30
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/IconPage.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ResourceCategoryEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+
+public class IconPage extends GeneralPageElements {
+
+ public IconPage() {
+ super();
+ }
+
+ public static void clickOnIcon(ResourceCategoryEnum iconName) {
+ GeneralUIUtils.getWebButton(iconStringBuilder(iconName) + DataTestIdEnum.ServiceMetadataEnum.ICON.getValue())
+ .click();
+ }
+
+ private static String iconStringBuilder(ResourceCategoryEnum icon) {
+ String iconName = icon.getSubCategory();
+ String[] splitedIconName = iconName.split(" ");
+ splitedIconName[0] = splitedIconName[0].toLowerCase();
+
+ StringBuilder sb = new StringBuilder();
+ for (String word : splitedIconName) {
+ sb.append(word);
+ }
+
+ return sb.toString();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/InformationalArtifactPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/InformationalArtifactPage.java
new file mode 100644
index 0000000000..1dbf4dfa61
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/InformationalArtifactPage.java
@@ -0,0 +1,57 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import java.util.List;
+
+import javax.lang.model.util.Elements;
+
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+public class InformationalArtifactPage extends DeploymentArtifactPage {
+
+ public InformationalArtifactPage() {
+ super();
+ }
+
+ public static void clickAddNewArtifact() {
+ addNewArtifact(ArtifactGroupTypeEnum.INFORMATIONAL);
+ }
+
+ public static String getArtifactDescription(String artifactName) throws Exception {
+ clickOnArtifact(artifactName);
+ String artifactDesc = GeneralUIUtils.getWebElementWaitForVisible(
+ artifactName + DataTestIdEnum.ArtifactPageEnum.GET_INFORMATIONAL_ARTIFACT_DESCRIPTION.getValue())
+ .getText();
+ clickOnArtifact(artifactName); // close artifact
+ return artifactDesc;
+ }
+
+ public static List<WebElement> getElemenetsFromTable() {
+ return GeneralUIUtils.getWebElementsListByDataTestId("InformationalArtifactRow");
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/OpsOperationPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/OpsOperationPage.java
new file mode 100644
index 0000000000..e7151078f9
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/OpsOperationPage.java
@@ -0,0 +1,183 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Assert;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class OpsOperationPage {
+
+ public OpsOperationPage() {
+ super();
+ }
+
+ public static void distributeService() {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "distributing...");
+ clickOnButton(DataTestIdEnum.DistributionChangeButtons.DISTRIBUTE);
+ GeneralUIUtils.getWebButton(DataTestIdEnum.DistributionChangeButtons.MONITOR.getValue());
+ }
+
+ public static void displayMonitor() {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "clicking on monitor button");
+ // clickOnButton(DataTestIdEnum.DistributionChangeButtons.MONITOR);
+ GeneralUIUtils.moveToStep(StepsEnum.MONITOR);
+ }
+
+ public static void re_distributeService() {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "redistributing...");
+ clickOnButton(DataTestIdEnum.DistributionChangeButtons.RE_DISTRIBUTE);
+ GeneralUIUtils.getWebButton(DataTestIdEnum.DistributionChangeButtons.MONITOR.getValue());
+ }
+
+ private static void clickOnButton(DataTestIdEnum.DistributionChangeButtons button) {
+ GeneralUIUtils.getWebElementWaitForVisible(button.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static List<WebElement> getRowsFromMonitorTable() {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "getting number of rows from distribution table");
+ GeneralPageElements.checkElementsCountInTable(1, () -> GeneralUIUtils.waitForElementsListVisibility("ditributionTable"));
+ List<WebElement> distributionRecords = GeneralUIUtils.waitForElementsListVisibility("ditributionTable");
+ List<WebElement> findElements = distributionRecords.get(0).findElements(By.className("w-sdc-distribute-parent-block"));
+ return findElements;
+
+
+
+ }
+
+ public static void showDistributionStatus(int rowIndex) {
+ GeneralUIUtils.getWebElementWaitForVisible("ShowRecordButton_" + String.valueOf(rowIndex)).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static String getTotalArtifactsSum(int rowIndex) {
+ return GeneralUIUtils.waitFordataTestIdVisibility("totalArtifacts_" + String.valueOf(rowIndex)).getText();
+ }
+
+ public static String getNotifiedArtifactsSum(int rowIndex) {
+ return GeneralUIUtils.waitFordataTestIdVisibility("notified_" + String.valueOf(rowIndex)).getText();
+ }
+
+ public static String getDownloadedArtifactsSum(int rowIndex) {
+ return GeneralUIUtils.waitFordataTestIdVisibility("downloaded_" + String.valueOf(rowIndex)).getText();
+ }
+
+ public static String getDeployedArtifactsSum(int rowIndex) {
+ return GeneralUIUtils.waitFordataTestIdVisibility("deployed_" + String.valueOf(rowIndex)).getText();
+ }
+
+ public static String getNotNotifiedArtifactsSum(int rowIndex) {
+ return GeneralUIUtils.waitFordataTestIdVisibility("NotNotified_" + String.valueOf(rowIndex)).getText();
+ }
+
+ public static String getErrorsSum(int rowIndex) {
+ return GeneralUIUtils.waitFordataTestIdVisibility("errors_" + String.valueOf(rowIndex)).getText();
+ }
+
+ public static void clickRefreshTableButton(int rowIndex) {
+ // SetupCDTest.getExtendTest().log(LogStatus.INFO, "refreshing
+ // distribution table");
+ GeneralUIUtils.getWebElementWaitForVisible("refreshButton").click();
+
+ // wait until total artifacts field disappear
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 30);
+ wait.until(ExpectedConditions.invisibilityOfElementLocated(
+ By.xpath("//*[@data-tests-id='" + "totalArtifacts_" + String.valueOf(rowIndex) + "']")));
+ }
+
+ public static void waitUntilArtifactsDistributed(int rowIndex) throws Exception {
+ waitUntilArtifactsDistributed("0", 0);
+ }
+
+ public static void waitUntilArtifactsDistributed(String expectedArtifactsSum, int rowIndex) throws Exception {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "waiting until all artifacts distributed");
+ boolean isKeepWaiting = true;
+ int maxWaitingPeriodMS = 5 * 60 * 1000;
+ int sumWaitingTime = 0;
+ int napPeriod = 1000;
+ while (isKeepWaiting) {
+ showDistributionStatus(rowIndex);
+ String actualTotalArtifactsSize = getTotalArtifactsSum(rowIndex);
+ String actualNotifiedArtifactsSize = getNotifiedArtifactsSum(rowIndex);
+ String actualDownloadedArtifactsSize = getDownloadedArtifactsSum(rowIndex);
+ String actualDeployedArtifactsSize = getDeployedArtifactsSum(rowIndex);
+ String actualNotNotifedArtifactsSize = getNotNotifiedArtifactsSum(rowIndex);
+ isKeepWaiting = !actualTotalArtifactsSize.equals(actualDownloadedArtifactsSize)
+ || !actualTotalArtifactsSize.equals(actualNotifiedArtifactsSize)
+ || !actualTotalArtifactsSize.equals(actualDeployedArtifactsSize)
+ || actualTotalArtifactsSize.equals("0") || actualDownloadedArtifactsSize.equals("0")
+ || actualNotifiedArtifactsSize.equals("0") || actualDeployedArtifactsSize.equals("0");
+ // isKeepWaiting =
+ // !expectedArtifactsSum.equals(actualTotalArtifactsSize) &&
+ // !expectedArtifactsSum.equals(actualNotifiedArtifactsSize) &&
+ // !expectedArtifactsSum.equals(actualDownloadedArtifactsSize) &&
+ // !expectedArtifactsSum.equals(actualDeployedArtifactsSize) &&
+ // actualNotNotifedArtifactsSize.equals("0");
+ // if (Integer.valueOf(actualTotalArtifactsSize) >
+ // Integer.valueOf(expectedArtifactsSum)){
+ // isKeepWaiting = false;
+ // throw new Exception(String.format("MORE ARTIFACTS THEN EXPECTED -
+ // actual: %s, expected: %s", actualTotalArtifactsSize,
+ // expectedArtifactsSum));
+ //// Assert.fail(String.format("MORE ARTIFACTS THEN EXPECTED -
+ // actual: %s, expected: %s", actualTotalArtifactsSize,
+ // expectedArtifactsSum));
+ // }
+ if (isKeepWaiting) {
+
+ if (Integer.parseInt(actualNotNotifedArtifactsSize) > 1) {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "Some artifacts are not notified...");
+ isKeepWaiting = false;
+ throw new Exception("Some artifacts are not notified... check distribution client");
+ }
+
+ GeneralUIUtils.sleep(napPeriod);
+ sumWaitingTime += napPeriod;
+
+ if (sumWaitingTime > maxWaitingPeriodMS) {
+ SetupCDTest.getExtendTest().log(LogStatus.FAIL, "not all artifacts are displayed");
+ isKeepWaiting = false;
+ throw new Exception(String.format("NOT ALL ARTIFACTS ARE DISPLAYED WITHIN %s SECONDS",
+ String.valueOf(maxWaitingPeriodMS / 1000)));
+ }
+
+ clickRefreshTableButton(rowIndex);
+ }
+ }
+
+ SetupCDTest.getExtendTest().log(LogStatus.PASS, "all artifacts are distributed successfully");
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertiesPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertiesPage.java
new file mode 100644
index 0000000000..614fa0a81a
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertiesPage.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+public class PropertiesPage extends GeneralPageElements {
+
+ public PropertiesPage() {
+ super();
+ }
+
+ public static List<WebElement> getElemenetsFromTable() {
+ return GeneralUIUtils.getWebElementsListByDataTestId(DataTestIdEnum.PropertiesPageEnum.PROPERTY_ROW.getValue());
+ }
+
+ public static void clickAddPropertyArtifact() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.ADD_NEW_PROPERTY.getValue()).click();
+ }
+
+ public static void clickEditPropertyArtifact(String propertyName) {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.EDIT_PROPERTY.getValue() + propertyName).click();
+ }
+
+ public static void clickDeletePropertyArtifact(String propertyName) {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.DELETE_PROPERTY.getValue() + propertyName)
+ .click();
+ }
+
+ public static void clickOnProperty(String propertyName) {
+ GeneralUIUtils.getWebButton(propertyName).click();
+ }
+
+ public static PropertyPopup getPropertyPopup() {
+ return new PropertyPopup();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertyPopup.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertyPopup.java
new file mode 100644
index 0000000000..11cbea3093
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/PropertyPopup.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.WebElement;
+
+public class PropertyPopup {
+
+ public PropertyPopup() {
+ }
+
+ public void insertPropertyName(String name) {
+ WebElement propertyNameField = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.PropertiesPageEnum.PROPERTY_NAME.getValue());
+ propertyNameField.clear();
+ propertyNameField.sendKeys(name);
+ }
+
+ public void insertPropertyDefaultValue(String value) {
+ WebElement propertyValue = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.PropertiesPageEnum.PROPERTY_VALUE.getValue());
+ propertyValue.clear();
+ propertyValue.sendKeys(value);
+ }
+
+ public void insertPropertyDescription(String description) {
+ WebElement propertyDescription = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.PropertiesPageEnum.PROPERTY_DESCRIPTION.getValue());
+ propertyDescription.clear();
+ propertyDescription.sendKeys(description);
+ }
+
+ public void selectPropertyType(String propertyType) {
+ GeneralUIUtils.getSelectList(propertyType, DataTestIdEnum.PropertiesPageEnum.PROPERTY_TYPE.getValue());
+ }
+
+ public void clickAdd() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.ADD.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void clickSave() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.SAVE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void clickCancel() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.CANCEL.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void clickDone() {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.PropertiesPageEnum.DONE.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceGeneralPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceGeneralPage.java
new file mode 100644
index 0000000000..2494282e1c
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceGeneralPage.java
@@ -0,0 +1,154 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.ComponentType;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.Select;
+
+public class ResourceGeneralPage extends GeneralPageElements {
+
+ public ResourceGeneralPage() {
+ super();
+ }
+
+ private static WebElement getNameField() {
+ return GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ResourceMetadataEnum.RESOURCE_NAME.getValue());
+ }
+
+ private static WebElement getDescriptionField() {
+ return GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ServiceMetadataEnum.DESCRIPTION.getValue());
+ }
+
+ private static String getCategoryField() {
+ return DataTestIdEnum.ResourceMetadataEnum.CATEGORY.getValue();
+ }
+
+ private static WebElement getVendorNameField() {
+ return GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ResourceMetadataEnum.VENDOR_NAME.getValue());
+ }
+
+ private static WebElement getVendorReleaseField() {
+ return GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.ResourceMetadataEnum.VENDOR_RELEASE.getValue());
+ }
+
+ private static WebElement getTagsField() {
+ return GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ResourceMetadataEnum.TAGS.getValue());
+ }
+
+ private static WebElement getUserIdField() {
+ return GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ResourceMetadataEnum.CONTACT_ID.getValue());
+ }
+
+ /***************************************************************/
+
+ public static String getNameText() {
+ return getNameField().getAttribute("value");
+ }
+
+ public static void defineName(String resourceName) {
+ WebElement resourceNameTextbox = getNameField();
+ resourceNameTextbox.clear();
+ resourceNameTextbox.sendKeys(resourceName);
+ }
+
+ public static String getDescriptionText() {
+ return getDescriptionField().getAttribute("value");
+ }
+
+ public static void defineDescription(String description) {
+ WebElement descriptionTextbox = getDescriptionField();
+ descriptionTextbox.clear();
+ descriptionTextbox.sendKeys(description);
+ }
+
+ public static String getVendorNameText() {
+ return getVendorNameField().getAttribute("value");
+ }
+
+ public static void defineVendorName(String vendorName) {
+ WebElement vendorNameTextbox = getVendorNameField();
+ vendorNameTextbox.clear();
+ vendorNameTextbox.sendKeys(vendorName);
+ }
+
+ public static String getVendorReleaseText() {
+ return getVendorReleaseField().getAttribute("value");
+ }
+
+ public static void defineVendorRelease(String vendorRelease) {
+ WebElement vendorReleaseTextbox = getVendorReleaseField();
+ vendorReleaseTextbox.clear();
+ vendorReleaseTextbox.sendKeys(vendorRelease);
+ }
+
+ public static void defineTag(String resourceTags) {
+ WebElement tagTextbox = getTagsField();
+ tagTextbox.clear();
+ tagTextbox.sendKeys(resourceTags);
+ tagTextbox.sendKeys(Keys.ENTER);
+ }
+
+ public static void defineTagsList(ComponentReqDetails resource, String[] resourceTags) {
+ List<String> taglist = new ArrayList<String>();
+ ;
+ WebElement resourceTagsTextbox = getTagsField();
+ for (String tag : resourceTags) {
+ resourceTagsTextbox.clear();
+ resourceTagsTextbox.sendKeys(tag);
+ GeneralUIUtils.sleep(1000);
+ resourceTagsTextbox.sendKeys(Keys.ENTER);
+ taglist.add(tag);
+ }
+ resource.setTags(taglist);
+ }
+
+ public static void defineCategory(String category) {
+// GeneralUIUtils.getSelectList(category, getCategoryField());
+
+ Actions action = new Actions(GeneralUIUtils.getDriver());
+ action.click(GeneralUIUtils.getWebElementByDataTestId(getCategoryField()));
+ action.sendKeys(category).perform();
+ }
+
+ public static String getUserIdContactText() {
+ return getUserIdField().getAttribute("value");
+ }
+
+ public static void defineUserIdContact(String userId) {
+ WebElement contactIdTextbox = getUserIdField();
+ contactIdTextbox.clear();
+ contactIdTextbox.sendKeys(userId);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceLeftMenu.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceLeftMenu.java
new file mode 100644
index 0000000000..bd93f18d2a
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ResourceLeftMenu.java
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+
+public class ResourceLeftMenu implements ComponentLeftMenu {
+
+ public void moveToGeneralScreen() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.GENERAL);
+ }
+
+ public void moveToIconScreen() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.ICON);
+ }
+
+ public void moveToDeploymentArtifactScreen() {
+ GeneralUIUtils.moveToStep(StepsEnum.DEPLOYMENT_ARTIFACT);
+ }
+
+ public void moveToInformationalArtifactScreen() {
+ GeneralUIUtils.moveToStep(StepsEnum.INFORMATION_ARTIFACT);
+ }
+
+ public void moveToPropertiesScreen() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.PROPERTIES);
+ }
+
+ public void moveToCompositionScreen() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.COMPOSITION);
+ }
+
+ public void moveToActivityLogScreen() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.ACTIVITY_LOG);
+ }
+
+ public void moveToDeploymentViewScreen() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.DEPLOYMENT_VIEW);
+ }
+
+ public void moveToToscaArtifactsScreen() {
+ GeneralUIUtils.moveToStep(StepsEnum.TOSCA_ARTIFACTS);
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ServiceGeneralPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ServiceGeneralPage.java
new file mode 100644
index 0000000000..9e8c4b2265
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ServiceGeneralPage.java
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.WebElement;
+
+public class ServiceGeneralPage extends ResourceGeneralPage {
+
+ public ServiceGeneralPage() {
+ super();
+ }
+
+ public static void defineName(String serviceName) {
+ WebElement serviceNameTextbox = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.ServiceMetadataEnum.SERVICE_NAME.getValue());
+ serviceNameTextbox.clear();
+ serviceNameTextbox.sendKeys(serviceName);
+ }
+
+ public static void defineProjectCode(String pmat) {
+ WebElement projectCodeTextbox = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.ServiceMetadataEnum.PROJECT_CODE.getValue());
+ projectCodeTextbox.clear();
+ projectCodeTextbox.sendKeys(pmat);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/TesterOperationPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/TesterOperationPage.java
new file mode 100644
index 0000000000..6c18561492
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/TesterOperationPage.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.interactions.Actions;
+import org.testng.annotations.Test;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class TesterOperationPage {
+
+ public TesterOperationPage() {
+ super();
+ }
+
+ public static void certifyComponent(String componentName) throws Exception {
+ clickStartTestingButton();
+ clickAccpetCertificationButton(componentName);
+
+ }
+
+ public static void clickAccpetCertificationButton(String componentName) {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "clicking on accept certification button");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.ACCEPT.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.ACCEP_TESTING_MESSAGE.getValue())
+ .sendKeys(componentName + " tested successfuly");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(2000);
+ GeneralUIUtils.getWebElementWaitForVisible("main-menu-input-search");
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, componentName + " is certifed ");
+ }
+
+ public static void clickStartTestingButton() {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "start testing");
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.START_TESTING.getValue())
+ .click();
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.ACCEPT.getValue());
+ GeneralUIUtils.sleep(1000);
+
+ // bug
+ // Actions actionObject = new Actions(GeneralUIUtils.getDriver());
+ // actionObject.keyDown(Keys.CONTROL).sendKeys(Keys.F5).perform();
+ //
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ToscaArtifactsPage.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ToscaArtifactsPage.java
new file mode 100644
index 0000000000..dcc5f0640a
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/ToscaArtifactsPage.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import java.util.List;
+
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+public class ToscaArtifactsPage extends InformationalArtifactPage {
+
+ public ToscaArtifactsPage() {
+
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/UploadArtifactPopup.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/UploadArtifactPopup.java
new file mode 100644
index 0000000000..5d27809774
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/pages/UploadArtifactPopup.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.pages;
+
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
+
+public class UploadArtifactPopup {
+
+ public UploadArtifactPopup() {
+ super();
+ }
+
+ public void loadFile(String path, String filename) {
+ final WebElement browseWebElement = GeneralUIUtils
+ .getWebElementByDataTestId(DataTestIdEnum.ArtifactPopup.BROWSE.getValue());
+ browseWebElement.sendKeys(path + "\\" + filename);
+ }
+
+ public void insertDescription(String artifactDescriptoin) {
+ WebElement artifactDescriptionTextbox = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.ArtifactPopup.ARTIFACT_DESCRIPTION.getValue());
+ artifactDescriptionTextbox.clear();
+ artifactDescriptionTextbox.sendKeys(artifactDescriptoin);
+ }
+
+ public Select defineArtifactLabel(String requiredArtifactLabel) {
+ Select selectList = GeneralUIUtils.getSelectList("Create New Artifact",
+ DataTestIdEnum.ArtifactPopup.ARTIFACT_LABEL.getValue());
+ WebElement artifactLabelWebElement = GeneralUIUtils.getDriver().findElement(By.name("artifactLabel"));
+ artifactLabelWebElement.clear();
+ artifactLabelWebElement.sendKeys(requiredArtifactLabel);
+ return selectList;
+ }
+
+ public Select selectArtifactType(String artifactType) {
+ return GeneralUIUtils.getSelectList(artifactType, DataTestIdEnum.ArtifactPopup.ARTIFACT_TYPE.getValue());
+ }
+
+ public void clickAddButton() throws Exception {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPopup.ADD_BUTTON.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void clickCancelButton() throws Exception {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPopup.CANCEL_BUTTON.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public void clickUpdateButton() throws Exception {
+ GeneralUIUtils.getWebButton(DataTestIdEnum.ArtifactPopup.UPDATE_BUTTON.getValue()).click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdditionalConditions.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdditionalConditions.java
new file mode 100644
index 0000000000..1b7f4c3641
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdditionalConditions.java
@@ -0,0 +1,84 @@
+package org.openecomp.sdc.ci.tests.utilities;
+
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+public class AdditionalConditions {
+
+ public static ExpectedCondition<Boolean> jQueryAJAXCallsHaveCompleted() {
+ return new ExpectedCondition<Boolean>() {
+ @Override
+ public Boolean apply(WebDriver driver) {
+ return (Boolean) ((JavascriptExecutor)driver).
+ executeScript("return (window.jQuery!= null) && (jQuery.active === 0);");
+ }
+ };
+ }
+
+ public static ExpectedCondition <Boolean> angularHasFinishedProcessing() {
+ return new ExpectedCondition<Boolean>() {
+ @Override
+ public Boolean apply(WebDriver driver) {
+ String scriptJS = "return (window.angular !==undefined) &&"
+ + " (angular.element(document).injector() !==undefined) &&"
+ + " (angular.element(document).injector().get('$http').pendingRequests.length === 0)";
+ return Boolean.valueOf(( (JavascriptExecutor) driver).executeScript(scriptJS).toString());
+ }
+ };
+ }
+
+ public static ExpectedCondition <Boolean> pageLoadWait() {
+ return new ExpectedCondition<Boolean>() {
+ @Override
+ public Boolean apply(WebDriver driver) {
+ String scriptJS =
+ "try {\r\n" +
+ " if (document.readyState !== 'complete') {\r\n" +
+ " return false; // Page not loaded yet\r\n" +
+ " }\r\n" +
+ " if (window.jQuery) {\r\n" +
+ " if (window.jQuery.active) {\r\n" +
+ " return false;\r\n" +
+ " } else if (window.jQuery.ajax && window.jQuery.ajax.active) {\r\n" +
+ " return false;\r\n" +
+ " }\r\n" +
+ " }\r\n" +
+ " if (window.angular) {\r\n" +
+ " if (!window.qa) {\r\n" +
+ " // Used to track the render cycle finish after loading is complete\r\n" +
+ " window.qa = {\r\n" +
+ " doneRendering: false\r\n" +
+ " };\r\n" +
+ " }\r\n" +
+ " // Get the angular injector for this app (change element if necessary)\r\n" +
+ " var injector = window.angular.element('body').injector();\r\n" +
+ " // Store providers to use for these checks\r\n" +
+ " var $rootScope = injector.get('$rootScope');\r\n" +
+ " var $http = injector.get('$http');\r\n" +
+ " var $timeout = injector.get('$timeout');\r\n" +
+ " // Check if digest\r\n" +
+ " if ($rootScope.$$phase === '$apply' || $rootScope.$$phase === '$digest' || $http.pendingRequests.length !== 0) {\r\n" +
+ " window.qa.doneRendering = false;\r\n" +
+ " return false; // Angular digesting or loading data\r\n" +
+ " }\r\n" +
+ " if (!window.qa.doneRendering) {\r\n" +
+ " // Set timeout to mark angular rendering as finished\r\n" +
+ " $timeout(function() {\r\n" +
+ " window.qa.doneRendering = true;\r\n" +
+ " }, 0);\r\n" +
+ " return false;\r\n" +
+ " }\r\n" +
+ " }\r\n" +
+ " return true;\r\n" +
+ "} catch (ex) {\r\n" +
+ " return false;\r\n" +
+ "}";
+ return Boolean.valueOf(( (JavascriptExecutor) driver).executeScript(scriptJS).toString());
+ }
+ };
+ }
+
+
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdminWorkspaceUIUtilies.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdminWorkspaceUIUtilies.java
new file mode 100644
index 0000000000..472b69fd0b
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AdminWorkspaceUIUtilies.java
@@ -0,0 +1,71 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import org.junit.rules.TestName;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.Select;
+
+public class AdminWorkspaceUIUtilies {
+
+ public AdminWorkspaceUIUtilies(TestName name, String className) {
+ super();
+ // TODO Auto-generated constructor stub
+ }
+
+ protected static WebDriver driver;
+
+ public static void deleteuser(String userId) throws Exception {
+ typeToSearchBox(userId);
+ if (GeneralUIUtils.getWebElements("tdRow") != null) {
+ GeneralUIUtils.getWebElementWaitForVisible("delete" + userId + "").click();
+ GeneralUIUtils.clickOkButton();
+ }
+ }
+
+ public static String defineNewUserId(String userId) {
+ GeneralUIUtils.getWebElementWaitForVisible("newUserId").clear();
+ GeneralUIUtils.getWebElementWaitForVisible("newUserId").sendKeys(userId);
+ ;
+ return userId;
+ }
+
+ public static String defineNewMacUid(String MacUid) {
+ GeneralUIUtils.getWebElementWaitForVisible("newUserId").clear();
+ GeneralUIUtils.getWebElementWaitForVisible("newUserId").sendKeys(MacUid);
+ ;
+ return MacUid;
+ }
+
+ public static String selectUserRole(String Role) {
+ Select selectrole = new Select(GeneralUIUtils.getWebElementWaitForVisible("selectrole"));
+ selectrole.deselectByVisibleText(Role);
+ selectrole.selectByVisibleText(Role);
+ return Role;
+ }
+
+ public static void typeToSearchBox(String Text) throws Exception {
+ GeneralUIUtils.getWebElementWaitForVisible("searchbox").clear();
+ GeneralUIUtils.getWebElementWaitForVisible("searchbox").sendKeys(Text);
+ Thread.sleep(1000);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ArtifactUIUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ArtifactUIUtils.java
new file mode 100644
index 0000000000..f22d52d3bd
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ArtifactUIUtils.java
@@ -0,0 +1,302 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import static org.junit.Assert.assertEquals;
+
+import java.awt.AWTException;
+import java.awt.Robot;
+import java.awt.Toolkit;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.KeyEvent;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactInfo;
+import org.openecomp.sdc.ci.tests.datatypes.ArtifactReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.InformationalArtifacts;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.pages.DeploymentArtifactPage;
+import org.openecomp.sdc.ci.tests.pages.InformationalArtifactPage;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.thinkaurelius.titan.diskstorage.util.StaticArrayBuffer;
+
+public final class ArtifactUIUtils {
+
+ private ArtifactUIUtils() {
+ }
+
+ public static void fillAndAddNewArtifactParameters(ArtifactInfo artifactInfo) throws Exception {
+ DeploymentArtifactPage.artifactPopup().loadFile(artifactInfo.getFilepath(), artifactInfo.getFilename());
+ DeploymentArtifactPage.artifactPopup().insertDescription(artifactInfo.getDescription());
+ DeploymentArtifactPage.artifactPopup().defineArtifactLabel(artifactInfo.getArtifactLabel());
+ DeploymentArtifactPage.artifactPopup().selectArtifactType(artifactInfo.getArtifactType());
+ DeploymentArtifactPage.artifactPopup().clickAddButton();
+ }
+
+ public static void fillPlaceHolderInformationalArtifact(DataTestIdEnum.InformationalArtifacts artifactLabel,
+ String filepath, String filename, String description) throws Exception {
+ GeneralUIUtils.getWebButton(artifactLabel.getValue()).click();
+ InformationalArtifactPage.artifactPopup().loadFile(filepath, filename);
+ InformationalArtifactPage.artifactPopup().insertDescription(description);
+ InformationalArtifactPage.artifactPopup().clickAddButton();
+ }
+
+ public static RestResponse deploymentArtifactResourceInUI(ResourceReqDetails resource, User user,
+ ArtifactReqDetails artifact, String file) throws Exception {
+ Thread.sleep(1000);
+
+ List<WebElement> listFormInput = GeneralUIUtils.getDriver()
+ .findElements(By.className("i-sdc-designer-sidebar-tab"));
+ WebElement addArtifactElement = listFormInput.get(2);
+ addArtifactElement.click();
+
+ WebElement addArtifact = GeneralUIUtils.getDriver()
+ .findElement(By.className("i-sdc-designer-sidebar-section-content-item-artifact-details-name"));
+ addArtifact.click();
+
+ Thread.sleep(1000);
+ WebElement descriptionProperty = GeneralUIUtils.getDriver().findElement(By.className("i-sdc-form-textarea"));
+ descriptionProperty.clear();
+ descriptionProperty.sendKeys(artifact.getDescription());
+
+ WebElement uploadFile = GeneralUIUtils.getDriver().findElement(By.className("i-sdc-form-label-upload"));
+ uploadFile.click();
+
+ StringSelection sel = new StringSelection(file);
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(sel, null);
+ // System.out.println("selection" + sel);
+ Thread.sleep(1000);
+
+ Robot robot = new Robot();
+ Thread.sleep(1000);
+
+ Thread.sleep(2000);
+
+ robot.keyPress(KeyEvent.VK_ENTER);
+
+ // Release Enter
+ robot.keyRelease(KeyEvent.VK_ENTER);
+
+ // Press CTRL+V
+ robot.keyPress(KeyEvent.VK_CONTROL);
+ robot.keyPress(KeyEvent.VK_V);
+
+ // Release CTRL+V
+ robot.keyRelease(KeyEvent.VK_CONTROL);
+ robot.keyRelease(KeyEvent.VK_V);
+ Thread.sleep(1000);
+
+ // Press Enter
+ robot.keyPress(KeyEvent.VK_ENTER);
+ robot.keyRelease(KeyEvent.VK_ENTER);
+ Thread.sleep(3000);
+
+ WebElement clickDone = GeneralUIUtils.getDriver().findElement(By.className("w-sdc-form-action"));
+ clickDone.click();
+
+ Thread.sleep(3500);
+
+ GeneralUIUtils.getDriver().findElement(By.cssSelector("button[data-ng-click^=save]")).click();
+
+ RestResponse getResource = RestCDUtils.getResource(resource, user);
+ assertEquals("Did not succeed to get resource after create", 200, getResource.getErrorCode().intValue());
+ return getResource;
+ }
+
+ public static void addInformationArtifact(ArtifactReqDetails artifact, String filePath,
+ final InformationalArtifacts dataTestEnum) throws Exception {
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.sleep(2000);
+ GeneralUIUtils.getWebElementWaitForVisible(dataTestEnum.getValue()).click();
+
+ final WebElement browseWebElement = GeneralUIUtils.retryMethodOnException(
+ () -> GeneralUIUtils.getWebElementByDataTestId(DataTestIdEnum.ModalItems.BROWSE_BUTTON.getValue()));
+ browseWebElement.sendKeys(filePath);
+
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.DESCRIPTION.getValue())
+ .sendKeys(artifact.getDescription());
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.ADD.getValue()).click();
+
+ }
+
+ private static void addFileToWindowBrowse(String file) throws InterruptedException, AWTException {
+ StringSelection sel = new StringSelection(file);
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(sel, null);
+ // System.out.println("selection" + sel);
+ Thread.sleep(1000);
+
+ Robot robot = new Robot();
+
+ robot.keyPress(KeyEvent.VK_ENTER);
+
+ // Release Enter
+ robot.keyRelease(KeyEvent.VK_ENTER);
+
+ // Press CTRL+V
+ robot.keyPress(KeyEvent.VK_CONTROL);
+ robot.keyPress(KeyEvent.VK_V);
+
+ // Release CTRL+V
+ robot.keyRelease(KeyEvent.VK_CONTROL);
+ robot.keyRelease(KeyEvent.VK_V);
+ Thread.sleep(1000);
+
+ // Press Enter
+ robot.keyPress(KeyEvent.VK_ENTER);
+ robot.keyRelease(KeyEvent.VK_ENTER);
+ Thread.sleep(3000);
+ }
+
+ static WebElement ArtifactLabel;
+
+ public static Map<String, String> addInformationalArtifact(String artifactLabel) throws Exception {
+ String type = GeneralUIUtils.getSelectList(null, "artifacttype").getFirstSelectedOption().getText();
+ Map<String, String> artifactValues = new HashMap<String, String>();
+ String labelName = GeneralUIUtils.getSelectList(artifactLabel, "selectArtifact").getFirstSelectedOption()
+ .getText();
+ ArtifactLabel = GeneralUIUtils.getDriver().findElement(By.name("artifactLabel"));
+ if (ArtifactLabel.getAttribute("value").equals("")) {
+ labelName = "New-Test-Artifact";
+ ArtifactLabel.sendKeys(labelName);
+ type = GeneralUIUtils.getSelectList("HEAT", "artifacttype").getFirstSelectedOption().getText();
+ }
+ String description = "This is Description";
+ String fileName = "Heat-File.yaml";
+ GeneralUIUtils.defineDescription(description);
+ ResourceUIUtils.importFileWithSendKeyBrowse(GeneralUIUtils.FILE_PATH, fileName);
+ GeneralUIUtils.getWebButton("Add").click();
+ GeneralUIUtils.waitFordataTestIdVisibility(labelName);
+
+ artifactValues.put("type", type);
+ artifactValues.put("description", description);
+ artifactValues.put("name", labelName);
+ artifactValues.put("fileName", fileName);
+ return artifactValues;
+ }
+
+ public static Map<String, String> addDeploymentArtifact(String artifactLabel, String artifactType, String fileName)
+ throws Exception {
+ String type = null;
+ String labelName;
+ Map<String, String> artifactValues = new HashMap<String, String>();
+ try {
+ labelName = GeneralUIUtils.getSelectList(artifactLabel, "selectArtifact").getOptions().get(1).getText();
+ GeneralUIUtils.getSelectList(artifactLabel, "selectArtifact").selectByVisibleText(labelName);
+ } catch (Exception e) {
+ labelName = GeneralUIUtils.getWebElementByName(artifactLabel).getText();
+ }
+ ArtifactLabel = GeneralUIUtils.getDriver().findElement(By.name("artifactLabel"));
+ if (ArtifactLabel.getText().equals("")) {
+ labelName = "New-Test-Artifact";
+ ArtifactLabel.sendKeys(labelName);
+ type = GeneralUIUtils.getSelectList(artifactType, "artifacttype").getFirstSelectedOption().getText();
+ }
+ String description = "This is Description";
+ GeneralUIUtils.defineDescription(description);
+ ResourceUIUtils.importFileWithSendKeyBrowse(GeneralUIUtils.FILE_PATH, fileName);
+ try {
+ GeneralUIUtils.getWebButton("Add").click();
+ } catch (Exception e) {
+ GeneralUIUtils.getButtonByClassName("w-sdc-form-action add-property").click();
+ }
+
+ artifactValues.put("type", artifactType);
+ artifactValues.put("description", description);
+ artifactValues.put("name", labelName);
+ artifactValues.put("fileName", fileName);
+ return artifactValues;
+ }
+
+ public static Map<String, String> addDeploymentArtifactFromCanvas(String artifactLabel) throws Exception {
+ String type = null;
+ Map<String, String> artifactValues = new HashMap<String, String>();
+ String labelName = GeneralUIUtils.getSelectList(artifactLabel, "selectArtifact").getFirstSelectedOption()
+ .getText();
+ ArtifactLabel = GeneralUIUtils.getDriver().findElement(By.name("artifactLabel"));
+ if (ArtifactLabel.getText().equals("")) {
+ labelName = "New-Test-Artifact";
+ ArtifactLabel.sendKeys(labelName);
+ type = GeneralUIUtils.getSelectList("OTHER", "artifacttype").getFirstSelectedOption().getText();
+ }
+ String description = "This is Description";
+ String filePath = "C:\\Git_work\\ASDC\\d2-sdnc\\ui-ci\\src\\main\\resources\\Files\\";
+ String fileName = "Heat-File.yaml";
+ GeneralUIUtils.defineDescription(description);
+ ResourceUIUtils.importFileWithSendKeyBrowse(filePath, fileName);
+ GeneralUIUtils.getWebButton("Add").click();
+ artifactValues.put("type", type);
+ artifactValues.put("description", description);
+ artifactValues.put("name", labelName);
+ artifactValues.put("fileName", fileName);
+ return artifactValues;
+ }
+
+ public static Map<String, String> valideArtifact(Map<String, String> artifactValues, Boolean condition)
+ throws Exception {
+ if (condition) {
+ GeneralUIUtils.getEelementBycontainsClassName("table-edit-btn").click();
+ } else {
+ System.out.println(artifactValues.get("name"));
+ GeneralUIUtils.getWebElementWaitForVisible("edit_" + artifactValues.get("name")).click();
+ }
+ Thread.sleep(1000);
+ String labelname = GeneralUIUtils.getWebElementByName("artifactLabel").getAttribute("value");
+ String filename = GeneralUIUtils.getWebElementWaitForVisible("filename").getText();
+ String description = GeneralUIUtils.getWebElementWaitForVisible("description").getAttribute("value");
+ String type = GeneralUIUtils.getSelectList(null, "artifacttype").getFirstSelectedOption().getText();
+ labelname.compareToIgnoreCase(artifactValues.get("name").replaceAll("-", ""));
+ assertEquals(filename, artifactValues.get("fileName").replaceAll(" ", "-"));
+ assertEquals(type, artifactValues.get("type"));
+ assertEquals(description, artifactValues.get("description"));
+ GeneralUIUtils.getWebButton("Update").click();
+ return artifactValues;
+ }
+
+ public static void valideArtifactFromCanvas(Map<String, String> artifactValues) throws Exception {
+ GeneralUIUtils.getWebElementWaitForVisible("artifactDisplayName-" + artifactValues.get("name")).click();
+ Thread.sleep(1000);
+ String labelname = GeneralUIUtils.getWebElementByName("artifactLabel").getAttribute("value");
+ String filename = GeneralUIUtils.getWebElementWaitForVisible("filename").getText();
+ String description = GeneralUIUtils.getWebElementWaitForVisible("description").getAttribute("value");
+ String type = GeneralUIUtils.getSelectList(null, "artifacttype").getFirstSelectedOption().getText();
+ labelname.compareToIgnoreCase(artifactValues.get("name").replaceAll("-", ""));
+ assertEquals(filename, artifactValues.get("fileName"));
+ assertEquals(type, artifactValues.get("type"));
+ assertEquals(description, artifactValues.get("description"));
+ }
+
+ public static Map<String, Map<String, Object>> getArtifactsListFromResponse(String jsonResponse,
+ String fieldOfArtifactList) {
+ JSONObject object = (JSONObject) JSONValue.parse(jsonResponse);
+ Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) object.get(fieldOfArtifactList);
+ return map;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AuditCDUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AuditCDUtils.java
new file mode 100644
index 0000000000..c53fef596a
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/AuditCDUtils.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import org.codehaus.jettison.json.JSONObject;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.expected.ExpectedResourceAuditJavaObject;
+import org.openecomp.sdc.ci.tests.utils.general.Convertor;
+import org.openecomp.sdc.ci.tests.utils.validation.AuditValidationUtils;
+
+public class AuditCDUtils {
+
+ public static void validateResourceSuccessAudit(ResourceReqDetails resource, User user, String action)
+ throws Exception {
+ JSONObject auditBody = AuditValidationUtils.filterAuditByUuid(action, resource.getUUID());
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = Convertor
+ .constructFieldsForAuditValidation(resource, resource.getVersion(), user);
+ String auditAction = "Create";
+ expectedResourceAuditJavaObject.setAction(auditAction);
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, auditAction, auditBody.toString(), false);
+ }
+
+ public static void validateServiceSuccessAudit(ServiceReqDetails service, User user, String action)
+ throws Exception {
+ validateServiceSuccessAudit(service, user, action, LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT);
+ }
+
+ public static void validateServiceSuccessAudit(ServiceReqDetails service, User user, String action,
+ LifecycleStateEnum lifecycleStatus) throws Exception {
+ ExpectedResourceAuditJavaObject expectedResourceAuditJavaObject = AuditValidationUtils
+ .constructFieldsForAuditValidation(service, service.getVersion(), user);
+ String body = AuditValidationUtils.filterAuditByUuid(action, service.getUUID()).toString();
+ expectedResourceAuditJavaObject.setAction(action);
+ expectedResourceAuditJavaObject.setPrevState("");
+ expectedResourceAuditJavaObject.setPrevVersion("");
+ expectedResourceAuditJavaObject.setCurrState(lifecycleStatus.toString());
+ expectedResourceAuditJavaObject.setStatus("201");
+ expectedResourceAuditJavaObject.setDesc("OK");
+ AuditValidationUtils.validateAudit(expectedResourceAuditJavaObject, action, body, false);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasElement.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasElement.java
new file mode 100644
index 0000000000..fbe8c04036
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasElement.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openqa.selenium.WebElement;
+
+public final class CanvasElement {
+ private final String uniqueId;
+ private ImmutablePair<Integer, Integer> location;
+ private WebElement elementType;
+
+ CanvasElement(String name, ImmutablePair<Integer, Integer> location, WebElement canvasItem) {
+ super();
+ this.uniqueId = name;
+ this.location = location;
+ elementType = canvasItem;
+ }
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public ImmutablePair<Integer, Integer> getLocation() {
+ return location;
+ }
+
+ public void setLocation(ImmutablePair<Integer, Integer> location) {
+ this.location = location;
+ }
+
+ public WebElement getElementType() {
+ return elementType;
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasManager.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasManager.java
new file mode 100644
index 0000000000..a52367a454
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CanvasManager.java
@@ -0,0 +1,181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.LeftPanelCanvasItems;
+import org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+public final class CanvasManager {
+ private Map<String, CanvasElement> canvasElements;
+ private Actions actions;
+ private WebElement canvas;
+ private int reduceCanvasWidthFactor;
+ // Offsets Are used to find upper right corner of canvas element in order to
+ // connect links
+ private static final int CANVAS_ELEMENT_Y_OFFSET = 40;
+ private static final int CANVAS_ELEMENT_X_OFFSET = 21; // 14 - 27
+
+ private CanvasManager() {
+ canvasElements = new HashMap<>();
+ actions = new Actions(GeneralUIUtils.getDriver());
+ canvas = GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.CANVAS.getValue());
+ try {
+ WebElement webElement = GeneralUIUtils
+ .getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.CANVAS_RIGHT_PANEL.getValue());
+ reduceCanvasWidthFactor = webElement.getSize().width;
+ } catch (Exception e) {
+ reduceCanvasWidthFactor = 0;
+ }
+ }
+
+ public static CanvasManager getCanvasManager() {
+ return new CanvasManager();
+ }
+
+ public List<CanvasElement> getCanvasElements() {
+ return canvasElements.values().stream().collect(Collectors.toList());
+ }
+
+ private void addCanvasElement(CanvasElement element) {
+ canvasElements.put(element.getUniqueId(), element);
+ }
+
+ private void moveElementOnCanvas(CanvasElement canvasElement, ImmutablePair<Integer, Integer> newLocation)
+ throws Exception {
+ GeneralUIUtils.waitForLoader();
+ Thread.sleep(500);
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, newLocation.left, newLocation.right);
+ actions.release();
+ actions.perform();
+ canvasElement.setLocation(newLocation);
+ GeneralUIUtils.waitForLoader();
+
+ }
+
+ public void moveElementOnCanvas(CanvasElement canvasElement) throws Exception {
+ moveElementOnCanvas(canvasElement, getFreePosition());
+ }
+
+ public void deleteElementFromCanvas(CanvasElement canvasElement) throws Exception {
+ GeneralUIUtils.waitForLoader();
+ actions.moveToElement(canvas, canvasElement.getLocation().left, canvasElement.getLocation().right);
+ actions.click();
+ actions.perform();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.GeneralCanvasItems.DELETE_INSTANCE_BUTTON.getValue())
+ .click();
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.ModalItems.OK.getValue()).click();
+ canvasElements.remove(canvasElement.getUniqueId());
+ GeneralUIUtils.waitForLoader();
+ }
+
+ private String getItemName(WebElement canvasItem) {
+ String canvasItemName = canvasItem.getAttribute("data-tests-id");
+
+ return canvasItemName.substring(canvasItemName.lastIndexOf("-"));
+ }
+
+ public CanvasElement createElementOnCanvas(WebElement canvasItem) throws Exception {
+ GeneralUIUtils.waitForLoader();
+ ImmutablePair<Integer, Integer> freePosition = getFreePosition();
+ actions.moveToElement(canvasItem, 0, 0);
+ actions.clickAndHold();
+ actions.moveToElement(canvas, freePosition.left, freePosition.right);
+ actions.release();
+ actions.perform();
+
+ String uniqueId = getItemName(canvasItem) + "_" + UUID.randomUUID().toString();
+ CanvasElement canvasElement = new CanvasElement(uniqueId, freePosition, canvasItem);
+ addCanvasElement(canvasElement);
+ GeneralUIUtils.waitForLoader();
+ return canvasElement;
+ }
+
+ private ImmutablePair<Integer, Integer> getFreePosition() {
+ // TODO mshitrit use better method
+ ImmutablePair<Integer, Integer> randomPosition = null;
+ boolean freePosition = false;
+ int minSpace = 150;
+ while (!freePosition) {
+ ImmutablePair<Integer, Integer> tempRandomPosition = getRandomPosition();
+ freePosition = !canvasElements.values().stream().map(e -> e.getLocation())
+ .filter(e -> Math.abs(e.left - tempRandomPosition.left) < minSpace
+ && Math.abs(e.right - tempRandomPosition.right) < minSpace)
+ .findAny().isPresent();
+ randomPosition = tempRandomPosition;
+ }
+ return randomPosition;
+ }
+
+ private ImmutablePair<Integer, Integer> getRandomPosition() {
+ int edgeBuffer = 50;
+ Random random = new Random();
+ int xElement = random.nextInt(canvas.getSize().width - 2 * edgeBuffer - reduceCanvasWidthFactor) + edgeBuffer;
+ int yElement = random.nextInt(canvas.getSize().height - 2 * edgeBuffer) + edgeBuffer;
+ return new ImmutablePair<Integer, Integer>(xElement, yElement);
+ }
+
+ public void linkElements(CanvasElement firstElement, CanvasElement secondElement) throws Exception {
+ GeneralUIUtils.waitForLoader();
+ drawSimpleLink(firstElement, secondElement);
+
+ selectReqAndCapAndConnect();
+
+ GeneralUIUtils.waitForLoader();
+
+ }
+
+ private void selectReqAndCapAndConnect() {
+ // Select First Cap
+ GeneralUIUtils.getWebElements(DataTestIdEnum.LinkMenuItems.LINK_ITEM_CAP.getValue()).get(0).click();
+ // Select First Req
+ GeneralUIUtils.getWebElements(DataTestIdEnum.LinkMenuItems.LINK_ITEM_REQ.getValue()).get(0).click();
+ // Connect
+ GeneralUIUtils.getWebElementWaitForVisible(DataTestIdEnum.LinkMenuItems.CONNECT_BUTTON.getValue()).click();
+ }
+
+ private void drawSimpleLink(CanvasElement firstElement, CanvasElement secondElement) {
+
+ int yOffset = CANVAS_ELEMENT_Y_OFFSET;
+ int xOffset = CANVAS_ELEMENT_X_OFFSET;
+
+ actions.moveToElement(canvas, firstElement.getLocation().left + xOffset,
+ firstElement.getLocation().right - yOffset);
+
+ actions.clickAndHold();
+ actions.moveToElement(canvas, secondElement.getLocation().left + xOffset,
+ secondElement.getLocation().right - yOffset);
+ actions.release();
+ actions.perform();
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CaptureFailedTests.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CaptureFailedTests.java
new file mode 100644
index 0000000000..e2b2f8800b
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CaptureFailedTests.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+import org.openqa.selenium.OutputType;
+import org.openqa.selenium.TakesScreenshot;
+import org.testng.ITestResult;
+import org.testng.Reporter;
+import org.testng.TestListenerAdapter;
+
+public class CaptureFailedTests extends TestListenerAdapter {
+
+ @Override
+ public void onTestFailure(ITestResult tr) {
+
+ String testName = tr.getName();
+ String parameter = tr.getParameters().length == 1 ? tr.getParameters()[0].toString() : "";
+ File folder = new File(String.format("test-output\\failure_screenshots\\%s_%s.png", testName, parameter));
+
+ File scrFile = ((TakesScreenshot) GeneralUIUtils.getDriver()).getScreenshotAs(OutputType.FILE);
+
+ try {
+ FileUtils.copyFile(scrFile, folder);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Reporter.setEscapeHtml(false);
+ String screenPath = String.format("<img src=%s />", folder.getAbsolutePath());
+ Reporter.log(screenPath);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CatalogUIUtilitis.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CatalogUIUtilitis.java
new file mode 100644
index 0000000000..d204645566
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/CatalogUIUtilitis.java
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.json.Json;
+
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.CatalogRestUtils;
+import org.testng.annotations.Test;
+
+public class CatalogUIUtilitis {
+
+ // Get all Categories , Subcategories and Icons.
+ public void getAllCategoriesAndSubcategories() throws IOException, JSONException {
+ RestResponse allcategoriesJson = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+ JSONArray categories = new JSONArray(allcategoriesJson.getResponse());
+ for (int i = 0; i < categories.length(); i++) {
+ String categoryname = (String) categories.getJSONObject(i).get("name");
+ JSONArray subcategories = (JSONArray) categories.getJSONObject(i).get("subcategories");
+ for (int j = 0; j < subcategories.length(); j++) {
+ String subcategoryname = (String) subcategories.getJSONObject(j).get("name");
+ System.out.println(subcategoryname);
+ }
+ for (int j = 0; j < subcategories.length(); j++) {
+ JSONArray icons = (JSONArray) subcategories.getJSONObject(j).get("icons");
+ for (int k = 0; k < icons.length(); k++) {
+ System.out.println(icons.get(k));
+ }
+ }
+ System.out.println("-------------------------------");
+ }
+ }
+
+ @Test
+ // FOr testing---delete.
+ public static List<String> abcd() throws IOException, JSONException {
+ RestResponse allcategoriesJson = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+ JSONArray categories = new JSONArray(allcategoriesJson.getResponse());
+ List<String> allcat = new ArrayList<>();
+ String uniqueId = null;
+ for (int i = 0; i < categories.length(); i++) {
+ String categoryname = (String) categories.getJSONObject(i).get("name");
+ uniqueId = (String) categories.getJSONObject(i).get("uniqueId");
+ allcat.add(uniqueId);
+ JSONArray subcategories = (JSONArray) categories.getJSONObject(i).get("subcategories");
+ for (int j = 0; j < subcategories.length(); j++) {
+ String subcategoryname = (String) subcategories.getJSONObject(j).get("name");
+ uniqueId = (String) subcategories.getJSONObject(j).get("uniqueId");
+ allcat.add(uniqueId);
+ }
+ }
+ return allcat;
+
+ }
+
+ // Get all Categories uniqueID .//The parent categories.
+ public static List<String> getCategories() throws IOException, JSONException {
+ List<String> allCategoriesList = new ArrayList<>();
+ RestResponse allcategoriesJson = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+ JSONArray categories = new JSONArray(allcategoriesJson.getResponse());
+ for (int i = 0; i < categories.length(); i++) {
+ String categoryname = (String) categories.getJSONObject(i).get("name");
+ System.out.println(categoryname);
+ allCategoriesList.add(categoryname);
+ }
+ return allCategoriesList;
+ }
+
+ @Test
+ // Get Subcategories by Category name
+ public static List<String> getAllSubcategoriesByUniqueId(String uniqueId) throws IOException, JSONException {
+
+ RestResponse allcategoriesJson = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+ JSONArray categories = new JSONArray(allcategoriesJson.getResponse());
+ List<String> subCategories = new ArrayList<>();// subCategories to
+ // return.
+ JSONArray subcategories = null;
+
+ for (int i = 0; i < categories.length(); i++) {
+
+ String categoryuniqueId = (String) categories.getJSONObject(i).get("uniqueId");
+
+ if (categoryuniqueId.contentEquals(uniqueId)) {
+ subcategories = (JSONArray) categories.getJSONObject(i).get("subcategories");
+
+ for (int j = 0; j < subcategories.length(); j++) {
+
+ subCategories.add((String) subcategories.getJSONObject(j).get("uniqueId"));
+ }
+
+ break;
+ }
+ }
+ if (subcategories == null) {
+ subCategories.add(uniqueId);
+ }
+ return subCategories;
+ }
+
+ @Test
+ // Get icons by category name
+ public void getSubCategoryIcons() throws IOException, JSONException {
+ RestResponse allcategoriesJson = CatalogRestUtils.getAllCategoriesTowardsCatalogBe();
+
+ JSONArray categories = new JSONArray(allcategoriesJson.getResponse());
+ for (int i = 0; i < categories.length(); i++) {
+ String subcategoryname = (String) categories.getJSONObject(i).get("name");
+ if (subcategoryname.contentEquals("Generic")) {
+ JSONArray subcategories = (JSONArray) categories.getJSONObject(i).get("subcategories");
+ for (int j = 0; j < subcategories.length(); j++) {
+ JSONArray icons = (JSONArray) subcategories.getJSONObject(j).get("icons");
+ for (int k = 0; k < icons.length(); k++) {
+ System.out.println(icons.get(k));
+ }
+ }
+ break;
+ }
+ }
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/FileHandling.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/FileHandling.java
new file mode 100644
index 0000000000..9533b6485e
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/FileHandling.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class FileHandling {
+
+ public static Map<?, ?> parseYamlFile(String filePath) throws Exception {
+ Yaml yaml = new Yaml();
+ File file = new File(filePath);
+ InputStream inputStream = new FileInputStream(file);
+ Map<?, ?> map = (Map<?, ?>) yaml.load(inputStream);
+ return map;
+ }
+
+ public static String getBasePath() {
+ return System.getProperty("user.dir");
+ }
+
+ public static String getResourcesFilesPath() {
+ return getBasePath() + File.separator + "src" + File.separator + "main" + File.separator + "resources"
+ + File.separator + "Files" + File.separator;
+ }
+
+ public static String getCiFilesPath() {
+ return getBasePath() + File.separator + "src" + File.separator + "main" + File.separator + "resources"
+ + File.separator + "ci";
+ }
+
+ public static String getConfFilesPath() {
+ return getCiFilesPath() + File.separator + "conf" + File.separator;
+ }
+
+ public static String getTestSuitesFilesPath() {
+ return getCiFilesPath() + File.separator + "testSuites" + File.separator;
+ }
+
+ public static Object[] getFileNamesFromFolder(String filepath, String extension) {
+ try {
+ File dir = new File(filepath);
+ List<String> filenames = new ArrayList<String>();
+ if (dir.isDirectory()) {
+ for (File file : dir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(extension);
+ }
+ })) {
+
+ filenames.add(file.getName());
+ }
+ return filenames.toArray();
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/GeneralUIUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/GeneralUIUtils.java
new file mode 100644
index 0000000000..4fd36955ae
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/GeneralUIUtils.java
@@ -0,0 +1,1081 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import java.awt.AWTException;
+import java.awt.Robot;
+import java.awt.Toolkit;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.time.StopWatch;
+import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.BreadCrumbsButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CatalogFilterTitlesEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CreateAndImportButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.MenuOptionsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.TypesEnum;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.OutputType;
+import org.openqa.selenium.Platform;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.firefox.FirefoxDriver;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.remote.DesiredCapabilities;
+import org.openqa.selenium.remote.RemoteWebDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.Select;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.Assert;
+import org.testng.Reporter;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public final class GeneralUIUtils {
+
+ private static int timeOut=60*3;
+
+ public static final String FILE_NAME = "Valid_tosca_Mycompute.yml";
+
+ /**************** DRIVERS ****************/
+ private static WebDriver driver;
+
+ public static void findComponentAndClick(String componentName) throws Exception {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "finding component " + componentName);
+ waitFordataTestIdVisibility("main-menu-input-search").sendKeys(componentName);
+ WebElement foundComp = null;
+ try {
+ foundComp = waitFordataTestIdVisibility(componentName);
+ foundComp.click();
+ GeneralUIUtils.waitForLoader();
+ waitFordataTestIdVisibility("formlifecyclestate");
+ } catch (Exception e) {
+ String msg = String.format("DID NOT FIND A COMPONENT NAMED %s", componentName);
+ SetupCDTest.getExtendTest().log(LogStatus.FAIL, msg);
+ System.out.println(msg);
+ Assert.fail(msg);
+ }
+ }
+
+ public static List<WebElement> getElemenetsFromTable(By by) {
+ return GeneralUIUtils.getDriver().findElements(by);
+ }
+
+ private static List<WebElement> getNewButtonsList() {
+ WebElement createButtonsArea = driver.findElement(By.className("w-sdc-dashboard-card-new"));
+ createButtonsArea.click();
+ List<WebElement> buttonsList = driver.findElements(By.className("w-sdc-dashboard-card-new-button"));
+ return buttonsList;
+ }
+
+ public static final String FILE_PATH = System.getProperty("user.dir") + "\\src\\main\\resources\\Files\\";
+ public static String fileName = "JDM_vfc.yml";
+ public static final String toscaErrorMessage = "Invalid TOSCA template.";
+ public static final String yamlError = "Invalid YAML file.";
+ public static final String allReadyExistErro = "Imported resource already exists in ASDC Catalog.";
+
+ public static WebElement hoverOnArea(String areaId) {
+ Actions actions = new Actions(driver);
+ WebElement area = getWebElementWaitForVisible(areaId);
+ actions.moveToElement(area).perform();
+ return area;
+ }
+
+ public static WebElement actionBuild(WebElement element) throws InterruptedException {
+ // make an action on page//hover on element
+ Actions build = new Actions(driver); // here you state ActionBuider
+ build.moveToElement(element).build().perform();// hover the element.
+ Thread.sleep(1000);
+ return element;
+ }
+
+ public static File takeScreenshot(String zipFile, String dir, String testName) throws IOException {
+ if (zipFile == null) {
+ zipFile = testName;
+ }
+ try {
+ File scrFile = ((TakesScreenshot) GeneralUIUtils.getDriver()).getScreenshotAs(OutputType.FILE);
+ File filePath = new File(String.format("%s/%s.png", dir, zipFile));
+ new File(dir).mkdirs();
+ FileUtils.copyFile(scrFile, filePath);
+ return filePath;
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ return null;
+ }
+
+ public static void errorMessagePopupHandle(@SuppressWarnings("rawtypes") Supplier func) throws Exception {
+ try {
+ WebElement errorMessagePopupHeader = getDriver().findElement(By.className("w-sdc-modal-head-text"));
+ if (errorMessagePopupHeader.getText().equals("Error")) {
+ WebElement okButton = getWebButton("OK");
+ if (okButton.isDisplayed()) {
+ okButton.click();
+ func.get();
+ }
+ }
+ } catch (Exception e) {
+ throw new Exception("something went wrong, can't do anything");
+ }
+ }
+
+ public static void waitForLoader() {
+ waitForElements(By.className("tlv-loader"), 200, 3 * 60 * 1000);
+ }
+
+ public static void waitForLoaderOnboarding() {
+ waitForElements(By.className("tlv-loader"), 200, 13 * 60 * 1000);
+ }
+
+ public static List<WebElement> waitForElements(By by, int napPeriod, int maxWaitMS) {
+ boolean isKeepWaiting = true;
+ int currentWaitTimeMS = 0;
+ List<WebElement> elements = null;
+ while (isKeepWaiting) {
+ elements = getDriver().findElements(by);
+ isKeepWaiting = elements.size() > 0;
+ if (isKeepWaiting) {
+ sleep(napPeriod);
+ currentWaitTimeMS += napPeriod;
+ if (currentWaitTimeMS > maxWaitMS) {
+ isKeepWaiting = false;
+ }
+ }
+ }
+
+ return elements;
+
+ }
+
+ public static WebDriver getDriver() {
+ return driver;
+ }
+
+ public static WebElement rihtPanelAPI() {
+ return getWebElementWaitForVisible("tab-api");
+ }
+
+ public static void scrollDown() throws AWTException {
+ Robot robot = new Robot();
+ robot.keyPress(KeyEvent.VK_DOWN);
+ robot.keyRelease(KeyEvent.VK_DOWN);
+ }
+
+ // solution for "element not attached to the DOM anymore"
+ public static List<WebElement> getWorkspaceElements() throws InterruptedException {
+ Thread.sleep(1000);
+ List<WebElement> assets = GeneralUIUtils.getEelementsByClassName("w-sdc-dashboard-card-body");
+ return assets;
+ }
+
+ public static String getMethodName(Method method) {
+ return method.getName();
+ }
+
+ public static FileWriter InitializeprintToTxt(String testName) {
+ String idForTxtFile = new SimpleDateFormat("dd.MM.yyyy_HH.mm.ss").format(new Date());
+ File file = new File(testName + idForTxtFile);
+ FileWriter fw = null;
+ try {
+ fw = new FileWriter(file, true);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println(e.getLocalizedMessage());
+ }
+ return fw;
+ }
+
+ public static void closeFileWriter(FileWriter file) throws IOException {
+ file.flush();
+ file.close();
+ }
+
+ public static WebElement createAndImportButtons(CreateAndImportButtonsEnum type, WebDriver driver)
+ throws InterruptedException {
+ switch (type) {
+ case IMPORT_CP:
+ case IMPORT_VFC:
+ case IMPORT_VL:
+ hoverOnArea("importButtonsArea");
+ return GeneralUIUtils.getWebElementWaitForVisible("importVFCbutton");
+
+ case IMPORT_VF:
+ hoverOnArea("importButtonsArea");
+ return GeneralUIUtils.getWebElement(driver, "importVFbutton");
+ case CREATE_SERVICE:
+ hoverOnArea("AddButtonsArea", driver);
+ GeneralUIUtils.getWebElementWaitForVisible("createServiceButton").click();
+ ;
+ break;
+
+ case CREATE_PRODUCT:
+ GeneralUIUtils.getWebElement(driver, "createServiceButton").click();
+ GeneralUIUtils.getWebElementWaitForVisible("createServiceButton").click();
+ break;
+
+ default:
+ hoverOnArea("AddButtonsArea");
+ driver.findElement(By.xpath("//*[@data-tests-id='createResourceButton']")).click();
+ break;
+ }
+ return null;
+
+ }
+
+ public static String checkBoxLifeCyclestate(CheckBoxStatusEnum lifeCycle) {
+ String Status = "IN DESIGN CHECK OUT";
+ switch (lifeCycle) {
+ case CHECKIN:
+ Status = "IN DESIGN CHECK IN";
+ if (GeneralUIUtils.getWebElementWaitForVisible(lifeCycle.getValue()).isDisplayed()) {
+ GeneralUIUtils.getWebElementWaitForVisible(lifeCycle.getValue()).click();
+ }
+ break;
+ case CHECKOUT:
+ GeneralUIUtils.getWebElementWaitForVisible(lifeCycle.getValue()).click();
+ Status = "IN DESIGN CHECK OUT";
+ break;
+ case IN_TESTING:
+ GeneralUIUtils.getWebElementWaitForVisible(lifeCycle.getValue()).click();
+ Status = "IN TESTING";
+ break;
+ case READY_FOR_TESTING:
+ GeneralUIUtils.getWebElementWaitForVisible(lifeCycle.getValue()).click();
+ Status = "READY FOR TESTING";
+ break;
+ case CERTIFIED:
+ GeneralUIUtils.getWebElementWaitForVisible(lifeCycle.getValue()).click();
+ Status = "CERTIFIED";
+ break;
+ }
+ return Status;
+ }
+
+ public static String setFileTypeAndGetUniqId(ResourceTypeEnum fileType, ResourceReqDetails resourceDetails,
+ User user) throws IOException, Exception {
+ resourceDetails.setResourceType(fileType.toString());
+ RestCDUtils.getResource(resourceDetails, user);
+ return resourceDetails.getUniqueId();
+ }
+
+ public static void minimizeCatalogFilterByTitle(CatalogFilterTitlesEnum titlesEnum) {
+
+ switch (titlesEnum) {
+ case CATEGORIES:
+ GeneralUIUtils.getWebElementWaitForVisible(titlesEnum.getValue()).click();
+ break;
+ case STATUS:
+ GeneralUIUtils.getWebElementWaitForVisible(titlesEnum.getValue()).click();
+ break;
+ case TYPE:
+ GeneralUIUtils.getWebElementWaitForVisible(titlesEnum.getValue()).click();
+ break;
+ default:
+ break;
+ }
+ // webElementWaitForVisible.get(0).click();
+ // }
+ }
+
+ public static WebElement getWebElementWaitForVisible(String dataTestId) {
+ // try{
+ return waitFordataTestIdVisibility(dataTestId);
+ // }
+ // catch(Exception e){
+ // try{
+ // WebElement errorMessagePopupHeader =
+ // GeneralUIUtils.getDriver().findElement(By.className("w-sdc-modal-head-text"));
+ // if (errorMessagePopupHeader.getText().equals("Error")){
+ // WebElement okButton = GeneralUIUtils.getWebButton("OK");
+ // if (okButton.isDisplayed()){
+ //// takeScreenshot(LocalDateTime.now().toString().replaceAll(":", ""),
+ // SetupCDTest.SCREENSHOT_LOG_DIR);
+ // okButton.click();
+ // return getWebElementWaitForVisible(dataTestId);
+ // }
+ // }
+ // }
+ // catch(Exception exception){
+ // System.out.println(String.format("didn't find element with
+ // data-tests-id of %s", dataTestId));
+ // }
+ // }
+ // return null;
+
+ }
+
+ public static WebElement getWebElementById(String id) {
+ WebDriverWait wait = new WebDriverWait(driver, 5);
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='" + id + "']")));
+ }
+
+ public static WebElement getWebElementByName(String name) {
+ return driver.findElement(By.name(name));
+ }
+
+ // New tedy , this function will get the web elements by The new attribute
+ // value(data-tests-id)
+ public static List<WebElement> getWebElements(String dataTestId) {
+ return waitForElementsListVisibility(dataTestId);
+ }
+
+ // New tedy , this function will get the web element Button by The new
+ // attribute value(data-tests-id)
+ public static WebElement getWebButton(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait
+ .until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ }
+
+ // New tedy , this function will wait till the web element be
+ // visible(data-tests-id)
+ public static Boolean waitForInvisibileElement(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(
+ ExpectedConditions.invisibilityOfElementLocated(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ }
+
+ public static WebElement waitFordataTestIdVisibility(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(
+ ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ }
+
+ public static boolean clickcheckbox(String category) {
+ try {
+
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ public static WebElement waitForContainsdataTestIdVisibility2(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 5);
+ return wait.until(ExpectedConditions
+ .visibilityOfElementLocated(By.xpath("//*[contains (@data-tests-id, '" + dataTestId + "'])")));
+ }
+
+ public static List<WebElement> waitForContainsdataTestIdVisibility(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 5);
+ return wait.until(ExpectedConditions
+ .visibilityOfAllElementsLocatedBy(By.xpath("//*[contains (@data-tests-id, '" + dataTestId + "'])")));
+ }
+
+ public static WebElement waitForClassNameVisibility(String className) {
+ return waitForElementVisibility(By.className(className));
+ }
+
+ public static WebElement waitForElementVisibility(By by) {
+ return waitForElementVisibility(by, 3 * 60);
+ }
+
+ public static WebElement waitForElementVisibility(By by, int duration) {
+ WebDriverWait wait = new WebDriverWait(driver, duration);
+ return wait.until(ExpectedConditions.visibilityOf(driver.findElement(by)));
+ }
+
+ public static List<WebElement> waitForElementsListVisibility(By by) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions.visibilityOfAllElements(driver.findElements(by)));
+ }
+
+ public static boolean waitForElementsListInvisibility(By by) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions.invisibilityOfElementLocated(by));
+ }
+
+ // New tedy , this function will wait till the web elements be
+ // visible(data-tests-id)
+ public static List<WebElement> waitForElementsListVisibility(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ List<WebElement> findElements = wait.until(ExpectedConditions
+ .visibilityOfAllElementsLocatedBy(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ if (findElements.size() > 0) {
+ return findElements;
+ }
+ System.out.println("Elements not Exist!");
+ return null;
+ }
+
+ public static List<WebElement> waitForElementsListVisibilityTestMethod(String dataTestId) {
+ return driver.findElements(By.xpath("//*[@data-tests-id='" + dataTestId + "']"));
+ }
+
+ public static WebElement waitForBrowseButton(String dataTestId) {
+
+ return driver.findElement(By.xpath("//*[@data-tests-id='" + dataTestId + "']"));
+ }
+
+ public static List<WebElement> getWebElementsListByDataTestId(String dataTestId) {
+ return driver.findElements(By.xpath("//*[@data-tests-id='" + dataTestId + "']"));
+
+ }
+
+ public static WebElement getWebElementByDataTestId(String dataTestId) {
+ return driver.findElement(By.xpath("//*[@data-tests-id='" + dataTestId + "']"));
+ }
+
+ public static WebElement waitUntilClickableButton(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions
+ .elementToBeClickable(driver.findElement(By.xpath("//*[@data-tests-id='" + dataTestId + "']"))));
+ }
+
+ // interface Throws {
+ // <T,R, E extends Exception> R apply(T t) throws E;
+ // }
+ // public static <R> R swallowException(Throws<T,R,E> supplier){
+ // R returnValue;
+ // try{
+ // returnValue = supplier.get();
+ // }
+ // catch(Exception e){
+ // returnValue = null;
+ // }
+ // return returnValue;
+ // }
+ // Use this method only for special cases, otherwise use
+ // org.openecomp.sdc.ci.tests.utilities.GeneralUIUtils.getWebElementWaitForVisible(WebDriver,
+ // String)
+
+ public static boolean isElementPresent(String dataTestId) {
+ try {
+ driver.findElement(By.xpath("//*[@data-tests-id='" + dataTestId + "']"));
+ return true;
+ } catch (org.openqa.selenium.NoSuchElementException e) {
+ return false;
+ }
+ }
+
+ public static <R> R retryMethodOnException(Supplier<R> supplier) {
+ boolean stopSearch = false;
+ R ret = null;
+ Exception throwMe = null;
+ int timeElapsed = 0;
+ while (!stopSearch) {
+ try {
+ ret = supplier.get();
+ } catch (Exception e) {
+ throwMe = e;
+ GeneralUIUtils.sleep(250);
+ timeElapsed += 250;
+ if (timeElapsed > 5000) {
+ stopSearch = true;
+ }
+
+ } finally {
+ if (ret != null) {
+ stopSearch = true;
+ }
+ }
+ }
+ if (ret == null) {
+ throw new RuntimeException(throwMe);
+ } else {
+ return ret;
+ }
+
+ }
+
+ // this method will login as tester and start test or accept Assets.
+ public static void testerUser(Boolean startTest, Boolean accept, ResourceReqDetails resource) throws Exception {
+ // GeneralUIUtils.getWebElement(ResourceUIUtils.getName()).click();
+ String url = "http://localhost:8181/sdc1/proxy-tester1#/dashboard";
+ sleep(2000);
+ SetupCDTest.navigateToUrl(url);
+ GeneralUIUtils.getWebElementWaitForVisible(resource.getName()).click();
+
+ if (startTest) {
+ clickStartTesting();
+ }
+
+ if (accept) {
+ clickAccept();
+ }
+ }
+
+ public static void governorUser(Boolean reject, Boolean approve, ResourceReqDetails resource) throws Exception {
+ // GeneralUIUtils.getWebElement(ResourceUIUtils.getName()).click();
+ String url = "http://localhost:8181/sdc1/proxy-governor1#/dashboard";
+ sleep(2000);
+ SetupCDTest.navigateToUrl(url);
+ GeneralUIUtils.getWebElementWaitForVisible("w-sdc-dashboard-card-info");
+ GeneralUIUtils.getWebElementWaitForVisible(resource.getName()).click();
+ if (reject) {
+ clickReject();
+ }
+ if (approve) {
+ clickApprove();
+ }
+ sleep(1000);
+ }
+
+ public static void opsUser(Boolean disribute, Boolean reDisribute, ResourceReqDetails resource) throws Exception {
+ // GeneralUIUtils.getWebElement(ResourceUIUtils.getName()).click();
+ String url = "http://localhost:8181/sdc1/proxy-ops1#/dashboard";
+ sleep(2000);
+ SetupCDTest.navigateToUrl(url);
+ sleep(2000);
+ GeneralUIUtils.getWebElementWaitForVisible("w-sdc-dashboard-card-info");
+ GeneralUIUtils.getWebElementWaitForVisible(resource.getName()).click();
+ if (reDisribute) {
+ clickReDistribute();
+ }
+ if (disribute) {
+ clickDistribute();
+ }
+ sleep(1000);
+ }
+
+ // this function located select list by the data-test-id value and the item
+ // to be selected..
+ public static Select getSelectList(String item, String datatestsid) {
+ Select selectlist = new Select(waitFordataTestIdVisibility(datatestsid));
+ if (item != null) {
+ selectlist.selectByVisibleText(item);
+ }
+ return selectlist;
+ }
+
+ // Define description area .
+ public static String defineDescription(String descriptionText) {
+
+ WebElement resourceDescriptionTextbox = GeneralUIUtils.getWebElementWaitForVisible("description");
+ resourceDescriptionTextbox.clear();
+ resourceDescriptionTextbox.sendKeys(descriptionText);
+ return descriptionText;
+ }
+
+ public static WebElement catalogSearchBox(String searchText) {
+ WebElement searchBox = GeneralUIUtils.getWebElementWaitForVisible("main-menu-input-search");
+ searchBox.clear();
+ searchBox.sendKeys(searchText);
+ return searchBox;
+ }
+
+ // enum
+ public static void selectMenuOptionbyname(List<WebElement> options, MenuOptionsEnum optionName)
+ throws InterruptedException {
+
+ for (WebElement webElement : options) {
+ if (webElement.getText().equals(optionName.getValue())) {
+ actionBuild(webElement).click();
+ } else {
+ System.out.println("No such element!");
+ }
+ }
+
+ }
+
+ // back to workspace by Clicking the ASDC Logo.!
+ public static void clickASDCLogo() {
+ WebDriverWait wait = new WebDriverWait(driver, 15);
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.linkText("ASDC")));
+ WebElement ClickASDCLogo = driver.findElement(By.linkText("ASDC"));
+ ClickASDCLogo.click();
+ GeneralUIUtils.waitForLoader();
+ }
+
+ public static void clickExitSign() throws InterruptedException {
+ WebElement exitSign = driver.findElement(By.xpath("//*[contains(@class, 'x-btn')]"));
+ actionBuild(exitSign);
+ Thread.sleep(1000);
+ driver.findElement(By.xpath("//*[contains(@class, 'x-btn')]")).click();
+ }
+
+ public static void clickCreateButton() throws Exception {
+ getWebButton("create/save").click();
+ GeneralUIUtils.waitForLoader();
+ getWebElementWaitForVisible(DataTestIdEnum.LifeCyleChangeButtons.CHECK_IN.getValue());
+ }
+
+ public static void clickUpdateButton() throws Exception {
+ GeneralUIUtils.sleep(500);
+ clickCreateButton();
+ }
+
+ public static void checkOut() throws InterruptedException, AWTException {
+ actionBuild(getWebButton("check_out"));
+ getWebButton("check_out").click();
+ waitForInvisibileElement("check_out");
+ }
+
+ public static void clickStartTesting() throws InterruptedException {
+ actionBuild(getWebButton("start_testing"));
+ getWebButton("start_testing").click();
+ waitForInvisibileElement("start_testing");
+ getWebButton("create/save").click();
+ }
+
+ public static void clickAccept() throws InterruptedException {
+ actionBuild(getWebButton("accept"));
+ getWebButton("accept").click();
+ getWebElementWaitForVisible("checkindialog").sendKeys("Accept!");
+ clickOkButton();
+ sleep(1000);
+ }
+
+ public static void clickReject() throws InterruptedException {
+ actionBuild(getWebButton("reject"));
+ getWebButton("reject").click();
+ waitForInvisibileElement("reject");
+ }
+
+ public static void clickApprove() throws InterruptedException {
+ actionBuild(getWebButton("approve"));
+ getWebButton("approve").click();
+ waitForInvisibileElement("approve");
+ }
+
+ public static void clickDistribute() throws InterruptedException {
+ actionBuild(getWebButton("distribute"));
+ getWebButton("distribute").click();
+ waitForInvisibileElement("redistribute");
+ }
+
+ public static void clickReDistribute() throws InterruptedException {
+ actionBuild(getWebButton("redistribute"));
+ getWebButton("redistribute").click();
+ }
+
+ public static void clickCancel() {
+ getWebButton("cancel").click();
+ waitForInvisibileElement("cancel");
+ }
+
+ public static void checkIn() throws InterruptedException {
+ actionBuild(getWebButton("check_in"));
+ getWebButton("check_in").click();
+ getWebElementWaitForVisible("checkindialog").sendKeys("Check in!");
+ clickOkButton();
+ waitForInvisibileElement("checkindialog");
+ }
+
+ public static void clickSaveIcon() throws InterruptedException {
+ actionBuild(GeneralUIUtils.waitFordataTestIdVisibility("create/save"));
+ GeneralUIUtils.getWebButton(/* "delete_version" */"create/save").click();
+ Thread.sleep(1000);
+
+ }
+
+ // Open menu of Created Object and select option.
+ public static void openObjectMenuAndSelectOption(String uniqid, MenuOptionsEnum optionName)
+ throws InterruptedException, AWTException {
+ WebElement hoverOnMenu = actionBuild(getWebElementWaitForVisible(uniqid));
+ List<WebElement> menuOptions = hoverOnMenu.findElement(By.xpath("./following-sibling::*[1]"))
+ .findElements(By.xpath(".//*"));
+ selectMenuOptionbyname(menuOptions, optionName);
+ }
+
+ // Get elements by className.
+ public static WebElement getEelementByClassName(String element) {
+ try {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@class='" + element + "']")));
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public static List<WebElement> getEelementsByClassName(String element) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait
+ .until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath("//*[@class='" + element + "']")));
+ }
+
+ public static WebElement getEelementByContainsdatatestsid(String datatestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions
+ .visibilityOfElementLocated(By.xpath("//*[contains(@data-tests-id, '" + datatestId + "')]")));
+ }
+
+ // list
+ public static List<WebElement> getEelementsByContainsDataTestsId(String datatestId) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions
+ .visibilityOfAllElementsLocatedBy(By.xpath("//*[contains(@data-tests-id, '" + datatestId + "')]")));
+ }
+
+ public static WebElement getEelementBycontainsClassName(String classname) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(
+ ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(@class, '" + classname + "')]")));
+ }
+
+ public static WebElement getEelementByLinkText(String linkText) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@text='" + linkText + "']")));
+ }
+
+ public static List<WebElement> getEelementsBycontainsClassName(String classname) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions
+ .visibilityOfAllElementsLocatedBy(By.xpath("//*[contains(@class, '" + classname + "')]")));
+ }
+
+ public static WebElement getButtonByClassName(String element) {
+ WebDriverWait wait = new WebDriverWait(driver, 3 * 60);
+ return wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@class='" + element + "']")));
+ }
+
+ public static void checkinCheckout(String elementName) throws Exception, Exception {
+ checkIn();
+ getWebElementWaitForVisible(elementName).click();
+ ;
+ checkOut();
+
+ }
+
+ public static void moveToStep(DataTestIdEnum.StepsEnum Stepname) {
+ moveToStep(Stepname.getValue());
+ }
+
+ public static void moveToStep(String dataTestId) {
+ getWebButton(dataTestId).click();
+ waitForLoader();
+ }
+
+ public static void editFile(String name) {
+ WebElement editfilebutton = driver.findElement(By.id("edit" + name + ""));
+ editfilebutton.click();
+ }
+
+ public static void deleteFile(String name) {
+ WebElement deletebutton = driver.findElement(By.id("delete" + name + ""));
+ deletebutton.click();
+ }
+
+ public static void downloadFile(String name) {
+ WebElement downloadbutton = driver.findElement(By.id("download" + name + ""));
+ downloadbutton.click();
+ }
+
+ public static void sleep(int duration) {
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void hasDriver() {
+ try {
+ driver.getCurrentUrl();
+ driver.quit();
+ } catch (NullPointerException e) {
+ }
+ }
+
+ public static void initDriver() {
+ try {
+ boolean remoteTesting = SetupCDTest.config.isRemoteTesting();
+ if (!remoteTesting) {
+ System.out.println("opening LOCAL browser");
+ driver = new FirefoxDriver();
+
+ } else {
+ System.out.println("opening REMOTE browser");
+ String remoteEnvIP = SetupCDTest.config.getRemoteTestingMachineIP();
+ String remoteEnvPort = SetupCDTest.config.getRemoteTestingMachinePort();
+ DesiredCapabilities cap = new DesiredCapabilities();
+ cap = DesiredCapabilities.firefox();
+ cap.setPlatform(Platform.WINDOWS);
+ cap.setBrowserName("firefox");
+
+ String remoteNodeUrl = String.format(SetupCDTest.SELENIUM_NODE_URL, remoteEnvIP, remoteEnvPort);
+ driver = new RemoteWebDriver(new URL(remoteNodeUrl), cap);
+ }
+
+
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public static void windowZoomOut() {
+ final int zoomOutFactor = 2;
+ for (int i = 0; i < zoomOutFactor; i++) {
+ driver.findElement(By.tagName("html")).sendKeys(Keys.chord(Keys.CONTROL, Keys.SUBTRACT));
+ }
+ }
+
+ public static void defineTagsList(ResourceReqDetails resource, String[] resourceTags) {
+ List<String> taglist = new ArrayList<String>();
+ ;
+ WebElement resourceTagsTextbox = getWebElementWaitForVisible("i-sdc-tag-input");
+ for (String tag : resourceTags) {
+ resourceTagsTextbox.clear();
+ resourceTagsTextbox.sendKeys(tag);
+ resourceTagsTextbox.sendKeys(Keys.ENTER);
+ taglist.add(tag);
+ // waitForElements(By.className("sdc-loader"), 250, 15000);
+ }
+ }
+
+ // public static List<WebElement> waitForElements(By by, int napPeriod, int
+ // maxWaitMS){
+ // List<WebElement> elements = null;
+ //
+ // elements = getDriver().findElements(by);
+ // if( currentWaitTimeMS > maxWaitMS){
+ // }
+ // resource.setTags(taglist);
+ // return elements;
+ // }
+ public static void selectTabInRightPallete(String className) throws Exception {
+ WebElement tab = getEelementBycontainsClassName(className);
+ tab.click();
+ }
+
+ public static WebElement getWebElement(WebDriver driver, String dataTestId) {
+ return waitForElementVisibility(dataTestId);
+ }
+
+ public static void clickOkButton() throws InterruptedException {
+ // actionBuild(getWebButton("OK"));
+ // sleep(2000);
+ getWebButton("OK").click();
+ }
+
+ public static WebElement waitForElementVisibility(String dataTestId) {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 30);
+ return wait.until(
+ ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ }
+
+ public static WebElement deleteVersion() {
+ return GeneralUIUtils.waitFordataTestIdVisibility("delete_version");
+ }
+
+ // public static List<WebElement> getFilterTitles() throws Exception {
+ //
+ // return
+ // GeneralUIUtils.getEelementsByClassName("i-sdc-designer-leftbar-section-title-text");
+ //
+ // }
+
+ public static void deleteVersionInUI() throws Exception {
+
+ actionBuild(deleteVersion());
+ deleteVersion().click();
+ GeneralUIUtils.clickOkButton();
+ }
+
+ public static void uploadFileWithJavaRobot(String FilePath, String FileName) throws Exception {
+ StringSelection sel = new StringSelection(FilePath + FileName);
+ Toolkit.getDefaultToolkit().getSystemClipboard().setContents(sel, null);
+ Thread.sleep(1000);
+ Robot robot = new Robot();
+ robot.delay(1000);
+
+ // Release Enter
+ robot.keyRelease(KeyEvent.VK_ENTER);
+
+ // Press CTRL+V
+ robot.keyPress(KeyEvent.VK_CONTROL);
+ robot.keyPress(KeyEvent.VK_V);
+
+ // Release CTRL+V
+ robot.keyRelease(KeyEvent.VK_CONTROL);
+ robot.keyRelease(KeyEvent.VK_V);
+ Thread.sleep(1000);
+
+ // Press Enter
+ robot.keyPress(KeyEvent.VK_ENTER);
+ robot.keyRelease(KeyEvent.VK_ENTER);
+ Thread.sleep(3000);
+ }
+
+ public static String catalogFilterTypeChecBox(TypesEnum enumtype) throws Exception {
+ String Type = enumtype.toString().toLowerCase();
+ getWebElementWaitForVisible(enumtype.getValue()).click();
+ return Type;
+ }
+
+ public static List<String> catalogFilterStatusChecBox(CheckBoxStatusEnum statusEnum) throws Exception {
+ List<String> status = null;
+ switch (statusEnum) {
+ case IN_DESIGN:
+ status = Arrays.asList("NOT_CERTIFIED_CHECKIN", "NOT_CERTIFIED_CHECKOUT");
+ getWebElementWaitForVisible(statusEnum.getCatalogValue()).click();
+ break;
+ case READY_FOR_TESTING:
+ status = Arrays.asList("READY_FOR_CERTIFICATION");
+ getWebElementWaitForVisible(statusEnum.getCatalogValue()).click();
+ break;
+ case IN_TESTING:
+ status = Arrays.asList("CERTIFICATION_IN_PROGRESS");
+ getWebElementWaitForVisible(statusEnum.getCatalogValue()).click();
+ break;
+ case CERTIFIED:
+ status = Arrays.asList("CERTIFIED");
+ getWebElementWaitForVisible(statusEnum.getCatalogValue()).click();
+ break;
+ case DISTRIBUTED:
+ status = Arrays.asList("CERTIFIED");
+ getWebElementWaitForVisible(statusEnum.getCatalogValue()).click();
+ break;
+ }
+ return status;
+ }
+
+ public static void clickBreadCrumbs(BreadCrumbsButtonsEnum button) {
+ switch (button) {
+ case CATALOG:
+ GeneralUIUtils.getWebButton(button.getButton()).click();
+ break;
+ case HOME:
+ GeneralUIUtils.getWebButton(button.getButton()).click();
+ break;
+ case ON_BOARDING:
+ GeneralUIUtils.getWebButton(button.getButton()).click();
+ break;
+ default:
+ break;
+ }
+ }
+
+ public static void clickPrintScreen() {
+ getEelementByClassName("e-sdc-small-print-screen").click();
+ }
+
+ public static void clickSubmitForTest() throws InterruptedException {
+ getWebButton("submit_for_testing").click();
+ WebElement commentText = getDriver().findElement(By.className("w-sdc-modal-body-email"));
+ commentText.sendKeys("Submit For Test");
+ sleep(3000);
+ clickOkButton();
+ }
+
+ public static WebElement hoverOnArea(String areaId, WebDriver driver) {
+ Actions actions = new Actions(driver);
+ WebElement area = getWebElement(driver, areaId);
+ actions.moveToElement(area).perform();
+ return area;
+ }
+
+ public static WebElement moveToNextStep(DataTestIdEnum.StepsEnum Stepname) {
+ return getWebButton(Stepname.getValue());
+ }
+
+ public static String getComponentVersion(String componentName) {
+ return GeneralUIUtils.getWebElementWaitForVisible(componentName + "Version").getText();
+ }
+
+ public static void clickOnHTMLElementByDataTestId(String dataTestId) throws Exception {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 20);
+ StopWatch performanceMesuring = new StopWatch();
+ performanceMesuring.start();
+ WebElement element = wait
+ .until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ performanceMesuring.stop();
+ element.click();
+ performanceMesuring(dataTestId, performanceMesuring);
+
+ }
+
+ private static void performanceMesuring(String dataTestId, StopWatch performanceMesuring) {
+ Reporter.log("click on: " + dataTestId);
+ System.out.println("click on: " + dataTestId);
+ Reporter.log("Total Load Time Until click on button : " + dataTestId + " "
+ + (((double) performanceMesuring.getTime()) / 1000) + " seconds");
+ System.out.println("Total Load Time Until click on button : " + dataTestId + " "
+ + (((double) performanceMesuring.getTime()) / 1000) + " seconds");
+ performanceMesuring.reset();
+ performanceMesuring.start();
+ Boolean waitForElementInVisibilityByClassName = GeneralUIUtils.waitForElementInVisibilityByClassName(driver,
+ "tlv-loader");
+ performanceMesuring.stop();
+ Reporter.log("Total time before loader disappear: " + (((double) performanceMesuring.getTime()) / 1000)
+ + " seconds");
+ System.out.println("Total time before loader disappear: " + (((double) performanceMesuring.getTime()) / 1000)
+ + " seconds");
+ }
+
+ public static Boolean waitForElementInVisibilityByClassName(WebDriver driver, String className) {
+ WebDriverWait wait = new WebDriverWait(driver, 30);
+ return wait.until(ExpectedConditions.invisibilityOfElementLocated(By.className(className)));
+ }
+
+ public static void findComponentAndClick(ResourceReqDetails resource) throws Exception {
+
+ WebElement searchTextbox = GeneralUIUtils.getWebElementWaitForVisible("main-menu-input-search");
+ searchTextbox.clear();
+ searchTextbox.sendKeys(resource.getName());
+ clickOnHTMLElementByDataTestId(resource.getName());
+ }
+
+ public static void clickOnHTMLElementBylinkText(String linkText) throws Exception {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 20);
+ StopWatch performanceMesuring = new StopWatch();
+ performanceMesuring.start();
+ WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.linkText(linkText)));
+ performanceMesuring.stop();
+ element.click();
+ performanceMesuring(linkText, performanceMesuring);
+ }
+
+ public static void ultimateWait(){
+ long startTime = System.nanoTime();
+
+ GeneralUIUtils.waitForLoader();
+ GeneralUIUtils.waitForAngular();
+
+ long estimateTime = System.nanoTime();
+ long duration = TimeUnit.NANOSECONDS.toSeconds(estimateTime - startTime);
+ if(duration > timeOut){
+ SetupCDTest.getExtendTest().log(LogStatus.WARNING, String.format("Delays on page, %d seconds", duration));
+ }
+ }
+
+ public static void waitForAngular(){
+ WebDriverWait wait = new WebDriverWait(getDriver(), 90, 100);
+ wait.until(AdditionalConditions.pageLoadWait());
+ wait.until(AdditionalConditions.angularHasFinishedProcessing());
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ImportAssetUIUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ImportAssetUIUtils.java
new file mode 100644
index 0000000000..f917a4784f
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ImportAssetUIUtils.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.CreateAndImportButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+
+public class ImportAssetUIUtils {
+
+ public static void importAsssetAndFillGeneralInfo(String FILE_PATH, String fileName,
+ ResourceReqDetails resourceDetails, User user, CreateAndImportButtonsEnum type) throws Exception {
+ ResourceUIUtils.importFileWithSendKey(FILE_PATH, fileName, type);
+ ResourceUIUtils.fillResourceGeneralInformationPage(resourceDetails, user);
+ }
+
+ public static void importAsssetFillGeneralInfoAndSelectIcon(String FILE_PATH, String fileName,
+ ResourceReqDetails resourceDetails, User user, CreateAndImportButtonsEnum type) throws Exception {
+ importAsssetAndFillGeneralInfo(FILE_PATH, fileName, resourceDetails, user, type);
+ GeneralUIUtils.clickCreateButton();
+ ResourceUIUtils.selectRandomResourceIcon();
+ }
+
+ // checking or unchecking the checkbox on right palette at designer
+ // workspace
+ public static void checkbox(String checkBoxname, WebDriver driver) {
+ driver.findElement(By.xpath("//label[@for='" + checkBoxname + "']")).click();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/OnboardingUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/OnboardingUtils.java
new file mode 100644
index 0000000000..db6bada41e
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/OnboardingUtils.java
@@ -0,0 +1,457 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.nio.file.FileSystems;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.zip.ZipFile;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.json.JSONObject;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.config.Config;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpHeaderEnum;
+import org.openecomp.sdc.ci.tests.datatypes.http.HttpRequest;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.utils.Utils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.testng.Assert;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class OnboardingUtils {
+
+ public OnboardingUtils() {
+ }
+
+ private static String vendorId;
+ private static String vendorLicenseName;
+ private static String vendorLicenseAgreementId;
+ private static String featureGroupId;
+
+ public int countFilesInZipDirectory(String filepath, String filename) throws Exception, Throwable {
+ ZipFile zipFile = new ZipFile(filepath + filename);
+ return zipFile.size() - 1;
+ }
+
+ public static Object[] getZipFileNamesFromFolder(String filepath) {
+ return FileHandling.getFileNamesFromFolder(filepath, ".zip");
+ }
+
+ public static String createVendorSoftwareProduct(String HeatFileName, String filepath, User user)
+ throws Exception, Throwable {
+ String vspName = handleFilename(HeatFileName);
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "Starting to create vendor software product");
+
+ RestResponse createNewVendorSoftwareProduct = createNewVendorSoftwareProduct(vspName, vendorLicenseName,
+ vendorId, vendorLicenseAgreementId, featureGroupId, user);
+ assertEquals("did not succeed to create new VSP", 200,
+ createNewVendorSoftwareProduct.getErrorCode().intValue());
+ String vspid = ResponseParser.getValueFromJsonResponse(createNewVendorSoftwareProduct.getResponse(), "vspId");
+
+ RestResponse uploadHeatPackage = uploadHeatPackage(filepath, HeatFileName, vspid, user);
+ assertEquals("did not succeed to upload HEAT package", 200, uploadHeatPackage.getErrorCode().intValue());
+
+ RestResponse checkin = checkinVendorSoftwareProduct(vspid, user);
+ assertEquals("did not succeed to checking new VSP", 200, checkin.getErrorCode().intValue());
+
+ RestResponse submit = submitVendorSoftwareProduct(vspid, user);
+ assertEquals("did not succeed to submit new VSP", 200, submit.getErrorCode().intValue());
+
+ RestResponse createPackage = createPackageOfVendorSoftwareProduct(vspid, user);
+ assertEquals("did not succeed to create package of new VSP ", 200, createPackage.getErrorCode().intValue());
+
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "Succeeded to create vendor software product");
+
+ return vspName;
+ }
+
+ private static String handleFilename(String heatFileName) {
+ final String namePrefix = "ciVFOnboarded-";
+ final String nameSuffix = "-" + getShortUUID();
+
+ String subHeatFileName = heatFileName.substring(0, heatFileName.lastIndexOf("."));
+
+ if ((namePrefix + subHeatFileName + nameSuffix).length() >= 50) {
+ subHeatFileName = subHeatFileName.substring(0, 50 - namePrefix.length() - nameSuffix.length());
+ }
+
+ if (subHeatFileName.contains("(") || subHeatFileName.contains(")")) {
+ subHeatFileName = subHeatFileName.replace("(", "-");
+ subHeatFileName = subHeatFileName.replace(")", "-");
+ }
+
+ String vnfName = namePrefix + subHeatFileName + nameSuffix;
+ return vnfName;
+ }
+
+ public static void createVendorLicense(User user) throws Exception {
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "Starting to create vendor license");
+ vendorLicenseName = "ciLicense" + getShortUUID();
+ RestResponse vendorLicenseResponse = createVendorLicenseModels_1(vendorLicenseName, user);
+ assertEquals("did not succeed to create vendor license model", 200,
+ vendorLicenseResponse.getErrorCode().intValue());
+ vendorId = ResponseParser.getValueFromJsonResponse(vendorLicenseResponse.getResponse(), "value");
+
+ RestResponse vendorKeyGroupsResponse = createVendorKeyGroups_2(vendorId, user);
+ assertEquals("did not succeed to create vendor key groups", 200,
+ vendorKeyGroupsResponse.getErrorCode().intValue());
+ String keyGroupId = ResponseParser.getValueFromJsonResponse(vendorKeyGroupsResponse.getResponse(), "value");
+
+ RestResponse vendorEntitlementPool = createVendorEntitlementPool_3(vendorId, user);
+ assertEquals("did not succeed to create vendor entitlement pool", 200,
+ vendorEntitlementPool.getErrorCode().intValue());
+ String entitlementPoolId = ResponseParser.getValueFromJsonResponse(vendorEntitlementPool.getResponse(),
+ "value");
+
+ RestResponse vendorLicenseFeatureGroups = createVendorLicenseFeatureGroups_4(vendorId, keyGroupId,
+ entitlementPoolId, user);
+ assertEquals("did not succeed to create vendor license feature groups", 200,
+ vendorLicenseFeatureGroups.getErrorCode().intValue());
+ featureGroupId = ResponseParser.getValueFromJsonResponse(vendorLicenseFeatureGroups.getResponse(), "value");
+
+ RestResponse vendorLicenseAgreement = createVendorLicenseAgreement_5(vendorId, featureGroupId, user);
+ assertEquals("did not succeed to create vendor license agreement", 200,
+ vendorLicenseAgreement.getErrorCode().intValue());
+ vendorLicenseAgreementId = ResponseParser.getValueFromJsonResponse(vendorLicenseAgreement.getResponse(),
+ "value");
+
+ RestResponse checkinVendorLicense = checkinVendorLicense(vendorId, user);
+ assertEquals("did not succeed to checkin vendor license", 200, checkinVendorLicense.getErrorCode().intValue());
+
+ RestResponse submitVendorLicense = submitVendorLicense(vendorId, user);
+ assertEquals("did not succeed to submit vendor license", 200, submitVendorLicense.getErrorCode().intValue());
+
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, "Succeeded to create vendor license");
+ }
+
+ private static String getShortUUID() {
+ return UUID.randomUUID().toString().split("-")[0];
+ }
+
+ private static RestResponse actionOnComponent(String vspid, String action, String onboardComponent, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/" + onboardComponent + "/%s/actions", config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+ String userId = user.getUserId();
+
+ JSONObject jObject = new JSONObject();
+ jObject.put("action", action);
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPut(url, jObject.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse checkinVendorLicense(String vspid, User user) throws Exception {
+ return actionOnComponent(vspid, "Checkin", "vendor-license-models", user);
+ }
+
+ private static RestResponse submitVendorLicense(String vspid, User user) throws Exception {
+ return actionOnComponent(vspid, "Submit", "vendor-license-models", user);
+ }
+
+ private static RestResponse createVendorLicenseModels_1(String name, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models", config.getCatalogBeHost(), config.getCatalogBePort());
+ String userId = user.getUserId();
+ JSONObject jObject = new JSONObject();
+ jObject.put("vendorName", name);
+ jObject.put("description", "new vendor license model");
+ jObject.put("iconRef", "icon");
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObject.toString(), headersMap);
+ return response;
+
+ }
+
+ private static RestResponse createVendorLicenseAgreement_5(String vspid, String featureGroupId, User user)
+ throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/license-agreements",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ String userId = user.getUserId();
+
+ JSONObject licenseTermpObject = new JSONObject();
+ licenseTermpObject.put("choice", "Fixed_Term");
+ licenseTermpObject.put("other", "");
+
+ JSONObject jObjectBody = new JSONObject();
+ jObjectBody.put("name", "abc");
+ jObjectBody.put("description", "new vendor license agreement");
+ jObjectBody.put("requirementsAndConstrains", "abc");
+ jObjectBody.put("licenseTerm", licenseTermpObject);
+ jObjectBody.put("addedFeatureGroupsIds", Arrays.asList(featureGroupId).toArray());
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObjectBody.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse createVendorLicenseFeatureGroups_4(String vspid, String licenseKeyGroupId,
+ String entitlementPoolId, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/feature-groups",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ String userId = user.getUserId();
+
+ JSONObject jObject = new JSONObject();
+ jObject.put("name", "xyz");
+ jObject.put("description", "new vendor license feature groups");
+ jObject.put("partNumber", "123abc456");
+ jObject.put("addedLicenseKeyGroupsIds", Arrays.asList(licenseKeyGroupId).toArray());
+ jObject.put("addedEntitlementPoolsIds", Arrays.asList(entitlementPoolId).toArray());
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObject.toString(), headersMap);
+ return response;
+
+ }
+
+ private static RestResponse createVendorEntitlementPool_3(String vspid, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/entitlement-pools",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ String userId = user.getUserId();
+
+ JSONObject jEntitlementMetricObject = new JSONObject();
+ jEntitlementMetricObject.put("choice", "CPU");
+ jEntitlementMetricObject.put("other", "");
+
+ JSONObject jAggregationFunctionObject = new JSONObject();
+ jAggregationFunctionObject.put("choice", "Peak");
+ jAggregationFunctionObject.put("other", "");
+
+ JSONObject jOperationalScope = new JSONObject();
+ jOperationalScope.put("choices", Arrays.asList("Availability_Zone").toArray());
+ jOperationalScope.put("other", "");
+
+ JSONObject jTimeObject = new JSONObject();
+ jTimeObject.put("choice", "Hour");
+ jTimeObject.put("other", "");
+
+ JSONObject jObjectBody = new JSONObject();
+ jObjectBody.put("name", "def");
+ jObjectBody.put("description", "new vendor license entitlement pool");
+ jObjectBody.put("thresholdValue", "23");
+ jObjectBody.put("thresholdUnits", "Absolute");
+ jObjectBody.put("entitlementMetric", jEntitlementMetricObject);
+ jObjectBody.put("increments", "abcd");
+ jObjectBody.put("aggregationFunction", jAggregationFunctionObject);
+ jObjectBody.put("operationalScope", jOperationalScope);
+ jObjectBody.put("time", jTimeObject);
+ jObjectBody.put("manufacturerReferenceNumber", "123aaa");
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObjectBody.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse createVendorKeyGroups_2(String vspid, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-license-models/%s/license-key-groups",
+ config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ String userId = user.getUserId();
+
+ JSONObject jOperationalScope = new JSONObject();
+ jOperationalScope.put("choices", Arrays.asList("Tenant").toArray());
+ jOperationalScope.put("other", "");
+
+ JSONObject jObjectBody = new JSONObject();
+ jObjectBody.put("name", "keyGroup");
+ jObjectBody.put("description", "new vendor license key group");
+ jObjectBody.put("operationalScope", jOperationalScope);
+ jObjectBody.put("type", "Universal");
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+
+ HttpRequest http = new HttpRequest();
+ RestResponse response = http.httpSendPost(url, jObjectBody.toString(), headersMap);
+ return response;
+ }
+
+ private static RestResponse createNewVendorSoftwareProduct(String name, String vendorName, String vendorId,
+ String licenseAgreementId, String featureGroupsId, User user) throws Exception {
+ Config config = Utils.getConfig();
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-software-products",
+ config.getCatalogBeHost(), config.getCatalogBePort());
+
+ String userId = user.getUserId();
+
+ JSONObject jlicensingDataObj = new JSONObject();
+ jlicensingDataObj.put("licenseAgreement", licenseAgreementId);
+ jlicensingDataObj.put("featureGroups", Arrays.asList(featureGroupsId).toArray());
+
+ JSONObject jObject = new JSONObject();
+ jObject.put("name", name);
+ jObject.put("description", "new VSP description");
+ jObject.put("category", "resourceNewCategory.generic");
+ jObject.put("subCategory", "resourceNewCategory.generic.database");
+ jObject.put("licensingVersion", "1.0");
+ jObject.put("vendorName", vendorName);
+ jObject.put("vendorId", vendorId);
+ jObject.put("icon", "icon");
+ jObject.put("licensingData", jlicensingDataObj);
+
+ Map<String, String> headersMap = prepareHeadersMap(userId);
+ HttpRequest http = new HttpRequest();
+
+ RestResponse response = http.httpSendPost(url, jObject.toString(), headersMap);
+
+ return response;
+ }
+
+ private static RestResponse uploadHeatPackage(String filepath, String filename, String vspid, User user)
+ throws Exception {
+ Config config = Utils.getConfig();
+ CloseableHttpResponse response = null;
+
+ MultipartEntityBuilder mpBuilder = MultipartEntityBuilder.create();
+ mpBuilder.addPart("upload", new FileBody(getTestZipFile(filepath, filename)));
+
+ String url = String.format("http://%s:%s/onboarding-api/v1.0/vendor-software-products/%s/upload", config.getCatalogBeHost(), config.getCatalogBePort(), vspid);
+
+ Map<String, String> headersMap = prepareHeadersMap(user.getUserId());
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "multipart/form-data");
+
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost httpPost = new HttpPost(url);
+ RestResponse restResponse = new RestResponse();
+
+ Iterator<String> iterator = headersMap.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ String value = headersMap.get(key);
+ httpPost.addHeader(key, value);
+ }
+ httpPost.setEntity(mpBuilder.build());
+ response = client.execute(httpPost);
+ HttpEntity entity = response.getEntity();
+ String responseBody = null;
+ if (entity != null) {
+ InputStream instream = entity.getContent();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(instream, writer);
+ responseBody = writer.toString();
+ try {
+
+ } finally {
+ instream.close();
+ }
+ }
+
+ restResponse.setErrorCode(response.getStatusLine().getStatusCode());
+ restResponse.setResponse(responseBody);
+
+ return restResponse;
+
+ } finally {
+ closeResponse(response);
+ closeHttpClient(client);
+
+ }
+ }
+
+ private static void closeResponse(CloseableHttpResponse response) {
+ try {
+ if (response != null) {
+ response.close();
+ }
+ } catch (IOException e) {
+ System.out.println(String.format("failed to close client or response: %s", e.getMessage()));
+ }
+ }
+
+ private static void closeHttpClient(CloseableHttpClient client) {
+ try {
+ if (client != null) {
+ client.close();
+ }
+ } catch (IOException e) {
+ System.out.println(String.format("failed to close client or response: %s", e.getMessage()));
+ }
+ }
+
+ private static File getTestZipFile(String filepath, String filename) throws IOException {
+ Config config = Utils.getConfig();
+ String sourceDir = config.getImportResourceTestsConfigDir();
+ java.nio.file.Path filePath = FileSystems.getDefault().getPath(filepath + File.separator + filename);
+ return filePath.toFile();
+ }
+
+ private static RestResponse checkinVendorSoftwareProduct(String vspid, User user) throws Exception {
+ return actionOnComponent(vspid, "Checkin", "vendor-software-products", user);
+ }
+
+ private static RestResponse submitVendorSoftwareProduct(String vspid, User user) throws Exception {
+ return actionOnComponent(vspid, "Submit", "vendor-software-products", user);
+ }
+
+ private static RestResponse createPackageOfVendorSoftwareProduct(String vspid, User user) throws Exception {
+ return actionOnComponent(vspid, "Create_Package", "vendor-software-products", user);
+ }
+
+ protected static Map<String, String> prepareHeadersMap(String userId) {
+ Map<String, String> headersMap = new HashMap<String, String>();
+ headersMap.put(HttpHeaderEnum.CONTENT_TYPE.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.ACCEPT.getValue(), "application/json");
+ headersMap.put(HttpHeaderEnum.USER_ID.getValue(), userId);
+
+ return headersMap;
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/PropertiesUIUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/PropertiesUIUtils.java
new file mode 100644
index 0000000000..a625630196
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/PropertiesUIUtils.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.openecomp.sdc.ci.tests.datatypes.PropertyInfo;
+import org.openecomp.sdc.ci.tests.pages.PropertiesPage;
+import org.openqa.selenium.WebElement;
+
+public class PropertiesUIUtils {
+
+ // public static void addPropertByType(String type,String name,String
+ // defaultValue,String description) throws Exception{
+ //
+ // ResourceUIUtils.defineNewSelectList(type);
+ // ResourceUIUtils.definePropertyName(name);
+ // ResourceUIUtils.defineDefaultValueByType(defaultValue);
+ // ResourceUIUtils.defineDescription(description);
+ // Thread.sleep(2000);
+ // ResourceUIUtils.clickButton("Add");
+ // }
+ public static Map<String, String> addProperties(String name, String itemType, String defaultValue,
+ String description, String schemaType) throws Exception {
+ Map<String, String> propertyvalues = new HashMap<String, String>();
+ GeneralUIUtils.getSelectList(itemType, "propertyType");
+ ResourceUIUtils.definePropertyName(name);
+ if (itemType == "boolean") {
+ ResourceUIUtils.defineBoolenDefaultValue(defaultValue);
+ GeneralUIUtils.defineDescription(description);
+ Thread.sleep(2000);
+ GeneralUIUtils.getWebButton("Add").click();
+ ;
+ } else if (itemType == "list" || itemType == "map") {
+ GeneralUIUtils.getSelectList(schemaType, "schemaType");
+ }
+ if (!(itemType == "boolean")) {
+ ResourceUIUtils.defineDefaultValueByType(defaultValue);
+ GeneralUIUtils.defineDescription(description);
+ GeneralUIUtils.getWebButton("Add").click();
+ ;
+ Thread.sleep(2000);
+ }
+ propertyvalues.put("type", itemType);
+ propertyvalues.put("defaultValue", defaultValue);
+ propertyvalues.put("description", description);
+ propertyvalues.put("name", name);
+
+ return propertyvalues;
+ }
+
+ public static void vlidateProperties(Map<String, String> propertyValues) throws InterruptedException {
+ WebElement name = GeneralUIUtils
+ .actionBuild(GeneralUIUtils.getWebElementWaitForVisible(propertyValues.get("name")));
+ name.getText().equalsIgnoreCase(propertyValues.get("name"));
+ WebElement defaultValue = GeneralUIUtils
+ .actionBuild(GeneralUIUtils.getWebElementWaitForVisible(propertyValues.get("name")));
+ defaultValue.getText().equalsIgnoreCase(propertyValues.get("defaultValue"));
+ WebElement type = GeneralUIUtils
+ .actionBuild(GeneralUIUtils.getWebElementWaitForVisible(propertyValues.get("type")));
+ type.getText().equalsIgnoreCase(propertyValues.get("type"));
+ }
+
+ public static void addNewProperty(PropertyInfo property) {
+ PropertiesPage.getPropertyPopup().insertPropertyName(property.getName());
+ PropertiesPage.getPropertyPopup().selectPropertyType(property.getType().getType());
+ PropertiesPage.getPropertyPopup().insertPropertyDescription(property.getDescription());
+ PropertiesPage.getPropertyPopup().insertPropertyDefaultValue(property.getValue());
+
+ PropertiesPage.getPropertyPopup().clickSave();
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ResourceUIUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ResourceUIUtils.java
new file mode 100644
index 0000000000..9b374c10b8
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ResourceUIUtils.java
@@ -0,0 +1,864 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.awt.AWTException;
+import java.awt.Robot;
+import java.awt.event.KeyEvent;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.http.HttpStatus;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.CheckBoxStatusEnum;
+import org.openecomp.sdc.ci.tests.datatypes.CreateAndImportButtonsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.Dashboard;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceCategoriesNameEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.pages.ResourceGeneralPage;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.Select;
+import org.openqa.selenium.support.ui.WebDriverWait;
+import org.testng.AssertJUnit;
+
+public final class ResourceUIUtils {
+ public static final String RESOURCE_NAME_PREFIX = "ResourceCDTest-";
+ protected static final boolean IS_BEFORE_TEST = true;
+ public static final String INITIAL_VERSION = "0.1";
+ public static final String ICON_RESOURCE_NAME = "call_controll";
+ protected static final String UPDATED_RESOURCE_ICON_NAME = "objectStorage";
+
+ private ResourceUIUtils() {
+ }
+
+ static WebDriver driver = GeneralUIUtils.getDriver();
+
+ public static void defineResourceName(String resourceName) {
+
+ WebElement resourceNameTextbox = GeneralUIUtils.getDriver().findElement(By.name("componentName"));
+ resourceNameTextbox.clear();
+ resourceNameTextbox.sendKeys(resourceName);
+ }
+
+ public static void defineResourceCategory(String category, String datatestsid) {
+
+ GeneralUIUtils.getSelectList(category, datatestsid);
+ }
+
+ public static void importFileWithSendKey(String FilePath, String FileName, CreateAndImportButtonsEnum type)
+ throws Exception {
+ WebElement importButton = GeneralUIUtils.createAndImportButtons(type, driver).findElement(By.tagName("input"));
+ importButton.sendKeys(FilePath + FileName);
+ }
+
+ public static void importFileWithSendKeyBrowse(String FilePath, String FileName) throws Exception {
+ WebElement browsebutton = GeneralUIUtils.waitForBrowseButton("browseButton");
+ browsebutton.sendKeys(FilePath + FileName);
+ }
+
+ public static String defineUserId(String userId) {
+ WebElement resourceUserIdTextbox = GeneralUIUtils.getWebElementWaitForVisible("contactId");
+ resourceUserIdTextbox.clear();
+ resourceUserIdTextbox.sendKeys(userId);
+ return userId;
+ }
+
+ public static void defineVendorRelease(String resourceVendorRelease) {
+
+ WebElement resourceVendorReleaseTextbox = GeneralUIUtils.getWebElementWaitForVisible("vendorRelease");
+ resourceVendorReleaseTextbox.clear();
+ resourceVendorReleaseTextbox.sendKeys(resourceVendorRelease);
+ }
+
+ public static void selectResourceIcon(String resourceIcon) throws Exception {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 10);
+ wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@data-tests-id='" + resourceIcon + "']")))
+ .click();
+ }
+
+ public static String definePropertyName(String name) {
+
+ WebElement nameProperty = GeneralUIUtils.getDriver().findElement(By.name("propertyName"));
+ nameProperty.sendKeys(name);
+ return name;
+ }
+
+ public static void selectRandomResourceIcon() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.ICON);
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 4);
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(@data-tests-id, 'iconBox')]")));
+ List<WebElement> iconElement = GeneralUIUtils.getDriver()
+ .findElements(By.xpath("//*[contains(@data-tests-id, 'iconBox')]"));
+ iconElement.get(0).click();
+ }
+
+ public static List<WebElement> getAllObjectsOnWorkspace(WebDriver driver, ResourceReqDetails resource)
+ throws Exception {
+
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 10);
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@*='" + resource.getName() + "']")));
+ return GeneralUIUtils.getDriver()
+ .findElements(By.xpath("//div[@class='" + "w-sdc-dashboard-card-info-name" + "']"));
+
+ }
+
+ public static String getErrorMessageText(String text) throws Exception {
+
+ return GeneralUIUtils.getEelementBycontainsClassName(text).getText();
+
+ }
+
+ public static WebElement scrollElement(WebDriver driver) throws Exception {
+
+ return GeneralUIUtils.getDriver().findElement(By.className("ps-scrollbar-y"));
+ }
+
+ public static void scrollDownPage() throws AWTException, InterruptedException {
+ Robot robot = new Robot();
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ robot.keyPress(KeyEvent.VK_PAGE_DOWN);
+ robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
+ }
+
+ public static void defineNewSelectList(String Text) {
+ WebElement mySelectElm = GeneralUIUtils.getDriver().findElement(By.className("i-sdc-form-select"));
+ Select mySelectString = new Select(mySelectElm);
+ mySelectString.selectByVisibleText(Text);
+ }
+
+ public static void defineDefaultValueByType(String Value) {
+
+ WebElement valueString = GeneralUIUtils.getDriver().findElement(By.name("value"));
+ valueString.clear();
+ valueString.sendKeys(Value);
+ }
+
+ public static void defineBoolenDefaultValue(String Value) {
+
+ WebElement elementBoolean = GeneralUIUtils.getDriver().findElement(By.name("value"));
+ Select se = new Select(elementBoolean);
+ se.selectByValue(Value);
+ }
+
+ public static void clickButtonBlue() {
+ WebElement clickButtonBlue = GeneralUIUtils.getDriver().findElement(By.className("w-sdc-btn-blue"));
+ clickButtonBlue.click();
+ }
+
+ public static void clickButton(String selectButton) {
+
+ WebElement clickButton = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + selectButton + "']"));
+ clickButton.click();
+ }
+
+ public static WebElement Waitfunctionforbuttons(String element, int timeout) {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), timeout);
+ return wait.until(ExpectedConditions.elementToBeClickable(By.xpath(element)));
+ }
+
+ public static WebElement waitToButtonSubmitForTesting() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='submitForTesting']", 10);
+ }
+
+ public static WebElement waitToFinishButtonEnabled() {
+ return Waitfunctionforbuttons("//button[@data-tests-id='Finish']", 10);
+ }
+
+ public static WebElement waitToNextButtonEnabled() {
+ return Waitfunctionforbuttons("//button[@data-tests-id='Next']", 10);
+ }
+
+ public static WebElement waitToHomeMenu() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='main-menu-button-home']", 10);
+ }
+
+ public static WebElement waitToCatalogMenu() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='main-menu-button-catalog']", 10);
+ }
+
+ public static WebElement waitSearch() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='main-menu-input-search']", 10);
+ }
+
+ public static WebElement waitSubmitforTestingCard() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='i-sdc-dashboard-card-menu-item-SubmitforTesting']", 10);
+ }
+
+ public static WebElement waitViewCard() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='i-sdc-dashboard-card-menu-item-View']", 5);
+ }
+
+ public static void waitOpenCard(String requiredElementUniqueId) throws Exception {
+ WebElement menu = GeneralUIUtils.getDriver()
+ .findElement(By.xpath("//*[@data-tests-id='" + requiredElementUniqueId + "']"));
+ GeneralUIUtils.actionBuild(menu);
+ }
+
+ public static void fillResourceGeneralInformationPage(ResourceReqDetails resource, User user) {
+ try {
+
+ ResourceGeneralPage.defineName(resource.getName());
+ ResourceGeneralPage.defineDescription(resource.getDescription());
+ ResourceGeneralPage.defineCategory(resource.getCategories().get(0).getSubcategories().get(0).getName());
+ ResourceGeneralPage.defineVendorName(resource.getVendorName());
+ ResourceGeneralPage.defineVendorRelease(resource.getVendorRelease());
+ ResourceGeneralPage.defineTagsList(resource, new String[] { "This-is-tag", "another-tag", "Test-automation-tag" });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void fillNewResourceValues(ResourceReqDetails resource, User user) throws Exception {
+ fillResourceGeneralInformationPage(resource, user);
+ GeneralUIUtils.clickCreateButton();
+ }
+
+ // coded by teddy.
+ public static WebElement waitfunctionforallelements(String element) {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 5);
+ return wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@*='" + element + "']")));
+ }
+
+ public static WebElement waitFunctionForaGetElements(String element, int timeout) {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), timeout);
+ return wait.until(
+ ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@data-tests-id='" + element + "']")));
+ }
+
+ public static void getVFCGeneralInfo(ResourceReqDetails resource, User user) throws InterruptedException {
+ Thread.sleep(2000);
+ String version = GeneralUIUtils.getWebElements("versionvalue").get(0).getText().substring(1);
+ String name = GeneralUIUtils.getWebElementWaitForVisible("name").getAttribute("value");
+ String description = GeneralUIUtils.getWebElementWaitForVisible("description").getAttribute("value");
+ String category = GeneralUIUtils.getSelectList(null, "selectGeneralCategory").getFirstSelectedOption()
+ .getText();
+ String vendorName = GeneralUIUtils.getWebElementWaitForVisible("vendorName").getAttribute("value");
+ String vendorRelease = GeneralUIUtils.getWebElementWaitForVisible("vendorRelease").getAttribute("value");
+ List<WebElement> tags = GeneralUIUtils.waitForElementsListVisibility("i-sdc-tag-text");
+ String type = GeneralUIUtils.waitForElementsListVisibility("type").get(1).getText();
+ int index = type.lastIndexOf(":");
+ System.out.println(type.substring(0, index));
+ String ContactId = GeneralUIUtils.getWebElementWaitForVisible("contactId").getAttribute("value");
+ System.out.println(resource.getVersion());
+ assertTrue(resource.getVersion().equals(version));
+ assertTrue(resource.getName().equals(name));
+ assertTrue(resource.getDescription().equals(description));
+ System.out.println(resource.getVendorName());
+ System.out.println(resource.getVendorRelease());
+ assertTrue(resource.getCategories().get(0).getSubcategories().get(0).getName().equals(category));
+ assertTrue(resource.getVendorName().equals(vendorName));
+ assertTrue(resource.getVendorRelease().equals(vendorRelease));
+ assertTrue(resource.getCreatorUserId().equals(ContactId));
+ assertEquals(type.substring(0, index), resource.getResourceType());
+
+ for (int i = 0; i < tags.size(); i++) {
+ assertEquals(resource.getTags().get(i), tags.get(i).getText());
+ }
+ }
+
+ public static void getGeneralInfo(ResourceReqDetails resource, User user) {
+ List<WebElement> tags = GeneralUIUtils.waitForElementsListVisibility("tag");
+
+ for (WebElement tag : tags) {
+ System.out.println(resource.getTags().get(0));
+ }
+ }
+
+ public static void getGeneralInfoForTags(ResourceReqDetails resource, User user) {
+
+ clickMore();
+ String componentType = waitFunctionForaGetElements("componentType", 3).getText();
+ String version = waitFunctionForaGetElements("version", 3).getText();
+ String category = waitFunctionForaGetElements("category", 3).getText();// get
+ // right
+ // panel
+ // Category.
+ String resourceType = waitFunctionForaGetElements("resourceType", 3).getText();// get
+ // right
+ // panel
+ // SubCategory.
+ String date = GeneralUIUtils.getEelementByClassName("creationDate").getText();
+ String aouthor = waitfunctionforallelements("author'").getText();
+ String vendorName = waitFunctionForaGetElements("vendorName", 3).getText();
+ String vendorRelease = waitFunctionForaGetElements("vendorRelease", 3).getText();
+ String contactId = waitFunctionForaGetElements("contactId", 3).getText();
+ String description = waitFunctionForaGetElements("description", 3).getText();
+ List<WebElement> tags = GeneralUIUtils.waitForElementsListVisibility("tag");
+ assertTrue(componentType.equals("RESOURCE"));
+ assertTrue(version.equals(resource.getVersion()));
+ assertTrue(category.equals(resource.getCategories().get(0).getName()));
+ assertEquals(resourceType, resource.getResourceType());
+ // assertEquals(Date,resource.getCreationDate());
+ // assertEquals(Aouthor,resource.getCreatorFullName());
+ assertTrue(vendorName.equals(resource.getVendorName()));
+ assertTrue(vendorRelease.equals(resource.getVendorRelease()));
+ assertTrue(contactId.equals(resource.getContactId()));
+ assertTrue(description.equals(resource.getDescription() + "\nLess"));
+ assertTrue(tags.equals("Tag-150"));
+ }
+
+ public static WebElement searchVFNameInWorkspace(ResourceReqDetails resource, User user) throws Exception {
+
+ List<WebElement> findElements = GeneralUIUtils.getDriver()
+ .findElements(By.xpath("//div[@data-tests-id='" + resource.getUniqueId() + "']"));
+ assertNotNull("did not find any elements", findElements);
+ for (WebElement webElement : findElements) {
+ if (webElement.getText().contains(resource.getUniqueId())) {
+ System.out.println("I find it");
+ return webElement;
+ }
+ }
+ return null;
+ }
+
+ public static Boolean searchCheckOutWorkspace(ResourceReqDetails resource, User user,
+ CheckBoxStatusEnum checkBoxStatusEnum) throws Exception {
+
+ List<WebElement> findElements = GeneralUIUtils.getDriver()
+ .findElements(By.xpath("//div[@data-tests-id='component.lifecycleState']"));
+ assertNotNull("did not find any elements", findElements);
+ for (WebElement webElement : findElements) {
+ if (!webElement.getAttribute("class").contains(checkBoxStatusEnum.name())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // coded by tedy.
+ public static void validateWithRightPalett(ResourceReqDetails resource, User user) {
+ // String
+ // Type=Waitfunctionforallelements("sharingService.selectedEntity.getTypeForView()",3).getText();
+ String ResourceType = waitfunctionforallelements("selectedComponent.resourceType").getText();
+ System.out.println(ResourceType);
+ String Version = waitfunctionforallelements("selectedComponent.version").getText();
+ String Category = waitfunctionforallelements("selectedComponent.categories[0].name").getText();// get
+ // right
+ // panel
+ // Category.
+ String CanvasSubCategory = waitfunctionforallelements("selectedComponent.categories[0].subcategories[0].name")
+ .getText();// get right panel SubCategory.
+ // String Date=Waitfunctionforelements("selectedComponent.creationDate |
+ // date: 'MM/dd/yyyy'").getText();
+ // String
+ // Aouthor=waitfunctionforallelements("selectedComponent.creatorFullName'").getText();
+ String VendorName = waitfunctionforallelements("selectedComponent.vendorName").getText();
+ String VendorRelease = waitfunctionforallelements("selectedComponent.vendorRelease").getText();
+ String contactId = waitfunctionforallelements("selectedComponent.contactId").getText();
+ String Description = waitfunctionforallelements("selectedComponent.description").getText();
+ String TagVF = waitfunctionforallelements("tag").getText();
+ AssertJUnit.assertEquals(ResourceType, resource.getResourceType());
+ AssertJUnit.assertEquals(Version, resource.getVersion());
+ AssertJUnit.assertEquals(Category, resource.getCategories().get(0).getName());
+ AssertJUnit.assertEquals(CanvasSubCategory,
+ resource.getCategories().get(0).getSubcategories().get(0).getName());
+ // assertEquals(Date,resource.getCreationDate());
+ // assertEquals(Aouthor,resource.getCreatorFullName());
+ AssertJUnit.assertEquals(VendorName, resource.getVendorName());
+ AssertJUnit.assertEquals(VendorRelease, resource.getVendorRelease());
+ AssertJUnit.assertEquals(contactId, resource.getContactId());
+ AssertJUnit.assertEquals(Description, resource.getDescription() + "\nLess");
+ AssertJUnit.assertEquals(TagVF, "qa123");
+ }
+
+ public static void clickMore() {
+ WebElement clickButtonSubmit = GeneralUIUtils.getDriver()
+ .findElement(By.className("ellipsis-directive-more-less"));
+ clickButtonSubmit.click();
+ }
+
+ public static RestResponse createResourceInUI(ResourceReqDetails resource, User user)
+ throws Exception, AWTException {
+ System.out.println("creating resource...");
+ fillNewResourceValues(resource, user);
+ RestResponse getCreatedResource = RestCDUtils.getResource(resource, user);
+ AssertJUnit.assertEquals("Did not succeed to get any resource", HttpStatus.SC_OK,
+ getCreatedResource.getErrorCode().intValue());
+
+ return getCreatedResource;
+ }
+
+ public static RestResponse createResource(ResourceReqDetails resource, User user) throws Exception, AWTException {
+
+ ResourceUIUtils.moveToHTMLElementByClassName("w-sdc-dashboard-card-new");
+ ResourceUIUtils.clickOnHTMLElementByDataTestId(DataTestIdEnum.Dashboard.BUTTON_ADD_VF.getValue());
+ GeneralUIUtils.waitForLoader();
+ // GeneralUIUtils.sleep(1000);
+ fillResourceGeneralInformationPage(resource, user);
+ GeneralUIUtils.clickCreateButton();
+ return null;
+
+ }
+
+ public static RestResponse updateResourceInformationPage(ResourceReqDetails resource, User user)
+ throws Exception, AWTException {
+
+ fillResourceGeneralInformationPage(resource, user);
+ GeneralUIUtils.clickCreateButton();
+ return null;
+
+ }
+
+ public static RestResponse checkInResourceInUI(ResourceReqDetails resource, User user) throws Exception {
+
+ WebElement ASDCLink = GeneralUIUtils.getDriver().findElement(By.className("w-sdc-header-logo-link"));
+ ASDCLink.click();
+ Thread.sleep(2000);
+
+ List<WebElement> listFormInput = GeneralUIUtils.getDriver()
+ .findElements(By.className("i-sdc-left-sidebar-nav-item"));
+ WebElement addPropertyElement = listFormInput.get(0);
+ addPropertyElement.click();
+ Thread.sleep(2000);
+
+ WebElement searchResource = GeneralUIUtils.getDriver()
+ .findElement(By.className("w-sdc-header-catalog-search-input"));
+ searchResource.sendKeys("newresource4test");
+
+ Thread.sleep(1000);
+
+ WebElement buttonClickMenu = GeneralUIUtils.getDriver()
+ .findElement(By.className("w-sdc-dashboard-card-menu-button"));
+ buttonClickMenu.click();
+
+ WebElement clickMenu = GeneralUIUtils.getDriver().findElement(By.className("w-sdc-dashboard-card-menu"));
+ clickMenu.click();
+
+ List<WebElement> clickCheckIn = GeneralUIUtils.getDriver()
+ .findElements(By.className("i-sdc-dashboard-card-menu-item"));
+ WebElement clickCheckInMenu = clickCheckIn.get(1);
+ clickCheckInMenu.click();
+
+ WebElement descriptionForSubmit = GeneralUIUtils.getDriver()
+ .findElement(By.className("w-sdc-modal-body-comment"));
+ descriptionForSubmit.sendKeys("checkin resource");
+ Thread.sleep(2000);
+ WebElement clickButtonSubmitTwo = GeneralUIUtils.getDriver().findElement(By.className("w-sdc-btn-blue"));
+ clickButtonSubmitTwo.click();
+ Thread.sleep(2000);
+
+ WebElement buttonClickMenu1 = GeneralUIUtils.getDriver()
+ .findElement(By.className("w-sdc-dashboard-card-menu-button"));
+ buttonClickMenu1.click();
+
+ WebElement clickMenu1 = GeneralUIUtils.getDriver().findElement(By.className("w-sdc-dashboard-card-menu"));
+ clickMenu1.click();
+
+ List<WebElement> clickCheckOut = GeneralUIUtils.getDriver()
+ .findElements(By.className("i-sdc-dashboard-card-menu-item"));
+ WebElement clickCheckOutMenu = clickCheckOut.get(0);
+ clickCheckOutMenu.click();
+
+ Thread.sleep(3000);
+ RestResponse getResource = RestCDUtils.getResource(resource, user);
+ AssertJUnit.assertEquals("Did not succeed to get resource after create", 200,
+ getResource.getErrorCode().intValue());
+ return getResource;
+
+ }
+
+ public static String lifeCycleStateUI() throws InterruptedException {
+ return GeneralUIUtils.getWebElementWaitForVisible("formlifecyclestate").getText();
+ }
+
+ public static List<String> catalogFilterResourceCategoriesChecBox(ResourceCategoriesNameEnum enumName)
+ throws Exception {
+ List<String> categories = Arrays.asList();
+ switch (enumName) {
+ case APPLICATIONL4:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("applicationServer", "defaulticon", "vl", "cp", "call_controll", "borderElement",
+ "network", "firewall", "database", "loadBalancer");
+ break;
+ case APPLICATION_SERVER:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("applicationServer", "vl", "cp", "defaulticon");
+ break;
+ case BORDER_ELEMENT:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("borderElement", "vl", "cp", "defaulticon");
+ break;
+ case CALL_CONTROL:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("call_controll", "vl", "cp", "defaulticon");
+ break;
+ case COMMON_NETWORK_RESOURCES:
+ GeneralUIUtils.getEelementByLinkText("Common Network Resources").click();
+ categories = Arrays.asList("network", "vl", "cp", "defaulticon");
+ break;
+ case CONNECTION_POINTS:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("cp", "defaulticon");
+ break;
+ case DATABASE:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("database", "vl", "cp", "defaulticon");
+ break;
+ case DATABASE_GENERIC:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("database", "vl", "cp", "defaulticon");
+ break;
+ case FIREWALL:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("firewall", "vl", "cp", "defaulticon");
+ break;
+ case GATEWAY:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("gateway", "vl", "cp", "defaulticon");
+ break;
+ case INFRASTRUCTURE:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("connector", "vl", "cp", "defaulticon");
+ break;
+ case INFRASTRUCTUREL23:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("ucpe", "vl", "cp", "defaulticon");
+ break;
+ case LAN_CONNECTORS:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("network", "port", "connector", "vl", "cp", "defaulticon");
+ break;
+ case LOAD_BALANCER:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("loadBalancer", "vl", "cp", "defaulticon");
+ break;
+ case MEDIA_SERVERS:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("network", "vl", "cp", "defaulticon");
+ break;
+ case NETWORKL4:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("network", "vl", "cp", "defaulticon");
+ break;
+ case NETWORK_ELEMENTS:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("port", "defaulticon", "network", "connector", "vl", "cp");
+ break;
+ case NETWORK_L23:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("network", "vl", "defaulticon", "cp", "router", "port", "connector", "gateway",
+ "ucpe");
+ break;
+ case NETWORK_CONNECTIVITY:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("network", "vl", "cp", "defaulticon");
+ break;
+ case GENERIC:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("database", "port", "loadBalancer", "vl", "cp", "objectStorage", "compute",
+ "defaulticon", "ucpe", "network", "connector");
+ break;
+ case ABSTRACT:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("objectStorage", "compute", "defaulticon", "cp", "vl");
+ break;
+ case Router:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("router", "vl", "cp", "defaulticon");
+ break;
+ case VIRTUAL_LINKS:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("vl", "defaulticon");
+ break;
+ case WAN_Connectors:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("network", "port", "connector", "vl", "cp", "defaulticon");
+ break;
+ case WEB_SERVER:
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ categories = Arrays.asList("applicationServer", "vl", "cp", "defaulticon");
+ break;
+ }
+ return categories;
+ }
+
+ public static void deleteVersionInUI() throws Exception {
+
+ waitToDeleteVersion().click();
+ ResourceUIUtils.clickButtonBlue();
+ }
+
+ public static void selectTabInRightPallete(String className) throws Exception {
+ WebElement tab = GeneralUIUtils.getEelementBycontainsClassName(className);
+ tab.click();
+ }
+
+ public static WebElement waitToDeleteVersion() {
+ return Waitfunctionforbuttons("//*[@data-tests-id='deleteVersion']", 10);
+ }
+
+ public static WebElement rihtPanelAPI() {
+ return waitFunctionForaGetElements("tab-api", 10);
+ }
+
+ /**
+ * Click on HTML element.
+ *
+ * @param dataTestId
+ * @throws Exception
+ */
+ public static void clickOnHTMLElementByDataTestId(String dataTestId) throws Exception {
+ WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(), 20);
+ WebElement element = wait
+ .until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@data-tests-id='" + dataTestId + "']")));
+ element.click();
+ // wait.until(ExpectedConditions.elemetto)
+ // WebElement serviceButton =
+ // GeneralUIUtils.getDriver().findElement(By.xpath("//*[@data-tests-id='"
+ // + dataTestId + "']"));
+ // serviceButton.
+ // serviceButton.click();
+ }
+
+ /**
+ * Move to HTML element by class name. When moving to the HTML element, it
+ * will raise hover event.
+ *
+ * @param className
+ */
+ public static void moveToHTMLElementByClassName(String className) {
+ Actions actions = new Actions(GeneralUIUtils.getDriver());
+ final WebElement createButtonsArea = GeneralUIUtils
+ .retryMethodOnException(() -> GeneralUIUtils.getDriver().findElement(By.className(className)));
+ actions.moveToElement(createButtonsArea).perform();
+ }
+
+ /**
+ * Move to HTML element by element id. When moving to the HTML element, it
+ * will raise hover event.
+ *
+ * @param className
+ */
+ static void moveToHTMLElementByDataTestId(String dataTestId) {
+ // WebElement hoverArea =
+ // GeneralUIUtils.getDriver().findElement(By.xpath("//*[@data-tests-id='"
+ // + dataTestId + "']"));
+ WebElement hoverArea = GeneralUIUtils.waitForElementVisibility(dataTestId);
+ // WebDriverWait wait = new WebDriverWait(GeneralUIUtils.getDriver(),
+ // 30);
+ // wait.until(ExpectedConditions.visibilityOf(hoverArea));
+
+ Actions actions = new Actions(GeneralUIUtils.getDriver());
+ actions.moveToElement(hoverArea).perform();
+ }
+
+ // public static ResourceReqDetails createResourceInUI(User user){
+ // try{
+ // ResourceReqDetails defineResourceDetails =
+ // defineResourceDetails(ResourceTypeEnum.VF);
+ // ResourceUIUtils.moveToHTMLElementByClassName("w-sdc-dashboard-card-new");
+ // ResourceUIUtils.clickOnHTMLElementByDataTestId(DataTestIdEnum.Dashboard.BUTTON_ADD_VF.getValue());
+ // GeneralUIUtils.waitForLoader();
+ //// GeneralUIUtils.sleep(1000);
+ // fillResourceGeneralInformationPage(defineResourceDetails, user);
+ // GeneralUIUtils.clickCreateButton();
+ // return defineResourceDetails;
+ // }
+ // catch( Exception e){
+ // throw new RuntimeException(e);
+ // }
+ // }
+
+ /**
+ * Import VFC
+ *
+ * @param user
+ * @param filePath
+ * @param fileName
+ * @return
+ * @throws Exception
+ */
+
+ public static void importVfc(ResourceReqDetails resourceMetaData, String filePath, String fileName, User user)
+ throws Exception {
+ GeneralUIUtils.hoverOnArea(Dashboard.IMPORT_AREA.getValue());
+ // Insert file to the browse dialog
+ final WebElement browseWebElement = GeneralUIUtils
+ .getWebElementByDataTestId(DataTestIdEnum.Dashboard.IMPORT_VFC_FILE.getValue());
+ browseWebElement.sendKeys(filePath + fileName);
+
+ // Fill the general page fields.
+ GeneralUIUtils.waitForLoader();
+ fillResourceGeneralInformationPage(resourceMetaData, user);
+ GeneralUIUtils.clickCreateButton();
+
+ }
+
+ // public static ResourceReqDetails importVfcInUI(User user, String
+ // filePath, String fileName, ResourceTypeEnum resourceType) {
+ // ResourceReqDetails defineResourceDetails =
+ // defineResourceDetails(resourceType);
+ // ResourceUIUtils.moveToHTMLElementByDataTestId(Dashboard.IMPORT_AREA.getValue());
+ //
+ // // Insert file to the browse dialog
+ // final WebElement browseWebElement =
+ // GeneralUIUtils.getWebElementByDataTestId(DataTestIdEnum.Dashboard.IMPORT_VFC_FILE.getValue());
+ // browseWebElement.sendKeys(filePath + fileName);
+ //
+ // // Fill the general page fields.
+ // GeneralUIUtils.waitForLoader();
+ // fillResourceGeneralInformationPage(defineResourceDetails, user);
+ // GeneralUIUtils.clickCreateButton();
+ // return defineResourceDetails;
+ // }
+
+ /**
+ * Import VF
+ *
+ * @param user
+ * @param filePath
+ * @param fileName
+ * @return
+ * @throws Exception
+ */
+ // public static ResourceReqDetails importVfInUI(User user, String filePath,
+ // String fileName) throws Exception {
+ // ResourceReqDetails defineResourceDetails =
+ // defineResourceDetails(ResourceTypeEnum.VF);
+ // ResourceUIUtils.moveToHTMLElementByDataTestId(Dashboard.IMPORT_AREA.getValue());
+ //
+ // // Insert file to the browse dialog
+ // final WebElement browseWebElement =
+ // GeneralUIUtils.getWebElementByDataTestId(DataTestIdEnum.Dashboard.IMPORT_VF_FILE.getValue());
+ // browseWebElement.sendKeys(filePath + fileName);
+ //
+ // // Fill the general page fields.
+ // GeneralUIUtils.waitForLoader();
+ // fillResourceGeneralInformationPage(defineResourceDetails, user);
+ // GeneralUIUtils.clickCreateButton();
+ // return defineResourceDetails;
+ // }
+
+ // public static ResourceReqDetails defineResourceDetails(ResourceTypeEnum
+ // resourceType) {
+ // ResourceReqDetails resource = new ResourceReqDetails();
+ // resource = ElementFactory.getDefaultResource(NormativeTypesEnum.ROOT,
+ // ResourceCategoryEnum.APPLICATION_L4_CALL_CONTROL);
+ // resource.setVersion(INITIAL_VERSION);
+ // resource.setIcon(ICON_RESOURCE_NAME);
+ // resource.setResourceType(resourceType.toString());
+ // resource.setName(getRandomComponentName(RESOURCE_NAME_PREFIX));
+ //
+ // SetupCDTest.setCreatedComponents(Arrays.asList(resource));
+ //
+ // return resource;
+ // }
+
+ protected static String getRandomComponentName(String prefix) {
+ return prefix + new Random().nextInt(10000);
+ }
+
+ public static ImmutablePair<String, String> getFirstRIPos(ResourceReqDetails createResourceInUI, User user) {
+ String responseAfterDrag = RestCDUtils.getResource(createResourceInUI, user).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ String xPosPostDrag = (String) ((JSONObject) ((JSONArray) jsonResource.get("componentInstances")).get(0))
+ .get("posX");
+ String yPosPostDrag = (String) ((JSONObject) ((JSONArray) jsonResource.get("componentInstances")).get(0))
+ .get("posY");
+ return new ImmutablePair<String, String>(xPosPostDrag, yPosPostDrag);
+
+ }
+
+ public static WebElement getErrorMessageText(WebDriver driver, String text) throws Exception {
+
+ return GeneralUIUtils.getEelementBycontainsClassName(text);
+
+ }
+
+ public static void fillGeneralInfoValuesAndIcon(ResourceReqDetails resource, User user) throws Exception {
+ fillResourceGeneralInformationPage(resource, user);
+ GeneralUIUtils.clickCreateButton();
+
+ selectRandomResourceIcon();
+ }
+
+ // coded by teddy.
+ public static void getVFCGeneralInfoAndValidate(ResourceReqDetails resource, User user)
+ throws InterruptedException {
+ Thread.sleep(2000);
+ WebDriver driver = GeneralUIUtils.getDriver();
+ String version = GeneralUIUtils.getSelectList(null, "versionHeader").getFirstSelectedOption().getText();
+ String name = GeneralUIUtils.getWebElement(driver, "name").getAttribute("value");
+ String description = GeneralUIUtils.getWebElement(driver, "description").getAttribute("value");
+ String category = GeneralUIUtils.getSelectList(null, "selectGeneralCategory").getFirstSelectedOption()
+ .getText();
+ String vendorName = GeneralUIUtils.getWebElement(driver, "vendorName").getAttribute("value");
+ String vendorRelease = GeneralUIUtils.getWebElement(driver, "vendorRelease").getAttribute("value");
+ List<WebElement> tags = GeneralUIUtils.waitForElementsListVisibility("i-sdc-tag-text");
+ String type = GeneralUIUtils.waitForElementsListVisibility("type").get(1).getText();
+ int index = type.lastIndexOf(":");
+ System.out.println(type.substring(0, index));
+ String contactId = GeneralUIUtils.getWebElement(driver, "contactId").getAttribute("value");
+ System.out.println(resource.getVersion());
+ assertTrue(resource.getVersion().equals(version.substring(1)));
+ assertTrue(resource.getName().equals(name));
+ assertTrue(resource.getDescription().equals(description));
+ System.out.println(resource.getVendorName());
+ System.out.println(resource.getVendorRelease());
+ assertTrue(resource.getCategories().get(0).getSubcategories().get(0).getName().equals(category));
+ assertTrue(resource.getVendorName().equals(vendorName));
+ assertTrue(resource.getVendorRelease().equals(vendorRelease));
+ assertTrue(resource.getCreatorUserId().equals(contactId));
+ assertEquals(type.substring(0, index), resource.getResourceType());
+
+ for (int i = 0; i < tags.size(); i++) {
+ assertEquals(resource.getTags().get(i), tags.get(i).getText());
+ }
+ }
+
+ public static RestResponse createResourceNG(ResourceReqDetails resource, User user) throws Exception, AWTException {
+
+ ResourceUIUtils.moveToHTMLElementByClassName("w-sdc-dashboard-card-new");
+ ResourceUIUtils.clickOnHTMLElementByDataTestId(DataTestIdEnum.Dashboard.BUTTON_ADD_VF.getValue());
+ fillResourceGeneralInformationPage(resource, user);
+ GeneralUIUtils.clickOnHTMLElementByDataTestId(DataTestIdEnum.LifeCyleChangeButtons.CREATE.getValue());
+ return null;
+
+ }
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/RestCDUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/RestCDUtils.java
new file mode 100644
index 0000000000..10535e0157
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/RestCDUtils.java
@@ -0,0 +1,182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ProductReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.http.RestResponse;
+import org.openecomp.sdc.ci.tests.utils.rest.ProductRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResourceRestUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+import org.openecomp.sdc.ci.tests.utils.rest.ServiceRestUtils;
+
+public class RestCDUtils {
+
+ private static void setResourceUniqueIdAndUUID(ComponentReqDetails element, RestResponse getResourceResponse) {
+ element.setUniqueId(ResponseParser.getUniqueIdFromResponse(getResourceResponse));
+ element.setUUID(ResponseParser.getUuidFromResponse(getResourceResponse));
+ }
+
+ public static RestResponse getResource(ResourceReqDetails resource, User user) {
+ try {
+ System.out.println("trying to get resource");
+ GeneralUIUtils.sleep(1000);
+ RestResponse getResourceResponse = null;
+ String reourceUniqueId = resource.getUniqueId();
+ if (reourceUniqueId != null) {
+ getResourceResponse = ResourceRestUtils.getResource(reourceUniqueId);
+ if (getResourceResponse.getErrorCode().intValue() == 200) {
+ System.out.println("succeeded to get resource");
+ }
+ return getResourceResponse;
+ }
+ JSONObject getResourceJSONObject = null;
+ getResourceResponse = ResourceRestUtils.getResourceByNameAndVersion(user.getUserId(), resource.getName(),
+ resource.getVersion());
+ if (getResourceResponse.getErrorCode().intValue() == 200) {
+ JSONArray jArray = new JSONArray(getResourceResponse.getResponse());
+ for (int i = 0; i < jArray.length(); i++) {
+ getResourceJSONObject = jArray.getJSONObject(i);
+ String resourceType = ResponseParser.getValueFromJsonResponse(getResourceJSONObject.toString(),
+ "resourceType");
+ if (resourceType.equals(resource.getResourceType())) {
+ getResourceResponse.setResponse(getResourceJSONObject.toString());
+ setResourceUniqueIdAndUUID(resource, getResourceResponse);
+ System.out.println("succeeded to get resource");
+ return getResourceResponse;
+ }
+ }
+ }
+
+ return getResourceResponse;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static RestResponse getService(ServiceReqDetails service, User user) {
+ try {
+ Thread.sleep(3500);
+ RestResponse getServiceResponse = ServiceRestUtils.getServiceByNameAndVersion(user, service.getName(),
+ service.getVersion());
+ if (getServiceResponse.getErrorCode().intValue() == 200) {
+ setResourceUniqueIdAndUUID(service, getServiceResponse);
+ }
+ return getServiceResponse;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public static RestResponse getProduct(ProductReqDetails product, User user) {
+ try {
+ Thread.sleep(3500);
+ RestResponse getProductResponse = ProductRestUtils.getProductByNameAndVersion(product.getName(),
+ product.getVersion(), user.getUserId());
+ if (getProductResponse.getErrorCode().intValue() == 200) {
+ setResourceUniqueIdAndUUID(product, getProductResponse);
+ }
+ return getProductResponse;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Map<String, String> getAllElementVersionsFromResponse(RestResponse getResource) throws Exception {
+ Map<String, String> versionsMap = new HashMap<String, String>();
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+
+ JSONObject object = new JSONObject(getResource.getResponse());
+ versionsMap = mapper.readValue(object.get("allVersions").toString(), Map.class);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return versionsMap;
+
+ }
+
+ return versionsMap;
+ }
+
+ public static void deleteElementVersions(Map<String, String> elementVersions, boolean isBeforeTest, Object clazz,
+ User user) throws Exception {
+ Iterator<String> iterator = elementVersions.keySet().iterator();
+ while (iterator.hasNext()) {
+ String singleVersion = iterator.next();
+ String uniqueId = elementVersions.get(singleVersion);
+ RestResponse deleteResponse = null;
+ if (clazz instanceof ServiceReqDetails) {
+ deleteResponse = ServiceRestUtils.deleteServiceById(uniqueId, user.getUserId());
+ } else if (clazz instanceof ResourceReqDetails) {
+ deleteResponse = ResourceRestUtils.deleteResource(uniqueId, user.getUserId());
+ } else if (clazz instanceof ProductReqDetails) {
+ deleteResponse = ProductRestUtils.deleteProduct(uniqueId, user.getUserId());
+ }
+
+ if (isBeforeTest) {
+ assertTrue(deleteResponse.getErrorCode().intValue() == 204
+ || deleteResponse.getErrorCode().intValue() == 404);
+ } else {
+ assertTrue(deleteResponse.getErrorCode().intValue() == 204);
+ }
+ }
+ }
+
+ public static void deleteAllResourceVersionsAfterTest(ComponentReqDetails componentDetails,
+ RestResponse getObjectResponse, User user) {
+ try {
+ deleteAllComponentVersion(false, componentDetails, getObjectResponse, user);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void deleteAllResourceVersionsBeforeTest(ComponentReqDetails componentDetails,
+ RestResponse getObjectResponse, User user) throws Exception {
+ deleteAllComponentVersion(true, componentDetails, getObjectResponse, user);
+ }
+
+ public static void deleteAllComponentVersion(boolean isBeforeTest, ComponentReqDetails componentDetails,
+ RestResponse getObjectResponse, User user) throws Exception {
+ if (getObjectResponse.getErrorCode().intValue() == 404)
+ return;
+ Map<String, String> componentVersionsMap = getAllElementVersionsFromResponse(getObjectResponse);
+ System.out.println("deleting...");
+ deleteElementVersions(componentVersionsMap, isBeforeTest, componentDetails, user);
+ componentDetails.setUniqueId(null);
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ServiceUIUtils.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ServiceUIUtils.java
new file mode 100644
index 0000000000..5c2608b51e
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/utilities/ServiceUIUtils.java
@@ -0,0 +1,241 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.utilities;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.awt.AWTException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.rules.TestName;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum;
+import org.openecomp.sdc.ci.tests.datatypes.DataTestIdEnum.StepsEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceCategoriesNameEnum;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.execute.setup.SetupCDTest;
+import org.openecomp.sdc.ci.tests.pages.ServiceGeneralPage;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.Select;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import com.relevantcodes.extentreports.LogStatus;
+
+public class ServiceUIUtils {
+
+ protected static WebDriver driver;
+
+ public ServiceUIUtils(TestName name, String className) {
+ super();
+ }
+
+ public static String defineServiceName(String Name) {
+ WebElement serviceName = GeneralUIUtils.getWebElementWaitForVisible("name");
+ serviceName.clear();
+ serviceName.sendKeys(Name);
+ return Name;
+ }
+
+ public void moveResourceInstanceToCanvasUI() throws Exception {
+ List<WebElement> moveResource = driver.findElements(By.className("sprite-resource-icons"));
+ WebElement moveResourceToCanvasResourceOne = moveResource.get(0);
+ // WebElement moveResource =
+ // driver.findElement(By.className("sprite-resource-icons"));
+ Actions action = new Actions(driver);
+ action.moveToElement(moveResourceToCanvasResourceOne);
+ action.clickAndHold(moveResourceToCanvasResourceOne);
+ action.moveByOffset(635, 375);
+ action.release();
+ action.perform();
+ WebElement moveResourceToCanvasResourceTwo = moveResource.get(1);
+ action.moveToElement(moveResourceToCanvasResourceTwo);
+ action.clickAndHold(moveResourceToCanvasResourceTwo);
+ action.moveByOffset(535, 375);
+ action.release();
+ action.perform();
+ WebElement moveResourceToCanvasResourceTree = moveResource.get(2);
+ action.moveToElement(moveResourceToCanvasResourceTree);
+ action.clickAndHold(moveResourceToCanvasResourceTree);
+ action.moveByOffset(435, 375);
+ action.release();
+ action.perform();
+ Thread.sleep(2000);
+ }
+
+ public static String catalogFilterServiceCategoriesChecBox(ServiceCategoriesNameEnum enumName) throws Exception {
+ String Type = null;
+ GeneralUIUtils.getWebElementWaitForVisible(enumName.getValue()).click();
+ return Type;
+ }
+
+ public static List<String> catalogServiceTypeChecBox(ServiceCategoriesNameEnum enumtype) throws Exception {
+ List<String> categories = null;
+ switch (enumtype) {
+ case NETWORK_L13:
+ GeneralUIUtils.getWebElementWaitForVisible(enumtype.getValue()).click();
+ categories = Arrays.asList("network_l_1-3");
+ break;
+ case NETWORKL4:
+ GeneralUIUtils.getWebElementWaitForVisible(enumtype.getValue()).click();
+ categories = Arrays.asList("network_l_4 ");
+ break;
+ case MOBILITY:
+ GeneralUIUtils.getWebElementWaitForVisible(enumtype.getValue()).click();
+ categories = Arrays.asList("mobility");
+ break;
+ case VOIPCALL_CONTROL:
+ GeneralUIUtils.getWebElementWaitForVisible(enumtype.getValue()).click();
+ categories = Arrays.asList("call_controll ");
+ break;
+ }
+ return categories;
+ }
+
+ public static WebElement waitToNextButtonEnabled() {
+ return GeneralUIUtils.getWebButton("Next");
+ }
+
+ public static WebElement waitToFinishButtonEnabled() {
+ return GeneralUIUtils.getWebButton("Finish");
+ }
+
+ public static WebElement deleteServiceInUI() {
+
+ return GeneralUIUtils.getWebButton("deleteVersion");
+ }
+
+ // get the service view data for validate.
+ // created by tedy.
+ public static void getServiceGeneralInfo(ServiceReqDetails service, User user) throws InterruptedException {
+ Thread.sleep(2000);
+ String version = GeneralUIUtils.getSelectList(null, "versionHeader").getFirstSelectedOption().getText()
+ .substring(1);
+ String name = GeneralUIUtils.getWebElementWaitForVisible("name").getAttribute("value");
+ String description = GeneralUIUtils.getWebElementWaitForVisible("description").getAttribute("value");
+ String category = GeneralUIUtils.getSelectList(null, "selectGeneralCategory").getFirstSelectedOption()
+ .getText();
+ List<WebElement> tags = GeneralUIUtils.waitForElementsListVisibility("i-sdc-tag-text");
+ String type = GeneralUIUtils.waitForElementsListVisibility("type").get(1).getText();
+ int index = type.lastIndexOf(":");
+ System.out.println(type.substring(0, index));
+ String contactId = GeneralUIUtils.getWebElementWaitForVisible("contactId").getAttribute("value");
+ String projectCode = GeneralUIUtils.getWebElementWaitForVisible("projectCode").getAttribute("value");
+ System.out.println(service.getVersion());
+ assertTrue(service.getVersion().equals(version));
+ assertTrue(service.getName().equals(name));
+ assertTrue(service.getDescription().equals(description));
+ assertTrue(service.getCategories().get(0).getName().equals(category));
+ System.out.println(service.getContactId());
+ assertTrue(service.getContactId().equals(contactId));
+ assertTrue(service.getProjectCode().equals(projectCode));
+ for (int i = 0; i < tags.size(); i++) {
+ assertEquals(service.getTags().get(i), tags.get(i).getText());
+ }
+
+ }
+
+ public static void defineTagsList(ServiceReqDetails service, String[] serviceTags) {
+ List<String> taglist = new ArrayList<String>();
+ ;
+ WebElement serviceTagsTextbox = GeneralUIUtils.getWebElementWaitForVisible("i-sdc-tag-input");
+ for (String tag : serviceTags) {
+ serviceTagsTextbox.clear();
+ serviceTagsTextbox.sendKeys(tag);
+ serviceTagsTextbox.sendKeys(Keys.ENTER);
+ taglist.add(tag);
+ }
+ taglist.add(0, service.getName());
+ service.setTags(taglist);
+ }
+
+ public static Select defineServiceCategory(String category) {
+
+ return GeneralUIUtils.getSelectList(category, "selectGeneralCategory");
+ }
+
+ public static void defineServiceProjectCode(String projectCode) {
+ WebElement projectCodeTextbox = GeneralUIUtils.getWebElementWaitForVisible("projectCode");
+ projectCodeTextbox.clear();
+ projectCodeTextbox.sendKeys(projectCode);
+ }
+
+ public static void selectRandomResourceIcon() throws Exception {
+ GeneralUIUtils.moveToStep(StepsEnum.ICON);
+ WebDriverWait wait = new WebDriverWait(driver, 6);
+ wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(@data-tests-id, 'iconBox')]")));
+ List<WebElement> iconElement = driver.findElements(By.xpath("//*[contains(@data-tests-id, 'iconBox')]"));
+ iconElement.get(0).click();
+ }
+
+ public static String defineDescription(String description) {
+ WebElement descriptionTextbox = GeneralUIUtils.getWebElementWaitForVisible("description");
+ descriptionTextbox.clear();
+ descriptionTextbox.sendKeys(description);
+ return description;
+ }
+
+ public static void defineContactId(String userId) {
+ WebElement contactId = GeneralUIUtils.getWebElementWaitForVisible("contactId");
+ contactId.clear();
+ contactId.sendKeys(userId);
+ }
+
+ public static WebElement clickAddArtifact() {
+
+ return GeneralUIUtils.getWebButton("addArtifactButton");
+ }
+
+ public static WebElement getArtifactName() {
+ return GeneralUIUtils.getWebButton("artifactName");
+ }
+
+ public static WebElement getArtifactDetails() {
+ return GeneralUIUtils.getWebButton("artifactDisplayName");
+ }
+
+ public static void fillServiceGeneralPage(ServiceReqDetails service, User user) throws Exception {
+ ServiceGeneralPage.defineName(service.getName());
+ ServiceGeneralPage.defineDescription(service.getDescription());
+ ServiceGeneralPage.defineCategory(service.getCategories().get(0).getName());
+ ServiceGeneralPage.defineProjectCode(service.getProjectCode());
+ ServiceGeneralPage.defineTagsList(service, new String[] { "This-is-tag", "another-tag" });
+
+ }
+
+ public static void createService(ServiceReqDetails service, User user) throws Exception, AWTException {
+
+ ResourceUIUtils.moveToHTMLElementByClassName("w-sdc-dashboard-card-new");
+ ResourceUIUtils.clickOnHTMLElementByDataTestId(DataTestIdEnum.Dashboard.BUTTON_ADD_SERVICE.getValue());
+ GeneralUIUtils.waitForLoader();
+ fillServiceGeneralPage(service, user);
+ GeneralUIUtils.clickCreateButton();
+ SetupCDTest.getExtendTest().log(LogStatus.INFO, String.format("Service %s created", service.getName()));
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/ServiceVerificator.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/ServiceVerificator.java
new file mode 100644
index 0000000000..cbad80d344
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/ServiceVerificator.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.verificator;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ComponentReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.ServiceReqDetails;
+import org.openecomp.sdc.ci.tests.utilities.RestCDUtils;
+
+public class ServiceVerificator {
+
+ private ServiceVerificator() {
+ }
+
+ public static void verifyNumOfComponentInstances(ComponentReqDetails component, String version, int numOfVFC,
+ User user) {
+ String responseAfterDrag = null;
+ component.setVersion(version);
+ if (component instanceof ServiceReqDetails) {
+ responseAfterDrag = RestCDUtils.getService((ServiceReqDetails) component, user).getResponse();
+ } else if (component instanceof ResourceReqDetails) {
+ responseAfterDrag = RestCDUtils.getResource((ResourceReqDetails) component, user).getResponse();
+ }
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ int size = ((JSONArray) jsonResource.get("componentInstances")).size();
+ assertTrue(size == numOfVFC);
+ }
+
+ public static void verifyLinkCreated(ServiceReqDetails createServiceInUI, User user, int expectedRelationsSize) {
+ String responseAfterDrag = RestCDUtils.getService(createServiceInUI, user).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ assertTrue(((JSONArray) jsonResource.get("componentInstancesRelations")).size() == expectedRelationsSize);
+
+ }
+
+}
diff --git a/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/VfVerificator.java b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/VfVerificator.java
new file mode 100644
index 0000000000..1ffde75414
--- /dev/null
+++ b/ui-ci/src/main/java/org/openecomp/sdc/ci/tests/verificator/VfVerificator.java
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.sdc.ci.tests.verificator;
+
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.util.Iterator;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.openecomp.sdc.be.model.LifecycleStateEnum;
+import org.openecomp.sdc.be.model.Resource;
+import org.openecomp.sdc.be.model.User;
+import org.openecomp.sdc.ci.tests.datatypes.ResourceReqDetails;
+import org.openecomp.sdc.ci.tests.datatypes.enums.LifeCycleStatesEnum;
+import org.openecomp.sdc.ci.tests.pages.ResourceGeneralPage;
+import org.openecomp.sdc.ci.tests.utilities.ResourceUIUtils;
+import org.openecomp.sdc.ci.tests.utilities.RestCDUtils;
+import org.openecomp.sdc.ci.tests.utils.rest.ResponseParser;
+
+public final class VfVerificator {
+ private VfVerificator() {
+ }
+
+ public static void verifyNumOfComponentInstances(ResourceReqDetails createResourceInUI, int numOfVFC, User user) {
+ String responseAfterDrag = RestCDUtils.getResource(createResourceInUI, user).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ int size = ((JSONArray) jsonResource.get("componentInstances")).size();
+ assertTrue(size == numOfVFC);
+ }
+
+ public static void verifyRILocationChanged(ResourceReqDetails createResourceInUI,
+ ImmutablePair<String, String> prevRIPos, User user) {
+
+ ImmutablePair<String, String> currRIPos = ResourceUIUtils.getFirstRIPos(createResourceInUI, user);
+ assertTrue(!prevRIPos.left.equals(currRIPos.left) || !prevRIPos.right.equals(currRIPos.right));
+ }
+
+ public static void verifyLinkCreated(ResourceReqDetails createResourceInUI, User user, int expectedRelationsSize) {
+ String responseAfterDrag = RestCDUtils.getResource(createResourceInUI, user).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ assertTrue(((JSONArray) jsonResource.get("componentInstancesRelations")).size() == expectedRelationsSize);
+
+ }
+
+ public static void verifyVFUpdatedInUI(ResourceReqDetails vf) {
+ assertTrue(vf.getName().equals(ResourceGeneralPage.getNameText()));
+ assertTrue(vf.getDescription().equals(ResourceGeneralPage.getDescriptionText()));
+ assertTrue(vf.getVendorName().equals(ResourceGeneralPage.getVendorNameText()));
+ assertTrue(vf.getVendorRelease().equals(ResourceGeneralPage.getVendorReleaseText()));
+ assertTrue(vf.getContactId().equals(ResourceGeneralPage.getUserIdContactText()));
+ }
+
+ public static void verifyVFUpdated(ResourceReqDetails vf, User user) {
+ String response = RestCDUtils.getResource(vf, user).getResponse();
+ Resource resource = ResponseParser.convertResourceResponseToJavaObject(response);
+ assertTrue(vf.getName().equals(resource.getName()));
+ assertTrue(vf.getDescription().equals(resource.getDescription()));
+ assertTrue(vf.getCategories().equals(resource.getCategories()));
+ assertTrue(vf.getVendorName().equals(resource.getVendorName()));
+ assertTrue(vf.getVendorRelease().equals(resource.getVendorRelease()));
+ assertTrue(vf.getTags().equals(resource.getTags()));
+ assertTrue(vf.getContactId().equals(resource.getContactId()));
+ }
+
+ public static void verifyVFLifecycle(ResourceReqDetails vf, User user, LifecycleStateEnum expectedLifecycleState) {
+ String responseAfterDrag = RestCDUtils.getResource(vf, user).getResponse();
+ JSONObject jsonResource = (JSONObject) JSONValue.parse(responseAfterDrag);
+ String actualLifecycleState = jsonResource.get("lifecycleState").toString();
+ assertTrue("actual: " + actualLifecycleState + "--expected: " + expectedLifecycleState,
+ expectedLifecycleState.name().equals(actualLifecycleState));
+ }
+}
diff --git a/ui-ci/src/main/resources/Downloads/CP_WAN.yml b/ui-ci/src/main/resources/Downloads/CP_WAN.yml
new file mode 100644
index 0000000000..eeabbb8490
--- /dev/null
+++ b/ui-ci/src/main/resources/Downloads/CP_WAN.yml
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.WANTedy: derived_from: org.openecomp.resource.cp.CPTedy
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink_in:
+ capability: tosca.capabilities.network.Linkable relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out:
+ capability: tosca.capabilities.network.Linkable relationship: tosca.relationships.network.LinksTo
+ - virtualLink_in1:
+ capability: tosca.capabilities.network.Linkable relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out1:
+ capability: tosca.capabilities.network.Linkable relationship: tosca.relationships.network.LinksTo
+ - virtualLink_in2:
+ capability: tosca.capabilities.network.Linkable relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out2:
+ capability: tosca.capabilities.network.Linkable relationship: tosca.relationships.network.LinksTo
+ - virtualbinding:
+ capability: tosca.capabilities.network.Bindable relationship: tosca.relationships.network.BindsTo \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Downloads/Fortigate02_vFW_VFC.yml b/ui-ci/src/main/resources/Downloads/Fortigate02_vFW_VFC.yml
new file mode 100644
index 0000000000..a24d004929
--- /dev/null
+++ b/ui-ci/src/main/resources/Downloads/Fortigate02_vFW_VFC.yml
@@ -0,0 +1,63 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+
+ org.openecomp.resource.vfc.vFW.Fortigate02Tedy:
+ derived_from: org.openecomp.resource.vfc.vFWTedy
+
+ properties:
+ att-part-number:
+ type: string
+ default: "ATT-FortiGate-VM02"
+ vendor-name:
+ type: string
+ default: “FORTINETâ€
+ vendor-model:
+ type: string
+ default: "VM02"
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ default: 2
+ vcpu-min:
+ type: integer
+ default: 1
+ vcpu-max:
+ type: integer
+ default: 2
+ vmemory-default:
+ default: 4
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ default: 1
+ vmemory-max:
+ type: integer
+ default: 4
+ vdisk-default:
+ type: integer
+ default: 20
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ default: 2
+ vdisk-max:
+ type: integer
+ default: 20
+ vnf-type:
+ type: string
+ default: “Advanced FWâ€
+ software-version:
+ type: string
+ default: “5.2.4â€
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ default: “IPS, AntiVirus, URL Filter, APPID†\ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/CP.yml b/ui-ci/src/main/resources/Files/CP.yml
new file mode 100644
index 0000000000..48b592265f
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/CP.yml
@@ -0,0 +1,65 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-ucpe-part-number:
+ type: string
+ vendor-name:
+ type: string
+ required: true
+ vendor-model:
+ type: string
+ required: true
+ total-vcpu:
+ type: integer
+ description: number of vCPUs
+ total-memory:
+ type: integer
+ description: GB
+ total-disk:
+ type: integer
+ description: GB
+ base-system-image-file-name:
+ type: string
+ linux-host-vendor:
+ type: string
+ linux-host-os-version:
+ type: version
+ base-system-software:
+ type: string
+ jdm-vcpu:
+ type: integer
+ jdm-memory:
+ type: integer
+ description: GB
+ jdm-disk:
+ type: integer
+ description: GB
+ jdm-version:
+ type: string
+ jcp-vcpu:
+ type: integer
+ jcp-memory:
+ type: integer
+ description: GB
+ jcp-disk:
+ type: integer
+ description: GB
+ jcp-version:
+ type: version
+ capabilities:
+ vnf_hosting:
+ type: tosca.capabilities.Container
+ description: Provides hosting capability for VNFs
+ WAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Wan]
+ description: external WAN1 n/w interface
+ occurrences: [1,2]
+ LAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Lan]
+ description: external LAN n/w interface
+ occurrences: [1,8] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/CPWithAttributes.yml b/ui-ci/src/main/resources/Files/CPWithAttributes.yml
new file mode 100644
index 0000000000..847181b5c9
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/CPWithAttributes.yml
@@ -0,0 +1,78 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.CP:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-ucpe-part-number:
+ type: string
+ vendor-name:
+ type: string
+ required: true
+ vendor-model:
+ type: string
+ required: true
+ total-vcpu:
+ type: integer
+ description: number of vCPUs
+ total-memory:
+ type: integer
+ description: GB
+ total-disk:
+ type: integer
+ description: GB
+ base-system-image-file-name:
+ type: string
+ linux-host-vendor:
+ type: string
+ linux-host-os-version:
+ type: version
+ base-system-software:
+ type: string
+ jdm-vcpu:
+ type: integer
+ jdm-memory:
+ type: integer
+ description: GB
+ jdm-disk:
+ type: integer
+ description: GB
+ jdm-version:
+ type: string
+ jcp-vcpu:
+ type: integer
+ jcp-memory:
+ type: integer
+ description: GB
+ jcp-disk:
+ type: integer
+ description: GB
+ jcp-version:
+ type: version
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ capabilities:
+ vnf_hosting:
+ type: tosca.capabilities.Container
+ description: Provides hosting capability for VNFs
+ WAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Wan]
+ description: external WAN1 n/w interface
+ occurrences: [1,2]
+ LAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Lan]
+ description: external LAN n/w interface
+ occurrences: [1,8] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/CP_LAN - Copy.yml b/ui-ci/src/main/resources/Files/CP_LAN - Copy.yml
new file mode 100644
index 0000000000..5663168e4a
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/CP_LAN - Copy.yml
@@ -0,0 +1,12 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.uCPE: derived_from: tosca.nodes.Root
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
diff --git a/ui-ci/src/main/resources/Files/CP_LAN.yml b/ui-ci/src/main/resources/Files/CP_LAN.yml
new file mode 100644
index 0000000000..3420a3cfcb
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/CP_LAN.yml
@@ -0,0 +1,19 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp.LAN:
+ derived_from: org.openecomp.resource.cp.CP
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink_in:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualbinding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/CP_WAN.yml b/ui-ci/src/main/resources/Files/CP_WAN.yml
new file mode 100644
index 0000000000..9f21b902d9
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/CP_WAN.yml
@@ -0,0 +1,19 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.cp1.WAN:
+ derived_from: org.openecomp.resource.cp.CP
+ properties:
+ type:
+ type: string
+ required: false
+ requirements:
+ - virtualLink_in:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualLink_out:
+ capability: tosca.capabilities.network.Linkable
+ relationship: tosca.relationships.network.LinksTo
+ - virtualbinding:
+ capability: tosca.capabilities.network.Bindable
+ relationship: tosca.relationships.network.BindsTo \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/DNSscaling12.8.16.zip b/ui-ci/src/main/resources/Files/DNSscaling12.8.16.zip
new file mode 100644
index 0000000000..cadb6138e7
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/DNSscaling12.8.16.zip
Binary files differ
diff --git a/ui-ci/src/main/resources/Files/Heat-File 1.yaml b/ui-ci/src/main/resources/Files/Heat-File 1.yaml
new file mode 100644
index 0000000000..d332078d35
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Heat-File 1.yaml
@@ -0,0 +1,791 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+ be0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/Heat-File 2.yaml b/ui-ci/src/main/resources/Files/Heat-File 2.yaml
new file mode 100644
index 0000000000..d332078d35
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Heat-File 2.yaml
@@ -0,0 +1,791 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+ be0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/Heat-File.yaml b/ui-ci/src/main/resources/Files/Heat-File.yaml
new file mode 100644
index 0000000000..d332078d35
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Heat-File.yaml
@@ -0,0 +1,791 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+ be0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/InValid_tosca_File .yml b/ui-ci/src/main/resources/Files/InValid_tosca_File .yml
new file mode 100644
index 0000000000..4eea0a15ac
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/InValid_tosca_File .yml
@@ -0,0 +1,34 @@
+node_types:
+ org.openecomp.resource.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/ui-ci/src/main/resources/Files/JDM_vf.yml b/ui-ci/src/main/resources/Files/JDM_vf.yml
new file mode 100644
index 0000000000..5a7edd4aaf
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/JDM_vf.yml
@@ -0,0 +1,57 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vf.JDM:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/JDM_vfc.yml b/ui-ci/src/main/resources/Files/JDM_vfc.yml
new file mode 100644
index 0000000000..b9c9ca0c4a
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/JDM_vfc.yml
@@ -0,0 +1,57 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.JDM:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/Sample_CSAR.csar b/ui-ci/src/main/resources/Files/Sample_CSAR.csar
new file mode 100644
index 0000000000..3001fe8222
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Sample_CSAR.csar
Binary files differ
diff --git a/ui-ci/src/main/resources/Files/UCPE_VFC.yml b/ui-ci/src/main/resources/Files/UCPE_VFC.yml
new file mode 100644
index 0000000000..ef3966b68f
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/UCPE_VFC.yml
@@ -0,0 +1,65 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.uCPE:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-ucpe-part-number:
+ type: string
+ vendor-name:
+ type: string
+ required: true
+ vendor-model:
+ type: string
+ required: true
+ total-vcpu:
+ type: integer
+ description: number of vCPUs
+ total-memory:
+ type: integer
+ description: GB
+ total-disk:
+ type: integer
+ description: GB
+ base-system-image-file-name:
+ type: string
+ linux-host-vendor:
+ type: string
+ linux-host-os-version:
+ type: version
+ base-system-software:
+ type: string
+ jdm-vcpu:
+ type: integer
+ jdm-memory:
+ type: integer
+ description: GB
+ jdm-disk:
+ type: integer
+ description: GB
+ jdm-version:
+ type: string
+ jcp-vcpu:
+ type: integer
+ jcp-memory:
+ type: integer
+ description: GB
+ jcp-disk:
+ type: integer
+ description: GB
+ jcp-version:
+ type: version
+ capabilities:
+ vnf_hosting:
+ type: tosca.capabilities.Container
+ description: Provides hosting capability for VNFs
+ WAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Wan]
+ description: external WAN1 n/w interface
+ occurrences: [1,2]
+ LAN_connectivity:
+ type: tosca.capabilities.network.Bindable
+ valid_source_types: [org.openecomp.cp.Lan]
+ description: external LAN n/w interface
+ occurrences: [1,8] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/VF.yml b/ui-ci/src/main/resources/Files/VF.yml
new file mode 100644
index 0000000000..ec089900ad
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/VF.yml
@@ -0,0 +1,17 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vf.VFF:
+ derived_from: tosca.nodes.Root
+ properties:
+ vendor:
+ type: string
+ required: false
+ vl_name:
+ type: string
+ required: false
+ capabilities:
+ virtual_linkable:
+ type: tosca.capabilities.network.Linkable
+
+ \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/VFC.yml b/ui-ci/src/main/resources/Files/VFC.yml
new file mode 100644
index 0000000000..853ed35374
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/VFC.yml
@@ -0,0 +1,77 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vfc.vRouter:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ management-v6-address:
+ type: string
+ nm-lan-v6-address:
+ type: string
+ nm-lan-v6-prefix-length:
+ type: string
+ management-v4-address:
+ type: string
+ nm-lan-v4-address:
+ type: string
+ nm-lan-v4-prefix-length:
+ type: string
+ routing-instance-name:
+ type: string
+ routing-instances:
+ type: map
+ entry_schema:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable
+ occurrences: [1,UNBOUNDED]
+ \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/VL.yml b/ui-ci/src/main/resources/Files/VL.yml
new file mode 100644
index 0000000000..72e6d2be32
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/VL.yml
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+ org.openecomp.resource.vl.VL00:
+ derived_from: tosca.nodes.network.Network
+ properties:
+ vendor:
+ type: string
+ required: false
+ vl_name:
+ type: string
+ required: false
+
+ capabilities:
+ virtual_linkable:
+ type: tosca.capabilities.network.Linkable
+ end_point:
+ type: tosca.capabilities.Endpoint \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/Valid xml.xml b/ui-ci/src/main/resources/Files/Valid xml.xml
new file mode 100644
index 0000000000..0d67e48340
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Valid xml.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<dirs>
+<entry loc="C:\Program Files\Java\jdk1.7.0_79" stamp="1449076618518"/>
+</dirs>
diff --git a/ui-ci/src/main/resources/Files/Valid_tosca_Mycompute.yml b/ui-ci/src/main/resources/Files/Valid_tosca_Mycompute.yml
new file mode 100644
index 0000000000..8fac5e16a8
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Valid_tosca_Mycompute.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.vf.Database:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/ui-ci/src/main/resources/Files/Valid_tosca_ReplaceTest.yml b/ui-ci/src/main/resources/Files/Valid_tosca_ReplaceTest.yml
new file mode 100644
index 0000000000..90e771dab1
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/Valid_tosca_ReplaceTest.yml
@@ -0,0 +1,35 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+node_types:
+ org.openecomp.resource.VF.MyCompute:
+ derived_from: tosca.nodes.Root
+ attributes:
+ private_address:
+ type: string
+ public_address:
+ type: string
+ networks:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.NetworkInfo
+ ports:
+ type: map
+ entry_schema:
+ type: tosca.datatypes.network.PortInfo
+ requirements:
+ - local_storage:
+ capability: tosca.capabilities.Attachment
+ node: tosca.nodes.BlockStorage
+ relationship: tosca.relationships.AttachesTo
+ occurrences: [0, UNBOUNDED]
+ capabilities:
+ host:
+ type: tosca.capabilities.Container
+ valid_source_types: [tosca.nodes.SoftwareComponent]
+ endpoint :
+ type: tosca.capabilities.Endpoint.Admin
+ os:
+ type: tosca.capabilities.OperatingSystem
+ scalable:
+ type: tosca.capabilities.Scalable
+ binding:
+ type: tosca.capabilities.network.Bindable
diff --git a/ui-ci/src/main/resources/Files/asc_heat 0 2.yaml b/ui-ci/src/main/resources/Files/asc_heat 0 2.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/asc_heat 0 2.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env b/ui-ci/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env
new file mode 100644
index 0000000000..b494d8c270
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/hot-nimbus-oam-volumes_v0.3.env
@@ -0,0 +1,6 @@
+parameters:
+ pcrf_oam_vol_size: 500
+ pcrf_oam_volume_silver-1: Silver
+ pcrf_oam_volume_silver-2: Silver
+ pcrf_oam_vol_name_1: sde1-pcrfx01-oam001-vol-1
+ pcrf_oam_vol_name_2: sde1-pcrfx01-oam001-vol-2 \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.env b/ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.env
new file mode 100644
index 0000000000..cf7cf710ce
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.env
@@ -0,0 +1,18 @@
+parameters:
+ pcrf_oam_server_names: ZRDM1PCRF01OAM001,ZRDM1PCRF01OAM002
+ pcrf_oam_image_name: PCRF_8.995-ATTM1.0.3.qcow2
+ pcrf_oam_flavor_name: lc.4xlarge4
+ availabilityzone_name: nova
+ pcrf_cps_net_name: int_pcrf_net_0
+ pcrf_cps_net_ips: 172.26.16.111,172.26.16.112
+ pcrf_arbiter_vip: 172.26.16.115
+ pcrf_cps_net_mask: 255.255.255.0
+ pcrf_oam_net_name: oam_protected_net_0
+ pcrf_oam_net_ips: 107.239.64.117,107.239.64.118
+ pcrf_oam_net_gw: 107.239.64.1
+ pcrf_oam_net_mask: 255.255.248.0
+ pcrf_oam_volume_id_1: a4aa05fb-fcdc-457b-8077-6845fdfc3257
+ pcrf_oam_volume_id_2: 93d8fc1f-f1c3-4933-86b2-039881ee910f
+ pcrf_security_group_name: nimbus_security_group
+ pcrf_vnf_id: 730797234b4a40aa99335157b02871cd
+
diff --git a/ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml b/ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml
new file mode 100644
index 0000000000..6636eba210
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/hot-nimbus-oam_v0.6.yaml
@@ -0,0 +1,108 @@
+heat_template_version: 2013-05-23
+
+description: heat template that creates multiple PCRF OAM nodes stack
+
+parameters:
+ pcrf_oam_server_names:
+ type: comma_delimited_list
+ label: PCRF OAM server names
+ description: name of the PCRF OAM instance
+ pcrf_oam_image_name:
+ type: string
+ label: PCRF OAM image name
+ description: PCRF OAM image name
+ pcrf_oam_flavor_name:
+ type: string
+ label: PCRF OAM flavor name
+ description: flavor name of PCRF OAM instance
+ availabilityzone_name:
+ type: string
+ label: availabilityzone name
+ description: availabilityzone name
+ pcrf_cps_net_name:
+ type: string
+ label: CPS network name
+ description: CPS network name
+ pcrf_cps_net_ips:
+ type: comma_delimited_list
+ label: CPS network ips
+ description: CPS network ips
+ pcrf_cps_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_arbiter_vip:
+ type: string
+ label: OAM Arbiter LB VIP
+ description: OAM Arbiter LB VIP
+ pcrf_oam_net_name:
+ type: string
+ label: OAM network name
+ description: OAM network name
+ pcrf_oam_net_ips:
+ type: comma_delimited_list
+ label: OAM network ips
+ description: OAM network ips
+ pcrf_oam_net_gw:
+ type: string
+ label: CPS network gateway
+ description: CPS network gateway
+ pcrf_oam_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_oam_volume_id_1:
+ type: string
+ label: CPS OAM 001 Cinder Volume
+ description: CPS OAM 001 Cinder Volumes
+ pcrf_oam_volume_id_2:
+ type: string
+ label: CPS OAM 002 Cinder Volume
+ description: CPS OAM 002 Cinder Volumes
+ pcrf_security_group_name:
+ type: string
+ label: security group name
+ description: the name of security group
+ pcrf_vnf_id:
+ type: string
+ label: PCRF VNF Id
+ description: PCRF VNF Id
+
+resources:
+ server_pcrf_oam_001:
+ type: nested-oam_v0.2.yaml
+ properties:
+ pcrf_oam_server_name: { get_param: [pcrf_oam_server_names, 0] }
+ pcrf_oam_image_name: { get_param: pcrf_oam_image_name }
+ pcrf_oam_flavor_name: { get_param: pcrf_oam_flavor_name }
+ availabilityzone_name: { get_param: availabilityzone_name }
+ pcrf_security_group_name: { get_param: pcrf_security_group_name }
+ pcrf_oam_volume_id: { get_param: pcrf_oam_volume_id_1 }
+ pcrf_cps_net_name: { get_param: pcrf_cps_net_name }
+ pcrf_cps_net_ip: { get_param: [pcrf_cps_net_ips, 0] }
+ pcrf_cps_net_mask: { get_param: pcrf_cps_net_mask }
+ pcrf_oam_net_name: { get_param: pcrf_oam_net_name }
+ pcrf_oam_net_ip: { get_param: [pcrf_oam_net_ips, 0] }
+ pcrf_oam_net_mask: { get_param: pcrf_oam_net_mask }
+ pcrf_oam_net_gw: { get_param: pcrf_oam_net_gw }
+ pcrf_arbiter_vip: { get_param: pcrf_arbiter_vip }
+ pcrf_vnf_id: {get_param: pcrf_vnf_id}
+
+ server_pcrf_oam_002:
+ type: nested-oam_v0.2.yaml
+ properties:
+ pcrf_oam_server_name: { get_param: [pcrf_oam_server_names, 1] }
+ pcrf_oam_image_name: { get_param: pcrf_oam_image_name }
+ pcrf_oam_flavor_name: { get_param: pcrf_oam_flavor_name }
+ availabilityzone_name: { get_param: availabilityzone_name }
+ pcrf_security_group_name: { get_param: pcrf_security_group_name }
+ pcrf_oam_volume_id: { get_param: pcrf_oam_volume_id_2 }
+ pcrf_cps_net_name: { get_param: pcrf_cps_net_name }
+ pcrf_cps_net_ip: { get_param: [pcrf_cps_net_ips, 1] }
+ pcrf_cps_net_mask: { get_param: pcrf_cps_net_mask }
+ pcrf_oam_net_name: { get_param: pcrf_oam_net_name }
+ pcrf_oam_net_ip: { get_param: [pcrf_oam_net_ips, 1] }
+ pcrf_oam_net_mask: { get_param: pcrf_oam_net_mask }
+ pcrf_oam_net_gw: { get_param: pcrf_oam_net_gw }
+ pcrf_arbiter_vip: { get_param: pcrf_arbiter_vip }
+ pcrf_vnf_id: {get_param: pcrf_vnf_id}
diff --git a/ui-ci/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml b/ui-ci/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml
new file mode 100644
index 0000000000..564104174a
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/hot-nimbus-pcm_v0.6.yaml
@@ -0,0 +1,80 @@
+heat_template_version: 2013-05-23
+
+description: heat template that creates PCRF Cluman stack
+
+parameters:
+ pcrf_pcm_server_names:
+ type: comma_delimited_list
+ label: PCRF CM server names
+ description: name of the PCRF CM instance
+ pcrf_pcm_image_name:
+ type: string
+ label: PCRF CM image name
+ description: PCRF CM image name
+ pcrf_pcm_flavor_name:
+ type: string
+ label: PCRF CM flavor name
+ description: flavor name of PCRF CM instance
+ availabilityzone_name:
+ type: string
+ label: availabilityzone name
+ description: availabilityzone name
+ pcrf_cps_net_name:
+ type: string
+ label: CPS network name
+ description: CPS network name
+ pcrf_cps_net_ips:
+ type: comma_delimited_list
+ label: CPS network ips
+ description: CPS network ips
+ pcrf_cps_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_oam_net_name:
+ type: string
+ label: OAM network name
+ description: OAM network name
+ pcrf_oam_net_ips:
+ type: comma_delimited_list
+ label: OAM network ips
+ description: OAM network ips
+ pcrf_oam_net_gw:
+ type: string
+ label: CPS network gateway
+ description: CPS network gateway
+ pcrf_oam_net_mask:
+ type: string
+ label: CPS network mask
+ description: CPS network mask
+ pcrf_pcm_volume_id_1:
+ type: string
+ label: CPS Cluman Cinder Volume
+ description: CPS Cluman Cinder Volume
+ pcrf_security_group_name:
+ type: string
+ label: security group name
+ description: the name of security group
+ pcrf_vnf_id:
+ type: string
+ label: PCRF VNF Id
+ description: PCRF VNF Id
+
+resources:
+ server_pcrf_pcm_001:
+ type: nested-pcm_v0.2.yaml
+ properties:
+ pcrf_pcm_server_name: { get_param: [pcrf_pcm_server_names, 0] }
+ pcrf_pcm_image_name: { get_param: pcrf_pcm_image_name }
+ pcrf_pcm_flavor_name: { get_param: pcrf_pcm_flavor_name }
+ availabilityzone_name: { get_param: availabilityzone_name }
+ pcrf_security_group_name: { get_param: pcrf_security_group_name }
+ pcrf_pcm_volume_id: { get_param: pcrf_pcm_volume_id_1 }
+ pcrf_cps_net_name: { get_param: pcrf_cps_net_name }
+ pcrf_cps_net_ip: { get_param: [pcrf_cps_net_ips, 0] }
+ pcrf_cps_net_mask: { get_param: pcrf_cps_net_mask }
+ pcrf_oam_net_name: { get_param: pcrf_oam_net_name }
+ pcrf_oam_net_ip: { get_param: [pcrf_oam_net_ips, 0] }
+ pcrf_oam_net_mask: { get_param: pcrf_oam_net_mask }
+ pcrf_oam_net_gw: { get_param: pcrf_oam_net_gw }
+ pcrf_vnf_id: {get_param: pcrf_vnf_id}
diff --git a/ui-ci/src/main/resources/Files/sample-xml-alldata-1-1.xml b/ui-ci/src/main/resources/Files/sample-xml-alldata-1-1.xml
new file mode 100644
index 0000000000..1723f6b312
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/sample-xml-alldata-1-1.xml
@@ -0,0 +1,298 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<ns2:vnf-catalog xmlns:ns2="http://com/att/co/db-definition">
+ <ns2:part-number-list>
+ <ns2:part-number>FG-VM00*</ns2:part-number>
+ <ns2:vnf-type>FW</ns2:vnf-type>
+ <ns2:vendor-info>
+ <ns2:vendor-name>FORTINET</ns2:vendor-name>
+ <ns2:vendor-part-number>FG-VM00*</ns2:vendor-part-number>
+ <ns2:vendor-model>VM00</ns2:vendor-model>
+ </ns2:vendor-info>
+ <ns2:vcpu>
+ <ns2:vcpu-default>1</ns2:vcpu-default>
+ <ns2:vcpu-min>1</ns2:vcpu-min>
+ <ns2:vcpu-max>1</ns2:vcpu-max>
+ </ns2:vcpu>
+ <ns2:vmemory>
+ <ns2:vmemory-default>1</ns2:vmemory-default>
+ <ns2:vmemory-units>GB</ns2:vmemory-units>
+ <ns2:vmemory-min>1</ns2:vmemory-min>
+ <ns2:vmemory-max>1</ns2:vmemory-max>
+ </ns2:vmemory>
+ <ns2:vdisk>
+ <ns2:vdisk-default>20</ns2:vdisk-default>
+ <ns2:vdisk-units>GB</ns2:vdisk-units>
+ <ns2:vdisk-min>20</ns2:vdisk-min>
+ <ns2:vdisk-max>20</ns2:vdisk-max>
+ </ns2:vdisk>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filename>fg-5.2.4.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.3</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filename>fg-5.2.3.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.2</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filename>fg-5.2.2.qcow</software-filename >
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <supported-hardware-list>
+ <ucpe-model>U401</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U410</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U412</ucpe-model >
+ </supported-hardware-list>
+ </ns2:part-number-list>
+ <ns2:part-number-list>
+ <ns2:part-number>FG-VM01*</ns2:part-number>
+ <ns2:vnf-type>FW</ns2:vnf-type>
+ <ns2:vendor-info>
+ <ns2:vendor-name> FORTINET</ns2:vendor-name>
+ <ns2:vendor-part-number>FG-VM01*</ns2:vendor-part-number>
+ <ns2:vendor-model>VM01</ns2:vendor-model>
+ </ns2:vendor-info>
+ <ns2:vcpu>
+ <ns2:vcpu-default>1</ns2:vcpu-default>
+ <ns2:vcpu-min>1</ns2:vcpu-min>
+ <ns2:vcpu-max>1</ns2:vcpu-max>
+ </ns2:vcpu>
+ <ns2:vmemory>
+ <ns2:vmemory-default>2</ns2:vmemory-default>
+ <ns2:vmemory-units>GB</ns2:vmemory-units>
+ <ns2:vmemory-min>1</ns2:vmemory-min>
+ <ns2:vmemory-max>2</ns2:vmemory-max>
+ </ns2:vmemory>
+ <ns2:vdisk>
+ <ns2:vdisk-default>20</ns2:vdisk-default>
+ <ns2:vdisk-units>GB</ns2:vdisk-units>
+ <ns2:vdisk-min>20</ns2:vdisk-min>
+ <ns2:vdisk-max>20</ns2:vdisk-max>
+ </ns2:vdisk>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filename>fg-5.2.4.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.3</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filename>fg-5.2.3.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.2</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filename>fg-5.2.2.qcow</software-filename >
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <supported-hardware-list>
+ <ucpe-model>U401</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U410</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U412</ucpe-model >
+ </supported-hardware-list>
+ </ns2:part-number-list>
+ <ns2:part-number-list>
+ <ns2:part-number>FG-VM02*</ns2:part-number>
+ <ns2:vnf-type>FW</ns2:vnf-type>
+ <ns2:vendor-info>
+ <ns2:vendor-name> FORTINET</ns2:vendor-name>
+ <ns2:vendor-part-number>FG-VM02*</ns2:vendor-part-number>
+ <ns2:vendor-model>VM02</ns2:vendor-model>
+ </ns2:vendor-info>
+ <ns2:vcpu>
+ <ns2:vcpu-default>2</ns2:vcpu-default>
+ <ns2:vcpu-min>1</ns2:vcpu-min>
+ <ns2:vcpu-max>2</ns2:vcpu-max>
+ </ns2:vcpu>
+ <ns2:vmemory>
+ <ns2:vmemory-default>4</ns2:vmemory-default>
+ <ns2:vmemory-units>GB</ns2:vmemory-units>
+ <ns2:vmemory-min>1</ns2:vmemory-min>
+ <ns2:vmemory-max>4</ns2:vmemory-max>
+ </ns2:vmemory>
+ <ns2:vdisk>
+ <ns2:vdisk-default>20</ns2:vdisk-default>
+ <ns2:vdisk-units>GB</ns2:vdisk-units>
+ <ns2:vdisk-min>20</ns2:vdisk-min>
+ <ns2:vdisk-max>20</ns2:vdisk-max>
+ </ns2:vdisk>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filename>fg-5.2.4.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.3</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filename>fg-5.2.3.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.2</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filename>fg-5.2.2.qcow</software-filename >
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <supported-hardware-list>
+ <ucpe-model>U401</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U410</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U412</ucpe-model >
+ </supported-hardware-list>
+ </ns2:part-number-list>
+ <ns2:part-number-list>
+ <ns2:part-number>FG-VM04*</ns2:part-number>
+ <ns2:vnf-type>FW</ns2:vnf-type>
+ <ns2:vendor-info>
+ <ns2:vendor-name>FORTINET</ns2:vendor-name>
+ <ns2:vendor-part-number>FG-VM04*</ns2:vendor-part-number>
+ <ns2:vendor-model>VM04</ns2:vendor-model>
+ </ns2:vendor-info>
+ <ns2:vcpu>
+ <ns2:vcpu-default>4</ns2:vcpu-default>
+ <ns2:vcpu-min>1</ns2:vcpu-min>
+ <ns2:vcpu-max>4</ns2:vcpu-max>
+ </ns2:vcpu>
+ <ns2:vmemory>
+ <ns2:vmemory-default>6</ns2:vmemory-default>
+ <ns2:vmemory-units>GB</ns2:vmemory-units>
+ <ns2:vmemory-min>1</ns2:vmemory-min>
+ <ns2:vmemory-max>6</ns2:vmemory-max>
+ </ns2:vmemory>
+ <ns2:vdisk>
+ <ns2:vdisk-default>20</ns2:vdisk-default>
+ <ns2:vdisk-units>GB</ns2:vdisk-units>
+ <ns2:vdisk-min>20</ns2:vdisk-min>
+ <ns2:vdisk-max>20</ns2:vdisk-max>
+ </ns2:vdisk>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filename>fg-5.2.4.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.3</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filename>fg-5.2.3.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.2</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filename>fg-5.2.2.qcow</software-filename >
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <supported-hardware-list>
+ <ucpe-model>U401</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U410</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U412</ucpe-model >
+ </supported-hardware-list>
+ </ns2:part-number-list>
+ <ns2:part-number-list>
+ <ns2:part-number>FG-VM08*</ns2:part-number>
+ <ns2:vnf-type>FW</ns2:vnf-type>
+ <ns2:vendor-info>
+ <ns2:vendor-name>FORTINET</ns2:vendor-name>
+ <ns2:vendor-part-number>FG-VM08*</ns2:vendor-part-number>
+ <ns2:vendor-model>VM08</ns2:vendor-model>
+ </ns2:vendor-info>
+ <ns2:vcpu>
+ <ns2:vcpu-default>8</ns2:vcpu-default>
+ <ns2:vcpu-min>1</ns2:vcpu-min>
+ <ns2:vcpu-max>8</ns2:vcpu-max>
+ </ns2:vcpu>
+ <ns2:vmemory>
+ <ns2:vmemory-default>12</ns2:vmemory-default>
+ <ns2:vmemory-units>GB</ns2:vmemory-units>
+ <ns2:vmemory-min>1</ns2:vmemory-min>
+ <ns2:vmemory-max>12</ns2:vmemory-max>
+ </ns2:vmemory>
+ <ns2:vdisk>
+ <ns2:vdisk-default>20</ns2:vdisk-default>
+ <ns2:vdisk-units>GB</ns2:vdisk-units>
+ <ns2:vdisk-min>20</ns2:vdisk-min>
+ <ns2:vdisk-max>20</ns2:vdisk-max>
+ </ns2:vdisk>
+ <software-version-list>
+ <software-version>5.2.4</software-version>
+ <software-version-state>0</software-version-state>
+ <software-filename>fg-5.2.4.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.3</software-version>
+ <software-version-state>1</software-version-state>
+ <software-filename>fg-5.2.3.qcow</software-filename >
+ </software-version-list>
+ <software-version-list>
+ <software-version>5.2.2</software-version>
+ <software-version-state>2</software-version-state>
+ <software-filename>fg-5.2.2.qcow</software-filename >
+ </software-version-list>
+ <vnf-features-list>
+ <vnf-feature>IPS-IDS</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>URLF</vnf-feature>
+ </vnf-features-list>
+ <vnf-features-list>
+ <vnf-feature>Anti-Virus</vnf-feature>
+ </vnf-features-list>
+ <supported-hardware-list>
+ <ucpe-model>U401</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U410</ucpe-model >
+ </supported-hardware-list>
+ <supported-hardware-list>
+ <ucpe-model>U412</ucpe-model >
+ </supported-hardware-list>
+ </ns2:part-number-list>
+</ns2:vnf-catalog> \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/vFW_8.12.16.zip b/ui-ci/src/main/resources/Files/vFW_8.12.16.zip
new file mode 100644
index 0000000000..006c56ca4b
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/vFW_8.12.16.zip
Binary files differ
diff --git a/ui-ci/src/main/resources/Files/vFW_VF.yml b/ui-ci/src/main/resources/Files/vFW_VF.yml
new file mode 100644
index 0000000000..100883e399
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/vFW_VF.yml
@@ -0,0 +1,58 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+
+ org.openecomp.resource.vf.vFW:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: version
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/vFW_VFC.yml b/ui-ci/src/main/resources/Files/vFW_VFC.yml
new file mode 100644
index 0000000000..d0814c43aa
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/vFW_VFC.yml
@@ -0,0 +1,58 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+node_types:
+
+ org.openecomp.resource.vfc.vFW:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: version
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/vLB12.8.16.zip b/ui-ci/src/main/resources/Files/vLB12.8.16.zip
new file mode 100644
index 0000000000..2f61e5ac40
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/vLB12.8.16.zip
Binary files differ
diff --git a/ui-ci/src/main/resources/Files/vRouter_vfc.yml b/ui-ci/src/main/resources/Files/vRouter_vfc.yml
new file mode 100644
index 0000000000..95ffe959de
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/vRouter_vfc.yml
@@ -0,0 +1,78 @@
+tosca_definitions_version: tosca_simple_yaml_1_0_0
+
+
+node_types:
+ org.openecomp.resource.vfc.vRouter:
+ derived_from: tosca.nodes.Root
+ properties:
+ att-part-number:
+ type: string
+ vendor-name:
+ type: string
+ vendor-part-number:
+ type: string
+ vendor-model:
+ type: string
+ vendor-model-description:
+ type: string
+ vcpu-default:
+ type: integer
+ vcpu-min:
+ type: integer
+ vcpu-max:
+ type: integer
+ vmemory-default:
+ type: integer
+ vmemory-units:
+ type: string
+ default: "GB"
+ vmemory-min:
+ type: integer
+ vmemory-max:
+ type: integer
+ vdisk-default:
+ type: integer
+ vdisk-units:
+ type: string
+ default: "GB"
+ vdisk-min:
+ type: integer
+ vdisk-max:
+ type: integer
+ vnf-type:
+ type: string
+ software-version:
+ type: string
+ software-version-state:
+ type: integer
+ software-file-name:
+ type: string
+ vnf-feature:
+ type: string
+ management-v6-address:
+ type: string
+ nm-lan-v6-address:
+ type: string
+ nm-lan-v6-prefix-length:
+ type: string
+ management-v4-address:
+ type: string
+ nm-lan-v4-address:
+ type: string
+ nm-lan-v4-prefix-length:
+ type: string
+ routing-instance-name:
+ type: string
+ routing-instances:
+ type: map
+ entry_schema:
+ type: string
+ requirements:
+ - host:
+ capability: tosca.capabilities.Container
+ relationship: tosca.relationships.HostedOn
+ capabilities:
+ binding:
+ type: tosca.capabilities.network.Bindable
+ occurrences: [1,UNBOUNDED]
+ \ No newline at end of file
diff --git a/ui-ci/src/main/resources/Files/valid HEAT_ENV files.env b/ui-ci/src/main/resources/Files/valid HEAT_ENV files.env
new file mode 100644
index 0000000000..e576c0f67d
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/valid HEAT_ENV files.env
@@ -0,0 +1,54 @@
+parameters:
+ flavor_scp_be_id: m1.small
+ flavor_scp_fe_id: m1.small
+ flavor_smp_id: m1.small
+ flavor_db_id: m1.small
+ image_scp_be_id: CCLINUX
+ image_scp_fe_id: CCLINUX
+ image_smp_id: CCLINUX
+ image_db_id: CCLINUX
+
+ int_vscp_fe_cluster_net_id: int_vscp_fe_cluster_net
+ int_vscp_fe_cluster_cidr: 172.26.2.0/24
+ int_vscp_cluster_net_id: int_vscp_cluster_net
+ int_vscp_cluster_cidr: 172.26.3.0/24
+ int_vscp_db_network_net_id: int_vscp_db_network_net
+ int_vscp_db_network_cidr: 172.26.1.0/24
+
+ SIGNET_vrf_A1_direct_net_id: SIGNET_vrf_A1_direct_net
+ SIGNET_vrf_B1_direct_net_id: SIGNET_vrf_B1_direct_net
+ Cricket_OCS_protected_net_id: Cricket_OCS_protected_net
+# OAM_direct_net_id: OAM_net
+# OAM_direct_net_id: oam-direct-net
+ OAM_direct_net_id: Marks_OAM_direct_net
+
+ be0_Cricket_OCS_protected_ips: 107.239.15.17
+ be1_Cricket_OCS_protected_ips: 107.239.15.18
+ be2_Cricket_OCS_protected_ips: 107.239.15.19
+ be3_Cricket_OCS_protected_ips: 107.239.15.20
+ be4_Cricket_OCS_protected_ips: 107.239.15.21
+ be0_OAM_direct_ips: 10.250.10.33
+ be1_OAM_direct_ips: 10.250.10.34
+ be2_OAM_direct_ips: 10.250.10.35
+ be3_OAM_direct_ips: 10.250.10.36
+ be4_OAM_direct_ips: 10.250.10.37
+ fe0_SIGNET_vrf_A1_direct_ips: 172.26.4.1
+ fe0_OAM_direct_ips: 10.250.10.38
+ fe1_SIGNET_vrf_B1_direct_ips: 172.26.4.5
+ fe1_OAM_direct_ips: 10.250.10.39
+ smp0_OAM_direct_ips: 10.250.10.40
+ smp1_OAM_direct_ips: 10.250.10.41
+ db0_OAM_direct_ips: 10.250.10.42
+ db1_OAM_direct_ips: 10.250.10.43
+
+ vm_scp_be0_name: vSCP_BE0
+ vm_scp_be1_name: vSCP_BE1
+ vm_scp_be2_name: vSCP_BE2
+ vm_scp_be3_name: vSCP_BE3
+ vm_scp_be4_name: vSCP_BE4
+ vm_scp_fe0_name: vSCP_FE0
+ vm_scp_fe1_name: vSCP_FE1
+ vm_smp0_name: vSMP0
+ vm_smp1_name: vSMP1
+ vm_db0_name: vDB0
+ vm_db1_name: vDB1
diff --git a/ui-ci/src/main/resources/Files/validHEATfiles.yaml b/ui-ci/src/main/resources/Files/validHEATfiles.yaml
new file mode 100644
index 0000000000..6835485ca1
--- /dev/null
+++ b/ui-ci/src/main/resources/Files/validHEATfiles.yaml
@@ -0,0 +1,787 @@
+heat_template_version: 2013-05-23
+#################################
+#
+# Changes in v0.2:
+# - Unique availability zone for each VM
+# - LAN8 and SLAN networks removed according to latest Prod/Type I diagram
+# - 2 DB VMs added
+# - Images corrected
+# - VM start-up order: SMP->DB->BE->FE (no error handling yet)
+# - Provisioning scripts placeholders
+#
+#################################
+
+description: ASC Template
+
+parameters:
+# availability_zone_smp0:
+# type: string
+# default: nova
+# availability_zone_smp1:
+# type: string
+# default: nova
+# availability_zone_fe0:
+# type: string
+# default: nova
+# availability_zone_fe1:
+# type: string
+# default: nova
+# availability_zone_db0:
+# type: string
+# default: nova
+# availability_zone_db1:
+# type: string
+# default: nova
+# availability_zone_be0:
+# type: string
+# default: nova
+# availability_zone_be1:
+# type: string
+# default: nova
+# availability_zone_be2:
+# type: string
+# default: nova
+# availability_zone_be3:
+# type: string
+# default: nova
+# availability_zone_be4:
+# type: string
+# default: nova
+
+ vnf_name:
+ type: string
+ description: Unique name for this VNF instance
+ default: This_is_the_SCP_name
+ vnf_id:
+ type: string
+ description: Unique ID for this VNF instance
+ default: This_is_ths_SCP_id
+
+ flavor_scp_be_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_scp_fe_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_smp_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ flavor_db_id:
+ type: string
+ description: flavor type
+ default: a1.Small
+ image_scp_be_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_be
+ image_scp_fe_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_fe
+ image_smp_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_smp
+ image_db_id:
+ type: string
+ description: Image use to boot a server
+ default: asc_base_image_db
+
+ int_vscp_fe_cluster_net_id:
+ type: string
+ description: LAN2 FE Cluster/KA
+ int_vscp_fe_cluster_cidr:
+ type: string
+ description: Private Network2 Address (CIDR notation)
+ int_vscp_cluster_net_id:
+ type: string
+ description: LAN3 Cluster
+ int_vscp_cluster_cidr:
+ type: string
+ description: Private Network3 Address (CIDR notation)
+ int_vscp_db_network_net_id:
+ type: string
+ description: LAN4 DB
+ int_vscp_db_network_cidr:
+ type: string
+ description: Private Network4 Address (CIDR notation)
+ SIGNET_vrf_A1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_A
+ SIGNET_vrf_B1_direct_net_id:
+ type: string
+ description: Network name for SIGTRAN_B
+ Cricket_OCS_protected_net_id:
+ type: string
+ description: Network name for CRICKET_OCS
+ OAM_direct_net_id:
+ type: string
+ description: Network name for OAM
+ be0_Cricket_OCS_protected_ips:
+ type: string
+ label: be0 port 5 OAM ip address
+ description: be0 port 5 OAM ip address
+ be1_Cricket_OCS_protected_ips:
+ type: string
+ label: be1 port 5 OAM ip address
+ description: be1 port 5 OAM ip address
+ be2_Cricket_OCS_protected_ips:
+ type: string
+ label: be2 port 5 OAM ip address
+ description: be2 port 5 OAM ip address
+ be3_Cricket_OCS_protected_ips:
+ type: string
+ label: be3 port 5 OAM ip address
+ description: be3 port 5 OAM ip address
+ be4_Cricket_OCS_protected_ips:
+ type: string
+ label: be4 port 5 OAM ip address
+ description: be4 port 5 OAM ip address
+ be0_OAM_direct_ips:
+ type: string
+ label: be0 port 7 OAM ip address
+ description: be0 port 7 OAM ip address
+ be1_OAM_direct_ips:
+ type: string
+ label: be1 port 7 OAM ip address
+ description: be1 port 7 OAM ip address
+ be2_OAM_direct_ips:
+ type: string
+ label: be2 port 7 OAM ip address
+ description: be2 port 7 OAM ip address
+ be3_OAM_direct_ips:
+ type: string
+ label: be3 port 7 OAM ip address
+ description: be3 port 7 OAM ip address
+ be4_OAM_direct_ips:
+ type: string
+ label: be4 port 7 OAM ip address
+ description: be4 port 7 OAM ip address
+ fe0_SIGNET_vrf_A1_direct_ips:
+ type: string
+ label: fe0 port 0 SIGTRAN ip address
+ description: fe0 port 0 SIGTRAN ip address
+ fe0_OAM_direct_ips:
+ type: string
+ label: fe0 port 7 OAM ip address
+ description: fe0 port 7 OAM ip address
+ fe1_SIGNET_vrf_B1_direct_ips:
+ type: string
+ label: fe1 port 1 SIGTRAN ip address
+ description: fe1 port 1 SIGTRAN ip address
+ fe1_OAM_direct_ips:
+ type: string
+ label: fe1 port 7 OAM ip address
+ description: fe1 port 7 OAM ip address
+ smp0_OAM_direct_ips:
+ type: string
+ label: smp0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ smp1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: smp1 port 7 OAM ip address
+ db0_OAM_direct_ips:
+ type: string
+ label: db0 port 7 OAM ip address
+ description: smp0 port 7 OAM ip address
+ db1_OAM_direct_ips:
+ type: string
+ label: smp1 port 7 OAM ip address
+ description: db1 port 7 OAM ip address
+ vm_scp_be0_name:
+ type: string
+ default: vSCP_BE0
+ description: name of VM
+ vm_scp_be1_name:
+ type: string
+ default: vSCP_BE1
+ description: name of VM
+ vm_scp_be2_name:
+ type: string
+ default: vSCP_BE2
+ description: name of VM
+ vm_scp_be3_name:
+ type: string
+ default: vSCP_BE3
+ description: name of VM
+ vm_scp_be4_name:
+ type: string
+ default: vSCP_BE4
+ description: name of VM
+ vm_scp_fe0_name:
+ type: string
+ default: vSCP_FE0
+ description: name of VM
+ vm_scp_fe1_name:
+ type: string
+ default: vSCP_FE1
+ description: name of VM
+ vm_smp0_name:
+ type: string
+ default: vSMP0
+ description: name of VM
+ vm_smp1_name:
+ type: string
+ default: vSMP1
+ description: name of VM
+ vm_db0_name:
+ type: string
+ default: vDB0
+ description: name of VM
+ vm_db1_name:
+ type: string
+ default: vDB1
+ description: name of VM
+
+resources:
+# scp_be_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_be_wait_handle }
+# count: 5
+# timeout: 300
+# scp_be_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# scp_fe_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: scp_fe_wait_handle }
+# count: 2
+# timeout: 300
+# scp_fe_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# smp_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: smp_wait_handle }
+# count: 2
+# timeout: 300
+# smp_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+#
+# db_wait_condition:
+# type: OS::Heat::WaitCondition
+# properties:
+# handle: { get_resource: db_wait_handle }
+# count: 2
+# timeout: 300
+# db_wait_handle:
+# type: OS::Heat::WaitConditionHandle
+
+ FE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ BE_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ SMP_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+ DB_Affinity:
+ type: OS::Nova::ServerGroup
+ properties:
+ policies: ["anti-affinity"]
+
+ FE_Clustering_KA:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_fe_cluster_net_id }
+
+ FE_Clustering_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+ cidr: { get_param: int_vscp_fe_cluster_cidr }
+
+ Clustering_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_cluster_net_id }
+
+ Clustering_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: Clustering_Network }
+ cidr: { get_param: int_vscp_cluster_cidr }
+
+ DB_Network:
+ type: OS::Contrail::VirtualNetwork
+ properties:
+ name: { get_param: int_vscp_db_network_net_id }
+
+ DB_Network_subnet:
+ type: OS::Neutron::Subnet
+ properties:
+ network_id: { get_resource: DB_Network }
+ cidr: { get_param: int_vscp_db_network_cidr }
+
+ server_scp_be0:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be0_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be0 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be0_port_3 }
+ - port: { get_resource: be0_port_4 }
+ - port: { get_resource: be0_port_5 }
+ - port: { get_resource: be0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be0_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be0_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_Cricket_OCS_protected_ips}}]
+
+ be0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be0_OAM_direct_ips}}]
+
+ server_scp_be1:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be1_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be1 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be1_port_3 }
+ - port: { get_resource: be1_port_4 }
+ - port: { get_resource: be1_port_5 }
+ - port: { get_resource: be1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be1_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be1_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_Cricket_OCS_protected_ips}}]
+
+ be1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be1_OAM_direct_ips}}]
+
+ server_scp_be2:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be2_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be2 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be2_port_3 }
+ - port: { get_resource: be2_port_4 }
+ - port: { get_resource: be2_port_5 }
+ - port: { get_resource: be2_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be2_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be2_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be2_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be2_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_Cricket_OCS_protected_ips}}]
+
+ be2_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be2_OAM_direct_ips}}]
+
+ server_scp_be3:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be3_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be3 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be3_port_3 }
+ - port: { get_resource: be3_port_4 }
+ - port: { get_resource: be3_port_5 }
+ - port: { get_resource: be3_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be3_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be3_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be3_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be3_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_Cricket_OCS_protected_ips}}]
+
+ be3_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be3_OAM_direct_ips}}]
+
+ server_scp_be4:
+ type: OS::Nova::Server
+# depends on: db_wait_condition
+ properties:
+ name: { get_param: vm_scp_be4_name }
+ image: { get_param: image_scp_be_id }
+# availability_zone: { get_param: availability_zone_be4 }
+ flavor: { get_param: flavor_scp_be_id }
+ scheduler_hints: { group: { get_resource: BE_Affinity } }
+ networks:
+ - port: { get_resource: be4_port_3 }
+ - port: { get_resource: be4_port_4 }
+ - port: { get_resource: be4_port_5 }
+ - port: { get_resource: be4_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_be4_name}
+# wc_notify: { get_attr: ['scp_be_wait_handle', 'curl_cli'] }
+
+ be4_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ be4_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ be4_port_5:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: Cricket_OCS_protected_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_Cricket_OCS_protected_ips}}]
+
+ be4_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: be4_OAM_direct_ips}}]
+
+ server_scp_fe0:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe0_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe0 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe0_port_0 }
+ - port: { get_resource: fe0_port_2 }
+ - port: { get_resource: fe0_port_3 }
+ - port: { get_resource: fe0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe0_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe0_port_0:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_A1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_SIGNET_vrf_A1_direct_ips}}]
+
+ fe0_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe0_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe0_OAM_direct_ips}}]
+
+ server_scp_fe1:
+ type: OS::Nova::Server
+# depends on: scp_be_wait_condition
+ properties:
+ name: { get_param: vm_scp_fe1_name }
+ image: { get_param: image_scp_fe_id }
+# availability_zone: { get_param: availability_zone_fe1 }
+ flavor: { get_param: flavor_scp_fe_id }
+ scheduler_hints: { group: { get_resource: FE_Affinity } }
+ networks:
+ - port: { get_resource: fe1_port_1 }
+ - port: { get_resource: fe1_port_2 }
+ - port: { get_resource: fe1_port_3 }
+ - port: { get_resource: fe1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_scp_fe1_name}
+# wc_notify: { get_attr: ['scp_fe_wait_handle', 'curl_cli'] }
+
+ fe1_port_1:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: SIGNET_vrf_B1_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_SIGNET_vrf_B1_direct_ips}}]
+
+ fe1_port_2:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: FE_Clustering_KA }
+
+ fe1_port_3:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: Clustering_Network }
+
+ fe1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: fe1_OAM_direct_ips}}]
+
+ server_smp0:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp0_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp0 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp0_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp0_OAM_direct_ips}}]
+
+ server_smp1:
+ type: OS::Nova::Server
+ properties:
+ name: { get_param: vm_smp1_name }
+ image: { get_param: image_smp_id }
+# availability_zone: { get_param: availability_zone_smp1 }
+ flavor: { get_param: flavor_smp_id }
+ scheduler_hints: { group: { get_resource: SMP_Affinity } }
+ networks:
+ - port: { get_resource: smp1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_smp1_name}
+# wc_notify: { get_attr: ['smp_wait_handle', 'curl_cli'] }
+
+ smp1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: smp1_OAM_direct_ips}}]
+
+ server_db0:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db0_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db0 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db0_port_4 }
+ - port: { get_resource: db0_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db0_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db0_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db0_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db0_OAM_direct_ips}}]
+
+ server_db1:
+ type: OS::Nova::Server
+# depends_on: smp_wait_condition
+ properties:
+ name: { get_param: vm_db1_name }
+ image: { get_param: image_db_id }
+# availability_zone: { get_param: availability_zone_db1 }
+ flavor: { get_param: flavor_db_id }
+ scheduler_hints: { group: { get_resource: DB_Affinity } }
+ networks:
+ - port: { get_resource: db1_port_4 }
+ - port: { get_resource: db1_port_7 }
+ metadata:
+ vnf_id: { get_param: vnf_id }
+ user_data:
+ str_replace:
+ template: |
+ #!/bin/bash
+ #todo: provision $vm_name
+ wc_notify --data-binary '{"status": "SUCCESS"}'
+ params:
+ $vm_name: {get_param: vm_db1_name}
+# wc_notify: { get_attr: ['db_wait_handle', 'curl_cli'] }
+
+ db1_port_4:
+ type: OS::Neutron::Port
+ properties:
+ network_id: { get_resource: DB_Network }
+
+ db1_port_7:
+ type: OS::Neutron::Port
+ properties:
+ network: { get_param: OAM_direct_net_id }
+ fixed_ips: [{"ip_address": {get_param: db1_OAM_direct_ips}}] \ No newline at end of file
diff --git a/ui-ci/src/main/resources/ci/conf/credentials.yaml b/ui-ci/src/main/resources/ci/conf/credentials.yaml
new file mode 100644
index 0000000000..a30d8ce538
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/conf/credentials.yaml
@@ -0,0 +1,48 @@
+ designer: {
+ username: cs0008,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ admin: {
+ username: jh0003,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ ops: {
+ username: op0001,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ tester: {
+ username: jm0007,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ governor: {
+ username: gv0001,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ product_strategist: {
+ username: ps0001,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ product_manager: {
+ username: pm0001,
+ password: demo,
+ firstname: ASDC,
+ lastname: KASPIN
+ }
+ product_local: {
+ username: pm0001,
+ password: 123123a,
+ firstname: ASDC,
+ lastname: KASPIN
+ } \ No newline at end of file
diff --git a/ui-ci/src/main/resources/ci/conf/log4j.properties b/ui-ci/src/main/resources/ci/conf/log4j.properties
new file mode 100644
index 0000000000..3e159ec8df
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/conf/log4j.properties
@@ -0,0 +1,34 @@
+# Define the root logger with appender file
+log4j.rootLogger = DEBUG, FILE, stdout
+
+# Define the file appender
+log4j.appender.FILE=org.apache.log4j.RollingFileAppender
+log4j.appender.FILE.File=${targetlog}logs/ci-log.out
+
+# Define the layout for file appender
+log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
+log4j.appender.FILE.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p [%10c] : %m%n
+
+# Set the maximum file size before rollover
+log4j.appender.FILE.maxFileSize=5MB
+
+# Set the the backup index
+log4j.appender.FILE.maxBackupIndex=10
+
+
+#############################################################
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+#log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p %10c:%L - %m%n
+
+log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG
+log4j.logger.com.thinkaurelius.titan.diskstorage.cassandra.CassandraTransaction=INFO, FILE, stdout
+
+log4j.logger.org.openecomp.sdc.ci.tests.utils=TRACE, FILE, stdout
+log4j.additivity.org.openecomp.sdc.ci.tests.utils=false
+
+
diff --git a/ui-ci/src/main/resources/ci/conf/sdc-packages.yaml b/ui-ci/src/main/resources/ci/conf/sdc-packages.yaml
new file mode 100644
index 0000000000..dcb78eefc1
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/conf/sdc-packages.yaml
@@ -0,0 +1,2 @@
+packages:
+ - org.openecomp.sdc.ci.tests.execute.resourceui \ No newline at end of file
diff --git a/ui-ci/src/main/resources/ci/conf/sdc.yaml b/ui-ci/src/main/resources/ci/conf/sdc.yaml
new file mode 100644
index 0000000000..8e84fecf3b
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/conf/sdc.yaml
@@ -0,0 +1,57 @@
+outputFolder: target
+reportName: index.html
+catalogBeHost: localhost
+catalogFeHost: localhost
+esHost: eshost
+disributionClientHost: disClient
+catalogFePort: 8181
+catalogBePort: 8080
+disributionClientPort: 8181
+esPort: 9200
+neoHost: neoHost
+neoPort: 7474
+neoDBusername: neo4j
+neoDBpassword: 123456
+url: http://localhost:8285/sdc1
+remoteTestingMachineIP: 1.2.3.4
+remoteTestingMachinePort: 5555
+remoteTesting: false
+
+resourceConfigDir: src/test/resources/CI/tests
+componentsConfigDir: src/test/resources/CI/components
+importResourceConfigDir: src/test/resources/CI/importResource
+importResourceTestsConfigDir: src/test/resources/CI/importResourceTests
+errorConfigurationFile: ../catalog-be/src/main/resources/config/error-configuration.yaml
+configurationFile: ../catalog-be/src/main/resources/config/configuration.yaml
+importTypesConfigDir: src/test/resources/CI/importTypesTest
+
+
+titanPropertiesFile: src/main/resources/ci/conf/titan.properties
+
+stopOnClassFailure: false
+
+#List of non-abstract resources to keep during titan cleanup between tests
+#Only 1.0 version will be kept
+resourcesNotToDelete:
+ - tosca.nodes.Compute
+ - tosca.nodes.Database
+ - tosca.nodes.ObjectStorage
+ - tosca.nodes.BlockStorage
+ - tosca.nodes.LoadBalancer
+ - org.openecomp.resource.cp.Port
+ - org.openecomp.resource.vl.Network
+
+#Resource categories to keep (including all their subcategories)
+resourceCategoriesNotToDelete:
+ - Generic
+ - Network L2-3
+ - Network L4+
+ - Application L4+
+ - Network Connectivity
+
+#Service categories to keep
+serviceCategoriesNotToDelete:
+ - Mobility
+ - Network L1-3
+ - Network L4
+ - VoIP Call Control \ No newline at end of file
diff --git a/ui-ci/src/main/resources/ci/conf/titan.properties b/ui-ci/src/main/resources/ci/conf/titan.properties
new file mode 100644
index 0000000000..94d12cfba0
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/conf/titan.properties
@@ -0,0 +1,7 @@
+storage.backend=cassandra
+storage.hostname=cassandrahost
+storage.port=9160
+
+cache.db-cache-clean-wait = 20
+cache.db-cache-time = 180000
+cache.db-cache-size = 0.5 \ No newline at end of file
diff --git a/ui-ci/src/main/resources/ci/scripts/startTest.sh b/ui-ci/src/main/resources/ci/scripts/startTest.sh
new file mode 100644
index 0000000000..b4b2cb114e
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/scripts/startTest.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage: $0 <jar file>"
+}
+
+function exitOnError() {
+ if [ $1 -ne 0 ]
+ then
+ echo "Failed running task $2"
+ exit 2
+ fi
+}
+
+if [ $# -lt 1 ]
+then
+ usage
+ exit 2
+fi
+
+CURRENT_DIR=`pwd`
+BASEDIR=$(dirname $0)
+
+if [ ${BASEDIR:0:1} = "/" ]
+then
+ FULL_PATH=$BASEDIR
+else
+ FULL_PATH=$CURRENT_DIR/$BASEDIR
+fi
+
+LOGS_PROP_FILE=file:${FULL_PATH}/conf/log4j.properties
+#############################################
+TARGET_DIR=${FULL_PATH}/target
+CONF_FILE=${FULL_PATH}/conf/sdc.yaml
+
+DEBUG=true
+MainClass=org.openecomp.sdc.ci.tests.run.StartTest
+
+JAR_FILE=$1
+SUITE_FILE=$2
+FILES_TEST=$3
+
+if [ -z "$3" ]
+then
+ FILES_TEST=${FULL_PATH}/Files
+ echo "$3"
+fi
+
+#TARGET_DIR=`echo ${TARGET_DIR} | sed 's/\//\//g'`
+#echo $TARGET_DIR
+
+TESTS_DIR=/opt/app/sdc/ci/resources/tests
+COMPONENTS_DIR=/opt/app/sdc/ci/resources/components
+
+#sed -i 's#\(outputFolder:\).*#\1 '${TARGET_DIR}'#g' $CONF_FILE
+#sed -i 's#\(resourceConfigDir:\).*#\1 '${TESTS_DIR}'#g' $CONF_FILE
+#sed -i 's#\(componentsConfigDir:\).*#\1 '${COMPONENTS_DIR}'#g' $CONF_FILE
+TARGET_LOG_DIR="${TARGET_DIR}/"
+
+# mkdir -p ${TARGET_DIR}
+#if [ -d ${TARGET_DIR} ]
+#then
+# rm -rf ${TARGET_DIR}/*
+#exitOnError $? "Failed_to_delete_target_dir"
+#fi
+
+debug_port=8800
+#JAVA_OPTION="-javaagent:/var/tmp/jacoco/lib/jacocoagent.jar=destfile=jacoco-it.exec"
+JAVA_OPTION=""
+case "$2" in
+ -debug) echo "Debug mode, Listen on port $debug_port"; JAVA_OPTION="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${debug_port}" ;;
+ "") echo "Standard mode";;
+ *) echo "USAGE: startTest.sh [-debug]";;
+esac
+
+cmd="nohup java $JAVA_OPTION -DdisplayException=true -Dtargetlog=${TARGET_LOG_DIR} -Dfilepath=${FILES_TEST} -Dconfig.resource=${CONF_FILE} -Ddebug=${DEBUG} -Dlog4j.configuration=${LOGS_PROP_FILE} -cp $JAR_FILE ${MainClass} $SUITE_FILE &"
+
+#echo $cmd
+#console=`$cmd`
+
+if [ $DEBUG == "true" ]
+then
+ $cmd
+else
+ $cmd >> /dev/null
+fi
+status=`echo $?`
+
+
+
+echo "##################################################"
+echo "################# status is ${status} #################"
+echo "##################################################"
+
+exit $status
+
diff --git a/ui-ci/src/main/resources/ci/testSuites/ui-ci.xml b/ui-ci/src/main/resources/ci/testSuites/ui-ci.xml
new file mode 100644
index 0000000000..a6c2584a2c
--- /dev/null
+++ b/ui-ci/src/main/resources/ci/testSuites/ui-ci.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="uitests" configfailurepolicy="continue">
+ <test name="uitests">
+ <classes>
+ <!--onboard-->
+ <class name="org.openecomp.sdc.ci.tests.execute.sanity.Onboard">
+ <methods>
+ <include name="onboardVNFTest"/>
+ </methods>
+ </class>
+ <!--vf-->
+ <class name="org.openecomp.sdc.ci.tests.execute.sanity.Vf"/>
+ </classes>
+ </test> <!-- uitests -->
+</suite> <!-- uisuite --> \ No newline at end of file
diff --git a/ui-ci/src/main/resources/images/gizmorambo.jpg b/ui-ci/src/main/resources/images/gizmorambo.jpg
new file mode 100644
index 0000000000..c9a8fe8a64
--- /dev/null
+++ b/ui-ci/src/main/resources/images/gizmorambo.jpg
Binary files differ
diff --git a/ui-ci/src/test/Completetheform.js b/ui-ci/src/test/Completetheform.js
new file mode 100644
index 0000000000..13779e24b8
--- /dev/null
+++ b/ui-ci/src/test/Completetheform.js
@@ -0,0 +1,23 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+/**
+ * New node file
+ */
diff --git a/ui-ci/tarball.xml b/ui-ci/tarball.xml
new file mode 100644
index 0000000000..1413038c94
--- /dev/null
+++ b/ui-ci/tarball.xml
@@ -0,0 +1,60 @@
+<assembly
+ xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+ <id>bin</id>
+ <formats>
+ <format>tar</format>
+ </formats>
+ <files>
+ <file>
+ <source>${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>${project.artifactId}-${project.version}-jar-with-dependencies.jar</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/scripts/startTest.sh</source>
+ <outputDirectory>./</outputDirectory>
+ <destName>startTest.sh</destName>
+ </file>
+ <!--file> <source>src/main/resources/ci/scripts/postinstall</source> <outputDirectory>./</outputDirectory>
+ <destName>postinstall</destName> </file -->
+ <file>
+ <source>src/main/resources/ci/conf/sdc.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>sdc.yaml</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/conf/sdc-packages.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>sdc-packages.yaml</destName>
+ </file>
+ <file>
+ <source>../asdc-tests/src/main/resources/ci/conf/log4j.properties</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>log4j.properties</destName>
+ </file>
+ <file>
+ <source>src/main/resources/ci/conf/credentials.yaml</source>
+ <outputDirectory>conf</outputDirectory>
+ <destName>credentials.yaml</destName>
+ </file>
+
+ </files>
+
+ <fileSets>
+ <fileSet>
+ <directory>src/test/resources</directory>
+ <outputDirectory>./</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/ci/testSuites</directory>
+ <outputDirectory>./testSuites</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/resources/Files</directory>
+ <outputDirectory>./Files</outputDirectory>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/webseal-simulator/README.txt b/webseal-simulator/README.txt
new file mode 100644
index 0000000000..46fd622bc8
--- /dev/null
+++ b/webseal-simulator/README.txt
@@ -0,0 +1,18 @@
+------------------------
+| |
+| webseal simulator |
+| |
+------------------------
+
+Working with webseal simulator:
+-------------------------------
+
+1. Build the project using: mvn clean install
+2. Ftp war file: webseal-simulator\target\WSSimulator.war to your localhost vagrant machine: /home/vagrant/webseal-simulator/webapps folder
+3. Ftp configuration file: webseal-simulator\src\main\resources\webseal.conf to your localhost vagrant machine: /home/vagrant/webseal-simulator/config
+4. To run the simulator, enter to your local vagrant and run: startWebsealSimulator.sh
+5. Open browser and navigate to: http://localhost:8285/sdc1.login
+
+Note: the user in webseal configuration file will appear in the login screen. Pressing on user link will perform authentication and redirect to SDC.
+The users should be predefined in SDC
+
diff --git a/webseal-simulator/pom.xml b/webseal-simulator/pom.xml
new file mode 100644
index 0000000000..5d183303f0
--- /dev/null
+++ b/webseal-simulator/pom.xml
@@ -0,0 +1,126 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.openecomp.sdc</groupId>
+ <artifactId>webseal-simulator</artifactId>
+ <packaging>war</packaging>
+ <version>0.0.1-SNAPSHOT</version>
+
+<parent>
+<groupId>org.openecomp.sdc</groupId>
+<artifactId>sdc-main</artifactId>
+<version>1610.2.4</version>
+</parent>
+
+
+ <properties>
+ <jetty-version>9.2.10.v20150310</jetty-version>
+ </properties>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>3.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging-api</artifactId>
+ <version>1.0.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+
+
+
+ <!-- Jetty Proxy -->
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-webapp</artifactId>
+ <version>9.2.10.v20150310</version>
+ <scope>provided</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-proxy</artifactId>
+ <version>9.2.10.v20150310</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlets</artifactId>
+ <version>9.2.10.v20150310</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Proxy servlet -->
+
+
+ <dependency>
+ <groupId>com.typesafe</groupId>
+ <artifactId>config</artifactId>
+ <version>1.0.2</version>
+ <scope>compile</scope>
+ </dependency>
+
+ </dependencies>
+ <build>
+ <finalName>WSSimulator</finalName>
+ <plugins>
+ <!-- ================================================== -->
+ <!-- Set the JDK compiler version. -->
+ <!-- ================================================== -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <inherited>true</inherited>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+
+
+ <plugin>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-maven-plugin</artifactId>
+ <version>9.2.10.v20150310</version>
+ <configuration>
+ <contextPath>/</contextPath>
+ <webApp>
+ <contextPath>/</contextPath>
+ <webInfIncludeJarPattern>.*/.*jersey-[^/]\.jar$</webInfIncludeJarPattern>
+ </webApp>
+ <war>WSSimulator.war</war>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+</project>
diff --git a/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/Login.java b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/Login.java
new file mode 100644
index 0000000000..b345824bd6
--- /dev/null
+++ b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/Login.java
@@ -0,0 +1,131 @@
+package org.openecomp.sdc.webseal.simulator;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.openecomp.sdc.webseal.simulator.conf.Conf;
+
+public class Login extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void init(final ServletConfig config) throws ServletException {
+ super.init(config);
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
+ throws ServletException, IOException {
+
+ if (null != request.getParameter("userId")) {
+ doPost(request, response);
+ return;
+ }
+ System.out.println("about to build login page");
+ response.setContentType("text/html");
+ PrintWriter writer = response.getWriter();
+ String message = (String) request.getAttribute("message");
+ if (message == null) {
+ message = "";
+ }
+
+ Collection<User> allUsers = Conf.getInstance().getUsers().values();
+ writer.println("<html>");
+
+ writer.println("<head>");
+ writer.println("<style>");
+ writer.println("body {padding: 40px; font-family: Arial; font-size: 14px;}");
+ writer.println("h1 {background-color: #DDDDDD; padding: 4px 10px;}");
+ writer.println("h2 {margin-top: 20px;}");
+ writer.println(".label {width: 100px; float:left;}");
+ writer.println(".break {display: block; margin-bottom: 10px;}");
+ writer.println("li {margin-bottom: 10px;}");
+ writer.println("</style>");
+ writer.println("</head>");
+
+ writer.println("<body>");
+
+ writer.println("<h1>Webseal simulator</h1>");
+ writer.println("<h2>Login:</h2>");
+
+ writer.println("<form action=\"\" method=\"post\">");
+ writer.println(" <div class='label'>User id:</div>");
+ writer.println(" <input type='text' name='userId'>");
+ writer.println(" <div class='break'></div>");
+
+ writer.println(" <div class='label'>Password:</div>");
+ writer.println(" <input type='password' name='password'>");
+ writer.println(" <div class='break'></div>");
+
+ writer.println(" <input type='submit' value='Login'>");
+ writer.println(" <label name='message'></label>");
+ writer.println("</form>");
+
+ writer.println("<hr/>");
+ writer.println("<h2>Quick links:</h2>");
+ writer.println("<ul>");
+ allUsers.forEach(u -> writer.println("<li>" + u.getUserRef() + " (" + u.getUserId() + ")" + "</li>"));
+ writer.println("</ul>");
+
+ writer.println("</body>");
+ writer.println("</html>");
+
+ }
+
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+
+ String userId = request.getParameter("userId");
+ String password = request.getParameter("password");
+ request.setAttribute("message", "OK");
+
+ System.out.println("Login -> doPOst userId=" + userId);
+ User user = getUser(userId, password);
+ if (user == null) {
+ request.setAttribute("message", "ERROR: attuid or password incorect");
+ doGet(request, response);
+ } else {
+ System.out.println("Login -> doPOst redirext to /sdc1 (to proxy)");
+ Cookie cookieUser = new Cookie("HTTP_IV_USER", user.getUserId());
+ Cookie cookieAttuid = new Cookie("USER_ID", user.getUserId());
+ Cookie cookieFirstName = new Cookie("HTTP_CSP_FIRSTNAME", user.getFirstName());
+ Cookie cookieEmail = new Cookie("HTTP_CSP_EMAIL", user.getEmail());
+ Cookie cookieLastName = new Cookie("HTTP_CSP_LASTNAME", user.getLastName());
+ Cookie cookieRemoteAddress = new Cookie("HTTP_IV_REMOTE_ADDRESS", "0.0.0.0");
+ Cookie cookieWsType = new Cookie("HTTP_CSP_WSTYPE", "Intranet");
+ response.addCookie(cookieUser);
+ response.addCookie(cookieAttuid);
+ response.addCookie(cookieFirstName);
+ response.addCookie(cookieEmail);
+ response.addCookie(cookieLastName);
+ response.addCookie(cookieRemoteAddress);
+ response.addCookie(cookieWsType);
+ response.sendRedirect("/sdc1");
+ }
+
+ }
+
+ private User getUser(String userId, String password) {
+ User user = Conf.getInstance().getUsers().get(userId);
+ if (user == null) {
+ return null;
+ }
+ if (!password.equals(user.getPassword())) {
+ return null;
+ }
+ return user;
+ }
+
+ @Override
+ public String getServletInfo() {
+ return "Http Proxy Servlet";
+ }
+}
diff --git a/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MethodEnum.java b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MethodEnum.java
new file mode 100644
index 0000000000..6b9b01d259
--- /dev/null
+++ b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MethodEnum.java
@@ -0,0 +1,10 @@
+package org.openecomp.sdc.webseal.simulator;
+
+public enum MethodEnum {
+
+ GET,
+ POST,
+ PUT,
+ DELETE
+
+}
diff --git a/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MutableHttpServletRequest.java b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MutableHttpServletRequest.java
new file mode 100644
index 0000000000..74c8a15c80
--- /dev/null
+++ b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/MutableHttpServletRequest.java
@@ -0,0 +1,55 @@
+package org.openecomp.sdc.webseal.simulator;
+
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
+ // holds custom header and value mapping
+ private final Map<String, String> customHeaders;
+
+ public MutableHttpServletRequest(HttpServletRequest request){
+ super(request);
+ this.customHeaders = new HashMap<String, String>();
+ }
+
+ public void putHeader(String name, String value){
+ this.customHeaders.put(name, value);
+ }
+
+ public String getHeader(String name) {
+ // check the custom headers first
+ String headerValue = customHeaders.get(name);
+
+ if (headerValue != null){
+ return headerValue;
+ }
+ // else return from into the original wrapped object
+ return ((HttpServletRequest) getRequest()).getHeader(name);
+ }
+
+ public Enumeration<String> getHeaderNames() {
+ // create a set of the custom header names
+ Set<String> set = new HashSet<String>(customHeaders.keySet());
+
+ // now add the headers from the wrapped request object
+ @SuppressWarnings("unchecked")
+ Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
+ while (e.hasMoreElements()) {
+ // add the names of the request headers into the list
+ String n = e.nextElement();
+ set.add(n);
+ }
+
+ // create an enumeration from the set and return
+ return Collections.enumeration(set);
+ }
+}
+
diff --git a/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/SdcProxy.java b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/SdcProxy.java
new file mode 100644
index 0000000000..674f39ff36
--- /dev/null
+++ b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/SdcProxy.java
@@ -0,0 +1,318 @@
+package org.openecomp.sdc.webseal.simulator;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.openecomp.sdc.webseal.simulator.conf.Conf;
+
+public class SdcProxy extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+ private URL url;
+ private HttpClient proxy;
+ private Conf conf;
+
+ private final String SDC1 = "/sdc1";
+ private final String ONBOARDING = "/onboarding/";
+ private final String SCRIPTS = "/scripts";
+ private final String STYLES = "/styles";
+ private final String LANGUAGES = "/languages";
+
+
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ conf = Conf.getInstance();
+ try {
+ String feHost = conf.getFeHost();
+ this.url = new URL(feHost);
+ } catch (MalformedURLException me) {
+ throw new ServletException("Proxy URL is invalid", me);
+ }
+ this.proxy = new HttpClient();
+ this.proxy.getHostConfiguration().setHost(this.url.getHost());
+ }
+
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ proxy(request, response, MethodEnum.GET);
+ }
+
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+
+ String userId = request.getParameter("userId");
+ String password = request.getParameter("password");
+
+ // Already sign-in
+ if (userId == null){
+ userId = request.getHeader("USER_ID");
+ }
+
+ System.out.println("SdcProxy -> doPost userId=" + userId);
+ request.setAttribute("message", "OK");
+ if (password != null && getUser(userId, password) == null) {
+ MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
+ RequestDispatcher view = request.getRequestDispatcher("login");
+ request.setAttribute("message", "ERROR: userid or password incorect");
+ view.forward(mutableRequest, response);
+ } else {
+ System.out.println("SdcProxy -> doPost going to doGet");
+ request.setAttribute("HTTP_IV_USER", userId);
+ proxy(request, response, MethodEnum.POST);
+ }
+ }
+
+ public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ proxy(request, response, MethodEnum.PUT);
+ }
+
+ public void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ proxy(request, response, MethodEnum.DELETE);
+ }
+
+ private synchronized void proxy(HttpServletRequest request, HttpServletResponse response, MethodEnum methodEnum) throws IOException, UnsupportedEncodingException, HttpException {
+ Map<String, String[]> requestParameters = request.getParameterMap();
+ System.out.print(request.getRequestURI() + " -> ");
+
+ String userIdHeader = getUseridFromRequest(request);
+ //System.out.print(" (userIdHeader=" + userIdHeader + ") ");
+ User user = getUser(userIdHeader);
+
+ // new request - forward to login page
+ if (userIdHeader == null) {
+ System.out.print("Going to login");
+ response.sendRedirect("/login");
+ return;
+ }
+
+ String uri = getUri(request, requestParameters);
+ HttpMethodBase proxyMethod = getMethod(request, methodEnum, uri);
+ System.out.println(uri);
+ addHeaders(user, proxyMethod);
+ addHeaders(request, proxyMethod);
+ this.proxy.executeMethod(proxyMethod);
+ response.setStatus(proxyMethod.getStatusCode());
+
+ if (request.getRequestURI().indexOf(".svg") > -1) {
+ response.setContentType("image/svg+xml");
+ }
+
+ InputStream responseBodyStream = proxyMethod.getResponseBodyAsStream();
+ Header contentEncodingHeader = proxyMethod.getResponseHeader("Content-Encoding");
+ if (contentEncodingHeader != null && contentEncodingHeader.getValue().equalsIgnoreCase("gzip")) {
+ responseBodyStream = new GZIPInputStream(responseBodyStream);
+ }
+ write(responseBodyStream, response.getOutputStream());
+ }
+
+ private void addHeaders(HttpServletRequest request, HttpMethodBase proxyMethod) {
+ Enumeration<String> headerNames = request.getHeaderNames();
+ while (headerNames.hasMoreElements()) {
+ String headerName = headerNames.nextElement();
+ Enumeration<String> headers = request.getHeaders(headerName);
+ while (headers.hasMoreElements()) {
+ String headerValue = headers.nextElement();
+ proxyMethod.addRequestHeader(headerName, headerValue);
+ }
+ }
+ }
+
+ private User getUser(String userId, String password) {
+ User user = getUser(userId);
+ if (user.getPassword().equals(password)) {
+ return user;
+ }
+ return null;
+ }
+
+ private User getUser(String userId) {
+ return conf.getUsers().get(userId);
+
+ }
+
+ private List<String> getContextPaths(){
+ List<String> contextPaths = new ArrayList<>();
+ contextPaths.add(SDC1);
+ contextPaths.add(ONBOARDING);
+ contextPaths.add(STYLES);
+ contextPaths.add(SCRIPTS);
+ contextPaths.add(LANGUAGES);
+ return contextPaths;
+ }
+
+ private String getUri(HttpServletRequest request, Map<String, String[]> requestParameters) throws UnsupportedEncodingException {
+ String suffix = request.getRequestURI();
+ if (getContextPaths().stream().anyMatch(request.getRequestURI()::contains)) {
+ suffix = alignUrlProxy(suffix);
+ }
+ StringBuilder query = alignUrlParameters(requestParameters);
+ String uri = String.format("%s%s", new Object[] {this.url.toString() + suffix, query.toString() });
+ return uri;
+ }
+
+ private HttpMethodBase getMethod(HttpServletRequest request, MethodEnum methodEnum, String uri) throws IOException {
+ HttpMethodBase proxyMethod = null;
+ switch (methodEnum) {
+ case GET:
+ proxyMethod = new GetMethod(uri);
+ break;
+ case POST:
+ proxyMethod = new PostMethod(uri);
+ ((PostMethod) proxyMethod).setRequestEntity(new InputStreamRequestEntity(request.getInputStream()));
+ break;
+ case PUT:
+ proxyMethod = new PutMethod(uri);
+ ((PutMethod) proxyMethod).setRequestBody(getBody(request));
+ break;
+ case DELETE:
+ proxyMethod = new DeleteMethod(uri);
+ break;
+ }
+ return proxyMethod;
+ }
+
+ private String getUseridFromRequest(HttpServletRequest request) {
+
+ String userIdHeader = request.getHeader("USER_ID");
+ if (userIdHeader != null){
+ return userIdHeader;
+ }
+ Object o = request.getAttribute("HTTP_IV_USER");
+ if (o != null) {
+ return o.toString();
+ }
+ Cookie[] cookies = request.getCookies();
+
+ if (cookies != null){
+ for (int i=0; i<cookies.length; ++i){
+ if (cookies[i].getName().equals("USER_ID")){
+ userIdHeader = cookies[i].getValue();
+ }
+ }
+ }
+ return userIdHeader;
+ }
+
+ private void addHeaders(User user, HttpMethodBase proxyMethod) {
+ proxyMethod.addRequestHeader("HTTP_IV_USER", user.getUserId());
+ proxyMethod.addRequestHeader("USER_ID", user.getUserId());
+ proxyMethod.addRequestHeader("HTTP_CSP_FIRSTNAME", user.getFirstName());
+ proxyMethod.addRequestHeader("HTTP_CSP_EMAIL", user.getEmail());
+ proxyMethod.addRequestHeader("HTTP_CSP_LASTNAME", user.getLastName());
+ proxyMethod.addRequestHeader("HTTP_IV_REMOTE_ADDRESS", "0.0.0.0");
+ proxyMethod.addRequestHeader("HTTP_CSP_WSTYPE", "Intranet");
+ proxyMethod.addRequestHeader("HTTP_CSP_EMAIL", "me@mail.com");
+ }
+
+ private String alignUrlProxy(String requestURI) {
+
+ int i = requestURI.indexOf(ONBOARDING);
+ if (-1 != i){
+ return requestURI.substring(i);
+ }
+
+ i = requestURI.indexOf(SDC1+SDC1);
+ if (-1 != i){
+ return requestURI.substring(SDC1.length());
+ }
+
+ i = requestURI.indexOf(SDC1);
+ if (-1 != i){
+ return requestURI;
+ }
+
+ return SDC1+requestURI;
+ }
+
+ private StringBuilder alignUrlParameters(Map<String, String[]> requestParameters) throws UnsupportedEncodingException {
+ StringBuilder query = new StringBuilder();
+ for (String name : requestParameters.keySet()) {
+ for (String value : (String[]) requestParameters.get(name)) {
+ if (query.length() == 0) {
+ query.append("?");
+ } else {
+ query.append("&");
+ }
+ name = URLEncoder.encode(name, "UTF-8");
+ value = URLEncoder.encode(value, "UTF-8");
+
+ query.append(String.format("&%s=%s", new Object[] { name, value }));
+ }
+ }
+ return query;
+ }
+
+ private void write(InputStream inputStream, OutputStream outputStream) throws IOException {
+ int b;
+ while (inputStream != null && (b = inputStream.read()) != -1) {
+ outputStream.write(b);
+ }
+ outputStream.flush();
+ }
+
+ public String getServletInfo() {
+ return "Http Proxy Servlet";
+ }
+
+
+ public String getBody(HttpServletRequest request) throws IOException {
+
+ String body = null;
+ StringBuilder stringBuilder = new StringBuilder();
+ BufferedReader bufferedReader = null;
+
+ try {
+ InputStream inputStream = request.getInputStream();
+ if (inputStream != null) {
+ bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+ char[] charBuffer = new char[128];
+ int bytesRead = -1;
+ while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
+ stringBuilder.append(charBuffer, 0, bytesRead);
+ }
+ } else {
+ stringBuilder.append("");
+ }
+ } catch (IOException ex) {
+ throw ex;
+ } finally {
+ if (bufferedReader != null) {
+ try {
+ bufferedReader.close();
+ } catch (IOException ex) {
+ throw ex;
+ }
+ }
+ }
+
+ body = stringBuilder.toString();
+ return body;
+ }
+}
diff --git a/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/User.java b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/User.java
new file mode 100644
index 0000000000..7ec1d936ba
--- /dev/null
+++ b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/User.java
@@ -0,0 +1,64 @@
+package org.openecomp.sdc.webseal.simulator;
+
+public class User {
+
+ private String firstName;
+ private String lastName;
+ private String email;
+ private String userId;
+
+ private String password;
+
+ public User(){
+ }
+
+ public User(String userId){
+ setUserId(userId);
+ }
+
+ public User(String firstName,String lastName,String email,String userId,String password){
+ setUserId(userId);
+ setFirstName(firstName);
+ setLastName(lastName);
+ setEmail(email);
+ setPassword(password);
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+ public String getLastName() {
+ return lastName;
+ }
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+ public String getEmail() {
+ return email;
+ }
+ public void setEmail(String email) {
+ this.email = email;
+ }
+ public String getUserId() {
+ return userId;
+ }
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getUserRef() {
+ return "<a href='?userId="+getUserId()+"&password="+getPassword()+"'>"+getFirstName()+" "+getLastName()+"</a>";
+ }
+
+
+}
diff --git a/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/conf/Conf.java b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/conf/Conf.java
new file mode 100644
index 0000000000..cde77c6c96
--- /dev/null
+++ b/webseal-simulator/src/main/java/org/openecomp/sdc/webseal/simulator/conf/Conf.java
@@ -0,0 +1,67 @@
+package org.openecomp.sdc.webseal.simulator.conf;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.sdc.webseal.simulator.User;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+
+public class Conf {
+
+ private static Conf conf= null;
+ private String feHost;
+ Map<String,User> users = new HashMap<String,User>();
+
+ private Conf(){
+ initConf();
+ }
+
+ private void initConf() {
+ try{
+ String confPath = System.getProperty("config.resource");
+ if (confPath == null){
+ System.out.println("config.resource is empty - goint to get it from config.home");
+ confPath = System.getProperty("config.home") + "/webseal.conf";
+ }
+ System.out.println("confPath=" + confPath );
+ Config confFile = ConfigFactory.parseFileAnySyntax(new File(confPath));
+ Config resolve = confFile.resolve();
+ setFeHost(resolve.getString("webseal.fe"));
+ List<? extends Config> list = resolve.getConfigList("webseal.users");
+ for (Config conf : list ){
+ String userId = conf.getString("userId");
+ String password = conf.getString("password");
+ String firstName = conf.getString("firstName");
+ String lastName = conf.getString("lastName");
+ String email = conf.getString("email");
+ users.put(userId,new User(firstName,lastName,email,userId,password));
+ }
+
+ }catch(Exception e){
+ e.printStackTrace();
+ }
+ }
+
+ public static Conf getInstance(){
+ if (conf == null){
+ conf = new Conf();
+ }
+ return conf;
+ }
+
+ public String getFeHost() {
+ return feHost;
+ }
+
+ public void setFeHost(String feHost) {
+ this.feHost = feHost;
+ }
+
+ public Map<String,User> getUsers() {
+ return users;
+ }
+
+}
diff --git a/webseal-simulator/src/main/resources/webseal.conf b/webseal-simulator/src/main/resources/webseal.conf
new file mode 100644
index 0000000000..4f6455210a
--- /dev/null
+++ b/webseal-simulator/src/main/resources/webseal.conf
@@ -0,0 +1,64 @@
+{
+ webseal {
+ fe="http://localhost:8181"
+ users = [
+ {
+ userId="cs0008"
+ password="123123a"
+ firstName="Carlos"
+ lastName="Santana"
+ email="csantana@att.com"
+ },
+ {
+ userId="af0006"
+ password="123123a"
+ firstName="Aretha"
+ lastName="Franklin"
+ email="afranklin@att.com"
+ },
+ {
+ userId="jh0003"
+ password="123123a"
+ firstName="Jimmy"
+ lastName="Hendrix"
+ email="admin@sdc.com"
+ },
+ {
+ userId="kb0004"
+ password="123123a"
+ firstName="Kate"
+ lastName="Bush"
+ email="tester@sdc.com"
+ },
+ {
+ userId="op0001"
+ password="123123a"
+ firstName="Steve"
+ lastName="Regev"
+ email="ops@sdc.com"
+ },
+ {
+ userId="gv0001"
+ password="123123a"
+ firstName="David"
+ lastName="Shadmi"
+ email="governor@sdc.com"
+ },
+ {
+ userId="pm0001"
+ password="123123a"
+ firstName="Teddy"
+ lastName="Isashar"
+ email="pm1@sdc.com"
+ },
+ {
+ userId="ps0001"
+ password="123123a"
+ firstName="Eden"
+ lastName="Rozin"
+ email="ps1@sdc.com"
+ }
+ ]
+ }
+
+} \ No newline at end of file
diff --git a/webseal-simulator/src/main/webapp/WEB-INF/jetty-web.xml b/webseal-simulator/src/main/webapp/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000000..2e287bcc90
--- /dev/null
+++ b/webseal-simulator/src/main/webapp/WEB-INF/jetty-web.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE Configure PUBLIC
+ "-//Mort Bay Consulting//DTD Configure//EN"
+ "http://www.eclipse.org/jetty/configure_9_0.dtd">
+
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Set name="contextPath">/</Set>
+</Configure>
diff --git a/webseal-simulator/src/main/webapp/WEB-INF/web.xml b/webseal-simulator/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..787335ea20
--- /dev/null
+++ b/webseal-simulator/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,32 @@
+<!DOCTYPE web-app PUBLIC
+ "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+ "http://java.sun.com/dtd/web-app_2_3.dtd" >
+
+<web-app>
+
+ <display-name>Archetype Created Web Application</display-name>
+
+ <servlet>
+ <servlet-name>Proxy</servlet-name>
+ <servlet-class>org.openecomp.sdc.webseal.simulator.SdcProxy</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>Proxy</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>Login</servlet-name>
+ <servlet-class>org.openecomp.sdc.webseal.simulator.Login</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>Login</servlet-name>
+ <url-pattern>/login</url-pattern>
+ </servlet-mapping>
+
+ <welcome-file-list>
+ <welcome-file>login</welcome-file>
+ </welcome-file-list>
+
+</web-app>
+
diff --git a/webseal-simulator/src/main/webapp/login.html b/webseal-simulator/src/main/webapp/login.html
new file mode 100644
index 0000000000..1760fcf8e6
--- /dev/null
+++ b/webseal-simulator/src/main/webapp/login.html
@@ -0,0 +1,18 @@
+<html>
+<body>
+Login
+
+
+<form action="access" method="post">
+ ATTUID:<br>
+ <input type="text" name="attuid" >
+ <br>
+ PASSWORD:<br>
+ <input type="password" name="password" >
+ <br><br>
+ <input type="submit" value="Submit">
+
+ <label name="message"/>
+</form>
+</body>
+</html> \ No newline at end of file